diff --git a/.gitignore b/.gitignore new file mode 100755 index 00000000..e7a97233 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Python artifacts +*.pyc +*.pyo + +# Batch +*.bat + +# PyCharm +.idea + +# Mac directory info +.DS_Store + +# Shortcuts +*.lnk + +# Git +*.rej + +# Local config +dependencies/config/local.prc + +# PyMongo +bson +gridfs +pymongo \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e4dc8317 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "build/nirai/src"] + path = build/nirai/src + url = https://github.com/nirai-compiler/src +[submodule "build/nirai/panda3d"] + path = build/nirai/panda3d + url = https://github.com/nirai-compiler/panda3d +[submodule "build/nirai/python"] + path = build/nirai/python + url = https://github.com/nirai-compiler/python \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 26ea76f8..00000000 --- a/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Toontown Stride -September build of Toontown Stride - open sourced and free to use. - -## Tired of waiting for keys? -No need to worry! The official Toontown Stride team welcomes you to **build off any files listed inside the repository after you clone.** - -## Why do this? -Since Toontown Stride was originally meant to be a community-based server, we decided to make it *truly* community based on behalf of the original contributers to the source. - -##Is this version safe? -Feel free to scan yourself and give us feedback on your findings! - -#Credit goes to Mr. Batty and Alexalexer for this build of the source. diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 00000000..0705049e --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,3 @@ +libpandadna/ +built/ +*.pdb diff --git a/build/data/NiraiStart.py b/build/data/NiraiStart.py new file mode 100644 index 00000000..905cf26a --- /dev/null +++ b/build/data/NiraiStart.py @@ -0,0 +1,72 @@ +from panda3d.core import * +import __builtin__, os, sys +import aes + +import niraidata + +# Config +prc = niraidata.CONFIG +iv, key, prc = prc[:16], prc[16:32], prc[32:] +prc = aes.decrypt(prc, key, iv) + +for line in prc.split('\n'): + line = line.strip() + if line: + loadPrcFileData('nirai config', line) + +del prc +del iv +del key + +# DC +__builtin__.dcStream = StringStream() + +dc = niraidata.DC +iv, key, dc = dc[:16], dc[16:32], dc[32:] +dc = aes.decrypt(dc, key, iv) + +dcStream.setData(dc) +del dc +del iv +del key + +# Resources +# TO DO: Sign and verify the phases to prevent editing. + +vfs = VirtualFileSystem.getGlobalPtr() +mfs = [3, 3.5, 4, 5, 5.5, 6, 7, 8, 9, 10, 11, 12, 13] +abort = False + +for mf in mfs: + filename = 'resources/default/phase_%s.mf' % mf + if not os.path.isfile(filename): + print 'Phase %s not found' % filename + abort = True + break + + mf = Multifile() + mf.openRead(filename) + + if not vfs.mount(mf, '/', 0): + print 'Unable to mount %s' % filename + abort = True + break + +# Packs +pack = os.environ.get('TT_STRIDE_CONTENT_PACK') +import glob +if pack and pack != 'default': + print 'Loading content pack', pack + for file in glob.glob('resources/%s/*.mf' % pack): + mf = Multifile() + mf.openReadWrite(Filename(file)) + names = mf.getSubfileNames() + for name in names: + ext = os.path.splitext(name)[1] + if ext not in ['.jpg', '.jpeg', '.ogg', '.rgb']: + mf.removeSubfile(name) + vfs.mount(mf, Filename('/'), 0) + +if not abort: + # Run + import toontown.toonbase.ToontownStart diff --git a/build/linux/aes-to-so.sh b/build/linux/aes-to-so.sh new file mode 100755 index 00000000..dc31b0c1 --- /dev/null +++ b/build/linux/aes-to-so.sh @@ -0,0 +1,3 @@ +gcc -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -fPIC -DMAJOR_VERSION=1 -DMINOR_VERSION=0 -I/usr/include -I/usr/include/python2.7 -lstdc++ -lssl -lcrypto -c ../nirai/src/aes.cxx -c -o ../nirai/src/aes.o + +gcc -shared ../nirai/src/aes.o -L/usr/local/lib -lstdc++ -lssl -lcrypto -o ../nirai/src/aes.so diff --git a/build/linux/ubuntu-build-panda3d.sh b/build/linux/ubuntu-build-panda3d.sh new file mode 100755 index 00000000..0776c5b7 --- /dev/null +++ b/build/linux/ubuntu-build-panda3d.sh @@ -0,0 +1,2 @@ +cd ../nirai/panda3d +python2.7 makepanda/makepanda.py --everything --no-contrib --no-fmodex --no-physx --no-bullet --no-pview --no-pandatool --no-swscale --no-swresample --no-speedtree --no-vrpn --no-artoolkit --no-opencv --no-directcam --no-vision --no-rocket --no-awesomium --no-deploytools --no-skel --no-ffmpeg --no-eigen --static diff --git a/build/make.py b/build/make.py new file mode 100644 index 00000000..e56f002e --- /dev/null +++ b/build/make.py @@ -0,0 +1,141 @@ +from panda3d.core import * + +import argparse, struct +import sys, glob +import os + +sys.path.append('nirai/src') + +from niraitools import * + +parser = argparse.ArgumentParser() +parser.add_argument('--compile-cxx', '-c', action='store_true', + help='Compile the CXX codes and generate stride.exe into built.') +parser.add_argument('--make-nri', '-n', action='store_true', + help='Generate stride NRI.') +parser.add_argument('--make-mfs', '-m', action='store_true', + help='Make multifiles') +args = parser.parse_args() + +if not os.path.exists('built'): + os.mkdir('built') + +def niraicall_obfuscate(code): + # We'll obfuscate if len(code) % 4 == 0 + # This way we make sure both obfuscated and non-obfuscated code work. + if len(code) % 4: + return False, None + + # Reverse + code = code[::-1] + + # XOR + key = ['B', 'A', 'Q', 'J', 'R', 'P', 'Z', 'P', 'A', 'H', 'U', 'T'] + output = [] + + for i in range(len(code)): + xor_num = ord(code[i]) ^ ord(key[i % len(key)]) + output.append(chr(xor_num)) + + code = ''.join(output) + + return True, code + +niraimarshal.niraicall_obfuscate = niraicall_obfuscate + +class StridePackager(NiraiPackager): + HEADER = 'TTSTRIDE' + BASEDIR = '..' + os.sep + + def __init__(self, outfile, configPath=None): + NiraiPackager.__init__(self, outfile) + self.__manglebase = self.get_mangle_base(self.BASEDIR) + self.add_panda3d_dirs() + self.add_default_lib() + self.globalConfigPath = configPath + + def add_source_dir(self, dir): + self.add_directory(self.BASEDIR + dir, mangler=self.__mangler) + + def add_data_file(self, file): + mb = self.get_mangle_base('data/') + self.add_file('data/%s.py' % file, mangler=lambda x: x[mb:]) + + def __mangler(self, name): + if name.endswith('AI') or name.endswith('UD') or name in ('ToontownAIRepository', 'ToontownUberRepository', + 'ToontownInternalRepository', 'ServiceStart'): + if not 'NonRepeatableRandomSource' in name: + return '' + + return name[self.__manglebase:].strip('.') + + def generate_niraidata(self): + print 'Generating niraidata' + if self.globalConfigPath is not None: + config = self.get_file_contents(self.globalConfigPath) + else: + config = self.get_file_contents('../dependencies/config/general.prc') + config += '\n\n' + self.get_file_contents('../dependencies/config/release/qa.prc') + + config_iv = self.generate_key(16) + config_key = self.generate_key(16) + config = config_iv + config_key + aes.encrypt(config, config_key, config_iv) + niraidata = 'CONFIG = %r' % config + + # DC + niraidata += '\nDC = %r' % self.get_file_contents('../dependencies/astron/dclass/stride.dc', True) + self.add_module('niraidata', niraidata, compile=True) + + def process_modules(self): + # TODO: Compression + dg = Datagram() + dg.addUint32(len(self.modules)) + for moduleName in self.modules: + data, size = self.modules[moduleName] + + dg.addString(moduleName) + dg.addInt32(size) + dg.appendData(data) + + data = dg.getMessage() + iv = self.generate_key(16) + key = self.generate_key(16) + fixed_key = ''.join(chr((i ^ (7 * i + 16)) % ((i + 5) * 3)) for i in xrange(16)) + fixed_iv = ''.join(chr((i ^ (2 * i + 53)) % ((i + 9) * 6)) for i in xrange(16)) + securekeyandiv = aes.encrypt(iv + key, fixed_key, fixed_iv) + return securekeyandiv + aes.encrypt(data, key, iv) + +# Compile the engine +if args.compile_cxx: + compiler = NiraiCompiler('stride.exe', libs=set(glob.glob('libpandadna/libpandadna.dir/Release/*.obj'))) + + compiler.add_nirai_files() + compiler.add_source('src/stride.cxx') + + compiler.run() + +# Compile the game data +if args.make_nri: + pkg = StridePackager('built/TTSData.bin') + + pkg.add_source_dir('otp') + pkg.add_source_dir('toontown') + + pkg.add_data_file('NiraiStart') + + pkg.generate_niraidata() + + pkg.write_out() + +if args.make_mfs: + os.chdir('../resources') + cmd = '' + for phasenum in ['3', '3.5', '4', '5', '5.5', '6', '7', '8', '9', '10', '11', '12', '13']: + print 'phase_%s' % (phasenum) + cmd = 'multify -cf ../build/built/resources/default/phase_%s.mf phase_%s' % (phasenum, phasenum) + p = subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr) + v = p.wait() + + if v != 0: + print 'The following command returned non-zero value (%d): %s' % (v, cmd[:100] + '...') + sys.exit(1) diff --git a/build/nirai/panda3d b/build/nirai/panda3d new file mode 160000 index 00000000..510b6b2b --- /dev/null +++ b/build/nirai/panda3d @@ -0,0 +1 @@ +Subproject commit 510b6b2b3fef1674611e4666b45db949868a8869 diff --git a/build/nirai/python b/build/nirai/python new file mode 160000 index 00000000..d24aeac5 --- /dev/null +++ b/build/nirai/python @@ -0,0 +1 @@ +Subproject commit d24aeac557165f986373fa6d59de128902b74112 diff --git a/build/nirai/src b/build/nirai/src new file mode 160000 index 00000000..93697776 --- /dev/null +++ b/build/nirai/src @@ -0,0 +1 @@ +Subproject commit 936977760bfac218f634533dd4d926ad7e8e2092 diff --git a/build/src/stride.cxx b/build/src/stride.cxx new file mode 100644 index 00000000..45a600b5 --- /dev/null +++ b/build/src/stride.cxx @@ -0,0 +1,158 @@ +#include "nirai.h" +#include +#include +#include +#include +#include + +extern "C" __declspec(dllexport) void initlibpandadna(); +void init_libpandadna(); + +const char* header = "TTSTRIDE"; +const int header_size = 8; + +const int key_and_iv_size = 16; + +int niraicall_onPreStart(int argc, char* argv[]) +{ + return 0; +} + +int niraicall_onLoadGameData() +{ + fstream gd; + + // Open the file + gd.open("TTSData.bin", ios_base::in | ios_base::binary); + if (!gd.is_open()) + { + std::cerr << "Unable to open game file!" << std::endl; + return 1; + } + + // Check the header + char* read_header = new char[header_size]; + gd.read(read_header, header_size); + + if (memcmp(header, read_header, header_size)) + { + std::cerr << "Invalid header" << std::endl; + return 1; + } + + delete[] read_header; + + // Decrypt + std::stringstream ss; + + ss << gd.rdbuf(); + gd.close(); + + std::string brawdata = ss.str(); + + // Decrypted the encrypted key and iv + std::string enckeyandiv = brawdata.substr(0, (key_and_iv_size * 2) + key_and_iv_size); + + unsigned char* deckeyandiv = new unsigned char[enckeyandiv.size()]; + unsigned char* fixed_key = new unsigned char[key_and_iv_size]; + unsigned char* fixed_iv = new unsigned char[key_and_iv_size]; + + // Create fixed key and iv + + for (int i = 0; i < key_and_iv_size; ++i) + fixed_key[i] = (i ^ (7 * i + 16)) % ((i + 5) * 3); + + for (int i = 0; i < key_and_iv_size; ++i) + fixed_iv[i] = (i ^ (2 * i + 53)) % ((i + 9) * 6); + + int deckeyandivsize = AES_decrypt((unsigned char*)enckeyandiv.c_str(), enckeyandiv.size(), fixed_key, fixed_iv, deckeyandiv); + + delete[] fixed_key; + delete[] fixed_iv; + + unsigned char* key = new unsigned char[key_and_iv_size]; + unsigned char* iv = new unsigned char[key_and_iv_size]; + + // Move the decrypted key and iv into their subsequent char + + for (int i = 0; i < key_and_iv_size; ++i) + iv[i] = deckeyandiv[i]; + + for (int i = 0; i < key_and_iv_size; ++i) + key[i] = deckeyandiv[i + key_and_iv_size]; + + delete[] deckeyandiv; + + // Decrypt the game data + std::string rawdata = brawdata.substr((key_and_iv_size * 2) + key_and_iv_size); + unsigned char* decrypted_data = new unsigned char[rawdata.size()]; + + int decsize = AES_decrypt((unsigned char*)rawdata.c_str(), rawdata.size(), key, iv, decrypted_data); // Assumes no error + + delete[] key; + delete[] iv; + + // Read + + // TODO: Compression + + Datagram dg(decrypted_data, decsize); + DatagramIterator dgi(dg); + + unsigned int num_modules = dgi.get_uint32(); + _frozen* fzns = new _frozen[num_modules + 1]; + std::string module, data; + int size; + + for (unsigned int i = 0; i < num_modules; ++i) + { + module = dgi.get_string(); + size = dgi.get_int32(); + data = dgi.extract_bytes(abs(size)); + + char* name = new char[module.size() + 1]; + memcpy(name, module.c_str(), module.size()); + memset(&name[module.size()], 0, 1); + + unsigned char* code = new unsigned char[data.size()]; + memcpy(code, data.c_str(), data.size()); + + _frozen fz; + fz.name = name; + fz.code = code; + fz.size = size; + + memcpy(&fzns[i], &fz, sizeof(_frozen)); + } + + nassertd(dgi.get_remaining_size() == 0) + { + std::cerr << "Corrupted data!" << std::endl; + return 1; + } + + delete[] decrypted_data; + + memset(&fzns[num_modules], 0, sizeof(_frozen)); + PyImport_FrozenModules = fzns; + + // libpandadna + init_libpandadna(); + initlibpandadna(); + + return 0; +} + +extern "C" PyObject* niraicall_deobfuscate(char* code, Py_ssize_t size) +{ + std::string codestr(code, size); + + const char key[12] = {'B', 'A', 'Q', 'J', 'R', 'P', 'Z', 'P', 'A', 'H', 'U', 'T'}; + std::string output = codestr; + + for (int i = 0; i < codestr.size(); i++) + output[i] = codestr[i] ^ key[i % (sizeof(key) / sizeof(char))]; + + std::reverse(output.begin(), output.end()); + return PyString_FromStringAndSize(output.data(), size); +} diff --git a/dependencies/.gitignore b/dependencies/.gitignore new file mode 100644 index 00000000..9f0dc330 --- /dev/null +++ b/dependencies/.gitignore @@ -0,0 +1,2 @@ +*.json +backups/ diff --git a/dependencies/astron/.gitignore b/dependencies/astron/.gitignore new file mode 100644 index 00000000..12eb8cb3 --- /dev/null +++ b/dependencies/astron/.gitignore @@ -0,0 +1,3 @@ +# Exclude the debug builds of Astron. +astrond_debug +astrond_debug.exe diff --git a/dependencies/astron/astrond b/dependencies/astron/astrond new file mode 100755 index 00000000..1ffd4e28 Binary files /dev/null and b/dependencies/astron/astrond differ diff --git a/dependencies/astron/astrond.exe b/dependencies/astron/astrond.exe new file mode 100755 index 00000000..75ab02c0 Binary files /dev/null and b/dependencies/astron/astrond.exe differ diff --git a/dependencies/astron/config/clientagent-0.yml b/dependencies/astron/config/clientagent-0.yml new file mode 100644 index 00000000..4ed7f76b --- /dev/null +++ b/dependencies/astron/config/clientagent-0.yml @@ -0,0 +1,42 @@ +daemon: + name: Client Agent #0 + +general: + eventlogger: 127.0.0.1:7198 + dc_files: + - ../dclass/stride.dc + +uberdogs: + - class: ClientServicesManager + id: 4665 + anonymous: true + + - class: ChatAgent + id: 4681 + anonymous: false + + - class: FriendManager + id: 4501 + anonymous: false + + - class: TTSFriendsManager + id: 4666 + anonymous: false + + - class: GlobalPartyManager + id: 4477 + anonymous: false + +messagedirector: + connect: 127.0.0.1:7100 + +roles: + - type: clientagent + bind: 0.0.0.0:7199 + version: "stride-dev" + client: + relocate: true + add_interest: enabled + channels: + min: 1000000000 + max: 1000001000 diff --git a/dependencies/astron/config/clientagent-1.yml b/dependencies/astron/config/clientagent-1.yml new file mode 100644 index 00000000..780f7175 --- /dev/null +++ b/dependencies/astron/config/clientagent-1.yml @@ -0,0 +1,42 @@ +daemon: + name: Client Agent #1 + +general: + eventlogger: 127.0.0.1:7198 + dc_files: + - ../dclass/stride.dc + +uberdogs: + - class: ClientServicesManager + id: 4665 + anonymous: true + + - class: ChatAgent + id: 4681 + anonymous: false + + - class: FriendManager + id: 4501 + anonymous: false + + - class: TTSFriendsManager + id: 4666 + anonymous: false + + - class: GlobalPartyManager + id: 4477 + anonymous: false + +messagedirector: + connect: 127.0.0.1:7100 + +roles: + - type: clientagent + bind: 0.0.0.0:7299 + version: "stride-dev" + client: + relocate: true + add_interest: enabled + channels: + min: 1000001001 + max: 1000002000 diff --git a/dependencies/astron/config/clientagent-2.yml b/dependencies/astron/config/clientagent-2.yml new file mode 100644 index 00000000..277d3570 --- /dev/null +++ b/dependencies/astron/config/clientagent-2.yml @@ -0,0 +1,42 @@ +daemon: + name: Client Agent #2 + +general: + eventlogger: 127.0.0.1:7198 + dc_files: + - ../dclass/stride.dc + +uberdogs: + - class: ClientServicesManager + id: 4665 + anonymous: true + + - class: ChatAgent + id: 4681 + anonymous: false + + - class: FriendManager + id: 4501 + anonymous: false + + - class: TTSFriendsManager + id: 4666 + anonymous: false + + - class: GlobalPartyManager + id: 4477 + anonymous: false + +messagedirector: + connect: 127.0.0.1:7100 + +roles: + - type: clientagent + bind: 0.0.0.0:7399 + version: "stride-dev" + client: + relocate: true + add_interest: enabled + channels: + min: 1000002001 + max: 1000003000 diff --git a/dependencies/astron/config/clientagent-3.yml b/dependencies/astron/config/clientagent-3.yml new file mode 100644 index 00000000..40a03d12 --- /dev/null +++ b/dependencies/astron/config/clientagent-3.yml @@ -0,0 +1,42 @@ +daemon: + name: Client Agent #3 + +general: + eventlogger: 127.0.0.1:7198 + dc_files: + - ../dclass/stride.dc + +uberdogs: + - class: ClientServicesManager + id: 4665 + anonymous: true + + - class: ChatAgent + id: 4681 + anonymous: false + + - class: FriendManager + id: 4501 + anonymous: false + + - class: TTSFriendsManager + id: 4666 + anonymous: false + + - class: GlobalPartyManager + id: 4477 + anonymous: false + +messagedirector: + connect: 127.0.0.1:7100 + +roles: + - type: clientagent + bind: 0.0.0.0:7499 + version: "stride-dev" + client: + relocate: true + add_interest: enabled + channels: + min: 1000003001 + max: 1000004000 diff --git a/dependencies/astron/config/clientagent-4.yml b/dependencies/astron/config/clientagent-4.yml new file mode 100644 index 00000000..795ad10a --- /dev/null +++ b/dependencies/astron/config/clientagent-4.yml @@ -0,0 +1,42 @@ +daemon: + name: Client Agent #4 + +general: + eventlogger: 127.0.0.1:7198 + dc_files: + - ../dclass/stride.dc + +uberdogs: + - class: ClientServicesManager + id: 4665 + anonymous: true + + - class: ChatAgent + id: 4681 + anonymous: false + + - class: FriendManager + id: 4501 + anonymous: false + + - class: TTSFriendsManager + id: 4666 + anonymous: false + + - class: GlobalPartyManager + id: 4477 + anonymous: false + +messagedirector: + connect: 127.0.0.1:7100 + +roles: + - type: clientagent + bind: 0.0.0.0:7599 + version: "stride-dev" + client: + relocate: true + add_interest: enabled + channels: + min: 1000004001 + max: 1000005000 diff --git a/dependencies/astron/config/clientagent-5.yml b/dependencies/astron/config/clientagent-5.yml new file mode 100644 index 00000000..ea34c1c8 --- /dev/null +++ b/dependencies/astron/config/clientagent-5.yml @@ -0,0 +1,42 @@ +daemon: + name: Client Agent #5 + +general: + eventlogger: 127.0.0.1:7198 + dc_files: + - ../dclass/stride.dc + +uberdogs: + - class: ClientServicesManager + id: 4665 + anonymous: true + + - class: ChatAgent + id: 4681 + anonymous: false + + - class: FriendManager + id: 4501 + anonymous: false + + - class: TTSFriendsManager + id: 4666 + anonymous: false + + - class: GlobalPartyManager + id: 4477 + anonymous: false + +messagedirector: + connect: 127.0.0.1:7100 + +roles: + - type: clientagent + bind: 0.0.0.0:7699 + version: "stride-dev" + client: + relocate: true + add_interest: enabled + channels: + min: 1000005001 + max: 1000006000 diff --git a/dependencies/astron/config/clientagent-6.yml b/dependencies/astron/config/clientagent-6.yml new file mode 100644 index 00000000..b5044745 --- /dev/null +++ b/dependencies/astron/config/clientagent-6.yml @@ -0,0 +1,42 @@ +daemon: + name: Client Agent #6 + +general: + eventlogger: 127.0.0.1:7198 + dc_files: + - ../dclass/stride.dc + +uberdogs: + - class: ClientServicesManager + id: 4665 + anonymous: true + + - class: ChatAgent + id: 4681 + anonymous: false + + - class: FriendManager + id: 4501 + anonymous: false + + - class: TTSFriendsManager + id: 4666 + anonymous: false + + - class: GlobalPartyManager + id: 4477 + anonymous: false + +messagedirector: + connect: 127.0.0.1:7100 + +roles: + - type: clientagent + bind: 0.0.0.0:7799 + version: "stride-dev" + client: + relocate: true + add_interest: enabled + channels: + min: 1000006001 + max: 1000007000 diff --git a/dependencies/astron/config/clientagent-7.yml b/dependencies/astron/config/clientagent-7.yml new file mode 100644 index 00000000..1dcfbdeb --- /dev/null +++ b/dependencies/astron/config/clientagent-7.yml @@ -0,0 +1,42 @@ +daemon: + name: Client Agent #7 + +general: + eventlogger: 127.0.0.1:7198 + dc_files: + - ../dclass/stride.dc + +uberdogs: + - class: ClientServicesManager + id: 4665 + anonymous: true + + - class: ChatAgent + id: 4681 + anonymous: false + + - class: FriendManager + id: 4501 + anonymous: false + + - class: TTSFriendsManager + id: 4666 + anonymous: false + + - class: GlobalPartyManager + id: 4477 + anonymous: false + +messagedirector: + connect: 127.0.0.1:7100 + +roles: + - type: clientagent + bind: 0.0.0.0:7899 + version: "stride-dev" + client: + relocate: true + add_interest: enabled + channels: + min: 1000007001 + max: 1000008000 diff --git a/dependencies/astron/config/cluster.yml b/dependencies/astron/config/cluster.yml new file mode 100644 index 00000000..ba1c9049 --- /dev/null +++ b/dependencies/astron/config/cluster.yml @@ -0,0 +1,64 @@ +daemon: + name: Developer Cluster + +general: + eventlogger: 127.0.0.1:7198 + dc_files: + - ../dclass/stride.dc + +messagedirector: + bind: 127.0.0.1:7100 + +uberdogs: + - class: ClientServicesManager + id: 4665 + anonymous: true + + - class: ChatAgent + id: 4681 + anonymous: false + + - class: FriendManager + id: 4501 + anonymous: false + + - class: TTSFriendsManager + id: 4666 + anonymous: false + + - class: GlobalPartyManager + id: 4477 + anonymous: false + +roles: + - type: clientagent + bind: 0.0.0.0:7199 + version: "tts-dev" + client: + relocate: true + add_interest: enabled + channels: + min: 1000000000 + max: 1000001000 + + - type: stateserver + control: 4002 + + - type: database + control: 4003 + generate: + min: 100000000 + max: 399999999 + backend: + type: yaml + directory: ../databases/astrondb + + - type: dbss + database: 4003 + ranges: + - min: 100000000 + max: 399999999 + + - type: eventlogger + bind: 127.0.0.1:7198 + output: ../logs/events-%y%m%d_%H%M%S.log diff --git a/dependencies/astron/config/eventlogger.yml b/dependencies/astron/config/eventlogger.yml new file mode 100644 index 00000000..03e810d8 --- /dev/null +++ b/dependencies/astron/config/eventlogger.yml @@ -0,0 +1,7 @@ +daemon: + name: Event Logger + +roles: + - type: eventlogger + bind: 127.0.0.1:7198 + output: ../logs/events-%y%m%d_%H%M%S.log diff --git a/dependencies/astron/config/prod-test.yml b/dependencies/astron/config/prod-test.yml new file mode 100644 index 00000000..9102559d --- /dev/null +++ b/dependencies/astron/config/prod-test.yml @@ -0,0 +1,64 @@ +daemon: + name: Developer Cluster + +general: + eventlogger: 0.0.0.0:7198 + dc_files: + - ../dclass/stride.dc + +messagedirector: + bind: 0.0.0.0:7100 + +uberdogs: + - class: ClientServicesManager + id: 4665 + anonymous: true + + - class: ChatAgent + id: 4681 + anonymous: false + + - class: FriendManager + id: 4501 + anonymous: false + + - class: TTSFriendsManager + id: 4666 + anonymous: false + + - class: GlobalPartyManager + id: 4477 + anonymous: false + +roles: + - type: clientagent + bind: 0.0.0.0:7199 + version: "tts-dev" + client: + relocate: true + add_interest: enabled + channels: + min: 1000000000 + max: 1000001000 + + - type: stateserver + control: 4002 + + - type: database + control: 4003 + generate: + min: 100000000 + max: 399999999 + backend: + type: mongodb + server: mongodb://127.0.0.1:27017/stride + + - type: dbss + database: 4003 + ranges: + - min: 100000000 + max: 399999999 + + - type: eventlogger + bind: 0.0.0.0:7198 + output: ../logs/events-%y%m%d_%H%M%S.log diff --git a/dependencies/astron/config/production-cluster.yml b/dependencies/astron/config/production-cluster.yml new file mode 100644 index 00000000..33256c09 --- /dev/null +++ b/dependencies/astron/config/production-cluster.yml @@ -0,0 +1,29 @@ +daemon: + name: Production Cluster + +general: + eventlogger: 127.0.0.1:7198 + dc_files: + - ../dclass/stride.dc + +messagedirector: + bind: 127.0.0.1:7100 + +roles: + - type: stateserver + control: 4002 + + - type: database + control: 4003 + generate: + min: 100000000 + max: 399999999 + backend: + type: mongodb + server: mongodb://127.0.0.1:27017/stride + + - type: dbss + database: 4003 + ranges: + - min: 100000000 + max: 399999999 diff --git a/dependencies/astron/config/uberdogs.yml b/dependencies/astron/config/uberdogs.yml new file mode 100755 index 00000000..d06019ae --- /dev/null +++ b/dependencies/astron/config/uberdogs.yml @@ -0,0 +1,20 @@ +uberdogs: + - class: ClientServicesManager + id: 4665 + anonymous: true + + - class: ChatAgent + id: 4681 + anonymous: false + + - class: FriendManager + id: 4501 + anonymous: false + + - class: TTSFriendsManager + id: 4666 + anonymous: false + + - class: GlobalPartyManager + id: 4477 + anonymous: false diff --git a/dependencies/astron/darwin/start-ai-server.sh b/dependencies/astron/darwin/start-ai-server.sh new file mode 100644 index 00000000..d49b8dcf --- /dev/null +++ b/dependencies/astron/darwin/start-ai-server.sh @@ -0,0 +1,35 @@ +#!/bin/sh +cd ../.. + +export DYLD_LIBRARY_PATH=`pwd`/Libraries.bundle +export DYLD_FRAMEWORK_PATH="Frameworks" + +# Define some constants for our AI server: +MAX_CHANNELS=999999 +STATESERVER=4002 +ASTRON_IP="127.0.0.1:7100" +EVENTLOGGER_IP="127.0.0.1:7198" + +# Get the user input: +read -p "District name (DEFAULT: Nuttyboro): " DISTRICT_NAME +DISTRICT_NAME=${DISTRICT_NAME:-Nuttyboro} +read -p "Base channel (DEFAULT: 401000000): " BASE_CHANNEL +BASE_CHANNEL=${BASE_CHANNEL:-401000000} + +echo "===============================" +echo "Starting Toontown Stride AI server..." +echo "District name: $DISTRICT_NAME" +echo "Base channel: $BASE_CHANNEL" +echo "Max channels: $MAX_CHANNELS" +echo "State Server: $STATESERVER" +echo "Astron IP: $ASTRON_IP" +echo "Event Logger IP: $EVENTLOGGER_IP" +echo "===============================" + +while [ true ] +do + ppython -m toontown.ai.ServiceStart --base-channel $BASE_CHANNEL \ + --max-channels $MAX_CHANNELS --stateserver $STATESERVER \ + --astron-ip $ASTRON_IP --eventlogger-ip $EVENTLOGGER_IP \ + --district-name $DISTRICT_NAME +done diff --git a/dependencies/astron/darwin/start-astron-cluster.sh b/dependencies/astron/darwin/start-astron-cluster.sh new file mode 100644 index 00000000..eaef6aea --- /dev/null +++ b/dependencies/astron/darwin/start-astron-cluster.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd .. +./astrond --loglevel info config/cluster.yml diff --git a/dependencies/astron/darwin/start-uberdog-server.sh b/dependencies/astron/darwin/start-uberdog-server.sh new file mode 100644 index 00000000..175a636b --- /dev/null +++ b/dependencies/astron/darwin/start-uberdog-server.sh @@ -0,0 +1,31 @@ +#!/bin/sh +cd ../.. + +export DYLD_LIBRARY_PATH=`pwd`/Libraries.bundle +export DYLD_FRAMEWORK_PATH="Frameworks" + +# Define some constants for our AI server: +MAX_CHANNELS=999999 +STATESERVER=4002 +ASTRON_IP="127.0.0.1:7100" +EVENTLOGGER_IP="127.0.0.1:7198" + +# Get the user input: +read -p "Base channel (DEFAULT: 1000000): " BASE_CHANNEL +BASE_CHANNEL=${BASE_CHANNEL:-1000000} + +echo "===============================" +echo "Starting Toontown Stride UberDOG server..." +echo "Base channel: $BASE_CHANNEL" +echo "Max channels: $MAX_CHANNELS" +echo "State Server: $STATESERVER" +echo "Astron IP: $ASTRON_IP" +echo "Event Logger IP: $EVENTLOGGER_IP" +echo "===============================" + +while [ true ] +do +ppython -m toontown.uberdog.ServiceStart --base-channel $BASE_CHANNEL \ + --max-channels $MAX_CHANNELS --stateserver $STATESERVER \ + --astron-ip $ASTRON_IP --eventlogger-ip $EVENTLOGGER_IP +done diff --git a/dependencies/astron/databases/.gitignore b/dependencies/astron/databases/.gitignore new file mode 100644 index 00000000..98e6ef67 --- /dev/null +++ b/dependencies/astron/databases/.gitignore @@ -0,0 +1 @@ +*.db diff --git a/dependencies/astron/databases/astrondb/.gitignore b/dependencies/astron/databases/astrondb/.gitignore new file mode 100755 index 00000000..c96a04f0 --- /dev/null +++ b/dependencies/astron/databases/astrondb/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/dependencies/astron/dclass/stride.dc b/dependencies/astron/dclass/stride.dc new file mode 100644 index 00000000..c7c5e205 --- /dev/null +++ b/dependencies/astron/dclass/stride.dc @@ -0,0 +1,3238 @@ +from direct.distributed import DistributedObject/AI/UD +from direct.distributed import DistributedObjectGlobal +from direct.distributed import DistributedNode/AI/UD +from direct.distributed import DistributedSmoothNode/AI +from direct.distributed import DistributedCartesianGrid/AI +from direct.distributed import DistributedCamera/AI/OV +from otp.distributed import Account/AI/UD +from otp.ai import TimeManager/AI +from otp.ai import MagicWordManager/AI +from otp.avatar import DistributedAvatar/AI/UD +from otp.avatar import DistributedPlayer/AI +from otp.friends import FriendManager/AI +from otp.distributed import DistributedDistrict/AI/UD +from otp.distributed import DistributedDirectory/AI +from otp.chat import ChatAgent/UD + +typedef uint8 bool; +typedef uint32 DoId; +typedef DoId DoIdList[]; + +struct AvatarPendingDel { + uint32 avId; + uint32 date; +}; + +dclass Account { + uint32 ACCOUNT_AV_SET[] required db; + uint32 ESTATE_ID db; + AvatarPendingDel ACCOUNT_AV_SET_DEL[] db; + string CREATED db; + string LAST_LOGIN db; + string ACCOUNT_ID db; + uint16 ACCESS_LEVEL db; + uint32 LAST_LOGIN_TS db; + uint8[] CHAT_SETTINGS db; +}; + +struct BarrierData { + uint16 context; + string name; + DoIdList avIds; +}; + +dclass DistributedObject { + setBarrierData(BarrierData []) broadcast ram; + setBarrierReady(uint16 barrierContext) airecv clsend; + execCommand(string command, DoId magicWordMgr, DoId avatar, uint32 zoneId); + broadcastMessage() broadcast; +}; + +dclass DistributedObjectGlobal : DistributedObject { +}; + +dclass TimeManager : DistributedObject { + requestServerTime(uint8 context) airecv clsend; + serverTime(uint8 context, int32 timestamp, uint32 timeOfDay); + setDisconnectReason(uint8) airecv clsend; + setExceptionInfo(string(0-1024)) airecv clsend; +}; + +dclass DistributedDirectory : DistributedObject {}; + +dclass DistributedDistrict : DistributedObject { + setName(string) required broadcast ram; + setAvailable(uint8) required broadcast ram; +}; + +dclass DistributedNode : DistributedObject { + setParentStr(blob) broadcast ram ownsend airecv; + setParent(uint32) broadcast ram ownsend airecv; + setX(int16/10) broadcast ram ownsend airecv; + setY(int16/10) broadcast ram ownsend airecv; + setZ(int16/10) broadcast ram ownsend airecv; + setH(int16%360/10) broadcast ram ownsend airecv; + setP(int16%360/10) broadcast ram ownsend airecv; + setR(int16%360/10) broadcast ram ownsend airecv; + setPos : setX, setY, setZ; + setHpr : setH, setP, setR; + setPosHpr : setX, setY, setZ, setH, setP, setR; + setXY : setX, setY; + setXZ : setX, setZ; + setXYH : setX, setY, setH; + setXYZH : setX, setY, setZ, setH; +}; + +dclass DistributedSmoothNode : DistributedNode { + setComponentL(uint64) broadcast ram ownsend airecv; + setComponentX(int16/10) broadcast ram ownsend airecv; + setComponentY(int16/10) broadcast ram ownsend airecv; + setComponentZ(int16/10) broadcast ram ownsend airecv; + setComponentH(int16%360/10) broadcast ram ownsend airecv; + setComponentP(int16%360/10) broadcast ram ownsend airecv; + setComponentR(int16%360/10) broadcast ram ownsend airecv; + setComponentT(int16) broadcast ram ownsend airecv; + setSmStop : setComponentT; + setSmH : setComponentH, setComponentT; + setSmZ : setComponentZ, setComponentT; + setSmXY : setComponentX, setComponentY, setComponentT; + setSmXZ : setComponentX, setComponentZ, setComponentT; + setSmPos : setComponentX, setComponentY, setComponentZ, setComponentT; + setSmHpr : setComponentH, setComponentP, setComponentR, setComponentT; + setSmXYH : setComponentX, setComponentY, setComponentH, setComponentT; + setSmXYZH : setComponentX, setComponentY, setComponentZ, setComponentH, setComponentT; + setSmPosHpr : setComponentX, setComponentY, setComponentZ, setComponentH, setComponentP, setComponentR, setComponentT; + setSmPosHprL : setComponentL, setComponentX, setComponentY, setComponentZ, setComponentH, setComponentP, setComponentR, setComponentT; + clearSmoothing(int8) broadcast ownsend; + suggestResync(uint32, int16, int16, int32, uint16, uint16/100) ownrecv clsend; + returnResync(uint32, int16, int32, uint16, uint16/100) ownrecv clsend; +}; + +dclass DistributedCartesianGrid : DistributedNode { + setCellWidth(uint32) required broadcast ram; + setParentingRules(string, string) broadcast ram; +}; + +struct Fixture { + int32/10 x; + int32/10 y; + int32/10 z; + int16/10 h; + int16/10 p; + int16/10 r; + string state; +}; + +dclass DistributedCamera : DistributedNode { + setCamParent(uint32) required broadcast ram ownsend airecv; + setFixtures(Fixture []) required broadcast ram ownsend airecv; +}; + +dclass DistributedAvatar : DistributedSmoothNode { + setName(string = "[Name not set]") required broadcast db airecv; + friendsNotify(DoId avId, int8 status) ownrecv airecv; + checkAvOnShard(DoId) clsend airecv; + confirmAvOnShard(DoId avId, int8 isOnShard); + setTalk(string(0-400) chat) broadcast ram; + setTalkWhisper(uint32 avId, string(0-400) chat) ownrecv clsend; +}; + +struct FriendEntry { + uint32 friendId; + uint8 friendType; +} + +dclass DistributedPlayer : DistributedAvatar { + arrivedOnDistrict(DoId districtId) ownrecv ram; + setWhisperSCFrom(DoId fromAv, uint16 msgIndex) ownrecv clsend; + setWhisperSCCustomFrom(DoId fromAv, uint16 msgIndex) ownrecv clsend; + setWhisperSCEmoteFrom(DoId fromAv, uint16 emoteId) ownrecv clsend; + setSystemMessage(DoId aboutId, string(0-256) chatString) ownrecv; + setSC(uint16 msgIndex) broadcast ownsend airecv; + setSCCustom(uint16 msgIndex) broadcast ownsend airecv; + setFriendsList(uint32[] = []) ownrecv required db airecv; + setDISLid(uint32 = 0) airecv ram db; + setWishName(string = "") db ram; + setWishNameState(string = "OPEN") db ram; + setAdminAccess(uint16 = 0) required broadcast ownrecv db; +}; + +dclass MagicWordManager : DistributedObject { + sendMagicWord(string, uint32) airecv clsend; + sendMagicWordResponse(string); +}; + +dclass ChatAgent : DistributedObject { + chatMessage(string(0-256) message, uint8 chatMode) clsend; +}; + +dclass FriendManager : DistributedObject { + friendQuery(int32) airecv clsend; + cancelFriendQuery(int32) airecv clsend; + inviteeFriendConsidering(int8, int32) airecv clsend; + inviteeFriendResponse(int8, int32) airecv clsend; + inviteeAcknowledgeCancel(int32) airecv clsend; + friendConsidering(int8, int32); + friendResponse(int8, int32); + inviteeFriendQuery(int32, string, blob, int32); + inviteeCancelFriendQuery(int32); + requestTFCode() airecv clsend; + redeemTFCode(string) airecv clsend; + tfResponse(uint8, string); +}; + +from toontown.building import DistributedAnimatedProp/AI +from toontown.toon import DistributedToon/AI/UD +from toontown.safezone import DistributedTrolley/AI +from toontown.safezone import DistributedPartyGate/AI +from toontown.suit import DistributedSuitPlanner/AI +from toontown.suit import DistributedSuitBase/AI +from toontown.suit import DistributedSuit/AI +from toontown.suit import DistributedTutorialSuit/AI +from toontown.suit import DistributedFactorySuit/AI +from toontown.suit import DistributedMintSuit/AI +from toontown.suit import DistributedStageSuit/AI +from toontown.suit import DistributedSellbotBoss/AI +from toontown.suit import DistributedCashbotBoss/AI +from toontown.coghq import DistributedCashbotBossSafe/AI +from toontown.coghq import DistributedCashbotBossCrane/AI +from toontown.suit import DistributedCashbotBossGoon/AI +from toontown.battle import DistributedBattleBase/AI +from toontown.battle import DistributedBattle/AI +from toontown.battle import DistributedBattleBldg/AI +from toontown.tutorial import DistributedBattleTutorial/AI +from toontown.coghq import DistributedBattleFactory/AI +from toontown.battle import DistributedBattleFinal/AI +from toontown.safezone import DistributedBoat/AI +from toontown.safezone import DistributedButterfly/AI +from toontown.safezone import DistributedMMPiano/AI +from toontown.safezone import DistributedDGFlower/AI +from toontown.fishing import DistributedFishingPond/AI +from toontown.fishing import DistributedFishingTarget/AI +from toontown.fishing import DistributedPondBingoManager/AI +from toontown.safezone import DistributedFishingSpot/AI +from toontown.estate import DistributedCannon/AI +from toontown.estate import DistributedTarget/AI +from toontown.minigame import DistributedMinigame/AI +from toontown.minigame import DistributedMinigameTemplate/AI +from toontown.minigame import DistributedRaceGame/AI +from toontown.minigame import DistributedCannonGame/AI +from toontown.minigame import DistributedPatternGame/AI +from toontown.minigame import DistributedRingGame/AI +from toontown.minigame import DistributedTagGame/AI +from toontown.minigame import DistributedMazeGame/AI +from toontown.minigame import DistributedTugOfWarGame/AI +from toontown.minigame import DistributedCatchGame/AI +from toontown.minigame import DistributedDivingGame/AI +from toontown.minigame import DistributedTargetGame/AI +from toontown.estate import EstateManager/AI +from toontown.estate import DistributedEstate/AI +from toontown.estate import DistributedHouse/AI +from toontown.estate import DistributedHouseInterior/AI +from toontown.estate import DistributedGarden/AI +from toontown.shtiker import DeleteManager/AI +from toontown.ai import NewsManager/AI +from toontown.shtiker import PurchaseManager/AI +from toontown.shtiker import NewbiePurchaseManager/AI +from toontown.safezone import SafeZoneManager/AI +from toontown.tutorial import TutorialManager/AI +from toontown.catalog import CatalogManager/AI +from toontown.safezone import DistributedTreasure/AI +from toontown.safezone import DistributedEFlyingTreasure/AI +from toontown.coghq import DistributedCashbotBossTreasure/AI +from toontown.building import DistributedTrophyMgr/AI +from toontown.building import DistributedBuilding/AI +from toontown.building import DistributedBuildingQueryMgr/AI +from toontown.building import DistributedToonInterior/AI +from toontown.building import DistributedToonHallInterior/AI +from toontown.building import DistributedSuitInterior/AI +from toontown.building import DistributedHQInterior/AI +from toontown.building import DistributedGagshopInterior/AI +from toontown.building import DistributedPetshopInterior/AI +from toontown.building import DistributedKartShopInterior/AI +from toontown.building import DistributedDoor/AI +from toontown.estate import DistributedHouseDoor/AI +from toontown.coghq import DistributedCogHQDoor/AI +//from toontown.coghq import DistributedCogHQExteriorDoor/AI +from toontown.coghq import DistributedSellbotHQDoor/AI +from toontown.toon import DistributedNPCToonBase/AI +from toontown.toon import DistributedNPCToon/AI +from toontown.toon import DistributedNPCSpecialQuestGiver/AI +from toontown.toon import DistributedNPCFlippyInToonHall/AI +from toontown.toon import DistributedNPCScientist/AI +from toontown.toon import DistributedNPCClerk/AI +from toontown.toon import DistributedNPCTailor/AI +from toontown.toon import DistributedNPCBlocker/AI +from toontown.toon import DistributedNPCFisherman/AI +from toontown.toon import DistributedNPCPartyPerson/AI +from toontown.toon import DistributedNPCPetclerk/AI +from toontown.toon import DistributedNPCKartClerk/AI +from toontown.toon import DistributedNPCGlove/AI +from toontown.toon import DistributedNPCLaffRestock/AI +from toontown.building import DistributedKnockKnockDoor/AI +from toontown.building import DistributedElevator/AI +from toontown.building import DistributedElevatorFSM/AI +from toontown.building import DistributedElevatorExt/AI +from toontown.building import DistributedElevatorInt/AI +from toontown.coghq import DistributedFactoryElevatorExt/AI +from toontown.coghq import DistributedMintElevatorExt/AI +from toontown.coghq import DistributedLawOfficeElevatorExt/AI +from toontown.coghq import DistributedLawOfficeElevatorInt/AI +from toontown.building import DistributedElevatorFloor/AI +from toontown.building import DistributedBossElevator/AI +from toontown.building import DistributedVPElevator/AI +from toontown.building import DistributedCFOElevator/AI +from toontown.building import DistributedCJElevator/AI +from toontown.building import DistributedBBElevator/AI +from toontown.building import DistributedBoardingParty/AI +from toontown.building import DistributedTutorialInterior/AI +from toontown.estate import DistributedMailbox/AI +from toontown.estate import DistributedFurnitureManager/AI +from toontown.estate import DistributedFurnitureItem/AI +from toontown.estate import DistributedBank/AI +from toontown.estate import DistributedCloset/AI +from toontown.estate import DistributedTrunk/AI +from toontown.estate import DistributedPhone/AI +from toontown.estate import DistributedRewardCrate/AI +from toontown.estate import DistributedChair/AI +from toontown.estate import DistributedTV/AI +from toontown.effects import DistributedFireworkShow/AI +from toontown.estate import DistributedFireworksCannon/AI +from toontown.coghq import LobbyManager/AI +from otp.level import DistributedLevel/AI +from otp.level import DistributedEntity/AI +from otp.level import DistributedInteractiveEntity/AI +from toontown.coghq import DistributedMegaCorp/AI +from toontown.coghq import DistributedFactory/AI +from toontown.coghq import DistributedLawOffice/AI +from toontown.coghq import DistributedLawOfficeFloor/AI +from toontown.coghq import DistributedLift/AI +from toontown.coghq import DistributedDoorEntity/AI +from toontown.coghq import DistributedSwitch/AI +from toontown.coghq import DistributedButton/AI +from toontown.coghq import DistributedTrigger/AI +from toontown.coghq import DistributedCrushableEntity/AI +from toontown.coghq import DistributedCrusherEntity/AI +from toontown.coghq import DistributedStomper/AI +from toontown.coghq import DistributedStomperPair/AI +from toontown.coghq import DistributedLaserField/AI +from toontown.coghq import DistributedGolfGreenGame/AI +from toontown.coghq import DistributedSecurityCamera/AI +from toontown.coghq import DistributedMover/AI +from toontown.coghq import DistributedElevatorMarker/AI +from toontown.coghq import DistributedBarrelBase/AI +from toontown.coghq import DistributedGagBarrel/AI +from toontown.coghq import DistributedBeanBarrel/AI +from toontown.coghq import DistributedHealBarrel/AI +from toontown.coghq import DistributedGrid/AI +from toontown.coghq import ActiveCell/AI +from toontown.coghq import DirectionalCell/AI +from toontown.coghq import CrusherCell/AI +from toontown.coghq import DistributedCrate/AI +from toontown.coghq import DistributedSinkingPlatform/AI +from toontown.suit import DistributedGoon/AI +from toontown.suit import DistributedGridGoon/AI +from toontown.coghq import BattleBlocker/AI +from toontown.ai import DistributedBlackCatMgr/AI +from toontown.ai import DistributedReportMgr/AI +from toontown.ai import DistributedPolarPlaceEffectMgr/AI +from toontown.ai import DistributedEffectMgr/AI +from toontown.ai import DistributedResistanceEmoteMgr/AI +from toontown.coghq import DistributedMint/AI +from toontown.coghq import DistributedMintRoom/AI +from toontown.coghq import DistributedMintBattle/AI +from toontown.coghq import DistributedStage/AI +from toontown.coghq import DistributedStageRoom/AI +from toontown.coghq import DistributedStageBattle/AI +from toontown.pets.DistributedPet/AI import * +from toontown.pets import DistributedPetProxy/AI +from toontown.distributed import ToontownDistrict/AI +from toontown.distributed import ToontownDistrictStats/AI +from toontown.racing import DistributedVehicle/AI +from toontown.racing import DistributedStartingBlock/AI +from toontown.racing import DistributedRace/AI +from toontown.racing import DistributedKartPad/AI +from toontown.racing import DistributedRacePad/AI +from toontown.racing import DistributedViewPad/AI +from toontown.racing import DistributedStartingBlock/AI +from toontown.racing import DistributedLeaderBoard/AI +from toontown.racing import DistributedGag/AI +from toontown.racing import DistributedProjectile/AI +from toontown.racing.DistributedStartingBlock/AI import DistributedViewingBlock/AI +from toontown.uberdog.ClientServicesManager/UD import ClientServicesManager/UD +from toontown.suit import DistributedLawbotBoss/AI +from toontown.coghq import DistributedLawbotBossGavel/AI +from toontown.suit import DistributedLawbotBossSuit/AI +from toontown.coghq import DistributedLawbotCannon/AI +from toontown.coghq import DistributedLawbotChair/AI +from toontown.estate import DistributedLawnDecor/AI +from toontown.estate import DistributedGardenPlot/AI +from toontown.estate import DistributedGardenBox/AI +from toontown.estate import DistributedFlower/AI +from toontown.estate import DistributedGagTree/AI +from toontown.estate import DistributedStatuary/AI +from toontown.estate import DistributedToonStatuary/AI +from toontown.estate import DistributedChangingStatuary/AI +from toontown.estate import DistributedAnimatedStatuary/AI +from toontown.estate import DistributedPlantBase/AI +from toontown.estate import DistributedLawnDecor/AI +from toontown.minigame import DistributedVineGame/AI +from toontown.golf import DistributedPhysicsWorld/AI +from toontown.golf import DistributedGolfHole/AI +from toontown.golf import DistributedGolfCourse/AI +from toontown.parties import DistributedParty/AI +from toontown.parties import DistributedPartyActivity/AI +from toontown.parties import DistributedPartyTeamActivity/AI +from toontown.parties import DistributedPartyCannon/AI +from toontown.parties import DistributedPartyCannonActivity/AI +from toontown.parties import DistributedPartyCatchActivity/AI +from toontown.parties import DistributedPartyWinterCatchActivity/AI +from toontown.parties import DistributedPartyCogActivity/AI +from toontown.parties import DistributedPartyWinterCogActivity/AI +from toontown.parties import DistributedPartyFireworksActivity/AI +from toontown.parties import DistributedPartyDanceActivityBase/AI +from toontown.parties import DistributedPartyDanceActivity/AI +from toontown.parties import DistributedPartyDance20Activity/AI +from toontown.parties import DistributedPartyValentineDanceActivity/AI +from toontown.parties import DistributedPartyValentineDance20Activity/AI +from toontown.parties import DistributedPartyTrampolineActivity/AI +from toontown.parties import DistributedPartyValentineTrampolineActivity/AI +from toontown.parties import DistributedPartyVictoryTrampolineActivity/AI +from toontown.parties import DistributedPartyWinterTrampolineActivity/AI +from toontown.parties import DistributedPartyTugOfWarActivity/AI +from toontown.parties import DistributedPartyJukeboxActivityBase/AI +from toontown.parties import DistributedPartyJukeboxActivity/AI +from toontown.parties import DistributedPartyJukebox40Activity/AI +from toontown.parties import DistributedPartyValentineJukeboxActivity/AI +from toontown.parties import DistributedPartyValentineJukebox40Activity/AI +from toontown.friends import TTSFriendsManager/UD +from toontown.safezone import DistributedGolfKart/AI +from toontown.safezone import DistributedPicnicBasket/AI +from toontown.distributed import DistributedTimer/AI +from toontown.suit import DistributedBossbotBoss/AI +from toontown.coghq import DistributedCogKart/AI +from toontown.coghq import DistributedCountryClub/AI +from toontown.coghq import DistributedCountryClubRoom/AI +from toontown.coghq import DistributedMoleField/AI +from toontown.coghq import DistributedCountryClubBattle/AI +from toontown.building import DistributedClubElevator/AI +from toontown.coghq import DistributedMaze/AI +from toontown.battle import DistributedBattleWaiters/AI +from toontown.coghq import DistributedFoodBelt/AI +from toontown.coghq import DistributedBanquetTable/AI +from toontown.battle import DistributedBattleDiners/AI +from toontown.coghq import DistributedGolfSpot/AI +from toontown.minigame import DistributedIceGame/AI +from toontown.minigame import DistributedCogThiefGame/AI +from toontown.minigame import DistributedTwoDGame/AI +from toontown.safezone import DistributedPicnicTable/AI +from toontown.safezone import DistributedChineseCheckers/AI +from toontown.safezone import DistributedCheckers/AI +from toontown.safezone import DistributedFindFour/AI +from toontown.uberdog.DistributedPartyManager/AI/UD import DistributedPartyManager/AI/UD +//from toontown.uberdog.DistributedLobbyManager/AI/UD import DistributedLobbyManager/AI/UD +from toontown.coderedemption.TTCodeRedemptionMgr/AI import TTCodeRedemptionMgr/AI +from toontown.cogdominium import DistributedCogdoInterior/AI +from toontown.cogdominium import DistributedCogdoBattleBldg/AI +from toontown.cogdominium import DistributedCogdoElevatorExt/AI +from toontown.cogdominium import DistributedCogdoElevatorInt/AI +from toontown.cogdominium import DistributedCogdoBarrel/AI +from toontown.cogdominium import DistCogdoGame/AI +from toontown.cogdominium import DistCogdoLevelGame/AI +from toontown.cogdominium import DistCogdoBoardroomGame/AI +from toontown.cogdominium import DistCogdoCraneGame/AI +from toontown.cogdominium import DistCogdoMazeGame/AI +from toontown.cogdominium import DistCogdoFlyingGame/AI +from toontown.cogdominium import DistCogdoCrane/AI +from toontown.cogdominium import DistCogdoCraneMoneyBag/AI +from toontown.cogdominium import DistCogdoCraneCog/AI +from toontown.parties.GlobalPartyManager/AI/UD import GlobalPartyManager/AI/UD +//from toontown.uberdog.GlobalLobbyManager/AI/UD import GlobalLobbyManager/AI/UD +from toontown.uberdog.ARGManager import ARGManager + +struct gardenSpecial { + uint8 index; + uint8 count; +}; + +struct simpleMail { + uint64 msgId; + uint32 senderId; + uint16 year; + uint8 month; + uint8 day; + string body; +}; + +struct invite { + uint64 inviteKey; + uint64 partyId; + uint8 status; +}; + +struct decoration { + uint8 decorId; + uint8 x; + uint8 y; + uint8 h; +}; + +struct activity { + uint8 activityId; + uint8 x; + uint8 y; + uint8 h; +}; + +//struct lobby { +// uint64 lobbyId; +// uint32 hostId; +//}; + +struct party { + uint64 partyId; + uint32 hostId; + uint16 startYear; + uint8 startMonth; + uint8 startDay; + uint8 startHour; + uint8 startMinute; + uint16 endYear; + uint8 endMonth; + uint8 endDay; + uint8 endHour; + uint8 endMinute; + uint8 isPrivate; + uint8 inviteTheme; + activity activities[]; + decoration decors[]; + uint8 status; +}; + +struct partyReply { + uint32 inviteeId; + uint8 status; +}; + +struct repliesForOneParty { + uint64 partyId; + partyReply partyReplies[]; +}; + +struct publicPartyInfo { + uint32 shardId; + uint32 zoneId; + uint8 numberOfGuests; + string hostName; + uint8[] activityIds; + uint16 minLeft; +}; + +struct jukeboxSongInfo { + uint8/10 phase; + string fileName; +}; + +struct partyCloudColor { + uint16 cloudNumber; + uint8/100 r; + uint8/100 g; + uint8/100 b; +}; + +struct datetime { + uint16 year; + uint8 month; + uint8 day; + uint8 hour; + uint8 minutes; + uint8 seconds; +}; + +dclass ToontownDistrict : DistributedDistrict { + setParentingRules(string, string) broadcast ram; +}; + +dclass ToontownDistrictStats : DistributedObject { + setDistrictId(uint32) broadcast required ram; + setAvatarCount(uint32) broadcast required ram; + setInvasionStatus(uint8) broadcast required ram; + setGroupAvCount(uint32[]) broadcast required ram; +}; + +dclass DistributedAnimatedProp : DistributedObject { + setPropId(uint16) required broadcast ram; + setAvatarInteract(uint32) required broadcast ram; + requestInteract() airecv clsend; + rejectInteract(); + requestExit() airecv clsend; + avatarExit(uint32) broadcast; + setState(string, int16) required broadcast ram; +}; + +typedef int16 pair16[2]; + +dclass DistributedToon : DistributedPlayer { + setDNAString(blob) required broadcast ownrecv db; + setMaxBankMoney(int16 maxMoney = 10000) required broadcast ownrecv db; + setBankMoney(int16 money = 0) required broadcast ownrecv db; + setMaxMoney(int16 maxMoney = 40) required broadcast ownrecv db; + setMoney(int16 money = 0) required broadcast ownrecv db; + setMaxHp(int16 = 15) required broadcast ownrecv db; + setHp(int16 = 15) required broadcast ownrecv db; + toonUp(uint16) broadcast ownrecv; + takeDamage(uint16) broadcast ownrecv; + setBattleId(uint32 = 0) required broadcast ram; + setExperience(blob = [0*14]) required broadcast db; + setIgnored(uint32[] = []) required clsend airecv ownrecv db; + setReported(uint32[] = []) required ownrecv db; + setMaxCarry(uint8 = 20) required ownrecv db; + setTrackAccess(uint16[] = [0,0,0,0,1,1,0]) required broadcast ownrecv db; + setTrackProgress(int8 = -1, uint32 = 0) required ownrecv db; + setTrackBonusLevel(int8[] = [-1,-1,-1,-1,-1,-1,-1]) required broadcast ownrecv db; + setInventory(blob = [0*7, 0*7, 0*7, 0*7, 0, 0*6, 0, 0*6, 0*7]) required broadcast ownrecv db; + setNPCFriendsDict(FriendEntry[] = []) required broadcast ownrecv db; + setDefaultShard(uint32 = 0) required ownrecv broadcast db; + setDefaultZone(uint32 = 0) required ownrecv broadcast db; + setHoodsVisited(uint32[] = [ 2000 ]) required ownrecv db; + setLastHood(uint32 = 0) required ownrecv broadcast db; + setTutorialAck(uint8) required ownrecv db; + setMaxClothes(uint32 = 10) required ownrecv db; + setClothesTopsList(uint8[] = []) required ownrecv db; + setClothesBottomsList(uint8[] = []) required ownrecv db; + setHatList(uint8[] = []) required ownrecv db; + setGlassesList(uint8[] = []) required ownrecv db; + setBackpackList(uint8[] = []) required ownrecv db; + setShoesList(uint8[] = []) required ownrecv db; + setHat(uint8 = 0, uint8 = 0, uint8 = 0) required broadcast db ownrecv; + setGlasses(uint8 = 0, uint8 = 0, uint8 = 0) required broadcast db ownrecv; + setBackpack(uint8 = 0, uint8 = 0, uint8 = 0) required broadcast db ownrecv; + setShoes(uint8 = 0, uint8 = 0, uint8 = 0) required broadcast db ownrecv; + setGardenSpecials(gardenSpecial [] = []) required ownrecv db airecv; + setEarnedExperience(uint16[]) ownrecv; + setTunnelIn(int16, int16/10, int16/10, int16/10, int16/100, int32/100) ownsend broadcast; + setTunnelOut(int16, int16/10, int16/10, int16/10, int16/10, int16/100, int32/100) ownsend broadcast; + setAnimState(char [0-1024], int16/1000, int16) broadcast ram ownsend airecv; + setEmoteState(int16, int16/1000, int16) broadcast ram ownsend; + setEmoteAccess(uint8[] = [1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]) required ownrecv db; + setCustomMessages(uint16[] = []) required ownrecv db; + setSleepAutoReply(uint32) broadcast clsend ownrecv; + setResistanceMessages(pair16 [] = []) required ownrecv db; + setPetTrickPhrases(uint8[] = [0]) required ownrecv db; + setCatalogSchedule(uint16 = 0, uint32 = 0) required ownrecv db; + setCatalog(blob = [], blob = [], blob = []) required ownrecv db; + setMailboxContents(blob = []) required ownrecv db; + setDeliverySchedule(blob = []) required ownrecv db airecv; + setGiftSchedule(blob = []) required ownrecv db airecv; + setAwardMailboxContents(blob = []) required ownrecv db; + setAwardSchedule(blob = []) required ownrecv db airecv; + setAwardNotify(uint8 = 0) required ownrecv db; + setCatalogNotify(uint8 = 0, uint8 = 0) required ownrecv db; + playSplashEffect(int16/10, int16/10, int16/10) broadcast ownsend; + setWhisperSCToontaskFrom(uint32, uint32, uint32, uint32, uint8) ownrecv clsend; + setSCToontask(uint32, uint32, uint32, uint8) broadcast ownsend; + reqSCResistance(uint16, uint32 []) ownsend airecv; + setSCResistance(uint16, uint32 []) broadcast ownrecv; + setSpeedChatStyleIndex(uint8 = 1) required ownsend broadcast db; + setTrophyScore(uint16) broadcast ownrecv ram; + setTeleportAccess(uint32[] = []) required ownrecv db; + setScavengerHunt(uint16[] = []) required ownrecv db; + checkTeleportAccess(uint16) airecv ownsend; + setTeleportOverride(uint8) clsend airecv; + battleSOS(uint32) ownrecv clsend; + teleportQuery(uint32) ownrecv clsend; + teleportResponse(uint32, int8, uint32, uint32, uint32) ownrecv clsend; + teleportResponseToAI(uint32, int8, uint32, uint32, uint32, uint32) ownsend airecv; + teleportGiveup(uint32) ownrecv clsend; + teleportGreeting(uint32) broadcast ownsend; + setCogStatus(uint32[] = [1 * 32]) required ownrecv db; + setCogCount(uint32[] = [0 * 32]) required ownrecv db; + setCogRadar(uint8[] = [0 * 4]) required ownrecv db; + setBuildingRadar(uint8[] = [0 * 4]) required ownrecv db; + setCogLevels(uint8[] = [0 * 4]) required broadcast ownrecv db; + setCogTypes(uint8[] = [0 * 4]) required broadcast ownrecv db; + setCogParts(uint32[] = [0 * 4]) required broadcast ownrecv db; + setCogMerits(uint16[] = [0 * 4]) required broadcast ownrecv db; + setCogIndex(int8) broadcast ram; + setDisguisePageFlag(int8) ownrecv; + setSosPageFlag(int8) ownrecv; + setHouseId(uint32 = 0) required ownrecv db; + setQuests(uint32[] = []) required broadcast ownrecv db; + setQuestHistory(uint16[] = []) required ownrecv db; + setRewardHistory(uint8 = 0, uint16[] = []) required ownrecv db; + setQuestCarryLimit(uint8 = 1) required ownrecv db; + requestDeleteQuest(uint32[]) ownsend airecv; + setCheesyEffect(int16 = 0, uint32 = 0, uint32 = 0) required broadcast ownrecv db; + setGhostMode(uint8) broadcast ownrecv ram; + setFishCollection(uint8[] = [], uint8[] = [], uint16[] = []) required ownrecv db; + setMaxFishTank(uint8 = 20) required ownrecv db; + setFishTank(uint8[] = [], uint8[] = [], uint16[] = []) required ownrecv db; + setFishingRod(uint8 = 0) required broadcast ownrecv db; + setMaxFishingRod(uint8 = 0) required broadcast ownrecv db; + setFishingTrophies(uint8[] = []) required ownrecv db; + setFlowerCollection(uint8[] = [], uint8[] = []) required ownrecv db; + setFlowerBasket(uint8[] = [], uint8[] = []) required ownrecv db; + setMaxFlowerBasket(uint8 = 20) required ownrecv db; + setGardenTrophies(uint8[] = []) required ownrecv db; + setShovel(uint8 = 0) required broadcast ownrecv db; + setShovelSkill(uint32 = 0) required ownrecv db; + setWateringCan(uint8 = 0) required broadcast ownrecv db; + setWateringCanSkill(uint32 = 0) required ownrecv db; + promoteShovel(uint8) ownrecv; + promoteWateringCan(uint8) ownrecv; + reactivateWater() ownrecv; + presentPie(int16/10 x, int16/10 y, int16/10 z, int16/10 h, int32 timestamp) broadcast ownsend; + tossPie(int16/10 x, int16/10 y, int16/10 z, int16/10 h, uint8 sequence, uint8 power, uint8 throwType, int32 timestamp) broadcast ownsend; + pieSplat(int16/10, int16/10, int16/10, uint8, uint8, int32) broadcast ownsend; + setPieType(uint8) broadcast ownrecv ram; + setNumPies(uint16) broadcast ownrecv ram; + catalogGenClothes(uint32) broadcast ownrecv; + catalogGenAccessories(uint32) broadcast ownrecv; + setPetId(uint32 = 0) required broadcast ownrecv db; + setPetMovie(uint32, uint8) ownsend airecv; + setPetTutorialDone(uint8 = 0) required ownsend airecv db; + setFishBingoTutorialDone(uint8 = 0) required ownsend airecv db; + setFishBingoMarkTutorialDone(uint8 = 0) required ownsend airecv db; + setKartBodyType(int8 = -1) required broadcast ownrecv db; + setKartBodyColor(int8 = -1) required broadcast ownrecv db; + setKartAccessoryColor(int8 = -1) required broadcast ownrecv db; + setKartEngineBlockType(int8 = -1) required broadcast ownrecv db; + setKartSpoilerType(int8 = -1) required broadcast ownrecv db; + setKartFrontWheelWellType(int8 = -1) required broadcast ownrecv db; + setKartBackWheelWellType(int8 = -1) required broadcast ownrecv db; + setKartRimType(int8 = -1) required broadcast ownrecv db; + setKartDecalType(int8 = -1) required broadcast ownrecv db; + updateKartDNAField(int8, int8) ownsend airecv; + addOwnedAccessory(int8) ownsend airecv; + removeOwnedAccessory(int8) ownsend airecv; + setTickets(uint32 = 200) required broadcast ownrecv db; + setKartingHistory(uint8 [16] = [0*16]) required ownrecv db; + setKartingTrophies(uint8 [33] = [0*33]) required ownrecv db; + setKartingPersonalBest(uint32/1000 [6] = [0*6]) required ownrecv db; + setKartingPersonalBest2(uint32/1000 [12] = [0*12]) required ownrecv db; + setKartAccessoriesOwned(int8 [16] = [-1*16]) required broadcast ownrecv db; + setCurrentKart(uint32) broadcast ownrecv ram; + squish(uint8) ownsend airecv; + announceBingo() broadcast ownrecv; + setCogSummonsEarned(uint8[] = [0*32]) required ownrecv db; + reqCogSummons(char [0-256], uint32) ownsend airecv; + cogSummonsResponse(string, uint32, uint32) ownrecv; + reqUseSpecial(int32) ownsend airecv; + useSpecialResponse(string) ownrecv; + setGardenStarted(uint8 = 0) required ownrecv db; + sendToGolfCourse(uint32) ownrecv; + setGolfHistory(uint16 [18] = [0*18]) required ownrecv db; + setPackedGolfHoleBest(uint8 [18] = [0*18]) required ownrecv db; + setGolfCourseBest(uint8 [3] = [0*3]) required ownrecv db; + setUnlimitedSwing(uint8) broadcast ownrecv ram; + logSuspiciousEvent(char [0-1024]) ownsend airecv; + forceLogoutWithNotify() ownrecv; + setSpecialInventory(uint8[] = [0, 0]) required ownrecv db; + setNametagStyle(uint8 = 0) required broadcast ownrecv db; + setNametagStyles(uint8[] = [0]) required broadcast ownrecv db; + setMail(simpleMail []) ownrecv; + setNumMailItems(uint32) airecv; + setSimpleMailNotify(uint8) ownrecv airecv; + setInvites(invite []) ownrecv airecv ram; + setPartiesInvitedTo(party []) ownrecv airecv ram; + setHostedParties(party []) ownrecv airecv ram; + setPartyReplies(repliesForOneParty []) ownrecv airecv ram; + updateInvite(uint64, uint8) ownrecv airecv; + updateReply(uint64, uint64, uint8) ownrecv airecv; + setPartyCanStart(uint64) ownrecv airecv; + setPartyStatus(uint64, uint8) ownrecv airecv; + announcePartyStarted(uint64) ownrecv; + setNeverStartedPartyRefunded(uint64, int8, uint16) ownrecv; + setAnimalSound(uint8 index) ram broadcast ownrecv; + setBuffs(uint32[] = []) required ownrecv db; + setRedeemedCodes(string [] = []) required ownrecv db; + setEmblems(uint32[] = [0, 0]) required ownrecv db; + setTrueFriends(uint32[] = []) required ownrecv db; + setNextKnockHeal(uint32) ram airecv; + setTFRequest(uint32[] = [0, 0]) ram airecv; + setEPP(uint8[] = []) required ownrecv db; + setStats(uint32[] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) required ownrecv db; + requestNametagStyle(uint8) airecv ownsend; + requestFishingRod(uint8) airecv ownsend; + wipeStats() airecv ownsend; + takeMoney(int16) airecv ownsend; +}; + +dclass DistributedPartyGate : DistributedObject { + getPartyList(uint32) airecv clsend; + partyChoiceRequest(uint32, uint64, uint64) airecv clsend; + listAllPublicParties(publicPartyInfo []); + partyRequestDenied(uint8); + setParty(publicPartyInfo, uint32 hostId); +}; + +dclass DistributedTrolley : DistributedObject { + setState(string, int16) broadcast ram; + fillSlot0(uint32) broadcast ram; + fillSlot1(uint32) broadcast ram; + fillSlot2(uint32) broadcast ram; + fillSlot3(uint32) broadcast ram; + emptySlot0(uint32, int16) broadcast ram; + emptySlot1(uint32, int16) broadcast ram; + emptySlot2(uint32, int16) broadcast ram; + emptySlot3(uint32, int16) broadcast ram; + requestBoard() airecv clsend; + rejectBoard(uint32); + requestExit() airecv clsend; + setMinigameZone(uint32, uint16); +}; + +dclass DistributedSuitPlanner : DistributedObject { + setZoneId(uint32) required broadcast ram; + suitListQuery() airecv clsend; + suitListResponse(uint8[]); + buildingListQuery() airecv clsend; + buildingListResponse(uint8[]); +}; + +dclass DistributedSuitBase : DistributedObject { + denyBattle(); + setDNAString(blob) required broadcast ram; + setLevelDist(int16) required broadcast ram; + setBrushOff(int16) broadcast; + setWaiter(uint8) broadcast ram; + setSkelecog(uint8) required broadcast ram; + setSkeleRevives(uint8) required broadcast ram; + setHP(int16) required broadcast ram; +}; + +dclass DistributedSuit : DistributedSuitBase { + requestBattle(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) airecv clsend; + setSPDoId(uint32) required broadcast ram; + setPathEndpoints(uint16, uint16, uint16, uint16) required broadcast ram; + setPathPosition(uint16, int16) required broadcast ram; + setPathState(int8) required broadcast ram; + debugSuitPosition(int16/10, int16, int16/10, int16/10, int16) broadcast; +}; + +dclass DistributedTutorialSuit : DistributedSuitBase { + requestBattle(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) airecv clsend; +}; + +dclass DistributedFactorySuit : DistributedSuitBase { + setLevelDoId(uint32) required broadcast ram; + setCogId(uint32) required broadcast ram; + setReserve(uint8) required broadcast ram; + requestBattle(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) airecv clsend; + setAlert(uint32) airecv clsend; + setConfrontToon(uint32) broadcast; + setStrayed() airecv clsend; + setReturn() broadcast; +}; + +dclass DistributedMintSuit : DistributedFactorySuit { +}; + +dclass DistributedStageSuit : DistributedFactorySuit { +}; + +dclass DistributedBossCog : DistributedNode { + setDNAString(blob) required broadcast db; + setToonIds(uint32[], uint32[], uint32[]) broadcast ram; + setBattleIds(uint8, uint32, uint32) broadcast ram; + setArenaSide(uint8) broadcast ram; + setKeyReward(bool) required broadcast ram; + avatarEnter() airecv clsend; + avatarExit() airecv clsend; + avatarNearEnter() airecv clsend; + avatarNearExit() airecv clsend; + toonDied(uint32) broadcast; + setBattleExperience(int32, int16[], int16[], uint32[], int16[], int16[], int16[], int16[], uint32[], int32, int16[], int16[], uint32[], int16[], int16[], int16[], int16[], uint32[], int32, int16[], int16[], uint32[], int16[], int16[], int16[], int16[], uint32[], int32, int16[], int16[], uint32[], int16[], int16[], int16[], int16[], uint32[], int32, int16[], int16[], uint32[], int16[], int16[], int16[], int16[], uint32[], int32, int16[], int16[], uint32[], int16[], int16[], int16[], int16[], uint32[], int32, int16[], int16[], uint32[], int16[], int16[], int16[], int16[], uint32[], int32, int16[], int16[], uint32[], int16[], int16[], int16[], int16[], uint32[], uint8[], int16[], uint32[]) required broadcast ram; + zapToon(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10, int8/100, int8/100, uint8, int16) airecv clsend; + showZapToon(uint32, int16/10, int16/10, int16/10, int16/10, int16/10, int16/10, uint8, int16) broadcast; + setAttackCode(uint8, uint32) broadcast; +}; + +dclass DistributedSellbotBoss : DistributedBossCog { + setCagedToonNpcId(uint32) required broadcast ram; + setDooberIds(uint32[]) broadcast ram; + setBossDamage(uint16, uint8, int16) broadcast ram; + setState(string) broadcast ram; + hitBoss(uint8) airecv clsend; + hitBossInsides() airecv clsend; + hitToon(uint32) airecv clsend; + finalPieSplat() airecv clsend; + touchCage() airecv clsend; + doStrafe(uint8, uint8) broadcast; + cagedToonBattleThree(uint16, uint32) broadcast; + toonPromoted(uint8(0-1)); +}; + +dclass DistributedCashbotBoss : DistributedBossCog { + setState(string) broadcast ram; + setBossDamage(uint16) broadcast ram; + setRewardId(uint16) broadcast ram; + applyReward() airecv clsend; +}; + +struct LinkPosition { + int16/100 x; + int16/100 y; + int16/100 z; +}; + +dclass DistributedCashbotBossCrane : DistributedObject { + setBossCogId(uint32) required broadcast ram; + setIndex(uint8) required broadcast ram; + setState(char, uint32) broadcast ram; + requestControl() airecv clsend; + requestFree() airecv clsend; + clearSmoothing(int8) broadcast clsend; + setCablePos(uint8, int16/100, uint16%360/100, LinkPosition [3], int16) broadcast clsend; +}; + +dclass DistributedCashbotBossObject : DistributedObject { + setBossCogId(uint32) required broadcast ram; + setObjectState(char, uint32, uint32) broadcast ram; + requestGrab() airecv clsend; + rejectGrab(); + requestDrop() airecv clsend; + hitFloor() clsend; + requestFree(int16/10, int16/10, int16/10, uint16%360/100) airecv clsend; + hitBoss(uint16/255) airecv clsend; + setX(int16/10) broadcast ram clsend airecv; + setY(int16/10) broadcast ram clsend airecv; + setZ(int16/10) broadcast ram clsend airecv; + setH(int16%360/10) broadcast ram clsend airecv; + setP(int16%360/10) broadcast ram clsend airecv; + setR(int16%360/10) broadcast ram clsend airecv; + setPos : setX, setY, setZ; + setHpr : setH, setP, setR; + setPosHpr : setX, setY, setZ, setH, setP, setR; + setXY : setX, setY; + setXZ : setX, setZ; + setXYH : setX, setY, setH; + setXYZH : setX, setY, setZ, setH; + setComponentL(uint64) broadcast ram clsend airecv; + setComponentX(int16/10) broadcast ram clsend airecv; + setComponentY(int16/10) broadcast ram clsend airecv; + setComponentZ(int16/10) broadcast ram clsend airecv; + setComponentH(int16%360/10) broadcast ram clsend airecv; + setComponentP(int16%360/10) broadcast ram clsend airecv; + setComponentR(int16%360/10) broadcast ram clsend airecv; + setComponentT(int16) broadcast ram clsend airecv; + setSmStop : setComponentT; + setSmH : setComponentH, setComponentT; + setSmZ : setComponentZ, setComponentT; + setSmXY : setComponentX, setComponentY, setComponentT; + setSmXZ : setComponentX, setComponentZ, setComponentT; + setSmPos : setComponentX, setComponentY, setComponentZ, setComponentT; + setSmHpr : setComponentH, setComponentP, setComponentR, setComponentT; + setSmXYH : setComponentX, setComponentY, setComponentH, setComponentT; + setSmXYZH : setComponentX, setComponentY, setComponentZ, setComponentH, setComponentT; + setSmPosHpr : setComponentX, setComponentY, setComponentZ, setComponentH, setComponentP, setComponentR, setComponentT; + setSmPosHprL : setComponentL, setComponentX, setComponentY, setComponentZ, setComponentH, setComponentP, setComponentR, setComponentT; + clearSmoothing(int8) broadcast clsend; +}; + +dclass DistributedCashbotBossSafe : DistributedCashbotBossObject { + setIndex(uint8) required broadcast ram; + requestInitial() airecv clsend; +}; + +dclass DistributedCashbotBossGoon : DistributedCashbotBossObject { + requestBattle(int16/10) airecv clsend; + requestStunned(int16/10) airecv clsend; + setVelocity(uint8/10) broadcast ram; + setHFov(uint8) broadcast ram; + setAttackRadius(uint8) broadcast ram; + setStrength(uint8) broadcast ram; + setGoonScale(uint8/50) broadcast ram; + setupGoon : setVelocity, setHFov, setAttackRadius, setStrength, setGoonScale; + setTarget(int16/10, int16/10, uint16%360/100, int16) broadcast ram; + destroyGoon() broadcast clsend airecv; +}; + +dclass DistributedBattleBase : DistributedObject { + setLevelDoId(uint32) required broadcast ram; + setBattleCellId(uint32) required broadcast ram; + setInteractivePropTrackBonus(int8) required broadcast ram; + setPosition(int16/10, int16/10, int16/10) required broadcast ram; + setZoneId(uint32) required broadcast ram; + setInitialSuitPos(int16/10, int16/10, int16/10) required broadcast ram; + setMembers(uint32[], string, string, string, string, string, uint32[], string, string, string, string, int16) required broadcast ram; + adjust(int16) broadcast; + setMovie(int8, uint32[], uint32[], int8, int8, int8, int32, int16[], int16, int16, int16[], int8, int8, int8, int8, int8, int32, int16[], int16, int16, int16[], int8, int8, int8, int8, int8, int32, int16[], int16, int16, int16[], int8, int8, int8, int8, int8, int32, int16[], int16, int16, int16[], int8, int8, int8, int8, int8, int16[], int8, int8, int8, int8, int8, int8, int16[], int8, int8, int8, int8, int8, int8, int16[], int8, int8, int8, int8, int8, int8, int16[], int8, int8, int8) required broadcast ram; + setChosenToonAttacks(uint32[], int16[], int16[], int32[]) broadcast ram; + setBattleExperience(int32, int16[], int16[], uint32[], int16[], int16[], int16[], int16[], uint32[], int32, int16[], int16[], uint32[], int16[], int16[], int16[], int16[], uint32[], int32, int16[], int16[], uint32[], int16[], int16[], int16[], int16[], uint32[], int32, int16[], int16[], uint32[], int16[], int16[], int16[], int16[], uint32[], uint8[], int16[], uint32[]) required broadcast ram; + denyLocalToonJoin(); + setBossBattle(uint8) required broadcast ram; + setState(string, int16) required broadcast ram; + faceOffDone() airecv clsend; + toonRequestJoin(int16/10, int16/10, int16/10) airecv clsend; + toonRequestRun() airecv clsend; + toonDied() airecv clsend; + adjustDone() airecv clsend; + timeout() airecv clsend; + movieDone() airecv clsend; + rewardDone() airecv clsend; + joinDone(uint32) airecv clsend; + requestAttack(int8, int8, int32) airecv clsend; + requestPetProxy(uint32) airecv clsend; +}; + +dclass DistributedBattle : DistributedBattleBase { +}; + +dclass DistributedBattleBldg : DistributedBattleBase { +}; + +dclass DistributedBattleTutorial : DistributedBattle { +}; + +dclass DistributedLevelBattle : DistributedBattle { + announceCrateReward() broadcast; +}; + +dclass DistributedBattleFactory : DistributedLevelBattle { +}; + +dclass DistributedMintBattle : DistributedLevelBattle { +}; + +dclass DistributedStageBattle : DistributedLevelBattle { +}; + +dclass DistributedBattleFinal : DistributedBattleBase { + setBossCogId(uint32) required broadcast ram; + setBattleNumber(uint8) required broadcast ram; + setBattleSide(uint8) required broadcast ram; +}; + +dclass DistributedBoat : DistributedObject { + setState(string, int16) required broadcast ram; +}; + +dclass DistributedButterfly : DistributedObject { + setArea(int16, int16) required broadcast ram; + setState(int8, uint8, uint8, uint16/10, int16) required broadcast ram; + avatarEnter() airecv clsend; +}; + +dclass DistributedMMPiano : DistributedObject { + requestSpeedUp() airecv clsend; + requestSlowDown() airecv clsend; + requestChangeDirection() airecv clsend; + setSpeed(int16/1000, uint16/100, int16) broadcast ram; + playSpeedUp(uint32) broadcast; + playChangeDirection(uint32) broadcast; +}; + +dclass DistributedDGFlower : DistributedObject { + avatarEnter() airecv clsend; + avatarExit() airecv clsend; + setHeight(uint8/10) broadcast ram; +}; + +dclass DistributedFishingPond : DistributedObject { + hitTarget(uint32) airecv clsend; + setArea(uint32) required broadcast ram; +}; + +dclass DistributedFishingTarget : DistributedNode { + setPondDoId(uint32) required broadcast ram; + setState(uint8, int16/10, uint16/100, uint16/10, int16) required broadcast ram; +}; + +dclass DistributedFishingSpot : DistributedObject { + setPondDoId(uint32) required broadcast ram; + setPosHpr(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) required broadcast ram; + requestEnter() airecv clsend; + rejectEnter(); + requestExit() airecv clsend; + setOccupied(uint32) broadcast ram; + doCast(uint8/255, int16/100) airecv clsend; + sellFish() airecv clsend; + sellFishComplete(uint8, uint16); + setMovie(uint8, uint8, uint16, uint16, uint16, uint8/100, int16/100) broadcast ram; +}; + +dclass DistributedPondBingoManager : DistributedObject { + setPondDoId(uint32) required broadcast ram; + updateGameState(uint32, uint8); + setCardState(uint16, uint8, uint16, uint32); + setState(string, int16); + cardUpdate(uint16, uint8, uint8, uint8) airecv clsend; + enableBingo(); + handleBingoCall(uint16) airecv clsend; + setJackpot(uint16); +}; + +dclass DistributedCannon : DistributedObject { + setEstateId(uint32) required broadcast ram; + setTargetId(uint32) required broadcast ram; + setPosHpr(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) required broadcast ram; + setActive(uint8) airecv clsend; + setActiveState(uint8) broadcast ram; + requestEnter() airecv clsend; + requestExit() broadcast; + setMovie(uint8, uint32) broadcast ram; + setCannonPosition(int32/100, uint32/100) airecv clsend; + setCannonLit(int32/100, uint32/100) airecv clsend; + setFired() airecv clsend; + setLanded() airecv clsend; + updateCannonPosition(uint32, int32/100, uint32/100) broadcast ram; + setCannonWillFire(uint32, int32/100, int32/100, uint32/100, int16) broadcast; + setCannonExit(uint32) broadcast; + requestBumperMove(int32/100, int32/100, int32/100) airecv clsend; + setCannonBumperPos(int32/100, int32/100, int32/100) required broadcast ram; +}; + +dclass DistributedTarget : DistributedObject { + setPosition(int16/10, int16/10, int16/10) required broadcast ram; + setState(uint8, uint32/10, uint8) broadcast; + setReward(uint32) broadcast; + setResult(uint32) airecv clsend; + setBonus(int16/10) airecv clsend; + setCurPinballScore(uint32, int32, int32) clsend airecv; + setPinballHiScorer(string) broadcast ram; + setPinballHiScore(int32) broadcast ram; +}; + +dclass DistributedMinigame : DistributedObject { + setParticipants(uint32[]) broadcast ram required; + setTrolleyZone(uint32) broadcast ram required; + setDifficultyOverrides(int32, int32) broadcast ram required; + setAvatarJoined() airecv clsend; + setAvatarReady() broadcast airecv clsend; + setAvatarExited() airecv clsend; + requestExit() airecv clsend; + requestSkip() airecv clsend; + setSkipCount(uint8) broadcast; + setGameReady() broadcast; + setGameStart(int16) broadcast; + setGameExit() broadcast; + setGameAbort() broadcast; +}; + +dclass DistributedMinigameTemplate : DistributedMinigame { +}; + +dclass DistributedRaceGame : DistributedMinigame { + setTimerStartTime(int16) broadcast; + setAvatarChoice(uint8) airecv clsend; + setAvatarChose(uint32) broadcast; + setChancePositions(uint8[]) broadcast; + setServerChoices(int8[], uint8[], int8[]) broadcast; +}; + +dclass DistributedCannonGame : DistributedMinigame { + setCannonPosition(int32/100, uint32/100) airecv clsend; + setCannonLit(int32/100, uint32/100) airecv clsend; + updateCannonPosition(uint32, int32/100, uint32/100) broadcast; + setCannonWillFire(uint32, int32/100, int32/100, uint32/100) broadcast; + setToonWillLandInWater(int32/100) airecv clsend; + announceToonWillLandInWater(uint32, int32/100) broadcast; +}; + +dclass DistributedPatternGame : DistributedMinigame { + reportPlayerReady() airecv clsend; + setPattern(uint8[]) broadcast; + reportPlayerPattern(uint8[], uint16/1000) airecv clsend; + setPlayerPatterns(uint8[], uint8[], uint8[], uint8[], uint32) broadcast; + reportButtonPress(uint8, uint8) airecv clsend; + remoteButtonPressed(uint32, uint8, uint8) broadcast; +}; + +dclass DistributedRingGame : DistributedMinigame { + setTimeBase(int16) broadcast ram required; + setColorIndices(int8, int8, int8, int8) broadcast ram required; + setToonGotRing(uint8) airecv clsend; + setRingGroupResults(uint8) broadcast; +}; + +dclass DistributedTagGame : DistributedMinigame { + tag(uint32) airecv clsend; + setIt(uint32) broadcast; + setTreasureScore(uint16[]) broadcast; +}; + +dclass DistributedMazeGame : DistributedMinigame { + claimTreasure(uint32) airecv clsend; + setTreasureGrabbed(uint32, uint32) broadcast; + allTreasuresTaken() broadcast; + hitBySuit(uint32, int16) clsend broadcast; +}; + +dclass DistributedTugOfWarGame : DistributedMinigame { + reportPlayerReady(uint8) airecv clsend; + sendGoSignal(uint8[]) broadcast; + sendStopSignal(uint32[], uint32[], uint32[]) broadcast; + sendGameType(uint8, uint8) broadcast; + reportEndOfContest(uint8) airecv clsend; + sendNewAvIdList(uint32[]) airecv clsend; + reportCurrentKeyRate(uint32, int16/100) airecv clsend; + sendCurrentPosition(uint32[], int16/1000[]) broadcast; + sendSuitPosition(int32/1000) broadcast; + remoteKeyRateUpdate(uint32, uint32) broadcast; +}; + +dclass DistributedCatchGame : DistributedMinigame { + claimCatch(uint32, uint32) airecv clsend; + setObjectCaught(uint32, uint32) broadcast; + hitBySuit(uint32, int16) clsend broadcast; + reportDone() airecv clsend; + setEveryoneDone() broadcast; +}; + +dclass DistributedDivingGame : DistributedMinigame { + pickupTreasure(uint32) airecv clsend; + setTreasureGrabbed(uint32, uint32) broadcast; + handleFishCollision(uint32, uint32, uint32, char [0-256]) airecv clsend; + performFishCollision(uint32, uint32, uint32, int16) broadcast; + handleCrabCollision(uint32, char [0-256]) airecv clsend; + performCrabCollision(uint32, int16) broadcast; + setTreasureDropped(uint32, int16) broadcast; + fishSpawn(int16, uint32, uint32, uint16) broadcast; + removeFish(uint32) airecv clsend; + getCrabMoving(uint32, int16, int8) airecv clsend; + setCrabMoving(uint32, int16, int8, int8, int16, int8) broadcast; + treasureRecovered() airecv clsend; + incrementScore(uint32, uint32, int16) broadcast; +}; + +dclass DistributedTargetGame : DistributedMinigame { + setTimeBase(int16) broadcast ram required; + setToonGotRing(uint8) airecv clsend; + setRingGroupResults(uint8) broadcast; + setPlayerDone() airecv clsend; + setScore(int32, int32) airecv clsend; + setTargetSeed(uint32) broadcast ram; + setRoundDone() broadcast; + setSingleScore(uint16, uint32) broadcast; + setGameDone() broadcast; +}; + +dclass EstateManager : DistributedObject { + getEstateZone(uint32 avId) airecv clsend; + setEstateZone(uint32 ownerId, uint32 zoneId); + setAvHouseId(uint32, uint32[]) broadcast; + sendAvToPlayground(DoId avId, uint8 reason); + exitEstate() airecv clsend; + removeFriend(uint32, uint32) airecv clsend; +}; + +struct decorItem { + uint8 decorType; + uint8 dataByte[]; + uint32 dataWord[]; +}; + +struct lawnItem { + uint8 type; + uint8 hardPoint; + int8 waterLevel; + int8 growthLevel; + uint16 optional; +}; + +dclass DistributedEstate : DistributedObject { + setEstateReady() broadcast; + setClientReady() airecv clsend; + setEstateType(uint8 type = 0) required broadcast db; + setClosestHouse(uint8) airecv clsend; + setTreasureIds(uint32[]) broadcast ram; + requestServerTime() airecv clsend; + setServerTime(uint32); + setDawnTime(uint32) required broadcast ram; + placeOnGround(uint32) broadcast ram; + setLastEpochTimeStamp(uint32 timestamp = 0) required airecv db; + setRentalTimeStamp(uint32 timestamp = 0) required airecv db; + setRentalType(uint8 type = 0) required airecv db; + setSlot0ToonId(uint32 toonId = 0) required airecv db; + setSlot0Garden(blob g) required airecv db; + setSlot1ToonId(uint32 toonId = 0) required airecv db; + setSlot1Garden(blob g) required airecv db; + setSlot2ToonId(uint32 toonId = 0) required airecv db; + setSlot2Garden(blob g) required airecv db; + setSlot3ToonId(uint32 toonId = 0) required airecv db; + setSlot3Garden(blob g) required airecv db; + setSlot4ToonId(uint32 toonId = 0) required airecv db; + setSlot4Garden(blob g) required airecv db; + setSlot5ToonId(uint32 toonId = 0) required airecv db; + setSlot5Garden(blob g) required airecv db; + setIdList(uint32 []) broadcast ram; + completeFlowerSale(uint8) airecv clsend; + completeFishSale() airecv clsend; + thankSeller(int8, int8, int8); + awardedTrophy(uint32) broadcast; + setClouds(uint8) required broadcast ram; + cannonsOver() broadcast; + gameTableOver() broadcast; +}; + +dclass DistributedHouse : DistributedObject { + setHousePos(uint8) required broadcast; + setHouseType(uint8 type = 0) required broadcast db; + setGardenPos(uint8 index = 0) required broadcast db; + setAvatarId(uint32 toonId = 0) required broadcast db; + setName(string toonName = "") required broadcast db; + setColor(uint8 colorIndex = 0) required broadcast db; + setGender(uint8 = 0) required broadcast db; + setAtticItems(blob = "") required db; + setInteriorItems(blob = "") required db; + setAtticWallpaper(blob = "") required db; + setInteriorWallpaper(blob = "") required db; + setAtticWindows(blob = "") required db; + setInteriorWindows(blob = "") required db; + setDeletedItems(blob = "") required db; + setInteriorInitialized(uint8 initialized = 0) required db; + setCannonEnabled(uint8) required; + setHouseReady() broadcast ram; +}; + +dclass DistributedHouseInterior : DistributedObject { + setHouseId(uint32) required broadcast ram; + setHouseIndex(uint8) required broadcast ram; + setWallpaper(blob) required broadcast ram; + setWindows(blob) required broadcast ram; +}; + +dclass DistributedGarden : DistributedObject { + sendNewProp(uint8 prop, int16/10 x, int16/10 y, int16/10 z) broadcast; +}; + +dclass DistributedParty : DistributedObject { + setPartyClockInfo(uint8, uint8, uint8) required broadcast; + setInviteeIds(uint32[]) required broadcast; + setPartyState(bool) required broadcast; + setPartyInfoTuple(party) required broadcast; + setAvIdsAtParty(uint32 []) required broadcast; + setPartyStartedTime(string) required broadcast; + setHostName(string) required broadcast; + enteredParty() clsend airecv; +}; + +dclass DistributedPartyActivity : DistributedObject { + setX(int16/10) required broadcast ram; + setY(int16/10) required broadcast ram; + setH(uint16%360/100) required broadcast ram; + setPartyDoId(uint32) required broadcast; + unloadSign() broadcast ram; + toonJoinRequest() airecv clsend; + toonExitRequest() airecv clsend; + toonExitDemand() airecv clsend; + toonReady() airecv clsend; + joinRequestDenied(uint8); + exitRequestDenied(uint8); + setToonsPlaying(uint32 []) broadcast ram; + setState(string, int16) broadcast ram; + showJellybeanReward(uint32, uint32, string); +}; + +dclass DistributedPartyTeamActivity : DistributedPartyActivity { + toonJoinRequest(uint8(0-1)) airecv clsend; + toonExitRequest(uint8(0-1)) airecv clsend; + toonSwitchTeamRequest() airecv clsend; + setPlayersPerTeam(uint8, uint8) broadcast required; + setDuration(uint8) broadcast required; + setCanSwitchTeams(bool) broadcast required; + setState(string, int16, uint32) broadcast ram; + setToonsPlaying(uint32 [0-8], uint32 [0-8]) required broadcast ram; + setAdvantage(uint16/100); + switchTeamRequestDenied(uint8); +}; + +struct CatchGeneration { + uint32 generation; + uint32 timestamp; + int8 numPlayers; +}; + +dclass DistributedPartyCatchActivity : DistributedPartyActivity { + setStartTimestamp(uint32) required broadcast ram; + setGenerations(CatchGeneration []) required broadcast ram; + requestActivityStart() airecv clsend; + startRequestResponse(uint8); + claimCatch(uint32, uint32, uint32) airecv clsend; + setObjectCaught(uint32, uint32, uint32) broadcast; +}; + +dclass DistributedPartyWinterCatchActivity : DistributedPartyCatchActivity { +}; + +dclass DistributedPartyCogActivity : DistributedPartyTeamActivity { + pieThrow(uint32, int32, int32/100, int32/100, int32/100, int32/100, uint8) clsend broadcast; + pieHitsToon(uint32, int32, int32/100, int32/100, int32/100) clsend broadcast; + pieHitsCog(uint32, int32, int8(0-2), int32/100, int32/100, int32/100, int32, bool) clsend broadcast airecv; + setCogDistances(int8/100 [3]) broadcast ram; + setHighScore(string, uint16) broadcast ram; +}; + +dclass DistributedPartyWinterCogActivity : DistributedPartyCogActivity { +}; + +dclass DistributedPartyDanceActivityBase : DistributedPartyActivity { + updateDancingToon(uint8, char [0-256]) clsend airecv; + setToonsPlaying(uint32 [], uint16%360/100 []) broadcast ram; + setDancingToonState(uint32, uint8, string) broadcast; +}; + +dclass DistributedPartyDanceActivity : DistributedPartyDanceActivityBase { +}; + +dclass DistributedPartyDance20Activity : DistributedPartyDanceActivityBase { +}; + +dclass DistributedPartyValentineDanceActivity : DistributedPartyDanceActivityBase { +}; + +dclass DistributedPartyValentineDance20Activity : DistributedPartyDanceActivityBase { +}; + +dclass DistributedPartyJukeboxActivityBase : DistributedPartyActivity { + setNextSong(jukeboxSongInfo) clsend airecv; + setSongPlaying(jukeboxSongInfo, uint32) broadcast ram; + queuedSongsRequest() clsend airecv; + queuedSongsResponse(jukeboxSongInfo [], int16); + setSongInQueue(jukeboxSongInfo); + moveHostSongToTopRequest() clsend airecv; + moveHostSongToTop(); +}; + +dclass DistributedPartyJukeboxActivity : DistributedPartyJukeboxActivityBase { +}; + +dclass DistributedPartyJukebox40Activity : DistributedPartyJukeboxActivityBase { +}; + +dclass DistributedPartyValentineJukeboxActivity : DistributedPartyJukeboxActivityBase { +}; + +dclass DistributedPartyValentineJukebox40Activity : DistributedPartyJukeboxActivityBase { +}; + +dclass DistributedPartyCannonActivity : DistributedPartyActivity { + setMovie(uint8, uint32) broadcast; + setLanded(uint32) airecv broadcast clsend; + setCannonWillFire(uint32, int32/100, uint32/100) broadcast; + cloudsColorRequest() clsend airecv; + cloudsColorResponse(partyCloudColor []); + requestCloudHit(uint16, uint8/100, uint8/100, uint8/100) clsend airecv; + setCloudHit(uint16, uint8/100, uint8/100, uint8/100) broadcast; + setToonTrajectoryAi(int32, int32/100, int32/100, int32/100, int32/100, int32/100, int32/100, int32/100, int32/100, int32/100) airecv clsend; + setToonTrajectory(uint32, int32, int32/100, int32/100, int32/100, int32/100, int32/100, int32/100, int32/100, int32/100, int32/100) broadcast; + updateToonTrajectoryStartVelAi(int32/100, int32/100, int32/100) airecv clsend; + updateToonTrajectoryStartVel(uint32, int32/100, int32/100, int32/100) broadcast; +}; + +dclass DistributedPartyCannon : DistributedObject { + setActivityDoId(uint64) required broadcast ram; + setPosHpr(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) required broadcast ram; + requestEnter() airecv clsend; + requestExit() broadcast; + setMovie(uint8, uint32) broadcast ram; + setCannonPosition(int32/100, uint32/100) airecv clsend; + setCannonLit(int32/100, uint32/100) airecv clsend; + setFired() airecv clsend; + setLanded(uint32) airecv clsend; + updateCannonPosition(uint32, int32/100, uint32/100) broadcast ram; + setCannonExit(uint32) broadcast; + setTimeout() clsend airecv; +}; + +dclass DistributedPartyFireworksActivity : DistributedPartyActivity { + setEventId(uint8 eventId) required broadcast; + setShowStyle(uint8 style) required broadcast; + setSongId(uint8 songId) required broadcast; +}; + +dclass DistributedPartyTrampolineActivity : DistributedPartyActivity { + awardBeans(uint8, uint16) clsend airecv; + setBestHeightInfo(string, uint16) broadcast ram; + reportHeightInformation(uint16) airecv clsend; + leaveTrampoline() broadcast; + requestAnim(char [0-256]) clsend airecv; + requestAnimEcho(string) broadcast; + removeBeans(int8 []) clsend airecv; + removeBeansEcho(int8 []) broadcast; +}; + +dclass DistributedPartyValentineTrampolineActivity : DistributedPartyTrampolineActivity { +}; + +dclass DistributedPartyVictoryTrampolineActivity : DistributedPartyTrampolineActivity { +}; + +dclass DistributedPartyWinterTrampolineActivity : DistributedPartyTrampolineActivity { +}; + +dclass DistributedPartyTugOfWarActivity : DistributedPartyTeamActivity { + reportKeyRateForce(uint32, int16/100) airecv clsend; + reportFallIn(uint8) airecv clsend; + setToonsPlaying(uint32 [0-4], uint32 [0-4]) required broadcast ram; + updateToonKeyRate(uint32, uint32) broadcast; + updateToonPositions(int16/1000) broadcast; +}; + +dclass DeleteManager : DistributedObject { + setInventory(blob) airecv clsend; +}; + +dclass NewsManager : DistributedObject { + startHoliday(uint8) broadcast; + endHoliday(uint8) broadcast; + setActiveHolidays(uint8[]); + setInvasionStatus(uint8, string, uint32, uint8) broadcast; +}; + +dclass PurchaseManager : DistributedObject { + setAvIds(uint32, uint32, uint32, uint32) required broadcast ram; + setNewbieIds(uint32[]) required broadcast ram; + setMinigamePoints(uint8, uint8, uint8, uint8) required broadcast ram; + setPlayerMoney(uint16, uint16, uint16, uint16) required broadcast ram; + setPlayerStates(uint8, uint8, uint8, uint8) required broadcast ram; + setCountdown(int16) required broadcast ram; + requestExit() airecv clsend; + requestPlayAgain() airecv clsend; + setInventory(blob, int16, uint8) airecv clsend; + setPurchaseExit() broadcast; +}; + +dclass NewbiePurchaseManager : PurchaseManager { + setOwnedNewbieId(uint32) required broadcast ram; +}; + +dclass SafeZoneManager : DistributedObject { + enterSafeZone() airecv clsend; + exitSafeZone() airecv clsend; +}; + +dclass TutorialManager : DistributedObject { + requestTutorial() airecv clsend; + rejectTutorial() airecv clsend; + requestSkipTutorial() airecv clsend; + skipTutorialResponse(uint8); + enterTutorial(uint32, uint32, uint32, uint32); + allDone() airecv clsend; + toonArrived() airecv clsend; +}; + +dclass CatalogManager : DistributedObject { + startCatalog() airecv clsend; +}; + +dclass DistributedTreasure : DistributedObject { + setTreasureType(uint16) required broadcast ram; + setPosition(int16/10, int16/10, int16/10) required broadcast ram; + requestGrab() airecv clsend; + setGrab(uint32) broadcast ram; + setReject() broadcast; +}; + +dclass DistributedEFlyingTreasure : DistributedTreasure { +}; + +dclass DistributedCashbotBossTreasure : DistributedTreasure { + setGoonId(uint32) required broadcast ram; + setFinalPosition(int16/10, int16/10, int16/10) required broadcast ram; + setStyle(uint16) required broadcast ram; +}; + +dclass DistributedLevel : DistributedObject { + setLevelZoneId(uint32) required broadcast ram; + setAvIds(uint32[]) required broadcast ram; + setEntranceId(uint8) required broadcast ram; + setZoneIds(uint32[]) broadcast ram; + setStartTimestamp(int32) broadcast ram; + setOuch(uint8) airecv clsend; +}; + +dclass DistributedEntity : DistributedObject { + setLevelDoId(uint32) required broadcast ram; + setEntId(uint32) required broadcast ram; +}; + +dclass DistributedInteractiveEntity : DistributedEntity { + setAvatarInteract(uint32) required broadcast ram; + requestInteract() airecv clsend; + rejectInteract(); + requestExit() airecv clsend; + avatarExit(uint32) broadcast; + setState(string, int32) required broadcast ram; +}; + +dclass DistributedTrophyMgr : DistributedObject { + requestTrophyScore() airecv clsend; +}; + +dclass DistributedBuilding : DistributedObject { + setBlock(uint16, uint32) required broadcast ram; + setSuitData(int8, int8, int8) required broadcast ram; + setVictorList(uint32[]) broadcast ram; + setState(string, int16) broadcast ram; + setVictorReady() airecv clsend; +}; + +dclass DistributedBuildingQueryMgr : DistributedObject { + isSuit(uint8, uint32) airecv clsend; + response(uint8, bool); +}; + +dclass DistributedToonInterior : DistributedObject { + setZoneIdAndBlock(uint32, uint16) required broadcast ram; + setToonData(blob) required broadcast ram; + setState(string, int16) required broadcast ram; + nextSnowmanHeadPart() clsend airecv; +}; + +dclass DistributedToonHallInterior : DistributedToonInterior { +}; + +dclass DistributedSuitInterior : DistributedObject { + setZoneId(uint32) required broadcast ram; + setExtZoneId(uint32) required broadcast ram; + setDistBldgDoId(uint32) required broadcast ram; + setNumFloors(int8) required broadcast ram; + setToons(uint32[], uint16) broadcast ram; + setSuits(uint32[], uint32[], uint16[]) broadcast ram; + setState(string, int16) required broadcast ram; + setAvatarJoined() airecv clsend; + elevatorDone() airecv clsend; + reserveJoinDone() airecv clsend; +}; + +dclass DistributedCogdoBarrel : DistributedObject { + requestGrab() airecv clsend; + setIndex(uint32) required broadcast ram; + setState(uint32) required broadcast ram; + setGrab(uint32) broadcast ram; + setReject() broadcast; +}; + +dclass DistributedCogdoInterior : DistributedObject { + setZoneId(uint32) required broadcast ram; + setExtZoneId(uint32) required broadcast ram; + setDistBldgDoId(uint32) required broadcast ram; + setNumFloors(int8) required broadcast ram; + setShopOwnerNpcId(uint32) required broadcast ram; + setSOSNpcId(uint32) broadcast ram; + setFOType(int8) broadcast ram; + setToons(uint32[], uint16) broadcast ram; + setSuits(uint32[], uint32[], uint16[]) broadcast ram; + setState(string, int16) required broadcast ram; + setAvatarJoined() airecv clsend; + elevatorDone() airecv clsend; + reserveJoinDone() airecv clsend; + toonLeftBarrelRoom() airecv clsend; + toonBarrelRoomIntroDone() airecv clsend; + setBarrelRoomReward(uint32 [], uint8 []) broadcast; + toonBarrelRoomRewardDone() airecv clsend; +}; + +dclass DistributedCogdoBattleBldg : DistributedBattleBldg { +}; + +dclass DistCogdoGame : DistributedObject { + setInteriorId(uint32) required broadcast ram; + setExteriorZone(uint32) broadcast ram required; + setDifficultyOverrides(int32, int32) broadcast ram required; + setVisible() broadcast; + setIntroStart() broadcast; + setToonSad(uint32) broadcast; + setToonDisconnect(uint32) broadcast; + setAvatarReady() airecv clsend; + setGameStart(int16) broadcast; + setGameFinish(int16) broadcast; +}; + +dclass DistCogdoLevelGame : DistCogdoGame, DistributedLevel { +}; + +dclass DistCogdoMazeGame : DistCogdoGame { + requestAction(uint8, uint32) airecv clsend; + doAction(uint8, uint32, int16) broadcast; + setNumSuits(uint8 [3]) required broadcast; + requestUseGag(int16/10, int16/10, int16/10, int16) clsend airecv; + toonUsedGag(uint32, int16/10, int16/10, int16/10, int16) broadcast; + requestSuitHitByGag(uint8, uint8) clsend airecv; + suitHitByGag(uint32, uint8, uint8) broadcast; + requestHitBySuit(uint8, uint8, int16) clsend airecv; + toonHitBySuit(uint32, uint8, uint8, int16) broadcast; + requestHitByDrop() clsend airecv; + toonHitByDrop(uint32) broadcast; + requestPickUp(uint8) clsend airecv; + pickUp(uint32, uint8, int16) broadcast; + requestGag(uint8) clsend airecv; + hasGag(uint32, int16) broadcast; +}; + +dclass DistCogdoFlyingGame : DistCogdoGame { + requestAction(uint8, uint8) airecv clsend; + requestPickUp(uint16, uint8) airecv clsend; + pickUp(uint32, uint16, int16) broadcast; + debuffPowerup(uint32, uint16, int16) broadcast; + doAction(uint8, uint32) broadcast; + eagleExitCooldown(uint32, int16) broadcast; + toonSetAsEagleTarget(uint32, uint8, int16) broadcast; + toonClearAsEagleTarget(uint32, uint8, int16) broadcast; + toonDied(uint32, int32) broadcast; + toonSpawn(uint32, int32) broadcast; + toonSetBlades(uint32, int32) broadcast; + toonBladeLost(uint32) broadcast; +}; + +dclass DistCogdoBoardroomGame : DistCogdoLevelGame { +}; + + +dclass DistributedHQInterior : DistributedObject { + setZoneIdAndBlock(uint32, uint16) required broadcast ram; + setLeaderBoard(blob) required broadcast ram; + setTutorial(uint8) required broadcast ram; +}; + +dclass DistributedGagshopInterior : DistributedObject { + setZoneIdAndBlock(uint32, uint16) required broadcast ram; +}; + +dclass DistributedPetshopInterior : DistributedObject { + setZoneIdAndBlock(uint32, uint16) required broadcast ram; +}; + +dclass DistributedKartShopInterior : DistributedObject { + setZoneIdAndBlock(uint32, uint16) required broadcast ram; +}; + +dclass DistributedDoor : DistributedObject { + setZoneIdAndBlock(uint32, uint32) required broadcast ram; + setSwing(int8) required broadcast ram; + setDoorType(uint8) required broadcast ram; + setDoorIndex(uint8) required broadcast ram; + setOtherZoneIdAndDoId(uint32, uint32); + requestEnter() airecv clsend; + requestExit() airecv clsend; + rejectEnter(int8); + avatarEnter(uint32) broadcast; + avatarExit(uint32) broadcast; + setState(string, int16) required broadcast ram; + setExitDoorState(string, int16) required broadcast ram; +}; + +dclass DistributedHouseDoor : DistributedDoor { +}; + +dclass DistributedCogHQDoor : DistributedDoor { +}; + +//dclass DistributedCogHQExteriorDoor : DistributedCogHQDoor { +// selectLobby(uint32) broadcast; +// confirmEntrance(uint32, bool) airecv clsend; +//}; + +dclass DistributedSellbotHQDoor : DistributedCogHQDoor { + informPlayer(uint8) broadcast ram; +} + +dclass DistributedNPCToonBase : DistributedNode { + setName(string) required broadcast ram; + setDNAString(blob) required broadcast ram; + setPositionIndex(uint8) required broadcast ram; + setAnimState(string, int16/1000, int16) broadcast ram; + setPageNumber(int16, int8, int16) broadcast ram clsend; + avatarEnter() airecv clsend; + freeAvatar(); + setHat(uint8 = 0, uint8 = 0, uint8 = 0) broadcast ram; + setGlasses(uint8 = 0, uint8 = 0, uint8 = 0) broadcast ram; + setBackpack(uint8 = 0, uint8 = 0, uint8 = 0) broadcast ram; + setShoes(uint8 = 0, uint8 = 0, uint8 = 0) broadcast ram; +}; + +dclass DistributedNPCToon : DistributedNPCToonBase { + setMovie(uint8, uint32, uint32, uint16[], int16) broadcast ram; + setMovieDone() airecv clsend; + chooseQuest(uint16) airecv clsend; + chooseTrack(int8) airecv clsend; +}; + +dclass DistributedNPCSpecialQuestGiver : DistributedNPCToonBase { + setMovie(uint8, uint32, uint32, uint16[], int16) broadcast ram; + setMovieDone() airecv clsend; + chooseQuest(uint16) airecv clsend; + chooseTrack(int8) airecv clsend; +}; + +dclass DistributedNPCFlippyInToonHall : DistributedNPCToon { +}; + +dclass DistributedNPCScientist : DistributedNPCToonBase { +}; + +dclass DistributedNPCClerk : DistributedNPCToonBase { + setState(uint32, uint8) broadcast airecv clsend; + setInventory(blob, int16) airecv clsend; +}; + +dclass DistributedNPCTailor : DistributedNPCToonBase { + setMovie(uint8, uint32, uint32, int16) broadcast ram; + setDNA(blob, int8, uint8) airecv clsend; + setCustomerDNA(uint32, blob) broadcast ram; +}; + +dclass DistributedNPCBlocker : DistributedNPCToonBase { + setMovie(uint8, uint32, uint32, int16) broadcast ram; +}; + +dclass DistributedNPCFisherman : DistributedNPCToonBase { + setMovie(uint8, uint32, uint32, uint32[], int16) broadcast ram; + completeSale(uint8) airecv clsend; +}; + +dclass DistributedNPCPartyPerson : DistributedNPCToonBase { + setMovie(uint8, uint32, uint32, uint32[], int16) broadcast ram; + answer(uint8) airecv clsend; +}; + +dclass DistributedNPCPetclerk : DistributedNPCToonBase { + setMovie(uint8, uint32, uint32, uint32[], int16) broadcast ram; + setPetSeeds(uint32[]); + petAdopted(uint8, uint32) airecv clsend; + petReturned() airecv clsend; + fishSold() airecv clsend; + transactionDone() airecv clsend; +}; + +dclass DistributedNPCKartClerk : DistributedNPCToonBase { + setMovie(uint8, uint32, uint32, uint32[], int16) broadcast ram; + buyKart(uint8) airecv clsend; + buyAccessory(uint8) airecv clsend; + transactionDone() airecv clsend; +}; + +dclass DistributedNPCLaffRestock : DistributedNPCToonBase { + restock(uint8) airecv clsend; + restockResult(uint8) broadcast; +}; + +dclass DistributedNPCGlove : DistributedNPCToonBase { + changeGlove(uint8) airecv clsend; + changeGloveResult(uint32 avId, uint8) broadcast; +}; + +dclass DistributedKnockKnockDoor : DistributedAnimatedProp { + requestToonup() airecv clsend; +}; + +dclass DistributedElevator : DistributedObject { + setBldgDoId(uint32) required broadcast ram; + setState(string, int16) broadcast ram; + fillSlot0(uint32, uint8) broadcast ram; + fillSlot1(uint32, uint8) broadcast ram; + fillSlot2(uint32, uint8) broadcast ram; + fillSlot3(uint32, uint8) broadcast ram; + fillSlot4(uint32, uint8) broadcast ram; + fillSlot5(uint32, uint8) broadcast ram; + fillSlot6(uint32, uint8) broadcast ram; + fillSlot7(uint32, uint8) broadcast ram; + emptySlot0(uint32, int16, int16) broadcast ram; + emptySlot1(uint32, int16, int16) broadcast ram; + emptySlot2(uint32, int16, int16) broadcast ram; + emptySlot3(uint32, int16, int16) broadcast ram; + emptySlot4(uint32, int16, int16) broadcast ram; + emptySlot5(uint32, int16, int16) broadcast ram; + emptySlot6(uint32, int16, int16) broadcast ram; + emptySlot7(uint32, int16, int16) broadcast ram; + requestBoard() airecv clsend; + rejectBoard(uint32, uint8); + requestExit() airecv clsend; +}; + +dclass DistributedElevatorFSM : DistributedObject { + setBldgDoId(uint32) required broadcast ram; + setState(string, int16) broadcast ram; + fillSlot0(uint32) broadcast ram; + fillSlot1(uint32) broadcast ram; + fillSlot2(uint32) broadcast ram; + fillSlot3(uint32) broadcast ram; + fillSlot4(uint32) broadcast ram; + fillSlot5(uint32) broadcast ram; + fillSlot6(uint32) broadcast ram; + fillSlot7(uint32) broadcast ram; + emptySlot0(uint32, int16) broadcast ram; + emptySlot1(uint32, int16) broadcast ram; + emptySlot2(uint32, int16) broadcast ram; + emptySlot3(uint32, int16) broadcast ram; + emptySlot4(uint32, int16) broadcast ram; + emptySlot5(uint32, int16) broadcast ram; + emptySlot6(uint32, int16) broadcast ram; + emptySlot7(uint32, int16) broadcast ram; + requestBoard() airecv clsend; + rejectBoard(uint32, uint8); + requestExit() airecv clsend; +}; + +dclass DistributedElevatorFloor : DistributedElevatorFSM { + setFloor(int8) broadcast ram; + setLocked(uint16) required broadcast ram; + setEntering(uint16) required broadcast ram; + kickToonsOut() broadcast; + setLatch(uint32) required broadcast ram; +}; + +dclass DistributedElevatorExt : DistributedElevator { + setFloor(int8) broadcast ram; +}; + +dclass DistributedLawOfficeElevatorExt : DistributedElevatorExt { + setEntranceId(uint8) required broadcast ram; + setLawOfficeInteriorZone(uint32); + setLawOfficeInteriorZoneForce(uint32); +}; + +dclass DistributedElevatorInt : DistributedElevator { + requestBuildingExit() airecv clsend; + forcedExit(uint32); +}; + +dclass DistributedFactoryElevatorExt : DistributedElevatorExt { + setEntranceId(uint8) required broadcast ram; + setFactoryInteriorZone(uint32); + setFactoryInteriorZoneForce(uint32); +}; + +dclass DistributedMintElevatorExt : DistributedElevatorExt { + setMintId(uint16) required broadcast ram; + setMintInteriorZone(uint32); + setMintInteriorZoneForce(uint32); +}; + +dclass DistributedCogdoElevatorExt : DistributedElevatorExt { +}; + +dclass DistributedLawOfficeElevatorInt : DistributedElevatorFloor { + setLawOfficeInteriorZone(uint32); +}; + +dclass DistributedCogdoElevatorInt : DistributedElevatorInt { +}; + +dclass DistributedBossElevator : DistributedElevatorExt { + setBossOfficeZone(uint32); + setBossOfficeZoneForce(uint32); +}; + +dclass DistributedVPElevator : DistributedBossElevator { +}; + +dclass DistributedCFOElevator : DistributedBossElevator { +}; + +dclass DistributedCJElevator : DistributedBossElevator { +}; + +dclass DistributedBBElevator : DistributedBossElevator { +}; + +dclass DistributedBoardingParty : DistributedObject { + postGroupInfo(uint32, uint32[], uint32[], uint32[]) broadcast; + informDestinationInfo(uint8) clsend airecv; + postDestinationInfo(uint8) broadcast; + postInvite(uint32, uint32, bool) broadcast; + postInviteCanceled() broadcast; + postKick(uint32) broadcast; + postKickReject(uint32, uint32, uint32) broadcast; + postSizeReject(uint32, uint32, uint32) broadcast; + postInviteAccepted(uint32) broadcast; + postInviteDelcined(uint32) broadcast; + postInviteNotQualify(uint32, int8) broadcast; + postAlreadyInGroup() broadcast; + postGroupDissolve(uint32, uint32, uint32 [], uint8) broadcast; + postMessageAcceptanceFailed(uint32, int8) broadcast; + postGroupAlreadyFull() broadcast; + postSomethingMissing() broadcast; + postRejectBoard(uint32, int8, uint32 [], uint32 []) broadcast; + postRejectGoto(uint32, int8, uint32 [], uint32 []) broadcast; + postMessageInvited(uint32, uint32) broadcast; + postMessageInvitationFailed(uint32) broadcast; + acceptGoToFirstTime(uint32) broadcast; + acceptGoToSecondTime(uint32) broadcast; + rejectGoToRequest(uint32, int8, uint32 [], uint32 []) broadcast; + requestInvite(uint32) airecv clsend; + requestCancelInvite(uint32) airecv clsend; + requestAcceptInvite(uint32, uint32) airecv clsend; + requestRejectInvite(uint32, uint32) airecv clsend; + requestKick(uint32) airecv clsend; + requestLeave(uint32) airecv clsend; + requestBoard(uint32) airecv clsend; + requestGoToFirstTime(uint32) airecv clsend; + requestGoToSecondTime(uint32) airecv clsend; + setElevatorIdList(uint32[]) required broadcast ram; + setGroupSize(uint8) required broadcast ram; +}; + +dclass DistributedTutorialInterior : DistributedObject { + setZoneIdAndBlock(uint32, uint16) required broadcast ram; + setTutorialNpcId(uint32) required broadcast ram; +}; + +dclass DistributedMailbox : DistributedObject { + setHouseId(uint32) required broadcast ram; + setHousePos(uint8) required broadcast ram; + setName(string) required broadcast ram; + setFullIndicator(uint8) broadcast ram; + avatarEnter() airecv clsend; + avatarExit() airecv clsend; + freeAvatar(); + setMovie(uint8, uint32) broadcast ram; + acceptItemMessage(uint16, blob, uint8, int32) airecv clsend; + acceptItemResponse(uint16, int8); + discardItemMessage(uint16, blob, uint8, int32) airecv clsend; + discardItemResponse(uint16, int8); + acceptInviteMessage(uint16, uint64) airecv clsend; + rejectInviteMessage(uint16, uint64) airecv clsend; + markInviteReadButNotReplied(uint64) airecv clsend; +}; + +dclass DistributedFurnitureManager : DistributedObject { + setOwnerId(uint32 ownerId) required broadcast ram; + setOwnerName(string ownerName) required broadcast ram; + setInteriorId(uint32 interiorId) required broadcast ram; + setAtticItems(blob atticItems) required broadcast ram; + setAtticWallpaper(blob atticWallpaper) required broadcast ram; + setAtticWindows(blob atticWindows) required broadcast ram; + setDeletedItems(blob deletedItems) required broadcast ram; + suggestDirector(uint32 directorId) airecv clsend; + setDirector(uint32 directorId) broadcast ram; + avatarEnter() airecv clsend; + avatarExit() airecv clsend; + moveItemToAtticMessage(uint32 doId, uint16 context) airecv clsend; + moveItemToAtticResponse(int8 retval, uint16 context); + moveItemFromAtticMessage(uint16 index, int16/10 x, int16/10 y, int16/100 z, int16/10 h, int16/10 p, int16/10 r, uint16 context) airecv clsend; + moveItemFromAtticResponse(int8 retval, uint32 doId, uint16 context); + deleteItemFromAtticMessage(blob item, uint16 index, uint16 context) airecv clsend; + deleteItemFromAtticResponse(int8 retval, uint16 context); + deleteItemFromRoomMessage(blob item, uint32 doId, uint16 context) airecv clsend; + deleteItemFromRoomResponse(int8 retval, uint16 context); + moveWallpaperFromAtticMessage(uint16 index, uint8 room, uint16 context) airecv clsend; + moveWallpaperFromAtticResponse(int8 retval, uint16 context); + deleteWallpaperFromAtticMessage(blob item, uint16 index, uint16 context) airecv clsend; + deleteWallpaperFromAtticResponse(int8 retval, uint16 context); + moveWindowToAtticMessage(uint8 slot, uint16 context) airecv clsend; + moveWindowToAtticResponse(int8 retval, uint16 context); + moveWindowFromAtticMessage(uint16 index, uint8 slot, uint16 context) airecv clsend; + moveWindowFromAtticResponse(int8 retval, uint16 context); + moveWindowMessage(uint8 fromSlot, uint8 toSlot, uint16 context) airecv clsend; + moveWindowResponse(int8 retval, uint16 context); + deleteWindowFromAtticMessage(blob item, uint16 index, uint16 context) airecv clsend; + deleteWindowFromAtticResponse(int8 retval, uint16 context); + recoverDeletedItemMessage(blob item, uint16 index, uint16 context) airecv clsend; + recoverDeletedItemResponse(int8 retval, uint16 context); +}; + +dclass DistributedFurnitureItem : DistributedSmoothNode { + setItem(uint32 furnitureMgrId, blob item) required broadcast ram; + requestPosHpr(uint8 final, int16/10 x, int16/10 y, int16/100 z, int16/10 h, int16/10 p, int16/10 r, int16 t) airecv clsend; + setMode(uint8 mdoe, uint32 avId) required broadcast ram; +}; + +dclass DistributedBank : DistributedFurnitureItem { + avatarEnter() airecv clsend; + freeAvatar(); + setMovie(uint8, uint32, int16) broadcast ram; + transferMoney(int16) airecv clsend; +}; + +dclass DistributedCloset : DistributedFurnitureItem { + setOwnerId(uint32) required broadcast ram; + enterAvatar() airecv clsend; + freeAvatar(); + removeItem(blob, uint8) airecv clsend; + setDNA(blob, int8, uint8) airecv clsend; + setState(uint8, uint32, uint32, string, uint8[], uint8[]) broadcast ram; + setMovie(uint8, uint32, int16) broadcast ram; + setCustomerDNA(uint32, blob) broadcast ram; +}; + +dclass DistributedTrunk : DistributedCloset { + setState(uint8, uint32, uint32, string, uint8[], uint8[], uint8[], uint8[]) broadcast ram; + removeItem(uint8, uint8, uint8, uint8) airecv clsend; + setDNA(uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, int8, uint8) airecv clsend; + setCustomerDNA(uint32, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8) broadcast ram; +}; + +dclass DistributedPhone : DistributedFurnitureItem { + setInitialScale(uint8/170, uint8/170, uint8/170) required broadcast ram; + setNewScale(uint8/170, uint8/170, uint8/170) airecv clsend; + avatarEnter() airecv clsend; + avatarExit() airecv clsend; + freeAvatar(); + setLimits(uint16); + setMovie(uint8, uint32, int32) broadcast ram; + requestPurchaseMessage(uint16, blob, int32) airecv clsend; + requestPurchaseResponse(uint16, int8); + requestGiftPurchaseMessage(uint16, uint32, blob, int32) airecv clsend; + requestGiftPurchaseResponse(uint16, int8); + purchaseItemComplete(); + requestGiftAvatar(uint32) airecv clsend; + setGiftAvatar(blob); +}; + +dclass DistributedRewardCrate : DistributedFurnitureItem { + requestKeyUsage() airecv clsend; + useKeyResponse(uint8, uint32); +}; + +dclass DistributedChair : DistributedFurnitureItem { + setAvId(uint32) broadcast ram; + requestSit(uint8) airecv clsend; + setStatus(uint8) broadcast ram; +}; + +dclass DistributedTV : DistributedFurnitureItem { + setVideo(string(0-255), uint32) required broadcast ram; + requestVideo(string(0-255)) airecv clsend; + requestVideoResponse(uint8); +}; + +dclass DistributedFireworkShow : DistributedObject { + startShow(uint8, uint8, uint8, int16) broadcast ram; + requestFirework(int16/10, int16/10, int16/100, uint8, uint8, uint8) airecv clsend; + shootFirework(int16/10, int16/10, int16/100, uint8, uint8, uint8) broadcast; +}; + +dclass DistributedFireworksCannon : DistributedFireworkShow { + avatarEnter() airecv clsend; + avatarExit() airecv clsend; + freeAvatar(); + setMovie(uint8, uint32, int16) broadcast ram; + setPosition(int16/10, int16/10, int16/10) required broadcast ram; +}; + +dclass LobbyManager : DistributedObject { +}; + +dclass DistributedFactory : DistributedLevel { + setFactoryId(uint16) required broadcast ram; + setSuits(uint32[], uint32[]) broadcast ram; + setForemanConfronted(uint32) broadcast ram; + setDefeated() broadcast ram; +}; + +dclass DistributedMegaCorp: DistributedFactory { +}; + +dclass DistributedLawOffice : DistributedObject { + setLawOfficeId(uint16) required broadcast ram; + startSignal() broadcast ram; + readyForNextFloor() airecv clsend; +}; + +dclass DistributedLawOfficeFloor : DistributedLevel { + setLawOfficeId(uint16) required broadcast ram; + setSuits(uint32[], uint32[]) broadcast ram; + readyForNextFloor() airecv clsend; + setForemanConfronted(uint32) broadcast ram; + setDefeated() broadcast ram; +}; + +dclass DistributedMint : DistributedObject { + setZoneId(uint32) required broadcast ram; + setMintId(uint16) required broadcast ram; + setFloorNum(uint8) required broadcast ram; + setRoomDoIds(uint32[]) broadcast ram; +}; + +dclass DistributedMintRoom : DistributedLevel { + setMintId(uint16) required broadcast ram; + setRoomId(uint16) required broadcast ram; + setRoomNum(uint8) required broadcast ram; + setSuits(uint32[], uint32[]) broadcast ram; + setBossConfronted(uint32) broadcast ram; + setDefeated() broadcast ram; +}; + +dclass DistributedStage : DistributedObject { + setZoneId(uint32) required broadcast ram; + setStageId(uint16) required broadcast ram; + setLayoutIndex(uint16) required broadcast ram; + setFloorNum(uint8) required broadcast ram; + setRoomDoIds(uint32[]) broadcast ram; + setStageZone(uint32) broadcast ram; + elevatorAlert(uint32) broadcast ram; +}; + +dclass DistributedStageRoom : DistributedLevel { + setStageId(uint16) required broadcast ram; + setRoomId(uint16) required broadcast ram; + setRoomNum(uint8) required broadcast ram; + setSuits(uint32[], uint32[]) broadcast ram; + setBossConfronted(uint32) broadcast ram; + setDefeated() broadcast ram; +}; + +dclass DistributedLift : DistributedEntity { + setStateTransition(uint8, uint8, uint32) required broadcast ram; + setAvatarEnter() airecv clsend; + setAvatarLeave() airecv clsend; +}; + +dclass DistributedDoorEntity : DistributedEntity { + setLocksState(uint16) required broadcast ram; + setDoorState(uint8, int32) required broadcast ram; + requestOpen() airecv clsend; +}; + +dclass DistributedSwitch : DistributedInteractiveEntity { +}; + +dclass DistributedButton : DistributedSwitch { +}; + +dclass DistributedTrigger : DistributedSwitch { +}; + +dclass DistributedCrushableEntity : DistributedEntity { + setPosition(int16/10, int16/10, int16/10) broadcast ram; + setCrushed(uint32, uint8) broadcast ram; +}; + +dclass DistributedCrusherEntity : DistributedEntity { +}; + +dclass DistributedElevatorMarker : DistributedEntity { +}; + +dclass DistributedStomper : DistributedCrusherEntity { + setMovie(uint8, int16, uint32[]) broadcast ram; +}; + +dclass DistributedStomperPair : DistributedEntity { + setChildren(uint32[]) broadcast ram; + setSquash() airecv clsend; +}; + +dclass DistributedBarrelBase : DistributedEntity { + requestGrab() airecv clsend; + setGrab(uint32) broadcast ram; + setReject() broadcast; +}; + +dclass DistributedGagBarrel : DistributedBarrelBase { +}; + +dclass DistributedBeanBarrel : DistributedBarrelBase { +}; + +dclass DistributedHealBarrel : DistributedBarrelBase { +}; + +dclass DistributedGrid : DistributedEntity { +}; + +dclass ActiveCell : DistributedEntity { + setState(uint8, uint32) broadcast ram; +}; + +dclass DirectionalCell : ActiveCell { +}; + +dclass CrusherCell : ActiveCell { +}; + +dclass DistributedCrate : DistributedCrushableEntity { + requestPush(uint8) airecv clsend; + setReject(); + setAccept() broadcast; + setMoveTo(uint32, int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) broadcast ram; + setDone() airecv clsend; +}; + +dclass DistributedSinkingPlatform : DistributedEntity { + setOnOff(uint8, uint32) airecv clsend; + setSinkMode(uint32, uint8, uint32) broadcast ram; +}; + +dclass DistributedGoon : DistributedCrushableEntity { + requestBattle(int16/10) airecv clsend; + requestStunned(int16/10) airecv clsend; + requestResync() airecv clsend; + setParameterize(int16/10, int16/10, int16/10, uint32) airecv clsend; + setMovie(uint8, uint32, int32/10, int16) broadcast ram; +}; + +dclass DistributedGridGoon : DistributedGoon { + setPathPts(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) broadcast ram; +}; + +dclass BattleBlocker : DistributedEntity { + setActive(uint8) required broadcast ram; + setSuits(uint32[]) broadcast ram; + setBattle(uint32) broadcast ram; + setBattleFinished() broadcast ram; +}; + +dclass DistributedLaserField : BattleBlocker { + setGrid(uint8, uint8) required broadcast ram; + setField(uint8 []) required broadcast ram; + setSuccess(uint8) broadcast ram; + hit(int8, int8, int8, int8) airecv clsend; + trapFire() airecv clsend; + setActiveLF(uint8) broadcast ram; + hideSuit(uint32[]) broadcast ram; + showSuit(uint32[]) broadcast ram; + setGridGame(string) broadcast ram; +}; + +struct golfGreenGameBoardData { + uint8 posX; + uint8 posZ; + uint8 typeIndex; +}; + +struct golfGreenGameScoreData { + uint32 avId; + uint8 score; +}; + +dclass DistributedGolfGreenGame : BattleBlocker { + requestJoin() airecv clsend; + leaveGame() airecv clsend; + acceptJoin(uint16, int32, uint32 []) broadcast ram; + requestBoard(uint8) airecv clsend; + startBoard(golfGreenGameBoardData [], uint8 []); + signalDone(uint8) broadcast ram; + boardCleared(uint32); + scoreData(uint8, uint8, golfGreenGameScoreData []) broadcast ram; + informGag(uint8, uint8); + helpOthers(uint32) broadcast; + setTimerStart(uint16, int32) broadcast ram; +}; + +dclass DistributedSecurityCamera : DistributedEntity { + trapFire() airecv clsend; + setTarget(uint8) broadcast ram; +}; + +dclass DistributedMover : DistributedEntity { + startMove(int16) broadcast ram; +}; + +typedef uint16/10000 PetTrait; + +dclass DistributedPet : DistributedSmoothNode { + setOwnerId(uint32) required broadcast db; + setPetName(string) required broadcast db; + setTraitSeed(uint32) required broadcast db; + setSafeZone(uint32) required broadcast db; + setForgetfulness(PetTrait) required broadcast db; + setBoredomThreshold(PetTrait) required broadcast db; + setRestlessnessThreshold(PetTrait) required broadcast db; + setPlayfulnessThreshold(PetTrait) required broadcast db; + setLonelinessThreshold(PetTrait) required broadcast db; + setSadnessThreshold(PetTrait) required broadcast db; + setFatigueThreshold(PetTrait) required broadcast db; + setHungerThreshold(PetTrait) required broadcast db; + setConfusionThreshold(PetTrait) required broadcast db; + setExcitementThreshold(PetTrait) required broadcast db; + setAngerThreshold(PetTrait) required broadcast db; + setSurpriseThreshold(PetTrait) required broadcast db; + setAffectionThreshold(PetTrait) required broadcast db; + setHead(int8(-1 - 1)) required broadcast db; // Supposed to be -1 - 0, but minification causes this to become -1-0, which is a parse problem. + setEars(int8(-1 - 4)) required broadcast db; + setNose(int8(-1 - 3)) required broadcast db; + setTail(int8(-1 - 6)) required broadcast db; + setBodyTexture(int8(0-6)) required broadcast db; + setColor(int8(0-25)) required broadcast db; + setColorScale(int8(0-8)) required broadcast db; + setEyeColor(int8(0-5)) required broadcast db; + setGender(int8(0-1)) required broadcast db; + setLastSeenTimestamp(uint32) required broadcast db; + setBoredom(uint16/1000(0-1)) required broadcast db; + setRestlessness(uint16/1000(0-1)) required broadcast db; + setPlayfulness(uint16/1000(0-1)) required broadcast db; + setLoneliness(uint16/1000(0-1)) required broadcast db; + setSadness(uint16/1000(0-1)) required broadcast db; + setAffection(uint16/1000(0-1)) required broadcast db; + setHunger(uint16/1000(0-1)) required broadcast db; + setConfusion(uint16/1000(0-1)) required broadcast db; + setExcitement(uint16/1000(0-1)) required broadcast db; + setFatigue(uint16/1000(0-1)) required broadcast db; + setAnger(uint16/1000(0-1)) required broadcast db; + setSurprise(uint16/1000(0-1)) required broadcast db; + setMood : setBoredom, setRestlessness, setPlayfulness, setLoneliness, setSadness, setAffection, setHunger, setConfusion, setExcitement, setFatigue, setAnger, setSurprise; + teleportIn(int16) broadcast ownsend; + teleportOut(int16) broadcast ownsend; + setTrickAptitudes(uint16/10000(0-1) []) required broadcast db; + doTrick(uint8, int16) broadcast ram; + avatarInteract(uint32); + setMovie(uint8, uint32, int16) broadcast ram; + freeAvatar(); +}; + +dclass DistributedPetProxy : DistributedPet { + setDominantMood(string) broadcast ram; +}; + +dclass DistributedBlackCatMgr : DistributedObject { + doBlackCatTransformation() broadcast; + requestBlackCatTransformation() airecv clsend; +}; + +dclass DistributedReportMgr : DistributedObject { + sendReport(uint32, string) airecv clsend; +}; + +dclass DistributedPolarPlaceEffectMgr : DistributedObject { + addPolarPlaceEffect() airecv clsend; +}; + +dclass DistributedEffectMgr : DistributedObject { + setHoliday(uint8) required broadcast; + requestEffect() airecv clsend; + effectDone(uint8); +}; + +dclass DistributedResistanceEmoteMgr : DistributedObject { + addResistanceEmote() clsend airecv; +}; + +dclass DistributedVehicle : DistributedSmoothNode { + setOwner(uint32) required broadcast ram; + setState(char, uint32) broadcast ram; + setBodyType(int8) required broadcast ram; + setBodyColor(int8) required broadcast ram; + setAccessoryColor(int8) required broadcast ram; + setEngineBlockType(int8) required broadcast ram; + setSpoilerType(int8) required broadcast ram; + setFrontWheelWellType(int8) required broadcast ram; + setBackWheelWellType(int8) required broadcast ram; + setRimType(int8) required broadcast ram; + setDecalType(int8) required broadcast ram; + requestControl() airecv clsend; + requestParked() airecv clsend; + setInput(int8) broadcast ram; +}; + +struct avatarAndKart { + uint32 avId; + uint32 kartId; +}; + +dclass DistributedRace : DistributedObject { + setZoneId(uint32) required broadcast ram; + setTrackId(uint16) required broadcast ram; + setRaceType(uint16) required broadcast ram; + setCircuitLoop(uint16[]) required broadcast ram; + setAvatars(uint32[]) required broadcast ram; + setStartingPlaces(uint8[]) required broadcast ram; + setLapCount(uint8) broadcast required ram; + waitingForJoin() broadcast ram; + setEnteredRacers(avatarAndKart []) broadcast ram; + prepForRace() broadcast ram; + startTutorial() broadcast ram; + startRace(int16) broadcast ram; + goToSpeedway(uint32[], uint8) broadcast ram; + genGag(uint8, uint16, uint8) broadcast ram; + dropAnvilOn(uint32, uint32, int16) broadcast ram; + shootPiejectile(uint32, uint32, uint8) broadcast ram; + racerDisconnected(uint32) broadcast ram; + setPlace(uint32, uint32/1000, uint8, uint32, uint8, uint32, uint32, uint32[], uint8[], uint32/1000) broadcast ram; + setCircuitPlace(uint32, uint8, uint32, uint32, uint32, uint32[]) broadcast ram; + endCircuitRace() broadcast ram; + setRaceZone(uint32, uint32); + hasGag(uint8, uint8, uint8) broadcast airecv clsend; + racerLeft(uint32) clsend airecv broadcast ram; + heresMyT(uint32, int8, uint16/65535, int16) clsend airecv broadcast; + requestThrow(int32/1000, int32/1000, int32/1000) clsend airecv; + requestKart() clsend airecv; +}; + +dclass DistributedGag : DistributedObject { + setInitTime(int16) required broadcast ram; + setActivateTime(int16) required broadcast ram; + setPos(int32/1000, int32/1000, int32/1000) required broadcast ram; + setRace(uint32) required broadcast ram; + setOwnerId(uint32) required broadcast ram; + setType(uint8) required broadcast ram; + hitSomebody(uint32, int16) broadcast clsend airecv; +}; + +dclass DistributedProjectile : DistributedObject { + setInitTime(int16) required broadcast ram; + setPos(int32/1000, int32/1000, int32/1000) required broadcast ram; + setRace(uint32) required broadcast ram; + setOwnerId(uint32) required broadcast ram; + setType(uint8) required broadcast ram; + hitSomebody(uint32, int16) broadcast clsend airecv; +}; + +dclass DistributedKartPad : DistributedObject { + setArea(uint32) required broadcast ram; +}; + +dclass DistributedRacePad : DistributedKartPad { + setState(string, int16) required broadcast ram; + setRaceZone(uint32); + setTrackInfo(uint16[]) required broadcast ram; +}; + +dclass DistributedViewPad : DistributedKartPad { + setLastEntered(int16) required broadcast ram; +}; + +dclass DistributedStartingBlock : DistributedObject { + setPadDoId(uint32) required broadcast ram; + setPosHpr(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) required broadcast ram; + setPadLocationId(uint8) required broadcast ram; + requestEnter() airecv clsend; + rejectEnter(uint8); + requestExit() airecv clsend; + setOccupied(uint32) broadcast ram; + setMovie(uint8) broadcast ram; + movieFinished() airecv clsend; +}; + +dclass DistributedViewingBlock : DistributedStartingBlock { +}; + +struct LeaderboardResult { + string name; + uint16 time; +}; + +dclass DistributedLeaderBoard : DistributedObject { + setPosHpr(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) required broadcast ram; + setDisplay(uint8, uint8, LeaderboardResult[]) required broadcast ram; +}; + +dclass DistributedLawbotBoss : DistributedBossCog { + setState(string) broadcast ram; + setBossDamage(uint16, uint8, int16) broadcast ram; + touchWitnessStand() airecv clsend; + hitBoss(uint8) airecv clsend; + healBoss(uint8) airecv clsend; + hitToon(uint32) airecv clsend; + hitDefensePan() airecv clsend; + hitProsecutionPan() airecv clsend; + hitChair(uint8, uint8) airecv clsend; + setLawyerIds(uint32[]) broadcast ram; + setTaunt(int8, int8) broadcast; + toonGotHealed(uint32) broadcast; + enteredBonusState() broadcast; + setBattleDifficulty(uint8) broadcast ram; +}; + +dclass DistributedLawbotBossSuit : DistributedSuitBase { + setPosHpr(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) required broadcast ram; + doAttack(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) broadcast; + doProsecute() broadcast; + hitByToon() airecv clsend; + doStun() broadcast; +}; + +dclass DistributedLawbotBossGavel : DistributedObject { + setBossCogId(uint32) required broadcast ram; + setIndex(uint8) required broadcast ram; + setState(char) broadcast ram; +}; + +dclass DistributedLawbotCannon : DistributedObject { + setBossCogId(uint32) required broadcast ram; + setIndex(uint8) required broadcast ram; + setPosHpr(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) required broadcast ram; + requestEnter() airecv clsend; + setMovie(int8, uint32, uint8) broadcast; + setCannonPosition(int16/10, int16/10) airecv clsend; + updateCannonPosition(uint32, int16/10, int16/10) broadcast; + setCannonLit(int16/10, int16/10) airecv clsend; + setCannonWillFire(uint32, int16/10, int16/10, int16/10, int16) broadcast; + setLanded() airecv clsend; + requestLeave() airecv clsend; +}; + +dclass DistributedLawbotChair : DistributedObject { + setBossCogId(uint32) required broadcast ram; + setIndex(uint8) required broadcast ram; + setState(char) broadcast ram; + showCogJurorFlying() broadcast; + setToonJurorIndex(int8) broadcast ram; +}; + +dclass DistributedLawnDecor : DistributedNode { + setPlot(int8) required broadcast ram; + setHeading(int16/10) required broadcast ram; + setOwnerIndex(int8) required broadcast ram; + setPosition(int16/10, int16/10, int16/10) required broadcast ram; + plotEntered() airecv clsend; + removeItem() airecv clsend; + setMovie(uint8, uint32) broadcast ram; + movieDone() airecv clsend; + interactionDenied(uint32) broadcast ram; + setBoxDoId(uint32, uint8) broadcast ram; +}; + +dclass DistributedGardenPlot : DistributedLawnDecor { + plantFlower(uint8, uint8) airecv clsend; + plantGagTree(uint8, uint8) airecv clsend; + plantStatuary(uint8) airecv clsend; + plantToonStatuary(uint8, uint16) airecv clsend; + plantNothing(uint8) airecv clsend; +}; + +dclass DistributedGardenBox : DistributedLawnDecor { + setTypeIndex(uint8) required broadcast ram; +}; + +dclass DistributedStatuary : DistributedLawnDecor { + setTypeIndex(uint8) required broadcast ram; + setWaterLevel(int8) required broadcast ram; + setGrowthLevel(int8) required broadcast ram; +}; + +dclass DistributedToonStatuary : DistributedStatuary { + setOptional(uint16) required broadcast ram; +}; + +dclass DistributedAnimatedStatuary : DistributedStatuary { +}; + +dclass DistributedChangingStatuary : DistributedStatuary { + setGrowthLevel(int8) required broadcast ram; +}; + +dclass DistributedPlantBase : DistributedLawnDecor { + setTypeIndex(uint8) required broadcast ram; + setWaterLevel(int8) required broadcast ram; + setGrowthLevel(int8) required broadcast ram; + waterPlant() airecv clsend; + waterPlantDone() airecv clsend; +}; + +dclass DistributedFlower : DistributedPlantBase { + setTypeIndex(uint8) required broadcast ram; + setVariety(uint8) required broadcast ram; +}; + +dclass DistributedGagTree : DistributedPlantBase { + setWilted(int8) required broadcast ram; + setFruiting(bool) required broadcast ram; + requestHarvest() airecv clsend; +}; + +struct golfData { + int16 frame; + int32/100000 x; + int32/100000 y; + int32/100000 z; +}; + +struct Coord3 { + int32/100000 x; + int32/100000 y; + int32/100000 z; +}; + +struct CommonObjectData { + uint8 id; + uint8 type; + int32/100000 x; + int32/100000 y; + int32/100000 z; + int32/100000 q1; + int32/100000 q2; + int32/100000 q3; + int32/100000 q4; + int32/100000 aVX; + int32/100000 aVY; + int32/100000 aVZ; + int32/100000 lVX; + int32/100000 lVY; + int32/100000 lVZ; +}; + +dclass DistributedPhysicsWorld : DistributedObject { + clientCommonObject(uint8, uint8, Coord3, Coord3, int32/100, int32/100, int32/1000) broadcast ram; + setCommonObjects(CommonObjectData []) broadcast; + upSetCommonObjects(CommonObjectData []) airecv clsend; +}; + +dclass DistributedGolfHole : DistributedPhysicsWorld { + setHoleId(int8) broadcast ram required; + setTimingCycleLength(uint32/1000) broadcast ram required; + setAvatarReadyHole() airecv clsend; + setGolfCourseDoId(uint32) broadcast ram required; + turnDone() airecv clsend; + ballInHole() airecv clsend; + setAvatarTempTee(uint32, uint8) clsend broadcast; + setTempAimHeading(uint32, int32/1000) clsend broadcast; + setAvatarFinalTee(uint32, uint8) broadcast; + setGolferIds(uint32[]) broadcast ram required; + golfersTurn(uint32) broadcast; + golferChooseTee(uint32) broadcast; + setAvatarTee(uint8) airecv clsend; + postSwing(uint32/1000, int32, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000) airecv clsend; + postSwingState(uint32/1000, int32, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, uint16/100, CommonObjectData []) airecv clsend; + swing(uint32, int32, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000) broadcast; + ballMovie2AI(uint32/1000, uint32, golfData [], golfData [], uint16, uint16, uint16, CommonObjectData []) airecv clsend; + ballMovie2Client(uint32/1000, uint32, golfData [], golfData [], uint16, uint16, uint16, CommonObjectData []) broadcast; + assignRecordSwing(uint32, uint32/1000, int32, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, CommonObjectData []); + setBox(int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000) airecv clsend; + sendBox(int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000, int32/1000) broadcast; +}; + +dclass DistributedGolfCourse : DistributedObject { + setGolferIds(uint32[]) broadcast ram required; + setCourseId(int8) broadcast ram required; + setAvatarJoined() airecv clsend; + setAvatarReadyCourse() airecv clsend; + setAvatarReadyHole() airecv clsend; + setAvatarExited() airecv clsend; + setCurHoleIndex(int8) broadcast ram required; + setCurHoleDoId(uint32) broadcast ram required; + setDoneReward() airecv clsend; + setReward(uint8[] [], int8[], uint8[] [], uint8[] [], uint8[] [], uint32, uint32/100, uint32/100, uint32/100, uint32/100) broadcast; + setCourseReady(int8, int16[], int8) broadcast; + setHoleStart(int16) broadcast; + setCourseExit() broadcast; + setCourseAbort(uint32) broadcast; + setPlayHole() broadcast; + avExited(uint32) broadcast; + setScores(int16 []) broadcast; + changeDrivePermission(uint32, int8) broadcast; +}; + +dclass DistributedVineGame : DistributedMinigame { + reachedEndVine(int8) clsend airecv; + setNewVine(uint32, int8, uint32/10000, int8) airecv clsend broadcast; + setNewVineT(uint32, uint32/10000, int8) clsend broadcast; + setJumpingFromVine(uint32, int8, int8, int32/100, int16/100, int16/100, int16) clsend broadcast; + claimTreasure(uint32) airecv clsend; + setTreasureGrabbed(uint32, uint32) broadcast; + setScore(uint32, uint32) broadcast; + allAtEndVine() broadcast; + setFallingFromVine(uint32, int8, int8, int32/100, int16/100, int16/100, int16, int8) clsend broadcast; + setFallingFromMidair(uint32, int8, int32/100, int16/100, int16/100, int16, int8) clsend broadcast; + setVineSections(uint8[]) required broadcast ram; +}; + +dclass DistributedGolfKart : DistributedObject { + setState(string, int16) broadcast ram; + fillSlot0(uint32) broadcast ram; + fillSlot1(uint32) broadcast ram; + fillSlot2(uint32) broadcast ram; + fillSlot3(uint32) broadcast ram; + emptySlot0(uint32, int16) broadcast ram; + emptySlot1(uint32, int16) broadcast ram; + emptySlot2(uint32, int16) broadcast ram; + emptySlot3(uint32, int16) broadcast ram; + requestBoard() airecv clsend; + rejectBoard(uint32); + requestExit() airecv clsend; + setMinigameZone(uint32, uint16); + setGolfZone(uint32, uint16); + setGolfCourse(int8) required broadcast ram; + setPosHpr(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) required broadcast ram; + setColor(int16, int16, int16) required broadcast ram; +}; + +dclass DistributedTimer : DistributedObject { + setStartTime(int32) broadcast ram required; +}; + +dclass DistributedPicnicBasket : DistributedObject { + setState(string, uint16, int16) broadcast ram; + fillSlot0(uint32) broadcast ram; + fillSlot1(uint32) broadcast ram; + fillSlot2(uint32) broadcast ram; + fillSlot3(uint32) broadcast ram; + emptySlot0(uint32, int16) broadcast ram; + emptySlot1(uint32, int16) broadcast ram; + emptySlot2(uint32, int16) broadcast ram; + emptySlot3(uint32, int16) broadcast ram; + requestBoard(int16) airecv clsend; + rejectBoard(uint32); + requestExit() airecv clsend; + doneExit() airecv clsend; + setMinigameZone(uint32, uint16); + setPicnicDone(); + setPosHpr(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) required broadcast ram; + setTableNumber(int16) required broadcast ram; +}; + +dclass DistributedGameTable : DistributedObject { + requestJoin(uint8) airecv clsend; + rejectJoin(); + requestExit() airecv clsend; + fillSlot0(uint32) broadcast ram; + fillSlot1(uint32) broadcast ram; + fillSlot2(uint32) broadcast ram; + fillSlot3(uint32) broadcast ram; + fillSlot4(uint32) broadcast ram; + fillSlot5(uint32) broadcast ram; + emptySlot0(uint32, int16) broadcast ram; + emptySlot1(uint32, int16) broadcast ram; + emptySlot2(uint32, int16) broadcast ram; + emptySlot3(uint32, int16) broadcast ram; + emptySlot4(uint32, int16) broadcast ram; + emptySlot5(uint32, int16) broadcast ram; + setPosHpr(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) required broadcast ram; + announceWinner(uint32) broadcast; +}; + +dclass DistributedBossbotBoss : DistributedBossCog { + setState(string) broadcast ram; + setBattleDifficulty(uint8) broadcast ram; + requestGetFood(int8, int8, uint32) airecv clsend; + toonGotFood(uint32, int8, int8, uint32) broadcast; + requestServeFood(int8, int8) airecv clsend; + toonServeFood(uint32, int8, int8) broadcast; + hitBoss(uint8) airecv clsend; + hitToon(uint32) airecv clsend; + ballHitBoss(uint8) airecv clsend; + setBossDamage(uint16, uint8, int16) broadcast ram; + setSpeedDamage(uint16, uint8, int16) broadcast ram; + reachedTable(uint8) airecv clsend; + hitTable(uint8) airecv clsend; + awayFromTable(uint8) airecv clsend; + toonGotHealed(uint32) broadcast; + requestGetToonup(int8, int8, uint32) airecv clsend; + toonGotToonup(uint32, int8, int8, uint32) broadcast; +}; + +dclass DistributedCogKart : DistributedElevatorExt { + setCountryClubId(uint16) required broadcast ram; + setPosHpr(int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) required broadcast ram; + setCountryClubInteriorZone(uint32); + setCountryClubInteriorZoneForce(uint32); +}; + +dclass DistributedCountryClub : DistributedObject { + setZoneId(uint32) required broadcast ram; + setBlockedRooms(uint8[]) required broadcast ram; + setCountryClubId(uint16) required broadcast ram; + setLayoutIndex(uint16) required broadcast ram; + setFloorNum(uint8) required broadcast ram; + setRoomDoIds(uint32[]) broadcast ram; + setCountryClubZone(uint32) broadcast ram; + elevatorAlert(uint32) broadcast ram; +}; + +dclass DistributedCountryClubRoom : DistributedLevel { + setCountryClubId(uint16) required broadcast ram; + setRoomId(uint16) required broadcast ram; + setRoomNum(uint8) required broadcast ram; + setSuits(uint32[], uint32[]) broadcast ram; + setBossConfronted(uint32) broadcast ram; + setDefeated() broadcast ram; + forceOuch(uint8) broadcast; +}; + +dclass DistributedMoleField : DistributedEntity { + setGameStart(int16, uint8, uint16) broadcast; + setClientTriggered() airecv clsend; + whackedMole(int8, int16) airecv clsend; + whackedBomb(int8, int16, int32) airecv clsend; + updateMole(int8, int8) broadcast; + reportToonHitByBomb(uint32, int8, int32) broadcast; + setScore(int16) required broadcast ram; + damageMe() airecv clsend; + setPityWin() broadcast; +}; + +dclass DistributedCountryClubBattle : DistributedLevelBattle { +}; + +dclass DistributedClubElevator : DistributedElevatorFSM { + setFloor(int8) broadcast ram; + setLocked(uint16) required broadcast ram; + setEntering(uint16) required broadcast ram; + kickToonsOut() broadcast; + setLatch(uint32) required broadcast ram; +}; + +dclass DistributedMaze : DistributedEntity { + setRoomDoId(uint32) required broadcast ram; + setGameStart(int16) broadcast; + setClientTriggered() airecv clsend; + setFinishedMaze() airecv clsend; + setGameOver() broadcast; + toonFinished(uint32, uint8, uint8) broadcast; + damageMe() airecv clsend; +}; + +dclass DistributedBattleWaiters : DistributedBattleFinal { +}; + +dclass DistributedFoodBelt : DistributedObject { + setBossCogId(uint32) required broadcast ram; + setIndex(uint8) required broadcast ram; + setState(char) broadcast ram; +}; + +dclass DistributedBanquetTable : DistributedObject { + setIndex(uint8) required broadcast ram; + setNumDiners(uint8) required broadcast ram; + setBossCogId(uint32) required broadcast ram; + setDinerInfo(uint8[], uint8[], uint8[]) required broadcast ram; + setState(char, uint32, int8) broadcast ram; + setDinerStatus(uint8, uint8) broadcast; + requestControl() airecv clsend; + requestFree(int8) airecv clsend; + setPitcherPos(uint8, uint16%360/100, int16) broadcast clsend; + clearSmoothing(int8) broadcast clsend; + firingWater(int32/100, int32/100, int32/100, int32/100, int32/100, int32/100) broadcast clsend; + waterHitBoss(uint8) broadcast clsend; +}; + +dclass DistributedBattleDiners : DistributedBattleFinal { +}; + +dclass DistributedGolfSpot : DistributedObject { + setIndex(uint8) required broadcast ram; + setBossCogId(uint32) required broadcast ram; + setState(char, uint32, int8) broadcast ram; + setGoingToReward() broadcast ram; + requestControl() airecv clsend; + requestFree(int8) airecv clsend; + setGolfSpotPos(uint8, uint16%360/100, int16) broadcast clsend; + clearSmoothing(int8) broadcast clsend; + setSwingInfo(uint8, int16/10, uint8) broadcast clsend; +}; + +struct TireInput { + int32/100 force; + int32/100 heading; +}; + +dclass DistributedIceGame : DistributedMinigame { + setForceArrowInfo(uint32, int32/100, int32/100) broadcast clsend; + setAvatarChoice(int32/100, int32/100) airecv clsend; + endingPositions(Coord3 []) airecv clsend; + reportScoringMovieDone() airecv clsend; + claimTreasure(uint8) airecv clsend; + claimPenalty(uint8) airecv clsend; + setTireInputs(TireInput []) broadcast; + setTimerStartTime(int16) broadcast; + setFinalPositions(Coord3 []) broadcast; + setMatchAndRound(int8, int8) broadcast; + setScores(int8, int8, int16[]) broadcast; + setNewState(string) broadcast; + setTreasureGrabbed(uint32, uint32) broadcast; + setPenaltyGrabbed(uint32, uint32) broadcast; +}; + +dclass DistributedCogThiefGame : DistributedMinigame { + throwingPie(uint32, int32, int32/100, int32/100, int32/100, int32/100) clsend broadcast; + hitBySuit(uint32, int32, int8, int32/100, int32/100, int32/100) clsend broadcast airecv; + pieHitSuit(uint32, int32, int8, int32/100, int32/100, int32/100) clsend broadcast airecv; + cogHitBarrel(int32, int8, int8, int32/100, int32/100, int32/100) clsend airecv; + cogAtReturnPos(int32, int8, int8) clsend airecv; + updateSuitGoal(int32, int32, int8, int8, int64, int32/100, int32/100, int32/100) broadcast; + makeCogCarryBarrel(int32, int32, int8, int8, int32/100, int32/100, int32/100) broadcast; + makeCogDropBarrel(int32, int32, int8, int8, int32/100, int32/100, int32/100) broadcast; + markBarrelStolen(int32, int32, int8) broadcast; +}; + +struct twoDTreasureInfo { + uint8 treasureIndex; + uint8 treasureValue; +}; + +struct twoDSectionInfo { + uint8 sectionIndex; + uint8 enemyIndicesSelected[]; + twoDTreasureInfo treasureIndicesSelected[]; + uint8 spawnPointIndicesSelected[]; + uint8 stomperIndicesSelected[]; +}; + +dclass DistributedTwoDGame : DistributedMinigame { + showShootGun(uint32, int16) clsend broadcast; + toonHitByEnemy(uint32, int16) clsend broadcast airecv; + toonFellDown(uint32, int16) clsend broadcast airecv; + toonSquished(uint32, int16) clsend broadcast airecv; + toonVictory(uint32, int16) clsend broadcast airecv; + claimTreasure(uint8, uint8) airecv clsend; + claimEnemyShot(uint8, uint8) airecv clsend; + reportDone() airecv clsend; + setSectionsSelected(twoDSectionInfo []) required broadcast ram; + setTreasureGrabbed(uint32, uint8, uint8) broadcast; + setEnemyShot(uint32, uint8, uint8, uint32) broadcast; + addVictoryScore(uint32, uint8) broadcast; + setEveryoneDone() broadcast; +}; + +dclass DistributedPicnicTable : DistributedNode { + setTableIndex(uint8) required broadcast ram; + fillSlot(uint32, uint8, int16/10, int16/10, int16/10, int16/10, int16/10, int16/10, int16, uint32) broadcast; + emptySlot(uint32, uint8, int16) broadcast; + requestTableState() airecv clsend; + setTableState(uint32 [], uint8) broadcast ram; + setGameZone(uint32, uint8) broadcast; + setIsPlaying(uint8) broadcast ram; + requestJoin(uint8, int16/10, int16/10, int16/10, int16/10, int16/10, int16/10) airecv clsend; + rejectJoin() broadcast; + requestObserve() airecv clsend; + leaveObserve() airecv clsend; + requestGameZone() airecv clsend; + requestPickedGame(uint8) clsend airecv; + requestExit() airecv clsend; + requestZone() clsend airecv; + announceWinner(string, uint32) broadcast; + allowObserve() broadcast; + allowPick() broadcast; + setZone(uint32) broadcast; +}; + +dclass DistributedChineseCheckers : DistributedNode { + requestExit() clsend airecv; + requestBegin() clsend airecv; + requestMove(uint8 []) clsend airecv; + requestTimer() clsend airecv; + requestSeatPositions() clsend airecv; + startBeginTimer(uint16, int16) broadcast ram; + gameStart(uint8) broadcast; + setTableDoId(uint32) required broadcast ram; + setGameState(uint8 [], uint8 []) required broadcast ram; + setTimer(int16) broadcast ram required; + setTurnTimer(int16) broadcast ram required; + sendTurn(uint8) broadcast ram; + requestWin() clsend airecv; + announceWin(uint32) broadcast; + announceSeatPositions(uint8 []) broadcast; +}; + +dclass DistributedCheckers : DistributedNode { + requestExit() clsend airecv; + requestBegin() clsend airecv; + requestTimer() clsend airecv; + requestMove(uint8 []) clsend airecv; + startBeginTimer(uint16, int16) broadcast ram; + gameStart(uint8) broadcast; + setTableDoId(uint32) required broadcast ram; + setGameState(uint8 [], uint8 []) required broadcast ram; + setTimer(int16) broadcast ram required; + setTurnTimer(int16) broadcast ram required; + sendTurn(uint8) broadcast ram; + requestWin() clsend airecv; + announceWin(uint32) broadcast; + illegalMove() broadcast; +}; + +dclass DistributedFindFour : DistributedNode { + requestExit() clsend airecv; + requestBegin() clsend airecv; + requestMove(uint8) clsend airecv; + requestTimer() clsend airecv; + requestWin(uint8 []) clsend airecv; + startBeginTimer(uint16, int16) broadcast ram; + setTableDoId(uint32) required broadcast ram; + setGameState(uint8 [][], uint8, uint8, uint8) required broadcast ram; + setTimer(int16) broadcast ram required; + setTurnTimer(int16) broadcast ram required; + gameStart(uint8) broadcast; + sendTurn(uint8) broadcast ram; + announceWin(uint32) broadcast; + announceWinLocation(uint8, uint8, uint8, uint8) broadcast; + announceWinnerPosition(uint8, uint8, uint8, uint8) broadcast; + illegalMove() broadcast; + tie() broadcast; +}; + +//dclass DistributedLobbyManager : DistributedObject { +// addLobby(uint32, uint32, string, string, int8, int8, activity [], decoration [], uint32[], uint16); +// addLobbyRequest(uint32) airecv clsend; +// addLobbyResponse(uint32, int8); +// addLobbyResponseUdToAi(uint64, int8, lobby) airecv; + +// getLobbyZone(uint32, uint32, uint8) clsend airecv; +// receiveLobbyZone(uint32, uint64, uint32); +// freeZoneIdFromCreatedLobby(uint32, uint32) clsend airecv; + +// sendAvToPlayground(uint32, uint8); +// exitParty(uint32) clsend airecv; + +// toonHasEnteredPartyAiToUd(uint32); +// toonHasExitedPartyAiToUd(uint32); + +// lobbyManagerAIStartingUp(uint32, uint32); +// lobbyManagerAIGoingDown(uint32, uint32); +// lobbyHasFinishedUdToAllAi(uint64 lobbyId) airecv; + +// requestShardIdZoneIdForHostId(uint32) clsend airecv; +// sendShardIdZoneIdToAvatar(uint32, uint32); + +// lobbyManagerUdStartingUp() airecv; +// lobbyManagerUdLost() airecv; + +// receiveId(uint64 ids[]) airecv; +//}; + +dclass DistributedPartyManager : DistributedObject { + addParty(uint32, uint32, string, string, int8, int8, activity [], decoration [], uint32[], uint16); + addPartyRequest(uint32, char [0-256], char [0-256], int8, int8, activity [], decoration [], uint32[]) airecv clsend; + addPartyResponse(uint32, int8); + addPartyResponseUdToAi(uint64, int8, party) airecv; + + markInviteAsReadButNotReplied(uint32, uint64); + respondToInvite(uint32, uint32, uint16, uint64, uint8); + respondToInviteResponse(uint32, uint16, uint64, int8, uint8) airecv; + + changePrivateRequest(uint64, int8) airecv clsend; + changePrivateRequestAiToUd(uint32, uint64, int8); + changePrivateResponseUdToAi(uint32, uint64, int8, int8) airecv; + changePrivateResponse(uint64, int8, int8); + + changePartyStatusRequest(uint64, int8) airecv clsend; + changePartyStatusRequestAiToUd(uint32, uint64, int8); + changePartyStatusResponseUdToAi(uint32, uint64, int8, int8) airecv; + changePartyStatusResponse(uint64, int8, int8, uint16); + + partyInfoOfHostRequestAiToUd(uint32, uint32); + partyInfoOfHostFailedResponseUdToAi(uint32) airecv; + partyInfoOfHostResponseUdToAi(party, uint32[]) airecv; + + givePartyRefundResponse(uint32, uint64, int8, uint16, uint32); + getPartyZone(uint32, uint32, uint8) clsend airecv; + receivePartyZone(uint32, uint64, uint32); + freeZoneIdFromPlannedParty(uint32, uint32) clsend airecv; + + sendAvToPlayground(uint32, uint8); + exitParty(uint32) clsend airecv; + removeGuest(uint32, uint32) airecv clsend; + partyManagerAIStartingUp(uint32, uint32); + partyManagerAIGoingDown(uint32, uint32); + partyHasStartedAiToUd(uint64 partyId, uint32 shardId, uint32 zoneId, string hostName); + toonHasEnteredPartyAiToUd(uint32); + toonHasExitedPartyAiToUd(uint32); + partyHasFinishedUdToAllAi(uint64 partyId) airecv; + updateToPublicPartyInfoUdToAllAi(uint32 shardId, uint32 zoneId, uint64 partyId, uint32 hostId, uint8 numGuests, uint8 maxGuests, string hostName, uint8 activities[], uint8 minLeft) airecv; + updateToPublicPartyCountUdToAllAi(uint32 partyCount, uint64 partyId) airecv; + requestShardIdZoneIdForHostId(uint32) clsend airecv; + sendShardIdZoneIdToAvatar(uint32, uint32); + partyManagerUdStartingUp() airecv; + partyManagerUdLost() airecv; + updateAllPartyInfoToUd(uint32, uint64, uint32, uint32, uint8, uint8, string, uint8 [], uint64); + forceCheckStart(); + requestMw(uint32, string, uint32, uint32); + mwResponseUdToAllAi(uint32, string, uint32, uint32) airecv; + receiveId(uint64 ids[]) airecv; +}; + +//dclass GlobalLobbyManager : DistributedObjectGlobal { +// lobbyManagerAIHello(uint32 channel); +// queryLobby(uint32 hostId); +// addLobby(DoId avId, uint64 lobbyId); +// toonJoinedLobby(uint64 lobbyId, uint32 avId); +// toonLeftLobby(uint64 lobbyId, uint32 avId); +// requestLobbySlot(uint64 lobbyId, uint32 avId); +// lobbyDone(uint64 lobbyId); +// allocIds(uint16 count); +//}; + +dclass GlobalPartyManager : DistributedObjectGlobal { + partyManagerAIHello(uint32 channel); + queryParty(uint32 hostId); + addParty(DoId avId, uint64 partyId, string start, string end, int8 isPrivate, + int8 inviteTheme, activity [], decoration [], DoIdList inviteeIds); + partyHasStarted(uint64 partyId, uint32 shardId, uint32 zoneId, string hostName); + toonJoinedParty(uint64 partyId, uint32 avId); + toonLeftParty(uint64 partyId, uint32 avId); + requestPartySlot(uint64 partyId, uint32 avId, uint32 gateId); + partyDone(uint64 partyId); + allocIds(uint16 count); +}; + +struct PotentialToon { + uint32 avNum; + string avName; + string avDNA; + uint8 avPosition; + uint8 aname; +}; + +dclass ClientServicesManager : DistributedObjectGlobal { + login(string cookie, char auth [0-256]) clsend; + acceptLogin(uint32 timestamp); + + requestAvatars() clsend; + setAvatars(uint8[], PotentialToon[]); + + createAvatar(blob dna, uint8 index) clsend; + createAvatarResp(uint32 avId); + + setNameTyped(uint32 avId, string name) clsend; + setNameTypedResp(uint32 avId, uint8 status); + setNamePattern(uint32 avId, int16 p1, uint8 f1, int16 p2, uint8 f2, int16 p3, uint8 f3, int16 p4, uint8 f4) clsend; + setNamePatternResp(uint32 avId, uint8 status); + + acknowledgeAvatarName(uint32 avId) clsend; + acknowledgeAvatarNameResp(); + + deleteAvatar(uint32 avId) clsend; + + chooseAvatar(uint32 avId) clsend; + + systemMessage(string message); +}; + +dclass TTCodeRedemptionMgr : DistributedObject { + redeemCode(char [0-50]) airecv clsend; + redeemCodeResult(uint8); +}; + +struct Friend +{ + uint32 doId; + string name; + blob dna; + uint16 adminAccess; + uint32 petId; +}; + +dclass TTSFriendsManager : DistributedObjectGlobal { + removeFriend(uint32) clsend; + + requestFriendsList() clsend; + + friendList(Friend []); + + friendOnline(uint32); + friendOffline(uint32); + + goingOffline(uint32 avId); + + getAvatarDetails(uint32) clsend; + friendDetails(uint32, blob, uint16[], int16, int16, uint32, uint32, blob, blob, int8[], FriendEntry[]); + + getPetDetails(uint32) clsend; + petDetails(uint32, uint32, string, uint32, uint32, uint16/1000[], PetTrait[], int8[], uint32); + + routeTeleportQuery(uint32 toId) clsend; + teleportQuery(uint32 fromId); + + teleportResponse(uint32 fromId, uint8 tpAvailable, uint32 defaultShard, uint32 hoodId, uint32 zoneId) clsend; + setTeleportResponse(uint32 toId, uint8 tpAvailable, uint32 defaultShard, uint32 hoodId, uint32 zoneId); + + whisperSCTo(uint32 toId, uint16 msgIndex) clsend; + setWhisperSCFrom(uint32 fromId, uint16 msgIndex); + + whisperSCCustomTo(uint32 toId, uint16 msgIndex) clsend; + setWhisperSCCustomFrom(uint32 fromId, uint16 msgIndex); + + whisperSCEmoteTo(uint32 toId, uint16 emoteId) clsend; + setWhisperSCEmoteFrom(uint32 fromId, uint16 emoteId); + + sendTalkWhisper(uint32 toId, string message) clsend; + receiveTalkWhisper(uint32 fromId, string message); + + battleSOS(uint32 toId) clsend; + setBattleSOS(uint32 fromId); + + teleportGiveup(uint32 toId) clsend; + setTeleportGiveup(uint32 fromId); + + whisperSCToontaskTo(uint32, uint32, uint32, uint32, uint8) clsend; + setWhisperSCToontaskFrom(uint32, uint32, uint32, uint32, uint8); + + sleepAutoReply(uint32 toId) clsend; + setSleepAutoReply(uint32 fromId); +}; + +dclass ARGManager : DistributedObjectGlobal { +}; \ No newline at end of file diff --git a/dependencies/astron/libeay32.dll b/dependencies/astron/libeay32.dll new file mode 100755 index 00000000..0c14258d Binary files /dev/null and b/dependencies/astron/libeay32.dll differ diff --git a/dependencies/astron/linux/start-ai-server.sh b/dependencies/astron/linux/start-ai-server.sh new file mode 100644 index 00000000..fb3d573f --- /dev/null +++ b/dependencies/astron/linux/start-ai-server.sh @@ -0,0 +1,32 @@ +#!/bin/sh +cd ../.. + +# Define some constants for our AI server: +MAX_CHANNELS=999999 +STATESERVER=4002 +ASTRON_IP="127.0.0.1:7100" +EVENTLOGGER_IP="127.0.0.1:7198" + +# Get the user input: +read -p "District name (DEFAULT: Nuttyboro): " DISTRICT_NAME +DISTRICT_NAME=${DISTRICT_NAME:-Nuttyboro} +read -p "Base channel (DEFAULT: 401000000): " BASE_CHANNEL +BASE_CHANNEL=${BASE_CHANNEL:-401000000} + +echo "===============================" +echo "Starting Toontown Stride AI server..." +echo "District name: $DISTRICT_NAME" +echo "Base channel: $BASE_CHANNEL" +echo "Max channels: $MAX_CHANNELS" +echo "State Server: $STATESERVER" +echo "Astron IP: $ASTRON_IP" +echo "Event Logger IP: $EVENTLOGGER_IP" +echo "===============================" + +while [ true ] +do + /usr/bin/python2 -m toontown.ai.ServiceStart --base-channel $BASE_CHANNEL \ + --max-channels $MAX_CHANNELS --stateserver $STATESERVER \ + --astron-ip $ASTRON_IP --eventlogger-ip $EVENTLOGGER_IP \ + --district-name $DISTRICT_NAME +done diff --git a/dependencies/astron/linux/start-astron-cluster.sh b/dependencies/astron/linux/start-astron-cluster.sh new file mode 100644 index 00000000..eaef6aea --- /dev/null +++ b/dependencies/astron/linux/start-astron-cluster.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd .. +./astrond --loglevel info config/cluster.yml diff --git a/dependencies/astron/linux/start-uberdog-server.sh b/dependencies/astron/linux/start-uberdog-server.sh new file mode 100644 index 00000000..8dc6114b --- /dev/null +++ b/dependencies/astron/linux/start-uberdog-server.sh @@ -0,0 +1,28 @@ +#!/bin/sh +cd ../.. + +# Define some constants for our AI server: +MAX_CHANNELS=999999 +STATESERVER=4002 +ASTRON_IP="127.0.0.1:7100" +EVENTLOGGER_IP="127.0.0.1:7198" + +# Get the user input: +read -p "Base channel (DEFAULT: 1000000): " BASE_CHANNEL +BASE_CHANNEL=${BASE_CHANNEL:-1000000} + +echo "===============================" +echo "Starting Toontown Stride UberDOG server..." +echo "Base channel: $BASE_CHANNEL" +echo "Max channels: $MAX_CHANNELS" +echo "State Server: $STATESERVER" +echo "Astron IP: $ASTRON_IP" +echo "Event Logger IP: $EVENTLOGGER_IP" +echo "===============================" + +while [ true ] +do +/usr/bin/python2 -m toontown.uberdog.ServiceStart --base-channel $BASE_CHANNEL \ + --max-channels $MAX_CHANNELS --stateserver $STATESERVER \ + --astron-ip $ASTRON_IP --eventlogger-ip $EVENTLOGGER_IP +done diff --git a/dependencies/astron/logs/.gitignore b/dependencies/astron/logs/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/dependencies/astron/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/dependencies/astron/msvcp120.dll b/dependencies/astron/msvcp120.dll new file mode 100755 index 00000000..a237d2d7 Binary files /dev/null and b/dependencies/astron/msvcp120.dll differ diff --git a/dependencies/astron/msvcr120.dll b/dependencies/astron/msvcr120.dll new file mode 100755 index 00000000..8c36149a Binary files /dev/null and b/dependencies/astron/msvcr120.dll differ diff --git a/dependencies/astron/prod/start-ai-server.sh b/dependencies/astron/prod/start-ai-server.sh new file mode 100755 index 00000000..18b3b021 --- /dev/null +++ b/dependencies/astron/prod/start-ai-server.sh @@ -0,0 +1,32 @@ +#!/bin/sh +cd ../../.. + +# Define some constants for our AI server: +MAX_CHANNELS=999999 +STATESERVER=4002 +ASTRON_IP="158.69.28.83:7100" +EVENTLOGGER_IP="158.69.28.83:7198" + +# Get the user input: +read -p "District name (DEFAULT: Nuttyboro): " DISTRICT_NAME +DISTRICT_NAME=${DISTRICT_NAME:-Nuttyboro} +read -p "Base channel (DEFAULT: 401000000): " BASE_CHANNEL +BASE_CHANNEL=${BASE_CHANNEL:-401000000} + +echo "===============================" +echo "Starting Toontown Stride AI server..." +echo "District name: $DISTRICT_NAME" +echo "Base channel: $BASE_CHANNEL" +echo "Max channels: $MAX_CHANNELS" +echo "State Server: $STATESERVER" +echo "Astron IP: $ASTRON_IP" +echo "Event Logger IP: $EVENTLOGGER_IP" +echo "===============================" + +while [ true ] +do + python -m toontown.ai.ServiceStart --base-channel $BASE_CHANNEL \ + --max-channels $MAX_CHANNELS --stateserver $STATESERVER \ + --astron-ip $ASTRON_IP --eventlogger-ip $EVENTLOGGER_IP \ + --district-name $DISTRICT_NAME +done diff --git a/dependencies/astron/prod/start-astron-cluster.sh b/dependencies/astron/prod/start-astron-cluster.sh new file mode 100755 index 00000000..cfa4bdf9 --- /dev/null +++ b/dependencies/astron/prod/start-astron-cluster.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd .. +./astrond --loglevel info config/prod-test.yml diff --git a/dependencies/astron/prod/start-uberdog-server.sh b/dependencies/astron/prod/start-uberdog-server.sh new file mode 100755 index 00000000..4fa316ad --- /dev/null +++ b/dependencies/astron/prod/start-uberdog-server.sh @@ -0,0 +1,28 @@ +#!/bin/sh +cd ../../.. + +# Define some constants for our AI server: +MAX_CHANNELS=999999 +STATESERVER=4002 +ASTRON_IP="158.69.28.83:7100" +EVENTLOGGER_IP="158.69.28.83:7198" + +# Get the user input: +read -p "Base channel (DEFAULT: 1000000): " BASE_CHANNEL +BASE_CHANNEL=${BASE_CHANNEL:-1000000} + +echo "===============================" +echo "Starting Toontown Stride UberDOG server..." +echo "Base channel: $BASE_CHANNEL" +echo "Max channels: $MAX_CHANNELS" +echo "State Server: $STATESERVER" +echo "Astron IP: $ASTRON_IP" +echo "Event Logger IP: $EVENTLOGGER_IP" +echo "===============================" + +while [ true ] +do +python -m toontown.uberdog.ServiceStart --base-channel $BASE_CHANNEL \ + --max-channels $MAX_CHANNELS --stateserver $STATESERVER \ + --astron-ip $ASTRON_IP --eventlogger-ip $EVENTLOGGER_IP +done diff --git a/dependencies/astron/ssleay32.dll b/dependencies/astron/ssleay32.dll new file mode 100755 index 00000000..af071f2e Binary files /dev/null and b/dependencies/astron/ssleay32.dll differ diff --git a/dependencies/config/general.prc b/dependencies/config/general.prc new file mode 100644 index 00000000..493aacbe --- /dev/null +++ b/dependencies/config/general.prc @@ -0,0 +1,112 @@ +# Window settings: +window-title Toontown Stride +win-origin -1 -1 +icon-filename phase_3/etc/icon.ico +cursor-filename phase_3/etc/toonmono.cur + +# Audio: +audio-library-name p3openal_audio +video-library-name p3ffmpeg + +# Graphics: +aux-display pandagl +aux-display pandadx9 +aux-display p3tinydisplay + +# Models: +model-cache-models #f +model-cache-textures #f +default-model-extension .bam + +# Textures: +texture-anisotropic-degree 16 + +# Preferences: +preferences-filename user/preferences.json + +# Backups: +backups-filepath dependencies/backups/ +backups-extension .json + +# Server: +server-timezone EST/EDT/-5 +server-port 7199 +account-bridge-filename astron/databases/account-bridge.db + +# Performance: +texture-power-2 none +gl-check-errors #f +garbage-collect-states #t + +# Egg object types: +egg-object-type-barrier collide-mask { 0x01 } { Polyset descend } +egg-object-type-trigger collide-mask { 0x01 } { Polyset descend intangible } +egg-object-type-sphere collide-mask { 0x01 } { Sphere descend } +egg-object-type-trigger-sphere collide-mask { 0x01 } { Sphere descend intangible } +egg-object-type-floor collide-mask { 0x02 } { Polyset descend } +egg-object-type-dupefloor collide-mask { 0x02 } { Polyset keep descend } +egg-object-type-camera-collide collide-mask { 0x04 } { Polyset descend } +egg-object-type-camera-collide-sphere collide-mask { 0x04 } { Sphere descend } +egg-object-type-camera-barrier collide-mask { 0x05 } { Polyset descend } +egg-object-type-camera-barrier-sphere collide-mask { 0x05 } { Sphere descend } +egg-object-type-model { 1 } +egg-object-type-dcs { 1 } + +# Safe zones: +want-safe-zones #t +want-toontown-central #t +want-donalds-dock #t +want-daisys-garden #t +want-minnies-melodyland #t +want-the-brrrgh #t +want-donalds-dreamland #t +want-goofy-speedway #t +want-outdoor-zone #t +want-golf-zone #t + +# Safe zone settings: +want-treasure-planners #t +want-suit-planners #t +want-butterflies #t + +# Trolley minigames: +want-minigames #t + +# Picnic table board games: +want-game-tables #t + +# Cog headquarters: +want-cog-headquarters #t +want-sellbot-headquarters #t +want-cashbot-headquarters #t +want-lawbot-headquarters #t +want-bossbot-headquarters #t + +# Cog battles: +base-xp-multiplier 1.0 + +# SOS toons: +sos-card-reward 2 + +# CogDominiums (Field Offices): +want-emblems #t +cogdo-want-barrel-room #t +want-lawbot-cogdo #t + +# Cog buildings: +want-cogbuildings #t + +# Optional: +show-total-population #t +want-mat-all-tailors #t +estate-day-night #t +want-garden-game #f +want-language-selection #t + +# Developer options: +want-dev #f +want-pstats 0 + +# Temporary: +smooth-lag 0.4 +want-old-fireworks #t diff --git a/dependencies/config/guieditor.prc b/dependencies/config/guieditor.prc new file mode 100644 index 00000000..860fd6fe --- /dev/null +++ b/dependencies/config/guieditor.prc @@ -0,0 +1 @@ +model-path ../resources/ \ No newline at end of file diff --git a/dependencies/config/release/dev.prc b/dependencies/config/release/dev.prc new file mode 100644 index 00000000..b1db0ec3 --- /dev/null +++ b/dependencies/config/release/dev.prc @@ -0,0 +1,64 @@ +# Distribution: +distribution dev + +# Art assets: +vfs-mount resources/phase_3 /phase_3 +vfs-mount resources/phase_3.5 /phase_3.5 +vfs-mount resources/phase_4 /phase_4 +vfs-mount resources/phase_5 /phase_5 +vfs-mount resources/phase_5.5 /phase_5.5 +vfs-mount resources/phase_6 /phase_6 +vfs-mount resources/phase_7 /phase_7 +vfs-mount resources/phase_8 /phase_8 +vfs-mount resources/phase_9 /phase_9 +vfs-mount resources/phase_10 /phase_10 +vfs-mount resources/phase_11 /phase_11 +vfs-mount resources/phase_12 /phase_12 +vfs-mount resources/phase_13 /phase_13 +vfs-mount resources/server /server +model-path / + +# Server: +server-version tts-dev +min-access-level 700 +accountdb-type developer +shard-low-pop 50 +shard-mid-pop 100 + +# RPC: +want-rpc-server #f +rpc-server-endpoint http://localhost:8080/ + +# DClass file: +dc-file dependencies/astron/dclass/stride.dc + +# Core features: +want-pets #t +want-parties #f +want-cogdominiums #t +want-lawbot-cogdo #t +want-anim-props #t +want-game-tables #t +want-find-four #t +want-chinese-checkers #t +want-checkers #t +want-house-types #t +want-gifting #t +want-top-toons #f + +# Chat: +want-whitelist #f +want-sequence-list #f + +# Developer options: +show-population #t +want-instant-parties #t +want-instant-delivery #t +cogdo-pop-factor 1.5 +cogdo-ratio 0.5 +default-directnotify-level info + +# Crates: +dont-destroy-crate #t +get-key-reward-always #t +get-crate-reward-always #t \ No newline at end of file diff --git a/dependencies/config/release/qa.prc b/dependencies/config/release/qa.prc new file mode 100644 index 00000000..92eeb708 --- /dev/null +++ b/dependencies/config/release/qa.prc @@ -0,0 +1,50 @@ +# Distribution: +distribution qa + +# Art assets: +model-path / + +# Server: +server-version SERVER-VERSION-HERE +min-access-level 100 +accountdb-type remote +shard-low-pop 50 +shard-mid-pop 100 + +# RPC: +want-rpc-server #t +rpc-server-endpoint http://localhost:8080/ + +# DClass file is automatically wrapped into the niraidata. + +# Core features: +want-pets #t +want-parties #f +want-cogdominiums #t +want-lawbot-cogdo #f +want-anim-props #t +want-game-tables #t +want-find-four #t +want-chinese-checkers #t +want-checkers #t +want-house-types #f +want-gifting #t +want-top-toons #f +want-emblems #f + +# Chat: +want-whitelist #t +want-sequence-list #t + +# Developer options: +show-population #t +want-instant-parties #f +want-instant-delivery #f +cogdo-pop-factor 1.5 +cogdo-ratio 0.5 +default-directnotify-level info + +# Crates: +dont-destroy-crate #f +get-key-reward-always #f +get-crate-reward-always #f \ No newline at end of file diff --git a/dependencies/libpandadna.pyd b/dependencies/libpandadna.pyd new file mode 100755 index 00000000..9be66630 Binary files /dev/null and b/dependencies/libpandadna.pyd differ diff --git a/dependencies/libpandadna.so b/dependencies/libpandadna.so new file mode 100755 index 00000000..a955327f Binary files /dev/null and b/dependencies/libpandadna.so differ diff --git a/dev/darwin/start-game-localhost.sh b/dev/darwin/start-game-localhost.sh new file mode 100644 index 00000000..01c87d85 --- /dev/null +++ b/dev/darwin/start-game-localhost.sh @@ -0,0 +1,22 @@ +#!/bin/sh +cd .. + +export DYLD_LIBRARY_PATH=`pwd`/Libraries.bundle +export DYLD_FRAMEWORK_PATH="Frameworks" + +# Get the user input: +read -p "Username: " ttsUsername + +# Export the environment variables: +export ttsUsername=$ttsUsername +export ttsPassword="password" +export TTS_PLAYCOOKIE=$ttsUsername +export TTS_GAMESERVER="127.0.0.1" + +echo "===============================" +echo "Starting Toontown Stride..." +echo "Username: $ttsUsername" +echo "Gameserver: $TTS_GAMESERVER" +echo "===============================" + +ppython -m toontown.toonbase.ToontownStart diff --git a/dev/darwin/start-game-remotedb.sh b/dev/darwin/start-game-remotedb.sh new file mode 100644 index 00000000..7ff6dfab --- /dev/null +++ b/dev/darwin/start-game-remotedb.sh @@ -0,0 +1,26 @@ +#!/bin/sh +cd .. + +export DYLD_LIBRARY_PATH=`pwd`/Libraries.bundle +export DYLD_FRAMEWORK_PATH="Frameworks" + +# Get the user input: +read -p "Username: " ttsUsername +read -s -p "Password: " ttsPassword +echo +read -p "Gameserver (DEFAULT: 167.114.28.238): " TTS_GAMESERVER +TTS_GAMESERVER=${TTS_GAMESERVER:-"167.114.28.238"} + +# Export the environment variables: +export ttsUsername=$ttsUsername +export ttsPassword=$ttsPassword +export TTS_PLAYCOOKIE=$ttsUsername +export TTS_GAMESERVER=$TTS_GAMESERVER + +echo "===============================" +echo "Starting Toontown Stride..." +echo "Username: $ttsUsername" +echo "Gameserver: $TTS_GAMESERVER" +echo "===============================" + +ppython -m toontown.toonbase.ToontownStartRemoteDB diff --git a/dev/darwin/start-game.sh b/dev/darwin/start-game.sh new file mode 100644 index 00000000..aeb9c33f --- /dev/null +++ b/dev/darwin/start-game.sh @@ -0,0 +1,24 @@ +#!/bin/sh +cd .. + +export DYLD_LIBRARY_PATH=`pwd`/Libraries.bundle +export DYLD_FRAMEWORK_PATH="Frameworks" + +# Get the user input: +read -p "Username: " ttsUsername +read -p "Gameserver (DEFAULT: 167.114.28.238): " TTS_GAMESERVER +TTS_GAMESERVER=${TTS_GAMESERVER:-"167.114.28.238"} + +# Export the environment variables: +export ttsUsername=$ttsUsername +export ttsPassword="password" +export TTS_PLAYCOOKIE=$ttsUsername +export TTS_GAMESERVER=$TTS_GAMESERVER + +echo "===============================" +echo "Starting Toontown Stride..." +echo "Username: $ttsUsername" +echo "Gameserver: $TTS_GAMESERVER" +echo "===============================" + +ppython -m toontown.toonbase.ToontownStart diff --git a/dev/tools/cleaner/cleanse.py b/dev/tools/cleaner/cleanse.py new file mode 100644 index 00000000..8722849a --- /dev/null +++ b/dev/tools/cleaner/cleanse.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python2 +# Cleanse the "../" directory of the following: +# - all files with a .pyc extension. +# - all "trash files" listed below: +# . parsetab.py - generated by the PLY module. + +import os + + +extensions = ('.pyc',) +trashFiles = ('parsetab.py',) + +print 'Changing to root directory...' +os.chdir('../../../') + +print 'Scanning for garbage files...' + + +def delete(filepath): + print "Removing '%s'..." % filepath + os.unlink(filepath) + + +for root, folders, files in os.walk('.'): + for filename in files: + filepath = os.path.join(root, filename) + if os.path.splitext(filename)[1] in extensions: + delete(filepath) + elif filename in trashFiles: + delete(filepath) + +print 'Done.' diff --git a/dev/tools/doc/building/README.md b/dev/tools/doc/building/README.md new file mode 100644 index 00000000..65f51536 --- /dev/null +++ b/dev/tools/doc/building/README.md @@ -0,0 +1,9 @@ +Building +======== +These documents outline everything you need to know for building a Toontown Stride client. + +- - - + +## Steps ## + +TODO diff --git a/dev/tools/doc/style-guide/README.md b/dev/tools/doc/style-guide/README.md new file mode 100644 index 00000000..b23d5978 --- /dev/null +++ b/dev/tools/doc/style-guide/README.md @@ -0,0 +1,9 @@ +Toontown Stride Style Guidelines +================================== +Code and documentation in the master and release branches of the Toontown Stride repositories must conform to these guidelines. Any code submitted that is not properly formated will be rejected, as it is best to keep a readable, and consistent style for future contributors to read, and understand the code. Don't, however, blindly follow these guidelines into writing unreadable code. Sometimes it is best to use your own judgement. + +- - - + +* [Git style guidelines](git-style.md) +* [Python style guidelines](python-style.md) +* [C++ style guidelines](cxx-style.md) diff --git a/dev/tools/doc/style-guide/cxx-style.md b/dev/tools/doc/style-guide/cxx-style.md new file mode 100644 index 00000000..f216506c --- /dev/null +++ b/dev/tools/doc/style-guide/cxx-style.md @@ -0,0 +1,145 @@ +C++ Style Guidelines +==================== +For C++ programming, we use the [Oct 8, 2013 - Astron C++ Style Guide](https://github.com/Astron/Astron/blob/6a974ce247a364fdcd11d440db1cad0f1c2f6ba2/doc/style-guide/cxx-style.md "Oct 8, 2013 - Astron C++ Style Guide"). +- - - +## Whitespace ## +Tabs shall be used to indent, spaces shall be used to align. +Source files should end with a newline. + +## Variable Names ## +Variables shall have a descriptive lowercase name, where words are seperated by underscores. +Global variables shall start with g_ +Member variables shall start with m_ + +Example: `field_count` or + + class Foo + { + private: + int m_my_number; + }; + +## Function Names ## +Functions shall be named the same way as variables + +## Class Names ## +Class names shall be in CamelCase + +Example: `DistributedObject` + +## Braces ## +Each brace shall be on it's own line, even if it's for an empty member: + +Example: + + void foo() + { + } + +## Header Files ## +A class shall not have a header file if nothing else interacts with it or if nothing else will ever inherit from it. + +## Typedefs ## +Typedefs shall have a descriptive name and end with _t + +Example: `typedef unsigned int uint32_t` + +## Usage of std::string (and when possible for stl types) ## +Whenever possible, have function parameters be of type `const std::string&` and not `std::string` + +Example: + + void foo(const std::string &name) + { + std::cout << "Hello " << name << "!" << std::endl; + } + +## Preprocessor Macros ## +Preproc macros shall be UPPER CASE and words shall be seperated by underlines. + +Example: `#define CLIENT_HELLO 1` + +## auto Keyword ## +The `auto` keyword shall be used to avoid typing out long iterator types, and only for that. + +Example: + + std::list my_objects; + auto it = my_objects.begin(); + +## Template specifiers ## +There shall be no space between the identifier and the <> + +Good example: `std::list my_channels;` + +Bad example: `std::list my_channels;` + +## Access specifiers ## +Class/struct access specifiers shall be indented. + +Good example: + + class Foo + { + public: + Foo(); + }; + +Bad examples: + + class Foo + { + public: + Foo(); + }; + + class Foo + { + public: + Foo(); + }; + +## Switches ## +Case statements inside switches shall be indented. If the case does not fall through, it shall have it's own scope. +The braces for the scope shall be on the same indentation level as the case statement itself. +At the end of the scope, a `break;` shall be placed on the same indentation level as the case statement. + +Example: + + int temp = some_int; + switch(temp) + { + case 0: + { + //code + } + break; + default: + //code + } + +## Character Limit ## +Each line shall be no longer than 100 characters, each tab counting as 4 characters. +This is not a hard-limit, exceptions may be made. + +## Initializer lists ## +The first variable inside of the initializer list shall be on the same line as the function header +if the character limit allows. +The following variables will be on the next line, with another level of indentation. +That line will continue until it hits the character limit, once that occurs a new line will be created, +with the same level of indenation. + +Example: + + class Foo + { + private: + int m_number; + int m_number2; + + public: + Foo() : m_number(0), + m_number2(0) + { + } + }; diff --git a/dev/tools/doc/style-guide/git-style.md b/dev/tools/doc/style-guide/git-style.md new file mode 100644 index 00000000..2a013f1c --- /dev/null +++ b/dev/tools/doc/style-guide/git-style.md @@ -0,0 +1,30 @@ +Git Style Guidelines +==================== +For Git, we try to follow a general pattern for commit messages and branch naming to make things organized and neat. +- - - +## Commit Messages ## +All commit messages should: +* Start with a capital letter. +* Never end in puncuation. +* Be in the present tense. +* Have a title less than 100 characters. +* End in a new line. + +If a description is provided in the commit message, it should be separated from the title by a blank line. If the commit addresses an issue, its issue number should be referenced at the end of the commit message's description. + +Whenever possible, commit messages should be prefixed with the directory name of which the commit modified the most, followed by a colon and a space. + +For example: ```toon: ``` or ```tools: ``` or ```ai: ``` + +## Branch Naming ## +All branch names should: +* Be entirely lower case. +* Use **-** as a separator. +* Be categorized into one of the following groups: + * wip + * bugfix + * test + * enhancement + * feature + +For example: ```feature/parties``` or ```bugfix/toontorial``` or ```enhancement/fix-memory-leak``` diff --git a/dev/tools/doc/style-guide/python-style.md b/dev/tools/doc/style-guide/python-style.md new file mode 100644 index 00000000..03262164 --- /dev/null +++ b/dev/tools/doc/style-guide/python-style.md @@ -0,0 +1,37 @@ +Python Style Guidelines +======================= +For Python programming, we use a slightly modified version of the standard [PEP-8 Style Guide for Python Code](http://legacy.python.org/dev/peps/pep-0008 "PEP-8 Style Guide for Python Code"). Read below for our modifications. +- - - +## Code lay-out ## +### Indentation ### +The closing brace/bracket/parenthesis on multi-line constructs may either be directly at the end, as in: + + my_list = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20] + result = some_function_that_takes_arguments( + 'a', 'b', 'c', 'd', 'e', 'f') + +or it may be by itself on the next line: + + my_list = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20 + ] + result = some_function_that_takes_arguments( + 'a', 'b', 'c', 'd', 'e', 'f' + ) + +### Tabs or Spaces? ### +**_Always_** use spaces. + +### Maximum Line Length ### +**Docstrings and comments** should be restricted to _80 characters_. Anything else should be limited to _100 characters_. + +## Naming Conventions ## +### Variables ### +Intentionally **unused** variables should be named "**_**". This will make common IDEs and editors ignore it. + +## Strings ## +### Quotations ### +Use single quotations _(')_ unless there is one inside the string, in which case use double quotations _(")_. diff --git a/dev/tools/findterm/findterm.py b/dev/tools/findterm/findterm.py new file mode 100644 index 00000000..5957f503 --- /dev/null +++ b/dev/tools/findterm/findterm.py @@ -0,0 +1,19 @@ +import glob + +def processFile(f,t): + data = open(f,'rb').read() + lines = data.replace('\r\n','\n').split('\n') + lines_found = [] + for i,x in enumerate(lines): + if t in x: + lines_found.append(i+1) + + return lines_found + +term = raw_input('> ') +for x in glob.glob('../../../toontown/*/*.py'): + r = processFile(x,term) + if r: + print x,r + +raw_input('*****') diff --git a/dev/tools/findterm/findtermAI.py b/dev/tools/findterm/findtermAI.py new file mode 100644 index 00000000..4354e705 --- /dev/null +++ b/dev/tools/findterm/findtermAI.py @@ -0,0 +1,19 @@ +import glob + +def processFile(f,t): + data = open(f,'rb').read() + lines = data.replace('\r\n','\n').split('\n') + lines_found = [] + for i,x in enumerate(lines): + if t in x: + lines_found.append(i+1) + + return lines_found + +term = raw_input('> ') +for x in glob.glob('../../../toontown/*/*AI.py'): + r = processFile(x,term) + if r: + print x,r + +raw_input('*****') diff --git a/dev/tools/findterm/findterm_otp.py b/dev/tools/findterm/findterm_otp.py new file mode 100644 index 00000000..a44b2178 --- /dev/null +++ b/dev/tools/findterm/findterm_otp.py @@ -0,0 +1,19 @@ +import glob + +def processFile(f,t): + data = open(f,'rb').read() + lines = data.replace('\r\n','\n').split('\n') + lines_found = [] + for i,x in enumerate(lines): + if t in x: + lines_found.append(i+1) + + return lines_found + +term = raw_input('> ') +for x in glob.glob('../../../otp/*/*.py'): + r = processFile(x,term) + if r: + print x,r + +raw_input('*****') diff --git a/dev/tools/gui/EditorStart.py b/dev/tools/gui/EditorStart.py new file mode 100644 index 00000000..33ec32a1 --- /dev/null +++ b/dev/tools/gui/EditorStart.py @@ -0,0 +1,32 @@ +from direct.stdpy import threading +from direct.showbase.ShowBase import ShowBase +from panda3d.core import VirtualFileSystem +import __builtin__, wx, os + +__builtin__.__dict__.update(__import__('panda3d.core', fromlist=['*']).__dict__) + +loadPrcFile('dependencies/config/guieditor.prc') +loadPrcFile('dependencies/config/general.prc') + +defaultText = """from direct.gui.DirectGui import * + +""" + +def inject(_): + code = textbox.GetValue() + exec(code, globals()) + +app = wx.App(redirect=False) +frame = wx.Frame(None, title="Injector", size=(640, 400), style=wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX) +panel = wx.Panel(frame) +button = wx.Button(parent=panel, id=-1, label="Inject", size=(50, 20), pos=(295, 0)) +textbox = wx.TextCtrl(parent=panel, id=-1, pos=(20, 22), size=(600, 340), style=wx.TE_MULTILINE) + +frame.Bind(wx.EVT_BUTTON, inject, button) +frame.Show() +app.SetTopWindow(frame) +textbox.AppendText(defaultText) +threading.Thread(target=app.MainLoop).start() + +__builtin__.base = ShowBase() +base.run() \ No newline at end of file diff --git a/dev/tools/gui/start-gui-editor.bat b/dev/tools/gui/start-gui-editor.bat new file mode 100644 index 00000000..f5c7d055 --- /dev/null +++ b/dev/tools/gui/start-gui-editor.bat @@ -0,0 +1,15 @@ +@echo off + +cd ../../../ + +title GUI Editor +set GUI= + +:main + +"C:\Panda3D-1.10.0\python\ppython.exe" "dev/tools/gui/EditorStart.py" + +echo. +echo. + +goto main \ No newline at end of file diff --git a/dev/tools/rpc/generate_key.py b/dev/tools/rpc/generate_key.py new file mode 100644 index 00000000..de1c01f7 --- /dev/null +++ b/dev/tools/rpc/generate_key.py @@ -0,0 +1,34 @@ +from Crypto.Cipher import AES +from Crypto import Random +import base64 +import os + +# the block size for the cipher object; must be 16, 24, or 32 for AES +BLOCK_SIZE = 16 + +# the character used for padding--with a block cipher such as AES, the value +# you encrypt must be a multiple of BLOCK_SIZE in length. This character is +# used to ensure that your value is always a multiple of BLOCK_SIZE +PADDING = '{' + +# one-liner to sufficiently pad the text to be encrypted +pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING + +# one-liners to encrypt/encode and decrypt/decode a string +# encrypt with AES, encode with base64 +EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s))) +DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING) + +# generate a random secret key +secret = os.urandom(BLOCK_SIZE) + +# create a cipher object using the random secret +cipher = AES.new( secret, AES.MODE_CBC, secret ) + +# encode a string +encoded = EncodeAES(cipher, 'ttsrpclib691256') +print 'Encrypted string:', encoded + +# decode the encoded string +decoded = DecodeAES(cipher, encoded) +print 'Decrypted string:', decoded diff --git a/dev/tools/rpc/rpc-invasions.bat b/dev/tools/rpc/rpc-invasions.bat new file mode 100644 index 00000000..e3b3ce40 --- /dev/null +++ b/dev/tools/rpc/rpc-invasions.bat @@ -0,0 +1,5 @@ +@echo off +cd ../../../ +set /P serv=RPC Server: +"C:\Panda3D-1.10.0\python\ppython.exe" "dev/tools/rpc/rpc-invasions.py" 6163636f756e7473 %serv% +pause \ No newline at end of file diff --git a/dev/tools/rpc/rpc-invasions.py b/dev/tools/rpc/rpc-invasions.py new file mode 100644 index 00000000..bb8ac209 --- /dev/null +++ b/dev/tools/rpc/rpc-invasions.py @@ -0,0 +1,78 @@ +#!/usr/bin/python + +from jsonrpclib import Server +import time +import random +import json +import math +import os +from Crypto.Cipher import AES +import base64 +import sys + + +RPC_SERVER_SECRET = sys.argv[1] + +client = Server(sys.argv[2]) + +def generate_token(accessLevel): + """ + Generate an RPC server token with the given access level. + """ + token = {'timestamp': int(time.mktime(time.gmtime())), 'accesslevel': accessLevel} + data = json.dumps(token) + iv = os.urandom(AES.block_size) + cipher = AES.new(RPC_SERVER_SECRET, mode=AES.MODE_CBC, IV=iv) + data += '\x00' * (16 - (len(data)%AES.block_size)) + token = cipher.encrypt(data) + return base64.b64encode(iv + token) + +random.seed() +while True: + try: + res = client.ping(generate_token(700), 12345) + if res != 12345: + print "Is the server accessable?\n" + exit + + # How many times a day is this script going to be called? + ChecksPerDay = 60.0*24.0 # Once a minute + InvasionsPerDay = 72.0 # How many invasions a day per district + BaseInvasionChance = InvasionsPerDay/ChecksPerDay + + safeHarbor = {'Wacky Falls'} + superDistricts = {'Nuttyboro'} + + + while True: + shards = client.listShards(generate_token(700)) + print shards + count = 0 + for skey in shards: + shard = shards[skey] + if shard['invasion'] != None: + count = count + 1 + for skey in shards: + shard = shards[skey] + if shard['invasion'] == None: + if shard['name'] in superDistricts: + typ = int(float(random.random()) * 4.0) + suit = int(float(random.random()) * 4.0) + 4 # Bias the cogs to be big + client.startInvasion(generate_token(700), int(skey), typ, suit, 0, 0) + count = count + 1 + print 'Calling invasion for %s with %d,%d'%(shard['name'],typ,suit) + if count < 3: + for skey in shards: + shard = shards[skey] + if shard['invasion'] == None and not shard['name'] in safeHarbor: + r = random.random() + if r < BaseInvasionChance and not shard['name'] in superDistricts: + typ = int(float(random.random()) * 4.0) + suit = int(float(random.random()) * 8.0) + client.startInvasion(generate_token(700), int(skey), typ, suit, 0, 0) + print 'Calling invasion for %s with %d,%d'%(shard['name'],typ,suit) + print "tick..(was %d)\n"%(count) + time.sleep(60) + except Exception, e: + print e + time.sleep(300) diff --git a/dev/tools/rpc/rpctest.py b/dev/tools/rpc/rpctest.py new file mode 100644 index 00000000..ed148a01 --- /dev/null +++ b/dev/tools/rpc/rpctest.py @@ -0,0 +1,5 @@ +import pyjsonrpc + +http_client = pyjsonrpc.HttpClient(url = 'http://localhost:8080') +print 'Connected' +print http_client.ping('test') \ No newline at end of file diff --git a/dev/tools/rpc/write_rpc_doc.py b/dev/tools/rpc/write_rpc_doc.py new file mode 100644 index 00000000..24e2dd1f --- /dev/null +++ b/dev/tools/rpc/write_rpc_doc.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python2 +import collections +import compiler +import re + + +class CategoryParser: + def __init__(self, filepath): + self.filepath = filepath + + self.categories = {} + + def parse(self): + with open(self.filepath, 'r') as f: + for i, line in enumerate(f.readlines()): + line = line.strip() + if (not line.startswith('# --- ')) or (not line.endswith(' ---')): + continue + self.addCategory(i + 1, line[6:-4].title()) + + def addCategory(self, lineno, category): + self.categories[lineno] = category + + def getCategory(self, lineno): + category = 'Unknown' + for k in sorted(self.categories.keys()): + if k > lineno: + break + category = self.categories[k] + return category + + +class MethodParser(CategoryParser): + def __init__(self, filepath): + CategoryParser.__init__(self, filepath) + + # Order matters, so store the method information in an OrderedDict: + self.methods = collections.OrderedDict() + + def parse(self): + CategoryParser.parse(self) + + # Get the root node: + node = compiler.parseFile(self.filepath).node + + # Parse any class objects: + for child in node.getChildren(): + if isinstance(child, compiler.ast.Class): + self.parseClass(child) + + def parseClass(self, node): + # Skip past the class definition, and go into the body: + stmt = node.getChildNodes()[-1] + + # Parse any function objects: + for child in stmt.getChildren(): + if isinstance(child, compiler.ast.Function): + self.parseFunction(child) + + def parseFunction(self, node): + # First, verify that this is an RPC method: + if not node.name.startswith('rpc_'): + # RPC methods are required to have their name begin with 'rpc_'. + return + name = node.name[4:] + if node.decorators is None: + # RPC methods are also required to utilize the @rpcmethod + # decorator. + return + for decorator in node.decorators: + if decorator.node.name != 'rpcmethod': + continue + accessLevel = 'Unknown' + for arg in decorator.args: + if arg.name != 'accessLevel': + continue + + # Format the access level string: + accessLevel = ' '.join(arg.expr.name.split('_')).title() + break + else: + return + + # A docstring is also required: + if node.doc is None: + return + + # Get rid of the indentation in our docstring so that we can have an + # easier time parsing it: + lines = node.doc.split('\n') + for i, line in enumerate(lines): + lines[i] = line.lstrip() + doc = '\n'.join(lines) + + # Get the category in which this method is underneath: + category = self.getCategory(node.lineno) + + # Store this method's information: + self.methods.setdefault(category, []).append((name, accessLevel, doc)) + + def getMethods(self): + return self.methods + + +class MediaWikiGenerator: + def __init__(self, methods): + self.methods = methods + + self.content = '' + + def generate(self): + # Start on a clean slate: + self.content = '' + + # Write the documentation header: + self.writeHeader() + + # Write the categories and methods: + for category, methods in self.methods.items(): + self.writeCategory(category) + for name, accessLevel, doc in methods: + self.writeMethod(name, accessLevel, doc) + + # Write the documentation footer: + self.writeFooter() + + return self.content + + def writeHeader(self): + # Force a table of contents: + self.content += '__TOC__\n' + + def writeCategory(self, category): + self.content += '= %s =\n' % category + + def writeMethod(self, name, accessLevel, doc): + # First, add the method name and access level: + self.writeHeading(3, name + ' - %s' % accessLevel) + + # Split the docstring by the '\n\n' terminator: + doc = doc.split('\n\n') + + # A summary is required, so let's assume it's first: + summary = doc[0][9:].strip() + self.writeBlockQuote(' '.join(summary.split('\n'))) + + # Let's do parameters next if we have them: + for entry in doc: + if entry.startswith('Parameters:'): + entry = entry[13:].strip() + break + else: + entry = None + if entry is not None: + parameters = [] + for parameter in re.split('\\n\\[|\\n<', entry): + name, description = parameter.split(' = ', 1) + type, name = name.strip()[:-1].split(' ', 1) + description = ' '.join(description.split('\n')) + parameters.append((name, type, description)) + self.writeParameters(parameters) + + # Finally, we have an optional example response: + for entry in doc: + if entry.startswith('Example response:'): + entry = entry[18:].strip() + break + else: + entry = None + if entry is not None: + self.content += '{|\n' + self.content += '|-\n' + if (not entry.startswith('On success:')) or ( + 'On failure:' not in entry): + # Generate a single-row table: + self.content += '! Example Response\n' + self.content += '| %s\n' % entry + else: + # Generate a double-row table: + success, failure = entry[12:].split('On failure:', 1) + self.content += '! rowspan="2"|Example Response\n' + self.content += '| Success\n' + self.content += '| %s\n' % success.strip() + self.content += '|-\n' + self.content += '| Failure\n' + self.content += '| %s\n' % failure.strip() + self.content += '|}\n' + + return self.content + + def writeHeading(self, size, text): + self.content += '%s\n' % (size, text, size) + + def writeBlockQuote(self, text): + self.content += '
%s
\n' % text + + def writeParameters(self, parameters): + self.content += '{|\n' + self.content += '|-\n' + self.content += '! rowspan="%d"|Parameters\n' % (len(parameters) + 1) + self.content += '! Name\n' + self.content += '! Type\n' + self.content += '! Description\n' + for name, type, description in parameters: + self.content += '|-\n' + self.content += '| %s\n' % name + self.content += '| %s\n' % type + self.content += '| %s\n' % description + self.content += '|}\n' + + def writeFooter(self): + # Let the reader know that this documentation was automatically + # generated: + self.content += '----\n' + self.content += ("''This document was automatically generated by the " + "write_rpc_doc.py utility.''\n") + + +parser = MethodParser('toontown/rpc/ToontownRPCHandler.py') +parser.parse() +generator = MediaWikiGenerator(parser.getMethods()) +with open('wiki.txt', 'w') as f: + f.write(generator.generate()) diff --git a/dev/tools/whitelist/whitelist_tool.bat b/dev/tools/whitelist/whitelist_tool.bat new file mode 100644 index 00000000..4b63516f --- /dev/null +++ b/dev/tools/whitelist/whitelist_tool.bat @@ -0,0 +1,3 @@ +@echo off +"C:\Panda3D-1.10.0\python\ppython.exe" -m whitelist_tool +pause \ No newline at end of file diff --git a/dev/tools/whitelist/whitelist_tool.py b/dev/tools/whitelist/whitelist_tool.py new file mode 100644 index 00000000..914ed3b4 --- /dev/null +++ b/dev/tools/whitelist/whitelist_tool.py @@ -0,0 +1,85 @@ +import os +os.chdir('../../../') + +from otp.chat import WhiteListData + +def acceptWord(): + word = raw_input('> ').rstrip().lower() + + if word == 'exit()': + saveChanges() + return + + if word.startswith('r '): + word = word.replace('r ', '') + if word not in LOCAL_LIST: + print 'Could not remove unknown word "%s" from the whitelist.' % word + else: + LOCAL_LIST.remove(word) + print 'Removed "%s" from the whitelist.' % word + elif word.startswith('m '): + merge = word.replace('m ', '') + + if os.path.isfile(merge): + print 'Opening %s...' % merge + + with open(merge) as file: + for line in file.readlines(): + line = line.replace('\r', '').replace('\n', '').lower() + print 'Adding %s...' % line + LOCAL_LIST.append(line) + else: + print 'No file named %s!' % merge + elif word in LOCAL_LIST: + print 'The word "%s" is already whitelisted.' % word + else: + LOCAL_LIST.append(word) + print 'Added the word "%s" to the whitelist.' % word + + acceptWord() + +def removeDuplicates(list): + seen = set() + seen_add = seen.add + return [x for x in list if not (x in seen or seen_add(x))] + +def saveChanges(): + global LOCAL_LIST + print 'Saving the whitelist...' + + with open('otp/chat/WhiteListData.py', 'w') as f: + f.write('WHITELIST = [\n') + + LOCAL_LIST.sort() + LOCAL_LIST = removeDuplicates(LOCAL_LIST) + + for word in LOCAL_LIST: + + if '\\' in word: + print 'Word contains illegal characters: %s' % word + continue + try: + word.decode('ascii') + except: + print 'Word cannot be decoded in ASCII mode: %s' % word + continue + + if "'" in word: + f.write(' "%s",\n' % word) + else: + f.write(" '%s',\n" % word) + + f.write(']') + + print 'Your changes have been saved! Make sure to push your changes!' + +LOCAL_LIST = WhiteListData.WHITELIST + +print 'Welcome to the Toontown Stride Whitelist Tool!' +print 'Type any word you want to add to the whitelist.' +print 'If you wish to remove a word, type "r ".' +print 'If you wish to merge a file, type "m ".' +print 'When you are done and want to save your changes, type "exit()".' + + +acceptWord() diff --git a/dev/win32/start-ai-server.bat b/dev/win32/start-ai-server.bat new file mode 100755 index 00000000..3b4bb1a1 --- /dev/null +++ b/dev/win32/start-ai-server.bat @@ -0,0 +1,37 @@ +@echo off + +rem Define some constants for our AI server: +set MAX_CHANNELS=999999 +set STATESERVER=4002 +set ASTRON_IP=127.0.0.1:7100 +set EVENTLOGGER_IP=127.0.0.1:7198 + +rem Get the user input: +set /P DISTRICT_NAME="District name (DEFAULT: Nuttyboro): " || ^ +set DISTRICT_NAME=Nuttyboro +set /P BASE_CHANNEL="Base channel (DEFAULT: 401000000): " || ^ +set BASE_CHANNEL=401000000 + +echo =============================== +echo Starting Toontown Stride AI server... +echo ppython: "C:\Panda3D-1.10.0\python\ppython.exe" +echo District name: %DISTRICT_NAME% +echo Base channel: %BASE_CHANNEL% +echo Max channels: %MAX_CHANNELS% +echo State Server: %STATESERVER% +echo Astron IP: %ASTRON_IP% +echo Event Logger IP: %EVENTLOGGER_IP% +echo =============================== + +cd ../../ + +:main +"C:\Panda3D-1.10.0\python\ppython.exe" ^ + -m toontown.ai.ServiceStart ^ + --base-channel %BASE_CHANNEL% ^ + --max-channels %MAX_CHANNELS% ^ + --stateserver %STATESERVER% ^ + --astron-ip %ASTRON_IP% ^ + --eventlogger-ip %EVENTLOGGER_IP% ^ + --district-name "%DISTRICT_NAME%" +goto main diff --git a/dev/win32/start-astron-cluster.bat b/dev/win32/start-astron-cluster.bat new file mode 100755 index 00000000..1fb40d5e --- /dev/null +++ b/dev/win32/start-astron-cluster.bat @@ -0,0 +1,5 @@ +@echo off +cd "../../dependencies/astron/" + +astrond --loglevel info config/cluster.yml +pause diff --git a/dev/win32/start-game.bat b/dev/win32/start-game.bat new file mode 100755 index 00000000..76089c1c --- /dev/null +++ b/dev/win32/start-game.bat @@ -0,0 +1,75 @@ +@echo off + +title Toontown Stride Game Launcher + +echo Choose your connection method! +echo. +echo #1 - Localhost +echo #2 - Dev Server +echo #3 - Custom +echo #4 - Local RemoteDB +echo #5 - Prod Server +echo. + +:selection + +set INPUT=-1 +set /P INPUT=Selection: + +if %INPUT%==1 ( + set TTS_GAMESERVER=127.0.0.1 +) else if %INPUT%==2 ( + set TTS_GAMESERVER=167.114.220.172 +) else if %INPUT%==4 ( + set TTS_GAMESERVER=127.0.0.1 +) else if %INPUT%==5 ( + SET TTS_GAMESERVER=lw2.ez-webz.com:7198 +) else if %INPUT%==3 ( + echo. + set /P TTS_GAMESERVER=Gameserver: +) else ( + goto selection +) + +echo. + +if %INPUT%==2 ( + set /P ttsUsername="Username: " + set /P ttsPassword="Password: " +) else if %INPUT%==4 ( + set /P ttsUsername="Username: " + set /P ttsPassword="Password: " +) else ( + set /P TTS_PLAYCOOKIE=Username: +) + +echo. + +echo =============================== +echo Starting Toontown Stride... +echo ppython: "C:\Panda3D-1.10.0\python\ppython.exe" + +if %INPUT%==2 ( + echo Username: %ttsUsername% +) else if %INPUT%==4 ( + echo Username: %ttsUsername% +) else ( + echo Username: %TTS_PLAYCOOKIE% +) + +echo Gameserver: %TTS_GAMESERVER% +echo =============================== + +cd ../../ + +:main +if %INPUT%==2 ( + "C:\Panda3D-1.10.0\python\ppython.exe" -m toontown.toonbase.ToontownStartRemoteDB +) else if %INPUT%==4 ( + "C:\Panda3D-1.10.0\python\ppython.exe" -m toontown.toonbase.ToontownStartRemoteDB +) else ( + "C:\Panda3D-1.10.0\python\ppython.exe" -m toontown.toonbase.ToontownStart +) +pause + +goto main diff --git a/dev/win32/start-uberdog-server.bat b/dev/win32/start-uberdog-server.bat new file mode 100755 index 00000000..a7cca59b --- /dev/null +++ b/dev/win32/start-uberdog-server.bat @@ -0,0 +1,33 @@ +@echo off + +rem Define some constants for our UberDOG server: +set MAX_CHANNELS=999999 +set STATESERVER=4002 +set ASTRON_IP=127.0.0.1:7100 +set EVENTLOGGER_IP=127.0.0.1:7198 + +rem Get the user input: +set /P BASE_CHANNEL="Base channel (DEFAULT: 1000000): " || ^ +set BASE_CHANNEL=1000000 + +echo =============================== +echo Starting Toontown Stride UberDOG server... +echo ppython: "C:\Panda3D-1.10.0\python\ppython.exe" +echo Base channel: %BASE_CHANNEL% +echo Max channels: %MAX_CHANNELS% +echo State Server: %STATESERVER% +echo Astron IP: %ASTRON_IP% +echo Event Logger IP: %EVENTLOGGER_IP% +echo =============================== + +cd ../../ + +:main +"C:\Panda3D-1.10.0\python\ppython.exe" ^ + -m toontown.uberdog.ServiceStart ^ + --base-channel %BASE_CHANNEL% ^ + --max-channels %MAX_CHANNELS% ^ + --stateserver %STATESERVER% ^ + --astron-ip %ASTRON_IP% ^ + --eventlogger-ip %EVENTLOGGER_IP% +goto main diff --git a/otp/__init__.py b/otp/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/otp/ai/AIBase.py b/otp/ai/AIBase.py new file mode 100755 index 00000000..009ea062 --- /dev/null +++ b/otp/ai/AIBase.py @@ -0,0 +1,133 @@ +import gc +import math +import sys +import time + +from direct.directnotify.DirectNotifyGlobal import * +from direct.interval.IntervalManager import ivalMgr +from direct.showbase import EventManager +from direct.showbase import PythonUtil +from direct.showbase.BulletinBoardGlobal import * +from direct.showbase.EventManagerGlobal import * +from direct.showbase.JobManagerGlobal import * +from direct.showbase.MessengerGlobal import * +from direct.showbase.PythonUtil import * +from direct.task import Task +from direct.task.TaskManagerGlobal import * +from otp.otpbase import BackupManager +from panda3d.core import * + + +class AIBase: + notify = directNotify.newCategory('AIBase') + + def __init__(self): + self.config = getConfigShowbase() + __builtins__['__dev__'] = self.config.GetBool('want-dev', 0) + if self.config.GetBool('use-vfs', 1): + vfs = VirtualFileSystem.getGlobalPtr() + else: + vfs = None + self.wantTk = self.config.GetBool('want-tk', 0) + self.AISleep = self.config.GetFloat('ai-sleep', 0.04) + self.AIRunningNetYield = self.config.GetBool('ai-running-net-yield', 0) + self.AIForceSleep = self.config.GetBool('ai-force-sleep', 0) + self.eventMgr = eventMgr + self.messenger = messenger + self.bboard = bulletinBoard + self.taskMgr = taskMgr + Task.TaskManager.taskTimerVerbose = self.config.GetBool('task-timer-verbose', 0) + Task.TaskManager.extendedExceptions = self.config.GetBool('extended-exceptions', 0) + self.sfxManagerList = None + self.musicManager = None + self.jobMgr = jobMgr + self.hidden = NodePath('hidden') + self.graphicsEngine = GraphicsEngine() + globalClock = ClockObject.getGlobalClock() + self.trueClock = TrueClock.getGlobalPtr() + globalClock.setRealTime(self.trueClock.getShortTime()) + globalClock.setAverageFrameRateInterval(30.0) + globalClock.tick() + taskMgr.globalClock = globalClock + __builtins__['ostream'] = Notify.out() + __builtins__['globalClock'] = globalClock + __builtins__['vfs'] = vfs + __builtins__['hidden'] = self.hidden + PythonUtil.recordFunctorCreationStacks() + self.wantStats = self.config.GetBool('want-pstats', 0) + Task.TaskManager.pStatsTasks = self.config.GetBool('pstats-tasks', 0) + taskMgr.resumeFunc = PStatClient.resumeAfterPause + wantFakeTextures = self.config.GetBool('want-fake-textures-ai', 1) + if wantFakeTextures: + loadPrcFileData('aibase', 'textures-header-only 1') + self.wantPets = self.config.GetBool('want-pets', 1) + if self.wantPets: + from toontown.pets import PetConstants + self.petMoodTimescale = self.config.GetFloat('pet-mood-timescale', 1.0) + self.petMoodDriftPeriod = self.config.GetFloat('pet-mood-drift-period', PetConstants.MoodDriftPeriod) + self.petThinkPeriod = self.config.GetFloat('pet-think-period', PetConstants.ThinkPeriod) + self.petMovePeriod = self.config.GetFloat('pet-move-period', PetConstants.MovePeriod) + self.petPosBroadcastPeriod = self.config.GetFloat('pet-pos-broadcast-period', PetConstants.PosBroadcastPeriod) + self.wantBingo = self.config.GetBool('want-fish-bingo', 1) + self.wantKarts = self.config.GetBool('want-karts', 1) + self.backups = BackupManager.BackupManager( + filepath=self.config.GetString('backups-filepath', 'dependencies/backups/'), + extension=self.config.GetString('backups-extension', '.json')) + self.createStats() + self.restart() + + def createStats(self, hostname = None, port = None): + if not self.wantStats: + return False + if PStatClient.isConnected(): + PStatClient.disconnect() + if hostname is None: + hostname = '' + if port is None: + port = -1 + PStatClient.connect(hostname, port) + return PStatClient.isConnected() + + def __sleepCycleTask(self, task): + time.sleep(self.AISleep) + return Task.cont + + def __resetPrevTransform(self, state): + PandaNode.resetAllPrevTransform() + return Task.cont + + def __ivalLoop(self, state): + ivalMgr.step() + return Task.cont + + def __igLoop(self, state): + self.graphicsEngine.renderFrame() + return Task.cont + + def shutdown(self): + self.taskMgr.remove('ivalLoop') + self.taskMgr.remove('igLoop') + self.taskMgr.remove('aiSleep') + self.eventMgr.shutdown() + + def restart(self): + self.shutdown() + self.taskMgr.add(self.__resetPrevTransform, 'resetPrevTransform', priority=-51) + self.taskMgr.add(self.__ivalLoop, 'ivalLoop', priority=20) + self.taskMgr.add(self.__igLoop, 'igLoop', priority=50) + if self.config.GetBool('garbage-collect-states', 1): + self.taskMgr.add(self.__garbageCollectStates, 'garbageCollectStates', priority=46) + if self.AISleep >= 0 and (not self.AIRunningNetYield or self.AIForceSleep): + self.taskMgr.add(self.__sleepCycleTask, 'aiSleep', priority=55) + self.eventMgr.restart() + + def __garbageCollectStates(self, state): + TransformState.garbageCollect() + RenderState.garbageCollect() + return Task.cont + + def getRepository(self): + return self.air + + def run(self): + self.taskMgr.run() diff --git a/otp/ai/AIBaseGlobal.py b/otp/ai/AIBaseGlobal.py new file mode 100755 index 00000000..10bc58c9 --- /dev/null +++ b/otp/ai/AIBaseGlobal.py @@ -0,0 +1,32 @@ +from AIBase import * +__builtins__['simbase'] = AIBase() +__builtins__['ostream'] = Notify.out() +__builtins__['run'] = simbase.run +__builtins__['taskMgr'] = simbase.taskMgr +__builtins__['jobMgr'] = simbase.jobMgr +__builtins__['eventMgr'] = simbase.eventMgr +__builtins__['messenger'] = simbase.messenger +__builtins__['bboard'] = simbase.bboard +__builtins__['config'] = simbase.config +__builtins__['directNotify'] = directNotify +from direct.showbase import Loader +simbase.loader = Loader.Loader(simbase) +__builtins__['loader'] = simbase.loader +directNotify.setDconfigLevels() + +def inspect(anObject): + from direct.tkpanels import Inspector + Inspector.inspect(anObject) + + +__builtins__['inspect'] = inspect +taskMgr.finalInit() + +# The VirtualFileSystem, which has already initialized, doesn't see the mount +# directives in the config(s) yet. We have to force it to load those manually: +from panda3d.core import VirtualFileSystem, ConfigVariableList, Filename +vfs = VirtualFileSystem.getGlobalPtr() +mounts = ConfigVariableList('vfs-mount') +for mount in mounts: + mountfile, mountpoint = (mount.split(' ', 2) + [None, None, None])[:2] + vfs.mount(Filename(mountfile), Filename(mountpoint), 0) \ No newline at end of file diff --git a/otp/ai/AIZoneData.py b/otp/ai/AIZoneData.py new file mode 100755 index 00000000..26037683 --- /dev/null +++ b/otp/ai/AIZoneData.py @@ -0,0 +1,209 @@ +from panda3d.core import * +from direct.distributed import ParentMgr +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.task import Task +from otp.otpbase import OTPGlobals +import random + +class AIZoneData: + notify = directNotify.newCategory('AIZoneData') + + def __init__(self, air, parentId, zoneId): + self._air = air + self._parentId = parentId + self._zoneId = zoneId + self._data = self._air.getZoneDataStore().getDataForZone(self._parentId, self._zoneId) + + def destroy(self): + del self._data + self._air.getZoneDataStore().releaseDataForZone(self._parentId, self._zoneId) + del self._zoneId + del self._parentId + del self._air + + def __getattr__(self, attr): + return getattr(self._data, attr) + + +class AIZoneDataObj: + notify = directNotify.newCategory('AIZoneDataObj') + DefaultCTravName = 'default' + + def __init__(self, parentId, zoneId): + self._parentId = parentId + self._zoneId = zoneId + self._refCount = 0 + self._collTravs = {} + self._collTravsStarted = set() + + def __str__(self): + output = str(self._collTravs) + output += '\n' + totalColliders = 0 + totalTraversers = 0 + for currCollTrav in self._collTravs.values(): + totalTraversers += 1 + totalColliders += currCollTrav.getNumColliders() + + output += 'Num traversers: %s Num total colliders: %s' % (totalTraversers, totalColliders) + return output + + def _incRefCount(self): + self._refCount += 1 + + def _decRefCount(self): + self._refCount -= 1 + + def _getRefCount(self): + return self._refCount + + def destroy(self): + for name in list(self._collTravsStarted): + self.stopCollTrav(cTravName=name) + + del self._collTravsStarted + del self._collTravs + if hasattr(self, '_nonCollidableParent'): + self._nonCollidableParent.removeNode() + del self._nonCollidableParent + if hasattr(self, '_render'): + self._render.removeNode() + del self._render + if hasattr(self, '_parentMgr'): + self._parentMgr.destroy() + del self._parentMgr + del self._zoneId + del self._parentId + + def getLocation(self): + return (self._parentId, self._zoneId) + + def getRender(self): + if not hasattr(self, '_render'): + self._render = NodePath('render-%s-%s' % (self._parentId, self._zoneId)) + return self._render + + def getNonCollidableParent(self): + if not hasattr(self, '_nonCollidableParent'): + render = self.getRender() + self._nonCollidableParent = render.attachNewNode('nonCollidables') + return self._nonCollidableParent + + def getParentMgr(self): + if not hasattr(self, '_parentMgr'): + self._parentMgr = ParentMgr.ParentMgr() + self._parentMgr.registerParent(OTPGlobals.SPHidden, hidden) + self._parentMgr.registerParent(OTPGlobals.SPRender, self.getRender()) + return self._parentMgr + + def hasCollTrav(self, name = None): + if name is None: + name = AIZoneDataObj.DefaultCTravName + return name in self._collTravs + + def getCollTrav(self, name = None): + if name is None: + name = AIZoneDataObj.DefaultCTravName + if name not in self._collTravs: + self._collTravs[name] = CollisionTraverser('cTrav-%s-%s-%s' % (name, self._parentId, self._zoneId)) + return self._collTravs[name] + + def removeCollTrav(self, name): + if name in self._collTravs: + del self._collTravs[name] + + def _getCTravTaskName(self, name = None): + if name is None: + name = AIZoneDataObj.DefaultCTravName + return 'collTrav-%s-%s-%s' % (name, self._parentId, self._zoneId) + + def _doCollisions(self, task = None, topNode = None, cTravName = None): + render = self.getRender() + curTime = globalClock.getFrameTime() + render.setTag('lastTraverseTime', str(curTime)) + if topNode is not None: + if not render.isAncestorOf(topNode): + self.notify.warning('invalid topNode for collision traversal in %s: %s' % (self.getLocation(), topNode)) + else: + topNode = render + if cTravName is None: + cTravName = AIZoneDataObj.DefaultCTravName + collTrav = self._collTravs[cTravName] + messenger.send('preColl-' + collTrav.getName()) + collTrav.traverse(topNode) + messenger.send('postColl-' + collTrav.getName()) + return Task.cont + + def doCollTrav(self, topNode = None, cTravName = None): + self.getCollTrav(cTravName) + self._doCollisions(topNode=topNode, cTravName=cTravName) + + def startCollTrav(self, respectPrevTransform = 1, cTravName = None): + if cTravName is None: + cTravName = AIZoneDataObj.DefaultCTravName + if cTravName not in self._collTravsStarted: + self.getCollTrav(name=cTravName) + taskMgr.add(self._doCollisions, self._getCTravTaskName(name=cTravName), priority=OTPGlobals.AICollisionPriority, extraArgs=[self._zoneId]) + self._collTravsStarted.add(cTravName) + self.setRespectPrevTransform(respectPrevTransform, cTravName=cTravName) + return + + def stopCollTrav(self, cTravName = None): + if cTravName is None: + cTravName = AIZoneDataObj.DefaultCTravName + self.notify.debug('stopCollTrav(%s, %s, %s)' % (cTravName, self._parentId, self._zoneId)) + if cTravName in self._collTravsStarted: + self.notify.info('removing %s collision traversal for (%s, %s)' % (cTravName, self._parentId, self._zoneId)) + taskMgr.remove(self._getCTravTaskName(name=cTravName)) + self._collTravsStarted.remove(cTravName) + return + + def setRespectPrevTransform(self, flag, cTravName = None): + if cTravName is None: + cTravName = AIZoneDataObj.DefaultCTravName + self._collTravs[cTravName].setRespectPrevTransform(flag) + return + + def getRespectPrevTransform(self, cTravName = None): + if cTravName is None: + cTravName = AIZoneDataObj.DefaultCTravName + return self._collTravs[cTravName].getRespectPrevTransform() + + +class AIZoneDataStore: + notify = directNotify.newCategory('AIZoneDataStore') + + def __init__(self): + self._zone2data = {} + + def destroy(self): + for zone, data in self._zone2data.items(): + data.destroy() + + del self._zone2data + + def hasDataForZone(self, parentId, zoneId): + key = (parentId, zoneId) + return key in self._zone2data + + def getDataForZone(self, parentId, zoneId): + key = (parentId, zoneId) + if key not in self._zone2data: + self._zone2data[key] = AIZoneDataObj(parentId, zoneId) + self.printStats() + data = self._zone2data[key] + data._incRefCount() + return data + + def releaseDataForZone(self, parentId, zoneId): + key = (parentId, zoneId) + data = self._zone2data[key] + data._decRefCount() + refCount = data._getRefCount() + if refCount == 0: + del self._zone2data[key] + data.destroy() + self.printStats() + + def printStats(self): + self.notify.debug('%s zones have zone data allocated' % len(self._zone2data)) diff --git a/otp/ai/BanManagerAI.py b/otp/ai/BanManagerAI.py new file mode 100755 index 00000000..ba5fd020 --- /dev/null +++ b/otp/ai/BanManagerAI.py @@ -0,0 +1,142 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.uberdog.ClientServicesManagerUD import executeHttpRequest +import time +from direct.fsm.FSM import FSM +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.MsgTypes import * +from otp.ai.MagicWordGlobal import * +from direct.showbase.DirectObject import DirectObject + + +class BanFSM(FSM): + + def __init__(self, air, avId, comment, duration, banner): + FSM.__init__(self, 'banFSM-%s' % avId) + self.air = air + self.avId = avId + + # Needed variables for the actual banning. + self.comment = comment + self.duration = duration + self.DISLid = None + self.accountId = None + self.avName = None + self.banner = banner + + def performBan(self, duration): + executeHttpRequest('ban', username=self.accountId, start=int(time.time()), + duration=duration, reason=self.comment, bannedby=self.banner) + + def ejectPlayer(self): + av = self.air.doId2do.get(self.avId) + if not av: + return + + # Send the client a 'CLIENTAGENT_EJECT' with the players name. + datagram = PyDatagram() + datagram.addServerHeader( + av.GetPuppetConnectionChannel(self.avId), + self.air.ourChannel, CLIENTAGENT_EJECT) + datagram.addUint16(152) + datagram.addString(self.avName) + simbase.air.send(datagram) + + def dbCallback(self, dclass, fields): + if dclass != simbase.air.dclassesByName['AccountAI']: + return + + self.accountId = fields.get('ACCOUNT_ID') + + if not self.accountId: + return + + if simbase.config.GetBool('want-bans', True): + self.performBan(self.duration) + self.duration = None + + def getAvatarDetails(self): + av = self.air.doId2do.get(self.avId) + if not av: + return + + self.DISLid = av.getDISLid() + self.avName = av.getName() + + def log(self): + simbase.air.writeServerEvent('ban', self.accountId, self.comment) + + def cleanup(self): + self.air = None + self.avId = None + + self.DISLid = None + self.avName = None + self.accountId = None + self.comment = None + self.duration = None + self = None + + def enterStart(self): + self.getAvatarDetails() + self.air.dbInterface.queryObject(self.air.dbId, self.DISLid, + self.dbCallback) + self.ejectPlayer() + + def exitStart(self): + self.log() + self.cleanup() + + def enterOff(self): + pass + + def exitOff(self): + pass + + +class BanManagerAI(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('BanManagerAI') + + def __init__(self, air): + self.air = air + self.banFSMs = {} + + def ban(self, avId, duration, comment, banner): + self.banFSMs[avId] = BanFSM(self.air, avId, comment, duration, banner) + self.banFSMs[avId].request('Start') + + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.banDone, [avId]) + + def banDone(self, avId): + self.banFSMs[avId].request('Off') + self.banFSMs[avId] = None + + +@magicWord(category=CATEGORY_MODERATOR, types=[str]) +def kick(reason='No reason specified'): + """ + Kick the target from the game server. + """ + target = spellbook.getTarget() + datagram = PyDatagram() + datagram.addServerHeader( + target.GetPuppetConnectionChannel(target.doId), + simbase.air.ourChannel, CLIENTAGENT_EJECT) + datagram.addUint16(155) + datagram.addString('You were kicked by a moderator for the following reason: %s' % reason) + simbase.air.send(datagram) + return "Kicked %s from the game server!" % target.getName() + + +@magicWord(category=CATEGORY_MODERATOR, types=[str, int]) +def ban(reason, duration): + """ + Ban the target from the game server. + """ + target = spellbook.getTarget() + if target == spellbook.getInvoker(): + return "You can't ban yourself!" + if reason not in ('hacking', 'language', 'other'): + return "'%s' is not a valid reason." % reason + banner = spellbook.getInvoker().DISLid + simbase.air.banManager.ban(target.doId, duration, reason, banner) + return "Banned %s from the game server!" % target.getName() diff --git a/otp/ai/Barrier.py b/otp/ai/Barrier.py new file mode 100755 index 00000000..dce9a04d --- /dev/null +++ b/otp/ai/Barrier.py @@ -0,0 +1,83 @@ +from otp.ai.AIBase import * +from direct.task import Task +from direct.showbase import DirectObject +import random + +class Barrier(DirectObject.DirectObject): + notify = directNotify.newCategory('Barrier') + + def __init__(self, name, uniqueName, avIdList, timeout, clearedFunc = None, timeoutFunc = None, doneFunc = None): + self.name = name + self.uniqueName = uniqueName + '-Barrier' + self.avIdList = avIdList[:] + self.pendingAvatars = self.avIdList[:] + self.timeout = timeout + self.clearedFunc = clearedFunc + self.timeoutFunc = timeoutFunc + self.doneFunc = doneFunc + if len(self.pendingAvatars) == 0: + self.notify.debug('%s: barrier with empty list' % self.uniqueName) + self.active = 0 + if self.clearedFunc: + self.clearedFunc() + if self.doneFunc: + self.doneFunc(self.avIdList) + return + self.taskName = self.uniqueName + '-Timeout' + origTaskName = self.taskName + while taskMgr.hasTaskNamed(self.taskName): + self.taskName = origTaskName + '-' + str(random.randint(0, 10000)) + + taskMgr.doMethodLater(self.timeout, self.__timerExpired, self.taskName) + for avId in self.avIdList: + event = simbase.air.getAvatarExitEvent(avId) + self.acceptOnce(event, self.__handleUnexpectedExit, extraArgs=[avId]) + + self.notify.debug('%s: expecting responses from %s within %s seconds' % (self.uniqueName, self.avIdList, self.timeout)) + self.active = 1 + + def cleanup(self): + if self.active: + taskMgr.remove(self.taskName) + self.active = 0 + self.ignoreAll() + + def clear(self, avId): + if avId not in self.pendingAvatars: + self.notify.warning('%s: tried to clear %s, who was not listed.' % (self.uniqueName, avId)) + return + self.notify.debug('%s: clearing avatar %s' % (self.uniqueName, avId)) + self.pendingAvatars.remove(avId) + if len(self.pendingAvatars) == 0: + self.notify.debug('%s: barrier cleared by %s' % (self.uniqueName, self.avIdList)) + self.cleanup() + if self.clearedFunc: + self.clearedFunc() + if self.doneFunc: + self.doneFunc(self.avIdList) + + def isActive(self): + return self.active + + def getPendingAvatars(self): + return self.pendingAvatars[:] + + def __timerExpired(self, task): + self.notify.warning('%s: timeout expired; responses not received from %s' % (self.uniqueName, self.pendingAvatars)) + self.cleanup() + if self.timeoutFunc: + self.timeoutFunc(self.pendingAvatars[:]) + if self.doneFunc: + clearedAvIds = self.avIdList[:] + for avId in self.pendingAvatars: + clearedAvIds.remove(avId) + + self.doneFunc(clearedAvIds) + return Task.done + + def __handleUnexpectedExit(self, avId): + if avId not in self.avIdList: + return + self.avIdList.remove(avId) + if avId in self.pendingAvatars: + self.clear(avId) diff --git a/otp/ai/MagicWordGlobal.py b/otp/ai/MagicWordGlobal.py new file mode 100755 index 00000000..5854ef46 --- /dev/null +++ b/otp/ai/MagicWordGlobal.py @@ -0,0 +1,156 @@ + +class MagicError(Exception): pass + +def ensureAccess(access, msg='Insufficient access'): + if spellbook.getInvokerAccess() < access: + raise MagicError(msg) + +class Spellbook: + """ + The Spellbook manages the list of all Magic Words that have been registered + anywhere in the system. When the MagicWordManager(AI) wants to process a + Magic Word, it is passed off to the Spellbook, which performs the operation. + + To add Magic Words to the Spellbook, use the @magicWord() decorator. + """ + + def __init__(self): + self.words = {} + + self.currentInvoker = None + self.currentTarget = None + + def addWord(self, word): + self.words[word.name.lower()] = word # lets make this stuff case insensitive + + def process(self, invoker, target, incantation): + self.currentInvoker = invoker + self.currentTarget = target + word, args = (incantation.split(' ', 1) + [''])[:2] + + try: + return self.doWord(word, args) + except MagicError as e: + return e.message + except Exception: + return describeException(backTrace=1) + finally: + self.currentInvoker = None + self.currentTarget = None + + def doWord(self, wordName, args): + word = self.words.get(wordName.lower()) # look it up by its lower case value + + if not word: + if process == 'ai': + wname = wordName.lower() + for key in self.words: + if self.words.get(key).access <= self.getInvokerAccess(): + if wname in key: + return 'Did you mean %s' % (self.words.get(key).name) + if not word: + return + + ensureAccess(word.access) + if self.getTarget() and self.getTarget() != self.getInvoker(): + if self.getInvokerAccess() <= self.getTarget().getAdminAccess(): + raise MagicError('Target must have lower access') + + result = word.run(args) + if result is not None: + return str(result) + + def getInvoker(self): + return self.currentInvoker + + def getTarget(self): + return self.currentTarget + + def getInvokerAccess(self): + if not self.currentInvoker: + return 0 + return self.currentInvoker.getAdminAccess() + +spellbook = Spellbook() + + +# CATEGORIES +class MagicWordCategory: + def __init__(self, name, defaultAccess=600): + self.name = name + self.defaultAccess = defaultAccess + +CATEGORY_UNKNOWN = MagicWordCategory('Unknown') +CATEGORY_USER = MagicWordCategory('Community manager', defaultAccess=100) +CATEGORY_COMMUNITY_MANAGER = MagicWordCategory('Community manager', defaultAccess=200) +CATEGORY_MODERATOR = MagicWordCategory('Moderator', defaultAccess=300) +CATEGORY_CREATIVE = MagicWordCategory('Creative', defaultAccess=400) +CATEGORY_PROGRAMMER = MagicWordCategory('Programmer', defaultAccess=500) +CATEGORY_ADMINISTRATOR = MagicWordCategory('Administrator', defaultAccess=600) +CATEGORY_SYSTEM_ADMINISTRATOR = MagicWordCategory('System administrator', defaultAccess=700) + +MINIMUM_MAGICWORD_ACCESS = CATEGORY_COMMUNITY_MANAGER.defaultAccess + +class MagicWord: + def __init__(self, name, func, types, access, doc): + self.name = name + self.func = func + self.types = types + self.access = access + self.doc = doc + + def parseArgs(self, string): + maxArgs = self.func.func_code.co_argcount + minArgs = maxArgs - (len(self.func.func_defaults) if self.func.func_defaults else 0) + + args = string.split(None, maxArgs-1)[:maxArgs] + if len(args) < minArgs: + raise MagicError('Magic word %s requires at least %d arguments' % (self.name, minArgs)) + + output = [] + for i, (type, arg) in enumerate(zip(self.types, args)): + try: + targ = type(arg) + except (TypeError, ValueError): + raise MagicError('Argument %d of magic word %s must be %s' % (i, self.name, type.__name__)) + + output.append(targ) + + return output + + def run(self, rawArgs): + args = self.parseArgs(rawArgs) + return self.func(*args) + + +class MagicWordDecorator: + """ + This class manages Magic Word decoration. It is aliased as magicWord, so that + the @magicWord(...) construct instantiates this class and has the resulting + object process the Magic Word's construction. + """ + + def __init__(self, name=None, types=[str], access=None, category=CATEGORY_UNKNOWN): + self.name = name + self.types = types + self.category = category + if access is not None: + self.access = access + else: + self.access = self.category.defaultAccess + + def __call__(self, mw): + # This is the actual decoration routine. We add the function 'mw' as a + # Magic Word to the Spellbook, using the attributes specified at construction + # time. + + name = self.name + if name is None: + name = mw.func_name + + word = MagicWord(name, mw, self.types, self.access, mw.__doc__) + spellbook.addWord(word) + + return mw + +magicWord = MagicWordDecorator diff --git a/otp/ai/MagicWordManager.py b/otp/ai/MagicWordManager.py new file mode 100755 index 00000000..e9442937 --- /dev/null +++ b/otp/ai/MagicWordManager.py @@ -0,0 +1,43 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from otp.ai.MagicWordGlobal import * + +lastClickedNametag = None + +class MagicWordManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('MagicWordManager') + neverDisable = 1 + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.accept('magicWord', self.handleMagicWord) + + def disable(self): + self.ignore('magicWord') + DistributedObject.DistributedObject.disable(self) + + def handleMagicWord(self, magicWord): + if not base.localAvatar.isAdmin(): + return + + if magicWord.startswith('~~'): + if lastClickedNametag == None: + target = base.localAvatar + else: + target = lastClickedNametag + magicWord = magicWord[2:] + if magicWord.startswith('~'): + target = base.localAvatar + magicWord = magicWord[1:] + + targetId = target.doId + self.sendUpdate('sendMagicWord', [magicWord, targetId]) + + if target == base.localAvatar: + response = spellbook.process(base.localAvatar, target, magicWord) + if response: + self.sendMagicWordResponse(response) + + def sendMagicWordResponse(self, response): + self.notify.info(response) + base.localAvatar.setSystemMessage(0, 'Spellbook: ' + str(response)) diff --git a/otp/ai/MagicWordManagerAI.py b/otp/ai/MagicWordManagerAI.py new file mode 100755 index 00000000..69d75a91 --- /dev/null +++ b/otp/ai/MagicWordManagerAI.py @@ -0,0 +1,75 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from otp.ai.MagicWordGlobal import * +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.MsgTypes import * + +class MagicWordManagerAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("MagicWordManagerAI") + + def sendMagicWord(self, word, targetId): + invokerId = self.air.getAvatarIdFromSender() + invoker = self.air.doId2do.get(invokerId) + + if not 'DistributedToonAI' in str(self.air.doId2do.get(targetId)): + self.sendUpdateToAvatarId(invokerId, 'sendMagicWordResponse', ['Target is not a toon object!']) + return + + if not invoker: + self.sendUpdateToAvatarId(invokerId, 'sendMagicWordResponse', ['missing invoker']) + return + + if not invoker.isAdmin(): + self.air.writeServerEvent('suspicious', invokerId, 'Attempted to issue magic word: %s' % word) + dg = PyDatagram() + dg.addServerHeader(self.GetPuppetConnectionChannel(invokerId), self.air.ourChannel, CLIENTAGENT_EJECT) + dg.addUint16(102) + dg.addString('Magic Words are reserved for administrators only!') + self.air.send(dg) + return + + target = self.air.doId2do.get(targetId) + if not target: + self.sendUpdateToAvatarId(invokerId, 'sendMagicWordResponse', ['missing target']) + return + + response = spellbook.process(invoker, target, word) + if response: + self.sendUpdateToAvatarId(invokerId, 'sendMagicWordResponse', [response]) + + self.air.writeServerEvent('magic-word', + invokerId, invoker.getAdminAccess(), + targetId, target.getAdminAccess(), + word, response) + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[str]) +def help(wordName=None): + if not wordName: + return "What were you interested getting help for?" + word = spellbook.words.get(wordName.lower()) + if not word: + accessLevel = spellbook.getInvoker().getAdminAccess() + wname = wordName.lower() + for key in spellbook.words: + if spellbook.words.get(key).access <= accessLevel: + if wname in key: + return 'Did you mean %s' % (spellbook.words.get(key).name) + return 'I have no clue what %s is referring to' % (wordName) + return word.doc + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[]) +def words(): + accessLevel = spellbook.getInvoker().getAdminAccess() + wordString = None + for key in spellbook.words: + word = spellbook.words.get(key) + if word.access <= accessLevel: + if wordString is None: + wordString = key + else: + wordString += ", "; + wordString += key; + if wordString is None: + return "You are chopped liver" + else: + return wordString diff --git a/otp/ai/TimeManager.py b/otp/ai/TimeManager.py new file mode 100755 index 00000000..3b23aa19 --- /dev/null +++ b/otp/ai/TimeManager.py @@ -0,0 +1,127 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.distributed.ClockDelta import * +from direct.showbase import PythonUtil +from direct.showbase.DirectObject import * +from direct.task import Task +from panda3d.core import * +from otp.otpbase import OTPGlobals +import time + +class TimeManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TimeManager') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.updateFreq = base.config.GetFloat('time-manager-freq', 1800) + self.minWait = base.config.GetFloat('time-manager-min-wait', 10) + self.maxUncertainty = base.config.GetFloat('time-manager-max-uncertainty', 1) + self.maxAttempts = base.config.GetInt('time-manager-max-attempts', 5) + self.thisContext = -1 + self.nextContext = 0 + self.attemptCount = 0 + self.start = 0 + self.lastAttempt = -self.minWait * 2 + + def generate(self): + self._gotFirstTimeSync = False + if self.cr.timeManager != None: + self.cr.timeManager.delete() + self.cr.timeManager = self + DistributedObject.DistributedObject.generate(self) + self.accept('clock_error', self.handleClockError) + if self.updateFreq > 0: + self.startTask() + self.setDisconnectReason(OTPGlobals.DisconnectNone) + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.synchronize('TimeManager.announceGenerate') + + def gotInitialTimeSync(self): + return self._gotFirstTimeSync + + def disable(self): + self.ignore('clock_error') + self.stopTask() + if self.cr.timeManager == self: + self.cr.timeManager = None + del self._gotFirstTimeSync + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + self.ignore('clock_error') + self.stopTask() + if self.cr.timeManager == self: + self.cr.timeManager = None + DistributedObject.DistributedObject.delete(self) + return + + def startTask(self): + self.stopTask() + taskMgr.doMethodLater(self.updateFreq, self.doUpdate, 'timeMgrTask') + + def stopTask(self): + taskMgr.remove('timeMgrTask') + + def doUpdate(self, task): + self.synchronize('timer') + taskMgr.doMethodLater(self.updateFreq, self.doUpdate, 'timeMgrTask') + return Task.done + + def handleClockError(self): + self.synchronize('clock error') + + def synchronize(self, description): + now = globalClock.getRealTime() + if now - self.lastAttempt < self.minWait: + self.notify.debug('Not resyncing (too soon): %s' % description) + return 0 + self.thisContext = self.nextContext + self.attemptCount = 0 + self.nextContext = self.nextContext + 1 & 255 + self.notify.info('Clock sync: %s' % description) + self.start = now + self.lastAttempt = now + self.sendUpdate('requestServerTime', [self.thisContext]) + return 1 + + def serverTime(self, context, timestamp, timeOfDay): + end = globalClock.getRealTime() + aiTimeSkew = timeOfDay - self.cr.getServerTimeOfDay() + if context != self.thisContext: + self.notify.info('Ignoring TimeManager response for old context %d' % context) + return + elapsed = end - self.start + self.attemptCount += 1 + self.notify.info('Clock sync roundtrip took %0.3f ms' % (elapsed * 1000.0)) + self.notify.info('AI time delta is %s from server delta' % PythonUtil.formatElapsedSeconds(aiTimeSkew)) + average = (self.start + end) / 2.0 + uncertainty = (end - self.start) / 2.0 + globalClockDelta.resynchronize(average, timestamp, uncertainty) + self.notify.info('Local clock uncertainty +/- %.3f s' % globalClockDelta.getUncertainty()) + if globalClockDelta.getUncertainty() > self.maxUncertainty: + if self.attemptCount < self.maxAttempts: + self.notify.info('Uncertainty is too high, trying again.') + self.start = globalClock.getRealTime() + self.sendUpdate('requestServerTime', [self.thisContext]) + return + self.notify.info('Giving up on uncertainty requirement.') + self._gotFirstTimeSync = True + messenger.send('gotTimeSync') + + toontownTimeManager = getattr(base.cr, 'toontownTimeManager', None) + if toontownTimeManager: + toontownTimeManager.updateLoginTimes(timeOfDay, int(time.time()), globalClock.getRealTime()) + + def setDisconnectReason(self, disconnectCode): + self.sendUpdate('setDisconnectReason', [disconnectCode]) + + def setExceptionInfo(self): + info = describeException() + self.notify.info('Client exception: %s' % info) + self.sendUpdate('setExceptionInfo', [info]) + self.cr.flush() diff --git a/otp/ai/TimeManagerAI.py b/otp/ai/TimeManagerAI.py new file mode 100755 index 00000000..44cea38b --- /dev/null +++ b/otp/ai/TimeManagerAI.py @@ -0,0 +1,30 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.distributed.ClockDelta import globalClockDelta +from otp.otpbase import OTPGlobals +import time + +class TimeManagerAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("TimeManagerAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.avId2DcReason = {} + + def requestServerTime(self, context): + self.sendUpdateToAvatarId(self.air.getAvatarIdFromSender(), 'serverTime', [context, globalClockDelta.getRealNetworkTime(bits=32), int(time.time())]) + + def setDisconnectReason(self, reason): + avId = self.air.getAvatarIdFromSender() + + if reason == OTPGlobals.DisconnectNone and avId in self.avId2DcReason: + del self.avId2DcReason[avId] + else: + self.avId2DcReason[avId] = reason + + def setExceptionInfo(self, exception): + avId = self.air.getAvatarIdFromSender() + self.air.writeServerEvent('client-exception', avId, exception) + + def getDisconnectReason(self, avId): + return self.avId2DcReason.get(avId, 0) diff --git a/otp/ai/__init__.py b/otp/ai/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/otp/avatar/Avatar.py b/otp/avatar/Avatar.py new file mode 100755 index 00000000..75e7a584 --- /dev/null +++ b/otp/avatar/Avatar.py @@ -0,0 +1,568 @@ +from direct.actor.Actor import Actor +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from direct.interval.IntervalGlobal import * +from direct.showbase.PythonUtil import recordCreationStack +from panda3d.core import * +import random + +from otp.ai import MagicWordManager +from otp.ai.MagicWordGlobal import * +from otp.avatar.ShadowCaster import ShadowCaster +from otp.chat import ChatUtil +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPLocalizer +from otp.otpbase import OTPRender +from otp.nametag.Nametag import Nametag +from otp.nametag.NametagGroup import NametagGroup +from otp.nametag.NametagConstants import * + +teleportNotify = DirectNotifyGlobal.directNotify.newCategory('Teleport') +teleportNotify.showTime = True +if config.GetBool('want-teleport-debug', 1): + teleportNotify.setDebug(1) + +def reconsiderAllUnderstandable(): + for av in Avatar.ActiveAvatars: + av.considerUnderstandable() + +class Avatar(Actor, ShadowCaster): + notify = directNotify.newCategory('Avatar') + ActiveAvatars = [] + + def __init__(self, other = None): + Actor.__init__(self, None, None, other, flattenable=0, setFinal=1) + ShadowCaster.__init__(self) + self.__font = OTPGlobals.getInterfaceFont() + self.name = '' + self.soundChatBubble = None + self.avatarType = '' + self.nametagNodePath = None + self.__nameVisible = 1 + self.nametag = NametagGroup() + self.nametag.setAvatar(self) + self.nametag.setFont(OTPGlobals.getInterfaceFont()) + self.nametag.setSpeechFont(OTPGlobals.getInterfaceFont()) + self.nametag2dContents = Nametag.CName | Nametag.CSpeech + self.nametag2dDist = Nametag.CName | Nametag.CSpeech + self.nametag2dNormalContents = Nametag.CName | Nametag.CSpeech + self.nametag3d = self.attachNewNode('nametag3d') + self.nametag3d.setTag('cam', 'nametag') + self.nametag3d.setLightOff() + self.getGeomNode().showThrough(OTPRender.ShadowCameraBitmask) + self.nametag3d.hide(OTPRender.ShadowCameraBitmask) + self.collTube = None + self.scale = 1.0 + self.height = 0.0 + self.style = None + self.understandable = 1 + self.setPlayerType(NametagGroup.CCNormal) + self.ghostMode = 0 + self.__chatParagraph = None + self.__chatMessage = None + self.__chatFlags = 0 + self.__chatPageNumber = None + self.__chatAddressee = None + self.__chatDialogueList = [] + self.__chatSet = 0 + self.__chatLocal = 0 + self.__currentDialogue = None + self.wantAdminTag = True + + def delete(self): + try: + self.Avatar_deleted + except: + self.deleteNametag3d() + Actor.cleanup(self) + self.Avatar_deleted = 1 + del self.__font + del self.style + del self.soundChatBubble + self.nametag.destroy() + del self.nametag + self.nametag3d.removeNode() + ShadowCaster.delete(self) + Actor.delete(self) + + def isLocal(self): + return 0 + + def isPet(self): + return False + + def isProxy(self): + return False + + def setPlayerType(self, playerType): + self.playerType = playerType + if not hasattr(self, 'nametag'): + self.notify.warning('no nametag attributed, but would have been used.') + return + if self.isUnderstandable(): + self.nametag.setColorCode(self.playerType) + else: + self.nametag.setColorCode(NametagGroup.CCNonPlayer) + self.setNametagName() + + def considerUnderstandable(self): + if self.playerType in (NametagGroup.CCNormal, NametagGroup.CCSpeedChat): + self.setPlayerType(NametagGroup.CCSpeedChat) + if hasattr(base, 'localAvatar') and (self == base.localAvatar): + self.understandable = 1 + self.setPlayerType(NametagGroup.CCNormal) + elif self.playerType == NametagGroup.CCSuit: + self.understandable = 1 + self.setPlayerType(NametagGroup.CCSuit) + elif self.playerType not in (NametagGroup.CCNormal, NametagGroup.CCSpeedChat): + self.understandable = 1 + self.setPlayerType(NametagGroup.CCNonPlayer) + elif base.localAvatar.isTrueFriends(self.doId): + self.understandable = 2 + self.setPlayerType(NametagGroup.CCNormal) + elif base.cr.wantSpeedchatPlus(): + self.understandable = 1 + self.setPlayerType(NametagGroup.CCSpeedChat) + else: + self.understandable = 0 + self.setPlayerType(NametagGroup.CCSpeedChat) + if base.cr.wantSpeedchatPlus() and hasattr(self, 'adminAccess') and self.isAdmin() and self != base.localAvatar: + self.understandable = 2 + if not hasattr(self, 'nametag'): + self.notify.warning('no nametag attributed, but would have been used') + else: + self.nametag.setColorCode(self.playerType) + + def isUnderstandable(self): + return self.understandable + + def setDNAString(self, dnaString): + pass + + def setDNA(self, dna): + pass + + def getAvatarScale(self): + return self.scale + + def setAvatarScale(self, scale): + if self.scale != scale: + self.scale = scale + self.getGeomNode().setScale(scale) + self.setHeight(self.height) + + def adjustNametag3d(self, parentScale = 1.0): + self.nametag3d.setPos(0, 0, self.height + 0.5) + + def getHeight(self): + return self.height + + def setHeight(self, height): + self.height = height + self.adjustNametag3d() + if self.collTube: + self.collTube.setPointB(0, 0, height - self.getRadius()) + if self.collNodePath: + self.collNodePath.forceRecomputeBounds() + + def getRadius(self): + return OTPGlobals.AvatarDefaultRadius + + def getName(self): + return self.name + + def getType(self): + return self.avatarType + + def setWantAdminTag(self, bool): + self.wantAdminTag = bool + + def getWantAdminTag(self): + return self.wantAdminTag + + def setName(self, name): + if hasattr(self, 'isDisguised') and self.isDisguised: + return + + self.name = name + + if hasattr(self, 'nametag'): + self.setNametagName() + + def setDisplayName(self, str): + if hasattr(self, 'isDisguised'): + if self.isDisguised: + return + self.setNametagName(str) + + def setNametagName(self, name=None): + if not name: + name = self.name + + self.nametag.setName(name) + + if hasattr(self, 'adminAccess') and self.isAdmin() and self.getWantAdminTag(): + access = self.getAdminAccess() + + if access in OTPLocalizer.AccessToString: + name += '\n\x01shadow\x01%s\x02' % OTPLocalizer.AccessToString[access] + + self.nametag.setDisplayName(name) + + def getFont(self): + return self.__font + + def setFont(self, font): + self.__font = font + self.nametag.setFont(font) + + def getStyle(self): + return self.style + + def setStyle(self, style): + self.style = style + + def getDialogueArray(self): + return None + + def playCurrentDialogue(self, dialogue, chatFlags, interrupt = 1): + if interrupt and self.__currentDialogue is not None: + self.__currentDialogue.stop() + self.__currentDialogue = dialogue + if dialogue: + base.playSfx(dialogue, node=self) + elif chatFlags & CFSpeech != 0 and self.nametag.getNumChatPages() > 0: + self.playDialogueForString(self.nametag.getChat()) + if self.soundChatBubble != None: + base.playSfx(self.soundChatBubble, node=self) + + def playDialogueForString(self, chatString): + searchString = chatString.lower() + if searchString.find(OTPLocalizer.DialogSpecial) >= 0: + type = 'special' + elif searchString.find(OTPLocalizer.DialogExclamation) >= 0: + type = 'exclamation' + elif searchString.find(OTPLocalizer.DialogQuestion) >= 0: + type = 'question' + elif random.randint(0, 1): + type = 'statementA' + else: + type = 'statementB' + stringLength = len(chatString) + if stringLength <= OTPLocalizer.DialogLength1: + length = 1 + elif stringLength <= OTPLocalizer.DialogLength2: + length = 2 + elif stringLength <= OTPLocalizer.DialogLength3: + length = 3 + else: + length = 4 + self.playDialogue(type, length) + + def playDialogue(self, type, length): + dialogueArray = self.getDialogueArray() + if dialogueArray == None: + return + sfxIndex = None + if type == 'statementA' or type == 'statementB': + if length == 1: + sfxIndex = 0 + elif length == 2: + sfxIndex = 1 + elif length >= 3: + sfxIndex = 2 + elif type == 'question': + sfxIndex = 3 + elif type == 'exclamation': + sfxIndex = 4 + elif type == 'special': + sfxIndex = 5 + else: + notify.error('unrecognized dialogue type: ', type) + if sfxIndex != None and sfxIndex < len(dialogueArray) and dialogueArray[sfxIndex] != None: + base.playSfx(dialogueArray[sfxIndex], node=self) + return + + def getDialogueSfx(self, type, length): + retval = None + dialogueArray = self.getDialogueArray() + if dialogueArray == None: + return + sfxIndex = None + if type == 'statementA' or type == 'statementB': + if length == 1: + sfxIndex = 0 + elif length == 2: + sfxIndex = 1 + elif length >= 3: + sfxIndex = 2 + elif type == 'question': + sfxIndex = 3 + elif type == 'exclamation': + sfxIndex = 4 + elif type == 'special': + sfxIndex = 5 + else: + notify.error('unrecognized dialogue type: ', type) + if sfxIndex != None and sfxIndex < len(dialogueArray) and dialogueArray[sfxIndex] != None: + retval = dialogueArray[sfxIndex] + return retval + + def setChatAbsolute(self, chatString, chatFlags, dialogue=None, interrupt=1): + self.clearChat() + self.nametag.setChat(chatString, chatFlags) + self.playCurrentDialogue(dialogue, chatFlags, interrupt) + + def displayTalk(self, chatString): + if not base.localAvatar.isIgnored(self.doId): + self.clearChat() + if ChatUtil.isThought(chatString): + chatString = ChatUtil.removeThoughtPrefix(chatString) + self.nametag.setChat(chatString, CFThought) + else: + self.nametag.setChat(chatString, CFSpeech | CFTimeout) + + def clearChat(self): + self.nametag.clearChat() + + def getNameVisible(self): + return self.__nameVisible + + def setNameVisible(self, bool): + self.__nameVisible = bool + if bool: + self.showName() + if not bool: + self.hideName() + + def hideName(self): + nametag3d = self.nametag.getNametag3d() + nametag3d.setContents(Nametag.CSpeech | Nametag.CThought) + + def showName(self): + if self.__nameVisible and (not self.ghostMode): + nametag3d = self.nametag.getNametag3d() + nametag3d.setContents(Nametag.CName | Nametag.CSpeech | Nametag.CThought) + + def hideNametag2d(self): + nametag2d = self.nametag.getNametag2d() + self.nametag2dContents = 0 + nametag2d.setContents(self.nametag2dContents & self.nametag2dDist) + + def showNametag2d(self): + nametag2d = self.nametag.getNametag2d() + self.nametag2dContents = self.nametag2dNormalContents + if self.ghostMode: + self.nametag2dContents = Nametag.CSpeech + nametag2d.setContents(self.nametag2dContents & self.nametag2dDist) + + def hideNametag3d(self): + nametag3d = self.nametag.getNametag3d() + nametag3d.setContents(0) + + def showNametag3d(self): + nametag3d = self.nametag.getNametag3d() + if self.__nameVisible and (not self.ghostMode): + nametag3d.setContents(Nametag.CName | Nametag.CSpeech | Nametag.CThought) + else: + nametag3d.setContents(0) + + def setPickable(self, flag): + self.nametag.setActive(flag) + + def clickedNametag(self): + MagicWordManager.lastClickedNametag = self + if self.nametag.hasButton(): + self.advancePageNumber() + elif self.nametag.isActive(): + messenger.send('clickedNametag', [self]) + + def setPageChat(self, addressee, paragraph, message, quitButton, + extraChatFlags=None, dialogueList=[], pageButton=True): + self.__chatAddressee = addressee + self.__chatPageNumber = None + self.__chatParagraph = paragraph + self.__chatMessage = message + if extraChatFlags is None: + self.__chatFlags = CFSpeech + else: + self.__chatFlags = CFSpeech | extraChatFlags + self.__chatDialogueList = dialogueList + self.__chatSet = 0 + self.__chatLocal = 0 + self.__updatePageChat() + if addressee == base.localAvatar.doId: + if pageButton: + self.__chatFlags |= CFPageButton + if quitButton == None: + self.__chatFlags |= CFNoQuitButton + elif quitButton: + self.__chatFlags |= CFQuitButton + self.b_setPageNumber(self.__chatParagraph, 0) + + def setLocalPageChat(self, message, quitButton, extraChatFlags=None, + dialogueList=[]): + self.__chatAddressee = base.localAvatar.doId + self.__chatPageNumber = None + self.__chatParagraph = None + self.__chatMessage = message + if extraChatFlags is None: + self.__chatFlags = CFSpeech + else: + self.__chatFlags = CFSpeech | extraChatFlags + self.__chatDialogueList = dialogueList + self.__chatSet = 1 + self.__chatLocal = 1 + self.__chatFlags |= CFPageButton + if quitButton == None: + self.__chatFlags |= CFNoQuitButton + elif quitButton: + self.__chatFlags |= CFQuitButton + if len(dialogueList) > 0: + dialogue = dialogueList[0] + else: + dialogue = None + self.clearChat() + self.setChatAbsolute(message, self.__chatFlags, dialogue) + self.setPageNumber(None, 0) + + def setPageNumber(self, paragraph, pageNumber, timestamp=None): + if timestamp is None: + elapsed = 0.0 + else: + elapsed = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.__chatPageNumber = [paragraph, pageNumber] + self.__updatePageChat() + if hasattr(self, 'uniqueName'): + if pageNumber >= 0: + messenger.send(self.uniqueName('nextChatPage'), [pageNumber, elapsed]) + else: + messenger.send(self.uniqueName('doneChatPage'), [elapsed]) + elif pageNumber >= 0: + messenger.send('nextChatPage', [pageNumber, elapsed]) + else: + messenger.send('doneChatPage', [elapsed]) + + def advancePageNumber(self): + if (self.__chatAddressee == base.localAvatar.doId) and ( + self.__chatPageNumber is not None) and ( + self.__chatPageNumber[0] == self.__chatParagraph): + pageNumber = self.__chatPageNumber[1] + if pageNumber >= 0: + pageNumber += 1 + if pageNumber >= self.nametag.getNumChatPages(): + pageNumber = -1 + if self.__chatLocal: + self.setPageNumber(self.__chatParagraph, pageNumber) + else: + self.b_setPageNumber(self.__chatParagraph, pageNumber) + + def __updatePageChat(self): + if (self.__chatPageNumber is not None) and ( + self.__chatPageNumber[0] == self.__chatParagraph): + pageNumber = self.__chatPageNumber[1] + if pageNumber >= 0: + if not self.__chatSet: + if len(self.__chatDialogueList) > 0: + dialogue = self.__chatDialogueList[0] + else: + dialogue = None + self.setChatAbsolute(self.__chatMessage, self.__chatFlags, dialogue) + self.__chatSet = 1 + if pageNumber < self.nametag.getNumChatPages(): + self.nametag.setPageNumber(pageNumber) + if pageNumber > 0: + if len(self.__chatDialogueList) > pageNumber: + dialogue = self.__chatDialogueList[pageNumber] + else: + dialogue = None + self.playCurrentDialogue(dialogue, self.__chatFlags) + else: + self.clearChat() + else: + self.clearChat() + + def getAirborneHeight(self): + height = self.getPos(self.shadowPlacer.shadowNodePath) + return height.getZ() + 0.025 + + def initializeNametag3d(self): + self.deleteNametag3d() + nametagNode = self.nametag.getNametag3d() + self.nametagNodePath = self.nametag3d.attachNewNode(nametagNode) + iconNodePath = self.nametag.getNameIcon() + for cJoint in self.getNametagJoints(): + cJoint.clearNetTransforms() + cJoint.addNetTransform(nametagNode) + + def deleteNametag3d(self): + if self.nametagNodePath: + self.nametagNodePath.removeNode() + self.nametagNodePath = None + + def initializeBodyCollisions(self, collIdStr): + self.collTube = CollisionTube(0, 0, 0.5, 0, 0, self.height - self.getRadius(), self.getRadius()) + self.collNode = CollisionNode(collIdStr) + self.collNode.addSolid(self.collTube) + self.collNodePath = self.attachNewNode(self.collNode) + if self.ghostMode: + self.collNode.setCollideMask(OTPGlobals.GhostBitmask) + else: + self.collNode.setCollideMask(OTPGlobals.WallBitmask) + + def stashBodyCollisions(self): + if hasattr(self, 'collNodePath'): + self.collNodePath.stash() + + def unstashBodyCollisions(self): + if hasattr(self, 'collNodePath'): + self.collNodePath.unstash() + + def disableBodyCollisions(self): + if hasattr(self, 'collNodePath'): + self.collNodePath.removeNode() + del self.collNodePath + self.collTube = None + return + + def addActive(self): + if base.wantNametags: + try: + Avatar.ActiveAvatars.remove(self) + except ValueError: + pass + + Avatar.ActiveAvatars.append(self) + self.nametag.manage(base.marginManager) + self.accept(self.nametag.getUniqueId(), self.clickedNametag) + + def removeActive(self): + if base.wantNametags: + try: + Avatar.ActiveAvatars.remove(self) + except ValueError: + pass + + self.nametag.unmanage(base.marginManager) + self.ignore(self.nametag.getUniqueId()) + + def loop(self, animName, restart = 1, partName = None, fromFrame = None, toFrame = None): + return Actor.loop(self, animName, restart, partName, fromFrame, toFrame) + + def createTalkSequence(self, speech, waitTime, name='talkSequence'): + sequence = Sequence(name=name) + + for text in speech: + sequence.append(Func(self.setChatAbsolute, text, CFSpeech)) + sequence.append(Wait(len(text.split(' ')))) + sequence.append(Func(self.clearChat)) + sequence.append(Wait(waitTime)) + + return sequence + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def target(): + """ + Returns the current Spellbook target. + """ + return 'Your current target is: %s [avId: %s, access: %s]' % (spellbook.getTarget().getName(), spellbook.getTarget().doId, spellbook.getTarget().getAdminAccess()) \ No newline at end of file diff --git a/otp/avatar/AvatarDetail.py b/otp/avatar/AvatarDetail.py new file mode 100755 index 00000000..662fe6f6 --- /dev/null +++ b/otp/avatar/AvatarDetail.py @@ -0,0 +1,51 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from otp.avatar import Avatar + +class AvatarDetail: + notify = directNotify.newCategory('AvatarDetail') + + def __init__(self, doId, callWhenDone): + self.id = doId + self.callWhenDone = callWhenDone + self.enterQuery() + + def isReady(self): + return true + + def getId(self): + return self.id + + def enterQuery(self): + self.avatar = base.cr.doId2do.get(self.id) + if self.avatar != None and not self.avatar.ghostMode: + self.createdAvatar = 0 + dclass = self.getDClass() + self.__handleResponse(True, self.avatar, dclass) + else: + self.avatar = self.createHolder() + self.createdAvatar = 1 + self.avatar.doId = self.id + dclass = self.getDClass() + base.cr.getAvatarDetails(self.avatar, self.__handleResponse, dclass) + return + + def exitQuery(self): + return true + + def createHolder(self): + pass + + def getDClass(self): + pass + + def __handleResponse(self, gotData, avatar, dclass): + if avatar != self.avatar: + self.notify.warning('Ignoring unexpected request for avatar %s' % avatar.doId) + return + if gotData: + self.callWhenDone(self.avatar) + del self.callWhenDone + else: + self.callWhenDone(None) + del self.callWhenDone + return diff --git a/otp/avatar/AvatarPanel.py b/otp/avatar/AvatarPanel.py new file mode 100755 index 00000000..cdd70ea5 --- /dev/null +++ b/otp/avatar/AvatarPanel.py @@ -0,0 +1,75 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from direct.showbase import DirectObject +import Avatar +from direct.distributed import DistributedObject + +class AvatarPanel(DirectObject.DirectObject): + currentAvatarPanel = None + + def __init__(self, avatar, FriendsListPanel = None): + if AvatarPanel.currentAvatarPanel: + AvatarPanel.currentAvatarPanel.cleanup() + AvatarPanel.currentAvatarPanel = self + self.friendsListShown = False + self.FriendsListPanel = FriendsListPanel + if FriendsListPanel: + self.friendsListShown = FriendsListPanel.isFriendsListShown() + FriendsListPanel.hideFriendsList() + if avatar: + self.avatar = avatar + self.avName = avatar.getName() + else: + self.avatar = None + self.avName = 'Avatar' + if hasattr(avatar, 'uniqueName'): + self.avId = avatar.doId + self.avDisableName = avatar.uniqueName('disable') + self.avGenerateName = avatar.uniqueName('generate') + self.avHpChangeName = avatar.uniqueName('hpChange') + if self.avId in base.cr.doId2do: + self.avatar = base.cr.doId2do[self.avId] + else: + self.avDisableName = None + self.avGenerateName = None + self.avHpChangeName = None + self.avId = None + if self.avDisableName: + self.accept(self.avDisableName, self.__handleDisableAvatar) + return + + def cleanup(self): + if AvatarPanel.currentAvatarPanel != self: + return + if self.avDisableName: + self.ignore(self.avDisableName) + if self.avGenerateName: + self.ignore(self.avGenerateName) + if self.avHpChangeName: + self.ignore(self.avHpChangeName) + AvatarPanel.currentAvatarPanel = None + return + + def __handleClose(self): + self.cleanup() + AvatarPanel.currentAvatarPanel = None + if self.friendsListShown: + self.FriendsListPanel.showFriendsList() + return + + def __handleDisableAvatar(self): + if AvatarPanel.currentAvatarPanel: + AvatarPanel.currentAvatarPanel.handleDisableAvatar() + else: + self.handleDisableAvatar() + + def handleDisableAvatar(self): + self.cleanup() + AvatarPanel.currentAvatarPanel = None + return + + def isHidden(self): + return 1 + + def getType(self): + return None diff --git a/otp/avatar/DistributedAvatar.py b/otp/avatar/DistributedAvatar.py new file mode 100755 index 00000000..85644f94 --- /dev/null +++ b/otp/avatar/DistributedAvatar.py @@ -0,0 +1,346 @@ +from direct.actor.DistributedActor import DistributedActor +from direct.distributed import DistributedNode +from direct.interval.IntervalGlobal import * +from direct.showbase import PythonUtil +from direct.task import Task +from panda3d.core import * + +from Avatar import Avatar +from otp.ai.MagicWordGlobal import * +from otp.otpbase import OTPGlobals +from toontown.battle.BattleProps import globalPropPool +from otp.nametag.Nametag import Nametag + + +class DistributedAvatar(DistributedActor, Avatar): + HpTextGenerator = TextNode('HpTextGenerator') + HpTextEnabled = 1 + ManagesNametagAmbientLightChanged = True + + def __init__(self, cr): + try: + self.DistributedAvatar_initialized + return + except: + self.DistributedAvatar_initialized = 1 + + Avatar.__init__(self) + DistributedActor.__init__(self, cr) + self.hpText = None + self.hp = None + self.maxHp = None + return + + def disable(self): + try: + del self.DistributedAvatar_announced + except: + return + + self.reparentTo(hidden) + self.removeActive() + self.disableBodyCollisions() + self.hideHpText() + self.hp = None + self.ignore('nameTagShowAvId') + self.ignore('nameTagShowName') + DistributedActor.disable(self) + return + + def delete(self): + try: + self.DistributedAvatar_deleted + except: + self.DistributedAvatar_deleted = 1 + Avatar.delete(self) + DistributedActor.delete(self) + + def generate(self): + DistributedActor.generate(self) + if not self.isLocal(): + self.addActive() + self.considerUnderstandable() + self.setParent(OTPGlobals.SPHidden) + self.setTag('avatarDoId', str(self.doId)) + self.accept('nameTagShowAvId', self.__nameTagShowAvId) + self.accept('nameTagShowName', self.__nameTagShowName) + + def announceGenerate(self): + try: + self.DistributedAvatar_announced + return + except: + self.DistributedAvatar_announced = 1 + + if not self.isLocal(): + self.initializeBodyCollisions('distAvatarCollNode-' + str(self.doId)) + DistributedActor.announceGenerate(self) + + def __setTags(self, extra = None): + if hasattr(base, 'idTags'): + if base.idTags: + self.__nameTagShowAvId() + else: + self.__nameTagShowName() + + def do_setParent(self, parentToken): + if not self.isDisabled(): + if parentToken == OTPGlobals.SPHidden: + self.nametag2dDist &= ~Nametag.CName + else: + self.nametag2dDist |= Nametag.CName + self.nametag.getNametag2d().setContents(self.nametag2dContents & self.nametag2dDist) + DistributedActor.do_setParent(self, parentToken) + self.__setTags() + + def toonUp(self, hpGained): + if self.hp == None or hpGained < 0: + return + oldHp = self.hp + if self.hp + hpGained <= 0: + self.hp += hpGained + else: + self.hp = min(max(self.hp, 0) + hpGained, self.maxHp) + hpGained = self.hp - max(oldHp, 0) + if hpGained > 0: + self.showHpText(hpGained) + self.hpChange(quietly=0) + return + + def takeDamage(self, hpLost, bonus = 0): + if self.hp == None or hpLost < 0: + return + oldHp = self.hp + self.hp = max(self.hp - hpLost, 0) + hpLost = oldHp - self.hp + if hpLost > 0: + self.showHpText(-hpLost, bonus) + self.hpChange(quietly=0) + if self.hp <= 0 and oldHp > 0: + self.died() + return + + def setHp(self, hitPoints): + justRanOutOfHp = (hitPoints is not None and self.hp is not None and self.hp - hitPoints > 0) and (hitPoints <= 0) + self.hp = hitPoints + self.hpChange(quietly=1) + if justRanOutOfHp: + self.died() + return + + def hpChange(self, quietly = 0): + if (not hasattr(self, 'doId')) or self.hp == None: + return + + if self.maxHp != None: + messenger.send(self.uniqueName('hpChange'), [self.hp, self.maxHp, quietly]) + if self.hp > 0: + messenger.send(self.uniqueName('positiveHP')) + + def died(self): + pass + + def getHp(self): + return self.hp + + def setMaxHp(self, hitPoints): + self.maxHp = hitPoints + self.hpChange() + + def getMaxHp(self): + return self.maxHp + + def getName(self): + return Avatar.getName(self) + + def setName(self, name): + try: + self.node().setName('%s-%d' % (name, self.doId)) + self.gotName = 1 + except: + pass + + return Avatar.setName(self, name) + + def showHpText(self, number, bonus = 0, scale = 1): + if self.HpTextEnabled and not self.ghostMode: + if number != 0: + if self.hpText: + self.hideHpText() + self.HpTextGenerator.setFont(OTPGlobals.getSignFont()) + if number < 0: + self.HpTextGenerator.setText(str(number)) + else: + self.HpTextGenerator.setText('+' + str(number)) + self.HpTextGenerator.clearShadow() + self.HpTextGenerator.setAlign(TextNode.ACenter) + if bonus == 1: + r = 1.0 + g = 1.0 + b = 0 + a = 1 + elif bonus == 2: + r = 1.0 + g = 0.5 + b = 0 + a = 1 + elif number < 0: + r = 0.9 + g = 0 + b = 0 + a = 1 + else: + r = 0 + g = 0.9 + b = 0 + a = 1 + self.HpTextGenerator.setTextColor(r, g, b, a) + self.hpTextNode = self.HpTextGenerator.generate() + self.hpText = self.attachNewNode(self.hpTextNode) + self.hpText.setScale(scale) + self.hpText.setBillboardPointEye() + self.hpText.setBin('fixed', 100) + self.hpText.setPos(0, 0, self.height / 2) + seq = Sequence(self.hpText.posInterval(1.0, Point3(0, 0, self.height + 1.5), blendType='easeOut'), Wait(0.85), self.hpText.colorInterval(0.1, Vec4(r, g, b, 0)), Func(self.hideHpText)) + seq.start() + + def showHpString(self, text, duration = 0.85, scale = 0.7): + if self.HpTextEnabled and not self.ghostMode: + if text != '': + if self.hpText: + self.hideHpText() + self.HpTextGenerator.setFont(OTPGlobals.getSignFont()) + self.HpTextGenerator.setText(text) + self.HpTextGenerator.clearShadow() + self.HpTextGenerator.setAlign(TextNode.ACenter) + r = a = 1.0 + g = b = 0.0 + self.HpTextGenerator.setTextColor(r, g, b, a) + self.hpTextNode = self.HpTextGenerator.generate() + self.hpText = self.attachNewNode(self.hpTextNode) + self.hpText.setScale(scale) + self.hpText.setBillboardAxis() + self.hpText.setPos(0, 0, self.height / 2) + seq = Sequence(self.hpText.posInterval(1.0, Point3(0, 0, self.height + 1.5), blendType='easeOut'), Wait(duration), self.hpText.colorInterval(0.1, Vec4(r, g, b, 0)), Func(self.hideHpText)) + seq.start() + + def hideHpText(self): + if self.hpText: + taskMgr.remove(self.uniqueName('hpText')) + self.hpText.removeNode() + self.hpText = None + return + + def getStareAtNodeAndOffset(self): + return (self, Point3(0, 0, self.height)) + + def getAvIdName(self): + return '%s\n%s' % (self.getName(), self.doId) + + def __nameTagShowAvId(self, extra = None): + self.setDisplayName(self.getAvIdName()) + + def __nameTagShowName(self, extra = None): + self.setDisplayName(self.getName()) + + def askAvOnShard(self, avId): + if base.cr.doId2do.get(avId): + messenger.send('AvOnShard%s' % avId, [True]) + else: + self.sendUpdate('checkAvOnShard', [avId]) + + def confirmAvOnShard(self, avId, onShard = True): + messenger.send('AvOnShard%s' % avId, [onShard]) + + def getDialogueArray(self): + return None + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def warp(): + """ + warp the target to the invoker's current position, and rotation. + """ + invoker = spellbook.getInvoker() + target = spellbook.getTarget() + if invoker.doId == target.doId: + return "You can't warp yourself!" + target.setPosHpr(invoker.getPos(), invoker.getHpr()) + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[str]) +def loop(anim): + """ + animate the target using animation [anim] on the entire actor. + """ + target = spellbook.getTarget() + target.loop(anim) + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[str, int, str]) +def pose(anim, frame, part=None): + """ + freeze the target on frame [frame] of animation [anim] on the entire actor, + or optional [part] of the actor. + """ + target = spellbook.getTarget() + target.pose(anim, frame, partName=part) + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[str, int, int, str]) +def pingpong(anim, start=None, end=None, part=None): + """ + animate the target by bouncing back and forth between the start and end, or + the optional frames , and [end] of animation [anim] on the entire + actor, or optional of the actor. + """ + target = spellbook.getTarget() + target.pingpong(anim, partName=part, fromFrame=start, toFrame=end) + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[str]) +def rightHand(prop=None): + """ + parents the optional to the target's right hand node. + """ + target = spellbook.getTarget() + rightHand = target.find('**/rightHand') + if prop is None: + for child in rightHand.getChildren(): + child.removeNode() + else: + for child in rightHand.getChildren(): + child.removeNode() + requestedProp = globalPropPool.getProp(prop) + requestedProp.reparentTo(rightHand) + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[str]) +def leftHand(prop=None): + """ + parents the optional to the target's left hand node. + """ + target = spellbook.getTarget() + leftHand = target.find('**/leftHand') + if prop is None: + for child in leftHand.getChildren(): + child.removeNode() + else: + for child in leftHand.getChildren(): + child.removeNode() + requestedProp = globalPropPool.getProp(prop) + requestedProp.reparentTo(leftHand) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[]) +def getPos(): + """ + Return your target's position. + """ + return spellbook.getTarget().getPos() + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def setFov(fov=OTPGlobals.DefaultCameraFov): + """ + Set your field of view in-game. + """ + if fov == 0: + return 'Cannot set FOV to 0!' + base.camLens.setMinFov(fov/(4./3.)) + if fov == OTPGlobals.DefaultCameraFov: + return 'Set FOV to the default.' + else: + return 'Set FOV to %s.' % fov diff --git a/otp/avatar/DistributedAvatarAI.py b/otp/avatar/DistributedAvatarAI.py new file mode 100755 index 00000000..f03ce7ac --- /dev/null +++ b/otp/avatar/DistributedAvatarAI.py @@ -0,0 +1,68 @@ +from otp.ai.AIBaseGlobal import * +from otp.otpbase import OTPGlobals +from direct.distributed import DistributedNodeAI + +class DistributedAvatarAI(DistributedNodeAI.DistributedNodeAI): + + def __init__(self, air): + DistributedNodeAI.DistributedNodeAI.__init__(self, air) + self.hp = 0 + self.maxHp = 0 + + def b_setName(self, name): + self.setName(name) + self.d_setName(name) + + def d_setName(self, name): + self.sendUpdate('setName', [name]) + + def setName(self, name): + self.name = name + + def getName(self): + return self.name + + def b_setMaxHp(self, maxHp): + self.d_setMaxHp(maxHp) + self.setMaxHp(maxHp) + + def d_setMaxHp(self, maxHp): + self.sendUpdate('setMaxHp', [maxHp]) + + def setMaxHp(self, maxHp): + self.maxHp = maxHp + + def getMaxHp(self): + return self.maxHp + + def b_setHp(self, hp): + self.d_setHp(hp) + self.setHp(hp) + + def d_setHp(self, hp): + self.sendUpdate('setHp', [hp]) + + def setHp(self, hp): + self.hp = hp + + def getHp(self): + return self.hp + + def toonUp(self, num): + if self.hp >= self.maxHp: + return + self.hp = min(self.hp + num, self.maxHp) + self.b_setHp(self.hp) + + def getRadius(self): + return OTPGlobals.AvatarDefaultRadius + + def checkAvOnShard(self, avId): + self.sendUpdateToAvatarId(self.air.getAvatarIdFromSender(), 'confirmAvOnShard', [avId, avId in self.air.doId2do]) + + def setParentStr(self, parentToken): + if parentToken: + senderId = self.air.getAvatarIdFromSender() + self.air.writeServerEvent('Admin chat warning', senderId, 'using setParentStr to send "%s"' % parentToken) + self.notify.warning('Admin chat warning: %s using setParentStr to send "%s"' % (senderId, parentToken)) + DistributedNodeAI.DistributedNodeAI.setParentStr(self, parentToken) diff --git a/otp/avatar/DistributedAvatarUD.py b/otp/avatar/DistributedAvatarUD.py new file mode 100755 index 00000000..8f4fe20e --- /dev/null +++ b/otp/avatar/DistributedAvatarUD.py @@ -0,0 +1,17 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectUD import DistributedObjectUD + +class DistributedAvatarUD(DistributedObjectUD): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedAvatarUD") + + def setName(self, todo0): + pass + + def friendsNotify(self, todo0, todo1): + pass + + def checkAvOnShard(self, todo0): + pass + + def confirmAvOnShard(self, todo0, todo1): + pass diff --git a/otp/avatar/DistributedPlayer.py b/otp/avatar/DistributedPlayer.py new file mode 100755 index 00000000..fca75216 --- /dev/null +++ b/otp/avatar/DistributedPlayer.py @@ -0,0 +1,357 @@ +from direct.showbase import PythonUtil +from direct.task import Task +from panda3d.core import * +import string +import time + +from otp.ai.MagicWordGlobal import * +from otp.avatar import Avatar, PlayerBase, DistributedAvatar +from otp.avatar.Avatar import teleportNotify +from otp.chat import ChatGarbler, TalkAssistant +from otp.distributed.TelemetryLimited import TelemetryLimited +from otp.otpbase import OTPGlobals, OTPLocalizer +from otp.speedchat import SCDecoders +from otp.nametag.NametagConstants import * +from otp.margins.WhisperPopup import WhisperPopup + +class DistributedPlayer(DistributedAvatar.DistributedAvatar, PlayerBase.PlayerBase, TelemetryLimited): + chatGarbler = ChatGarbler.ChatGarbler({'default': OTPLocalizer.ChatGarblerDefault}) + + def __init__(self, cr): + try: + self.DistributedPlayer_initialized + except: + self.DistributedPlayer_initialized = 1 + DistributedAvatar.DistributedAvatar.__init__(self, cr) + TelemetryLimited.__init__(self) + self.__teleportAvailable = 0 + self.inventory = None + self.experience = None + self.friendsList = [] + self._districtWeAreGeneratedOn = None + self.DISLid = 0 + self.adminAccess = 0 + self.autoRun = 0 + self.lastTeleportQuery = time.time() + + @staticmethod + def GetPlayerGenerateEvent(): + return 'DistributedPlayerGenerateEvent' + + @staticmethod + def GetPlayerNetworkDeleteEvent(): + return 'DistributedPlayerNetworkDeleteEvent' + + @staticmethod + def GetPlayerDeleteEvent(): + return 'DistributedPlayerDeleteEvent' + + def networkDelete(self): + DistributedAvatar.DistributedAvatar.networkDelete(self) + messenger.send(self.GetPlayerNetworkDeleteEvent(), [self]) + + def disable(self): + DistributedAvatar.DistributedAvatar.disable(self) + messenger.send(self.GetPlayerDeleteEvent(), [self]) + + def delete(self): + try: + self.DistributedPlayer_deleted + except: + self.DistributedPlayer_deleted = 1 + del self.experience + if self.inventory: + self.inventory.unload() + del self.inventory + DistributedAvatar.DistributedAvatar.delete(self) + + def generate(self): + DistributedAvatar.DistributedAvatar.generate(self) + + def announceGenerate(self): + DistributedAvatar.DistributedAvatar.announceGenerate(self) + messenger.send(self.GetPlayerGenerateEvent(), [self]) + + def setLocation(self, parentId, zoneId): + DistributedAvatar.DistributedAvatar.setLocation(self, parentId, zoneId) + if not (parentId in (0, None) and zoneId in (0, None)): + if not self.cr._isValidPlayerLocation(parentId, zoneId): + self.cr.disableDoId(self.doId) + self.cr.deleteObject(self.doId) + return None + + def isGeneratedOnDistrict(self, districtId = None): + return True # fix for the task button + if districtId is None: + return self._districtWeAreGeneratedOn is not None + else: + return self._districtWeAreGeneratedOn == districtId + return + + def getArrivedOnDistrictEvent(self, districtId = None): + if districtId is None: + return 'arrivedOnDistrict' + else: + return 'arrivedOnDistrict-%s' % districtId + return + + def arrivedOnDistrict(self, districtId): + curFrameTime = globalClock.getFrameTime() + if hasattr(self, 'frameTimeWeArrivedOnDistrict') and curFrameTime == self.frameTimeWeArrivedOnDistrict: + if districtId == 0 and self._districtWeAreGeneratedOn: + self.notify.warning('ignoring arrivedOnDistrict 0, since arrivedOnDistrict %d occured on the same frame' % self._districtWeAreGeneratedOn) + return + self._districtWeAreGeneratedOn = districtId + self.frameTimeWeArrivedOnDistrict = globalClock.getFrameTime() + messenger.send(self.getArrivedOnDistrictEvent(districtId)) + messenger.send(self.getArrivedOnDistrictEvent()) + + def setLeftDistrict(self): + self._districtWeAreGeneratedOn = None + return + + def hasParentingRules(self): + if self is localAvatar: + return True + + def setSystemMessage(self, aboutId, chatString, whisperType = WhisperPopup.WTSystem): + self.displayWhisper(aboutId, chatString, whisperType) + + def displayWhisper(self, fromId, chatString, whisperType): + print 'Whisper type %s from %s: %s' % (whisperType, fromId, chatString) + + def whisperSCTo(self, msgIndex, sendToId): + messenger.send('wakeup') + base.cr.ttsFriendsManager.d_whisperSCTo(sendToId, msgIndex) + + def setWhisperSCFrom(self, fromId, msgIndex): + handle = base.cr.identifyAvatar(fromId) + if handle == None or base.localAvatar.isIgnored(fromId): + return + chatString = SCDecoders.decodeSCStaticTextMsg(msgIndex) + if chatString: + self.displayWhisper(fromId, chatString, WhisperPopup.WTNormal) + return + + def whisperSCCustomTo(self, msgIndex, sendToId): + messenger.send('wakeup') + base.cr.ttsFriendsManager.d_whisperSCCustomTo(sendToId, msgIndex) + + def _isValidWhisperSource(self, source): + return True + + def setWhisperSCCustomFrom(self, fromId, msgIndex): + handle = base.cr.identifyAvatar(fromId) + if handle == None: + return + if not self._isValidWhisperSource(handle): + self.notify.warning('displayWhisper from non-toon %s' % fromId) + return + if base.localAvatar.isIgnored(fromId): + return + chatString = SCDecoders.decodeSCCustomMsg(msgIndex) + if chatString: + self.displayWhisper(fromId, chatString, WhisperPopup.WTNormal) + + def whisperSCEmoteTo(self, emoteId, sendToId): + messenger.send('wakeup') + base.cr.ttsFriendsManager.d_whisperSCEmoteTo(sendToId, emoteId) + + def setWhisperSCEmoteFrom(self, fromId, emoteId): + handle = base.cr.identifyAvatar(fromId) + if handle == None or base.localAvatar.isIgnored(fromId): + return + chatString = SCDecoders.decodeSCEmoteWhisperMsg(emoteId, handle.getName()) + if chatString: + self.displayWhisper(fromId, chatString, WhisperPopup.WTEmote) + return + + def setChatAbsolute(self, chatString, chatFlags, dialogue = None, interrupt = 1, quiet = 0): + DistributedAvatar.DistributedAvatar.setChatAbsolute(self, chatString, chatFlags, dialogue, interrupt) + if not quiet: + pass + + def setTalk(self, chat): + if not base.cr.chatAgent.verifyMessage(chat): + return + if base.localAvatar.isIgnored(self.doId): + return + if not self.understandable: + chat = self.chatGarbler.garble(self, len(chat.split(' '))) + elif base.whiteList and self.understandable < 2: + chat = base.whiteList.processThroughAll(chat, self, self.chatGarbler) + self.displayTalk(chat) + + def setTalkWhisper(self, avId, chat): + if not base.cr.chatAgent.verifyMessage(chat): + return + if base.localAvatar.isIgnored(avId): + return + if not self.understandable: + chat = self.chatGarbler.garble(self, len(chat.split(' '))) + elif base.whiteList and self.understandable < 2: + chat = base.whiteList.processThroughAll(chat, self.chatGarbler) + self.displayTalkWhisper(avId, chat) + + def displayTalk(self, chat): + print 'Talk: %s' % chat + + def displayTalkWhisper(self, avId, chat): + print 'TalkWhisper from %s: %s' % (avId, chat) + + def b_setSC(self, msgIndex): + self.setSC(msgIndex) + self.d_setSC(msgIndex) + + def d_setSC(self, msgIndex): + messenger.send('wakeup') + self.sendUpdate('setSC', [msgIndex]) + + def setSC(self, msgIndex): + if base.localAvatar.isIgnored(self.doId): + return + chatString = SCDecoders.decodeSCStaticTextMsg(msgIndex) + if chatString: + self.setChatAbsolute(chatString, CFSpeech | CFQuicktalker | CFTimeout, quiet=1) + + def b_setSCCustom(self, msgIndex): + self.setSCCustom(msgIndex) + self.d_setSCCustom(msgIndex) + + def d_setSCCustom(self, msgIndex): + messenger.send('wakeup') + self.sendUpdate('setSCCustom', [msgIndex]) + + def setSCCustom(self, msgIndex): + if base.localAvatar.isIgnored(self.doId): + return + chatString = SCDecoders.decodeSCCustomMsg(msgIndex) + if chatString: + self.setChatAbsolute(chatString, CFSpeech | CFQuicktalker | CFTimeout) + + def b_setSCEmote(self, emoteId): + self.b_setEmoteState(emoteId, animMultiplier=self.animMultiplier) + + def d_friendsNotify(self, avId, status): + self.sendUpdate('friendsNotify', [avId, status]) + + def friendsNotify(self, avId, status): + avatar = base.cr.identifyFriend(avId) + if avatar != None: + if status == 1: + self.setSystemMessage(avId, OTPLocalizer.WhisperNoLongerFriend % avatar.getName()) + elif status == 2: + self.setSystemMessage(avId, OTPLocalizer.WhisperNowSpecialFriend % avatar.getName()) + return + + def d_teleportQuery(self, requesterId, sendToId = None): + lastQuery = self.lastTeleportQuery + currentQuery = time.time() + + if currentQuery - lastQuery < 0.1: # Oh boy! We found a skid! + self.cr.stopReaderPollTask() + self.cr.lostConnection() + + self.lastTeleportQuery = time.time() + + base.cr.ttsFriendsManager.d_teleportQuery(sendToId) + + def teleportQuery(self, requesterId): + avatar = base.cr.identifyFriend(requesterId) + + if avatar is None: + self.d_teleportResponse(self.doId, 0, 0, 0, 0, sendToId=requesterId) + elif base.localAvatar.isIgnored(requesterId): + self.d_teleportResponse(self.doId, 2, 0, 0, 0, sendToId=requesterId) + elif hasattr(base, 'distributedParty') and ((base.distributedParty.partyInfo.isPrivate and requesterId not in base.distributedParty.inviteeIds) or base.distributedParty.isPartyEnding): + self.d_teleportResponse(self.doId, 0, 0, 0, 0, sendToId=requesterId) + elif self.__teleportAvailable and not self.ghostMode: + self.setSystemMessage(requesterId, OTPLocalizer.WhisperComingToVisit % avatar.getName()) + messenger.send('teleportQuery', [avatar, self]) + else: + self.setSystemMessage(requesterId, OTPLocalizer.WhisperFailedVisit % avatar.getName()) + self.d_teleportResponse(self.doId, 0, 0, 0, 0, sendToId=requesterId) + + def d_teleportResponse(self, avId, available, shardId, hoodId, zoneId, sendToId): + teleportNotify.debug('sending teleportResponse%s' % ((avId, available, + shardId, hoodId, zoneId, sendToId),) + ) + + base.cr.ttsFriendsManager.d_teleportResponse(sendToId, available, + shardId, hoodId, zoneId + ) + + def teleportResponse(self, avId, available, shardId, hoodId, zoneId): + teleportNotify.debug('received teleportResponse%s' % ((avId, available, + shardId, hoodId, zoneId),) + ) + + messenger.send('teleportResponse', [avId, available, shardId, hoodId, zoneId]) + + def d_teleportGiveup(self, requesterId, sendToId): + teleportNotify.debug('sending teleportGiveup(%s) to %s' % (requesterId, sendToId)) + + base.cr.ttsFriendsManager.d_teleportGiveup(sendToId) + + def teleportGiveup(self, requesterId): + teleportNotify.debug('received teleportGiveup(%s)' % (requesterId,)) + avatar = base.cr.identifyAvatar(requesterId) + + if not self._isValidWhisperSource(avatar): + self.notify.warning('teleportGiveup from non-toon %s' % requesterId) + return + + if avatar is not None: + self.setSystemMessage(requesterId, + OTPLocalizer.WhisperGiveupVisit % avatar.getName() + ) + + def b_teleportGreeting(self, avId): + if hasattr(self, 'ghostMode') and self.ghostMode: + return + self.d_teleportGreeting(avId) + self.teleportGreeting(avId) + + def d_teleportGreeting(self, avId): + self.sendUpdate('teleportGreeting', [avId]) + + def teleportGreeting(self, avId): + avatar = base.cr.getDo(avId) + if isinstance(avatar, Avatar.Avatar): + self.setChatAbsolute(OTPLocalizer.TeleportGreeting % avatar.getName(), CFSpeech | CFTimeout) + elif avatar is not None: + self.notify.warning('got teleportGreeting from %s referencing non-toon %s' % (self.doId, avId)) + return + + def setTeleportAvailable(self, available): + self.__teleportAvailable = available + + def getTeleportAvailable(self): + return self.__teleportAvailable + + def getFriendsList(self): + return self.friendsList + + def setFriendsList(self, friendsList): + self.friendsList = friendsList + messenger.send('friendsListChanged') + Avatar.reconsiderAllUnderstandable() + + def setDISLid(self, id): + self.DISLid = id + + def setAdminAccess(self, access): + self.adminAccess = access + self.considerUnderstandable() + + def getAdminAccess(self): + return self.adminAccess + + def isAdmin(self): + return self.adminAccess >= MINIMUM_MAGICWORD_ACCESS + + def setAutoRun(self, value): + self.autoRun = value + + def getAutoRun(self): + return self.autoRun diff --git a/otp/avatar/DistributedPlayerAI.py b/otp/avatar/DistributedPlayerAI.py new file mode 100755 index 00000000..788e6259 --- /dev/null +++ b/otp/avatar/DistributedPlayerAI.py @@ -0,0 +1,251 @@ +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.MsgTypes import CLIENTAGENT_EJECT + +from otp.ai.AIBaseGlobal import * +from otp.ai.MagicWordGlobal import * +from otp.avatar import DistributedAvatarAI +from otp.avatar import PlayerBase +from otp.distributed import OtpDoGlobals +from otp.otpbase import OTPLocalizer + +class DistributedPlayerAI(DistributedAvatarAI.DistributedAvatarAI, PlayerBase.PlayerBase): + + def __init__(self, air): + DistributedAvatarAI.DistributedAvatarAI.__init__(self, air) + self.friendsList = [] + self.DISLid = 0 + self.adminAccess = 0 + + def announceGenerate(self): + DistributedAvatarAI.DistributedAvatarAI.announceGenerate(self) + self._doPlayerEnter() + + def _announceArrival(self): + self.sendUpdate('arrivedOnDistrict', [self.air.districtId]) + + def _announceExit(self): + self.sendUpdate('arrivedOnDistrict', [0]) + + def _sendExitServerEvent(self): + self.air.writeServerEvent('avatarExit', self.doId, '') + + def delete(self): + self._doPlayerExit() + DistributedAvatarAI.DistributedAvatarAI.delete(self) + + def isPlayerControlled(self): + return True + + def setLocation(self, parentId, zoneId): + DistributedAvatarAI.DistributedAvatarAI.setLocation(self, parentId, zoneId) + if self.isPlayerControlled(): + if not self.air._isValidPlayerLocation(parentId, zoneId): + self.notify.info('booting player %s for doing setLocation to (%s, %s)' % (self.doId, parentId, zoneId)) + self.air.writeServerEvent('suspicious', self.doId, 'invalid setLocation: (%s, %s)' % (parentId, zoneId)) + self.requestDelete() + + def _doPlayerEnter(self): + self.incrementPopulation() + self._announceArrival() + + def _doPlayerExit(self): + self._announceExit() + self.decrementPopulation() + + def incrementPopulation(self): + self.air.incrementPopulation() + + def decrementPopulation(self): + simbase.air.decrementPopulation() + + def d_setMaxHp(self, maxHp): + DistributedAvatarAI.DistributedAvatarAI.d_setMaxHp(self, maxHp) + self.air.writeServerEvent('setMaxHp', self.doId, '%s' % maxHp) + + def d_setSystemMessage(self, aboutId, chatString): + self.sendUpdate('setSystemMessage', [aboutId, chatString]) + + def d_friendsNotify(self, avId, status): + self.sendUpdate('friendsNotify', [avId, status]) + + def friendsNotify(self, avId, status): + pass + + def setDISLid(self, id): + self.DISLid = id + + def getDISLid(self): + return self.DISLid + + def d_setFriendsList(self, friendsList): + self.sendUpdate('setFriendsList', [friendsList]) + + def setFriendsList(self, friendsList): + self.friendsList = friendsList + self.notify.debug('setting friends list to %s' % self.friendsList) + + def getFriendsList(self): + return self.friendsList + + def setAdminAccess(self, access): + self.adminAccess = access + + def d_setAdminAccess(self, access): + self.sendUpdate('setAdminAccess', [access]) + + def b_setAdminAccess(self, access): + self.setAdminAccess(access) + self.d_setAdminAccess(access) + + def getAdminAccess(self): + return self.adminAccess + + def isAdmin(self): + return self.adminAccess >= MINIMUM_MAGICWORD_ACCESS + + def extendFriendsList(self, friendId): + if friendId in self.friendsList: + return + + self.friendsList.append(friendId) + +@magicWord(category=CATEGORY_SYSTEM_ADMINISTRATOR, types=[str]) +def system(message): + """ + Broadcast a to the game server. + """ + message = 'ADMIN: ' + message + dclass = simbase.air.dclassesByName['ClientServicesManager'] + dg = dclass.aiFormatUpdate('systemMessage', + OtpDoGlobals.OTP_DO_ID_CLIENT_SERVICES_MANAGER, + 10, 1000000, [message]) + simbase.air.send(dg) + +@magicWord(category=CATEGORY_SYSTEM_ADMINISTRATOR, types=[int]) +def maintenance(minutes): + """ + Initiate the maintenance message sequence. It will last for the specified + amount of . + """ + def disconnect(task): + dg = PyDatagram() + dg.addServerHeader(10, simbase.air.ourChannel, CLIENTAGENT_EJECT) + dg.addUint16(154) + dg.addString('Toontown Stride is now closed for maintenance.') + simbase.air.send(dg) + return Task.done + + def countdown(minutes): + if minutes > 0: + system(OTPLocalizer.CRMaintenanceCountdownMessage % minutes) + else: + system(OTPLocalizer.CRMaintenanceMessage) + taskMgr.doMethodLater(10, disconnect, 'maintenance-disconnection') + if minutes <= 5: + next = 60 + minutes -= 1 + elif minutes % 5: + next = 60 * (minutes%5) + minutes -= minutes % 5 + else: + next = 300 + minutes -= 5 + if minutes >= 0: + taskMgr.doMethodLater(next, countdown, 'maintenance-task', + extraArgs=[minutes]) + + + countdown(minutes) + +@magicWord(category=CATEGORY_SYSTEM_ADMINISTRATOR, types=[str, str]) +def accessLevel(accessLevel, storage='PERSISTENT'): + """ + Modify the target's access level. + """ + accessName2Id = { + 'user': CATEGORY_USER.defaultAccess, + 'u': CATEGORY_USER.defaultAccess, + 'communitymanager': CATEGORY_COMMUNITY_MANAGER.defaultAccess, + 'community': CATEGORY_COMMUNITY_MANAGER.defaultAccess, + 'c': CATEGORY_COMMUNITY_MANAGER.defaultAccess, + 'moderator': CATEGORY_MODERATOR.defaultAccess, + 'mod': CATEGORY_MODERATOR.defaultAccess, + 'm': CATEGORY_MODERATOR.defaultAccess, + 'creative': CATEGORY_CREATIVE.defaultAccess, + 'creativity': CATEGORY_CREATIVE.defaultAccess, + 'c': CATEGORY_CREATIVE.defaultAccess, + 'programmer': CATEGORY_PROGRAMMER.defaultAccess, + 'coder': CATEGORY_PROGRAMMER.defaultAccess, + 'p': CATEGORY_PROGRAMMER.defaultAccess, + 'administrator': CATEGORY_ADMINISTRATOR.defaultAccess, + 'admin': CATEGORY_ADMINISTRATOR.defaultAccess, + 'a': CATEGORY_ADMINISTRATOR.defaultAccess, + 'systemadministrator': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess, + 'systemadmin': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess, + 'sysadministrator': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess, + 'sysadmin': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess, + 'system': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess, + 'sys': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess, + 's': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess + } + try: + accessLevel = int(accessLevel) + except: + if accessLevel not in accessName2Id: + return 'Invalid access level!' + accessLevel = accessName2Id[accessLevel] + if accessLevel not in accessName2Id.values(): + return 'Invalid access level!' + target = spellbook.getTarget() + invoker = spellbook.getInvoker() + if invoker == target: + return "You can't set your own access level!" + if not accessLevel < invoker.getAdminAccess(): + return "The target's access level must be lower than yours!" + if target.getAdminAccess() == accessLevel: + return "%s's access level is already %d!" % (target.getName(), accessLevel) + target.b_setAdminAccess(accessLevel) + temporary = storage.upper() in ('SESSION', 'TEMP', 'TEMPORARY') + if not temporary: + target.air.dbInterface.updateObject( + target.air.dbId, + target.getDISLid(), + target.air.dclassesByName['AccountAI'], + {'ADMIN_ACCESS': accessLevel}) + if not temporary: + target.d_setSystemMessage(0, '%s set your access level to %d!' % (invoker.getName(), accessLevel)) + return "%s's access level has been set to %d." % (target.getName(), accessLevel) + else: + target.d_setSystemMessage(0, '%s set your access level to %d temporarily!' % (invoker.getName(), accessLevel)) + return "%s's access level has been set to %d temporarily." % (target.getName(), accessLevel) + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def disableGM(): + """ + Temporarily disable GM features. + """ + target = spellbook.getTarget() + + if hasattr(target, 'oldAccess'): + return 'GM features are already disabled!\nTo enable, use ~enableGM.' + + if not target.isAdmin(): + return 'Target is not an admin!' + + target.oldAccess = target.adminAccess + target.d_setAdminAccess(100) + return 'GM features are disabled!' + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def enableGM(): + """ + Enable GM features. + """ + target = spellbook.getTarget() + + if not hasattr(target, 'oldAccess'): + return 'GM features are not disabled!' + + target.d_setAdminAccess(target.oldAccess) + del target.oldAccess + return 'GM features are enabled!' diff --git a/otp/avatar/Emote.py b/otp/avatar/Emote.py new file mode 100755 index 00000000..69e0301f --- /dev/null +++ b/otp/avatar/Emote.py @@ -0,0 +1,22 @@ +from otp.otpbase import OTPLocalizer +import types + +class Emote: + EmoteClear = -1 + EmoteEnableStateChanged = 'EmoteEnableStateChanged' + + def __init__(self): + self.emoteFunc = None + return + + def isEnabled(self, index): + if isinstance(index, types.StringType): + index = OTPLocalizer.EmoteFuncDict[index] + if self.emoteFunc == None: + return 0 + elif self.emoteFunc[index][1] == 0: + return 1 + return 0 + + +globalEmote = None diff --git a/otp/avatar/LocalAvatar.py b/otp/avatar/LocalAvatar.py new file mode 100755 index 00000000..b19d5672 --- /dev/null +++ b/otp/avatar/LocalAvatar.py @@ -0,0 +1,1177 @@ +from direct.controls import ControlManager +from direct.controls.GhostWalker import GhostWalker +from direct.controls.GravityWalker import GravityWalker +from direct.controls.ObserverWalker import ObserverWalker +from direct.controls.SwimWalker import SwimWalker +from direct.controls.TwoDWalker import TwoDWalker +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedSmoothNode +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from direct.showbase.InputStateGlobal import inputState +from direct.showbase.PythonUtil import * +from direct.task import Task +import math +from panda3d.core import * +import random +import webbrowser +import numbers +import DistributedAvatar +from otp.ai.MagicWordGlobal import * +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPLocalizer +from otp.nametag.NametagConstants import * +from otp.margins.WhisperPopup import * +from toontown.toonbase import ToontownGlobals + + +class LocalAvatar(DistributedAvatar.DistributedAvatar, DistributedSmoothNode.DistributedSmoothNode): + notify = DirectNotifyGlobal.directNotify.newCategory('LocalAvatar') + wantDevCameraPositions = base.config.GetBool('want-dev-camera-positions', 0) + wantMouse = base.config.GetBool('want-mouse', 0) + sleepTimeout = base.config.GetInt('sleep-timeout', 120) + __enableMarkerPlacement = base.config.GetBool('place-markers', 0) + + def __init__(self, cr, chatMgr, talkAssistant = None, passMessagesThrough = False): + try: + self.LocalAvatar_initialized + return + except: + pass + + self.LocalAvatar_initialized = 1 + DistributedAvatar.DistributedAvatar.__init__(self, cr) + DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr) + self.cTrav = CollisionTraverser('base.cTrav') + base.pushCTrav(self.cTrav) + self.cTrav.setRespectPrevTransform(1) + self.avatarControlsEnabled = 0 + self.controlManager = ControlManager.ControlManager(True, passMessagesThrough) + self.initializeCollisions() + self.initializeSmartCamera() + self.cameraPositions = [] + self.animMultiplier = 1.0 + self.runTimeout = 2.5 + self.customMessages = [] + self.chatMgr = chatMgr + base.talkAssistant = talkAssistant + self.teleportAllowed = 1 + self.lockedDown = 0 + self.isPageUp = 0 + self.isPageDown = 0 + self.soundRun = None + self.soundWalk = None + self.sleepFlag = 0 + self.isDisguised = 0 + self.movingFlag = 0 + self.preventCameraDisable = False + self.lastNeedH = None + self.accept('friendOnline', self.__friendOnline) + self.accept('friendOffline', self.__friendOffline) + self.accept('clickedWhisper', self.clickedWhisper) + self.sleepCallback = None + self.accept('wakeup', self.wakeUp) + self.jumpLandAnimFixTask = None + self.fov = settings['fov'] + self.accept('avatarMoving', self.clearPageUpDown) + self.showNametag2d() + self.setPickable(0) + + def setPreventCameraDisable(self, prevent): + self.preventCameraDisable = prevent + + def useSwimControls(self): + self.controlManager.use('swim', self) + + def useGhostControls(self): + self.controlManager.use('ghost', self) + + def useWalkControls(self): + self.controlManager.use('walk', self) + + def useTwoDControls(self): + self.controlManager.use('twoD', self) + + def isLockedDown(self): + return self.lockedDown + + def lock(self): + if self.lockedDown == 1: + self.notify.debug('lock() - already locked!') + self.lockedDown = 1 + + def unlock(self): + if self.lockedDown == 0: + self.notify.debug('unlock() - already unlocked!') + self.lockedDown = 0 + + def isInWater(self): + return self.getZ(render) <= 0.0 + + def isTeleportAllowed(self): + return self.teleportAllowed and not self.isDisguised + + def setTeleportAllowed(self, flag): + self.teleportAllowed = flag + self.refreshOnscreenButtons() + + def sendFriendsListEvent(self): + self.wakeUp() + messenger.send('openFriendsList') + + def delete(self): + try: + self.LocalAvatar_deleted + return + except: + self.LocalAvatar_deleted = 1 + + self.ignoreAll() + self.stopJumpLandTask() + taskMgr.remove('shadowReach') + base.popCTrav() + taskMgr.remove('posCamera') + self.disableAvatarControls() + self.stopTrackAnimToSpeed() + self.stopUpdateSmartCamera() + self.shutdownSmartCamera() + self.deleteCollisions() + self.controlManager.delete() + self.physControls = None + del self.controlManager + self.positionExaminer.delete() + del self.positionExaminer + taskMgr.remove(self.uniqueName('walkReturnTask')) + self.chatMgr.delete() + del self.chatMgr + del self.soundRun + del self.soundWalk + if hasattr(self, 'soundWhisper'): + del self.soundWhisper + DistributedAvatar.DistributedAvatar.delete(self) + return + + def shadowReach(self, state): + if base.localAvatar.shadowPlacer: + base.localAvatar.shadowPlacer.lifter.setReach(base.localAvatar.getAirborneHeight() + 4.0) + return Task.cont + + def wantLegacyLifter(self): + return False + + def setupControls(self, avatarRadius = 1.4, floorOffset = OTPGlobals.FloorOffset, reach = 4.0, wallBitmask = OTPGlobals.WallBitmask, floorBitmask = OTPGlobals.FloorBitmask, ghostBitmask = OTPGlobals.GhostBitmask): + walkControls = GravityWalker(legacyLifter=self.wantLegacyLifter()) + walkControls.setWallBitMask(wallBitmask) + walkControls.setFloorBitMask(floorBitmask) + walkControls.initializeCollisions(self.cTrav, self, avatarRadius, floorOffset, reach) + walkControls.setAirborneHeightFunc(self.getAirborneHeight) + self.controlManager.add(walkControls, 'walk') + self.physControls = walkControls + twoDControls = TwoDWalker() + twoDControls.setWallBitMask(wallBitmask) + twoDControls.setFloorBitMask(floorBitmask) + twoDControls.initializeCollisions(self.cTrav, self, avatarRadius, floorOffset, reach) + twoDControls.setAirborneHeightFunc(self.getAirborneHeight) + self.controlManager.add(twoDControls, 'twoD') + swimControls = SwimWalker() + swimControls.setWallBitMask(wallBitmask) + swimControls.setFloorBitMask(floorBitmask) + swimControls.initializeCollisions(self.cTrav, self, avatarRadius, floorOffset, reach) + swimControls.setAirborneHeightFunc(self.getAirborneHeight) + self.controlManager.add(swimControls, 'swim') + ghostControls = GhostWalker() + ghostControls.setWallBitMask(ghostBitmask) + ghostControls.setFloorBitMask(floorBitmask) + ghostControls.initializeCollisions(self.cTrav, self, avatarRadius, floorOffset, reach) + ghostControls.setAirborneHeightFunc(self.getAirborneHeight) + self.controlManager.add(ghostControls, 'ghost') + observerControls = ObserverWalker() + observerControls.setWallBitMask(ghostBitmask) + observerControls.setFloorBitMask(floorBitmask) + observerControls.initializeCollisions(self.cTrav, self, avatarRadius, floorOffset, reach) + observerControls.setAirborneHeightFunc(self.getAirborneHeight) + self.controlManager.add(observerControls, 'observer') + self.controlManager.use('walk', self) + self.controlManager.disable() + + def initializeCollisions(self): + self.setupControls() + + def deleteCollisions(self): + self.controlManager.deleteCollisions() + self.ignore('entero157') + del self.cTrav + + def initializeSmartCameraCollisions(self): + self.ccTrav = CollisionTraverser('LocalAvatar.ccTrav') + self.ccLine = CollisionSegment(0.0, 0.0, 0.0, 1.0, 0.0, 0.0) + self.ccLineNode = CollisionNode('ccLineNode') + self.ccLineNode.addSolid(self.ccLine) + self.ccLineNodePath = self.attachNewNode(self.ccLineNode) + self.ccLineBitMask = OTPGlobals.CameraBitmask + self.ccLineNode.setFromCollideMask(self.ccLineBitMask) + self.ccLineNode.setIntoCollideMask(BitMask32.allOff()) + self.camCollisionQueue = CollisionHandlerQueue() + self.ccTrav.addCollider(self.ccLineNodePath, self.camCollisionQueue) + self.ccSphere = CollisionSphere(0, 0, 0, 1) + self.ccSphereNode = CollisionNode('ccSphereNode') + self.ccSphereNode.addSolid(self.ccSphere) + self.ccSphereNodePath = base.camera.attachNewNode(self.ccSphereNode) + self.ccSphereNode.setFromCollideMask(OTPGlobals.CameraBitmask) + self.ccSphereNode.setIntoCollideMask(BitMask32.allOff()) + self.camPusher = CollisionHandlerPusher() + self.camPusher.addCollider(self.ccSphereNodePath, base.camera) + self.camPusher.setCenter(self) + self.ccPusherTrav = CollisionTraverser('LocalAvatar.ccPusherTrav') + self.ccSphere2 = self.ccSphere + self.ccSphereNode2 = CollisionNode('ccSphereNode2') + self.ccSphereNode2.addSolid(self.ccSphere2) + self.ccSphereNodePath2 = base.camera.attachNewNode(self.ccSphereNode2) + self.ccSphereNode2.setFromCollideMask(OTPGlobals.CameraBitmask) + self.ccSphereNode2.setIntoCollideMask(BitMask32.allOff()) + self.camPusher2 = CollisionHandlerPusher() + self.ccPusherTrav.addCollider(self.ccSphereNodePath2, self.camPusher2) + self.camPusher2.addCollider(self.ccSphereNodePath2, base.camera) + self.camPusher2.setCenter(self) + self.camFloorRayNode = self.attachNewNode('camFloorRayNode') + self.ccRay = CollisionRay(0.0, 0.0, 0.0, 0.0, 0.0, -1.0) + self.ccRayNode = CollisionNode('ccRayNode') + self.ccRayNode.addSolid(self.ccRay) + self.ccRayNodePath = self.camFloorRayNode.attachNewNode(self.ccRayNode) + self.ccRayBitMask = OTPGlobals.FloorBitmask + self.ccRayNode.setFromCollideMask(self.ccRayBitMask) + self.ccRayNode.setIntoCollideMask(BitMask32.allOff()) + self.ccTravFloor = CollisionTraverser('LocalAvatar.ccTravFloor') + self.camFloorCollisionQueue = CollisionHandlerQueue() + self.ccTravFloor.addCollider(self.ccRayNodePath, self.camFloorCollisionQueue) + self.ccTravOnFloor = CollisionTraverser('LocalAvatar.ccTravOnFloor') + self.ccRay2 = CollisionRay(0.0, 0.0, 0.0, 0.0, 0.0, -1.0) + self.ccRay2Node = CollisionNode('ccRay2Node') + self.ccRay2Node.addSolid(self.ccRay2) + self.ccRay2NodePath = self.camFloorRayNode.attachNewNode(self.ccRay2Node) + self.ccRay2BitMask = OTPGlobals.FloorBitmask + self.ccRay2Node.setFromCollideMask(self.ccRay2BitMask) + self.ccRay2Node.setIntoCollideMask(BitMask32.allOff()) + self.ccRay2MoveNodePath = hidden.attachNewNode('ccRay2MoveNode') + self.camFloorCollisionBroadcaster = CollisionHandlerFloor() + self.camFloorCollisionBroadcaster.setInPattern('on-floor') + self.camFloorCollisionBroadcaster.setOutPattern('off-floor') + self.camFloorCollisionBroadcaster.addCollider(self.ccRay2NodePath, self.ccRay2MoveNodePath) + + def deleteSmartCameraCollisions(self): + del self.ccTrav + del self.ccLine + del self.ccLineNode + self.ccLineNodePath.removeNode() + del self.ccLineNodePath + del self.camCollisionQueue + del self.ccRay + del self.ccRayNode + self.ccRayNodePath.removeNode() + del self.ccRayNodePath + del self.ccRay2 + del self.ccRay2Node + self.ccRay2NodePath.removeNode() + del self.ccRay2NodePath + self.ccRay2MoveNodePath.removeNode() + del self.ccRay2MoveNodePath + del self.ccTravOnFloor + del self.ccTravFloor + del self.camFloorCollisionQueue + del self.camFloorCollisionBroadcaster + del self.ccSphere + del self.ccSphereNode + self.ccSphereNodePath.removeNode() + del self.ccSphereNodePath + del self.camPusher + del self.ccPusherTrav + del self.ccSphere2 + del self.ccSphereNode2 + self.ccSphereNodePath2.removeNode() + del self.ccSphereNodePath2 + del self.camPusher2 + + def collisionsOff(self): + self.controlManager.collisionsOff() + + def collisionsOn(self): + self.controlManager.collisionsOn() + + def recalcCameraSphere(self): + nearPlaneDist = base.camLens.getNear() + hFov = base.camLens.getHfov() + vFov = base.camLens.getVfov() + hOff = nearPlaneDist * math.tan(deg2Rad(hFov / 2.0)) + vOff = nearPlaneDist * math.tan(deg2Rad(vFov / 2.0)) + camPnts = [Point3(hOff, nearPlaneDist, vOff), + Point3(-hOff, nearPlaneDist, vOff), + Point3(hOff, nearPlaneDist, -vOff), + Point3(-hOff, nearPlaneDist, -vOff), + Point3(0.0, 0.0, 0.0)] + avgPnt = Point3(0.0, 0.0, 0.0) + for camPnt in camPnts: + avgPnt = avgPnt + camPnt + + avgPnt = avgPnt / len(camPnts) + sphereRadius = 0.0 + for camPnt in camPnts: + dist = Vec3(camPnt - avgPnt).length() + if dist > sphereRadius: + sphereRadius = dist + + avgPnt = Point3(avgPnt) + self.ccSphereNodePath.setPos(avgPnt) + self.ccSphereNodePath2.setPos(avgPnt) + self.ccSphere.setRadius(sphereRadius) + + def putCameraFloorRayOnAvatar(self): + self.camFloorRayNode.setPos(self, 0, 0, 5) + + def putCameraFloorRayOnCamera(self): + self.camFloorRayNode.setPos(self.ccSphereNodePath, 0, 0, 0) + + def attachCamera(self): + camera.reparentTo(self) + base.enableMouse() + base.setMouseOnNode(self.node()) + self.ignoreMouse = not self.wantMouse + self.setWalkSpeedNormal() + + def detachCamera(self): + base.disableMouse() + + def stopJumpLandTask(self): + if self.jumpLandAnimFixTask: + self.jumpLandAnimFixTask.remove() + self.jumpLandAnimFixTask = None + return + + def jumpStart(self): + if not self.sleepFlag and self.hp > 0: + self.b_setAnimState('jumpAirborne', 1.0) + self.stopJumpLandTask() + + def returnToWalk(self, task): + if self.sleepFlag: + state = 'Sleep' + elif self.hp > 0: + state = 'Happy' + else: + state = 'Sad' + self.b_setAnimState(state, 1.0) + return Task.done + + if 1: + def jumpLandAnimFix(self, jumpTime): + if self.playingAnim != 'run' and self.playingAnim != 'walk': + return taskMgr.doMethodLater(jumpTime, self.returnToWalk, self.uniqueName('walkReturnTask')) + + def jumpHardLand(self): + if self.allowHardLand(): + self.b_setAnimState('jumpLand', 1.0) + self.stopJumpLandTask() + self.jumpLandAnimFixTask = self.jumpLandAnimFix(1.0) + if self.d_broadcastPosHpr: + self.d_broadcastPosHpr() + + def jumpLand(self): + self.jumpLandAnimFixTask = self.jumpLandAnimFix(0.01) + if self.d_broadcastPosHpr: + self.d_broadcastPosHpr() + + def setupAnimationEvents(self): + self.accept('jumpStart', self.jumpStart, []) + self.accept('jumpHardLand', self.jumpHardLand, []) + self.accept('jumpLand', self.jumpLand, []) + + def ignoreAnimationEvents(self): + self.ignore('jumpStart') + self.ignore('jumpHardLand') + self.ignore('jumpLand') + + def allowHardLand(self): + return not self.sleepFlag and self.hp > 0 + + def enableSmartCameraViews(self): + self.accept('tab', self.nextCameraPos, [1]) + self.accept('shift-tab', self.nextCameraPos, [0]) + self.accept('page_up', self.pageUp) + self.accept('page_down', self.pageDown) + + def disableSmartCameraViews(self): + self.ignore('tab') + self.ignore('shift-tab') + self.ignore('page_up') + self.ignore('page_down') + self.ignore('page_down-up') + + def enableAvatarControls(self): + if self.avatarControlsEnabled: + return + self.avatarControlsEnabled = 1 + self.setupAnimationEvents() + self.controlManager.enable() + + def disableAvatarControls(self): + if not self.avatarControlsEnabled: + return + self.avatarControlsEnabled = 0 + self.ignoreAnimationEvents() + self.controlManager.disable() + self.clearPageUpDown() + + def setWalkSpeedNormal(self): + self.controlManager.setSpeeds(OTPGlobals.ToonForwardSpeed, OTPGlobals.ToonJumpForce, OTPGlobals.ToonReverseSpeed, OTPGlobals.ToonRotateSpeed) + + def setWalkSpeedSlow(self): + self.controlManager.setSpeeds(OTPGlobals.ToonForwardSlowSpeed, OTPGlobals.ToonJumpSlowForce, OTPGlobals.ToonReverseSlowSpeed, OTPGlobals.ToonRotateSlowSpeed) + + def pageUp(self): + if not (self.avatarControlsEnabled or self.preventCameraDisable): + return + self.wakeUp() + if not self.isPageUp: + self.isPageDown = 0 + self.isPageUp = 1 + self.lerpCameraFov(70, 0.6) + self.setCameraPositionByIndex(self.cameraIndex) + else: + self.clearPageUpDown() + + def pageDown(self): + if not (self.avatarControlsEnabled and self.preventCameraDisable): + return + self.wakeUp() + if not self.isPageDown: + self.isPageUp = 0 + self.isPageDown = 1 + self.lerpCameraFov(70, 0.6) + self.setCameraPositionByIndex(self.cameraIndex) + else: + self.clearPageUpDown() + + def clearPageUpDown(self): + if self.isPageDown or self.isPageUp: + self.lerpCameraFov(self.fov, 0.6) + self.isPageDown = 0 + self.isPageUp = 0 + self.setCameraPositionByIndex(self.cameraIndex) + + def nextCameraPos(self, forward): + if not (self.avatarControlsEnabled or self.preventCameraDisable): + return + self.wakeUp() + self.__cameraHasBeenMoved = 1 + if forward: + self.cameraIndex += 1 + if self.cameraIndex > len(self.cameraPositions) - 1: + self.cameraIndex = 0 + else: + self.cameraIndex -= 1 + if self.cameraIndex < 0: + self.cameraIndex = len(self.cameraPositions) - 1 + self.setCameraPositionByIndex(self.cameraIndex) + + def setCameraPosition(self, index): + self.cameraIndex = index + self.setCameraPositionByIndex(index) + + def initCameraPositions(self): + camHeight = self.getClampedAvatarHeight() + heightScaleFactor = camHeight * 0.3333333333 + defLookAt = Point3(0.0, 1.5, camHeight) + scXoffset = 3.0 + scPosition = (Point3(scXoffset - 1, -10.0, camHeight + 5.0), Point3(scXoffset, 2.0, camHeight)) + self.cameraPositions = [(Point3(0.0, -9.0 * heightScaleFactor, camHeight), + defLookAt, + Point3(0.0, camHeight, camHeight * 4.0), + Point3(0.0, camHeight, camHeight * -1.0), + 0), + (Point3(0.0, 0.7, camHeight), + defLookAt, + Point3(0.0, camHeight, camHeight * 1.33), + Point3(0.0, camHeight, camHeight * 0.66), + 1), + (Point3(5.7 * heightScaleFactor, 7.65 * heightScaleFactor, camHeight + 2.0), + Point3(0.0, 1.0, camHeight), + Point3(0.0, 1.0, camHeight * 4.0), + Point3(0.0, 1.0, camHeight * -1.0), + 0), + (Point3(0.0, 8.65 * heightScaleFactor, camHeight), + Point3(0.0, 1.0, camHeight), + Point3(0.0, 1.0, camHeight * 4.0), + Point3(0.0, 1.0, camHeight * -1.0), + 0), + (Point3(-camHeight * 3, 0.0, camHeight), + Point3(0.0, 0.0, camHeight), + Point3(0.0, camHeight, camHeight * 1.1), + Point3(0.0, camHeight, camHeight * 0.9), + 1), + (Point3(camHeight * 3, 0.0, camHeight), + Point3(0.0, 0.0, camHeight), + Point3(0.0, camHeight, camHeight * 1.1), + Point3(0.0, camHeight, camHeight * 0.9), + 1), + (Point3(0.0, -24.0 * heightScaleFactor, camHeight + 4.0), + defLookAt, + Point3(0.0, 1.5, camHeight * 4.0), + Point3(0.0, 1.5, camHeight * -1.0), + 0), + (Point3(0.0, -12.0 * heightScaleFactor, camHeight + 4.0), + defLookAt, + Point3(0.0, 1.5, camHeight * 4.0), + Point3(0.0, 1.5, camHeight * -1.0), + 0)] + self.auxCameraPositions + if self.wantDevCameraPositions: + self.cameraPositions += [(Point3(0.0, 0.0, camHeight * 3), + Point3(0.0, 0.0, 0.0), + Point3(0.0, camHeight * 2, 0.0), + Point3(0.0, -camHeight * 2, 0.0), + 1), + (Point3(camHeight * 3, 0.0, camHeight), + Point3(0.0, 0.0, camHeight), + Point3(0.0, camHeight, camHeight * 1.1), + Point3(0.0, camHeight, camHeight * 0.9), + 1), + (Point3(camHeight * 3, 0.0, 0.0), + Point3(0.0, 0.0, camHeight), + Point3(0.0, camHeight, camHeight * 1.1), + Point3(0.0, camHeight, camHeight * 0.9), + 1), + (Point3(-camHeight * 3, 0.0, camHeight), + Point3(0.0, 0.0, camHeight), + Point3(0.0, camHeight, camHeight * 1.1), + Point3(0.0, camHeight, camHeight * 0.9), + 1), + (Point3(0.0, -60, 60), + defLookAt + Point3(0, 15, 0), + defLookAt + Point3(0, 15, 0), + defLookAt + Point3(0, 15, 0), + 1), + (Point3(0.0, -20, 20), + defLookAt + Point3(0, 5, 0), + defLookAt + Point3(0, 5, 0), + defLookAt + Point3(0, 5, 0), + 1)] + + def addCameraPosition(self, camPos = None): + if camPos == None: + lookAtNP = self.attachNewNode('lookAt') + lookAtNP.setPos(base.cam, 0, 1, 0) + lookAtPos = lookAtNP.getPos() + camHeight = self.getClampedAvatarHeight() + camPos = (base.cam.getPos(self), + lookAtPos, + Point3(0.0, 1.5, camHeight * 4.0), + Point3(0.0, 1.5, camHeight * -1.0), + 1) + lookAtNP.removeNode() + self.auxCameraPositions.append(camPos) + self.cameraPositions.append(camPos) + return + + def resetCameraPosition(self): + self.cameraIndex = 0 + self.setCameraPositionByIndex(self.cameraIndex) + + def removeCameraPosition(self): + if len(self.cameraPositions) > 1: + camPos = self.cameraPositions[self.cameraIndex] + if camPos in self.auxCameraPositions: + self.auxCameraPositions.remove(camPos) + if camPos in self.cameraPositions: + self.cameraPositions.remove(camPos) + self.nextCameraPos(1) + + def posCamera(self, lerp, time): + if not lerp: + self.positionCameraWithPusher(self.getCompromiseCameraPos(), self.getLookAtPoint()) + else: + camPos = self.getCompromiseCameraPos() + savePos = camera.getPos() + saveHpr = camera.getHpr() + self.positionCameraWithPusher(camPos, self.getLookAtPoint()) + x = camPos[0] + y = camPos[1] + z = camPos[2] + destHpr = camera.getHpr() + h = destHpr[0] + p = destHpr[1] + r = destHpr[2] + camera.setPos(savePos) + camera.setHpr(saveHpr) + taskMgr.remove('posCamera') + self.cameraLerp = LerpPosHprInterval(camera, time, Point3(x, y, z), Point3(h, p, r), other=self, name='posCamera') + self.cameraLerp.start() + + def getClampedAvatarHeight(self): + return max(self.getHeight(), 3.0) + + def getVisibilityPoint(self): + return Point3(0.0, 0.0, self.getHeight()) + + def setLookAtPoint(self, la): + self.__curLookAt = Point3(la) + + def getLookAtPoint(self): + return Point3(self.__curLookAt) + + def setIdealCameraPos(self, pos): + self.__idealCameraPos = Point3(pos) + self.updateSmartCameraCollisionLineSegment() + + def getIdealCameraPos(self): + return Point3(self.__idealCameraPos) + + def setCameraPositionByIndex(self, index): + self.notify.debug('switching to camera position %s' % index) + self.setCameraSettings(self.cameraPositions[index]) + + def setCameraPosForPetInteraction(self): + height = self.getClampedAvatarHeight() + point = Point3(height * (7 / 3.0), height * (-7 / 3.0), height) + self.prevIdealPos = self.getIdealCameraPos() + self.setIdealCameraPos(point) + self.posCamera(1, 0.7) + + def unsetCameraPosForPetInteraction(self): + if hasattr(self, 'prevIdealPos'): + self.setIdealCameraPos(self.prevIdealPos) + del self.prevIdealPos + self.posCamera(1, 0.7) + + def setCameraSettings(self, camSettings): + self.setIdealCameraPos(camSettings[0]) + if self.isPageUp and self.isPageDown or not self.isPageUp and not self.isPageDown: + self.__cameraHasBeenMoved = 1 + self.setLookAtPoint(camSettings[1]) + elif self.isPageUp: + self.__cameraHasBeenMoved = 1 + self.setLookAtPoint(camSettings[2]) + elif self.isPageDown: + self.__cameraHasBeenMoved = 1 + self.setLookAtPoint(camSettings[3]) + else: + self.notify.error('This case should be impossible.') + self.__disableSmartCam = camSettings[4] + if self.__disableSmartCam: + self.putCameraFloorRayOnAvatar() + self.cameraZOffset = 0.0 + + def getCompromiseCameraPos(self): + if self.__idealCameraObstructed == 0: + compromisePos = self.getIdealCameraPos() + else: + visPnt = self.getVisibilityPoint() + idealPos = self.getIdealCameraPos() + distance = Vec3(idealPos - visPnt).length() + ratio = self.closestObstructionDistance / distance + compromisePos = idealPos * ratio + visPnt * (1 - ratio) + liftMult = 1.0 - ratio * ratio + compromisePos = Point3(compromisePos[0], compromisePos[1], compromisePos[2] + self.getHeight() * 0.4 * liftMult) + compromisePos.setZ(compromisePos[2] + self.cameraZOffset) + return compromisePos + + def updateSmartCameraCollisionLineSegment(self): + pointB = self.getIdealCameraPos() + pointA = self.getVisibilityPoint() + vectorAB = Vec3(pointB - pointA) + lengthAB = vectorAB.length() + if lengthAB > 0.001: + self.ccLine.setPointA(pointA) + self.ccLine.setPointB(pointB) + + def initializeSmartCamera(self): + self.__idealCameraObstructed = 0 + self.closestObstructionDistance = 0.0 + self.cameraIndex = 0 + self.auxCameraPositions = [] + self.cameraZOffset = 0.0 + self.__onLevelGround = 0 + self.__camCollCanMove = 0 + self.__geom = render + self.__disableSmartCam = 0 + self.initializeSmartCameraCollisions() + self._smartCamEnabled = False + + def shutdownSmartCamera(self): + self.deleteSmartCameraCollisions() + + def setOnLevelGround(self, flag): + self.__onLevelGround = flag + + def setCameraCollisionsCanMove(self, flag): + self.__camCollCanMove = flag + + def setGeom(self, geom): + self.__geom = geom + + def startUpdateSmartCamera(self, push = 1): + if self._smartCamEnabled: + LocalAvatar.notify.warning('redundant call to startUpdateSmartCamera') + return + self._smartCamEnabled = True + self.__floorDetected = 0 + self.__cameraHasBeenMoved = 0 + self.recalcCameraSphere() + self.initCameraPositions() + self.setCameraPositionByIndex(self.cameraIndex) + self.posCamera(0, 0.0) + self.__instantaneousCamPos = camera.getPos() + if push: + self.cTrav.addCollider(self.ccSphereNodePath, self.camPusher) + self.ccTravOnFloor.addCollider(self.ccRay2NodePath, self.camFloorCollisionBroadcaster) + self.__disableSmartCam = 0 + else: + self.__disableSmartCam = 1 + self.__lastPosWrtRender = camera.getPos(render) + self.__lastHprWrtRender = camera.getHpr(render) + taskName = self.taskName('updateSmartCamera') + taskMgr.remove(taskName) + taskMgr.add(self.updateSmartCamera, taskName, priority=47) + self.enableSmartCameraViews() + + def stopUpdateSmartCamera(self): + if not self._smartCamEnabled: + LocalAvatar.notify.warning('redundant call to stopUpdateSmartCamera') + return + self.disableSmartCameraViews() + self.cTrav.removeCollider(self.ccSphereNodePath) + self.ccTravOnFloor.removeCollider(self.ccRay2NodePath) + if not base.localAvatar.isEmpty(): + self.putCameraFloorRayOnAvatar() + taskName = self.taskName('updateSmartCamera') + taskMgr.remove(taskName) + self._smartCamEnabled = False + + def updateSmartCamera(self, task): + if not self.__camCollCanMove and not self.__cameraHasBeenMoved: + if self.__lastPosWrtRender == camera.getPos(render): + if self.__lastHprWrtRender == camera.getHpr(render): + return Task.cont + self.__cameraHasBeenMoved = 0 + self.__lastPosWrtRender = camera.getPos(render) + self.__lastHprWrtRender = camera.getHpr(render) + self.__idealCameraObstructed = 0 + if not self.__disableSmartCam: + self.ccTrav.traverse(self.__geom) + if self.camCollisionQueue.getNumEntries() > 0: + try: + self.camCollisionQueue.sortEntries() + self.handleCameraObstruction(self.camCollisionQueue.getEntry(0)) + except AssertionError: # FIXME: Hacky. + pass + if not self.__onLevelGround: + self.handleCameraFloorInteraction() + if not self.__idealCameraObstructed: + self.nudgeCamera() + if not self.__disableSmartCam: + self.ccPusherTrav.traverse(self.__geom) + self.putCameraFloorRayOnCamera() + self.ccTravOnFloor.traverse(self.__geom) + return Task.cont + + def positionCameraWithPusher(self, pos, lookAt): + camera.setPos(pos) + self.ccPusherTrav.traverse(self.__geom) + camera.lookAt(lookAt) + + def nudgeCamera(self): + CLOSE_ENOUGH = 0.1 + curCamPos = self.__instantaneousCamPos + curCamHpr = camera.getHpr() + targetCamPos = self.getCompromiseCameraPos() + targetCamLookAt = self.getLookAtPoint() + posDone = 0 + if Vec3(curCamPos - targetCamPos).length() <= CLOSE_ENOUGH: + camera.setPos(targetCamPos) + posDone = 1 + camera.setPos(targetCamPos) + camera.lookAt(targetCamLookAt) + targetCamHpr = camera.getHpr() + hprDone = 0 + if Vec3(curCamHpr - targetCamHpr).length() <= CLOSE_ENOUGH: + hprDone = 1 + if posDone and hprDone: + return + lerpRatio = 0.15 + lerpRatio = 1 - pow(1 - lerpRatio, globalClock.getDt() * 30.0) + self.__instantaneousCamPos = targetCamPos * lerpRatio + curCamPos * (1 - lerpRatio) + if self.__disableSmartCam or not self.__idealCameraObstructed: + newHpr = targetCamHpr * lerpRatio + curCamHpr * (1 - lerpRatio) + else: + newHpr = targetCamHpr + camera.setPos(self.__instantaneousCamPos) + camera.setHpr(newHpr) + + def popCameraToDest(self): + newCamPos = self.getCompromiseCameraPos() + newCamLookAt = self.getLookAtPoint() + self.positionCameraWithPusher(newCamPos, newCamLookAt) + self.__instantaneousCamPos = camera.getPos() + + def handleCameraObstruction(self, camObstrCollisionEntry): + collisionPoint = camObstrCollisionEntry.getSurfacePoint(self.ccLineNodePath) + collisionVec = Vec3(collisionPoint - self.ccLine.getPointA()) + distance = collisionVec.length() + self.__idealCameraObstructed = 1 + self.closestObstructionDistance = distance + self.popCameraToDest() + + def handleCameraFloorInteraction(self): + self.putCameraFloorRayOnCamera() + self.ccTravFloor.traverse(self.__geom) + if self.__onLevelGround: + return + if self.camFloorCollisionQueue.getNumEntries() == 0: + return + self.camFloorCollisionQueue.sortEntries() + camObstrCollisionEntry = self.camFloorCollisionQueue.getEntry(0) + camHeightFromFloor = camObstrCollisionEntry.getSurfacePoint(self.ccRayNodePath)[2] + self.cameraZOffset = camera.getPos()[2] + camHeightFromFloor + if self.cameraZOffset < 0: + self.cameraZOffset = 0 + if self.__floorDetected == 0: + self.__floorDetected = 1 + self.popCameraToDest() + + def lerpCameraFov(self, fov, time): + taskMgr.remove('cam-fov-lerp-play') + oldFov = base.camLens.getHfov() + if abs(fov - oldFov) > 0.1: + + def setCamFov(fov): + base.camLens.setMinFov(fov/(4./3.)) + + self.camLerpInterval = LerpFunctionInterval(setCamFov, fromData=oldFov, toData=fov, duration=time, name='cam-fov-lerp') + self.camLerpInterval.start() + + def setCameraFov(self, fov): + self.fov = fov + if not (self.isPageDown or self.isPageUp): + base.camLens.setMinFov(self.fov/(4./3.)) + + def gotoNode(self, node, eyeHeight = 3): + possiblePoints = (Point3(3, 6, 0), + Point3(-3, 6, 0), + Point3(6, 6, 0), + Point3(-6, 6, 0), + Point3(3, 9, 0), + Point3(-3, 9, 0), + Point3(6, 9, 0), + Point3(-6, 9, 0), + Point3(9, 9, 0), + Point3(-9, 9, 0), + Point3(6, 0, 0), + Point3(-6, 0, 0), + Point3(6, 3, 0), + Point3(-6, 3, 0), + Point3(9, 9, 0), + Point3(-9, 9, 0), + Point3(0, 12, 0), + Point3(3, 12, 0), + Point3(-3, 12, 0), + Point3(6, 12, 0), + Point3(-6, 12, 0), + Point3(9, 12, 0), + Point3(-9, 12, 0), + Point3(0, -6, 0), + Point3(-3, -6, 0), + Point3(0, -9, 0), + Point3(-6, -9, 0)) + for point in possiblePoints: + pos = self.positionExaminer.consider(node, point, eyeHeight) + if pos: + self.setPos(node, pos) + self.lookAt(node) + self.setHpr(self.getH() + random.choice((-10, 10)), 0, 0) + return + + self.setPos(node, 0, 0, 0) + + def setCustomMessages(self, customMessages): + self.customMessages = customMessages + messenger.send('customMessagesChanged') + + def displayWhisper(self, fromId, chatString, whisperType): + sender = None + + if isinstance(fromId, numbers.Number): + sender = base.cr.identifyAvatar(fromId) + + if whisperType == WTNormal: + chatString = '%s: %s' % (sender.getName(), chatString) + + whisper = WhisperPopup(chatString, OTPGlobals.getInterfaceFont(), whisperType) + + if sender or isinstance(fromId, basestring): + whisper.setClickable(fromId) + + whisper.manage(base.marginManager) + base.playSfx(self.soundSystemMessage if whisperType == WTSystem else self.soundWhisper) + + def setAnimMultiplier(self, value): + self.animMultiplier = value + + def getAnimMultiplier(self): + return self.animMultiplier + + def enableRun(self): + self.accept('arrow_up', self.startRunWatch) + self.accept('arrow_up-up', self.stopRunWatch) + self.accept('control-arrow_up', self.startRunWatch) + self.accept('control-arrow_up-up', self.stopRunWatch) + self.accept('alt-arrow_up', self.startRunWatch) + self.accept('alt-arrow_up-up', self.stopRunWatch) + self.accept('shift-arrow_up', self.startRunWatch) + self.accept('shift-arrow_up-up', self.stopRunWatch) + + def disableRun(self): + self.ignore('arrow_up') + self.ignore('arrow_up-up') + self.ignore('control-arrow_up') + self.ignore('control-arrow_up-up') + self.ignore('alt-arrow_up') + self.ignore('alt-arrow_up-up') + self.ignore('shift-arrow_up') + self.ignore('shift-arrow_up-up') + + def startRunWatch(self): + + def setRun(ignored): + messenger.send('running-on') + + taskMgr.doMethodLater(self.runTimeout, setRun, self.uniqueName('runWatch')) + return Task.cont + + def stopRunWatch(self): + taskMgr.remove(self.uniqueName('runWatch')) + messenger.send('running-off') + return Task.cont + + def runSound(self): + self.soundWalk.stop() + base.playSfx(self.soundRun, looping=1) + + def walkSound(self): + self.soundRun.stop() + base.playSfx(self.soundWalk, looping=1) + + def stopSound(self): + self.soundRun.stop() + self.soundWalk.stop() + + def wakeUp(self): + if self.sleepCallback != None: + taskMgr.remove(self.uniqueName('sleepwatch')) + self.startSleepWatch(self.sleepCallback) + self.lastMoved = globalClock.getFrameTime() + if self.sleepFlag: + self.sleepFlag = 0 + return + + def gotoSleep(self): + if not self.sleepFlag: + self.b_setAnimState('Sleep', self.animMultiplier) + self.sleepFlag = 1 + + def forceGotoSleep(self): + if self.hp > 0: + self.sleepFlag = 0 + self.gotoSleep() + + def startSleepWatch(self, callback): + self.sleepCallback = callback + taskMgr.doMethodLater(self.sleepTimeout, callback, self.uniqueName('sleepwatch')) + + def stopSleepWatch(self): + taskMgr.remove(self.uniqueName('sleepwatch')) + self.sleepCallback = None + return + + def trackAnimToSpeed(self, task): + speed, rotSpeed, slideSpeed = self.controlManager.getSpeeds() + if speed != 0.0 or rotSpeed != 0.0 or inputState.isSet('jump'): + if not self.movingFlag: + self.movingFlag = 1 + self.stopLookAround() + elif self.movingFlag: + self.movingFlag = 0 + self.startLookAround() + if self.movingFlag or self.hp <= 0: + self.wakeUp() + elif not self.sleepFlag: + now = globalClock.getFrameTime() + if now - self.lastMoved > self.sleepTimeout: + self.gotoSleep() + state = None + if self.sleepFlag: + state = 'Sleep' + elif self.hp > 0: + state = 'Happy' + else: + state = 'Sad' + if state != self.lastState: + self.lastState = state + self.b_setAnimState(state, self.animMultiplier) + if state == 'Sad': + self.setWalkSpeedSlow() + else: + self.setWalkSpeedNormal() + if self.cheesyEffect == OTPGlobals.CEFlatProfile or self.cheesyEffect == OTPGlobals.CEFlatPortrait: + needH = None + if rotSpeed > 0.0: + needH = -10 + elif rotSpeed < 0.0: + needH = 10 + elif speed != 0.0: + needH = 0 + if needH != None and self.lastNeedH != needH: + node = self.getGeomNode().getChild(0) + lerp = Sequence(LerpHprInterval(node, 0.5, Vec3(needH, 0, 0), blendType='easeInOut'), name='cheesy-lerp-hpr', autoPause=1) + lerp.start() + self.lastNeedH = needH + else: + self.lastNeedH = None + action = self.setSpeed(speed, rotSpeed) + if action != self.lastAction: + self.lastAction = action + if self.emoteTrack: + self.emoteTrack.finish() + self.emoteTrack = None + if action == OTPGlobals.WALK_INDEX or action == OTPGlobals.REVERSE_INDEX: + self.walkSound() + elif action == OTPGlobals.RUN_INDEX: + self.runSound() + else: + self.stopSound() + return Task.cont + + def hasTrackAnimToSpeed(self): + taskName = self.taskName('trackAnimToSpeed') + return taskMgr.hasTaskNamed(taskName) + + def startTrackAnimToSpeed(self): + taskName = self.taskName('trackAnimToSpeed') + taskMgr.remove(taskName) + task = Task.Task(self.trackAnimToSpeed) + self.lastMoved = globalClock.getFrameTime() + self.lastState = None + self.lastAction = None + self.trackAnimToSpeed(task) + taskMgr.add(self.trackAnimToSpeed, taskName, 35) + return + + def stopTrackAnimToSpeed(self): + taskName = self.taskName('trackAnimToSpeed') + taskMgr.remove(taskName) + self.stopSound() + + def startChat(self): + self.chatMgr.start() + self.accept(OTPGlobals.ThinkPosHotkey, self.thinkPos) + if self.__enableMarkerPlacement: + self.accept(OTPGlobals.PlaceMarkerHotkey, self.__placeMarker) + + def stopChat(self): + self.chatMgr.stop() + self.ignore(OTPGlobals.ThinkPosHotkey) + if self.__enableMarkerPlacement: + self.ignore(OTPGlobals.PlaceMarkerHotkey) + + def d_broadcastPositionNow(self): + self.d_clearSmoothing() + self.d_broadcastPosHpr() + + def travCollisionsLOS(self, n = None): + if n == None: + n = self.__geom + self.ccTrav.traverse(n) + return + + def travCollisionsFloor(self, n = None): + if n == None: + n = self.__geom + self.ccTravFloor.traverse(n) + return + + def travCollisionsPusher(self, n = None): + if n == None: + n = self.__geom + self.ccPusherTrav.traverse(n) + return + + def __friendOnline(self, doId): + friend = base.cr.identifyFriend(doId) + if friend != None: + self.setSystemMessage(doId, OTPLocalizer.WhisperFriendComingOnline % friend.getName()) + + def __friendOffline(self, doId): + friend = base.cr.identifyFriend(doId) + if friend != None: + self.setSystemMessage(0, OTPLocalizer.WhisperFriendLoggedOut % friend.getName()) + + def clickedWhisper(self, doId): + if isinstance(doId, basestring): + webbrowser.open(doId, new=2, autoraise=True) + return + + friend = base.cr.identifyFriend(doId) + + if friend != None: + messenger.send('clickedNametag', [friend]) + self.chatMgr.whisperTo(friend.getName(), doId) + + def d_setParent(self, parentToken): + DistributedSmoothNode.DistributedSmoothNode.d_setParent(self, parentToken) + + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def run(): + """ + Toggles debugging run speed. + """ + inputState.set('debugRunning', inputState.isSet('debugRunning') != True) + return 'Toggled debug run speed.' + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def collisionsOff(): + """ + Turns collisions off. + """ + base.localAvatar.collisionsOff() + return 'Collisions are disabled.' + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def collisionsOn(): + """ + Turns collisions on. + """ + base.localAvatar.collisionsOn() + return 'Collisions are enabled.' + +@magicWord(category=CATEGORY_ADMINISTRATOR, types=[int]) +def gravity(value): + """ + Modifies the invoker's gravity. For default, use 0. + """ + if value < 0: + return 'Invalid gravity value!' + if value == 0: + base.localAvatar.controlManager.currentControls.setGravity(ToontownGlobals.GravityValue * 2.0) + else: + base.localAvatar.controlManager.currentControls.setGravity(value) + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[float, float, float]) +def xyz(x, y, z): + """ + Modifies the position of the invoker. + """ + base.localAvatar.setPos(x, y, z) + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[float, float, float]) +def hpr(h, p, r): + """ + Modifies the rotation of the invoker. + """ + base.localAvatar.setHpr(h, p, r) diff --git a/otp/avatar/PlayerBase.py b/otp/avatar/PlayerBase.py new file mode 100755 index 00000000..6b54496a --- /dev/null +++ b/otp/avatar/PlayerBase.py @@ -0,0 +1,7 @@ +class PlayerBase: + + def atLocation(self, locationId): + return True + + def getLocation(self): + return [] diff --git a/otp/avatar/PositionExaminer.py b/otp/avatar/PositionExaminer.py new file mode 100755 index 00000000..5ab5ba3d --- /dev/null +++ b/otp/avatar/PositionExaminer.py @@ -0,0 +1,94 @@ +from panda3d.core import * +from direct.showbase.DirectObject import DirectObject +from otp.otpbase import OTPGlobals + +class PositionExaminer(DirectObject, NodePath): + + def __init__(self): + try: + self.__initialized + return + except: + self.__initialized = 1 + + NodePath.__init__(self, hidden.attachNewNode('PositionExaminer')) + self.cRay = CollisionRay(0.0, 0.0, 6.0, 0.0, 0.0, -1.0) + self.cRayNode = CollisionNode('cRayNode') + self.cRayNode.addSolid(self.cRay) + self.cRayNodePath = self.attachNewNode(self.cRayNode) + self.cRayNodePath.hide() + self.cRayBitMask = OTPGlobals.FloorBitmask + self.cRayNode.setFromCollideMask(self.cRayBitMask) + self.cRayNode.setIntoCollideMask(BitMask32.allOff()) + self.cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.5) + self.cSphereNode = CollisionNode('cSphereNode') + self.cSphereNode.addSolid(self.cSphere) + self.cSphereNodePath = self.attachNewNode(self.cSphereNode) + self.cSphereNodePath.hide() + self.cSphereBitMask = OTPGlobals.WallBitmask + self.cSphereNode.setFromCollideMask(self.cSphereBitMask) + self.cSphereNode.setIntoCollideMask(BitMask32.allOff()) + self.ccLine = CollisionSegment(0.0, 0.0, 0.0, 1.0, 0.0, 0.0) + self.ccLineNode = CollisionNode('ccLineNode') + self.ccLineNode.addSolid(self.ccLine) + self.ccLineNodePath = self.attachNewNode(self.ccLineNode) + self.ccLineNodePath.hide() + self.ccLineBitMask = OTPGlobals.CameraBitmask + self.ccLineNode.setFromCollideMask(self.ccLineBitMask) + self.ccLineNode.setIntoCollideMask(BitMask32.allOff()) + self.cRayTrav = CollisionTraverser('PositionExaminer.cRayTrav') + self.cRayTrav.setRespectPrevTransform(False) + self.cRayQueue = CollisionHandlerQueue() + self.cRayTrav.addCollider(self.cRayNodePath, self.cRayQueue) + self.cSphereTrav = CollisionTraverser('PositionExaminer.cSphereTrav') + self.cSphereTrav.setRespectPrevTransform(False) + self.cSphereQueue = CollisionHandlerQueue() + self.cSphereTrav.addCollider(self.cSphereNodePath, self.cSphereQueue) + self.ccLineTrav = CollisionTraverser('PositionExaminer.ccLineTrav') + self.ccLineTrav.setRespectPrevTransform(False) + self.ccLineQueue = CollisionHandlerQueue() + self.ccLineTrav.addCollider(self.ccLineNodePath, self.ccLineQueue) + + def delete(self): + del self.cRay + del self.cRayNode + self.cRayNodePath.removeNode() + del self.cRayNodePath + del self.cSphere + del self.cSphereNode + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + del self.ccLine + del self.ccLineNode + self.ccLineNodePath.removeNode() + del self.ccLineNodePath + del self.cRayTrav + del self.cRayQueue + del self.cSphereTrav + del self.cSphereQueue + del self.ccLineTrav + del self.ccLineQueue + + def consider(self, node, pos, eyeHeight): + self.reparentTo(node) + self.setPos(pos) + result = None + self.cRayTrav.traverse(render) + if self.cRayQueue.getNumEntries() != 0: + self.cRayQueue.sortEntries() + floorPoint = self.cRayQueue.getEntry(0).getSurfacePoint(self.cRayNodePath) + if abs(floorPoint[2]) <= 4.0: + pos += floorPoint + self.setPos(pos) + self.cSphereTrav.traverse(render) + if self.cSphereQueue.getNumEntries() == 0: + self.ccLine.setPointA(0, 0, eyeHeight) + self.ccLine.setPointB(-pos[0], -pos[1], eyeHeight) + self.ccLineTrav.traverse(render) + if self.ccLineQueue.getNumEntries() == 0: + result = pos + self.reparentTo(hidden) + self.cRayQueue.clearEntries() + self.cSphereQueue.clearEntries() + self.ccLineQueue.clearEntries() + return result diff --git a/otp/avatar/ShadowCaster.py b/otp/avatar/ShadowCaster.py new file mode 100755 index 00000000..90bf23f3 --- /dev/null +++ b/otp/avatar/ShadowCaster.py @@ -0,0 +1,128 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.ShadowPlacer import ShadowPlacer +from panda3d.core import * + +from otp.otpbase import OTPGlobals + + +globalDropShadowFlag = 1 +def setGlobalDropShadowFlag(flag): + global globalDropShadowFlag + if flag != globalDropShadowFlag: + globalDropShadowFlag = flag + messenger.send('globalDropShadowFlagChanged') + + +globalDropShadowGrayLevel = 0.5 +def setGlobalDropShadowGrayLevel(grayLevel): + global globalDropShadowGrayLevel + if grayLevel != globalDropShadowGrayLevel: + globalDropShadowGrayLevel = grayLevel + messenger.send('globalDropShadowGrayLevelChanged') + + +class ShadowCaster: + notify = DirectNotifyGlobal.directNotify.newCategory('ShadowCaster') + + def __init__(self, squareShadow = False): + if squareShadow: + self.shadowFileName = 'phase_3/models/props/square_drop_shadow' + else: + self.shadowFileName = 'phase_3/models/props/drop_shadow' + self.dropShadow = None + self.shadowPlacer = None + self.activeShadow = 0 + self.wantsActive = 1 + self.storedActiveState = 0 + if hasattr(base, 'wantDynamicShadows') and base.wantDynamicShadows: + messenger.accept('globalDropShadowFlagChanged', self, self.__globalDropShadowFlagChanged) + messenger.accept('globalDropShadowGrayLevelChanged', self, self.__globalDropShadowGrayLevelChanged) + + def delete(self): + if hasattr(base, 'wantDynamicShadows') and base.wantDynamicShadows: + messenger.ignore('globalDropShadowFlagChanged', self) + messenger.ignore('globalDropShadowGrayLevelChanged', self) + self.deleteDropShadow() + self.shadowJoint = None + + def initializeDropShadow(self, hasGeomNode = True): + self.deleteDropShadow() + if hasGeomNode: + self.getGeomNode().setTag('cam', 'caster') + dropShadow = loader.loadModel(self.shadowFileName) + dropShadow.setScale(0.4) + dropShadow.flattenMedium() + dropShadow.setBillboardAxis(2) + dropShadow.setColor(0.0, 0.0, 0.0, globalDropShadowGrayLevel, 1) + self.shadowPlacer = ShadowPlacer(base.shadowTrav, dropShadow, OTPGlobals.WallBitmask, OTPGlobals.FloorBitmask) + self.dropShadow = dropShadow + if not globalDropShadowFlag: + self.dropShadow.hide() + if self.getShadowJoint(): + dropShadow.reparentTo(self.getShadowJoint()) + else: + self.dropShadow.hide() + self.setActiveShadow(self.wantsActive) + self.__globalDropShadowFlagChanged() + self.__globalDropShadowGrayLevelChanged() + + def update(self): + pass + + def deleteDropShadow(self): + if self.shadowPlacer: + self.shadowPlacer.delete() + self.shadowPlacer = None + if self.dropShadow: + self.dropShadow.removeNode() + self.dropShadow = None + + def setActiveShadow(self, isActive = 1): + isActive = isActive and self.wantsActive + if not globalDropShadowFlag: + self.storedActiveState = isActive + if self.shadowPlacer != None: + isActive = isActive and globalDropShadowFlag + if self.activeShadow != isActive: + self.activeShadow = isActive + if isActive: + self.shadowPlacer.on() + else: + self.shadowPlacer.off() + + def setShadowHeight(self, shadowHeight): + if self.dropShadow: + self.dropShadow.setZ(-shadowHeight) + + def getShadowJoint(self): + if hasattr(self, 'shadowJoint'): + return self.shadowJoint + shadowJoint = self.find('**/attachShadow') + if shadowJoint.isEmpty(): + self.shadowJoint = NodePath(self) + else: + self.shadowJoint = shadowJoint + return self.shadowJoint + + def hideShadow(self): + self.dropShadow.hide() + + def showShadow(self): + if not globalDropShadowFlag: + self.dropShadow.hide() + else: + self.dropShadow.show() + + def __globalDropShadowFlagChanged(self): + if self.dropShadow != None: + if globalDropShadowFlag == 0: + if self.activeShadow == 1: + self.storedActiveState = 1 + self.setActiveShadow(0) + elif self.activeShadow == 0: + self.setActiveShadow(1) + self.showShadow() + + def __globalDropShadowGrayLevelChanged(self): + if self.dropShadow != None: + self.dropShadow.setColor(0.0, 0.0, 0.0, globalDropShadowGrayLevel, 1) diff --git a/otp/avatar/__init__.py b/otp/avatar/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/otp/chat/ChatAgent.py b/otp/chat/ChatAgent.py new file mode 100755 index 00000000..37d69a2a --- /dev/null +++ b/otp/chat/ChatAgent.py @@ -0,0 +1,52 @@ +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal +from otp.ai.MagicWordGlobal import * +from otp.avatar import Avatar + +class ChatAgent(DistributedObjectGlobal): + + def __init__(self, cr): + DistributedObjectGlobal.__init__(self, cr) + self.cr.chatAgent = self + self.chatMode = 0 + + def delete(self): + self.ignoreAll() + self.cr.chatAgent = None + DistributedObjectGlobal.delete(self) + + def verifyMessage(self, message): + try: + message.decode('ascii') + return True + except: + return False + + def sendChatMessage(self, message): + if not self.verifyMessage(message): + return + self.sendUpdate('chatMessage', [message, self.chatMode]) + +@magicWord(category=CATEGORY_MODERATOR, types=[int]) +def chatmode(mode=-1): + """ Set the chat mode of the current avatar. """ + mode2name = { + 0 : "user", + 1 : "moderator", + 2 : "administrator", + 3 : "system administrator", + } + if base.cr.chatAgent is None: + return "No ChatAgent found." + if mode == -1: + return "You are currently talking in the %s chat mode." % mode2name.get(base.cr.chatAgent.chatMode, "N/A") + if not 0 <= mode <= 3: + return "Invalid chat mode specified." + if mode == 3 and spellbook.getInvoker().getAdminAccess() < 500: + return "Chat mode 3 is reserved for system administrators." + if mode == 2 and spellbook.getInvoker().getAdminAccess() < 400: + return "Chat mode 2 is reserved for administrators." + if mode == 1 and spellbook.getInvoker().getAdminAccess() < 200: + # Like this will ever happen, but whatever. + return "Chat mode 1 is reserved for moderators." + base.cr.chatAgent.chatMode = mode + return "You are now talking in the %s chat mode." % mode2name.get(mode, "N/A") diff --git a/otp/chat/ChatAgentUD.py b/otp/chat/ChatAgentUD.py new file mode 100755 index 00000000..5b816f13 --- /dev/null +++ b/otp/chat/ChatAgentUD.py @@ -0,0 +1,78 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectGlobalUD import DistributedObjectGlobalUD +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.MsgTypes import * +from otp.distributed import OtpDoGlobals +from toontown.toonbase import TTLocalizer + +BLACKLIST = TTLocalizer.Blacklist +OFFENSE_MSGS = ('-- DEV CHAT -- word blocked: %s', 'Watch your language! This is your first offense. You said "%s".', + 'Watch your language! This is your second offense. Next offense you\'ll get banned for 24 hours. You said "%s".') + +class ChatAgentUD(DistributedObjectGlobalUD): + notify = DirectNotifyGlobal.directNotify.newCategory('ChatAgentUD') + wantWhitelist = config.GetBool('want-whitelist', True) + + chatMode2channel = { + 1 : OtpDoGlobals.OTP_MOD_CHANNEL, + 2 : OtpDoGlobals.OTP_ADMIN_CHANNEL, + 3 : OtpDoGlobals.OTP_SYSADMIN_CHANNEL, + } + chatMode2prefix = { + 1 : "[MOD] ", + 2 : "[ADMIN] ", + 3 : "[SYSADMIN] ", + } + + def announceGenerate(self): + DistributedObjectGlobalUD.announceGenerate(self) + + self.offenses = {} + + def chatMessage(self, message, chatMode): + sender = self.air.getAvatarIdFromSender() + if sender == 0: + self.air.writeServerEvent('suspicious', self.air.getAccountIdFromSender(), + 'Account sent chat without an avatar', message) + return + + if chatMode == 0 and self.wantWhitelist: + if self.detectBadWords(self.air.getMsgSender(), message): + return + + self.air.writeServerEvent('chat-said', sender, message) + self.air.send(self.air.dclassesByName['DistributedAvatarUD'].aiFormatUpdate('setTalk', sender, sender, self.air.ourChannel, [message])) + + def detectBadWords(self, sender, message): + words = message.split() + print words + for word in words: + if word.lower() in BLACKLIST: + accountId = (sender >> 32) & 0xFFFFFFFF + avId = sender & 0xFFFFFFFF + + if not sender in self.offenses: + self.offenses[sender] = 0 + + if self.air.friendsManager.getToonAccess(avId) < 300: + self.offenses[sender] += 1 + + if self.offenses[sender] >= 3: + msg = 'Banned' + + else: + msg = OFFENSE_MSGS[self.offenses[sender]] % word + dclass = self.air.dclassesByName['ClientServicesManagerUD'] + dg = dclass.aiFormatUpdate('systemMessage', + OtpDoGlobals.OTP_DO_ID_CLIENT_SERVICES_MANAGER, + sender, 1000000, [msg]) + self.air.send(dg) + #self.air.banManager.ban(sender, 2, 'language') + + self.air.writeServerEvent('chat-offense', accountId, word=word, num=self.offenses[sender], msg=msg) + if self.offenses[sender] >= 3: + del self.offenses[sender] + + return 1 + + return 0 \ No newline at end of file diff --git a/otp/chat/ChatGarbler.py b/otp/chat/ChatGarbler.py new file mode 100755 index 00000000..5e8b5a60 --- /dev/null +++ b/otp/chat/ChatGarbler.py @@ -0,0 +1,22 @@ +import random + +class ChatGarbler: + + def __init__(self, messages): + self.messages = messages + + def garble(self, avatar, numWords): + newMessage = '' + + if avatar.style: + avatarType = avatar.style.getType() + wordList = self.messages[avatarType if avatarType in self.messages else 'default'] + + for i in xrange(1, numWords + 1): + wordIndex = random.randint(0, len(wordList) - 1) + newMessage = newMessage + wordList[wordIndex] + + if i < numWords: + newMessage = newMessage + ' ' + + return '\x01WLDisplay\x01%s\x02' % newMessage diff --git a/otp/chat/ChatGlobals.py b/otp/chat/ChatGlobals.py new file mode 100755 index 00000000..605a3907 --- /dev/null +++ b/otp/chat/ChatGlobals.py @@ -0,0 +1,37 @@ +import string +NORMAL_CHAT = 1 +WHISPER_CHAT = 2 +GUILD_CHAT = 3 +CREW_CHAT = 4 +SHIPPVP_CHAT = 5 +ERROR_NONE = None +ERROR_NO_OPEN_CHAT = 1 +ERROR_NOT_FRIENDS = 2 +ERROR_NO_RECEIVER = 3 +ERROR_NO_GUILD_CHAT = 4 +ERROR_NO_CREW_CHAT = 5 +ERROR_NO_SHIPPVP_CHAT = 6 +TYPEDCHAT = 0 +SPEEDCHAT_NORMAL = 1 +SPEEDCHAT_EMOTE = 2 +SPEEDCHAT_CUSTOM = 3 +SYSTEMCHAT = 4 +GAMECHAT = 5 +GUILDCHAT = 6 +PARTYCHAT = 7 +SPEEDCHAT_QUEST = 8 +FRIEND_UPDATE = 9 +CREW_UPDATE = 10 +GUILD_UPDATE = 11 +AVATAR_UNAVAILABLE = 12 +SHIPPVPCHAT = 13 +GMCHAT = 14 +ChatEvent = 'ChatEvent' +NormalChatEvent = 'NormalChatEvent' +SCChatEvent = 'SCChatEvent' +SCCustomChatEvent = 'SCCustomChatEvent' +SCEmoteChatEvent = 'SCEmoteChatEvent' +SCQuestEvent = 'SCQuestEvent' +OnScreen = 0 +OffScreen = 1 +Thought = 2 diff --git a/otp/chat/ChatInputWhiteListFrame.py b/otp/chat/ChatInputWhiteListFrame.py new file mode 100755 index 00000000..7fcf179e --- /dev/null +++ b/otp/chat/ChatInputWhiteListFrame.py @@ -0,0 +1,154 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from direct.fsm import FSM +from otp.otpbase import OTPGlobals + +class ChatInputWhiteListFrame(FSM.FSM, DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('ChatInputWhiteList') + + def __init__(self, entryOptions, parent = None, **kw): + FSM.FSM.__init__(self, 'ChatInputWhiteListFrame') + self.receiverId = None + DirectFrame.__init__(self, parent=aspect2dp, pos=(0, 0, 0.3), relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(1.6, 1, 1.4), image_pos=(0, 0, -0.05), image_color=OTPGlobals.GlobalDialogColor, borderWidth=(0.01, 0.01)) + optiondefs = {'parent': self, + 'relief': DGG.SUNKEN, + 'scale': 0.05, + 'frameSize': (-0.2, 25.3, -0.5, 1.2), + 'borderWidth': (0.1, 0.1), + 'frameColor': (0.9, 0.9, 0.85, 0.8), + 'pos': (-0.2, 0, 0.11), + 'entryFont': OTPGlobals.getInterfaceFont(), + 'width': 8.6, + 'numLines': 3, + 'cursorKeys': 1, + 'backgroundFocus': 0, + 'suppressKeys': 1, + 'suppressMouse': 1, + 'command': self.sendChat, + 'focus': 0, + 'text': '', + 'sortOrder': DGG.FOREGROUND_SORT_INDEX} + entryOptions['parent'] = self + self.chatEntry = DirectEntry(**entryOptions) + self.whisperId = None + self.chatEntry.bind(DGG.OVERFLOW, self.chatOverflow) + self.active = 0 + self.autoOff = 0 + self.sendBy = 'Mode' + from direct.gui import DirectGuiGlobals + self.chatEntry.bind(DirectGuiGlobals.TYPE, self.applyFilter) + self.chatEntry.bind(DirectGuiGlobals.ERASE, self.applyFilter) + + def destroy(self): + from direct.gui import DirectGuiGlobals + self.chatEntry.unbind(DGG.OVERFLOW) + self.chatEntry.unbind(DirectGuiGlobals.TYPE) + self.chatEntry.unbind(DirectGuiGlobals.ERASE) + self.chatEntry.ignoreAll() + DirectFrame.destroy(self) + + def delete(self): + self.ignore('arrow_up-up') + self.ignore('arrow_down-up') + + def requestMode(self, mode, *args): + return self.request(mode, *args) + + def enterOff(self): + self.deactivate() + localAvatar.chatMgr.fsm.request('mainMenu') + + def exitOff(self): + self.activate() + + def enterAllChat(self): + self.chatEntry['focus'] = 1 + self.show() + + def exitAllChat(self): + pass + + def enterAvatarWhisper(self): + self.tempText = self.chatEntry.get() + self.activate() + + def exitAvatarWhisper(self): + self.chatEntry.set(self.tempText) + self.whisperId = None + + def activateByData(self, receiverId = None): + self.receiverId = receiverId + result = None + if not self.receiverId: + result = self.requestMode('AllChat') + elif self.receiverId: + self.whisperId = receiverId + result = self.requestMode('AvatarWhisper') + return result + + def activate(self): + self.chatEntry['focus'] = 1 + self.show() + self.active = 1 + self.chatEntry.guiItem.setAcceptEnabled(True) + + def deactivate(self): + self.chatEntry.set('') + self.chatEntry['focus'] = 0 + self.hide() + self.active = 0 + + def isActive(self): + return self.active + + def sendChat(self, text, overflow = False): + if not (len(text) > 0 and text[0] in ['~', '>']): + text = self.chatEntry.get(plain=True) + + if text: + self.chatEntry.set('') + + if not base.cr.chatAgent.verifyMessage(text): + return + + self.sendChatBySwitch(text) + + if not overflow: + self.hide() + if self.autoOff: + self.requestMode('Off') + + localAvatar.chatMgr.messageSent() + + def sendChatBySwitch(self, text): + if len(text) > 0 and text[0] == '~': + base.talkAssistant.sendOpenTalk(text) + elif self.sendBy == 'Mode': + self.sendChatByMode(text) + elif self.sendBy == 'Data': + self.sendChatByData(text) + else: + self.sendChatByMode(text) + + def sendChatByData(self, text): + if self.receiverId: + base.talkAssistant.sendWhisperTalk(text, self.receiverId) + else: + base.talkAssistant.sendOpenTalk(text) + + def sendChatByMode(self, text): + state = self.getCurrentOrNextState() + messenger.send('sentRegularChat') + if state == 'AvatarWhisper': + base.talkAssistant.sendAvatarWhisperWLChat(text, self.whisperId) + else: + base.talkAssistant.sendOpenTalk(text) + + def chatOverflow(self, overflowText): + self.notify.debug('chatOverflow') + self.sendChat(self.chatEntry.get(plain=True), overflow=True) + + def applyFilter(self, keyArgs): + if base.whiteList: + self.chatEntry.set(base.whiteList.processThroughAll(self.chatEntry.get(plain=True))) diff --git a/otp/chat/ChatManager.py b/otp/chat/ChatManager.py new file mode 100755 index 00000000..05779a5f --- /dev/null +++ b/otp/chat/ChatManager.py @@ -0,0 +1,279 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.gui.DirectGui import * +from direct.showbase import DirectObject +from panda3d.core import * +from otp.otpbase import OTPLocalizer +from otp.nametag.NametagConstants import * +import ChatUtil + +ChatEvent = 'ChatEvent' +NormalChatEvent = 'NormalChatEvent' +SCChatEvent = 'SCChatEvent' +SCCustomChatEvent = 'SCCustomChatEvent' +SCEmoteChatEvent = 'SCEmoteChatEvent' + +class ChatManager(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('ChatManager') + + def __init__(self, cr, localAvatar): + self.cr = cr + self.localAvatar = localAvatar + self.wantBackgroundFocus = 1 + self.__scObscured = 0 + self.__normalObscured = 0 + self.noTrueFriends = None + self.noSpeedchatPlus = None + self.fsm = ClassicFSM.ClassicFSM('chatManager', [State.State('off', self.enterOff, self.exitOff), + State.State('mainMenu', self.enterMainMenu, self.exitMainMenu), + State.State('speedChat', self.enterSpeedChat, self.exitSpeedChat), + State.State('normalChat', self.enterNormalChat, self.exitNormalChat), + State.State('whisper', self.enterWhisper, self.exitWhisper), + State.State('whisperChat', self.enterWhisperChat, self.exitWhisperChat), + State.State('whisperSpeedChat', self.enterWhisperSpeedChat, self.exitWhisperSpeedChat), + State.State('noTrueFriends', self.enterNoTrueFriends, self.exitNoTrueFriends), + State.State('noSpeedchatPlus', self.enterNoSpeedchatPlus, self.exitNoSpeedchatPlus), + State.State('otherDialog', self.enterOtherDialog, self.exitOtherDialog), + State.State('whiteListOpenChat', self.enterWhiteListOpenChat, self.exitWhiteListOpenChat), + State.State('whiteListAvatarChat', self.enterWhiteListAvatarChat, self.exitWhiteListAvatarChat)], 'off', 'off') + self.fsm.enterInitialState() + return + + def delete(self): + self.ignoreAll() + del self.fsm + if hasattr(self.chatInputNormal, 'destroy'): + self.chatInputNormal.destroy() + self.chatInputNormal.delete() + del self.chatInputNormal + self.chatInputSpeedChat.delete() + del self.chatInputSpeedChat + if self.noTrueFriends: + self.noTrueFriends.destroy() + self.noTrueFriends = None + if self.noSpeedchatPlus: + self.noSpeedchatPlus.destroy() + self.noSpeedchatPlus = None + del self.localAvatar + del self.cr + return + + def obscure(self, normal, sc): + self.__scObscured = sc + if self.__scObscured: + self.scButton.hide() + self.__normalObscured = normal + if self.__normalObscured: + self.normalButton.hide() + + def isObscured(self): + return (self.__normalObscured, self.__scObscured) + + def stop(self): + self.fsm.request('off') + self.ignoreAll() + + def start(self): + self.fsm.request('mainMenu') + + def announceChat(self): + messenger.send(ChatEvent) + + def announceSCChat(self): + messenger.send(SCChatEvent) + self.announceChat() + + def sendChatString(self, message): + chatFlags = CFSpeech | CFTimeout + if ChatUtil.isThought(message): + message = ChatUtil.removeThoughtPrefix(message) + chatFlags = CFThought + messenger.send(NormalChatEvent) + self.announceChat() + + def sendWhisperString(self, message, whisperAvatarId): + pass + + def sendSCChatMessage(self, msgIndex): + base.talkAssistant.sendOpenSpeedChat(1, msgIndex) + + def sendSCWhisperMessage(self, msgIndex, whisperAvatarId): + base.talkAssistant.sendAvatarWhisperSpeedChat(1, msgIndex, whisperAvatarId) + + def sendSCCustomChatMessage(self, msgIndex): + base.talkAssistant.sendOpenSpeedChat(3, msgIndex) + + def sendSCCustomWhisperMessage(self, msgIndex, whisperAvatarId): + base.talkAssistant.sendAvatarWhisperSpeedChat(3, msgIndex, whisperAvatarId) + + def sendSCEmoteChatMessage(self, emoteId): + base.talkAssistant.sendOpenSpeedChat(2, emoteId) + + def sendSCEmoteWhisperMessage(self, emoteId, whisperAvatarId): + base.talkAssistant.sendAvatarWhisperSpeedChat(2, emoteId, whisperAvatarId) + + def enterOff(self): + self.scButton.hide() + self.normalButton.hide() + self.ignoreAll() + + def exitOff(self): + pass + + def enterMainMenu(self): + self.checkObscurred() + if base.cr.wantTypedChat(): + if self.wantBackgroundFocus: + self.chatInputNormal.chatEntry['backgroundFocus'] = 1 + self.acceptOnce('enterNormalChat', self.fsm.request, ['normalChat']) + + def checkObscurred(self): + if not self.__scObscured: + self.scButton.show() + if not self.__normalObscured: + self.normalButton.show() + + def exitMainMenu(self): + self.scButton.hide() + self.normalButton.hide() + self.ignore('enterNormalChat') + if self.wantBackgroundFocus: + self.chatInputNormal.chatEntry['backgroundFocus'] = 0 + + def whisperTo(self, avatarName, avatarId): + self.fsm.request('whisper', [avatarName, avatarId]) + + def noWhisper(self): + self.fsm.request('mainMenu') + + def handleWhiteListSelect(self): + self.fsm.request('whiteListOpenChat') + + def enterWhiteListOpenChat(self): + self.checkObscurred() + if self.wantBackgroundFocus: + self.chatInputNormal.chatEntry['backgroundFocus'] = 0 + base.localAvatar.chatMgr.chatInputWhiteList.activateByData() + + def exitWhiteListOpenChat(self): + pass + + def enterWhiteListAvatarChat(self, receiverId): + if self.wantBackgroundFocus: + self.chatInputNormal.chatEntry['backgroundFocus'] = 0 + base.localAvatar.chatMgr.chatInputWhiteList.activateByData(receiverId) + + def exitWhiteListAvatarChat(self): + pass + + def enterWhisper(self, avatarName, avatarId): + self.whisperScButton['extraArgs'] = [avatarName, avatarId] + self.whisperButton['extraArgs'] = [avatarName, avatarId] + online = 0 + if avatarId in self.cr.doId2do: + online = 1 + elif self.cr.isFriend(avatarId): + online = self.cr.isFriendOnline(avatarId) + avatarUnderstandable = 0 + av = None + if avatarId: + av = self.cr.identifyAvatar(avatarId) + if av != None: + avatarUnderstandable = av.isUnderstandable() + chatName = avatarName + normalButtonObscured, scButtonObscured = self.isObscured() + if avatarUnderstandable and online and not normalButtonObscured: + self.whisperButton['state'] = 'normal' + self.enablewhisperButton() + else: + self.whisperButton['state'] = 'inactive' + self.disablewhisperButton() + if online: + self.whisperScButton['state'] = 'normal' + self.whisperButton['state'] = 'normal' + self.changeFrameText(OTPLocalizer.ChatManagerWhisperToName % chatName) + else: + self.whisperScButton['state'] = 'inactive' + self.whisperButton['state'] = 'inactive' + self.changeFrameText(OTPLocalizer.ChatManagerWhisperOffline % chatName) + self.whisperFrame.show() + self.refreshWhisperFrame() + if avatarUnderstandable and online: + if self.wantBackgroundFocus: + self.chatInputNormal.chatEntry['backgroundFocus'] = 1 + self.acceptOnce('enterNormalChat', self.fsm.request, ['whisperChat', [avatarName, avatarId]]) + + def disablewhisperButton(self): + pass + + def enablewhisperButton(self): + pass + + def refreshWhisperFrame(self): + pass + + def changeFrameText(self, newText): + self.whisperFrame['text'] = newText + + def exitWhisper(self): + self.whisperFrame.hide() + self.ignore('enterNormalChat') + self.chatInputNormal.chatEntry['backgroundFocus'] = 0 + + def enterWhisperSpeedChat(self, avatarId): + self.whisperFrame.show() + if self.wantBackgroundFocus: + self.chatInputNormal.chatEntry['backgroundFocus'] = 0 + self.chatInputSpeedChat.show(avatarId) + + def exitWhisperSpeedChat(self): + self.whisperFrame.hide() + self.chatInputSpeedChat.hide() + + def enterWhisperChat(self, avatarName, avatarId): + result = self.chatInputNormal.activateByData(avatarId) + return result + + def exitWhisperChat(self): + self.chatInputNormal.deactivate() + + def enterSpeedChat(self): + messenger.send('enterSpeedChat') + if not self.__scObscured: + self.scButton.show() + if not self.__normalObscured: + self.normalButton.show() + if self.wantBackgroundFocus: + self.chatInputNormal.chatEntry['backgroundFocus'] = 0 + self.chatInputSpeedChat.show() + + def exitSpeedChat(self): + self.scButton.hide() + self.normalButton.hide() + self.chatInputSpeedChat.hide() + + def enterNormalChat(self): + result = self.chatInputNormal.activateByData() + return result + + def exitNormalChat(self): + self.chatInputNormal.deactivate() + + def enterNoTrueFriends(self): + self.notify.error('called enterNoTrueFriends() on parent class') + + def exitNoTrueFriends(self): + self.notify.error('called exitNoTrueFriends() on parent class') + + def enterNoSpeedchatPlus(self): + self.notify.error('called enterNoSpeedchatPlus() on parent class') + + def exitNoSpeedchatPlus(self): + self.notify.error('called exitNoSpeedchatPlus() on parent class') + + def enterOtherDialog(self): + pass + + def exitOtherDialog(self): + pass diff --git a/otp/chat/ChatUtil.py b/otp/chat/ChatUtil.py new file mode 100644 index 00000000..116d2cfd --- /dev/null +++ b/otp/chat/ChatUtil.py @@ -0,0 +1,15 @@ +ThoughtPrefix = '.' + +def isThought(message): + return message and message.startswith(ThoughtPrefix) + +def removeThoughtPrefix(message): + if isThought(message): + return message[len(ThoughtPrefix):] + else: + return message + +def findAvatarName(id): + info = base.cr.identifyAvatar(id) + + return info.getName() if info else '' diff --git a/otp/chat/SequenceListData.py b/otp/chat/SequenceListData.py new file mode 100644 index 00000000..36b0ecd7 --- /dev/null +++ b/otp/chat/SequenceListData.py @@ -0,0 +1,379 @@ +SEQUENCES = { + 'all': ['a ack bar', 'ah ack bar', 'ah ache bar', 'ah snack bar'], + 'bull': ['sheep', 'sheeps', 'ship', 'shift'], + 'neigh': ['err', 'grr', 'grrr', 'grrrrrrrl', 'grow', 'grove', 'gurl', 'girl', 'gear', 'gears', 'gross', 'ah', 'a', 'gah'], + 'four': ['chan', 'twenty', '20', '2 0', 'twin ty', 'twin tea'], + 'go': ['to hello'], + 'hate': ['black people', 'back people', 'white people'], + 'hmmm': ['arr y juan', 'arr y jane', 'arr y jan', 'arr y jam'], + 'vet': ['china'], + 'grape': ['me', 'you', 'him', 'his', 'their', 'him', 'them', 'your', 'ed', 'yourself', 'ur self'], + 'zone': ['tan'], + 'tt': ['its', 'its', 'it'], + 'to': ["he'll", 'bangs'], + 'under': ['my skirt', 'her skirt', 'your skirt', 'my dress', 'her dress', 'your dress', 'ur skirt', 'ur dress'], + 'coop': ['kk'], + 'far': ['kk', 'king', 'k ing', 'k eng', 'kk ing', 'kk eng'], + 'fat': ['as', 'asset', 'tas'], + 'jack': ['ed', 'ing', 'me', 'myself', 'her', 'herself', 'him', 'himself', 'off', 'ourselves', 'they', 'themselves', 'us', 'you', 'yourself', 'u late'], + 'huger': ['as', 'mass', 'ashton', 'ask', 'asp', 'jazz', 'ash'], + 'fad': ['gg ate', 'gg eat', 'gate', 'get', 'it', 'goat', 'git'], + 'vern': ['gen'], + 'gg': ['ay', 'a y', 'ah y', 'ayy', 'aye'], + 'marry': ['juan a', 'juan ha', 'juan ah', 'jane', 'jan', 'jam'], + 'brother': ['dead', 'deads'], + 'dip': ['stick'], + 'flick': ['you', 'king', 'ing', 'this', 'my life', 'everyone', 'me', 'off'], + 'nigh': ['err', 'grr', 'grrr', 'grrrrrrrl', 'grow', 'grove', 'gurl', 'girl', 'gear', 'gears', 'gross', 'ah', 'a', 'gah'], + 'tea': ['bag', 'bagged', 'bagging', 'bags'], + 'horn': ['y', 'horn eh', 'knee', 'ie', 'i'], + 'folk': ['ed', 'err', 'error', 'errors', 'ear', 'ears', 'you', 'gate', 'goat', 'ing', 'this', 'my life', 'everyone', 'me', 'off', 'king'], + 'blue': ['waffle', 'job', 'jobs'], + 'faye': ['get', 'git', 'got'], + 'sue': ['kk', 'lute', 'lutes'], + 'sun': ['of a bit', 'of a peach', 'of a be', 'of a bee'], + 'fingers': ['you', 'me', 'her', 'him', 'them', 'us', 'your', 'ur', 'yourself', 'u'], + 'kun': ['tt'], + 'new': ['york', 'fork', 'folk', 'forks', 'folks'], + 'freaky': ['in', 'ing'], + 'ahhhhh': ['zzz', 'sees', '$', "'s"], + 'burn': ['in hello'], + 'climbs': ['max', 'maxed', 'maxes', 'maxing'], + 'men': ['str u ate'], + 'buck': ['err', 'error', 'errors', 'my life', 'everyone', 'me', 'ing', 'yourself', 'your self', 'ur self'], + 'rekt': ['um', 'hum', 'u hmm', 'huh', 'u hm', 'u hmmm'], + 'climbed': ['max', 'maxed', 'maxes', 'maxing'], + 'bow': ['job', 'jobs'], + 'queue': ['ear'], + 'bangs': ['me', 'her', 'him', 'it', 'them'], + 'bon': ['r', 'or', 'err', 'errs', 'me', 'him', 'it', 'ro', 'her'], + 'boo': ['be', 'bee', 'bees', 'by', 'ty', 'bye', 'byes', 'tay', 'tea'], + 'bob': ['zzz', 'zz'], + 'makes': ['him hard', 'love'], + 'via': ['grape'], + 'family': ['dead', 'deads'], + 'put': ['cee', 'say', 'says', 'sea', 'seas', 'see', 'sees', 'she', 'shes', "she's", 'si'], + 'fake': ['king', 'k ing', 'k eng', 'kk ing', 'kk eng', 'off'], + 'ape': ['me', 'you', 'him', 'his', 'their', 'him', 'them', 'your', 'yourself', 'ur self'], + 'flunk': ['ing', 'king', 'eng', 'in gg', 'her', 'u', 'you', 'ur', 'yourself'], + 'two': ["he'll"], + 'few': ['ack or', 'hack or', 'kk', 'king', 'ok', 'k ing', 'k eng', 'kk ing', 'kk eng'], + 'ayy': ['ds'], + 'ahhh': ['zzz', 'sees', '$', "'s"], + 'yuck': ['err', 'error', 'errors', 'my life', 'me', 'ing', 'you', 'dee'], + 'phony': ['number', 'numb err'], + 'clue': ['luxe', 'lucks'], + 'glass': ['hole', 'whole', 'ole', 'ooo le', 'holes'], + "i'm": ['moist', 'wet', 'hard'], + 'cunning': ['link us', 'link is'], + 'ma': ['stir bait'], + 'mm': ['arr y juan', 'arr y jane', 'arr y jan', 'arr y jam'], + 'mo': ['foe', 'foes', 'for', 'four'], + 'oh': ['rn y'], + 'pour': ['on', 'no'], + 'cam': ['bucket', 'buckets', 'dumpster', 'girl', 'girls', 'on me', 'tastes'], + 'making': ['him hard', 'love'], + 'my': ['as', 'asp', 'bowls', "bowl's", 'bows', 'bock', 'butted', 'but', 'come', 'pew', 'period', 'ah', 'dee', 'ash', 'di ik', 'deck', 'decks', 'dock', 'docks', 'cook', 'cooks'], + 'dee': ['bag', 'kay', 'k'], + 'give': ['me head', 'me pleasure', 'a truck'], + 'pro': ['st i tut', 'stick tut '], + 'chin': ['kk'], + 'bend': ['over'], + 'chic': ['a go', 'ago'], + 'tah': ['tas', 'tah'], + 'huge': ['as', 'mass', 'ashton', 'ask', 'asp', 'jazz', 'ash'], + 'lucks': ['clan'], + 'id': ['i'], + 'hot': ['mail', 'come'], + 'purr': ['cee', 'say', 'says', 'sea', 'seas', 'see', 'sees', 'she', 'shes', "she's", 'si'], + 'inst': ['a gram', 'ah gram', 'ahh gram', 'ahhh gram', 'ahhhhh gram', 'ahhhhhh gram'], + 'mah': ['as', 'asp', 'bowls', "bowl's", 'bows', 'bock', 'butted', 'but', 'come', 'pew', 'period', 'ah', 'dee', 'ash', 'di ik', 'deck', 'decks', 'dock', 'docks', 'cook', 'cooks'], + 'dumbo': ['as', 'ash', 'ask', 'asp'], + 'lap': ['dance'], + 'a': ['zzz', 'sees', '$', "'s"], + 'curry': ['man cher', 'men cher', 'min cher', 'moon cher'], + 'eats': ['me out', 'you out', 'u out', 'her out'], + 'st': ['on ed', 'rip'], + 'si': ['u tt', 'exe', 'kk zzz', 'k zzz', 'ex'], + 'so': ['exe', 'kk zzz', 'k zzz', 'ex', 'kk'], + 'sc': ['hum', 'um', 'u hmm', 'huh', 'u hm', 'u hmmm'], + 'mexican': ['brown'], + 'octopuses': ['y', 'ye', 'sea', 'seas'], + 'pant': ['tease', 'teas', 'ties'], + 'mayor': ['a juan', 'ah juan'], + 'fads': ['gg ate', 'gg eat', 'eat', 'gate', 'get', 'it', 'goat', 'git'], + 'apes': ['me', 'you', 'him', 'his', 'their', 'him', 'them', 'your', 'yourself', 'ur self'], + 'pens': ['ice', 'iced', 'ices', 'icing', 'island', 'eh tray', 'eh tate', '15', 'is', '1s', 'i zzz', 'his', '1 5'], + '<': ['=', '-'], + 'dad': ['dead', 'deads'], + 'cee': ['man', 'men', 'min', 'mins', 'moon', 'ex', 'a tt le', 'xii', 'exe', 'kk zzz'], + 'dab': ['itch', 'itches'], + "jack's": ['ed', 'ing', 'me', 'myself', 'her', 'herself', 'him', 'himself', 'of', 'off', 'ourselves', 'they', 'themselves', 'us', 'you', 'yourself', 'u late'], + 'nada': ['zen', 'zeke', 'z.z.'], + 'rake': ['you'], + 'dah': ['am', 'yum'], + 'dat': ['as', 'ask', 'asp', 'asset', 'ashton'], + 'half': ['baked'], + 'not': ['zen', 'zeke', 'z.z.'], + 'day': ['um', 'yum'], + 'masters': ['bait', 'baits', 'baiter', 'bait eng', 'bait ed'], + "ape's": ['me', 'you', 'him', 'his', 'their', 'him', 'them', 'your', 'yourself', 'ur self'], + 'mastered': ['bait', 'baits', 'baiter', 'bait eng', 'bait ed'], + 'bone': ['r', 'or', 'err', 'errs', 'me', 'him', 'it', 'ro', 'her'], + 'luck': ['err', 'error', 'errors', 'my life', 'everyone', 'me', 'ing', 'yourself', 'your self', 'ur self'], + 'el': ['mayo'], + 'eh': ['bo la', 'bowl a', 'rekt ion', 'rekt ions', 'wrekt ion', 'wrekt ions'], + 'wrap': ['me', 'you', 'him', 'them', 'them'], + 'ex': ['tube', 'tubes', 'at', 'cream mint'], + 'flock': ['ed', 'err', 'error', 'errors', 'ear', 'ears', 'you', 'ate', 'eat', 'gate', 'goat', 'got', 'ing', 'this', 'my life', 'everyone', 'me', 'off', 'king', 'u', 'you', 'yourself', 'ur'], + 'your': ['as', 'but', 'bum', 'come', 'period', 'hole', 'holes', 'ash', 'sass'], + 're': ['tar ed', 'tart', 'tarts', 'tar tt ed', 'tar teed', 'tar dead', 'tar deed', 'tar dee ed', 'tar dad'], + 'hill': ['yourself', 'your self', 'ur self', 'u err self', 'u r self'], + 'got': ['wasted', 'waste ed', 'hi', 'high', 'higher', 'highest', 'ooo hello'], + 'get': ['wasted', 'waste ed', 'bent', 'hi', 'high', 'higher', 'highest', 'lay'], + 'bla': ['zz it', 'zzz it', 'k tar', 'kk tar'], + 'red': ['tube', 'tubes'], + 'shut': ['the duck', 'the luck'], + 'asp': ['hole', 'whole', 'ole', 'ooo le', 'holes', 'zzz', "'s"], + 'bass': ['hole', 'whole', 'ole', 'stir', 'stir ed', 'stair', 'stair ed', 'tar', 'star', 'stared', 'tt a r ed', 'holes'], + 'dirt': ['y', 'ye', 'eh'], + 'free': ['kin', 'k in', 'k ing', 'k eng'], + 'bulls': ['hit', 'sheep', 'sheeps', 'ship', 'shift'], + 'shh': ['it', 'eat', 'hit', 'hits', 'its', 'ex i', 'he mail', 'he mails', 'he mailed', 'he mailing', 'he male', 'he males', 'ii tt', 'i it', 'i tt'], + 'grapes': ['me', 'you', 'him', 'his', 'their', 'him', 'them', 'your', 'ed', 'yourself', 'ur self'], + 'mastering': ['bait', 'baits', 'baiter', 'bait eng', 'bait ed'], + 'ask': ['hole', 'whole', 'ole', 'ooo le', 'holes', 'zzz', "'s"], + 'ash': ['hole', 'whole', 'ole', 'ooo le', 'holes', 'zzz', "'s"], + 'knit': ['gah'], + 'ate': ['me out', 'you out', 'u out', 'her out'], + 'her': ['as', 'asp', 'bowls', "bowl's", 'bows', 'bock', 'butted', 'but', 'come', 'pew', 'period', 'ah', 'dee', 'ash'], + 'stone': ['ed'], + 'shucks': ['it', 'on it', 'my deck', 'my dock', 'my doc', 'ur deck', 'ur dock', 'your deck', 'your dock', 'his deck', 'his dock', 'cooks', 'cook', 'my cook', 'ur cook', 'your cook', 'his cook', 'my cooks', 'ur cooks', 'your cooks', 'his cooks', 'deck', 'decks', 'a dock', 'a deck', 'a docks', 'a decks', 'a cook', 'a cooks', 'my duck', 'my ducks', 'dock'], + 'ding': ['us', 'usa', 'uss', 'u.s.a.'], + 'fuchsia': ['err', 'error', 'errors', 'ear', 'ears', 'you', 'this', 'my life', 'everyone', 'me', 'off'], + 'blowy': ['job', 'jobs'], + 'open': ['legs', 'leg'], + 'little': ['sit', 'hitch', 'itch'], + 'bite': ['cha', 'chi', 'chez', 'chin', 'chine', 'china', 'chose', 'chow', 'chess', 'itch', 'itches', 'ach', 'cheese', 'cheddar', 'shh', 'shhh', 'shhhh', 'shhhhhh'], + 'girls': ['1 cup', 'one cup', 'on cup', 'won cup', 'and a cup', 'plus a cup'], + 'master': ['bait', 'baits', 'baiter', 'bait eng', 'bait ed'], + 'too': ["he'll", 'bangs'], + 'passed': ['off'], + 'white': ['power'], + 'hue': ['jazz'], + 'selling': ['cracked', 'cracked-uptick', "crackin'", 'cracking', 'crackle', "crackle's", 'crackles', 'crackly', 'herbs'], + 'haved': ['sec', 'see ex'], + 'peck': ['err'], + 'king': ['kk y'], + 'kind': ['kk y'], + 'ben': ['dover', 'dove err', 'doves err'], + 'bloat': ['job', 'jobs'], + 'bee': ['ach', 'i tea see ache', 'i tee see ache', 'i tea sea ache', 'i tee sea ache', 'eye tea see ache', 'eye tee see ache', 'eye tea sea ache', 'eye tee sea ache', 'itches', 'itch', 'jay', 'jays', 'job', 'jobs', 'etch', 'cheese', 'itching', 'each', 'shh', 'shhh', 'shhhh', 'shhhhhh', 'it cha', 'cha', 'ache'], + 'stu': ['pit', 'pod'], + 'bet': ['ouch'], + 'ann': ['a hon', 'a honda', 'a con', 'a cone', 'al', 'ails', 'ailed', 'ailing', 'ale', 'ales', 'all', 'awl', 'us', 'u.s.', 'u.s.a.', 'u si', 'usa', 'use', 'used', 'using', 'uses', 'uss'], + 'rap': ['me', 'you', 'her', 'his', 'their', 'him', 'them', 'your', 'ed', '35', 'yourself', 'ur self', 'eh me', 'ping'], + 'seem': ['en', 'an', 'man', 'men', 'min', 'ex', 'a tt le'], + 'saw': ['kk'], + 'ray': ['ping', 'pi'], + 'sell': ['cracked', 'cracked-uptick', "crackin'", 'cracking', 'crackle', "crackle's", 'crackles', 'crackly', 'herbs'], + 'truck': ['eh ru', 'ing', 'eng', 'you', 'u', 'up', 'ed', 'her', 'or'], + '=': ['8', '=', '-'], + 'play': ['boy'], + 'who': ['err', 'errs', 're', 'ree'], + 'what': ['the hello', 'the fork', 'the duck', 'the freaky', 'the yuck', 'your sky', 'ur sky', 'the truck', 'the fire truck', "the he'll"], + 'knee': ['gg', 'grow', 'gross', 'grr', 'grrr', 'gah', 'gas', 'gauss'], + 'kay': ['kay kay'], + 'class': ['hole', 'whole', 'ole'], + 'kk': ['awk', 'bock', 'err', 'ill', 'ills', 'kk'], + 'face': ['book'], + 'dot': ['come'], + 'flack': ['you', 'king', 'ing', 'this', 'my life', 'everyone', 'me', 'off'], + 'pause': ['i', 'eek', 'eh'], + 'ayyyy': ['ds'], + 'wut': ['the hello', 'the fork', 'the duck', 'the freaky', 'the yuck', 'your sky', 'ur sky', 'the truck', 'the fire truck', "the he'll"], + 'sugar': ['daddy'], + 'black': ['tar', 'k tar', 'kk tar'], + 'folks': ['ed', 'err', 'error', 'errors', 'ear', 'ears', 'you', 'gate', 'goat', 'ing', 'this', 'my life', 'everyone', 'me', 'off'], + '8': ['=', '-'], + 'his': ['as', 'asp', 'bowls', "bowl's", 'bows', 'bock', 'butted', 'but', 'come', 'pew', 'period', 'ah', 'dee', 'ash'], + 'hit': ['learn'], + 'di': ['i do', 'ill do', 'ill does', 'kk', 'ill dot', "i'll do", "i'll dot", "i'll does", 'ik', 'i'], + 'jazz': ['hole', 'ole', 'on'], + 'da': ['am', 'mm', 'yum'], + 'gee': ['mail', 'ay', 'yay'], + 'sassy': ['hole', 'whole', 'ole'], + 'dr': ['ugh', 'un kk'], + 'gen': ['it', 'i tall', 'i tail', 'i tails'], + 'flowerpots': ['head', 'headed', 'heading', 'heads'], + 'bat': ['star', 'stair', 'stair ed', 'star', 'stared'], + 'shy': ['it', 'eat', 'hit', 'hits', 'its', 'ex i', 'he mail', 'he mails', 'he mailed', 'he mailing', 'he male', 'he males', 'ii tt', 'i it', 'i tt'], + 'bay': ['be maker'], + 'she': ['it', 'hitting', 'its', 'ex i', 'mail', 'mails', 'mailed', 'mailing', 'male', 'males', 'tt', 'i', 'ii'], + 'ban': ['gg'], + 'sew': ['kk'], + 'blew': ['job', 'jobs'], + 'finger': ['you', 'me', 'her', 'him', 'them', 'us', 'your', 'ur', 'yourself', 'u'], + 'kneed': ['gg', 'grow', 'gross', 'grr', 'grrr', 'gah', 'gas', 'gauss'], + 'we': ['ed', 'neigh is', 'knees'], + 'omg': ['egg al'], + 'sold': ['cracked', 'cracked-uptick', "crackin'", 'cracking', 'crackle', "crackle's", 'crackles', 'crackly', 'herbs'], + 'come': ['bucket', 'buckets', 'dumpster', 'on me', 'tastes'], + 'cop': ['kk'], + 'hits': ['learn'], + 'grass': ['hole', 'whole', 'ole', 'ooo le', 'holes'], + 'instant': ['graham', 'grand'], + 'cog': ['awk'], + 'comes': ['bucket', 'buckets', 'dumpster', 'on me', 'tastes'], + 'con': ['dim', 'dims', 'dome', 'domes', 'dooms', 'doom', 'do hm', 'do hmm', 'do hmmm', 'do mm'], + 'pose': ['eh'], + 'period': ['cramps'], + '69': ['ed', 'ing'], + 'due': ['shh', 'shhh', 'shhhh', 'shhhhhh'], + 'dug': ['rugs'], + 'pi': ['pi', 'ssw'], + 'mary': ['juan a', 'juan ha', 'juan ah', 'jane', 'jan', 'jam'], + 'mike': ['hawk', 'hawks', 'hunt', 'hunts'], + 'wanna': ['duck', 'bangs'], + "kyle's": ['yourself', 'your self', 'ur self', 'u err self', 'u r self'], + 'snap': ['chat'], + 'gah': ['ay', 'yay'], + 'last': ['name'], + 'blowfish': ['job', 'jobs'], + 'coca': ['in', 'ing'], + 'fun': ['king', 'k ing', 'k eng', 'kk ing', 'kk eng', 'luck'], + 'pound': ['ed', 'ing', 'me', 'myself', 'her', 'herself', 'him', 'himself', 'of', 'off', 'ourselves', 'they', 'themselves', 'us', 'you', 'yourself'], + 'have': ['sec', 'see ex'], + 'kyles': ['yourself', 'your self', 'ur self', 'u err self', 'u r self'], + 'im': ['moist', 'wet', 'hard'], + 'in': ["he'll", 'the assistant', 'your mom', 'your mother', 'your assistant', 'ur assistant', 'ur mom', 'ur mother', 'the as', 'your as', 'ur as'], + 'concentration': ['camp'], + 'if': ['uk'], + 'make': ['him hard', 'love'], + 'honk': ['y', 'eye'], + 'docs': ['me', 'you', 'him', 'his', 'their', 'him', 'them', 'your'], + 'pah': ['key', 'keys'], + 'gets': ['wasted', 'waste ed', 'hi', 'high', 'higher', 'highest', 'lay'], + 'nik': ['err', 'grr', 'grrr', 'grrrrrrrl', 'grow', 'grove', 'gurl', 'girl', 'gear', 'gears', 'gross', 'ah', 'a', 'gah'], + 'ball': ['it more'], + 'hang': ['your', 'yourself', 'ur self', 'myself', 'my self', 'me', 'you'], + 'hand': ['job', 'jobs'], + 'beast': ['tea al i tea', 'tea al i ty', 'tea al i tie'], + 'kin': ['kk y'], + 'bos': ['ton'], + 'climb': ['max', 'maxed', 'maxes', 'maxing'], + 'kyle': ['yourself', 'your self', 'ur self', 'u err self', 'u r self'], + 'tho': ['tt', 'tea', 'tee', 'ty'], + 'mother': ['flick', 'flicker', 'fork', 'fuchsia', 'duck', 'ducking', 'folk', 'folks', 'yuck', 'flock', 'heck', 'truck', 'funky', 'flunky', 'fax', 'quacker', 'bucker', 'bicker', 'faker', 'fake', 'flunk', 'flunking', 'tru'], + 'the': ['di', 'hello', 'flunk'], + 'less': ['be i an', 'be i ann', 'be i anne', 'be an', 'be ann', 'be anne', 'bean', 'beans'], + '42': ['0'], + 'f.a.q.': ['ed', 'err', 'error', 'errors', 'ear', 'ears', 'you', 'ate', 'eat', 'gate', 'goat', 'got', 'ing', 'this', 'my life', 'everyone', 'me', 'off', 'king'], + 'skill': ['your', 'yourself', 'ur self', 'myself', 'my self', 'me', 'you'], + '$': ['exe', 'kk zzz', 'k zzz', 'ex', 'hit', 'hits', 'hole', 'whole', 'ole', 'ooo le', 'holes'], + 'sky': ['pea', 'peas', 'peel', 'pen', 'pet', 'pi', 'pie', 'peta', 'pico', 'pens', 'pop', 'hype', 'ape', 'pets', 'peep', 'per', 'pell', 'pa'], + 'shhh': ['it', 'eat', 'hit', 'hits', 'its', 'ex i', 'he mail', 'he mails', 'he mailed', 'he mailing', 'he male', 'he males', 'ii tt', 'i it', 'i tt'], + '4': ['20', '2 0', 'twenty', 'chan', 'twin tea', 'twin ty'], + 'bloo': ['job', 'jobs'], + 'ski': ['it', 'ii tt'], + 'read': ['tube', 'tubes'], + 'big': ['deck', 'decks', 'dock', 'docks', 'clock', 'clocks', 'cook', 'cooks'], + 'moo': ['foe', 'foes', 'for', 'four'], + 'mom': ['dead', 'deads'], + 'bit': ['cha', 'chi', 'chez', 'chin', 'chine', 'china', 'chose', 'chow', 'chess', 'itch', 'itches', 'ach', 'cheese', 'cheddar', 'shh', 'shhh', 'shhhh', 'shhhhhh'], + 'vague': ['in a', 'i nah'], + 'moe': ['foe', 'foes', 'for', 'four'], + 'pounds': ['ed', 'ing', 'me', 'myself', 'her', 'herself', 'him', 'himself', 'of', 'off', 'ourselves', 'they', 'themselves', 'us', 'you', 'yourself'], + 'sofa': ['king', 'kin', 'keen', 'kings'], + 'inter': ['course'], + 'old': ['are you', 'r you', 'are u', 'r u'], + 'duck': ['err', 'error', 'errors', 'my life', 'everyone', 'me', 'ing', 'yourself', 'your self', 'ur self'], + 'some': ['cricket'], + 'ahhhhhh': ['zzz', 'sees', '$', "'s"], + 'luxe': ['clan'], + 'for': ['kk', 'king', 'k ing', 'k eng', 'kk ing', 'kk eng', 'twenty', 'twin ty', '20', 'twin tea'], + 'pet': ['oh pile', 'oh piles', 'oh file', 'oh files'], + 'pew': ['cee', 'say', 'says', 'sea', 'seas', 'see', 'sees', 'she', 'shes', "she's", 'si'], + 'ice': ['hole', 'whole', 'ole', 'ooo le'], + 'moon': ['shine'], + 'foe': ['kk', 'king', 'ok', 'k ing', 'k eng', 'kk ing', 'kk eng'], + 'pen': ['ice', 'iced', 'ices', 'icing', 'island', 'eh tray', 'eh tate', '15', 'is', '1s', 'i zzz', 'his', '1 5'], + 'fog': ['ate', 'eat', 'gate', 'get', 'it', 'goat', 'got'], + 'anne': ['a hon', 'a honda', 'a con', 'a cone', 'al', 'us', 'u.s.', 'u.s.a.', 'u si', 'usa', 'use', 'used', 'using', 'uses', 'uss'], + 'pea': ['nest', 'mess', 'pea', 'pi', 'pie', 'do file', 'knees', 'nice', 'niece'], + 'anna': ['hon', 'honda', 'con da', 'con duh', 'cone da', 'cone duh'], + 'be': ['ach', 'i tea see ache', 'i tee see ache', 'i tea sea ache', 'i tee sea ache', 'eye tea see ache', 'eye tee see ache', 'eye tea sea ache', 'eye tee sea ache', 'itches', 'itch', 'jay', 'jays', 'job', 'jobs', 'etch', 'cheese', 'itching', 'each', 'shh', 'shhh', 'shhhh', 'shhhhhh', 'it cha', 'cha', 'ache'], + 'eating': ['me out', 'you out', 'u out', 'her out'], + 'zzz': ['3 ex', '33 ex', 'ex', 'ugh eng', 'hole', 'holes'], + 'rub': ['one off', 'one of', '1 off', '1 of', 'on off', 'on of'], + 'corn': ['oh graphic', 'ooo', 'hoo'], + 'bo': ['ach', 'i tea see ache', 'i tee see ache', 'i tea sea ache', 'i tee sea ache', 'eye tea see ache', 'eye tee see ache', 'eye tea sea ache', 'eye tee sea ache', 'itches', 'itch', 'jay', 'jays', 'job', 'jobs', 'etch', 'cheese', 'itching', 'each', 'shh', 'shhh', 'shhhh', 'shhhhhh', 'it cha', 'cha', 'ache'], + 'peep': ['show'], + 'by': ['itch', 'itches'], + "hit's": ['learn'], + 'on': ['your knees', 'your knee'], + 'sister': ['dead', 'deads'], + 'ace': ['hole', 'whole', 'ole', 'ooo le', 'holes', 'zzz', "'s"], + 'getting': ['wasted', 'waste ed', 'hi', 'high', 'higher', 'highest', 'lay'], + 'ack': ['ools'], + 'of': ['u kk', 'uk'], + 'octopus': ['y', 'ye', 'sea', 'seas'], + 'or': ['gah some', 'gah sum', 'gg y', 'gee'], + 'ahh': ['zzz', 'sees', '$', "'s"], + 'socks': ['it', 'on it', 'my deck', 'my dock', 'my doc', 'ur deck', 'ur dock', 'your deck', 'your dock', 'his deck', 'his dock', 'cooks', 'cook', 'my cook', 'ur cook', 'your cook', 'his cook', 'my cooks', 'ur cooks', 'your cooks', 'his cooks', 'deck', 'decks', 'a dock', 'a deck', 'a docks', 'a decks', 'a cook', 'a cooks', 'my duck', 'my ducks', 'dock'], + 'son': ['of a bit', 'of a peach', 'of a be', 'of a bee'], + 'ayyy': ['ds'], + '.': ['y .'], + 'pooh': ['cee', 'say', 'says', 'sea', 'seas', 'see', 'sees', 'she', 'shes', "she's", 'si'], + 'shhhh': ['it', 'eat', 'hit', 'hits', 'its', 'ex i', 'he mail', 'he mails', 'he mailed', 'he mailing', 'he male', 'he males', 'ii tt', 'i it', 'i tt'], + 'sock': ['it', 'on it', 'my deck', 'my dock', 'my doc', 'ur deck', 'ur dock', 'your deck', 'your dock', 'his deck', 'his dock', 'cooks', 'cook', 'my cook', 'ur cook', 'your cook', 'his cook', 'my cooks', 'ur cooks', 'your cooks', 'his cooks', 'deck', 'decks', 'a dock', 'a deck', 'a docks', 'a decks', 'a cook', 'a cooks', 'my duck', 'my ducks', 'dock'], + 'stuck': ['my duck', 'my dock', 'my deck', 'ing'], + 'wat': ['the hello', 'the fork', 'the duck', 'the freaky', 'the yuck', 'your sky', 'ur sky', 'the fire truck', 'the truck', "the he'll"], + 'fork': ['err', 'error', 'errors', 'ear', 'ears', 'you', 'this', 'my life', 'everyone', 'me', 'off', 'king', 'ing', 'her', 'eng', 'in', 'u', 'ur', 'hair', 'air'], + 'jacks': ['ed', 'ing', 'me', 'myself', 'her', 'herself', 'him', 'himself', 'of', 'off', 'ourselves', 'they', 'themselves', 'us', 'you', 'yourself', 'u late'], + 'but': ['hole', 'whole', 'plug', 'plugs', 'sec', 'toll', 'head', 'face'], + 'reed': ['tube', 'tubes'], + 'hm': ['arr y juan', 'arr y jane', 'arr y jan', 'arr y jam'], + 'eat': ['me out', 'you out', 'u out', 'her out'], + 'he': ['ill', 'ii', 'ell', 'el'], + 'made': ['him hard', 'love'], + 'glory': ['hole', 'whole', 'ole', 'ooo le', 'holes'], + 'shhhhhh': ['it', 'eat', 'hit', 'hits', 'its', 'ex i', 'he mail', 'he mails', 'he mailed', 'he mailing', 'he male', 'he males', 'ii tt', 'i it', 'i tt'], + 'ur': ['but', 'bum', 'as', 'period', 'hole', 'holes'], + 'un': ['tee'], + 'uk': ['you', 'u'], + 'piece': ['of shift', 'of ship', 'of shut', 'of shirt'], + 'dill': ['doe', 'do', 'dot', 'does'], + 'ai': ['ds'], + 'ah': ['zzz', 'sees', '$', "'s"], + 'al': ['coco ol', 'cool', 'a ack bar', 'ah ack bar', 'ah ache bar', 'ah snack bar'], + 'an': ['a hon', 'a honda', 'a con', 'a cone', 'ail', 'ails', 'ailed', 'ailing', 'al', 'ale', 'ales', 'all', 'awl', 'us', 'u.s.', 'u.s.a.', 'u si', 'usa', 'use', 'used', 'using', 'uses', 'uss'], + 'as': ['hole', 'whole', 'ole', 'ooo le', 'holes', 'zzz', "'s"], + 'ay': ['ds'], + 'girl': ['1 cup', 'one cup', 'on cup', 'won cup', 'and a cup', 'plus a cup'], + 'fill': ['my kitty', 'your kitty', 'her kitty', 'his kitty', 'their kitty', 'ur kitty'], + 'nag': ['zen', 'zeke', 'z.z.', 'grr', 'grrr', 'a', 'ah'], + 'whee': ['ed', 'neigh is', 'knees'], + 'nah': ['zen', 'zeke', 'z.z.', 'gg a', 'gg ah'], + 'peace': ['of shift', 'of ship', 'of shut', 'of shirt'], + 'boot': ['bee', 'bees', 'ty', 'y'], + 'nap': ['chat'], + 'other': ['flick', 'flicker', 'fork', 'fuchsia', 'duck', 'folk', 'folks', 'yuck', 'flock', 'heck', 'truck', 'funky', 'flunky', 'fax', 'quacker', 'bucker', 'bicker'], + 'ducked': ['your', 'ur', 'his', 'her', 'you'], + 'test': ['i cools', 'i cool', 'tickle', 'tickles'], + 'you': ['a hole', 'in me'], + 'nay': ['kit', 'grow', 'growl', 'gah', 'gg a', 'gg ah'], + 'roll': ['grass', 'in the hay', 'in the hey'], + "'s": ['exe', 'kk zzz', 'k zzz', 'ex', 'hit', 'hits', 'u kk', 'uk'], + "'n": ['i gg', 'i grow', 'i gross', 'i grr', 'i grrr', 'i gah'], + 'brass': ['hole', 'whole', 'ole', 'ooo le', 'holes'], + 'sass': ['hole', 'whole', 'ole'], + 'hmm': ['arr y juan', 'arr y jane', 'arr y jan', 'arr y jam'], + 'flowerpot': ['head', 'headed', 'heading', 'heads'], + 'mast': ['are bait', 'are baits', 'are baiter', 'are bait eng', 'are bait ed', 'stir bait'], + 'mass': ['hole', 'whole', 'ole', 'ooo le', 'stir bait'], + 'came': ['bucket', 'buckets', 'dumpster', 'on me', 'tastes', 'in you', 'in u'], + 'push': ['y', 'ye', 'cee', 'say', 'says', 'sea', 'seas', 'see', 'sees', 'she', 'shes', "she's", 'si'], + 'ashton': ['hole', 'whole', 'ole', 'ooo le', 'holes', 'zzz', "'s"], + 'having': ['sec', 'see ex'], +} diff --git a/otp/chat/TalkAssistant.py b/otp/chat/TalkAssistant.py new file mode 100755 index 00000000..f7f413bf --- /dev/null +++ b/otp/chat/TalkAssistant.py @@ -0,0 +1,52 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import DirectObject +from otp.chat.ChatGlobals import * +from otp.nametag.NametagConstants import * +import ChatUtil + +class TalkAssistant(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TalkAssistant') + + def delete(self): + self.ignoreAll() + + def start(self): + pass + + def stop(self): + pass + + def sendOpenTalk(self, message): + if len(message) > 0 and message[0] == '~': + messenger.send('magicWord', [message]) + else: + chatFlags = CFSpeech | CFTimeout + if ChatUtil.isThought(message): + chatFlags = CFThought + base.cr.chatAgent.sendChatMessage(message) + messenger.send('chatUpdate', [message, chatFlags]) + + def sendWhisperTalk(self, message, receiverAvId): + base.cr.ttsFriendsManager.sendUpdate('sendTalkWhisper', [receiverAvId, message]) + + def sendOpenSpeedChat(self, type, messageIndex): + if type == SPEEDCHAT_NORMAL: + messenger.send(SCChatEvent) + messenger.send('chatUpdateSC', [messageIndex]) + base.localAvatar.b_setSC(messageIndex) + elif type == SPEEDCHAT_EMOTE: + messenger.send('chatUpdateSCEmote', [messageIndex]) + messenger.send(SCEmoteChatEvent) + base.localAvatar.b_setSCEmote(messageIndex) + elif type == SPEEDCHAT_CUSTOM: + messenger.send('chatUpdateSCCustom', [messageIndex]) + messenger.send(SCCustomChatEvent) + base.localAvatar.b_setSCCustom(messageIndex) + + def sendAvatarWhisperSpeedChat(self, type, messageIndex, receiverId): + if type == SPEEDCHAT_NORMAL: + base.localAvatar.whisperSCTo(messageIndex, receiverId) + elif type == SPEEDCHAT_EMOTE: + base.localAvatar.whisperSCEmoteTo(messageIndex, receiverId) + elif type == SPEEDCHAT_CUSTOM: + base.localAvatar.whisperSCCustomTo(messageIndex, receiverId) diff --git a/otp/chat/WhiteList.py b/otp/chat/WhiteList.py new file mode 100755 index 00000000..4a513f89 --- /dev/null +++ b/otp/chat/WhiteList.py @@ -0,0 +1,87 @@ +from bisect import bisect_left +import re + +class WhiteList: + + def __init__(self): + self.sequenceList = [] + + def setWords(self, words): + self.words = words + self.numWords = len(self.words) + + def setSequenceList(self, sequences): + self.sequenceList = sequences + + def getSequenceList(self, word): + return self.sequenceList[word] if word and word in self.sequenceList else None + + def cleanText(self, text): + return text.strip('.,?!').lower() + + def isWord(self, text): + return self.cleanText(text) in self.words + + def isPrefix(self, text): + text = self.cleanText(text) + i = bisect_left(self.words, text) + + return i != self.numWords and self.words[i].startswith(text) + + def getReplacement(self, text, av=None, garbler=None): + if av and av == base.localAvatar: + return '\x01WLDisplay\x01%s\x02' % text + elif not garbler: + return '\x01WLRed\x01%s\x02' % text + return garbler.garble(av, len(text.split(' '))) + + def processText(self, text, av=None, garbler=None): + if not self.words: + return text + + words = text.split(' ') + newWords = [] + + for word in words: + if (not word) or self.isWord(word): + newWords.append(word) + else: + newWords.append(self.getReplacement(word, av, garbler)) + + lastWord = words[-1] + + if not garbler: + if (not lastWord) or self.isPrefix(lastWord): + newWords[-1] = lastWord + else: + newWords[-1] = self.getReplacement(lastWord, av, garbler) + + return ' '.join(newWords) + + def processSequences(self, text, av=None, garbler=None): + if not self.sequenceList: + return text + + words = text.split(' ') + + for wordNum in xrange(len(words)): + word = words[wordNum].lower() + sequences = self.getSequenceList(word) + + if not sequences: + continue + + for sequenceNum in xrange(len(sequences)): + sequence = sequences[sequenceNum].split() + total = wordNum + len(sequence) + 1 + + if total <= len(words) and sequence == [word.lower() for word in words[wordNum + 1:total]]: + words[wordNum:total] = self.getReplacement(' '.join(words[wordNum:total]), av, garbler).split() + + return ' '.join(words) + + def processThroughAll(self, text, av=None, garbler=None): + if (text.startswith('~') and not garbler): + return text + + return self.processSequences(self.processText(re.sub(' +', ' ', text), av, garbler), av, garbler) diff --git a/otp/chat/WhiteListData.py b/otp/chat/WhiteListData.py new file mode 100755 index 00000000..e99854de --- /dev/null +++ b/otp/chat/WhiteListData.py @@ -0,0 +1,36872 @@ +WHITELIST = [ + '', + '!', + '"', + '$', + '$1', + '$10', + '$5', + '%', + '%s', + '&', + "'", + "'boss", + "'cause", + "'course", + "'ello", + "'em", + "'n", + "'s", + '(', + '(:', + '(=<', + '(>^.^)>', + ')', + '):', + ')=<', + '*', + '*scared', + '+', + ',', + '-', + '-.-', + '-.-"', + "-.-'", + '-_-', + '-_-"', + "-_-'", + '-_-:', + '.', + '...', + '....', + '...?', + '/', + '/:', + '/=', + '0', + '0%', + '0.o', + '00', + '000', + '0:', + '0_0', + '0_o', + '1', + '1+', + '1.5x', + '10', + '10%', + '10+', + '100', + '100%', + '1000', + '10000', + '101', + '102', + '103', + '104', + '105', + '106', + '107', + '108', + '109', + '10th', + '11', + '11+', + '110', + '110%', + '111', + '112', + '113', + '114', + '115', + '116', + '117', + '118', + '119', + '12', + '12+', + '120', + '121', + '122', + '123', + '124', + '125', + '126', + '127', + '128', + '129', + '13', + '130', + '131', + '132', + '133', + '1337', + '134', + '135', + '136', + '137', + '138', + '139', + '14', + '140', + '141', + '142', + '143', + '144', + '145', + '15', + '16', + '17', + '18', + '19', + '1994', + '1995', + '1996', + '1997', + '1998', + '1999', + '1st', + '2', + '2+', + '2.0', + '2.5x', + '20', + '20%', + '200', + '2000', + '2001', + '2002', + '2003', + '2004', + '2005', + '2006', + '2007', + '2008', + '2009', + '2010', + '2011', + '2012', + '2013', + '2014', + '2015', + '21', + '22', + '23', + '24', + '25', + '25%', + '26', + '27', + '28', + '29', + '2d', + '2nd', + '2x', + '3', + '3+', + '30', + '30%', + '300', + '31', + '32', + '33', + '34', + '35', + '36', + '360', + '37', + '38', + '39', + '3d', + '3rd', + '3x', + '4', + '4+', + '40', + '40%', + '400', + '41', + '42', + '43', + '44', + '45', + '46', + '47', + '48', + '49', + '4th', + '4x', + '5', + '5%', + '5+', + '50', + '50%', + '500', + '51', + '52', + '53', + '54', + '55', + '56', + '57', + '58', + '59', + '5th', + '5x', + '6', + '6+', + '60', + '60%', + '600', + '61', + '62', + '63', + '64', + '65', + '66', + '67', + '68', + '69', + '6th', + '6x', + '7', + '7+', + '70', + '70%', + '700', + '71', + '72', + '73', + '74', + '75', + '75%', + '76', + '77', + '78', + '79', + '7th', + '8', + '8+', + '80', + '80%', + '800', + '81', + '82', + '83', + '84', + '85', + '85%', + '86', + '87', + '88', + '89', + '8th', + '9', + '9+', + '90', + '90%', + '900', + '9001', + '91', + '92', + '93', + '94', + '95', + '96', + '97', + '98', + '99', + '9th', + ':', + ":'(", + ":')", + ":'o(", + ':(', + ':)', + ':*', + ':-(', + ':-)', + ':-o', + ':/', + ':0', + ':3', + ':::<', + ':<', + ':>', + ':[', + ':]', + ':^)', + ':_', + ':b', + ':c', + ':d', + ':i', + ':j', + ':o', + ':o&', + ':o)', + ':p', + ':s', + ':v', + ':x', + ':|', + ';', + ';)', + ';-)', + ';-;', + ';3', + ';;', + ';_;', + ';c', + ';d', + ';p', + ';x', + '<', + '<(^.^<)', + '<.<', + '', + '>.<', + '>.>', + '>:', + '>:(', + '>:)', + '>:c', + '>=(', + '>=)', + '>=d', + '>_<', + '>_>', + '>~<', + '?', + '@.@', + '@_@', + '@o@', + '[', + ']', + '^', + '^.^', + '^^', + '^_^', + '_', + 'a', + 'a-hem', + 'a-oo-oo-oo-ooo', + 'aa', + 'aacres', + 'aah', + 'aardvark', + "aardvark's", + 'aardvarks', + 'aarg', + 'aargghh', + 'aargh', + 'aaron', + 'aarrgghh', + 'aarrgghhh', + 'aarrm', + 'aarrr', + 'aarrrgg', + 'aarrrgh', + 'aarrrr', + 'aarrrrggg', + 'aarrrrr', + 'aarrrrrr', + 'aarrrrrrr', + 'aarrrrrrrrr', + 'aarrrrrrrrrghhhhh', + 'aay', + 'abacus', + 'abaft', + 'abalone-shell', + 'abandon', + 'abandoned', + 'abandoner', + 'abandoning', + 'abandons', + 'abassa', + 'abay-ba-da', + 'abbot', + 'abbrev', + 'abbreviate', + 'abbreviated', + 'abbreviation', + 'abbreviations', + 'abby', + 'abe', + 'abeam', + 'aberrant', + 'abhor', + 'abhors', + 'abi', + 'abide', + 'abigail', + 'abilities', + 'ability', + "ability's", + 'abira', + 'able', + 'able-bodied', + 'abler', + 'ablest', + 'abnormal', + 'aboard', + 'abode', + 'abominable', + 'abound', + 'abounds', + 'about', + 'above', + 'abracadabra', + 'abraham', + 'abrasive', + 'abrupt', + 'abruptly', + 'absence', + "absence's", + 'absences', + 'absent', + 'absent-minded', + 'absented', + 'absenting', + 'absently', + 'absents', + 'absolute', + 'absolutely', + 'absolutes', + 'absolution', + 'absorb', + 'absorbs', + 'abstemious', + 'absurd', + 'absurdly', + 'abu', + "abu's", + 'abundant', + 'academic', + 'academics', + 'academies', + 'academy', + "academy's", + 'acc', + 'accelerate', + 'accelerated', + 'accelerates', + 'acceleration', + 'accelerator', + 'accelerators', + 'accent', + 'accented', + 'accents', + 'accentuate', + 'accentuates', + 'accept', + 'acceptable', + 'acceptance', + 'accepted', + 'accepter', + "accepter's", + 'accepters', + 'accepting', + 'acceptive', + 'accepts', + 'access', + 'accessed', + 'accesses', + 'accessibility', + 'accessing', + 'accessories', + 'accessorize', + 'accessory', + 'accident', + "accident's", + 'accidental', + 'accidentally', + 'accidently', + 'accidents', + 'accolade', + 'accompanies', + 'accompany', + 'accompanying', + 'accomplice', + 'accomplish', + 'accomplished', + 'accomplishes', + 'accomplishing', + 'accomplishment', + 'accord', + 'according', + 'accordingly', + 'accordion', + 'accordions', + 'accountable', + 'accountant', + 'accounted', + 'accounting', + 'accountings', + 'accounts', + 'accrue', + 'acct', + "acct's", + 'accts', + 'accumulate', + 'accumulated', + 'accumulating', + 'accumulator', + 'accumulators', + 'accuracy', + 'accurate', + 'accurately', + 'accursed', + 'accusation', + 'accusations', + 'accuse', + 'accused', + 'accuser', + 'accusers', + 'accuses', + 'accusing', + 'accustomed', + 'ace', + "ace's", + 'aced', + 'aces', + 'ach', + 'ache', + 'ached', + 'aches', + 'achieve', + 'achieved', + 'achievement', + "achievement's", + 'achievements', + 'achiever', + 'achievers', + 'achieves', + 'achieving', + 'aching', + 'achoo', + 'achy', + 'ack', + 'acknowledge', + 'acknowledged', + 'acknowledgement', + 'acme', + 'acorn', + 'acorns', + 'acoustic', + 'acoustics', + 'acquaintance', + 'acquaintances', + 'acquainted', + 'acquiesce', + 'acquire', + 'acquired', + 'acquires', + 'acquiring', + 'acquit', + 'acre', + 'acres', + 'acrobat', + 'acron', + 'acronym', + 'acronyms', + 'across', + 'acrylic', + 'acsot', + 'act', + "act's", + 'acted', + 'acting', + 'action', + "action's", + 'action-figure', + 'actions', + 'activate', + 'activated', + 'activates', + 'activating', + 'activation', + 'active', + 'actively', + 'activies', + 'activist', + 'activities', + 'activity', + "activity's", + 'actor', + "actor's", + 'actors', + 'actress', + "actress's", + 'actresses', + 'acts', + 'actual', + 'actually', + 'actuals', + 'acuda', + 'acupuncture', + 'ad', + "ad's", + 'adam', + 'adamant', + 'adapt', + 'adapted', + 'adapter', + 'adaptor', + 'adaptors', + 'add', + 'add-', + 'added', + 'adder', + 'adders', + 'adding', + 'addison', + 'addition', + "addition's", + 'additional', + 'additionally', + 'additions', + 'addle', + 'addled', + 'addressed', + 'addresses', + 'addressing', + 'adds', + 'adella', + 'adept', + 'adeptly', + 'adequate', + 'adequately', + 'adhere', + 'adhered', + 'adheres', + 'adhering', + 'adhesive', + 'adieu', + 'adios', + 'adjective', + 'adjoined', + 'adjoining', + 'adjourn', + 'adjourned', + 'adjudicator', + 'adjust', + 'adjusted', + 'adjuster', + "adjuster's", + 'adjusters', + 'adjusting', + 'adjustive', + 'adjustment', + "adjustment's", + 'adjustments', + 'adjusts', + 'admin', + 'administrative', + 'administrator', + 'administrators', + 'admins', + 'admirable', + 'admirably', + 'admiral', + "admiral's", + 'admirals', + 'admiralty', + 'admiration', + 'admire', + 'admired', + 'admirer', + "admirer's", + 'admirers', + 'admires', + 'admiring', + 'admission', + 'admissions', + 'admit', + 'admits', + 'admittance', + 'admitted', + 'admittedly', + 'admitting', + 'ado', + 'adobe', + 'adopt', + 'adopted', + 'adopting', + 'adopts', + 'adorable', + 'adoration', + 'adore', + 'adored', + 'adores', + 'adoria', + 'adoring', + 'adrenalin', + 'adrenaline', + 'adriaan', + 'adrian', + "adrian's", + 'adrienne', + "adrienne's", + 'adrift', + 'ads', + 'adults', + 'adv', + 'advance', + 'advanced', + 'advancer', + 'advancers', + 'advances', + 'advancing', + 'advantage', + 'advantaged', + 'advantages', + 'advantaging', + 'advent', + 'adventure', + 'adventured', + 'adventureland', + "adventureland's", + 'adventurer', + "adventurer's", + 'adventurers', + 'adventures', + 'adventuring', + 'adventurous', + 'adversary', + 'adverse', + 'advert', + 'advertise', + 'advertised', + 'advertisement', + 'advertisements', + 'advertising', + 'adverts', + 'advice', + 'advices', + 'advisable', + 'advise', + 'advised', + 'adviser', + 'advisers', + 'advises', + 'advising', + 'advisor', + 'advocacy', + 'advocate', + 'adware', + 'adz', + "adz's", + 'aerobic', + 'aerobics', + 'aerodynamic', + 'aesthetically', + 'afar', + 'affect', + 'affected', + 'affecter', + 'affecting', + 'affection', + 'affectionate', + 'affections', + 'affective', + 'affects', + 'affiliate', + 'affiliation', + 'affirmation', + 'affirmative', + 'affixed', + 'afflict', + 'afflicted', + 'affliction', + 'afford', + 'affordable', + 'afforded', + 'affording', + 'affords', + 'afire', + 'afk', + 'afloat', + 'afloatin', + 'afloats', + 'afn', + 'afoot', + 'afore', + 'afoul', + 'afraid', + 'africa', + 'aft', + 'afta', + 'after', + 'afterburner', + 'afterburners', + 'afterlife', + 'afternoon', + 'afternoons', + 'aftershave', + 'afterthought', + 'afterward', + 'afterwards', + 'again', + 'against', + 'agatha', + 'age', + 'aged', + 'ageless', + 'agencies', + 'agency', + 'agenda', + 'agent', + "agent's", + 'agentive', + 'agents', + 'ages', + 'aggravate', + 'aggravated', + 'aggravates', + 'aggravating', + 'aggravation', + 'aggregation', + 'aggression', + 'aggressions', + 'aggressive', + 'aggressively', + 'aggressiveness', + 'aggrieved', + 'aggro', + 'agile', + 'agility', + 'aging', + 'agitated', + 'aglow', + 'ago', + 'agony', + 'agora', + 'agoraphobia', + 'agoraphobic', + 'agrabah', + "agrabah's", + 'agree', + 'agreeable', + 'agreed', + 'agreeing', + 'agreement', + "agreement's", + 'agreements', + 'agreer', + 'agreers', + 'agrees', + 'agro', + 'aground', + 'ah', + 'aha', + "ahab's", + 'ahead', + 'ahem', + 'ahh', + 'ahhh', + 'ahhhh', + 'ahhhhh', + 'ahhhhhh', + 'ahhhhhhh', + 'ahhhhhhhh', + 'ahhhhhhhhh', + 'ahhhhhhhhhh', + 'ahhhhhhhhhhh', + 'ahhhhhhhhhhhh', + 'ahhhhhhhhhhhhh', + 'ahhhhhhhhhhhhhh', + 'ahhhhhhhhhhhhhhhh', + 'ahhhhhhhhhhhhhhhhh', + 'ahhhhhhhhhhhhhhhhhh', + 'ahhhhhhhhhhhhhhhhhhh', + 'ahhhhhhhhhhhhhhhhhhhhh', + 'ahhhhhhhhhhhhhhhhhhhhhhhhhhh', + 'ahhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh', + 'ahhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh', + 'ahoy', + 'ahs', + 'ai', + 'aid', + 'aide', + 'aided', + 'aiden', + "aiden's", + 'aiding', + 'aight', + 'ail', + 'ailed', + 'aileen', + 'ailing', + 'ailment', + 'ailments', + 'ails', + 'aim', + 'aimed', + 'aimer', + 'aimers', + 'aiming', + 'aimless', + 'aimlessly', + 'aims', + "ain't", + 'aint', + 'air', + 'airbags', + 'airborne', + 'aircraft', + 'aircrew', + 'aired', + 'airer', + 'airers', + 'airhead', + "airhead's", + 'airheads', + 'airing', + 'airings', + 'airlock', + 'airplane', + "airplane's", + 'airplanes', + 'airport', + "airport's", + 'airports', + 'airs', + 'airship', + 'airships', + 'airwaves', + 'aisle', + 'aj', + "aj's", + 'aka', + 'akaboshi', + 'akin', + 'akon', + 'al', + "al's", + 'ala', + 'alabaster', + 'aladdin', + "aladdin's", + 'alameda', + "alameda's", + 'alamedas', + 'alan', + "alan's", + 'alana', + "alana's", + 'alarm', + 'alarmed', + 'alarming', + 'alarms', + 'alas', + 'alb', + 'alba', + 'albatross', + 'albeit', + 'albert', + 'alberto', + 'albino', + 'album', + 'albums', + 'alchemic', + 'alcove', + 'aldous', + 'aldrin', + "aldrin's", + 'ale', + 'alec', + 'alehouse', + 'alert', + 'alerted', + 'alerting', + 'alerts', + 'ales', + 'alex', + "alex's", + 'alexa', + 'alexalexer', + 'alexander', + 'alexia', + "alexis'", + 'alfa', + 'alfredo', + 'algebra', + 'algeria', + 'algorithm', + 'ali', + 'alias', + 'aliases', + 'alibi', + 'alibis', + 'alice', + "alice's", + 'alien', + "alien's", + 'alienated', + 'aliens', + 'alight', + 'align', + 'aligned', + 'alignment', + 'alike', + 'alina', + 'alive', + 'alkies', + 'alky', + 'all', + 'all-4-fun', + 'all-in', + 'all-new', + 'all-out', + 'all-small', + 'all-star', + 'all-stars', + 'allaince', + 'allegiance', + 'allegory', + 'allergic', + 'allergies', + 'allergy', + 'alley', + 'alleys', + 'allianc', + 'alliance', + 'alliances', + 'allied', + 'allies', + 'alligator', + "alligator's", + 'alligators', + "alligators'", + 'allin', + 'allocate', + 'allocated', + 'allocation', + 'allosaurus', + 'allot', + 'allover', + 'allow', + 'allowable', + 'allowance', + "allowance's", + 'allowanced', + 'allowances', + 'allowancing', + 'allowed', + 'allowing', + 'allows', + 'alls', + 'allsorts', + 'alludes', + 'allure', + 'ally', + "ally's", + 'alma', + 'almighty', + 'almond', + 'almost', + 'alodia', + 'aloe', + 'aloft', + 'aloha', + 'alone', + 'along', + 'alongside', + 'alot', + 'aloud', + 'alpha', + 'alphabet', + 'alphabetical', + 'alphabetically', + 'alphas', + 'alpine', + 'alps', + 'already', + 'alright', + 'alrighty', + 'also', + 'alt', + 'altar', + 'altar-ego', + 'alter', + 'alterations', + 'altercations', + 'altered', + 'altering', + 'alternate', + 'alternately', + 'alternates', + 'alternating', + 'alternative', + 'alternatively', + 'alternatives', + 'alternator', + 'alters', + 'althea', + 'although', + 'altitude', + 'alto', + 'altogether', + 'altos', + 'altruistic', + 'alts', + 'aluminum', + 'alumni', + 'always', + 'aly', + "aly's", + 'alyson', + "alyson's", + 'am', + 'amanda', + 'amaryllis', + 'amass', + 'amassing', + 'amateur', + 'amaze', + 'amazed', + 'amazes', + 'amazing', + 'amazingly', + 'amazon', + "amazon's", + 'amazons', + 'ambassador', + "ambassador's", + 'ambassadors', + 'amber', + 'ambidextrous', + 'ambiguous', + 'ambition', + "ambition's", + 'ambitions', + 'ambitious', + 'ambitiously', + 'ambrosia', + 'ambulance', + 'ambulances', + 'ambush', + 'ambushed', + 'ambushes', + 'ambushing', + 'amelia', + 'amen', + 'amenable', + 'amend', + 'amending', + 'amends', + 'amenities', + 'america', + 'american', + 'amethyst', + 'amidships', + 'amidst', + 'amigas', + 'amigo', + 'amigos', + 'amiibo', + 'amiibos', + 'amine', + 'amiss', + 'amnesia', + 'among', + 'amongst', + 'amore', + 'amos', + 'amount', + 'amounted', + 'amounter', + 'amounters', + 'amounting', + 'amounts', + 'amp', + 'amphitrite', + 'ample', + 'amputated', + 'amry', + 'ams', + 'amt', + 'amts', + 'amuck', + 'amulets', + 'amuse', + 'amused', + 'amusement', + "amusement's", + 'amusements', + 'amuses', + 'amusing', + 'amy', + 'an', + "an'", + 'ana', + 'anachronistic', + 'analese', + 'analog', + 'analytical', + 'analyze', + 'analyzing', + 'anarchy', + 'anatomy', + 'ancestors', + 'anchor', + 'anchorage', + 'anchored', + 'anchoring', + 'anchors', + 'anchovies', + 'ancient', + 'anciently', + 'ancients', + 'and', + 'andaba', + 'andago', + 'andaire', + 'andama', + 'anddd', + 'andi', + 'andila', + 'andira', + 'andoso', + 'andrea', + "andrea's", + 'andrew', + 'andrina', + "andrina's", + 'andro', + 'andros', + 'andumal', + 'andy', + 'anegola', + 'anegoso', + 'anemic', + 'anemones', + 'anent', + 'anew', + 'angaba', + 'angama', + 'angassa', + 'ange', + 'angel', + "angel's", + 'angelfish', + 'angelfood', + 'angels', + 'anger', + 'angered', + 'angering', + 'angers', + 'angle', + 'angled', + 'angler', + "angler's", + 'anglers', + 'angles', + 'angling', + 'angrier', + 'angries', + 'angriest', + 'angrily', + 'angry', + 'angst', + 'angus', + 'animal', + "animal's", + 'animal-talent', + 'animal-talents', + 'animally', + 'animals', + 'animate', + 'animated', + 'animates', + 'animatings', + 'animation', + 'animations', + 'animator', + "animator's", + 'animators', + 'anime', + 'anita', + 'ankle', + 'anklet', + 'anklets', + 'ankoku', + 'ann', + "ann's", + 'anna', + "anna's", + 'anne', + "anne's", + 'anneliese', + "anneliese's", + 'annie', + "annie's", + 'annihilate', + 'annihilated', + 'annihilation', + 'anniversary', + 'annos', + 'annotate', + 'announce', + 'announced', + 'announcement', + 'announcements', + 'announcer', + "announcer's", + 'announcers', + 'announces', + 'announcing', + 'annoy', + 'annoyance', + "annoyance's", + 'annoyances', + 'annoyed', + 'annoyer', + 'annoyers', + 'annoying', + 'annoyingly', + 'annoys', + 'annual', + 'annually', + 'annuals', + 'annul', + 'anomaly', + 'anon', + 'anonymity', + 'another', + "another's", + 'anselmo', + 'answer', + 'answered', + 'answerer', + 'answerers', + 'answering', + 'answers', + 'ant', + "ant's", + 'antacid', + 'antagonist', + 'antagonize', + 'antagonized', + 'antagonizing', + 'antama', + 'antarctic', + 'antassa', + 'ante', + 'antelope', + "antelope's", + 'antelopes', + 'antenna', + 'antes', + 'anthem', + 'anther', + 'anthers', + 'anthill', + "anthill's", + 'anthills', + 'anthony', + 'anthropology', + 'anti', + 'anti-cog', + 'anti-gravity', + 'antiano', + 'antibacterial', + 'antibiotic', + 'antibiotics', + 'antibodies', + 'antic', + 'anticipate', + 'anticipated', + 'anticipating', + 'anticipation', + 'anticipatively', + 'anticlimactic', + 'antics', + 'antidisestablishmentarianism', + 'antigue', + 'antik', + 'antima', + 'antios', + 'antique', + 'antiques', + 'antiros', + 'antis', + 'antisocial', + 'antivirus', + 'anton', + "anton's", + 'ants', + 'antsy', + 'antumal', + 'anuberos', + 'anubi', + 'anubos', + 'anvil', + 'anvils', + 'anxieties', + 'anxiety', + 'anxious', + 'anxiously', + 'any', + 'anybodies', + 'anybody', + "anybody's", + 'anyhow', + 'anymore', + 'anyone', + "anyone's", + 'anyones', + 'anyplace', + 'anything', + "anything's", + 'anythings', + 'anytime', + 'anywas', + 'anyway', + 'anyways', + 'anywhere', + 'anywheres', + 'aoba', + 'aobasar', + 'aoboshi', + 'aoi', + 'aoogah', + 'aoogahs', + 'aoteoroa', + 'apart', + 'apartment', + 'apartments', + 'apathetic', + 'apathy', + 'ape', + "ape's", + 'apes', + 'apex', + 'apiece', + 'aplenty', + 'apocalyptyca', + 'apodous', + 'apologies', + 'apologize', + 'apologized', + 'apologizes', + 'apologizing', + 'apology', + "apology's", + 'apostles', + 'apostrophe', + "apostrophe's", + 'apostrophes', + 'app', + 'appalled', + 'apparel', + 'apparent', + 'apparently', + 'appeal', + 'appealed', + 'appealer', + 'appealers', + 'appealing', + 'appeals', + 'appear', + 'appearance', + 'appearances', + 'appeared', + 'appearer', + 'appearers', + 'appearing', + 'appears', + 'appease', + 'appeasing', + 'append', + 'appendices', + 'appendix', + 'appetite', + 'appetites', + 'appetizer', + 'appetizers', + 'appetizing', + 'applaud', + 'applauded', + 'applauder', + 'applauding', + 'applauds', + 'applause', + 'apple', + 'apples', + 'applesauce', + 'appliances', + 'applicable', + 'applicants', + 'application', + "application's", + 'applications', + 'applied', + 'applier', + 'appliers', + 'applies', + 'apply', + 'applying', + 'appoint', + 'appointed', + 'appointer', + 'appointers', + 'appointing', + 'appointive', + 'appointment', + 'appointments', + 'appoints', + 'appose', + 'apposed', + 'appreciate', + 'appreciated', + 'appreciates', + 'appreciation', + 'appreciative', + 'apprehension', + 'apprehensive', + 'apprentice', + 'approach', + 'approached', + 'approacher', + 'approachers', + 'approaches', + 'approaching', + 'appropriate', + 'appropriated', + 'appropriately', + 'appropriates', + 'appropriatest', + 'appropriating', + 'appropriation', + 'appropriations', + 'appropriative', + 'approval', + 'approve', + 'approved', + 'approver', + "approver's", + 'approvers', + 'approves', + 'approving', + 'approx', + 'approximate', + 'approximately', + 'apps', + 'appt', + 'apr', + 'apricot', + 'april', + "april's", + 'apron', + 'apt', + 'aptly', + 'aqua', + 'aquablue', + 'aquarium', + 'aquariums', + 'aquatic', + 'aquatta', + 'arabian', + 'arbitrage', + 'arbitrarily', + 'arbitrary', + 'arbor', + 'arc', + "arc's", + 'arcade', + 'arcades', + 'arcadia', + 'arcane', + 'arch', + 'archaeology', + 'archaic', + 'archer', + "archer's", + 'archers', + 'arches', + 'archibald', + 'architect', + 'architects', + 'architecture', + 'archway', + 'archways', + 'arctic', + "arctic's", + 'are', + 'area', + "area's", + 'areas', + "aren't", + 'arena', + 'arenas', + 'arenberg', + "arenberg's", + 'arent', + 'arf', + 'arfur', + 'arg', + 'argentina', + 'argg', + 'arggest', + 'arggg', + 'argggg', + 'arggggg', + 'arggggge', + 'argggggg', + 'argggggggggggg', + 'argggggggh', + 'argggghhh', + 'argggh', + 'arggghhh', + 'arggh', + 'argghh', + 'argghhh', + 'argghhhh', + 'argh', + 'arghgh', + 'arghghghggh', + 'arghh', + 'arghhh', + 'arghhhh', + 'arghhhhh', + 'arghhhhhhhhhhhhhhhhhh', + 'argon', + 'argue', + 'argued', + 'arguer', + "arguer's", + 'arguers', + 'argues', + 'arguing', + 'argument', + "argument's", + 'arguments', + 'argust', + 'aria', + 'ariana', + 'arianna', + 'ariel', + "ariel's", + 'ariela', + 'aright', + 'aril', + 'arising', + 'arista', + 'aristocat', + "aristocat's", + 'aristocats', + 'aristocratic', + 'ark', + 'arks', + 'arm', + "arm's", + 'armada', + 'armadas', + 'armadillo', + "armadillo's", + 'armadillos', + 'armchair', + "armchair's", + 'armchairs', + 'armed', + 'armer', + 'armers', + 'armies', + 'arming', + 'armlets', + 'armoire', + 'armor', + 'armory', + 'armpit', + 'arms', + 'armstrong', + 'army', + "army's", + 'aroma', + 'aromatic', + 'around', + 'arr', + 'arrack', + 'arraignment', + 'arrange', + 'arranged', + 'arrangement', + "arrangement's", + 'arrangements', + 'arranger', + 'arrangers', + 'arranges', + 'arranging', + 'arrant', + 'array', + 'arrest', + 'arrested', + 'arresting', + 'arrests', + 'arrgg', + 'arrggg', + 'arrgggg', + 'arrggghhh', + 'arrgghh', + 'arrgghhh', + 'arrgh', + 'arrghh', + 'arrghhh', + 'arrghhhh', + 'arrghhhhhhh', + 'arrgonauts', + 'arrival', + 'arrivals', + 'arrive', + 'arrived', + 'arrivederci', + 'arriver', + 'arrives', + 'arriving', + 'arrogant', + 'arrow', + 'arrowed', + 'arrowing', + 'arrows', + 'arrr', + 'arrrr', + 'arrrrgh', + 'arsis', + 'art', + "art's", + 'art-talent', + 'arte', + 'artezza', + 'arthritis', + 'artichoke', + 'article', + "article's", + 'articled', + 'articles', + 'articling', + 'articulate', + 'artie', + 'artifact', + 'artifacts', + 'artificial', + 'artificially', + 'artillerymen', + 'artist', + "artist's", + 'artiste', + 'artistic', + 'artists', + 'arts', + 'artwork', + 'arty', + 'aruba', + 'arwin', + "arwin's", + 'as', + 'asap', + 'asarion', + 'ascended', + 'ascending', + 'ascent', + 'ashame', + 'ashamed', + 'ashes', + 'ashley', + "ashley's", + 'ashore', + 'ashtray', + 'ashy', + 'asia', + 'aside', + 'asides', + 'ask', + 'asked', + 'asker', + 'askers', + 'asking', + 'asks', + 'aslan', + "aslan's", + 'aslans', + 'asleep', + 'asp', + 'asparagus', + 'aspect', + "aspect's", + 'aspects', + 'aspen', + 'asphalt', + 'aspiration', + 'aspirations', + 'aspire', + 'aspirin', + 'aspiring', + 'asps', + 'assemble', + 'assembled', + 'assembler', + 'assemblers', + 'assembles', + 'assembling', + 'assembly', + 'assert', + 'assertive', + 'assessment', + 'asset', + "asset's", + 'assets', + 'assign', + 'assigned', + 'assigning', + 'assignment', + 'assignments', + 'assigns', + 'assist', + 'assistance', + 'assistant', + 'assistants', + 'assisted', + 'assisting', + 'assistive', + 'assn', + 'assoc', + 'associate', + 'associated', + 'associates', + 'associating', + 'association', + "association's", + 'associations', + 'associative', + 'assorted', + 'assortment', + 'asst', + 'assume', + 'assumed', + 'assumer', + 'assumes', + 'assuming', + 'assumption', + "assumption's", + 'assumptions', + 'assurance', + 'assure', + 'assured', + 'assuredly', + 'assures', + 'aster', + 'asterisks', + 'asterius', + 'astern', + 'asteroid', + "asteroid's", + 'asteroids', + 'asthma', + 'astir', + 'aston', + 'astonish', + 'astonished', + 'astonishes', + 'astonishing', + 'astounded', + 'astounds', + 'astray', + 'astro', + "astro's", + 'astro-barrier', + 'astron', + 'astronaut', + "astronaut's", + 'astronauts', + 'astrond', + 'astronomy', + 'astroturf', + 'asuna', + 'asylum', + 'at', + 'ate', + 'atheist', + 'athlete', + 'athletes', + 'athletic', + 'athletics', + 'atlantic', + 'atlantis', + 'atlantyans', + 'atlas', + 'atm', + "atm's", + 'atmosphere', + "atmosphere's", + 'atmosphered', + 'atmospheres', + 'atms', + 'atom', + "atom's", + 'atomettes', + 'atomic', + 'atoms', + 'atone', + 'atonement', + 'atop', + 'atrocious', + 'atrocities', + 'atrocity', + 'atta', + 'attach', + 'attached', + 'attacher', + 'attachers', + 'attaches', + 'attaching', + 'attachment', + 'attachments', + 'attack', + 'attackable', + 'attacked', + 'attacker', + "attacker's", + 'attackers', + 'attacking', + 'attacks', + 'attainable', + 'attained', + 'attempt', + 'attempted', + 'attempter', + 'attempters', + 'attempting', + 'attempts', + 'attend', + 'attendance', + 'attendant', + 'attended', + 'attender', + 'attenders', + 'attending', + 'attends', + 'attention', + "attention's", + 'attentions', + 'attentive', + 'attentively', + 'attest', + 'attic', + "attic's", + 'attics', + 'attina', + 'attire', + 'attitude', + "attitude's", + 'attitudes', + 'attn', + 'attorney', + "attorney's", + 'attorneys', + 'attract', + 'attractant', + 'attracted', + 'attracting', + 'attraction', + 'attractions', + 'attractive', + 'attractively', + 'attracts', + 'attribute', + 'attributes', + 'attribution', + 'attrition', + 'attune', + 'attuned', + 'attunement', + 'attunements', + 'attunes', + 'attuning', + 'atty', + 'auburn', + 'auction', + 'audi', + 'audience', + "audience's", + 'audiences', + 'audio', + 'audit', + 'audition', + 'auditioned', + 'audits', + 'auf', + 'aug', + 'aught', + 'augmenter', + 'august', + 'auguste', + 'aula', + 'aunt', + 'auntie', + "auntie's", + 'aunties', + 'aunts', + 'aunty', + 'aura', + 'aurora', + "aurora's", + 'aurorium', + 'aurors', + 'aurours', + 'auspicious', + 'auspiciously', + 'australia', + "australia's", + 'auth', + 'authenticity', + 'author', + "author's", + 'authored', + 'authoring', + 'authoritative', + 'authorities', + 'authority', + "authority's", + 'authorization', + 'authorize', + 'authorized', + 'authors', + 'auto', + "auto's", + 'auto-reel', + 'autocratic', + 'autograph', + 'autographed', + 'autographs', + 'automated', + 'automatic', + 'automatically', + 'automatics', + 'automobile', + "automobile's", + 'automobiles', + 'autopia', + "autopia's", + 'autopilot', + 'autos', + 'autumn', + "autumn's", + 'autumns', + 'aux', + 'av', + 'avail', + 'availability', + 'available', + 'avalanche', + 'avarice', + 'avaricia', + 'avast', + 'avatar', + "avatar's", + 'avatars', + 'avater', + 'avec', + 'avenge', + 'avenged', + 'avenger', + "avenger's", + 'avengers', + 'avenging', + 'avenue', + 'aver', + 'average', + 'averaged', + 'averagely', + 'averages', + 'averaging', + 'aversion', + 'averted', + 'aviation', + 'aviator', + 'aviators', + 'avid', + 'avis', + 'avocados', + 'avoid', + 'avoidance', + 'avoided', + 'avoider', + 'avoiders', + 'avoiding', + 'avoids', + 'aw', + 'await', + 'awaiting', + 'awaits', + 'awake', + 'awaked', + 'awaken', + 'awakening', + 'awakes', + 'awaking', + 'award', + 'award-winning', + 'awarded', + 'awarder', + 'awarders', + 'awarding', + 'awards', + 'aware', + 'awareness', + 'awash', + 'away', + 'awayme', + 'awe', + 'awed', + 'aweigh', + 'awesome', + 'awesomely', + 'awesomeness', + 'awesomers', + 'awesomus', + 'awestruck', + 'awful', + 'awfully', + 'awhile', + 'awkward', + 'awkwardly', + 'awkwardness', + 'awl', + 'awn', + 'awning', + 'awnings', + 'awoke', + 'awry', + 'aww', + 'awwdai', + 'axed', + 'axel', + 'axis', + 'axisd', + 'axle', + 'axles', + 'ay', + 'aye', + 'ayes', + 'azamaros', + 'azamaru', + 'azapi', + 'azazel', + 'azeko', + 'azenor', + 'azewana', + 'aztec', + "aztec's", + 'aztecs', + 'azur', + 'b)', + 'b-day', + 'b-sharp', + 'b4', + 'babble', + 'babbles', + 'babbling', + 'babied', + 'babies', + 'baboon', + "baby's", + 'babyface', + 'babyish', + 'babysitter', + 'babysitters', + 'babysitting', + 'baccaneer', + 'bacchus', + "bacchus's", + 'bachelor', + 'bachelors', + 'back', + 'back-to-school', + 'back-up', + 'backbiters', + 'backbone', + 'backbones', + 'backcrash', + 'backdrop', + 'backed', + 'backer', + 'backers', + 'backfall', + 'backfire', + 'backfired', + 'backfires', + 'backflip', + 'backflips', + 'backginty', + 'background', + "background's", + 'backgrounds', + 'backing', + 'backpack', + "backpack's", + 'backpacking', + 'backpacks', + 'backpedaling', + 'backpedals', + 'backs', + 'backslash', + 'backspace', + 'backspaces', + 'backspacing', + 'backstabbed', + 'backstabber', + 'backstabbers', + 'backstabbing', + 'backstreet', + 'backstroke', + 'backtrack', + 'backup', + 'backups', + 'backward', + 'backwardly', + 'backwardness', + 'backwards', + 'backwash', + 'backwater', + 'backwaters', + 'backwoods', + 'backyard', + "backyard's", + 'backyards', + 'bacon', + "bacon's", + 'bacons', + 'bacteria', + 'bad', + 'baddest', + 'baddie', + 'baddies', + 'baddy', + 'bade', + 'badge', + 'badger', + "badger's", + 'badgered', + 'badgering', + 'badgers', + 'badges', + 'badlands', + 'badly', + 'badness', + 'badr', + 'baffle', + 'baffled', + 'bafflement', + 'bag', + "bag's", + 'bagel', + "bagel's", + 'bagelbee', + 'bagelberry', + 'bagelblabber', + 'bagelbocker', + 'bagelboing', + 'bagelboom', + 'bagelbounce', + 'bagelbouncer', + 'bagelbrains', + 'bagelbubble', + 'bagelbumble', + 'bagelbump', + 'bagelbumper', + 'bagelburger', + 'bagelchomp', + 'bagelcorn', + 'bagelcrash', + 'bagelcrumbs', + 'bagelcrump', + 'bagelcrunch', + 'bageldoodle', + 'bageldorf', + 'bagelface', + 'bagelfidget', + 'bagelfink', + 'bagelfish', + 'bagelflap', + 'bagelflapper', + 'bagelflinger', + 'bagelflip', + 'bagelflipper', + 'bagelfoot', + 'bagelfuddy', + 'bagelfussen', + 'bagelgadget', + 'bagelgargle', + 'bagelgloop', + 'bagelglop', + 'bagelgoober', + 'bagelgoose', + 'bagelgrooven', + 'bagelhoffer', + 'bagelhopper', + 'bageljinks', + 'bagelklunk', + 'bagelknees', + 'bagelmarble', + 'bagelmash', + 'bagelmonkey', + 'bagelmooch', + 'bagelmouth', + 'bagelmuddle', + 'bagelmuffin', + 'bagelmush', + 'bagelnerd', + 'bagelnoodle', + 'bagelnose', + 'bagelnugget', + 'bagelphew', + 'bagelphooey', + 'bagelpocket', + 'bagelpoof', + 'bagelpop', + 'bagelpounce', + 'bagelpow', + 'bagelpretzel', + 'bagelquack', + 'bagelroni', + 'bagels', + 'bagelscooter', + 'bagelscreech', + 'bagelsmirk', + 'bagelsnooker', + 'bagelsnoop', + 'bagelsnout', + 'bagelsocks', + 'bagelspeed', + 'bagelspinner', + 'bagelsplat', + 'bagelsprinkles', + 'bagelsticks', + 'bagelstink', + 'bagelswirl', + 'bagelteeth', + 'bagelthud', + 'bageltoes', + 'bagelton', + 'bageltoon', + 'bageltooth', + 'bageltwist', + 'bagelwhatsit', + 'bagelwhip', + 'bagelwig', + 'bagelwoof', + 'bagelzaner', + 'bagelzap', + 'bagelzapper', + 'bagelzilla', + 'bagelzoom', + 'bagg o. wattar', + 'baggage', + 'bagged', + 'bagger', + 'baggie', + 'bagging', + 'bagheera', + "bagheera's", + 'bagpipe', + 'bags', + "bags'", + 'bah', + 'baha', + 'bahaha', + 'bahama', + 'bahamas', + 'bahano', + 'bahh', + 'bahhh', + 'bahia', + 'bahira', + 'bai', + 'bail', + 'bailed', + 'bailey', + "bailey's", + 'baileys', + 'bailing', + 'bails', + 'bain', + 'bait', + 'baiter', + 'baiters', + 'baits', + 'bajillion', + 'bake', + 'baked', + 'baker', + "baker's", + 'bakers', + 'bakery', + 'baking', + 'bakuraiya', + 'balance', + 'balanced', + 'balancer', + 'balancers', + 'balances', + 'balancing', + 'balas', + 'balboa', + 'balconies', + 'balcony', + 'bald', + 'balding', + 'baldness', + 'baldy', + 'bale', + 'baled', + 'bales', + 'baling', + 'balios', + 'balk', + 'ball', + 'ballad', + 'ballast', + 'ballasts', + 'ballerina', + 'ballet', + 'ballgame', + 'ballistae', + 'ballistic', + 'balloon', + 'balloons', + 'ballroom', + 'ballrooms', + 'ballsy', + 'balmy', + 'baloney', + 'baloo', + "baloo's", + 'balsa', + 'balthasar', + 'bam', + 'bambadee', + 'bambi', + "bambi's", + 'bamboo', + 'ban', + 'banana', + 'bananabee', + 'bananaberry', + 'bananablabber', + 'bananabocker', + 'bananaboing', + 'bananaboom', + 'bananabounce', + 'bananabouncer', + 'bananabrains', + 'bananabubble', + 'bananabumble', + 'bananabump', + 'bananabumper', + 'bananaburger', + 'bananachomp', + 'bananacorn', + 'bananacrash', + 'bananacrumbs', + 'bananacrump', + 'bananacrunch', + 'bananadoodle', + 'bananadorf', + 'bananaface', + 'bananafidget', + 'bananafink', + 'bananafish', + 'bananaflap', + 'bananaflapper', + 'bananaflinger', + 'bananaflip', + 'bananaflipper', + 'bananafoot', + 'bananafuddy', + 'bananafussen', + 'bananagadget', + 'bananagargle', + 'bananagloop', + 'bananaglop', + 'bananagoober', + 'bananagoose', + 'bananagrooven', + 'bananahoffer', + 'bananahopper', + 'bananajinks', + 'bananaklunk', + 'bananaknees', + 'bananamarble', + 'bananamash', + 'bananamonkey', + 'bananamooch', + 'bananamouth', + 'bananamuddle', + 'bananamuffin', + 'bananamush', + 'banananerd', + 'banananoodle', + 'banananose', + 'banananugget', + 'bananaphew', + 'bananaphooey', + 'bananapocket', + 'bananapoof', + 'bananapop', + 'bananapounce', + 'bananapow', + 'bananapretzel', + 'bananaquack', + 'bananaroni', + 'bananas', + 'bananascooter', + 'bananascreech', + 'bananasmirk', + 'bananasnooker', + 'bananasnoop', + 'bananasnout', + 'bananasocks', + 'bananaspeed', + 'bananaspinner', + 'bananasplat', + 'bananasprinkles', + 'bananasticks', + 'bananastink', + 'bananaswirl', + 'bananateeth', + 'bananathud', + 'bananatoes', + 'bananaton', + 'bananatoon', + 'bananatooth', + 'bananatwist', + 'bananawhatsit', + 'bananawhip', + 'bananawig', + 'bananawoof', + 'bananazaner', + 'bananazap', + 'bananazapper', + 'bananazilla', + 'bananazoom', + 'band', + "band's", + 'bandage', + 'bandages', + 'bandaid', + 'bandaids', + 'bandana', + 'banded', + 'banding', + 'bandit', + 'banditos', + 'bandits', + 'bands', + 'bandstand', + 'bandwagon', + 'bandwidth', + 'bane', + 'banes', + 'bangle', + 'bangs', + 'banish', + 'banished', + 'banishes', + 'banishing', + 'banjo', + 'bank', + "bank's", + 'banked', + 'banker', + "banker's", + 'bankers', + 'banking', + 'bankroll', + 'bankrupt', + 'bankrupted', + 'banks', + 'banned', + 'banner', + 'banners', + 'banning', + 'banque', + 'banquet', + 'banquets', + 'bans', + 'banshee', + 'banter', + 'banzai', + 'bapple', + 'baptized', + 'bar', + "bar's", + 'baraba', + 'barago', + 'barama', + 'barano', + 'barb', + 'barbara', + 'barbarian', + "barbarian's", + 'barbarians', + 'barbaric', + 'barbary', + 'barbecue', + 'barbecued', + 'barbecuing', + 'barbed', + 'barbeque', + 'barbequed', + 'barber', + 'barbers', + 'barbershop', + "barbosa's", + 'barbossa', + "barbossa's", + 'barbossas', + 'barbs', + 'bard', + 'barded', + 'barding', + 'bards', + 'bared', + 'barefoot', + 'barefooted', + 'barefootraiders', + 'barely', + 'bares', + 'barette', + 'barf', + 'barfed', + 'bargain', + "bargain's", + 'bargained', + 'bargainer', + "bargainin'", + 'bargaining', + 'bargains', + 'barge', + "barge's", + 'barged', + 'barges', + 'barila', + 'baritone', + 'baritones', + 'bark', + 'barkeep', + 'barkeeper', + 'barker', + "barker's", + 'barkin', + 'barking', + 'barks', + 'barky', + 'barley', + 'barmaid', + 'barman', + 'barmen', + 'barn', + "barn's", + 'barnacle', + "barnacle's", + 'barnacles', + 'barney', + 'barns', + 'barnyard', + "barnyard's", + 'barnyards', + 'baron', + "baron's", + 'barons', + 'barrack', + 'barracks', + 'barracuda', + 'barracudas', + 'barrage', + 'barrages', + 'barras', + 'barred', + 'barrel', + "barrel's", + 'barreled', + 'barrels', + 'barren', + 'barrens', + 'barrette', + 'barrettes', + 'barricader', + 'barricading', + 'barrier', + "barrier's", + 'barriers', + 'barron', + "barron's", + 'barrons', + 'barrow', + 'barrows', + 'barry', + "barry's", + 'bars', + 'barsinister', + 'barstool', + 'bart', + 'barten', + 'bartend', + "bartender's", + 'bartenders', + 'bartending', + 'barter', + 'bartholomew', + 'bartolor', + 'bartolosa', + 'bartor', + 'barts', + 'barumal', + 'base', + 'baseball', + "baseball's", + 'baseballs', + 'based', + 'baseline', + "baseline's", + 'baselines', + 'basely', + 'basement', + "basement's", + 'basements', + 'baser', + 'bases', + 'basest', + 'bash', + 'bashed', + 'bashes', + 'bashful', + "bashful's", + 'bashing', + 'basic', + 'basically', + 'basics', + 'basil', + "basil's", + 'basils', + 'basin', + "basin's", + 'basing', + 'basins', + 'basis', + 'bask', + 'basket', + "basket's", + 'basketball', + "basketball's", + 'basketballs', + 'baskets', + 'basks', + 'bass', + 'baste', + 'basted', + 'bastien', + 'bastion', + 'bat', + "bat's", + 'batcave', + "batcave's", + 'batcaves', + 'bateau', + 'bateaus', + 'bateaux', + 'batgirl', + 'bath', + 'bath-drawing', + 'bathe', + 'bathed', + 'bather', + 'bathers', + 'bathes', + 'bathing', + 'bathrobes', + 'bathroom', + "bathroom's", + 'bathrooms', + 'bathroon', + 'baths', + 'bathtub', + 'bathtubs', + 'bathwater', + 'batman', + 'baton', + "baton's", + 'batons', + 'bats', + 'battaba', + 'battacare', + 'battada', + 'battago', + 'battagua', + 'battaire', + 'battalions', + 'battama', + 'battano', + 'battassa', + 'batted', + 'batten', + 'battens', + 'batter', + 'battered', + 'batteries', + 'battering', + 'battermo', + 'batters', + 'battery', + 'battevos', + 'batting', + 'battira', + 'battle', + 'battled', + 'battledore', + 'battlefield', + 'battlefront', + 'battlegrounds', + 'battlements', + 'battleon', + 'battler', + 'battlers', + 'battles', + 'battleship', + 'battling', + 'battola', + 'batty', + 'batwing', + 'batwings', + 'baubles', + 'baud', + 'bavarian', + 'bawd', + 'bawl', + 'baxter', + 'bay', + 'bay-do', + 'bayard', + 'bayberry', + 'baying', + 'baylor', + 'bayou', + 'bayous', + 'bays', + 'bazaar', + 'bazaars', + 'bazillion', + 'bazookas', + 'bbhq', + 'bbl', + 'bbq', + 'bc', + 'bcnu', + 'bday', + 'be', + 'be-awesome', + 'be-yoink', + 'beach', + 'beachcombers', + 'beachead', + 'beached', + 'beaches', + 'beachfront', + 'beachhead', + 'beaching', + 'beachplum', + 'beachside', + 'beacon', + 'bead', + 'beaded', + 'beads', + 'beagle', + 'beagles', + 'beak', + 'beaker', + 'beaks', + 'beam', + "beam's", + 'beamed', + 'beamer', + 'beamers', + 'beaming', + 'beams', + 'bean', + "bean's", + 'beanbee', + 'beanberry', + 'beanblabber', + 'beanbocker', + 'beanboing', + 'beanboom', + 'beanbounce', + 'beanbouncer', + 'beanbrains', + 'beanbubble', + 'beanbumble', + 'beanbump', + 'beanbumper', + 'beanburger', + 'beanchomp', + 'beancorn', + 'beancrash', + 'beancrumbs', + 'beancrump', + 'beancrunch', + 'beandoodle', + 'beandorf', + 'beanface', + 'beanfest', + 'beanfidget', + 'beanfink', + 'beanfish', + 'beanflap', + 'beanflapper', + 'beanflinger', + 'beanflip', + 'beanflipper', + 'beanfoot', + 'beanfuddy', + 'beanfussen', + 'beangadget', + 'beangargle', + 'beangloop', + 'beanglop', + 'beangoober', + 'beangoose', + 'beangrooven', + 'beanhoffer', + 'beanhopper', + 'beanie', + 'beaniebee', + 'beanieberry', + 'beanieblabber', + 'beaniebocker', + 'beanieboing', + 'beanieboom', + 'beaniebounce', + 'beaniebouncer', + 'beaniebrains', + 'beaniebubble', + 'beaniebumble', + 'beaniebump', + 'beaniebumper', + 'beanieburger', + 'beaniechomp', + 'beaniecorn', + 'beaniecrash', + 'beaniecrumbs', + 'beaniecrump', + 'beaniecrunch', + 'beaniedoodle', + 'beaniedorf', + 'beanieface', + 'beaniefidget', + 'beaniefink', + 'beaniefish', + 'beanieflap', + 'beanieflapper', + 'beanieflinger', + 'beanieflip', + 'beanieflipper', + 'beaniefoot', + 'beaniefuddy', + 'beaniefussen', + 'beaniegadget', + 'beaniegargle', + 'beaniegloop', + 'beanieglop', + 'beaniegoober', + 'beaniegoose', + 'beaniegrooven', + 'beaniehoffer', + 'beaniehopper', + 'beaniejinks', + 'beanieklunk', + 'beanieknees', + 'beaniemarble', + 'beaniemash', + 'beaniemonkey', + 'beaniemooch', + 'beaniemouth', + 'beaniemuddle', + 'beaniemuffin', + 'beaniemush', + 'beanienerd', + 'beanienoodle', + 'beanienose', + 'beanienugget', + 'beaniephew', + 'beaniephooey', + 'beaniepocket', + 'beaniepoof', + 'beaniepop', + 'beaniepounce', + 'beaniepow', + 'beaniepretzel', + 'beaniequack', + 'beanieroni', + 'beanies', + 'beaniescooter', + 'beaniescreech', + 'beaniesmirk', + 'beaniesnooker', + 'beaniesnoop', + 'beaniesnout', + 'beaniesocks', + 'beaniespeed', + 'beaniespinner', + 'beaniesplat', + 'beaniesprinkles', + 'beaniesticks', + 'beaniestink', + 'beanieswirl', + 'beanieteeth', + 'beaniethud', + 'beanietoes', + 'beanieton', + 'beanietoon', + 'beanietooth', + 'beanietwist', + 'beaniewhatsit', + 'beaniewhip', + 'beaniewig', + 'beaniewoof', + 'beaniezaner', + 'beaniezap', + 'beaniezapper', + 'beaniezilla', + 'beaniezoom', + 'beanjinks', + 'beanklunk', + 'beanknees', + 'beanmarble', + 'beanmash', + 'beanmonkey', + 'beanmooch', + 'beanmouth', + 'beanmuddle', + 'beanmuffin', + 'beanmush', + 'beannerd', + 'beannoodle', + 'beannose', + 'beannugget', + 'beanphew', + 'beanphooey', + 'beanpocket', + 'beanpoof', + 'beanpop', + 'beanpounce', + 'beanpow', + 'beanpretzel', + 'beanquack', + 'beanroni', + 'beans', + 'beanscooter', + 'beanscreech', + 'beansmirk', + 'beansnooker', + 'beansnoop', + 'beansnout', + 'beansocks', + 'beanspeed', + 'beanspinner', + 'beansplat', + 'beansprinkles', + 'beanstalks', + 'beansticks', + 'beanstink', + 'beanswirl', + 'beanteeth', + 'beanthud', + 'beantoes', + 'beanton', + 'beantoon', + 'beantooth', + 'beantwist', + 'beanwhatsit', + 'beanwhip', + 'beanwig', + 'beanwoof', + 'beanzaner', + 'beanzap', + 'beanzapper', + 'beanzilla', + 'beanzoom', + 'bear', + "bear's", + 'beard', + "beard's", + 'bearded', + 'beardless', + 'beardmonsters', + 'beards', + 'beardy', + 'bearer', + 'bearers', + 'bearing', + 'bearings', + 'bearish', + 'bears', + 'beast', + "beast's", + 'beastie', + 'beasties', + 'beastings', + 'beastly', + 'beasts', + 'beat', + 'beatable', + 'beau', + 'beaucoup', + 'beauteous', + 'beauticians', + 'beauties', + 'beautiful', + 'beautifully', + 'beautifulness', + 'beauty', + "beauty's", + 'beawesome', + 'became', + 'because', + 'beck', + "beck's", + 'beckets', + 'beckett', + "beckett's", + 'beckoned', + 'beckons', + 'become', + 'becomes', + 'becoming', + 'bed', + 'bed-sized', + 'bedazzle', + 'bedazzled', + 'bedazzler', + 'bedazzles', + 'bedclothes', + 'bedding', + 'bedhog', + 'bedknobs', + "bedo's", + 'bedroll', + 'bedroom', + 'bedrooms', + 'beds', + 'bedspread', + 'bedspreads', + 'bee', + "bee's", + 'beef', + 'beefcake', + 'beefed', + 'beefs', + 'beefy', + 'beehive', + "beehive's", + 'beehives', + 'beeline', + 'been', + 'beep', + 'beeped', + 'beepers', + 'beeping', + 'beeps', + 'bees', + 'beeswax', + 'beet', + 'beethoven', + 'beetle', + "beetle's", + 'beetles', + 'beets', + 'before', + 'beforehand', + 'befriend', + 'befriended', + 'befriending', + 'befriends', + 'befuddle', + 'befuddled', + 'beg', + 'began', + 'begat', + 'begets', + 'beggar', + 'beggars', + 'begged', + 'begging', + 'begin', + 'beginner', + "beginner's", + 'beginners', + 'beginning', + "beginning's", + 'beginnings', + 'begins', + 'begonia', + 'begotten', + 'begs', + 'begun', + 'behalf', + 'behave', + 'behaved', + 'behaver', + 'behaves', + 'behaving', + 'behavior', + 'behavioral', + 'behaviors', + 'behemoth', + 'behemoths', + 'behind', + 'behind-the-scenes', + 'behinds', + 'behold', + 'beholden', + 'beholding', + 'behoove', + 'behop', + 'behr', + 'beige', + "bein'", + 'being', + "being's", + 'beings', + 'bejeweled', + 'bela', + 'belated', + 'belay', + 'belch', + 'belief', + "belief's", + 'beliefs', + 'believable', + 'believe', + 'believed', + 'believer', + 'believers', + 'believes', + 'believing', + 'belittle', + 'belittles', + 'bell', + "bell's", + 'bella', + "bella's", + 'bellas', + 'belle', + "belle's", + 'belles', + "belles'", + 'bellflower', + 'bellhop', + 'belli', + 'bellied', + 'bellies', + 'bellow', + 'bellows', + 'bells', + 'belly', + 'bellyache', + 'belong', + 'belonged', + 'belonging', + 'belongings', + 'belongs', + 'beloved', + 'beloveds', + 'below', + 'belowdeck', + 'belt', + 'belts', + 'ben', + 'benches', + 'benchmark', + 'benchwarmers', + 'bend', + 'bending', + 'bends', + 'beneath', + 'benedek', + "benedek's", + 'beneficial', + 'benefit', + 'benefited', + 'benefiting', + 'benefits', + 'benjamin', + 'benjy', + 'benne', + 'benny', + "benny's", + 'bent', + 'benz', + 'beppo', + 'bequeaths', + 'bequermo', + 'bequila', + 'beret', + 'berets', + 'berg', + 'beriths27th', + 'bernadette', + 'bernard', + 'bernie', + 'berried', + 'berries', + 'berry', + 'berserk', + 'bert', + "bert's", + 'berth', + 'bertha', + 'berthed', + 'berthing', + 'berths', + 'beruna', + 'beseech', + 'beside', + 'besides', + 'bess', + "bess'", + "bess's", + 'bess-statue', + 'bessie', + 'best', + 'bested', + 'bester', + 'besting', + 'bests', + 'bet', + "bet's", + 'beta', + "beta's", + 'betas', + 'betcha', + 'beth', + 'betray', + 'betrayal', + 'betrayed', + 'bets', + 'betsy', + 'better', + 'bettered', + 'bettering', + 'betterment', + 'betters', + 'bettie', + 'betting', + 'betty', + 'between', + 'betwixt', + 'bev', + 'bevel', + 'beverage', + 'beverages', + 'beware', + 'bewilder', + 'bewildered', + 'bewildering', + 'bewitch', + 'bewitched', + 'bewitches', + 'bewitching', + 'beyond', + 'bezerk', + 'bff', + 'bfs', + 'bi-lingual', + 'bias', + 'biased', + 'bib', + 'bibbidi', + 'bible', + 'biblical', + 'bicep', + 'biceps', + 'bickering', + 'bicuspid', + 'bicycle', + 'bicycles', + 'bid', + 'bidder', + 'bidding', + 'biddlesmore', + 'bide', + 'bids', + 'bien', + 'bifocals', + 'big', + 'big-screen', + 'biggen', + 'biggenbee', + 'biggenberry', + 'biggenblabber', + 'biggenbocker', + 'biggenboing', + 'biggenboom', + 'biggenbounce', + 'biggenbouncer', + 'biggenbrains', + 'biggenbubble', + 'biggenbumble', + 'biggenbump', + 'biggenbumper', + 'biggenburger', + 'biggenchomp', + 'biggencorn', + 'biggencrash', + 'biggencrumbs', + 'biggencrump', + 'biggencrunch', + 'biggendoodle', + 'biggendorf', + 'biggenface', + 'biggenfidget', + 'biggenfink', + 'biggenfish', + 'biggenflap', + 'biggenflapper', + 'biggenflinger', + 'biggenflip', + 'biggenflipper', + 'biggenfoot', + 'biggenfuddy', + 'biggenfussen', + 'biggengadget', + 'biggengargle', + 'biggengloop', + 'biggenglop', + 'biggengoober', + 'biggengoose', + 'biggengrooven', + 'biggenhoffer', + 'biggenhopper', + 'biggenjinks', + 'biggenklunk', + 'biggenknees', + 'biggenmarble', + 'biggenmash', + 'biggenmonkey', + 'biggenmooch', + 'biggenmouth', + 'biggenmuddle', + 'biggenmuffin', + 'biggenmush', + 'biggennerd', + 'biggennoodle', + 'biggennose', + 'biggennugget', + 'biggenphew', + 'biggenphooey', + 'biggenpocket', + 'biggenpoof', + 'biggenpop', + 'biggenpounce', + 'biggenpow', + 'biggenpretzel', + 'biggenquack', + 'biggenroni', + 'biggenscooter', + 'biggenscreech', + 'biggensmirk', + 'biggensnooker', + 'biggensnoop', + 'biggensnout', + 'biggensocks', + 'biggenspeed', + 'biggenspinner', + 'biggensplat', + 'biggensprinkles', + 'biggensticks', + 'biggenstink', + 'biggenswirl', + 'biggenteeth', + 'biggenthud', + 'biggentoes', + 'biggenton', + 'biggentoon', + 'biggentooth', + 'biggentwist', + 'biggenwhatsit', + 'biggenwhip', + 'biggenwig', + 'biggenwoof', + 'biggenzaner', + 'biggenzap', + 'biggenzapper', + 'biggenzilla', + 'biggenzoom', + 'bigger', + 'biggest', + 'biggie', + 'biggies', + 'biggles', + 'biggleton', + 'bight', + 'bigwig', + 'bike', + "bike's", + 'biked', + 'biker', + 'bikers', + 'bikes', + 'biking', + 'bikini', + 'bile', + 'bilge', + 'bilgepump', + 'bilgerats', + 'bilges', + 'bilging', + 'bilingual', + 'bill', + "bill's", + 'billed', + 'biller', + 'billers', + 'billiards', + 'billing', + 'billings', + 'billington', + 'billion', + 'billionaire', + 'billions', + 'billionth', + 'billow', + 'billows', + 'billowy', + 'bills', + 'billy', + 'billybob', + 'bim', + 'bimbim', + 'bimonthly', + 'bimos', + 'bin', + 'binary', + 'bind', + 'binding', + 'bing', + 'bingham', + 'bingo', + "bingo's", + 'bingos', + 'binky', + 'binnacle', + 'binoculars', + 'bins', + 'bio', + 'biog', + 'biology', + 'bionic', + 'bionicle', + 'biopsychology', + 'bios', + 'bip', + 'birch-bark', + 'bird', + "bird's", + 'birdbrain', + 'birddog', + 'birder', + 'birdhouse', + 'birdie', + 'birdies', + 'birdman', + 'birds', + 'birdseed', + 'birth', + 'birthdates', + 'birthday', + 'birthdays', + 'birthed', + 'birthmark', + 'birthmarks', + 'birthplace', + 'birthstone', + 'biscotti', + 'biscuit', + 'biscuits', + 'bishop', + 'bishops', + 'bison', + 'bisque', + 'bisquit', + 'bistro', + 'bit', + "bit's", + 'bite', + 'biter', + 'biters', + 'bites', + 'biting', + 'bits', + 'bitsy', + 'bitten', + 'bitter', + 'bitty', + 'biweekly', + 'biyearly', + 'biz', + 'bizarre', + 'bizzenbee', + 'bizzenberry', + 'bizzenblabber', + 'bizzenbocker', + 'bizzenboing', + 'bizzenboom', + 'bizzenbounce', + 'bizzenbouncer', + 'bizzenbrains', + 'bizzenbubble', + 'bizzenbumble', + 'bizzenbump', + 'bizzenbumper', + 'bizzenburger', + 'bizzenchomp', + 'bizzencorn', + 'bizzencrash', + 'bizzencrumbs', + 'bizzencrump', + 'bizzencrunch', + 'bizzendoodle', + 'bizzendorf', + 'bizzenface', + 'bizzenfidget', + 'bizzenfink', + 'bizzenfish', + 'bizzenflap', + 'bizzenflapper', + 'bizzenflinger', + 'bizzenflip', + 'bizzenflipper', + 'bizzenfoot', + 'bizzenfuddy', + 'bizzenfussen', + 'bizzengadget', + 'bizzengargle', + 'bizzengloop', + 'bizzenglop', + 'bizzengoober', + 'bizzengoose', + 'bizzengrooven', + 'bizzenhoffer', + 'bizzenhopper', + 'bizzenjinks', + 'bizzenklunk', + 'bizzenknees', + 'bizzenmarble', + 'bizzenmash', + 'bizzenmonkey', + 'bizzenmooch', + 'bizzenmouth', + 'bizzenmuddle', + 'bizzenmuffin', + 'bizzenmush', + 'bizzennerd', + 'bizzennoodle', + 'bizzennose', + 'bizzennugget', + 'bizzenphew', + 'bizzenphooey', + 'bizzenpocket', + 'bizzenpoof', + 'bizzenpop', + 'bizzenpounce', + 'bizzenpow', + 'bizzenpretzel', + 'bizzenquack', + 'bizzenroni', + 'bizzenscooter', + 'bizzenscreech', + 'bizzensmirk', + 'bizzensnooker', + 'bizzensnoop', + 'bizzensnout', + 'bizzensocks', + 'bizzenspeed', + 'bizzenspinner', + 'bizzensplat', + 'bizzensprinkles', + 'bizzensticks', + 'bizzenstink', + 'bizzenswirl', + 'bizzenteeth', + 'bizzenthud', + 'bizzentoes', + 'bizzenton', + 'bizzentoon', + 'bizzentooth', + 'bizzentwist', + 'bizzenwhatsit', + 'bizzenwhip', + 'bizzenwig', + 'bizzenwoof', + 'bizzenzaner', + 'bizzenzap', + 'bizzenzapper', + 'bizzenzilla', + 'bizzenzoom', + 'bla', + 'blabbing', + 'black', + 'black-eyed', + 'blackbeard', + "blackbeard's", + 'blackbeards', + 'blackbeared', + 'blackbelt', + 'blackberries', + 'blackberry', + 'blackbird', + 'blackboard', + 'blackboards', + 'blackdeath', + 'blacked', + 'blackened', + 'blackest', + 'blackguard', + 'blackguards', + 'blackhaerts', + 'blackhawk', + 'blackheads', + 'blackheart', + "blackheart's", + 'blackhearts', + 'blacking', + 'blackish', + 'blackjack', + 'blackjacks', + 'blacklist', + 'blackness', + 'blackout', + 'blackouts', + 'blackrage', + 'blackrose', + 'blacksail', + 'blacksmith', + "blacksmith's", + 'blacksmithing', + 'blacksmiths', + 'blackthorn', + 'blackwatch', + 'blackwater', + 'bladder', + 'bladders', + "blade's", + 'bladebreakerr', + 'blademasters', + 'blades', + 'bladeskulls', + 'bladestorm', + 'blaggards', + 'blah', + 'blair', + 'blake', + 'blakeley', + "blakeley's", + 'blame', + 'blamed', + 'blamer', + 'blamers', + 'blames', + 'blaming', + 'blanada', + 'blanago', + 'blanca', + "blanca's", + 'blanche', + 'bland', + 'blank', + 'blanked', + 'blanket', + 'blankets', + 'blanking', + 'blankly', + 'blanks', + 'blanos', + 'blaring', + 'blast', + "blast'em", + 'blasted', + 'blaster', + 'blasters', + "blastin'", + 'blasting', + 'blasts', + 'blasty', + 'blat', + 'bldg', + 'bldgs', + 'bleached', + 'bleak', + 'bleary', + 'bled', + 'bleep', + 'bleeped', + 'bleeper', + 'bleepin', + 'bleeping', + 'bleeps', + 'blend', + 'blended', + 'blender', + 'blenders', + 'blending', + 'blends', + 'blenny', + 'bless', + 'blessed', + 'blesses', + 'blessing', + 'blessings', + 'bleu', + 'blew', + 'bligh', + 'blight', + 'blighters', + 'blimey', + 'blimp', + 'blind', + 'blinded', + 'blinder', + 'blinders', + 'blindfold', + 'blinding', + 'blindly', + 'blindness', + 'blinds', + 'blindsided', + 'bling', + 'bling-bling', + 'blingbling', + 'blinged', + 'blinging', + 'blings', + 'blink', + 'blinked', + 'blinker', + 'blinkers', + 'blinking', + 'blinks', + 'blinky', + 'blip', + 'blipping', + 'bliss', + 'blissfully', + 'blister', + 'blistering', + 'blisters', + 'blitz', + 'blizzard', + 'blizzards', + 'bloat', + 'bloated', + 'bloats', + 'blob', + 'blobby', + 'blobs', + 'bloc', + 'block', + "block's", + 'blockade', + 'blockader', + 'blockades', + 'blockading', + 'blockbuster', + 'blocked', + 'blocker', + 'blockers', + 'blocking', + 'blockout', + 'blocks', + 'blocky', + 'bloke', + 'blokes', + 'blond', + 'blonde', + "blonde's", + 'blondes', + 'blondie', + 'blonds', + 'bloodbrothers', + 'bloodhounds', + 'bloodless', + 'bloodshot', + 'bloodsucker', + 'bloodsuckers', + 'bloodthrushers', + 'bloom', + 'bloomers', + 'blooming', + 'blooms', + 'bloop', + 'bloopers', + 'blossom', + 'blossoms', + 'blossum', + 'blot', + 'blots', + 'blouse', + 'blowfish', + 'blowy', + 'blu-ray', + 'blub', + 'blubber', + 'blubberbee', + 'blubberberry', + 'blubberblabber', + 'blubberbocker', + 'blubberboing', + 'blubberboom', + 'blubberbounce', + 'blubberbouncer', + 'blubberbrains', + 'blubberbubble', + 'blubberbumble', + 'blubberbump', + 'blubberbumper', + 'blubberburger', + 'blubberchomp', + 'blubbercorn', + 'blubbercrash', + 'blubbercrumbs', + 'blubbercrump', + 'blubbercrunch', + 'blubberdoodle', + 'blubberdorf', + 'blubberface', + 'blubberfidget', + 'blubberfink', + 'blubberfish', + 'blubberflap', + 'blubberflapper', + 'blubberflinger', + 'blubberflip', + 'blubberflipper', + 'blubberfoot', + 'blubberfuddy', + 'blubberfussen', + 'blubbergadget', + 'blubbergargle', + 'blubbergloop', + 'blubberglop', + 'blubbergoober', + 'blubbergoose', + 'blubbergrooven', + 'blubberhoffer', + 'blubberhopper', + 'blubbering', + 'blubberjinks', + 'blubberklunk', + 'blubberknees', + 'blubbermarble', + 'blubbermash', + 'blubbermonkey', + 'blubbermooch', + 'blubbermouth', + 'blubbermuddle', + 'blubbermuffin', + 'blubbermush', + 'blubbernerd', + 'blubbernoodle', + 'blubbernose', + 'blubbernugget', + 'blubberphew', + 'blubberphooey', + 'blubberpocket', + 'blubberpoof', + 'blubberpop', + 'blubberpounce', + 'blubberpow', + 'blubberpretzel', + 'blubberquack', + 'blubberroni', + 'blubberscooter', + 'blubberscreech', + 'blubbersmirk', + 'blubbersnooker', + 'blubbersnoop', + 'blubbersnout', + 'blubbersocks', + 'blubberspeed', + 'blubberspinner', + 'blubbersplat', + 'blubbersprinkles', + 'blubbersticks', + 'blubberstink', + 'blubberswirl', + 'blubberteeth', + 'blubberthud', + 'blubbertoes', + 'blubberton', + 'blubbertoon', + 'blubbertooth', + 'blubbertwist', + 'blubberwhatsit', + 'blubberwhip', + 'blubberwig', + 'blubberwoof', + 'blubberzaner', + 'blubberzap', + 'blubberzapper', + 'blubberzilla', + 'blubberzoom', + 'bludgeon', + 'bludgeoning', + 'blue', + "blue's", + 'bluebeards', + 'bluebell', + 'blueberries', + 'blueberry', + 'bluebird', + 'bluebirds', + 'blueblood', + 'bluefishes', + 'bluegrass', + 'bluejay', + 'blueprints', + 'blues', + 'bluff', + 'bluffed', + 'bluffer', + 'bluffers', + 'bluffing', + 'bluffs', + 'blunder', + 'blundering', + 'bluntly', + 'blur', + 'blurb', + 'blurbs', + 'blurred', + 'blurry', + 'blurs', + 'blurting', + 'blush', + 'blushed', + 'blushes', + 'blushing', + 'blustery', + 'blut', + 'blynken', + 'bmw', + 'bo', + 'boa', + 'boar', + 'board', + "board's", + 'boarded', + 'boarder', + 'boarders', + 'boarding', + 'boards', + 'boardwalk', + 'boardwalks', + 'boarhound', + 'boars', + 'boas', + 'boast', + 'boastful', + 'boasting', + 'boat', + "boat's", + 'boated', + 'boater', + 'boaters', + 'boathouse', + 'boating', + 'boatload', + 'boatloads', + 'boats', + 'boatswain', + "boatswain's", + 'boatswains', + 'boatyard', + 'bob', + 'bobbed', + 'bobber', + 'bobbidi', + 'bobble', + 'bobbleheads', + 'bobby', + "bobby's", + 'bobbys', + 'boberts', + 'bobo', + 'bobsled', + 'bobsleded', + 'bobsleding', + 'bobsleds', + 'bobsleigh', + 'bobsleighes', + 'bock', + 'bodacious', + 'bode', + 'bodeguita', + 'bodice', + 'bodices', + 'bodied', + 'bodies', + 'bodily', + 'body', + "body's", + 'bodyguard', + 'bodyguards', + 'boffo', + 'bog', + 'bogart', + 'bogey', + 'bogger', + 'boggle', + 'boggles', + 'boggy', + 'bogie', + 'bogs', + 'bogus', + 'boil', + 'boiled', + 'boiler', + 'boiling', + 'boils', + 'boingenbee', + 'boingenberry', + 'boingenblabber', + 'boingenbocker', + 'boingenboing', + 'boingenboom', + 'boingenbounce', + 'boingenbouncer', + 'boingenbrains', + 'boingenbubble', + 'boingenbumble', + 'boingenbump', + 'boingenbumper', + 'boingenburger', + 'boingenchomp', + 'boingencorn', + 'boingencrash', + 'boingencrumbs', + 'boingencrump', + 'boingencrunch', + 'boingendoodle', + 'boingendorf', + 'boingenface', + 'boingenfidget', + 'boingenfink', + 'boingenfish', + 'boingenflap', + 'boingenflapper', + 'boingenflinger', + 'boingenflip', + 'boingenflipper', + 'boingenfoot', + 'boingenfuddy', + 'boingenfussen', + 'boingengadget', + 'boingengargle', + 'boingengloop', + 'boingenglop', + 'boingengoober', + 'boingengoose', + 'boingengrooven', + 'boingenhoffer', + 'boingenhopper', + 'boingenjinks', + 'boingenklunk', + 'boingenknees', + 'boingenmarble', + 'boingenmash', + 'boingenmonkey', + 'boingenmooch', + 'boingenmouth', + 'boingenmuddle', + 'boingenmuffin', + 'boingenmush', + 'boingennerd', + 'boingennoodle', + 'boingennose', + 'boingennugget', + 'boingenphew', + 'boingenphooey', + 'boingenpocket', + 'boingenpoof', + 'boingenpop', + 'boingenpounce', + 'boingenpow', + 'boingenpretzel', + 'boingenquack', + 'boingenroni', + 'boingenscooter', + 'boingenscreech', + 'boingensmirk', + 'boingensnooker', + 'boingensnoop', + 'boingensnout', + 'boingensocks', + 'boingenspeed', + 'boingenspinner', + 'boingensplat', + 'boingensprinkles', + 'boingensticks', + 'boingenstink', + 'boingenswirl', + 'boingenteeth', + 'boingenthud', + 'boingentoes', + 'boingenton', + 'boingentoon', + 'boingentooth', + 'boingentwist', + 'boingenwhatsit', + 'boingenwhip', + 'boingenwig', + 'boingenwoof', + 'boingenzaner', + 'boingenzap', + 'boingenzapper', + 'boingenzilla', + 'boingenzoom', + 'bokugeki', + 'bokuji', + 'bokuzama', + 'bold', + 'bolder', + 'boldest', + 'boldly', + 'bole', + 'bolivia', + 'bollard', + 'bologna', + 'bolt', + "bolt's", + 'bolted', + 'bolton', + 'bolts', + 'boma', + 'boma-boma', + 'bombard', + 'bombarding', + 'bombardment', + 'bombe', + 'bombed', + 'bomber', + 'bombers', + 'bombing', + 'bombs', + 'bombshell', + 'bon', + 'bonaam', + 'bonanza', + 'bonbons', + 'bond', + 'bonded', + 'bonding', + 'bonds', + 'bondsman', + 'bonehead', + 'boneheads', + 'boneless', + 'boneyard', + 'boneyards', + 'bonfire', + 'bonfires', + 'bongo', + 'bonita', + "bonita's", + 'bonito', + 'bonjour', + 'bonkers', + 'bonkl', + 'bonnet', + 'bonnets', + 'bonney', + 'bonnie', + 'bonny', + 'bono', + 'bonsai', + 'bonsoir', + 'bonus', + 'bonuses', + 'bony', + 'bonzo', + 'boo', + "boo's", + 'boo-yaa', + 'booed', + 'booger', + 'boogers', + 'boogey', + 'boogie', + 'boogie-woogie', + 'boogied', + 'boogies', + 'boohoo', + 'book', + "book's", + 'bookball', + 'bookcase', + 'bookcases', + 'booked', + 'bookie', + 'booking', + 'bookings', + 'bookkeeper', + 'bookkeepers', + 'bookkeeping', + 'booklet', + 'bookmaker', + 'bookmakers', + 'bookmark', + 'bookmarked', + 'books', + 'bookshelf', + 'bookshelves', + 'bookstore', + 'bookworm', + 'boom', + 'boomcrash', + 'boomed', + 'boomer', + 'boomerang', + 'boomers', + 'booming', + 'booms', + 'boon', + 'boonies', + 'booo', + 'boooo', + 'booooh', + 'boooom', + 'booooo', + 'booooom', + 'booooomin', + 'boooooo', + 'boooooooooooooooooooo', + 'boooooooooooooooooooooooooommm', + 'boor', + 'boos', + 'boost', + 'boosted', + 'booster', + 'boosters', + 'boosting', + 'boosts', + 'boot', + "boot'n'ears", + 'booted', + 'booth', + "booth's", + 'booths', + 'bootiful', + 'booting', + 'bootleggers', + 'boots', + 'bootstrap', + 'bootstraps', + 'bootsy', + 'booyah', + 'bop', + 'bopper', + 'bord', + 'border', + "border's", + 'bordered', + 'borderer', + 'bordering', + 'borderings', + 'borderline', + 'borders', + 'bore', + 'borealis', + 'bored', + 'boredom', + 'borer', + 'bores', + 'boring', + 'boris', + 'bork', + 'born', + 'borrow', + 'borrowed', + 'borrower', + 'borrowers', + 'borrowing', + 'borrowings', + 'borrows', + 'bos', + 'boss', + "boss'", + 'bossbot', + 'bossbots', + 'bossed', + 'bosses', + 'bossily', + 'bossing', + 'bossy', + 'bossyboots', + 'bot', + "bot's", + 'botched', + 'both', + 'bother', + 'bothered', + 'bothering', + 'bothers', + 'bothersome', + 'bots', + 'bottle', + "bottle's", + 'bottled', + 'bottleneck', + 'bottler', + 'bottlers', + 'bottles', + 'bottling', + 'bottom', + 'bottomed', + 'bottomer', + 'bottoming', + 'bottomless', + 'bottoms', + 'bough', + 'boughs', + 'bought', + 'boulder', + 'boulders', + 'boulevard', + "boulevard's", + 'boulevards', + 'bounce', + 'bounced', + 'bouncer', + 'bouncers', + 'bounces', + 'bouncing', + 'bouncy', + 'bound', + 'boundaries', + 'boundary', + "boundary's", + 'bounding', + 'bounds', + 'bounteous', + 'bounties', + 'bountiful', + 'bounty', + 'bountyhunter', + 'bourbon', + 'bourse', + 'bout', + 'boutique', + 'bouts', + 'bovel', + 'bovine', + 'bow', + "bow's", + 'bowdash', + 'bowed', + 'bowers', + 'bowie', + 'bowing', + 'bowl', + "bowl's", + 'bowlegged', + 'bowler', + 'bowling', + 'bowls', + 'bowman', + 'bows', + 'box', + 'boxcar', + 'boxed', + 'boxer', + 'boxes', + 'boxfish', + 'boxing', + 'boy', + "boy's", + 'boycott', + 'boycotting', + 'boyfriend', + "boyfriend's", + 'boyfriends', + 'boyish', + 'boys', + 'boysenberry', + 'bozo', + "bozo's", + 'bozos', + 'brace', + 'bracelet', + 'bracelets', + 'braces', + 'bracket', + 'bracketing', + 'brad', + "brad's", + 'bradley', + 'brag', + 'braggart', + 'braggarts', + 'bragged', + 'bragger', + 'braggers', + 'bragging', + 'brags', + 'braid', + 'braided', + 'braiding', + 'braids', + 'brail', + 'brain', + "brain's", + 'brained', + 'braining', + 'brainless', + 'brains', + 'brainstorm', + 'brainwash', + 'brainwashed', + 'brainy', + 'brake', + 'braken', + 'brakes', + 'braking', + 'bran', + 'branch', + 'branched', + 'branches', + 'branching', + 'branchings', + 'brand', + 'brandishing', + 'brandon', + "brandon's", + 'brands', + 'brandy', + "brandy's", + 'brantley', + 'brash', + 'brass', + 'brat', + 'brats', + 'bratty', + 'brave', + 'braved', + 'bravely', + 'braver', + 'bravery', + 'braves', + 'bravest', + 'braving', + 'bravo', + "bravo's", + 'brawl', + 'brawny', + 'bray', + 'brazen', + 'brazil', + 'brb', + 'breach', + 'breached', + 'bread', + "bread's", + 'bread-buttering', + 'breadcrumbs', + 'breaded', + 'breading', + 'breads', + 'breadstick', + "breadstick's", + 'breadth', + 'break', + 'breakable', + 'breakdown', + 'breaker', + 'breakers', + 'breakfast', + 'breakfasted', + 'breakfaster', + 'breakfasters', + 'breakfasting', + 'breakfasts', + 'breaking', + 'breakout', + 'breaks', + 'breakup', + 'breath', + 'breathe', + 'breathed', + 'breather', + 'breathers', + 'breathes', + 'breathing', + 'breathless', + 'breaths', + 'breathtaking', + 'bred', + 'breech', + 'breeches', + 'breeze', + "breeze's", + 'breezed', + 'breezes', + 'breezest', + 'breezing', + 'breezy', + 'brenda', + "brenda's", + 'brethern', + 'brethren', + 'brevrend', + 'brew', + 'brewed', + 'brewer', + 'brewers', + 'brewing', + 'brews', + 'bria', + 'brian', + 'briar', + 'briars', + 'briarstone', + 'briarstones', + 'bribe', + 'bribed', + 'bribery', + 'bribes', + 'bribing', + 'brick', + 'bricked', + 'bricker', + 'bricking', + 'bricks', + 'bridal', + 'bride', + "bride's", + 'brides', + 'bridesmaid', + 'bridge', + "bridge's", + 'bridged', + 'bridges', + 'bridget', + 'bridging', + 'brie', + 'brief', + 'briefed', + 'briefer', + 'briefest', + 'briefing', + 'briefings', + 'briefly', + 'briefs', + 'brig', + "brig's", + 'brigad', + 'brigade', + 'brigadeers', + 'brigades', + 'brigadier', + 'brigadiers', + 'brigand', + 'brigands', + 'brigantine', + 'brigantines', + 'bright', + 'brighten', + 'brightens', + 'brighter', + 'brightest', + 'brighting', + 'brightly', + 'brightness', + 'brights', + 'brigs', + 'brilliance', + 'brilliant', + 'brilliantly', + 'brim', + 'brimming', + 'brimstone', + 'brine', + 'briney', + 'bring', + 'bringer', + 'bringers', + 'bringing', + 'brings', + 'bringthemadness', + 'brining', + 'brink', + 'brinks', + 'briny', + 'brio', + 'briquettes', + 'brisk', + 'brisket', + 'britches', + 'british', + 'brittle', + 'bro', + "bro's", + 'broached', + 'broad', + 'broadband', + 'broadcast', + 'broadcasts', + 'broaden', + 'broadens', + 'broader', + 'broadest', + 'broadly', + 'broads', + 'broadside', + "broadside's", + 'broadsided', + 'broadsides', + 'broadsword', + 'broadway', + 'broccoli', + 'brochure', + 'brogan', + 'brogans', + 'broil', + 'broiled', + 'broke', + 'broken', + 'brokenly', + 'broker', + 'brokers', + 'broking', + 'bronco', + 'broncos', + 'bronze', + 'bronzed', + 'bronzy', + 'brood', + 'brook', + "brook's", + 'brooks', + 'broom', + "broom's", + 'brooms', + 'broomstick', + "broomstick's", + 'broomsticks', + 'bros', + 'broth', + 'brother', + "brother's", + 'brotherhood', + "brotherhood's", + 'brotherhoods', + 'brothering', + 'brotherly', + 'brothers', + "brothers'", + 'broths', + 'brought', + 'brouhaha', + 'brow', + 'brown', + 'browncoats', + 'browner', + 'brownie', + 'brownies', + 'browning', + 'brownish', + 'browns', + 'brows', + 'browse', + 'browsed', + 'browser', + 'browsers', + 'browsing', + 'brrr', + 'brrrgh', + 'brt', + 'bruce', + "bruce's", + 'bruckheimer', + 'bruh', + 'bruin', + 'bruise', + 'bruised', + 'bruiser', + 'bruises', + 'bruising', + 'brulee', + 'brume', + 'brunch', + 'brunette', + 'brunettes', + 'brunt', + 'brush', + 'brushed', + 'brusher', + 'brushes', + 'brushing', + 'brushoff', + 'brussels', + 'brutal', + 'brute', + 'brutish', + 'bryan', + 'bryanna', + 'bryce', + 'bryson', + 'btb', + 'btl', + 'btw', + 'bubba', + 'bubble', + "bubble's", + 'bubbled', + 'bubblegum', + 'bubbles', + 'bubbling', + 'bubbloon', + 'bubbly', + 'bubo', + 'bubs', + "buc's", + 'bucaneer', + 'bucanneers', + 'buccaneer', + "buccaneer's", + 'buccaneers', + 'bucco', + 'buckaroo', + "buckaroo's", + 'buckaroos', + 'bucket', + "bucket's", + 'bucketed', + 'bucketing', + 'buckets', + 'buckeye', + 'buckeyes', + 'buckle', + "buckle's", + 'buckled', + 'buckler', + 'buckles', + 'bucko', + "bucko's", + 'buckos', + 'bucks', + 'buckshot', + 'buckskin', + 'buckwheat', + 'bucs', + 'bud', + "bud's", + 'budd', + "budd's", + 'buddies', + 'buddy', + "buddy's", + 'budge', + 'budged', + 'budget', + 'budgeted', + 'budgeter', + 'budgeters', + 'budgeting', + 'budgets', + 'budgie', + 'budging', + 'buds', + 'bueno', + 'buff', + 'buffalo', + "buffalo's", + 'buffalos', + 'buffed', + 'buffer', + 'buffet', + "buffet's", + 'buffets', + 'buffoon', + 'buffoons', + 'buffs', + 'bug', + "bug's", + 'bug-eye', + 'bugalicious', + 'bugariffic', + 'bugatti', + 'bugbear', + 'bugbears', + 'bugeye', + 'bugged', + 'buggered', + 'buggers', + 'buggier', + 'buggies', + 'buggiest', + 'bugging', + 'buggy', + 'bugle', + 'bugles', + 'bugs', + 'bugsit', + 'bugtastic', + 'buh', + 'buick', + 'build', + 'builded', + 'builder', + "builder's", + 'builders', + 'building', + "building's", + 'buildings', + 'builds', + 'buildup', + 'buillion', + 'built', + 'bulb', + "bulb's", + 'bulbs', + 'bulge', + 'bulging', + 'bulgy', + 'bulk', + 'bulkhead', + 'bulky', + 'bull', + "bull's", + 'bulldog', + 'bulldogs', + 'bulldozer', + 'bulletin', + 'bullfighters', + 'bullfighting', + 'bullied', + 'bullies', + 'bullion', + 'bullpen', + 'bulls', + 'bullseye', + 'bully', + "bully's", + 'bullying', + 'bulwark', + "bulwark's", + 'bulwarks', + 'bum-bum', + 'bumberbee', + 'bumberberry', + 'bumberblabber', + 'bumberbocker', + 'bumberboing', + 'bumberboom', + 'bumberbounce', + 'bumberbouncer', + 'bumberbrains', + 'bumberbubble', + 'bumberbumble', + 'bumberbump', + 'bumberbumper', + 'bumberburger', + 'bumberchomp', + 'bumbercorn', + 'bumbercrash', + 'bumbercrumbs', + 'bumbercrump', + 'bumbercrunch', + 'bumberdoodle', + 'bumberdorf', + 'bumberface', + 'bumberfidget', + 'bumberfink', + 'bumberfish', + 'bumberflap', + 'bumberflapper', + 'bumberflinger', + 'bumberflip', + 'bumberflipper', + 'bumberfoot', + 'bumberfuddy', + 'bumberfussen', + 'bumbergadget', + 'bumbergargle', + 'bumbergloop', + 'bumberglop', + 'bumbergoober', + 'bumbergoose', + 'bumbergrooven', + 'bumberhoffer', + 'bumberhopper', + 'bumberjinks', + 'bumberklunk', + 'bumberknees', + 'bumbermarble', + 'bumbermash', + 'bumbermonkey', + 'bumbermooch', + 'bumbermouth', + 'bumbermuddle', + 'bumbermuffin', + 'bumbermush', + 'bumbernerd', + 'bumbernoodle', + 'bumbernose', + 'bumbernugget', + 'bumberphew', + 'bumberphooey', + 'bumberpocket', + 'bumberpoof', + 'bumberpop', + 'bumberpounce', + 'bumberpow', + 'bumberpretzel', + 'bumberquack', + 'bumberroni', + 'bumberscooter', + 'bumberscreech', + 'bumbersmirk', + 'bumbersnooker', + 'bumbersnoop', + 'bumbersnout', + 'bumbersocks', + 'bumberspeed', + 'bumberspinner', + 'bumbersplat', + 'bumbersprinkles', + 'bumbersticks', + 'bumberstink', + 'bumberswirl', + 'bumberteeth', + 'bumberthud', + 'bumbertoes', + 'bumberton', + 'bumbertoon', + 'bumbertooth', + 'bumbertwist', + 'bumberwhatsit', + 'bumberwhip', + 'bumberwig', + 'bumberwoof', + 'bumberzaner', + 'bumberzap', + 'bumberzapper', + 'bumberzilla', + 'bumberzoom', + 'bumble', + 'bumblebee', + 'bumblebehr', + 'bumbleberry', + 'bumbleblabber', + 'bumblebocker', + 'bumbleboing', + 'bumbleboom', + 'bumblebounce', + 'bumblebouncer', + 'bumblebrains', + 'bumblebubble', + 'bumblebumble', + 'bumblebump', + 'bumblebumper', + 'bumbleburger', + 'bumblechomp', + 'bumblecorn', + 'bumblecrash', + 'bumblecrumbs', + 'bumblecrump', + 'bumblecrunch', + 'bumbledoodle', + 'bumbledorf', + 'bumbleface', + 'bumblefidget', + 'bumblefink', + 'bumblefish', + 'bumbleflap', + 'bumbleflapper', + 'bumbleflinger', + 'bumbleflip', + 'bumbleflipper', + 'bumblefoot', + 'bumblefuddy', + 'bumblefussen', + 'bumblegadget', + 'bumblegargle', + 'bumblegloop', + 'bumbleglop', + 'bumblegoober', + 'bumblegoose', + 'bumblegrooven', + 'bumblehoffer', + 'bumblehopper', + 'bumblejinks', + 'bumbleklunk', + 'bumbleknees', + 'bumblemarble', + 'bumblemash', + 'bumblemonkey', + 'bumblemooch', + 'bumblemouth', + 'bumblemuddle', + 'bumblemuffin', + 'bumblemush', + 'bumblenerd', + 'bumblenoodle', + 'bumblenose', + 'bumblenugget', + 'bumblephew', + 'bumblephooey', + 'bumblepocket', + 'bumblepoof', + 'bumblepop', + 'bumblepounce', + 'bumblepow', + 'bumblepretzel', + 'bumblequack', + 'bumbler', + "bumbler's", + 'bumbleroni', + 'bumblers', + 'bumblescooter', + 'bumblescreech', + 'bumblesmirk', + 'bumblesnooker', + 'bumblesnoop', + 'bumblesnout', + 'bumblesocks', + 'bumblesoup', + 'bumblespeed', + 'bumblespinner', + 'bumblesplat', + 'bumblesprinkles', + 'bumblesticks', + 'bumblestink', + 'bumbleswirl', + 'bumbleteeth', + 'bumblethud', + 'bumbletoes', + 'bumbleton', + 'bumbletoon', + 'bumbletooth', + 'bumbletwist', + 'bumblewhatsit', + 'bumblewhip', + 'bumblewig', + 'bumblewoof', + 'bumblezaner', + 'bumblezap', + 'bumblezapper', + 'bumblezilla', + 'bumblezoom', + 'bumm', + 'bummed', + 'bummer', + 'bummers', + 'bumming', + 'bumms', + 'bumpenbee', + 'bumpenberry', + 'bumpenblabber', + 'bumpenbocker', + 'bumpenboing', + 'bumpenboom', + 'bumpenbounce', + 'bumpenbouncer', + 'bumpenbrains', + 'bumpenbubble', + 'bumpenbumble', + 'bumpenbump', + 'bumpenbumper', + 'bumpenburger', + 'bumpenchomp', + 'bumpencorn', + 'bumpencrash', + 'bumpencrumbs', + 'bumpencrump', + 'bumpencrunch', + 'bumpendoodle', + 'bumpendorf', + 'bumpenface', + 'bumpenfidget', + 'bumpenfink', + 'bumpenfish', + 'bumpenflap', + 'bumpenflapper', + 'bumpenflinger', + 'bumpenflip', + 'bumpenflipper', + 'bumpenfoot', + 'bumpenfuddy', + 'bumpenfussen', + 'bumpengadget', + 'bumpengargle', + 'bumpengloop', + 'bumpenglop', + 'bumpengoober', + 'bumpengoose', + 'bumpengrooven', + 'bumpenhoffer', + 'bumpenhopper', + 'bumpenjinks', + 'bumpenklunk', + 'bumpenknees', + 'bumpenmarble', + 'bumpenmash', + 'bumpenmonkey', + 'bumpenmooch', + 'bumpenmouth', + 'bumpenmuddle', + 'bumpenmuffin', + 'bumpenmush', + 'bumpennerd', + 'bumpennoodle', + 'bumpennose', + 'bumpennugget', + 'bumpenphew', + 'bumpenphooey', + 'bumpenpocket', + 'bumpenpoof', + 'bumpenpop', + 'bumpenpounce', + 'bumpenpow', + 'bumpenpretzel', + 'bumpenquack', + 'bumpenroni', + 'bumpenscooter', + 'bumpenscreech', + 'bumpensmirk', + 'bumpensnooker', + 'bumpensnoop', + 'bumpensnout', + 'bumpensocks', + 'bumpenspeed', + 'bumpenspinner', + 'bumpensplat', + 'bumpensprinkles', + 'bumpensticks', + 'bumpenstink', + 'bumpenswirl', + 'bumpenteeth', + 'bumpenthud', + 'bumpentoes', + 'bumpenton', + 'bumpentoon', + 'bumpentooth', + 'bumpentwist', + 'bumpenwhatsit', + 'bumpenwhip', + 'bumpenwig', + 'bumpenwoof', + 'bumpenzaner', + 'bumpenzap', + 'bumpenzapper', + 'bumpenzilla', + 'bumpenzoom', + 'bumpkin', + 'bumpy', + 'bun', + 'bunch', + 'bunched', + 'bunches', + 'bunching', + 'bunchy', + 'bund', + 'bundle', + 'bundled', + 'bundler', + 'bundles', + 'bundling', + 'bunions', + 'bunk', + 'bunked', + 'bunker', + 'bunking', + 'bunks', + 'bunnies', + 'bunny', + 'buns', + 'bunsen', + 'bunting', + 'bunyan', + 'buoy', + 'buoys', + 'bur', + 'burden', + 'burdened', + 'bureau', + 'bureaus', + 'burg', + 'burger', + "burger's", + 'burgers', + 'burghers', + 'burglar', + 'burgundy', + 'burial', + 'buried', + 'burier', + 'buries', + 'burly', + 'burn', + 'burned', + 'burner', + 'burners', + 'burning', + 'burnings', + 'burnout', + 'burns', + 'burnt', + 'burp', + 'burped', + 'burping', + 'burps', + 'burr', + 'burred', + 'burrito', + "burrito's", + 'burritos', + 'burrning', + 'burro', + 'burrow', + 'burrowing', + 'burry', + 'burst', + 'bursted', + 'burster', + 'bursting', + 'bursts', + 'burton', + 'bus', + "bus'", + 'buses', + 'bushed', + 'bushel', + "bushel's", + 'bushels', + 'bushes', + 'bushido', + 'bushy', + 'busier', + 'busies', + 'busiest', + 'business', + "business's", + 'businesses', + 'busing', + 'buss', + 'bussed', + 'bussing', + 'busters', + 'bustle', + 'busy', + 'busybody', + 'busywork', + 'but', + 'butler', + "butler's", + 'butlers', + 'butobasu', + 'butte', + 'butted', + 'butter', + 'butterball', + 'butterballs', + 'butterbean', + 'butterbeans', + 'buttercup', + 'buttercups', + 'buttered', + 'butterfingers', + 'butterflies', + 'butterfly', + "butterfly's", + 'butterfly-herders', + 'buttering', + 'buttermilk', + 'butternut', + 'butterscotch', + 'buttery', + 'button', + "button's", + 'buttoned', + 'buttoner', + 'buttoning', + 'buttons', + 'buy', + 'buyable', + 'buyer', + "buyer's", + 'buyers', + 'buying', + 'buys', + 'buzz', + "buzz's", + 'buzz-worthy', + 'buzzard', + 'buzzards', + 'buzzer', + "buzzer's", + 'buzzers', + 'buzzes', + 'buzzing', + 'buzzsaw', + 'buzzword', + 'buzzwords', + 'bwah', + 'bwahaha', + 'bwahahah', + 'bwahahaha', + 'bwahahahah', + 'bwahahahaha', + 'bwahahahahaha', + 'bwahahha', + 'bwhahahaha', + 'by', + 'bye', + 'byes', + 'bygones', + 'bylaws', + 'bypass', + 'bypassed', + 'bypassing', + 'bystander', + 'bystanders', + 'byte', + 'bytes', + 'c#', + "c'mon", + 'c(:', + 'c++', + 'c-flat', + 'c.a.r.t.e.r.', + 'c.f.o.', + 'c.f.o.s', + 'c:', + "ca'me", + 'cab', + 'caballeros', + 'cabana', + 'cabbage', + 'cabbages', + 'caber', + 'cabeza', + 'cabin', + "cabin's", + 'cabinboy', + 'cabinet', + "cabinet's", + 'cabinets', + 'cabins', + 'cable', + 'cables', + 'cabob', + 'caboose', + 'cabs', + 'caca', + 'cache', + 'caches', + 'cackle', + 'cacti', + 'cactus', + "cactus'", + 'cad', + 'caddies', + 'caddy', + "caddy's", + 'cades', + 'cadet', + "cadet's", + 'cadets', + "cadets'", + 'cadillac', + 'cads', + 'caesar', + 'caesious', + 'caf', + 'cafac', + 'cafe', + "cafe's", + 'cafes', + 'cafeteria', + "cafeteria's", + 'cafeterias', + 'caffeine', + 'cage', + "cage's", + 'caged', + 'cages', + 'cahoots', + 'caicaba', + 'caicada', + 'caicama', + 'caicaux', + 'caicos', + 'caicumal', + 'caio', + 'caitlin', + 'cake', + "cake's", + 'caked', + 'cakes', + 'cakewalk', + 'cal', + "cal's", + 'calamari', + 'calamity', + 'calcium', + 'calculate', + 'calculated', + 'calculating', + 'calculations', + 'calculator', + 'calculators', + 'calendar', + 'calendars', + 'calf', + 'caliber', + 'calibrate', + 'calico', + "calico's", + 'california', + 'calked', + 'call', + 'calle', + "callecutter's", + 'called', + 'caller', + 'callers', + 'calligraphy', + 'calling', + 'calls', + 'calluses', + 'calm', + 'calmed', + 'calmer', + 'calmest', + 'calming', + 'calmly', + 'calms', + 'calories', + 'calves', + 'calypso', + 'calzone', + 'calzones', + 'cam', + 'camaada', + 'camaago', + 'camanes', + 'camareau', + 'camaros', + 'camaten', + 'camcorder', + 'came', + 'camellia', + 'camels', + 'cameo', + 'camera', + 'cami', + 'camilla', + 'camillia', + 'camillo', + 'cammy', + 'camouflage', + 'camouflages', + 'camp', + "camp's", + 'campaign', + 'camped', + 'camper', + 'campers', + 'campfire', + 'campfires', + 'campground', + 'campgrounds', + 'camping', + 'camps', + 'campus', + 'camren', + 'camryn', + 'can', + "can's", + "can't", + 'canada', + 'canal', + 'canals', + 'canard', + 'canary', + "canary's", + 'cancel', + 'canceled', + 'canceling', + 'cancelled', + 'cancelling', + 'cancels', + 'candelabra', + 'candelabras', + 'candid', + 'candidate', + "candidate's", + 'candidates', + 'candied', + 'candies', + 'candle', + 'candlelight', + 'candles', + "candles'", + 'candlestick', + "candlestick's", + 'candlesticks', + "candlesticks'", + 'candy', + "candy's", + 'cane', + "cane's", + 'caner', + 'canes', + 'cangrejos', + 'canine', + 'canister', + 'canisters', + 'canker', + 'cannas', + 'canned', + 'cannelloni', + 'canner', + 'canners', + 'cannery', + 'canning', + 'cannon', + "cannon's", + 'cannonade', + 'cannonball', + "cannonball's", + 'cannonballs', + 'cannoned', + "cannoneer's", + 'cannoneering', + 'cannoneers', + 'cannoning', + 'cannonry', + 'cannons', + 'cannot', + 'canny', + 'canoe', + "canoe's", + 'canoed', + 'canoeing', + 'canoes', + "canoes'", + 'canoing', + 'canon', + "canon's", + 'canonballs', + 'cans', + 'canst', + 'cant', + "cant's", + 'cantaloupe', + 'canteen', + 'canteens', + 'canter', + 'canticle', + 'cantina', + 'canto', + 'canton', + 'cants', + 'canvas', + 'canvasses', + 'canyon', + 'canyons', + 'cap', + "cap'n", + "cap's", + 'capabilities', + 'capability', + 'capable', + 'capacities', + 'capacity', + 'cape', + "cape's", + 'caped', + 'capellago', + 'capelligos', + 'caper', + 'capers', + 'capes', + 'capisce', + 'capisci', + 'capisco', + 'capital', + "capital's", + 'capitalize', + 'capitalizes', + 'capitally', + 'capitals', + 'capitano', + 'capitol', + 'capitols', + 'caplock', + 'caplocks', + 'capn', + 'capo', + 'capos', + 'capped', + 'capping', + 'capri', + 'caprice', + 'caps', + 'capsize', + 'capsized', + 'capstan', + 'captain', + "captain's", + 'captained', + 'captaining', + 'captains', + "captains'", + 'captainscolors', + 'captainship', + 'captian', + 'captin', + 'caption', + 'captioning', + 'captivating', + 'captive', + 'captives', + 'capture', + 'captured', + 'capturer', + 'capturers', + 'captures', + 'capturing', + 'caput', + 'capybaras', + 'car', + "car's", + 'cara nz', + 'carafe', + 'caramel', + 'caramelized', + 'caramels', + 'carapace', + 'caravan', + 'caravans', + 'carbon', + 'carbonate', + 'carbonated', + 'card', + "card's", + 'carda', + 'cardboard', + 'carded', + 'carder', + 'cardinal', + 'cardinalfish', + 'cardinals', + 'carding', + 'cardio', + 'cardiologist', + 'cardj', + 'cardk', + 'cardq', + 'cards', + 'care', + 'cared', + 'careening', + 'career', + "career's", + 'careered', + 'careering', + 'careers', + 'carefree', + 'careful', + 'carefully', + 'careless', + 'carer', + 'carers', + 'cares', + 'caretaker', + "caretaker's", + 'caretakers', + 'carey', + 'cargo', + "cargo's", + 'cargoes', + 'cargos', + 'carib', + 'caribbean', + 'caring', + 'carl', + 'carla', + 'carlos', + "carlos's", + 'carmine', + 'carnation', + 'carnations', + 'carnevore', + 'carnival', + "carnival's", + 'carnivale', + 'carnivals', + 'carol', + "carol's", + 'caroling', + 'carols', + 'carousel', + "carousel's", + 'carousels', + 'carpal', + 'carpe', + 'carped', + 'carpel', + 'carpenter', + 'carpenters', + 'carper', + 'carpet', + "carpet's", + 'carpeted', + 'carpeting', + 'carpets', + 'carpool', + 'carrebean', + 'carrera', + "carrera's", + 'carreras', + 'carriage', + 'carribean', + 'carried', + 'carrier', + 'carriers', + 'carries', + 'carrion', + 'carrions', + 'carrot', + "carrot's", + 'carrots', + 'carrou', + 'carrousel', + "carrousel's", + 'carrousels', + 'carry', + "carry's", + 'carrying', + 'cars', + 'carsick', + 'cart', + "cart's", + 'carte', + 'carted', + 'carter', + "carter's", + 'carters', + 'carting', + 'carton', + "carton's", + 'cartons', + 'cartoon', + "cartoon's", + 'cartoonists', + 'cartoons', + 'cartridges', + 'cartrip', + 'carts', + 'carve', + 'carved', + 'carven', + 'carver', + "carver's", + 'carvers', + 'carving', + "carving's", + 'carvings', + 'casa', + 'cascade', + 'cascades', + 'case', + 'cased', + 'cases', + 'cash', + "cash's", + 'cashbot', + "cashbot's", + 'cashbots', + 'cashed', + 'cashemerus-appearus', + 'casher', + 'cashers', + 'cashes', + 'cashew', + 'cashews', + 'cashier', + "cashier's", + 'cashiers', + 'cashing', + 'cashmere', + 'casing', + 'casings', + 'caspian', + "caspian's", + 'caspians', + 'cassandra', + 'casserole', + 'cast', + "cast's", + 'castanet', + 'castanets', + 'castaway', + "castaway's", + 'castaways', + 'caste', + 'casted', + 'caster', + 'casters', + 'casting', + 'castings', + 'castle', + "castle's", + 'castled', + 'castles', + 'castling', + 'casts', + 'casual', + 'casually', + 'casualties', + 'cat', + "cat's", + 'cat-eye', + 'cataclysm', + 'cataclysms', + 'catacomb', + 'catacombs', + 'catalog', + "catalog's", + 'cataloging', + 'catalogs', + 'catalogue', + 'cataloguing', + 'catalyst', + 'catapult', + 'cataract', + 'cataracts', + 'catastrophe', + 'catastrophic', + 'catatonic', + 'catch', + 'catcher', + "catcher's", + 'catchers', + 'catches', + "catchin'", + 'catching', + 'catchy', + 'categories', + 'category', + "category's", + 'cater', + 'catered', + 'catering', + 'caterpillar', + "caterpillar's", + 'caterpillar-herdering', + 'caterpillar-shearing', + 'caterpillars', + 'caters', + 'cateye', + 'catfight', + 'catfights', + 'catfish', + 'catherine', + 'catholic', + 'cathrine', + 'catish', + 'catnip', + 'cats', + 'catsup', + 'cattle', + 'cattlelog', + 'catty', + 'caught', + 'cauldron', + "cauldron's", + 'cauldrons', + 'cauliflower', + 'cause', + 'caused', + 'causer', + 'causes', + 'causing', + 'caution', + 'cautioned', + 'cautioner', + 'cautioners', + 'cautioning', + 'cautionings', + 'cautions', + 'cautious', + 'cautiously', + 'cava', + 'cavalier', + 'cavaliers', + 'cavalry', + "cavalry's", + 'cave', + "cave's", + 'caveat', + 'caved', + 'caveman', + 'cavemen', + 'caver', + 'cavern', + "cavern's", + 'caverns', + 'caves', + 'caviar', + "caviar's", + 'caving', + 'cavort', + 'cavy', + 'caws', + 'cbhq', + 'ccg', + 'ccger', + 'cd', + "cd's", + 'cds', + 'cdt', + 'cease', + 'ceased', + 'ceasefire', + 'ceases', + 'ceasing', + 'cecile', + 'cedar', + 'cee', + 'ceiling', + "ceiling's", + 'ceilings', + 'celeb', + "celeb's", + 'celebrate', + 'celebrated', + 'celebrates', + 'celebrating', + 'celebration', + 'celebration-setup', + 'celebrationen', + 'celebrations', + 'celebrities', + 'celebrity', + 'celebs', + 'celery', + 'cell', + 'cellar', + 'cellars', + 'cellmate', + 'cellos', + 'cells', + 'cellular', + 'cellulite', + 'celtic', + 'cement', + 'cements', + 'cemetery', + 'censor', + 'censored', + 'censoring', + 'censors', + 'censorship', + 'cent', + 'centaur', + "centaur's", + 'centaurs', + 'center', + "center's", + 'centered', + 'centering', + 'centerpiece', + 'centers', + 'centimeter', + 'central', + 'centrally', + 'centrals', + 'centre', + 'cents', + 'centuries', + 'centurion', + "centurion's", + 'centurions', + 'century', + "century's", + 'ceo', + "ceo'd", + "ceo's", + 'ceod', + 'ceoing', + 'ceos', + 'ceramic', + 'cerdo', + 'cereal', + "cereal's", + 'cereals', + 'ceremonies', + 'ceremony', + "ceremony's", + 'cert', + 'certain', + 'certainly', + 'certificate', + "certificate's", + 'certificated', + 'certificates', + 'certificating', + 'certification', + 'certifications', + 'certified', + 'cerulean', + 'cesar', + "cesar's", + 'cezanne', + 'cfo', + "cfo'd", + "cfo's", + 'cfoed', + 'cfoing', + 'cfos', + 'cg', + 'chad', + "chad's", + 'chads', + 'chafed', + 'chafes', + 'chain', + "chain's", + 'chained', + 'chaining', + 'chains', + 'chair', + "chair's", + 'chaired', + 'chairing', + 'chairlift', + 'chairs', + 'chaise', + 'chalet', + 'chalk', + 'chalkboard', + 'chalkboards', + 'challenge', + "challenge's", + 'challenged', + 'challenger', + "challenger's", + 'challengers', + 'challenges', + 'challenging', + 'chamber', + "chamber's", + 'chambered', + 'chamberer', + 'chamberers', + 'chambering', + 'chamberpot', + 'chambers', + 'chameleon', + 'chameleons', + 'champ', + "champ's", + 'champagne', + 'champion', + "champion's", + 'champions', + 'championship', + 'champs', + 'chan', + 'chance', + 'chanced', + 'chances', + 'chancing', + 'chancy', + 'chandelier', + 'chandler', + 'chanel', + 'change', + 'change-o', + 'changeable', + 'changed', + 'changer', + 'changers', + 'changes', + "changin'", + 'changing', + 'chanisthebest', + 'channel', + "channel's", + 'channeled', + 'channels', + 'chant', + "chant's", + 'chanted', + 'chanting', + 'chants', + 'chanty', + 'chanukah', + 'chaos', + 'chaotic', + 'chap', + 'chapeau', + 'chapel', + 'chappy', + 'chaps', + 'chapsitck', + 'chapter', + "chapter's", + 'chaptered', + 'chaptering', + 'chapters', + 'char', + 'character', + "character's", + 'charactered', + 'charactering', + 'characteristics', + 'characters', + 'charade', + "charade's", + 'charades', + 'charcoal', + 'chard', + 'chare', + 'chares', + 'charge', + 'charged', + 'charger', + "charger's", + 'chargers', + 'charges', + 'charging', + 'chariot', + 'charisma', + 'charismatic', + 'charitable', + 'charity', + 'charles', + 'charley', + 'charlie', + "charlie's", + 'charlotte', + 'charlottes', + 'charm', + "charm's", + 'charmed', + 'charmer', + 'charmers', + 'charming', + "charming's", + 'charmingly', + 'charms', + 'charred', + 'chars', + 'chart', + "chart's", + 'charted', + 'charter', + 'chartered', + 'charters', + 'charting', + 'chartings', + 'chartreuse', + 'charts', + 'chase', + "chase's", + 'chased', + 'chaser', + 'chasers', + 'chases', + 'chasing', + 'chasse', + 'chassed', + 'chastise', + 'chastised', + 'chastity', + 'chat', + "chat's", + 'chateau', + 'chatoyant', + 'chats', + 'chatted', + 'chatter', + "chatter's", + 'chattering', + 'chatters', + 'chatting', + 'chatty', + 'chauffer', + 'chauffeur', + 'chd', + 'cheap', + 'cheapen', + 'cheapens', + 'cheaper', + 'cheapest', + 'cheaply', + 'cheapo', + 'cheapskate', + 'cheapskates', + 'cheat', + "cheat's", + 'cheated', + 'cheater', + "cheater's", + 'cheaters', + 'cheating', + 'cheats', + 'check', + "check's", + 'checkbook', + 'checkbox', + 'checked', + 'checker', + "checker's", + 'checkerboard', + 'checkered', + 'checkers', + 'checking', + 'checklist', + 'checkmark', + 'checkout', + 'checkpoint', + 'checks', + 'checkup', + 'cheddar', + 'cheek', + 'cheeks', + 'cheeky', + 'cheep', + 'cheer', + "cheer's", + 'cheered', + 'cheerer', + 'cheerers', + 'cheerful', + 'cheerier', + 'cheering', + 'cheerio', + 'cheerios', + 'cheerleader', + 'cheerleaders', + 'cheerleading', + 'cheerly', + 'cheers', + 'cheery', + 'cheese', + "cheese's", + 'cheeseburger', + "cheeseburger's", + 'cheeseburgers', + 'cheesecake', + 'cheesed', + 'cheeses', + 'cheesey', + 'cheesier', + 'cheesiest', + 'cheesiness', + 'cheesing', + 'cheesy', + 'cheetah', + "cheetah's", + 'cheetahs', + 'cheezer', + 'cheezybee', + 'cheezyberry', + 'cheezyblabber', + 'cheezybocker', + 'cheezyboing', + 'cheezyboom', + 'cheezybounce', + 'cheezybouncer', + 'cheezybrains', + 'cheezybubble', + 'cheezybumble', + 'cheezybump', + 'cheezybumper', + 'cheezyburger', + 'cheezychomp', + 'cheezycorn', + 'cheezycrash', + 'cheezycrumbs', + 'cheezycrump', + 'cheezycrunch', + 'cheezydoodle', + 'cheezydorf', + 'cheezyface', + 'cheezyfidget', + 'cheezyfink', + 'cheezyfish', + 'cheezyflap', + 'cheezyflapper', + 'cheezyflinger', + 'cheezyflip', + 'cheezyflipper', + 'cheezyfoot', + 'cheezyfuddy', + 'cheezyfussen', + 'cheezygadget', + 'cheezygargle', + 'cheezygloop', + 'cheezyglop', + 'cheezygoober', + 'cheezygoose', + 'cheezygrooven', + 'cheezyhoffer', + 'cheezyhopper', + 'cheezyjinks', + 'cheezyklunk', + 'cheezyknees', + 'cheezymarble', + 'cheezymash', + 'cheezymonkey', + 'cheezymooch', + 'cheezymouth', + 'cheezymuddle', + 'cheezymuffin', + 'cheezymush', + 'cheezynerd', + 'cheezynoodle', + 'cheezynose', + 'cheezynugget', + 'cheezyphew', + 'cheezyphooey', + 'cheezypocket', + 'cheezypoof', + 'cheezypop', + 'cheezypounce', + 'cheezypow', + 'cheezypretzel', + 'cheezyquack', + 'cheezyroni', + 'cheezyscooter', + 'cheezyscreech', + 'cheezysmirk', + 'cheezysnooker', + 'cheezysnoop', + 'cheezysnout', + 'cheezysocks', + 'cheezyspeed', + 'cheezyspinner', + 'cheezysplat', + 'cheezysprinkles', + 'cheezysticks', + 'cheezystink', + 'cheezyswirl', + 'cheezyteeth', + 'cheezythud', + 'cheezytoes', + 'cheezyton', + 'cheezytoon', + 'cheezytooth', + 'cheezytwist', + 'cheezywhatsit', + 'cheezywhip', + 'cheezywig', + 'cheezywoof', + 'cheezyzaner', + 'cheezyzap', + 'cheezyzapper', + 'cheezyzilla', + 'cheezyzoom', + 'chef', + "chef's", + 'chefs', + 'chelsea', + "chelsea's", + 'chelseas', + 'chemical', + 'chemicals', + 'cherish', + 'cherishes', + 'chernabog', + "chernabog's", + 'cherries', + 'cherrywood', + 'cherubfish', + 'cheshire', + "cheshire's", + 'chess', + 'chest', + 'chester', + 'chestnut', + 'chestnut-shell', + 'chetagua', + 'chetermo', + 'chetik', + 'chetros', + 'chettia', + 'chetuan', + 'chevalle', + 'chevrolet', + 'chew', + 'chewed', + 'chewing', + 'cheyenne', + "cheyenne's", + 'cheyennes', + 'chez', + 'chic', + 'chick', + "chick's", + 'chickadee', + 'chickadees', + 'chicken', + "chicken's", + 'chickened', + 'chickenhearted', + 'chickening', + 'chickens', + 'chicks', + 'chief', + "chief's", + 'chiefly', + 'chiefs', + 'chiffon', + 'chihuahua', + 'child', + "child's", + 'childcare', + 'childhood', + 'childish', + 'childlike', + 'children', + "children's", + 'chili', + "chili's", + 'chill', + "chill's", + 'chilled', + 'chillin', + "chillin'", + 'chilling', + 'chills', + 'chilly', + 'chillycog', + 'chim', + 'chime', + "chime's", + 'chimes', + 'chiming', + 'chimnes', + 'chimney', + 'chimneys', + 'chimp', + 'chin', + "chin's", + 'china', + 'chinchilla', + "chinchilla's", + 'chinchillas', + 'chine', + 'ching', + 'chino', + 'chins', + 'chip', + "chip's", + 'chipmunk', + "chipmunk's", + 'chipmunks', + 'chipotle', + 'chipped', + 'chipper', + 'chipping', + 'chips', + 'chiropractic', + 'chiropractor', + 'chirp', + 'chirping', + 'chirpy', + 'chisel', + 'chit', + 'chit-chat', + 'chivalrous', + 'chivalry', + 'chloe', + 'choc', + 'chock', + 'chocolate', + "chocolate's", + 'chocolates', + 'choice', + 'choicely', + 'choicer', + 'choices', + 'choicest', + 'choir', + 'choirs', + 'choking', + 'chomp', + 'chomping', + 'chomugon', + 'choo', + 'choo-choo', + "choo-choo's", + 'choo-choos', + 'chook', + 'choose', + 'chooser', + 'choosers', + 'chooses', + 'choosey', + 'choosing', + 'choosy', + 'chop', + 'chopin', + 'chopped', + 'chopper', + 'choppers', + 'choppier', + 'choppiness', + 'chopping', + 'choppy', + 'chops', + 'chopsticks', + 'choral', + 'chord', + 'chords', + 'chore', + 'choreographed', + 'chores', + 'chortle', + 'chortled', + 'chortles', + 'chortling', + 'chorus', + 'chose', + 'chosen', + 'chow', + 'chowder', + 'chris', + 'christin', + 'christina', + "christina's", + 'christinas', + 'christmas', + 'christmastime', + 'christopher', + 'chrome', + "chrome's", + 'chromed', + 'chromes', + 'chronicle', + 'chronicled', + 'chronicles', + 'chronicling', + 'chrysler', + 'chuck', + "chuck's", + 'chucked', + 'chucking', + 'chuckle', + "chuckle's", + 'chuckled', + 'chuckles', + 'chuckling', + 'chucks', + 'chucky', + 'chuff', + 'chug', + 'chugged', + 'chugging', + 'chugyo', + 'chum', + "chum's", + 'chummed', + 'chums', + 'chunk', + 'chunked', + 'chunking', + 'chunks', + 'chunky', + 'church', + 'churches', + 'churn', + 'churned', + 'churning', + 'churro', + "churro's", + 'churros', + 'chute', + 'chutes', + 'cia', + 'ciao', + 'ciaran', + 'cid', + 'cider', + 'cienfuegos', + 'cierra', + 'cimson', + 'cinammon', + 'cinch', + 'cinda', + "cinda's", + 'cinder', + "cinder's", + 'cinderbones', + 'cinderella', + "cinderella's", + 'cinderellas', + 'cinders', + 'cindy', + 'cine', + 'cinema', + "cinema's", + 'cinemas', + 'cinematic', + 'cinematics', + 'cineplex', + 'cinerama', + 'cinnamon', + "cinnamon's", + 'cinnamons', + 'cir', + 'circ', + 'circle', + "circle's", + 'circled', + 'circler', + 'circlers', + 'circles', + 'circling', + 'circuit', + "circuit's", + 'circuited', + 'circuiting', + 'circuits', + 'circular', + "circular's", + 'circularly', + 'circulars', + 'circumnavigate', + 'circumstance', + 'circumstances', + 'circumvent', + 'circus', + "circus's", + 'circuses', + 'citadel', + 'cite', + 'cites', + 'cities', + 'citified', + 'citing', + 'citizen', + "citizen's", + 'citizenly', + 'citizens', + 'citn', + 'citrine', + 'citrus', + 'city', + 'civics', + 'civil', + 'civilians', + 'civility', + 'civilization', + 'civilizations', + 'civilized', + 'cj', + "cj'd", + "cj's", + 'cjed', + 'cjing', + 'cjs', + 'clack', + 'clad', + 'claim', + "claim's", + 'claimed', + 'claimer', + 'claiming', + 'claims', + 'claire', + 'clam', + "clam's", + 'clamed', + 'clammed', + 'clams', + 'clan', + 'clang', + 'clangs', + 'clank', + 'clans', + 'clap', + 'clapped', + 'clapping', + 'claps', + 'clara', + 'clarabelle', + "clarabelle's", + 'clarence', + 'clarified', + 'clarify', + 'clarifying', + 'clarion', + 'clarissa', + 'clarity', + 'clark', + 'clash', + 'clashes', + 'clashing', + 'class', + "class's", + 'classed', + 'classer', + 'classes', + 'classic', + "classic's", + 'classical', + 'classics', + 'classiest', + 'classifications', + 'classified', + 'classify', + 'classing', + 'classmate', + "classmate's", + 'classmates', + 'classy', + 'claus', + 'clause', + 'clauses', + 'claustrophobia', + 'claustrophobic', + 'clavier', + 'claw', + "claw's", + 'clawed', + 'clawing', + 'claws', + 'clawssified', + 'clay', + 'clayton', + 'clean', + 'cleaned', + 'cleaner', + "cleaner's", + 'cleaners', + 'cleanest', + 'cleaning', + 'cleanliness', + 'cleanly', + 'cleanout', + 'cleans', + 'cleanse', + 'cleansing', + 'cleanup', + 'clear', + 'clearance', + 'cleared', + 'clearer', + 'clearest', + 'clearing', + 'clearings', + 'clearly', + 'clears', + "cleat's", + 'cleats', + 'cleave', + 'cleaved', + 'cleaver', + 'cleaves', + 'cleff', + 'clementine', + 'cleric', + 'clerics', + 'clerk', + "clerk's", + 'clerks', + 'clever', + 'clew', + 'click', + 'click-and-drag', + 'clickable', + 'clickables', + 'clicked', + 'clicker', + 'clickers', + 'clicking', + 'clicks', + 'client', + "client's", + 'clientele', + 'clients', + 'cliff', + "cliff's", + 'cliffs', + 'climactic', + 'climate', + "climate's", + 'climates', + 'climb', + 'climbed', + 'climber', + 'climbers', + 'climbing', + 'climbs', + 'clime', + 'cling', + 'clinger', + 'clinging', + 'clings', + 'clingy', + 'clinic', + 'clinics', + 'clink', + 'clinker', + 'clip', + 'clipboard', + 'clipped', + 'clipper', + 'clippers', + 'clipping', + 'clips', + 'clique', + 'cloak', + 'cloaking', + 'clobber', + 'clobbered', + 'clock', + 'clocked', + 'clocker', + 'clockers', + 'clocking', + 'clockings', + 'clocks', + 'clockwise', + 'clockwork', + 'clockworks', + 'clod', + 'clodley', + 'clods', + 'clog', + 'clogged', + 'clogging', + 'clogs', + 'clomping', + 'clone', + "clone's", + 'cloned', + 'clones', + 'cloning', + 'clonk', + 'clopping', + 'close', + 'close-up', + 'closed', + 'closely', + 'closeness', + 'closer', + 'closers', + 'closes', + 'closest', + 'closet', + 'closets', + 'closing', + 'closings', + 'closure', + 'cloth', + 'clothe', + 'clothed', + 'clothes', + 'clothesline', + 'clothespins', + 'clothing', + 'cloths', + 'clots', + 'cloture', + 'cloud', + "cloud's", + 'clouded', + 'clouding', + 'clouds', + 'cloudy', + 'clout', + 'clove', + 'clover', + "clover's", + 'cloverleaf', + "cloverleaf's", + 'cloverleafs', + 'clovers', + 'cloves', + 'clovi', + 'clovinia', + 'clovis', + 'clowder', + 'clown', + 'clowns', + 'clu', + 'club', + "club's", + 'club33', + 'clubbing', + 'clubhouse', + 'clubpenguin', + 'clubs', + 'clucked', + 'clucking', + 'clue', + "clue's", + 'clued', + 'clueing', + 'clueless', + 'clues', + 'clump', + 'clumped', + 'clumsies', + "clumsies'", + 'clumsily', + 'clumsy', + 'clunk', + 'clunker', + 'clunkers', + 'clunky', + 'cluster', + "cluster's", + 'clustered', + 'clusters', + 'clutch', + 'clutches', + 'clutching', + 'clutter', + 'cluttered', + 'clyde', + 'clydesdale', + 'co', + 'co-starred', + 'coach', + "coach's", + "coache's", + 'coached', + 'coacher', + 'coaches', + 'coaching', + 'coal', + "coal's", + 'coaled', + 'coaler', + 'coalfire', + 'coaling', + 'coalmine', + 'coals', + 'coarse', + 'coast', + 'coastal', + 'coasted', + 'coaster', + "coaster's", + 'coasters', + 'coasting', + 'coastline', + 'coasts', + 'coat', + "coat's", + 'coated', + 'coater', + 'coatings', + 'coats', + 'coax', + 'coaxes', + 'cobalt', + 'cobber', + 'cobble', + 'cobbler', + 'cobbles', + 'cobblestone', + 'cobra', + "cobra's", + 'cobras', + 'cobweb', + 'cobwebs', + 'coca', + 'coco', + "coco's", + 'cocoa', + 'coconut', + "coconut's", + 'coconuts', + 'cod', + 'coda', + 'codas', + 'coddles', + 'code', + "code's", + 'codec', + 'coded', + 'coder', + 'coders', + 'codes', + 'codex', + 'codfish', + 'coding', + 'codings', + 'cods', + 'cody', + "cody's", + 'coed', + 'coerce', + 'coffee', + "coffee's", + 'coffees', + 'coffer', + 'coffers', + 'cog', + "cog's", + 'cog-o-war', + 'cog-tastrophe', + 'cog0war', + "cogbuck's", + 'cogbucks', + 'cogcicle', + 'cognation', + 'cogowar', + 'cogs', + 'cogwar', + 'coherently', + 'cohesive', + 'cohort', + 'coiffure', + 'coiled', + 'coin', + "coin's", + 'coinage', + 'coincide', + 'coincidence', + 'coincidences', + 'coined', + 'coiner', + 'coining', + 'coins', + 'cola', + 'colada', + "colada's", + 'coladas', + 'cold', + "cold's", + 'colder', + 'coldest', + 'coldly', + 'colds', + 'cole', + "cole's", + 'coleman', + "coleman's", + 'colemans', + 'colestra', + 'colette', + "colette's", + 'colettes', + 'colin', + "colin's", + 'coliseum', + "coliseum's", + 'coliseums', + 'collaborate', + 'collaboration', + 'collage', + 'collapse', + 'collapsed', + 'collapsing', + 'collar', + 'collard', + 'collars', + 'collateral', + 'collaterals', + 'colleague', + 'colleagues', + 'collect', + "collect's", + 'collectable', + 'collectables', + 'collected', + 'collectible', + 'collectibles', + 'collecting', + 'collection', + "collection's", + 'collections', + 'collective', + 'collector', + 'collectors', + 'collects', + 'colleen', + 'colleens', + 'college', + 'colleting', + 'collette', + "collette's", + 'collettes', + 'collide', + 'collie', + 'collier', + 'collision', + "collision's", + 'collisions', + 'colm', + 'cologne', + 'colombia', + 'colonel', + 'colonial', + 'colonials', + 'colonies', + 'colonized', + 'colony', + 'color', + "color's", + 'colorblind', + 'colored', + 'colorfast', + 'colorful', + 'colorhost', + 'coloring', + 'colorless', + 'colors', + 'colossal', + 'colossus', + 'colour', + "colour's", + 'coloured', + 'colourful', + 'colouring', + 'colours', + 'cols', + 'colt', + 'coltello', + 'coltellos', + 'colts', + 'columbia', + "columbia's", + 'columbus', + 'column', + 'columns', + 'coma', + 'comatose', + 'comb', + "comb's", + 'combat', + "combat's", + 'combatants', + 'combater', + 'combative', + 'combats', + 'combination', + "combination's", + 'combinations', + 'combine', + 'combined', + 'combiner', + 'combiners', + 'combines', + 'combing', + 'combining', + 'combo', + "combo's", + 'combos', + 'combs', + 'combustible', + 'combustion', + 'come', + 'comeback', + 'comebacks', + 'comedian', + 'comedians', + 'comedies', + 'comedown', + 'comedy', + 'comely', + 'comer', + 'comers', + 'comes', + 'comet', + 'comfort', + 'comfortable', + 'comfortably', + 'comforted', + 'comforter', + 'comforters', + 'comforting', + 'comforts', + 'comfy', + 'comic', + "comic's", + 'comic-con', + 'comical', + 'comics', + 'coming', + 'comings', + 'comma', + 'command', + "command's", + 'commandant', + 'commanded', + 'commandeer', + 'commandeered', + 'commandeering', + 'commander', + "commander's", + 'commanders', + 'commanding', + 'commando', + 'commands', + 'commence', + 'commencer', + 'commences', + 'commencing', + 'commend', + 'commendations', + 'comment', + "comment's", + 'commentary', + 'commented', + 'commenter', + 'commenting', + 'comments', + 'commerce', + 'commercial', + 'commercially', + 'commercials', + 'commissar', + 'commission', + "commission's", + 'commissioned', + 'commissioner', + 'commissioners', + 'commissioning', + 'commissions', + 'commit', + 'commitment', + "commitment's", + 'commitments', + 'commits', + 'committed', + 'committee', + "committee's", + 'committees', + 'committing', + 'commodore', + "commodore's", + 'commodores', + 'common', + 'commoner', + "commoner's", + 'commoners', + 'commonest', + 'commonly', + 'commons', + 'commotion', + 'communal', + 'communicate', + 'communicated', + 'communicates', + 'communicating', + 'communication', + 'communications', + 'communicative', + 'communist', + 'communities', + 'community', + "community's", + 'commute', + 'comp', + "comp's", + 'compact', + 'companies', + 'companion', + 'companions', + 'companionships', + 'company', + "company's", + 'companying', + 'comparable', + 'comparatively', + 'compare', + 'compared', + 'comparer', + 'compares', + 'comparing', + 'comparison', + "comparison's", + 'comparisons', + 'compartments', + 'compass', + 'compassed', + 'compasses', + 'compassing', + 'compassion', + 'compassionate', + 'compatibility', + 'compatible', + 'compel', + 'compelled', + 'compensate', + 'compensates', + 'compensating', + 'compensation', + 'compete', + 'competed', + 'competence', + 'competences', + 'competent', + 'competes', + 'competing', + 'competition', + "competition's", + 'competitions', + 'competitive', + 'complain', + 'complained', + 'complainer', + "complainer's", + 'complainers', + 'complaining', + 'complains', + 'complaint', + "complaint's", + 'complaints', + 'complement', + 'complete', + 'completed', + 'completely', + 'completer', + 'completes', + 'completing', + 'completion', + "completion's", + 'completions', + 'completive', + 'complex', + 'complexes', + 'complexly', + 'complicate', + 'complicated', + 'complicates', + 'complicating', + 'complication', + 'complications', + 'complied', + 'compliment', + "compliment's", + 'complimentary', + 'complimented', + 'complimenting', + 'compliments', + 'comply', + 'component', + "component's", + 'components', + 'compos', + 'compose', + 'composed', + 'composer', + "composer's", + 'composers', + 'composes', + 'composing', + 'composition', + 'compost', + 'composure', + 'compound', + 'compounded', + 'compounds', + 'comprehend', + 'compress', + 'compressed', + 'compresses', + 'compression', + 'compressions', + 'compromise', + 'compromising', + 'comps', + 'compulsive', + 'compulsively', + 'compute', + 'computer', + "computer's", + 'computers', + 'computes', + 'computing', + 'comrade', + 'comrades', + 'con', + "con's", + 'conceal', + 'concealment', + 'concede', + 'conceited', + 'conceivably', + 'conceived', + 'concentrate', + 'concentrated', + 'concentrates', + 'concentrating', + 'concentration', + 'concentrations', + 'concentrative', + 'concept', + "concept's", + 'concepts', + 'concern', + "concern's", + 'concerned', + 'concernedly', + 'concerner', + 'concerners', + 'concerning', + 'concerns', + 'concert', + 'concertina', + 'concerto', + 'concerts', + 'concession', + 'conch', + 'conches', + 'conclude', + 'concluded', + 'concludes', + 'concluding', + 'conclusion', + "conclusion's", + 'conclusions', + 'concoction', + 'concord', + 'concourse', + 'concrete', + 'concreted', + 'concretely', + 'concretes', + 'concreting', + 'concretion', + 'concur', + 'concussed', + 'concussion', + 'concussions', + 'condense', + 'condescending', + 'condition', + 'conditioned', + 'conditioner', + 'conditioners', + 'conditioning', + 'conditions', + 'condo', + 'condolence', + 'condolences', + 'condone', + 'condoning', + 'condor', + 'condorman', + 'conduct', + 'conducted', + 'conducting', + 'conductive', + 'conductor', + 'conducts', + 'conduit', + 'conduits', + 'cone', + 'cones', + 'conf', + 'conference', + "conference's", + 'conferences', + 'conferencing', + 'confess', + 'confessing', + 'confession', + 'confetti', + 'confidant', + 'confide', + 'confidence', + 'confidences', + 'confident', + 'confidential', + 'confidentially', + 'confidently', + 'configuration', + 'configure', + 'configured', + 'configuring', + 'confine', + 'confined', + 'confinement', + 'confines', + 'confirm', + 'confirmation', + 'confirmed', + 'confirming', + 'confirms', + 'confiscate', + 'confiscated', + 'conflict', + 'conflicted', + 'conflicting', + 'conflictive', + 'conflicts', + 'conform', + 'conformed', + 'conformist', + 'conformists', + 'confounded', + 'confront', + 'confrontation', + 'confronted', + 'confuse', + 'confused', + 'confuser', + 'confusers', + 'confuses', + 'confusing', + 'confusion', + 'confusions', + 'conga', + "conga's", + 'congas', + 'congeniality', + 'congested', + 'congrats', + 'congratulate', + 'congratulated', + 'congratulates', + 'congratulating', + 'congratulation', + 'congratulations', + 'congratulatory', + 'congregate', + 'congregates', + 'congregation', + 'congrejos', + 'congress', + "congress's", + 'congressed', + 'congresses', + 'congressing', + 'conjunction', + 'conjure', + 'conman', + 'connect', + 'connected', + 'connecter', + 'connecters', + 'connecting', + 'connection', + "connection's", + 'connections', + 'connective', + 'connectivity', + 'connectors', + 'connects', + 'conned', + 'connie', + 'connor', + 'conor', + 'conqestadors', + 'conquer', + 'conquered', + 'conquerer', + "conquerer's", + 'conquerers', + 'conquering', + 'conqueror', + 'conquerors', + 'conquers', + 'conquest', + 'conquests', + 'conquistador', + 'conquistadors', + 'conrad', + "conrad's", + 'conrads', + 'cons', + 'conscience', + 'conscious', + 'consciousness', + 'conscript', + 'consensus', + 'consent', + 'consequence', + "consequence's", + 'consequences', + 'consequently', + 'conservation', + 'conservative', + "conservative's", + 'conservatively', + 'conservatives', + 'conservatory', + 'conserve', + 'consider', + 'considerably', + 'considerate', + 'consideration', + 'considerations', + 'considered', + 'considerer', + "considerer's", + 'considerers', + 'considering', + 'considers', + 'consign', + 'consist', + 'consisted', + 'consistent', + 'consistently', + 'consisting', + 'consists', + 'consolation', + 'console', + "console's", + 'consoles', + 'consolidation', + 'consonant', + 'consonants', + 'conspicuous', + 'conspiracy', + 'conspiring', + 'constable', + 'constance', + 'constant', + 'constantly', + 'constants', + 'constellation', + 'constellations', + 'constitution', + 'constrain', + 'constrict', + 'construct', + 'construction', + "construction's", + 'constructions', + 'constructive', + 'consul', + 'consult', + 'consultation', + 'consulted', + 'consulting', + 'consumable', + 'consumables', + 'consume', + 'consumed', + 'consumer', + "consumer's", + 'consumers', + 'consumes', + 'consuming', + 'consumption', + 'cont', + 'contact', + 'contacted', + 'contacting', + 'contacts', + 'contagious', + 'contain', + 'contained', + 'container', + "container's", + 'containers', + 'containing', + 'contains', + 'contaminated', + 'contemplate', + 'contemplates', + 'contemplating', + 'contemplative', + 'contemporary', + 'contempt', + 'contend', + 'contender', + 'content', + "content's", + 'contented', + 'contenting', + 'contention', + 'contently', + 'contents', + 'contest', + "contest's", + 'contestant', + 'contests', + 'context', + "context's", + 'contexts', + 'continent', + 'continents', + 'contingent', + 'continual', + 'continually', + 'continuation', + 'continue', + 'continued', + 'continuer', + 'continues', + 'continuing', + 'continuous', + 'continuously', + 'contra', + 'contraband', + 'contract', + "contract's", + 'contracted', + 'contracting', + 'contractions', + 'contractive', + 'contractor', + 'contracts', + 'contradict', + 'contradiction', + 'contradicts', + 'contranym', + 'contranyms', + 'contraption', + "contraption's", + 'contraptions', + 'contrary', + 'contrast', + 'contribute', + 'contributed', + 'contributer', + "contributer's", + 'contributers', + 'contributes', + 'contributing', + 'contribution', + 'contributions', + 'contributive', + 'control', + "control's", + 'controled', + 'controling', + 'controlled', + 'controller', + 'controllers', + 'controlling', + 'controls', + 'controversy', + 'convection', + 'convene', + 'convenience', + 'convenient', + 'conveniently', + 'convention', + 'conventional', + 'conventions', + 'converge', + 'converging', + 'conversation', + "conversation's", + 'conversations', + 'converse', + 'conversed', + 'conversely', + 'conversing', + 'conversion', + "conversion's", + 'conversions', + 'convert', + 'converted', + 'converter', + 'convertible', + 'converting', + 'converts', + 'convey', + 'convict', + 'conviction', + 'convicts', + 'convince', + 'convinced', + 'convincer', + 'convincing', + 'convoy', + 'convoys', + 'coo', + 'cook', + "cook's", + 'cookbook', + "cookbook's", + 'cookbooks', + 'cooked', + 'cooker', + "cooker's", + 'cookers', + 'cookie', + "cookie's", + 'cookies', + 'cookiesncream', + "cookin'", + 'cooking', + 'cookouts', + 'cooks', + 'cool', + 'cool-errific', + 'cool-palooza', + 'coolcats', + 'cooldown', + 'cooled', + 'cooler', + "cooler's", + 'coolers', + 'coolest', + 'cooling', + "cooling's", + 'coolings', + 'coolio', + 'coolly', + 'coolness', + 'cools', + 'coop', + 'cooper', + 'cooperate', + 'cooperates', + 'cooperating', + 'cooperation', + 'cooperative', + 'coordinate', + 'coordinated', + 'coordinates', + 'coordination', + 'cooties', + 'cop', + "cop's", + 'cope', + 'copes', + "copie's", + 'copied', + 'copier', + "copier's", + 'copiers', + 'copies', + 'coping', + 'copper', + "copper's", + 'coppered', + 'coppering', + 'coppers', + 'coppertop', + 'copping', + 'cops', + 'copter', + 'copters', + 'copy', + "copy's", + 'copycat', + 'copying', + 'copyright', + 'copyrightable', + 'copyrighted', + 'cora', + "cora's", + 'coral', + "coral's", + 'corals', + 'corazon', + 'corbin', + "corbin's", + 'cord', + "cord's", + 'corded', + 'corder', + 'cordial', + 'cordially', + 'cording', + 'cordless', + 'cords', + 'core', + 'coriander', + 'corianders', + 'cork', + 'corks', + 'corkscrew', + "corkscrew's", + 'corkscrews', + 'corky', + 'corm', + 'corn', + "corn's", + 'cornball', + "cornball's", + 'cornballs', + 'cornbread', + 'corned', + 'cornelius', + "cornelius'", + 'corner', + "corner's", + 'cornered', + 'cornering', + 'corners', + 'cornfields', + 'cornflower', + 'cornflowers', + 'corns', + 'corny', + 'coronium', + 'corporal', + 'corporate', + 'corporation', + 'corporations', + 'corps', + 'corral', + "corral's", + 'corrals', + 'correct', + 'corrected', + 'correcting', + 'correction', + 'corrective', + 'correctly', + 'correctness', + 'corrects', + 'correlation', + 'corresponding', + 'corridor', + 'corrupt', + 'corrupted', + 'corrupting', + 'corruption', + 'corsair', + 'corsaire', + 'corsairs', + 'corseers', + 'corset', + 'corsets', + 'cortada', + 'cortagua', + 'cortassa', + 'cortaux', + 'cortevos', + 'cortilles', + 'cortola', + 'cortona', + 'cortos', + 'cortreau', + 'coru', + 'corvette', + "corvette's", + 'corvettes', + 'cory', + "cory's", + 'corys', + 'cosmic', + 'cosmicly', + 'cost', + "cost's", + 'costed', + 'costello', + "costello's", + 'costing', + 'costive', + 'costly', + 'costs', + 'costume', + "costume's", + 'costumer', + 'costumers', + 'costumes', + 'cot', + 'cote', + 'cotes', + 'cotillion', + "cotillion's", + 'cotillions', + 'cots', + "cottage's", + 'cottager', + 'cottagers', + 'cottages', + 'cotton', + "cotton's", + 'cottons', + 'cottontail', + 'couch', + 'couches', + "couches'", + 'cough', + 'coughed', + 'cougher', + 'coughes', + "coughes'", + 'coughing', + 'coughs', + 'could', + "could've", + 'coulda', + 'couldest', + "couldn't", + 'couldnt', + 'coulter', + 'council', + "council's", + 'councils', + 'counsel', + 'counseling', + 'counselor', + "counselor's", + 'counselors', + 'count', + "count's", + 'countdown', + "countdown's", + 'countdowns', + 'counted', + 'counter', + 'counteract', + 'counteracts', + 'counterattack', + 'countered', + 'countermeasures', + 'counterpart', + 'counterparts', + 'counters', + 'counterstrike', + 'countess', + 'counting', + 'countless', + 'countries', + "countries'", + 'country', + 'countryside', + 'counts', + 'county', + "county's", + 'coup', + 'coupe', + 'couple', + "couple's", + 'coupled', + 'coupler', + 'couplers', + 'couples', + 'coupling', + 'couplings', + 'coupon', + 'courage', + 'courageous', + 'courant', + 'courier', + "courier's", + 'couriers', + 'course', + "course's", + 'coursed', + 'courser', + 'courses', + 'coursework', + 'coursing', + 'court', + "court's", + 'courted', + 'courteously', + 'courter', + 'courters', + 'courtesies', + 'courtesy', + 'courthouse', + 'courting', + 'courtly', + 'courts', + 'courtside', + 'courtyard', + "courtyard's", + 'courtyards', + 'couscous', + 'cousin', + "cousin's", + 'cousins', + 'couture', + 'cove', + "cove's", + 'coven', + 'covenant', + 'cover', + "cover's", + 'coverage', + "coverage's", + 'coverages', + 'covered', + 'covering', + 'coverings', + 'covers', + 'covert', + 'coves', + 'covet', + 'covets', + 'cow', + "cow's", + 'coward', + "coward's", + 'cowardice', + 'cowardly', + 'cowards', + 'cowbos', + 'cowboy', + "cowboy's", + 'cowboys', + 'cowed', + 'cower', + 'cowering', + 'cowers', + 'cowfish', + 'cowgirl', + 'cowing', + 'coworker', + 'coworkers', + 'cows', + 'coy', + 'coyote', + 'coyotes', + 'coz', + 'cozana', + 'cozigos', + 'cozila', + 'cozilles', + 'cozreau', + 'cozros', + 'cozuan', + 'cozy', + 'cp', + 'cpip', + 'cps', + 'crab', + "crab's", + 'crabapple', + 'crabean', + 'crabmeat', + 'crabster', + "crack's", + 'cracked', + 'cracked-uptick', + 'crackers', + "crackin'", + 'cracking', + 'crackle', + "crackle's", + 'crackles', + 'crackly', + 'crackpot', + 'cracks', + 'cradle', + "cradle's", + 'cradles', + 'craft', + 'crafted', + 'crafter', + 'craftiness', + 'crafting', + 'crafts', + 'craftsmanship', + 'crafty', + 'crag', + 'craggy', + 'crags', + 'craig', + 'craigy', + 'cram', + 'crammed', + 'cramming', + 'cramp', + 'cramped', + 'cramping', + 'cramps', + 'crams', + 'cranberries', + 'cranberry', + 'crane', + "crane's", + 'cranes', + 'craning', + 'cranium', + "cranium's", + 'craniums', + 'cranky', + 'cranny', + 'crash', + 'crashed', + 'crasher', + "crasher's", + 'crashers', + 'crashes', + 'crashing', + 'crass', + 'crate', + "crate's", + 'crated', + 'crater', + "crater's", + 'craters', + 'crates', + 'crating', + 'crave', + 'craved', + 'craven', + "craven's", + 'cravens', + 'craver', + 'cravers', + 'craves', + 'craving', + 'cravings', + 'craw', + 'crawfish', + 'crawford', + "crawford's", + 'crawfords', + 'crawl', + 'crawled', + 'crawlers', + 'crawlies', + "crawlin'", + 'crawling', + 'crawls', + 'crawly', + 'craws', + 'craze', + 'crazed', + 'crazier', + 'crazies', + 'craziest', + 'craziness', + 'crazy', + 'crazycorner', + "crazycorner's", + 'crazycorners', + 'crazyquilt', + "crazyquilt's", + 'crazyquilts', + 'crazzlepops', + 'creak', + 'creaked', + 'creaking', + 'creaks', + 'creaky', + 'cream', + 'creamed', + 'creamer', + 'creamery', + 'creaming', + 'creams', + 'creamy', + 'crease', + 'creasy', + 'create', + 'created', + 'creates', + 'creating', + 'creation', + "creation's", + 'creations', + 'creative', + 'creatively', + 'creativity', + 'creator', + "creator's", + 'creators', + 'creature', + "creature's", + 'creaturely', + 'creatures', + 'cred', + 'credit', + "credit's", + 'credited', + 'crediting', + 'creditor', + 'credits', + 'credo', + 'creed', + 'creek', + "creek's", + 'creeked', + 'creeking', + 'creeks', + 'creep', + "creep's", + 'creeper', + "creeper's", + 'creepers', + 'creeping', + 'creeps', + 'creepy', + 'cremate', + 'cremated', + 'crept', + 'crepuscular', + 'crescent', + 'crest', + "crest's", + 'crested', + 'crests', + 'cretin', + 'cretins', + 'crew', + "crew's", + 'crewe', + 'crewed', + 'crewing', + 'crewman', + 'crewmate', + 'crewmates', + 'crewmember', + "crewmember's", + 'crewmembers', + 'crewmen', + 'crewperson', + 'crews', + "crews'", + 'crewship', + 'cri', + 'crib', + "crib's", + 'cribs', + 'crick', + 'cricket', + "cricket's", + 'cricket-whistling', + 'crickets', + 'cried', + 'crier', + 'criers', + 'cries', + 'crime', + 'crime-fighting', + 'crimes', + 'criminal', + 'crimonson', + 'crimson', + "crimson's", + 'crimsonm', + 'crimsons', + 'cringe', + 'cringes', + 'cringing', + 'crinklebee', + 'crinkleberry', + 'crinkleblabber', + 'crinklebocker', + 'crinkleboing', + 'crinkleboom', + 'crinklebounce', + 'crinklebouncer', + 'crinklebrains', + 'crinklebubble', + 'crinklebumble', + 'crinklebump', + 'crinklebumper', + 'crinklechomp', + 'crinklecorn', + 'crinklecrash', + 'crinklecrumbs', + 'crinklecrump', + 'crinklecrunch', + 'crinkledoodle', + 'crinkledorf', + 'crinkleface', + 'crinklefidget', + 'crinklefink', + 'crinklefish', + 'crinkleflap', + 'crinkleflapper', + 'crinkleflinger', + 'crinkleflip', + 'crinkleflipper', + 'crinklefoot', + 'crinklefuddy', + 'crinklefussen', + 'crinklegadget', + 'crinklegargle', + 'crinklegloop', + 'crinkleglop', + 'crinklegoober', + 'crinklegoose', + 'crinklegrooven', + 'crinklehoffer', + 'crinklehopper', + 'crinklejinks', + 'crinkleklunk', + 'crinkleknees', + 'crinklemarble', + 'crinklemash', + 'crinklemonkey', + 'crinklemooch', + 'crinklemouth', + 'crinklemuddle', + 'crinklemuffin', + 'crinklemush', + 'crinklenerd', + 'crinklenoodle', + 'crinklenose', + 'crinklenugget', + 'crinklephew', + 'crinklephooey', + 'crinklepocket', + 'crinklepoof', + 'crinklepop', + 'crinklepounce', + 'crinklepow', + 'crinklepretzel', + 'crinklequack', + 'crinkleroni', + 'crinklescooter', + 'crinklescreech', + 'crinklesmirk', + 'crinklesnooker', + 'crinklesnoop', + 'crinklesnout', + 'crinklesocks', + 'crinklespeed', + 'crinklespinner', + 'crinklesplat', + 'crinklesprinkles', + 'crinklesticks', + 'crinklestink', + 'crinkleswirl', + 'crinkleteeth', + 'crinklethud', + 'crinkletoes', + 'crinkleton', + 'crinkletoon', + 'crinkletooth', + 'crinkletwist', + 'crinklewhatsit', + 'crinklewhip', + 'crinklewig', + 'crinklewoof', + 'crinklezaner', + 'crinklezap', + 'crinklezapper', + 'crinklezilla', + 'crinklezoom', + 'cripes', + 'cripples', + 'crippling', + 'crises', + 'crisis', + 'crisp', + 'crisps', + 'crispy', + 'cristo', + "cristo's", + 'criteria', + 'criterias', + 'critic', + "critic's", + 'critical', + 'critically', + 'criticism', + "criticism's", + 'criticisms', + 'criticize', + 'criticized', + 'criticizing', + 'critics', + 'critique', + 'critiqued', + 'critiques', + 'critiquing', + 'critter', + "critter's", + 'critters', + 'croak', + 'croaked', + 'croaking', + 'croatia', + 'croatian', + 'croc', + "croc's", + 'crochet', + 'crock', + 'crocked', + 'crocket', + 'crockett', + "crockett's", + 'crocketts', + 'crockpot', + "crockpot's", + 'crockpots', + 'crocks', + 'crocodile', + "crocodile's", + 'crocodiles', + 'crocodilian', + 'crocs', + 'crocus', + 'crom', + 'cronies', + 'crook', + 'crooked', + 'cropped', + 'cropping', + 'crops', + 'croquet', + 'cross', + 'cross-eyed', + 'crossbar', + 'crossbones', + 'crossbow', + 'crossed', + 'crosser', + 'crossers', + 'crosses', + 'crossfire', + 'crosshair', + 'crosshairs', + 'crossing', + 'crossings', + 'crossly', + 'crossover', + 'crossroads', + 'crosstrees', + 'crosswalk', + 'crossway', + 'crossword', + 'crosswords', + 'crouch', + 'crouched', + 'croup', + 'croupier', + 'croutons', + 'crow', + "crow's", + 'crowbar', + 'crowd', + 'crowded', + 'crowder', + 'crowding', + 'crowds', + 'crowed', + 'crowing', + 'crown', + "crown's", + 'crown-repair', + 'crowned', + 'crowner', + 'crowning', + 'crowns', + 'crows', + 'cruces', + 'crucial', + 'crud', + 'cruddier', + 'cruddy', + 'crude', + 'cruder', + 'cruds', + 'cruel', + 'crueler', + 'cruelest', + 'cruella', + "cruella's", + 'cruelly', + 'cruelty', + 'cruise', + "cruise's", + 'cruised', + 'cruiser', + 'cruisers', + 'cruises', + "cruisin'", + 'cruising', + 'crumb', + 'crumble', + 'crumblebee', + 'crumbleberry', + 'crumbleblabber', + 'crumblebocker', + 'crumbleboing', + 'crumbleboom', + 'crumblebounce', + 'crumblebouncer', + 'crumblebrains', + 'crumblebubble', + 'crumblebumble', + 'crumblebump', + 'crumblebumper', + 'crumbleburger', + 'crumblechomp', + 'crumblecorn', + 'crumblecrash', + 'crumblecrumbs', + 'crumblecrump', + 'crumblecrunch', + 'crumbled', + 'crumbledoodle', + 'crumbledorf', + 'crumbleface', + 'crumblefidget', + 'crumblefink', + 'crumblefish', + 'crumbleflap', + 'crumbleflapper', + 'crumbleflinger', + 'crumbleflip', + 'crumbleflipper', + 'crumblefoot', + 'crumblefuddy', + 'crumblefussen', + 'crumblegadget', + 'crumblegargle', + 'crumblegloop', + 'crumbleglop', + 'crumblegoober', + 'crumblegoose', + 'crumblegrooven', + 'crumblehoffer', + 'crumblehopper', + 'crumblejinks', + 'crumbleklunk', + 'crumbleknees', + 'crumblemarble', + 'crumblemash', + 'crumblemonkey', + 'crumblemooch', + 'crumblemouth', + 'crumblemuddle', + 'crumblemuffin', + 'crumblemush', + 'crumblenerd', + 'crumblenoodle', + 'crumblenose', + 'crumblenugget', + 'crumblephew', + 'crumblephooey', + 'crumblepocket', + 'crumblepoof', + 'crumblepop', + 'crumblepounce', + 'crumblepow', + 'crumblepretzel', + 'crumblequack', + 'crumbleroni', + 'crumbles', + 'crumblescooter', + 'crumblescreech', + 'crumblesmirk', + 'crumblesnooker', + 'crumblesnoop', + 'crumblesnout', + 'crumblesocks', + 'crumblespeed', + 'crumblespinner', + 'crumblesplat', + 'crumblesprinkles', + 'crumblesticks', + 'crumblestink', + 'crumbleswirl', + 'crumbleteeth', + 'crumblethud', + 'crumbletoes', + 'crumbleton', + 'crumbletoon', + 'crumbletooth', + 'crumbletwist', + 'crumblewhatsit', + 'crumblewhip', + 'crumblewig', + 'crumblewoof', + 'crumblezaner', + 'crumblezap', + 'crumblezapper', + 'crumblezilla', + 'crumblezoom', + 'crumbly', + 'crumbs', + 'crumby', + 'crummy', + 'crunch', + "crunche's", + 'crunched', + 'crunchenbee', + 'crunchenberry', + 'crunchenblabber', + 'crunchenbocker', + 'crunchenboing', + 'crunchenboom', + 'crunchenbounce', + 'crunchenbouncer', + 'crunchenbrains', + 'crunchenbubble', + 'crunchenbumble', + 'crunchenbump', + 'crunchenbumper', + 'crunchenburger', + 'crunchenchomp', + 'crunchencorn', + 'crunchencrash', + 'crunchencrumbs', + 'crunchencrump', + 'crunchencrunch', + 'crunchendoodle', + 'crunchendorf', + 'crunchenface', + 'crunchenfidget', + 'crunchenfink', + 'crunchenfish', + 'crunchenflap', + 'crunchenflapper', + 'crunchenflinger', + 'crunchenflip', + 'crunchenflipper', + 'crunchenfoot', + 'crunchenfuddy', + 'crunchenfussen', + 'crunchengadget', + 'crunchengargle', + 'crunchengloop', + 'crunchenglop', + 'crunchengoober', + 'crunchengoose', + 'crunchengrooven', + 'crunchenhoffer', + 'crunchenhopper', + 'crunchenjinks', + 'crunchenklunk', + 'crunchenknees', + 'crunchenmarble', + 'crunchenmash', + 'crunchenmonkey', + 'crunchenmooch', + 'crunchenmouth', + 'crunchenmuddle', + 'crunchenmuffin', + 'crunchenmush', + 'crunchennerd', + 'crunchennoodle', + 'crunchennose', + 'crunchennugget', + 'crunchenphew', + 'crunchenphooey', + 'crunchenpocket', + 'crunchenpoof', + 'crunchenpop', + 'crunchenpounce', + 'crunchenpow', + 'crunchenpretzel', + 'crunchenquack', + 'crunchenroni', + 'crunchenscooter', + 'crunchenscreech', + 'crunchensmirk', + 'crunchensnooker', + 'crunchensnoop', + 'crunchensnout', + 'crunchensocks', + 'crunchenspeed', + 'crunchenspinner', + 'crunchensplat', + 'crunchensprinkles', + 'crunchensticks', + 'crunchenstink', + 'crunchenswirl', + 'crunchenteeth', + 'crunchenthud', + 'crunchentoes', + 'crunchenton', + 'crunchentoon', + 'crunchentooth', + 'crunchentwist', + 'crunchenwhatsit', + 'crunchenwhip', + 'crunchenwig', + 'crunchenwoof', + 'crunchenzaner', + 'crunchenzap', + 'crunchenzapper', + 'crunchenzilla', + 'crunchenzoom', + 'cruncher', + "cruncher's", + 'crunchers', + 'crunches', + 'crunchmouth', + 'crunchy', + 'crunchybee', + 'crunchyberry', + 'crunchyblabber', + 'crunchybocker', + 'crunchyboing', + 'crunchyboom', + 'crunchybounce', + 'crunchybouncer', + 'crunchybrains', + 'crunchybubble', + 'crunchybumble', + 'crunchybump', + 'crunchybumper', + 'crunchyburger', + 'crunchychomp', + 'crunchycorn', + 'crunchycrash', + 'crunchycrumbs', + 'crunchycrump', + 'crunchycrunch', + 'crunchydoodle', + 'crunchydorf', + 'crunchyface', + 'crunchyfidget', + 'crunchyfink', + 'crunchyfish', + 'crunchyflap', + 'crunchyflapper', + 'crunchyflinger', + 'crunchyflip', + 'crunchyflipper', + 'crunchyfoot', + 'crunchyfuddy', + 'crunchyfussen', + 'crunchygadget', + 'crunchygargle', + 'crunchygloop', + 'crunchyglop', + 'crunchygoober', + 'crunchygoose', + 'crunchygrooven', + 'crunchyhoffer', + 'crunchyhopper', + 'crunchyjinks', + 'crunchyklunk', + 'crunchyknees', + 'crunchymarble', + 'crunchymash', + 'crunchymonkey', + 'crunchymooch', + 'crunchymouth', + 'crunchymuddle', + 'crunchymuffin', + 'crunchymush', + 'crunchynerd', + 'crunchynoodle', + 'crunchynose', + 'crunchynugget', + 'crunchyphew', + 'crunchyphooey', + 'crunchypocket', + 'crunchypoof', + 'crunchypop', + 'crunchypounce', + 'crunchypow', + 'crunchypretzel', + 'crunchyquack', + 'crunchyroni', + 'crunchyscooter', + 'crunchyscreech', + 'crunchysmirk', + 'crunchysnooker', + 'crunchysnoop', + 'crunchysnout', + 'crunchysocks', + 'crunchyspeed', + 'crunchyspinner', + 'crunchysplat', + 'crunchysprinkles', + 'crunchysticks', + 'crunchystink', + 'crunchyswirl', + 'crunchyteeth', + 'crunchythud', + 'crunchytoes', + 'crunchyton', + 'crunchytoon', + 'crunchytooth', + 'crunchytwist', + 'crunchywhatsit', + 'crunchywhip', + 'crunchywig', + 'crunchywoof', + 'crunchyzaner', + 'crunchyzap', + 'crunchyzapper', + 'crunchyzilla', + 'crunchyzoom', + 'crusade', + 'crusader', + 'crusaders', + 'cruse', + 'cruses', + 'crush', + "crush's", + 'crushed', + 'crusher', + "crusher's", + 'crushers', + 'crushes', + 'crushing', + 'crust', + 'crustaceans', + 'crustatious', + 'crusted', + 'crustless', + 'crusty', + 'crutch', + 'crutches', + 'crux', + 'cruz', + 'cruzada', + 'cruzaire', + 'cruzigos', + 'cruzilles', + 'cruzman', + 'cruzola', + 'cruzos', + 'cruzuan', + 'cruzumal', + 'cry', + 'crying', + 'crypt', + "crypt's", + 'cryptic', + 'crypts', + 'crystal', + "crystal's", + 'crystallise', + 'crystallised', + 'crystallises', + 'crystallising', + 'crystallize', + 'crystallized', + 'crystallizes', + 'crystallizing', + 'crystals', + 'cs', + 'cscript', + 'css', + 'ctf', + 'ctrl', + 'cub', + 'cuba', + 'cuban', + 'cubby', + "cubby's", + 'cubbyhole', + "cubbyhole's", + 'cubbyholes', + 'cubbys', + 'cube', + "cube's", + 'cubes', + 'cubic', + 'cubicle', + "cubicle's", + 'cubicles', + "cubkyle's", + 'cuckoo', + "cuckoo's", + 'cuckoos', + 'cud', + 'cuddle', + 'cuddled', + 'cuddles', + 'cuddling', + 'cuddly', + 'cuds', + 'cue', + 'cues', + 'cuff', + 'cuffed', + 'cufflinks', + 'cuffs', + 'culinary', + 'cull', + 'culling', + 'cully', + 'culpa', + 'culprit', + 'cult', + 'cultivate', + 'cultural', + 'culturally', + 'culture', + "culture's", + 'cultured', + 'cultures', + 'culturing', + 'cumbersome', + 'cumulative', + 'cunning', + 'cup', + "cup's", + 'cupboard', + "cupboard's", + 'cupboards', + 'cupcake', + "cupcake's", + 'cupcakes', + 'cups', + 'cur', + 'curb', + "curb's", + 'curbs', + 'curd', + 'curdle', + 'cure', + "cure's", + 'cured', + 'curer', + 'cures', + 'curing', + 'curio', + "curio's", + 'curios', + 'curiosity', + 'curious', + 'curiouser', + 'curiousest', + 'curiously', + 'curl', + "curl's", + 'curled', + 'curling', + 'curlly', + 'curls', + 'curly', + 'curly-maned', + 'currant', + 'currants', + 'currency', + 'current', + "current's", + 'currently', + 'currents', + 'curriculum', + 'curry', + "curry's", + 'curs', + 'curse', + "curse's", + 'cursed', + 'curser', + 'cursers', + 'curses', + 'cursing', + 'cursive', + 'cursor', + 'curst', + 'curt', + 'curtain', + "curtain's", + 'curtained', + 'curtaining', + 'curtains', + 'curtis', + 'curtsey', + 'curtseys', + 'curtsies', + 'curtsy', + 'curve', + "curve's", + 'curved', + 'curves', + 'curvey', + 'curving', + 'curvy', + 'cushion', + "cushion's", + 'cushioned', + 'cushioning', + 'cushions', + 'cushy', + 'cuss', + 'cussed', + 'cusses', + 'cussing', + 'custard', + 'custody', + 'custom', + 'customary', + 'customer', + "customer's", + 'customers', + 'customizable', + 'customization', + 'customize', + 'customized', + 'customizer', + 'customizes', + 'customizing', + 'customs', + "cut's", + 'cut-throat', + 'cutbacks', + 'cute', + 'cuteness', + 'cuter', + 'cutest', + 'cutesy', + 'cutie', + "cutie's", + 'cuties', + 'cutla', + 'cutlass', + "cutlass'", + "cutlass's", + 'cutlasses', + 'cutler', + "cutler's", + 'cutoff', + 'cutout', + 'cuts', + 'cutscene', + 'cutter', + 'cutters', + 'cutthroart', + 'cutthroat', + "cutthroat's", + 'cutthroats', + 'cutts', + 'cuz', + 'cx', + 'cya', + 'cyan', + 'cyberspace', + 'cycle', + 'cycled', + 'cycles', + 'cycling', + 'cyclone', + 'cyclones', + 'cynthia', + "cynthia's", + 'cypress', + 'cyrus', + "cyrus'", + 'd&b', + "d'dogs", + "d'etable", + "d'juanio", + 'd*concert', + 'd-concert', + 'd-name', + 'd-points', + 'd:', + 'da', + "da's", + 'dab', + 'dabbled', + 'daccor', + 'dace', + 'dachshund', + 'dachshunds', + 'dacja', + 'dad', + "dad's", + 'dada', + 'daddies', + 'daddy', + "daddy's", + 'daddy-long-legs', + 'daffodil', + 'daffodilly', + 'daffodils', + 'daffy', + 'daft', + 'dahlia', + 'daichi', + 'daigunder', + 'daigyo', + 'daihatsu', + 'dailies', + 'daily', + 'dainty', + 'dairy', + 'dais', + 'daisies', + 'daisy', + "daisy's", + 'dajin', + 'dale', + "dale's", + 'dales', + 'dalma', + "dalma's", + 'dalmatian', + "dalmatian's", + 'dalmatians', + 'damage', + "damage's", + 'damaged', + 'damager', + 'damagers', + 'damages', + 'damaging', + "dame's", + 'dames', + 'damp', + 'damper', + 'damps', + 'damsel', + "damsel's", + 'damsels', + 'dan', + "dan's", + 'dana', + 'danaphant', + 'danapix', + 'danawa', + 'dance', + "dance's", + 'dance-along', + 'danced', + 'dancer', + "dancer's", + 'dancers', + "dancers'", + 'dances', + "dancin'", + 'dancing', + 'dandelion', + 'dandelion-fluff', + 'dandelions', + 'dander', + 'dandruff', + 'dandy', + 'dandybee', + 'dandyberry', + 'dandyblabber', + 'dandybocker', + 'dandyboing', + 'dandyboom', + 'dandybounce', + 'dandybouncer', + 'dandybrains', + 'dandybubble', + 'dandybumble', + 'dandybump', + 'dandybumper', + 'dandyburger', + 'dandychomp', + 'dandycorn', + 'dandycrash', + 'dandycrumbs', + 'dandycrump', + 'dandycrunch', + 'dandydoodle', + 'dandydorf', + 'dandyface', + 'dandyfidget', + 'dandyfink', + 'dandyfish', + 'dandyflap', + 'dandyflapper', + 'dandyflinger', + 'dandyflip', + 'dandyflipper', + 'dandyfoot', + 'dandyfuddy', + 'dandyfussen', + 'dandygadget', + 'dandygargle', + 'dandygloop', + 'dandyglop', + 'dandygoober', + 'dandygoose', + 'dandygrooven', + 'dandyhoffer', + 'dandyhopper', + 'dandyjinks', + 'dandyklunk', + 'dandyknees', + 'dandymarble', + 'dandymash', + 'dandymonkey', + 'dandymooch', + 'dandymouth', + 'dandymuddle', + 'dandymuffin', + 'dandymush', + 'dandynerd', + 'dandynoodle', + 'dandynose', + 'dandynugget', + 'dandyphew', + 'dandyphooey', + 'dandypocket', + 'dandypoof', + 'dandypop', + 'dandypounce', + 'dandypow', + 'dandypretzel', + 'dandyquack', + 'dandyroni', + 'dandyscooter', + 'dandyscreech', + 'dandysmirk', + 'dandysnooker', + 'dandysnoop', + 'dandysnout', + 'dandysocks', + 'dandyspeed', + 'dandyspinner', + 'dandysplat', + 'dandysprinkles', + 'dandysticks', + 'dandystink', + 'dandyswirl', + 'dandyteeth', + 'dandythud', + 'dandytoes', + 'dandyton', + 'dandytoon', + 'dandytooth', + 'dandytwist', + 'dandywhatsit', + 'dandywhip', + 'dandywig', + 'dandywoof', + 'dandyzaner', + 'dandyzap', + 'dandyzapper', + 'dandyzilla', + 'dandyzoom', + 'danforth', + "danforth's", + 'danforths', + 'dang', + 'danged', + 'danger', + "danger's", + 'dangerous', + 'dangerously', + 'dangers', + 'dangle', + 'daniel', + "daniel's", + 'danield', + 'daniels', + 'danke', + 'danny', + 'dans', + 'dante', + 'dap', + 'daphne', + 'dapple', + 'darby', + 'dare', + "dare's", + 'dared', + 'daredevil', + 'daredevils', + 'darer', + "darer's", + 'darers', + 'dares', + 'daring', + 'daring-do', + 'daringly', + 'dark', + "dark's", + 'dark-blade', + 'dark-sail', + 'dark-water', + 'dark-wind', + 'darkage', + 'darkblade', + 'darkblood', + 'darken', + 'darkened', + 'darkening', + 'darkens', + 'darker', + 'darkest', + 'darkiron', + 'darkly', + 'darkmasters', + 'darkmos', + 'darkness', + 'darkraptors', + 'darks', + 'darkshadow', + 'darkskulls', + 'darkstalkers', + 'darkstealers', + 'darkwaters', + 'darkwind', + 'darkwing', + 'darling', + 'darn', + 'darned', + 'darns', + 'darrell', + "darrell's", + 'darrells', + 'darryl', + 'dart', + "dart's", + 'darted', + 'darts', + 'darucho', + 'darutake', + 'darutori', + 'darwin', + "darwin's", + 'das', + 'dash', + 'dashboard', + "dashboard's", + 'dashboards', + "dashe's", + 'dasher', + 'dashes', + 'dashing', + 'dastardly', + 'data', + 'database', + 'date', + 'dated', + 'dateline', + 'dater', + 'dates', + 'dating', + 'datsun', + 'daughter', + "daughter's", + 'daughters', + 'daunting', + 'dauntless', + 'dave', + "dave's", + 'davenport', + "davenport's", + 'davenports', + 'daves', + 'davey', + "davey's", + 'david', + 'davis', + 'davy', + "davy's", + 'dawdling', + 'dawg', + 'dawgs', + 'dawn', + "dawn's", + 'dawned', + 'dawning', + 'dawns', + 'daxisd', + 'day', + "day's", + 'daybreak', + 'daycare', + 'daydream', + 'daydreamer', + "daydreamer's", + 'daydreamers', + 'daydreaming', + 'daydreams', + 'daylight', + 'daylights', + 'days', + 'daytime', + 'daze', + 'dazed', + 'dazy', + 'dazzle', + 'dazzling', + 'db', + 'dbl', + 'dc', + 'dca', + 'dced', + 'dcom', + 'dd', + 'ddl', + 'ddock', + 'ddos', + 'ddosed', + 'ddosing', + 'ddream', + 'ddreamland', + 'deacon', + 'deactivate', + 'deactivated', + 'dead', + 'deadeye', + 'deadeyes', + 'deadhead', + 'deadheading', + 'deadline', + 'deadlines', + 'deadliness', + 'deadlok', + 'deads', + 'deadwood', + 'deaf', + 'deafening', + 'deal', + "deal's", + 'dealer', + "dealer's", + 'dealers', + 'dealership', + 'dealing', + "dealing's", + 'dealings', + 'deals', + 'dealt', + 'dealthy', + 'dean', + "dean's", + 'deans', + 'dear', + "dear's", + 'dearer', + 'dearest', + 'dearheart', + 'dearly', + 'dears', + 'dearth', + 'debark', + 'debatable', + 'debate', + "debate's", + 'debated', + 'debater', + 'debaters', + 'debates', + 'debating', + 'debbie', + "debbie's", + 'debbies', + 'debilitating', + 'debit', + 'debonair', + 'debris', + 'debs', + 'debt', + "debt's", + 'debts', + 'debug', + 'debugged', + 'debugging', + 'debut', + "debut's", + 'debuted', + 'debuts', + 'decade', + 'decades', + 'decaf', + 'decal', + 'decals', + 'decamps', + 'decapitate', + 'decathlon', + "decathlon's", + 'decathlons', + 'decay', + 'deceased', + 'deceiving', + 'december', + "december's", + 'decembers', + 'decemeber', + 'decency', + 'decent', + 'decently', + 'deception', + 'deceptive', + 'decide', + 'decided', + 'decidedly', + 'decider', + 'decides', + 'deciding', + 'decimate', + 'decimated', + 'decimating', + 'decipher', + 'deciphering', + 'decision', + "decision's", + 'decisions', + 'decked', + 'decker', + 'deckhand', + 'deckhands', + 'decking', + 'deckings', + 'declaration', + "declaration's", + 'declarations', + 'declare', + 'declared', + 'declarer', + "declarer's", + 'declarers', + 'declares', + 'declaring', + 'decline', + 'declined', + 'declines', + 'declining', + 'deco', + 'decode', + 'decompress', + 'decompressing', + 'decor', + 'decorate', + 'decorated', + 'decorates', + 'decorating', + 'decoration', + "decoration's", + 'decorations', + 'decorator', + "decorator's", + 'decorators', + 'decoy', + 'decrease', + 'decreased', + 'decreases', + 'decreasing', + 'dedans', + 'dedicate', + 'dedicated', + 'dedicating', + 'dedication', + 'deduct', + 'deduction', + 'deductive', + 'deducts', + 'dee', + 'deed', + 'deeds', + 'deejay', + 'deem', + 'deemed', + 'deems', + 'deep', + 'deeper', + 'deepest', + 'deeply', + 'deeps', + 'deepwater', + 'deer', + "deer's", + 'deers', + 'deez', + 'def', + 'default', + 'defaulted', + 'defaulting', + 'defaults', + 'defeat', + 'defeated', + 'defeateds', + 'defeater', + "defeater's", + 'defeaters', + 'defeating', + 'defeats', + 'defect', + "defect's", + 'defected', + 'defecting', + 'defective', + 'defector', + 'defects', + 'defence', + 'defend', + 'defended', + 'defender', + "defender's", + 'defenders', + 'defending', + 'defends', + 'defense', + 'defenseless', + 'defenses', + 'defensive', + 'defensively', + 'defer', + 'deff', + 'defiant', + 'defies', + 'define', + 'defined', + 'definer', + "definer's", + 'definers', + 'defines', + 'defining', + 'definite', + 'definitely', + 'definition', + "definition's", + 'definitions', + 'definitive', + 'deflate', + 'defog', + 'defogging', + 'deform', + 'deformed', + 'defrag', + 'defragged', + 'defragging', + 'defrags', + 'defriend', + 'defrost', + 'deft', + 'defy', + 'defying', + 'deg', + 'degenerate', + 'degenerative', + 'deglitched', + 'degraded', + 'degree', + "degree's", + 'degreed', + 'degrees', + 'dehydrated', + 'dehydration', + 'deity', + 'deja', + 'dejectedly', + 'dejon', + 'deka', + 'dekadenz', + 'dekay', + 'del', + 'delas', + 'delay', + 'delayed', + 'delaying', + 'delays', + 'dele', + 'delegate', + 'delegated', + 'delegates', + 'delegating', + 'deles', + 'delete', + 'deleted', + 'deletes', + 'deleting', + 'deletion', + 'deli', + 'deliberated', + 'deliberately', + 'deliberating', + 'deliberation', + 'delicacy', + 'delicate', + 'delicately', + 'delicates', + 'delicioso', + 'delicious', + 'delight', + 'delighted', + 'delightful', + 'delinquent', + 'delirious', + 'deliriously', + 'delis', + 'deliver', + "deliver's", + 'delivered', + 'deliverer', + 'deliverers', + 'deliveries', + 'delivering', + 'delivers', + 'delivery', + 'dell', + "dell's", + 'della', + 'dells', + 'delta', + 'deluded', + 'delusional', + 'delusions', + 'deluxe', + 'delve', + 'delver', + 'delves', + 'demand', + 'demanded', + 'demander', + 'demanding', + 'demands', + 'demeanor', + 'demented', + 'dementor', + "dementor's", + 'dementors', + 'demise', + 'democratic', + 'demolition', + 'demolitions', + 'demon', + 'demons', + 'demonstrate', + 'demonstrated', + 'demonstrates', + 'demonstrating', + 'demonstration', + 'demonstrations', + 'demonstrative', + 'demoted', + 'demotion', + 'demure', + 'demures', + 'den', + "den's", + 'denary', + 'dendama', + 'denden', + 'denial', + 'denialmc', + 'denied', + 'denier', + 'deniers', + "deniers'", + 'denies', + 'denim', + "denim's", + 'denims', + 'dennis', + 'dennison', + 'denomination', + 'denominational', + 'denote', + 'denpachi', + 'dens', + "dens'", + 'dense', + 'dent', + 'dental', + 'dented', + 'dentin', + 'dentinal', + 'denting', + 'dentist', + "dentist's", + 'dentistry', + 'dentists', + 'dents', + 'dentures', + 'deny', + 'denying', + 'deodorant', + 'deorro', + 'depart', + 'departed', + 'departing', + 'department', + "department's", + 'departments', + 'departs', + 'departure', + 'departures', + 'depend', + 'dependable', + 'dependant', + 'depended', + 'dependent', + 'depending', + 'depends', + 'depleted', + 'deploy', + 'deployed', + 'deploying', + 'deport', + 'deporting', + 'deposed', + 'deposer', + 'deposit', + 'deposited', + 'depositing', + 'deposits', + 'depot', + "depot's", + 'depots', + 'depp', + "depp's", + 'depreciated', + 'depress', + 'depressed', + 'depressing', + 'depression', + 'deprivation', + 'deprive', + 'deprived', + 'depriving', + 'dept', + 'depth', + 'depths', + 'deputy', + 'derail', + 'derange', + 'deranged', + 'derby', + 'deregulate', + 'derek', + "derek's", + 'dereks', + 'derezzed', + 'derivation', + 'derivations', + 'derivative', + 'derivatives', + 'derive', + 'derived', + 'derives', + 'deriving', + 'dernier', + 'derogatory', + 'derp', + 'derped', + 'derping', + 'derrick', + 'derriere', + 'des', + 'desc', + 'descended', + 'descending', + 'descent', + "descent's", + 'descents', + 'descm', + 'descp', + 'descpl', + 'descpn', + 'describe', + 'described', + 'describer', + "describer's", + 'describers', + 'describes', + 'describing', + 'descript', + 'description', + "description's", + 'descriptions', + 'descriptive', + 'descs', + 'descsl', + 'descsn', + 'deseago', + 'deseano', + 'desecration', + 'desegua', + 'deselect', + 'desensitization', + 'deseona', + 'deseos', + 'desereau', + 'deseros', + 'desert', + 'deserted', + 'deserter', + "deserter's", + 'deserters', + 'deserting', + 'deserts', + "deserts'", + 'deserve', + 'deserved', + 'deserves', + 'deserving', + 'design', + "design's", + 'designate', + 'designated', + 'designation', + 'designed', + 'designer', + "designer's", + 'designers', + 'designing', + 'designs', + 'desirable', + 'desire', + 'desired', + 'desirer', + 'desires', + 'desiring', + 'desk', + "desk's", + 'desks', + 'desktop', + 'desktops', + 'desmond', + 'desolate', + 'desolation', + 'desolations', + 'despair', + 'desperate', + 'desperately', + 'despicable', + 'despise', + 'despite', + 'despited', + 'despoiler', + 'dessert', + "dessert's", + 'desserts', + 'destination', + 'destinations', + 'destined', + "destinie's", + 'destinies', + 'destiny', + "destiny's", + 'destinys', + 'destoryers', + 'destroy', + 'destroyable', + 'destroye', + 'destroyed', + 'destroyer', + "destroyer's", + 'destroyers', + 'destroying', + 'destroys', + 'destruct', + 'destruction', + 'destructive', + 'detachment', + 'detail', + 'detailed', + 'detailer', + "detailer's", + 'detailers', + 'detailing', + 'details', + 'detained', + 'detect', + 'detected', + 'detecting', + 'detection', + 'detective', + "detective's", + 'detectives', + 'detector', + "detector's", + 'detectors', + 'detects', + 'detention', + 'deter', + 'deteriorating', + 'determination', + "determination's", + 'determinations', + 'determine', + 'determined', + 'determiner', + "determiner's", + 'determiners', + 'determines', + 'determining', + 'detest', + 'detonate', + 'detonates', + 'detonation', + 'detour', + "detour's", + 'detouring', + 'detours', + 'deuce', + 'deuces', + 'deutsche', + 'dev', + 'devastated', + 'devastating', + 'devastatingly', + 'develop', + 'developed', + 'developer', + "developer's", + 'developers', + 'developing', + 'development', + "development's", + 'developments', + 'develops', + 'deviant', + 'device', + "device's", + 'devices', + 'devilish', + 'devilishly', + 'devious', + 'devise', + 'devised', + 'devises', + 'devoid', + 'devoir', + 'devote', + 'devoted', + 'devotion', + 'devour', + 'devoured', + 'devours', + 'devs', + 'dew', + 'dewdrop', + 'dewdrops', + 'dews', + 'dewy', + 'dexterity', + 'dg', + 'dgamer', + 'dgarden', + 'dhow', + 'diabetes', + 'diabetic', + 'diabolical', + 'diagnosed', + 'diagnosis', + 'diagonal', + 'diagonally', + 'diagonals', + 'dial', + 'dialect', + 'dialing', + 'dialog', + 'dialogue', + 'dialup', + 'dialysis', + 'diamante', + 'diamond', + "diamond's", + 'diamonds', + 'diana', + 'diane', + 'diaper', + "diaper's", + 'diapered', + 'diapers', + 'diaries', + 'diary', + 'dibs', + 'dice', + "dice'", + 'diced', + 'dicer', + 'dices', + 'dicey', + 'dicing', + 'dickens', + 'dictate', + 'dictates', + 'diction', + 'dictionaries', + 'dictionary', + "dictionary's", + 'did', + "didn't", + 'didnt', + 'didst', + 'didymus', + 'die', + 'died', + 'diego', + 'diehard', + 'dieing', + 'diem', + 'dies', + 'diesel', + 'diet', + 'dietary', + 'diets', + 'dif', + 'diff', + 'differ', + 'differed', + 'difference', + "difference's", + 'differenced', + 'differences', + 'differencing', + 'different', + 'differential', + 'differentiate', + 'differentiated', + 'differentiates', + 'differentiating', + 'differentiations', + 'differently', + 'differing', + 'differs', + 'difficult', + 'difficulties', + 'difficultly', + 'difficulty', + "difficulty's", + 'diffusers', + 'diffy', + 'dig', + 'digest', + 'digg', + 'diggable', + 'digger', + 'diggers', + 'digging', + "digging's", + 'diggings', + 'diggity', + 'digimon', + 'digit', + 'digital', + 'dignity', + 'digress', + 'digs', + 'dilemma', + "dill's", + 'dillinger', + "dillinger's", + 'dilly', + 'dilute', + 'diluted', + 'dim', + 'dime', + 'dimension', + 'dimensions', + 'diminished', + 'diminishing', + 'diminutive', + 'dimm', + 'dimmed', + 'dimond', + 'dimple', + 'dimples', + 'dims', + 'dimwit', + 'dimwits', + 'dimwitted', + 'din', + 'dinah', + 'dine', + 'dine-in', + 'dined', + 'diner', + "diner's", + 'diners', + 'dines', + 'dinette', + 'ding', + 'dinged', + 'dingeringeding', + 'dinghies', + 'dinghy', + "dinghy's", + 'dinghys', + 'dinging', + 'dinglebee', + 'dingleberry', + 'dingleblabber', + 'dinglebocker', + 'dingleboing', + 'dingleboom', + 'dinglebounce', + 'dinglebouncer', + 'dinglebrains', + 'dinglebubble', + 'dinglebumble', + 'dinglebump', + 'dinglebumper', + 'dingleburger', + 'dinglechomp', + 'dinglecorn', + 'dinglecrash', + 'dinglecrumbs', + 'dinglecrump', + 'dinglecrunch', + 'dingledoodle', + 'dingledorf', + 'dingleface', + 'dinglefidget', + 'dinglefink', + 'dinglefish', + 'dingleflap', + 'dingleflapper', + 'dingleflinger', + 'dingleflip', + 'dingleflipper', + 'dinglefoot', + 'dinglefuddy', + 'dinglefussen', + 'dinglegadget', + 'dinglegargle', + 'dinglegloop', + 'dingleglop', + 'dinglegoober', + 'dinglegoose', + 'dinglegrooven', + 'dinglehoffer', + 'dinglehopper', + 'dinglejinks', + 'dingleklunk', + 'dingleknees', + 'dinglemarble', + 'dinglemash', + 'dinglemonkey', + 'dinglemooch', + 'dinglemouth', + 'dinglemuddle', + 'dinglemuffin', + 'dinglemush', + 'dinglenerd', + 'dinglenoodle', + 'dinglenose', + 'dinglenugget', + 'dinglephew', + 'dinglephooey', + 'dinglepocket', + 'dinglepoof', + 'dinglepop', + 'dinglepounce', + 'dinglepow', + 'dinglepretzel', + 'dinglequack', + 'dingleroni', + 'dinglescooter', + 'dinglescreech', + 'dinglesmirk', + 'dinglesnooker', + 'dinglesnoop', + 'dinglesnout', + 'dinglesocks', + 'dinglespeed', + 'dinglespinner', + 'dinglesplat', + 'dinglesprinkles', + 'dinglesticks', + 'dinglestink', + 'dingleswirl', + 'dingleteeth', + 'dinglethud', + 'dingletoes', + 'dingleton', + 'dingletoon', + 'dingletooth', + 'dingletwist', + 'dinglewhatsit', + 'dinglewhip', + 'dinglewig', + 'dinglewoof', + 'dinglezaner', + 'dinglezap', + 'dinglezapper', + 'dinglezilla', + 'dinglezoom', + 'dingo', + 'dings', + 'dingy', + 'dining', + 'dink', + 'dinks', + 'dinky', + 'dinner', + "dinner's", + 'dinners', + 'dinnertime', + 'dino', + "dino's", + 'dinos', + 'dinosaur', + "dinosaur's", + 'dinosaurs', + 'dinothunder', + 'dins', + 'dint', + 'dip', + 'diplomat', + 'diplomatic', + 'diplomats', + 'dipped', + 'dipper', + 'dipping', + 'dippy', + 'dips', + 'dir', + 'dire', + 'direct', + 'directed', + 'directing', + 'direction', + "direction's", + 'directional', + 'directions', + 'directive', + 'directives', + 'directly', + 'director', + "director's", + 'directors', + 'directory', + 'directs', + 'direr', + 'direst', + 'dirge', + 'dirk', + 'dirks', + 'dirt', + 'dirty', + 'dis', + 'disability', + 'disable', + 'disabled', + 'disabler', + 'disables', + 'disabling', + 'disadvantage', + 'disadvantaged', + 'disadvantages', + 'disaffected', + 'disagree', + 'disagreed', + 'disagreement', + 'disagreements', + 'disagrees', + 'disappear', + 'disappearance', + 'disappeared', + 'disappearing', + 'disappears', + 'disappoint', + 'disappointed', + 'disappointing', + 'disappointment', + 'disappoints', + 'disapprove', + 'disapproved', + 'disapprover', + 'disapproves', + 'disapproving', + 'disarm', + 'disarray', + 'disaster', + 'disasters', + 'disastrous', + 'disavow', + 'disband', + 'disbanded', + 'disbanding', + 'disbands', + 'disbelief', + 'disc', + 'discarded', + 'discernible', + 'discharge', + 'discharged', + 'discharger', + 'disciples', + 'disciplinary', + 'discipline', + 'disciplined', + 'discipliner', + 'disciplines', + 'disciplining', + 'disclaimer', + 'disco', + 'discoed', + 'discoing', + 'disconcerting', + 'disconnect', + 'disconnected', + 'disconnecting', + 'disconnection', + 'disconnections', + 'disconnects', + 'discontinued', + 'discos', + 'discount', + "discount's", + 'discounted', + 'discounter', + "discounter's", + 'discounters', + 'discounting', + 'discounts', + 'discourage', + 'discouraged', + 'discourages', + 'discouraging', + 'discover', + 'discovered', + 'discoverer', + "discoverer's", + 'discoverers', + 'discoveries', + 'discovering', + 'discovers', + 'discovery', + "discovery's", + 'discrepancies', + 'discrepancy', + 'discrete', + 'discretion', + 'discriminate', + 'discrimination', + 'discs', + 'discus', + 'discuss', + 'discussed', + 'discusser', + "discusser's", + 'discusses', + 'discussing', + 'discussion', + "discussion's", + 'discussions', + 'disdain', + 'disease', + 'diseased', + 'diseases', + 'diseasing', + 'disembark', + 'disembarked', + 'disembarking', + 'disembodied', + 'disenfranchised', + 'disengage', + 'disengaged', + 'disengages', + 'disengaging', + 'disfunctional', + 'disgrace', + 'disgraced', + 'disgraces', + 'disguise', + "disguise's", + 'disguised', + 'disguises', + 'disgust', + "disgust's", + 'disgusted', + 'disgusting', + 'disgustingly', + 'disgusts', + 'dish', + 'disheartened', + 'dished', + 'dishes', + "dishes'", + 'dishing', + 'dishonest', + 'dishonorable', + 'dishwasher', + 'disillusioned', + 'disinclined', + 'disintegrated', + 'disinterest', + 'disinterested', + 'disjoin', + 'disjoined', + 'disk', + "disk's", + 'disks', + 'disky', + 'dislike', + 'disliked', + 'dislikes', + 'disliking', + 'disloyal', + 'dismal', + 'dismantle', + 'dismantled', + 'dismay', + 'dismiss', + 'dismissal', + 'dismissed', + 'dismisser', + 'dismissers', + 'dismisses', + 'dismissing', + 'dismissive', + 'disney', + "disney's", + 'disney.com', + 'disneyana', + "disneyana's", + 'disneyland', + "disneyland's", + 'disneymania', + 'disneyworld', + "disneyworld's", + 'disobedience', + 'disobey', + 'disobeyed', + 'disobeying', + 'disorder', + 'disorders', + 'disorganized', + 'disorienting', + 'disown', + 'disowned', + 'dispassionately', + 'dispatch', + 'dispatched', + 'dispatching', + 'dispense', + 'dispenser', + 'dispensers', + 'displaced', + 'displaces', + 'displas', + 'display', + 'displayed', + 'displayer', + 'displaying', + 'displays', + 'displeased', + 'displeases', + 'displeasure', + 'disposal', + 'dispose', + 'dispute', + 'disqualification', + 'disregard', + 'disregarding', + 'disrespect', + 'disrespectful', + 'disrespecting', + 'disrespects', + 'disrupt', + 'disrupted', + 'disrupting', + 'disruptive', + 'disrupts', + 'dissect', + 'dissension', + 'dissent', + 'dissenter', + 'dissention', + 'dissociate', + 'dissociated', + 'dissociates', + 'dissociating', + 'dissociation', + 'dissolved', + 'dist', + 'distance', + 'distanced', + 'distances', + 'distancing', + 'distant', + 'distantly', + 'distemper', + 'distill', + 'distinct', + 'distinction', + 'distinctions', + 'distinguish', + 'distinguished', + 'distorted', + 'distortion', + 'distortions', + 'distract', + 'distracted', + 'distracting', + 'distraction', + 'distractions', + 'distractive', + 'distracts', + 'distraught', + 'distress', + 'distressed', + 'distressing', + 'distribute', + 'distributed', + 'distributer', + "distributer's", + 'distributers', + 'distributes', + 'distributing', + 'distribution', + 'distributions', + 'distributive', + 'district', + "district's", + 'districts', + 'disturb', + 'disturbance', + 'disturbances', + 'disturbed', + 'disturber', + "disturber's", + 'disturbers', + 'disturbing', + 'disturbingly', + 'disturbs', + 'disyer', + 'ditched', + 'ditcher', + 'ditchers', + 'ditches', + 'ditching', + 'ditsy', + 'dittany', + 'ditties', + 'ditto', + 'ditty', + 'ditz', + 'ditzy', + 'diva', + "diva's", + 'divas', + 'dive', + 'dived', + 'diver', + "diver's", + 'divers', + 'diverse', + 'diversion', + 'divert', + 'diverted', + 'diverts', + 'dives', + 'divest', + 'divide', + 'divided', + 'divider', + "divider's", + 'dividers', + 'divides', + 'dividing', + 'divine', + 'divinely', + 'diving', + 'divinity', + 'division', + "division's", + 'divisions', + 'divorce', + 'divorced', + 'divorcing', + 'divulge', + 'divvied', + 'divvying', + 'diwali', + 'dizzenbee', + 'dizzenberry', + 'dizzenblabber', + 'dizzenbocker', + 'dizzenboing', + 'dizzenboom', + 'dizzenbounce', + 'dizzenbouncer', + 'dizzenbrains', + 'dizzenbubble', + 'dizzenbumble', + 'dizzenbump', + 'dizzenbumper', + 'dizzenburger', + 'dizzenchomp', + 'dizzencorn', + 'dizzencrash', + 'dizzencrumbs', + 'dizzencrump', + 'dizzencrunch', + 'dizzendoodle', + 'dizzendorf', + 'dizzenface', + 'dizzenfidget', + 'dizzenfink', + 'dizzenfish', + 'dizzenflap', + 'dizzenflapper', + 'dizzenflinger', + 'dizzenflip', + 'dizzenflipper', + 'dizzenfoot', + 'dizzenfuddy', + 'dizzenfussen', + 'dizzengadget', + 'dizzengargle', + 'dizzengloop', + 'dizzenglop', + 'dizzengoober', + 'dizzengoose', + 'dizzengrooven', + 'dizzenhoffer', + 'dizzenhopper', + 'dizzenjinks', + 'dizzenklunk', + 'dizzenknees', + 'dizzenmarble', + 'dizzenmash', + 'dizzenmonkey', + 'dizzenmooch', + 'dizzenmouth', + 'dizzenmuddle', + 'dizzenmuffin', + 'dizzenmush', + 'dizzennerd', + 'dizzennoodle', + 'dizzennose', + 'dizzennugget', + 'dizzenphew', + 'dizzenphooey', + 'dizzenpocket', + 'dizzenpoof', + 'dizzenpop', + 'dizzenpounce', + 'dizzenpow', + 'dizzenpretzel', + 'dizzenquack', + 'dizzenroni', + 'dizzenscooter', + 'dizzenscreech', + 'dizzensmirk', + 'dizzensnooker', + 'dizzensnoop', + 'dizzensnout', + 'dizzensocks', + 'dizzenspeed', + 'dizzenspinner', + 'dizzensplat', + 'dizzensprinkles', + 'dizzensticks', + 'dizzenstink', + 'dizzenswirl', + 'dizzenteeth', + 'dizzenthud', + 'dizzentoes', + 'dizzenton', + 'dizzentoon', + 'dizzentooth', + 'dizzentwist', + 'dizzenwhatsit', + 'dizzenwhip', + 'dizzenwig', + 'dizzenwoof', + 'dizzenzaner', + 'dizzenzap', + 'dizzenzapper', + 'dizzenzilla', + 'dizzenzoom', + 'dizzied', + 'dizzier', + 'dizziest', + 'dizziness', + 'dizzy', + 'dizzybee', + 'dizzyberry', + 'dizzyblabber', + 'dizzybocker', + 'dizzyboing', + 'dizzyboom', + 'dizzybounce', + 'dizzybouncer', + 'dizzybrains', + 'dizzybubble', + 'dizzybumble', + 'dizzybump', + 'dizzybumper', + 'dizzyburger', + 'dizzychomp', + 'dizzycorn', + 'dizzycrash', + 'dizzycrumbs', + 'dizzycrump', + 'dizzycrunch', + 'dizzydoodle', + 'dizzydorf', + 'dizzyface', + 'dizzyfidget', + 'dizzyfink', + 'dizzyfish', + 'dizzyflap', + 'dizzyflapper', + 'dizzyflinger', + 'dizzyflip', + 'dizzyflipper', + 'dizzyfoot', + 'dizzyfuddy', + 'dizzyfussen', + 'dizzygadget', + 'dizzygargle', + 'dizzygloop', + 'dizzyglop', + 'dizzygoober', + 'dizzygoose', + 'dizzygrooven', + 'dizzyhoffer', + 'dizzyhopper', + 'dizzying', + 'dizzyjinks', + 'dizzyklunk', + 'dizzyknees', + 'dizzyly', + 'dizzymarble', + 'dizzymash', + 'dizzymonkey', + 'dizzymooch', + 'dizzymouth', + 'dizzymuddle', + 'dizzymuffin', + 'dizzymush', + 'dizzynerd', + 'dizzynoodle', + 'dizzynose', + 'dizzynugget', + 'dizzyphew', + 'dizzyphooey', + 'dizzypocket', + 'dizzypoof', + 'dizzypop', + 'dizzypounce', + 'dizzypow', + 'dizzypretzel', + 'dizzyquack', + 'dizzyroni', + 'dizzyscooter', + 'dizzyscreech', + 'dizzysmirk', + 'dizzysnooker', + 'dizzysnoop', + 'dizzysnout', + 'dizzysocks', + 'dizzyspeed', + 'dizzyspinner', + 'dizzysplat', + 'dizzysprinkles', + 'dizzysticks', + 'dizzystink', + 'dizzyswirl', + 'dizzyteeth', + 'dizzythud', + 'dizzytoes', + 'dizzyton', + 'dizzytoon', + 'dizzytooth', + 'dizzytwist', + 'dizzywhatsit', + 'dizzywhip', + 'dizzywig', + 'dizzywoof', + 'dizzyzaner', + 'dizzyzap', + 'dizzyzapper', + 'dizzyzilla', + 'dizzyzoom', + 'dj', + 'dlite', + 'dlp', + 'dlr', + 'dluffy', + 'dmg', + 'dna', + 'dname', + 'do', + 'do-re-mi', + 'dobra', + 'doc', + "doc's", + 'docile', + 'dociousaliexpiisticfragilcalirupus', + 'dock', + "dock's", + 'docked', + 'docker', + "docker's", + 'dockers', + 'dockhand', + 'docking', + 'docks', + 'docksplinter', + 'dockworker', + 'dockworkers', + 'docs', + 'doctor', + "doctor's", + 'doctored', + 'doctoring', + 'doctors', + 'document', + "document's", + 'documentary', + 'documented', + 'documenter', + 'documenters', + 'documenting', + 'documents', + 'dodge', + 'dodgeball', + "dodgeball's", + 'dodgeballs', + 'dodged', + 'dodgem', + 'dodger', + 'dodgers', + 'dodges', + 'dodging', + 'dodgy', + 'dodo', + 'doe', + 'doer', + 'does', + 'doesdoesnt', + "doesn't", + 'doesnt', + 'doest', + 'dog', + "dog's", + 'doge', + 'dogfish', + 'dogged', + 'doggenbee', + 'doggenberry', + 'doggenblabber', + 'doggenbocker', + 'doggenboing', + 'doggenboom', + 'doggenbounce', + 'doggenbouncer', + 'doggenbrains', + 'doggenbubble', + 'doggenbumble', + 'doggenbump', + 'doggenbumper', + 'doggenburger', + 'doggenchomp', + 'doggencorn', + 'doggencrash', + 'doggencrumbs', + 'doggencrump', + 'doggencrunch', + 'doggendoodle', + 'doggendorf', + 'doggenface', + 'doggenfidget', + 'doggenfink', + 'doggenfish', + 'doggenflap', + 'doggenflapper', + 'doggenflinger', + 'doggenflip', + 'doggenflipper', + 'doggenfoot', + 'doggenfuddy', + 'doggenfussen', + 'doggengadget', + 'doggengargle', + 'doggengloop', + 'doggenglop', + 'doggengoober', + 'doggengoose', + 'doggengrooven', + 'doggenhoffer', + 'doggenhopper', + 'doggenjinks', + 'doggenklunk', + 'doggenknees', + 'doggenmarble', + 'doggenmash', + 'doggenmonkey', + 'doggenmooch', + 'doggenmouth', + 'doggenmuddle', + 'doggenmuffin', + 'doggenmush', + 'doggennerd', + 'doggennoodle', + 'doggennose', + 'doggennugget', + 'doggenphew', + 'doggenphooey', + 'doggenpocket', + 'doggenpoof', + 'doggenpop', + 'doggenpounce', + 'doggenpow', + 'doggenpretzel', + 'doggenquack', + 'doggenroni', + 'doggenscooter', + 'doggenscreech', + 'doggensmirk', + 'doggensnooker', + 'doggensnoop', + 'doggensnout', + 'doggensocks', + 'doggenspeed', + 'doggenspinner', + 'doggensplat', + 'doggensprinkles', + 'doggensticks', + 'doggenstink', + 'doggenswirl', + 'doggenteeth', + 'doggenthud', + 'doggentoes', + 'doggenton', + 'doggentoon', + 'doggentooth', + 'doggentwist', + 'doggenwhatsit', + 'doggenwhip', + 'doggenwig', + 'doggenwoof', + 'doggenzaner', + 'doggenzap', + 'doggenzapper', + 'doggenzilla', + 'doggenzoom', + 'doggerel', + 'doggie', + 'doggies', + 'doggone', + 'doggy', + "doggy's", + 'doghouse', + "doghouse's", + 'doghouses', + 'dogs', + 'dogwood', + 'doh', + 'doids', + 'doilies', + 'doin', + "doin'", + 'doing', + 'doings', + 'dojo', + "dojo's", + 'dojos', + 'dole', + 'doll', + "doll's", + 'dollar', + "dollar's", + 'dollars', + 'dolled', + 'dollhouse', + "dollhouse's", + 'dollhouses', + 'dollies', + 'dolls', + 'dolly', + 'dolman', + 'dolor', + 'dolores', + 'dolph', + 'dolphin', + "dolphin's", + 'dolphins', + 'doma-boma', + 'domain', + 'domed', + 'domestic', + 'domesticated', + 'dominant', + 'dominion', + 'domino', + "domino's", + 'dominoes', + 'dominos', + 'don', + "don't", + 'donald', + "donald's", + 'donalds', + 'donate', + 'donated', + 'donates', + 'donating', + 'donation', + 'donations', + 'done', + 'dongiga', + 'dongor', + 'dongora', + 'donkey', + 'donkeys', + 'donned', + 'donnon', + 'dont', + 'donut', + "donut's", + 'donuts', + 'doodad', + "doodad's", + 'doodads', + 'doodle', + "doodle's", + 'doodlebops', + 'doodlebug', + 'doodlebugs', + 'doodles', + "doodles'", + 'doohickey', + 'dooly', + 'doom', + 'doombringers', + 'doomed', + 'dooming', + 'doompirates', + 'doomraiders', + 'dooms', + 'doonan', + 'door', + "door's", + 'doorbell', + 'doorknob', + "doorknob's", + 'doorknobs', + 'doorman', + "doorman's", + 'doormans', + 'doors', + 'doorway', + "doorway's", + 'doorways', + 'dopey', + "dopey's", + "doppler's", + 'dorado', + 'doris', + "doris'", + 'dorm', + "dorm's", + 'dormant', + 'dormouse', + 'dorms', + 'dory', + "dory's", + 'dos', + 'dose', + 'dost', + 'dot', + 'doth', + 'doting', + 'dots', + 'dotted', + 'dotty', + 'double', + 'double-click', + 'double-decker', + 'doubled', + 'doubledown', + 'doubler', + 'doublers', + 'doubles', + 'doubling', + 'doubloon', + 'doubloons', + 'doubly', + 'doubt', + 'doubted', + 'doubter', + "doubter's", + 'doubters', + 'doubtful', + 'doubting', + 'doubts', + 'doug', + "doug's", + 'dougal', + 'dough', + 'doughnut', + 'doughnuts', + 'dougs', + 'douse', + 'douses', + 'dove', + "dove's", + 'dover', + 'doves', + 'dowdy', + 'dower', + 'down', + 'down-home', + 'downed', + 'downer', + "downer's", + 'downers', + 'downfall', + 'downfalls', + 'downgrade', + 'downgraded', + 'downgrades', + 'downhill', + 'downing', + 'download', + 'downloaded', + 'downloading', + 'downloads', + 'downrange', + 'downright', + 'downs', + 'downside', + 'downsize', + 'downsized', + 'downsizer', + 'downsizers', + 'downsizes', + 'downsizing', + 'downstairs', + 'downtime', + 'downtown', + 'downward', + 'downwards', + 'downy', + 'dowry', + 'doze', + 'dozed', + 'dozen', + 'dozens', + 'dozer', + 'dozes', + "dozin'", + 'dozing', + 'dr', + 'dr.', + 'drab', + 'drabs', + "draco's", + 'draconis', + 'dracos', + 'dracula', + "dracyla's", + 'draft', + 'drafted', + 'drafting', + 'drafts', + 'drag', + 'dragged', + 'dragger', + 'dragging', + 'dragion', + 'dragon', + "dragon's", + 'dragonblood', + 'dragonfly', + 'dragons', + 'dragonslayers', + 'dragoon', + 'drags', + 'dragstrip', + 'drain', + 'drainage', + 'drained', + 'drainer', + 'draining', + 'drains', + 'drak', + 'drake', + 'drakes', + 'drakken', + "drakken's", + 'dram', + 'drama', + 'dramamine', + 'dramas', + 'dramatic', + 'dramatically', + 'drams', + 'drank', + 'drapes', + 'draping', + 'drapmeister', + 'drastic', + 'drastically', + 'drat', + 'drats', + 'dratted', + 'draught', + 'draughts', + 'draw', + 'drawback', + 'drawbacks', + 'drawbridge', + 'drawbridges', + 'drawer', + 'drawers', + 'drawing', + 'drawings', + 'drawl', + 'drawly', + 'drawn', + 'drawnly', + 'draws', + 'dray', + 'drays', + 'dread', + "dread's", + 'dreaded', + 'dreadful', + 'dreadfully', + 'dreading', + 'dreadlock', + 'dreadlocks', + 'dreadnaught', + 'dreadnaughts', + 'dreadnought', + 'dreadnoughts', + 'dreads', + 'dream', + 'dreamboat', + 'dreamed', + 'dreamer', + "dreamer's", + 'dreamers', + 'dreaming', + 'dreamland', + 'dreamlike', + 'dreams', + 'dreamscape', + 'dreamscapes', + 'dreamt', + 'dreamy', + 'dreary', + 'dredd', + 'dreg', + 'dregs', + 'dreidel', + "dreidel's", + 'dreidels', + 'drench', + 'drenched', + 'drenches', + 'dress', + "dress'", + 'dress-making', + 'dressed', + 'dresser', + 'dressers', + 'dresses', + "dresses'", + 'dressing', + 'dressings', + 'drew', + 'drib', + 'dribble', + 'dribbles', + 'dribbling', + 'dried', + 'drier', + "drier's", + 'driers', + 'dries', + 'driest', + 'drift', + 'drifted', + 'drifter', + "drifter's", + 'drifters', + 'drifting', + 'drifts', + 'driftwood', + 'driftwoods', + 'drifty', + 'drill', + 'drilled', + 'drilling', + 'drills', + 'drink', + "drink's", + 'drinkable', + 'drinker', + "drinker's", + 'drinkers', + 'drinking', + 'drinks', + 'drip', + 'dripping', + 'drips', + 'drivable', + 'drive', + 'drive-thru', + 'driven', + 'driver', + "driver's", + 'drivers', + 'drives', + 'driveway', + "drivin'", + 'driving', + 'drizzle', + 'drizzles', + 'droid', + 'drone', + 'droned', + 'droning', + 'drool', + 'drooled', + 'drooling', + 'drools', + 'droop', + 'drooped', + 'drooping', + 'droops', + 'droopy', + 'drop', + "drop's", + 'dropdown', + 'dropless', + 'dropout', + 'droppable', + 'dropped', + 'dropper', + 'droppers', + 'dropping', + 'droppings', + 'drops', + 'drought', + 'droughts', + 'drove', + 'droves', + 'drown', + 'drowned', + 'drowning', + 'drowns', + 'drowsy', + 'druid', + 'drum', + "drum's", + 'drummer', + "drummer's", + 'drummers', + 'drumming', + 'drums', + 'dry', + 'dryad', + "dryad's", + 'dryads', + 'drydock', + 'dryer', + 'drying', + 'dryly', + 'drywall', + 'ds', + 'du', + 'dual', + 'dually', + 'duals', + 'dub', + 'dubious', + 'dubloon', + 'dubs', + 'ducat', + 'ducati', + 'duce', + 'duchamps', + 'duchess', + 'duck', + "duck's", + 'ducked', + 'duckies', + 'ducking', + 'ducks', + 'ducktales', + 'ducky', + 'duct', + 'ducts', + 'dud', + 'dude', + "dude's", + 'dudes', + 'dudley', + "dudley's", + 'duds', + 'due', + 'duel', + 'dueled', + 'dueling', + 'duels', + 'dues', + 'duet', + 'dug', + 'dugout', + 'duh', + 'duke', + "duke's", + 'dukes', + 'dulcie', + "dulcie's", + 'dull', + 'dulled', + 'duller', + 'dulling', + 'dulls', + 'duly', + 'dumbfounded', + 'dumbness', + 'dumbo', + "dumbo's", + 'dummies', + "dummy's", + 'dump', + 'dumped', + 'dumping', + 'dumpling', + "dumpling's", + 'dumplings', + 'dumps', + 'dumpster', + 'dumpy', + 'dun', + 'dunce', + 'dundee', + 'dune', + 'dunes', + 'dung', + 'dungeon', + "dungeon's", + 'dungeons', + 'dunk', + 'dunked', + 'dunking', + 'dunks', + 'dunno', + 'duns', + 'duo', + "duo's", + 'duodenary', + 'duos', + 'dup', + 'dupe', + 'duped', + 'duper', + 'dupes', + 'duplicate', + 'duplicated', + 'duplicates', + 'durability', + 'durable', + 'duranies', + 'duration', + 'durations', + 'during', + 'dusk', + 'duskfall', + 'dusky', + 'dust', + 'dusted', + 'duster', + "duster's", + 'dusters', + 'dusting', + 'dusts', + 'dusty', + 'dutch', + 'dutchman', + 'duties', + 'duty', + "duty's", + 'dvd', + "dvd's", + 'dvds', + 'dwarf', + 'dwarfs', + 'dwarves', + 'dwayne', + "dwayne's", + 'dwaynes', + 'dweeb', + 'dweebs', + 'dwell', + 'dwellers', + 'dwelling', + 'dwells', + 'dwight', + 'dwindle', + 'dwindling', + 'dxd', + 'dxd3', + 'dxdart', + 'dxdef', + 'dxdome', + 'dxdream', + 'dye', + 'dyed', + 'dyeing', + 'dyeing-talent', + 'dyes', + 'dying', + 'dylan', + "dylan's", + 'dylans', + 'dylon', + 'dynamic', + 'dynamite', + 'dynamo', + "dynamo's", + 'dynamos', + 'dynasty', + 'dynobee', + 'dynoberry', + 'dynoblabber', + 'dynobocker', + 'dynoboing', + 'dynoboom', + 'dynobounce', + 'dynobouncer', + 'dynobrains', + 'dynobubble', + 'dynobumble', + 'dynobump', + 'dynobumper', + 'dynoburger', + 'dynochomp', + 'dynocorn', + 'dynocrash', + 'dynocrumbs', + 'dynocrump', + 'dynocrunch', + 'dynodoodle', + 'dynodorf', + 'dynoface', + 'dynofidget', + 'dynofink', + 'dynofish', + 'dynoflap', + 'dynoflapper', + 'dynoflinger', + 'dynoflip', + 'dynoflipper', + 'dynofoot', + 'dynofuddy', + 'dynofussen', + 'dynogadget', + 'dynogargle', + 'dynogloop', + 'dynoglop', + 'dynogoober', + 'dynogoose', + 'dynogrooven', + 'dynohoffer', + 'dynohopper', + 'dynojinks', + 'dynoklunk', + 'dynoknees', + 'dynomarble', + 'dynomash', + 'dynomonkey', + 'dynomooch', + 'dynomouth', + 'dynomuddle', + 'dynomuffin', + 'dynomush', + 'dynonerd', + 'dynonoodle', + 'dynonose', + 'dynonugget', + 'dynophew', + 'dynophooey', + 'dynopocket', + 'dynopoof', + 'dynopop', + 'dynopounce', + 'dynopow', + 'dynopretzel', + 'dynoquack', + 'dynoroni', + 'dynoscooter', + 'dynoscreech', + 'dynosmirk', + 'dynosnooker', + 'dynosnoop', + 'dynosnout', + 'dynosocks', + 'dynospeed', + 'dynospinner', + 'dynosplat', + 'dynosprinkles', + 'dynosticks', + 'dynostink', + 'dynoswirl', + 'dynoteeth', + 'dynothud', + 'dynotoes', + 'dynoton', + 'dynotoon', + 'dynotooth', + 'dynotwist', + 'dynowhatsit', + 'dynowhip', + 'dynowig', + 'dynowoof', + 'dynozaner', + 'dynozap', + 'dynozapper', + 'dynozilla', + 'dynozoom', + 'dysfunctional', + 'dyslectic', + 'dyslexia', + 'dyslexic', + 'e-mail', + 'e.g.', + 'e.z.', + 'e_e', + 'eac', + 'each', + 'eager', + 'eagerly', + 'eagle', + "eagle's", + 'eagles', + 'ear', + "ear's", + 'earache', + 'earaches', + 'eared', + 'earful', + 'earing', + 'earl', + "earl's", + 'earlier', + 'earliest', + 'earls', + 'early', + 'early-morning', + 'earn', + 'earnable', + 'earned', + 'earner', + "earner's", + 'earners', + 'earnest', + 'earning', + "earning's", + 'earnings', + 'earns', + 'earplug', + 'earplugs', + 'earring', + 'earrings', + 'ears', + 'earshot', + 'earth', + "earth's", + 'earthed', + 'earthen', + 'earthling', + 'earthlings', + 'earthly', + 'earthquake', + 'earthy', + 'earwax', + 'ease', + 'easel', + "easel's", + 'easels', + 'eases', + 'easier', + 'easiest', + 'easily', + 'easing', + 'east', + "east's", + 'easter', + "easter's", + 'eastern', + 'easterner', + 'easterners', + 'easting', + 'easton', + 'easts', + 'easy', + 'eat', + 'eaten', + 'eater', + 'eateries', + 'eaters', + 'eatery', + 'eating', + 'eats', + 'eau', + 'eave', + 'eavesdropped', + 'eavesdroppers', + 'ebay', + "ebay's", + 'ebbohknee', + 'ebony', + 'eccentric', + 'echo', + 'echoes', + 'eclectic', + 'eclipse', + 'eco', + 'eco-logic', + 'economic', + 'economical', + 'economically', + 'economics', + 'economy', + 'ed', + "ed's", + 'eddie', + "eddie's", + 'eddies', + 'eddy', + 'eden', + 'edgar', + 'edge', + "edge's", + 'edged', + 'edger', + 'edges', + 'edgest', + 'edgewise', + 'edging', + 'edgy', + 'edible', + 'edit', + "edit's", + 'edited', + 'editing', + 'edition', + "edition's", + 'editions', + 'editor', + "editor's", + 'editors', + 'edits', + 'edmund', + "edmund's", + 'edmunds', + 'eds', + 'edt', + 'educate', + 'educated', + 'educating', + 'education', + "education's", + 'educational', + 'educationally', + 'educations', + 'edutainment', + 'edward', + 'eek', + 'eeky', + 'eepr', + 'eepy', + 'eerie', + 'eerily', + 'eewee', + "eewee's", + 'eeyore', + "eeyore's", + 'effect', + "effect's", + 'effected', + 'effecting', + 'effective', + 'effectively', + 'effectiveness', + 'effectives', + 'effects', + 'efficiency', + 'efficient', + 'effort', + "effort's", + 'effortless', + 'efforts', + 'egad', + 'egalitarian', + 'egalitarianism', + 'egalitarians', + 'egan', + 'egg', + "egg's", + 'egg-cellent', + 'eggcellent', + 'egged', + 'egging', + 'eggnog', + 'eggplant', + 'eggroll', + 'eggs', + 'eggshells', + 'eggventure', + 'ego', + "ego's", + 'egocentric', + 'egomaniac', + 'egos', + 'egotistical', + 'egypt', + 'eh', + 'ehem', + 'ehre', + 'eider', + 'eight', + 'eileen', + 'einherjar', + 'einstein', + "einstein's", + 'einsteins', + 'eitc', + 'either', + 'eject', + 'ejected', + 'ejecting', + 'ejects', + 'ekes', + 'el', + 'elaborate', + 'eland', + 'elastic', + 'elbow', + 'elbowed', + 'elbows', + 'elda', + "elda's", + 'elder', + 'elderberry', + 'elderly', + 'elders', + 'eldest', + 'elect', + 'elected', + 'electing', + 'election', + "election's", + 'elections', + 'elective', + "elective's", + 'electives', + 'electoral', + 'electra', + 'electric', + "electric's", + 'electrical', + 'electricities', + 'electricity', + 'electrics', + 'electrified', + 'electrifying', + 'electro', + 'electrobee', + 'electroberry', + 'electroblabber', + 'electrobocker', + 'electroboing', + 'electroboom', + 'electrobounce', + 'electrobouncer', + 'electrobrains', + 'electrobubble', + 'electrobumble', + 'electrobump', + 'electrobumper', + 'electroburger', + 'electrochomp', + 'electrocorn', + 'electrocrash', + 'electrocrumbs', + 'electrocrump', + 'electrocrunch', + 'electrocuted', + 'electrodoodle', + 'electrodorf', + 'electroface', + 'electrofidget', + 'electrofink', + 'electrofish', + 'electroflap', + 'electroflapper', + 'electroflinger', + 'electroflip', + 'electroflipper', + 'electrofoot', + 'electrofuddy', + 'electrofussen', + 'electrogadget', + 'electrogargle', + 'electrogloop', + 'electroglop', + 'electrogoober', + 'electrogoose', + 'electrogrooven', + 'electrohoffer', + 'electrohopper', + 'electrojinks', + 'electroklunk', + 'electroknees', + 'electromarble', + 'electromash', + 'electromonkey', + 'electromooch', + 'electromouth', + 'electromuddle', + 'electromuffin', + 'electromush', + 'electron', + 'electronerd', + 'electronic', + 'electronically', + 'electronics', + 'electronoodle', + 'electronose', + 'electrons', + 'electronugget', + 'electrophew', + 'electrophooey', + 'electropocket', + 'electropoof', + 'electropop', + 'electropounce', + 'electropow', + 'electropretzel', + 'electroquack', + 'electroroni', + 'electroscooter', + 'electroscreech', + 'electrosmirk', + 'electrosnooker', + 'electrosnoop', + 'electrosnout', + 'electrosocks', + 'electrospeed', + 'electrospinner', + 'electrosplat', + 'electrosprinkles', + 'electrosticks', + 'electrostink', + 'electroswirl', + 'electroteeth', + 'electrothud', + 'electrotoes', + 'electroton', + 'electrotoon', + 'electrotooth', + 'electrotwist', + 'electrowhatsit', + 'electrowhip', + 'electrowig', + 'electrowoof', + 'electrozaner', + 'electrozap', + 'electrozapper', + 'electrozilla', + 'electrozoom', + 'elects', + 'elegance', + 'elegant', + 'elegantly', + 'elegies', + 'element', + "element's", + 'elemental', + 'elementals', + 'elements', + 'elephant', + "elephant's", + 'elephants', + 'elevate', + 'elevated', + 'elevator', + "elevator's", + 'elevators', + 'eleven', + 'elf', + "elf's", + 'elif', + 'eligible', + 'eliminate', + 'eliminated', + 'elimination', + 'eliminator', + 'elite', + 'elites', + 'elitism', + 'elixa', + "elixa's", + 'elixir', + 'elixirs', + 'eliza', + 'elizabeth', + "elizabeth's", + 'elk', + 'ell', + 'ella', + "ella's", + 'elle', + 'ellie', + "ellie's", + "ello'", + 'ells', + 'elm', + 'elma', + 'elms', + 'elo', + 'eloise', + "eloise's", + 'elope', + 'elopuba', + 'eloquent', + 'eloquently', + 'elozar', + 'else', + "else's", + 'elsewhere', + 'elsie', + 'elude', + 'eludes', + 'eluding', + 'elusive', + 'elva', + 'elves', + 'elvis', + "elvis's", + 'em', + 'email', + 'embarcadero', + 'embark', + 'embarking', + 'embarks', + 'embarrass', + 'embarrassed', + 'embarrasses', + 'embarrassing', + 'embassy', + 'embed', + 'embedded', + 'ember', + 'embers', + 'emblem', + 'emblems', + 'embrace', + 'embraced', + 'embraces', + 'embracing', + 'emerald', + 'emeralds', + 'emerge', + 'emergencies', + 'emergency', + 'emerges', + 'emile', + "emile's", + 'emily', + "emily's", + 'emit', + 'emitting', + 'emma', + 'emote', + 'emoted', + 'emotes', + 'emoticon', + "emoticon's", + 'emoticons', + 'emotion', + "emotion's", + 'emotional', + 'emotions', + 'emoto-scope', + 'empathize', + 'emperor', + "emperor's", + 'emphasis', + 'emphasize', + 'emphasized', + 'empire', + "empire's", + 'empires', + 'employed', + 'employee', + 'employees', + 'employers', + 'employment', + 'employs', + 'empoison', + 'emporium', + "emporium's", + 'emporiums', + 'empower', + 'empowered', + 'empowering', + 'empress', + 'empresses', + 'emptied', + 'emptier', + 'empties', + 'emptiest', + 'emptiness', + 'empty', + 'emptying', + 'empyrean', + 'emrald', + 'emre', + 'enable', + 'enabled', + 'enabler', + "enabler's", + 'enablers', + 'enables', + 'enabling', + 'encampment', + 'enchant', + 'enchanted', + 'enchanter', + "enchanter's", + 'enchanting', + 'enchantmen', + 'enchantment', + 'enchantmet', + 'enchants', + 'enchilada', + 'enchiladas', + 'encircle', + 'encircling', + 'enclosed', + 'encoder', + 'encom', + 'encore', + "encore's", + 'encores', + 'encounter', + 'encourage', + 'encouraged', + 'encouragement', + 'encourager', + 'encourages', + 'encouraging', + 'encrusted', + 'encryption', + 'encyclopedia', + 'end', + 'endear', + 'endearing', + 'endears', + 'endeavor', + 'endeavors', + 'endeavour', + 'endeavours', + 'ended', + 'ender', + 'enders', + 'ending', + 'endings', + 'endive', + "endive's", + 'endives', + 'endless', + 'endlessly', + 'endorse', + 'endorsed', + 'endorsement', + 'endorsements', + 'endorses', + 'endorsing', + 'ends', + 'endurance', + 'endure', + 'enduring', + 'enemies', + 'enemy', + "enemy's", + 'energetic', + 'energies', + 'energize', + 'energized', + 'energizer', + 'energy', + 'enflame', + 'enforce', + 'enforcement', + 'enforcing', + 'eng', + 'engaged', + 'engagement', + 'engagements', + 'engenio', + 'engine', + "engine's", + 'engined', + 'engineer', + "engineer's", + 'engineered', + 'engineering', + 'engineers', + 'engines', + 'engining', + 'english', + 'engrave', + 'engraved', + 'engraves', + 'engrossed', + 'enigma', + 'enigmatic', + 'enigmeow', + 'enjos', + 'enjoy', + 'enjoyable', + 'enjoyed', + 'enjoying', + 'enjoyment', + 'enjoys', + 'enkindle', + 'enkindled', + 'enkindles', + 'enkindling', + 'enlighten', + 'enlightening', + 'enlightenment', + 'enlist', + 'enlisted', + 'enlisting', + 'enough', + 'enquire', + 'enraged', + 'enraging', + 'enriching', + 'enrique', + 'enroll', + 'enrolled', + 'enrolling', + 'ensemble', + 'ensembles', + 'ensign', + 'ensnare', + 'ensue', + 'ensues', + 'ensure', + 'ensured', + 'ensures', + 'ensuring', + 'entail', + 'entails', + 'entendre', + 'entendres', + 'enter', + 'entered', + 'enterer', + 'entering', + 'enterprise', + 'enterprisers', + 'enterprises', + 'enters', + 'entertain', + 'entertained', + 'entertainer', + 'entertainers', + 'entertaining', + 'entertainment', + "entertainment's", + 'entertainments', + 'entertains', + 'enthused', + 'enthusiasm', + 'enthusiastic', + 'entire', + 'entirely', + 'entirety', + 'entitled', + 'entourage', + 'entrain', + 'entrance', + "entrance's", + 'entranced', + 'entrances', + 'entrancing', + 'entries', + 'entropic', + 'entry', + "entry's", + 'entryway', + 'envelope', + "envelope's", + 'enveloped', + 'enveloper', + 'envelopes', + 'enveloping', + 'envied', + 'envious', + 'environ', + 'environment', + "environment's", + 'environmental', + 'environmentally', + 'environments', + 'envision', + 'envoy', + 'envy', + 'enzyme', + 'enzymes', + 'eon', + 'eons', + 'epcot', + "epcot's", + 'epic', + 'epics', + 'epilepsy', + 'epiphany', + 'episode', + 'episodes', + 'equal', + 'equaling', + 'equalizer', + 'equally', + 'equals', + 'equation', + 'equations', + 'equilibrium', + 'equip', + 'equipage', + 'equipment', + 'equipments', + 'equipped', + 'equips', + 'equity', + 'equivalent', + 'era', + 'eradicate', + 'eradication', + 'eradicators', + 'erase', + 'erased', + 'eraser', + 'erasers', + 'erases', + 'erasing', + 'erasmus', + 'ere', + 'erge', + 'ergo', + 'ergonomic', + 'eric', + 'errand', + 'errands', + 'errant', + 'erratic', + 'erratically', + 'erring', + 'error', + "error's", + 'errors', + 'errs', + 'errup', + 'erza', + 'esc', + 'escalate', + 'escalated', + 'escalates', + 'escalator', + 'escapade', + 'escapades', + 'escape', + 'escaped', + 'escaper', + "escaper's", + 'escapers', + 'escapes', + 'escaping', + 'escorted', + 'esmeralda', + "esmeralda's", + 'esmerelda', + 'especial', + 'especially', + 'esplanade', + 'espn', + "espn's", + 'espresso', + 'esquada', + 'esquago', + 'esquira', + 'esquire', + 'esqujillo', + 'esquoso', + 'esqutia', + 'essay', + 'essayer', + 'essays', + 'essence', + 'essences', + 'essential', + 'essentially', + 'essentials', + 'establish', + 'established', + 'establisher', + "establisher's", + 'establishers', + 'establishes', + 'establishing', + 'establishment', + "establishment's", + 'establishments', + 'estate', + 'estates', + 'esteem', + 'esteemed', + 'estenicks', + 'estimate', + 'estimated', + 'estimates', + 'estimating', + 'estimation', + 'estimations', + 'estimative', + 'eta', + 'etc', + 'eternal', + 'eternally', + 'eternity', + 'eternus', + 'ethan', + 'ethel', + 'ether', + 'ethereal', + 'ethics', + 'ethiopia', + 'ethnic', + 'etiquette', + 'etude', + 'eugene', + 'euphoric', + 'eureka', + 'euro', + 'europe', + 'euros', + 'eustabia', + 'eustaros', + 'evacuate', + 'evacuated', + 'evacuation', + 'evade', + 'evaded', + 'evades', + 'evading', + 'eval', + 'evan', + "evan's", + 'evans', + 'evaporate', + 'evaporated', + 'evasion', + 'evasive', + 'eve', + 'even', + 'evened', + 'evener', + 'evening', + "evening's", + 'evenings', + 'evenly', + 'evenness', + 'evens', + 'event', + "event's", + 'eventful', + 'events', + 'eventual', + 'eventually', + 'ever', + 'everest', + "everest's", + 'everfruit', + 'evergreen', + 'evergreens', + 'everlasting', + 'everlife', + "everlife's", + 'evermore', + 'evertree', + 'every', + 'everybody', + "everybody's", + 'everyday', + 'everyman', + 'everyone', + "everyone's", + 'everyones', + 'everything', + "everything's", + 'everywhere', + 'eves', + 'evict', + 'evicted', + 'eviction', + 'evidence', + 'evidenced', + 'evidences', + 'evidencing', + 'evident', + 'evidently', + 'evil', + 'evildance', + 'evilest', + 'evilly', + 'evilness', + 'evils', + 'evolution', + 'ewan', + "ewan's", + 'eww', + 'ewww', + 'ewwww', + 'ewwwww', + 'ewwwwww', + 'ewwwwwww', + 'ewwwwwwww', + 'ewwwwwwwww', + 'ex', + 'exacerbate', + 'exact', + 'exacted', + 'exacter', + "exacter's", + 'exacters', + 'exacting', + 'exactly', + 'exacts', + 'exaggerate', + 'exaggerated', + 'exaggeration', + 'exam', + 'examine', + 'examined', + 'examiner', + "examiner's", + 'examiners', + 'examines', + 'examining', + 'example', + "example's", + 'exampled', + 'examples', + 'exampling', + 'exams', + 'excavate', + 'excavation', + 'exceed', + 'exceeded', + 'exceedingly', + 'exceeds', + 'excel', + 'excellence', + 'excellences', + 'excellent', + 'excellently', + 'excels', + 'except', + 'excepted', + 'excepting', + 'exception', + 'exceptionally', + 'exceptions', + 'exceptive', + 'excepts', + 'excess', + 'excesses', + 'excessive', + 'exchange', + 'exchanged', + 'exchanger', + "exchanger's", + 'exchangers', + 'exchanges', + 'exchanging', + 'excitable', + 'excite-o-meter', + 'excited', + 'excitedly', + 'excitement', + 'exciter', + "exciter's", + 'exciters', + 'excites', + 'exciting', + 'exclaim', + 'exclaims', + 'exclamation', + 'exclamations', + 'exclude', + 'excluded', + 'excludes', + 'excluding', + 'exclusive', + 'exclusively', + 'excommunicate', + 'excruciating', + 'excursion', + 'excuse', + 'excused', + 'excuser', + "excuser's", + 'excusers', + 'excuses', + 'excusing', + 'exe', + 'exec', + 'executive', + 'executor', + 'exemplary', + 'exempt', + 'exercise', + 'exercised', + 'exerciser', + "exerciser's", + 'exercisers', + 'exercises', + 'exercising', + 'exert', + 'exhales', + 'exhaust', + 'exhausted', + 'exhausting', + 'exhibit', + 'exhibition', + "exhibition's", + 'exhibitioner', + "exhibitioner's", + 'exhibitions', + 'exhibitor', + 'exhilarating', + 'exile', + "exile's", + 'exiled', + 'exiles', + 'exist', + 'existed', + 'existence', + 'existences', + 'existing', + 'exists', + 'exit', + 'exited', + 'exiting', + 'exits', + 'exodus', + 'exorcising', + 'exotic', + 'expand', + 'expanded', + 'expanding', + 'expands', + 'expansion', + 'expansions', + 'expect', + 'expectation', + "expectation's", + 'expectations', + 'expected', + 'expecting', + 'expects', + 'expedition', + "expedition's", + 'expeditions', + 'expel', + 'expelled', + 'expend', + 'expenditures', + 'expends', + 'expense', + 'expensed', + 'expenses', + 'expensing', + 'expensive', + 'expensively', + 'experience', + 'experienced', + 'experiences', + 'experiencing', + 'experiment', + 'experimental', + 'experimented', + 'experimenter', + "experimenter's", + 'experimenters', + 'experimenting', + 'experiments', + 'expert', + "expert's", + 'expertise', + 'expertly', + 'experts', + 'expiration', + 'expire', + 'expired', + 'expires', + 'explain', + 'explained', + 'explainer', + "explainer's", + 'explainers', + 'explaining', + 'explains', + 'explanation', + "explanation's", + 'explanations', + 'explanatory', + 'explode', + 'exploded', + 'exploder', + "exploder's", + 'exploders', + 'explodes', + 'exploding', + 'exploit', + 'exploiting', + 'exploits', + 'exploration', + "exploration's", + 'explorations', + 'explore', + 'explored', + 'explorer', + "explorer's", + 'explorers', + 'explores', + 'exploring', + 'explosion', + "explosion's", + 'explosions', + 'explosive', + 'expo', + "expo's", + 'exponential', + 'exponentially', + 'export', + 'exporter', + 'exports', + 'expose', + 'exposed', + 'exposing', + 'exposition', + 'exposure', + 'express', + 'expressed', + 'expresser', + "expresser's", + 'expresses', + 'expressing', + 'expression', + "expression's", + 'expressions', + 'expressive', + 'expressly', + 'expunge', + 'expunged', + 'ext', + 'extend', + 'extended', + 'extender', + 'extending', + 'extends', + 'extension', + 'extensive', + 'extent', + "extent's", + 'extents', + 'exterior', + 'external', + 'externals', + 'extinct', + 'extinction', + 'extinguish', + 'extinguisher', + 'extinguishers', + 'extra', + 'extract', + 'extracting', + 'extraordinarily', + 'extraordinary', + 'extras', + 'extravagant', + 'extravaganza', + 'extream', + 'extreme', + 'extremed', + 'extremely', + 'extremer', + 'extremes', + 'extremest', + 'extricate', + 'exuberant', + 'exubia', + 'exuma', + 'exumbris', + 'eye', + "eye's", + 'eyeball', + 'eyeballing', + 'eyeballs', + 'eyebrow', + 'eyebrows', + 'eyed', + 'eyeglass', + 'eyeglasses', + 'eyeing', + 'eyelash', + 'eyelashes', + 'eyeless', + 'eyelids', + 'eyepatch', + 'eyes', + 'eyesight', + 'eyestrain', + 'eying', + 'ezra', + "ezra's", + 'f-untangles', + 'f1', + 'f10', + 'f11', + 'f12', + 'f2', + 'f3', + 'f4', + 'f5', + 'f6', + 'f7', + 'f8', + 'f9', + 'fab', + 'faber', + 'fabiola', + 'fable', + 'fabric', + 'fabrics', + 'fabulous', + 'facade', + 'face', + "face's", + 'faced', + 'faceing', + 'faceless', + 'faceoff', + 'facepalm', + 'facepalms', + 'facer', + 'faces', + 'facets', + 'facialhair', + 'facile', + 'facilitate', + 'facilities', + 'facility', + "facin'", + 'facing', + 'facings', + 'fact', + "fact's", + 'factio', + 'faction', + 'factious', + 'factor', + "factor's", + 'factored', + 'factories', + 'factoring', + 'factorings', + 'factors', + 'factory', + 'facts', + 'factual', + 'faculties', + 'faculty', + 'fad', + 'faddy', + 'fade', + 'faded', + 'fader', + 'faders', + 'fades', + 'fading', + 'fads', + 'fae', + 'faffy', + 'fail', + 'failed', + 'failing', + 'failings', + 'fails', + 'failure', + "failure's", + 'failures', + 'faint', + 'fainted', + 'fainter', + "fainter's", + 'fainters', + 'faintest', + 'fainting', + 'faintly', + 'faints', + 'fair', + "fair's", + 'fairbanks', + 'faircrest', + 'faire', + "faire's", + 'faired', + 'fairer', + 'fairest', + 'fairies', + "fairies'", + 'fairing', + 'fairly', + 'fairness', + 'fairs', + 'fairy', + "fairy's", + 'fairycake', + 'fairytale', + 'fairytales', + 'fait', + 'faith', + 'faithful', + 'faithless', + 'fajitas', + 'fake', + 'faked', + 'faker', + 'faking', + 'falchion', + 'falco', + 'falcon', + 'falcons', + 'fall', + 'fallback', + 'fallbrook', + 'fallen', + 'faller', + 'falling', + 'fallout', + 'fallover', + 'fallow', + 'fallowing', + 'fallows', + 'falls', + 'false', + 'falsely', + 'falser', + 'falsest', + 'falsified', + 'fame', + 'famed', + 'famers', + 'fames', + 'familiar', + 'familiarize', + 'familiarly', + 'familiars', + 'families', + 'family', + "family's", + 'famine', + 'faming', + 'famished', + 'famous', + 'famously', + 'fan', + "fan's", + 'fanatic', + 'fanatical', + 'fanboy', + 'fancied', + 'fancier', + "fancier's", + 'fanciers', + 'fancies', + 'fanciest', + 'fancy', + 'fancying', + 'fandom', + 'fane', + 'fanfare', + 'fanfiction', + "fang's", + 'fangled', + 'fangs', + 'fanned', + 'fans', + 'fantabulous', + 'fantasia', + 'fantasmic', + 'fantastic', + 'fantasticly', + 'fantastico', + 'fantasy', + "fantasy's", + 'fantasyland', + "fantasyland's", + 'far', + 'far-fetched', + 'faraway', + 'farce', + 'fare', + 'fared', + 'farer', + 'fares', + 'farewell', + 'farewells', + 'faring', + 'farm', + "farm's", + 'farmed', + 'farmer', + "farmer's", + 'farmers', + 'farming', + 'farmland', + 'farms', + 'farther', + 'farthest', + 'fascinate', + 'fascinated', + 'fascinates', + 'fascinating', + 'fascination', + 'fascists', + 'fashion', + "fashion's", + 'fashionable', + 'fashionably', + 'fashioned', + 'fashioner', + "fashioner's", + 'fashioners', + 'fashioning', + 'fashions', + 'fast', + 'fast-flying', + 'fast-pass', + 'fasted', + 'fasten', + 'fastens', + 'faster', + 'fasters', + 'fastest', + 'fasting', + 'fastpass', + 'fasts', + 'fat', + 'fatale', + 'fatales', + 'fatality', + 'fate', + "fate's", + 'fated', + 'fateful', + 'fates', + 'father', + "father's", + 'fathers', + 'fathom', + 'fatigue', + 'fating', + 'fatten', + 'fattening', + 'faucet', + 'fault', + 'faulted', + 'faulting', + 'faults', + 'faulty', + 'fauna', + "fauna's", + 'faunas', + 'fauns', + 'fausto', + 'fauxhawk', + 'fave', + 'favor', + 'favorable', + 'favored', + 'favoring', + 'favorite', + 'favorites', + 'favoritism', + 'favors', + 'favourite', + 'fawn', + "fawn's", + 'fax', + 'faxing', + 'faye', + 'faze', + 'fear', + "fear's", + 'feared', + 'fearer', + 'fearful', + 'fearfully', + 'fearhawk', + 'fearing', + 'fearles', + 'fearless', + 'fearlessly', + 'fearlessness', + 'fears', + 'fearsome', + 'feasible', + 'feast', + "feast's", + 'feasted', + 'feaster', + 'feasting', + 'feasts', + 'feat', + 'feather', + "feather's", + 'feather.', + 'featherbee', + 'featherberry', + 'featherblabber', + 'featherbocker', + 'featherboing', + 'featherboom', + 'featherbounce', + 'featherbouncer', + 'featherbrains', + 'featherbubble', + 'featherbumble', + 'featherbump', + 'featherbumper', + 'featherburger', + 'featherchomp', + 'feathercorn', + 'feathercrash', + 'feathercrumbs', + 'feathercrump', + 'feathercrunch', + 'featherdoodle', + 'featherdorf', + 'feathered', + 'featherer', + "featherer's", + 'featherers', + 'featherface', + 'featherfidget', + 'featherfink', + 'featherfish', + 'featherflap', + 'featherflapper', + 'featherflinger', + 'featherflip', + 'featherflipper', + 'featherfoot', + 'featherfuddy', + 'featherfussen', + 'feathergadget', + 'feathergargle', + 'feathergloop', + 'featherglop', + 'feathergoober', + 'feathergoose', + 'feathergrooven', + 'featherhoffer', + 'featherhopper', + 'feathering', + 'featherjinks', + 'featherklunk', + 'featherknees', + 'feathermarble', + 'feathermash', + 'feathermonkey', + 'feathermooch', + 'feathermouth', + 'feathermuddle', + 'feathermuffin', + 'feathermush', + 'feathernerd', + 'feathernoodle', + 'feathernose', + 'feathernugget', + 'featherphew', + 'featherphooey', + 'featherpocket', + 'featherpoof', + 'featherpop', + 'featherpounce', + 'featherpow', + 'featherpretzel', + 'featherquack', + 'featherroni', + 'feathers', + 'featherscooter', + 'featherscreech', + 'feathersmirk', + 'feathersnooker', + 'feathersnoop', + 'feathersnout', + 'feathersocks', + 'featherspeed', + 'featherspinner', + 'feathersplat', + 'feathersprinkles', + 'feathersticks', + 'featherstink', + 'featherswirl', + 'featherteeth', + 'featherthud', + 'feathertoes', + 'featherton', + 'feathertoon', + 'feathertooth', + 'feathertwist', + 'featherwhatsit', + 'featherwhip', + 'featherwig', + 'featherwoof', + 'feathery', + 'featherzaner', + 'featherzap', + 'featherzapper', + 'featherzilla', + 'featherzoom', + 'feature', + 'featured', + 'features', + 'featurette', + 'featurettes', + 'featuring', + 'feb', + 'february', + 'feckless', + 'fed', + 'federal', + 'federation', + 'fedex', + 'fedora', + 'feds', + 'feeble', + 'feed', + 'feedback', + 'feeder', + "feeder's", + 'feeders', + 'feeding', + 'feedings', + 'feeds', + 'feel', + 'feelers', + 'feelin', + "feelin'", + 'feeling', + 'feelings', + 'feels', + 'fees', + 'feet', + 'feints', + 'feisty', + 'felicia', + 'felicitation', + 'felicity', + "felicity's", + 'feline', + 'felipe', + 'felix', + 'fell', + 'fella', + 'felled', + 'feller', + 'fellers', + 'felling', + 'felloe', + 'fellow', + 'fellows', + 'fellowship', + 'fells', + 'felt', + 'fem', + 'female', + 'females', + 'feminine', + 'femme', + 'femmes', + 'fen', + 'fence', + 'fenced', + 'fencer', + "fencer's", + 'fencers', + 'fences', + 'fencing', + 'fend', + 'fender', + 'fenders', + 'fending', + 'feng', + 'feral', + 'ferdie', + 'fern', + "fern's", + 'fern-frond', + 'fernando', + 'ferns', + 'ferrari', + 'ferrera', + "ferrera's", + 'ferret', + "ferret's", + 'ferrets', + 'ferris', + 'fertilizer', + 'fess', + 'fesses', + 'fest', + 'festering', + 'festival', + "festival's", + 'festivals', + 'festive', + 'festively', + 'festivities', + 'fests', + 'feta', + 'fetch', + 'fetcher', + 'fetches', + 'fetching', + 'fetes', + 'fetter', + 'fetters', + 'feud', + 'feuding', + 'fever', + "fever's", + 'fevered', + 'fevering', + 'feverish', + 'fevers', + 'few', + 'fewer', + 'fewest', + 'fews', + 'fez', + 'fi', + 'fiasco', + 'fiat', + 'fib', + 'fibbed', + 'fibber', + 'fibbing', + 'fiber', + 'fiberglass', + 'fibre', + 'fickle', + 'fiction', + "fiction's", + 'fictional', + 'fictions', + 'fid', + "fid's", + 'fiddle', + 'fiddlebee', + 'fiddleberry', + 'fiddleblabber', + 'fiddlebocker', + 'fiddleboing', + 'fiddleboom', + 'fiddlebounce', + 'fiddlebouncer', + 'fiddlebrains', + 'fiddlebubble', + 'fiddlebumble', + 'fiddlebump', + 'fiddlebumper', + 'fiddleburger', + 'fiddlechomp', + 'fiddlecorn', + 'fiddlecrash', + 'fiddlecrumbs', + 'fiddlecrump', + 'fiddlecrunch', + 'fiddled', + 'fiddledoodle', + 'fiddledorf', + 'fiddleface', + 'fiddlefidget', + 'fiddlefink', + 'fiddlefish', + 'fiddleflap', + 'fiddleflapper', + 'fiddleflinger', + 'fiddleflip', + 'fiddleflipper', + 'fiddlefoot', + 'fiddlefuddy', + 'fiddlefussen', + 'fiddlegadget', + 'fiddlegargle', + 'fiddlegloop', + 'fiddleglop', + 'fiddlegoober', + 'fiddlegoose', + 'fiddlegrooven', + 'fiddlehead', + 'fiddlehoffer', + 'fiddlehopper', + 'fiddlejinks', + 'fiddleklunk', + 'fiddleknees', + 'fiddlemarble', + 'fiddlemash', + 'fiddlemonkey', + 'fiddlemooch', + 'fiddlemouth', + 'fiddlemuddle', + 'fiddlemuffin', + 'fiddlemush', + 'fiddlenerd', + 'fiddlenoodle', + 'fiddlenose', + 'fiddlenugget', + 'fiddlephew', + 'fiddlephooey', + 'fiddlepocket', + 'fiddlepoof', + 'fiddlepop', + 'fiddlepounce', + 'fiddlepow', + 'fiddlepretzel', + 'fiddlequack', + "fiddler's", + 'fiddleroni', + 'fiddles', + 'fiddlescooter', + 'fiddlescreech', + 'fiddlesmirk', + 'fiddlesnooker', + 'fiddlesnoop', + 'fiddlesnout', + 'fiddlesocks', + 'fiddlespeed', + 'fiddlespinner', + 'fiddlesplat', + 'fiddlesprinkles', + 'fiddlestick', + 'fiddlesticks', + 'fiddlestink', + 'fiddleswirl', + 'fiddleteeth', + 'fiddlethud', + 'fiddletoes', + 'fiddleton', + 'fiddletoon', + 'fiddletooth', + 'fiddletwist', + 'fiddlewhatsit', + 'fiddlewhip', + 'fiddlewig', + 'fiddlewoof', + 'fiddlezaner', + 'fiddlezap', + 'fiddlezapper', + 'fiddlezilla', + 'fiddlezoom', + 'fiddling', + 'fide', + 'fidelity', + 'fidget', + 'fidgety', + 'fids', + 'fie', + 'fief', + 'field', + "field's", + 'fielded', + 'fielder', + "fielder's", + 'fielders', + 'fielding', + 'fieldpiece', + 'fields', + 'fiend', + 'fiends', + 'fierce', + 'fiercely', + 'fiercer', + 'fiercest', + 'fiery', + 'fifa', + 'fifi', + "fifi's", + 'fifth', + 'fig', + 'figaro', + "figaro's", + 'figaros', + 'fight', + "fight's", + 'fightable', + 'fighter', + "fighter's", + 'fighters', + 'fighting', + 'fights', + 'figment', + "figment's", + 'figments', + 'figurations', + 'figurative', + 'figure', + "figure's", + 'figured', + 'figurehead', + 'figureheads', + 'figurer', + "figurer's", + 'figurers', + 'figures', + 'figurine', + "figurine's", + 'figurines', + 'figuring', + 'figurings', + 'file', + "file's", + 'filed', + 'filename', + 'fileplanet', + 'filer', + 'filers', + 'files', + 'filial', + 'filibuster', + 'filing', + 'filings', + 'filipino', + 'fill', + "fill's", + 'filled', + 'filler', + 'fillers', + 'fillies', + 'filling', + 'fillings', + 'fillmore', + 'fills', + 'filly', + 'filmed', + 'filming', + 'filmmaking', + 'films', + 'filter', + 'filtered', + 'filtering', + 'filters', + 'fin', + "fin's", + 'finagled', + 'final', + 'finale', + "finale's", + 'finales', + 'finalist', + 'finalize', + 'finalized', + 'finally', + 'finals', + 'finance', + 'finances', + 'financial', + 'financially', + 'financing', + 'finch', + 'find', + 'finder', + "finder's", + 'finders', + 'findin', + "findin'", + 'finding', + "finding's", + 'findings', + 'finds', + 'fine', + 'fined', + 'finely', + 'finer', + 'fines', + 'finesse', + 'finest', + 'finfish', + 'fingerboards', + 'fingernails', + 'fingers', + 'fingertips', + 'finicky', + 'fining', + 'finis', + 'finish', + 'finished', + 'finisher', + 'finishers', + 'finishes', + 'finishing', + 'finishings', + 'fink', + 'finks', + 'finn', + "finn's", + 'fins', + 'fir', + 'fira', + "fira's", + 'fire', + "fire's", + 'fire-sail', + 'firebrand', + 'firebrands', + 'firecrackers', + 'fired', + 'firefighter', + 'fireflies', + 'firehawks', + 'firehydrant', + 'firehydrants', + 'fireman', + "fireman's", + 'firemans', + 'firemen', + "firemen's", + 'fireplace', + 'fireplaces', + 'firepots', + 'firepower', + 'fireproof', + 'firer', + 'firers', + 'fires', + 'firespinner', + 'firetoon', + 'firewall', + 'firewalls', + 'fireweed', + 'firework', + "firework's", + 'fireworks', + 'firey', + 'firing', + 'firings', + 'firms', + 'firmware', + 'first', + 'firsthand', + 'firstly', + 'firsts', + 'fiscal', + 'fish', + "fish's", + 'fished', + 'fisher', + 'fisherman', + "fisherman's", + 'fishermans', + 'fishermen', + 'fishers', + 'fishertoon', + 'fishertoons', + 'fishery', + 'fishes', + 'fisheyes', + 'fishier', + 'fishies', + 'fishin', + "fishin'", + 'fishing', + 'fishtailed', + 'fishy', + 'fist', + 'fistful', + 'fists', + 'fit', + 'fitly', + 'fitness', + 'fits', + 'fitte', + 'fitted', + 'fittest', + 'fitting', + 'fitz', + 'fitzpatrick', + 'five', + 'fix', + 'fixable', + 'fixated', + 'fixe', + 'fixed', + 'fixer', + "fixer's", + 'fixers', + 'fixes', + 'fixin', + "fixin'", + "fixin's", + 'fixing', + 'fixings', + 'fixit', + 'fixture', + 'fizpatrick', + 'fizzle', + 'fizzlebee', + 'fizzleberry', + 'fizzleblabber', + 'fizzlebocker', + 'fizzleboing', + 'fizzleboom', + 'fizzlebounce', + 'fizzlebouncer', + 'fizzlebrains', + 'fizzlebubble', + 'fizzlebumble', + 'fizzlebump', + 'fizzlebumper', + 'fizzleburger', + 'fizzlechomp', + 'fizzlecorn', + 'fizzlecrash', + 'fizzlecrumbs', + 'fizzlecrump', + 'fizzlecrunch', + 'fizzled', + 'fizzledoodle', + 'fizzledorf', + 'fizzleface', + 'fizzlefidget', + 'fizzlefink', + 'fizzlefish', + 'fizzleflap', + 'fizzleflapper', + 'fizzleflinger', + 'fizzleflip', + 'fizzleflipper', + 'fizzlefoot', + 'fizzlefuddy', + 'fizzlefussen', + 'fizzlegadget', + 'fizzlegargle', + 'fizzlegloop', + 'fizzleglop', + 'fizzlegoober', + 'fizzlegoose', + 'fizzlegrooven', + 'fizzlehoffer', + 'fizzlehopper', + 'fizzlejinks', + 'fizzleklunk', + 'fizzleknees', + 'fizzlemarble', + 'fizzlemash', + 'fizzlemonkey', + 'fizzlemooch', + 'fizzlemouth', + 'fizzlemuddle', + 'fizzlemuffin', + 'fizzlemush', + 'fizzlenerd', + 'fizzlenoodle', + 'fizzlenose', + 'fizzlenugget', + 'fizzlephew', + 'fizzlephooey', + 'fizzlepocket', + 'fizzlepoof', + 'fizzlepop', + 'fizzlepounce', + 'fizzlepow', + 'fizzlepretzel', + 'fizzlequack', + 'fizzleroni', + 'fizzles', + 'fizzlescooter', + 'fizzlescreech', + 'fizzlesmirk', + 'fizzlesnooker', + 'fizzlesnoop', + 'fizzlesnout', + 'fizzlesocks', + 'fizzlespeed', + 'fizzlespinner', + 'fizzlesplat', + 'fizzlesprinkles', + 'fizzlesticks', + 'fizzlestink', + 'fizzleswirl', + 'fizzleteeth', + 'fizzlethud', + 'fizzletoes', + 'fizzleton', + 'fizzletoon', + 'fizzletooth', + 'fizzletwist', + 'fizzlewhatsit', + 'fizzlewhip', + 'fizzlewig', + 'fizzlewoof', + 'fizzlezaner', + 'fizzlezap', + 'fizzlezapper', + 'fizzlezilla', + 'fizzlezoom', + 'fizzling', + 'fizzy', + 'flack', + 'flag', + "flag's", + 'flaged', + 'flagged', + 'flagger', + 'flaggers', + 'flagging', + 'flaggy', + 'flaging', + 'flagon', + 'flagons', + 'flagpole', + "flagpole's", + 'flagpoles', + 'flagrant', + 'flagrantly', + 'flags', + 'flagship', + 'flagships', + "flagships'", + 'flail', + 'flailin', + 'flailing', + 'flails', + 'flair', + 'flak', + 'flakcannon', + 'flake', + 'flaked', + 'flakes', + 'flakey', + 'flaky', + 'flam', + 'flamboyant', + 'flame', + "flame's", + 'flamed', + 'flamefish', + 'flameless', + 'flames', + 'flamethrower', + 'flaming', + 'flamingo', + "flamingo's", + 'flamingos', + 'flammable', + 'flammables', + 'flank', + 'flanked', + 'flanking', + 'flannel', + 'flap', + 'flapjack', + 'flapjacks', + 'flappin', + "flappin'", + 'flapping', + 'flappy', + 'flare', + "flare's", + 'flared', + 'flares', + 'flash', + 'flashback', + 'flashbacks', + 'flashed', + 'flasher', + 'flashers', + 'flashing', + 'flashium', + 'flashlight', + 'flashy', + 'flask', + 'flat', + 'flatbed', + 'flatfish', + 'flatly', + 'flats', + 'flatten', + 'flattened', + 'flattener', + 'flattening', + 'flattens', + 'flatter', + 'flattered', + 'flatterer', + 'flattering', + 'flattery', + 'flattop', + 'flatts', + 'flaunt', + 'flava', + 'flavio', + "flavio's", + 'flavor', + "flavor's", + 'flavored', + 'flavorful', + 'flavoring', + 'flavors', + 'flavour', + 'flaw', + 'flawed', + 'flawless', + 'flaws', + 'flax', + 'flayin', + 'flaying', + 'flea', + "flea's", + 'fleabag', + 'fleas', + 'fleck', + 'fled', + 'fledge', + 'fledged', + 'flee', + 'fleece', + 'fleeces', + 'fleed', + 'fleein', + 'fleeing', + 'fleem', + 'fleemco', + 'fleer', + 'fleet', + "fleet's", + 'fleeting', + 'fleets', + 'fleshwound', + 'fletching', + 'fleur', + 'flew', + 'flex', + 'flexible', + 'flick', + 'flicker', + 'flickered', + 'flickering', + 'flickers', + 'flicking', + 'flicks', + 'flied', + 'flier', + 'fliers', + 'flies', + 'flight', + 'flightless', + 'flights', + 'flighty', + 'flim', + 'flimsy', + 'flinches', + 'fling', + 'flinging', + 'flint', + "flint's", + 'flintlock', + 'flintlocke', + 'flintlocks', + 'flints', + 'flinty', + "flinty's", + 'flip', + 'flipbook', + "flipbook's", + 'flipbooks', + 'fliped', + 'flipped', + 'flippenbee', + 'flippenberry', + 'flippenblabber', + 'flippenbocker', + 'flippenboing', + 'flippenboom', + 'flippenbounce', + 'flippenbouncer', + 'flippenbrains', + 'flippenbubble', + 'flippenbumble', + 'flippenbump', + 'flippenbumper', + 'flippenburger', + 'flippenchomp', + 'flippencorn', + 'flippencrash', + 'flippencrumbs', + 'flippencrump', + 'flippencrunch', + 'flippendoodle', + 'flippendorf', + 'flippenface', + 'flippenfidget', + 'flippenfink', + 'flippenfish', + 'flippenflap', + 'flippenflapper', + 'flippenflinger', + 'flippenflip', + 'flippenflipper', + 'flippenfoot', + 'flippenfuddy', + 'flippenfussen', + 'flippengadget', + 'flippengargle', + 'flippengloop', + 'flippenglop', + 'flippengoober', + 'flippengoose', + 'flippengrooven', + 'flippenhoffer', + 'flippenhopper', + 'flippenjinks', + 'flippenklunk', + 'flippenknees', + 'flippenmarble', + 'flippenmash', + 'flippenmonkey', + 'flippenmooch', + 'flippenmouth', + 'flippenmuddle', + 'flippenmuffin', + 'flippenmush', + 'flippennerd', + 'flippennoodle', + 'flippennose', + 'flippennugget', + 'flippenphew', + 'flippenphooey', + 'flippenpocket', + 'flippenpoof', + 'flippenpop', + 'flippenpounce', + 'flippenpow', + 'flippenpretzel', + 'flippenquack', + 'flippenroni', + 'flippenscooter', + 'flippenscreech', + 'flippensmirk', + 'flippensnooker', + 'flippensnoop', + 'flippensnout', + 'flippensocks', + 'flippenspeed', + 'flippenspinner', + 'flippensplat', + 'flippensprinkles', + 'flippensticks', + 'flippenstink', + 'flippenswirl', + 'flippenteeth', + 'flippenthud', + 'flippentoes', + 'flippenton', + 'flippentoon', + 'flippentooth', + 'flippentwist', + 'flippenwhatsit', + 'flippenwhip', + 'flippenwig', + 'flippenwoof', + 'flippenzaner', + 'flippenzap', + 'flippenzapper', + 'flippenzilla', + 'flippenzoom', + 'flipper', + 'flipperbee', + 'flipperberry', + 'flipperblabber', + 'flipperbocker', + 'flipperboing', + 'flipperboom', + 'flipperbounce', + 'flipperbouncer', + 'flipperbrains', + 'flipperbubble', + 'flipperbumble', + 'flipperbump', + 'flipperbumper', + 'flipperburger', + 'flipperchomp', + 'flippercorn', + 'flippercrash', + 'flippercrumbs', + 'flippercrump', + 'flippercrunch', + 'flipperdoodle', + 'flipperdorf', + 'flipperface', + 'flipperfidget', + 'flipperfink', + 'flipperfish', + 'flipperflap', + 'flipperflapper', + 'flipperflinger', + 'flipperflip', + 'flipperflipper', + 'flipperfoot', + 'flipperfuddy', + 'flipperfussen', + 'flippergadget', + 'flippergargle', + 'flippergloop', + 'flipperglop', + 'flippergoober', + 'flippergoose', + 'flippergrooven', + 'flipperhoffer', + 'flipperhopper', + 'flipperjinks', + 'flipperklunk', + 'flipperknees', + 'flippermarble', + 'flippermash', + 'flippermonkey', + 'flippermooch', + 'flippermouth', + 'flippermuddle', + 'flippermuffin', + 'flippermush', + 'flippernerd', + 'flippernoodle', + 'flippernose', + 'flippernugget', + 'flipperphew', + 'flipperphooey', + 'flipperpocket', + 'flipperpoof', + 'flipperpop', + 'flipperpounce', + 'flipperpow', + 'flipperpretzel', + 'flipperquack', + 'flipperroni', + 'flippers', + 'flipperscooter', + 'flipperscreech', + 'flippersmirk', + 'flippersnooker', + 'flippersnoop', + 'flippersnout', + 'flippersocks', + 'flipperspeed', + 'flipperspinner', + 'flippersplat', + 'flippersprinkles', + 'flippersticks', + 'flipperstink', + 'flipperswirl', + 'flipperteeth', + 'flipperthud', + 'flippertoes', + 'flipperton', + 'flippertoon', + 'flippertooth', + 'flippertwist', + 'flipperwhatsit', + 'flipperwhip', + 'flipperwig', + 'flipperwoof', + 'flipperzaner', + 'flipperzap', + 'flipperzapper', + 'flipperzilla', + 'flipperzoom', + 'flippin', + "flippin'", + 'flipping', + 'flippy', + "flippy's", + 'flips', + 'flipside', + 'flit', + 'flits', + 'flitter', + 'flix', + 'flo', + "flo's", + 'float', + 'floatation', + 'floated', + 'floater', + "floater's", + 'floaters', + 'floating', + 'floats', + 'floccinaucinihilipilification', + 'floe', + 'flood', + 'flooded', + 'flooder', + 'flooding', + 'floods', + 'floor', + 'floorboard', + 'floored', + 'floorer', + 'flooring', + 'floorings', + 'floorplan', + 'floors', + 'flop', + 'flopped', + 'flopping', + 'flops', + 'flora', + "flora's", + 'floras', + 'florence', + 'florian', + "florian's", + 'florist', + 'floss', + 'flossing', + 'flotation', + 'flotsam', + "flotsam's", + 'flotsams', + 'flounder', + "flounder's", + 'flounders', + 'flour', + 'flourish', + 'flourishes', + 'flours', + 'flout', + 'flouting', + 'flow', + 'flowchart', + 'flowed', + 'flower', + "flower's", + 'flowered', + 'flowerer', + 'flowering', + 'flowers', + 'flowery', + 'flowing', + 'flown', + 'flows', + 'floyd', + 'flu', + 'flub', + 'flubber', + 'flue', + 'fluent', + 'fluently', + 'fluffy', + "fluffy's", + 'flugle', + 'fluid', + 'fluids', + 'fluke', + 'fluky', + 'flump', + 'flung', + 'flunk', + 'flunked', + 'flunkies', + 'flunking', + 'flunky', + 'fluorescence', + 'fluorescent', + 'flurries', + 'flurry', + 'flustered', + 'flute', + 'flutes', + 'flutter', + 'flutterby', + "flutterby's", + 'fluttering', + 'flutters', + 'fluttershy', + 'fluttery', + 'fly', + "fly's", + 'fly-away', + 'flyby', + 'flycatcher', + 'flycatchers', + 'flyer', + 'flyers', + 'flyinator', + 'flying', + 'flyleaf', + 'flyleaves', + 'flynn', + "flynn's", + 'flys', + 'flyswatter', + 'flytrap', + 'flytraps', + 'foam', + 'foaming', + 'fobs', + 'focus', + 'focused', + 'focuser', + 'focuses', + 'focusing', + 'fodder', + 'fodders', + 'foe', + 'foes', + 'fog', + "fog's", + 'foggiest', + 'foggy', + 'foghorn', + 'foghorns', + 'fogs', + 'foil', + 'foiled', + 'fold', + 'folded', + 'folder', + "folder's", + 'folders', + 'folding', + 'foldings', + 'folds', + 'foley', + 'foliage', + 'folio', + "folio's", + 'folk', + "folk's", + 'folks', + 'follow', + 'followed', + 'follower', + 'followers', + 'following', + 'followings', + 'follows', + 'folly', + 'fond', + 'fonder', + 'fondness', + 'fondue', + 'fons', + 'font', + 'fonts', + 'food', + "food's", + 'foodnetwork', + 'foods', + 'foodservice', + 'fool', + "fool's", + 'fooled', + 'fooler', + "fooler's", + 'foolers', + 'foolery', + 'foolhardiness', + 'foolhardy', + 'fooling', + 'foolings', + 'foolish', + 'foolishly', + 'foolishness', + 'foolproof', + 'fools', + 'foot', + 'foot-high', + 'football', + "football's", + 'footballed', + 'footballer', + "footballer's", + 'footballers', + 'footballs', + 'foote', + 'footed', + 'footer', + "footer's", + 'footers', + 'foothills', + 'footie', + 'footing', + 'footings', + 'footprints', + 'foots', + 'footsies', + 'footsteps', + 'footsy', + 'footwork', + 'footy', + 'for', + 'forage', + 'forager', + 'foraging', + 'forbid', + 'forbidden', + 'forbids', + 'force', + "force's", + 'force-field', + 'forced', + 'forcer', + 'forces', + 'forcing', + 'ford', + "ford's", + 'fordoing', + 'fords', + 'fore', + 'forearm', + 'forecast', + 'forecastle', + 'forecasts', + 'foredeck', + 'forego', + 'forehead', + 'foreign', + 'foreigner', + 'foreman', + 'foremast', + 'foresail', + 'foresee', + 'foreshadow', + 'foreshadowing', + 'forest', + "forest's", + 'forested', + 'forester', + 'foresters', + 'forests', + 'forever', + 'forewarn', + 'forewarned', + 'forfeit', + 'forfeited', + 'forgave', + 'forge', + 'forged', + 'forger', + 'forget', + 'forget-me-not', + 'forgetful', + 'forgetive', + 'forgets', + 'forgettin', + "forgettin'", + 'forgetting', + 'forging', + 'forgive', + 'forgiven', + 'forgiveness', + 'forgiver', + 'forgives', + 'forgiving', + 'forgo', + 'forgot', + 'forgotten', + 'fork', + "fork's", + 'forks', + 'form', + 'forma', + 'formal', + 'formalities', + 'formality', + 'formally', + 'formals', + 'format', + 'formation', + 'formations', + 'formatted', + 'formatting', + 'formed', + 'former', + 'formerly', + 'formers', + 'formidable', + 'forming', + 'forms', + 'formula', + 'forsake', + 'forsaken', + 'forsworn', + 'fort', + "fort's", + 'forte', + 'forted', + 'forth', + 'forthcoming', + 'forthington', + 'forthwith', + 'fortification', + 'forting', + 'fortitude', + 'fortnight', + 'fortress', + 'fortresses', + 'forts', + 'fortuitous', + 'fortunate', + 'fortunately', + 'fortunates', + 'fortune', + "fortune's", + 'fortuned', + 'fortunes', + 'fortuneteller', + "fortuneteller's", + 'fortuning', + 'forty', + 'forum', + 'forums', + 'forward', + 'forwarded', + 'forwarder', + 'forwarders', + 'forwarding', + 'forwardly', + 'forwards', + 'fossil', + 'fossils', + 'foster', + 'fought', + 'foughted', + 'foughter', + 'foughters', + 'foughting', + 'foughts', + 'foul', + 'fouling', + 'fouls', + 'found', + 'foundation', + "foundation's", + 'foundations', + 'founded', + 'founder', + "founder's", + 'founders', + 'founding', + 'foundlings', + 'foundry', + 'founds', + 'fount', + 'fountain', + "fountain's", + 'fountains', + 'four', + 'fourth', + "fousto's", + 'fowl', + 'fowler', + 'fox', + 'foxed', + 'foxes', + 'foxglove', + 'foxtail', + 'foxtails', + 'foxtrot', + 'fozzie', + 'fps', + "fps's", + 'fraction', + 'fractions', + 'fractured', + 'fracturing', + 'fragaba', + 'fragermo', + 'fraggue', + 'fragile', + 'fragility', + 'fragilles', + 'fragmented', + 'fragnoe', + 'fragoso', + 'fragrance', + 'fraguilla', + 'fraid', + 'frail', + 'frailago', + 'frailano', + 'fraise', + 'frame', + 'framed', + 'framer', + "framer's", + 'framerate', + 'framers', + 'frames', + 'framework', + 'framing', + 'fran', + 'franc', + 'francais', + 'francaise', + 'france', + 'frances', + 'francesca', + 'franchise', + 'francis', + 'francisco', + "francisco's", + 'franciscos', + 'francois', + 'frank', + "frank's", + 'frankfurters', + 'frankie', + "frankie's", + 'frankies', + 'frankly', + 'franks', + "franky's", + 'franny', + "franny's", + 'frannys', + 'frantic', + 'frantically', + 'franticly', + 'franz', + 'frap', + 'frappe', + 'fraps', + 'fraser', + 'frat', + 'fraternal', + 'fraternities', + 'fraternity', + 'fraternize', + 'frau', + 'fray', + 'frayed', + 'freakier', + 'freakish', + 'freakishly', + 'freaks', + 'freaky', + 'freckles', + 'fred', + "fred's", + 'freddie', + "freddie's", + 'freddy', + "freddy's", + 'fredrica', + 'fredrick', + 'free', + 'free2play', + 'freebooter', + 'freebooters', + 'freed', + 'freedom', + "freedom's", + 'freedoms', + 'freefall', + 'freeing', + 'freelance', + 'freelancers', + 'freeload', + 'freeloader', + 'freeloaders', + 'freeloading', + 'freely', + 'freeman', + 'freemason', + 'freemasons', + 'freeness', + 'freer', + 'frees', + 'freest', + 'freestyle', + 'freeware', + 'freeway', + 'freeze', + "freeze's", + 'freezer', + "freezer's", + 'freezers', + 'freezes', + 'freezing', + 'freida', + 'freight', + 'freighter', + 'freights', + 'frenzy', + 'frequency', + 'frequent', + 'frequented', + 'frequenter', + "frequenter's", + 'frequenters', + 'frequenting', + 'frequently', + 'frequents', + 'fresh', + 'freshasa', + 'freshen', + 'freshener', + 'freshens', + 'fresher', + 'freshers', + 'freshest', + 'freshly', + 'freshman', + 'freshmen', + 'freshness', + 'fret', + "fret's", + 'fretless', + 'fretting', + 'freud', + 'frication', + 'friday', + "friday's", + 'fridays', + 'fridge', + 'fried', + 'friend', + "friend's", + 'friended', + 'friendlier', + 'friendly', + 'friends', + "friends'", + 'friendship', + "friendship's", + 'friendships', + 'frier', + 'fries', + 'friezeframe', + 'frigate', + "frigate's", + 'frigates', + 'fright', + 'frighten', + 'frightened', + 'frightening', + 'frightens', + 'frightful', + 'frights', + 'frigid', + 'frigs', + 'frill', + 'frills', + 'frilly', + 'fringe', + 'fringes', + 'frinkelbee', + 'frinkelberry', + 'frinkelblabber', + 'frinkelbocker', + 'frinkelboing', + 'frinkelboom', + 'frinkelbounce', + 'frinkelbouncer', + 'frinkelbrains', + 'frinkelbubble', + 'frinkelbumble', + 'frinkelbump', + 'frinkelbumper', + 'frinkelburger', + 'frinkelchomp', + 'frinkelcorn', + 'frinkelcrash', + 'frinkelcrumbs', + 'frinkelcrump', + 'frinkelcrunch', + 'frinkeldoodle', + 'frinkeldorf', + 'frinkelface', + 'frinkelfidget', + 'frinkelfink', + 'frinkelfish', + 'frinkelflap', + 'frinkelflapper', + 'frinkelflinger', + 'frinkelflip', + 'frinkelflipper', + 'frinkelfoot', + 'frinkelfuddy', + 'frinkelfussen', + 'frinkelgadget', + 'frinkelgargle', + 'frinkelgloop', + 'frinkelglop', + 'frinkelgoober', + 'frinkelgoose', + 'frinkelgrooven', + 'frinkelhoffer', + 'frinkelhopper', + 'frinkeljinks', + 'frinkelklunk', + 'frinkelknees', + 'frinkelmarble', + 'frinkelmash', + 'frinkelmonkey', + 'frinkelmooch', + 'frinkelmouth', + 'frinkelmuddle', + 'frinkelmuffin', + 'frinkelmush', + 'frinkelnerd', + 'frinkelnoodle', + 'frinkelnose', + 'frinkelnugget', + 'frinkelphew', + 'frinkelphooey', + 'frinkelpocket', + 'frinkelpoof', + 'frinkelpop', + 'frinkelpounce', + 'frinkelpow', + 'frinkelpretzel', + 'frinkelquack', + 'frinkelroni', + 'frinkelscooter', + 'frinkelscreech', + 'frinkelsmirk', + 'frinkelsnooker', + 'frinkelsnoop', + 'frinkelsnout', + 'frinkelsocks', + 'frinkelspeed', + 'frinkelspinner', + 'frinkelsplat', + 'frinkelsprinkles', + 'frinkelsticks', + 'frinkelstink', + 'frinkelswirl', + 'frinkelteeth', + 'frinkelthud', + 'frinkeltoes', + 'frinkelton', + 'frinkeltoon', + 'frinkeltooth', + 'frinkeltwist', + 'frinkelwhatsit', + 'frinkelwhip', + 'frinkelwig', + 'frinkelwoof', + 'frinkelzaner', + 'frinkelzap', + 'frinkelzapper', + 'frinkelzilla', + 'frinkelzoom', + 'frisbee', + 'frisbees', + 'frit', + 'frites', + 'fritos', + 'frits', + 'fritter', + 'fritters', + 'fritz', + 'frivolity', + 'frizz', + 'frizzle', + 'frizzles', + 'frizzy', + 'fro', + 'frock', + 'frocks', + 'froe', + 'frog', + "frog's", + 'frogg', + 'froggy', + 'frogs', + 'frolicking', + 'from', + 'frond', + 'front', + "front's", + 'fronted', + 'frontier', + 'frontierland', + "frontierland's", + 'fronting', + 'fronts', + 'frontwards', + 'froot', + 'frost', + 'frostbite', + 'frostbites', + 'frosted', + 'frosting', + 'frosts', + 'frosty', + "frosty's", + 'froth', + 'frown', + 'frowned', + 'frowning', + 'froze', + 'frozen', + 'frozenly', + 'frozenness', + 'frugal', + 'fruit', + 'fruitful', + 'fruitless', + 'fruitloop', + 'fruitloops', + 'fruits', + 'fruity', + 'frump', + 'frustrate', + 'frustrated', + 'frustrates', + 'frustratin', + 'frustrating', + 'frustration', + 'frustrations', + 'fry', + "fry's", + 'fryer', + 'fryin', + 'frying', + 'fuchsia', + 'fuego', + 'fuegos', + 'fuel', + 'fueling', + 'fuels', + 'fufalla', + 'fufallas', + 'fugitive', + 'fugitives', + 'fugitve', + 'fugue', + 'fulfill', + 'fulfilled', + 'fulfilling', + 'full', + 'full-length', + 'full-on', + 'full-trailer', + 'fuller', + "fuller's", + 'fullest', + 'fullly', + 'fullscreen', + 'fulltime', + 'fully', + 'fulvina', + 'fumble', + 'fumblebee', + 'fumbleberry', + 'fumbleblabber', + 'fumblebocker', + 'fumbleboing', + 'fumbleboom', + 'fumblebounce', + 'fumblebouncer', + 'fumblebrains', + 'fumblebubble', + 'fumblebumble', + 'fumblebump', + 'fumblebumper', + 'fumbleburger', + 'fumblechomp', + 'fumblecorn', + 'fumblecrash', + 'fumblecrumbs', + 'fumblecrump', + 'fumblecrunch', + 'fumbled', + 'fumbledoodle', + 'fumbledorf', + 'fumbleface', + 'fumblefidget', + 'fumblefink', + 'fumblefish', + 'fumbleflap', + 'fumbleflapper', + 'fumbleflinger', + 'fumbleflip', + 'fumbleflipper', + 'fumblefoot', + 'fumblefuddy', + 'fumblefussen', + 'fumblegadget', + 'fumblegargle', + 'fumblegloop', + 'fumbleglop', + 'fumblegoober', + 'fumblegoose', + 'fumblegrooven', + 'fumblehoffer', + 'fumblehopper', + 'fumblejinks', + 'fumbleklunk', + 'fumbleknees', + 'fumblemarble', + 'fumblemash', + 'fumblemonkey', + 'fumblemooch', + 'fumblemouth', + 'fumblemuddle', + 'fumblemuffin', + 'fumblemush', + 'fumblenerd', + 'fumblenoodle', + 'fumblenose', + 'fumblenugget', + 'fumblephew', + 'fumblephooey', + 'fumblepocket', + 'fumblepoof', + 'fumblepop', + 'fumblepounce', + 'fumblepow', + 'fumblepretzel', + 'fumblequack', + 'fumbleroni', + 'fumbles', + 'fumblescooter', + 'fumblescreech', + 'fumblesmirk', + 'fumblesnooker', + 'fumblesnoop', + 'fumblesnout', + 'fumblesocks', + 'fumblespeed', + 'fumblespinner', + 'fumblesplat', + 'fumblesprinkles', + 'fumblesticks', + 'fumblestink', + 'fumbleswirl', + 'fumbleteeth', + 'fumblethud', + 'fumbletoes', + 'fumbleton', + 'fumbletoon', + 'fumbletooth', + 'fumbletwist', + 'fumblewhatsit', + 'fumblewhip', + 'fumblewig', + 'fumblewoof', + 'fumblezaner', + 'fumblezap', + 'fumblezapper', + 'fumblezilla', + 'fumblezoom', + 'fumbling', + 'fume', + 'fumed', + 'fumes', + 'fumigate', + 'fuming', + 'fun', + 'fun-filled', + 'fun-loving', + 'fun-palooza', + 'funcovers', + 'function', + "function's", + 'functional', + 'functioned', + 'functioning', + 'functions', + 'fund', + 'fundamental', + 'fundamentally', + 'funded', + 'funder', + 'funders', + 'funding', + 'fundraiser', + 'fundraising', + 'funds', + 'funerals', + 'funfest', + 'fungi', + 'fungus', + 'funhouse', + 'funkiest', + 'funky', + 'funland', + 'funload', + 'funn-ee', + 'funnel', + 'funnier', + 'funnies', + 'funniest', + "funnin'", + 'funning', + 'funny', + 'funnybee', + 'funnyberry', + 'funnyblabber', + 'funnybocker', + 'funnyboing', + 'funnyboom', + 'funnybounce', + 'funnybouncer', + 'funnybrains', + 'funnybubble', + 'funnybumble', + 'funnybump', + 'funnybumper', + 'funnyburger', + 'funnychomp', + 'funnycorn', + 'funnycrash', + 'funnycrumbs', + 'funnycrump', + 'funnycrunch', + 'funnydoodle', + 'funnydorf', + 'funnyface', + 'funnyfidget', + 'funnyfink', + 'funnyfish', + 'funnyflap', + 'funnyflapper', + 'funnyflinger', + 'funnyflip', + 'funnyflipper', + 'funnyfoot', + 'funnyfuddy', + 'funnyfussen', + 'funnygadget', + 'funnygargle', + 'funnygloop', + 'funnyglop', + 'funnygoober', + 'funnygoose', + 'funnygrooven', + 'funnyhoffer', + 'funnyhopper', + 'funnyjinks', + 'funnyklunk', + 'funnyknees', + 'funnymarble', + 'funnymash', + 'funnymonkey', + 'funnymooch', + 'funnymouth', + 'funnymuddle', + 'funnymuffin', + 'funnymush', + 'funnynerd', + 'funnynoodle', + 'funnynose', + 'funnynugget', + 'funnyphew', + 'funnyphooey', + 'funnypocket', + 'funnypoof', + 'funnypop', + 'funnypounce', + 'funnypow', + 'funnypretzel', + 'funnyquack', + 'funnyroni', + 'funnyscooter', + 'funnyscreech', + 'funnysmirk', + 'funnysnooker', + 'funnysnoop', + 'funnysnout', + 'funnysocks', + 'funnyspeed', + 'funnyspinner', + 'funnysplat', + 'funnysprinkles', + 'funnysticks', + 'funnystink', + 'funnyswirl', + 'funnyteeth', + 'funnythud', + 'funnytoes', + 'funnyton', + 'funnytoon', + 'funnytooth', + 'funnytwist', + 'funnywhatsit', + 'funnywhip', + 'funnywig', + 'funnywoof', + 'funnyzaner', + 'funnyzap', + 'funnyzapper', + 'funnyzilla', + 'funnyzoom', + 'funs', + 'funscape', + 'funstuff', + 'funtime', + 'funzone', + 'fur', + 'furball', + 'furbud', + 'furious', + 'furiously', + 'furnace', + 'furnish', + 'furnished', + 'furnisher', + "furnisher's", + 'furnishers', + 'furnishes', + 'furnishing', + "furnishing's", + 'furnishings', + 'furniture', + 'furrowing', + 'furrows', + 'furter', + 'further', + 'furthered', + 'furtherer', + 'furtherest', + 'furthering', + 'furthers', + 'furthest', + 'fury', + 'furys', + 'fuse', + 'fused', + 'fuses', + 'fusil', + 'fusion', + 'fuss', + 'fussed', + 'fussing', + 'fussy', + 'futile', + 'future', + "future's", + 'futures', + 'futuristic', + 'futz', + 'fuzz', + 'fuzzy', + 'fuzzybee', + 'fuzzyberry', + 'fuzzyblabber', + 'fuzzybocker', + 'fuzzyboing', + 'fuzzyboom', + 'fuzzybounce', + 'fuzzybouncer', + 'fuzzybrains', + 'fuzzybubble', + 'fuzzybumble', + 'fuzzybump', + 'fuzzybumper', + 'fuzzyburger', + 'fuzzychomp', + 'fuzzycorn', + 'fuzzycrash', + 'fuzzycrumbs', + 'fuzzycrump', + 'fuzzycrunch', + 'fuzzydoodle', + 'fuzzydorf', + 'fuzzyface', + 'fuzzyfidget', + 'fuzzyfink', + 'fuzzyfish', + 'fuzzyflap', + 'fuzzyflapper', + 'fuzzyflinger', + 'fuzzyflip', + 'fuzzyflipper', + 'fuzzyfoot', + 'fuzzyfuddy', + 'fuzzyfussen', + 'fuzzygadget', + 'fuzzygargle', + 'fuzzygloop', + 'fuzzyglop', + 'fuzzygoober', + 'fuzzygoose', + 'fuzzygrooven', + 'fuzzyhoffer', + 'fuzzyhopper', + 'fuzzyjinks', + 'fuzzyklunk', + 'fuzzyknees', + 'fuzzymarble', + 'fuzzymash', + 'fuzzymonkey', + 'fuzzymooch', + 'fuzzymouth', + 'fuzzymuddle', + 'fuzzymuffin', + 'fuzzymush', + 'fuzzynerd', + 'fuzzynoodle', + 'fuzzynose', + 'fuzzynugget', + 'fuzzyphew', + 'fuzzyphooey', + 'fuzzypocket', + 'fuzzypoof', + 'fuzzypop', + 'fuzzypounce', + 'fuzzypow', + 'fuzzypretzel', + 'fuzzyquack', + 'fuzzyroni', + 'fuzzyscooter', + 'fuzzyscreech', + 'fuzzysmirk', + 'fuzzysnooker', + 'fuzzysnoop', + 'fuzzysnout', + 'fuzzysocks', + 'fuzzyspeed', + 'fuzzyspinner', + 'fuzzysplat', + 'fuzzysprinkles', + 'fuzzysticks', + 'fuzzystink', + 'fuzzyswirl', + 'fuzzyteeth', + 'fuzzythud', + 'fuzzytoes', + 'fuzzyton', + 'fuzzytoon', + 'fuzzytooth', + 'fuzzytwist', + 'fuzzywhatsit', + 'fuzzywhip', + 'fuzzywig', + 'fuzzywoof', + 'fuzzyzaner', + 'fuzzyzap', + 'fuzzyzapper', + 'fuzzyzilla', + 'fuzzyzoom', + 'fyi', + "g'bye", + "g'day", + "g'luck", + "g'night", + "g'nite", + "g'way", + 'g2g', + 'g2get', + 'g2go', + 'g2store', + 'g2take', + 'gab', + 'gabber', + 'gabbing', + 'gabble', + 'gabby', + 'gabe', + 'gabriella', + "gabriella's", + 'gabs', + 'gad', + 'gadget', + "gadget's", + 'gadgets', + 'gaelins', + 'gaff', + 'gaffing', + 'gaffs', + 'gag', + 'gaga', + 'gage', + 'gaggle', + 'gagless', + 'gagong', + 'gags', + 'gain', + 'gained', + 'gainer', + 'gainers', + 'gaining', + 'gainings', + 'gainly', + 'gains', + 'gainst', + 'gaits', + 'gal', + "gal's", + 'gala', + 'galaacare', + 'galaana', + 'galactic', + 'galagris', + 'galagua', + 'galaigos', + 'galaira', + 'galajeres', + 'galanoe', + 'galaros', + 'galaxies', + 'galaxy', + "galaxy's", + 'gale', + 'galeon', + 'gall', + "gall's", + 'gallant', + 'gallants', + 'gallbladder', + 'galleon', + 'galleons', + 'galleria', + 'galleries', + 'gallery', + 'galley', + 'galleys', + 'gallions', + 'gallium', + 'gallon', + 'gallons', + 'galloon', + 'galloons', + 'galloping', + 'gallow', + "gallow's", + 'gallows', + 'galls', + 'galoot', + 'galore', + 'galosh', + 'galoshes', + 'gals', + 'gambit', + 'game', + "game's", + 'game-face', + 'gamecards', + 'gamecrashes', + 'gamecube', + 'gamed', + 'gameface', + "gamekeeper's", + 'gamekeepers', + 'gamely', + 'gamemaster', + 'gamemasters', + 'gameplan', + 'gameplay', + 'gamer', + 'gamerfriend', + 'gamergirl', + 'gamermaniac', + 'gamers', + 'gamertag', + 'gamerz', + 'games', + 'gamesboy', + 'gameserver', + 'gamesite', + 'gamestop', + 'gamestyle', + 'gamesurge', + 'gametap', + 'gamin', + 'gaming', + 'gamma', + 'gamming', + 'gamzee', + 'gander', + 'gandolf', + 'ganga', + 'gange', + 'gangley', + 'gangly', + 'gangplanks', + 'gank', + 'ganked', + 'gankers', + 'ganking', + 'gantu', + 'gap', + 'gaps', + 'garage', + "garage's", + 'garaged', + 'garages', + 'garaging', + 'garbage', + 'garbahj', + 'garcia', + 'garcon', + 'garden', + "garden's", + 'garden-talent', + 'gardened', + 'gardener', + "gardener's", + 'gardeners', + 'gardenia', + 'gardening', + 'gardens', + 'gardien', + 'gargantuan', + 'garget', + 'gargoyle', + "gargoyle's", + 'gargoyles', + 'garibay-immobilitay', + 'garland', + 'garlic', + 'garment', + 'garner', + 'garners', + 'garnet', + 'garret', + 'garrett', + "garrett's", + 'garrison', + 'garry', + "garry's", + 'garter', + 'garters', + 'gary', + "gary's", + 'gasket', + 'gasoline', + 'gasp', + 'gasped', + 'gasps', + 'gaston', + "gaston's", + 'gastons', + 'gate', + "gate's", + 'gatecrash', + 'gatecrashers', + 'gated', + 'gatekeeper', + 'gates', + 'gateway', + 'gateways', + 'gather', + 'gathered', + 'gatherer', + "gatherer's", + 'gatherers', + 'gatherin', + "gatherin'", + 'gathering', + 'gatherings', + 'gathers', + 'gating', + 'gatling', + 'gator', + "gator's", + 'gatorade', + 'gators', + 'gauche', + 'gauge', + 'gaunt', + 'gauntlet', + 'gauze', + 'gave', + 'gavel', + 'gawk', + 'gawrsh', + 'gaze', + 'gazebo', + 'gazelle', + 'gazillion', + 'gazing', + 'gba', + 'gc', + 'gear', + 'geared', + 'gearing', + 'gearloose', + 'gears', + 'gee', + 'geek', + "geek's", + 'geeks', + 'geez', + 'geezer', + 'geezers', + "geffory's", + 'gejigage', + 'gejigen', + 'gejio', + 'gekikro', + 'gekikuri', + 'gekimugon', + 'gelatin', + 'gelberus', + 'geld', + 'gem', + "gem's", + 'gems', + 'gemstone', + "gemstone's", + 'gemstones', + 'gen', + 'gender', + 'genders', + 'general', + "general's", + 'generalize', + 'generally', + 'generals', + 'generate', + 'generated', + 'generates', + 'generating', + 'generation', + 'generational', + 'generations', + 'generative', + "generator's", + 'generators', + 'generic', + 'genericly', + 'generous', + 'generously', + 'genes', + 'genesis', + 'genetic', + 'genetics', + 'genial', + 'genie', + "genie's", + 'genies', + 'genius', + 'geniuses', + 'genre', + 'genres', + 'gens', + 'genshi', + 'gent', + "gent's", + 'gentle', + 'gentlefolk', + 'gentleman', + "gentleman's", + 'gentlemanlike', + 'gentlemanly', + 'gentlemen', + "gentlemen's", + 'gently', + 'gentry', + "gentry's", + 'gents', + 'genuine', + 'genuinely', + 'genus', + 'geo', + 'geoffrey', + 'geography', + 'geology', + 'geometry', + 'george', + "george's", + 'georges', + 'geos', + 'gepetto', + "gepetto's", + 'gepettos', + 'geranium', + 'gerard', + 'gerbil', + 'germ', + 'german', + 'germany', + 'germs', + 'germy', + 'gertrude', + 'gesture', + 'gestures', + 'gesturing', + 'get', + "get'cha", + "get's", + 'get-cha', + 'getaway', + 'getaways', + 'gets', + 'gettable', + 'getter', + 'getting', + 'geyser', + 'geysers', + 'gf', + 'gfx', + 'gg', + 'gguuiilldd', + 'ghastly', + 'ghede', + 'ghost', + "ghost's", + 'ghostbusters', + 'ghosted', + 'ghostly', + 'ghosts', + 'ghostwriter', + 'ghosty', + 'ghoul', + 'ghouls', + 'ghoxt', + 'gi-normous', + 'gianna', + 'giant', + "giant's", + 'giants', + 'gibber', + 'gibberish', + 'gibbet', + 'gibbons', + 'gibbous', + 'gibbs', + 'gibby', + 'gibe', + 'gibes', + 'gibing', + 'giddy', + 'gif', + 'gift', + "gift's", + 'gifted', + 'gifts', + 'giftshop', + 'giftwrapped', + 'gig', + 'gigantic', + 'giggle', + 'gigglebee', + 'giggleberry', + 'giggleblabber', + 'gigglebocker', + 'giggleboing', + 'giggleboom', + 'gigglebounce', + 'gigglebouncer', + 'gigglebrains', + 'gigglebubble', + 'gigglebumble', + 'gigglebump', + 'gigglebumper', + 'giggleburger', + 'gigglechomp', + 'gigglecorn', + 'gigglecrash', + 'gigglecrumbs', + 'gigglecrump', + 'gigglecrunch', + 'giggled', + 'giggledoodle', + 'giggledorf', + 'giggleface', + 'gigglefidget', + 'gigglefink', + 'gigglefish', + 'giggleflap', + 'giggleflapper', + 'giggleflinger', + 'giggleflip', + 'giggleflipper', + 'gigglefoot', + 'gigglefuddy', + 'gigglefussen', + 'gigglegadget', + 'gigglegargle', + 'gigglegloop', + 'giggleglop', + 'gigglegoober', + 'gigglegoose', + 'gigglegrooven', + 'gigglehoffer', + 'gigglehopper', + 'gigglejinks', + 'giggleklunk', + 'giggleknees', + 'gigglemarble', + 'gigglemash', + 'gigglemonkey', + 'gigglemooch', + 'gigglemouth', + 'gigglemuddle', + 'gigglemuffin', + 'gigglemush', + 'gigglenerd', + 'gigglenoodle', + 'gigglenose', + 'gigglenugget', + 'gigglephew', + 'gigglephooey', + 'gigglepocket', + 'gigglepoof', + 'gigglepop', + 'gigglepounce', + 'gigglepow', + 'gigglepretzel', + 'gigglequack', + 'giggleroni', + 'giggles', + 'gigglescooter', + 'gigglescreech', + 'gigglesmirk', + 'gigglesnooker', + 'gigglesnoop', + 'gigglesnout', + 'gigglesocks', + 'gigglespeed', + 'gigglespinner', + 'gigglesplat', + 'gigglesprinkles', + 'gigglesticks', + 'gigglestink', + 'giggleswirl', + 'giggleteeth', + 'gigglethud', + 'giggletoes', + 'giggleton', + 'giggletoon', + 'giggletooth', + 'giggletwist', + 'gigglewhatsit', + 'gigglewhip', + 'gigglewig', + 'gigglewoof', + 'gigglezaner', + 'gigglezap', + 'gigglezapper', + 'gigglezilla', + 'gigglezoom', + 'gigglin', + 'giggling', + 'giggly', + 'gigi', + "gigi's", + 'gigis', + 'gigs', + 'gila', + 'giladoga', + 'gilbert', + 'gilded', + 'gill', + 'gilled', + 'gills', + 'gimme', + 'gimmie', + 'ginger', + 'gingerbread', + 'ginkgo', + 'ginny', + 'ginty', + 'giorna', + 'giraff-o-dil', + 'giraffe', + "giraffe's", + 'giraffes', + 'girdle', + 'girl', + "girl's", + 'girl-next-door', + 'girlfriend', + 'girlie', + 'girls', + "girls'", + 'gist', + 'giuld', + 'giulia', + "giulia's", + 'giulias', + 'giulio', + "giulio's", + 'giulios', + 'give', + 'given', + 'giver', + "giver's", + 'givers', + 'gives', + 'giveth', + 'giving', + 'gizmo', + 'gizmos', + 'gizzard', + 'gizzards', + 'gl', + 'glace', + 'glacia', + 'glacier', + 'glad', + 'glade', + 'gladiator', + 'gladly', + 'gladness', + 'glados', + 'glam', + 'glamorous', + 'glance', + 'glanced', + 'glances', + 'glancing', + 'glare', + 'glared', + 'glares', + 'glaring', + 'glass', + "glass's", + 'glassed', + 'glasses', + "glasses'", + 'glasswater', + 'glaze', + 'glazed', + 'glazing', + 'gleam', + 'gleaming', + 'glee', + 'glen', + 'glenstorm', + 'glider', + 'glimmer', + 'glimmering', + 'glimpse', + 'glint', + 'glints', + 'glitch', + 'glitched', + 'glitches', + 'glitching', + 'glitchy', + 'glitter', + 'glitterbee', + 'glitterberry', + 'glitterblabber', + 'glitterbocker', + 'glitterboing', + 'glitterboom', + 'glitterbounce', + 'glitterbouncer', + 'glitterbrains', + 'glitterbubble', + 'glitterbumble', + 'glitterbump', + 'glitterbumper', + 'glitterburger', + 'glitterchomp', + 'glittercorn', + 'glittercrash', + 'glittercrumbs', + 'glittercrump', + 'glittercrunch', + 'glitterdoodle', + 'glitterdorf', + 'glittered', + 'glitterface', + 'glitterfidget', + 'glitterfink', + 'glitterfish', + 'glitterflap', + 'glitterflapper', + 'glitterflinger', + 'glitterflip', + 'glitterflipper', + 'glitterfoot', + 'glitterfuddy', + 'glitterfussen', + 'glittergadget', + 'glittergargle', + 'glittergloop', + 'glitterglop', + 'glittergoober', + 'glittergoose', + 'glittergrooven', + 'glitterhoffer', + 'glitterhopper', + 'glittering', + 'glitterjinks', + 'glitterklunk', + 'glitterknees', + 'glittermarble', + 'glittermash', + 'glittermonkey', + 'glittermooch', + 'glittermouth', + 'glittermuddle', + 'glittermuffin', + 'glittermush', + 'glitternerd', + 'glitternoodle', + 'glitternose', + 'glitternugget', + 'glitterphew', + 'glitterphooey', + 'glitterpocket', + 'glitterpoof', + 'glitterpop', + 'glitterpounce', + 'glitterpow', + 'glitterpretzel', + 'glitterquack', + 'glitterroni', + 'glitterscooter', + 'glitterscreech', + 'glittersmirk', + 'glittersnooker', + 'glittersnoop', + 'glittersnout', + 'glittersocks', + 'glitterspeed', + 'glitterspinner', + 'glittersplat', + 'glittersprinkles', + 'glittersticks', + 'glitterstink', + 'glitterswirl', + 'glitterteeth', + 'glitterthud', + 'glittertoes', + 'glitterton', + 'glittertoon', + 'glittertooth', + 'glittertwist', + 'glitterwhatsit', + 'glitterwhip', + 'glitterwig', + 'glitterwoof', + 'glittery', + 'glitterzaner', + 'glitterzap', + 'glitterzapper', + 'glitterzilla', + 'glitterzoom', + 'glitzy', + 'gloat', + 'gloating', + 'glob', + 'global', + 'globals', + 'globe', + "globe's", + 'globes', + 'glogg', + 'gloom', + 'gloomy', + 'gloria', + 'glorified', + 'gloriosa', + 'glorious', + 'gloriously', + 'gloriouspcmasterrace', + 'glory', + 'gloss', + 'glossy', + 'glove', + 'gloved', + 'glover', + "glover's", + 'glovers', + 'gloves', + 'glovey', + 'gloving', + 'glow', + 'glowed', + 'glower', + 'glowie', + 'glowies', + 'glowing', + 'glows', + 'glowworm', + 'glowworms', + 'glozelle', + "glozelle's", + 'glozelles', + 'glubglub', + 'glucose', + 'glue', + 'glued', + 'glues', + 'gluey', + 'glug', + 'glugging', + 'gluing', + 'glum', + 'glumness', + 'gluten', + 'glutton', + 'gm', + "gm's", + 'gman', + 'gmta', + 'gnarly', + 'gnasher', + 'gnat', + 'gnats', + 'gnawing', + 'gnaws', + 'gnight', + 'gnite', + 'gnome', + 'gnomes', + 'gnu', + 'gnus', + 'go', + 'go-getter', + 'go2g', + 'goal', + "goal's", + 'goalie', + 'goals', + 'goat', + "goat's", + 'goatee', + 'goats', + 'gob', + 'goblet', + 'goblin', + 'goblins', + 'gobs', + 'goby', + 'goes', + 'goggle', + 'goggles', + 'goin', + "goin'", + 'going', + 'goings', + 'gokazoa', + 'gold', + 'golden', + 'goldenly', + 'goldenrod', + 'goldenseal', + 'goldfarmers', + 'goldfarming', + 'golding', + 'goldmine', + 'golds', + 'golem', + 'golf', + 'golfed', + 'golfing', + 'golfs', + 'goliath', + 'goliaths', + 'golly', + 'gona', + 'gone', + 'goner', + 'goners', + 'gong', + 'gonna', + 'gonzalo', + 'gonzo', + 'goob', + 'goober', + 'goobers', + 'good', + 'good-day', + 'good-hearted', + 'goodbye', + "goodbye's", + 'goodbyes', + 'goodfellas', + 'goodie', + 'goodies', + 'goodly', + 'goodness', + 'goodnight', + 'goodnights', + 'goodprice', + 'goods', + 'goodwill', + 'goof', + 'goofball', + 'goofballs', + 'goofed', + 'goofing', + 'goofy', + "goofy's", + 'goofywrench', + 'google', + 'googlebee', + 'googleberry', + 'googleblabber', + 'googlebocker', + 'googleboing', + 'googleboom', + 'googlebounce', + 'googlebouncer', + 'googlebrains', + 'googlebubble', + 'googlebumble', + 'googlebump', + 'googlebumper', + 'googleburger', + 'googlechomp', + 'googlecorn', + 'googlecrash', + 'googlecrumbs', + 'googlecrump', + 'googlecrunch', + 'googledoodle', + 'googledorf', + 'googleface', + 'googlefidget', + 'googlefink', + 'googlefish', + 'googleflap', + 'googleflapper', + 'googleflinger', + 'googleflip', + 'googleflipper', + 'googlefoot', + 'googlefuddy', + 'googlefussen', + 'googlegadget', + 'googlegargle', + 'googlegloop', + 'googleglop', + 'googlegoober', + 'googlegoose', + 'googlegrooven', + 'googlehoffer', + 'googlehopper', + 'googlejinks', + 'googleklunk', + 'googleknees', + 'googlemarble', + 'googlemash', + 'googlemonkey', + 'googlemooch', + 'googlemouth', + 'googlemuddle', + 'googlemuffin', + 'googlemush', + 'googlenerd', + 'googlenoodle', + 'googlenose', + 'googlenugget', + 'googlephew', + 'googlephooey', + 'googlepocket', + 'googlepoof', + 'googlepop', + 'googlepounce', + 'googlepow', + 'googlepretzel', + 'googlequack', + 'googleroni', + 'googlescooter', + 'googlescreech', + 'googlesmirk', + 'googlesnooker', + 'googlesnoop', + 'googlesnout', + 'googlesocks', + 'googlespeed', + 'googlespinner', + 'googlesplat', + 'googlesprinkles', + 'googlesticks', + 'googlestink', + 'googleswirl', + 'googleteeth', + 'googlethud', + 'googletoes', + 'googleton', + 'googletoon', + 'googletooth', + 'googletwist', + 'googlewhatsit', + 'googlewhip', + 'googlewig', + 'googlewoof', + 'googlezaner', + 'googlezap', + 'googlezapper', + 'googlezilla', + 'googlezoom', + 'goon', + "goon's", + 'goonies', + 'goons', + 'goonsquad', + 'goopy', + 'goose', + 'gooseberry', + 'goosebumps', + 'gooseburger', + 'goosed', + 'gooses', + 'goosing', + 'gopher', + "gopher's", + 'gophers', + 'gordo', + "gordo's", + 'gordon', + 'gorge', + "gorge's", + 'gorgeous', + 'gorges', + 'gorgon', + 'gorgong', + 'gorilla', + "gorilla's", + 'gorillas', + 'gorp', + 'gosh', + 'goslin', + 'gospel', + 'gospels', + 'gossip', + 'gossiping', + 'gossips', + 'got', + "got'm", + 'gotcha', + 'gotes', + 'goth', + 'gothic', + 'gotta', + 'gotten', + 'gouge', + 'gouged', + 'gound', + 'gourd', + 'gourds', + 'gourmet', + 'gourmets', + "gourtmet's", + 'gout', + 'gov', + "gov'na", + "gov'ner", + "gov'ners", + 'govern', + 'governator', + 'governess', + 'government', + "government's", + 'governmental', + 'governments', + 'governor', + "governor's", + 'governors', + 'governs', + 'goway', + 'gown', + 'gowns', + 'gr8', + 'grab', + 'grabbed', + 'grabber', + "grabbin'", + 'grabbing', + 'grabble', + 'grabbles', + 'grabbling', + 'grabeel', + 'grabing', + 'grabs', + 'grace', + "grace's", + 'graced', + 'graceful', + 'gracefully', + 'graces', + "graces'", + 'gracias', + 'gracie', + "gracie's", + 'gracing', + 'gracious', + 'graciously', + 'grad', + 'grade', + 'graded', + 'grader', + 'graders', + 'grades', + 'gradient', + 'grading', + 'grads', + 'gradual', + 'gradually', + 'graduate', + 'graduated', + 'graduation', + 'grafts', + 'graham', + 'grahams', + 'grail', + 'grain', + 'grains', + 'grammar', + 'grammars', + 'grammas', + 'grammatical', + 'grammy', + 'grammys', + 'grampa', + 'grampy', + 'grams', + 'grand', + 'grandbabies', + 'grandbaby', + 'grandchildren', + 'granddaughter', + 'grande', + 'grander', + 'grandest', + 'grandfather', + "grandfather's", + 'grandfathers', + 'grandiose', + 'grandkid', + 'grandkids', + 'grandly', + 'grandma', + "grandma's", + 'grandmas', + 'grandmother', + 'grandmothers', + 'grandpa', + 'grandpappy', + 'grandparent', + 'grandparents', + 'grandpas', + 'grandpop', + 'grands', + 'grandson', + 'grannies', + 'granny', + 'granola', + 'grant', + "grant's", + 'granted', + 'granter', + 'granting', + 'grants', + 'grapefruits', + 'grapeshot', + 'graphic', + 'graphics', + 'graphics..', + 'graphs', + 'grapple', + 'grappled', + 'grappler', + 'grapplers', + 'grapples', + 'grappling', + 'grasps', + 'grass', + 'grass-weaving', + 'grasses', + 'grasshopper', + "grasshopper's", + 'grasshoppers', + 'grassy', + 'grate', + 'grated', + 'grateful', + 'gratefully', + 'gratefulness', + 'grater', + 'graters', + 'grates', + 'gratifying', + 'gratin', + 'gratis', + 'gratitude', + 'grats', + 'gratz', + 'gravel', + 'gravely', + 'graven', + 'gravepeople', + 'graveryard', + 'graves', + "graves'", + 'gravest', + 'graveyard', + 'graveyards', + 'gravitate', + 'gravity', + 'gravy', + 'gray', + 'grayed', + 'grayish', + 'grays', + 'graze', + 'grazed', + 'grazing', + 'grease', + 'greased', + 'greaser', + 'greases', + 'greasier', + 'greasy', + 'great', + 'greaten', + 'greater', + 'greatest', + 'greatly', + 'greatness', + 'greats', + 'greave', + 'greece', + 'greed', + 'greediest', + 'greedy', + 'greek', + 'green', + 'greened', + 'greener', + 'greeners', + 'greenery', + 'greenethumb', + 'greenhorns', + 'greenhouse', + 'greenie', + 'greening', + 'greenish', + 'greenly', + 'greer', + 'greet', + 'greeted', + 'greeter', + "greeter's", + 'greeting', + 'greetings', + 'greets', + 'greg', + 'gregoire', + "gregoire's", + 'gregoires', + 'gremlin', + 'gremlins', + 'grew', + 'grey', + 'greybeard', + 'greyhound', + 'greyhounds', + 'grid', + 'griddle', + 'grief', + "grief's", + 'griefs', + 'grieve', + 'grieved', + 'griever', + "griever's", + 'grievers', + 'grieves', + 'grieving', + 'grievous', + 'griffin', + 'grilda', + 'grilden', + 'grildragos', + 'grill', + 'grilled', + 'grilling', + 'grills', + 'grim', + 'grimsditch', + 'grimy', + 'grin', + 'grinch', + 'grinded', + 'grinder', + 'grinders', + 'grining', + 'grinned', + 'grinning', + 'grins', + 'grintley', + 'grip', + 'gripe', + 'griper', + 'gripes', + 'griping', + 'gripper', + 'gripping', + 'grips', + 'grisly', + 'gristly', + 'grit', + 'grits', + 'gritty', + 'grizzle', + 'grizzly', + "grizzly's", + 'groceries', + 'grocery', + "grocery's", + 'grog', + "grog's", + 'grogginess', + 'groggy', + 'groggybeards', + 'grogs', + 'grommet', + 'gronos', + 'groom', + 'groove', + 'grooved', + 'groover', + 'grooves', + 'grooving', + 'groovy', + 'gross', + 'grossed', + 'grosser', + 'grosses', + 'grossest', + 'grossing', + 'grossly', + 'grossness', + 'grotesque', + 'grotesquely', + 'grotto', + 'grouch', + 'groucho', + 'grouchy', + 'ground', + 'ground-up', + 'grounded', + 'grounder', + "grounder's", + 'grounders', + 'groundhog', + 'grounding', + 'grounds', + 'groundskeeper', + 'group', + 'grouped', + 'grouper', + 'groupie', + 'groupies', + 'grouping', + 'groupleader', + 'groupmanager', + 'groups', + 'grousing', + 'grouting', + 'grove', + 'groves', + 'grow', + 'grower', + 'growin', + "growin'", + 'growing', + 'growl', + 'growl-licous', + 'growling', + 'growls', + 'grown', + 'grown-up', + 'grownups', + 'grows', + 'growth', + 'grr', + 'grrr', + 'grrrrrrrl', + 'gru', + 'grub', + 'grubb', + 'grubbing', + 'grubby', + 'grubs', + 'grudge', + 'grudger', + 'grudges', + 'gruel', + 'grueling', + 'gruesome', + 'gruff', + 'gruffly', + 'grumble', + 'grumblebee', + 'grumbleberry', + 'grumbleblabber', + 'grumblebocker', + 'grumbleboing', + 'grumbleboom', + 'grumblebounce', + 'grumblebouncer', + 'grumblebrains', + 'grumblebubble', + 'grumblebumble', + 'grumblebump', + 'grumblebumper', + 'grumbleburger', + 'grumblechomp', + 'grumblecorn', + 'grumblecrash', + 'grumblecrumbs', + 'grumblecrump', + 'grumblecrunch', + 'grumbledoodle', + 'grumbledorf', + 'grumbleface', + 'grumblefidget', + 'grumblefink', + 'grumblefish', + 'grumbleflap', + 'grumbleflapper', + 'grumbleflinger', + 'grumbleflip', + 'grumbleflipper', + 'grumblefoot', + 'grumblefuddy', + 'grumblefussen', + 'grumblegadget', + 'grumblegargle', + 'grumblegloop', + 'grumbleglop', + 'grumblegoober', + 'grumblegoose', + 'grumblegrooven', + 'grumblehoffer', + 'grumblehopper', + 'grumblejinks', + 'grumbleklunk', + 'grumbleknees', + 'grumblemarble', + 'grumblemash', + 'grumblemonkey', + 'grumblemooch', + 'grumblemouth', + 'grumblemuddle', + 'grumblemuffin', + 'grumblemush', + 'grumblenerd', + 'grumblenoodle', + 'grumblenose', + 'grumblenugget', + 'grumblephew', + 'grumblephooey', + 'grumblepocket', + 'grumblepoof', + 'grumblepop', + 'grumblepounce', + 'grumblepow', + 'grumblepretzel', + 'grumblequack', + 'grumbleroni', + 'grumbles', + 'grumblescooter', + 'grumblescreech', + 'grumblesmirk', + 'grumblesnooker', + 'grumblesnoop', + 'grumblesnout', + 'grumblesocks', + 'grumblespeed', + 'grumblespinner', + 'grumblesplat', + 'grumblesprinkles', + 'grumblesticks', + 'grumblestink', + 'grumbleswirl', + 'grumbleteeth', + 'grumblethud', + 'grumbletoes', + 'grumbleton', + 'grumbletoon', + 'grumbletooth', + 'grumbletwist', + 'grumblewhatsit', + 'grumblewhip', + 'grumblewig', + 'grumblewoof', + 'grumblezaner', + 'grumblezap', + 'grumblezapper', + 'grumblezilla', + 'grumblezoom', + 'grumbling', + 'grumly', + 'grummet', + 'grumpy', + "grumpy's", + 'grundy', + 'grunge', + 'grungy', + 'grunt', + "grunt's", + 'gruntbusters', + 'grunter', + 'grunting', + 'grunts', + "grunts'", + 'gryphons', + 'gsw', + 'gtg', + 'guacamole', + 'guano', + 'guarantee', + 'guaranteed', + 'guarantees', + "guarantees'", + 'guard', + "guard's", + 'guarded', + 'guarder', + 'guardian', + 'guardians', + 'guarding', + 'guards', + "guards'", + 'guardsman', + 'guardsmen', + 'guess', + 'guessed', + 'guesser', + 'guessers', + 'guesses', + 'guessin', + "guessin'", + 'guessing', + 'guesstimate', + 'guest', + "guest's", + 'guestbook', + 'guested', + 'guesting', + 'guests', + 'guff', + 'guffaw', + 'gui', + 'guide', + "guide's", + 'guided', + 'guideline', + 'guidelines', + 'guidemap', + "guidemap's", + 'guider', + 'guides', + 'guiding', + 'guil', + 'guila', + 'guild', + 'guilded', + 'guilders', + 'guildhall', + 'guildhouse', + 'guildie', + 'guildies', + 'guilding', + 'guildleader', + 'guildless', + 'guildmasta', + 'guildmaster', + "guildmaster's", + 'guildmasters', + 'guildmate', + 'guildmates', + 'guildmateys', + 'guildmeister', + 'guildmember', + 'guildmembers', + 'guildname', + 'guildpirates', + 'guilds', + 'guildship', + 'guildships', + 'guildsmen', + 'guildtag', + 'guildtalk', + 'guildwars', + 'guildwise', + 'guildy', + "guildy's", + 'guildys', + 'guile', + 'guilld', + 'guilt', + 'guilty', + 'guinea', + 'guines', + 'guise', + 'guised', + 'guitar', + "guitar's", + 'guitarist', + 'guitars', + 'gulf', + "gulf's", + 'gulfs', + 'gull', + 'gullet', + 'gullible', + 'gulls', + 'gullwings', + 'gully', + 'gulp', + 'gulped', + 'gulps', + 'gum', + "gum's", + 'gumball', + 'gumballs', + 'gumbo', + 'gumby', + 'gumdropbee', + 'gumdropberry', + 'gumdropblabber', + 'gumdropbocker', + 'gumdropboing', + 'gumdropboom', + 'gumdropbounce', + 'gumdropbouncer', + 'gumdropbrains', + 'gumdropbubble', + 'gumdropbumble', + 'gumdropbump', + 'gumdropbumper', + 'gumdropburger', + 'gumdropchomp', + 'gumdropcorn', + 'gumdropcrash', + 'gumdropcrumbs', + 'gumdropcrump', + 'gumdropcrunch', + 'gumdropdoodle', + 'gumdropdorf', + 'gumdropface', + 'gumdropfidget', + 'gumdropfink', + 'gumdropfish', + 'gumdropflap', + 'gumdropflapper', + 'gumdropflinger', + 'gumdropflip', + 'gumdropflipper', + 'gumdropfoot', + 'gumdropfuddy', + 'gumdropfussen', + 'gumdropgadget', + 'gumdropgargle', + 'gumdropgloop', + 'gumdropglop', + 'gumdropgoober', + 'gumdropgoose', + 'gumdropgrooven', + 'gumdrophoffer', + 'gumdrophopper', + 'gumdropjinks', + 'gumdropklunk', + 'gumdropknees', + 'gumdropmarble', + 'gumdropmash', + 'gumdropmonkey', + 'gumdropmooch', + 'gumdropmouth', + 'gumdropmuddle', + 'gumdropmuffin', + 'gumdropmush', + 'gumdropnerd', + 'gumdropnoodle', + 'gumdropnose', + 'gumdropnugget', + 'gumdropphew', + 'gumdropphooey', + 'gumdroppocket', + 'gumdroppoof', + 'gumdroppop', + 'gumdroppounce', + 'gumdroppow', + 'gumdroppretzel', + 'gumdropquack', + 'gumdroproni', + 'gumdropscooter', + 'gumdropscreech', + 'gumdropsmirk', + 'gumdropsnooker', + 'gumdropsnoop', + 'gumdropsnout', + 'gumdropsocks', + 'gumdropspeed', + 'gumdropspinner', + 'gumdropsplat', + 'gumdropsprinkles', + 'gumdropsticks', + 'gumdropstink', + 'gumdropswirl', + 'gumdropteeth', + 'gumdropthud', + 'gumdroptoes', + 'gumdropton', + 'gumdroptoon', + 'gumdroptooth', + 'gumdroptwist', + 'gumdropwhatsit', + 'gumdropwhip', + 'gumdropwig', + 'gumdropwoof', + 'gumdropzaner', + 'gumdropzap', + 'gumdropzapper', + 'gumdropzilla', + 'gumdropzoom', + 'gummer', + 'gummy', + 'gumption', + 'gums', + 'gunga', + 'gunk', + 'gunman', + 'gunmates', + 'gunnies', + 'gunny', + "gunny's", + 'gunship', + 'gunshow', + 'gunsights', + 'gunskill', + 'gunskills', + 'gunsmith', + "gunsmith's", + 'gunsmithes', + 'gunsmithing', + 'gunsmiths', + 'gunwale', + 'guppy', + 'gurl', + 'gurth', + 'guru', + 'gus', + "gus's", + 'gush', + 'gusher', + 'gushing', + 'gushy', + 'gussied', + 'gust', + 'gusteau', + "gusteau's", + 'gusto', + 'gusts', + 'gusty', + 'gut', + 'guts', + 'gutsy', + 'gutted', + 'gutter', + 'gutterat', + 'gutters', + 'gutting', + 'guy', + "guy's", + 'guyago', + 'guyona', + 'guyoso', + 'guyros', + 'guys', + 'guytia', + 'gwa', + 'gwarsh', + 'gwen', + 'gwinn', + "gwinn's", + 'gym', + "gym's", + 'gymnasium', + 'gymnastic', + 'gymnastics', + 'gyms', + 'gypsies', + 'gypsy', + "gypsy's", + 'gyro', + "gyro's", + 'gyros', + 'h-pos', + 'h.g.', + 'h2o', + 'ha', + 'habbo', + "habbo's", + 'habbos', + 'habit', + "habit's", + 'habitat', + "habitat's", + 'habitats', + 'habited', + 'habits', + 'habitual', + 'hack', + 'hacked', + 'hacker', + 'hackers', + 'hacking', + 'hacks', + 'hacky', + 'had', + 'hade', + 'hades', + "hades'", + "hadn't", + 'haft', + 'hah', + 'haha', + 'hail', + 'hailed', + 'hailing', + 'hails', + 'hair', + "hair's", + 'hairball', + 'hairballs', + 'hairband', + 'hairbrush', + 'hairbrushes', + 'hairclip', + 'haircut', + "haircut's", + 'haircuts', + 'hairdo', + 'hairdresser', + 'hairdryer', + 'haired', + 'hairs', + 'hairspray', + 'hairstyle', + "hairstyle's", + 'hairstyles', + 'hairy', + 'hakaba', + 'hake', + 'hakuna', + 'hal', + 'halau', + 'hale', + 'hales', + 'haley', + "haley's", + 'haleys', + 'half', + 'half-o-dil', + 'half-palindrome', + 'halftime', + 'halfway', + 'halfwits', + 'hali', + 'halibut', + 'haling', + 'hall', + "hall's", + 'hallelujah', + 'haller', + 'hallmark', + 'hallo', + 'halloo', + 'hallow', + 'halloween', + "halloween's", + 'hallows', + 'halls', + 'hallway', + 'hallways', + 'halo', + 'halos', + 'halt', + 'halting', + 'halva', + 'halve', + 'halves', + 'ham', + 'hambrrrgers', + 'hamburger', + 'hamburgers', + 'hamburglar', + 'hamilton', + 'hamlet', + 'hammer', + 'hammerhead', + "hammerhead's", + 'hammerheads', + 'hammering', + 'hammers', + 'hammock', + 'hammocks', + 'hammy', + 'hams', + 'hamster', + "hamster's", + 'hamsterball', + 'hamsters', + 'hanaroni', + 'hand', + "hand's", + 'handbag', + 'handbags', + 'handball', + 'handcraft', + 'handed', + 'handedly', + 'handel', + 'hander', + 'handers', + 'handful', + 'handheld', + 'handicapped', + 'handier', + 'handing', + 'handkerchief', + 'handkerchiefs', + 'handle', + 'handlebar', + 'handled', + 'handler', + "handler's", + 'handlers', + 'handles', + 'handling', + 'handout', + 'handpicked', + 'handrails', + 'hands', + 'handshake', + 'handshakes', + 'handsome', + 'handsomely', + 'handwriting', + 'handy', + 'hanebakuon', + 'hanegaku', + 'haneoto', + 'hang', + 'hang-loose', + 'hangnail', + 'hangout', + 'hank', + 'hanker', + 'hankering', + 'hankie', + "hankie's", + 'hankies', + 'hanks', + 'hanky', + 'hanna', + 'hannah', + "hannah's", + 'hannahs', + 'hans', + "hans'", + 'hanukkah', + 'hap', + "hap'n", + 'hapacha', + 'hapaxion', + 'hapazoa', + 'happen', + 'happened', + 'happening', + 'happenings', + 'happens', + 'happier', + 'happiest', + 'happily', + 'happiness', + 'happy', + 'happy-go-lucky', + 'haps', + 'hara', + 'harassed', + 'harassment', + 'harbinger', + 'harbingers', + 'harbor', + "harbor's", + 'harbored', + 'harbormaster', + 'harbormasters', + 'harbors', + 'hard', + 'hardball', + 'hardcode', + 'harder', + 'hardest', + 'hardies', + 'hardly', + 'hardness', + 'hardships', + 'hardware', + 'hardwire', + 'hardwood', + 'hardy', + 'hare', + 'hared', + 'hares', + 'hark', + 'harlequin', + 'harlets', + 'harley', + 'harm', + "harm's", + 'harmed', + 'harmful', + 'harming', + 'harmless', + 'harmonicas', + 'harmonies', + 'harmonious', + 'harmony', + 'harms', + 'harness', + 'harp', + 'harper', + "harper's", + 'harping', + 'harpoon', + 'harpooning', + 'harpoons', + 'harps', + 'harr', + 'harried', + 'harriet', + 'harrow', + 'harrows', + 'harry', + 'harsh', + 'harsher', + 'harshly', + 'hart', + 'harumi', + 'harumite', + 'harumitey', + 'harv', + "harv's", + 'harvest', + 'harvested', + 'harvesting', + 'harvests', + 'harvs', + 'has', + 'hashanah', + 'hasher', + 'hashtag', + 'hashtags', + "hasn't", + 'hasnt', + 'hassaba', + 'hassagua', + 'hassano', + 'hassigos', + 'hassilles', + 'hassle', + 'hassled', + 'hassling', + 'hassros', + 'hast', + 'haste', + 'hasten', + 'hastily', + 'hasty', + 'hat', + "hat's", + 'hatch', + 'hatched', + 'hatches', + 'hatchet', + 'hatchets', + 'hate', + 'hates', + 'hath', + 'hating', + 'hatred', + 'hats', + 'hatter', + 'haul', + 'hauled', + 'hauling', + 'hauls', + 'haunt', + "haunt's", + 'haunted', + 'haunter', + 'haunting', + "haunting's", + 'hauntings', + 'haunts', + 'haut', + 'havarti', + 'have', + 'haven', + "haven's", + "haven't", + 'havendish', + "havendish's", + 'havens', + 'havent', + 'haver', + 'havers', + 'haves', + "havin'", + 'having', + 'havoc', + 'haw', + "hawai'i", + 'hawaii', + 'hawk', + "hawk's", + 'hawkeyes', + 'hawkling', + 'hawks', + 'hawkster', + 'hawkweed', + 'haws', + 'hawthorne', + 'haxby', + 'hay', + 'haydn', + 'hayes', + 'haymaker', + 'hayseed', + 'haystack', + 'haystacks', + 'haywire', + 'hazard', + 'hazardous', + 'hazel', + 'hazelnut', + 'hazelstar', + 'hazes', + 'hazy', + 'hazzard', + 'hd', + 'he', + "he'd", + "he'll", + "he's", + 'head', + 'headache', + 'headaches', + 'headband', + 'headboards', + 'headbutt', + 'headdress', + 'headed', + 'header', + 'headgear', + 'headhunter', + 'headhunters', + 'heading', + "heading's", + 'headings', + 'headless', + 'headlight', + 'headlights', + 'headlock', + 'headpiece', + 'headpieces', + 'headquarter', + 'headquarters', + 'heads', + 'headset', + 'headsets', + 'headshot', + 'headstone', + "headstone's", + 'headstones', + 'headstrong', + 'headway', + 'heal', + 'healed', + 'healer', + 'healers', + 'healing', + 'healings', + 'heals', + 'health', + "health's", + 'healthier', + 'healthiest', + 'healthly', + 'healthy', + 'heap', + 'heaps', + 'hear', + 'heard', + 'hearer', + 'hearers', + 'hearest', + 'hearing', + 'hearings', + 'hears', + 'hearsay', + 'heart', + "heart's", + 'heart-shaped', + 'heartache', + 'heartbeat', + 'heartbreak', + 'heartbreakers', + 'heartbreaking', + 'heartbreaks', + 'heartbroken', + 'hearted', + 'heartedly', + 'hearten', + 'heartens', + 'heartfelt', + 'hearth', + "hearth's", + 'hearties', + 'heartily', + 'heartiness', + 'heartless', + 'hearts', + 'heartstea', + 'heartthrob', + 'heartwrecker', + 'hearty', + "hearty's", + 'heartys', + 'heat', + 'heat-get', + 'heated', + 'heater', + "heater's", + 'heaters', + 'heath', + 'heathen', + 'heathens', + 'heather', + 'heating', + 'heats', + 'heave', + 'heaven', + "heaven's", + 'heavenly', + 'heavens', + 'heaver', + 'heavier', + 'heavies', + 'heaviest', + 'heavily', + 'heavy', + 'heavyset', + 'heavyweight', + 'heck', + 'heckle', + 'hectic', + 'hector', + "hector's", + 'hectors', + 'hedge', + 'hedgehog', + "hedgehog's", + 'hedgehogs', + 'hedley', + 'hedly', + 'hedy', + 'hee', + 'heed', + 'heeded', + 'heel', + 'heeled', + 'heeler', + 'heeling', + 'heels', + 'heffalump', + "heffalump's", + 'heffalumps', + 'heft', + 'hefts', + 'hefty', + 'heh', + 'hehe', + 'heidi', + 'height', + 'heights', + 'heikyung', + 'heinous', + 'heirloom', + 'heirs', + 'heist', + 'heisting', + 'heists', + 'held', + 'helen', + 'helicopter', + 'helicopters', + 'helios', + 'hello', + 'hellos', + 'helm', + "helm's", + 'helmed', + 'helmet', + 'helmets', + 'helming', + 'helms', + 'helmsman', + 'helmsmen', + 'helmswoman', + 'help', + 'helpdesk', + 'helped', + 'helper', + "helper's", + 'helpers', + 'helpful', + 'helpfully', + 'helping', + 'helpings', + 'helpless', + 'helps', + 'hem', + 'hemi', + 'hemisphere', + 'hempen', + 'hems', + 'hen', + "hen's", + 'hence', + 'henchmen', + 'hendry', + 'henhouse', + 'henry', + 'henrys', + 'hens', + 'her', + "her's", + 'herbert', + 'herbie', + "herbie's", + 'herbies', + 'herbs', + 'hercules', + "hercules'", + 'herd', + 'herded', + 'herder', + 'herders', + 'herding', + 'herds', + 'here', + "here's", + 'hereby', + 'herecomestheworld', + 'herein', + 'heres', + 'heresy', + 'heretic', + 'hereunto', + 'heritage', + 'hermione', + 'hermit', + "hermit's", + 'hermits', + 'hermoine', + 'hernia', + 'hero', + "hero's", + 'heroes', + 'heroic', + 'heroism', + 'heron', + 'herons', + 'heros', + 'herring', + 'herrings', + 'herro', + 'hers', + 'herself', + 'hershey', + 'hersheys', + 'hes', + 'hesitant', + 'hesitate', + 'hesitating', + 'hesitation', + 'hest', + 'hewent', + 'hey', + 'heya', + 'heywood', + 'hf', + 'hi', + 'hi-top', + 'hibernate', + 'hibernating', + 'hibernation', + 'hibiscus', + 'hic', + 'hiccup', + 'hiccups', + 'hick', + 'hickory', + 'hickorytip', + 'hicks', + 'hid', + 'hidden', + 'hide', + 'hide-and-seek', + 'hideaway', + 'hideaways', + 'hided', + 'hideous', + 'hideously', + 'hideout', + 'hideouts', + 'hides', + 'hiding', + 'hierarchy', + 'higglyball', + 'higglytown', + 'high', + 'high-energy', + 'high-flying', + 'high-impact', + 'high-octane', + 'high-powered', + 'high-seas', + 'high-speed', + 'high-strung', + 'high-voltage', + 'higher', + 'highest', + 'highest-rated', + 'highjack', + 'highlander', + 'highlands', + 'highlight', + 'highlighted', + 'highlighting', + 'highlights', + 'highly', + 'highness', + 'highs', + 'highsea', + 'highseas', + 'hightail', + 'hightailed', + 'highway', + 'hihi', + 'hiiii-yaaaaa', + 'hike', + 'hiker', + 'hiking', + 'hilarious', + 'hilarity', + 'hill', + "hill's", + 'hilled', + 'hiller', + 'hilling', + 'hills', + 'hillside', + 'hilltop', + 'hilly', + 'hilt', + 'him', + 'hims', + 'himself', + 'himuro', + 'hinata', + 'hindered', + 'hindering', + 'hinds', + 'hindsight', + 'hinges', + 'hint', + 'hinted', + 'hinter', + 'hinterseas', + 'hinting', + 'hints', + 'hip', + 'hip-hop', + "hip-hoppin'", + 'hippie', + 'hippies', + "hippies'", + 'hippo', + "hippo's", + 'hippos', + 'hippy', + 'hipster', + 'hire', + 'hired', + 'hirer', + 'hires', + 'hiring', + 'his', + 'hissed', + 'hisses', + 'hissing', + 'hissy', + 'histoire', + 'historian', + 'historic', + 'historical', + 'historically', + 'histories', + 'history', + "history's", + 'hit', + "hit's", + 'hitch', + 'hitched', + 'hitchhike', + 'hitchhiked', + 'hitchhiker', + 'hitchhikers', + 'hitchhiking', + 'hitching', + 'hither', + 'hitop', + 'hits', + 'hitter', + 'hitters', + 'hitting', + 'hive', + "hive's", + 'hives', + 'hiya', + 'hkdl', + 'hmm', + 'hmmm', + 'hms', + 'ho-o-o-o-orse', + 'hoard', + 'hoarder', + 'hoarding', + 'hoards', + 'hoax', + 'hob', + 'hobbies', + 'hobbit', + 'hobbits', + 'hobbles', + 'hobbling', + 'hobby', + 'hoboken', + 'hocked', + 'hockey', + 'hocks', + 'hocus', + 'hocus-focus', + 'hodgepodge', + 'hofflord', + 'hog', + 'hogg', + 'hogge', + 'hogged', + 'hogging', + 'hogglestock', + 'hogs', + 'hogwarts', + 'hogwash', + 'hoi', + 'hoist', + 'hoisting', + 'hola', + 'hold', + "hold'em", + "hold's", + 'holdem', + 'holden', + 'holder', + 'holders', + "holdin'", + 'holding', + 'holdings', + 'holdout', + 'holds', + 'hole', + 'holey', + 'holidas', + 'holiday', + "holiday's", + 'holidayer', + 'holidays', + 'holl', + 'holler', + 'hollered', + 'hollering', + 'holli', + 'hollies', + 'hollow', + "hollow's", + 'hollowed-out', + 'hollows', + 'holly', + "holly's", + 'hollywood', + 'hollywoods', + 'holms', + 'holo', + 'hologram', + 'holograms', + 'holt', + 'holy', + 'homage', + 'hombre', + 'hombres', + 'home', + "home's", + 'home-grown', + 'home-made', + 'homebound', + 'homeboy', + 'homecoming', + 'homed', + 'homedog', + 'homegirl', + 'homeland', + 'homeless', + 'homely', + 'homemade', + 'homeopathic', + 'homepage', + 'homeport', + 'homer', + "homer's", + 'homers', + 'homes', + 'homespun', + 'homestead', + 'hometown', + 'homework', + 'homeworker', + "homeworker's", + 'homeworkers', + 'homey', + 'homeys', + 'homier', + 'homies', + 'homing', + 'homograph', + 'homographs', + 'hon', + 'honcho', + 'honda', + 'hone', + 'honed', + 'hones', + 'honest', + 'honestly', + 'honesty', + 'honey', + 'honeybunch', + 'honeycomb', + 'honeydew', + 'honeys', + 'honeysuckle', + 'honeysuckles', + 'honing', + 'honk', + 'honor', + 'honorable', + 'honorary', + 'honored', + 'honorific', + 'honorifics', + 'honoring', + 'honors', + 'honour', + 'hood', + "hood's", + 'hoods', + 'hoof', + 'hook', + "hook's", + 'hooked', + 'hooks', + 'hooligan', + 'hooligans', + 'hoop', + 'hoopla', + 'hooplah', + 'hoops', + 'hoopster', + 'hoopsters', + 'hooray', + 'hoot', + 'hootenanny', + 'hooting', + 'hoots', + 'hop', + 'hope', + 'hoped', + 'hopeful', + "hopeful's", + 'hopefully', + 'hoper', + 'hopes', + 'hopey', + 'hoping', + 'hopped', + 'hopper', + 'hopping', + 'hoppy', + 'hops', + 'hopscotch', + 'horatio', + "horatio's", + 'horatios', + 'hordes', + 'horison', + 'horizon', + "horizon's", + 'horizons', + 'horizontal', + 'horn', + 'hornbow', + 'hornet', + "hornet's", + 'hornets', + 'horns', + 'hornswoggle', + 'horoscope', + 'horrendous', + 'horrible', + 'horribly', + 'horrid', + 'horridness', + 'horrific', + 'horrified', + 'horrifying', + 'horror', + "horror's", + 'horrors', + 'horse', + "horse's", + 'horseman', + 'horsepower', + 'horses', + 'horseshoe', + "horseshoe's", + 'horseshoes', + 'horseshow', + 'horsey', + 'horsing', + 'hose', + 'hosed', + 'hoses', + 'hosiery', + 'hospital', + 'hospitality', + 'hospitalize', + 'hospitals', + 'host', + "host's", + 'hosted', + 'hostile', + 'hostiles', + 'hostility', + 'hosting', + 'hosts', + 'hot', + 'hot-tempered', + 'hotel', + "hotel's", + 'hotels', + 'hothead', + 'hotkey', + 'hotkeys', + 'hotshot', + 'hotspot', + 'hotspots', + 'hotter', + 'hottest', + 'hound', + 'hounded', + 'hounding', + 'hounds', + 'hour', + "hour's", + 'hourglass', + 'hourly', + 'hours', + 'house', + "house's", + 'housebroken', + 'housecleaning', + 'housed', + 'houseful', + 'household', + 'housekeeper', + 'housemates', + 'houseplant', + 'houser', + 'houses', + 'housewife', + 'housewives', + 'housework', + 'housing', + 'housings', + 'hovel', + 'hover', + 'hovercraft', + 'hovercrafts', + 'hovered', + 'hovering', + 'hovers', + 'how', + "how'd", + "how're", + "how's", + "how've", + 'how-to', + 'how-to-video', + 'how2', + 'howard', + 'howdy', + 'however', + 'howl', + 'howled', + 'howling', + 'hows', + 'hp', + "hp's", + 'hq', + 'hqofficerf', + 'hqofficerm', + 'hr', + 'hr.', + 'hrage', + 'hsm', + "hsm-2's", + 'hsm2', + 'hsm3', + 'html', + 'hub', + "hub's", + 'hubbies', + 'hubby', + "hubby's", + 'hubs', + 'hucklebee', + 'huckleberry', + 'huckleblabber', + 'hucklebocker', + 'huckleboing', + 'huckleboom', + 'hucklebounce', + 'hucklebouncer', + 'hucklebrains', + 'hucklebubble', + 'hucklebumble', + 'hucklebump', + 'hucklebumper', + 'huckleburger', + 'hucklechomp', + 'hucklecorn', + 'hucklecrash', + 'hucklecrumbs', + 'hucklecrump', + 'hucklecrunch', + 'huckledoodle', + 'huckledorf', + 'huckleface', + 'hucklefidget', + 'hucklefink', + 'hucklefish', + 'huckleflap', + 'huckleflapper', + 'huckleflinger', + 'huckleflip', + 'huckleflipper', + 'hucklefoot', + 'hucklefuddy', + 'hucklefussen', + 'hucklegadget', + 'hucklegargle', + 'hucklegloop', + 'huckleglop', + 'hucklegoober', + 'hucklegoose', + 'hucklegrooven', + 'hucklehoffer', + 'hucklehopper', + 'hucklejinks', + 'huckleklunk', + 'huckleknees', + 'hucklemarble', + 'hucklemash', + 'hucklemonkey', + 'hucklemooch', + 'hucklemouth', + 'hucklemuddle', + 'hucklemuffin', + 'hucklemush', + 'hucklenerd', + 'hucklenoodle', + 'hucklenose', + 'hucklenugget', + 'hucklephew', + 'hucklephooey', + 'hucklepocket', + 'hucklepoof', + 'hucklepop', + 'hucklepounce', + 'hucklepow', + 'hucklepretzel', + 'hucklequack', + 'huckleroni', + 'hucklescooter', + 'hucklescreech', + 'hucklesmirk', + 'hucklesnooker', + 'hucklesnoop', + 'hucklesnout', + 'hucklesocks', + 'hucklespeed', + 'hucklespinner', + 'hucklesplat', + 'hucklesprinkles', + 'hucklesticks', + 'hucklestink', + 'huckleswirl', + 'huckleteeth', + 'hucklethud', + 'huckletoes', + 'huckleton', + 'huckletoon', + 'huckletooth', + 'huckletwist', + 'hucklewhatsit', + 'hucklewhip', + 'hucklewig', + 'hucklewoof', + 'hucklezaner', + 'hucklezap', + 'hucklezapper', + 'hucklezilla', + 'hucklezoom', + 'huddle', + 'huddled', + "hudgen's", + 'hudgens', + 'hudson', + "hudson's", + 'hudsons', + 'hue', + 'hug', + "hug's", + 'huge', + 'hugely', + 'huger', + 'hugers', + 'hugest', + 'hugged', + 'hugh', + 'hugo', + 'hugs', + 'huh', + 'hula', + "hula's", + 'hulabee', + 'hulaberry', + 'hulablabber', + 'hulabocker', + 'hulaboing', + 'hulaboom', + 'hulabounce', + 'hulabouncer', + 'hulabrains', + 'hulabubble', + 'hulabumble', + 'hulabump', + 'hulabumper', + 'hulaburger', + 'hulachomp', + 'hulacorn', + 'hulacrash', + 'hulacrumbs', + 'hulacrump', + 'hulacrunch', + 'huladoodle', + 'huladorf', + 'hulaface', + 'hulafidget', + 'hulafink', + 'hulafish', + 'hulaflap', + 'hulaflapper', + 'hulaflinger', + 'hulaflip', + 'hulaflipper', + 'hulafoot', + 'hulafuddy', + 'hulafussen', + 'hulagadget', + 'hulagargle', + 'hulagloop', + 'hulaglop', + 'hulagoober', + 'hulagoose', + 'hulagrooven', + 'hulahoffer', + 'hulahopper', + 'hulajinks', + 'hulaklunk', + 'hulaknees', + 'hulamarble', + 'hulamash', + 'hulamonkey', + 'hulamooch', + 'hulamouth', + 'hulamuddle', + 'hulamuffin', + 'hulamush', + 'hulanerd', + 'hulanoodle', + 'hulanose', + 'hulanugget', + 'hulaphew', + 'hulaphooey', + 'hulapocket', + 'hulapoof', + 'hulapop', + 'hulapounce', + 'hulapow', + 'hulapretzel', + 'hulaquack', + 'hularoni', + 'hulas', + 'hulascooter', + 'hulascreech', + 'hulasmirk', + 'hulasnooker', + 'hulasnoop', + 'hulasnout', + 'hulasocks', + 'hulaspeed', + 'hulaspinner', + 'hulasplat', + 'hulasprinkles', + 'hulasticks', + 'hulastink', + 'hulaswirl', + 'hulateeth', + 'hulathud', + 'hulatoes', + 'hulaton', + 'hulatoon', + 'hulatooth', + 'hulatwist', + 'hulawhatsit', + 'hulawhip', + 'hulawig', + 'hulawoof', + 'hulazaner', + 'hulazap', + 'hulazapper', + 'hulazilla', + 'hulazoom', + 'hulk', + 'hulking', + 'hull', + "hull's", + 'hullabaloo', + 'hulled', + 'hullo', + 'hulls', + 'human', + "human's", + 'humane', + 'humanism', + 'humanist', + 'humanists', + 'humanities', + 'humanity', + 'humankind', + 'humans', + 'humble', + 'humbled', + 'humbly', + 'humbuckers', + 'humbug', + 'humdinger', + 'humid', + 'humidia', + "humidia's", + 'humiliating', + 'humility', + 'hummingbird', + "hummingbird's", + 'hummingbirds', + 'hummus', + 'humor', + "humor's", + 'humored', + 'humoring', + 'humorous', + 'humors', + 'hums', + 'hunch', + 'hunchback', + 'hundred', + 'hundreds', + 'hunger', + 'hungering', + 'hungrier', + 'hungriest', + 'hungry', + 'hunny', + 'hunt', + 'hunter', + "hunter's", + 'hunters', + 'hunting', + 'huntress', + 'hunts', + 'huntsclan', + 'huntsgirl', + 'huntsman', + "huntsman's", + 'hunty', + 'hurdle', + 'hurl', + 'hurled', + 'hurls', + 'hurrah', + 'hurray', + 'hurricane', + 'hurricanes', + 'hurried', + 'hurrier', + 'hurries', + 'hurry', + 'hurrying', + 'hurt', + 'hurter', + 'hurtful', + 'hurting', + 'hurts', + 'husband', + "husband's", + 'husbands', + 'hush', + 'hushes', + 'husk', + 'huskers', + 'huskies', + 'husky', + 'hustle', + 'hustling', + 'hut', + 'hutch', + 'huts', + 'huzza', + 'huzzah', + 'hw', + 'hxdq', + 'hyacinth', + 'hybrid', + 'hydra', + 'hydrant', + 'hydrants', + 'hydrated', + 'hydro-hopper', + 'hydrofoil', + 'hyena', + "hyena's", + 'hyenas', + 'hygiene', + 'hymn', + 'hymns', + 'hyoga', + 'hype', + 'hyped', + 'hyper', + 'hyper-drive', + 'hyperactive', + 'hyperforce', + 'hypersensitive', + 'hyperspace', + 'hypno', + 'hypno-goggles', + 'hypnotic', + 'hypnotise', + 'hypnotised', + 'hypnotising', + 'hypnotized', + 'hypocrite', + 'hypothetical', + 'hypothetically', + 'hyrdo', + 'hysterical', + 'hysterically', + 'hyundai', + 'i', + "i'd", + "i'll", + "i'm", + "i've", + 'i.e.', + 'i.m.', + 'i.w.g.', + 'iago', + "iago's", + 'iagos', + 'iam', + 'iambic', + 'ibuprofen', + 'ice', + "ice's", + 'iceberg', + 'icebergs', + 'iceburg', + 'icecold', + 'icecream', + 'iced', + 'iceman', + 'icerage', + 'ices', + 'iceshark', + 'icestockings', + 'ichabod', + "ichabod's", + 'ichabods', + 'icicle', + 'icicles', + 'icing', + "icing's", + 'icings', + 'icky', + 'icon', + 'iconic', + 'icons', + 'icy', + 'id', + 'ida', + 'idc', + 'idea', + "idea's", + 'ideal', + 'ideally', + 'ideals', + 'ideas', + 'identical', + 'identify', + 'identifying', + 'identity', + 'ides', + 'idk', + 'idol', + "idol's", + 'idolizing', + 'idols', + 'ids', + 'if', + 'ifalla', + 'iger', + 'ight', + 'igloo', + 'igloos', + 'ignatius', + "ignatius'", + 'ignite', + 'ignorance', + 'ignorant', + 'ignore', + 'ignored', + 'ignorer', + 'ignores', + 'ignoring', + 'igo', + "igo's", + 'iguana', + 'ii', + 'iid', + 'ik', + 'ike', + 'ikr', + 'ile', + 'ill', + 'illegal', + 'illegally', + 'illiterate', + 'illness', + 'illnesses', + 'ills', + 'illumination', + 'illusion', + 'illusive', + 'illustrate', + 'illustrated', + 'illustrates', + 'illustrating', + 'illustration', + 'illustrations', + 'illustrative', + 'illustrious', + 'ily', + 'im', + 'image', + 'imaged', + 'images', + 'imaginable', + 'imaginary', + 'imagination', + "imagination's", + 'imaginations', + 'imaginative', + 'imaginatively', + 'imagine', + 'imagined', + 'imagineer', + 'imagineers', + 'imaginer', + 'imagines', + 'imaging', + 'imagining', + 'imaginings', + 'imam', + 'imbedded', + 'imho', + 'imitate', + 'imitated', + 'imitating', + 'imitation', + 'immature', + 'immediate', + 'immediately', + 'immense', + 'immensely', + 'immigrant', + 'immigrate', + 'immigrated', + 'imminent', + 'immortal', + 'immune', + 'immunity', + 'imo', + 'imp', + 'impact', + 'impacted', + 'impacter', + 'impacting', + 'impactive', + 'impacts', + 'impaired', + 'impartial', + 'impatient', + 'impending', + 'imperal', + 'imperfect', + 'imperial', + 'impersonal', + 'impersonate', + 'impersonating', + 'impersonation', + 'impervious', + 'implement', + 'implemented', + 'implementing', + 'implication', + 'implications', + 'implicit', + 'implicitly', + 'implied', + 'implies', + 'implode', + 'imploded', + 'imploding', + 'imploringly', + 'imply', + 'implying', + 'impolite', + 'import', + 'importance', + 'important', + 'importantly', + 'imported', + 'importer', + "importer's", + 'importers', + 'importing', + 'imports', + 'impose', + 'imposed', + 'imposer', + 'imposes', + 'imposing', + 'impossibility', + 'impossible', + 'impossibles', + 'impossibly', + 'imposter', + 'impound', + 'impounded', + 'impractical', + 'impress', + 'impressed', + 'impresses', + 'impressing', + 'impression', + "impression's", + 'impressions', + 'impressive', + 'imprison', + 'imprisoned', + 'imprisonment', + 'improbably', + 'impromptu', + 'improper', + 'improprieties', + 'improvaganza', + 'improve', + 'improved', + 'improvement', + 'improvements', + 'improver', + 'improves', + 'improving', + 'improvise', + 'imps', + 'impudent', + 'impulse', + 'impulsive', + 'impunity', + 'imsosorry', + 'in', + 'inability', + 'inaccuracies', + 'inaccurate', + 'inaction', + 'inactive', + 'inadequate', + 'inadequately', + 'inadvertently', + 'inane', + 'inappropriate', + 'inappropriately', + 'inboards', + 'inbound', + 'inbox', + 'inc', + 'inc.', + 'incapable', + 'incapacitated', + 'incapacitating', + 'incarnate', + 'incarnation', + 'incase', + 'incased', + 'incautious', + 'incentive', + 'incessantly', + 'inch', + 'inches', + 'inching', + 'incident', + "incident's", + 'incidentally', + 'incidents', + 'incisor', + 'incite', + 'incited', + 'incline', + 'inclined', + 'include', + 'included', + 'includes', + 'including', + 'inclusive', + 'incognito', + 'incoherent', + 'income', + 'incoming', + 'incomings', + 'incompatible', + 'incompetency', + 'incompetent', + 'incomplete', + 'incompletes', + 'inconsiderate', + 'inconsideration', + 'inconsistent', + 'inconspicuous', + 'inconspicuously', + 'inconvenience', + 'inconveniencing', + 'inconvenient', + 'incorporated', + 'incorporeal', + 'incorrect', + 'incorrectly', + 'increase', + 'increased', + 'increases', + 'increasing', + 'incredible', + "incredible's", + 'incredibles', + 'incredibly', + 'incrementally', + 'increments', + 'incriminate', + 'incriminating', + 'indebt', + 'indecisive', + 'indeed', + 'indeedy', + 'independence', + 'independent', + 'independently', + 'indestructible', + 'index', + 'indexed', + 'indexer', + 'indexers', + 'indexes', + 'indexing', + 'india', + 'indian', + 'indicate', + 'indicated', + 'indicates', + 'indicating', + 'indication', + 'indications', + 'indicative', + 'indicator', + 'indices', + 'indie', + 'indies', + 'indifference', + 'indifferent', + 'indifferently', + 'indigenous', + 'indigestion', + 'indigo', + 'indigos', + 'indirect', + 'indirectly', + 'indiscriminately', + 'indisposed', + 'individual', + "individual's", + 'individuality', + 'individually', + 'individuals', + 'indo', + 'indoctrinations', + 'indomitable', + 'indonesia', + 'indoor', + 'indoors', + 'indubitab', + 'induced', + 'inducted', + 'inducting', + 'indulge', + 'industrial', + 'industrial-sized', + 'industrially', + 'industrials', + 'industries', + 'industry', + "industry's", + 'inedible', + 'ineffective', + 'inefficient', + 'inept', + 'inertia', + 'inevitable', + 'inexpensive', + 'inexperience', + 'inexperienced', + 'inexplicably', + 'infamous', + 'infancy', + 'infant', + 'infantile', + 'infantry', + 'infants', + 'infected', + 'infecting', + 'infection', + 'infections', + 'infectious', + 'infer', + 'inferior', + 'inferiority', + 'infernal', + 'inferno', + "inferno's", + 'infernos', + 'inferring', + 'infest', + 'infestation', + 'infested', + 'infidels', + 'infiltrate', + 'infiltrated', + 'infinite', + 'infiniti', + 'infinities', + 'infinity', + 'infirmary', + 'inflame', + 'inflammation', + 'inflatable', + 'inflate', + 'inflated', + 'inflict', + 'inflicted', + 'inflicting', + 'infliction', + 'inflicts', + 'influence', + 'influenced', + 'influencer', + 'influences', + 'influencing', + 'influx', + 'info', + 'inform', + 'informal', + 'informants', + 'information', + "information's", + 'informations', + 'informative', + 'informed', + 'informer', + "informer's", + 'informers', + 'informing', + 'informs', + 'infotainment', + 'infrastructure', + 'infringe', + 'infuriating', + 'ing', + 'ingenious', + 'ingles', + 'ingot', + 'ingots', + 'ingrate', + 'ingredient', + 'ingredients', + 'inhabit', + 'inhabitated', + 'inhabited', + 'inhalation', + 'inhere', + 'inherit', + 'inialate', + 'init', + 'initial', + 'initialization', + 'initialized', + 'initially', + 'initials', + 'initiate', + 'initiated', + 'initiates', + 'initiating', + 'initiation', + 'initiations', + 'initiative', + 'injure', + 'injured', + 'injures', + 'injuries', + 'injuring', + 'injury', + 'injustices', + 'ink', + "ink's", + 'ink-making', + 'ink-making-talent', + 'inkaflare', + 'inkana', + 'inkanapa', + 'inked', + 'inker', + 'inkers', + 'inking', + 'inkings', + 'inks', + 'inkwell', + 'inkwells', + 'inky', + 'inland', + 'inlanders', + 'inlet', + 'inline', + 'inmates', + 'inn', + 'innate', + 'inner', + 'innerly', + 'innkeeper', + 'innocence', + 'innocent', + 'innocently', + 'innocents', + 'innovative', + 'innovention', + "innovention's", + 'innoventions', + 'inns', + 'innuendo', + 'innuendoes', + 'ino', + 'inoperable', + 'inpatient', + 'input', + "input's", + 'inputed', + 'inputer', + 'inputing', + 'inputs', + 'inputting', + 'inquest', + 'inquire', + 'inquiries', + 'inquiring', + 'inquiry', + 'inquisitively', + 'ins', + 'insane', + 'insanely', + 'insanity', + 'insatiable', + 'insect', + 'insects', + 'insecure', + 'insecurities', + 'insecurity', + 'insensitive', + 'insert', + 'inserted', + 'inserting', + 'inserts', + 'inset', + 'inside', + "inside's", + 'insider', + "insider's", + 'insiders', + 'insides', + 'insight', + 'insightful', + 'insignia', + 'insignificant', + 'insinuate', + 'insinuating', + 'insinuation', + 'insist', + 'insisted', + 'insisting', + 'insists', + 'insolence', + 'insomnia', + 'insomniac', + 'insomniatic', + 'inspect', + 'inspected', + 'inspecting', + 'inspection', + 'inspections', + 'inspector', + 'inspects', + 'inspiration', + 'inspire', + 'inspired', + 'inspires', + 'inspiring', + 'inst', + "inst'", + 'insta-grow', + 'instable', + 'install', + 'installation', + 'installed', + 'installer', + 'installing', + 'installment', + 'installs', + 'instance', + 'instanced', + 'instances', + 'instancing', + 'instant', + 'instantly', + 'instead', + 'instep', + 'instigate', + 'instigator', + 'instill', + 'instilling', + 'instinct', + 'instinctively', + 'instincts', + 'institution', + 'instruct', + 'instructed', + 'instruction', + "instruction's", + 'instructions', + 'instructor', + 'instructors', + 'instrument', + "instrument's", + 'instrumented', + 'instrumenting', + 'instruments', + 'insubordinate', + 'insubordinates', + 'insubordination', + 'insufferable', + 'insufficient', + 'insulates', + 'insulating', + 'insulation', + 'insult', + "insult's", + 'insulted', + 'insulter', + 'insulting', + 'insults', + 'insurance', + 'insure', + 'insured', + 'intact', + 'integrate', + 'integrated', + 'integrity', + 'intel', + 'intellectual', + 'intellectualizing', + 'intelligence', + 'intelligent', + 'intend', + 'intended', + 'intender', + 'intending', + 'intends', + 'intense', + 'intensified', + 'intension', + 'intensions', + 'intensity', + 'intensive', + 'intent', + 'intention', + "intention's", + 'intentional', + 'intentionally', + 'intentioned', + 'intentions', + 'intently', + 'intents', + 'inter', + 'interact', + 'interacting', + 'interaction', + 'interactive', + 'interactively', + 'intercept', + 'intercepted', + 'intercepten', + 'intercepting', + 'interception', + 'interceptive', + 'interceptor', + 'interchange', + 'intercom', + 'interconnection', + 'interest', + "interest's", + 'interested', + 'interesting', + 'interestingly', + 'interests', + 'interface', + "interface's", + 'interfaced', + 'interfacer', + 'interfaces', + 'interfacing', + 'interfere', + 'interference', + 'interferes', + 'interfering', + 'interim', + 'interior', + "interior's", + 'interiorly', + 'interiors', + 'interject', + 'interjections', + 'interlopers', + 'intermediaries', + 'intermediate', + 'interminable', + 'intermission', + 'intermittent', + 'intermittently', + 'intern', + 'internal', + 'international', + 'interned', + 'internet', + "internet's", + 'internets', + 'internship', + 'interpretation', + 'interprets', + 'interrogate', + 'interrupt', + "interrupt's", + 'interrupted', + 'interrupter', + 'interrupters', + 'interrupting', + 'interruption', + 'interruptions', + 'interruptive', + 'interrupts', + 'intersect', + 'interstate', + 'intervals', + 'intervene', + 'intervened', + 'intervention', + 'interview', + 'interviewing', + 'interviews', + 'interwebz', + 'intimately', + 'intimidate', + 'intimidated', + 'intimidating', + 'into', + 'intolerant', + 'intranet', + 'intrepid', + 'intrigues', + 'intriguing', + 'intro', + "intro's", + 'introduce', + 'introduced', + 'introducer', + 'introduces', + 'introducing', + 'introduction', + "introduction's", + 'introductions', + 'introductory', + 'intros', + 'intrude', + 'intruder', + 'intruders', + 'intrudes', + 'intruding', + 'intrusion', + 'intuition', + 'intuitive', + 'inundated', + 'inutile', + 'inv', + 'invade', + 'invaded', + 'invader', + 'invaders', + 'invading', + 'invalid', + 'invaluable', + 'invasion', + 'invasions', + 'invasive', + 'invent', + 'invented', + 'inventing', + 'invention', + 'inventions', + 'inventive', + 'inventor', + 'inventories', + 'inventors', + 'inventory', + 'invents', + 'inverse', + 'invert', + 'inverted', + 'invest', + 'invested', + 'investigate', + 'investigated', + 'investigates', + 'investigating', + 'investigation', + 'investigations', + 'investigative', + 'investigator', + 'investigators', + 'investing', + 'investment', + 'investments', + 'invigorating', + 'invincibility', + 'invincible', + 'invincibles', + 'invisibility', + 'invisible', + 'invisibles', + 'invisibly', + 'invitation', + "invitation's", + 'invitations', + 'invite', + 'invited', + 'invitee', + 'inviter', + 'invites', + 'inviting', + 'invoice', + 'invoices', + 'involuntarily', + 'involve', + 'involved', + 'involver', + 'involves', + 'involving', + 'invulnerability', + 'invulnerable', + 'iow', + 'ipad', + 'iphone', + 'ipod', + 'ipods', + 'iran', + 'iraq', + 'irc', + 'iridessa', + "iridessa's", + 'iris', + "iris'", + 'irk', + 'irked', + 'irking', + 'irks', + 'irksome', + 'irl', + 'iron', + 'ironclad', + 'ironed', + 'ironhoof', + 'ironic', + 'ironically', + 'ironing', + 'ironman', + 'irons', + 'ironsides', + "ironskull's", + 'ironwall', + 'ironwalls', + 'irony', + 'irrational', + 'irregular', + 'irrelevant', + 'irrelevantly', + 'irresistible', + 'irritable', + 'irritant', + 'irritate', + 'irritated', + 'irritates', + "irritatin'", + 'irritating', + 'irritation', + 'is', + 'isabel', + 'isabella', + "isabella's", + 'isabellas', + 'isadora', + 'isadore', + 'isaiah', + 'isla', + 'island', + "island's", + 'islander', + 'islanders', + 'islands', + 'isle', + "isle's", + 'isles', + "isn't", + 'isnt', + 'isolate', + 'isolated', + 'isolating', + 'isometric', + 'isp', + 'issue', + 'issued', + 'issuer', + 'issuers', + 'issues', + 'issuing', + 'istilla', + 'isuzu', + 'it', + "it'll", + "it's", + 'italics', + 'italy', + 'itched', + 'itchie', + 'itching', + 'itchy', + 'item', + 'items', + 'its', + 'itself', + 'ivanna', + 'ive', + 'ivona', + 'ivor', + 'ivory', + 'ivy', + "ivy's", + 'ivys', + 'ix', + 'izzy', + "izzy's", + 'izzys', + 'j.k.', + 'ja', + 'jab', + 'jabberbee', + 'jabberberry', + 'jabberblabber', + 'jabberbocker', + 'jabberboing', + 'jabberboom', + 'jabberbounce', + 'jabberbouncer', + 'jabberbrains', + 'jabberbubble', + 'jabberbumble', + 'jabberbump', + 'jabberbumper', + 'jabberburger', + 'jabberchomp', + 'jabbercorn', + 'jabbercrash', + 'jabbercrumbs', + 'jabbercrump', + 'jabbercrunch', + 'jabberdoodle', + 'jabberdorf', + 'jabberface', + 'jabberfidget', + 'jabberfink', + 'jabberfish', + 'jabberflap', + 'jabberflapper', + 'jabberflinger', + 'jabberflip', + 'jabberflipper', + 'jabberfoot', + 'jabberfuddy', + 'jabberfussen', + 'jabbergadget', + 'jabbergargle', + 'jabbergloop', + 'jabberglop', + 'jabbergoober', + 'jabbergoose', + 'jabbergrooven', + 'jabberhoffer', + 'jabberhopper', + 'jabberjinks', + 'jabberklunk', + 'jabberknees', + 'jabbermarble', + 'jabbermash', + 'jabbermonkey', + 'jabbermooch', + 'jabbermouth', + 'jabbermuddle', + 'jabbermuffin', + 'jabbermush', + 'jabbernerd', + 'jabbernoodle', + 'jabbernose', + 'jabbernugget', + 'jabberphew', + 'jabberphooey', + 'jabberpocket', + 'jabberpoof', + 'jabberpop', + 'jabberpounce', + 'jabberpow', + 'jabberpretzel', + 'jabberquack', + 'jabberroni', + 'jabberscooter', + 'jabberscreech', + 'jabbersmirk', + 'jabbersnooker', + 'jabbersnoop', + 'jabbersnout', + 'jabbersocks', + 'jabberspeed', + 'jabberspinner', + 'jabbersplat', + 'jabbersprinkles', + 'jabbersticks', + 'jabberstink', + 'jabberswirl', + 'jabberteeth', + 'jabberthud', + 'jabbertoes', + 'jabberton', + 'jabbertoon', + 'jabbertooth', + 'jabbertwist', + 'jabberwhatsit', + 'jabberwhip', + 'jabberwig', + 'jabberwoof', + 'jabberzaner', + 'jabberzap', + 'jabberzapper', + 'jabberzilla', + 'jabberzoom', + 'jace', + 'jack', + "jack's", + 'jackals', + 'jacket', + "jacket's", + 'jackets', + 'jackfruit', + 'jackie', + "jackie's", + 'jackies', + 'jackolantern', + "jackolantern's", + 'jackolanterns', + 'jackpie', + 'jackpot', + "jackpot's", + 'jackpots', + 'jacks', + 'jackson', + "jackson's", + 'jacques', + 'jade', + 'jaded', + 'jado', + 'jafar', + "jafar's", + 'jafars', + 'jagged', + 'jaguar', + 'jail', + 'jails', + 'jake', + "jake's", + 'jakes', + 'jalex', + 'jam', + 'jamada', + 'jamago', + 'jamble', + 'jamboree', + 'james', + "james'", + 'jamie', + 'jamigos', + 'jamilles', + 'jammania', + 'jammed', + 'jammer', + 'jammin', + "jammin'", + "jammin's", + 'jamming', + 'jammy', + 'jamoso', + 'jams', + 'jan', + 'jane', + "jane's", + 'janes', + 'janet', + 'janice', + 'january', + "january's", + 'januarys', + 'japan', + 'jar', + "jar's", + 'jargon', + 'jars', + 'jasmine', + "jasmine's", + 'jasmines', + 'jason', + "jason's", + 'jasons', + 'java', + 'javascript', + 'jaw', + "jaw's", + 'jaw-dropper', + 'jawed', + 'jaws', + 'jazz', + 'jazzed', + 'jazzy', + 'jb', + 'jbs', + 'jealous', + 'jealously', + 'jealousy', + 'jean', + "jean's", + 'jeanie', + 'jeanne', + 'jeans', + 'jed', + 'jedi', + "jedi's", + 'jedis', + 'jeena', + 'jeep', + 'jeeperbee', + 'jeeperberry', + 'jeeperblabber', + 'jeeperbocker', + 'jeeperboing', + 'jeeperboom', + 'jeeperbounce', + 'jeeperbouncer', + 'jeeperbrains', + 'jeeperbubble', + 'jeeperbumble', + 'jeeperbump', + 'jeeperbumper', + 'jeeperburger', + 'jeeperchomp', + 'jeepercorn', + 'jeepercrash', + 'jeepercrumbs', + 'jeepercrump', + 'jeepercrunch', + 'jeeperdoodle', + 'jeeperdorf', + 'jeeperface', + 'jeeperfidget', + 'jeeperfink', + 'jeeperfish', + 'jeeperflap', + 'jeeperflapper', + 'jeeperflinger', + 'jeeperflip', + 'jeeperflipper', + 'jeeperfoot', + 'jeeperfuddy', + 'jeeperfussen', + 'jeepergadget', + 'jeepergargle', + 'jeepergloop', + 'jeeperglop', + 'jeepergoober', + 'jeepergoose', + 'jeepergrooven', + 'jeeperhoffer', + 'jeeperhopper', + 'jeeperjinks', + 'jeeperklunk', + 'jeeperknees', + 'jeepermarble', + 'jeepermash', + 'jeepermonkey', + 'jeepermooch', + 'jeepermouth', + 'jeepermuddle', + 'jeepermuffin', + 'jeepermush', + 'jeepernerd', + 'jeepernoodle', + 'jeepernose', + 'jeepernugget', + 'jeeperphew', + 'jeeperphooey', + 'jeeperpocket', + 'jeeperpoof', + 'jeeperpop', + 'jeeperpounce', + 'jeeperpow', + 'jeeperpretzel', + 'jeeperquack', + 'jeeperroni', + 'jeeperscooter', + 'jeeperscreech', + 'jeepersmirk', + 'jeepersnooker', + 'jeepersnoop', + 'jeepersnout', + 'jeepersocks', + 'jeeperspeed', + 'jeeperspinner', + 'jeepersplat', + 'jeepersprinkles', + 'jeepersticks', + 'jeeperstink', + 'jeeperswirl', + 'jeeperteeth', + 'jeeperthud', + 'jeepertoes', + 'jeeperton', + 'jeepertoon', + 'jeepertooth', + 'jeepertwist', + 'jeeperwhatsit', + 'jeeperwhip', + 'jeeperwig', + 'jeeperwoof', + 'jeeperzaner', + 'jeeperzap', + 'jeeperzapper', + 'jeeperzilla', + 'jeeperzoom', + 'jeff', + 'jeffrey', + 'jehan', + 'jellies', + 'jelly', + 'jellybean', + "jellybean's", + 'jellybeans', + 'jellyfish', + "jellyfish's", + 'jellyroll', + 'jenny', + 'jeopardy', + 'jeremiah', + 'jeremy', + 'jerome', + 'jerry', + "jerry's", + 'jerrys', + 'jersey', + 'jess', + 'jessamine', + 'jesse', + "jesse's", + 'jesses', + 'jest', + 'jester', + "jester's", + 'jesters', + 'jests', + 'jet', + "jet's", + 'jethred', + 'jetix', + 'jetixtreme', + 'jetpack', + 'jets', + 'jetsam', + "jetsam's", + 'jetsams', + 'jett', + "jett's", + 'jetts', + 'jeune', + "jewel's", + 'jeweled', + 'jeweler', + 'jewelers', + 'jewelry', + 'jex', + 'jig', + 'jigsaw', + 'jill', + 'jim', + 'jima', + 'jimmie', + 'jimmyleg', + 'jingle', + 'jinglebells', + 'jingles', + 'jingly', + 'jinks', + 'jinx', + 'jinxbee', + 'jinxberry', + 'jinxblabber', + 'jinxbocker', + 'jinxboing', + 'jinxboom', + 'jinxbounce', + 'jinxbouncer', + 'jinxbrains', + 'jinxbubble', + 'jinxbumble', + 'jinxbump', + 'jinxbumper', + 'jinxburger', + 'jinxchomp', + 'jinxcorn', + 'jinxcrash', + 'jinxcrumbs', + 'jinxcrump', + 'jinxcrunch', + 'jinxdoodle', + 'jinxdorf', + 'jinxed', + 'jinxes', + 'jinxface', + 'jinxfidget', + 'jinxfink', + 'jinxfish', + 'jinxflap', + 'jinxflapper', + 'jinxflinger', + 'jinxflip', + 'jinxflipper', + 'jinxfoot', + 'jinxfuddy', + 'jinxfussen', + 'jinxgadget', + 'jinxgargle', + 'jinxgloop', + 'jinxglop', + 'jinxgoober', + 'jinxgoose', + 'jinxgrooven', + 'jinxhoffer', + 'jinxhopper', + 'jinxing', + 'jinxjinks', + 'jinxklunk', + 'jinxknees', + 'jinxmarble', + 'jinxmash', + 'jinxmonkey', + 'jinxmooch', + 'jinxmouth', + 'jinxmuddle', + 'jinxmuffin', + 'jinxmush', + 'jinxnerd', + 'jinxnoodle', + 'jinxnose', + 'jinxnugget', + 'jinxphew', + 'jinxphooey', + 'jinxpocket', + 'jinxpoof', + 'jinxpop', + 'jinxpounce', + 'jinxpow', + 'jinxpretzel', + 'jinxquack', + 'jinxroni', + 'jinxscooter', + 'jinxscreech', + 'jinxsmirk', + 'jinxsnooker', + 'jinxsnoop', + 'jinxsnout', + 'jinxsocks', + 'jinxspeed', + 'jinxspinner', + 'jinxsplat', + 'jinxsprinkles', + 'jinxsticks', + 'jinxstink', + 'jinxswirl', + 'jinxteeth', + 'jinxthud', + 'jinxtoes', + 'jinxton', + 'jinxtoon', + 'jinxtooth', + 'jinxtwist', + 'jinxwhatsit', + 'jinxwhip', + 'jinxwig', + 'jinxwoof', + 'jinxzaner', + 'jinxzap', + 'jinxzapper', + 'jinxzilla', + 'jinxzoom', + 'jitterbug', + 'jittery', + 'jive', + 'jive_turkies', + "jivin'", + 'jk', + 'joan', + 'job', + 'jobs', + 'jocard', + 'joe', + "joe's", + 'joes', + 'joey', + 'joff', + 'john', + 'johnny', + "johnny's", + 'johnnys', + 'johns', + 'johnson', + "johnson's", + 'johnsons', + 'johny', + 'join', + 'joined', + 'joiner', + 'joiners', + 'joining', + 'joins', + 'jojo', + "jojo's", + 'jojos', + 'joke', + "joke's", + 'joked', + 'joker', + "joker's", + 'jokers', + 'jokes', + 'jokey', + 'joking', + 'jolene', + 'jollibee', + 'jollibeefellowship', + 'jollibeeinfinite', + 'jollibeerewritten', + 'jolly', + "jolly's", + 'jollyroger', + "jollyroger's", + 'jollyrogers', + 'jolt', + "jona's", + 'jonas', + 'jonathan', + 'jones', + "jones'", + "jones's", + 'jonny', + 'jordan', + 'jos', + 'joseph', + 'josh', + 'joshamee', + 'joshsora', + 'joshua', + 'joshuas', + 'josie', + 'journal', + 'journals', + 'journes', + 'journey', + 'journeyed', + 'journeying', + 'journeyings', + 'joy', + "joy's", + 'joyce', + 'joyful', + 'joyous', + 'joys', + 'jp187187', + 'jpeg', + 'jpn', + 'jps', + 'jr', + 'jr.', + 'js', + 'juan', + 'jubilee', + 'judge', + "judge's", + 'judged', + 'judger', + 'judges', + 'judging', + 'judy', + "judy's", + 'judys', + 'juels', + 'juggernaut', + 'juggernauts', + 'juggle', + 'juggler', + 'juggles', + 'juggling', + 'juice', + 'juiced', + 'juju', + 'jukebox', + 'jul', + 'julep', + 'julie', + 'juliet', + 'julius', + 'july', + "july's", + 'julys', + 'jumba', + "jumba's", + 'jumble', + 'jumblebee', + 'jumbleberry', + 'jumbleblabber', + 'jumblebocker', + 'jumbleboing', + 'jumbleboom', + 'jumblebounce', + 'jumblebouncer', + 'jumblebrains', + 'jumblebubble', + 'jumblebumble', + 'jumblebump', + 'jumblebumper', + 'jumblechomp', + 'jumblecorn', + 'jumblecrash', + 'jumblecrumbs', + 'jumblecrump', + 'jumblecrunch', + 'jumbledoodle', + 'jumbledorf', + 'jumbleface', + 'jumblefidget', + 'jumblefink', + 'jumblefish', + 'jumbleflap', + 'jumbleflapper', + 'jumbleflinger', + 'jumbleflip', + 'jumbleflipper', + 'jumblefoot', + 'jumblefuddy', + 'jumblefussen', + 'jumblegadget', + 'jumblegargle', + 'jumblegloop', + 'jumbleglop', + 'jumblegoober', + 'jumblegoose', + 'jumblegrooven', + 'jumblehoffer', + 'jumblehopper', + 'jumblejinks', + 'jumbleklunk', + 'jumbleknees', + 'jumblemarble', + 'jumblemash', + 'jumblemonkey', + 'jumblemooch', + 'jumblemouth', + 'jumblemuddle', + 'jumblemuffin', + 'jumblemush', + 'jumblenerd', + 'jumblenoodle', + 'jumblenose', + 'jumblenugget', + 'jumblephew', + 'jumblephooey', + 'jumblepocket', + 'jumblepoof', + 'jumblepop', + 'jumblepounce', + 'jumblepow', + 'jumblepretzel', + 'jumblequack', + 'jumbleroni', + 'jumbles', + 'jumblescooter', + 'jumblescreech', + 'jumblesmirk', + 'jumblesnooker', + 'jumblesnoop', + 'jumblesnout', + 'jumblesocks', + 'jumblespeed', + 'jumblespinner', + 'jumblesplat', + 'jumblesprinkles', + 'jumblesticks', + 'jumblestink', + 'jumbleswirl', + 'jumbleteeth', + 'jumblethud', + 'jumbletoes', + 'jumbleton', + 'jumbletoon', + 'jumbletooth', + 'jumbletwist', + 'jumblewhatsit', + 'jumblewhip', + 'jumblewig', + 'jumblewoof', + 'jumblezaner', + 'jumblezap', + 'jumblezapper', + 'jumblezilla', + 'jumblezoom', + 'jumbo', + 'jump', + "jump's", + 'jumped', + 'jumper', + 'jumpers', + 'jumpin', + 'jumping', + 'jumps', + 'jumpy', + 'jun', + 'june', + "june's", + 'juneau', + 'junebug', + 'junehs', + 'junes', + 'jung', + 'jungle', + "jungle's", + 'jungled', + 'jungles', + 'junior', + "junior's", + 'juniors', + 'juniper', + 'junk', + 'jupiter', + "jupiter's", + 'jupiters', + 'jurassic', + 'jury', + 'just', + 'just-waking-up', + 'juster', + 'justice', + 'justin', + "justin's", + 'justing', + 'justins', + 'justly', + 'juvenile', + 'juvenilia', + 'k', + 'ka', + 'ka-boom', + 'ka-ching', + 'kabob', + 'kaboomery', + 'kagero', + 'kai', + 'kaken', + 'kamakuri', + 'kanga', + "kanga's", + 'kangaroo', + 'kangas', + 'kapahala', + 'karakuri', + 'karaoke', + 'karat', + 'karate', + 'karbay', + 'karen', + 'karin', + 'karma', + 'karnival', + 'karo', + "karo's", + 'kart', + 'karts', + 'kasumi', + 'kasumire', + 'kasumite', + 'kat', + 'katarina', + 'kate', + "kate's", + 'kathy', + "katie's", + 'katya', + 'katz', + 'kawaii', + 'kawasaki', + 'kay', + 'kazaam', + 'kazoo', + 'kazoology', + 'kdf', + 'keelgrin', + 'keely', + "keely's", + 'keelys', + 'keen', + 'keep', + 'keeper', + "keeper's", + 'keepers', + "keepin'", + 'keeping', + 'keeps', + 'keepsake', + "keepsake's", + 'keepsakes', + 'keira', + "keira's", + 'keiras', + 'keke', + 'kellogg', + "kellogg's", + 'kelloggs', + 'kelly', + "kelly's", + 'kellys', + 'kelp', + 'kelp-jelly', + 'kelsi', + "kelsi's", + 'kelsis', + 'kelvin', + 'ken', + 'kenai', + "kenai's", + 'kenais', + 'kennel', + 'kenneth', + 'kenny', + "kenny's", + 'kennys', + 'kent', + 'kept', + 'kerchak', + "kerchak's", + 'kerchaks', + 'kermie', + 'kermit', + 'kes', + 'kestred', + 'ketchup', + 'ketobasu', + 'kettle', + 'kettles', + 'kevin', + "kevin's", + 'kevinh', + 'kevins', + 'kevman95', + 'kewl', + 'key', + "key's", + 'keyboard', + "keyboard's", + 'keyboarding', + 'keyboards', + 'keyhole-design', + 'keys', + 'keystone', + 'keyword', + 'khaki', + "khaki's", + 'khakis', + 'khamsin', + 'ki', + 'kia', + 'kibatekka', + 'kick', + 'kickball', + 'kicked', + 'kicker', + 'kickers', + 'kickflip', + "kickin'", + 'kicking', + 'kicks', + 'kid', + "kid's", + 'kidd', + 'kiddie', + 'kidding', + 'kids', + 'kidstuff', + 'kiely', + "kiely's", + 'kielys', + 'kiki', + "kiki's", + 'kikis', + 'kikyo', + 'kill', + 'killed', + 'kim', + "kim's", + 'kimchi', + 'kimmunicator', + 'kims', + 'kind', + 'kinda', + 'kindergarten', + "kindergarten's", + 'kindergartens', + 'kindest', + 'kindle', + 'kindled', + 'kindles', + 'kindling', + 'kindly', + 'kindness', + 'kinds', + 'king', + "king's", + 'king-sized', + 'kingdom', + "kingdom's", + 'kingdoms', + 'kingfisher', + 'kingfishers', + 'kingly', + 'kingman', + "kingman's", + 'kings', + 'kingshead', + 'kinna', + 'kiosk', + "kiosk's", + 'kiosks', + 'kipp', + 'kippur', + 'kirby', + 'kirk', + 'kirke', + 'kit', + 'kitchen', + "kitchen's", + 'kitchener', + 'kitchens', + 'kite', + 'kites', + 'kitt', + 'kitten', + "kitten's", + 'kittens', + 'kitties', + 'kitty', + "kitty's", + 'kiwi', + 'kk', + 'klebba', + 'klutz', + 'klutzy', + 'knap', + 'knave', + 'knee', + 'kneed', + 'kneel', + 'knees', + 'knew', + 'knghts', + 'knight', + 'knightley', + "knightley's", + 'knights', + 'knitting', + 'knock', + 'knockdown', + 'knocked', + 'knocking', + 'knockoff', + 'knocks', + 'knoll', + 'knot', + 'knots', + 'knotty', + 'know', + 'know-it-alls', + 'knower', + 'knowing', + 'knowledge', + 'knowledgeable', + 'knowledges', + 'known', + 'knows', + 'knowzone', + 'knuckle', + 'knucklehead', + 'knuckles', + 'koala', + "koala's", + 'koalas', + 'kobi', + 'kobra', + 'koda', + "koda's", + 'kodas', + 'kodiac', + 'koenigsegg', + 'kokeshi', + 'koko', + 'kokoago', + 'kokoros', + 'koleniko', + 'kollin', + 'komadoros', + 'komainu', + 'komanoto', + 'kong', + "kong's", + 'kongs', + 'kooky', + 'kookybee', + 'kookyberry', + 'kookyblabber', + 'kookybocker', + 'kookyboing', + 'kookyboom', + 'kookybounce', + 'kookybouncer', + 'kookybrains', + 'kookybubble', + 'kookybumble', + 'kookybump', + 'kookybumper', + 'kookyburger', + 'kookychomp', + 'kookycorn', + 'kookycrash', + 'kookycrumbs', + 'kookycrump', + 'kookycrunch', + 'kookydoodle', + 'kookydorf', + 'kookyface', + 'kookyfidget', + 'kookyfink', + 'kookyfish', + 'kookyflap', + 'kookyflapper', + 'kookyflinger', + 'kookyflip', + 'kookyflipper', + 'kookyfoot', + 'kookyfuddy', + 'kookyfussen', + 'kookygadget', + 'kookygargle', + 'kookygloop', + 'kookyglop', + 'kookygoober', + 'kookygoose', + 'kookygrooven', + 'kookyhoffer', + 'kookyhopper', + 'kookyjinks', + 'kookyklunk', + 'kookyknees', + 'kookymarble', + 'kookymash', + 'kookymonkey', + 'kookymooch', + 'kookymouth', + 'kookymuddle', + 'kookymuffin', + 'kookymush', + 'kookynerd', + 'kookynoodle', + 'kookynose', + 'kookynugget', + 'kookyphew', + 'kookyphooey', + 'kookypocket', + 'kookypoof', + 'kookypop', + 'kookypounce', + 'kookypow', + 'kookypretzel', + 'kookyquack', + 'kookyroni', + 'kookyscooter', + 'kookyscreech', + 'kookysmirk', + 'kookysnooker', + 'kookysnoop', + 'kookysnout', + 'kookysocks', + 'kookyspeed', + 'kookyspinner', + 'kookysplat', + 'kookysprinkles', + 'kookysticks', + 'kookystink', + 'kookyswirl', + 'kookyteeth', + 'kookythud', + 'kookytoes', + 'kookyton', + 'kookytoon', + 'kookytooth', + 'kookytwist', + 'kookywhatsit', + 'kookywhip', + 'kookywig', + 'kookywoof', + 'kookyzaner', + 'kookyzap', + 'kookyzapper', + 'kookyzilla', + 'kookyzoom', + 'kool', + 'korogeki', + 'koroko', + 'korozama', + 'korra', + 'kouki', + 'kp', + "kp's", + 'kraken', + "kraken's", + 'krakens', + 'krawl', + 'krazy', + 'kreepers', + 'krew', + 'krewe', + 'krispies', + 'krissy', + 'kristin', + 'kristina', + 'krogager', + 'kronk', + "kronk's", + 'krunklehorn', + 'krux', + 'krybots', + 'ktta', + 'kubaku', + 'kuganon', + 'kugaster', + 'kumonn', + 'kun', + 'kung', + 'kuzco', + 'kwanzaa', + 'kyle', + "kyle's", + 'kyles', + 'kyra', + 'kyto', + "kyto's", + 'l8r', + 'la', + 'lab', + 'label', + 'labeled', + 'labeling', + 'labelled', + 'labelling', + 'labels', + 'labor', + 'labradoodle', + 'labradoodles', + 'labs', + 'labyrinth', + 'lace', + 'lack', + 'lackadaisical', + 'lacked', + 'lacker', + 'lacking', + 'lacks', + 'lacrosse', + 'lad', + 'lada', + 'ladder', + "ladder's", + 'ladders', + 'ladies', + "ladies'", + 'ladle', + 'ladles', + 'lady', + "lady's", + 'ladybug', + "ladybug's", + 'ladybugs', + 'ladys', + 'laff', + 'laff-o-dil', + 'laffer', + 'laffing', + 'laffs', + 'lag', + 'lagged', + 'laggin', + 'lagging', + 'laggy', + 'lagoon', + "lagoon's", + 'lagoons', + 'lags', + 'laid-back', + 'laidel', + 'lake', + "lake's", + 'laker', + 'lakes', + 'laking', + 'lala', + 'lalala', + 'lamanai', + 'lamb', + 'lambda', + 'lamberginias', + 'lamborghini', + 'lame', + 'lamed', + 'lamely', + 'lamer', + 'lames', + 'lamest', + 'laming', + 'lamp', + "lamp's", + 'lamper', + 'lamps', + 'lana', + 'lanai', + 'lance', + "lance's", + 'lances', + 'lancia', + 'land', + 'landa', + 'landed', + 'lander', + 'landers', + 'landing', + 'landings', + 'landlubber', + 'landlubbers', + 'landmark', + 'lands', + 'landscape', + 'lane', + 'lanes', + 'language', + "language's", + 'languages', + 'lantern', + "lantern's", + 'lanterns', + 'lanyard', + "lanyard's", + 'lanyards', + 'laptop', + 'large', + 'lark', + 'larkspur', + 'larp', + 'larrup', + 'larry', + 'lars', + 'laser', + 'lashes', + 'lass', + "lass'", + 'lassard', + 'lassie', + "lassie's", + 'lassies', + 'lasso', + 'last', + 'lasted', + 'laster', + 'lasting', + 'lastly', + 'lasts', + 'late', + 'lated', + 'lately', + 'later', + 'lateral', + 'latered', + 'laters', + 'lates', + 'latest', + 'latia', + 'latin', + 'latrine', + 'latte', + 'laugh', + 'laughable', + 'laughed', + 'laugher', + "laugher's", + 'laughers', + 'laughfest', + "laughin'", + 'laughing', + 'laughs', + 'laughter', + 'laughters', + 'launch', + 'launched', + 'launcher', + 'launchers', + 'launches', + 'launching', + 'launchings', + 'launchpad', + 'laundry', + 'laura', + 'laurel', + 'laurel-leaf', + 'lauren', + 'lava', + 'lavendar', + 'lavender', + 'lavish', + 'law', + "law's", + 'lawbot', + "lawbot's", + 'lawbots', + 'lawful', + 'lawless', + 'lawn', + "lawn's", + 'lawns', + 'lawrence', + 'laws', + 'lawyer', + 'lawyers', + 'layer', + 'layered', + 'layers', + 'laying', + 'laziness', + 'lazy', + 'lbhq', + 'le', + 'lead', + 'leaded', + 'leaden', + 'leader', + "leader's", + 'leaderboard', + "leaderboard's", + 'leaderboards', + 'leaders', + 'leadership', + 'leading', + 'leadings', + 'leads', + 'leaf', + "leaf's", + 'leaf-boat', + 'leaf-stack', + 'leafboarding', + 'leafed', + 'leafing', + 'leafkerchief', + 'leafkerchiefs', + 'leafs', + 'leafy', + 'league', + 'leagued', + 'leaguer', + 'leaguers', + 'leagues', + 'leaguing', + 'leaks', + 'lean', + 'leaned', + 'leaner', + 'leanest', + 'leaning', + 'leanings', + 'leanly', + 'leans', + 'leap', + 'leapfrog', + 'leaping', + 'leapt', + 'learn', + 'learned', + 'learner', + "learner's", + 'learners', + 'learning', + 'learnings', + 'learns', + 'learnt', + 'least', + 'leatherneck', + 'leave', + 'leaved', + 'leaver', + 'leavers', + 'leaves', + 'leavin', + 'leaving', + 'leavings', + 'ledge', + 'lee', + 'leed', + 'leeta', + 'leeward', + 'left', + 'left-click', + 'left-clicking', + 'leftover', + 'lefts', + 'lefty', + 'leg', + 'legaba', + 'legaja', + 'legal', + 'legalese', + 'legano', + 'legassa', + 'legen', + 'legend', + "legend's", + 'legendary', + 'legends', + 'leghorn', + 'legion', + 'legit', + 'lego', + 'legondary', + 'legs', + 'leibovitz', + "leibovitz's", + 'leif', + 'leigons', + 'leisure', + 'leisurely', + 'lel', + 'lemme', + 'lemon', + 'lemonade', + 'lemonbee', + 'lemonberry', + 'lemonblabber', + 'lemonbocker', + 'lemonboing', + 'lemonboom', + 'lemonbounce', + 'lemonbouncer', + 'lemonbrains', + 'lemonbubble', + 'lemonbumble', + 'lemonbump', + 'lemonbumper', + 'lemonburger', + 'lemonchomp', + 'lemoncorn', + 'lemoncrash', + 'lemoncrumbs', + 'lemoncrump', + 'lemoncrunch', + 'lemondoodle', + 'lemondorf', + 'lemonface', + 'lemonfidget', + 'lemonfink', + 'lemonfish', + 'lemonflap', + 'lemonflapper', + 'lemonflinger', + 'lemonflip', + 'lemonflipper', + 'lemonfoot', + 'lemonfuddy', + 'lemonfussen', + 'lemongadget', + 'lemongargle', + 'lemongloop', + 'lemonglop', + 'lemongoober', + 'lemongoose', + 'lemongrooven', + 'lemonhoffer', + 'lemonhopper', + 'lemonjinks', + 'lemonklunk', + 'lemonknees', + 'lemonmarble', + 'lemonmash', + 'lemonmonkey', + 'lemonmooch', + 'lemonmouth', + 'lemonmuddle', + 'lemonmuffin', + 'lemonmush', + 'lemonnerd', + 'lemonnoodle', + 'lemonnose', + 'lemonnugget', + 'lemonphew', + 'lemonphooey', + 'lemonpocket', + 'lemonpoof', + 'lemonpop', + 'lemonpounce', + 'lemonpow', + 'lemonpretzel', + 'lemonquack', + 'lemonroni', + 'lemons', + 'lemonscooter', + 'lemonscreech', + 'lemonsmirk', + 'lemonsnooker', + 'lemonsnoop', + 'lemonsnout', + 'lemonsocks', + 'lemonspeed', + 'lemonspinner', + 'lemonsplat', + 'lemonsprinkles', + 'lemonsticks', + 'lemonstink', + 'lemonswirl', + 'lemonteeth', + 'lemonthud', + 'lemontoes', + 'lemonton', + 'lemontoon', + 'lemontooth', + 'lemontwist', + 'lemonwhatsit', + 'lemonwhip', + 'lemonwig', + 'lemonwoof', + 'lemony', + 'lemonzaner', + 'lemonzap', + 'lemonzapper', + 'lemonzilla', + 'lemonzoom', + 'lempago', + 'lempona', + 'lempos', + 'len', + 'lend', + 'lender', + 'lenders', + 'lending', + 'lends', + 'lengendary', + 'length', + 'lengthen', + 'lengths', + 'lenient', + 'lenny', + 'lenon', + 'lenora', + 'lentil', + 'leo', + "leo's", + 'leon', + 'leonardo', + 'leons', + 'leopard', + 'leopards', + 'leopuba', + 'leota', + "leota's", + 'leotas', + 'leozar', + 'leroy', + 'lerping', + 'les', + 'less', + 'lessen', + 'lessens', + 'lesser', + 'lesses', + 'lessing', + 'lesson', + 'lessoners', + 'lessons', + 'lest', + 'let', + "let's", + 'lethargy', + 'lets', + 'letter', + 'letterhead', + 'lettering', + 'letterman', + 'letters', + "lettin'", + 'letting', + 'lettuce', + 'level', + 'leveling', + 'levelly', + 'levels', + 'levelup', + 'leviathan', + 'levica', + 'levy', + 'lewis', + "lewis'", + "lex's", + 'lexi', + 'lexus', + 'li', + 'liar', + "liar's", + 'liars', + 'libby', + 'liberal', + 'liberally', + 'liberated', + 'liberties', + 'liberty', + "liberty's", + 'librarian', + 'libraries', + 'library', + "library's", + 'licence', + 'license', + 'lichen', + 'lichens', + 'licorice', + 'lid', + 'lie', + 'lied', + 'lies', + 'lieutenant', + "lieutenant's", + 'lieutenants', + 'life', + "life's", + 'lifeguard', + "lifeguard's", + 'lifeguards', + 'lifejacket', + 'lifeless', + 'lifelong', + 'lifer', + 'lifers', + 'lifes', + 'lifestyle', + 'lift', + 'lifted', + 'lifter', + 'lifters', + 'lifting', + 'lifts', + 'light', + "light's", + 'light-green', + 'light-t', + 'light-talent', + 'light-talents', + 'light-up', + 'lightbeams', + 'lightcycle', + 'lightcycles', + 'lighted', + 'lighten', + 'lightening', + 'lightens', + 'lighter', + 'lighters', + 'lightest', + 'lightfinders', + 'lighthouse', + "lighthouse's", + 'lighthouses', + 'lighting', + 'lightly', + 'lightning', + 'lights', + 'lightspeed', + 'lightwater', + 'lightyear', + "lightyear's", + 'like', + 'likeable', + 'liked', + 'likelier', + 'likeliest', + 'likelihood', + 'likely', + 'likes', + 'likest', + 'likewise', + 'liki', + 'liking', + 'likings', + 'lil', + "lil'fairy", + 'lila', + 'lilac', + 'lilies', + 'lillipop', + 'lilly', + 'lilo', + "lilo's", + 'lily', + "lily's", + 'lily-of-the-valley', + 'lily-pad', + 'lilypad', + 'lilys', + 'lima', + 'lime', + 'limes', + 'limit', + 'limited', + 'limiter', + 'limiters', + 'limiting', + 'limitly', + 'limitness', + 'limits', + 'lincoln', + "lincoln's", + 'lincolns', + 'linda', + 'linden', + 'line', + "line's", + 'lined', + 'linen', + 'linens', + 'liner', + "liner's", + 'liners', + 'lines', + 'lingering', + 'linguini', + "linguini's", + 'linguinis', + 'lining', + 'linings', + 'link', + 'links', + 'lion', + "lion's", + 'liona', + 'lione', + 'lions', + 'lip', + 'lipsky', + 'lipstick', + 'lipsticks', + 'liquidate', + 'liri', + 'lisa', + 'lisel', + 'list', + 'listed', + 'listen', + 'listened', + 'listener', + "listener's", + 'listeners', + 'listening', + 'listens', + 'lister', + 'listers', + 'listing', + 'listings', + 'listners', + 'lists', + 'lit', + 'literal', + 'literally', + 'literature', + 'little', + 'littler', + 'littlest', + 'live', + 'live-action', + 'lived', + 'lively', + 'livens', + 'liver', + "liver's", + 'livered', + 'livers', + 'lives', + 'livest', + 'livestream', + 'livestreaming', + 'livestreams', + 'liveth', + 'living', + 'livingly', + 'livings', + 'livingston', + "livingston's", + 'livingstons', + 'liz', + 'liza', + 'lizard', + "lizard's", + 'lizards', + 'lizzie', + "lizzie's", + 'lizzy', + 'llama', + "llama's", + 'llamas', + 'lloyd', + "lloyd's", + 'lloyds', + 'lml', + 'load', + 'loaded', + 'loader', + 'loaders', + 'loading', + 'loadings', + 'loafers', + 'loan', + 'loaned', + 'loaner', + 'loaning', + 'loans', + 'loather', + 'lobbies', + 'lobby', + 'lobe', + 'lobster', + 'lobsters', + 'local', + 'localized', + 'locally', + 'location', + 'locations', + 'lock', + 'lockbox', + 'lockboxes', + 'locked', + 'locker', + 'lockers', + 'locket', + 'locking', + 'lockings', + "lockjaw's", + 'lockpick', + 'locks', + "lockspinner's", + 'loco-motion', + 'lodge', + "lodge's", + 'lodges', + 'lofty', + 'log', + "log's", + 'logan', + 'logged', + 'loggers', + 'logging', + 'logic', + 'logical', + 'logout', + 'logs', + 'lol', + 'lola', + "lola's", + 'loled', + 'loling', + 'lolipop', + 'lollipop', + 'lolo', + "lolo's", + 'lolos', + 'lolz', + 'lone', + 'lonelier', + 'loneliest', + 'loneliness', + 'lonely', + 'lonepirates', + 'loner', + "loner's", + 'loners', + 'long', + 'longboard', + 'longboards', + 'longed', + 'longer', + 'longest', + 'longing', + 'longings', + 'longjohn', + 'longly', + 'longs', + 'longskirt', + 'lonick', + 'loo', + 'look', + 'looked', + 'looker', + "looker's", + 'lookers', + 'lookin', + "lookin'", + 'looking', + 'lookout', + 'lookouts', + 'looks', + 'looksee', + 'lool', + 'loom', + 'loon', + 'loony', + 'loool', + 'looool', + 'looooong', + 'loop', + 'loop.', + 'loopenbee', + 'loopenberry', + 'loopenblabber', + 'loopenbocker', + 'loopenboing', + 'loopenboom', + 'loopenbounce', + 'loopenbouncer', + 'loopenbrains', + 'loopenbubble', + 'loopenbumble', + 'loopenbump', + 'loopenbumper', + 'loopenburger', + 'loopenchomp', + 'loopencorn', + 'loopencrash', + 'loopencrumbs', + 'loopencrump', + 'loopencrunch', + 'loopendoodle', + 'loopendorf', + 'loopenface', + 'loopenfidget', + 'loopenfink', + 'loopenfish', + 'loopenflap', + 'loopenflapper', + 'loopenflinger', + 'loopenflip', + 'loopenflipper', + 'loopenfoot', + 'loopenfuddy', + 'loopenfussen', + 'loopengadget', + 'loopengargle', + 'loopengloop', + 'loopenglop', + 'loopengoober', + 'loopengoose', + 'loopengrooven', + 'loopenhoffer', + 'loopenhopper', + 'loopenjinks', + 'loopenklunk', + 'loopenknees', + 'loopenmarble', + 'loopenmash', + 'loopenmonkey', + 'loopenmooch', + 'loopenmouth', + 'loopenmuddle', + 'loopenmuffin', + 'loopenmush', + 'loopennerd', + 'loopennoodle', + 'loopennose', + 'loopennugget', + 'loopenphew', + 'loopenphooey', + 'loopenpocket', + 'loopenpoof', + 'loopenpop', + 'loopenpounce', + 'loopenpow', + 'loopenpretzel', + 'loopenquack', + 'loopenroni', + 'loopenscooter', + 'loopenscreech', + 'loopensmirk', + 'loopensnooker', + 'loopensnoop', + 'loopensnout', + 'loopensocks', + 'loopenspeed', + 'loopenspinner', + 'loopensplat', + 'loopensprinkles', + 'loopensticks', + 'loopenstink', + 'loopenswirl', + 'loopenteeth', + 'loopenthud', + 'loopentoes', + 'loopenton', + 'loopentoon', + 'loopentooth', + 'loopentwist', + 'loopenwhatsit', + 'loopenwhip', + 'loopenwig', + 'loopenwoof', + 'loopenzaner', + 'loopenzap', + 'loopenzapper', + 'loopenzilla', + 'loopenzoom', + 'loops', + 'loopy', + 'lopsided', + 'lord', + "lord's", + 'lords', + 'lordz', + 'lore', + 'lorella', + 'lorenzo', + 'lori', + 'los', + 'lose', + 'loses', + 'losing', + 'loss', + "loss's", + 'losses', + 'lost', + 'lot', + "lot's", + 'lots', + 'lotsa', + 'lotus', + 'lou', + 'loud', + 'louder', + 'loudest', + 'loudly', + 'loudrob', + 'louie', + "louie's", + 'louies', + 'louis', + "louis'", + 'lounge', + 'lounged', + 'lounger', + 'lounges', + 'lounging', + 'lousy', + 'lovable', + 'love', + "love's", + 'loved', + 'lovel', + 'lovelier', + 'lovelies', + 'loveliest', + 'loveliness', + 'lovely', + 'loves', + 'loveseat', + 'low', + 'lowbrow', + 'lowdenclear', + 'lowdown', + 'lower', + 'lowered', + 'lowers', + 'lowest', + 'lowing', + 'lowly', + 'lows', + 'loyal', + 'loyalty', + 'lozenge', + 'lozenges', + 'lt.', + 'ltns', + 'luau', + "luau's", + 'luaus', + 'luc', + "luc's", + 'lucas', + "lucas'", + 'lucia', + 'luciano', + 'lucille', + 'lucinda', + 'luck', + 'lucked', + 'lucks', + 'lucky', + "lucky's", + 'luckys', + 'lucs', + 'lucy', + "lucy's", + 'lucys', + 'luff', + 'luffy', + 'lug-nut', + 'luge', + 'luggage', + 'luigi', + "luigi's", + 'luke', + 'lul', + 'lulla-squeak', + 'lullaby', + 'lulu', + 'lumber', + 'lumen', + 'lumens', + 'lumiere', + "lumiere's", + 'luminous', + 'luna', + 'lunar', + 'lunatics', + 'lunch', + 'lunched', + 'luncher', + 'lunches', + 'lunching', + 'lunge', + 'lunge-n-plunge', + 'lupine', + 'lure', + 'lured', + 'lureless', + 'lures', + 'luring', + 'lurk', + 'lurked', + 'lurking', + 'lute', + 'lutes', + 'luther', + "luther's", + 'luthers', + 'luxe', + 'luxury', + 'lv', + 'lv8', + 'lvl', + 'lye', + 'lying', + 'lympia', + 'lynn', + 'lynx', + 'lynxes', + 'lyre', + 'lyric', + 'lyrical', + 'lyrics', + 'm8', + 'ma', + "ma'am", + 'mac', + "mac's", + 'macaroons', + 'macbee', + 'macberry', + 'macblabber', + 'macbocker', + 'macboing', + 'macbook', + 'macbooks', + 'macboom', + 'macbounce', + 'macbouncer', + 'macbrains', + 'macbubble', + 'macbumble', + 'macbump', + 'macbumper', + 'macburger', + 'macchomp', + 'maccorn', + 'maccrash', + 'maccrumbs', + 'maccrump', + 'maccrunch', + 'macdoodle', + 'macdorf', + 'macface', + 'macfidget', + 'macfink', + 'macfish', + 'macflap', + 'macflapper', + 'macflinger', + 'macflip', + 'macflipper', + 'macfoot', + 'macfuddy', + 'macfussen', + 'macgadget', + 'macgargle', + 'macgloop', + 'macglop', + 'macgoober', + 'macgoose', + 'macgrooven', + 'machine', + "machine's", + 'machined', + 'machines', + 'machining', + 'macho', + 'machoffer', + 'machopper', + 'macjinks', + "mack's", + 'mackerel', + 'macklemore', + 'macklunk', + 'macknees', + 'macks', + "macmalley's", + 'macmarble', + 'macmash', + 'macmonkey', + 'macmooch', + 'macmouth', + 'macmuddle', + 'macmuffin', + 'macmush', + 'macnerd', + 'macnoodle', + 'macnose', + 'macnugget', + 'macomo', + 'macphew', + 'macphooey', + 'macpocket', + 'macpoof', + 'macpop', + 'macpounce', + 'macpow', + 'macpretzel', + 'macquack', + 'macro', + 'macroni', + 'macs', + 'macscooter', + 'macscreech', + 'macsmirk', + 'macsnooker', + 'macsnoop', + 'macsnout', + 'macsocks', + 'macspeed', + 'macspinner', + 'macsplat', + 'macsprinkles', + 'macsticks', + 'macstink', + 'macswirl', + 'macteeth', + 'macthud', + 'mactoes', + 'macton', + 'mactoon', + 'mactooth', + 'mactwist', + 'macwhatsit', + 'macwhip', + 'macwig', + 'macwoof', + 'maczaner', + 'maczap', + 'maczapper', + 'maczilla', + 'maczoom', + 'mad', + 'madcap', + 'maddie', + "maddie's", + 'maddies', + 'made', + 'madge', + 'madison', + "madison's", + 'madisons', + 'madly', + 'madness', + 'madrigal', + 'mads', + 'maelstrom', + "maelstrom's", + 'maelstroms', + 'maestro', + 'maestros', + 'magazine', + "magazine's", + 'magazines', + 'magenta', + 'maggie', + "maggie's", + 'maggies', + 'magic', + 'magical', + 'magically', + 'magicians', + 'magna', + 'magnet', + "magnet's", + 'magnets', + 'magnificent', + 'magnolia', + 'magoo', + "magoo's", + 'magpie', + 'mahalo', + 'mahogany', + 'maiara', + "maiara's", + 'maiaras', + 'maid', + 'mail', + 'mailbox', + 'mailboxes', + 'main', + 'mainland', + 'mainly', + 'mains', + 'maintain', + 'maintained', + 'maintainer', + 'maintainers', + 'maintaining', + 'maintains', + 'maintenance', + 'maize', + 'maja', + 'majestic', + 'majesty', + 'major', + "major's", + 'majored', + 'majoring', + 'majorities', + 'majority', + "majority's", + 'majors', + 'makadoros', + 'makanoto', + 'makanui', + 'make', + 'make-a-pirate', + 'make-a-wish', + 'make-up', + 'makeovers', + 'maker', + 'makers', + 'makes', + 'making', + 'maladies', + 'male', + 'maleficent', + "maleficent's", + 'malevolo', + 'malicious', + 'maliciously', + 'malik', + 'malina', + "malina's", + 'malinas', + 'mall', + 'mallet', + 'malley', + 'malt', + 'malware', + 'mama', + "mama's", + 'mamba', + 'mambas', + 'mammoth', + 'man', + "man's", + 'man-o-war', + 'man-o-wars', + 'mana', + 'manage', + 'managed', + 'management', + 'manager', + "manager's", + 'managers', + 'manages', + 'managing', + 'manatees', + 'mancala', + 'mandolin', + 'mandolins', + 'mandy', + 'mane', + 'maneuver', + 'maneuverable', + 'maneuvered', + 'maneuvering', + 'maneuvers', + 'manga', + 'mango', + 'mania', + 'maniac', + 'manicuranda', + 'manner', + 'mannered', + 'mannerly', + 'manners', + 'manny', + "manny's", + 'mannys', + 'mans', + 'mansion', + "mansion's", + 'mansions', + 'mansuetude', + 'mantle', + 'mantrador', + 'mantradora', + 'mantrados', + "manu's", + 'manual', + 'manually', + 'manuals', + 'many', + 'map', + "map's", + 'maple', + 'mapleseed', + 'mapped', + 'mapping', + 'maps', + 'mar', + 'mara', + 'marathon', + "marathon's", + 'marathons', + 'marble', + "marble's", + 'marbled', + 'marbler', + 'marbles', + 'marbling', + 'marc', + 'march', + 'marches', + 'marching', + 'marcooo', + 'mardi', + 'margaret', + 'marge', + 'margo', + 'maria', + 'marigold', + 'marigolds', + 'marine', + 'mariner', + "mariner's", + 'mariners', + "mariners'", + 'marines', + 'mario', + "mario's", + 'marios', + 'marissa', + 'mark', + "mark's", + 'marked', + 'marker', + 'market', + "market's", + 'marketed', + 'marketer', + 'marketing', + 'marketings', + 'marketplace', + 'markets', + 'markgasus', + 'marking', + 'markintosh', + 'marks', + 'marksman', + 'marksmen', + 'marlin', + "marlin's", + 'marlins', + 'maroni', + 'maroon', + 'marooned', + "marooner's", + 'marooning', + 'maroons', + 'marque', + 'marquis', + 'marrow-mongers', + 'mars', + 'marsh', + 'marshall', + "marshall's", + 'marshmallow', + 'mart', + 'martha', + 'martin', + "martin's", + 'martinaba', + 'martinez', + 'martins', + 'marty', + "marty's", + 'maruaders', + 'marvel', + 'marveled', + 'marveling', + 'marvelled', + 'marvelling', + 'marvelous', + 'marvelously', + 'marvels', + 'mary', + "mary's", + 'marzi', + 'mascara', + 'mascot', + 'maserati', + 'maserobo', + 'masetosu', + 'masetto', + 'mash', + 'mashed', + 'mask', + 'masodoodle', + 'mass', + 'massey', + 'massive', + 'mast', + 'master', + "master's", + 'mastered', + 'mastering', + 'masterings', + 'masterly', + 'masterpiece', + 'masters', + 'mastervoltage', + 'mastervolty', + 'mastery', + 'mat', + 'matata', + 'match', + 'match-up', + 'matched', + 'matcher', + 'matchers', + 'matches', + 'matching', + 'matchings', + 'mate', + 'mater', + "mater's", + 'material', + "material's", + 'materialistic', + 'materialize', + 'materially', + 'materials', + 'mates', + 'matey', + 'mateys', + 'math', + 'maties', + 'matilda', + "matilda's", + 'matriarch', + 'matrix', + 'matt', + "matt's", + 'matter', + 'mattered', + 'matterhorn', + "matterhorn's", + 'mattering', + 'matters', + 'matthew', + 'mature', + "maurader's", + 'mauraders', + 'max', + 'maxed', + 'maximum', + 'maximus', + 'maxing', + 'maxxed', + 'may', + 'maya', + 'mayada', + 'mayano', + 'maybe', + 'mayday', + 'mayhem', + 'mayigos', + 'mayo', + 'mayola', + 'mayonnaise', + 'mayor', + 'mazda', + 'maze', + 'mazers', + 'mazes', + 'mc', + 'mcbee', + 'mcberry', + 'mcblabber', + 'mcbocker', + 'mcboing', + 'mcboom', + 'mcbounce', + 'mcbouncer', + 'mcbrains', + 'mcbubble', + 'mcbumble', + 'mcbump', + 'mcbumper', + 'mcburger', + 'mccartney', + "mccartney's", + 'mcchomp', + 'mccorn', + 'mccraken', + 'mccrash', + 'mccrumbs', + 'mccrump', + 'mccrunch', + 'mcdoodle', + 'mcdorf', + 'mcduck', + "mcduck's", + 'mcf', + 'mcface', + 'mcfidget', + 'mcfink', + 'mcfish', + 'mcflap', + 'mcflapper', + 'mcflinger', + 'mcflip', + 'mcflipper', + 'mcfoot', + 'mcfuddy', + "mcfury's", + 'mcfussen', + 'mcgadget', + 'mcgargle', + 'mcghee', + 'mcgloop', + 'mcglop', + 'mcgoober', + 'mcgoose', + 'mcgreeny', + 'mcgrooven', + 'mcguire', + "mcguire's", + 'mchoffer', + 'mchopper', + 'mcintosh', + 'mcjinks', + 'mckee', + 'mcklunk', + 'mcknees', + 'mcmarble', + 'mcmash', + 'mcmonkey', + 'mcmooch', + 'mcmouth', + 'mcmuddle', + 'mcmuffin', + 'mcmuggin', + 'mcmush', + 'mcnerd', + 'mcnoodle', + 'mcnose', + 'mcnugget', + 'mcp', + 'mcphew', + 'mcphooey', + 'mcpocket', + 'mcpoof', + 'mcpop', + 'mcpounce', + 'mcpow', + 'mcpretzel', + 'mcq', + 'mcquack', + 'mcqueen', + "mcqueen's", + 'mcreary-timereary', + 'mcreedy', + 'mcroni', + 'mcscooter', + 'mcscreech', + 'mcshoe', + 'mcsmirk', + 'mcsnooker', + 'mcsnoop', + 'mcsnout', + 'mcsocks', + 'mcspeed', + 'mcspinner', + 'mcsplat', + 'mcsprinkles', + 'mcsticks', + 'mcstink', + 'mcswirl', + 'mcteeth', + 'mcthud', + 'mctoes', + 'mcton', + 'mctoon', + 'mctooth', + 'mctwist', + 'mcwhatsit', + 'mcwhip', + 'mcwig', + 'mcwoof', + 'mczaner', + 'mczap', + 'mczapper', + 'mczilla', + 'mczoom', + 'mdt', + 'me', + 'me-self', + 'meadow', + 'meadows', + 'meal', + "meal's", + 'meals', + 'mean', + 'meander', + 'meaner', + 'meanest', + 'meanie', + 'meanies', + 'meaning', + "meaning's", + 'meanings', + 'meanly', + 'meanness', + 'means', + 'meant', + 'meantime', + 'meanwhile', + 'measure', + 'measured', + 'measurer', + 'measures', + 'measuring', + 'mechanic', + 'mechanical', + 'mechanics', + 'mechanism', + 'mechano-duster', + 'med', + 'medal', + "medal's", + 'medallion', + 'medals', + 'meddle', + 'meddles', + 'meddling', + 'media', + "media's", + 'medias', + 'medic', + 'medical', + 'medically', + 'medicine', + 'medicines', + 'meditate', + 'medium', + "medium's", + 'mediums', + 'medley', + 'medly', + 'meena', + "meena's", + 'meenah', + 'meenas', + 'meep', + 'meet', + 'meeting', + 'meg', + 'mega', + 'mega-cool', + 'mega-rad', + 'mega-rific', + 'megabee', + 'megaberry', + 'megablabber', + 'megabocker', + 'megaboing', + 'megaboom', + 'megabounce', + 'megabouncer', + 'megabrains', + 'megabubble', + 'megabumble', + 'megabump', + 'megabumper', + 'megaburger', + 'megachomp', + 'megacorn', + 'megacrash', + 'megacrumbs', + 'megacrump', + 'megacrunch', + 'megadoodle', + 'megadorf', + 'megaface', + 'megafidget', + 'megafink', + 'megafish', + 'megaflap', + 'megaflapper', + 'megaflinger', + 'megaflip', + 'megaflipper', + 'megafoot', + 'megafuddy', + 'megafussen', + 'megagadget', + 'megagargle', + 'megagloop', + 'megaglop', + 'megagoober', + 'megagoose', + 'megagrooven', + 'megahoffer', + 'megahopper', + 'megahot', + 'megajinks', + 'megaklunk', + 'megaknees', + 'megamagic', + 'megamarble', + 'megamash', + 'megamix', + 'megamonkey', + 'megamooch', + 'megamouth', + 'megamuddle', + 'megamuffin', + 'megamush', + 'megan', + 'meganerd', + 'meganoodle', + 'meganose', + 'meganugget', + 'megaphew', + 'megaphone', + 'megaphones', + 'megaphooey', + 'megaplay', + 'megapocket', + 'megapoof', + 'megapop', + 'megapounce', + 'megapow', + 'megapretzel', + 'megaquack', + 'megaroni', + 'megascooter', + 'megascreech', + 'megashare', + 'megasmirk', + 'megasnooker', + 'megasnoop', + 'megasnout', + 'megasocks', + 'megaspeed', + 'megaspinner', + 'megasplat', + 'megasprinkles', + 'megasticks', + 'megastink', + 'megaswirl', + 'megateeth', + 'megathud', + 'megatoes', + 'megaton', + 'megatoon', + 'megatooth', + 'megatwist', + 'megawatch', + 'megawhatsit', + 'megawhip', + 'megawig', + 'megawoof', + 'megazaner', + 'megazap', + 'megazapper', + 'megazilla', + 'megazoom', + 'megazord', + 'meghan', + 'meh', + 'mehmet', + 'meido', + 'mel', + 'melanie', + 'melee', + 'melekalikimaka', + 'mello', + 'mellow', + 'mellowed', + 'mellower', + 'mellowing', + 'mellows', + 'melodic', + 'melody', + 'melodyland', + 'melt', + 'meltdown', + 'melted', + 'melting', + 'melville', + "melville's", + 'mem', + 'member', + "member's", + 'membered', + 'members', + 'membership', + "membership's", + 'memberships', + 'meme', + 'memes', + 'memo', + 'memorial', + "memorial's", + 'memorials', + 'memories', + 'memorise', + 'memory', + "memory's", + 'memos', + 'men', + "men's", + 'menagerie', + "menagerie's", + 'menageries', + 'mending', + 'menorah', + "menorah's", + 'menorahs', + 'menswear', + 'mental', + 'mentally', + 'mention', + 'mentioned', + 'mentioner', + 'mentioners', + 'mentioning', + 'mentions', + 'mentius', + 'mentor', + 'mentors', + 'menu', + "menu's", + 'menus', + 'meow', + 'merc', + 'mercantile', + "mercantile's", + 'mercedes', + 'mercedes-benz', + 'mercenaries', + 'mercenary', + 'mercenarys', + 'merchandise', + 'merchant', + "merchant's", + 'merchants', + 'merchantsrevenge', + 'merci', + 'merciless', + 'mercs', + 'mercury', + "mercury's", + 'mercy', + 'merigold', + 'merigolds', + 'merik', + 'merit', + 'merits', + 'mermaid', + "mermaid's", + 'mermaids', + 'mermain', + 'mermish', + 'merry', + 'merrychristmas', + "merryweather's", + 'merryweathers', + 'mership', + 'mertle', + "mertle's", + 'mertles', + 'mesa', + 'mesabone', + 'mesathorn', + 'mesmerizing', + 'mess', + 'message', + "message's", + 'messaged', + 'messages', + 'messaging', + 'messed', + 'messenger', + 'messes', + 'messing', + 'messy', + 'met', + 'metabolism', + 'metal', + "metal's", + 'metals', + 'meteor', + 'meter', + 'meters', + 'method', + "method's", + 'methodical', + 'methods', + 'metra', + 'metre', + 'metroville', + 'mettle', + 'mew', + 'mexico', + 'mezzo', + 'mgm', + "mgm's", + 'mgr', + 'mgracer', + 'mgracer48', + 'mhm', + 'mibbit', + 'mic', + 'mice', + 'michael', + "michael's", + 'michaels', + 'michalka', + "michalka's", + 'michalkas', + 'michelle', + 'mickes', + 'mickey', + "mickey's", + 'mickeys', + 'micro', + 'micromanager', + 'micromanagers', + 'microphone', + "microphone's", + 'microphones', + 'microsoft', + 'middle', + 'middled', + 'middler', + 'middles', + 'middling', + 'middlings', + 'midnight', + 'midsummer', + 'midwaymarauders', + 'mies', + 'might', + 'mights', + 'mighty', + 'migos', + 'migrator', + 'mike', + "mike's", + 'mikeizepic', + 'mikes', + 'mikey', + "mikey's", + 'milan', + 'mild', + 'milden', + 'milder', + 'mildest', + 'mildly', + 'mile', + "mile's", + 'miler', + 'miles', + 'miley', + "miley's", + 'milian', + "milian's", + 'military', + 'militia', + 'milk', + 'milks', + 'milkweed', + 'mill', + "mill's", + 'miller', + 'millie', + "millie's", + 'million', + "million's", + 'millions', + 'mills', + 'milo', + "milo's", + 'milos', + 'milton', + "mim's", + 'mimes', + 'mimetoon', + 'mimic', + "mimic's", + 'mimics', + 'mims', + 'min', + 'min.', + 'mina', + 'mincemeat', + 'mind', + 'mind-blowing', + 'minded', + 'minder', + "minder's", + 'minders', + 'minding', + 'minds', + 'mindy', + 'mine', + 'mine-train', + 'mined', + 'miner', + "miner's", + 'mineral', + 'minerals', + 'miners', + 'minerva', + 'mines', + 'ming', + "ming's", + 'mingler', + 'minglers', + 'mings', + 'mini', + 'miniature', + 'miniblind', + 'miniblinds', + 'minigame', + 'minigames', + 'minigolf', + 'minimum', + 'mining', + 'mining-talent', + 'minion', + "minion's", + 'minions', + 'minipumpkins', + 'minister', + 'ministry', + 'mink', + "mink's", + 'minks', + 'minnie', + "minnie's", + 'minnies', + 'minnow', + 'minny', + "minny's", + 'minnys', + 'minor', + 'minotaur', + "minotaur's", + 'minotaurs', + 'mins', + 'mint', + "mint's", + 'mints', + 'minty', + 'minus', + 'minute', + 'minuted', + 'minutely', + 'minuter', + 'minutes', + 'minutest', + 'minuting', + 'miracle', + 'miracles', + 'miranda', + "miranda's", + 'mirandas', + 'miraz', + "miraz's", + 'mirazs', + 'mire', + 'mires', + 'mirror', + "mirror's", + 'mirrors', + 'mischief', + 'mischievous', + 'miserable', + 'misery', + 'misfit', + 'misfortune', + 'mishaps', + 'mishmash', + 'mislead', + 'miss', + 'missed', + 'misses', + 'missile', + 'missing', + 'mission', + "mission's", + 'missioned', + 'missioner', + 'missioning', + 'missions', + 'missive', + 'missives', + 'mist', + "mist's", + 'mistake', + 'mistaken', + 'mistaker', + 'mistakes', + 'mistaking', + 'mister', + 'mistimed', + 'mistletoe', + 'mistpirates', + 'mistreated', + 'mistrustful', + 'mists', + 'misty', + "misty's", + 'mistys', + 'mithos', + 'mitsubishi', + 'mitten', + 'mittens', + 'mix', + "mix'n", + "mix'n'match", + 'mixed', + 'mixer', + "mixer's", + 'mixers', + 'mixes', + 'mixing', + 'mixmaster', + 'mixolydian', + 'mixture', + 'mixup', + 'mizzen', + 'mizzenbee', + 'mizzenberry', + 'mizzenblabber', + 'mizzenbocker', + 'mizzenboing', + 'mizzenboom', + 'mizzenbounce', + 'mizzenbouncer', + 'mizzenbrains', + 'mizzenbubble', + 'mizzenbumble', + 'mizzenbump', + 'mizzenbumper', + 'mizzenburger', + 'mizzenchomp', + 'mizzencorn', + 'mizzencrash', + 'mizzencrumbs', + 'mizzencrump', + 'mizzencrunch', + 'mizzendoodle', + 'mizzendorf', + 'mizzenface', + 'mizzenfidget', + 'mizzenfink', + 'mizzenfish', + 'mizzenflap', + 'mizzenflapper', + 'mizzenflinger', + 'mizzenflip', + 'mizzenflipper', + 'mizzenfoot', + 'mizzenfuddy', + 'mizzenfussen', + 'mizzengadget', + 'mizzengargle', + 'mizzengloop', + 'mizzenglop', + 'mizzengoober', + 'mizzengoose', + 'mizzengrooven', + 'mizzenhoffer', + 'mizzenhopper', + 'mizzenjinks', + 'mizzenklunk', + 'mizzenknees', + 'mizzenmarble', + 'mizzenmash', + 'mizzenmast', + 'mizzenmonkey', + 'mizzenmooch', + 'mizzenmouth', + 'mizzenmuddle', + 'mizzenmuffin', + 'mizzenmush', + 'mizzennerd', + 'mizzennoodle', + 'mizzennose', + 'mizzennugget', + 'mizzenphew', + 'mizzenphooey', + 'mizzenpocket', + 'mizzenpoof', + 'mizzenpop', + 'mizzenpounce', + 'mizzenpow', + 'mizzenpretzel', + 'mizzenquack', + 'mizzenroni', + 'mizzenscooter', + 'mizzenscreech', + 'mizzensmirk', + 'mizzensnooker', + 'mizzensnoop', + 'mizzensnout', + 'mizzensocks', + 'mizzenspeed', + 'mizzenspinner', + 'mizzensplat', + 'mizzensprinkles', + 'mizzensticks', + 'mizzenstink', + 'mizzenswirl', + 'mizzenteeth', + 'mizzenthud', + 'mizzentoes', + 'mizzenton', + 'mizzentoon', + 'mizzentooth', + 'mizzentwist', + 'mizzenwhatsit', + 'mizzenwhip', + 'mizzenwig', + 'mizzenwoof', + 'mizzenzaner', + 'mizzenzap', + 'mizzenzapper', + 'mizzenzilla', + 'mizzenzoom', + 'mlg', + 'mm', + 'mmelodyland', + 'mmg', + 'mml', + 'mmo', + 'mmocentral', + 'mmorpg', + 'mnemonic', + 'mnemonics', + 'mo-o-o-o-orse', + 'moat', + "moat's", + 'moats', + 'mobile', + 'mobilize', + 'moccasin', + "moccasin's", + 'moccasins', + 'mocha', + "mocha's", + 'mochas', + 'mochi', + 'mock', + 'mockingbird', + "mockingbird's", + 'mockingbirds', + 'mod', + 'mode', + 'moded', + 'model', + "model's", + 'modeler', + 'modelers', + 'modeling', + 'models', + 'moderate', + 'moderated', + 'moderately', + 'moderates', + 'moderating', + 'moderation', + 'moderations', + 'moderator', + "moderator's", + 'moderators', + 'modern', + 'modernly', + 'moderns', + 'modes', + 'modest', + 'mods', + 'module', + 'modules', + 'moe', + 'mog', + 'mogul', + 'mohawk', + 'moi', + 'moises', + 'mojo', + 'mola', + 'molar', + 'molasses', + 'mold', + 'moldy', + 'mole', + 'molecule', + 'molecules', + 'molloy', + 'molly', + 'molted', + 'molten', + 'mom', + 'moment', + "moment's", + 'momently', + 'momentous', + 'moments', + 'momifier', + 'monada', + 'monarch', + 'monarchs', + 'monatia', + 'monday', + "monday's", + 'mondays', + 'money', + "money's", + 'monger', + 'mongers', + 'mongrel', + 'mongrels', + 'mongrols', + 'monies', + 'monique', + "monique's", + 'moniques', + 'monitor', + 'monk', + "monk's", + 'monkes', + 'monkey', + "monkey's", + 'monkeying', + 'monkeys', + 'monkies', + 'monks', + 'monocle', + 'monocles', + 'monodevelop', + 'mononoke', + 'monopolize', + 'monopolized', + 'monopolizes', + 'monopolizing', + 'monopoly', + 'monorail', + "monorail's", + 'monorails', + 'monos', + 'monroe', + "monroe's", + 'monroes', + 'monster', + "monster's", + 'monstercat', + 'monsters', + 'monstro', + "monstro's", + 'monstropolis', + 'monstrous', + 'month', + 'months', + 'monument', + 'monumental', + 'moo', + 'mood', + "mood's", + 'moods', + 'moon', + "moon's", + "moonbeam's", + 'mooning', + 'moonlight', + 'moonlighted', + 'moonlighter', + 'moonlighting', + 'moonlights', + 'moonliner', + 'moonlit', + 'moonraker', + 'moons', + 'moonwort', + 'mop', + 'mopp', + 'moptop', + 'moral', + 'morale', + 'morally-sound', + 'moray', + 'more', + 'morgan', + "morgan's", + 'morgans', + 'morning', + "morning's", + 'mornings', + 'morningstar', + 'morrigan', + 'morris', + 'morse', + 'morsel', + 'mortar', + 'mortimer', + "mortimer's", + 'moseby', + "moseby's", + 'mosona', + 'mosquito', + 'mosreau', + 'moss', + 'mossari', + 'mossarito', + 'mossax', + 'mossman', + 'mossy', + 'most', + 'mosters', + 'mostly', + 'moth', + 'mother', + "mother's", + 'mother-of-pearl', + 'mothership', + 'motherships', + 'moths', + 'motion', + 'motioned', + 'motioner', + 'motioning', + 'motions', + 'motivating', + 'motivator', + 'motley', + 'moto', + 'motocrossed', + 'motor', + "motor's", + 'motorcycle', + 'motorcycles', + 'motored', + 'motoring', + 'motors', + 'motto', + 'moulding', + 'mound', + 'mountain', + "mountain's", + 'mountains', + 'mountaintop', + 'mouse', + "mouse's", + 'mousekadoer', + "mousekadoer's", + 'mousekadoers', + 'mousekespotter', + "mousekespotter's", + 'mousekespotters', + 'mouseover', + 'mouser', + 'mouses', + 'mousing', + 'moussaka', + 'move', + 'moved', + 'movement', + "movement's", + 'movements', + 'mover', + "mover's", + 'movers', + 'moves', + 'movie', + "movie's", + 'moviemaker', + "moviemaker's", + 'moviemakers', + 'movies', + "movin'", + 'moving', + 'movingly', + 'movings', + 'mower', + 'mowers', + 'mowgli', + "mowgli's", + 'mowglis', + 'moyers', + 'mr', + 'mr.', + 'mrs', + 'mrs.', + 'msg', + 'mt', + 'mtn', + 'mtr', + 'muaba', + 'muahaha', + 'much', + 'mucho', + 'mucks', + 'mucky', + 'mud', + 'mud-talents', + 'muddle', + 'muddled', + 'muddy', + 'mudhands', + 'mudkip', + 'mudmoss', + 'mudpie', + 'muerte', + 'mufasa', + "mufasa's", + 'mufasas', + 'muffin', + 'muffins', + 'mugon', + 'muharram', + 'muigos', + 'mukluk', + 'mulan', + "mulan's", + 'mulans', + 'mulberry', + 'muldoon', + "muldoon's", + 'mullet', + 'multi', + 'multi-barreled', + 'multi-colored', + 'multi-player', + 'multi-sweetwrap', + 'multi-wrap', + 'multichoice', + 'multiplane', + 'multiplayer', + 'multiple', + 'multiplex', + 'multitask', + 'multitasking', + 'mum', + "mum's", + 'mumble', + 'mumbleface', + 'mumbo', + 'mummies', + 'mummy', + "mummy's", + 'munk', + 'muppet', + 'muppets', + "muppets'", + 'muriel', + 'murky', + 'murrieta-animata', + 'musageki', + 'musakabu', + 'musarite', + 'musckets', + 'muscled', + 'muse', + 'museum', + "museum's", + 'museums', + 'mush', + 'mushu', + "mushu's", + 'mushus', + 'mushy', + 'music', + "music's", + 'musica', + 'musical', + 'musical2', + 'musical3', + 'musically', + 'musicals', + 'musician', + 'musicians', + 'musics', + 'musket', + 'musketeer', + "musketeer's", + 'musketeers', + 'muskets', + 'muslin', + 'mussel', + 'must', + 'mustache', + 'mustaches', + 'mustard', + 'mute', + 'muted', + 'mutes', + 'mutiny', + 'mutual', + 'mvp', + 'my', + 'my name is jeff', + 'mygamefox', + 'myrna', + 'myself', + 'myst', + 'myst-a-find', + 'mysteries', + 'mysterious', + 'mysteriously', + 'mystery', + "mystery's", + 'mystic', + 'mystical', + 'mystik', + 'myth', + 'myths', + 'n-nw', + 'n.e.', + 'na', + 'naa_ve', + 'naaa', + 'nachos', + 'nada', + 'naggy', + 'nagu', + 'naguryu', + 'naguzoro', + 'nah', + 'nail', + 'nails', + 'naive', + 'naketas', + 'name', + "name's", + 'named', + 'names', + 'nametag', + 'naming', + 'nan', + 'nana', + 'nanairo', + 'nancy', + 'nani', + 'nano', + 'nanos', + 'nap', + "nap's", + 'napkin', + 'napkins', + 'napmasters', + 'napoleon', + 'nappy', + 'naps', + 'narnia', + "narnia's", + 'narnias', + 'narrow', + 'narrowed', + 'narrower', + 'narrowest', + 'narrowing', + 'narrowly', + 'narrows', + 'nascar', + "nascar's", + 'nascars', + 'nat', + 'nate', + 'nathaniel', + 'nation', + 'national', + 'native', + 'natives', + 'natural', + 'naturally', + 'naturals', + 'nature', + "nature's", + 'natured', + 'natures', + 'nautical', + 'nautilus', + 'navago', + 'navermo', + 'navies', + 'navigate', + 'navigation', + 'navigator', + "navigator's", + 'navigators', + 'navona', + 'navy', + "navy's", + 'nay', + 'nd', + 'near', + 'nearby', + 'neared', + 'nearer', + 'nearest', + 'nearing', + 'nearly', + 'nears', + 'neat', + 'necessaries', + 'necessarily', + 'necessary', + 'necessities', + 'neck', + 'necktie', + 'neckties', + 'neckvein', + 'nectar', + 'nectarine', + 'ned', + "ned's", + 'nedison', + 'need', + 'needed', + 'needer', + "needin'", + 'needing', + 'needle-bristle', + 'needless', + 'needly', + 'needs', + 'negative', + 'negatively', + 'negativity', + 'negotiate', + 'negotiated', + 'negotiates', + 'negotiating', + 'negotiation', + 'negotiations', + 'neigh', + 'neighbor', + "neighbor's", + 'neighborhood', + 'neighborhoods', + 'neighbors', + 'neighbour', + 'neil', + 'neither', + 'neko', + 'nell', + 'nelly', + 'nelson', + 'nemesis', + 'nemo', + "nemo's", + 'nemos', + "neptoon's", + 'neptune', + "neptune's", + 'nerd', + 'nerds', + 'nerf', + 'nerfed', + 'nerve', + "nerve's", + 'nerved', + 'nerves', + 'nerving', + 'nervous', + 'nessa', + 'nest', + 'nestor', + "nestor's", + 'nestors', + 'net', + 'nettie', + 'nettle', + 'network', + "network's", + 'networked', + 'networking', + 'networks', + 'neuton', + 'neutral', + 'never', + 'never-before-seen', + 'never-ending', + 'neverland', + 'neville', + "neville's", + 'nevilles', + 'new', + 'new-ager', + 'newer', + 'newest', + 'newfound', + 'newly', + 'newp', + 'newport', + 'news', + 'newsletter', + "newsletter's", + 'newsletters', + 'newsman', + 'newspaper', + "newspaper's", + 'newspapers', + 'newt', + "newt's", + 'newts', + 'next', + 'nibs', + 'nicada', + 'nice', + 'nicely', + 'nicer', + 'nicest', + 'nick', + "nick's", + 'nickel', + 'nickelbee', + 'nickelberry', + 'nickelblabber', + 'nickelbocker', + 'nickelboing', + 'nickelboom', + 'nickelbounce', + 'nickelbouncer', + 'nickelbrains', + 'nickelbubble', + 'nickelbumble', + 'nickelbump', + 'nickelbumper', + 'nickelburger', + 'nickelchomp', + 'nickelcorn', + 'nickelcrash', + 'nickelcrumbs', + 'nickelcrump', + 'nickelcrunch', + 'nickeldoodle', + 'nickeldorf', + 'nickelface', + 'nickelfidget', + 'nickelfink', + 'nickelfish', + 'nickelflap', + 'nickelflapper', + 'nickelflinger', + 'nickelflip', + 'nickelflipper', + 'nickelfoot', + 'nickelfuddy', + 'nickelfussen', + 'nickelgadget', + 'nickelgargle', + 'nickelgloop', + 'nickelglop', + 'nickelgoober', + 'nickelgoose', + 'nickelgrooven', + 'nickelhoffer', + 'nickelhopper', + 'nickeljinks', + 'nickelklunk', + 'nickelknees', + 'nickelmarble', + 'nickelmash', + 'nickelmonkey', + 'nickelmooch', + 'nickelmouth', + 'nickelmuddle', + 'nickelmuffin', + 'nickelmush', + 'nickelnerd', + 'nickelnoodle', + 'nickelnose', + 'nickelnugget', + 'nickelphew', + 'nickelphooey', + 'nickelpocket', + 'nickelpoof', + 'nickelpop', + 'nickelpounce', + 'nickelpow', + 'nickelpretzel', + 'nickelquack', + 'nickelroni', + 'nickelscooter', + 'nickelscreech', + 'nickelsmirk', + 'nickelsnooker', + 'nickelsnoop', + 'nickelsnout', + 'nickelsocks', + 'nickelspeed', + 'nickelspinner', + 'nickelsplat', + 'nickelsprinkles', + 'nickelsticks', + 'nickelstink', + 'nickelswirl', + 'nickelteeth', + 'nickelthud', + 'nickeltoes', + 'nickelton', + 'nickeltoon', + 'nickeltooth', + 'nickeltwist', + 'nickelwhatsit', + 'nickelwhip', + 'nickelwig', + 'nickelwoof', + 'nickelzaner', + 'nickelzap', + 'nickelzapper', + 'nickelzilla', + 'nickelzoom', + 'nicki', + 'nickname', + 'nicknamed', + 'nicks', + 'nicos', + 'nifty', + 'night', + "night's", + 'nightbreed', + 'nighted', + 'nighters', + 'nightfall', + 'nightgown', + 'nightingale', + "nightingale's", + 'nightingales', + 'nightkillers', + 'nightlife', + 'nightly', + 'nightmare', + "nightmare's", + 'nightmares', + 'nights', + 'nightshade', + 'nightstalkers', + 'nightstand', + 'nighttime', + 'nikabrik', + 'nill', + 'nilla', + 'nilsa', + 'nimrood', + 'nimue', + "nimue's", + 'nimues', + 'nina', + "nina's", + 'nine', + 'ninja', + "ninja's", + 'ninjas', + 'nintendo', + 'ninth', + 'nirvana', + 'nissa', + 'nite', + "nite's", + 'nitelight', + 'nites', + 'nitpick', + 'nitpicked', + 'nitpicking', + 'nitpicks', + 'nitpicky', + 'nitro', + 'nitrous', + 'no', + 'no-fire', + 'no-fly', + 'no-nonsense', + 'noah', + 'nobaddy', + 'noble', + 'nobodies', + 'nobody', + "nobody's", + 'noctem', + 'nocturnal', + 'noctus', + 'nod', + "nod's", + 'nods', + 'noel', + "noel's", + 'noels', + 'noggin', + "noggin'", + "noggin's", + 'noho', + 'noir', + 'noise', + 'noised', + 'noisemakers', + 'noises', + 'noising', + 'noisy', + 'nokogilla', + 'nokogiro', + 'nokoko', + 'nomes', + 'nominated', + 'non-bat-oriented', + 'nona', + 'nonary', + 'nonchalant', + 'none', + 'nones', + 'nonsense', + 'nonstop', + 'noo', + 'noob', + 'noobs', + 'noodle', + "noodle's", + 'noodles', + 'noogy', + 'nook', + 'nooo', + 'noooo', + 'nooooo', + 'noooooo', + 'nooooooo', + 'noooooooo', + 'nooooooooo', + 'noooooooooo', + 'nope', + 'nor', + "nor'easter", + 'nora', + 'nordic', + 'normal', + 'normally', + 'normals', + 'norman', + 'north', + "north's", + 'norther', + 'northern', + 'northerner', + "northerner's", + 'northerners', + 'northernly', + 'northers', + 'northing', + 'nose', + 'nosed', + 'noses', + 'nostalgia', + 'nostalgic', + 'nostril', + 'nostrils', + 'not', + 'notable', + 'notations', + 'notch', + 'note', + 'notebook', + 'noted', + 'notepad', + 'notepad++', + 'notepads', + 'noter', + 'notes', + 'noteworthy', + 'nothin', + 'nothing', + 'nothings', + 'notice', + 'noticed', + 'notices', + 'noticing', + 'notified', + 'noting', + 'notion', + 'notions', + 'notiriety', + 'notoriety', + 'notorious', + 'notre', + 'nov', + "nova's", + 'novel', + 'novelty', + 'november', + "november's", + 'novembers', + 'novemeber', + 'novice', + "novice's", + 'novices', + 'now', + 'nowhere', + 'nowheres', + 'nows', + 'nox', + 'noxious', + 'np', + 'npc', + 'npcnames', + 'npcs', + 'ntc', + 'nterceptor', + 'nugget', + 'numb', + 'number', + 'numbers', + 'nurse', + 'nursery', + 'nurses', + 'nursing', + 'nutmeg', + 'nutrition', + 'nutronium', + 'nuts', + 'nutshell', + 'nutty', + 'nuttybee', + 'nuttyberry', + 'nuttyblabber', + 'nuttybocker', + 'nuttyboing', + 'nuttyboom', + 'nuttybounce', + 'nuttybouncer', + 'nuttybrains', + 'nuttybubble', + 'nuttybumble', + 'nuttybump', + 'nuttybumper', + 'nuttyburger', + 'nuttychomp', + 'nuttycorn', + 'nuttycrash', + 'nuttycrumbs', + 'nuttycrump', + 'nuttycrunch', + 'nuttydoodle', + 'nuttydorf', + 'nuttyface', + 'nuttyfidget', + 'nuttyfink', + 'nuttyfish', + 'nuttyflap', + 'nuttyflapper', + 'nuttyflinger', + 'nuttyflip', + 'nuttyflipper', + 'nuttyfoot', + 'nuttyfuddy', + 'nuttyfussen', + 'nuttygadget', + 'nuttygargle', + 'nuttygloop', + 'nuttyglop', + 'nuttygoober', + 'nuttygoose', + 'nuttygrooven', + 'nuttyhoffer', + 'nuttyhopper', + 'nuttyjinks', + 'nuttyklunk', + 'nuttyknees', + 'nuttymarble', + 'nuttymash', + 'nuttymonkey', + 'nuttymooch', + 'nuttymouth', + 'nuttymuddle', + 'nuttymuffin', + 'nuttymush', + 'nuttynerd', + 'nuttynoodle', + 'nuttynose', + 'nuttynugget', + 'nuttyphew', + 'nuttyphooey', + 'nuttypocket', + 'nuttypoof', + 'nuttypop', + 'nuttypounce', + 'nuttypow', + 'nuttypretzel', + 'nuttyquack', + 'nuttyroni', + 'nuttyscooter', + 'nuttyscreech', + 'nuttysmirk', + 'nuttysnooker', + 'nuttysnoop', + 'nuttysnout', + 'nuttysocks', + 'nuttyspeed', + 'nuttyspinner', + 'nuttysplat', + 'nuttysprinkles', + 'nuttysticks', + 'nuttystink', + 'nuttyswirl', + 'nuttyteeth', + 'nuttythud', + 'nuttytoes', + 'nuttyton', + 'nuttytoon', + 'nuttytooth', + 'nuttytwist', + 'nuttywhatsit', + 'nuttywhip', + 'nuttywig', + 'nuttywoof', + 'nuttyzaner', + 'nuttyzap', + 'nuttyzapper', + 'nuttyzilla', + 'nuttyzoom', + 'nvidia', + 'nvitation', + 'nvm', + 'nw', + 'nyra', + 'o', + "o'clock", + "o'eight", + "o'henry", + "o's", + "o'toole", + 'o-torch', + 'o.o', + 'o:', + 'o_o', + 'oak', + "oak's", + 'oaks', + 'oao ovo', + 'oar', + "oar's", + 'oared', + 'oaring', + 'oars', + 'oasis', + 'oath', + 'oban', + 'obay', + 'obedience', + 'obey', + 'obeys', + 'obj', + 'object', + "object's", + 'objected', + 'objecting', + 'objective', + 'objects', + 'obnoxious', + 'obnoxiously', + 'obrigado', + 'obs', + 'obscure', + 'obsequious', + 'observation', + "observation's", + 'observations', + 'observe', + 'observed', + 'observer', + "observer's", + 'observers', + 'observes', + 'observing', + 'obsidian', + 'obsidians', + 'obstacle', + "obstacle's", + 'obstacles', + 'obtain', + 'obtained', + 'obtainer', + "obtainer's", + 'obtainers', + 'obtaining', + 'obtains', + 'obvious', + 'obviously', + 'occasion', + 'occasioned', + 'occasioning', + 'occasionings', + 'occasions', + 'occupied', + 'occupies', + 'occupy', + 'occupying', + 'occur', + 'occurred', + 'occurs', + 'ocean', + "ocean's", + 'oceana', + 'oceanic', + 'oceanliner', + 'oceanliners', + 'oceans', + 'ocelot', + 'oct', + 'octavia', + 'octobee', + 'october', + "october's", + 'octoberry', + 'octobers', + 'octoblabber', + 'octobocker', + 'octoboing', + 'octoboom', + 'octobounce', + 'octobouncer', + 'octobrains', + 'octobubble', + 'octobumble', + 'octobump', + 'octobumper', + 'octoburger', + 'octochomp', + 'octocorn', + 'octocrash', + 'octocrumbs', + 'octocrump', + 'octocrunch', + 'octodoodle', + 'octodorf', + 'octoface', + 'octofidget', + 'octofink', + 'octofish', + 'octoflap', + 'octoflapper', + 'octoflinger', + 'octoflip', + 'octoflipper', + 'octofoot', + 'octofuddy', + 'octofussen', + 'octogadget', + 'octogargle', + 'octogloop', + 'octoglop', + 'octogoober', + 'octogoose', + 'octogrooven', + 'octohoffer', + 'octohopper', + 'octojinks', + 'octoklunk', + 'octoknees', + 'octomarble', + 'octomash', + 'octomonkey', + 'octomooch', + 'octomouth', + 'octomuddle', + 'octomuffin', + 'octomush', + 'octonary', + 'octonerd', + 'octonoodle', + 'octonose', + 'octonugget', + 'octophew', + 'octophooey', + 'octopocket', + 'octopoof', + 'octopop', + 'octopounce', + 'octopow', + 'octopretzel', + 'octopus', + "octopus'", + 'octoquack', + 'octoroni', + 'octoscooter', + 'octoscreech', + 'octosmirk', + 'octosnooker', + 'octosnoop', + 'octosnout', + 'octosocks', + 'octospeed', + 'octospinner', + 'octosplat', + 'octosprinkles', + 'octosticks', + 'octostink', + 'octoswirl', + 'octoteeth', + 'octothud', + 'octotoes', + 'octoton', + 'octotoon', + 'octotooth', + 'octotwist', + 'octowhatsit', + 'octowhip', + 'octowig', + 'octowoof', + 'octozaner', + 'octozap', + 'octozapper', + 'octozilla', + 'octozoom', + 'oculus', + 'odd', + 'odder', + 'oddest', + 'oddly', + 'odds', + 'of', + 'off', + 'off-the-chain', + 'off-the-hook', + 'off-the-wall', + 'offbeat', + 'offend', + 'offended', + 'offender', + "offender's", + 'offenders', + 'offending', + 'offends', + 'offer', + "offer's", + 'offered', + 'offerer', + 'offerers', + 'offering', + 'offerings', + 'offers', + 'office', + "office's", + 'officer', + 'officers', + 'offices', + 'official', + "official's", + 'officially', + 'officials', + 'offing', + 'offkey', + 'offline', + 'offrill', + 'offs', + 'offset', + 'often', + 'oftener', + 'og', + 'ogre', + "ogre's", + 'ogres', + 'oh', + 'ohana', + 'oi', + 'oic', + 'oik', + 'oil', + 'oiled', + 'oiler', + "oiler's", + 'oilers', + 'oiling', + 'oils', + 'oin', + 'oink', + 'ojidono', + 'ojimaru', + 'ok', + 'okas', + 'okay', + "okay's", + 'oken', + 'okie', + "ol'", + 'old', + 'old-fashioned', + 'older', + 'oldest', + 'oldman', + 'ole', + 'olive', + 'oliver', + "oliver's", + 'olivia', + 'olivier', + 'ollallaberry', + 'ollie', + 'ollo', + 'olympic', + 'olympics', + 'omalley', + 'omar', + 'ombres', + 'omen', + 'omens', + 'omg', + 'omibug', + 'omigosh', + 'omniscient', + 'omw', + 'on', + 'once', + 'one', + 'one-liner', + 'ones', + 'ongoing', + 'onion', + 'online', + 'only', + 'ono', + 'onomatopoeic', + 'onscreen', + 'onstage', + 'onto', + 'onward', + 'onyx', + 'ood', + 'oodles', + 'oof', + 'oogie', + "oogie's", + 'ooh', + 'oola', + "oola's", + 'oomph', + 'ooo', + 'oooh', + 'oooo', + 'ooooo', + 'oooooo', + 'ooooooo', + 'oooooooo', + 'ooooooooo', + 'oooooooooo', + 'oops', + 'op', + 'opal', + 'opalescence', + 'opalescent', + 'opel', + 'open', + 'opened', + 'opener', + 'openers', + 'openest', + 'opening', + 'openings', + 'openly', + 'openness', + 'opens', + 'opera', + "opera's", + 'operas', + 'operate', + 'operated', + 'operates', + 'operating', + 'operation', + 'operations', + 'operative', + 'operator', + "operator's", + 'operators', + 'opinion', + "opinion's", + 'opinions', + 'opponent', + "opponent's", + 'opponents', + 'opportunities', + 'opportunity', + "opportunity's", + 'oppose', + 'opposed', + 'opposer', + 'opposes', + 'opposing', + 'opposite', + 'oppositely', + 'opposites', + 'opposition', + 'oppositions', + 'ops', + 'optics', + 'optimal', + 'optimism', + 'optimist', + 'optimistic', + 'optimists', + 'optimize', + 'optimizing', + 'option', + "option's", + 'optional', + 'options', + 'optometry', + 'opulent', + 'or', + 'oracle', + 'orange', + 'oranges', + 'orb', + 'orbit', + 'orbited', + 'orbiter', + 'orbiters', + 'orbiting', + 'orbits', + 'orbs', + 'orcas', + 'orchana', + 'orchard', + "orchard's", + 'orchards', + 'orchestra', + "orchestra's", + 'orchestras', + 'orchid', + 'order', + 'ordered', + 'orderer', + 'ordering', + 'orderings', + 'orderly', + 'orders', + 'ordinaries', + 'ordinary', + 'ore', + 'oregano', + 'oreo', + 'organic', + 'organization', + 'organizations', + 'organize', + 'organized', + 'organizes', + 'organizing', + 'organs', + 'oriental', + 'origin', + 'original', + 'originally', + 'originals', + 'orinda', + "orinda's", + 'oriole', + 'orleans', + 'ornament', + "ornament's", + 'ornaments', + 'ornate', + 'ornery', + 'orphaned', + 'orren', + 'ortega', + "ortega's", + 'ortegas', + 'orville', + "orzoz's", + 'oscar', + "oscar's", + 'oscars', + 'osment', + "osment's", + 'osments', + 'osso', + 'ostrich', + "ostrich's", + 'ostrichs', + 'oswald', + "oswald's", + 'oswalds', + 'otencakes', + 'other', + "other's", + 'others', + "others'", + 'otherwise', + 'otoh', + 'otp', + 'otter', + 'otto', + 'ouch', + 'ought', + 'ouo', + 'our', + 'ours', + 'ourselves', + 'out', + 'outback', + 'outcast', + 'outcome', + 'outcomes', + 'outdated', + 'outdoor', + 'outdoors', + 'outed', + 'outer', + 'outerspace', + 'outfield', + 'outfit', + 'outfits', + 'outgoing', + 'outing', + 'outings', + 'outlandish', + 'outlaw', + 'outlawed', + 'outlawing', + 'outlaws', + 'outlet', + 'outnumber', + 'outnumbered', + 'outnumbers', + 'output', + "output's", + 'outputs', + 'outrageous', + 'outriggers', + 'outs', + 'outside', + 'outsider', + 'outsiders', + 'outsource', + 'outsourced', + 'outsources', + 'outsourcing', + 'outspoken', + 'outta', + 'outwit', + 'ouya', + 'oval', + 'ovals', + 'oven', + 'over', + 'overall', + "overall's", + 'overalls', + 'overbearing', + 'overboard', + 'overcoming', + 'overdressed', + 'overdue', + 'overhaul', + 'overhauled', + 'overhauls', + 'overhead', + 'overing', + 'overjoyed', + 'overlap', + "overlap's", + 'overlaps', + 'overly', + 'overprotective', + 'overrated', + 'overrun', + 'overs', + 'overshoes', + 'overture', + 'overview', + 'overwhelming', + 'ow', + 'owe', + 'owed', + 'owes', + 'owing', + 'owl', + "owl's", + 'owls', + 'own', + 'owned', + 'owner', + "owner's", + 'owners', + 'owning', + 'owns', + 'owo', + 'owooo', + 'owoooo', + 'owooooo', + 'owoooooo', + 'oxford', + 'oxfords', + 'oxide', + 'oxygen', + 'oyster', + "oyster's", + 'oysters', + 'oz', + 'p.j.', + 'pa', + 'pacha', + "pachelbel's", + 'pacific', + 'pack', + 'package', + 'packages', + 'packet', + "packin'", + 'packing', + 'packs', + 'pad', + "pad's", + 'padding', + 'paddle', + "paddle's", + 'paddlebee', + 'paddleberry', + 'paddleblabber', + 'paddlebocker', + 'paddleboing', + 'paddleboom', + 'paddlebounce', + 'paddlebouncer', + 'paddlebrains', + 'paddlebubble', + 'paddlebumble', + 'paddlebump', + 'paddlebumper', + 'paddleburger', + 'paddlechomp', + 'paddlecorn', + 'paddlecrash', + 'paddlecrumbs', + 'paddlecrump', + 'paddlecrunch', + 'paddledoodle', + 'paddledorf', + 'paddleface', + 'paddlefidget', + 'paddlefink', + 'paddlefish', + 'paddleflap', + 'paddleflapper', + 'paddleflinger', + 'paddleflip', + 'paddleflipper', + 'paddlefoot', + 'paddlefuddy', + 'paddlefussen', + 'paddlegadget', + 'paddlegargle', + 'paddlegloop', + 'paddleglop', + 'paddlegoober', + 'paddlegoose', + 'paddlegrooven', + 'paddlehoffer', + 'paddlehopper', + 'paddlejinks', + 'paddleklunk', + 'paddleknees', + 'paddlemarble', + 'paddlemash', + 'paddlemonkey', + 'paddlemooch', + 'paddlemouth', + 'paddlemuddle', + 'paddlemuffin', + 'paddlemush', + 'paddlenerd', + 'paddlenoodle', + 'paddlenose', + 'paddlenugget', + 'paddlephew', + 'paddlephooey', + 'paddlepocket', + 'paddlepoof', + 'paddlepop', + 'paddlepounce', + 'paddlepow', + 'paddlepretzel', + 'paddlequack', + 'paddler', + 'paddleroni', + 'paddles', + 'paddlescooter', + 'paddlescreech', + 'paddlesmirk', + 'paddlesnooker', + 'paddlesnoop', + 'paddlesnout', + 'paddlesocks', + 'paddlespeed', + 'paddlespinner', + 'paddlesplat', + 'paddlesprinkles', + 'paddlesticks', + 'paddlestink', + 'paddleswirl', + 'paddleteeth', + 'paddlethud', + 'paddletoes', + 'paddleton', + 'paddletoon', + 'paddletooth', + 'paddletwist', + 'paddlewhatsit', + 'paddlewheel', + "paddlewheel's", + 'paddlewheels', + 'paddlewhip', + 'paddlewig', + 'paddlewoof', + 'paddlezaner', + 'paddlezap', + 'paddlezapper', + 'paddlezilla', + 'paddlezoom', + 'paddock', + 'padre', + 'padres', + 'pads', + 'pagani', + 'page', + 'pago', + 'pagoni', + 'pagoyama', + 'pah', + 'pahacha', + 'pahaxion', + 'pahazoa', + 'paid', + 'pain', + 'paine', + 'pained', + 'paining', + 'pains', + 'paint', + 'paint-spattered', + 'paintball', + "paintball's", + 'paintballs', + 'paintbrush', + 'painted', + 'painter', + "painter's", + 'painters', + 'painting', + 'paintings', + 'paints', + 'pair', + "pair's", + 'paired', + 'pairing', + 'pairings', + 'pairs', + 'paisley', + 'pajama', + "pajama's", + 'pajamas', + 'pakistan', + 'pal', + "pal's", + 'palace', + "palace's", + 'palaces', + 'palatable', + 'pale', + 'palebee', + 'paleberry', + 'paleblabber', + 'palebocker', + 'paleboing', + 'paleboom', + 'palebounce', + 'palebouncer', + 'palebrains', + 'palebubble', + 'palebumble', + 'palebump', + 'palebumper', + 'paleburger', + 'palechomp', + 'palecorn', + 'palecrash', + 'palecrumbs', + 'palecrump', + 'palecrunch', + 'paled', + 'paledoodle', + 'paledorf', + 'paleface', + 'palefidget', + 'palefink', + 'palefish', + 'paleflap', + 'paleflapper', + 'paleflinger', + 'paleflip', + 'paleflipper', + 'palefoot', + 'palefuddy', + 'palefussen', + 'palegadget', + 'palegargle', + 'palegloop', + 'paleglop', + 'palegoober', + 'palegoose', + 'palegrooven', + 'palehoffer', + 'palehopper', + 'palejinks', + 'paleklunk', + 'paleknees', + 'palemarble', + 'palemash', + 'palemonkey', + 'palemooch', + 'palemouth', + 'palemuddle', + 'palemuffin', + 'palemush', + 'palenerd', + 'palenoodle', + 'palenose', + 'palenugget', + 'palephew', + 'palephooey', + 'palepocket', + 'palepoof', + 'palepop', + 'palepounce', + 'palepow', + 'palepretzel', + 'palequack', + 'paler', + 'paleroni', + 'palescooter', + 'palescreech', + 'palesmirk', + 'palesnooker', + 'palesnoop', + 'palesnout', + 'palesocks', + 'palespeed', + 'palespinner', + 'palesplat', + 'palesprinkles', + 'palest', + 'palesticks', + 'palestink', + 'paleswirl', + 'paleteeth', + 'palethud', + 'paletoes', + 'paleton', + 'paletoon', + 'paletooth', + 'paletwist', + 'palewhatsit', + 'palewhip', + 'palewig', + 'palewoof', + 'palezaner', + 'palezap', + 'palezapper', + 'palezilla', + 'palezoom', + 'palifico', + 'paling', + 'pally', + 'palm', + "palm's", + 'palmer', + 'palms', + "palms'", + 'pals', + "pals'", + "pals's", + 'pamela', + 'pan', + "pan's", + 'pancake', + 'pancakes', + 'pancys', + 'panda', + "panda's", + 'panda3d', + 'pandas', + 'pandora', + 'panel', + "panel's", + 'panels', + 'pangram', + 'pangrams', + 'panic', + "panic's", + 'panicked', + 'panics', + 'pans', + 'pansy', + 'pant', + "pant's", + 'pantano', + 'panther', + 'panthers', + 'pants', + 'pants.', + 'paper', + "paper's", + 'papercut', + 'papered', + 'paperer', + 'paperers', + 'papering', + 'paperings', + 'papers', + 'pappy', + 'paprika', + 'par', + 'par-tee', + 'parade', + "parade's", + 'paraded', + 'parades', + 'paradigm', + 'parading', + 'paradise', + 'parakeet', + 'parakeets', + 'parallel', + 'parallels', + 'paralyzing', + 'paranoid', + 'paranoids', + 'parchment', + 'pardon', + 'pardoned', + 'pardoner', + 'pardoners', + 'pardoning', + 'pardons', + 'parender', + 'parent', + 'parentheses', + 'parenthesis', + 'parents', + 'parfaits', + 'park', + "park's", + 'parking', + 'parks', + 'parlay', + 'parlays', + 'parle', + 'parlor', + 'parlors', + 'paroom', + 'parquet', + 'parr', + 'parrot', + "parrot's", + 'parrotfish', + 'parrothead', + 'parrots', + 'parry', + 'parsley', + 'part', + 'parted', + 'parter', + "parter's", + 'parters', + 'participant', + "participant's", + 'participants', + 'participate', + 'participated', + 'participates', + 'participating', + 'participation', + 'particular', + 'particularly', + 'particulars', + 'partied', + 'parties', + 'parting', + 'partings', + 'partly', + 'partner', + "partner's", + 'partnered', + 'partnering', + 'partners', + 'parts', + 'party', + "party's", + 'partying', + 'partys', + 'partytime', + "partytime's", + 'partytimes', + 'partyzone', + "partyzone's", + 'partyzones', + 'pascal', + 'pass', + 'passable', + 'passage', + "passage's", + 'passaged', + 'passages', + 'passaging', + 'passed', + 'passenger', + "passenger's", + 'passengerly', + 'passengers', + 'passer', + 'passers', + 'passes', + 'passing', + 'passive', + 'passover', + 'passport', + "passport's", + 'passports', + 'password', + 'passwords', + 'past', + "past's", + 'pasta', + 'paste', + 'pasted', + 'pastes', + 'pasting', + 'pastoral', + 'pastries', + 'pasts', + 'pataba', + 'patch', + 'patched', + 'patches', + 'patching', + 'patchwork', + 'path', + 'pathes', + 'paths', + 'patience', + 'patient', + "patient's", + 'patiently', + 'patients', + 'patona', + 'patrick', + "patrick's", + 'patricks', + 'patrol', + "patrol's", + 'patrols', + 'patros', + 'patsy', + 'pattern', + "pattern's", + 'patterned', + 'patterning', + 'patterns', + 'pattertwig', + "pattertwig's", + 'pattertwigs', + 'patty', + 'paul', + "paul's", + 'paula', + 'pauls', + 'pauper', + 'pause', + 'paused', + 'pauses', + 'pausing', + 'pavement', + 'pawn', + 'paws', + 'pax', + 'pay', + "pay's", + "payin'", + 'paying', + 'payment', + "payment's", + 'payments', + 'pays', + 'pb&j', + 'pc', + 'pcs', + 'pdt', + 'pea', + 'peace', + 'peaceful', + 'peach', + 'peaches', + 'peachy', + 'peacock', + 'peak', + 'peaks', + 'peal', + 'peanut', + 'peanuts', + 'peapod', + 'pear', + 'pearl', + 'pearls', + 'pearly', + 'pears', + 'peas', + 'peasant', + 'peasants', + 'peat', + 'pebble', + 'pebbles', + 'pecan', + 'peck', + 'pecking', + 'pecos', + 'peculiar', + 'pedal', + 'pedalbee', + 'pedalberry', + 'pedalblabber', + 'pedalbocker', + 'pedalboing', + 'pedalboom', + 'pedalbounce', + 'pedalbouncer', + 'pedalbrains', + 'pedalbubble', + 'pedalbumble', + 'pedalbump', + 'pedalbumper', + 'pedalburger', + 'pedalchomp', + 'pedalcorn', + 'pedalcrash', + 'pedalcrumbs', + 'pedalcrump', + 'pedalcrunch', + 'pedaldoodle', + 'pedaldorf', + 'pedalface', + 'pedalfidget', + 'pedalfink', + 'pedalfish', + 'pedalflap', + 'pedalflapper', + 'pedalflinger', + 'pedalflip', + 'pedalflipper', + 'pedalfoot', + 'pedalfuddy', + 'pedalfussen', + 'pedalgadget', + 'pedalgargle', + 'pedalgloop', + 'pedalglop', + 'pedalgoober', + 'pedalgoose', + 'pedalgrooven', + 'pedalhoffer', + 'pedalhopper', + 'pedaljinks', + 'pedalklunk', + 'pedalknees', + 'pedalmarble', + 'pedalmash', + 'pedalmonkey', + 'pedalmooch', + 'pedalmouth', + 'pedalmuddle', + 'pedalmuffin', + 'pedalmush', + 'pedalnerd', + 'pedalnoodle', + 'pedalnose', + 'pedalnugget', + 'pedalphew', + 'pedalphooey', + 'pedalpocket', + 'pedalpoof', + 'pedalpop', + 'pedalpounce', + 'pedalpow', + 'pedalpretzel', + 'pedalquack', + 'pedalroni', + 'pedals', + 'pedalscooter', + 'pedalscreech', + 'pedalsmirk', + 'pedalsnooker', + 'pedalsnoop', + 'pedalsnout', + 'pedalsocks', + 'pedalspeed', + 'pedalspinner', + 'pedalsplat', + 'pedalsprinkles', + 'pedalsticks', + 'pedalstink', + 'pedalswirl', + 'pedalteeth', + 'pedalthud', + 'pedaltoes', + 'pedalton', + 'pedaltoon', + 'pedaltooth', + 'pedaltwist', + 'pedalwhatsit', + 'pedalwhip', + 'pedalwig', + 'pedalwoof', + 'pedalzaner', + 'pedalzap', + 'pedalzapper', + 'pedalzilla', + 'pedalzoom', + 'pedro', + 'peek', + 'peek-a-boo', + 'peekaboo', + 'peeks', + 'peel', + 'peeled', + 'peels', + 'peenick', + 'peep', + 'peepers', + 'peeps', + 'peesy', + 'pegasus', + 'pegboard', + 'pegleg', + 'peglegfleet', + 'pelican', + "pelican's", + 'pelicans', + 'pell', + 'pen', + 'penalty', + 'pencil', + 'pencils', + 'pendant', + 'pending', + 'penelope', + 'penguin', + "penguin's", + 'penguins', + 'pennies', + 'penny', + "penny's", + 'penrod', + "penrod's", + 'pens', + 'pentagon', + "pentagon's", + 'pentagons', + 'pentameter', + 'peony', + 'people', + "people's", + 'peopled', + 'peoples', + 'peopling', + 'pepe', + 'pepper', + "pepper's", + 'pepperbee', + 'pepperberry', + 'pepperblabber', + 'pepperbocker', + 'pepperboing', + 'pepperboom', + 'pepperbounce', + 'pepperbouncer', + 'pepperbrains', + 'pepperbubble', + 'pepperbumble', + 'pepperbump', + 'pepperbumper', + 'pepperburger', + 'pepperchomp', + 'peppercorn', + 'peppercrash', + 'peppercrumbs', + 'peppercrump', + 'peppercrunch', + 'pepperdoodle', + 'pepperdorf', + 'pepperface', + 'pepperfidget', + 'pepperfink', + 'pepperfish', + 'pepperflap', + 'pepperflapper', + 'pepperflinger', + 'pepperflip', + 'pepperflipper', + 'pepperfoot', + 'pepperfuddy', + 'pepperfussen', + 'peppergadget', + 'peppergargle', + 'peppergloop', + 'pepperglop', + 'peppergoober', + 'peppergoose', + 'peppergrooven', + 'pepperhoffer', + 'pepperhopper', + 'pepperjinks', + 'pepperklunk', + 'pepperknees', + 'peppermarble', + 'peppermash', + 'peppermonkey', + 'peppermooch', + 'peppermouth', + 'peppermuddle', + 'peppermuffin', + 'peppermush', + 'peppernerd', + 'peppernoodle', + 'peppernose', + 'peppernugget', + 'pepperoni', + 'pepperonis', + 'pepperphew', + 'pepperphooey', + 'pepperpocket', + 'pepperpoof', + 'pepperpop', + 'pepperpounce', + 'pepperpow', + 'pepperpretzel', + 'pepperquack', + 'pepperroni', + 'peppers', + 'pepperscooter', + 'pepperscreech', + 'peppersmirk', + 'peppersnooker', + 'peppersnoop', + 'peppersnout', + 'peppersocks', + 'pepperspeed', + 'pepperspinner', + 'peppersplat', + 'peppersprinkles', + 'peppersticks', + 'pepperstink', + 'pepperswirl', + 'pepperteeth', + 'pepperthud', + 'peppertoes', + 'pepperton', + 'peppertoon', + 'peppertooth', + 'peppertwist', + 'pepperwhatsit', + 'pepperwhip', + 'pepperwig', + 'pepperwoof', + 'pepperzaner', + 'pepperzap', + 'pepperzapper', + 'pepperzilla', + 'pepperzoom', + 'peppy', + 'pepsi', + 'per', + 'percent', + 'percents', + 'perch', + 'perdida', + 'perfect', + 'perfected', + 'perfectemente', + 'perfecter', + 'perfecting', + 'perfective', + 'perfectly', + 'perfects', + 'perform', + 'performance', + "performance's", + 'performances', + 'performed', + 'performer', + "performer's", + 'performers', + 'performing', + 'performs', + 'perfume', + 'perfumes', + 'perhaps', + 'period', + 'periwinkle', + 'perky', + 'perla', + "perla's", + 'permanent', + 'permanently', + 'permission', + 'permissions', + 'permit', + "permit's", + 'permits', + 'perpetua', + 'perseverance', + 'persimmon', + 'person', + "person's", + 'personal', + 'personalize', + 'personalized', + 'personally', + 'personals', + 'persons', + 'persuade', + 'persuaded', + 'persuader', + 'persuaders', + 'persuades', + 'persuading', + 'pescetarian', + 'pescetarians', + 'pesky', + "pesky's", + 'pessimism', + 'pessimist', + 'pessimistic', + 'pessimists', + 'pest', + 'pestilence', + 'pestle', + 'pestles', + 'pet', + "pet's", + 'petal', + 'petalbee', + 'petalberry', + 'petalblabber', + 'petalbocker', + 'petalboing', + 'petalboom', + 'petalbounce', + 'petalbouncer', + 'petalbrains', + 'petalbubble', + 'petalbumble', + 'petalbump', + 'petalbumper', + 'petalburger', + 'petalchomp', + 'petalcorn', + 'petalcrash', + 'petalcrumbs', + 'petalcrump', + 'petalcrunch', + 'petaldoodle', + 'petaldorf', + 'petalface', + 'petalfidget', + 'petalfink', + 'petalfish', + 'petalflap', + 'petalflapper', + 'petalflinger', + 'petalflip', + 'petalflipper', + 'petalfoot', + 'petalfuddy', + 'petalfussen', + 'petalgadget', + 'petalgargle', + 'petalgloop', + 'petalglop', + 'petalgoober', + 'petalgoose', + 'petalgrooven', + 'petalhead', + 'petalhoffer', + 'petalhopper', + 'petaljinks', + 'petalklunk', + 'petalknees', + 'petalmarble', + 'petalmash', + 'petalmonkey', + 'petalmooch', + 'petalmouth', + 'petalmuddle', + 'petalmuffin', + 'petalmush', + 'petalnerd', + 'petalnoodle', + 'petalnose', + 'petalnugget', + 'petalphew', + 'petalphooey', + 'petalpocket', + 'petalpoof', + 'petalpop', + 'petalpounce', + 'petalpow', + 'petalpretzel', + 'petalquack', + 'petalroni', + 'petals', + 'petalscooter', + 'petalscreech', + 'petalsmirk', + 'petalsnooker', + 'petalsnoop', + 'petalsnout', + 'petalsocks', + 'petalspeed', + 'petalspinner', + 'petalsplat', + 'petalsprinkles', + 'petalsticks', + 'petalstink', + 'petalswirl', + 'petalteeth', + 'petalthud', + 'petaltoes', + 'petalton', + 'petaltoon', + 'petaltooth', + 'petaltwist', + 'petalwhatsit', + 'petalwhip', + 'petalwig', + 'petalwoof', + 'petalzaner', + 'petalzap', + 'petalzapper', + 'petalzilla', + 'petalzoom', + 'pete', + "pete's", + 'petel', + 'petels', + 'peter', + 'petit', + 'petite', + 'pets', + 'petshop', + "petshop's", + 'petshops', + 'pettis', + 'pettiskirt', + 'petunia', + 'pevensie', + 'pewter', + 'pewterer', + 'peyton', + "peyton's", + 'peytons', + 'phab', + 'phantom', + "phantom's", + 'phantoms', + 'phase', + 'phased', + 'phaser', + 'phasers', + 'phases', + 'phasing', + 'phenomenon', + "phenomenon's", + 'phenomenons', + 'phew', + 'phil', + "phil's", + 'philharmagic', + 'philharmagics', + 'philip', + 'philippines', + 'phill', + 'phillip', + "phillip's", + 'phillips', + 'philosopher', + 'phils', + 'phineas', + "phineas'", + 'phinneas', + 'phinnies', + 'phinny', + "phinny's", + 'phinnys', + 'phoebe', + "phoenix's", + 'phoenixs', + 'phone', + 'phony', + 'phosphorescence', + 'phosphorescent', + 'photo', + 'photos', + 'phrase', + 'phrases', + 'phrasings', + 'phree', + 'piano', + "piano's", + 'pianos', + 'piarates', + 'pic', + 'pic-a-toon', + 'picachu', + 'picchu', + 'piccolo', + "piccolo's", + 'pick', + 'pick-a-name', + 'pick-up', + 'picked', + 'picker', + 'pickers', + 'pickert', + 'picking', + 'pickings', + 'picklebee', + 'pickleberry', + 'pickleblabber', + 'picklebocker', + 'pickleboing', + 'pickleboom', + 'picklebounce', + 'picklebouncer', + 'picklebrains', + 'picklebubble', + 'picklebumble', + 'picklebump', + 'picklebumper', + 'pickleburger', + 'picklechomp', + 'picklecorn', + 'picklecrash', + 'picklecrumbs', + 'picklecrump', + 'picklecrunch', + 'pickled', + 'pickledoodle', + 'pickledorf', + 'pickleface', + 'picklefidget', + 'picklefink', + 'picklefish', + 'pickleflap', + 'pickleflapper', + 'pickleflinger', + 'pickleflip', + 'pickleflipper', + 'picklefoot', + 'picklefuddy', + 'picklefussen', + 'picklegadget', + 'picklegargle', + 'picklegloop', + 'pickleglop', + 'picklegoober', + 'picklegoose', + 'picklegrooven', + 'picklehoffer', + 'picklehopper', + 'picklejinks', + 'pickleklunk', + 'pickleknees', + 'picklemarble', + 'picklemash', + 'picklemonkey', + 'picklemooch', + 'picklemouth', + 'picklemuddle', + 'picklemuffin', + 'picklemush', + 'picklenerd', + 'picklenoodle', + 'picklenose', + 'picklenugget', + 'picklephew', + 'picklephooey', + 'picklepocket', + 'picklepoof', + 'picklepop', + 'picklepounce', + 'picklepow', + 'picklepretzel', + 'picklequack', + 'pickleroni', + 'pickles', + 'picklescooter', + 'picklescreech', + 'picklesmirk', + 'picklesnooker', + 'picklesnoop', + 'picklesnout', + 'picklesocks', + 'picklespeed', + 'picklespinner', + 'picklesplat', + 'picklesprinkles', + 'picklesticks', + 'picklestink', + 'pickleswirl', + 'pickleteeth', + 'picklethud', + 'pickletoes', + 'pickleton', + 'pickletoon', + 'pickletooth', + 'pickletwist', + 'picklewhatsit', + 'picklewhip', + 'picklewig', + 'picklewoof', + 'picklezaner', + 'picklezap', + 'picklezapper', + 'picklezilla', + 'picklezoom', + 'picks', + 'pickup', + 'picnic', + "picnic's", + 'picnics', + 'picture', + 'pictured', + 'pictures', + 'picturing', + 'pie', + 'piece', + 'pieced', + 'piecer', + 'pieces', + 'piecing', + 'pier', + 'pierre', + 'pies', + 'pig', + "pig's", + 'pigeon', + "pigeon's", + 'pigeons', + 'pigge', + 'piggy', + "piggy's", + 'piggys', + 'piglet', + "piglet's", + 'piglets', + 'pigments', + 'pigs', + 'pikachu', + 'pikos', + 'pilagers', + 'pile', + 'piledriver', + 'piles', + 'pilfer', + 'pilfered', + 'pilfering', + 'pilfers', + 'pillage', + 'pillager', + "pillager's", + 'pillagers', + 'pillages', + 'pillaging', + 'pillar', + 'pillars', + 'pillow', + 'pillows', + "pilot's", + 'pilots', + 'pim', + "pim's", + 'pin', + 'pinball', + "pinball's", + 'pinballs', + 'pincer', + 'pincers', + 'pincher', + 'pinchers', + 'pine', + 'pine-needle', + 'pineapple', + 'pineapples', + 'pinecone', + 'pinecones', + 'pined', + 'ping', + 'pingas', + 'pinged', + 'pinging', + 'pining', + 'pink', + 'pinkerbee', + 'pinkerberry', + 'pinkerblabber', + 'pinkerbocker', + 'pinkerboing', + 'pinkerboom', + 'pinkerbounce', + 'pinkerbouncer', + 'pinkerbrains', + 'pinkerbubble', + 'pinkerbumble', + 'pinkerbump', + 'pinkerbumper', + 'pinkerburger', + 'pinkerchomp', + 'pinkercorn', + 'pinkercrash', + 'pinkercrumbs', + 'pinkercrump', + 'pinkercrunch', + 'pinkerdoodle', + 'pinkerdorf', + 'pinkerface', + 'pinkerfidget', + 'pinkerfink', + 'pinkerfish', + 'pinkerflap', + 'pinkerflapper', + 'pinkerflinger', + 'pinkerflip', + 'pinkerflipper', + 'pinkerfoot', + 'pinkerfuddy', + 'pinkerfussen', + 'pinkergadget', + 'pinkergargle', + 'pinkergloop', + 'pinkerglop', + 'pinkergoober', + 'pinkergoose', + 'pinkergrooven', + 'pinkerhoffer', + 'pinkerhopper', + 'pinkerjinks', + 'pinkerklunk', + 'pinkerknees', + 'pinkermarble', + 'pinkermash', + 'pinkermonkey', + 'pinkermooch', + 'pinkermouth', + 'pinkermuddle', + 'pinkermuffin', + 'pinkermush', + 'pinkernerd', + 'pinkernoodle', + 'pinkernose', + 'pinkernugget', + 'pinkerphew', + 'pinkerphooey', + 'pinkerpocket', + 'pinkerpoof', + 'pinkerpop', + 'pinkerpounce', + 'pinkerpow', + 'pinkerpretzel', + 'pinkerquack', + 'pinkerroni', + 'pinkerscooter', + 'pinkerscreech', + 'pinkersmirk', + 'pinkersnooker', + 'pinkersnoop', + 'pinkersnout', + 'pinkersocks', + 'pinkerspeed', + 'pinkerspinner', + 'pinkersplat', + 'pinkersprinkles', + 'pinkersticks', + 'pinkerstink', + 'pinkerswirl', + 'pinkerteeth', + 'pinkerthud', + 'pinkertoes', + 'pinkerton', + 'pinkertoon', + 'pinkertooth', + 'pinkertwist', + 'pinkerwhatsit', + 'pinkerwhip', + 'pinkerwig', + 'pinkerwoof', + 'pinkerzaner', + 'pinkerzap', + 'pinkerzapper', + 'pinkerzilla', + 'pinkerzoom', + 'pinkie', + 'pinned', + 'pinocchio', + "pinocchio's", + 'pinocchios', + 'pinorska', + 'pinpoint', + "pinpoint's", + 'pinpoints', + 'pinprick', + 'pins', + 'pinska', + 'pinstripe', + 'pinstripes', + 'pint', + 'pintel', + 'pints', + 'pinwheel', + 'pinwheels', + 'pioneer', + 'pioneers', + 'pippa', + 'pirate', + 'pirates', + 'pistachio', + 'pit', + 'pit-crew', + "pita's", + 'pitas', + 'pitfire', + 'pith', + 'pits', + 'pity', + 'pixar', + "pixar's", + 'pixie', + "pixie's", + 'pixie-dust', + 'pixie-dusted', + 'pixie-dusting', + 'pixie-licious', + 'pixie-licous', + 'pixie-perfect', + 'pixies', + 'pizza', + "pizza's", + 'pizzas', + 'pizzatron', + 'pj', + "pj's", + 'pl', + 'pl0x', + 'place', + 'placed', + 'placement', + 'placer', + 'places', + 'placid', + 'placing', + 'plagued', + 'plaid', + 'plaids', + 'plain', + 'plainer', + 'plainest', + 'plainly', + 'plains', + 'plainsmen', + 'plan', + "plan's", + 'plane', + "plane's", + 'planed', + 'planer', + 'planers', + 'planes', + 'planet', + "planet's", + 'planetarium', + 'planetariums', + 'planets', + 'planing', + 'plank', + 'plankbite', + 'planklove', + 'planks', + 'planned', + 'planners', + 'planning', + 'plans', + 'plant', + 'plantain', + 'plantains', + 'planted', + 'planter', + 'planters', + 'planting', + 'plantings', + 'plants', + 'plaque', + 'plas', + 'plaster', + 'plastic', + 'plasticly', + 'plastics', + 'plata', + 'plate', + 'plateau', + 'plateaus', + 'plated', + 'plater', + 'platers', + 'plates', + 'platform', + 'platforms', + 'plating', + 'platings', + 'platinum', + 'platoon', + 'platoonia', + 'platter', + 'platypus', + 'plausible', + 'play', + "play's", + 'playable', + 'played', + 'player', + "player's", + 'players', + 'playful', + 'playfulness', + 'playground', + "playground's", + 'playgrounds', + 'playhouse', + "playhouse's", + 'playhouses', + 'playin', + 'playing', + 'playlist', + 'playlists', + 'playmates', + 'plays', + 'playset', + 'playstation', + 'plaza', + "plaza's", + 'plazas', + 'pleakley', + 'pleaklies', + 'pleakly', + "pleakly's", + 'pleasant', + 'pleasantry', + 'please', + 'pleased', + 'pleasely', + 'pleaser', + "pleaser's", + 'pleasers', + 'pleases', + 'pleasing', + 'pleasure', + 'pleated', + 'plebeian', + 'plebeians', + 'plenties', + 'plenty', + 'plop', + 'plows', + 'plox', + 'pls', + 'pluck', + 'plucking', + 'plug', + 'plum', + 'pluma', + 'plumbers', + 'plumbing', + 'plume', + 'plumeria', + 'plummet', + 'plummeting', + 'plummets', + 'plump', + 'plums', + 'plunderbutlers', + 'plundered', + 'plunderer', + 'plunderers', + 'plunderhounds', + 'plunderin', + "plunderin'", + 'plundering', + 'plunderrs', + 'plunders', + 'plundershots', + 'plural', + 'plurals', + 'plus', + 'plush', + 'pluto', + "pluto's", + 'plz', + 'pm', + 'pocahontas', + "pocahontas'", + 'pocket', + 'pocketed', + 'pocketing', + 'pockets', + 'pocus', + 'pod', + 'podium', + "podium's", + 'podiums', + 'pods', + 'poem', + 'poems', + 'poetry', + 'poforums', + 'point', + 'pointed', + 'pointed-toed', + 'pointer', + 'pointers', + 'pointing', + 'points', + 'poisend', + 'poish', + 'poison', + 'pokemon', + 'pokercheat', + 'pokereval', + 'pokergame', + 'poland', + 'polar', + 'policies', + 'policy', + "policy's", + 'polite', + 'politely', + 'politeness', + 'polk', + 'polk-a-dot', + 'polka', + "polka's", + 'polkadot', + 'polkas', + 'poll', + 'pollen', + 'pollooo', + 'polls', + 'pollux', + 'polly', + 'polo', + 'polynesian', + "polynesian's", + 'polynesians', + 'pompadour', + 'pompous', + 'pond', + 'ponder', + 'ponds', + 'poney', + 'pong', + 'ponged', + 'ponies', + 'pontiac', + 'pony', + "pony's", + 'ponytail', + 'poodle', + 'poodlebee', + 'poodleberry', + 'poodleblabber', + 'poodlebocker', + 'poodleboing', + 'poodleboom', + 'poodlebounce', + 'poodlebouncer', + 'poodlebrains', + 'poodlebubble', + 'poodlebumble', + 'poodlebump', + 'poodlebumper', + 'poodleburger', + 'poodlechomp', + 'poodlecorn', + 'poodlecrash', + 'poodlecrumbs', + 'poodlecrump', + 'poodlecrunch', + 'poodledoodle', + 'poodledorf', + 'poodleface', + 'poodlefidget', + 'poodlefink', + 'poodlefish', + 'poodleflap', + 'poodleflapper', + 'poodleflinger', + 'poodleflip', + 'poodleflipper', + 'poodlefoot', + 'poodlefuddy', + 'poodlefussen', + 'poodlegadget', + 'poodlegargle', + 'poodlegloop', + 'poodleglop', + 'poodlegoober', + 'poodlegoose', + 'poodlegrooven', + 'poodlehoffer', + 'poodlehopper', + 'poodlejinks', + 'poodleklunk', + 'poodleknees', + 'poodlemarble', + 'poodlemash', + 'poodlemonkey', + 'poodlemooch', + 'poodlemouth', + 'poodlemuddle', + 'poodlemuffin', + 'poodlemush', + 'poodlenerd', + 'poodlenoodle', + 'poodlenose', + 'poodlenugget', + 'poodlephew', + 'poodlephooey', + 'poodlepocket', + 'poodlepoof', + 'poodlepop', + 'poodlepounce', + 'poodlepow', + 'poodlepretzel', + 'poodlequack', + 'poodleroni', + 'poodlescooter', + 'poodlescreech', + 'poodlesmirk', + 'poodlesnooker', + 'poodlesnoop', + 'poodlesnout', + 'poodlesocks', + 'poodlespeed', + 'poodlespinner', + 'poodlesplat', + 'poodlesprinkles', + 'poodlesticks', + 'poodlestink', + 'poodleswirl', + 'poodleteeth', + 'poodlethud', + 'poodletoes', + 'poodleton', + 'poodletoon', + 'poodletooth', + 'poodletwist', + 'poodlewhatsit', + 'poodlewhip', + 'poodlewig', + 'poodlewoof', + 'poodlezaner', + 'poodlezap', + 'poodlezapper', + 'poodlezilla', + 'poodlezoom', + "pooh's", + 'pool', + 'pooled', + 'pooling', + 'pools', + 'poor', + 'poorer', + 'poorest', + 'poorly', + 'pop', + "pop's", + 'popcorn', + 'popcorns', + 'poplar', + 'poplin', + 'popovers', + 'poppenbee', + 'poppenberry', + 'poppenblabber', + 'poppenbocker', + 'poppenboing', + 'poppenboom', + 'poppenbounce', + 'poppenbouncer', + 'poppenbrains', + 'poppenbubble', + 'poppenbumble', + 'poppenbump', + 'poppenbumper', + 'poppenburger', + 'poppenchomp', + 'poppencorn', + 'poppencrash', + 'poppencrumbs', + 'poppencrump', + 'poppencrunch', + 'poppendoodle', + 'poppendorf', + 'poppenface', + 'poppenfidget', + 'poppenfink', + 'poppenfish', + 'poppenflap', + 'poppenflapper', + 'poppenflinger', + 'poppenflip', + 'poppenflipper', + 'poppenfoot', + 'poppenfuddy', + 'poppenfussen', + 'poppengadget', + 'poppengargle', + 'poppengloop', + 'poppenglop', + 'poppengoober', + 'poppengoose', + 'poppengrooven', + 'poppenhoffer', + 'poppenhopper', + 'poppenjinks', + 'poppenklunk', + 'poppenknees', + 'poppenmarble', + 'poppenmash', + 'poppenmonkey', + 'poppenmooch', + 'poppenmouth', + 'poppenmuddle', + 'poppenmuffin', + 'poppenmush', + 'poppennerd', + 'poppennoodle', + 'poppennose', + 'poppennugget', + 'poppenphew', + 'poppenphooey', + 'poppenpocket', + 'poppenpoof', + 'poppenpop', + 'poppenpounce', + 'poppenpow', + 'poppenpretzel', + 'poppenquack', + 'poppenroni', + 'poppenscooter', + 'poppenscreech', + 'poppensmirk', + 'poppensnooker', + 'poppensnoop', + 'poppensnout', + 'poppensocks', + 'poppenspeed', + 'poppenspinner', + 'poppensplat', + 'poppensprinkles', + 'poppensticks', + 'poppenstink', + 'poppenswirl', + 'poppenteeth', + 'poppenthud', + 'poppentoes', + 'poppenton', + 'poppentoon', + 'poppentooth', + 'poppentwist', + 'poppenwhatsit', + 'poppenwhip', + 'poppenwig', + 'poppenwoof', + 'poppenzaner', + 'poppenzap', + 'poppenzapper', + 'poppenzilla', + 'poppenzoom', + 'popping', + 'poppins', + 'poppy', + 'poppy-puff', + 'poppyseed', + 'pops', + 'popsicle', + 'popsicles', + 'popular', + 'popularity', + 'popularly', + 'populate', + 'populated', + 'populates', + 'populating', + 'population', + 'populations', + 'popup', + 'por', + 'porch', + 'porcupine', + 'porgy', + 'porkchop', + 'porpoise', + 'porsche', + 'port', + 'portable', + 'portal', + 'ported', + 'porter', + 'porters', + 'porting', + 'portly', + 'portmouths', + 'portrait', + 'portraits', + 'ports', + 'pose', + 'posh', + 'posies', + 'position', + 'positioned', + 'positioning', + 'positions', + 'positive', + 'positively', + 'positives', + 'positivity', + 'posse', + 'possess', + 'possessions', + 'possibilities', + 'possibility', + "possibility's", + 'possible', + 'possibles', + 'possibly', + 'possum', + "possum's", + 'possums', + 'post', + 'post-concert', + 'post-show', + 'postcard', + 'postcards', + 'posted', + 'poster', + 'posters', + 'posthaste', + 'posting', + 'postings', + 'postman', + 'postmaster', + 'posts', + 'postshow', + 'posture', + 'posy', + 'potato', + "potato's", + 'potatoes', + 'potatos', + 'potc', + 'potential', + 'potentially', + 'potentials', + 'potion', + "potion's", + 'potions', + 'potpies', + 'pots', + 'pots-and-pans', + 'potsen', + 'potter', + 'pouch', + 'pouches', + 'pounce', + 'pour', + "pour's", + 'poured', + 'pourer', + 'pourers', + 'pouring', + 'pours', + 'pouty', + 'pow', + 'powder-burnt', + 'powdered', + 'powders', + 'powe', + 'power', + "power's", + 'powered', + 'powerful', + 'powerfully', + 'powerhouse', + 'powering', + 'powers', + 'pox', + 'ppl', + 'practical', + 'practically', + 'practice', + "practice's", + 'practices', + 'practicing', + 'prairie', + 'prairies', + 'pram', + 'prank', + 'pranks', + 'pratt', + 'prattle', + 'prawn', + 'pre-concert', + 'precious', + 'preciousbee', + 'preciousberry', + 'preciousblabber', + 'preciousbocker', + 'preciousboing', + 'preciousboom', + 'preciousbounce', + 'preciousbouncer', + 'preciousbrains', + 'preciousbubble', + 'preciousbumble', + 'preciousbump', + 'preciousbumper', + 'preciousburger', + 'preciouschomp', + 'preciouscorn', + 'preciouscrash', + 'preciouscrumbs', + 'preciouscrump', + 'preciouscrunch', + 'preciousdoodle', + 'preciousdorf', + 'preciousface', + 'preciousfidget', + 'preciousfink', + 'preciousfish', + 'preciousflap', + 'preciousflapper', + 'preciousflinger', + 'preciousflip', + 'preciousflipper', + 'preciousfoot', + 'preciousfuddy', + 'preciousfussen', + 'preciousgadget', + 'preciousgargle', + 'preciousgloop', + 'preciousglop', + 'preciousgoober', + 'preciousgoose', + 'preciousgrooven', + 'precioushoffer', + 'precioushopper', + 'preciousjinks', + 'preciousklunk', + 'preciousknees', + 'preciousmarble', + 'preciousmash', + 'preciousmonkey', + 'preciousmooch', + 'preciousmouth', + 'preciousmuddle', + 'preciousmuffin', + 'preciousmush', + 'preciousnerd', + 'preciousnoodle', + 'preciousnose', + 'preciousnugget', + 'preciousphew', + 'preciousphooey', + 'preciouspocket', + 'preciouspoof', + 'preciouspop', + 'preciouspounce', + 'preciouspow', + 'preciouspretzel', + 'preciousquack', + 'preciousroni', + 'preciousscooter', + 'preciousscreech', + 'precioussmirk', + 'precioussnooker', + 'precioussnoop', + 'precioussnout', + 'precioussocks', + 'preciousspeed', + 'preciousspinner', + 'precioussplat', + 'precioussprinkles', + 'precioussticks', + 'preciousstink', + 'preciousswirl', + 'preciousteeth', + 'preciousthud', + 'precioustoes', + 'preciouston', + 'precioustoon', + 'precioustooth', + 'precioustwist', + 'preciouswhatsit', + 'preciouswhip', + 'preciouswig', + 'preciouswoof', + 'preciouszaner', + 'preciouszap', + 'preciouszapper', + 'preciouszilla', + 'preciouszoom', + 'precipice', + 'precipitate', + 'precipitated', + 'precipitates', + 'precipitating', + 'precipitation', + 'precisely', + 'precocious', + 'predicaments', + 'predict', + 'predictometer', + 'predicts', + 'pree', + 'prefab', + 'prefer', + 'preference', + 'preferences', + 'preferred', + 'prefers', + 'prefix', + 'prefixes', + 'prehysterical', + 'premiere', + 'premium', + 'prepare', + 'prepared', + 'preparedness', + 'preparer', + 'prepares', + 'preparing', + 'preposition', + 'prepositions', + 'prepostera', + 'prescription', + 'prescriptions', + 'presence', + "presence's", + 'presences', + 'present', + 'presentation', + 'presentations', + 'presented', + 'presenter', + "presenter's", + 'presenters', + 'presenting', + 'presently', + 'presents', + 'preserver', + 'preservers', + 'president', + "presidents'", + 'press', + 'pressed', + 'presser', + 'presses', + 'pressing', + 'pressings', + 'presto', + 'pretend', + 'pretended', + 'pretender', + "pretender's", + 'pretenders', + 'pretending', + 'pretends', + 'pretentious', + 'prettied', + 'prettier', + 'pretties', + 'prettiest', + 'pretty', + 'prettying', + 'pretzel', + 'pretzels', + 'prev', + 'prevent', + 'prevented', + 'preventer', + 'preventing', + 'prevention', + 'preventive', + 'prevents', + 'preview', + 'previous', + 'previously', + 'priate', + 'price', + 'priced', + 'pricer', + 'pricers', + 'prices', + 'pricing', + 'prickly', + 'pride', + "pride's", + 'prigate', + 'prilla', + "prilla's", + 'prim', + 'primaries', + 'primary', + "primary's", + 'primate', + 'prime', + 'primed', + 'primely', + 'primer', + 'primers', + 'primes', + 'priming', + 'primitive', + 'primp', + 'primrose', + 'prince', + "prince's", + 'princely', + 'princes', + 'princess', + "princess's", + 'princesses', + 'principal', + "principal's", + 'principals', + 'principle', + 'principled', + 'principles', + 'prinna', + 'print', + 'printed', + 'printer', + "printer's", + 'printers', + 'printing', + 'prints', + 'prior', + 'priorities', + 'priority', + "priority's", + 'priscila', + 'priscilla', + 'privacy', + 'privateer', + "privateer's", + 'privateered', + 'privateering', + 'privateers', + 'privilege', + 'privileged', + 'privileges', + 'prix', + 'prize', + 'prized', + 'prizer', + 'prizers', + 'prizes', + 'prizing', + 'prizmod', + "prizmod's", + 'pro', + 'proactive', + 'prob', + 'probability', + 'probably', + 'problem', + "problem's", + 'problems', + 'procastinators', + 'procedure', + 'procedures', + 'proceed', + 'proceeded', + 'proceeding', + 'proceedings', + 'proceeds', + 'process', + "process's", + 'processed', + 'processes', + 'processing', + 'proddy', + 'prodigies', + 'prodigy', + 'produce', + 'produced', + 'producer', + 'producers', + 'produces', + 'producing', + 'product', + "product's", + 'production', + 'productive', + 'products', + 'professor', + "professor's", + 'professors', + 'profile', + 'profiles', + 'profit', + "profit's", + 'profited', + 'profiter', + 'profiters', + 'profiting', + 'profits', + 'program', + "program's", + 'programed', + 'programing', + 'programmer', + 'programmers', + 'programs', + 'progress', + 'progressed', + 'progresses', + 'progressing', + 'progressive', + 'prohibit', + 'prohibited', + 'prohibiting', + 'prohibits', + 'project', + "project's", + 'projected', + 'projectile', + 'projecting', + 'projective', + 'projector', + 'projectors', + 'projects', + 'prolly', + 'prom', + 'promise', + 'promised', + 'promiser', + 'promises', + 'promising', + 'promo', + 'promos', + 'promote', + 'promoted', + 'promoter', + "promoter's", + 'promoters', + 'promotes', + 'promoting', + 'promotion', + 'promotional', + 'promotions', + 'promotive', + 'prompt', + 'prompter', + 'prompters', + 'pronto', + 'proof', + "proof's", + 'proofed', + 'proofer', + 'proofing', + 'proofs', + 'prop', + 'propeller', + 'propellor', + 'proper', + 'properly', + 'propertied', + 'properties', + 'property', + 'proposal', + "proposal's", + 'proposals', + 'propose', + 'proposes', + 'proposition', + 'props', + 'prospect', + 'prospected', + 'prospecting', + 'prospective', + 'prospector', + "prospector's", + 'prospects', + 'protect', + 'protected', + 'protecting', + "protection's", + 'protections', + 'protective', + 'protects', + 'prototype', + 'proud', + 'proudest', + 'prove', + 'proved', + 'prover', + "prover's", + 'provers', + 'proves', + 'provide', + 'provided', + 'providence', + 'provider', + 'providers', + 'provides', + 'providing', + 'proving', + 'provoke', + 'provoked', + 'provokes', + 'provoking', + 'prow', + 'prower', + 'proximity', + 'proxy', + 'prudence', + 'prunaprismia', + 'prymme', + 'ps2', + 'ps3', + 'ps4', + 'psa', + 'psp', + 'psyched', + 'psychic', + "psychic's", + 'psychics', + 'pt', + 'pt.', + 'ptr', + 'public', + "public's", + 'publicly', + 'publics', + 'publish', + 'published', + 'publisher', + "publisher's", + 'publishers', + 'publishes', + 'publishing', + 'pucca', + 'puccas', + 'puce', + 'pucker', + 'pudding', + 'puddle', + 'puddles', + 'pudge', + 'pufferang', + 'puffle', + 'puffles', + 'puffy', + 'pug', + "pugpratt's", + 'pula', + 'pull', + 'pulled', + 'puller', + 'pulling', + 'pullings', + 'pullover', + 'pullovers', + 'pulls', + 'pulse', + 'pulyurleg', + 'pumba', + "pumba's", + 'pumbaa', + "pumbaa's", + 'pumbaas', + 'pummel', + 'pump', + 'pumpkin', + "pumpkin's", + 'pumpkinbee', + 'pumpkinberry', + 'pumpkinblabber', + 'pumpkinbocker', + 'pumpkinboing', + 'pumpkinboom', + 'pumpkinbounce', + 'pumpkinbouncer', + 'pumpkinbrains', + 'pumpkinbubble', + 'pumpkinbumble', + 'pumpkinbump', + 'pumpkinbumper', + 'pumpkinburger', + 'pumpkinchomp', + 'pumpkincorn', + 'pumpkincrash', + 'pumpkincrumbs', + 'pumpkincrump', + 'pumpkincrunch', + 'pumpkindoodle', + 'pumpkindorf', + 'pumpkinface', + 'pumpkinfidget', + 'pumpkinfink', + 'pumpkinfish', + 'pumpkinflap', + 'pumpkinflapper', + 'pumpkinflinger', + 'pumpkinflip', + 'pumpkinflipper', + 'pumpkinfoot', + 'pumpkinfuddy', + 'pumpkinfussen', + 'pumpkingadget', + 'pumpkingargle', + 'pumpkingloop', + 'pumpkinglop', + 'pumpkingoober', + 'pumpkingoose', + 'pumpkingrooven', + 'pumpkinhoffer', + 'pumpkinhopper', + 'pumpkinjinks', + 'pumpkinklunk', + 'pumpkinknees', + 'pumpkinmarble', + 'pumpkinmash', + 'pumpkinmonkey', + 'pumpkinmooch', + 'pumpkinmouth', + 'pumpkinmuddle', + 'pumpkinmuffin', + 'pumpkinmush', + 'pumpkinnerd', + 'pumpkinnoodle', + 'pumpkinnose', + 'pumpkinnugget', + 'pumpkinphew', + 'pumpkinphooey', + 'pumpkinpocket', + 'pumpkinpoof', + 'pumpkinpop', + 'pumpkinpounce', + 'pumpkinpow', + 'pumpkinpretzel', + 'pumpkinquack', + 'pumpkinroni', + 'pumpkins', + 'pumpkinscooter', + 'pumpkinscreech', + 'pumpkinsmirk', + 'pumpkinsnooker', + 'pumpkinsnoop', + 'pumpkinsnout', + 'pumpkinsocks', + 'pumpkinspeed', + 'pumpkinspinner', + 'pumpkinsplat', + 'pumpkinsprinkles', + 'pumpkinsticks', + 'pumpkinstink', + 'pumpkinswirl', + 'pumpkinteeth', + 'pumpkinthud', + 'pumpkintoes', + 'pumpkinton', + 'pumpkintoon', + 'pumpkintooth', + 'pumpkintwist', + 'pumpkinwhatsit', + 'pumpkinwhip', + 'pumpkinwig', + 'pumpkinwoof', + 'pumpkinzaner', + 'pumpkinzap', + 'pumpkinzapper', + 'pumpkinzilla', + 'pumpkinzoom', + 'pun', + 'punchline', + 'punchlines', + 'punchy', + 'punctuality', + 'punctuation', + 'punk', + 'punny', + 'puns', + 'puny', + 'pupert', + "pupert's", + 'pupil', + 'pupils', + 'puppet', + 'puppets', + 'puppies', + 'puppy', + "puppy's", + 'purchase', + 'purchased', + 'purchaser', + "purchaser's", + 'purchasers', + 'purchases', + 'purchasing', + 'pure', + 'purebred', + 'puree', + 'purim', + "purim's", + 'purple', + 'purplebee', + 'purpleberry', + 'purpleblabber', + 'purplebocker', + 'purpleboing', + 'purpleboom', + 'purplebounce', + 'purplebouncer', + 'purplebrains', + 'purplebubble', + 'purplebumble', + 'purplebump', + 'purplebumper', + 'purpleburger', + 'purplechomp', + 'purplecorn', + 'purplecrash', + 'purplecrumbs', + 'purplecrump', + 'purplecrunch', + 'purpledoodle', + 'purpledorf', + 'purpleface', + 'purplefidget', + 'purplefink', + 'purplefish', + 'purpleflap', + 'purpleflapper', + 'purpleflinger', + 'purpleflip', + 'purpleflipper', + 'purplefoot', + 'purplefuddy', + 'purplefussen', + 'purplegadget', + 'purplegargle', + 'purplegloop', + 'purpleglop', + 'purplegoober', + 'purplegoose', + 'purplegrooven', + 'purplehoffer', + 'purplehopper', + 'purplejinks', + 'purpleklunk', + 'purpleknees', + 'purplemarble', + 'purplemash', + 'purplemonkey', + 'purplemooch', + 'purplemouth', + 'purplemuddle', + 'purplemuffin', + 'purplemush', + 'purplenerd', + 'purplenoodle', + 'purplenose', + 'purplenugget', + 'purplephew', + 'purplephooey', + 'purplepocket', + 'purplepoof', + 'purplepop', + 'purplepounce', + 'purplepow', + 'purplepretzel', + 'purplequack', + 'purpleroni', + 'purplescooter', + 'purplescreech', + 'purplesmirk', + 'purplesnooker', + 'purplesnoop', + 'purplesnout', + 'purplesocks', + 'purplespeed', + 'purplespinner', + 'purplesplat', + 'purplesprinkles', + 'purplesticks', + 'purplestink', + 'purpleswirl', + 'purpleteeth', + 'purplethud', + 'purpletoes', + 'purpleton', + 'purpletoon', + 'purpletooth', + 'purpletwist', + 'purplewhatsit', + 'purplewhip', + 'purplewig', + 'purplewoof', + 'purplezaner', + 'purplezap', + 'purplezapper', + 'purplezilla', + 'purplezoom', + 'purpose', + 'purposed', + 'purposely', + 'purposes', + 'purposing', + 'purposive', + 'purr', + 'purr-fect', + 'purr-fectly', + 'purr-form', + 'purse', + 'pursuit', + 'pursuits', + 'push', + 'pushed', + 'pusher', + 'pushers', + 'pushes', + 'pushing', + 'put', + 'putrid', + 'puts', + 'putt', + 'putt-putt', + 'putting', + 'putts', + 'puzzle', + 'puzzled', + 'puzzler', + "puzzler's", + 'puzzlers', + 'puzzles', + 'puzzling', + 'puzzlings', + 'pvp', + 'pwnage', + 'pwncake', + 'pwned', + 'pyjama', + 'pyjamas', + 'pyle', + 'pylon', + 'pyramid', + 'pyrate', + 'pyrates', + 'pyrats', + 'pyro', + 'python', + 'qack', + 'quack', + 'quacker', + 'quackintosh', + 'quackity', + 'quacks', + 'quacky', + 'quad', + 'quad-barrel', + 'quad-barrels', + 'quadrant', + 'quadrilles', + 'quads', + 'quaint', + 'quake', + 'qualification', + 'qualifications', + 'qualified', + 'qualifier', + "qualifier's", + 'qualifiers', + 'qualifies', + 'qualify', + 'qualifying', + 'qualities', + 'quality', + "quality's", + 'quantities', + 'quantity', + "quantity's", + 'quantum', + 'quarry', + 'quarter', + 'quarterdeck', + 'quartered', + 'quartering', + 'quarterly', + 'quarters', + 'quartet', + 'quasimodo', + "quasimodo's", + 'quasimodos', + 'quater', + 'quaterers', + 'quebec', + 'quebecor', + 'queen', + "queen's", + 'queenly', + 'queens', + 'quentin', + "quentin's", + 'quesadilla', + 'quesadillas', + 'quest', + 'quested', + 'quester', + "quester's", + 'questers', + 'questing', + 'question', + 'questioned', + 'questioner', + 'questioners', + 'questioning', + 'questionings', + 'questions', + 'quests', + 'queued', + 'queuing', + 'quick', + 'quick-rot', + 'quick-witted', + 'quicken', + 'quickens', + 'quicker', + 'quickest', + 'quickly', + 'quicksilver', + 'quidditch', + 'quiet', + 'quieted', + 'quieten', + 'quietens', + 'quieter', + 'quietest', + 'quieting', + 'quietly', + 'quiets', + 'quilt', + 'quilting', + 'quilts', + 'quinary', + 'quintessential', + 'quirtle', + 'quit', + 'quite', + 'quits', + 'quitting', + 'quixotic', + 'quiz', + 'quizzed', + 'quizzes', + 'quizzical', + 'quo', + 'quote', + 'quotes', + 'r', + 'rabbit', + "rabbit's", + 'rabbits', + 'rabbitstew', + 'raccoon', + "raccoon's", + 'raccoons', + 'race', + 'raced', + 'racer', + "racer's", + 'racers', + 'races', + 'raceway', + 'rachel', + 'rachelle', + "racin'", + 'racing', + 'racket', + 'rackets', + 'rackham', + 'rad', + 'radar', + 'radiant', + 'radiate', + 'radiator', + 'radiators', + 'radical', + 'radio', + "radio's", + 'radioed', + 'radioing', + 'radios', + 'radishes', + 'radius', + 'rae', + 'raff', + 'raft', + "raft's", + 'rafting', + 'rafts', + 'ragetti', + 'ragged', + 'ragtime', + 'raid', + 'raided', + 'raider', + 'raiders', + 'raiding', + 'raids', + 'raikiri', + 'rail', + 'railing', + 'railroad', + 'railroaded', + 'railroader', + 'railroaders', + 'railroading', + 'railroads', + 'rails', + 'railstand', + 'railwas', + 'railway', + "railway's", + 'rain', + "rain's", + 'rainbow', + 'rainbows', + 'rained', + 'raining', + 'rains', + 'rainstorms', + 'rainy', + 'raise', + 'raised', + 'raiser', + 'raisers', + 'raises', + 'raising', + 'raisins', + 'rake', + 'raked', + 'rakes', + 'raking', + 'rallen', + 'rally', + 'ralph', + 'rama', + 'ramadan', + 'ramay', + 'ramble', + 'rambleshack', + 'ramen', + 'ramone', + "ramone's", + 'ramones', + 'ramp', + 'ramps', + 'ran', + 'ranch', + 'ranched', + 'rancher', + 'ranchers', + 'ranches', + 'ranching', + 'rancid', + 'randolph', + 'random', + 'randomizer', + 'randomly', + 'range', + 'ranged', + 'ranger', + 'rangers', + 'ranges', + 'ranging', + 'rani', + "rani's", + 'rank', + 'ranked', + 'ranker', + 'rankers', + 'rankest', + 'ranking', + 'rankings', + 'rankly', + 'ranks', + 'rap', + 'rapid', + "rapid's", + 'rapidly', + 'rapids', + "rappin'", + 'raps', + 'raptor', + 'raptors', + 'rare', + 'rarely', + 'rarer', + 'rarest', + 'raring', + 'rarity', + 'rasberry', + 'rascals', + 'rash', + 'raspberries', + 'raspberry', + 'raspberry-vanilla', + 'raspy', + 'rat', + "rat's", + 'rat-tastic', + 'ratatouille', + "ratatouille's", + 'rate', + 'rated', + 'rates', + 'rather', + 'rating', + 'ratings', + 'rats', + 'ratskellar', + 'ratte', + 'rattle', + 'ratz', + 'raven', + "raven's", + 'raven-symonnd', + 'ravenhearst', + 'ravenous', + 'ravens', + 'raving', + 'rawrimadino', + 'rawvoyage', + 'ray', + "ray's", + 'rayna', + "rayna's", + 'raynas', + 'rayos', + 'rays', + 'razorfish', + 'razz', + 'razzle', + 'razzorbacks', + 'rd', + 're', + 're-captured', + 're-org', + 'reach', + 'reached', + 'reaches', + 'reaching', + 'react', + 'reaction', + 'reactions', + 'reactive', + 'reacts', + 'read', + 'reader', + "reader's", + 'readers', + 'readied', + 'readier', + 'readies', + 'readiest', + 'reading', + 'readings', + 'reads', + 'ready', + 'readying', + 'reagent', + 'reagents', + 'real', + 'real-life', + 'realest', + 'realise', + 'realised', + 'realises', + 'realising', + 'realities', + 'reality', + 'realize', + 'realized', + 'realizer', + "realizer's", + 'realizers', + 'realizes', + 'realizing', + 'realizings', + 'really', + 'realm', + 'realms', + 'reals', + 'reaper', + 'reapers', + 'rear', + 'reared', + 'rearer', + 'rearing', + 'rearrange', + 'rearrangement', + 'rears', + 'rearup', + 'reason', + 'reasonable', + 'reasoned', + 'reasoner', + 'reasoning', + 'reasonings', + 'reasons', + 'reaver', + 'reavers', + 'rebellion', + 'rebels', + 'reboot', + 'rec', + 'recall', + 'recalled', + 'recalling', + 'recalls', + 'receipt', + 'receipts', + 'receive', + 'received', + 'receiver', + "receiver's", + 'receivers', + 'receives', + 'receiving', + 'recent', + 'recently', + 'recess', + "recess'", + 'recharge', + 'recharged', + 'recharging', + 'recipe', + 'recipes', + 'recipient', + 'reckon', + "reckonin'", + 'reckoning', + 'reclaim', + 'reclaimed', + 'reclaiming', + 'reclaims', + 'recognise', + 'recognised', + 'recognize', + 'recognized', + 'recognizer', + "recognizer's", + 'recollect', + 'recollection', + 'recombination', + 'recommend', + 'recommendation', + 'recommendations', + 'recommended', + 'recommends', + 'recon', + 'reconnect', + 'reconnection', + 'reconstruct', + 'record', + "record's", + 'recorded', + 'recorder', + "recorder's", + 'recorders', + 'recording', + 'recordings', + 'records', + 'recover', + 'recovered', + 'recoverer', + 'recovering', + 'recovers', + 'recovery', + 'recreate', + 'recreates', + 'recreation', + 'recruit', + 'recruit-a-toon', + 'recruite', + 'recruited', + 'recruits', + 'rectangle', + 'recurse', + 'recycling', + 'red', + "red's", + 'redassa', + "redbeard's", + 'reddit', + 'redeem', + 'redeemer', + 'redeems', + 'redefined', + 'redefinition', + 'redemption', + 'redemptions', + 'redeposit', + 'redevelop', + 'redfeathers', + 'redid', + 'redirector', + 'redlegs', + 'redo', + 'redone', + 'redonkulous', + 'redros', + 'reds', + 'redscurvykid', + 'redskulls', + 'reduce', + 'reduced', + 'ree', + "ree's", + 'reed', + 'reed-grass', + 'reeds', + 'reef', + 'reefs', + 'reek', + 'reeks', + 'reel', + 'reelect', + 'reeled', + 'reeling', + 'reels', + 'reepicheep', + 'reese', + 'ref', + 'refer', + 'refered', + 'referee', + 'reference', + 'referenced', + 'referencer', + 'references', + 'referencing', + 'referer', + 'referr', + 'referral', + 'referrals', + 'referred', + 'referrer', + "referrer's", + 'referrers', + 'refill', + 'refills', + 'refined', + 'reflect', + 'reflected', + 'reflecting', + 'reflection', + 'reflections', + 'reflective', + 'reflects', + 'reflex', + 'reform', + 'refrain', + 'refresh', + 'refreshed', + 'refreshen', + 'refresher', + "refresher's", + 'refreshers', + 'refreshes', + 'refreshing', + 'refuel', + 'refuge', + 'refugee', + 'refund', + 'refuse', + 'refused', + 'refuser', + 'refuses', + 'refusing', + 'reg', + 'regalia', + 'regard', + 'regarded', + 'regarding', + 'regards', + 'regatti', + 'reggae', + 'reginald', + 'region', + "region's", + 'regions', + 'register', + 'registered', + 'registering', + 'registers', + 'registration', + 'regret', + 'regrets', + 'regrow', + 'regular', + 'regularly', + 'regulars', + 'regulate', + 'regulated', + 'regulates', + 'regulating', + 'regulation', + 'regulations', + 'regulative', + 'rehearsal', + 'rehearsals', + 'reign', + 'reigning', + 'reimburse', + 'reincarnations', + 'reindeer', + "reindeer's", + 'reindeers', + 'reinvent', + 'reissue', + 'reject', + "reject's", + 'rejected', + 'rejecter', + 'rejecting', + 'rejection', + 'rejections', + 'rejective', + 'rejects', + 'rekt', + 'relate', + 'related', + 'relater', + 'relates', + 'relating', + 'relation', + 'relations', + 'relationship', + 'relationships', + 'relative', + 'relatively', + 'relatives', + 'relax', + 'relaxed', + 'relaxer', + 'relaxes', + 'relaxing', + 'relay', + 'release', + "release's", + 'released', + 'releaser', + 'releases', + 'releasing', + 'relegate', + 'relegated', + 'relegates', + 'relegating', + 'relevant', + 'relevantly', + 'reliant', + 'relic', + 'relics', + 'relied', + 'relief', + 'reliefs', + 'relier', + 'relies', + 'relive', + 'relltrem', + 'relog', + 'relogged', + 'relogging', + 'reluctant', + 'reluctantly', + 'rely', + 'relying', + 'rem', + 'remade', + 'remain', + 'remained', + 'remaining', + 'remains', + 'remake', + 'remark', + 'remarkable', + 'remarked', + 'remarking', + 'remarks', + 'rembrandt', + 'remedies', + 'remedy', + 'remember', + 'remembered', + 'rememberer', + 'remembering', + 'remembers', + 'remind', + 'reminded', + 'reminder', + 'reminding', + 'reminds', + 'remix', + 'removal', + 'remove', + 'removed', + 'remover', + 'removes', + 'removing', + 'remy', + "remy's", + 'remys', + 'rename', + 'renault', + 'rend', + 'render', + 'rendered', + 'rendering', + 'rends', + 'renee', + 'renegade', + 'renegades', + 'renew', + 'rennd', + 'rent', + 'rental', + 'rentals', + 'rented', + 'renter', + "renter's", + 'renting', + 'rents', + 'reorganize', + 'rep', + 'repaid', + 'repair', + 'repaired', + 'repairer', + "repairer's", + 'repairers', + 'repairing', + 'repairs', + 'repeat', + 'repeated', + 'repeater', + "repeater's", + 'repeaters', + 'repeating', + 'repeats', + 'replace', + 'replaced', + 'replacement', + 'replacements', + 'replacing', + 'replay', + 'replicant', + 'replication', + 'replications', + 'replicator', + 'replied', + 'replier', + 'replies', + 'reply', + 'replying', + 'report', + "report's", + 'reported', + 'reporter', + "reporter's", + 'reporters', + 'reporting', + 'reports', + 'reposition', + 'repository', + 'represent', + 'represents', + 'republic', + 'republish', + 'reputation', + 'reputations', + 'req', + 'request', + 'requested', + 'requesting', + 'requests', + 'require', + 'required', + 'requirement', + "requirement's", + 'requirements', + 'requirer', + 'requires', + 'requiring', + 'requite', + 'reran', + 'rerunning', + 'rescue', + 'rescued', + 'rescuer', + "rescuer's", + 'rescuers', + 'rescues', + 'rescuing', + 'resell', + 'reservation', + "reservation's", + 'reservations', + 'reserve', + 'reserved', + 'reserver', + 'reserves', + 'reserving', + 'reset', + 'resets', + 'resetting', + 'residence', + 'resist', + 'resistance', + 'resistant', + 'resolute', + 'resolution', + 'resolutions', + 'resolve', + 'resolved', + 'resolves', + 'resolving', + 'resort', + "resort's", + 'resorts', + 'resource', + "resource's", + 'resourced', + 'resourceful', + 'resources', + 'resourcing', + 'respect', + 'respected', + 'respecter', + 'respectful', + 'respecting', + 'respective', + 'respects', + 'respond', + 'responded', + 'responder', + "responder's", + 'responders', + 'responding', + 'responds', + 'response', + 'responser', + 'responses', + 'responsibility', + 'responsible', + 'responsions', + 'responsive', + 'rest', + 'restart', + 'restarting', + 'restaurant', + "restaurant's", + 'restaurants', + 'rested', + 'rester', + 'resting', + 'restive', + 'restless', + 'restlessness', + 'restock', + 'restocked', + 'restocking', + 'restocks', + 'restore', + 'restored', + 'restores', + 'restoring', + 'restraining', + 'rests', + 'resubmit', + 'result', + 'resulted', + 'resulting', + 'results', + 'resurresction', + 'retavick', + 'retire', + 'retired', + 'retires', + 'retiring', + 'retold', + 'retreat', + 'retried', + 'retrieve', + 'retrieving', + 'retro', + 'retry', + 'return', + "return's", + 'returned', + 'returner', + "returner's", + 'returners', + 'returning', + 'returns', + 'reused', + 'rev', + 'reveal', + 'revealed', + 'revenant', + 'revenants', + 'reverse', + 'revert', + 'review', + "review's", + 'reviewed', + 'reviewer', + 'reviewers', + 'reviewing', + 'reviews', + 'revisit', + 'revolution', + "revolution's", + 'revolutionaries', + 'revolutions', + 'revolve', + 'revolvus', + 'revs', + 'reward', + 'rewarded', + 'rewarder', + 'rewarding', + 'rewards', + 'rewind', + 'rewinding', + 'rewinds', + 'rewound', + 'rewritten', + 'rewrote', + 'rex', + 'rey', + 'rhett', + 'rhia', + 'rhineworth', + 'rhino', + "rhino's", + 'rhinobee', + 'rhinoberry', + 'rhinoblabber', + 'rhinobocker', + 'rhinoboing', + 'rhinoboom', + 'rhinobounce', + 'rhinobouncer', + 'rhinobrains', + 'rhinobubble', + 'rhinobumble', + 'rhinobump', + 'rhinobumper', + 'rhinoburger', + 'rhinochomp', + 'rhinocorn', + 'rhinocrash', + 'rhinocrumbs', + 'rhinocrump', + 'rhinocrunch', + 'rhinodoodle', + 'rhinodorf', + 'rhinoface', + 'rhinofidget', + 'rhinofink', + 'rhinofish', + 'rhinoflap', + 'rhinoflapper', + 'rhinoflinger', + 'rhinoflip', + 'rhinoflipper', + 'rhinofoot', + 'rhinofuddy', + 'rhinofussen', + 'rhinogadget', + 'rhinogargle', + 'rhinogloop', + 'rhinoglop', + 'rhinogoober', + 'rhinogoose', + 'rhinogrooven', + 'rhinohoffer', + 'rhinohopper', + 'rhinojinks', + 'rhinoklunk', + 'rhinoknees', + 'rhinomarble', + 'rhinomash', + 'rhinomonkey', + 'rhinomooch', + 'rhinomouth', + 'rhinomuddle', + 'rhinomuffin', + 'rhinomush', + 'rhinonerd', + 'rhinonoodle', + 'rhinonose', + 'rhinonugget', + 'rhinophew', + 'rhinophooey', + 'rhinopocket', + 'rhinopoof', + 'rhinopop', + 'rhinopounce', + 'rhinopow', + 'rhinopretzel', + 'rhinoquack', + 'rhinoroni', + 'rhinos', + 'rhinoscooter', + 'rhinoscreech', + 'rhinosmirk', + 'rhinosnooker', + 'rhinosnoop', + 'rhinosnout', + 'rhinosocks', + 'rhinospeed', + 'rhinospinner', + 'rhinosplat', + 'rhinosprinkles', + 'rhinosticks', + 'rhinostink', + 'rhinoswirl', + 'rhinoteeth', + 'rhinothud', + 'rhinotoes', + 'rhinoton', + 'rhinotoon', + 'rhinotooth', + 'rhinotwist', + 'rhinowhatsit', + 'rhinowhip', + 'rhinowig', + 'rhinowoof', + 'rhinozaner', + 'rhinozap', + 'rhinozapper', + 'rhinozilla', + 'rhinozoom', + 'rhoda', + 'rhodie', + 'rhonda', + 'rhubarb', + 'rhyme', + 'rhythm', + "rhythm's", + 'rhythms', + 'ribbit', + 'ribbon', + 'ribbons', + 'ric', + 'rice', + 'rich', + 'richard', + "richard's", + 'richen', + 'richer', + 'riches', + 'richest', + 'richly', + 'rick', + 'rico', + 'rid', + 'ridden', + 'ridders', + 'ride', + 'rideo', + 'rider', + "rider's", + 'riders', + 'rides', + 'ridge', + 'ridges', + 'ridiculous', + 'riding', + 'ridings', + 'ridley', + 'riff', + "riff's", + 'rig', + 'rigging', + 'right', + 'right-on-thyme', + 'righted', + 'righten', + 'righteous', + 'righter', + 'rightful', + 'righting', + 'rightly', + 'rights', + 'rigs', + 'riley', + "riley's", + 'rileys', + 'rill', + 'ring', + "ring's", + 'ringing', + 'rings', + 'rink', + "rink's", + 'rinks', + 'rinky', + 'riot', + 'rip', + 'ripley', + 'riposte', + 'ripple', + 'riptide', + 'rise', + 'riser', + "riser's", + 'risers', + 'rises', + 'rising', + 'risings', + 'risk', + 'risk-takers', + 'risked', + 'risker', + 'risking', + 'risks', + 'risky', + 'rita', + 'rites', + 'ritzy', + 'rivalry', + 'rivals', + 'river', + "river's", + 'riverbank', + "riverbank's", + 'riverbanks', + 'rivers', + 'riverveil', + 'rizzo', + 'rlly', + 'rly', + 'rm', + 'rna', + 'ro', + 'road', + "road's", + 'roadrunner', + 'roads', + 'roadster', + 'roam', + 'roams', + 'roar', + 'roared', + 'roarer', + 'roaring', + 'roars', + 'roast', + 'roasted', + 'roasting', + 'roasts', + 'rob', + "rob's", + 'robber', + "robber's", + 'robbers', + 'robby', + "robby's", + 'robbys', + 'robed', + 'rober', + 'robers7', + 'robert', + "robert's", + 'roberts', + 'robin', + "robin's", + 'robing', + 'robins', + 'robinson', + "robinson's", + 'robinsons', + 'robo', + 'robobee', + 'roboberry', + 'roboblabber', + 'robobocker', + 'roboboing', + 'roboboom', + 'robobounce', + 'robobouncer', + 'robobrains', + 'robobubble', + 'robobumble', + 'robobump', + 'robobumper', + 'roboburger', + 'robochomp', + 'robocorn', + 'robocrash', + 'robocrumbs', + 'robocrump', + 'robocrunch', + 'robodoodle', + 'robodorf', + 'roboface', + 'robofidget', + 'robofink', + 'robofish', + 'roboflap', + 'roboflapper', + 'roboflinger', + 'roboflip', + 'roboflipper', + 'robofoot', + 'robofuddy', + 'robofussen', + 'robogadget', + 'robogargle', + 'robogloop', + 'roboglop', + 'robogoober', + 'robogoose', + 'robogrooven', + 'robohoffer', + 'robohopper', + 'robojinks', + 'roboklunk', + 'roboknees', + 'robomarble', + 'robomash', + 'robomonkey', + 'robomooch', + 'robomouth', + 'robomuddle', + 'robomuffin', + 'robomush', + 'robonerd', + 'robonoodle', + 'robonose', + 'robonugget', + 'robophew', + 'robophooey', + 'robopocket', + 'robopoof', + 'robopop', + 'robopounce', + 'robopow', + 'robopretzel', + 'roboquack', + 'roboroni', + 'roboscooter', + 'roboscreech', + 'robosmirk', + 'robosnooker', + 'robosnoop', + 'robosnout', + 'robosocks', + 'robospeed', + 'robospinner', + 'robosplat', + 'robosprinkles', + 'robosticks', + 'robostink', + 'roboswirl', + 'robot', + "robot's", + 'roboteeth', + 'robothud', + 'robotic', + 'robotoes', + 'robotomy', + 'roboton', + 'robotoon', + 'robotooth', + 'robots', + 'robotwist', + 'robowhatsit', + 'robowhip', + 'robowig', + 'robowoof', + 'robozaner', + 'robozap', + 'robozapper', + 'robozilla', + 'robozoom', + 'robs', + 'robson', + 'robust', + 'rocco', + 'rochelle', + 'rock', + "rock'n'spell", + "rock'n'words", + "rock's", + 'rocka', + 'rocked', + 'rockenbee', + 'rockenberry', + 'rockenblabber', + 'rockenbocker', + 'rockenboing', + 'rockenboom', + 'rockenbounce', + 'rockenbouncer', + 'rockenbrains', + 'rockenbubble', + 'rockenbumble', + 'rockenbump', + 'rockenbumper', + 'rockenburger', + 'rockenchomp', + 'rockencorn', + 'rockencrash', + 'rockencrumbs', + 'rockencrump', + 'rockencrunch', + 'rockendoodle', + 'rockendorf', + 'rockenface', + 'rockenfidget', + 'rockenfink', + 'rockenfish', + 'rockenflap', + 'rockenflapper', + 'rockenflinger', + 'rockenflip', + 'rockenflipper', + 'rockenfoot', + 'rockenfuddy', + 'rockenfussen', + 'rockengadget', + 'rockengargle', + 'rockengloop', + 'rockenglop', + 'rockengoober', + 'rockengoose', + 'rockengrooven', + 'rockenhoffer', + 'rockenhopper', + 'rockenjinks', + 'rockenklunk', + 'rockenknees', + 'rockenmarble', + 'rockenmash', + 'rockenmonkey', + 'rockenmooch', + 'rockenmouth', + 'rockenmuddle', + 'rockenmuffin', + 'rockenmush', + 'rockennerd', + 'rockennoodle', + 'rockennose', + 'rockennugget', + 'rockenphew', + 'rockenphooey', + 'rockenpirate', + 'rockenpocket', + 'rockenpoof', + 'rockenpop', + 'rockenpounce', + 'rockenpow', + 'rockenpretzel', + 'rockenquack', + 'rockenroni', + 'rockenscooter', + 'rockenscreech', + 'rockensmirk', + 'rockensnooker', + 'rockensnoop', + 'rockensnout', + 'rockensocks', + 'rockenspeed', + 'rockenspinner', + 'rockensplat', + 'rockensprinkles', + 'rockensticks', + 'rockenstink', + 'rockenswirl', + 'rockenteeth', + 'rockenthud', + 'rockentoes', + 'rockenton', + 'rockentoon', + 'rockentooth', + 'rockentwist', + 'rockenwhatsit', + 'rockenwhip', + 'rockenwig', + 'rockenwoof', + 'rockenzaner', + 'rockenzap', + 'rockenzapper', + 'rockenzilla', + 'rockenzoom', + 'rocker', + "rocker's", + 'rockers', + 'rocket', + "rocket's", + 'rocketed', + 'rocketeer', + 'rocketing', + 'rockets', + 'rocketship', + 'rocketships', + 'rockhead', + 'rockhopper', + "rockhopper's", + 'rocking', + 'rocks', + 'rockstar', + 'rockstarbr', + 'rocky', + "rocky's", + 'rod', + 'rodeo', + 'rodgerrodger', + 'rods', + 'roe', + 'rof', + 'rofl', + 'roger', + "roger's", + 'rogerrabbit', + 'rogers', + 'rogue', + "rogue's", + 'rogues', + 'role', + "role's", + 'roles', + 'roll', + 'rollback', + 'rolle', + 'rolled', + 'roller', + 'roller-ramp', + 'rollers', + 'rolling', + 'rollo', + 'rollover', + 'rolls', + 'rolodex', + 'rom', + 'roman', + 'romana', + 'romany', + 'romeo', + 'ron', + "ron's", + 'rongo', + 'roo', + "roo's", + 'roof', + 'roofed', + 'roofer', + 'roofers', + 'roofing', + 'roofs', + 'rook', + 'rookie', + 'rooks', + 'room', + "room's", + 'roomed', + 'roomer', + 'roomers', + 'rooming', + 'rooms', + 'roos', + 'rooster', + 'roosters', + 'root', + "root's", + 'rooting', + 'roots', + 'rope', + "rope's", + 'ropes', + 'roquica', + 'roquos', + 'rory', + 'rosa', + 'roscoe', + 'rose', + "rose's", + 'rosebush', + 'rosehips', + 'rosemary', + 'roses', + 'rosetta', + "rosetta's", + 'rosey', + 'rosh', + 'rosie', + 'rosy', + 'rotate', + 'rotates', + 'rotfl', + 'rotten', + 'rottenly', + 'rouge', + "rouge's", + 'rougeport', + 'rouges', + 'rough', + 'roughtongue', + 'rougue', + 'round', + 'round-a-bout', + 'round-up', + 'rounded', + 'rounder', + 'rounders', + 'roundest', + 'roundhouse', + 'rounding', + 'roundly', + 'roundness', + 'rounds', + 'roundup', + 'route', + 'routed', + 'router', + "router's", + 'routers', + 'routes', + 'routines', + 'routing', + 'routings', + 'roux', + 'rove', + 'row', + 'rowdy', + 'rowed', + 'rowing', + 'rowlf', + 'rows', + 'roxanne', + 'roxy', + 'royal', + 'royale', + 'royally', + 'royals', + 'royalty', + 'royce', + 'royko', + 'roz', + 'rruff', + 'rsnail', + 'rsync', + 'ru', + 'rub', + 'rubber', + 'rubbery', + 'rubies', + 'ruby', + "ruby's", + 'rubys', + 'rudacho', + 'rudatake', + 'rudatori', + 'rudder', + 'rudderly', + 'rudders', + 'rude', + 'rudy', + 'rudyard', + "rudyard's", + 'rue', + 'rufescent', + 'ruff', + 'ruffians', + 'ruffle', + 'rufflebee', + 'ruffleberry', + 'ruffleblabber', + 'rufflebocker', + 'ruffleboing', + 'ruffleboom', + 'rufflebounce', + 'rufflebouncer', + 'rufflebrains', + 'rufflebubble', + 'rufflebumble', + 'rufflebump', + 'rufflebumper', + 'ruffleburger', + 'rufflechomp', + 'rufflecorn', + 'rufflecrash', + 'rufflecrumbs', + 'rufflecrump', + 'rufflecrunch', + 'ruffledoodle', + 'ruffledorf', + 'ruffleface', + 'rufflefidget', + 'rufflefink', + 'rufflefish', + 'ruffleflap', + 'ruffleflapper', + 'ruffleflinger', + 'ruffleflip', + 'ruffleflipper', + 'rufflefoot', + 'rufflefuddy', + 'rufflefussen', + 'rufflegadget', + 'rufflegargle', + 'rufflegloop', + 'ruffleglop', + 'rufflegoober', + 'rufflegoose', + 'rufflegrooven', + 'rufflehoffer', + 'rufflehopper', + 'rufflejinks', + 'ruffleklunk', + 'ruffleknees', + 'rufflemarble', + 'rufflemash', + 'rufflemonkey', + 'rufflemooch', + 'rufflemouth', + 'rufflemuddle', + 'rufflemuffin', + 'rufflemush', + 'rufflenerd', + 'rufflenoodle', + 'rufflenose', + 'rufflenugget', + 'rufflephew', + 'rufflephooey', + 'rufflepocket', + 'rufflepoof', + 'rufflepop', + 'rufflepounce', + 'rufflepow', + 'rufflepretzel', + 'rufflequack', + 'ruffleroni', + 'rufflescooter', + 'rufflescreech', + 'rufflesmirk', + 'rufflesnooker', + 'rufflesnoop', + 'rufflesnout', + 'rufflesocks', + 'rufflespeed', + 'rufflespinner', + 'rufflesplat', + 'rufflesprinkles', + 'rufflesticks', + 'rufflestink', + 'ruffleswirl', + 'ruffleteeth', + 'rufflethud', + 'ruffletoes', + 'ruffleton', + 'ruffletoon', + 'ruffletooth', + 'ruffletwist', + 'rufflewhatsit', + 'rufflewhip', + 'rufflewig', + 'rufflewoof', + 'rufflezaner', + 'rufflezap', + 'rufflezapper', + 'rufflezilla', + 'rufflezoom', + 'rufus', + "rufus'", + 'rug', + 'rugged', + 'rugs', + 'ruin', + 'ruination', + 'ruined', + 'ruins', + 'rukia', + 'rule', + 'ruled', + 'ruler', + 'rulers', + 'rules', + 'ruling', + 'rulings', + 'rumble', + 'rumbly', + 'rumor', + 'rumors', + 'rumrun', + 'rumrunner', + "rumrunner's", + 'run', + "run's", + 'runawas', + 'runaway', + "runaway's", + 'runaways', + 'rung', + 'runner', + 'runners', + "runnin'", + 'running', + 'runo', + "runo's", + 'runoff', + 'runos', + 'runs', + 'runway', + 'rupert', + 'rural', + 'rush', + 'rushed', + 'rushes', + 'rushing', + 'russell', + 'russia', + 'russo', + 'rust', + 'rusted', + 'rusteze', + 'rustic', + 'rusty', + 'ruth', + 'rutherford', + 'ruthless', + 'ryan', + "ryan's", + 'ryans', + 'rydrake', + 'rygazelle', + 'ryza', + "s'mores", + 's.o.s.', + 'saab', + 'sabada', + 'sabago', + 'sabeltann', + 'saber', + 'sabona', + 'sabos', + 'sabotage', + 'sabotaged', + 'sabotages', + 'saboteur', + 'saboteurs', + 'sabrefish', + 'sabrina', + "sabrina's", + 'sabrinas', + 'sacked', + 'sacred', + 'sad', + 'sadden', + 'saddens', + 'saddest', + 'saddle', + 'saddlebag', + 'saddlebags', + 'saddles', + 'sadie', + "sadie's", + 'sadly', + 'sadness', + 'safari', + 'safaris', + 'safe', + 'safely', + 'safer', + 'safes', + 'safest', + 'safetied', + 'safeties', + 'safety', + "safety's", + 'safetying', + 'saffron', + 'sage', + 'sahara', + 'said', + 'sail', + 'sailcloth', + 'sailed', + 'sailer', + 'sailers', + 'sailing', + "sailing's", + 'sailingfoxes', + 'sailor', + "sailor's", + 'sailorly', + 'sailors', + 'sails', + 'sailsmen', + 'saint', + 'saints', + 'saj', + 'sake', + 'sal', + 'salad', + 'salads', + 'salama', + 'sale', + "sale's", + 'saleen', + 'sales', + 'salesman', + 'salesmen', + 'salient', + 'saliently', + 'saligos', + 'sally', + "sally's", + 'sallys', + 'salmon', + 'salmons', + 'saloon', + 'salt', + 'salt-sifting', + 'saltpeter', + 'salty', + 'saludos', + 'salutations', + 'salute', + 'salvage', + 'salve', + 'sam', + 'sama', + 'samantha', + 'samba', + 'same', + 'samerobo', + 'sametosu', + 'sametto', + 'sample', + "sample's", + 'sampled', + 'sampler', + 'samplers', + 'samples', + 'sampling', + 'samplings', + 'samuel', + 'samugeki', + 'samukabu', + 'samurite', + 'san', + 'sanassa', + 'sand', + 'sand-sorting', + "sandal's", + 'sandals', + 'sandalwood', + 'sandbag', + 'sandcastle', + "sandcastle's", + 'sandcastles', + 'sanded', + 'sander', + 'sanders', + 'sanding', + 'sandman', + "sandman's", + 'sands', + 'sandwich', + 'sandwiches', + 'sandy', + 'sane', + 'sang', + 'sanguine', + 'sanila', + 'sanity', + 'sanjay', + 'sanquilla', + 'sans', + 'santa', + "santa's", + 'santia', + 'sao', + 'sap', + 'saphera', + 'saphire', + 'sapphire', + 'sappy', + 'saps', + 'sara', + 'sarah', + 'sarcastic', + 'sardine', + 'sardines', + 'sarge', + 'sarges', + 'sark', + 'sarong', + 'sas', + 'sash', + 'sasha', + 'sashes', + 'sassafras', + 'sat', + 'satchel', + "satchel's", + 'sated', + 'satellite', + "satellite's", + 'satellites', + 'sating', + 'satisfactory', + 'saturday', + "saturday's", + 'saturdays', + 'saturn', + "saturn's", + 'satyr', + 'satyrs', + 'sauce', + 'saucer', + 'saucers', + 'sauces', + 'sauvignon', + 'savada', + 'savage', + 'savagers', + 'savannah', + 'savano', + 'save', + 'saved', + 'saver', + 'savers', + 'saves', + 'saveyoursoul', + 'savica', + 'savies', + 'savigos', + 'saving', + 'savings', + 'savor', + 'savory', + 'savvy', + 'savvypirates', + 'savys', + 'saw', + 'sawdust', + 'sawing', + 'saws', + 'sawyer', + "sawyer's", + 'saxophones', + 'say', + 'sayer', + "sayer's", + 'sayers', + "sayin'", + 'saying', + 'sayings', + 'says', + 'sbhq', + 'sbt', + 'sbtgame', + 'sc', + 'sc+', + 'scabbard', + 'scabbards', + 'scabs', + 'scalawag', + 'scalawags', + 'scale', + 'scaled', + 'scaler', + 'scalers', + 'scales', + 'scaling', + 'scalings', + 'scaliwags', + 'scalleywags', + 'scallop', + 'scalloped', + 'scally', + 'scallywag', + 'scallywags', + 'scamps', + 'scan', + 'scanner', + 'scans', + 'scar', + "scar's", + 'scards', + 'scare', + 'scared', + 'scarer', + 'scares', + 'scarf', + 'scarier', + 'scariest', + "scarin'", + 'scaring', + 'scarlet', + "scarlet's", + 'scarlets', + 'scarlett', + "scarlett's", + 'scarletundrground', + 'scars', + 'scarves', + 'scary', + 'scatter', + 'scatty', + 'scavenger', + "scavenger's", + 'scavengers', + 'scelitons', + 'scene', + "scene's", + 'scenery', + 'scenes', + 'scenic', + 'scepter', + 'scepters', + 'schedule', + "schedule's", + 'schedules', + 'schell', + 'schellgames', + 'scheme', + 'schemer', + 'schemes', + 'scheming', + 'schmaltzy', + 'schmooze', + 'scholarship', + 'scholastic', + 'school', + 'schools', + "schumann's", + 'sci-fi', + 'science', + "science's", + 'sciences', + 'scientific', + 'scientist', + "scientist's", + 'scientists', + "scissor's", + 'scissorfish', + 'scissors', + 'scold', + 'scones', + 'scoop', + 'scooper', + 'scooper-ball', + 'scooperball', + 'scoops', + 'scoot', + 'scooter', + 'scooters', + 'scope', + 'scorch', + 'scorching', + 'score', + 'scoreboard', + 'scoreboards', + 'scored', + 'scores', + 'scoring', + 'scorn', + 'scorpio', + 'scorpion', + 'scorpions', + 'scott', + 'scoundrel', + 'scoundrels', + 'scourge', + 'scourges', + 'scout', + 'scouting', + 'scouts', + 'scowl', + 'scramble', + 'scrambled', + 'scrap', + 'scrap-metal', + 'scrapbook', + 'scrape', + 'scraped', + 'scrapes', + 'scraping', + 'scrapped', + 'scrapping', + 'scrappy', + 'scraps', + 'scratch', + 'scratches', + 'scratchier', + 'scratchy', + 'scrawled', + 'scrawny', + 'scream', + "scream's", + 'screamed', + 'screamer', + 'screamers', + 'screaming', + 'screech', + 'screeched', + 'screeches', + 'screeching', + 'screen', + 'screened', + 'screener', + 'screenhog', + 'screening', + 'screenings', + 'screens', + 'screensaver', + 'screenshot', + 'screenshots', + 'screwball', + 'screwy', + 'scribe', + 'script', + 'scripts', + 'scroll', + 'scrounge', + 'scrounged', + 'scrounges', + 'scrounging', + 'scruffy', + 'scrumptious', + 'scuba', + 'scullery', + 'sculpt', + 'sculpture', + 'sculpture-things', + 'sculptured', + 'sculptures', + 'scurrvy', + 'scurry', + 'scurvey', + 'scurvy', + 'scurvydog', + 'scuttle', + "scuttle's", + 'scuttlebutt', + 'scuttles', + 'scuvy', + 'scythely', + 'sea', + 'seabass', + 'seabourne', + 'seachest', + 'seademons', + 'seadogs', + 'seadragons', + 'seadragonz', + 'seafarer', + 'seafarers', + 'seafoams', + 'seafood', + 'seafurys', + 'seagull', + 'seagulls', + 'seahags', + 'seahorse', + 'seahorses', + 'seahounds', + 'seal', + 'sealands', + 'seaman', + 'seamasterfr', + 'seamstress', + 'seamus', + 'sean', + 'seance', + 'sear', + 'searaiders', + 'search', + 'searchable', + 'searched', + 'searcher', + 'searchers', + 'searches', + 'searching', + 'searchings', + 'seas', + 'seashadowselite', + 'seashell', + 'seashells', + 'seashore', + "seashore's", + 'seashores', + 'seaside', + "seaside's", + 'seasides', + 'seaskulls', + 'seaslipperdogs', + 'seasnake', + 'season', + "season's", + 'seasonal', + 'seasoned', + 'seasoner', + 'seasoners', + 'seasoning', + 'seasonings', + 'seasonly', + 'seasons', + 'seat', + 'seated', + 'seater', + 'seating', + 'seats', + 'seaweed', + 'seaweeds', + 'sebastian', + 'second', + 'secondary', + 'seconds', + 'secret', + 'secreted', + 'secreting', + 'secretive', + 'secretly', + 'secrets', + 'section', + 'sectioned', + 'sectioning', + 'sections', + 'sector', + 'secure', + 'secured', + 'securely', + 'securer', + 'secures', + 'securing', + 'securings', + 'securities', + 'security', + 'see', + 'seed', + 'seedling', + 'seedlings', + 'seedpod', + 'seeds', + 'seeing', + 'seek', + 'seeker', + 'seekers', + 'seeking', + 'seeks', + 'seeley', + 'seem', + 'seemed', + 'seeming', + 'seemly', + 'seems', + 'seen', + 'seer', + 'seers', + 'sees', + 'seeya', + 'segu', + 'segulara', + 'segulos', + 'seige', + 'seing', + 'select', + 'selected', + 'selecting', + 'selection', + "selection's", + 'selections', + 'selective', + 'selects', + 'self', + 'self-absorbed', + 'self-centered', + 'self-important', + 'self-possessed', + 'selfie', + 'selfish', + 'selfishness', + 'sell', + 'sellbot', + 'sellbotfrontentrance', + 'sellbots', + 'sellbotsideentrance', + 'seller', + 'sellers', + 'selling', + 'sells', + 'seltzer', + 'seltzers', + 'semi-palindrome', + 'seminar', + 'seminars', + 'semper', + 'senary', + 'send', + 'sender', + 'senders', + 'sending', + 'sends', + 'senior', + 'seniors', + 'senkro', + 'sennet', + 'senpu', + 'senpuga', + 'senpura', + 'sensation', + 'sense', + 'sensed', + 'sensei', + 'senses', + 'sensing', + 'sensor', + 'sensors', + 'sent', + 'sentence', + 'sentenced', + 'sentences', + 'sentencing', + 'sentinel', + 'sentinels', + 'sep', + 'separate', + 'separated', + 'separately', + 'separates', + 'separating', + 'separation', + 'separations', + 'separative', + 'september', + 'septenary', + 'sequence', + 'sequences', + 'sequin', + 'serena', + 'serendipity', + 'serene', + 'sergeant', + 'sergeants', + 'sergio', + 'series', + 'serious', + 'seriously', + 'serphants', + 'servants', + 'serve', + 'served', + 'server', + 'servers', + 'serves', + 'service', + "service's", + 'serviced', + 'servicer', + 'services', + 'servicing', + 'serving', + 'servings', + 'sesame', + 'sesame-seed', + 'session', + "session's", + 'sessions', + 'set', + "set's", + 'sets', + 'setter', + 'setting', + 'settings', + 'settle', + 'settled', + 'settler', + 'settlers', + 'settles', + 'settling', + 'settlings', + 'setup', + 'seven', + 'sever', + 'several', + 'severally', + 'severals', + 'severe', + 'severed', + 'severely', + 'seville', + 'sew', + 'sewing', + 'sews', + 'sf', + 'sgt', + 'sh-boom', + 'shabby', + 'shack', + 'shackleby', + 'shackles', + 'shade', + 'shader', + 'shades', + 'shadow', + 'shadowcrows', + 'shadowed', + 'shadower', + 'shadowhunters', + 'shadowing', + 'shadowofthedead', + 'shadows', + 'shadowy', + 'shady', + 'shaggy', + 'shake', + 'shaken', + 'shaker', + 'shakers', + 'shakes', + 'shakey', + "shakey's", + 'shakin', + "shakin'", + 'shaking', + 'shakoblad', + 'shakor', + 'shaky', + 'shall', + 'shallow', + 'shallows', + 'shame', + 'shamrock', + 'shamrocks', + 'shane', + "shane's", + "shang's", + 'shanna', + "shanna's", + 'shannara', + 'shanty', + 'shape', + 'shaped', + 'shaper', + 'shapers', + 'shapes', + 'shaping', + 'shard', + 'shards', + 'share', + 'shared', + 'sharer', + 'sharers', + 'shares', + 'sharing', + 'shark', + "shark's", + 'sharkbait', + 'sharkhunters', + 'sharks', + 'sharky', + 'sharon', + 'sharp', + 'sharpay', + "sharpay's", + 'sharpen', + 'sharpened', + 'sharpie', + 'shatter', + 'shazam', + 'she', + "she'll", + "she's", + 'sheared', + 'shearing', + 'shed', + 'sheep', + 'sheeps', + 'sheer', + 'sheila', + 'sheild', + 'sheldon', + 'shelf', + 'shell', + 'shellbacks', + 'shellhorns', + 'shells', + 'shelly', + 'shenanigans', + 'shep', + 'sheriff', + "sheriff's", + 'sheriffs', + 'sherry', + 'shes', + 'shh', + 'shhh', + 'shhhhhh', + 'shiba', + 'shield', + 'shields', + 'shift', + 'shifts', + 'shifty', + 'shimadoros', + 'shimainu', + 'shimanoto', + 'shimmer', + 'shimmering', + 'shimmy', + 'shin', + 'shine', + 'shines', + 'shining', + 'shiny', + 'ship', + "ship's", + 'shipman', + 'shipmates', + 'shipment', + 'shipments', + 'shippart', + 'shippers', + 'shipping', + 'ships', + 'shipwarriors', + 'shipwreck', + 'shipwrecked', + 'shipwreckers', + 'shipwrecks', + 'shipwright', + 'shipwrights', + 'shipyard', + 'shire', + 'shirley', + 'shirt', + 'shirt.', + 'shirting', + 'shirts', + 'shirtspot', + 'shiver', + "shiverin'", + 'shivering', + 'shochett', + 'shock', + 'shocked', + 'shocker', + 'shockers', + 'shocking', + 'shockit', + 'shocks', + 'shockwave', + 'shockwaves', + 'shoe', + 'shoe-making', + 'shoes', + 'shoeshine', + 'shogyo', + 'shone', + 'shook', + 'shop', + "shop's", + 'shopaholic', + 'shopaholics', + 'shoped', + 'shoper', + 'shoping', + 'shoppe', + 'shopped', + 'shopper', + 'shopping', + 'shops', + 'shore', + 'shores', + 'short', + 'short-stack', + 'short-term', + 'shortcake', + 'shortcut', + 'shortcuts', + 'shorted', + 'shorten', + 'shortens', + 'shorter', + 'shortest', + 'shorting', + 'shortly', + 'shorts', + 'shorts.', + 'shortsheeter', + 'shorty', + 'shoshanna', + 'shot', + 'shots', + 'should', + "should've", + 'shoulda', + 'shoulder', + 'shouldered', + 'shouldering', + 'shoulders', + 'shouldest', + "shouldn'a", + "shouldn't", + 'shouldnt', + 'shout', + 'shouted', + 'shouter', + 'shouters', + 'shouting', + 'shouts', + 'shove', + 'shoved', + 'shovel', + 'shovels', + 'shoves', + 'shoving', + 'show', + 'show-offs', + 'showbiz', + 'showcase', + 'showdown', + 'showed', + 'showing', + 'showings', + 'shown', + 'shows', + 'showtime', + 'showy', + 'shrapnel', + 'shred', + 'shredding', + 'shriek', + 'shrieked', + 'shrieking', + 'shrieks', + 'shrill', + 'shrimp', + 'shrink', + 'shrinking', + 'shrug', + 'shrunk', + 'shrunken', + 'shticker', + 'shtickers', + 'shucks', + 'shuffle', + 'shuffled', + 'shuffles', + 'shuffling', + 'shulla', + 'shut', + 'shut-eye', + 'shutdown', + 'shuts', + 'shuttle', + 'shy', + 'siamese', + 'sib', + "sib's", + 'siblings', + 'sick', + 'sickness', + 'sicknesses', + 'sid', + 'side', + 'sidearm', + 'sideburns', + 'sided', + 'sidekick', + 'sideline', + 'sidepipes', + 'sides', + 'sidesplitter', + "sidesplitter's", + 'sidewalk', + "sidewalk's", + 'sidewalks', + 'sidewinder', + 'sidewinders', + 'siding', + 'sidings', + 'siefer', + 'siege', + 'sieges', + 'sienna', + 'sierra', + 'siesta', + 'siestas', + 'sif', + 'sigh', + 'sighes', + 'sight', + 'sighted', + 'sighter', + 'sighting', + 'sightings', + 'sightly', + 'sights', + 'sightsee', + 'sightseeing', + 'sightsees', + 'sign', + 'signal', + 'signaled', + 'signaling', + 'signally', + 'signals', + 'signature', + 'signed', + 'signer', + 'signers', + 'signing', + 'signs', + 'silence', + 'silenced', + 'silences', + 'silencing', + 'silent', + 'silently', + 'silents', + 'silenus', + 'silhouette', + 'silk', + 'silken', + 'silks', + 'silkworm', + 'sillier', + 'silliest', + 'silliness', + 'silly', + 'sillypirate', + 'sillywillows', + 'silo', + 'silos', + 'silver', + 'silver943', + 'silverbell', + 'silvered', + 'silverer', + 'silvering', + 'silverly', + 'silvermist', + "silvermist's", + 'silvers', + 'silverwolves', + 'silvery', + 'simba', + "simba's", + 'simian', + 'similar', + 'similarly', + 'simile', + 'simmer', + 'simon', + 'simone', + 'simple', + 'simpler', + 'simples', + 'simplest', + 'simplified', + 'simplifies', + 'simplify', + 'simplifying', + 'simply', + 'simulator', + 'since', + 'sincere', + 'sing', + 'sing-a-longs', + 'sing-along', + 'singapore', + 'singaporean', + 'singe', + 'singed', + 'singer', + 'singers', + "singin'", + 'singing', + 'single', + 'sings', + 'sinjin', + 'sink', + "sink's", + 'sinker', + 'sinkers', + 'sinking', + 'sinks', + 'sins', + 'sip', + 'sir', + 'sirbiscuit', + 'siren', + "siren's", + 'sirens', + 'siring', + 'sirs', + 'sis', + 'sister', + "sister's", + 'sisterhood', + 'sisters', + "sisters'", + 'sit', + 'sitch', + 'site', + "site's", + 'sited', + 'sites', + 'siting', + 'sits', + 'sitting', + 'situation', + 'situations', + 'six', + 'size', + 'sized', + 'sizer', + 'sizers', + 'sizes', + 'sizing', + 'sizings', + 'sizzle', + 'sizzlin', + "sizzlin'", + 'sizzling', + 'skarlett', + 'skate', + 'skateboard', + "skateboard's", + 'skateboarded', + 'skateboarder', + 'skateboarders', + 'skateboarding', + 'skateboardings', + 'skateboards', + 'skated', + 'skater', + "skater's", + 'skaters', + 'skates', + 'skating', + 'skel', + 'skelecog', + 'skelecogs', + 'skeletalknights', + 'skeleton', + 'skeletoncrew', + 'skeletonhunters', + 'skeletons', + 'skellington', + 'skeptical', + 'sketch', + 'sketchbook', + 'ski', + 'skied', + 'skier', + 'skiers', + 'skies', + 'skiff', + 'skiing', + 'skill', + 'skilled', + 'skillful', + 'skilling', + 'skills', + 'skimmers', + 'skinny', + 'skip', + 'skipped', + 'skipper', + 'skippers', + 'skipping', + 'skips', + 'skirmish', + 'skirmished', + 'skirmishes', + 'skirmishing', + 'skirt', + 'skirted', + 'skirter', + 'skirting', + 'skirts', + 'skis', + 'skits', + 'skrillex', + 'skulky', + 'skull', + "skull's", + 'skullcap-and-comfrey', + 'skulled', + 'skullraiders', + 'skulls', + 'skunk', + 'skunks', + 'sky', + "sky's", + 'skyak', + 'skydiving', + 'skying', + 'skyler', + 'skype', + 'skyrocketing', + 'skysail', + 'skyway', + "skyway's", + 'slam', + 'slam-dunk', + 'slammin', + "slammin'", + 'slapped', + 'slapper', + 'slaps', + 'slate', + "slate's", + 'slater', + 'slates', + 'slaughter', + 'slaves', + 'sled', + 'sleds', + 'sleek', + 'sleep', + 'sleeper', + 'sleepers', + 'sleeping', + 'sleepless', + 'sleeps', + 'sleepwalking', + 'sleepy', + "sleepy's", + 'sleet', + 'sleeting', + 'sleeve', + 'sleeveless', + 'sleigh', + 'sleighing', + 'sleighs', + 'slendy', + 'slept', + 'sli', + 'slice', + 'sliced', + 'slicer', + 'slicers', + 'slices', + 'slicing', + 'slick', + 'slid', + 'slide', + 'slides', + 'sliding', + 'slier', + 'slight', + 'slighted', + 'slighter', + 'slightest', + 'slighting', + 'slightly', + 'slights', + 'slim', + "slim's", + 'slims', + 'slimy', + 'slinger', + 'slingers', + 'slingshot', + 'slingshots', + 'slip', + 'slipper', + 'slippers', + 'slips', + 'slither', + 'slithered', + 'sliver', + 'slobs', + 'sloop', + 'sloopers', + 'sloops', + 'slopes', + 'slots', + 'slow', + 'slow-thinker', + 'slowed', + 'slower', + 'slowest', + 'slowing', + 'slowly', + 'slows', + 'sludge', + 'sludges', + 'slug', + 'slugged', + 'slugging', + 'sluggish', + 'sluggo', + 'slugs', + 'slumber', + 'slumbered', + 'slumbering', + 'slumbers', + 'slump', + 'slush', + 'slushy', + 'sly', + 'smackdab', + 'small', + 'smallband', + 'smaller', + 'smallest', + 'smart', + 'smartguy', + 'smartly', + 'smarts', + 'smarty', + 'smarty-pants', + 'smartybee', + 'smartyberry', + 'smartyblabber', + 'smartybocker', + 'smartyboing', + 'smartyboom', + 'smartybounce', + 'smartybouncer', + 'smartybrains', + 'smartybubble', + 'smartybumble', + 'smartybump', + 'smartybumper', + 'smartyburger', + 'smartychomp', + 'smartycorn', + 'smartycrash', + 'smartycrumbs', + 'smartycrump', + 'smartycrunch', + 'smartydoodle', + 'smartydorf', + 'smartyface', + 'smartyfidget', + 'smartyfink', + 'smartyfish', + 'smartyflap', + 'smartyflapper', + 'smartyflinger', + 'smartyflip', + 'smartyflipper', + 'smartyfoot', + 'smartyfuddy', + 'smartyfussen', + 'smartygadget', + 'smartygargle', + 'smartygloop', + 'smartyglop', + 'smartygoober', + 'smartygoose', + 'smartygrooven', + 'smartyhoffer', + 'smartyhopper', + 'smartyjinks', + 'smartyklunk', + 'smartyknees', + 'smartymarble', + 'smartymash', + 'smartymonkey', + 'smartymooch', + 'smartymouth', + 'smartymuddle', + 'smartymuffin', + 'smartymush', + 'smartynerd', + 'smartynoodle', + 'smartynose', + 'smartynugget', + 'smartyphew', + 'smartyphooey', + 'smartypocket', + 'smartypoof', + 'smartypop', + 'smartypounce', + 'smartypow', + 'smartypretzel', + 'smartyquack', + 'smartyroni', + 'smartyscooter', + 'smartyscreech', + 'smartysmirk', + 'smartysnooker', + 'smartysnoop', + 'smartysnout', + 'smartysocks', + 'smartyspeed', + 'smartyspinner', + 'smartysplat', + 'smartysprinkles', + 'smartysticks', + 'smartystink', + 'smartyswirl', + 'smartyteeth', + 'smartythud', + 'smartytoes', + 'smartyton', + 'smartytoon', + 'smartytooth', + 'smartytwist', + 'smartywhatsit', + 'smartywhip', + 'smartywig', + 'smartywoof', + 'smartyzaner', + 'smartyzap', + 'smartyzapper', + 'smartyzilla', + 'smartyzoom', + 'smash', + 'smashed', + 'smasher', + 'smashers', + 'smashes', + 'smashing', + 'smell', + 'smelled', + 'smeller', + 'smelling', + 'smells', + 'smelly', + 'smile', + 'smiled', + 'smiler', + 'smiles', + 'smiley', + 'smiling', + 'smirk', + 'smirking', + 'smirky', + 'smith', + 'smithery', + 'smithing', + 'smitty', + "smitty's", + 'smock', + 'smokey', + 'smokey-blue', + "smokin'", + 'smolder', + 'smoldered', + 'smoldering', + 'smolders', + 'smooch', + 'smoothed', + 'smoothen', + 'smoother', + 'smoothers', + 'smoothes', + 'smoothest', + 'smoothie', + 'smoothing', + 'smoothly', + 'smudgy', + 'smulley', + 'smythe', + 'snack', + 'snackdown', + 'snag', + 'snag-it', + 'snaggletooth', + 'snail', + "snail's", + 'snails', + 'snaked', + 'snakers', + 'snakes', + 'snap', + 'snapdragon', + 'snapdragons', + 'snapped', + 'snapper', + 'snappy', + 'snaps', + 'snapshot', + 'snare', + 'snazzy', + 'sneak', + 'sneaker', + 'sneakers', + 'sneaks', + 'sneaky', + 'sneeze', + 'sneezewort', + 'sneezy', + "sneezy's", + 'snerbly', + 'snicker', + 'snifflebee', + 'sniffleberry', + 'sniffleblabber', + 'snifflebocker', + 'sniffleboing', + 'sniffleboom', + 'snifflebounce', + 'snifflebouncer', + 'snifflebrains', + 'snifflebubble', + 'snifflebumble', + 'snifflebump', + 'snifflebumper', + 'sniffleburger', + 'snifflechomp', + 'snifflecorn', + 'snifflecrash', + 'snifflecrumbs', + 'snifflecrump', + 'snifflecrunch', + 'sniffledoodle', + 'sniffledorf', + 'sniffleface', + 'snifflefidget', + 'snifflefink', + 'snifflefish', + 'sniffleflap', + 'sniffleflapper', + 'sniffleflinger', + 'sniffleflip', + 'sniffleflipper', + 'snifflefoot', + 'snifflefuddy', + 'snifflefussen', + 'snifflegadget', + 'snifflegargle', + 'snifflegloop', + 'sniffleglop', + 'snifflegoober', + 'snifflegoose', + 'snifflegrooven', + 'snifflehoffer', + 'snifflehopper', + 'snifflejinks', + 'sniffleklunk', + 'sniffleknees', + 'snifflemarble', + 'snifflemash', + 'snifflemonkey', + 'snifflemooch', + 'snifflemouth', + 'snifflemuddle', + 'snifflemuffin', + 'snifflemush', + 'snifflenerd', + 'snifflenoodle', + 'snifflenose', + 'snifflenugget', + 'snifflephew', + 'snifflephooey', + 'snifflepocket', + 'snifflepoof', + 'snifflepop', + 'snifflepounce', + 'snifflepow', + 'snifflepretzel', + 'snifflequack', + 'sniffleroni', + 'snifflescooter', + 'snifflescreech', + 'snifflesmirk', + 'snifflesnooker', + 'snifflesnoop', + 'snifflesnout', + 'snifflesocks', + 'snifflespeed', + 'snifflespinner', + 'snifflesplat', + 'snifflesprinkles', + 'snifflesticks', + 'snifflestink', + 'sniffleswirl', + 'sniffleteeth', + 'snifflethud', + 'sniffletoes', + 'sniffleton', + 'sniffletoon', + 'sniffletooth', + 'sniffletwist', + 'snifflewhatsit', + 'snifflewhip', + 'snifflewig', + 'snifflewoof', + 'snifflezaner', + 'snifflezap', + 'snifflezapper', + 'snifflezilla', + 'snifflezoom', + 'snippy', + 'snobby', + 'snobs', + 'snoop', + 'snooty', + 'snooze', + "snoozin'", + 'snoozing', + 'snore', + 'snorer', + 'snores', + 'snorkel', + 'snorkelbee', + 'snorkelberry', + 'snorkelblabber', + 'snorkelbocker', + 'snorkelboing', + 'snorkelboom', + 'snorkelbounce', + 'snorkelbouncer', + 'snorkelbrains', + 'snorkelbubble', + 'snorkelbumble', + 'snorkelbump', + 'snorkelbumper', + 'snorkelburger', + 'snorkelchomp', + 'snorkelcorn', + 'snorkelcrash', + 'snorkelcrumbs', + 'snorkelcrump', + 'snorkelcrunch', + 'snorkeldoodle', + 'snorkeldorf', + 'snorkeler', + "snorkeler's", + 'snorkelface', + 'snorkelfidget', + 'snorkelfink', + 'snorkelfish', + 'snorkelflap', + 'snorkelflapper', + 'snorkelflinger', + 'snorkelflip', + 'snorkelflipper', + 'snorkelfoot', + 'snorkelfuddy', + 'snorkelfussen', + 'snorkelgadget', + 'snorkelgargle', + 'snorkelgloop', + 'snorkelglop', + 'snorkelgoober', + 'snorkelgoose', + 'snorkelgrooven', + 'snorkelhoffer', + 'snorkelhopper', + 'snorkeljinks', + 'snorkelklunk', + 'snorkelknees', + 'snorkelmarble', + 'snorkelmash', + 'snorkelmonkey', + 'snorkelmooch', + 'snorkelmouth', + 'snorkelmuddle', + 'snorkelmuffin', + 'snorkelmush', + 'snorkelnerd', + 'snorkelnoodle', + 'snorkelnose', + 'snorkelnugget', + 'snorkelphew', + 'snorkelphooey', + 'snorkelpocket', + 'snorkelpoof', + 'snorkelpop', + 'snorkelpounce', + 'snorkelpow', + 'snorkelpretzel', + 'snorkelquack', + 'snorkelroni', + 'snorkelscooter', + 'snorkelscreech', + 'snorkelsmirk', + 'snorkelsnooker', + 'snorkelsnoop', + 'snorkelsnout', + 'snorkelsocks', + 'snorkelspeed', + 'snorkelspinner', + 'snorkelsplat', + 'snorkelsprinkles', + 'snorkelsticks', + 'snorkelstink', + 'snorkelswirl', + 'snorkelteeth', + 'snorkelthud', + 'snorkeltoes', + 'snorkelton', + 'snorkeltoon', + 'snorkeltooth', + 'snorkeltwist', + 'snorkelwhatsit', + 'snorkelwhip', + 'snorkelwig', + 'snorkelwoof', + 'snorkelzaner', + 'snorkelzap', + 'snorkelzapper', + 'snorkelzilla', + 'snorkelzoom', + 'snow', + 'snowball', + 'snowballs', + 'snowboard', + 'snowboarder', + 'snowboarders', + 'snowboarding', + 'snowboards', + 'snowdoodle', + 'snowdragon', + 'snowdrift', + 'snowed', + 'snowflake', + 'snowflakes', + 'snowing', + 'snowman', + "snowman's", + 'snowmen', + 'snowplace', + 'snowplows', + 'snows', + 'snowshoes', + 'snowy', + 'snuffy', + 'snug', + 'snuggle', + 'snuggles', + 'snuze', + 'so', + 'soaker', + 'soapstone', + 'soar', + "soarin'", + 'soaring', + 'soccer', + 'social', + 'socialize', + 'socially', + 'socials', + 'society', + 'soda', + 'sodality', + 'sodas', + 'sodie', + 'sofa', + 'sofas', + 'sofia', + 'sofie', + 'soft', + 'softball', + 'soften', + 'softens', + 'softer', + 'softest', + 'softly', + 'software', + "software's", + 'soil', + 'soiled', + 'soiling', + 'soils', + 'solar', + 'sold', + 'solder', + 'solders', + 'soldier', + 'soldiers', + 'sole', + 'soled', + 'soles', + 'solicitor', + 'solid', + 'solo', + 'soloed', + 'solomon', + 'solos', + 'soluble', + 'solute', + 'solutes', + 'solution', + "solution's", + 'solutions', + 'solve', + 'solved', + 'solvent', + 'solvents', + 'solves', + 'solving', + 'sombrero', + 'sombreros', + 'some', + 'somebody', + "somebody's", + 'someday', + 'somehow', + 'someone', + "someone's", + 'somers', + 'something', + "something's", + 'sometime', + 'sometimes', + 'somewhat', + 'somewhere', + 'somewheres', + 'son', + 'sonata', + 'sonatas', + 'song', + "song's", + 'songbird', + 'songs', + 'sonic', + 'sons', + 'sony', + 'soon', + 'soon-to-be', + 'sooner', + 'soonest', + 'soooo', + 'soop', + 'soothing', + 'sopespian', + 'sophie', + "sophie's", + 'sophisticated', + 'sora', + 'sorcerer', + "sorcerer's", + 'sorcerers', + 'sord', + 'sororal', + 'sorority', + 'sorrier', + 'sorriest', + 'sorrow', + 'sorrows', + 'sorry', + 'sort', + "sort's", + 'sorta', + 'sorted', + 'sorter', + 'sorters', + 'sortie', + 'sorting', + 'sorts', + 'sos', + 'souffle', + 'sought', + 'soul', + 'soulflay', + 'souls', + 'sound', + 'sounded', + 'sounder', + 'soundest', + 'sounding', + 'soundings', + 'soundless', + 'soundly', + 'sounds', + 'soundtrack', + 'soup', + 'soups', + 'sour', + 'sour-plum', + 'sourbee', + 'sourberry', + 'sourblabber', + 'sourbocker', + 'sourboing', + 'sourboom', + 'sourbounce', + 'sourbouncer', + 'sourbrains', + 'sourbubble', + 'sourbumble', + 'sourbump', + 'sourbumper', + 'sourburger', + 'source', + 'sourchomp', + 'sourcorn', + 'sourcrash', + 'sourcrumbs', + 'sourcrump', + 'sourcrunch', + 'sourdoodle', + 'sourdorf', + 'sourface', + 'sourfidget', + 'sourfink', + 'sourfish', + 'sourflap', + 'sourflapper', + 'sourflinger', + 'sourflip', + 'sourflipper', + 'sourfoot', + 'sourfuddy', + 'sourfussen', + 'sourgadget', + 'sourgargle', + 'sourgloop', + 'sourglop', + 'sourgoober', + 'sourgoose', + 'sourgrooven', + 'sourhoffer', + 'sourhopper', + 'sourjinks', + 'sourklunk', + 'sourknees', + 'sourmarble', + 'sourmash', + 'sourmonkey', + 'sourmooch', + 'sourmouth', + 'sourmuddle', + 'sourmuffin', + 'sourmush', + 'sournerd', + 'sournoodle', + 'sournose', + 'sournugget', + 'sourphew', + 'sourphooey', + 'sourpocket', + 'sourpoof', + 'sourpop', + 'sourpounce', + 'sourpow', + 'sourpretzel', + 'sourquack', + 'sourroni', + 'sourscooter', + 'sourscreech', + 'soursmirk', + 'soursnooker', + 'soursnoop', + 'soursnout', + 'soursocks', + 'sourspeed', + 'sourspinner', + 'soursplat', + 'soursprinkles', + 'soursticks', + 'sourstink', + 'sourswirl', + 'sourteeth', + 'sourthud', + 'sourtoes', + 'sourton', + 'sourtoon', + 'sourtooth', + 'sourtwist', + 'sourwhatsit', + 'sourwhip', + 'sourwig', + 'sourwoof', + 'sourzaner', + 'sourzap', + 'sourzapper', + 'sourzilla', + 'sourzoom', + 'south', + 'south-eastern', + 'souther', + 'southern', + 'southerner', + 'southerners', + 'southernly', + 'southing', + 'southsea', + 'southside', + 'souvenir', + 'souvenirs', + 'sovereign', + 'sovreigns', + 'spaaarrow', + 'space', + "space's", + 'space-age', + 'spaced', + 'spacer', + 'spacers', + 'spaces', + 'spaceship', + "spaceship's", + 'spaceships', + 'spacing', + 'spacings', + 'spacklebee', + 'spackleberry', + 'spackleblabber', + 'spacklebocker', + 'spackleboing', + 'spackleboom', + 'spacklebounce', + 'spacklebouncer', + 'spacklebrains', + 'spacklebubble', + 'spacklebumble', + 'spacklebump', + 'spacklebumper', + 'spackleburger', + 'spacklechomp', + 'spacklecorn', + 'spacklecrash', + 'spacklecrumbs', + 'spacklecrump', + 'spacklecrunch', + 'spackledoodle', + 'spackledorf', + 'spackleface', + 'spacklefidget', + 'spacklefink', + 'spacklefish', + 'spackleflap', + 'spackleflapper', + 'spackleflinger', + 'spackleflip', + 'spackleflipper', + 'spacklefoot', + 'spacklefuddy', + 'spacklefussen', + 'spacklegadget', + 'spacklegargle', + 'spacklegloop', + 'spackleglop', + 'spacklegoober', + 'spacklegoose', + 'spacklegrooven', + 'spacklehoffer', + 'spacklehopper', + 'spacklejinks', + 'spackleklunk', + 'spackleknees', + 'spacklemarble', + 'spacklemash', + 'spacklemonkey', + 'spacklemooch', + 'spacklemouth', + 'spacklemuddle', + 'spacklemuffin', + 'spacklemush', + 'spacklenerd', + 'spacklenoodle', + 'spacklenose', + 'spacklenugget', + 'spacklephew', + 'spacklephooey', + 'spacklepocket', + 'spacklepoof', + 'spacklepop', + 'spacklepounce', + 'spacklepow', + 'spacklepretzel', + 'spacklequack', + 'spackleroni', + 'spacklescooter', + 'spacklescreech', + 'spacklesmirk', + 'spacklesnooker', + 'spacklesnoop', + 'spacklesnout', + 'spacklesocks', + 'spacklespeed', + 'spacklespinner', + 'spacklesplat', + 'spacklesprinkles', + 'spacklesticks', + 'spacklestink', + 'spackleswirl', + 'spackleteeth', + 'spacklethud', + 'spackletoes', + 'spackleton', + 'spackletoon', + 'spackletooth', + 'spackletwist', + 'spacklewhatsit', + 'spacklewhip', + 'spacklewig', + 'spacklewoof', + 'spacklezaner', + 'spacklezap', + 'spacklezapper', + 'spacklezilla', + 'spacklezoom', + 'spade', + 'spades', + 'spaghetti', + 'spain', + 'spam', + 'spamonia', + 'spanish', + 'spare', + 'spared', + 'sparely', + 'sparer', + 'spares', + 'sparest', + 'sparing', + 'spark', + 'sparkies', + 'sparkle', + 'sparklebee', + 'sparkleberry', + 'sparkleblabber', + 'sparklebocker', + 'sparkleboing', + 'sparkleboom', + 'sparklebounce', + 'sparklebouncer', + 'sparklebrains', + 'sparklebubble', + 'sparklebumble', + 'sparklebump', + 'sparklebumper', + 'sparkleburger', + 'sparklechomp', + 'sparklecorn', + 'sparklecrash', + 'sparklecrumbs', + 'sparklecrump', + 'sparklecrunch', + 'sparkledoodle', + 'sparkledorf', + 'sparkleface', + 'sparklefidget', + 'sparklefink', + 'sparklefish', + 'sparkleflap', + 'sparkleflapper', + 'sparkleflinger', + 'sparkleflip', + 'sparkleflipper', + 'sparklefoot', + 'sparklefuddy', + 'sparklefussen', + 'sparklegadget', + 'sparklegargle', + 'sparklegloop', + 'sparkleglop', + 'sparklegoober', + 'sparklegoose', + 'sparklegrooven', + 'sparklehoffer', + 'sparklehopper', + 'sparklejinks', + 'sparkleklunk', + 'sparkleknees', + 'sparklemarble', + 'sparklemash', + 'sparklemonkey', + 'sparklemooch', + 'sparklemouth', + 'sparklemuddle', + 'sparklemuffin', + 'sparklemush', + 'sparklenerd', + 'sparklenoodle', + 'sparklenose', + 'sparklenugget', + 'sparklephew', + 'sparklephooey', + 'sparklepocket', + 'sparklepoof', + 'sparklepop', + 'sparklepounce', + 'sparklepow', + 'sparklepretzel', + 'sparklequack', + 'sparkler', + 'sparkleroni', + 'sparklers', + 'sparkles', + 'sparklescooter', + 'sparklescreech', + 'sparklesmirk', + 'sparklesnooker', + 'sparklesnoop', + 'sparklesnout', + 'sparklesocks', + 'sparklespeed', + 'sparklespinner', + 'sparklesplat', + 'sparklesprinkles', + 'sparklesticks', + 'sparklestink', + 'sparkleswirl', + 'sparkleteeth', + 'sparklethud', + 'sparkletoes', + 'sparkleton', + 'sparkletoon', + 'sparkletooth', + 'sparkletwist', + 'sparklewhatsit', + 'sparklewhip', + 'sparklewig', + 'sparklewoof', + 'sparklezaner', + 'sparklezap', + 'sparklezapper', + 'sparklezilla', + 'sparklezoom', + 'sparkling', + 'sparkly', + 'sparks', + 'sparky', + "sparky's", + 'sparrow', + "sparrow's", + 'sparrow-man', + 'sparrows', + 'sparrowsfiight', + 'spartans', + 'spation', + 'spatula', + 'speak', + 'speaker', + "speaker's", + 'speakers', + 'speaking', + 'speaks', + 'special', + 'specially', + 'specials', + 'species', + 'specific', + 'specifically', + 'specifics', + 'specified', + 'specify', + 'specifying', + 'speck', + 'speckled', + 'spectacular', + 'specter', + 'specters', + 'spectral', + 'spectrobe', + 'spectrobes', + 'speech', + "speech's", + 'speeches', + 'speed', + 'speedchat', + 'speedchat+', + 'speeded', + 'speeder', + 'speeders', + 'speediest', + 'speeding', + 'speedmaster', + 'speeds', + 'speedway', + 'speedwell', + 'speedy', + 'spell', + 'spelled', + 'speller', + 'spellers', + 'spelling', + 'spellings', + 'spells', + 'spend', + 'spender', + 'spenders', + 'spending', + 'spends', + 'spent', + 'spice', + 'spices', + 'spicey', + 'spicy', + 'spider', + "spider's", + 'spider-silk', + 'spider-toon', + 'spiderman', + 'spiders', + 'spidertoon', + 'spidertoons', + 'spiderwebs', + 'spiel', + 'spiffy', + 'spikan', + 'spikanor', + 'spike', + 'spikes', + 'spiko', + 'spill', + 'spilled', + 'spilling', + 'spills', + 'spin', + 'spin-out', + 'spin-to-win', + 'spinach', + 'spines', + 'spinner', + 'spinning', + 'spins', + 'spiral', + 'spirit', + 'spirited', + 'spiriting', + 'spirits', + 'spit', + 'spiteful', + 'spits', + 'spittake', + 'splash', + 'splashed', + 'splasher', + 'splashers', + 'splashes', + 'splashing', + 'splashy', + 'splat', + 'splatter', + 'splatters', + 'splendid', + 'splendiferous', + 'splice', + 'spliced', + 'splices', + 'splicing', + 'splinter', + 'splinters', + 'splish', + 'splish-splash', + 'split', + 'splitting', + 'splurge', + 'spoiled', + 'spoiler', + 'spoke', + 'spoken', + 'spondee', + 'sponge', + 'spongy', + 'sponsor', + 'sponsored', + 'sponsoring', + 'sponsors', + 'spook', + 'spooks', + 'spooky', + 'spoon', + 'spoons', + 'sport', + 'sported', + 'sporting', + 'sportive', + 'sports', + 'spot', + "spot's", + 'spotcheek', + 'spotify', + 'spotless', + 'spotlight', + 'spots', + 'spotted', + 'spotting', + 'spotz', + 'spout', + 'spouts', + 'spray', + 'sprays', + 'spree', + 'sprightly', + 'spring', + 'springer', + 'springers', + 'springing', + 'springs', + 'springtime', + 'springy', + 'sprinkle', + 'sprinkled', + 'sprinkler', + 'sprinkles', + 'sprinkling', + 'sprint', + 'sprinting', + 'sprite', + 'sprites', + 'sprocket', + 'sprockets', + 'sprouse', + 'sprout', + 'sprouter', + 'spruce', + 'spud', + 'spuds', + 'spunkiness', + 'spunky', + 'spy', + 'spyp.o.d.', + 'spypod', + 'spyro', + 'sqad364', + 'squad', + 'squall', + "squall's", + 'squalls', + 'square', + 'squared', + 'squarely', + 'squares', + 'squaring', + 'squash', + 'squashed', + 'squashing', + 'squawk', + 'squawks', + 'squeak', + 'squeakity', + 'squeaky', + 'squeal', + 'squeeks', + 'squeeze', + 'squeezebox', + 'squeezed', + 'squeezing', + 'squid', + "squid's", + 'squids', + 'squidzoid', + 'squiggle', + 'squigglebee', + 'squiggleberry', + 'squiggleblabber', + 'squigglebocker', + 'squiggleboing', + 'squiggleboom', + 'squigglebounce', + 'squigglebouncer', + 'squigglebrains', + 'squigglebubble', + 'squigglebumble', + 'squigglebump', + 'squigglebumper', + 'squiggleburger', + 'squigglechomp', + 'squigglecorn', + 'squigglecrash', + 'squigglecrumbs', + 'squigglecrump', + 'squigglecrunch', + 'squiggledoodle', + 'squiggledorf', + 'squiggleface', + 'squigglefidget', + 'squigglefink', + 'squigglefish', + 'squiggleflap', + 'squiggleflapper', + 'squiggleflinger', + 'squiggleflip', + 'squiggleflipper', + 'squigglefoot', + 'squigglefuddy', + 'squigglefussen', + 'squigglegadget', + 'squigglegargle', + 'squigglegloop', + 'squiggleglop', + 'squigglegoober', + 'squigglegoose', + 'squigglegrooven', + 'squigglehoffer', + 'squigglehopper', + 'squigglejinks', + 'squiggleklunk', + 'squiggleknees', + 'squigglemarble', + 'squigglemash', + 'squigglemonkey', + 'squigglemooch', + 'squigglemouth', + 'squigglemuddle', + 'squigglemuffin', + 'squigglemush', + 'squigglenerd', + 'squigglenoodle', + 'squigglenose', + 'squigglenugget', + 'squigglephew', + 'squigglephooey', + 'squigglepocket', + 'squigglepoof', + 'squigglepop', + 'squigglepounce', + 'squigglepow', + 'squigglepretzel', + 'squigglequack', + 'squiggleroni', + 'squigglescooter', + 'squigglescreech', + 'squigglesmirk', + 'squigglesnooker', + 'squigglesnoop', + 'squigglesnout', + 'squigglesocks', + 'squigglespeed', + 'squigglespinner', + 'squigglesplat', + 'squigglesprinkles', + 'squigglesticks', + 'squigglestink', + 'squiggleswirl', + 'squiggleteeth', + 'squigglethud', + 'squiggletoes', + 'squiggleton', + 'squiggletoon', + 'squiggletooth', + 'squiggletwist', + 'squigglewhatsit', + 'squigglewhip', + 'squigglewig', + 'squigglewoof', + 'squigglezaner', + 'squigglezap', + 'squigglezapper', + 'squigglezilla', + 'squigglezoom', + 'squiggly', + 'squillace', + 'squirmy', + 'squirrel', + 'squirrelfish', + 'squirrels', + 'squirt', + 'squirting', + 'squirtless', + 'squishy', + 'srawhats', + 'sri', + 'srry', + 'sry', + 'ssw', + 'st', + 'st.', + 'stabber', + 'stack', + 'stackable', + 'stacker', + 'stacking', + 'stacks', + 'stadium', + 'stadiums', + 'staff', + "staff's", + 'staffed', + 'staffer', + 'staffers', + 'staffing', + 'staffs', + 'stage', + 'staged', + 'stager', + 'stagers', + 'stages', + 'staging', + 'stain', + 'stained-glass', + 'stainless', + 'stains', + 'stair', + "stair's", + 'stairs', + 'stake', + 'stalkers', + 'stall', + 'stallion', + 'stamp', + 'stamped', + 'stamper', + 'stampers', + 'stamping', + 'stamps', + 'stan', + 'stanchion', + 'stanchions', + 'stand', + 'stand-up', + 'stand-up-and-cheer', + 'standard', + 'standardly', + 'standards', + 'stander', + 'standing', + 'standings', + 'stands', + 'stanley', + "stanley's", + 'star', + "star's", + 'star-chaser', + 'star-shaped', + 'starboard', + 'starbr', + 'starcatchers', + 'starch', + 'stardom', + 'stareaston', + 'stared', + 'starer', + 'starfire', + 'starfish', + 'stargate', + 'stargazer', + 'staring', + 'starlight', + 'starring', + 'starry', + 'stars', + 'starscream', + 'start', + 'started', + 'starter', + 'starters', + 'starting', + 'starting-line', + 'starts', + 'stas', + 'stash', + 'stat', + 'statement', + "statement's", + 'statements', + 'states', + 'station', + "station's", + 'stationed', + 'stationer', + 'stationery', + 'stationing', + 'stations', + 'statistic', + 'statler', + 'stats', + 'statuary', + 'statue', + 'statues', + 'statuesque', + 'status', + 'statuses', + 'stay', + 'stayed', + 'staying', + 'stayne', + 'stays', + 'steadfast', + 'steadman', + 'steady', + 'steak', + 'steakhouse', + 'steakhouses', + 'steal', + 'stealer', + 'stealing', + 'steals', + 'stealth', + 'steam', + 'steamboat', + 'steaming', + 'steel', + 'steelhawk', + 'steeple', + 'steer', + 'steered', + 'steerer', + 'steering', + 'steers', + 'steffi', + 'stella', + 'stem', + 'stench', + 'stenches', + 'stenchy', + 'step', + "step's", + 'stepanek', + 'steph', + 'stephante', + 'stepped', + 'stepping', + 'steps', + 'stern', + 'stetson', + 'steve', + 'steven', + 'stew', + 'stewart', + 'stflush', + 'stick', + 'sticker', + 'stickerbook', + 'stickers', + 'sticking', + 'sticks', + 'sticky', + "sticky's", + 'stickyfeet', + 'still', + 'stilled', + 'stiller', + 'stillest', + 'stilling', + 'stillness', + 'stills', + 'stillwater', + 'sting', + 'stinger', + 'stingers', + 'stings', + 'stink', + 'stinkbucket', + 'stinkbugs', + 'stinker', + 'stinking', + 'stinks', + 'stinky', + "stinky's", + 'stir', + 'stitch', + "stitch's", + 'stitched', + 'stitcher', + 'stitches', + 'stitching', + 'stock', + 'stocked', + 'stockers', + 'stockier', + 'stocking', + 'stockings', + 'stockpile', + 'stocks', + 'stoke', + 'stoked', + 'stole', + 'stolen', + 'stomp', + 'stone', + 'stones', + 'stood', + 'stool', + 'stools', + 'stop', + "stop's", + 'stoped', + 'stoppable', + 'stopped', + 'stopper', + 'stopping', + 'stops', + 'storage', + 'store', + 'stored', + 'stores', + 'storied', + 'stories', + 'storing', + 'storm', + "storm's", + 'storm-sail', + 'stormbringers', + 'stormed', + 'stormers', + 'stormfire', + 'stormhaw', + 'stormhold', + 'storming', + 'stormlords', + 'stormrider', + 'storms', + 'stormy', + 'story', + "story's", + 'storybook', + 'storybookland', + 'storybooks', + 'storying', + 'storylines', + 'storytelling', + 'stow', + 'stowaway', + 'str', + 'straggler', + 'stragglers', + 'straight', + 'strait', + 'strand', + 'strands', + 'strange', + 'strangely', + 'stranger', + 'strangers', + 'strangest', + 'strategies', + 'strategy', + "strategy's", + 'straw', + 'strawberrie', + 'strawberries', + 'strawberry', + 'strawhats', + 'strays', + 'stream', + 'streamer', + 'streamers', + 'streaming', + 'streams', + 'street', + 'streeters', + 'streetlight', + 'streetlights', + 'streets', + 'streetwise', + 'strength', + 'strengthen', + 'strengthens', + 'stress', + 'stressed', + 'stresses', + 'stressing', + 'stretch', + 'stretched', + 'stretcher', + 'stretchers', + 'stretches', + 'stretching', + 'strict', + 'strictly', + 'striders', + 'strike', + 'striker', + 'strikers', + 'strikes', + 'striking', + 'string', + 'stringbean', + 'stringing', + 'strings', + 'stringy', + 'stripe', + 'strive', + 'stroll', + 'strolling', + 'strom', + 'strong', + 'strong-minded', + 'strongbox', + 'stronger', + 'strongest', + 'strongly', + 'structure', + 'struggle', + 'struggled', + 'struggling', + 'strung', + 'strut', + 'stu', + 'stubborn', + 'stubby', + 'stuck', + 'stud', + 'studded', + 'studied', + 'studier', + 'studies', + 'studio', + "studio's", + 'studios', + 'study', + 'studying', + 'stuff', + 'stuffed', + 'stuffer', + 'stuffing', + 'stuffings', + 'stuffs', + 'stuffy', + 'stumble', + 'stump', + 'stumps', + 'stumpy', + 'stun', + 'stunned', + 'stunners', + 'stunning', + 'stuns', + 'stunts', + 'stupendous', + 'sturdy', + 'stut', + 'stutter', + 'stutters', + 'style', + 'style-talent', + 'styled', + 'styler', + 'stylers', + 'styles', + "stylin'", + 'styling', + 'stylish', + 'sub', + 'subaru', + 'subject', + "subject's", + 'subjected', + 'subjecting', + 'subjective', + 'subjects', + 'sublocation', + 'submarine', + 'submarines', + 'submit', + 'submits', + 'submitted', + 'submitting', + 'subscribe', + 'subscribed', + 'subscribers', + 'subscribing', + 'subscription', + 'subscriptions', + 'substitute', + 'subtalent', + 'subtalents', + 'subtitle', + 'subtle', + 'subzero', + 'succeed', + 'succeeded', + 'succeeder', + 'succeeding', + 'succeeds', + 'success', + 'successes', + 'successful', + 'successfully', + 'successive', + 'succinct', + 'succinctly', + 'such', + 'sucha', + 'suckerpunch', + 'suction', + 'sudan', + 'sudden', + 'suddenly', + 'sudoku', + 'sudoron', + 'sue', + 'suffice', + 'suffix', + 'suffixes', + 'sufrigate', + 'sugar', + 'sugarplum', + 'sugary', + 'suggest', + 'suggested', + 'suggester', + 'suggesting', + 'suggestion', + "suggestion's", + 'suggestions', + 'suggestive', + 'suggests', + 'suit', + "suit's", + 'suitcase', + 'suitcases', + 'suite', + 'suited', + 'suiters', + 'suiting', + 'suits', + 'sulfur', + 'sulley', + 'sully', + 'sultan', + 'sum', + "sum's", + 'sumer', + 'sumhajee', + 'summary', + 'summer', + "summer's", + 'summered', + 'summering', + 'summerland', + 'summers', + 'summit', + 'summon', + 'summoned', + 'summoning', + 'summons', + 'sumo', + 'sums', + 'sun', + "sun's", + 'sunburst', + 'sundae', + 'sundaes', + 'sunday', + 'sundays', + 'sundown', + 'suneroo', + 'sunflower-seed', + 'sunflowers', + 'sung', + 'sunk', + 'sunken', + 'sunnies', + 'sunny', + "sunny's", + 'sunrise', + 'suns', + 'sunsational', + 'sunscreen', + 'sunset', + 'sunsets', + "sunshine's", + 'sunshines', + 'sunswept', + 'sup', + 'supa-star', + 'super', + "super's", + 'super-cool', + 'super-duper', + 'super-powerful', + 'super-talented', + 'super-thoughtful', + 'super-toon', + 'superb', + 'superbee', + 'superberry', + 'superblabber', + 'superbly', + 'superbocker', + 'superboing', + 'superboom', + 'superbounce', + 'superbouncer', + 'superbrains', + 'superbubble', + 'superbumble', + 'superbump', + 'superbumper', + 'superburger', + 'supercalifragilisticexpialidocious', + 'superchomp', + 'supercool', + 'supercorn', + 'supercrash', + 'supercrumbs', + 'supercrump', + 'supercrunch', + 'superdoodle', + 'superdorf', + 'superduper', + 'superface', + 'superficial', + 'superficially', + 'superfidget', + 'superfink', + 'superfish', + 'superflap', + 'superflapper', + 'superflinger', + 'superflip', + 'superflipper', + 'superfluous', + 'superfoot', + 'superfuddy', + 'superfussen', + 'supergadget', + 'supergargle', + 'supergloop', + 'superglop', + 'supergoober', + 'supergoose', + 'supergrooven', + 'superhero', + "superhero's", + 'superheroes', + 'superhoffer', + 'superhopper', + 'superior', + 'superjinks', + 'superklunk', + 'superknees', + 'superman', + 'supermarble', + 'supermash', + 'supermonkey', + 'supermooch', + 'supermouth', + 'supermuddle', + 'supermuffin', + 'supermush', + 'supernatural', + 'supernerd', + 'supernoodle', + 'supernose', + 'supernugget', + 'superphew', + 'superphooey', + 'superpocket', + 'superpoof', + 'superpop', + 'superpounce', + 'superpow', + 'superpretzel', + 'superquack', + 'superroni', + 'supers', + 'superscooter', + 'superscreech', + 'superserpents', + 'supersmirk', + 'supersnooker', + 'supersnoop', + 'supersnout', + 'supersocks', + 'superspeed', + 'superspinner', + 'supersplat', + 'supersprinkles', + 'superstar', + 'supersticks', + 'superstink', + 'superstition', + 'superstitions', + 'superswirl', + 'superteeth', + 'superthud', + 'supertoes', + 'superton', + 'supertoon', + 'supertoons', + 'supertooth', + 'supertwist', + 'supervise', + 'supervised', + 'supervising', + 'supervisor', + 'supervisors', + 'superwhatsit', + 'superwhip', + 'superwig', + 'superwoof', + 'superzaner', + 'superzap', + 'superzapper', + 'superzilla', + 'superzoom', + 'supplication', + 'supplied', + 'supplier', + 'suppliers', + 'supplies', + 'supply', + "supply's", + 'supplying', + 'support', + 'supported', + 'supporter', + 'supporters', + 'supporting', + 'supportive', + 'supports', + 'suppose', + 'supposed', + 'supposedly', + 'supposer', + 'supposes', + 'supposing', + 'supreme', + 'supremo', + "supremo's", + 'sure', + 'sured', + 'surely', + 'surer', + 'surest', + 'surf', + "surf's", + 'surface', + 'surfaced', + 'surfacer', + 'surfacers', + 'surfaces', + 'surfacing', + 'surfari', + 'surfboard', + 'surfer', + 'surfers', + "surfin'", + 'surfing', + 'surfs', + 'surge', + 'surgeon', + 'surgeons', + 'surges', + 'surging', + 'surlee', + 'surplus', + 'surprise', + "surprise's", + 'surprised', + 'surpriser', + 'surprises', + 'surprising', + 'surprize', + 'surrender', + 'surrendered', + 'surrendering', + 'surrenders', + 'surround', + 'surrounded', + 'surrounding', + 'surroundings', + 'surrounds', + 'surves', + 'survey', + 'surveying', + 'survival', + 'survive', + 'survived', + 'surviver', + 'survives', + 'surviving', + 'survivor', + "survivor's", + 'survivors', + 'susan', + "susan's", + 'sushi', + 'suspect', + 'suspected', + 'suspecting', + 'suspects', + 'suspended', + 'suspenders', + 'suspense', + 'suspicion', + 'suspicions', + 'suspicious', + 'suspiciously', + 'suzuki', + 'svaal', + 'svage', + 'sven', + 'svetlana', + 'swab', + 'swabbie', + "swabbin'", + 'swabby', + 'swag', + 'swagalicious', + 'swain', + 'swam', + 'swamies', + 'swamp', + 'swamps', + 'swan', + 'swanky', + 'swann', + "swann's", + 'swans', + 'swap', + 'swapped', + 'swapping', + 'swaps', + 'swarm', + 'swarthy', + 'swash', + 'swashbuckler', + 'swashbucklers', + 'swashbuckling', + 'swashbucler', + 'swashbuculer', + 'swat', + 'swats', + 'swatted', + 'swatting', + 'sweat', + 'sweater', + 'sweaters', + 'sweatheart', + 'sweatshirt', + 'sweatshirts', + 'sweaty', + 'sweden', + 'swedish', + 'sweep', + 'sweeping', + 'sweeps', + 'sweepstakes', + 'sweet', + 'sweeten', + 'sweetens', + 'sweeter', + 'sweetest', + 'sweetgum', + 'sweetie', + 'sweeting', + 'sweetly', + 'sweetness', + 'sweets', + 'sweetums', + 'sweetwrap', + 'sweety', + 'swell', + 'swelled', + 'swelling', + 'swellings', + 'swells', + 'swept', + 'swervy', + 'swift', + 'swiftness', + 'swifty', + 'swig', + 'swim', + 'swimer', + 'swimmer', + 'swimming', + 'swimmingly', + 'swims', + 'swimwear', + 'swindler', + 'swindlers', + 'swine', + 'swing', + 'swinger', + 'swingers', + 'swinging', + 'swings', + 'swipe', + 'swiped', + 'swipes', + "swipin'", + 'swirl', + 'swirled', + 'swirls', + 'swirly', + 'swiss', + 'switch', + "switch's", + 'switchbox', + 'switched', + 'switcher', + 'switcheroo', + 'switchers', + 'switches', + 'switching', + 'switchings', + 'swiveling', + 'swoop', + 'sword', + "sword's", + 'swordbreakers', + 'swords', + 'swordslashers', + 'swordsman', + 'swordsmen', + 'sycamore', + 'sydney', + 'sylvia', + 'symbol', + 'symbols', + 'symmetrical', + 'symmetry', + 'sync', + 'syncopation', + 'syndicate', + 'synergise', + 'synergised', + 'synergises', + 'synergising', + 'synergized', + 'synergizes', + 'synergizing', + 'synergy', + 'synopsis', + 'syrberus', + 'syrup', + 'syrupy', + 'system', + "system's", + 'systems', + 't-shirt', + 't-shirts', + 't-squad', + "t-squad's", + 't.b.', + 't.p.', + 'ta', + 'tab', + 'tabatha', + 'tabby', + 'tabitha', + "tabitha's", + 'table', + "table's", + 'table-setting-talent', + 'tabled', + 'tables', + 'tableset', + 'tabling', + 'tabs', + 'tabulate', + 'tack', + 'tacked', + 'tacking', + 'tackle', + 'tackled', + 'tackles', + 'tackling', + 'tacks', + 'tacky', + 'taco', + 'tact', + 'tactful', + 'tactics', + 'tad', + 'taffy', + 'tag', + 'tags', + 'tailed', + 'tailgater', + 'tailgaters', + 'tailgating', + 'tailing', + 'tailor', + 'tailored', + 'tailoring', + 'tailors', + 'tailpipe', + 'tailpipes', + 'tails', + 'tailswim', + 'tainted', + 'take', + 'taken', + 'takeover', + 'taker', + 'takers', + 'takes', + 'taketh', + "takin'", + 'taking', + 'takings', + 'takion', + 'tale', + "tale's", + 'talent', + 'talented', + 'talents', + 'tales', + 'talespin', + 'talina', + 'talk', + 'talkative', + 'talked', + 'talker', + 'talkers', + 'talkin', + 'talking', + 'talks', + 'tall', + 'tall-tale-telling-talent', + 'taller', + 'tallest', + 'tally', + 'talon', + 'talons', + 'tam', + 'tamazoa', + 'tamers', + 'tammy', + 'tampa', + 'tan', + 'tandemfrost', + 'tangaroa', + "tangaroa's", + 'tangaroa-ru', + "tangaroa-ru's", + 'tangerine', + 'tangle', + 'tango', + 'tangoed', + 'tangoing', + 'tangos', + 'tangy', + 'tanith', + 'tank', + 'tanker', + 'tankers', + 'tanking', + 'tanks', + 'tanned', + 'tanning', + 'tanny', + 'tans', + 'tansy', + 'tap', + "tap's", + 'tape', + 'taped', + 'taper', + 'tapers', + 'tapes', + 'tapestry', + 'taping', + 'tapings', + 'taps', + 'tar', + 'tara', + 'tarantula', + 'target', + 'targeted', + 'targeting', + 'targets', + 'tarlets', + 'tarnished', + 'tarp', + 'tarps', + 'tarred', + 'tarring', + 'tars', + 'tartar', + 'tarzan', + "tarzan's", + 'tarzans', + 'tasha', + 'task', + 'tasked', + 'tasking', + 'taskmaster', + 'taskmasters', + 'tasks', + 'taste', + 'tasted', + 'tasteful', + 'taster', + 'tasters', + 'tastes', + 'tastiest', + 'tasting', + 'tasty', + 'tate', + 'tater', + 'tats', + 'tattletales', + 'tattoo', + 'tattooed', + 'tattoos', + 'tatum', + 'taught', + 'taunt', + 'taunted', + 'taunting', + 'taunts', + 'tax', + 'taxes', + 'taxi', + 'taxis', + 'taylor', + 'tbh', + 'tbrrrgh', + 'tbt', + 'tchoff', + 'tchoff-tchoff-tchoffo-tchoffo-tchoff', + 'td', + 'tdl', + 'tea', + 'tea-making', + 'teacakes', + 'teach', + 'teacher', + "teacher's", + 'teachers', + 'teaches', + 'teaching', + 'teachings', + 'teacups', + 'teakettle', + 'teal', + 'team', + "team's", + 'teamed', + 'teaming', + 'teamo', + 'teams', + 'teamwork', + 'teapot', + 'tear', + "tear's", + 'teared', + 'tearer', + 'tearing', + 'tearoom', + 'tears', + 'teas', + 'tech', + 'techknows', + 'technical', + 'technically', + 'technique', + 'techniques', + 'techno', + 'technobabble', + 'technological', + 'technologically', + 'technology', + 'ted', + 'teddie', + 'teddy', + 'tee', + 'tee-hee', + 'teenager', + 'teeny', + 'teepee', + 'teepees', + 'teeth', + 'teethed', + 'teether', + 'teethes', + 'teething', + 'tegueste', + 'telemarketer', + 'telemarketers', + 'teleport', + "teleport's", + 'teleportation', + 'teleported', + 'teleporter', + 'teleporters', + 'teleporting', + 'teleportings', + 'teleports', + 'telescope', + 'television', + "television's", + 'televisions', + 'teli-caster', + 'tell', + 'teller', + 'tellers', + 'telling', + 'tellings', + 'tells', + 'telly', + 'telmar', + 'temma', + 'temper', + 'temperament', + 'temperamental', + 'temperaments', + 'temperature', + 'temperatures', + 'templars', + 'template', + 'templates', + 'temple', + "temple's", + 'templed', + 'temples', + 'templeton', + 'tempo', + "tempo's", + 'temporarily', + 'temporary', + 'ten', + 'tend', + 'tended', + 'tendency', + 'tender', + 'tenderleaf', + 'tenders', + 'tendershoot', + 'tending', + 'tends', + 'tenebrific', + 'tenkro', + 'tennis', + 'tenor', + 'tenors', + 'tens', + 'tensa', + 'tension', + 'tent', + 'tentacle', + 'tentacles', + 'tented', + 'tenter', + 'tenting', + 'tents', + 'teo', + 'terabithia', + 'terence', + 'terk', + "terk's", + 'terks', + 'term', + 'terminate', + 'terminated', + 'terminates', + 'terminating', + 'terminator', + 'termite', + 'ternary', + 'teror', + 'terra', + 'terrace', + 'terrain', + 'terrains', + 'terrance', + "terrance's", + 'terrible', + 'terrific', + 'terry', + 'tertiary', + 'tessa', + 'test', + "test's", + 'test1', + 'tested', + 'tester', + 'testers', + 'testing', + 'testings', + 'tests', + 'tetherball', + 'tewas', + 'tex', + 'text', + "text's", + 'text-message', + 'textile-talent', + 'texts', + 'texture', + 'textured', + 'textures', + 'tgf', + 'th', + "th'", + 'thailand', + 'than', + 'thang', + 'thank', + 'thanked', + 'thanker', + 'thankful', + 'thankfulness', + 'thanking', + 'thanks', + 'thanksgiving', + 'thanx', + 'thar', + 'that', + "that'd", + "that'll", + "that's", + 'thats', + 'thayer', + 'thayers', + 'the', + 'thea', + 'theater', + 'theaters', + 'theatre', + "theatre's", + 'theatres', + 'thee', + 'theifs', + 'their', + "their's", + 'theirs', + 'theives', + 'thelma', + 'thelonius', + 'them', + 'theme', + "theme's", + 'themes', + 'themselves', + 'then', + 'thenights', + 'theodore', + 'therandomdog', + 'there', + "there'll", + "there's", + 'therefore', + 'theres', + 'theresa', + 'thermos', + 'thermoses', + 'these', + 'theses', + 'theta', + 'they', + "they'll", + "they're", + "they've", + 'theyll', + 'theyre', + 'thicket', + 'thief', + "thief's", + 'thieves', + 'thimble', + 'thin', + 'thing', + 'thingamabob', + 'thingamabobs', + 'thingamajigs', + 'thingie', + 'thingies', + 'things', + 'thingy', + 'think', + 'thinker', + 'thinkers', + "thinkin'", + 'thinking', + 'thinks', + 'thinnamin', + 'third', + 'thirst', + 'thirst-quenching', + 'thirsted', + 'thirster', + 'thirstier', + 'thirsts', + 'thirsty', + 'thirty', + 'this', + 'thistle', + 'thistle-down', + 'thnx', + 'tho', + 'thomas', + 'thon', + 'thor', + 'thorhammer', + 'thorn', + 'those', + 'though', + 'thought', + "thought's", + 'thoughtful', + 'thoughts', + 'thousand', + 'thousands', + 'thrashers', + 'thread', + 'threads', + 'threats', + 'three', + 'threw', + 'thrice', + 'thrifty', + 'thrill', + 'thriller', + 'thrilling', + 'thrills', + 'thrillseeker', + 'thrives', + 'throne', + 'thrones', + 'through', + 'throughly', + 'throughout', + 'throw', + 'thrower', + 'throwing', + 'throwless', + 'thrown', + 'throws', + 'thumb', + 'thumbs', + 'thumper', + 'thunba', + 'thunder', + 'thunderbee', + 'thunderberry', + 'thunderblabber', + 'thunderbocker', + 'thunderboing', + 'thunderbolt', + 'thunderbolts', + 'thunderboom', + 'thunderbounce', + 'thunderbouncer', + 'thunderbrains', + 'thunderbubble', + 'thunderbumble', + 'thunderbump', + 'thunderbumper', + 'thunderburger', + 'thunderchomp', + 'thundercorn', + 'thundercrash', + 'thundercrumbs', + 'thundercrump', + 'thundercrunch', + 'thunderdoodle', + 'thunderdorf', + 'thunderface', + 'thunderfidget', + 'thunderfink', + 'thunderfish', + 'thunderflap', + 'thunderflapper', + 'thunderflinger', + 'thunderflip', + 'thunderflipper', + 'thunderfoot', + 'thunderfuddy', + 'thunderfussen', + 'thundergadget', + 'thundergargle', + 'thundergloop', + 'thunderglop', + 'thundergoober', + 'thundergoose', + 'thundergrooven', + 'thunderhoffer', + 'thunderhopper', + 'thundering', + 'thunderjinks', + 'thunderklunk', + 'thunderknees', + 'thundermarble', + 'thundermash', + 'thundermonkey', + 'thundermooch', + 'thundermouth', + 'thundermuddle', + 'thundermuffin', + 'thundermush', + 'thundernerd', + 'thundernoodle', + 'thundernose', + 'thundernugget', + 'thunderphew', + 'thunderphooey', + 'thunderpocket', + 'thunderpoof', + 'thunderpop', + 'thunderpounce', + 'thunderpow', + 'thunderpretzel', + 'thunderquack', + 'thunderroni', + 'thunderscooter', + 'thunderscreech', + 'thundersmirk', + 'thundersnooker', + 'thundersnoop', + 'thundersnout', + 'thundersocks', + 'thunderspeed', + 'thunderspinner', + 'thundersplat', + 'thundersprinkles', + 'thundersticks', + 'thunderstink', + 'thunderstorms', + 'thunderswirl', + 'thunderteeth', + 'thunderthud', + 'thundertoes', + 'thunderton', + 'thundertoon', + 'thundertooth', + 'thundertwist', + 'thunderwhatsit', + 'thunderwhip', + 'thunderwig', + 'thunderwoof', + 'thunderzaner', + 'thunderzap', + 'thunderzapper', + 'thunderzilla', + 'thunderzoom', + 'thundor', + 'thundora', + 'thursday', + 'thursdays', + 'thus', + 'thusly', + 'thx', + 'tia', + 'tiara', + 'tiazoa', + 'tiberius', + 'tibian', + 'tic', + 'tic-toc', + 'tick', + 'ticker', + 'ticket', + "ticket's", + 'ticketed', + 'ticketing', + 'tickets', + 'ticking', + 'tickle', + 'tickled', + 'tickles', + 'tickling', + 'ticklish', + 'ticks', + 'tidal', + 'tidbits', + 'tide', + 'tidy', + 'tie', + 'tier', + 'ties', + 'tiffany', + 'tiffens', + 'tiger', + "tiger's", + 'tigers', + 'tigger', + "tigger's", + 'tightwad', + 'tightwads', + 'tiki', + 'tikis', + 'til', + 'tile', + 'tiled', + 'tiles', + 'till', + 'tilled', + 'tiller', + 'tillers', + 'tilling', + 'tills', + 'tilly', + 'tim', + 'timberleaf', + 'timbers', + 'timberwolves', + 'timbre', + 'time', + 'timed', + 'timeless', + 'timelords', + 'timely', + 'timeout', + 'timer', + 'timers', + 'times', + 'timing', + 'timings', + 'timon', + "timon's", + 'timons', + 'timothy', + 'tin', + 'tina', + "tina's", + 'tindera', + 'tink', + "tink's", + 'tinker', + "tinker's", + 'tinkerbell', + "tinkerbell's", + 'tinkling', + 'tinny', + 'tinsel', + 'tint', + 'tinted', + 'tints', + 'tiny', + 'tip', + 'tip-top', + 'tipped', + 'tipping', + 'tips', + 'tipton', + 'tire', + 'tired', + 'tires', + 'tiresome', + 'tiring', + 'tisdale', + "tish's", + 'titan', + "titan's", + 'titanic', + 'titans', + 'title', + 'titles', + 'tkp', + 'tl;dr', + 'tlc', + 'tm', + 'tnt', + 'tnts', + 'to', + 'toad', + 'toads', + 'toadstool', + 'toadstool-drying', + 'toadstools', + 'toast', + 'toasted', + 'toaster', + 'toasting', + 'toasty', + 'tobasu', + 'tobias', + 'toboggan', + 'tobogganing', + 'toboggans', + 'toby', + 'todas', + 'today', + "today's", + 'todd', + "todd's", + 'toddler', + 'toe', + "toe's", + 'toed', + 'toehooks', + 'toes', + 'tofu', + 'together', + 'toggle', + 'toggler', + 'token', + 'tokens', + 'told', + 'tolerable', + 'toll', + 'tom', + "tom's", + "tom-tom's", + 'tomas', + 'tomato', + 'tomboy', + 'tomorrow', + "tomorrow's", + 'tomorrowland', + "tomorrowland's", + 'tomorrows', + 'tomswordfish', + 'ton', + 'tone', + 'toner', + 'tones', + 'tong', + 'tongs', + 'tonic', + 'tonics', + 'tonight', + 'toning', + 'tonite', + 'tons', + 'tony', + 'too', + 'toodles', + 'took', + 'tool', + 'toolbar', + 'toolbars', + 'toolbelt', + 'tooled', + 'tooler', + 'toolers', + 'tooling', + 'tools', + 'toolshed', + 'toon', + "toon's", + 'toon-tastic', + 'toon-torial', + 'toon-up', + 'toon-upless', + 'toon-ups', + 'toonacious', + 'toonblr', + 'tooned', + 'toonerific', + 'toonfinite', + 'toonhq.org', + 'toonification', + 'tooninator', + 'tooning', + 'toonity', + 'toonosaur', + 'toons', + 'toonscape', + 'toontanic', + 'toontask', + 'toontasks', + 'toontastic', + 'toonter', + 'toontorial', + 'toontown', + 'toontownstride', + 'toontownstride.com', + 'toontrooper', + 'toontroopers', + 'toonup', + 'toonupless', + 'toonups', + 'toony', + 'tooth', + 'toothless', + 'toothpaste', + 'toothpick', + 'tootie', + 'tootles', + 'top', + 'top-ranking', + 'topaz', + 'tophat', + 'tophats', + 'topiary', + 'topic', + 'topics', + 'toppenbee', + 'toppenberry', + 'toppenblabber', + 'toppenbocker', + 'toppenboing', + 'toppenboom', + 'toppenbounce', + 'toppenbouncer', + 'toppenbrains', + 'toppenbubble', + 'toppenbumble', + 'toppenbump', + 'toppenbumper', + 'toppenburger', + 'toppenchomp', + 'toppencorn', + 'toppencrash', + 'toppencrumbs', + 'toppencrump', + 'toppencrunch', + 'toppendoodle', + 'toppendorf', + 'toppenface', + 'toppenfidget', + 'toppenfink', + 'toppenfish', + 'toppenflap', + 'toppenflapper', + 'toppenflinger', + 'toppenflip', + 'toppenflipper', + 'toppenfoot', + 'toppenfuddy', + 'toppenfussen', + 'toppengadget', + 'toppengargle', + 'toppengloop', + 'toppenglop', + 'toppengoober', + 'toppengoose', + 'toppengrooven', + 'toppenhoffer', + 'toppenhopper', + 'toppenjinks', + 'toppenklunk', + 'toppenknees', + 'toppenmarble', + 'toppenmash', + 'toppenmonkey', + 'toppenmooch', + 'toppenmouth', + 'toppenmuddle', + 'toppenmuffin', + 'toppenmush', + 'toppennerd', + 'toppennoodle', + 'toppennose', + 'toppennugget', + 'toppenphew', + 'toppenphooey', + 'toppenpocket', + 'toppenpoof', + 'toppenpop', + 'toppenpounce', + 'toppenpow', + 'toppenpretzel', + 'toppenquack', + 'toppenroni', + 'toppenscooter', + 'toppenscreech', + 'toppensmirk', + 'toppensnooker', + 'toppensnoop', + 'toppensnout', + 'toppensocks', + 'toppenspeed', + 'toppenspinner', + 'toppensplat', + 'toppensprinkles', + 'toppensticks', + 'toppenstink', + 'toppenswirl', + 'toppenteeth', + 'toppenthud', + 'toppentoes', + 'toppenton', + 'toppentoon', + 'toppentooth', + 'toppentwist', + 'toppenwhatsit', + 'toppenwhip', + 'toppenwig', + 'toppenwoof', + 'toppenzaner', + 'toppenzap', + 'toppenzapper', + 'toppenzilla', + 'toppenzoom', + 'tops', + 'topsy', + 'topsy-turvy', + 'toptoon', + 'toptoons', + 'tor', + 'torga', + 'torgallup', + 'torgazar', + 'tori', + 'tormenta', + 'torn', + 'tortaba', + 'tortaire', + 'tortana', + 'torth', + 'tortoises', + 'tortos', + 'tos', + 'tosis', + 'toss', + 'tossed', + 'tosses', + 'tossing', + 'total', + "total's", + 'totaled', + 'totaling', + 'totally', + 'totals', + 'tote', + 'totem', + 'totems', + 'tothestars', + 'tots', + 'toucan', + 'toucans', + 'touch', + 'touchdown', + 'touchdowns', + 'touge', + 'tough', + 'tough-skinned', + 'toughest', + 'toughness', + 'toupee', + 'toupees', + 'tour', + 'toured', + 'tourer', + 'tourguide', + 'touring', + 'tournament', + 'tournaments', + 'tours', + 'tow', + 'tow-mater', + 'toward', + 'towardly', + 'towards', + 'tower', + 'towered', + 'towering', + 'towers', + 'towing', + 'town', + "town's", + 'towner', + 'towns', + 'townsend', + 'townsfolk', + 'townsperson', + 'tows', + 'toy', + 'toyer', + 'toying', + 'toyota', + 'toys', + 'tp', + 'tping', + 'trace', + 'track', + "track's", + 'tracked', + 'tracker', + 'trackers', + 'tracking', + 'tracks', + 'tracy', + 'trade', + 'tradeable', + 'traded', + 'trader', + 'traders', + 'trades', + 'trading', + 'tradition', + 'traditions', + 'traffic', + "traffic's", + 'traffics', + 'tragedy', + 'trail', + 'trailed', + 'trailer', + 'trailers', + 'trailing', + 'trailings', + 'trails', + 'train', + 'trained', + 'trainer', + 'trainers', + 'training', + 'trains', + 'trampoline', + 'trampolines', + 'tranquil', + 'transfer', + "transfer's", + 'transfered', + 'transferred', + 'transferring', + 'transfers', + 'translate', + 'translated', + 'translates', + 'translating', + 'transom', + 'transparent', + 'transport', + 'transportation', + 'transported', + 'transporter', + 'transporters', + 'transporting', + 'transports', + 'trap', + "trap's", + 'trapdoor', + 'trapdoors', + 'trapeze', + 'trapless', + 'trapped', + 'traps', + 'trash', + 'trashcan', + "trashcan's", + 'trashcans', + 'travel', + 'traveled', + 'traveler', + 'traveling', + 'travelling', + 'travels', + 'travis', + 'tray', + 'treacherous', + 'treachery', + "treachery's", + 'tread', + 'treaded', + 'treading', + 'treads', + 'treasure', + 'treasurechest', + 'treasurechests', + 'treasured', + 'treasuremaps', + 'treasurer', + 'treasures', + 'treasuring', + 'treat', + 'treated', + 'treater', + 'treaters', + 'treating', + 'treatment', + 'treats', + 'treble', + 'tree', + 'tree-bark-grading', + 'tree-picking', + 'tree-picking-talent', + 'treehouse', + 'treehouses', + 'trees', + 'treetop', + 'trek', + 'trellis', + 'tremendous', + 'tremor', + 'trend', + 'trending', + 'trends', + 'trent', + "trent's", + 'trespass', + 'trespasser', + 'treyarch', + 'tri-barrel', + 'tri-lock', + 'triad', + 'trial', + "trial's", + 'trials', + 'triangle', + 'tribal', + 'tribe', + 'tribulation', + 'tribulations', + 'trick', + 'tricked', + 'tricked-out', + 'tricker', + 'tricking', + 'tricks', + 'tricky', + 'trickybee', + 'trickyberry', + 'trickyblabber', + 'trickybocker', + 'trickyboing', + 'trickyboom', + 'trickybounce', + 'trickybouncer', + 'trickybrains', + 'trickybubble', + 'trickybumble', + 'trickybump', + 'trickybumper', + 'trickyburger', + 'trickychomp', + 'trickycorn', + 'trickycrash', + 'trickycrumbs', + 'trickycrump', + 'trickycrunch', + 'trickydoodle', + 'trickydorf', + 'trickyface', + 'trickyfidget', + 'trickyfink', + 'trickyfish', + 'trickyflap', + 'trickyflapper', + 'trickyflinger', + 'trickyflip', + 'trickyflipper', + 'trickyfoot', + 'trickyfuddy', + 'trickyfussen', + 'trickygadget', + 'trickygargle', + 'trickygloop', + 'trickyglop', + 'trickygoober', + 'trickygoose', + 'trickygrooven', + 'trickyhoffer', + 'trickyhopper', + 'trickyjinks', + 'trickyklunk', + 'trickyknees', + 'trickymarble', + 'trickymash', + 'trickymonkey', + 'trickymooch', + 'trickymouth', + 'trickymuddle', + 'trickymuffin', + 'trickymush', + 'trickynerd', + 'trickynoodle', + 'trickynose', + 'trickynugget', + 'trickyphew', + 'trickyphooey', + 'trickypocket', + 'trickypoof', + 'trickypop', + 'trickypounce', + 'trickypow', + 'trickypretzel', + 'trickyquack', + 'trickyroni', + 'trickyscooter', + 'trickyscreech', + 'trickysmirk', + 'trickysnooker', + 'trickysnoop', + 'trickysnout', + 'trickysocks', + 'trickyspeed', + 'trickyspinner', + 'trickysplat', + 'trickysprinkles', + 'trickysticks', + 'trickystink', + 'trickyswirl', + 'trickyteeth', + 'trickythud', + 'trickytoes', + 'trickyton', + 'trickytoon', + 'trickytooth', + 'trickytwist', + 'trickywhatsit', + 'trickywhip', + 'trickywig', + 'trickywoof', + 'trickyzaner', + 'trickyzap', + 'trickyzapper', + 'trickyzilla', + 'trickyzoom', + 'tried', + 'trier', + 'triers', + 'tries', + 'trifle', + 'triforce', + 'triggerfish', + 'trike', + 'trillion', + 'trim', + 'trims', + 'trinity', + 'trinket', + 'trinkets', + 'trio', + 'trip', + "trip's", + 'triple', + 'triplet', + 'triplets', + 'triply', + "trippin'", + 'trippy', + 'trips', + 'triskaidekaphobia', + 'tristam', + 'tristan', + 'triton', + "triton's", + 'tritons', + 'triumph', + 'triumphant', + 'trivia', + 'trixie', + 'trliable', + 'troga', + 'trogallup', + 'trogazar', + 'trogdor', + 'troll', + 'trolley', + 'trolleys', + 'trolls', + 'trolly', + 'tron', + 'troop', + 'trooper', + 'troopers', + 'troops', + 'trophies', + 'trophy', + 'trophys', + 'tropic', + "tropic's", + 'tropical', + 'tropically', + 'tropics', + 'trot', + 'troubadours', + 'trouble', + 'troubled', + 'troublemaker', + 'troubler', + 'troubles', + 'troublesome', + 'troubling', + 'trough', + 'troughes', + 'trounce', + "trounce'em", + 'trousers', + 'trout', + 'trove', + 'trowels', + 'troy', + 'tru', + 'truchemistry', + 'truck', + 'truckloads', + 'trudy', + 'true', + 'trued', + "truehound's", + 'truer', + 'trues', + 'truest', + 'truetosilver', + 'truffle', + 'trufflehunter', + "trufflehunter's", + 'trufflehunters', + 'truffles', + 'truigos', + 'truing', + 'truly', + 'trumpet', + 'trumpets', + 'trumpkin', + 'trunk', + "trunk's", + 'trunked', + 'trunkfish', + 'trunks', + 'truscott', + 'trust', + 'trusted', + 'truster', + 'trusting', + 'trusts', + 'trustworthy', + 'trusty', + 'truth', + 'truths', + 'try', + 'tryin', + 'trying', + 'tt', + 'ttc', + 'ttcentral', + 'ttf', + 'ttfn', + 'ttfs', + 'tthe', + 'tti', + 'tto', + 'ttr', + 'tts', + 'ttstride', + 'ttyl', + 'tub', + 'tuba', + 'tubas', + 'tubby', + 'tube', + 'tuber', + 'tubes', + 'tubs', + 'tuesday', + 'tuesdays', + 'tuft', + 'tufts', + 'tug', + 'tug-o-war', + 'tug-of-war', + 'tugs', + 'tuless', + 'tulip', + 'tulips', + 'tumble', + 'tumbly', + 'tumnus', + 'tuna', + 'tunas', + 'tundra', + 'tune', + 'tune-licious', + 'tuned', + 'tuner', + 'tuners', + 'tunes', + 'tuning', + 'tunnel', + 'tunneled', + 'tunneling', + 'tunnels', + 'turbo', + 'turbojugend', + 'turbonegro', + 'turf', + 'turk', + 'turkey', + 'turkish', + 'turn', + 'turnbull', + "turnbull's", + 'turned', + 'turner', + 'turners', + 'turning', + 'turnings', + 'turnip', + 'turnover', + 'turnovers', + 'turns', + 'turnstile', + 'turnstiles', + 'turqouise', + 'turquoise', + 'turret', + 'turtle', + 'turtles', + 'turvey', + 'tusk', + 'tusked', + 'tut', + 'tutorial', + 'tutorials', + 'tutu', + 'tutupia', + 'tuxedo', + 'tuxedos', + 'tv', + "tv's", + 'tvr', + 'tvs', + 'twain', + 'tweebs', + 'tweedlebee', + 'tweedleberry', + 'tweedleblabber', + 'tweedlebocker', + 'tweedleboing', + 'tweedleboom', + 'tweedlebounce', + 'tweedlebouncer', + 'tweedlebrains', + 'tweedlebubble', + 'tweedlebumble', + 'tweedlebump', + 'tweedlebumper', + 'tweedleburger', + 'tweedlechomp', + 'tweedlecorn', + 'tweedlecrash', + 'tweedlecrumbs', + 'tweedlecrump', + 'tweedlecrunch', + 'tweedledee', + 'tweedledoodle', + 'tweedledorf', + 'tweedledum', + 'tweedleface', + 'tweedlefidget', + 'tweedlefink', + 'tweedlefish', + 'tweedleflap', + 'tweedleflapper', + 'tweedleflinger', + 'tweedleflip', + 'tweedleflipper', + 'tweedlefoot', + 'tweedlefuddy', + 'tweedlefussen', + 'tweedlegadget', + 'tweedlegargle', + 'tweedlegloop', + 'tweedleglop', + 'tweedlegoober', + 'tweedlegoose', + 'tweedlegrooven', + 'tweedlehoffer', + 'tweedlehopper', + 'tweedlejinks', + 'tweedleklunk', + 'tweedleknees', + 'tweedlemarble', + 'tweedlemash', + 'tweedlemonkey', + 'tweedlemooch', + 'tweedlemouth', + 'tweedlemuddle', + 'tweedlemuffin', + 'tweedlemush', + 'tweedlenerd', + 'tweedlenoodle', + 'tweedlenose', + 'tweedlenugget', + 'tweedlephew', + 'tweedlephooey', + 'tweedlepocket', + 'tweedlepoof', + 'tweedlepop', + 'tweedlepounce', + 'tweedlepow', + 'tweedlepretzel', + 'tweedlequack', + 'tweedleroni', + 'tweedlescooter', + 'tweedlescreech', + 'tweedlesmirk', + 'tweedlesnooker', + 'tweedlesnoop', + 'tweedlesnout', + 'tweedlesocks', + 'tweedlespeed', + 'tweedlespinner', + 'tweedlesplat', + 'tweedlesprinkles', + 'tweedlesticks', + 'tweedlestink', + 'tweedleswirl', + 'tweedleteeth', + 'tweedlethud', + 'tweedletoes', + 'tweedleton', + 'tweedletoon', + 'tweedletooth', + 'tweedletwist', + 'tweedlewhatsit', + 'tweedlewhip', + 'tweedlewig', + 'tweedlewoof', + 'tweedlezaner', + 'tweedlezap', + 'tweedlezapper', + 'tweedlezilla', + 'tweedlezoom', + 'tweet', + 'twelve', + 'twenty', + 'twice', + 'twiddlebee', + 'twiddleberry', + 'twiddleblabber', + 'twiddlebocker', + 'twiddleboing', + 'twiddleboom', + 'twiddlebounce', + 'twiddlebouncer', + 'twiddlebrains', + 'twiddlebubble', + 'twiddlebumble', + 'twiddlebump', + 'twiddlebumper', + 'twiddleburger', + 'twiddlechomp', + 'twiddlecorn', + 'twiddlecrash', + 'twiddlecrumbs', + 'twiddlecrump', + 'twiddlecrunch', + 'twiddledoodle', + 'twiddledorf', + 'twiddleface', + 'twiddlefidget', + 'twiddlefink', + 'twiddlefish', + 'twiddleflap', + 'twiddleflapper', + 'twiddleflinger', + 'twiddleflip', + 'twiddleflipper', + 'twiddlefoot', + 'twiddlefuddy', + 'twiddlefussen', + 'twiddlegadget', + 'twiddlegargle', + 'twiddlegloop', + 'twiddleglop', + 'twiddlegoober', + 'twiddlegoose', + 'twiddlegrooven', + 'twiddlehoffer', + 'twiddlehopper', + 'twiddlejinks', + 'twiddleklunk', + 'twiddleknees', + 'twiddlemarble', + 'twiddlemash', + 'twiddlemonkey', + 'twiddlemooch', + 'twiddlemouth', + 'twiddlemuddle', + 'twiddlemuffin', + 'twiddlemush', + 'twiddlenerd', + 'twiddlenoodle', + 'twiddlenose', + 'twiddlenugget', + 'twiddlephew', + 'twiddlephooey', + 'twiddlepocket', + 'twiddlepoof', + 'twiddlepop', + 'twiddlepounce', + 'twiddlepow', + 'twiddlepretzel', + 'twiddlequack', + 'twiddleroni', + 'twiddlescooter', + 'twiddlescreech', + 'twiddlesmirk', + 'twiddlesnooker', + 'twiddlesnoop', + 'twiddlesnout', + 'twiddlesocks', + 'twiddlespeed', + 'twiddlespinner', + 'twiddlesplat', + 'twiddlesprinkles', + 'twiddlesticks', + 'twiddlestink', + 'twiddleswirl', + 'twiddleteeth', + 'twiddlethud', + 'twiddletoes', + 'twiddleton', + 'twiddletoon', + 'twiddletooth', + 'twiddletwist', + 'twiddlewhatsit', + 'twiddlewhip', + 'twiddlewig', + 'twiddlewoof', + 'twiddlezaner', + 'twiddlezap', + 'twiddlezapper', + 'twiddlezilla', + 'twiddlezoom', + 'twig', + 'twiggys', + 'twigs', + 'twilight', + 'twilightclan', + 'twill', + 'twin', + "twin's", + 'twinkle', + 'twinklebee', + 'twinkleberry', + 'twinkleblabber', + 'twinklebocker', + 'twinkleboing', + 'twinkleboom', + 'twinklebounce', + 'twinklebouncer', + 'twinklebrains', + 'twinklebubble', + 'twinklebumble', + 'twinklebump', + 'twinklebumper', + 'twinkleburger', + 'twinklechomp', + 'twinklecorn', + 'twinklecrash', + 'twinklecrumbs', + 'twinklecrump', + 'twinklecrunch', + 'twinkledoodle', + 'twinkledorf', + 'twinkleface', + 'twinklefidget', + 'twinklefink', + 'twinklefish', + 'twinkleflap', + 'twinkleflapper', + 'twinkleflinger', + 'twinkleflip', + 'twinkleflipper', + 'twinklefoot', + 'twinklefuddy', + 'twinklefussen', + 'twinklegadget', + 'twinklegargle', + 'twinklegloop', + 'twinkleglop', + 'twinklegoober', + 'twinklegoose', + 'twinklegrooven', + 'twinklehoffer', + 'twinklehopper', + 'twinklejinks', + 'twinkleklunk', + 'twinkleknees', + 'twinklemarble', + 'twinklemash', + 'twinklemonkey', + 'twinklemooch', + 'twinklemouth', + 'twinklemuddle', + 'twinklemuffin', + 'twinklemush', + 'twinklenerd', + 'twinklenoodle', + 'twinklenose', + 'twinklenugget', + 'twinklephew', + 'twinklephooey', + 'twinklepocket', + 'twinklepoof', + 'twinklepop', + 'twinklepounce', + 'twinklepow', + 'twinklepretzel', + 'twinklequack', + 'twinkleroni', + 'twinklescooter', + 'twinklescreech', + 'twinklesmirk', + 'twinklesnooker', + 'twinklesnoop', + 'twinklesnout', + 'twinklesocks', + 'twinklespeed', + 'twinklespinner', + 'twinklesplat', + 'twinklesprinkles', + 'twinklesticks', + 'twinklestink', + 'twinkleswirl', + 'twinkleteeth', + 'twinklethud', + 'twinkletoes', + 'twinkleton', + 'twinkletoon', + 'twinkletooth', + 'twinkletwist', + 'twinklewhatsit', + 'twinklewhip', + 'twinklewig', + 'twinklewoof', + 'twinklezaner', + 'twinklezap', + 'twinklezapper', + 'twinklezilla', + 'twinklezoom', + 'twinkling', + 'twins', + 'twire', + "twire's", + 'twirl', + 'twirls', + 'twirly', + 'twist', + 'twistable', + 'twisted', + 'twister', + 'twisters', + 'twisting', + 'twists', + 'twisty', + 'twitch', + 'twizzler', + 'twizzlers', + 'two', + 'two-face', + 'txt', + 'ty', + 'tycho', + 'tycoon', + 'tyler', + 'tyme', + 'type', + "type's", + 'typea', + 'typed', + 'typer', + 'types', + 'typhoon', + 'typical', + 'typing', + 'typo', + 'tyra', + 'tyrannosaurus', + 'tyranny', + 'tyrant', + 'tyrants', + 'tyrone', + 'tyrus', + 'tyvm', + 'u', + 'uber', + 'uberdog', + 'uchiha', + 'ufo', + 'ugh', + 'ugly', + 'ugo', + 'ugone', + 'uh', + 'uhh', + 'uhhh', + 'uhhhh', + 'uk', + 'ukelele', + 'ukeleles', + 'ukulele', + 'ukuleles', + 'ultimate', + 'ultimately', + 'ultimatum', + 'ultimoose', + 'ultra', + 'ultra-popular', + 'ultra-smart', + 'ultracool', + 'ultralord', + 'ultramix', + 'um', + 'umbrella', + 'umbrellas', + 'un', + 'un-ignore', + 'unable', + 'unafraid', + 'unassuming', + 'unattractive', + 'unattune', + 'unattuned', + 'unavailable', + 'unaware', + 'unbearable', + 'unbeatable', + 'unbelievable', + 'unbelievably', + 'uncaught', + 'uncertain', + 'unchained', + 'uncharted', + 'uncle', + 'uncles', + 'uncomplicated', + 'unconcerned', + 'uncool', + 'uncopyrightable', + 'und', + 'undea', + 'undecided', + 'undefeated', + 'undemocratic', + 'under', + 'underdog', + 'underdogs', + 'underground', + 'underly', + 'underrated', + 'undersea', + 'understand', + 'understanding', + 'understandingly', + 'understandings', + 'understands', + 'understanza', + 'understood', + 'understudies', + 'underwater', + 'underwing', + 'undid', + 'undo', + 'undoer', + 'undoes', + 'undoing', + 'undoings', + 'undone', + 'undreamt', + 'undying', + 'uneasily', + 'unequally', + 'unexpected', + 'unexpectedly', + 'unfair', + 'unfaith', + 'unfamiliar', + 'unfit', + 'unforgettable', + 'unfortunate', + 'unfortunately', + 'unfortunates', + 'unfriend', + 'unger', + 'ungrateful', + 'unguildable', + 'unguilded', + 'unhappier', + 'unhappiest', + 'unhappily', + 'unhappiness', + 'unhappy', + 'unheard', + 'unicorn', + 'unicycle', + 'unification', + 'uniform', + 'uniforms', + 'unignore', + 'unimportance', + 'unintended', + 'unintentional', + 'unintentionally', + 'union', + 'unique', + 'uniques', + 'unit', + 'unite', + 'united', + 'unites', + 'unity', + 'universal', + 'universe', + 'unjoin', + 'unknowingly', + 'unknown', + 'unleash', + 'unless', + 'unlikely', + 'unlimited', + 'unload', + 'unlock', + 'unlocked', + 'unlocking', + 'unlocks', + 'unmeant', + 'unmet', + 'unnecessary', + 'unpaid', + 'unplayabale', + 'unprovided', + 'unreasonable', + 'unsaid', + 'unscramble', + 'unselfish', + 'unsocial', + 'unspent', + 'unsteady', + 'unstuck', + 'untamed', + 'until', + 'untold', + 'untouchable', + 'untradeable', + 'untried', + 'untrustworthy', + 'untruth', + 'unused', + 'unusual', + 'unusually', + 'unwritten', + 'up', + 'upbeat', + 'upcoming', + 'update', + 'updated', + 'updates', + 'updating', + 'upgrade', + 'upgraded', + 'upgrades', + 'uphill', + 'uplay', + 'upload', + 'uploaded', + 'uploading', + 'upon', + 'upper', + 'uppity', + 'upright', + 'uproot', + 'ups', + 'upset', + 'upsets', + 'upsetting', + 'upside-down', + 'upstairs', + 'upstream', + 'upsy', + 'uptick', + 'uptown', + 'ur', + 'urban', + 'uriah', + 'urs', + 'ursatz', + 'ursula', + "ursula's", + 'ursulas', + 'us', + 'usa', + 'usable', + 'use', + 'used', + 'useful', + 'usefully', + 'usefulness', + 'useless', + 'user', + "user's", + 'users', + 'uses', + 'usf', + 'using', + 'usual', + 'usually', + 'utilities', + 'utility', + 'utmost', + 'utopian', + 'utter', + 'uway', + 'v-8', + 'v-pos', + 'v.p.', + 'v.p.s', + 'v2', + 'vacation', + 'vacationed', + 'vacationing', + 'vacations', + 'vachago', + 'vachilles', + 'vagrant', + 'vaild', + 'vain', + 'vale', + 'valentina', + "valentine's", + 'valentoon', + "valentoon's", + 'valentoons', + 'valet', + 'valets', + 'valheru', + 'valiant', + 'valid', + 'validated', + 'vallance', + 'vallenueva', + 'valley', + 'valleys', + 'valor', + 'valorie', + 'valuable', + 'valuables', + 'value', + 'valued', + 'valuer', + 'valuers', + 'values', + 'valuing', + 'vampire', + "vampire's", + 'vampires', + 'van', + 'vane', + 'vanessa', + 'vanguard', + 'vanguards', + 'vanilla', + 'vanish', + 'vanished', + 'vanishes', + 'vanishing', + 'vans', + 'vapor', + 'vaporize', + 'variable', + "variable's", + 'variables', + 'varied', + 'varier', + 'varies', + 'varieties', + 'variety', + "variety's", + 'various', + 'variously', + 'varsity', + 'vary', + 'varying', + 'varyings', + 'vase', + 'vases', + 'vasquez', + 'vast', + 'vaster', + 'vastest', + 'vastly', + 'vastness', + 'vaughan', + 'vault', + 'vedi', + 'veer', + 'vegas', + 'vege-tables', + 'vegetable', + 'vegetables', + 'vegetarian', + 'veggie', + 'veggies', + "veggin'", + 'vehicle', + "vehicle's", + 'vehicles', + 'veil', + 'velociraptor', + 'velvet', + 'velvet-moss', + 'vengeful', + 'veni', + 'venom', + 'venomous', + 'venture', + 'ventures', + 'venue', + 'venus', + 'verb', + 'verbs', + 'verdant', + 'verify', + 'vermin', + 'vern', + 'veronica', + 'veronique', + 'versatile', + 'verse', + 'verses', + 'version', + 'versions', + 'versus', + 'vertical', + 'very', + 'vessel', + 'vessels', + 'vest', + 'vests', + 'vet', + 'veteran', + 'veterans', + 'veterinarian', + 'veto', + 'vexation', + 'via', + 'vibe', + 'vibes', + 'vibrant', + 'vici', + 'vicki', + 'victor', + 'victoria', + 'victories', + 'victorious', + 'victory', + "victory's", + 'vida', + 'vidalia', + 'video', + 'videogame', + 'videoriffic', + 'videos', + 'vidia', + "vidia's", + 'vienna', + 'vietnam', + 'view', + 'viewed', + 'viewer', + 'viewers', + 'viewing', + 'viewings', + 'views', + 'vigilant', + 'vigor', + 'viking', + 'vikings', + 'vil', + 'vilakroma', + 'vilamasta', + 'vilanox', + 'vilar', + 'vile', + 'village', + "village's", + 'villager', + 'villages', + 'villain', + 'villainous', + 'villains', + 'villany', + 'ville', + 'vine', + 'vines', + 'vintage', + 'viola', + 'violas', + 'violet', + 'violets', + 'violin', + 'violins', + 'vip', + 'virtual', + 'virtually', + 'virtue', + 'virulence', + 'viscous', + 'vision', + "vision's", + 'visionary', + 'visioned', + 'visioning', + 'visions', + 'visious', + 'visit', + 'visited', + 'visiting', + 'visitors', + 'visits', + 'vista', + 'visual', + 'visualization', + 'visualize', + 'vitae', + 'vitality', + 'vittles', + 'viva', + 'vmk', + 'vocabulary', + 'vocal', + 'vocals', + 'vocational', + 'voice', + 'voiced', + 'voicer', + 'voicers', + 'voices', + 'voicing', + 'void', + 'volatile', + 'volcanic', + 'volcano', + 'volcanoes', + 'volcanos', + 'voldemort', + 'vole', + 'volkswagen', + 'volley', + 'volleyball', + 'voltage', + 'voltorn', + 'volty', + 'volume', + "volume's", + 'volumed', + 'volumes', + 'voluming', + 'volunteer', + 'volunteered', + 'volunteering', + 'volunteers', + 'von', + 'voodoo', + 'voona', + 'vortex', + 'vote', + 'voted', + 'voter', + 'voters', + 'votes', + 'voting', + 'votive', + 'vouch', + 'vovage', + 'vowels', + 'voyage', + 'voyager', + 'voyagers', + 'voyages', + 'vp', + "vp's", + 'vping', + 'vps', + 'vr', + 'vroom', + 'vs', + 'vs.', + 'vu', + 'vulture', + 'vultures', + 'vw', + 'w00t', + 'w8', + 'wa', + 'wa-pa-pa-pa-pa-pa-pow', + 'wacky', + 'wackybee', + 'wackyberry', + 'wackyblabber', + 'wackybocker', + 'wackyboing', + 'wackyboom', + 'wackybounce', + 'wackybouncer', + 'wackybrains', + 'wackybubble', + 'wackybumble', + 'wackybump', + 'wackybumper', + 'wackyburger', + 'wackychomp', + 'wackycorn', + 'wackycrash', + 'wackycrumbs', + 'wackycrump', + 'wackycrunch', + 'wackydoodle', + 'wackydorf', + 'wackyface', + 'wackyfidget', + 'wackyfink', + 'wackyfish', + 'wackyflap', + 'wackyflapper', + 'wackyflinger', + 'wackyflip', + 'wackyflipper', + 'wackyfoot', + 'wackyfuddy', + 'wackyfussen', + 'wackygadget', + 'wackygargle', + 'wackygloop', + 'wackyglop', + 'wackygoober', + 'wackygoose', + 'wackygrooven', + 'wackyhoffer', + 'wackyhopper', + 'wackyjinks', + 'wackyklunk', + 'wackyknees', + 'wackymarble', + 'wackymash', + 'wackymonkey', + 'wackymooch', + 'wackymouth', + 'wackymuddle', + 'wackymuffin', + 'wackymush', + 'wackynerd', + 'wackyness', + 'wackynoodle', + 'wackynose', + 'wackynugget', + 'wackyphew', + 'wackyphooey', + 'wackypocket', + 'wackypoof', + 'wackypop', + 'wackypounce', + 'wackypow', + 'wackypretzel', + 'wackyquack', + 'wackyroni', + 'wackyscooter', + 'wackyscreech', + 'wackysmirk', + 'wackysnooker', + 'wackysnoop', + 'wackysnout', + 'wackysocks', + 'wackyspeed', + 'wackyspinner', + 'wackysplat', + 'wackysprinkles', + 'wackysticks', + 'wackystink', + 'wackyswirl', + 'wackyteeth', + 'wackythud', + 'wackytoes', + 'wackyton', + 'wackytoon', + 'wackytooth', + 'wackytwist', + 'wackyville', + 'wackywhatsit', + 'wackywhip', + 'wackywig', + 'wackywoof', + 'wackyzaner', + 'wackyzap', + 'wackyzapper', + 'wackyzilla', + 'wackyzone', + 'wackyzoom', + 'waddle', + 'waddling', + 'waddup', + 'wade', + 'wag', + 'wager', + 'wagered', + "wagerin'", + "wagerin's", + 'wagers', + 'waggly', + "wagner's", + 'wagon', + "wagon's", + 'wagons', + 'wags', + 'wahoo', + 'wai', + 'wail', + 'wailing', + 'wainscoting', + 'wait', + 'waited', + 'waiter', + 'waiters', + 'waiting', + 'waits', + 'wakaba', + 'wake', + 'wake-up', + 'wake-up-talent', + 'waked', + 'waker', + 'wakes', + 'wakey', + 'waking', + 'walden', + 'waldo', + 'waldorf', + 'walk', + 'walked', + 'walker', + 'walkers', + 'walking', + 'walks', + 'wall', + "wall's", + 'wall-e', + 'wallaberries', + 'wallaby', + 'wallace', + 'walle', + 'walled', + 'waller', + 'wallet', + 'wallflower', + 'walling', + 'wallop', + 'wallpaper', + 'wallpapered', + 'wallpapering', + 'wallpapers', + 'walls', + 'walnut', + 'walnut-drumming', + 'walrus', + 'walruses', + 'walt', + "walt's", + 'waltz', + 'waltzed', + 'waltzes', + 'waltzing', + 'wampum', + 'wand', + 'wanded', + 'wander', + 'wandered', + 'wanderers', + 'wandering', + 'wanders', + 'wandies', + 'wands', + 'wandy', + 'wango', + 'wanick', + 'wanna', + 'wannabe', + 'want', + 'wanted', + 'wanter', + 'wanting', + 'wants', + 'war', + 'ward', + 'wardrobe', + 'wardrobes', + 'ware', + 'warehouse', + 'wares', + 'waring', + 'wariors', + 'warlord', + 'warlords', + 'warm', + 'warmed', + 'warmer', + 'warmers', + 'warmest', + 'warming', + 'warmly', + 'warmongers', + 'warmonks', + 'warmth', + 'warn', + 'warned', + 'warner', + 'warning', + 'warnings', + 'warns', + 'warp', + 'warped', + 'warrant', + 'warrants', + 'warren', + 'warrior', + 'warriors', + 'warrrr', + 'wars', + 'warship', + "warship's", + 'warships', + "warships'", + 'warskulls', + 'wart', + 'warzepple', + 'was', + 'wash', + 'washcloths', + 'washed', + 'washer', + 'washers', + 'washes', + 'washing', + 'washings', + 'washington', + "washington's", + "wasn't", + 'wasnt', + 'wasp', + 'wasp-skin', + 'wasps', + 'wassup', + 'waste', + 'wasted', + 'wasteland', + 'wastelands', + 'waster', + 'wasters', + 'wastes', + 'wasting', + 'wat', + 'watch', + 'watched', + 'watcher', + 'watchers', + 'watches', + 'watchin', + 'watching', + 'watchings', + 'water', + "water's", + 'water-talent', + 'watercooler', + 'watered', + 'waterer', + 'waterers', + 'waterfall', + "waterfall's", + 'waterfalls', + 'waterguns', + 'watering', + 'watermelon', + 'watermelons', + 'waterpark', + "waterpark's", + 'waterparkers', + 'waterproof', + 'waters', + 'waterslide', + "waterslide's", + 'watersliders', + 'watery', + 'watkins', + 'wats', + 'wave', + 'waved', + 'waver', + 'waverly', + 'wavers', + 'waverunners', + 'waves', + 'waving', + 'wavy', + 'way', + "way's", + 'waylon', + 'ways', + 'wayward', + 'wazup', + 'wb', + "wdig's", + 'wdw', + 'we', + "we'd", + "we'll", + "we're", + "we've", + 'we-evil', + 'weak', + 'weaken', + 'weakens', + 'weaker', + 'weakest', + 'weakling', + 'weakly', + 'weakness', + 'wealthy', + 'wear', + 'wearer', + 'wearing', + 'wears', + 'weasel', + 'weaselbee', + 'weaselberry', + 'weaselblabber', + 'weaselbocker', + 'weaselboing', + 'weaselboom', + 'weaselbounce', + 'weaselbouncer', + 'weaselbrains', + 'weaselbubble', + 'weaselbumble', + 'weaselbump', + 'weaselbumper', + 'weaselburger', + 'weaselchomp', + 'weaselcorn', + 'weaselcrash', + 'weaselcrumbs', + 'weaselcrump', + 'weaselcrunch', + 'weaseldoodle', + 'weaseldorf', + 'weaselface', + 'weaselfidget', + 'weaselfink', + 'weaselfish', + 'weaselflap', + 'weaselflapper', + 'weaselflinger', + 'weaselflip', + 'weaselflipper', + 'weaselfoot', + 'weaselfuddy', + 'weaselfussen', + 'weaselgadget', + 'weaselgargle', + 'weaselgloop', + 'weaselglop', + 'weaselgoober', + 'weaselgoose', + 'weaselgrooven', + 'weaselhoffer', + 'weaselhopper', + 'weaseljinks', + 'weaselklunk', + 'weaselknees', + 'weaselmarble', + 'weaselmash', + 'weaselmonkey', + 'weaselmooch', + 'weaselmouth', + 'weaselmuddle', + 'weaselmuffin', + 'weaselmush', + 'weaselnerd', + 'weaselnoodle', + 'weaselnose', + 'weaselnugget', + 'weaselphew', + 'weaselphooey', + 'weaselpocket', + 'weaselpoof', + 'weaselpop', + 'weaselpounce', + 'weaselpow', + 'weaselpretzel', + 'weaselquack', + 'weaselroni', + 'weasels', + 'weaselscooter', + 'weaselscreech', + 'weaselsmirk', + 'weaselsnooker', + 'weaselsnoop', + 'weaselsnout', + 'weaselsocks', + 'weaselspeed', + 'weaselspinner', + 'weaselsplat', + 'weaselsprinkles', + 'weaselsticks', + 'weaselstink', + 'weaselswirl', + 'weaselteeth', + 'weaselthud', + 'weaseltoes', + 'weaselton', + 'weaseltoon', + 'weaseltooth', + 'weaseltwist', + 'weaselwhatsit', + 'weaselwhip', + 'weaselwig', + 'weaselwoof', + 'weaselzaner', + 'weaselzap', + 'weaselzapper', + 'weaselzilla', + 'weaselzoom', + 'weather', + 'weathered', + 'weatherer', + 'weathering', + 'weatherly', + 'weathers', + 'weave', + 'weaves', + 'weaving', + 'web', + 'webber', + 'webepirates', + 'websight', + 'website', + 'webster', + 'wedding', + 'weddings', + 'wednesday', + 'wednesdays', + 'weeds', + 'week', + "week's", + 'weekdays', + 'weekend', + 'weekenders', + 'weekly', + 'weeks', + 'wego', + 'wei', + 'weigh', + 'weight', + 'weights', + 'weird', + 'weirded', + 'weirdings', + 'weirdo', + 'weirdos', + 'weiss', + 'welch', + 'welcome', + 'welcomed', + 'welcomely', + 'welcomer', + 'welcomes', + 'welcoming', + 'well', + 'welled', + 'welling', + 'wells', + 'wenchs', + 'went', + 'were', + "weren't", + 'werent', + 'west', + 'wester', + 'western', + 'westerner', + 'westerners', + 'westing', + 'westward', + 'wet', + 'wha', + 'whaddya', + 'whale', + 'whalebone', + 'whaler', + 'whales', + 'whaling', + 'wham', + 'whammo', + 'what', + "what'cha", + "what's", + 'what-in', + 'what-the-hey', + 'whatcha', + 'whatever', + "whatever's", + 'whats', + 'wheat', + 'whee', + 'wheee', + 'wheeee', + 'wheeeee', + 'wheeeeee', + 'wheeeeeee', + 'wheel', + 'wheelbarrow', + 'wheelbarrows', + 'wheeled', + 'wheeler', + 'wheelers', + 'wheeling', + 'wheelings', + 'wheels', + 'wheezer', + 'when', + "when's", + 'whenever', + 'whens', + 'where', + "where's", + 'wheres', + 'wherever', + 'whether', + 'whew', + 'which', + 'whiff', + 'whiffle', + 'whiffs', + 'while', + 'whiled', + 'whiles', + 'whiling', + 'whimsical', + 'whimsy', + 'whining', + 'whinnie', + "whinnie's", + 'whiny', + 'whiplash', + 'whirl', + 'whirligig', + 'whirling', + 'whirlpool', + 'whirly', + 'whirr', + 'whisk', + 'whisked', + 'whisker', + 'whiskerbee', + 'whiskerberry', + 'whiskerblabber', + 'whiskerbocker', + 'whiskerboing', + 'whiskerboom', + 'whiskerbounce', + 'whiskerbouncer', + 'whiskerbrains', + 'whiskerbubble', + 'whiskerbumble', + 'whiskerbump', + 'whiskerbumper', + 'whiskerburger', + 'whiskerchomp', + 'whiskercorn', + 'whiskercrash', + 'whiskercrumbs', + 'whiskercrump', + 'whiskercrunch', + 'whiskerdoodle', + 'whiskerdorf', + 'whiskerface', + 'whiskerfidget', + 'whiskerfink', + 'whiskerfish', + 'whiskerflap', + 'whiskerflapper', + 'whiskerflinger', + 'whiskerflip', + 'whiskerflipper', + 'whiskerfoot', + 'whiskerfuddy', + 'whiskerfussen', + 'whiskergadget', + 'whiskergargle', + 'whiskergloop', + 'whiskerglop', + 'whiskergoober', + 'whiskergoose', + 'whiskergrooven', + 'whiskerhoffer', + 'whiskerhopper', + 'whiskerjinks', + 'whiskerklunk', + 'whiskerknees', + 'whiskermarble', + 'whiskermash', + 'whiskermonkey', + 'whiskermooch', + 'whiskermouth', + 'whiskermuddle', + 'whiskermuffin', + 'whiskermush', + 'whiskernerd', + 'whiskernoodle', + 'whiskernose', + 'whiskernugget', + 'whiskerphew', + 'whiskerphooey', + 'whiskerpocket', + 'whiskerpoof', + 'whiskerpop', + 'whiskerpounce', + 'whiskerpow', + 'whiskerpretzel', + 'whiskerquack', + 'whiskerroni', + 'whiskers', + 'whiskerscooter', + 'whiskerscreech', + 'whiskersmirk', + 'whiskersnooker', + 'whiskersnoop', + 'whiskersnout', + 'whiskersocks', + 'whiskerspeed', + 'whiskerspinner', + 'whiskersplat', + 'whiskersprinkles', + 'whiskersticks', + 'whiskerstink', + 'whiskerswirl', + 'whiskerteeth', + 'whiskerthud', + 'whiskertoes', + 'whiskerton', + 'whiskertoon', + 'whiskertooth', + 'whiskertwist', + 'whiskerwhatsit', + 'whiskerwhip', + 'whiskerwig', + 'whiskerwoof', + 'whiskerzaner', + 'whiskerzap', + 'whiskerzapper', + 'whiskerzilla', + 'whiskerzoom', + 'whisper', + 'whispered', + 'whispering', + 'whispers', + 'whistle', + 'whistlebee', + 'whistleberry', + 'whistleblabber', + 'whistlebocker', + 'whistleboing', + 'whistleboom', + 'whistlebounce', + 'whistlebouncer', + 'whistlebrains', + 'whistlebubble', + 'whistlebumble', + 'whistlebump', + 'whistlebumper', + 'whistleburger', + 'whistlechomp', + 'whistlecorn', + 'whistlecrash', + 'whistlecrumbs', + 'whistlecrump', + 'whistlecrunch', + 'whistled', + 'whistledoodle', + 'whistledorf', + 'whistleface', + 'whistlefidget', + 'whistlefink', + 'whistlefish', + 'whistleflap', + 'whistleflapper', + 'whistleflinger', + 'whistleflip', + 'whistleflipper', + 'whistlefoot', + 'whistlefuddy', + 'whistlefussen', + 'whistlegadget', + 'whistlegargle', + 'whistlegloop', + 'whistleglop', + 'whistlegoober', + 'whistlegoose', + 'whistlegrooven', + 'whistlehoffer', + 'whistlehopper', + 'whistlejinks', + 'whistleklunk', + 'whistleknees', + 'whistlemarble', + 'whistlemash', + 'whistlemonkey', + 'whistlemooch', + 'whistlemouth', + 'whistlemuddle', + 'whistlemuffin', + 'whistlemush', + 'whistlenerd', + 'whistlenoodle', + 'whistlenose', + 'whistlenugget', + 'whistlephew', + 'whistlephooey', + 'whistlepocket', + 'whistlepoof', + 'whistlepop', + 'whistlepounce', + 'whistlepow', + 'whistlepretzel', + 'whistlequack', + "whistler's", + 'whistleroni', + 'whistles', + 'whistlescooter', + 'whistlescreech', + 'whistlesmirk', + 'whistlesnooker', + 'whistlesnoop', + 'whistlesnout', + 'whistlesocks', + 'whistlespeed', + 'whistlespinner', + 'whistlesplat', + 'whistlesprinkles', + 'whistlesticks', + 'whistlestink', + 'whistleswirl', + 'whistleteeth', + 'whistlethud', + 'whistletoes', + 'whistleton', + 'whistletoon', + 'whistletooth', + 'whistletwist', + 'whistlewhatsit', + 'whistlewhip', + 'whistlewig', + 'whistlewoof', + 'whistlezaner', + 'whistlezap', + 'whistlezapper', + 'whistlezilla', + 'whistlezoom', + 'whistling', + 'white', + "white's", + 'whiteboard', + 'whiteboards', + 'whitelist', + 'whitelisted', + 'whitening', + 'whitestar', + 'whitewater', + 'who', + "who'd", + "who's", + 'whoa', + 'whoah', + 'whodunit', + 'whoever', + 'whoframedrogerrabbit', + 'whole', + 'wholly', + 'whom', + 'whooo', + 'whoop', + 'whoopee', + 'whoopie', + 'whoops', + 'whoopsie', + 'whoosh', + 'whopper', + 'whopping', + 'whos', + 'whose', + 'why', + 'wicked', + 'wicker', + 'wide', + 'widely', + 'wider', + 'widescreen', + 'widest', + 'widget', + 'widgets', + 'widow', + 'width', + 'wife', + 'wiffle', + 'wifi', + 'wig', + 'wiggle', + "wiggle's", + 'wiggles', + 'wigs', + 'wii', + 'wiidburns', + 'wikipedia', + 'wilbur', + 'wild', + 'wild-n-crazy', + 'wild7', + 'wildbee', + 'wildberry', + 'wildblabber', + 'wildbocker', + 'wildboing', + 'wildboom', + 'wildbounce', + 'wildbouncer', + 'wildbrains', + 'wildbubble', + 'wildbumble', + 'wildbump', + 'wildbumper', + 'wildburger', + 'wildburns', + 'wildcat', + 'wildcats', + 'wildchomp', + 'wildcorn', + 'wildcrash', + 'wildcrumbs', + 'wildcrump', + 'wildcrunch', + 'wilddoodle', + 'wilddorf', + 'wilder', + 'wilderness', + 'wildest', + 'wildface', + 'wildfidget', + 'wildfink', + 'wildfire', + 'wildfish', + 'wildflap', + 'wildflapper', + 'wildflinger', + 'wildflip', + 'wildflipper', + 'wildfoot', + 'wildfuddy', + 'wildfussen', + 'wildgadget', + 'wildgargle', + 'wildgloop', + 'wildglop', + 'wildgoober', + 'wildgoose', + 'wildgrooven', + 'wildhoffer', + 'wildhopper', + 'wilding', + 'wildjinks', + 'wildklunk', + 'wildknees', + 'wildly', + 'wildmarble', + 'wildmash', + 'wildmonkey', + 'wildmooch', + 'wildmouth', + 'wildmuddle', + 'wildmuffin', + 'wildmush', + 'wildnerd', + 'wildnoodle', + 'wildnose', + 'wildnugget', + 'wildphew', + 'wildphooey', + 'wildpocket', + 'wildpoof', + 'wildpop', + 'wildpounce', + 'wildpow', + 'wildpretzel', + 'wildquack', + 'wildroni', + 'wildscooter', + 'wildscreech', + 'wildsmirk', + 'wildsnooker', + 'wildsnoop', + 'wildsnout', + 'wildsocks', + 'wildspeed', + 'wildspinner', + 'wildsplat', + 'wildsprinkles', + 'wildsticks', + 'wildstink', + 'wildswirl', + 'wildteeth', + 'wildthud', + 'wildtoes', + 'wildton', + 'wildtoon', + 'wildtooth', + 'wildtwist', + 'wildwhatsit', + 'wildwhip', + 'wildwig', + 'wildwoods', + 'wildwoof', + 'wildzaner', + 'wildzap', + 'wildzapper', + 'wildzilla', + 'wildzoom', + 'wilee', + 'will', + 'willa', + "willa's", + 'willas', + 'willed', + 'willer', + 'william', + 'williams', + 'willing', + 'willings', + 'willow', + 'willows', + 'willpower', + 'wills', + 'wilma', + 'wilt', + 'wilts', + 'wimbleweather', + 'win', + 'winba', + 'winbus', + 'wind', + 'wind-racer', + 'windburn', + 'windcatcher', + 'winded', + 'winder', + 'winders', + 'winding', + 'windjammer', + 'windjammers', + 'windmane', + 'windmill', + 'windmills', + 'windora', + 'window', + "window's", + 'windowed', + 'windowing', + 'windows', + 'winds', + 'windshadow', + 'windsor', + 'windswept', + 'windward', + 'windy', + 'wing', + 'wing-washing', + 'winged', + 'winger', + 'wingers', + 'winging', + 'wings', + 'wingtip', + 'wingtips', + 'wink', + 'winkination', + 'winkle', + "winkle's", + 'winks', + 'winky', + 'winn', + 'winner', + "winner's", + 'winners', + 'winnie', + "winnie's", + 'winning', + 'winnings', + 'wins', + 'winter', + "winter's", + 'wintered', + 'winterer', + 'wintering', + 'winterly', + 'winters', + 'wipeout', + 'wire', + 'wires', + 'wisdom', + 'wise', + 'wiseacre', + "wiseacre's", + 'wiseacres', + 'wisely', + 'wish', + 'wished', + 'wisher', + 'wishers', + 'wishes', + 'wishing', + 'wispa', + 'wit', + 'witch', + "witch's", + 'witches', + 'witching', + 'witchy', + 'with', + 'withdrawal', + 'wither', + 'withers', + 'within', + 'without', + 'witness', + 'witnessed', + 'witnesses', + 'witnessing', + 'witty', + 'wittybee', + 'wittyberry', + 'wittyblabber', + 'wittybocker', + 'wittyboing', + 'wittyboom', + 'wittybounce', + 'wittybouncer', + 'wittybrains', + 'wittybubble', + 'wittybumble', + 'wittybump', + 'wittybumper', + 'wittyburger', + 'wittychomp', + 'wittycorn', + 'wittycrash', + 'wittycrumbs', + 'wittycrump', + 'wittycrunch', + 'wittydoodle', + 'wittydorf', + 'wittyface', + 'wittyfidget', + 'wittyfink', + 'wittyfish', + 'wittyflap', + 'wittyflapper', + 'wittyflinger', + 'wittyflip', + 'wittyflipper', + 'wittyfoot', + 'wittyfuddy', + 'wittyfussen', + 'wittygadget', + 'wittygargle', + 'wittygloop', + 'wittyglop', + 'wittygoober', + 'wittygoose', + 'wittygrooven', + 'wittyhoffer', + 'wittyhopper', + 'wittyjinks', + 'wittyklunk', + 'wittyknees', + 'wittymarble', + 'wittymash', + 'wittymonkey', + 'wittymooch', + 'wittymouth', + 'wittymuddle', + 'wittymuffin', + 'wittymush', + 'wittynerd', + 'wittynoodle', + 'wittynose', + 'wittynugget', + 'wittyphew', + 'wittyphooey', + 'wittypocket', + 'wittypoof', + 'wittypop', + 'wittypounce', + 'wittypow', + 'wittypretzel', + 'wittyquack', + 'wittyroni', + 'wittyscooter', + 'wittyscreech', + 'wittysmirk', + 'wittysnooker', + 'wittysnoop', + 'wittysnout', + 'wittysocks', + 'wittyspeed', + 'wittyspinner', + 'wittysplat', + 'wittysprinkles', + 'wittysticks', + 'wittystink', + 'wittyswirl', + 'wittyteeth', + 'wittythud', + 'wittytoes', + 'wittyton', + 'wittytoon', + 'wittytooth', + 'wittytwist', + 'wittywhatsit', + 'wittywhip', + 'wittywig', + 'wittywoof', + 'wittyzaner', + 'wittyzap', + 'wittyzapper', + 'wittyzilla', + 'wittyzoom', + 'wiz', + 'wizard', + "wizard's", + 'wizards', + 'wizrd', + 'wo', + 'woah', + 'wobble', + 'wobbled', + 'wobbles', + 'wobbling', + 'wobbly', + 'wocka', + 'woe', + 'woeful', + 'wok', + 'woke', + 'woks', + 'wolf', + "wolf's", + 'wolfbane', + 'wolfe', + 'wolfer', + 'wolfes', + 'wolfhearts', + 'wolfpack', + 'wolfs', + 'wolfsbane', + 'wolves', + 'woman', + 'women', + 'womp', + 'won', + "won't", + 'wonder', + 'wonderbee', + 'wonderberry', + 'wonderblabber', + 'wonderblue', + 'wonderbocker', + 'wonderboing', + 'wonderboom', + 'wonderbounce', + 'wonderbouncer', + 'wonderbrains', + 'wonderbubble', + 'wonderbumble', + 'wonderbump', + 'wonderbumper', + 'wonderburger', + 'wonderchomp', + 'wondercorn', + 'wondercrash', + 'wondercrumbs', + 'wondercrump', + 'wondercrunch', + 'wonderdoodle', + 'wonderdorf', + 'wondered', + 'wonderer', + 'wonderers', + 'wonderface', + 'wonderfidget', + 'wonderfink', + 'wonderfish', + 'wonderflap', + 'wonderflapper', + 'wonderflinger', + 'wonderflip', + 'wonderflipper', + 'wonderfoot', + 'wonderfuddy', + 'wonderful', + 'wonderfully', + 'wonderfussen', + 'wondergadget', + 'wondergargle', + 'wondergloop', + 'wonderglop', + 'wondergoober', + 'wondergoose', + 'wondergrooven', + 'wonderhoffer', + 'wonderhopper', + 'wondering', + 'wonderings', + 'wonderjinks', + 'wonderklunk', + 'wonderknees', + 'wonderland', + "wonderland's", + 'wonderlands', + 'wondermarble', + 'wondermash', + 'wondermonkey', + 'wondermooch', + 'wondermouth', + 'wondermuddle', + 'wondermuffin', + 'wondermush', + 'wondernerd', + 'wondernoodle', + 'wondernose', + 'wondernugget', + 'wonderphew', + 'wonderphooey', + 'wonderpocket', + 'wonderpoof', + 'wonderpop', + 'wonderpounce', + 'wonderpow', + 'wonderpretzel', + 'wonderquack', + 'wonderroni', + 'wonders', + 'wonderscooter', + 'wonderscreech', + 'wondersmirk', + 'wondersnooker', + 'wondersnoop', + 'wondersnout', + 'wondersocks', + 'wonderspeed', + 'wonderspinner', + 'wondersplat', + 'wondersprinkles', + 'wondersticks', + 'wonderstink', + 'wonderswirl', + 'wonderteeth', + 'wonderthud', + 'wondertoes', + 'wonderton', + 'wondertoon', + 'wondertooth', + 'wondertwist', + 'wonderwhatsit', + 'wonderwhip', + 'wonderwig', + 'wonderwoof', + 'wonderzaner', + 'wonderzap', + 'wonderzapper', + 'wonderzilla', + 'wonderzoom', + 'wondrous', + 'wont', + 'woo', + 'wood', + 'wooded', + 'woodland', + 'woodruff', + 'woods', + 'woof', + 'woohoo', + 'woop', + 'woot', + 'woozy', + 'word', + 'word-licious', + 'wordbelch', + 'wordbug', + 'wordburps', + 'worddog', + 'worded', + 'wordeze', + 'wordfly', + 'wordglitch', + 'wording', + 'wordlo', + 'wordmania', + 'wordmeister', + 'wordmist', + 'wordpaths', + 'words', + "words'n'stuff", + 'wordseek', + 'wordsmith', + 'wordsmiths', + 'wordstinkers', + 'wordstuff', + 'wordwings', + 'wordworks', + 'wordworms', + 'woriors', + 'work', + "work's", + 'worked', + 'worker', + 'workers', + 'workin', + 'working', + 'workings', + 'workout', + 'works', + 'works-in-progress', + 'workshop', + 'workshops', + 'world', + "world's", + 'worlds', + 'worm', + 'worms', + 'worn', + 'worried', + 'worrier', + 'worriers', + 'worries', + 'worriors', + 'worry', + 'worrying', + 'worse', + 'worst', + 'worth', + 'worthing', + 'worthy', + 'wot', + 'wough', + 'would', + "would've", + 'woulda', + 'wouldest', + "wouldn't", + 'wouldnt', + 'wound', + 'wound-up', + 'wounded', + 'wounding', + 'wounds', + 'woven', + 'wow', + 'wraith', + 'wraiths', + 'wrapper', + 'wrath', + 'wreath', + 'wreathes', + 'wreaths', + 'wreck', + 'wrecked', + 'wrecking', + 'wrecking-talents', + 'wrecks', + 'wrench', + 'wrestling', + 'wretch', + 'wriggle', + 'wright', + "wright's", + 'wringling', + 'wrinkle', + 'wrinklebee', + 'wrinkleberry', + 'wrinkleblabber', + 'wrinklebocker', + 'wrinkleboing', + 'wrinkleboom', + 'wrinklebounce', + 'wrinklebouncer', + 'wrinklebrains', + 'wrinklebubble', + 'wrinklebumble', + 'wrinklebump', + 'wrinklebumper', + 'wrinkleburger', + 'wrinklechomp', + 'wrinklecorn', + 'wrinklecrash', + 'wrinklecrumbs', + 'wrinklecrump', + 'wrinklecrunch', + 'wrinkled', + 'wrinkledoodle', + 'wrinkledorf', + 'wrinkleface', + 'wrinklefidget', + 'wrinklefink', + 'wrinklefish', + 'wrinkleflap', + 'wrinkleflapper', + 'wrinkleflinger', + 'wrinkleflip', + 'wrinkleflipper', + 'wrinklefoot', + 'wrinklefuddy', + 'wrinklefussen', + 'wrinklegadget', + 'wrinklegargle', + 'wrinklegloop', + 'wrinkleglop', + 'wrinklegoober', + 'wrinklegoose', + 'wrinklegrooven', + 'wrinklehoffer', + 'wrinklehopper', + 'wrinklejinks', + 'wrinkleklunk', + 'wrinkleknees', + 'wrinklemarble', + 'wrinklemash', + 'wrinklemonkey', + 'wrinklemooch', + 'wrinklemouth', + 'wrinklemuddle', + 'wrinklemuffin', + 'wrinklemush', + 'wrinklenerd', + 'wrinklenoodle', + 'wrinklenose', + 'wrinklenugget', + 'wrinklephew', + 'wrinklephooey', + 'wrinklepocket', + 'wrinklepoof', + 'wrinklepop', + 'wrinklepounce', + 'wrinklepow', + 'wrinklepretzel', + 'wrinklequack', + 'wrinkleroni', + 'wrinkles', + 'wrinklescooter', + 'wrinklescreech', + 'wrinklesmirk', + 'wrinklesnooker', + 'wrinklesnoop', + 'wrinklesnout', + 'wrinklesocks', + 'wrinklespeed', + 'wrinklespinner', + 'wrinklesplat', + 'wrinklesprinkles', + 'wrinklesticks', + 'wrinklestink', + 'wrinkleswirl', + 'wrinkleteeth', + 'wrinklethud', + 'wrinkletoes', + 'wrinkleton', + 'wrinkletoon', + 'wrinkletooth', + 'wrinkletwist', + 'wrinklewhatsit', + 'wrinklewhip', + 'wrinklewig', + 'wrinklewoof', + 'wrinklezaner', + 'wrinklezap', + 'wrinklezapper', + 'wrinklezilla', + 'wrinklezoom', + 'wriot', + 'write', + 'writer', + 'writers', + 'writes', + 'writing', + 'writings', + 'written', + 'wrld', + 'wrong', + 'wronged', + 'wronger', + 'wrongest', + 'wronging', + 'wrongly', + 'wrongs', + 'wrote', + 'wtg', + 'wumbo', + 'wut', + 'wwod', + 'www.toonhq.org', + 'www.toontownstride.com', + "wyatt's", + 'wyda', + 'wynken', + 'wynn', + 'wynne', + 'wysteria', + 'x-shop', + 'x-tremely', + 'xavier', + 'xbox', + 'xd', + 'xd-buy', + 'xdash', + 'xdeals', + 'xder', + 'xdibs', + 'xdig', + 'xdirect', + 'xdoer', + 'xdome', + 'xdot', + 'xdough', + 'xdrive', + 'xem', + 'xenops', + 'xia', + "xiamen's", + 'xii', + 'xiii', + 'xmas', + 'xp', + 'xpedition', + 'xpend', + 'xpert', + 'xsentials', + 'xtra', + 'xtraordinary', + 'xtreme', + 'xtremely', + 'y', + "y'all", + "y'er", + 'ya', + "ya'll", + 'yaarrrgghh', + 'yacht', + "yacht's", + 'yachting', + 'yachts', + 'yackety-yak', + 'yah', + 'yalarad', + 'yall', + 'yamaha', + 'yang', + "yang's", + 'yank', + 'yankee', + 'yankees', + 'yanks', + 'yanni', + 'yar', + 'yard', + "yard's", + 'yardage', + 'yardarm', + 'yarded', + 'yarding', + 'yards', + 'yardwork', + 'yarn', + 'yarr', + 'yarrow', + 'yarrr', + 'yas', + 'yasmine', + 'yasss', + 'yavn', + 'yawn', + 'yawner', + 'yawning', + 'yawns', + 'yay', + 'ye', + "ye'll", + "ye're", + "ye've", + 'yea', + 'yeah', + 'year', + "year's", + 'yearbook', + 'years', + 'yee', + 'yee-haw', + 'yeehah', + 'yeehaw', + 'yeh', + 'yell', + 'yelled', + 'yeller', + 'yelling', + 'yellow', + 'yellow-green', + 'yellow-orange', + 'yellow-shelled', + 'yells', + 'yelp', + 'yensid', + "yensid's", + 'yep', + 'yeppers', + 'yer', + 'yerself', + 'yes', + 'yeses', + 'yesman', + 'yesmen', + 'yess', + 'yesss', + 'yesterday', + 'yet', + 'yeti', + "yeti's", + 'yetis', + 'yets', + 'yey', + 'yield', + 'yikes', + 'yin', + 'ying', + 'yippee', + 'yippie', + 'yo', + 'yodo', + 'yogi', + 'yogurt', + 'yolo', + 'yom', + 'yoo', + 'york', + 'yoshi', + 'you', + "you'd", + "you'll", + "you're", + "you've", + 'youd', + 'youll', + 'young', + 'your', + "your's", + 'youre', + 'yours', + 'yourself', + 'youth', + 'youtube', + 'youtuber', + 'youve', + 'yow', + 'yowl', + 'yoyo', + 'yuck', + 'yucks', + 'yufalla', + 'yugoslav', + 'yugoslavia', + 'yuki', + "yuki's", + 'yukon', + 'yumeko', + 'yummy', + 'yup', + 'yus', + 'yw', + 'yzma', + 'z-fighting', + 'zaamaros', + 'zaamaru', + 'zaapi', + 'zabuton', + 'zabutons', + 'zac', + 'zach', + 'zachgates7', + 'zack', + "zack's", + 'zamboni', + 'zambonis', + 'zanes', + 'zangetsu', + 'zany', + 'zanzibarbarians', + 'zaoran', + 'zap', + 'zari', + 'zart', + 'zazu', + "zazu's", + 'zazus', + 'zazzle', + 'zealous', + 'zebra', + "zebra's", + 'zebras', + 'zedd', + 'zeddars', + 'zeke', + 'zelda', + 'zen', + 'zenith', + 'zeniths', + 'zenon', + 'zep', + 'zephyr', + 'zeppelin', + 'zeragong', + 'zero', + 'zero-gravity', + 'zesty', + 'zeus', + 'zhilo', + 'ziba', + 'zigeunermusik', + 'zigg', + 'ziggs', + 'ziggurat', + 'zigguratxnaut', + 'ziggy', + "ziggy's", + 'zigzag', + 'zillerbee', + 'zillerberry', + 'zillerblabber', + 'zillerbocker', + 'zillerboing', + 'zillerboom', + 'zillerbounce', + 'zillerbouncer', + 'zillerbrains', + 'zillerbubble', + 'zillerbumble', + 'zillerbump', + 'zillerbumper', + 'zillerburger', + 'zillerchomp', + 'zillercorn', + 'zillercrash', + 'zillercrumbs', + 'zillercrump', + 'zillercrunch', + 'zillerdoodle', + 'zillerdorf', + 'zillerface', + 'zillerfidget', + 'zillerfink', + 'zillerfish', + 'zillerflap', + 'zillerflapper', + 'zillerflinger', + 'zillerflip', + 'zillerflipper', + 'zillerfoot', + 'zillerfuddy', + 'zillerfussen', + 'zillergadget', + 'zillergargle', + 'zillergloop', + 'zillerglop', + 'zillergoober', + 'zillergoose', + 'zillergrooven', + 'zillerhoffer', + 'zillerhopper', + 'zillerjinks', + 'zillerklunk', + 'zillerknees', + 'zillermarble', + 'zillermash', + 'zillermonkey', + 'zillermooch', + 'zillermouth', + 'zillermuddle', + 'zillermuffin', + 'zillermush', + 'zillernerd', + 'zillernoodle', + 'zillernose', + 'zillernugget', + 'zillerphew', + 'zillerphooey', + 'zillerpocket', + 'zillerpoof', + 'zillerpop', + 'zillerpounce', + 'zillerpow', + 'zillerpretzel', + 'zillerquack', + 'zillerroni', + 'zillerscooter', + 'zillerscreech', + 'zillersmirk', + 'zillersnooker', + 'zillersnoop', + 'zillersnout', + 'zillersocks', + 'zillerspeed', + 'zillerspinner', + 'zillersplat', + 'zillersprinkles', + 'zillersticks', + 'zillerstink', + 'zillerswirl', + 'zillerteeth', + 'zillerthud', + 'zillertoes', + 'zillerton', + 'zillertoon', + 'zillertooth', + 'zillertwist', + 'zillerwhatsit', + 'zillerwhip', + 'zillerwig', + 'zillerwoof', + 'zillerzaner', + 'zillerzap', + 'zillerzapper', + 'zillerzilla', + 'zillerzoom', + 'zillion', + 'zimmer', + 'zing', + 'zinger', + 'zingers', + 'zinnia', + 'zinnias', + 'zip-a-dee-doo-dah', + 'zippenbee', + 'zippenberry', + 'zippenblabber', + 'zippenbocker', + 'zippenboing', + 'zippenboom', + 'zippenbounce', + 'zippenbouncer', + 'zippenbrains', + 'zippenbubble', + 'zippenbumble', + 'zippenbump', + 'zippenbumper', + 'zippenburger', + 'zippenchomp', + 'zippencorn', + 'zippencrash', + 'zippencrumbs', + 'zippencrump', + 'zippencrunch', + 'zippendoodle', + 'zippendorf', + 'zippenface', + 'zippenfidget', + 'zippenfink', + 'zippenfish', + 'zippenflap', + 'zippenflapper', + 'zippenflinger', + 'zippenflip', + 'zippenflipper', + 'zippenfoot', + 'zippenfuddy', + 'zippenfussen', + 'zippengadget', + 'zippengargle', + 'zippengloop', + 'zippenglop', + 'zippengoober', + 'zippengoose', + 'zippengrooven', + 'zippenhoffer', + 'zippenhopper', + 'zippenjinks', + 'zippenklunk', + 'zippenknees', + 'zippenmarble', + 'zippenmash', + 'zippenmonkey', + 'zippenmooch', + 'zippenmouth', + 'zippenmuddle', + 'zippenmuffin', + 'zippenmush', + 'zippennerd', + 'zippennoodle', + 'zippennose', + 'zippennugget', + 'zippenphew', + 'zippenphooey', + 'zippenpocket', + 'zippenpoof', + 'zippenpop', + 'zippenpounce', + 'zippenpow', + 'zippenpretzel', + 'zippenquack', + 'zippenroni', + 'zippenscooter', + 'zippenscreech', + 'zippensmirk', + 'zippensnooker', + 'zippensnoop', + 'zippensnout', + 'zippensocks', + 'zippenspeed', + 'zippenspinner', + 'zippensplat', + 'zippensprinkles', + 'zippensticks', + 'zippenstink', + 'zippenswirl', + 'zippenteeth', + 'zippenthud', + 'zippentoes', + 'zippenton', + 'zippentoon', + 'zippentooth', + 'zippentwist', + 'zippenwhatsit', + 'zippenwhip', + 'zippenwig', + 'zippenwoof', + 'zippenzaner', + 'zippenzap', + 'zippenzapper', + 'zippenzilla', + 'zippenzoom', + 'zippity', + 'zippy', + "zippy's", + 'zither', + 'zithers', + 'zizzle', + 'zombats', + 'zombie', + 'zombies', + 'zone', + 'zoner', + 'zones', + 'zonk', + 'zonked', + 'zonks', + 'zoo', + "zoo's", + 'zooblebee', + 'zoobleberry', + 'zoobleblabber', + 'zooblebocker', + 'zoobleboing', + 'zoobleboom', + 'zooblebounce', + 'zooblebouncer', + 'zooblebrains', + 'zooblebubble', + 'zooblebumble', + 'zooblebump', + 'zooblebumper', + 'zoobleburger', + 'zooblechomp', + 'zooblecorn', + 'zooblecrash', + 'zooblecrumbs', + 'zooblecrump', + 'zooblecrunch', + 'zoobledoodle', + 'zoobledorf', + 'zoobleface', + 'zooblefidget', + 'zooblefink', + 'zooblefish', + 'zoobleflap', + 'zoobleflapper', + 'zoobleflinger', + 'zoobleflip', + 'zoobleflipper', + 'zooblefoot', + 'zooblefuddy', + 'zooblefussen', + 'zooblegadget', + 'zooblegargle', + 'zooblegloop', + 'zoobleglop', + 'zooblegoober', + 'zooblegoose', + 'zooblegrooven', + 'zooblehoffer', + 'zooblehopper', + 'zooblejinks', + 'zoobleklunk', + 'zoobleknees', + 'zooblemarble', + 'zooblemash', + 'zooblemonkey', + 'zooblemooch', + 'zooblemouth', + 'zooblemuddle', + 'zooblemuffin', + 'zooblemush', + 'zooblenerd', + 'zooblenoodle', + 'zooblenose', + 'zooblenugget', + 'zooblephew', + 'zooblephooey', + 'zooblepocket', + 'zooblepoof', + 'zooblepop', + 'zooblepounce', + 'zooblepow', + 'zooblepretzel', + 'zooblequack', + 'zoobleroni', + 'zooblescooter', + 'zooblescreech', + 'zooblesmirk', + 'zooblesnooker', + 'zooblesnoop', + 'zooblesnout', + 'zooblesocks', + 'zooblespeed', + 'zooblespinner', + 'zooblesplat', + 'zooblesprinkles', + 'zooblesticks', + 'zooblestink', + 'zoobleswirl', + 'zoobleteeth', + 'zooblethud', + 'zoobletoes', + 'zoobleton', + 'zoobletoon', + 'zoobletooth', + 'zoobletwist', + 'zooblewhatsit', + 'zooblewhip', + 'zooblewig', + 'zooblewoof', + 'zooblezaner', + 'zooblezap', + 'zooblezapper', + 'zooblezilla', + 'zooblezoom', + 'zooks', + 'zoological', + 'zoology', + 'zoom', + 'zoos', + 'zoot', + 'zorna', + 'zorro', + 'zowie', + 'zoza', + 'zozane', + 'zozanero', + 'zulu', + 'zurg', + 'zuzu', + 'zydeco', + 'zyra', + 'zyrdrake', + 'zyrgazelle', + 'zyyk', + 'zzz', + 'zzzzzs', +] \ No newline at end of file diff --git a/otp/chat/__init__.py b/otp/chat/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/otp/distributed/Account.py b/otp/distributed/Account.py new file mode 100755 index 00000000..c0ff825b --- /dev/null +++ b/otp/distributed/Account.py @@ -0,0 +1,6 @@ +from direct.distributed.DistributedObject import DistributedObject + +class Account(DistributedObject): + + def __init__(self, cr): + DistributedObject.__init__(self, cr) diff --git a/otp/distributed/AccountAI.py b/otp/distributed/AccountAI.py new file mode 100755 index 00000000..939fe0d8 --- /dev/null +++ b/otp/distributed/AccountAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI + +class AccountAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("AccountAI") diff --git a/otp/distributed/AccountUD.py b/otp/distributed/AccountUD.py new file mode 100755 index 00000000..99adbece --- /dev/null +++ b/otp/distributed/AccountUD.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectUD import DistributedObjectUD + +class AccountUD(DistributedObjectUD): + notify = DirectNotifyGlobal.directNotify.newCategory("AccountUD") diff --git a/otp/distributed/DistributedDirectory.py b/otp/distributed/DistributedDirectory.py new file mode 100755 index 00000000..4863db7e --- /dev/null +++ b/otp/distributed/DistributedDirectory.py @@ -0,0 +1,4 @@ +from direct.distributed.DistributedObject import DistributedObject + +class DistributedDirectory(DistributedObject): + pass diff --git a/otp/distributed/DistributedDirectoryAI.py b/otp/distributed/DistributedDirectoryAI.py new file mode 100755 index 00000000..e94b78ef --- /dev/null +++ b/otp/distributed/DistributedDirectoryAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI + +class DistributedDirectoryAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedDirectoryAI") diff --git a/otp/distributed/DistributedDistrict.py b/otp/distributed/DistributedDistrict.py new file mode 100755 index 00000000..8ef8d75e --- /dev/null +++ b/otp/distributed/DistributedDistrict.py @@ -0,0 +1,33 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.distributed.DistributedObject import DistributedObject + +class DistributedDistrict(DistributedObject): + notify = directNotify.newCategory('DistributedDistrict') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + self.name = 'NotGiven' + self.available = 0 + self.avatarCount = 0 + + def announceGenerate(self): + DistributedObject.announceGenerate(self) + self.cr.activeDistrictMap[self.doId] = self + messenger.send('shardInfoUpdated') + + def delete(self): + if base.cr.distributedDistrict is self: + base.cr.distributedDistrict = None + if self.doId in self.cr.activeDistrictMap: + del self.cr.activeDistrictMap[self.doId] + DistributedObject.delete(self) + messenger.send('shardInfoUpdated') + + def setAvailable(self, available): + self.available = available + messenger.send('shardInfoUpdated') + + def setName(self, name): + self.name = name + messenger.send('shardInfoUpdated') diff --git a/otp/distributed/DistributedDistrictAI.py b/otp/distributed/DistributedDistrictAI.py new file mode 100755 index 00000000..c80b2ac3 --- /dev/null +++ b/otp/distributed/DistributedDistrictAI.py @@ -0,0 +1,34 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.distributed.DistributedObjectAI import DistributedObjectAI + +class DistributedDistrictAI(DistributedObjectAI): + notify = directNotify.newCategory('DistributedDistrictAI') + + name = 'District' + available = 0 + + def setName(self, name): + self.name = name + + def d_setName(self, name): + self.sendUpdate('setName', [name]) + + def b_setName(self, name): + self.setName(name) + self.d_setName(name) + + def getName(self): + return self.name + + def setAvailable(self, available): + self.available = available + + def d_setAvailable(self, available): + self.sendUpdate('setAvailable', [available]) + + def b_setAvailable(self, available): + self.setAvailable(available) + self.d_setAvailable(available) + + def getAvailable(self): + return self.available diff --git a/otp/distributed/DistributedDistrictUD.py b/otp/distributed/DistributedDistrictUD.py new file mode 100755 index 00000000..8ed7d0ce --- /dev/null +++ b/otp/distributed/DistributedDistrictUD.py @@ -0,0 +1,11 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectUD import DistributedObjectUD + +class DistributedDistrictUD(DistributedObjectUD): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedDistrictUD") + + def setName(self, todo0): + pass + + def setAvailable(self, todo0): + pass diff --git a/otp/distributed/OTPClientRepository.py b/otp/distributed/OTPClientRepository.py new file mode 100755 index 00000000..292af53d --- /dev/null +++ b/otp/distributed/OTPClientRepository.py @@ -0,0 +1,1423 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.distributed import DistributedSmoothNode +from direct.distributed.ClientRepositoryBase import ClientRepositoryBase +from direct.distributed.MsgTypes import * +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.PyDatagramIterator import PyDatagramIterator +from direct.fsm.ClassicFSM import ClassicFSM +from direct.fsm.State import State +from direct.gui.DirectGui import * +from direct.task import Task +from panda3d.core import * +from otp.avatar import Avatar, DistributedAvatar +from otp.avatar.DistributedPlayer import DistributedPlayer +from otp.distributed import OtpDoGlobals +from otp.distributed.OtpDoGlobals import * +from otp.distributed.TelemetryLimiter import TelemetryLimiter +from otp.otpbase import OTPGlobals, OTPLocalizer +from otp.otpgui import OTPDialog +from otp.nametag.NametagConstants import * +import sys, time, types, random +import __builtin__ + +class OTPClientRepository(ClientRepositoryBase): + notify = directNotify.newCategory('OTPClientRepository') + avatarLimit = 6 + + def __init__(self, serverVersion, playGame = None): + ClientRepositoryBase.__init__(self) + self.handler = None + self.__currentAvId = 0 + self.createAvatarClass = None + self.systemMessageSfx = None + self.playToken = launcher.getPlayToken() + + self.parentMgr.registerParent(OTPGlobals.SPRender, base.render) + self.parentMgr.registerParent(OTPGlobals.SPHidden, NodePath()) + self.timeManager = None + + self._proactiveLeakChecks = config.GetBool('crash-on-proactive-leak-detect', 1) + self.activeDistrictMap = {} + self.telemetryLimiter = TelemetryLimiter() + self.serverVersion = serverVersion + self.waitingForDatabase = None + self.loginFSM = ClassicFSM('loginFSM', [ + State('loginOff', + self.enterLoginOff, + self.exitLoginOff, [ + 'connect']), + State('connect', + self.enterConnect, + self.exitConnect, [ + 'noConnection', + 'login', + 'failedToConnect']), + State('login', + self.enterLogin, + self.exitLogin, [ + 'noConnection', + 'waitForGameList', + 'reject', + 'failedToConnect', + 'shutdown']), + State('failedToConnect', + self.enterFailedToConnect, + self.exitFailedToConnect, [ + 'connect', + 'shutdown']), + State('shutdown', + self.enterShutdown, + self.exitShutdown, [ + 'loginOff']), + State('waitForGameList', + self.enterWaitForGameList, + self.exitWaitForGameList, [ + 'noConnection', + 'waitForShardList']), + State('waitForShardList', + self.enterWaitForShardList, + self.exitWaitForShardList, [ + 'noConnection', + 'waitForAvatarList', + 'noShards']), + State('noShards', + self.enterNoShards, + self.exitNoShards, [ + 'noConnection', + 'noShardsWait', + 'shutdown']), + State('noShardsWait', + self.enterNoShardsWait, + self.exitNoShardsWait, [ + 'noConnection', + 'waitForShardList', + 'shutdown']), + State('reject', + self.enterReject, + self.exitReject, []), + State('noConnection', + self.enterNoConnection, + self.exitNoConnection, [ + 'login', + 'connect', + 'shutdown']), + State('afkTimeout', + self.enterAfkTimeout, + self.exitAfkTimeout, [ + 'waitForAvatarList', + 'shutdown']), + State('waitForAvatarList', + self.enterWaitForAvatarList, + self.exitWaitForAvatarList, [ + 'noConnection', + 'chooseAvatar', + 'shutdown']), + State('chooseAvatar', + self.enterChooseAvatar, + self.exitChooseAvatar, [ + 'noConnection', + 'createAvatar', + 'waitForAvatarList', + 'waitForSetAvatarResponse', + 'waitForDeleteAvatarResponse', + 'shutdown', + 'login']), + State('createAvatar', + self.enterCreateAvatar, + self.exitCreateAvatar, [ + 'noConnection', + 'chooseAvatar', + 'waitForSetAvatarResponse', + 'shutdown']), + State('waitForDeleteAvatarResponse', + self.enterWaitForDeleteAvatarResponse, + self.exitWaitForDeleteAvatarResponse, [ + 'noConnection', + 'chooseAvatar', + 'shutdown']), + State('rejectRemoveAvatar', + self.enterRejectRemoveAvatar, + self.exitRejectRemoveAvatar, [ + 'noConnection', + 'chooseAvatar', + 'shutdown']), + State('waitForSetAvatarResponse', + self.enterWaitForSetAvatarResponse, + self.exitWaitForSetAvatarResponse, [ + 'noConnection', + 'playingGame', + 'shutdown']), + State('playingGame', + self.enterPlayingGame, + self.exitPlayingGame, [ + 'noConnection', + 'waitForAvatarList', + 'login', + 'shutdown', + 'afkTimeout', + 'noShards'])], + 'loginOff', 'loginOff') + self.gameFSM = ClassicFSM('gameFSM', [ + State('gameOff', + self.enterGameOff, + self.exitGameOff, [ + 'waitOnEnterResponses']), + State('waitOnEnterResponses', + self.enterWaitOnEnterResponses, + self.exitWaitOnEnterResponses, [ + 'playGame', + 'tutorialQuestion', + 'gameOff']), + State('tutorialQuestion', + self.enterTutorialQuestion, + self.exitTutorialQuestion, [ + 'playGame', + 'gameOff']), + State('playGame', + self.enterPlayGame, + self.exitPlayGame, [ + 'gameOff', + 'closeShard', + 'switchShards']), + State('switchShards', + self.enterSwitchShards, + self.exitSwitchShards, [ + 'gameOff', + 'waitOnEnterResponses']), + State('closeShard', + self.enterCloseShard, + self.exitCloseShard, [ + 'gameOff', + 'waitOnEnterResponses'])], + 'gameOff', 'gameOff') + self.loginFSM.getStateNamed('playingGame').addChild(self.gameFSM) + self.loginFSM.enterInitialState() + self.music = None + self.gameDoneEvent = 'playGameDone' + self.playGame = playGame(self.gameFSM, self.gameDoneEvent) + self.shardListHandle = None + self.uberZoneInterest = None + + self.__pendingGenerates = {} + self.__pendingMessages = {} + self.__doId2pendingInterest = {} + + self.chatAgent = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_CHAT_MANAGER, 'ChatAgent') + self.csm = None # To be set by subclass. + + def hasPlayToken(): + return self.playToken != None + + def readDCFile(self, dcFileNames=None): + dcFile = self.getDcFile() + dcFile.clear() + self.dclassesByName = {} + self.dclassesByNumber = {} + self.hashVal = 0 + + if isinstance(dcFileNames, types.StringTypes): + # If we were given a single string, make it a list. + dcFileNames = [dcFileNames] + + dcImports = {} + if dcFileNames == None: + try: + # For Nirai + readResult = dcFile.read(dcStream, '__dc__') + del __builtin__.dcStream + + except NameError: + readResult = dcFile.readAll() + + if not readResult: + self.notify.error("Could not read dc file.") + + else: + searchPath = getModelPath().getValue() + for dcFileName in dcFileNames: + pathname = Filename(dcFileName) + vfs.resolveFilename(pathname, searchPath) + readResult = dcFile.read(pathname) + if not readResult: + self.notify.error("Could not read dc file: %s" % (pathname)) + + self.hashVal = dcFile.getHash() + + # Now import all of the modules required by the DC file. + for n in xrange(dcFile.getNumImportModules()): + moduleName = dcFile.getImportModule(n)[:] + + # Maybe the module name is represented as "moduleName/AI". + suffix = moduleName.split('/') + moduleName = suffix[0] + suffix=suffix[1:] + if self.dcSuffix in suffix: + moduleName += self.dcSuffix + elif self.dcSuffix == 'UD' and 'AI' in suffix: #HACK: + moduleName += 'AI' + + importSymbols = [] + for i in xrange(dcFile.getNumImportSymbols(n)): + symbolName = dcFile.getImportSymbol(n, i) + + # Maybe the symbol name is represented as "symbolName/AI". + suffix = symbolName.split('/') + symbolName = suffix[0] + suffix=suffix[1:] + if self.dcSuffix in suffix: + symbolName += self.dcSuffix + elif self.dcSuffix == 'UD' and 'AI' in suffix: #HACK: + symbolName += 'AI' + + importSymbols.append(symbolName) + + self.importModule(dcImports, moduleName, importSymbols) + + # Now get the class definition for the classes named in the DC + # file. + for i in xrange(dcFile.getNumClasses()): + dclass = dcFile.getClass(i) + number = dclass.getNumber() + className = dclass.getName() + self.dcSuffix + + # Does the class have a definition defined in the newly + # imported namespace? + classDef = dcImports.get(className) + if classDef is None and self.dcSuffix == 'UD': #HACK: + className = dclass.getName() + 'AI' + classDef = dcImports.get(className) + + # Also try it without the dcSuffix. + if classDef == None: + className = dclass.getName() + classDef = dcImports.get(className) + if classDef is None: + self.notify.debug("No class definition for %s." % (className)) + else: + if type(classDef) == types.ModuleType: + if not hasattr(classDef, className): + self.notify.warning("Module %s does not define class %s." % (className, className)) + continue + classDef = getattr(classDef, className) + + if type(classDef) != types.ClassType and type(classDef) != types.TypeType: + self.notify.error("Symbol %s is not a class name." % (className)) + else: + dclass.setClassDef(classDef) + + self.dclassesByName[className] = dclass + if number >= 0: + self.dclassesByNumber[number] = dclass + + # Owner Views + if self.hasOwnerView(): + ownerDcSuffix = self.dcSuffix + 'OV' + # dict of class names (without 'OV') that have owner views + ownerImportSymbols = {} + + # Now import all of the modules required by the DC file. + for n in xrange(dcFile.getNumImportModules()): + moduleName = dcFile.getImportModule(n) + + # Maybe the module name is represented as "moduleName/AI". + suffix = moduleName.split('/') + moduleName = suffix[0] + suffix=suffix[1:] + if ownerDcSuffix in suffix: + moduleName = moduleName + ownerDcSuffix + + importSymbols = [] + for i in xrange(dcFile.getNumImportSymbols(n)): + symbolName = dcFile.getImportSymbol(n, i) + + # Check for the OV suffix + suffix = symbolName.split('/') + symbolName = suffix[0] + suffix=suffix[1:] + if ownerDcSuffix in suffix: + symbolName += ownerDcSuffix + importSymbols.append(symbolName) + ownerImportSymbols[symbolName] = None + + self.importModule(dcImports, moduleName, importSymbols) + + # Now get the class definition for the owner classes named + # in the DC file. + for i in xrange(dcFile.getNumClasses()): + dclass = dcFile.getClass(i) + if ((dclass.getName()+ownerDcSuffix) in ownerImportSymbols): + number = dclass.getNumber() + className = dclass.getName() + ownerDcSuffix + + # Does the class have a definition defined in the newly + # imported namespace? + classDef = dcImports.get(className) + if classDef is None: + self.notify.error("No class definition for %s." % className) + else: + if type(classDef) == types.ModuleType: + if not hasattr(classDef, className): + self.notify.error("Module %s does not define class %s." % (className, className)) + classDef = getattr(classDef, className) + dclass.setOwnerClassDef(classDef) + self.dclassesByName[className] = dclass + + def getGameDoId(self): + return self.GameGlobalsId + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterLoginOff(self): + self.handler = self.handleMessageType + self.shardListHandle = None + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitLoginOff(self): + self.handler = None + return + + def getServerVersion(self): + return self.serverVersion + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterConnect(self, serverList): + self.serverList = serverList + dialogClass = OTPGlobals.getGlobalDialogClass() + self.connectingBox = dialogClass(message=OTPLocalizer.CRConnecting) + self.connectingBox.show() + self.renderFrame() + self.handler = self.handleConnecting + self.connect(self.serverList, successCallback=self._sendHello, failureCallback=self.failedToConnect) + + def _sendHello(self): + datagram = PyDatagram() + datagram.addUint16(CLIENT_HELLO) + datagram.addUint32(self.hashVal) + datagram.addString(self.serverVersion) + self.send(datagram) + + def handleConnecting(self, msgtype, di): + if msgtype == CLIENT_HELLO_RESP: + self._handleConnected() + else: + self.handleMessageType(msgtype, di) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def failedToConnect(self, statusCode, statusString): + self.loginFSM.request('failedToConnect', [statusCode, statusString]) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitConnect(self): + self.connectingBox.cleanup() + del self.connectingBox + + def handleSystemMessage(self, di): + message = ClientRepositoryBase.handleSystemMessage(self, di) + whisper = WhisperPopup(message, OTPGlobals.getInterfaceFont(), WTSystem) + whisper.manage(base.marginManager) + if not self.systemMessageSfx: + self.systemMessageSfx = base.loadSfx('phase_3/audio/sfx/clock03.ogg') + if self.systemMessageSfx: + base.playSfx(self.systemMessageSfx) + + def getConnectedEvent(self): + return 'OTPClientRepository-connected' + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def _handleConnected(self): + launcher.setDisconnectDetailsNormal() + messenger.send(self.getConnectedEvent()) + self.gotoFirstScreen() + + def gotoFirstScreen(self): + self.startReaderPollTask() + #self.startHeartbeat() + self.loginFSM.request('login') + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterLogin(self): + self.sendSetAvatarIdMsg(0) + self.loginDoneEvent = 'loginDone' + self.accept(self.loginDoneEvent, self.__handleLoginDone) + self.csm.performLogin(self.loginDoneEvent) + self.waitForDatabaseTimeout(requestName='WaitOnCSMLoginResponse') + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def __handleLoginDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'success': + self.loginFSM.request('waitForGameList') + elif mode == 'reject': + self.loginFSM.request('reject') + elif mode == 'quit': + self.loginFSM.request('shutdown') + elif mode == 'failure': + self.loginFSM.request('failedToConnect', [-1, '?']) + else: + self.notify.error('Invalid doneStatus mode from ClientServicesManager: ' + str(mode)) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitLogin(self): + self.cleanupWaitingForDatabase() + self.ignore(self.loginDoneEvent) + del self.loginDoneEvent + self.handler = None + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterFailedToConnect(self, statusCode, statusString): + self.handler = self.handleMessageType + messenger.send('connectionIssue') + url = self.serverList[0] + self.notify.warning('Failed to connect to %s (%s %s). Notifying user.' % (url.cStr(), statusCode, statusString)) + dialogClass = OTPGlobals.getGlobalDialogClass() + self.failedToConnectBox = dialogClass(message=OTPLocalizer.CRNoConnectTryAgain % (url.getServer(), url.getPort()), doneEvent='failedToConnectAck', text_wordwrap=18, style=OTPDialog.TwoChoice) + self.failedToConnectBox.show() + self.accept('failedToConnectAck', self.__handleFailedToConnectAck) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def __handleFailedToConnectAck(self): + doneStatus = self.failedToConnectBox.doneStatus + if doneStatus == 'ok': + self.loginFSM.request('connect', [self.serverList]) + messenger.send('connectionRetrying') + elif doneStatus == 'cancel': + self.loginFSM.request('shutdown') + else: + self.notify.error('Unrecognized doneStatus: ' + str(doneStatus)) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitFailedToConnect(self): + self.handler = None + self.ignore('failedToConnectAck') + self.failedToConnectBox.cleanup() + del self.failedToConnectBox + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterShutdown(self, errorCode = None): + self.handler = self.handleMessageType + self.sendDisconnect() + self.notify.info('Exiting cleanly') + base.exitShow(errorCode) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitShutdown(self): + self.handler = None + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterWaitForGameList(self): + self.gameDoDirectory = self.addTaggedInterest(self.GameGlobalsId, OTP_ZONE_ID_MANAGEMENT, self.ITAG_PERM, 'game directory', event='GameList_Complete') + self.acceptOnce('GameList_Complete', self.loginFSM.request, ['waitForShardList']) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitWaitForGameList(self): + self.handler = None + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterWaitForShardList(self): + if not self.isValidInterestHandle(self.shardListHandle): + self.shardListHandle = self.addTaggedInterest(self.GameGlobalsId, OTP_ZONE_ID_DISTRICTS, self.ITAG_PERM, 'LocalShardList', event='ShardList_Complete') + self.acceptOnce('ShardList_Complete', self._wantShardListComplete) + else: + self._wantShardListComplete() + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def _wantShardListComplete(self): + if self._shardsAreReady(): + self.loginFSM.request('waitForAvatarList') + else: + self.loginFSM.request('noShards') + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def _shardsAreReady(self): + maxPop = config.GetInt('shard-mid-pop', 300) + for shard in self.activeDistrictMap.values(): + if shard.available: + if shard.avatarCount < maxPop: + return True + else: + return False + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitWaitForShardList(self): + self.ignore('ShardList_Complete') + self.handler = None + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterNoShards(self): + messenger.send('connectionIssue') + self.handler = self.handleMessageType + dialogClass = OTPGlobals.getGlobalDialogClass() + self.noShardsBox = dialogClass(message=OTPLocalizer.CRNoDistrictsTryAgain, doneEvent='noShardsAck', style=OTPDialog.TwoChoice) + self.noShardsBox.show() + self.accept('noShardsAck', self.__handleNoShardsAck) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def __handleNoShardsAck(self): + doneStatus = self.noShardsBox.doneStatus + if doneStatus == 'ok': + messenger.send('connectionRetrying') + self.loginFSM.request('noShardsWait') + elif doneStatus == 'cancel': + self.loginFSM.request('shutdown') + else: + self.notify.error('Unrecognized doneStatus: ' + str(doneStatus)) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitNoShards(self): + self.handler = None + self.ignore('noShardsAck') + self.noShardsBox.cleanup() + del self.noShardsBox + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterNoShardsWait(self): + dialogClass = OTPGlobals.getGlobalDialogClass() + self.connectingBox = dialogClass(message=OTPLocalizer.CRConnecting) + self.connectingBox.show() + self.renderFrame() + self.noShardsWaitTaskName = 'noShardsWait' + + def doneWait(task, self = self): + self.loginFSM.request('waitForShardList') + + delay = 6.5 + random.random() * 2.0 + taskMgr.doMethodLater(delay, doneWait, self.noShardsWaitTaskName) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitNoShardsWait(self): + taskMgr.remove(self.noShardsWaitTaskName) + del self.noShardsWaitTaskName + self.connectingBox.cleanup() + del self.connectingBox + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterReject(self): + self.handler = self.handleMessageType + self.notify.warning('Connection Rejected') + sys.exit() + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitReject(self): + self.handler = None + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterNoConnection(self): + messenger.send('connectionIssue') + self.resetInterestStateForConnectionLoss() + self.shardListHandle = None + self.handler = self.handleMessageType + self.__currentAvId = 0 + self.stopHeartbeat() + self.stopReaderPollTask() + if (self.bootedIndex is not None) and (self.bootedIndex in OTPLocalizer.CRBootedReasons): + message = OTPLocalizer.CRBootedReasons[self.bootedIndex] + elif self.bootedIndex == 155: + message = self.bootedText + elif self.bootedText is not None: + message = OTPLocalizer.CRBootedReasonUnknownCode % self.bootedIndex + else: + message = OTPLocalizer.CRLostConnection + reconnect = 1 + if self.bootedIndex in (152, 127, 124, 101, 102, 103): + reconnect = 0 + if self.bootedIndex == 152: + message = message % {'name': self.bootedText} + launcher.setDisconnectDetails(self.bootedIndex, message) + style = OTPDialog.Acknowledge + if reconnect: + message += OTPLocalizer.CRTryConnectAgain + style = OTPDialog.TwoChoice + dialogClass = OTPGlobals.getGlobalDialogClass() + self.lostConnectionBox = dialogClass(doneEvent='lostConnectionAck', message=message, text_wordwrap=18, style=style) + self.lostConnectionBox.show() + self.accept('lostConnectionAck', self.__handleLostConnectionAck) + self.notify.warning('Lost connection to server. Notifying user.') + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def __handleLostConnectionAck(self): + if self.lostConnectionBox.doneStatus == 'ok': + self.loginFSM.request('connect', [self.serverList]) + else: + self.loginFSM.request('shutdown') + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitNoConnection(self): + self.handler = None + self.ignore('lostConnectionAck') + self.lostConnectionBox.cleanup() + messenger.send('connectionRetrying') + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterAfkTimeout(self): + self.sendSetAvatarIdMsg(0) + msg = OTPLocalizer.AfkForceAcknowledgeMessage + dialogClass = OTPGlobals.getDialogClass() + self.afkDialog = dialogClass(text=msg, command=self.__handleAfkOk, style=OTPDialog.Acknowledge) + self.handler = self.handleMessageType + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def __handleAfkOk(self, value): + self.loginFSM.request('waitForAvatarList') + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitAfkTimeout(self): + if self.afkDialog: + self.afkDialog.cleanup() + self.afkDialog = None + self.handler = None + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterWaitForAvatarList(self): + self._requestAvatarList() + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def _requestAvatarList(self): + self.csm.requestAvatars() + self.waitForDatabaseTimeout(requestName='WaitForAvatarList') + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitWaitForAvatarList(self): + self.cleanupWaitingForDatabase() + self.handler = None + return + + def handleAvatarsList(self, avatars): + self.avList = avatars + self.loginFSM.request('chooseAvatar', [self.avList]) + + def handleChatSettings(self, chatSettings): + self.chatSettings = chatSettings + + def wantSpeedchatPlus(self): + return self.chatSettings[0] + + def wantTrueFriends(self): + return self.chatSettings[1] + + def wantTypedChat(self): + return self.wantSpeedchatPlus() or self.wantTrueFriends() + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterChooseAvatar(self, avList): + pass + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitChooseAvatar(self): + pass + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterCreateAvatar(self, avList, index, newDNA = None): + pass + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitCreateAvatar(self): + pass + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterWaitForDeleteAvatarResponse(self, potAv): + self.csm.sendDeleteAvatar(potAv.id) + self.waitForDatabaseTimeout(requestName='WaitForDeleteAvatarResponse') + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitWaitForDeleteAvatarResponse(self): + self.cleanupWaitingForDatabase() + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterRejectRemoveAvatar(self, reasonCode): + self.notify.warning('Rejected removed avatar. (%s)' % (reasonCode,)) + self.handler = self.handleMessageType + dialogClass = OTPGlobals.getGlobalDialogClass() + self.rejectRemoveAvatarBox = dialogClass(message='%s\n(%s)' % (OTPLocalizer.CRRejectRemoveAvatar, reasonCode), doneEvent='rejectRemoveAvatarAck', style=OTPDialog.Acknowledge) + self.rejectRemoveAvatarBox.show() + self.accept('rejectRemoveAvatarAck', self.__handleRejectRemoveAvatar) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def __handleRejectRemoveAvatar(self): + self.loginFSM.request('chooseAvatar') + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitRejectRemoveAvatar(self): + self.handler = None + self.ignore('rejectRemoveAvatarAck') + self.rejectRemoveAvatarBox.cleanup() + del self.rejectRemoveAvatarBox + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterWaitForSetAvatarResponse(self, potAv): + self.sendSetAvatarMsg(potAv) + self.waitForDatabaseTimeout(requestName='WaitForSetAvatarResponse') + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitWaitForSetAvatarResponse(self): + self.cleanupWaitingForDatabase() + self.handler = None + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def sendSetAvatarMsg(self, potAv): + self.sendSetAvatarIdMsg(potAv.id) + self.avData = potAv + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def sendSetAvatarIdMsg(self, avId): + if avId != self.__currentAvId: + self.__currentAvId = avId + self.csm.sendChooseAvatar(avId) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def handleAvatarResponseMsg(self, avatarId, di): + pass + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterPlayingGame(self): + pass + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitPlayingGame(self): + self.notify.info('sending clientLogout') + messenger.send('clientLogout') + + def _abandonShard(self): + self.notify.error('%s must override _abandonShard' % self.__class__.__name__) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterGameOff(self): + self.uberZoneInterest = None + if not hasattr(self, 'cleanGameExit'): + self.cleanGameExit = True + if self.cleanGameExit: + if self.isShardInterestOpen(): + self.notify.error('enterGameOff: shard interest is still open') + elif self.isShardInterestOpen(): + self.notify.warning('unclean exit, abandoning shard') + self._abandonShard() + self.cleanupWaitAllInterestsComplete() + del self.cleanGameExit + self.cache.flush() + self.doDataCache.flush() + self.handler = self.handleMessageType + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitGameOff(self): + self.handler = None + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterWaitOnEnterResponses(self, shardId, hoodId, zoneId, avId): + self.cleanGameExit = False + self.handlerArgs = {'hoodId': hoodId, + 'zoneId': zoneId, + 'avId': avId} + if shardId is not None: + district = self.activeDistrictMap.get(shardId) + else: + district = None + if not district: + self.distributedDistrict = self.getStartingDistrict() + if not self.distributedDistrict: + self.loginFSM.request('noShards') + return + shardId = self.distributedDistrict.doId + else: + self.distributedDistrict = district + self.notify.info('Entering shard %s' % shardId) + localAvatar.setLocation(shardId, zoneId) + base.localAvatar.defaultShard = shardId + self.waitForDatabaseTimeout(requestName='WaitOnEnterResponses') + self.handleSetShardComplete() + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def handleSetShardComplete(self): + hoodId = self.handlerArgs['hoodId'] + zoneId = self.handlerArgs['zoneId'] + avId = self.handlerArgs['avId'] + self.uberZoneInterest = self.addInterest(base.localAvatar.defaultShard, OTPGlobals.UberZone, 'uberZone', 'uberZoneInterestComplete') + self.acceptOnce('uberZoneInterestComplete', self.uberZoneInterestComplete) + self.waitForDatabaseTimeout(20, requestName='waitingForUberZone') + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def uberZoneInterestComplete(self): + self.__gotTimeSync = 0 + self.cleanupWaitingForDatabase() + if self.timeManager == None: + self.notify.info('TimeManager is not present.') + DistributedSmoothNode.globalActivateSmoothing(0, 0) + self.gotTimeSync() + else: + DistributedSmoothNode.globalActivateSmoothing(1, 0) + if self.timeManager.synchronize('startup'): + self.accept('gotTimeSync', self.gotTimeSync) + self.waitForDatabaseTimeout(requestName='uberZoneInterest-timeSync') + else: + self.notify.info('No sync from TimeManager.') + self.gotTimeSync() + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitWaitOnEnterResponses(self): + self.ignore('uberZoneInterestComplete') + self.cleanupWaitingForDatabase() + self.handler = None + self.handlerArgs = None + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterCloseShard(self, loginState = None): + self.notify.info('Exiting shard') + if loginState is None: + loginState = 'waitForAvatarList' + self._closeShardLoginState = loginState + base.cr.setNoNewInterests(True) + return + + def _removeLocalAvFromStateServer(self): + self.sendSetAvatarIdMsg(0) + self._removeAllOV() + self.removeShardInterest(Functor(self.loginFSM.request, self._closeShardLoginState)) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def _removeAllOV(self): + ownerDoIds = self.doId2ownerView.keys() + for doId in ownerDoIds: + self.disableDoId(doId, ownerView=True) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def isShardInterestOpen(self): + self.notify.error('%s must override isShardInterestOpen' % self.__class__.__name__) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def removeShardInterest(self, callback, task = None): + self._removeCurrentShardInterest(Functor(self._removeShardInterestComplete, callback)) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def _removeShardInterestComplete(self, callback): + self.cleanGameExit = True + self.cache.flush() + self.doDataCache.flush() + callback() + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def _removeCurrentShardInterest(self, callback): + self.notify.error('%s must override _removeCurrentShardInterest' % self.__class__.__name__) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitCloseShard(self): + del self._closeShardLoginState + base.cr.setNoNewInterests(False) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterTutorialQuestion(self, hoodId, zoneId, avId): + pass + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitTutorialQuestion(self): + pass + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterPlayGame(self, hoodId, zoneId, avId): + if self.music: + self.music.stop() + self.music = None + self.handler = self.handlePlayGame + self.accept(self.gameDoneEvent, self.handleGameDone) + base.transitions.noFade() + self.playGame.load() + try: + loader.endBulkLoad('localAvatarPlayGame') + except: + pass + + self.playGame.enter(hoodId, zoneId, avId) + + def checkScale(task): + return Task.cont + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def handleGameDone(self): + if self.timeManager: + self.timeManager.setDisconnectReason(OTPGlobals.DisconnectSwitchShards) + doneStatus = self.playGame.getDoneStatus() + how = doneStatus['how'] + shardId = doneStatus['shardId'] + hoodId = doneStatus['hoodId'] + zoneId = doneStatus['zoneId'] + avId = doneStatus['avId'] + if how == 'teleportIn': + self.gameFSM.request('switchShards', [shardId, + hoodId, + zoneId, + avId]) + else: + self.notify.error('Exited shard with unexpected mode %s' % how) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitPlayGame(self): + taskMgr.remove('globalScaleCheck') + self.handler = None + self.playGame.exit() + self.playGame.unload() + self.ignore(self.gameDoneEvent) + return + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def gotTimeSync(self): + self.notify.info('gotTimeSync') + self.ignore('gotTimeSync') + self.__gotTimeSync = 1 + self.moveOnFromUberZone() + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def moveOnFromUberZone(self): + if not self.__gotTimeSync: + self.notify.info('Waiting for time sync.') + return + hoodId = self.handlerArgs['hoodId'] + zoneId = self.handlerArgs['zoneId'] + avId = self.handlerArgs['avId'] + + if not self.SupportTutorial or base.localAvatar.tutorialAck: + self.gameFSM.request('playGame', [hoodId, zoneId, avId]) + return + if base.config.GetBool('force-tutorial', 0): + self.gameFSM.request('tutorialQuestion', [hoodId, zoneId, avId]) + return + else: + if hasattr(self, 'skipTutorialRequest') and self.skipTutorialRequest: + self.skipTutorialRequest = None + self.gameFSM.request('skipTutorialRequest', [hoodId, zoneId, avId]) + return + else: + self.gameFSM.request('tutorialQuestion', [hoodId, zoneId, avId]) + return + + self.gameFSM.request('playGame', [hoodId, zoneId, avId]) + + def handlePlayGame(self, msgType, di): + if self.notify.getDebug(): + self.notify.debug('handle play game got message type: ' + `msgType`) + if self.__recordObjectMessage(msgType, di): + return + if msgType == CLIENT_ENTER_OBJECT_REQUIRED: + self.handleGenerateWithRequired(di) + elif msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER: + self.handleGenerateWithRequired(di, other=True) + elif msgType == CLIENT_OBJECT_SET_FIELD: + # TODO: Properly fix this: + try: + self.handleUpdateField(di) + except: + pass + elif msgType == CLIENT_OBJECT_LEAVING: + self.handleDelete(di) + else: + self.handleMessageType(msgType, di) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def enterSwitchShards(self, shardId, hoodId, zoneId, avId): + self._switchShardParams = [shardId, + hoodId, + zoneId, + avId] + localAvatar.setLeftDistrict() + self.removeShardInterest(self._handleOldShardGone) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def _handleOldShardGone(self): + self.gameFSM.request('waitOnEnterResponses', self._switchShardParams) + + @report(types=['args', 'deltaStamp'], dConfigParam='teleport') + def exitSwitchShards(self): + pass + + def getStartingDistrict(self): + if not self.activeDistrictMap: + self.notify.info('no shards') + return + + maxPop = config.GetInt('shard-mid-pop', 300) + preferred = settings.get('preferredShard', None) + + if preferred: + for shard in self.activeDistrictMap.values(): + if shard.available and shard.name == preferred and shard.avatarCount < maxPop: + return shard + + for shard in self.activeDistrictMap.values(): + if shard.available and shard.avatarCount < maxPop: + district = shard + maxPop = district.avatarCount + + if district: + self.notify.debug('chose %s: pop %s' % (district.name, district.avatarCount)) + + return district + + def getShardName(self, shardId): + try: + return self.activeDistrictMap[shardId].name + except: + return None + + return None + + def isShardAvailable(self, shardId): + try: + return self.activeDistrictMap[shardId].available + except: + return 0 + + def listActiveShards(self): + list = [] + + for s in self.activeDistrictMap.values(): + if s.available: + list.append((s.doId, s.name, s.avatarCount, s.invasionStatus, s.groupAvCount)) + + return list + + def getPlayerAvatars(self): + return [i for i in self.doId2do.values() if isinstance(i, DistributedPlayer)] + + def queryObjectField(self, dclassName, fieldName, doId, context = 0): + dclass = self.dclassesByName.get(dclassName) + if dclass is not None: + fieldId = dclass.getFieldByName(fieldName).getNumber() + self.queryObjectFieldId(doId, fieldId, context) + return + + def lostConnection(self): + ClientRepositoryBase.lostConnection(self) + self.loginFSM.request('noConnection') + + def waitForDatabaseTimeout(self, extraTimeout = 0, requestName = 'unknown'): + OTPClientRepository.notify.debug('waiting for database timeout %s at %s' % (requestName, globalClock.getFrameTime())) + self.cleanupWaitingForDatabase() + globalClock.tick() + taskMgr.doMethodLater((OTPGlobals.DatabaseDialogTimeout + extraTimeout) * choice(0, 10, 1), self.__showWaitingForDatabase, 'waitingForDatabase', extraArgs=[requestName]) + + def cleanupWaitingForDatabase(self): + if self.waitingForDatabase: + self.waitingForDatabase.hide() + self.waitingForDatabase.cleanup() + self.waitingForDatabase = None + taskMgr.remove('waitingForDatabase') + return + + def __showWaitingForDatabase(self, requestName): + messenger.send('connectionIssue') + OTPClientRepository.notify.info('timed out waiting for %s at %s' % (requestName, globalClock.getFrameTime())) + dialogClass = OTPGlobals.getDialogClass() + self.waitingForDatabase = dialogClass(text=OTPLocalizer.CRToontownUnavailable, dialogName='WaitingForDatabase', buttonTextList=[OTPLocalizer.CRToontownUnavailableCancel], style=OTPDialog.CancelOnly, command=self.__handleCancelWaiting) + self.waitingForDatabase.show() + taskMgr.remove('waitingForDatabase') + taskMgr.doMethodLater(OTPGlobals.DatabaseGiveupTimeout, self.__giveUpWaitingForDatabase, 'waitingForDatabase', extraArgs=[requestName]) + return Task.done + + def __giveUpWaitingForDatabase(self, requestName): + OTPClientRepository.notify.info('giving up waiting for %s at %s' % (requestName, globalClock.getFrameTime())) + self.cleanupWaitingForDatabase() + self.loginFSM.request('noConnection') + return Task.done + + def __handleCancelWaiting(self, value): + self.loginFSM.request('shutdown') + + def renderFrame(self): + gsg = base.win.getGsg() + if gsg: + render2d.prepareScene(gsg) + base.graphicsEngine.renderFrame() + + def handleMessageType(self, msgType, di): + if self.__recordObjectMessage(msgType, di): + return + if msgType == CLIENT_EJECT: + self.handleGoGetLost(di) + elif msgType == CLIENT_HEARTBEAT: + self.handleServerHeartbeat(di) + elif msgType == CLIENT_ENTER_OBJECT_REQUIRED: + self.handleGenerateWithRequired(di) + elif msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER: + self.handleGenerateWithRequired(di, other=True) + elif msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER_OWNER: + self.handleGenerateWithRequiredOtherOwner(di) + elif msgType == CLIENT_OBJECT_SET_FIELD: + self.handleUpdateField(di) + elif msgType == CLIENT_OBJECT_LEAVING: + self.handleDisable(di) + elif msgType == CLIENT_OBJECT_LEAVING_OWNER: + self.handleDisable(di, ownerView=True) + elif msgType == CLIENT_DONE_INTEREST_RESP: + self.gotInterestDoneMessage(di) + elif msgType == CLIENT_OBJECT_LOCATION: + self.gotObjectLocationMessage(di) + else: + currentLoginState = self.loginFSM.getCurrentState() + if currentLoginState: + currentLoginStateName = currentLoginState.getName() + else: + currentLoginStateName = 'None' + currentGameState = self.gameFSM.getCurrentState() + if currentGameState: + currentGameStateName = currentGameState.getName() + else: + currentGameStateName = 'None' + + def gotInterestDoneMessage(self, di): + if self.deferredGenerates: + dg = Datagram(di.getDatagram()) + di = DatagramIterator(dg, di.getCurrentIndex()) + self.deferredGenerates.append((CLIENT_DONE_INTEREST_RESP, (dg, di))) + else: + di2 = DatagramIterator(di.getDatagram(), di.getCurrentIndex()) + di2.getUint32() # Ignore the context. + handle = di2.getUint16() + self.__playBackGenerates(handle) + + self.handleInterestDoneMessage(di) + + def gotObjectLocationMessage(self, di): + if self.deferredGenerates: + dg = Datagram(di.getDatagram()) + di = DatagramIterator(dg, di.getCurrentIndex()) + di2 = DatagramIterator(dg, di.getCurrentIndex()) + doId = di2.getUint32() + if doId in self.deferredDoIds: + self.deferredDoIds[doId][3].append((CLIENT_OBJECT_LOCATION, (dg, di))) + else: + self.handleObjectLocation(di) + else: + self.handleObjectLocation(di) + + def replayDeferredGenerate(self, msgType, extra): + if msgType == CLIENT_DONE_INTEREST_RESP: + dg, di = extra + self.handleInterestDoneMessage(di) + elif msgType == CLIENT_OBJECT_LOCATION: + dg, di = extra + self.handleObjectLocation(di) + else: + ClientRepositoryBase.replayDeferredGenerate(self, msgType, extra) + + @exceptionLogged(append=False) + def handleDatagram(self, di): + msgType = self.getMsgType() + if msgType == 65535: + self.lostConnection() + return + if self.handler == None: + self.handleMessageType(msgType, di) + else: + self.handler(msgType, di) + self.considerHeartbeat() + return + + def identifyFriend(self, doId): + return self.identifyAvatar(doId) + + def identifyAvatar(self, doId): + info = self.doId2do.get(doId) + + return info if info else self.identifyFriend(doId) + + def sendDisconnect(self): + if self.isConnected(): + datagram = PyDatagram() + datagram.addUint16(CLIENT_DISCONNECT) + self.send(datagram) + self.notify.info('Sent disconnect message to server') + self.disconnect() + self.stopHeartbeat() + + def _isPlayerDclass(self, dclass): + return False + + def _isValidPlayerLocation(self, parentId, zoneId): + return True + + def _isInvalidPlayerAvatarGenerate(self, doId, dclass, parentId, zoneId): + if self._isPlayerDclass(dclass): + if not self._isValidPlayerLocation(parentId, zoneId): + return True + return False + + def handleGenerateWithRequired(self, di, other=False): + doId = di.getUint32() + parentId = di.getUint32() + zoneId = di.getUint32() + classId = di.getUint16() + + # Decide whether we should add this to the interest's pending + # generates, or process it right away: + for handle, interest in self._interests.items(): + if parentId != interest.parentId: + continue + + if isinstance(interest.zoneIdList, list): + if zoneId not in interest.zoneIdList: + continue + else: + if zoneId != interest.zoneIdList: + continue + + break + else: + interest = None + + if (not interest) or (not interest.events): + # This object can be generated right away: + return self.__doGenerate(doId, parentId, zoneId, classId, di, other) + + # This object must be generated when the operation completes: + pending = self.__pendingGenerates.setdefault(handle, []) + pending.append((doId, parentId, zoneId, classId, Datagram(di.getDatagram()), other)) + self.__doId2pendingInterest[doId] = handle + + def __playBackGenerates(self, handle): + if handle not in self.__pendingGenerates: + return + + # This interest has pending generates! Play them: + generates = self.__pendingGenerates[handle] + del self.__pendingGenerates[handle] + generates.sort(key=lambda i: i[3]) # Sort by classId. + for doId, parentId, zoneId, classId, dg, other in generates: + di = DatagramIterator(dg) + di.skipBytes(16) + self.__doGenerate(doId, parentId, zoneId, classId, di, other) + if doId in self.__doId2pendingInterest: + del self.__doId2pendingInterest[doId] + + # Also play back any messages we missed: + self.__playBackMessages(handle) + + def __playBackMessages(self, handle): + if handle not in self.__pendingMessages: + return + + # Any pending messages? Play them: + for dg in self.__pendingMessages[handle]: + di = DatagramIterator(dg) + msgType = di.getUint16() + if self.handler is None: + self.handleMessageType(msgType, di) + else: + self.handler(msgType, di) + + del self.__pendingMessages[handle] + + def __recordObjectMessage(self, msgType, di): + if msgType not in (CLIENT_OBJECT_SET_FIELD, CLIENT_OBJECT_LEAVING, + CLIENT_OBJECT_LOCATION): + return False + + di2 = DatagramIterator(di.getDatagram(), di.getCurrentIndex()) + doId = di2.getUint32() + + if doId not in self.__doId2pendingInterest: + return False + + pending = self.__pendingMessages.setdefault(self.__doId2pendingInterest[doId], []) + pending.append(Datagram(di.getDatagram())) + + return True + + def __doGenerate(self, doId, parentId, zoneId, classId, di, other): + dclass = self.dclassesByNumber[classId] + if self._isInvalidPlayerAvatarGenerate(doId, dclass, parentId, zoneId): + return + dclass.startGenerate() + if other: + self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId) + else: + self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId) + dclass.stopGenerate() + + def handleGenerateWithRequiredOtherOwner(self, di): + doId = di.getUint32() + parentId = di.getUint32() + zoneId = di.getUint32() + classId = di.getUint16() + dclass = self.dclassesByNumber[classId] + dclass.startGenerate() + distObj = self.generateWithRequiredOtherFieldsOwner(dclass, doId, di) + dclass.stopGenerate() + + def handleQuietZoneGenerateWithRequired(self, di): + doId = di.getUint32() + parentId = di.getUint32() + zoneId = di.getUint32() + classId = di.getUint16() + dclass = self.dclassesByNumber[classId] + dclass.startGenerate() + distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId) + dclass.stopGenerate() + + def handleQuietZoneGenerateWithRequiredOther(self, di): + doId = di.getUint32() + parentId = di.getUint32() + zoneId = di.getUint32() + classId = di.getUint16() + dclass = self.dclassesByNumber[classId] + dclass.startGenerate() + distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId) + dclass.stopGenerate() + + def handleDisable(self, di, ownerView = False): + doId = di.getUint32() + if not self.isLocalId(doId): + self.disableDoId(doId, ownerView) + + if doId == self.__currentAvId: + self.bootedIndex = 153 + self.bootedText = '' + + self.notify.warning("Avatar deleted! Closing connection...") + + # disconnect now, don't wait for send/recv to fail + self.stopReaderPollTask() + self.lostConnection() + + def sendSetLocation(self, doId, parentId, zoneId): + datagram = PyDatagram() + datagram.addUint16(CLIENT_OBJECT_LOCATION) + datagram.addUint32(doId) + datagram.addUint32(parentId) + datagram.addUint32(zoneId) + self.send(datagram) + + def sendHeartbeat(self): + datagram = PyDatagram() + datagram.addUint16(CLIENT_HEARTBEAT) + self.send(datagram) + self.lastHeartbeat = globalClock.getRealTime() + self.considerFlush() + + def isLocalId(self, id): + try: + return localAvatar.doId == id + except: + self.notify.debug('In isLocalId(), localAvatar not created yet') + return False + + ITAG_PERM = 'perm' + ITAG_AVATAR = 'avatar' + ITAG_SHARD = 'shard' + ITAG_WORLD = 'world' + ITAG_GAME = 'game' + + def addTaggedInterest(self, parentId, zoneId, mainTag, desc, otherTags = [], event = None): + return self.addInterest(parentId, zoneId, desc, event) diff --git a/otp/distributed/OtpDoGlobals.py b/otp/distributed/OtpDoGlobals.py new file mode 100755 index 00000000..fbf3ca12 --- /dev/null +++ b/otp/distributed/OtpDoGlobals.py @@ -0,0 +1,14 @@ +from direct.distributed.MsgTypes import * +OTP_DO_ID_FRIEND_MANAGER = 4501 +OTP_DO_ID_TOONTOWN = 1337 +OTP_DO_ID_CLIENT_SERVICES_MANAGER = 4665 +OTP_DO_ID_TTS_FRIENDS_MANAGER = 4666 +OTP_DO_ID_GLOBAL_PARTY_MANAGER = 4477 +OTP_DO_ID_GLOBAL_LOBBY_MANAGER = 4478 +OTP_DO_ID_CHAT_MANAGER = 4681 +OTP_ZONE_ID_MANAGEMENT = 2 +OTP_ZONE_ID_DISTRICTS = 3 +OTP_ZONE_ID_DISTRICTS_STATS = 4 +OTP_MOD_CHANNEL = 6200 +OTP_ADMIN_CHANNEL = 6400 +OTP_SYSADMIN_CHANNEL = 6500 diff --git a/otp/distributed/PotentialAvatar.py b/otp/distributed/PotentialAvatar.py new file mode 100755 index 00000000..67bab13c --- /dev/null +++ b/otp/distributed/PotentialAvatar.py @@ -0,0 +1,11 @@ +class PotentialAvatar: + + def __init__(self, id, names, dna, position, allowedName): + self.id = id + self.name = names[0] + self.dna = dna + self.position = position + self.wantName = names[1] + self.approvedName = names[2] + self.rejectedName = names[3] + self.allowedName = allowedName diff --git a/otp/distributed/PotentialShard.py b/otp/distributed/PotentialShard.py new file mode 100755 index 00000000..fe28f6a8 --- /dev/null +++ b/otp/distributed/PotentialShard.py @@ -0,0 +1,8 @@ +class PotentialShard: + + def __init__(self, id): + self.id = id + self.name = None + self.population = 0 + self.active = 1 + self.available = 1 diff --git a/otp/distributed/TelemetryLimited.py b/otp/distributed/TelemetryLimited.py new file mode 100755 index 00000000..af65ef02 --- /dev/null +++ b/otp/distributed/TelemetryLimited.py @@ -0,0 +1,22 @@ + + +class TelemetryLimited: + Sng = SerialNumGen() + + def __init__(self): + self._telemetryLimiterId = self.Sng.next() + self._limits = set() + + def getTelemetryLimiterId(self): + return self._telemetryLimiterId + + def addTelemetryLimit(self, limit): + self._limits.add(limit) + + def removeTelemetryLimit(self, limit): + if limit in self._limits: + self._limits.remove(limit) + + def enforceTelemetryLimits(self): + for limit in self._limits: + limit(self) diff --git a/otp/distributed/TelemetryLimiter.py b/otp/distributed/TelemetryLimiter.py new file mode 100755 index 00000000..0e16c81d --- /dev/null +++ b/otp/distributed/TelemetryLimiter.py @@ -0,0 +1,120 @@ +from direct.showbase.DirectObject import DirectObject +from otp.avatar.DistributedPlayer import DistributedPlayer +from direct.task.Task import Task + +class TelemetryLimiter(DirectObject): + TaskName = 'TelemetryLimiterEnforce' + LeakDetectEventName = 'telemetryLimiter' + + def __init__(self): + self._objs = {} + self._task = taskMgr.add(self._enforceLimits, self.TaskName, priority=40) + + def destroy(self): + taskMgr.remove(self._task) + del self._objs + + def getNumObjs(self): + return len(self._objs) + + def addObj(self, obj): + id = obj.getTelemetryLimiterId() + self._objs[id] = obj + self.accept(self._getDummyEventName(obj), self._dummyEventHandler) + + def hasObj(self, obj): + id = obj.getTelemetryLimiterId() + return id in self._objs + + def _getDummyEventName(self, obj): + return '%s-%s-%s-%s' % (self.LeakDetectEventName, + obj.getTelemetryLimiterId(), + id(obj), + obj.__class__.__name__) + + def _dummyEventHandler(self, *args, **kargs): + pass + + def removeObj(self, obj): + id = obj.getTelemetryLimiterId() + self._objs.pop(id) + self.ignore(self._getDummyEventName(obj)) + + def _enforceLimits(self, task = None): + for obj in self._objs.itervalues(): + obj.enforceTelemetryLimits() + + return Task.cont + + +class TelemetryLimit: + + def __call__(self, obj): + pass + + +class RotationLimitToH(TelemetryLimit): + + def __init__(self, pConst = 0.0, rConst = 0.0): + self._pConst = pConst + self._rConst = rConst + + def __call__(self, obj): + if obj.isEmpty(): + return + obj.setHpr(obj.getH(), self._pConst, self._rConst) + + +class TLNull: + + def __init__(self, *limits): + pass + + def destroy(self): + pass + + +class TLGatherAllAvs(DirectObject): + + def __init__(self, name, *limits): + self._name = name + self._avs = {} + self._limits = makeList(limits) + self._avId2limits = {} + avs = base.cr.doFindAllInstances(DistributedPlayer) + for av in avs: + self._handlePlayerArrive(av) + + self.accept(DistributedPlayer.GetPlayerGenerateEvent(), self._handlePlayerArrive) + self.accept(DistributedPlayer.GetPlayerDeleteEvent(), self._handlePlayerLeave) + + def _handlePlayerArrive(self, av): + if av is not localAvatar: + self._avs[av.doId] = av + limitList = [] + for limit in self._limits: + l = limit() + limitList.append(l) + av.addTelemetryLimit(l) + + self._avId2limits[av.doId] = limitList + base.cr.telemetryLimiter.addObj(av) + + def _handlePlayerLeave(self, av): + if av is not localAvatar and base.cr.telemetryLimiter.hasObj(av) and av.doId in self._avId2limits: + base.cr.telemetryLimiter.removeObj(av) + for limit in self._avId2limits[av.doId]: + av.removeTelemetryLimit(limit) + + del self._avId2limits[av.doId] + if av.doId in self._avs: + del self._avs[av.doId] + + def destroy(self): + self.ignoreAll() + while len(self._avs): + self._handlePlayerLeave(self._avs.values()[0]) + + del self._avs + del self._limits + del self._avId2limits diff --git a/otp/distributed/__init__.py b/otp/distributed/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/otp/friends/FriendManager.py b/otp/friends/FriendManager.py new file mode 100755 index 00000000..0315a272 --- /dev/null +++ b/otp/friends/FriendManager.py @@ -0,0 +1,104 @@ +from panda3d.core import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from otp.otpbase import OTPGlobals + +class FriendManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('FriendManager') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.__available = 0 + self.otherToon = 0 + + def setAvailable(self, available): + self.__available = available + + def getAvailable(self): + return self.__available + + def generate(self): + if base.cr.friendManager != None: + base.cr.friendManager.delete() + base.cr.friendManager = self + DistributedObject.DistributedObject.generate(self) + return + + def disable(self): + base.cr.friendManager = None + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + base.cr.friendManager = None + DistributedObject.DistributedObject.delete(self) + return + + def up_friendQuery(self, inviteeId): + self.otherToon = inviteeId + self.sendUpdate('friendQuery', [inviteeId]) + self.notify.debug('Client: friendQuery(%d)' % inviteeId) + + def up_cancelFriendQuery(self, context): + self.sendUpdate('cancelFriendQuery', [context]) + self.notify.debug('Client: cancelFriendQuery(%d)' % context) + + def up_inviteeFriendConsidering(self, yesNo, context): + self.sendUpdate('inviteeFriendConsidering', [yesNo, context]) + self.notify.debug('Client: inviteeFriendConsidering(%d, %d)' % (yesNo, context)) + + def up_inviteeFriendResponse(self, yesNoMaybe, context): + if yesNoMaybe == 1: + base.cr.ttsFriendsManager.friendOnline(self.otherToon) + self.sendUpdate('inviteeFriendResponse', [yesNoMaybe, context]) + self.notify.debug('Client: inviteeFriendResponse(%d, %d)' % (yesNoMaybe, context)) + + def up_inviteeAcknowledgeCancel(self, context): + self.sendUpdate('inviteeAcknowledgeCancel', [context]) + self.notify.debug('Client: inviteeAcknowledgeCancel(%d)' % context) + + def friendConsidering(self, yesNoAlready, context): + self.notify.info('Roger Client: friendConsidering(%d, %d)' % (yesNoAlready, context)) + messenger.send('friendConsidering', [yesNoAlready, context]) + + def friendResponse(self, yesNoMaybe, context): + if yesNoMaybe == 1: + base.cr.ttsFriendsManager.friendOnline(self.otherToon) + self.notify.debug('Client: friendResponse(%d, %d)' % (yesNoMaybe, context)) + messenger.send('friendResponse', [yesNoMaybe, context]) + + def inviteeFriendQuery(self, inviterId, inviterName, inviterDna, context): + self.notify.debug('Client: inviteeFriendQuery(%d, %s, dna, %d)' % (inviterId, inviterName, context)) + if not hasattr(base, 'localAvatar'): + self.up_inviteeFriendConsidering(0, context) + return + if base.localAvatar.isIgnored(inviterId): + self.up_inviteeFriendConsidering(4, context) + return + if not base.localAvatar.acceptingNewFriends: + self.up_inviteeFriendConsidering(6, context) + return + self.up_inviteeFriendConsidering(self.__available, context) + self.otherToon = inviterId + if self.__available: + messenger.send('friendInvitation', [inviterId, + inviterName, + inviterDna, + context]) + + def inviteeCancelFriendQuery(self, context): + self.notify.debug('Client: inviteeCancelFriendQuery(%d)' % context) + messenger.send('cancelFriendInvitation', [context]) + self.up_inviteeAcknowledgeCancel(context) + + def requestTFCode(self, callback): + self.tfCallback = callback + self.sendUpdate('requestTFCode') + + def redeemTFCode(self, code, callback): + self.tfCallback = callback + self.sendUpdate('redeemTFCode', [code]) + + def tfResponse(self, response, code): + self.tfCallback(response, code) \ No newline at end of file diff --git a/otp/friends/FriendManagerAI.py b/otp/friends/FriendManagerAI.py new file mode 100755 index 00000000..b089526a --- /dev/null +++ b/otp/friends/FriendManagerAI.py @@ -0,0 +1,260 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from otp.otpbase import OTPGlobals +from toontown.toonbase import ToontownGlobals +import datetime, uuid, time, string, random + +AVAILABLE_CHARS = string.ascii_lowercase + string.digits + +class AddTrueFriend: + + def __init__(self, manager, av, targetId, code): + self.air = manager.air + self.manager = manager + self.av = av + self.targetId = targetId + self.code = code + + def start(self): + self.air.dbInterface.queryObject(self.air.dbId, self.targetId, self.__gotAvatar) + + def __gotAvatar(self, dclass, fields): + dclasses = self.air.dclassesByName['DistributedToonAI'] + + if dclass != dclasses: + return + + friendsList = fields['setFriendsList'][0] + trueFriendsList = fields['setTrueFriends'][0] + name = fields['setName'][0] + avId = self.av.doId + + if avId in trueFriendsList: + self.manager.sendUpdateToAvatarId(avId, 'tfResponse', [ToontownGlobals.TF_ALREADY_FRIENDS_NAME, name]) + return + elif avId not in friendsList: + if len(friendsList) >= OTPGlobals.MaxFriends: + self.manager.sendUpdateToAvatarId(avId, 'tfResponse', [ToontownGlobals.TF_FRIENDS_LIST_FULL_HIM, name]) + return + + friendsList.append(avId) + + if self.targetId not in self.av.getFriendsList(): + self.av.extendFriendsList(self.targetId) + + if hasattr(self.manager, 'data'): + del self.manager.data[self.code] + else: + self.air.dbGlobalCursor.tfCodes.remove({'_id': self.code}) + + self.av.addTrueFriend(self.targetId) + trueFriendsList.append(avId) + self.air.send(dclasses.aiFormatUpdate('setFriendsList', self.targetId, self.targetId, self.air.ourChannel, [friendsList])) + self.air.send(dclasses.aiFormatUpdate('setTrueFriends', self.targetId, self.targetId, self.air.ourChannel, [trueFriendsList])) + self.manager.sendUpdateToAvatarId(avId, 'tfResponse', [ToontownGlobals.TF_SUCCESS, name]) + del self.manager.tfFsms[avId] + +class FriendManagerAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("FriendManagerAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.air = air + self.currentContext = 0 + self.requests = {} + self.tfFsms = {} + self.connectToDatabase() + + def connectToDatabase(self): + if not self.air.dbConn: + self.notify.warning('Not using mongodb, true friends will be non-persistent') + self.data = {} + else: + self.air.dbGlobalCursor.tfCodes.ensure_index('date', expireAfterSeconds=ToontownGlobals.TF_EXPIRE_SECS) + + def friendQuery(self, requested): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + + if not av: + self.air.writeServerEvent('suspicious', avId, 'Player tried to friend a player that does not exist!') + return + + context = self.currentContext + self.requests[context] = [ [ avId, requested ], 'friendQuery'] + self.currentContext += 1 + self.sendUpdateToAvatarId(requested, 'inviteeFriendQuery', [avId, av.getName(), av.getDNAString(), context]) + + def cancelFriendQuery(self, context): + avId = self.air.getAvatarIdFromSender() + + if not context in self.requests: + self.air.writeServerEvent('suspicious', avId, 'Player tried to cancel a request that doesn\'t exist!') + return + + if avId != self.requests[context][0][0]: + self.air.writeServerEvent('suspicious', avId, 'Player tried to cancel someone elses request!') + return + + self.requests[context][1] = 'cancelled' + self.sendUpdateToAvatarId(self.requests[context][0][1], 'inviteeCancelFriendQuery', [context]) + + def inviteeFriendConsidering(self, yesNo, context): + avId = self.air.getAvatarIdFromSender() + + if not context in self.requests: + self.air.writeServerEvent('suspicious', avId, 'Player tried to consider a friend request that doesn\'t exist!') + return + + if avId != self.requests[context][0][1]: + self.air.writeServerEvent('suspicious', avId, 'Player tried to consider for someone else!') + return + + if self.requests[context][1] != 'friendQuery': + self.air.writeServerEvent('suspicious', avId, 'Player tried to reconsider friend request!') + return + + if yesNo != 1: + self.sendUpdateToAvatarId(self.requests[context][0][0], 'friendConsidering', [yesNo, context]) + del self.requests[context] + return + + self.requests[context][1] = 'friendConsidering' + self.sendUpdateToAvatarId(self.requests[context][0][0], 'friendConsidering', [yesNo, context]) + + def inviteeFriendResponse(self, response, context): + avId = self.air.getAvatarIdFromSender() + + if not context in self.requests: + self.air.writeServerEvent('suspicious', avId, 'Player tried to respond to a friend request that doesn\'t exist!') + return + + if avId != self.requests[context][0][1]: + self.air.writeServerEvent('suspicious', avId, 'Player tried to respond to someone else\'s request!') + return + + if self.requests[context][1] == 'cancelled': + self.air.writeServerEvent('suspicious', avId, 'Player tried to respond to non-active friend request!') + return + + self.sendUpdateToAvatarId(self.requests[context][0][0], 'friendResponse', [response, context]) + + if response == 1: + requested = self.requests[context][0][1] + + if requested in self.air.doId2do: + requested = self.air.doId2do[requested] + else: + del self.requests[context] + return + + requester = self.requests[context][0][0] + + if requester in self.air.doId2do: + requester = self.air.doId2do[requester] + else: + del self.requests[context] + return + + requested.extendFriendsList(requester.getDoId()) + requester.extendFriendsList(requested.getDoId()) + + requested.d_setFriendsList(requested.getFriendsList()) + requester.d_setFriendsList(requester.getFriendsList()) + + del self.requests[context] + + def inviteeAcknowledgeCancel(self, context): + avId = self.air.getAvatarIdFromSender() + + if not context in self.requests: + self.air.writeServerEvent('suspicious', avId, 'Player tried to acknowledge the cancel of a friend request that doesn\'t exist!') + return + + if avId != self.requests[context][0][1]: + self.air.writeServerEvent('suspicious', avId, 'Player tried to acknowledge someone else\'s cancel!') + return + + if self.requests[context][1] != 'cancelled': + self.air.writeServerEvent('suspicious', avId, 'Player tried to cancel non-cancelled request!') + return + + del self.requests[context] + + def getRandomCharSequence(self, count): + return ''.join(random.choice(AVAILABLE_CHARS) for i in xrange(count)) + + def getTFCode(self, tryNumber): + if tryNumber == ToontownGlobals.MAX_TF_TRIES: + return str(uuid.uuid4()) + + code = 'TT %s %s' % (self.getRandomCharSequence(3), self.getRandomCharSequence(3)) + + if (hasattr(self, 'data') and code in self.data) or (self.air.dbConn and self.air.dbGlobalCursor.tfCodes.find({'_id': code}).count() > 0): + return self.getTFCode(tryNumber + 1) + + return code + + def requestTFCode(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + + if not av: + return + + tfRequest = av.getTFRequest() + + if tfRequest[1] >= ToontownGlobals.MAX_TF_TRIES and tfRequest[0] >= time.time(): + self.sendUpdateToAvatarId(avId, 'tfResponse', [ToontownGlobals.TF_COOLDOWN, '']) + return + + code = self.getTFCode(0) + + if hasattr(self, 'data'): + self.data[code] = avId + else: + self.air.dbGlobalCursor.tfCodes.insert({'_id': code, 'date': datetime.datetime.utcnow(), 'avId': avId}) + + av.b_setTFRequest((time.time() + ToontownGlobals.TF_COOLDOWN_SECS, tfRequest[1] + 1)) + self.sendUpdateToAvatarId(avId, 'tfResponse', [ToontownGlobals.TF_SUCCESS, code]) + + def redeemTFCode(self, code): + avId = self.air.getAvatarIdFromSender() + + if avId in self.tfFsms: + self.sendUpdateToAvatarId(avId, 'tfResponse', [ToontownGlobals.TF_TOO_FAST, '']) + return + + av = self.air.doId2do.get(avId) + + if not av: + return + + if hasattr(self, 'data'): + if code not in self.data: + self.sendUpdateToAvatarId(avId, 'tfResponse', [ToontownGlobals.TF_UNKNOWN_SECRET, '']) + return + + targetId = self.data[code] + else: + fields = self.air.dbGlobalCursor.tfCodes.find_one({'_id': code}) + + if not fields: + self.sendUpdateToAvatarId(avId, 'tfResponse', [ToontownGlobals.TF_UNKNOWN_SECRET, '']) + return + + targetId = fields['avId'] + + if avId == targetId: + self.sendUpdateToAvatarId(avId, 'tfResponse', [ToontownGlobals.TF_SELF_SECRET, '']) + return + elif av.isTrueFriends(targetId): + self.sendUpdateToAvatarId(avId, 'tfResponse', [ToontownGlobals.TF_ALREADY_FRIENDS, '']) + return + elif targetId not in av.getFriendsList() and len(av.getFriendsList()) >= OTPGlobals.MaxFriends: + self.sendUpdateToAvatarId(avId, 'tfResponse', [ToontownGlobals.TF_FRIENDS_LIST_FULL_YOU, '']) + return + + tfOperation = AddTrueFriend(self, av, targetId, code) + tfOperation.start() + self.tfFsms[avId] = tfOperation \ No newline at end of file diff --git a/otp/friends/__init__.py b/otp/friends/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/otp/level/AmbientSound.py b/otp/level/AmbientSound.py new file mode 100755 index 00000000..fb23bfc4 --- /dev/null +++ b/otp/level/AmbientSound.py @@ -0,0 +1,33 @@ +from direct.interval.IntervalGlobal import * +import BasicEntities +import random + +class AmbientSound(BasicEntities.NodePathEntity): + + def __init__(self, level, entId): + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.initSound() + + def destroy(self): + self.destroySound() + BasicEntities.NodePathEntity.destroy(self) + + def initSound(self): + if not self.enabled: + return + if self.soundPath == '': + return + self.sound = base.loadSfx(self.soundPath) + if self.sound is None: + return + self.soundIval = SoundInterval(self.sound, node=self, volume=self.volume) + self.soundIval.loop() + self.soundIval.setT(random.random() * self.sound.length()) + return + + def destroySound(self): + if hasattr(self, 'soundIval'): + self.soundIval.pause() + del self.soundIval + if hasattr(self, 'sound'): + del self.sound diff --git a/otp/level/AttribDesc.py b/otp/level/AttribDesc.py new file mode 100755 index 00000000..c962a355 --- /dev/null +++ b/otp/level/AttribDesc.py @@ -0,0 +1,30 @@ + + +class AttribDesc: + + def __init__(self, name, default, datatype = 'string', params = {}): + self.name = name + self.default = default + self.datatype = datatype + self.params = params + + def getName(self): + return self.name + + def getDefaultValue(self): + return self.default + + def getDatatype(self): + return self.datatype + + def getParams(self): + return self.params + + def __str__(self): + return self.name + + def __repr__(self): + return 'AttribDesc(%s, %s, %s, %s)' % (repr(self.name), + repr(self.default), + repr(self.datatype), + repr(self.params)) diff --git a/otp/level/BasicEntities.py b/otp/level/BasicEntities.py new file mode 100755 index 00000000..4a459159 --- /dev/null +++ b/otp/level/BasicEntities.py @@ -0,0 +1,128 @@ +import Entity +import DistributedEntity +from pandac.PandaModules import NodePath + +class NodePathEntityBase: + + def initNodePathAttribs(self, doReparent = 1): + self.callSetters('pos', 'x', 'y', 'z', 'hpr', 'h', 'p', 'r', 'scale', 'sx', 'sy', 'sz') + if doReparent: + self.callSetters('parentEntId') + self.getNodePath().setName('%s-%s' % (self.__class__.__name__, self.entId)) + + def setParentEntId(self, parentEntId): + self.parentEntId = parentEntId + self.level.requestReparent(self, self.parentEntId) + + def destroy(self): + pass + + +class NodePathAttribs(NodePathEntityBase): + + def initNodePathAttribs(self, doReparent = 1): + NodePathEntityBase.initNodePathAttribs(self, doReparent) + + def destroy(self): + NodePathEntityBase.destroy(self) + + def getNodePath(self): + return self + + +class NodePathAndAttribs(NodePathEntityBase, NodePath): + + def __init__(self): + node = hidden.attachNewNode('EntityNodePath') + NodePath.__init__(self, node) + + def initNodePathAttribs(self, doReparent = 1): + NodePathEntityBase.initNodePathAttribs(self, doReparent) + + def destroy(self): + NodePathEntityBase.destroy(self) + self.removeNode() + + def getNodePath(self): + return self + + +class NodePathAttribsProxy(NodePathEntityBase): + + def initNodePathAttribs(self, doReparent = 1): + NodePathEntityBase.initNodePathAttribs(self, doReparent) + + def destroy(self): + NodePathEntityBase.destroy(self) + + def setPos(self, *args): + self.getNodePath().setPos(*args) + + def setX(self, *args): + self.getNodePath().setX(*args) + + def setY(self, *args): + self.getNodePath().setY(*args) + + def setZ(self, *args): + self.getNodePath().setZ(*args) + + def setHpr(self, *args): + self.getNodePath().setHpr(*args) + + def setH(self, *args): + self.getNodePath().setH(*args) + + def setP(self, *args): + self.getNodePath().setP(*args) + + def setR(self, *args): + self.getNodePath().setR(*args) + + def setScale(self, *args): + self.getNodePath().setScale(*args) + + def setSx(self, *args): + self.getNodePath().setSx(*args) + + def setSy(self, *args): + self.getNodePath().setSy(*args) + + def setSz(self, *args): + self.getNodePath().setSz(*args) + + def reparentTo(self, *args): + self.getNodePath().reparentTo(*args) + + +class NodePathEntity(Entity.Entity, NodePath, NodePathAttribs): + + def __init__(self, level, entId): + node = hidden.attachNewNode('NodePathEntity') + NodePath.__init__(self, node) + Entity.Entity.__init__(self, level, entId) + self.initNodePathAttribs(self) + + def destroy(self): + NodePathAttribs.destroy(self) + Entity.Entity.destroy(self) + self.removeNode() + + +class DistributedNodePathEntity(DistributedEntity.DistributedEntity, NodePath, NodePathAttribs): + + def __init__(self, cr): + DistributedEntity.DistributedEntity.__init__(self, cr) + + def generateInit(self): + DistributedEntity.DistributedEntity.generateInit(self) + node = hidden.attachNewNode('DistributedNodePathEntity') + NodePath.__init__(self, node) + + def announceGenerate(self): + DistributedEntity.DistributedEntity.announceGenerate(self) + self.initNodePathAttribs(self) + + def delete(self): + self.removeNode() + DistributedEntity.DistributedEntity.delete(self) diff --git a/otp/level/CollisionSolidEntity.py b/otp/level/CollisionSolidEntity.py new file mode 100755 index 00000000..3951711d --- /dev/null +++ b/otp/level/CollisionSolidEntity.py @@ -0,0 +1,34 @@ +from panda3d.core import * +from otp.otpbase import OTPGlobals +from direct.directnotify import DirectNotifyGlobal +import BasicEntities + +class CollisionSolidEntity(BasicEntities.NodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('CollisionSolidEntity') + + def __init__(self, level, entId): + self.collNodePath = None + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.initSolid() + return + + def destroy(self): + self.destroySolid() + BasicEntities.NodePathEntity.destroy(self) + + def initSolid(self): + self.destroySolid() + if self.solidType == 'sphere': + solid = CollisionSphere(0, 0, 0, self.radius) + else: + solid = CollisionTube(0, 0, 0, 0, 0, self.length, self.radius) + node = CollisionNode(self.getUniqueName(self.__class__.__name__)) + node.addSolid(solid) + node.setCollideMask(OTPGlobals.WallBitmask) + self.collNodePath = self.attachNewNode(node) + + def destroySolid(self): + if self.collNodePath is not None: + self.collNodePath.removeNode() + self.collNodePath = None + return diff --git a/otp/level/CutScene.py b/otp/level/CutScene.py new file mode 100755 index 00000000..6f3e5724 --- /dev/null +++ b/otp/level/CutScene.py @@ -0,0 +1,98 @@ +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +import BasicEntities +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM + +def nothing(self, track, subjectNodePath, duration): + return track + + +def irisInOut(self, track, subjectNodePath, duration): + track.append(Sequence(Func(base.transitions.irisOut, 0.5), Func(base.transitions.irisIn, 1.5), Wait(duration), Func(base.transitions.irisOut, 1.0), Func(base.transitions.irisIn, 0.5))) + return track + + +def letterBox(self, track, subjectNodePath, duration): + track.append(Sequence(Wait(duration))) + return track + + +def foo1(self, track, subjectNodePath, duration): + track.append(Sequence(Func(base.localAvatar.stopUpdateSmartCamera), PosHprInterval(camera, other=subjectNodePath, pos=Point3(-2, -35, 7.5), hpr=VBase3(-7, 0, 0)), LerpPosHprInterval(nodePath=camera, other=subjectNodePath, duration=duration, pos=Point3(2, -22, 7.5), hpr=VBase3(4, 0, 0), blendType='easeInOut'), PosHprInterval(camera, other=subjectNodePath, pos=Point3(0, -28, 7.5), hpr=VBase3(0, 0, 0)), Func(base.localAvatar.startUpdateSmartCamera))) + return track + + +def doorUnlock(self, track, subjectNodePath, duration): + track.append(Sequence(Func(base.localAvatar.stopUpdateSmartCamera), PosHprInterval(camera, other=self, pos=Point3(-2, -35, 7.5), hpr=VBase3(-7, 0, 0)), LerpPosHprInterval(nodePath=camera, other=self, duration=duration, pos=Point3(2, -22, 7.5), hpr=VBase3(4, 0, 0), blendType='easeInOut'), PosHprInterval(camera, other=self, pos=Point3(0, -28, 7.5), hpr=VBase3(0, 0, 0)), Func(base.localAvatar.startUpdateSmartCamera))) + return track + + +class CutScene(BasicEntities.NodePathEntity, DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('CutScene') + effects = {'nothing': nothing, + 'irisInOut': irisInOut, + 'letterBox': letterBox} + motions = {'foo1': foo1, + 'doorUnlock': doorUnlock} + + def __init__(self, level, entId): + DirectObject.DirectObject.__init__(self) + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.track = None + self.setEffect(self.effect) + self.setMotion(self.motion) + self.subjectNodePath = render.attachNewNode('CutScene') + self.subjectNodePath.setPos(self.pos) + self.subjectNodePath.setHpr(self.hpr) + self.setStartStop(self.startStopEvent) + return + + def destroy(self): + self.ignore(self.startStopEvent) + self.startStopEvent = None + BasicEntities.NodePathEntity.destroy(self) + return + + def setEffect(self, effect): + self.effect = effect + self.getEffect = self.effects[effect] + + def setMotion(self, motion): + self.motionType = motion + self.getMotion = self.motions[motion] + + def setSubjectNodePath(self, subjectNodePath): + self.subjectNodePath = subjectNodePath + + def startOrStop(self, start): + trackName = 'cutSceneTrack-%d' % (id(self),) + if start: + if self.track: + self.track.finish() + self.track = None + track = Parallel(name=trackName) + track = self.getEffect(self, track, self.subjectNodePath, self.duration) + track = self.getMotion(self, track, self.subjectNodePath, self.duration) + track = Sequence(Wait(0.4), track) + track.start(0.0) + self.track = track + elif self.track: + self.track.pause() + self.track = None + base.localAvatar.startUpdateSmartCamera() + return + + def setStartStop(self, event): + if self.startStopEvent: + self.ignore(self.startStopEvent) + self.startStopEvent = self.getOutputEventName(event) + if self.startStopEvent: + self.accept(self.startStopEvent, self.startOrStop) + + def getName(self): + return 'switch-%s' % (self.entId,) diff --git a/otp/level/DistributedEntity.py b/otp/level/DistributedEntity.py new file mode 100755 index 00000000..9d078593 --- /dev/null +++ b/otp/level/DistributedEntity.py @@ -0,0 +1,50 @@ +from direct.distributed import DistributedObject +import Entity +from direct.directnotify import DirectNotifyGlobal + +class DistributedEntity(DistributedObject.DistributedObject, Entity.Entity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedEntity') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + Entity.Entity.__init__(self) + self.levelDoId = 0 + self.entId = 0 + self.level = None + return + + def generateInit(self): + DistributedEntity.notify.debug('generateInit') + DistributedObject.DistributedObject.generateInit(self) + + def generate(self): + DistributedEntity.notify.debug('generate') + DistributedObject.DistributedObject.generate(self) + + def setLevelDoId(self, levelDoId): + DistributedEntity.notify.debug('setLevelDoId: %s' % levelDoId) + self.levelDoId = levelDoId + + def setEntId(self, entId): + DistributedEntity.notify.debug('setEntId: %s' % entId) + self.entId = entId + + def announceGenerate(self): + DistributedEntity.notify.debug('announceGenerate (%s)' % self.entId) + if self.levelDoId != 0: + level = base.cr.doId2do[self.levelDoId] + self.initializeEntity(level, self.entId) + self.level.onEntityCreate(self.entId) + else: + self.level = None + DistributedObject.DistributedObject.announceGenerate(self) + return + + def disable(self): + DistributedEntity.notify.debug('disable (%s)' % self.entId) + self.destroy() + DistributedObject.DistributedObject.disable(self) + + def delete(self): + DistributedEntity.notify.debug('delete') + DistributedObject.DistributedObject.delete(self) diff --git a/otp/level/DistributedEntityAI.py b/otp/level/DistributedEntityAI.py new file mode 100755 index 00000000..5b4969f2 --- /dev/null +++ b/otp/level/DistributedEntityAI.py @@ -0,0 +1,37 @@ +from direct.distributed import DistributedObjectAI +import Entity +from direct.directnotify import DirectNotifyGlobal + +class DistributedEntityAI(DistributedObjectAI.DistributedObjectAI, Entity.Entity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedEntityAI') + + def __init__(self, level, entId): + if hasattr(level, 'air'): + air = level.air + self.levelDoId = level.doId + else: + air = level + level = None + self.levelDoId = 0 + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + Entity.Entity.__init__(self, level, entId) + return + + def generate(self): + self.notify.debug('generate') + DistributedObjectAI.DistributedObjectAI.generate(self) + + def destroy(self): + self.notify.debug('destroy') + Entity.Entity.destroy(self) + self.requestDelete() + + def delete(self): + self.notify.debug('delete') + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getLevelDoId(self): + return self.levelDoId + + def getEntId(self): + return self.entId diff --git a/otp/level/DistributedInteractiveEntity.py b/otp/level/DistributedInteractiveEntity.py new file mode 100755 index 00000000..9c532607 --- /dev/null +++ b/otp/level/DistributedInteractiveEntity.py @@ -0,0 +1,69 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +import DistributedEntity + +class DistributedInteractiveEntity(DistributedEntity.DistributedEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedInteractiveEntity') + + def __init__(self, cr): + DistributedEntity.DistributedEntity.__init__(self, cr) + self.fsm = ClassicFSM.ClassicFSM('DistributedInteractiveEntity', [State.State('off', self.enterOff, self.exitOff, ['playing', 'attract']), State.State('attract', self.enterAttract, self.exitAttract, ['playing']), State.State('playing', self.enterPlaying, self.exitPlaying, ['attract'])], 'off', 'off') + self.fsm.enterInitialState() + + def generate(self): + DistributedEntity.DistributedEntity.generate(self) + + def disable(self): + self.fsm.request('off') + DistributedEntity.DistributedEntity.disable(self) + + def delete(self): + del self.fsm + DistributedEntity.DistributedEntity.delete(self) + + def setAvatarInteract(self, avatarId): + self.avatarId = avatarId + + def setOwnerDoId(self, ownerDoId): + self.ownerDoId = ownerDoId + + def setState(self, state, timestamp): + if self.isGenerated(): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + else: + self.initialState = state + self.initialStateTimestamp = timestamp + + def enterTrigger(self, args = None): + messenger.send('DistributedInteractiveEntity_enterTrigger') + self.sendUpdate('requestInteract') + + def exitTrigger(self, args = None): + messenger.send('DistributedInteractiveEntity_exitTrigger') + self.sendUpdate('requestExit') + + def rejectInteract(self): + self.cr.playGame.getPlace().setState('walk') + + def avatarExit(self, avatarId): + pass + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterAttract(self, ts): + pass + + def exitAttract(self): + pass + + def enterPlaying(self, ts): + pass + + def exitPlaying(self): + pass diff --git a/otp/level/DistributedInteractiveEntityAI.py b/otp/level/DistributedInteractiveEntityAI.py new file mode 100755 index 00000000..716b7617 --- /dev/null +++ b/otp/level/DistributedInteractiveEntityAI.py @@ -0,0 +1,23 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.level.DistributedEntityAI import DistributedEntityAI + +class DistributedInteractiveEntityAI(DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedInteractiveEntityAI") + + def setAvatarInteract(self, todo0): + pass + + def requestInteract(self): + pass + + def rejectInteract(self): + pass + + def requestExit(self): + pass + + def avatarExit(self, todo0): + pass + + def setState(self, todo0, todo1): + pass diff --git a/otp/level/DistributedLevel.py b/otp/level/DistributedLevel.py new file mode 100755 index 00000000..a37417d9 --- /dev/null +++ b/otp/level/DistributedLevel.py @@ -0,0 +1,535 @@ +from direct.distributed.ClockDelta import * +from panda3d.core import * +from direct.showbase.PythonUtil import Functor, sameElements, list2dict, uniqueElements +from direct.interval.IntervalGlobal import * +from toontown.distributed.ToontownMsgTypes import * +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from direct.distributed import DistributedObject +import Level +import LevelConstants +from direct.directnotify import DirectNotifyGlobal +import EntityCreator +from direct.gui import OnscreenText +from direct.task import Task +import LevelUtil +import random + +class DistributedLevel(DistributedObject.DistributedObject, Level.Level): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevel') + ColorZonesAllDOs = 0 + FloorCollPrefix = 'zoneFloor' + OuchTaskName = 'ouchTask' + VisChangeTaskName = 'visChange' + EmulateEntrancePoint = True + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + Level.Level.__init__(self) + self.lastToonZone = None + self.lastCamZone = 0 + self.titleColor = (1, 1, 1, 1) + self.titleText = OnscreenText.OnscreenText('', fg=self.titleColor, shadow=(0, 0, 0, 1), font=ToontownGlobals.getSuitFont(), pos=(0, -0.5), scale=0.16, drawOrder=0, mayChange=1) + self.smallTitleText = OnscreenText.OnscreenText('', fg=self.titleColor, font=ToontownGlobals.getSuitFont(), pos=(0.65, 0.9), scale=0.08, drawOrder=0, mayChange=1, bg=(0.5, 0.5, 0.5, 0.5), align=TextNode.ARight) + self.titleSeq = None + self.zonesEnteredList = [] + self.fColorZones = 0 + self.scenarioIndex = 0 + + def generate(self): + DistributedLevel.notify.debug('generate') + DistributedObject.DistributedObject.generate(self) + self.parent2pendingChildren = {} + self.curSpec = None + if base.cr.timeManager is not None: + base.cr.timeManager.synchronize('DistributedLevel.generate') + else: + self.notify.warning('generate(): no TimeManager!') + return + + def setLevelZoneId(self, zoneId): + self.levelZone = zoneId + + def setAvIds(self, avIdList): + self.avIdList = avIdList + + def setEntranceId(self, entranceId): + self.entranceId = entranceId + + def getEntranceId(self): + return self.entranceId + + def setZoneIds(self, zoneIds): + DistributedLevel.notify.debug('setZoneIds: %s' % zoneIds) + self.zoneIds = zoneIds + self.privGotAllRequired() + + def setStartTimestamp(self, timestamp): + DistributedLevel.notify.debug('setStartTimestamp: %s' % timestamp) + self.startTime = globalClockDelta.networkToLocalTime(timestamp, bits=32) + self.privGotAllRequired() + + def privGotAllRequired(self): + if hasattr(self, 'zoneIds') and hasattr(self, 'startTime'): + self.levelAnnounceGenerate() + + def levelAnnounceGenerate(self): + pass + + def initializeLevel(self, levelSpec): + self.privGotSpec(levelSpec) + + def privGotSpec(self, levelSpec): + Level.Level.initializeLevel(self, self.doId, levelSpec, self.scenarioIndex) + modelZoneNums = self.zoneNums + specZoneNums = self.zoneNum2zoneId.keys() + self.initVisibility() + self.placeLocalToon() + + def announceLeaving(self): + DistributedLevel.notify.debug('announceLeaving') + self.doneBarrier() + + def placeLocalToon(self, moveLocalAvatar = True): + initialZoneEnt = None + if self.entranceId in self.entranceId2entity: + epEnt = self.entranceId2entity[self.entranceId] + if moveLocalAvatar: + epEnt.placeToon(base.localAvatar, self.avIdList.index(base.localAvatar.doId), len(self.avIdList)) + initialZoneEnt = self.getEntity(epEnt.getZoneEntId()) + elif self.EmulateEntrancePoint: + self.notify.debug('unknown entranceId %s' % self.entranceId) + if moveLocalAvatar: + base.localAvatar.reparentTo(render) + base.localAvatar.setPosHpr(0, 0, 0, 0, 0, 0) + self.notify.debug('showing all zones') + self.setColorZones(1) + zoneEntIds = list(self.entType2ids['zone']) + zoneEntIds.remove(LevelConstants.UberZoneEntId) + if len(zoneEntIds): + zoneEntId = random.choice(zoneEntIds) + initialZoneEnt = self.getEntity(zoneEntId) + if moveLocalAvatar: + base.localAvatar.setPos(render, initialZoneEnt.getZoneNode().getPos(render)) + else: + initialZoneEnt = self.getEntity(LevelConstants.UberZoneEntId) + if moveLocalAvatar: + base.localAvatar.setPos(render, 0, 0, 0) + if initialZoneEnt is not None: + self.enterZone(initialZoneEnt.entId) + return + + def createEntityCreator(self): + return EntityCreator.EntityCreator(level=self) + + def onEntityTypePostCreate(self, entType): + Level.Level.onEntityTypePostCreate(self, entType) + if entType == 'levelMgr': + self.__handleLevelMgrCreated() + + def __handleLevelMgrCreated(self): + levelMgr = self.getEntity(LevelConstants.LevelMgrEntId) + self.geom = levelMgr.geom + self.zoneNum2node = LevelUtil.getZoneNum2Node(self.geom) + self.zoneNums = self.zoneNum2node.keys() + self.zoneNums.sort() + self.zoneNumDict = list2dict(self.zoneNums) + DistributedLevel.notify.debug('zones from model: %s' % self.zoneNums) + self.fixupLevelModel() + + def fixupLevelModel(self): + for zoneNum, zoneNode in self.zoneNum2node.items(): + if zoneNum == LevelConstants.UberZoneEntId: + continue + allColls = zoneNode.findAllMatches('**/+CollisionNode') + floorColls = [] + for coll in allColls: + bitmask = coll.node().getIntoCollideMask() + if not (bitmask & ToontownGlobals.FloorBitmask).isZero(): + floorColls.append(coll) + + if len(floorColls) > 0: + floorCollName = '%s%s' % (DistributedLevel.FloorCollPrefix, zoneNum) + others = zoneNode.findAllMatches('**/%s' % floorCollName) + for other in others: + other.setName('%s_renamed' % floorCollName) + + for floorColl in floorColls: + floorColl.setName(floorCollName) + + def handleZoneEnter(collisionEntry, self = self, zoneNum = zoneNum): + self.toonEnterZone(zoneNum) + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + ouchLevel = int(self.getFloorOuchLevel()) + self.startOuch(ouchLevel) + + self.accept('enter%s' % floorCollName, handleZoneEnter) + + def handleZoneExit(collisionEntry, self = self, zoneNum = zoneNum): + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + self.stopOuch() + + self.accept('exit%s' % floorCollName, handleZoneExit) + + def getFloorOuchLevel(self): + return 1 + + def announceGenerate(self): + DistributedLevel.notify.debug('announceGenerate') + DistributedObject.DistributedObject.announceGenerate(self) + + def disable(self): + DistributedLevel.notify.debug('disable') + if hasattr(self, 'geom'): + del self.geom + self.shutdownVisibility() + self.destroyLevel() + self.ignoreAll() + taskMgr.remove(self.uniqueName('titleText')) + if self.smallTitleText: + self.smallTitleText.cleanup() + self.smallTitleText = None + if self.titleText: + self.titleText.cleanup() + self.titleText = None + self.zonesEnteredList = [] + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + DistributedLevel.notify.debug('delete') + DistributedObject.DistributedObject.delete(self) + self.stopOuch() + + def requestReparent(self, entity, parentId, wrt = False): + parent = self.getEntity(parentId) + if parent is not None: + if wrt: + entity.wrtReparentTo(parent.getNodePath()) + else: + entity.reparentTo(parent.getNodePath()) + else: + DistributedLevel.notify.debug('entity %s requesting reparent to %s, not yet created' % (entity, parentId)) + entity.reparentTo(hidden) + if parentId not in self.parent2pendingChildren: + self.parent2pendingChildren[parentId] = [] + + def doReparent(parentId = parentId, self = self, wrt = wrt): + parent = self.getEntity(parentId) + for child in self.parent2pendingChildren[parentId]: + DistributedLevel.notify.debug('performing pending reparent of %s to %s' % (child, parent)) + if wrt: + child.wrtReparentTo(parent.getNodePath()) + else: + child.reparentTo(parent.getNodePath()) + + del self.parent2pendingChildren[parentId] + self.ignore(self.getEntityCreateEvent(parentId)) + + self.accept(self.getEntityCreateEvent(parentId), doReparent) + self.parent2pendingChildren[parentId].append(entity) + return + + def getZoneNode(self, zoneEntId): + return self.zoneNum2node.get(zoneEntId) + + def warpToZone(self, zoneNum): + zoneNode = self.getZoneNode(zoneNum) + if zoneNode is None: + return + base.localAvatar.setPos(zoneNode, 0, 0, 0) + base.localAvatar.setHpr(zoneNode, 0, 0, 0) + self.enterZone(zoneNum) + return + + def showZone(self, zoneNum): + zone = self.getZoneNode(zoneNum) + zone.unstash() + zone.clearColor() + + def setColorZones(self, fColorZones): + self.fColorZones = fColorZones + self.resetVisibility() + + def getColorZones(self): + return self.fColorZones + + def hideZone(self, zoneNum): + zone = self.getZoneNode(zoneNum) + if self.fColorZones: + zone.unstash() + zone.setColor(1, 0, 0) + else: + zone.stash() + + def setTransparency(self, alpha, zone = None): + self.geom.setTransparency(1) + if zone is None: + node = self.geom + else: + node = self.getZoneNode(zoneNum) + node.setAlphaScale(alpha) + return + + def initVisibility(self): + self.curVisibleZoneNums = list2dict(self.zoneNums) + del self.curVisibleZoneNums[LevelConstants.UberZoneEntId] + self.curZoneNum = None + self.visChangedThisFrame = 0 + self.fForceSetZoneThisFrame = 0 + + def handleCameraRayFloorCollision(collEntry, self = self): + name = collEntry.getIntoNode().getName() + self.notify.debug('camera floor ray collided with: %s' % name) + prefixLen = len(DistributedLevel.FloorCollPrefix) + if name[:prefixLen] == DistributedLevel.FloorCollPrefix: + try: + zoneNum = int(name[prefixLen:]) + except: + DistributedLevel.notify.warning('Invalid zone floor collision node: %s' % name) + else: + self.camEnterZone(zoneNum) + + self.accept('on-floor', handleCameraRayFloorCollision) + taskMgr.add(self.visChangeTask, self.uniqueName(DistributedLevel.VisChangeTaskName), priority=49) + + def shutdownVisibility(self): + taskMgr.remove(self.uniqueName(DistributedLevel.VisChangeTaskName)) + + def toonEnterZone(self, zoneNum, ouchLevel = None): + DistributedLevel.notify.debug('toonEnterZone%s' % zoneNum) + if zoneNum != self.lastToonZone: + self.lastToonZone = zoneNum + self.notify.debug('toon is standing in zone %s' % zoneNum) + messenger.send('factoryZoneChanged', [zoneNum]) + + def camEnterZone(self, zoneNum): + DistributedLevel.notify.debug('camEnterZone%s' % zoneNum) + self.enterZone(zoneNum) + if zoneNum != self.lastCamZone: + self.lastCamZone = zoneNum + self.smallTitleText.hide() + self.spawnTitleText() + + def lockVisibility(self, zoneNum = None, zoneId = None): + if zoneId is not None: + zoneNum = self.getZoneNumFromId(zoneId) + self.notify.debug('lockVisibility to zoneNum %s' % zoneNum) + self.lockVizZone = zoneNum + self.enterZone(self.lockVizZone) + return + + def unlockVisibility(self): + self.notify.debug('unlockVisibility') + if not hasattr(self, 'lockVizZone'): + self.notify.warning('visibility already unlocked') + else: + del self.lockVizZone + self.updateVisibility() + + def enterZone(self, zoneNum): + DistributedLevel.notify.debug('entering zone %s' % zoneNum) + if zoneNum == self.curZoneNum: + return + if zoneNum not in self.zoneNumDict: + DistributedLevel.notify.error('no ZoneEntity for this zone (%s)!!' % zoneNum) + self.updateVisibility(zoneNum) + + def updateVisibility(self, zoneNum = None): + if zoneNum is None: + zoneNum = self.curZoneNum + if zoneNum is None: + return + if hasattr(self, 'lockVizZone'): + zoneNum = self.lockVizZone + zoneEnt = self.getEntity(zoneNum) + visibleZoneNums = list2dict([zoneNum]) + visibleZoneNums.update(list2dict(zoneEnt.getVisibleZoneNums())) + if not __debug__: + if self.lastToonZone not in visibleZoneNums: + if self.lastToonZone is not None: + self.notify.warning('adding zoneNum %s to visibility list because toon is standing in that zone!' % self.lastToonZone) + visibleZoneNums.update(list2dict([self.lastToonZone])) + zoneEntIds = list(self.entType2ids['zone']) + zoneEntIds.remove(LevelConstants.UberZoneEntId) + if len(zoneEntIds): + pass + vizZonesChanged = 1 + addedZoneNums = [] + removedZoneNums = [] + allVZ = dict(visibleZoneNums) + allVZ.update(self.curVisibleZoneNums) + for vz, dummy in allVZ.items(): + new = vz in visibleZoneNums + old = vz in self.curVisibleZoneNums + if new and old: + continue + if new: + addedZoneNums.append(vz) + else: + removedZoneNums.append(vz) + + if not addedZoneNums and not removedZoneNums: + DistributedLevel.notify.debug('visible zone list has not changed') + vizZonesChanged = 0 + else: + DistributedLevel.notify.debug('showing zones %s' % addedZoneNums) + for az in addedZoneNums: + self.showZone(az) + + DistributedLevel.notify.debug('hiding zones %s' % removedZoneNums) + for rz in removedZoneNums: + self.hideZone(rz) + + if vizZonesChanged or self.fForceSetZoneThisFrame: + self.setVisibility(visibleZoneNums.keys()) + self.fForceSetZoneThisFrame = 0 + self.curZoneNum = zoneNum + self.curVisibleZoneNums = visibleZoneNums + return + + def setVisibility(self, vizList): + if self.fColorZones and DistributedLevel.ColorZonesAllDOs: + vizList = list(self.zoneNums) + vizList.remove(LevelConstants.UberZoneEntId) + uberZone = self.getZoneId(LevelConstants.UberZoneEntId) + visibleZoneIds = [OTPGlobals.UberZone, self.levelZone, uberZone] + for vz in vizList: + if vz is not LevelConstants.UberZoneEntId: + visibleZoneIds.append(self.getZoneId(vz)) + + DistributedLevel.notify.debug('new viz list: %s' % visibleZoneIds) + base.cr.sendSetZoneMsg(self.levelZone, visibleZoneIds) + + def resetVisibility(self): + self.curVisibleZoneNums = list2dict(self.zoneNums) + del self.curVisibleZoneNums[LevelConstants.UberZoneEntId] + for vz, dummy in self.curVisibleZoneNums.items(): + self.showZone(vz) + + self.updateVisibility() + + def handleVisChange(self): + Level.Level.handleVisChange(self) + self.visChangedThisFrame = 1 + + def forceSetZoneThisFrame(self): + self.fForceSetZoneThisFrame = 1 + + def visChangeTask(self, task): + if self.visChangedThisFrame or self.fForceSetZoneThisFrame: + self.updateVisibility() + self.visChangedThisFrame = 0 + return Task.cont + + def spawnTitleText(self): + + def getDescription(zoneNum, self = self): + ent = self.entities.get(zoneNum) + if ent and hasattr(ent, 'description'): + return ent.description + return None + + description = getDescription(self.lastCamZone) + if description and description != '': + taskMgr.remove(self.uniqueName('titleText')) + self.smallTitleText.setText(description) + self.titleText.setText(description) + self.titleText.setColor(Vec4(*self.titleColor)) + self.titleText.setFg(self.titleColor) + titleSeq = None + if self.lastCamZone not in self.zonesEnteredList: + self.zonesEnteredList.append(self.lastCamZone) + titleSeq = Sequence(Func(self.hideSmallTitleText), Func(self.showTitleText), Wait(6.1), LerpColorInterval(self.titleText, 0.5, Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], self.titleColor[3]), startColor=Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], 0.0))) + smallTitleSeq = Sequence(Func(self.hideTitleText), Func(self.showSmallTitle)) + if titleSeq: + self.titleSeq = Sequence(titleSeq, smallTitleSeq) + else: + self.titleSeq = smallTitleSeq + self.titleSeq.start() + + def showInfoText(self, text = 'hello world'): + description = text + if description and description != '': + if self.titleSeq is not None: + self.titleSeq.finish() + self.titleSeq = None + self.smallTitleText.setText(description) + self.titleText.setText(description) + self.titleText.setColor(Vec4(*self.titleColor)) + self.titleText.setFg(self.titleColor) + titleSeq = None + titleSeq = Sequence(Func(self.hideSmallTitleText), Func(self.showTitleText), Wait(3.1), LerpColorInterval(self.titleText, 0.5, Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], self.titleColor[3]), startColor=Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], 0.0))) + if titleSeq: + self.titleSeq = Sequence(titleSeq) + self.titleSeq.start() + + def showTitleText(self): + self.titleText.show() + + def hideTitleText(self): + if self.titleText: + self.titleText.hide() + + def showSmallTitle(self): + if self.titleText: + self.titleText.hide() + if self.smallTitleText: + self.smallTitleText.show() + + def hideSmallTitleText(self): + if self.smallTitleText: + self.smallTitleText.hide() + + def startOuch(self, ouchLevel, period = 2): + self.notify.debug('startOuch %s' % ouchLevel) + if not hasattr(self, 'doingOuch'): + + def doOuch(task, self = self, ouchLevel = ouchLevel, period = period): + self.b_setOuch(ouchLevel) + self.lastOuchTime = globalClock.getFrameTime() + taskMgr.doMethodLater(period, doOuch, DistributedLevel.OuchTaskName) + + delay = 0 + if hasattr(self, 'lastOuchTime'): + curFrameTime = globalClock.getFrameTime() + timeSinceLastOuch = curFrameTime - self.lastOuchTime + if timeSinceLastOuch < period: + delay = period - timeSinceLastOuch + if delay > 0: + taskMgr.doMethodLater(period, doOuch, DistributedLevel.OuchTaskName) + else: + doOuch(None) + self.doingOuch = 1 + return + + def stopOuch(self): + if hasattr(self, 'doingOuch'): + taskMgr.remove(DistributedLevel.OuchTaskName) + del self.doingOuch + + def b_setOuch(self, penalty, anim = None): + self.notify.debug('b_setOuch %s' % penalty) + av = base.localAvatar + if not av.isStunned: + self.d_setOuch(penalty) + self.setOuch(penalty, anim) + + def d_setOuch(self, penalty): + self.sendUpdate('setOuch', [penalty]) + + def setOuch(self, penalty, anim = None): + if anim == 'Squish': + if base.cr.playGame.getPlace(): + base.cr.playGame.getPlace().fsm.request('squished') + elif anim == 'Fall': + if base.cr.playGame.getPlace(): + base.cr.playGame.getPlace().fsm.request('fallDown') + av = base.localAvatar + av.stunToon() + av.playDialogueForString('!') + + def complexVis(self): + return 1 diff --git a/otp/level/DistributedLevelAI.py b/otp/level/DistributedLevelAI.py new file mode 100755 index 00000000..3b62d964 --- /dev/null +++ b/otp/level/DistributedLevelAI.py @@ -0,0 +1,98 @@ +from otp.ai.AIBaseGlobal import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObjectAI +import Level +from direct.directnotify import DirectNotifyGlobal +import EntityCreatorAI +from direct.showbase.PythonUtil import Functor, weightedChoice + +class DistributedLevelAI(DistributedObjectAI.DistributedObjectAI, Level.Level): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevelAI') + + def __init__(self, air, zoneId, entranceId, avIds): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + Level.Level.__init__(self) + self.zoneId = zoneId + self.entranceId = entranceId + if len(avIds) <= 0 or len(avIds) > 4: + self.notify.warning('How do we have this many avIds? avIds: %s' % avIds) + self.avIdList = avIds + self.numPlayers = len(self.avIdList) + self.presentAvIds = list(self.avIdList) + self.notify.debug('expecting avatars: %s' % str(self.avIdList)) + + def setLevelSpec(self, levelSpec): + self.levelSpec = levelSpec + + def generate(self, levelSpec = None): + self.notify.debug('generate') + DistributedObjectAI.DistributedObjectAI.generate(self) + if levelSpec == None: + levelSpec = self.levelSpec + self.initializeLevel(levelSpec) + self.sendUpdate('setZoneIds', [self.zoneIds]) + self.sendUpdate('setStartTimestamp', [self.startTimestamp]) + + def getLevelZoneId(self): + return self.zoneId + + def getAvIds(self): + return self.avIdList + + def getEntranceId(self): + return self.entranceId + + def getBattleCreditMultiplier(self): + return 1.0 + + def delete(self, deAllocZone = True): + self.notify.debug('delete') + self.destroyLevel() + self.ignoreAll() + if deAllocZone: + self.air.deallocateZone(self.zoneId) + DistributedObjectAI.DistributedObjectAI.delete(self) + + def initializeLevel(self, levelSpec): + self.startTime = globalClock.getRealTime() + self.startTimestamp = globalClockDelta.localToNetworkTime(self.startTime, bits=32) + lol = zip([1] * levelSpec.getNumScenarios(), range(levelSpec.getNumScenarios())) + scenarioIndex = weightedChoice(lol) + Level.Level.initializeLevel(self, self.doId, levelSpec, scenarioIndex) + for avId in self.avIdList: + self.acceptOnce(self.air.getAvatarExitEvent(avId), Functor(self.handleAvatarDisconnect, avId)) + + self.allToonsGoneBarrier = self.beginBarrier('allToonsGone', self.avIdList, 3 * 24 * 60 * 60, self.allToonsGone) + + def handleAvatarDisconnect(self, avId): + try: + self.presentAvIds.remove(avId) + DistributedLevelAI.notify.warning('av %s has disconnected' % avId) + except: + DistributedLevelAI.notify.warning('got disconnect for av %s, not in list' % avId) + + if not self.presentAvIds: + self.allToonsGone([]) + + def allToonsGone(self, toonsThatCleared): + DistributedLevelAI.notify.info('allToonsGone') + if hasattr(self, 'allToonsGoneBarrier'): + self.ignoreBarrier(self.allToonsGoneBarrier) + del self.allToonsGoneBarrier + for avId in self.avIdList: + self.ignore(self.air.getAvatarExitEvent(avId)) + + self.requestDelete() + + def createEntityCreator(self): + return EntityCreatorAI.EntityCreatorAI(level=self) + + def setOuch(self, penalty): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + self.notify.debug('setOuch %s' % penalty) + if av and penalty > 0: + av.takeDamage(penalty) + if av.getHp() <= 0: + av.inventory.zeroInv() + av.d_setInventory(av.inventory.makeNetString()) diff --git a/otp/level/Entity.py b/otp/level/Entity.py new file mode 100755 index 00000000..8e2e65bf --- /dev/null +++ b/otp/level/Entity.py @@ -0,0 +1,87 @@ +from direct.showbase.DirectObject import DirectObject +from direct.showbase.PythonUtil import lineInfo +import string +from direct.directnotify import DirectNotifyGlobal + +class Entity(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('Entity') + + def __init__(self, level = None, entId = None): + self.initializeEntity(level, entId) + + def initializeEntity(self, level, entId): + self.level = level + self.entId = entId + if self.level is not None and self.entId is not None: + self.level.initializeEntity(self) + return + + def __str__(self): + if hasattr(self, 'level') and self.level: + return 'ent%s(%s)' % (self.entId, self.level.getEntityType(self.entId)) + elif hasattr(self, 'name'): + return self.name + elif hasattr(self, 'entId'): + return '%s-%s' % (self.__class__.__name__, self.entId) + else: + return self.__class__.__name__ + + def destroy(self): + Entity.notify.debug('Entity.destroy() %s' % self.entId) + if self.level: + if self.level.isInitialized(): + self.level.onEntityDestroy(self.entId) + else: + Entity.notify.warning('Entity %s destroyed after level??' % self.entId) + self.ignoreAll() + del self.level + del self.entId + + def getUniqueName(self, name, entId = None): + if entId is None: + entId = self.entId + return '%s-%s-%s' % (name, self.level.levelId, entId) + + def getParentToken(self): + return self.level.getParentTokenForEntity(self.entId) + + def getOutputEventName(self, entId = None): + if entId is None: + entId = self.entId + return self.getUniqueName('entityOutput', entId) + + def getZoneEntId(self): + return self.level.getEntityZoneEntId(self.entId) + + def getZoneEntity(self): + return self.level.getEntity(self.getZoneEntId()) + + def getZoneNode(self): + return self.getZoneEntity().getNodePath() + + def privGetSetter(self, attrib): + setFuncName = 'set%s%s' % (attrib[0].upper(), attrib[1:]) + if hasattr(self, setFuncName): + return getattr(self, setFuncName) + return None + + def callSetters(self, *attribs): + self.privCallSetters(0, *attribs) + + def callSettersAndDelete(self, *attribs): + self.privCallSetters(1, *attribs) + + def privCallSetters(self, doDelete, *attribs): + for attrib in attribs: + if hasattr(self, attrib): + setter = self.privGetSetter(attrib) + if setter is not None: + value = getattr(self, attrib) + if doDelete: + delattr(self, attrib) + setter(value) + + return + + def setAttribInit(self, attrib, value): + self.__dict__[attrib] = value diff --git a/otp/level/EntityCreator.py b/otp/level/EntityCreator.py new file mode 100755 index 00000000..6d0a384a --- /dev/null +++ b/otp/level/EntityCreator.py @@ -0,0 +1,47 @@ +import CutScene +import EntityCreatorBase +import BasicEntities +from direct.directnotify import DirectNotifyGlobal +import EntrancePoint +import LevelMgr +import LogicGate +import ZoneEntity +import ModelEntity +import PathEntity +import VisibilityExtender +import PropSpinner +import AmbientSound +import LocatorEntity +import CollisionSolidEntity + +def nothing(*args): + return 'nothing' + + +def nonlocal(*args): + return 'nonlocal' + + +class EntityCreator(EntityCreatorBase.EntityCreatorBase): + + def __init__(self, level): + EntityCreatorBase.EntityCreatorBase.__init__(self, level) + self.level = level + self.privRegisterTypes({'attribModifier': nothing, + 'ambientSound': AmbientSound.AmbientSound, + 'collisionSolid': CollisionSolidEntity.CollisionSolidEntity, + 'cutScene': CutScene.CutScene, + 'entityGroup': nothing, + 'entrancePoint': EntrancePoint.EntrancePoint, + 'levelMgr': LevelMgr.LevelMgr, + 'locator': LocatorEntity.LocatorEntity, + 'logicGate': LogicGate.LogicGate, + 'model': ModelEntity.ModelEntity, + 'nodepath': BasicEntities.NodePathEntity, + 'path': PathEntity.PathEntity, + 'propSpinner': PropSpinner.PropSpinner, + 'visibilityExtender': VisibilityExtender.VisibilityExtender, + 'zone': ZoneEntity.ZoneEntity}) + + def doCreateEntity(self, ctor, entId): + return ctor(self.level, entId) diff --git a/otp/level/EntityCreatorAI.py b/otp/level/EntityCreatorAI.py new file mode 100755 index 00000000..57ae4dcc --- /dev/null +++ b/otp/level/EntityCreatorAI.py @@ -0,0 +1,46 @@ +import EntityCreatorBase +import LogicGate +import LevelMgrAI +import ZoneEntityAI +from direct.showbase.PythonUtil import Functor + +def createDistributedEntity(AIclass, level, entId, zoneId): + ent = AIclass(level, entId) + ent.generateWithRequired(zoneId) + return ent + + +def createLocalEntity(AIclass, level, entId, zoneId): + ent = AIclass(level, entId) + return ent + + +def nothing(*args): + return 'nothing' + + +class EntityCreatorAI(EntityCreatorBase.EntityCreatorBase): + + def __init__(self, level): + EntityCreatorBase.EntityCreatorBase.__init__(self, level) + cLE = createLocalEntity + self.privRegisterTypes({'attribModifier': nothing, + 'ambientSound': nothing, + 'collisionSolid': nothing, + 'cutScene': nothing, + 'entityGroup': nothing, + 'entrancePoint': nothing, + 'levelMgr': Functor(cLE, LevelMgrAI.LevelMgrAI), + 'locator': nothing, + 'logicGate': Functor(cLE, LogicGate.LogicGate), + 'model': nothing, + 'nodepath': nothing, + 'path': nothing, + 'propSpinner': nothing, + 'visibilityExtender': nothing, + 'zone': Functor(cLE, ZoneEntityAI.ZoneEntityAI)}) + + def doCreateEntity(self, ctor, entId): + zoneId = self.level.getEntityZoneId(entId) + self.notify.debug('creating entity %s in zone %s' % (entId, zoneId)) + return ctor(self.level, entId, zoneId) diff --git a/otp/level/EntityCreatorBase.py b/otp/level/EntityCreatorBase.py new file mode 100755 index 00000000..904e78ce --- /dev/null +++ b/otp/level/EntityCreatorBase.py @@ -0,0 +1,27 @@ +from direct.directnotify import DirectNotifyGlobal + +class EntityCreatorBase: + notify = DirectNotifyGlobal.directNotify.newCategory('EntityCreator') + + def __init__(self, level): + self.level = level + self.entType2Ctor = {} + + def createEntity(self, entId): + entType = self.level.getEntityType(entId) + if entType not in self.entType2Ctor: + self.notify.error('unknown entity type: %s (ent%s)' % (entType, entId)) + ent = self.doCreateEntity(self.entType2Ctor[entType], entId) + return ent + + def getEntityTypes(self): + return self.entType2Ctor.keys() + + def privRegisterType(self, entType, ctor): + if entType in self.entType2Ctor: + self.notify.debug('replacing %s ctor %s with %s' % (entType, self.entType2Ctor[entType], ctor)) + self.entType2Ctor[entType] = ctor + + def privRegisterTypes(self, type2ctor): + for entType, ctor in type2ctor.items(): + self.privRegisterType(entType, ctor) diff --git a/otp/level/EntityStateVarSet.py b/otp/level/EntityStateVarSet.py new file mode 100755 index 00000000..8c471aeb --- /dev/null +++ b/otp/level/EntityStateVarSet.py @@ -0,0 +1,35 @@ +from direct.fsm.StatePush import StateVar +from direct.showbase.PythonUtil import getSetterName +from otp.level.Entity import Entity + +class EntityStateVarSet(Entity): + + def __init__(self, entType): + self._entType = entType + self._attribNames = [] + for attrib in self._entType.attribs: + name, defaultVal, type = attrib + self._addAttrib(name, defaultVal, type) + + def initializeEntity(self, level, entId): + stateVars = {} + for attribName in self._attribNames: + stateVars[attribName] = getattr(self, attribName) + + Entity.initializeEntity(self, level, entId) + for attribName in self._attribNames: + stateVars[attribName].set(getattr(self, attribName)) + + for attribName in self._attribNames: + setattr(self, attribName, stateVars[attribName]) + + def _getAttributeNames(self): + return self._attribNames[:] + + def _setter(self, name, value): + getattr(self, name).set(value) + + def _addAttrib(self, name, defaultVal, type): + setattr(self, name, StateVar(defaultVal)) + setattr(self, getSetterName(name), Functor(self._setter, name)) + self._attribNames.append(name) diff --git a/otp/level/EntityTypeDesc.py b/otp/level/EntityTypeDesc.py new file mode 100755 index 00000000..55eb0b99 --- /dev/null +++ b/otp/level/EntityTypeDesc.py @@ -0,0 +1,85 @@ +from direct.directnotify import DirectNotifyGlobal +import AttribDesc +from direct.showbase.PythonUtil import mostDerivedLast + +class EntityTypeDesc: + notify = DirectNotifyGlobal.directNotify.newCategory('EntityTypeDesc') + output = None + + def __init__(self): + self.__class__.privCompileAttribDescs(self.__class__) + self.attribNames = [] + self.attribDescDict = {} + attribDescs = self.__class__._attribDescs + for desc in attribDescs: + attribName = desc.getName() + self.attribNames.append(attribName) + self.attribDescDict[attribName] = desc + + def isConcrete(self): + return 'abstract' not in self.__class__.__dict__ + + def isPermanent(self): + return 'permanent' in self.__class__.__dict__ + + def getOutputType(self): + return self.output + + def getAttribNames(self): + return self.attribNames + + def getAttribDescDict(self): + return self.attribDescDict + + def getAttribsOfType(self, type): + names = [] + for attribName, desc in self.attribDescDict.items(): + if desc.getDatatype() == type: + names.append(attribName) + + return names + + @staticmethod + def privCompileAttribDescs(entTypeClass): + if '_attribDescs' in entTypeClass.__dict__: + return + c = entTypeClass + EntityTypeDesc.notify.debug('compiling attrib descriptors for %s' % c.__name__) + for base in c.__bases__: + EntityTypeDesc.privCompileAttribDescs(base) + + blockAttribs = c.__dict__.get('blockAttribs', []) + baseADs = [] + bases = list(c.__bases__) + mostDerivedLast(bases) + for base in bases: + for desc in base._attribDescs: + if desc.getName() in blockAttribs: + continue + for d in baseADs: + if desc.getName() == d.getName(): + EntityTypeDesc.notify.warning('%s inherits attrib %s from multiple bases' % (c.__name__, desc.getName())) + break + else: + baseADs.append(desc) + + attribDescs = [] + if 'attribs' in c.__dict__: + for attrib in c.attribs: + desc = AttribDesc.AttribDesc(*attrib) + if desc.getName() == 'type' and entTypeClass.__name__ != 'Entity': + EntityTypeDesc.notify.error("(%s): '%s' is a reserved attribute name" % (entTypeClass.__name__, desc.getName())) + for ad in baseADs: + if ad.getName() == desc.getName(): + baseADs.remove(ad) + break + + attribDescs.append(desc) + + c._attribDescs = baseADs + attribDescs + + def __str__(self): + return str(self.__class__) + + def __repr__(self): + return str(self.__class__.__dict__.get('type', None)) + str(self.output) + str(self.attribDescDict) diff --git a/otp/level/EntityTypes.py b/otp/level/EntityTypes.py new file mode 100755 index 00000000..31dcbfea --- /dev/null +++ b/otp/level/EntityTypes.py @@ -0,0 +1,164 @@ +from EntityTypeDesc import EntityTypeDesc +from toontown.coghq.SpecImports import * + +class Entity(EntityTypeDesc): + abstract = 1 + type = 'entity' + attribs = (('type', None, 'const'), + ('name', '', 'string'), + ('comment', '', 'string'), + ('parentEntId', 0, 'entId')) + + +class LevelMgr(Entity): + type = 'levelMgr' + permanent = 1 + attribs = (('name', 'LevelMgr', 'const'), ('parentEntId', 0, 'const'), ('modelFilename', '', 'const'), ('removeNodes', [], 'const')) + + +class AttribModifier(Entity): + type = 'attribModifier' + attribs = (('recursive', 0, 'bool'), + ('typeName', '', 'string'), + ('attribName', '', 'string'), + ('value', '', 'string')) + + +class Locator(Entity): + type = 'locator' + attribs = (('searchPath', '', 'string'),) + + +class Nodepath(Entity): + type = 'nodepath' + attribs = (('parentEntId', + 0, + 'entId', + {'type': 'nodepath'}), + ('pos', Point3(0, 0, 0), 'pos'), + ('hpr', Vec3(0, 0, 0), 'hpr'), + ('scale', 1, 'scale')) + + +class Zone(Nodepath): + type = 'zone' + permanent = 1 + blockAttribs = ('pos', 'hpr') + attribs = (('parentEntId', 0, 'const'), ('description', '', 'string'), ('visibility', [], 'visZoneList')) + + +class EntrancePoint(Nodepath): + type = 'entrancePoint' + attribs = (('entranceId', -1, 'int'), ('radius', + 15, + 'float', + {'min': 0}), ('theta', + 20, + 'float', + {'min': 0})) + + +class LogicGate(Entity): + type = 'logicGate' + output = 'bool' + attribs = (('input1Event', + 0, + 'entId', + {'output': 'bool'}), + ('input2Event', + 0, + 'entId', + {'output': 'bool'}), + ('isInput1', 0, 'bool'), + ('isInput2', 0, 'bool'), + ('logicType', + 'or', + 'choice', + {'choiceSet': ['or', + 'and', + 'xor', + 'nand', + 'nor', + 'xnor']})) + + +class CutScene(Entity): + type = 'cutScene' + output = 'bool' + attribs = (('pos', Point3(0, 0, 0), 'pos'), + ('hpr', Vec3(0, 0, 0), 'hpr'), + ('startStopEvent', + 0, + 'entId', + {'output': 'bool'}), + ('effect', + 'irisInOut', + 'choice', + {'choiceSet': ['nothing', 'irisInOut', 'letterBox']}), + ('motion', + 'foo1', + 'choice', + {'choiceSet': ['foo1']}), + ('duration', 5.0, 'float')) + + +class CollisionSolid(Nodepath): + type = 'collisionSolid' + attribs = (('solidType', + 'sphere', + 'choice', + {'choiceSet': ['sphere', 'tube']}), + ('radius', 1.0, 'float'), + ('length', 0.0, 'float'), + ('showSolid', 0, 'bool')) + + +class Model(Nodepath): + type = 'model' + attribs = (('loadType', + 'loadModelCopy', + 'choice', + {'choiceSet': ['loadModelCopy', 'loadModel', 'loadModelOnce']}), + ('modelPath', None, 'bamfilename'), + ('flattenType', + 'light', + 'choice', + {'choiceSet': ['none', + 'light', + 'medium', + 'strong']}), + ('collisionsOnly', 0, 'bool'), + ('goonHatType', + 'none', + 'choice', + {'choiceSet': ['none', 'hardhat', 'security']})) + + +class Path(Nodepath): + type = 'path' + attribs = (('pathIndex', 0, 'int'), ('pathScale', 1.0, 'float')) + + +class VisibilityExtender(Entity): + type = 'visibilityExtender' + attribs = (('event', + None, + 'entId', + {'output': 'bool'}), ('newZones', [], 'visZoneList')) + + +class AmbientSound(Nodepath): + type = 'ambientSound' + attribs = (('soundPath', '', 'bamfilename'), ('volume', + 1, + 'float', + {'min': 0, + 'max': 1}), ('enabled', 1, 'bool')) + + +class PropSpinner(Entity): + type = 'propSpinner' + + +class EntityGroup(Entity): + type = 'entityGroup' diff --git a/otp/level/EntrancePoint.py b/otp/level/EntrancePoint.py new file mode 100755 index 00000000..1ba2aa90 --- /dev/null +++ b/otp/level/EntrancePoint.py @@ -0,0 +1,33 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +import BasicEntities + +class EntrancePoint(BasicEntities.NodePathEntity): + + def __init__(self, level, entId): + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.rotator = self.attachNewNode('rotator') + self.placer = self.rotator.attachNewNode('placer') + self.initEntrancePoint() + + def destroy(self): + self.destroyEntrancePoint() + self.placer.removeNode() + self.rotator.removeNode() + del self.placer + del self.rotator + BasicEntities.NodePathEntity.destroy(self) + + def placeToon(self, toon, toonIndex, numToons): + self.placer.setY(-self.radius) + self.rotator.setH(-self.theta * (numToons - 1) * 0.5 + toonIndex * self.theta) + toon.setPosHpr(self.placer, 0, 0, 0, 0, 0, 0) + + def initEntrancePoint(self): + if self.entranceId >= 0: + self.level.entranceId2entity[self.entranceId] = self + + def destroyEntrancePoint(self): + if self.entranceId >= 0: + if self.entranceId in self.level.entranceId2entity: + del self.level.entranceId2entity[self.entranceId] diff --git a/otp/level/Level.py b/otp/level/Level.py new file mode 100755 index 00000000..1b2d16d9 --- /dev/null +++ b/otp/level/Level.py @@ -0,0 +1,219 @@ +from direct.directnotify import DirectNotifyGlobal +import string +import LevelConstants +from direct.showbase.PythonUtil import lineInfo, uniqueElements +import types + +class Level: + notify = DirectNotifyGlobal.directNotify.newCategory('Level') + + def __init__(self): + self.levelSpec = None + self.initialized = 0 + return + + def initializeLevel(self, levelId, levelSpec, scenarioIndex): + self.levelId = levelId + self.levelSpec = levelSpec + self.scenarioIndex = scenarioIndex + self.levelSpec.setScenario(self.scenarioIndex) + self.entranceId2entity = {} + self.entId2createCallbacks = {} + self.createdEntIds = [] + self.nonlocalEntIds = {} + self.nothingEntIds = {} + self.entityCreator = self.createEntityCreator() + self.entType2ids = self.levelSpec.getEntType2ids(self.levelSpec.getAllEntIds()) + for entType in self.entityCreator.getEntityTypes(): + self.entType2ids.setdefault(entType, []) + + self.createAllEntities(priorityTypes=['levelMgr', 'zone', 'propSpinner']) + self.levelMgrEntity = self.getEntity(LevelConstants.LevelMgrEntId) + self.uberZoneEntity = self.getEntity(LevelConstants.UberZoneEntId) + self.initialized = 1 + + def isInitialized(self): + return self.initialized + + def getLevelId(self): + return self.levelId + + def destroyLevel(self): + self.destroyAllEntities() + if self.initialized: + del self.levelMgrEntity + del self.uberZoneEntity + del self.entityCreator + del self.entId2createCallbacks + del self.entranceId2entity + self.levelSpec.destroy() + del self.levelSpec + self.initialized = 0 + del self.createdEntIds + del self.nonlocalEntIds + del self.nothingEntIds + if hasattr(self, 'entities'): + del self.entities + if hasattr(self, 'levelSpec'): + self.levelSpec.destroy() + del self.levelSpec + + def createEntityCreator(self): + Level.notify.error('concrete Level class must override %s' % lineInfo()[2]) + + def createAllEntities(self, priorityTypes = []): + self.entities = {} + entTypes = self.entityCreator.getEntityTypes() + self.onLevelPreCreate() + for type in priorityTypes: + self.createAllEntitiesOfType(type) + entTypes.remove(type) + + for type in entTypes: + self.createAllEntitiesOfType(type) + + self.onLevelPostCreate() + + def destroyAllEntities(self): + self.nonlocalEntIds = {} + self.nothingEntIds = {} + if not uniqueElements(self.createdEntIds): + Level.notify.warning('%s: self.createdEntIds is not unique: %s' % (getattr(self, 'doId', None), self.createdEntIds)) + while len(self.createdEntIds) > 0: + entId = self.createdEntIds.pop() + entity = self.getEntity(entId) + if entity is not None: + Level.notify.debug('destroying %s %s' % (self.getEntityType(entId), entId)) + entity.destroy() + else: + Level.notify.error('trying to destroy entity %s, but it is already gone' % entId) + + return + + def createAllEntitiesOfType(self, entType): + self.onEntityTypePreCreate(entType) + for entId in self.entType2ids[entType]: + self.createEntity(entId) + + self.onEntityTypePostCreate(entType) + + def createEntity(self, entId): + spec = self.levelSpec.getEntitySpec(entId) + Level.notify.debug('creating %s %s' % (spec['type'], entId)) + entity = self.entityCreator.createEntity(entId) + announce = False + if entity is 'nonlocal': + self.nonlocalEntIds[entId] = None + elif entity is 'nothing': + self.nothingEntIds[entId] = None + announce = True + else: + self.createdEntIds.append(entId) + announce = True + if announce: + self.onEntityCreate(entId) + return entity + + def initializeEntity(self, entity): + entId = entity.entId + spec = self.levelSpec.getEntitySpec(entId) + for key, value in spec.items(): + if key in ('type', 'name', 'comment'): + continue + entity.setAttribInit(key, value) + + self.entities[entId] = entity + + def getEntity(self, entId): + if hasattr(self, 'entities'): + return self.entities.get(entId) + else: + return None + return None + + def getEntityType(self, entId): + return self.levelSpec.getEntityType(entId) + + def getEntityZoneEntId(self, entId): + return self.levelSpec.getEntityZoneEntId(entId) + + def getEntityZoneId(self, entId): + zoneEntId = self.getEntityZoneEntId(entId) + if not hasattr(self, 'zoneNum2zoneId'): + return None + return self.zoneNum2zoneId.get(zoneEntId) + + def getZoneId(self, zoneEntId): + return self.zoneNum2zoneId[zoneEntId] + + def getZoneNumFromId(self, zoneId): + return self.zoneId2zoneNum[zoneId] + + def getParentTokenForEntity(self, entId): + return entId + + def getLevelPreCreateEvent(self): + return 'levelPreCreate-%s' % self.levelId + + def getLevelPostCreateEvent(self): + return 'levelPostCreate-%s' % self.levelId + + def getEntityTypePreCreateEvent(self, entType): + return 'entityTypePreCreate-%s-%s' % (self.levelId, entType) + + def getEntityTypePostCreateEvent(self, entType): + return 'entityTypePostCreate-%s-%s' % (self.levelId, entType) + + def getEntityCreateEvent(self, entId): + return 'entityCreate-%s-%s' % (self.levelId, entId) + + def getEntityOfTypeCreateEvent(self, entType): + return 'entityOfTypeCreate-%s-%s' % (self.levelId, entType) + + def onLevelPreCreate(self): + messenger.send(self.getLevelPreCreateEvent()) + + def onLevelPostCreate(self): + messenger.send(self.getLevelPostCreateEvent()) + + def onEntityTypePreCreate(self, entType): + messenger.send(self.getEntityTypePreCreateEvent(entType)) + + def onEntityTypePostCreate(self, entType): + messenger.send(self.getEntityTypePostCreateEvent(entType)) + + def onEntityCreate(self, entId): + messenger.send(self.getEntityCreateEvent(entId)) + messenger.send(self.getEntityOfTypeCreateEvent(self.getEntityType(entId)), [entId]) + if entId in self.entId2createCallbacks: + for callback in self.entId2createCallbacks[entId]: + callback() + + del self.entId2createCallbacks[entId] + + def setEntityCreateCallback(self, entId, callback): + ent = self.getEntity(entId) + if ent is not None: + callNow = True + elif entId in self.nothingEntIds: + callNow = True + else: + callNow = False + if callNow: + callback() + else: + self.entId2createCallbacks.setdefault(entId, []) + self.entId2createCallbacks[entId].append(callback) + return + + def getEntityDestroyEvent(self, entId): + return 'entityDestroy-%s-%s' % (self.levelId, entId) + + def onEntityDestroy(self, entId): + messenger.send(self.getEntityDestroyEvent(entId)) + del self.entities[entId] + if entId in self.createdEntIds: + self.createdEntIds.remove(entId) + + def handleVisChange(self): + pass diff --git a/otp/level/LevelConstants.py b/otp/level/LevelConstants.py new file mode 100755 index 00000000..df5ddd00 --- /dev/null +++ b/otp/level/LevelConstants.py @@ -0,0 +1,4 @@ +MinZoneNum = 0 +MaxZoneNum = 999 +UberZoneEntId = 0 +LevelMgrEntId = 1000 diff --git a/otp/level/LevelMgr.py b/otp/level/LevelMgr.py new file mode 100755 index 00000000..009675f5 --- /dev/null +++ b/otp/level/LevelMgr.py @@ -0,0 +1,48 @@ +from direct.showbase.PythonUtil import Functor +import LevelMgrBase + +class LevelMgr(LevelMgrBase.LevelMgrBase): + + def __init__(self, level, entId): + LevelMgrBase.LevelMgrBase.__init__(self, level, entId) + self.geom = loader.loadModel(self.modelFilename) + + if hasattr(self, 'removeNodes'): + for node in self.removeNodes: + self.geom.find(node).removeNode() + + self.zoneNums = [] + self.level.zoneNum2zoneId = {} + self.level.zoneId2zoneNum = {} + self.accept(self.level.getEntityOfTypeCreateEvent('zone'), self.handleZoneCreated) + + def destroy(self): + del self.level.zoneIds + del self.level.zoneId2zoneNum + del self.level.zoneNum2zoneId + self.geom.removeNode() + del self.geom + LevelMgrBase.LevelMgrBase.destroy(self) + + def handleZoneCreated(self, entId): + zoneEnt = self.level.getEntity(entId) + self.zoneNums.append(zoneEnt.entId) + self.privAssignZoneIds() + self.accept(self.level.getEntityDestroyEvent(entId), Functor(self.handleZoneDestroy, entId)) + + def handleZoneDestroy(self, entId): + zoneEnt = self.level.getEntity(entId) + del self.level.zoneId2zoneNum[self.level.zoneNum2zoneId[zoneEnt.entId]] + del self.level.zoneNum2zoneId[zoneEnt.entId] + self.zoneNums.remove(zoneEnt.entId) + self.privAssignZoneIds() + + def privAssignZoneIds(self): + self.zoneNums.sort() + for i in xrange(len(self.zoneNums)): + zoneNum = self.zoneNums[i] + zoneEnt = self.level.getEntity(zoneNum) + zoneId = self.level.zoneIds[i] + zoneEnt.setZoneId(zoneId) + self.level.zoneNum2zoneId[zoneNum] = zoneId + self.level.zoneId2zoneNum[zoneId] = zoneNum diff --git a/otp/level/LevelMgrAI.py b/otp/level/LevelMgrAI.py new file mode 100755 index 00000000..13700191 --- /dev/null +++ b/otp/level/LevelMgrAI.py @@ -0,0 +1,33 @@ +from direct.showbase.PythonUtil import Functor +import LevelMgrBase + +class LevelMgrAI(LevelMgrBase.LevelMgrBase): + + def __init__(self, level, entId): + LevelMgrBase.LevelMgrBase.__init__(self, level, entId) + self.level.zoneNum2zoneId = {} + self.level.zoneIds = [] + self.accept(self.level.getEntityOfTypeCreateEvent('zone'), self.handleZoneCreated) + + def destroy(self): + del self.level.zoneIds + del self.level.zoneNum2zoneId + LevelMgrBase.LevelMgrBase.destroy(self) + + def handleZoneCreated(self, entId): + zoneEnt = self.level.getEntity(entId) + self.level.zoneNum2zoneId[zoneEnt.entId] = zoneEnt.getZoneId() + self.privCreateSortedZoneIdList() + self.accept(self.level.getEntityDestroyEvent(entId), Functor(self.handleZoneDestroy, entId)) + + def handleZoneDestroy(self, entId): + zoneEnt = self.level.getEntity(entId) + del self.level.zoneNum2zoneId[zoneEnt.entId] + self.privCreateSortedZoneIdList() + + def privCreateSortedZoneIdList(self): + zoneNums = self.level.zoneNum2zoneId.keys() + zoneNums.sort() + self.level.zoneIds = [] + for zoneNum in zoneNums: + self.level.zoneIds.append(self.level.zoneNum2zoneId[zoneNum]) diff --git a/otp/level/LevelMgrBase.py b/otp/level/LevelMgrBase.py new file mode 100755 index 00000000..8a206944 --- /dev/null +++ b/otp/level/LevelMgrBase.py @@ -0,0 +1,10 @@ +import Entity + +class LevelMgrBase(Entity.Entity): + + def __init__(self, level, entId): + Entity.Entity.__init__(self, level, entId) + + def destroy(self): + Entity.Entity.destroy(self) + self.ignoreAll() diff --git a/otp/level/LevelSpec.py b/otp/level/LevelSpec.py new file mode 100755 index 00000000..0cb481fb --- /dev/null +++ b/otp/level/LevelSpec.py @@ -0,0 +1,84 @@ +from pandac import PandaModules as PM +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.PythonUtil import list2dict, uniqueElements +import LevelConstants, types + +class LevelSpec: + notify = DirectNotifyGlobal.directNotify.newCategory('LevelSpec') + SystemEntIds = (LevelConstants.UberZoneEntId, LevelConstants.LevelMgrEntId) + + def __init__(self, spec = None, scenario = 0): + newSpec = 0 + if type(spec) is types.ModuleType: + self.specDict = spec.levelSpec + elif type(spec) is types.DictType: + self.specDict = spec + self.entId2specDict = {} + self.entId2specDict.update(list2dict(self.getGlobalEntIds(), value=self.privGetGlobalEntityDict())) + for i in xrange(self.getNumScenarios()): + self.entId2specDict.update(list2dict(self.getScenarioEntIds(i), value=self.privGetScenarioEntityDict(i))) + + self.setScenario(scenario) + + def destroy(self): + del self.specDict + del self.entId2specDict + del self.scenario + if hasattr(self, 'level'): + del self.level + + def getNumScenarios(self): + return len(self.specDict['scenarios']) + + def setScenario(self, scenario): + self.scenario = scenario + + def getScenario(self): + return self.scenario + + def getGlobalEntIds(self): + return self.privGetGlobalEntityDict().keys() + + def getScenarioEntIds(self, scenario = None): + if scenario is None: + scenario = self.scenario + return self.privGetScenarioEntityDict(scenario).keys() + + def getAllEntIds(self): + return self.getGlobalEntIds() + self.getScenarioEntIds() + + def getAllEntIdsFromAllScenarios(self): + entIds = self.getGlobalEntIds() + for scenario in xrange(self.getNumScenarios()): + entIds.extend(self.getScenarioEntIds(scenario)) + + return entIds + + def getEntitySpec(self, entId): + specDict = self.entId2specDict[entId] + return specDict[entId] + + def getEntityType(self, entId): + return self.getEntitySpec(entId)['type'] + + def getEntityZoneEntId(self, entId): + spec = self.getEntitySpec(entId) + type = spec['type'] + if type == 'zone': + return entId + return self.getEntityZoneEntId(spec['parentEntId']) + + def getEntType2ids(self, entIds): + entType2ids = {} + for entId in entIds: + type = self.getEntityType(entId) + entType2ids.setdefault(type, []) + entType2ids[type].append(entId) + + return entType2ids + + def privGetGlobalEntityDict(self): + return self.specDict['globalEntities'] + + def privGetScenarioEntityDict(self, scenario): + return self.specDict['scenarios'][scenario] diff --git a/otp/level/LevelUtil.py b/otp/level/LevelUtil.py new file mode 100755 index 00000000..93d692b6 --- /dev/null +++ b/otp/level/LevelUtil.py @@ -0,0 +1,40 @@ +import string +import LevelConstants + +def getZoneNum2Node(levelModel, logFunc = lambda str: str): + + def findNumberedNodes(baseString, model, caseInsens = 1): + srch = '**/%s*' % baseString + if caseInsens: + srch += ';+i' + potentialNodes = model.findAllMatches(srch) + num2node = {} + for potentialNode in potentialNodes: + name = potentialNode.getName() + logFunc('potential match for %s: %s' % (baseString, name)) + name = name[len(baseString):] + numDigits = 0 + while numDigits < len(name): + if name[numDigits] not in string.digits: + break + numDigits += 1 + + if numDigits == 0: + continue + num = int(name[:numDigits]) + if num == LevelConstants.UberZoneEntId: + logFunc('warning: cannot use UberZone zoneNum (%s). ignoring %s' % (LevelConstants.UberZoneEntId, potentialNode)) + continue + if num < LevelConstants.MinZoneNum or num > LevelConstants.MaxZoneNum: + logFunc('warning: zone %s is out of range. ignoring %s' % (num, potentialNode)) + continue + if num in num2node: + logFunc('warning: zone %s already assigned to %s. ignoring %s' % (num, num2node[num], potentialNode)) + continue + num2node[num] = potentialNode + + return num2node + + zoneNum2node = findNumberedNodes('zone', levelModel) + zoneNum2node[LevelConstants.UberZoneEntId] = levelModel + return zoneNum2node diff --git a/otp/level/LocatorEntity.py b/otp/level/LocatorEntity.py new file mode 100755 index 00000000..e0f14dbc --- /dev/null +++ b/otp/level/LocatorEntity.py @@ -0,0 +1,28 @@ +import Entity, BasicEntities +from pandac.PandaModules import NodePath +from direct.directnotify import DirectNotifyGlobal + +class LocatorEntity(Entity.Entity, NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('LocatorEntity') + + def __init__(self, level, entId): + node = hidden.attachNewNode('LocatorEntity-%s' % entId) + NodePath.__init__(self, node) + Entity.Entity.__init__(self, level, entId) + self.doReparent() + + def destroy(self): + Entity.Entity.destroy(self) + self.removeNode() + + def getNodePath(self): + return self + + def doReparent(self): + if self.searchPath != '': + parent = self.level.geom.find(self.searchPath) + if parent.isEmpty(): + LocatorEntity.notify.warning("could not find '%s'" % self.searchPath) + self.reparentTo(hidden) + else: + self.reparentTo(parent) diff --git a/otp/level/LogicGate.py b/otp/level/LogicGate.py new file mode 100755 index 00000000..67756310 --- /dev/null +++ b/otp/level/LogicGate.py @@ -0,0 +1,91 @@ +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +import Entity + +def andTest(self, a, b): + if b: + messenger.send(self.getOutputEventName(), [a]) + + +def orTest(self, a, b): + if not b: + messenger.send(self.getOutputEventName(), [a]) + + +def xorTest(self, a, b): + messenger.send(self.getOutputEventName(), [not (a and b) and (a or b)]) + + +def nandTest(self, a, b): + if b: + messenger.send(self.getOutputEventName(), [not (a and b)]) + + +def norTest(self, a, b): + if not b: + messenger.send(self.getOutputEventName(), [not (a or b)]) + + +def xnorTest(self, a, b): + messenger.send(self.getOutputEventName(), [(a and b) or not (a or b)]) + + +class LogicGate(Entity.Entity, DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('LogicGate') + logicTests = {'and': andTest, + 'or': orTest, + 'xor': xorTest, + 'nand': nandTest, + 'nor': norTest, + 'xnor': xnorTest} + + def __init__(self, level, entId): + self.input1Event = None + self.input2Event = None + DirectObject.DirectObject.__init__(self) + Entity.Entity.__init__(self, level, entId) + self.setLogicType(self.logicType) + self.setIsInput1(self.isInput1) + self.setIsInput2(self.isInput2) + self.setInput1Event(self.input1Event) + self.setInput2Event(self.input2Event) + return + + def destroy(self): + self.ignore(self.input1Event) + self.input1Event = None + self.ignore(self.input2Event) + self.input2Event = None + Entity.Entity.destroy(self) + return + + def setLogicType(self, logicType): + self.logicType = logicType + self.logicTest = self.logicTests[logicType] + + def setIsInput1(self, isTrue): + if 1 or (not isTrue) != (not self.input1Event): + self.isInput1 = isTrue + self.logicTest(self, isTrue, self.isInput2) + + def setIsInput2(self, isTrue): + if 1 or (not isTrue) != (not self.input2Event): + self.isInput2 = isTrue + self.logicTest(self, isTrue, self.isInput1) + + def setInput1Event(self, event): + if self.input1Event: + self.ignore(self.input1Event) + self.input1Event = self.getOutputEventName(event) + if self.input1Event: + self.accept(self.input1Event, self.setIsInput1) + + def setInput2Event(self, event): + if self.input2Event: + self.ignore(self.input2Event) + self.input2Event = self.getOutputEventName(event) + if self.input2Event: + self.accept(self.input2Event, self.setIsInput2) + + def getName(self): + return 'switch-%s' % (self.entId,) diff --git a/otp/level/ModelEntity.py b/otp/level/ModelEntity.py new file mode 100755 index 00000000..02db755a --- /dev/null +++ b/otp/level/ModelEntity.py @@ -0,0 +1,76 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +import BasicEntities + +class ModelEntity(BasicEntities.NodePathEntity): + LoadFuncs = {'loadModelCopy': loader.loadModelCopy, + 'loadModel': loader.loadModel, + 'loadModelOnce': loader.loadModelOnce} + + def __init__(self, level, entId): + self.collisionsOnly = False + self.loadType = 'loadModelCopy' + self.flattenType = 'light' + self.goonHatType = 'none' + self.entInitialized = False + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.entInitialized = True + self.model = None + self.loadModel() + return + + def destroy(self): + if self.model: + self.model.removeNode() + del self.model + BasicEntities.NodePathEntity.destroy(self) + + def loadModel(self): + if self.model: + self.model.removeNode() + self.model = None + if self.modelPath is None: + return + self.model = ModelEntity.LoadFuncs[self.loadType](self.modelPath) + if self.model: + self.model.reparentTo(self) + if self.collisionsOnly: + self.model.hide() + else: + self.model.show() + if self.modelPath in ('phase_9/models/cogHQ/woodCrateB.bam', 'phase_9/models/cogHQ/metal_crateB.bam', 'phase_10/models/cashbotHQ/CBMetalCrate.bam', 'phase_10/models/cogHQ/CBMetalCrate2.bam', 'phase_10/models/cashbotHQ/CBWoodCrate.bam', 'phase_11/models/lawbotHQ/LB_metal_crate.bam', 'phase_11/models/lawbotHQ/LB_metal_crate2.bam'): + cNode = self.find('**/wall') + cNode.setZ(cNode, -.75) + colNode = self.find('**/collision') + floor = colNode.find('**/floor') + floor2 = floor.copyTo(colNode) + floor2.setZ(floor2, -.75) + if self.goonHatType is not 'none': + self.goonType = {'hardhat': 'pg', + 'security': 'sg'}[self.goonHatType] + self.hat = self.model + if self.goonType == 'pg': + self.hat.find('**/security_hat').hide() + elif self.goonType == 'sg': + self.hat.find('**/hard_hat').hide() + del self.hat + del self.goonType + if self.flattenType == 'light': + self.model.flattenLight() + elif self.flattenType == 'medium': + self.model.flattenMedium() + elif self.flattenType == 'strong': + self.model.flattenStrong() + return + + def setModelPath(self, path): + self.modelPath = path + self.loadModel() + + def setCollisionsOnly(self, collisionsOnly): + self.collisionsOnly = collisionsOnly + self.loadModel() + + def setGoonHatType(self, goonHatType): + self.goonHatType = goonHatType + self.loadModel() diff --git a/otp/level/PathEntity.py b/otp/level/PathEntity.py new file mode 100755 index 00000000..54784e86 --- /dev/null +++ b/otp/level/PathEntity.py @@ -0,0 +1,45 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +import BasicEntities +from toontown.suit import GoonPathData + +class PathEntity(BasicEntities.NodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('PathEntity') + + def __init__(self, level, entId): + self.pathScale = 1.0 + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.setPathIndex(self.pathIndex) + + def destroy(self): + BasicEntities.NodePathEntity.destroy(self) + + def setPathIndex(self, pathIndex): + self.pathIndex = pathIndex + pathTableId = GoonPathData.taskZoneId2pathId[self.level.getTaskZoneId()] + if self.pathIndex in GoonPathData.Paths[pathTableId]: + self.path = GoonPathData.Paths[pathTableId][self.pathIndex] + else: + PathEntity.notify.warning('invalid pathIndex: %s' % pathIndex) + self.path = None + + def makePathTrack(self, node, velocity, name, turnTime = 1, lookAroundNode = None): + track = Sequence(name=name) + if self.path is None: + track.append(WaitInterval(1.0)) + return track + path = self.path + [self.path[0]] + for pointIndex in xrange(len(path) - 1): + startPoint = Point3(path[pointIndex]) * self.pathScale + endPoint = Point3(path[pointIndex + 1]) * self.pathScale + v = startPoint - endPoint + node.setPos(startPoint[0], startPoint[1], startPoint[2]) + node.headsUp(endPoint[0], endPoint[1], endPoint[2]) + theta = node.getH() % 360 + track.append(LerpHprInterval(node, turnTime, Vec3(theta, 0, 0))) + distance = Vec3(v).length() + duration = distance / velocity + track.append(LerpPosInterval(node, duration=duration, pos=endPoint, startPos=startPoint)) + + return track diff --git a/otp/level/PropSpinner.py b/otp/level/PropSpinner.py new file mode 100755 index 00000000..ced535ae --- /dev/null +++ b/otp/level/PropSpinner.py @@ -0,0 +1,52 @@ +import string +from direct.interval.IntervalGlobal import * +from Entity import Entity +from pandac.PandaModules import Vec3 + +class PropSpinner(Entity): + + def __init__(self, level, entId): + Entity.__init__(self, level, entId) + self.initProps() + + def destroy(self): + self.destroyProps() + Entity.destroy(self) + + def initProps(self): + topNode = self.getZoneNode() + props = topNode.findAllMatches('**/Prop_*') + spinTracks = Parallel() + for prop in props: + name = prop.getName() + nameParts = name.split('_') + axis = nameParts[2] + rate = 0 + neg = nameParts[3][0].upper() == 'N' + if neg: + nameParts[3] = nameParts[3][1:] + try: + rate = int(nameParts[3]) + except: + print 'invalid prop rotate string: %s' % name + + if neg: + rate = -rate + prop.setHpr(0, 0, 0) + if axis == 'X': + hpr = Vec3(0, rate * 360, 0) + elif axis == 'Y': + hpr = Vec3(rate * 360, 0, 0) + elif axis == 'Z': + hpr = Vec3(0, 0, rate * 360) + else: + print 'error', axis + spinTracks.append(LerpHprInterval(prop, 60, hpr)) + + spinTracks.loop() + self.spinTracks = spinTracks + + def destroyProps(self): + if hasattr(self, 'spinTracks'): + self.spinTracks.pause() + del self.spinTracks diff --git a/otp/level/VisibilityBlocker.py b/otp/level/VisibilityBlocker.py new file mode 100755 index 00000000..3f604ce7 --- /dev/null +++ b/otp/level/VisibilityBlocker.py @@ -0,0 +1,29 @@ +import Entity + +class VisibilityBlocker: + + def __init__(self): + self.__nextSetZoneDoneEvent = None + return + + def destroy(self): + self.cancelUnblockVis() + + def requestUnblockVis(self): + if self.__nextSetZoneDoneEvent is None: + self.__nextSetZoneDoneEvent = self.level.cr.getNextSetZoneDoneEvent() + self.acceptOnce(self.__nextSetZoneDoneEvent, self.okToUnblockVis) + self.level.forceSetZoneThisFrame() + return + + def cancelUnblockVis(self): + if self.__nextSetZoneDoneEvent is not None: + self.ignore(self.__nextSetZoneDoneEvent) + self.__nextSetZoneDoneEvent = None + return + + def isWaitingForUnblockVis(self): + return self.__nextSetZoneDoneEvent is not None + + def okToUnblockVis(self): + self.cancelUnblockVis() diff --git a/otp/level/VisibilityExtender.py b/otp/level/VisibilityExtender.py new file mode 100755 index 00000000..1b840a16 --- /dev/null +++ b/otp/level/VisibilityExtender.py @@ -0,0 +1,44 @@ +import Entity + +class VisibilityExtender(Entity.Entity): + + def __init__(self, level, entId): + Entity.Entity.__init__(self, level, entId) + self.initVisExt() + + def initVisExt(self): + self.extended = 0 + self.zoneEntId = self.getZoneEntId() + self.eventName = None + if self.event is not None: + self.eventName = self.getOutputEventName(self.event) + self.accept(self.eventName, self.handleEvent) + + def destroyVisExt(self): + if self.eventName is not None: + self.ignore(self.eventName) + if self.extended: + self.retract() + + def handleEvent(self, doExtend): + if doExtend: + if not self.extended: + self.extend() + elif self.extended: + self.retract() + + def extend(self): + zoneEnt = self.level.getEntity(self.getZoneEntId()) + zoneEnt.incrementRefCounts(self.newZones) + self.extended = 1 + self.level.handleVisChange() + + def retract(self): + zoneEnt = self.level.getEntity(self.getZoneEntId()) + zoneEnt.decrementRefCounts(self.newZones) + self.extended = 0 + self.level.handleVisChange() + + def destroy(self): + self.destroyVisExt() + Entity.Entity.destroy(self) diff --git a/otp/level/ZoneEntity.py b/otp/level/ZoneEntity.py new file mode 100755 index 00000000..8d7a55d9 --- /dev/null +++ b/otp/level/ZoneEntity.py @@ -0,0 +1,34 @@ +import ZoneEntityBase +import BasicEntities + +class ZoneEntity(ZoneEntityBase.ZoneEntityBase, BasicEntities.NodePathAttribs): + + def __init__(self, level, entId): + ZoneEntityBase.ZoneEntityBase.__init__(self, level, entId) + self.nodePath = self.level.getZoneNode(self.entId) + if self.nodePath is None: + self.notify.error('zone %s not found in level model' % self.entId) + BasicEntities.NodePathAttribs.initNodePathAttribs(self, doReparent=0) + self.visibleZoneNums = {} + self.incrementRefCounts(self.visibility) + + def destroy(self): + BasicEntities.NodePathAttribs.destroy(self) + ZoneEntityBase.ZoneEntityBase.destroy(self) + + def getNodePath(self): + return self.nodePath + + def getVisibleZoneNums(self): + return self.visibleZoneNums.keys() + + def incrementRefCounts(self, zoneNumList): + for zoneNum in zoneNumList: + self.visibleZoneNums.setdefault(zoneNum, 0) + self.visibleZoneNums[zoneNum] += 1 + + def decrementRefCounts(self, zoneNumList): + for zoneNum in zoneNumList: + self.visibleZoneNums[zoneNum] -= 1 + if self.visibleZoneNums[zoneNum] == 0: + del self.visibleZoneNums[zoneNum] diff --git a/otp/level/ZoneEntityAI.py b/otp/level/ZoneEntityAI.py new file mode 100755 index 00000000..bdb5c3a9 --- /dev/null +++ b/otp/level/ZoneEntityAI.py @@ -0,0 +1,12 @@ +import ZoneEntityBase + +class ZoneEntityAI(ZoneEntityBase.ZoneEntityBase): + + def __init__(self, level, entId): + ZoneEntityBase.ZoneEntityBase.__init__(self, level, entId) + self.setZoneId(self.level.air.allocateZone()) + + def destroy(self): + if not self.isUberZone(): + self.level.air.deallocateZone(self.getZoneId()) + ZoneEntityBase.ZoneEntityBase.destroy(self) diff --git a/otp/level/ZoneEntityBase.py b/otp/level/ZoneEntityBase.py new file mode 100755 index 00000000..cf17b39c --- /dev/null +++ b/otp/level/ZoneEntityBase.py @@ -0,0 +1,25 @@ +import Entity +import LevelConstants + +class ZoneEntityBase(Entity.Entity): + + def __init__(self, level, entId): + Entity.Entity.__init__(self, level, entId) + self.zoneId = None + return + + def destroy(self): + del self.zoneId + Entity.Entity.destroy(self) + + def isUberZone(self): + return self.entId == LevelConstants.UberZoneEntId + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def getZoneId(self): + return self.zoneId + + def getZoneNum(self): + return self.entId diff --git a/otp/level/__init__.py b/otp/level/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/otp/margins/ClickablePopup.py b/otp/margins/ClickablePopup.py new file mode 100644 index 00000000..ed9b8aec --- /dev/null +++ b/otp/margins/ClickablePopup.py @@ -0,0 +1,162 @@ +from pandac.PandaModules import * +from direct.showbase.DirectObject import DirectObject +from otp.nametag import NametagGlobals + +class ClickablePopup(PandaNode, DirectObject): + CS_NORMAL = 0 + CS_CLICK = 1 + CS_HOVER = 2 + CS_DISABLED = 3 + + def __init__(self, cam=None): + PandaNode.__init__(self, 'popup') + DirectObject.__init__(self) + + self.__mwn = NametagGlobals.mouseWatcher + self.__name = 'clickregion-%d' % id(self) + + self.__cam = cam + self.__region = MouseWatcherRegion(self.__name, 0, 0, 0, 0) + self.__mwn.addRegion(self.__region) + + self.__disabled = False + self.__clicked = False + self.__hovered = False + self.__onscreen = False + self.__clickState = 0 + self.__clickArgs = [] + + self.__clickEvent = '' + + self.accept(self.__getEvent(self.__mwn.getEnterPattern()), self.__mouseEnter) + self.accept(self.__getEvent(self.__mwn.getLeavePattern()), self.__mouseLeave) + self.accept(self.__getEvent(self.__mwn.getButtonDownPattern()), self.__buttonDown) + self.accept(self.__getEvent(self.__mwn.getButtonUpPattern()), self.__buttonUp) + + def destroy(self): + self.__mwn.removeRegion(self.__region) + self.ignoreAll() + + def setClickRegionEvent(self, event, clickArgs=[]): + if event is None: + # The caller is disabling us, so instead: + self.__disabled = True + self.__region.setActive(False) + self.__updateClickState() + else: + self.__clickEvent = event + self.__clickArgs = clickArgs + self.__disabled = False + self.__region.setActive(True) + self.__updateClickState() + + def getClickState(self): + return self.__clickState + + def clickStateChanged(self): + pass # Intended for subclasses. + + def __getEvent(self, pattern): + return pattern.replace('%r', self.__name) + + def __mouseEnter(self, region, extra): + self.__hovered = True + self.__updateClickState() + + def __mouseLeave(self, region, extra): + self.__hovered = False + self.__updateClickState() + + def __buttonDown(self, region, button): + if button == 'mouse1': + self.__clicked = True + self.__updateClickState() + + def __buttonUp(self, region, button): + if button == 'mouse1': + self.__clicked = False + self.__updateClickState() + + def __updateClickState(self): + if self.__disabled: + state = self.CS_DISABLED + elif self.__clicked: + state = self.CS_CLICK + elif self.__hovered: + state = self.CS_HOVER + else: + state = self.CS_NORMAL + + if self.__clickState == state: return + oldState = self.__clickState + self.__clickState = state + + if oldState == self.CS_NORMAL and state == self.CS_HOVER: + # Play rollover sound: + base.playSfx(NametagGlobals.rolloverSound) + elif state == self.CS_CLICK: + # Play click sound: + base.playSfx(NametagGlobals.clickSound) + elif oldState == self.CS_CLICK and state == self.CS_HOVER: + # Fire click event: + messenger.send(self.__clickEvent, self.__clickArgs) + + self.clickStateChanged() + + def updateClickRegion(self, left, right, bottom, top, offset=0): + transform = NodePath.anyPath(self).getNetTransform() + + if self.__cam: + # We have a camera, so get its transform and move our net transform + # into the coordinate space of the camera: + camTransform = self.__cam.getNetTransform() + transform = camTransform.invertCompose(transform) + + # We must discard the rotational component on our transform, thus: + transform = transform.setQuat(Quat()) + + # Next, we'll transform the frame into camspace: + mat = transform.getMat() + cTopLeft = mat.xformPoint(Point3(left, 0, top)) + cBottomRight = mat.xformPoint(Point3(right, 0, bottom)) + + # Shift along the offset while in camspace, not worldspace. + if offset: + mid = mat.xformPoint(Point3(0,0,0)) + length = mid.length() + shift = mid*(length - offset)/length - mid + cTopLeft += shift + cBottomRight += shift + + if self.__cam: + # We must go further and project to screenspace: + lens = self.__cam.node().getLens() + + sTopLeft = Point2() + sBottomRight = Point2() + + if not (lens.project(Point3(cTopLeft), sTopLeft) and + lens.project(Point3(cBottomRight), sBottomRight)): + # Not on-screen! Disable the click region: + self.__region.setActive(False) + self.__onscreen = False + return + else: + # No cam; the "camspace" (actually just net transform) IS the + # screenspace transform. + sTopLeft = Point2(cTopLeft[0], cTopLeft[2]) + sBottomRight = Point2(cBottomRight[0], cBottomRight[2]) + + sLeft, sTop = sTopLeft + sRight, sBottom = sBottomRight + + self.__region.setFrame(sLeft, sRight, sBottom, sTop) + self.__region.setActive(not self.__disabled) + self.__onscreen = True + + def stashClickRegion(self): + self.__region.setActive(False) + self.__onscreen = False + + def isOnScreen(self): + return self.__onscreen diff --git a/otp/margins/MarginCell.py b/otp/margins/MarginCell.py new file mode 100644 index 00000000..5fc13874 --- /dev/null +++ b/otp/margins/MarginCell.py @@ -0,0 +1,83 @@ +from pandac.PandaModules import * + +class MarginCell(NodePath): + def __init__(self, manager): + NodePath.__init__(self, 'cell') + + self.manager = manager + + self.content = None + self.available = False + + self.debugSquare = None + self.debugMode = False + + self.setDebug(config.GetBool('want-cell-debug', False)) + + def setAvailable(self, available): + if not available and self.hasContent(): + self.setContent(None) + + self.available = available + + self.updateDebug() + + def setContent(self, content): + if self.content: + self.content._assignedCell = None + self.contentNP.removeNode() + self.content.marginVisibilityChanged() + + if content: + content._assignedCell = self + content._lastCell = self + self.contentNP = self.attachNewNode(content) + content.marginVisibilityChanged() + + self.content = content + + self.updateDebug() + + def hasContent(self): + return self.content is not None + + def getContent(self): + return self.content + + def isAvailable(self): + return self.available + + def isFree(self): + return self.isAvailable() and not self.hasContent() + + def setDebugColor(self, color): + if not self.debugSquare: + cm = CardMaker('debugSquare') + cm.setFrameFullscreenQuad() + self.debugSquare = self.attachNewNode(cm.generate()) + self.debugSquare.setTransparency(1) + self.debugSquare.setY(1) + + self.debugSquare.setColor(color) + + def updateDebug(self): + if not self.debugMode: return + + if self.hasContent(): + self.setDebugColor(VBase4(0.0, 0.8, 0.0, 0.5)) + elif self.isAvailable(): + self.setDebugColor(VBase4(0.0, 0.0, 0.8, 0.5)) + else: + self.setDebugColor(VBase4(0.8, 0.0, 0.0, 0.5)) + + def setDebug(self, status): + if bool(status) == self.debugMode: + return + + self.debugMode = status + + if self.debugMode: + self.updateDebug() + elif self.debugSquare: + self.debugSquare.removeNode() + self.debugSquare = None diff --git a/otp/margins/MarginManager.py b/otp/margins/MarginManager.py new file mode 100644 index 00000000..60f60bac --- /dev/null +++ b/otp/margins/MarginManager.py @@ -0,0 +1,83 @@ +from pandac.PandaModules import * +from MarginCell import MarginCell +import random + +class MarginManager(PandaNode): + def __init__(self): + PandaNode.__init__(self, 'margins') + + self.cells = set() + self.visiblePopups = set() + + def addGridCell(self, x, y, a2d): + # Yucky! + nodePath = NodePath.anyPath(self) + a2d.reparentTo(nodePath) + cell = MarginCell(self) + cell.reparentTo(a2d) + cell.setScale(0.2) + cell.setPos(x, 0, y + 0.025) + cell.setAvailable(True) + cell.setPythonTag('MarginCell', cell) + + self.cells.add(cell) + self.reorganize() + + return cell + + def setCellAvailable(self, cell, available): + cell = cell.getPythonTag('MarginCell') + cell.setAvailable(available) + self.reorganize() + + def addVisiblePopup(self, popup): + self.visiblePopups.add(popup) + self.reorganize() + + def removeVisiblePopup(self, popup): + if popup not in self.visiblePopups: return + self.visiblePopups.remove(popup) + self.reorganize() + + def reorganize(self): + # First, get all active cells: + activeCells = [cell for cell in self.cells if cell.isAvailable()] + + # Next, get all visible popups, sorted by priority: + popups = list(self.visiblePopups) + popups.sort(key=lambda x: -x.getPriority()) + + # We can only display so many popups, so truncate to the number of active + # margin cells: + popups = popups[:len(activeCells)] + + # Now, we need to build up a list of free cells: + freeCells = [] + for cell in activeCells: + if not cell.hasContent(): + freeCells.append(cell) + elif cell.getContent() in popups: + # It's already displaying something we want to show, so we can + # safely ignore this cell/popup pair: + popups.remove(cell.getContent()) + else: + # It's not displaying something we want to see, evict the old + # popup: + cell.setContent(None) + freeCells.append(cell) + + # At this point, there should be enough cells to show the popups: + assert len(freeCells) >= len(popups) + + # Now we assign the popups: + for popup in popups: + if popup._lastCell in freeCells and popup._lastCell.isFree(): + # The last cell it had assigned is available, so let's assign it + # again: + popup._lastCell.setContent(popup) + freeCells.remove(popup._lastCell) + else: + # We assign a cell at random. + cell = random.choice(freeCells) + cell.setContent(popup) + freeCells.remove(cell) diff --git a/otp/margins/MarginPopup.py b/otp/margins/MarginPopup.py new file mode 100644 index 00000000..69ead38a --- /dev/null +++ b/otp/margins/MarginPopup.py @@ -0,0 +1,52 @@ +from pandac.PandaModules import * + +class MarginPopup: + def __init__(self): + self.__manager = None + self.__visible = False + + self.__priority = 0 + + # The margin management system uses these: + self._assignedCell = None + self._lastCell = None + + def setVisible(self, visibility): + visibility = bool(visibility) + if self.__visible == visibility: return + + self.__visible = visibility + + if self.__manager is not None: + if visibility: + self.__manager.addVisiblePopup(self) + else: + self.__manager.removeVisiblePopup(self) + + def getPriority(self): + return self.__priority + + def setPriority(self, priority): + self.__priority = priority + if self.__manager is not None: + self.__manager.reorganize() + + def isDisplayed(self): + return self._assignedCell is not None + + def marginVisibilityChanged(self): + pass # Fired externally when the result of isDisplayed changes. For subclasses. + + def manage(self, manager): + if self.__manager: + self.unmanage(self.__manager) + self.__manager = manager + + if self.__visible: + manager.addVisiblePopup(self) + + def unmanage(self, manager): + if self.__manager is not None: + if self.__visible: + self.__manager.removeVisiblePopup(self) + self.__manager = None diff --git a/otp/margins/WhisperPopup.py b/otp/margins/WhisperPopup.py new file mode 100644 index 00000000..8542fd94 --- /dev/null +++ b/otp/margins/WhisperPopup.py @@ -0,0 +1,97 @@ +from MarginPopup import * +from ClickablePopup import * +from otp.nametag import NametagGlobals +from otp.nametag.NametagConstants import * + +class WhisperPopup(MarginPopup, ClickablePopup): + WTNormal = WTNormal + WTQuickTalker = WTQuickTalker + WTSystem = WTSystem + WTBattleSOS = WTBattleSOS + WTEmote = WTEmote + WTToontownBoardingGroup = WTToontownBoardingGroup + + WORDWRAP = 7.5 + SCALE_2D = 0.25 + + def __init__(self, text, font, whisperType, timeout=10.0): + ClickablePopup.__init__(self) + MarginPopup.__init__(self) + + self.innerNP = NodePath.anyPath(self).attachNewNode('innerNP') + self.innerNP.setScale(self.SCALE_2D) + + self.text = text + self.font = font + self.whisperType = whisperType + self.timeout = timeout + + self.active = False + self.fromId = 0 + + self.left = 0.0 + self.right = 0.0 + self.top = 0.0 + self.bottom = 0.0 + + self.updateContents() + + self.setPriority(2) + self.setVisible(True) + + def updateContents(self): + if self.whisperType in WHISPER_COLORS: + cc = self.whisperType + else: + cc = WTSystem + + fgColor, bgColor = WHISPER_COLORS[cc][self.getClickState()] + self.innerNP.node().removeAllChildren() + + balloon, frame = NametagGlobals.speechBalloon2d.generate( + self.text, self.font, textColor=fgColor, balloonColor=bgColor, + wordWrap=self.WORDWRAP) + balloon.reparentTo(self.innerNP) + + # Calculate the center of the TextNode. + text = balloon.find('**/+TextNode') + t = text.node() + self.left, self.right, self.bottom, self.top = t.getFrameActual() + center = self.innerNP.getRelativePoint(text, ((self.left + self.right) / 2., 0, (self.bottom + self.top) / 2.)) + + # Next translate the balloon along the inverse. + balloon.setPos(balloon, -center) + + if self.active and self.fromId: + self.setClickRegionEvent('clickedWhisper', clickArgs=[self.fromId]) + + def setClickable(self, fromId): + self.active = True + self.fromId = fromId + + self.updateContents() + self.__updateClickRegion() + + def marginVisibilityChanged(self): + self.__updateClickRegion() + + def __updateClickRegion(self): + if self.isDisplayed() and self.active: + self.updateClickRegion(-1, 1, self.bottom, self.top) + else: + self.stashClickRegion() + + def clickStateChanged(self): + self.updateContents() + + def manage(self, manager): + MarginPopup.manage(self, manager) + + taskMgr.doMethodLater(self.timeout, self.unmanage, 'whisper-timeout-%d' % id(self), [manager]) + + # Manually Clean up + def unmanage(self, manager): + MarginPopup.unmanage(self, manager) + + ClickablePopup.destroy(self) + self.innerNP.removeNode() diff --git a/otp/margins/__init__.py b/otp/margins/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/otp/namepanel/NameCheck.py b/otp/namepanel/NameCheck.py new file mode 100755 index 00000000..7c82d8a9 --- /dev/null +++ b/otp/namepanel/NameCheck.py @@ -0,0 +1,335 @@ +import string +from otp.otpbase import OTPLocalizer +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NSError +from pandac.PandaModules import TextEncoder, TextNode +notify = DirectNotifyGlobal.directNotify.newCategory('NameCheck') + +def filterString(str, filter): + result = '' + for char in str: + if char in filter: + result = result + char + + return result + + +def justLetters(str): + letters = '' + for c in str: + if c.isalpha(): + letters = letters + c + + return letters + + +def justUpper(str): + upperCaseLetters = '' + for c in str: + if c.upper() != c.lower(): + if c == c.upper(): + upperCaseLetters = upperCaseLetters + c + + return upperCaseLetters + + +def wordList(str): + words = str.split() + result = [] + for word in words: + subWords = word.split('-') + for sw in subWords: + if sw: + result.append(sw) + + return result + + +def checkName(name, otherCheckFuncs = [], font = None): + + def longEnough(name): + if len(name) < 2: + notify.info('name is too short') + return OTPLocalizer.NCTooShort + + def emptyName(name): + if name.strip() == '': + notify.info('name is empty') + return OTPLocalizer.NCTooShort + + def printableChars(name): + for char in name: + if ord(char) < 128 and char not in string.printable: + notify.info('name contains non-printable char #%s' % ord(char)) + return OTPLocalizer.NCGeneric + + validAsciiChars = set(".,'-" + string.letters + string.whitespace) + + def _validCharacter(c, validAsciiChars = validAsciiChars, font = font): + if c in validAsciiChars: + return True + if c.isalpha() or c.isspace(): + return True + return False + + def badCharacters(name, _validCharacter = _validCharacter): + for char in name: + if not _validCharacter(char): + if char in string.digits: + notify.info('name contains digits') + return OTPLocalizer.NCNoDigits + else: + notify.info('name contains bad char: %s' % TextEncoder().encodeWtext(char)) + return OTPLocalizer.NCBadCharacter % TextEncoder().encodeWtext(char) + + def fontHasCharacters(name, font = font): + if font: + tn = TextNode('NameCheck') + tn.setFont(font) + for c in name: + if not tn.hasCharacter(c.decode('utf-8')): + notify.info('name contains bad char: %s' % TextEncoder().encodeWtext(c)) + return OTPLocalizer.NCBadCharacter % TextEncoder().encodeWtext(c) + + def hasLetters(name): + words = wordList(name) + for word in words: + letters = justLetters(word) + if len(letters) == 0: + notify.info('word "%s" has no letters' % TextEncoder().encodeWtext(word)) + return OTPLocalizer.NCNeedLetters + + def hasVowels(name): + + def perWord(word): + if '.' in word: + return None + for char in word: + if ord(char) >= 128: + return None + + letters = filterString(word, string.letters) + if len(letters) > 2: + vowels = filterString(letters, 'aeiouyAEIOUY') + if len(vowels) == 0: + notify.info('word "%s" has no vowels' % TextEncoder().encodeWtext(word)) + return OTPLocalizer.NCNeedVowels + return None + + for word in wordList(name): + problem = perWord(word) + if problem: + return problem + + def monoLetter(name): + + def perWord(word): + word = word + letters = justLetters(word) + if len(letters) > 2: + letters = TextEncoder().decodeText(TextEncoder.lower(TextEncoder().encodeWtext(letters))) + filtered = filterString(letters, letters[0]) + if filtered == letters: + notify.info('word "%s" uses only one letter' % TextEncoder().encodeWtext(word)) + return OTPLocalizer.NCGeneric + + for word in wordList(name): + problem = perWord(word) + if problem: + return problem + + def checkDashes(name): + def validDash(index, name=name): + if index == 0 or i == len(name)-1: + return 0 + if not name[i-1].isalpha(): + return 0 + if not name[i+1].isalpha(): + return 0 + return 1 + + i=0 + while 1: + i = name.find('-', i, len(name)) + if i < 0: + return None + if not validDash(i): + notify.info('name makes invalid use of dashes') + return OTPLocalizer.NCDashUsage + i += 1 + + def checkCommas(name): + def validComma(index, name=name): + if index == 0 or i == len(name)-1: + return OTPLocalizer.NCCommaEdge + if name[i-1].isspace(): + return OTPLocalizer.NCCommaAfterWord + if not name[i+1].isspace(): + return OTPLocalizer.NCCommaUsage + return None + + i=0 + while 1: + i = name.find(',', i, len(name)) + if i < 0: + return None + problem = validComma(i) + if problem: + notify.info('name makes invalid use of commas') + return problem + i += 1 + + def checkPeriods(name): + words = wordList(name) + for word in words: + if word[-1] == ',': + word = word[:-1] + numPeriods = word.count('.') + if not numPeriods: + continue + letters = justLetters(word) + numLetters = len(letters) + if word[-1] != '.': + notify.info('word "%s" does not end in a period' % TextEncoder().encodeWtext(word)) + return OTPLocalizer.NCPeriodUsage + if numPeriods > 2: + notify.info('word "%s" has too many periods' % TextEncoder().encodeWtext(word)) + return OTPLocalizer.NCPeriodUsage + if numPeriods == 2: + if not (word[1] == '.' and word[3] == '.'): + notify.info('word "%s" does not fit the J.T. pattern' % TextEncoder().encodeWtext(word)) + return OTPLocalizer.NCPeriodUsage + + return None + + def checkApostrophes(name): + words = wordList(name) + for word in words: + numApos = word.count("'") + if numApos > 2: + notify.info('word "%s" has too many apostrophes.' % TextEncoder().encodeWtext(word)) + return OTPLocalizer.NCApostrophes + + numApos = name.count("'") + if numApos > 3: + notify.info('name has too many apostrophes.') + return OTPLocalizer.NCApostrophes + + def tooManyWords(name): + if len(wordList(name)) > 4: + notify.info('name has too many words') + return OTPLocalizer.NCTooManyWords + + def allCaps(name): + letters = justLetters(name) + if len(letters) > 2: + upperLetters = TextEncoder().decodeText(TextEncoder.upper(TextEncoder().encodeWtext(letters))) + for i in xrange(len(upperLetters)): + if not upperLetters[0].isupper(): + return + + if upperLetters == letters: + notify.info('name is all caps') + return OTPLocalizer.NCAllCaps + + def mixedCase(name): + words = wordList(name) + for word in words: + if len(word) > 2: + capitals = justUpper(word) + if len(capitals) > 2: + notify.info('name has mixed case') + return OTPLocalizer.NCMixedCase + + def checkJapanese(name): + asciiSpace = range(32, 33) + asciiDigits = range(48, 64) + hiragana = range(12353, 12448) + katakana = range(12449, 12544) + halfwidthKatakana = range(65381, 65440) + halfwidthCharacter = set(asciiSpace + halfwidthKatakana) + allowedUtf8 = set(asciiSpace + hiragana + katakana + halfwidthKatakana) + + te = TextEncoder() + dc = 0.0 + + for char in (ord(char) for char in te.decodeText(name)): + if char not in allowedUtf8: + if char in asciiDigits: + notify.info('name contains not allowed ascii digits') + return OTPLocalizer.NCNoDigits + else: + notify.info('name contains not allowed utf8 char: 0x%04x' % char) + return OTPLocalizer.NCBadCharacter % te.encodeWtext(unichr(char)) + elif char in halfwidthCharacter: + dc += 0.5 + else: + dc += 1 + + if dc < 2: + notify.info('name is too short: %0.1f' % dc) + return OTPLocalizer.NCTooShort + elif dc > 8: + notify.info('name has been occupied more than eight display cells: %0.1f' % dc) + return OTPLocalizer.NCGeneric + + def repeatedChars(name): + count = 1 + lastChar = None + i = 0 + while i < len(name): + char = name[i] + i += 1 + if char == lastChar: + count += 1 + else: + count = 1 + lastChar = char + if count > 2: + notify.info('character %s is repeated too many times' % TextEncoder().encodeWtext(char)) + return OTPLocalizer.NCRepeatedChar % TextEncoder().encodeWtext(char) + + return + + checks = [printableChars, + badCharacters, + fontHasCharacters, + longEnough, + emptyName, + hasLetters, + hasVowels, + monoLetter, + checkDashes, + checkCommas, + checkPeriods, + checkApostrophes, + tooManyWords, + allCaps, + mixedCase, + repeatedChars] + otherCheckFuncs + symmetricChecks = [] + name = TextEncoder().decodeText(name) + notify.info('checking name "%s"...' % TextEncoder().encodeWtext(name)) + for check in checks: + problem = check(name[:]) + if not problem and check in symmetricChecks: + nName = name[:] + bName.reverse() + problem = check(bName) + print 'problem = %s' % problem + if problem: + return problem + + return None + + +severity = notify.getSeverity() +notify.setSeverity(NSError) +for i in xrange(32): + pass + +for c in '!"#$%&()*+/:;<=>?@[\\]^_`{|}~': + pass + +notify.setSeverity(severity) +del severity diff --git a/otp/namepanel/NameTumbler.py b/otp/namepanel/NameTumbler.py new file mode 100755 index 00000000..be5426fc --- /dev/null +++ b/otp/namepanel/NameTumbler.py @@ -0,0 +1,143 @@ +from panda3d.core import * +from direct.showbase import DirectObject +import random +from direct.task import Task +from direct.gui.DirectGui import * +import string +from direct.gui import OnscreenText + +class NameTumbler(DirectFrame): + + def __init__(self, nameList, category): + DirectFrame.__init__(self, parent=aspect2d, relief='flat', scale=(1, 1, 1), state='disabled', frameColor=(1, 1, 1, 0)) + self.initialiseoptions(NameTumbler) + self.nameList = nameList + self.nameList.sort() + self.category = category + self.tumblerColor = Vec4(1, 1, 1, 1) + self.displayList = [' '] + [' '] + self.nameList + [' '] + [' '] + self.nameIndex = -1 + self.isActive = 1 + self.loadTumblerGUI() + + def loadTumblerGUI(self): + self.circle = 'src/maps/NameTumblerCheck.tif' + self.background = 'src/maps/NameTumbler.tif' + self.upArrow = 'src/maps/NameTumblerUpArrow.tif' + self.downArrow = 'src/maps/NameTumblerDownArrow.tif' + self.tumblerscrollList = self.makeScrollList(self.displayList, self.makeLabel, [TextNode.ACenter, 'title']) + self.tumblerscrollList['command'] = self.listsChanged + self.tumblerscrollList.reparentTo(self) + self.hilight = self.makeHighlight((0, 0, -0.15)) + self.hilight.reparentTo(self.tumblerscrollList) + if self.category != '': + self.check = self.makeCheckBox((-0.617, 0, 0.374), self.category, (0, 0.25, 0.5, 1), self.toggleTumbler) + self.check.reparentTo(self) + self.getRandomResult() + + def unloadTumblerGUI(self): + if self.category != '': + self.check.destroy() + del self.check + self.tumblerscrollList.destroy() + del self.tumblerscrollList + self.hilight.destroy() + del self.hilight + + def toggleTumbler(self, value): + if self.isActive: + if self.priority == 1: + messenger.send('CheckTumblerPriority', [self.category]) + else: + self.deactivateTumbler() + else: + self.activateTumbler() + if self.linkage > 0: + messenger.send('CheckTumblerLinkage', [self.category]) + self.listsChanged() + if self.isActive: + self.tumblerscrollList.refresh() + self.updateCheckBoxes() + + def listsChanged(self): + newname = '' + self.nameIndex = self.tumblerscrollList.index + 2 + messenger.send('updateNameResult') + + def updateLists(self): + self.tumblerscrollList.scrollTo(self.nameIndex - 2) + messenger.send('updateNameResult') + + def updateCheckBoxes(self): + if self.category != '': + if self.isActive: + self.check['indicatorValue'] = self.isActive + else: + self.check['indicatorValue'] = -1 + self.check.setIndicatorValue() + + def nameClickedOn(self, index): + self.nameIndex = index + self.updateLists() + self.listsChanged() + + def activateTumbler(self): + self.hilight.show() + self.isActive = 1 + self.tumblerscrollList.itemFrame['frameColor'] = self.tumblerColor + + def deactivateTumbler(self): + self.hilight.hide() + self.isActive = 0 + self.tumblerscrollList.itemFrame['frameColor'] = (0.7, 0.7, 0.7, 1) + + def getName(self): + if self.isActive: + name = self.nameList[self.nameIndex - 2] + else: + name = '' + return name + + def makeLabel(self, te, index, others): + alig = others[0] + if alig == TextNode.ARight: + newpos = (0.44, 0, 0) + elif alig == TextNode.ALeft: + newpos = (0, 0, 0) + else: + newpos = (0.2, 0, 0) + df = DirectFrame(state='normal', relief=None, text=te, text_scale=0.1, text_pos=newpos, text_align=alig, textMayChange=0) + df.bind(DGG.B1PRESS, lambda x, df = df: self.nameClickedOn(index)) + return df + + def makeScrollList(self, nitems, nitemMakeFunction, nitemMakeExtraArgs): + it = nitems[:] + ds = DirectScrolledList(items=it, itemMakeFunction=nitemMakeFunction, itemMakeExtraArgs=nitemMakeExtraArgs, parent=aspect2d, relief=None, command=None, scale=0.6, pad=(0.1, 0.1), incButton_image=(self.downArrow, + self.upArrow, + self.circle, + self.downArrow), incButton_relief=None, incButton_scale=(0.2, 0.05, 0.05), incButton_pos=(0, 0, -0.58), decButton_image=(self.upArrow, + self.downArrow, + self.circle, + self.upArrow), decButton_relief=None, decButton_scale=(0.2, 0.05, 0.05), decButton_pos=(0, 0, 0.23), itemFrame_pos=(-.2, 0, 0.028), itemFrame_scale=1.0, itemFrame_relief=None, itemFrame_image=self.background, itemFrame_image_scale=(0.38, 0, 0.33), itemFrame_image_pos=(0.2, 0, -0.2), itemFrame_frameSize=(-0.05, + 0.48, + -0.5, + 0.1), itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=5) + ds.setTransparency(1) + return ds + + def makeCheckBox(self, npos, ntex, ntexcolor, comm): + dcf = DirectCheckButton(parent=aspect2d, relief=None, scale=0.1, boxBorder=0.08, boxImage=self.circle, boxImageScale=(0.4, 0.4, 0.4), boxRelief=None, pos=npos, text=ntex, text_fg=ntexcolor, text_scale=0.8, text_pos=(0.2, 0), indicator_pos=(-0.566667, 0, -0.045), indicator_image_pos=(-0.26, 0, 0.075), command=comm, text_align=TextNode.ALeft) + dcf.setTransparency(1) + return dcf + + def makeHighlight(self, npos): + return DirectFrame(parent=aspect2d, relief='flat', state='disabled', frameSize=(-0.25, + 0.26, + -0.05, + 0.05), borderWidth=(0.01, 0.01), pos=npos, frameColor=(1, 0, 1, 0.4)) + + def getRandomResult(self): + randomName = random.choice(self.nameList) + self.nameIndex = self.displayList.index(randomName) + self.updateCheckBoxes() + self.updateLists() diff --git a/otp/namepanel/PickANamePattern.py b/otp/namepanel/PickANamePattern.py new file mode 100755 index 00000000..2d033e4f --- /dev/null +++ b/otp/namepanel/PickANamePattern.py @@ -0,0 +1,135 @@ + + +class PickANamePattern: + + def __init__(self, nameStr, gender): + self._nameStr = nameStr + self._namePattern = self._compute(self._nameStr, gender) + + def hasNamePattern(self): + return self._namePattern is not None + + def getNamePattern(self): + return self._namePattern + + def getNameString(self, pattern, gender): + nameParts = self._getNameParts(gender) + invNameParts = [] + for i in xrange(len(nameParts)): + invNameParts.append(invertDict(nameParts[i])) + + name = '' + for i in xrange(len(pattern)): + if pattern[i] != -1: + if len(name): + name += ' ' + name += invNameParts[i][pattern[i]] + + return name + + def getNamePartString(self, gender, patternIndex, partIndex): + nameParts = self._getNameParts(gender) + invNamePart = invertDict(nameParts[patternIndex]) + return invNamePart[partIndex] + + def _genWordListSplitPermutations(self, words): + if not len(words): + + return + if len(words) == 1: + + yield words + return + + for permutation in self._genWordListSplitPermutations(words[1:]): + yield [words[0]]+permutation + yield [(words[0] + ' ')+permutation[0]]+permutation[1:] + + def _genNameSplitPermutations(self, name): + for splitName in self._genWordListSplitPermutations(name.split()): + yield splitName + + def _compute(self, nameStr, gender): + return self._computeWithNameParts(nameStr, self._getNameParts(gender)) + + def _computeWithNameParts(self, nameStr, nameParts): + for splitPermutation in self._genNameSplitPermutations(nameStr): + pattern = self._recursiveCompute(splitPermutation, nameParts) + if pattern is not None: + return pattern + + return + + def _getNameParts(self, gender): + pass + + def _recursiveCompute(self, words, nameParts, wi = 0, nwli = 0, pattern = None): + if wi >= len(words): + return pattern + if nwli >= len(nameParts): + return + if words[wi] in nameParts[nwli]: + if pattern is None: + pattern = [-1] * len(nameParts) + word2index = nameParts[nwli] + newPattern = pattern[:] + newPattern[nwli] = word2index[words[wi]] + result = self._recursiveCompute(words, nameParts, wi + 1, nwli + 1, newPattern) + if result: + return result + return self._recursiveCompute(words, nameParts, wi, nwli + 1, pattern) + + +class PickANamePatternTwoPartLastName(PickANamePattern): + + def getNameString(self, pattern, gender): + name = PickANamePattern.getNameString(self, pattern, gender) + if pattern[-2] != -1: + words = name.split() + name = '' + for word in words[:-2]: + if len(name): + name += ' ' + name += word + + if len(name): + name += ' ' + name += words[-2] + if words[-2] in set(self._getLastNameCapPrefixes()): + name += words[-1].capitalize() + else: + name += words[-1] + return name + + def _getLastNameCapPrefixes(self): + return [] + + def _compute(self, nameStr, gender): + nameParts = self._getNameParts(gender) + combinedNameParts = nameParts[:-2] + combinedNameParts.append({}) + combinedIndex2indices = {} + lastNamePrefixesCapped = set(self._getLastNameCapPrefixes()) + k = 0 + for first, i in nameParts[-2].iteritems(): + capitalize = first in lastNamePrefixesCapped + for second, j in nameParts[-1].iteritems(): + combinedLastName = first + if capitalize: + combinedLastName += second.capitalize() + else: + combinedLastName += second + combinedNameParts[-1][combinedLastName] = k + combinedIndex2indices[k] = (i, j) + k += 1 + + pattern = self._computeWithNameParts(nameStr, combinedNameParts) + if pattern: + combinedIndex = pattern[-1] + pattern = pattern[:-1] + pattern.append(-1) + pattern.append(-1) + if combinedIndex != -1: + pattern[-2] = combinedIndex2indices[combinedIndex][0] + pattern[-1] = combinedIndex2indices[combinedIndex][1] + return pattern diff --git a/otp/namepanel/__init__.py b/otp/namepanel/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/otp/nametag/ChatBalloon.py b/otp/nametag/ChatBalloon.py new file mode 100644 index 00000000..598eb47b --- /dev/null +++ b/otp/nametag/ChatBalloon.py @@ -0,0 +1,93 @@ +from pandac.PandaModules import * + +class ChatBalloon: + TEXT_SHIFT = (0.1, -0.05, 1.1) + TEXT_SHIFT_REVERSED = -0.05 + TEXT_SHIFT_PROP = 0.08 + NATIVE_WIDTH = 10.0 + MIN_WIDTH = 2.5 + MIN_HEIGHT = 1 + BUBBLE_PADDING = 0.3 + BUBBLE_PADDING_PROP = 0.05 + BUTTON_SCALE = 6 + BUTTON_SHIFT = (-0.2, 0, 0.6) + FRAME_SHIFT = (0.2, 1.4) + + def __init__(self, model): + self.model = model + + def generate(self, text, font, textColor=(0,0,0,1), balloonColor=(1,1,1,1), + wordWrap = 10.0, button=None, reversed=False): + root = NodePath('balloon') + + # Add balloon geometry: + balloon = self.model.copyTo(root) + top = balloon.find('**/top') + middle = balloon.find('**/middle') + bottom = balloon.find('**/bottom') + + balloon.setColor(balloonColor) + if balloonColor[3] < 1.0: + balloon.setTransparency(1) + + # Render the text into a TextNode, using the font: + t = root.attachNewNode(TextNode('text')) + t.node().setFont(font) + t.node().setWordwrap(wordWrap) + t.node().setText(text) + t.node().setTextColor(textColor) + + width, height = t.node().getWidth(), t.node().getHeight() + + # Turn off depth write for the text: The place in the depth buffer is + # held by the chat bubble anyway, and the text renders after the bubble + # so there's no risk of the bubble overwriting the text's pixels. + t.setAttrib(DepthWriteAttrib.make(0)) + t.setPos(self.TEXT_SHIFT) + t.setX(t, self.TEXT_SHIFT_PROP*width) + t.setZ(t, height) + + if reversed: + # The nametag code wants the text on the left side of the axis, + # rather than on the right side. Therefore, we move the text to the + # opposite side: + t.setX(self.TEXT_SHIFT_REVERSED - self.TEXT_SHIFT_PROP*width - width) + + # Give the chat bubble a button, if one is requested: + if button: + np = button.copyTo(root) + np.setPos(t, width, 0, -height) + np.setPos(np, self.BUTTON_SHIFT) + np.setScale(self.BUTTON_SCALE) + + # Set a minimum width and height for short or empty messages + if width < self.MIN_WIDTH: + width = self.MIN_WIDTH + if reversed: + t.setX(t, -width/2.0) + else: + t.setX(t, width/2.0) + t.node().setAlign(TextNode.ACenter) + + if height < self.MIN_HEIGHT: + height = self.MIN_HEIGHT + t.setX(t, height/2.0) + t.node().setAlign(TextNode.ACenter) + + # Set the balloon's size: + width *= 1+self.BUBBLE_PADDING_PROP + width += self.BUBBLE_PADDING + balloon.setSx(width/self.NATIVE_WIDTH) + if reversed: + balloon.setSx(-balloon.getSx()) + balloon.setTwoSided(True) # Render the backface of the balloon + middle.setSz(height) + top.setZ(top, height-1) + + # Calculate the frame occupied by the balloon: + left, bottom = self.FRAME_SHIFT + if reversed: + left = -left - width + frame = (left, left+width, bottom, bottom+height+1) + + return root, frame diff --git a/otp/nametag/Nametag.py b/otp/nametag/Nametag.py new file mode 100644 index 00000000..1d9d11a4 --- /dev/null +++ b/otp/nametag/Nametag.py @@ -0,0 +1,160 @@ +from NametagConstants import * +import NametagGlobals +from otp.margins.ClickablePopup import ClickablePopup +from otp.otpbase import OTPGlobals +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * + +class Nametag(ClickablePopup): + CName = 1 + CSpeech = 2 + CThought = 4 + + NAME_PADDING = 0.2 + CHAT_ALPHA = 1.0 + + DEFAULT_CHAT_WORDWRAP = 10.0 + + IS_3D = False # 3D variants will set this to True. + + def __init__(self): + if self.IS_3D: + ClickablePopup.__init__(self, NametagGlobals.camera) + else: + ClickablePopup.__init__(self) + + self.contents = 0 # To be set by subclass. + + self.innerNP = NodePath.anyPath(self).attachNewNode('nametag_contents') + + self.wordWrap = 7.5 + self.chatWordWrap = None + + self.font = None + self.speechFont = None + self.name = '' + self.displayName = '' + self.qtColor = VBase4(1,1,1,1) + self.colorCode = CCNormal + self.avatar = None + self.icon = NodePath('icon') + + self.frame = (0, 0, 0, 0) + + self.nameFg = (0,0,0,1) + self.nameBg = (1,1,1,1) + self.chatFg = (0,0,0,1) + self.chatBg = (1,1,1,1) + + self.chatString = '' + self.chatFlags = 0 + + def destroy(self): + ClickablePopup.destroy(self) + + def setContents(self, contents): + self.contents = contents + self.update() + + def setAvatar(self, avatar): + self.avatar = avatar + + def setChatWordwrap(self, chatWordWrap): + self.chatWordWrap = chatWordWrap + + def tick(self): + pass # Does nothing by default. + + def clickStateChanged(self): + self.update(False) + + def getButton(self): + cs = self.getClickState() + if self.buttons is None: + return None + elif cs in self.buttons: + return self.buttons[cs] + else: + return self.buttons.get(0) + + def update(self, scale=True): + if self.colorCode in NAMETAG_COLORS: + cc = self.colorCode + else: + cc = CCNormal + + self.nameFg, self.nameBg, self.chatFg, self.chatBg = NAMETAG_COLORS[cc][self.getClickState()] + + self.innerNP.node().removeAllChildren() + if self.contents & self.CThought and self.chatFlags & CFThought: + balloon = self.showBalloon(self.getThoughtBalloon(), self.chatString) + elif self.contents & self.CSpeech and self.chatFlags&CFSpeech: + balloon = self.showBalloon(self.getSpeechBalloon(), self.chatString) + elif self.contents & self.CName and self.displayName: + self.showName() + return + else: + return + + if scale and self.IS_3D: + balloon.setScale(0) + scaleLerp = Sequence(Wait(0.10), LerpScaleInterval(balloon, 0.2, VBase3(1, 1, 1), VBase3(0, 0, 0), blendType='easeInOut')) + scaleLerp.start() + + def showBalloon(self, balloon, text): + if not self.speechFont: + # If no font is set, we can't display anything yet... + return + color = self.qtColor if (self.chatFlags&CFQuicktalker) else self.chatBg + if color[3] > self.CHAT_ALPHA: + color = (color[0], color[1], color[2], self.CHAT_ALPHA) + + reversed = (self.IS_3D and (self.chatFlags&CFReversed)) + + balloon, frame = balloon.generate(text, self.speechFont, textColor=self.chatFg, + balloonColor=color, + wordWrap=self.chatWordWrap or \ + self.DEFAULT_CHAT_WORDWRAP, + button=self.getButton(), + reversed=reversed) + balloon.reparentTo(self.innerNP) + self.frame = frame + return balloon + + def showName(self): + if not self.font: + # If no font is set, we can't actually display a name yet... + return + + # Create text node: + self.innerNP.attachNewNode(self.icon) + t = self.innerNP.attachNewNode(TextNode('name'), 1) + t.node().setFont(self.font) + t.node().setAlign(TextNode.ACenter) + t.node().setWordwrap(self.wordWrap) + t.node().setText(self.displayName) + t.node().setTextColor(self.nameFg) + t.setTransparency(self.nameFg[3] < 1.0) + + width, height = t.node().getWidth(), t.node().getHeight() + + # Put the actual written name a little in front of the nametag and + # disable depth write so the text appears nice and clear, free from + # z-fighting and bizarre artifacts. The text renders *after* the tag + # behind it, due to both being in the transparency bin, + # so there's really no problem with doing this. + t.setY(-0.05) + t.setAttrib(DepthWriteAttrib.make(0)) + + # Apply panel behind the text: + panel = NametagGlobals.nametagCardModel.copyTo(self.innerNP, 0) + panel.setPos((t.node().getLeft()+t.node().getRight())/2.0, 0, + (t.node().getTop()+t.node().getBottom())/2.0) + panel.setScale(width + self.NAME_PADDING, 1, height + self.NAME_PADDING) + panel.setColor(self.nameBg) + panel.setTransparency(self.nameBg[3] < 1.0) + + self.frame = (t.node().getLeft()-self.NAME_PADDING/2.0, + t.node().getRight()+self.NAME_PADDING/2.0, + t.node().getBottom()-self.NAME_PADDING/2.0, + t.node().getTop()+self.NAME_PADDING/2.0) diff --git a/otp/nametag/Nametag2d.py b/otp/nametag/Nametag2d.py new file mode 100644 index 00000000..3b485556 --- /dev/null +++ b/otp/nametag/Nametag2d.py @@ -0,0 +1,113 @@ +from Nametag import * +from otp.margins.MarginPopup import * +from pandac.PandaModules import * +import math + +class Nametag2d(Nametag, MarginPopup): + SCALE_2D = 0.25 + CHAT_ALPHA = 0.5 + ARROW_OFFSET = -1.0 + ARROW_SCALE = 1.5 + + DEFAULT_CHAT_WORDWRAP = 8.0 + + def __init__(self): + Nametag.__init__(self) + MarginPopup.__init__(self) + + self.contents = self.CName|self.CSpeech + self.chatWordWrap = 7.5 + + self.arrow = None + + self.innerNP.setScale(self.SCALE_2D) + + def showBalloon(self, balloon, text): + text = '%s: %s' % (self.name, text) + Nametag.showBalloon(self, balloon, text) + + # Next, center the balloon in the cell: + balloon = NodePath.anyPath(self).find('*/balloon') + + # Calculate the center of the TextNode. + text = balloon.find('**/+TextNode') + t = text.node() + left, right, bottom, top = t.getFrameActual() + center = self.innerNP.getRelativePoint(text, + ((left+right)/2., 0, (bottom+top)/2.)) + + # Next translate the balloon along the inverse. + balloon.setPos(balloon, -center) + # Also translate the frame: + left, right, bottom, top = self.frame + self.frame = (left-center.getX(), right-center.getX(), + bottom-center.getZ(), top-center.getZ()) + + # When a balloon is active, we need to be somewhat higher-priority in the + # popup system: + self.setPriority(1) + + # Remove our pointer arrow: + if self.arrow is not None: + self.arrow.removeNode() + self.arrow = None + + def showName(self): + Nametag.showName(self) + + # Revert our priority back to basic: + self.setPriority(0) + + # Tack on an arrow: + t = self.innerNP.find('**/+TextNode') + arrowZ = self.ARROW_OFFSET + t.node().getBottom() + + self.arrow = NametagGlobals.arrowModel.copyTo(self.innerNP) + self.arrow.setZ(arrowZ) + self.arrow.setScale(self.ARROW_SCALE) + self.arrow.setColor(ARROW_COLORS.get(self.colorCode, self.nameFg)) + + def update(self, scale=True): + Nametag.update(self, scale) + self.considerUpdateClickRegion() + + def marginVisibilityChanged(self): + self.considerUpdateClickRegion() + + def considerUpdateClickRegion(self): + # If we are onscreen, we update our click region: + if self.isDisplayed(): + left, right, bottom, top = self.frame + self.updateClickRegion(left*self.SCALE_2D, right*self.SCALE_2D, + bottom*self.SCALE_2D, top*self.SCALE_2D) + else: + self.stashClickRegion() + + def tick(self): + # Update the arrow's pointing. + if not self.isDisplayed() or self.arrow is None: + return # No arrow or not onscreen. + + if self.avatar is None or self.avatar.isEmpty(): + return # No avatar, can't be done. + + # Get points needed in calculation: + cam = NametagGlobals.camera or base.cam + toon = NametagGlobals.toon or cam + + # libotp calculates this using the offset from localToon->avatar, but + # the orientation from cam. Therefore, we duplicate it like so: + location = self.avatar.getPos(toon) + rotation = toon.getQuat(cam) + + camSpacePos = rotation.xform(location) + arrowRadians = math.atan2(camSpacePos[0], camSpacePos[1]) + arrowDegrees = arrowRadians/math.pi*180 + + self.arrow.setR(arrowDegrees - 90) + + def getSpeechBalloon(self): + return NametagGlobals.speechBalloon2d + + def getThoughtBalloon(self): + return NametagGlobals.thoughtBalloon2d diff --git a/otp/nametag/Nametag3d.py b/otp/nametag/Nametag3d.py new file mode 100644 index 00000000..18e9834a --- /dev/null +++ b/otp/nametag/Nametag3d.py @@ -0,0 +1,71 @@ +from Nametag import * +import NametagGlobals +from NametagConstants import * +from pandac.PandaModules import * +import math + +class Nametag3d(Nametag): + WANT_DYNAMIC_SCALING = True + MAX_SCALE = 2.5 + SCALING_FACTOR = 0.055 + SCALING_MINDIST = 1 + SCALING_MAXDIST = math.pow(MAX_SCALE / SCALING_FACTOR, 2) + + BILLBOARD_OFFSET = 3.0 + SHOULD_BILLBOARD = True + + IS_3D = True + + def __init__(self): + Nametag.__init__(self) + + self.contents = self.CName|self.CSpeech|self.CThought + + self.bbOffset = self.BILLBOARD_OFFSET + self._doBillboard() + + def _doBillboard(self): + if self.SHOULD_BILLBOARD: + self.innerNP.setEffect(BillboardEffect.make( + Vec3(0,0,1), + True, + False, + self.bbOffset, + NodePath(), # Empty; look at scene camera + Point3(0,0,0))) + else: + self.bbOffset = 0.0 + + def setBillboardOffset(self, bbOffset): + self.bbOffset = bbOffset + self._doBillboard() + + def tick(self): + if not self.WANT_DYNAMIC_SCALING: + scale = self.SCALING_FACTOR + else: + # Attempt to maintain the same on-screen size. + distance = self.innerNP.getPos(NametagGlobals.camera).length() + distance = max(min(distance, self.SCALING_MAXDIST), self.SCALING_MINDIST) + + scale = math.sqrt(distance)*self.SCALING_FACTOR + + self.innerNP.setScale(scale) + + # As 3D nametags can move around on their own, we need to update the + # click frame constantly: + path = NodePath.anyPath(self) + if path.isHidden() or (path.getTop() != NametagGlobals.camera.getTop() and + path.getTop() != render2d): + self.stashClickRegion() + else: + left, right, bottom, top = self.frame + self.updateClickRegion(left*scale, right*scale, + bottom*scale, top*scale, + self.bbOffset) + + def getSpeechBalloon(self): + return NametagGlobals.speechBalloon3d + + def getThoughtBalloon(self): + return NametagGlobals.thoughtBalloon3d diff --git a/otp/nametag/NametagConstants.py b/otp/nametag/NametagConstants.py new file mode 100644 index 00000000..38307453 --- /dev/null +++ b/otp/nametag/NametagConstants.py @@ -0,0 +1,205 @@ +CFNoQuitButton=256 +CFPageButton=16 +CFQuicktalker=4 +CFQuitButton=32 +CFReversed=64 +CFSndOpenchat=128 +CFSpeech=1 +CFThought=2 +CFTimeout=8 + +CCNormal = 0 +CCNonPlayer = 1 +CCSuit = 2 +CCToonBuilding = 3 +CCSuitBuilding = 4 +CCHouseBuilding = 5 +CCSpeedChat = 6 + +NAMETAG_COLORS = { + CCNormal: ( + # Normal FG BG + ((0.3, 0.3, 0.7, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Click FG BG + ((0.3, 0.3, 0.7, 1.0), (0.2, 0.2, 0.2, 0.6), # Name + (1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Hover FG BG + ((0.5, 0.5, 1.0, 1.0), (1.0, 1.0, 1.0, 1.0), # Name + (0.0, 0.6, 0.6, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Disable FG BG + ((0.3, 0.3, 0.7, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + ), + CCNonPlayer: ( + # Normal FG BG + ((0.8, 0.4, 0.0, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Click FG BG + ((0.8, 0.4, 0.0, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Hover FG BG + ((0.8, 0.4, 0.0, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Disable FG BG + ((0.8, 0.4, 0.0, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + ), + CCSuit: ( + # Normal FG BG + ((0.2, 0.2, 0.2, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Click FG BG + ((0.2, 0.2, 0.2, 1.0), (0.2, 0.2, 0.2, 0.6), # Name + (1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Hover FG BG + ((0.4, 0.4, 0.4, 1.0), (1.0, 1.0, 1.0, 0.7), # Name + (0.0, 0.6, 0.6, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Disable FG BG + ((0.2, 0.2, 0.2, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + ), + CCSuitBuilding: ( + # Normal FG BG + ((0.5, 0.5, 0.5, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Click FG BG + ((0.5, 0.5, 0.5, 1.0), (0.2, 0.2, 0.2, 0.6), # Name + (1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Hover FG BG + ((0.7, 0.7, 0.7, 1.0), (1.0, 1.0, 1.0, 0.7), # Name + (0.0, 0.6, 0.6, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Disable FG BG + ((0.5, 0.5, 0.5, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + ), + CCToonBuilding: ( + # Normal FG BG + ((0.2, 0.6, 0.9, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Click FG BG + ((0.2, 0.6, 0.9, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Hover FG BG + ((0.2, 0.6, 0.9, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Disable FG BG + ((0.2, 0.6, 0.9, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + ), + CCHouseBuilding: ( + # Normal FG BG + ((0.2, 0.6, 0.9, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Click FG BG + ((0.2, 0.2, 0.5, 1.0), (0.2, 0.2, 0.2, 0.6), # Name + (1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Hover FG BG + ((0.5, 0.5, 1.0, 1.0), (1.0, 1.0, 1.0, 1.0), # Name + (0.0, 0.6, 0.6, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Disable FG BG + ((0.0, 0.6, 0.2, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + ), + CCSpeedChat: ( + # Normal FG BG + ((0.0, 0.6, 0.2, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Click FG BG + ((0.0, 0.5, 0.0, 1.0), (0.5, 0.5, 0.5, 0.6), # Name + (1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Hover FG BG + ((0.0, 0.7, 0.2, 1.0), (1.0, 1.0, 1.0, 0.7), # Name + (0.0, 0.6, 0.6, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + # Disable FG BG + ((0.0, 0.6, 0.2, 1.0), (0.8, 0.8, 0.8, 0.5), # Name + (0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)), # Chat + ) +} + +ARROW_COLORS = { + CCSuit: (0.8, 0.4, 0.0, 1.0), +} + +DEFAULT_WORDWRAPS = { + CCNormal: 7.5, + CCNonPlayer: 7.5, + CCSuit: 7.5, + CCToonBuilding: 8.5, + CCSuitBuilding: 8.5, + CCHouseBuilding: 10.0, + CCSpeedChat: 7.5 +} + +WTNormal = 0 +WTQuickTalker = 1 +WTSystem = 2 +WTBattleSOS = 3 +WTEmote = 4 +WTToontownBoardingGroup = 5 + +WHISPER_COLORS = { + WTNormal: ( + # Normal FG BG + ((0.0, 0.0, 0.0, 1.0), (0.2, 0.6, 0.8, 0.6)), + # Click FG BG + ((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 0.8)), + # Hover FG BG + ((0.0, 0.0, 0.0, 1.0), (0.2, 0.7, 0.9, 0.6)), + # Disable FG BG + ((0.0, 0.0, 0.0, 1.0), (0.2, 0.7, 0.8, 0.6)) + ), + WTQuickTalker: ( + # Normal FG BG + ((0.0, 0.0, 0.0, 1.0), (0.2, 0.6, 0.8, 0.6)), + # Click FG BG + ((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 0.8)), + # Hover FG BG + ((0.0, 0.0, 0.0, 1.0), (0.2, 0.7, 0.9, 0.6)), + # Disable FG BG + ((0.0, 0.0, 0.0, 1.0), (0.2, 0.7, 0.8, 0.6)) + ), + WTSystem: ( + # Normal FG BG + ((0.0, 0.0, 0.0, 1.0), (0.8, 0.3, 0.6, 0.6)), + # Click FG BG + ((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 0.8)), + # Hover FG BG + ((0.0, 0.0, 0.0, 1.0), (0.8, 0.4, 1.0, 0.6)), + # Disable FG BG + ((0.0, 0.0, 0.0, 1.0), (0.8, 0.3, 0.6, 0.6)) + ), + WTBattleSOS: ( + # Normal FG BG + ((0.0, 0.0, 0.0, 1.0), (0.8, 0.3, 0.6, 0.6)), + # Click FG BG + ((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 0.8)), + # Hover FG BG + ((0.0, 0.0, 0.0, 1.0), (0.8, 0.4, 0.0, 0.8)), + # Disable FG BG + ((0.0, 0.0, 0.0, 1.0), (0.8, 0.3, 0.6, 0.6)) + ), + WTEmote: ( + # Normal FG BG + ((0.0, 0.0, 0.0, 1.0), (0.9, 0.5, 0.1, 0.6)), + # Click FG BG + ((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 0.8)), + # Hover FG BG + ((0.0, 0.0, 0.0, 1.0), (0.9, 0.6, 0.2, 0.6)), + # Disable FG BG + ((0.0, 0.0, 0.0, 1.0), (0.9, 0.6, 0.1, 0.6)) + ), + WTToontownBoardingGroup: ( + # Normal FG BG + ((0.0, 0.0, 0.0, 1.0), (0.9, 0.5, 0.1, 0.6)), + # Click FG BG + ((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 0.8)), + # Hover FG BG + ((0.0, 0.0, 0.0, 1.0), (0.9, 0.6, 0.2, 0.6)), + # Disable FG BG + ((0.0, 0.0, 0.0, 1.0), (0.9, 0.6, 0.1, 0.6)) + ) +} + +def getFriendColor(handle): + return CCNormal if base.localAvatar.isTrueFriends(handle.doId) else CCSpeedChat \ No newline at end of file diff --git a/otp/nametag/NametagFloat2d.py b/otp/nametag/NametagFloat2d.py new file mode 100644 index 00000000..258e2e97 --- /dev/null +++ b/otp/nametag/NametagFloat2d.py @@ -0,0 +1,7 @@ +from Nametag3d import * + +class NametagFloat2d(Nametag3d): + WANT_DYNAMIC_SCALING = False + SCALING_FACTOR = 1.0 + SHOULD_BILLBOARD = False + IS_3D = False diff --git a/otp/nametag/NametagFloat3d.py b/otp/nametag/NametagFloat3d.py new file mode 100644 index 00000000..0d331f58 --- /dev/null +++ b/otp/nametag/NametagFloat3d.py @@ -0,0 +1,7 @@ +from Nametag3d import * + +class NametagFloat3d(Nametag3d): + WANT_DYNAMIC_SCALING = False + SCALING_FACTOR = 1.0 + SHOULD_BILLBOARD = True + IS_3D = False diff --git a/otp/nametag/NametagGlobals.py b/otp/nametag/NametagGlobals.py new file mode 100644 index 00000000..7f74d09a --- /dev/null +++ b/otp/nametag/NametagGlobals.py @@ -0,0 +1,100 @@ +camera = None +def setCamera(cam): + global camera + camera = cam + +arrowModel = None +def setArrowModel(am): + global arrowModel + arrowModel = am + +nametagCardModel = None +nametagCardDimensions = None +def setNametagCard(model, dimensions): + global nametagCardModel, nametagCardDimensions + nametagCardModel = model + nametagCardDimensions = dimensions + +mouseWatcher = None +def setMouseWatcher(mw): + global mouseWatcher + mouseWatcher = mw + +speechBalloon3d = None +def setSpeechBalloon3d(sb3d): + global speechBalloon3d + speechBalloon3d = sb3d + +thoughtBalloon3d = None +def setThoughtBalloon3d(tb3d): + global thoughtBalloon3d + thoughtBalloon3d = tb3d + +speechBalloon2d = None +def setSpeechBalloon2d(sb2d): + global speechBalloon2d + speechBalloon2d = sb2d + +thoughtBalloon2d = None +def setThoughtBalloon2d(tb2d): + global thoughtBalloon2d + thoughtBalloon2d = tb2d + +pageButtons = {} +def setPageButton(state, model): + pageButtons[state] = model + +quitButtons = {} +def setQuitButton(state, model): + quitButtons[state] = model + +rolloverSound = None +def setRolloverSound(ros): + global rolloverSound + rolloverSound = ros + +clickSound = None +def setClickSound(cs): + global clickSound + clickSound = cs + +toon = None +def setToon(t): + global toon + toon = t + +masterArrowsOn = 0 +def setMasterArrowsOn(mao): + global masterArrowsOn + masterArrowsOn = mao + +masterNametagsActive = 0 +def setMasterNametagsActive(mna): + global masterNametagsActive + masterNametagsActive = mna + +min2dAlpha = 0.0 +def setMin2dAlpha(m2a): + global min2dAlpha + min2dAlpha = m2a + +def getMin2dAlpha(): + global min2dAlpha + return min2dAlpha + +max2dAlpha = 0.0 +def setMax2dAlpha(m2a): + global max2dAlpha + max2dAlpha = m2a + +def getMax2dAlpha(): + global max2dAlpha + return max2dAlpha + +onscreenChatForced = 0 +def setOnscreenChatForced(ocf): + global onscreenChatForced + onscreenChatForced = ocf + +def setGlobalNametagScale(s): + pass diff --git a/otp/nametag/NametagGroup.py b/otp/nametag/NametagGroup.py new file mode 100644 index 00000000..04490c7f --- /dev/null +++ b/otp/nametag/NametagGroup.py @@ -0,0 +1,322 @@ +from pandac.PandaModules import * +from NametagConstants import * +from Nametag3d import * +from Nametag2d import * +import subprocess + +class NametagGroup: + CCNormal = CCNormal + CCNonPlayer = CCNonPlayer + CCSuit = CCSuit + CCToonBuilding = CCToonBuilding + CCSuitBuilding = CCSuitBuilding + CCHouseBuilding = CCHouseBuilding + CCSpeedChat = CCSpeedChat + + CHAT_TIMEOUT_MAX = 12.0 + CHAT_TIMEOUT_MIN = 4.0 + CHAT_TIMEOUT_PROP = 0.5 + + def __init__(self): + self.nametag2d = Nametag2d() + self.nametag3d = Nametag3d() + self.icon = PandaNode('icon') + + self.chatTimeoutTask = None + + self.font = None + self.speechFont = None + self.name = '' + self.displayName = '' + self.wordWrap = None + self.qtColor = VBase4(1,1,1,1) + self.colorCode = CCNormal + self.avatar = None + self.active = True + + self.chatPages = [] + self.chatPage = 0 + self.chatFlags = 0 + + self.objectCode = None + + self.manager = None + + self.nametags = [] + self.addNametag(self.nametag2d) + self.addNametag(self.nametag3d) + + self.visible3d = True # Is a 3D nametag visible, or do we need a 2D popup? + + self.tickTask = taskMgr.add(self.__tickTask, self.getUniqueId(), sort=45) + + self.stompTask = None + self.stompText = None + self.stompFlags = 0 + + def destroy(self): + taskMgr.remove(self.tickTask) + if self.manager is not None: + self.unmanage(self.manager) + for nametag in list(self.nametags): + self.removeNametag(nametag) + if self.stompTask: + self.stompTask.remove() + + def getNametag2d(self): + return self.nametag2d + + def getNametag3d(self): + return self.nametag3d + + def getNameIcon(self): + return self.icon + + def getNumChatPages(self): + if not self.chatFlags & (CFSpeech|CFThought): + return 0 + + return len(self.chatPages) + + def setPageNumber(self, page): + self.chatPage = page + self.updateTags() + + def getChatStomp(self): + return bool(self.stompTask) + + def getChat(self): + if self.chatPage >= len(self.chatPages): + return '' + else: + return self.chatPages[self.chatPage] + + def getStompText(self): + return self.stompText + + def getStompDelay(self): + return 0.2 + + def getUniqueId(self): + return 'Nametag-%d' % id(self) + + def hasButton(self): + return bool(self.getButtons()) + + def getButtons(self): + if self.getNumChatPages() < 2: + # Either only one page or no pages displayed. This means no button, + # unless the game code specifically requests one. + if self.chatFlags & CFQuitButton: + return NametagGlobals.quitButtons + elif self.chatFlags & CFPageButton: + return NametagGlobals.pageButtons + else: + return None + elif self.chatPage == self.getNumChatPages()-1: + # Last page of a multiple-page chat. This calls for a quit button, + # unless the game says otherwise. + if not self.chatFlags & CFNoQuitButton: + return NametagGlobals.quitButtons + else: + return None + else: + # Non-last page of a multiple-page chat. This calls for a page + # button, but only if the game requests it: + if self.chatFlags & CFPageButton: + return NametagGlobals.pageButtons + else: + return None + + def setActive(self, active): + self.active = active + + def isActive(self): + return self.active + + def setAvatar(self, avatar): + self.avatar = avatar + + def setFont(self, font): + self.font = font + self.updateTags() + + def setSpeechFont(self, font): + self.speechFont = font + self.updateTags() + + def setWordwrap(self, wrap): + self.wordWrap = wrap + self.updateTags() + + def setColorCode(self, cc): + self.colorCode = cc + self.updateTags() + + def setName(self, name): + self.name = name + self.updateTags() + + def setDisplayName(self, name): + self.displayName = name + self.updateTags() + + def setQtColor(self, color): + self.qtColor = color + self.updateTags() + + def setChat(self, chatString, chatFlags): + if not self.chatFlags&CFSpeech: + # We aren't already displaying some chat. Therefore, we don't have + # to stomp. + self._setChat(chatString, chatFlags) + else: + # Stomp! + self.clearChat() + self.stompText = chatString + self.stompFlags = chatFlags + self.stompTask = taskMgr.doMethodLater(self.getStompDelay(), self.__updateStomp, + 'ChatStomp-' + self.getUniqueId()) + + def _setChat(self, chatString, chatFlags): + if chatString: + self.chatPages = chatString.split('\x07') + self.chatFlags = chatFlags + else: + self.chatPages = [] + self.chatFlags = 0 + self.setPageNumber(0) # Calls updateTags() for us. + + self._stopChatTimeout() + if chatFlags&CFTimeout: + self._startChatTimeout() + + def __updateStomp(self, task): + self._setChat(self.stompText, self.stompFlags) + self.stompTask = None + + def setContents(self, contents): + # This function is a little unique, it's meant to override contents on + # EXISTING nametags only: + for tag in self.nametags: + tag.setContents(contents) + + def setObjectCode(self, objectCode): + self.objectCode = objectCode + + def getObjectCode(self): + return self.objectCode + + def _startChatTimeout(self): + length = len(self.getChat()) + timeout = min(max(length*self.CHAT_TIMEOUT_PROP, self.CHAT_TIMEOUT_MIN), self.CHAT_TIMEOUT_MAX) + self.chatTimeoutTask = taskMgr.doMethodLater(timeout, self.__doChatTimeout, + 'ChatTimeout-' + self.getUniqueId()) + + def __doChatTimeout(self, task): + self._setChat('', 0) + return task.done + + def _stopChatTimeout(self): + if self.chatTimeoutTask: + taskMgr.remove(self.chatTimeoutTask) + + def clearShadow(self): + pass + + def clearChat(self): + self._setChat('', 0) + if self.stompTask: + self.stompTask.remove() + + def updateNametag(self, tag): + tag.font = self.font + tag.speechFont = self.speechFont + tag.name = self.name + tag.wordWrap = self.wordWrap or DEFAULT_WORDWRAPS[self.colorCode] + tag.displayName = self.displayName or self.name + tag.qtColor = self.qtColor + tag.colorCode = self.colorCode + tag.chatString = self.getChat() + tag.buttons = self.getButtons() + tag.chatFlags = self.chatFlags + tag.avatar = self.avatar + tag.icon = self.icon + + if settings['talk2speech']: + subprocess.Popen('espeak "%s"' % tag.chatString) + + tag.update() + + def __testVisible3D(self): + # We must determine if a 3D nametag is visible or not, since this + # affects the visibility state of 2D nametags. + + # Next, we iterate over all of our nametags until we find a visible + # one: + for nametag in self.nametags: + if not isinstance(nametag, Nametag3d): + continue # It's not in the 3D system, disqualified. + + if nametag.isOnScreen(): + return True + + # If we got here, none of the tags were a match... + return False + + + def __tickTask(self, task): + for nametag in self.nametags: + nametag.tick() + if (NametagGlobals.masterNametagsActive and self.active) or self.hasButton(): + nametag.setClickRegionEvent(self.getUniqueId()) + else: + nametag.setClickRegionEvent(None) + + if NametagGlobals.onscreenChatForced and self.chatFlags & CFSpeech: + # Because we're *forcing* chat onscreen, we skip the visible3d test + # and go ahead and display it anyway. + visible3d = False + elif not NametagGlobals.masterArrowsOn and not self.chatFlags: + # We're forcing margins offscreen; therefore, we should pretend + # that the 3D nametag is always visible. + visible3d = True + else: + visible3d = self.__testVisible3D() + + if visible3d ^ self.visible3d: + self.visible3d = visible3d + for nametag in self.nametags: + if isinstance(nametag, MarginPopup): + nametag.setVisible(not visible3d) + + return task.cont + + def updateTags(self): + for nametag in self.nametags: + self.updateNametag(nametag) + + def addNametag(self, nametag): + self.nametags.append(nametag) + self.updateNametag(nametag) + if self.manager is not None and isinstance(nametag, MarginPopup): + nametag.manage(manager) + + def removeNametag(self, nametag): + self.nametags.remove(nametag) + if self.manager is not None and isinstance(nametag, MarginPopup): + nametag.unmanage(manager) + nametag.destroy() + + def manage(self, manager): + self.manager = manager + for tag in self.nametags: + if isinstance(tag, MarginPopup): + tag.manage(manager) + + def unmanage(self, manager): + self.manager = None + for tag in self.nametags: + if isinstance(tag, MarginPopup): + tag.unmanage(manager) + tag.destroy() diff --git a/otp/nametag/__init__.py b/otp/nametag/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/otp/otpbase/BackupManager.py b/otp/otpbase/BackupManager.py new file mode 100755 index 00000000..bbb96adf --- /dev/null +++ b/otp/otpbase/BackupManager.py @@ -0,0 +1,29 @@ +import json +import os + + +class BackupManager: + def __init__(self, filepath='dependencies/backups/', extension='.json'): + self.filepath = filepath + self.extension = extension + + def getFileName(self, category, info): + filename = os.path.join(self.filepath, category) + '/' + for i in info: + filename += str(i) + '_' + return filename[:-1] + self.extension + + def load(self, category, info, default=None): + filename = self.getFileName(category, info) + if not os.path.exists(filename): + return default + with open(filename, 'r') as f: + return json.load(f) + + def save(self, category, info, data): + filepath = os.path.join(self.filepath, category) + if not os.path.exists(filepath): + os.makedirs(filepath) + filename = self.getFileName(category, info) + with open(filename, 'w') as f: + json.dump(data, f) diff --git a/otp/otpbase/OTPBase.py b/otp/otpbase/OTPBase.py new file mode 100755 index 00000000..2071199e --- /dev/null +++ b/otp/otpbase/OTPBase.py @@ -0,0 +1,135 @@ +from direct.showbase.ShowBase import ShowBase +from otp.ai.MagicWordGlobal import * +from otp.chat import WhiteList, WhiteListData, SequenceListData +from pandac.PandaModules import Camera, TPLow, VBase4, ColorWriteAttrib, Filename, getModelPath, NodePath, Vec4 +import OTPGlobals, OTPRender, math + +class OTPBase(ShowBase): + + def __init__(self, windowType = None): + ShowBase.__init__(self, windowType=windowType) + self.idTags = config.GetBool('want-id-tags', 0) + if not self.idTags: + del self.idTags + self.wantNametags = self.config.GetBool('want-nametags', 1) + self.wantDynamicShadows = 1 + self.stereoEnabled = False + self.whiteList = None + + if config.GetBool('want-whitelist', True): + self.whiteList = WhiteList.WhiteList() + self.whiteList.setWords(WhiteListData.WHITELIST) + + if config.GetBool('want-sequence-list', True): + self.whiteList.setSequenceList(SequenceListData.SEQUENCES) + + base.cam.node().setCameraMask(OTPRender.MainCameraBitmask) + taskMgr.setupTaskChain('net', numThreads=1, frameBudget=0.001, threadPriority=TPLow) + + def getRepository(self): + return self.cr + + def run(self): + try: + taskMgr.run() + except SystemExit: + self.notify.info('Normal exit.') + self.destroy() + raise + except: + self.notify.warning('Handling Python exception.') + if getattr(self, 'cr', None): + if self.cr.timeManager: + from otp.otpbase import OTPGlobals + self.cr.timeManager.setDisconnectReason(OTPGlobals.DisconnectPythonError) + self.cr.timeManager.setExceptionInfo() + self.cr.sendDisconnect() + self.notify.info('Exception exit.\n') + self.destroy() + import traceback + traceback.print_exc() + + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def oobe(): + """ + Toggle the 'out of body experience' view. + """ + base.oobe() + +@magicWord(category=CATEGORY_PROGRAMMER) +def oobeCull(): + """ + Toggle the 'out of body experience' view with culling debugging. + """ + base.oobeCull() + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def wire(): + """ + Toggle the 'wireframe' view. + """ + base.toggleWireframe() + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def idNametags(): + """ + Display avatar IDs inside nametags. + """ + messenger.send('nameTagShowAvId') + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def nameNametags(): + """ + Display only avatar names inside nametags. + """ + messenger.send('nameTagShowName') + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def a2d(): + """ + Toggle aspect2d. + """ + if aspect2d.isHidden(): + aspect2d.show() + else: + aspect2d.hide() + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def placer(): + """ + Toggle the camera placer. + """ + base.camera.place() + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def explorer(): + """ + Toggle the scene graph explorer. + """ + base.render.explore() + + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def neglect(): + """ + toggle the neglection of network updates on the invoker's client. + """ + if base.cr.networkPlugPulled(): + base.cr.restoreNetworkPlug() + return 'You are no longer neglecting network updates.' + else: + base.cr.pullNetworkPlug() + return 'You are now neglecting network updates.' + + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[float, float, float, float]) +def backgroundColor(r=None, g=1, b=1, a=1): + """ + set the background color. Specify no arguments for the default background + color. + """ + if r is None: + r, g, b, a = OTPGlobals.DefaultBackgroundColor + base.setBackgroundColor(Vec4(r, g, b, a)) + return 'The background color has been changed.' diff --git a/otp/otpbase/OTPGlobals.py b/otp/otpbase/OTPGlobals.py new file mode 100755 index 00000000..80a91425 --- /dev/null +++ b/otp/otpbase/OTPGlobals.py @@ -0,0 +1,298 @@ +from panda3d.core import * + +QuietZone = 1 +UberZone = 2 +WallBitmask = BitMask32(1) +FloorBitmask = BitMask32(2) +CameraBitmask = BitMask32(4) +CameraTransparentBitmask = BitMask32(8) +SafetyNetBitmask = BitMask32(512) +SafetyGateBitmask = BitMask32(1024) +GhostBitmask = BitMask32(2048) +PathFindingBitmask = BitMask32.bit(29) +PickerBitmask = BitMask32(4096) +DefaultCameraFov = 52.0 +MaxCameraFov = 120.0 +DefaultCameraFar = 800.0 +DefaultCameraNear = 1.0 +AICollisionPriority = 10 +AICollMovePriority = 8 +MaxFriends = 200 +MaxBackCatalog = 48 +MaxCustomMessages = 25 +SPInvalid = 0 +SPHidden = 1 +SPRender = 2 +SPDynamic = 5 +CENormal = 0 +CEBigHead = 1 +CESmallHead = 2 +CEBigLegs = 3 +CESmallLegs = 4 +CEBigToon = 5 +CESmallToon = 6 +CEFlatPortrait = 7 +CEFlatProfile = 8 +CETransparent = 9 +CENoColor = 10 +CEInvisible = 11 +CEPumpkin = 12 +CEBigWhite = 13 +CESnowMan = 14 +CEGreenToon = 15 +CEGhost = 'g' +CEName2Id = { + 'normal': CENormal, + 'bighead': CEBigHead, + 'smallhead': CESmallHead, + 'biglegs': CEBigLegs, + 'smalllegs': CESmallLegs, + 'bigtoon': CEBigToon, + 'smalltoon': CESmallToon, + 'flatportrait': CEFlatPortrait, + 'flatprofile': CEFlatProfile, + 'transparent': CETransparent, + 'nocolor': CENoColor, + 'invisible': CEInvisible, + 'pumpkin': CEPumpkin, + 'bigwhite': CEBigWhite, + 'snowman': CESnowMan, + 'greentoon': CEGreenToon +} +BigToonScale = 1.5 +SmallToonScale = 0.5 +DisconnectNone = 0 +DisconnectBookExit = 1 +DisconnectCloseWindow = 2 +DisconnectPythonError = 3 +DisconnectSwitchShards = 4 +DisconnectGraphicsError = 5 +DatabaseDialogTimeout = 20.0 +DatabaseGiveupTimeout = 45.0 +WalkCutOff = 0.5 +RunCutOff = 8.0 +FloorOffset = 0.025 +AvatarDefaultRadius = 1 +InterfaceFont = None +InterfaceFontPath = None +SignFont = None +SignFontPath = None +ChalkFont = None +ChalkFontPath = None +NametagFonts = {} +NametagFontPaths = {} +DialogClass = None +GlobalDialogClass = None + +def getInterfaceFont(): + global InterfaceFontPath + global InterfaceFont + if InterfaceFont == None: + if InterfaceFontPath == None: + InterfaceFont = TextNode.getDefaultFont() + else: + InterfaceFont = loader.loadFont(InterfaceFontPath, lineHeight=1.0) + return InterfaceFont + +def setInterfaceFont(path): + global InterfaceFontPath + global InterfaceFont + InterfaceFontPath = path + InterfaceFont = None + +def getSignFont(): + global SignFont + global SignFontPath + if SignFont == None: + if SignFontPath == None: + InterfaceFont = TextNode.getDefaultFont() + SignFont = TextNode.getDefaultFont() + else: + SignFont = loader.loadFont(SignFontPath, lineHeight=1.0) + return SignFont + +def setSignFont(path): + global SignFontPath + SignFontPath = path + +def getChalkFont(): + global ChalkFontPath + global ChalkFont + if ChalkFont == None: + if ChalkFontPath == None: + InterfaceFont = TextNode.getDefaultFont() + ChalkFont = TextNode.getDefaultFont() + else: + ChalkFont = loader.loadFont(ChalkFontPath, lineHeight=1.0) + return ChalkFont + +def setChalkFont(path): + global ChalkFontPath + ChalkFontPath = path + +def getNametagFont(index): + global NametagFontPaths + global NametagFonts + if (index not in NametagFonts) or (NametagFonts[index] is None): + if (index not in NametagFontPaths) or (NametagFontPaths[index] is None): + InterfaceFont = TextNode.getDefaultFont() + NametagFonts[index] = TextNode.getDefaultFont() + else: + NametagFonts[index] = loader.loadFont(NametagFontPaths[index], lineHeight=1.0) + return NametagFonts[index] + +def setNametagFont(index, path): + NametagFontPaths[index] = path + +def getDialogClass(): + global DialogClass + if DialogClass == None: + from otp.otpgui.OTPDialog import OTPDialog + DialogClass = OTPDialog + return DialogClass + +def getGlobalDialogClass(): + global GlobalDialogClass + if DialogClass == None: + from otp.otpgui.OTPDialog import GlobalDialog + GlobalDialogClass = GlobalDialog + return GlobalDialogClass + +def setDialogClasses(dialogClass, globalDialogClass): + global DialogClass + global GlobalDialogClass + DialogClass = dialogClass + GlobalDialogClass = globalDialogClass + +NetworkLatency = 1.0 +STAND_INDEX = 0 +WALK_INDEX = 1 +RUN_INDEX = 2 +REVERSE_INDEX = 3 +STRAFE_LEFT_INDEX = 4 +STRAFE_RIGHT_INDEX = 5 +ToonStandableGround = 0.707 +ToonSpeedFactor = 1.25 +ToonForwardSpeed = 16.0 * ToonSpeedFactor +ToonJumpForce = 24.0 +ToonReverseSpeed = 8.0 * ToonSpeedFactor +ToonRotateSpeed = 80.0 * ToonSpeedFactor +ToonForwardSlowSpeed = 6.0 +ToonJumpSlowForce = 4.0 +ToonReverseSlowSpeed = 2.5 +ToonRotateSlowSpeed = 33.0 +ThinkPosHotkey = 'shift-f1' +PlaceMarkerHotkey = 'f2' +FriendsListHotkey = 'f7' +StickerBookHotkey = 'f8' +OptionsPageHotkey = 'escape' +ScreenshotHotkey = 'f9' +QuestsHotkeyOn = 'end' +QuestsHotkeyOff = 'end-up' +InventoryHotkeyOn = 'home' +InventoryHotkeyOff = 'home-up' +MapHotkeyOn = 'delete' +MapHotkeyOff = 'delete-up' +QuitGameHotKeyOSX = 'meta-q' +QuitGameHotKeyRepeatOSX = 'meta-q-repeat' +HideGameHotKeyOSX = 'meta-h' +HideGameHotKeyRepeatOSX = 'meta-h-repeat' +MinimizeGameHotKeyOSX = 'meta-m' +MinimizeGameHotKeyRepeatOSX = 'meta-m-repeat' +GlobalDialogColor = (1, 1, 0.75, 1) +DefaultBackgroundColor = (0.3, 0.3, 0.3, 1) +toonBodyScales = { + 'mouse': 0.6, + 'cat': 0.73, + 'duck': 0.66, + 'rabbit': 0.74, + 'horse': 0.85, + 'dog': 0.85, + 'monkey': 0.68, + 'bear': 0.85, + 'pig': 0.77 +} +toonHeadScales = { + 'mouse': Point3(1.0), + 'cat': Point3(1.0), + 'duck': Point3(1.0), + 'rabbit': Point3(1.0), + 'horse': Point3(1.0), + 'dog': Point3(1.0), + 'monkey': Point3(1.0), + 'bear': Point3(1.0), + 'pig': Point3(1.0) +} +legHeightDict = { + 's': 1.5, + 'm': 2.0, + 'l': 2.75 +} +torsoHeightDict = { + 's': 1.5, + 'm': 1.75, + 'l': 2.25, + 'ss': 1.5, + 'ms': 1.75, + 'ls': 2.25, + 'sd': 1.5, + 'md': 1.75, + 'ld': 2.25 +} +headHeightDict = { + 'dls': 0.75, + 'dss': 0.5, + 'dsl': 0.5, + 'dll': 0.75, + 'cls': 0.75, + 'css': 0.5, + 'csl': 0.5, + 'cll': 0.75, + 'hls': 0.75, + 'hss': 0.5, + 'hsl': 0.5, + 'hll': 0.75, + 'mls': 0.75, + 'mss': 0.5, + 'rls': 0.75, + 'rss': 0.5, + 'rsl': 0.5, + 'rll': 0.75, + 'fls': 0.75, + 'fss': 0.5, + 'fsl': 0.5, + 'fll': 0.75, + 'pls': 0.75, + 'pss': 0.5, + 'psl': 0.5, + 'pll': 0.75, + 'bls': 0.75, + 'bss': 0.5, + 'bsl': 0.5, + 'bll': 0.75, + 'sls': 0.75, + 'sss': 0.5, + 'ssl': 0.5, + 'sll': 0.75 +} +RandomButton = 'Randomize' +TypeANameButton = 'Type Name' +PickANameButton = 'Pick-A-Name' +NameShopSubmitButton = 'Submit' +RejectNameText = 'That name is not allowed. Please try again.' +WaitingForNameSubmission = 'Submitting your name...' +NameShopNameMaster = 'NameMasterEnglish.txt' +NameShopContinueSubmission = 'Continue Submission' +NameShopChooseAnother = 'Choose Another Name' +NameShopToonCouncil = 'The Toon Council\nwill review your\nname. ' + 'Review may\ntake a few days.\nWhile you wait\nyour name will be\n ' +PleaseTypeName = 'Please type your name:' +AllNewNames = 'All new names\nmust be approved\nby the Toon Council.' +NameShopNameRejected = 'The name you\nsubmitted has\nbeen rejected.' +NameShopNameAccepted = 'Congratulations!\nThe name you\nsubmitted has\nbeen accepted!' +NoPunctuation = "You can't use punctuation marks in your name!" +PeriodOnlyAfterLetter = 'You can use a period in your name, but only after a letter.' +ApostropheOnlyAfterLetter = 'You can use an apostrophe in your name, but only after a letter.' +NoNumbersInTheMiddle = 'Numeric digits may not appear in the middle of a word.' +ThreeWordsOrLess = 'Your name must be three words or fewer.' + +TeleportFailCooldown = 2.0 \ No newline at end of file diff --git a/otp/otpbase/OTPLocalizer.py b/otp/otpbase/OTPLocalizer.py new file mode 100755 index 00000000..7490cfe7 --- /dev/null +++ b/otp/otpbase/OTPLocalizer.py @@ -0,0 +1,35 @@ +import string +import types + +try: + language = settings['language'] +except: + language = 'English' + +print 'OTPLocalizer: Running in language: %s' % language +from otp.otpbase.OTPLocalizerEnglish import * + +if language != 'English': + l = {} + g = {} + module = 'otp.otpbase.OTPLocalizer' + language + englishModule = __import__('otp.otpbase.OTPLocalizerEnglish', g, l) + foreignModule = __import__(module, g, l) + for key, val in englishModule.__dict__.items(): + if key not in foreignModule.__dict__: + print 'WARNING: Foreign module: %s missing key: %s' % (module, key) + locals()[key] = val + elif isinstance(val, types.DictType): + fval = foreignModule.__dict__.get(key) + for dkey, dval in val.items(): + if dkey not in fval: + print 'WARNING: Foreign module: %s missing key: %s.%s' % (module, key, dkey) + fval[dkey] = dval + + for dkey in fval.keys(): + if dkey not in val: + print 'WARNING: Foreign module: %s extra key: %s.%s' % (module, key, dkey) + + for key in foreignModule.__dict__.keys(): + if key not in englishModule.__dict__: + print 'WARNING: Foreign module: %s extra key: %s' % (module, key) diff --git a/otp/otpbase/OTPLocalizerEnglish.py b/otp/otpbase/OTPLocalizerEnglish.py new file mode 100755 index 00000000..7878f6f8 --- /dev/null +++ b/otp/otpbase/OTPLocalizerEnglish.py @@ -0,0 +1,2346 @@ +import string +from otp.otpbase.OTPLocalizerEnglishProperty import * +lTheBrrrgh = 'The Brrrgh' +lDaisyGardens = 'Daisy Gardens' +lDonaldsDock = "Donald's Dock" +lDonaldsDreamland = "Donald's Dreamland" +lMinniesMelodyland = "Minnie's Melodyland" +lToontownCentral = 'Toontown Central' +lGoofySpeedway = 'Goofy Speedway' +lOutdoorZone = "Chip 'n Dale's Acorn Acres" +lGolfZone = "Chip 'n Dale's MiniGolf" +lCancel = 'Cancel' +lClose = 'Close' +lOK = 'OK' +lNext = 'Next' +lNo = 'No' +lQuit = 'Quit' +lYes = 'Yes' +Cog = 'Cog' +Cogs = 'Cogs' +DialogOK = lOK +DialogCancel = lCancel +DialogYes = lYes +DialogNo = lNo +DialogDoNotShowAgain = 'Do Not\nShow Again' +WhisperNoLongerFriend = '%s left your friends list.' +WhisperNowSpecialFriend = '%s is now your True Friend!' +WhisperComingToVisit = '%s is coming to visit you.' +WhisperFailedVisit = '%s tried to visit you.' +WhisperTargetLeftVisit = '%s has gone somewhere else. Try again!' +WhisperGiveupVisit = "%s couldn't find you because you're moving around!" +TeleportGreeting = 'Hi, %s.' +WhisperFriendComingOnline = '%s is coming online!' +WhisperFriendLoggedOut = '%s has logged out.' +DialogSpecial = 'ooo' +DialogExclamation = '!' +DialogQuestion = '?' +ChatInputNormalSayIt = 'Say It' +ChatInputNormalCancel = lCancel +ChatInputNormalWhisper = 'Whisper' +ChatInputWhisperLabel = 'To %s' +SCEmoteNoAccessMsg = 'You do not have access\nto this emotion yet.' +SCEmoteNoAccessOK = lOK +ChatGarblerDefault = ['blah'] +ChatManagerChat = 'Chat' +ChatManagerWhisperTo = 'Whisper to:' +ChatManagerWhisperToName = 'Whisper To:\n%s' +ChatManagerCancel = lCancel +ChatManagerWhisperOffline = '%s is offline.' +# True Friends +NoTrueFriendsTitle = 'Open Chat With True Friends' +NoTrueFriends = 'Open Chat with True Friends allows real-life friends to chat openly with each other by means of a True Friend Code that must be shared outside of the game.\n\nTo activate these features or to learn more, exit Toontown and then click on Membership and select Manage Account. Log in to edit your "Community Settings."' +# Speedchat Plus +NoSpeedchatPlusTitle = 'Chat Button' +NoSpeedchatPlus = 'You can use the blue Chat button to communicate with other Toons by using Speechat Plus or Open Chat with True Friends.\n\nSpeedchat Plus is a form of type chat that allows users to communicate by using the SpeedChat Plus dictionary.\n\nOpen Chat with True Friends allows real-life friends to chat openly with each other by means of a True Friend Code that must be shared outside of the game.\n\nTo activate these features or to learn more, exit Toontown and then click on Membership and select Manage Account. Log in to edit your "Community Settings."' +NoTrueFriendsOK = lOK +WhisperToFormat = 'To %s %s' +WhisperToFormatName = 'To %s' +WhisperFromFormatName = '%s whispers' +ThoughtOtherFormatName = '%s thinks' +ThoughtSelfFormatName = 'You think' +from pandac.PandaModules import TextProperties +from pandac.PandaModules import TextPropertiesManager +shadow = TextProperties() +shadow.setShadow(-0.025, -0.025) +shadow.setShadowColor(0, 0, 0, 1) +TextPropertiesManager.getGlobalPtr().setProperties('shadow', shadow) +red = TextProperties() +red.setTextColor(1, 0, 0, 1) +TextPropertiesManager.getGlobalPtr().setProperties('red', red) +green = TextProperties() +green.setTextColor(0, 1, 0, 1) +TextPropertiesManager.getGlobalPtr().setProperties('green', green) +yellow = TextProperties() +yellow.setTextColor(1, 1, 0, 1) +TextPropertiesManager.getGlobalPtr().setProperties('yellow', yellow) +midgreen = TextProperties() +midgreen.setTextColor(0.2, 1, 0.2, 1) +TextPropertiesManager.getGlobalPtr().setProperties('midgreen', midgreen) +blue = TextProperties() +blue.setTextColor(0, 0, 1, 1) +TextPropertiesManager.getGlobalPtr().setProperties('blue', blue) +white = TextProperties() +white.setTextColor(1, 1, 1, 1) +TextPropertiesManager.getGlobalPtr().setProperties('white', white) +black = TextProperties() +black.setTextColor(0, 0, 0, 1) +TextPropertiesManager.getGlobalPtr().setProperties('black', black) +grey = TextProperties() +grey.setTextColor(0.5, 0.5, 0.5, 1) +TextPropertiesManager.getGlobalPtr().setProperties('grey', grey) + +# New colors: +# Orange +amber = TextProperties() +amber.setTextColor(1, 0.75, 0, 1) +TextPropertiesManager.getGlobalPtr().setProperties('amber', amber) + +# Pink +amaranth = TextProperties() +amaranth.setTextColor(0.9, 0.17, 0.31, 1) +TextPropertiesManager.getGlobalPtr().setProperties('amaranth', amaranth) + +# Green +androidGreen = TextProperties() +androidGreen.setTextColor(0.64, 0.78, 0.22, 1) +TextPropertiesManager.getGlobalPtr().setProperties('androidGreen', androidGreen) + +# Turquoise-green +caribbeanGreen = TextProperties() +caribbeanGreen.setTextColor(0, 0.8, 0.6, 1) +TextPropertiesManager.getGlobalPtr().setProperties('caribbeanGreen', caribbeanGreen) + +# Blue +azure = TextProperties() +azure.setTextColor(0, 0.5, 1, 1) +TextPropertiesManager.getGlobalPtr().setProperties('azure', azure) + +# Cobalt-blue +cobalt = TextProperties() +cobalt.setTextColor(0, 0.28, 0.67, 1) +TextPropertiesManager.getGlobalPtr().setProperties('cobalt', cobalt) + +CRConnecting = 'Connecting...' +CRNoConnectTryAgain = 'Could not connect to %s:%s. Try again?' +CRNoConnectProxyNoPort = 'Could not connect to %s:%s.\n\nYou are communicating to the internet via a proxy, but your proxy does not permit connections on port %s.\n\nYou must open up this port, or disable your proxy, in order to play. If your proxy has been provided by your ISP, you must contact your ISP to request them to open up this port.' +CRNoDistrictsTryAgain = 'No Districts are available. Try again?' +CRRejectRemoveAvatar = 'The avatar was not able to be deleted, try again another time.' +CRLostConnection = 'Your internet connection to the servers has been unexpectedly broken.' +CRBootedReasons = {100: 'You have been disconnected because someone else just logged in using your account on another computer.', + 101: 'Please relaunch the game from the official launcher.', + 102: 'You are not authorized to use administrator privileges.', + 103: 'You were banned by a moderator.\n\nBehave next time!', + 105: 'Toontown Stride is now temporarily closed for maintenance. Everyone who was playing has been disconnected from the game.\n\nFor more information, please visit the Toontown Stride website.', + 124: 'Your installed files are out of date! Use the official launcher to download the newest version, or contact Toontown Stride Support if the problem persists.', + 153: 'The district you were playing on has been reset. Everyone who was playing on that district has been disconnected. However, you should be able to connect again and go right back into the game.', + 166: 'You were disconnected to prevent a district reset.'} +CRBootedReasonUnknownCode = 'An unexpected problem has occurred (error code %s). Your connection has been lost, but you should be able to connect again and go right back into the game.' +CRTryConnectAgain = '\n\nTry to connect again?' +CRToontownUnavailable = 'The server appears to be temporarily unavailable, still trying...' +CRToontownUnavailableCancel = lCancel +CRNameCongratulations = 'CONGRATULATIONS!!' +CRNameAccepted = 'Your name has been\napproved by the Toon Council.\n\nFrom this day forth\nyou will be named\n"%s"' +CRMaintenanceCountdownMessage = 'Attention Toons! Toontown Stride will be going down for maintenance in %d minutes.' +CRMaintenanceMessage = 'Attention Toons! Toontown Stride is now going down for maintenance.' +AfkForceAcknowledgeMessage = 'Your toon got sleepy and went to bed.' +CREnteringToontown = 'Entering...' +DialogSpecial = 'ooo' +DialogExclamation = '!' +DialogQuestion = '?' +DialogLength1 = 6 +DialogLength2 = 12 +DialogLength3 = 20 +GlobalSpeedChatName = 'SpeedChat' +SCMenuPromotion = 'PROMOTIONAL' +SCMenuEmotions = 'EMOTIONS' +SCMenuCustom = 'MY PHRASES' +SCMenuResistance = 'UNITE!' +SCMenuPets = 'PETS' +SCMenuPetTricks = 'TRICKS' +SCMenuCog = 'COG SPEAK' +SCMenuHello = 'HELLO' +SCMenuBye = 'GOODBYE' +SCMenuConvo = 'CHIT CHAT' +SCMenuEmoticons = 'EMOTICONS' +SCMenuResponse="REPLIES" +SCMenuGood = 'GOOD' +SCMenuBad = 'BAD' +SCMenuHappy = 'HAPPY' +SCMenuSad = 'SAD' +SCMenuFriendly = 'FRIENDLY' +SCMenuSorry = 'SORRY' +SCMenuBusy = "I'M BUSY..." +SCMenuStinky = 'STINKY' +SCMenuPlaces = 'PLACES' +SCMenuToontasks = 'TOONTASKS' +SCMenuBattle = 'BATTLE' +SCMenuBattleUse = 'YOU SHOULD USE...' +SCMenuBattleToonUp = 'TOON-UP' +SCMenuBattleTrap = 'TRAP' +SCMenuBattleLure = 'LURE' +SCMenuBattleSound = 'SOUND' +SCMenuBattleThrow = 'THROW' +SCMenuBattleSquirt = 'SQUIRT' +SCMenuBattleDrop = 'DROP' +SCMenuGagShop = 'TROLLEY' +SCMenuFactory = 'FACTORY' +SCMenuKartRacing = 'RACING' +SCMenuFactoryMeet = 'MEET' +SCMenuCFOBattle = 'C.F.O.' +SCMenuCFOBattleCranes = 'CRANES' +SCMenuCFOBattleGoons = 'GOONS' +SCMenuCJBattle = 'CHIEF JUSTICE' +SCMenuCEOBattle = 'C.E.O.' +SCMenuGolf = 'GOLF' +SCMenuWhiteList = 'WHITELIST' +SCMenuPlacesPlayground = 'PLAYGROUND' +SCMenuPlacesEstate = 'ESTATE' +SCMenuPlacesCogs = 'COGS' +SCMenuPlacesWait = 'WAIT' +SCMenuFriendlyYou = 'YOU...' +SCMenuFriendlyILike = 'I LIKE YOUR...' +SCMenuPlacesLetsGo = "LET'S GO..." +SCMenuToontasksMyTasks = 'MY TASKS' +SCMenuToontasksYouShouldChoose = 'I THINK YOU SHOULD...' +SCMenuToontasksINeedMore = 'I NEED MORE...' +SCMenuBattleGags = 'GAGS' +SCMenuBattleTaunts = 'TAUNTS' +SCMenuBattleStrategy = 'STRATEGY' +SCMenuBoardingGroup = 'BOARDING' +SCMenuParties = 'PARTIES' +SCMenuAprilToons = "APRIL TOONS'" +SCMenuCarol = 'CAROLING' +SCMenuSillyHoliday = 'SILLY METER' +SCMenuVictoryParties = 'VICTORY PARTIES' +SCMenuSellbotNerf = 'STORM SELLBOT' +SCMenuJellybeanJam = 'JELLYBEAN WEEK' +SCMenuHalloween = 'HALLOWEEN' +SCMenuWinter = 'WINTER' +SCMenuSellbotInvasion = 'SELLBOT INVASION' +SCMenuFieldOffice = 'FIELD OFFICES' +SCMenuIdesOfMarch = 'GREEN' +ScMenuBugs = 'Bugs' +FriendInviteeTooManyFriends = '%s would like to be your friend, but you already have too many friends on your list!' +FriendInviteeInvitation = '%s would like to be your friend.' +FriendInviteeOK = lOK +FriendInviteeNo = lNo +FriendOnline = 'has come online.' +FriendOffline = 'has gone offline.' +FriendInviterOK = lOK +FriendInviterCancel = lCancel +FriendInviterStopBeingFriends = 'Stop being friends' +FriendInviterConfirmRemove = 'Remove' +FriendInviterYes = lYes +FriendInviterNo = lNo +FriendInviterClickToon = 'Click on the toon you would like to make friends with.' +FriendInviterTooMany = 'You have too many friends on your list to add another one now. You will have to remove some friends if you want to make friends with %s.' +FriendInviterToonTooMany = 'You have too many toon friends on your list to add another one now. You will have to remove some toon friends if you want to make friends with %s.' +FriendInviterNotYet = 'Would you like to make friends with %s?' +FriendInviterCheckAvailability = 'Seeing if %s is available.' +FriendInviterNotAvailable = '%s is busy right now; try again later.' +FriendInviterWentAway = '%s went away.' +FriendInviterAlready = '%s is already your friend.' +FriendInviterAlreadyInvited = '%s has already been invited.' +FriendInviterAskingCog = 'Asking %s to be your friend.' +FriendInviterAskingPet = '%s jumps around, runs in circles and licks your face.' +FriendInviterAskingMyPet = '%s is already your BEST friend.' +FriendInviterEndFriendship = 'Are you sure you want to stop being friends with %s?' +FriendInviterFriendsNoMore = '%s is no longer your friend.' +FriendInviterSelf = "You are already 'friends' with yourself!" +FriendInviterIgnored = '%s is ignoring you.' +FriendInviterAsking = 'Asking %s to be your friend.' +FriendInviterFriendSaidYes = 'You are now friends with %s!' +FriendInviterFriendSaidNo = '%s said no, thank you.' +FriendInviterFriendSaidNoNewFriends = "%s isn't looking for new friends right now." +FriendInviterOtherTooMany = '%s has too many friends already!' +FriendInviterMaybe = '%s was unable to answer.' +FriendInviterDown = 'Cannot make friends now.' +AntiSpamInChat = '***Spamming***' +IgnoreConfirmOK = lOK +IgnoreConfirmCancel = lCancel +IgnoreConfirmYes = lYes +IgnoreConfirmNo = lNo +IgnoreConfirmNotYet = 'Would you like to Ignore %s?' +IgnoreConfirmAlready = 'You are already ignoring %s.' +IgnoreConfirmSelf = 'You cannot ignore yourself!' +IgnoreConfirmNewIgnore = 'You are ignoring %s.' +IgnoreConfirmEndIgnore = 'You are no longer ignoring %s.' +IgnoreConfirmRemoveIgnore = 'Stop ignoring %s?' +EmoteList = ['Wave', + 'Happy', + 'Sad', + 'Angry', + 'Sleepy', + 'Shrug', + 'Dance', + 'Think', + 'Bored', + 'Applause', + 'Cringe', + 'Confused', + 'Belly Flop', + 'Bow', + 'Banana Peel', + 'Resistance Salute', + 'Laugh', + lYes, + lNo, + lOK, + 'Surprise', + 'Cry', + 'Delighted', + 'Furious', + 'Laugh', + 'Rage'] +EmoteWhispers = ['%s waves.', + '%s is happy.', + '%s is sad.', + '%s is angry.', + '%s is sleepy.', + '%s shrugs.', + '%s dances.', + '%s thinks.', + '%s is bored.', + '%s applauds.', + '%s cringes.', + '%s is confused.', + '%s does a belly flop.', + '%s bows to you.', + '%s slips on a banana peel.', + '%s gives the resistance salute.', + '%s laughs.', + "%s says '" + lYes + "'.", + "%s says '" + lNo + "'.", + "%s says '" + lOK + "'.", + '%s is surprised.', + '%s is crying.', + '%s is delighted.', + '%s is furious.', + '%s is laughing.', + '%s is raging.'] +EmoteFuncDict = {'Wave': 0, + 'Happy': 1, + 'Sad': 2, + 'Angry': 3, + 'Sleepy': 4, + 'Shrug': 5, + 'Dance': 6, + 'Think': 7, + 'Bored': 8, + 'Applause': 9, + 'Cringe': 10, + 'Confused': 11, + 'Belly Flop': 12, + 'Bow': 13, + 'Banana Peel': 14, + 'Resistance Salute': 15, + 'Laugh': 16, + lYes: 17, + lNo: 18, + lOK: 19, + 'Surprise': 20, + 'Cry': 21, + 'Delighted': 22, + 'Furious': 23, + 'Laugh': 24, + 'Rage': 25} +SuitBrushOffs = {'f': ["I'm late for a meeting."], + 'p': ['Push off.'], + 'ym': ['Yes Man says NO.'], + None: ["It's my day off.", + "I believe you're in the wrong office.", + 'Have your people call my people.', + "You're in no position to meet with me.", + 'Talk to my assistant.', + "I'll pretend I don't see you Toon.", + "There's a restraining order coming your way."]} +SuitFaceoffTaunts = {'b': ['Do you have a donation for me?', + "I'm going to make you a sore loser.", + "I'm going to leave you high and dry.", + 'I\'m "A Positive" I\'m going to win.', + '"O" don\'t be so "Negative".', + "I'm surprised you found me, I'm very mobile.", + "I'm going to need to do a quick count on you.", + "You're soon going to need a cookie and some juice.", + "When I'm through you'll need to lie down.", + 'This will only hurt for a second.', + "I'm going to make you dizzy.", + "Good timing, I'm a pint low.", + "You'll B the opposite of A happy toon when I'm finished with you."], + 'm': ["You don't know who you're mingling with.", + 'Ever mingle with the likes of me?', + 'Good, it takes two to mingle.', + "Let's mingle.", + 'This looks like a good place to mingle.', + "Well, isn't this cozy?", + "You're mingling with defeat.", + "I'm going to mingle in your business.", + "Are you sure you're ready to mingle?"], + 'ms': ['Get ready for a shake down.', + 'You had better move out of the way.', + 'Move it or lose it.', + "I believe it's my move.", + 'This should shake you up.', + 'Prepare to be moved.', + "I'm ready to make my move.", + "Watch out toon, you're on shaky ground.", + 'This should be a moving moment.', + 'I feel moved to defeat you.', + 'Are you shaking yet?'], + 'hh': ["I'm way ahead of you.", + "You're headed for big trouble.", + "You'll wish this was all in your head.", + "Oh good, I've been hunting for you.", + "I'll have your head for this.", + 'Heads up!', + "Looks like you've got a head for trouble.", + 'Headed my way?', + 'A perfect trophy for my collection.', + 'You are going to have such a headache.', + "Don't lose your head over me."], + 'tbc': ["Watch out, I'm gouda getcha.", + 'You can call me Jack.', + 'Are you sure? I can be such a Muenster at times.', + 'Well finally, I was afraid you were stringing me along.', + "I'm going to cream you.", + "Don't you think I've aged well?", + "I'm going to make mozzarella outta ya.", + "I've been told I'm very strong.", + 'Careful, I know your expiration date.', + "Watch out, I'm a whiz at this game.", + 'Beating you will be a brieeze.'], + 'cr': ['RAID!', + "You don't fit in my corporation.", + 'Prepare to be raided.', + "Looks like you're primed for a take-over.", + 'That is not proper corporate attire.', + "You're looking rather vulnerable.", + 'Time to sign over your assets.', + "I'm on a toon removal crusade.", + 'You are defenseless against my ideas.', + "Relax, you'll find this is for the best."], + 'mh': ['Are you ready for my take?', + 'Lights, camera, action!', + "Let's start rolling.", + 'Today the role of defeated toon, will be played by - YOU!', + 'This scene will go on the cutting room floor.', + 'I already know my motivation for this scene.', + 'Are you ready for your final scene?', + "I'm ready to roll your end credits.", + 'I told you not to call me.', + "Let's get on with the show.", + "There's no business like it!", + "I hope you don't forget your lines."], + 'nc': ['Looks like your number is up.', + 'I hope you prefer extra crunchy.', + "Now you're really in a crunch.", + 'Is it time for crunch already?', + "Let's do crunch.", + 'Where would you like to have your crunch today?', + "You've given me something to crunch on.", + 'This will not be smooth.', + 'Go ahead, try and take a number.', + 'I could do with a nice crunch about now.'], + 'ls': ["It's time to collect on your loan.", + "You've been on borrowed time.", + 'Your loan is now due.', + 'Time to pay up.', + 'Well you asked for an advance and you got it.', + "You're going to pay for this.", + "It's pay back time.", + 'Can you lend me an ear?', + "Good thing you're here, I'm in a frenzy.", + 'Shall we have a quick bite?', + 'Let me take a bite at it.'], + 'mb': ['Time to bring in the big bags.', + 'I can bag this.', + 'Paper or plastic?', + 'Do you have your baggage claim?', + "Remember, money won't make you happy.", + 'Careful, I have some serious baggage.', + "You're about to have money trouble.", + 'Money will make your world go around.', + "I'm too rich for your blood.", + 'You can never have too much money!'], + 'rb': ["You've been robbed.", + "I'll rob you of this victory.", + "I'm a royal pain!", + 'Hope you can grin and baron.', + "You'll need to report this robbery.", + "Stick 'em up.", + "I'm a noble adversary.", + "I'm going to take everything you have.", + 'You could call this neighborhood robbery.', + 'You should know not to talk to strangers.'], + 'bs': ['Never turn your back on me.', + "You won't be coming back.", + 'Take that back or else!', + "I'm good at cutting costs.", + 'I have lots of back up.', + "There's no backing down now.", + "I'm the best and I can back that up.", + 'Whoa, back up there toon.', + 'Let me get your back.', + "You're going to have a stabbing headache soon.", + 'I have perfect puncture.', + "Don't worry toon, you can always trust me."], + 'bw': ["Don't brush me aside.", + 'You make my hair curl.', + 'I can make this permanent if you want.', + "It looks like you're going to have some split ends.", + "You can't handle the truth.", + "I think it's your turn to be dyed.", + "I'm so glad you're on time for your cut.", + "You're in big trouble.", + "I'm going to wig out on you.", + "I'm a big deal little toon."], + 'le': ["Careful, my legal isn't very tender.", + 'I soar, then I score.', + "I'm bringing down the law on you.", + 'You should know, I have some killer instincts.', + "I'm going to give you legal nightmares.", + "You won't win this battle.", + 'This is so much fun it should be illegal.', + "Legally, you're too small to fight me.", + 'There is no limit to my talons.', + "I call this a citizen's arrest.", + "I've got the court under my wing."], + 'sd': ["You'll never know when I'll stop.", + 'Let me take you for a spin.', + 'The doctor will see you now.', + "I'm going to put you into a spin.", + 'You look like you need a doctor.', + 'The doctor is in, the Toon is out.', + "You won't like my spin on this.", + 'You are going to spin out of control.', + 'Care to take a few turns with me?', + 'I have my own special spin on the subject.'], + 'f': ["I'm gonna tell the boss about you!", + "I may be just a Flunky - But I'm real spunky.", + "I'm using you to step up the corporate ladder.", + "You're not going to like the way I work.", + 'The boss is counting on me to stop you.', + "You're going to look good on my resume.", + "You'll have to go through me first.", + "Let's see how you rate my job performance.", + 'I excel at Toon disposal.', + "You're never going to meet my boss.", + "I'm sending you back to the Playground."], + 'p': ["I'm gonna rub you out!", + "Hey, you can't push me around.", + "I'm No.2!", + "I'm going to scratch you out.", + "I'll have to make my point more clear.", + 'Let me get right to the point.', + "Let's hurry, I bore easily.", + 'I hate it when things get dull.', + 'So you want to push your luck?', + 'Did you pencil me in?', + 'Careful, I may leave a mark.'], + 'ym': ["I'm positive you're not going to like this.", + "I don't know the meaning of no.", + 'Want to meet? I say yes, anytime.', + 'You need some positive enforcement.', + "I'm going to make a positive impression.", + "I haven't been wrong yet.", + "Yes, I'm ready for you.", + 'Are you positive you want to do this?', + "I'll be sure to end this on a positive note.", + "I'm confirming our meeting time.", + "I won't take no for an answer."], + 'mm': ["I'm going to get into your business!", + 'Sometimes big hurts come in small packages.', + 'No job is too small for me.', + "I want the job done right, so I'll do it myself.", + 'You need someone to manage your assets.', + 'Oh good, a project.', + "Well, you've managed to find me.", + 'I think you need some managing.', + "I'll take care of you in no time.", + "I'm watching every move you make.", + 'Are you sure you want to do this?', + "We're going to do this my way.", + "I'm going to be breathing down your neck.", + 'I can be very intimidating.', + "I've been searching for my growth spurt."], + 'ds': ["You're going down!", + 'Your options are shrinking.', + 'Expect diminishing returns.', + "You've just become expendable.", + "Don't ask me to lay off.", + 'I might have to make a few cutbacks.', + 'Things are looking down for you.', + 'Why do you look so down?'], + 'cc': ['Surprised to hear from me?', + 'You rang?', + 'Are you ready to accept my charges?', + 'This caller always collects.', + "I'm one smooth operator.", + "Hold the phone -- I'm here.", + 'Have you been waiting for my call?', + "I was hoping you'd answer my call.", + "I'm going to cause a ringing sensation.", + 'I always make my calls direct.', + 'Boy, did you get your wires crossed.', + 'This call is going to cost you.', + "You've got big trouble on the line."], + 'tm': ['I plan on making this inconvenient for you.', + 'Can I interest you in an insurance plan?', + 'You should have missed my call.', + "You won't be able to get rid of me now.", + 'This a bad time? Good.', + 'I was planning on running into you.', + 'I will be reversing the charges for this call.', + 'I have some costly items for you today.', + 'Too bad for you - I make house calls.', + "I'm prepared to close this deal quickly.", + "I'm going to use up a lot of your resources."], + 'nd': ['In my opinion, your name is mud.', + "I hope you don't mind if I drop your name.", + "Haven't we met before?", + "Let's hurry, I'm having lunch with 'Mr. Hollywood.'", + "Have I mentioned I know 'The Mingler?'", + "You'll never forget me.", + 'I know all the right people to bring you down.', + "I think I'll just drop in.", + "I'm in the mood to drop some Toons.", + "You name it, I've dropped it."], + 'gh': ['Put it there, Toon.', + "Let's shake on it.", + "I'm going to enjoy this.", + "You'll notice I have a very firm grip.", + "Let's seal the deal.", + "Let's get right to the business at hand.", + "Off handedly I'd say, you're in trouble.", + "You'll find I'm a handful.", + 'I can be quite handy.', + "I'm a very hands-on kinda guy.", + 'Would you like some hand-me-downs?', + 'Let me show you some of my handiwork.', + 'I think the handwriting is on the wall.', + "I'll gladly handle your gags for you."], + 'sc': ['I will make short work of you.', + "You're about to have money trouble.", + "You're about to be overcharged.", + 'This will be a short-term assignment.', + "I'll be done with you in short order.", + "You'll soon experience a shortfall.", + "Let's make this a short stop.", + "I think you've come up short.", + 'I have a short temper for Toons.', + "I'll be with you shortly.", + "You're about to be shorted.", + "Well, aren't you a little short on your changes?"], + 'pp': ['This is going to sting a little.', + "I'm going to give you a pinch for luck.", + "You don't want to press your luck with me.", + "I'm going to put a crimp in your smile.", + 'Perfect, I have an opening for you.', + 'Let me add my two cents.', + "I've been asked to pinch-hit.", + "I'll prove you're not dreaming.", + 'Heads you lose, tails I win.', + 'A penny for your gags.'], + 'tw': ['Things are about to get very tight.', + "That's Mr. Tightwad to you.", + "I'm going to cut off your funding.", + 'Is this the best deal you can offer?', + "Let's get going - time is money.", + "You'll find I'm very tightfisted.", + "You're in a tight spot.", + 'Prepare to walk a tight rope.', + 'I hope you can afford this.', + "I'm going to make this a tight squeeze.", + "I'm going to make a big dent in your budget."], + 'bc': ['I enjoy subtracting Toons.', + 'You can count on me to make you pay.', + 'Bean there, done that.', + 'I can hurt you where it counts.', + 'I make every bean count.', + 'Your expense report is overdue.', + 'Time for an audit.', + "Let's step into my office.", + 'Where have you bean?', + "I've bean waiting for you.", + "I'm going to bean you."], + 'bf': ["Looks like you've hit rock bottom.", + "I'm ready to feast.", + "I'm a sucker for Toons.", + 'Oh goody, lunch time.', + 'Perfect timing, I need a quick bite.', + "I'd like some feedback on my performance.", + "Let's talk about the bottom line.", + "You'll find my talents are bottomless.", + 'Good, I need a little pick-me-up.', + "I'd love to have you for lunch."], + 'tf': ["It's time to face-off!", + 'You had better face up to defeat.', + 'Prepare to face your worst nightmare!', + "Face it, I'm better than you.", + 'Two heads are better than one.', + 'It takes two to tango, you wanna tango?', + "You're in for two times the trouble.", + 'Which face would you like to defeat you?', + "I'm 'two' much for you.", + "You don't know who you're facing.", + 'Are you ready to face your doom?', + "My eyes are on you."], + 'dt': ["I'm gonna give you double the trouble.", + 'See if you can stop my double cross.', + 'I serve a mean double-\x04DECKER.', + "It's time to do some double-dealing.", + 'I plan to do some double DIPPING.', + "You're not going to like my double play.", + 'You may want to double think this.', + 'Get ready for a double TAKE.', + 'You may want to double up against me.', + 'Doubles anyone??'], + 'ac': ["I'm going to chase you out of town!", + 'Do you hear a siren?', + "I'm going to enjoy this.", + 'I love the thrill of the chase.', + 'Let me give you the run down.', + 'Do you have insurance?', + 'I hope you brought a stretcher with you.', + 'I doubt you can keep up with me.', + "It's all uphill from here.", + "You're going to need some urgent care soon.", + 'This is no laughing matter.', + "I'm going to give you the business."]} +SpeedChatStaticTextCommon = {1: lYes, + 2: lNo, + 3: lOK, + 4: 'SPEEDCHAT PLUS'} +SpeedChatStaticTextToontown = {100: 'Hi!', + 101: 'Hello!', + 102: 'Hi there!', + 103: 'Hey!', + 104: 'Howdy!', + 105: 'Hi everybody!', + 106: 'Welcome to Toontown!', + 107: "What's up?", + 108: 'How are you doing?', + 109: 'Hello?', + 200: 'Bye!', + 201: 'Later!', + 202: 'See ya!', + 203: 'Have a nice day!', + 204: 'Have fun!', + 205: 'Good luck!', + 206: "I'll be right back.", + 207: 'I need to go.', + 208: "I'll be back later!", + 209: 'I only have a few minutes.', + 300: ':-)', + 301: 'Yay!', + 302: 'Hooray!', + 303: 'Cool!', + 304: 'Woo hoo!', + 305: 'Yeah!', + 306: 'Ha ha!', + 307: 'Hee hee!', + 308: 'Wow!', + 309: 'Great!', + 310: 'Whee!', + 311: 'Oh boy!', + 312: 'Whoopee!', + 313: 'Yippee!', + 314: 'Yee hah!', + 315: 'Toontastic!', + 400: ':-(', + 401: 'Oh no!', + 402: 'Uh oh!', + 403: 'Rats!', + 404: 'Drat!', + 405: 'Ouch!', + 406: 'Oof!', + 407: 'No!!!', + 408: 'Yikes!', + 409: 'Huh?', + 410: 'I need more Laff points.', + 500: 'Thanks!', + 501: 'No problem.', + 502: "You're welcome!", + 503: 'Any time!', + 504: 'No thank you.', + 505: 'Good teamwork!', + 506: 'That was fun!', + 507: 'Please be my friend!', + 508: "Let's work together!", + 509: 'You guys are great!', + 510: 'Are you new here?', + 511: 'Did you win?', + 512: 'I think this is too risky for you.', + 513: 'Would you like some help?', + 514: 'Can you help me?', + 515: 'Have you been here before?', + 600: 'You look nice.', + 601: 'You are awesome!', + 602: 'You rock!', + 603: 'You are a genius!', + 700: 'I like your name.', + 701: 'I like your look.', + 702: 'I like your shirt.', + 703: 'I like your skirt.', + 704: 'I like your shorts.', + 705: 'I like this game!', + 800: 'Sorry!', + 801: 'Oops!', + 802: "Sorry, I'm busy fighting Cogs!", + 803: "Sorry, I'm busy getting Jellybeans!", + 804: "Sorry, I'm busy completing a ToonTask!", + 805: 'Sorry, I had to leave unexpectedly.', + 806: 'Sorry, I was delayed.', + 807: "Sorry, I can't.", + 808: "I couldn't wait any longer.", + 809: "I can't understand you.", + 810: 'Use the %s.' % GlobalSpeedChatName, + 811: "Sorry, I'm busy fishing!", + 812: "Sorry, I'm in a building!", + 813: "Sorry, I'm helping a friend!", + 814: "Sorry, I'm busy kart racing!", + 815: "Sorry, I'm busy gardening!", + 816: "I can't get on the elevator now.", + 817: "Sorry, I'm busy golfing!", + 818: 'Sorry, my Friends List is full.', + 900: 'Hey!', + 901: 'Please go away!', + 902: 'Stop that!', + 903: "That wasn't nice!", + 904: "Don't be mean!", + 905: 'You stink!', + 906: 'Send a bug report.', + 907: "I'm stuck.", + 1000: "Let's go!", + 1001: 'Can you teleport to me?', + 1002: 'Shall we go?', + 1003: 'Where should we go?', + 1004: 'Which way?', + 1005: 'This way.', + 1006: 'Follow me.', + 1007: 'Wait for me!', + 1008: "Let's wait for my friend.", + 1009: "Let's find other Toons.", + 1010: 'Wait here.', + 1011: 'Wait a minute.', + 1012: 'Meet here.', + 1013: 'Can you come to my house?', + 1014: "Don't wait for me.", + 1015: 'Wait!', + 1016: 'Come check out my garden.', + 1017: "Let's catch the next one.", + 1100: "Let's go on the trolley!", + 1101: "Let's go back to the playground!", + 1102: "Let's go fight the %s!" % Cogs, + 1103: "Let's go take over a %s building!" % Cog, + 1104: "Let's go in the elevator!", + 1105: "Let's go to %s!" % lToontownCentral, + 1106: "Let's go to %s!" % lDonaldsDock, + 1107: "Let's go to %s!" % lMinniesMelodyland, + 1108: "Let's go to %s!" % lDaisyGardens, + 1109: "Let's go to %s!" % lTheBrrrgh, + 1110: "Let's go to %s!" % lDonaldsDreamland, + 1111: "Let's go to %s!" % lGoofySpeedway, + 1112: "Let's go to my house!", + 1113: "Let's go to your house!", + 1114: "Let's go to Sellbot HQ!", + 1115: "Let's go fight the VP!", + 1116: "Let's go in the Factory!", + 1117: "Let's go fishing!", + 1118: "Let's go fishing at my house!", + 1119: "Let's go to Cashbot HQ!", + 1120: "Let's go fight the CFO!", + 1121: "Let's go in the Mint!", + 1122: "Let's go to Lawbot HQ!", + 1123: "Let's go fight the Chief Justice!", + 1124: "Let's go in the District Attorney's Office!", + 1125: "Let's go to %s!" % lOutdoorZone, + 1126: "Let's go to %s!" % lGolfZone, + 1127: "Let's go to Bossbot HQ!", + 1128: "Let's go fight the CEO!", + 1129: "Let's go in the Cog Golf Courses!", + 1130: "Let's go take over a Field Office!", + 1200: 'What ToonTask are you working on?', + 1201: "Let's work on that.", + 1202: "This isn't what I'm looking for.", + 1203: "I'm going to look for that.", + 1204: "It isn't on this street.", + 1205: "I haven't found it yet.", + 1206: 'I need more Merits.', + 1207: 'I need more Sellbot Suit Parts.', + 1208: "This isn't what you need.", + 1209: 'I found what you need.', + 1210: 'I need more Cogbucks.', + 1211: 'I need more Jury Notices.', + 1212: 'I need more Stock Options.', + 1213: 'I need more Cashbot Suit Parts.', + 1214: 'I need more Lawbot Suit Parts.', + 1215: 'I need more Bossbot Suit Parts.', + 1299: 'I need to get a ToonTask.', + 1300: 'I think you should choose Toon-up.', + 1301: 'I think you should choose Sound.', + 1302: 'I think you should choose Drop.', + 1303: 'I think you should choose Trap.', + 1304: 'I think you should choose Lure.', + 1400: 'Hurry!', + 1401: 'Nice shot!', + 1402: 'Nice gag!', + 1403: 'Missed me!', + 1404: 'You did it!', + 1405: 'We did it!', + 1406: 'Bring it on!', + 1407: 'Piece of cake!', + 1408: 'That was easy!', + 1409: 'Run!', + 1410: 'Help!', + 1411: 'Phew!', + 1412: 'We are in trouble.', + 1413: 'I need more gags.', + 1414: 'I need a Toon-Up.', + 1415: 'You should pass.', + 1416: 'We can do this!', + 1500: "Let's use toon-up!", + 1501: "Let's use trap!", + 1502: "Let's use lure!", + 1503: "Let's use sound!", + 1504: "Let's use throw!", + 1505: "Let's use squirt!", + 1506: "Let's use drop!", + 1520: 'Rock and roll!', + 1521: "That's gotta hurt.", + 1522: 'Catch!', + 1523: 'Special delivery!', + 1524: 'Are you still here?', + 1525: "I'm SO scared!", + 1526: "That's going to leave a mark!", + 1550: "I'm going to use trap.", + 1551: "I'm going to use lure.", + 1552: "I'm going to use drop.", + 1553: 'You should use a different gag.', + 1554: "Let's all go for the same Cog.", + 1555: 'You should choose a different Cog.', + 1556: 'Go for the weakest Cog first.', + 1557: 'Go for the strongest Cog first.', + 1558: 'Save your powerful gags.', + 1559: "Don't use sound on lured Cogs.", + 1600: 'I have enough gags.', + 1601: 'I need more jellybeans.', + 1602: 'Me too.', + 1603: 'Hurry up!', + 1604: 'One more?', + 1605: 'Play again?', + 1606: "Let's play again.", + 1700: "Let's split up.", + 1701: "Let's stay together.", + 1702: "Let's battle the Cogs.", + 1703: 'Step on the switch.', + 1704: 'Go through the door.', + 1803: "I'm in the Front Entrance.", + 1804: "I'm in the Lobby.", + 1805: "I'm in the hallway outside the Lobby.", + 1806: "I'm in the hallway outside the Lobby.", + 1807: "I'm in the Gear Room.", + 1808: "I'm in the Boiler Room.", + 1809: "I'm on the East Catwalk.", + 1810: "I'm in the Paint Mixer.", + 1811: "I'm in the Paint Mixer Storage Room.", + 1812: "I'm on the West Silo Catwalk.", + 1813: "I'm in the Pipe Room.", + 1814: "I'm on the stairs to the Pipe Room.", + 1815: "I'm in the Duct Room.", + 1816: "I'm in the Side Entrance.", + 1817: "I'm in Stomper Alley.", + 1818: "I'm outside the Lava Room.", + 1819: "I'm in the Lava Room.", + 1820: "I'm in the Lava Storage Room.", + 1821: "I'm on the West Catwalk.", + 1822: "I'm in the Oil Room.", + 1823: "I'm on the Warehouse Lookout.", + 1824: "I'm in the Warehouse.", + 1825: "I'm outside the Paint Mixer.", + 1827: "I'm outside the Oil Room.", + 1830: "I'm in the East Silo Control Room.", + 1831: "I'm in the West Silo Control Room.", + 1832: "I'm in the Center Silo Control Room.", + 1833: "I'm at the East Silo.", + 1834: "I'm on the West Silo.", + 1835: "I'm on the Center Silo.", + 1836: "I'm on the West Silo.", + 1837: "I'm at the East Silo.", + 1838: "I'm on the East Silo Catwalk.", + 1840: "I'm on top of the West Silo.", + 1841: "I'm on top of the East Silo.", + 1860: "I'm on the West Silo Elevator.", + 1861: "I'm on the East Silo Elevator.", + 1903: "Let's meet in the Front Entrance.", + 1904: "Let's meet in the Lobby.", + 1905: "Let's meet in the hallway outside the Lobby.", + 1906: "Let's meet in the hallway outside the Lobby.", + 1907: "Let's meet in the Gear Room.", + 1908: "Let's meet in the Boiler Room.", + 1909: "Let's meet on the East Catwalk.", + 1910: "Let's meet in the Paint Mixer.", + 1911: "Let's meet in the Paint Mixer Storage Room.", + 1912: "Let's meet on the West Silo Catwalk.", + 1913: "Let's meet in the Pipe Room.", + 1914: "Let's meet on the stairs to the Pipe Room.", + 1915: "Let's meet in the Duct Room.", + 1916: "Let's meet in the Side Entrance.", + 1917: "Let's meet in Stomper Alley.", + 1918: "Let's meet outside the Lava Room.", + 1919: "Let's meet in the Lava Room.", + 1920: "Let's meet in the Lava Storage Room.", + 1921: "Let's meet on the West Catwalk.", + 1922: "Let's meet in the Oil Room.", + 1923: "Let's meet on the Warehouse Lookout.", + 1924: "Let's meet in the Warehouse.", + 1925: "Let's meet outside the Paint Mixer.", + 1927: "Let's meet outside the Oil Room.", + 1930: "Let's meet in the East Silo Control Room.", + 1931: "Let's meet in the West Silo Control Room.", + 1932: "Let's meet in the Center Silo Control Room.", + 1933: "Let's meet at the East Silo.", + 1934: "Let's meet on the West Silo.", + 1935: "Let's meet on the Center Silo.", + 1936: "Let's meet on the West Silo.", + 1937: "Let's meet at the East Silo.", + 1938: "Let's meet on the East Silo Catwalk.", + 1940: "Let's meet on top of the West Silo.", + 1941: "Let's meet on top of the East Silo.", + 1960: "Let's meet on the West Silo Elevator.", + 1961: "Let's meet on the East Silo Elevator.", + 2000: 'Purple', + 2001: 'Blue', + 2002: 'Cyan', + 2003: 'Teal', + 2004: 'Green', + 2005: 'Yellow', + 2006: 'Orange', + 2007: 'Red', + 2008: 'Pink', + 2009: 'Brown', + 2010: 'Ocean Blue', + 2011: 'Peach', + 2012: 'Lavender', + 2013: 'Dark Red', + 2014: 'Dark Blue', + 2015: 'Light Green', + 2016: 'Light Red', + 2100: 'Please operate the crane.', + 2101: 'May I operate the crane?', + 2102: 'I need practice operating the crane.', + 2103: 'Pick up a disabled goon.', + 2104: 'Throw the goon at the CFO.', + 2105: 'Throw a safe now!', + 2106: "Don't throw a safe now!", + 2107: 'A safe will knock off his helmet.', + 2108: 'A safe will become his new helmet.', + 2109: "I can't reach any safes.", + 2110: "I can't reach any goons.", + 2120: 'Please disable the goons.', + 2121: 'I would rather disable goons.', + 2122: 'I need practice disabling goons.', + 2123: 'Please stay nearby.', + 2124: 'Keep moving.', + 2125: 'I need to keep moving.', + 2126: 'Look for someone who needs help.', + 2130: 'Please save the treasures.', + 2131: 'Take the treasures.', + 2132: 'I need treasures!', + 2133: 'Look out!', + 2200: 'You need to hit the scale.', + 2201: 'I will hit the scale.', + 2202: 'I need help with the scale!', + 2203: 'You need to stun the Cogs.', + 2204: 'I will stun the Cogs.', + 2205: 'I need help with the Cogs!', + 2206: 'I need more evidence!', + 2207: "I'm shooting for chairs in the top row.", + 2208: "I'm shooting for chairs in the bottom row.", + 2209: "Move out of the way! We can't hit the pan.", + 2210: "I'll do Toon-Ups for us.", + 2211: "I don't have any bonus weight.", + 2212: 'I have a bonus weight of 1.', + 2213: 'I have a bonus weight of 2.', + 2214: 'I have a bonus weight of 3.', + 2215: 'I have a bonus weight of 4.', + 2216: 'I have a bonus weight of 5.', + 2217: 'I have a bonus weight of 6.', + 2218: 'I have a bonus weight of 7.', + 2219: 'I have a bonus weight of 8.', + 2220: 'I have a bonus weight of 9.', + 2221: 'I have a bonus weight of 10.', + 2222: 'I have a bonus weight of 11.', + 2223: 'I have a bonus weight of 12.', + 2300: 'You feed the Cogs on the left.', + 2301: "I'll feed the Cogs on the left.", + 2302: 'You feed the Cogs on the right.', + 2303: "I'll feed the Cogs on the right.", + 2304: 'You feed the Cogs in the front.', + 2305: "I'll feed the Cogs in the front.", + 2306: 'You feed the Cogs in the back.', + 2307: "I'll feed the Cogs in the back.", + 2308: 'You use the seltzer bottle.', + 2309: "I'll use the seltzer bottle.", + 2310: 'You use the golf tee.', + 2311: "I'll use the golf tee.", + 2312: "I'll serve this table.", + 2313: 'Can you serve this table?', + 2314: 'Feed the same cog again.', + 2315: 'Hurry, your cog is hungry!', + 2316: 'Please save the snacks for sadder toons.', + 2317: 'Take the snacks before they fall.', + 3010: 'Anyone want to race?', + 3020: "Let's race!", + 3030: 'Want to race?', + 3040: "Let's show off our karts!", + 3050: "I don't have enough tickets.", + 3060: "Let's race again!", + 3061: 'Want to race again?', + 3150: 'I need to go to the Kart Shop.', + 3160: "Let's go to the Race Tracks!", + 3170: "Let's go to Pit Row to show off our karts!", + 3180: "I'm going to Pit Row to show off my kart!", + 3190: 'Meet me at the Race Tracks!', + 3110: 'Meet up near the Kart Shop!', + 3130: 'Where should we meet?', + 3200: 'Where do you want to race?', + 3201: "Let's pick a different race.", + 3210: "Let's do a practice race.", + 3211: "Let's do a battle race.", + 3220: 'I like the Screwball Stadium race!', + 3221: 'I like the Rustic Raceway race!', + 3222: 'I like the City Circuit race!', + 3223: 'I like the Corkscrew Coliseum race!', + 3224: 'I like the Airborne Acres race!', + 3225: 'I like the Blizzard Boulevard race!', + 3230: "Let's race in the Screwball Stadium!", + 3231: "Let's race on the Rustic Raceway!", + 3232: "Let's race on the City Circuit!", + 3233: "Let's race in the Corkscrew Coliseum!", + 3234: "Let's race on the Airborne Acres!", + 3235: "Let's race on the Blizzard Boulevard!", + 3600: 'Which track do you want to race on?', + 3601: 'Pick a track!', + 3602: 'Can we race on a different track?', + 3603: "Let's pick a different track!", + 3640: 'I want to race on the first track!', + 3641: 'I want to race on the second track!', + 3642: 'I want to race on the third track!', + 3643: 'I want to race on the fourth track!', + 3660: "I don't want to race on the first track!", + 3661: "I don't want to race on the second track!", + 3662: "I don't want to race on the third track!", + 3663: "I don't want to race on the fourth track!", + 3300: 'Wow! You are FAST!', + 3301: "You're too fast for me!", + 3310: 'Good race!', + 3320: 'I really like your kart!', + 3330: 'Sweet ride!', + 3340: 'Your kart is cool!', + 3350: 'Your kart is awesome!', + 3360: 'Your kart is totally sweet!', + 3400: 'Too scared to race me?', + 3410: 'See you at the finish line!', + 3430: "I'm as fast as lightning!", + 3450: "You'll never catch me!", + 3451: "You'll never beat me!", + 3452: 'No one can beat my time!', + 3453: 'Hurry up slow pokes!', + 3460: 'Give me another shot!', + 3461: 'You got lucky!', + 3462: 'Ooooh! That was a close one!', + 3470: 'Wow, I thought you had me beat!', + 4000: "Let's play minigolf!", + 4001: "Let's play again!", + 4002: 'Want to golf?', + 4100: "Let's play 'Walk In The Par.'", + 4101: "Let's play 'Hole Some Fun.'", + 4102: "Let's play 'The Hole Kit and Caboodle.'", + 4103: 'That course is too easy.', + 4104: 'That course is too hard.', + 4105: 'That course is just right.', + 4200: 'Try standing more to the left.', + 4201: 'Try standing more to the right.', + 4202: 'Try standing right in the middle.', + 4203: 'Try hitting it harder.', + 4204: 'Try hitting it softer.', + 4205: 'Try aiming more to the left.', + 4206: 'Try aiming more to the right.', + 4207: 'Try aiming right down the middle.', + 4300: 'So close!', + 4301: 'What a great shot!', + 4302: 'That was a lucky shot.', + 4303: "I'll take a mulligan...", + 4304: "That's a gimme.", + 4305: 'Fore!', + 4306: 'Shhhh!', + 4307: 'Good game!', + 5000: "Let's form a Boarding Group.", + 5001: 'Join my Boarding Group.', + 5002: 'Can you invite me to your Boarding Group?', + 5003: "I'm already in a Boarding Group.", + 5004: 'Leave your Boarding Group.', + 5005: 'We are boarding now.', + 5006: 'Where are we going?', + 5007: 'Are we ready?', + 5008: "Let's Go!", + 5009: "Don't leave this area or you will leave the Boarding Group.", + 5100: "Let's go to the Front Three.", + 5101: "Let's go to the Middle Six.", + 5102: "Let's go to the Back Nine.", + 5103: "Let's go to the C.E.O. Battle.", + 5104: "Let's go to the Senior V.P Battle.", + 5105: "Let's go to the Front Entrance.", + 5106: "Let's go to the Side Entrance.", + 5107: "Let's go to the Coin Mint.", + 5108: "Let's go to the Dollar Mint.", + 5109: "Let's go to the Bullion Mint.", + 5110: "Let's go to the C.F.O. Battle.", + 5111: "Let's go to the Chief Justice Battle.", + 5112: "Let's go to the Lawbot A Office.", + 5113: "Let's go to the Lawbot B Office.", + 5114: "Let's go to the Lawbot C Office.", + 5115: "Let's go to the Lawbot D Office.", + 5200: "We're going to the Front Three.", + 5201: "We're going to the Middle Six.", + 5202: "We're going to the Back Nine.", + 5203: "We're going to the C.E.O. Battle.", + 5204: "We're going to the Senior V.P Battle.", + 5205: "We're going to the Front Entrance.", + 5206: "We're going to the Side Entrance.", + 5207: "We're going to the Coin Mint.", + 5208: "We're going to the Dollar Mint.", + 5209: "We're going to the Bullion Mint.", + 5210: "We're going to the C.F.O. Battle.", + 5211: "We're going to the Chief Justice Battle.", + 5212: "We're going to the Lawbot A Office.", + 5213: "We're going to the Lawbot B Office.", + 5214: "We're going to the Lawbot C Office.", + 5215: "We're going to the Lawbot D Office.", + 5300: "Let's go to a party.", + 5301: 'See you at the party!', + 5302: 'My party has started!', + 5303: 'Come to my party!', + 5304: 'Welcome to my party!', + 5305: 'This party rules!', + 5306: 'Your party is fun!', + 5307: "It's party time!", + 5308: 'Time is running out!', + 5309: 'No cogs allowed!', + 5310: 'I like this song!', + 5311: 'This music is great!', + 5312: 'Cannons are a blast!', + 5313: 'Watch me jump!', + 5314: 'Trampolines are fun!', + 5315: "Let's play Catch!", + 5316: "Let's dance!", + 5317: 'To the dance floor!', + 5318: "Let's play Tug of War!", + 5319: 'Start the fireworks!', + 5320: 'These fireworks are beautiful!', + 5321: 'Nice decorations.', + 5322: 'I wish I could eat this cake!', + 10000: 'The choice is yours!', + 10001: 'Who are you voting for?', + 10002: "I'm pickin' Chicken!", + 10003: 'Trick or Treat!', + 10004: 'Go bananas! Vote Monkey!', + 10005: 'Be a honey! Vote Bear!', + 10006: 'Think big! Vote Pig!', + 10007: "Vote Goat - and that's all she wrote!", + 20000: SuitBrushOffs[None][0], + 20001: SuitBrushOffs[None][1], + 20002: SuitBrushOffs[None][2], + 20003: SuitBrushOffs[None][3], + 20004: SuitBrushOffs[None][4], + 20005: SuitFaceoffTaunts['bf'][0], + 20006: SuitFaceoffTaunts['bf'][1], + 20007: SuitFaceoffTaunts['bf'][2], + 20008: SuitFaceoffTaunts['bf'][3], + 20009: SuitFaceoffTaunts['bf'][4], + 20010: SuitFaceoffTaunts['bf'][5], + 20011: SuitFaceoffTaunts['bf'][6], + 20012: SuitFaceoffTaunts['bf'][7], + 20013: SuitFaceoffTaunts['bf'][8], + 20014: SuitFaceoffTaunts['bf'][9], + 20015: SuitFaceoffTaunts['nc'][0], + 20016: SuitFaceoffTaunts['nc'][1], + 20017: SuitFaceoffTaunts['nc'][2], + 20018: SuitFaceoffTaunts['nc'][3], + 20019: SuitFaceoffTaunts['nc'][4], + 20020: SuitFaceoffTaunts['nc'][5], + 20021: SuitFaceoffTaunts['nc'][6], + 20022: SuitFaceoffTaunts['nc'][7], + 20023: SuitFaceoffTaunts['nc'][8], + 20024: SuitFaceoffTaunts['nc'][9], + 20025: SuitFaceoffTaunts['ym'][0], + 20026: SuitFaceoffTaunts['ym'][1], + 20027: SuitFaceoffTaunts['ym'][2], + 20028: SuitFaceoffTaunts['ym'][3], + 20029: SuitFaceoffTaunts['ym'][4], + 20030: SuitFaceoffTaunts['ym'][5], + 20031: SuitFaceoffTaunts['ym'][6], + 20032: SuitFaceoffTaunts['ym'][7], + 20033: SuitFaceoffTaunts['ym'][8], + 20034: SuitFaceoffTaunts['ym'][9], + 20035: SuitFaceoffTaunts['ym'][10], + 20036: SuitFaceoffTaunts['ms'][0], + 20037: SuitFaceoffTaunts['ms'][1], + 20038: SuitFaceoffTaunts['ms'][2], + 20039: SuitFaceoffTaunts['ms'][3], + 20040: SuitFaceoffTaunts['ms'][4], + 20041: SuitFaceoffTaunts['ms'][5], + 20042: SuitFaceoffTaunts['ms'][6], + 20043: SuitFaceoffTaunts['ms'][7], + 20044: SuitFaceoffTaunts['ms'][8], + 20045: SuitFaceoffTaunts['ms'][9], + 20046: SuitFaceoffTaunts['ms'][10], + 20047: SuitFaceoffTaunts['bc'][0], + 20048: SuitFaceoffTaunts['bc'][1], + 20049: SuitFaceoffTaunts['bc'][2], + 20050: SuitFaceoffTaunts['bc'][3], + 20051: SuitFaceoffTaunts['bc'][4], + 20052: SuitFaceoffTaunts['bc'][5], + 20053: SuitFaceoffTaunts['bc'][6], + 20054: SuitFaceoffTaunts['bc'][7], + 20055: SuitFaceoffTaunts['bc'][8], + 20056: SuitFaceoffTaunts['bc'][9], + 20057: SuitFaceoffTaunts['bc'][10], + 20058: SuitFaceoffTaunts['cc'][0], + 20059: SuitFaceoffTaunts['cc'][1], + 20060: SuitFaceoffTaunts['cc'][2], + 20061: SuitFaceoffTaunts['cc'][3], + 20062: SuitFaceoffTaunts['cc'][4], + 20063: SuitFaceoffTaunts['cc'][5], + 20064: SuitFaceoffTaunts['cc'][6], + 20065: SuitFaceoffTaunts['cc'][7], + 20066: SuitFaceoffTaunts['cc'][8], + 20067: SuitFaceoffTaunts['cc'][9], + 20068: SuitFaceoffTaunts['cc'][10], + 20069: SuitFaceoffTaunts['cc'][11], + 20070: SuitFaceoffTaunts['cc'][12], + 20071: SuitFaceoffTaunts['nd'][0], + 20072: SuitFaceoffTaunts['nd'][1], + 20073: SuitFaceoffTaunts['nd'][2], + 20074: SuitFaceoffTaunts['nd'][3], + 20075: SuitFaceoffTaunts['nd'][4], + 20076: SuitFaceoffTaunts['nd'][5], + 20077: SuitFaceoffTaunts['nd'][6], + 20078: SuitFaceoffTaunts['nd'][7], + 20079: SuitFaceoffTaunts['nd'][8], + 20080: SuitFaceoffTaunts['nd'][9], + 20081: SuitFaceoffTaunts['ac'][0], + 20082: SuitFaceoffTaunts['ac'][1], + 20083: SuitFaceoffTaunts['ac'][2], + 20084: SuitFaceoffTaunts['ac'][3], + 20085: SuitFaceoffTaunts['ac'][4], + 20086: SuitFaceoffTaunts['ac'][5], + 20087: SuitFaceoffTaunts['ac'][6], + 20088: SuitFaceoffTaunts['ac'][7], + 20089: SuitFaceoffTaunts['ac'][8], + 20090: SuitFaceoffTaunts['ac'][9], + 20091: SuitFaceoffTaunts['ac'][10], + 20092: SuitFaceoffTaunts['ac'][11], + 20093: SuitFaceoffTaunts['tf'][0], + 20094: SuitFaceoffTaunts['tf'][1], + 20095: SuitFaceoffTaunts['tf'][2], + 20096: SuitFaceoffTaunts['tf'][3], + 20097: SuitFaceoffTaunts['tf'][4], + 20098: SuitFaceoffTaunts['tf'][5], + 20099: SuitFaceoffTaunts['tf'][6], + 20100: SuitFaceoffTaunts['tf'][7], + 20101: SuitFaceoffTaunts['tf'][8], + 20102: SuitFaceoffTaunts['tf'][9], + 20103: SuitFaceoffTaunts['tf'][10], + 20104: SuitFaceoffTaunts['hh'][0], + 20105: SuitFaceoffTaunts['hh'][1], + 20106: SuitFaceoffTaunts['hh'][2], + 20107: SuitFaceoffTaunts['hh'][3], + 20108: SuitFaceoffTaunts['hh'][4], + 20109: SuitFaceoffTaunts['hh'][5], + 20110: SuitFaceoffTaunts['hh'][6], + 20111: SuitFaceoffTaunts['hh'][7], + 20112: SuitFaceoffTaunts['hh'][8], + 20113: SuitFaceoffTaunts['hh'][9], + 20114: SuitFaceoffTaunts['hh'][10], + 20115: SuitFaceoffTaunts['le'][0], + 20116: SuitFaceoffTaunts['le'][1], + 20117: SuitFaceoffTaunts['le'][2], + 20118: SuitFaceoffTaunts['le'][3], + 20119: SuitFaceoffTaunts['le'][4], + 20120: SuitFaceoffTaunts['le'][5], + 20121: SuitFaceoffTaunts['le'][6], + 20122: SuitFaceoffTaunts['le'][7], + 20123: SuitFaceoffTaunts['le'][8], + 20124: SuitFaceoffTaunts['le'][9], + 20125: SuitFaceoffTaunts['bs'][0], + 20126: SuitFaceoffTaunts['bs'][1], + 20127: SuitFaceoffTaunts['bs'][2], + 20128: SuitFaceoffTaunts['bs'][3], + 20129: SuitFaceoffTaunts['bs'][4], + 20130: SuitFaceoffTaunts['bs'][5], + 20131: SuitFaceoffTaunts['bs'][6], + 20132: SuitFaceoffTaunts['bs'][7], + 20133: SuitFaceoffTaunts['bs'][8], + 20134: SuitFaceoffTaunts['bs'][9], + 20135: SuitFaceoffTaunts['bs'][10], + 20136: SuitFaceoffTaunts['cr'][0], + 20137: SuitFaceoffTaunts['cr'][1], + 20138: SuitFaceoffTaunts['cr'][2], + 20139: SuitFaceoffTaunts['cr'][3], + 20140: SuitFaceoffTaunts['cr'][4], + 20141: SuitFaceoffTaunts['cr'][5], + 20142: SuitFaceoffTaunts['cr'][6], + 20143: SuitFaceoffTaunts['cr'][7], + 20144: SuitFaceoffTaunts['cr'][8], + 20145: SuitFaceoffTaunts['cr'][9], + 20146: SuitFaceoffTaunts['tbc'][0], + 20147: SuitFaceoffTaunts['tbc'][1], + 20148: SuitFaceoffTaunts['tbc'][2], + 20149: SuitFaceoffTaunts['tbc'][3], + 20150: SuitFaceoffTaunts['tbc'][4], + 20151: SuitFaceoffTaunts['tbc'][5], + 20152: SuitFaceoffTaunts['tbc'][6], + 20153: SuitFaceoffTaunts['tbc'][7], + 20154: SuitFaceoffTaunts['tbc'][8], + 20155: SuitFaceoffTaunts['tbc'][9], + 20156: SuitFaceoffTaunts['tbc'][10], + 20157: SuitFaceoffTaunts['ds'][0], + 20158: SuitFaceoffTaunts['ds'][1], + 20159: SuitFaceoffTaunts['ds'][2], + 20160: SuitFaceoffTaunts['ds'][3], + 20161: SuitFaceoffTaunts['ds'][4], + 20162: SuitFaceoffTaunts['ds'][5], + 20163: SuitFaceoffTaunts['ds'][6], + 20164: SuitFaceoffTaunts['ds'][7], + 20165: SuitFaceoffTaunts['gh'][0], + 20166: SuitFaceoffTaunts['gh'][1], + 20167: SuitFaceoffTaunts['gh'][2], + 20168: SuitFaceoffTaunts['gh'][3], + 20169: SuitFaceoffTaunts['gh'][4], + 20170: SuitFaceoffTaunts['gh'][5], + 20171: SuitFaceoffTaunts['gh'][6], + 20172: SuitFaceoffTaunts['gh'][7], + 20173: SuitFaceoffTaunts['gh'][8], + 20174: SuitFaceoffTaunts['gh'][9], + 20175: SuitFaceoffTaunts['gh'][10], + 20176: SuitFaceoffTaunts['gh'][11], + 20177: SuitFaceoffTaunts['gh'][12], + 20178: SuitFaceoffTaunts['pp'][0], + 20179: SuitFaceoffTaunts['pp'][1], + 20180: SuitFaceoffTaunts['pp'][2], + 20181: SuitFaceoffTaunts['pp'][3], + 20182: SuitFaceoffTaunts['pp'][4], + 20183: SuitFaceoffTaunts['pp'][5], + 20184: SuitFaceoffTaunts['pp'][6], + 20185: SuitFaceoffTaunts['pp'][7], + 20186: SuitFaceoffTaunts['pp'][8], + 20187: SuitFaceoffTaunts['pp'][9], + 20188: SuitFaceoffTaunts['b'][0], + 20189: SuitFaceoffTaunts['b'][1], + 20190: SuitFaceoffTaunts['b'][2], + 20191: SuitFaceoffTaunts['b'][3], + 20192: SuitFaceoffTaunts['b'][4], + 20193: SuitFaceoffTaunts['b'][5], + 20194: SuitFaceoffTaunts['b'][6], + 20195: SuitFaceoffTaunts['b'][7], + 20196: SuitFaceoffTaunts['b'][8], + 20197: SuitFaceoffTaunts['b'][9], + 20198: SuitFaceoffTaunts['b'][10], + 20199: SuitFaceoffTaunts['b'][11], + 20200: SuitFaceoffTaunts['f'][0], + 20201: SuitFaceoffTaunts['f'][1], + 20202: SuitFaceoffTaunts['f'][2], + 20203: SuitFaceoffTaunts['f'][3], + 20204: SuitFaceoffTaunts['f'][4], + 20205: SuitFaceoffTaunts['f'][5], + 20206: SuitFaceoffTaunts['f'][6], + 20207: SuitFaceoffTaunts['f'][7], + 20208: SuitFaceoffTaunts['f'][8], + 20209: SuitFaceoffTaunts['f'][9], + 20210: SuitFaceoffTaunts['f'][10], + 20211: SuitFaceoffTaunts['mm'][0], + 20212: SuitFaceoffTaunts['mm'][1], + 20213: SuitFaceoffTaunts['mm'][2], + 20214: SuitFaceoffTaunts['mm'][3], + 20215: SuitFaceoffTaunts['mm'][4], + 20216: SuitFaceoffTaunts['mm'][5], + 20217: SuitFaceoffTaunts['mm'][6], + 20218: SuitFaceoffTaunts['mm'][7], + 20219: SuitFaceoffTaunts['mm'][8], + 20220: SuitFaceoffTaunts['mm'][9], + 20221: SuitFaceoffTaunts['mm'][10], + 20222: SuitFaceoffTaunts['mm'][11], + 20223: SuitFaceoffTaunts['mm'][12], + 20224: SuitFaceoffTaunts['mm'][13], + 20225: SuitFaceoffTaunts['tw'][0], + 20226: SuitFaceoffTaunts['tw'][1], + 20227: SuitFaceoffTaunts['tw'][2], + 20228: SuitFaceoffTaunts['tw'][3], + 20229: SuitFaceoffTaunts['tw'][4], + 20230: SuitFaceoffTaunts['tw'][5], + 20231: SuitFaceoffTaunts['tw'][6], + 20232: SuitFaceoffTaunts['tw'][7], + 20233: SuitFaceoffTaunts['tw'][8], + 20234: SuitFaceoffTaunts['tw'][9], + 20235: SuitFaceoffTaunts['tw'][10], + 20236: SuitFaceoffTaunts['mb'][0], + 20237: SuitFaceoffTaunts['mb'][1], + 20238: SuitFaceoffTaunts['mb'][2], + 20239: SuitFaceoffTaunts['mb'][3], + 20240: SuitFaceoffTaunts['mb'][4], + 20241: SuitFaceoffTaunts['mb'][5], + 20242: SuitFaceoffTaunts['mb'][6], + 20243: SuitFaceoffTaunts['mb'][7], + 20244: SuitFaceoffTaunts['mb'][8], + 20245: SuitFaceoffTaunts['mb'][9], + 20246: SuitFaceoffTaunts['m'][0], + 20247: SuitFaceoffTaunts['m'][1], + 20248: SuitFaceoffTaunts['m'][2], + 20249: SuitFaceoffTaunts['m'][3], + 20250: SuitFaceoffTaunts['m'][4], + 20251: SuitFaceoffTaunts['m'][5], + 20252: SuitFaceoffTaunts['m'][6], + 20253: SuitFaceoffTaunts['m'][7], + 20254: SuitFaceoffTaunts['m'][8], + 20255: SuitFaceoffTaunts['mh'][0], + 20256: SuitFaceoffTaunts['mh'][1], + 20257: SuitFaceoffTaunts['mh'][2], + 20258: SuitFaceoffTaunts['mh'][3], + 20259: SuitFaceoffTaunts['mh'][4], + 20260: SuitFaceoffTaunts['mh'][5], + 20261: SuitFaceoffTaunts['mh'][6], + 20262: SuitFaceoffTaunts['mh'][7], + 20263: SuitFaceoffTaunts['mh'][8], + 20264: SuitFaceoffTaunts['mh'][9], + 20265: SuitFaceoffTaunts['mh'][10], + 20266: SuitFaceoffTaunts['mh'][11], + 20267: SuitFaceoffTaunts['dt'][0], + 20268: SuitFaceoffTaunts['dt'][1], + 20269: SuitFaceoffTaunts['dt'][2], + 20270: SuitFaceoffTaunts['dt'][3], + 20271: SuitFaceoffTaunts['dt'][4], + 20272: SuitFaceoffTaunts['dt'][5], + 20273: SuitFaceoffTaunts['dt'][6], + 20274: SuitFaceoffTaunts['dt'][7], + 20275: SuitFaceoffTaunts['dt'][8], + 20276: SuitFaceoffTaunts['dt'][9], + 20277: SuitFaceoffTaunts['p'][0], + 20278: SuitFaceoffTaunts['p'][1], + 20279: SuitFaceoffTaunts['p'][2], + 20280: SuitFaceoffTaunts['p'][3], + 20281: SuitFaceoffTaunts['p'][4], + 20282: SuitFaceoffTaunts['p'][5], + 20283: SuitFaceoffTaunts['p'][6], + 20284: SuitFaceoffTaunts['p'][7], + 20285: SuitFaceoffTaunts['p'][8], + 20286: SuitFaceoffTaunts['p'][9], + 20287: SuitFaceoffTaunts['p'][10], + 20288: SuitFaceoffTaunts['tm'][0], + 20289: SuitFaceoffTaunts['tm'][1], + 20290: SuitFaceoffTaunts['tm'][2], + 20291: SuitFaceoffTaunts['tm'][3], + 20292: SuitFaceoffTaunts['tm'][4], + 20293: SuitFaceoffTaunts['tm'][5], + 20294: SuitFaceoffTaunts['tm'][6], + 20295: SuitFaceoffTaunts['tm'][7], + 20296: SuitFaceoffTaunts['tm'][8], + 20297: SuitFaceoffTaunts['tm'][9], + 20298: SuitFaceoffTaunts['tm'][10], + 20299: SuitFaceoffTaunts['bw'][0], + 20300: SuitFaceoffTaunts['bw'][1], + 20301: SuitFaceoffTaunts['bw'][2], + 20302: SuitFaceoffTaunts['bw'][3], + 20303: SuitFaceoffTaunts['bw'][4], + 20304: SuitFaceoffTaunts['bw'][5], + 20305: SuitFaceoffTaunts['bw'][6], + 20306: SuitFaceoffTaunts['bw'][7], + 20307: SuitFaceoffTaunts['bw'][8], + 20308: SuitFaceoffTaunts['bw'][9], + 20309: SuitFaceoffTaunts['ls'][0], + 20310: SuitFaceoffTaunts['ls'][1], + 20311: SuitFaceoffTaunts['ls'][2], + 20312: SuitFaceoffTaunts['ls'][3], + 20313: SuitFaceoffTaunts['ls'][4], + 20314: SuitFaceoffTaunts['ls'][5], + 20315: SuitFaceoffTaunts['ls'][6], + 20316: SuitFaceoffTaunts['ls'][7], + 20317: SuitFaceoffTaunts['ls'][8], + 20318: SuitFaceoffTaunts['ls'][9], + 20319: SuitFaceoffTaunts['ls'][10], + 20320: SuitFaceoffTaunts['rb'][0], + 20321: SuitFaceoffTaunts['rb'][1], + 20322: SuitFaceoffTaunts['rb'][2], + 20323: SuitFaceoffTaunts['rb'][3], + 20324: SuitFaceoffTaunts['rb'][4], + 20325: SuitFaceoffTaunts['rb'][5], + 20326: SuitFaceoffTaunts['rb'][6], + 20327: SuitFaceoffTaunts['rb'][7], + 20328: SuitFaceoffTaunts['rb'][8], + 20329: SuitFaceoffTaunts['rb'][9], + 20330: SuitFaceoffTaunts['sc'][0], + 20331: SuitFaceoffTaunts['sc'][1], + 20332: SuitFaceoffTaunts['sc'][2], + 20333: SuitFaceoffTaunts['sc'][3], + 20334: SuitFaceoffTaunts['sc'][4], + 20335: SuitFaceoffTaunts['sc'][5], + 20336: SuitFaceoffTaunts['sc'][6], + 20337: SuitFaceoffTaunts['sc'][7], + 20338: SuitFaceoffTaunts['sc'][8], + 20339: SuitFaceoffTaunts['sc'][9], + 20340: SuitFaceoffTaunts['sc'][10], + 20341: SuitFaceoffTaunts['sd'][0], + 20342: SuitFaceoffTaunts['sd'][1], + 20343: SuitFaceoffTaunts['sd'][2], + 20344: SuitFaceoffTaunts['sd'][3], + 20345: SuitFaceoffTaunts['sd'][4], + 20346: SuitFaceoffTaunts['sd'][5], + 20347: SuitFaceoffTaunts['sd'][6], + 20348: SuitFaceoffTaunts['sd'][7], + 20349: SuitFaceoffTaunts['sd'][8], + 20350: SuitFaceoffTaunts['sd'][9], + 21000: 'Here boy!', + 21001: 'Here girl!', + 21002: 'Stay.', + 21003: 'Good boy!', + 21004: 'Good girl!', + 21005: 'Nice Doodle.', + 21006: "Please don't bother me.", + 21200: 'Jump!', + 21201: 'Beg!', + 21202: 'Play dead!', + 21203: 'Rollover!', + 21204: 'Backflip!', + 21205: 'Dance!', + 21206: 'Speak!', + 30100: "Happy April Toons' Week!", + 30101: "Welcome to my April Toons' Week party!", + 30130: 'Watch how far I can jump.', + 30131: 'Wow, you jumped really far!', + 30132: 'Hey, Doodles can talk!', + 30133: 'Did your Doodle just talk?', + 30140: 'Things sure are silly around here!', + 30141: 'How sillier could things get?', + 30150: 'Operation: Storm Sellbot is here!', + 30151: 'Sellbot Towers had its power drained by Doodles!', + 30152: 'The VP had his power drained by Doodles!', + 30153: 'Everyone can fight the VP right now!', + 30154: "You don't need a Sellbot Disguise to fight the VP!", + 30155: 'You get a Rental Suit when you go into Sellbot Towers.', + 30156: 'Do you like my Rental Suit? Sorry about the safety pins!', + 30157: "It's best to have eight Toons to fight the VP.", + 30158: 'Will you help me fight the VP?', + 30159: 'Do you want to fight the VP with me?', + 30160: 'Would you like to join my Sellbot VP group?', + 30161: 'I am looking for a Toon with a Rental Suit to fight the VP.', + 30162: 'I have a Rental Suit, and am looking to fight the VP.', + 30163: 'Just walk through the doors to get your Rental Suit.', + 30164: 'Save your gags for the Cogs inside!', + 30165: 'We have to defeat these Cogs first!', + 30166: 'Bump the barrels to gag up.', + 30167: 'Bump the barrel to get a Toon-up.', + 30168: 'Now we have to fight some Skelecogs!', + 30169: "Jump up and touch the Toon's cage for pies!", + 30170: 'Now we fight the VP!', + 30171: 'Aim your pies by pressing the Delete button.', + 30172: "Two Toons should throw pies through the VP's open doors!", + 30173: "I'll stun the VP from the front.", + 30174: "I'll stun the VP from the back.", + 30175: 'Jump when the VP jumps!', + 30180: 'I got double jellybeans on the Trolley!', + 30181: 'I got double jellybeans from fishing!', + 30182: 'I got double jellybeans at a party!', + 30183: 'Jellybeans jellybeans jellybeans!', + 30184: "I'm really keen to earn a bean!", + 30185: "Don't be smelly, get beans of jelly!", + 30186: "I'm gonna adopt a Doodle with all these jellybeans!", + 30187: 'What am I gonna spend all these jellybeans on?', + 30188: "I'm gonna throw a huge party!", + 30189: "I'm gonna decorate my whole Estate!", + 30190: "I'm gonna buy a whole new wardrobe!", + 30191: 'Jellybeans, please!', + 30192: "Don't be mean, give a bean!", + 30193: 'Who wants jellybeans?', + 30194: 'Dance for jellybeans!', + 30200: 'Deck the halls... ', + 30201: 'Load some pies...', + 30202: 'Joyful toons...', + 30203: 'Snowman heads...', + 30204: "Toontown's merry...", + 30205: 'Lure good cheer...', + 30220: 'Deck the halls with seltzer spray!\nHappy Winter Holiday!', + 30221: 'Load some pies into your sleigh!\nHappy Winter Holiday!', + 30222: 'Joyful toons bring Cogs dismay!\nHappy Winter Holiday!', + 30223: 'Snowman heads are hot today!\nHappy Winter Holiday!', + 30224: "Toontown's merry, come what may!\nHappy Winter Holiday!", + 30225: 'Lure good cheer the Toontown way!\nHappy Winter Holiday!', + 30250: 'Boo!', + 30251: 'Happy Halloween!', + 30252: 'Spooky!', + 30275: 'Happy holidays!', + 30276: "Season's greetings!", + 30277: 'Have a Wonderful Winter!', + 30301: 'Have you seen the Silly Meter?', + 30302: 'The Silly Meter is in Toon Hall.', + 30303: 'Things sure are getting silly around here!', + 30304: 'I saw a fire hydrant moving!', + 30305: 'Toontown is coming to life!', + 30306: "Have you been to Flippy's new office?", + 30307: 'I caused a Silly Surge in battle!', + 30308: "Let's defeat some Cogs to make Toontown sillier!", + 30309: 'The Silly Meter is bigger and crazier than ever!', + 30310: 'Lots of hydrants have come alive!', + 30311: 'I saw a mail box moving!', + 30312: 'I watched a trash can wake up!', + 30313: 'How silly can it get?', + 30314: "What's going to happen next?", + 30315: 'Something silly, I bet!', + 30316: 'Have you caused a Silly Surge yet?', + 30317: "Let's defeat some Cogs to make Toontown sillier!", + 30318: 'Cog Invasion!', + 30319: 'Incoming!', + 30320: "Let's stop those Cogs!", + 30321: 'I miss the Silly Surges!', + 30322: "Let's go stop an Invasion!", + 30323: 'Toontown is sillier than ever now!', + 30324: 'Have you seen something come alive?', + 30325: 'My favorites are the fire hydrants!', + 30326: 'My favorites are the mailboxes!', + 30327: 'My favorites are the trash cans!', + 30328: 'Hooray! We stopped the Cog invasions!', + 30329: 'A hydrant helped me in battle!', + 30330: 'A hydrant boosted my Squirt Gags!', + 30331: 'A trash can boosted my Toon-Up Gags!', + 30332: 'A mailbox helped my Throw Gags!', + 30350: 'Welcome to my Victory Party!', + 30351: 'This is a great Victory Party!', + 30352: "We showed those Cogs who's boss!", + 30353: 'Good job helping end the Cog invasions!', + 30354: 'I bet this is driving the Cogs crazy!', + 30355: "Let's play Cog-O-War!", + 30356: 'My team won at Cog-O-War!', + 30357: "It's nice to have fire hydrants, trash cans, and mailboxes here!", + 30358: 'I like the balloon of the Doodle biting the Cog!', + 30359: 'I like the balloon of the Cog covered in ice cream!', + 30360: 'I like the wavy Cog that flaps his arms!', + 30361: "I jumped on a Cog's face!", + 30400: 'The Sellbots are invading!', + 30401: 'The V.P. was hopping mad about Operation: Storm Sellbot ...', + 30402: "He's sending the Sellbots in to invade Toontown!", + 30403: "Let's go fight some Sellbots!", + 30404: "There's a new kind of building in Toontown!", + 30405: 'Have you seen the Mover & Shaker Field Offices?', + 30406: 'The V.P. created them as a reward for the Movers & Shakers.', + 30407: "Let's go defeat a Field Office!", + 30408: 'I got an SOS Card for defeating a Field Office!', + 30409: 'Clear the map by exploring the maze.', + 30410: 'Destroy the Cogs by hitting them with water balloons!', + 30411: 'Movers & Shakers take two balloons to destroy.', + 30412: 'Look out for falling objects!', + 30413: 'Watch out for the Cogs!', + 30414: 'Collect Jokes to get a Toon-up at the end!', + 30415: 'When the room shakes, a Mover & Shaker is nearby.', + 30416: 'Defeat all four Movers & Shakers to open the exit!', + 30417: 'The exit is open!', + 30418: "It's the Boss!", + 30450: "It's easy to be green!", + 30451: 'Visit Green Bean Jeans and you can be green too!', + 30452: "It's on Oak Street in Daisy Gardens."} +SpeedChatStaticText = SpeedChatStaticTextCommon +SCFactoryMeetMenuIndexes = (1903, + 1904, + 1906, + 1907, + 1908, + 1910, + 1913, + 1915, + 1916, + 1917, + 1919, + 1922, + 1923, + 1924, + 1932, + 1940, + 1941) +CustomSCStrings = {10: 'Oh, well.', + 20: 'Why not?', + 30: 'Naturally!', + 40: "That's the way to do it.", + 50: 'Right on!', + 60: 'What up?', + 70: 'But of course!', + 80: 'Bingo!', + 90: "You've got to be kidding...", + 100: 'Sounds good to me.', + 110: "That's kooky!", + 120: 'Awesome!', + 130: 'For crying out loud!', + 140: "Don't worry.", + 150: 'Grrrr!', + 160: "What's new?", + 170: 'Hey, hey, hey!', + 180: 'See you tomorrow.', + 190: 'See you next time.', + 200: 'See ya later, alligator.', + 210: 'After a while, crocodile.', + 220: 'I need to go soon.', + 230: "I don't know about this!", + 240: "You're outta here!", + 250: 'Ouch, that really smarts!', + 260: 'Gotcha!', + 270: 'Please!', + 280: 'Thanks a million!', + 290: "You are stylin'!", + 300: 'Excuse me!', + 310: 'Can I help you?', + 320: "That's what I'm talking about!", + 330: "If you can't take the heat, stay out of the kitchen.", + 340: 'Well shiver me timbers!', + 350: "Well isn't that special!", + 360: 'Quit horsing around!', + 370: 'Cat got your tongue?', + 380: "You're in the dog house now!", + 390: 'Look what the cat dragged in.', + 400: 'I need to go see a Toon.', + 410: "Don't have a cow!", + 420: "Don't chicken out!", + 430: "You're a sitting duck.", + 440: 'Whatever!', + 450: 'Totally!', + 460: 'Sweet!', + 470: 'That rules!', + 480: 'Yeah, baby!', + 490: 'Catch me if you can!', + 500: 'You need to heal first.', + 510: 'You need more Laff Points.', + 520: "I'll be back in a minute.", + 530: "I'm hungry.", + 540: 'Yeah, right!', + 550: "I'm sleepy.", + 560: "I'm ready!", + 570: "I'm bored.", + 580: 'I love it!', + 590: 'That was exciting!', + 600: 'Jump!', + 610: 'Got gags?', + 620: "What's wrong?", + 630: 'Easy does it.', + 640: 'Slow and steady wins the race.', + 650: 'Touchdown!', + 660: 'Ready?', + 670: 'Set!', + 680: 'Go!', + 690: "Let's go this way!", + 700: 'You won!', + 710: 'I vote yes.', + 720: 'I vote no.', + 730: 'Count me in.', + 740: 'Count me out.', + 750: "Stay here, I'll be back.", + 760: 'That was quick!', + 770: 'Did you see that?', + 780: "What's that smell?", + 790: 'That stinks!', + 800: "I don't care.", + 810: 'Just what the doctor ordered.', + 820: "Let's get this party started!", + 830: 'This way everybody!', + 840: 'What in the world?', + 850: "The check's in the mail.", + 860: 'I heard that!', + 870: 'Are you talking to me?', + 880: "Thank you, I'll be here all week.", + 890: 'Hmm.', + 900: "I'll get this one.", + 910: 'I got it!', + 920: "It's mine!", + 930: 'Please, take it.', + 940: 'Stand back, this could be dangerous.', + 950: 'No worries!', + 960: 'Oh, my!', + 970: 'Whew!', + 980: 'Owoooo!', + 990: 'All Aboard!', + 1000: 'Hot Diggity Dog!', + 1010: 'Curiosity killed the cat.', + 2000: 'Act your age!', + 2010: 'Am I glad to see you!', + 2020: 'Be my guest.', + 2030: 'Been keeping out of trouble?', + 2040: 'Better late than never!', + 2050: 'Bravo!', + 2060: 'But seriously, folks...', + 2070: 'Care to join us?', + 2080: 'Catch you later!', + 2090: 'Changed your mind?', + 2100: 'Come and get it!', + 2110: 'Dear me!', + 2120: 'Delighted to make your acquaintance.', + 2130: "Don't do anything I wouldn't do!", + 2140: "Don't even think about it!", + 2150: "Don't give up the ship!", + 2160: "Don't hold your breath.", + 2170: "Don't ask.", + 2180: 'Easy for you to say.', + 2190: 'Enough is enough!', + 2200: 'Excellent!', + 2210: 'Fancy meeting you here!', + 2220: 'Give me a break.', + 2230: 'Glad to hear it.', + 2240: 'Go ahead, make my day!', + 2250: 'Go for it!', + 2260: 'Good job!', + 2270: 'Good to see you!', + 2280: 'Got to get moving.', + 2290: 'Got to hit the road.', + 2300: 'Hang in there.', + 2310: 'Hang on a second.', + 2320: 'Have a ball!', + 2330: 'Have fun!', + 2340: "Haven't got all day!", + 2350: 'Hold your horses!', + 2360: 'Horsefeathers!', + 2370: "I don't believe this!", + 2380: 'I doubt it.', + 2390: 'I owe you one.', + 2400: 'I read you loud and clear.', + 2410: 'I think so.', + 2420: 'I think you should pass.', + 2430: "I wish I'd said that.", + 2440: "I wouldn't if I were you.", + 2450: "I'd be happy to!", + 2460: "I'm helping my friend.", + 2470: "I'm here all week.", + 2480: 'Imagine that!', + 2490: 'In the nick of time...', + 2500: "It's not over 'til it's over.", + 2510: 'Just thinking out loud.', + 2520: 'Keep in touch.', + 2530: 'Lovely weather for ducks!', + 2540: 'Make it snappy!', + 2550: 'Make yourself at home.', + 2560: 'Maybe some other time.', + 2570: 'Mind if I join you?', + 2580: 'Nice place you have here.', + 2590: 'Nice talking to you.', + 2600: 'No doubt about it.', + 2610: 'No kidding!', + 2620: 'Not by a long shot.', + 2630: 'Of all the nerve!', + 2640: 'Okay by me.', + 2650: 'Righto.', + 2660: 'Say cheese!', + 2670: 'Say what?', + 2680: 'Tah-dah!', + 2690: 'Take it easy.', + 2700: 'Ta-ta for now!', + 2710: 'Thanks, but no thanks.', + 2720: 'That takes the cake!', + 2730: "That's funny.", + 2740: "That's the ticket!", + 2750: "There's a Cog invasion!", + 2760: 'Toodles.', + 2770: 'Watch out!', + 2780: 'Well done!', + 2790: "What's cooking?", + 2800: "What's happening?", + 2810: 'Works for me.', + 2820: 'Yes sirree.', + 2830: 'You betcha.', + 2840: 'You do the math.', + 2850: 'You leaving so soon?', + 2860: 'You make me laugh!', + 2870: 'You take right.', + 2880: "You're going down!", + 3000: 'Anything you say.', + 3010: 'Care if I join you?', + 3020: 'Check, please.', + 3030: "Don't be too sure.", + 3040: "Don't mind if I do.", + 3050: "Don't sweat it!", + 3060: "Don't you know it!", + 3070: "Don't mind me.", + 3080: 'Eureka!', + 3090: 'Fancy that!', + 3100: 'Forget about it!', + 3110: 'Going my way?', + 3120: 'Good for you!', + 3130: 'Good grief.', + 3140: 'Have a good one!', + 3150: 'Heads up!', + 3160: 'Here we go again.', + 3170: 'How about that!', + 3180: 'How do you like that?', + 3190: 'I believe so.', + 3200: 'I think not.', + 3210: "I'll get back to you.", + 3220: "I'm all ears.", + 3230: "I'm busy.", + 3240: "I'm not kidding!", + 3250: "I'm speechless.", + 3260: 'Keep smiling.', + 3270: 'Let me know!', + 3280: 'Let the pie fly!', + 3290: "Likewise, I'm sure.", + 3300: 'Look alive!', + 3310: 'My, how time flies.', + 3320: 'No comment.', + 3330: "Now you're talking!", + 3340: 'Okay by me.', + 3350: 'Pleased to meet you.', + 3360: 'Righto.', + 3370: 'Sure thing.', + 3380: 'Thanks a million.', + 3390: "That's more like it.", + 3400: "That's the stuff!", + 3410: 'Time for me to hit the hay.', + 3420: 'Trust me!', + 3430: 'Until next time.', + 3440: 'Wait up!', + 3450: 'Way to go!', + 3460: 'What brings you here?', + 3470: 'What happened?', + 3480: 'What now?', + 3490: 'You first.', + 3500: 'You take left.', + 3510: 'You wish!', + 3520: "You're toast!", + 3530: "You're too much!", + 4000: 'Toons rule!', + 4010: 'Cogs drool!', + 4020: 'Toons of the world unite!', + 4030: 'Howdy, partner!', + 4040: 'Much obliged.', + 4050: 'Get along, little doggie.', + 4060: "I'm going to hit the hay.", + 4070: "I'm chomping at the bit!", + 4080: "This town isn't big enough for the two of us!", + 4090: 'Saddle up!', + 4100: 'Draw!!!', + 4110: "There's gold in them there hills!", + 4120: 'Happy trails!', + 4130: 'This is where I ride off into the sunset...', + 4140: "Let's skedaddle!", + 4150: 'You got a bee in your bonnet?', + 4160: 'Lands sake!', + 4170: 'Right as rain.', + 4180: 'I reckon so.', + 4190: "Let's ride!", + 4200: 'Well, go figure!', + 4210: "I'm back in the saddle again!", + 4220: 'Round up the usual suspects.', + 4230: 'Giddyup!', + 4240: 'Reach for the sky.', + 4250: "I'm fixing to.", + 4260: 'Hold your horses!', + 4270: "I can't hit the broad side of a barn.", + 4280: "Y'all come back now.", + 4290: "It's a real barn burner!", + 4300: "Don't be a yellow belly.", + 4310: 'Feeling lucky?', + 4320: "What in Sam Hill's goin' on here?", + 4330: 'Shake your tail feathers!', + 4340: "Well, don't that take all.", + 4350: "That's a sight for sore eyes!", + 4360: 'Pickins is mighty slim around here.', + 4370: 'Take a load off.', + 4380: "Aren't you a sight!", + 4390: "That'll learn ya!", + 6000: 'I want candy!', + 6010: "I've got a sweet tooth.", + 6020: "That's half-baked.", + 6030: 'Just like taking candy from a baby!', + 6040: "They're cheaper by the dozen.", + 6050: 'Let them eat cake!', + 6060: "That's the icing on the cake.", + 6070: "You can't have your cake and eat it too.", + 6080: 'I feel like a kid in a candy store.', + 6090: 'Six of one, half a dozen of the other...', + 6100: "Let's keep it short and sweet.", + 6110: 'Keep your eye on the doughnut not the hole.', + 6120: "That's pie in the sky.", + 6130: "But it's wafer thin.", + 6140: "Let's gum up the works!", + 6150: "You're one tough cookie!", + 6160: "That's the way the cookie crumbles.", + 6170: 'Like water for chocolate.', + 6180: 'Are you trying to sweet talk me?', + 6190: 'A spoonful of sugar helps the medicine go down.', + 6200: 'You are what you eat!', + 6210: 'Easy as pie!', + 6220: "Don't be a sucker!", + 6230: 'Sugar and spice and everything nice.', + 6240: "It's like butter!", + 6250: 'The candyman can!', + 6260: 'We all scream for ice cream!', + 6270: "Let's not sugar coat it.", + 6280: 'Knock knock...', + 6290: "Who's there?", + 7000: 'Quit monkeying around!', + 7010: 'That really throws a monkey-wrench in things.', + 7020: 'Monkey see, monkey do.', + 7030: 'They made a monkey out of you.', + 7040: 'That sounds like monkey business.', + 7050: "I'm just monkeying with you.", + 7060: "Who's gonna be monkey in the middle?", + 7070: "That's a monkey off my back...", + 7080: 'This is more fun than a barrel of monkeys!', + 7090: "Well I'll be a monkey's uncle.", + 7100: "I've got monkeys on the brain.", + 7110: "What's with the monkey suit?", + 7120: 'Hear no evil.', + 7130: 'See no evil.', + 7140: 'Speak no evil.', + 7150: "Let's make like a banana and split.", + 7160: "It's a jungle out there.", + 7170: "You're the top banana.", + 7180: 'Cool bananas!', + 7190: "I'm going bananas!", + 7200: "Let's get into the swing of things!", + 7210: 'This place is swinging!', + 7220: "I'm dying on the vine.", + 7230: 'This whole affair has me up a tree.', + 7230: "Let's make like a tree and leave.", + 7240: "Jellybeans don't grow on trees!", + 10000: 'This place is a ghost town.', + 10001: 'Nice costume!', + 10002: 'I think this place is haunted.', + 10003: 'Trick or Treat!', + 10004: 'Boo!', + 10005: 'Happy Haunting!', + 10006: 'Happy Halloween!', + 10007: "It's time for me to turn into a pumpkin.", + 10008: 'Spooktastic!', + 10009: 'Spooky!', + 10010: "That's creepy!", + 10011: 'I hate spiders!', + 10012: 'Did you hear that?', + 10013: "You don't have a ghost of a chance!", + 10014: 'You scared me!', + 10015: "That's spooky!", + 10016: "That's freaky!", + 10017: 'That was strange....', + 10018: 'Skeletons in your closet?', + 10019: 'Did I scare you?', + 11000: 'Bah! Humbug!', + 11001: 'Better not pout!', + 11002: 'Brrr!', + 11003: 'Chill out!', + 11004: 'Come and get it!', + 11005: "Don't be a turkey.", + 11006: 'Gobble gobble!', + 11007: 'Happy holidays!', + 11008: 'Happy New Year!', + 11009: 'Happy Thanksgiving!', + 11010: 'Happy Turkey Day!', + 11011: 'Ho! Ho! Ho!', + 11012: 'It\'s "snow" problem.', + 11013: 'It\'s "snow" wonder.', + 11014: 'Let it snow!', + 11015: "Rake 'em in.", + 11016: "Season's greetings!", + 11017: 'Snow doubt about it!', + 11018: 'Snow far, snow good!', + 11019: 'Yule be sorry!', + 11020: 'Have a Wonderful Winter!', + 11021: 'The Holiday Party decorations are Toontastic!', + 11022: 'Toon Troopers are hosting Holiday Parties!', + 12000: 'Be mine!', + 12001: 'Be my sweetie!', + 12002: "Happy ValenToon's Day!", + 12003: 'Aww, how cute.', + 12004: "I'm sweet on you.", + 12005: "It's puppy love.", + 12006: 'Love ya!', + 12007: 'Will you be my ValenToon?', + 12008: 'You are a sweetheart.', + 12009: 'You are as sweet as pie.', + 12010: 'You are cute.', + 12011: 'You need a hug.', + 12012: 'Lovely!', + 12013: "That's darling!", + 12014: 'Roses are red...', + 12015: 'Violets are blue...', + 12016: "That's sweet!", + 12050: 'I LOVE busting Cogs!', + 12051: "You're dynamite!", + 12052: 'I only have hypno-eyes for you!', + 12053: "You're sweeter than a jellybean!", + 12054: "I'd LOVE for you to come to my ValenToon's party!", + 13000: "Top o' the mornin' to you!", + 13001: "Happy St. Patrick's Day!", + 13002: "You're not wearing green!", + 13003: "It's the luck of the Irish.", + 13004: "I'm green with envy.", + 13005: 'You lucky dog!', + 13006: "You're my four leaf clover!", + 13007: "You're my lucky charm!", + 14000: "Let's have a summer Estate party!", + 14001: "It's party time!", + 14002: 'Last one in the pond is a rotten Cog!', + 14003: 'Group Doodle training time!', + 14004: 'Doodle training time!', + 14005: 'Your Doodle is cool!', + 14006: 'What tricks can your Doodle do?', + 14007: 'Time for Cannon Pinball!', + 14008: 'Cannon Pinball rocks!', + 14009: 'Your Estate rocks!', + 14010: 'Your Garden is cool!', + 14011: 'Your Estate is cool!'} +SCMenuCommonCogIndices = (20000, 20004) +SCMenuCustomCogIndices = {'bf': (20005, 20014), + 'nc': (20015, 20024), + 'ym': (20025, 20035), + 'ms': (20036, 20046), + 'bc': (20047, 20057), + 'cc': (20058, 20070), + 'nd': (20071, 20080), + 'ac': (20081, 20092), + 'tf': (20093, 20103), + 'hh': (20104, 20114), + 'le': (20115, 20124), + 'bs': (20125, 20135), + 'cr': (20136, 20145), + 'tbc': (20146, 20156), + 'ds': (20157, 20164), + 'gh': (20165, 20177), + 'pp': (20178, 20187), + 'b': (20188, 20199), + 'f': (20200, 20210), + 'mm': (20211, 20224), + 'tw': (20225, 20235), + 'mb': (20236, 20245), + 'm': (20246, 20254), + 'mh': (20255, 20266), + 'dt': (20267, 20276), + 'p': (20277, 20287), + 'tm': (20288, 20298), + 'bw': (20299, 20308), + 'ls': (20309, 20319), + 'rb': (20320, 20329), + 'sc': (20330, 20331), + 'sd': (20341, 20350)} +RandomButton = 'Randomize' +TypeANameButton = 'Type Name' +PickANameButton = 'Pick-A-Name' +NameShopSubmitButton = 'Submit' +RejectNameText = 'That name is not allowed. Please try again.' +WaitingForNameSubmission = 'Submitting your name...' +NameShopNameMaster = 'NameMasterEnglish.txt' +NameShopContinueSubmission = 'Continue Submission' +NameShopChooseAnother = 'Choose Another Name' +NameShopToonCouncil = 'The Toon Council\nwill review your\nname. ' + 'Review may\ntake a few days.\nWhile you wait\nyour name will be\n ' +PleaseTypeName = 'Please type your name:' +ToonAlreadyExists = '%s already exists' +AllNewNames = 'All new names\nmust be approved\nby the Name Council.' +NameShopNameRejected = 'The name you\nsubmitted has\nbeen rejected.' +NameShopNameAccepted = 'Congratulations!\nThe name you\nsubmitted has\nbeen accepted!' +NoPunctuation = "You can't use punctuation marks in your name!" +PeriodOnlyAfterLetter = 'You can use a period in your name, but only after a letter.' +ApostropheOnlyAfterLetter = 'You can use an apostrophe in your name, but only after a letter.' +NoNumbersInTheMiddle = 'Numeric digits may not appear in the middle of a word.' +ThreeWordsOrLess = 'Your name must be three words or fewer.' +NCTooShort = 'That name is too short.' +NCNoDigits = 'Your name cannot contain numbers.' +NCNeedLetters = 'Each word in your name must contain some letters.' +NCNeedVowels = 'Each word in your name must contain some vowels.' +NCAllCaps = 'Your name cannot be all capital letters.' +NCMixedCase = 'That name has too many capital letters.' +NCBadCharacter = "Your name cannot contain the character '%s'" +NCRepeatedChar = "Your name has too many of the character '%s'" +NCGeneric = 'Sorry, that name will not work.' +NCTooManyWords = 'Your name cannot be more than four words long.' +NCDashUsage = "Dashes may only be used to connect two words together (like in 'Boo-Boo')." +NCCommaEdge = 'Your name may not begin or end with a comma.' +NCCommaAfterWord = 'You may not begin a word with a comma.' +NCCommaUsage = 'That name does not use commas properly. Commas must join two words together, like in the name "Dr. Quack, MD". Commas must also be followed by a space.' +NCPeriodUsage = 'That name does not use periods properly. Periods are only allowed in words like "Mr.", "Mrs.", "J.T.", etc.' +NCApostrophes = 'That name has too many apostrophes.' +AvatarDetailPanelOK = lOK +AvatarDetailPanelCancel = lCancel +AvatarDetailPanelClose = lClose +AvatarDetailPanelLookup = 'Looking up details for %s.' +AvatarDetailPanelFailedLookup = 'Unable to get details for %s.' +AvatarDetailPanelOnline = 'District: %(district)s\nLocation: %(location)s' +AvatarDetailPanelOffline = 'District: offline\nLocation: offline' +AvatarPanelFriends = 'Friends' +AvatarPanelWhisper = 'Whisper' +AvatarPanelTrueFriends = 'True Friends' +AvatarPanelGoTo = 'Go To' +AvatarPanelIgnore = 'Ignore' +AvatarPanelStopIgnore = 'Stop Ignoring' +AvatarPanelEndIgnore = 'End Ignore' +AvatarPanelCogLevel = 'Level: %s' +AvatarPanelCogDetailClose = lClose +TeleportPanelOK = lOK +TeleportPanelCancel = lCancel +TeleportPanelYes = lYes +TeleportPanelNo = lNo +TeleportPanelCheckAvailability = 'Trying to go to %s.' +TeleportPanelNotAvailable = '%s is busy right now; try again later.' +TeleportPanelIgnored = '%s is ignoring you.' +TeleportPanelNotOnline = "%s isn't online right now." +TeleportPanelWentAway = '%s went away.' +TeleportPanelUnknownHood = "You don't know how to get to %s!" +TeleportPanelUnavailableHood = '%s is not available right now; try again later.' +TeleportPanelDenySelf = "You can't go to yourself!" +TeleportPanelOtherShard = "%(avName)s is in district %(shardName)s, and you're in district %(myShardName)s. Do you want to switch to %(shardName)s?" +KartRacingMenuSections = [-1, + 'PLACES', + 'RACES', + 'TRACKS', + 'COMPLIMENTS', + 'TAUNTS'] +AprilToonsMenuSections = [-1, + 'GREETINGS', + 'ESTATES'] +SillyHolidayMenuSections = [-1, 'WORLD', 'BATTLE'] +CarolMenuSections = [-1] +VictoryPartiesMenuSections = [-1, 'PARTY', 'ITEMS'] +GolfMenuSections = [-1, + 'COURSES', + 'TIPS', + 'COMMENTS'] +BoardingMenuSections = ['GROUP', + "Let's go to...", + "We're going to...", + -1] +SellbotNerfMenuSections = [-1, 'GROUPING', 'SELLBOT TOWERS/VP'] +JellybeanJamMenuSections = ['GET JELLYBEANS', 'SPEND JELLYBEANS'] +WinterMenuSections = ['CAROLING', -1] +HalloweenMenuSections = [-1] +WhiteListMenu = [-1, 'WHITELIST'] +SellbotInvasionMenuSections = [-1] +SellbotFieldOfficeMenuSections = [-1, 'STRATEGY'] +IdesOfMarchMenuSections = [-1] + +def timeElapsedString(timeDelta): + timeDelta = abs(timeDelta) + if timeDelta.days > 0: + if timeDelta.days == 1: + return '1 day ago' + else: + return '%s days ago' % timeDelta.days + elif timeDelta.seconds / 3600 > 0: + if timeDelta.seconds / 3600 == 1: + return '1 hour ago' + else: + return '%s hours ago' % (timeDelta.seconds / 3600) + elif timeDelta.seconds / 60 < 2: + return '1 minute ago' + else: + return '%s minutes ago' % (timeDelta.seconds / 60) + +AsciiNotSupported = 'Sorry, but Toontown Stride does not support non-ASCII characters.' +AccessToString = { + 200: '\x01amaranth\x01Community Manager\x02', + 300: '\x01caribbeanGreen\x01Moderator\x02', + 400: '\x01amber\x01Artist\x02', + 500: '\x01androidGreen\x01Developer\x02', + 600: '\x01cobalt\x01Admin\x02', + 700: '\x01azure\x01System Admin\x02' +} \ No newline at end of file diff --git a/otp/otpbase/OTPLocalizerEnglishProperty.py b/otp/otpbase/OTPLocalizerEnglishProperty.py new file mode 100755 index 00000000..65cdcc67 --- /dev/null +++ b/otp/otpbase/OTPLocalizerEnglishProperty.py @@ -0,0 +1,4 @@ +LTPDdirectButtonYesText = 0.05 +LTPDdirectButtonNoText = 0.05 +LTPDdirectFrameText = 0.06 +SCOsubmenuOverlap = 2.0 / 3 diff --git a/otp/otpbase/OTPRender.py b/otp/otpbase/OTPRender.py new file mode 100755 index 00000000..ecb99b74 --- /dev/null +++ b/otp/otpbase/OTPRender.py @@ -0,0 +1,47 @@ +from panda3d.core import * +MainCameraBitmask = BitMask32.bit(0) +ReflectionCameraBitmask = BitMask32.bit(1) +ShadowCameraBitmask = BitMask32.bit(2) +SkyReflectionCameraBitmask = BitMask32.bit(3) +GlowCameraBitmask = BitMask32.bit(4) + +def setCameraBitmask(default, node_path, camera_bitmask, tag = None, tag_function = None, context = None): + if node_path: + show = default + if tag_function: + show = tag_function(default, tag, context) + if show: + node_path.show(camera_bitmask) + else: + node_path.hide(camera_bitmask) + + +def renderReflection(default, node_path, tag = None, tag_function = None, context = None): + setCameraBitmask(default, node_path, ReflectionCameraBitmask, tag, tag_function, context) + + +def renderShadow(default, node_path, tag = None, tag_function = None, context = None): + setCameraBitmask(default, node_path, ShadowCameraBitmask, tag, tag_function, context) + + +def renderSkyReflection(default, node_path, tag = None, tag_function = None, context = None): + setCameraBitmask(default, node_path, SkyReflectionCameraBitmask, tag, tag_function, context) + + +def renderGlow(default, node_path, tag = None, tag_function = None, context = None): + setCameraBitmask(default, node_path, GlowCameraBitmask, tag, tag_function, context) + + +def setAdditiveEffect(node_path, tag = None, bin_name = None, lighting_on = False, reflect = False): + if node_path: + node_path.setTransparency(True) + node_path.setDepthWrite(False) + node_path.node().setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + if lighting_on == False: + node_path.setLightOff() + node_path.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.CRed | ColorWriteAttrib.CGreen | ColorWriteAttrib.CBlue)) + if reflect == False: + renderReflection(False, node_path, tag, None) + if bin_name: + node_path.setBin(bin_name, 0) + return diff --git a/otp/otpbase/__init__.py b/otp/otpbase/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/otp/otpgui/OTPDialog.py b/otp/otpgui/OTPDialog.py new file mode 100755 index 00000000..d5dea0fe --- /dev/null +++ b/otp/otpgui/OTPDialog.py @@ -0,0 +1,115 @@ +from direct.gui.DirectGui import * +from direct.directnotify import DirectNotifyGlobal +import string +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPLocalizer +NoButtons = 0 +Acknowledge = 1 +CancelOnly = 2 +TwoChoice = 3 +YesNo = 4 +TwoChoiceCustom = 5 + +class OTPDialog(DirectDialog): + + def __init__(self, parent = None, style = NoButtons, **kw): + if parent == None: + parent = aspect2d + self.style = style + buttons = None + if self.style != NoButtons: + buttons = loader.loadModel(self.path) + if self.style == TwoChoiceCustom: + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + buttonImage = [okImageList, cancelImageList] + buttonValue = [DGG.DIALOG_OK, DGG.DIALOG_CANCEL] + if 'buttonText' in kw: + buttonText = kw['buttonText'] + del kw['buttonText'] + else: + buttonText = [OTPLocalizer.DialogOK, OTPLocalizer.DialogCancel] + elif self.style == TwoChoice: + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + buttonImage = [okImageList, cancelImageList] + buttonText = [OTPLocalizer.DialogOK, OTPLocalizer.DialogCancel] + buttonValue = [DGG.DIALOG_OK, DGG.DIALOG_CANCEL] + elif self.style == YesNo: + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + buttonImage = [okImageList, cancelImageList] + buttonText = [OTPLocalizer.DialogYes, OTPLocalizer.DialogNo] + buttonValue = [DGG.DIALOG_OK, DGG.DIALOG_CANCEL] + elif self.style == Acknowledge: + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + buttonImage = [okImageList] + buttonText = [OTPLocalizer.DialogOK] + buttonValue = [DGG.DIALOG_OK] + elif self.style == CancelOnly: + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + buttonImage = [cancelImageList] + buttonText = [OTPLocalizer.DialogCancel] + buttonValue = [DGG.DIALOG_CANCEL] + elif self.style == NoButtons: + buttonImage = [] + buttonText = [] + buttonValue = [] + else: + self.notify.error('No such style as: ' + str(self.style)) + optiondefs = (('buttonImageList', buttonImage, DGG.INITOPT), + ('buttonTextList', buttonText, DGG.INITOPT), + ('buttonValueList', buttonValue, DGG.INITOPT), + ('buttonPadSF', 2.2, DGG.INITOPT), + ('text_font', DGG.getDefaultFont(), None), + ('text_wordwrap', 12, None), + ('text_scale', 0.07, None), + ('buttonSize', (-.05, 0.05, -.05, 0.05), None), + ('button_pad', (0, 0), None), + ('button_relief', None, None), + ('button_text_pos', (0, -0.1), None), + ('fadeScreen', 0.5, None), + ('image_color', OTPGlobals.GlobalDialogColor, None), + ('image', DGG.getDefaultDialogGeom(), None), + ('relief', None, None)) + self.defineoptions(kw, optiondefs) + DirectDialog.__init__(self, parent) + self.initialiseoptions(OTPDialog) + if buttons != None: + buttons.removeNode() + return + + +class GlobalDialog(OTPDialog): + notify = DirectNotifyGlobal.directNotify.newCategory('GlobalDialog') + + def __init__(self, message = '', doneEvent = None, style = NoButtons, okButtonText = OTPLocalizer.DialogOK, cancelButtonText = OTPLocalizer.DialogCancel, **kw): + if not hasattr(self, 'path'): + self.path = 'phase_3/models/gui/dialog_box_buttons_gui' + if doneEvent == None and style != NoButtons: + self.notify.error('Boxes with buttons must specify a doneEvent.') + self.__doneEvent = doneEvent + if style == NoButtons: + buttonText = [] + elif style == Acknowledge: + buttonText = [okButtonText] + elif style == CancelOnly: + buttonText = [cancelButtonText] + else: + buttonText = [okButtonText, cancelButtonText] + optiondefs = (('dialogName', 'globalDialog', DGG.INITOPT), + ('buttonTextList', buttonText, DGG.INITOPT), + ('text', message, None), + ('command', self.handleButton, None)) + self.defineoptions(kw, optiondefs) + OTPDialog.__init__(self, style=style) + self.initialiseoptions(GlobalDialog) + return + + def handleButton(self, value): + if value == DGG.DIALOG_OK: + self.doneStatus = 'ok' + messenger.send(self.__doneEvent) + elif value == DGG.DIALOG_CANCEL: + self.doneStatus = 'cancel' + messenger.send(self.__doneEvent) diff --git a/otp/otpgui/__init__.py b/otp/otpgui/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/otp/settings/Settings.py b/otp/settings/Settings.py new file mode 100755 index 00000000..de87fe0e --- /dev/null +++ b/otp/settings/Settings.py @@ -0,0 +1,37 @@ +import collections +import json +import os + +class Settings(collections.MutableMapping): + def __init__(self, filename): + self.filename = filename + self.store = {} + self.read() + + def read(self): + if os.path.exists(self.filename): + with open(self.filename, 'r') as f: + self.store = json.load(f) + else: + self.write() + + def write(self): + with open(self.filename, 'w') as f: + json.dump(self.store, f, sort_keys=True, indent=2, separators=(',', ': ')) + + def __setitem__(self, key, value): + self.store[key] = value + self.write() + + def __delitem__(self, key): + del self.store[key] + self.write() + + def __getitem__(self, key): + return self.store[key] + + def __iter__(self): + return iter(self.store) + + def __len__(self): + return len(self.store) diff --git a/otp/settings/__init__.py b/otp/settings/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/otp/speedchat/ColorSpace.py b/otp/speedchat/ColorSpace.py new file mode 100755 index 00000000..854b19d1 --- /dev/null +++ b/otp/speedchat/ColorSpace.py @@ -0,0 +1,61 @@ +import math + +def rgb2hsv(r, g, b): + _min = float(min(r, g, b)) + _max = float(max(r, g, b)) + v = _max + delta = _max - _min + if delta != 0.0: + s = delta / _max + else: + s = 0.0 + h = -1.0 + return (h, s, v) + if r == _max: + h = (g - b) / delta + elif g == _max: + h = 2.0 + (b - r) / delta + else: + h = 4.0 + (r - g) / delta + h *= 60.0 + if h < 0.0: + h += 360.0 + return (h, s, v) + + +def hsv2rgb(h, s, v): + if s == 0.0: + return (v, v, v) + h %= 360.0 + h /= 60.0 + i = int(math.floor(h)) + f = h - i + p = v * (1.0 - s) + q = v * (1.0 - s * f) + t = v * (1.0 - s * (1.0 - f)) + if i == 0: + return (v, t, p) + elif i == 1: + return (q, v, p) + elif i == 2: + return (p, v, t) + elif i == 3: + return (p, q, v) + elif i == 4: + return (t, p, v) + else: + return (v, p, q) + + +def rgb2yuv(r, g, b): + y = 0.299 * r + 0.587 * g + 0.114 * b + u = -.169 * r - 0.331 * g + 0.5 * b + 0.5 + v = 0.5 * r - 0.419 * g - 0.081 * b + 0.5 + return tuple(map(lambda x: min(max(x, 0), 1), (y, u, v))) + + +def yuv2rgb(y, u, v): + r = y - 0.0009267 * (u - 0.5) + 1.4016868 * (v - 0.5) + g = y - 0.3436954 * (u - 0.5) - 0.714169 * (v - 0.5) + b = y + 1.7721604 * (u - 0.5) + 0.0009902 * (v - 0.5) + return tuple(map(lambda x: min(max(x, 0), 1), (r, g, b))) diff --git a/otp/speedchat/SCColorScheme.py b/otp/speedchat/SCColorScheme.py new file mode 100755 index 00000000..7b2050bd --- /dev/null +++ b/otp/speedchat/SCColorScheme.py @@ -0,0 +1,79 @@ +from ColorSpace import * + +class SCColorScheme: + + def __init__(self, arrowColor = (0.5, 0.5, 1), rolloverColor = (0.53, 0.9, 0.53), frameColor = None, pressedColor = None, menuHolderActiveColor = None, emoteIconColor = None, textColor = (0, 0, 0), emoteIconDisabledColor = (0.5, 0.5, 0.5), textDisabledColor = (0.4, 0.4, 0.4), alpha = 0.95): + + def scaleColor(color, s): + y, u, v = rgb2yuv(*color) + return yuv2rgb(y * s, u, v) + + def scaleIfNone(color, srcColor, s): + if color is not None: + return color + else: + return scaleColor(srcColor, s) + return + + self.__arrowColor = arrowColor + self.__rolloverColor = rolloverColor + self.__frameColor = frameColor + if self.__frameColor is None: + h, s, v = rgb2hsv(*arrowColor) + self.__frameColor = hsv2rgb(h, 0.2 * s, v) + h, s, v = rgb2hsv(*self.__frameColor) + self.__frameColor = hsv2rgb(h, 0.5 * s, v) + self.__pressedColor = scaleIfNone(pressedColor, self.__rolloverColor, 0.92) + self.__menuHolderActiveColor = scaleIfNone(menuHolderActiveColor, self.__rolloverColor, 0.84) + self.__emoteIconColor = emoteIconColor + if self.__emoteIconColor is None: + h, s, v = rgb2hsv(*self.__rolloverColor) + self.__emoteIconColor = hsv2rgb(h, 1.0, 0.8 * v) + self.__emoteIconDisabledColor = emoteIconDisabledColor + self.__textColor = textColor + self.__textDisabledColor = textDisabledColor + self.__alpha = alpha + return + + def getArrowColor(self): + return self.__arrowColor + + def getRolloverColor(self): + return self.__rolloverColor + + def getFrameColor(self): + return self.__frameColor + + def getPressedColor(self): + return self.__pressedColor + + def getMenuHolderActiveColor(self): + return self.__menuHolderActiveColor + + def getEmoteIconColor(self): + return self.__emoteIconColor + + def getTextColor(self): + return self.__textColor + + def getEmoteIconDisabledColor(self): + return self.__emoteIconDisabledColor + + def getTextDisabledColor(self): + return self.__textDisabledColor + + def getAlpha(self): + return self.__alpha + + def __str__(self): + members = ('arrowColor', 'rolloverColor', 'frameColor', 'pressedColor', 'menuHolderActiveColor', 'emoteIconColor', 'textColor', 'emoteIconDisabledColor', 'textDisabledColor', 'alpha') + result = '' + for member in members: + result += '%s = %s' % (member, self.__dict__['_%s__%s' % (self.__class__.__name__, member)]) + if member is not members[-1]: + result += '\n' + + return result + + def __repr__(self): + return str(self) diff --git a/otp/speedchat/SCConstants.py b/otp/speedchat/SCConstants.py new file mode 100755 index 00000000..2be1e505 --- /dev/null +++ b/otp/speedchat/SCConstants.py @@ -0,0 +1,2 @@ +SCMenuFinalizePriority = 48 +SCElementFinalizePriority = 47 diff --git a/otp/speedchat/SCCustomMenu.py b/otp/speedchat/SCCustomMenu.py new file mode 100755 index 00000000..e5133992 --- /dev/null +++ b/otp/speedchat/SCCustomMenu.py @@ -0,0 +1,22 @@ +from SCCustomTerminal import SCCustomTerminal +from SCMenu import SCMenu +from otp.otpbase.OTPLocalizer import CustomSCStrings + + +class SCCustomMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + + self.accept('customMessagesChanged', self.__customMessagesChanged) + self.__customMessagesChanged() + + def __customMessagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + for msgIndex in lt.customMessages: + if msgIndex in CustomSCStrings: + self.append(SCCustomTerminal(msgIndex)) diff --git a/otp/speedchat/SCCustomTerminal.py b/otp/speedchat/SCCustomTerminal.py new file mode 100755 index 00000000..d04a521e --- /dev/null +++ b/otp/speedchat/SCCustomTerminal.py @@ -0,0 +1,18 @@ +from SCTerminal import SCTerminal +from otp.otpbase.OTPLocalizer import CustomSCStrings +SCCustomMsgEvent = 'SCCustomMsg' + +def decodeSCCustomMsg(textId): + return CustomSCStrings.get(textId, None) + + +class SCCustomTerminal(SCTerminal): + + def __init__(self, textId): + SCTerminal.__init__(self) + self.textId = textId + self.text = CustomSCStrings[self.textId] + + def handleSelect(self): + SCTerminal.handleSelect(self) + messenger.send(self.getEventName(SCCustomMsgEvent), [self.textId]) diff --git a/otp/speedchat/SCDecoders.py b/otp/speedchat/SCDecoders.py new file mode 100755 index 00000000..39357c78 --- /dev/null +++ b/otp/speedchat/SCDecoders.py @@ -0,0 +1,3 @@ +from SCStaticTextTerminal import decodeSCStaticTextMsg +from SCCustomTerminal import decodeSCCustomMsg +from SCEmoteTerminal import decodeSCEmoteWhisperMsg diff --git a/otp/speedchat/SCElement.py b/otp/speedchat/SCElement.py new file mode 100755 index 00000000..7f9bd25c --- /dev/null +++ b/otp/speedchat/SCElement.py @@ -0,0 +1,174 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from direct.task import Task +from SCConstants import * +from SCObject import SCObject +from direct.showbase.PythonUtil import boolEqual +from otp.otpbase import OTPGlobals + +class SCElement(SCObject, NodePath): + font = OTPGlobals.getInterfaceFont() + SerialNum = 0 + + def __init__(self, parentMenu = None): + SCObject.__init__(self) + self.SerialNum = SCElement.SerialNum + SCElement.SerialNum += 1 + node = hidden.attachNewNode('SCElement%s' % self.SerialNum) + NodePath.__init__(self, node) + self.FinalizeTaskName = 'SCElement%s_Finalize' % self.SerialNum + self.parentMenu = parentMenu + self.__active = 0 + self.__viewable = 1 + self.lastWidth = 0 + self.lastHeight = 0 + self.setDimensions(0, 0) + self.padX = 0.25 + self.padZ = 0.1 + + def destroy(self): + if self.isActive(): + self.exitActive() + SCObject.destroy(self) + if hasattr(self, 'button'): + self.button.destroy() + del self.button + self.parentMenu = None + self.detachNode() + return + + def setParentMenu(self, parentMenu): + self.parentMenu = parentMenu + + def getParentMenu(self): + return self.parentMenu + + def getDisplayText(self): + self.notify.error('getDisplayText is pure virtual, derived class must override') + + def onMouseEnter(self, event): + if self.parentMenu is not None: + self.parentMenu.memberGainedInputFocus(self) + return + + def onMouseLeave(self, event): + if self.parentMenu is not None: + self.parentMenu.memberLostInputFocus(self) + return + + def onMouseClick(self, event): + pass + + def enterActive(self): + self.__active = 1 + + def exitActive(self): + self.__active = 0 + + def isActive(self): + return self.__active + + def hasStickyFocus(self): + return 0 + + def setViewable(self, viewable): + if not boolEqual(self.__viewable, viewable): + self.__viewable = viewable + if self.parentMenu is not None: + self.parentMenu.memberViewabilityChanged(self) + return + + def isViewable(self): + return self.__viewable + + def getMinDimensions(self): + text = TextNode('SCTemp') + text.setFont(SCElement.font) + dText = self.getDisplayText() + text.setText(dText) + bounds = text.getCardActual() + width = abs(bounds[1] - bounds[0]) + self.padX + height = abs(bounds[3] - bounds[2]) + 2.0 * self.padZ + return (width, height) + + def setDimensions(self, width, height): + self.width = float(width) + self.height = float(height) + if (self.lastWidth, self.lastHeight) != (self.width, self.height): + self.invalidate() + + def invalidate(self): + SCObject.invalidate(self) + parentMenu = self.getParentMenu() + if parentMenu is not None: + if not parentMenu.isFinalizing(): + parentMenu.invalidate() + return + + def enterVisible(self): + SCObject.enterVisible(self) + self.privScheduleFinalize() + + def exitVisible(self): + SCObject.exitVisible(self) + self.privCancelFinalize() + + def privScheduleFinalize(self): + + def finalizeElement(task, self = self): + if self.parentMenu is not None: + if self.parentMenu.isDirty(): + return Task.done + self.finalize() + return Task.done + + taskMgr.remove(self.FinalizeTaskName) + taskMgr.add(finalizeElement, self.FinalizeTaskName, priority=SCElementFinalizePriority) + + def privCancelFinalize(self): + taskMgr.remove(self.FinalizeTaskName) + + def finalize(self, dbArgs = {}): + if not self.isDirty(): + return + SCObject.finalize(self) + if hasattr(self, 'button'): + self.button.destroy() + del self.button + halfHeight = self.height / 2.0 + textX = 0 + if 'text_align' in dbArgs: + if dbArgs['text_align'] == TextNode.ACenter: + textX = self.width / 2.0 + args = {'text': self.getDisplayText(), + 'frameColor': (0, 0, 0, 0), + 'rolloverColor': self.getColorScheme().getRolloverColor() + (1,), + 'pressedColor': self.getColorScheme().getPressedColor() + (1,), + 'text_font': OTPGlobals.getInterfaceFont(), + 'text_align': TextNode.ALeft, + 'text_fg': self.getColorScheme().getTextColor() + (1,), + 'text_pos': (textX, -.25 - halfHeight, 0), + 'relief': DGG.FLAT, + 'pressEffect': 0} + args.update(dbArgs) + rolloverColor = args['rolloverColor'] + pressedColor = args['pressedColor'] + del args['rolloverColor'] + del args['pressedColor'] + btn = DirectButton(parent=self, frameSize=(0, + self.width, + -self.height, + 0), **args) + btn.frameStyle[DGG.BUTTON_ROLLOVER_STATE].setColor(*rolloverColor) + btn.frameStyle[DGG.BUTTON_DEPRESSED_STATE].setColor(*pressedColor) + btn.updateFrameStyle() + btn.bind(DGG.ENTER, self.onMouseEnter) + btn.bind(DGG.EXIT, self.onMouseLeave) + btn.bind(DGG.B1PRESS, self.onMouseClick) + self.button = btn + self.lastWidth = self.width + self.lastHeight = self.height + self.validate() + + def __str__(self): + return '%s: %s' % (self.__class__.__name__, self.getDisplayText()) diff --git a/otp/speedchat/SCEmoteMenu.py b/otp/speedchat/SCEmoteMenu.py new file mode 100755 index 00000000..fd91460b --- /dev/null +++ b/otp/speedchat/SCEmoteMenu.py @@ -0,0 +1,23 @@ +from SCMenu import SCMenu +from SCEmoteTerminal import SCEmoteTerminal + +class SCEmoteMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.accept('emotesChanged', self.__emoteAccessChanged) + self.__emoteAccessChanged() + + def destroy(self): + SCMenu.destroy(self) + + def __emoteAccessChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for i in xrange(len(lt.emoteAccess)): + if lt.emoteAccess[i]: + self.append(SCEmoteTerminal(i)) diff --git a/otp/speedchat/SCEmoteTerminal.py b/otp/speedchat/SCEmoteTerminal.py new file mode 100755 index 00000000..0dc1c66e --- /dev/null +++ b/otp/speedchat/SCEmoteTerminal.py @@ -0,0 +1,97 @@ +from direct.gui.DirectGui import * +from SCTerminal import SCTerminal +from otp.otpbase.OTPLocalizer import EmoteList, EmoteWhispers +from otp.avatar import Emote +SCEmoteMsgEvent = 'SCEmoteMsg' +SCEmoteNoAccessEvent = 'SCEmoteNoAccess' + +def decodeSCEmoteWhisperMsg(emoteId, avName): + if emoteId >= len(EmoteWhispers): + return None + return EmoteWhispers[emoteId] % avName + + +class SCEmoteTerminal(SCTerminal): + + def __init__(self, emoteId): + SCTerminal.__init__(self) + self.emoteId = emoteId + if not self.__ltHasAccess(): + self.text = '?' + else: + self.text = EmoteList[self.emoteId] + + def __ltHasAccess(self): + try: + lt = base.localAvatar + return lt.emoteAccess[self.emoteId] + except: + return 0 + + def __emoteEnabled(self): + if self.isWhispering(): + return 1 + return Emote.globalEmote.isEnabled(self.emoteId) + + def finalize(self, dbArgs = {}): + if not self.isDirty(): + return + args = {} + if not self.__ltHasAccess() or not self.__emoteEnabled(): + args.update({'rolloverColor': (0, 0, 0, 0), + 'pressedColor': (0, 0, 0, 0), + 'rolloverSound': None, + 'text_fg': self.getColorScheme().getTextDisabledColor() + (1,)}) + if not self.__ltHasAccess(): + args.update({'text_align': TextNode.ACenter}) + elif not self.__emoteEnabled(): + args.update({'clickSound': None}) + self.lastEmoteEnableState = self.__emoteEnabled() + args.update(dbArgs) + SCTerminal.finalize(self, dbArgs=args) + return + + def __emoteEnableStateChanged(self): + if self.isDirty(): + self.notify.info("skipping __emoteEnableStateChanged; we're marked as dirty") + return + elif not hasattr(self, 'button'): + self.notify.error('SCEmoteTerminal is not marked as dirty, but has no button!') + btn = self.button + if self.__emoteEnabled(): + rolloverColor = self.getColorScheme().getRolloverColor() + (1,) + pressedColor = self.getColorScheme().getPressedColor() + (1,) + btn.frameStyle[DGG.BUTTON_ROLLOVER_STATE].setColor(*rolloverColor) + btn.frameStyle[DGG.BUTTON_DEPRESSED_STATE].setColor(*pressedColor) + btn.updateFrameStyle() + btn['text_fg'] = self.getColorScheme().getTextColor() + (1,) + btn['rolloverSound'] = DGG.getDefaultRolloverSound() + btn['clickSound'] = DGG.getDefaultClickSound() + else: + btn.frameStyle[DGG.BUTTON_ROLLOVER_STATE].setColor(0, 0, 0, 0) + btn.frameStyle[DGG.BUTTON_DEPRESSED_STATE].setColor(0, 0, 0, 0) + btn.updateFrameStyle() + btn['text_fg'] = self.getColorScheme().getTextDisabledColor() + (1,) + btn['rolloverSound'] = None + btn['clickSound'] = None + return + + def enterVisible(self): + SCTerminal.enterVisible(self) + if self.__ltHasAccess(): + if hasattr(self, 'lastEmoteEnableState'): + if self.lastEmoteEnableState != self.__emoteEnabled(): + self.invalidate() + if not self.isWhispering(): + self.accept(Emote.globalEmote.EmoteEnableStateChanged, self.__emoteEnableStateChanged) + + def exitVisible(self): + SCTerminal.exitVisible(self) + self.ignore(Emote.globalEmote.EmoteEnableStateChanged) + + def handleSelect(self): + if not self.__ltHasAccess(): + messenger.send(self.getEventName(SCEmoteNoAccessEvent)) + elif self.__emoteEnabled(): + SCTerminal.handleSelect(self) + messenger.send(self.getEventName(SCEmoteMsgEvent), [self.emoteId]) diff --git a/otp/speedchat/SCMenu.py b/otp/speedchat/SCMenu.py new file mode 100755 index 00000000..b041de52 --- /dev/null +++ b/otp/speedchat/SCMenu.py @@ -0,0 +1,450 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from direct.task import Task +from SCConstants import * +from direct.interval.IntervalGlobal import * +from SCObject import SCObject +from direct.showbase.PythonUtil import makeTuple +import types + +class SCMenu(SCObject, NodePath): + SpeedChatRolloverTolerance = config.GetFloat('speedchat-rollover-tolerance', 0.08) + WantFade = config.GetBool('want-speedchat-fade', 1) + FadeDuration = config.GetFloat('speedchat-fade-duration', 0.4) + SerialNum = 0 + BackgroundModelName = None + GuiModelName = None + + def __init__(self, holder = None): + SCObject.__init__(self) + self.SerialNum = SCMenu.SerialNum + SCMenu.SerialNum += 1 + node = hidden.attachNewNode('SCMenu%s' % self.SerialNum) + NodePath.__init__(self, node) + self.setHolder(holder) + self.FinalizeTaskName = 'SCMenu%s_Finalize' % self.SerialNum + self.ActiveMemberSwitchTaskName = 'SCMenu%s_SwitchActiveMember' % self.SerialNum + self.bg = loader.loadModel(self.BackgroundModelName) + + def findNodes(names, model = self.bg): + results = [] + for name in names: + for nm in makeTuple(name): + node = model.find('**/%s' % nm) + if not node.isEmpty(): + results.append(node) + break + + return results + + self.bgTop, self.bgBottom, self.bgLeft, self.bgRight, self.bgMiddle, self.bgTopLeft, self.bgBottomLeft, self.bgTopRight, self.bgBottomRight = findNodes([('top', 'top1'), + 'bottom', + 'left', + 'right', + 'middle', + 'topLeft', + 'bottomLeft', + 'topRight', + 'bottomRight']) + self.bg.reparentTo(self, -1) + self.__members = [] + self.activeMember = None + self.activeCandidate = None + self.fadeIval = None + self.width = 1 + self.inFinalize = 0 + return + + def destroy(self): + self.stopFade() + SCObject.destroy(self) + del self.bgTop + del self.bgBottom + del self.bgLeft + del self.bgRight + del self.bgMiddle + del self.bgBottomLeft + del self.bgTopRight + del self.bgBottomRight + self.bg.removeNode() + del self.bg + self.holder = None + for member in self.__members: + member.destroy() + + del self.__members + self.removeNode() + taskMgr.remove(self.FinalizeTaskName) + taskMgr.remove(self.ActiveMemberSwitchTaskName) + return + + def clearMenu(self): + while len(self): + item = self[0] + del self[0] + item.destroy() + + def rebuildFromStructure(self, structure, title = None): + self.clearMenu() + if title: + holder = self.getHolder() + if holder: + holder.setTitle(title) + self.appendFromStructure(structure) + + def appendFromStructure(self, structure): + from SpeedChatTypes import SCMenuHolder, SCStaticTextTerminal + from otp.otpbase import OTPLocalizer + + def addChildren(menu, childList): + for child in childList: + emote = None + if type(child) == type({}): + item = child.keys()[0] + emote = child[item] + child = item + if type(child) == type(0): + terminal = SCStaticTextTerminal(child) + if emote is not None: + terminal.setLinkedEmote(emote) + menu.append(terminal) + elif type(child) == type([]): + if type(child[0]) == type(''): + holderTitle = child[0] + subMenu = SCMenu() + subMenuChildren = child[1:] + else: + menuType, holderTitle = child[0], child[1] + subMenu = menuType() + subMenuChildren = child[2:] + if emote: + print 'warning: tried to link emote %s to a menu holder' % emote + holder = SCMenuHolder(holderTitle, menu=subMenu) + menu.append(holder) + addChildren(subMenu, subMenuChildren) + else: + raise 'error parsing speedchat structure. invalid child: %s' % child + + return + + addChildren(self, structure) + addChildren = None + return + + def fadeFunc(self, t): + cs = self.getColorScale() + self.setColorScale(cs[0], cs[1], cs[2], t) + + def stopFade(self): + if self.fadeIval is not None: + self.fadeIval.pause() + self.fadeIval = None + return + + def enterVisible(self): + SCObject.enterVisible(self) + self.privScheduleFinalize() + for member in self: + if member.isViewable(): + if not member.isVisible(): + member.enterVisible() + + self.childHasFaded = 0 + alreadyFaded = 0 + parentMenu = None + if self.holder is not None: + if self.holder.parentMenu is not None: + parentMenu = self.holder.parentMenu + alreadyFaded = parentMenu.childHasFaded + if SCMenu.WantFade: + if alreadyFaded: + self.fadeFunc(1.0) + else: + self.stopFade() + self.fadeIval = LerpFunctionInterval(self.fadeFunc, fromData=0.0, toData=1.0, duration=SCMenu.FadeDuration) + self.fadeIval.start() + if parentMenu is not None: + parentMenu.childHasFaded = 1 + return + + def exitVisible(self): + SCObject.exitVisible(self) + self.stopFade() + self.privCancelFinalize() + self.__cancelActiveMemberSwitch() + self.__setActiveMember(None) + for member in self: + if member.isVisible(): + member.exitVisible() + + return + + def setHolder(self, holder): + self.holder = holder + + def getHolder(self): + return self.holder + + def isTopLevel(self): + return self.holder == None + + def memberSelected(self, member): + self.__cancelActiveMemberSwitch() + self.__setActiveMember(member) + + def __setActiveMember(self, member): + if self.activeMember is member: + return + if self.activeMember is not None: + self.activeMember.exitActive() + self.activeMember = member + if self.activeMember is not None: + self.activeMember.reparentTo(self) + self.activeMember.enterActive() + return + + def memberGainedInputFocus(self, member): + self.__cancelActiveMemberSwitch() + if member is self.activeMember: + return + if self.activeMember is None or SCMenu.SpeedChatRolloverTolerance == 0 or member.posInParentMenu < self.activeMember.posInParentMenu: + self.__setActiveMember(member) + else: + + def doActiveMemberSwitch(task, self = self, member = member): + self.activeCandidate = None + self.__setActiveMember(member) + return Task.done + + minFrameRate = 1.0 / SCMenu.SpeedChatRolloverTolerance + if globalClock.getAverageFrameRate() > minFrameRate: + taskMgr.doMethodLater(SCMenu.SpeedChatRolloverTolerance, doActiveMemberSwitch, self.ActiveMemberSwitchTaskName) + self.activeCandidate = member + else: + self.__setActiveMember(member) + return + + def __cancelActiveMemberSwitch(self): + taskMgr.remove(self.ActiveMemberSwitchTaskName) + self.activeCandidate = None + return + + def memberLostInputFocus(self, member): + if member is self.activeCandidate: + self.__cancelActiveMemberSwitch() + if member is not self.activeMember: + pass + elif not member.hasStickyFocus(): + self.__setActiveMember(None) + return + + def memberViewabilityChanged(self, member): + self.invalidate() + + def invalidate(self): + SCObject.invalidate(self) + if self.isVisible(): + self.privScheduleFinalize() + + def privScheduleFinalize(self): + + def finalizeMenu(task, self = self): + self.finalize() + return Task.done + + taskMgr.remove(self.FinalizeTaskName) + taskMgr.add(finalizeMenu, self.FinalizeTaskName, priority=SCMenuFinalizePriority) + + def privCancelFinalize(self): + taskMgr.remove(self.FinalizeTaskName) + + def isFinalizing(self): + return self.inFinalize + + def finalize(self): + if not self.isDirty(): + return + self.inFinalize = 1 + SCObject.finalize(self) + visibleMembers = [] + for member in self: + if member.isViewable(): + visibleMembers.append(member) + member.reparentTo(self) + else: + member.reparentTo(hidden) + if self.activeMember is member: + self.__setActiveMember(None) + + maxWidth = 0.0 + maxHeight = 0.0 + for member in visibleMembers: + width, height = member.getMinDimensions() + maxWidth = max(maxWidth, width) + maxHeight = max(maxHeight, height) + + holder = self.getHolder() + if holder is not None: + widthToCover = holder.getMinSubmenuWidth() + maxWidth = max(maxWidth, widthToCover) + memberWidth, memberHeight = maxWidth, maxHeight + self.width = maxWidth + for i in xrange(len(visibleMembers)): + member = visibleMembers[i] + member.setPos(0, 0, -i * maxHeight) + member.setDimensions(memberWidth, memberHeight) + member.finalize() + + if len(visibleMembers) > 0: + z1 = visibleMembers[0].getZ(aspect2d) + visibleMembers[0].setZ(-maxHeight) + z2 = visibleMembers[0].getZ(aspect2d) + visibleMembers[0].setZ(0) + actualHeight = (z2 - z1) * len(visibleMembers) + bottomZ = self.getZ(aspect2d) + actualHeight + if bottomZ < -1.0: + overlap = bottomZ - -1.0 + self.setZ(aspect2d, self.getZ(aspect2d) - overlap) + if self.getZ(aspect2d) > 1.0: + self.setZ(aspect2d, 1.0) + sX = memberWidth + sZ = memberHeight * len(visibleMembers) + self.bgMiddle.setScale(sX, 1, sZ) + self.bgTop.setScale(sX, 1, 1) + self.bgBottom.setScale(sX, 1, 1) + self.bgLeft.setScale(1, 1, sZ) + self.bgRight.setScale(1, 1, sZ) + self.bgBottomLeft.setZ(-sZ) + self.bgBottom.setZ(-sZ) + self.bgTopRight.setX(sX) + self.bgRight.setX(sX) + self.bgBottomRight.setX(sX) + self.bgBottomRight.setZ(-sZ) + sB = 0.15 + self.bgTopLeft.setSx(aspect2d, sB) + self.bgTopLeft.setSz(aspect2d, sB) + self.bgBottomRight.setSx(aspect2d, sB) + self.bgBottomRight.setSz(aspect2d, sB) + self.bgBottomLeft.setSx(aspect2d, sB) + self.bgBottomLeft.setSz(aspect2d, sB) + self.bgTopRight.setSx(aspect2d, sB) + self.bgTopRight.setSz(aspect2d, sB) + self.bgTop.setSz(aspect2d, sB) + self.bgBottom.setSz(aspect2d, sB) + self.bgLeft.setSx(aspect2d, sB) + self.bgRight.setSx(aspect2d, sB) + r, g, b = self.getColorScheme().getFrameColor() + a = self.getColorScheme().getAlpha() + self.bg.setColorScale(r, g, b, a) + if self.activeMember is not None: + self.activeMember.reparentTo(self) + self.validate() + self.inFinalize = 0 + return + + def append(self, element): + if isinstance(self.__members, types.TupleType): + self.__members = list(self.__members) + self.__members.append(element) + self.privMemberListChanged(added=[element]) + + def extend(self, elements): + self += elements + + def index(self, element): + return self.__members.index(element) + + def __len__(self): + return len(self.__members) + + def __getitem__(self, index): + return self.__members[index] + + def __setitem__(self, index, value): + if isinstance(self.__members, types.TupleType): + self.__members = list(self.__members) + removedMember = self.__members[index] + self.__members[index] = value + self.privMemberListChanged(added=[value], removed=[removedMember]) + + def __delitem__(self, index): + if isinstance(self.__members, types.TupleType): + self.__members = list(self.__members) + removedMember = self.__members[index] + del self.__members[index] + self.privMemberListChanged(removed=[removedMember]) + + def __getslice__(self, i, j): + if isinstance(self.__members, types.TupleType): + self.__members = list(self.__members) + return self.__members[i:j] + + def __setslice__(self, i, j, s): + if isinstance(self.__members, types.TupleType): + self.__members = list(self.__members) + removedMembers = self.__members[i:j] + self.__members[i:j] = list(s) + self.privMemberListChanged(added=list(s), removed=removedMembers) + + def __delslice__(self, i, j): + if isinstance(self.__members, types.TupleType): + self.__members = list(self.__members) + removedMembers = self.__members[i:j] + del self.__members[i:j] + self.privMemberListChanged(removed=removedMembers) + + def __iadd__(self, other): + if isinstance(self.__members, types.TupleType): + self.__members = list(self.__members) + if isinstance(other, SCMenu): + otherMenu = other + other = otherMenu.__members + del otherMenu[:] + self.__members += list(other) + self.privMemberListChanged(added=list(other)) + return self + + def privMemberListChanged(self, added = None, removed = None): + if removed is not None: + for element in removed: + if element is self.activeMember: + self.__setActiveMember(None) + if element.getParentMenu() is self: + if element.isVisible(): + element.exitVisible() + element.setParentMenu(None) + element.reparentTo(hidden) + + if added is not None: + for element in added: + self.privAdoptSCObject(element) + element.setParentMenu(self) + + if self.holder is not None: + self.holder.updateViewability() + for i in xrange(len(self.__members)): + self.__members[i].posInParentMenu = i + + self.invalidate() + return + + def privSetSettingsRef(self, settingsRef): + SCObject.privSetSettingsRef(self, settingsRef) + for member in self: + member.privSetSettingsRef(settingsRef) + + def invalidateAll(self): + SCObject.invalidateAll(self) + for member in self: + member.invalidateAll() + + def finalizeAll(self): + SCObject.finalizeAll(self) + for member in self: + member.finalizeAll() + + def getWidth(self): + return self.width + + def __str__(self): + return '%s: menu%s' % (self.__class__.__name__, self.SerialNum) diff --git a/otp/speedchat/SCMenuHolder.py b/otp/speedchat/SCMenuHolder.py new file mode 100755 index 00000000..13ca3e79 --- /dev/null +++ b/otp/speedchat/SCMenuHolder.py @@ -0,0 +1,178 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from SCObject import SCObject +from SCElement import SCElement +from SCMenu import SCMenu +import types + +class SCMenuHolder(SCElement): + N = 0.9 + DefaultFrameColor = (0, + 0, + 0, + 1.0 - N) + del N + MenuColorScaleDown = 0.95 + + def __init__(self, title, menu = None): + SCElement.__init__(self) + self.title = title + scGui = loader.loadModel(SCMenu.GuiModelName) + self.scArrow = scGui.find('**/chatArrow') + self.menu = None + self.setMenu(menu) + return + + def destroy(self): + if self.menu is not None: + self.menu.destroy() + self.menu = None + SCElement.destroy(self) + return + + def setTitle(self, title): + self.title = title + self.invalidate() + + def getTitle(self): + return self.title + + def setMenu(self, menu): + if self.menu is not None: + self.menu.destroy() + self.menu = menu + if self.menu is not None: + self.privAdoptSCObject(self.menu) + self.menu.setHolder(self) + self.menu.reparentTo(self, 1) + self.menu.hide() + self.updateViewability() + return + + def getMenu(self): + return self.menu + + def showMenu(self): + if self.menu is not None: + cS = SCMenuHolder.MenuColorScaleDown + self.menu.setColorScale(cS, cS, cS, 1) + self.menu.enterVisible() + self.menu.show() + return + + def hideMenu(self): + if self.menu is not None: + self.menu.hide() + self.menu.exitVisible() + return + + def getMenuOverlap(self): + if self.parentMenu.isTopLevel(): + return self.getTopLevelOverlap() + else: + return self.getSubmenuOverlap() + + def getMenuOffset(self): + xOffset = self.width * (1.0 - self.getMenuOverlap()) + return Point3(xOffset, 0, 0) + + def onMouseClick(self, event): + SCElement.enterActive(self) + self.parentMenu.memberSelected(self) + + def enterActive(self): + SCElement.enterActive(self) + self.showMenu() + if hasattr(self, 'button'): + r, g, b = self.getColorScheme().getMenuHolderActiveColor() + a = self.getColorScheme().getAlpha() + self.button.frameStyle[DGG.BUTTON_READY_STATE].setColor(r, g, b, a) + self.button.updateFrameStyle() + else: + self.notify.warning('SCMenuHolder has no button (has finalize been called?).') + + def exitActive(self): + SCElement.exitActive(self) + self.hideMenu() + self.button.frameStyle[DGG.BUTTON_READY_STATE].setColor(*SCMenuHolder.DefaultFrameColor) + self.button.updateFrameStyle() + + def getDisplayText(self): + return self.title + + def updateViewability(self): + if self.menu is None: + self.setViewable(0) + return + isViewable = False + for child in self.menu: + if child.isViewable(): + isViewable = True + break + + self.setViewable(isViewable) + return + + def getMinSubmenuWidth(self): + parentMenu = self.getParentMenu() + if parentMenu is None: + myWidth, myWeight = self.getMinDimensions() + else: + myWidth = parentMenu.getWidth() + return 0.15 + myWidth * self.getMenuOverlap() + + def getMinDimensions(self): + width, height = SCElement.getMinDimensions(self) + width += 1.0 + return (width, height) + + def invalidate(self): + SCElement.invalidate(self) + if self.menu is not None: + self.menu.invalidate() + return + + def finalize(self, dbArgs = {}): + if not self.isDirty(): + return + r, g, b = self.getColorScheme().getArrowColor() + a = self.getColorScheme().getAlpha() + self.scArrow.setColorScale(r, g, b, a) + if self.menu is not None: + self.menu.setPos(self.getMenuOffset()) + if self.isActive(): + r, g, b = self.getColorScheme().getMenuHolderActiveColor() + a = self.getColorScheme().getAlpha() + frameColor = (r, + g, + b, + a) + else: + frameColor = SCMenuHolder.DefaultFrameColor + args = {'image': self.scArrow, + 'image_pos': (self.width - 0.5, 0, -self.height * 0.5), + 'frameColor': frameColor} + args.update(dbArgs) + SCElement.finalize(self, dbArgs=args) + return + + def hasStickyFocus(self): + return 1 + + def privSetSettingsRef(self, settingsRef): + SCObject.privSetSettingsRef(self, settingsRef) + if self.menu is not None: + self.menu.privSetSettingsRef(settingsRef) + return + + def invalidateAll(self): + SCObject.invalidateAll(self) + if self.menu is not None: + self.menu.invalidateAll() + return + + def finalizeAll(self): + SCObject.finalizeAll(self) + if self.menu is not None: + self.menu.finalizeAll() + return diff --git a/otp/speedchat/SCObject.py b/otp/speedchat/SCObject.py new file mode 100755 index 00000000..5b37ba74 --- /dev/null +++ b/otp/speedchat/SCObject.py @@ -0,0 +1,68 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject + +class SCObject(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('SpeedChat') + + def __init__(self): + self.settingsRef = None + self.__visible = 0 + self.__dirty = 1 + return + + def destroy(self): + self.ignoreAll() + if self.isVisible(): + self.exitVisible() + + def enterVisible(self): + self.__visible = 1 + + def exitVisible(self): + self.__visible = 0 + + def isVisible(self): + return self.__visible + + def invalidate(self): + self.__dirty = 1 + + def isDirty(self): + return self.__dirty + + def validate(self): + self.__dirty = 0 + + def finalize(self): + pass + + def getEventName(self, name): + return '%s%s' % (self.settingsRef.eventPrefix, name) + + def getColorScheme(self): + return self.settingsRef.colorScheme + + def isWhispering(self): + return self.settingsRef.whisperMode + + def getSubmenuOverlap(self): + return self.settingsRef.submenuOverlap + + def getTopLevelOverlap(self): + if self.settingsRef.topLevelOverlap is None: + return self.getSubmenuOverlap() + else: + return self.settingsRef.topLevelOverlap + return + + def privSetSettingsRef(self, settingsRef): + self.settingsRef = settingsRef + + def privAdoptSCObject(self, scObj): + scObj.privSetSettingsRef(self.settingsRef) + + def invalidateAll(self): + self.invalidate() + + def finalizeAll(self): + self.finalize() diff --git a/otp/speedchat/SCSettings.py b/otp/speedchat/SCSettings.py new file mode 100755 index 00000000..74379f8c --- /dev/null +++ b/otp/speedchat/SCSettings.py @@ -0,0 +1,14 @@ +from SCColorScheme import SCColorScheme +from otp.otpbase import OTPLocalizer + +class SCSettings: + + def __init__(self, eventPrefix, whisperMode = 0, colorScheme = None, submenuOverlap = OTPLocalizer.SCOsubmenuOverlap, topLevelOverlap = None): + self.eventPrefix = eventPrefix + self.whisperMode = whisperMode + if colorScheme is None: + colorScheme = SCColorScheme() + self.colorScheme = colorScheme + self.submenuOverlap = submenuOverlap + self.topLevelOverlap = topLevelOverlap + return diff --git a/otp/speedchat/SCStaticTextTerminal.py b/otp/speedchat/SCStaticTextTerminal.py new file mode 100755 index 00000000..64ce7266 --- /dev/null +++ b/otp/speedchat/SCStaticTextTerminal.py @@ -0,0 +1,20 @@ +from SCTerminal import SCTerminal +from otp.otpbase.OTPLocalizer import SpeedChatStaticText +SCStaticTextMsgEvent = 'SCStaticTextMsg' + +def decodeSCStaticTextMsg(textId): + if 30200 <= textId <= 30205: + textId += 20 + + return SpeedChatStaticText.get(textId, None) + +class SCStaticTextTerminal(SCTerminal): + + def __init__(self, textId): + SCTerminal.__init__(self) + self.textId = textId + self.text = SpeedChatStaticText[self.textId] + + def handleSelect(self): + SCTerminal.handleSelect(self) + messenger.send(self.getEventName(SCStaticTextMsgEvent), [self.textId]) diff --git a/otp/speedchat/SCTerminal.py b/otp/speedchat/SCTerminal.py new file mode 100755 index 00000000..ff2f1fc0 --- /dev/null +++ b/otp/speedchat/SCTerminal.py @@ -0,0 +1,168 @@ +from SCElement import SCElement +from SCObject import SCObject +from SCMenu import SCMenu +from direct.fsm.StatePush import StateVar, FunctionCall +from direct.showbase.DirectObject import DirectObject +from otp.avatar import Emote +SCTerminalSelectedEvent = 'SCTerminalSelected' +SCTerminalLinkedEmoteEvent = 'SCTerminalLinkedEmoteEvent' +SCWhisperModeChangeEvent = 'SCWhisperModeChange' + +class SCTerminal(SCElement): + + def __init__(self, linkedEmote = None): + SCElement.__init__(self) + self.setLinkedEmote(linkedEmote) + scGui = loader.loadModel(SCMenu.GuiModelName) + self.emotionIcon = scGui.find('**/emotionIcon') + self.setDisabled(False) + self.__numCharges = -1 + self._handleWhisperModeSV = StateVar(False) + self._handleWhisperModeFC = None + return + + def destroy(self): + self._handleWhisperModeSV.set(False) + if self._handleWhisperModeFC: + self._handleWhisperModeFC.destroy() + self._handleWhisperModeSV.destroy() + SCElement.destroy(self) + + def privSetSettingsRef(self, settingsRef): + SCElement.privSetSettingsRef(self, settingsRef) + if self._handleWhisperModeFC is None: + self._handleWhisperModeFC = FunctionCall(self._handleWhisperModeSVChanged, self._handleWhisperModeSV) + self._handleWhisperModeFC.pushCurrentState() + self._handleWhisperModeSV.set(self.settingsRef is not None and not self.isWhisperable()) + return + + def _handleWhisperModeSVChanged(self, handleWhisperMode): + if handleWhisperMode: + self._wmcListener = DirectObject() + self._wmcListener.accept(self.getEventName(SCWhisperModeChangeEvent), self._handleWhisperModeChange) + elif hasattr(self, '_wmcListener'): + self._wmcListener.ignoreAll() + del self._wmcListener + self.invalidate() + + def _handleWhisperModeChange(self, whisperMode): + self.invalidate() + + def handleSelect(self): + messenger.send(self.getEventName(SCTerminalSelectedEvent)) + if self.hasLinkedEmote() and self.linkedEmoteEnabled(): + messenger.send(self.getEventName(SCTerminalLinkedEmoteEvent), [self.linkedEmote]) + + def isWhisperable(self): + return True + + def getLinkedEmote(self): + return self.linkedEmote + + def setLinkedEmote(self, linkedEmote): + self.linkedEmote = linkedEmote + self.invalidate() + + def hasLinkedEmote(self): + return self.linkedEmote is not None + + def linkedEmoteEnabled(self): + if Emote.globalEmote: + return Emote.globalEmote.isEnabled(self.linkedEmote) + + def getCharges(self): + return self.__numCharges + + def setCharges(self, nCharges): + self.__numCharges = nCharges + if nCharges is 0: + self.setDisabled(True) + + def isDisabled(self): + return self.__disabled or self.isWhispering() and not self.isWhisperable() + + def setDisabled(self, bDisabled): + self.__disabled = bDisabled + + def onMouseClick(self, event): + if not self.isDisabled(): + SCElement.onMouseClick(self, event) + self.handleSelect() + + def getMinDimensions(self): + width, height = SCElement.getMinDimensions(self) + if self.hasLinkedEmote(): + width += 1.3 + return (width, height) + + def finalize(self, dbArgs = {}): + if not self.isDirty(): + return + args = {} + if self.hasLinkedEmote(): + self.lastEmoteIconColor = self.getEmoteIconColor() + self.emotionIcon.setColorScale(*self.lastEmoteIconColor) + args.update({'image': self.emotionIcon, + 'image_pos': (self.width - 0.6, 0, -self.height * 0.5)}) + if self.isDisabled(): + args.update({'rolloverColor': (0, 0, 0, 0), + 'pressedColor': (0, 0, 0, 0), + 'rolloverSound': None, + 'clickSound': None, + 'text_fg': self.getColorScheme().getTextDisabledColor() + (1,)}) + args.update(dbArgs) + SCElement.finalize(self, dbArgs=args) + return + + def getEmoteIconColor(self): + if self.linkedEmoteEnabled() and not self.isWhispering(): + r, g, b = self.getColorScheme().getEmoteIconColor() + else: + r, g, b = self.getColorScheme().getEmoteIconDisabledColor() + return (r, + g, + b, + 1) + + def updateEmoteIcon(self): + if hasattr(self, 'button'): + self.lastEmoteIconColor = self.getEmoteIconColor() + for i in xrange(self.button['numStates']): + self.button['image%s_image' % i].setColorScale(*self.lastEmoteIconColor) + + else: + self.invalidate() + + def enterVisible(self): + SCElement.enterVisible(self) + if hasattr(self, 'lastEmoteIconColor'): + if self.getEmoteIconColor() != self.lastEmoteIconColor: + self.invalidate() + + def handleWhisperModeChange(whisperMode, self = self): + if self.hasLinkedEmote(): + if self.isVisible() and not self.isWhispering(): + self.updateEmoteIcon() + + self.accept(self.getEventName(SCWhisperModeChangeEvent), handleWhisperModeChange) + + def handleEmoteEnableStateChange(self = self): + if self.hasLinkedEmote(): + if self.isVisible() and not self.isWhispering(): + self.updateEmoteIcon() + + if self.hasLinkedEmote(): + if Emote.globalEmote: + self.accept(Emote.globalEmote.EmoteEnableStateChanged, handleEmoteEnableStateChange) + + def exitVisible(self): + SCElement.exitVisible(self) + self.ignore(self.getEventName(SCWhisperModeChangeEvent)) + if Emote.globalEmote: + self.ignore(Emote.globalEmote.EmoteEnableStateChanged) + + def getDisplayText(self): + if self.getCharges() is not -1: + return self.text + ' (%s)' % self.getCharges() + else: + return self.text diff --git a/otp/speedchat/SpeedChat.py b/otp/speedchat/SpeedChat.py new file mode 100755 index 00000000..d2741dc9 --- /dev/null +++ b/otp/speedchat/SpeedChat.py @@ -0,0 +1,66 @@ +from direct.showbase.PythonUtil import boolEqual +from SpeedChatTypes import * +from SCSettings import SCSettings +from SCTerminal import SCWhisperModeChangeEvent +from otp.otpbase import OTPLocalizer + +class SpeedChat(SCMenu): + + def __init__(self, name = '', structure = None, backgroundModelName = None, guiModelName = None): + SCMenu.BackgroundModelName = backgroundModelName + SCMenu.GuiModelName = guiModelName + SCMenu.__init__(self) + self.name = name + self.settings = SCSettings(eventPrefix=self.name) + self.privSetSettingsRef(self.settings) + if structure is not None: + self.rebuildFromStructure(structure) + self._lastTransform = None + return + + def destroy(self): + if self.isVisible(): + self.exitVisible() + self._lastTransform = None + SCMenu.destroy(self) + return + + def __str__(self): + return "%s: '%s'" % (self.__class__.__name__, self.name) + + def enter(self): + self._detectTransformChange() + self.enterVisible() + + def exit(self): + self.exitVisible() + + def _detectTransformChange(self): + newTransform = self.getTransform(aspect2d) + if self._lastTransform is not None: + if newTransform != self._lastTransform: + self.invalidateAll() + self._lastTransform = newTransform + return + + def setWhisperMode(self, whisperMode): + if not boolEqual(self.settings.whisperMode, whisperMode): + self.settings.whisperMode = whisperMode + messenger.send(self.getEventName(SCWhisperModeChangeEvent), [whisperMode]) + + def setColorScheme(self, colorScheme): + self.settings.colorScheme = colorScheme + self.invalidateAll() + + def setSubmenuOverlap(self, submenuOverlap): + self.settings.submenuOverlap = submenuOverlap + self.invalidateAll() + + def setTopLevelOverlap(self, topLevelOverlap): + self.settings.topLevelOverlap = topLevelOverlap + self.invalidateAll() + + def finalizeAll(self): + self.notify.debug('finalizing entire SpeedChat tree') + self._detectTransformChange() + SCMenu.finalizeAll(self) diff --git a/otp/speedchat/SpeedChatGlobals.py b/otp/speedchat/SpeedChatGlobals.py new file mode 100755 index 00000000..d5f8438f --- /dev/null +++ b/otp/speedchat/SpeedChatGlobals.py @@ -0,0 +1,5 @@ +from SCTerminal import SCTerminalSelectedEvent +from SCTerminal import SCTerminalLinkedEmoteEvent +from SCStaticTextTerminal import SCStaticTextMsgEvent +from SCCustomTerminal import SCCustomMsgEvent +from SCEmoteTerminal import SCEmoteMsgEvent, SCEmoteNoAccessEvent diff --git a/otp/speedchat/SpeedChatTypes.py b/otp/speedchat/SpeedChatTypes.py new file mode 100755 index 00000000..229394e1 --- /dev/null +++ b/otp/speedchat/SpeedChatTypes.py @@ -0,0 +1,11 @@ +from SCObject import SCObject +from SCMenu import SCMenu +from SCElement import SCElement +from SCMenuHolder import SCMenuHolder +from SCTerminal import SCTerminal +from SCCustomMenu import SCCustomMenu +from SCEmoteMenu import SCEmoteMenu +from SCStaticTextTerminal import SCStaticTextTerminal +from SCCustomTerminal import SCCustomTerminal +from SCEmoteTerminal import SCEmoteTerminal +from SCColorScheme import SCColorScheme diff --git a/otp/speedchat/__init__.py b/otp/speedchat/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/__init__.py b/toontown/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/ai/CogPageManagerAI.py b/toontown/ai/CogPageManagerAI.py new file mode 100755 index 00000000..4de0e2e5 --- /dev/null +++ b/toontown/ai/CogPageManagerAI.py @@ -0,0 +1,56 @@ +from toontown.suit import SuitDNA +from toontown.shtiker.CogPageGlobals import * + +class CogPageManagerAI: + + def toonEncounteredCogs(self, toon, encounteredCogs, zoneId): + cogs = toon.cogs + for cog in encounteredCogs: + if toon.getDoId() in cog['activeToons']: + cogIndex = SuitDNA.suitHeadTypes.index(cog['type']) + + if cogs[cogIndex] == COG_UNSEEN: + cogs[cogIndex] = COG_BATTLED + toon.b_setCogStatus(cogs) + + def toonKilledCogs(self, toon, killedCogs, zoneId): + cogCounts = toon.cogCounts + cogs = toon.cogs + for cog in killedCogs: + if cog['isSkelecog'] or cog['isBoss'] > 0: + continue + if toon.getDoId() in cog['activeToons']: + deptIndex = SuitDNA.suitDepts.index(cog['track']) + if toon.buildingRadar[deptIndex] == 1: + continue + cogIndex = SuitDNA.suitHeadTypes.index(cog['type']) + buildingQuota = COG_QUOTAS[1][cogIndex % SuitDNA.suitsPerDept] + cogQuota = COG_QUOTAS[0][cogIndex % SuitDNA.suitsPerDept] + if cogCounts[cogIndex] >= buildingQuota: + return + cogCounts[cogIndex] += 1 + if cogCounts[cogIndex] < cogQuota: + cogs[cogIndex] = COG_DEFEATED + elif cogQuota <= cogCounts[cogIndex] < buildingQuota: + cogs[cogIndex] = COG_COMPLETE1 + else: + cogs[cogIndex] = COG_COMPLETE2 + toon.b_setCogCount(cogCounts) + toon.b_setCogStatus(cogs) + newCogRadar = toon.cogRadar + newBuildingRadar = toon.buildingRadar + for dept in xrange(len(SuitDNA.suitDepts)): + if newBuildingRadar[dept] == 1: + continue + cogRadar = 1 + buildingRadar = 1 + for cog in xrange(SuitDNA.suitsPerDept): + status = toon.cogs[dept*SuitDNA.suitsPerDept + cog] + if status != COG_COMPLETE2: + buildingRadar = 0 + if status != COG_COMPLETE1 or status != COG_COMPLETE2: + cogRadar = 0 + newCogRadar[dept] = cogRadar + newBuildingRadar[dept] = buildingRadar + toon.b_setCogRadar(newCogRadar) + toon.b_setBuildingRadar(newBuildingRadar) diff --git a/toontown/ai/CogSuitManagerAI.py b/toontown/ai/CogSuitManagerAI.py new file mode 100755 index 00000000..673d2c2e --- /dev/null +++ b/toontown/ai/CogSuitManagerAI.py @@ -0,0 +1,24 @@ +from toontown.coghq import CogDisguiseGlobals +from toontown.suit import SuitDNA + +class CogSuitManagerAI: + + def recoverPart(self, toon, factoryType, suitTrack): + if suitTrack not in SuitDNA.suitDepts: + return + + recoveredParts = [0, 0, 0, 0] + parts = toon.getCogParts() + suitTrack = SuitDNA.suitDepts.index(suitTrack) + + if CogDisguiseGlobals.isSuitComplete(parts, suitTrack): + return recoveredParts + + recoveredParts[suitTrack] = toon.giveGenericCogPart(factoryType, suitTrack) + return recoveredParts + + def removeParts(self, toon, suitDept): + parts = toon.getCogParts() + + if CogDisguiseGlobals.isSuitComplete(parts, suitDept): + toon.loseCogParts(suitDept) diff --git a/toontown/ai/DistributedBlackCatMgr.py b/toontown/ai/DistributedBlackCatMgr.py new file mode 100755 index 00000000..e5f83c7a --- /dev/null +++ b/toontown/ai/DistributedBlackCatMgr.py @@ -0,0 +1,24 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObject import DistributedObject +from toontown.toonbase import ToontownGlobals + +class DistributedBlackCatMgr(DistributedObject): + neverDisable = 1 + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBlackCatMgr') + + def announceGenerate(self): + DistributedObject.announceGenerate(self) + base.cr.blackCatMgr = self + + def delete(self): + base.cr.blackCatMgr = None + DistributedObject.delete(self) + + def requestBlackCatTransformation(self): + if not base.cr.newsManager.isHolidayRunning(ToontownGlobals.BLACK_CAT_DAY): + return + + self.sendUpdate('requestBlackCatTransformation') + + def doBlackCatTransformation(self): + base.localAvatar.getDustCloud(0.0, color=base.localAvatar.style.getBlackColor()).start() diff --git a/toontown/ai/DistributedBlackCatMgrAI.py b/toontown/ai/DistributedBlackCatMgrAI.py new file mode 100755 index 00000000..1c2c68c0 --- /dev/null +++ b/toontown/ai/DistributedBlackCatMgrAI.py @@ -0,0 +1,23 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.toon.ToonDNA import ToonDNA +from toontown.toonbase import ToontownGlobals + +class DistributedBlackCatMgrAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedBlackCatMgrAI") + + def requestBlackCatTransformation(self): + if not self.air.newsManager.isHolidayRunning(ToontownGlobals.BLACK_CAT_DAY): + return + + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + + if not av or av.getTutorialAck() or av.dna.getAnimal() != 'cat' or av.dna.headColor == 0x1a: + return + + newDNA = ToonDNA() + newDNA.makeFromNetString(av.getDNAString()) + newDNA.updateToonProperties(armColor=26, legColor=26, headColor=26) + taskMgr.doMethodLater(1.0, lambda task: av.b_setDNAString(newDNA.makeNetString()), 'transform-%d' % avId) + self.sendUpdateToAvatarId(avId, 'doBlackCatTransformation', []) diff --git a/toontown/ai/DistributedEffectMgr.py b/toontown/ai/DistributedEffectMgr.py new file mode 100644 index 00000000..37d04fe5 --- /dev/null +++ b/toontown/ai/DistributedEffectMgr.py @@ -0,0 +1,49 @@ +from direct.distributed.DistributedObject import DistributedObject +from otp.speedchat import SpeedChatGlobals +import HolidayGlobals, time + +class DistributedEffectMgr(DistributedObject): + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + self.nextTime = 0 + + def delete(self): + self.ignoreAll() + DistributedObject.delete(self) + + def setHoliday(self, holiday): + self.holiday = holiday + self.accept(SpeedChatGlobals.SCStaticTextMsgEvent, self.__saidPhrase) + + def __saidPhrase(self, phraseId): + if not self.cr.newsManager.isHolidayRunning(self.holiday): + return + + currentTime = time.time() + + if self.nextTime > currentTime: + return + + holidayInfo = HolidayGlobals.getHoliday(self.holiday) + + if 'speedchatIndexes' not in holidayInfo or phraseId not in holidayInfo['speedchatIndexes']: + return + + if 'effectDelay' in holidayInfo: + self.nextTime = currentTime + holidayInfo['effectDelay'] + + self.sendUpdate('requestEffect') + + def effectDone(self, amount): + holidayInfo = HolidayGlobals.getHoliday(self.holiday) + + self.cr.newsManager.broadcastHoliday(holidayInfo, 'effectMessage') + + if 'scavengerHunt' in holidayInfo: + type = holidayInfo['scavengerHunt'] + + if type == HolidayGlobals.TRICK_OR_TREAT: + base.localAvatar.trickOrTreatTargetMet(amount) + elif type == HolidayGlobals.WINTER_CAROLING: + base.localAvatar.winterCarolingTargetMet(amount) diff --git a/toontown/ai/DistributedEffectMgrAI.py b/toontown/ai/DistributedEffectMgrAI.py new file mode 100644 index 00000000..54dff1f7 --- /dev/null +++ b/toontown/ai/DistributedEffectMgrAI.py @@ -0,0 +1,43 @@ +from direct.distributed.DistributedObjectAI import DistributedObjectAI +import HolidayGlobals +import datetime + +class DistributedEffectMgrAI(DistributedObjectAI): + + def __init__(self, air, holiday, effectId): + DistributedObjectAI.__init__(self, air) + self.holiday = holiday + self.effectId = effectId + + def getHoliday(self): + return self.holiday + + def requestEffect(self): + if not self.air.newsManager.isHolidayRunning(self.holiday): + return + + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + + if not av: + return + + holidayInfo = HolidayGlobals.getHoliday(self.holiday) + expireTime = int(HolidayGlobals.getServerTime(HolidayGlobals.getEndDate(holidayInfo) + datetime.timedelta(days=1)) / 60) + + if 'scavengerHunt' in holidayInfo: + scavengerHunt = av.getScavengerHunt() + + if self.zoneId in scavengerHunt: + self.sendUpdateToAvatarId(avId, 'effectDone', [0]) + else: + scavengerHunt.append(self.zoneId) + av.b_setScavengerHunt(scavengerHunt) + av.addMoney(HolidayGlobals.CAROLING_REWARD) + self.sendUpdateToAvatarId(avId, 'effectDone', [HolidayGlobals.CAROLING_REWARD]) + + if len(scavengerHunt) == HolidayGlobals.SCAVENGER_HUNT_LOCATIONS: + av.b_setCheesyEffect(self.effectId, 0, expireTime) + else: + av.b_setCheesyEffect(self.effectId, 0, expireTime) + self.sendUpdateToAvatarId(avId, 'effectDone', [0]) diff --git a/toontown/ai/DistributedPolarPlaceEffectMgr.py b/toontown/ai/DistributedPolarPlaceEffectMgr.py new file mode 100755 index 00000000..cb3ca514 --- /dev/null +++ b/toontown/ai/DistributedPolarPlaceEffectMgr.py @@ -0,0 +1,32 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.interval.IntervalGlobal import * +from otp.speedchat import SpeedChatGlobals +from toontown.toonbase import TTLocalizer + +class DistributedPolarPlaceEffectMgr(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPolarPlaceEffectMgr') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + DistributedPolarPlaceEffectMgr.notify.debug('announceGenerate') + self.accept(SpeedChatGlobals.SCStaticTextMsgEvent, self.phraseSaid) + + def phraseSaid(self, phraseId): + helpPhrase = 104 + if phraseId == helpPhrase: + self.addPolarPlaceEffect() + + def delete(self): + self.ignore(SpeedChatGlobals.SCStaticTextMsgEvent) + DistributedObject.DistributedObject.delete(self) + + def addPolarPlaceEffect(self): + DistributedPolarPlaceEffectMgr.notify.debug('addResitanceEffect') + av = base.localAvatar + self.sendUpdate('addPolarPlaceEffect', []) + msgTrack = Sequence(Func(av.setSystemMessage, 0, TTLocalizer.PolarPlaceEffect1), Wait(2), Func(av.setSystemMessage, 0, TTLocalizer.PolarPlaceEffect2), Wait(4), Func(av.setSystemMessage, 0, TTLocalizer.PolarPlaceEffect3)) + msgTrack.start() diff --git a/toontown/ai/DistributedPolarPlaceEffectMgrAI.py b/toontown/ai/DistributedPolarPlaceEffectMgrAI.py new file mode 100755 index 00000000..9e6ba6b0 --- /dev/null +++ b/toontown/ai/DistributedPolarPlaceEffectMgrAI.py @@ -0,0 +1,26 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm.FSM import FSM +import time + +class DistributedPolarPlaceEffectMgrAI(DistributedObjectAI, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPolarPlaceEffectMgrAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + FSM.__init__(self, 'ResistanceFSM') + self.air = air + + def enterOff(self): + self.requestDelete() + + def addPolarPlaceEffect(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + + if not av: + return + + expireTime = int((time.time()/60) + 0.5) + 60 + av.b_setCheesyEffect(13, 3000, expireTime) diff --git a/toontown/ai/DistributedReportMgr.py b/toontown/ai/DistributedReportMgr.py new file mode 100755 index 00000000..704cc3b5 --- /dev/null +++ b/toontown/ai/DistributedReportMgr.py @@ -0,0 +1,17 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject + +class DistributedReportMgr(DistributedObject.DistributedObject): + neverDisable = 1 + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedReportMgr') + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + base.cr.reportMgr = self + + def delete(self): + base.cr.reportMgr = None + DistributedObject.DistributedObject.delete(self) + + def sendReport(self, avId, category): + self.sendUpdate('sendReport', [avId, category]) diff --git a/toontown/ai/DistributedReportMgrAI.py b/toontown/ai/DistributedReportMgrAI.py new file mode 100755 index 00000000..7fd4724d --- /dev/null +++ b/toontown/ai/DistributedReportMgrAI.py @@ -0,0 +1,43 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.uberdog.ClientServicesManagerUD import executeHttpRequestAndLog +import ReportGlobals, threading, time + +# TODO: FIX + +''' +THREADING.TIMER CAUSES CONTROL C NOT TO WORK, AND FOR THE AI NOT TO DIE +''' + +class DistributedReportMgrAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedReportMgrAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.reports = [] + self.interval = config.GetInt('report-interval', 600) + #self.scheduleReport() + + #def scheduleReport(self): + # threading.Timer(self.interval, self.sendAllReports).start() + + def sendReport(self, avId, category): + if not ReportGlobals.isValidCategoryName(category) or not len(str(avId)) == 9: + return + + reporter = self.air.doId2do.get(self.air.getAvatarIdFromSender()) + + if not reporter or reporter.isReported(avId): + return + + timestamp = int(round(time.time() * 1000)) + self.reports.append('%s|%s|%s|%s' % (timestamp, reporter.doId, avId, category)) + + def sendAllReports(self): + #self.scheduleReport() + + if not self.reports or config.GetString('accountdb-type', 'developer') != 'remote': + return + + executeHttpRequestAndLog('report', reports=','.join(self.reports)) + self.reports = [] diff --git a/toontown/ai/DistributedResistanceEmoteMgr.py b/toontown/ai/DistributedResistanceEmoteMgr.py new file mode 100755 index 00000000..4c8be832 --- /dev/null +++ b/toontown/ai/DistributedResistanceEmoteMgr.py @@ -0,0 +1,35 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.interval.IntervalGlobal import * +from otp.speedchat import SpeedChatGlobals +from otp.otpbase.OTPLocalizerEnglish import EmoteFuncDict +from toontown.toonbase import TTLocalizer +RESIST_INDEX = EmoteFuncDict['Resistance Salute'] + +class DistributedResistanceEmoteMgr(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedResistanceEmoteMgr') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + DistributedResistanceEmoteMgr.notify.debug('announceGenerate') + self.accept(SpeedChatGlobals.SCStaticTextMsgEvent, self.phraseSaid) + + def phraseSaid(self, phraseId): + helpPhrase = 513 + if phraseId == helpPhrase: + self.addResistanceEmote() + + def delete(self): + self.ignore(SpeedChatGlobals.SCStaticTextMsgEvent) + DistributedObject.DistributedObject.delete(self) + + def addResistanceEmote(self): + DistributedResistanceEmoteMgr.notify.debug('addResitanceEmote') + av = base.localAvatar + if not av.emoteAccess[RESIST_INDEX]: + self.sendUpdate('addResistanceEmote', []) + msgTrack = Sequence(Wait(1), Func(av.setSystemMessage, 0, TTLocalizer.ResistanceEmote1), Wait(3), Func(av.setSystemMessage, 0, TTLocalizer.ResistanceEmote2), Wait(4), Func(av.setSystemMessage, 0, TTLocalizer.ResistanceEmote3)) + msgTrack.start() diff --git a/toontown/ai/DistributedResistanceEmoteMgrAI.py b/toontown/ai/DistributedResistanceEmoteMgrAI.py new file mode 100755 index 00000000..d0ed02a9 --- /dev/null +++ b/toontown/ai/DistributedResistanceEmoteMgrAI.py @@ -0,0 +1,24 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm.FSM import FSM +from otp.otpbase.OTPLocalizerEnglish import EmoteFuncDict + +class DistributedResistanceEmoteMgrAI(DistributedObjectAI, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedResistanceEmoteMgrAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + FSM.__init__(self, 'ResistanceFSM') + self.air = air + + def enterOff(self): + self.requestDelete() + + def addResistanceEmote(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if not av: return + RESIST_INDEX = EmoteFuncDict['Resistance Salute'] + av.emoteAccess[RESIST_INDEX] = 1 + av.d_setEmoteAccess(av.emoteAccess) diff --git a/toontown/ai/FishManagerAI.py b/toontown/ai/FishManagerAI.py new file mode 100755 index 00000000..124a2e83 --- /dev/null +++ b/toontown/ai/FishManagerAI.py @@ -0,0 +1,125 @@ +import random + +from otp.ai.MagicWordGlobal import * +from toontown.fishing import FishGlobals +from toontown.fishing.FishBase import FishBase +from toontown.toonbase import TTLocalizer, ToontownGlobals +from toontown.uberdog import TopToonsGlobals + + +class FishManagerAI: + def __init__(self, air): + self.air = air + self.ponds = {} + self.requestedFish = {} + + def creditFishTank(self, av): + totalFish = len(av.fishCollection) + trophies = int(totalFish / 10) + curTrophies = len(av.fishingTrophies) + av.addMoney(av.fishTank.getTotalValue()) + av.b_setFishTank([], [], []) + if trophies > curTrophies: + av.b_setMaxHp(av.getMaxHp() + trophies - curTrophies) + av.toonUp(av.getMaxHp()) + av.b_setFishingTrophies(range(trophies)) + return True + return False + + def generateCatch(self, av, zoneId): + if len(av.fishTank) >= av.getMaxFishTank(): + return [FishGlobals.OverTankLimit, 0, 0, 0] + rand = random.random() * 100.0 + for cutoff in FishGlobals.SortedProbabilityCutoffs: + if rand <= cutoff: + itemType = FishGlobals.ProbabilityDict[cutoff] + break + if av.doId in self.requestedFish: + genus, species = self.requestedFish[av.doId] + weight = FishGlobals.getRandomWeight(genus, species) + fish = FishBase(genus, species, weight) + fishType = av.fishCollection.collectFish(fish) + if fishType == FishGlobals.COLLECT_NEW_ENTRY: + itemType = FishGlobals.FishItemNewEntry + elif fishType == FishGlobals.COLLECT_NEW_RECORD: + itemType = FishGlobals.FishItemNewRecord + else: + itemType = FishGlobals.FishItem + netlist = av.fishCollection.getNetLists() + av.d_setFishCollection(netlist[0], netlist[1], netlist[2]) + av.fishTank.addFish(fish) + netlist = av.fishTank.getNetLists() + av.d_setFishTank(netlist[0], netlist[1], netlist[2]) + del self.requestedFish[av.doId] + av.addStat(ToontownGlobals.STAT_FISH) + return [itemType, genus, species, weight] + if itemType == FishGlobals.FishItem: + success, genus, species, weight = FishGlobals.getRandomFishVitals(zoneId, av.getFishingRod()) + fish = FishBase(genus, species, weight) + fishType = av.fishCollection.collectFish(fish) + if fishType == FishGlobals.COLLECT_NEW_ENTRY: + itemType = FishGlobals.FishItemNewEntry + elif fishType == FishGlobals.COLLECT_NEW_RECORD: + itemType = FishGlobals.FishItemNewRecord + else: + itemType = FishGlobals.FishItem + netlist = av.fishCollection.getNetLists() + av.d_setFishCollection(netlist[0], netlist[1], netlist[2]) + av.fishTank.addFish(fish) + netlist = av.fishTank.getNetLists() + av.d_setFishTank(netlist[0], netlist[1], netlist[2]) + messenger.send('topToonsManager-event', [av.doId, TopToonsGlobals.CAT_FISH, 1]) + av.addStat(ToontownGlobals.STAT_FISH) + return [itemType, genus, species, weight] + elif itemType == FishGlobals.BootItem: + return [itemType, 0, 0, 0] + elif itemType == FishGlobals.QuestItem: + itemId = simbase.air.questManager.toonCaughtFishingItem(av) + + if itemId != -1: + return [itemType, itemId, 0, 0] + else: + success, genus, species, weight = FishGlobals.getRandomFishVitals(zoneId, av.getFishingRod()) + fish = FishBase(genus, species, weight) + fishType = av.fishCollection.collectFish(fish) + if fishType == FishGlobals.COLLECT_NEW_ENTRY: + itemType = FishGlobals.FishItemNewEntry + elif fishType == FishGlobals.COLLECT_NEW_RECORD: + itemType = FishGlobals.FishItemNewRecord + else: + itemType = FishGlobals.FishItem + netlist = av.fishCollection.getNetLists() + av.d_setFishCollection(netlist[0], netlist[1], netlist[2]) + av.fishTank.addFish(fish) + netlist = av.fishTank.getNetLists() + av.d_setFishTank(netlist[0], netlist[1], netlist[2]) + messenger.send('topToonsManager-event', [av.doId, TopToonsGlobals.CAT_FISH, 1]) + av.addStat(ToontownGlobals.STAT_FISH) + return [itemType, genus, species, weight] + else: + money = FishGlobals.Rod2JellybeanDict[av.getFishingRod()] + av.addMoney(money) + return [itemType, money, 0, 0] + + +@magicWord(category=CATEGORY_ADMINISTRATOR, types=[str]) +def fish(fishName): + """ + Register/unregister the fish to be caught on the invoker. + """ + invoker = spellbook.getInvoker() + if fishName.lower() == 'remove': + if invoker.doId not in simbase.air.fishManager.requestedFish: + return 'You have not requested a fish.' + del simbase.air.fishManager.requestedFish[invoker.doId] + return 'Removed your fish request.' + + for genus, species in TTLocalizer.FishSpeciesNames: + for name in species: + if fishName.lower() != name.lower(): + continue + fishRequest = (genus, species.index(name)) + simbase.air.fishManager.requestedFish[invoker.doId] = fishRequest + return 'A request for the fish %s was saved.' % name + + return "Couldn't find a fish with the name %s!" % fishName diff --git a/toontown/ai/HalloweenHolidayDecorator.py b/toontown/ai/HalloweenHolidayDecorator.py new file mode 100755 index 00000000..ce5cbbde --- /dev/null +++ b/toontown/ai/HalloweenHolidayDecorator.py @@ -0,0 +1,107 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import HolidayDecorator +from toontown.toonbase import ToontownGlobals +from toontown.safezone import Playground +from toontown.town import Street +from toontown.estate import Estate +from pandac.PandaModules import Vec4, TransformState, NodePath, TransparencyAttrib + +class HalloweenHolidayDecorator(HolidayDecorator.HolidayDecorator): + notify = DirectNotifyGlobal.directNotify.newCategory('HalloweenHolidayDecorator') + + def __init__(self): + HolidayDecorator.HolidayDecorator.__init__(self) + + def __checkStreetValidity(self): + if hasattr(base.cr.playGame, 'getPlace') and base.cr.playGame.getPlace() and isinstance(base.cr.playGame.getPlace(), Street.Street) and hasattr(base.cr.playGame.getPlace(), 'loader') and base.cr.playGame.getPlace().loader and hasattr(base.cr.playGame.getPlace().loader, 'geom') and base.cr.playGame.getPlace().loader.geom: + return True + else: + if hasattr(base.cr.playGame, 'getPlace') and base.cr.playGame.getPlace(): + self.notify.debug('Failed Street Check %s' % base.cr.playGame.getPlace()) + else: + self.notify.debug('Failed Street Check') + return False + + def __checkHoodValidity(self): + if (hasattr(base.cr.playGame, 'getPlace') and + base.cr.playGame.getPlace() and + (isinstance(base.cr.playGame.getPlace(), Playground.Playground) or + isinstance(base.cr.playGame.getPlace(), Estate.Estate)) and + hasattr(base.cr.playGame.getPlace(), 'loader') and + base.cr.playGame.getPlace().loader and + hasattr(base.cr.playGame.getPlace().loader, 'hood') and + base.cr.playGame.getPlace().loader.hood and + hasattr(base.cr.playGame.getPlace().loader.hood, 'loader') and + base.cr.playGame.getPlace().loader.hood.loader and + hasattr(base.cr.playGame.getPlace().loader.hood.loader, 'geom') and + base.cr.playGame.getPlace().loader.hood.loader.geom): + return True + else: + if hasattr(base.cr.playGame, 'getPlace') and base.cr.playGame.getPlace(): + self.notify.debug('Failed Hood Check %s' % base.cr.playGame.getPlace()) + else: + self.notify.debug('Failed Hood Check') + return False + + def __startSpookySky(self): + if (self.__checkHoodValidity() or self.__checkStreetValidity()) and hasattr(base.cr.playGame.hood, 'sky') and base.cr.playGame.hood.sky: + base.cr.playGame.hood.startSpookySky() + + def __stopSpookySky(self): + if (self.__checkHoodValidity() or self.__checkStreetValidity()) and hasattr(base.cr.playGame.hood, 'sky') and base.cr.playGame.hood.sky: + base.cr.playGame.hood.endSpookySky() + + def decorate(self): + self.updateHoodDNAStore() + self.swapIval = self.getSwapVisibleIval() + if self.swapIval: + self.swapIval.start() + + def __lightDecorationOn__(): + place = base.cr.playGame.getPlace() + if hasattr(place, 'halloweenLights'): + if not self.__checkStreetValidity(): + return + else: + place.halloweenLights = place.loader.geom.findAllMatches('**/*light*') + place.halloweenLights += place.loader.geom.findAllMatches('**/*lamp*') + place.halloweenLights += place.loader.geom.findAllMatches('**/prop_snow_tree*') + for light in place.halloweenLights: + light.setColorScaleOff(0) + + elif not self.__checkHoodValidity(): + return + else: + place.loader.hood.halloweenLights = place.loader.hood.loader.geom.findAllMatches('**/*light*') + place.loader.hood.halloweenLights += place.loader.hood.loader.geom.findAllMatches('**/*lamp*') + place.loader.hood.halloweenLights += place.loader.hood.loader.geom.findAllMatches('**/prop_snow_tree*') + for light in place.loader.hood.halloweenLights: + light.setColorScaleOff(0) + + if not base.cr.newsManager.isHolidayRunning(ToontownGlobals.HALLOWEEN): + return + if (self.__checkHoodValidity() or self.__checkStreetValidity()) and hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'sky') and base.cr.playGame.hood.sky: + preShow = Sequence(Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 1.5, Vec4(1, 1, 1, 0.25)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(0.55, 0.55, 0.65, 1)), Func(__lightDecorationOn__)), Func(self.__startSpookySky)) + preShow.start() + distributedEstate = base.cr.doFind('DistributedEstate') + if distributedEstate: + distributedEstate.loadWitch() + + def undecorate(self): + if (self.__checkHoodValidity() or self.__checkStreetValidity()) and hasattr(base.cr.playGame.hood, 'sky') and base.cr.playGame.hood.sky: + postShow = Sequence(Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 1.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(1, 1, 1, 1))), Func(self.__stopSpookySky)) + postShow.start() + distributedEstate = base.cr.doFind('DistributedEstate') + if distributedEstate: + distributedEstate.unloadWitch() + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.HALLOWEEN): + self.decorate() + return + storageFile = base.cr.playGame.hood.storageDNAFile + if storageFile: + loadDNAFile(self.dnaStore, storageFile, CSDefault) + self.swapIval = self.getSwapVisibleIval() + if self.swapIval: + self.swapIval.start() diff --git a/toontown/ai/HolidayDecorator.py b/toontown/ai/HolidayDecorator.py new file mode 100755 index 00000000..6913ee95 --- /dev/null +++ b/toontown/ai/HolidayDecorator.py @@ -0,0 +1,64 @@ +from direct.interval.IntervalGlobal import Parallel, Sequence, Func, Wait +from pandac.PandaModules import Vec4, TransformState, NodePath, TransparencyAttrib + +class HolidayDecorator: + + def __init__(self): + self.dnaStore = base.cr.playGame.dnaStore + self.swapIval = None + return + + def exit(self): + if self.swapIval is not None and self.swapIval.isPlaying(): + self.swapIval.finish() + return + + def decorate(self): + self.updateHoodDNAStore() + self.swapIval = self.getSwapVisibleIval() + if self.swapIval: + self.swapIval.start() + + def undecorate(self): + storageFile = base.cr.playGame.hood.storageDNAFile + if storageFile: + loadDNAFile(self.dnaStore, storageFile, CSDefault) + self.swapIval = self.getSwapVisibleIval() + if self.swapIval: + self.swapIval.start() + + def updateHoodDNAStore(self): + hood = base.cr.playGame.hood + + for key, value in self.holidayStorageDNADict.iteritems(): + if base.cr.newsManager.isHolidayRunning(key): + for storageFile in value: + loadDNAFile(self.dnaStore, storageFile, CSDefault) + + def getSwapVisibleIval(self, wait = 5.0, tFadeOut = 3.0, tFadeIn = 3.0): + loader = base.cr.playGame.hood.loader + npl = render.findAllMatches('**/=DNARoot=holiday_prop;+s') + p = Parallel() + for i in xrange(npl.getNumPaths()): + np = npl.getPath(i) + np.setTransparency(TransparencyAttrib.MDual, 1) + if not np.hasTag('DNACode'): + continue + dnaCode = np.getTag('DNACode') + dnaNode = self.dnaStore.findNode(dnaCode) + if dnaNode.isEmpty(): + continue + newNP = dnaNode.copyTo(np.getParent()) + newNP.setTag('DNARoot', 'holiday_prop') + newNP.setTag('DNACode', dnaCode) + newNP.setColorScale(1, 1, 1, 0) + newNP.setTransparency(TransparencyAttrib.MDual, 1) + if np.hasTag('transformIndex'): + index = int(np.getTag('transformIndex')) + transform = loader.holidayPropTransforms.get(index, TransformState.makeIdentity()) + newNP.setTransform(NodePath(), transform) + newNP.setTag('transformIndex', `index`) + s = Sequence(Wait(wait), np.colorScaleInterval(tFadeOut, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='easeInOut'), Func(np.detachNode), Func(np.clearTransparency), newNP.colorScaleInterval(tFadeOut, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0), blendType='easeInOut'), Func(newNP.clearTransparency), Func(newNP.clearColorScale)) + p.append(s) + + return p diff --git a/toontown/ai/HolidayGlobals.py b/toontown/ai/HolidayGlobals.py new file mode 100644 index 00000000..4910516b --- /dev/null +++ b/toontown/ai/HolidayGlobals.py @@ -0,0 +1,145 @@ +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.parties import ToontownTimeZone +import calendar, datetime + +TIME_ZONE = ToontownTimeZone.ToontownTimeZone() +TRICK_OR_TREAT = 0 +WINTER_CAROLING = 1 +CAROLING_REWARD = 100 +SCAVENGER_HUNT_LOCATIONS = 6 + +Holidays = { + ToontownGlobals.GRAND_PRIX: { + 'weekDay': 0, + 'startMessage': TTLocalizer.CircuitRaceStart, + 'ongoingMessage': TTLocalizer.CircuitRaceOngoing, + 'endMessage': TTLocalizer.CircuitRaceEnd + }, + ToontownGlobals.FISH_BINGO: { + 'weekDay': 2, + 'startMessage': TTLocalizer.FishBingoStart, + 'ongoingMessage': TTLocalizer.FishBingoOngoing, + 'endMessage': TTLocalizer.FishBingoEnd + }, + ToontownGlobals.SILLY_SATURDAY: { + 'weekDay': 5, + 'startMessage': TTLocalizer.SillySaturdayStart, + 'ongoingMessage': TTLocalizer.SillySaturdayOngoing, + 'endMessage': TTLocalizer.SillySaturdayEnd + }, + ToontownGlobals.BLACK_CAT_DAY: { + 'startMonth': 10, + 'startDay': 31, + 'endMonth': 10, + 'endDay': 31, + 'startMessage': TTLocalizer.BlackCatHolidayStart, + 'ongoingMessage': TTLocalizer.BlackCatHolidayStart, + 'endMessage': TTLocalizer.BlackCatHolidayEnd + }, + ToontownGlobals.APRIL_TOONS_WEEK: { + 'startMonth': 4, + 'startDay': 1, + 'endMonth': 4, + 'endDay': 7, + 'startMessage': TTLocalizer.AprilToonsWeekStart, + 'ongoingMessage': TTLocalizer.AprilToonsWeekStart, + 'endMessage': TTLocalizer.AprilToonsWeekEnd + }, + ToontownGlobals.IDES_OF_MARCH: { + 'startMonth': 3, + 'startDay': 14, + 'endMonth': 3, + 'endDay': 20, + 'startMessage': TTLocalizer.IdesOfMarchStart, + 'ongoingMessage': TTLocalizer.IdesOfMarchStart, + 'endMessage': TTLocalizer.IdesOfMarchEnd, + 'speedchatIndexes': [30450], # It's easy to be green! + 'effectMessage': TTLocalizer.GreenToonEffectMsg, + 'effectDelay': 10 + }, + ToontownGlobals.CHRISTMAS: { + 'startMonth': 12, + 'startDay': 14, + 'endMonth': 1, + 'endDay': 4, + 'startMessage': TTLocalizer.WinterCarolingStart, + 'ongoingMessage': TTLocalizer.WinterCarolingStart, + 'endMessage': TTLocalizer.WinterCarolingEnd, + 'speedchatIndexes': range(30200, 30206), + 'effectDelay': 15, + 'scavengerHunt': WINTER_CAROLING + }, + ToontownGlobals.HALLOWEEN: { + 'startMonth': 10, + 'startDay': 13, + 'endMonth': 10, + 'endDay': 31, + 'startMessage': TTLocalizer.TrickOrTreatStart, + 'ongoingMessage': TTLocalizer.TrickOrTreatStart, + 'endMessage': TTLocalizer.TrickOrTreatEnd, + 'speedchatIndexes': [10003], + 'effectDelay': 15, + 'scavengerHunt': TRICK_OR_TREAT + }, + ToontownGlobals.SUMMER_FIREWORKS: { + 'startMonth': 6, + 'startDay': 30, + 'endMonth': 7, + 'endDay': 15, + 'startMessage': TTLocalizer.SummerFireworksStart, + 'ongoingMessage': TTLocalizer.SummerFireworksStart, + 'endMessage': TTLocalizer.SummerFireworksEnd + }, + ToontownGlobals.NEW_YEAR_FIREWORKS: { + 'startMonth': 12, + 'startDay': 31, + 'endMonth': 1, + 'endDay': 7, + 'startMessage': TTLocalizer.NewYearFireworksStart, + 'ongoingMessage': TTLocalizer.NewYearFireworksStart, + 'endMessage': TTLocalizer.NewYearFireworksEnd + }, + ToontownGlobals.VALENTOONS_DAY: { + 'startMonth': 2, + 'startDay': 9, + 'endMonth': 2, + 'endDay': 16, + 'startMessage': TTLocalizer.ValentinesDayStart, + 'ongoingMessage': TTLocalizer.ValentinesDayStart, + 'endMessage': TTLocalizer.ValentinesDayEnd + } +} + +def getHoliday(id): + return Holidays.get(id, {}) + +def getServerTime(date): + epoch = datetime.datetime.fromtimestamp(0, TIME_ZONE) + delta = date - epoch + + return delta.total_seconds() + +def getStartDate(holiday, rightNow=None): + if not rightNow: + rightNow = datetime.datetime.now() + + startMonth = holiday['startMonth'] if 'startMonth' in holiday else rightNow.month + startDay = holiday['startDay'] if 'startDay' in holiday else (rightNow.day if 'weekDay' in holiday else calendar.monthrange(rightNow.year, startMonth)[0]) + startDate = datetime.datetime(rightNow.year, startMonth, startDay, tzinfo=TIME_ZONE) + + return startDate + +def getEndDate(holiday, rightNow=None): + if not rightNow: + rightNow = datetime.datetime.now() + + endMonth = holiday['endMonth'] if 'endMonth' in holiday else rightNow.month + endDay = holiday['endDay'] if 'endDay' in holiday else (rightNow.day if 'weekDay' in holiday else calendar.monthrange(rightNow.year, endMonth)[1]) + endYear = rightNow.year + + if 'startMonth' in holiday and holiday['startMonth'] > endMonth: + endYear += 1 + + endDate = datetime.datetime(endYear, endMonth, endDay, tzinfo=TIME_ZONE) + + return endDate diff --git a/toontown/ai/NewsManager.py b/toontown/ai/NewsManager.py new file mode 100755 index 00000000..13e5d7e0 --- /dev/null +++ b/toontown/ai/NewsManager.py @@ -0,0 +1,99 @@ +from direct.distributed.DistributedObject import DistributedObject +from direct.interval.IntervalGlobal import * +from toontown.battle import SuitBattleGlobals +from toontown.estate import Estate +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals, TTLocalizer +from toontown.suit import SuitDNA +import HolidayGlobals + +class NewsManager(DistributedObject): + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + self.invading = False + self.activeHolidays = [] + base.localAvatar.inventory.setInvasionCreditMultiplier(1) + base.cr.newsManager = self + + def delete(self): + self.cr.newsManager = None + DistributedObject.delete(self) + + def isHolidayRunning(self, id): + return id in self.activeHolidays + + def setActiveHolidays(self, ids): + for id in ids: + self.startHoliday(id, True) + + def broadcastHoliday(self, holiday, type): + if type in holiday: + base.localAvatar.setSystemMessage(0, holiday[type]) + + def startHoliday(self, id, ongoing=False): + if id in self.activeHolidays or id not in HolidayGlobals.Holidays: + return + + holiday = HolidayGlobals.getHoliday(id) + + self.activeHolidays.append(id) + self.broadcastHoliday(holiday, 'ongoingMessage' if ongoing else 'startMessage') + self.startSpecialHoliday(id) + + def endHoliday(self, id): + if id not in self.activeHolidays or id not in HolidayGlobals.Holidays: + return + + holiday = HolidayGlobals.getHoliday(id) + + self.activeHolidays.remove(id) + self.broadcastHoliday(holiday, 'endMessage') + self.endSpecialHoliday(id) + + def startSpecialHoliday(self, id): + if id == ToontownGlobals.APRIL_TOONS_WEEK: + if isinstance(base.cr.playGame.getPlace(), Estate.Estate): + base.localAvatar.startAprilToonsControls() + + base.localAvatar.chatMgr.chatInputSpeedChat.addAprilToonsMenu() + elif id == ToontownGlobals.IDES_OF_MARCH: + base.localAvatar.chatMgr.chatInputSpeedChat.addIdesOfMarchMenu() + elif id == ToontownGlobals.HALLOWEEN: + base.localAvatar.chatMgr.chatInputSpeedChat.addHalloweenMenu() + elif id == ToontownGlobals.CHRISTMAS: + base.localAvatar.chatMgr.chatInputSpeedChat.addWinterMenu() + + def endSpecialHoliday(self, id): + if id == ToontownGlobals.APRIL_TOONS_WEEK: + if isinstance(base.cr.playGame.getPlace(), Estate.Estate): + base.localAvatar.stopAprilToonsControls() + + base.localAvatar.chatMgr.chatInputSpeedChat.removeAprilToonsMenu() + elif id == ToontownGlobals.IDES_OF_MARCH: + base.localAvatar.chatMgr.chatInputSpeedChat.removeIdesOfMarchMenu() + elif id == ToontownGlobals.HALLOWEEN: + base.localAvatar.chatMgr.chatInputSpeedChat.removeHalloweenMenu() + elif id == ToontownGlobals.CHRISTMAS: + base.localAvatar.chatMgr.chatInputSpeedChat.removeWinterMenu() + + def setInvasionStatus(self, msgType, suitType, remaining, flags): + if msgType not in ToontownGlobals.SuitInvasions: + return + + if suitType in SuitDNA.suitHeadTypes: + attributes = SuitBattleGlobals.SuitAttributes[suitType] + suitNames = {'singular': attributes['name'], 'plural': attributes['pluralname']} + elif suitType in SuitDNA.suitDepts: + suitNames = {'singular': SuitDNA.getDeptFullname(suitType), 'plural': SuitDNA.getDeptFullnameP(suitType)} + else: + return + + track = Sequence() + base.localAvatar.inventory.setInvasionCreditMultiplier(1 if msgType in ToontownGlobals.EndingInvasions else ToontownBattleGlobals.getInvasionMultiplier()) + + for i, message in enumerate(ToontownGlobals.SuitInvasions[msgType]): + track.append(Wait(5 if i else 1)) + track.append(Func(base.localAvatar.setSystemMessage, 0, (TTLocalizer.SuitInvasionPrefix + message) % suitNames)) + + track.start() diff --git a/toontown/ai/NewsManagerAI.py b/toontown/ai/NewsManagerAI.py new file mode 100755 index 00000000..f5a5f657 --- /dev/null +++ b/toontown/ai/NewsManagerAI.py @@ -0,0 +1,151 @@ +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.distributed.ClockDelta import globalClockDelta +from direct.task import Task +from otp.ai.MagicWordGlobal import * +from toontown.effects.DistributedFireworkShowAI import DistributedFireworkShowAI +from toontown.effects import FireworkShows +from toontown.toonbase import ToontownGlobals +from toontown.parties import PartyGlobals +import HolidayGlobals +import datetime, random + +class NewsManagerAI(DistributedObjectAI): + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.activeHolidays = [] + self.fireworkTasks = [] + + def announceGenerate(self): + DistributedObjectAI.announceGenerate(self) + self.__checkHolidays() + self.accept('avatarEntered', self.__handleAvatarEntered) + taskMgr.doMethodLater(15, self.__checkHolidays, 'holidayCheckTask') + + def delete(self): + DistributedObjectAI.delete(self) + self.deleteTasks() + + def deleteTasks(self): + taskMgr.remove('holidayCheckTask') + self.deleteFireworkTasks() + + def deleteFireworkTasks(self): + for task in self.fireworkTasks: + taskMgr.remove(task) + self.fireworkTasks = [] + + def __handleAvatarEntered(self, av): + avId = av.getDoId() + + if self.air.suitInvasionManager.getInvading(): + self.air.suitInvasionManager.notifyInvasionBulletin(avId) + + self.sendUpdateToAvatarId(avId, 'setActiveHolidays', [self.activeHolidays]) + + def getActiveHolidays(self): + return self.activeHolidays + + def __checkHolidays(self, task=None): + date = datetime.datetime.now(HolidayGlobals.TIME_ZONE) + + for id in HolidayGlobals.Holidays: + holiday = HolidayGlobals.Holidays[id] + running = self.isHolidayRunning(id) + + if self.isHolidayInRange(holiday, date): + if not running: + self.startHoliday(id) + elif running: + self.endHoliday(id) + + return Task.again + + def isHolidayInRange(self, holiday, date): + if 'weekDay' in holiday: + return holiday['weekDay'] == date.weekday() + else: + return HolidayGlobals.getStartDate(holiday) <= date <= HolidayGlobals.getEndDate(holiday) + + def isHolidayRunning(self, *args): + for id in args: + if id in self.activeHolidays: + return True + + def startHoliday(self, id): + if id in self.activeHolidays or id not in HolidayGlobals.Holidays: + return False + + self.activeHolidays.append(id) + self.startSpecialHoliday(id) + self.sendUpdate('startHoliday', [id]) + return True + + def endHoliday(self, id): + if id not in self.activeHolidays or id not in HolidayGlobals.Holidays: + return False + + self.activeHolidays.remove(id) + self.endSpecialHoliday(id) + self.sendUpdate('endHoliday', [id]) + return True + + def startSpecialHoliday(self, id): + if id == ToontownGlobals.FISH_BINGO or id == ToontownGlobals.SILLY_SATURDAY: + messenger.send('startBingo') + elif id in [ToontownGlobals.SUMMER_FIREWORKS, ToontownGlobals.NEW_YEAR_FIREWORKS]: + self.fireworkTasks.append(taskMgr.doMethodLater((60 - datetime.datetime.now().minute) * 60, self.startFireworkTask, 'initialFireworkTask-%s' % id, extraArgs=[id])) + + def endSpecialHoliday(self, id): + if id == ToontownGlobals.FISH_BINGO or id == ToontownGlobals.SILLY_SATURDAY: + messenger.send('stopBingo') + elif id in [ToontownGlobals.SUMMER_FIREWORKS, ToontownGlobals.NEW_YEAR_FIREWORKS]: + self.deleteFireworkTasks() + + def startFireworkTask(self, id, task=None): + self.startFireworks(id) + self.fireworkTasks.append(taskMgr.doMethodLater(3600, self.startFireworks, 'fireworkTask-%s' % id, extraArgs=[id])) + + def startFireworks(self, type, task=None): + maxShow = len(FireworkShows.shows.get(type, [])) - 1 + + for hood in self.air.hoods: + if hood.zoneId == ToontownGlobals.GolfZone: + continue + + fireworkShow = DistributedFireworkShowAI(self.air) + fireworkShow.generateWithRequired(hood.zoneId) + fireworkShow.b_startShow(type, random.randint(0, maxShow), globalClockDelta.getRealNetworkTime()) + + return Task.again + + def isGrandPrixRunning(self): + return self.isHolidayRunning(ToontownGlobals.SILLY_SATURDAY, ToontownGlobals.GRAND_PRIX) or True + +@magicWord(category=CATEGORY_PROGRAMMER) +def newsShutdown(): + """ + Shutdown the news manager tasks. + """ + simbase.air.newsManager.deleteTasks() + return 'News manager shut down!' + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def startHoliday(holiday): + """ + Start a holiday. + """ + if simbase.air.newsManager.startHoliday(holiday): + return 'Started holiday %s!' % holiday + + return 'Holiday %s is already running!' % holiday + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def stopHoliday(holiday): + """ + Stop a holiday. + """ + if simbase.air.newsManager.endHoliday(holiday): + return 'Stopped holiday %s!' % holiday + + return 'Holiday %s is not running!' % holiday \ No newline at end of file diff --git a/toontown/ai/PromotionManagerAI.py b/toontown/ai/PromotionManagerAI.py new file mode 100755 index 00000000..0c36b767 --- /dev/null +++ b/toontown/ai/PromotionManagerAI.py @@ -0,0 +1,63 @@ +from otp.ai.AIBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +import random +from toontown.suit import SuitDNA +from toontown.coghq import CogDisguiseGlobals +from toontown.toonbase.ToontownBattleGlobals import getInvasionMultiplier +MeritMultiplier = 0.5 + +class PromotionManagerAI: + notify = DirectNotifyGlobal.directNotify.newCategory('PromotionManagerAI') + + def __init__(self, air): + self.air = air + + def getPercentChance(self): + return 100.0 + + def recoverMerits(self, av, cogList, zoneId, multiplier = 1, extraMerits = None, addInvasion = True): + avId = av.getDoId() + meritsRecovered = [0, 0, 0, 0] + if extraMerits is None: + extraMerits = [0, 0, 0, 0] + if addInvasion and self.air.suitInvasionManager.getInvading(): + multiplier *= getInvasionMultiplier() + for i in xrange(len(extraMerits)): + if CogDisguiseGlobals.isSuitComplete(av.getCogParts(), i): + meritsRecovered[i] += extraMerits[i] + self.notify.debug('recoverMerits: extra merits = %s' % extraMerits[i]) + self.notify.debug('recoverMerits: multiplier = %s' % multiplier) + for cogDict in cogList: + dept = SuitDNA.suitDepts.index(cogDict['track']) + if avId in cogDict['activeToons']: + if CogDisguiseGlobals.isSuitComplete(av.getCogParts(), SuitDNA.suitDepts.index(cogDict['track'])): + self.notify.debug('recoverMerits: checking against cogDict: %s' % cogDict) + rand = random.random() * 100 + if rand <= self.getPercentChance() and not cogDict['isVirtual']: + merits = cogDict['level'] * MeritMultiplier + merits = int(round(merits)) + if cogDict['hasRevives']: + merits *= 2 + merits = merits * multiplier + merits = int(round(merits)) + meritsRecovered[dept] += merits + self.notify.debug('recoverMerits: merits = %s' % merits) + else: + self.notify.debug('recoverMerits: virtual cog!') + if meritsRecovered != [0, 0, 0, 0]: + actualCounted = [0, 0, 0, 0] + merits = av.getCogMerits() + for i in xrange(len(meritsRecovered)): + max = CogDisguiseGlobals.getTotalMerits(av, i) + if max: + if merits[i] + meritsRecovered[i] <= max: + actualCounted[i] = meritsRecovered[i] + merits[i] += meritsRecovered[i] + else: + actualCounted[i] = max - merits[i] + merits[i] = max + av.b_setCogMerits(merits) + if reduce(lambda x, y: x + y, actualCounted): + self.air.writeServerEvent('merits', avId, '%s|%s|%s|%s' % tuple(actualCounted)) + self.notify.debug('recoverMerits: av %s recovered merits %s' % (avId, actualCounted)) + return meritsRecovered diff --git a/toontown/ai/QuestManagerAI.py b/toontown/ai/QuestManagerAI.py new file mode 100755 index 00000000..c7a50c95 --- /dev/null +++ b/toontown/ai/QuestManagerAI.py @@ -0,0 +1,644 @@ +from toontown.toon.DistributedNPCSpecialQuestGiverAI import DistributedNPCSpecialQuestGiverAI +from toontown.building import FADoorCodes +from otp.ai.MagicWordGlobal import * +from toontown.hood import ZoneUtil +from toontown.quest import Quests +from toontown.uberdog import TopToonsGlobals +from toontown.toonbase import ToontownGlobals + +QuestIdIndex = 0 +QuestFromNpcIdIndex = 1 +QuestToNpcIdIndex = 2 +QuestRewardIdIndex = 3 +QuestProgressIndex = 4 + +class QuestManagerAI: + notify = directNotify.newCategory('QuestManagerAI') + + def __init__(self, air): + self.air = air + + def requestInteract(self, avId, npc): + # Get the avatar. + av = self.air.doId2do.get(avId) + if not av: + return + + avQuestPocketSize = av.getQuestCarryLimit() + avQuests = av.getQuests() + + needTrackTask = False + fakeTier = 0 + + avTrackProgress = av.getTrackProgress() + if avTrackProgress[0] == -1: + avQuestTier = av.getRewardTier() + if avQuestTier < Quests.DG_TIER and avQuestTier > Quests.DD_TIER: + fakeTier = Quests.DD_TIER + needTrackTask = True + elif avQuestTier < Quests.BR_TIER and avQuestTier > Quests.MM_TIER: + fakeTier = Quests.MM_TIER + needTrackTask = True + elif avQuestTier < Quests.DL_TIER and avQuestTier > Quests.BR_TIER: + fakeTier = Quests.BR_TIER + needTrackTask = True + + # Iterate through their quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i:i + 5] + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questClass = Quests.getQuest(questId) + if questClass: + completeStatus = questClass.getCompletionStatus(av, questDesc, npc) + else: + continue + + # If the quest is a DeliverGagQuest, add the gags. + if isinstance(questClass, Quests.DeliverGagQuest): + # Check if it's the required NPC. + if npc.npcId == toNpcId: + # Add progress. + questList = [] + progress = questClass.removeGags(av) + + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i:i + 5] + if questDesc[QuestIdIndex] == questId: + questDesc[QuestProgressIndex] += progress + if questDesc[QuestProgressIndex] >= questClass.getNumGags(): + completeStatus = Quests.COMPLETE + questList.append(questDesc) + av.b_setQuests(questList) + + if completeStatus != Quests.COMPLETE: + continue + + # If they've completed a quest. + if completeStatus == Quests.COMPLETE: + # ToonUp the toon to max health. + messenger.send('topToonsManager-event', [av.doId, TopToonsGlobals.CAT_TASKS, 1]) + av.toonUp(av.maxHp) + + # If it's a TrackChoiceQuest then present their track choices. + if isinstance(questClass, Quests.TrackChoiceQuest): + npc.presentTrackChoice(avId, questId, questClass.getChoices()) + break + # If there is another part to this quest then give them that. + if Quests.getNextQuest(questId, npc, av)[0] != Quests.NA: + self.nextQuest(av, npc, questId) + if avId in self.air.tutorialManager.avId2fsm: + self.air.tutorialManager.avId2fsm[avId].demand('Tunnel') + break + else: + # The toon has completed this quest. Give them a reward! + npc.completeQuest(avId, questId, rewardId) + self.completeQuest(av, questId) + av.addStat(ToontownGlobals.STAT_TASKS) + break + else: + # They haven't completed any quests so we have to give them choices. + # If they've got a full pouch then reject them. + if (len(avQuests) == avQuestPocketSize*5): + npc.rejectAvatar(avId) + return + elif isinstance(npc, DistributedNPCSpecialQuestGiverAI): + # Don't display choices. Force a choice. + self.tutorialQuestChoice(avId, npc) + return + else: + #Present quest choices. + if needTrackTask: + choices = self.npcGiveTrackChoice(av, fakeTier) + else: + choices = self.avatarQuestChoice(av, npc) + if choices != []: + npc.presentQuestChoice(avId, choices) + else: + npc.rejectAvatar(avId) + + def npcGiveTrackChoice(self, av, tier): + trackQuest = Quests.chooseTrackChoiceQuest(tier, av) + return [(trackQuest, 400, Quests.ToonHQ)] + + def avatarQuestChoice(self, av, npc): + # Get the best quests for an avatar/npc. + return Quests.chooseBestQuests(av.getRewardTier(), npc, av) + + def avatarChoseQuest(self, avId, npc, questId, rewardId, toNpcId): + # Get the avatar. + av = self.air.doId2do.get(avId) + if not av: + return + + # Get the npcIds + fromNpcId = npc.npcId if npc else 0 + if toNpcId == 0: + toNpcId = Quests.getQuestToNpcId(questId) + + # Add the quest to the avatars list. + transformedRewardId = Quests.transformReward(rewardId, av) + av.addQuest([questId, fromNpcId, toNpcId, rewardId, 0], transformedRewardId) + + if not npc: + return + + # Remove the tasks for timeout. + taskMgr.remove(npc.uniqueName('clearMovie')) + + # Assign the quest. + npc.assignQuest(avId, questId, rewardId, toNpcId) + + def avatarChoseTrack(self, avId, npc, pendingTrackQuest, trackId): + # Get the avatar. + av = self.air.doId2do.get(avId) + if not av: + return + + # Remove the tasks for timeout. + taskMgr.remove(npc.uniqueName('clearMovie')) + + # Show the completion movie and remove the task. + npc.completeQuest(avId, pendingTrackQuest, Quests.getRewardIdFromTrackId(trackId)) + self.completeQuest(av, pendingTrackQuest) + + # Set their track their working on. + av.b_setTrackProgress(trackId, 0) + + def avatarCancelled(self, npcId): + # Get the NPC. + npc = self.air.doId2do.get(npcId) + if not npc: + return + + # Remove the task for timeout. + taskMgr.remove(npc.uniqueName('clearMovie')) + + def nextQuest(self, av, npc, questId): + # Get the next QuestId and toNpcId. + nextQuestId, toNpcId = Quests.getNextQuest(questId, npc, av) + + # Get the avatars current quests. + avQuests = av.getQuests() + questList = [] + + # Iterate through their current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i:i + 5] + + if questDesc[QuestIdIndex] == questId: + questDesc[QuestIdIndex] = nextQuestId + questDesc[QuestToNpcIdIndex] = toNpcId + questDesc[QuestProgressIndex] = 0 + questList.append(questDesc) + + # Show the quest movie and set their quests. + npc.incompleteQuest(av.doId, nextQuestId, Quests.QUEST, toNpcId) + av.b_setQuests(questList) + + def completeQuest(self, av, completeQuestId): + #Get the avatars current quests. + avQuests = av.getQuests() + + # Iterate through their current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i:i + 5] + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questClass = Quests.getQuest(questId) + + if questId == completeQuestId: + av.removeQuest(questId) + self.giveReward(av, questId, rewardId) + self.avatarConsiderProgressTier(av) + break + + def giveReward(self, av, questId, rewardId): + # Give the reward. + rewardClass = Quests.getReward(rewardId) + if rewardClass is None: + self.notify.warning('rewardClass was None for rewardId: %s.' % rewardId) + else: + rewardClass.sendRewardAI(av) + + # Add the rewardId to the avatars rewardHistory. + rewardTier, rewardHistory = av.getRewardHistory() + transformedRewardId = Quests.transformReward(rewardId, av) + if transformedRewardId != rewardId: + rewardHistory.append(rewardId) + + av.b_setRewardHistory(rewardTier, rewardHistory) + + def avatarConsiderProgressTier(self, av): + # Get the avatars current tier. + currentTier = av.getRewardTier() + + # Check if they have all required rewards. + if Quests.avatarHasAllRequiredRewards(av, currentTier): + if currentTier != Quests.ELDER_TIER: + currentTier += 1 + av.b_setRewardHistory(currentTier, []) + + def tutorialQuestChoice(self, avId, npc): + # Get the avatar. + av = self.air.doId2do.get(avId) + if not av: + return + + # Get the possible quest choices and force the player to choose it. + choices = self.avatarQuestChoice(av, npc) + quest = choices[0] + + self.avatarChoseQuest(avId, npc, quest[0], quest[1], 0) + + # Are we in the tutorial speaking to Tutorial Tom? + if avId in self.air.tutorialManager.avId2fsm: + if av.getRewardHistory()[0] == 0: + self.air.tutorialManager.avId2fsm[avId].demand('Battle') + + def toonRodeTrolleyFirstTime(self, av): + # Toon played a minigame. + self.toonPlayedMinigame(av, []) + + def toonPlayedMinigame(self, av, toons): + # Get the avatars current quests. + avQuests = av.getQuests() + questList = [] + + # Iterate through their current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i : i + 5] + questClass = Quests.getQuest(questDesc[QuestIdIndex]) + if isinstance(questClass, Quests.TrolleyQuest): + questDesc[QuestProgressIndex] = 1 + questList.append(questDesc) + + av.b_setQuests(questList) + + def toonMadeFriend(self, avId): + # Get the avatar. + av = self.air.doId2do.get(avId) + if not av: + return + + # Get the avatars current quests. + avQuests = av.getQuests() + questList = [] + + # Iterate through their current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i : i + 5] + questClass = Quests.getQuest(questDesc[QuestIdIndex]) + if isinstance(questClass, Quests.FriendQuest) and questClass.getCompletionStatus(av, questDesc) == Quests.INCOMPLETE: + questDesc[QuestProgressIndex] += 1 + questList.append(questDesc) + + av.b_setQuests(questList) + + def toonCalledClarabelle(self, av): + avQuests = av.getQuests() + questList = [] + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i : i + 5] + questClass = Quests.getQuest(questDesc[QuestIdIndex]) + if isinstance(questClass, Quests.PhoneQuest) and questClass.getCompletionStatus(av, questDesc) == Quests.INCOMPLETE: + questDesc[QuestProgressIndex] += 1 + questList.append(questDesc) + av.b_setQuests(questList) + + def toonMadeNPCFriend(self, av, count, method): + avQuests = av.getQuests() + questList = [] + + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i : i + 5] + questClass = Quests.getQuest(questDesc[QuestIdIndex]) + + if isinstance(questClass, Quests.RescueQuest) and questClass.getCompletionStatus(av, questDesc) == Quests.INCOMPLETE and questClass.isMethodMatch(method): + questDesc[QuestProgressIndex] += count + questList.append(questDesc) + + av.b_setQuests(questList) + + def toonUsedPhone(self, avId): + # Get the avatar. + av = self.air.doId2do.get(avId) + if not av: + return + + # Get the avatars current quests. + avQuests = av.getQuests() + questList = [] + + # Iterate through their current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i : i + 5] + questClass = Quests.getQuest(questDesc[QuestIdIndex]) + if isinstance(questClass, Quests.PhoneQuest): + questDesc[QuestProgressIndex] += 1 + questList.append(questDesc) + + av.b_setQuests(questList) + + def toonCaughtFishingItem(self, av): + # Get the avatars current quests. + avQuests = av.getQuests() + fishingItem = -1 + questList = [] + + # Iterate through their current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i : i + 5] + questClass = Quests.getQuest(questDesc[QuestIdIndex]) + if fishingItem != -1: + questList.append(questDesc) + continue + if isinstance(questClass, Quests.RecoverItemQuest): + if not hasattr(questClass, 'getItem'): + questList.append(questDesc) + continue + if questClass.getHolder() == Quests.AnyFish: + if not questClass.getCompletionStatus(av, questDesc) == Quests.COMPLETE: + baseChance = questClass.getPercentChance() + amountRemaining = questClass.getNumItems() - questDesc[QuestProgressIndex] + chance = Quests.calcRecoverChance(amountRemaining, baseChance) + if chance >= baseChance: + questDesc[QuestProgressIndex] += 1 + fishingItem = questClass.getItem() + questList.append(questDesc) + + av.b_setQuests(questList) + return fishingItem + + def hasTailorClothingTicket(self, av, npc): + # Get the avatars current quests. + avQuests = av.getQuests() + + # Iterate through their current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i : i + 5] + questClass = Quests.getQuest(questDesc[QuestIdIndex]) + if isinstance(questClass, Quests.DeliverItemQuest): + if questClass.getCompletionStatus(av, questDesc, npc) == Quests.COMPLETE: + # They have a clothing ticket. + return 1 + return 0 + + def removeClothingTicket(self, av, npc): + # Get the avatars current quests. + avQuests = av.getQuests() + + # Iterate through their current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i : i + 5] + questClass = Quests.getQuest(questDesc[QuestIdIndex]) + if isinstance(questClass, Quests.DeliverItemQuest): + if questClass.getCompletionStatus(av, questDesc, npc) == Quests.COMPLETE: + av.removeQuest(questDesc[QuestIdIndex]) + break + + def recoverItems(self, av, suitsKilled, taskZoneId): + # Get the avatars current quests. + avQuests = av.getQuests() + questList = [] + recoveredItems = [] + unrecoveredItems = [] + taskZoneId = ZoneUtil.getBranchZone(taskZoneId) + + # Iterate through the avatars current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i : i + 5] + questClass = Quests.getQuest(questDesc[QuestIdIndex]) + + # Check if the Quest isnt already complete + if questClass.getCompletionStatus(av, questDesc) == Quests.INCOMPLETE: + + # Check if we are dealing with a RecoverItemQuest + if isinstance(questClass, Quests.RecoverItemQuest): + + # Iterate through all the Cogs that were killed in the battle + for suit in suitsKilled: + + # Because the RecoveItemQuest class doesn't have a + # function to see if a Cog counts. We need to manually + # check if the Cog is valid for the Quest + if (questClass.getHolder() == Quests.Any) or \ + (questClass.getHolderType() == 'type' and \ + questClass.getHolder() == suit['type']) or \ + (questClass.getHolderType() == 'track' and \ + questClass.getHolder() == suit['track']) or \ + (questClass.getHolderType() == 'level' and \ + questClass.getHolder() <= suit['level']): + + # It looks like the Cog was valid. Lets see if they + # found what they were looking for. + baseChance = questClass.getPercentChance() + amountRemaining = questClass.getNumItems() - questDesc[QuestProgressIndex] + chance = Quests.calcRecoverChance(amountRemaining, baseChance) + + # They found it! Give them their reward! + if chance >= baseChance: + questDesc[QuestProgressIndex] += 1 + recoveredItems.append(questClass.getItem()) + + # Better luck next time :( + else: + unrecoveredItems.append(questClass.getItem()) + + questList.append(questDesc) + + av.b_setQuests(questList) + + return (recoveredItems, unrecoveredItems) + + def toonKilledBuilding(self, av, type, difficulty, floors, zoneId, cogdo): + # Get the avatars current quests. + messenger.send('topToonsManager-event', [av.doId, TopToonsGlobals.CAT_BLDG, 1]) + avQuests = av.getQuests() + questList = [] + zoneId = ZoneUtil.getBranchZone(zoneId) + + # Iterate through the avatars current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i : i + 5] + questClass = Quests.getQuest(questDesc[QuestIdIndex]) + if isinstance(questClass, Quests.BuildingQuest) and questClass.getCompletionStatus(av, questDesc) == Quests.INCOMPLETE: + if questClass.isLocationMatch(zoneId) and questClass.doesBuildingTypeCount(type): + if questClass.isCogdo() == cogdo: + if floors >= questClass.getNumFloors(): + questDesc[QuestProgressIndex] += 1 + questList.append(questDesc) + + av.b_setQuests(questList) + + def toonDefeatedFactory(self, av, factoryId): + # Get the avatars current quests. + avQuests = av.getQuests() + questList = [] + + # Iterate through the avatars current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i : i + 5] + questClass = Quests.getQuest(questDesc[QuestIdIndex]) + if isinstance(questClass, Quests.FactoryQuest): + if questClass.doesFactoryCount(av, factoryId): + questDesc[QuestProgressIndex] += 1 + questList.append(questDesc) + + av.b_setQuests(questList) + + def toonDefeatedMint(self, av, mintId): + # Get the avatars current quests. + avQuests = av.getQuests() + questList = [] + + # Iterate through the avatars current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i : i + 5] + questClass = Quests.getQuest(questDesc[QuestIdIndex]) + if isinstance(questClass, Quests.MintQuest): + if questClass.doesMintCount(av, mintId): + questDesc[QuestProgressIndex] += 1 + questList.append(questDesc) + + av.b_setQuests(questList) + + def toonDefeatedStage(self, av, stageId): + pass + + def toonKilledCogs(self, av, suitsKilled, zoneId): + # Get the avatar's current quests. + messenger.send('topToonsManager-event', [av.doId, TopToonsGlobals.CAT_COGS, len(suitsKilled)]) + avQuests = av.getQuests() + questList = [] + + # Iterate through the avatar's current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i : i + 5] + questClass = Quests.getQuest(questDesc[QuestIdIndex]) + + # Check if they are doing a cog quest + if isinstance(questClass, Quests.CogQuest): + # Check if the cog counts... + for suit in suitsKilled: + if questClass.doesCogCount(av.doId, suit, zoneId): + # Looks like the cog counts! + if questClass.getCompletionStatus(av, questDesc) != Quests.COMPLETE: + questDesc[QuestProgressIndex] += 1 + + # Add the quest to the questList + questList.append(questDesc) + + # Update the avatar's quests + av.b_setQuests(questList) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[str, int, int]) +def quests(command, arg0=0, arg1=0): + invoker = spellbook.getInvoker() + currQuests = invoker.getQuests() + currentQuestIds = [] + + for i in xrange(0, len(currQuests), 5): + currentQuestIds.append(currQuests[i]) + + pocketSize = invoker.getQuestCarryLimit() + carrying = len(currQuests) / 5 + canCarry = False + + if (carrying < pocketSize): + canCarry = True + + if command == 'clear': + invoker.b_setQuests([]) + return 'Cleared quests' + elif command == 'clearHistory': + invoker.d_setQuestHistory([]) + return 'Cleared quests history' + elif command == 'add': + if arg0: + if canCarry: + if arg0 in Quests.QuestDict.keys(): + quest = Quests.QuestDict[arg0] + + simbase.air.questManager.avatarChoseQuest(invoker.doId, None, arg0, quest[5], quest[4]) + return 'Added QuestID %s'%(arg0) + else: + return 'Invalid QuestID %s'%(arg0) + else: + return 'Cannot take anymore quests' + else: + return 'add needs 1 argument.' + elif command == 'remove': + if arg0: + if arg0 in currentQuestIds: + invoker.removeQuest(arg0) + return 'Removed QuestID %s'%(arg0) + elif arg0 < pocketSize and arg0 > 0: + if len(currentQuestIds) <= arg0: + questIdToRemove = currentQuestIds[arg0 - 1] + invoker.removeQuest(questIdToRemove) + return 'Removed quest from slot %s'%(arg0) + else: + return 'Invalid quest slot' + else: + return 'Cannot remove quest %s'%(arg0) + else: + return 'remove needs 1 argument.' + elif command == 'list': + if arg0: + if arg0 > 0 and arg0 <= pocketSize: + start = (arg0 -1) * 5 + questDesc = currQuests[start : start + 5] + return 'QuestDesc in slot %s: %s.'%(arg0, questDesc) + else: + return 'Invalid quest slot %s.'%(arg0) + else: + return 'CurrentQuests: %s'%(currentQuestIds) + elif command == 'bagSize': + if arg0 > 0 and arg0 < 5: + invoker.b_setQuestCarryLimit(arg0) + return 'Set carry limit to %s'%(arg0) + else: + return 'Argument 0 must be between 1 and 4.' + elif command == 'progress': + if arg0 and arg1: + if arg0 > 0 and arg0 <= pocketSize: + questList = [] + wantedQuestId = currentQuestIds[arg0 - 1] + + for i in xrange(0, len(currQuests), 5): + questDesc = currQuests[i : i + 5] + + if questDesc[0] == wantedQuestId: + questDesc[4] = arg1 + + questList.append(questDesc) + + invoker.b_setQuests(questList) + return 'Set quest slot %s progress to %s'%(arg0, arg1) + elif arg0 in Quests.QuestDict.keys(): + if arg0 in currentQuestIds: + questList = [] + + for i in xrange(0, len(currQuests), 5): + questDesc = currQuests[i : i + 5] + + if questDesc[0] == arg0: + questDesc[4] = arg1 + + questList.append(questDesc) + + invoker.b_setQuests(questList) + return 'Set QuestID %s progress to %s'%(arg0, arg1) + else: + return 'Cannot progress QuestID: %s.'%(arg0) + else: + return 'Invalid quest or slot id' + else: + return 'progress needs 2 arguments.' + elif command == 'tier': + if arg0: + invoker.b_setRewardHistory(arg0, invoker.getRewardHistory()[1]) + return 'Set tier to %s'%(arg0) + else: + return 'tier needs 1 argument.' + else: + return 'Invalid first argument.' diff --git a/toontown/ai/ReportGlobals.py b/toontown/ai/ReportGlobals.py new file mode 100755 index 00000000..67e5f668 --- /dev/null +++ b/toontown/ai/ReportGlobals.py @@ -0,0 +1,10 @@ +categories = ['foul-language', 'greening', 'rude-behavior', 'bad-name', 'hacking'] + +def isValidCategory(value): + return value < len(categories) + +def isValidCategoryName(value): + return value in categories + +def getCategory(value): + return categories[value] diff --git a/toontown/ai/ServiceStart.py b/toontown/ai/ServiceStart.py new file mode 100755 index 00000000..4899cc4c --- /dev/null +++ b/toontown/ai/ServiceStart.py @@ -0,0 +1,76 @@ +import __builtin__ + + +__builtin__.process = 'ai' + + +# Temporary hack patch: +__builtin__.__dict__.update(__import__('pandac.PandaModules', fromlist=['*']).__dict__) +from direct.extensions_native import HTTPChannel_extensions + +import sys, os +sys.path.append( + os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../../dependencies" + ) + ) +) + +import argparse +import gc + +# Panda3D 1.10.0 is 63. +gc.disable() + +parser = argparse.ArgumentParser() +parser.add_argument('--base-channel', help='The base channel that the server may use.') +parser.add_argument('--max-channels', help='The number of channels the server may use.') +parser.add_argument('--stateserver', help="The control channel of this AI's designated State Server.") +parser.add_argument('--district-name', help="What this AI Server's district will be named.") +parser.add_argument('--astron-ip', help="The IP address of the Astron Message Director to connect to.") +parser.add_argument('--eventlogger-ip', help="The IP address of the Astron Event Logger to log to.") +parser.add_argument('config', nargs='*', default=['dependencies/config/general.prc', 'dependencies/config/release/dev.prc'], help="PRC file(s) to load.") +args = parser.parse_args() + +for prc in args.config: + loadPrcFile(prc) + +if os.path.isfile('dependencies/config/local.prc'): + loadPrcFile('dependencies/config/local.prc') + +localconfig = '' +if args.base_channel: localconfig += 'air-base-channel %s\n' % args.base_channel +if args.max_channels: localconfig += 'air-channel-allocation %s\n' % args.max_channels +if args.stateserver: localconfig += 'air-stateserver %s\n' % args.stateserver +if args.district_name: localconfig += 'district-name %s\n' % args.district_name +if args.astron_ip: localconfig += 'air-connect %s\n' % args.astron_ip +if args.eventlogger_ip: localconfig += 'eventlog-host %s\n' % args.eventlogger_ip +loadPrcFileData('Command-line', localconfig) + +from otp.ai.AIBaseGlobal import * + +from toontown.ai.ToontownAIRepository import ToontownAIRepository +simbase.air = ToontownAIRepository(config.GetInt('air-base-channel', 401000000), + config.GetInt('air-stateserver', 4002), + config.GetString('district-name', 'Devhaven')) +host = config.GetString('air-connect', '127.0.0.1') +port = 7100 +if ':' in host: + host, port = host.split(':', 1) + port = int(port) +simbase.air.connect(host, port) + +gc.enable() + +try: + run() +except SystemExit: + raise +except Exception: + info = describeException() + + simbase.air.writeServerEvent('ai-exception', avId=simbase.air.getAvatarIdFromSender(), accId=simbase.air.getAccountIdFromSender(), exception=info) + + raise diff --git a/toontown/ai/ToonBarrier.py b/toontown/ai/ToonBarrier.py new file mode 100755 index 00000000..14e18c66 --- /dev/null +++ b/toontown/ai/ToonBarrier.py @@ -0,0 +1,83 @@ +from otp.ai.AIBase import * +from direct.task import Task +from direct.showbase import DirectObject +import random + +class ToonBarrier(DirectObject.DirectObject): + notify = directNotify.newCategory('ToonBarrier') + + def __init__(self, name, uniqueName, avIdList, timeout, clearedFunc = None, timeoutFunc = None, doneFunc = None): + self.name = name + self.uniqueName = uniqueName + '-Barrier' + self.avIdList = avIdList[:] + self.pendingToons = self.avIdList[:] + self.timeout = timeout + self.clearedFunc = clearedFunc + self.timeoutFunc = timeoutFunc + self.doneFunc = doneFunc + if len(self.pendingToons) == 0: + self.notify.debug('%s: barrier with empty list' % self.uniqueName) + self.active = 0 + if self.clearedFunc: + self.clearedFunc() + if self.doneFunc: + self.doneFunc(self.avIdList) + return + self.taskName = self.uniqueName + '-Timeout' + origTaskName = self.taskName + while taskMgr.hasTaskNamed(self.taskName): + self.taskName = origTaskName + '-' + str(random.randint(0, 10000)) + + taskMgr.doMethodLater(self.timeout, self.__timerExpired, self.taskName) + for avId in self.avIdList: + event = simbase.air.getAvatarExitEvent(avId) + self.acceptOnce(event, self.__handleUnexpectedExit, extraArgs=[avId]) + + self.notify.debug('%s: expecting responses from %s within %s seconds' % (self.uniqueName, self.avIdList, self.timeout)) + self.active = 1 + + def cleanup(self): + if self.active: + taskMgr.remove(self.taskName) + self.active = 0 + self.ignoreAll() + + def clear(self, avId): + if avId not in self.pendingToons: + self.notify.warning('%s: tried to clear %s, who was not listed.' % (self.uniqueName, avId)) + return + self.notify.debug('%s: clearing avatar %s' % (self.uniqueName, avId)) + self.pendingToons.remove(avId) + if len(self.pendingToons) == 0: + self.notify.debug('%s: barrier cleared by %s' % (self.uniqueName, self.avIdList)) + self.cleanup() + if self.clearedFunc: + self.clearedFunc() + if self.doneFunc: + self.doneFunc(self.avIdList) + + def isActive(self): + return self.active + + def getPendingToons(self): + return self.pendingToons[:] + + def __timerExpired(self, task): + self.notify.warning('%s: timeout expired; responses not received from %s' % (self.uniqueName, self.pendingToons)) + self.cleanup() + if self.timeoutFunc: + self.timeoutFunc(self.pendingToons[:]) + if self.doneFunc: + clearedAvIds = self.avIdList[:] + for avId in self.pendingToons: + clearedAvIds.remove(avId) + + self.doneFunc(clearedAvIds) + return Task.done + + def __handleUnexpectedExit(self, avId): + if avId not in self.avIdList: + return + self.avIdList.remove(avId) + if avId in self.pendingToons: + self.clear(avId) diff --git a/toontown/ai/ToontownAIRepository.py b/toontown/ai/ToontownAIRepository.py new file mode 100755 index 00000000..958e3f1c --- /dev/null +++ b/toontown/ai/ToontownAIRepository.py @@ -0,0 +1,257 @@ +from direct.distributed.PyDatagram import * +from panda3d.core import * + +from otp.ai.AIZoneData import AIZoneDataStore +from otp.ai.MagicWordManagerAI import MagicWordManagerAI +from otp.ai.TimeManagerAI import TimeManagerAI +from otp.ai import BanManagerAI +from otp.distributed.OtpDoGlobals import * +from otp.friends.FriendManagerAI import FriendManagerAI +from toontown.ai import CogPageManagerAI +from toontown.ai import CogSuitManagerAI +from toontown.ai import PromotionManagerAI +from toontown.ai.FishManagerAI import FishManagerAI +from toontown.ai.NewsManagerAI import NewsManagerAI +from toontown.ai.QuestManagerAI import QuestManagerAI +from toontown.ai.DistributedBlackCatMgrAI import DistributedBlackCatMgrAI +from toontown.ai.DistributedReportMgrAI import DistributedReportMgrAI +from toontown.building.DistributedBuildingQueryMgrAI import DistributedBuildingQueryMgrAI +from toontown.building.DistributedTrophyMgrAI import DistributedTrophyMgrAI +from toontown.catalog.CatalogManagerAI import CatalogManagerAI +from toontown.coghq import CountryClubManagerAI +from toontown.coghq import FactoryManagerAI +from toontown.coghq import LawOfficeManagerAI +from toontown.coghq import MintManagerAI +from toontown.distributed.ToontownDistrictAI import ToontownDistrictAI +from toontown.distributed.ToontownDistrictStatsAI import ToontownDistrictStatsAI +from toontown.distributed.ToontownInternalRepository import ToontownInternalRepository +from toontown.coderedemption.TTCodeRedemptionMgrAI import TTCodeRedemptionMgrAI +from toontown.dna.DNAParser import loadDNAFileAI +from toontown.estate.EstateManagerAI import EstateManagerAI +from toontown.hood import BRHoodAI +from toontown.hood import BossbotHQAI +from toontown.hood import CashbotHQAI +from toontown.hood import DDHoodAI +from toontown.hood import DGHoodAI +from toontown.hood import DLHoodAI +from toontown.hood import GSHoodAI +from toontown.hood import GZHoodAI +from toontown.hood import LawbotHQAI +from toontown.hood import MMHoodAI +from toontown.hood import OZHoodAI +from toontown.hood import SellbotHQAI +from toontown.hood import TTHoodAI +from toontown.hood import ZoneUtil +from toontown.racing.LeaderboardMgrAI import LeaderboardMgrAI +from toontown.pets.PetManagerAI import PetManagerAI +from toontown.safezone.SafeZoneManagerAI import SafeZoneManagerAI +from toontown.suit.SuitInvasionManagerAI import SuitInvasionManagerAI +from toontown.toon import NPCToons +from toontown.toonbase import ToontownGlobals +from toontown.tutorial.TutorialManagerAI import TutorialManagerAI +from toontown.uberdog.DistributedPartyManagerAI import DistributedPartyManagerAI +from toontown.uberdog.TopToonsManagerAI import TopToonsManagerAI +#from toontown.uberdog.DistributedLobbyManagerAI import DistributedLobbyManagerAI +import threading + +class ToontownAIRepository(ToontownInternalRepository): + def __init__(self, baseChannel, stateServerChannel, districtName): + ToontownInternalRepository.__init__( + self, baseChannel, stateServerChannel, dcSuffix='AI') + + self.districtName = districtName + + self.notify.setInfo(True) # Our AI repository should always log info. + self.hoods = [] + self.cogHeadquarters = [] + self.dnaStoreMap = {} + self.dnaDataMap = {} + self.suitPlanners = {} + self.buildingManagers = {} + self.factoryMgr = None + self.mintMgr = None + self.lawOfficeMgr = None + self.countryClubMgr = None + + self.zoneAllocator = UniqueIdAllocator(ToontownGlobals.DynamicZonesBegin, + ToontownGlobals.DynamicZonesEnd) + self.zoneDataStore = AIZoneDataStore() + + self.wantFishing = self.config.GetBool('want-fishing', True) + self.wantHousing = self.config.GetBool('want-housing', True) + self.wantPets = self.config.GetBool('want-pets', True) + self.wantKarts = self.config.GetBool('want-karts', True) + self.wantParties = self.config.GetBool('want-parties', True) + self.wantEmblems = self.config.GetBool('want-emblems', True) + self.wantCogbuildings = self.config.GetBool('want-cogbuildings', True) + self.wantCogdominiums = self.config.GetBool('want-cogdominiums', True) + self.wantTrackClsends = self.config.GetBool('want-track-clsends', False) + self.wantTopToons = self.config.GetBool('want-top-toons', True) + self.baseXpMultiplier = self.config.GetFloat('base-xp-multiplier', 1.0) + + self.cogSuitMessageSent = False + + def createManagers(self): + self.timeManager = TimeManagerAI(self) + self.timeManager.generateWithRequired(2) + self.magicWordManager = MagicWordManagerAI(self) + self.magicWordManager.generateWithRequired(2) + self.newsManager = NewsManagerAI(self) + self.newsManager.generateWithRequired(2) + self.safeZoneManager = SafeZoneManagerAI(self) + self.safeZoneManager.generateWithRequired(2) + self.tutorialManager = TutorialManagerAI(self) + self.tutorialManager.generateWithRequired(2) + self.friendManager = FriendManagerAI(self) + self.friendManager.generateWithRequired(2) + self.questManager = QuestManagerAI(self) + self.banManager = BanManagerAI.BanManagerAI(self) + self.suitInvasionManager = SuitInvasionManagerAI(self) + self.blackCatMgr = DistributedBlackCatMgrAI(self) + self.blackCatMgr.generateWithRequired(2) + self.reportMgr = DistributedReportMgrAI(self) + self.reportMgr.generateWithRequired(2) + self.trophyMgr = DistributedTrophyMgrAI(self) + self.trophyMgr.generateWithRequired(2) + self.cogSuitMgr = CogSuitManagerAI.CogSuitManagerAI() + self.promotionMgr = PromotionManagerAI.PromotionManagerAI(self) + self.cogPageManager = CogPageManagerAI.CogPageManagerAI() + self.codeRedemptionMgr = TTCodeRedemptionMgrAI(self) + self.codeRedemptionMgr.generateWithRequired(2) + self.buildingQueryMgr = DistributedBuildingQueryMgrAI(self) + self.buildingQueryMgr.generateWithRequired(2) + if self.wantTopToons: + self.topToonsMgr = TopToonsManagerAI(self) + if self.wantKarts: + self.leaderboardMgr = LeaderboardMgrAI(self) + if self.wantFishing: + self.fishManager = FishManagerAI(self) + if self.wantHousing: + self.estateManager = EstateManagerAI(self) + self.estateManager.generateWithRequired(2) + self.catalogManager = CatalogManagerAI(self) + self.catalogManager.generateWithRequired(2) + if self.wantPets: + self.petMgr = PetManagerAI(self) + if self.wantParties: + self.partyManager = DistributedPartyManagerAI(self) + self.partyManager.generateWithRequired(2) + self.globalPartyMgr = self.generateGlobalObject( + OTP_DO_ID_GLOBAL_PARTY_MANAGER, 'GlobalPartyManager') + #self.lobbyManager = DistributedLobbyManagerAI(self) + #self.lobbyManager.generateWithRequired(2) + #self.globalLobbyMgr = self.generateGlobalObject( + # OTP_DO_ID_GLOBAL_LOBBY_MANAGER, 'GlobalLobbyManager') + + def createSafeZones(self): + NPCToons.generateZone2NpcDict() + if self.config.GetBool('want-toontown-central', True): + self.hoods.append(TTHoodAI.TTHoodAI(self)) + if self.config.GetBool('want-donalds-dock', True): + self.hoods.append(DDHoodAI.DDHoodAI(self)) + if self.config.GetBool('want-daisys-garden', True): + self.hoods.append(DGHoodAI.DGHoodAI(self)) + if self.config.GetBool('want-minnies-melodyland', True): + self.hoods.append(MMHoodAI.MMHoodAI(self)) + if self.config.GetBool('want-the-brrrgh', True): + self.hoods.append(BRHoodAI.BRHoodAI(self)) + if self.config.GetBool('want-donalds-dreamland', True): + self.hoods.append(DLHoodAI.DLHoodAI(self)) + if self.config.GetBool('want-goofy-speedway', True): + self.hoods.append(GSHoodAI.GSHoodAI(self)) + if self.config.GetBool('want-outdoor-zone', True): + self.hoods.append(OZHoodAI.OZHoodAI(self)) + if self.config.GetBool('want-golf-zone', True): + self.hoods.append(GZHoodAI.GZHoodAI(self)) + + def createCogHeadquarters(self): + NPCToons.generateZone2NpcDict() + if self.config.GetBool('want-sellbot-headquarters', True): + self.factoryMgr = FactoryManagerAI.FactoryManagerAI(self) + self.cogHeadquarters.append(SellbotHQAI.SellbotHQAI(self)) + if self.config.GetBool('want-cashbot-headquarters', True): + self.mintMgr = MintManagerAI.MintManagerAI(self) + self.cogHeadquarters.append(CashbotHQAI.CashbotHQAI(self)) + if self.config.GetBool('want-lawbot-headquarters', True): + self.lawOfficeMgr = LawOfficeManagerAI.LawOfficeManagerAI(self) + self.cogHeadquarters.append(LawbotHQAI.LawbotHQAI(self)) + if self.config.GetBool('want-bossbot-headquarters', True): + self.countryClubMgr = CountryClubManagerAI.CountryClubManagerAI(self) + self.cogHeadquarters.append(BossbotHQAI.BossbotHQAI(self)) + + def handleConnected(self): + ToontownInternalRepository.handleConnected(self) + threading.Thread(target=self.startDistrict).start() + + def startDistrict(self): + self.districtId = self.allocateChannel() + self.notify.info('Creating ToontownDistrictAI(%d)...' % self.districtId) + self.distributedDistrict = ToontownDistrictAI(self) + self.distributedDistrict.setName(self.districtName) + self.distributedDistrict.generateWithRequiredAndId( + self.districtId, self.getGameDoId(), 2) + self.notify.info('Claiming ownership of channel ID: %d...' % self.districtId) + self.claimOwnership(self.districtId) + + self.districtStats = ToontownDistrictStatsAI(self) + self.districtStats.setDistrictId(self.districtId) + self.districtStats.generateWithRequiredAndId( + self.allocateChannel(), self.getGameDoId(), 3) + self.notify.info('Created ToontownDistrictStats(%d)' % self.districtStats.doId) + + self.notify.info('Creating managers...') + self.createManagers() + if self.config.GetBool('want-safe-zones', True): + self.notify.info('Creating safe zones...') + self.createSafeZones() + if self.config.GetBool('want-cog-headquarters', True): + self.notify.info('Creating Cog headquarters...') + self.createCogHeadquarters() + + self.notify.info('Making district available...') + self.distributedDistrict.b_setAvailable(1) + self.notify.info('Done.') + + def claimOwnership(self, channelId): + datagram = PyDatagram() + datagram.addServerHeader(channelId, self.ourChannel, STATESERVER_OBJECT_SET_AI) + datagram.addChannel(self.ourChannel) + self.send(datagram) + + def lookupDNAFileName(self, zoneId): + zoneId = ZoneUtil.getCanonicalZoneId(zoneId) + hoodId = ZoneUtil.getCanonicalHoodId(zoneId) + hood = ToontownGlobals.dnaMap[hoodId] + if hoodId == zoneId: + zoneId = 'sz' + phaseNum = ToontownGlobals.phaseMap[hoodId] + else: + phaseNum = ToontownGlobals.streetPhaseMap[hoodId] + return 'phase_%s/dna/%s_%s.pdna' % (phaseNum, hood, zoneId) + + def loadDNAFileAI(self, dnastore, filename): + return loadDNAFileAI(dnastore, filename) + + def incrementPopulation(self): + self.districtStats.b_setAvatarCount(self.districtStats.getAvatarCount() + 1) + + def decrementPopulation(self): + self.districtStats.b_setAvatarCount(self.districtStats.getAvatarCount() - 1) + + def allocateZone(self): + return self.zoneAllocator.allocate() + + def deallocateZone(self, zone): + self.zoneAllocator.free(zone) + + def getZoneDataStore(self): + return self.zoneDataStore + + def getTrackClsends(self): + return self.wantTrackClsends + + def getAvatarExitEvent(self, avId): + return 'distObjDelete-%d' % avId + + def trueUniqueName(self, name): + return self.uniqueName(name) diff --git a/toontown/ai/__init__.py b/toontown/ai/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/battle/BattleBase.py b/toontown/battle/BattleBase.py new file mode 100755 index 00000000..fd00f92c --- /dev/null +++ b/toontown/battle/BattleBase.py @@ -0,0 +1,310 @@ +from panda3d.core import * +from toontown.toonbase.ToontownBattleGlobals import * +from direct.task.Timer import * +import math +from direct.directnotify import DirectNotifyGlobal +from toontown.toon import NPCToons +from toontown.toonbase import TTLocalizer +TOON_ID_COL = 0 +TOON_TRACK_COL = 1 +TOON_LVL_COL = 2 +TOON_TGT_COL = 3 +TOON_HP_COL = 4 +TOON_ACCBONUS_COL = 5 +TOON_HPBONUS_COL = 6 +TOON_KBBONUS_COL = 7 +SUIT_DIED_COL = 8 +SUIT_REVIVE_COL = 9 +SUIT_ID_COL = 0 +SUIT_ATK_COL = 1 +SUIT_TGT_COL = 2 +SUIT_HP_COL = 3 +TOON_DIED_COL = 4 +SUIT_BEFORE_TOONS_COL = 5 +SUIT_TAUNT_COL = 6 +NO_ID = -1 +NO_ATTACK = -1 +UN_ATTACK = -2 +PASS_ATTACK = -3 +NO_TRAP = -1 +LURE_SUCCEEDED = -1 +PASS = 98 +SOS = 99 +NPCSOS = 97 +PETSOS = 96 +FIRE = 100 +HEAL = HEAL_TRACK +TRAP = TRAP_TRACK +LURE = LURE_TRACK +SOUND = SOUND_TRACK +THROW = THROW_TRACK +SQUIRT = SQUIRT_TRACK +DROP = DROP_TRACK +TOON_ATTACK_TIME = 15.0 +SUIT_ATTACK_TIME = 12.0 +TOON_TRAP_DELAY = 0.8 +TOON_SOUND_DELAY = 1.0 +TOON_THROW_DELAY = 0.5 +TOON_THROW_SUIT_DELAY = 1.0 +TOON_SQUIRT_DELAY = 0.5 +TOON_SQUIRT_SUIT_DELAY = 1.0 +TOON_DROP_DELAY = 0.8 +TOON_DROP_SUIT_DELAY = 1.0 +TOON_RUN_T = 3.3 +TIMEOUT_PER_USER = 5 +TOON_FIRE_DELAY = 0.5 +TOON_FIRE_SUIT_DELAY = 1.0 +REWARD_TIMEOUT = 120 +FLOOR_REWARD_TIMEOUT = 4 +BUILDING_REWARD_TIMEOUT = 300 +CLIENT_INPUT_TIMEOUT = config.GetFloat('battle-input-timeout', TTLocalizer.BBbattleInputTimeout) + +def levelAffectsGroup(track, level): + return attackAffectsGroup(track, level) + + +def attackAffectsGroup(track, level, type = None): + if track == NPCSOS or type == NPCSOS or track == PETSOS or type == PETSOS: + return 1 + elif track >= 0 and track <= DROP_TRACK: + return AvPropTargetCat[AvPropTarget[track]][level] + else: + return 0 + + +def getToonAttack(id, track = NO_ATTACK, level = -1, target = -1): + return [id, + track, + level, + target, + [], + 0, + 0, + [], + 0, + 0] + + +def getDefaultSuitAttacks(): + suitAttacks = [[NO_ID, + NO_ATTACK, + -1, + [], + 0, + 0, + 0], + [NO_ID, + NO_ATTACK, + -1, + [], + 0, + 0, + 0], + [NO_ID, + NO_ATTACK, + -1, + [], + 0, + 0, + 0], + [NO_ID, + NO_ATTACK, + -1, + [], + 0, + 0, + 0]] + return suitAttacks + + +def getDefaultSuitAttack(): + return [NO_ID, + NO_ATTACK, + -1, + [], + 0, + 0, + 0] + + +def findToonAttack(toons, attacks, track): + foundAttacks = [] + for t in toons: + if t in attacks: + attack = attacks[t] + local_track = attack[TOON_TRACK_COL] + if track != NPCSOS and attack[TOON_TRACK_COL] == NPCSOS: + local_track = NPCToons.getNPCTrack(attack[TOON_TGT_COL]) + if local_track == track: + if local_track == FIRE: + canFire = 1 + for attackCheck in foundAttacks: + if attackCheck[TOON_TGT_COL] == attack[TOON_TGT_COL]: + canFire = 0 + + if canFire: + foundAttacks.append(attack) + else: + foundAttacks.append(attack) + + def compFunc(a, b): + if a[TOON_LVL_COL] > b[TOON_LVL_COL]: + return 1 + elif a[TOON_LVL_COL] < b[TOON_LVL_COL]: + return -1 + return 0 + + foundAttacks.sort(compFunc) + return foundAttacks + + +SERVER_BUFFER_TIME = 2.0 +SERVER_INPUT_TIMEOUT = CLIENT_INPUT_TIMEOUT + SERVER_BUFFER_TIME +MAX_JOIN_T = TTLocalizer.BBbattleInputTimeout +FACEOFF_TAUNT_T = 3.5 +ELEVATOR_T = 4.0 +BATTLE_SMALL_VALUE = 1e-07 +MAX_EXPECTED_DISTANCE_FROM_BATTLE = 50.0 + +class BattleBase: + notify = DirectNotifyGlobal.directNotify.newCategory('BattleBase') + suitPoints = (((Point3(0, 5, 0), 179),), + ((Point3(2, 5.3, 0), 170), (Point3(-2, 5.3, 0), 180)), + ((Point3(4, 5.2, 0), 170), (Point3(0, 6, 0), 179), (Point3(-4, 5.2, 0), 190)), + ((Point3(6, 4.4, 0), 160), + (Point3(2, 6.3, 0), 170), + (Point3(-2, 6.3, 0), 190), + (Point3(-6, 4.4, 0), 200))) + suitPendingPoints = ((Point3(-4, 8.2, 0), 190), + (Point3(0, 9, 0), 179), + (Point3(4, 8.2, 0), 170), + (Point3(8, 3.2, 0), 160)) + toonPoints = (((Point3(0, -6, 0), 0),), + ((Point3(1.5, -6.5, 0), 5), (Point3(-1.5, -6.5, 0), -5)), + ((Point3(3, -6.75, 0), 5), (Point3(0, -7, 0), 0), (Point3(-3, -6.75, 0), -5)), + ((Point3(4.5, -7, 0), 10), + (Point3(1.5, -7.5, 0), 5), + (Point3(-1.5, -7.5, 0), -5), + (Point3(-4.5, -7, 0), -10))) + toonPendingPoints = ((Point3(-3, -8, 0), -5), + (Point3(0, -9, 0), 0), + (Point3(3, -8, 0), 5), + (Point3(5.5, -5.5, 0), 20)) + posA = Point3(0, 10, 0) + posB = Point3(-7.071, 7.071, 0) + posC = Point3(-10, 0, 0) + posD = Point3(-7.071, -7.071, 0) + posE = Point3(0, -10, 0) + posF = Point3(7.071, -7.071, 0) + posG = Point3(10, 0, 0) + posH = Point3(7.071, 7.071, 0) + allPoints = (posA, + posB, + posC, + posD, + posE, + posF, + posG, + posH) + toonCwise = [posA, + posB, + posC, + posD, + posE] + toonCCwise = [posH, + posG, + posF, + posE] + suitCwise = [posE, + posF, + posG, + posH, + posA] + suitCCwise = [posD, + posC, + posB, + posA] + suitSpeed = 4.8 + toonSpeed = 8.0 + maxTimeToon = 3.0 + maxTimeSuit = 4.0 + + def __init__(self): + self.pos = Point3(0, 0, 0) + self.initialSuitPos = Point3(0, 1, 0) + self.timer = Timer() + self.resetLists() + + def resetLists(self): + self.suits = [] + self.pendingSuits = [] + self.joiningSuits = [] + self.activeSuits = [] + self.luredSuits = [] + self.suitGone = 0 + self.toons = [] + self.joiningToons = [] + self.pendingToons = [] + self.activeToons = [] + self.runningToons = [] + self.toonGone = 0 + self.helpfulToons = [] + + def calcFaceoffTime(self, centerpos, suitpos): + facing = Vec3(centerpos - suitpos) + facing.normalize() + suitdest = Point3(centerpos - Point3(facing * 6.0)) + dist = Vec3(suitdest - suitpos).length() + return min(dist / BattleBase.suitSpeed, BattleBase.maxTimeSuit) + + def calcSuitMoveTime(self, pos0, pos1): + dist = Vec3(pos0 - pos1).length() + return min(dist / BattleBase.suitSpeed, BattleBase.maxTimeSuit) + + def calcToonMoveTime(self, pos0, pos1): + dist = Vec3(pos0 - pos1).length() + return min(dist / BattleBase.toonSpeed, BattleBase.maxTimeToon) + + def buildJoinPointList(self, avPos, destPos, toon = 0): + minDist = 999999.0 + nearestP = None + for p in BattleBase.allPoints: + dist = Vec3(avPos - p).length() + if dist < minDist: + nearestP = p + minDist = dist + + self.notify.debug('buildJoinPointList() - avp: %s nearp: %s' % (avPos, nearestP)) + dist = Vec3(avPos - destPos).length() + if dist < minDist: + self.notify.debug('buildJoinPointList() - destPos is nearest') + return [] + if toon == 1: + if nearestP == BattleBase.posE: + self.notify.debug('buildJoinPointList() - posE') + plist = [BattleBase.posE] + elif BattleBase.toonCwise.count(nearestP) == 1: + self.notify.debug('buildJoinPointList() - clockwise') + index = BattleBase.toonCwise.index(nearestP) + plist = BattleBase.toonCwise[index + 1:] + else: + self.notify.debug('buildJoinPointList() - counter-clockwise') + index = BattleBase.toonCCwise.index(nearestP) + plist = BattleBase.toonCCwise[index + 1:] + elif nearestP == BattleBase.posA: + self.notify.debug('buildJoinPointList() - posA') + plist = [BattleBase.posA] + elif BattleBase.suitCwise.count(nearestP) == 1: + self.notify.debug('buildJoinPointList() - clockwise') + index = BattleBase.suitCwise.index(nearestP) + plist = BattleBase.suitCwise[index + 1:] + else: + self.notify.debug('buildJoinPointList() - counter-clockwise') + index = BattleBase.suitCCwise.index(nearestP) + plist = BattleBase.suitCCwise[index + 1:] + self.notify.debug('buildJoinPointList() - plist: %s' % plist) + return plist + + def addHelpfulToon(self, toonId): + if toonId not in self.helpfulToons: + self.helpfulToons.append(toonId) diff --git a/toontown/battle/BattleCalculatorAI.py b/toontown/battle/BattleCalculatorAI.py new file mode 100755 index 00000000..0cac39f5 --- /dev/null +++ b/toontown/battle/BattleCalculatorAI.py @@ -0,0 +1,1588 @@ +from BattleBase import * +from DistributedBattleAI import * +from toontown.toonbase.ToontownBattleGlobals import * +import random +from toontown.suit import DistributedSuitBaseAI +import SuitBattleGlobals +import BattleExperienceAI +from toontown.toon import NPCToons +from toontown.pets import PetTricks +from toontown.hood import ZoneUtil +from direct.showbase.PythonUtil import lerp +import sys + +class BattleCalculatorAI: + AccuracyBonuses = [0, + 20, + 40, + 60] + DamageBonuses = [0, + 20, + 20, + 20] + AttackExpPerTrack = [0, + 10, + 20, + 30, + 40, + 50, + 60] + TRAP_CONFLICT = -2 + APPLY_HEALTH_ADJUSTMENTS = 1 + TOONS_TAKE_NO_DAMAGE = 0 + CAP_HEALS = 1 + CLEAR_SUIT_ATTACKERS = 1 + SUITS_UNLURED_IMMEDIATELY = 1 + CLEAR_MULTIPLE_TRAPS = 0 + KBBONUS_LURED_FLAG = 0 + KBBONUS_TGT_LURED = 1 + notify = DirectNotifyGlobal.directNotify.newCategory('BattleCalculatorAI') + toonsAlwaysHit = simbase.config.GetBool('toons-always-hit', 0) + toonsAlwaysMiss = simbase.config.GetBool('toons-always-miss', 0) + toonsAlways5050 = simbase.config.GetBool('toons-always-5050', 0) + suitsAlwaysHit = simbase.config.GetBool('suits-always-hit', 0) + suitsAlwaysMiss = simbase.config.GetBool('suits-always-miss', 0) + immortalSuits = simbase.config.GetBool('immortal-suits', 0) + propAndOrganicBonusStack = simbase.config.GetBool('prop-and-organic-bonus-stack', 0) + + def __init__(self, battle, tutorialFlag = 0): + self.battle = battle + self.SuitAttackers = {} + self.currentlyLuredSuits = {} + self.successfulLures = {} + self.toonAtkOrder = [] + self.toonHPAdjusts = {} + self.toonSkillPtsGained = {} + self.traps = {} + self.npcTraps = {} + self.suitAtkStats = {} + self.__clearBonuses(hp=1) + self.__clearBonuses(hp=0) + self.delayedUnlures = [] + self.__skillCreditMultiplier = simbase.air.baseXpMultiplier + self.tutorialFlag = tutorialFlag + self.trainTrapTriggered = False + + def setSkillCreditMultiplier(self, mult): + self.__skillCreditMultiplier = simbase.air.baseXpMultiplier * mult + + def getSkillCreditMultiplier(self): + return self.__skillCreditMultiplier + + def cleanup(self): + self.battle = None + return + + def __calcToonAtkHit(self, attackIndex, atkTargets): + if len(atkTargets) == 0: + return (0, 0) + if self.tutorialFlag: + return (1, 95) + if self.toonsAlways5050: + roll = random.randint(0, 99) + if roll < 50: + return (1, 95) + else: + return (0, 0) + if self.toonsAlwaysHit: + return (1, 95) + elif self.toonsAlwaysMiss: + return (0, 0) + debug = self.notify.getDebug() + attack = self.battle.toonAttacks[attackIndex] + atkTrack, atkLevel = self.__getActualTrackLevel(attack) + + if atkTrack == NPCSOS: + return (1, 95) + if atkTrack == FIRE: + return (1, 95) + if atkTrack == TRAP: + if debug: + self.notify.debug('Attack is a trap, so it hits regardless') + attack[TOON_ACCBONUS_COL] = 0 + return (1, 100) + elif atkTrack == DROP and attack[TOON_TRACK_COL] == NPCSOS: + unluredSuits = 0 + for tgt in atkTargets: + if not self.__suitIsLured(tgt.getDoId()): + unluredSuits = 1 + + if unluredSuits == 0: + attack[TOON_ACCBONUS_COL] = 1 + return (0, 0) + elif atkTrack == DROP: + allLured = True + for i in xrange(len(atkTargets)): + if self.__suitIsLured(atkTargets[i].getDoId()): + pass + else: + allLured = False + + if allLured: + attack[TOON_ACCBONUS_COL] = 1 + return (0, 0) + elif atkTrack == PETSOS: + return self.__calculatePetTrickSuccess(attack) + tgtDef = 0 + numLured = 0 + if atkTrack != HEAL: + for currTarget in atkTargets: + thisSuitDef = self.__targetDefense(currTarget, atkTrack) + if debug: + self.notify.debug('Examining suit def for toon attack: ' + str(thisSuitDef)) + tgtDef = min(thisSuitDef, tgtDef) + if self.__suitIsLured(currTarget.getDoId()): + numLured += 1 + + trackExp = self.__toonTrackExp(attack[TOON_ID_COL], atkTrack) + for currOtherAtk in self.toonAtkOrder: + if currOtherAtk != attack[TOON_ID_COL]: + nextAttack = self.battle.toonAttacks[currOtherAtk] + nextAtkTrack = self.__getActualTrack(nextAttack) + if atkTrack == nextAtkTrack and attack[TOON_TGT_COL] == nextAttack[TOON_TGT_COL]: + currTrackExp = self.__toonTrackExp(nextAttack[TOON_ID_COL], atkTrack) + if debug: + self.notify.debug('Examining toon track exp bonus: ' + str(currTrackExp)) + trackExp = max(currTrackExp, trackExp) + + if debug: + if atkTrack == HEAL: + self.notify.debug('Toon attack is a heal, no target def used') + else: + self.notify.debug('Suit defense used for toon attack: ' + str(tgtDef)) + self.notify.debug('Toon track exp bonus used for toon attack: ' + str(trackExp)) + if attack[TOON_TRACK_COL] == NPCSOS: + randChoice = 0 + else: + randChoice = random.randint(0, 99) + propAcc = AvPropAccuracy[atkTrack][atkLevel] + propAcc = min(propAcc * 1.3, 100) + if atkTrack == LURE: + treebonus = self.__toonCheckGagBonus(attack[TOON_ID_COL], atkTrack, atkLevel) + propBonus = self.__checkPropBonus(atkTrack) + if self.propAndOrganicBonusStack: + propAcc = 0 + if treebonus: + self.notify.debug('using organic bonus lure accuracy') + propAcc += AvLureBonusAccuracy[atkLevel] + if propBonus: + self.notify.debug('using prop bonus lure accuracy') + propAcc += AvLureBonusAccuracy[atkLevel] + elif treebonus or propBonus: + self.notify.debug('using oragnic OR prop bonus lure accuracy') + propAcc = AvLureBonusAccuracy[atkLevel] + attackAcc = propAcc + trackExp + tgtDef + currAtk = self.toonAtkOrder.index(attackIndex) + if currAtk > 0 and atkTrack != HEAL: + prevAtkId = self.toonAtkOrder[currAtk - 1] + prevAttack = self.battle.toonAttacks[prevAtkId] + prevAtkTrack = self.__getActualTrack(prevAttack) + lure = atkTrack == LURE and (not attackAffectsGroup(atkTrack, atkLevel, + attack[TOON_TRACK_COL]) and attack[TOON_TGT_COL] in self.successfulLures or attackAffectsGroup(atkTrack, atkLevel, attack[TOON_TRACK_COL])) + if atkTrack == prevAtkTrack and (attack[TOON_TGT_COL] == prevAttack[TOON_TGT_COL] or lure): + if prevAttack[TOON_ACCBONUS_COL] == 1: + if debug: + self.notify.debug('DODGE: Toon attack track dodged') + elif prevAttack[TOON_ACCBONUS_COL] == 0: + if debug: + self.notify.debug('HIT: Toon attack track hit') + attack[TOON_ACCBONUS_COL] = prevAttack[TOON_ACCBONUS_COL] + return (not attack[TOON_ACCBONUS_COL], attackAcc) + atkAccResult = attackAcc + if debug: + self.notify.debug('setting atkAccResult to %d' % atkAccResult) + acc = attackAcc + self.__calcToonAccBonus(attackIndex) + if atkTrack != LURE and atkTrack != HEAL: + if atkTrack != DROP: + if numLured == len(atkTargets): + if debug: + self.notify.debug('all targets are lured, attack hits') + attack[TOON_ACCBONUS_COL] = 0 + return (1, 100) + else: + luredRatio = float(numLured) / float(len(atkTargets)) + accAdjust = 100 * luredRatio + if accAdjust > 0 and debug: + self.notify.debug(str(numLured) + ' out of ' + str(len(atkTargets)) + ' targets are lured, so adding ' + str(accAdjust) + ' to attack accuracy') + acc += accAdjust + elif numLured == len(atkTargets): + if debug: + self.notify.debug('all targets are lured, attack misses') + attack[TOON_ACCBONUS_COL] = 0 + return (0, 0) + if acc > MaxToonAcc: + acc = MaxToonAcc + if randChoice < acc: + if debug: + self.notify.debug('HIT: Toon attack rolled' + str(randChoice) + 'to hit with an accuracy of' + str(acc)) + attack[TOON_ACCBONUS_COL] = 0 + else: + if debug: + self.notify.debug('MISS: Toon attack rolled' + str(randChoice) + 'to hit with an accuracy of' + str(acc)) + attack[TOON_ACCBONUS_COL] = 1 + return (not attack[TOON_ACCBONUS_COL], atkAccResult) + + def __toonTrackExp(self, toonId, track): + toon = self.battle.getToon(toonId) + if toon != None: + toonExpLvl = toon.experience.getExpLevel(track) + exp = self.AttackExpPerTrack[toonExpLvl] + if track == HEAL: + exp = exp * 0.5 + self.notify.debug('Toon track exp: ' + str(toonExpLvl) + ' and resulting acc bonus: ' + str(exp)) + return exp + else: + return 0 + return + + def __toonCheckGagBonus(self, toonId, track, level): + toon = self.battle.getToon(toonId) + if toon != None: + return toon.checkGagBonus(track, level) + else: + return False + return + + def __checkPropBonus(self, track): + return self.battle.getInteractivePropTrackBonus() == track + + def __targetDefense(self, suit, atkTrack): + if atkTrack == HEAL: + return 0 + suitDef = SuitBattleGlobals.SuitAttributes[suit.dna.name]['def'][suit.getLevel()] + return -suitDef + + def __createToonTargetList(self, attackIndex): + attack = self.battle.toonAttacks[attackIndex] + atkTrack, atkLevel = self.__getActualTrackLevel(attack) + targetList = [] + if atkTrack == NPCSOS: + return targetList + if not attackAffectsGroup(atkTrack, atkLevel, attack[TOON_TRACK_COL]): + if atkTrack == HEAL: + target = attack[TOON_TGT_COL] + else: + target = self.battle.findSuit(attack[TOON_TGT_COL]) + if target != None: + targetList.append(target) + elif atkTrack == HEAL or atkTrack == PETSOS: + if attack[TOON_TRACK_COL] == NPCSOS or atkTrack == PETSOS: + targetList = self.battle.activeToons + else: + for currToon in self.battle.activeToons: + if attack[TOON_ID_COL] != currToon: + targetList.append(currToon) + + else: + targetList = self.battle.activeSuits + return targetList + + def __prevAtkTrack(self, attackerId, toon = 1): + if toon: + prevAtkIdx = self.toonAtkOrder.index(attackerId) - 1 + if prevAtkIdx >= 0: + prevAttackerId = self.toonAtkOrder[prevAtkIdx] + attack = self.battle.toonAttacks[prevAttackerId] + return self.__getActualTrack(attack) + else: + return NO_ATTACK + + def getSuitTrapType(self, suitId): + if suitId in self.traps: + if self.traps[suitId][0] == self.TRAP_CONFLICT: + return NO_TRAP + else: + return self.traps[suitId][0] + else: + return NO_TRAP + + def __suitTrapDamage(self, suitId): + if suitId in self.traps: + return self.traps[suitId][2] + else: + return 0 + + def addTrainTrapForJoiningSuit(self, suitId): + self.notify.debug('addTrainTrapForJoiningSuit suit=%d self.traps=%s' % (suitId, self.traps)) + trapInfoToUse = None + for trapInfo in self.traps.values(): + if trapInfo[0] == UBER_GAG_LEVEL_INDEX: + trapInfoToUse = trapInfo + break + + if trapInfoToUse: + self.traps[suitId] = trapInfoToUse + else: + self.notify.warning('huh we did not find a train trap?') + return + + def __addSuitGroupTrap(self, suitId, trapLvl, attackerId, allSuits, npcDamage = 0): + if npcDamage == 0: + if suitId in self.traps: + if self.traps[suitId][0] == self.TRAP_CONFLICT: + pass + else: + self.traps[suitId][0] = self.TRAP_CONFLICT + for suit in allSuits: + id = suit.doId + if id in self.traps: + self.traps[id][0] = self.TRAP_CONFLICT + else: + self.traps[id] = [self.TRAP_CONFLICT, 0, 0] + + else: + toon = self.battle.getToon(attackerId) + organicBonus = toon.checkGagBonus(TRAP, trapLvl) + propBonus = self.__checkPropBonus(TRAP) + damage = getAvPropDamage(TRAP, trapLvl, toon.experience.getExp(TRAP), organicBonus, propBonus, self.propAndOrganicBonusStack) + if self.itemIsCredit(TRAP, trapLvl): + self.traps[suitId] = [trapLvl, attackerId, damage] + else: + self.traps[suitId] = [trapLvl, 0, damage] + self.notify.debug('calling __addLuredSuitsDelayed') + self.__addLuredSuitsDelayed(attackerId, targetId=-1, ignoreDamageCheck=True) + elif suitId in self.traps: + if self.traps[suitId][0] == self.TRAP_CONFLICT: + self.traps[suitId] = [trapLvl, 0, npcDamage] + elif not self.__suitIsLured(suitId): + self.traps[suitId] = [trapLvl, 0, npcDamage] + + def __addSuitTrap(self, suitId, trapLvl, attackerId, npcDamage = 0): + if npcDamage == 0: + if suitId in self.traps: + if self.traps[suitId][0] == self.TRAP_CONFLICT: + pass + else: + self.traps[suitId][0] = self.TRAP_CONFLICT + else: + toon = self.battle.getToon(attackerId) + organicBonus = toon.checkGagBonus(TRAP, trapLvl) + propBonus = self.__checkPropBonus(TRAP) + damage = getAvPropDamage(TRAP, trapLvl, toon.experience.getExp(TRAP), organicBonus, propBonus, self.propAndOrganicBonusStack) + if self.itemIsCredit(TRAP, trapLvl): + self.traps[suitId] = [trapLvl, attackerId, damage] + else: + self.traps[suitId] = [trapLvl, 0, damage] + elif suitId in self.traps: + if self.traps[suitId][0] == self.TRAP_CONFLICT: + self.traps[suitId] = [trapLvl, 0, npcDamage] + elif not self.__suitIsLured(suitId): + self.traps[suitId] = [trapLvl, 0, npcDamage] + + def __removeSuitTrap(self, suitId): + if suitId in self.traps: + del self.traps[suitId] + + def __clearTrapCreator(self, creatorId, suitId = None): + if suitId == None: + for currTrap in self.traps.keys(): + if creatorId == self.traps[currTrap][1]: + self.traps[currTrap][1] = 0 + + elif suitId in self.traps: + self.traps[suitId][1] = 0 + return + + def __trapCreator(self, suitId): + if suitId in self.traps: + return self.traps[suitId][1] + else: + return 0 + + def __initTraps(self): + self.trainTrapTriggered = False + keysList = self.traps.keys() + for currTrap in keysList: + if self.traps[currTrap][0] == self.TRAP_CONFLICT: + del self.traps[currTrap] + + def __calcToonAtkHp(self, toonId): + attack = self.battle.toonAttacks[toonId] + targetList = self.__createToonTargetList(toonId) + atkHit, atkAcc = self.__calcToonAtkHit(toonId, targetList) + atkTrack, atkLevel, atkHp = self.__getActualTrackLevelHp(attack) + if not atkHit and atkTrack != HEAL: + return + validTargetAvail = 0 + lureDidDamage = 0 + currLureId = -1 + for currTarget in xrange(len(targetList)): + attackLevel = -1 + attackTrack = None + attackDamage = 0 + toonTarget = 0 + targetLured = 0 + if atkTrack == HEAL or atkTrack == PETSOS: + targetId = targetList[currTarget] + toonTarget = 1 + else: + targetId = targetList[currTarget].getDoId() + if atkTrack == LURE: + if self.getSuitTrapType(targetId) == NO_TRAP: + if self.notify.getDebug(): + self.notify.debug('Suit lured, but no trap exists') + if self.SUITS_UNLURED_IMMEDIATELY: + if not self.__suitIsLured(targetId, prevRound=1): + if not self.__combatantDead(targetId, toon=toonTarget): + validTargetAvail = 1 + rounds = NumRoundsLured[atkLevel] + wakeupChance = 100 - atkAcc * 2 + npcLurer = attack[TOON_TRACK_COL] == NPCSOS + currLureId = self.__addLuredSuitInfo(targetId, -1, rounds, wakeupChance, toonId, atkLevel, lureId=currLureId, npc=npcLurer) + if self.notify.getDebug(): + self.notify.debug('Suit lured for ' + str(rounds) + ' rounds max with ' + str(wakeupChance) + '% chance to wake up each round') + targetLured = 1 + else: + attackTrack = TRAP + if targetId in self.traps: + trapInfo = self.traps[targetId] + attackLevel = trapInfo[0] + else: + attackLevel = NO_TRAP + attackDamage = self.__suitTrapDamage(targetId) + trapCreatorId = self.__trapCreator(targetId) + if trapCreatorId > 0: + self.notify.debug('Giving trap EXP to toon ' + str(trapCreatorId)) + self.__addAttackExp(attack, track=TRAP, level=attackLevel, attackerId=trapCreatorId) + self.__clearTrapCreator(trapCreatorId, targetId) + lureDidDamage = 1 + if self.notify.getDebug(): + self.notify.debug('Suit lured right onto a trap! (' + str(AvProps[attackTrack][attackLevel]) + ',' + str(attackLevel) + ')') + if not self.__combatantDead(targetId, toon=toonTarget): + validTargetAvail = 1 + targetLured = 1 + if not self.SUITS_UNLURED_IMMEDIATELY: + if not self.__suitIsLured(targetId, prevRound=1): + if not self.__combatantDead(targetId, toon=toonTarget): + validTargetAvail = 1 + rounds = NumRoundsLured[atkLevel] + wakeupChance = 100 - atkAcc * 2 + npcLurer = attack[TOON_TRACK_COL] == NPCSOS + currLureId = self.__addLuredSuitInfo(targetId, -1, rounds, wakeupChance, toonId, atkLevel, lureId=currLureId, npc=npcLurer) + if self.notify.getDebug(): + self.notify.debug('Suit lured for ' + str(rounds) + ' rounds max with ' + str(wakeupChance) + '% chance to wake up each round') + targetLured = 1 + if attackLevel != -1: + self.__addLuredSuitsDelayed(toonId, targetId) + if targetLured and (not targetId in self.successfulLures or targetId in self.successfulLures and self.successfulLures[targetId][1] < atkLevel): + self.notify.debug('Adding target ' + str(targetId) + ' to successfulLures list') + self.successfulLures[targetId] = [toonId, + atkLevel, + atkAcc, + -1] + else: + if atkTrack == TRAP: + npcDamage = 0 + if attack[TOON_TRACK_COL] == NPCSOS: + npcDamage = atkHp + if self.CLEAR_MULTIPLE_TRAPS: + if self.getSuitTrapType(targetId) != NO_TRAP: + self.__clearAttack(toonId) + return + if atkLevel == UBER_GAG_LEVEL_INDEX: + self.__addSuitGroupTrap(targetId, atkLevel, toonId, targetList, npcDamage) + if self.__suitIsLured(targetId): + self.notify.debug('Train Trap on lured suit %d, \n indicating with KBBONUS_COL flag' % targetId) + tgtPos = self.battle.activeSuits.index(targetList[currTarget]) + attack[TOON_KBBONUS_COL][tgtPos] = self.KBBONUS_LURED_FLAG + else: + self.__addSuitTrap(targetId, atkLevel, toonId, npcDamage) + elif self.__suitIsLured(targetId) and atkTrack == SOUND: + self.notify.debug('Sound on lured suit, ' + 'indicating with KBBONUS_COL flag') + tgtPos = self.battle.activeSuits.index(targetList[currTarget]) + attack[TOON_KBBONUS_COL][tgtPos] = self.KBBONUS_LURED_FLAG + attackLevel = atkLevel + attackTrack = atkTrack + toon = self.battle.getToon(toonId) + if attack[TOON_TRACK_COL] == NPCSOS and lureDidDamage != 1 or attack[TOON_TRACK_COL] == PETSOS: + attackDamage = atkHp + elif atkTrack == FIRE: + suit = self.battle.findSuit(targetId) + if suit: + slips = toon.getPinkSlips() + + if slips < 1: + simbase.air.writeServerEvent('suspicious', toonId, 'Toon attempting to fire a cog without any pinkslips') + else: + suit.skeleRevives = 0 + attackDamage = suit.getHP() + toon.b_setPinkSlips(slips - 1) + else: + attackDamage = 0 + bonus = 0 + else: + organicBonus = toon.checkGagBonus(attackTrack, attackLevel) + propBonus = self.__checkPropBonus(attackTrack) + attackDamage = getAvPropDamage(attackTrack, attackLevel, toon.experience.getExp(attackTrack), organicBonus, propBonus, self.propAndOrganicBonusStack) + if not self.__combatantDead(targetId, toon=toonTarget): + if self.__suitIsLured(targetId) and atkTrack == DROP: + self.notify.debug('not setting validTargetAvail, since drop on a lured suit') + else: + validTargetAvail = 1 + if attackLevel == -1 and not atkTrack == FIRE: + result = LURE_SUCCEEDED + elif atkTrack != TRAP: + result = attackDamage + if atkTrack == HEAL: + if not self.__attackHasHit(attack, suit=0): + result = result * 0.2 + if self.notify.getDebug(): + self.notify.debug('toon does ' + str(result) + ' healing to toon(s)') + else: + if self.__suitIsLured(targetId) and atkTrack == DROP: + result = 0 + self.notify.debug('setting damage to 0, since drop on a lured suit') + if self.notify.getDebug(): + self.notify.debug('toon does ' + str(result) + ' damage to suit') + else: + result = 0 + if result != 0 or atkTrack == PETSOS: + targets = self.__getToonTargets(attack) + if targetList[currTarget] not in targets: + if self.notify.getDebug(): + self.notify.debug('Target of toon is not accessible!') + continue + targetIndex = targets.index(targetList[currTarget]) + if atkTrack == HEAL: + result = result / len(targetList) + if self.notify.getDebug(): + self.notify.debug('Splitting heal among ' + str(len(targetList)) + ' targets') + if targetId in self.successfulLures and atkTrack == LURE: + self.notify.debug('Updating lure damage to ' + str(result)) + self.successfulLures[targetId][3] = result + else: + attack[TOON_HP_COL][targetIndex] = result + if result > 0 and atkTrack != HEAL and atkTrack != DROP and atkTrack != PETSOS: + attackTrack = LURE + lureInfos = self.__getLuredExpInfo(targetId) + for currInfo in lureInfos: + if currInfo[3]: + self.notify.debug('Giving lure EXP to toon ' + str(currInfo[0])) + self.__addAttackExp(attack, track=attackTrack, level=currInfo[1], attackerId=currInfo[0]) + self.__clearLurer(currInfo[0], lureId=currInfo[2]) + + if lureDidDamage: + if self.itemIsCredit(atkTrack, atkLevel): + self.notify.debug('Giving lure EXP to toon ' + str(toonId)) + self.__addAttackExp(attack) + if not validTargetAvail and self.__prevAtkTrack(toonId) != atkTrack: + self.__clearAttack(toonId) + return + + def __getToonTargets(self, attack): + track = self.__getActualTrack(attack) + if track == HEAL or track == PETSOS: + return self.battle.activeToons + else: + return self.battle.activeSuits + + def __attackHasHit(self, attack, suit = 0): + if suit == 1: + for dmg in attack[SUIT_HP_COL]: + if dmg > 0: + return 1 + + return 0 + else: + track = self.__getActualTrack(attack) + return not attack[TOON_ACCBONUS_COL] and track != NO_ATTACK + + def __attackDamage(self, attack, suit = 0): + if suit: + for dmg in attack[SUIT_HP_COL]: + if dmg > 0: + return dmg + + return 0 + else: + for dmg in attack[TOON_HP_COL]: + if dmg > 0: + return dmg + + return 0 + + def __attackDamageForTgt(self, attack, tgtPos, suit = 0): + if suit: + return attack[SUIT_HP_COL][tgtPos] + else: + return attack[TOON_HP_COL][tgtPos] + + def __calcToonAccBonus(self, attackKey): + numPrevHits = 0 + attackIdx = self.toonAtkOrder.index(attackKey) + for currPrevAtk in xrange(attackIdx - 1, -1, -1): + attack = self.battle.toonAttacks[attackKey] + atkTrack, atkLevel = self.__getActualTrackLevel(attack) + prevAttackKey = self.toonAtkOrder[currPrevAtk] + prevAttack = self.battle.toonAttacks[prevAttackKey] + prvAtkTrack, prvAtkLevel = self.__getActualTrackLevel(prevAttack) + if self.__attackHasHit(prevAttack) and (attackAffectsGroup(prvAtkTrack, prvAtkLevel, prevAttack[TOON_TRACK_COL]) or attackAffectsGroup(atkTrack, atkLevel, attack[TOON_TRACK_COL]) or attack[TOON_TGT_COL] == prevAttack[TOON_TGT_COL]) and atkTrack != prvAtkTrack: + numPrevHits += 1 + + if numPrevHits > 0 and self.notify.getDebug(): + self.notify.debug('ACC BONUS: toon attack received accuracy ' + 'bonus of ' + str(self.AccuracyBonuses[numPrevHits]) + ' from previous attack by (' + str(attack[TOON_ID_COL]) + ') which hit') + return self.AccuracyBonuses[numPrevHits] + + def __applyToonAttackDamages(self, toonId, hpbonus = 0, kbbonus = 0): + totalDamages = 0 + if not self.APPLY_HEALTH_ADJUSTMENTS: + return totalDamages + attack = self.battle.toonAttacks[toonId] + track = self.__getActualTrack(attack) + if track != NO_ATTACK and track != SOS and track != TRAP and track != NPCSOS: + targets = self.__getToonTargets(attack) + for position in xrange(len(targets)): + if hpbonus: + if targets[position] in self.__createToonTargetList(toonId): + damageDone = attack[TOON_HPBONUS_COL] + else: + damageDone = 0 + elif kbbonus: + if targets[position] in self.__createToonTargetList(toonId): + damageDone = attack[TOON_KBBONUS_COL][position] + else: + damageDone = 0 + else: + damageDone = attack[TOON_HP_COL][position] + if damageDone <= 0 or self.immortalSuits: + continue + if track == HEAL or track == PETSOS: + currTarget = targets[position] + if self.CAP_HEALS: + toonHp = self.__getToonHp(currTarget) + toonMaxHp = self.__getToonMaxHp(currTarget) + if toonHp + damageDone > toonMaxHp: + damageDone = toonMaxHp - toonHp + attack[TOON_HP_COL][position] = damageDone + self.toonHPAdjusts[currTarget] += damageDone + totalDamages = totalDamages + damageDone + continue + currTarget = targets[position] + currTarget.setHP(currTarget.getHP() - damageDone) + targetId = currTarget.getDoId() + if self.notify.getDebug(): + if hpbonus: + self.notify.debug(str(targetId) + ': suit takes ' + str(damageDone) + ' damage from HP-Bonus') + elif kbbonus: + self.notify.debug(str(targetId) + ': suit takes ' + str(damageDone) + ' damage from KB-Bonus') + else: + self.notify.debug(str(targetId) + ': suit takes ' + str(damageDone) + ' damage') + totalDamages = totalDamages + damageDone + if currTarget.getHP() <= 0: + if currTarget.getSkeleRevives() >= 1: + currTarget.useSkeleRevive() + attack[SUIT_REVIVE_COL] = attack[SUIT_REVIVE_COL] | 1 << position + else: + self.suitLeftBattle(targetId) + attack[SUIT_DIED_COL] = attack[SUIT_DIED_COL] | 1 << position + if self.notify.getDebug(): + self.notify.debug('Suit' + str(targetId) + 'bravely expired in combat') + + return totalDamages + + def __combatantDead(self, avId, toon): + if toon: + if self.__getToonHp(avId) <= 0: + return 1 + else: + suit = self.battle.findSuit(avId) + if suit.getHP() <= 0: + return 1 + return 0 + + def __combatantJustRevived(self, avId): + suit = self.battle.findSuit(avId) + if suit.reviveCheckAndClear(): + return 1 + else: + return 0 + + def __addAttackExp(self, attack, track = -1, level = -1, attackerId = -1): + trk = -1 + lvl = -1 + id = -1 + if track != -1 and level != -1 and attackerId != -1: + trk = track + lvl = level + id = attackerId + elif self.__attackHasHit(attack): + if self.notify.getDebug(): + self.notify.debug('Attack ' + repr(attack) + ' has hit') + trk = attack[TOON_TRACK_COL] + lvl = attack[TOON_LVL_COL] + id = attack[TOON_ID_COL] + if trk != -1 and trk != NPCSOS and trk != PETSOS and lvl != -1 and id != -1: + expList = self.toonSkillPtsGained.get(id, None) + if expList == None: + expList = [0, + 0, + 0, + 0, + 0, + 0, + 0] + self.toonSkillPtsGained[id] = expList + expList[trk] = min(ExperienceCap, expList[trk] + (lvl + 1) * self.__skillCreditMultiplier) + return + + def __clearTgtDied(self, tgt, lastAtk, currAtk): + position = self.battle.activeSuits.index(tgt) + currAtkTrack = self.__getActualTrack(currAtk) + lastAtkTrack = self.__getActualTrack(lastAtk) + if currAtkTrack == lastAtkTrack and lastAtk[SUIT_DIED_COL] & 1 << position and self.__attackHasHit(currAtk, suit=0): + if self.notify.getDebug(): + self.notify.debug('Clearing suit died for ' + str(tgt.getDoId()) + ' at position ' + str(position) + ' from toon attack ' + str(lastAtk[TOON_ID_COL]) + ' and setting it for ' + str(currAtk[TOON_ID_COL])) + lastAtk[SUIT_DIED_COL] = lastAtk[SUIT_DIED_COL] ^ 1 << position + self.suitLeftBattle(tgt.getDoId()) + currAtk[SUIT_DIED_COL] = currAtk[SUIT_DIED_COL] | 1 << position + + def __addDmgToBonuses(self, dmg, attackIndex, hp = 1): + toonId = self.toonAtkOrder[attackIndex] + attack = self.battle.toonAttacks[toonId] + atkTrack = self.__getActualTrack(attack) + if atkTrack == HEAL or atkTrack == PETSOS: + return + tgts = self.__createToonTargetList(toonId) + for currTgt in tgts: + tgtPos = self.battle.activeSuits.index(currTgt) + attackerId = self.toonAtkOrder[attackIndex] + attack = self.battle.toonAttacks[attackerId] + track = self.__getActualTrack(attack) + if hp: + if track in self.hpBonuses[tgtPos]: + self.hpBonuses[tgtPos][track].append([attackIndex, dmg]) + else: + self.hpBonuses[tgtPos][track] = [[attackIndex, dmg]] + elif self.__suitIsLured(currTgt.getDoId()): + if track in self.kbBonuses[tgtPos]: + self.kbBonuses[tgtPos][track].append([attackIndex, dmg]) + else: + self.kbBonuses[tgtPos][track] = [[attackIndex, dmg]] + + def __clearBonuses(self, hp = 1): + if hp: + self.hpBonuses = [{}, + {}, + {}, + {}] + else: + self.kbBonuses = [{}, + {}, + {}, + {}] + + def __bonusExists(self, tgtSuit, hp = 1): + tgtPos = self.activeSuits.index(tgtSuit) + if hp: + bonusLen = len(self.hpBonuses[tgtPos]) + else: + bonusLen = len(self.kbBonuses[tgtPos]) + if bonusLen > 0: + return 1 + return 0 + + def __processBonuses(self, hp = 1): + if hp: + bonusList = self.hpBonuses + self.notify.debug('Processing hpBonuses: ' + repr(self.hpBonuses)) + else: + bonusList = self.kbBonuses + self.notify.debug('Processing kbBonuses: ' + repr(self.kbBonuses)) + tgtPos = 0 + for currTgt in bonusList: + for currAtkType in currTgt.keys(): + if len(currTgt[currAtkType]) > 1 or not hp and len(currTgt[currAtkType]) > 0: + totalDmgs = 0 + for currDmg in currTgt[currAtkType]: + totalDmgs += currDmg[1] + + numDmgs = len(currTgt[currAtkType]) + attackIdx = currTgt[currAtkType][numDmgs - 1][0] + attackerId = self.toonAtkOrder[attackIdx] + attack = self.battle.toonAttacks[attackerId] + if hp: + attack[TOON_HPBONUS_COL] = math.ceil(totalDmgs * (self.DamageBonuses[numDmgs - 1] * 0.01)) + if self.notify.getDebug(): + self.notify.debug('Applying hp bonus to track ' + str(attack[TOON_TRACK_COL]) + ' of ' + str(attack[TOON_HPBONUS_COL])) + elif len(attack[TOON_KBBONUS_COL]) > tgtPos: + attack[TOON_KBBONUS_COL][tgtPos] = totalDmgs * 0.5 + if self.notify.getDebug(): + self.notify.debug('Applying kb bonus to track ' + str(attack[TOON_TRACK_COL]) + ' of ' + str(attack[TOON_KBBONUS_COL][tgtPos]) + ' to target ' + str(tgtPos)) + else: + self.notify.warning('invalid tgtPos for knock back bonus: %d' % tgtPos) + + tgtPos += 1 + + if hp: + self.__clearBonuses() + else: + self.__clearBonuses(hp=0) + + def __handleBonus(self, attackIdx, hp = 1): + attackerId = self.toonAtkOrder[attackIdx] + attack = self.battle.toonAttacks[attackerId] + atkDmg = self.__attackDamage(attack, suit=0) + atkTrack = self.__getActualTrack(attack) + if atkDmg > 0: + if hp: + if atkTrack != LURE: + self.notify.debug('Adding dmg of ' + str(atkDmg) + ' to hpBonuses list') + self.__addDmgToBonuses(atkDmg, attackIdx) + elif self.__knockBackAtk(attackerId, toon=1): + self.notify.debug('Adding dmg of ' + str(atkDmg) + ' to kbBonuses list') + self.__addDmgToBonuses(atkDmg, attackIdx, hp=0) + + def __clearAttack(self, attackIdx, toon = 1): + if toon: + if self.notify.getDebug(): + self.notify.debug('clearing out toon attack for toon ' + str(attackIdx) + '...') + attack = self.battle.toonAttacks[attackIdx] + self.battle.toonAttacks[attackIdx] = getToonAttack(attackIdx) + longest = max(len(self.battle.activeToons), len(self.battle.activeSuits)) + taList = self.battle.toonAttacks + for j in xrange(longest): + taList[attackIdx][TOON_HP_COL].append(-1) + taList[attackIdx][TOON_KBBONUS_COL].append(-1) + + if self.notify.getDebug(): + self.notify.debug('toon attack is now ' + repr(self.battle.toonAttacks[attackIdx])) + else: + self.notify.warning('__clearAttack not implemented for suits!') + + def __rememberToonAttack(self, suitId, toonId, damage): + if not suitId in self.SuitAttackers: + self.SuitAttackers[suitId] = {toonId: damage} + elif not toonId in self.SuitAttackers[suitId]: + self.SuitAttackers[suitId][toonId] = damage + elif self.SuitAttackers[suitId][toonId] <= damage: + self.SuitAttackers[suitId] = [toonId, damage] + + def __postProcessToonAttacks(self): + self.notify.debug('__postProcessToonAttacks()') + lastTrack = -1 + lastAttacks = [] + self.__clearBonuses() + for currToonAttack in self.toonAtkOrder: + if currToonAttack != -1: + attack = self.battle.toonAttacks[currToonAttack] + atkTrack, atkLevel = self.__getActualTrackLevel(attack) + if atkTrack != HEAL and atkTrack != SOS and atkTrack != NO_ATTACK and atkTrack != NPCSOS and atkTrack != PETSOS: + targets = self.__createToonTargetList(currToonAttack) + allTargetsDead = 1 + for currTgt in targets: + damageDone = self.__attackDamage(attack, suit=0) + if damageDone > 0: + self.__rememberToonAttack(currTgt.getDoId(), attack[TOON_ID_COL], damageDone) + if atkTrack == TRAP: + if currTgt.doId in self.traps: + trapInfo = self.traps[currTgt.doId] + currTgt.battleTrap = trapInfo[0] + targetDead = 0 + if currTgt.getHP() > 0: + allTargetsDead = 0 + else: + targetDead = 1 + if atkTrack != LURE: + for currLastAtk in lastAttacks: + self.__clearTgtDied(currTgt, currLastAtk, attack) + + tgtId = currTgt.getDoId() + if tgtId in self.successfulLures and atkTrack == LURE: + lureInfo = self.successfulLures[tgtId] + self.notify.debug('applying lure data: ' + repr(lureInfo)) + toonId = lureInfo[0] + lureAtk = self.battle.toonAttacks[toonId] + tgtPos = self.battle.activeSuits.index(currTgt) + if currTgt.doId in self.traps: + trapInfo = self.traps[currTgt.doId] + if trapInfo[0] == UBER_GAG_LEVEL_INDEX: + self.notify.debug('train trap triggered for %d' % currTgt.doId) + self.trainTrapTriggered = True + self.__removeSuitTrap(tgtId) + lureAtk[TOON_KBBONUS_COL][tgtPos] = self.KBBONUS_TGT_LURED + lureAtk[TOON_HP_COL][tgtPos] = lureInfo[3] + elif self.__suitIsLured(tgtId) and atkTrack == DROP: + self.notify.debug('Drop on lured suit, ' + 'indicating with KBBONUS_COL ' + 'flag') + tgtPos = self.battle.activeSuits.index(currTgt) + attack[TOON_KBBONUS_COL][tgtPos] = self.KBBONUS_LURED_FLAG + if targetDead and atkTrack != lastTrack: + tgtPos = self.battle.activeSuits.index(currTgt) + attack[TOON_HP_COL][tgtPos] = 0 + attack[TOON_KBBONUS_COL][tgtPos] = -1 + + if allTargetsDead and atkTrack != lastTrack: + if self.notify.getDebug(): + self.notify.debug('all targets of toon attack ' + str(currToonAttack) + ' are dead') + self.__clearAttack(currToonAttack, toon=1) + attack = self.battle.toonAttacks[currToonAttack] + atkTrack, atkLevel = self.__getActualTrackLevel(attack) + damagesDone = self.__applyToonAttackDamages(currToonAttack) + self.__applyToonAttackDamages(currToonAttack, hpbonus=1) + if atkTrack != LURE and atkTrack != DROP and atkTrack != SOUND: + self.__applyToonAttackDamages(currToonAttack, kbbonus=1) + if lastTrack != atkTrack: + lastAttacks = [] + lastTrack = atkTrack + lastAttacks.append(attack) + if self.itemIsCredit(atkTrack, atkLevel): + if atkTrack == TRAP or atkTrack == LURE: + pass + elif atkTrack == HEAL: + if damagesDone != 0: + self.__addAttackExp(attack) + else: + self.__addAttackExp(attack) + + if self.trainTrapTriggered: + for suit in self.battle.activeSuits: + suitId = suit.doId + self.__removeSuitTrap(suitId) + suit.battleTrap = NO_TRAP + self.notify.debug('train trap triggered, removing trap from %d' % suitId) + + if self.notify.getDebug(): + for currToonAttack in self.toonAtkOrder: + attack = self.battle.toonAttacks[currToonAttack] + self.notify.debug('Final Toon attack: ' + str(attack)) + + def __allTargetsDead(self, attackIdx, toon = 1): + allTargetsDead = 1 + if toon: + targets = self.__createToonTargetList(attackIdx) + for currTgt in targets: + if currTgt.getHp() > 0: + allTargetsDead = 0 + break + + else: + self.notify.warning('__allTargetsDead: suit ver. not implemented!') + return allTargetsDead + + def __clearLuredSuitsByAttack(self, toonId, kbBonusReq = 0, targetId = -1): + if self.notify.getDebug(): + self.notify.debug('__clearLuredSuitsByAttack') + if targetId != -1 and self.__suitIsLured(t.getDoId()): + self.__removeLured(t.getDoId()) + else: + tgtList = self.__createToonTargetList(toonId) + for t in tgtList: + if self.__suitIsLured(t.getDoId()) and (not kbBonusReq or self.__bonusExists(t, hp=0)): + self.__removeLured(t.getDoId()) + if self.notify.getDebug(): + self.notify.debug('Suit %d stepping from lured spot' % t.getDoId()) + else: + self.notify.debug('Suit ' + str(t.getDoId()) + ' not found in currently lured suits') + + def __clearLuredSuitsDelayed(self): + if self.notify.getDebug(): + self.notify.debug('__clearLuredSuitsDelayed') + for t in self.delayedUnlures: + if self.__suitIsLured(t): + self.__removeLured(t) + if self.notify.getDebug(): + self.notify.debug('Suit %d stepping back from lured spot' % t) + else: + self.notify.debug('Suit ' + str(t) + ' not found in currently lured suits') + + self.delayedUnlures = [] + + def __addLuredSuitsDelayed(self, toonId, targetId = -1, ignoreDamageCheck = False): + if self.notify.getDebug(): + self.notify.debug('__addLuredSuitsDelayed') + if targetId != -1: + self.delayedUnlures.append(targetId) + else: + tgtList = self.__createToonTargetList(toonId) + for t in tgtList: + if self.__suitIsLured(t.getDoId()) and t.getDoId() not in self.delayedUnlures and (self.__attackDamageForTgt(self.battle.toonAttacks[toonId], self.battle.activeSuits.index(t), suit=0) > 0 or ignoreDamageCheck): + self.delayedUnlures.append(t.getDoId()) + + def __calculateToonAttacks(self): + self.notify.debug('__calculateToonAttacks()') + self.__clearBonuses(hp=0) + currTrack = None + self.notify.debug('Traps: ' + str(self.traps)) + maxSuitLevel = 0 + for cog in self.battle.activeSuits: + maxSuitLevel = max(maxSuitLevel, cog.getActualLevel()) + + self.creditLevel = maxSuitLevel + for toonId in self.toonAtkOrder: + if self.__combatantDead(toonId, toon=1): + if self.notify.getDebug(): + self.notify.debug("Toon %d is dead and can't attack" % toonId) + continue + attack = self.battle.toonAttacks[toonId] + atkTrack = self.__getActualTrack(attack) + if atkTrack != NO_ATTACK and atkTrack != SOS and atkTrack != NPCSOS: + if self.notify.getDebug(): + self.notify.debug('Calculating attack for toon: %d' % toonId) + if self.SUITS_UNLURED_IMMEDIATELY: + if currTrack and atkTrack != currTrack: + self.__clearLuredSuitsDelayed() + currTrack = atkTrack + self.__calcToonAtkHp(toonId) + attackIdx = self.toonAtkOrder.index(toonId) + self.__handleBonus(attackIdx, hp=0) + self.__handleBonus(attackIdx, hp=1) + lastAttack = self.toonAtkOrder.index(toonId) >= len(self.toonAtkOrder) - 1 + unlureAttack = self.__attackHasHit(attack, suit=0) and self.__unlureAtk(toonId, toon=1) + if unlureAttack: + if lastAttack: + self.__clearLuredSuitsByAttack(toonId) + else: + self.__addLuredSuitsDelayed(toonId) + if lastAttack: + self.__clearLuredSuitsDelayed() + + self.__processBonuses(hp=0) + self.__processBonuses(hp=1) + self.__postProcessToonAttacks() + return + + def __knockBackAtk(self, attackIndex, toon = 1): + if toon and (self.battle.toonAttacks[attackIndex][TOON_TRACK_COL] == THROW or self.battle.toonAttacks[attackIndex][TOON_TRACK_COL] == SQUIRT): + if self.notify.getDebug(): + self.notify.debug('attack is a knockback') + return 1 + return 0 + + def __unlureAtk(self, attackIndex, toon = 1): + attack = self.battle.toonAttacks[attackIndex] + track = self.__getActualTrack(attack) + if toon and (track == THROW or track == SQUIRT or track == SOUND): + if self.notify.getDebug(): + self.notify.debug('attack is an unlure') + return 1 + return 0 + + def __calcSuitAtkType(self, attackIndex): + theSuit = self.battle.activeSuits[attackIndex] + attacks = SuitBattleGlobals.SuitAttributes[theSuit.dna.name]['attacks'] + atk = SuitBattleGlobals.pickSuitAttack(attacks, theSuit.getLevel()) + return atk + + def __calcSuitTarget(self, attackIndex): + attack = self.battle.suitAttacks[attackIndex] + suitId = attack[SUIT_ID_COL] + if suitId in self.SuitAttackers and random.randint(0, 99) < 75: + totalDamage = 0 + for currToon in self.SuitAttackers[suitId].keys(): + totalDamage += self.SuitAttackers[suitId][currToon] + + dmgs = [] + for currToon in self.SuitAttackers[suitId].keys(): + dmgs.append(self.SuitAttackers[suitId][currToon] / totalDamage * 100) + + dmgIdx = SuitBattleGlobals.pickFromFreqList(dmgs) + if dmgIdx == None: + toonId = self.__pickRandomToon(suitId) + else: + toonId = self.SuitAttackers[suitId].keys()[dmgIdx] + if toonId == -1 or toonId not in self.battle.activeToons: + return -1 + self.notify.debug('Suit attacking back at toon ' + str(toonId)) + return self.battle.activeToons.index(toonId) + else: + return self.__pickRandomToon(suitId) + return + + def __pickRandomToon(self, suitId): + liveToons = [] + for currToon in self.battle.activeToons: + if not self.__combatantDead(currToon, toon=1): + liveToons.append(self.battle.activeToons.index(currToon)) + + if len(liveToons) == 0: + self.notify.debug('No tgts avail. for suit ' + str(suitId)) + return -1 + chosen = random.choice(liveToons) + self.notify.debug('Suit randomly attacking toon ' + str(self.battle.activeToons[chosen])) + return chosen + + def __suitAtkHit(self, attackIndex): + if self.suitsAlwaysHit: + return 1 + elif self.suitsAlwaysMiss: + return 0 + theSuit = self.battle.activeSuits[attackIndex] + atkType = self.battle.suitAttacks[attackIndex][SUIT_ATK_COL] + atkInfo = SuitBattleGlobals.getSuitAttack(theSuit.dna.name, theSuit.getLevel(), atkType) + atkAcc = atkInfo['acc'] + suitAcc = SuitBattleGlobals.SuitAttributes[theSuit.dna.name]['acc'][theSuit.getLevel()] + acc = atkAcc + randChoice = random.randint(0, 99) + if self.notify.getDebug(): + self.notify.debug('Suit attack rolled ' + str(randChoice) + ' to hit with an accuracy of ' + str(acc) + ' (attackAcc: ' + str(atkAcc) + ' suitAcc: ' + str(suitAcc) + ')') + if randChoice < acc: + return 1 + return 0 + + def __suitAtkAffectsGroup(self, attack): + atkType = attack[SUIT_ATK_COL] + theSuit = self.battle.findSuit(attack[SUIT_ID_COL]) + atkInfo = SuitBattleGlobals.getSuitAttack(theSuit.dna.name, theSuit.getLevel(), atkType) + return atkInfo['group'] != SuitBattleGlobals.ATK_TGT_SINGLE + + def __createSuitTargetList(self, attackIndex): + attack = self.battle.suitAttacks[attackIndex] + targetList = [] + if attack[SUIT_ATK_COL] == NO_ATTACK: + self.notify.debug('No attack, no targets') + return targetList + debug = self.notify.getDebug() + if not self.__suitAtkAffectsGroup(attack): + targetList.append(self.battle.activeToons[attack[SUIT_TGT_COL]]) + if debug: + self.notify.debug('Suit attack is single target') + else: + if debug: + self.notify.debug('Suit attack is group target') + for currToon in self.battle.activeToons: + if debug: + self.notify.debug('Suit attack will target toon' + str(currToon)) + targetList.append(currToon) + + return targetList + + def __calcSuitAtkHp(self, attackIndex): + targetList = self.__createSuitTargetList(attackIndex) + attack = self.battle.suitAttacks[attackIndex] + for currTarget in xrange(len(targetList)): + toonId = targetList[currTarget] + toon = self.battle.getToon(toonId) + result = 0 + if (toon and toon.immortalMode) or self.TOONS_TAKE_NO_DAMAGE: + result = 0 + elif self.__suitAtkHit(attackIndex): + atkType = attack[SUIT_ATK_COL] + theSuit = self.battle.findSuit(attack[SUIT_ID_COL]) + atkInfo = SuitBattleGlobals.getSuitAttack(theSuit.dna.name, theSuit.getLevel(), atkType) + result = atkInfo['hp'] + targetIndex = self.battle.activeToons.index(toonId) + attack[SUIT_HP_COL][targetIndex] = result + + def __getToonHp(self, toonDoId): + handle = self.battle.getToon(toonDoId) + if handle != None and toonDoId in self.toonHPAdjusts: + return handle.hp + self.toonHPAdjusts[toonDoId] + else: + return 0 + return + + def __getToonMaxHp(self, toonDoId): + handle = self.battle.getToon(toonDoId) + if handle != None: + return handle.maxHp + else: + return 0 + return + + def __applySuitAttackDamages(self, attackIndex): + attack = self.battle.suitAttacks[attackIndex] + if self.APPLY_HEALTH_ADJUSTMENTS: + for t in self.battle.activeToons: + position = self.battle.activeToons.index(t) + if attack[SUIT_HP_COL][position] <= 0: + continue + toonHp = self.__getToonHp(t) + if toonHp - attack[SUIT_HP_COL][position] <= 0: + if self.notify.getDebug(): + self.notify.debug('Toon %d has died, removing' % t) + self.toonLeftBattle(t) + attack[TOON_DIED_COL] = attack[TOON_DIED_COL] | 1 << position + if self.notify.getDebug(): + self.notify.debug('Toon ' + str(t) + ' takes ' + str(attack[SUIT_HP_COL][position]) + ' damage') + self.toonHPAdjusts[t] -= attack[SUIT_HP_COL][position] + self.notify.debug('Toon ' + str(t) + ' now has ' + str(self.__getToonHp(t)) + ' health') + + def __suitCanAttack(self, suitId): + if self.__combatantDead(suitId, toon=0) or self.__suitIsLured(suitId) or self.__combatantJustRevived(suitId): + return 0 + return 1 + + def __updateSuitAtkStat(self, toonId): + if toonId in self.suitAtkStats: + self.suitAtkStats[toonId] += 1 + else: + self.suitAtkStats[toonId] = 1 + + def __printSuitAtkStats(self): + self.notify.debug('Suit Atk Stats:') + for currTgt in self.suitAtkStats.keys(): + if currTgt not in self.battle.activeToons: + continue + tgtPos = self.battle.activeToons.index(currTgt) + self.notify.debug(' toon ' + str(currTgt) + ' at position ' + str(tgtPos) + ' was attacked ' + str(self.suitAtkStats[currTgt]) + ' times') + + self.notify.debug('\n') + + def __calculateSuitAttacks(self): + for i in xrange(len(self.battle.suitAttacks)): + if i < len(self.battle.activeSuits): + suitId = self.battle.activeSuits[i].doId + self.battle.suitAttacks[i][SUIT_ID_COL] = suitId + if not self.__suitCanAttack(suitId): + if self.notify.getDebug(): + self.notify.debug("Suit %d can't attack" % suitId) + continue + if self.battle.pendingSuits.count(self.battle.activeSuits[i]) > 0 or self.battle.joiningSuits.count(self.battle.activeSuits[i]) > 0: + continue + attack = self.battle.suitAttacks[i] + attack[SUIT_ID_COL] = self.battle.activeSuits[i].doId + attack[SUIT_ATK_COL] = self.__calcSuitAtkType(i) + attack[SUIT_TGT_COL] = self.__calcSuitTarget(i) + if attack[SUIT_TGT_COL] == -1: + self.battle.suitAttacks[i] = getDefaultSuitAttack() + attack = self.battle.suitAttacks[i] + self.notify.debug('clearing suit attack, no avail targets') + self.__calcSuitAtkHp(i) + if attack[SUIT_ATK_COL] != NO_ATTACK: + if self.__suitAtkAffectsGroup(attack): + for currTgt in self.battle.activeToons: + self.__updateSuitAtkStat(currTgt) + + else: + tgtId = self.battle.activeToons[attack[SUIT_TGT_COL]] + self.__updateSuitAtkStat(tgtId) + targets = self.__createSuitTargetList(i) + allTargetsDead = 1 + for currTgt in targets: + if self.__getToonHp(currTgt) > 0: + allTargetsDead = 0 + break + + if allTargetsDead: + self.battle.suitAttacks[i] = getDefaultSuitAttack() + if self.notify.getDebug(): + self.notify.debug('clearing suit attack, targets dead') + self.notify.debug('suit attack is now ' + repr(self.battle.suitAttacks[i])) + self.notify.debug('all attacks: ' + repr(self.battle.suitAttacks)) + attack = self.battle.suitAttacks[i] + if self.__attackHasHit(attack, suit=1): + self.__applySuitAttackDamages(i) + if self.notify.getDebug(): + self.notify.debug('Suit attack: ' + str(self.battle.suitAttacks[i])) + attack[SUIT_BEFORE_TOONS_COL] = 0 + + def __updateLureTimeouts(self): + if self.notify.getDebug(): + self.notify.debug('__updateLureTimeouts()') + self.notify.debug('Lured suits: ' + str(self.currentlyLuredSuits)) + noLongerLured = [] + for currLuredSuit in self.currentlyLuredSuits.keys(): + self.__incLuredCurrRound(currLuredSuit) + if self.__luredMaxRoundsReached(currLuredSuit) or self.__luredWakeupTime(currLuredSuit): + noLongerLured.append(currLuredSuit) + + for currLuredSuit in noLongerLured: + self.__removeLured(currLuredSuit) + + if self.notify.getDebug(): + self.notify.debug('Lured suits: ' + str(self.currentlyLuredSuits)) + + def __initRound(self): + if self.CLEAR_SUIT_ATTACKERS: + self.SuitAttackers = {} + self.toonAtkOrder = [] + attacks = findToonAttack(self.battle.activeToons, self.battle.toonAttacks, PETSOS) + for atk in attacks: + self.toonAtkOrder.append(atk[TOON_ID_COL]) + + attacks = findToonAttack(self.battle.activeToons, self.battle.toonAttacks, FIRE) + for atk in attacks: + self.toonAtkOrder.append(atk[TOON_ID_COL]) + + for track in xrange(HEAL, DROP + 1): + attacks = findToonAttack(self.battle.activeToons, self.battle.toonAttacks, track) + if track == TRAP: + sortedTraps = [] + for atk in attacks: + if atk[TOON_TRACK_COL] == TRAP: + sortedTraps.append(atk) + + for atk in attacks: + if atk[TOON_TRACK_COL] == NPCSOS: + sortedTraps.append(atk) + + attacks = sortedTraps + for atk in attacks: + self.toonAtkOrder.append(atk[TOON_ID_COL]) + + specials = findToonAttack(self.battle.activeToons, self.battle.toonAttacks, NPCSOS) + toonsHit = 0 + cogsMiss = 0 + for special in specials: + npc_track = NPCToons.getNPCTrack(special[TOON_TGT_COL]) + if npc_track == NPC_TOONS_HIT: + BattleCalculatorAI.toonsAlwaysHit = 1 + toonsHit = 1 + elif npc_track == NPC_COGS_MISS: + BattleCalculatorAI.suitsAlwaysMiss = 1 + cogsMiss = 1 + + if self.notify.getDebug(): + self.notify.debug('Toon attack order: ' + str(self.toonAtkOrder)) + self.notify.debug('Active toons: ' + str(self.battle.activeToons)) + self.notify.debug('Toon attacks: ' + str(self.battle.toonAttacks)) + self.notify.debug('Active suits: ' + str(self.battle.activeSuits)) + self.notify.debug('Suit attacks: ' + str(self.battle.suitAttacks)) + self.toonHPAdjusts = {} + for t in self.battle.activeToons: + self.toonHPAdjusts[t] = 0 + + self.__clearBonuses() + self.__updateActiveToons() + self.delayedUnlures = [] + self.__initTraps() + self.successfulLures = {} + return (toonsHit, cogsMiss) + + def calculateRound(self): + longest = max(len(self.battle.activeToons), len(self.battle.activeSuits)) + for t in self.battle.activeToons: + for j in xrange(longest): + self.battle.toonAttacks[t][TOON_HP_COL].append(-1) + self.battle.toonAttacks[t][TOON_KBBONUS_COL].append(-1) + + for i in xrange(4): + for j in xrange(len(self.battle.activeToons)): + self.battle.suitAttacks[i][SUIT_HP_COL].append(-1) + + toonsHit, cogsMiss = self.__initRound() + for suit in self.battle.activeSuits: + if suit.isGenerated(): + suit.b_setHP(suit.getHP()) + + for suit in self.battle.activeSuits: + if not hasattr(suit, 'dna'): + self.notify.warning('a removed suit is in this battle!') + return None + + self.__calculateToonAttacks() + self.__updateLureTimeouts() + self.__calculateSuitAttacks() + if toonsHit == 1: + BattleCalculatorAI.toonsAlwaysHit = 0 + if cogsMiss == 1: + BattleCalculatorAI.suitsAlwaysMiss = 0 + if self.notify.getDebug(): + self.notify.debug('Toon skills gained after this round: ' + repr(self.toonSkillPtsGained)) + self.__printSuitAtkStats() + return None + + def toonLeftBattle(self, toonId): + if self.notify.getDebug(): + self.notify.debug('toonLeftBattle()' + str(toonId)) + if toonId in self.toonSkillPtsGained: + del self.toonSkillPtsGained[toonId] + if toonId in self.suitAtkStats: + del self.suitAtkStats[toonId] + if not self.CLEAR_SUIT_ATTACKERS: + oldSuitIds = [] + for s in self.SuitAttackers.keys(): + if toonId in self.SuitAttackers[s]: + del self.SuitAttackers[s][toonId] + if len(self.SuitAttackers[s]) == 0: + oldSuitIds.append(s) + + for oldSuitId in oldSuitIds: + del self.SuitAttackers[oldSuitId] + + self.__clearTrapCreator(toonId) + self.__clearLurer(toonId) + + def suitLeftBattle(self, suitId): + if self.notify.getDebug(): + self.notify.debug('suitLeftBattle(): ' + str(suitId)) + self.__removeLured(suitId) + if suitId in self.SuitAttackers: + del self.SuitAttackers[suitId] + self.__removeSuitTrap(suitId) + + def __updateActiveToons(self): + if self.notify.getDebug(): + self.notify.debug('updateActiveToons()') + if not self.CLEAR_SUIT_ATTACKERS: + oldSuitIds = [] + for s in self.SuitAttackers.keys(): + for t in self.SuitAttackers[s].keys(): + if t not in self.battle.activeToons: + del self.SuitAttackers[s][t] + if len(self.SuitAttackers[s]) == 0: + oldSuitIds.append(s) + + for oldSuitId in oldSuitIds: + del self.SuitAttackers[oldSuitId] + + for trap in self.traps.keys(): + if self.traps[trap][1] not in self.battle.activeToons: + self.notify.debug('Trap for toon ' + str(self.traps[trap][1]) + ' will no longer give exp') + self.traps[trap][1] = 0 + + def getSkillGained(self, toonId, track): + return BattleExperienceAI.getSkillGained(self.toonSkillPtsGained, toonId, track) + + def getLuredSuits(self): + luredSuits = self.currentlyLuredSuits.keys() + self.notify.debug('Lured suits reported to battle: ' + repr(luredSuits)) + return luredSuits + + def __suitIsLured(self, suitId, prevRound = 0): + inList = suitId in self.currentlyLuredSuits + if prevRound: + return inList and self.currentlyLuredSuits[suitId][0] != -1 + return inList + + def __findAvailLureId(self, lurerId): + luredSuits = self.currentlyLuredSuits.keys() + lureIds = [] + for currLured in luredSuits: + lurerInfo = self.currentlyLuredSuits[currLured][3] + lurers = lurerInfo.keys() + for currLurer in lurers: + currId = lurerInfo[currLurer][1] + if currLurer == lurerId and currId not in lureIds: + lureIds.append(currId) + + lureIds.sort() + currId = 1 + for currLureId in lureIds: + if currLureId != currId: + return currId + currId += 1 + + return currId + + def __addLuredSuitInfo(self, suitId, currRounds, maxRounds, wakeChance, lurer, lureLvl, lureId = -1, npc = 0): + if lureId == -1: + availLureId = self.__findAvailLureId(lurer) + else: + availLureId = lureId + if npc == 1: + credit = 0 + else: + credit = self.itemIsCredit(LURE, lureLvl) + if suitId in self.currentlyLuredSuits: + lureInfo = self.currentlyLuredSuits[suitId] + if not lurer in lureInfo[3]: + lureInfo[1] += maxRounds + if wakeChance < lureInfo[2]: + lureInfo[2] = wakeChance + lureInfo[3][lurer] = [lureLvl, availLureId, credit] + else: + lurerInfo = {lurer: [lureLvl, availLureId, credit]} + self.currentlyLuredSuits[suitId] = [currRounds, + maxRounds, + wakeChance, + lurerInfo] + self.notify.debug('__addLuredSuitInfo: currLuredSuits -> %s' % repr(self.currentlyLuredSuits)) + return availLureId + + def __getLurers(self, suitId): + if self.__suitIsLured(suitId): + return self.currentlyLuredSuits[suitId][3].keys() + return [] + + def __getLuredExpInfo(self, suitId): + returnInfo = [] + lurers = self.__getLurers(suitId) + if len(lurers) == 0: + return returnInfo + lurerInfo = self.currentlyLuredSuits[suitId][3] + for currLurer in lurers: + returnInfo.append([currLurer, + lurerInfo[currLurer][0], + lurerInfo[currLurer][1], + lurerInfo[currLurer][2]]) + + return returnInfo + + def __clearLurer(self, lurerId, lureId = -1): + luredSuits = self.currentlyLuredSuits.keys() + for currLured in luredSuits: + lurerInfo = self.currentlyLuredSuits[currLured][3] + lurers = lurerInfo.keys() + for currLurer in lurers: + if currLurer == lurerId and (lureId == -1 or lureId == lurerInfo[currLurer][1]): + del lurerInfo[currLurer] + + def __setLuredMaxRounds(self, suitId, rounds): + if self.__suitIsLured(suitId): + self.currentlyLuredSuits[suitId][1] = rounds + + def __setLuredWakeChance(self, suitId, chance): + if self.__suitIsLured(suitId): + self.currentlyLuredSuits[suitId][2] = chance + + def __incLuredCurrRound(self, suitId): + if self.__suitIsLured(suitId): + self.currentlyLuredSuits[suitId][0] += 1 + + def __removeLured(self, suitId): + if self.__suitIsLured(suitId): + del self.currentlyLuredSuits[suitId] + + def __luredMaxRoundsReached(self, suitId): + return self.__suitIsLured(suitId) and self.currentlyLuredSuits[suitId][0] >= self.currentlyLuredSuits[suitId][1] + + def __luredWakeupTime(self, suitId): + return self.__suitIsLured(suitId) and self.currentlyLuredSuits[suitId][0] > 0 and random.randint(0, 99) < self.currentlyLuredSuits[suitId][2] + + def itemIsCredit(self, track, level): + return track != PETSOS and level < self.creditLevel + + def __getActualTrack(self, toonAttack): + if toonAttack[TOON_TRACK_COL] == NPCSOS: + track = NPCToons.getNPCTrack(toonAttack[TOON_TGT_COL]) + if track != None: + return track + else: + self.notify.warning('No NPC with id: %d' % toonAttack[TOON_TGT_COL]) + return toonAttack[TOON_TRACK_COL] + + def __getActualTrackLevel(self, toonAttack): + if toonAttack[TOON_TRACK_COL] == NPCSOS: + track, level, hp = NPCToons.getNPCTrackLevelHp(toonAttack[TOON_TGT_COL]) + if track != None: + return (track, level) + else: + self.notify.warning('No NPC with id: %d' % toonAttack[TOON_TGT_COL]) + return (toonAttack[TOON_TRACK_COL], toonAttack[TOON_LVL_COL]) + + def __getActualTrackLevelHp(self, toonAttack): + if toonAttack[TOON_TRACK_COL] == NPCSOS: + track, level, hp = NPCToons.getNPCTrackLevelHp(toonAttack[TOON_TGT_COL]) + if track != None: + return (track, level, hp) + else: + self.notify.warning('No NPC with id: %d' % toonAttack[TOON_TGT_COL]) + elif toonAttack[TOON_TRACK_COL] == PETSOS: + trick = toonAttack[TOON_LVL_COL] + petProxyId = toonAttack[TOON_TGT_COL] + trickId = toonAttack[TOON_LVL_COL] + healRange = PetTricks.TrickHeals[trickId] + hp = 0 + if petProxyId in simbase.air.doId2do: + petProxy = simbase.air.doId2do[petProxyId] + if trickId < len(petProxy.trickAptitudes): + aptitude = petProxy.trickAptitudes[trickId] + hp = int(lerp(healRange[0], healRange[1], aptitude)) + else: + self.notify.warning('pet proxy: %d not in doId2do!' % petProxyId) + return (toonAttack[TOON_TRACK_COL], toonAttack[TOON_LVL_COL], hp) + return (toonAttack[TOON_TRACK_COL], toonAttack[TOON_LVL_COL], 0) + + def __calculatePetTrickSuccess(self, toonAttack): + petProxyId = toonAttack[TOON_TGT_COL] + if not petProxyId in simbase.air.doId2do: + self.notify.warning('pet proxy %d not in doId2do!' % petProxyId) + toonAttack[TOON_ACCBONUS_COL] = 1 + return (0, 0) + petProxy = simbase.air.doId2do[petProxyId] + trickId = toonAttack[TOON_LVL_COL] + toonAttack[TOON_ACCBONUS_COL] = petProxy.attemptBattleTrick(trickId) + if toonAttack[TOON_ACCBONUS_COL] == 1: + return (0, 0) + else: + return (1, 100) diff --git a/toontown/battle/BattleExperience.py b/toontown/battle/BattleExperience.py new file mode 100755 index 00000000..61ce72f8 --- /dev/null +++ b/toontown/battle/BattleExperience.py @@ -0,0 +1,22 @@ +from toontown.toonbase import ToontownBattleGlobals + +def genRewardDicts(entries): + toonRewardDicts = [] + for toonId, origExp, earnedExp, origQuests, items, missedItems, origMerits, merits, parts in entries: + if toonId != -1: + dict = {} + toon = base.cr.doId2do.get(toonId) + if toon == None: + continue + dict['toon'] = toon + dict['origExp'] = origExp + dict['earnedExp'] = earnedExp + dict['origQuests'] = origQuests + dict['items'] = items + dict['missedItems'] = missedItems + dict['origMerits'] = origMerits + dict['merits'] = merits + dict['parts'] = parts + toonRewardDicts.append(dict) + + return toonRewardDicts diff --git a/toontown/battle/BattleExperienceAI.py b/toontown/battle/BattleExperienceAI.py new file mode 100755 index 00000000..573b6793 --- /dev/null +++ b/toontown/battle/BattleExperienceAI.py @@ -0,0 +1,197 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownBattleGlobals, ToontownGlobals +from toontown.suit import SuitDNA +BattleExperienceAINotify = DirectNotifyGlobal.directNotify.newCategory('BattleExprienceAI') + +def getSkillGained(toonSkillPtsGained, toonId, track): + exp = 0 + expList = toonSkillPtsGained.get(toonId, None) + if expList != None: + exp = expList[track] + return int(exp + 0.5) + +def getBattleExperience(numToons, activeToons, toonExp, toonSkillPtsGained, toonOrigQuests, toonItems, toonOrigMerits, toonMerits, toonParts, suitsKilled, helpfulToonsList = None): + if helpfulToonsList == None: + BattleExperienceAINotify.warning('=============\nERROR ERROR helpfulToons=None in assignRewards , tell Red') + p = [] + for k in xrange(numToons): + toon = None + if k < len(activeToons): + toonId = activeToons[k] + toon = simbase.air.doId2do.get(toonId) + if toon == None: + p.append(-1) + p.append([0, + 0, + 0, + 0, + 0, + 0, + 0]) + p.append([0, + 0, + 0, + 0, + 0, + 0, + 0]) + p.append([]) + p.append([]) + p.append([]) + p.append([0, + 0, + 0, + 0]) + p.append([0, + 0, + 0, + 0]) + p.append([0, + 0, + 0, + 0]) + else: + p.append(toonId) + origExp = toonExp[toonId] + earnedExp = [] + for i in xrange(len(ToontownBattleGlobals.Tracks)): + earnedExp.append(getSkillGained(toonSkillPtsGained, toonId, i)) + + p.append(origExp) + p.append(earnedExp) + origQuests = toonOrigQuests.get(toonId, []) + p.append(origQuests) + items = toonItems.get(toonId, ([], [])) + p.append(items[0]) + p.append(items[1]) + origMerits = toonOrigMerits.get(toonId, []) + p.append(origMerits) + merits = toonMerits.get(toonId, [0, + 0, + 0, + 0]) + p.append(merits) + parts = toonParts.get(toonId, [0, + 0, + 0, + 0]) + p.append(parts) + + deathList = [] + toonIndices = {} + for i in xrange(len(activeToons)): + toonIndices[activeToons[i]] = i + + for deathRecord in suitsKilled: + level = deathRecord['level'] + type = deathRecord['type'] + if deathRecord['isBoss'] > 0: + level = 0 + typeNum = SuitDNA.suitDepts.index(deathRecord['track']) + else: + typeNum = SuitDNA.suitHeadTypes.index(type) + involvedToonIds = deathRecord['activeToons'] + toonBits = 0 + for toonId in involvedToonIds: + if toonId in toonIndices: + toonBits |= 1 << toonIndices[toonId] + + flags = 0 + if deathRecord['isSkelecog']: + flags |= ToontownBattleGlobals.DLF_SKELECOG + if deathRecord['isForeman']: + flags |= ToontownBattleGlobals.DLF_FOREMAN + if deathRecord['isBoss'] > 0: + flags |= ToontownBattleGlobals.DLF_BOSS + if deathRecord['isSupervisor']: + flags |= ToontownBattleGlobals.DLF_SUPERVISOR + if deathRecord['isVirtual']: + flags |= ToontownBattleGlobals.DLF_VIRTUAL + if 'hasRevies' in deathRecord and deathRecord['hasRevives']: + flags |= ToontownBattleGlobals.DLF_REVIVES + deathList.extend([typeNum, + level, + toonBits, + flags]) + + p.append(deathList) + uberStats = getToonUberStatus(activeToons, numToons) + p.append(uberStats) + if helpfulToonsList == None: + helpfulToonsList = [] + p.append(helpfulToonsList) + return p + + +def getToonUberStatus(toons, numToons): + fieldList = [] + uberIndex = ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1 + for toonId in toons: + toonList = [] + toon = simbase.air.doId2do.get(toonId) + if toon == None: + fieldList.append(-1) + else: + for trackIndex in xrange(ToontownBattleGlobals.MAX_TRACK_INDEX + 1): + toonList.append(toon.inventory.numItem(trackIndex, uberIndex)) + + fieldList.append(ToontownBattleGlobals.encodeUber(toonList)) + + lenDif = numToons - len(toons) + if lenDif > 0: + for index in xrange(lenDif): + fieldList.append(-1) + + return fieldList + + +def assignRewards(activeToons, toonSkillPtsGained, suitsKilled, zoneId, helpfulToons = None): + if helpfulToons == None: + BattleExperienceAINotify.warning('=============\nERROR ERROR helpfulToons=None in assignRewards , tell Red') + activeToonList = [] + for t in activeToons: + toon = simbase.air.doId2do.get(t) + if toon != None: + activeToonList.append(toon) + + for toon in activeToonList: + for i in xrange(len(ToontownBattleGlobals.Tracks)): + uberIndex = ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1 + exp = getSkillGained(toonSkillPtsGained, toon.doId, i) + needed = ToontownBattleGlobals.Levels[i][ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1] + ToontownBattleGlobals.UberSkill + hasUber = 0 + totalExp = exp + toon.experience.getExp(i) + if toon.inventory.numItem(i, uberIndex) > 0: + hasUber = 1 + if totalExp >= needed or totalExp >= ToontownBattleGlobals.MaxSkill: + if toon.inventory.totalProps < toon.getMaxCarry() and not hasUber: + uberLevel = ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1 + toon.inventory.addItem(i, uberLevel) + toon.experience.setExp(i, ToontownBattleGlobals.Levels[i][ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1]) + else: + toon.experience.setExp(i, ToontownBattleGlobals.MaxSkill) + else: + if exp > 0: + newGagList = toon.experience.getNewGagIndexList(i, exp) + toon.experience.addExp(i, amount=exp) + toon.inventory.addItemWithList(i, newGagList) + toon.b_setExperience(toon.experience.makeNetString()) + toon.d_setInventory(toon.inventory.makeNetString()) + toon.b_setAnimState('victory', 1) + + if simbase.air.config.GetBool('battle-passing-no-credit', True): + if helpfulToons and toon.doId in helpfulToons: + simbase.air.questManager.toonKilledCogs(toon, suitsKilled, zoneId) + simbase.air.cogPageManager.toonKilledCogs(toon, suitsKilled, zoneId) + addStats(toon, suitsKilled) + else: + BattleExperienceAINotify.debug('toon=%d unhelpful not getting killed cog quest credit' % toon.doId) + else: + simbase.air.questManager.toonKilledCogs(toon, suitsKilled, zoneId) + simbase.air.cogPageManager.toonKilledCogs(toon, suitsKilled, zoneId) + addStats(toon, suitsKilled) + +def addStats(toon, suitsKilled): + toon.addStat(ToontownGlobals.STAT_COGS, len(suitsKilled)) + toon.addStat(ToontownGlobals.STAT_V2, len([suit for suit in suitsKilled if 'hasRevives' in suit and suit['hasRevives']])) + toon.addStat(ToontownGlobals.STAT_SKELE, len([suit for suit in suitsKilled if 'isSkelecog' in suit and suit['isSkelecog']])) \ No newline at end of file diff --git a/toontown/battle/BattleManagerAI.py b/toontown/battle/BattleManagerAI.py new file mode 100755 index 00000000..542692ec --- /dev/null +++ b/toontown/battle/BattleManagerAI.py @@ -0,0 +1,41 @@ +import DistributedBattleAI +from direct.directnotify import DirectNotifyGlobal + +class BattleManagerAI: + notify = DirectNotifyGlobal.directNotify.newCategory('BattleManagerAI') + + def __init__(self, air): + self.air = air + self.cellId2battle = {} + self.battleConstructor = DistributedBattleAI.DistributedBattleAI + + def cellHasBattle(self, cellId): + return cellId in self.cellId2battle + + def getBattle(self, cellId): + if cellId in self.cellId2battle: + return self.cellId2battle[cellId] + return None + + def newBattle(self, cellId, zoneId, pos, suit, toonId, finishCallback = None, maxSuits = 4, interactivePropTrackBonus = -1): + if cellId in self.cellId2battle: + self.notify.info("A battle is already present in the suit's zone!") + if not self.requestBattleAddSuit(cellId, suit): + suit.flyAwayNow() + battle = self.cellId2battle[cellId] + battle.signupToon(toonId, pos[0], pos[1], pos[2]) + else: + battle = self.battleConstructor(self.air, self, pos, suit, toonId, zoneId, finishCallback, maxSuits, interactivePropTrackBonus=interactivePropTrackBonus) + battle.generateWithRequired(zoneId) + battle.battleCellId = cellId + self.cellId2battle[cellId] = battle + return battle + + def requestBattleAddSuit(self, cellId, suit): + return self.cellId2battle[cellId].suitRequestJoin(suit) + + def destroy(self, battle): + cellId = battle.battleCellId + self.notify.debug('BattleManager - destroying battle %d' % cellId) + del self.cellId2battle[cellId] + battle.requestDelete() diff --git a/toontown/battle/BattleParticles.py b/toontown/battle/BattleParticles.py new file mode 100755 index 00000000..d5a1241d --- /dev/null +++ b/toontown/battle/BattleParticles.py @@ -0,0 +1,149 @@ +from direct.particles.ParticleEffect import * +from direct.directnotify import DirectNotifyGlobal +import ParticleDefs + +notify = DirectNotifyGlobal.directNotify.newCategory('BattleParticles') +TutorialParticleEffects = ('gearExplosionBig.ptf', 'gearExplosionSmall.ptf', 'gearExplosion.ptf') +ParticleNames = ('audit-div', 'audit-five', 'audit-four', 'audit-minus', 'audit-mult', 'audit-one', 'audit-plus', 'audit-six', 'audit-three', 'audit-two', 'blah', 'brainstorm-box', 'brainstorm-env', 'brainstorm-track', 'buzzwords-crash', 'buzzwords-inc', 'buzzwords-main', 'buzzwords-over', 'buzzwords-syn', 'confetti', 'doubletalk-double', 'doubletalk-dup', 'doubletalk-good', 'filibuster-cut', 'filibuster-fiscal', 'filibuster-impeach', 'filibuster-inc', 'jargon-brow', 'jargon-deep', 'jargon-hoop', 'jargon-ipo', 'legalese-hc', 'legalese-qpq', 'legalese-vd', 'mumbojumbo-boiler', 'mumbojumbo-creative', 'mumbojumbo-deben', 'mumbojumbo-high', 'mumbojumbo-iron', 'poundsign', 'schmooze-genius', 'schmooze-instant', 'schmooze-master', 'schmooze-viz', 'roll-o-dex', 'rollodex-card', 'dagger', 'fire', 'snow-particle', 'raindrop', 'gear', 'checkmark', 'dollar-sign', 'spark') +particleModel = None +particleSearchPath = None + +def loadParticles(): + global particleModel + if particleModel == None: + particleModel = loader.loadModel('phase_3.5/models/props/suit-particles') + +def unloadParticles(): + global particleModel + if particleModel != None: + particleModel.removeNode() + del particleModel + particleModel = None + +def getParticle(name): + if name in ParticleNames: + particle = particleModel.find('**/' + str(name)) + return particle + else: + notify.warning('getParticle() - no name: %s' % name) + return None + return None + +def loadParticleFile(name): + assert name.endswith('.ptf') + name = name[:-4] # Strip .ptf + particleFunc = ParticleDefs.ParticleTable[name] + + effect = ParticleEffect() + particleFunc(effect) + return effect + +def createParticleEffect(name = None, file = None, numParticles = None, color = None): + if not name: + fileName = file + '.ptf' + return loadParticleFile(fileName) + if name == 'GearExplosion': + return __makeGearExplosion(numParticles) + elif name == 'BigGearExplosion': + return __makeGearExplosion(numParticles, 'Big') + elif name == 'WideGearExplosion': + return __makeGearExplosion(numParticles, 'Wide') + elif name == 'BrainStorm': + return loadParticleFile('brainStorm.ptf') + elif name == 'BuzzWord': + return loadParticleFile('buzzWord.ptf') + elif name == 'Calculate': + return loadParticleFile('calculate.ptf') + elif name == 'Confetti': + return loadParticleFile('confetti.ptf') + elif name == 'DemotionFreeze': + return loadParticleFile('demotionFreeze.ptf') + elif name == 'DemotionSpray': + return loadParticleFile('demotionSpray.ptf') + elif name == 'DoubleTalkLeft': + return loadParticleFile('doubleTalkLeft.ptf') + elif name == 'DoubleTalkRight': + return loadParticleFile('doubleTalkRight.ptf') + elif name == 'FingerWag': + return loadParticleFile('fingerwag.ptf') + elif name == 'FiredFlame': + return loadParticleFile('firedFlame.ptf') + elif name == 'FreezeAssets': + return loadParticleFile('freezeAssets.ptf') + elif name == 'GlowerPower': + return loadParticleFile('glowerPowerKnives.ptf') + elif name == 'HotAir': + return loadParticleFile('hotAirSpray.ptf') + elif name == 'PoundKey': + return loadParticleFile('poundkey.ptf') + elif name == 'ShiftSpray': + return loadParticleFile('shiftSpray.ptf') + elif name == 'ShiftLift': + return __makeShiftLift() + elif name == 'Shred': + return loadParticleFile('shred.ptf') + elif name == 'Smile': + return loadParticleFile('smile.ptf') + elif name == 'Smoke': + return loadParticleFile('smoke.ptf') + elif name == 'SpriteFiredFlecks': + return loadParticleFile('spriteFiredFlecks.ptf') + elif name == 'Synergy': + return loadParticleFile('synergy.ptf') + elif name == 'Waterfall': + return loadParticleFile('waterfall.ptf') + elif name == 'PoundKey': + return loadParticleFile('poundkey.ptf') + elif name == 'RubOut': + return __makeRubOut(color) + elif name == 'SplashLines': + return loadParticleFile('splashlines.ptf') + elif name == 'Withdrawal': + return loadParticleFile('withdrawal.ptf') + else: + notify.warning('createParticleEffect() - no name: %s' % name) + return None + +def setEffectTexture(effect, name, color = None): + particles = effect.getParticlesNamed('particles-1') + np = getParticle(name) + if color: + particles.renderer.setColor(color) + particles.renderer.setFromNode(np) + +def __makeGearExplosion(numParticles = None, style = 'Normal'): + if style == 'Normal': + effect = loadParticleFile('gearExplosion.ptf') + elif style == 'Big': + effect = loadParticleFile('gearExplosionBig.ptf') + elif style == 'Wide': + effect = loadParticleFile('gearExplosionWide.ptf') + if numParticles: + particles = effect.getParticlesNamed('particles-1') + particles.setPoolSize(numParticles) + return effect + +def __makeRubOut(color = None): + effect = loadParticleFile('demotionUnFreeze.ptf') + loadParticles() + setEffectTexture(effect, 'snow-particle') + particles = effect.getParticlesNamed('particles-1') + particles.renderer.setInitialXScale(0.03) + particles.renderer.setFinalXScale(0.0) + particles.renderer.setInitialYScale(0.02) + particles.renderer.setFinalYScale(0.0) + if color: + particles.renderer.setColor(color) + else: + particles.renderer.setColor(Vec4(0.54, 0.92, 0.32, 0.7)) + return effect + +def __makeShiftLift(): + effect = loadParticleFile('pixieDrop.ptf') + particles = effect.getParticlesNamed('particles-1') + particles.renderer.setCenterColor(Vec4(1, 1, 0, 0.9)) + particles.renderer.setEdgeColor(Vec4(1, 1, 0, 0.6)) + particles.emitter.setRadius(0.01) + effect.setHpr(0, 180, 0) + effect.setPos(0, 0, 0) + return effect diff --git a/toontown/battle/BattlePlace.py b/toontown/battle/BattlePlace.py new file mode 100755 index 00000000..2529e4c3 --- /dev/null +++ b/toontown/battle/BattlePlace.py @@ -0,0 +1,113 @@ +from panda3d.core import * +from toontown.hood import Place, ZoneUtil +from toontown.toon import Toon +from toontown.toonbase import ToontownGlobals + + +class BattlePlace(Place.Place): + + def __init__(self, loader, doneEvent): + Place.Place.__init__(self, loader, doneEvent) + + def load(self): + Place.Place.load(self) + Toon.loadBattleAnims() + + def setState(self, state, battleEvent = None): + if battleEvent: + if not self.fsm.request(state, [battleEvent]): + self.notify.warning("fsm.request('%s') returned 0 (zone id %s, avatar pos %s)." % (state, self.zoneId, base.localAvatar.getPos(render))) + elif not self.fsm.request(state): + self.notify.warning("fsm.request('%s') returned 0 (zone id %s, avatar pos %s)." % (state, self.zoneId, base.localAvatar.getPos(render))) + + def enterWalk(self, flag = 0): + Place.Place.enterWalk(self, flag) + self.accept('enterBattle', self.handleBattleEntry) + + def exitWalk(self): + Place.Place.exitWalk(self) + self.ignore('enterBattle') + + def enterWaitForBattle(self): + base.localAvatar.b_setAnimState('neutral', 1) + + def exitWaitForBattle(self): + pass + + def enterBattle(self, event): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: COGBATTLE: Enter Battle') + self.loader.music.stop() + base.playMusic(self.loader.battleMusic, looping=1, volume=0.9) + self.enterTownBattle(event) + base.localAvatar.b_setAnimState('off', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.cantLeaveGame = 1 + + def enterTownBattle(self, event): + self.loader.townBattle.enter(event, self.fsm.getStateNamed('battle')) + + def exitBattle(self): + self.loader.townBattle.exit() + self.loader.battleMusic.stop() + base.playMusic(self.loader.music, looping=1, volume=0.8) + base.localAvatar.cantLeaveGame = 0 + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + + def handleBattleEntry(self): + self.fsm.request('battle') + + def enterFallDown(self, extraArgs = []): + base.localAvatar.laffMeter.start() + base.localAvatar.b_setAnimState('FallDown', callback=self.handleFallDownDone, extraArgs=extraArgs) + + def handleFallDownDone(self): + base.cr.playGame.getPlace().setState('walk') + + def exitFallDown(self): + base.localAvatar.laffMeter.stop() + + def enterSquished(self): + base.localAvatar.laffMeter.start() + base.localAvatar.b_setAnimState('Squish') + taskMgr.doMethodLater(2.0, self.handleSquishDone, base.localAvatar.uniqueName('finishSquishTask')) + + def handleSquishDone(self, extraArgs = []): + base.cr.playGame.getPlace().setState('walk') + + def exitSquished(self): + taskMgr.remove(base.localAvatar.uniqueName('finishSquishTask')) + base.localAvatar.laffMeter.stop() + + def enterZone(self, newZone): + if isinstance(newZone, CollisionEntry): + try: + newZoneId = int(newZone.getIntoNode().getName()) + except: + self.notify.warning('Invalid floor collision node in street: %s' % newZone.getIntoNode().getName()) + return + else: + newZoneId = newZone + self.doEnterZone(newZoneId) + + def doEnterZone(self, newZoneId): + if newZoneId != self.zoneId: + if newZoneId != None: + if hasattr(self, 'zoneVisDict'): + visList = self.zoneVisDict[newZoneId] + else: + visList = base.cr.playGame.getPlace().loader.zoneVisDict[newZoneId] + base.cr.sendSetZoneMsg(newZoneId, visList) + self.notify.debug('Entering Zone %d' % newZoneId) + self.zoneId = newZoneId + + def genDNAFileName(self, zoneId): + zoneId = ZoneUtil.getCanonicalZoneId(zoneId) + hoodId = ZoneUtil.getCanonicalHoodId(zoneId) + hood = ToontownGlobals.dnaMap[hoodId] + phase = ToontownGlobals.streetPhaseMap[hoodId] + if hoodId == zoneId: + zoneId = 'sz' + return 'phase_%s/dna/%s_%s.pdna' % (phase, hood, zoneId) diff --git a/toontown/battle/BattleProps.py b/toontown/battle/BattleProps.py new file mode 100755 index 00000000..2d77cbb5 --- /dev/null +++ b/toontown/battle/BattleProps.py @@ -0,0 +1,438 @@ +from panda3d.core import * +from direct.actor import Actor +from direct.directnotify import DirectNotifyGlobal +from otp.otpbase import OTPGlobals +import random +Props = ((5, 'partyBall', 'partyBall'), + (5, + 'feather', + 'feather-mod', + 'feather-chan'), + (5, 'lips', 'lips'), + (5, 'lipstick', 'lipstick'), + (5, 'hat', 'hat'), + (5, 'cane', 'cane'), + (5, + 'cubes', + 'cubes-mod', + 'cubes-chan'), + (5, 'ladder', 'ladder2'), + (4, + 'fishing-pole', + 'fishing-pole-mod', + 'fishing-pole-chan'), + (5, + '1dollar', + '1dollar-bill-mod', + '1dollar-bill-chan'), + (5, 'big-magnet', 'magnet'), + (5, + 'hypno-goggles', + 'hypnotize-mod', + 'hypnotize-chan'), + (5, 'slideshow', 'av_screen'), + (5, + 'banana', + 'banana-peel-mod', + 'banana-peel-chan'), + (5, + 'rake', + 'rake-mod', + 'rake-chan'), + (5, + 'marbles', + 'marbles-mod', + 'marbles-chan'), + (5, + 'tnt', + 'tnt-mod', + 'tnt-chan'), + (5, 'trapdoor', 'trapdoor'), + (5, 'quicksand', 'quicksand'), + (5, 'traintrack', 'traintrack2'), + (5, 'train', 'train'), + (5, 'megaphone', 'megaphone'), + (5, 'aoogah', 'aoogah'), + (5, 'bikehorn', 'bikehorn'), + (5, 'bugle', 'bugle'), + (5, 'elephant', 'elephant'), + (5, 'fog_horn', 'fog_horn'), + (5, 'whistle', 'whistle'), + (5, 'singing', 'singing'), + (3.5, 'creampie', 'tart'), + (5, 'fruitpie-slice', 'fruit-pie-slice'), + (5, 'creampie-slice', 'cream-pie-slice'), + (5, + 'birthday-cake', + 'birthday-cake-mod', + 'birthday-cake-chan'), + (5, 'wedding-cake', 'wedding_cake'), + (3.5, 'squirting-flower', 'squirting-flower'), + (5, + 'glass', + 'glass-mod', + 'glass-chan'), + (4, 'water-gun', 'water-gun'), + (3.5, 'bottle', 'bottle'), + (5, + 'firehose', + 'firehose-mod', + 'firehose-chan'), + (5, 'hydrant', 'battle_hydrant'), + (4, + 'stormcloud', + 'stormcloud-mod', + 'stormcloud-chan'), + (5, 'geyser', 'geyser'), + (3.5, 'button', 'button'), + (5, + 'flowerpot', + 'flowerpot-mod', + 'flowerpot-chan'), + (5, + 'sandbag', + 'sandbag-mod', + 'sandbag-chan'), + (4, + 'anvil', + 'anvil-mod', + 'anvil-chan'), + (5, + 'weight', + 'weight-mod', + 'weight-chan'), + (5, + 'safe', + 'safe-mod', + 'safe-chan'), + (5, + 'piano', + 'piano-mod', + 'piano-chan'), + (5, + 'rake-react', + 'rake-step-mod', + 'rake-step-chan'), + (5, 'pad', 'pad'), + (4, + 'propeller', + 'propeller-mod', + 'propeller-chan'), + (5, + 'calculator', + 'calculator-mod', + 'calculator-chan'), + (5, 'rollodex', 'roll-o-dex'), + (5, 'rubber-stamp', 'rubber-stamp'), + (5, + 'rubber-stamp-pad', + 'rubber-stamp-pad-mod', + 'rubber-stamp-pad-chan'), + (5, + 'smile', + 'smile-mod', + 'smile-chan'), + (5, 'golf-club', 'golf-club'), + (5, 'golf-ball', 'golf-ball'), + (5, 'redtape', 'redtape'), + (5, 'redtape-tube', 'redtape-tube'), + (5, 'bounced-check', 'bounced-check'), + (5, + 'calculator', + 'calculator-mod', + 'calculator-chan'), + (3.5, + 'clip-on-tie', + 'clip-on-tie-mod', + 'clip-on-tie-chan'), + (5, 'pen', 'pen'), + (5, 'pencil', 'pencil'), + (3.5, 'phone', 'phone'), + (3.5, 'receiver', 'receiver'), + (5, 'sharpener', 'sharpener'), + (3.5, 'shredder', 'shredder'), + (3.5, + 'shredder-paper', + 'shredder-paper-mod', + 'shredder-paper-chan'), + (5, 'watercooler', 'watercooler'), + (5, 'dagger', 'dagger'), + (5, 'card', 'card'), + (5, 'baseball', 'baseball'), + (5, 'bird', 'bird'), + (5, 'can', 'can'), + (5, 'cigar', 'cigar'), + (5, 'evil-eye', 'evil-eye'), + (5, 'gavel', 'gavel'), + (5, 'half-windsor', 'half-windsor'), + (5, 'lawbook', 'lawbook'), + (5, 'newspaper', 'newspaper'), + (5, 'pink-slip', 'pink-slip'), + (5, + 'teeth', + 'teeth-mod', + 'teeth-chan'), + (5, 'power-tie', 'power-tie'), + (3.5, 'spray', 'spray'), + (3.5, 'splash', 'splash'), + (3.5, + 'splat', + 'splat-mod', + 'splat-chan'), + (3.5, + 'stun', + 'stun-mod', + 'stun-chan'), + (3.5, 'glow', 'glow'), + (3.5, + 'suit_explosion', + 'suit_explosion-mod', + 'suit_explosion-chan'), + (3.5, 'suit_explosion_dust', 'dust_cloud'), + (4, 'ripples', 'ripples'), + (4, 'wake', 'wake'), + (4, + 'splashdown', + 'SZ_splashdown-mod', + 'SZ_splashdown-chan')) +CreampieColor = VBase4(250.0 / 255.0, 241.0 / 255.0, 24.0 / 255.0, 1.0) +FruitpieColor = VBase4(55.0 / 255.0, 40.0 / 255.0, 148.0 / 255.0, 1.0) +BirthdayCakeColor = VBase4(253.0 / 255.0, 119.0 / 255.0, 220.0 / 255.0, 1.0) +SnowballColor = VBase4(1.0, 1.0, 1.0, 1.0) +Splats = {'tart': (0.3, FruitpieColor), + 'fruitpie-slice': (0.5, FruitpieColor), + 'creampie-slice': (0.5, CreampieColor), + 'fruitpie': (0.7, FruitpieColor), + 'creampie': (0.7, CreampieColor), + 'birthday-cake': (0.9, BirthdayCakeColor), + 'wedding-cake': (0.9, BirthdayCakeColor)} +Variants = ('tart', + 'fruitpie', + 'splat-tart', + 'dust', + 'kapow', + 'double-windsor', + 'splat-fruitpie-slice', + 'splat-creampie-slice', + 'splat-fruitpie', + 'splat-creampie', + 'splat-birthday-cake', + 'splat-wedding-cake', + 'splash-from-splat', + 'clip-on-tie', + 'lips', + 'small-magnet', + '5dollar', + '10dollar', + 'suit_explosion', + 'quicksand', + 'trapdoor', + 'geyser', + 'ship', + 'trolley', + 'traintrack') + +class PropPool: + notify = DirectNotifyGlobal.directNotify.newCategory('PropPool') + + def __init__(self): + self.props = {} + self.propCache = [] + self.propStrings = {} + self.propTypes = {} + self.maxPoolSize = base.config.GetInt('prop-pool-size', 8) + for p in Props: + phase = p[0] + propName = p[1] + modelName = p[2] + if len(p) == 4: + animName = p[3] + propPath = self.getPath(phase, modelName) + animPath = self.getPath(phase, animName) + self.propTypes[propName] = 'actor' + self.propStrings[propName] = (propPath, animPath) + else: + propPath = self.getPath(phase, modelName) + self.propTypes[propName] = 'model' + self.propStrings[propName] = (propPath,) + + propName = 'tart' + self.propStrings[propName] = (self.getPath(3.5, 'tart'),) + self.propTypes[propName] = 'model' + propName = 'fruitpie' + self.propStrings[propName] = (self.getPath(3.5, 'tart'),) + self.propTypes[propName] = 'model' + propName = 'double-windsor' + self.propStrings[propName] = (self.getPath(5, 'half-windsor'),) + self.propTypes[propName] = 'model' + splatAnimFileName = self.getPath(3.5, 'splat-chan') + for splat in Splats.keys(): + propName = 'splat-' + splat + self.propStrings[propName] = (self.getPath(3.5, 'splat-mod'), splatAnimFileName) + self.propTypes[propName] = 'actor' + + propName = 'splash-from-splat' + self.propStrings[propName] = (self.getPath(3.5, 'splat-mod'), splatAnimFileName) + self.propTypes[propName] = 'actor' + propName = 'small-magnet' + self.propStrings[propName] = (self.getPath(5, 'magnet'),) + self.propTypes[propName] = 'model' + propName = '5dollar' + self.propStrings[propName] = (self.getPath(5, '1dollar-bill-mod'), self.getPath(5, '1dollar-bill-chan')) + self.propTypes[propName] = 'actor' + propName = '10dollar' + self.propStrings[propName] = (self.getPath(5, '1dollar-bill-mod'), self.getPath(5, '1dollar-bill-chan')) + self.propTypes[propName] = 'actor' + propName = 'dust' + self.propStrings[propName] = (self.getPath(5, 'dust-mod'), self.getPath(5, 'dust-chan')) + self.propTypes[propName] = 'actor' + propName = 'kapow' + self.propStrings[propName] = (self.getPath(5, 'kapow-mod'), self.getPath(5, 'kapow-chan')) + self.propTypes[propName] = 'actor' + propName = 'ship' + self.propStrings[propName] = ('phase_5/models/props/ship.bam',) + self.propTypes[propName] = 'model' + propName = 'trolley' + self.propStrings[propName] = ('phase_4/models/modules/trolley_station_TT',) + self.propTypes[propName] = 'model' + + def getPath(self, phase, model): + return 'phase_%s/models/props/%s' % (phase, model) + + def makeVariant(self, name): + if name == 'tart': + self.props[name].setScale(0.5) + elif name == 'fruitpie': + self.props[name].setScale(0.75) + elif name == 'double-windsor': + self.props[name].setScale(1.5) + elif name[:6] == 'splat-': + prop = self.props[name] + scale = prop.getScale() * Splats[name[6:]][0] + prop.setScale(scale) + prop.setColor(Splats[name[6:]][1]) + elif name == 'splash-from-splat': + self.props[name].setColor(0.75, 0.75, 1.0, 1.0) + elif name == 'clip-on-tie': + tie = self.props[name] + tie.getChild(0).setHpr(23.86, -16.03, 9.18) + elif name == 'small-magnet': + self.props[name].setScale(0.5) + elif name == 'shredder-paper': + paper = self.props[name] + paper.setPosHpr(2.22, -0.95, 1.16, -48.61, 26.57, -111.51) + paper.flattenMedium() + elif name == 'lips': + lips = self.props[name] + lips.setPos(0, 0, -3.04) + lips.flattenMedium() + elif name == '5dollar': + tex = loader.loadTexture('phase_5/maps/dollar_5.jpg') + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + self.props[name].setTexture(tex, 1) + elif name == '10dollar': + tex = loader.loadTexture('phase_5/maps/dollar_10.jpg') + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + self.props[name].setTexture(tex, 1) + elif name == 'dust': + bin = 110 + for cloudNum in xrange(1, 12): + cloudName = '**/cloud' + str(cloudNum) + cloud = self.props[name].find(cloudName) + cloud.setBin('fixed', bin) + bin -= 10 + + elif name == 'kapow': + l = self.props[name].find('**/letters') + l.setBin('fixed', 20) + e = self.props[name].find('**/explosion') + e.setBin('fixed', 10) + elif name == 'suit_explosion': + joints = ['**/joint_scale_POW', '**/joint_scale_BLAM', '**/joint_scale_BOOM'] + joint = random.choice(joints) + self.props[name].find(joint).hide() + joints.remove(joint) + joint = random.choice(joints) + self.props[name].find(joint).hide() + elif name == 'quicksand' or name == 'trapdoor': + p = self.props[name] + p.setBin('shadow', -5) + p.setDepthWrite(0) + p.getChild(0).setPos(0, 0, OTPGlobals.FloorOffset) + elif name == 'traintrack' or name == 'traintrack2': + prop = self.props[name] + prop.find('**/tunnel3').hide() + prop.find('**/tunnel2').hide() + prop.find('**/tracksA').setPos(0, 0, OTPGlobals.FloorOffset) + elif name == 'geyser': + p = self.props[name] + s = SequenceNode('geyser') + p.findAllMatches('**/Splash*').reparentTo(NodePath(s)) + s.loop(0) + s.setFrameRate(12) + p.attachNewNode(s) + elif name == 'ship': + self.props[name] = self.props[name].find('**/ship_gag') + elif name == 'trolley': + self.props[name] = self.props[name].find('**/trolley_car') + + def unloadProps(self): + for p in self.props.values(): + if type(p) != type(()): + self.__delProp(p) + + self.props = {} + self.propCache = [] + + def getProp(self, name): + return self.__getPropCopy(name) + + def __getPropCopy(self, name): + if self.propTypes[name] == 'actor': + if name not in self.props: + prop = Actor.Actor() + prop.loadModel(self.propStrings[name][0]) + animDict = {} + animDict[name] = self.propStrings[name][1] + prop.loadAnims(animDict) + prop.setName(name) + self.storeProp(name, prop) + if name in Variants: + self.makeVariant(name) + return Actor.Actor(other=self.props[name]) + else: + if name not in self.props: + prop = loader.loadModel(self.propStrings[name][0]) + prop.setName(name) + self.storeProp(name, prop) + if name in Variants: + self.makeVariant(name) + return self.props[name].copyTo(hidden) + + def storeProp(self, name, prop): + self.props[name] = prop + self.propCache.append(prop) + if len(self.props) > self.maxPoolSize: + oldest = self.propCache.pop(0) + del self.props[oldest.getName()] + self.__delProp(oldest) + self.notify.debug('props = %s' % self.props) + self.notify.debug('propCache = %s' % self.propCache) + + def getPropType(self, name): + return self.propTypes[name] + + def __delProp(self, prop): + if prop == None: + self.notify.warning('tried to delete null prop!') + return + if isinstance(prop, Actor.Actor): + prop.cleanup() + else: + prop.removeNode() + return + + +globalPropPool = PropPool() diff --git a/toontown/battle/BattleSounds.py b/toontown/battle/BattleSounds.py new file mode 100755 index 00000000..52cc3630 --- /dev/null +++ b/toontown/battle/BattleSounds.py @@ -0,0 +1,46 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal + +class BattleSounds: + notify = DirectNotifyGlobal.directNotify.newCategory('BattleSounds') + + def __init__(self): + self.mgr = AudioManager.createAudioManager() + self.isValid = 0 + if self.mgr != None and self.mgr.isValid(): + self.isValid = 1 + limit = base.config.GetInt('battle-sound-cache-size', 15) + self.mgr.setCacheLimit(limit) + base.addSfxManager(self.mgr) + self.setupSearchPath() + + def setupSearchPath(self): + self.sfxSearchPath = DSearchPath() + self.sfxSearchPath.appendDirectory(Filename('../resources/phase_3/audio/sfx')) + self.sfxSearchPath.appendDirectory(Filename('../resources/phase_3.5/audio/sfx')) + self.sfxSearchPath.appendDirectory(Filename('../resources/phase_4/audio/sfx')) + self.sfxSearchPath.appendDirectory(Filename('../resources/phase_5/audio/sfx')) + self.sfxSearchPath.appendDirectory(Filename('/phase_3/audio/sfx')) + self.sfxSearchPath.appendDirectory(Filename('/phase_3.5/audio/sfx')) + self.sfxSearchPath.appendDirectory(Filename('/phase_4/audio/sfx')) + self.sfxSearchPath.appendDirectory(Filename('/phase_5/audio/sfx')) + + def clear(self): + if self.isValid: + self.mgr.clearCache() + + def getSound(self, name): + if self.isValid: + filename = Filename(name) + found = vfs.resolveFilename(filename, self.sfxSearchPath) + if not found: + self.setupSearchPath() + found = vfs.resolveFilename(filename, self.sfxSearchPath) + if not found: + self.notify.warning('%s not found on:' % name) + print self.sfxSearchPath + else: + return self.mgr.getSound(filename.getFullpath()) + return self.mgr.getNullSound() + +globalBattleSoundCache = BattleSounds() diff --git a/toontown/battle/DistributedBattle.py b/toontown/battle/DistributedBattle.py new file mode 100755 index 00000000..1d5dc341 --- /dev/null +++ b/toontown/battle/DistributedBattle.py @@ -0,0 +1,217 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from panda3d.core import * +import random + +from BattleBase import * +import DistributedBattleBase +import SuitBattleGlobals +from otp.avatar import Emote +from toontown.distributed import DelayDelete +from toontown.toonbase import ToontownBattleGlobals +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals + + +class DistributedBattle(DistributedBattleBase.DistributedBattleBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattle') + camFOFov = ToontownBattleGlobals.BattleCamFaceOffFov + camFOPos = ToontownBattleGlobals.BattleCamFaceOffPos + PlayGameSetPlaceEvent = 'playGameSetPlace' + + def __init__(self, cr): + townBattle = cr.playGame.hood.loader.townBattle + DistributedBattleBase.DistributedBattleBase.__init__(self, cr, townBattle) + self.setupCollisions(self.uniqueBattleName('battle-collide')) + + def generate(self): + DistributedBattleBase.DistributedBattleBase.generate(self) + + def announceGenerate(self): + DistributedBattleBase.DistributedBattleBase.generate(self) + + def disable(self): + DistributedBattleBase.DistributedBattleBase.disable(self) + self.ignore(self.PlayGameSetPlaceEvent) + + def delete(self): + DistributedBattleBase.DistributedBattleBase.delete(self) + self.ignore(self.PlayGameSetPlaceEvent) + self.removeCollisionData() + + def setMembers(self, suits, suitsJoining, suitsPending, suitsActive, suitsLured, suitTraps, toons, toonsJoining, toonsPending, toonsActive, toonsRunning, timestamp): + if self.battleCleanedUp(): + return + oldtoons = DistributedBattleBase.DistributedBattleBase.setMembers(self, suits, suitsJoining, suitsPending, suitsActive, suitsLured, suitTraps, toons, toonsJoining, toonsPending, toonsActive, toonsRunning, timestamp) + if len(self.toons) == 4 and len(oldtoons) < 4: + self.notify.debug('setMembers() - battle is now full of toons') + self.closeBattleCollision() + elif len(self.toons) < 4 and len(oldtoons) == 4: + self.openBattleCollision() + + def __faceOff(self, ts, name, callback): + if len(self.suits) == 0: + self.notify.warning('__faceOff(): no suits.') + return + if len(self.toons) == 0: + self.notify.warning('__faceOff(): no toons.') + return + suit = self.suits[0] + point = self.suitPoints[0][0] + suitPos = point[0] + suitHpr = VBase3(point[1], 0.0, 0.0) + toon = self.toons[0] + point = self.toonPoints[0][0] + toonPos = point[0] + toonHpr = VBase3(point[1], 0.0, 0.0) + p = toon.getPos(self) + toon.setPos(self, p[0], p[1], 0.0) + toon.setShadowHeight(0) + suit.setState('Battle') + suitTrack = Sequence() + toonTrack = Sequence() + suitTrack.append(Func(suit.loop, 'neutral')) + suitTrack.append(Func(suit.headsUp, toon)) + taunt = SuitBattleGlobals.getFaceoffTaunt(suit.getStyleName(), suit.doId) + suitTrack.append(Func(suit.setChatAbsolute, taunt, CFSpeech | CFTimeout)) + toonTrack.append(Func(toon.loop, 'neutral')) + toonTrack.append(Func(toon.headsUp, suit)) + suitHeight = suit.getHeight() + suitOffsetPnt = Point3(0, 0, suitHeight) + faceoffTime = self.calcFaceoffTime(self.getPos(), self.initialSuitPos) + faceoffTime = max(faceoffTime, BATTLE_SMALL_VALUE) + delay = FACEOFF_TAUNT_T + if self.hasLocalToon(): + MidTauntCamHeight = suitHeight * 0.66 + MidTauntCamHeightLim = suitHeight - 1.8 + if MidTauntCamHeight < MidTauntCamHeightLim: + MidTauntCamHeight = MidTauntCamHeightLim + TauntCamY = 16 + TauntCamX = random.choice((-5, 5)) + TauntCamHeight = random.choice((MidTauntCamHeight, 1, 11)) + camTrack = Sequence() + camTrack.append(Func(camera.wrtReparentTo, suit)) + camTrack.append(Func(base.camLens.setMinFov, self.camFOFov/(4./3.))) + camTrack.append(Func(camera.setPos, TauntCamX, TauntCamY, TauntCamHeight)) + camTrack.append(Func(camera.lookAt, suit, suitOffsetPnt)) + camTrack.append(Wait(delay)) + camTrack.append(Func(base.camLens.setMinFov, self.camFov/(4./3.))) + camTrack.append(Func(camera.wrtReparentTo, self)) + camTrack.append(Func(camera.setPos, self.camFOPos)) + camTrack.append(Func(camera.lookAt, suit.getPos(self))) + camTrack.append(Wait(faceoffTime)) + suitTrack.append(Wait(delay)) + toonTrack.append(Wait(delay)) + suitTrack.append(Func(suit.headsUp, self, suitPos)) + suitTrack.append(Func(suit.clearChat)) + toonTrack.append(Func(toon.headsUp, self, toonPos)) + suitTrack.append(Func(suit.loop, 'walk')) + suitTrack.append(LerpPosInterval(suit, faceoffTime, suitPos, other=self)) + suitTrack.append(Func(suit.loop, 'neutral')) + suitTrack.append(Func(suit.setHpr, self, suitHpr)) + toonTrack.append(Func(toon.loop, 'run')) + toonTrack.append(LerpPosInterval(toon, faceoffTime, toonPos, other=self)) + toonTrack.append(Func(toon.loop, 'neutral')) + toonTrack.append(Func(toon.setHpr, self, toonHpr)) + if base.localAvatar == toon: + soundTrack = Sequence(Wait(delay), SoundInterval(base.localAvatar.soundRun, loop=1, duration=faceoffTime, node=base.localAvatar)) + else: + soundTrack = Wait(delay + faceoffTime) + mtrack = Parallel(suitTrack, toonTrack, soundTrack) + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + mtrack = Parallel(mtrack, camTrack) + done = Func(callback) + track = Sequence(mtrack, done, name=name) + track.delayDeletes = [DelayDelete.DelayDelete(toon, '__faceOff'), DelayDelete.DelayDelete(suit, '__faceOff')] + track.start(ts) + self.storeInterval(track, name) + + def enterFaceOff(self, ts): + self.notify.debug('enterFaceOff()') + self.delayDeleteMembers() + if len(self.toons) > 0 and base.localAvatar == self.toons[0]: + Emote.globalEmote.disableAll(self.toons[0], 'dbattle, enterFaceOff') + self.__faceOff(ts, self.faceOffName, self.__handleFaceOffDone) + prop = self.getInteractiveProp() + + if prop: + prop.gotoBattleCheer() + + def __handleFaceOffDone(self): + self.notify.debug('FaceOff done') + if len(self.toons) > 0 and base.localAvatar == self.toons[0]: + self.d_faceOffDone(base.localAvatar.doId) + + def exitFaceOff(self): + self.notify.debug('exitFaceOff()') + if len(self.toons) > 0 and base.localAvatar == self.toons[0]: + Emote.globalEmote.releaseAll(self.toons[0], 'dbattle exitFaceOff') + self.finishInterval(self.faceOffName) + self.clearInterval(self.faceOffName) + self._removeMembersKeep() + + def enterReward(self, ts): + self.notify.debug('enterReward()') + self.disableCollision() + self.delayDeleteMembers() + Emote.globalEmote.disableAll(base.localAvatar, 'dbattle, enterReward') + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + if self.localToonActive() == 0: + self.removeInactiveLocalToon(base.localAvatar) + for toon in self.toons: + toon.startSmooth() + + self.accept('resumeAfterReward', self.handleResumeAfterReward) + prop = self.getInteractiveProp() + + if prop: + prop.gotoVictory() + self.playReward(ts) + + def playReward(self, ts): + self.movie.playReward(ts, self.uniqueName('reward'), self.handleRewardDone) + + def handleRewardDone(self): + self.notify.debug('Reward done') + if self.hasLocalToon(): + self.d_rewardDone(base.localAvatar.doId) + self.movie.resetReward() + messenger.send('resumeAfterReward') + + def handleResumeAfterReward(self): + self.fsm.request('Resume') + + def exitReward(self): + self.notify.debug('exitReward()') + self.ignore('resumeAfterReward') + self.movie.resetReward(finish=1) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) + Emote.globalEmote.releaseAll(base.localAvatar, 'dbattle, exitReward') + + def enterResume(self, ts = 0): + self.notify.debug('enterResume()') + if self.hasLocalToon(): + self.removeLocalToon() + prop = self.getInteractiveProp() + + if prop: + prop.requestIdleOrSad() + + def exitResume(self): + pass + + def enterNoLocalToon(self): + self.notify.debug('enterNoLocalToon()') + self.enableCollision() + + def exitNoLocalToon(self): + self.disableCollision() + + def enterWaitForServer(self): + self.notify.debug('enterWaitForServer()') + + def exitWaitForServer(self): + pass diff --git a/toontown/battle/DistributedBattleAI.py b/toontown/battle/DistributedBattleAI.py new file mode 100755 index 00000000..7be99f36 --- /dev/null +++ b/toontown/battle/DistributedBattleAI.py @@ -0,0 +1,127 @@ +from otp.ai.AIBase import * +from BattleBase import * +from BattleCalculatorAI import * +from toontown.toonbase.ToontownBattleGlobals import * +from SuitBattleGlobals import * +import DistributedBattleBaseAI +from direct.task import Task +from direct.directnotify import DirectNotifyGlobal +import random + + +class DistributedBattleAI(DistributedBattleBaseAI.DistributedBattleBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleAI') + + def __init__(self, air, battleMgr, pos, suit, toonId, zoneId, finishCallback = None, maxSuits = 4, tutorialFlag = 0, levelFlag = 0, interactivePropTrackBonus = -1): + DistributedBattleBaseAI.DistributedBattleBaseAI.__init__(self, air, zoneId, finishCallback, maxSuits=maxSuits, tutorialFlag=tutorialFlag, interactivePropTrackBonus=interactivePropTrackBonus) + self.battleMgr = battleMgr + self.pos = pos + self.initialSuitPos = suit.getConfrontPosHpr()[0] + self.initialToonPos = suit.getConfrontPosHpr()[0] + self.addSuit(suit) + self.avId = toonId + if levelFlag == 0: + self.addToon(toonId) + self.faceOffToon = toonId + self.fsm.request('FaceOff') + + def generate(self): + DistributedBattleBaseAI.DistributedBattleBaseAI.generate(self) + toon = simbase.air.doId2do.get(self.avId) + if toon: + if hasattr(self, 'doId'): + toon.b_setBattleId(self.doId) + else: + toon.b_setBattleId(-1) + self.avId = None + return + + def faceOffDone(self): + toonId = self.air.getAvatarIdFromSender() + if self.ignoreFaceOffDone == 1: + self.notify.debug('faceOffDone() - ignoring toon: %d' % toonId) + return + elif self.fsm.getCurrentState().getName() != 'FaceOff': + self.notify.warning('faceOffDone() - in state: %s' % self.fsm.getCurrentState().getName()) + return + elif self.toons.count(toonId) == 0: + self.notify.warning('faceOffDone() - toon: %d not in toon list' % toonId) + return + self.notify.debug('toon: %d done facing off' % toonId) + self.handleFaceOffDone() + + def enterFaceOff(self): + self.notify.debug('enterFaceOff()') + self.joinableFsm.request('Joinable') + self.runableFsm.request('Unrunable') + self.suits[0].releaseControl() + timeForFaceoff = self.calcFaceoffTime(self.pos, self.initialSuitPos) + FACEOFF_TAUNT_T + SERVER_BUFFER_TIME + self.timer.startCallback(timeForFaceoff, self.__serverFaceOffDone) + return None + + def __serverFaceOffDone(self): + self.notify.debug('faceoff timed out on server') + self.ignoreFaceOffDone = 1 + self.handleFaceOffDone() + + def exitFaceOff(self): + self.timer.stop() + return None + + def handleFaceOffDone(self): + self.timer.stop() + self.activeSuits.append(self.suits[0]) + if len(self.toons) == 0: + self.b_setState('Resume') + elif self.faceOffToon == self.toons[0]: + self.activeToons.append(self.toons[0]) + self.sendEarnedExperience(self.toons[0]) + self.d_setMembers() + self.b_setState('WaitForInput') + + def localMovieDone(self, needUpdate, deadToons, deadSuits, lastActiveSuitDied): + if len(self.toons) == 0: + self.d_setMembers() + self.b_setState('Resume') + elif len(self.suits) == 0: + for toonId in self.activeToons: + toon = self.getToon(toonId) + if toon: + self.toonItems[toonId] = self.air.questManager.recoverItems(toon, self.suitsKilled, self.zoneId) + if toonId in self.helpfulToons: + self.toonMerits[toonId] = self.air.promotionMgr.recoverMerits(toon, self.suitsKilled, self.zoneId) + else: + self.notify.debug('toon %d not helpful, skipping merits' % toonId) + + self.d_setMembers() + self.d_setBattleExperience() + self.b_setState('Reward') + else: + if needUpdate == 1: + self.d_setMembers() + if len(deadSuits) > 0 and lastActiveSuitDied == 0 or len(deadToons) > 0: + self.needAdjust = 1 + self.setState('WaitForJoin') + + def enterReward(self): + self.notify.debug('enterReward()') + self.joinableFsm.request('Unjoinable') + self.runableFsm.request('Unrunable') + self.resetResponses() + self.assignRewards() + self.startRewardTimer() + + def startRewardTimer(self): + self.timer.startCallback(REWARD_TIMEOUT, self.serverRewardDone) + + def exitReward(self): + return None + + def enterResume(self): + self.notify.debug('enterResume()') + self.joinableFsm.request('Unjoinable') + self.runableFsm.request('Unrunable') + DistributedBattleBaseAI.DistributedBattleBaseAI.enterResume(self) + if self.finishCallback: + self.finishCallback(self.zoneId) + self.battleMgr.destroy(self) diff --git a/toontown/battle/DistributedBattleBase.py b/toontown/battle/DistributedBattleBase.py new file mode 100755 index 00000000..932d9eed --- /dev/null +++ b/toontown/battle/DistributedBattleBase.py @@ -0,0 +1,1537 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from BattleBase import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownBattleGlobals +from direct.distributed import DistributedNode +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task.Task import Task +from direct.directnotify import DirectNotifyGlobal +import Movie +import MovieUtil +from toontown.suit import Suit +from direct.actor import Actor +import BattleProps +from direct.particles import ParticleEffect +import BattleParticles +from toontown.hood import ZoneUtil +from toontown.distributed import DelayDelete +from toontown.toon import TTEmote +from otp.avatar import Emote +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals + + +class DistributedBattleBase(DistributedNode.DistributedNode, BattleBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleBase') + camPos = ToontownBattleGlobals.BattleCamDefaultPos + camHpr = ToontownBattleGlobals.BattleCamDefaultHpr + camFov = ToontownBattleGlobals.BattleCamDefaultFov + camMenuFov = ToontownBattleGlobals.BattleCamMenuFov + camJoinPos = ToontownBattleGlobals.BattleCamJoinPos + camJoinHpr = ToontownBattleGlobals.BattleCamJoinHpr + id = 0 + + def __init__(self, cr, townBattle): + DistributedNode.DistributedNode.__init__(self, cr) + NodePath.__init__(self) + self.assign(render.attachNewNode(self.uniqueBattleName('distributed-battle'))) + BattleBase.__init__(self) + self.bossBattle = 0 + self.townBattle = townBattle + self.__battleCleanedUp = 0 + self.activeIntervals = {} + self.localToonJustJoined = 0 + self.choseAttackAlready = 0 + self.toons = [] + self.exitedToons = [] + self.suitTraps = '' + self.membersKeep = None + self.faceOffName = self.uniqueBattleName('faceoff') + self.localToonBattleEvent = self.uniqueBattleName('localtoon-battle-event') + self.adjustName = self.uniqueBattleName('adjust') + self.timerCountdownTaskName = self.uniqueBattleName('timer-countdown') + self.movie = Movie.Movie(self) + self.timer = Timer() + self.needAdjustTownBattle = 0 + self.streetBattle = 1 + self.levelBattle = 0 + self.localToonFsm = ClassicFSM.ClassicFSM('LocalToon', [State.State('HasLocalToon', self.enterHasLocalToon, self.exitHasLocalToon, ['NoLocalToon', 'WaitForServer']), State.State('NoLocalToon', self.enterNoLocalToon, self.exitNoLocalToon, ['HasLocalToon', 'WaitForServer']), State.State('WaitForServer', self.enterWaitForServer, self.exitWaitForServer, ['HasLocalToon', 'NoLocalToon'])], 'WaitForServer', 'WaitForServer') + self.localToonFsm.enterInitialState() + self.fsm = ClassicFSM.ClassicFSM('DistributedBattle', [State.State('Off', self.enterOff, self.exitOff, ['FaceOff', + 'WaitForInput', + 'WaitForJoin', + 'MakeMovie', + 'PlayMovie', + 'Reward', + 'Resume']), + State.State('FaceOff', self.enterFaceOff, self.exitFaceOff, ['WaitForInput']), + State.State('WaitForJoin', self.enterWaitForJoin, self.exitWaitForJoin, ['WaitForInput', 'Resume']), + State.State('WaitForInput', self.enterWaitForInput, self.exitWaitForInput, ['WaitForInput', 'PlayMovie', 'Resume']), + State.State('MakeMovie', self.enterMakeMovie, self.exitMakeMovie, ['PlayMovie', 'Resume']), + State.State('PlayMovie', self.enterPlayMovie, self.exitPlayMovie, ['WaitForInput', + 'WaitForJoin', + 'Reward', + 'Resume']), + State.State('Reward', self.enterReward, self.exitReward, ['Resume']), + State.State('Resume', self.enterResume, self.exitResume, [])], 'Off', 'Off') + self.fsm.enterInitialState() + self.adjustFsm = ClassicFSM.ClassicFSM('Adjust', [State.State('Adjusting', self.enterAdjusting, self.exitAdjusting, ['NotAdjusting']), State.State('NotAdjusting', self.enterNotAdjusting, self.exitNotAdjusting, ['Adjusting'])], 'NotAdjusting', 'NotAdjusting') + self.adjustFsm.enterInitialState() + self.interactiveProp = None + self.fireCount = 0 + return + + def uniqueBattleName(self, name): + DistributedBattleBase.id += 1 + return name + '-%d' % DistributedBattleBase.id + + def generate(self): + self.notify.debug('generate(%s)' % self.doId) + DistributedNode.DistributedNode.generate(self) + self.__battleCleanedUp = 0 + self.reparentTo(render) + self._skippingRewardMovie = False + + def storeInterval(self, interval, name): + if name in self.activeIntervals: + ival = self.activeIntervals[name] + if hasattr(ival, 'delayDelete') or hasattr(ival, 'delayDeletes'): + self.clearInterval(name, finish=1) + self.activeIntervals[name] = interval + + def __cleanupIntervals(self): + for interval in self.activeIntervals.values(): + interval.finish() + DelayDelete.cleanupDelayDeletes(interval) + + self.activeIntervals = {} + + def clearInterval(self, name, finish = 0): + if name in self.activeIntervals: + ival = self.activeIntervals[name] + if finish: + ival.finish() + else: + ival.pause() + if name in self.activeIntervals: + DelayDelete.cleanupDelayDeletes(ival) + if name in self.activeIntervals: + del self.activeIntervals[name] + else: + self.notify.debug('interval: %s already cleared' % name) + + def finishInterval(self, name): + if name in self.activeIntervals: + interval = self.activeIntervals[name] + interval.finish() + + def disable(self): + self.notify.debug('disable(%s)' % self.doId) + self.cleanupBattle() + DistributedNode.DistributedNode.disable(self) + + def battleCleanedUp(self): + return self.__battleCleanedUp + + def cleanupBattle(self): + if self.__battleCleanedUp: + return + self.notify.debug('cleanupBattle(%s)' % self.doId) + self.__battleCleanedUp = 1 + self.__cleanupIntervals() + self.fsm.requestFinalState() + if self.hasLocalToon(): + self.removeLocalToon() + base.camLens.setMinFov(settings['fov']/(4./3.)) + self.localToonFsm.request('WaitForServer') + self.ignoreAll() + for suit in self.suits: + if suit.battleTrap != NO_TRAP: + self.notify.debug('250 calling self.removeTrap, suit=%d' % suit.doId) + self.removeTrap(suit) + suit.battleTrap = NO_TRAP + suit.battleTrapProp = None + self.notify.debug('253 suit.battleTrapProp = None') + suit.battleTrapIsFresh = 0 + + self.suits = [] + self.pendingSuits = [] + self.joiningSuits = [] + self.activeSuits = [] + self.suitTraps = '' + self.toons = [] + self.joiningToons = [] + self.pendingToons = [] + self.activeToons = [] + self.runningToons = [] + self.__stopTimer() + self.__cleanupIntervals() + self._removeMembersKeep() + return + + def delete(self): + self.notify.debug('delete(%s)' % self.doId) + self.__cleanupIntervals() + self._removeMembersKeep() + self.movie.cleanup() + del self.townBattle + self.removeNode() + self.fsm = None + self.localToonFsm = None + self.adjustFsm = None + self.__stopTimer() + self.timer = None + DistributedNode.DistributedNode.delete(self) + return + + def loadTrap(self, suit, trapid): + self.notify.debug('loadTrap() trap: %d suit: %d' % (trapid, suit.doId)) + trapName = AvProps[TRAP][trapid] + trap = BattleProps.globalPropPool.getProp(trapName) + suit.battleTrap = trapid + suit.battleTrapIsFresh = 0 + suit.battleTrapProp = trap + self.notify.debug('suit.battleTrapProp = trap %s' % trap) + if trap.getName() == 'traintrack': + pass + else: + trap.wrtReparentTo(suit) + distance = MovieUtil.SUIT_TRAP_DISTANCE + if trapName == 'rake': + distance = MovieUtil.SUIT_TRAP_RAKE_DISTANCE + distance += MovieUtil.getSuitRakeOffset(suit) + trap.setH(180) + trap.setScale(0.7) + elif trapName == 'trapdoor' or trapName == 'quicksand': + trap.setScale(1.7) + elif trapName == 'marbles': + distance = MovieUtil.SUIT_TRAP_MARBLES_DISTANCE + trap.setH(94) + elif trapName == 'tnt': + trap.setP(90) + tip = trap.find('**/joint_attachEmitter') + sparks = BattleParticles.createParticleEffect(file='tnt') + trap.sparksEffect = sparks + sparks.start(tip) + trap.setPos(0, distance, 0) + if isinstance(trap, Actor.Actor): + frame = trap.getNumFrames(trapName) - 1 + trap.pose(trapName, frame) + + def removeTrap(self, suit, removeTrainTrack = False): + self.notify.debug('removeTrap() from suit: %d, removeTrainTrack=%s' % (suit.doId, removeTrainTrack)) + if suit.battleTrapProp == None: + self.notify.debug('suit.battleTrapProp == None, suit.battleTrap=%s setting to NO_TRAP, returning' % suit.battleTrap) + suit.battleTrap = NO_TRAP + return + if suit.battleTrap == UBER_GAG_LEVEL_INDEX: + if removeTrainTrack: + self.notify.debug('doing removeProp on traintrack') + MovieUtil.removeProp(suit.battleTrapProp) + for otherSuit in self.suits: + if not otherSuit == suit: + otherSuit.battleTrapProp = None + self.notify.debug('351 otherSuit=%d otherSuit.battleTrapProp = None' % otherSuit.doId) + otherSuit.battleTrap = NO_TRAP + otherSuit.battleTrapIsFresh = 0 + + else: + self.notify.debug('deliberately not doing removeProp on traintrack') + else: + self.notify.debug('suit.battleTrap != UBER_GAG_LEVEL_INDEX') + MovieUtil.removeProp(suit.battleTrapProp) + suit.battleTrapProp = None + self.notify.debug('360 suit.battleTrapProp = None') + suit.battleTrap = NO_TRAP + suit.battleTrapIsFresh = 0 + return + + def pause(self): + self.timer.stop() + + def unpause(self): + self.timer.resume() + + def findSuit(self, id): + for s in self.suits: + if s.doId == id: + return s + + return None + + def findToon(self, id): + toon = self.getToon(id) + if toon == None: + return + for t in self.toons: + if t == toon: + return t + + return + + def isSuitLured(self, suit): + if self.luredSuits.count(suit) != 0: + return 1 + return 0 + + def unlureSuit(self, suit): + self.notify.debug('movie unluring suit %s' % suit.doId) + if self.luredSuits.count(suit) != 0: + self.luredSuits.remove(suit) + self.needAdjustTownBattle = 1 + return None + + def lureSuit(self, suit): + self.notify.debug('movie luring suit %s' % suit.doId) + if self.luredSuits.count(suit) == 0: + self.luredSuits.append(suit) + self.needAdjustTownBattle = 1 + return None + + def getActorPosHpr(self, actor, actorList = []): + if isinstance(actor, Suit.Suit): + if actorList == []: + actorList = self.activeSuits + if actorList.count(actor) != 0: + numSuits = len(actorList) - 1 + index = actorList.index(actor) + point = self.suitPoints[numSuits][index] + return (Point3(point[0]), VBase3(point[1], 0.0, 0.0)) + else: + self.notify.warning('getActorPosHpr() - suit not active') + else: + if actorList == []: + actorList = self.activeToons + if actorList.count(actor) != 0: + numToons = len(actorList) - 1 + index = actorList.index(actor) + point = self.toonPoints[numToons][index] + return (Point3(point[0]), VBase3(point[1], 0.0, 0.0)) + else: + self.notify.warning('getActorPosHpr() - toon not active') + + def setLevelDoId(self, levelDoId): + pass + + def setBattleCellId(self, battleCellId): + pass + + def getInteractiveProp(self): + if config.GetBool('want-anim-props', True): + if self.interactiveProp: + return self.interactiveProp + elif base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'loader'): + loader = base.cr.playGame.hood.loader + + if hasattr(loader, 'getInteractiveProp'): + self.interactiveProp = base.cr.playGame.hood.loader.getInteractiveProp(self.zoneId) + + return self.interactiveProp + return None + else: + return None + + def getInteractivePropTrackBonus(self): + prop = self.getInteractiveProp() + + return prop.BattleTrack if prop else -1 + + def setPosition(self, x, y, z): + self.notify.debug('setPosition() - %d %d %d' % (x, y, z)) + pos = Point3(x, y, z) + self.setPos(pos) + + def setInitialSuitPos(self, x, y, z): + self.initialSuitPos = Point3(x, y, z) + self.headsUp(self.initialSuitPos) + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def setBossBattle(self, value): + self.bossBattle = value + + def setState(self, state, timestamp): + if self.__battleCleanedUp: + return + self.notify.debug('setState(%s)' % state) + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def setMembers(self, suits, suitsJoining, suitsPending, suitsActive, suitsLured, suitTraps, toons, toonsJoining, toonsPending, toonsActive, toonsRunning, timestamp): + if self.__battleCleanedUp: + return + self.notify.debug('setMembers() - suits: %s suitsJoining: %s suitsPending: %s suitsActive: %s suitsLured: %s suitTraps: %s toons: %s toonsJoining: %s toonsPending: %s toonsActive: %s toonsRunning: %s' % (suits, + suitsJoining, + suitsPending, + suitsActive, + suitsLured, + suitTraps, + toons, + toonsJoining, + toonsPending, + toonsActive, + toonsRunning)) + ts = globalClockDelta.localElapsedTime(timestamp) + oldsuits = self.suits + self.suits = [] + suitGone = 0 + prop = self.getInteractiveProp() + + for s in suits: + if s in self.cr.doId2do: + suit = self.cr.doId2do[s] + suit.setState('Battle') + self.suits.append(suit) + + if prop: + suit.interactivePropTrackBonus = prop.BattleTrack + + try: + suit.battleTrap + except: + suit.battleTrap = NO_TRAP + suit.battleTrapProp = None + self.notify.debug('496 suit.battleTrapProp = None') + suit.battleTrapIsFresh = 0 + + else: + self.notify.warning('setMembers() - no suit in repository: %d' % s) + self.suits.append(None) + suitGone = 1 + + numSuitsThatDied = 0 + for s in oldsuits: + if self.suits.count(s) == 0: + self.__removeSuit(s) + numSuitsThatDied += 1 + self.notify.debug('suit %d dies, numSuitsThatDied=%d' % (s.doId, numSuitsThatDied)) + + if numSuitsThatDied == 4: + trainTrap = self.find('**/traintrack') + if not trainTrap.isEmpty(): + self.notify.debug('removing old train trap when 4 suits died') + trainTrap.removeNode() + for s in suitsJoining: + suit = self.suits[int(s)] + if suit != None and self.joiningSuits.count(suit) == 0: + self.makeSuitJoin(suit, ts) + + for s in suitsPending: + suit = self.suits[int(s)] + if suit != None and self.pendingSuits.count(suit) == 0: + self.__makeSuitPending(suit) + + activeSuits = [] + for s in suitsActive: + suit = self.suits[int(s)] + if suit != None and self.activeSuits.count(suit) == 0: + activeSuits.append(suit) + + oldLuredSuits = self.luredSuits + self.luredSuits = [] + for s in suitsLured: + suit = self.suits[int(s)] + if suit != None: + self.luredSuits.append(suit) + if oldLuredSuits.count(suit) == 0: + self.needAdjustTownBattle = 1 + + if self.needAdjustTownBattle == 0: + for s in oldLuredSuits: + if self.luredSuits.count(s) == 0: + self.needAdjustTownBattle = 1 + + index = 0 + oldSuitTraps = self.suitTraps + self.suitTraps = suitTraps + for s in suitTraps: + trapid = int(s) + if trapid == 9: + trapid = -1 + suit = self.suits[index] + index += 1 + if suit != None: + if (trapid == NO_TRAP or trapid != suit.battleTrap) and suit.battleTrapProp != None: + self.notify.debug('569 calling self.removeTrap, suit=%d' % suit.doId) + self.removeTrap(suit) + if trapid != NO_TRAP and suit.battleTrapProp == None: + if self.fsm.getCurrentState().getName() != 'PlayMovie': + self.loadTrap(suit, trapid) + + if len(oldSuitTraps) != len(self.suitTraps): + self.needAdjustTownBattle = 1 + else: + for i in xrange(len(oldSuitTraps)): + if oldSuitTraps[i] == '9' and self.suitTraps[i] != '9' or oldSuitTraps[i] != '9' and self.suitTraps[i] == '9': + self.needAdjustTownBattle = 1 + break + + if suitGone: + validSuits = [] + for s in self.suits: + if s != None: + validSuits.append(s) + + self.suits = validSuits + self.needAdjustTownBattle = 1 + oldtoons = self.toons + self.toons = [] + toonGone = 0 + for t in toons: + toon = self.getToon(t) + if toon == None: + self.notify.warning('setMembers() - toon not in cr!') + self.toons.append(None) + toonGone = 1 + continue + self.toons.append(toon) + if oldtoons.count(toon) == 0: + self.notify.debug('setMembers() - add toon: %d' % toon.doId) + self.__listenForUnexpectedExit(toon) + toon.stopLookAround() + toon.stopSmooth() + + for t in oldtoons: + if self.toons.count(t) == 0: + if self.__removeToon(t) == 1: + self.notify.debug('setMembers() - local toon left battle') + return [] + + for t in toonsJoining: + if int(t) < len(self.toons): + toon = self.toons[int(t)] + if toon != None and self.joiningToons.count(toon) == 0: + self.__makeToonJoin(toon, toonsPending, ts) + else: + self.notify.warning('setMembers toonsJoining t=%s not in self.toons %s' % (t, self.toons)) + + for t in toonsPending: + if int(t) < len(self.toons): + toon = self.toons[int(t)] + if toon != None and self.pendingToons.count(toon) == 0: + self.__makeToonPending(toon, ts) + else: + self.notify.warning('setMembers toonsPending t=%s not in self.toons %s' % (t, self.toons)) + + for t in toonsRunning: + toon = self.toons[int(t)] + if toon != None and self.runningToons.count(toon) == 0: + self.__makeToonRun(toon, ts) + + activeToons = [] + for t in toonsActive: + toon = self.toons[int(t)] + if toon != None and self.activeToons.count(toon) == 0: + activeToons.append(toon) + + if len(activeSuits) > 0 or len(activeToons) > 0: + self.__makeAvsActive(activeSuits, activeToons) + if toonGone == 1: + validToons = [] + for toon in self.toons: + if toon != None: + validToons.append(toon) + + self.toons = validToons + if len(self.activeToons) > 0: + self.__requestAdjustTownBattle() + currStateName = self.localToonFsm.getCurrentState().getName() + if self.toons.count(base.localAvatar): + if oldtoons.count(base.localAvatar) == 0: + self.notify.debug('setMembers() - local toon just joined') + if self.streetBattle == 1: + base.cr.playGame.getPlace().enterZone(self.zoneId) + self.localToonJustJoined = 1 + if currStateName != 'HasLocalToon': + self.localToonFsm.request('HasLocalToon') + else: + if oldtoons.count(base.localAvatar): + self.notify.debug('setMembers() - local toon just ran') + if self.levelBattle: + self.unlockLevelViz() + if currStateName != 'NoLocalToon': + self.localToonFsm.request('NoLocalToon') + for suit in self.luredSuits: + suit.loop('lured') + return oldtoons + + def adjust(self, timestamp): + if self.__battleCleanedUp: + return + self.notify.debug('adjust(%f) from server' % globalClockDelta.localElapsedTime(timestamp)) + self.adjustFsm.request('Adjusting', [globalClockDelta.localElapsedTime(timestamp)]) + + def setMovie(self, active, toons, suits, id0, tr0, le0, tg0, hp0, ac0, hpb0, kbb0, died0, revive0, id1, tr1, le1, tg1, hp1, ac1, hpb1, kbb1, died1, revive1, id2, tr2, le2, tg2, hp2, ac2, hpb2, kbb2, died2, revive2, id3, tr3, le3, tg3, hp3, ac3, hpb3, kbb3, died3, revive3, sid0, at0, stg0, dm0, sd0, sb0, st0, sid1, at1, stg1, dm1, sd1, sb1, st1, sid2, at2, stg2, dm2, sd2, sb2, st2, sid3, at3, stg3, dm3, sd3, sb3, st3): + if self.__battleCleanedUp: + return + self.notify.debug('setMovie()') + if int(active) == 1: + self.notify.debug('setMovie() - movie is active') + self.movie.genAttackDicts(toons, suits, id0, tr0, le0, tg0, hp0, ac0, hpb0, kbb0, died0, revive0, id1, tr1, le1, tg1, hp1, ac1, hpb1, kbb1, died1, revive1, id2, tr2, le2, tg2, hp2, ac2, hpb2, kbb2, died2, revive2, id3, tr3, le3, tg3, hp3, ac3, hpb3, kbb3, died3, revive3, sid0, at0, stg0, dm0, sd0, sb0, st0, sid1, at1, stg1, dm1, sd1, sb1, st1, sid2, at2, stg2, dm2, sd2, sb2, st2, sid3, at3, stg3, dm3, sd3, sb3, st3) + + def setChosenToonAttacks(self, ids, tracks, levels, targets): + if self.__battleCleanedUp: + return + self.notify.debug('setChosenToonAttacks() - (%s), (%s), (%s), (%s)' % (ids, + tracks, + levels, + targets)) + toonIndices = [] + targetIndices = [] + unAttack = 0 + localToonInList = 0 + for i in xrange(len(ids)): + track = tracks[i] + level = levels[i] + toon = self.findToon(ids[i]) + if toon == None or self.activeToons.count(toon) == 0: + self.notify.warning('setChosenToonAttacks() - toon gone or not in battle: %d!' % ids[i]) + toonIndices.append(-1) + tracks.append(-1) + levels.append(-1) + targetIndices.append(-1) + continue + if toon == base.localAvatar: + localToonInList = 1 + toonIndices.append(self.activeToons.index(toon)) + if track == SOS: + targetIndex = -1 + elif track == NPCSOS: + targetIndex = targets[i] + elif track == PETSOS: + targetIndex = -1 + elif track == PASS: + targetIndex = -1 + tracks[i] = PASS_ATTACK + elif attackAffectsGroup(track, level): + targetIndex = -1 + elif track == HEAL: + target = self.findToon(targets[i]) + if target != None and self.activeToons.count(target) != 0: + targetIndex = self.activeToons.index(target) + else: + targetIndex = -1 + elif track == UN_ATTACK: + targetIndex = -1 + tracks[i] = NO_ATTACK + if toon == base.localAvatar: + unAttack = 1 + self.choseAttackAlready = 0 + elif track == NO_ATTACK: + targetIndex = -1 + else: + target = self.findSuit(targets[i]) + if target != None and self.activeSuits.count(target) != 0: + targetIndex = self.activeSuits.index(target) + else: + targetIndex = -1 + targetIndices.append(targetIndex) + + for i in xrange(4 - len(ids)): + toonIndices.append(-1) + tracks.append(-1) + levels.append(-1) + targetIndices.append(-1) + + self.townBattleAttacks = (toonIndices, + tracks, + levels, + targetIndices) + if self.localToonActive() and localToonInList == 1: + if unAttack == 1 and self.fsm.getCurrentState().getName() == 'WaitForInput': + if self.townBattle.fsm.getCurrentState().getName() != 'Attack': + self.townBattle.setState('Attack') + self.townBattle.updateChosenAttacks(self.townBattleAttacks[0], self.townBattleAttacks[1], self.townBattleAttacks[2], self.townBattleAttacks[3]) + return + + def setBattleExperience(self, id0, origExp0, earnedExp0, origQuests0, items0, missedItems0, origMerits0, merits0, parts0, id1, origExp1, earnedExp1, origQuests1, items1, missedItems1, origMerits1, merits1, parts1, id2, origExp2, earnedExp2, origQuests2, items2, missedItems2, origMerits2, merits2, parts2, id3, origExp3, earnedExp3, origQuests3, items3, missedItems3, origMerits3, merits3, parts3, deathList, uberList, helpfulToonsList): + if self.__battleCleanedUp: + return + self.movie.genRewardDicts(id0, origExp0, earnedExp0, origQuests0, items0, missedItems0, origMerits0, merits0, parts0, id1, origExp1, earnedExp1, origQuests1, items1, missedItems1, origMerits1, merits1, parts1, id2, origExp2, earnedExp2, origQuests2, items2, missedItems2, origMerits2, merits2, parts2, id3, origExp3, earnedExp3, origQuests3, items3, missedItems3, origMerits3, merits3, parts3, deathList, uberList, helpfulToonsList) + + def __listenForUnexpectedExit(self, toon): + self.accept(toon.uniqueName('disable'), self.__handleUnexpectedExit, extraArgs=[toon]) + self.accept(toon.uniqueName('died'), self.__handleDied, extraArgs=[toon]) + + def __handleUnexpectedExit(self, toon): + self.notify.warning('handleUnexpectedExit() - toon: %d' % toon.doId) + self.__removeToon(toon, unexpected=1) + + def __handleDied(self, toon): + self.notify.warning('handleDied() - toon: %d' % toon.doId) + if toon == base.localAvatar: + self.d_toonDied(toon.doId) + self.cleanupBattle() + + def delayDeleteMembers(self): + membersKeep = [] + for t in self.toons: + membersKeep.append(DelayDelete.DelayDelete(t, 'delayDeleteMembers')) + + for s in self.suits: + membersKeep.append(DelayDelete.DelayDelete(s, 'delayDeleteMembers')) + + self._removeMembersKeep() + self.membersKeep = membersKeep + + def _removeMembersKeep(self): + if self.membersKeep: + for delayDelete in self.membersKeep: + delayDelete.destroy() + + self.membersKeep = None + return + + def __removeSuit(self, suit): + self.notify.debug('__removeSuit(%d)' % suit.doId) + if self.suits.count(suit) != 0: + self.suits.remove(suit) + if self.joiningSuits.count(suit) != 0: + self.joiningSuits.remove(suit) + if self.pendingSuits.count(suit) != 0: + self.pendingSuits.remove(suit) + if self.activeSuits.count(suit) != 0: + self.activeSuits.remove(suit) + self.suitGone = 1 + if suit.battleTrap != NO_TRAP: + self.notify.debug('882 calling self.removeTrap, suit=%d' % suit.doId) + self.removeTrap(suit) + suit.battleTrap = NO_TRAP + suit.battleTrapProp = None + self.notify.debug('883 suit.battleTrapProp = None') + suit.battleTrapIsFresh = 0 + return + + def __removeToon(self, toon, unexpected = 0): + self.notify.debug('__removeToon(%d)' % toon.doId) + self.exitedToons.append(toon) + if self.toons.count(toon) != 0: + self.toons.remove(toon) + if self.joiningToons.count(toon) != 0: + self.clearInterval(self.taskName('to-pending-toon-%d' % toon.doId)) + if toon in self.joiningToons: + self.joiningToons.remove(toon) + if self.pendingToons.count(toon) != 0: + self.pendingToons.remove(toon) + if self.activeToons.count(toon) != 0: + self.activeToons.remove(toon) + if self.runningToons.count(toon) != 0: + self.clearInterval(self.taskName('running-%d' % toon.doId), finish=1) + if toon in self.runningToons: + self.runningToons.remove(toon) + self.ignore(toon.uniqueName('disable')) + self.ignore(toon.uniqueName('died')) + self.toonGone = 1 + if toon == base.localAvatar: + self.removeLocalToon() + self.__teleportToSafeZone(toon) + return 1 + return 0 + + def removeLocalToon(self): + if self._skippingRewardMovie: + return + if base.cr.playGame.getPlace() != None: + base.cr.playGame.getPlace().setState('walk') + base.localAvatar.earnedExperience = None + self.localToonFsm.request('NoLocalToon') + return + + def removeInactiveLocalToon(self, toon): + self.notify.debug('removeInactiveLocalToon(%d)' % toon.doId) + self.exitedToons.append(toon) + if self.toons.count(toon) != 0: + self.toons.remove(toon) + if self.joiningToons.count(toon) != 0: + self.clearInterval(self.taskName('to-pending-toon-%d' % toon.doId), finish=1) + if toon in self.joiningToons: + self.joiningToons.remove(toon) + if self.pendingToons.count(toon) != 0: + self.pendingToons.remove(toon) + self.ignore(toon.uniqueName('disable')) + self.ignore(toon.uniqueName('died')) + base.cr.playGame.getPlace().setState('walk') + self.localToonFsm.request('WaitForServer') + + def __createJoinInterval(self, av, destPos, destHpr, name, ts, callback, toon = 0): + joinTrack = Sequence() + joinTrack.append(Func(Emote.globalEmote.disableAll, av, 'dbattlebase, createJoinInterval')) + avPos = av.getPos(self) + avPos = Point3(avPos[0], avPos[1], 0.0) + av.setShadowHeight(0) + plist = self.buildJoinPointList(avPos, destPos, toon) + if len(plist) == 0: + joinTrack.append(Func(av.headsUp, self, destPos)) + if toon == 0: + timeToDest = self.calcSuitMoveTime(avPos, destPos) + joinTrack.append(Func(av.loop, 'walk')) + else: + timeToDest = self.calcToonMoveTime(avPos, destPos) + joinTrack.append(Func(av.loop, 'run')) + if timeToDest > BATTLE_SMALL_VALUE: + joinTrack.append(LerpPosInterval(av, timeToDest, destPos, other=self)) + totalTime = timeToDest + else: + totalTime = 0 + else: + timeToPerimeter = 0 + if toon == 0: + timeToPerimeter = self.calcSuitMoveTime(plist[0], avPos) + timePerSegment = 10.0 / BattleBase.suitSpeed + timeToDest = self.calcSuitMoveTime(BattleBase.posA, destPos) + else: + timeToPerimeter = self.calcToonMoveTime(plist[0], avPos) + timePerSegment = 10.0 / BattleBase.toonSpeed + timeToDest = self.calcToonMoveTime(BattleBase.posE, destPos) + totalTime = timeToPerimeter + (len(plist) - 1) * timePerSegment + timeToDest + if totalTime > MAX_JOIN_T: + self.notify.warning('__createJoinInterval() - time: %f' % totalTime) + joinTrack.append(Func(av.headsUp, self, plist[0])) + if toon == 0: + joinTrack.append(Func(av.loop, 'walk')) + else: + joinTrack.append(Func(av.loop, 'run')) + joinTrack.append(LerpPosInterval(av, timeToPerimeter, plist[0], other=self)) + for p in plist[1:]: + joinTrack.append(Func(av.headsUp, self, p)) + joinTrack.append(LerpPosInterval(av, timePerSegment, p, other=self)) + + joinTrack.append(Func(av.headsUp, self, destPos)) + joinTrack.append(LerpPosInterval(av, timeToDest, destPos, other=self)) + joinTrack.append(Func(av.loop, 'neutral')) + joinTrack.append(Func(av.headsUp, self, Point3(0, 0, 0))) + tval = totalTime - ts + if tval < 0: + tval = totalTime + joinTrack.append(Func(Emote.globalEmote.releaseAll, av, 'dbattlebase, createJoinInterval')) + joinTrack.append(Func(callback, av, tval)) + if av == base.localAvatar: + camTrack = Sequence() + + def setCamFov(fov): + base.camLens.setMinFov(fov/(4./3.)) + + camTrack.append(Func(setCamFov, self.camFov)) + camTrack.append(Func(camera.wrtReparentTo, self)) + camTrack.append(Func(camera.setPos, self.camJoinPos)) + camTrack.append(Func(camera.setHpr, self.camJoinHpr)) + return Parallel(joinTrack, camTrack, name=name) + else: + return Sequence(joinTrack, name=name) + + def makeSuitJoin(self, suit, ts): + self.notify.debug('makeSuitJoin(%d)' % suit.doId) + spotIndex = len(self.pendingSuits) + len(self.joiningSuits) + self.joiningSuits.append(suit) + suit.setState('Battle') + openSpot = self.suitPendingPoints[spotIndex] + pos = openSpot[0] + hpr = VBase3(openSpot[1], 0.0, 0.0) + trackName = self.taskName('to-pending-suit-%d' % suit.doId) + track = self.__createJoinInterval(suit, pos, hpr, trackName, ts, self.__handleSuitJoinDone) + track.start(ts) + track.delayDelete = DelayDelete.DelayDelete(suit, 'makeSuitJoin') + self.storeInterval(track, trackName) + if ToontownBattleGlobals.SkipMovie: + track.finish() + + def __handleSuitJoinDone(self, suit, ts): + self.notify.debug('suit: %d is now pending' % suit.doId) + if self.hasLocalToon(): + self.d_joinDone(base.localAvatar.doId, suit.doId) + + def __makeSuitPending(self, suit): + self.notify.debug('__makeSuitPending(%d)' % suit.doId) + self.clearInterval(self.taskName('to-pending-suit-%d' % suit.doId), finish=1) + if self.joiningSuits.count(suit): + self.joiningSuits.remove(suit) + self.pendingSuits.append(suit) + + def __teleportToSafeZone(self, toon): + self.notify.debug('teleportToSafeZone(%d)' % toon.doId) + hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId) + if hoodId in base.localAvatar.hoodsVisited: + target_sz = ZoneUtil.getSafeZoneId(self.zoneId) + else: + target_sz = ZoneUtil.getSafeZoneId(base.localAvatar.defaultZone) + base.cr.playGame.getPlace().fsm.request('teleportOut', [{'loader': ZoneUtil.getLoaderName(target_sz), + 'where': ZoneUtil.getWhereName(target_sz, 1), + 'how': 'teleportIn', + 'hoodId': target_sz, + 'zoneId': target_sz, + 'shardId': None, + 'avId': -1, + 'battle': 1}]) + return + + def __makeToonJoin(self, toon, pendingToons, ts): + self.notify.debug('__makeToonJoin(%d)' % toon.doId) + spotIndex = len(pendingToons) + len(self.joiningToons) + self.joiningToons.append(toon) + openSpot = self.toonPendingPoints[spotIndex] + pos = openSpot[0] + hpr = VBase3(openSpot[1], 0.0, 0.0) + trackName = self.taskName('to-pending-toon-%d' % toon.doId) + track = self.__createJoinInterval(toon, pos, hpr, trackName, ts, self.__handleToonJoinDone, toon=1) + if toon != base.localAvatar: + toon.animFSM.request('off') + track.start(ts) + track.delayDelete = DelayDelete.DelayDelete(toon, '__makeToonJoin') + self.storeInterval(track, trackName) + + def __handleToonJoinDone(self, toon, ts): + self.notify.debug('__handleToonJoinDone() - pending: %d' % toon.doId) + if self.hasLocalToon(): + self.d_joinDone(base.localAvatar.doId, toon.doId) + + def __makeToonPending(self, toon, ts): + self.notify.debug('__makeToonPending(%d)' % toon.doId) + self.clearInterval(self.taskName('to-pending-toon-%d' % toon.doId), finish=1) + if self.joiningToons.count(toon): + self.joiningToons.remove(toon) + spotIndex = len(self.pendingToons) + self.pendingToons.append(toon) + openSpot = self.toonPendingPoints[spotIndex] + pos = openSpot[0] + hpr = VBase3(openSpot[1], 0.0, 0.0) + toon.loop('neutral') + toon.setPosHpr(self, pos, hpr) + if base.localAvatar == toon: + currStateName = self.fsm.getCurrentState().getName() + + def __makeAvsActive(self, suits, toons): + self.notify.debug('__makeAvsActive()') + self.__stopAdjusting() + for s in suits: + if self.joiningSuits.count(s): + self.notify.warning('suit: %d was in joining list!' % s.doId) + self.joiningSuits.remove(s) + if self.pendingSuits.count(s): + self.pendingSuits.remove(s) + self.notify.debug('__makeAvsActive() - suit: %d' % s.doId) + self.activeSuits.append(s) + + if len(self.activeSuits) >= 1: + for suit in self.activeSuits: + suitPos, suitHpr = self.getActorPosHpr(suit) + if self.isSuitLured(suit) == 0: + suit.setPosHpr(self, suitPos, suitHpr) + else: + spos = Point3(suitPos[0], suitPos[1] - MovieUtil.SUIT_LURE_DISTANCE, suitPos[2]) + suit.setPosHpr(self, spos, suitHpr) + suit.loop('neutral') + + for toon in toons: + if self.joiningToons.count(toon): + self.notify.warning('toon: %d was in joining list!' % toon.doId) + self.joiningToons.remove(toon) + if self.pendingToons.count(toon): + self.pendingToons.remove(toon) + self.notify.debug('__makeAvsActive() - toon: %d' % toon.doId) + if self.activeToons.count(toon) == 0: + self.activeToons.append(toon) + else: + self.notify.warning('makeAvsActive() - toon: %d is active!' % toon.doId) + + if len(self.activeToons) >= 1: + for toon in self.activeToons: + toonPos, toonHpr = self.getActorPosHpr(toon) + toon.setPosHpr(self, toonPos, toonHpr) + toon.loop('neutral') + + if self.fsm.getCurrentState().getName() == 'WaitForInput' and self.localToonActive() and self.localToonJustJoined == 1: + self.notify.debug('makeAvsActive() - local toon just joined') + self.__enterLocalToonWaitForInput() + self.localToonJustJoined = 0 + self.startTimer() + + def __makeToonRun(self, toon, ts): + self.notify.debug('__makeToonRun(%d)' % toon.doId) + if self.activeToons.count(toon): + self.activeToons.remove(toon) + self.runningToons.append(toon) + self.toonGone = 1 + self.__stopTimer() + if self.localToonRunning(): + self.townBattle.setState('Off') + runMTrack = MovieUtil.getToonTeleportOutInterval(toon) + runName = self.taskName('running-%d' % toon.doId) + self.notify.debug('duration: %f' % runMTrack.getDuration()) + runMTrack.start(ts) + runMTrack.delayDelete = DelayDelete.DelayDelete(toon, '__makeToonRun') + self.storeInterval(runMTrack, runName) + + def getToon(self, toonId): + if toonId in self.cr.doId2do: + return self.cr.doId2do[toonId] + else: + self.notify.warning('getToon() - toon: %d not in repository!' % toonId) + return None + return None + + def d_toonRequestJoin(self, toonId, pos): + self.notify.debug('network:toonRequestJoin()') + self.sendUpdate('toonRequestJoin', [pos[0], pos[1], pos[2]]) + + def d_toonRequestRun(self, toonId): + self.notify.debug('network:toonRequestRun()') + self.sendUpdate('toonRequestRun', []) + + def d_toonDied(self, toonId): + self.notify.debug('network:toonDied()') + self.sendUpdate('toonDied', []) + + def d_faceOffDone(self, toonId): + self.notify.debug('network:faceOffDone()') + self.sendUpdate('faceOffDone', []) + + def d_adjustDone(self, toonId): + self.notify.debug('network:adjustDone()') + self.sendUpdate('adjustDone', []) + + def d_timeout(self, toonId): + self.notify.debug('network:timeout()') + self.sendUpdate('timeout', []) + + def d_movieDone(self, toonId): + self.notify.debug('network:movieDone()') + self.sendUpdate('movieDone', []) + + def d_rewardDone(self, toonId): + self.notify.debug('network:rewardDone()') + self.sendUpdate('rewardDone', []) + + def d_joinDone(self, toonId, avId): + self.notify.debug('network:joinDone(%d)' % avId) + self.sendUpdate('joinDone', [avId]) + + def d_requestAttack(self, toonId, track, level, av): + self.notify.debug('network:requestAttack(%d, %d, %d)' % (track, level, av)) + self.sendUpdate('requestAttack', [track, level, av]) + + def d_requestPetProxy(self, toonId, av): + self.notify.debug('network:requestPetProxy(%s)' % av) + self.sendUpdate('requestPetProxy', [av]) + + def enterOff(self, ts = 0): + self.localToonFsm.requestFinalState() + return None + + def exitOff(self): + return None + + def enterFaceOff(self, ts = 0): + return None + + def exitFaceOff(self): + return None + + def enterWaitForJoin(self, ts = 0): + self.notify.debug('enterWaitForJoin()') + return None + + def exitWaitForJoin(self): + return None + + def __enterLocalToonWaitForInput(self): + self.notify.debug('enterLocalToonWaitForInput()') + camera.setPosHpr(self.camPos, self.camHpr) + base.camLens.setMinFov(self.camMenuFov/(4./3.)) + NametagGlobals.setMasterArrowsOn(0) + self.townBattle.setState('Attack') + self.accept(self.localToonBattleEvent, self.__handleLocalToonBattleEvent) + + def startTimer(self, ts = 0): + self.notify.debug('startTimer()') + if ts >= CLIENT_INPUT_TIMEOUT: + self.notify.warning('startTimer() - ts: %f timeout: %f' % (ts, CLIENT_INPUT_TIMEOUT)) + self.__timedOut() + return + self.timer.startCallback(CLIENT_INPUT_TIMEOUT - ts, self.__timedOut) + timeTask = Task.loop(Task(self.__countdown), Task.pause(0.2)) + taskMgr.add(timeTask, self.timerCountdownTaskName) + + def __stopTimer(self): + self.notify.debug('__stopTimer()') + self.timer.stop() + taskMgr.remove(self.timerCountdownTaskName) + + def __countdown(self, task): + if hasattr(self.townBattle, 'timer'): + self.townBattle.updateTimer(int(self.timer.getT())) + else: + self.notify.warning('__countdown has tried to update a timer that has been deleted. Stopping timer') + self.__stopTimer() + return Task.done + + def enterWaitForInput(self, ts = 0): + prop = self.getInteractiveProp() + + if prop: + prop.gotoBattleCheer() + self.choseAttackAlready = 0 + if self.localToonActive(): + self.__enterLocalToonWaitForInput() + self.startTimer(ts) + if self.needAdjustTownBattle == 1: + self.__adjustTownBattle() + return None + + def exitWaitForInput(self): + self.notify.debug('exitWaitForInput()') + if self.localToonActive(): + self.townBattle.setState('Off') + base.camLens.setMinFov(self.camFov/(4./3.)) + self.ignore(self.localToonBattleEvent) + self.__stopTimer() + return None + + def __handleLocalToonBattleEvent(self, response): + mode = response['mode'] + noAttack = 0 + if mode == 'Attack': + self.notify.debug('got an attack') + track = response['track'] + level = response['level'] + target = response['target'] + targetId = target + if track == HEAL and not levelAffectsGroup(HEAL, level): + if target >= 0 and target < len(self.activeToons): + targetId = self.activeToons[target].doId + else: + self.notify.warning('invalid toon target: %d' % target) + track = -1 + level = -1 + targetId = -1 + elif track == HEAL and len(self.activeToons) == 1: + self.notify.warning('invalid group target for heal') + track = -1 + level = -1 + elif not attackAffectsGroup(track, level): + if target >= 0 and target < len(self.activeSuits): + targetId = self.activeSuits[target].doId + else: + target = -1 + if len(self.luredSuits) > 0: + if track == TRAP or track == LURE and not levelAffectsGroup(LURE, level): + if target != -1: + suit = self.findSuit(targetId) + if self.luredSuits.count(suit) != 0: + self.notify.warning('Suit: %d was lured!' % targetId) + track = -1 + level = -1 + targetId = -1 + elif track == LURE: + if levelAffectsGroup(LURE, level) and len(self.activeSuits) == len(self.luredSuits): + self.notify.warning('All suits are lured!') + track = -1 + level = -1 + targetId = -1 + if track == TRAP: + if target != -1: + if attackAffectsGroup(track, level): + pass + else: + suit = self.findSuit(targetId) + if suit.battleTrap != NO_TRAP: + self.notify.warning('Suit: %d was already trapped!' % targetId) + track = -1 + level = -1 + targetId = -1 + self.d_requestAttack(base.localAvatar.doId, track, level, targetId) + elif mode == 'Run': + self.notify.debug('got a run') + self.d_toonRequestRun(base.localAvatar.doId) + elif mode == 'SOS': + targetId = response['id'] + self.notify.debug('got an SOS for friend: %d' % targetId) + self.d_requestAttack(base.localAvatar.doId, SOS, -1, targetId) + elif mode == 'NPCSOS': + targetId = response['id'] + self.notify.debug('got an NPCSOS for friend: %d' % targetId) + self.d_requestAttack(base.localAvatar.doId, NPCSOS, -1, targetId) + elif mode == 'PETSOS': + targetId = response['id'] + trickId = response['trickId'] + self.notify.debug('got an PETSOS for pet: %d' % targetId) + self.d_requestAttack(base.localAvatar.doId, PETSOS, trickId, targetId) + elif mode == 'PETSOSINFO': + petProxyId = response['id'] + self.notify.debug('got a PETSOSINFO for pet: %d' % petProxyId) + if petProxyId in base.cr.doId2do: + self.notify.debug('pet: %d was already in the repository' % petProxyId) + proxyGenerateMessage = 'petProxy-%d-generated' % petProxyId + messenger.send(proxyGenerateMessage) + else: + self.d_requestPetProxy(base.localAvatar.doId, petProxyId) + noAttack = 1 + elif mode == 'Pass': + targetId = response['id'] + self.notify.debug('got a Pass') + self.d_requestAttack(base.localAvatar.doId, PASS, -1, -1) + elif mode == 'UnAttack': + self.d_requestAttack(base.localAvatar.doId, UN_ATTACK, -1, -1) + noAttack = 1 + elif mode == 'Fire': + target = response['target'] + targetId = self.activeSuits[target].doId + self.d_requestAttack(base.localAvatar.doId, FIRE, -1, targetId) + else: + self.notify.warning('unknown battle response') + return + if noAttack == 1: + self.choseAttackAlready = 0 + else: + self.choseAttackAlready = 1 + + def __timedOut(self): + if self.choseAttackAlready == 1: + return + self.notify.debug('WaitForInput timed out') + if self.localToonActive(): + self.notify.debug('battle timed out') + self.d_timeout(base.localAvatar.doId) + + def enterMakeMovie(self, ts = 0): + self.notify.debug('enterMakeMovie()') + return None + + def exitMakeMovie(self): + return None + + def enterPlayMovie(self, ts): + self.notify.debug('enterPlayMovie()') + self.delayDeleteMembers() + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + if ToontownBattleGlobals.SkipMovie: + self.movie.play(ts, self.__handleMovieDone) + self.movie.finish() + else: + self.movie.play(ts, self.__handleMovieDone) + return None + + def __handleMovieDone(self): + self.notify.debug('__handleMovieDone()') + if self.hasLocalToon(): + self.d_movieDone(base.localAvatar.doId) + self.movie.reset() + + def exitPlayMovie(self): + self.notify.debug('exitPlayMovie()') + self.movie.reset(finish=1) + self._removeMembersKeep() + self.townBattleAttacks = ([-1, + -1, + -1, + -1], + [-1, + -1, + -1, + -1], + [-1, + -1, + -1, + -1], + [0, + 0, + 0, + 0]) + return None + + def hasLocalToon(self): + return self.toons.count(base.localAvatar) > 0 + + def localToonPendingOrActive(self): + return self.pendingToons.count(base.localAvatar) > 0 or self.activeToons.count(base.localAvatar) > 0 + + def localToonActive(self): + return self.activeToons.count(base.localAvatar) > 0 + + def localToonActiveOrRunning(self): + return self.activeToons.count(base.localAvatar) > 0 or self.runningToons.count(base.localAvatar) > 0 + + def localToonRunning(self): + return self.runningToons.count(base.localAvatar) > 0 + + def enterHasLocalToon(self): + self.notify.debug('enterHasLocalToon()') + if base.cr.playGame.getPlace() != None: + base.cr.playGame.getPlace().setState('battle', self.localToonBattleEvent) + if localAvatar and hasattr(localAvatar, 'inventory') and localAvatar.inventory: + prop = self.getInteractiveProp() + + if prop: + localAvatar.inventory.setInteractivePropTrackBonus(prop.BattleTrack) + camera.wrtReparentTo(self) + base.camLens.setMinFov(self.camFov/(4./3.)) + return + + def exitHasLocalToon(self): + self.ignore(self.localToonBattleEvent) + self.__stopTimer() + if localAvatar and hasattr(localAvatar, 'inventory') and localAvatar.inventory: + localAvatar.inventory.setInteractivePropTrackBonus(-1) + stateName = None + place = base.cr.playGame.getPlace() + if place: + stateName = place.fsm.getCurrentState().getName() + if stateName == 'died': + self.movie.reset() + camera.reparentTo(render) + camera.setPosHpr(localAvatar, 5.2, 5.45, localAvatar.getHeight() * 0.66, 131.5, 3.6, 0) + else: + camera.wrtReparentTo(base.localAvatar) + messenger.send('localToonLeftBattle') + base.camLens.setMinFov(settings['fov']/(4./3.)) + return + + def enterNoLocalToon(self): + self.notify.debug('enterNoLocalToon()') + return None + + def exitNoLocalToon(self): + return None + + def setSkippingRewardMovie(self): + self._skippingRewardMovie = True + + def enterWaitForServer(self): + self.notify.debug('enterWaitForServer()') + return None + + def exitWaitForServer(self): + return None + + def createAdjustInterval(self, av, destPos, destHpr, toon = 0, run = 0): + if run == 1: + adjustTime = self.calcToonMoveTime(destPos, av.getPos(self)) + else: + adjustTime = self.calcSuitMoveTime(destPos, av.getPos(self)) + self.notify.debug('creating adjust interval for: %d' % av.doId) + adjustTrack = Sequence() + if run == 1: + adjustTrack.append(Func(av.loop, 'run')) + else: + adjustTrack.append(Func(av.loop, 'walk')) + adjustTrack.append(Func(av.headsUp, self, destPos)) + adjustTrack.append(LerpPosInterval(av, adjustTime, destPos, other=self)) + adjustTrack.append(Func(av.setHpr, self, destHpr)) + adjustTrack.append(Func(av.loop, 'neutral')) + return adjustTrack + + def __adjust(self, ts, callback): + self.notify.debug('__adjust(%f)' % ts) + adjustTrack = Parallel() + if len(self.pendingSuits) > 0 or self.suitGone == 1: + self.suitGone = 0 + numSuits = len(self.pendingSuits) + len(self.activeSuits) - 1 + index = 0 + for suit in self.activeSuits: + point = self.suitPoints[numSuits][index] + pos = suit.getPos(self) + destPos = point[0] + if self.isSuitLured(suit) == 1: + destPos = Point3(destPos[0], destPos[1] - MovieUtil.SUIT_LURE_DISTANCE, destPos[2]) + if pos != destPos: + destHpr = VBase3(point[1], 0.0, 0.0) + adjustTrack.append(self.createAdjustInterval(suit, destPos, destHpr)) + index += 1 + + for suit in self.pendingSuits: + point = self.suitPoints[numSuits][index] + destPos = point[0] + destHpr = VBase3(point[1], 0.0, 0.0) + adjustTrack.append(self.createAdjustInterval(suit, destPos, destHpr)) + index += 1 + + if len(self.pendingToons) > 0 or self.toonGone == 1: + self.toonGone = 0 + numToons = len(self.pendingToons) + len(self.activeToons) - 1 + index = 0 + for toon in self.activeToons: + point = self.toonPoints[numToons][index] + pos = toon.getPos(self) + destPos = point[0] + if pos != destPos: + destHpr = VBase3(point[1], 0.0, 0.0) + adjustTrack.append(self.createAdjustInterval(toon, destPos, destHpr)) + index += 1 + + for toon in self.pendingToons: + point = self.toonPoints[numToons][index] + destPos = point[0] + destHpr = VBase3(point[1], 0.0, 0.0) + adjustTrack.append(self.createAdjustInterval(toon, destPos, destHpr)) + index += 1 + + if len(adjustTrack) > 0: + self.notify.debug('creating adjust multitrack') + e = Func(self.__handleAdjustDone) + track = Sequence(adjustTrack, e, name=self.adjustName) + self.storeInterval(track, self.adjustName) + track.start(ts) + if ToontownBattleGlobals.SkipMovie: + track.finish() + else: + self.notify.warning('adjust() - nobody needed adjusting') + self.__adjustDone() + + def __handleAdjustDone(self): + self.notify.debug('__handleAdjustDone() - client adjust finished') + self.clearInterval(self.adjustName) + self.__adjustDone() + + def __stopAdjusting(self): + self.notify.debug('__stopAdjusting()') + self.clearInterval(self.adjustName) + if self.adjustFsm.getCurrentState().getName() == 'Adjusting': + self.adjustFsm.request('NotAdjusting') + + def __requestAdjustTownBattle(self): + self.notify.debug('__requestAdjustTownBattle() curstate = %s' % self.fsm.getCurrentState().getName()) + if self.fsm.getCurrentState().getName() == 'WaitForInput': + self.__adjustTownBattle() + else: + self.needAdjustTownBattle = 1 + + def __adjustTownBattle(self): + self.notify.debug('__adjustTownBattle()') + if self.localToonActive() and len(self.activeSuits) > 0: + self.notify.debug('__adjustTownBattle() - adjusting town battle') + luredSuits = [] + for suit in self.luredSuits: + if suit not in self.activeSuits: + self.notify.error('lured suit not in self.activeSuits') + luredSuits.append(self.activeSuits.index(suit)) + + trappedSuits = [] + for suit in self.activeSuits: + if suit.battleTrap != NO_TRAP: + trappedSuits.append(self.activeSuits.index(suit)) + + self.townBattle.adjustCogsAndToons(self.activeSuits, luredSuits, trappedSuits, self.activeToons) + if hasattr(self, 'townBattleAttacks'): + self.townBattle.updateChosenAttacks(self.townBattleAttacks[0], self.townBattleAttacks[1], self.townBattleAttacks[2], self.townBattleAttacks[3]) + self.needAdjustTownBattle = 0 + + def __adjustDone(self): + self.notify.debug('__adjustDone()') + if self.hasLocalToon(): + self.d_adjustDone(base.localAvatar.doId) + self.adjustFsm.request('NotAdjusting') + + def enterAdjusting(self, ts): + self.notify.debug('enterAdjusting()') + if self.localToonActive(): + self.__stopTimer() + self.delayDeleteMembers() + self.__adjust(ts, self.__handleAdjustDone) + return None + + def exitAdjusting(self): + self.notify.debug('exitAdjusting()') + self.finishInterval(self.adjustName) + self._removeMembersKeep() + currStateName = self.fsm.getCurrentState().getName() + if currStateName == 'WaitForInput' and self.localToonActive(): + self.startTimer() + return None + + def enterNotAdjusting(self): + self.notify.debug('enterNotAdjusting()') + return None + + def exitNotAdjusting(self): + return None + + def visualize(self): + try: + self.isVisualized + except: + self.isVisualized = 0 + + if self.isVisualized: + self.vis.removeNode() + del self.vis + self.detachNode() + self.isVisualized = 0 + else: + lsegs = LineSegs() + lsegs.setColor(0.5, 0.5, 1, 1) + lsegs.moveTo(0, 0, 0) + for p in BattleBase.allPoints: + lsegs.drawTo(p[0], p[1], p[2]) + + p = BattleBase.allPoints[0] + lsegs.drawTo(p[0], p[1], p[2]) + self.vis = self.attachNewNode(lsegs.create()) + self.reparentTo(render) + self.isVisualized = 1 + + def setupCollisions(self, name): + self.lockout = CollisionTube(0, 0, 0, 0, 0, 9, 9) + lockoutNode = CollisionNode(name) + lockoutNode.addSolid(self.lockout) + lockoutNode.setCollideMask(ToontownGlobals.WallBitmask) + self.lockoutNodePath = self.attachNewNode(lockoutNode) + self.lockoutNodePath.detachNode() + + def removeCollisionData(self): + del self.lockout + self.lockoutNodePath.removeNode() + del self.lockoutNodePath + + def enableCollision(self): + self.lockoutNodePath.reparentTo(self) + if len(self.toons) < 4: + self.accept(self.getCollisionName(), self.__handleLocalToonCollision) + + def __handleLocalToonCollision(self, collEntry): + self.notify.debug('localToonCollision') + if self.fsm.getCurrentState().getName() == 'Off': + self.notify.debug('ignoring collision in Off state') + return + if not base.localAvatar.wantBattles: + return + if self._skippingRewardMovie: + return + base.cr.playGame.getPlace().setState('WaitForBattle') + toon = base.localAvatar + self.d_toonRequestJoin(toon.doId, toon.getPos(self)) + base.localAvatar.preBattleHpr = base.localAvatar.getHpr(render) + self.localToonFsm.request('WaitForServer') + self.onWaitingForJoin() + + def onWaitingForJoin(self): + pass + + def denyLocalToonJoin(self): + self.notify.debug('denyLocalToonJoin()') + place = self.cr.playGame.getPlace() + if place.fsm.getCurrentState().getName() == 'WaitForBattle': + place.setState('walk') + self.localToonFsm.request('NoLocalToon') + + def disableCollision(self): + self.ignore(self.getCollisionName()) + self.lockoutNodePath.detachNode() + + def openBattleCollision(self): + if not self.hasLocalToon(): + self.enableCollision() + + def closeBattleCollision(self): + self.ignore(self.getCollisionName()) + + def getCollisionName(self): + return 'enter' + self.lockoutNodePath.getName() + + def setFireCount(self, amount): + self.fireCount = amount + + def getFireCount(self): + return self.fireCount diff --git a/toontown/battle/DistributedBattleBaseAI.py b/toontown/battle/DistributedBattleBaseAI.py new file mode 100755 index 00000000..3c1b31cc --- /dev/null +++ b/toontown/battle/DistributedBattleBaseAI.py @@ -0,0 +1,1848 @@ +from otp.ai.AIBase import * +from direct.distributed.ClockDelta import * +from BattleBase import * +from BattleCalculatorAI import * +from toontown.toonbase.ToontownBattleGlobals import * +from SuitBattleGlobals import * +from panda3d.core import * +import BattleExperienceAI +from direct.distributed import DistributedObjectAI +from direct.fsm import ClassicFSM, State +from direct.task import Task +from direct.directnotify import DirectNotifyGlobal +from toontown.toon import InventoryBase +from toontown.toonbase import ToontownGlobals +from toontown.pets import DistributedPetProxyAI +import random +from toontown.toon import NPCToons +from otp.ai.MagicWordGlobal import * + +class DistributedBattleBaseAI(DistributedObjectAI.DistributedObjectAI, BattleBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleBaseAI') + + def __init__(self, air, zoneId, finishCallback = None, maxSuits = 4, bossBattle = 0, tutorialFlag = 0, interactivePropTrackBonus = -1): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.serialNum = 0 + self.zoneId = zoneId + self.maxSuits = maxSuits + self.setBossBattle(bossBattle) + self.tutorialFlag = tutorialFlag + self.interactivePropTrackBonus = interactivePropTrackBonus + self.finishCallback = finishCallback + self.avatarExitEvents = [] + self.responses = {} + self.adjustingResponses = {} + self.joinResponses = {} + self.adjustingSuits = [] + self.adjustingToons = [] + self.numSuitsEver = 0 + BattleBase.__init__(self) + self.streetBattle = 1 + self.pos = Point3(0, 0, 0) + self.initialSuitPos = Point3(0, 0, 0) + self.toonExp = {} + self.toonOrigQuests = {} + self.toonItems = {} + self.toonOrigMerits = {} + self.toonMerits = {} + self.toonParts = {} + self.battleCalc = BattleCalculatorAI(self, tutorialFlag) + if self.air.suitInvasionManager.getInvading(): + mult = getInvasionMultiplier() + self.battleCalc.setSkillCreditMultiplier(mult) + if self.air.newsManager.isHolidayRunning(ToontownGlobals.MORE_XP_HOLIDAY): + mult = getMoreXpHolidayMultiplier() + self.battleCalc.setSkillCreditMultiplier(mult) + self.fsm = None + self.clearAttacks() + self.ignoreFaceOffDone = 0 + self.needAdjust = 0 + self.movieHasBeenMade = 0 + self.movieHasPlayed = 0 + self.rewardHasPlayed = 0 + self.movieRequested = 0 + self.ignoreResponses = 0 + self.ignoreAdjustingResponses = 0 + self.taskNames = [] + self.exitedToons = [] + self.suitsKilled = [] + self.suitsKilledThisBattle = [] + self.suitsKilledPerFloor = [] + self.suitsEncountered = [] + self.newToons = [] + self.newSuits = [] + self.numNPCAttacks = 0 + self.npcAttacks = {} + self.pets = {} + self.fsm = ClassicFSM.ClassicFSM('DistributedBattleAI', [State.State('FaceOff', self.enterFaceOff, self.exitFaceOff, ['WaitForInput', 'Resume']), + State.State('WaitForJoin', self.enterWaitForJoin, self.exitWaitForJoin, ['WaitForInput', 'Resume']), + State.State('WaitForInput', self.enterWaitForInput, self.exitWaitForInput, ['MakeMovie', 'Resume']), + State.State('MakeMovie', self.enterMakeMovie, self.exitMakeMovie, ['PlayMovie', 'Resume']), + State.State('PlayMovie', self.enterPlayMovie, self.exitPlayMovie, ['WaitForJoin', 'Reward', 'Resume']), + State.State('Reward', self.enterReward, self.exitReward, ['Resume']), + State.State('Resume', self.enterResume, self.exitResume, []), + State.State('Off', self.enterOff, self.exitOff, ['FaceOff', 'WaitForJoin'])], 'Off', 'Off') + self.joinableFsm = ClassicFSM.ClassicFSM('Joinable', [State.State('Joinable', self.enterJoinable, self.exitJoinable, ['Unjoinable']), State.State('Unjoinable', self.enterUnjoinable, self.exitUnjoinable, ['Joinable'])], 'Unjoinable', 'Unjoinable') + self.joinableFsm.enterInitialState() + self.runableFsm = ClassicFSM.ClassicFSM('Runable', [State.State('Runable', self.enterRunable, self.exitRunable, ['Unrunable']), State.State('Unrunable', self.enterUnrunable, self.exitUnrunable, ['Runable'])], 'Unrunable', 'Unrunable') + self.runableFsm.enterInitialState() + self.adjustFsm = ClassicFSM.ClassicFSM('Adjust', [State.State('Adjusting', self.enterAdjusting, self.exitAdjusting, ['NotAdjusting', 'Adjusting']), State.State('NotAdjusting', self.enterNotAdjusting, self.exitNotAdjusting, ['Adjusting'])], 'NotAdjusting', 'NotAdjusting') + self.adjustFsm.enterInitialState() + self.fsm.enterInitialState() + self.startTime = globalClock.getRealTime() + self.adjustingTimer = Timer() + return + + def clearAttacks(self): + self.toonAttacks = {} + self.suitAttacks = getDefaultSuitAttacks() + + def requestDelete(self): + if hasattr(self, 'fsm'): + self.fsm.request('Off') + self.__removeTaskName(self.uniqueName('make-movie')) + DistributedObjectAI.DistributedObjectAI.requestDelete(self) + + def delete(self): + self.notify.debug('deleting battle') + self.fsm.request('Off') + self.ignoreAll() + self.__removeAllTasks() + del self.fsm + del self.joinableFsm + del self.runableFsm + del self.adjustFsm + self.__cleanupJoinResponses() + self.timer.stop() + del self.timer + self.adjustingTimer.stop() + del self.adjustingTimer + self.battleCalc.cleanup() + del self.battleCalc + for suit in self.suits: + del suit.battleTrap + + del self.finishCallback + for petProxy in self.pets.values(): + petProxy.requestDelete() + + DistributedObjectAI.DistributedObjectAI.delete(self) + + def pause(self): + self.timer.stop() + self.adjustingTimer.stop() + + def unpause(self): + self.timer.resume() + self.adjustingTimer.resume() + + def abortBattle(self): + self.notify.debug('%s.abortBattle() called.' % self.doId) + toonsCopy = self.toons[:] + for toonId in toonsCopy: + self.__removeToon(toonId) + if self.fsm.getCurrentState().getName() == 'PlayMovie' or self.fsm.getCurrentState().getName() == 'MakeMovie': + self.exitedToons.append(toonId) + + self.d_setMembers() + self.b_setState('Resume') + self.__removeAllTasks() + self.timer.stop() + self.adjustingTimer.stop() + + def __removeSuit(self, suit): + self.notify.debug('__removeSuit(%d)' % suit.doId) + self.suits.remove(suit) + self.activeSuits.remove(suit) + if self.luredSuits.count(suit) == 1: + self.luredSuits.remove(suit) + self.suitGone = 1 + del suit.battleTrap + + def findSuit(self, id): + for s in self.suits: + if s.doId == id: + return s + + return None + + def __removeTaskName(self, name): + if self.taskNames.count(name): + self.taskNames.remove(name) + self.notify.debug('removeTaskName() - %s' % name) + taskMgr.remove(name) + + def __removeAllTasks(self): + for n in self.taskNames: + self.notify.debug('removeAllTasks() - %s' % n) + taskMgr.remove(n) + + self.taskNames = [] + + def __removeToonTasks(self, toonId): + name = self.taskName('running-toon-%d' % toonId) + self.__removeTaskName(name) + name = self.taskName('to-pending-av-%d' % toonId) + self.__removeTaskName(name) + + def getLevelDoId(self): + return 0 + + def getBattleCellId(self): + return 0 + + def getPosition(self): + self.notify.debug('getPosition() - %s' % self.pos) + return [self.pos[0], self.pos[1], self.pos[2]] + + def getInitialSuitPos(self): + p = [] + p.append(self.initialSuitPos[0]) + p.append(self.initialSuitPos[1]) + p.append(self.initialSuitPos[2]) + return p + + def setBossBattle(self, bossBattle): + self.bossBattle = bossBattle + + def getBossBattle(self): + return self.bossBattle + + def b_setState(self, state): + self.notify.debug('network:setState(%s)' % state) + stime = globalClock.getRealTime() + SERVER_BUFFER_TIME + self.sendUpdate('setState', [state, globalClockDelta.localToNetworkTime(stime)]) + self.setState(state) + + def setState(self, state): + self.fsm.request(state) + + def getState(self): + return [self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()] + + def d_setMembers(self): + self.notify.debug('network:setMembers()') + self.sendUpdate('setMembers', self.getMembers()) + + def getMembers(self): + suits = [] + for s in self.suits: + suits.append(s.doId) + + joiningSuits = '' + for s in self.joiningSuits: + joiningSuits += str(suits.index(s.doId)) + + pendingSuits = '' + for s in self.pendingSuits: + pendingSuits += str(suits.index(s.doId)) + + activeSuits = '' + for s in self.activeSuits: + activeSuits += str(suits.index(s.doId)) + + luredSuits = '' + for s in self.luredSuits: + luredSuits += str(suits.index(s.doId)) + + suitTraps = '' + for s in self.suits: + if s.battleTrap == NO_TRAP: + suitTraps += '9' + elif s.battleTrap == BattleCalculatorAI.TRAP_CONFLICT: + suitTraps += '9' + else: + suitTraps += str(s.battleTrap) + + toons = [] + for t in self.toons: + toons.append(t) + + joiningToons = '' + for t in self.joiningToons: + joiningToons += str(toons.index(t)) + + pendingToons = '' + for t in self.pendingToons: + pendingToons += str(toons.index(t)) + + activeToons = '' + for t in self.activeToons: + activeToons += str(toons.index(t)) + + runningToons = '' + for t in self.runningToons: + runningToons += str(toons.index(t)) + + self.notify.debug('getMembers() - suits: %s joiningSuits: %s pendingSuits: %s activeSuits: %s luredSuits: %s suitTraps: %s toons: %s joiningToons: %s pendingToons: %s activeToons: %s runningToons: %s' % (suits, + joiningSuits, + pendingSuits, + activeSuits, + luredSuits, + suitTraps, + toons, + joiningToons, + pendingToons, + activeToons, + runningToons)) + return [suits, + joiningSuits, + pendingSuits, + activeSuits, + luredSuits, + suitTraps, + toons, + joiningToons, + pendingToons, + activeToons, + runningToons, + globalClockDelta.getRealNetworkTime()] + + def d_adjust(self): + self.notify.debug('network:adjust()') + self.sendUpdate('adjust', [globalClockDelta.getRealNetworkTime()]) + + def getInteractivePropTrackBonus(self): + return self.interactivePropTrackBonus + + def getZoneId(self): + return self.zoneId + + def getTaskZoneId(self): + return self.zoneId + + def d_setMovie(self): + self.notify.debug('network:setMovie()') + self.sendUpdate('setMovie', self.getMovie()) + self.__updateEncounteredCogs() + + def getMovie(self): + suitIds = [] + for s in self.activeSuits: + suitIds.append(s.doId) + + p = [self.movieHasBeenMade] + p.append(self.activeToons) + p.append(suitIds) + for t in self.activeToons: + if t in self.toonAttacks: + ta = self.toonAttacks[t] + index = -1 + id = ta[TOON_ID_COL] + if id != -1: + index = self.activeToons.index(id) + track = ta[TOON_TRACK_COL] + if (track == NO_ATTACK or attackAffectsGroup(track, ta[TOON_LVL_COL])) and track != NPCSOS and track != PETSOS: + target = -1 + if track == HEAL: + if ta[TOON_LVL_COL] == 1: + ta[TOON_HPBONUS_COL] = random.randint(0, 10000) + elif track == SOS or track == NPCSOS or track == PETSOS: + target = ta[TOON_TGT_COL] + elif track == HEAL: + if self.activeToons.count(ta[TOON_TGT_COL]) != 0: + target = self.activeToons.index(ta[TOON_TGT_COL]) + else: + target = -1 + elif suitIds.count(ta[TOON_TGT_COL]) != 0: + target = suitIds.index(ta[TOON_TGT_COL]) + else: + target = -1 + p = p + [index, + track, + ta[TOON_LVL_COL], + target] + p = p + ta[4:] + else: + index = self.activeToons.index(t) + attack = getToonAttack(index) + p = p + attack + + for i in xrange(4 - len(self.activeToons)): + p = p + getToonAttack(-1) + + for sa in self.suitAttacks: + index = -1 + id = sa[SUIT_ID_COL] + if id != -1: + index = suitIds.index(id) + if sa[SUIT_ATK_COL] == -1: + targetIndex = -1 + else: + targetIndex = sa[SUIT_TGT_COL] + if targetIndex == -1: + self.notify.debug('suit attack: %d must be group' % sa[SUIT_ATK_COL]) + else: + toonId = self.activeToons[targetIndex] + p = p + [index, sa[SUIT_ATK_COL], targetIndex] + sa[SUIT_TAUNT_COL] = 0 + if sa[SUIT_ATK_COL] != -1: + suit = self.findSuit(id) + sa[SUIT_TAUNT_COL] = getAttackTauntIndexFromIndex(suit, sa[SUIT_ATK_COL]) + p = p + sa[3:] + + return p + + def d_setChosenToonAttacks(self): + self.notify.debug('network:setChosenToonAttacks()') + self.sendUpdate('setChosenToonAttacks', self.getChosenToonAttacks()) + + def getChosenToonAttacks(self): + ids = [] + tracks = [] + levels = [] + targets = [] + for t in self.activeToons: + if t in self.toonAttacks: + ta = self.toonAttacks[t] + else: + ta = getToonAttack(t) + ids.append(t) + tracks.append(ta[TOON_TRACK_COL]) + levels.append(ta[TOON_LVL_COL]) + targets.append(ta[TOON_TGT_COL]) + + return [ids, + tracks, + levels, + targets] + + def d_setBattleExperience(self): + self.notify.debug('network:setBattleExperience()') + self.sendUpdate('setBattleExperience', self.getBattleExperience()) + + def getBattleExperience(self): + returnValue = BattleExperienceAI.getBattleExperience(4, self.activeToons, self.toonExp, self.battleCalc.toonSkillPtsGained, self.toonOrigQuests, self.toonItems, self.toonOrigMerits, self.toonMerits, self.toonParts, self.suitsKilled, self.helpfulToons) + return returnValue + + def getToonUberStatus(self): + fieldList = [] + uberIndex = LAST_REGULAR_GAG_LEVEL + 1 + for toon in self.activeToons: + toonList = [] + for trackIndex in xrange(MAX_TRACK_INDEX): + toonList.append(toon.inventory.numItem(track, uberIndex)) + + fieldList.append(encodeUber(toonList)) + + return fieldList + + def addSuit(self, suit): + self.notify.debug('addSuit(%d)' % suit.doId) + self.newSuits.append(suit) + self.suits.append(suit) + suit.battleTrap = NO_TRAP + self.numSuitsEver += 1 + + def __joinSuit(self, suit): + self.joiningSuits.append(suit) + toPendingTime = MAX_JOIN_T + SERVER_BUFFER_TIME + taskName = self.taskName('to-pending-av-%d' % suit.doId) + self.__addJoinResponse(suit.doId, taskName) + self.taskNames.append(taskName) + taskMgr.doMethodLater(toPendingTime, self.__serverJoinDone, taskName, extraArgs=(suit.doId, taskName)) + + def __serverJoinDone(self, avId, taskName): + self.notify.debug('join for av: %d timed out on server' % avId) + self.__removeTaskName(taskName) + self.__makeAvPending(avId) + return Task.done + + def __makeAvPending(self, avId): + self.notify.debug('__makeAvPending(%d)' % avId) + self.__removeJoinResponse(avId) + self.__removeTaskName(self.taskName('to-pending-av-%d' % avId)) + if self.toons.count(avId) > 0: + self.joiningToons.remove(avId) + self.pendingToons.append(avId) + else: + suit = self.findSuit(avId) + if suit != None: + if not suit.isEmpty(): + if not self.joiningSuits.count(suit) == 1: + self.notify.warning('__makeAvPending(%d) in zone: %d' % (avId, self.zoneId)) + self.notify.warning('toons: %s' % self.toons) + self.notify.warning('joining toons: %s' % self.joiningToons) + self.notify.warning('pending toons: %s' % self.pendingToons) + self.notify.warning('suits: %s' % self.suits) + self.notify.warning('joining suits: %s' % self.joiningSuits) + self.notify.warning('pending suits: %s' % self.pendingSuits) + self.joiningSuits.remove(suit) + self.pendingSuits.append(suit) + else: + self.notify.warning('makeAvPending() %d not in toons or suits' % avId) + return + self.d_setMembers() + self.needAdjust = 1 + self.__requestAdjust() + return + + def suitRequestJoin(self, suit): + self.notify.debug('suitRequestJoin(%d)' % suit.getDoId()) + if self.suitCanJoin(): + self.addSuit(suit) + self.__joinSuit(suit) + self.d_setMembers() + suit.prepareToJoinBattle() + return 1 + else: + self.notify.warning('suitRequestJoin() - not joinable - joinable state: %s max suits: %d' % (self.joinableFsm.getCurrentState().getName(), self.maxSuits)) + return 0 + + def addToon(self, avId): + self.notify.debug('addToon(%d)' % avId) + toon = self.getToon(avId) + if toon == None: + return 0 + toon.stopToonUp() + event = simbase.air.getAvatarExitEvent(avId) + self.avatarExitEvents.append(event) + self.accept(event, self.__handleUnexpectedExit, extraArgs=[avId]) + event = 'inSafezone-%s' % avId + self.avatarExitEvents.append(event) + self.accept(event, self.__handleSuddenExit, extraArgs=[avId, 0]) + self.newToons.append(avId) + self.toons.append(avId) + toon = simbase.air.doId2do.get(avId) + if toon: + if hasattr(self, 'doId'): + toon.b_setBattleId(self.doId) + else: + toon.b_setBattleId(-1) + messageToonAdded = 'Battle adding toon %s' % avId + messenger.send(messageToonAdded, [avId]) + if self.fsm != None and self.fsm.getCurrentState().getName() == 'PlayMovie': + self.responses[avId] = 1 + else: + self.responses[avId] = 0 + self.adjustingResponses[avId] = 0 + if avId not in self.toonExp: + p = [] + for t in Tracks: + p.append(toon.experience.getExp(t)) + + self.toonExp[avId] = p + if avId not in self.toonOrigMerits: + self.toonOrigMerits[avId] = toon.cogMerits[:] + if avId not in self.toonMerits: + self.toonMerits[avId] = [0, + 0, + 0, + 0] + if avId not in self.toonOrigQuests: + flattenedQuests = [] + for quest in toon.quests: + flattenedQuests.extend(quest) + + self.toonOrigQuests[avId] = flattenedQuests + if avId not in self.toonItems: + self.toonItems[avId] = ([], []) + return 1 + + def __joinToon(self, avId, pos): + self.joiningToons.append(avId) + toPendingTime = MAX_JOIN_T + SERVER_BUFFER_TIME + taskName = self.taskName('to-pending-av-%d' % avId) + self.__addJoinResponse(avId, taskName, toon=1) + taskMgr.doMethodLater(toPendingTime, self.__serverJoinDone, taskName, extraArgs=(avId, taskName)) + self.taskNames.append(taskName) + + def __updateEncounteredCogs(self): + for toon in self.activeToons: + if toon in self.newToons: + for suit in self.activeSuits: + if hasattr(suit, 'dna'): + self.suitsEncountered.append({'type': suit.dna.name, + 'activeToons': self.activeToons[:]}) + else: + self.notify.warning('Suit has no DNA in zone %s: toons involved = %s' % (self.zoneId, self.activeToons)) + return + + self.newToons.remove(toon) + + for suit in self.activeSuits: + if suit in self.newSuits: + if hasattr(suit, 'dna'): + self.suitsEncountered.append({'type': suit.dna.name, + 'activeToons': self.activeToons[:]}) + else: + self.notify.warning('Suit has no DNA in zone %s: toons involved = %s' % (self.zoneId, self.activeToons)) + return + self.newSuits.remove(suit) + + def __makeToonRun(self, toonId, updateAttacks): + self.activeToons.remove(toonId) + self.toonGone = 1 + self.runningToons.append(toonId) + taskName = self.taskName('running-toon-%d' % toonId) + taskMgr.doMethodLater(TOON_RUN_T, self.__serverRunDone, taskName, extraArgs=(toonId, updateAttacks, taskName)) + self.taskNames.append(taskName) + + def __serverRunDone(self, toonId, updateAttacks, taskName): + self.notify.debug('run for toon: %d timed out on server' % toonId) + self.__removeTaskName(taskName) + self.__removeToon(toonId) + self.d_setMembers() + if len(self.toons) == 0: + self.notify.debug('last toon is gone - battle is finished') + self.b_setState('Resume') + else: + if updateAttacks == 1: + self.d_setChosenToonAttacks() + self.needAdjust = 1 + self.__requestAdjust() + return Task.done + + def __requestAdjust(self): + if not self.fsm: + return + cstate = self.fsm.getCurrentState().getName() + if cstate == 'WaitForInput' or cstate == 'WaitForJoin': + if self.adjustFsm.getCurrentState().getName() == 'NotAdjusting': + if self.needAdjust == 1: + self.d_adjust() + self.adjustingSuits = [] + for s in self.pendingSuits: + self.adjustingSuits.append(s) + + self.adjustingToons = [] + for t in self.pendingToons: + self.adjustingToons.append(t) + + self.adjustFsm.request('Adjusting') + else: + self.notify.debug('requestAdjust() - dont need to') + else: + self.notify.debug('requestAdjust() - already adjusting') + else: + self.notify.debug('requestAdjust() - in state: %s' % cstate) + + def __handleUnexpectedExit(self, avId): + userAborted = self.air.timeManager.getDisconnectReason(avId) == ToontownGlobals.DisconnectCloseWindow + self.__handleSuddenExit(avId, userAborted) + + def __handleSuddenExit(self, avId, userAborted): + self.__removeToon(avId, userAborted=userAborted) + if self.fsm.getCurrentState().getName() == 'PlayMovie' or self.fsm.getCurrentState().getName() == 'MakeMovie': + self.exitedToons.append(avId) + self.d_setMembers() + if len(self.toons) == 0: + self.notify.debug('last toon is gone - battle is finished') + self.__removeAllTasks() + self.timer.stop() + self.adjustingTimer.stop() + self.b_setState('Resume') + else: + self.needAdjust = 1 + self.__requestAdjust() + + def __removeSuit(self, suit): + self.notify.debug('__removeSuit(%d)' % suit.doId) + self.suits.remove(suit) + self.activeSuits.remove(suit) + if self.luredSuits.count(suit) == 1: + self.luredSuits.remove(suit) + self.suitGone = 1 + del suit.battleTrap + + def __removeToon(self, toonId, userAborted = 0): + self.notify.debug('__removeToon(%d)' % toonId) + if self.toons.count(toonId) == 0: + return + self.battleCalc.toonLeftBattle(toonId) + self.__removeToonTasks(toonId) + self.toons.remove(toonId) + if self.joiningToons.count(toonId) == 1: + self.joiningToons.remove(toonId) + if self.pendingToons.count(toonId) == 1: + self.pendingToons.remove(toonId) + if self.activeToons.count(toonId) == 1: + activeToonIdx = self.activeToons.index(toonId) + self.notify.debug('removing activeToons[%d], updating suitAttacks SUIT_HP_COL to match' % activeToonIdx) + for i in xrange(len(self.suitAttacks)): + if activeToonIdx < len(self.suitAttacks[i][SUIT_HP_COL]): + del self.suitAttacks[i][SUIT_HP_COL][activeToonIdx] + else: + self.notify.warning("suitAttacks %d doesn't have an HP column for active toon index %d" % (i, activeToonIdx)) + + self.activeToons.remove(toonId) + if self.runningToons.count(toonId) == 1: + self.runningToons.remove(toonId) + if self.adjustingToons.count(toonId) == 1: + self.notify.warning('removeToon() - toon: %d was adjusting!' % toonId) + self.adjustingToons.remove(toonId) + self.toonGone = 1 + if toonId in self.pets: + self.pets[toonId].requestDelete() + del self.pets[toonId] + self.__removeResponse(toonId) + self.__removeAdjustingResponse(toonId) + self.__removeJoinResponses(toonId) + event = simbase.air.getAvatarExitEvent(toonId) + self.avatarExitEvents.remove(event) + self.ignore(event) + event = 'inSafezone-%s' % toonId + self.avatarExitEvents.remove(event) + self.ignore(event) + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.b_setBattleId(0) + messageToonReleased = 'Battle releasing toon %s' % toon.doId + messenger.send(messageToonReleased, [toon.doId]) + if not userAborted: + toon = self.getToon(toonId) + if toon != None: + toon.hpOwnedByBattle = 0 + toon.d_setHp(toon.hp) + toon.d_setInventory(toon.inventory.makeNetString()) + self.air.cogPageManager.toonEncounteredCogs(toon, self.suitsEncountered, self.getTaskZoneId()) + elif len(self.suits) > 0 and not self.streetBattle: + empty = InventoryBase.InventoryBase(None).makeNetString() + self.air.dbInterface.updateObject(self.air.dbId, toonId, self.air.dclassesByName['DistributedToonAI'], {'setHp': [0], 'setInventory': [empty]}) + + def getToon(self, toonId): + if toonId in self.air.doId2do: + return self.air.doId2do[toonId] + self.notify.warning('getToon() - toon: %d not in repository!' % toonId) + return None + + def toonRequestRun(self): + toonId = self.air.getAvatarIdFromSender() + if self.ignoreResponses == 1: + self.notify.debug('ignoring response from toon: %d' % toonId) + return + self.notify.debug('toonRequestRun(%d)' % toonId) + if not self.isRunable(): + self.notify.warning('toonRequestRun() - not runable') + return + updateAttacks = 0 + if self.activeToons.count(toonId) == 0: + self.notify.warning('toon tried to run, but not found in activeToons: %d' % toonId) + return + for toon in self.activeToons: + if toon in self.toonAttacks: + ta = self.toonAttacks[toon] + track = ta[TOON_TRACK_COL] + level = ta[TOON_LVL_COL] + if ta[TOON_TGT_COL] == toonId or track == HEAL and attackAffectsGroup(track, level) and len(self.activeToons) <= 2: + healerId = ta[TOON_ID_COL] + self.notify.debug('resetting toon: %ds attack' % healerId) + self.toonAttacks[toon] = getToonAttack(toon, track=UN_ATTACK) + self.responses[healerId] = 0 + updateAttacks = 1 + + self.__makeToonRun(toonId, updateAttacks) + self.d_setMembers() + self.needAdjust = 1 + self.__requestAdjust() + + def toonRequestJoin(self, x, y, z): + toonId = self.air.getAvatarIdFromSender() + self.notify.debug('toonRequestJoin(%d)' % toonId) + self.signupToon(toonId, x, y, z) + + def toonDied(self): + toonId = self.air.getAvatarIdFromSender() + self.notify.debug('toonDied(%d)' % toonId) + if toonId in self.toons: + toon = self.getToon(toonId) + if toon: + toon.hp = -1 + toon.inventory.zeroInv(1) + self.__handleSuddenExit(toonId, 0) + + def signupToon(self, toonId, x, y, z): + if self.toons.count(toonId): + return + if self.toonCanJoin(): + if self.addToon(toonId): + self.__joinToon(toonId, Point3(x, y, z)) + self.d_setMembers() + else: + self.notify.warning('toonRequestJoin() - not joinable') + self.d_denyLocalToonJoin(toonId) + + def d_denyLocalToonJoin(self, toonId): + self.notify.debug('network: denyLocalToonJoin(%d)' % toonId) + self.sendUpdateToAvatarId(toonId, 'denyLocalToonJoin', []) + + def resetResponses(self): + self.responses = {} + for t in self.toons: + self.responses[t] = 0 + + self.ignoreResponses = 0 + + def allToonsResponded(self): + for t in self.toons: + if self.responses[t] == 0: + return 0 + + self.ignoreResponses = 1 + return 1 + + def __allPendingActiveToonsResponded(self): + for t in self.pendingToons + self.activeToons: + if self.responses[t] == 0: + return 0 + + self.ignoreResponses = 1 + return 1 + + def __allActiveToonsResponded(self): + for t in self.activeToons: + if self.responses[t] == 0: + return 0 + + self.ignoreResponses = 1 + return 1 + + def __removeResponse(self, toonId): + del self.responses[toonId] + if self.ignoreResponses == 0 and len(self.toons) > 0: + currStateName = self.fsm.getCurrentState().getName() + if currStateName == 'WaitForInput': + if self.__allActiveToonsResponded(): + self.notify.debug('removeResponse() - dont wait for movie') + self.__requestMovie() + elif currStateName == 'PlayMovie': + if self.__allPendingActiveToonsResponded(): + self.notify.debug('removeResponse() - surprise movie done') + self.__movieDone() + elif currStateName == 'Reward' or currStateName == 'BuildingReward': + if self.__allActiveToonsResponded(): + self.notify.debug('removeResponse() - surprise reward done') + self.handleRewardDone() + + def __resetAdjustingResponses(self): + self.adjustingResponses = {} + for t in self.toons: + self.adjustingResponses[t] = 0 + + self.ignoreAdjustingResponses = 0 + + def __allAdjustingToonsResponded(self): + for t in self.toons: + if self.adjustingResponses[t] == 0: + return 0 + + self.ignoreAdjustingResponses = 1 + return 1 + + def __removeAdjustingResponse(self, toonId): + if toonId in self.adjustingResponses: + del self.adjustingResponses[toonId] + if self.ignoreAdjustingResponses == 0 and len(self.toons) > 0: + if self.__allAdjustingToonsResponded(): + self.__adjustDone() + + def __addJoinResponse(self, avId, taskName, toon = 0): + if toon == 1: + for jr in self.joinResponses.values(): + jr[avId] = 0 + + self.joinResponses[avId] = {} + for t in self.toons: + self.joinResponses[avId][t] = 0 + + self.joinResponses[avId]['taskName'] = taskName + + def __removeJoinResponses(self, avId): + self.__removeJoinResponse(avId) + removedOne = 0 + for j in self.joinResponses.values(): + if avId in j: + del j[avId] + removedOne = 1 + + if removedOne == 1: + for t in self.joiningToons: + if self.__allToonsRespondedJoin(t): + self.__makeAvPending(t) + + def __removeJoinResponse(self, avId): + if avId in self.joinResponses: + taskMgr.remove(self.joinResponses[avId]['taskName']) + del self.joinResponses[avId] + + def __allToonsRespondedJoin(self, avId): + jr = self.joinResponses[avId] + for t in self.toons: + if jr[t] == 0: + return 0 + + return 1 + + def __cleanupJoinResponses(self): + for jr in self.joinResponses.values(): + taskMgr.remove(jr['taskName']) + del jr + + def adjustDone(self): + toonId = self.air.getAvatarIdFromSender() + if self.ignoreAdjustingResponses == 1: + self.notify.debug('adjustDone() - ignoring toon: %d' % toonId) + return + elif self.adjustFsm.getCurrentState().getName() != 'Adjusting': + self.notify.warning('adjustDone() - in state %s' % self.fsm.getCurrentState().getName()) + return + elif self.toons.count(toonId) == 0: + self.notify.warning('adjustDone() - toon: %d not in toon list' % toonId) + return + self.adjustingResponses[toonId] += 1 + self.notify.debug('toon: %d done adjusting' % toonId) + if self.__allAdjustingToonsResponded(): + self.__adjustDone() + + def timeout(self): + toonId = self.air.getAvatarIdFromSender() + if self.ignoreResponses == 1: + self.notify.debug('timeout() - ignoring toon: %d' % toonId) + return + elif self.fsm.getCurrentState().getName() != 'WaitForInput': + self.notify.warning('timeout() - in state: %s' % self.fsm.getCurrentState().getName()) + return + elif self.toons.count(toonId) == 0: + self.notify.warning('timeout() - toon: %d not in toon list' % toonId) + return + self.toonAttacks[toonId] = getToonAttack(toonId) + self.d_setChosenToonAttacks() + self.responses[toonId] += 1 + self.notify.debug('toon: %d timed out' % toonId) + if self.__allActiveToonsResponded(): + self.__requestMovie(timeout=1) + + def movieDone(self): + toonId = self.air.getAvatarIdFromSender() + if self.ignoreResponses == 1: + self.notify.debug('movieDone() - ignoring toon: %d' % toonId) + return + elif self.fsm.getCurrentState().getName() != 'PlayMovie': + self.notify.warning('movieDone() - in state %s' % self.fsm.getCurrentState().getName()) + return + elif self.toons.count(toonId) == 0: + self.notify.warning('movieDone() - toon: %d not in toon list' % toonId) + return + self.responses[toonId] += 1 + self.notify.debug('toon: %d done with movie' % toonId) + if self.__allPendingActiveToonsResponded(): + self.__movieDone() + else: + self.timer.stop() + self.timer.startCallback(TIMEOUT_PER_USER, self.__serverMovieDone) + + def rewardDone(self): + toonId = self.air.getAvatarIdFromSender() + stateName = self.fsm.getCurrentState().getName() + if self.ignoreResponses == 1: + self.notify.debug('rewardDone() - ignoring toon: %d' % toonId) + return + elif stateName not in ('Reward', 'BuildingReward', 'FactoryReward', 'MintReward', 'StageReward', 'CountryClubReward'): + self.notify.warning('rewardDone() - in state %s' % stateName) + return + elif self.toons.count(toonId) == 0: + self.notify.warning('rewardDone() - toon: %d not in toon list' % toonId) + return + self.responses[toonId] += 1 + self.notify.debug('toon: %d done with reward' % toonId) + if self.__allActiveToonsResponded(): + self.handleRewardDone() + else: + self.timer.stop() + self.timer.startCallback(TIMEOUT_PER_USER, self.serverRewardDone) + + def assignRewards(self): + if self.rewardHasPlayed == 1: + self.notify.debug('handleRewardDone() - reward has already played') + return + self.rewardHasPlayed = 1 + BattleExperienceAI.assignRewards(self.activeToons, self.battleCalc.toonSkillPtsGained, self.suitsKilled, self.getTaskZoneId(), self.helpfulToons) + + def joinDone(self, avId): + toonId = self.air.getAvatarIdFromSender() + if self.toons.count(toonId) == 0: + self.notify.warning('joinDone() - toon: %d not in toon list' % toonId) + return + if avId not in self.joinResponses: + self.notify.debug('joinDone() - no entry for: %d - ignoring: %d' % (avId, toonId)) + return + jr = self.joinResponses[avId] + if toonId in jr: + jr[toonId] += 1 + self.notify.debug('client with localToon: %d done joining av: %d' % (toonId, avId)) + if self.__allToonsRespondedJoin(avId): + self.__makeAvPending(avId) + + def requestAttack(self, track, level, av): + toonId = self.air.getAvatarIdFromSender() + if self.ignoreResponses == 1: + self.notify.debug('requestAttack() - ignoring toon: %d' % toonId) + return + elif self.fsm.getCurrentState().getName() != 'WaitForInput': + self.notify.warning('requestAttack() - in state: %s' % self.fsm.getCurrentState().getName()) + return + elif self.activeToons.count(toonId) == 0: + self.notify.warning('requestAttack() - toon: %d not in toon list' % toonId) + return + self.notify.debug('requestAttack(%d, %d, %d, %d)' % (toonId, + track, + level, + av)) + toon = self.getToon(toonId) + if toon == None: + self.notify.warning('requestAttack() - no toon: %d' % toonId) + return + validResponse = 1 + self.npcAttacks = {k:v for k, v in self.npcAttacks.iteritems() if v != toonId} + if track == SOS: + self.notify.debug('toon: %d calls for help' % toonId) + self.air.writeServerEvent('friendSOS', toonId, '%s' % av) + self.toonAttacks[toonId] = getToonAttack(toonId, track=SOS, target=av) + elif track == NPCSOS: + self.notify.debug('toon: %d calls for help' % toonId) + self.air.writeServerEvent('NPCSOS', toonId, '%s' % av) + toon = self.getToon(toonId) + if toon == None: + return + if av in toon.NPCFriendsDict: + npcCollision = 0 + if av in self.npcAttacks: + callingToon = self.npcAttacks[av] + if callingToon != toonId and self.activeToons.count(callingToon) == 1: + self.toonAttacks[toonId] = getToonAttack(toonId, track=PASS) + npcCollision = 1 + if npcCollision == 0: + self.toonAttacks[toonId] = getToonAttack(toonId, track=NPCSOS, level=5, target=av) + self.numNPCAttacks += 1 + self.npcAttacks[av] = toonId + elif track == PETSOS: + self.notify.debug('toon: %d calls for pet: %d' % (toonId, av)) + self.air.writeServerEvent('PETSOS', toonId, '%s' % av) + toon = self.getToon(toonId) + if toon == None: + return + if not self.validate(toonId, level in toon.petTrickPhrases, 'requestAttack: invalid pet trickId: %s' % level): + return + self.toonAttacks[toonId] = getToonAttack(toonId, track=PETSOS, level=level, target=av) + elif track == UN_ATTACK: + self.notify.debug('toon: %d changed its mind' % toonId) + self.toonAttacks[toonId] = getToonAttack(toonId, track=UN_ATTACK) + if toonId in self.responses: + self.responses[toonId] = 0 + validResponse = 0 + elif track == PASS: + self.toonAttacks[toonId] = getToonAttack(toonId, track=PASS) + elif track == FIRE: + self.toonAttacks[toonId] = getToonAttack(toonId, track=FIRE, target=av) + else: + if not self.validate(toonId, track >= 0 and track <= MAX_TRACK_INDEX, 'requestAttack: invalid track %s' % track): + return + if not self.validate(toonId, level >= 0 and level <= MAX_LEVEL_INDEX, 'requestAttack: invalid level %s' % level): + return + if toon.inventory.numItem(track, level) == 0: + self.notify.warning('requestAttack() - toon has no item track: %d level: %d' % (track, level)) + self.toonAttacks[toonId] = getToonAttack(toonId) + return + if track == HEAL: + if self.runningToons.count(av) == 1 or attackAffectsGroup(track, level) and len(self.activeToons) < 2: + self.toonAttacks[toonId] = getToonAttack(toonId, track=UN_ATTACK) + validResponse = 0 + else: + self.toonAttacks[toonId] = getToonAttack(toonId, track=track, level=level, target=av) + else: + self.toonAttacks[toonId] = getToonAttack(toonId, track=track, level=level, target=av) + if av == -1 and not attackAffectsGroup(track, level): + validResponse = 0 + self.d_setChosenToonAttacks() + if validResponse == 1: + self.responses[toonId] += 1 + self.notify.debug('toon: %d chose an attack' % toonId) + if self.__allActiveToonsResponded(): + self.__requestMovie() + return + + def requestPetProxy(self, av): + toonId = self.air.getAvatarIdFromSender() + if self.ignoreResponses == 1: + self.notify.debug('requestPetProxy() - ignoring toon: %d' % toonId) + return + elif self.fsm.getCurrentState().getName() != 'WaitForInput': + self.notify.warning('requestPetProxy() - in state: %s' % self.fsm.getCurrentState().getName()) + return + elif self.activeToons.count(toonId) == 0: + self.notify.warning('requestPetProxy() - toon: %d not in toon list' % toonId) + return + self.notify.debug('requestPetProxy(%s, %s)' % (toonId, av)) + toon = self.getToon(toonId) + if toon == None: + self.notify.warning('requestPetProxy() - no toon: %d' % toonId) + return + petId = toon.getPetId() + zoneId = self.zoneId + if petId == av: + if not toonId in self.pets: + def handleGetPetProxy(success, pet, petId = petId, zoneId = zoneId, toonId = toonId): + if success: + petProxy = DistributedPetProxyAI.DistributedPetProxyAI(self.air) + petProxy.setOwnerId(pet.getOwnerId()) + petProxy.setPetName(pet.getPetName()) + petProxy.setTraitSeed(pet.getTraitSeed()) + petProxy.setSafeZone(pet.getSafeZone()) + petProxy.setForgetfulness(pet.getForgetfulness()) + petProxy.setBoredomThreshold(pet.getBoredomThreshold()) + petProxy.setRestlessnessThreshold(pet.getRestlessnessThreshold()) + petProxy.setPlayfulnessThreshold(pet.getPlayfulnessThreshold()) + petProxy.setLonelinessThreshold(pet.getLonelinessThreshold()) + petProxy.setSadnessThreshold(pet.getSadnessThreshold()) + petProxy.setFatigueThreshold(pet.getFatigueThreshold()) + petProxy.setHungerThreshold(pet.getHungerThreshold()) + petProxy.setConfusionThreshold(pet.getConfusionThreshold()) + petProxy.setExcitementThreshold(pet.getExcitementThreshold()) + petProxy.setAngerThreshold(pet.getAngerThreshold()) + petProxy.setSurpriseThreshold(pet.getSurpriseThreshold()) + petProxy.setAffectionThreshold(pet.getAffectionThreshold()) + petProxy.setHead(pet.getHead()) + petProxy.setEars(pet.getEars()) + petProxy.setNose(pet.getNose()) + petProxy.setTail(pet.getTail()) + petProxy.setBodyTexture(pet.getBodyTexture()) + petProxy.setColor(pet.getColor()) + petProxy.setColorScale(pet.getColorScale()) + petProxy.setEyeColor(pet.getEyeColor()) + petProxy.setGender(pet.getGender()) + petProxy.setLastSeenTimestamp(pet.getLastSeenTimestamp()) + petProxy.setBoredom(pet.getBoredom()) + petProxy.setRestlessness(pet.getRestlessness()) + petProxy.setPlayfulness(pet.getPlayfulness()) + petProxy.setLoneliness(pet.getLoneliness()) + petProxy.setSadness(pet.getSadness()) + petProxy.setAffection(pet.getAffection()) + petProxy.setHunger(pet.getHunger()) + petProxy.setConfusion(pet.getConfusion()) + petProxy.setExcitement(pet.getExcitement()) + petProxy.setFatigue(pet.getFatigue()) + petProxy.setAnger(pet.getAnger()) + petProxy.setSurprise(pet.getSurprise()) + petProxy.setTrickAptitudes(pet.getTrickAptitudes()) + pet.requestDelete() + def deleted(task): + petProxy.doNotDeallocateChannel = True + petProxy.generateWithRequiredAndId(petId, self.air.districtId, self.zoneId) + petProxy.broadcastDominantMood() + self.pets[toonId] = petProxy + return task.done + + self.acceptOnce(self.air.getAvatarExitEvent(petId), + lambda: taskMgr.doMethodLater(0, + deleted, self.uniqueName('petdel-%d' % petId))) + else: + self.notify.warning('error generating petProxy: %s' % petId) + + self.getPetProxyObject(petId, handleGetPetProxy) + + def suitCanJoin(self): + return len(self.suits) < self.maxSuits and self.isJoinable() + + def toonCanJoin(self): + return len(self.toons) < 4 and self.isJoinable() + + def __requestMovie(self, timeout = 0): + if self.adjustFsm.getCurrentState().getName() == 'Adjusting': + self.notify.debug('__requestMovie() - in Adjusting') + self.movieRequested = 1 + else: + movieDelay = 0 + if len(self.activeToons) == 0: + self.notify.warning('only pending toons left in battle %s, toons = %s' % (self.doId, self.toons)) + elif len(self.activeSuits) == 0: + self.notify.warning('only pending suits left in battle %s, suits = %s' % (self.doId, self.suits)) + elif len(self.activeToons) > 1 and not timeout: + movieDelay = 1 + self.fsm.request('MakeMovie') + if movieDelay: + taskMgr.doMethodLater(0.8, self.__makeMovie, self.uniqueName('make-movie')) + self.taskNames.append(self.uniqueName('make-movie')) + else: + self.__makeMovie() + + def __makeMovie(self, task = None): + self.notify.debug('makeMovie()') + if self._DOAI_requestedDelete: + self.notify.warning('battle %s requested delete, then __makeMovie was called!' % self.doId) + if hasattr(self, 'levelDoId'): + self.notify.warning('battle %s in level %s' % (self.doId, self.levelDoId)) + return + self.__removeTaskName(self.uniqueName('make-movie')) + if self.movieHasBeenMade == 1: + self.notify.debug('__makeMovie() - movie has already been made') + return + self.movieRequested = 0 + self.movieHasBeenMade = 1 + self.movieHasPlayed = 0 + self.rewardHasPlayed = 0 + for t in self.activeToons: + if t not in self.toonAttacks: + self.toonAttacks[t] = getToonAttack(t) + attack = self.toonAttacks[t] + if attack[TOON_TRACK_COL] == PASS or attack[TOON_TRACK_COL] == UN_ATTACK: + self.toonAttacks[t] = getToonAttack(t) + if self.toonAttacks[t][TOON_TRACK_COL] != NO_ATTACK: + self.addHelpfulToon(t) + + self.battleCalc.calculateRound() + for t in self.activeToons: + self.sendEarnedExperience(t) + toon = self.getToon(t) + if toon != None: + toon.hpOwnedByBattle = 1 + if toon.immortalMode: + toon.toonUp(toon.maxHp) + + self.d_setMovie() + self.b_setState('PlayMovie') + return Task.done + + def sendEarnedExperience(self, toonId): + toon = self.getToon(toonId) + if toon != None: + expList = self.battleCalc.toonSkillPtsGained.get(toonId, None) + if expList == None: + toon.d_setEarnedExperience([]) + else: + roundList = [] + for exp in expList: + roundList.append(int(exp + 0.5)) + + toon.d_setEarnedExperience(roundList) + return + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterFaceOff(self): + return None + + def exitFaceOff(self): + return None + + def enterWaitForJoin(self): + self.notify.debug('enterWaitForJoin()') + if len(self.activeSuits) > 0: + self.b_setState('WaitForInput') + else: + self.notify.debug('enterWaitForJoin() - no active suits') + self.runableFsm.request('Runable') + self.resetResponses() + self.__requestAdjust() + return None + + def exitWaitForJoin(self): + return None + + def enterWaitForInput(self): + self.notify.debug('enterWaitForInput()') + self.joinableFsm.request('Joinable') + self.runableFsm.request('Runable') + self.resetResponses() + self.__requestAdjust() + if not self.tutorialFlag: + self.timer.startCallback(SERVER_INPUT_TIMEOUT, self.__serverTimedOut) + self.npcAttacks = {} + for toonId in self.toons: + if bboard.get('autoRestock-%s' % toonId, False): + toon = self.air.doId2do.get(toonId) + if toon is not None: + toon.doRestock(0) + + return + + def exitWaitForInput(self): + self.npcAttacks = {} + self.timer.stop() + return None + + def __serverTimedOut(self): + self.notify.debug('wait for input timed out on server') + self.ignoreResponses = 1 + self.__requestMovie(timeout=1) + + def enterMakeMovie(self): + self.notify.debug('enterMakeMovie()') + self.runableFsm.request('Unrunable') + self.resetResponses() + return None + + def exitMakeMovie(self): + return None + + def enterPlayMovie(self): + self.notify.debug('enterPlayMovie()') + self.joinableFsm.request('Joinable') + self.runableFsm.request('Unrunable') + self.resetResponses() + movieTime = TOON_ATTACK_TIME * (len(self.activeToons) + self.numNPCAttacks) + SUIT_ATTACK_TIME * len(self.activeSuits) + SERVER_BUFFER_TIME + self.numNPCAttacks = 0 + self.notify.debug('estimated upper bound of movie time: %f' % movieTime) + self.timer.startCallback(movieTime, self.__serverMovieDone) + + def __serverMovieDone(self): + self.notify.debug('movie timed out on server') + self.ignoreResponses = 1 + self.__movieDone() + + def serverRewardDone(self): + self.notify.debug('reward timed out on server') + self.ignoreResponses = 1 + self.handleRewardDone() + + def handleRewardDone(self): + self.b_setState('Resume') + + def exitPlayMovie(self): + self.timer.stop() + return None + + def __movieDone(self): + self.notify.debug('__movieDone() - movie is finished') + if self.movieHasPlayed == 1: + self.notify.debug('__movieDone() - movie had already finished') + return + self.movieHasBeenMade = 0 + self.movieHasPlayed = 1 + self.ignoreResponses = 1 + needUpdate = 0 + toonHpDict = {} + for toon in self.activeToons: + toonHpDict[toon] = [0, 0, 0] + actualToon = self.getToon(toon) + self.notify.debug('BEFORE ROUND: toon: %d hp: %d' % (toon, actualToon.hp)) + + deadSuits = [] + trapDict = {} + suitsLuredOntoTraps = [] + npcTrapAttacks = [] + for activeToon in self.activeToons + self.exitedToons: + if activeToon in self.toonAttacks: + attack = self.toonAttacks[activeToon] + track = attack[TOON_TRACK_COL] + npc_level = None + if track == NPCSOS: + track, npc_level, npc_hp = NPCToons.getNPCTrackLevelHp(attack[TOON_TGT_COL]) + if track == None: + track = NPCSOS + elif track == TRAP: + npcTrapAttacks.append(attack) + toon = self.getToon(attack[TOON_ID_COL]) + av = attack[TOON_TGT_COL] + if toon != None and av in toon.NPCFriendsDict: + toon.NPCFriendsDict[av] -= 1 + if toon.NPCFriendsDict[av] <= 0: + del toon.NPCFriendsDict[av] + toon.d_setNPCFriendsDict(toon.NPCFriendsDict) + continue + if track != NO_ATTACK: + toonId = attack[TOON_ID_COL] + level = attack[TOON_LVL_COL] + if npc_level != None: + level = npc_level + if attack[TOON_TRACK_COL] == NPCSOS: + toon = self.getToon(toonId) + av = attack[TOON_TGT_COL] + if toon != None and av in toon.NPCFriendsDict: + toon.NPCFriendsDict[av] -= 1 + if toon.NPCFriendsDict[av] <= 0: + del toon.NPCFriendsDict[av] + toon.d_setNPCFriendsDict(toon.NPCFriendsDict) + elif track == PETSOS: + pass + elif track == FIRE: + pass + elif track != SOS: + toon = self.getToon(toonId) + if toon != None and not toon.unlimitedGags: + check = toon.inventory.useItem(track, level) + if check == -1: + self.air.writeServerEvent('suspicious', toonId, 'Toon generating movie for non-existent gag track %s level %s' % (track, level)) + self.notify.warning('generating movie for non-existent gag track %s level %s! avId: %s' % (track, level, toonId)) + toon.addStat(ToontownGlobals.STAT_GAGS) + toon.d_setInventory(toon.inventory.makeNetString()) + hps = attack[TOON_HP_COL] + if track == SOS: + self.notify.debug('toon: %d called for help' % toonId) + elif track == NPCSOS: + self.notify.debug('toon: %d called for help' % toonId) + elif track == PETSOS: + self.notify.debug('toon: %d called for pet' % toonId) + for i in xrange(len(self.activeToons)): + toon = self.getToon(self.activeToons[i]) + if toon != None: + if i < len(hps): + hp = hps[i] + if hp > 0: + toonHpDict[toon.doId][0] += hp + self.notify.debug('pet heal: toon: %d healed for hp: %d' % (toon.doId, hp)) + else: + self.notify.warning('Invalid targetIndex %s in hps %s.' % (i, hps)) + + elif track == NPC_RESTOCK_GAGS: + for at in self.activeToons: + toon = self.getToon(at) + if toon != None: + toon.inventory.NPCMaxOutInv(npc_level) + toon.d_setInventory(toon.inventory.makeNetString()) + + elif track == HEAL: + if levelAffectsGroup(HEAL, level): + for i in xrange(len(self.activeToons)): + at = self.activeToons[i] + if at != toonId or attack[TOON_TRACK_COL] == NPCSOS: + toon = self.getToon(at) + if toon != None: + if i < len(hps): + hp = hps[i] + else: + self.notify.warning('Invalid targetIndex %s in hps %s.' % (i, hps)) + hp = 0 + toonHpDict[toon.doId][0] += hp + self.notify.debug('HEAL: toon: %d healed for hp: %d' % (toon.doId, hp)) + + else: + targetId = attack[TOON_TGT_COL] + toon = self.getToon(targetId) + if toon != None and targetId in self.activeToons: + targetIndex = self.activeToons.index(targetId) + if targetIndex < len(hps): + hp = hps[targetIndex] + else: + self.notify.warning('Invalid targetIndex %s in hps %s.' % (targetIndex, hps)) + hp = 0 + toonHpDict[toon.doId][0] += hp + elif attackAffectsGroup(track, level, attack[TOON_TRACK_COL]): + for suit in self.activeSuits: + targetIndex = self.activeSuits.index(suit) + if targetIndex < 0 or targetIndex >= len(hps): + self.notify.warning('Got attack (%s, %s) on target suit %s, but hps has only %s entries: %s' % (track, + level, + targetIndex, + len(hps), + hps)) + else: + hp = hps[targetIndex] + if hp > 0 and track == LURE: + if suit.battleTrap == UBER_GAG_LEVEL_INDEX: + pass + suit.battleTrap = NO_TRAP + needUpdate = 1 + if suit.doId in trapDict: + del trapDict[suit.doId] + if suitsLuredOntoTraps.count(suit) == 0: + suitsLuredOntoTraps.append(suit) + if track == TRAP: + targetId = suit.doId + if targetId in trapDict: + trapDict[targetId].append(attack) + else: + trapDict[targetId] = [attack] + needUpdate = 1 + died = attack[SUIT_DIED_COL] & 1 << targetIndex + if died != 0: + if deadSuits.count(suit) == 0: + deadSuits.append(suit) + + else: + targetId = attack[TOON_TGT_COL] + target = self.findSuit(targetId) + if target != None: + targetIndex = self.activeSuits.index(target) + if targetIndex < 0 or targetIndex >= len(hps): + self.notify.warning('Got attack (%s, %s) on target suit %s, but hps has only %s entries: %s' % (track, + level, + targetIndex, + len(hps), + hps)) + else: + hp = hps[targetIndex] + if track == TRAP: + if targetId in trapDict: + trapDict[targetId].append(attack) + else: + trapDict[targetId] = [attack] + if hp > 0 and track == LURE: + oldBattleTrap = target.battleTrap + if oldBattleTrap == UBER_GAG_LEVEL_INDEX: + pass + target.battleTrap = NO_TRAP + needUpdate = 1 + if target.doId in trapDict: + del trapDict[target.doId] + if suitsLuredOntoTraps.count(target) == 0: + suitsLuredOntoTraps.append(target) + if oldBattleTrap == UBER_GAG_LEVEL_INDEX: + for otherSuit in self.activeSuits: + if not otherSuit == target: + otherSuit.battleTrap = NO_TRAP + if otherSuit.doId in trapDict: + del trapDict[otherSuit.doId] + + died = attack[SUIT_DIED_COL] & 1 << targetIndex + if died != 0: + if deadSuits.count(target) == 0: + deadSuits.append(target) + + self.exitedToons = [] + for suitKey in trapDict.keys(): + attackList = trapDict[suitKey] + attack = attackList[0] + target = self.findSuit(attack[TOON_TGT_COL]) + if attack[TOON_LVL_COL] == UBER_GAG_LEVEL_INDEX: + targetId = suitKey + target = self.findSuit(targetId) + if len(attackList) == 1: + if suitsLuredOntoTraps.count(target) == 0: + self.notify.debug('movieDone() - trap set') + target.battleTrap = attack[TOON_LVL_COL] + needUpdate = 1 + else: + target.battleTrap = NO_TRAP + else: + self.notify.debug('movieDone() - traps collided') + if target != None: + target.battleTrap = NO_TRAP + + if self.battleCalc.trainTrapTriggered: + self.notify.debug('Train trap triggered, clearing all traps') + for otherSuit in self.activeSuits: + self.notify.debug('suit =%d, oldBattleTrap=%d' % (otherSuit.doId, otherSuit.battleTrap)) + otherSuit.battleTrap = NO_TRAP + + currLuredSuits = self.battleCalc.getLuredSuits() + if len(self.luredSuits) == len(currLuredSuits): + for suit in self.luredSuits: + if currLuredSuits.count(suit.doId) == 0: + needUpdate = 1 + break + + else: + needUpdate = 1 + self.luredSuits = [] + for i in currLuredSuits: + suit = self.air.doId2do[i] + self.luredSuits.append(suit) + self.notify.debug('movieDone() - suit: %d is lured' % i) + + for attack in npcTrapAttacks: + track, level, hp = NPCToons.getNPCTrackLevelHp(attack[TOON_TGT_COL]) + for suit in self.activeSuits: + if self.luredSuits.count(suit) == 0 and suit.battleTrap == NO_TRAP: + suit.battleTrap = level + + needUpdate = 1 + + for suit in deadSuits: + self.notify.debug('removing dead suit: %d' % suit.doId) + if suit.isDeleted(): + self.notify.debug('whoops, suit %d is deleted.' % suit.doId) + else: + self.notify.debug('suit had revives? %d' % suit.getMaxSkeleRevives()) + encounter = {'type': suit.dna.name, + 'level': suit.getActualLevel(), + 'track': suit.dna.dept, + 'isSkelecog': suit.getSkelecog(), + 'isForeman': suit.isForeman(), + 'isBoss': 0, + 'isSupervisor': suit.isSupervisor(), + 'isVirtual': suit.isVirtual(), + 'hasRevives': suit.getMaxSkeleRevives(), + 'activeToons': self.activeToons[:]} + self.suitsKilled.append(encounter) + self.suitsKilledThisBattle.append(encounter) + self.air.suitInvasionManager.handleSuitDefeated() + self.__removeSuit(suit) + needUpdate = 1 + suit.resume() + + lastActiveSuitDied = 0 + if len(self.activeSuits) == 0 and len(self.pendingSuits) == 0: + lastActiveSuitDied = 1 + for i in xrange(4): + attack = self.suitAttacks[i][SUIT_ATK_COL] + if attack != NO_ATTACK: + suitId = self.suitAttacks[i][SUIT_ID_COL] + suit = self.findSuit(suitId) + if suit == None: + self.notify.warning('movieDone() - suit: %d is gone!' % suitId) + continue + if not (hasattr(suit, 'dna') and suit.dna): + toonId = self.air.getAvatarIdFromSender() + self.notify.warning('_movieDone avoiding crash, sender=%s but suit has no dna' % toonId) + self.air.writeServerEvent('suspicious', toonId, '_movieDone avoiding crash, suit has no dna') + continue + adict = getSuitAttack(suit.getStyleName(), suit.getLevel(), attack) + hps = self.suitAttacks[i][SUIT_HP_COL] + if adict['group'] == ATK_TGT_GROUP: + for activeToon in self.activeToons: + toon = self.getToon(activeToon) + if toon != None: + targetIndex = self.activeToons.index(activeToon) + toonDied = self.suitAttacks[i][TOON_DIED_COL] & 1 << targetIndex + if targetIndex >= len(hps): + self.notify.warning('DAMAGE: toon %s is no longer in battle!' % activeToon) + else: + hp = hps[targetIndex] + if hp > 0: + self.notify.debug('DAMAGE: toon: %d hit for dmg: %d' % (activeToon, hp)) + if toonDied != 0: + toonHpDict[toon.doId][2] = 1 + toonHpDict[toon.doId][1] += hp + + elif adict['group'] == ATK_TGT_SINGLE: + targetIndex = self.suitAttacks[i][SUIT_TGT_COL] + if targetIndex >= len(self.activeToons): + self.notify.warning('movieDone() - toon: %d gone!' % targetIndex) + break + toonId = self.activeToons[targetIndex] + toon = self.getToon(toonId) + toonDied = self.suitAttacks[i][TOON_DIED_COL] & 1 << targetIndex + if targetIndex >= len(hps): + self.notify.warning('DAMAGE: toon %s is no longer in battle!' % toonId) + else: + hp = hps[targetIndex] + if hp > 0: + self.notify.debug('DAMAGE: toon: %d hit for dmg: %d' % (toonId, hp)) + if toonDied != 0: + toonHpDict[toon.doId][2] = 1 + toonHpDict[toon.doId][1] += hp + + deadToons = [] + for activeToon in self.activeToons: + hp = toonHpDict[activeToon] + toon = self.getToon(activeToon) + if toon != None: + self.notify.debug('AFTER ROUND: currtoonHP: %d toonMAX: %d hheal: %d damage: %d' % (toon.hp, + toon.maxHp, + hp[0], + hp[1])) + toon.hpOwnedByBattle = 0 + hpDelta = hp[0] - hp[1] + if hpDelta >= 0: + toon.toonUp(hpDelta, quietly=1) + else: + toon.takeDamage(-hpDelta, quietly=1) + if toon.hp <= 0: + self.notify.debug('movieDone() - toon: %d was killed' % activeToon) + toon.inventory.zeroInv(1) + deadToons.append(activeToon) + self.notify.debug('AFTER ROUND: toon: %d setHp: %d' % (toon.doId, toon.hp)) + + for deadToon in deadToons: + self.__removeToon(deadToon) + needUpdate = 1 + + self.clearAttacks() + self.d_setMovie() + self.d_setChosenToonAttacks() + self.localMovieDone(needUpdate, deadToons, deadSuits, lastActiveSuitDied) + return + + def enterResume(self): + for suit in self.suits: + self.notify.info('battle done, resuming suit: %d' % suit.doId) + if suit.isDeleted(): + self.notify.info('whoops, suit %d is deleted.' % suit.doId) + else: + suit.resume() + + self.suits = [] + self.joiningSuits = [] + self.pendingSuits = [] + self.adjustingSuits = [] + self.activeSuits = [] + self.luredSuits = [] + for toonId in self.toons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.b_setBattleId(0) + messageToonReleased = 'Battle releasing toon %s' % toon.doId + messenger.send(messageToonReleased, [toon.doId]) + + for exitEvent in self.avatarExitEvents: + self.ignore(exitEvent) + + eventMsg = {} + for encounter in self.suitsKilledThisBattle: + cog = encounter['type'] + level = encounter['level'] + msgName = '%s%s' % (cog, level) + if encounter['isSkelecog']: + msgName += '+' + if msgName in eventMsg: + eventMsg[msgName] += 1 + else: + eventMsg[msgName] = 1 + + msgText = '' + for msgName, count in eventMsg.items(): + if msgText != '': + msgText += ',' + msgText += '%s%s' % (count, msgName) + + self.air.writeServerEvent('battleCogsDefeated', self.doId, '%s|%s' % (msgText, self.getTaskZoneId())) + + def exitResume(self): + pass + + def isJoinable(self): + return self.joinableFsm.getCurrentState().getName() == 'Joinable' + + def enterJoinable(self): + self.notify.debug('enterJoinable()') + return None + + def exitJoinable(self): + return None + + def enterUnjoinable(self): + self.notify.debug('enterUnjoinable()') + return None + + def exitUnjoinable(self): + return None + + def isRunable(self): + return self.runableFsm.getCurrentState().getName() == 'Runable' + + def enterRunable(self): + self.notify.debug('enterRunable()') + return None + + def exitRunable(self): + return None + + def enterUnrunable(self): + self.notify.debug('enterUnrunable()') + return None + + def exitUnrunable(self): + return None + + def __estimateAdjustTime(self): + self.needAdjust = 0 + adjustTime = 0 + if len(self.pendingSuits) > 0 or self.suitGone == 1: + self.suitGone = 0 + pos0 = self.suitPendingPoints[0][0] + pos1 = self.suitPoints[0][0][0] + adjustTime = self.calcSuitMoveTime(pos0, pos1) + if len(self.pendingToons) > 0 or self.toonGone == 1: + self.toonGone = 0 + if adjustTime == 0: + pos0 = self.toonPendingPoints[0][0] + pos1 = self.toonPoints[0][0][0] + adjustTime = self.calcToonMoveTime(pos0, pos1) + return adjustTime + + def enterAdjusting(self): + self.notify.debug('enterAdjusting()') + self.timer.stop() + self.__resetAdjustingResponses() + self.adjustingTimer.startCallback(self.__estimateAdjustTime() + SERVER_BUFFER_TIME, self.__serverAdjustingDone) + return None + + def __serverAdjustingDone(self): + if self.needAdjust == 1: + self.adjustFsm.request('NotAdjusting') + self.__requestAdjust() + else: + self.notify.debug('adjusting timed out on the server') + self.ignoreAdjustingResponses = 1 + self.__adjustDone() + + def exitAdjusting(self): + currStateName = self.fsm.getCurrentState().getName() + if currStateName == 'WaitForInput': + self.timer.restart() + elif currStateName == 'WaitForJoin': + self.b_setState('WaitForInput') + self.adjustingTimer.stop() + return None + + def __addTrainTrapForNewSuits(self): + hasTrainTrap = False + trapInfo = None + for otherSuit in self.activeSuits: + if otherSuit.battleTrap == UBER_GAG_LEVEL_INDEX: + hasTrainTrap = True + + if hasTrainTrap: + for curSuit in self.activeSuits: + if not curSuit.battleTrap == UBER_GAG_LEVEL_INDEX: + oldBattleTrap = curSuit.battleTrap + curSuit.battleTrap = UBER_GAG_LEVEL_INDEX + self.battleCalc.addTrainTrapForJoiningSuit(curSuit.doId) + self.notify.debug('setting traintrack trap for joining suit %d oldTrap=%s' % (curSuit.doId, oldBattleTrap)) + + return + + def __adjustDone(self): + for s in self.adjustingSuits: + self.pendingSuits.remove(s) + self.activeSuits.append(s) + + self.adjustingSuits = [] + for toon in self.adjustingToons: + if self.pendingToons.count(toon) == 1: + self.pendingToons.remove(toon) + else: + self.notify.warning('adjustDone() - toon: %d not pending!' % toon.doId) + if self.activeToons.count(toon) == 0: + self.activeToons.append(toon) + self.ignoreResponses = 0 + self.sendEarnedExperience(toon) + else: + self.notify.warning('adjustDone() - toon: %d already active!' % toon.doId) + + self.adjustingToons = [] + self.__addTrainTrapForNewSuits() + self.d_setMembers() + self.adjustFsm.request('NotAdjusting') + if self.needAdjust == 1: + self.notify.debug('__adjustDone() - need to adjust again') + self.__requestAdjust() + + def enterNotAdjusting(self): + self.notify.debug('enterNotAdjusting()') + if self.movieRequested == 1: + if len(self.activeToons) > 0 and self.__allActiveToonsResponded(): + self.__requestMovie() + return None + + def exitNotAdjusting(self): + return None + + def getPetProxyObject(self, petId, callback): + doneEvent = 'generate-%d' % petId + + def handlePetProxyRead(pet): + callback(1, pet) + + self.air.sendActivate(petId, self.air.districtId, 0) + self.acceptOnce(doneEvent, handlePetProxyRead) + + def _getNextSerialNum(self): + num = self.serialNum + self.serialNum += 1 + return num + + +@magicWord(category=CATEGORY_MODERATOR) +def skipMovie(): + invoker = spellbook.getInvoker() + battleId = invoker.getBattleId() + if not battleId: + return 'You are not currently in a battle!' + battle = simbase.air.doId2do.get(battleId) + battle._DistributedBattleBaseAI__movieDone() + return 'Battle movie skipped.' diff --git a/toontown/battle/DistributedBattleBldg.py b/toontown/battle/DistributedBattleBldg.py new file mode 100755 index 00000000..3d00623c --- /dev/null +++ b/toontown/battle/DistributedBattleBldg.py @@ -0,0 +1,217 @@ +from direct.actor import Actor +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import State +from direct.interval.IntervalGlobal import * +from panda3d.core import * +import random + +from BattleBase import * +import DistributedBattleBase +import MovieUtil +import SuitBattleGlobals +from otp.avatar import Emote +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals +from toontown.suit import Suit +from toontown.suit import SuitDNA +from toontown.toon import TTEmote +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals + + +class DistributedBattleBldg(DistributedBattleBase.DistributedBattleBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleBldg') + camFOFov = 30.0 + camFOPos = Point3(0, -10, 4) + + def __init__(self, cr): + townBattle = cr.playGame.getPlace().townBattle + DistributedBattleBase.DistributedBattleBase.__init__(self, cr, townBattle) + self.streetBattle = 0 + self.fsm.addState(State.State('BuildingReward', self.enterBuildingReward, self.exitBuildingReward, ['Resume'])) + offState = self.fsm.getStateNamed('Off') + offState.addTransition('BuildingReward') + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('BuildingReward') + + def generate(self): + DistributedBattleBase.DistributedBattleBase.generate(self) + + def setBossBattle(self, value): + self.bossBattle = value + if self.bossBattle: + self.battleMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.ogg') + else: + self.battleMusic = base.loadMusic('phase_7/audio/bgm/encntr_general_bg_indoor.ogg') + base.playMusic(self.battleMusic, looping=1, volume=0.9) + + def getBossBattleTaunt(self): + return TTLocalizer.BattleBldgBossTaunt + + def disable(self): + DistributedBattleBase.DistributedBattleBase.disable(self) + self.battleMusic.stop() + + def delete(self): + DistributedBattleBase.DistributedBattleBase.delete(self) + del self.battleMusic + + def buildJoinPointList(self, avPos, destPos, toon = 0): + return [] + + def __faceOff(self, ts, name, callback): + if len(self.suits) == 0: + self.notify.warning('__faceOff(): no suits.') + return + if len(self.toons) == 0: + self.notify.warning('__faceOff(): no toons.') + return + elevatorPos = self.toons[0].getPos() + if len(self.suits) == 1: + leaderIndex = 0 + elif self.bossBattle == 1: + leaderIndex = 1 + else: + maxTypeNum = -1 + for suit in self.suits: + suitTypeNum = SuitDNA.getSuitType(suit.dna.name) + if maxTypeNum < suitTypeNum: + maxTypeNum = suitTypeNum + leaderIndex = self.suits.index(suit) + + delay = FACEOFF_TAUNT_T + suitTrack = Parallel() + suitLeader = None + for suit in self.suits: + suit.setState('Battle') + suitIsLeader = 0 + oneSuitTrack = Sequence() + oneSuitTrack.append(Func(suit.loop, 'neutral')) + oneSuitTrack.append(Func(suit.headsUp, elevatorPos)) + if self.suits.index(suit) == leaderIndex: + suitLeader = suit + suitIsLeader = 1 + if self.bossBattle == 1: + taunt = self.getBossBattleTaunt() + else: + taunt = SuitBattleGlobals.getFaceoffTaunt(suit.getStyleName(), suit.doId) + oneSuitTrack.append(Func(suit.setChatAbsolute, taunt, CFSpeech | CFTimeout)) + destPos, destHpr = self.getActorPosHpr(suit, self.suits) + oneSuitTrack.append(Wait(delay)) + if suitIsLeader == 1: + oneSuitTrack.append(Func(suit.clearChat)) + oneSuitTrack.append(self.createAdjustInterval(suit, destPos, destHpr)) + suitTrack.append(oneSuitTrack) + + toonTrack = Parallel() + for toon in self.toons: + oneToonTrack = Sequence() + destPos, destHpr = self.getActorPosHpr(toon, self.toons) + oneToonTrack.append(Wait(delay)) + oneToonTrack.append(self.createAdjustInterval(toon, destPos, destHpr, toon=1, run=1)) + toonTrack.append(oneToonTrack) + + camTrack = Sequence() + + def setCamFov(fov): + base.camLens.setMinFov(fov/(4./3.)) + + camTrack.append(Func(camera.wrtReparentTo, suitLeader)) + camTrack.append(Func(setCamFov, self.camFOFov)) + suitHeight = suitLeader.getHeight() + suitOffsetPnt = Point3(0, 0, suitHeight) + MidTauntCamHeight = suitHeight * 0.66 + MidTauntCamHeightLim = suitHeight - 1.8 + if MidTauntCamHeight < MidTauntCamHeightLim: + MidTauntCamHeight = MidTauntCamHeightLim + TauntCamY = 18 + TauntCamX = 0 + TauntCamHeight = random.choice((MidTauntCamHeight, 1, 11)) + camTrack.append(Func(camera.setPos, TauntCamX, TauntCamY, TauntCamHeight)) + camTrack.append(Func(camera.lookAt, suitLeader, suitOffsetPnt)) + camTrack.append(Wait(delay)) + camPos = Point3(0, -6, 4) + camHpr = Vec3(0, 0, 0) + camTrack.append(Func(camera.reparentTo, base.localAvatar)) + camTrack.append(Func(setCamFov, settings['fov'])) + camTrack.append(Func(camera.setPosHpr, camPos, camHpr)) + mtrack = Parallel(suitTrack, toonTrack, camTrack) + done = Func(callback) + track = Sequence(mtrack, done, name=name) + track.start(ts) + self.storeInterval(track, name) + return + + def enterFaceOff(self, ts): + if len(self.toons) > 0 and base.localAvatar == self.toons[0]: + Emote.globalEmote.disableAll(self.toons[0], 'dbattlebldg, enterFaceOff') + self.delayDeleteMembers() + self.__faceOff(ts, self.faceOffName, self.__handleFaceOffDone) + return None + + def __handleFaceOffDone(self): + self.notify.debug('FaceOff done') + self.d_faceOffDone(base.localAvatar.doId) + + def exitFaceOff(self): + self.notify.debug('exitFaceOff()') + if len(self.toons) > 0 and base.localAvatar == self.toons[0]: + Emote.globalEmote.releaseAll(self.toons[0], 'dbattlebldg exitFaceOff') + self.clearInterval(self.faceOffName) + self._removeMembersKeep() + camera.wrtReparentTo(self) + base.camLens.setMinFov(self.camFov/(4./3.)) + return None + + def __playReward(self, ts, callback): + toonTracks = Parallel() + for toon in self.toons: + toonTracks.append(Sequence(Func(toon.loop, 'victory'), Wait(FLOOR_REWARD_TIMEOUT), Func(toon.loop, 'neutral'))) + + name = self.uniqueName('floorReward') + track = Sequence(toonTracks, Func(callback), name=name) + camera.setPos(0, 0, 1) + camera.setHpr(180, 10, 0) + self.storeInterval(track, name) + track.start(ts) + + def enterReward(self, ts): + self.notify.debug('enterReward()') + self.delayDeleteMembers() + self.__playReward(ts, self.__handleFloorRewardDone) + return None + + def __handleFloorRewardDone(self): + return None + + def exitReward(self): + self.notify.debug('exitReward()') + self.clearInterval(self.uniqueName('floorReward')) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) + for toon in self.toons: + toon.startSmooth() + + def enterBuildingReward(self, ts): + self.delayDeleteMembers() + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + self.movie.playReward(ts, self.uniqueName('building-reward'), self.__handleBuildingRewardDone, noSkip=True) + + def __handleBuildingRewardDone(self): + if self.hasLocalToon(): + self.d_rewardDone(base.localAvatar.doId) + self.movie.resetReward() + self.fsm.request('Resume') + + def exitBuildingReward(self): + self.movie.resetReward(finish=1) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) + + def enterResume(self, ts=0): + if self.hasLocalToon(): + self.removeLocalToon() + + def exitResume(self): + return None diff --git a/toontown/battle/DistributedBattleBldgAI.py b/toontown/battle/DistributedBattleBldgAI.py new file mode 100755 index 00000000..d98879f0 --- /dev/null +++ b/toontown/battle/DistributedBattleBldgAI.py @@ -0,0 +1,172 @@ +from BattleBase import * +from BattleCalculatorAI import * +import DistributedBattleBaseAI +from SuitBattleGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.showbase import PythonUtil +from direct.showbase.PythonUtil import addListsByValue +from direct.task import Task +from otp.ai.AIBase import * +from toontown.toonbase.ToontownBattleGlobals import * + + +class DistributedBattleBldgAI(DistributedBattleBaseAI.DistributedBattleBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleBldgAI') + + def __init__(self, air, zoneId, roundCallback=None, finishCallback=None, maxSuits=4, bossBattle=0): + DistributedBattleBaseAI.DistributedBattleBaseAI.__init__(self, air, zoneId, finishCallback, maxSuits, bossBattle) + self.streetBattle = 0 + self.roundCallback = roundCallback + self.fsm.addState(State.State('BuildingReward', self.enterBuildingReward, self.exitBuildingReward, ['Resume'])) + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('BuildingReward') + self.elevatorPos = Point3(0, -30, 0) + self.resumeNeedUpdate = 0 + + def announceGenerate(self): + DistributedBattleBaseAI.DistributedBattleBaseAI.announceGenerate(self) + self.registerToons() + + def setInitialMembers(self, toonIds, suits): + for suit in suits: + self.addSuit(suit) + for toonId in toonIds: + self.addToon(toonId) + self.fsm.request('FaceOff') + + def registerToons(self): + for toonId in self.toons: + toon = simbase.air.doId2do.get(toonId) + toon.b_setBattleId(self.doId) + + def delete(self): + del self.roundCallback + DistributedBattleBaseAI.DistributedBattleBaseAI.delete(self) + + def faceOffDone(self): + toonId = self.air.getAvatarIdFromSender() + if self.ignoreResponses == 1: + self.notify.debug('faceOffDone() - ignoring toon: %d' % toonId) + return + elif self.fsm.getCurrentState().getName() != 'FaceOff': + self.notify.warning('faceOffDone() - in state: %s' % self.fsm.getCurrentState().getName()) + return + elif self.toons.count(toonId) == 0: + self.notify.warning('faceOffDone() - toon: %d not in toon list' % toonId) + return + self.responses[toonId] += 1 + self.notify.debug('toon: %d done facing off' % toonId) + if not self.ignoreFaceOffDone: + if self.allToonsResponded(): + self.handleFaceOffDone() + else: + self.timer.stop() + self.timer.startCallback(TIMEOUT_PER_USER, self.__serverFaceOffDone) + + def enterFaceOff(self): + self.notify.debug('enterFaceOff()') + self.joinableFsm.request('Joinable') + self.runableFsm.request('Unrunable') + self.timer.startCallback(self.calcToonMoveTime(self.pos, self.elevatorPos) + FACEOFF_TAUNT_T + SERVER_BUFFER_TIME, self.__serverFaceOffDone) + + def __serverFaceOffDone(self): + self.notify.debug('faceoff timed out on server') + self.ignoreFaceOffDone = 1 + self.handleFaceOffDone() + + def exitFaceOff(self): + self.timer.stop() + self.resetResponses() + + def handleFaceOffDone(self): + for suit in self.suits: + self.activeSuits.append(suit) + for toon in self.toons: + self.activeToons.append(toon) + self.sendEarnedExperience(toon) + self.d_setMembers() + self.b_setState('WaitForInput') + + def localMovieDone(self, needUpdate, deadToons, deadSuits, lastActiveSuitDied): + self.timer.stop() + self.resumeNeedUpdate = needUpdate + self.resumeDeadToons = deadToons + self.resumeDeadSuits = deadSuits + self.resumeLastActiveSuitDied = lastActiveSuitDied + if len(self.toons) == 0: + self.d_setMembers() + self.b_setState('Resume') + else: + totalHp = 0 + for suit in self.suits: + if suit.currHP > 0: + totalHp += suit.currHP + continue + self.roundCallback(self.activeToons, totalHp, deadSuits) + + def __goToResumeState(self, task): + self.b_setState('Resume') + + def resume(self, currentFloor=0, topFloor=0): + if len(self.suits) == 0: + self.d_setMembers() + self.suitsKilledPerFloor.append(self.suitsKilledThisBattle) + if topFloor == 0: + self.b_setState('Reward') + else: + for floorNum, cogsThisFloor in enumerate(self.suitsKilledPerFloor): + for toonId in self.activeToons: + toon = self.getToon(toonId) + if toon: + (recovered, notRecovered) = self.air.questManager.recoverItems(toon, cogsThisFloor, self.zoneId) + self.toonItems[toonId][0].extend(recovered) + self.toonItems[toonId][1].extend(notRecovered) + meritArray = self.air.promotionMgr.recoverMerits(toon, cogsThisFloor, self.zoneId, getCreditMultiplier(floorNum)) + if toonId in self.helpfulToons: + self.toonMerits[toonId] = addListsByValue(self.toonMerits[toonId], meritArray) + else: + self.notify.debug('toon %d not helpful, skipping merits' % toonId) + self.d_setBattleExperience() + self.b_setState('BuildingReward') + elif self.resumeNeedUpdate == 1: + self.d_setMembers() + if len(self.resumeDeadSuits) > 0 or self.resumeLastActiveSuitDied == 0 or len(self.resumeDeadToons) > 0: + self.needAdjust = 1 + self.setState('WaitForJoin') + self.resumeNeedUpdate = 0 + self.resumeDeadToons = [] + self.resumeDeadSuits = [] + self.resumeLastActiveSuitDied = 0 + + def enterReservesJoining(self, ts=0): + return None + + def exitReservesJoining(self, ts=0): + return None + + def enterReward(self): + self.timer.startCallback(FLOOR_REWARD_TIMEOUT, self.serverRewardDone) + + def exitReward(self): + self.timer.stop() + + def enterBuildingReward(self): + self.resetResponses() + self.assignRewards() + self.timer.startCallback(BUILDING_REWARD_TIMEOUT, self.serverRewardDone) + + def exitBuildingReward(self): + self.exitResume() + pass + + def enterResume(self): + DistributedBattleBaseAI.DistributedBattleBaseAI.enterResume(self) + self.finishCallback(self.zoneId, self.activeToons) + + def exitResume(self): + DistributedBattleBaseAI.DistributedBattleBaseAI.exitResume(self) + taskName = self.taskName('finish') + taskMgr.remove(taskName) diff --git a/toontown/battle/DistributedBattleDiners.py b/toontown/battle/DistributedBattleDiners.py new file mode 100755 index 00000000..a16efd05 --- /dev/null +++ b/toontown/battle/DistributedBattleDiners.py @@ -0,0 +1,123 @@ +import random +from pandac.PandaModules import VBase3, Point3 +from direct.interval.IntervalGlobal import Sequence, Wait, Func, Parallel, Track, LerpPosInterval, ProjectileInterval, SoundInterval, ActorInterval +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import DistributedBattleFinal +from toontown.suit import SuitTimings +from toontown.toonbase import ToontownGlobals +from toontown.battle import BattleProps + +class DistributedBattleDiners(DistributedBattleFinal.DistributedBattleFinal): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleDiners') + + def __init__(self, cr): + DistributedBattleFinal.DistributedBattleFinal.__init__(self, cr) + self.initialReservesJoiningDone = False + base.dbw = self + + def announceGenerate(self): + DistributedBattleFinal.DistributedBattleFinal.announceGenerate(self) + self.moveSuitsToInitialPos() + + def showSuitsJoining(self, suits, ts, name, callback): + if len(suits) == 0 and not self.initialReservesJoiningDone: + self.initialReservesJoiningDone = True + self.doInitialSuitsJoining(ts, name, callback) + return + self.showSuitsFalling(suits, ts, name, callback) + + def doInitialSuitsJoining(self, ts, name, callback): + done = Func(callback) + if self.hasLocalToon(): + camera.reparentTo(self) + if random.choice([0, 1]): + camera.setPosHpr(20, -4, 7, 60, 0, 0) + else: + camera.setPosHpr(-20, -4, 7, -60, 0, 0) + track = Sequence(Wait(0.5), done, name=name) + track.start(ts) + self.storeInterval(track, name) + + def moveSuitsToInitialPos(self): + battlePts = self.suitPoints[len(self.suitPendingPoints) - 1] + for i in xrange(len(self.suits)): + suit = self.suits[i] + suit.reparentTo(self) + destPos, destHpr = self.getActorPosHpr(suit, self.suits) + suit.setPos(destPos) + suit.setHpr(destHpr) + + def showSuitsFalling(self, suits, ts, name, callback): + if self.bossCog == None: + return + suitTrack = Parallel() + delay = 0 + for suit in suits: + suit.setState('Battle') + if suit.dna.dept == 'l': + suit.reparentTo(self.bossCog) + suit.setPos(0, 0, 0) + if suit in self.joiningSuits: + i = len(self.pendingSuits) + self.joiningSuits.index(suit) + destPos, h = self.suitPendingPoints[i] + destHpr = VBase3(h, 0, 0) + else: + destPos, destHpr = self.getActorPosHpr(suit, self.suits) + startPos = destPos + Point3(0, 0, SuitTimings.fromSky * ToontownGlobals.SuitWalkSpeed) + self.notify.debug('startPos for %s = %s' % (suit, startPos)) + suit.reparentTo(self) + suit.setPos(startPos) + suit.headsUp(self) + moveIval = Sequence() + chairInfo = self.bossCog.claimOneChair() + if chairInfo: + moveIval = self.createDinerMoveIval(suit, destPos, chairInfo) + suitTrack.append(Track((delay, Sequence(moveIval, Func(suit.loop, 'neutral'))))) + delay += 1 + + if self.hasLocalToon(): + camera.reparentTo(self) + self.notify.debug('self.battleSide =%s' % self.battleSide) + camHeading = -20 + camX = -4 + if self.battleSide == 0: + camHeading = 20 + camX = 4 + camera.setPosHpr(camX, -15, 7, camHeading, 0, 0) + done = Func(callback) + track = Sequence(suitTrack, done, name=name) + track.start(ts) + self.storeInterval(track, name) + return + + def createDinerMoveIval(self, suit, destPos, chairInfo): + dur = suit.getDuration('landing') + fr = suit.getFrameRate('landing') + landingDur = dur + totalDur = 7.3 + animTimeInAir = totalDur - dur + flyingDur = animTimeInAir + impactLength = dur - animTimeInAir + tableIndex = chairInfo[0] + chairIndex = chairInfo[1] + table = self.bossCog.tables[tableIndex] + chairLocator = table.chairLocators[chairIndex] + chairPos = chairLocator.getPos(self) + chairHpr = chairLocator.getHpr(self) + suit.setPos(chairPos) + table.setDinerStatus(chairIndex, table.HIDDEN) + suit.setHpr(chairHpr) + wayPoint = (chairPos + destPos) / 2.0 + wayPoint.setZ(wayPoint.getZ() + 20) + moveIval = Sequence(Func(suit.headsUp, self), Func(suit.pose, 'landing', 0), ProjectileInterval(suit, duration=flyingDur, startPos=chairPos, endPos=destPos, gravityMult=0.25), ActorInterval(suit, 'landing')) + if suit.prop == None: + suit.prop = BattleProps.globalPropPool.getProp('propeller') + propDur = suit.prop.getDuration('propeller') + lastSpinFrame = 8 + fr = suit.prop.getFrameRate('propeller') + spinTime = lastSpinFrame / fr + openTime = (lastSpinFrame + 1) / fr + suit.attachPropeller() + propTrack = Parallel(SoundInterval(suit.propInSound, duration=flyingDur, node=suit), Sequence(ActorInterval(suit.prop, 'propeller', constrainedLoop=1, duration=flyingDur + 1, startTime=0.0, endTime=spinTime), ActorInterval(suit.prop, 'propeller', duration=landingDur, startTime=openTime), Func(suit.detachPropeller))) + result = Parallel(moveIval, propTrack) + return result diff --git a/toontown/battle/DistributedBattleDinersAI.py b/toontown/battle/DistributedBattleDinersAI.py new file mode 100755 index 00000000..5b689d4c --- /dev/null +++ b/toontown/battle/DistributedBattleDinersAI.py @@ -0,0 +1,22 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import DistributedBattleFinalAI + +class DistributedBattleDinersAI(DistributedBattleFinalAI.DistributedBattleFinalAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleDinersAI') + + def __init__(self, air, bossCog, roundCallback, finishCallback, battleSide): + DistributedBattleFinalAI.DistributedBattleFinalAI.__init__(self, air, bossCog, roundCallback, finishCallback, battleSide) + + def startBattle(self, toonIds, suits): + self.joinableFsm.request('Joinable') + for toonId in toonIds: + if self.addToon(toonId): + self.activeToons.append(toonId) + + self.d_setMembers() + for suit in suits: + self.pendingSuits.append(suit) + + self.d_setMembers() + self.needAdjust = 1 + self.b_setState('ReservesJoining') diff --git a/toontown/battle/DistributedBattleFinal.py b/toontown/battle/DistributedBattleFinal.py new file mode 100755 index 00000000..a682c529 --- /dev/null +++ b/toontown/battle/DistributedBattleFinal.py @@ -0,0 +1,205 @@ +from direct.actor import Actor +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import State +from direct.interval.IntervalGlobal import * +from panda3d.core import * +import random + +from BattleBase import * +import DistributedBattleBase +import MovieUtil +import SuitBattleGlobals +from toontown.distributed import DelayDelete +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals +from toontown.suit import Suit +from toontown.toonbase import ToontownBattleGlobals, ToontownGlobals, TTLocalizer + +class DistributedBattleFinal(DistributedBattleBase.DistributedBattleBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleFinal') + + def __init__(self, cr): + townBattle = cr.playGame.hood.loader.townBattle + DistributedBattleBase.DistributedBattleBase.__init__(self, cr, townBattle) + self.setupCollisions(self.uniqueBattleName('battle-collide')) + self.bossCog = None + self.bossCogRequest = None + self.streetBattle = 0 + self.joiningSuitsName = self.uniqueBattleName('joiningSuits') + self.fsm.addState(State.State('ReservesJoining', self.enterReservesJoining, self.exitReservesJoining, ['WaitForJoin'])) + offState = self.fsm.getStateNamed('Off') + offState.addTransition('ReservesJoining') + waitForJoinState = self.fsm.getStateNamed('WaitForJoin') + waitForJoinState.addTransition('ReservesJoining') + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('ReservesJoining') + return + + def generate(self): + DistributedBattleBase.DistributedBattleBase.generate(self) + + def disable(self): + DistributedBattleBase.DistributedBattleBase.disable(self) + base.cr.relatedObjectMgr.abortRequest(self.bossCogRequest) + self.bossCogRequest = None + self.bossCog = None + return + + def delete(self): + DistributedBattleBase.DistributedBattleBase.delete(self) + self.removeCollisionData() + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + if bossCogId in base.cr.doId2do: + tempBossCog = base.cr.doId2do[bossCogId] + self.__gotBossCog([tempBossCog]) + else: + self.notify.debug('doing relatedObjectMgr.request for bossCog') + self.bossCogRequest = base.cr.relatedObjectMgr.requestObjects([bossCogId], allCallback=self.__gotBossCog) + + def __gotBossCog(self, bossCogList): + self.bossCogRequest = None + self.bossCog = bossCogList[0] + currStateName = self.localToonFsm.getCurrentState().getName() + if currStateName == 'NoLocalToon' and self.bossCog.hasLocalToon(): + self.enableCollision() + return + + def setBattleNumber(self, battleNumber): + self.battleNumber = battleNumber + + def setBattleSide(self, battleSide): + self.battleSide = battleSide + + def setMembers(self, suits, suitsJoining, suitsPending, suitsActive, suitsLured, suitTraps, toons, toonsJoining, toonsPending, toonsActive, toonsRunning, timestamp): + if self.battleCleanedUp(): + return + oldtoons = DistributedBattleBase.DistributedBattleBase.setMembers(self, suits, suitsJoining, suitsPending, suitsActive, suitsLured, suitTraps, toons, toonsJoining, toonsPending, toonsActive, toonsRunning, timestamp) + if len(self.toons) == 4 and len(oldtoons) < 4: + self.notify.debug('setMembers() - battle is now full of toons') + self.closeBattleCollision() + elif len(self.toons) < 4 and len(oldtoons) == 4: + self.openBattleCollision() + + def makeSuitJoin(self, suit, ts): + self.notify.debug('makeSuitJoin(%d)' % suit.doId) + self.joiningSuits.append(suit) + if self.hasLocalToon(): + self.d_joinDone(base.localAvatar.doId, suit.doId) + + def showSuitsJoining(self, suits, ts, name, callback): + if self.bossCog == None: + return + + bossDept = self.bossCog.dna.dept + + if bossDept in TTLocalizer.SendCogBossTaunts: + self.bossCog.setChatAbsolute(random.choice(TTLocalizer.SendCogBossTaunts[bossDept]), CFSpeech | CFTimeout) + + if self.battleSide: + openDoor = Func(self.bossCog.doorB.request, 'open') + closeDoor = Func(self.bossCog.doorB.request, 'close') + else: + openDoor = Func(self.bossCog.doorA.request, 'open') + closeDoor = Func(self.bossCog.doorA.request, 'close') + suitTrack = Parallel() + delay = 0 + for suit in suits: + suit.setState('Battle') + if suit.dna.dept == 'l': + suit.reparentTo(self.bossCog) + suit.setPos(0, 0, 0) + suit.setPos(self.bossCog, 0, 0, 0) + suit.headsUp(self) + suit.setScale(3.8 / suit.height) + if suit in self.joiningSuits: + i = len(self.pendingSuits) + self.joiningSuits.index(suit) + destPos, h = self.suitPendingPoints[i] + destHpr = VBase3(h, 0, 0) + else: + destPos, destHpr = self.getActorPosHpr(suit, self.suits) + suitTrack.append(Track((delay, self.createAdjustInterval(suit, destPos, destHpr)), (delay + 1.5, suit.scaleInterval(1.5, 1)))) + delay += 1 + + if self.hasLocalToon() and hasattr(base, 'camera') and base.camera is not None: + base.camera.reparentTo(self) + if random.choice([0, 1]): + base.camera.setPosHpr(20, -4, 7, 60, 0, 0) + else: + base.camera.setPosHpr(-20, -4, 7, -60, 0, 0) + done = Func(callback) + track = Sequence(openDoor, suitTrack, closeDoor, done, name=name) + track.start(ts) + self.storeInterval(track, name) + return + + def __playReward(self, ts, callback): + toonTracks = Parallel() + for toon in self.toons: + toonTracks.append(Sequence(Func(toon.loop, 'victory'), Wait(FLOOR_REWARD_TIMEOUT), Func(toon.loop, 'neutral'))) + + name = self.uniqueName('floorReward') + track = Sequence(toonTracks, name=name) + if self.hasLocalToon(): + base.camera.setPos(0, 0, 1) + base.camera.setHpr(180, 10, 0) + track += [self.bossCog.makeEndOfBattleMovie(self.hasLocalToon()), Func(callback)] + self.storeInterval(track, name) + track.start(ts) + + def enterReward(self, ts): + self.notify.debug('enterReward()') + self.disableCollision() + self.delayDeleteMembers() + self.__playReward(ts, self.__handleFloorRewardDone) + return None + + def __handleFloorRewardDone(self): + return None + + def exitReward(self): + self.notify.debug('exitReward()') + self.clearInterval(self.uniqueName('floorReward'), finish=1) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) + for toon in self.toons: + toon.startSmooth() + + def enterResume(self, ts = 0): + if self.hasLocalToon(): + self.removeLocalToon() + self.fsm.requestFinalState() + + def exitResume(self): + return None + + def enterReservesJoining(self, ts = 0): + self.delayDeleteMembers() + self.showSuitsJoining(self.joiningSuits, ts, self.joiningSuitsName, self.__reservesJoiningDone) + + def __reservesJoiningDone(self): + self._removeMembersKeep() + self.doneBarrier() + + def exitReservesJoining(self): + self.clearInterval(self.joiningSuitsName) + + def enterNoLocalToon(self): + self.notify.debug('enterNoLocalToon()') + if self.bossCog != None and self.bossCog.hasLocalToon(): + self.enableCollision() + else: + self.disableCollision() + return + + def exitNoLocalToon(self): + self.disableCollision() + return None + + def enterWaitForServer(self): + self.notify.debug('enterWaitForServer()') + return None + + def exitWaitForServer(self): + return None diff --git a/toontown/battle/DistributedBattleFinalAI.py b/toontown/battle/DistributedBattleFinalAI.py new file mode 100755 index 00000000..a5b72c0e --- /dev/null +++ b/toontown/battle/DistributedBattleFinalAI.py @@ -0,0 +1,125 @@ +from otp.ai.AIBase import * +from BattleBase import * +from BattleCalculatorAI import * +from toontown.toonbase.ToontownBattleGlobals import * +from SuitBattleGlobals import * +import DistributedBattleBaseAI +from direct.task import Task +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import State +from direct.showbase.PythonUtil import addListsByValue +import random +import types + +class DistributedBattleFinalAI(DistributedBattleBaseAI.DistributedBattleBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleFinalAI') + + def __init__(self, air, bossCog, roundCallback, finishCallback, battleSide): + DistributedBattleBaseAI.DistributedBattleBaseAI.__init__(self, air, bossCog.zoneId, finishCallback) + self.bossCogId = bossCog.doId + self.battleNumber = bossCog.battleNumber + self.battleSide = battleSide + self.streetBattle = 0 + self.roundCallback = roundCallback + self.elevatorPos = Point3(0, 0, 0) + self.pos = Point3(0, 30, 0) + self.resumeNeedUpdate = 0 + self.fsm.addState(State.State('ReservesJoining', self.enterReservesJoining, self.exitReservesJoining, ['WaitForJoin'])) + offState = self.fsm.getStateNamed('Off') + offState.addTransition('ReservesJoining') + waitForJoinState = self.fsm.getStateNamed('WaitForJoin') + waitForJoinState.addTransition('ReservesJoining') + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('ReservesJoining') + + def getBossCogId(self): + return self.bossCogId + + def getBattleNumber(self): + return self.battleNumber + + def getBattleSide(self): + return self.battleSide + + def startBattle(self, toonIds, suits): + self.joinableFsm.request('Joinable') + for toonId in toonIds: + if self.addToon(toonId): + self.activeToons.append(toonId) + + self.d_setMembers() + for suit in suits: + joined = self.suitRequestJoin(suit) + + self.d_setMembers() + self.b_setState('ReservesJoining') + + def localMovieDone(self, needUpdate, deadToons, deadSuits, lastActiveSuitDied): + self.timer.stop() + self.resumeNeedUpdate = needUpdate + self.resumeDeadToons = deadToons + self.resumeDeadSuits = deadSuits + self.resumeLastActiveSuitDied = lastActiveSuitDied + if len(self.toons) == 0: + self.d_setMembers() + self.b_setState('Resume') + else: + totalHp = 0 + for suit in self.suits: + if suit.currHP > 0: + totalHp += suit.currHP + + self.roundCallback(self.activeToons, totalHp, deadSuits) + + def resume(self, joinedReserves): + if len(joinedReserves) != 0: + for info in joinedReserves: + joined = self.suitRequestJoin(info[0]) + + self.d_setMembers() + self.b_setState('ReservesJoining') + elif len(self.suits) == 0: + battleMultiplier = getBossBattleCreditMultiplier(self.battleNumber) + for toonId in self.activeToons: + toon = self.getToon(toonId) + if toon and hasattr(self.air, 'questManager'): + recovered, notRecovered = self.air.questManager.recoverItems(toon, self.suitsKilledThisBattle, self.zoneId) + self.toonItems[toonId][0].extend(recovered) + self.toonItems[toonId][1].extend(notRecovered) + + self.d_setMembers() + self.d_setBattleExperience() + self.b_setState('Reward') + else: + if self.resumeNeedUpdate == 1: + self.d_setMembers() + if len(self.resumeDeadSuits) > 0 and self.resumeLastActiveSuitDied == 0 or len(self.resumeDeadToons) > 0: + self.needAdjust = 1 + self.setState('WaitForJoin') + self.resumeNeedUpdate = 0 + self.resumeDeadToons = [] + self.resumeDeadSuits = [] + self.resumeLastActiveSuitDied = 0 + + def enterReservesJoining(self, ts = 0): + self.beginBarrier('ReservesJoining', self.toons, 15, self.__doneReservesJoining) + + def __doneReservesJoining(self, avIds): + self.b_setState('WaitForJoin') + + def exitReservesJoining(self, ts = 0): + return None + + def enterReward(self): + self.timer.startCallback(FLOOR_REWARD_TIMEOUT + 5, self.serverRewardDone) + return None + + def exitReward(self): + self.timer.stop() + return None + + def enterResume(self): + self.joinableFsm.request('Unjoinable') + self.runableFsm.request('Unrunable') + DistributedBattleBaseAI.DistributedBattleBaseAI.enterResume(self) + self.finishCallback(self.zoneId, self.activeToons) diff --git a/toontown/battle/DistributedBattleWaiters.py b/toontown/battle/DistributedBattleWaiters.py new file mode 100755 index 00000000..400675e0 --- /dev/null +++ b/toontown/battle/DistributedBattleWaiters.py @@ -0,0 +1,94 @@ +import random +from pandac.PandaModules import VBase3, Point3 +from direct.interval.IntervalGlobal import Sequence, Wait, Func, Parallel, Track +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import DistributedBattleFinal +from toontown.suit import SuitTimings +from toontown.toonbase import ToontownGlobals + +class DistributedBattleWaiters(DistributedBattleFinal.DistributedBattleFinal): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleWaiters') + + def __init__(self, cr): + DistributedBattleFinal.DistributedBattleFinal.__init__(self, cr) + self.initialReservesJoiningDone = False + base.dbw = self + + def announceGenerate(self): + DistributedBattleFinal.DistributedBattleFinal.announceGenerate(self) + for suit in self.suits: + suit.makeWaiter() + + self.moveSuitsToInitialPos() + + def showSuitsJoining(self, suits, ts, name, callback): + if len(suits) == 0 and not self.initialReservesJoiningDone: + self.initialReservesJoiningDone = True + self.doInitialSuitsJoining(ts, name, callback) + return + self.showSuitsFalling(suits, ts, name, callback) + + def doInitialSuitsJoining(self, ts, name, callback): + done = Func(callback) + if self.hasLocalToon(): + self.notify.debug('parenting camera to distributed battle waiters') + camera.reparentTo(self) + if random.choice([0, 1]): + camera.setPosHpr(20, -4, 7, 60, 0, 0) + else: + camera.setPosHpr(-20, -4, 7, -60, 0, 0) + track = Sequence(Wait(0.5), done, name=name) + track.start(ts) + self.storeInterval(track, name) + + def moveSuitsToInitialPos(self): + battlePts = self.suitPoints[len(self.suitPendingPoints) - 1] + for i in xrange(len(self.suits)): + suit = self.suits[i] + suit.reparentTo(self) + destPos, destHpr = self.getActorPosHpr(suit, self.suits) + suit.setPos(destPos) + suit.setHpr(destHpr) + + def showSuitsFalling(self, suits, ts, name, callback): + if self.bossCog == None: + return + suitTrack = Parallel() + delay = 0 + for suit in suits: + suit.makeWaiter() + suit.setState('Battle') + if suit.dna.dept == 'l': + suit.reparentTo(self.bossCog) + suit.setPos(0, 0, 0) + if suit in self.joiningSuits: + i = len(self.pendingSuits) + self.joiningSuits.index(suit) + destPos, h = self.suitPendingPoints[i] + destHpr = VBase3(h, 0, 0) + else: + destPos, destHpr = self.getActorPosHpr(suit, self.suits) + startPos = destPos + Point3(0, 0, SuitTimings.fromSky * ToontownGlobals.SuitWalkSpeed) + self.notify.debug('startPos for %s = %s' % (suit, startPos)) + suit.reparentTo(self) + suit.setPos(startPos) + suit.headsUp(self) + flyIval = suit.beginSupaFlyMove(destPos, True, 'flyIn') + suitTrack.append(Track((delay, Sequence(flyIval, Func(suit.loop, 'neutral'))))) + delay += 1 + + if self.hasLocalToon(): + camera.reparentTo(self) + if random.choice([0, 1]): + camera.setPosHpr(20, -4, 7, 60, 0, 0) + else: + camera.setPosHpr(-20, -4, 7, -60, 0, 0) + done = Func(callback) + track = Sequence(suitTrack, done, name=name) + track.start(ts) + self.storeInterval(track, name) + return + + def enterWaitForInput(self, ts = 0): + DistributedBattleFinal.DistributedBattleFinal.enterWaitForInput(self, ts) + if self.hasLocalToon(): + camera.reparentTo(self) diff --git a/toontown/battle/DistributedBattleWaitersAI.py b/toontown/battle/DistributedBattleWaitersAI.py new file mode 100755 index 00000000..66f312e9 --- /dev/null +++ b/toontown/battle/DistributedBattleWaitersAI.py @@ -0,0 +1,22 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import DistributedBattleFinalAI + +class DistributedBattleWaitersAI(DistributedBattleFinalAI.DistributedBattleFinalAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleWaitersAI') + + def __init__(self, air, bossCog, roundCallback, finishCallback, battleSide): + DistributedBattleFinalAI.DistributedBattleFinalAI.__init__(self, air, bossCog, roundCallback, finishCallback, battleSide) + + def startBattle(self, toonIds, suits): + self.joinableFsm.request('Joinable') + for toonId in toonIds: + if self.addToon(toonId): + self.activeToons.append(toonId) + + self.d_setMembers() + for suit in suits: + self.pendingSuits.append(suit) + + self.d_setMembers() + self.needAdjust = 1 + self.b_setState('ReservesJoining') diff --git a/toontown/battle/Fanfare.py b/toontown/battle/Fanfare.py new file mode 100755 index 00000000..0f63870a --- /dev/null +++ b/toontown/battle/Fanfare.py @@ -0,0 +1,155 @@ +from direct.interval.IntervalGlobal import * +from BattleBase import * +from BattleProps import * +from BattleSounds import * +from toontown.toon.ToonDNA import * +from toontown.suit.SuitDNA import * +from direct.particles.ParticleEffect import * +from direct.gui.DirectGui import * +from panda3d.core import * +import MovieUtil +import MovieCamera +from direct.directnotify import DirectNotifyGlobal +import BattleParticles +from toontown.toonbase import ToontownGlobals +import RewardPanel +notify = DirectNotifyGlobal.directNotify.newCategory('Fanfare') + +def makePanel(toon, showToonName): + panel = DirectFrame(relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.75, 1, 0.75), pos=(0, 0, 0.587)) + panel.initialiseoptions(RewardPanel) + panel.setTransparency(1) + panel.hide() + if showToonName is 1: + panel.avNameLabel = DirectLabel(parent=panel, relief=None, pos=Vec3(0, 0, 0.3), text=toon.getName(), text_scale=0.08) + return panel + + +def makeMessageBox(panel, message, messagePos, messageScale, wordwrap = 100): + panel.itemFrame = DirectFrame(parent=panel, relief=None, text=message, text_pos=messagePos, text_scale=messageScale, text_wordwrap=wordwrap) + return + + +def makeImageBox(frame, image, imagePos, imageScale): + frame.imageIcon = image.copyTo(frame) + frame.imageIcon.setPos(imagePos) + frame.imageIcon.setScale(imageScale) + + +def makeFanfare(delay, toon): + return doFanfare(delay, toon, None) + + +def makeFanfareWithMessage(delay, toon, showToonName, message, messagePos, messageScale, wordwrap = 100): + panel = makePanel(toon, showToonName) + makeMessageBox(panel, message, messagePos, messageScale, wordwrap) + return doFanfare(delay, toon, panel) + + +def makeFanfareWithImage(delay, toon, showToonName, image, imagePos, imageScale, wordwrap = 100): + panel = makePanel(toon, showToonName) + makeMessageBox(panel, '', Vec3(0, 0, 0), 1, wordwrap) + makeImageBox(panel.itemFrame, image, imagePos, imageScale) + return doFanfare(delay, toon, panel) + + +def makeFanfareWithMessageImage(delay, toon, showToonName, message, messagePos, messageScale, image, imagePos, imageScale, wordwrap = 100): + panel = makePanel(toon, showToonName) + makeMessageBox(panel, message, messagePos, messageScale, wordwrap) + makeImageBox(panel.itemFrame, image, imagePos, imageScale) + return doFanfare(delay, toon, panel) + + +def doFanfare(delay, toon, panel): + fanfareNode = toon.attachNewNode('fanfareNode') + partyBall = fanfareNode.attachNewNode('partyBall') + headparts = toon.getHeadParts() + pos = headparts[2].getPos(fanfareNode) + partyBallLeft = globalPropPool.getProp('partyBall') + partyBallLeft.reparentTo(partyBall) + partyBallLeft.setScale(0.8) + partyBallLeft.setH(90) + partyBallLeft.setColorScale(1, 0, 0, 0) + partyBallRight = globalPropPool.getProp('partyBall') + partyBallRight.reparentTo(partyBall) + partyBallRight.setScale(0.8) + partyBallRight.setH(-90) + partyBallRight.setColorScale(1, 1, 0, 0) + partyBall.setZ(pos.getZ() + 3.2) + ballShake1 = Sequence(Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, 0, 0), hpr=Vec3(90, 10, 0), blendType='easeInOut'), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, 0, 0), hpr=Vec3(-90, -10, 0), blendType='easeInOut')), Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, 10, 0), hpr=Vec3(90, -10, 0), blendType='easeInOut'), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, -10, 0), hpr=Vec3(-90, 10, 0), blendType='easeInOut')), Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, -10, 0), hpr=Vec3(90, 0, 0), blendType='easeInOut'), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, 10, 0), hpr=Vec3(-90, 0, 0), blendType='easeInOut'))) + ballShake2 = Sequence(Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, 0, 0), hpr=Vec3(90, -10, 0), blendType='easeInOut'), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, 0, 0), hpr=Vec3(-90, 10, 0), blendType='easeInOut')), Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, -10, 0), hpr=Vec3(90, 10, 0), blendType='easeInOut'), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, 10, 0), hpr=Vec3(-90, -10, 0), blendType='easeInOut')), Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, 10, 0), hpr=Vec3(90, 0, 0), blendType='easeInOut'), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, -10, 0), hpr=Vec3(-90, 0, 0), blendType='easeInOut'))) + openBall = Parallel(LerpHprInterval(partyBallLeft, duration=0.2, startHpr=Vec3(90, 0, 0), hpr=Vec3(90, 30, 0)), LerpHprInterval(partyBallRight, duration=0.2, startHpr=Vec3(-90, 0, 0), hpr=Vec3(-90, 30, 0))) + confettiNode = fanfareNode.attachNewNode('confetti') + confettiNode.setScale(3) + confettiNode.setZ(pos.getZ() + 2.5) + + def longshake(models, num, duration): + inShake = getScaleBlendIntervals(models, duration=duration, startScale=0.23, endScale=0.2, blendType='easeInOut') + outShake = getScaleBlendIntervals(models, duration=duration, startScale=0.2, endScale=0.23, blendType='easeInOut') + i = 1 + seq = Sequence() + while i < num: + if i % 2 == 0: + seq.append(inShake) + else: + seq.append(outShake) + i += 1 + + return seq + + def getScaleBlendIntervals(props, duration, startScale, endScale, blendType): + tracks = Parallel() + for prop in props: + tracks.append(LerpScaleInterval(prop, duration, endScale, startScale=startScale, blendType=blendType)) + + return tracks + + trumpetNode = fanfareNode.attachNewNode('trumpetNode') + trumpet1 = globalPropPool.getProp('bugle') + trumpet2 = MovieUtil.copyProp(trumpet1) + trumpet1.reparentTo(trumpetNode) + trumpet1.setScale(0.2) + trumpet1.setPos(2, 2, 1) + trumpet1.setHpr(120, 65, 0) + trumpet2.reparentTo(trumpetNode) + trumpet2.setScale(0.2) + trumpet2.setPos(-2, 2, 1) + trumpet2.setHpr(-120, 65, 0) + trumpetNode.setTransparency(1) + trumpetNode.setColor(1, 1, 1, 0) + trumpturn1 = LerpHprInterval(trumpet1, duration=4, startHpr=Vec3(80, 15, 0), hpr=Vec3(150, 40, 0)) + trumpturn2 = LerpHprInterval(trumpet2, duration=4, startHpr=Vec3(-80, 15, 0), hpr=Vec3(-150, 40, 0)) + trumpetTurn = Parallel(trumpturn1, trumpturn2) + BattleParticles.loadParticles() + confettiBlue = BattleParticles.createParticleEffect('Confetti') + confettiBlue.reparentTo(confettiNode) + blue_p0 = confettiBlue.getParticlesNamed('particles-1') + blue_p0.renderer.getColorInterpolationManager().addConstant(0.0, 1.0, Vec4(0.0, 0.0, 1.0, 1.0), 1) + confettiYellow = BattleParticles.createParticleEffect('Confetti') + confettiYellow.reparentTo(confettiNode) + yellow_p0 = confettiYellow.getParticlesNamed('particles-1') + yellow_p0.renderer.getColorInterpolationManager().addConstant(0.0, 1.0, Vec4(1.0, 1.0, 0.0, 1.0), 1) + confettiRed = BattleParticles.createParticleEffect('Confetti') + confettiRed.reparentTo(confettiNode) + red_p0 = confettiRed.getParticlesNamed('particles-1') + red_p0.renderer.getColorInterpolationManager().addConstant(0.0, 1.0, Vec4(1.0, 0.0, 0.0, 1.0), 1) + trumpetsAppear = LerpColorInterval(trumpetNode, 0.3, startColor=Vec4(1, 1, 0, 0), color=Vec4(1, 1, 0, 1)) + trumpetsVanish = LerpColorInterval(trumpetNode, 0.3, startColor=Vec4(1, 1, 0, 1), color=Vec4(1, 1, 0, 0)) + crabHorn = globalBattleSoundCache.getSound('King_Crab.ogg') + drumroll = globalBattleSoundCache.getSound('SZ_MM_drumroll.ogg') + fanfare = globalBattleSoundCache.getSound('SZ_MM_fanfare.ogg') + crabHorn.setTime(1.5) + partyBall.setTransparency(1) + partyBall.setColorScale(1, 1, 1, 1) + ballAppear = Parallel(LerpColorScaleInterval(partyBallLeft, 0.3, startColorScale=Vec4(1, 0, 0, 0), colorScale=Vec4(1, 0, 0, 1)), LerpColorScaleInterval(partyBallRight, 0.3, startColorScale=Vec4(1, 1, 0, 0), colorScale=Vec4(1, 1, 0, 1))) + ballVanish = Parallel(LerpColorScaleInterval(partyBallLeft, 0.3, startColorScale=Vec4(1, 0, 0, 1), colorScale=Vec4(1, 0, 0, 0)), LerpColorScaleInterval(partyBallRight, 0.3, startColorScale=Vec4(1, 1, 0, 1), colorScale=Vec4(1, 1, 0, 0))) + play = Parallel(SoundInterval(crabHorn, startTime=1.5, duration=4.0, node=toon), Sequence(Wait(0.25), longshake([trumpet1, trumpet2], 3, 0.2), Wait(0.5), longshake([trumpet1, trumpet2], 3, 0.2), Wait(0.5), longshake([trumpet1, trumpet2], 9, 0.1), longshake([trumpet1, trumpet2], 3, 0.2))) + killParticles = Parallel(Func(blue_p0.setLitterSize, 0), Func(red_p0.setLitterSize, 0), Func(yellow_p0.setLitterSize, 0)) + p = Parallel(ParticleInterval(confettiBlue, confettiNode, worldRelative=0, duration=3, cleanup=True), ParticleInterval(confettiRed, confettiNode, worldRelative=0, duration=3, cleanup=True), ParticleInterval(confettiYellow, confettiNode, worldRelative=0, duration=3, cleanup=True)) + pOff = Parallel(Func(confettiBlue.remove), Func(confettiRed.remove), Func(confettiYellow.remove)) + partInterval = Parallel(p, Sequence(Wait(1.7), killParticles, Wait(1.3), pOff, Func(p.finish)), Sequence(Wait(3), Parallel(ballVanish))) + seq1 = Parallel(Sequence(Wait(delay + 4.1), SoundInterval(drumroll, node=toon), Wait(0.25), SoundInterval(fanfare, node=toon)), Sequence(Wait(delay), trumpetsAppear, Wait(3), ballAppear, Wait(0.5), ballShake1, Wait(0.1), ballShake2, Wait(0.2), Wait(0.1), Parallel(openBall, partInterval), Func(fanfareNode.remove))) + seq = Parallel(seq1, Sequence(Wait(delay), Parallel(trumpetTurn, Sequence(Wait(0.5), play)), Wait(0.5), trumpetsVanish)) + if panel != None: + return (seq, panel) + return (seq, None) diff --git a/toontown/battle/FireCogPanel.py b/toontown/battle/FireCogPanel.py new file mode 100755 index 00000000..45a94da7 --- /dev/null +++ b/toontown/battle/FireCogPanel.py @@ -0,0 +1,127 @@ +from toontown.toonbase.ToontownBattleGlobals import * +from toontown.toonbase import ToontownGlobals +from direct.fsm import StateData +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattleBase +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer + +class FireCogPanel(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('ChooseAvatarPanel') + + def __init__(self, doneEvent): + self.notify.debug('Init choose panel...') + StateData.StateData.__init__(self, doneEvent) + self.numAvatars = 0 + self.chosenAvatar = 0 + self.toon = 0 + self.loaded = 0 + + def load(self): + gui = loader.loadModel('phase_3.5/models/gui/battle_gui') + self.frame = DirectFrame(relief=None, image=gui.find('**/BtlPick_TAB'), image_color=Vec4(1, 0.2, 0.2, 1)) + self.frame.hide() + self.statusFrame = DirectFrame(parent=self.frame, relief=None, image=gui.find('**/ToonBtl_Status_BG'), image_color=Vec4(0.5, 0.9, 0.5, 1), pos=(0.611, 0, 0)) + self.textFrame = DirectFrame(parent=self.frame, relief=None, image=gui.find('**/PckMn_Select_Tab'), image_color=Vec4(1, 1, 0, 1), image_scale=(1.0, 1.0, 2.0), text='', text_fg=Vec4(0, 0, 0, 1), text_pos=(0, 0.02, 0), text_scale=TTLocalizer.FCPtextFrame, pos=(-0.013, 0, 0.013)) + self.textFrame['text'] = TTLocalizer.FireCogTitle % localAvatar.getPinkSlips() + self.avatarButtons = [] + for i in xrange(4): + button = DirectButton(parent=self.frame, relief=None, text='', text_fg=Vec4(0, 0, 0, 1), text_scale=0.067, text_pos=(0, -0.015, 0), textMayChange=1, image_scale=(1.0, 1.0, 1.0), image=(gui.find('**/PckMn_Arrow_Up'), gui.find('**/PckMn_Arrow_Dn'), gui.find('**/PckMn_Arrow_Rlvr')), command=self.__handleAvatar, extraArgs=[i]) + button.setScale(1, 1, 1) + button.setPos(0, 0, 0.2) + self.avatarButtons.append(button) + + self.backButton = DirectButton(parent=self.frame, relief=None, image=(gui.find('**/PckMn_BackBtn'), gui.find('**/PckMn_BackBtn_Dn'), gui.find('**/PckMn_BackBtn_Rlvr')), pos=(-0.647, 0, 0.006), scale=1.05, text=TTLocalizer.TownBattleChooseAvatarBack, text_scale=0.05, text_pos=(0.01, -0.012), text_fg=Vec4(0, 0, 0.8, 1), command=self.__handleBack) + gui.removeNode() + self.loaded = 1 + return + + def unload(self): + if self.loaded: + self.frame.destroy() + del self.frame + del self.statusFrame + del self.textFrame + del self.avatarButtons + del self.backButton + self.loaded = 0 + + def enter(self, numAvatars, localNum = None, luredIndices = None, trappedIndices = None, track = None): + if not self.loaded: + self.load() + self.frame.show() + invalidTargets = [] + if not self.toon: + if len(luredIndices) > 0: + if track == BattleBase.TRAP or track == BattleBase.LURE: + invalidTargets += luredIndices + if len(trappedIndices) > 0: + if track == BattleBase.TRAP: + invalidTargets += trappedIndices + self.__placeButtons(numAvatars, invalidTargets, localNum) + + def exit(self): + self.frame.hide() + + def __handleBack(self): + doneStatus = {'mode': 'Back'} + messenger.send(self.doneEvent, [doneStatus]) + + def __handleAvatar(self, avatar): + doneStatus = {'mode': 'Avatar', + 'avatar': avatar} + messenger.send(self.doneEvent, [doneStatus]) + + def adjustCogs(self, numAvatars, luredIndices, trappedIndices, track): + invalidTargets = [] + if len(luredIndices) > 0: + if track == BattleBase.TRAP or track == BattleBase.LURE: + invalidTargets += luredIndices + if len(trappedIndices) > 0: + if track == BattleBase.TRAP: + invalidTargets += trappedIndices + self.__placeButtons(numAvatars, invalidTargets, None) + return + + def adjustToons(self, numToons, localNum): + self.__placeButtons(numToons, [], localNum) + + def __placeButtons(self, numAvatars, invalidTargets, localNum): + canFire = 0 + + for i in xrange(4): + if numAvatars > i and i not in invalidTargets and i != localNum: + self.avatarButtons[i].show() + self.avatarButtons[i]['text'] = '' + if localAvatar.getPinkSlips(): + self.avatarButtons[i]['state'] = DGG.NORMAL + self.avatarButtons[i]['text_fg'] = (0, 0, 0, 1) + canFire = 1 + else: + self.avatarButtons[i]['state'] = DGG.DISABLED + self.avatarButtons[i]['text_fg'] = (1.0, 0, 0, 1) + else: + self.avatarButtons[i].hide() + + if canFire: + self.textFrame['text'] = TTLocalizer.FireCogTitle % localAvatar.getPinkSlips() + else: + self.textFrame['text'] = TTLocalizer.FireCogLowTitle % localAvatar.getPinkSlips() + if numAvatars == 1: + self.avatarButtons[0].setX(0) + elif numAvatars == 2: + self.avatarButtons[0].setX(0.2) + self.avatarButtons[1].setX(-0.2) + elif numAvatars == 3: + self.avatarButtons[0].setX(0.4) + self.avatarButtons[1].setX(0.0) + self.avatarButtons[2].setX(-0.4) + elif numAvatars == 4: + self.avatarButtons[0].setX(0.6) + self.avatarButtons[1].setX(0.2) + self.avatarButtons[2].setX(-0.2) + self.avatarButtons[3].setX(-0.6) + else: + self.notify.error('Invalid number of avatars: %s' % numAvatars) + return None diff --git a/toontown/battle/Movie.py b/toontown/battle/Movie.py new file mode 100755 index 00000000..232e80f5 --- /dev/null +++ b/toontown/battle/Movie.py @@ -0,0 +1,887 @@ +import copy +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.showbase import DirectObject +import random + +from BattleBase import * +import BattleExperience +import BattleParticles +import MovieDrop +import MovieFire +import MovieHeal +import MovieLure +import MovieNPCSOS +import MoviePetSOS +import MovieSOS +import MovieSound +import MovieSquirt +import MovieSuitAttacks +import MovieThrow +import MovieToonVictory +import MovieTrap +import MovieUtil +import PlayByPlayText +import RewardPanel +from SuitBattleGlobals import * +from toontown.distributed import DelayDelete +from toontown.toon import NPCToons +from toontown.toon import Toon +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase.ToontownBattleGlobals import * +from toontown.toontowngui import TTDialog +from otp.nametag.NametagConstants import * +from otp.nametag.NametagGroup import * + + +camPos = Point3(14, 0, 10) +camHpr = Vec3(89, -30, 0) +randomBattleTimestamp = base.config.GetBool('random-battle-timestamp', 0) + +class Movie(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('Movie') + + def __init__(self, battle): + self.battle = battle + self.track = None + self.rewardPanel = None + self.rewardCallback = None + self.playByPlayText = PlayByPlayText.PlayByPlayText() + self.playByPlayText.hide() + self.renderProps = [] + self.hasBeenReset = 0 + self.reset() + self.rewardHasBeenReset = 0 + self.tutRewardDialog = None + self.resetReward() + return + + def cleanup(self): + self.reset() + self.resetReward() + self.battle = None + if self.playByPlayText != None: + self.playByPlayText.cleanup() + self.playByPlayText = None + if self.rewardPanel != None: + self.rewardPanel.cleanup() + self.rewardPanel = None + self.rewardCallback = None + return + + def needRestoreColor(self): + self.restoreColor = 1 + + def clearRestoreColor(self): + self.restoreColor = 0 + + def needRestoreHips(self): + self.restoreHips = 1 + + def clearRestoreHips(self): + self.restoreHips = 0 + + def needRestoreHeadScale(self): + self.restoreHeadScale = 1 + + def clearRestoreHeadScale(self): + self.restoreHeadScale = 0 + + def needRestoreToonScale(self): + self.restoreToonScale = 1 + + def clearRestoreToonScale(self): + self.restoreToonScale = 0 + + def needRestoreParticleEffect(self, effect): + self.specialParticleEffects.append(effect) + + def clearRestoreParticleEffect(self, effect): + if self.specialParticleEffects.count(effect) > 0: + self.specialParticleEffects.remove(effect) + + def needRestoreRenderProp(self, prop): + self.renderProps.append(prop) + + def clearRenderProp(self, prop): + if self.renderProps.count(prop) > 0: + self.renderProps.remove(prop) + + def restore(self): + return + for toon in self.battle.activeToons: + toon.loop('neutral') + origPos, origHpr = self.battle.getActorPosHpr(toon) + toon.setPosHpr(self.battle, origPos, origHpr) + hands = toon.getRightHands()[:] + hands += toon.getLeftHands() + for hand in hands: + props = hand.getChildren() + for prop in props: + if prop.getName() != 'book': + MovieUtil.removeProp(prop) + + if self.restoreColor == 1: + headParts = toon.getHeadParts() + torsoParts = toon.getTorsoParts() + legsParts = toon.getLegsParts() + partsList = [headParts, torsoParts, legsParts] + for parts in partsList: + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + nextPart.clearColorScale() + nextPart.clearTransparency() + + if self.restoreHips == 1: + parts = toon.getHipsParts() + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + props = nextPart.getChildren() + for prop in props: + if prop.getName() == 'redtape-tube.egg': + MovieUtil.removeProp(prop) + + if self.restoreHeadScale == 1: + headScale = ToontownGlobals.toonHeadScales[toon.style.getAnimal()] + for lod in toon.getLODNames(): + toon.getPart('head', lod).setScale(headScale) + + if self.restoreToonScale == 1: + toon.setScale(1) + headParts = toon.getHeadParts() + for partNum in xrange(0, headParts.getNumPaths()): + part = headParts.getPath(partNum) + part.setHpr(0, 0, 0) + part.setPos(0, 0, 0) + + arms = toon.findAllMatches('**/arms') + sleeves = toon.findAllMatches('**/sleeves') + hands = toon.findAllMatches('**/hands') + for partNum in xrange(0, arms.getNumPaths()): + armPart = arms.getPath(partNum) + sleevePart = sleeves.getPath(partNum) + handsPart = hands.getPath(partNum) + armPart.setHpr(0, 0, 0) + sleevePart.setHpr(0, 0, 0) + handsPart.setHpr(0, 0, 0) + + for suit in self.battle.activeSuits: + if suit._Actor__animControlDict != None: + suit.loop('neutral') + suit.battleTrapIsFresh = 0 + origPos, origHpr = self.battle.getActorPosHpr(suit) + suit.setPosHpr(self.battle, origPos, origHpr) + hands = [suit.getRightHand(), suit.getLeftHand()] + for hand in hands: + props = hand.getChildren() + for prop in props: + MovieUtil.removeProp(prop) + + for effect in self.specialParticleEffects: + if effect != None: + effect.cleanup() + + self.specialParticleEffects = [] + for prop in self.renderProps: + MovieUtil.removeProp(prop) + + self.renderProps = [] + return + + def _deleteTrack(self): + if self.track: + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + return + + def reset(self, finish = 0): + if self.hasBeenReset == 1: + return + self.hasBeenReset = 1 + self.stop() + self._deleteTrack() + if finish == 1: + self.restore() + self.toonAttackDicts = [] + self.suitAttackDicts = [] + self.restoreColor = 0 + self.restoreHips = 0 + self.restoreHeadScale = 0 + self.restoreToonScale = 0 + self.specialParticleEffects = [] + for prop in self.renderProps: + MovieUtil.removeProp(prop) + + self.renderProps = [] + + def resetReward(self, finish = 0): + if self.rewardHasBeenReset == 1: + return + self.rewardHasBeenReset = 1 + self.stop() + self._deleteTrack() + if finish == 1: + self.restore() + self.toonRewardDicts = [] + if self.rewardPanel != None: + self.rewardPanel.destroy() + self.rewardPanel = None + return + + def play(self, ts, callback): + self.hasBeenReset = 0 + ptrack = Sequence() + camtrack = Sequence() + if random.random() > 0.5: + MovieUtil.shotDirection = 'left' + else: + MovieUtil.shotDirection = 'right' + for s in self.battle.activeSuits: + s.battleTrapIsFresh = 0 + + tattacks, tcam = self.__doToonAttacks() + if tattacks: + ptrack.append(tattacks) + camtrack.append(tcam) + sattacks, scam = self.__doSuitAttacks() + if sattacks: + ptrack.append(sattacks) + camtrack.append(scam) + ptrack.append(Func(callback)) + self._deleteTrack() + self.track = Sequence(ptrack, name='movie-track-%d' % self.battle.doId) + if self.battle.localToonPendingOrActive(): + self.track = Parallel(self.track, Sequence(camtrack), name='movie-track-with-cam-%d' % self.battle.doId) + if randomBattleTimestamp == 1: + randNum = random.randint(0, 99) + dur = self.track.getDuration() + ts = float(randNum) / 100.0 * dur + self.track.delayDeletes = [] + for suit in self.battle.suits: + self.track.delayDeletes.append(DelayDelete.DelayDelete(suit, 'Movie.play')) + + for toon in self.battle.toons: + self.track.delayDeletes.append(DelayDelete.DelayDelete(toon, 'Movie.play')) + + self.track.start(ts) + return None + + def finish(self): + self.track.finish() + return None + + def playReward(self, ts, name, callback, noSkip = False): + self.rewardHasBeenReset = 0 + ptrack = Sequence() + camtrack = Sequence() + self.rewardPanel = RewardPanel.RewardPanel(name) + self.rewardPanel.hide() + victory, camVictory, skipper = MovieToonVictory.doToonVictory(self.battle.localToonActive(), self.battle.activeToons, self.toonRewardIds, self.toonRewardDicts, self.deathList, self.rewardPanel, 1, self.uberList, self.helpfulToonsList, noSkip=noSkip) + if victory: + skipper.setIvals((ptrack, camtrack), ptrack.getDuration()) + ptrack.append(victory) + camtrack.append(camVictory) + ptrack.append(Func(callback)) + self._deleteTrack() + self.track = Sequence(ptrack, name='movie-reward-track-%d' % self.battle.doId) + if self.battle.localToonActive(): + self.track = Parallel(self.track, camtrack, name='movie-reward-track-with-cam-%d' % self.battle.doId) + self.track.delayDeletes = [] + for t in self.battle.activeToons: + self.track.delayDeletes.append(DelayDelete.DelayDelete(t, 'Movie.playReward')) + + skipper.setIvals((self.track,), 0.0) + skipper.setBattle(self.battle) + self.track.start(ts) + return None + + def playTutorialReward(self, ts, name, callback): + self.rewardHasBeenReset = 0 + self.rewardPanel = RewardPanel.RewardPanel(name) + self.rewardCallback = callback + self.questList = self.rewardPanel.getQuestIntervalList(base.localAvatar, [0, 1, 1, 0], [base.localAvatar], base.localAvatar.quests[0], [], [base.localAvatar.getDoId()]) + camera.setPosHpr(0, 8, base.localAvatar.getHeight() * 0.66, 179, 15, 0) + self.rewardPanel.initGagFrame(base.localAvatar, [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0], noSkip=True) + self.playTutorialReward_1(None, self.toonRewardDicts[0]['earnedExp']) + + def playTutorialReward_1(self, result, earnedXp, tutTrack=0): + if self.tutRewardDialog: + self.tutRewardDialog.cleanup() + elif tutTrack >= len(earnedXp): + self.playTutorialReward_2() + return + + xp = earnedXp[tutTrack] + + if xp > 0: + self.tutRewardDialog = TTDialog.TTDialog(text=TTLocalizer.MovieTutorialReward1 % (xp, TTLocalizer.BattleGlobalTracks[tutTrack].capitalize()), command=self.playTutorialReward_1, extraArgs=[earnedXp, tutTrack + 1], style=TTDialog.Acknowledge, fadeScreen=None, pos=(0.65, 0, 0.5), scale=0.8) + sequence = Sequence() + sequence += self.rewardPanel.getTrackIntervalList(base.localAvatar, tutTrack, 0, xp, 0) + sequence.start() + else: + self.playTutorialReward_1(None, earnedXp, tutTrack + 1) + + def playTutorialReward_2(self, value=None): + from toontown.toon import Toon + from toontown.toon import ToonDNA + + def doneChat1(page, elapsed = 0): + self.track2.start() + + def doneChat2(elapsed): + self.track2.pause() + self.track3.start() + + def uniqueName(hook): + return 'TutorialTom-' + hook + + self.tutorialTom = Toon.Toon() + dna = ToonDNA.ToonDNA() + dna.newToonFromProperties(*('dls', 'ms', 'm', 'm', 7, 0, 7, 7, 2, 6, 2, 6, 2, 16)) + self.tutorialTom.setDNA(dna) + self.tutorialTom.setName(TTLocalizer.NPCToonNames[20000]) + self.tutorialTom.setPickable(0) + self.tutorialTom.setPlayerType(NametagGroup.CCNonPlayer) + self.tutorialTom.uniqueName = uniqueName + self.musicVolume = 0.9 + music = base.cr.playGame.place.loader.battleMusic + if self.questList: + self.track1 = Sequence(Wait(1.0), Func(self.rewardPanel.initQuestFrame, base.localAvatar, copy.deepcopy(base.localAvatar.quests)), Wait(1.0), Sequence(*self.questList), Wait(1.0), Func(self.rewardPanel.hide), Func(camera.setPosHpr, render, 34, 19.88, 3.48, -90, -2.36, 0), Func(base.localAvatar.animFSM.request, 'neutral'), Func(base.localAvatar.setPosHpr, 40.31, 22.0, -0.47, 150.0, 360.0, 0.0), Wait(0.5), Func(self.tutorialTom.reparentTo, render), Func(self.tutorialTom.show), Func(self.tutorialTom.setPosHpr, 40.29, 17.9, -0.47, 11.31, 0.0, 0.07), Func(self.tutorialTom.animFSM.request, 'TeleportIn'), Wait(1.517), Func(self.tutorialTom.animFSM.request, 'neutral'), Func(self.acceptOnce, self.tutorialTom.uniqueName('doneChatPage'), doneChat1), Func(self.tutorialTom.addActive), Func(music.setVolume, self.musicVolume), Func(self.tutorialTom.setLocalPageChat, TTLocalizer.MovieTutorialReward3, 0, None, [None]), name='tutorial-reward-3a') + self.track2 = Sequence(Func(self.acceptOnce, self.tutorialTom.uniqueName('doneChatPage'), doneChat2), Func(self.tutorialTom.setLocalPageChat, TTLocalizer.MovieTutorialReward4, 1, None, [None]), Func(self.tutorialTom.setPlayRate, 1.5, 'right-hand-start'), Func(self.tutorialTom.play, 'right-hand-start'), Wait(self.tutorialTom.getDuration('right-hand-start') / 1.5), Func(self.tutorialTom.loop, 'right-hand'), name='tutorial-reward-3b') + self.track3 = Parallel(Sequence(Func(self.tutorialTom.setPlayRate, -1.8, 'right-hand-start'), Func(self.tutorialTom.play, 'right-hand-start'), Wait(self.tutorialTom.getDuration('right-hand-start') / 1.8), Func(self.tutorialTom.animFSM.request, 'neutral'), name='tutorial-reward-3ca'), Sequence(Wait(0.5), Func(self.tutorialTom.setChatAbsolute, TTLocalizer.MovieTutorialReward5, CFSpeech | CFTimeout), Wait(1.0), Func(self.tutorialTom.animFSM.request, 'TeleportOut'), Wait(self.tutorialTom.getDuration('teleport')), Wait(1.0), Func(self.playTutorialReward_3, 0), name='tutorial-reward-3cb'), name='tutorial-reward-3c') + self.track1.start() + else: + self.playTutorialReward_3() + + def playTutorialReward_3(self, value=None): + base.localAvatar.setH(270) + self.tutorialTom.removeActive() + self.tutorialTom.delete() + self.questList = None + self.rewardCallback() + + def stop(self): + if self.track: + self.track.finish() + self._deleteTrack() + if hasattr(self, 'track1'): + self.track1.finish() + self.track1 = None + if hasattr(self, 'track2'): + self.track2.finish() + self.track2 = None + if hasattr(self, 'track3'): + self.track3.finish() + self.track3 = None + if self.rewardPanel: + self.rewardPanel.hide() + if self.playByPlayText: + self.playByPlayText.hide() + return + + def __doToonAttacks(self): + if base.config.GetBool('want-toon-attack-anims', 1): + track = Sequence(name='toon-attacks') + camTrack = Sequence(name='toon-attacks-cam') + ival, camIval = MovieFire.doFires(self.__findToonAttack(FIRE)) + if ival: + track.append(ival) + camTrack.append(camIval) + ival, camIval = MovieSOS.doSOSs(self.__findToonAttack(SOS)) + if ival: + track.append(ival) + camTrack.append(camIval) + ival, camIval = MovieNPCSOS.doNPCSOSs(self.__findToonAttack(NPCSOS)) + if ival: + track.append(ival) + camTrack.append(camIval) + ival, camIval = MoviePetSOS.doPetSOSs(self.__findToonAttack(PETSOS)) + if ival: + track.append(ival) + camTrack.append(camIval) + ival, camIval = MovieHeal.doHeals(self.__findToonAttack(HEAL), self.battle.getInteractivePropTrackBonus() == HEAL) + if ival: + track.append(ival) + camTrack.append(camIval) + ival, camIval = MovieTrap.doTraps(self.__findToonAttack(TRAP)) + if ival: + track.append(ival) + camTrack.append(camIval) + ival, camIval = MovieLure.doLures(self.__findToonAttack(LURE)) + if ival: + track.append(ival) + camTrack.append(camIval) + ival, camIval = MovieSound.doSounds(self.__findToonAttack(SOUND)) + if ival: + track.append(ival) + camTrack.append(camIval) + ival, camIval = MovieThrow.doThrows(self.__findToonAttack(THROW)) + if ival: + track.append(ival) + camTrack.append(camIval) + ival, camIval = MovieSquirt.doSquirts(self.__findToonAttack(SQUIRT)) + if ival: + track.append(ival) + camTrack.append(camIval) + ival, camIval = MovieDrop.doDrops(self.__findToonAttack(DROP)) + if ival: + track.append(ival) + camTrack.append(camIval) + if len(track) == 0: + return (None, None) + else: + return (track, camTrack) + else: + return (None, None) + return None + + def genRewardDicts(self, id0, origExp0, earnedExp0, origQuests0, items0, missedItems0, origMerits0, merits0, parts0, id1, origExp1, earnedExp1, origQuests1, items1, missedItems1, origMerits1, merits1, parts1, id2, origExp2, earnedExp2, origQuests2, items2, missedItems2, origMerits2, merits2, parts2, id3, origExp3, earnedExp3, origQuests3, items3, missedItems3, origMerits3, merits3, parts3, deathList, uberList, helpfulToonsList): + self.deathList = deathList + self.helpfulToonsList = helpfulToonsList + entries = ((id0, + origExp0, + earnedExp0, + origQuests0, + items0, + missedItems0, + origMerits0, + merits0, + parts0), + (id1, + origExp1, + earnedExp1, + origQuests1, + items1, + missedItems1, + origMerits1, + merits1, + parts1), + (id2, + origExp2, + earnedExp2, + origQuests2, + items2, + missedItems2, + origMerits2, + merits2, + parts2), + (id3, + origExp3, + earnedExp3, + origQuests3, + items3, + missedItems3, + origMerits3, + merits3, + parts3)) + self.toonRewardDicts = BattleExperience.genRewardDicts(entries) + self.toonRewardIds = [id0, + id1, + id2, + id3] + self.uberList = uberList + + def genAttackDicts(self, toons, suits, id0, tr0, le0, tg0, hp0, ac0, hpb0, kbb0, died0, revive0, id1, tr1, le1, tg1, hp1, ac1, hpb1, kbb1, died1, revive1, id2, tr2, le2, tg2, hp2, ac2, hpb2, kbb2, died2, revive2, id3, tr3, le3, tg3, hp3, ac3, hpb3, kbb3, died3, revive3, sid0, at0, stg0, dm0, sd0, sb0, st0, sid1, at1, stg1, dm1, sd1, sb1, st1, sid2, at2, stg2, dm2, sd2, sb2, st2, sid3, at3, stg3, dm3, sd3, sb3, st3): + if self.track and self.track.isPlaying(): + self.notify.warning('genAttackDicts() - track is playing!') + toonAttacks = ((id0, + tr0, + le0, + tg0, + hp0, + ac0, + hpb0, + kbb0, + died0, + revive0), + (id1, + tr1, + le1, + tg1, + hp1, + ac1, + hpb1, + kbb1, + died1, + revive1), + (id2, + tr2, + le2, + tg2, + hp2, + ac2, + hpb2, + kbb2, + died2, + revive2), + (id3, + tr3, + le3, + tg3, + hp3, + ac3, + hpb3, + kbb3, + died3, + revive3)) + self.__genToonAttackDicts(toons, suits, toonAttacks) + suitAttacks = ((sid0, + at0, + stg0, + dm0, + sd0, + sb0, + st0), + (sid1, + at1, + stg1, + dm1, + sd1, + sb1, + st1), + (sid2, + at2, + stg2, + dm2, + sd2, + sb2, + st2), + (sid3, + at3, + stg3, + dm3, + sd3, + sb3, + st3)) + self.__genSuitAttackDicts(toons, suits, suitAttacks) + + def __genToonAttackDicts(self, toons, suits, toonAttacks): + for ta in toonAttacks: + targetGone = 0 + track = ta[TOON_TRACK_COL] + if track != NO_ATTACK: + adict = {} + toonIndex = ta[TOON_ID_COL] + toonId = toons[toonIndex] + toon = self.battle.findToon(toonId) + if toon == None: + continue + level = ta[TOON_LVL_COL] + adict['toon'] = toon + adict['track'] = track + adict['level'] = level + hps = ta[TOON_HP_COL] + kbbonuses = ta[TOON_KBBONUS_COL] + if track == NPCSOS: + adict['npcId'] = ta[TOON_TGT_COL] + toonId = ta[TOON_TGT_COL] + track, npc_level, npc_hp = NPCToons.getNPCTrackLevelHp(adict['npcId']) + if track == None: + track = NPCSOS + adict['track'] = track + adict['level'] = npc_level + elif track == PETSOS: + petId = ta[TOON_TGT_COL] + adict['toonId'] = toonId + adict['petId'] = petId + if track == SOS: + targetId = ta[TOON_TGT_COL] + if targetId == base.localAvatar.doId: + target = base.localAvatar + adict['targetType'] = 'callee' + elif toon == base.localAvatar: + target = base.cr.identifyAvatar(targetId) + adict['targetType'] = 'caller' + else: + target = None + adict['targetType'] = 'observer' + adict['target'] = target + elif track == NPCSOS or track == NPC_COGS_MISS or track == NPC_TOONS_HIT or track == NPC_RESTOCK_GAGS or track == PETSOS: + adict['special'] = 1 + toonHandles = [] + for t in toons: + if t != -1: + target = self.battle.findToon(t) + if target == None: + continue + if track == NPC_TOONS_HIT and t == toonId: + continue + toonHandles.append(target) + + adict['toons'] = toonHandles + suitHandles = [] + for s in suits: + if s != -1: + target = self.battle.findSuit(s) + if target == None: + continue + suitHandles.append(target) + + adict['suits'] = suitHandles + if track == PETSOS: + del adict['special'] + targets = [] + for t in toons: + if t != -1: + target = self.battle.findToon(t) + if target == None: + continue + tdict = {} + tdict['toon'] = target + tdict['hp'] = hps[toons.index(t)] + self.notify.debug('PETSOS: toon: %d healed for hp: %d' % (target.doId, hps[toons.index(t)])) + targets.append(tdict) + + if len(targets) > 0: + adict['target'] = targets + elif track == HEAL: + if levelAffectsGroup(HEAL, level): + targets = [] + for t in toons: + if t != toonId and t != -1: + target = self.battle.findToon(t) + if target == None: + continue + tdict = {} + tdict['toon'] = target + tdict['hp'] = hps[toons.index(t)] + self.notify.debug('HEAL: toon: %d healed for hp: %d' % (target.doId, hps[toons.index(t)])) + targets.append(tdict) + + if len(targets) > 0: + adict['target'] = targets + else: + targetGone = 1 + else: + targetIndex = ta[TOON_TGT_COL] + if targetIndex < 0: + targetGone = 1 + else: + targetId = toons[targetIndex] + target = self.battle.findToon(targetId) + if target != None: + tdict = {} + tdict['toon'] = target + tdict['hp'] = hps[targetIndex] + adict['target'] = tdict + else: + targetGone = 1 + elif attackAffectsGroup(track, level, ta[TOON_TRACK_COL]): + targets = [] + for s in suits: + if s != -1: + target = self.battle.findSuit(s) + if ta[TOON_TRACK_COL] == NPCSOS: + if track == LURE and self.battle.isSuitLured(target) == 1: + continue + elif track == TRAP and (self.battle.isSuitLured(target) == 1 or target.battleTrap != NO_TRAP): + continue + targetIndex = suits.index(s) + sdict = {} + sdict['suit'] = target + sdict['hp'] = hps[targetIndex] + if ta[TOON_TRACK_COL] == NPCSOS and track == DROP and hps[targetIndex] == 0: + continue + sdict['kbbonus'] = kbbonuses[targetIndex] + sdict['died'] = ta[SUIT_DIED_COL] & 1 << targetIndex + sdict['revived'] = ta[SUIT_REVIVE_COL] & 1 << targetIndex + if sdict['died'] != 0: + pass + sdict['leftSuits'] = [] + sdict['rightSuits'] = [] + targets.append(sdict) + + adict['target'] = targets + else: + targetIndex = ta[TOON_TGT_COL] + if targetIndex < 0: + targetGone = 1 + else: + targetId = suits[targetIndex] + target = self.battle.findSuit(targetId) + sdict = {} + sdict['suit'] = target + if self.battle.activeSuits.count(target) == 0: + targetGone = 1 + suitIndex = 0 + else: + suitIndex = self.battle.activeSuits.index(target) + leftSuits = [] + for si in xrange(0, suitIndex): + asuit = self.battle.activeSuits[si] + if self.battle.isSuitLured(asuit) == 0: + leftSuits.append(asuit) + + lenSuits = len(self.battle.activeSuits) + rightSuits = [] + if lenSuits > suitIndex + 1: + for si in xrange(suitIndex + 1, lenSuits): + asuit = self.battle.activeSuits[si] + if self.battle.isSuitLured(asuit) == 0: + rightSuits.append(asuit) + + sdict['leftSuits'] = leftSuits + sdict['rightSuits'] = rightSuits + sdict['hp'] = hps[targetIndex] + sdict['kbbonus'] = kbbonuses[targetIndex] + sdict['died'] = ta[SUIT_DIED_COL] & 1 << targetIndex + sdict['revived'] = ta[SUIT_REVIVE_COL] & 1 << targetIndex + if sdict['revived'] != 0: + pass + if sdict['died'] != 0: + pass + if track == DROP or track == TRAP: + adict['target'] = [sdict] + else: + adict['target'] = sdict + adict['hpbonus'] = ta[TOON_HPBONUS_COL] + adict['sidestep'] = ta[TOON_ACCBONUS_COL] + if 'npcId' in adict: + adict['sidestep'] = 0 + adict['battle'] = self.battle + adict['playByPlayText'] = self.playByPlayText + if targetGone == 0: + self.toonAttackDicts.append(adict) + else: + self.notify.warning('genToonAttackDicts() - target gone!') + + def compFunc(a, b): + alevel = a['level'] + blevel = b['level'] + if alevel > blevel: + return 1 + elif alevel < blevel: + return -1 + return 0 + + self.toonAttackDicts.sort(compFunc) + return + + def __findToonAttack(self, track): + setCapture = 0 + tp = [] + for ta in self.toonAttackDicts: + if ta['track'] == track or track == NPCSOS and 'sepcial' in ta: + tp.append(ta) + if track == SQUIRT: + setCapture = 1 + + if track == TRAP: + sortedTraps = [] + for attack in tp: + if 'npcId' not in attack: + sortedTraps.append(attack) + + for attack in tp: + if 'npcId' in attack: + sortedTraps.append(attack) + + tp = sortedTraps + if setCapture: + pass + return tp + + def __genSuitAttackDicts(self, toons, suits, suitAttacks): + for sa in suitAttacks: + targetGone = 0 + attack = sa[SUIT_ATK_COL] + if attack != NO_ATTACK: + suitIndex = sa[SUIT_ID_COL] + suitId = suits[suitIndex] + suit = self.battle.findSuit(suitId) + if suit == None: + self.notify.warning('suit: %d not in battle!' % suitId) + return + adict = getSuitAttack(suit.getStyleName(), suit.getLevel(), attack) + adict['suit'] = suit + adict['battle'] = self.battle + adict['playByPlayText'] = self.playByPlayText + adict['taunt'] = sa[SUIT_TAUNT_COL] + hps = sa[SUIT_HP_COL] + if adict['group'] == ATK_TGT_GROUP: + targets = [] + for t in toons: + if t != -1: + target = self.battle.findToon(t) + if target == None: + continue + targetIndex = toons.index(t) + tdict = {} + tdict['toon'] = target + tdict['hp'] = hps[targetIndex] + self.notify.debug('DAMAGE: toon: %d hit for hp: %d' % (target.doId, hps[targetIndex])) + toonDied = sa[TOON_DIED_COL] & 1 << targetIndex + tdict['died'] = toonDied + targets.append(tdict) + + if len(targets) > 0: + adict['target'] = targets + else: + targetGone = 1 + elif adict['group'] == ATK_TGT_SINGLE: + targetIndex = sa[SUIT_TGT_COL] + targetId = toons[targetIndex] + target = self.battle.findToon(targetId) + if target == None: + targetGone = 1 + break + tdict = {} + tdict['toon'] = target + tdict['hp'] = hps[targetIndex] + self.notify.debug('DAMAGE: toon: %d hit for hp: %d' % (target.doId, hps[targetIndex])) + toonDied = sa[TOON_DIED_COL] & 1 << targetIndex + tdict['died'] = toonDied + toonIndex = self.battle.activeToons.index(target) + rightToons = [] + for ti in xrange(0, toonIndex): + rightToons.append(self.battle.activeToons[ti]) + + lenToons = len(self.battle.activeToons) + leftToons = [] + if lenToons > toonIndex + 1: + for ti in xrange(toonIndex + 1, lenToons): + leftToons.append(self.battle.activeToons[ti]) + + tdict['leftToons'] = leftToons + tdict['rightToons'] = rightToons + adict['target'] = tdict + else: + self.notify.warning('got suit attack not group or single!') + if targetGone == 0: + self.suitAttackDicts.append(adict) + else: + self.notify.warning('genSuitAttackDicts() - target gone!') + + return + + def __doSuitAttacks(self): + if base.config.GetBool('want-suit-anims', 1): + track = Sequence(name='suit-attacks') + camTrack = Sequence(name='suit-attacks-cam') + isLocalToonSad = False + for a in self.suitAttackDicts: + ival, camIval = MovieSuitAttacks.doSuitAttack(a) + if ival: + track.append(ival) + camTrack.append(camIval) + targetField = a.get('target') + if targetField is None: + continue + if a['group'] == ATK_TGT_GROUP: + for target in targetField: + if target['died'] and target['toon'].doId == base.localAvatar.doId: + isLocalToonSad = True + + elif a['group'] == ATK_TGT_SINGLE: + if targetField['died'] and targetField['toon'].doId == base.localAvatar.doId: + isLocalToonSad = True + if isLocalToonSad: + break + + if len(track) == 0: + return (None, None) + return (track, camTrack) + else: + return (None, None) + return diff --git a/toontown/battle/MovieCamera.py b/toontown/battle/MovieCamera.py new file mode 100755 index 00000000..aa666dd5 --- /dev/null +++ b/toontown/battle/MovieCamera.py @@ -0,0 +1,922 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from BattleBase import * +from BattleProps import * +from toontown.toonbase.ToontownBattleGlobals import * +from SuitBattleGlobals import * +from direct.directnotify import DirectNotifyGlobal +import random +import MovieUtil +notify = DirectNotifyGlobal.directNotify.newCategory('MovieCamera') + +def chooseHealShot(heals, attackDuration): + isUber = 0 + for heal in heals: + if heal['level'] == 6 and not heal.get('petId'): + isUber = 1 + + if isUber: + openShot = chooseHealOpenShot(heals, attackDuration, isUber) + openDuration = openShot.getDuration() + openName = openShot.getName() + closeShot = chooseHealCloseShot(heals, openDuration, openName, attackDuration * 3, isUber) + track = Sequence(closeShot) + else: + openShot = chooseHealOpenShot(heals, attackDuration, isUber) + openDuration = openShot.getDuration() + openName = openShot.getName() + closeShot = chooseHealCloseShot(heals, openDuration, openName, attackDuration, isUber) + track = Sequence(openShot, closeShot) + return track + + +def chooseHealOpenShot(heals, attackDuration, isUber = 0): + numHeals = len(heals) + av = None + duration = 2.8 + if isUber: + duration = 5.0 + shotChoices = [toonGroupShot] + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseHealMidShot(heals, attackDuration, isUber = 0): + numHeals = len(heals) + av = None + duration = 2.1 + if isUber: + duration = 2.1 + shotChoices = [toonGroupHighShot] + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseHealCloseShot(heals, openDuration, openName, attackDuration, isUber = 0): + av = None + duration = attackDuration - openDuration + shotChoices = [toonGroupShot] + if isUber: + shotChoices = [allGroupLowShot] + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseTrapShot(traps, attackDuration, enterDuration = 0, exitDuration = 0): + enterShot = chooseNPCEnterShot(traps, enterDuration) + openShot = chooseTrapOpenShot(traps, attackDuration) + openDuration = openShot.getDuration() + openName = openShot.getName() + closeShot = chooseTrapCloseShot(traps, openDuration, openName, attackDuration) + exitShot = chooseNPCExitShot(traps, exitDuration) + track = Sequence(enterShot, openShot, closeShot, exitShot) + return track + + +def chooseTrapOpenShot(traps, attackDuration): + numTraps = len(traps) + av = None + duration = 3.0 + shotChoices = [allGroupLowShot] + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseTrapCloseShot(traps, openDuration, openName, attackDuration): + av = None + duration = attackDuration - openDuration + shotChoices = [allGroupLowShot] + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseLureShot(lures, attackDuration, enterDuration = 0.0, exitDuration = 0.0): + enterShot = chooseNPCEnterShot(lures, enterDuration) + openShot = chooseLureOpenShot(lures, attackDuration) + openDuration = openShot.getDuration() + openName = openShot.getName() + closeShot = chooseLureCloseShot(lures, openDuration, openName, attackDuration) + exitShot = chooseNPCExitShot(lures, exitDuration) + track = Sequence(enterShot, openShot, closeShot, exitShot) + return track + + +def chooseLureOpenShot(lures, attackDuration): + numLures = len(lures) + av = None + duration = 3.0 + shotChoices = [allGroupLowShot] + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseLureCloseShot(lures, openDuration, openName, attackDuration): + av = None + duration = attackDuration - openDuration + hasTrainTrackTrap = False + battle = lures[0]['battle'] + for suit in battle.suits: + if hasattr(suit, 'battleTrap') and suit.battleTrap == UBER_GAG_LEVEL_INDEX: + hasTrainTrackTrap = True + + if hasTrainTrackTrap: + shotChoices = [avatarBehindHighRightShot] + av = lures[0]['toon'] + else: + shotChoices = [allGroupLowShot] + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseSoundShot(sounds, targets, attackDuration, enterDuration = 0.0, exitDuration = 0.0): + enterShot = chooseNPCEnterShot(sounds, enterDuration) + openShot = chooseSoundOpenShot(sounds, targets, attackDuration) + openDuration = openShot.getDuration() + openName = openShot.getName() + closeShot = chooseSoundCloseShot(sounds, targets, openDuration, openName, attackDuration) + exitShot = chooseNPCExitShot(sounds, exitDuration) + track = Sequence(enterShot, openShot, closeShot, exitShot) + return track + + +def chooseSoundOpenShot(sounds, targets, attackDuration): + duration = 3.1 + isUber = 0 + for sound in sounds: + if sound['level'] == 6: + isUber = 1 + duration = 5.0 + + numSounds = len(sounds) + av = None + if numSounds == 1: + av = sounds[0]['toon'] + if isUber: + shotChoices = [avatarCloseUpThreeQuarterRightShotWide, allGroupLowShot, suitGroupThreeQuarterLeftBehindShot] + else: + shotChoices = [avatarCloseUpThreeQuarterRightShot, allGroupLowShot, suitGroupThreeQuarterLeftBehindShot] + elif numSounds >= 2 and numSounds <= 4: + shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot] + else: + notify.error('Bad number of sounds: %s' % numSounds) + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseSoundCloseShot(sounds, targets, openDuration, openName, attackDuration): + numSuits = len(targets) + av = None + duration = attackDuration - openDuration + if numSuits == 1: + av = targets[0]['suit'] + shotChoices = [avatarCloseUpThrowShot, + avatarCloseUpThreeQuarterLeftShot, + allGroupLowShot, + suitGroupThreeQuarterLeftBehindShot] + elif numSuits >= 2 and numSuits <= 4: + shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot] + else: + notify.error('Bad number of suits: %s' % numSuits) + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseThrowShot(throws, suitThrowsDict, attackDuration): + openShot = chooseThrowOpenShot(throws, suitThrowsDict, attackDuration) + openDuration = openShot.getDuration() + openName = openShot.getName() + closeShot = chooseThrowCloseShot(throws, suitThrowsDict, openDuration, openName, attackDuration) + track = Sequence(openShot, closeShot) + return track + + +def chooseThrowOpenShot(throws, suitThrowsDict, attackDuration): + numThrows = len(throws) + av = None + duration = 3.0 + if numThrows == 1: + av = throws[0]['toon'] + shotChoices = [avatarCloseUpThrowShot, + avatarCloseUpThreeQuarterRightShot, + avatarBehindShot, + allGroupLowShot, + suitGroupThreeQuarterLeftBehindShot] + elif numThrows >= 2 and numThrows <= 4: + shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot] + else: + notify.error('Bad number of throws: %s' % numThrows) + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseThrowCloseShot(throws, suitThrowsDict, openDuration, openName, attackDuration): + numSuits = len(suitThrowsDict) + av = None + duration = attackDuration - openDuration + if numSuits == 1: + av = base.cr.doId2do[suitThrowsDict.keys()[0]] + shotChoices = [avatarCloseUpThrowShot, + avatarCloseUpThreeQuarterLeftShot, + allGroupLowShot, + suitGroupThreeQuarterLeftBehindShot] + elif numSuits >= 2 and numSuits <= 4 or numSuits == 0: + shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot] + else: + notify.error('Bad number of suits: %s' % numSuits) + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseSquirtShot(squirts, suitSquirtsDict, attackDuration): + openShot = chooseSquirtOpenShot(squirts, suitSquirtsDict, attackDuration) + openDuration = openShot.getDuration() + openName = openShot.getName() + closeShot = chooseSquirtCloseShot(squirts, suitSquirtsDict, openDuration, openName, attackDuration) + track = Sequence(openShot, closeShot) + return track + + +def chooseSquirtOpenShot(squirts, suitSquirtsDict, attackDuration): + numSquirts = len(squirts) + av = None + duration = 3.0 + if numSquirts == 1: + av = squirts[0]['toon'] + shotChoices = [avatarCloseUpThrowShot, + avatarCloseUpThreeQuarterRightShot, + avatarBehindShot, + allGroupLowShot, + suitGroupThreeQuarterLeftBehindShot] + elif numSquirts >= 2 and numSquirts <= 4: + shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot] + else: + notify.error('Bad number of squirts: %s' % numSquirts) + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseSquirtCloseShot(squirts, suitSquirtsDict, openDuration, openName, attackDuration): + numSuits = len(suitSquirtsDict) + av = None + duration = attackDuration - openDuration + if numSuits == 1: + av = base.cr.doId2do[suitSquirtsDict.keys()[0]] + shotChoices = [avatarCloseUpThrowShot, + avatarCloseUpThreeQuarterLeftShot, + allGroupLowShot, + suitGroupThreeQuarterLeftBehindShot] + elif numSuits >= 2 and numSuits <= 4: + shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot] + else: + notify.error('Bad number of suits: %s' % numSuits) + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseDropShot(drops, suitDropsDict, attackDuration, enterDuration = 0.0, exitDuration = 0.0): + enterShot = chooseNPCEnterShot(drops, enterDuration) + openShot = chooseDropOpenShot(drops, suitDropsDict, attackDuration) + openDuration = openShot.getDuration() + openName = openShot.getName() + closeShot = chooseDropCloseShot(drops, suitDropsDict, openDuration, openName, attackDuration) + exitShot = chooseNPCExitShot(drops, exitDuration) + track = Sequence(enterShot, openShot, closeShot, exitShot) + return track + + +def chooseDropOpenShot(drops, suitDropsDict, attackDuration): + numDrops = len(drops) + av = None + duration = 3.0 + if numDrops == 1: + av = drops[0]['toon'] + shotChoices = [avatarCloseUpThrowShot, + avatarCloseUpThreeQuarterRightShot, + avatarBehindShot, + allGroupLowShot, + suitGroupThreeQuarterLeftBehindShot] + elif numDrops >= 2 and numDrops <= 4 or numDrops == 0: + shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot] + else: + notify.error('Bad number of drops: %s' % numDrops) + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseDropCloseShot(drops, suitDropsDict, openDuration, openName, attackDuration): + numSuits = len(suitDropsDict) + av = None + duration = attackDuration - openDuration + if numSuits == 1: + av = base.cr.doId2do[suitDropsDict.keys()[0]] + shotChoices = [avatarCloseUpThrowShot, + avatarCloseUpThreeQuarterLeftShot, + allGroupLowShot, + suitGroupThreeQuarterLeftBehindShot] + elif numSuits >= 2 and numSuits <= 4 or numSuits == 0: + shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot] + else: + notify.error('Bad number of suits: %s' % numSuits) + choice = random.choice(shotChoices) + track = choice(av, duration) + return track + + +def chooseNPCEnterShot(enters, entersDuration): + av = None + duration = entersDuration + shotChoices = [toonGroupShot] + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseNPCExitShot(exits, exitsDuration): + av = None + duration = exitsDuration + shotChoices = [toonGroupShot] + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseSuitShot(attack, attackDuration): + groupStatus = attack['group'] + target = attack['target'] + if groupStatus == ATK_TGT_SINGLE: + toon = target['toon'] + suit = attack['suit'] + name = attack['id'] + battle = attack['battle'] + camTrack = Sequence() + + def defaultCamera(attack = attack, attackDuration = attackDuration, openShotDuration = 3.5, target = target): + if attack['group'] == ATK_TGT_GROUP: + return randomGroupAttackCam(attack['suit'], target, attack['battle'], attackDuration, openShotDuration) + else: + return randomAttackCam(attack['suit'], target['toon'], attack['battle'], attackDuration, openShotDuration, 'suit') + + if name == AUDIT: + camTrack.append(defaultCamera()) + elif name == BITE: + camTrack.append(defaultCamera(openShotDuration=2.8)) + elif name == BOUNCE_CHECK: + camTrack.append(defaultCamera()) + elif name == BRAIN_STORM: + camTrack.append(defaultCamera(openShotDuration=2.4)) + elif name == BUZZ_WORD: + camTrack.append(defaultCamera(openShotDuration=4.7)) + elif name == CALCULATE: + camTrack.append(defaultCamera()) + elif name == CANNED: + camTrack.append(defaultCamera(openShotDuration=2.9)) + elif name == CHOMP: + camTrack.append(defaultCamera(openShotDuration=2.8)) + elif name == CIGAR_SMOKE: + camTrack.append(defaultCamera(openShotDuration=3.0)) + elif name == CLIPON_TIE: + camTrack.append(defaultCamera(openShotDuration=3.3)) + elif name == CRUNCH: + camTrack.append(defaultCamera(openShotDuration=3.4)) + elif name == DEMOTION: + camTrack.append(defaultCamera(openShotDuration=1.7)) + elif name == DOUBLE_TALK: + camTrack.append(defaultCamera(openShotDuration=3.9)) + elif name == EVICTION_NOTICE: + camTrack.append(defaultCamera(openShotDuration=3.2)) + elif name == EVIL_EYE: + camTrack.append(defaultCamera(openShotDuration=2.7)) + elif name == FILIBUSTER: + camTrack.append(defaultCamera(openShotDuration=2.7)) + elif name == FILL_WITH_LEAD: + camTrack.append(defaultCamera(openShotDuration=3.2)) + elif name == FINGER_WAG: + camTrack.append(defaultCamera(openShotDuration=2.3)) + elif name == FIRED: + camTrack.append(defaultCamera(openShotDuration=1.7)) + elif name == FOUNTAIN_PEN: + camTrack.append(defaultCamera(openShotDuration=2.6)) + elif name == FREEZE_ASSETS: + camTrack.append(defaultCamera(openShotDuration=2.5)) + elif name == HALF_WINDSOR: + camTrack.append(defaultCamera(openShotDuration=2.8)) + elif name == HEAD_SHRINK: + camTrack.append(defaultCamera(openShotDuration=1.3)) + elif name == GLOWER_POWER: + camTrack.append(defaultCamera(openShotDuration=1.4)) + elif name == GUILT_TRIP: + camTrack.append(defaultCamera(openShotDuration=0.9)) + elif name == HANG_UP: + camTrack.append(defaultCamera(openShotDuration=5.1)) + elif name == HOT_AIR: + camTrack.append(defaultCamera(openShotDuration=2.5)) + elif name == JARGON: + camTrack.append(defaultCamera()) + elif name == LEGALESE: + camTrack.append(defaultCamera(openShotDuration=1.5)) + elif name == LIQUIDATE: + camTrack.append(defaultCamera(openShotDuration=2.5)) + elif name == MARKET_CRASH: + camTrack.append(defaultCamera(openShotDuration=2.9)) + elif name == MUMBO_JUMBO: + camTrack.append(defaultCamera(openShotDuration=2.8)) + elif name == PARADIGM_SHIFT: + camTrack.append(defaultCamera(openShotDuration=1.6)) + elif name == PECKING_ORDER: + camTrack.append(defaultCamera(openShotDuration=2.8)) + elif name == PLAY_HARDBALL: + camTrack.append(defaultCamera(openShotDuration=2.3)) + elif name == PICK_POCKET: + camTrack.append(allGroupLowShot(suit, 2.7)) + elif name == PINK_SLIP: + camTrack.append(defaultCamera(openShotDuration=2.8)) + elif name == POUND_KEY: + camTrack.append(defaultCamera(openShotDuration=2.8)) + elif name == POWER_TIE: + camTrack.append(defaultCamera(openShotDuration=2.4)) + elif name == POWER_TRIP: + camTrack.append(defaultCamera(openShotDuration=1.1)) + elif name == QUAKE: + shakeIntensity = 5.15 + quake = 1 + camTrack.append(suitCameraShakeShot(suit, attackDuration, shakeIntensity, quake)) + elif name == RAZZLE_DAZZLE: + camTrack.append(defaultCamera(openShotDuration=2.2)) + elif name == RED_TAPE: + camTrack.append(defaultCamera(openShotDuration=3.5)) + elif name == RE_ORG: + camTrack.append(defaultCamera(openShotDuration=1.1)) + elif name == RESTRAINING_ORDER: + camTrack.append(defaultCamera(openShotDuration=2.8)) + elif name == ROLODEX: + camTrack.append(defaultCamera()) + elif name == RUBBER_STAMP: + camTrack.append(defaultCamera(openShotDuration=3.2)) + elif name == RUB_OUT: + camTrack.append(defaultCamera(openShotDuration=2.2)) + elif name == SACKED: + camTrack.append(defaultCamera(openShotDuration=2.9)) + elif name == SCHMOOZE: + camTrack.append(defaultCamera(openShotDuration=2.8)) + elif name == SHAKE: + shakeIntensity = 1.75 + camTrack.append(suitCameraShakeShot(suit, attackDuration, shakeIntensity)) + elif name == SHRED: + camTrack.append(defaultCamera(openShotDuration=4.1)) + elif name == SPIN: + camTrack.append(defaultCamera(openShotDuration=1.7)) + elif name == SYNERGY: + camTrack.append(defaultCamera(openShotDuration=1.7)) + elif name == TABULATE: + camTrack.append(defaultCamera()) + elif name == TEE_OFF: + camTrack.append(defaultCamera(openShotDuration=4.5)) + elif name == TREMOR: + shakeIntensity = 0.25 + camTrack.append(suitCameraShakeShot(suit, attackDuration, shakeIntensity)) + elif name == WATERCOOLER: + camTrack.append(defaultCamera()) + elif name == WITHDRAWAL: + camTrack.append(defaultCamera(openShotDuration=1.2)) + elif name == WRITE_OFF: + camTrack.append(defaultCamera()) + elif name == THROW_BOOK: + camTrack.append(defaultCamera(openShotDuration=2.9)) + else: + notify.warning('unknown attack id in chooseSuitShot: %d using default cam' % name) + camTrack.append(defaultCamera()) + pbpText = attack['playByPlayText'] + displayName = TTLocalizer.SuitAttackNames[attack['name']] + pbpTrack = pbpText.getShowInterval(displayName, 3.5) + return Parallel(camTrack, pbpTrack) + +def makeShot(x, y, z, h, p, r, duration, other = None, name = 'makeShot'): + if other: + return heldRelativeShot(other, x, y, z, h, p, r, duration, name) + else: + return heldShot(x, y, z, h, p, r, duration, name) + + +def focusShot(x, y, z, duration, target, other = None, splitFocusPoint = None, name = 'focusShot'): + track = Sequence() + if other: + track.append(Func(camera.setPos, other, Point3(x, y, z))) + else: + track.append(Func(camera.setPos, Point3(x, y, z))) + if splitFocusPoint: + track.append(Func(focusCameraBetweenPoints, target, splitFocusPoint)) + else: + track.append(Func(camera.lookAt, target)) + track.append(Wait(duration)) + return track + + +def moveShot(x, y, z, h, p, r, duration, other = None, name = 'moveShot'): + return motionShot(x, y, z, h, p, r, duration, other, name) + + +def focusMoveShot(x, y, z, duration, target, other = None, name = 'focusMoveShot'): + camera.setPos(Point3(x, y, z)) + camera.lookAt(target) + hpr = camera.getHpr() + return motionShot(x, y, z, hpr[0], hpr[1], hpr[2], duration, other, name) + + +def chooseSOSShot(av, duration): + shotChoices = [avatarCloseUpThreeQuarterRightShot, + avatarBehindShot, + avatarBehindHighShot, + suitGroupThreeQuarterLeftBehindShot] + track = apply(random.choice(shotChoices), [av, duration]) + return track + + +def chooseRewardShot(av, duration, allowGroupShot = 1): + + def chooseRewardShotNow(av): + if av.playingAnim == 'victory' or not allowGroupShot: + shotChoices = [(0, + 8, + av.getHeight() * 0.66, + 179, + 15, + 0), (5.2, + 5.45, + av.getHeight() * 0.66, + 131.5, + 3.6, + 0)] + shot = random.choice(shotChoices) + camera.setPosHpr(av, *shot) + else: + camera.setPosHpr(10, 0, 10, 115, -30, 0) + + return Sequence(Func(chooseRewardShotNow, av), Wait(duration)) + + +def heldShot(x, y, z, h, p, r, duration, name = 'heldShot'): + track = Sequence(name=name) + track.append(Func(camera.setPosHpr, x, y, z, h, p, r)) + track.append(Wait(duration)) + return track + + +def heldRelativeShot(other, x, y, z, h, p, r, duration, name = 'heldRelativeShot'): + track = Sequence(name=name) + track.append(Func(camera.setPosHpr, other, x, y, z, h, p, r)) + track.append(Wait(duration)) + return track + + +def motionShot(x, y, z, h, p, r, duration, other = None, name = 'motionShot'): + if other: + posTrack = LerpPosInterval(camera, duration, pos=Point3(x, y, z), other=other) + hprTrack = LerpHprInterval(camera, duration, hpr=Point3(h, p, r), other=other) + else: + posTrack = LerpPosInterval(camera, duration, pos=Point3(x, y, z)) + hprTrack = LerpHprInterval(camera, duration, hpr=Point3(h, p, r)) + return Parallel(posTrack, hprTrack) + + +def allGroupShot(avatar, duration): + return heldShot(10, 0, 10, 89, -30, 0, duration, 'allGroupShot') + + +def allGroupLowShot(avatar, duration): + return heldShot(15, 0, 3, 89, 0, 0, duration, 'allGroupLowShot') + + +def allGroupLowDiagonalShot(avatar, duration): + return heldShot(7, 5, 6, 119, -30, 0, duration, 'allGroupLowShot') + + +def toonGroupShot(avatar, duration): + return heldShot(10, 0, 10, 115, -30, 0, duration, 'toonGroupShot') + + +def toonGroupHighShot(avatar, duration): + return heldShot(5, 0, 1, 115, 45, 0, duration, 'toonGroupHighShot') + + +def suitGroupShot(avatar, duration): + return heldShot(10, 0, 10, 65, -30, 0, duration, 'suitGroupShot') + + +def suitGroupLowLeftShot(avatar, duration): + return heldShot(8.4, -3.85, 2.75, 36.3, 3.25, 0, duration, 'suitGroupLowLeftShot') + + +def suitGroupThreeQuarterLeftBehindShot(avatar, duration): + if random.random() > 0.5: + x = 12.37 + h = 134.61 + else: + x = -12.37 + h = -134.61 + return heldShot(x, 11.5, 8.16, h, -22.7, 0, duration, 'suitGroupThreeQuarterLeftBehindShot') + + +def suitWakeUpShot(avatar, duration): + return heldShot(10, -5, 10, 65, -30, 0, duration, 'suitWakeUpShot') + + +def suitCameraShakeShot(avatar, duration, shakeIntensity, quake = 0): + track = Sequence(name='suitShakeCameraShot') + if quake == 1: + shakeDelay = 1.1 + numShakes = 4 + else: + shakeDelay = 0.3 + numShakes = 5 + postShakeDelay = 0.5 + shakeTime = (duration - shakeDelay - postShakeDelay) / numShakes + shakeDuration = shakeTime * (1.0 / numShakes) + shakeWaitInterval = shakeTime * ((numShakes - 1.0) / numShakes) + + def shakeCameraTrack(intensity, shakeWaitInterval = shakeWaitInterval, quake = quake, shakeDuration = shakeDuration, numShakes = numShakes): + vertShakeTrack = Sequence(Wait(shakeWaitInterval), Func(camera.setZ, camera.getZ() + intensity / 2), Wait(shakeDuration / 2), Func(camera.setZ, camera.getZ() - intensity), Wait(shakeDuration / 2), Func(camera.setZ, camera.getZ() + intensity / 2)) + horizShakeTrack = Sequence(Wait(shakeWaitInterval - shakeDuration / 2), Func(camera.setY, camera.getY() + intensity / 4), Wait(shakeDuration / 2), Func(camera.setY, camera.getY() - intensity / 2), Wait(shakeDuration / 2), Func(camera.setY, camera.getY() + intensity / 4), Wait(shakeDuration / 2), Func(camera.lookAt, Point3(0, 0, 0))) + shakeTrack = Sequence() + for i in xrange(0, numShakes): + if quake == 0: + shakeTrack.append(vertShakeTrack) + else: + shakeTrack.append(Parallel(vertShakeTrack, horizShakeTrack)) + + return shakeTrack + + x = 10 + random.random() * 3 + if random.random() > 0.5: + x = -x + z = 7 + random.random() * 3 + track.append(Func(camera.setPos, x, -5, z)) + track.append(Func(camera.lookAt, Point3(0, 0, 0))) + track.append(Wait(shakeDelay)) + track.append(shakeCameraTrack(shakeIntensity)) + track.append(Wait(postShakeDelay)) + return track + + +def avatarCloseUpShot(avatar, duration): + return heldRelativeShot(avatar, 0, 8, avatar.getHeight() * 0.66, 179, 15, 0, duration, 'avatarCloseUpShot') + + +def avatarCloseUpThrowShot(avatar, duration): + return heldRelativeShot(avatar, 3, 8, avatar.getHeight() * 0.66, 159, 3.6, 0, duration, 'avatarCloseUpThrowShot') + + +def avatarCloseUpThreeQuarterRightShot(avatar, duration): + return heldRelativeShot(avatar, 5.2, 5.45, avatar.getHeight() * 0.66, 131.5, 3.6, 0, duration, 'avatarCloseUpThreeQuarterRightShot') + + +def avatarCloseUpThreeQuarterRightShotWide(avatar, duration): + return heldRelativeShot(avatar, 7.2, 8.45, avatar.getHeight() * 0.66, 131.5, 3.6, 0, duration, 'avatarCloseUpThreeQuarterRightShot') + + +def avatarCloseUpThreeQuarterLeftShot(avatar, duration): + return heldRelativeShot(avatar, -5.2, 5.45, avatar.getHeight() * 0.66, -131.5, 3.6, 0, duration, 'avatarCloseUpThreeQuarterLeftShot') + + +def avatarCloseUpThreeQuarterRightFollowShot(avatar, duration): + track = Sequence(name='avatarCloseUpThreeQuarterRightFollowShot') + track.append(heldRelativeShot(avatar, 5.2, 5.45, avatar.getHeight() * 0.66, 131.5, 3.6, 0, duration * 0.65)) + track.append(LerpHprInterval(nodePath=camera, other=avatar, duration=duration * 0.2, hpr=Point3(110, 3.6, 0), blendType='easeInOut')) + track.append(Wait(duration * 0.25)) + return track + + +def avatarCloseUpZoomShot(avatar, duration): + track = Sequence('avatarCloseUpZoomShot') + track.append(LerpPosHprInterval(nodePath=camera, other=avatar, duration=duration / 2, startPos=Point3(0, 10, avatar.getHeight()), startHpr=Point3(179, -10, 0), pos=Point3(0, 6, avatar.getHeight()), hpr=Point3(179, -10, 0), blendType='easeInOut')) + track.append(Wait(duration / 2)) + return track + + +def avatarBehindShot(avatar, duration): + return heldRelativeShot(avatar, 5, -7, avatar.getHeight(), 40, -12, 0, duration, 'avatarBehindShot') + + +def avatarBehindHighShot(avatar, duration): + return heldRelativeShot(avatar, -4, -7, 5 + avatar.getHeight(), -30, -35, 0, duration, 'avatarBehindHighShot') + + +def avatarBehindHighRightShot(avatar, duration): + return heldRelativeShot(avatar, 4, -7, 5 + avatar.getHeight(), 30, -35, 0, duration, 'avatarBehindHighShot') + + +def avatarBehindThreeQuarterRightShot(avatar, duration): + return heldRelativeShot(avatar, 7.67, -8.52, avatar.getHeight() * 0.66, 25, 7.5, 0, duration, 'avatarBehindThreeQuarterRightShot') + + +def avatarSideFollowAttack(suit, toon, duration, battle): + windupDuration = duration * (0.1 + random.random() * 0.1) + projectDuration = duration * 0.75 + impactDuration = duration - windupDuration - projectDuration + suitHeight = suit.getHeight() + toonHeight = toon.getHeight() + suitCentralPoint = suit.getPos(battle) + suitCentralPoint.setZ(suitCentralPoint.getZ() + suitHeight * 0.75) + toonCentralPoint = toon.getPos(battle) + toonCentralPoint.setZ(toonCentralPoint.getZ() + toonHeight * 0.75) + initialX = random.randint(12, 14) + finalX = random.randint(7, 8) + initialY = finalY = random.randint(-3, 0) + initialZ = suitHeight * 0.5 + random.random() * suitHeight + finalZ = toonHeight * 0.5 + random.random() * toonHeight + if random.random() > 0.5: + initialX = -initialX + finalX = -finalX + return Sequence(focusShot(initialX, initialY, initialZ, windupDuration, suitCentralPoint), focusMoveShot(finalX, finalY, finalZ, projectDuration, toonCentralPoint), Wait(impactDuration)) + + +def focusCameraBetweenPoints(point1, point2): + if point1[0] > point2[0]: + x = point2[0] + (point1[0] - point2[0]) * 0.5 + else: + x = point1[0] + (point2[0] - point1[0]) * 0.5 + if point1[1] > point2[1]: + y = point2[1] + (point1[1] - point2[1]) * 0.5 + else: + y = point1[1] + (point2[1] - point1[1]) * 0.5 + if point1[2] > point2[2]: + z = point2[2] + (point1[2] - point2[2]) * 0.5 + else: + z = point1[2] + (point2[2] - point1[2]) * 0.5 + camera.lookAt(Point3(x, y, z)) + + +def randomCamera(suit, toon, battle, attackDuration, openShotDuration): + return randomAttackCam(suit, toon, battle, attackDuration, openShotDuration, 'suit') + + +def randomAttackCam(suit, toon, battle, attackDuration, openShotDuration, attackerString = 'suit'): + if openShotDuration > attackDuration: + openShotDuration = attackDuration + closeShotDuration = attackDuration - openShotDuration + if attackerString == 'suit': + attacker = suit + defender = toon + defenderString = 'toon' + else: + attacker = toon + defender = suit + defenderString = 'suit' + randomDouble = random.random() + if randomDouble > 0.6: + openShot = randomActorShot(attacker, battle, openShotDuration, attackerString) + elif randomDouble > 0.2: + openShot = randomOverShoulderShot(suit, toon, battle, openShotDuration, focus=attackerString) + else: + openShot = randomSplitShot(attacker, defender, battle, openShotDuration) + randomDouble = random.random() + if randomDouble > 0.6: + closeShot = randomActorShot(defender, battle, closeShotDuration, defenderString) + elif randomDouble > 0.2: + closeShot = randomOverShoulderShot(suit, toon, battle, closeShotDuration, focus=defenderString) + else: + closeShot = randomSplitShot(attacker, defender, battle, closeShotDuration) + return Sequence(openShot, closeShot) + + +def randomGroupAttackCam(suit, targets, battle, attackDuration, openShotDuration): + if openShotDuration > attackDuration: + openShotDuration = attackDuration + closeShotDuration = attackDuration - openShotDuration + openShot = randomActorShot(suit, battle, openShotDuration, 'suit', groupShot=0) + closeShot = randomToonGroupShot(targets, suit, closeShotDuration, battle) + return Sequence(openShot, closeShot) + + +def randomActorShot(actor, battle, duration, actorType, groupShot = 0): + height = actor.getHeight() + centralPoint = actor.getPos(battle) + centralPoint.setZ(centralPoint.getZ() + height * 0.75) + if actorType == 'suit': + x = 4 + random.random() * 8 + y = -2 - random.random() * 4 + z = height * 0.5 + random.random() * height * 1.5 + if groupShot == 1: + y = -4 + z = height * 0.5 + else: + x = 2 + random.random() * 8 + y = -2 + random.random() * 3 + z = height + random.random() * height * 1.5 + if groupShot == 1: + y = y + 3 + z = height * 0.5 + if MovieUtil.shotDirection == 'left': + x = -x + return focusShot(x, y, z, duration, centralPoint) + + +def randomSplitShot(suit, toon, battle, duration): + suitHeight = suit.getHeight() + toonHeight = toon.getHeight() + suitCentralPoint = suit.getPos(battle) + suitCentralPoint.setZ(suitCentralPoint.getZ() + suitHeight * 0.75) + toonCentralPoint = toon.getPos(battle) + toonCentralPoint.setZ(toonCentralPoint.getZ() + toonHeight * 0.75) + x = 9 + random.random() * 2 + y = -2 - random.random() * 2 + z = suitHeight * 0.5 + random.random() * suitHeight + if MovieUtil.shotDirection == 'left': + x = -x + return focusShot(x, y, z, duration, toonCentralPoint, splitFocusPoint=suitCentralPoint) + + +def randomOverShoulderShot(suit, toon, battle, duration, focus): + suitHeight = suit.getHeight() + toonHeight = toon.getHeight() + suitCentralPoint = suit.getPos(battle) + suitCentralPoint.setZ(suitCentralPoint.getZ() + suitHeight * 0.75) + toonCentralPoint = toon.getPos(battle) + toonCentralPoint.setZ(toonCentralPoint.getZ() + toonHeight * 0.75) + x = 2 + random.random() * 10 + if focus == 'toon': + y = 8 + random.random() * 6 + z = suitHeight * 1.2 + random.random() * suitHeight + else: + y = -10 - random.random() * 6 + z = toonHeight * 1.5 + if MovieUtil.shotDirection == 'left': + x = -x + return focusShot(x, y, z, duration, toonCentralPoint, splitFocusPoint=suitCentralPoint) + + +def randomToonGroupShot(toons, suit, duration, battle): + sum = 0 + for t in toons: + toon = t['toon'] + height = toon.getHeight() + sum = sum + height + + avgHeight = sum / len(toons) * 0.75 + suitPos = suit.getPos(battle) + x = 1 + random.random() * 6 + if suitPos.getX() > 0: + x = -x + if random.random() > 0.5: + y = 4 + random.random() * 1 + z = avgHeight + random.random() * 6 + else: + y = 11 + random.random() * 2 + z = 13 + random.random() * 2 + focalPoint = Point3(0, -4, avgHeight) + return focusShot(x, y, z, duration, focalPoint) + + +def chooseFireShot(throws, suitThrowsDict, attackDuration): + openShot = chooseFireOpenShot(throws, suitThrowsDict, attackDuration) + openDuration = openShot.getDuration() + openName = openShot.getName() + closeShot = chooseFireCloseShot(throws, suitThrowsDict, openDuration, openName, attackDuration) + track = Sequence(openShot, closeShot) + return track + + +def chooseFireOpenShot(throws, suitThrowsDict, attackDuration): + numThrows = len(throws) + av = None + duration = 3.0 + if numThrows == 1: + av = throws[0]['toon'] + shotChoices = [avatarCloseUpThrowShot, + avatarCloseUpThreeQuarterRightShot, + avatarBehindShot, + allGroupLowShot, + suitGroupThreeQuarterLeftBehindShot] + elif numThrows >= 2 and numThrows <= 4: + shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot] + else: + notify.error('Bad number of throws: %s' % numThrows) + shotChoice = random.choice(shotChoices) + track = apply(shotChoice, [av, duration]) + print 'chooseFireOpenShot %s' % shotChoice + return track + + +def chooseFireCloseShot(throws, suitThrowsDict, openDuration, openName, attackDuration): + numSuits = len(suitThrowsDict) + av = None + duration = attackDuration - openDuration + if numSuits == 1: + av = base.cr.doId2do[suitThrowsDict.keys()[0]] + shotChoices = [avatarCloseUpFireShot, + avatarCloseUpThreeQuarterLeftFireShot, + allGroupLowShot, + suitGroupThreeQuarterLeftBehindShot] + elif numSuits >= 2 and numSuits <= 4 or numSuits == 0: + shotChoices = [allGroupLowShot, suitGroupThreeQuarterLeftBehindShot] + else: + notify.error('Bad number of suits: %s' % numSuits) + shotChoice = random.choice(shotChoices) + track = apply(shotChoice, [av, duration]) + print 'chooseFireOpenShot %s' % shotChoice + return track + + +def avatarCloseUpFireShot(avatar, duration): + return heldRelativeShot(avatar, 7, 17, avatar.getHeight() * 0.66, 159, 3.6, 0, duration, 'avatarCloseUpFireShot') + + +def avatarCloseUpThreeQuarterLeftFireShot(avatar, duration): + return heldRelativeShot(avatar, -8.2, 8.45, avatar.getHeight() * 0.66, -131.5, 3.6, 0, duration, 'avatarCloseUpThreeQuarterLeftShot') diff --git a/toontown/battle/MovieDrop.py b/toontown/battle/MovieDrop.py new file mode 100755 index 00000000..f1e13d61 --- /dev/null +++ b/toontown/battle/MovieDrop.py @@ -0,0 +1,435 @@ +from direct.interval.IntervalGlobal import * +from BattleBase import * +from BattleProps import * +from BattleSounds import * +import MovieCamera +from direct.directnotify import DirectNotifyGlobal +import MovieUtil +import MovieNPCSOS +from MovieUtil import calcAvgSuitPos +from direct.showutil import Effects +notify = DirectNotifyGlobal.directNotify.newCategory('MovieDrop') +hitSoundFiles = ('AA_drop_flowerpot.ogg', 'AA_drop_sandbag.ogg', 'AA_drop_anvil.ogg', 'AA_drop_bigweight.ogg', 'AA_drop_safe.ogg', 'AA_drop_piano.ogg', 'AA_drop_boat.ogg') +missSoundFiles = ('AA_drop_flowerpot_miss.ogg', 'AA_drop_sandbag_miss.ogg', 'AA_drop_anvil_miss.ogg', 'AA_drop_bigweight_miss.ogg', 'AA_drop_safe_miss.ogg', 'AA_drop_piano_miss.ogg', 'AA_drop_boat_miss.ogg') +tDropShadow = 1.3 +tSuitDodges = 2.45 + tDropShadow +tObjectAppears = 3.0 + tDropShadow +tButtonPressed = 2.44 +dShrink = 0.3 +dShrinkOnMiss = 0.1 +dPropFall = 0.6 +objects = ('flowerpot', 'sandbag', 'anvil', 'weight', 'safe', 'piano', 'ship') +objZOffsets = (0.75, 0.75, 0.0, 0.0, 0.0, 0.0, 0.0) +objStartingScales = (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0) +landFrames = (12, 4, 1, 11, 11, 11, 2) +shoulderHeights = {'a': 13.28 / 4.0, + 'b': 13.74 / 4.0, + 'c': 10.02 / 4.0} + +def doDrops(drops): + if len(drops) == 0: + return (None, None) + npcArrivals, npcDepartures, npcs = MovieNPCSOS.doNPCTeleports(drops) + suitDropsDict = {} + groupDrops = [] + for drop in drops: + track = drop['track'] + level = drop['level'] + targets = drop['target'] + if len(targets) == 1: + suitId = targets[0]['suit'].doId + if suitId in suitDropsDict: + suitDropsDict[suitId].append((drop, targets[0])) + else: + suitDropsDict[suitId] = [(drop, targets[0])] + elif level <= MAX_LEVEL_INDEX and attackAffectsGroup(track, level): + groupDrops.append(drop) + else: + for target in targets: + suitId = target['suit'].doId + if suitId in suitDropsDict: + otherDrops = suitDropsDict[suitId] + alreadyInList = 0 + for oDrop in otherDrops: + if oDrop[0]['toon'] == drop['toon']: + alreadyInList = 1 + + if alreadyInList == 0: + suitDropsDict[suitId].append((drop, target)) + else: + suitDropsDict[suitId] = [(drop, target)] + + suitDrops = suitDropsDict.values() + + def compFunc(a, b): + if len(a) > len(b): + return 1 + elif len(a) < len(b): + return -1 + return 0 + + suitDrops.sort(compFunc) + delay = 0.0 + mtrack = Parallel(name='toplevel-drop') + npcDrops = {} + for st in suitDrops: + if len(st) > 0: + ival = __doSuitDrops(st, npcs, npcDrops) + if ival: + mtrack.append(Sequence(Wait(delay), ival)) + delay = delay + TOON_DROP_SUIT_DELAY + + dropTrack = Sequence(npcArrivals, mtrack, npcDepartures) + camDuration = mtrack.getDuration() + if groupDrops: + ival = __doGroupDrops(groupDrops) + dropTrack.append(ival) + camDuration += ival.getDuration() + enterDuration = npcArrivals.getDuration() + exitDuration = npcDepartures.getDuration() + camTrack = MovieCamera.chooseDropShot(drops, suitDropsDict, camDuration, enterDuration, exitDuration) + return (dropTrack, camTrack) + + +def __getSoundTrack(level, hitSuit, node = None): + if hitSuit: + soundEffect = globalBattleSoundCache.getSound(hitSoundFiles[level]) + else: + soundEffect = globalBattleSoundCache.getSound(missSoundFiles[level]) + soundTrack = Sequence() + if soundEffect: + buttonSound = globalBattleSoundCache.getSound('AA_drop_trigger_box.ogg') + fallingSound = None + buttonDelay = tButtonPressed - 0.3 + fallingDuration = 1.5 + if not level == UBER_GAG_LEVEL_INDEX: + fallingSound = globalBattleSoundCache.getSound('incoming_whistleALT.ogg') + soundTrack.append(Wait(buttonDelay)) + soundTrack.append(SoundInterval(buttonSound, duration=0.67, node=node)) + if fallingSound: + soundTrack.append(SoundInterval(fallingSound, duration=fallingDuration, node=node)) + if not level == UBER_GAG_LEVEL_INDEX: + soundTrack.append(SoundInterval(soundEffect, node=node)) + if level == UBER_GAG_LEVEL_INDEX: + if hitSuit: + uberDelay = tButtonPressed + else: + uberDelay = tButtonPressed - 0.1 + oldSoundTrack = soundTrack + soundTrack = Parallel() + soundTrack.append(oldSoundTrack) + uberTrack = Sequence() + uberTrack.append(Wait(uberDelay)) + uberTrack.append(SoundInterval(soundEffect, node=node)) + soundTrack.append(uberTrack) + else: + soundTrack.append(Wait(0.1)) + return soundTrack + + +def __doSuitDrops(dropTargetPairs, npcs, npcDrops): + toonTracks = Parallel() + delay = 0.0 + alreadyDodged = 0 + alreadyTeased = 0 + for dropTargetPair in dropTargetPairs: + drop = dropTargetPair[0] + level = drop['level'] + objName = objects[level] + target = dropTargetPair[1] + track = __dropObjectForSingle(drop, delay, objName, level, alreadyDodged, alreadyTeased, npcs, target, npcDrops) + if track: + toonTracks.append(track) + delay += TOON_DROP_DELAY + hp = target['hp'] + if hp <= 0: + if level >= 3: + alreadyTeased = 1 + else: + alreadyDodged = 1 + + return toonTracks + + +def __doGroupDrops(groupDrops): + toonTracks = Parallel() + delay = 0.0 + alreadyDodged = 0 + alreadyTeased = 0 + for drop in groupDrops: + battle = drop['battle'] + level = drop['level'] + centerPos = calcAvgSuitPos(drop) + targets = drop['target'] + numTargets = len(targets) + closestTarget = -1 + nearestDistance = 100000.0 + for i in xrange(numTargets): + suit = drop['target'][i]['suit'] + suitPos = suit.getPos(battle) + displacement = Vec3(centerPos) + displacement -= suitPos + distance = displacement.lengthSquared() + if distance < nearestDistance: + closestTarget = i + nearestDistance = distance + + track = __dropGroupObject(drop, delay, closestTarget, alreadyDodged, alreadyTeased) + if track: + toonTracks.append(track) + delay = delay + TOON_DROP_SUIT_DELAY + hp = drop['target'][closestTarget]['hp'] + if hp <= 0: + if level >= 3: + alreadyTeased = 1 + else: + alreadyDodged = 1 + + return toonTracks + + +def __dropGroupObject(drop, delay, closestTarget, alreadyDodged, alreadyTeased): + level = drop['level'] + objName = objects[level] + target = drop['target'][closestTarget] + suit = drop['target'][closestTarget]['suit'] + npcDrops = {} + npcs = [] + returnedParallel = __dropObject(drop, delay, objName, level, alreadyDodged, alreadyTeased, npcs, target, npcDrops) + for i in xrange(len(drop['target'])): + target = drop['target'][i] + suitTrack = __createSuitTrack(drop, delay, level, alreadyDodged, alreadyTeased, target, npcs) + if suitTrack: + returnedParallel.append(suitTrack) + + return returnedParallel + + +def __dropObjectForSingle(drop, delay, objName, level, alreadyDodged, alreadyTeased, npcs, target, npcDrops): + singleDropParallel = __dropObject(drop, delay, objName, level, alreadyDodged, alreadyTeased, npcs, target, npcDrops) + suitTrack = __createSuitTrack(drop, delay, level, alreadyDodged, alreadyTeased, target, npcs) + if suitTrack: + singleDropParallel.append(suitTrack) + return singleDropParallel + + +def __dropObject(drop, delay, objName, level, alreadyDodged, alreadyTeased, npcs, target, npcDrops): + toon = drop['toon'] + repeatNPC = 0 + battle = drop['battle'] + if 'npc' in drop: + toon = drop['npc'] + if toon in npcDrops: + repeatNPC = 1 + else: + npcDrops[toon] = 1 + origHpr = Vec3(0, 0, 0) + else: + origHpr = toon.getHpr(battle) + hpbonus = drop['hpbonus'] + suit = target['suit'] + hp = target['hp'] + hitSuit = hp > 0 + died = target['died'] + leftSuits = target['leftSuits'] + rightSuits = target['rightSuits'] + kbbonus = target['kbbonus'] + suitPos = suit.getPos(battle) + majorObject = level >= 3 + if repeatNPC == 0: + button = globalPropPool.getProp('button') + buttonType = globalPropPool.getPropType('button') + button2 = MovieUtil.copyProp(button) + buttons = [button, button2] + hands = toon.getLeftHands() + object = globalPropPool.getProp(objName) + objectType = globalPropPool.getPropType(objName) + if objName == 'weight': + object.setScale(object.getScale() * 0.75) + elif objName == 'safe': + object.setScale(object.getScale() * 0.85) + node = object.node() + node.setBounds(OmniBoundingVolume()) + node.setFinal(1) + soundTrack = __getSoundTrack(level, hitSuit, toon) + toonTrack = Sequence() + if repeatNPC == 0: + toonFace = Func(toon.headsUp, battle, suitPos) + toonTrack.append(Wait(delay)) + toonTrack.append(toonFace) + toonTrack.append(ActorInterval(toon, 'pushbutton')) + toonTrack.append(Func(toon.loop, 'neutral')) + toonTrack.append(Func(toon.setHpr, battle, origHpr)) + buttonTrack = Sequence() + if repeatNPC == 0: + buttonShow = Func(MovieUtil.showProps, buttons, hands) + buttonScaleUp = LerpScaleInterval(button, 1.0, button.getScale(), startScale=Point3(0.01, 0.01, 0.01)) + buttonScaleDown = LerpScaleInterval(button, 1.0, Point3(0.01, 0.01, 0.01), startScale=button.getScale()) + buttonHide = Func(MovieUtil.removeProps, buttons) + buttonTrack.append(Wait(delay)) + buttonTrack.append(buttonShow) + buttonTrack.append(buttonScaleUp) + buttonTrack.append(Wait(2.5)) + buttonTrack.append(buttonScaleDown) + buttonTrack.append(buttonHide) + objectTrack = Sequence() + + def posObject(object, suit, level, majorObject, miss, battle = battle): + object.reparentTo(battle) + if battle.isSuitLured(suit): + suitPos, suitHpr = battle.getActorPosHpr(suit) + object.setPos(suitPos) + object.setHpr(suitHpr) + if level >= 3: + object.setY(object.getY() + 2) + else: + object.setPos(suit.getPos(battle)) + object.setHpr(suit.getHpr(battle)) + if miss and level >= 3: + object.setY(object.getY(battle) + 5) + if not majorObject: + if not miss: + shoulderHeight = shoulderHeights[suit.style.body] * suit.scale + object.setZ(object.getPos(battle)[2] + shoulderHeight) + object.setZ(object.getPos(battle)[2] + objZOffsets[level]) + + objectTrack.append(Func(battle.movie.needRestoreRenderProp, object)) + objInit = Func(posObject, object, suit, level, majorObject, hp <= 0) + objectTrack.append(Wait(delay + tObjectAppears)) + objectTrack.append(objInit) + if hp > 0 or level == 1 or level == 2: + if hasattr(object, 'getAnimControls'): + animProp = ActorInterval(object, objName) + shrinkProp = LerpScaleInterval(object, dShrink, Point3(0.01, 0.01, 0.01), startScale=object.getScale()) + objAnimShrink = ParallelEndTogether(animProp, shrinkProp) + objectTrack.append(objAnimShrink) + else: + startingScale = objStartingScales[level] + object2 = MovieUtil.copyProp(object) + posObject(object2, suit, level, majorObject, hp <= 0) + endingPos = object2.getPos() + startPos = Point3(endingPos[0], endingPos[1], endingPos[2] + 5) + startHpr = object2.getHpr() + endHpr = Point3(startHpr[0] + 90, startHpr[1], startHpr[2]) + animProp = LerpPosInterval(object, landFrames[level] / 24.0, endingPos, startPos=startPos) + shrinkProp = LerpScaleInterval(object, dShrink, Point3(0.01, 0.01, 0.01), startScale=startingScale) + bounceProp = Effects.createZBounce(object, 2, endingPos, 0.5, 1.5) + objAnimShrink = Sequence(Func(object.setScale, startingScale), Func(object.setH, endHpr[0]), animProp, bounceProp, Wait(1.5), shrinkProp) + objectTrack.append(objAnimShrink) + MovieUtil.removeProp(object2) + elif hasattr(object, 'getAnimControls'): + animProp = ActorInterval(object, objName, duration=landFrames[level] / 24.0) + + def poseProp(prop, animName, level): + prop.pose(animName, landFrames[level]) + + poseProp = Func(poseProp, object, objName, level) + wait = Wait(1.0) + shrinkProp = LerpScaleInterval(object, dShrinkOnMiss, Point3(0.01, 0.01, 0.01), startScale=object.getScale()) + objectTrack.append(animProp) + objectTrack.append(poseProp) + objectTrack.append(wait) + objectTrack.append(shrinkProp) + else: + startingScale = objStartingScales[level] + object2 = MovieUtil.copyProp(object) + posObject(object2, suit, level, majorObject, hp <= 0) + endingPos = object2.getPos() + startPos = Point3(endingPos[0], endingPos[1], endingPos[2] + 5) + startHpr = object2.getHpr() + endHpr = Point3(startHpr[0] + 90, startHpr[1], startHpr[2]) + animProp = LerpPosInterval(object, landFrames[level] / 24.0, endingPos, startPos=startPos) + shrinkProp = LerpScaleInterval(object, dShrinkOnMiss, Point3(0.01, 0.01, 0.01), startScale=startingScale) + bounceProp = Effects.createZBounce(object, 2, endingPos, 0.5, 1.5) + objAnimShrink = Sequence(Func(object.setScale, startingScale), Func(object.setH, endHpr[0]), animProp, bounceProp, Wait(1.5), shrinkProp) + objectTrack.append(objAnimShrink) + MovieUtil.removeProp(object2) + objectTrack.append(Func(MovieUtil.removeProp, object)) + objectTrack.append(Func(battle.movie.clearRenderProp, object)) + dropShadow = MovieUtil.copyProp(suit.getShadowJoint()) + if level == 0: + dropShadow.setScale(0.5) + elif level <= 2: + dropShadow.setScale(0.8) + elif level == 3: + dropShadow.setScale(2.0) + elif level == 4: + dropShadow.setScale(2.3) + else: + dropShadow.setScale(3.6) + + def posShadow(dropShadow = dropShadow, suit = suit, battle = battle, hp = hp, level = level): + dropShadow.reparentTo(battle) + if battle.isSuitLured(suit): + suitPos, suitHpr = battle.getActorPosHpr(suit) + dropShadow.setPos(suitPos) + dropShadow.setHpr(suitHpr) + if level >= 3: + dropShadow.setY(dropShadow.getY() + 2) + else: + dropShadow.setPos(suit.getPos(battle)) + dropShadow.setHpr(suit.getHpr(battle)) + if hp <= 0 and level >= 3: + dropShadow.setY(dropShadow.getY(battle) + 5) + dropShadow.setZ(dropShadow.getZ() + 0.5) + + shadowTrack = Sequence(Wait(delay + tButtonPressed), Func(battle.movie.needRestoreRenderProp, dropShadow), Func(posShadow), LerpScaleInterval(dropShadow, tObjectAppears - tButtonPressed, dropShadow.getScale(), startScale=Point3(0.01, 0.01, 0.01)), Wait(0.3), Func(MovieUtil.removeProp, dropShadow), Func(battle.movie.clearRenderProp, dropShadow)) + return Parallel(toonTrack, soundTrack, buttonTrack, objectTrack, shadowTrack) + + +def __createSuitTrack(drop, delay, level, alreadyDodged, alreadyTeased, target, npcs): + toon = drop['toon'] + if 'npc' in drop: + toon = drop['npc'] + battle = drop['battle'] + majorObject = level >= 3 + suit = target['suit'] + hp = target['hp'] + hitSuit = hp > 0 + died = target['died'] + revived = target['revived'] + leftSuits = target['leftSuits'] + rightSuits = target['rightSuits'] + kbbonus = target['kbbonus'] + hpbonus = drop['hpbonus'] + if hp > 0: + suitTrack = Sequence() + showDamage = Func(suit.showHpText, -hp, openEnded=0) + updateHealthBar = Func(suit.updateHealthBar, hp) + if majorObject: + anim = 'flatten' + else: + anim = 'drop-react' + suitReact = ActorInterval(suit, anim) + suitTrack.append(Wait(delay + tObjectAppears)) + suitTrack.append(showDamage) + suitTrack.append(updateHealthBar) + suitGettingHit = Parallel(suitReact) + if level == UBER_GAG_LEVEL_INDEX: + gotHitSound = globalBattleSoundCache.getSound('AA_drop_boat_cog.ogg') + suitGettingHit.append(SoundInterval(gotHitSound, node=toon)) + suitTrack.append(suitGettingHit) + bonusTrack = None + if hpbonus > 0: + bonusTrack = Sequence(Wait(delay + tObjectAppears + 0.75), Func(suit.showHpText, -hpbonus, 1, openEnded=0), Func(suit.updateHealthBar, hpbonus)) + if revived != 0: + suitTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle, npcs)) + elif died != 0: + suitTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle, npcs)) + else: + suitTrack.append(Func(suit.loop, 'neutral')) + if bonusTrack != None: + suitTrack = Parallel(suitTrack, bonusTrack) + elif kbbonus == 0: + suitTrack = Sequence(Wait(delay + tObjectAppears), Func(MovieUtil.indicateMissed, suit, 0.6), Func(suit.loop, 'neutral')) + else: + if alreadyDodged > 0: + return + if level >= 3: + if alreadyTeased > 0: + return + else: + suitTrack = MovieUtil.createSuitTeaseMultiTrack(suit, delay=delay + tObjectAppears) + else: + suitTrack = MovieUtil.createSuitDodgeMultitrack(delay + tSuitDodges, suit, leftSuits, rightSuits) + return suitTrack diff --git a/toontown/battle/MovieFire.py b/toontown/battle/MovieFire.py new file mode 100755 index 00000000..17f6ad9e --- /dev/null +++ b/toontown/battle/MovieFire.py @@ -0,0 +1,345 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from BattleBase import * +from BattleProps import * +from BattleSounds import * +from toontown.toon.ToonDNA import * +from toontown.toonbase import TTLocalizer +from toontown.suit.SuitDNA import * +from direct.directnotify import DirectNotifyGlobal +import random +import MovieCamera +import MovieUtil +from MovieUtil import calcAvgSuitPos +notify = DirectNotifyGlobal.directNotify.newCategory('MovieThrow') +hitSoundFiles = ('AA_tart_only.ogg', 'AA_slice_only.ogg', 'AA_slice_only.ogg', 'AA_slice_only.ogg', 'AA_slice_only.ogg', 'AA_wholepie_only.ogg', 'AA_wholepie_only.ogg') +tPieLeavesHand = 2.7 +tPieHitsSuit = 3.0 +tSuitDodges = 2.45 +ratioMissToHit = 1.5 +tPieShrink = 0.7 +pieFlyTaskName = 'MovieThrow-pieFly' + +def addHit(dict, suitId, hitCount): + if suitId in dict: + dict[suitId] += hitCount + else: + dict[suitId] = hitCount + + +def doFires(fires): + if len(fires) == 0: + return (None, None) + + suitFiresDict = {} + for fire in fires: + suitId = fire['target']['suit'].doId + if suitId in suitFiresDict: + suitFiresDict[suitId].append(fire) + else: + suitFiresDict[suitId] = [fire] + + suitFires = suitFiresDict.values() + def compFunc(a, b): + if len(a) > len(b): + return 1 + elif len(a) < len(b): + return -1 + return 0 + suitFires.sort(compFunc) + + totalHitDict = {} + singleHitDict = {} + groupHitDict = {} + + for fire in fires: + suitId = fire['target']['suit'].doId + if 1: + if fire['target']['hp'] > 0: + addHit(singleHitDict, suitId, 1) + addHit(totalHitDict, suitId, 1) + else: + addHit(singleHitDict, suitId, 0) + addHit(totalHitDict, suitId, 0) + + notify.debug('singleHitDict = %s' % singleHitDict) + notify.debug('groupHitDict = %s' % groupHitDict) + notify.debug('totalHitDict = %s' % totalHitDict) + + delay = 0.0 + mtrack = Parallel() + firedTargets = [] + for sf in suitFires: + if len(sf) > 0: + ival = __doSuitFires(sf) + if ival: + mtrack.append(Sequence(Wait(delay), ival)) + delay = delay + TOON_FIRE_SUIT_DELAY + + retTrack = Sequence() + retTrack.append(mtrack) + camDuration = retTrack.getDuration() + camTrack = MovieCamera.chooseFireShot(fires, suitFiresDict, camDuration) + return (retTrack, camTrack) + +def __doSuitFires(fires): + toonTracks = Parallel() + delay = 0.0 + hitCount = 0 + for fire in fires: + if fire['target']['hp'] > 0: + hitCount += 1 + else: + break + + suitList = [] + for fire in fires: + if fire['target']['suit'] not in suitList: + suitList.append(fire['target']['suit']) + + for fire in fires: + showSuitCannon = 1 + if fire['target']['suit'] not in suitList: + showSuitCannon = 0 + else: + suitList.remove(fire['target']['suit']) + tracks = __throwPie(fire, delay, hitCount, showSuitCannon) + if tracks: + for track in tracks: + toonTracks.append(track) + + delay = delay + TOON_THROW_DELAY + + return toonTracks + + +def __showProp(prop, parent, pos): + prop.reparentTo(parent) + prop.setPos(pos) + + +def __animProp(props, propName, propType): + if 'actor' == propType: + for prop in props: + prop.play(propName) + + elif 'model' == propType: + pass + else: + notify.error('No such propType as: %s' % propType) + + +def __billboardProp(prop): + scale = prop.getScale() + prop.setBillboardPointWorld() + prop.setScale(scale) + + +def __suitMissPoint(suit, other = render): + pnt = suit.getPos(other) + pnt.setZ(pnt[2] + suit.getHeight() * 1.3) + return pnt + + +def __propPreflight(props, suit, toon, battle): + prop = props[0] + toon.update(0) + prop.wrtReparentTo(battle) + props[1].reparentTo(hidden) + for ci in xrange(prop.getNumChildren()): + prop.getChild(ci).setHpr(0, -90, 0) + + targetPnt = MovieUtil.avatarFacePoint(suit, other=battle) + prop.lookAt(targetPnt) + + +def __propPreflightGroup(props, suits, toon, battle): + prop = props[0] + toon.update(0) + prop.wrtReparentTo(battle) + props[1].reparentTo(hidden) + for ci in xrange(prop.getNumChildren()): + prop.getChild(ci).setHpr(0, -90, 0) + + avgTargetPt = Point3(0, 0, 0) + for suit in suits: + avgTargetPt += MovieUtil.avatarFacePoint(suit, other=battle) + + avgTargetPt /= len(suits) + prop.lookAt(avgTargetPt) + + +def __piePreMiss(missDict, pie, suitPoint, other = render): + missDict['pie'] = pie + missDict['startScale'] = pie.getScale() + missDict['startPos'] = pie.getPos(other) + v = Vec3(suitPoint - missDict['startPos']) + endPos = missDict['startPos'] + v * ratioMissToHit + missDict['endPos'] = endPos + + +def __pieMissLerpCallback(t, missDict): + pie = missDict['pie'] + newPos = missDict['startPos'] * (1.0 - t) + missDict['endPos'] * t + if t < tPieShrink: + tScale = 0.0001 + else: + tScale = (t - tPieShrink) / (1.0 - tPieShrink) + newScale = missDict['startScale'] * max(1.0 - tScale, 0.01) + pie.setPos(newPos) + pie.setScale(newScale) + + +def __piePreMissGroup(missDict, pies, suitPoint, other = render): + missDict['pies'] = pies + missDict['startScale'] = pies[0].getScale() + missDict['startPos'] = pies[0].getPos(other) + v = Vec3(suitPoint - missDict['startPos']) + endPos = missDict['startPos'] + v * ratioMissToHit + missDict['endPos'] = endPos + notify.debug('startPos=%s' % missDict['startPos']) + notify.debug('v=%s' % v) + notify.debug('endPos=%s' % missDict['endPos']) + + +def __pieMissGroupLerpCallback(t, missDict): + pies = missDict['pies'] + newPos = missDict['startPos'] * (1.0 - t) + missDict['endPos'] * t + if t < tPieShrink: + tScale = 0.0001 + else: + tScale = (t - tPieShrink) / (1.0 - tPieShrink) + newScale = missDict['startScale'] * max(1.0 - tScale, 0.01) + for pie in pies: + pie.setPos(newPos) + pie.setScale(newScale) + + +def __getSoundTrack(level, hitSuit, node = None): + throwSound = globalBattleSoundCache.getSound('AA_drop_trigger_box.ogg') + throwTrack = Sequence(Wait(2.15), SoundInterval(throwSound, node=node)) + return throwTrack + + +def __throwPie(throw, delay, hitCount, showCannon = 1): + toon = throw['toon'] + hpbonus = throw['hpbonus'] + target = throw['target'] + suit = target['suit'] + hp = target['hp'] + kbbonus = target['kbbonus'] + sidestep = throw['sidestep'] + died = target['died'] + revived = target['revived'] + leftSuits = target['leftSuits'] + rightSuits = target['rightSuits'] + level = throw['level'] + battle = throw['battle'] + suitPos = suit.getPos(battle) + origHpr = toon.getHpr(battle) + notify.debug('toon: %s throws tart at suit: %d for hp: %d died: %d' % (toon.getName(), + suit.doId, + hp, + died)) + pieName = pieNames[0] + hitSuit = hp > 0 + button = globalPropPool.getProp('button') + buttonType = globalPropPool.getPropType('button') + button2 = MovieUtil.copyProp(button) + buttons = [button, button2] + hands = toon.getLeftHands() + toonTrack = Sequence() + toonFace = Func(toon.headsUp, battle, suitPos) + toonTrack.append(Wait(delay)) + toonTrack.append(toonFace) + toonTrack.append(ActorInterval(toon, 'pushbutton')) + if toon == base.localAvatar: + toonTrack.append(Func(base.talkAssistant.sendOpenTalk, TTLocalizer.FireTalkMessage)) + toonTrack.append(ActorInterval(toon, 'wave', duration=2.0)) + toonTrack.append(ActorInterval(toon, 'duck')) + toonTrack.append(Func(toon.loop, 'neutral')) + toonTrack.append(Func(toon.setHpr, battle, origHpr)) + buttonTrack = Sequence() + buttonShow = Func(MovieUtil.showProps, buttons, hands) + buttonScaleUp = LerpScaleInterval(button, 1.0, button.getScale(), startScale=Point3(0.01, 0.01, 0.01)) + buttonScaleDown = LerpScaleInterval(button, 1.0, Point3(0.01, 0.01, 0.01), startScale=button.getScale()) + buttonHide = Func(MovieUtil.removeProps, buttons) + buttonTrack.append(Wait(delay)) + buttonTrack.append(buttonShow) + buttonTrack.append(buttonScaleUp) + buttonTrack.append(Wait(2.5)) + buttonTrack.append(buttonScaleDown) + buttonTrack.append(buttonHide) + soundTrack = __getSoundTrack(level, hitSuit, toon) + suitResponseTrack = Sequence() + reactIval = Sequence() + if showCannon: + showDamage = Func(suit.showHpText, -hp, openEnded=0) + updateHealthBar = Func(suit.updateHealthBar, hp) + cannon = loader.loadModel('phase_4/models/minigames/toon_cannon') + barrel = cannon.find('**/cannon') + barrel.setHpr(0, 90, 0) + cannonHolder = render.attachNewNode('CannonHolder') + cannon.reparentTo(cannonHolder) + cannon.setPos(0, 0, -8.6) + cannonHolder.setPos(suit.getPos(render)) + cannonHolder.setHpr(suit.getHpr(render)) + cannonAttachPoint = barrel.attachNewNode('CannonAttach') + kapowAttachPoint = barrel.attachNewNode('kapowAttach') + scaleFactor = 1.6 + iScale = 1 / scaleFactor + barrel.setScale(scaleFactor, 1, scaleFactor) + cannonAttachPoint.setScale(iScale, 1, iScale) + cannonAttachPoint.setPos(0, 6.7, 0) + kapowAttachPoint.setPos(0, -0.5, 1.9) + suit.reparentTo(cannonAttachPoint) + suit.setPos(0, 0, 0) + suit.setHpr(0, -90, 0) + suitLevel = suit.getActualLevel() + deep = 2.5 + suitLevel * 0.2 + suitScale = 0.9 + import math + suitScale = 0.9 - math.sqrt(suitLevel) * 0.1 + sival = [] + posInit = cannonHolder.getPos() + posFinal = Point3(posInit[0] + 0.0, posInit[1] + 0.0, posInit[2] + 7.0) + kapow = globalPropPool.getProp('kapow') + kapow.reparentTo(kapowAttachPoint) + kapow.hide() + kapow.setScale(0.25) + kapow.setBillboardPointEye() + smoke = loader.loadModel('phase_4/models/props/test_clouds') + smoke.reparentTo(cannonAttachPoint) + smoke.setScale(0.5) + smoke.hide() + smoke.setBillboardPointEye() + soundBomb = base.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.ogg') + playSoundBomb = SoundInterval(soundBomb, node=cannonHolder) + soundFly = base.loadSfx('phase_4/audio/sfx/firework_whistle_01.ogg') + playSoundFly = SoundInterval(soundFly, node=cannonHolder) + soundCannonAdjust = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.ogg') + playSoundCannonAdjust = SoundInterval(soundCannonAdjust, duration=0.6, node=cannonHolder) + soundCogPanic = base.loadSfx('phase_5/audio/sfx/ENC_cogafssm.ogg') + playSoundCogPanic = SoundInterval(soundCogPanic, node=cannonHolder) + reactIval = Parallel(ActorInterval(suit, 'pie-small-react'), Sequence(Wait(0.0), LerpPosInterval(cannonHolder, 2.0, posFinal, startPos=posInit, blendType='easeInOut'), Parallel(LerpHprInterval(barrel, 0.6, Point3(0, 45, 0), startHpr=Point3(0, 90, 0), blendType='easeIn'), playSoundCannonAdjust), Wait(2.0), Parallel(LerpHprInterval(barrel, 0.6, Point3(0, 90, 0), startHpr=Point3(0, 45, 0), blendType='easeIn'), playSoundCannonAdjust), LerpPosInterval(cannonHolder, 1.0, posInit, startPos=posFinal, blendType='easeInOut')), Sequence(Wait(0.0), Parallel(ActorInterval(suit, 'flail'), suit.scaleInterval(1.0, suitScale), LerpPosInterval(suit, 0.25, Point3(0, -1.0, 0.0)), Sequence(Wait(0.25), Parallel(playSoundCogPanic, LerpPosInterval(suit, 1.5, Point3(0, -deep, 0.0), blendType='easeIn')))), Wait(2.5), Parallel(playSoundBomb, playSoundFly, Sequence(Func(smoke.show), Parallel(LerpScaleInterval(smoke, 0.5, 3), LerpColorScaleInterval(smoke, 0.5, Vec4(2, 2, 2, 0))), Func(smoke.hide)), Sequence(Func(kapow.show), +ActorInterval(kapow, 'kapow'), Func(kapow.hide)), LerpPosInterval(suit, 3.0, Point3(0, 150.0, 0.0)), suit.scaleInterval(3.0, 0.01)), Func(suit.hide))) + if hitCount == 1: + sival = Sequence(Parallel(reactIval, MovieUtil.createSuitStunInterval(suit, 0.3, 1.3)), Wait(0.0), Func(cannonHolder.remove)) + else: + sival = reactIval + suitResponseTrack.append(Wait(delay + tPieHitsSuit)) + suitResponseTrack.append(showDamage) + suitResponseTrack.append(updateHealthBar) + suitResponseTrack.append(sival) + bonusTrack = Sequence(Wait(delay + tPieHitsSuit)) + if kbbonus > 0: + bonusTrack.append(Wait(0.75)) + bonusTrack.append(Func(suit.showHpText, -kbbonus, 2, openEnded=0)) + if hpbonus > 0: + bonusTrack.append(Wait(0.75)) + bonusTrack.append(Func(suit.showHpText, -hpbonus, 1, openEnded=0)) + suitResponseTrack = Parallel(suitResponseTrack, bonusTrack) + return [toonTrack, + soundTrack, + buttonTrack, + suitResponseTrack] diff --git a/toontown/battle/MovieHeal.py b/toontown/battle/MovieHeal.py new file mode 100755 index 00000000..d1307ad9 --- /dev/null +++ b/toontown/battle/MovieHeal.py @@ -0,0 +1,487 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.task import Task +import random + +from BattleBase import * +import BattleParticles +from BattleProps import * +from BattleSounds import * +import MovieCamera +import MovieNPCSOS +import MovieUtil +from otp.nametag.NametagConstants import * +from toontown.effects import Splash +from toontown.toon import NPCToons +from toontown.toonbase import TTLocalizer +from toontown.toonbase.ToontownBattleGlobals import AvPropDamage + + +notify = DirectNotifyGlobal.directNotify.newCategory('MovieHeal') +soundFiles = ('AA_heal_tickle.ogg', 'AA_heal_telljoke.ogg', 'AA_heal_smooch.ogg', 'AA_heal_happydance.ogg', 'AA_heal_pixiedust.ogg', 'AA_heal_juggle.ogg', 'AA_heal_High_Dive.ogg') +healPos = Point3(0, 0, 0) +healHpr = Vec3(180.0, 0, 0) +runHealTime = 1.0 + +def doHeals(heals, hasInteractivePropHealBonus): + if len(heals) == 0: + return (None, None) + track = Sequence() + for h in heals: + ival = __doHealLevel(h, hasInteractivePropHealBonus) + if ival: + track.append(ival) + + camDuration = track.getDuration() + camTrack = MovieCamera.chooseHealShot(heals, camDuration) + return (track, camTrack) + + +def __doHealLevel(heal, hasInteractivePropHealBonus): + level = heal['level'] + if level == 0: + return __healTickle(heal, hasInteractivePropHealBonus) + elif level == 1: + return __healJoke(heal, hasInteractivePropHealBonus) + elif level == 2: + return __healSmooch(heal, hasInteractivePropHealBonus) + elif level == 3: + return __healDance(heal, hasInteractivePropHealBonus) + elif level == 4: + return __healSprinkle(heal, hasInteractivePropHealBonus) + elif level == 5: + return __healJuggle(heal, hasInteractivePropHealBonus) + elif level == 6: + return __healDive(heal, hasInteractivePropHealBonus) + return None + + +def __runToHealSpot(heal): + toon = heal['toon'] + battle = heal['battle'] + level = heal['level'] + origPos, origHpr = battle.getActorPosHpr(toon) + runAnimI = ActorInterval(toon, 'run', duration=runHealTime) + a = Func(toon.headsUp, battle, healPos) + b = Parallel(runAnimI, LerpPosInterval(toon, runHealTime, healPos, other=battle)) + if levelAffectsGroup(HEAL, level): + c = Func(toon.setHpr, battle, healHpr) + else: + target = heal['target']['toon'] + targetPos = target.getPos(battle) + c = Func(toon.headsUp, battle, targetPos) + return Sequence(a, b, c) + + +def __returnToBase(heal): + toon = heal['toon'] + battle = heal['battle'] + origPos, origHpr = battle.getActorPosHpr(toon) + runAnimI = ActorInterval(toon, 'run', duration=runHealTime) + a = Func(toon.headsUp, battle, origPos) + b = Parallel(runAnimI, LerpPosInterval(toon, runHealTime, origPos, other=battle)) + c = Func(toon.setHpr, battle, origHpr) + d = Func(toon.loop, 'neutral') + return Sequence(a, b, c, d) + + +def __healToon(toon, hp, ineffective, hasInteractivePropHealBonus): + notify.debug('healToon() - toon: %d hp: %d ineffective: %d' % (toon.doId, hp, ineffective)) + if ineffective == 1: + laughter = random.choice(TTLocalizer.MovieHealLaughterMisses) + else: + maxDam = AvPropDamage[0][1][0][1] + if hp >= maxDam - 1: + laughter = random.choice(TTLocalizer.MovieHealLaughterHits2) + else: + laughter = random.choice(TTLocalizer.MovieHealLaughterHits1) + toon.setChatAbsolute(laughter, CFSpeech | CFTimeout) + if hp > 0 and toon.hp != None: + toon.toonUp(hp, hasInteractivePropHealBonus) + else: + notify.debug('__healToon() - toon: %d hp: %d' % (toon.doId, hp)) + return + + +def __getPartTrack(particleEffect, startDelay, durationDelay, partExtraArgs): + pEffect = partExtraArgs[0] + parent = partExtraArgs[1] + if len(partExtraArgs) == 3: + worldRelative = partExtraArgs[2] + else: + worldRelative = 1 + return Sequence(Wait(startDelay), ParticleInterval(pEffect, parent, worldRelative, duration=durationDelay, cleanup=True)) + + +def __getSoundTrack(level, delay, duration = None, node = None): + soundEffect = globalBattleSoundCache.getSound(soundFiles[level]) + soundIntervals = Sequence() + if soundEffect: + if duration: + playSound = SoundInterval(soundEffect, duration=duration, node=node) + else: + playSound = SoundInterval(soundEffect, node=node) + soundIntervals.append(Wait(delay)) + soundIntervals.append(playSound) + return soundIntervals + + +def __healTickle(heal, hasInteractivePropHealBonus): + toon = heal['toon'] + target = heal['target']['toon'] + hp = heal['target']['hp'] + ineffective = heal['sidestep'] + level = heal['level'] + track = Sequence(__runToHealSpot(heal)) + feather = globalPropPool.getProp('feather') + feather2 = MovieUtil.copyProp(feather) + feathers = [feather, feather2] + hands = toon.getRightHands() + + def scaleFeathers(feathers, toon = toon, target = target): + toon.pose('tickle', 63) + toon.update(0) + hand = toon.getRightHands()[0] + horizDistance = Vec3(hand.getPos(render) - target.getPos(render)) + horizDistance.setZ(0) + distance = horizDistance.length() + if target.style.torso[0] == 's': + distance -= 0.5 + else: + distance -= 0.3 + featherLen = 2.4 + scale = distance / (featherLen * hand.getScale(render)[0]) + for feather in feathers: + feather.setScale(scale) + + tFeatherScaleUp = 0.5 + dFeatherScaleUp = 0.5 + dFeatherScaleDown = 0.5 + featherTrack = Parallel(MovieUtil.getActorIntervals(feathers, 'feather'), Sequence(Wait(tFeatherScaleUp), Func(MovieUtil.showProps, feathers, hands), Func(scaleFeathers, feathers), MovieUtil.getScaleIntervals(feathers, dFeatherScaleUp, MovieUtil.PNT3_NEARZERO, feathers[0].getScale)), Sequence(Wait(toon.getDuration('tickle') - dFeatherScaleDown), MovieUtil.getScaleIntervals(feathers, dFeatherScaleDown, None, MovieUtil.PNT3_NEARZERO))) + tHeal = 3.0 + mtrack = Parallel(featherTrack, ActorInterval(toon, 'tickle'), __getSoundTrack(level, 1, node=toon), Sequence(Wait(tHeal), Func(__healToon, target, hp, ineffective, hasInteractivePropHealBonus), ActorInterval(target, 'cringe', startTime=20.0 / target.getFrameRate('cringe')))) + track.append(mtrack) + track.append(Func(MovieUtil.removeProps, feathers)) + track.append(__returnToBase(heal)) + track.append(Func(target.clearChat)) + return track + + +def __healJoke(heal, hasInteractivePropHealBonus): + npcId = 0 + if 'npcId' in heal: + npcId = heal['npcId'] + toon = NPCToons.createLocalNPC(npcId) + if toon == None: + return + else: + toon = heal['toon'] + targets = heal['target'] + ineffective = heal['sidestep'] + level = heal['level'] + jokeIndex = heal['hpbonus'] % len(TTLocalizer.ToonHealJokes) + if npcId != 0: + track = Sequence(MovieNPCSOS.teleportIn(heal, toon)) + else: + track = Sequence(__runToHealSpot(heal)) + tracks = Parallel() + fSpeakPunchline = 58 + tSpeakSetup = 0.0 + tSpeakPunchline = 3.0 + dPunchLine = 3.0 + tTargetReact = tSpeakPunchline + 1.0 + dTargetLaugh = 1.5 + tRunBack = tSpeakPunchline + dPunchLine + tDoSoundAnimation = tSpeakPunchline - float(fSpeakPunchline) / toon.getFrameRate('sound') + megaphone = globalPropPool.getProp('megaphone') + megaphone2 = MovieUtil.copyProp(megaphone) + megaphones = [megaphone, megaphone2] + hands = toon.getRightHands() + dMegaphoneScale = 0.5 + tracks.append(Sequence(Wait(tDoSoundAnimation), Func(MovieUtil.showProps, megaphones, hands), MovieUtil.getScaleIntervals(megaphones, dMegaphoneScale, MovieUtil.PNT3_NEARZERO, MovieUtil.PNT3_ONE), Wait(toon.getDuration('sound') - 2.0 * dMegaphoneScale), MovieUtil.getScaleIntervals(megaphones, dMegaphoneScale, MovieUtil.PNT3_ONE, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProps, megaphones))) + tracks.append(Sequence(Wait(tDoSoundAnimation), ActorInterval(toon, 'sound'))) + soundTrack = __getSoundTrack(level, 2.0, node=toon) + tracks.append(soundTrack) + joke = TTLocalizer.ToonHealJokes[jokeIndex] + tracks.append(Sequence(Wait(tSpeakSetup), Func(toon.setChatAbsolute, joke[0], CFSpeech | CFTimeout))) + tracks.append(Sequence(Wait(tSpeakPunchline), Func(toon.setChatAbsolute, joke[1], CFSpeech | CFTimeout))) + reactTrack = Sequence(Wait(tTargetReact)) + for target in targets: + targetToon = target['toon'] + hp = target['hp'] + reactTrack.append(Func(__healToon, targetToon, hp, ineffective, hasInteractivePropHealBonus)) + + reactTrack.append(Wait(dTargetLaugh)) + for target in targets: + targetToon = target['toon'] + reactTrack.append(Func(targetToon.clearChat)) + + tracks.append(reactTrack) + if npcId != 0: + track.append(Sequence(Wait(tRunBack), Func(toon.clearChat), *MovieNPCSOS.teleportOut(heal, toon))) + else: + tracks.append(Sequence(Wait(tRunBack), Func(toon.clearChat), *__returnToBase(heal))) + track.append(tracks) + return track + + +def __healSmooch(heal, hasInteractivePropHealBonus): + toon = heal['toon'] + target = heal['target']['toon'] + level = heal['level'] + hp = heal['target']['hp'] + ineffective = heal['sidestep'] + track = Sequence(__runToHealSpot(heal)) + lipstick = globalPropPool.getProp('lipstick') + lipstick2 = MovieUtil.copyProp(lipstick) + lipsticks = [lipstick, lipstick2] + rightHands = toon.getRightHands() + dScale = 0.5 + lipstickTrack = Sequence(Func(MovieUtil.showProps, lipsticks, rightHands, Point3(-0.27, -0.24, -0.95), Point3(-118, -10.6, -25.9)), MovieUtil.getScaleIntervals(lipsticks, dScale, MovieUtil.PNT3_NEARZERO, MovieUtil.PNT3_ONE), Wait(toon.getDuration('smooch') - 2.0 * dScale), MovieUtil.getScaleIntervals(lipsticks, dScale, MovieUtil.PNT3_ONE, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProps, lipsticks)) + lips = globalPropPool.getProp('lips') + dScale = 0.5 + tLips = 2.5 + tThrow = 115.0 / toon.getFrameRate('smooch') + dThrow = 0.5 + + def getLipPos(toon = toon): + toon.pose('smooch', 57) + toon.update(0) + hand = toon.getRightHands()[0] + return hand.getPos(render) + + lipsTrack = Sequence(Wait(tLips), Func(MovieUtil.showProp, lips, render, getLipPos), Func(lips.setBillboardPointWorld), LerpScaleInterval(lips, dScale, Point3(3, 3, 3), startScale=MovieUtil.PNT3_NEARZERO), Wait(tThrow - tLips - dScale), LerpPosInterval(lips, dThrow, Point3(target.getPos() + Point3(0, 0, target.getHeight()))), Func(MovieUtil.removeProp, lips)) + delay = tThrow + dThrow + mtrack = Parallel(lipstickTrack, lipsTrack, __getSoundTrack(level, 2, node=toon), Sequence(ActorInterval(toon, 'smooch'), *__returnToBase(heal)), Sequence(Wait(delay), ActorInterval(target, 'conked')), Sequence(Wait(delay), Func(__healToon, target, hp, ineffective, hasInteractivePropHealBonus))) + track.append(mtrack) + track.append(Func(target.clearChat)) + return track + + +def __healDance(heal, hasInteractivePropHealBonus): + npcId = 0 + if 'npcId' in heal: + npcId = heal['npcId'] + toon = NPCToons.createLocalNPC(npcId) + if toon == None: + return + else: + toon = heal['toon'] + targets = heal['target'] + ineffective = heal['sidestep'] + level = heal['level'] + if npcId != 0: + track = Sequence(MovieNPCSOS.teleportIn(heal, toon)) + else: + track = Sequence(__runToHealSpot(heal)) + delay = 3.0 + first = 1 + targetTrack = Sequence() + for target in targets: + targetToon = target['toon'] + hp = target['hp'] + reactIval = Func(__healToon, targetToon, hp, ineffective, hasInteractivePropHealBonus) + if first: + targetTrack.append(Wait(delay)) + first = 0 + targetTrack.append(reactIval) + + hat = globalPropPool.getProp('hat') + hat2 = MovieUtil.copyProp(hat) + hats = [hat, hat2] + cane = globalPropPool.getProp('cane') + cane2 = MovieUtil.copyProp(cane) + canes = [cane, cane2] + leftHands = toon.getLeftHands() + rightHands = toon.getRightHands() + dScale = 0.5 + propTrack = Sequence(Func(MovieUtil.showProps, hats, rightHands, Point3(0.23, 0.09, 0.69), Point3(180, 0, 0)), Func(MovieUtil.showProps, canes, leftHands, Point3(-0.28, 0.0, 0.14), Point3(0.0, 0.0, -150.0)), MovieUtil.getScaleIntervals(hats + canes, dScale, MovieUtil.PNT3_NEARZERO, MovieUtil.PNT3_ONE), Wait(toon.getDuration('happy-dance') - 2.0 * dScale), MovieUtil.getScaleIntervals(hats + canes, dScale, MovieUtil.PNT3_ONE, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProps, hats + canes)) + mtrack = Parallel(propTrack, ActorInterval(toon, 'happy-dance'), __getSoundTrack(level, 0.2, duration=6.4, node=toon), targetTrack) + track.append(Func(toon.loop, 'neutral')) + track.append(Wait(0.1)) + track.append(mtrack) + if npcId != 0: + track.append(MovieNPCSOS.teleportOut(heal, toon)) + else: + track.append(__returnToBase(heal)) + for target in targets: + targetToon = target['toon'] + track.append(Func(targetToon.clearChat)) + + return track + + +def __healSprinkle(heal, hasInteractivePropHealBonus): + toon = heal['toon'] + target = heal['target']['toon'] + hp = heal['target']['hp'] + ineffective = heal['sidestep'] + level = heal['level'] + track = Sequence(__runToHealSpot(heal)) + sprayEffect = BattleParticles.createParticleEffect(file='pixieSpray') + dropEffect = BattleParticles.createParticleEffect(file='pixieDrop') + explodeEffect = BattleParticles.createParticleEffect(file='pixieExplode') + poofEffect = BattleParticles.createParticleEffect(file='pixiePoof') + wallEffect = BattleParticles.createParticleEffect(file='pixieWall') + + def face90(toon = toon, target = target): + vec = Point3(target.getPos() - toon.getPos()) + vec.setZ(0) + temp = vec[0] + vec.setX(-vec[1]) + vec.setY(temp) + targetPoint = Point3(toon.getPos() + vec) + toon.headsUp(render, targetPoint) + + delay = 2.5 + mtrack = Parallel(__getPartTrack(sprayEffect, 1.5, 0.5, [sprayEffect, toon, 0]), __getPartTrack(dropEffect, 1.9, 2.0, [dropEffect, target, 0]), __getPartTrack(explodeEffect, 2.7, 1.0, [explodeEffect, toon, 0]), __getPartTrack(poofEffect, 3.4, 1.0, [poofEffect, target, 0]), __getPartTrack(wallEffect, 4.05, 1.2, [wallEffect, toon, 0]), __getSoundTrack(level, 2, duration=4.1, node=toon), Sequence(Func(face90), ActorInterval(toon, 'sprinkle-dust')), Sequence(Wait(delay), Func(__healToon, target, hp, ineffective, hasInteractivePropHealBonus))) + track.append(mtrack) + track.append(__returnToBase(heal)) + track.append(Func(target.clearChat)) + return track + + +def __healJuggle(heal, hasInteractivePropHealBonus): + npcId = 0 + if 'npcId' in heal: + npcId = heal['npcId'] + toon = NPCToons.createLocalNPC(npcId) + if toon == None: + return + else: + toon = heal['toon'] + targets = heal['target'] + ineffective = heal['sidestep'] + level = heal['level'] + if npcId != 0: + track = Sequence(MovieNPCSOS.teleportIn(heal, toon)) + else: + track = Sequence(__runToHealSpot(heal)) + delay = 4.0 + first = 1 + targetTrack = Sequence() + for target in targets: + targetToon = target['toon'] + hp = target['hp'] + reactIval = Func(__healToon, targetToon, hp, ineffective, hasInteractivePropHealBonus) + if first == 1: + targetTrack.append(Wait(delay)) + first = 0 + targetTrack.append(reactIval) + + cube = globalPropPool.getProp('cubes') + cube2 = MovieUtil.copyProp(cube) + cubes = [cube, cube2] + hips = [toon.getLOD(toon.getLODNames()[0]).find('**/joint_hips'), toon.getLOD(toon.getLODNames()[1]).find('**/joint_hips')] + cubeTrack = Sequence(Func(MovieUtil.showProps, cubes, hips), MovieUtil.getActorIntervals(cubes, 'cubes'), Func(MovieUtil.removeProps, cubes)) + mtrack = Parallel(cubeTrack, __getSoundTrack(level, 0.7, duration=7.7, node=toon), ActorInterval(toon, 'juggle'), targetTrack) + track.append(mtrack) + if npcId != 0: + track.append(MovieNPCSOS.teleportOut(heal, toon)) + else: + track.append(__returnToBase(heal)) + for target in targets: + targetToon = target['toon'] + track.append(Func(targetToon.clearChat)) + + return track + + +def __healDive(heal, hasInteractivePropHealBonus): + splash = Splash.Splash(render) + splash.reparentTo(render) + npcId = 0 + if 'npcId' in heal: + npcId = heal['npcId'] + toon = NPCToons.createLocalNPC(npcId) + if toon == None: + return + else: + toon = heal['toon'] + targets = heal['target'] + ineffective = heal['sidestep'] + level = heal['level'] + if npcId != 0: + track = Sequence(MovieNPCSOS.teleportIn(heal, toon)) + else: + track = Sequence(__runToHealSpot(heal)) + delay = 7.0 + first = 1 + targetTrack = Sequence() + for target in targets: + targetToon = target['toon'] + hp = target['hp'] + reactIval = Func(__healToon, targetToon, hp, ineffective, hasInteractivePropHealBonus) + if first == 1: + targetTrack.append(Wait(delay)) + first = 0 + targetTrack.append(reactIval) + + thisBattle = heal['battle'] + toonsInBattle = thisBattle.toons + glass = globalPropPool.getProp('glass') + glass.setScale(4.0) + glass.setHpr(0.0, 90.0, 0.0) + ladder = globalPropPool.getProp('ladder') + placeNode = NodePath('lookNode') + diveProps = [glass, ladder] + ladderScale = toon.getBodyScale() / 0.66 + scaleUpPoint = Point3(0.5, 0.5, 0.45) * ladderScale + basePos = toon.getPos() + glassOffset = Point3(0, 1.1, 0.2) + glassToonOffset = Point3(0, 1.2, 0.2) + splashOffset = Point3(0, 1.0, 0.4) + ladderOffset = Point3(0, 4, 0) + ladderToonSep = Point3(0, 1, 0) * ladderScale + diveOffset = Point3(0, 0, 10) + divePos = add3(add3(ladderOffset, diveOffset), ladderToonSep) + ladder.setH(toon.getH()) + glassPos = render.getRelativePoint(toon, glassOffset) + glassToonPos = render.getRelativePoint(toon, glassToonOffset) + ladderPos = render.getRelativePoint(toon, ladderOffset) + climbladderPos = render.getRelativePoint(toon, add3(ladderOffset, ladderToonSep)) + divePos = render.getRelativePoint(toon, divePos) + topDivePos = render.getRelativePoint(toon, diveOffset) + lookBase = render.getRelativePoint(toon, ladderOffset) + lookTop = render.getRelativePoint(toon, add3(ladderOffset, diveOffset)) + LookGlass = render.getRelativePoint(toon, glassOffset) + splash.setPos(splashOffset) + walkToLadderTime = 1.0 + climbTime = 5.0 + diveTime = 1.0 + ladderGrowTime = 1.5 + splash.setPos(glassPos) + toonNode = toon.getGeomNode() + placeNode.reparentTo(render) + placeNode.setScale(5.0) + placeNode.setPos(toon.getPos(render)) + placeNode.setHpr(toon.getHpr(render)) + toonscale = toonNode.getScale() + toonFacing = toon.getHpr() + propTrack = Sequence(Func(MovieUtil.showProp, glass, render, glassPos), Func(MovieUtil.showProp, ladder, render, ladderPos), Func(toonsLook, toonsInBattle, placeNode, Point3(0, 0, 0)), Func(placeNode.setPos, lookBase), LerpScaleInterval(ladder, ladderGrowTime, scaleUpPoint, startScale=MovieUtil.PNT3_NEARZERO), Func(placeNode.setPos, lookTop), Wait(2.1), MovieCamera.toonGroupHighShot(None, 0), Wait(2.1), Func(placeNode.setPos, LookGlass), Wait(0.4), MovieCamera.allGroupLowShot(None, 0), Wait(1.8), LerpScaleInterval(ladder, ladderGrowTime, MovieUtil.PNT3_NEARZERO, startScale=scaleUpPoint), Func(MovieUtil.removeProps, diveProps)) + mtrack = Parallel(propTrack, __getSoundTrack(level, 0.6, duration=9.0, node=toon), Sequence(Parallel(Sequence(ActorInterval(toon, 'walk', loop=0, duration=walkToLadderTime), ActorInterval(toon, 'neutral', loop=0, duration=0.1)), LerpPosInterval(toon, walkToLadderTime, climbladderPos), Wait(ladderGrowTime)), Parallel(ActorInterval(toon, 'climb', loop=0, endFrame=116), Sequence(Wait(4.6), Func(toonNode.setTransparency, 1), LerpColorScaleInterval(toonNode, 0.25, VBase4(1, 1.0, 1, 0.0), blendType='easeInOut'), LerpScaleInterval(toonNode, 0.01, 0.1, startScale=toonscale), LerpHprInterval(toon, 0.01, toonFacing), LerpPosInterval(toon, 0.0, glassToonPos), Func(toonNode.clearTransparency), Func(toonNode.clearColorScale), Parallel(ActorInterval(toon, 'swim', loop=1, startTime=0.0, endTime=1.0), Wait(1.0))), Sequence(Wait(4.6), Func(splash.play), Wait(1.0), Func(splash.destroy))), Wait(0.5), Parallel(ActorInterval(toon, 'jump', loop=0, startTime=0.2), LerpScaleInterval(toonNode, 0.5, toonscale, startScale=0.1), Func(stopLook, toonsInBattle))), targetTrack) + track.append(mtrack) + if npcId != 0: + track.append(MovieNPCSOS.teleportOut(heal, toon)) + else: + track.append(__returnToBase(heal)) + for target in targets: + targetToon = target['toon'] + track.append(Func(targetToon.clearChat)) + + return track + + +def add3(t1, t2): + returnThree = Point3(t1[0] + t2[0], t1[1] + t2[1], t1[2] + t2[2]) + return returnThree + + +def stopLook(toonsInBattle): + for someToon in toonsInBattle: + someToon.stopStareAt() + + +def toonsLook(toons, someNode, offset): + for someToon in toons: + someToon.startStareAt(someNode, offset) diff --git a/toontown/battle/MovieLure.py b/toontown/battle/MovieLure.py new file mode 100755 index 00000000..6bec5aed --- /dev/null +++ b/toontown/battle/MovieLure.py @@ -0,0 +1,619 @@ +from direct.interval.IntervalGlobal import * +from BattleBase import * +from BattleProps import * +from toontown.suit.SuitBase import * +from toontown.toon.ToonDNA import * +from BattleSounds import * +import MovieCamera +from direct.directnotify import DirectNotifyGlobal +import MovieUtil +from toontown.toonbase import ToontownBattleGlobals +import BattleParticles +import BattleProps +import MovieNPCSOS +import random +notify = DirectNotifyGlobal.directNotify.newCategory('MovieLures') + +def safeWrtReparentTo(nodePath, parent): + if nodePath and not nodePath.isEmpty(): + nodePath.wrtReparentTo(parent) + + +def doLures(lures): + if len(lures) == 0: + return (None, None) + npcArrivals, npcDepartures, npcs = MovieNPCSOS.doNPCTeleports(lures) + mtrack = Parallel() + for l in lures: + ival = __doLureLevel(l, npcs) + if ival: + mtrack.append(ival) + + lureTrack = Sequence(npcArrivals, mtrack, npcDepartures) + camDuration = mtrack.getDuration() + enterDuration = npcArrivals.getDuration() + exitDuration = npcDepartures.getDuration() + camTrack = MovieCamera.chooseLureShot(lures, camDuration, enterDuration, exitDuration) + return (lureTrack, camTrack) + + +def __doLureLevel(lure, npcs): + level = lure['level'] + if level == 0: + return __lureOneDollar(lure) + elif level == 1: + return __lureSmallMagnet(lure, npcs) + elif level == 2: + return __lureFiveDollar(lure) + elif level == 3: + return __lureLargeMagnet(lure, npcs) + elif level == 4: + return __lureTenDollar(lure) + elif level == 5: + return __lureHypnotize(lure, npcs) + elif level == 6: + return __lureSlideshow(lure, npcs) + return None + + +def getSoundTrack(fileName, delay = 0.01, duration = None, node = None): + soundEffect = globalBattleSoundCache.getSound(fileName) + if duration: + return Sequence(Wait(delay), SoundInterval(soundEffect, duration=duration, node=node)) + else: + return Sequence(Wait(delay), SoundInterval(soundEffect, node=node)) + + +def __createFishingPoleMultiTrack(lure, dollar, dollarName): + toon = lure['toon'] + target = lure['target'] + battle = lure['battle'] + sidestep = lure['sidestep'] + hp = target['hp'] + kbbonus = target['kbbonus'] + suit = target['suit'] + targetPos = suit.getPos(battle) + died = target['died'] + revived = target['revived'] + reachAnimDuration = 3.5 + trapProp = suit.battleTrapProp + pole = globalPropPool.getProp('fishing-pole') + pole2 = MovieUtil.copyProp(pole) + poles = [pole, pole2] + hands = toon.getRightHands() + + def positionDollar(dollar, suit): + dollar.reparentTo(suit) + dollar.setPos(0, MovieUtil.SUIT_LURE_DOLLAR_DISTANCE, 0) + + dollarTrack = Sequence(Func(positionDollar, dollar, suit), Func(dollar.wrtReparentTo, battle), ActorInterval(dollar, dollarName, duration=3), getSplicedLerpAnimsTrack(dollar, dollarName, 0.7, 2.0, startTime=3), LerpPosInterval(dollar, 0.2, Point3(0, -10, 7)), Func(MovieUtil.removeProp, dollar)) + poleTrack = Sequence(Func(MovieUtil.showProps, poles, hands), ActorInterval(pole, 'fishing-pole'), Func(MovieUtil.removeProps, poles)) + toonTrack = Sequence(Func(toon.headsUp, battle, targetPos), ActorInterval(toon, 'battlecast'), Func(toon.loop, 'neutral')) + tracks = Parallel(dollarTrack, poleTrack, toonTrack) + if sidestep == 0: + if kbbonus == 1 or hp > 0: + suitTrack = Sequence() + opos, ohpr = battle.getActorPosHpr(suit) + reachDist = MovieUtil.SUIT_LURE_DISTANCE + reachPos = Point3(opos[0], opos[1] - reachDist, opos[2]) + suitTrack.append(Func(suit.loop, 'neutral')) + suitTrack.append(Wait(3.5)) + suitName = suit.getStyleName() + retardPos, retardHpr = battle.getActorPosHpr(suit) + retardPos.setY(retardPos.getY() + MovieUtil.SUIT_EXTRA_REACH_DISTANCE) + if suitName in MovieUtil.largeSuits: + moveTrack = lerpSuit(suit, 0.0, reachAnimDuration / 2.5, retardPos, battle, trapProp) + reachTrack = ActorInterval(suit, 'reach', duration=reachAnimDuration) + suitTrack.append(Parallel(moveTrack, reachTrack)) + else: + suitTrack.append(ActorInterval(suit, 'reach', duration=reachAnimDuration)) + if trapProp: + suitTrack.append(Func(trapProp.wrtReparentTo, battle)) + suitTrack.append(Func(suit.setPos, battle, reachPos)) + if trapProp: + suitTrack.append(Func(trapProp.wrtReparentTo, suit)) + suit.battleTrapProp = trapProp + suitTrack.append(Func(suit.loop, 'lured')) + suitTrack.append(Func(battle.lureSuit, suit)) + if hp > 0: + suitTrack.append(__createSuitDamageTrack(battle, suit, hp, lure, trapProp)) + if revived != 0: + suitTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle)) + if died != 0: + suitTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle)) + tracks.append(suitTrack) + else: + tracks.append(Sequence(Wait(3.7), Func(MovieUtil.indicateMissed, suit))) + tracks.append(getSoundTrack('TL_fishing_pole.ogg', delay=0.5, node=toon)) + return tracks + + +def __createMagnetMultiTrack(lure, magnet, pos, hpr, scale, isSmallMagnet = 1, npcs = []): + toon = lure['toon'] + if 'npc' in lure: + toon = lure['npc'] + battle = lure['battle'] + sidestep = lure['sidestep'] + targets = lure['target'] + tracks = Parallel() + tracks.append(Sequence(ActorInterval(toon, 'hold-magnet'), Func(toon.loop, 'neutral'))) + hands = toon.getLeftHands() + magnet2 = MovieUtil.copyProp(magnet) + magnets = [magnet, magnet2] + magnetTrack = Sequence(Wait(0.7), Func(MovieUtil.showProps, magnets, hands, pos, hpr, scale), Wait(6.3), Func(MovieUtil.removeProps, magnets)) + tracks.append(magnetTrack) + for target in targets: + suit = target['suit'] + trapProp = suit.battleTrapProp + if sidestep == 0: + hp = target['hp'] + kbbonus = target['kbbonus'] + died = target['died'] + revived = target['revived'] + if kbbonus == 1 or hp > 0: + suitDelay = 2.6 + suitMoveDuration = 0.8 + suitTrack = Sequence() + opos, ohpr = battle.getActorPosHpr(suit) + reachDist = MovieUtil.SUIT_LURE_DISTANCE + reachPos = Point3(opos[0], opos[1] - reachDist, opos[2]) + numShakes = 3 + shakeTotalDuration = 0.8 + shakeDuration = shakeTotalDuration / float(numShakes) + suitTrack.append(Func(suit.loop, 'neutral')) + suitTrack.append(Wait(suitDelay)) + suitTrack.append(ActorInterval(suit, 'landing', startTime=2.37, endTime=1.82)) + for i in xrange(0, numShakes): + suitTrack.append(ActorInterval(suit, 'landing', startTime=1.82, endTime=1.16, duration=shakeDuration)) + + suitTrack.append(ActorInterval(suit, 'landing', startTime=1.16, endTime=0.7)) + suitTrack.append(ActorInterval(suit, 'landing', startTime=0.7, duration=1.3)) + suitTrack.append(Func(suit.loop, 'lured')) + suitTrack.append(Func(battle.lureSuit, suit)) + if hp > 0: + suitTrack.append(__createSuitDamageTrack(battle, suit, hp, lure, trapProp)) + if revived != 0: + suitTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle, npcs)) + elif died != 0: + suitTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle, npcs)) + tracks.append(suitTrack) + tracks.append(lerpSuit(suit, suitDelay + 0.55 + shakeTotalDuration, suitMoveDuration, reachPos, battle, trapProp)) + else: + tracks.append(Sequence(Wait(3.7), Func(MovieUtil.indicateMissed, suit))) + + if isSmallMagnet == 1: + tracks.append(getSoundTrack('TL_small_magnet.ogg', delay=0.7, node=toon)) + else: + tracks.append(getSoundTrack('TL_large_magnet.ogg', delay=0.7, node=toon)) + return tracks + + +def __createHypnoGogglesMultiTrack(lure, npcs = []): + toon = lure['toon'] + if 'npc' in lure: + toon = lure['npc'] + targets = lure['target'] + battle = lure['battle'] + sidestep = lure['sidestep'] + goggles = globalPropPool.getProp('hypno-goggles') + goggles2 = MovieUtil.copyProp(goggles) + bothGoggles = [goggles, goggles2] + pos = Point3(-1.03, 1.04, -0.3) + hpr = Point3(-96.55, 36.14, -170.59) + scale = Point3(1.5, 1.5, 1.5) + hands = toon.getLeftHands() + gogglesTrack = Sequence(Wait(0.6), Func(MovieUtil.showProps, bothGoggles, hands, pos, hpr, scale), ActorInterval(goggles, 'hypno-goggles', duration=2.2), Func(MovieUtil.removeProps, bothGoggles)) + toonTrack = Sequence(ActorInterval(toon, 'hypnotize'), Func(toon.loop, 'neutral')) + tracks = Parallel(gogglesTrack, toonTrack) + for target in targets: + suit = target['suit'] + trapProp = suit.battleTrapProp + if sidestep == 0: + hp = target['hp'] + kbbonus = target['kbbonus'] + died = target['died'] + revived = target['revived'] + if kbbonus == 1 or hp > 0: + suitTrack = Sequence() + suitDelay = 1.6 + suitAnimDuration = 1.5 + opos, ohpr = battle.getActorPosHpr(suit) + reachDist = MovieUtil.SUIT_LURE_DISTANCE + reachPos = Point3(opos[0], opos[1] - reachDist, opos[2]) + suitTrack.append(Func(suit.loop, 'neutral')) + suitTrack.append(Wait(suitDelay)) + suitTrack.append(ActorInterval(suit, 'hypnotized', duration=3.1)) + suitTrack.append(Func(suit.setPos, battle, reachPos)) + suitTrack.append(Func(suit.loop, 'lured')) + suitTrack.append(Func(battle.lureSuit, suit)) + if hp > 0: + suitTrack.append(__createSuitDamageTrack(battle, suit, hp, lure, trapProp)) + if revived != 0: + suitTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle, npcs)) + elif died != 0: + suitTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle, npcs)) + tracks.append(suitTrack) + tracks.append(lerpSuit(suit, suitDelay + 1.7, 0.7, reachPos, battle, trapProp)) + else: + tracks.append(Sequence(Wait(2.3), Func(MovieUtil.indicateMissed, suit, 1.1))) + + tracks.append(getSoundTrack('TL_hypnotize.ogg', delay=0.5, node=toon)) + return tracks + + +def __lureOneDollar(lure): + dollarProp = '1dollar' + dollar = globalPropPool.getProp(dollarProp) + return __createFishingPoleMultiTrack(lure, dollar, dollarProp) + + +def __lureSmallMagnet(lure, npcs = []): + magnet = globalPropPool.getProp('small-magnet') + pos = Point3(-0.27, 0.19, 0.29) + hpr = Point3(-90.0, 84.17, -180.0) + scale = Point3(0.85, 0.85, 0.85) + return __createMagnetMultiTrack(lure, magnet, pos, hpr, scale, isSmallMagnet=1, npcs=npcs) + + +def __lureFiveDollar(lure): + dollarProp = '5dollar' + dollar = globalPropPool.getProp(dollarProp) + return __createFishingPoleMultiTrack(lure, dollar, dollarProp) + + +def __lureLargeMagnet(lure, npcs = []): + magnet = globalPropPool.getProp('big-magnet') + pos = Point3(-0.27, 0.08, 0.29) + hpr = Point3(-90.0, 84.17, -180) + scale = Point3(1.32, 1.32, 1.32) + return __createMagnetMultiTrack(lure, magnet, pos, hpr, scale, isSmallMagnet=0, npcs=npcs) + + +def __lureTenDollar(lure): + dollarProp = '10dollar' + dollar = globalPropPool.getProp(dollarProp) + return __createFishingPoleMultiTrack(lure, dollar, dollarProp) + + +def __lureHypnotize(lure, npcs = []): + return __createHypnoGogglesMultiTrack(lure, npcs) + + +def __lureSlideshow(lure, npcs): + return __createSlideshowMultiTrack(lure, npcs) + + +def __createSuitDamageTrack(battle, suit, hp, lure, trapProp): + if (trapProp is None) or trapProp.isEmpty(): + return Func(suit.loop, 'lured') + trapProp.wrtReparentTo(battle) + trapTrack = ToontownBattleGlobals.TRAP_TRACK + trapLevel = suit.battleTrap + trapTrackNames = ToontownBattleGlobals.AvProps[trapTrack] + trapName = trapTrackNames[trapLevel] + result = Sequence() + + def reparentTrap(trapProp = trapProp, battle = battle): + if trapProp and not trapProp.isEmpty(): + trapProp.wrtReparentTo(battle) + + result.append(Func(reparentTrap)) + parent = battle + if suit.battleTrapIsFresh == 1: + if trapName == 'quicksand' or trapName == 'trapdoor': + trapProp.hide() + trapProp.reparentTo(suit) + trapProp.setPos(Point3(0, MovieUtil.SUIT_TRAP_DISTANCE, 0)) + trapProp.setHpr(Point3(0, 0, 0)) + trapProp.wrtReparentTo(battle) + elif trapName == 'rake': + trapProp.hide() + trapProp.reparentTo(suit) + trapProp.setPos(0, MovieUtil.SUIT_TRAP_RAKE_DISTANCE, 0) + trapProp.setHpr(Point3(0, 270, 0)) + trapProp.setScale(Point3(0.7, 0.7, 0.7)) + rakeOffset = MovieUtil.getSuitRakeOffset(suit) + trapProp.setY(trapProp.getY() + rakeOffset) + else: + parent = render + if trapName == 'banana': + slidePos = trapProp.getPos(parent) + slidePos.setY(slidePos.getY() - 5.1) + moveTrack = Sequence(Wait(0.1), LerpPosInterval(trapProp, 0.1, slidePos, other=battle)) + animTrack = Sequence(ActorInterval(trapProp, 'banana', startTime=3.1), Wait(1.1), LerpScaleInterval(trapProp, 1, Point3(0.01, 0.01, 0.01))) + suitTrack = ActorInterval(suit, 'slip-backward') + damageTrack = Sequence(Wait(0.5), Func(suit.showHpText, -hp, openEnded=0), Func(suit.updateHealthBar, hp)) + soundTrack = Sequence(SoundInterval(globalBattleSoundCache.getSound('AA_pie_throw_only.ogg'), duration=0.55, node=suit), SoundInterval(globalBattleSoundCache.getSound('Toon_bodyfall_synergy.ogg'), node=suit)) + result.append(Parallel(moveTrack, animTrack, suitTrack, damageTrack, soundTrack)) + elif trapName == 'rake' or trapName == 'rake-react': + hpr = trapProp.getHpr(parent) + upHpr = Vec3(hpr[0], 179.9999, hpr[2]) + bounce1Hpr = Vec3(hpr[0], 120, hpr[2]) + bounce2Hpr = Vec3(hpr[0], 100, hpr[2]) + rakeTrack = Sequence(Wait(0.5), LerpHprInterval(trapProp, 0.1, upHpr, startHpr=hpr), Wait(0.7), LerpHprInterval(trapProp, 0.4, hpr, startHpr=upHpr), LerpHprInterval(trapProp, 0.15, bounce1Hpr, startHpr=hpr), LerpHprInterval(trapProp, 0.05, hpr, startHpr=bounce1Hpr), LerpHprInterval(trapProp, 0.15, bounce2Hpr, startHpr=hpr), LerpHprInterval(trapProp, 0.05, hpr, startHpr=bounce2Hpr), Wait(0.2), LerpScaleInterval(trapProp, 0.2, Point3(0.01, 0.01, 0.01))) + rakeAnimDuration = 3.125 + suitTrack = ActorInterval(suit, 'rake-react', duration=rakeAnimDuration) + damageTrack = Sequence(Wait(0.5), Func(suit.showHpText, -hp, openEnded=0), Func(suit.updateHealthBar, hp)) + soundTrack = getSoundTrack('TL_step_on_rake.ogg', delay=0.6, node=suit) + result.append(Parallel(rakeTrack, suitTrack, damageTrack, soundTrack)) + elif trapName == 'marbles': + slidePos = trapProp.getPos(parent) + slidePos.setY(slidePos.getY() - 6.5) + moveTrack = Sequence(Wait(0.1), LerpPosInterval(trapProp, 0.8, slidePos, other=battle), Wait(1.1), LerpScaleInterval(trapProp, 1, Point3(0.01, 0.01, 0.01))) + animTrack = ActorInterval(trapProp, 'marbles', startTime=3.1) + suitTrack = ActorInterval(suit, 'slip-backward') + damageTrack = Sequence(Wait(0.5), Func(suit.showHpText, -hp, openEnded=0), Func(suit.updateHealthBar, hp)) + soundTrack = Sequence(SoundInterval(globalBattleSoundCache.getSound('AA_pie_throw_only.ogg'), duration=0.55, node=suit), SoundInterval(globalBattleSoundCache.getSound('Toon_bodyfall_synergy.ogg'), node=suit)) + result.append(Parallel(moveTrack, animTrack, suitTrack, damageTrack, soundTrack)) + elif trapName == 'quicksand': + sinkPos1 = trapProp.getPos(battle) + sinkPos2 = trapProp.getPos(battle) + dropPos = trapProp.getPos(battle) + landPos = trapProp.getPos(battle) + sinkPos1.setZ(sinkPos1.getZ() - 3.1) + sinkPos2.setZ(sinkPos2.getZ() - 9.1) + dropPos.setZ(dropPos.getZ() + 15) + if base.config.GetBool('want-new-cogs', 0): + nameTag = suit.find('**/def_nameTag') + else: + nameTag = suit.find('**/joint_nameTag') + trapTrack = Sequence(Wait(2.4), LerpScaleInterval(trapProp, 0.8, Point3(0.01, 0.01, 0.01))) + moveTrack = Sequence(Wait(0.9), LerpPosInterval(suit, 0.9, sinkPos1, other=battle), LerpPosInterval(suit, 0.4, sinkPos2, other=battle), Func(suit.setPos, battle, dropPos), Func(suit.wrtReparentTo, hidden), Wait(1.1), Func(suit.wrtReparentTo, battle), LerpPosInterval(suit, 0.3, landPos, other=battle)) + animTrack = Sequence(ActorInterval(suit, 'flail'), ActorInterval(suit, 'flail', startTime=1.1), Wait(0.7), ActorInterval(suit, 'slip-forward', duration=2.1)) + damageTrack = Sequence(Wait(3.5), Func(suit.showHpText, -hp, openEnded=0), Func(suit.updateHealthBar, hp)) + soundTrack = Sequence(Wait(0.7), SoundInterval(globalBattleSoundCache.getSound('TL_quicksand.ogg'), node=suit), Wait(0.1), SoundInterval(globalBattleSoundCache.getSound('Toon_bodyfall_synergy.ogg'), node=suit)) + result.append(Parallel(trapTrack, moveTrack, animTrack, damageTrack, soundTrack)) + elif trapName == 'trapdoor': + sinkPos = trapProp.getPos(battle) + dropPos = trapProp.getPos(battle) + landPos = trapProp.getPos(battle) + sinkPos.setZ(sinkPos.getZ() - 9.1) + dropPos.setZ(dropPos.getZ() + 15) + trapTrack = Sequence(Wait(2.4), LerpScaleInterval(trapProp, 0.8, Point3(0.01, 0.01, 0.01))) + moveTrack = Sequence(Wait(2.2), LerpPosInterval(suit, 0.4, sinkPos, other=battle), Func(suit.setPos, battle, dropPos), Func(suit.wrtReparentTo, hidden), Wait(1.6), Func(suit.wrtReparentTo, battle), LerpPosInterval(suit, 0.3, landPos, other=battle)) + animTrack = Sequence(getSplicedLerpAnimsTrack(suit, 'flail', 0.7, 0.25), Func(trapProp.setColor, Vec4(0, 0, 0, 1)), ActorInterval(suit, 'flail', startTime=0.7, endTime=0), ActorInterval(suit, 'neutral', duration=0.5), ActorInterval(suit, 'flail', startTime=1.1), Wait(1.1), ActorInterval(suit, 'slip-forward', duration=2.1)) + damageTrack = Sequence(Wait(3.5), Func(suit.showHpText, -hp, openEnded=0), Func(suit.updateHealthBar, hp)) + soundTrack = Sequence(Wait(0.8), SoundInterval(globalBattleSoundCache.getSound('TL_trap_door.ogg'), node=suit), Wait(0.8), SoundInterval(globalBattleSoundCache.getSound('Toon_bodyfall_synergy.ogg'), node=suit)) + result.append(Parallel(trapTrack, moveTrack, animTrack, damageTrack, soundTrack)) + elif trapName == 'tnt': + tntTrack = ActorInterval(trapProp, 'tnt') + explosionTrack = Sequence(Wait(2.3), createTNTExplosionTrack(battle, trapProp=trapProp, relativeTo=parent)) + suitTrack = Sequence(ActorInterval(suit, 'flail', duration=0.7), ActorInterval(suit, 'flail', startTime=0.7, endTime=0.0), ActorInterval(suit, 'neutral', duration=0.4), ActorInterval(suit, 'flail', startTime=0.6, endTime=0.7), Wait(0.4), ActorInterval(suit, 'slip-forward', startTime=2.48, duration=0.1), Func(battle.movie.needRestoreColor), Func(suit.setColorScale, Vec4(0.2, 0.2, 0.2, 1)), Func(trapProp.reparentTo, hidden), ActorInterval(suit, 'slip-forward', startTime=2.58), Func(suit.clearColorScale), Func(trapProp.sparksEffect.cleanup), Func(battle.movie.clearRestoreColor)) + damageTrack = Sequence(Wait(2.3), Func(suit.showHpText, -hp, openEnded=0), Func(suit.updateHealthBar, hp)) + explosionSound = base.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart_%s.ogg' % random.randint(1, 6)) + soundTrack = Sequence(SoundInterval(globalBattleSoundCache.getSound('TL_dynamite.ogg'), duration=2.0, node=suit), SoundInterval(explosionSound, duration=0.6, node=suit)) + result.append(Parallel(tntTrack, suitTrack, damageTrack, explosionTrack, soundTrack)) + elif trapName == 'traintrack': + trainInterval = createIncomingTrainInterval(battle, suit, hp, lure, trapProp) + result.append(trainInterval) + else: + notify.warning('unknown trapName: %s detected on suit: %s' % (trapName, suit)) + suit.battleTrapProp = trapProp + result.append(Func(battle.removeTrap, suit, True)) + result.append(Func(battle.unlureSuit, suit)) + result.append(__createSuitResetPosTrack(suit, battle)) + result.append(Func(suit.loop, 'neutral')) + if trapName == 'traintrack': + result.append(Func(MovieUtil.removeProp, trapProp)) + return result + + +def __createSuitResetPosTrack(suit, battle): + resetPos, resetHpr = battle.getActorPosHpr(suit) + moveDist = Vec3(suit.getPos(battle) - resetPos).length() + moveDuration = 0.5 + walkTrack = Sequence(Func(suit.setHpr, battle, resetHpr), ActorInterval(suit, 'walk', startTime=1, duration=moveDuration, endTime=0.0001), Func(suit.loop, 'neutral')) + moveTrack = LerpPosInterval(suit, moveDuration, resetPos, other=battle) + return Parallel(walkTrack, moveTrack) + + +def getSplicedLerpAnimsTrack(object, animName, origDuration, newDuration, startTime = 0, fps = 30): + track = Sequence() + addition = 0 + numIvals = origDuration * fps + timeInterval = newDuration / numIvals + animInterval = origDuration / numIvals + for i in xrange(0, int(numIvals)): + track.append(Wait(timeInterval)) + track.append(ActorInterval(object, animName, startTime=startTime + addition, duration=animInterval)) + addition += animInterval + + return track + + +def lerpSuit(suit, delay, duration, reachPos, battle, trapProp): + track = Sequence() + if trapProp: + track.append(Func(safeWrtReparentTo, trapProp, battle)) + track.append(Wait(delay)) + track.append(LerpPosInterval(suit, duration, reachPos, other=battle)) + if trapProp: + if trapProp.getName() == 'traintrack': + notify.debug('UBERLURE MovieLure.lerpSuit deliberately not parenting trainTrack to suit') + else: + track.append(Func(safeWrtReparentTo, trapProp, suit)) + suit.battleTrapProp = trapProp + return track + + +def createTNTExplosionTrack(parent, explosionPoint = None, trapProp = None, relativeTo = render): + explosionTrack = Sequence() + explosion = BattleProps.globalPropPool.getProp('kapow') + explosion.setBillboardPointEye() + if not explosionPoint: + if trapProp: + explosionPoint = trapProp.getPos(relativeTo) + explosionPoint.setZ(explosionPoint.getZ() + 2.3) + else: + explosionPoint = Point3(0, 3.6, 2.1) + explosionTrack.append(Func(explosion.reparentTo, parent)) + explosionTrack.append(Func(explosion.setPos, explosionPoint)) + explosionTrack.append(Func(explosion.setScale, 0.11)) + explosionTrack.append(ActorInterval(explosion, 'kapow')) + explosionTrack.append(Func(MovieUtil.removeProp, explosion)) + return explosionTrack + + +TRAIN_STARTING_X = -7.131 +TRAIN_TUNNEL_END_X = 7.1 +TRAIN_TRAVEL_DISTANCE = 45 +TRAIN_SPEED = 35.0 +TRAIN_DURATION = TRAIN_TRAVEL_DISTANCE / TRAIN_SPEED +TRAIN_MATERIALIZE_TIME = 3 +TOTAL_TRAIN_TIME = TRAIN_DURATION + TRAIN_MATERIALIZE_TIME + +def createSuitReactionToTrain(battle, suit, hp, lure, trapProp): + toon = lure['toon'] + retval = Sequence() + suitPos, suitHpr = battle.getActorPosHpr(suit) + distance = suitPos.getX() - TRAIN_STARTING_X + timeToGetHit = distance / TRAIN_SPEED + suitTrack = Sequence() + showDamage = Func(suit.showHpText, -hp, openEnded=0) + updateHealthBar = Func(suit.updateHealthBar, hp) + anim = 'flatten' + suitReact = ActorInterval(suit, anim) + cogGettingHit = getSoundTrack('TL_train_cog.ogg', node=toon) + suitTrack.append(Func(suit.loop, 'neutral')) + suitTrack.append(Wait(timeToGetHit + TRAIN_MATERIALIZE_TIME)) + suitTrack.append(updateHealthBar) + suitTrack.append(Parallel(suitReact, cogGettingHit)) + suitTrack.append(showDamage) + curDuration = suitTrack.getDuration() + timeTillEnd = TOTAL_TRAIN_TIME - curDuration + if timeTillEnd > 0: + suitTrack.append(Wait(timeTillEnd)) + retval.append(suitTrack) + return retval + + +def createIncomingTrainInterval(battle, suit, hp, lure, trapProp): + toon = lure['toon'] + retval = Parallel() + suitTrack = createSuitReactionToTrain(battle, suit, hp, lure, trapProp) + retval.append(suitTrack) + if not trapProp.find('**/train_gag').isEmpty(): + return retval + clipper = PlaneNode('clipper') + clipper.setPlane(Plane(Vec3(1, 0, 0), Point3(TRAIN_STARTING_X, 0, 0))) + clipNP = trapProp.attachNewNode(clipper) + trapProp.setClipPlane(clipNP) + clipper2 = PlaneNode('clipper2') + clipper2.setPlane(Plane(Vec3(-1, 0, 0), Point3(TRAIN_TUNNEL_END_X, 0, 0))) + clipNP2 = trapProp.attachNewNode(clipper2) + trapProp.setClipPlane(clipNP2) + train = globalPropPool.getProp('train') + train.hide() + train.reparentTo(trapProp) + tempScale = trapProp.getScale() + trainScale = Vec3(1.0 / tempScale[0], 1.0 / tempScale[1], 1.0 / tempScale[2]) + trainIval = Sequence() + trainIval.append(Func(train.setScale, trainScale)) + trainIval.append(Func(train.setH, 90)) + trainIval.append(Func(train.setX, TRAIN_STARTING_X)) + trainIval.append(Func(train.setTransparency, 1)) + trainIval.append(Func(train.setColorScale, Point4(1, 1, 1, 0))) + trainIval.append(Func(train.show)) + tunnel2 = trapProp.find('**/tunnel3') + tunnel3 = trapProp.find('**/tunnel2') + tunnels = [tunnel2, tunnel3] + for tunnel in tunnels: + trainIval.append(Func(tunnel.setTransparency, 1)) + trainIval.append(Func(tunnel.setColorScale, Point4(1, 1, 1, 0))) + trainIval.append(Func(tunnel.setScale, Point3(1.0, 0.01, 0.01))) + trainIval.append(Func(tunnel.show)) + + materializeIval = Parallel() + materializeIval.append(LerpColorScaleInterval(train, TRAIN_MATERIALIZE_TIME, Point4(1, 1, 1, 1))) + for tunnel in tunnels: + materializeIval.append(LerpColorScaleInterval(tunnel, TRAIN_MATERIALIZE_TIME, Point4(1, 1, 1, 1))) + + for tunnel in tunnels: + tunnelScaleIval = Sequence() + tunnelScaleIval.append(LerpScaleInterval(tunnel, TRAIN_MATERIALIZE_TIME - 1.0, Point3(1.0, 2.0, 2.5))) + tunnelScaleIval.append(LerpScaleInterval(tunnel, 0.5, Point3(1.0, 3.0, 1.5))) + tunnelScaleIval.append(LerpScaleInterval(tunnel, 0.5, Point3(1.0, 2.5, 2.0))) + materializeIval.append(tunnelScaleIval) + + trainIval.append(materializeIval) + endingX = TRAIN_STARTING_X + TRAIN_TRAVEL_DISTANCE + trainIval.append(LerpPosInterval(train, TRAIN_DURATION, Point3(endingX, 0, 0), other=battle)) + trainIval.append(LerpColorScaleInterval(train, TRAIN_MATERIALIZE_TIME, Point4(1, 1, 1, 0))) + retval.append(trainIval) + trainSoundTrack = getSoundTrack('TL_train.ogg', node=toon) + retval.append(trainSoundTrack) + return retval + + +def __createSlideshowMultiTrack(lure, npcs = []): + toon = lure['toon'] + battle = lure['battle'] + sidestep = lure['sidestep'] + origHpr = toon.getHpr(battle) + slideshowDelay = 2.5 + hands = toon.getLeftHands() + endPos = toon.getPos(battle) + endPos.setY(endPos.getY() + 4) + button = globalPropPool.getProp('button') + button2 = MovieUtil.copyProp(button) + buttons = [button, button2] + toonTrack = Sequence() + toonTrack.append(Func(MovieUtil.showProps, buttons, hands)) + toonTrack.append(Func(toon.headsUp, battle, endPos)) + toonTrack.append(ActorInterval(toon, 'pushbutton')) + toonTrack.append(Func(MovieUtil.removeProps, buttons)) + toonTrack.append(Func(toon.loop, 'neutral')) + toonTrack.append(Func(toon.setHpr, battle, origHpr)) + slideShowProp = globalPropPool.getProp('slideshow') + propTrack = Sequence() + propTrack.append(Wait(slideshowDelay)) + propTrack.append(Func(slideShowProp.show)) + propTrack.append(Func(slideShowProp.setScale, Point3(0.1, 0.1, 0.1))) + propTrack.append(Func(slideShowProp.reparentTo, battle)) + propTrack.append(Func(slideShowProp.setPos, endPos)) + propTrack.append(LerpScaleInterval(slideShowProp, 1.2, Point3(1.0, 1.0, 1.0))) + shrinkDuration = 0.4 + totalDuration = 7.1 + propTrackDurationAtThisPoint = propTrack.getDuration() + waitTime = totalDuration - propTrackDurationAtThisPoint - shrinkDuration + if waitTime > 0: + propTrack.append(Wait(waitTime)) + propTrack.append(LerpScaleInterval(nodePath=slideShowProp, scale=Point3(1.0, 1.0, 0.1), duration=shrinkDuration)) + propTrack.append(Func(MovieUtil.removeProp, slideShowProp)) + tracks = Parallel(propTrack, toonTrack) + targets = lure['target'] + for target in targets: + suit = target['suit'] + trapProp = suit.battleTrapProp + if sidestep == 0: + hp = target['hp'] + kbbonus = target['kbbonus'] + died = target['died'] + revived = target['revived'] + if kbbonus == 1 or hp > 0: + suitTrack = Sequence() + suitDelay = 3.8 + suitAnimDuration = 1.5 + opos, ohpr = battle.getActorPosHpr(suit) + reachDist = MovieUtil.SUIT_LURE_DISTANCE + reachPos = Point3(opos[0], opos[1] - reachDist, opos[2]) + suitTrack.append(Func(suit.loop, 'neutral')) + suitTrack.append(Wait(suitDelay)) + suitTrack.append(ActorInterval(suit, 'hypnotized', duration=3.1)) + suitTrack.append(Func(suit.setPos, battle, reachPos)) + suitTrack.append(Func(suit.loop, 'lured')) + suitTrack.append(Func(battle.lureSuit, suit)) + if hp > 0: + suitTrack.append(__createSuitDamageTrack(battle, suit, hp, lure, trapProp)) + if revived != 0: + suitTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle, npcs)) + elif died != 0: + suitTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle, npcs)) + tracks.append(suitTrack) + tracks.append(lerpSuit(suit, suitDelay + 1.7, 0.7, reachPos, battle, trapProp)) + else: + tracks.append(Sequence(Wait(2.3), Func(MovieUtil.indicateMissed, suit, 1.1))) + + tracks.append(getSoundTrack('TL_presentation.ogg', delay=2.3, node=toon)) + tracks.append(getSoundTrack('AA_drop_trigger_box.ogg', delay=slideshowDelay, node=toon)) + return tracks diff --git a/toontown/battle/MovieNPCSOS.py b/toontown/battle/MovieNPCSOS.py new file mode 100755 index 00000000..47815ec5 --- /dev/null +++ b/toontown/battle/MovieNPCSOS.py @@ -0,0 +1,279 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +import random + +import BattleParticles +from BattleProps import * +from BattleSounds import * +import MovieCamera +import MovieUtil +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals +from toontown.toon import NPCToons +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownBattleGlobals + + +notify = DirectNotifyGlobal.directNotify.newCategory('MovieNPCSOS') +soundFiles = ('AA_heal_tickle.ogg', 'AA_heal_telljoke.ogg', 'AA_heal_smooch.ogg', 'AA_heal_happydance.ogg', 'AA_heal_pixiedust.ogg', 'AA_heal_juggle.ogg') +offset = Point3(0, 4.0, 0) + +def __cogsMiss(attack, level, hp): + return __doCogsMiss(attack, level, hp) + + +def __toonsHit(attack, level, hp): + return __doToonsHit(attack, level, hp) + + +def __restockGags(attack, level, hp): + return __doRestockGags(attack, level, hp) + + +NPCSOSfn_dict = {ToontownBattleGlobals.NPC_COGS_MISS: __cogsMiss, + ToontownBattleGlobals.NPC_TOONS_HIT: __toonsHit, + ToontownBattleGlobals.NPC_RESTOCK_GAGS: __restockGags} + +def doNPCSOSs(NPCSOSs): + if len(NPCSOSs) == 0: + return (None, None) + track = Sequence() + textTrack = Sequence() + for n in NPCSOSs: + ival, textIval = __doNPCSOS(n) + if ival: + track.append(ival) + textTrack.append(textIval) + + camDuration = track.getDuration() + if camDuration > 0.0: + camTrack = MovieCamera.chooseHealShot(NPCSOSs, camDuration) + else: + camTrack = Sequence() + return (track, Parallel(camTrack, textTrack)) + + +def __doNPCSOS(sos): + npcId = sos['npcId'] + track, level, hp = NPCToons.getNPCTrackLevelHp(npcId) + if track != None: + return NPCSOSfn_dict[track](sos, level, hp) + else: + return __cogsMiss(sos, 0, 0) + return + + +def __healToon(toon, hp, ineffective = 0): + notify.debug('healToon() - toon: %d hp: %d ineffective: %d' % (toon.doId, hp, ineffective)) + if ineffective == 1: + laughter = random.choice(TTLocalizer.MovieHealLaughterMisses) + else: + maxDam = ToontownBattleGlobals.AvPropDamage[0][1][0][1] + if hp >= maxDam - 1: + laughter = random.choice(TTLocalizer.MovieHealLaughterHits2) + else: + laughter = random.choice(TTLocalizer.MovieHealLaughterHits1) + toon.setChatAbsolute(laughter, CFSpeech | CFTimeout) + + +def __getSoundTrack(level, delay, duration = None, node = None): + soundEffect = globalBattleSoundCache.getSound(soundFiles[level]) + soundIntervals = Sequence() + if soundEffect: + if duration: + playSound = SoundInterval(soundEffect, duration=duration, node=node) + else: + playSound = SoundInterval(soundEffect, node=node) + soundIntervals.append(Wait(delay)) + soundIntervals.append(playSound) + return soundIntervals + + +def teleportIn(attack, npc, pos = Point3(0, 0, 0), hpr = Vec3(180.0, 0.0, 0.0)): + a = Func(npc.reparentTo, attack['battle']) + b = Func(npc.setPos, pos) + c = Func(npc.setHpr, hpr) + d = Func(npc.pose, 'teleport', npc.getNumFrames('teleport') - 1) + e = npc.getTeleportInTrack() + ee = Func(npc.addActive) + f = Func(npc.setChatAbsolute, TTLocalizer.MovieNPCSOSGreeting % attack['toon'].getName(), CFSpeech | CFTimeout) + g = ActorInterval(npc, 'wave') + h = Func(npc.loop, 'neutral') + seq = Sequence(a, b, c, d, e, ee, f, g, h) + seq.append(Func(npc.clearChat)) + if npc.getName() == 'Magic Cat': + magicCatTrack = Sequence() + magicCatTrack.append(Func(npc.setChatAbsolute, "I've got this, so start dancing!", CFSpeech | CFTimeout)) + magicCatTrack.append(Func(attack['toon'].loop, 'victory')) + seq.append(magicCatTrack) + return seq + + +def teleportOut(attack, npc): + if npc.style.getGender() == 'm': + a = ActorInterval(npc, 'bow') + else: + a = ActorInterval(npc, 'curtsy') + b = Func(npc.setChatAbsolute, TTLocalizer.MovieNPCSOSGoodbye, CFSpeech | CFTimeout) + c = npc.getTeleportOutTrack() + seq = Sequence(a, b, c) + seq.append(Func(npc.removeActive)) + seq.append(Func(npc.detachNode)) + seq.append(Func(npc.delete)) + return seq + +def __getPartTrack(particleEffect, startDelay, durationDelay, partExtraArgs): + pEffect = partExtraArgs[0] + parent = partExtraArgs[1] + if len(partExtraArgs) == 3: + worldRelative = partExtraArgs[2] + else: + worldRelative = 1 + return Sequence(Wait(startDelay), ParticleInterval(pEffect, parent, worldRelative, duration=durationDelay, cleanup=True)) + + +def __doSprinkle(attack, recipients, hp = 0): + toon = NPCToons.createLocalNPC(attack['npcId']) + if toon == None: + return + targets = attack[recipients] + level = 4 + battle = attack['battle'] + track = Sequence(teleportIn(attack, toon)) + + def face90(target, toon, battle): + vec = Point3(target.getPos(battle) - toon.getPos(battle)) + vec.setZ(0) + temp = vec[0] + vec.setX(-vec[1]) + vec.setY(temp) + targetPoint = Point3(toon.getPos(battle) + vec) + toon.headsUp(battle, targetPoint) + + delay = 2.5 + effectTrack = Sequence() + for target in targets: + sprayEffect = BattleParticles.createParticleEffect(file='pixieSpray') + dropEffect = BattleParticles.createParticleEffect(file='pixieDrop') + explodeEffect = BattleParticles.createParticleEffect(file='pixieExplode') + poofEffect = BattleParticles.createParticleEffect(file='pixiePoof') + wallEffect = BattleParticles.createParticleEffect(file='pixieWall') + mtrack = Parallel(__getPartTrack(sprayEffect, 1.5, 0.5, [sprayEffect, toon, 0]), __getPartTrack(dropEffect, 1.9, 2.0, [dropEffect, target, 0]), __getPartTrack(explodeEffect, 2.7, 1.0, [explodeEffect, toon, 0]), __getPartTrack(poofEffect, 3.4, 1.0, [poofEffect, target, 0]), __getPartTrack(wallEffect, 4.05, 1.2, [wallEffect, toon, 0]), __getSoundTrack(level, 2, duration=3.1, node=toon), Sequence(Func(face90, target, toon, battle), ActorInterval(toon, 'sprinkle-dust')), Sequence(Wait(delay), Func(__healToon, target, hp))) + effectTrack.append(mtrack) + + track.append(effectTrack) + track.append(Func(toon.setHpr, Vec3(180.0, 0.0, 0.0))) + track.append(teleportOut(attack, toon)) + return track + + +def __doSmooch(attack, hp = 0): + toon = NPCToons.createLocalNPC(attack['npcId']) + if toon == None: + return + targets = attack['toons'] + level = 2 + battle = attack['battle'] + track = Sequence(teleportIn(attack, toon)) + lipstick = globalPropPool.getProp('lipstick') + lipstick2 = MovieUtil.copyProp(lipstick) + lipsticks = [lipstick, lipstick2] + rightHands = toon.getRightHands() + dScale = 0.5 + lipstickTrack = Sequence(Func(MovieUtil.showProps, lipsticks, rightHands, Point3(-0.27, -0.24, -0.95), Point3(-118, -10.6, -25.9)), MovieUtil.getScaleIntervals(lipsticks, dScale, MovieUtil.PNT3_NEARZERO, MovieUtil.PNT3_ONE), Wait(toon.getDuration('smooch') - 2.0 * dScale), MovieUtil.getScaleIntervals(lipsticks, dScale, MovieUtil.PNT3_ONE, MovieUtil.PNT3_NEARZERO)) + lips = globalPropPool.getProp('lips') + dScale = 0.5 + tLips = 2.5 + tThrow = 115.0 / toon.getFrameRate('smooch') + dThrow = 0.5 + + def getLipPos(toon = toon): + toon.pose('smooch', 57) + toon.update(0) + hand = toon.getRightHands()[0] + return hand.getPos(render) + + effectTrack = Sequence() + for target in targets: + lipcopy = MovieUtil.copyProp(lips) + lipsTrack = Sequence(Wait(tLips), Func(MovieUtil.showProp, lipcopy, render, getLipPos), Func(lipcopy.setBillboardPointWorld), LerpScaleInterval(lipcopy, dScale, Point3(3, 3, 3), startScale=MovieUtil.PNT3_NEARZERO), Wait(tThrow - tLips - dScale), LerpPosInterval(lipcopy, dThrow, Point3(target.getPos() + Point3(0, 0, target.getHeight()))), Func(MovieUtil.removeProp, lipcopy)) + delay = tThrow + dThrow + mtrack = Parallel(lipstickTrack, lipsTrack, __getSoundTrack(level, 2, node=toon), Sequence(ActorInterval(toon, 'smooch')), Sequence(Wait(delay), ActorInterval(target, 'conked')), Sequence(Wait(delay), Func(__healToon, target, hp))) + effectTrack.append(mtrack) + + effectTrack.append(Func(MovieUtil.removeProps, lipsticks)) + track.append(effectTrack) + track.append(teleportOut(attack, toon)) + track.append(Func(target.clearChat)) + return track + + +def __doToonsHit(attack, level, hp): + track = __doSprinkle(attack, 'toons', hp) + pbpText = attack['playByPlayText'] + pbpTrack = pbpText.getShowInterval(TTLocalizer.MovieNPCSOSToonsHit, track.getDuration()) + return (track, pbpTrack) + + +def __doCogsMiss(attack, level, hp): + track = __doSprinkle(attack, 'suits', hp) + pbpText = attack['playByPlayText'] + pbpTrack = pbpText.getShowInterval(TTLocalizer.MovieNPCSOSCogsMiss, track.getDuration()) + return (track, pbpTrack) + + +def __doRestockGags(attack, level, hp): + track = __doSmooch(attack, hp) + pbpText = attack['playByPlayText'] + if level == ToontownBattleGlobals.HEAL_TRACK: + text = TTLocalizer.MovieNPCSOSHeal + elif level == ToontownBattleGlobals.TRAP_TRACK: + text = TTLocalizer.MovieNPCSOSTrap + elif level == ToontownBattleGlobals.LURE_TRACK: + text = TTLocalizer.MovieNPCSOSLure + elif level == ToontownBattleGlobals.SOUND_TRACK: + text = TTLocalizer.MovieNPCSOSSound + elif level == ToontownBattleGlobals.THROW_TRACK: + text = TTLocalizer.MovieNPCSOSThrow + elif level == ToontownBattleGlobals.SQUIRT_TRACK: + text = TTLocalizer.MovieNPCSOSSquirt + elif level == ToontownBattleGlobals.DROP_TRACK: + text = TTLocalizer.MovieNPCSOSDrop + elif level == -1: + text = TTLocalizer.MovieNPCSOSAll + pbpTrack = pbpText.getShowInterval(TTLocalizer.MovieNPCSOSRestockGags % text, track.getDuration()) + return (track, pbpTrack) + + +def doNPCTeleports(attacks): + npcs = [] + npcDatas = [] + arrivals = Sequence() + departures = Parallel() + for attack in attacks: + if 'npcId' in attack: + npcId = attack['npcId'] + npc = NPCToons.createLocalNPC(npcId) + if npc != None: + npcs.append(npc) + attack['npc'] = npc + toon = attack['toon'] + battle = attack['battle'] + pos = toon.getPos(battle) + offset + hpr = toon.getHpr(battle) + npcDatas.append((npc, battle, hpr)) + arrival = teleportIn(attack, npc, pos=pos) + arrivals.append(arrival) + departure = teleportOut(attack, npc) + departures.append(departure) + + turns = Parallel() + unturns = Parallel() + hpr = Vec3(180.0, 0, 0) + for npc in npcDatas: + turns.append(Func(npc[0].setHpr, npc[1], npc[2])) + unturns.append(Func(npc[0].setHpr, npc[1], hpr)) + + arrivals.append(turns) + unturns.append(departures) + return (arrivals, unturns, npcs) diff --git a/toontown/battle/MoviePetSOS.py b/toontown/battle/MoviePetSOS.py new file mode 100755 index 00000000..fa12e638 --- /dev/null +++ b/toontown/battle/MoviePetSOS.py @@ -0,0 +1,134 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +import random + +import BattleParticles +from BattleProps import * +from BattleSounds import * +import MovieCamera +import MovieUtil +from otp.nametag.NametagConstants import * +from toontown.pets import Pet, PetTricks +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownBattleGlobals + + +notify = DirectNotifyGlobal.directNotify.newCategory('MoviePetSOS') +soundFiles = ('AA_heal_tickle.ogg', 'AA_heal_telljoke.ogg', 'AA_heal_smooch.ogg', 'AA_heal_happydance.ogg', 'AA_heal_pixiedust.ogg', 'AA_heal_juggle.ogg') +offset = Point3(0, 4.0, 0) + +def doPetSOSs(PetSOSs): + if len(PetSOSs) == 0: + return (None, None) + track = Sequence() + textTrack = Sequence() + for p in PetSOSs: + ival = __doPetSOS(p) + if ival: + track.append(ival) + + camDuration = track.getDuration() + camTrack = MovieCamera.chooseHealShot(PetSOSs, camDuration) + return (track, camTrack) + + +def __healToon(toon, hp, gender, callerToonId, ineffective = 0): + notify.debug('healToon() - toon: %d hp: %d ineffective: %d' % (toon.doId, hp, ineffective)) + noLaughter = 0 + if ineffective == 1: + if callerToonId == toon.doId: + laughter = TTLocalizer.MoviePetSOSTrickFail + else: + noLaughter = 1 + else: + maxDam = ToontownBattleGlobals.AvPropDamage[0][1][0][1] + if callerToonId == toon.doId: + if gender == 1: + laughter = TTLocalizer.MoviePetSOSTrickSucceedBoy + else: + laughter = TTLocalizer.MoviePetSOSTrickSucceedGirl + elif hp >= maxDam - 1: + laughter = random.choice(TTLocalizer.MovieHealLaughterHits2) + else: + laughter = random.choice(TTLocalizer.MovieHealLaughterHits1) + if not noLaughter: + toon.setChatAbsolute(laughter, CFSpeech | CFTimeout) + if hp > 0 and toon.hp != None: + toon.toonUp(hp) + else: + notify.debug('__healToon() - toon: %d hp: %d' % (toon.doId, hp)) + + +def __teleportIn(attack, pet, pos = Point3(0, 0, 0), hpr = Vec3(180.0, 0.0, 0.0)): + callSfx = loader.loadSfx('phase_5.5/audio/sfx/call_pet.ogg') + toon = attack['toon'] + seq = Sequence() + + seq.append(Func(toon.clearChat)) + seq.append(Func(callSfx.play)) + seq.append(ActorInterval(toon, 'callPet')) + seq.append(Func(toon.loop, 'neutral')) + seq.append(Func(pet.reparentTo, attack['battle'])) + seq.append(Func(pet.setPos, pos)) + seq.append(Func(pet.setHpr, hpr)) + seq.append(Func(pet.pose, 'reappear', 0)) + seq.append(pet.getTeleportInTrack()) + seq.append(Func(toon.setSC, 21200 + attack['level'])) + seq.append(Func(pet.loop, 'neutral')) + seq.append(Func(loader.unloadSfx, callSfx)) + + return seq + + +def __teleportOut(attack, pet): + a = pet.getTeleportOutTrack() + c = Func(pet.detachNode) + d = Func(pet.delete) + return Sequence(a, c) + + +def __doPetSOS(heal): + petProxyId = heal['petId'] + pet = Pet.Pet() + gender = 0 + if petProxyId in base.cr.doId2do: + petProxy = base.cr.doId2do[petProxyId] + if petProxy == None: + return + pet.setDNA(petProxy.style) + pet.setName(petProxy.petName) + gender = petProxy.gender + else: + pet.setDNA([-1, 0, 0, -1, 2, 0, 4, 0, 1]) + pet.setName(TTLocalizer.DefaultDoodleName) + targets = heal['target'] + ineffective = heal['sidestep'] + level = heal['level'] + track = Sequence(__teleportIn(heal, pet)) + if ineffective: + trickTrack = Parallel(Wait(1.0), Func(pet.loop, 'neutralSad'), Func(pet.showMood, 'confusion')) + else: + trickTrack = PetTricks.getTrickIval(pet, level) + track.append(trickTrack) + delay = 4.0 + first = 1 + targetTrack = Sequence() + for target in targets: + targetToon = target['toon'] + hp = target['hp'] + callerToonId = heal['toonId'] + reactIval = Func(__healToon, targetToon, hp, gender, callerToonId, ineffective) + if first == 1: + first = 0 + targetTrack.append(reactIval) + + mtrack = Parallel(Wait(2.0), targetTrack) + track.append(mtrack) + track.append(Sequence(Func(pet.clearMood))) + track.append(__teleportOut(heal, pet)) + for target in targets: + targetToon = target['toon'] + track.append(Func(targetToon.clearChat)) + + track.append(Func(pet.delete)) + return track diff --git a/toontown/battle/MovieSOS.py b/toontown/battle/MovieSOS.py new file mode 100755 index 00000000..d486d85f --- /dev/null +++ b/toontown/battle/MovieSOS.py @@ -0,0 +1,48 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from panda3d.core import * + +import MovieCamera +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals +from toontown.toonbase import TTLocalizer + + +notify = DirectNotifyGlobal.directNotify.newCategory('MovieSOS') + + +def doSOSs(calls): + if len(calls) == 0: + return (None, None) + + def callerFunc(toon, handle): + toon.setChatAbsolute(TTLocalizer.MovieSOSCallHelp % handle.getName(), CFSpeech | CFTimeout) + handle.d_battleSOS(handle.doId) + + def calleeFunc(toon, handle): + toon.setChatAbsolute(TTLocalizer.MovieSOSCallHelp % handle.getName(), CFSpeech | CFTimeout) + + def observerFunc(toon): + toon.setChatAbsolute(TTLocalizer.MovieSOSObserverHelp, CFSpeech | CFTimeout) + + mtrack = Sequence() + for c in calls: + toon = c['toon'] + targetType = c['targetType'] + handle = c['target'] + mtrack.append(Wait(0.5)) + if targetType == 'observer': + ival = Func(observerFunc, toon) + elif targetType == 'caller': + ival = Func(callerFunc, toon, handle) + elif targetType == 'callee': + ival = Func(calleeFunc, toon, handle) + else: + notify.error('invalid target type: %s' % targetType) + mtrack.append(ival) + mtrack.append(Wait(2.0)) + notify.debug('toon: %s calls for help' % toon.getName()) + + camDuration = mtrack.getDuration() + camTrack = MovieCamera.chooseSOSShot(toon, camDuration) + return (mtrack, camTrack) diff --git a/toontown/battle/MovieSound.py b/toontown/battle/MovieSound.py new file mode 100755 index 00000000..3ab93d6e --- /dev/null +++ b/toontown/battle/MovieSound.py @@ -0,0 +1,631 @@ +from direct.interval.IntervalGlobal import * +from BattleBase import * +from BattleProps import * +from BattleSounds import * +import BattleParticles +from RewardPanel import * +import MovieCamera +from direct.directnotify import DirectNotifyGlobal +import MovieUtil +import MovieNPCSOS +from toontown.toonbase import ToontownBattleGlobals +notify = DirectNotifyGlobal.directNotify.newCategory('MovieSound') +soundFiles = ('AA_sound_bikehorn.ogg', 'AA_sound_whistle.ogg', 'AA_sound_bugle.ogg', 'AA_sound_aoogah.ogg', 'AA_sound_elephant.ogg', 'SZ_DD_foghorn.ogg', 'AA_sound_Opera_Singer.ogg') +appearSoundFiles = ('MG_tag_1.ogg', 'LB_receive_evidence.ogg', 'm_match_trumpet.ogg', 'TL_step_on_rake.ogg', 'toonbldg_grow.ogg', 'mailbox_full_wobble.ogg', 'mailbox_full_wobble.ogg') +hitSoundFiles = ('AA_sound_Opera_Singer_Cog_Glass.ogg',) +tSound = 2.45 +tSuitReact = 2.8 +DISTANCE_TO_WALK_BACK = MovieUtil.SUIT_LURE_DISTANCE * 0.75 +TIME_TO_WALK_BACK = 0.5 +if DISTANCE_TO_WALK_BACK == 0: + TIME_TO_WALK_BACK = 0 +INSTRUMENT_SCALE_MODIFIER = 0.5 +BEFORE_STARS = 0.5 +AFTER_STARS = 1.75 + +def doSounds(sounds): + if len(sounds) == 0: + return (None, None) + npcArrivals, npcDepartures, npcs = MovieNPCSOS.doNPCTeleports(sounds) + mtrack = Parallel() + hitCount = 0 + prevLevel = 0 + prevSounds = [[], + [], + [], + [], + [], + [], + []] + for sound in sounds: + level = sound['level'] + prevSounds[level].append(sound) + for target in sound['target']: + if target['hp'] > 0: + hitCount += 1 + break + + delay = 0.0 + for soundList in prevSounds: + if len(soundList) > 0: + mtrack.append(__doSoundsLevel(soundList, delay, hitCount, npcs)) + delay += TOON_SOUND_DELAY + + soundTrack = Sequence(npcArrivals, mtrack, npcDepartures) + targets = sounds[0]['target'] + camDuration = mtrack.getDuration() + enterDuration = npcArrivals.getDuration() + exitDuration = npcDepartures.getDuration() + camTrack = MovieCamera.chooseSoundShot(sounds, targets, camDuration, enterDuration, exitDuration) + return (soundTrack, camTrack) + + +def __getSuitTrack(sound, lastSoundThatHit, delay, hitCount, targets, totalDamage, hpbonus, toon, npcs): + tracks = Parallel() + attacks = 0 + uberDelay = 0.0 + isUber = 0 + if sound['level'] >= ToontownBattleGlobals.UBER_GAG_LEVEL_INDEX: + uberDelay = 3.0 + isUber = 1 + for target in targets: + suit = target['suit'] + if totalDamage > 0 and sound == lastSoundThatHit: + hp = target['hp'] + died = target['died'] + battle = sound['battle'] + kbbonus = target['kbbonus'] + suitTrack = Sequence() + showDamage = Func(suit.showHpText, -totalDamage, openEnded=0) + updateHealthBar = Func(suit.updateHealthBar, totalDamage) + if isUber: + breakEffect = BattleParticles.createParticleEffect(file='soundBreak') + breakEffect.setDepthWrite(0) + breakEffect.setDepthTest(0) + breakEffect.setTwoSided(1) + breakEffect.setBin('fixed', 10) + soundEffect = globalBattleSoundCache.getSound(hitSoundFiles[0]) + suitTrack.append(Wait(delay + tSuitReact)) + if isUber: + delayTime = random.random() + suitTrack.append(Wait(delayTime + 2.0)) + suitTrack.append(Func(setPosFromOther, breakEffect, suit, Point3(0, 0.0, suit.getHeight() - 1.0))) + suitTrack.append(Parallel(showDamage, updateHealthBar, SoundInterval(soundEffect, node=suit), __getPartTrack(breakEffect, 0.0, 1.0, [breakEffect, suit, 0], softStop=-0.5))) + else: + suitTrack.append(showDamage) + suitTrack.append(updateHealthBar) + if hitCount == 1: + suitTrack.append(Parallel(ActorInterval(suit, 'squirt-small-react'), MovieUtil.createSuitStunInterval(suit, 0.5, 1.8))) + else: + suitTrack.append(ActorInterval(suit, 'squirt-small-react')) + if kbbonus == 0: + suitTrack.append(__createSuitResetPosTrack(suit, battle)) + suitTrack.append(Func(battle.unlureSuit, suit)) + bonusTrack = None + if hpbonus > 0: + bonusTrack = Sequence(Wait(delay + tSuitReact + delay + 0.75 + uberDelay), Func(suit.showHpText, -hpbonus, 1, openEnded=0), Func(suit.updateHealthBar, hpbonus)) + suitTrack.append(Func(suit.loop, 'neutral')) + if bonusTrack == None: + tracks.append(suitTrack) + else: + tracks.append(Parallel(suitTrack, bonusTrack)) + elif totalDamage <= 0: + tracks.append(Sequence(Wait(2.9), Func(MovieUtil.indicateMissed, suit, 1.0))) + + return tracks + + +def __doSoundsLevel(sounds, delay, hitCount, npcs): + lastSoundThatHit = None + totalDamage = 0 + for sound in sounds: + for target in sound['target']: + if target['hp'] > 0: + lastSoundThatHit = sound + totalDamage += target['hp'] + break + + mainTrack = Sequence() + tracks = Parallel() + deathTracks = Parallel() + for sound in sounds: + toon = sound['toon'] + if 'npc' in sound: + toon = sound['npc'] + level = sound['level'] + targets = sound['target'] + hpbonus = sound['hpbonus'] + attackMTrack = soundfn_array[sound['level']](sound, delay, toon, targets, level) + tracks.append(Sequence(Wait(delay), attackMTrack)) + tracks.append(__getSuitTrack(sound, lastSoundThatHit, delay, hitCount, targets, totalDamage, hpbonus, toon, npcs)) + for target in targets: + battle = sound['battle'] + suit = target['suit'] + died = target['died'] + revived = target['revived'] + if revived: + deathTracks.append(MovieUtil.createSuitReviveTrack(suit, toon, battle, npcs)) + elif died: + deathTracks.append(MovieUtil.createSuitDeathTrack(suit, toon, battle, npcs)) + + mainTrack.append(tracks) + mainTrack.append(deathTracks) + return mainTrack + + +def __createSuitResetPosTrack(suit, battle): + resetPos, resetHpr = battle.getActorPosHpr(suit) + moveDist = Vec3(suit.getPos(battle) - resetPos).length() + moveDuration = 0.5 + walkTrack = Sequence(Func(suit.setHpr, battle, resetHpr), ActorInterval(suit, 'walk', startTime=1, duration=moveDuration, endTime=0.0001), Func(suit.loop, 'neutral')) + moveTrack = LerpPosInterval(suit, moveDuration, resetPos, other=battle) + return Parallel(walkTrack, moveTrack) + + +def createSuitResetPosTrack(suit, battle): + return __createSuitResetPosTrack(suit, battle) + + +def __createToonInterval(sound, delay, toon, operaInstrument = None): + isNPC = 0 + if sound.get('npc'): + isNPC = 1 + battle = sound['battle'] + hasLuredSuits = __hasLuredSuits(sound) + if not isNPC: + oldPos, oldHpr = battle.getActorPosHpr(toon) + newPos = Point3(oldPos) + newPos.setY(newPos.getY() - DISTANCE_TO_WALK_BACK) + retval = Sequence(Wait(delay)) + if DISTANCE_TO_WALK_BACK and hasLuredSuits and not isNPC: + retval.append(Parallel(ActorInterval(toon, 'walk', startTime=1, duration=TIME_TO_WALK_BACK, endTime=0.0001), LerpPosInterval(toon, TIME_TO_WALK_BACK, newPos, other=battle))) + if operaInstrument: + sprayEffect = BattleParticles.createParticleEffect(file='soundWave') + sprayEffect.setDepthWrite(0) + sprayEffect.setDepthTest(0) + sprayEffect.setTwoSided(1) + I1 = 2.8 + retval.append(ActorInterval(toon, 'sound', playRate=1.0, startTime=0.0, endTime=I1)) + retval.append(Func(setPosFromOther, sprayEffect, operaInstrument, (0, 1.6, -0.18))) + retval.append(__getPartTrack(sprayEffect, 0.0, 6.0, [sprayEffect, toon, 0], softStop=-3.5)) + retval.append(ActorInterval(toon, 'sound', playRate=1.0, startTime=I1)) + else: + retval.append(ActorInterval(toon, 'sound')) + if DISTANCE_TO_WALK_BACK and hasLuredSuits and not isNPC: + retval.append(Parallel(ActorInterval(toon, 'walk', startTime=0.0001, duration=TIME_TO_WALK_BACK, endTime=1), LerpPosInterval(toon, TIME_TO_WALK_BACK, oldPos, other=battle))) + retval.append(Func(toon.loop, 'neutral')) + return retval + + +def __hasLuredSuits(sound): + retval = False + targets = sound['target'] + for target in targets: + kbbonus = target['kbbonus'] + if kbbonus == 0: + retval = True + break + + return retval + + +def __doBikehorn(sound, delay, toon, targets, level): + tracks = Parallel() + instrMin = Vec3(0.001, 0.001, 0.001) + instrMax = Vec3(0.65, 0.65, 0.65) + instrMax *= INSTRUMENT_SCALE_MODIFIER + instrStretch = Vec3(0.6, 1.1, 0.6) + instrStretch *= INSTRUMENT_SCALE_MODIFIER + megaphone = globalPropPool.getProp('megaphone') + megaphone2 = MovieUtil.copyProp(megaphone) + megaphones = [megaphone, megaphone2] + instrument = globalPropPool.getProp('bikehorn') + instrument2 = MovieUtil.copyProp(instrument) + instruments = [instrument, instrument2] + + def setInstrumentStats(instrument = instrument, instrument2 = instrument2): + instrument.setPos(-1.1, -1.4, 0.1) + instrument.setHpr(145, 0, 0) + instrument.setScale(instrMin) + instrument2.setPos(-1.1, -1.4, 0.1) + instrument2.setHpr(145, 0, 0) + instrument2.setScale(instrMin) + + hands = toon.getRightHands() + megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats)) + megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments)) + instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level]) + grow = getScaleIntervals(instruments, duration=0.2, startScale=instrMin, endScale=instrMax) + instrumentAppear = Parallel(grow, Sequence(Wait(0.15), SoundInterval(instrumentAppearSfx, node=toon))) + stretchInstr = getScaleBlendIntervals(instruments, duration=0.2, startScale=instrMax, endScale=instrStretch, blendType='easeOut') + backInstr = getScaleBlendIntervals(instruments, duration=0.2, startScale=instrStretch, endScale=instrMax, blendType='easeIn') + stretchMega = getScaleBlendIntervals(megaphones, duration=0.2, startScale=megaphone.getScale(), endScale=0.9, blendType='easeOut') + backMega = getScaleBlendIntervals(megaphones, duration=0.2, startScale=0.9, endScale=megaphone.getScale(), blendType='easeIn') + attackTrack = Parallel(Sequence(stretchInstr, backInstr), Sequence(stretchMega, backMega)) + hasLuredSuits = __hasLuredSuits(sound) + delayTime = delay + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + megaphoneTrack = Sequence(Wait(delayTime), megaphoneShow, Wait(1.0), instrumentAppear, Wait(3.0), megaphoneHide) + tracks.append(megaphoneTrack) + toonTrack = __createToonInterval(sound, delay, toon) + tracks.append(toonTrack) + soundEffect = globalBattleSoundCache.getSound(soundFiles[level]) + instrumentshrink = getScaleIntervals(instruments, duration=0.1, startScale=instrMax, endScale=instrMin) + if soundEffect: + delayTime = delay + tSound + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + soundTrack = Sequence(Wait(delayTime), Parallel(attackTrack, SoundInterval(soundEffect, node=toon)), Wait(0.2), instrumentshrink) + tracks.append(soundTrack) + return tracks + + +def __doWhistle(sound, delay, toon, targets, level): + tracks = Parallel() + instrMin = Vec3(0.001, 0.001, 0.001) + instrMax = Vec3(0.2, 0.2, 0.2) + instrMax *= INSTRUMENT_SCALE_MODIFIER + instrStretch = Vec3(0.25, 0.25, 0.25) + instrStretch *= INSTRUMENT_SCALE_MODIFIER + megaphone = globalPropPool.getProp('megaphone') + megaphone2 = MovieUtil.copyProp(megaphone) + megaphones = [megaphone, megaphone2] + instrument = globalPropPool.getProp('whistle') + instrument2 = MovieUtil.copyProp(instrument) + instruments = [instrument, instrument2] + + def setInstrumentStats(instrument = instrument, instrument2 = instrument2): + instrument.setPos(-1.2, -1.3, 0.1) + instrument.setHpr(145, 0, 85) + instrument.setScale(instrMin) + instrument2.setPos(-1.2, -1.3, 0.1) + instrument2.setHpr(145, 0, 85) + instrument2.setScale(instrMin) + + hands = toon.getRightHands() + megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats)) + megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments)) + instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level]) + grow = getScaleIntervals(instruments, duration=0.2, startScale=instrMin, endScale=instrMax) + instrumentAppear = Parallel(grow, Sequence(Wait(0.05), SoundInterval(instrumentAppearSfx, node=toon))) + stretchInstr = getScaleBlendIntervals(instruments, duration=0.2, startScale=instrMax, endScale=instrStretch, blendType='easeOut') + backInstr = getScaleBlendIntervals(instruments, duration=0.2, startScale=instrStretch, endScale=instrMax, blendType='easeIn') + attackTrack = Sequence(stretchInstr, backInstr) + hasLuredSuits = __hasLuredSuits(sound) + delayTime = delay + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + megaphoneTrack = Sequence(Wait(delayTime), megaphoneShow, Wait(1.0), instrumentAppear, Wait(3.0), megaphoneHide) + tracks.append(megaphoneTrack) + toonTrack = __createToonInterval(sound, delay, toon) + tracks.append(toonTrack) + soundEffect = globalBattleSoundCache.getSound(soundFiles[level]) + instrumentshrink = getScaleIntervals(instruments, duration=0.1, startScale=instrMax, endScale=instrMin) + if soundEffect: + delayTime = delay + tSound + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + soundTrack = Sequence(Wait(delayTime), Parallel(attackTrack, SoundInterval(soundEffect, node=toon)), Wait(0.2), instrumentshrink) + tracks.append(soundTrack) + return tracks + + +def __doBugle(sound, delay, toon, targets, level): + tracks = Parallel() + instrMin = Vec3(0.001, 0.001, 0.001) + instrMax = Vec3(0.4, 0.4, 0.4) + instrMax *= INSTRUMENT_SCALE_MODIFIER + instrStretch = Vec3(0.5, 0.5, 0.5) + instrStretch *= INSTRUMENT_SCALE_MODIFIER + megaphone = globalPropPool.getProp('megaphone') + megaphone2 = MovieUtil.copyProp(megaphone) + megaphones = [megaphone, megaphone2] + instrument = globalPropPool.getProp('bugle') + instrument2 = MovieUtil.copyProp(instrument) + instruments = [instrument, instrument2] + + def setInstrumentStats(instrument = instrument, instrument2 = instrument2): + instrument.setPos(-1.3, -1.4, 0.1) + instrument.setHpr(145, 0, 85) + instrument.setScale(instrMin) + instrument2.setPos(-1.3, -1.4, 0.1) + instrument2.setHpr(145, 0, 85) + instrument2.setScale(instrMin) + + def longshake(models, num): + inShake = getScaleBlendIntervals(models, duration=0.2, startScale=instrMax, endScale=instrStretch, blendType='easeInOut') + outShake = getScaleBlendIntervals(models, duration=0.2, startScale=instrStretch, endScale=instrMax, blendType='easeInOut') + i = 1 + seq = Sequence() + while i < num: + if i % 2 == 0: + seq.append(inShake) + else: + seq.append(outShake) + i += 1 + + seq.start() + + hands = toon.getRightHands() + megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats)) + megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments)) + instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level]) + grow = getScaleBlendIntervals(instruments, duration=1, startScale=instrMin, endScale=instrMax, blendType='easeInOut') + instrumentshrink = getScaleIntervals(instruments, duration=0.1, startScale=instrMax, endScale=instrMin) + instrumentAppear = Sequence(grow, Wait(0), Func(longshake, instruments, 5)) + hasLuredSuits = __hasLuredSuits(sound) + delayTime = delay + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + soundEffect = globalBattleSoundCache.getSound(soundFiles[level]) + megaphoneTrack = Parallel(Sequence(Wait(delay + 1.7), SoundInterval(soundEffect, node=toon)), Sequence(Wait(delayTime), megaphoneShow, Wait(1.7), instrumentAppear, Wait(1), instrumentshrink, Wait(1.5), megaphoneHide)) + tracks.append(megaphoneTrack) + toonTrack = __createToonInterval(sound, delay, toon) + tracks.append(toonTrack) + if soundEffect: + delayTime = delay + tSound + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + soundTrack = Wait(delayTime) + tracks.append(soundTrack) + return tracks + + +def __doAoogah(sound, delay, toon, targets, level): + tracks = Parallel() + instrMin = Vec3(0.001, 0.001, 0.001) + instrMax = Vec3(0.5, 0.5, 0.5) + instrMax *= INSTRUMENT_SCALE_MODIFIER + instrStretch = Vec3(1.1, 0.9, 0.4) + instrStretch *= INSTRUMENT_SCALE_MODIFIER + megaphone = globalPropPool.getProp('megaphone') + megaphone2 = MovieUtil.copyProp(megaphone) + megaphones = [megaphone, megaphone2] + instrument = globalPropPool.getProp('aoogah') + instrument2 = MovieUtil.copyProp(instrument) + instruments = [instrument, instrument2] + + def setInstrumentStats(instrument = instrument, instrument2 = instrument2): + instrument.setPos(-1.0, -1.5, 0.2) + instrument.setHpr(145, 0, 85) + instrument.setScale(instrMin) + instrument2.setPos(-1.0, -1.5, 0.2) + instrument2.setHpr(145, 0, 85) + instrument2.setScale(instrMin) + + hands = toon.getRightHands() + megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats)) + megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments)) + instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level]) + grow = getScaleIntervals(instruments, duration=0.2, startScale=instrMin, endScale=instrMax) + instrumentAppear = Parallel(grow, Sequence(Wait(0.05), SoundInterval(instrumentAppearSfx, node=toon))) + stretchInstr = getScaleBlendIntervals(instruments, duration=0.2, startScale=instrMax, endScale=instrStretch, blendType='easeOut') + backInstr = getScaleBlendIntervals(instruments, duration=0.2, startScale=instrStretch, endScale=instrMax, blendType='easeInOut') + attackTrack = Sequence(stretchInstr, Wait(1), backInstr) + hasLuredSuits = __hasLuredSuits(sound) + delayTime = delay + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + megaphoneTrack = Sequence(Wait(delayTime), megaphoneShow, Wait(1.0), instrumentAppear, Wait(3.0), megaphoneHide) + tracks.append(megaphoneTrack) + toonTrack = __createToonInterval(sound, delay, toon) + tracks.append(toonTrack) + soundEffect = globalBattleSoundCache.getSound(soundFiles[level]) + instrumentshrink = getScaleIntervals(instruments, duration=0.1, startScale=instrMax, endScale=instrMin) + if soundEffect: + delayTime = delay + tSound + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + soundTrack = Sequence(Wait(delayTime), Parallel(attackTrack, SoundInterval(soundEffect, node=toon), Sequence(Wait(1.5), instrumentshrink))) + tracks.append(soundTrack) + return tracks + + +def __doElephant(sound, delay, toon, targets, level): + tracks = Parallel() + instrMin = Vec3(0.001, 0.001, 0.001) + instrMax1 = Vec3(0.3, 0.4, 0.2) + instrMax1 *= INSTRUMENT_SCALE_MODIFIER + instrMax2 = Vec3(0.3, 0.3, 0.3) + instrMax2 *= INSTRUMENT_SCALE_MODIFIER + instrStretch1 = Vec3(0.3, 0.5, 0.25) + instrStretch1 *= INSTRUMENT_SCALE_MODIFIER + instrStretch2 = Vec3(0.3, 0.7, 0.3) + instrStretch2 *= INSTRUMENT_SCALE_MODIFIER + megaphone = globalPropPool.getProp('megaphone') + megaphone2 = MovieUtil.copyProp(megaphone) + megaphones = [megaphone, megaphone2] + instrument = globalPropPool.getProp('elephant') + instrument2 = MovieUtil.copyProp(instrument) + instruments = [instrument, instrument2] + + def setInstrumentStats(instrument = instrument, instrument2 = instrument2): + instrument.setPos(-.6, -.9, 0.15) + instrument.setHpr(145, 0, 85) + instrument.setScale(instrMin) + instrument2.setPos(-.6, -.9, 0.15) + instrument2.setHpr(145, 0, 85) + instrument2.setScale(instrMin) + + hands = toon.getRightHands() + megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats)) + megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments)) + instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level]) + grow1 = getScaleIntervals(instruments, duration=0.3, startScale=instrMin, endScale=instrMax1) + grow2 = getScaleIntervals(instruments, duration=0.3, startScale=instrMax1, endScale=instrMax2) + instrumentAppear = Parallel(Sequence(grow1, grow2), Sequence(Wait(0.05), SoundInterval(instrumentAppearSfx, node=toon))) + stretchInstr1 = getScaleBlendIntervals(instruments, duration=0.1, startScale=instrMax2, endScale=instrStretch1, blendType='easeOut') + stretchInstr2 = getScaleBlendIntervals(instruments, duration=0.1, startScale=instrStretch1, endScale=instrStretch2, blendType='easeOut') + stretchInstr = Sequence(stretchInstr1, stretchInstr2) + backInstr = getScaleBlendIntervals(instruments, duration=0.1, startScale=instrStretch2, endScale=instrMax2, blendType='easeOut') + attackTrack = Sequence(stretchInstr, Wait(1), backInstr) + hasLuredSuits = __hasLuredSuits(sound) + delayTime = delay + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + megaphoneTrack = Sequence(Wait(delayTime), megaphoneShow, Wait(1.0), instrumentAppear, Wait(3.0), megaphoneHide) + tracks.append(megaphoneTrack) + toonTrack = __createToonInterval(sound, delay, toon) + tracks.append(toonTrack) + soundEffect = globalBattleSoundCache.getSound(soundFiles[level]) + instrumentshrink = getScaleIntervals(instruments, duration=0.1, startScale=instrMax2, endScale=instrMin) + if soundEffect: + delayTime = delay + tSound + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + soundTrack = Sequence(Wait(delayTime), Parallel(attackTrack, SoundInterval(soundEffect, node=toon), Sequence(Wait(1.5), instrumentshrink))) + tracks.append(soundTrack) + return tracks + + +def __doFoghorn(sound, delay, toon, targets, level): + tracks = Parallel() + instrMin = Vec3(0.001, 0.001, 0.001) + instrMax1 = Vec3(0.1, 0.1, 0.1) + instrMax1 *= INSTRUMENT_SCALE_MODIFIER + instrMax2 = Vec3(0.3, 0.3, 0.3) + instrMax2 *= INSTRUMENT_SCALE_MODIFIER + instrStretch = Vec3(0.4, 0.4, 0.4) + instrStretch *= INSTRUMENT_SCALE_MODIFIER + megaphone = globalPropPool.getProp('megaphone') + megaphone2 = MovieUtil.copyProp(megaphone) + megaphones = [megaphone, megaphone2] + instrument = globalPropPool.getProp('fog_horn') + instrument2 = MovieUtil.copyProp(instrument) + instruments = [instrument, instrument2] + + def setInstrumentStats(instrument = instrument, instrument2 = instrument2): + instrument.setPos(-.8, -.9, 0.2) + instrument.setHpr(145, 0, 0) + instrument.setScale(instrMin) + instrument2.setPos(-.8, -.9, 0.2) + instrument2.setHpr(145, 0, 0) + instrument2.setScale(instrMin) + + hands = toon.getRightHands() + megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats)) + megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments)) + instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level]) + grow1 = getScaleIntervals(instruments, duration=1, startScale=instrMin, endScale=instrMax1) + grow2 = getScaleIntervals(instruments, duration=0.1, startScale=instrMax1, endScale=instrMax2) + instrumentAppear = Parallel(Sequence(grow1, grow2), Sequence(Wait(0.05), SoundInterval(instrumentAppearSfx, node=toon))) + stretchInstr = getScaleBlendIntervals(instruments, duration=0.3, startScale=instrMax2, endScale=instrStretch, blendType='easeOut') + backInstr = getScaleBlendIntervals(instruments, duration=1.0, startScale=instrStretch, endScale=instrMin, blendType='easeIn') + spinInstr1 = LerpHprInterval(instrument, duration=1.5, startHpr=Vec3(145, 0, 0), hpr=Vec3(145, 0, 90), blendType='easeInOut') + spinInstr2 = LerpHprInterval(instrument2, duration=1.5, startHpr=Vec3(145, 0, 0), hpr=Vec3(145, 0, 90), blendType='easeInOut') + spinInstr = Parallel(spinInstr1, spinInstr2) + attackTrack = Parallel(Sequence(Wait(0.2), spinInstr), Sequence(stretchInstr, Wait(0.5), backInstr)) + hasLuredSuits = __hasLuredSuits(sound) + delayTime = delay + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + megaphoneTrack = Sequence(Wait(delayTime), megaphoneShow, Wait(1.0), instrumentAppear, Wait(3.0), megaphoneHide) + tracks.append(megaphoneTrack) + toonTrack = __createToonInterval(sound, delay, toon) + tracks.append(toonTrack) + soundEffect = globalBattleSoundCache.getSound(soundFiles[level]) + if soundEffect: + delayTime = delay + tSound + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + soundTrack = Sequence(Wait(delayTime), Parallel(attackTrack, SoundInterval(soundEffect, node=toon))) + tracks.append(soundTrack) + return tracks + + +def __doOpera(sound, delay, toon, targets, level): + tracks = Parallel() + delay = delay + instrMin = Vec3(0.001, 0.001, 0.001) + instrMax1 = Vec3(1.7, 1.7, 1.7) + instrMax1 *= INSTRUMENT_SCALE_MODIFIER + instrMax2 = Vec3(2.2, 2.2, 2.2) + instrMax2 *= INSTRUMENT_SCALE_MODIFIER + instrStretch = Vec3(0.4, 0.4, 0.4) + instrStretch *= INSTRUMENT_SCALE_MODIFIER + megaphone = globalPropPool.getProp('megaphone') + megaphone2 = MovieUtil.copyProp(megaphone) + megaphones = [megaphone, megaphone2] + instrument = globalPropPool.getProp('singing') + instrument2 = MovieUtil.copyProp(instrument) + instruments = [instrument, instrument2] + head = instrument2.find('**/opera_singer') + head.setPos(0, 0, 0) + + def setInstrumentStats(instrument = instrument, instrument2 = instrument2): + notify.debug('setInstrumentStats') + newPos = Vec3(-0.8, -0.9, 0.2) + newPos *= 1.3 + instrument.setPos(newPos[0], newPos[1], newPos[2]) + instrument.setHpr(145, 0, 90) + instrument.setScale(instrMin) + instrument2.setPos(newPos[0], newPos[1], newPos[2]) + instrument2.setHpr(145, 0, 90) + instrument2.setScale(instrMin) + + hands = toon.getRightHands() + megaphoneShow = Sequence(Func(MovieUtil.showProps, megaphones, hands), Func(MovieUtil.showProps, instruments, hands), Func(setInstrumentStats)) + megaphoneHide = Sequence(Func(MovieUtil.removeProps, megaphones), Func(MovieUtil.removeProps, instruments)) + instrumentAppearSfx = globalBattleSoundCache.getSound(appearSoundFiles[level]) + grow1 = getScaleBlendIntervals(instruments, duration=1, startScale=instrMin, endScale=instrMax1, blendType='easeOut') + grow2 = getScaleBlendIntervals(instruments, duration=1.1, startScale=instrMax1, endScale=instrMax2, blendType='easeIn') + shrink2 = getScaleIntervals(instruments, duration=0.1, startScale=instrMax2, endScale=instrMin) + instrumentAppear = Parallel(Sequence(grow1, grow2, Wait(6.0), shrink2), Sequence(Wait(0.0), SoundInterval(instrumentAppearSfx, node=toon))) + hasLuredSuits = __hasLuredSuits(sound) + delayTime = delay + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + megaphoneTrack = Sequence(Wait(delayTime), megaphoneShow, Wait(1.0), instrumentAppear, Wait(2.0), megaphoneHide) + tracks.append(megaphoneTrack) + toonTrack = __createToonInterval(sound, delay, toon, operaInstrument=instrument) + tracks.append(toonTrack) + soundEffect = globalBattleSoundCache.getSound(soundFiles[level]) + if soundEffect: + delayTime = delay + tSound - 0.3 + if hasLuredSuits: + delayTime += TIME_TO_WALK_BACK + soundTrack = Sequence(Wait(delayTime), SoundInterval(soundEffect, node=toon)) + tracks.append(Sequence(Wait(0))) + tracks.append(soundTrack) + return tracks + + +def setPosFromOther(dest, source, offset = Point3(0, 0, 0)): + pos = render.getRelativePoint(source, offset) + dest.setPos(pos) + dest.reparentTo(render) + + +def getScaleIntervals(props, duration, startScale, endScale): + tracks = Parallel() + for prop in props: + tracks.append(LerpScaleInterval(prop, duration, endScale, startScale=startScale)) + + return tracks + + +def getScaleBlendIntervals(props, duration, startScale, endScale, blendType): + tracks = Parallel() + for prop in props: + tracks.append(LerpScaleInterval(prop, duration, endScale, startScale=startScale, blendType=blendType)) + + return tracks + + +soundfn_array = (__doBikehorn, + __doWhistle, + __doBugle, + __doAoogah, + __doElephant, + __doFoghorn, + __doOpera) + +def __getPartTrack(particleEffect, startDelay, durationDelay, partExtraArgs, softStop = 0): + pEffect = partExtraArgs[0] + parent = partExtraArgs[1] + if len(partExtraArgs) == 3: + worldRelative = partExtraArgs[2] + else: + worldRelative = 1 + return Sequence(Wait(startDelay), ParticleInterval(pEffect, parent, worldRelative, duration=durationDelay, cleanup=True, softStopT=softStop)) diff --git a/toontown/battle/MovieSquirt.py b/toontown/battle/MovieSquirt.py new file mode 100755 index 00000000..77592ac3 --- /dev/null +++ b/toontown/battle/MovieSquirt.py @@ -0,0 +1,748 @@ +from direct.interval.IntervalGlobal import * +from BattleBase import * +from BattleProps import * +from BattleSounds import * +from toontown.toon.ToonDNA import * +from toontown.suit.SuitDNA import * +import MovieUtil +import MovieCamera +from direct.directnotify import DirectNotifyGlobal +import BattleParticles +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +import random +notify = DirectNotifyGlobal.directNotify.newCategory('MovieSquirt') +hitSoundFiles = ('AA_squirt_flowersquirt.ogg', 'AA_squirt_glasswater.ogg', 'AA_squirt_neonwatergun.ogg', 'AA_squirt_seltzer.ogg', 'firehose_spray.ogg', 'AA_throw_stormcloud.ogg', 'AA_squirt_Geyser.ogg') +missSoundFiles = ('AA_squirt_flowersquirt_miss.ogg', 'AA_squirt_glasswater_miss.ogg', 'AA_squirt_neonwatergun_miss.ogg', 'AA_squirt_seltzer_miss.ogg', 'firehose_spray.ogg', 'AA_throw_stormcloud_miss.ogg', 'AA_squirt_Geyser.ogg') +sprayScales = [0.2, + 0.3, + 0.1, + 0.6, + 0.8, + 1.0, + 2.0] +WaterSprayColor = Point4(0.75, 0.75, 1.0, 0.8) + +def doSquirts(squirts): + if len(squirts) == 0: + return (None, None) + + suitSquirtsDict = {} + doneUber = 0 + skip = 0 + for squirt in squirts: + skip = 0 + if skip: + pass + elif type(squirt['target']) == type([]): + if 1: + target = squirt['target'][0] + suitId = target['suit'].doId + if suitId in suitSquirtsDict: + suitSquirtsDict[suitId].append(squirt) + else: + suitSquirtsDict[suitId] = [squirt] + else: + suitId = squirt['target']['suit'].doId + if suitId in suitSquirtsDict: + suitSquirtsDict[suitId].append(squirt) + else: + suitSquirtsDict[suitId] = [squirt] + + suitSquirts = suitSquirtsDict.values() + + def compFunc(a, b): + if len(a) > len(b): + return 1 + elif len(a) < len(b): + return -1 + return 0 + suitSquirts.sort(compFunc) + + delay = 0.0 + + mtrack = Parallel() + for st in suitSquirts: + if len(st) > 0: + ival = __doSuitSquirts(st) + if ival: + mtrack.append(Sequence(Wait(delay), ival)) + delay = delay + TOON_SQUIRT_SUIT_DELAY + + camDuration = mtrack.getDuration() + camTrack = MovieCamera.chooseSquirtShot(squirts, suitSquirtsDict, camDuration) + return (mtrack, camTrack) + + +def __doSuitSquirts(squirts): + uberClone = 0 + toonTracks = Parallel() + delay = 0.0 + if type(squirts[0]['target']) == type([]): + for target in squirts[0]['target']: + if len(squirts) == 1 and target['hp'] > 0: + fShowStun = 1 + else: + fShowStun = 0 + + elif len(squirts) == 1 and squirts[0]['target']['hp'] > 0: + fShowStun = 1 + else: + fShowStun = 0 + for s in squirts: + tracks = __doSquirt(s, delay, fShowStun, uberClone) + if s['level'] >= ToontownBattleGlobals.UBER_GAG_LEVEL_INDEX: + uberClone = 1 + if tracks: + for track in tracks: + toonTracks.append(track) + + delay = delay + TOON_SQUIRT_DELAY + + return toonTracks + + +def __doSquirt(squirt, delay, fShowStun, uberClone = 0): + squirtSequence = Sequence(Wait(delay)) + if type(squirt['target']) == type([]): + for target in squirt['target']: + notify.debug('toon: %s squirts prop: %d at suit: %d for hp: %d' % (squirt['toon'].getName(), + squirt['level'], + target['suit'].doId, + target['hp'])) + + else: + notify.debug('toon: %s squirts prop: %d at suit: %d for hp: %d' % (squirt['toon'].getName(), + squirt['level'], + squirt['target']['suit'].doId, + squirt['target']['hp'])) + if uberClone: + ival = squirtfn_array[squirt['level']](squirt, delay, fShowStun, uberClone) + if ival: + squirtSequence.append(ival) + else: + ival = squirtfn_array[squirt['level']](squirt, delay, fShowStun) + if ival: + squirtSequence.append(ival) + return [squirtSequence] + + +def __suitTargetPoint(suit): + pnt = suit.getPos(render) + pnt.setZ(pnt[2] + suit.getHeight() * 0.66) + return Point3(pnt) + + +def __getSplashTrack(point, scale, delay, battle, splashHold = 0.01): + + def prepSplash(splash, point): + if callable(point): + point = point() + splash.reparentTo(render) + splash.setPos(point) + scale = splash.getScale() + splash.setBillboardPointWorld() + splash.setScale(scale) + + splash = globalPropPool.getProp('splash-from-splat') + splash.setScale(scale) + return Sequence(Func(battle.movie.needRestoreRenderProp, splash), Wait(delay), Func(prepSplash, splash, point), ActorInterval(splash, 'splash-from-splat'), Wait(splashHold), Func(MovieUtil.removeProp, splash), Func(battle.movie.clearRenderProp, splash)) + + +def __getSuitTrack(suit, tContact, tDodge, hp, hpbonus, kbbonus, anim, died, leftSuits, rightSuits, battle, toon, fShowStun, beforeStun = 0.5, afterStun = 1.8, geyser = 0, uberRepeat = 0, revived = 0): + if hp > 0: + suitTrack = Sequence() + sival = ActorInterval(suit, anim) + sival = [] + if kbbonus > 0 and not geyser: + suitPos, suitHpr = battle.getActorPosHpr(suit) + suitType = getSuitBodyType(suit.getStyleName()) + animTrack = Sequence() + animTrack.append(ActorInterval(suit, anim, duration=0.2)) + if suitType == 'a': + animTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.43)) + elif suitType == 'b': + animTrack.append(ActorInterval(suit, 'slip-forward', startTime=1.94)) + elif suitType == 'c': + animTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.58)) + animTrack.append(Func(battle.unlureSuit, suit)) + moveTrack = Sequence(Wait(0.2), LerpPosInterval(suit, 0.6, pos=suitPos, other=battle)) + sival = Parallel(animTrack, moveTrack) + elif geyser: + suitStartPos = suit.getPos() + suitFloat = Point3(0, 0, 14) + suitEndPos = Point3(suitStartPos[0] + suitFloat[0], suitStartPos[1] + suitFloat[1], suitStartPos[2] + suitFloat[2]) + suitType = getSuitBodyType(suit.getStyleName()) + if suitType == 'a': + startFlailFrame = 16 + endFlailFrame = 16 + elif suitType == 'b': + startFlailFrame = 15 + endFlailFrame = 15 + else: + startFlailFrame = 15 + endFlailFrame = 15 + sival = Sequence(ActorInterval(suit, 'slip-backward', playRate=0.5, startFrame=0, endFrame=startFlailFrame - 1), Func(suit.pingpong, 'slip-backward', fromFrame=startFlailFrame, toFrame=endFlailFrame), Wait(0.5), ActorInterval(suit, 'slip-backward', playRate=1.0, startFrame=endFlailFrame)) + sUp = LerpPosInterval(suit, 1.1, suitEndPos, startPos=suitStartPos, fluid=1) + sDown = LerpPosInterval(suit, 0.6, suitStartPos, startPos=suitEndPos, fluid=1) + elif fShowStun == 1: + sival = Parallel(ActorInterval(suit, anim), MovieUtil.createSuitStunInterval(suit, beforeStun, afterStun)) + else: + sival = ActorInterval(suit, anim) + showDamage = Func(suit.showHpText, -hp, openEnded=0, attackTrack=SQUIRT_TRACK) + updateHealthBar = Func(suit.updateHealthBar, hp) + suitTrack.append(Wait(tContact)) + suitTrack.append(showDamage) + suitTrack.append(updateHealthBar) + if not geyser: + suitTrack.append(sival) + elif not uberRepeat: + geyserMotion = Sequence(sUp, Wait(0.0), sDown) + suitLaunch = Parallel(sival, geyserMotion) + suitTrack.append(suitLaunch) + else: + suitTrack.append(Wait(5.5)) + bonusTrack = Sequence(Wait(tContact)) + if kbbonus > 0: + bonusTrack.append(Wait(0.75)) + bonusTrack.append(Func(suit.showHpText, -kbbonus, 2, openEnded=0, attackTrack=SQUIRT_TRACK)) + bonusTrack.append(Func(suit.updateHealthBar, kbbonus)) + if hpbonus > 0: + bonusTrack.append(Wait(0.75)) + bonusTrack.append(Func(suit.showHpText, -hpbonus, 1, openEnded=0, attackTrack=SQUIRT_TRACK)) + bonusTrack.append(Func(suit.updateHealthBar, hpbonus)) + if died != 0: + suitTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle)) + else: + suitTrack.append(Func(suit.loop, 'neutral')) + if revived != 0: + suitTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle)) + return Parallel(suitTrack, bonusTrack) + else: + return MovieUtil.createSuitDodgeMultitrack(tDodge, suit, leftSuits, rightSuits) + + +def say(statement): + print statement + + +def __getSoundTrack(level, hitSuit, delay, node = None): + if hitSuit: + soundEffect = globalBattleSoundCache.getSound(hitSoundFiles[level]) + else: + soundEffect = globalBattleSoundCache.getSound(missSoundFiles[level]) + soundTrack = Sequence() + if soundEffect: + soundTrack.append(Wait(delay)) + soundTrack.append(SoundInterval(soundEffect, node=node)) + return soundTrack + + +def __doFlower(squirt, delay, fShowStun): + toon = squirt['toon'] + level = squirt['level'] + hpbonus = squirt['hpbonus'] + target = squirt['target'] + suit = target['suit'] + hp = target['hp'] + kbbonus = target['kbbonus'] + died = target['died'] + revived = target['revived'] + leftSuits = target['leftSuits'] + rightSuits = target['rightSuits'] + battle = squirt['battle'] + suitPos = suit.getPos(battle) + origHpr = toon.getHpr(battle) + hitSuit = hp > 0 + scale = sprayScales[level] + tTotalFlowerToonAnimationTime = 2.5 + tFlowerFirstAppears = 1.0 + dFlowerScaleTime = 0.5 + tSprayStarts = tTotalFlowerToonAnimationTime + dSprayScale = 0.2 + dSprayHold = 0.1 + tContact = tSprayStarts + dSprayScale + tSuitDodges = tTotalFlowerToonAnimationTime + tracks = Parallel() + button = globalPropPool.getProp('button') + button2 = MovieUtil.copyProp(button) + buttons = [button, button2] + hands = toon.getLeftHands() + toonTrack = Sequence(Func(MovieUtil.showProps, buttons, hands), Func(toon.headsUp, battle, suitPos), ActorInterval(toon, 'pushbutton'), Func(MovieUtil.removeProps, buttons), Func(toon.loop, 'neutral'), Func(toon.setHpr, battle, origHpr)) + tracks.append(toonTrack) + tracks.append(__getSoundTrack(level, hitSuit, tTotalFlowerToonAnimationTime - 0.4, toon)) + flower = globalPropPool.getProp('squirting-flower') + flower.setScale(1.5, 1.5, 1.5) + targetPoint = lambda suit = suit: __suitTargetPoint(suit) + + def getSprayStartPos(flower = flower): + toon.update(0) + return flower.getPos(render) + + sprayTrack = MovieUtil.getSprayTrack(battle, WaterSprayColor, getSprayStartPos, targetPoint, dSprayScale, dSprayHold, dSprayScale, horizScale=scale, vertScale=scale) + lodnames = toon.getLODNames() + toonlod0 = toon.getLOD(lodnames[0]) + toonlod1 = toon.getLOD(lodnames[1]) + if base.config.GetBool('want-new-anims', 1): + if not toonlod0.find('**/def_joint_attachFlower').isEmpty(): + flower_joint0 = toonlod0.find('**/def_joint_attachFlower') + else: + flower_joint0 = toonlod0.find('**/joint_attachFlower') + if base.config.GetBool('want-new-anims', 1): + if not toonlod1.find('**/def_joint_attachFlower').isEmpty(): + flower_joint1 = toonlod1.find('**/def_joint_attachFlower') + else: + flower_joint1 = toonlod1.find('**/joint_attachFlower') + flower_jointpath0 = flower_joint0.attachNewNode('attachFlower-InstanceNode') + flower_jointpath1 = flower_jointpath0.instanceTo(flower_joint1) + flowerTrack = Sequence(Wait(tFlowerFirstAppears), Func(flower.reparentTo, flower_jointpath0), LerpScaleInterval(flower, dFlowerScaleTime, flower.getScale(), startScale=MovieUtil.PNT3_NEARZERO), Wait(tTotalFlowerToonAnimationTime - dFlowerScaleTime - tFlowerFirstAppears)) + if hp <= 0: + flowerTrack.append(Wait(0.5)) + flowerTrack.append(sprayTrack) + flowerTrack.append(LerpScaleInterval(flower, dFlowerScaleTime, MovieUtil.PNT3_NEARZERO)) + flowerTrack.append(Func(flower_jointpath1.removeNode)) + flowerTrack.append(Func(flower_jointpath0.removeNode)) + flowerTrack.append(Func(MovieUtil.removeProp, flower)) + tracks.append(flowerTrack) + if hp > 0: + tracks.append(__getSplashTrack(targetPoint, scale, tSprayStarts + dSprayScale, battle)) + if hp > 0 or delay <= 0: + tracks.append(__getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'squirt-small-react', died, leftSuits, rightSuits, battle, toon, fShowStun, revived=revived)) + return tracks + + +def __doWaterGlass(squirt, delay, fShowStun): + toon = squirt['toon'] + level = squirt['level'] + hpbonus = squirt['hpbonus'] + target = squirt['target'] + suit = target['suit'] + hp = target['hp'] + kbbonus = target['kbbonus'] + died = target['died'] + revived = target['revived'] + leftSuits = target['leftSuits'] + rightSuits = target['rightSuits'] + battle = squirt['battle'] + suitPos = suit.getPos(battle) + origHpr = toon.getHpr(battle) + hitSuit = hp > 0 + scale = sprayScales[level] + dGlassHold = 5.0 + dGlassScale = 0.5 + tSpray = 82.0 / toon.getFrameRate('spit') + sprayPoseFrame = 88 + dSprayScale = 0.1 + dSprayHold = 0.1 + tContact = tSpray + dSprayScale + tSuitDodges = max(tSpray - 0.5, 0.0) + tracks = Parallel() + tracks.append(ActorInterval(toon, 'spit')) + soundTrack = __getSoundTrack(level, hitSuit, 1.7, toon) + tracks.append(soundTrack) + glass = globalPropPool.getProp('glass') + hands = toon.getRightHands() + hand_jointpath0 = hands[0].attachNewNode('handJoint0-path') + hand_jointpath1 = hand_jointpath0.instanceTo(hands[1]) + glassTrack = Sequence(Func(MovieUtil.showProp, glass, hand_jointpath0), ActorInterval(glass, 'glass'), Func(hand_jointpath1.removeNode), Func(hand_jointpath0.removeNode), Func(MovieUtil.removeProp, glass)) + tracks.append(glassTrack) + targetPoint = lambda suit = suit: __suitTargetPoint(suit) + + def getSprayStartPos(toon = toon): + toon.update(0) + lod0 = toon.getLOD(toon.getLODNames()[0]) + if base.config.GetBool('want-new-anims', 1): + if not lod0.find('**/def_head').isEmpty(): + joint = lod0.find('**/def_head') + else: + joint = lod0.find('**/joint_head') + else: + joint = lod0.find('**/joint_head') + n = hidden.attachNewNode('pointInFrontOfHead') + n.reparentTo(toon) + n.setPos(joint.getPos(toon) + Point3(0, 0.3, -0.2)) + p = n.getPos(render) + n.removeNode() + del n + return p + + sprayTrack = MovieUtil.getSprayTrack(battle, WaterSprayColor, getSprayStartPos, targetPoint, dSprayScale, dSprayHold, dSprayScale, horizScale=scale, vertScale=scale) + tracks.append(Sequence(Wait(tSpray), sprayTrack)) + if hp > 0: + tracks.append(__getSplashTrack(targetPoint, scale, tSpray + dSprayScale, battle)) + if hp > 0 or delay <= 0: + tracks.append(__getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'squirt-small-react', died, leftSuits, rightSuits, battle, toon, fShowStun, revived=revived)) + return tracks + + +def __doWaterGun(squirt, delay, fShowStun): + toon = squirt['toon'] + level = squirt['level'] + hpbonus = squirt['hpbonus'] + target = squirt['target'] + suit = target['suit'] + hp = target['hp'] + kbbonus = target['kbbonus'] + died = target['died'] + revived = target['revived'] + leftSuits = target['leftSuits'] + rightSuits = target['rightSuits'] + battle = squirt['battle'] + suitPos = suit.getPos(battle) + origHpr = toon.getHpr(battle) + hitSuit = hp > 0 + scale = sprayScales[level] + tPistol = 0.0 + dPistolScale = 0.5 + dPistolHold = 1.8 + tSpray = 48.0 / toon.getFrameRate('water-gun') + sprayPoseFrame = 63 + dSprayScale = 0.1 + dSprayHold = 0.3 + tContact = tSpray + dSprayScale + tSuitDodges = 1.1 + tracks = Parallel() + toonTrack = Sequence(Func(toon.headsUp, battle, suitPos), ActorInterval(toon, 'water-gun'), Func(toon.loop, 'neutral'), Func(toon.setHpr, battle, origHpr)) + tracks.append(toonTrack) + soundTrack = __getSoundTrack(level, hitSuit, 1.8, toon) + tracks.append(soundTrack) + pistol = globalPropPool.getProp('water-gun') + hands = toon.getRightHands() + hand_jointpath0 = hands[0].attachNewNode('handJoint0-path') + hand_jointpath1 = hand_jointpath0.instanceTo(hands[1]) + targetPoint = lambda suit = suit: __suitTargetPoint(suit) + + def getSprayStartPos(pistol = pistol, toon = toon): + toon.update(0) + joint = pistol.find('**/joint_nozzle') + p = joint.getPos(render) + return p + + sprayTrack = MovieUtil.getSprayTrack(battle, WaterSprayColor, getSprayStartPos, targetPoint, dSprayScale, dSprayHold, dSprayScale, horizScale=scale, vertScale=scale) + pistolPos = Point3(0.28, 0.1, 0.08) + pistolHpr = VBase3(85.6, -4.44, 94.43) + pistolTrack = Sequence(Func(MovieUtil.showProp, pistol, hand_jointpath0, pistolPos, pistolHpr), LerpScaleInterval(pistol, dPistolScale, pistol.getScale(), startScale=MovieUtil.PNT3_NEARZERO), Wait(tSpray - dPistolScale)) + pistolTrack.append(sprayTrack) + pistolTrack.append(Wait(dPistolHold)) + pistolTrack.append(LerpScaleInterval(pistol, dPistolScale, MovieUtil.PNT3_NEARZERO)) + pistolTrack.append(Func(hand_jointpath1.removeNode)) + pistolTrack.append(Func(hand_jointpath0.removeNode)) + pistolTrack.append(Func(MovieUtil.removeProp, pistol)) + tracks.append(pistolTrack) + if hp > 0: + tracks.append(__getSplashTrack(targetPoint, 0.3, tSpray + dSprayScale, battle)) + if hp > 0 or delay <= 0: + tracks.append(__getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'squirt-small-react', died, leftSuits, rightSuits, battle, toon, fShowStun, revived=revived)) + return tracks + + +def __doSeltzerBottle(squirt, delay, fShowStun): + toon = squirt['toon'] + level = squirt['level'] + hpbonus = squirt['hpbonus'] + target = squirt['target'] + suit = target['suit'] + hp = target['hp'] + kbbonus = target['kbbonus'] + died = target['died'] + revived = target['revived'] + leftSuits = target['leftSuits'] + rightSuits = target['rightSuits'] + battle = squirt['battle'] + suitPos = suit.getPos(battle) + origHpr = toon.getHpr(battle) + hitSuit = hp > 0 + scale = sprayScales[level] + tBottle = 0.0 + dBottleScale = 0.5 + dBottleHold = 3.0 + tSpray = 53.0 / toon.getFrameRate('hold-bottle') + 0.05 + dSprayScale = 0.2 + dSprayHold = 0.1 + tContact = tSpray + dSprayScale + tSuitDodges = max(tContact - 0.7, 0.0) + tracks = Parallel() + toonTrack = Sequence(Func(toon.headsUp, battle, suitPos), ActorInterval(toon, 'hold-bottle'), Func(toon.loop, 'neutral'), Func(toon.setHpr, battle, origHpr)) + tracks.append(toonTrack) + soundTrack = __getSoundTrack(level, hitSuit, tSpray - 0.1, toon) + tracks.append(soundTrack) + bottle = globalPropPool.getProp('bottle') + hands = toon.getRightHands() + targetPoint = lambda suit = suit: __suitTargetPoint(suit) + + def getSprayStartPos(bottle = bottle, toon = toon): + toon.update(0) + joint = bottle.find('**/joint_toSpray') + n = hidden.attachNewNode('pointBehindSprayProp') + n.reparentTo(toon) + n.setPos(joint.getPos(toon) + Point3(0, -0.4, 0)) + p = n.getPos(render) + n.removeNode() + del n + return p + + sprayTrack = MovieUtil.getSprayTrack(battle, WaterSprayColor, getSprayStartPos, targetPoint, dSprayScale, dSprayHold, dSprayScale, horizScale=scale, vertScale=scale) + hand_jointpath0 = hands[0].attachNewNode('handJoint0-path') + hand_jointpath1 = hand_jointpath0.instanceTo(hands[1]) + bottleTrack = Sequence(Func(MovieUtil.showProp, bottle, hand_jointpath0), LerpScaleInterval(bottle, dBottleScale, bottle.getScale(), startScale=MovieUtil.PNT3_NEARZERO), Wait(tSpray - dBottleScale)) + bottleTrack.append(sprayTrack) + bottleTrack.append(Wait(dBottleHold)) + bottleTrack.append(LerpScaleInterval(bottle, dBottleScale, MovieUtil.PNT3_NEARZERO)) + bottleTrack.append(Func(hand_jointpath1.removeNode)) + bottleTrack.append(Func(hand_jointpath0.removeNode)) + bottleTrack.append(Func(MovieUtil.removeProp, bottle)) + tracks.append(bottleTrack) + if hp > 0: + tracks.append(__getSplashTrack(targetPoint, scale, tSpray + dSprayScale, battle)) + if (hp > 0 or delay <= 0) and suit: + tracks.append(__getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'squirt-small-react', died, leftSuits, rightSuits, battle, toon, fShowStun, revived=revived)) + return tracks + + +def __doFireHose(squirt, delay, fShowStun): + toon = squirt['toon'] + level = squirt['level'] + hpbonus = squirt['hpbonus'] + target = squirt['target'] + suit = target['suit'] + hp = target['hp'] + kbbonus = target['kbbonus'] + died = target['died'] + revived = target['revived'] + leftSuits = target['leftSuits'] + rightSuits = target['rightSuits'] + battle = squirt['battle'] + suitPos = suit.getPos(battle) + origHpr = toon.getHpr(battle) + hitSuit = hp > 0 + scale = 0.3 + tAppearDelay = 0.7 + dHoseHold = 0.7 + dAnimHold = 5.1 + tSprayDelay = 2.8 + tSpray = 0.2 + dSprayScale = 0.1 + dSprayHold = 1.8 + tContact = 2.9 + tSuitDodges = 2.1 + tracks = Parallel() + toonTrack = Sequence(Wait(tAppearDelay), Func(toon.headsUp, battle, suitPos), ActorInterval(toon, 'firehose'), Func(toon.loop, 'neutral'), Func(toon.setHpr, battle, origHpr)) + tracks.append(toonTrack) + soundTrack = __getSoundTrack(level, hitSuit, tSprayDelay, toon) + tracks.append(soundTrack) + hose = globalPropPool.getProp('firehose') + hydrant = globalPropPool.getProp('hydrant') + hose.reparentTo(hydrant) + (hose.pose('firehose', 2),) + hydrantNode = toon.attachNewNode('hydrantNode') + hydrantNode.clearTransform(toon.getGeomNode().getChild(0)) + hydrantScale = hydrantNode.attachNewNode('hydrantScale') + hydrant.reparentTo(hydrantScale) + toon.pose('firehose', 30) + toon.update(0) + torso = toon.getPart('torso', '1000') + if toon.style.torso[0] == 'm': + hydrant.setPos(torso, 0, 0, -1.85) + else: + hydrant.setPos(torso, 0, 0, -1.45) + hydrant.setPos(0, 0, hydrant.getZ()) + base = hydrant.find('**/base') + base.setColor(1, 1, 1, 0.5) + base.setPos(toon, 0, 0, 0) + toon.loop('neutral') + targetPoint = lambda suit = suit: __suitTargetPoint(suit) + + def getSprayStartPos(hose = hose, toon = toon, targetPoint = targetPoint): + toon.update(0) + if hose.isEmpty() == 1: + if callable(targetPoint): + return targetPoint() + else: + return targetPoint + joint = hose.find('**/joint_water_stream') + n = hidden.attachNewNode('pointBehindSprayProp') + n.reparentTo(toon) + n.setPos(joint.getPos(toon) + Point3(0, -0.55, 0)) + p = n.getPos(render) + n.removeNode() + del n + return p + + sprayTrack = Sequence() + sprayTrack.append(Wait(tSprayDelay)) + sprayTrack.append(MovieUtil.getSprayTrack(battle, WaterSprayColor, getSprayStartPos, targetPoint, dSprayScale, dSprayHold, dSprayScale, horizScale=scale, vertScale=scale)) + tracks.append(sprayTrack) + hydrantNode.detachNode() + propTrack = Sequence(Func(battle.movie.needRestoreRenderProp, hydrantNode), Func(hydrantNode.reparentTo, toon), LerpScaleInterval(hydrantScale, tAppearDelay * 0.5, Point3(1, 1, 1.4), startScale=Point3(1, 1, 0.01)), LerpScaleInterval(hydrantScale, tAppearDelay * 0.3, Point3(1, 1, 0.8), startScale=Point3(1, 1, 1.4)), LerpScaleInterval(hydrantScale, tAppearDelay * 0.1, Point3(1, 1, 1.2), startScale=Point3(1, 1, 0.8)), LerpScaleInterval(hydrantScale, tAppearDelay * 0.1, Point3(1, 1, 1), startScale=Point3(1, 1, 1.2)), ActorInterval(hose, 'firehose', duration=dAnimHold), Wait(dHoseHold - 0.2), LerpScaleInterval(hydrantScale, 0.2, Point3(1, 1, 0.01), startScale=Point3(1, 1, 1)), Func(MovieUtil.removeProps, [hydrantNode, hose]), Func(battle.movie.clearRenderProp, hydrantNode)) + tracks.append(propTrack) + if hp > 0: + tracks.append(__getSplashTrack(targetPoint, 0.4, 2.7, battle, splashHold=1.5)) + if hp > 0 or delay <= 0: + tracks.append(__getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'squirt-large-react', died, leftSuits, rightSuits, battle, toon, fShowStun, revived=revived)) + return tracks + + +def __doStormCloud(squirt, delay, fShowStun): + toon = squirt['toon'] + level = squirt['level'] + hpbonus = squirt['hpbonus'] + target = squirt['target'] + suit = target['suit'] + hp = target['hp'] + kbbonus = target['kbbonus'] + died = target['died'] + revived = target['revived'] + leftSuits = target['leftSuits'] + rightSuits = target['rightSuits'] + battle = squirt['battle'] + suitPos = suit.getPos(battle) + origHpr = toon.getHpr(battle) + hitSuit = hp > 0 + scale = sprayScales[level] + tButton = 0.0 + dButtonScale = 0.5 + dButtonHold = 3.0 + tContact = 2.9 + tSpray = 1 + tSuitDodges = 1.8 + tracks = Parallel() + soundTrack = __getSoundTrack(level, hitSuit, 2.3, toon) + soundTrack2 = __getSoundTrack(level, hitSuit, 4.6, toon) + tracks.append(soundTrack) + tracks.append(soundTrack2) + button = globalPropPool.getProp('button') + button2 = MovieUtil.copyProp(button) + buttons = [button, button2] + hands = toon.getLeftHands() + toonTrack = Sequence(Func(MovieUtil.showProps, buttons, hands), Func(toon.headsUp, battle, suitPos), ActorInterval(toon, 'pushbutton'), Func(MovieUtil.removeProps, buttons), Func(toon.loop, 'neutral'), Func(toon.setHpr, battle, origHpr)) + tracks.append(toonTrack) + cloud = globalPropPool.getProp('stormcloud') + cloud2 = MovieUtil.copyProp(cloud) + BattleParticles.loadParticles() + trickleEffect = BattleParticles.createParticleEffect(file='trickleLiquidate') + rainEffect = BattleParticles.createParticleEffect(file='liquidate') + rainEffect2 = BattleParticles.createParticleEffect(file='liquidate') + rainEffect3 = BattleParticles.createParticleEffect(file='liquidate') + cloudHeight = suit.height + 3 + cloudPosPoint = Point3(0, 0, cloudHeight) + scaleUpPoint = Point3(3, 3, 3) + rainEffects = [rainEffect, rainEffect2, rainEffect3] + rainDelay = 1 + effectDelay = 0.3 + if hp > 0: + cloudHold = 4.7 + else: + cloudHold = 1.7 + + def getCloudTrack(cloud, suit, cloudPosPoint, scaleUpPoint, rainEffects, rainDelay, effectDelay, cloudHold, useEffect, battle = battle, trickleEffect = trickleEffect): + track = Sequence(Func(MovieUtil.showProp, cloud, suit, cloudPosPoint), Func(cloud.pose, 'stormcloud', 0), LerpScaleInterval(cloud, 1.5, scaleUpPoint, startScale=MovieUtil.PNT3_NEARZERO), Wait(rainDelay)) + if useEffect == 1: + ptrack = Parallel() + delay = trickleDuration = cloudHold * 0.25 + trickleTrack = Sequence(Func(battle.movie.needRestoreParticleEffect, trickleEffect), ParticleInterval(trickleEffect, cloud, worldRelative=0, duration=trickleDuration, cleanup=True), Func(battle.movie.clearRestoreParticleEffect, trickleEffect)) + track.append(trickleTrack) + for i in xrange(0, 3): + dur = cloudHold - 2 * trickleDuration + ptrack.append(Sequence(Func(battle.movie.needRestoreParticleEffect, rainEffects[i]), Wait(delay), ParticleInterval(rainEffects[i], cloud, worldRelative=0, duration=dur, cleanup=True), Func(battle.movie.clearRestoreParticleEffect, rainEffects[i]))) + delay += effectDelay + + ptrack.append(Sequence(Wait(3 * effectDelay), ActorInterval(cloud, 'stormcloud', startTime=1, duration=cloudHold))) + track.append(ptrack) + else: + track.append(ActorInterval(cloud, 'stormcloud', startTime=1, duration=cloudHold)) + track.append(LerpScaleInterval(cloud, 0.5, MovieUtil.PNT3_NEARZERO)) + track.append(Func(MovieUtil.removeProp, cloud)) + return track + + tracks.append(getCloudTrack(cloud, suit, cloudPosPoint, scaleUpPoint, rainEffects, rainDelay, effectDelay, cloudHold, useEffect=1)) + tracks.append(getCloudTrack(cloud2, suit, cloudPosPoint, scaleUpPoint, rainEffects, rainDelay, effectDelay, cloudHold, useEffect=0)) + if hp > 0 or delay <= 0: + tracks.append(__getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'soak', died, leftSuits, rightSuits, battle, toon, fShowStun, beforeStun=2.6, afterStun=2.3, revived=revived)) + return tracks + + +def __doGeyser(squirt, delay, fShowStun, uberClone = 0): + toon = squirt['toon'] + level = squirt['level'] + hpbonus = squirt['hpbonus'] + tracks = Parallel() + tButton = 0.0 + dButtonScale = 0.5 + dButtonHold = 3.0 + tContact = 2.9 + tSpray = 1 + tSuitDodges = 1.8 + button = globalPropPool.getProp('button') + button2 = MovieUtil.copyProp(button) + buttons = [button, button2] + hands = toon.getLeftHands() + battle = squirt['battle'] + origHpr = toon.getHpr(battle) + suit = squirt['target'][0]['suit'] + suitPos = suit.getPos(battle) + toonTrack = Sequence(Func(MovieUtil.showProps, buttons, hands), Func(toon.headsUp, battle, suitPos), ActorInterval(toon, 'pushbutton'), Func(MovieUtil.removeProps, buttons), Func(toon.loop, 'neutral'), Func(toon.setHpr, battle, origHpr)) + tracks.append(toonTrack) + for target in squirt['target']: + suit = target['suit'] + hp = target['hp'] + kbbonus = target['kbbonus'] + died = target['died'] + revived = target['revived'] + leftSuits = target['leftSuits'] + rightSuits = target['rightSuits'] + suitPos = suit.getPos(battle) + hitSuit = hp > 0 + scale = sprayScales[level] + soundTrack = __getSoundTrack(level, hitSuit, 1.8, toon) + delayTime = random.random() + tracks.append(Wait(delayTime)) + tracks.append(soundTrack) + cloud = globalPropPool.getProp('geyser') + cloud2 = MovieUtil.copyProp(cloud) + BattleParticles.loadParticles() + geyserHeight = battle.getH() + geyserPosPoint = Point3(0, 0, geyserHeight) + scaleUpPoint = Point3(1.8, 1.8, 1.8) + rainEffects = [] + rainDelay = 2.5 + effectDelay = 0.3 + if hp > 0: + geyserHold = 1.5 + else: + geyserHold = 0.5 + + def getGeyserTrack(geyser, suit, geyserPosPoint, scaleUpPoint, rainEffects, rainDelay, effectDelay, geyserHold, useEffect, battle = battle): + geyserMound = MovieUtil.copyProp(geyser) + geyserRemoveM = geyserMound.findAllMatches('**/Splash*') + geyserRemoveM.addPathsFrom(geyserMound.findAllMatches('**/spout')) + for i in xrange(geyserRemoveM.getNumPaths()): + geyserRemoveM[i].removeNode() + + geyserWater = MovieUtil.copyProp(geyser) + geyserRemoveW = geyserWater.findAllMatches('**/hole') + geyserRemoveW.addPathsFrom(geyserWater.findAllMatches('**/shadow')) + for i in xrange(geyserRemoveW.getNumPaths()): + geyserRemoveW[i].removeNode() + + track = Sequence(Wait(rainDelay), Func(MovieUtil.showProp, geyserMound, battle, suit.getPos(battle)), Func(MovieUtil.showProp, geyserWater, battle, suit.getPos(battle)), LerpScaleInterval(geyserWater, 1.0, scaleUpPoint, startScale=MovieUtil.PNT3_NEARZERO), Wait(geyserHold * 0.5), LerpScaleInterval(geyserWater, 0.5, MovieUtil.PNT3_NEARZERO, startScale=scaleUpPoint)) + track.append(LerpScaleInterval(geyserMound, 0.5, MovieUtil.PNT3_NEARZERO)) + track.append(Func(MovieUtil.removeProp, geyserMound)) + track.append(Func(MovieUtil.removeProp, geyserWater)) + track.append(Func(MovieUtil.removeProp, geyser)) + return track + + if not uberClone: + tracks.append(Sequence(Wait(delayTime), getGeyserTrack(cloud, suit, geyserPosPoint, scaleUpPoint, rainEffects, rainDelay, effectDelay, geyserHold, useEffect=1))) + if hp > 0 or delay <= 0: + tracks.append(Sequence(Wait(delayTime), __getSuitTrack(suit, tContact, tSuitDodges, hp, hpbonus, kbbonus, 'soak', died, leftSuits, rightSuits, battle, toon, fShowStun, beforeStun=2.6, afterStun=2.3, geyser=1, uberRepeat=uberClone, revived=revived))) + + return tracks + + +squirtfn_array = (__doFlower, + __doWaterGlass, + __doWaterGun, + __doSeltzerBottle, + __doFireHose, + __doStormCloud, + __doGeyser) diff --git a/toontown/battle/MovieSuitAttacks.py b/toontown/battle/MovieSuitAttacks.py new file mode 100755 index 00000000..d15f4335 --- /dev/null +++ b/toontown/battle/MovieSuitAttacks.py @@ -0,0 +1,3646 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect + +from BattleBase import * +from BattleBase import * +import BattleParticles +from BattleProps import * +from BattleSounds import * +import MovieCamera +import MovieUtil +from SuitBattleGlobals import * +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals +from toontown.suit.SuitDNA import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase.ToontownGlobals import * + + +notify = DirectNotifyGlobal.directNotify.newCategory('MovieSuitAttacks') + +def __doDamage(toon, dmg, died): + if dmg > 0 and toon.hp != None: + toon.takeDamage(dmg) + return + + +def __showProp(prop, parent, pos, hpr = None, scale = None): + prop.reparentTo(parent) + prop.setPos(pos) + if hpr: + prop.setHpr(hpr) + if scale: + prop.setScale(scale) + + +def __animProp(prop, propName, propType = 'actor'): + if 'actor' == propType: + prop.play(propName) + elif 'model' == propType: + pass + else: + self.notify.error('No such propType as: %s' % propType) + + +def __suitFacePoint(suit, zOffset = 0): + pnt = suit.getPos() + pnt.setZ(pnt[2] + suit.shoulderHeight + 0.3 + zOffset) + return Point3(pnt) + + +def __toonFacePoint(toon, zOffset = 0, parent = render): + pnt = toon.getPos(parent) + pnt.setZ(pnt[2] + toon.shoulderHeight + 0.3 + zOffset) + return Point3(pnt) + + +def __toonTorsoPoint(toon, zOffset = 0): + pnt = toon.getPos() + pnt.setZ(pnt[2] + toon.shoulderHeight - 0.2) + return Point3(pnt) + + +def __toonGroundPoint(attack, toon, zOffset = 0, parent = render): + pnt = toon.getPos(parent) + battle = attack['battle'] + pnt.setZ(battle.getZ(parent) + zOffset) + return Point3(pnt) + + +def __toonGroundMissPoint(attack, prop, toon, zOffset = 0): + point = __toonMissPoint(prop, toon) + battle = attack['battle'] + point.setZ(battle.getZ() + zOffset) + return Point3(point) + + +def __toonMissPoint(prop, toon, yOffset = 0, parent = None): + if parent: + p = __toonFacePoint(toon) - prop.getPos(parent) + else: + p = __toonFacePoint(toon) - prop.getPos() + v = Vec3(p) + baseDistance = v.length() + v.normalize() + if parent: + endPos = prop.getPos(parent) + v * (baseDistance + 5 + yOffset) + else: + endPos = prop.getPos() + v * (baseDistance + 5 + yOffset) + return Point3(endPos) + + +def __toonMissBehindPoint(toon, parent = render, offset = 0): + point = toon.getPos(parent) + point.setY(point.getY() - 5 + offset) + return point + + +def __throwBounceHitPoint(prop, toon): + startPoint = prop.getPos() + endPoint = __toonFacePoint(toon) + return __throwBouncePoint(startPoint, endPoint) + + +def __throwBounceMissPoint(prop, toon): + startPoint = prop.getPos() + endPoint = __toonFacePoint(toon) + return __throwBouncePoint(startPoint, endPoint) + + +def __throwBouncePoint(startPoint, endPoint): + midPoint = startPoint + (endPoint - startPoint) / 2.0 + midPoint.setZ(0) + return Point3(midPoint) + + +def doSuitAttack(attack): + notify.debug('building suit attack in doSuitAttack: %s' % attack['name']) + name = attack['id'] + if name == AUDIT: + suitTrack = doAudit(attack) + elif name == BITE: + suitTrack = doBite(attack) + elif name == BOUNCE_CHECK: + suitTrack = doBounceCheck(attack) + elif name == BRAIN_STORM: + suitTrack = doBrainStorm(attack) + elif name == BUZZ_WORD: + suitTrack = doBuzzWord(attack) + elif name == CALCULATE: + suitTrack = doCalculate(attack) + elif name == CANNED: + suitTrack = doCanned(attack) + elif name == CHOMP: + suitTrack = doChomp(attack) + elif name == CIGAR_SMOKE: + suitTrack = doCigarSmoke(attack) + elif name == CLIPON_TIE: + suitTrack = doClipOnTie(attack) + elif name == CRUNCH: + suitTrack = doCrunch(attack) + elif name == DEMOTION: + suitTrack = doDemotion(attack) + elif name == DOUBLE_TALK: + suitTrack = doDoubleTalk(attack) + elif name == DOWNSIZE: + suitTrack = doDownsize(attack) + elif name == EVICTION_NOTICE: + suitTrack = doEvictionNotice(attack) + elif name == EVIL_EYE: + suitTrack = doEvilEye(attack) + elif name == FILIBUSTER: + suitTrack = doFilibuster(attack) + elif name == FILL_WITH_LEAD: + suitTrack = doFillWithLead(attack) + elif name == FINGER_WAG: + suitTrack = doFingerWag(attack) + elif name == FIRED: + suitTrack = doFired(attack) + elif name == FIVE_O_CLOCK_SHADOW: + suitTrack = doDefault(attack) + elif name == FLOOD_THE_MARKET: + suitTrack = doDefault(attack) + elif name == FOUNTAIN_PEN: + suitTrack = doFountainPen(attack) + elif name == FREEZE_ASSETS: + suitTrack = doFreezeAssets(attack) + elif name == GAVEL: + suitTrack = doDefault(attack) + elif name == GLOWER_POWER: + suitTrack = doGlowerPower(attack) + elif name == GUILT_TRIP: + suitTrack = doGuiltTrip(attack) + elif name == HALF_WINDSOR: + suitTrack = doHalfWindsor(attack) + elif name == HANG_UP: + suitTrack = doHangUp(attack) + elif name == HEAD_SHRINK: + suitTrack = doHeadShrink(attack) + elif name == HOT_AIR: + suitTrack = doHotAir(attack) + elif name == JARGON: + suitTrack = doJargon(attack) + elif name == LEGALESE: + suitTrack = doLegalese(attack) + elif name == LIQUIDATE: + suitTrack = doLiquidate(attack) + elif name == MARKET_CRASH: + suitTrack = doMarketCrash(attack) + elif name == MUMBO_JUMBO: + suitTrack = doMumboJumbo(attack) + elif name == PARADIGM_SHIFT: + suitTrack = doParadigmShift(attack) + elif name == PECKING_ORDER: + suitTrack = doPeckingOrder(attack) + elif name == PICK_POCKET: + suitTrack = doPickPocket(attack) + elif name == PINK_SLIP: + suitTrack = doPinkSlip(attack) + elif name == PLAY_HARDBALL: + suitTrack = doPlayHardball(attack) + elif name == POUND_KEY: + suitTrack = doPoundKey(attack) + elif name == POWER_TIE: + suitTrack = doPowerTie(attack) + elif name == POWER_TRIP: + suitTrack = doPowerTrip(attack) + elif name == QUAKE: + suitTrack = doQuake(attack) + elif name == RAZZLE_DAZZLE: + suitTrack = doRazzleDazzle(attack) + elif name == RED_TAPE: + suitTrack = doRedTape(attack) + elif name == RE_ORG: + suitTrack = doReOrg(attack) + elif name == RESTRAINING_ORDER: + suitTrack = doRestrainingOrder(attack) + elif name == ROLODEX: + suitTrack = doRolodex(attack) + elif name == RUBBER_STAMP: + suitTrack = doRubberStamp(attack) + elif name == RUB_OUT: + suitTrack = doRubOut(attack) + elif name == SACKED: + suitTrack = doSacked(attack) + elif name == SANDTRAP: + suitTrack = doDefault(attack) + elif name == SCHMOOZE: + suitTrack = doSchmooze(attack) + elif name == SHAKE: + suitTrack = doShake(attack) + elif name == SHRED: + suitTrack = doShred(attack) + elif name == SONG_AND_DANCE: + suitTrack = doDefault(attack) + elif name == SPIN: + suitTrack = doSpin(attack) + elif name == SYNERGY: + suitTrack = doSynergy(attack) + elif name == TABULATE: + suitTrack = doTabulate(attack) + elif name == TEE_OFF: + suitTrack = doTeeOff(attack) + elif name == THROW_BOOK: + suitTrack = doThrowBook(attack) + elif name == TREMOR: + suitTrack = doTremor(attack) + elif name == WATERCOOLER: + suitTrack = doWatercooler(attack) + elif name == WITHDRAWAL: + suitTrack = doWithdrawal(attack) + elif name == WRITE_OFF: + suitTrack = doWriteOff(attack) + else: + notify.warning('unknown attack: %d substituting Finger Wag' % name) + suitTrack = doDefault(attack) + camTrack = MovieCamera.chooseSuitShot(attack, suitTrack.getDuration()) + battle = attack['battle'] + target = attack['target'] + groupStatus = attack['group'] + if groupStatus == ATK_TGT_SINGLE: + toon = target['toon'] + toonHprTrack = Sequence(Func(toon.headsUp, battle, MovieUtil.PNT3_ZERO), Func(toon.loop, 'neutral')) + else: + toonHprTrack = Parallel() + for t in target: + toon = t['toon'] + toonHprTrack.append(Sequence(Func(toon.headsUp, battle, MovieUtil.PNT3_ZERO), Func(toon.loop, 'neutral'))) + + suit = attack['suit'] + neutralIval = Func(suit.loop, 'neutral') + suitTrack = Sequence(suitTrack, neutralIval, toonHprTrack) + suitPos = suit.getPos(battle) + resetPos, resetHpr = battle.getActorPosHpr(suit) + if battle.isSuitLured(suit): + resetTrack = getResetTrack(suit, battle) + resetSuitTrack = Sequence(resetTrack, suitTrack) + waitTrack = Sequence(Wait(resetTrack.getDuration()), Func(battle.unlureSuit, suit)) + resetCamTrack = Sequence(waitTrack, camTrack) + return (resetSuitTrack, resetCamTrack) + else: + return (suitTrack, camTrack) + + +def getResetTrack(suit, battle): + resetPos, resetHpr = battle.getActorPosHpr(suit) + moveDist = Vec3(suit.getPos(battle) - resetPos).length() + moveDuration = 0.5 + walkTrack = Sequence(Func(suit.setHpr, battle, resetHpr), ActorInterval(suit, 'walk', startTime=1, duration=moveDuration, endTime=1e-05), Func(suit.loop, 'neutral')) + moveTrack = LerpPosInterval(suit, moveDuration, resetPos, other=battle) + return Parallel(walkTrack, moveTrack) + + +def __makeCancelledNodePath(): + tn = TextNode('CANCELLED') + tn.setFont(getSuitFont()) + tn.setText(TTLocalizer.MovieSuitCancelled) + tn.setAlign(TextNode.ACenter) + tntop = hidden.attachNewNode('CancelledTop') + tnpath = tntop.attachNewNode(tn) + tnpath.setPosHpr(0, 0, 0, 0, 0, 0) + tnpath.setScale(1) + tnpath.setColor(0.7, 0, 0, 1) + tnpathback = tnpath.instanceUnderNode(tntop, 'backside') + tnpathback.setPosHpr(0, 0, 0, 180, 0, 0) + tnpath.setScale(1) + return tntop + + +def doDefault(attack): + notify.debug('building suit attack in doDefault') + suitName = attack['suitName'] + if suitName == 'f': + attack['id'] = POUND_KEY + attack['name'] = 'PoundKey' + attack['animName'] = 'phone' + return doPoundKey(attack) + elif suitName == 'p': + attack['id'] = FOUNTAIN_PEN + attack['name'] = 'FountainPen' + attack['animName'] = 'pen-squirt' + return doFountainPen(attack) + elif suitName == 'ym': + attack['id'] = RUBBER_STAMP + attack['name'] = 'RubberStamp' + attack['animName'] = 'rubber-stamp' + return doRubberStamp(attack) + elif suitName == 'mm': + attack['id'] = FINGER_WAG + attack['name'] = 'FingerWag' + attack['animName'] = 'finger-wag' + return doFingerWag(attack) + elif suitName == 'ds': + attack['id'] = DEMOTION + attack['name'] = 'Demotion' + attack['animName'] = 'magic1' + return doDemotion(attack) + elif suitName == 'hh': + attack['id'] = GLOWER_POWER + attack['name'] = 'GlowerPower' + attack['animName'] = 'glower' + return doGlowerPower(attack) + elif suitName == 'cr': + attack['id'] = PICK_POCKET + attack['name'] = 'PickPocket' + attack['animName'] = 'pickpocket' + return doPickPocket(attack) + elif suitName == 'tbc': + attack['id'] = GLOWER_POWER + attack['name'] = 'GlowerPower' + attack['animName'] = 'glower' + return doGlowerPower(attack) + elif suitName == 'cc': + attack['id'] = POUND_KEY + attack['name'] = 'PoundKey' + attack['animName'] = 'phone' + return doPoundKey(attack) + elif suitName == 'tm': + attack['id'] = CLIPON_TIE + attack['name'] = 'ClipOnTie' + attack['animName'] = 'throw-paper' + return doClipOnTie(attack) + elif suitName == 'nd': + attack['id'] = PICK_POCKET + attack['name'] = 'PickPocket' + attack['animName'] = 'pickpocket' + return doPickPocket(attack) + elif suitName == 'gh': + attack['id'] = FOUNTAIN_PEN + attack['name'] = 'FountainPen' + attack['animName'] = 'pen-squirt' + return doFountainPen(attack) + elif suitName == 'ms': + attack['id'] = BRAIN_STORM + attack['name'] = 'BrainStorm' + attack['animName'] = 'effort' + return doBrainStorm(attack) + elif suitName == 'tf': + attack['id'] = RED_TAPE + attack['name'] = 'RedTape' + attack['animName'] = 'throw-object' + return doRedTape(attack) + elif suitName == 'm': + attack['id'] = BUZZ_WORD + attack['name'] = 'BuzzWord' + attack['animName'] = 'speak' + return doBuzzWord(attack) + elif suitName == 'mh': + attack['id'] = RAZZLE_DAZZLE + attack['name'] = 'RazzleDazzle' + attack['animName'] = 'smile' + return doRazzleDazzle(attack) + elif suitName == 'sc': + attack['id'] = WATERCOOLER + attack['name'] = 'Watercooler' + attack['animName'] = 'water-cooler' + return doWatercooler(attack) + elif suitName == 'pp': + attack['id'] = BOUNCE_CHECK + attack['name'] = 'BounceCheck' + attack['animName'] = 'throw-paper' + return doBounceCheck(attack) + elif suitName == 'tw': + attack['id'] = GLOWER_POWER + attack['name'] = 'GlowerPower' + attack['animName'] = 'glower' + return doGlowerPower(attack) + elif suitName == 'bc': + attack['id'] = AUDIT + attack['name'] = 'Audit' + attack['animName'] = 'phone' + return doAudit(attack) + elif suitName == 'nc': + attack['id'] = RED_TAPE + attack['name'] = 'RedTape' + attack['animName'] = 'throw-object' + return doRedTape(attack) + elif suitName == 'mb': + attack['id'] = LIQUIDATE + attack['name'] = 'Liquidate' + attack['animName'] = 'magic1' + return doLiquidate(attack) + elif suitName == 'ls': + attack['id'] = WRITE_OFF + attack['name'] = 'WriteOff' + attack['animName'] = 'hold-pencil' + return doWriteOff(attack) + elif suitName == 'rb': + attack['id'] = TEE_OFF + attack['name'] = 'TeeOff' + attack['animName'] = 'golf-club-swing' + return doTeeOff(attack) + elif suitName == 'bf': + attack['id'] = RUBBER_STAMP + attack['name'] = 'RubberStamp' + attack['animName'] = 'rubber-stamp' + return doRubberStamp(attack) + elif suitName == 'b': + attack['id'] = EVICTION_NOTICE + attack['name'] = 'EvictionNotice' + attack['animName'] = 'throw-paper' + return doEvictionNotice(attack) + elif suitName == 'dt': + attack['id'] = RUBBER_STAMP + attack['name'] = 'RubberStamp' + attack['animName'] = 'rubber-stamp' + return doRubberStamp(attack) + elif suitName == 'ac': + attack['id'] = RED_TAPE + attack['name'] = 'RedTape' + attack['animName'] = 'throw-object' + return doRedTape(attack) + elif suitName == 'bs': + attack['id'] = FINGER_WAG + attack['name'] = 'FingerWag' + attack['animName'] = 'finger-wag' + return doFingerWag(attack) + elif suitName == 'sd': + attack['id'] = WRITE_OFF + attack['name'] = 'WriteOff' + attack['animName'] = 'hold-pencil' + return doWriteOff(attack) + elif suitName == 'le': + attack['id'] = JARGON + attack['name'] = 'Jargon' + attack['animName'] = 'speak' + return doJargon(attack) + elif suitName == 'bw': + attack['id'] = FINGER_WAG + attack['name'] = 'FingerWag' + attack['animName'] = 'finger-wag' + return doFingerWag(attack) + else: + self.notify.error('doDefault() - unsupported suit type: %s' % suitName) + return None + + +def getSuitTrack(attack, delay = 1e-06, splicedAnims = None): + suit = attack['suit'] + battle = attack['battle'] + tauntIndex = attack['taunt'] + target = attack['target'] + toon = target['toon'] + targetPos = toon.getPos(battle) + taunt = getAttackTaunt(attack['name'], tauntIndex) + trapStorage = {} + trapStorage['trap'] = None + track = Sequence(Wait(delay), Func(suit.setChatAbsolute, taunt, CFSpeech | CFTimeout)) + + def reparentTrap(suit = suit, battle = battle, trapStorage = trapStorage): + trapProp = suit.battleTrapProp + if trapProp != None: + trapProp.wrtReparentTo(battle) + trapStorage['trap'] = trapProp + return + + track.append(Func(reparentTrap)) + track.append(Func(suit.headsUp, battle, targetPos)) + if splicedAnims: + track.append(getSplicedAnimsTrack(splicedAnims, actor=suit)) + else: + track.append(ActorInterval(suit, attack['animName'])) + origPos, origHpr = battle.getActorPosHpr(suit) + track.append(Func(suit.setHpr, battle, origHpr)) + + def returnTrapToSuit(suit = suit, trapStorage = trapStorage): + trapProp = trapStorage['trap'] + if trapProp != None: + if trapProp.getName() == 'traintrack': + notify.debug('deliberately not parenting traintrack to suit') + else: + trapProp.wrtReparentTo(suit) + suit.battleTrapProp = trapProp + return + + track.append(Func(returnTrapToSuit)) + track.append(Func(suit.clearChat)) + return track + + +def getSuitAnimTrack(attack, delay = 0): + suit = attack['suit'] + tauntIndex = attack['taunt'] + taunt = getAttackTaunt(attack['name'], tauntIndex) + return Sequence(Wait(delay), Func(suit.setChatAbsolute, taunt, CFSpeech | CFTimeout), ActorInterval(attack['suit'], attack['animName']), Func(suit.clearChat)) + + +def getPartTrack(particleEffect, startDelay, durationDelay, partExtraArgs): + particleEffect = partExtraArgs[0] + parent = partExtraArgs[1] + if len(partExtraArgs) > 2: + worldRelative = partExtraArgs[2] + else: + worldRelative = 1 + return Sequence(Wait(startDelay), ParticleInterval(particleEffect, parent, worldRelative, duration=durationDelay, cleanup=True)) + + +def getToonTrack(attack, damageDelay = 1e-06, damageAnimNames = None, dodgeDelay = 0.0001, dodgeAnimNames = None, splicedDamageAnims = None, splicedDodgeAnims = None, target = None, showDamageExtraTime = 0.01, showMissedExtraTime = 0.5): + if not target: + target = attack['target'] + toon = target['toon'] + battle = attack['battle'] + suit = attack['suit'] + suitPos = suit.getPos(battle) + dmg = target['hp'] + animTrack = Sequence() + animTrack.append(Func(toon.headsUp, battle, suitPos)) + if dmg > 0: + animTrack.append(getToonTakeDamageTrack(toon, attack, target['died'], dmg, damageDelay, damageAnimNames, splicedDamageAnims, showDamageExtraTime)) + return animTrack + else: + animTrack.append(getToonDodgeTrack(target, dodgeDelay, dodgeAnimNames, splicedDodgeAnims, showMissedExtraTime)) + indicatorTrack = Sequence(Wait(dodgeDelay + showMissedExtraTime), Func(MovieUtil.indicateMissed, toon)) + return Parallel(animTrack, indicatorTrack) + + +def getToonTracks(attack, damageDelay = 1e-06, damageAnimNames = None, dodgeDelay = 1e-06, dodgeAnimNames = None, splicedDamageAnims = None, splicedDodgeAnims = None, showDamageExtraTime = 0.01, showMissedExtraTime = 0.5): + toonTracks = Parallel() + targets = attack['target'] + for i in xrange(len(targets)): + tgt = targets[i] + toonTracks.append(getToonTrack(attack, damageDelay, damageAnimNames, dodgeDelay, dodgeAnimNames, splicedDamageAnims, splicedDodgeAnims, target=tgt, showDamageExtraTime=showDamageExtraTime, showMissedExtraTime=showMissedExtraTime)) + + return toonTracks + + +def getToonDodgeTrack(target, dodgeDelay, dodgeAnimNames, splicedDodgeAnims, showMissedExtraTime): + toon = target['toon'] + toonTrack = Sequence() + toonTrack.append(Wait(dodgeDelay)) + if dodgeAnimNames: + for d in dodgeAnimNames: + if d == 'sidestep': + toonTrack.append(getAllyToonsDodgeParallel(target)) + else: + toonTrack.append(ActorInterval(toon, d)) + + else: + toonTrack.append(getSplicedAnimsTrack(splicedDodgeAnims, actor=toon)) + toonTrack.append(Func(toon.loop, 'neutral')) + return toonTrack + + +def getAllyToonsDodgeParallel(target): + toon = target['toon'] + leftToons = target['leftToons'] + rightToons = target['rightToons'] + if len(leftToons) > len(rightToons): + PoLR = rightToons + PoMR = leftToons + else: + PoLR = leftToons + PoMR = rightToons + upper = 1 + 4 * abs(len(leftToons) - len(rightToons)) + if random.randint(0, upper) > 0: + toonDodgeList = PoLR + else: + toonDodgeList = PoMR + if toonDodgeList is leftToons: + sidestepAnim = 'sidestep-left' + soundEffect = globalBattleSoundCache.getSound('AV_side_step.ogg') + else: + sidestepAnim = 'sidestep-right' + soundEffect = globalBattleSoundCache.getSound('AV_jump_to_side.ogg') + toonTracks = Parallel() + for t in toonDodgeList: + toonTracks.append(Sequence(ActorInterval(t, sidestepAnim), Func(t.loop, 'neutral'))) + + toonTracks.append(Sequence(ActorInterval(toon, sidestepAnim), Func(toon.loop, 'neutral'))) + toonTracks.append(Sequence(Wait(0.5), SoundInterval(soundEffect, node=toon))) + return toonTracks + + +def getPropTrack(prop, parent, posPoints, appearDelay, remainDelay, scaleUpPoint = Point3(1), scaleUpTime = 0.5, scaleDownTime = 0.5, startScale = Point3(0.01), anim = 0, propName = 'none', animDuration = 0.0, animStartTime = 0.0): + if anim == 1: + track = Sequence(Wait(appearDelay), Func(__showProp, prop, parent, *posPoints), LerpScaleInterval(prop, scaleUpTime, scaleUpPoint, startScale=startScale), ActorInterval(prop, propName, duration=animDuration, startTime=animStartTime), Wait(remainDelay), Func(MovieUtil.removeProp, prop)) + else: + track = Sequence(Wait(appearDelay), Func(__showProp, prop, parent, *posPoints), LerpScaleInterval(prop, scaleUpTime, scaleUpPoint, startScale=startScale), Wait(remainDelay), LerpScaleInterval(prop, scaleDownTime, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProp, prop)) + return track + + +def getPropAppearTrack(prop, parent, posPoints, appearDelay, scaleUpPoint = Point3(1), scaleUpTime = 0.5, startScale = Point3(0.01), poseExtraArgs = None): + propTrack = Sequence(Wait(appearDelay), Func(__showProp, prop, parent, *posPoints)) + if poseExtraArgs: + propTrack.append(Func(prop.pose, *poseExtraArgs)) + propTrack.append(LerpScaleInterval(prop, scaleUpTime, scaleUpPoint, startScale=startScale)) + return propTrack + + +def getPropThrowTrack(attack, prop, hitPoints = [], missPoints = [], hitDuration = 0.5, missDuration = 0.5, hitPointNames = 'none', missPointNames = 'none', lookAt = 'none', groundPointOffSet = 0, missScaleDown = None, parent = render): + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + battle = attack['battle'] + + def getLambdas(list, prop, toon): + for i in xrange(len(list)): + if list[i] == 'face': + list[i] = lambda toon = toon: __toonFacePoint(toon) + elif list[i] == 'miss': + list[i] = lambda prop = prop, toon = toon: __toonMissPoint(prop, toon) + elif list[i] == 'bounceHit': + list[i] = lambda prop = prop, toon = toon: __throwBounceHitPoint(prop, toon) + elif list[i] == 'bounceMiss': + list[i] = lambda prop = prop, toon = toon: __throwBounceMissPoint(prop, toon) + + return list + + if hitPointNames != 'none': + hitPoints = getLambdas(hitPointNames, prop, toon) + if missPointNames != 'none': + missPoints = getLambdas(missPointNames, prop, toon) + propTrack = Sequence() + propTrack.append(Func(battle.movie.needRestoreRenderProp, prop)) + propTrack.append(Func(prop.wrtReparentTo, parent)) + if lookAt != 'none': + propTrack.append(Func(prop.lookAt, lookAt)) + if dmg > 0: + for i in xrange(len(hitPoints)): + pos = hitPoints[i] + propTrack.append(LerpPosInterval(prop, hitDuration, pos=pos)) + + else: + for i in xrange(len(missPoints)): + pos = missPoints[i] + propTrack.append(LerpPosInterval(prop, missDuration, pos=pos)) + + if missScaleDown: + propTrack.append(LerpScaleInterval(prop, missScaleDown, MovieUtil.PNT3_NEARZERO)) + propTrack.append(Func(MovieUtil.removeProp, prop)) + propTrack.append(Func(battle.movie.clearRenderProp, prop)) + return propTrack + + +def getThrowTrack(object, target, duration = 1.0, parent = render, gravity = -32.144): + values = {} + + def calcOriginAndVelocity(object = object, target = target, values = values, duration = duration, parent = parent, gravity = gravity): + if callable(target): + target = target() + object.wrtReparentTo(parent) + values['origin'] = object.getPos(parent) + origin = object.getPos(parent) + values['velocity'] = (target[2] - origin[2] - 0.5 * gravity * duration * duration) / duration + + return Sequence(Func(calcOriginAndVelocity), LerpFunctionInterval(throwPos, fromData=0.0, toData=1.0, duration=duration, extraArgs=[object, + duration, + target, + values, + gravity])) + + +def throwPos(t, object, duration, target, values, gravity = -32.144): + origin = values['origin'] + velocity = values['velocity'] + if callable(target): + target = target() + x = origin[0] * (1 - t) + target[0] * t + y = origin[1] * (1 - t) + target[1] * t + time = t * duration + z = origin[2] + velocity * time + 0.5 * gravity * time * time + object.setPos(x, y, z) + + +def getToonTakeDamageTrack(toon, attack, died, dmg, delay, damageAnimNames = None, splicedDamageAnims = None, showDamageExtraTime = 0.01): + toonTrack = Sequence() + toonTrack.append(Wait(delay)) + if damageAnimNames: + for d in damageAnimNames: + toonTrack.append(ActorInterval(toon, d)) + + indicatorTrack = Sequence(Wait(delay + showDamageExtraTime), Func(__doDamage, toon, dmg, died)) + else: + splicedAnims = getSplicedAnimsTrack(splicedDamageAnims, actor=toon) + toonTrack.append(splicedAnims) + indicatorTrack = Sequence(Wait(delay + showDamageExtraTime), Func(__doDamage, toon, dmg, died)) + toonTrack.append(Func(toon.loop, 'neutral')) + if died: + pbpText = attack['playByPlayText'] + + toonTrack.append(pbpText.getToonsDiedInterval([TTLocalizer.ToonDefeatedMessage % toon.getName()], 7.0)) + return Parallel(toonTrack, indicatorTrack) + + +def getSplicedAnimsTrack(anims, actor = None): + track = Sequence() + for nextAnim in anims: + delay = 1e-06 + if len(nextAnim) >= 2: + if nextAnim[1] > 0: + delay = nextAnim[1] + if len(nextAnim) <= 0: + track.append(Wait(delay)) + elif len(nextAnim) == 1: + track.append(ActorInterval(actor, nextAnim[0])) + elif len(nextAnim) == 2: + track.append(Wait(delay)) + track.append(ActorInterval(actor, nextAnim[0])) + elif len(nextAnim) == 3: + track.append(Wait(delay)) + track.append(ActorInterval(actor, nextAnim[0], startTime=nextAnim[2])) + elif len(nextAnim) == 4: + track.append(Wait(delay)) + duration = nextAnim[3] + if duration < 0: + startTime = nextAnim[2] + endTime = startTime + duration + if endTime <= 0: + endTime = 0.01 + track.append(ActorInterval(actor, nextAnim[0], startTime=startTime, endTime=endTime)) + else: + track.append(ActorInterval(actor, nextAnim[0], startTime=nextAnim[2], duration=duration)) + elif len(nextAnim) == 5: + track.append(Wait(delay)) + track.append(ActorInterval(nextAnim[4], nextAnim[0], startTime=nextAnim[2], duration=nextAnim[3])) + + return track + + +def getSplicedLerpAnims(animName, origDuration, newDuration, startTime = 0, fps = 30, reverse = 0): + anims = [] + addition = 0 + numAnims = origDuration * fps + timeInterval = newDuration / numAnims + animInterval = origDuration / numAnims + if reverse == 1: + animInterval = -animInterval + for i in xrange(0, int(numAnims)): + anims.append([animName, + timeInterval, + startTime + addition, + animInterval]) + addition += animInterval + + return anims + + +def getSoundTrack(fileName, delay = 0.01, duration = None, node = None): + soundEffect = globalBattleSoundCache.getSound(fileName) + if duration: + return Sequence(Wait(delay), SoundInterval(soundEffect, duration=duration, node=node)) + else: + return Sequence(Wait(delay), SoundInterval(soundEffect, node=node)) + + +def doClipOnTie(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + tie = globalPropPool.getProp('clip-on-tie') + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'a': + throwDelay = 2.17 + damageDelay = 3.3 + dodgeDelay = 3.1 + elif suitType == 'b': + throwDelay = 2.17 + damageDelay = 3.3 + dodgeDelay = 3.1 + elif suitType == 'c': + throwDelay = 1.45 + damageDelay = 2.61 + dodgeDelay = 2.34 + suitTrack = getSuitTrack(attack) + posPoints = [Point3(0.66, 0.51, 0.28), VBase3(-69.652, -17.199, 67.96)] + tiePropTrack = Sequence(getPropAppearTrack(tie, suit.getRightHand(), posPoints, 0.5, MovieUtil.PNT3_ONE, scaleUpTime=0.5, poseExtraArgs=['clip-on-tie', 0])) + if dmg > 0: + tiePropTrack.append(ActorInterval(tie, 'clip-on-tie', duration=throwDelay, startTime=1.1)) + else: + tiePropTrack.append(Wait(throwDelay)) + tiePropTrack.append(Func(tie.setHpr, Point3(0, -90, 0))) + tiePropTrack.append(getPropThrowTrack(attack, tie, [__toonFacePoint(toon)], [__toonGroundPoint(attack, toon, 0.1)], hitDuration=0.4, missDuration=0.8, missScaleDown=1.2)) + toonTrack = getToonTrack(attack, damageDelay, ['conked'], dodgeDelay, ['sidestep']) + throwSound = getSoundTrack('SA_powertie_throw.ogg', delay=throwDelay + 1, node=suit) + return Parallel(suitTrack, toonTrack, tiePropTrack, throwSound) + + +def doPoundKey(attack): + suit = attack['suit'] + battle = attack['battle'] + phone = globalPropPool.getProp('phone') + receiver = globalPropPool.getProp('receiver') + BattleParticles.loadParticles() + particleEffect = BattleParticles.createParticleEffect('PoundKey') + BattleParticles.setEffectTexture(particleEffect, 'poundsign', color=Vec4(0, 0, 0, 1)) + suitTrack = getSuitTrack(attack) + partTrack = getPartTrack(particleEffect, 2.1, 1.55, [particleEffect, suit, 0]) + phonePosPoints = [Point3(0.23, 0.17, -0.11), VBase3(5.939, 2.763, -177.591)] + receiverPosPoints = [Point3(0.23, 0.17, -0.11), VBase3(5.939, 2.763, -177.591)] + propTrack = Sequence(Wait(0.3), Func(__showProp, phone, suit.getLeftHand(), phonePosPoints[0], phonePosPoints[1]), Func(__showProp, receiver, suit.getLeftHand(), receiverPosPoints[0], receiverPosPoints[1]), LerpScaleInterval(phone, 0.5, MovieUtil.PNT3_ONE, MovieUtil.PNT3_NEARZERO), Wait(0.74), Func(receiver.wrtReparentTo, suit.getRightHand()), LerpPosHprInterval(receiver, 0.0001, Point3(-0.45, 0.48, -0.62), VBase3(-87.47, -18.21, 7.82)), Wait(3.14), Func(receiver.wrtReparentTo, phone), Wait(0.62), LerpScaleInterval(phone, 0.5, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProps, [receiver, phone])) + toonTrack = getToonTrack(attack, 2.7, ['cringe'], 1.9, ['sidestep']) + soundTrack = getSoundTrack('SA_hangup.ogg', delay=1.3, node=suit) + return Parallel(suitTrack, toonTrack, propTrack, partTrack, soundTrack) + + +def doShred(attack): + suit = attack['suit'] + battle = attack['battle'] + paper = globalPropPool.getProp('shredder-paper') + shredder = globalPropPool.getProp('shredder') + particleEffect = BattleParticles.createParticleEffect('Shred') + suitTrack = getSuitTrack(attack) + partTrack = getPartTrack(particleEffect, 3.5, 1.9, [particleEffect, suit, 0]) + paperPosPoints = [Point3(0.59, -0.31, 0.81), VBase3(79.224, 32.576, -179.449)] + paperPropTrack = getPropTrack(paper, suit.getRightHand(), paperPosPoints, 2.4, 1e-05, scaleUpTime=0.2, anim=1, propName='shredder-paper', animDuration=1.5, animStartTime=2.8) + shredderPosPoints = [Point3(0, -0.12, -0.34), VBase3(-90.0, -53.77, -0.0)] + shredderPropTrack = getPropTrack(shredder, suit.getLeftHand(), shredderPosPoints, 1, 3, scaleUpPoint=Point3(4.81, 4.81, 4.81)) + toonTrack = getToonTrack(attack, suitTrack.getDuration() - 1.1, ['conked'], suitTrack.getDuration() - 3.1, ['sidestep']) + soundTrack = getSoundTrack('SA_shred.ogg', delay=3.4, node=suit) + return Parallel(suitTrack, paperPropTrack, shredderPropTrack, partTrack, toonTrack, soundTrack) + + +def doFillWithLead(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + pencil = globalPropPool.getProp('pencil') + sharpener = globalPropPool.getProp('sharpener') + BattleParticles.loadParticles() + sprayEffect = BattleParticles.createParticleEffect(file='fillWithLeadSpray') + headSmotherEffect = BattleParticles.createParticleEffect(file='fillWithLeadSmother') + torsoSmotherEffect = BattleParticles.createParticleEffect(file='fillWithLeadSmother') + legsSmotherEffect = BattleParticles.createParticleEffect(file='fillWithLeadSmother') + BattleParticles.setEffectTexture(sprayEffect, 'roll-o-dex', color=Vec4(0, 0, 0, 1)) + BattleParticles.setEffectTexture(headSmotherEffect, 'roll-o-dex', color=Vec4(0, 0, 0, 1)) + BattleParticles.setEffectTexture(torsoSmotherEffect, 'roll-o-dex', color=Vec4(0, 0, 0, 1)) + BattleParticles.setEffectTexture(legsSmotherEffect, 'roll-o-dex', color=Vec4(0, 0, 0, 1)) + suitTrack = getSuitTrack(attack) + sprayTrack = getPartTrack(sprayEffect, 2.5, 1.9, [sprayEffect, suit, 0]) + pencilPosPoints = [Point3(-0.29, -0.33, -0.13), VBase3(160.565, -11.653, -169.244)] + pencilPropTrack = getPropTrack(pencil, suit.getRightHand(), pencilPosPoints, 0.7, 3.2, scaleUpTime=0.2) + sharpenerPosPoints = [Point3(0.0, 0.0, -0.03), MovieUtil.PNT3_ZERO] + sharpenerPropTrack = getPropTrack(sharpener, suit.getLeftHand(), sharpenerPosPoints, 1.3, 2.3, scaleUpPoint=MovieUtil.PNT3_ONE) + damageAnims = [] + damageAnims.append(['conked', + suitTrack.getDuration() - 1.5, + 1e-05, + 1.4]) + damageAnims.append(['conked', + 1e-05, + 0.7, + 0.7]) + damageAnims.append(['conked', + 1e-05, + 0.7, + 0.7]) + damageAnims.append(['conked', 1e-05, 1.4]) + toonTrack = getToonTrack(attack, splicedDamageAnims=damageAnims, dodgeDelay=suitTrack.getDuration() - 3.1, dodgeAnimNames=['sidestep'], showDamageExtraTime=4.5, showMissedExtraTime=1.6) + animal = toon.style.getAnimal() + bodyScale = ToontownGlobals.toonBodyScales[animal] + headEffectHeight = __toonFacePoint(toon).getZ() + legsHeight = ToontownGlobals.legHeightDict[toon.style.legs] * bodyScale + torsoEffectHeight = ToontownGlobals.torsoHeightDict[toon.style.torso] * bodyScale / 2 + legsHeight + legsEffectHeight = legsHeight / 2 + effectX = headSmotherEffect.getX() + effectY = headSmotherEffect.getY() + headSmotherEffect.setPos(effectX, effectY - 1.5, headEffectHeight) + torsoSmotherEffect.setPos(effectX, effectY - 1, torsoEffectHeight) + legsSmotherEffect.setPos(effectX, effectY - 0.6, legsEffectHeight) + partDelay = 3.5 + partIvalDelay = 0.7 + partDuration = 1.0 + headTrack = getPartTrack(headSmotherEffect, partDelay, partDuration, [headSmotherEffect, toon, 0]) + torsoTrack = getPartTrack(torsoSmotherEffect, partDelay + partIvalDelay, partDuration, [torsoSmotherEffect, toon, 0]) + legsTrack = getPartTrack(legsSmotherEffect, partDelay + partIvalDelay * 2, partDuration, [legsSmotherEffect, toon, 0]) + + def colorParts(parts): + track = Parallel() + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + track.append(Func(nextPart.setColorScale, Vec4(0, 0, 0, 1))) + + return track + + def resetParts(parts): + track = Parallel() + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + track.append(Func(nextPart.clearColorScale)) + + return track + + if dmg > 0: + colorTrack = Sequence() + headParts = toon.getHeadParts() + torsoParts = toon.getTorsoParts() + legsParts = toon.getLegsParts() + colorTrack.append(Wait(partDelay + 0.2)) + colorTrack.append(Func(battle.movie.needRestoreColor)) + colorTrack.append(colorParts(headParts)) + colorTrack.append(Wait(partIvalDelay)) + colorTrack.append(colorParts(torsoParts)) + colorTrack.append(Wait(partIvalDelay)) + colorTrack.append(colorParts(legsParts)) + colorTrack.append(Wait(2.5)) + colorTrack.append(resetParts(headParts)) + colorTrack.append(resetParts(torsoParts)) + colorTrack.append(resetParts(legsParts)) + colorTrack.append(Func(battle.movie.clearRestoreColor)) + return Parallel(suitTrack, pencilPropTrack, sharpenerPropTrack, sprayTrack, headTrack, torsoTrack, legsTrack, colorTrack, toonTrack) + else: + return Parallel(suitTrack, pencilPropTrack, sharpenerPropTrack, sprayTrack, toonTrack) + + +def doFountainPen(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + pen = globalPropPool.getProp('pen') + + def getPenTip(pen = pen): + tip = pen.find('**/joint_toSpray') + return tip.getPos(render) + + hitPoint = lambda toon = toon: __toonFacePoint(toon) + missPoint = lambda prop = pen, toon = toon: __toonMissPoint(prop, toon, 0, parent=render) + hitSprayTrack = MovieUtil.getSprayTrack(battle, VBase4(0, 0, 0, 1), getPenTip, hitPoint, 0.2, 0.2, 0.2, horizScale=0.1, vertScale=0.1) + missSprayTrack = MovieUtil.getSprayTrack(battle, VBase4(0, 0, 0, 1), getPenTip, missPoint, 0.2, 0.2, 0.2, horizScale=0.1, vertScale=0.1) + suitTrack = getSuitTrack(attack) + propTrack = Sequence(Wait(0.01), Func(__showProp, pen, suit.getRightHand(), MovieUtil.PNT3_ZERO), LerpScaleInterval(pen, 0.5, Point3(1.5, 1.5, 1.5)), Wait(1.05)) + if dmg > 0: + propTrack.append(hitSprayTrack) + else: + propTrack.append(missSprayTrack) + propTrack += [LerpScaleInterval(pen, 0.5, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProp, pen)] + splashTrack = Sequence() + if dmg > 0: + + def prepSplash(splash, targetPoint): + splash.reparentTo(render) + splash.setPos(targetPoint) + scale = splash.getScale() + splash.setBillboardPointWorld() + splash.setScale(scale) + + splash = globalPropPool.getProp('splash-from-splat') + splash.setColor(0, 0, 0, 1) + splash.setScale(0.15) + splashTrack = Sequence(Func(battle.movie.needRestoreRenderProp, splash), Wait(1.65), Func(prepSplash, splash, __toonFacePoint(toon)), ActorInterval(splash, 'splash-from-splat'), Func(MovieUtil.removeProp, splash), Func(battle.movie.clearRenderProp, splash)) + headParts = toon.getHeadParts() + splashTrack.append(Func(battle.movie.needRestoreColor)) + for partNum in xrange(0, headParts.getNumPaths()): + nextPart = headParts.getPath(partNum) + splashTrack.append(Func(nextPart.setColorScale, Vec4(0, 0, 0, 1))) + + splashTrack.append(Func(MovieUtil.removeProp, splash)) + splashTrack.append(Wait(2.6)) + for partNum in xrange(0, headParts.getNumPaths()): + nextPart = headParts.getPath(partNum) + splashTrack.append(Func(nextPart.clearColorScale)) + + splashTrack.append(Func(battle.movie.clearRestoreColor)) + penSpill = BattleParticles.createParticleEffect(file='penSpill') + penSpill.setPos(getPenTip()) + penSpillTrack = getPartTrack(penSpill, 1.4, 0.7, [penSpill, pen, 0]) + toonTrack = getToonTrack(attack, 1.81, ['conked'], dodgeDelay=0.11, splicedDodgeAnims=[['duck', 0.01, 0.6]], showMissedExtraTime=1.66) + soundTrack = getSoundTrack('SA_fountain_pen.ogg', delay=1.6, node=suit) + return Parallel(suitTrack, toonTrack, propTrack, soundTrack, penSpillTrack, splashTrack) + + +def doRubOut(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + pad = globalPropPool.getProp('pad') + pencil = globalPropPool.getProp('pencil') + headEffect = BattleParticles.createParticleEffect('RubOut', color=toon.style.getHeadColor()) + torsoEffect = BattleParticles.createParticleEffect('RubOut', color=toon.style.getArmColor()) + legsEffect = BattleParticles.createParticleEffect('RubOut', color=toon.style.getLegColor()) + suitTrack = getSuitTrack(attack) + padPosPoints = [Point3(-0.66, 0.81, -0.06), VBase3(14.93, -2.29, 180.0)] + padPropTrack = getPropTrack(pad, suit.getLeftHand(), padPosPoints, 0.5, 2.57) + pencilPosPoints = [Point3(0.04, -0.38, -0.1), VBase3(-170.223, -3.762, -62.929)] + pencilPropTrack = getPropTrack(pencil, suit.getRightHand(), pencilPosPoints, 0.5, 2.57) + toonTrack = getToonTrack(attack, 2.2, ['conked'], 2.0, ['jump']) + hideTrack = Sequence() + headParts = toon.getHeadParts() + torsoParts = toon.getTorsoParts() + legsParts = toon.getLegsParts() + animal = toon.style.getAnimal() + bodyScale = ToontownGlobals.toonBodyScales[animal] + headEffectHeight = __toonFacePoint(toon).getZ() + legsHeight = ToontownGlobals.legHeightDict[toon.style.legs] * bodyScale + torsoEffectHeight = ToontownGlobals.torsoHeightDict[toon.style.torso] * bodyScale / 2 + legsHeight + legsEffectHeight = legsHeight / 2 + effectX = headEffect.getX() + effectY = headEffect.getY() + headEffect.setPos(effectX, effectY - 1.5, headEffectHeight) + torsoEffect.setPos(effectX, effectY - 1, torsoEffectHeight) + legsEffect.setPos(effectX, effectY - 0.6, legsEffectHeight) + partDelay = 2.5 + headTrack = getPartTrack(headEffect, partDelay + 0, 0.5, [headEffect, toon, 0]) + torsoTrack = getPartTrack(torsoEffect, partDelay + 1.1, 0.5, [torsoEffect, toon, 0]) + legsTrack = getPartTrack(legsEffect, partDelay + 2.2, 0.5, [legsEffect, toon, 0]) + + def hideParts(parts): + track = Parallel() + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + track.append(Func(nextPart.setTransparency, 1)) + track.append(LerpFunctionInterval(nextPart.setAlphaScale, fromData=1, toData=0, duration=0.2)) + + return track + + def showParts(parts): + track = Parallel() + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + track.append(Func(nextPart.clearColorScale)) + track.append(Func(nextPart.clearTransparency)) + + return track + + soundTrack = getSoundTrack('SA_rubout.ogg', delay=1.7, node=suit) + if dmg > 0: + hideTrack.append(Wait(2.2)) + hideTrack.append(Func(battle.movie.needRestoreColor)) + hideTrack.append(hideParts(headParts)) + hideTrack.append(Wait(0.4)) + hideTrack.append(hideParts(torsoParts)) + hideTrack.append(Wait(0.4)) + hideTrack.append(hideParts(legsParts)) + hideTrack.append(Wait(1)) + hideTrack.append(showParts(headParts)) + hideTrack.append(showParts(torsoParts)) + hideTrack.append(showParts(legsParts)) + hideTrack.append(Func(battle.movie.clearRestoreColor)) + return Parallel(suitTrack, toonTrack, padPropTrack, pencilPropTrack, soundTrack, hideTrack, headTrack, torsoTrack, legsTrack) + else: + return Parallel(suitTrack, toonTrack, padPropTrack, pencilPropTrack, soundTrack) + + +def doFingerWag(attack): + suit = attack['suit'] + battle = attack['battle'] + BattleParticles.loadParticles() + particleEffect = BattleParticles.createParticleEffect('FingerWag') + BattleParticles.setEffectTexture(particleEffect, 'blah', color=Vec4(0.55, 0, 0.55, 1)) + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'a': + partDelay = 1.3 + damageDelay = 2.7 + dodgeDelay = 1.7 + elif suitType == 'b': + partDelay = 1.3 + damageDelay = 2.7 + dodgeDelay = 1.8 + elif suitType == 'c': + partDelay = 1.3 + damageDelay = 2.7 + dodgeDelay = 2.0 + suitTrack = getSuitTrack(attack) + partTrack = getPartTrack(particleEffect, partDelay, 2, [particleEffect, suit, 0]) + suitName = attack['suitName'] + if suitName == 'mm': + particleEffect.setPos(0.167, 1.5, 2.731) + elif suitName == 'tw': + particleEffect.setPos(0.167, 1.8, 5) + particleEffect.setHpr(-90.0, -60.0, 180.0) + elif suitName == 'pp': + particleEffect.setPos(0.167, 1, 4.1) + elif suitName == 'bs': + particleEffect.setPos(0.167, 1, 5.1) + elif suitName == 'bw': + particleEffect.setPos(0.167, 1.9, suit.getHeight() - 1.8) + particleEffect.setP(-110) + toonTrack = getToonTrack(attack, damageDelay, ['slip-backward'], dodgeDelay, ['sidestep']) + soundTrack = getSoundTrack('SA_finger_wag.ogg', delay=1.3, node=suit) + return Parallel(suitTrack, toonTrack, partTrack, soundTrack) + + +def doWriteOff(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + pad = globalPropPool.getProp('pad') + pencil = globalPropPool.getProp('pencil') + BattleParticles.loadParticles() + checkmark = MovieUtil.copyProp(BattleParticles.getParticle('checkmark')) + checkmark.setBillboardPointEye() + suitTrack = getSuitTrack(attack) + padPosPoints = [Point3(-0.25, 1.38, -0.08), VBase3(-19.078, -6.603, -171.594)] + padPropTrack = getPropTrack(pad, suit.getLeftHand(), padPosPoints, 0.5, 2.57, Point3(1.89, 1.89, 1.89)) + missPoint = lambda checkmark = checkmark, toon = toon: __toonMissPoint(checkmark, toon) + pencilPosPoints = [Point3(-0.47, 1.08, 0.28), VBase3(21.045, 12.702, -176.374)] + extraArgsForShowProp = [pencil, suit.getRightHand()] + extraArgsForShowProp.extend(pencilPosPoints) + pencilPropTrack = Sequence(Wait(0.5), Func(__showProp, *extraArgsForShowProp), LerpScaleInterval(pencil, 0.5, Point3(1.5, 1.5, 1.5), startScale=Point3(0.01)), Wait(2), Func(battle.movie.needRestoreRenderProp, checkmark), Func(checkmark.reparentTo, render), Func(checkmark.setScale, 1.6), Func(checkmark.setPosHpr, pencil, 0, 0, 0, 0, 0, 0), Func(checkmark.setP, 0), Func(checkmark.setR, 0)) + pencilPropTrack.append(getPropThrowTrack(attack, checkmark, [__toonFacePoint(toon)], [missPoint])) + pencilPropTrack.append(Func(MovieUtil.removeProp, checkmark)) + pencilPropTrack.append(Func(battle.movie.clearRenderProp, checkmark)) + pencilPropTrack.append(Wait(0.3)) + pencilPropTrack.append(LerpScaleInterval(pencil, 0.5, MovieUtil.PNT3_NEARZERO)) + pencilPropTrack.append(Func(MovieUtil.removeProp, pencil)) + toonTrack = getToonTrack(attack, 3.4, ['slip-forward'], 2.4, ['sidestep']) + soundTrack = Sequence(Wait(2.3), SoundInterval(globalBattleSoundCache.getSound('SA_writeoff_pen_only.ogg'), duration=0.9, node=suit), SoundInterval(globalBattleSoundCache.getSound('SA_writeoff_ding_only.ogg'), node=suit)) + return Parallel(suitTrack, toonTrack, padPropTrack, pencilPropTrack, soundTrack) + + +def doRubberStamp(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + suitTrack = getSuitTrack(attack) + stamp = globalPropPool.getProp('rubber-stamp') + pad = globalPropPool.getProp('pad') + cancelled = __makeCancelledNodePath() + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'a': + padPosPoints = [Point3(-0.65, 0.83, -0.04), VBase3(5.625, 4.456, -165.125)] + stampPosPoints = [Point3(-0.64, -0.17, -0.03), MovieUtil.PNT3_ZERO] + elif suitType == 'c': + padPosPoints = [Point3(0.19, -0.55, -0.21), VBase3(-166.76, -4.001, -1.658)] + stampPosPoints = [Point3(-0.64, -0.08, 0.11), MovieUtil.PNT3_ZERO] + else: + padPosPoints = [Point3(-0.65, 0.83, -0.04), VBase3(5.625, 4.456, -165.125)] + stampPosPoints = [Point3(-0.64, -0.17, -0.03), MovieUtil.PNT3_ZERO] + padPropTrack = getPropTrack(pad, suit.getLeftHand(), padPosPoints, 1e-06, 3.2) + missPoint = lambda cancelled = cancelled, toon = toon: __toonMissPoint(cancelled, toon) + propTrack = Sequence(Func(__showProp, stamp, suit.getRightHand(), stampPosPoints[0], stampPosPoints[1]), LerpScaleInterval(stamp, 0.5, MovieUtil.PNT3_ONE), Wait(2.6), Func(battle.movie.needRestoreRenderProp, cancelled), Func(cancelled.reparentTo, render), Func(cancelled.setScale, 0.6), Func(cancelled.setPosHpr, stamp, 0.81, -1.11, -0.16, 0, 0, 90), Func(cancelled.setP, 0), Func(cancelled.setR, 0)) + propTrack.append(getPropThrowTrack(attack, cancelled, [__toonFacePoint(toon)], [missPoint])) + propTrack.append(Func(MovieUtil.removeProp, cancelled)) + propTrack.append(Func(battle.movie.clearRenderProp, cancelled)) + propTrack.append(Wait(0.3)) + propTrack.append(LerpScaleInterval(stamp, 0.5, MovieUtil.PNT3_NEARZERO)) + propTrack.append(Func(MovieUtil.removeProp, stamp)) + toonTrack = getToonTrack(attack, 3.4, ['conked'], 1.9, ['sidestep']) + soundTrack = getSoundTrack('SA_rubber_stamp.ogg', delay=1.3, duration=1.1, node=suit) + return Parallel(suitTrack, toonTrack, propTrack, padPropTrack, soundTrack) + + +def doRazzleDazzle(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + hitSuit = dmg > 0 + sign = globalPropPool.getProp('smile') + BattleParticles.loadParticles() + particleEffect = BattleParticles.createParticleEffect('Smile') + suitTrack = getSuitTrack(attack) + signPosPoints = [Point3(0.0, -0.42, -0.04), VBase3(105.715, 73.977, 65.932)] + if hitSuit: + hitPoint = lambda toon = toon: __toonFacePoint(toon) + else: + hitPoint = lambda particleEffect = particleEffect, toon = toon, suit = suit: __toonMissPoint(particleEffect, toon, parent=suit.getRightHand()) + signPropTrack = Sequence(Wait(0.5), Func(__showProp, sign, suit.getRightHand(), signPosPoints[0], signPosPoints[1]), LerpScaleInterval(sign, 0.5, Point3(1.39, 1.39, 1.39)), Wait(0.5), Func(battle.movie.needRestoreParticleEffect, particleEffect), Func(particleEffect.start, sign), Func(particleEffect.wrtReparentTo, render), LerpPosInterval(particleEffect, 2.0, pos=hitPoint), Func(particleEffect.cleanup), Func(battle.movie.clearRestoreParticleEffect, particleEffect)) + signPropAnimTrack = ActorInterval(sign, 'smile', duration=4, startTime=0) + toonTrack = getToonTrack(attack, 2.6, ['cringe'], 1.9, ['sidestep']) + soundTrack = getSoundTrack('SA_razzle_dazzle.ogg', delay=1.6, node=suit) + return Sequence(Parallel(suitTrack, signPropTrack, signPropAnimTrack, toonTrack, soundTrack), Func(MovieUtil.removeProp, sign)) + + +def doSynergy(attack): + suit = attack['suit'] + battle = attack['battle'] + targets = attack['target'] + damageDelay = 1.7 + hitAtleastOneToon = 0 + for t in targets: + if t['hp'] > 0: + hitAtleastOneToon = 1 + + particleEffect = BattleParticles.createParticleEffect('Synergy') + waterfallEffect = BattleParticles.createParticleEffect(file='synergyWaterfall') + suitTrack = getSuitAnimTrack(attack) + partTrack = getPartTrack(particleEffect, 1.0, 1.9, [particleEffect, suit, 0]) + waterfallTrack = getPartTrack(waterfallEffect, 0.8, 1.9, [waterfallEffect, suit, 0]) + damageAnims = [['slip-forward']] + dodgeAnims = [] + dodgeAnims.append(['jump', + 0.01, + 0, + 0.6]) + dodgeAnims.extend(getSplicedLerpAnims('jump', 0.31, 1.3, startTime=0.6)) + dodgeAnims.append(['jump', 0, 0.91]) + toonTracks = getToonTracks(attack, damageDelay=damageDelay, damageAnimNames=['slip-forward'], dodgeDelay=0.91, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=1.0) + synergySoundTrack = Sequence(Wait(0.9), SoundInterval(globalBattleSoundCache.getSound('SA_synergy.ogg'), node=suit)) + if hitAtleastOneToon > 0: + fallingSoundTrack = Sequence(Wait(damageDelay + 0.5), SoundInterval(globalBattleSoundCache.getSound('Toon_bodyfall_synergy.ogg'), node=suit)) + return Parallel(suitTrack, partTrack, waterfallTrack, synergySoundTrack, fallingSoundTrack, toonTracks) + else: + return Parallel(suitTrack, partTrack, waterfallTrack, synergySoundTrack, toonTracks) + + +def doTeeOff(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + club = globalPropPool.getProp('golf-club') + ball = globalPropPool.getProp('golf-ball') + suitTrack = getSuitTrack(attack) + clubPosPoints = [MovieUtil.PNT3_ZERO, VBase3(63.097, 43.988, -18.435)] + clubPropTrack = getPropTrack(club, suit.getLeftHand(), clubPosPoints, 0.5, 5.2, Point3(1.1, 1.1, 1.1)) + suitName = attack['suitName'] + if suitName == 'ym': + ballPosPoints = [Point3(2.1, 0, 0.1)] + elif suitName == 'tbc': + ballPosPoints = [Point3(4.1, 0, 0.1)] + elif suitName == 'm': + ballPosPoints = [Point3(3.2, 0, 0.1)] + elif suitName == 'rb': + ballPosPoints = [Point3(4.2, 0, 0.1)] + else: + ballPosPoints = [Point3(2.1, 0, 0.1)] + ballPropTrack = Sequence(getPropAppearTrack(ball, suit, ballPosPoints, 1.7, Point3(1.5, 1.5, 1.5)), Func(battle.movie.needRestoreRenderProp, ball), Func(ball.wrtReparentTo, render), Wait(2.15)) + missPoint = lambda ball = ball, toon = toon: __toonMissPoint(ball, toon) + ballPropTrack.append(getPropThrowTrack(attack, ball, [__toonFacePoint(toon)], [missPoint])) + ballPropTrack.append(Func(battle.movie.clearRenderProp, ball)) + dodgeDelay = suitTrack.getDuration() - 4.35 + toonTrack = getToonTrack(attack, suitTrack.getDuration() - 2.25, ['conked'], dodgeDelay, ['duck'], showMissedExtraTime=1.7) + soundTrack = getSoundTrack('SA_tee_off.ogg', delay=4.1, node=suit) + return Parallel(suitTrack, toonTrack, clubPropTrack, ballPropTrack, soundTrack) + + +def doBrainStorm(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + BattleParticles.loadParticles() + snowEffect = BattleParticles.createParticleEffect('BrainStorm') + snowEffect2 = BattleParticles.createParticleEffect('BrainStorm') + snowEffect3 = BattleParticles.createParticleEffect('BrainStorm') + effectColor = Vec4(0.65, 0.79, 0.93, 0.85) + BattleParticles.setEffectTexture(snowEffect, 'brainstorm-box', color=effectColor) + BattleParticles.setEffectTexture(snowEffect2, 'brainstorm-env', color=effectColor) + BattleParticles.setEffectTexture(snowEffect3, 'brainstorm-track', color=effectColor) + cloud = globalPropPool.getProp('stormcloud') + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'a': + partDelay = 1.2 + damageDelay = 4.5 + dodgeDelay = 3.3 + elif suitType == 'b': + partDelay = 1.2 + damageDelay = 4.5 + dodgeDelay = 3.3 + elif suitType == 'c': + partDelay = 1.2 + damageDelay = 4.5 + dodgeDelay = 3.3 + suitTrack = getSuitTrack(attack, delay=0.9) + initialCloudHeight = suit.height + 3 + cloudPosPoints = [Point3(0, 3, initialCloudHeight), VBase3(180, 0, 0)] + cloudPropTrack = Sequence() + cloudPropTrack.append(Func(cloud.pose, 'stormcloud', 0)) + cloudPropTrack.append(getPropAppearTrack(cloud, suit, cloudPosPoints, 1e-06, Point3(3, 3, 3), scaleUpTime=0.7)) + cloudPropTrack.append(Func(battle.movie.needRestoreRenderProp, cloud)) + cloudPropTrack.append(Func(cloud.wrtReparentTo, render)) + targetPoint = __toonFacePoint(toon) + targetPoint.setZ(targetPoint[2] + 3) + cloudPropTrack.append(Wait(1.1)) + cloudPropTrack.append(LerpPosInterval(cloud, 1, pos=targetPoint)) + cloudPropTrack.append(Wait(partDelay)) + cloudPropTrack.append(Parallel(ParticleInterval(snowEffect, cloud, worldRelative=0, duration=2.2, cleanup=True), Sequence(Wait(0.5), ParticleInterval(snowEffect2, cloud, worldRelative=0, duration=1.7, cleanup=True)), Sequence(Wait(1.0), ParticleInterval(snowEffect3, cloud, worldRelative=0, duration=1.2, cleanup=True)), Sequence(ActorInterval(cloud, 'stormcloud', startTime=3, duration=0.5), ActorInterval(cloud, 'stormcloud', startTime=2.5, duration=0.5), ActorInterval(cloud, 'stormcloud', startTime=1, duration=1.5)))) + cloudPropTrack.append(Wait(0.4)) + cloudPropTrack.append(LerpScaleInterval(cloud, 0.5, MovieUtil.PNT3_NEARZERO)) + cloudPropTrack.append(Func(MovieUtil.removeProp, cloud)) + cloudPropTrack.append(Func(battle.movie.clearRenderProp, cloud)) + damageAnims = [['cringe', + 0.01, + 0.4, + 0.8], ['duck', 1e-06, 1.6]] + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'], showMissedExtraTime=1.1) + soundTrack = getSoundTrack('SA_brainstorm.ogg', delay=2.6, node=suit) + return Parallel(suitTrack, toonTrack, cloudPropTrack, soundTrack) + + +def doBuzzWord(attack): + suit = attack['suit'] + target = attack['target'] + toon = target['toon'] + battle = attack['battle'] + BattleParticles.loadParticles() + particleEffects = [] + texturesList = ['buzzwords-crash', + 'buzzwords-inc', + 'buzzwords-main', + 'buzzwords-over', + 'buzzwords-syn'] + for i in xrange(0, 5): + effect = BattleParticles.createParticleEffect('BuzzWord') + if random.random() > 0.5: + BattleParticles.setEffectTexture(effect, texturesList[i], color=Vec4(1, 0.94, 0.02, 1)) + else: + BattleParticles.setEffectTexture(effect, texturesList[i], color=Vec4(0, 0, 0, 1)) + particleEffects.append(effect) + + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'a': + partDelay = 4.0 + partDuration = 2.2 + damageDelay = 4.5 + dodgeDelay = 3.8 + elif suitType == 'b': + partDelay = 1.3 + partDuration = 2 + damageDelay = 2.5 + dodgeDelay = 1.8 + elif suitType == 'c': + partDelay = 4.0 + partDuration = 2.2 + damageDelay = 4.5 + dodgeDelay = 3.8 + suitName = suit.getStyleName() + if suitName == 'm': + for effect in particleEffects: + effect.setPos(0, 2.8, suit.getHeight() - 2.5) + effect.setHpr(0, -20, 0) + + elif suitName == 'mm': + for effect in particleEffects: + effect.setPos(0, 2.1, suit.getHeight() - 0.8) + + suitTrack = getSuitTrack(attack) + particleTracks = [] + for effect in particleEffects: + particleTracks.append(getPartTrack(effect, partDelay, partDuration, [effect, suit, 0])) + + toonTrack = getToonTrack(attack, damageDelay=damageDelay, damageAnimNames=['cringe'], splicedDodgeAnims=[['duck', dodgeDelay, 1.4]], showMissedExtraTime=dodgeDelay + 0.5) + soundTrack = getSoundTrack('SA_buzz_word.ogg', delay=3.9, node=suit) + return Parallel(suitTrack, toonTrack, soundTrack, *particleTracks) + + +def doDemotion(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + BattleParticles.loadParticles() + sprayEffect = BattleParticles.createParticleEffect('DemotionSpray') + freezeEffect = BattleParticles.createParticleEffect('DemotionFreeze') + unFreezeEffect = BattleParticles.createParticleEffect(file='demotionUnFreeze') + BattleParticles.setEffectTexture(sprayEffect, 'snow-particle') + BattleParticles.setEffectTexture(freezeEffect, 'snow-particle') + BattleParticles.setEffectTexture(unFreezeEffect, 'snow-particle') + facePoint = __toonFacePoint(toon) + freezeEffect.setPos(0, 0, facePoint.getZ()) + unFreezeEffect.setPos(0, 0, facePoint.getZ()) + suitTrack = getSuitTrack(attack) + partTrack = getPartTrack(sprayEffect, 0.7, 1.1, [sprayEffect, suit, 0]) + partTrack2 = getPartTrack(freezeEffect, 1.4, 2.9, [freezeEffect, toon, 0]) + partTrack3 = getPartTrack(unFreezeEffect, 6.65, 0.5, [unFreezeEffect, toon, 0]) + dodgeAnims = [['duck', 1e-06, 0.8]] + damageAnims = [] + damageAnims.append(['cringe', + 0.01, + 0, + 0.5]) + damageAnims.extend(getSplicedLerpAnims('cringe', 0.4, 0.5, startTime=0.5)) + damageAnims.extend(getSplicedLerpAnims('cringe', 0.3, 0.5, startTime=0.9)) + damageAnims.extend(getSplicedLerpAnims('cringe', 0.3, 0.6, startTime=1.2)) + damageAnims.append(['cringe', 2.6, 1.5]) + toonTrack = getToonTrack(attack, damageDelay=1.0, splicedDamageAnims=damageAnims, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=1.6, showDamageExtraTime=1.3) + soundTrack = getSoundTrack('SA_demotion.ogg', delay=1.2, node=suit) + if dmg > 0: + return Parallel(suitTrack, toonTrack, soundTrack, partTrack, partTrack2, partTrack3) + else: + return Parallel(suitTrack, toonTrack, soundTrack, partTrack) + + +def doCanned(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + dmg = target['hp'] + toon = target['toon'] + hips = toon.getHipsParts() + propDelay = 0.8 + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'c': + suitDelay = 1.13 + dodgeDelay = 3.1 + else: + suitDelay = 1.83 + dodgeDelay = 3.6 + throwDuration = 1.5 + can = globalPropPool.getProp('can') + scale = 26 + torso = toon.style.torso + torso = torso[0] + if torso == 's': + scaleUpPoint = Point3(scale * 2.63, scale * 2.63, scale * 1.9975) + elif torso == 'm': + scaleUpPoint = Point3(scale * 2.63, scale * 2.63, scale * 1.7975) + elif torso == 'l': + scaleUpPoint = Point3(scale * 2.63, scale * 2.63, scale * 2.31) + canHpr = VBase3(-173.47, -0.42, 162.09) + suitTrack = getSuitTrack(attack) + posPoints = [Point3(-0.14, 0.15, 0.08), VBase3(-10.584, 11.945, -161.684)] + throwTrack = Sequence(getPropAppearTrack(can, suit.getRightHand(), posPoints, propDelay, Point3(6, 6, 6), scaleUpTime=0.5)) + propDelay = propDelay + 0.5 + throwTrack.append(Wait(suitDelay)) + hitPoint = toon.getPos(battle) + hitPoint.setX(hitPoint.getX() + 1.1) + hitPoint.setY(hitPoint.getY() - 0.5) + hitPoint.setZ(hitPoint.getZ() + toon.height + 1.1) + throwTrack.append(Func(battle.movie.needRestoreRenderProp, can)) + throwTrack.append(getThrowTrack(can, hitPoint, duration=throwDuration, parent=battle)) + if dmg > 0: + can2 = MovieUtil.copyProp(can) + hips1 = hips.getPath(2) + hips2 = hips.getPath(1) + can2Point = Point3(hitPoint.getX(), hitPoint.getY() + 6.4, hitPoint.getZ()) + can2.setPos(can2Point) + can2.setScale(scaleUpPoint) + can2.setHpr(canHpr) + throwTrack.append(Func(battle.movie.needRestoreHips)) + throwTrack.append(Func(can.wrtReparentTo, hips1)) + throwTrack.append(Func(can2.reparentTo, hips2)) + throwTrack.append(Wait(2.4)) + throwTrack.append(Func(MovieUtil.removeProp, can2)) + throwTrack.append(Func(battle.movie.clearRestoreHips)) + scaleTrack = Sequence(Wait(propDelay + suitDelay), LerpScaleInterval(can, throwDuration, scaleUpPoint)) + hprTrack = Sequence(Wait(propDelay + suitDelay), LerpHprInterval(can, throwDuration, canHpr)) + soundTrack = Sequence(Wait(2.6), SoundInterval(globalBattleSoundCache.getSound('SA_canned_tossup_only.ogg'), node=suit), SoundInterval(globalBattleSoundCache.getSound('SA_canned_impact_only.ogg'), node=suit)) + else: + land = toon.getPos(battle) + land.setZ(land.getZ() + 0.7) + bouncePoint1 = Point3(land.getX(), land.getY() - 1.5, land.getZ() + 2.5) + bouncePoint2 = Point3(land.getX(), land.getY() - 2.1, land.getZ() - 0.2) + bouncePoint3 = Point3(land.getX(), land.getY() - 3.1, land.getZ() + 1.5) + bouncePoint4 = Point3(land.getX(), land.getY() - 4.1, land.getZ() + 0.3) + throwTrack.append(LerpPosInterval(can, 0.4, land)) + throwTrack.append(LerpPosInterval(can, 0.4, bouncePoint1)) + throwTrack.append(LerpPosInterval(can, 0.3, bouncePoint2)) + throwTrack.append(LerpPosInterval(can, 0.3, bouncePoint3)) + throwTrack.append(LerpPosInterval(can, 0.3, bouncePoint4)) + throwTrack.append(Wait(1.1)) + throwTrack.append(LerpScaleInterval(can, 0.3, MovieUtil.PNT3_NEARZERO)) + scaleTrack = Sequence(Wait(propDelay + suitDelay), LerpScaleInterval(can, throwDuration, Point3(11, 11, 11))) + hprTrack = Sequence(Wait(propDelay + suitDelay), LerpHprInterval(can, throwDuration, canHpr), Wait(0.4), LerpHprInterval(can, 0.4, Point3(83.27, 19.52, -177.92)), LerpHprInterval(can, 0.3, Point3(95.24, -72.09, 88.65)), LerpHprInterval(can, 0.2, Point3(-96.34, -2.63, 179.89))) + soundTrack = getSoundTrack('SA_canned_tossup_only.ogg', delay=2.6, node=suit) + canTrack = Sequence(Parallel(throwTrack, scaleTrack, hprTrack), Func(MovieUtil.removeProp, can), Func(battle.movie.clearRenderProp, can)) + damageAnims = [['struggle', + propDelay + suitDelay + throwDuration, + 0.01, + 0.7], ['slip-backward', 0.01, 0.45]] + toonTrack = getToonTrack(attack, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'], showDamageExtraTime=propDelay + suitDelay + 2.4) + return Parallel(suitTrack, toonTrack, canTrack, soundTrack) + + +def doDownsize(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + damageDelay = 2.3 + sprayEffect = BattleParticles.createParticleEffect(file='downsizeSpray') + cloudEffect = BattleParticles.createParticleEffect(file='downsizeCloud') + toonPos = toon.getPos(toon) + cloudPos = Point3(toonPos.getX(), toonPos.getY(), toonPos.getZ() + toon.getHeight() * 0.55) + cloudEffect.setPos(cloudPos) + suitTrack = getSuitTrack(attack) + sprayTrack = getPartTrack(sprayEffect, 1.0, 1.28, [sprayEffect, suit, 0]) + cloudTrack = getPartTrack(cloudEffect, 2.1, 1.9, [cloudEffect, toon, 0]) + if dmg > 0: + initialScale = toon.getScale() + downScale = Vec3(0.4, 0.4, 0.4) + shrinkTrack = Sequence(Wait(damageDelay + 0.5), Func(battle.movie.needRestoreToonScale), LerpScaleInterval(toon, 1.0, downScale * 1.1), LerpScaleInterval(toon, 0.1, downScale * 0.9), LerpScaleInterval(toon, 0.1, downScale * 1.05), LerpScaleInterval(toon, 0.1, downScale * 0.95), LerpScaleInterval(toon, 0.1, downScale), Wait(2.1), LerpScaleInterval(toon, 0.5, initialScale * 1.5), LerpScaleInterval(toon, 0.15, initialScale * 0.5), LerpScaleInterval(toon, 0.15, initialScale * 1.2), LerpScaleInterval(toon, 0.15, initialScale * 0.8), LerpScaleInterval(toon, 0.15, initialScale), Func(battle.movie.clearRestoreToonScale)) + damageAnims = [] + damageAnims.append(['juggle', + 0.01, + 0.87, + 0.5]) + damageAnims.append(['lose', + 0.01, + 2.17, + 0.93]) + damageAnims.append(['lose', + 0.01, + 3.1, + -0.93]) + damageAnims.append(['struggle', + 0.01, + 0.8, + 1.8]) + damageAnims.append(['sidestep-right', + 0.01, + 2.97, + 1.49]) + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=0.6, dodgeAnimNames=['sidestep']) + if dmg > 0: + return Parallel(suitTrack, sprayTrack, cloudTrack, shrinkTrack, toonTrack) + else: + return Parallel(suitTrack, sprayTrack, toonTrack) + + +def doPinkSlip(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + paper = globalPropPool.getProp('pink-slip') + throwDelay = 3.03 + throwDuration = 0.5 + suitTrack = getSuitTrack(attack) + posPoints = [Point3(0.07, -0.06, -0.18), VBase3(-172.075, -26.715, -89.131)] + paperAppearTrack = Sequence(getPropAppearTrack(paper, suit.getRightHand(), posPoints, 0.8, Point3(8, 8, 8), scaleUpTime=0.5)) + paperAppearTrack.append(Wait(1.73)) + hitPoint = __toonGroundPoint(attack, toon, 0.2, parent=battle) + paperAppearTrack.append(Func(battle.movie.needRestoreRenderProp, paper)) + paperAppearTrack.append(Func(paper.wrtReparentTo, battle)) + paperAppearTrack.append(LerpPosInterval(paper, throwDuration, hitPoint)) + if dmg > 0: + paperPause = 0.01 + slidePoint = Point3(hitPoint.getX(), hitPoint.getY() - 5, hitPoint.getZ() + 4) + landPoint = Point3(hitPoint.getX(), hitPoint.getY() - 5, hitPoint.getZ()) + paperAppearTrack.append(Wait(paperPause)) + paperAppearTrack.append(LerpPosInterval(paper, 0.2, slidePoint)) + paperAppearTrack.append(LerpPosInterval(paper, 1.1, landPoint)) + paperSpinTrack = Sequence(Wait(throwDelay), LerpHprInterval(paper, throwDuration, VBase3(300, 0, 0)), Wait(paperPause), LerpHprInterval(paper, 1.3, VBase3(-200, 100, 100))) + else: + slidePoint = Point3(hitPoint.getX(), hitPoint.getY() - 5, hitPoint.getZ()) + paperAppearTrack.append(LerpPosInterval(paper, 0.5, slidePoint)) + paperSpinTrack = Sequence(Wait(throwDelay), LerpHprInterval(paper, throwDuration, VBase3(300, 0, 0)), LerpHprInterval(paper, 0.5, VBase3(10, 0, 0))) + propTrack = Sequence() + propTrack.append(Parallel(paperAppearTrack, paperSpinTrack)) + propTrack.append(LerpScaleInterval(paper, 0.4, MovieUtil.PNT3_NEARZERO)) + propTrack.append(Func(MovieUtil.removeProp, paper)) + propTrack.append(Func(battle.movie.clearRenderProp, paper)) + damageAnims = [['jump', + 0.01, + 0.3, + 0.7], ['slip-forward', 0.01]] + toonTrack = getToonTrack(attack, damageDelay=2.81, splicedDamageAnims=damageAnims, dodgeDelay=2.8, dodgeAnimNames=['jump'], showDamageExtraTime=0.9) + soundTrack = getSoundTrack('SA_pink_slip.ogg', delay=2.9, duration=1.1, node=suit) + return Parallel(suitTrack, toonTrack, propTrack, soundTrack) + + +def doReOrg(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + damageDelay = 1.7 + attackDelay = 1.7 + sprayEffect = BattleParticles.createParticleEffect(file='reorgSpray') + suitTrack = getSuitTrack(attack) + partTrack = getPartTrack(sprayEffect, 1.0, 1.9, [sprayEffect, suit, 0]) + if dmg > 0: + headParts = toon.getHeadParts() + headTracks = Parallel() + for partNum in xrange(0, headParts.getNumPaths()): + part = headParts.getPath(partNum) + x = part.getX() + y = part.getY() + z = part.getZ() + h = part.getH() + p = part.getP() + r = part.getR() + headTracks.append(Sequence(Wait(attackDelay), LerpPosInterval(part, 0.1, Point3(x - 0.2, y, z - 0.03)), LerpPosInterval(part, 0.1, Point3(x + 0.4, y, z - 0.03)), LerpPosInterval(part, 0.1, Point3(x - 0.4, y, z - 0.03)), LerpPosInterval(part, 0.1, Point3(x + 0.4, y, z - 0.03)), LerpPosInterval(part, 0.1, Point3(x - 0.2, y, z - 0.04)), LerpPosInterval(part, 0.25, Point3(x, y, z + 2.2)), LerpHprInterval(part, 0.4, VBase3(360, 0, 180)), LerpPosInterval(part, 0.3, Point3(x, y, z + 3.1)), LerpPosInterval(part, 0.15, Point3(x, y, z + 0.3)), Wait(0.15), LerpHprInterval(part, 0.6, VBase3(-745, 0, 180), startHpr=VBase3(0, 0, 180)), LerpHprInterval(part, 0.8, VBase3(25, 0, 180), startHpr=VBase3(0, 0, 180)), LerpPosInterval(part, 0.15, Point3(x, y, z + 1)), LerpHprInterval(part, 0.3, VBase3(h, p, r)), Wait(0.2), LerpPosInterval(part, 0.1, Point3(x, y, z)), Wait(0.9))) + + def getChestTrack(part, attackDelay = attackDelay): + origScale = part.getScale() + return Sequence(Wait(attackDelay), LerpHprInterval(part, 1.1, VBase3(180, 0, 0)), Wait(1.1), LerpHprInterval(part, 1.1, part.getHpr())) + + chestTracks = Parallel() + arms = toon.findAllMatches('**/arms') + sleeves = toon.findAllMatches('**/sleeves') + hands = toon.findAllMatches('**/hands') + for partNum in xrange(0, arms.getNumPaths()): + chestTracks.append(getChestTrack(arms.getPath(partNum))) + chestTracks.append(getChestTrack(sleeves.getPath(partNum))) + chestTracks.append(getChestTrack(hands.getPath(partNum))) + + damageAnims = [['neutral', + 0.01, + 0.01, + 0.5], ['juggle', + 0.01, + 0.01, + 1.48], ['think', 0.01, 2.28]] + dodgeAnims = [] + dodgeAnims.append(['think', + 0.01, + 0, + 0.6]) + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=0.01, dodgeAnimNames=['duck'], showDamageExtraTime=2.1, showMissedExtraTime=2.0) + if dmg > 0: + return Parallel(suitTrack, partTrack, toonTrack, headTracks, chestTracks) + else: + return Parallel(suitTrack, partTrack, toonTrack) + + +def doSacked(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + dmg = target['hp'] + toon = target['toon'] + hips = toon.getHipsParts() + propDelay = 0.85 + suitDelay = 1.93 + throwDuration = 0.9 + sack = globalPropPool.getProp('sandbag') + initialScale = Point3(0.65, 1.47, 1.28) + scaleUpPoint = Point3(1.05, 1.67, 0.98) * 4.1 + sackHpr = VBase3(-154.33, -6.33, 163.8) + suitTrack = getSuitTrack(attack) + posPoints = [Point3(0.51, -2.03, -0.73), VBase3(90.0, -24.98, 77.73)] + sackAppearTrack = Sequence(getPropAppearTrack(sack, suit.getRightHand(), posPoints, propDelay, initialScale, scaleUpTime=0.2)) + propDelay = propDelay + 0.2 + sackAppearTrack.append(Wait(suitDelay)) + hitPoint = toon.getPos(battle) + if dmg > 0: + hitPoint.setX(hitPoint.getX() + 2.1) + hitPoint.setY(hitPoint.getY() + 0.9) + hitPoint.setZ(hitPoint.getZ() + toon.height + 1.2) + else: + hitPoint.setZ(hitPoint.getZ() - 0.2) + sackAppearTrack.append(Func(battle.movie.needRestoreRenderProp, sack)) + sackAppearTrack.append(getThrowTrack(sack, hitPoint, duration=throwDuration, parent=battle)) + if dmg > 0: + sack2 = MovieUtil.copyProp(sack) + hips1 = hips.getPath(2) + hips2 = hips.getPath(1) + sack2.hide() + sack2.reparentTo(battle) + sack2.setPos(Point3(hitPoint.getX(), hitPoint.getY(), hitPoint.getZ())) + sack2.setScale(scaleUpPoint) + sack2.setHpr(sackHpr) + sackAppearTrack.append(Func(battle.movie.needRestoreHips)) + sackAppearTrack.append(Func(sack.wrtReparentTo, hips1)) + sackAppearTrack.append(Func(sack2.show)) + sackAppearTrack.append(Func(sack2.wrtReparentTo, hips2)) + sackAppearTrack.append(Wait(2.4)) + sackAppearTrack.append(Func(MovieUtil.removeProp, sack2)) + sackAppearTrack.append(Func(battle.movie.clearRestoreHips)) + scaleTrack = Sequence(Wait(propDelay + suitDelay), LerpScaleInterval(sack, throwDuration, scaleUpPoint), Wait(1.8), LerpScaleInterval(sack, 0.3, MovieUtil.PNT3_NEARZERO)) + hprTrack = Sequence(Wait(propDelay + suitDelay), LerpHprInterval(sack, throwDuration, sackHpr)) + sackTrack = Sequence(Parallel(sackAppearTrack, scaleTrack, hprTrack), Func(MovieUtil.removeProp, sack), Func(battle.movie.clearRenderProp, sack)) + else: + sackAppearTrack.append(Wait(1.1)) + sackAppearTrack.append(LerpScaleInterval(sack, 0.3, MovieUtil.PNT3_NEARZERO)) + sackTrack = Sequence(sackAppearTrack, Func(MovieUtil.removeProp, sack), Func(battle.movie.clearRenderProp, sack)) + damageAnims = [['struggle', + 0.01, + 0.01, + 0.7], ['slip-backward', 0.01, 0.45]] + toonTrack = getToonTrack(attack, damageDelay=propDelay + suitDelay + throwDuration, splicedDamageAnims=damageAnims, dodgeDelay=3.0, dodgeAnimNames=['sidestep'], showDamageExtraTime=1.8, showMissedExtraTime=0.8) + return Parallel(suitTrack, toonTrack, sackTrack) + + +def doGlowerPower(attack): + suit = attack['suit'] + battle = attack['battle'] + leftKnives = [] + rightKnives = [] + for i in xrange(0, 3): + leftKnives.append(globalPropPool.getProp('dagger')) + rightKnives.append(globalPropPool.getProp('dagger')) + + suitTrack = getSuitTrack(attack) + suitName = suit.getStyleName() + if suitName == 'hh': + leftPosPoints = [Point3(0.3, 4.3, 5.3), MovieUtil.PNT3_ZERO] + rightPosPoints = [Point3(-0.3, 4.3, 5.3), MovieUtil.PNT3_ZERO] + elif suitName == 'tbc': + leftPosPoints = [Point3(0.6, 4.5, 6), MovieUtil.PNT3_ZERO] + rightPosPoints = [Point3(-0.6, 4.5, 6), MovieUtil.PNT3_ZERO] + else: + leftPosPoints = [Point3(0.4, 3.8, 3.7), MovieUtil.PNT3_ZERO] + rightPosPoints = [Point3(-0.4, 3.8, 3.7), MovieUtil.PNT3_ZERO] + leftKnifeTracks = Parallel() + rightKnifeTracks = Parallel() + for i in xrange(0, 3): + knifeDelay = 0.11 + leftTrack = Sequence() + leftTrack.append(Wait(1.1)) + leftTrack.append(Wait(i * knifeDelay)) + leftTrack.append(getPropAppearTrack(leftKnives[i], suit, leftPosPoints, 1e-06, Point3(0.4, 0.4, 0.4), scaleUpTime=0.1)) + leftTrack.append(getPropThrowTrack(attack, leftKnives[i], hitPointNames=['face'], missPointNames=['miss'], hitDuration=0.3, missDuration=0.3)) + leftKnifeTracks.append(leftTrack) + rightTrack = Sequence() + rightTrack.append(Wait(1.1)) + rightTrack.append(Wait(i * knifeDelay)) + rightTrack.append(getPropAppearTrack(rightKnives[i], suit, rightPosPoints, 1e-06, Point3(0.4, 0.4, 0.4), scaleUpTime=0.1)) + rightTrack.append(getPropThrowTrack(attack, rightKnives[i], hitPointNames=['face'], missPointNames=['miss'], hitDuration=0.3, missDuration=0.3)) + rightKnifeTracks.append(rightTrack) + + damageAnims = [['slip-backward', 0.01, 0.35]] + toonTrack = getToonTrack(attack, damageDelay=1.6, splicedDamageAnims=damageAnims, dodgeDelay=0.7, dodgeAnimNames=['sidestep']) + soundTrack = getSoundTrack('SA_glower_power.ogg', delay=1.1, node=suit) + return Parallel(suitTrack, toonTrack, soundTrack, leftKnifeTracks, rightKnifeTracks) + + +def doHalfWindsor(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + tie = globalPropPool.getProp('half-windsor') + throwDelay = 2.17 + damageDelay = 3.4 + dodgeDelay = 2.4 + suitTrack = getSuitTrack(attack) + posPoints = [Point3(0.02, 0.88, 0.48), VBase3(99, -3, -108.2)] + tiePropTrack = getPropAppearTrack(tie, suit.getRightHand(), posPoints, 0.5, Point3(7, 7, 7), scaleUpTime=0.5) + tiePropTrack.append(Wait(throwDelay)) + missPoint = __toonMissBehindPoint(toon, parent=battle) + missPoint.setX(missPoint.getX() - 1.1) + missPoint.setZ(missPoint.getZ() + 4) + hitPoint = __toonFacePoint(toon, parent=battle) + hitPoint.setX(hitPoint.getX() - 1.1) + hitPoint.setY(hitPoint.getY() - 0.7) + hitPoint.setZ(hitPoint.getZ() + 0.9) + tiePropTrack.append(getPropThrowTrack(attack, tie, [hitPoint], [missPoint], hitDuration=0.4, missDuration=0.8, missScaleDown=0.3, parent=battle)) + damageAnims = [['conked', + 0.01, + 0.01, + 0.4], ['cringe', 0.01, 0.7]] + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep']) + return Parallel(suitTrack, toonTrack, tiePropTrack) + + +def doHeadShrink(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + damageDelay = 2.1 + dodgeDelay = 1.4 + shrinkSpray = BattleParticles.createParticleEffect(file='headShrinkSpray') + shrinkCloud = BattleParticles.createParticleEffect(file='headShrinkCloud') + shrinkDrop = BattleParticles.createParticleEffect(file='headShrinkDrop') + suitTrack = getSuitTrack(attack) + sprayTrack = getPartTrack(shrinkSpray, 0.3, 1.4, [shrinkSpray, suit, 0]) + shrinkCloud.reparentTo(battle) + adjust = 0.4 + x = toon.getX(battle) + y = toon.getY(battle) - adjust + z = 8 + shrinkCloud.setPos(Point3(x, y, z)) + shrinkDrop.setPos(Point3(0, 0 - adjust, 7.5)) + off = 0.7 + cloudPoints = [Point3(x + off, y, z), + Point3(x + off / 2, y + off / 2, z), + Point3(x, y + off, z), + Point3(x - off / 2, y + off / 2, z), + Point3(x - off, y, z), + Point3(x - off / 2, y - off / 2, z), + Point3(x, y - off, z), + Point3(x + off / 2, y - off / 2, z), + Point3(x + off, y, z), + Point3(x, y, z)] + circleTrack = Sequence() + for point in cloudPoints: + circleTrack.append(LerpPosInterval(shrinkCloud, 0.14, point, other=battle)) + + cloudTrack = Sequence() + cloudTrack.append(Wait(1.42)) + cloudTrack.append(Func(battle.movie.needRestoreParticleEffect, shrinkCloud)) + cloudTrack.append(Func(shrinkCloud.start, battle)) + cloudTrack.append(circleTrack) + cloudTrack.append(circleTrack) + cloudTrack.append(LerpFunctionInterval(shrinkCloud.setAlphaScale, fromData=1, toData=0, duration=0.7)) + cloudTrack.append(Func(shrinkCloud.cleanup)) + cloudTrack.append(Func(battle.movie.clearRestoreParticleEffect, shrinkCloud)) + shrinkDelay = 0.8 + shrinkDuration = 1.1 + shrinkTrack = Sequence() + if dmg > 0: + headParts = toon.getHeadParts() + initialScale = headParts.getPath(0).getScale()[0] + shrinkTrack.append(Wait(damageDelay + shrinkDelay)) + + def scaleHeadParallel(scale, duration, headParts = headParts): + headTracks = Parallel() + for partNum in xrange(0, headParts.getNumPaths()): + nextPart = headParts.getPath(partNum) + headTracks.append(LerpScaleInterval(nextPart, duration, Point3(scale, scale, scale))) + + return headTracks + + shrinkTrack.append(Func(battle.movie.needRestoreHeadScale)) + shrinkTrack.append(scaleHeadParallel(0.6, shrinkDuration)) + shrinkTrack.append(Wait(1.6)) + shrinkTrack.append(scaleHeadParallel(initialScale * 3.2, 0.4)) + shrinkTrack.append(scaleHeadParallel(initialScale * 0.7, 0.4)) + shrinkTrack.append(scaleHeadParallel(initialScale * 2.5, 0.3)) + shrinkTrack.append(scaleHeadParallel(initialScale * 0.8, 0.3)) + shrinkTrack.append(scaleHeadParallel(initialScale * 1.9, 0.2)) + shrinkTrack.append(scaleHeadParallel(initialScale * 0.85, 0.2)) + shrinkTrack.append(scaleHeadParallel(initialScale * 1.7, 0.15)) + shrinkTrack.append(scaleHeadParallel(initialScale * 0.9, 0.15)) + shrinkTrack.append(scaleHeadParallel(initialScale * 1.3, 0.1)) + shrinkTrack.append(scaleHeadParallel(initialScale, 0.1)) + shrinkTrack.append(Func(battle.movie.clearRestoreHeadScale)) + shrinkTrack.append(Wait(0.7)) + dropTrack = getPartTrack(shrinkDrop, 1.5, 2.5, [shrinkDrop, toon, 0]) + damageAnims = [] + damageAnims.append(['cringe', + 0.01, + 0.65, + 0.2]) + damageAnims.extend(getSplicedLerpAnims('cringe', 0.64, 1.0, startTime=0.85)) + damageAnims.append(['cringe', 0.4, 1.49]) + damageAnims.append(['conked', + 0.01, + 3.6, + -1.6]) + damageAnims.append(['conked', + 0.01, + 3.1, + 0.4]) + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep']) + if dmg > 0: + shrinkSound = globalBattleSoundCache.getSound('SA_head_shrink_only.ogg') + growSound = globalBattleSoundCache.getSound('SA_head_grow_back_only.ogg') + soundTrack = Sequence(Wait(2.1), SoundInterval(shrinkSound, duration=2.1, node=suit), Wait(1.6), SoundInterval(growSound, node=suit)) + return Parallel(suitTrack, sprayTrack, cloudTrack, dropTrack, toonTrack, shrinkTrack, soundTrack) + else: + return Parallel(suitTrack, sprayTrack, cloudTrack, dropTrack, toonTrack) + + +def doRolodex(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + rollodex = globalPropPool.getProp('rollodex') + particleEffect2 = BattleParticles.createParticleEffect(file='rollodexWaterfall') + particleEffect3 = BattleParticles.createParticleEffect(file='rollodexStream') + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'a': + propPosPoints = [Point3(-0.51, -0.03, -0.1), VBase3(89.673, 2.166, 177.786)] + propScale = Point3(1.2, 1.2, 1.2) + partDelay = 2.6 + part2Delay = 2.8 + part3Delay = 3.2 + partDuration = 1.6 + part2Duration = 1.9 + part3Duration = 1 + damageDelay = 3.8 + dodgeDelay = 2.5 + elif suitType == 'b': + propPosPoints = [Point3(0.12, 0.24, 0.01), VBase3(99.032, 5.973, -179.839)] + propScale = Point3(0.91, 0.91, 0.91) + partDelay = 2.9 + part2Delay = 3.1 + part3Delay = 3.5 + partDuration = 1.6 + part2Duration = 1.9 + part3Duration = 1 + damageDelay = 4 + dodgeDelay = 2.5 + elif suitType == 'c': + propPosPoints = [Point3(-0.51, -0.03, -0.1), VBase3(89.673, 2.166, 177.786)] + propScale = Point3(1.2, 1.2, 1.2) + partDelay = 2.3 + part2Delay = 2.8 + part3Delay = 3.2 + partDuration = 1.9 + part2Duration = 1.9 + part3Duration = 1 + damageDelay = 3.5 + dodgeDelay = 2.5 + hitPoint = lambda toon = toon: __toonFacePoint(toon) + partTrack2 = getPartTrack(particleEffect2, part2Delay, part2Duration, [particleEffect2, suit, 0]) + partTrack3 = getPartTrack(particleEffect3, part3Delay, part3Duration, [particleEffect3, suit, 0]) + suitTrack = getSuitTrack(attack) + propTrack = getPropTrack(rollodex, suit.getLeftHand(), propPosPoints, 1e-06, 4.7, scaleUpPoint=propScale, anim=0, propName='rollodex', animDuration=0, animStartTime=0) + toonTrack = getToonTrack(attack, damageDelay, ['conked'], dodgeDelay, ['sidestep']) + soundTrack = getSoundTrack('SA_rolodex.ogg', delay=2.8, node=suit) + return Parallel(suitTrack, toonTrack, propTrack, soundTrack, partTrack2, partTrack3) + + +def doEvilEye(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + eye = globalPropPool.getProp('evil-eye') + damageDelay = 2.44 + dodgeDelay = 1.64 + suitName = suit.getStyleName() + if suitName == 'cr': + posPoints = [Point3(-0.46, 4.85, 5.28), VBase3(-155.0, -20.0, 0.0)] + elif suitName == 'tf': + posPoints = [Point3(-0.4, 3.65, 5.01), VBase3(-155.0, -20.0, 0.0)] + elif suitName == 'le': + posPoints = [Point3(-0.64, 4.45, 5.91), VBase3(-155.0, -20.0, 0.0)] + else: + posPoints = [Point3(-0.4, 3.65, 5.01), VBase3(-155.0, -20.0, 0.0)] + appearDelay = 0.8 + suitHoldStart = 1.06 + suitHoldStop = 1.69 + suitHoldDuration = suitHoldStop - suitHoldStart + eyeHoldDuration = 1.1 + moveDuration = 1.1 + suitSplicedAnims = [] + suitSplicedAnims.append(['glower', + 0.01, + 0.01, + suitHoldStart]) + suitSplicedAnims.extend(getSplicedLerpAnims('glower', suitHoldDuration, 1.1, startTime=suitHoldStart)) + suitSplicedAnims.append(['glower', 0.01, suitHoldStop]) + suitTrack = getSuitTrack(attack, splicedAnims=suitSplicedAnims) + eyeAppearTrack = Sequence(Wait(suitHoldStart), Func(__showProp, eye, suit, posPoints[0], posPoints[1]), LerpScaleInterval(eye, suitHoldDuration, Point3(11, 11, 11)), Wait(eyeHoldDuration * 0.3), LerpHprInterval(eye, 0.02, Point3(205, 40, 0)), Wait(eyeHoldDuration * 0.7), Func(battle.movie.needRestoreRenderProp, eye), Func(eye.wrtReparentTo, battle)) + toonFace = __toonFacePoint(toon, parent=battle) + if dmg > 0: + lerpInterval = LerpPosInterval(eye, moveDuration, toonFace) + else: + lerpInterval = LerpPosInterval(eye, moveDuration, Point3(toonFace.getX(), toonFace.getY() - 5, toonFace.getZ() - 2)) + eyeMoveTrack = lerpInterval + eyeRollTrack = LerpHprInterval(eye, moveDuration, Point3(0, 0, -180)) + eyePropTrack = Sequence(eyeAppearTrack, Parallel(eyeMoveTrack, eyeRollTrack), Func(battle.movie.clearRenderProp, eye), Func(MovieUtil.removeProp, eye)) + damageAnims = [['duck', + 0.01, + 0.01, + 1.4], ['cringe', 0.01, 0.3]] + toonTrack = getToonTrack(attack, splicedDamageAnims=damageAnims, damageDelay=damageDelay, dodgeDelay=dodgeDelay, dodgeAnimNames=['duck'], showDamageExtraTime=1.7, showMissedExtraTime=1.7) + soundTrack = getSoundTrack('SA_evil_eye.ogg', delay=1.3, node=suit) + return Parallel(suitTrack, toonTrack, eyePropTrack, soundTrack) + + +def doPlayHardball(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + ball = globalPropPool.getProp('baseball') + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'a': + suitDelay = 1.09 + damageDelay = 2.76 + dodgeDelay = 1.86 + elif suitType == 'b': + suitDelay = 1.79 + damageDelay = 3.46 + dodgeDelay = 2.56 + elif suitType == 'c': + suitDelay = 1.09 + damageDelay = 2.76 + dodgeDelay = 1.86 + suitTrack = getSuitTrack(attack) + ballPosPoints = [Point3(0.04, 0.03, -0.31), VBase3(-1.152, 86.581, -76.784)] + propTrack = Sequence(getPropAppearTrack(ball, suit.getRightHand(), ballPosPoints, 0.8, Point3(5, 5, 5), scaleUpTime=0.5)) + propTrack.append(Wait(suitDelay)) + propTrack.append(Func(battle.movie.needRestoreRenderProp, ball)) + propTrack.append(Func(ball.wrtReparentTo, battle)) + toonPos = toon.getPos(battle) + x = toonPos.getX() + y = toonPos.getY() + z = toonPos.getZ() + z = z + 0.2 + if dmg > 0: + propTrack.append(LerpPosInterval(ball, 0.5, __toonFacePoint(toon, parent=battle))) + propTrack.append(LerpPosInterval(ball, 0.5, Point3(x, y + 3, z))) + propTrack.append(LerpPosInterval(ball, 0.4, Point3(x, y + 5, z + 2))) + propTrack.append(LerpPosInterval(ball, 0.3, Point3(x, y + 6, z))) + propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y + 7, z + 1))) + propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y + 8, z))) + propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y + 8.5, z + 0.6))) + propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y + 9, z + 0.2))) + propTrack.append(Wait(0.4)) + soundTrack = getSoundTrack('SA_hardball_impact_only.ogg', delay=2.8, node=suit) + else: + propTrack.append(LerpPosInterval(ball, 0.5, Point3(x, y + 2, z))) + propTrack.append(LerpPosInterval(ball, 0.4, Point3(x, y - 1, z + 2))) + propTrack.append(LerpPosInterval(ball, 0.3, Point3(x, y - 3, z))) + propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y - 4, z + 1))) + propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y - 5, z))) + propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y - 5.5, z + 0.6))) + propTrack.append(LerpPosInterval(ball, 0.1, Point3(x, y - 6, z + 0.2))) + propTrack.append(Wait(0.4)) + soundTrack = getSoundTrack('SA_hardball.ogg', delay=3.1, node=suit) + propTrack.append(LerpScaleInterval(ball, 0.3, MovieUtil.PNT3_NEARZERO)) + propTrack.append(Func(MovieUtil.removeProp, ball)) + propTrack.append(Func(battle.movie.clearRenderProp, ball)) + damageAnims = [['conked', + damageDelay, + 0.01, + 0.5], ['slip-backward', 0.01, 0.7]] + toonTrack = getToonTrack(attack, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'], showDamageExtraTime=3.9) + return Parallel(suitTrack, toonTrack, propTrack, soundTrack) + + +def doPowerTie(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + tie = globalPropPool.getProp('power-tie') + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'a': + throwDelay = 2.17 + damageDelay = 3.3 + dodgeDelay = 3.1 + elif suitType == 'b': + throwDelay = 2.17 + damageDelay = 3.3 + dodgeDelay = 3.1 + elif suitType == 'c': + throwDelay = 1.45 + damageDelay = 2.61 + dodgeDelay = 2.34 + suitTrack = getSuitTrack(attack) + posPoints = [Point3(1.16, 0.24, 0.63), VBase3(171.561, 1.745, -163.443)] + tiePropTrack = Sequence(getPropAppearTrack(tie, suit.getRightHand(), posPoints, 0.5, Point3(3.5, 3.5, 3.5), scaleUpTime=0.5)) + tiePropTrack.append(Wait(throwDelay)) + tiePropTrack.append(Func(tie.setBillboardPointEye)) + tiePropTrack.append(getPropThrowTrack(attack, tie, [__toonFacePoint(toon)], [__toonGroundPoint(attack, toon, 0.1)], hitDuration=0.4, missDuration=0.8)) + toonTrack = getToonTrack(attack, damageDelay, ['conked'], dodgeDelay, ['sidestep']) + throwSound = getSoundTrack('SA_powertie_throw.ogg', delay=2.3, node=suit) + if dmg > 0: + hitSound = getSoundTrack('SA_powertie_impact.ogg', delay=2.9, node=suit) + return Parallel(suitTrack, toonTrack, tiePropTrack, throwSound, hitSound) + else: + return Parallel(suitTrack, toonTrack, tiePropTrack, throwSound) + + +def doDoubleTalk(attack): + suit = attack['suit'] + battle = attack['battle'] + BattleParticles.loadParticles() + particleEffect = BattleParticles.createParticleEffect('DoubleTalkLeft') + particleEffect2 = BattleParticles.createParticleEffect('DoubleTalkRight') + BattleParticles.setEffectTexture(particleEffect, 'doubletalk-double', color=Vec4(0, 1.0, 0.0, 1)) + BattleParticles.setEffectTexture(particleEffect2, 'doubletalk-good', color=Vec4(0, 1.0, 0.0, 1)) + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'a': + partDelay = 3.3 + damageDelay = 3.5 + dodgeDelay = 3.3 + elif suitType == 'b': + partDelay = 3.3 + damageDelay = 3.5 + dodgeDelay = 3.3 + elif suitType == 'c': + partDelay = 3.3 + damageDelay = 3.5 + dodgeDelay = 3.3 + suitTrack = getSuitTrack(attack) + partTrack = getPartTrack(particleEffect, partDelay, 1.8, [particleEffect, suit, 0]) + partTrack2 = getPartTrack(particleEffect2, partDelay, 1.8, [particleEffect2, suit, 0]) + damageAnims = [['duck', + 0.01, + 0.4, + 1.05], ['cringe', 1e-06, 0.8]] + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, splicedDodgeAnims=[['duck', 0.01, 1.4]], showMissedExtraTime=0.9, showDamageExtraTime=0.8) + soundTrack = getSoundTrack('SA_filibuster.ogg', delay=2.5, node=suit) + return Parallel(suitTrack, toonTrack, partTrack, partTrack2, soundTrack) + + +def doFreezeAssets(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + BattleParticles.loadParticles() + snowEffect = BattleParticles.createParticleEffect('FreezeAssets') + BattleParticles.setEffectTexture(snowEffect, 'snow-particle') + cloud = globalPropPool.getProp('stormcloud') + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'a': + partDelay = 0.2 + damageDelay = 3.5 + dodgeDelay = 2.3 + elif suitType == 'b': + partDelay = 0.2 + damageDelay = 3.5 + dodgeDelay = 2.3 + elif suitType == 'c': + partDelay = 0.2 + damageDelay = 3.5 + dodgeDelay = 2.3 + suitTrack = getSuitTrack(attack, delay=0.9) + initialCloudHeight = suit.height + 3 + cloudPosPoints = [Point3(0, 3, initialCloudHeight), MovieUtil.PNT3_ZERO] + cloudPropTrack = Sequence() + cloudPropTrack.append(Func(cloud.pose, 'stormcloud', 0)) + cloudPropTrack.append(getPropAppearTrack(cloud, suit, cloudPosPoints, 1e-06, Point3(3, 3, 3), scaleUpTime=0.7)) + cloudPropTrack.append(Func(battle.movie.needRestoreRenderProp, cloud)) + cloudPropTrack.append(Func(cloud.wrtReparentTo, render)) + targetPoint = __toonFacePoint(toon) + targetPoint.setZ(targetPoint[2] + 3) + cloudPropTrack.append(Wait(1.1)) + cloudPropTrack.append(LerpPosInterval(cloud, 1, pos=targetPoint)) + cloudPropTrack.append(Wait(partDelay)) + cloudPropTrack.append(ParticleInterval(snowEffect, cloud, worldRelative=0, duration=2.1, cleanup=True)) + cloudPropTrack.append(Wait(0.4)) + cloudPropTrack.append(LerpScaleInterval(cloud, 0.5, MovieUtil.PNT3_NEARZERO)) + cloudPropTrack.append(Func(MovieUtil.removeProp, cloud)) + cloudPropTrack.append(Func(battle.movie.clearRenderProp, cloud)) + damageAnims = [['cringe', + 0.01, + 0.4, + 0.8], ['duck', 0.01, 1.6]] + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'], showMissedExtraTime=1.2) + return Parallel(suitTrack, toonTrack, cloudPropTrack) + + +def doHotAir(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + BattleParticles.loadParticles() + sprayEffect = BattleParticles.createParticleEffect('HotAir') + baseFlameEffect = BattleParticles.createParticleEffect(file='firedBaseFlame') + flameEffect = BattleParticles.createParticleEffect('FiredFlame') + flecksEffect = BattleParticles.createParticleEffect('SpriteFiredFlecks') + BattleParticles.setEffectTexture(sprayEffect, 'fire') + BattleParticles.setEffectTexture(baseFlameEffect, 'fire') + BattleParticles.setEffectTexture(flameEffect, 'fire') + BattleParticles.setEffectTexture(flecksEffect, 'roll-o-dex', color=Vec4(0.95, 0.95, 0.0, 1)) + sprayDelay = 1.3 + flameDelay = 3.2 + flameDuration = 2.6 + flecksDelay = flameDelay + 0.8 + flecksDuration = flameDuration - 0.8 + damageDelay = 3.6 + dodgeDelay = 2.0 + suitTrack = getSuitTrack(attack) + sprayTrack = getPartTrack(sprayEffect, sprayDelay, 2.3, [sprayEffect, suit, 0]) + baseFlameTrack = getPartTrack(baseFlameEffect, flameDelay, flameDuration, [baseFlameEffect, toon, 0]) + flameTrack = getPartTrack(flameEffect, flameDelay, flameDuration, [flameEffect, toon, 0]) + flecksTrack = getPartTrack(flecksEffect, flecksDelay, flecksDuration, [flecksEffect, toon, 0]) + + def changeColor(parts): + track = Parallel() + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + track.append(Func(nextPart.setColorScale, Vec4(0, 0, 0, 1))) + + return track + + def resetColor(parts): + track = Parallel() + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + track.append(Func(nextPart.clearColorScale)) + + return track + + if dmg > 0: + headParts = toon.getHeadParts() + torsoParts = toon.getTorsoParts() + legsParts = toon.getLegsParts() + colorTrack = Sequence() + colorTrack.append(Wait(4.0)) + colorTrack.append(Func(battle.movie.needRestoreColor)) + colorTrack.append(changeColor(headParts)) + colorTrack.append(changeColor(torsoParts)) + colorTrack.append(changeColor(legsParts)) + colorTrack.append(Wait(3.5)) + colorTrack.append(resetColor(headParts)) + colorTrack.append(resetColor(torsoParts)) + colorTrack.append(resetColor(legsParts)) + colorTrack.append(Func(battle.movie.clearRestoreColor)) + damageAnims = [] + damageAnims.append(['cringe', + 0.01, + 0.7, + 0.62]) + damageAnims.append(['slip-forward', + 0.01, + 0.4, + 1.2]) + damageAnims.append(['slip-forward', 0.01, 1.0]) + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep']) + soundTrack = getSoundTrack('SA_hot_air.ogg', delay=1.6, node=suit) + if dmg > 0: + return Parallel(suitTrack, toonTrack, sprayTrack, soundTrack, baseFlameTrack, flameTrack, flecksTrack, colorTrack) + else: + return Parallel(suitTrack, toonTrack, sprayTrack, soundTrack) + + +def doPickPocket(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + dmg = target['hp'] + bill = globalPropPool.getProp('1dollar') + suitTrack = getSuitTrack(attack) + billPosPoints = [Point3(-0.01, 0.45, -0.25), VBase3(136.424, -46.434, -129.712)] + billPropTrack = getPropTrack(bill, suit.getRightHand(), billPosPoints, 0.6, 0.55, scaleUpPoint=Point3(1.41, 1.41, 1.41)) + toonTrack = getToonTrack(attack, 0.6, ['cringe'], 0.01, ['sidestep']) + multiTrackList = Parallel(suitTrack, toonTrack) + if dmg > 0: + soundTrack = getSoundTrack('SA_pick_pocket.ogg', delay=0.2, node=suit) + multiTrackList.append(billPropTrack) + multiTrackList.append(soundTrack) + return multiTrackList + + +def doCigarSmoke(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + BattleParticles.loadParticles() + smoke = BattleParticles.createParticleEffect('Smoke') + BattleParticles.setEffectTexture(smoke, 'snow-particle') + cigar = globalPropPool.getProp('cigar') + suitTrack = getSuitTrack(attack) + cigarPosPoints = [Point3(-0.05, -0.2, -0.25), VBase3(180.0, 0.0, 0.0)] + cigarPropTrack = getPropTrack(cigar, suit.getRightHand(), cigarPosPoints, 0.6, 3.6, scaleUpPoint=Point3(6.0, 6.0, 6.0)) + toonTrack = getToonTrack(attack, 3.55, ['cringe'], 3.0, ['sidestep']) + multiTrackList = Parallel(suitTrack, toonTrack) + smokeTrack = getPartTrack(smoke, 3.45, 1.5, [smoke, suit, 0]) + multiTrackList.append(cigarPropTrack) + multiTrackList.append(smokeTrack) + + def changeColor(parts): + track = Parallel() + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + track.append(Func(nextPart.setColorScale, Vec4(0, 0, 0, 1))) + + return track + + def resetColor(parts): + track = Parallel() + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + track.append(Func(nextPart.clearColorScale)) + + return track + + if dmg > 0: + headParts = toon.getHeadParts() + torsoParts = toon.getTorsoParts() + legsParts = toon.getLegsParts() + colorTrack = Sequence() + colorTrack.append(Wait(3.6)) + colorTrack.append(Func(battle.movie.needRestoreColor)) + colorTrack.append(changeColor(headParts)) + colorTrack.append(changeColor(torsoParts)) + colorTrack.append(changeColor(legsParts)) + colorTrack.append(Wait(2.2)) + colorTrack.append(resetColor(headParts)) + colorTrack.append(resetColor(torsoParts)) + colorTrack.append(resetColor(legsParts)) + colorTrack.append(Func(battle.movie.clearRestoreColor)) + multiTrackList.append(colorTrack) + return multiTrackList + + +def doFilibuster(attack): + suit = attack['suit'] + target = attack['target'] + dmg = target['hp'] + battle = attack['battle'] + BattleParticles.loadParticles() + sprayEffect = BattleParticles.createParticleEffect(file='filibusterSpray') + sprayEffect2 = BattleParticles.createParticleEffect(file='filibusterSpray') + sprayEffect3 = BattleParticles.createParticleEffect(file='filibusterSpray') + sprayEffect4 = BattleParticles.createParticleEffect(file='filibusterSpray') + color = Vec4(0.4, 0, 0, 1) + BattleParticles.setEffectTexture(sprayEffect, 'filibuster-cut', color=color) + BattleParticles.setEffectTexture(sprayEffect2, 'filibuster-fiscal', color=color) + BattleParticles.setEffectTexture(sprayEffect3, 'filibuster-impeach', color=color) + BattleParticles.setEffectTexture(sprayEffect4, 'filibuster-inc', color=color) + partDelay = 1.3 + partDuration = 1.15 + damageDelay = 2.45 + dodgeDelay = 1.7 + suitTrack = getSuitTrack(attack) + sprayTrack = getPartTrack(sprayEffect, partDelay, partDuration, [sprayEffect, suit, 0]) + sprayTrack2 = getPartTrack(sprayEffect2, partDelay + 0.8, partDuration, [sprayEffect2, suit, 0]) + sprayTrack3 = getPartTrack(sprayEffect3, partDelay + 1.6, partDuration, [sprayEffect3, suit, 0]) + sprayTrack4 = getPartTrack(sprayEffect4, partDelay + 2.4, partDuration, [sprayEffect4, suit, 0]) + damageAnims = [] + for i in xrange(0, 4): + damageAnims.append(['cringe', + 1e-05, + 0.3, + 0.8]) + + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep']) + soundTrack = getSoundTrack('SA_filibuster.ogg', delay=1.1, node=suit) + if dmg > 0: + return Parallel(suitTrack, toonTrack, soundTrack, sprayTrack, sprayTrack2, sprayTrack3, sprayTrack4) + else: + return Parallel(suitTrack, toonTrack, soundTrack, sprayTrack, sprayTrack2, sprayTrack3) + + +def doSchmooze(attack): + suit = attack['suit'] + battle = attack['battle'] + BattleParticles.loadParticles() + upperEffects = [] + lowerEffects = [] + textureNames = ['schmooze-genius', + 'schmooze-instant', + 'schmooze-master', + 'schmooze-viz'] + for i in xrange(0, 4): + upperEffect = BattleParticles.createParticleEffect(file='schmoozeUpperSpray') + lowerEffect = BattleParticles.createParticleEffect(file='schmoozeLowerSpray') + BattleParticles.setEffectTexture(upperEffect, textureNames[i], color=Vec4(0, 0, 1, 1)) + BattleParticles.setEffectTexture(lowerEffect, textureNames[i], color=Vec4(0, 0, 1, 1)) + upperEffects.append(upperEffect) + lowerEffects.append(lowerEffect) + + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'a': + partDelay = 1.3 + damageDelay = 1.8 + dodgeDelay = 1.1 + elif suitType == 'b': + partDelay = 1.3 + damageDelay = 2.5 + dodgeDelay = 1.8 + elif suitType == 'c': + partDelay = 1.3 + damageDelay = partDelay + 1.4 + dodgeDelay = 0.9 + suitTrack = getSuitTrack(attack) + upperPartTracks = Parallel() + lowerPartTracks = Parallel() + for i in xrange(0, 4): + upperPartTracks.append(getPartTrack(upperEffects[i], partDelay + i * 0.65, 0.8, [upperEffects[i], suit, 0])) + lowerPartTracks.append(getPartTrack(lowerEffects[i], partDelay + i * 0.65 + 0.7, 1.0, [lowerEffects[i], suit, 0])) + + damageAnims = [] + for i in xrange(0, 3): + damageAnims.append(['conked', + 0.01, + 0.3, + 0.71]) + + damageAnims.append(['conked', 0.01, 0.3]) + dodgeAnims = [] + dodgeAnims.append(['duck', + 0.01, + 0.2, + 2.7]) + dodgeAnims.append(['duck', + 0.01, + 1.22, + 1.28]) + dodgeAnims.append(['duck', 0.01, 3.16]) + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=1.9, showDamageExtraTime=1.1) + return Parallel(suitTrack, toonTrack, upperPartTracks, lowerPartTracks) + + +def doQuake(attack): + suit = attack['suit'] + suitTrack = getSuitAnimTrack(attack) + damageAnims = [['slip-forward'], ['slip-forward', 0.01]] + dodgeAnims = [['jump'], ['jump', 0.01], ['jump', 0.01]] + toonTracks = getToonTracks(attack, damageDelay=1.8, splicedDamageAnims=damageAnims, dodgeDelay=1.1, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=2.8, showDamageExtraTime=1.1) + return Parallel(suitTrack, toonTracks) + + +def doShake(attack): + suit = attack['suit'] + suitTrack = getSuitAnimTrack(attack) + damageAnims = [['slip-forward'], ['slip-forward', 0.01]] + dodgeAnims = [['jump'], ['jump', 0.01]] + toonTracks = getToonTracks(attack, damageDelay=1.1, splicedDamageAnims=damageAnims, dodgeDelay=0.7, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=2.8, showDamageExtraTime=1.1) + return Parallel(suitTrack, toonTracks) + + +def doTremor(attack): + suit = attack['suit'] + suitTrack = getSuitAnimTrack(attack) + damageAnims = [['slip-forward'], ['slip-forward', 0.01]] + dodgeAnims = [['jump'], ['jump', 0.01]] + toonTracks = getToonTracks(attack, damageDelay=1.1, splicedDamageAnims=damageAnims, dodgeDelay=0.7, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=2.8, showDamageExtraTime=1.1) + soundTrack = getSoundTrack('SA_tremor.ogg', delay=0.9, node=suit) + return Parallel(suitTrack, soundTrack, toonTracks) + + +def doHangUp(attack): + suit = attack['suit'] + battle = attack['battle'] + phone = globalPropPool.getProp('phone') + receiver = globalPropPool.getProp('receiver') + suitTrack = getSuitTrack(attack) + suitName = suit.getStyleName() + if suitName == 'tf': + phonePosPoints = [Point3(-0.23, 0.01, -0.26), VBase3(5.939, 2.763, -177.591)] + receiverPosPoints = [Point3(-0.13, -0.07, -0.06), VBase3(-1.854, 2.434, -177.579)] + receiverAdjustScale = Point3(0.8, 0.8, 0.8) + pickupDelay = 0.44 + dialDuration = 3.07 + finalPhoneDelay = 0.01 + scaleUpPoint = Point3(0.75, 0.75, 0.75) + else: + phonePosPoints = [Point3(0.23, 0.17, -0.11), VBase3(5.939, 2.763, -177.591)] + receiverPosPoints = [Point3(0.23, 0.17, -0.11), VBase3(5.939, 2.763, -177.591)] + receiverAdjustScale = MovieUtil.PNT3_ONE + pickupDelay = 0.74 + dialDuration = 3.07 + finalPhoneDelay = 0.69 + scaleUpPoint = MovieUtil.PNT3_ONE + propTrack = Sequence(Wait(0.3), Func(__showProp, phone, suit.getLeftHand(), phonePosPoints[0], phonePosPoints[1]), Func(__showProp, receiver, suit.getLeftHand(), receiverPosPoints[0], receiverPosPoints[1]), LerpScaleInterval(phone, 0.5, scaleUpPoint, MovieUtil.PNT3_NEARZERO), Wait(pickupDelay), Func(receiver.wrtReparentTo, suit.getRightHand()), LerpScaleInterval(receiver, 0.01, receiverAdjustScale), LerpPosHprInterval(receiver, 0.0001, Point3(-0.53, 0.21, -0.54), VBase3(-99.49, -35.27, 1.84)), Wait(dialDuration), Func(receiver.wrtReparentTo, phone), Wait(finalPhoneDelay), LerpScaleInterval(phone, 0.5, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProps, [receiver, phone])) + toonTrack = getToonTrack(attack, 5.5, ['slip-backward'], 4.7, ['jump']) + soundTrack = getSoundTrack('SA_hangup.ogg', delay=1.3, node=suit) + return Parallel(suitTrack, toonTrack, propTrack, soundTrack) + + +def doRedTape(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + tape = globalPropPool.getProp('redtape') + tubes = [] + for i in xrange(0, 3): + tubes.append(globalPropPool.getProp('redtape-tube')) + + suitTrack = getSuitTrack(attack) + suitName = suit.getStyleName() + if suitName == 'tf' or suitName == 'nc': + tapePosPoints = [Point3(-0.24, 0.09, -0.38), VBase3(-1.152, 86.581, -76.784)] + else: + tapePosPoints = [Point3(0.24, 0.09, -0.38), VBase3(-1.152, 86.581, -76.784)] + tapeScaleUpPoint = Point3(0.9, 0.9, 0.24) + propTrack = Sequence(getPropAppearTrack(tape, suit.getRightHand(), tapePosPoints, 0.8, tapeScaleUpPoint, scaleUpTime=0.5)) + propTrack.append(Wait(1.73)) + hitPoint = lambda toon = toon: __toonTorsoPoint(toon) + propTrack.append(getPropThrowTrack(attack, tape, [hitPoint], [__toonGroundPoint(attack, toon, 0.7)])) + hips = toon.getHipsParts() + animal = toon.style.getAnimal() + scale = ToontownGlobals.toonBodyScales[animal] + legs = toon.style.legs + torso = toon.style.torso + torso = torso[0] + animal = animal[0] + tubeHeight = -0.8 + if torso == 's': + scaleUpPoint = Point3(scale * 2.03, scale * 2.03, scale * 0.7975) + elif torso == 'm': + scaleUpPoint = Point3(scale * 2.03, scale * 2.03, scale * 0.7975) + elif torso == 'l': + scaleUpPoint = Point3(scale * 2.03, scale * 2.03, scale * 1.11) + if animal == 'h' or animal == 'd': + tubeHeight = -0.87 + scaleUpPoint = Point3(scale * 1.69, scale * 1.69, scale * 0.67) + tubePosPoints = [Point3(0, 0, tubeHeight), MovieUtil.PNT3_ZERO] + tubeTracks = Parallel() + tubeTracks.append(Func(battle.movie.needRestoreHips)) + for partNum in xrange(0, hips.getNumPaths()): + nextPart = hips.getPath(partNum) + tubeTracks.append(getPropTrack(tubes[partNum], nextPart, tubePosPoints, 3.25, 3.17, scaleUpPoint=scaleUpPoint)) + + tubeTracks.append(Func(battle.movie.clearRestoreHips)) + toonTrack = getToonTrack(attack, 3.4, ['struggle'], 2.8, ['jump']) + soundTrack = getSoundTrack('SA_red_tape.ogg', delay=2.9, node=suit) + if dmg > 0: + return Parallel(suitTrack, toonTrack, propTrack, soundTrack, tubeTracks) + else: + return Parallel(suitTrack, toonTrack, propTrack, soundTrack) + + +def doParadigmShift(attack): + suit = attack['suit'] + battle = attack['battle'] + targets = attack['target'] + hitAtleastOneToon = 0 + for t in targets: + if t['hp'] > 0: + hitAtleastOneToon = 1 + + damageDelay = 1.95 + dodgeDelay = 0.95 + sprayEffect = BattleParticles.createParticleEffect('ShiftSpray') + suitName = suit.getStyleName() + if suitName == 'm': + sprayEffect.setPos(Point3(-5.2, 4.6, 2.7)) + elif suitName == 'sd': + sprayEffect.setPos(Point3(-5.2, 4.6, 2.7)) + else: + sprayEffect.setPos(Point3(0.1, 4.6, 2.7)) + suitTrack = getSuitAnimTrack(attack) + sprayTrack = getPartTrack(sprayEffect, 1.0, 1.9, [sprayEffect, suit, 0]) + liftTracks = Parallel() + toonRiseTracks = Parallel() + for t in targets: + toon = t['toon'] + dmg = t['hp'] + if dmg > 0: + liftEffect = BattleParticles.createParticleEffect('ShiftLift') + liftEffect.setPos(toon.getPos(battle)) + liftEffect.setZ(liftEffect.getZ() - 1.3) + liftTracks.append(getPartTrack(liftEffect, 1.1, 4.1, [liftEffect, battle, 0])) + shadow = toon.dropShadow + fakeShadow = MovieUtil.copyProp(shadow) + x = toon.getX() + y = toon.getY() + z = toon.getZ() + height = 3 + groundPoint = Point3(x, y, z) + risePoint = Point3(x, y, z + height) + shakeRight = Point3(x, y + 0.7, z + height) + shakeLeft = Point3(x, y - 0.7, z + height) + shakeTrack = Sequence() + shakeTrack.append(Wait(damageDelay + 0.25)) + shakeTrack.append(Func(shadow.hide)) + shakeTrack.append(LerpPosInterval(toon, 1.1, risePoint)) + for i in xrange(0, 17): + shakeTrack.append(LerpPosInterval(toon, 0.03, shakeLeft)) + shakeTrack.append(LerpPosInterval(toon, 0.03, shakeRight)) + + shakeTrack.append(LerpPosInterval(toon, 0.1, risePoint)) + shakeTrack.append(LerpPosInterval(toon, 0.1, groundPoint)) + shakeTrack.append(Func(shadow.show)) + shadowTrack = Sequence() + shadowTrack.append(Func(battle.movie.needRestoreRenderProp, fakeShadow)) + shadowTrack.append(Wait(damageDelay + 0.25)) + shadowTrack.append(Func(fakeShadow.hide)) + shadowTrack.append(Func(fakeShadow.setScale, 0.27)) + shadowTrack.append(Func(fakeShadow.reparentTo, toon)) + shadowTrack.append(Func(fakeShadow.setPos, MovieUtil.PNT3_ZERO)) + shadowTrack.append(Func(fakeShadow.wrtReparentTo, battle)) + shadowTrack.append(Func(fakeShadow.show)) + shadowTrack.append(LerpScaleInterval(fakeShadow, 0.4, Point3(0.17, 0.17, 0.17))) + shadowTrack.append(Wait(1.81)) + shadowTrack.append(LerpScaleInterval(fakeShadow, 0.1, Point3(0.27, 0.27, 0.27))) + shadowTrack.append(Func(MovieUtil.removeProp, fakeShadow)) + shadowTrack.append(Func(battle.movie.clearRenderProp, fakeShadow)) + toonRiseTracks.append(Parallel(shakeTrack, shadowTrack)) + + damageAnims = [] + damageAnims.extend(getSplicedLerpAnims('think', 0.66, 1.9, startTime=2.06)) + damageAnims.append(['slip-backward', 0.01, 0.5]) + dodgeAnims = [] + dodgeAnims.append(['jump', + 0.01, + 0, + 0.6]) + dodgeAnims.extend(getSplicedLerpAnims('jump', 0.31, 1.0, startTime=0.6)) + dodgeAnims.append(['jump', 0, 0.91]) + toonTracks = getToonTracks(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, splicedDodgeAnims=dodgeAnims, showDamageExtraTime=2.7) + if hitAtleastOneToon == 1: + soundTrack = getSoundTrack('SA_paradigm_shift.ogg', delay=2.1, node=suit) + return Parallel(suitTrack, sprayTrack, soundTrack, liftTracks, toonTracks, toonRiseTracks) + else: + return Parallel(suitTrack, sprayTrack, liftTracks, toonTracks, toonRiseTracks) + + +def doPowerTrip(attack): + suit = attack['suit'] + battle = attack['battle'] + centerColor = Vec4(0.1, 0.1, 0.1, 0.4) + edgeColor = Vec4(0.4, 0.1, 0.9, 0.7) + powerBar1 = BattleParticles.createParticleEffect(file='powertrip') + powerBar2 = BattleParticles.createParticleEffect(file='powertrip2') + powerBar1.setPos(0, 6.1, 0.4) + powerBar1.setHpr(-60, 0, 0) + powerBar2.setPos(0, 6.1, 0.4) + powerBar2.setHpr(60, 0, 0) + powerBar1Particles = powerBar1.getParticlesNamed('particles-1') + powerBar2Particles = powerBar2.getParticlesNamed('particles-1') + powerBar1Particles.renderer.setCenterColor(centerColor) + powerBar1Particles.renderer.setEdgeColor(edgeColor) + powerBar2Particles.renderer.setCenterColor(centerColor) + powerBar2Particles.renderer.setEdgeColor(edgeColor) + waterfallEffect = BattleParticles.createParticleEffect('Waterfall') + waterfallEffect.setScale(11) + waterfallParticles = waterfallEffect.getParticlesNamed('particles-1') + waterfallParticles.renderer.setCenterColor(centerColor) + waterfallParticles.renderer.setEdgeColor(edgeColor) + suitName = suit.getStyleName() + if suitName == 'mh': + waterfallEffect.setPos(0, 4, 3.6) + suitTrack = getSuitAnimTrack(attack) + + def getPowerTrack(effect, suit = suit, battle = battle): + partTrack = Sequence(Wait(1.0), Func(battle.movie.needRestoreParticleEffect, effect), Func(effect.start, suit), Wait(0.4), LerpPosInterval(effect, 1.0, Point3(0, 15, 0.4)), LerpFunctionInterval(effect.setAlphaScale, fromData=1, toData=0, duration=0.4), Func(effect.cleanup), Func(battle.movie.clearRestoreParticleEffect, effect)) + return partTrack + + partTrack1 = getPowerTrack(powerBar1) + partTrack2 = getPowerTrack(powerBar2) + waterfallTrack = getPartTrack(waterfallEffect, 0.6, 1.3, [waterfallEffect, suit, 0]) + toonTracks = getToonTracks(attack, 1.8, ['slip-forward'], 1.29, ['jump']) + return Parallel(suitTrack, partTrack1, partTrack2, waterfallTrack, toonTracks) + + +def getThrowEndPoint(suit, toon, battle, whichBounce): + pnt = toon.getPos(toon) + if whichBounce == 'one': + pnt.setY(pnt[1] + 8) + elif whichBounce == 'two': + pnt.setY(pnt[1] + 5) + elif whichBounce == 'threeHit': + pnt.setZ(pnt[2] + toon.shoulderHeight + 0.3) + elif whichBounce == 'threeMiss': + pass + elif whichBounce == 'four': + pnt.setY(pnt[1] - 5) + return Point3(pnt) + + +def doBounceCheck(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + battle = attack['battle'] + toon = target['toon'] + dmg = target['hp'] + hitSuit = dmg > 0 + check = globalPropPool.getProp('bounced-check') + checkPosPoints = [MovieUtil.PNT3_ZERO, VBase3(95.247, 79.025, 88.849)] + bounce1Point = lambda suit = suit, toon = toon, battle = battle: getThrowEndPoint(suit, toon, battle, 'one') + bounce2Point = lambda suit = suit, toon = toon, battle = battle: getThrowEndPoint(suit, toon, battle, 'two') + hit3Point = lambda suit = suit, toon = toon, battle = battle: getThrowEndPoint(suit, toon, battle, 'threeHit') + miss3Point = lambda suit = suit, toon = toon, battle = battle: getThrowEndPoint(suit, toon, battle, 'threeMiss') + bounce4Point = lambda suit = suit, toon = toon, battle = battle: getThrowEndPoint(suit, toon, battle, 'four') + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'a': + throwDelay = 2.5 + dodgeDelay = 4.3 + damageDelay = 5.1 + elif suitType == 'b': + throwDelay = 1.8 + dodgeDelay = 3.6 + damageDelay = 4.4 + elif suitType == 'c': + throwDelay = 1.8 + dodgeDelay = 3.6 + damageDelay = 4.4 + suitTrack = getSuitTrack(attack) + checkPropTrack = Sequence(getPropAppearTrack(check, suit.getRightHand(), checkPosPoints, 1e-05, Point3(8.5, 8.5, 8.5), startScale=MovieUtil.PNT3_ONE)) + checkPropTrack.append(Wait(throwDelay)) + checkPropTrack.append(Func(check.wrtReparentTo, toon)) + checkPropTrack.append(Func(check.setHpr, Point3(0, -90, 0))) + checkPropTrack.append(getThrowTrack(check, bounce1Point, duration=0.5, parent=toon)) + checkPropTrack.append(getThrowTrack(check, bounce2Point, duration=0.9, parent=toon)) + if hitSuit: + checkPropTrack.append(getThrowTrack(check, hit3Point, duration=0.7, parent=toon)) + else: + checkPropTrack.append(getThrowTrack(check, miss3Point, duration=0.7, parent=toon)) + checkPropTrack.append(getThrowTrack(check, bounce4Point, duration=0.7, parent=toon)) + checkPropTrack.append(LerpScaleInterval(check, 0.3, MovieUtil.PNT3_NEARZERO)) + checkPropTrack.append(Func(MovieUtil.removeProp, check)) + toonTrack = getToonTrack(attack, damageDelay, ['conked'], dodgeDelay, ['sidestep']) + soundTracks = Sequence(getSoundTrack('SA_pink_slip.ogg', delay=throwDelay + 0.5, duration=0.6, node=suit), getSoundTrack('SA_pink_slip.ogg', delay=0.4, duration=0.6, node=suit)) + return Parallel(suitTrack, checkPropTrack, toonTrack, soundTracks) + + +def doWatercooler(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + watercooler = globalPropPool.getProp('watercooler') + + def getCoolerSpout(watercooler = watercooler): + spout = watercooler.find('**/joint_toSpray') + return spout.getPos(render) + + hitPoint = lambda toon = toon: __toonFacePoint(toon) + missPoint = lambda prop = watercooler, toon = toon: __toonMissPoint(prop, toon, 0, parent=render) + hitSprayTrack = MovieUtil.getSprayTrack(battle, Point4(0.75, 0.75, 1.0, 0.8), getCoolerSpout, hitPoint, 0.2, 0.2, 0.2, horizScale=0.3, vertScale=0.3) + missSprayTrack = MovieUtil.getSprayTrack(battle, Point4(0.75, 0.75, 1.0, 0.8), getCoolerSpout, missPoint, 0.2, 0.2, 0.2, horizScale=0.3, vertScale=0.3) + suitTrack = getSuitTrack(attack) + posPoints = [Point3(0.48, 0.11, -0.92), VBase3(20.403, 33.158, 69.511)] + propTrack = Sequence(Wait(1.01), Func(__showProp, watercooler, suit.getLeftHand(), posPoints[0], posPoints[1]), LerpScaleInterval(watercooler, 0.5, Point3(1.15, 1.15, 1.15)), Wait(1.6)) + if dmg > 0: + propTrack.append(hitSprayTrack) + else: + propTrack.append(missSprayTrack) + propTrack += [Wait(0.01), LerpScaleInterval(watercooler, 0.5, MovieUtil.PNT3_NEARZERO), Func(MovieUtil.removeProp, watercooler)] + splashTrack = Sequence() + if dmg > 0: + + def prepSplash(splash, targetPoint): + splash.reparentTo(render) + splash.setPos(targetPoint) + scale = splash.getScale() + splash.setBillboardPointWorld() + splash.setScale(scale) + + splash = globalPropPool.getProp('splash-from-splat') + splash.setColor(0.75, 0.75, 1, 0.8) + splash.setScale(0.3) + splashTrack = Sequence(Func(battle.movie.needRestoreRenderProp, splash), Wait(3.2), Func(prepSplash, splash, __toonFacePoint(toon)), ActorInterval(splash, 'splash-from-splat'), Func(MovieUtil.removeProp, splash), Func(battle.movie.clearRenderProp, splash)) + toonTrack = getToonTrack(attack, suitTrack.getDuration() - 1.5, ['cringe'], 2.4, ['sidestep']) + soundTrack = Sequence(Wait(1.1), SoundInterval(globalBattleSoundCache.getSound('SA_watercooler_appear_only.ogg'), node=suit, duration=1.4722), Wait(0.4), SoundInterval(globalBattleSoundCache.getSound('SA_watercooler_spray_only.ogg'), node=suit, duration=2.313)) + return Parallel(suitTrack, toonTrack, propTrack, soundTrack, splashTrack) + + +def doFired(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + BattleParticles.loadParticles() + baseFlameEffect = BattleParticles.createParticleEffect(file='firedBaseFlame') + flameEffect = BattleParticles.createParticleEffect('FiredFlame') + flecksEffect = BattleParticles.createParticleEffect('SpriteFiredFlecks') + BattleParticles.setEffectTexture(baseFlameEffect, 'fire') + BattleParticles.setEffectTexture(flameEffect, 'fire') + BattleParticles.setEffectTexture(flecksEffect, 'roll-o-dex', color=Vec4(0.8, 0.8, 0.8, 1)) + baseFlameSmall = BattleParticles.createParticleEffect(file='firedBaseFlame') + flameSmall = BattleParticles.createParticleEffect('FiredFlame') + flecksSmall = BattleParticles.createParticleEffect('SpriteFiredFlecks') + BattleParticles.setEffectTexture(baseFlameSmall, 'fire') + BattleParticles.setEffectTexture(flameSmall, 'fire') + BattleParticles.setEffectTexture(flecksSmall, 'roll-o-dex', color=Vec4(0.8, 0.8, 0.8, 1)) + baseFlameSmall.setScale(0.7) + flameSmall.setScale(0.7) + flecksSmall.setScale(0.7) + suitTrack = getSuitTrack(attack) + baseFlameTrack = getPartTrack(baseFlameEffect, 1.0, 1.9, [baseFlameEffect, toon, 0]) + flameTrack = getPartTrack(flameEffect, 1.0, 1.9, [flameEffect, toon, 0]) + flecksTrack = getPartTrack(flecksEffect, 1.8, 1.1, [flecksEffect, toon, 0]) + baseFlameSmallTrack = getPartTrack(baseFlameSmall, 1.0, 1.9, [baseFlameSmall, toon, 0]) + flameSmallTrack = getPartTrack(flameSmall, 1.0, 1.9, [flameSmall, toon, 0]) + flecksSmallTrack = getPartTrack(flecksSmall, 1.8, 1.1, [flecksSmall, toon, 0]) + + def changeColor(parts): + track = Parallel() + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + track.append(Func(nextPart.setColorScale, Vec4(0, 0, 0, 1))) + + return track + + def resetColor(parts): + track = Parallel() + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + track.append(Func(nextPart.clearColorScale)) + + return track + + if dmg > 0: + headParts = toon.getHeadParts() + torsoParts = toon.getTorsoParts() + legsParts = toon.getLegsParts() + colorTrack = Sequence() + colorTrack.append(Wait(2.0)) + colorTrack.append(Func(battle.movie.needRestoreColor)) + colorTrack.append(changeColor(headParts)) + colorTrack.append(changeColor(torsoParts)) + colorTrack.append(changeColor(legsParts)) + colorTrack.append(Wait(3.5)) + colorTrack.append(resetColor(headParts)) + colorTrack.append(resetColor(torsoParts)) + colorTrack.append(resetColor(legsParts)) + colorTrack.append(Func(battle.movie.clearRestoreColor)) + damageAnims = [] + damageAnims.append(['cringe', + 0.01, + 0.7, + 0.62]) + damageAnims.append(['slip-forward', + 1e-05, + 0.4, + 1.2]) + damageAnims.extend(getSplicedLerpAnims('slip-forward', 0.31, 0.8, startTime=1.2)) + toonTrack = getToonTrack(attack, damageDelay=1.5, splicedDamageAnims=damageAnims, dodgeDelay=0.3, dodgeAnimNames=['sidestep']) + soundTrack = getSoundTrack('SA_hot_air.ogg', delay=1.0, node=suit) + if dmg > 0: + return Parallel(suitTrack, baseFlameTrack, flameTrack, flecksTrack, toonTrack, colorTrack, soundTrack) + else: + return Parallel(suitTrack, baseFlameSmallTrack, flameSmallTrack, flecksSmallTrack, toonTrack, soundTrack) + + +def doAudit(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + calculator = globalPropPool.getProp('calculator') + BattleParticles.loadParticles() + particleEffect = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect, 'audit-one', color=Vec4(0, 0, 0, 1)) + particleEffect2 = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect2, 'audit-two', color=Vec4(0, 0, 0, 1)) + particleEffect3 = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect3, 'audit-three', color=Vec4(0, 0, 0, 1)) + particleEffect4 = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect4, 'audit-four', color=Vec4(0, 0, 0, 1)) + particleEffect5 = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect5, 'audit-mult', color=Vec4(0, 0, 0, 1)) + suitTrack = getSuitTrack(attack) + partTrack = getPartTrack(particleEffect, 2.1, 1.9, [particleEffect, suit, 0]) + partTrack2 = getPartTrack(particleEffect2, 2.2, 2.0, [particleEffect2, suit, 0]) + partTrack3 = getPartTrack(particleEffect3, 2.3, 2.1, [particleEffect3, suit, 0]) + partTrack4 = getPartTrack(particleEffect4, 2.4, 2.2, [particleEffect4, suit, 0]) + partTrack5 = getPartTrack(particleEffect5, 2.5, 2.3, [particleEffect5, suit, 0]) + suitName = attack['suitName'] + if suitName == 'nc': + calcPosPoints = [Point3(-0.15, 0.37, 0.03), VBase3(1.352, -6.518, -6.045)] + calcDuration = 0.76 + scaleUpPoint = Point3(1.1, 1.85, 1.81) + else: + calcPosPoints = [Point3(0.35, 0.52, 0.03), VBase3(1.352, -6.518, -6.045)] + calcDuration = 1.87 + scaleUpPoint = Point3(1.0, 1.37, 1.31) + calcPropTrack = getPropTrack(calculator, suit.getLeftHand(), calcPosPoints, 1e-06, calcDuration, scaleUpPoint=scaleUpPoint, anim=1, propName='calculator', animStartTime=0.5, animDuration=3.4) + toonTrack = getToonTrack(attack, 3.2, ['conked'], 0.9, ['duck'], showMissedExtraTime=2.2) + soundTrack = getSoundTrack('SA_audit.ogg', delay=1.9, node=suit) + return Parallel(suitTrack, toonTrack, calcPropTrack, soundTrack, partTrack, partTrack2, partTrack3, partTrack4, partTrack5) + + +def doCalculate(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + calculator = globalPropPool.getProp('calculator') + BattleParticles.loadParticles() + particleEffect = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect, 'audit-one', color=Vec4(0, 0, 0, 1)) + particleEffect2 = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect2, 'audit-plus', color=Vec4(0, 0, 0, 1)) + particleEffect3 = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect3, 'audit-mult', color=Vec4(0, 0, 0, 1)) + particleEffect4 = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect4, 'audit-three', color=Vec4(0, 0, 0, 1)) + particleEffect5 = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect5, 'audit-div', color=Vec4(0, 0, 0, 1)) + suitTrack = getSuitTrack(attack) + partTrack = getPartTrack(particleEffect, 2.1, 1.9, [particleEffect, suit, 0]) + partTrack2 = getPartTrack(particleEffect2, 2.2, 2.0, [particleEffect2, suit, 0]) + partTrack3 = getPartTrack(particleEffect3, 2.3, 2.1, [particleEffect3, suit, 0]) + partTrack4 = getPartTrack(particleEffect4, 2.4, 2.2, [particleEffect4, suit, 0]) + partTrack5 = getPartTrack(particleEffect5, 2.5, 2.3, [particleEffect5, suit, 0]) + suitName = attack['suitName'] + if suitName == 'nc': + calcPosPoints = [Point3(-0.15, 0.37, 0.03), VBase3(1.352, -6.518, -6.045)] + calcDuration = 0.76 + scaleUpPoint = Point3(1.1, 1.85, 1.81) + else: + calcPosPoints = [Point3(0.35, 0.52, 0.03), VBase3(1.352, -6.518, -6.045)] + calcDuration = 1.87 + scaleUpPoint = Point3(1.0, 1.37, 1.31) + calcPropTrack = getPropTrack(calculator, suit.getLeftHand(), calcPosPoints, 1e-06, calcDuration, scaleUpPoint=scaleUpPoint, anim=1, propName='calculator', animStartTime=0.5, animDuration=3.4) + toonTrack = getToonTrack(attack, 3.2, ['conked'], 1.8, ['sidestep']) + return Parallel(suitTrack, toonTrack, calcPropTrack, partTrack, partTrack2, partTrack3, partTrack4, partTrack5) + + +def doTabulate(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + calculator = globalPropPool.getProp('calculator') + BattleParticles.loadParticles() + particleEffect = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect, 'audit-plus', color=Vec4(0, 0, 0, 1)) + particleEffect2 = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect2, 'audit-minus', color=Vec4(0, 0, 0, 1)) + particleEffect3 = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect3, 'audit-mult', color=Vec4(0, 0, 0, 1)) + particleEffect4 = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect4, 'audit-div', color=Vec4(0, 0, 0, 1)) + particleEffect5 = BattleParticles.createParticleEffect('Calculate') + BattleParticles.setEffectTexture(particleEffect5, 'audit-one', color=Vec4(0, 0, 0, 1)) + suitTrack = getSuitTrack(attack) + partTrack = getPartTrack(particleEffect, 2.1, 1.9, [particleEffect, suit, 0]) + partTrack2 = getPartTrack(particleEffect2, 2.2, 2.0, [particleEffect2, suit, 0]) + partTrack3 = getPartTrack(particleEffect3, 2.3, 2.1, [particleEffect3, suit, 0]) + partTrack4 = getPartTrack(particleEffect4, 2.4, 2.2, [particleEffect4, suit, 0]) + partTrack5 = getPartTrack(particleEffect5, 2.5, 2.3, [particleEffect5, suit, 0]) + suitName = attack['suitName'] + if suitName == 'nc': + calcPosPoints = [Point3(-0.15, 0.37, 0.03), VBase3(1.352, -6.518, -6.045)] + calcDuration = 0.76 + scaleUpPoint = Point3(1.1, 1.85, 1.81) + else: + calcPosPoints = [Point3(0.35, 0.52, 0.03), VBase3(1.352, -6.518, -6.045)] + calcDuration = 1.87 + scaleUpPoint = Point3(1.0, 1.37, 1.31) + calcPropTrack = getPropTrack(calculator, suit.getLeftHand(), calcPosPoints, 1e-06, calcDuration, scaleUpPoint=scaleUpPoint, anim=1, propName='calculator', animStartTime=0.5, animDuration=3.4) + toonTrack = getToonTrack(attack, 3.2, ['conked'], 1.8, ['sidestep']) + return Parallel(suitTrack, toonTrack, calcPropTrack, partTrack, partTrack2, partTrack3, partTrack4, partTrack5) + + +def doCrunch(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + throwDuration = 3.03 + suitTrack = getSuitTrack(attack) + numberNames = ['one', + 'two', + 'three', + 'four', + 'five', + 'six'] + BattleParticles.loadParticles() + numberSpill1 = BattleParticles.createParticleEffect(file='numberSpill') + numberSpill2 = BattleParticles.createParticleEffect(file='numberSpill') + spillTexture1 = random.choice(numberNames) + spillTexture2 = random.choice(numberNames) + BattleParticles.setEffectTexture(numberSpill1, 'audit-' + spillTexture1) + BattleParticles.setEffectTexture(numberSpill2, 'audit-' + spillTexture2) + numberSpillTrack1 = getPartTrack(numberSpill1, 1.1, 2.2, [numberSpill1, suit, 0]) + numberSpillTrack2 = getPartTrack(numberSpill2, 1.5, 1.0, [numberSpill2, suit, 0]) + numberSprayTracks = Parallel() + numOfNumbers = random.randint(5, 9) + for i in xrange(0, numOfNumbers - 1): + nextSpray = BattleParticles.createParticleEffect(file='numberSpray') + nextTexture = random.choice(numberNames) + BattleParticles.setEffectTexture(nextSpray, 'audit-' + nextTexture) + nextStartTime = random.random() * 0.6 + throwDuration + nextDuration = random.random() * 0.4 + 1.4 + nextSprayTrack = getPartTrack(nextSpray, nextStartTime, nextDuration, [nextSpray, suit, 0]) + numberSprayTracks.append(nextSprayTrack) + + numberTracks = Parallel() + for i in xrange(0, numOfNumbers): + texture = random.choice(numberNames) + next = MovieUtil.copyProp(BattleParticles.getParticle('audit-' + texture)) + next.reparentTo(suit.getRightHand()) + next.setScale(0.01, 0.01, 0.01) + next.setColor(Vec4(0.0, 0.0, 0.0, 1.0)) + next.setPos(random.random() * 0.6 - 0.3, random.random() * 0.6 - 0.3, random.random() * 0.6 - 0.3) + next.setHpr(VBase3(-1.15, 86.58, -76.78)) + numberTrack = Sequence(Wait(0.9), LerpScaleInterval(next, 0.6, MovieUtil.PNT3_ONE), Wait(1.7), Func(MovieUtil.removeProp, next)) + numberTracks.append(numberTrack) + + damageAnims = [] + damageAnims.append(['cringe', + 0.01, + 0.14, + 0.28]) + damageAnims.append(['cringe', + 0.01, + 0.16, + 0.3]) + damageAnims.append(['cringe', + 0.01, + 0.13, + 0.22]) + damageAnims.append(['slip-forward', 0.01, 0.6]) + toonTrack = getToonTrack(attack, damageDelay=4.7, splicedDamageAnims=damageAnims, dodgeDelay=3.6, dodgeAnimNames=['sidestep']) + return Parallel(suitTrack, toonTrack, numberSpillTrack1, numberSpillTrack2, numberTracks, numberSprayTracks) + + +def doLiquidate(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + dmg = target['hp'] + toon = target['toon'] + BattleParticles.loadParticles() + rainEffect = BattleParticles.createParticleEffect(file='liquidate') + rainEffect2 = BattleParticles.createParticleEffect(file='liquidate') + rainEffect3 = BattleParticles.createParticleEffect(file='liquidate') + cloud = globalPropPool.getProp('stormcloud') + suitType = getSuitBodyType(attack['suitName']) + if suitType == 'a': + partDelay = 0.2 + damageDelay = 3.5 + dodgeDelay = 2.45 + elif suitType == 'b': + partDelay = 0.2 + damageDelay = 3.5 + dodgeDelay = 2.45 + elif suitType == 'c': + partDelay = 0.2 + damageDelay = 3.5 + dodgeDelay = 2.45 + suitTrack = getSuitTrack(attack, delay=0.9) + initialCloudHeight = suit.height + 3 + cloudPosPoints = [Point3(0, 3, initialCloudHeight), VBase3(180, 0, 0)] + cloudPropTrack = Sequence() + cloudPropTrack.append(Func(cloud.pose, 'stormcloud', 0)) + cloudPropTrack.append(getPropAppearTrack(cloud, suit, cloudPosPoints, 1e-06, Point3(3, 3, 3), scaleUpTime=0.7)) + cloudPropTrack.append(Func(battle.movie.needRestoreRenderProp, cloud)) + cloudPropTrack.append(Func(cloud.wrtReparentTo, render)) + targetPoint = __toonFacePoint(toon) + targetPoint.setZ(targetPoint[2] + 3) + cloudPropTrack.append(Wait(1.1)) + cloudPropTrack.append(LerpPosInterval(cloud, 1, pos=targetPoint)) + cloudPropTrack.append(Wait(partDelay)) + cloudPropTrack.append(Parallel(Sequence(ParticleInterval(rainEffect, cloud, worldRelative=0, duration=2.1, cleanup=True)), Sequence(Wait(0.1), ParticleInterval(rainEffect2, cloud, worldRelative=0, duration=2.0, cleanup=True)), Sequence(Wait(0.1), ParticleInterval(rainEffect3, cloud, worldRelative=0, duration=2.0, cleanup=True)), Sequence(ActorInterval(cloud, 'stormcloud', startTime=3, duration=0.1), ActorInterval(cloud, 'stormcloud', startTime=1, duration=2.3)))) + cloudPropTrack.append(Wait(0.4)) + cloudPropTrack.append(LerpScaleInterval(cloud, 0.5, MovieUtil.PNT3_NEARZERO)) + cloudPropTrack.append(Func(MovieUtil.removeProp, cloud)) + cloudPropTrack.append(Func(battle.movie.clearRenderProp, cloud)) + damageAnims = [['melt'], ['jump', 1.5, 0.4]] + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep']) + soundTrack = getSoundTrack('SA_liquidate.ogg', delay=2.0, node=suit) + if dmg > 0: + puddle = globalPropPool.getProp('quicksand') + puddle.setColor(Vec4(0.0, 0.0, 1.0, 1)) + puddle.setHpr(Point3(120, 0, 0)) + puddle.setScale(0.01) + puddleTrack = Sequence(Func(battle.movie.needRestoreRenderProp, puddle), Wait(damageDelay - 0.7), Func(puddle.reparentTo, battle), Func(puddle.setPos, toon.getPos(battle)), LerpScaleInterval(puddle, 1.7, Point3(1.7, 1.7, 1.7), startScale=MovieUtil.PNT3_NEARZERO), Wait(3.2), LerpFunctionInterval(puddle.setAlphaScale, fromData=1, toData=0, duration=0.8), Func(MovieUtil.removeProp, puddle), Func(battle.movie.clearRenderProp, puddle)) + return Parallel(suitTrack, toonTrack, cloudPropTrack, soundTrack, puddleTrack) + else: + return Parallel(suitTrack, toonTrack, cloudPropTrack, soundTrack) + + +def doMarketCrash(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + suitDelay = 1.32 + propDelay = 0.6 + throwDuration = 1.5 + paper = globalPropPool.getProp('newspaper') + suitTrack = getSuitTrack(attack) + posPoints = [Point3(-0.07, 0.17, -0.13), VBase3(161.867, -33.149, -48.086)] + paperTrack = Sequence(getPropAppearTrack(paper, suit.getRightHand(), posPoints, propDelay, Point3(3, 3, 3), scaleUpTime=0.5)) + paperTrack.append(Wait(suitDelay)) + hitPoint = toon.getPos(battle) + hitPoint.setX(hitPoint.getX() + 1.2) + hitPoint.setY(hitPoint.getY() + 1.5) + if dmg > 0: + hitPoint.setZ(hitPoint.getZ() + 1.1) + movePoint = Point3(hitPoint.getX(), hitPoint.getY() - 1.8, hitPoint.getZ() + 0.2) + paperTrack.append(Func(battle.movie.needRestoreRenderProp, paper)) + paperTrack.append(Func(paper.wrtReparentTo, battle)) + paperTrack.append(getThrowTrack(paper, hitPoint, duration=throwDuration, parent=battle)) + paperTrack.append(Wait(0.6)) + paperTrack.append(LerpPosInterval(paper, 0.4, movePoint)) + spinTrack = Sequence(Wait(propDelay + suitDelay + 0.2), LerpHprInterval(paper, throwDuration, Point3(-360, 0, 0))) + sizeTrack = Sequence(Wait(propDelay + suitDelay + 0.2), LerpScaleInterval(paper, throwDuration, Point3(6, 6, 6)), Wait(0.95), LerpScaleInterval(paper, 0.4, MovieUtil.PNT3_NEARZERO)) + propTrack = Sequence(Parallel(paperTrack, spinTrack, sizeTrack), Func(MovieUtil.removeProp, paper), Func(battle.movie.clearRenderProp, paper)) + damageAnims = [] + damageAnims.append(['cringe', + 0.01, + 0.21, + 0.08]) + damageAnims.append(['slip-forward', + 0.01, + 0.6, + 0.85]) + damageAnims.extend(getSplicedLerpAnims('slip-forward', 0.31, 0.95, startTime=1.2)) + damageAnims.append(['slip-forward', 0.01, 1.51]) + toonTrack = getToonTrack(attack, damageDelay=3.8, splicedDamageAnims=damageAnims, dodgeDelay=2.4, dodgeAnimNames=['sidestep'], showDamageExtraTime=0.4, showMissedExtraTime=1.3) + return Parallel(suitTrack, toonTrack, propTrack) + + +def doBite(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + teeth = globalPropPool.getProp('teeth') + propDelay = 0.8 + propScaleUpTime = 0.5 + suitDelay = 1.73 + throwDelay = propDelay + propScaleUpTime + suitDelay + throwDuration = 0.4 + suitTrack = getSuitTrack(attack) + posPoints = [Point3(-0.05, 0.41, -0.54), VBase3(4.465, -3.563, 51.479)] + teethAppearTrack = Sequence(getPropAppearTrack(teeth, suit.getRightHand(), posPoints, propDelay, Point3(3, 3, 3), scaleUpTime=propScaleUpTime)) + teethAppearTrack.append(Wait(suitDelay)) + teethAppearTrack.append(Func(battle.movie.needRestoreRenderProp, teeth)) + teethAppearTrack.append(Func(teeth.wrtReparentTo, battle)) + if dmg > 0: + x = toon.getX(battle) + y = toon.getY(battle) + z = toon.getZ(battle) + toonHeight = z + toon.getHeight() + flyPoint = Point3(x, y + 2.7, toonHeight * 0.8) + teethAppearTrack.append(LerpPosInterval(teeth, throwDuration, pos=flyPoint)) + teethAppearTrack.append(LerpPosInterval(teeth, 0.4, pos=Point3(x, y + 3.2, toonHeight * 0.7))) + teethAppearTrack.append(LerpPosInterval(teeth, 0.3, pos=Point3(x, y + 4.7, toonHeight * 0.5))) + teethAppearTrack.append(Wait(0.2)) + teethAppearTrack.append(LerpPosInterval(teeth, 0.1, pos=Point3(x, y - 0.2, toonHeight * 0.9))) + teethAppearTrack.append(Wait(0.4)) + scaleTrack = Sequence(Wait(throwDelay), LerpScaleInterval(teeth, throwDuration, Point3(8, 8, 8)), Wait(0.9), LerpScaleInterval(teeth, 0.2, Point3(14, 14, 14)), Wait(1.2), LerpScaleInterval(teeth, 0.3, MovieUtil.PNT3_NEARZERO)) + hprTrack = Sequence(Wait(throwDelay), LerpHprInterval(teeth, 0.3, Point3(180, 0, 0)), Wait(0.2), LerpHprInterval(teeth, 0.4, Point3(180, -35, 0), startHpr=Point3(180, 0, 0)), Wait(0.6), LerpHprInterval(teeth, 0.1, Point3(180, -75, 0), startHpr=Point3(180, -35, 0))) + animTrack = Sequence(Wait(throwDelay), ActorInterval(teeth, 'teeth', duration=throwDuration), ActorInterval(teeth, 'teeth', duration=0.3), Func(teeth.pose, 'teeth', 1), Wait(0.7), ActorInterval(teeth, 'teeth', duration=0.9)) + propTrack = Sequence(Parallel(teethAppearTrack, scaleTrack, hprTrack, animTrack), Func(MovieUtil.removeProp, teeth), Func(battle.movie.clearRenderProp, teeth)) + else: + flyPoint = __toonFacePoint(toon, parent=battle) + flyPoint.setY(flyPoint.getY() - 7.1) + teethAppearTrack.append(LerpPosInterval(teeth, throwDuration, pos=flyPoint)) + teethAppearTrack.append(Func(MovieUtil.removeProp, teeth)) + teethAppearTrack.append(Func(battle.movie.clearRenderProp, teeth)) + propTrack = teethAppearTrack + damageAnims = [['cringe', + 0.01, + 0.7, + 1.2], ['conked', + 0.01, + 0.2, + 2.1], ['conked', 0.01, 3.2]] + dodgeAnims = [['cringe', + 0.01, + 0.7, + 0.2], ['duck', 0.01, 1.6]] + toonTrack = getToonTrack(attack, damageDelay=3.2, splicedDamageAnims=damageAnims, dodgeDelay=2.9, splicedDodgeAnims=dodgeAnims, showDamageExtraTime=2.4) + return Parallel(suitTrack, toonTrack, propTrack) + + +def doChomp(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + teeth = globalPropPool.getProp('teeth') + propDelay = 0.8 + propScaleUpTime = 0.5 + suitDelay = 1.73 + throwDelay = propDelay + propScaleUpTime + suitDelay + throwDuration = 0.4 + suitTrack = getSuitTrack(attack) + posPoints = [Point3(-0.05, 0.41, -0.54), VBase3(4.465, -3.563, 51.479)] + teethAppearTrack = Sequence(getPropAppearTrack(teeth, suit.getRightHand(), posPoints, propDelay, Point3(3, 3, 3), scaleUpTime=propScaleUpTime)) + teethAppearTrack.append(Wait(suitDelay)) + teethAppearTrack.append(Func(battle.movie.needRestoreRenderProp, teeth)) + teethAppearTrack.append(Func(teeth.wrtReparentTo, battle)) + if dmg > 0: + x = toon.getX(battle) + y = toon.getY(battle) + z = toon.getZ(battle) + toonHeight = z + toon.getHeight() + flyPoint = Point3(x, y + 2.7, toonHeight * 0.7) + teethAppearTrack.append(LerpPosInterval(teeth, throwDuration, pos=flyPoint)) + teethAppearTrack.append(LerpPosInterval(teeth, 0.4, pos=Point3(x, y + 3.2, toonHeight * 0.7))) + teethAppearTrack.append(LerpPosInterval(teeth, 0.3, pos=Point3(x, y + 4.7, toonHeight * 0.5))) + teethAppearTrack.append(Wait(0.2)) + teethAppearTrack.append(LerpPosInterval(teeth, 0.1, pos=Point3(x, y, toonHeight + 3))) + teethAppearTrack.append(LerpPosInterval(teeth, 0.1, pos=Point3(x, y - 1.2, toonHeight * 0.7))) + teethAppearTrack.append(LerpPosInterval(teeth, 0.1, pos=Point3(x, y - 0.7, toonHeight * 0.4))) + teethAppearTrack.append(Wait(0.4)) + scaleTrack = Sequence(Wait(throwDelay), LerpScaleInterval(teeth, throwDuration, Point3(6, 6, 6)), Wait(0.9), LerpScaleInterval(teeth, 0.2, Point3(10, 10, 10)), Wait(1.2), LerpScaleInterval(teeth, 0.3, MovieUtil.PNT3_NEARZERO)) + hprTrack = Sequence(Wait(throwDelay), LerpHprInterval(teeth, 0.3, Point3(180, 0, 0)), Wait(0.2), LerpHprInterval(teeth, 0.4, Point3(180, -35, 0), startHpr=Point3(180, 0, 0)), Wait(0.6), LerpHprInterval(teeth, 0.1, Point3(0, -35, 0), startHpr=Point3(180, -35, 0))) + animTrack = Sequence(Wait(throwDelay), ActorInterval(teeth, 'teeth', duration=throwDuration), ActorInterval(teeth, 'teeth', duration=0.3), Func(teeth.pose, 'teeth', 1), Wait(0.7), ActorInterval(teeth, 'teeth', duration=0.9)) + propTrack = Sequence(Parallel(teethAppearTrack, scaleTrack, hprTrack, animTrack), Func(MovieUtil.removeProp, teeth), Func(battle.movie.clearRenderProp, teeth)) + else: + x = toon.getX(battle) + y = toon.getY(battle) + z = toon.getZ(battle) + z = z + 0.2 + flyPoint = Point3(x, y - 2.1, z) + teethAppearTrack.append(LerpPosInterval(teeth, throwDuration, pos=flyPoint)) + teethAppearTrack.append(Wait(0.2)) + teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x + 0.5, y - 2.5, z))) + teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x + 1.0, y - 3.0, z + 0.4))) + teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x + 1.3, y - 3.6, z))) + teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x + 0.9, y - 3.1, z + 0.4))) + teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x + 0.3, y - 2.6, z))) + teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x - 0.1, y - 2.2, z + 0.4))) + teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x - 0.4, y - 1.9, z))) + teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x - 0.7, y - 2.1, z + 0.4))) + teethAppearTrack.append(LerpPosInterval(teeth, 0.2, pos=Point3(x - 0.8, y - 2.3, z))) + teethAppearTrack.append(LerpScaleInterval(teeth, 0.6, MovieUtil.PNT3_NEARZERO)) + hprTrack = Sequence(Wait(throwDelay), LerpHprInterval(teeth, 0.3, Point3(180, 0, 0)), Wait(0.5), LerpHprInterval(teeth, 0.4, Point3(80, 0, 0), startHpr=Point3(180, 0, 0)), LerpHprInterval(teeth, 0.8, Point3(-10, 0, 0), startHpr=Point3(80, 0, 0))) + animTrack = Sequence(Wait(throwDelay), ActorInterval(teeth, 'teeth', duration=3.6)) + propTrack = Sequence(Parallel(teethAppearTrack, hprTrack, animTrack), Func(MovieUtil.removeProp, teeth), Func(battle.movie.clearRenderProp, teeth)) + damageAnims = [['cringe', + 0.01, + 0.7, + 1.2], + ['spit', + 0.01, + 2.95, + 1.47], + ['spit', + 0.01, + 4.42, + 0.07], + ['spit', + 0.08, + 4.49, + -0.07], + ['spit', + 0.08, + 4.42, + 0.07], + ['spit', + 0.08, + 4.49, + -0.07], + ['spit', + 0.08, + 4.42, + 0.07], + ['spit', + 0.08, + 4.49, + -0.07], + ['spit', 0.01, 4.42]] + dodgeAnims = [['jump', 0.01, 0.01]] + toonTrack = getToonTrack(attack, damageDelay=3.2, splicedDamageAnims=damageAnims, dodgeDelay=2.75, splicedDodgeAnims=dodgeAnims, showDamageExtraTime=1.4) + return Parallel(suitTrack, toonTrack, propTrack) + + +def doEvictionNotice(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + paper = globalPropPool.getProp('shredder-paper') + suitTrack = getSuitTrack(attack) + posPoints = [Point3(-0.04, 0.15, -1.38), VBase3(10.584, -11.945, 18.316)] + propTrack = Sequence(getPropAppearTrack(paper, suit.getRightHand(), posPoints, 0.8, MovieUtil.PNT3_ONE, scaleUpTime=0.5)) + propTrack.append(Wait(1.73)) + hitPoint = __toonFacePoint(toon, parent=battle) + hitPoint.setX(hitPoint.getX() - 1.4) + missPoint = __toonGroundPoint(attack, toon, 0.7, parent=battle) + missPoint.setX(missPoint.getX() - 1.1) + propTrack.append(getPropThrowTrack(attack, paper, [hitPoint], [missPoint], parent=battle)) + toonTrack = getToonTrack(attack, 3.4, ['conked'], 2.8, ['jump']) + return Parallel(suitTrack, toonTrack, propTrack) + + +def doThrowBook(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + suitDelay = 2.0 + propDelay = 0.6 + throwDuration = 1.5 + paper = globalPropPool.getProp('lawbook') + suitTrack = getSuitTrack(attack) + posPoints = [Point3(0.00, -1.00, -1.85), VBase3(180.00, -45.00, -45.00)] + paperTrack = Sequence(getPropAppearTrack(paper, suit.getRightHand(), posPoints, propDelay, Point3(2.25, 2.25, 2.25), scaleUpTime=0.5)) + paperTrack.append(Wait(suitDelay)) + hitPoint = toon.getPos(battle) + hitPoint.setX(hitPoint.getX() + 1.2) + hitPoint.setY(hitPoint.getY() + 1.5) + if dmg > 0: + hitPoint.setZ(hitPoint.getZ() + 1.1) + movePoint = Point3(hitPoint.getX(), hitPoint.getY() - 1.8, hitPoint.getZ() + 0.2) + paperTrack.append(Func(battle.movie.needRestoreRenderProp, paper)) + paperTrack.append(Func(paper.wrtReparentTo, battle)) + paperTrack.append(getThrowTrack(paper, hitPoint, duration=throwDuration, parent=battle)) + paperTrack.append(Wait(0.6)) + paperTrack.append(LerpPosInterval(paper, 0.4, movePoint)) + spinTrack = Sequence(Wait(propDelay + suitDelay + 0.2), LerpHprInterval(paper, throwDuration, Point3(-360, 0, 0))) + sizeTrack = Sequence(Wait(propDelay + suitDelay + 0.2), LerpScaleInterval(paper, throwDuration, Point3(6, 6, 6)), Wait(0.95), LerpScaleInterval(paper, 0.4, MovieUtil.PNT3_NEARZERO)) + propTrack = Sequence(Parallel(paperTrack, spinTrack, sizeTrack), Func(MovieUtil.removeProp, paper), Func(battle.movie.clearRenderProp, paper)) + damageAnims = [] + damageAnims.append(['cringe', + 0.01, + 0.21, + 0.08]) + damageAnims.append(['slip-forward', + 0.01, + 0.6, + 0.85]) + damageAnims.extend(getSplicedLerpAnims('slip-forward', 0.31, 0.95, startTime=1.2)) + damageAnims.append(['slip-forward', 0.01, 1.51]) + toonTrack = getToonTrack(attack, damageDelay=4.35, splicedDamageAnims=damageAnims, dodgeDelay=2.4, dodgeAnimNames=['sidestep'], showDamageExtraTime=0.4, showMissedExtraTime=1.3) + return Parallel(suitTrack, toonTrack, propTrack) + + +def doWithdrawal(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + BattleParticles.loadParticles() + particleEffect = BattleParticles.createParticleEffect('Withdrawal') + BattleParticles.setEffectTexture(particleEffect, 'snow-particle') + suitTrack = getSuitAnimTrack(attack) + partTrack = getPartTrack(particleEffect, 1e-05, suitTrack.getDuration() + 1.2, [particleEffect, suit, 0]) + toonTrack = getToonTrack(attack, 1.2, ['cringe'], 0.2, splicedDodgeAnims=[['duck', 1e-05, 0.8]], showMissedExtraTime=0.8) + headParts = toon.getHeadParts() + torsoParts = toon.getTorsoParts() + legsParts = toon.getLegsParts() + + def changeColor(parts): + track = Parallel() + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + track.append(Func(nextPart.setColorScale, Vec4(0, 0, 0, 1))) + + return track + + def resetColor(parts): + track = Parallel() + for partNum in xrange(0, parts.getNumPaths()): + nextPart = parts.getPath(partNum) + track.append(Func(nextPart.clearColorScale)) + + return track + + soundTrack = getSoundTrack('SA_withdrawl.ogg', delay=1.4, node=suit) + if dmg > 0: + colorTrack = Sequence() + colorTrack.append(Wait(1.6)) + colorTrack.append(Func(battle.movie.needRestoreColor)) + colorTrack.append(Parallel(changeColor(headParts), changeColor(torsoParts), changeColor(legsParts))) + colorTrack.append(Wait(2.9)) + colorTrack.append(resetColor(headParts)) + colorTrack.append(resetColor(torsoParts)) + colorTrack.append(resetColor(legsParts)) + colorTrack.append(Func(battle.movie.clearRestoreColor)) + return Parallel(suitTrack, partTrack, toonTrack, soundTrack, colorTrack) + else: + return Parallel(suitTrack, partTrack, toonTrack, soundTrack) + + +def doJargon(attack): + suit = attack['suit'] + battle = attack['battle'] + BattleParticles.loadParticles() + particleEffect = BattleParticles.createParticleEffect(file='jargonSpray') + particleEffect2 = BattleParticles.createParticleEffect(file='jargonSpray') + particleEffect3 = BattleParticles.createParticleEffect(file='jargonSpray') + particleEffect4 = BattleParticles.createParticleEffect(file='jargonSpray') + BattleParticles.setEffectTexture(particleEffect, 'jargon-brow', color=Vec4(1, 0, 0, 1)) + BattleParticles.setEffectTexture(particleEffect2, 'jargon-deep', color=Vec4(0, 0, 0, 1)) + BattleParticles.setEffectTexture(particleEffect3, 'jargon-hoop', color=Vec4(1, 0, 0, 1)) + BattleParticles.setEffectTexture(particleEffect4, 'jargon-ipo', color=Vec4(0, 0, 0, 1)) + damageDelay = 2.2 + dodgeDelay = 1.5 + partDelay = 1.1 + partInterval = 1.2 + suitTrack = getSuitTrack(attack) + partTrack = getPartTrack(particleEffect, partDelay + partInterval * 0, 2, [particleEffect, suit, 0]) + partTrack2 = getPartTrack(particleEffect2, partDelay + partInterval * 1, 2, [particleEffect2, suit, 0]) + partTrack3 = getPartTrack(particleEffect3, partDelay + partInterval * 2, 2, [particleEffect3, suit, 0]) + partTrack4 = getPartTrack(particleEffect4, partDelay + partInterval * 3, 1.0, [particleEffect4, suit, 0]) + damageAnims = [] + damageAnims.append(['conked', + 0.0001, + 0, + 0.4]) + damageAnims.append(['conked', + 0.0001, + 2.7, + 0.85]) + damageAnims.append(['conked', + 0.0001, + 0.4, + 0.09]) + damageAnims.append(['conked', + 0.0001, + 0.4, + 0.09]) + damageAnims.append(['conked', + 0.0001, + 0.4, + 0.66]) + damageAnims.append(['conked', + 0.0001, + 0.4, + 0.09]) + damageAnims.append(['conked', + 0.0001, + 0.4, + 0.09]) + damageAnims.append(['conked', + 0.0001, + 0.4, + 0.86]) + damageAnims.append(['conked', + 0.0001, + 0.4, + 0.14]) + damageAnims.append(['conked', + 0.0001, + 0.4, + 0.14]) + damageAnims.append(['conked', 0.0001, 0.4]) + dodgeAnims = [['duck', 0.0001, 1.2], ['duck', 0.0001, 1.3]] + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, splicedDodgeAnims=dodgeAnims, showMissedExtraTime=1.6, showDamageExtraTime=0.7) + soundTrack = getSoundTrack('SA_jargon.ogg', delay=2.1, node=suit) + return Parallel(suitTrack, toonTrack, soundTrack, partTrack, partTrack2, partTrack3, partTrack4) + + +def doMumboJumbo(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + BattleParticles.loadParticles() + particleEffect = BattleParticles.createParticleEffect(file='mumboJumboSpray') + particleEffect2 = BattleParticles.createParticleEffect(file='mumboJumboSpray') + particleEffect3 = BattleParticles.createParticleEffect(file='mumboJumboSmother') + particleEffect4 = BattleParticles.createParticleEffect(file='mumboJumboSmother') + particleEffect5 = BattleParticles.createParticleEffect(file='mumboJumboSmother') + BattleParticles.setEffectTexture(particleEffect, 'mumbojumbo-boiler', color=Vec4(1, 0, 0, 1)) + BattleParticles.setEffectTexture(particleEffect2, 'mumbojumbo-creative', color=Vec4(1, 0, 0, 1)) + BattleParticles.setEffectTexture(particleEffect3, 'mumbojumbo-deben', color=Vec4(1, 0, 0, 1)) + BattleParticles.setEffectTexture(particleEffect4, 'mumbojumbo-high', color=Vec4(1, 0, 0, 1)) + BattleParticles.setEffectTexture(particleEffect5, 'mumbojumbo-iron', color=Vec4(1, 0, 0, 1)) + suitTrack = getSuitTrack(attack) + partTrack = getPartTrack(particleEffect, 2.5, 2, [particleEffect, suit, 0]) + partTrack2 = getPartTrack(particleEffect2, 2.5, 2, [particleEffect2, suit, 0]) + partTrack3 = getPartTrack(particleEffect3, 3.3, 1.7, [particleEffect3, toon, 0]) + partTrack4 = getPartTrack(particleEffect4, 3.3, 1.7, [particleEffect4, toon, 0]) + partTrack5 = getPartTrack(particleEffect5, 3.3, 1.7, [particleEffect5, toon, 0]) + toonTrack = getToonTrack(attack, 3.2, ['cringe'], 2.2, ['sidestep']) + soundTrack = getSoundTrack('SA_mumbo_jumbo.ogg', delay=2.5, node=suit) + if dmg > 0: + return Parallel(suitTrack, toonTrack, soundTrack, partTrack, partTrack2, partTrack3, partTrack4, partTrack5) + else: + return Parallel(suitTrack, toonTrack, soundTrack, partTrack, partTrack2) + + +def doGuiltTrip(attack): + suit = attack['suit'] + battle = attack['battle'] + centerColor = Vec4(1.0, 0.2, 0.2, 0.9) + edgeColor = Vec4(0.9, 0.9, 0.9, 0.4) + powerBar1 = BattleParticles.createParticleEffect(file='guiltTrip') + powerBar2 = BattleParticles.createParticleEffect(file='guiltTrip') + powerBar1.setPos(0, 6.1, 0.4) + powerBar1.setHpr(-90, 0, 0) + powerBar2.setPos(0, 6.1, 0.4) + powerBar2.setHpr(90, 0, 0) + powerBar1.setScale(5) + powerBar2.setScale(5) + powerBar1Particles = powerBar1.getParticlesNamed('particles-1') + powerBar2Particles = powerBar2.getParticlesNamed('particles-1') + powerBar1Particles.renderer.setCenterColor(centerColor) + powerBar1Particles.renderer.setEdgeColor(edgeColor) + powerBar2Particles.renderer.setCenterColor(centerColor) + powerBar2Particles.renderer.setEdgeColor(edgeColor) + waterfallEffect = BattleParticles.createParticleEffect('Waterfall') + waterfallEffect.setScale(11) + waterfallParticles = waterfallEffect.getParticlesNamed('particles-1') + waterfallParticles.renderer.setCenterColor(centerColor) + waterfallParticles.renderer.setEdgeColor(edgeColor) + suitTrack = getSuitAnimTrack(attack) + + def getPowerTrack(effect, suit = suit, battle = battle): + partTrack = Sequence(Wait(0.7), Func(battle.movie.needRestoreParticleEffect, effect), Func(effect.start, suit), Wait(0.4), LerpPosInterval(effect, 1.0, Point3(0, 15, 0.4)), LerpFunctionInterval(effect.setAlphaScale, fromData=1, toData=0, duration=0.4), Func(effect.cleanup), Func(battle.movie.clearRestoreParticleEffect, effect)) + return partTrack + + partTrack1 = getPowerTrack(powerBar1) + partTrack2 = getPowerTrack(powerBar2) + waterfallTrack = getPartTrack(waterfallEffect, 0.6, 0.6, [waterfallEffect, suit, 0]) + toonTracks = getToonTracks(attack, 1.5, ['slip-forward'], 0.86, ['jump']) + soundTrack = getSoundTrack('SA_guilt_trip.ogg', delay=1.1, node=suit) + return Parallel(suitTrack, partTrack1, partTrack2, soundTrack, waterfallTrack, toonTracks) + + +def doRestrainingOrder(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + paper = globalPropPool.getProp('shredder-paper') + suitTrack = getSuitTrack(attack) + posPoints = [Point3(-0.04, 0.15, -1.38), VBase3(10.584, -11.945, 18.316)] + propTrack = Sequence(getPropAppearTrack(paper, suit.getRightHand(), posPoints, 0.8, MovieUtil.PNT3_ONE, scaleUpTime=0.5)) + propTrack.append(Wait(1.73)) + hitPoint = __toonFacePoint(toon, parent=battle) + hitPoint.setX(hitPoint.getX() - 1.4) + missPoint = __toonGroundPoint(attack, toon, 0.7, parent=battle) + missPoint.setX(missPoint.getX() - 1.1) + propTrack.append(getPropThrowTrack(attack, paper, [hitPoint], [missPoint], parent=battle)) + damageAnims = [['conked', + 0.01, + 0.3, + 0.2], ['struggle', 0.01, 0.2]] + toonTrack = getToonTrack(attack, damageDelay=3.4, splicedDamageAnims=damageAnims, dodgeDelay=2.8, dodgeAnimNames=['sidestep']) + if dmg > 0: + restraintCloud = BattleParticles.createParticleEffect(file='restrainingOrderCloud') + restraintCloud.setPos(hitPoint.getX(), hitPoint.getY() + 0.5, hitPoint.getZ()) + cloudTrack = getPartTrack(restraintCloud, 3.5, 0.2, [restraintCloud, battle, 0]) + return Parallel(suitTrack, cloudTrack, toonTrack, propTrack) + else: + return Parallel(suitTrack, toonTrack, propTrack) + + +def doSpin(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + damageDelay = 1.7 + sprayEffect = BattleParticles.createParticleEffect(file='spinSpray') + spinEffect1 = BattleParticles.createParticleEffect(file='spinEffect') + spinEffect2 = BattleParticles.createParticleEffect(file='spinEffect') + spinEffect3 = BattleParticles.createParticleEffect(file='spinEffect') + spinEffect1.reparentTo(toon) + spinEffect2.reparentTo(toon) + spinEffect3.reparentTo(toon) + height1 = toon.getHeight() * (random.random() * 0.2 + 0.7) + height2 = toon.getHeight() * (random.random() * 0.2 + 0.4) + height3 = toon.getHeight() * (random.random() * 0.2 + 0.1) + spinEffect1.setPos(0.8, -0.7, height1) + spinEffect1.setHpr(0, 0, -random.random() * 10 - 85) + spinEffect1.setHpr(spinEffect1, 0, 50, 0) + spinEffect2.setPos(0.8, -0.7, height2) + spinEffect2.setHpr(0, 0, -random.random() * 10 - 85) + spinEffect2.setHpr(spinEffect2, 0, 50, 0) + spinEffect3.setPos(0.8, -0.7, height3) + spinEffect3.setHpr(0, 0, -random.random() * 10 - 85) + spinEffect3.setHpr(spinEffect3, 0, 50, 0) + spinEffect1.wrtReparentTo(battle) + spinEffect2.wrtReparentTo(battle) + spinEffect3.wrtReparentTo(battle) + suitTrack = getSuitTrack(attack) + sprayTrack = getPartTrack(sprayEffect, 1.0, 1.9, [sprayEffect, suit, 0]) + spinTrack1 = getPartTrack(spinEffect1, 2.1, 3.9, [spinEffect1, battle, 0]) + spinTrack2 = getPartTrack(spinEffect2, 2.1, 3.9, [spinEffect2, battle, 0]) + spinTrack3 = getPartTrack(spinEffect3, 2.1, 3.9, [spinEffect3, battle, 0]) + damageAnims = [] + damageAnims.append(['duck', + 0.01, + 0.01, + 1.1]) + damageAnims.extend(getSplicedLerpAnims('think', 0.66, 1.1, startTime=2.26)) + damageAnims.extend(getSplicedLerpAnims('think', 0.66, 1.1, startTime=2.26)) + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=0.91, dodgeAnimNames=['sidestep'], showDamageExtraTime=2.1, showMissedExtraTime=1.0) + if dmg > 0: + toonSpinTrack = Sequence(Wait(damageDelay + 0.9), LerpHprInterval(toon, 0.7, Point3(-10, 0, 0)), LerpHprInterval(toon, 0.5, Point3(-30, 0, 0)), LerpHprInterval(toon, 0.2, Point3(-60, 0, 0)), LerpHprInterval(toon, 0.7, Point3(-700, 0, 0)), LerpHprInterval(toon, 1.0, Point3(-1310, 0, 0)), LerpHprInterval(toon, 0.4, toon.getHpr()), Wait(0.5)) + return Parallel(suitTrack, sprayTrack, toonTrack, toonSpinTrack, spinTrack1, spinTrack2, spinTrack3) + else: + return Parallel(suitTrack, sprayTrack, toonTrack) + + +def doLegalese(attack): + suit = attack['suit'] + BattleParticles.loadParticles() + sprayEffect1 = BattleParticles.createParticleEffect(file='legaleseSpray') + sprayEffect2 = BattleParticles.createParticleEffect(file='legaleseSpray') + sprayEffect3 = BattleParticles.createParticleEffect(file='legaleseSpray') + color = Vec4(0.4, 0, 0, 1) + BattleParticles.setEffectTexture(sprayEffect1, 'legalese-hc', color=color) + BattleParticles.setEffectTexture(sprayEffect2, 'legalese-qpq', color=color) + BattleParticles.setEffectTexture(sprayEffect3, 'legalese-vd', color=color) + partDelay = 1.3 + partDuration = 1.15 + damageDelay = 1.9 + dodgeDelay = 1.1 + suitTrack = getSuitTrack(attack) + sprayTrack1 = getPartTrack(sprayEffect1, partDelay, partDuration, [sprayEffect1, suit, 0]) + sprayTrack2 = getPartTrack(sprayEffect2, partDelay + 0.8, partDuration, [sprayEffect2, suit, 0]) + sprayTrack3 = getPartTrack(sprayEffect3, partDelay + 1.6, partDuration, [sprayEffect3, suit, 0]) + damageAnims = [] + damageAnims.append(['cringe', + 1e-05, + 0.3, + 0.8]) + damageAnims.append(['cringe', + 1e-05, + 0.3, + 0.8]) + damageAnims.append(['cringe', 1e-05, 0.3]) + toonTrack = getToonTrack(attack, damageDelay=damageDelay, splicedDamageAnims=damageAnims, dodgeDelay=dodgeDelay, dodgeAnimNames=['sidestep'], showMissedExtraTime=0.8) + return Parallel(suitTrack, toonTrack, sprayTrack1, sprayTrack2, sprayTrack3) + + +def doPeckingOrder(attack): + suit = attack['suit'] + battle = attack['battle'] + target = attack['target'] + toon = target['toon'] + dmg = target['hp'] + throwDuration = 3.03 + throwDelay = 3.2 + suitTrack = getSuitTrack(attack) + numBirds = random.randint(4, 7) + birdTracks = Parallel() + propDelay = 1.5 + for i in xrange(0, numBirds): + next = globalPropPool.getProp('bird') + next.setScale(0.01) + next.reparentTo(suit.getRightHand()) + next.setPos(random.random() * 0.6 - 0.3, random.random() * 0.6 - 0.3, random.random() * 0.6 - 0.3) + if dmg > 0: + hitPoint = Point3(random.random() * 5 - 2.5, random.random() * 2 - 1 - 6, random.random() * 3 - 1.5 + toon.getHeight() - 0.9) + else: + hitPoint = Point3(random.random() * 2 - 1, random.random() * 4 - 2 - 15, random.random() * 4 - 2 + 2.2) + birdTrack = Sequence(Wait(throwDelay), Func(battle.movie.needRestoreRenderProp, next), Func(next.wrtReparentTo, battle), Func(next.setHpr, Point3(90, 20, 0)), LerpPosInterval(next, 1.1, hitPoint)) + scaleTrack = Sequence(Wait(throwDelay), LerpScaleInterval(next, 0.15, Point3(9, 9, 9))) + birdTracks.append(Sequence(Parallel(birdTrack, scaleTrack), Func(MovieUtil.removeProp, next))) + + damageAnims = [] + damageAnims.append(['cringe', + 0.01, + 0.14, + 0.21]) + damageAnims.append(['cringe', + 0.01, + 0.14, + 0.13]) + damageAnims.append(['cringe', 0.01, 0.43]) + toonTrack = getToonTrack(attack, damageDelay=4.2, splicedDamageAnims=damageAnims, dodgeDelay=2.8, dodgeAnimNames=['sidestep'], showMissedExtraTime=1.1) + return Parallel(suitTrack, toonTrack, birdTracks) diff --git a/toontown/battle/MovieThrow.py b/toontown/battle/MovieThrow.py new file mode 100755 index 00000000..9a60b549 --- /dev/null +++ b/toontown/battle/MovieThrow.py @@ -0,0 +1,594 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from BattleBase import * +from BattleProps import * +from BattleSounds import * +from toontown.toon.ToonDNA import * +from toontown.suit.SuitDNA import * +from direct.directnotify import DirectNotifyGlobal +import random +import MovieCamera +import MovieUtil +from MovieUtil import calcAvgSuitPos +notify = DirectNotifyGlobal.directNotify.newCategory('MovieThrow') +hitSoundFiles = ('AA_tart_only.ogg', 'AA_slice_only.ogg', 'AA_slice_only.ogg', 'AA_slice_only.ogg', 'AA_slice_only.ogg', 'AA_wholepie_only.ogg', 'AA_wholepie_only.ogg') +tPieLeavesHand = 2.7 +tPieHitsSuit = 3.0 +tSuitDodges = 2.45 +ratioMissToHit = 1.5 +tPieShrink = 0.7 +pieFlyTaskName = 'MovieThrow-pieFly' + +def addHit(dict, suitId, hitCount): + if suitId in dict: + dict[suitId] += hitCount + else: + dict[suitId] = hitCount + + +def doThrows(throws): + if len(throws) == 0: + return (None, None) + suitThrowsDict = {} + for throw in throws: + if attackAffectsGroup(throw['track'], throw['level']): + pass + else: + suitId = throw['target']['suit'].doId + if suitId in suitThrowsDict: + suitThrowsDict[suitId].append(throw) + else: + suitThrowsDict[suitId] = [throw] + + suitThrows = suitThrowsDict.values() + + def compFunc(a, b): + if len(a) > len(b): + return 1 + elif len(a) < len(b): + return -1 + return 0 + + suitThrows.sort(compFunc) + totalHitDict = {} + singleHitDict = {} + groupHitDict = {} + for throw in throws: + if attackAffectsGroup(throw['track'], throw['level']): + for i in xrange(len(throw['target'])): + target = throw['target'][i] + suitId = target['suit'].doId + if target['hp'] > 0: + addHit(groupHitDict, suitId, 1) + addHit(totalHitDict, suitId, 1) + else: + addHit(groupHitDict, suitId, 0) + addHit(totalHitDict, suitId, 0) + + else: + suitId = throw['target']['suit'].doId + if throw['target']['hp'] > 0: + addHit(singleHitDict, suitId, 1) + addHit(totalHitDict, suitId, 1) + else: + addHit(singleHitDict, suitId, 0) + addHit(totalHitDict, suitId, 0) + + notify.debug('singleHitDict = %s' % singleHitDict) + notify.debug('groupHitDict = %s' % groupHitDict) + notify.debug('totalHitDict = %s' % totalHitDict) + delay = 0.0 + mtrack = Parallel() + for st in suitThrows: + if len(st) > 0: + ival = __doSuitThrows(st) + if ival: + mtrack.append(Sequence(Wait(delay), ival)) + delay = delay + TOON_THROW_SUIT_DELAY + + retTrack = Sequence() + retTrack.append(mtrack) + groupThrowIvals = Parallel() + groupThrows = [] + for throw in throws: + if attackAffectsGroup(throw['track'], throw['level']): + groupThrows.append(throw) + + for throw in groupThrows: + tracks = None + tracks = __throwGroupPie(throw, 0, groupHitDict) + if tracks: + for track in tracks: + groupThrowIvals.append(track) + + retTrack.append(groupThrowIvals) + camDuration = retTrack.getDuration() + camTrack = MovieCamera.chooseThrowShot(throws, suitThrowsDict, camDuration) + return (retTrack, camTrack) + + +def __doSuitThrows(throws): + toonTracks = Parallel() + delay = 0.0 + hitCount = 0 + for throw in throws: + if throw['target']['hp'] > 0: + hitCount += 1 + else: + break + + for throw in throws: + tracks = __throwPie(throw, delay, hitCount) + if tracks: + for track in tracks: + toonTracks.append(track) + + delay = delay + TOON_THROW_DELAY + + return toonTracks + + +def __showProp(prop, parent, pos): + prop.reparentTo(parent) + prop.setPos(pos) + + +def __animProp(props, propName, propType): + if 'actor' == propType: + for prop in props: + prop.play(propName) + + elif 'model' == propType: + pass + else: + notify.error('No such propType as: %s' % propType) + + +def __billboardProp(prop): + scale = prop.getScale() + prop.setBillboardPointWorld() + prop.setScale(scale) + + +def __suitMissPoint(suit, other = render): + pnt = suit.getPos(other) + pnt.setZ(pnt[2] + suit.getHeight() * 1.3) + return pnt + + +def __propPreflight(props, suit, toon, battle): + prop = props[0] + toon.update(0) + prop.wrtReparentTo(battle) + props[1].reparentTo(hidden) + for ci in xrange(prop.getNumChildren()): + prop.getChild(ci).setHpr(0, -90, 0) + + targetPnt = MovieUtil.avatarFacePoint(suit, other=battle) + prop.lookAt(targetPnt) + + +def __propPreflightGroup(props, suits, toon, battle): + prop = props[0] + toon.update(0) + prop.wrtReparentTo(battle) + props[1].reparentTo(hidden) + for ci in xrange(prop.getNumChildren()): + prop.getChild(ci).setHpr(0, -90, 0) + + avgTargetPt = Point3(0, 0, 0) + for suit in suits: + avgTargetPt += MovieUtil.avatarFacePoint(suit, other=battle) + + avgTargetPt /= len(suits) + prop.lookAt(avgTargetPt) + + +def __piePreMiss(missDict, pie, suitPoint, other = render): + missDict['pie'] = pie + missDict['startScale'] = pie.getScale() + missDict['startPos'] = pie.getPos(other) + v = Vec3(suitPoint - missDict['startPos']) + endPos = missDict['startPos'] + v * ratioMissToHit + missDict['endPos'] = endPos + + +def __pieMissLerpCallback(t, missDict): + pie = missDict['pie'] + newPos = missDict['startPos'] * (1.0 - t) + missDict['endPos'] * t + if t < tPieShrink: + tScale = 0.0001 + else: + tScale = (t - tPieShrink) / (1.0 - tPieShrink) + newScale = missDict['startScale'] * max(1.0 - tScale, 0.01) + pie.setPos(newPos) + pie.setScale(newScale) + + +def __piePreMissGroup(missDict, pies, suitPoint, other = render): + missDict['pies'] = pies + missDict['startScale'] = pies[0].getScale() + missDict['startPos'] = pies[0].getPos(other) + v = Vec3(suitPoint - missDict['startPos']) + endPos = missDict['startPos'] + v * ratioMissToHit + missDict['endPos'] = endPos + notify.debug('startPos=%s' % missDict['startPos']) + notify.debug('v=%s' % v) + notify.debug('endPos=%s' % missDict['endPos']) + + +def __pieMissGroupLerpCallback(t, missDict): + pies = missDict['pies'] + newPos = missDict['startPos'] * (1.0 - t) + missDict['endPos'] * t + if t < tPieShrink: + tScale = 0.0001 + else: + tScale = (t - tPieShrink) / (1.0 - tPieShrink) + newScale = missDict['startScale'] * max(1.0 - tScale, 0.01) + for pie in pies: + pie.setPos(newPos) + pie.setScale(newScale) + + +def __getWeddingCakeSoundTrack(level, hitSuit, node = None): + throwTrack = Sequence() + if hitSuit: + throwSound = globalBattleSoundCache.getSound('AA_throw_wedding_cake.ogg') + songTrack = Sequence() + songTrack.append(Wait(1.0)) + songTrack.append(SoundInterval(throwSound, node=node)) + splatSound = globalBattleSoundCache.getSound('AA_throw_wedding_cake_cog.ogg') + splatTrack = Sequence() + splatTrack.append(Wait(tPieHitsSuit)) + splatTrack.append(SoundInterval(splatSound, node=node)) + throwTrack.append(Parallel(songTrack, splatTrack)) + else: + throwSound = globalBattleSoundCache.getSound('AA_throw_wedding_cake_miss.ogg') + throwTrack.append(Wait(tSuitDodges)) + throwTrack.append(SoundInterval(throwSound, node=node)) + return throwTrack + + +def __getSoundTrack(level, hitSuit, node = None): + if level == UBER_GAG_LEVEL_INDEX: + return __getWeddingCakeSoundTrack(level, hitSuit, node) + throwSound = globalBattleSoundCache.getSound('AA_pie_throw_only.ogg') + throwTrack = Sequence(Wait(2.6), SoundInterval(throwSound, node=node)) + if hitSuit: + hitSound = globalBattleSoundCache.getSound(hitSoundFiles[level]) + hitTrack = Sequence(Wait(tPieLeavesHand), SoundInterval(hitSound, node=node)) + return Parallel(throwTrack, hitTrack) + else: + return throwTrack + + +def __throwPie(throw, delay, hitCount): + toon = throw['toon'] + hpbonus = throw['hpbonus'] + target = throw['target'] + suit = target['suit'] + hp = target['hp'] + kbbonus = target['kbbonus'] + sidestep = throw['sidestep'] + died = target['died'] + revived = target['revived'] + leftSuits = target['leftSuits'] + rightSuits = target['rightSuits'] + level = throw['level'] + battle = throw['battle'] + suitPos = suit.getPos(battle) + origHpr = toon.getHpr(battle) + notify.debug('toon: %s throws tart at suit: %d for hp: %d died: %d' % (toon.getName(), + suit.doId, + hp, + died)) + pieName = pieNames[level] + hitSuit = hp > 0 + pie = globalPropPool.getProp(pieName) + pieType = globalPropPool.getPropType(pieName) + pie2 = MovieUtil.copyProp(pie) + pies = [pie, pie2] + hands = toon.getRightHands() + splatName = 'splat-' + pieName + splat = globalPropPool.getProp(splatName) + splatType = globalPropPool.getPropType(splatName) + toonTrack = Sequence() + toonFace = Func(toon.headsUp, battle, suitPos) + toonTrack.append(Wait(delay)) + toonTrack.append(toonFace) + toonTrack.append(ActorInterval(toon, 'throw')) + toonTrack.append(Func(toon.loop, 'neutral')) + toonTrack.append(Func(toon.setHpr, battle, origHpr)) + pieShow = Func(MovieUtil.showProps, pies, hands) + pieAnim = Func(__animProp, pies, pieName, pieType) + pieScale1 = LerpScaleInterval(pie, 1.0, pie.getScale(), startScale=MovieUtil.PNT3_NEARZERO) + pieScale2 = LerpScaleInterval(pie2, 1.0, pie2.getScale(), startScale=MovieUtil.PNT3_NEARZERO) + pieScale = Parallel(pieScale1, pieScale2) + piePreflight = Func(__propPreflight, pies, suit, toon, battle) + pieTrack = Sequence(Wait(delay), pieShow, pieAnim, pieScale, Func(battle.movie.needRestoreRenderProp, pies[0]), Wait(tPieLeavesHand - 1.0), piePreflight) + soundTrack = __getSoundTrack(level, hitSuit, toon) + if hitSuit: + pieFly = LerpPosInterval(pie, tPieHitsSuit - tPieLeavesHand, pos=MovieUtil.avatarFacePoint(suit, other=battle), name=pieFlyTaskName, other=battle) + pieHide = Func(MovieUtil.removeProps, pies) + splatShow = Func(__showProp, splat, suit, Point3(0, 0, suit.getHeight())) + splatBillboard = Func(__billboardProp, splat) + splatAnim = ActorInterval(splat, splatName) + splatHide = Func(MovieUtil.removeProp, splat) + pieTrack.append(pieFly) + pieTrack.append(pieHide) + pieTrack.append(Func(battle.movie.clearRenderProp, pies[0])) + pieTrack.append(splatShow) + pieTrack.append(splatBillboard) + pieTrack.append(splatAnim) + pieTrack.append(splatHide) + else: + missDict = {} + if sidestep: + suitPoint = MovieUtil.avatarFacePoint(suit, other=battle) + else: + suitPoint = __suitMissPoint(suit, other=battle) + piePreMiss = Func(__piePreMiss, missDict, pie, suitPoint, battle) + pieMiss = LerpFunctionInterval(__pieMissLerpCallback, extraArgs=[missDict], duration=(tPieHitsSuit - tPieLeavesHand) * ratioMissToHit) + pieHide = Func(MovieUtil.removeProps, pies) + pieTrack.append(piePreMiss) + pieTrack.append(pieMiss) + pieTrack.append(pieHide) + pieTrack.append(Func(battle.movie.clearRenderProp, pies[0])) + if hitSuit: + suitResponseTrack = Sequence() + showDamage = Func(suit.showHpText, -hp, openEnded=0, attackTrack=THROW_TRACK) + updateHealthBar = Func(suit.updateHealthBar, hp) + sival = [] + if kbbonus > 0: + suitPos, suitHpr = battle.getActorPosHpr(suit) + suitType = getSuitBodyType(suit.getStyleName()) + animTrack = Sequence() + animTrack.append(ActorInterval(suit, 'pie-small-react', duration=0.2)) + if suitType == 'a': + animTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.43)) + elif suitType == 'b': + animTrack.append(ActorInterval(suit, 'slip-forward', startTime=1.94)) + elif suitType == 'c': + animTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.58)) + animTrack.append(Func(battle.unlureSuit, suit)) + moveTrack = Sequence(Wait(0.2), LerpPosInterval(suit, 0.6, pos=suitPos, other=battle)) + sival = Parallel(animTrack, moveTrack) + elif hitCount == 1: + sival = Parallel(ActorInterval(suit, 'pie-small-react'), MovieUtil.createSuitStunInterval(suit, 0.3, 1.3)) + else: + sival = ActorInterval(suit, 'pie-small-react') + suitResponseTrack.append(Wait(delay + tPieHitsSuit)) + suitResponseTrack.append(showDamage) + suitResponseTrack.append(updateHealthBar) + suitResponseTrack.append(sival) + bonusTrack = Sequence(Wait(delay + tPieHitsSuit)) + if kbbonus > 0: + bonusTrack.append(Wait(0.75)) + bonusTrack.append(Func(suit.showHpText, -kbbonus, 2, openEnded=0, attackTrack=THROW_TRACK)) + bonusTrack.append(Func(suit.updateHealthBar, kbbonus)) + if hpbonus > 0: + bonusTrack.append(Wait(0.75)) + bonusTrack.append(Func(suit.showHpText, -hpbonus, 1, openEnded=0, attackTrack=THROW_TRACK)) + bonusTrack.append(Func(suit.updateHealthBar, hpbonus)) + if revived != 0: + suitResponseTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle)) + elif died != 0: + suitResponseTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle)) + else: + suitResponseTrack.append(Func(suit.loop, 'neutral')) + suitResponseTrack = Parallel(suitResponseTrack, bonusTrack) + else: + suitResponseTrack = MovieUtil.createSuitDodgeMultitrack(delay + tSuitDodges, suit, leftSuits, rightSuits) + if not hitSuit and delay > 0: + return [toonTrack, soundTrack, pieTrack] + else: + return [toonTrack, + soundTrack, + pieTrack, + suitResponseTrack] + + +def __createWeddingCakeFlight(throw, groupHitDict, pie, pies): + toon = throw['toon'] + battle = throw['battle'] + level = throw['level'] + sidestep = throw['sidestep'] + hpbonus = throw['hpbonus'] + numTargets = len(throw['target']) + pieName = pieNames[level] + splatName = 'splat-' + pieName + splat = globalPropPool.getProp(splatName) + splats = [splat] + for i in xrange(numTargets - 1): + splats.append(MovieUtil.copyProp(splat)) + + splatType = globalPropPool.getPropType(splatName) + cakePartStrs = ['cake1', + 'cake2', + 'cake3', + 'caketop'] + cakeParts = [] + for part in cakePartStrs: + cakeParts.append(pie.find('**/%s' % part)) + + cakePartDivisions = {} + cakePartDivisions[1] = [[cakeParts[0], + cakeParts[1], + cakeParts[2], + cakeParts[3]]] + cakePartDivisions[2] = [[cakeParts[0], cakeParts[1]], [cakeParts[2], cakeParts[3]]] + cakePartDivisions[3] = [[cakeParts[0], cakeParts[1]], [cakeParts[2]], [cakeParts[3]]] + cakePartDivisions[4] = [[cakeParts[0]], + [cakeParts[1]], + [cakeParts[2]], + [cakeParts[3]]] + cakePartDivToUse = cakePartDivisions[len(throw['target'])] + groupPieTracks = Parallel() + for i in xrange(numTargets): + target = throw['target'][i] + suit = target['suit'] + hitSuit = target['hp'] > 0 + singlePieTrack = Sequence() + if hitSuit: + piePartReparent = Func(reparentCakePart, pie, cakePartDivToUse[i]) + singlePieTrack.append(piePartReparent) + cakePartTrack = Parallel() + for cakePart in cakePartDivToUse[i]: + pieFly = LerpPosInterval(cakePart, tPieHitsSuit - tPieLeavesHand, pos=MovieUtil.avatarFacePoint(suit, other=battle), name=pieFlyTaskName, other=battle) + cakePartTrack.append(pieFly) + + singlePieTrack.append(cakePartTrack) + pieRemoveCakeParts = Func(MovieUtil.removeProps, cakePartDivToUse[i]) + pieHide = Func(MovieUtil.removeProps, pies) + splatShow = Func(__showProp, splats[i], suit, Point3(0, 0, suit.getHeight())) + splatBillboard = Func(__billboardProp, splats[i]) + splatAnim = ActorInterval(splats[i], splatName) + splatHide = Func(MovieUtil.removeProp, splats[i]) + singlePieTrack.append(pieRemoveCakeParts) + singlePieTrack.append(pieHide) + singlePieTrack.append(Func(battle.movie.clearRenderProp, pies[0])) + singlePieTrack.append(splatShow) + singlePieTrack.append(splatBillboard) + singlePieTrack.append(splatAnim) + singlePieTrack.append(splatHide) + else: + missDict = {} + if sidestep: + suitPoint = MovieUtil.avatarFacePoint(suit, other=battle) + else: + suitPoint = __suitMissPoint(suit, other=battle) + piePartReparent = Func(reparentCakePart, pie, cakePartDivToUse[i]) + piePreMiss = Func(__piePreMissGroup, missDict, cakePartDivToUse[i], suitPoint, battle) + pieMiss = LerpFunctionInterval(__pieMissGroupLerpCallback, extraArgs=[missDict], duration=(tPieHitsSuit - tPieLeavesHand) * ratioMissToHit) + pieHide = Func(MovieUtil.removeProps, pies) + pieRemoveCakeParts = Func(MovieUtil.removeProps, cakePartDivToUse[i]) + singlePieTrack.append(piePartReparent) + singlePieTrack.append(piePreMiss) + singlePieTrack.append(pieMiss) + singlePieTrack.append(pieRemoveCakeParts) + singlePieTrack.append(pieHide) + singlePieTrack.append(Func(battle.movie.clearRenderProp, pies[0])) + groupPieTracks.append(singlePieTrack) + + return groupPieTracks + + +def __throwGroupPie(throw, delay, groupHitDict): + toon = throw['toon'] + battle = throw['battle'] + level = throw['level'] + sidestep = throw['sidestep'] + hpbonus = throw['hpbonus'] + numTargets = len(throw['target']) + avgSuitPos = calcAvgSuitPos(throw) + origHpr = toon.getHpr(battle) + toonTrack = Sequence() + toonFace = Func(toon.headsUp, battle, avgSuitPos) + toonTrack.append(Wait(delay)) + toonTrack.append(toonFace) + toonTrack.append(ActorInterval(toon, 'throw')) + toonTrack.append(Func(toon.loop, 'neutral')) + toonTrack.append(Func(toon.setHpr, battle, origHpr)) + suits = [] + for i in xrange(numTargets): + suits.append(throw['target'][i]['suit']) + + pieName = pieNames[level] + pie = globalPropPool.getProp(pieName) + pieType = globalPropPool.getPropType(pieName) + pie2 = MovieUtil.copyProp(pie) + pies = [pie, pie2] + hands = toon.getRightHands() + pieShow = Func(MovieUtil.showProps, pies, hands) + pieAnim = Func(__animProp, pies, pieName, pieType) + pieScale1 = LerpScaleInterval(pie, 1.0, pie.getScale() * 1.5, startScale=MovieUtil.PNT3_NEARZERO) + pieScale2 = LerpScaleInterval(pie2, 1.0, pie2.getScale() * 1.5, startScale=MovieUtil.PNT3_NEARZERO) + pieScale = Parallel(pieScale1, pieScale2) + piePreflight = Func(__propPreflightGroup, pies, suits, toon, battle) + pieTrack = Sequence(Wait(delay), pieShow, pieAnim, pieScale, Func(battle.movie.needRestoreRenderProp, pies[0]), Wait(tPieLeavesHand - 1.0), piePreflight) + if level == UBER_GAG_LEVEL_INDEX: + groupPieTracks = __createWeddingCakeFlight(throw, groupHitDict, pie, pies) + else: + notify.error('unhandled throw level %d' % level) + pieTrack.append(groupPieTracks) + didThrowHitAnyone = False + for i in xrange(numTargets): + target = throw['target'][i] + hitSuit = target['hp'] > 0 + if hitSuit: + didThrowHitAnyone = True + + soundTrack = __getSoundTrack(level, didThrowHitAnyone, toon) + groupSuitResponseTrack = Parallel() + for i in xrange(numTargets): + target = throw['target'][i] + suit = target['suit'] + hitSuit = target['hp'] > 0 + leftSuits = target['leftSuits'] + rightSuits = target['rightSuits'] + hp = target['hp'] + kbbonus = target['kbbonus'] + died = target['died'] + revived = target['revived'] + if hitSuit: + singleSuitResponseTrack = Sequence() + showDamage = Func(suit.showHpText, -hp, openEnded=0, attackTrack=THROW_TRACK) + updateHealthBar = Func(suit.updateHealthBar, hp) + sival = [] + if kbbonus > 0: + suitPos, suitHpr = battle.getActorPosHpr(suit) + suitType = getSuitBodyType(suit.getStyleName()) + animTrack = Sequence() + animTrack.append(ActorInterval(suit, 'pie-small-react', duration=0.2)) + if suitType == 'a': + animTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.43)) + elif suitType == 'b': + animTrack.append(ActorInterval(suit, 'slip-forward', startTime=1.94)) + elif suitType == 'c': + animTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.58)) + animTrack.append(Func(battle.unlureSuit, suit)) + moveTrack = Sequence(Wait(0.2), LerpPosInterval(suit, 0.6, pos=suitPos, other=battle)) + sival = Parallel(animTrack, moveTrack) + elif groupHitDict[suit.doId] == 1: + sival = Parallel(ActorInterval(suit, 'pie-small-react'), MovieUtil.createSuitStunInterval(suit, 0.3, 1.3)) + else: + sival = ActorInterval(suit, 'pie-small-react') + singleSuitResponseTrack.append(Wait(delay + tPieHitsSuit)) + singleSuitResponseTrack.append(showDamage) + singleSuitResponseTrack.append(updateHealthBar) + singleSuitResponseTrack.append(sival) + bonusTrack = Sequence(Wait(delay + tPieHitsSuit)) + if kbbonus > 0: + bonusTrack.append(Wait(0.75)) + bonusTrack.append(Func(suit.showHpText, -kbbonus, 2, openEnded=0, attackTrack=THROW_TRACK)) + bonusTrack.append(Func(suit.updateHealthBar, kbbonus)) + if hpbonus > 0: + bonusTrack.append(Wait(0.75)) + bonusTrack.append(Func(suit.showHpText, -hpbonus, 1, openEnded=0, attackTrack=THROW_TRACK)) + bonusTrack.append(Func(suit.updateHealthBar, hpbonus)) + if revived != 0: + singleSuitResponseTrack.append(MovieUtil.createSuitReviveTrack(suit, toon, battle)) + elif died != 0: + singleSuitResponseTrack.append(MovieUtil.createSuitDeathTrack(suit, toon, battle)) + else: + singleSuitResponseTrack.append(Func(suit.loop, 'neutral')) + singleSuitResponseTrack = Parallel(singleSuitResponseTrack, bonusTrack) + else: + groupHitValues = groupHitDict.values() + if groupHitValues.count(0) == len(groupHitValues): + singleSuitResponseTrack = MovieUtil.createSuitDodgeMultitrack(delay + tSuitDodges, suit, leftSuits, rightSuits) + else: + singleSuitResponseTrack = Sequence(Wait(tPieHitsSuit - 0.1), Func(MovieUtil.indicateMissed, suit, 1.0)) + groupSuitResponseTrack.append(singleSuitResponseTrack) + + return [toonTrack, + pieTrack, + soundTrack, + groupSuitResponseTrack] + + +def reparentCakePart(pie, cakeParts): + pieParent = pie.getParent() + notify.debug('pieParent = %s' % pieParent) + for cakePart in cakeParts: + cakePart.wrtReparentTo(pieParent) diff --git a/toontown/battle/MovieToonVictory.py b/toontown/battle/MovieToonVictory.py new file mode 100755 index 00000000..e4998bd4 --- /dev/null +++ b/toontown/battle/MovieToonVictory.py @@ -0,0 +1,131 @@ +from direct.interval.IntervalGlobal import * +from direct.showbase.DirectObject import DirectObject +from RewardPanel import * +from BattleSounds import * +import MovieCamera +from direct.directnotify import DirectNotifyGlobal +import types +notify = DirectNotifyGlobal.directNotify.newCategory('MovieToonVictory') +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals + +def __findToonReward(rewards, toon): + for r in rewards: + if r['toon'] == toon: + return r + + return None + + +class ToonVictorySkipper(DirectObject): + + def __init__(self, numToons, noSkip): + self._numToons = numToons + self._noSkip = noSkip + self._startTimes = {} + self._ivals = [] + self._battle = None + return + + def destroy(self): + self._ivals = None + return + + def getSetupFunc(self, index): + return Func(self._setupSkipListen, index) + + def getTeardownFunc(self, index): + return Func(self._teardownSkipListen, index) + + def setBattle(self, battle): + self._battle = battle + + def setStartTime(self, index, startT): + self._startTimes[index] = startT + + def setIvals(self, ivals, timeOffset = 0.0): + for index in self._startTimes: + self._startTimes[index] += timeOffset + + self._ivals = ivals + + def _setupSkipListen(self, index): + if not self._noSkip: + func = Functor(self._skipToon, index) + self.accept('escape', func) + self.accept(RewardPanel.SkipBattleMovieEvent, func) + + def _teardownSkipListen(self, index): + if not self._noSkip: + self.ignore('escape') + self.ignore(RewardPanel.SkipBattleMovieEvent) + + def _skipToon(self, index): + nextIndex = index + 1 + if nextIndex >= self._numToons: + for ival in self._ivals: + ival.finish() + + if self._battle: + self._battle.setSkippingRewardMovie() + elif nextIndex in self._startTimes: + for ival in self._ivals: + ival.setT(self._startTimes[nextIndex]) + + +def doToonVictory(localToonActive, toons, rewardToonIds, rewardDicts, deathList, rpanel, allowGroupShot = 1, uberList = [], helpfulToonsList = [], noSkip = False): + track = Sequence() + if localToonActive == 1: + track.append(Func(rpanel.show)) + track.append(Func(NametagGlobals.setOnscreenChatForced, 1)) + camTrack = Sequence() + endTrack = Sequence() + danceSound = globalBattleSoundCache.getSound('ENC_Win.ogg') + toonList = [] + countToons = 0 + uberListNew = [] + for t in toons: + if isinstance(t, types.IntType): + t = base.cr.doId2do.get(t) + if t: + toonList.append(t) + uberListNew.append(uberList[countToons]) + countToons += 1 + + toonId2toon = {} + for toon in toonList: + toonId2toon[toon.doId] = toon + + rewardToonList = [] + for id in rewardToonIds: + rewardToonList.append(toonId2toon.get(id)) + + skipper = ToonVictorySkipper(len(toonList), noSkip) + lastListenIndex = 0 + track.append(skipper.getSetupFunc(lastListenIndex)) + for tIndex in xrange(len(toonList)): + t = toonList[tIndex] + rdict = __findToonReward(rewardDicts, t) + if rdict != None: + expTrack = rpanel.getExpTrack(t, rdict['origExp'], rdict['earnedExp'], deathList, rdict['origQuests'], rdict['items'], rdict['missedItems'], rdict['origMerits'], rdict['merits'], rdict['parts'], rewardToonList, uberListNew[tIndex], helpfulToonsList, noSkip=noSkip) + if expTrack: + skipper.setStartTime(tIndex, track.getDuration()) + track.append(skipper.getTeardownFunc(lastListenIndex)) + lastListenIndex = tIndex + track.append(skipper.getSetupFunc(lastListenIndex)) + track.append(expTrack) + camDuration = expTrack.getDuration() + camExpTrack = MovieCamera.chooseRewardShot(t, camDuration) + camTrack.append(MovieCamera.chooseRewardShot(t, camDuration, allowGroupShot=allowGroupShot)) + + track.append(skipper.getTeardownFunc(lastListenIndex)) + track.append(Func(skipper.destroy)) + if localToonActive == 1: + track.append(Func(rpanel.hide)) + track.append(Func(NametagGlobals.setOnscreenChatForced, 0)) + track.append(endTrack) + trackdur = track.getDuration() + soundTrack = SoundInterval(danceSound, duration=trackdur, loop=1) + mtrack = Parallel(track, soundTrack) + skipper.setIvals((mtrack, camTrack)) + return (mtrack, camTrack, skipper) diff --git a/toontown/battle/MovieTrap.py b/toontown/battle/MovieTrap.py new file mode 100755 index 00000000..17f0463a --- /dev/null +++ b/toontown/battle/MovieTrap.py @@ -0,0 +1,582 @@ +from direct.interval.IntervalGlobal import * +from BattleBase import * +from BattleProps import * +from BattleSounds import * +import MovieUtil +import MovieCamera +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownBattleGlobals +from direct.actor import Actor +from direct.particles import ParticleEffect +import BattleParticles +import BattleProps +import MovieNPCSOS +from MovieSound import createSuitResetPosTrack +notify = DirectNotifyGlobal.directNotify.newCategory('MovieTrap') + +def doTraps(traps): + if len(traps) == 0: + return (None, None) + npcArrivals, npcDepartures, npcs = MovieNPCSOS.doNPCTeleports(traps) + hasUberTrapConflict = False + suitTrapsDict = {} + for trap in traps: + targets = trap['target'] + if len(targets) == 1: + suitId = targets[0]['suit'].doId + if suitId in suitTrapsDict: + suitTrapsDict[suitId].append(trap) + else: + suitTrapsDict[suitId] = [trap] + else: + for target in targets: + suitId = target['suit'].doId + if suitId not in suitTrapsDict: + suitTrapsDict[suitId] = [trap] + break + + if trap['level'] == UBER_GAG_LEVEL_INDEX: + if len(traps) > 1: + hasUberTrapConflict = True + for oneTarget in trap['target']: + suit = oneTarget['suit'] + if suit.battleTrap != NO_TRAP: + hasUberTrapConflict = True + + suitTrapLists = suitTrapsDict.values() + mtrack = Parallel() + for trapList in suitTrapLists: + trapPropList = [] + for i in xrange(len(trapList)): + trap = trapList[i] + level = trap['level'] + if level == 0: + banana = globalPropPool.getProp('banana') + banana2 = MovieUtil.copyProp(banana) + trapPropList.append([banana, banana2]) + elif level == 1: + rake = globalPropPool.getProp('rake') + rake2 = MovieUtil.copyProp(rake) + rake.pose('rake', 0) + rake2.pose('rake', 0) + trapPropList.append([rake, rake2]) + elif level == 2: + marbles = globalPropPool.getProp('marbles') + marbles2 = MovieUtil.copyProp(marbles) + trapPropList.append([marbles, marbles2]) + elif level == 3: + trapPropList.append([globalPropPool.getProp('quicksand')]) + elif level == 4: + trapPropList.append([globalPropPool.getProp('trapdoor')]) + elif level == 5: + tnt = globalPropPool.getProp('tnt') + tnt2 = MovieUtil.copyProp(tnt) + trapPropList.append([tnt, tnt2]) + elif level == 6: + tnt = globalPropPool.getProp('traintrack') + tnt2 = MovieUtil.copyProp(tnt) + trapPropList.append([tnt, tnt2]) + else: + notify.warning('__doTraps() - Incorrect trap level: %d' % level) + + if len(trapList) == 1 and not hasUberTrapConflict: + ival = __doTrapLevel(trapList[0], trapPropList[0]) + if ival: + mtrack.append(ival) + else: + subMtrack = Parallel() + for i in xrange(len(trapList)): + trap = trapList[i] + trapProps = trapPropList[i] + ival = __doTrapLevel(trap, trapProps, explode=1) + if ival: + subMtrack.append(ival) + + mtrack.append(subMtrack) + + trapTrack = Sequence(npcArrivals, mtrack, npcDepartures) + camDuration = mtrack.getDuration() + enterDuration = npcArrivals.getDuration() + exitDuration = npcDepartures.getDuration() + camTrack = MovieCamera.chooseTrapShot(traps, camDuration, enterDuration, exitDuration) + return (trapTrack, camTrack) + + +def __doTrapLevel(trap, trapProps, explode = 0): + level = trap['level'] + if level == 0: + return __trapBanana(trap, trapProps, explode) + elif level == 1: + return __trapRake(trap, trapProps, explode) + elif level == 2: + return __trapMarbles(trap, trapProps, explode) + elif level == 3: + return __trapQuicksand(trap, trapProps, explode) + elif level == 4: + return __trapTrapdoor(trap, trapProps, explode) + elif level == 5: + return __trapTNT(trap, trapProps, explode) + elif level == 6: + return __trapTrain(trap, trapProps, explode) + return None + + +def getSoundTrack(fileName, delay = 0.01, duration = None, node = None): + soundEffect = globalBattleSoundCache.getSound(fileName) + if duration: + return Sequence(Wait(delay), SoundInterval(soundEffect, duration=duration, node=node)) + else: + return Sequence(Wait(delay), SoundInterval(soundEffect, node=node)) + + +def __createThrownTrapMultiTrack(trap, propList, propName, propPos = None, propHpr = None, anim = 0, explode = 0): + toon = trap['toon'] + level = trap['level'] + battle = trap['battle'] + target = trap['target'] + suit = target[0]['suit'] + targetPos = suit.getPos(battle) + thrownProp = propList[0] + unthrownProp = propList[1] + torso = toon.style.torso + torso = torso[0] + if torso == 'l': + throwDelay = 2.3 + elif torso == 'm': + throwDelay = 2.3 + else: + throwDelay = 1.9 + throwDuration = 0.9 + animBreakPoint = throwDelay + throwDuration + animDelay = 3.1 + trapTrack = ToontownBattleGlobals.TRAP_TRACK + trapTrackNames = ToontownBattleGlobals.AvProps[trapTrack] + trapName = trapTrackNames[level] + hands = toon.getRightHands() + propTrack = Sequence() + if propPos and propHpr: + propTrack.append(Func(MovieUtil.showProps, propList, hands, propPos, propHpr)) + else: + propTrack.append(Func(MovieUtil.showProps, propList, hands)) + if anim == 1: + pTracks = Parallel() + for prop in propList: + pTracks.append(ActorInterval(prop, propName, duration=animBreakPoint)) + + propTrack.append(pTracks) + throwTrack = Sequence() + throwTrack.append(Wait(throwDelay)) + throwTrack.append(Func(unthrownProp.reparentTo, hidden)) + throwTrack.append(Func(toon.update)) + if suit.battleTrap != NO_TRAP: + notify.debug('trapSuit() - trap: %d destroyed existing trap: %d' % (level, suit.battleTrap)) + battle.removeTrap(suit) + if trapName == 'rake': + trapProp = globalPropPool.getProp('rake-react') + else: + trapProp = MovieUtil.copyProp(thrownProp) + suit.battleTrapProp = trapProp + suit.battleTrap = level + suit.battleTrapIsFresh = 1 + if trapName == 'banana': + trapPoint, trapHpr = battle.getActorPosHpr(suit) + trapPoint.setY(MovieUtil.SUIT_TRAP_DISTANCE) + slidePoint = Vec3(trapPoint.getX(), trapPoint.getY() - 2, trapPoint.getZ()) + throwingTrack = createThrowingTrack(thrownProp, slidePoint, duration=0.9, parent=battle) + moveTrack = LerpPosInterval(thrownProp, 0.8, pos=trapPoint, other=battle) + animTrack = ActorInterval(thrownProp, propName, startTime=animBreakPoint) + slideTrack = Parallel(moveTrack, animTrack) + motionTrack = Sequence(throwingTrack, slideTrack) + hprTrack = LerpHprInterval(thrownProp, 1.7, hpr=Point3(0, 0, 0)) + soundTrack = getSoundTrack('TL_banana.ogg', node=toon) + scaleTrack = LerpScaleInterval(thrownProp, 1.7, scale=MovieUtil.PNT3_ONE) + throwTrack.append(Wait(0.25)) + throwTrack.append(Func(thrownProp.wrtReparentTo, suit)) + throwTrack.append(Parallel(motionTrack, hprTrack, scaleTrack, soundTrack)) + elif trapName == 'tnt': + trapPoint, trapHpr = battle.getActorPosHpr(suit) + trapPoint.setY(MovieUtil.SUIT_TRAP_TNT_DISTANCE - 3.9) + trapPoint.setZ(trapPoint.getZ() + 0.4) + throwingTrack = createThrowingTrack(thrownProp, trapPoint, duration=throwDuration, parent=battle) + hprTrack = LerpHprInterval(thrownProp, 0.9, hpr=Point3(0, 90, 0)) + scaleTrack = LerpScaleInterval(thrownProp, 0.9, scale=MovieUtil.PNT3_ONE) + soundTrack = getSoundTrack('TL_dynamite.ogg', delay=0.8, duration=0.7, node=suit) + throwTrack.append(Wait(0.2)) + throwTrack.append(Parallel(throwingTrack, hprTrack, scaleTrack, soundTrack)) + elif trapName == 'marbles': + trapPoint, trapHpr = battle.getActorPosHpr(suit) + trapPoint.setY(MovieUtil.SUIT_TRAP_MARBLES_DISTANCE) + flingDuration = 0.2 + rollDuration = 1.0 + throwDuration = flingDuration + rollDuration + landPoint = Point3(0, trapPoint.getY() + 2, trapPoint.getZ()) + throwPoint = Point3(0, trapPoint.getY(), trapPoint.getZ()) + moveTrack = Sequence(Func(thrownProp.wrtReparentTo, suit), Func(thrownProp.setHpr, Point3(94, 0, 0)), LerpPosInterval(thrownProp, flingDuration, pos=landPoint, other=suit), LerpPosInterval(thrownProp, rollDuration, pos=throwPoint, other=suit)) + animTrack = ActorInterval(thrownProp, propName, startTime=throwDelay + 0.9) + scaleTrack = LerpScaleInterval(thrownProp, throwDuration, scale=MovieUtil.PNT3_ONE) + soundTrack = getSoundTrack('TL_marbles.ogg', delay=0.1, node=toon) + throwTrack.append(Wait(0.2)) + throwTrack.append(Parallel(moveTrack, animTrack, scaleTrack, soundTrack)) + elif trapName == 'rake': + trapPoint, trapHpr = battle.getActorPosHpr(suit) + trapPoint.setY(MovieUtil.SUIT_TRAP_RAKE_DISTANCE) + throwDuration = 1.1 + throwingTrack = createThrowingTrack(thrownProp, trapPoint, duration=throwDuration, parent=suit) + hprTrack = LerpHprInterval(thrownProp, throwDuration, hpr=VBase3(63.43, -90.0, 63.43)) + scaleTrack = LerpScaleInterval(thrownProp, 0.9, scale=Point3(0.7, 0.7, 0.7)) + soundTrack = SoundInterval(globalBattleSoundCache.getSound('TL_rake_throw_only.ogg'), duration=1.1, node=suit) + throwTrack.append(Wait(0.2)) + throwTrack.append(Parallel(throwingTrack, hprTrack, scaleTrack, soundTrack)) + else: + notify.warning('__createThrownTrapMultiTrack() - Incorrect trap: %s thrown from toon' % trapName) + + def placeTrap(trapProp, suit, battle = battle, trapName = trapName): + if not trapProp or trapProp.isEmpty(): + return + trapProp.wrtReparentTo(suit) + trapProp.show() + if trapName == 'rake': + trapProp.setPos(0, MovieUtil.SUIT_TRAP_RAKE_DISTANCE, 0) + trapProp.setHpr(Point3(0, 270, 0)) + trapProp.setScale(Point3(0.7, 0.7, 0.7)) + rakeOffset = MovieUtil.getSuitRakeOffset(suit) + trapProp.setY(trapProp.getY() + rakeOffset) + elif trapName == 'banana': + trapProp.setHpr(0, 0, 0) + trapProp.setPos(0, MovieUtil.SUIT_TRAP_DISTANCE, -0.35) + trapProp.pose(trapName, trapProp.getNumFrames(trapName) - 1) + elif trapName == 'marbles': + trapProp.setHpr(Point3(94, 0, 0)) + trapProp.setPos(0, MovieUtil.SUIT_TRAP_MARBLES_DISTANCE, 0) + trapProp.pose(trapName, trapProp.getNumFrames(trapName) - 1) + elif trapName == 'tnt': + trapProp.setHpr(0, 90, 0) + trapProp.setPos(0, MovieUtil.SUIT_TRAP_TNT_DISTANCE, 0.4) + else: + notify.warning('placeTrap() - Incorrect trap: %s placed on a suit' % trapName) + + dustNode = hidden.attachNewNode('DustNode') + + def placeDustExplosion(dustNode = dustNode, thrownProp = thrownProp, battle = battle): + dustNode.reparentTo(battle) + dustNode.setPos(thrownProp.getPos(battle)) + + if explode == 1: + throwTrack.append(Func(thrownProp.wrtReparentTo, hidden)) + throwTrack.append(Func(placeDustExplosion)) + throwTrack.append(createCartoonExplosionTrack(dustNode, 'dust', explosionPoint=Point3(0, 0, 0))) + throwTrack.append(Func(battle.removeTrap, suit)) + else: + throwTrack.append(Func(placeTrap, trapProp, suit)) + if trapName == 'tnt': + tip = trapProp.find('**/joint_attachEmitter') + sparks = BattleParticles.createParticleEffect(file='tnt') + trapProp.sparksEffect = sparks + throwTrack.append(Func(sparks.start, tip)) + throwTrack.append(Func(MovieUtil.removeProps, propList)) + toonTrack = Sequence(Func(toon.headsUp, battle, targetPos), ActorInterval(toon, 'toss'), Func(toon.loop, 'neutral')) + return Parallel(propTrack, throwTrack, toonTrack) + + +def __createPlacedTrapMultiTrack(trap, prop, propName, propPos = None, propHpr = None, explode = 0, visibleOnlyForThisSuitId = None): + toon = trap['toon'] + if 'npc' in trap: + toon = trap['npc'] + level = trap['level'] + battle = trap['battle'] + origHpr = toon.getHpr(battle) + trapPoint = Point3(0, MovieUtil.SUIT_TRAP_DISTANCE, 0.025) + trapDelay = 2.5 + hands = toon.getLeftHands() + + def placeDustExplosion(dustNode, trapProp, battle): + dustNode.reparentTo(battle) + dustNode.setPos(trapProp.getPos(battle)) + + trapTracks = Parallel() + firstTime = 1 + targets = trap['target'] + for target in targets: + suit = target['suit'] + suitPos = suit.getPos(battle) + targetPos = suitPos + trapProp = MovieUtil.copyProp(prop) + showThisTrap = True + if visibleOnlyForThisSuitId and visibleOnlyForThisSuitId != suit.doId: + showThisTrap = False + trapTrack = Sequence() + trapTrack.append(Wait(trapDelay)) + if showThisTrap: + notify.debug('showing trap %s for %d' % (trapProp.getName(), suit.doId)) + trapTrack.append(Func(trapProp.show)) + else: + notify.debug('hiding trap %s for %d' % (trapProp.getName(), suit.doId)) + trapTrack.append(Func(trapProp.hide)) + trapTrack.append(Func(trapProp.setScale, Point3(0.1, 0.1, 0.1))) + trapTrack.append(Func(trapProp.reparentTo, suit)) + trapTrack.append(Func(trapProp.setPos, trapPoint)) + trapTrack.append(LerpScaleInterval(trapProp, 1.2, Point3(1.7, 1.7, 1.7))) + if explode == 1: + dustNode = hidden.attachNewNode('DustNode') + trapTrack.append(Func(trapProp.wrtReparentTo, hidden)) + trapTrack.append(Func(placeDustExplosion, dustNode, trapProp, battle)) + trapTrack.append(createCartoonExplosionTrack(dustNode, 'dust', explosionPoint=Point3(0, 0, 0))) + trapTrack.append(Func(MovieUtil.removeProp, trapProp)) + trapTrack.append(Func(battle.removeTrap, suit)) + else: + if suit.battleTrap != NO_TRAP: + notify.debug('trapSuit() - trap: %d destroyed existing trap: %d' % (level, suit.battleTrap)) + battle.removeTrap(suit) + suit.battleTrapProp = trapProp + suit.battleTrap = level + suit.battleTrapIsFresh = 1 + trapTracks.append(trapTrack) + + button = globalPropPool.getProp('button') + button2 = MovieUtil.copyProp(button) + buttons = [button, button2] + toonTrack = Sequence() + toonTrack.append(Func(MovieUtil.showProps, buttons, hands)) + toonTrack.append(Func(toon.headsUp, battle, suitPos)) + toonTrack.append(ActorInterval(toon, 'pushbutton')) + toonTrack.append(Func(MovieUtil.removeProps, buttons)) + toonTrack.append(Func(toon.loop, 'neutral')) + toonTrack.append(Func(toon.setHpr, battle, origHpr)) + if propName == 'quicksand': + propSound = globalBattleSoundCache.getSound('TL_quicksand.ogg') + else: + propSound = globalBattleSoundCache.getSound('TL_trap_door.ogg') + buttonSound = globalBattleSoundCache.getSound('AA_drop_trigger_box.ogg') + soundTrack = Sequence(Wait(2.3), SoundInterval(buttonSound, duration=0.67, node=toon), Wait(0.3), SoundInterval(propSound, duration=0.5, node=toon)) + return Parallel(trapTracks, toonTrack, soundTrack) + + +def __trapBanana(trap, trapProps, explode): + toon = trap['toon'] + suit = trap['target'][0]['suit'] + notify.debug('toon: %s lays banana peel in front of suit: %d' % (toon.getName(), suit.doId)) + bananas = trapProps + return __createThrownTrapMultiTrack(trap, bananas, 'banana', anim=1, explode=explode) + + +def __trapRake(trap, trapProps, explode): + toon = trap['toon'] + suit = trap['target'][0]['suit'] + notify.debug('toon: %s lays rake in front of suit: %d' % (toon.getName(), suit.doId)) + rakes = trapProps + return __createThrownTrapMultiTrack(trap, rakes, 'rake', anim=1, explode=explode) + + +def __trapMarbles(trap, trapProps, explode): + toon = trap['toon'] + suit = trap['target'][0]['suit'] + notify.debug('toon: %s lays marbles in front of suit: %d' % (toon.getName(), suit.doId)) + bothMarbles = trapProps + pos = Point3(0, 0, 0) + hpr = Point3(0, 0, -30) + return __createThrownTrapMultiTrack(trap, bothMarbles, 'marbles', pos, hpr, anim=1, explode=explode) + + +def __trapQuicksand(trap, trapProps, explode): + toon = trap['toon'] + suit = trap['target'][0]['suit'] + notify.debug('toon: %s lays quicksand in front of suit: %d' % (toon.getName(), suit.doId)) + quicksand = trapProps[0] + return __createPlacedTrapMultiTrack(trap, quicksand, 'quicksand', explode=explode) + + +def __trapTrapdoor(trap, trapProps, explode): + toon = trap['toon'] + if 'npc' in trap: + toon = trap['npc'] + targets = trap['target'] + for target in targets: + suit = target['suit'] + notify.debug('toon: %s lays trapdoor in front of suit: %d' % (toon.getName(), suit.doId)) + + trapdoor = trapProps[0] + return __createPlacedTrapMultiTrack(trap, trapdoor, 'trapdoor', explode=explode) + + +def __trapTNT(trap, trapProps, explode): + toon = trap['toon'] + suit = trap['target'][0]['suit'] + notify.debug('toon: %s lays TNT in front of suit: %d' % (toon.getName(), suit.doId)) + tnts = trapProps + return __createThrownTrapMultiTrack(trap, tnts, 'tnt', anim=0, explode=explode) + + +def __trapTrain(trap, trapProps, explode): + toon = trap['toon'] + if 'npc' in trap: + toon = trap['npc'] + targets = trap['target'] + battle = trap['battle'] + visibleOnlyForThisSuitId = 0 + centerSuit = None + closestXDistance = 10000 + for target in targets: + suit = target['suit'] + suitPoint, suitHpr = battle.getActorPosHpr(suit) + xDistance = abs(suitPoint.getX()) + if xDistance < closestXDistance: + visibleOnlyForThisSuitId = suit.doId + closestXDistance = xDistance + centerSuit = suit + notify.debug('toon: %s doing traintrack in front of suit: %d' % (toon.getName(), suit.doId)) + + traintrack = trapProps[0] + return __createPlacedGroupTrapTrack(trap, traintrack, 'traintrack', centerSuit, explode=explode) + + +def createThrowingTrack(object, target, duration = 1.0, parent = render, gravity = -32.144): + values = {} + values['origin'] = None + values['velocity'] = None + + def calcOriginAndVelocity(object = object, target = target, values = values, duration = duration, parent = parent, gravity = gravity): + object.wrtReparentTo(parent) + values['origin'] = object.getPos(parent) + origin = object.getPos(parent) + values['velocity'] = (target[2] - origin[2] - 0.5 * gravity * duration * duration) / duration + + def throwPos(t, object, duration, target, values = values, gravity = -32.144): + if values['origin'] != None: + origin = values['origin'] + else: + origin = object.getPos() + if values['velocity'] != None: + velocity = values['velocity'] + else: + velocity = 16 + x = origin[0] * (1 - t) + target[0] * t + y = origin[1] * (1 - t) + target[1] * t + time = t * duration + z = origin[2] + velocity * time + 0.5 * gravity * time * time + object.setPos(x, y, z) + return + + return Sequence(Func(calcOriginAndVelocity), LerpFunctionInterval(throwPos, fromData=0.0, toData=1.0, duration=duration, extraArgs=[object, duration, target])) + + +def createCartoonExplosionTrack(parent, animName, explosionPoint = None): + explosionTrack = Sequence() + explosion = BattleProps.globalPropPool.getProp(animName) + explosion.setBillboardPointEye() + if not explosionPoint: + explosionPoint = Point3(0, 3.6, 2.1) + if animName == 'dust': + scale = Point3(0.1, 0.9, 1) + explosionTrack.append(Func(explosion.reparentTo, parent)) + explosionTrack.append(Func(explosion.setPos, explosionPoint)) + explosionTrack.append(Func(explosion.setScale, scale)) + explosionTrack.append(ActorInterval(explosion, animName)) + explosionTrack.append(Func(MovieUtil.removeProp, explosion)) + return explosionTrack + + +def __createPlacedGroupTrapTrack(trap, prop, propName, centerSuit, propPos = None, propHpr = None, explode = 0): + toon = trap['toon'] + if 'npc' in trap: + toon = trap['npc'] + level = trap['level'] + battle = trap['battle'] + origHpr = toon.getHpr(battle) + trapPoint = Point3(0, 5 - MovieUtil.SUIT_TRAP_DISTANCE, 0.025) + trapDelay = 2.5 + hands = toon.getLeftHands() + + def placeDustExplosion(dustNode, trapProp, battle): + dustNode.reparentTo(battle) + dustNode.setPos(trapProp.getPos(battle)) + + trapTracks = Parallel() + firstTime = 1 + targets = trap['target'] + if True: + suit = centerSuit + suitPos = suit.getPos(battle) + targetPos = suitPos + trapProp = MovieUtil.copyProp(prop) + showThisTrap = True + trapTrack = Sequence() + trapTrack.append(Wait(trapDelay)) + if showThisTrap: + notify.debug('showing trap %s for %d' % (trapProp.getName(), suit.doId)) + trapTrack.append(Func(trapProp.show)) + else: + notify.debug('hiding trap %s for %d' % (trapProp.getName(), suit.doId)) + trapTrack.append(Func(trapProp.hide)) + trapTrack.append(Func(trapProp.setScale, Point3(0.1, 0.1, 0.1))) + trapTrack.append(Func(trapProp.reparentTo, battle)) + trapTrack.append(Func(trapProp.setPos, trapPoint)) + trapTrack.append(Func(trapProp.setH, 0)) + trapTrack.append(LerpScaleInterval(trapProp, 1.2, Point3(1.0, 1.0, 1.0))) + if explode == 1: + dustNode = hidden.attachNewNode('DustNode') + removeTrapsParallel = Parallel() + oneTrapTrack = Sequence() + oneTrapTrack.append(Func(trapProp.wrtReparentTo, hidden)) + oneTrapTrack.append(Func(placeDustExplosion, dustNode, trapProp, battle)) + oneTrapTrack.append(createCartoonExplosionTrack(dustNode, 'dust', explosionPoint=Point3(0, 0, 0))) + oneTrapTrack.append(Func(MovieUtil.removeProp, trapProp)) + removeTrapsParallel.append(oneTrapTrack) + for target in trap['target']: + otherSuit = target['suit'] + if otherSuit.battleTrapProp: + otherDustNode = hidden.attachNewNode('DustNodeOtherSuit') + otherTrapTrack = Sequence() + otherTrapTrack.append(Func(otherSuit.battleTrapProp.wrtReparentTo, hidden)) + otherTrapTrack.append(Func(placeDustExplosion, dustNode, otherSuit.battleTrapProp, battle)) + otherTrapTrack.append(createCartoonExplosionTrack(otherDustNode, 'dust', explosionPoint=Point3(0, 0, 0))) + otherTrapTrack.append(Func(battle.removeTrap, otherSuit)) + removeTrapsParallel.append(otherTrapTrack) + + trapTrack.append(removeTrapsParallel) + else: + if suit.battleTrap != NO_TRAP: + notify.debug('trapSuit() - trap: %d destroyed existing trap: %d' % (level, suit.battleTrap)) + battle.removeTrap(suit) + suit.battleTrapProp = trapProp + suit.battleTrap = level + suit.battleTrapIsFresh = 1 + unlureSuits = Parallel() + for target in targets: + kbbonus = target['kbbonus'] + if kbbonus == 0: + unluredSuit = target['suit'] + suitTrack = Sequence() + suitTrack.append(createSuitResetPosTrack(unluredSuit, battle)) + suitTrack.append(Func(battle.unlureSuit, unluredSuit)) + unlureSuits.append(suitTrack) + + trapTrack.append(unlureSuits) + for otherSuit in battle.suits: + if not otherSuit == suit: + if otherSuit.battleTrap != NO_TRAP: + notify.debug('trapSuit() - trap: %d destroyed existing trap: %d' % (level, suit.battleTrap)) + battle.removeTrap(otherSuit) + otherSuit.battleTrapProp = trapProp + otherSuit.battleTrap = level + otherSuit.battleTrapIsFresh = 1 + + trapTracks.append(trapTrack) + button = globalPropPool.getProp('button') + button2 = MovieUtil.copyProp(button) + buttons = [button, button2] + toonTrack = Sequence() + toonTrack.append(Func(MovieUtil.showProps, buttons, hands)) + toonTrack.append(Func(toon.headsUp, battle, suitPos)) + toonTrack.append(ActorInterval(toon, 'pushbutton')) + toonTrack.append(Func(MovieUtil.removeProps, buttons)) + toonTrack.append(Func(toon.loop, 'neutral')) + toonTrack.append(Func(toon.setHpr, battle, origHpr)) + if propName == 'quicksand': + propSound = globalBattleSoundCache.getSound('TL_quicksand.ogg') + elif propName == 'traintrack': + propSound = globalBattleSoundCache.getSound('TL_train_track_appear.ogg') + else: + propSound = globalBattleSoundCache.getSound('TL_trap_door.ogg') + buttonSound = globalBattleSoundCache.getSound('AA_drop_trigger_box.ogg') + soundTrack = Sequence(Wait(2.3), Parallel(SoundInterval(buttonSound, duration=0.67, node=toon), SoundInterval(propSound, node=toon))) + return Parallel(trapTracks, toonTrack, soundTrack) diff --git a/toontown/battle/MovieUtil.py b/toontown/battle/MovieUtil.py new file mode 100755 index 00000000..be47b06b --- /dev/null +++ b/toontown/battle/MovieUtil.py @@ -0,0 +1,603 @@ +from direct.interval.IntervalGlobal import * +from BattleBase import * +from BattleProps import * +from direct.directnotify import DirectNotifyGlobal +import random +from direct.particles import ParticleEffect +import BattleParticles +import BattleProps +from toontown.toonbase import TTLocalizer +notify = DirectNotifyGlobal.directNotify.newCategory('MovieUtil') +SUIT_LOSE_DURATION = 8.5 +SUIT_LOSE_REVIVE_DURATION = 6.0 +SUIT_LURE_DISTANCE = 2.6 +SUIT_LURE_DOLLAR_DISTANCE = 5.1 +SUIT_EXTRA_REACH_DISTANCE = 0.9 +SUIT_EXTRA_RAKE_DISTANCE = 1.1 +SUIT_TRAP_DISTANCE = 2.6 +SUIT_TRAP_RAKE_DISTANCE = 4.5 +SUIT_TRAP_MARBLES_DISTANCE = 3.7 +SUIT_TRAP_TNT_DISTANCE = 5.1 +PNT3_NEARZERO = Point3(0.01, 0.01, 0.01) +PNT3_ZERO = Point3(0.0, 0.0, 0.0) +PNT3_ONE = Point3(1.0, 1.0, 1.0) +largeSuits = ['f', + 'cc', + 'gh', + 'tw', + 'bf', + 'sc', + 'ds', + 'hh', + 'cr', + 'tbc', + 'bs', + 'sd', + 'le', + 'bw', + 'nc', + 'mb', + 'ls', + 'rb', + 'ms', + 'tf', + 'm', + 'mh'] +shotDirection = 'left' + +def avatarDodge(leftAvatars, rightAvatars, leftData, rightData): + if len(leftAvatars) > len(rightAvatars): + PoLR = rightAvatars + PoMR = leftAvatars + else: + PoLR = leftAvatars + PoMR = rightAvatars + upper = 1 + 4 * abs(len(leftAvatars) - len(rightAvatars)) + if random.randint(0, upper) > 0: + avDodgeList = PoLR + else: + avDodgeList = PoMR + if avDodgeList is leftAvatars: + data = leftData + else: + data = rightData + return (avDodgeList, data) + + +def avatarHide(avatar): + notify.debug('avatarHide(%d)' % avatar.doId) + if hasattr(avatar, 'battleTrapProp'): + notify.debug('avatar.battleTrapProp = %s' % avatar.battleTrapProp) + avatar.detachNode() + + +def copyProp(prop): + from direct.actor import Actor + if isinstance(prop, Actor.Actor): + return Actor.Actor(other=prop) + else: + return prop.copyTo(hidden) + + +def showProp(prop, hand, pos = None, hpr = None, scale = None): + prop.reparentTo(hand) + if pos: + if callable(pos): + pos = pos() + prop.setPos(pos) + if hpr: + if callable(hpr): + hpr = hpr() + prop.setHpr(hpr) + if scale: + if callable(scale): + scale = scale() + prop.setScale(scale) + + +def showProps(props, hands, pos = None, hpr = None, scale = None): + index = 0 + for prop in props: + prop.reparentTo(hands[index]) + if pos: + prop.setPos(pos) + if hpr: + prop.setHpr(hpr) + if scale: + prop.setScale(scale) + index += 1 + + +def hideProps(props): + for prop in props: + prop.detachNode() + + +def removeProp(prop): + from direct.actor import Actor + if prop.isEmpty() == 1 or prop == None: + return + prop.detachNode() + if isinstance(prop, Actor.Actor): + prop.cleanup() + else: + prop.removeNode() + return + + +def removeProps(props): + for prop in props: + removeProp(prop) + + +def getActorIntervals(props, anim): + tracks = Parallel() + for prop in props: + tracks.append(ActorInterval(prop, anim)) + + return tracks + + +def getScaleIntervals(props, duration, startScale, endScale): + tracks = Parallel() + for prop in props: + tracks.append(LerpScaleInterval(prop, duration, endScale, startScale=startScale)) + + return tracks + + +def avatarFacePoint(av, other = render): + pnt = av.getPos(other) + pnt.setZ(pnt[2] + av.getHeight()) + return pnt + + +def insertDeathSuit(suit, deathSuit, battle = None, pos = None, hpr = None): + holdParent = suit.getParent() + if suit.getVirtual(): + virtualize(deathSuit) + avatarHide(suit) + if deathSuit != None and not deathSuit.isEmpty(): + if holdParent and 0: + deathSuit.reparentTo(holdParent) + else: + deathSuit.reparentTo(render) + if battle != None and pos != None: + deathSuit.setPos(battle, pos) + if battle != None and hpr != None: + deathSuit.setHpr(battle, hpr) + return + + +def removeDeathSuit(suit, deathSuit): + notify.debug('removeDeathSuit()') + if not deathSuit.isEmpty(): + deathSuit.detachNode() + suit.cleanupLoseActor() + + +def insertReviveSuit(suit, deathSuit, battle = None, pos = None, hpr = None): + holdParent = suit.getParent() + if suit.getVirtual(): + virtualize(deathSuit) + suit.hide() + if deathSuit != None and not deathSuit.isEmpty(): + if holdParent and 0: + deathSuit.reparentTo(holdParent) + else: + deathSuit.reparentTo(render) + if battle != None and pos != None: + deathSuit.setPos(battle, pos) + if battle != None and hpr != None: + deathSuit.setHpr(battle, hpr) + return + + +def removeReviveSuit(suit, deathSuit): + notify.debug('removeDeathSuit()') + suit.setSkelecog(1) + suit.show() + if not deathSuit.isEmpty(): + deathSuit.detachNode() + suit.cleanupLoseActor() + suit.healthBar.geom.show() + suit.resetHealthBarForSkele() + + +def virtualize(deathsuit): + actorNode = deathsuit.find('**/__Actor_modelRoot') + actorCollection = actorNode.findAllMatches('*') + parts = () + for thingIndex in xrange(0, actorCollection.getNumPaths()): + thing = actorCollection[thingIndex] + if thing.getName() not in ('joint_attachMeter', 'joint_nameTag', 'def_nameTag'): + thing.setColorScale(1.0, 0.0, 0.0, 1.0) + thing.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + thing.setDepthWrite(False) + thing.setBin('fixed', 1) + + +def createTrainTrackAppearTrack(dyingSuit, toon, battle, npcs): + retval = Sequence() + return retval + possibleSuits = [] + for suitAttack in battle.movie.suitAttackDicts: + suit = suitAttack['suit'] + if not suit == dyingSuit: + if hasattr(suit, 'battleTrapProp') and suit.battleTrapProp and suit.battleTrapProp.getName() == 'traintrack': + possibleSuits.append(suitAttack['suit']) + + closestXDistance = 10000 + closestSuit = None + for suit in possibleSuits: + suitPoint, suitHpr = battle.getActorPosHpr(suit) + xDistance = abs(suitPoint.getX()) + if xDistance < closestXDistance: + closestSuit = suit + closestXDistance = xDistance + + if closestSuit and closestSuit.battleTrapProp.isHidden(): + closestSuit.battleTrapProp.setColorScale(1, 1, 1, 0) + closestSuit.battleTrapProp.show() + newRelativePos = dyingSuit.battleTrapProp.getPos(closestSuit) + newHpr = dyingSuit.battleTrapProp.getHpr(closestSuit) + closestSuit.battleTrapProp.setPos(newRelativePos) + closestSuit.battleTrapProp.setHpr(newHpr) + retval.append(LerpColorScaleInterval(closestSuit.battleTrapProp, 3.0, Vec4(1, 1, 1, 1))) + else: + notify.debug('could not find closest suit, returning empty sequence') + return retval + + +def createSuitReviveTrack(suit, toon, battle, npcs = []): + suitTrack = Sequence() + suitPos, suitHpr = battle.getActorPosHpr(suit) + if hasattr(suit, 'battleTrapProp') and suit.battleTrapProp and suit.battleTrapProp.getName() == 'traintrack' and not suit.battleTrapProp.isHidden(): + suitTrack.append(createTrainTrackAppearTrack(suit, toon, battle, npcs)) + deathSuit = suit.getLoseActor() + suitTrack.append(Func(insertReviveSuit, suit, deathSuit, battle, suitPos, suitHpr)) + suitTrack.append(ActorInterval(deathSuit, 'lose', duration=SUIT_LOSE_REVIVE_DURATION)) + suitTrack.append(Func(removeReviveSuit, suit, deathSuit, name='remove-death-suit')) + suitTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.48, duration=0.1)) + suitTrack.append(ActorInterval(suit, 'slip-forward', startTime=2.58)) + suitTrack.append(Func(suit.loop, 'neutral')) + suitTrack.append(Func(suit.setHP, suit.getMaxHP())) + spinningSound = base.loadSfx('phase_3.5/audio/sfx/Cog_Death.ogg') + deathSound = base.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart_%s.ogg' % random.randint(1, 6)) + deathSoundTrack = Sequence(Wait(0.8), SoundInterval(spinningSound, duration=1.2, startTime=1.5, volume=0.2), SoundInterval(spinningSound, duration=3.0, startTime=0.6, volume=0.8), SoundInterval(deathSound, volume=0.32)) + BattleParticles.loadParticles() + smallGears = BattleParticles.createParticleEffect(file='gearExplosionSmall') + singleGear = BattleParticles.createParticleEffect('GearExplosion', numParticles=1) + smallGearExplosion = BattleParticles.createParticleEffect('GearExplosion', numParticles=10) + bigGearExplosion = BattleParticles.createParticleEffect('BigGearExplosion', numParticles=30) + gearPoint = Point3(suitPos.getX(), suitPos.getY(), suitPos.getZ() + suit.height - 0.2) + smallGears.setPos(gearPoint) + singleGear.setPos(gearPoint) + smallGears.setDepthWrite(False) + singleGear.setDepthWrite(False) + smallGearExplosion.setPos(gearPoint) + bigGearExplosion.setPos(gearPoint) + smallGearExplosion.setDepthWrite(False) + bigGearExplosion.setDepthWrite(False) + explosionTrack = Sequence() + explosionTrack.append(Wait(5.4)) + explosionTrack.append(createKapowExplosionTrack(battle, explosionPoint=gearPoint)) + gears1Track = Sequence(Wait(2.1), ParticleInterval(smallGears, battle, worldRelative=0, duration=4.3, cleanup=True), name='gears1Track') + gears2MTrack = Track((0.0, explosionTrack), (0.7, ParticleInterval(singleGear, battle, worldRelative=0, duration=5.7, cleanup=True)), (5.2, ParticleInterval(smallGearExplosion, battle, worldRelative=0, duration=1.2, cleanup=True)), (5.4, ParticleInterval(bigGearExplosion, battle, worldRelative=0, duration=1.0, cleanup=True)), name='gears2MTrack') + toonMTrack = Parallel(name='toonMTrack') + for mtoon in battle.toons: + toonMTrack.append(Sequence(Wait(1.0), ActorInterval(mtoon, 'duck'), ActorInterval(mtoon, 'duck', startTime=1.8), Func(mtoon.loop, 'neutral'))) + + for mtoon in npcs: + toonMTrack.append(Sequence(Wait(1.0), ActorInterval(mtoon, 'duck'), ActorInterval(mtoon, 'duck', startTime=1.8), Func(mtoon.loop, 'neutral'))) + + return Parallel(suitTrack, deathSoundTrack, gears1Track, gears2MTrack, toonMTrack) + + +def createSuitDeathTrack(suit, toon, battle, npcs = []): + suitTrack = Sequence() + suitPos, suitHpr = battle.getActorPosHpr(suit) + if hasattr(suit, 'battleTrapProp') and suit.battleTrapProp and suit.battleTrapProp.getName() == 'traintrack' and not suit.battleTrapProp.isHidden(): + suitTrack.append(createTrainTrackAppearTrack(suit, toon, battle, npcs)) + deathSuit = suit.getLoseActor() + suitTrack.append(Func(notify.debug, 'before insertDeathSuit')) + suitTrack.append(Func(insertDeathSuit, suit, deathSuit, battle, suitPos, suitHpr)) + suitTrack.append(Func(notify.debug, 'before actorInterval lose')) + suitTrack.append(ActorInterval(deathSuit, 'lose', duration=SUIT_LOSE_DURATION)) + suitTrack.append(Func(notify.debug, 'before removeDeathSuit')) + suitTrack.append(Func(removeDeathSuit, suit, deathSuit, name='remove-death-suit')) + suitTrack.append(Func(notify.debug, 'after removeDeathSuit')) + spinningSound = base.loadSfx('phase_3.5/audio/sfx/Cog_Death.ogg') + deathSound = base.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart_%s.ogg' % random.randint(1, 6)) + deathSoundTrack = Sequence(Wait(0.8), SoundInterval(spinningSound, duration=1.2, startTime=1.5, volume=0.2), SoundInterval(spinningSound, duration=3.0, startTime=0.6, volume=0.8), SoundInterval(deathSound, volume=0.32)) + BattleParticles.loadParticles() + smallGears = BattleParticles.createParticleEffect(file='gearExplosionSmall') + singleGear = BattleParticles.createParticleEffect('GearExplosion', numParticles=1) + smallGearExplosion = BattleParticles.createParticleEffect('GearExplosion', numParticles=10) + bigGearExplosion = BattleParticles.createParticleEffect('BigGearExplosion', numParticles=30) + gearPoint = Point3(suitPos.getX(), suitPos.getY(), suitPos.getZ() + suit.height - 0.2) + smallGears.setPos(gearPoint) + singleGear.setPos(gearPoint) + smallGears.setDepthWrite(False) + singleGear.setDepthWrite(False) + smallGearExplosion.setPos(gearPoint) + bigGearExplosion.setPos(gearPoint) + smallGearExplosion.setDepthWrite(False) + bigGearExplosion.setDepthWrite(False) + explosionTrack = Sequence() + explosionTrack.append(Wait(5.4)) + explosionTrack.append(createKapowExplosionTrack(battle, explosionPoint=gearPoint)) + gears1Track = Sequence(Wait(2.1), ParticleInterval(smallGears, battle, worldRelative=0, duration=4.3, cleanup=True), name='gears1Track') + gears2MTrack = Track((0.0, explosionTrack), (0.7, ParticleInterval(singleGear, battle, worldRelative=0, duration=5.7, cleanup=True)), (5.2, ParticleInterval(smallGearExplosion, battle, worldRelative=0, duration=1.2, cleanup=True)), (5.4, ParticleInterval(bigGearExplosion, battle, worldRelative=0, duration=1.0, cleanup=True)), name='gears2MTrack') + toonMTrack = Parallel(name='toonMTrack') + for mtoon in battle.toons: + toonMTrack.append(Sequence(Wait(1.0), ActorInterval(mtoon, 'duck'), ActorInterval(mtoon, 'duck', startTime=1.8), Func(mtoon.loop, 'neutral'))) + + for mtoon in npcs: + toonMTrack.append(Sequence(Wait(1.0), ActorInterval(mtoon, 'duck'), ActorInterval(mtoon, 'duck', startTime=1.8), Func(mtoon.loop, 'neutral'))) + + return Parallel(suitTrack, deathSoundTrack, gears1Track, gears2MTrack, toonMTrack) + + +def createSuitDodgeMultitrack(tDodge, suit, leftSuits, rightSuits): + suitTracks = Parallel() + suitDodgeList, sidestepAnim = avatarDodge(leftSuits, rightSuits, 'sidestep-left', 'sidestep-right') + for s in suitDodgeList: + suitTracks.append(Sequence(ActorInterval(s, sidestepAnim), Func(s.loop, 'neutral'))) + + suitTracks.append(Sequence(ActorInterval(suit, sidestepAnim), Func(suit.loop, 'neutral'))) + suitTracks.append(Func(indicateMissed, suit)) + return Sequence(Wait(tDodge), suitTracks) + + +def createToonDodgeMultitrack(tDodge, toon, leftToons, rightToons): + toonTracks = Parallel() + if len(leftToons) > len(rightToons): + PoLR = rightToons + PoMR = leftToons + else: + PoLR = leftToons + PoMR = rightToons + upper = 1 + 4 * abs(len(leftToons) - len(rightToons)) + if random.randint(0, upper) > 0: + toonDodgeList = PoLR + else: + toonDodgeList = PoMR + if toonDodgeList is leftToons: + sidestepAnim = 'sidestep-left' + for t in toonDodgeList: + toonTracks.append(Sequence(ActorInterval(t, sidestepAnim), Func(t.loop, 'neutral'))) + + else: + sidestepAnim = 'sidestep-right' + toonTracks.append(Sequence(ActorInterval(toon, sidestepAnim), Func(toon.loop, 'neutral'))) + toonTracks.append(Func(indicateMissed, toon)) + return Sequence(Wait(tDodge), toonTracks) + + +def createSuitTeaseMultiTrack(suit, delay = 0.01): + suitTrack = Sequence(Wait(delay), ActorInterval(suit, 'victory', startTime=0.5, endTime=1.9), Func(suit.loop, 'neutral')) + missedTrack = Sequence(Wait(delay + 0.2), Func(indicateMissed, suit, 0.9)) + return Parallel(suitTrack, missedTrack) + + +SPRAY_LEN = 1.5 + +def getSprayTrack(battle, color, origin, target, dScaleUp, dHold, dScaleDown, horizScale = 1.0, vertScale = 1.0, parent = render): + track = Sequence() + sprayProp = globalPropPool.getProp('spray') + sprayScale = hidden.attachNewNode('spray-parent') + sprayRot = hidden.attachNewNode('spray-rotate') + spray = sprayRot + spray.setColor(color) + if color[3] < 1.0: + spray.setTransparency(1) + + def showSpray(sprayScale, sprayRot, sprayProp, origin, target, parent): + if callable(origin): + origin = origin() + if callable(target): + target = target() + sprayRot.reparentTo(parent) + sprayRot.clearMat() + sprayScale.reparentTo(sprayRot) + sprayScale.clearMat() + sprayProp.reparentTo(sprayScale) + sprayProp.clearMat() + sprayRot.setPos(origin) + sprayRot.lookAt(Point3(target)) + + track.append(Func(battle.movie.needRestoreRenderProp, sprayProp)) + track.append(Func(showSpray, sprayScale, sprayRot, sprayProp, origin, target, parent)) + + def calcTargetScale(target = target, origin = origin, horizScale = horizScale, vertScale = vertScale): + if callable(target): + target = target() + if callable(origin): + origin = origin() + distance = Vec3(target - origin).length() + yScale = distance / SPRAY_LEN + targetScale = Point3(yScale * horizScale, yScale, yScale * vertScale) + return targetScale + + track.append(LerpScaleInterval(sprayScale, dScaleUp, calcTargetScale, startScale=PNT3_NEARZERO)) + track.append(Wait(dHold)) + + def prepareToShrinkSpray(spray, sprayProp, origin, target): + if callable(target): + target = target() + if callable(origin): + origin = origin() + sprayProp.setPos(Point3(0.0, -SPRAY_LEN, 0.0)) + spray.setPos(target) + + track.append(Func(prepareToShrinkSpray, spray, sprayProp, origin, target)) + track.append(LerpScaleInterval(sprayScale, dScaleDown, PNT3_NEARZERO)) + + def hideSpray(spray, sprayScale, sprayRot, sprayProp, propPool): + sprayProp.detachNode() + removeProp(sprayProp) + sprayRot.removeNode() + sprayScale.removeNode() + + track.append(Func(hideSpray, spray, sprayScale, sprayRot, sprayProp, globalPropPool)) + track.append(Func(battle.movie.clearRenderProp, sprayProp)) + return track + + +T_HOLE_LEAVES_HAND = 1.708 +T_TELEPORT_ANIM = 3.3 +T_HOLE_CLOSES = 0.3 + +def getToonTeleportOutInterval(toon): + holeActors = toon.getHoleActors() + holes = [holeActors[0], holeActors[1]] + hole = holes[0] + hole2 = holes[1] + hands = toon.getRightHands() + delay = T_HOLE_LEAVES_HAND + dur = T_TELEPORT_ANIM + holeTrack = Sequence() + holeTrack.append(Func(showProps, holes, hands)) + (holeTrack.append(Wait(0.5)),) + holeTrack.append(Func(base.playSfx, toon.getSoundTeleport())) + holeTrack.append(Wait(delay - 0.5)) + holeTrack.append(Func(hole.reparentTo, toon)) + holeTrack.append(Func(hole2.reparentTo, hidden)) + holeAnimTrack = Sequence() + holeAnimTrack.append(ActorInterval(hole, 'hole', duration=dur)) + holeAnimTrack.append(Func(hideProps, holes)) + runTrack = Sequence(ActorInterval(toon, 'teleport', duration=dur), Wait(T_HOLE_CLOSES), Func(toon.detachNode)) + return Parallel(runTrack, holeAnimTrack, holeTrack) + + +def getToonTeleportInInterval(toon): + hole = toon.getHoleActors()[0] + holeAnimTrack = Sequence() + holeAnimTrack.append(Func(toon.detachNode)) + holeAnimTrack.append(Func(hole.reparentTo, toon)) + pos = Point3(0, -2.4, 0) + holeAnimTrack.append(Func(hole.setPos, toon, pos)) + holeAnimTrack.append(ActorInterval(hole, 'hole', startTime=T_TELEPORT_ANIM, endTime=T_HOLE_LEAVES_HAND)) + holeAnimTrack.append(ActorInterval(hole, 'hole', startTime=T_HOLE_LEAVES_HAND, endTime=T_TELEPORT_ANIM)) + holeAnimTrack.append(Func(hole.reparentTo, hidden)) + delay = T_TELEPORT_ANIM - T_HOLE_LEAVES_HAND + jumpTrack = Sequence(Wait(delay), Func(toon.reparentTo, render), ActorInterval(toon, 'jump')) + return Parallel(holeAnimTrack, jumpTrack) + + +def getSuitRakeOffset(suit): + suitName = suit.getStyleName() + if suitName == 'gh': + return 1.4 + elif suitName == 'f': + return 1.0 + elif suitName == 'cc': + return 0.7 + elif suitName == 'tw': + return 1.3 + elif suitName == 'bf': + return 1.0 + elif suitName == 'sc': + return 0.8 + elif suitName == 'ym': + return 0.1 + elif suitName == 'mm': + return 0.05 + elif suitName == 'tm': + return 0.07 + elif suitName == 'nd': + return 0.07 + elif suitName == 'pp': + return 0.04 + elif suitName == 'bc': + return 0.36 + elif suitName == 'b': + return 0.41 + elif suitName == 'dt': + return 0.31 + elif suitName == 'ac': + return 0.39 + elif suitName == 'ds': + return 0.41 + elif suitName == 'hh': + return 0.8 + elif suitName == 'cr': + return 2.1 + elif suitName == 'tbc': + return 1.4 + elif suitName == 'bs': + return 0.4 + elif suitName == 'sd': + return 1.02 + elif suitName == 'le': + return 1.3 + elif suitName == 'bw': + return 1.4 + elif suitName == 'nc': + return 0.6 + elif suitName == 'mb': + return 1.85 + elif suitName == 'ls': + return 1.4 + elif suitName == 'rb': + return 1.6 + elif suitName == 'ms': + return 0.7 + elif suitName == 'tf': + return 0.75 + elif suitName == 'm': + return 0.9 + elif suitName == 'mh': + return 1.3 + else: + notify.warning('getSuitRakeOffset(suit) - Unknown suit name: %s' % suitName) + return 0 + + +def startSparksIval(tntProp): + tip = tntProp.find('**/joint_attachEmitter') + sparks = BattleParticles.createParticleEffect(file='tnt') + return Func(sparks.start, tip) + + +def indicateMissed(actor, duration = 1.1, scale = 0.7): + actor.showHpString(TTLocalizer.AttackMissed, duration=duration, scale=scale) + + +def createKapowExplosionTrack(parent, explosionPoint = None, scale = 1.0): + explosionTrack = Sequence() + explosion = loader.loadModel('phase_3.5/models/props/explosion.bam') + explosion.setBillboardPointEye() + explosion.setDepthWrite(False) + if not explosionPoint: + explosionPoint = Point3(0, 3.6, 2.1) + explosionTrack.append(Func(explosion.reparentTo, parent)) + explosionTrack.append(Func(explosion.setPos, explosionPoint)) + explosionTrack.append(Func(explosion.setScale, 0.4 * scale)) + explosionTrack.append(Wait(0.6)) + explosionTrack.append(Func(removeProp, explosion)) + return explosionTrack + + +def createSuitStunInterval(suit, before, after): + p1 = Point3(0) + p2 = Point3(0) + stars = globalPropPool.getProp('stun') + stars.setColor(1, 1, 1, 1) + stars.adjustAllPriorities(100) + head = suit.getHeadParts()[0] + head.calcTightBounds(p1, p2) + return Sequence(Wait(before), Func(stars.reparentTo, head), Func(stars.setZ, max(0.0, p2[2] - 1.0)), Func(stars.loop, 'stun'), Wait(after), Func(stars.removeNode)) + + +def calcAvgSuitPos(throw): + battle = throw['battle'] + avgSuitPos = Point3(0, 0, 0) + numTargets = len(throw['target']) + for i in xrange(numTargets): + suit = throw['target'][i]['suit'] + avgSuitPos += suit.getPos(battle) + + avgSuitPos /= numTargets + return avgSuitPos diff --git a/toontown/battle/ParticleDefs.py b/toontown/battle/ParticleDefs.py new file mode 100755 index 00000000..3f85cbe7 --- /dev/null +++ b/toontown/battle/ParticleDefs.py @@ -0,0 +1,4904 @@ +from direct.particles import Particles, ForceGroup +from panda3d.core import * + +ParticleTable = {} + +def particle(func): + ParticleTable[func.func_name] = func + +@particle +def gearExplosion(self): + self.reset() + self.setPos(0.000, 0.000, 4.600) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(40) + p0.setBirthRate(0.1000) + p0.setLitterSize(40) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(4.2000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/gear") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.1600) + p0.renderer.setFinalXScale(0.160) + p0.renderer.setInitialYScale(0.1600) + p0.renderer.setFinalYScale(0.160) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(9.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 9.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -2.0000)) + + p0.emitter.setRadius(3.2282) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -79.0000), LinearDistanceForce.FTONEOVERRSQUARED, 15.9701, 95.0100, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + +@particle +def gearExplosionSmall(self): + self.reset() + self.setPos(0.000, 0.000, 4.600) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(6) + p0.setBirthRate(0.4000) + p0.setLitterSize(2) + p0.setLitterSpread(1) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.5000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/gear") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.112) + p0.renderer.setFinalXScale(0.112) + p0.renderer.setInitialYScale(0.112) + p0.renderer.setFinalYScale(0.112) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(9.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 9.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -2.0000)) + + p0.emitter.setRadius(3.2282) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -79.0000), LinearDistanceForce.FTONEOVERRSQUARED, 15.9701, 95.0100, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def gearExplosionBig(self): + self.reset() + self.setPos(0.000, 0.000, 4.600) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(40) + p0.setBirthRate(0.1000) + p0.setLitterSize(40) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(4.2000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/gear") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.1600) + p0.renderer.setFinalXScale(0.160) + p0.renderer.setInitialYScale(0.1600) + p0.renderer.setFinalYScale(0.160) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(15.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 18.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -2.0000)) + + p0.emitter.setRadius(1.6282) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -79.0000), LinearDistanceForce.FTONEOVERRSQUARED, 15.9701, 95.0100, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def poundkey(self): + self.reset() + self.setPos(-0.500, 1.000, 3.100) + self.setHpr(-180.000, -0.000, 180.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(40) + p0.setBirthRate(0.20) + p0.setLitterSize(3) + p0.setLitterSpread(1) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.5000) + p0.factory.setLifespanSpread(0.2000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(20.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/poundsign") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.0000) + p0.renderer.setFinalXScale(0.600) + p0.renderer.setInitialYScale(0.0000) + p0.renderer.setFinalYScale(0.600) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(10.0000) + p0.emitter.setAmplitudeSpread(3.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 4.0000, 0.0000)) + + p0.emitter.setRadius(0.200) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearVectorForce(Vec3(0.0000, 0.0000, 0.0000), 100.0000, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + force0 = LinearJitterForce(4.5449, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def shred(self): + self.reset() + self.setPos(0.000, 3.000, 2.300) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(60) + p0.setBirthRate(0.0600) + p0.setLitterSize(3) + p0.setLitterSpread(1) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.9000) + p0.factory.setLifespanSpread(0.4000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.2000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/roll-o-dex") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.0160) + p0.renderer.setFinalXScale(0.0240) + p0.renderer.setInitialYScale(0.3200) + p0.renderer.setFinalYScale(0.0800) + p0.renderer.setNonanimatedTheta(5.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(5.0000) + p0.emitter.setAmplitudeSpread(1.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 3.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -7.0000, 0.0000)) + + p0.emitter.setRadius(0.6000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearVectorForce(Vec3(0.0000, 0.0000, 5.0000), 1.0000, 0) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearSinkForce(Point3(0.0000, 0.0000, -8.0000), LinearDistanceForce.FTONEOVERRSQUARED, 14.5479, 155.9407, 1) + force1.setActive(1) + f0.addForce(force1) + force2 = LinearNoiseForce(1.7000, 0) + force2.setActive(1) + f0.addForce(force2) + force3 = LinearJitterForce(12.5698, 0) + force3.setActive(1) + f0.addForce(force3) + self.addForceGroup(f0) + + +@particle +def withdrawal(self): + self.reset() + self.setPos(0.000, 10.000, 2.500) + self.setHpr(-180.000, 0.000, 0.000) + self.setScale(4.000, 4.000, 4.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("DiscEmitter") + p0.setPoolSize(150) + p0.setBirthRate(0.0200) + p0.setLitterSize(10) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.4000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAIN) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/snow-particle") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.04) + p0.renderer.setFinalXScale(0.3125) + p0.renderer.setInitialYScale(0.03) + p0.renderer.setFinalYScale(0.25) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(-0.4000) + p0.emitter.setAmplitudeSpread(0.1000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 1.5000, 0.0000)) + + p0.emitter.setRadius(1.7000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearVectorForce(Vec3(0.0000, 1.0000, 0.0000), 1.0000, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def mumboJumboSmother(self): + self.reset() + self.setPos(0.000, 0.000, 3.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereSurfaceEmitter") + p0.setPoolSize(4) + p0.setBirthRate(0.1100) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.5000) + p0.factory.setLifespanSpread(0.0300) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/mumbojumbo-iron") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.40) + p0.renderer.setFinalXScale(0.10) + p0.renderer.setInitialYScale(0.20) + p0.renderer.setFinalYScale(0.05) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(-5.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(1.5000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(37.2697, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def buzzWord(self): + self.reset() + self.setPos(0.000, 2.000, 3.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(7) + p0.setBirthRate(0.2000) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.0000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/buzzwords-crash") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.300) + p0.renderer.setFinalXScale(0.070) + p0.renderer.setInitialYScale(0.200) + p0.renderer.setFinalYScale(0.050) + p0.renderer.setNonanimatedTheta(20.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(8.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 7.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -3.0000, 0.0000)) + + p0.emitter.setRadius(0.0010) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(64.5449, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def penSpill(self): + self.reset() + self.setPos(0.000, 0.000, -0.600) + self.setHpr(0.000, 0.000, -90.000) + self.setScale(1.100, 1.100, 1.100) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(70) + p0.setBirthRate(0.1000) + p0.setLitterSize(2) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.5000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/raindrop") + p0.renderer.setColor(Vec4(0, 0, 0, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.05) + p0.renderer.setFinalXScale(0.000) + p0.renderer.setInitialYScale(0.05) + p0.renderer.setFinalYScale(0.000) + p0.renderer.setNonanimatedTheta(90.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(3.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -2.0000)) + + p0.emitter.setRadius(0.2282) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -99.0000), LinearDistanceForce.FTONEOVERRSQUARED, 15.9701, 95.0100, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def fingerwag(self): + self.reset() + self.setPos(0.167, 0.692, 3.731) + self.setHpr(90.000, -36.310, -0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("PointEmitter") + p0.setPoolSize(250) + p0.setBirthRate(0.2000) + p0.setLitterSize(2) + p0.setLitterSpread(2) + p0.setSystemLifespan(2.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.6000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(410.7267) + p0.factory.setTerminalVelocitySpread(2.3816) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(0.86) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/blah") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.400) + p0.renderer.setFinalXScale(0.0200) + p0.renderer.setInitialYScale(0.200) + p0.renderer.setFinalYScale(0.0200) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPNOBLEND) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETEXPLICIT) + p0.emitter.setAmplitude(3.0000) + p0.emitter.setAmplitudeSpread(2.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setLocation(Point3(0.0000, 0.0000, 0.0000)) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('jfo') + + force0 = LinearJitterForce(4.0000, 0) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearSourceForce(Point3(0.0000, 0.0000, 0.0000), LinearDistanceForce.FTONEOVERRSQUARED, 0.5000, 1.0000, 0) + force1.setActive(1) + f0.addForce(force1) + force2 = LinearSinkForce(Point3(0.0000, 1.0000, 0.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 1.0000, 1) + force2.setActive(1) + f0.addForce(force2) + self.addForceGroup(f0) + + +@particle +def doubleTalkRight(self): + self.reset() + self.setPos(0.000, 3.000, 3.000) + self.setHpr(-55.000, 0.000, 0.000) + self.setScale(3.000, 3.000, 3.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(2) + p0.setBirthRate(0.7000) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.7000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/doubletalk-good") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(1.5) + p0.renderer.setFinalXScale(1.5) + p0.renderer.setInitialYScale(1.5) + p0.renderer.setFinalYScale(1.5) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(12.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.6000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -8.0000, 0.0000)) + + p0.emitter.setRadius(0.0500) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(-6.000, -3.0000, 0.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 1.5000, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def filibusterSpray(self): + self.reset() + self.setPos(0.000, 3.000, 4.000) + self.setHpr(0.000, 55.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(1) + p0.setBirthRate(0.400) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.2700) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/filibuster-cut") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.3*1.5) + p0.renderer.setFinalXScale(0.75*1.5) + p0.renderer.setInitialYScale(0.15*1.5) + p0.renderer.setFinalYScale(0.25*1.5) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(5.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 8.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -1.0000, 0.0000)) + + p0.emitter.setRadius(0.1000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, -9.0000, -11.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 1.3661, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def fingerwag2(self): + self.reset() + self.setPos(0.228, 0.880, 4.314) + self.setHpr(-2.862, -36.310, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("RingEmitter") + p0.setPoolSize(250) + p0.setBirthRate(0.3000) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(2.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.6000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(410.7267) + p0.factory.setTerminalVelocitySpread(2.3816) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(0.86) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/blah") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.400) + p0.renderer.setFinalXScale(0.0200) + p0.renderer.setInitialYScale(0.200) + p0.renderer.setFinalYScale(0.0200) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPNOBLEND) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(1.0000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('jfo') + + self.addForceGroup(f0) + + +@particle +def schmoozeLowerSpray(self): + self.reset() + self.setPos(0.000, 6.600, 3.290) + self.setHpr(0.000, -55.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(1) + p0.setBirthRate(0.400) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.900) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/schmooze-master") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.7) + p0.renderer.setFinalXScale(0.07) + p0.renderer.setInitialYScale(0.35) + p0.renderer.setFinalYScale(0.07) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(5.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 11.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -1.0000, 0.0000)) + + p0.emitter.setRadius(0.1000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, -23.0000, 9.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 1.3661, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def brainStorm(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("DiscEmitter") + p0.setPoolSize(70) + p0.setBirthRate(0.4) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/brainstorm-box") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.600) + p0.renderer.setFinalXScale(0.0400) + p0.renderer.setInitialYScale(0.30) + p0.renderer.setFinalYScale(0.0400 ) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(5.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 5.0000)) + + p0.emitter.setRadius(0.5000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(15.0000, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def numberSpray(self): + self.reset() + self.setPos(0.000, 2.700, 3.900) + self.setHpr(-180.000, 80.000, -180.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(1) + p0.setBirthRate(0.2000) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(2.1000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/fire") + p0.renderer.setColor(Vec4(0.00, 0.00, 0.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.125) + p0.renderer.setFinalXScale(0.5) + p0.renderer.setInitialYScale(0.2) + p0.renderer.setFinalYScale(1.0) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(5.1000) + p0.emitter.setAmplitudeSpread(2.5000) + p0.emitter.setOffsetForce(Vec3(0.0000, 9.1000, -4.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -4.0000, 0.0000)) + + p0.emitter.setRadius(0.500) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -3.5000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 2.5308, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearVectorForce(Vec3(0.0000, -10.0000, 0.0000), 1.0000, 0) + force1.setActive(1) + f0.addForce(force1) + self.addForceGroup(f0) + + +@particle +def demotionUnFreeze(self): + self.reset() + self.setPos(0.000, 0.000, 3.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(2.000, 2.000, 2.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(70) + p0.setBirthRate(0.0200) + p0.setLitterSize(10) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.5000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/roll-o-dex") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.04) + p0.renderer.setFinalXScale(0.000) + p0.renderer.setInitialYScale(0.04) + p0.renderer.setFinalYScale(0.000) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(4.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(0.6000) + self.addParticles(p0) + + +@particle +def fillWithLeadSmother(self): + self.reset() + self.setPos(0.000, 0.000, 3.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereSurfaceEmitter") + p0.setPoolSize(100) + p0.setBirthRate(0.0400) + p0.setLitterSize(20) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.5000) + p0.factory.setLifespanSpread(0.0300) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/mumbojumbo-iron") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.0100) + p0.renderer.setFinalXScale(0.0100) + p0.renderer.setInitialYScale(0.0100) + p0.renderer.setFinalYScale(0.0100) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(-5.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(1.1000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(37.2697, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def downsizeSpray(self): + self.reset() + self.setPos(0.000, 2.900, 3.400) + self.setHpr(0.000, 60.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(50) + p0.setBirthRate(0.1000) + p0.setLitterSize(7) + p0.setLitterSpread(2) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.3000) + p0.factory.setLifespanSpread(0.2000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/snow-particle") + p0.renderer.setColor(Vec4(1.00, 1.00, 0.00, 0.80)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.0750) + p0.renderer.setFinalXScale(0.0375) + p0.renderer.setInitialYScale(0.055) + p0.renderer.setFinalYScale(0.024) + p0.renderer.setNonanimatedTheta(20.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(4.9000) + p0.emitter.setAmplitudeSpread(0.3000) + p0.emitter.setOffsetForce(Vec3(0.0000, 7.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -3.0000, 0.0000)) + + p0.emitter.setRadius(0.0010) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -5.3000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 2.5308, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearVectorForce(Vec3(0.0000, -7.0000, 0.0000), 1.0000, 0) + force1.setActive(1) + f0.addForce(force1) + force3 = LinearJitterForce(8.5449, 0) + force3.setActive(1) + f0.addForce(force3) + self.addForceGroup(f0) + + +@particle +def fillWithLeadSpray(self): + self.reset() + self.setPos(0.000, 2.000, 2.300) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(150) + p0.setBirthRate(0.0400) + p0.setLitterSize(45) + p0.setLitterSpread(1) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(2.9000) + p0.factory.setLifespanSpread(0.4000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.2000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/roll-o-dex") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.010) + p0.renderer.setFinalXScale(0.010) + p0.renderer.setInitialYScale(0.010) + p0.renderer.setFinalYScale(0.010) + p0.renderer.setNonanimatedTheta(5.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(5.0000) + p0.emitter.setAmplitudeSpread(1.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 5.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -7.0000, 0.0000)) + + p0.emitter.setRadius(0.01000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearVectorForce(Vec3(0.0000, 0.0000, 5.0000), 1.0000, 0) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearSinkForce(Point3(0.0000, 0.0000, -8.0000), LinearDistanceForce.FTONEOVERRSQUARED, 14.5479, 155.9407, 1) + force1.setActive(1) + f0.addForce(force1) + force2 = LinearNoiseForce(1.7000, 0) + force2.setActive(1) + f0.addForce(force2) + force3 = LinearJitterForce(12.5698, 0) + force3.setActive(1) + f0.addForce(force3) + self.addForceGroup(f0) + + +@particle +def reorgCloud(self): + self.reset() + self.setPos(0.000, 0.000, 3.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(2.000, 2.000, 2.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereSurfaceEmitter") + p0.setPoolSize(70) + p0.setBirthRate(0.0200) + p0.setLitterSize(10) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.5000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/snow-particle") + p0.renderer.setColor(Vec4(1.00, 0.00, 0.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.003) + p0.renderer.setFinalXScale(0.000) + p0.renderer.setInitialYScale(0.003) + p0.renderer.setFinalYScale(0.000) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(-1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(1.0000) + self.addParticles(p0) + + +@particle +def demotionFreeze(self): + self.reset() + self.setPos(0.000, 0.000, 3.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(2.000, 2.000, 2.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereSurfaceEmitter") + p0.setPoolSize(70) + p0.setBirthRate(0.0200) + p0.setLitterSize(10) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.5000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/roll-o-dex") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.04) + p0.renderer.setFinalXScale(0.000) + p0.renderer.setInitialYScale(0.04) + p0.renderer.setFinalYScale(0.000) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(-1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(1.0000) + self.addParticles(p0) + + +@particle +def demotionSpray(self): + self.reset() + self.setPos(0.000, 4.000, 3.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(150) + p0.setBirthRate(0.0500) + p0.setLitterSize(7) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.8000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/roll-o-dex") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.04) + p0.renderer.setFinalXScale(0.009) + p0.renderer.setInitialYScale(0.04) + p0.renderer.setFinalYScale(0.009) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(3.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 6.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -4.0000, 0.0000)) + + p0.emitter.setRadius(0.8900) + self.addParticles(p0) + + +@particle +def powertrip2(self): + self.reset() + self.setPos(-2.000, 2.500, 2.200) + self.setHpr(-90.000, 0.000, 0.000) + self.setScale(4.800, 4.800, 4.800) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(100) + p0.setBirthRate(0.0800) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.2500) + p0.factory.setLifespanSpread(0.050) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(0.1, 0.95, 0.2, 1.00)) + p0.renderer.setEdgeColor(Vec4(0, 0, 0, 1.00)) + p0.renderer.setBirthRadius(0.1000) + p0.renderer.setDeathRadius(15.0000) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(2.4000) + p0.emitter.setAmplitudeSpread(1.1000) + p0.emitter.setOffsetForce(Vec3(0.0000, 1.1000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -4.0000, 0.0000)) + + p0.emitter.setRadius(0.120) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(-10.0000, 0.0000, 0.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 2.5308, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearVectorForce(Vec3(0.0000, 0.0000, 0.0000), 1.0000, 0) + force1.setActive(1) + f0.addForce(force1) + force2 = LinearJitterForce(4.5449, 0) + force2.setActive(1) + f0.addForce(force2) + self.addForceGroup(f0) + + +@particle +def rollodexVortex(self): + self.reset() + self.setPos(-0.003, 2.465, 3.714) + self.setHpr(84.924, 13.378, 56.334) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("TangentRingEmitter") + p0.setPoolSize(250) + p0.setBirthRate(0.1000) + p0.setLitterSize(5) + p0.setLitterSpread(3) + p0.setSystemLifespan(5.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.5000) + p0.factory.setLifespanSpread(0.2500) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(40.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/rollodex-card") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.4) + p0.renderer.setFinalXScale(0.4) + p0.renderer.setInitialYScale(0.3) + p0.renderer.setFinalYScale(0.3) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(3.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(0.7500) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forward') + + force0 = LinearSourceForce(Point3(0.0000, 0.0000, 0.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 1.0000, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearSinkForce(Point3(0.0000, 0.0000, 0.0000), LinearDistanceForce.FTONEOVERRSQUARED, 5.0000, 6.0000, 0) + force1.setActive(0) + f0.addForce(force1) + force2 = LinearCylinderVortexForce(1.0000, 1.0000, 15.0000, 1.0000, 0) + force2.setActive(1) + f0.addForce(force2) + force3 = LinearSourceForce(Point3(0.5000, 0.0000, 1.0000), LinearDistanceForce.FTONEOVERRCUBED, 4.0000, 4.0000, 1) + force3.setActive(1) + f0.addForce(force3) + self.addForceGroup(f0) + + +@particle +def pixieExplode(self): + self.reset() + self.setPos(2.500, 0.000, 2.500) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(3.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(100) + p0.setBirthRate(0.1000) + p0.setLitterSize(7) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.5000) + p0.factory.setLifespanSpread(0.2000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setEdgeColor(Vec4(0.00, 0.00, 1.00, 1.00)) + p0.renderer.setBirthRadius(0.0400) + p0.renderer.setDeathRadius(0.0000) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETEXPLICIT) + p0.emitter.setAmplitude(1.0000) + p0.emitter.setAmplitudeSpread(0.0100) + p0.emitter.setOffsetForce(Vec3(-0.1000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.5000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -4.0000, 0.0000)) + + p0.emitter.setRadius(0.1000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(2.0000, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def guiltTrip(self): + self.reset() + self.setPos(-2.000, 2.500, 2.200) + self.setHpr(-90.000, 0.000, 0.000) + self.setScale(4.800, 4.800, 4.800) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(100) + p0.setBirthRate(0.0800) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.4000) + p0.factory.setLifespanSpread(0.000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(1.0, 0, 0, 0.9)) + p0.renderer.setEdgeColor(Vec4(0.8, 0.8, 0.8, 0.4)) + p0.renderer.setBirthRadius(0.1000) + p0.renderer.setDeathRadius(15.0000) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(2.4000) + p0.emitter.setAmplitudeSpread(1.1000) + p0.emitter.setOffsetForce(Vec3(0.0000, 1.1000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -4.0000, 0.0000)) + + p0.emitter.setRadius(0.120) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(14.5449, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + +@particle +def soundBreak(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("ZSpinParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("PointEmitter") + p0.setPoolSize(7) + p0.setBirthRate(0.0500) + p0.setLitterSize(3) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.5000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.factory.setInitialAngle(0.0000) + p0.factory.setInitialAngleSpread(180.0000) + p0.factory.enableAngularVelocity(1) + p0.factory.setAngularVelocity(0.0000) + p0.factory.setAngularVelocitySpread(0.0000) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAINOUT) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setTextureFromNode("phase_5/models/props/uberSoundEffects", "**/break") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(1) + p0.renderer.setInitialXScale(1.5000) + p0.renderer.setFinalXScale(1.5000) + p0.renderer.setInitialYScale(0.0000) + p0.renderer.setFinalYScale(9.0000) + p0.renderer.setNonanimatedTheta(319.3987) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setLocation(Point3(0.0000, 0.0000, 0.0000)) + self.addParticles(p0) + + +@particle +def pixiePoof(self): + self.reset() + self.setPos(0.000, 0.000, 3.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(200) + p0.setBirthRate(0.0200) + p0.setLitterSize(2) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.0000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setEdgeColor(Vec4(0.04, 0.04, 1.00, 1.00)) + p0.renderer.setBirthRadius(0.0272) + p0.renderer.setDeathRadius(0.1872) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(0.200) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + +@particle +def waterfall(self): + self.reset() + self.setPos(0.000, 5.000, 2.300) + self.setHpr(0.000, -45.000, 0.000) + self.setScale(4.000, 4.000, 4.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("DiscEmitter") + p0.setPoolSize(50) + p0.setBirthRate(0.0500) + p0.setLitterSize(4) + p0.setLitterSpread(1) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.1000) + p0.factory.setLifespanSpread(0.1000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(0.1, 0.95, 0.2, 1.00)) + p0.renderer.setEdgeColor(Vec4(0.00, 0.00, 0.00, 1.00)) + p0.renderer.setBirthRadius(0.0200) + p0.renderer.setDeathRadius(0.0600) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(3.5000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -3.0000, 0.0000)) + + p0.emitter.setRadius(0.2000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -30.0000), LinearDistanceForce.FTONEOVERRSQUARED, 3.0400, 1.5000, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def headShrinkCloud(self): + self.reset() + self.setPos(0.000, 0.000, 8.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("SphereSurfaceEmitter") + p0.setPoolSize(60) + p0.setBirthRate(0.100) + p0.setLitterSize(5) + p0.setLitterSpread(3) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.3000) + p0.factory.setLifespanSpread(0.100) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(1, 0.84, 0, 1.00)) + p0.renderer.setEdgeColor(Vec4(1, 1, 1, 0.3)) + p0.renderer.setBirthRadius(0.1500) + p0.renderer.setDeathRadius(0.0000) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(4.0000) + p0.emitter.setAmplitudeSpread(2.5000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(0.0200) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(33.2697, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def firedFlame(self): + self.reset() + self.setPos(0.000, 0.000, 0.500) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(2.500, 4.500, 2.500) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(60) + p0.setBirthRate(0.0220) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.500) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/fire") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.15) + p0.renderer.setFinalXScale(0.00025) + p0.renderer.setInitialYScale(0.30) + p0.renderer.setFinalYScale(0.00025) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 4.800)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -30.0000)) + + p0.emitter.setRadius(0.4000) + self.addParticles(p0) + + +@particle +def spinSpray(self): + self.reset() + self.setPos(0.000, 6.500, 3.200) + self.setHpr(50.000, -0.000, -90.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(70) + p0.setBirthRate(0.2000) + p0.setLitterSize(9) + p0.setLitterSpread(4) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.2000) + p0.factory.setLifespanSpread(0.2000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/snow-particle") + p0.renderer.setColor(Vec4(1.00, 0.00, 0.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.025) + p0.renderer.setFinalXScale(0.05) + p0.renderer.setInitialYScale(0.025) + p0.renderer.setFinalYScale(0.05) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(6.0000) + p0.emitter.setAmplitudeSpread(0.7000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -4.0000, 0.0000)) + + p0.emitter.setRadius(0.200) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -3.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 2.5308, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearVectorForce(Vec3(0.0000, 0.0000, 0.0000), 1.0000, 0) + force1.setActive(1) + f0.addForce(force1) + self.addForceGroup(f0) + + +@particle +def confetti(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("DiscEmitter") + p0.setPoolSize(350) + p0.setBirthRate(0.0200) + p0.setLitterSize(5) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.7000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/spark") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.0070) + p0.renderer.setFinalXScale(0.0500) + p0.renderer.setInitialYScale(0.0070) + p0.renderer.setFinalYScale(0.0500) + p0.renderer.setNonanimatedTheta(145.0080) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(0.0100) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('gravity') + + force0 = LinearJitterForce(5.0000, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearSinkForce(Point3(0.0000, 0.0000, -0.8000), LinearDistanceForce.FTONEOVERRSQUARED, 0.5000, 1.0000, 1) + force1.setVectorMasks(1, 1, 1) + force1.setActive(1) + f0.addForce(force1) + self.addForceGroup(f0) + +@particle +def downsizeCloud(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(100) + p0.setBirthRate(0.2000) + p0.setLitterSize(12) + p0.setLitterSpread(4) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.3000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/snow-particle") + p0.renderer.setColor(Vec4(1.00, 1.00, 0.00, 0.80)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.015) + p0.renderer.setFinalXScale(0.075) + p0.renderer.setInitialYScale(0.0075) + p0.renderer.setFinalYScale(0.055) + p0.renderer.setNonanimatedTheta(20.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(-1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(2.70) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(14.5449, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def synergyWaterfall(self): + self.reset() + self.setPos(0.000, 5.000, 2.300) + self.setHpr(0.000, -45.000, 0.000) + self.setScale(4.000, 4.000, 4.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("DiscEmitter") + p0.setPoolSize(50) + p0.setBirthRate(0.0500) + p0.setLitterSize(4) + p0.setLitterSpread(1) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.1000) + p0.factory.setLifespanSpread(0.1000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/dollar-sign") + p0.renderer.setColor(Vec4(0.00, 1.00, 0.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.2) + p0.renderer.setFinalXScale(0.2) + p0.renderer.setInitialYScale(0.2) + p0.renderer.setFinalYScale(0.2) + p0.renderer.setNonanimatedTheta(20.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(3.5000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -3.0000, 0.0000)) + + p0.emitter.setRadius(0.2000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -15.0000), LinearDistanceForce.FTONEOVERRSQUARED, 3.0400, 1.5000, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def calculate(self): + self.reset() + self.setPos(0.000, 2.5, 3.5) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(30) + p0.setBirthRate(0.4000) + p0.setLitterSize(3) + p0.setLitterSpread(1) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.9000) + p0.factory.setLifespanSpread(0.2000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.3000) + p0.factory.setTerminalVelocityBase(8.0000) + p0.factory.setTerminalVelocitySpread(4.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/audit-plus") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.0000) + p0.renderer.setFinalXScale(0.400) + p0.renderer.setInitialYScale(0.0000) + p0.renderer.setFinalYScale(0.400) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(11.0000) + p0.emitter.setAmplitudeSpread(2.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -2.0000, 0.0000)) + + p0.emitter.setRadius(0.5000) + self.addParticles(p0) + + +@particle +def freezeAssets(self): + self.reset() + self.setPos(0.000, 0.000, -0.200) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("DiscEmitter") + p0.setPoolSize(200) + p0.setBirthRate(0.0800) + p0.setLitterSize(7) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.7000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/snow-particle") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.064) + p0.renderer.setFinalXScale(0.001) + p0.renderer.setInitialYScale(0.064) + p0.renderer.setFinalYScale(0.001) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(8.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 5.0000)) + + p0.emitter.setRadius(0.4500) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(15.0000, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def spriteFiredFlecks(self): + self.reset() + self.setPos(0.000, 0.000, 2.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(60) + p0.setBirthRate(0.200) + p0.setLitterSize(2) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.100) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/roll-o-dex") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.025) + p0.renderer.setFinalXScale(0.000) + p0.renderer.setInitialYScale(0.025) + p0.renderer.setFinalYScale(0.000) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 4.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -4.0000)) + + p0.emitter.setRadius(1.5000) + self.addParticles(p0) + + +@particle +def smile(self): + self.reset() + self.setPos(0.0, 0.0, 2.000) + self.setHpr(85.000, 0.000, 90.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("RingEmitter") + p0.setPoolSize(400) + p0.setBirthRate(0.0200) + p0.setLitterSize(10) + p0.setLitterSpread(0) + p0.setSystemLifespan(1.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.0000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(200.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setEdgeColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setBirthRadius(0.1000) + p0.renderer.setDeathRadius(0.0000) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(1.0000) + self.addParticles(p0) + + +@particle +def trickleLiquidate(self): + self.reset() + self.setPos(0.000, 0.000, -0.200) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(20) + p0.setBirthRate(0.0800) + p0.setLitterSize(3) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.4000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/raindrop") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.06) + p0.renderer.setFinalXScale(0.06) + p0.renderer.setInitialYScale(0.225) + p0.renderer.setFinalYScale(0.225) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(16.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 6.0000)) + + p0.emitter.setRadius(0.4500) + self.addParticles(p0) + + +@particle +def reorgSpray(self): + self.reset() + self.setPos(0.000, 5.700, 2.700) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(150) + p0.setBirthRate(0.0500) + p0.setLitterSize(7) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.8000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/snow-particle") + p0.renderer.setColor(Vec4(1.00, 0.00, 0.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.003) + p0.renderer.setFinalXScale(0.009) + p0.renderer.setInitialYScale(0.003) + p0.renderer.setFinalYScale(0.009) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(3.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 6.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -4.0000, 0.0000)) + + p0.emitter.setRadius(0.8900) + self.addParticles(p0) + + +@particle +def liquidate(self): + self.reset() + self.setPos(0.000, 0.000, -0.200) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(100) + p0.setBirthRate(0.0400) + p0.setLitterSize(3) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.4000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/raindrop") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.06) + p0.renderer.setFinalXScale(0.06) + p0.renderer.setInitialYScale(0.225) + p0.renderer.setFinalYScale(0.225) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(16.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 6.0000)) + + p0.emitter.setRadius(0.4500) + self.addParticles(p0) + + +@particle +def mumboJumboSpray(self): + self.reset() + self.setPos(0.000, 4.000, 4.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 4.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(3) + p0.setBirthRate(0.3000) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.900) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/mumbojumbo-iron") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.40) + p0.renderer.setFinalXScale(0.40) + p0.renderer.setInitialYScale(0.20) + p0.renderer.setFinalYScale(0.20) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(6.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -9.0000, 0.0000)) + + p0.emitter.setRadius(0.7000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(20.4636, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def gearExplosionWide(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(40) + p0.setBirthRate(0.1000) + p0.setLitterSize(40) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(4.2000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/gear") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.1600) + p0.renderer.setFinalXScale(0.1600) + p0.renderer.setInitialYScale(0.1600) + p0.renderer.setFinalYScale(0.1600) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(15.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 10.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -0.5000)) + + p0.emitter.setRadius(1.7500) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -79.0000), LinearDistanceForce.FTONEOVERRSQUARED, 15.9701, 95.0000, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def pixieSpray(self): + self.reset() + self.setPos(2.00, 0.000, 4.00) + self.setHpr(-90.000, 45.000, 0.000) + self.setScale(4.000, 4.000, 4.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("DiscEmitter") + p0.setPoolSize(50) + p0.setBirthRate(0.0500) + p0.setLitterSize(4) + p0.setLitterSpread(1) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.6000) + p0.factory.setLifespanSpread(0.1000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setEdgeColor(Vec4(0.00, 0.00, 1.00, 1.00)) + p0.renderer.setBirthRadius(0.0200) + p0.renderer.setDeathRadius(0.0500) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(3.5000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -3.0000, 0.0000)) + + p0.emitter.setRadius(0.100) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -30.0000), LinearDistanceForce.FTONEOVERRSQUARED, 3.0400, 1.5000, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def synergy(self): + self.reset() + self.setPos(0, 7.8, 0.4) + self.setHpr(90.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("RingEmitter") + p0.setPoolSize(250) + p0.setBirthRate(0.0100) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.6) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/dollar-sign") + p0.renderer.setColor(Vec4(0.00, 1.00, 0.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.2) + p0.renderer.setFinalXScale(0.2) + p0.renderer.setInitialYScale(0.2) + p0.renderer.setFinalYScale(0.2) + p0.renderer.setNonanimatedTheta(20.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(5.0697) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(-4.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(1.8607) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('jfo') + + force0 = LinearJitterForce(1.0000, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def soundWave(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(7.000, 7.000, 7.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("PointEmitter") + p0.setPoolSize(128) + p0.setBirthRate(0.4000) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(10.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(4.0000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(0.0010) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(0.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setTextureFromNode("phase_5/models/props/uberSoundEffects", "**/Circle") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.0000) + p0.renderer.setFinalXScale(3.0000) + p0.renderer.setInitialYScale(0.0000) + p0.renderer.setFinalYScale(3.0000) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(1) + p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETEXPLICIT) + p0.emitter.setAmplitude(0.0100) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setLocation(Point3(0.0000, 0.0000, 0.0000)) + self.addParticles(p0) + + +@particle +def tnt(self): + self.reset() + self.setPos(0.000, 0.000, -0.600) + self.setHpr(0.000, 10.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(40) + p0.setBirthRate(0.1000) + p0.setLitterSize(2) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.2000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/spark") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.3) + p0.renderer.setFinalXScale(0.3) + p0.renderer.setInitialYScale(0.3) + p0.renderer.setFinalYScale(0.03) + p0.renderer.setNonanimatedTheta(20.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(1.5000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -2.0000)) + + p0.emitter.setRadius(0.2282) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -19.0000), LinearDistanceForce.FTONEOVERRSQUARED, 15.9701, 95.0100, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def doubleTalkLeft(self): + self.reset() + self.setPos(0.000, 3.000, 3.000) + self.setHpr(55.000, 0.000, 0.000) + self.setScale(3.000, 3.000, 3.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(2) + p0.setBirthRate(0.7000) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.7000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/doubletalk-double") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(1.50) + p0.renderer.setFinalXScale(1.50) + p0.renderer.setInitialYScale(1.50) + p0.renderer.setFinalYScale(1.50) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(12.000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.6000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -8.0000, 0.0000)) + + p0.emitter.setRadius(0.0500) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(6.000, -3.0000, 0.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 1.5000, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def pixieWall(self): + self.reset() + self.setPos(2.500, 0.000, 2.500) + self.setHpr(-90.000, 90.000, -180.000) + self.setScale(1.50) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("DiscEmitter") + p0.setPoolSize(100) + p0.setBirthRate(0.1000) + p0.setLitterSize(100) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(4.0000) + p0.factory.setLifespanSpread(0.2000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setEdgeColor(Vec4(0.00, 0.00, 1.00, 1.00)) + p0.renderer.setBirthRadius(0.0400) + p0.renderer.setDeathRadius(0.0000) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(2.5000) + p0.emitter.setAmplitudeSpread(0.5000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(0.0000, 0.0000, 1.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -1.0000)) + + p0.emitter.setRadius(0.5000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearNoiseForce(0.0500, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def schmoozeUpperSpray(self): + self.reset() + self.setPos(0.000, 3.000, 4.000) + self.setHpr(0.000, 55.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(1) + p0.setBirthRate(0.400) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.900) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/schmooze-master") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.7) + p0.renderer.setFinalXScale(0.07) + p0.renderer.setInitialYScale(0.35) + p0.renderer.setFinalYScale(0.07) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(5.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 11.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -1.0000, 0.0000)) + + p0.emitter.setRadius(0.1000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, -23.0000, -9.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 1.3661, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def firedBaseFlame(self): + self.reset() + self.setPos(0.000, 0.000, 0.500) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(2.500, 4.500, 2.500) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(150) + p0.setBirthRate(0.0200) + p0.setLitterSize(10) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.100) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/fire") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.15) + p0.renderer.setFinalXScale(0.50) + p0.renderer.setInitialYScale(0.30) + p0.renderer.setFinalYScale(0.50) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 2.200)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -30.0000)) + + p0.emitter.setRadius(0.4000) + self.addParticles(p0) + + +@particle +def headShrinkSpray(self): + self.reset() + self.setPos(0.000, 2.900, 4.200) + self.setHpr(0.000, 60.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(60) + p0.setBirthRate(0.1000) + p0.setLitterSize(4) + p0.setLitterSpread(2) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.15) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(1, 0.84, 0, 1.00)) + p0.renderer.setEdgeColor(Vec4(1, 1, 1, 0.3)) + p0.renderer.setBirthRadius(0.1500) + p0.renderer.setDeathRadius(0.0000) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(12.0000) + p0.emitter.setAmplitudeSpread(0.9000) + p0.emitter.setOffsetForce(Vec3(0.0000, 5.1000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -4.0000, 0.0000)) + + p0.emitter.setRadius(0.4800) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -4.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 2.5308, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearVectorForce(Vec3(0.0000, -7.0000, 0.0000), 1.0000, 0) + force1.setActive(1) + f0.addForce(force1) + self.addForceGroup(f0) + + +@particle +def jargonSpray(self): + self.reset() + self.setPos(0.000, 3.000, 4.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("LineEmitter") + p0.setPoolSize(4) + p0.setBirthRate(0.200) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.0000) + p0.factory.setLifespanSpread(0.2000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/jargon-brow") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.40) + p0.renderer.setFinalXScale(1.60) + p0.renderer.setInitialYScale(0.10) + p0.renderer.setFinalYScale(0.40) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(5.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 4.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -9.0000, 0.0000)) + + p0.emitter.setEndpoint1(Point3(0.0000, 0.0000, 0.0000)) + p0.emitter.setEndpoint2(Point3(0.0000, 0.0000, 0.0000)) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(2.1279, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def legaleseSpray(self): + self.reset() + self.setPos(0.000, 2.000, 3.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(1) + p0.setBirthRate(0.2000) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(3.0000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/buzzwords-crash") + p0.renderer.setColor(Vec4(0.00, 0.00, 0.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(1.0) + p0.renderer.setFinalXScale(1.8) + p0.renderer.setInitialYScale(0.5) + p0.renderer.setFinalYScale(0.9) + p0.renderer.setNonanimatedTheta(20.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(8.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 7.0000, -1.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -3.0000, 0.0000)) + + p0.emitter.setRadius(0.0010) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(19.5449, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def powertrip(self): + self.reset() + self.setPos(-2.000, 2.500, 2.200) + self.setHpr(-90.000, 0.000, 0.000) + self.setScale(4.800, 4.800, 4.800) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(100) + p0.setBirthRate(0.0800) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.2500) + p0.factory.setLifespanSpread(0.050) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(0.1, 0.95, 0.2, 1.00)) + p0.renderer.setEdgeColor(Vec4(0, 0, 0, 1.00)) + p0.renderer.setBirthRadius(0.1000) + p0.renderer.setDeathRadius(15.0000) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(2.4000) + p0.emitter.setAmplitudeSpread(1.1000) + p0.emitter.setOffsetForce(Vec3(0.0000, 1.1000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -4.0000, 0.0000)) + + p0.emitter.setRadius(0.120) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(10.0000, 0.0000, 0.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 2.5308, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearVectorForce(Vec3(0.0000, 0.0000, 0.0000), 1.0000, 0) + force1.setActive(1) + f0.addForce(force1) + force2 = LinearJitterForce(4.5449, 0) + force2.setActive(1) + f0.addForce(force2) + self.addForceGroup(f0) + + +@particle +def spinEffect(self): + self.reset() + self.setScale(0.040, 0.040, 0.040) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(100) + p0.setBirthRate(0.1000) + p0.setLitterSize(6) + p0.setLitterSpread(2) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.3000) + p0.factory.setLifespanSpread(0.3000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/snow-particle") + p0.renderer.setColor(Vec4(1.00, 0.00, 0.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.05) + p0.renderer.setFinalXScale(0.05) + p0.renderer.setInitialYScale(0.05) + p0.renderer.setFinalYScale(0.05) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(4.000*1.2) + p0.emitter.setAmplitudeSpread(1.000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -4.0000)) + + p0.emitter.setRadius(0.300) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 1.2000, 0.0000), LinearDistanceForce.FTONEOVERRSQUARED,1.0000, 20, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearJitterForce(5.0000, 0) + force1.setActive(1) + f0.addForce(force1) + self.addForceGroup(f0) + + +@particle +def restrainingOrderCloud(self): + self.reset() + self.setPos(0.000, 4.000, 3.000) + self.setHpr(-180.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(60) + p0.setBirthRate(0.0001) + p0.setLitterSize(60) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.2000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/roll-o-dex") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.02) + p0.renderer.setFinalXScale(0.001) + p0.renderer.setInitialYScale(0.02) + p0.renderer.setFinalYScale(0.001) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(3.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 6.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -18.0000, 0.0000)) + + p0.emitter.setRadius(0.8900) + self.addParticles(p0) + + +@particle +def numberSpill(self): + self.reset() + self.setPos(0.900, 2.100, 1.90) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.100, 1.100, 1.100) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(6) + p0.setBirthRate(0.3000) + p0.setLitterSize(2) + p0.setLitterSpread(1) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(5.8000) + p0.factory.setLifespanSpread(0.4000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/raindrop") + p0.renderer.setColor(Vec4(0, 0, 0, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.2) + p0.renderer.setFinalXScale(0.03) + p0.renderer.setInitialYScale(0.3) + p0.renderer.setFinalYScale(0.05) + p0.renderer.setNonanimatedTheta(90.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(2.0000) + p0.emitter.setAmplitudeSpread(1.300) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -2.0000)) + + p0.emitter.setRadius(0.3282) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -33.0000), LinearDistanceForce.FTONEOVERRSQUARED, 15.9701, 95.0100, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def headShrinkDrop(self): + self.reset() + self.setPos(0.000, 0.000, 7.500) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(2.000, 2.000, 2.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("DiscEmitter") + p0.setPoolSize(60) + p0.setBirthRate(0.1500) + p0.setLitterSize(3) + p0.setLitterSpread(2) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.2000) + p0.factory.setLifespanSpread(0.2000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(1, 0.84, 0, 1.00)) + p0.renderer.setEdgeColor(Vec4(1, 1, 1, 0.3)) + p0.renderer.setBirthRadius(0.0400) + p0.renderer.setDeathRadius(0.0000) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(2.300) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 4.0000)) + + p0.emitter.setRadius(0.2800) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(0.060, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def rollodexWaterfall(self): + self.reset() + self.setPos(-0.160, 2.942, 3.400) + self.setHpr(89.908, -20.000, 179.476) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereSurfaceEmitter") + p0.setPoolSize(20) + p0.setBirthRate(0.2000) + p0.setLitterSize(3) + p0.setLitterSpread(2) + p0.setSystemLifespan(5.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.5000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/rollodex-card") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.4) + p0.renderer.setFinalXScale(0.4) + p0.renderer.setInitialYScale(0.3) + p0.renderer.setFinalYScale(0.3) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(1.0000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forward') + + force0 = LinearSourceForce(Point3(0.0000, 0.0000, 0.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 1.0000, 1) + force0.setActive(0) + f0.addForce(force0) + force1 = LinearSinkForce(Point3(0.0000, 0.0000, 10.0000), LinearDistanceForce.FTONEOVERRCUBED, 2.9550, 50.0000, 1) + force1.setActive(1) + f0.addForce(force1) + self.addForceGroup(f0) + + +@particle +def rollodexStream(self): + self.reset() + self.setPos(0.107, 2.799, 3.400) + self.setHpr(89.908, -20.000, 179.476) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("PointEmitter") + p0.setPoolSize(60) + p0.setBirthRate(0.2000) + p0.setLitterSize(2) + p0.setLitterSpread(1) + p0.setSystemLifespan(5.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.0000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(3.0000) + p0.factory.setMassSpread(2.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/rollodex-card") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.4) + p0.renderer.setFinalXScale(0.4) + p0.renderer.setInitialYScale(0.3) + p0.renderer.setFinalYScale(0.3) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETEXPLICIT) + p0.emitter.setAmplitude(-15.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setLocation(Point3(0.0000, 0.0000, 0.0000)) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forward') + + force0 = LinearSourceForce(Point3(0.0000, 0.0000, 0.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 1.0000, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearJitterForce(19.1346, 0) + force1.setActive(1) + f0.addForce(force1) + self.addForceGroup(f0) + + +@particle +def shiftSpray(self): + self.reset() + self.setPos(0.000, 5.000, 2.300) + self.setHpr(0.000, -55.000, 0.000) + self.setScale(9.000, 9.000, 9.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("LineEmitter") + p0.setPoolSize(100) + p0.setBirthRate(0.100) + p0.setLitterSize(7) + p0.setLitterSpread(2) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.3000) + p0.factory.setLifespanSpread(0.1000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(1.00, 1.00, 0.00, 0.9)) + p0.renderer.setEdgeColor(Vec4(1.00, 1.00, 0.00, 0.6)) + p0.renderer.setBirthRadius(0.0200) + p0.renderer.setDeathRadius(0.0600) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(3.5000) + p0.emitter.setAmplitudeSpread(0.5000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -3.0000, 0.0000)) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, 96.0000), LinearDistanceForce.FTONEOVERRSQUARED, 3.0400, 1.5000, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def pixieDrop(self): + self.reset() + self.setPos(0.000, 0.000, 6.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(2.000, 2.000, 2.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("DiscEmitter") + p0.setPoolSize(150) + p0.setBirthRate(0.1000) + p0.setLitterSize(7) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(2.2000) + p0.factory.setLifespanSpread(0.2000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setEdgeColor(Vec4(0.00, 0.00, 1.00, 1.00)) + p0.renderer.setBirthRadius(0.0400) + p0.renderer.setDeathRadius(0.0000) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 4.0000)) + + p0.emitter.setRadius(0.3000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearJitterForce(3.6003, 0) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def hotAirSpray(self): + self.reset() + self.setPos(0.000, 2.500, 3.200) + self.setHpr(-180.000, 80.000, -180.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(10) + p0.setBirthRate(0.2000) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.6000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/fire") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.6) + p0.renderer.setFinalXScale(0.3) + p0.renderer.setInitialYScale(0.6) + p0.renderer.setFinalYScale(0.3) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(2.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 5.1000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, -4.0000, 0.0000)) + + p0.emitter.setRadius(0.0200) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -4.0000), LinearDistanceForce.FTONEOVERRSQUARED, 1.0000, 2.5308, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearVectorForce(Vec3(0.0000, -10.0000, 0.0000), 1.0000, 0) + force1.setActive(1) + f0.addForce(force1) + self.addForceGroup(f0) + + +@particle +def resistanceEffectSparkle(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SparkleParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(500) + p0.setBirthRate(0.1000) + p0.setLitterSize(500) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(3.0000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setCenterColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setEdgeColor(Vec4(0.00, 0.00, 1.00, 1.00)) + p0.renderer.setBirthRadius(0.2000) + p0.renderer.setDeathRadius(0.1000) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPSCALE) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(20.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 20.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(2.0000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -79.0000), LinearDistanceForce.FTONEOVERRSQUARED, 15.9701, 95.0000, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def tt_p_efx_rocketLaunchFire(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("LineEmitter") + p0.setPoolSize(50) + p0.setBirthRate(0.0100) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(1.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.5000) + p0.factory.setLifespanSpread(0.1000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(0.69) + + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(2.0000) + p0.renderer.setFinalXScale(4.0000) + p0.renderer.setInitialYScale(1.0000) + p0.renderer.setFinalYScale(4.0000) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + p0.renderer.getColorInterpolationManager().addLinear(0.10999999940395355,1.0,Vec4(1.0,1.0,1.0,1.0),Vec4(0.729411780834198,0.40392157435417175,0.11372549086809158,1.0),1) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, -10.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setEndpoint1(Point3(1.0000, 0.0000, 0.0000)) + p0.emitter.setEndpoint2(Point3(0.0000, 0.0000, 0.0000)) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('Gravity') + + force0 = LinearVectorForce(Vec3(0.0000, 0.0000, 2.5000), 1.0000, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearVectorForce(Vec3(0.0000, 0.0000, -3.0000), 5.0000, 0) + force1.setVectorMasks(1, 1, 1) + force1.setActive(1) + f0.addForce(force1) + self.addForceGroup(f0) + + +@particle +def icetnt(self): + self.reset() + self.setPos(0.000, 0.000, -0.000) + self.setHpr(0.000, 10.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(40) + p0.setBirthRate(0.1000) + p0.setLitterSize(2) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.2000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/spark") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.3) + p0.renderer.setFinalXScale(0.3) + p0.renderer.setInitialYScale(0.3) + p0.renderer.setFinalYScale(0.03) + p0.renderer.setNonanimatedTheta(20.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(1.5000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -2.0000)) + + p0.emitter.setRadius(0.2282) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -19.0000), LinearDistanceForce.FTONEOVERRSQUARED, 15.9701, 95.0100, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def resistanceEffectSprite(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(50) + p0.setBirthRate(0.1000) + p0.setLitterSize(50) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(3.0000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.5000) + p0.renderer.setFinalXScale(0.5000) + p0.renderer.setInitialYScale(0.5000) + p0.renderer.setFinalYScale(0.5000) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(20.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 20.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(2.0000) + self.addParticles(p0) + p1 = Particles.Particles('particles-2') + + p1.setFactory("PointParticleFactory") + p1.setRenderer("SpriteParticleRenderer") + p1.setEmitter("SphereVolumeEmitter") + p1.setPoolSize(50) + p1.setBirthRate(0.1000) + p1.setLitterSize(50) + p1.setLitterSpread(0) + p1.setSystemLifespan(0.0000) + p1.setLocalVelocityFlag(1) + p1.setSystemGrowsOlderFlag(0) + + p1.factory.setLifespanBase(3.0000) + p1.factory.setLifespanSpread(0.0000) + p1.factory.setMassBase(1.0000) + p1.factory.setMassSpread(0.0000) + p1.factory.setTerminalVelocityBase(400.0000) + p1.factory.setTerminalVelocitySpread(0.0000) + + p1.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p1.renderer.setUserAlpha(1.00) + + p1.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p1.renderer.setXScaleFlag(0) + p1.renderer.setYScaleFlag(0) + p1.renderer.setAnimAngleFlag(0) + p1.renderer.setInitialXScale(0.5000) + p1.renderer.setFinalXScale(0.5000) + p1.renderer.setInitialYScale(0.5000) + p1.renderer.setFinalYScale(0.5000) + p1.renderer.setNonanimatedTheta(0.0000) + p1.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p1.renderer.setAlphaDisable(0) + + p1.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p1.emitter.setAmplitude(20.0000) + p1.emitter.setAmplitudeSpread(0.0000) + p1.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 20.0000)) + p1.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p1.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p1.emitter.setRadius(2.0000) + self.addParticles(p1) + p2 = Particles.Particles('particles-3') + + p2.setFactory("PointParticleFactory") + p2.setRenderer("SpriteParticleRenderer") + p2.setEmitter("SphereVolumeEmitter") + p2.setPoolSize(50) + p2.setBirthRate(0.1000) + p2.setLitterSize(50) + p2.setLitterSpread(0) + p2.setSystemLifespan(0.0000) + p2.setLocalVelocityFlag(1) + p2.setSystemGrowsOlderFlag(0) + + p2.factory.setLifespanBase(3.0000) + p2.factory.setLifespanSpread(0.0000) + p2.factory.setMassBase(1.0000) + p2.factory.setMassSpread(0.0000) + p2.factory.setTerminalVelocityBase(400.0000) + p2.factory.setTerminalVelocitySpread(0.0000) + + p2.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p2.renderer.setUserAlpha(1.00) + + p2.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p2.renderer.setXScaleFlag(0) + p2.renderer.setYScaleFlag(0) + p2.renderer.setAnimAngleFlag(0) + p2.renderer.setInitialXScale(0.5000) + p2.renderer.setFinalXScale(0.5000) + p2.renderer.setInitialYScale(0.5000) + p2.renderer.setFinalYScale(0.5000) + p2.renderer.setNonanimatedTheta(0.0000) + p2.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p2.renderer.setAlphaDisable(0) + + p2.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p2.emitter.setAmplitude(20.0000) + p2.emitter.setAmplitudeSpread(0.0000) + p2.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 20.0000)) + p2.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p2.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p2.emitter.setRadius(2.0000) + self.addParticles(p2) + p3 = Particles.Particles('particles-4') + + p3.setFactory("PointParticleFactory") + p3.setRenderer("SpriteParticleRenderer") + p3.setEmitter("SphereVolumeEmitter") + p3.setPoolSize(50) + p3.setBirthRate(0.1000) + p3.setLitterSize(50) + p3.setLitterSpread(0) + p3.setSystemLifespan(0.0000) + p3.setLocalVelocityFlag(1) + p3.setSystemGrowsOlderFlag(0) + + p3.factory.setLifespanBase(3.0000) + p3.factory.setLifespanSpread(0.0000) + p3.factory.setMassBase(1.0000) + p3.factory.setMassSpread(0.0000) + p3.factory.setTerminalVelocityBase(400.0000) + p3.factory.setTerminalVelocitySpread(0.0000) + + p3.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p3.renderer.setUserAlpha(1.00) + + p3.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p3.renderer.setXScaleFlag(0) + p3.renderer.setYScaleFlag(0) + p3.renderer.setAnimAngleFlag(0) + p3.renderer.setInitialXScale(0.5000) + p3.renderer.setFinalXScale(0.5000) + p3.renderer.setInitialYScale(0.5000) + p3.renderer.setFinalYScale(0.5000) + p3.renderer.setNonanimatedTheta(0.0000) + p3.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p3.renderer.setAlphaDisable(0) + + p3.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p3.emitter.setAmplitude(20.0000) + p3.emitter.setAmplitudeSpread(0.0000) + p3.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 20.0000)) + p3.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p3.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p3.emitter.setRadius(2.0000) + self.addParticles(p3) + p4 = Particles.Particles('particles-5') + + p4.setFactory("PointParticleFactory") + p4.setRenderer("SpriteParticleRenderer") + p4.setEmitter("SphereVolumeEmitter") + p4.setPoolSize(50) + p4.setBirthRate(0.1000) + p4.setLitterSize(50) + p4.setLitterSpread(0) + p4.setSystemLifespan(0.0000) + p4.setLocalVelocityFlag(1) + p4.setSystemGrowsOlderFlag(0) + + p4.factory.setLifespanBase(3.0000) + p4.factory.setLifespanSpread(0.0000) + p4.factory.setMassBase(1.0000) + p4.factory.setMassSpread(0.0000) + p4.factory.setTerminalVelocityBase(400.0000) + p4.factory.setTerminalVelocitySpread(0.0000) + + p4.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p4.renderer.setUserAlpha(1.00) + + p4.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p4.renderer.setXScaleFlag(0) + p4.renderer.setYScaleFlag(0) + p4.renderer.setAnimAngleFlag(0) + p4.renderer.setInitialXScale(0.5000) + p4.renderer.setFinalXScale(0.5000) + p4.renderer.setInitialYScale(0.5000) + p4.renderer.setFinalYScale(0.5000) + p4.renderer.setNonanimatedTheta(0.0000) + p4.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p4.renderer.setAlphaDisable(0) + + p4.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p4.emitter.setAmplitude(20.0000) + p4.emitter.setAmplitudeSpread(0.0000) + p4.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 20.0000)) + p4.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p4.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p4.emitter.setRadius(2.0000) + self.addParticles(p4) + p5 = Particles.Particles('particles-6') + + p5.setFactory("PointParticleFactory") + p5.setRenderer("SpriteParticleRenderer") + p5.setEmitter("SphereVolumeEmitter") + p5.setPoolSize(50) + p5.setBirthRate(0.1000) + p5.setLitterSize(50) + p5.setLitterSpread(0) + p5.setSystemLifespan(0.0000) + p5.setLocalVelocityFlag(1) + p5.setSystemGrowsOlderFlag(0) + + p5.factory.setLifespanBase(3.0000) + p5.factory.setLifespanSpread(0.0000) + p5.factory.setMassBase(1.0000) + p5.factory.setMassSpread(0.0000) + p5.factory.setTerminalVelocityBase(400.0000) + p5.factory.setTerminalVelocitySpread(0.0000) + + p5.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p5.renderer.setUserAlpha(1.00) + + p5.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p5.renderer.setXScaleFlag(0) + p5.renderer.setYScaleFlag(0) + p5.renderer.setAnimAngleFlag(0) + p5.renderer.setInitialXScale(0.5000) + p5.renderer.setFinalXScale(0.5000) + p5.renderer.setInitialYScale(0.5000) + p5.renderer.setFinalYScale(0.5000) + p5.renderer.setNonanimatedTheta(0.0000) + p5.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p5.renderer.setAlphaDisable(0) + + p5.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p5.emitter.setAmplitude(20.0000) + p5.emitter.setAmplitudeSpread(0.0000) + p5.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 20.0000)) + p5.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p5.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p5.emitter.setRadius(2.0000) + self.addParticles(p5) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -79.0000), LinearDistanceForce.FTONEOVERRSQUARED, 15.9701, 95.0000, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def splashlines(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("LineParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(40) + p0.setBirthRate(1000) + p0.setLitterSize(40) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(2.0) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setHeadColor(Vec4(0.02, 0.67, 0.92, 1.00)) + p0.renderer.setTailColor(Vec4(1.00, 1.00, 1.00, 1.00)) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(9.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 9.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, -2.0000)) + + p0.emitter.setRadius(3.2282) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -79.0000), LinearDistanceForce.FTONEOVERRSQUARED, 15.9701, 95.0100, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def resistanceEffectBean(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("GeomParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(20) + p0.setBirthRate(0.1000) + p0.setLitterSize(20) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(3.0000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p0.renderer.setUserAlpha(1.00) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(20.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 20.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(2.0000) + self.addParticles(p0) + p1 = Particles.Particles('particles-2') + + p1.setFactory("PointParticleFactory") + p1.setRenderer("GeomParticleRenderer") + p1.setEmitter("SphereVolumeEmitter") + p1.setPoolSize(20) + p1.setBirthRate(0.1000) + p1.setLitterSize(20) + p1.setLitterSpread(0) + p1.setSystemLifespan(0.0000) + p1.setLocalVelocityFlag(1) + p1.setSystemGrowsOlderFlag(0) + + p1.factory.setLifespanBase(3.0000) + p1.factory.setLifespanSpread(0.0000) + p1.factory.setMassBase(1.0000) + p1.factory.setMassSpread(0.0000) + p1.factory.setTerminalVelocityBase(400.0000) + p1.factory.setTerminalVelocitySpread(0.0000) + + p1.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p1.renderer.setUserAlpha(1.00) + + p1.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p1.emitter.setAmplitude(20.0000) + p1.emitter.setAmplitudeSpread(0.0000) + p1.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 20.0000)) + p1.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p1.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p1.emitter.setRadius(2.0000) + self.addParticles(p1) + p2 = Particles.Particles('particles-3') + + p2.setFactory("PointParticleFactory") + p2.setRenderer("GeomParticleRenderer") + p2.setEmitter("SphereVolumeEmitter") + p2.setPoolSize(20) + p2.setBirthRate(0.1000) + p2.setLitterSize(20) + p2.setLitterSpread(0) + p2.setSystemLifespan(0.0000) + p2.setLocalVelocityFlag(1) + p2.setSystemGrowsOlderFlag(0) + + p2.factory.setLifespanBase(3.0000) + p2.factory.setLifespanSpread(0.0000) + p2.factory.setMassBase(1.0000) + p2.factory.setMassSpread(0.0000) + p2.factory.setTerminalVelocityBase(400.0000) + p2.factory.setTerminalVelocitySpread(0.0000) + + p2.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p2.renderer.setUserAlpha(1.00) + + p2.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p2.emitter.setAmplitude(20.0000) + p2.emitter.setAmplitudeSpread(0.0000) + p2.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 20.0000)) + p2.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p2.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p2.emitter.setRadius(2.0000) + self.addParticles(p2) + p3 = Particles.Particles('particles-4') + + p3.setFactory("PointParticleFactory") + p3.setRenderer("GeomParticleRenderer") + p3.setEmitter("SphereVolumeEmitter") + p3.setPoolSize(20) + p3.setBirthRate(0.1000) + p3.setLitterSize(20) + p3.setLitterSpread(0) + p3.setSystemLifespan(0.0000) + p3.setLocalVelocityFlag(1) + p3.setSystemGrowsOlderFlag(0) + + p3.factory.setLifespanBase(3.0000) + p3.factory.setLifespanSpread(0.0000) + p3.factory.setMassBase(1.0000) + p3.factory.setMassSpread(0.0000) + p3.factory.setTerminalVelocityBase(400.0000) + p3.factory.setTerminalVelocitySpread(0.0000) + + p3.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p3.renderer.setUserAlpha(1.00) + + p3.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p3.emitter.setAmplitude(20.0000) + p3.emitter.setAmplitudeSpread(0.0000) + p3.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 20.0000)) + p3.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p3.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p3.emitter.setRadius(2.0000) + self.addParticles(p3) + p4 = Particles.Particles('particles-5') + + p4.setFactory("PointParticleFactory") + p4.setRenderer("GeomParticleRenderer") + p4.setEmitter("SphereVolumeEmitter") + p4.setPoolSize(20) + p4.setBirthRate(0.1000) + p4.setLitterSize(20) + p4.setLitterSpread(0) + p4.setSystemLifespan(0.0000) + p4.setLocalVelocityFlag(1) + p4.setSystemGrowsOlderFlag(0) + + p4.factory.setLifespanBase(3.0000) + p4.factory.setLifespanSpread(0.0000) + p4.factory.setMassBase(1.0000) + p4.factory.setMassSpread(0.0000) + p4.factory.setTerminalVelocityBase(400.0000) + p4.factory.setTerminalVelocitySpread(0.0000) + + p4.renderer.setAlphaMode(BaseParticleRenderer.PRALPHANONE) + p4.renderer.setUserAlpha(1.00) + + p4.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p4.emitter.setAmplitude(20.0000) + p4.emitter.setAmplitudeSpread(0.0000) + p4.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 20.0000)) + p4.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p4.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p4.emitter.setRadius(2.0000) + self.addParticles(p4) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -79.0000), LinearDistanceForce.FTONEOVERRSQUARED, 15.9701, 95.0000, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def tt_p_efx_rocketLaunchSmoke(self): + self.reset() + self.setPos(0.000, 0.000, 16.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(2.000, 2.000, 3.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("DiscEmitter") + p0.setPoolSize(300) + p0.setBirthRate(0.1000) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(4.0000) + p0.factory.setLifespanSpread(0.1000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAINOUT) + p0.renderer.setUserAlpha(0.47) + + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(2.0000) + p0.renderer.setFinalXScale(4.0000) + p0.renderer.setInitialYScale(1.0000) + p0.renderer.setFinalYScale(4.0000) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + p0.renderer.getColorInterpolationManager().addLinear(0.0,1.0,Vec4(1.0,1.0,1.0,1.0),Vec4(0.58823531866073608,0.58823531866073608,0.58823531866073608,1.0),1) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(0.4000) + p0.emitter.setAmplitudeSpread(2.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, -5.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(1.0000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('Gravity') + + force0 = LinearVectorForce(Vec3(0.0000, 0.0000, 2.5000), 1.0000, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) + + +@particle +def sparks(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("LineEmitter") + p0.setPoolSize(1024) + p0.setBirthRate(0.0200) + p0.setLitterSize(8) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.5000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.256) + p0.renderer.setFinalXScale(0.0000) + p0.renderer.setInitialYScale(0.256) + p0.renderer.setFinalYScale(0.0000) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(1) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETEXPLICIT) + p0.emitter.setAmplitude(0.0000) + p0.emitter.setAmplitudeSpread(10.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 1.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(0.0000, 0.0000, 1.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setEndpoint1(Point3(0.5000, 5.0000, -0.5000)) + p0.emitter.setEndpoint2(Point3(0.75000, -5.0000, 2.5000)) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('sparkforces') + + force0 = LinearVectorForce(Vec3(1.0000, 0.0000, 0.0000), -50.0000, 0) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearVectorForce(Vec3(0.0000, -1.0000, 0.0000), 100.0000, 0) + force1.setActive(1) + f0.addForce(force1) + force2 = LinearVectorForce(Vec3(0.0000, 0.0000, -1.0000), 20.0000, 0) + force2.setActive(1) + f0.addForce(force2) + force3 = LinearJitterForce(50.0000, 0) + force3.setActive(1) + f0.addForce(force3) + self.addForceGroup(f0) + + +@particle +def drift(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("ZSpinParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("PointEmitter") + p0.setPoolSize(1024) + p0.setBirthRate(0.0750) + p0.setLitterSize(7) + p0.setLitterSpread(2) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(0.1750) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.factory.enableAngularVelocity(1) + p0.factory.setInitialAngle(0.0000) + p0.factory.setInitialAngleSpread(45.0000) + p0.factory.setFinalAngle(0.0000) + p0.factory.setFinalAngleSpread(0.0000) + p0.factory.setAngularVelocity(0.0000) + p0.factory.setAngularVelocitySpread(90.0000) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(0.50) + + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(1) + p0.renderer.setInitialXScale(0.375) + p0.renderer.setFinalXScale(0.750) + p0.renderer.setInitialYScale(0.375) + p0.renderer.setFinalYScale(0.750) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETEXPLICIT) + p0.emitter.setAmplitude(1.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setLocation(Point3(0.0000,0.0000,0.0000)) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('Smoke') + + force0 = LinearVectorForce(Vec3(1.0000, 0.0000, 0.0000), 0.0000, 0) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearVectorForce(Vec3(0.0000, -1.0000, 0.0000), 100.0000, 0) + force1.setActive(1) + f0.addForce(force1) + force2 = LinearVectorForce(Vec3(0.0000, 0.0000, 1.0000), 50.0000, 0) + force2.setActive(1) + f0.addForce(force2) + force4 = LinearJitterForce(100.0000, 0) + force4.setActive(1) + f0.addForce(force4) + self.addForceGroup(f0) + + +@particle +def snowdisk(self): + self.reset() + self.setPos(0.000, 0.000, 0.000) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("ZSpinParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("DiscEmitter") + p0.setPoolSize(1024) + p0.setBirthRate(0.0200) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(4.5000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.factory.setInitialAngle(0.0000) + p0.factory.setInitialAngleSpread(10.0000) + p0.factory.enableAngularVelocity(1) + p0.factory.setAngularVelocity(0.0000) + p0.factory.setAngularVelocitySpread(500.0000) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAIN) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_8/models/props/snowflake_particle", "**/p1_2") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(0) + p0.renderer.setYScaleFlag(0) + p0.renderer.setAnimAngleFlag(1) + p0.renderer.setInitialXScale(0.03125) + p0.renderer.setFinalXScale(0.50) + p0.renderer.setInitialYScale(0.03125) + p0.renderer.setFinalYScale(0.50) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(0.1000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(50.0000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('gravity') + + force0 = LinearVectorForce(Vec3(0.0000, 0.0000, -1.0000), 1.5000, 0) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearJitterForce(10.0000, 0) + force1.setActive(1) + f0.addForce(force1) + self.addForceGroup(f0) + + +@particle +def smoke(self): + self.reset() + self.setPos(0.000, 3.500, 5.100) + self.setHpr(-180.000, 80.000, -180.000) + self.setScale(0.0250, 0.0250, 0.0250) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereVolumeEmitter") + p0.setPoolSize(50) + p0.setBirthRate(0.0300) + p0.setLitterSize(1) + p0.setLitterSpread(1) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.6000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.addTextureFromFile('phase_6/maps/tt_t_efx_ext_smoke_a.rgb') + p0.renderer.setColor(Vec4(0.10, 0.10, 0.10, 0.10)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.6) + p0.renderer.setFinalXScale(0.3) + p0.renderer.setInitialYScale(0.6) + p0.renderer.setFinalYScale(0.3) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(2.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, 0.0000, -4.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(0.0200) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -4.0000), LinearDistanceForce.FTONEOVERR, 1.0000, 2.5308, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearVectorForce(Vec3(0.0000, -5.0000, 0.0000), 1.0000, 0) + force1.setActive(1) + f0.addForce(force1) + self.addForceGroup(f0) + + +@particle +def bossCogFrontAttack(self): + self.reset() + self.setPos(0.000, 0.000, 4.600) + self.setHpr(0.000, 0.000, 0.000) + self.setScale(1.000, 1.000, 1.000) + p0 = Particles.Particles('particles-1') + + p0.setFactory("PointParticleFactory") + p0.setRenderer("SpriteParticleRenderer") + p0.setEmitter("SphereSurfaceEmitter") + p0.setPoolSize(200) + p0.setBirthRate(0.0050) + p0.setLitterSize(1) + p0.setLitterSpread(0) + p0.setSystemLifespan(0.0000) + p0.setLocalVelocityFlag(1) + p0.setSystemGrowsOlderFlag(0) + + p0.factory.setLifespanBase(1.0000) + p0.factory.setLifespanSpread(0.0000) + p0.factory.setMassBase(1.0000) + p0.factory.setMassSpread(0.0000) + p0.factory.setTerminalVelocityBase(400.0000) + p0.factory.setTerminalVelocitySpread(0.0000) + + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.00) + + p0.renderer.setIgnoreScale(1) + p0.renderer.setTextureFromNode("phase_3.5/models/props/suit-particles", "**/gear") + p0.renderer.setColor(Vec4(1.00, 1.00, 1.00, 1.00)) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setAnimAngleFlag(0) + p0.renderer.setInitialXScale(0.150) + p0.renderer.setFinalXScale(0.300) + p0.renderer.setInitialYScale(0.150) + p0.renderer.setFinalYScale(0.300) + p0.renderer.setNonanimatedTheta(0.0000) + p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + p0.renderer.setAlphaDisable(0) + + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(5.0000) + p0.emitter.setAmplitudeSpread(0.0000) + p0.emitter.setOffsetForce(Vec3(0.0000, -10.0000, 0.0000)) + p0.emitter.setExplicitLaunchVector(Vec3(1.0000, 0.0000, 0.0000)) + p0.emitter.setRadiateOrigin(Point3(0.0000, 0.0000, 0.0000)) + + p0.emitter.setRadius(1.0000) + self.addParticles(p0) + f0 = ForceGroup.ForceGroup('forces') + + force0 = LinearSinkForce(Point3(0.0000, 0.0000, -79.0000), LinearDistanceForce.FTONEOVERRSQUARED, 15.9701, 50.0000, 1) + force0.setActive(1) + f0.addForce(force0) + self.addForceGroup(f0) diff --git a/toontown/battle/PlayByPlayText.py b/toontown/battle/PlayByPlayText.py new file mode 100755 index 00000000..74890f53 --- /dev/null +++ b/toontown/battle/PlayByPlayText.py @@ -0,0 +1,32 @@ +from panda3d.core import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase.ToontownBattleGlobals import * +from toontown.toonbase.ToontownGlobals import * +from SuitBattleGlobals import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +import string +from direct.gui import OnscreenText +import BattleBase + +class PlayByPlayText(OnscreenText.OnscreenText): + notify = DirectNotifyGlobal.directNotify.newCategory('PlayByPlayText') + + def __init__(self): + OnscreenText.OnscreenText.__init__(self, mayChange=1, pos=(0.0, 0.75), scale=TTLocalizer.PBPTonscreenText, fg=(1, 0, 0, 1), font=getSignFont(), wordwrap=13) + + def getShowInterval(self, text, duration): + return Sequence(Func(self.hide), Wait(duration * 0.3), Func(self.setText, text), Func(self.show), Wait(duration * 0.7), Func(self.hide)) + + def getToonsDiedInterval(self, textList, duration): + track = Sequence(Func(self.hide), Wait(duration * 0.1)) + waitGap = 0.6 / len(textList) * duration + for text in textList: + newList = [Func(self.setText, text), + Func(self.show), + Wait(waitGap), + Func(self.hide)] + track += newList + + track.append(Wait(duration * 0.1)) + return track diff --git a/toontown/battle/RewardPanel.py b/toontown/battle/RewardPanel.py new file mode 100755 index 00000000..3acc9dd7 --- /dev/null +++ b/toontown/battle/RewardPanel.py @@ -0,0 +1,781 @@ +import copy +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +import math +from panda3d.core import * +import random + +import Fanfare +from otp.otpbase import OTPGlobals +from toontown.coghq import CogDisguiseGlobals +from toontown.quest import Quests +from toontown.shtiker import DisguisePage +from toontown.suit import SuitDNA +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import ToontownGlobals + + +class RewardPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('RewardPanel') + SkipBattleMovieEvent = 'skip-battle-movie-event' + + def __init__(self, name): + gscale = (TTLocalizer.RPdirectFrame[0], TTLocalizer.RPdirectFrame[1], TTLocalizer.RPdirectFrame[2] * 1.1) + DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_pos=Point3(0, 0, -.05), geom_scale=gscale, pos=(0, 0, 0.587)) + self.initialiseoptions(RewardPanel) + self.avNameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.3), text=name, text_scale=0.08) + self.gagExpFrame = DirectFrame(parent=self, relief=None, pos=(-0.32, 0, 0.24)) + self.itemFrame = DirectFrame(parent=self, relief=None, text=TTLocalizer.RewardPanelItems, text_pos=(0, 0.2), text_scale=0.08) + self.cogPartFrame = DirectFrame(parent=self, relief=None, text=TTLocalizer.RewardPanelCogPart, text_pos=(0, 0.2), text_scale=0.08) + self.missedItemFrame = DirectFrame(parent=self, relief=None, text=TTLocalizer.RewardPanelMissedItems, text_pos=(0, 0.2), text_scale=0.08) + self.itemLabel = DirectLabel(parent=self.itemFrame, text='', text_scale=0.06) + self.cogPartLabel = DirectLabel(parent=self.cogPartFrame, text='', text_scale=0.06) + self.missedItemLabel = DirectLabel(parent=self.missedItemFrame, text='', text_scale=0.06) + self.questFrame = DirectFrame(parent=self, relief=None, text=TTLocalizer.RewardPanelToonTasks, text_pos=(0, 0.2), text_scale=0.06) + self.questLabelList = [] + for i in xrange(ToontownGlobals.MaxQuestCarryLimit): + label = DirectLabel(parent=self.questFrame, relief=None, pos=(-0.85, 0, -0.1 * i), text=TTLocalizer.RewardPanelQuestLabel % i, text_scale=0.05, text_align=TextNode.ALeft) + label.hide() + self.questLabelList.append(label) + + self.newGagFrame = DirectFrame(parent=self, relief=None, pos=(0, 0, 0.24), text='', text_wordwrap=14.4, text_pos=(0, -0.46), text_scale=0.06) + self.endTrackFrame = DirectFrame(parent=self, relief=None, pos=(0, 0, 0.24), text='', text_wordwrap=14.4, text_pos=(0, -0.46), text_scale=0.06) + self.congratsLeft = DirectLabel(parent=self.newGagFrame, pos=(-0.2, 0, -0.1), text='', text_pos=(0, 0), text_scale=0.06) + self.congratsLeft.setHpr(0, 0, -30) + self.congratsRight = DirectLabel(parent=self.newGagFrame, pos=(0.2, 0, -0.1), text='', text_pos=(0, 0), text_scale=0.06) + self.congratsRight.setHpr(0, 0, 30) + self.promotionFrame = DirectFrame(parent=self, relief=None, pos=(0, 0, 0.24), text='', text_wordwrap=14.4, text_pos=(0, -0.46), text_scale=0.06) + self.trackLabels = [] + self.trackIncLabels = [] + self.trackBars = [] + self.trackBarsOffset = 0 + self.meritLabels = [] + self.meritIncLabels = [] + self.meritBars = [] + for i in xrange(len(SuitDNA.suitDepts)): + deptName = TextEncoder.upper(SuitDNA.suitDeptFullnames[SuitDNA.suitDepts[i]]) + self.meritLabels.append(DirectLabel(parent=self.gagExpFrame, relief=None, text=deptName, text_scale=0.05, text_align=TextNode.ARight, pos=(TTLocalizer.RPmeritLabelPosX, 0, -0.09 * i - 0.125), text_pos=(0, -0.02))) + self.meritIncLabels.append(DirectLabel(parent=self.gagExpFrame, relief=None, text='', text_scale=0.05, text_align=TextNode.ALeft, pos=(0.7, 0, -0.09 * i - 0.125), text_pos=(0, -0.02))) + self.meritBars.append(DirectWaitBar(parent=self.gagExpFrame, relief=DGG.SUNKEN, frameSize=(-1, + 1, + -0.15, + 0.15), borderWidth=(0.02, 0.02), scale=0.25, frameColor=(DisguisePage.DeptColors[i][0] * 0.7, + DisguisePage.DeptColors[i][1] * 0.7, + DisguisePage.DeptColors[i][2] * 0.7, + 1), barColor=(DisguisePage.DeptColors[i][0], + DisguisePage.DeptColors[i][1], + DisguisePage.DeptColors[i][2], + 1), text='0/0 ' + TTLocalizer.RewardPanelMeritBarLabels[i], text_scale=TTLocalizer.RPmeritBarLabels, text_fg=(0, 0, 0, 1), text_align=TextNode.ALeft, text_pos=(-0.96, -0.05), pos=(TTLocalizer.RPmeritBarsPosX, 0, -0.09 * i - 0.125))) + + for i in xrange(len(ToontownBattleGlobals.Tracks)): + trackName = TextEncoder.upper(ToontownBattleGlobals.Tracks[i]) + self.trackLabels.append(DirectLabel(parent=self.gagExpFrame, relief=None, text=trackName, text_scale=TTLocalizer.RPtrackLabels, text_align=TextNode.ARight, pos=(0.13, 0, -0.09 * i), text_pos=(0, -0.02))) + self.trackIncLabels.append(DirectLabel(parent=self.gagExpFrame, relief=None, text='', text_scale=0.05, text_align=TextNode.ALeft, pos=(0.65, 0, -0.09 * i), text_pos=(0, -0.02))) + self.trackBars.append(DirectWaitBar(parent=self.gagExpFrame, relief=DGG.SUNKEN, frameSize=(-1, + 1, + -0.15, + 0.15), borderWidth=(0.02, 0.02), scale=0.25, frameColor=(ToontownBattleGlobals.TrackColors[i][0] * 0.7, + ToontownBattleGlobals.TrackColors[i][1] * 0.7, + ToontownBattleGlobals.TrackColors[i][2] * 0.7, + 1), barColor=(ToontownBattleGlobals.TrackColors[i][0], + ToontownBattleGlobals.TrackColors[i][1], + ToontownBattleGlobals.TrackColors[i][2], + 1), text='0/0', text_scale=0.18, text_fg=(0, 0, 0, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05), pos=(0.4, 0, -0.09 * i))) + + self._battleGui = loader.loadModel('phase_3.5/models/gui/battle_gui') + self.skipButton = DirectButton(parent=self, relief=None, image=(self._battleGui.find('**/tt_t_gui_gen_skipSectionUp'), + self._battleGui.find('**/tt_t_gui_gen_skipSectionDown'), + self._battleGui.find('**/tt_t_gui_gen_skipSectionRollOver'), + self._battleGui.find('**/tt_t_gui_gen_skipSectionDisabled')), pos=(0.815, 0, -0.395), scale=(0.39, 1.0, 0.39), text=('', + TTLocalizer.RewardPanelSkip, + TTLocalizer.RewardPanelSkip, + ''), text_scale=TTLocalizer.RPskipScale, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=TTLocalizer.RPskipPos, textMayChange=0, command=self._handleSkip) + return + + def getNextExpValue(self, curSkill, trackIndex): + retVal = ToontownBattleGlobals.UberSkill + for amount in ToontownBattleGlobals.Levels[trackIndex]: + if curSkill < amount: + retVal = amount + return retVal + + return retVal + + def getNextExpValueUber(self, curSkill, trackIndex): + return ToontownBattleGlobals.UberSkill + + def getNextMeritValue(self, curMerits, toon, dept): + totalMerits = CogDisguiseGlobals.getTotalMerits(toon, dept) + retVal = totalMerits + if curMerits > totalMerits: + retVal = amount + return retVal + + def initItemFrame(self, toon): + self.endTrackFrame.hide() + self.gagExpFrame.hide() + self.newGagFrame.hide() + self.promotionFrame.hide() + self.questFrame.hide() + self.itemFrame.show() + self.cogPartFrame.hide() + self.missedItemFrame.hide() + + def initMissedItemFrame(self, toon): + self.endTrackFrame.hide() + self.gagExpFrame.hide() + self.newGagFrame.hide() + self.promotionFrame.hide() + self.questFrame.hide() + self.itemFrame.hide() + self.cogPartFrame.hide() + self.missedItemFrame.show() + + def initCogPartFrame(self, toon): + self.endTrackFrame.hide() + self.gagExpFrame.hide() + self.newGagFrame.hide() + self.promotionFrame.hide() + self.questFrame.hide() + self.itemFrame.hide() + self.cogPartFrame.show() + self.cogPartLabel['text'] = '' + self.missedItemFrame.hide() + + def initQuestFrame(self, toon, avQuests): + self.endTrackFrame.hide() + self.gagExpFrame.hide() + self.newGagFrame.hide() + self.promotionFrame.hide() + self.questFrame.show() + self.itemFrame.hide() + self.cogPartFrame.hide() + self.missedItemFrame.hide() + for i in xrange(ToontownGlobals.MaxQuestCarryLimit): + questLabel = self.questLabelList[i] + questLabel['text_fg'] = (0, 0, 0, 1) + questLabel.hide() + + for i in xrange(len(avQuests)): + questDesc = avQuests[i] + questId, npcId, toNpcId, rewardId, toonProgress = questDesc + quest = Quests.getQuest(questId) + if quest: + questString = quest.getString() + progressString = quest.getProgressString(toon, questDesc) + rewardString = quest.getRewardString(progressString) + rewardString = Quests.fillInQuestNames(rewardString, toNpcId=toNpcId) + completed = quest.getCompletionStatus(toon, questDesc) == Quests.COMPLETE + questLabel = self.questLabelList[i] + questLabel.show() + if base.localAvatar.tutorialAck: + questLabel['text'] = rewardString + if completed: + questLabel['text_fg'] = (0, 0.3, 0, 1) + else: + questLabel['text'] = questString + ' :' + + def initGagFrame(self, toon, expList, meritList, noSkip = False): + self.avNameLabel['text'] = toon.getName() + self.endTrackFrame.hide() + self.gagExpFrame.show() + self.newGagFrame.hide() + self.promotionFrame.hide() + self.questFrame.hide() + self.itemFrame.hide() + self.cogPartFrame.hide() + self.missedItemFrame.hide() + trackBarOffset = 0 + self.skipButton['state'] = choice(noSkip, DGG.DISABLED, DGG.NORMAL) + for i in xrange(len(SuitDNA.suitDepts)): + meritBar = self.meritBars[i] + meritLabel = self.meritLabels[i] + totalMerits = CogDisguiseGlobals.getTotalMerits(toon, i) + merits = meritList[i] + self.meritIncLabels[i].hide() + if CogDisguiseGlobals.isSuitComplete(toon.cogParts, i): + if not self.trackBarsOffset: + trackBarOffset = 0.47 + self.trackBarsOffset = 1 + meritBar.show() + meritLabel.show() + meritLabel.show() + if totalMerits: + meritBar['range'] = totalMerits + meritBar['value'] = merits + if merits == totalMerits: + meritBar['text'] = TTLocalizer.RewardPanelMeritAlert + else: + meritBar['text'] = '%s/%s %s' % (merits, totalMerits, TTLocalizer.RewardPanelMeritBarLabels[i]) + else: + meritBar['range'] = 1 + meritBar['value'] = 1 + meritBar['text'] = TTLocalizer.RewardPanelMeritsMaxed + self.resetMeritBarColor(i) + else: + meritBar.hide() + meritLabel.hide() + + for i in xrange(len(expList)): + curExp = expList[i] + trackBar = self.trackBars[i] + trackLabel = self.trackLabels[i] + trackIncLabel = self.trackIncLabels[i] + trackBar.setX(trackBar.getX() - trackBarOffset) + trackLabel.setX(trackLabel.getX() - trackBarOffset) + trackIncLabel.setX(trackIncLabel.getX() - trackBarOffset) + trackIncLabel.hide() + if toon.hasTrackAccess(i): + trackBar.show() + if curExp >= ToontownBattleGlobals.regMaxSkill: + nextExp = self.getNextExpValueUber(curExp, i) + trackBar['range'] = nextExp + uberCurrExp = curExp - ToontownBattleGlobals.regMaxSkill + trackBar['value'] = uberCurrExp + trackBar['text'] = TTLocalizer.InventoryUberTrackExp % {'nextExp': ToontownBattleGlobals.MaxSkill - curExp} + else: + nextExp = self.getNextExpValue(curExp, i) + trackBar['range'] = nextExp + trackBar['value'] = curExp + trackBar['text'] = '%s/%s' % (curExp, nextExp) + self.resetBarColor(i) + else: + trackBar.hide() + + def incrementExp(self, track, newValue, toon): + trackBar = self.trackBars[track] + oldValue = trackBar['value'] + newValue = min(ToontownBattleGlobals.MaxSkill, newValue) + nextExp = self.getNextExpValue(newValue, track) + if newValue >= ToontownBattleGlobals.regMaxSkill: + newValue = newValue - ToontownBattleGlobals.regMaxSkill + nextExp = self.getNextExpValueUber(newValue, track) + trackBar['text'] = TTLocalizer.InventoryUberTrackExp % {'nextExp': ToontownBattleGlobals.UberSkill - newValue} + else: + trackBar['text'] = '%s/%s' % (newValue, nextExp) + trackBar['range'] = nextExp + trackBar['value'] = newValue + trackBar['barColor'] = (ToontownBattleGlobals.TrackColors[track][0], + ToontownBattleGlobals.TrackColors[track][1], + ToontownBattleGlobals.TrackColors[track][2], + 1) + + def resetBarColor(self, track): + self.trackBars[track]['barColor'] = (ToontownBattleGlobals.TrackColors[track][0] * 0.8, + ToontownBattleGlobals.TrackColors[track][1] * 0.8, + ToontownBattleGlobals.TrackColors[track][2] * 0.8, + 1) + + def incrementMerits(self, toon, dept, newValue, totalMerits): + meritBar = self.meritBars[dept] + oldValue = meritBar['value'] + if totalMerits: + newValue = min(totalMerits, newValue) + meritBar['range'] = totalMerits + meritBar['value'] = newValue + if newValue == totalMerits: + meritBar['text'] = TTLocalizer.RewardPanelMeritAlert + meritBar['barColor'] = (DisguisePage.DeptColors[dept][0], + DisguisePage.DeptColors[dept][1], + DisguisePage.DeptColors[dept][2], + 1) + else: + meritBar['text'] = '%s/%s %s' % (newValue, totalMerits, TTLocalizer.RewardPanelMeritBarLabels[dept]) + + def resetMeritBarColor(self, dept): + self.meritBars[dept]['barColor'] = (DisguisePage.DeptColors[dept][0] * 0.8, + DisguisePage.DeptColors[dept][1] * 0.8, + DisguisePage.DeptColors[dept][2] * 0.8, + 1) + + def getRandomCongratsPair(self, toon): + congratsStrings = TTLocalizer.RewardPanelCongratsStrings + numStrings = len(congratsStrings) + indexList = range(numStrings) + index1 = random.choice(indexList) + indexList.remove(index1) + index2 = random.choice(indexList) + string1 = congratsStrings[index1] + string2 = congratsStrings[index2] + return (string1, string2) + + def uberGagInterval(self, toon, track, level): + self.endTrackFrame.hide() + self.gagExpFrame.hide() + self.newGagFrame.show() + self.promotionFrame.hide() + self.questFrame.hide() + self.itemFrame.hide() + self.missedItemFrame.hide() + self.newGagFrame['text'] = TTLocalizer.RewardPanelUberGag % {'gagName': ToontownBattleGlobals.Tracks[track].capitalize(), + 'exp': str(ToontownBattleGlobals.UberSkill), + 'avName': toon.getName()} + self.congratsLeft['text'] = '' + self.congratsRight['text'] = '' + gagOriginal = base.localAvatar.inventory.buttonLookup(track, level) + self.newGagIcon = gagOriginal.copyTo(self.newGagFrame) + self.newGagIcon.setPos(0, 0, -0.25) + self.newGagIcon.setScale(1.5) + + def newGag(self, toon, track, level): + self.endTrackFrame.hide() + self.gagExpFrame.hide() + self.newGagFrame.show() + self.promotionFrame.hide() + self.questFrame.hide() + self.itemFrame.hide() + self.missedItemFrame.hide() + self.newGagFrame['text'] = TTLocalizer.RewardPanelNewGag % {'gagName': ToontownBattleGlobals.Tracks[track].capitalize(), + 'avName': toon.getName()} + self.congratsLeft['text'] = '' + self.congratsRight['text'] = '' + gagOriginal = base.localAvatar.inventory.buttonLookup(track, level) + self.newGagIcon = gagOriginal.copyTo(self.newGagFrame) + self.newGagIcon.setPos(0, 0, -0.25) + self.newGagIcon.setScale(1.5) + + def cleanupNewGag(self): + self.endTrackFrame.hide() + if self.newGagIcon: + self.newGagIcon.removeNode() + self.newGagIcon = None + self.gagExpFrame.show() + self.newGagFrame.hide() + self.promotionFrame.hide() + self.questFrame.hide() + self.itemFrame.hide() + self.missedItemFrame.hide() + return + + def getNewGagIntervalList(self, toon, track, level): + leftCongratsAnticipate = 1.0 + rightCongratsAnticipate = 1.0 + finalDelay = 1.5 + leftString, rightString = self.getRandomCongratsPair(toon) + intervalList = [Func(self.newGag, toon, track, level), + Wait(leftCongratsAnticipate), + Func(self.congratsLeft.setProp, 'text', leftString), + Wait(rightCongratsAnticipate), + Func(self.congratsRight.setProp, 'text', rightString), + Wait(finalDelay), + Func(self.cleanupNewGag)] + return intervalList + + def getUberGagIntervalList(self, toon, track, level): + leftCongratsAnticipate = 1.0 + rightCongratsAnticipate = 1.0 + finalDelay = 1.5 + leftString, rightString = self.getRandomCongratsPair(toon) + intervalList = [Func(self.uberGagInterval, toon, track, level), + Wait(leftCongratsAnticipate), + Func(self.congratsLeft.setProp, 'text', leftString), + Wait(rightCongratsAnticipate), + Func(self.congratsRight.setProp, 'text', rightString), + Wait(finalDelay), + Func(self.cleanupNewGag)] + return intervalList + + def vanishFrames(self): + self.hide() + self.endTrackFrame.hide() + self.gagExpFrame.hide() + self.newGagFrame.hide() + self.promotionFrame.hide() + self.questFrame.hide() + self.itemFrame.hide() + self.missedItemFrame.hide() + self.cogPartFrame.hide() + self.missedItemFrame.hide() + + def endTrack(self, toon, toonList, track): + for t in toonList: + if t == base.localAvatar: + self.show() + + self.endTrackFrame.show() + self.endTrackFrame['text'] = TTLocalizer.RewardPanelEndTrack % {'gagName': ToontownBattleGlobals.Tracks[track].capitalize(), + 'avName': toon.getName()} + gagLast = base.localAvatar.inventory.buttonLookup(track, ToontownBattleGlobals.UBER_GAG_LEVEL_INDEX) + self.gagIcon = gagLast.copyTo(self.endTrackFrame) + self.gagIcon.setPos(0, 0, -0.25) + self.gagIcon.setScale(1.5) + + def cleanIcon(self): + self.gagIcon.removeNode() + self.gagIcon = None + return + + def cleanupEndTrack(self): + self.endTrackFrame.hide() + self.gagExpFrame.show() + self.newGagFrame.hide() + self.promotionFrame.hide() + self.questFrame.hide() + self.itemFrame.hide() + self.missedItemFrame.hide() + + def getEndTrackIntervalList(self, toon, toonList, track): + intervalList = [Func(self.endTrack, toon, toonList, track), Wait(2.0), Func(self.cleanIcon)] + return intervalList + + def showTrackIncLabel(self, track, earnedSkill): + if earnedSkill > 0: + self.trackIncLabels[track]['text'] = '+ ' + str(earnedSkill) + elif earnedSkill < 0: + self.trackIncLabels[track]['text'] = ' ' + str(earnedSkill) + self.trackIncLabels[track].show() + + def showMeritIncLabel(self, dept, earnedMerits): + self.meritIncLabels[dept]['text'] = '+ ' + str(earnedMerits) + self.meritIncLabels[dept].show() + + def getTrackIntervalList(self, toon, track, origSkill, earnedSkill, hasUber): + if hasUber < 0: + print (toon.doId, 'Reward Panel received an invalid hasUber from an uberList') + tickDelay = 0.1 + intervalList = [] + intervalList.append(Func(self.showTrackIncLabel, track, earnedSkill)) + barTime = math.log(earnedSkill + 0.5) + numTicks = int(math.ceil(barTime / tickDelay)) + for i in xrange(numTicks): + t = (i + 1) / float(numTicks) + newValue = int(origSkill + t * earnedSkill + 0.5) + intervalList.append(Func(self.incrementExp, track, newValue, toon)) + intervalList.append(Wait(tickDelay)) + + intervalList.append(Func(self.resetBarColor, track)) + intervalList.append(Wait(0.1)) + nextExpValue = self.getNextExpValue(origSkill, track) + finalGagFlag = 0 + while origSkill + earnedSkill >= nextExpValue and origSkill < nextExpValue and not finalGagFlag: + if nextExpValue != ToontownBattleGlobals.MaxSkill: + intervalList += self.getNewGagIntervalList(toon, track, ToontownBattleGlobals.Levels[track].index(nextExpValue)) + newNextExpValue = self.getNextExpValue(nextExpValue, track) + if newNextExpValue == nextExpValue: + finalGagFlag = 1 + else: + nextExpValue = newNextExpValue + + uberIndex = ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1 + currentSkill = origSkill + earnedSkill + uberSkill = ToontownBattleGlobals.UberSkill + ToontownBattleGlobals.Levels[track][ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1] + if currentSkill >= uberSkill and not hasUber > 0: + intervalList += self.getUberGagIntervalList(toon, track, ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1) + intervalList.append(Wait(0.3)) + skillDiff = currentSkill - ToontownBattleGlobals.Levels[track][ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1] + barTime = math.log(skillDiff + 0.5) + numTicks = int(math.ceil(barTime / tickDelay)) + displayedSkillDiff = skillDiff + if displayedSkillDiff > ToontownBattleGlobals.UberSkill: + displayedSkillDiff = ToontownBattleGlobals.UberSkill + intervalList.append(Func(self.showTrackIncLabel, track, -displayedSkillDiff)) + for i in xrange(numTicks): + t = (i + 1) / float(numTicks) + newValue = int(currentSkill - t * skillDiff + 0.5) + intervalList.append(Func(self.incrementExp, track, newValue, toon)) + intervalList.append(Wait(tickDelay * 0.5)) + + intervalList.append(Wait(0.3)) + return intervalList + + def getMeritIntervalList(self, toon, dept, origMerits, earnedMerits): + tickDelay = 0.08 + intervalList = [] + totalMerits = CogDisguiseGlobals.getTotalMerits(toon, dept) + neededMerits = 0 + if totalMerits and origMerits != totalMerits: + neededMerits = totalMerits - origMerits + intervalList.append(Func(self.showMeritIncLabel, dept, min(neededMerits, earnedMerits))) + barTime = math.log(earnedMerits + 1) + numTicks = int(math.ceil(barTime / tickDelay)) + for i in xrange(numTicks): + t = (i + 1) / float(numTicks) + newValue = int(origMerits + t * earnedMerits + 0.5) + intervalList.append(Func(self.incrementMerits, toon, dept, newValue, totalMerits)) + intervalList.append(Wait(tickDelay)) + + intervalList.append(Func(self.resetMeritBarColor, dept)) + intervalList.append(Wait(0.3)) + if toon.cogLevels[dept] < ToontownGlobals.MaxCogSuitLevel: + if neededMerits and toon.readyForPromotion(dept): + intervalList.append(Wait(0.3)) + intervalList += self.getPromotionIntervalList(toon, dept) + return intervalList + + def promotion(self, toon, dept): + self.endTrackFrame.hide() + self.gagExpFrame.hide() + self.newGagFrame.hide() + self.promotionFrame.show() + self.questFrame.hide() + self.itemFrame.hide() + self.missedItemFrame.hide() + name = SuitDNA.suitDepts[dept] + self.promotionFrame['text'] = TTLocalizer.RewardPanelPromotion % SuitDNA.suitDeptFullnames[name] + icons = loader.loadModel('phase_3/models/gui/cog_icons') + if dept in SuitDNA.suitDeptModelPaths: + self.deptIcon = icons.find(SuitDNA.suitDeptModelPaths[dept]).copyTo(self.promotionFrame) + icons.removeNode() + self.deptIcon.setPos(0, 0, -0.225) + self.deptIcon.setScale(0.33) + + def cleanupPromotion(self): + if not hasattr(self, 'deptIcon'): + return + self.deptIcon.removeNode() + self.deptIcon = None + self.endTrackFrame.hide() + self.gagExpFrame.show() + self.newGagFrame.hide() + self.promotionFrame.hide() + self.questFrame.hide() + self.itemFrame.hide() + self.missedItemFrame.hide() + return + + def getPromotionIntervalList(self, toon, dept): + finalDelay = 2.0 + intervalList = [Func(self.promotion, toon, dept), Wait(finalDelay), Func(self.cleanupPromotion)] + return intervalList + + def getQuestIntervalList(self, toon, deathList, toonList, origQuestsList, itemList, helpfulToonsList = []): + avId = toon.getDoId() + tickDelay = 0.2 + intervalList = [] + + cogList = [] + for i in xrange(0, len(deathList), 4): + cogIndex = deathList[i] + cogLevel = deathList[i + 1] + activeToonBits = deathList[i + 2] + flags = deathList[i + 3] + activeToonIds = [] + for j in xrange(8): + if activeToonBits & 1 << j: + if toonList[j] is not None: + activeToonIds.append(toonList[j].getDoId()) + + isSkelecog = flags & ToontownBattleGlobals.DLF_SKELECOG + isForeman = flags & ToontownBattleGlobals.DLF_FOREMAN + isBoss = flags & ToontownBattleGlobals.DLF_BOSS + isSupervisor = flags & ToontownBattleGlobals.DLF_SUPERVISOR + isVirtual = flags & ToontownBattleGlobals.DLF_VIRTUAL + hasRevives = flags & ToontownBattleGlobals.DLF_REVIVES + if isBoss > 0: + cogType = None + cogTrack = SuitDNA.suitDepts[cogIndex] + else: + cogType = SuitDNA.suitHeadTypes[cogIndex] + cogTrack = SuitDNA.getSuitDept(cogType) + cogList.append({'type': cogType, + 'level': cogLevel, + 'track': cogTrack, + 'isSkelecog': isSkelecog, + 'isForeman': isForeman, + 'isBoss': isBoss, + 'isSupervisor': isSupervisor, + 'isVirtual': isVirtual, + 'hasRevives': hasRevives, + 'activeToons': activeToonIds}) + + try: + zoneId = base.cr.playGame.getPlace().getTaskZoneId() + except: + zoneId = 0 + + avQuests = [] + for i in xrange(0, len(origQuestsList), 5): + avQuests.append(origQuestsList[i:i + 5]) + + for i in xrange(len(avQuests)): + questDesc = avQuests[i] + questId, npcId, toNpcId, rewardId, toonProgress = questDesc + quest = Quests.getQuest(questId) + if quest and i < len(self.questLabelList): + questString = quest.getString() + progressString = quest.getProgressString(toon, questDesc) + questLabel = self.questLabelList[i] + earned = 0 + orig = questDesc[4] & pow(2, 16) - 1 + num = 0 + if quest.getType() == Quests.RecoverItemQuest: + questItem = quest.getItem() + if questItem in itemList: + earned = itemList.count(questItem) + else: + for cogDict in cogList: + num = quest.doesCogCount(avId, cogDict, zoneId) + + if num: + if base.config.GetBool('battle-passing-no-credit', True): + if avId in helpfulToonsList: + earned += num + else: + self.notify.debug('avId=%d not getting %d kill cog quest credit' % (avId, num)) + else: + earned += num + + if base.localAvatar.tutorialAck: + if earned > 0: + earned = min(earned, quest.getNumQuestItems() - questDesc[4]) + if earned > 0 or base.localAvatar.tutorialAck == 0 and num == 1: + barTime = math.log(earned + 1) + numTicks = int(math.ceil(barTime / tickDelay)) + for i in xrange(numTicks): + t = (i + 1) / float(numTicks) + newValue = int(orig + t * earned + 0.5) + questDesc[4] = newValue + progressString = quest.getProgressString(toon, questDesc) + str = '%s : %s' % (questString, progressString) + if quest.getCompletionStatus(toon, questDesc) == Quests.COMPLETE: + intervalList.append(Func(questLabel.setProp, 'text_fg', (0, 0.3, 0, 1))) + intervalList.append(Func(questLabel.setProp, 'text', str)) + intervalList.append(Wait(tickDelay)) + + return intervalList + + def getItemIntervalList(self, toon, itemList): + intervalList = [] + for itemId in itemList: + itemName = Quests.getItemName(itemId) + intervalList.append(Func(self.itemLabel.setProp, 'text', itemName)) + intervalList.append(Wait(1)) + + return intervalList + + def getCogPartIntervalList(self, toon, cogPartList): + itemName = CogDisguiseGlobals.getPartName(cogPartList) + intervalList = [] + intervalList.append(Func(self.cogPartLabel.setProp, 'text', itemName)) + intervalList.append(Wait(1)) + return intervalList + + def getMissedItemIntervalList(self, toon, missedItemList): + intervalList = [] + for itemId in missedItemList: + itemName = Quests.getItemName(itemId) + intervalList.append(Func(self.missedItemLabel.setProp, 'text', itemName)) + intervalList.append(Wait(1)) + + return intervalList + + def getExpTrack(self, toon, origExp, earnedExp, deathList, origQuestsList, itemList, missedItemList, origMeritList, meritList, partList, toonList, uberEntry, helpfulToonsList, noSkip = False): + track = Sequence(Func(self.initGagFrame, toon, origExp, origMeritList, noSkip=noSkip), Wait(1.0)) + endTracks = [0, + 0, + 0, + 0, + 0, + 0, + 0] + trackEnded = 0 + for trackIndex in xrange(len(earnedExp)): + if earnedExp[trackIndex] > 0 or origExp[trackIndex] >= ToontownBattleGlobals.MaxSkill: + track += self.getTrackIntervalList(toon, trackIndex, origExp[trackIndex], earnedExp[trackIndex], ToontownBattleGlobals.getUberFlagSafe(uberEntry, trackIndex)) + maxExp = ToontownBattleGlobals.MaxSkill - ToontownBattleGlobals.UberSkill + if origExp[trackIndex] < maxExp and earnedExp[trackIndex] + origExp[trackIndex] >= maxExp: + endTracks[trackIndex] = 1 + trackEnded = 1 + + for dept in xrange(len(SuitDNA.suitDepts)): + if meritList[dept]: + track += self.getMeritIntervalList(toon, dept, origMeritList[dept], meritList[dept]) + + track.append(Wait(1.0)) + itemInterval = self.getItemIntervalList(toon, itemList) + if itemInterval: + track.append(Func(self.initItemFrame, toon)) + track.append(Wait(1.0)) + track += itemInterval + track.append(Wait(1.0)) + missedItemInterval = self.getMissedItemIntervalList(toon, missedItemList) + if missedItemInterval: + track.append(Func(self.initMissedItemFrame, toon)) + track.append(Wait(1.0)) + track += missedItemInterval + track.append(Wait(1.0)) + self.notify.debug('partList = %s' % partList) + newPart = 0 + for part in partList: + if part != 0: + newPart = 1 + break + + if newPart: + partList = self.getCogPartIntervalList(toon, partList) + if partList: + track.append(Func(self.initCogPartFrame, toon)) + track.append(Wait(1.0)) + track += partList + track.append(Wait(1.0)) + questList = self.getQuestIntervalList(toon, deathList, toonList, origQuestsList, itemList, helpfulToonsList) + if questList: + avQuests = [] + for i in xrange(0, len(origQuestsList), 5): + avQuests.append(origQuestsList[i:i + 5]) + + track.append(Func(self.initQuestFrame, toon, copy.deepcopy(avQuests))) + track.append(Wait(1.0)) + track += questList + track.append(Wait(2.0)) + track.append(Wait(0.25)) + if trackEnded: + track.append(Func(self.vanishFrames)) + track.append(Fanfare.makeFanfare(0, toon)[0]) + for i in xrange(len(endTracks)): + if endTracks[i] is 1: + track += self.getEndTrackIntervalList(toon, toonList, i) + + track.append(Func(self.cleanupEndTrack)) + return track + + def testMovie(self, otherToons = []): + track = Sequence() + track.append(Func(self.show)) + expTrack = self.getExpTrack(base.localAvatar, [1999, + 0, + 20, + 30, + 10, + 0, + 60], [2, + 0, + 2, + 6, + 1, + 0, + 8], [3, + 1, + 3, + 0, + 2, + 2, + 1, + 1, + 30, + 2, + 1, + 0], [], [], [], [0, + 0, + 0, + 0], [0, + 0, + 0, + 0], [], [base.localAvatar] + otherToons) + track.append(expTrack) + if len(track) > 0: + track.append(Func(self.hide)) + track.append(Func(base.localAvatar.loop, 'neutral')) + track.append(Func(base.localAvatar.startUpdateSmartCamera)) + track.start() + base.localAvatar.loop('victory') + base.localAvatar.stopUpdateSmartCamera() + camera.setPosHpr(0, 8, base.localAvatar.getHeight() * 0.66, 179, 15, 0) + else: + self.notify.debug('no experience, no movie.') + return None + + def _handleSkip(self): + messenger.send(self.SkipBattleMovieEvent) diff --git a/toontown/battle/SuitBattleGlobals.py b/toontown/battle/SuitBattleGlobals.py new file mode 100755 index 00000000..df0c9b73 --- /dev/null +++ b/toontown/battle/SuitBattleGlobals.py @@ -0,0 +1,3187 @@ +from BattleBase import * +import random +from direct.directnotify import DirectNotifyGlobal +from otp.otpbase import OTPLocalizer +from toontown.toonbase import TTLocalizer +notify = DirectNotifyGlobal.directNotify.newCategory('SuitBattleGlobals') +debugAttackSequence = {} + +def pickFromFreqList(freqList): + randNum = random.randint(0, 99) + count = 0 + index = 0 + level = None + for f in freqList: + count = count + f + if randNum < count: + level = index + break + index = index + 1 + + return level + + +def getActualFromRelativeLevel(name, relLevel): + data = SuitAttributes[name] + actualLevel = data['level'] + relLevel + return actualLevel + + +def getSuitVitals(name, level = -1): + data = SuitAttributes[name] + if level == -1: + level = pickFromFreqList(data['freq']) + dict = {} + dict['level'] = getActualFromRelativeLevel(name, level) + if dict['level'] == 11: + level = 0 + dict['hp'] = data['hp'][level] + dict['def'] = data['def'][level] + attacks = data['attacks'] + alist = [] + for a in attacks: + adict = {} + name = a[0] + adict['name'] = name + adict['animName'] = SuitAttacks[name][0] + adict['hp'] = a[1][level] + adict['acc'] = a[2][level] + adict['freq'] = a[3][level] + adict['group'] = SuitAttacks[name][1] + alist.append(adict) + + dict['attacks'] = alist + return dict + + +def pickSuitAttack(attacks, suitLevel): + attackNum = None + randNum = random.randint(0, 99) + notify.debug('pickSuitAttack: rolled %d' % randNum) + count = 0 + index = 0 + total = 0 + for c in attacks: + total = total + c[3][suitLevel] + + for c in attacks: + count = count + c[3][suitLevel] + if randNum < count: + attackNum = index + notify.debug('picking attack %d' % attackNum) + break + index = index + 1 + + configAttackName = simbase.config.GetString('attack-type', 'random') + if configAttackName == 'random': + return attackNum + elif configAttackName == 'sequence': + for i in xrange(len(attacks)): + if attacks[i] not in debugAttackSequence: + debugAttackSequence[attacks[i]] = 1 + return i + + return attackNum + else: + for i in xrange(len(attacks)): + if attacks[i][0] == configAttackName: + return i + + return attackNum + return + + +def getSuitAttack(suitName, suitLevel, attackNum = -1): + attackChoices = SuitAttributes[suitName]['attacks'] + if attackNum == -1: + notify.debug('getSuitAttack: picking attacking for %s' % suitName) + attackNum = pickSuitAttack(attackChoices, suitLevel) + attack = attackChoices[attackNum] + adict = {} + adict['suitName'] = suitName + name = attack[0] + adict['name'] = name + adict['id'] = SuitAttacks.keys().index(name) + adict['animName'] = SuitAttacks[name][0] + adict['hp'] = attack[1][suitLevel] + adict['acc'] = attack[2][suitLevel] + adict['freq'] = attack[3][suitLevel] + adict['group'] = SuitAttacks[name][1] + return adict + + +SuitAttributes = {'f': {'name': TTLocalizer.SuitFlunky, + 'singularname': TTLocalizer.SuitFlunkyS, + 'pluralname': TTLocalizer.SuitFlunkyP, + 'level': 0, + 'hp': (6, + 12, + 20, + 30, + 42), + 'def': (2, + 5, + 10, + 12, + 15), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('PoundKey', + (2, + 2, + 3, + 4, + 6), + (75, + 75, + 80, + 80, + 90), + (30, + 35, + 40, + 45, + 50)), ('Shred', + (3, + 4, + 5, + 6, + 7), + (50, + 55, + 60, + 65, + 70), + (10, + 15, + 20, + 25, + 30)), ('ClipOnTie', + (1, + 1, + 2, + 2, + 3), + (75, + 80, + 85, + 90, + 95), + (60, + 50, + 40, + 30, + 20)))}, + 'p': {'name': TTLocalizer.SuitPencilPusher, + 'singularname': TTLocalizer.SuitPencilPusherS, + 'pluralname': TTLocalizer.SuitPencilPusherP, + 'level': 1, + 'hp': (12, + 20, + 30, + 42, + 56), + 'def': (5, + 10, + 15, + 20, + 25), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (45, + 50, + 55, + 60, + 65), + 'attacks': (('FountainPen', + (2, + 3, + 4, + 6, + 9), + (75, + 75, + 75, + 75, + 75), + (20, + 20, + 20, + 20, + 20)), + ('RubOut', + (4, + 5, + 6, + 8, + 12), + (75, + 75, + 75, + 75, + 75), + (20, + 20, + 20, + 20, + 20)), + ('FingerWag', + (1, + 2, + 2, + 3, + 4), + (75, + 75, + 75, + 75, + 75), + (35, + 30, + 25, + 20, + 15)), + ('WriteOff', + (4, + 6, + 8, + 10, + 12), + (75, + 75, + 75, + 75, + 75), + (5, + 10, + 15, + 20, + 25)), + ('FillWithLead', + (3, + 4, + 5, + 6, + 7), + (75, + 75, + 75, + 75, + 75), + (20, + 20, + 20, + 20, + 20)))}, + 'ym': {'name': TTLocalizer.SuitYesman, + 'singularname': TTLocalizer.SuitYesmanS, + 'pluralname': TTLocalizer.SuitYesmanP, + 'level': 2, + 'hp': (20, + 30, + 42, + 56, + 72), + 'def': (10, + 15, + 20, + 25, + 30), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (65, + 70, + 75, + 80, + 85), + 'attacks': (('RubberStamp', + (2, + 2, + 3, + 3, + 4), + (75, + 75, + 75, + 75, + 75), + (35, + 35, + 35, + 35, + 35)), + ('RazzleDazzle', + (1, + 1, + 1, + 1, + 1), + (50, + 50, + 50, + 50, + 50), + (25, + 20, + 15, + 10, + 5)), + ('Synergy', + (4, + 5, + 6, + 7, + 8), + (50, + 60, + 70, + 80, + 90), + (5, + 10, + 15, + 20, + 25)), + ('TeeOff', + (3, + 3, + 4, + 4, + 5), + (50, + 60, + 70, + 80, + 90), + (35, + 35, + 35, + 35, + 35)))}, + 'mm': {'name': TTLocalizer.SuitMicromanager, + 'singularname': TTLocalizer.SuitMicromanagerS, + 'pluralname': TTLocalizer.SuitMicromanagerP, + 'level': 3, + 'hp': (30, + 42, + 56, + 72, + 90), + 'def': (15, + 20, + 25, + 30, + 35), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (70, + 75, + 80, + 82, + 85), + 'attacks': (('Demotion', + (6, + 8, + 12, + 15, + 18), + (50, + 60, + 70, + 80, + 90), + (30, + 30, + 30, + 30, + 30)), + ('FingerWag', + (4, + 6, + 9, + 12, + 15), + (50, + 60, + 70, + 80, + 90), + (10, + 10, + 10, + 10, + 10)), + ('FountainPen', + (3, + 4, + 6, + 8, + 10), + (50, + 60, + 70, + 80, + 90), + (15, + 15, + 15, + 15, + 15)), + ('BrainStorm', + (4, + 6, + 9, + 12, + 15), + (5, + 5, + 5, + 5, + 5), + (25, + 25, + 25, + 25, + 25)), + ('BuzzWord', + (4, + 6, + 9, + 12, + 15), + (50, + 60, + 70, + 80, + 90), + (20, + 20, + 20, + 20, + 20)))}, + 'ds': {'name': TTLocalizer.SuitDownsizer, + 'singularname': TTLocalizer.SuitDownsizerS, + 'pluralname': TTLocalizer.SuitDownsizerP, + 'level': 4, + 'hp': (42, + 56, + 72, + 90, + 110), + 'def': (20, + 25, + 30, + 35, + 40), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('Canned', + (5, + 6, + 8, + 10, + 12), + (60, + 75, + 80, + 85, + 90), + (25, + 25, + 25, + 25, + 25)), + ('Downsize', + (8, + 9, + 11, + 13, + 15), + (50, + 65, + 70, + 75, + 80), + (35, + 35, + 35, + 35, + 35)), + ('PinkSlip', + (4, + 5, + 6, + 7, + 8), + (60, + 65, + 75, + 80, + 85), + (25, + 25, + 25, + 25, + 25)), + ('Sacked', + (5, + 6, + 7, + 8, + 9), + (50, + 50, + 50, + 50, + 50), + (15, + 15, + 15, + 15, + 15)))}, + 'hh': {'name': TTLocalizer.SuitHeadHunter, + 'singularname': TTLocalizer.SuitHeadHunterS, + 'pluralname': TTLocalizer.SuitHeadHunterP, + 'level': 5, + 'hp': (56, + 72, + 90, + 110, + 132), + 'def': (25, + 30, + 35, + 40, + 45), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('FountainPen', + (5, + 6, + 8, + 10, + 12), + (60, + 75, + 80, + 85, + 90), + (15, + 15, + 15, + 15, + 15)), + ('GlowerPower', + (7, + 8, + 10, + 12, + 13), + (50, + 60, + 70, + 80, + 90), + (20, + 20, + 20, + 20, + 20)), + ('HalfWindsor', + (8, + 10, + 12, + 14, + 16), + (60, + 65, + 70, + 75, + 80), + (20, + 20, + 20, + 20, + 20)), + ('HeadShrink', + (10, + 12, + 15, + 18, + 21), + (65, + 75, + 80, + 85, + 95), + (20, + 20, + 20, + 20, + 20)), + ('ReOrg', + (5, + 8, + 11, + 13, + 15), + (65, + 75, + 80, + 85, + 90), + (15, + 15, + 15, + 15, + 15)), + ('Rolodex', + (6, + 7, + 8, + 9, + 10), + (60, + 65, + 70, + 75, + 80), + (10, + 10, + 10, + 10, + 10)))}, + 'cr': {'name': TTLocalizer.SuitCorporateRaider, + 'singularname': TTLocalizer.SuitCorporateRaiderS, + 'pluralname': TTLocalizer.SuitCorporateRaiderP, + 'level': 6, + 'hp': (72, + 90, + 110, + 132, + 156), + 'def': (30, + 35, + 40, + 45, + 50), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('Canned', + (6, + 7, + 8, + 9, + 10), + (60, + 75, + 80, + 85, + 90), + (20, + 20, + 20, + 20, + 20)), + ('EvilEye', + (12, + 15, + 18, + 21, + 24), + (60, + 70, + 75, + 80, + 90), + (35, + 35, + 35, + 35, + 35)), + ('PlayHardball', + (7, + 8, + 12, + 15, + 16), + (60, + 65, + 70, + 75, + 80), + (30, + 30, + 30, + 30, + 30)), + ('PowerTie', + (10, + 12, + 14, + 16, + 18), + (65, + 75, + 80, + 85, + 95), + (15, + 15, + 15, + 15, + 15)))}, + 'tbc': {'name': TTLocalizer.SuitTheBigCheese, + 'singularname': TTLocalizer.SuitTheBigCheeseS, + 'pluralname': TTLocalizer.SuitTheBigCheeseP, + 'level': 7, + 'hp': (90, + 110, + 132, + 156, + 200), + 'def': (35, + 40, + 45, + 50, + 55), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('CigarSmoke', + (10, + 12, + 15, + 18, + 20), + (55, + 65, + 75, + 85, + 95), + (20, + 20, + 20, + 20, + 20)), + ('FloodTheMarket', + (14, + 16, + 18, + 20, + 22), + (70, + 75, + 85, + 90, + 95), + (10, + 10, + 10, + 10, + 10)), + ('SongAndDance', + (14, + 15, + 17, + 19, + 20), + (60, + 65, + 70, + 75, + 80), + (20, + 20, + 20, + 20, + 20)), + ('TeeOff', + (8, + 11, + 14, + 17, + 20), + (55, + 65, + 70, + 75, + 80), + (50, + 50, + 50, + 50, + 50)))}, + 'cc': {'name': TTLocalizer.SuitColdCaller, + 'singularname': TTLocalizer.SuitColdCallerS, + 'pluralname': TTLocalizer.SuitColdCallerP, + 'level': 0, + 'hp': (6, + 12, + 20, + 30, + 42), + 'def': (2, + 5, + 10, + 12, + 15), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('FreezeAssets', + (1, + 1, + 1, + 1, + 1), + (90, + 90, + 90, + 90, + 90), + (5, + 10, + 15, + 20, + 25)), + ('PoundKey', + (2, + 2, + 3, + 4, + 5), + (75, + 80, + 85, + 90, + 95), + (25, + 25, + 25, + 25, + 25)), + ('DoubleTalk', + (2, + 3, + 4, + 6, + 8), + (50, + 55, + 60, + 65, + 70), + (25, + 25, + 25, + 25, + 25)), + ('HotAir', + (3, + 4, + 6, + 8, + 10), + (50, + 50, + 50, + 50, + 50), + (45, + 40, + 35, + 30, + 25)))}, + 'tm': {'name': TTLocalizer.SuitTelemarketer, + 'singularname': TTLocalizer.SuitTelemarketerS, + 'pluralname': TTLocalizer.SuitTelemarketerP, + 'level': 1, + 'hp': (12, + 20, + 30, + 42, + 56), + 'def': (5, + 10, + 15, + 20, + 25), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (45, + 50, + 55, + 60, + 65), + 'attacks': (('ClipOnTie', + (2, + 2, + 3, + 3, + 4), + (75, + 75, + 75, + 75, + 75), + (15, + 15, + 15, + 15, + 15)), + ('PickPocket', + (1, + 1, + 1, + 1, + 1), + (75, + 75, + 75, + 75, + 75), + (15, + 15, + 15, + 15, + 15)), + ('Rolodex', + (4, + 6, + 7, + 9, + 12), + (50, + 50, + 50, + 50, + 50), + (30, + 30, + 30, + 30, + 30)), + ('DoubleTalk', + (4, + 6, + 7, + 9, + 12), + (75, + 80, + 85, + 90, + 95), + (40, + 40, + 40, + 40, + 40)))}, + 'nd': {'name': TTLocalizer.SuitNameDropper, + 'singularname': TTLocalizer.SuitNameDropperS, + 'pluralname': TTLocalizer.SuitNameDropperP, + 'level': 2, + 'hp': (20, + 30, + 42, + 56, + 72), + 'def': (10, + 15, + 20, + 25, + 30), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (65, + 70, + 75, + 80, + 85), + 'attacks': (('RazzleDazzle', + (4, + 5, + 6, + 9, + 12), + (75, + 80, + 85, + 90, + 95), + (30, + 30, + 30, + 30, + 30)), + ('Rolodex', + (5, + 6, + 7, + 10, + 14), + (95, + 95, + 95, + 95, + 95), + (40, + 40, + 40, + 40, + 40)), + ('Synergy', + (3, + 4, + 6, + 9, + 12), + (50, + 50, + 50, + 50, + 50), + (15, + 15, + 15, + 15, + 15)), + ('PickPocket', + (2, + 2, + 2, + 2, + 2), + (95, + 95, + 95, + 95, + 95), + (15, + 15, + 15, + 15, + 15)))}, + 'gh': {'name': TTLocalizer.SuitGladHander, + 'singularname': TTLocalizer.SuitGladHanderS, + 'pluralname': TTLocalizer.SuitGladHanderP, + 'level': 3, + 'hp': (30, + 42, + 56, + 72, + 90), + 'def': (15, + 20, + 25, + 30, + 35), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (70, + 75, + 80, + 82, + 85), + 'attacks': (('RubberStamp', + (4, + 3, + 3, + 2, + 1), + (90, + 70, + 50, + 30, + 10), + (40, + 30, + 20, + 10, + 5)), + ('FountainPen', + (3, + 3, + 2, + 1, + 1), + (70, + 60, + 50, + 40, + 30), + (40, + 30, + 20, + 10, + 5)), + ('Filibuster', + (4, + 6, + 9, + 12, + 15), + (30, + 40, + 50, + 60, + 70), + (10, + 20, + 30, + 40, + 45)), + ('Schmooze', + (5, + 7, + 11, + 15, + 20), + (55, + 65, + 75, + 85, + 95), + (10, + 20, + 30, + 40, + 45)))}, + 'ms': {'name': TTLocalizer.SuitMoverShaker, + 'singularname': TTLocalizer.SuitMoverShakerS, + 'pluralname': TTLocalizer.SuitMoverShakerP, + 'level': 4, + 'hp': (42, + 56, + 72, + 90, + 110), + 'def': (20, + 25, + 30, + 35, + 40), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('BrainStorm', + (5, + 6, + 8, + 10, + 12), + (60, + 75, + 80, + 85, + 90), + (15, + 15, + 15, + 15, + 15)), + ('HalfWindsor', + (6, + 9, + 11, + 13, + 16), + (50, + 65, + 70, + 75, + 80), + (20, + 20, + 20, + 20, + 20)), + ('Quake', + (9, + 12, + 15, + 18, + 21), + (60, + 65, + 75, + 80, + 85), + (20, + 20, + 20, + 20, + 20)), + ('Shake', + (6, + 8, + 10, + 12, + 14), + (70, + 75, + 80, + 85, + 90), + (25, + 25, + 25, + 25, + 25)), + ('Tremor', + (5, + 6, + 7, + 8, + 9), + (50, + 50, + 50, + 50, + 50), + (20, + 20, + 20, + 20, + 20)))}, + 'tf': {'name': TTLocalizer.SuitTwoFace, + 'singularname': TTLocalizer.SuitTwoFaceS, + 'pluralname': TTLocalizer.SuitTwoFaceP, + 'level': 5, + 'hp': (56, + 72, + 90, + 110, + 132), + 'def': (25, + 30, + 35, + 40, + 45), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('EvilEye', + (10, + 12, + 14, + 16, + 18), + (60, + 75, + 80, + 85, + 90), + (25, + 25, + 25, + 25, + 25)), + ('HangUp', + (7, + 8, + 10, + 12, + 13), + (50, + 60, + 70, + 80, + 90), + (15, + 15, + 15, + 15, + 15)), + ('RazzleDazzle', + (8, + 10, + 12, + 14, + 16), + (60, + 65, + 70, + 75, + 80), + (25, + 25, + 25, + 25, + 25)), + ('ReOrg', + (5, + 8, + 11, + 13, + 15), + (65, + 75, + 80, + 85, + 90), + (15, + 15, + 15, + 15, + 15)), + ('RedTape', + (6, + 7, + 8, + 9, + 10), + (60, + 65, + 75, + 85, + 90), + (20, + 20, + 20, + 20, + 20)))}, + 'm': {'name': TTLocalizer.SuitTheMingler, + 'singularname': TTLocalizer.SuitTheMinglerS, + 'pluralname': TTLocalizer.SuitTheMinglerP, + 'level': 6, + 'hp': (72, + 90, + 110, + 132, + 156), + 'def': (30, + 35, + 40, + 45, + 50), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('BuzzWord', + (10, + 11, + 13, + 15, + 16), + (60, + 75, + 80, + 85, + 90), + (20, + 20, + 20, + 20, + 20)), + ('ParadigmShift', + (12, + 15, + 18, + 21, + 24), + (60, + 70, + 75, + 80, + 90), + (20, + 20, + 20, + 20, + 20)), + ('PowerTrip', + (10, + 13, + 14, + 15, + 18), + (60, + 65, + 70, + 75, + 80), + (15, + 15, + 15, + 15, + 15)), + ('Schmooze', + (7, + 8, + 12, + 15, + 16), + (55, + 65, + 75, + 85, + 95), + (20, + 20, + 20, + 20, + 20)), + ('CigarSmoke', + (10, + 12, + 15, + 18, + 20), + (55, + 65, + 75, + 85, + 95), + (15, + 15, + 15, + 15, + 15)), + ('TeeOff', + (8, + 9, + 10, + 11, + 12), + (70, + 75, + 80, + 85, + 95), + (10, + 10, + 10, + 10, + 10)))}, + 'mh': {'name': TTLocalizer.SuitMrHollywood, + 'singularname': TTLocalizer.SuitMrHollywoodS, + 'pluralname': TTLocalizer.SuitMrHollywoodP, + 'level': 7, + 'hp': (90, + 110, + 132, + 156, + 200), + 'def': (35, + 40, + 45, + 50, + 55), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('PowerTrip', + (10, + 12, + 15, + 18, + 20), + (55, + 65, + 75, + 85, + 95), + (50, + 50, + 50, + 50, + 50)), ('RazzleDazzle', + (8, + 11, + 14, + 17, + 20), + (70, + 75, + 85, + 90, + 95), + (50, + 50, + 50, + 50, + 50)))}, + 'sc': {'name': TTLocalizer.SuitShortChange, + 'singularname': TTLocalizer.SuitShortChangeS, + 'pluralname': TTLocalizer.SuitShortChangeP, + 'level': 0, + 'hp': (6, + 12, + 20, + 30, + 42), + 'def': (2, + 5, + 10, + 12, + 15), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('Watercooler', + (2, + 2, + 3, + 4, + 6), + (50, + 50, + 50, + 50, + 50), + (20, + 20, + 20, + 20, + 20)), + ('BounceCheck', + (3, + 5, + 7, + 9, + 11), + (75, + 80, + 85, + 90, + 95), + (15, + 15, + 15, + 15, + 15)), + ('ClipOnTie', + (1, + 1, + 2, + 2, + 3), + (50, + 50, + 50, + 50, + 50), + (25, + 25, + 25, + 25, + 25)), + ('PickPocket', + (2, + 2, + 3, + 4, + 6), + (95, + 95, + 95, + 95, + 95), + (40, + 40, + 40, + 40, + 40)))}, + 'pp': {'name': TTLocalizer.SuitPennyPincher, + 'singularname': TTLocalizer.SuitPennyPincherS, + 'pluralname': TTLocalizer.SuitPennyPincherP, + 'level': 1, + 'hp': (12, + 20, + 30, + 42, + 56), + 'def': (5, + 10, + 15, + 20, + 25), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (45, + 50, + 55, + 60, + 65), + 'attacks': (('BounceCheck', + (4, + 5, + 6, + 8, + 12), + (75, + 75, + 75, + 75, + 75), + (45, + 45, + 45, + 45, + 45)), ('FreezeAssets', + (2, + 3, + 4, + 6, + 9), + (75, + 75, + 75, + 75, + 75), + (20, + 20, + 20, + 20, + 20)), ('FingerWag', + (1, + 2, + 3, + 4, + 6), + (50, + 50, + 50, + 50, + 50), + (35, + 35, + 35, + 35, + 35)))}, + 'tw': {'name': TTLocalizer.SuitTightwad, + 'singularname': TTLocalizer.SuitTightwadS, + 'pluralname': TTLocalizer.SuitTightwadP, + 'level': 2, + 'hp': (20, + 30, + 42, + 56, + 72), + 'def': (10, + 15, + 20, + 25, + 30), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (65, + 70, + 75, + 80, + 85), + 'attacks': (('Fired', + (3, + 4, + 5, + 5, + 6), + (75, + 75, + 75, + 75, + 75), + (75, + 5, + 5, + 5, + 5)), + ('GlowerPower', + (3, + 4, + 6, + 9, + 12), + (95, + 95, + 95, + 95, + 95), + (10, + 15, + 20, + 25, + 30)), + ('FingerWag', + (3, + 3, + 4, + 4, + 5), + (75, + 75, + 75, + 75, + 75), + (5, + 70, + 5, + 5, + 5)), + ('FreezeAssets', + (3, + 4, + 6, + 9, + 12), + (75, + 75, + 75, + 75, + 75), + (5, + 5, + 65, + 5, + 30)), + ('BounceCheck', + (5, + 6, + 9, + 13, + 18), + (75, + 75, + 75, + 75, + 75), + (5, + 5, + 5, + 60, + 30)))}, + 'bc': {'name': TTLocalizer.SuitBeanCounter, + 'singularname': TTLocalizer.SuitBeanCounterS, + 'pluralname': TTLocalizer.SuitBeanCounterP, + 'level': 3, + 'hp': (30, + 42, + 56, + 72, + 90), + 'def': (15, + 20, + 25, + 30, + 35), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (70, + 75, + 80, + 82, + 85), + 'attacks': (('Audit', + (4, + 6, + 9, + 12, + 15), + (95, + 95, + 95, + 95, + 95), + (20, + 20, + 20, + 20, + 20)), + ('Calculate', + (4, + 6, + 9, + 12, + 15), + (75, + 75, + 75, + 75, + 75), + (25, + 25, + 25, + 25, + 25)), + ('Tabulate', + (4, + 6, + 9, + 12, + 15), + (75, + 75, + 75, + 75, + 75), + (25, + 25, + 25, + 25, + 25)), + ('WriteOff', + (4, + 6, + 9, + 12, + 15), + (95, + 95, + 95, + 95, + 95), + (30, + 30, + 30, + 30, + 30)))}, + 'nc': {'name': TTLocalizer.SuitNumberCruncher, + 'singularname': TTLocalizer.SuitNumberCruncherS, + 'pluralname': TTLocalizer.SuitNumberCruncherP, + 'level': 4, + 'hp': (42, + 56, + 72, + 90, + 110), + 'def': (20, + 25, + 30, + 35, + 40), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('Audit', + (5, + 6, + 8, + 10, + 12), + (60, + 75, + 80, + 85, + 90), + (15, + 15, + 15, + 15, + 15)), + ('Calculate', + (6, + 7, + 9, + 11, + 13), + (50, + 65, + 70, + 75, + 80), + (30, + 30, + 30, + 30, + 30)), + ('Crunch', + (8, + 9, + 11, + 13, + 15), + (60, + 65, + 75, + 80, + 85), + (35, + 35, + 35, + 35, + 35)), + ('Tabulate', + (5, + 6, + 7, + 8, + 9), + (50, + 50, + 50, + 50, + 50), + (20, + 20, + 20, + 20, + 20)))}, + 'mb': {'name': TTLocalizer.SuitMoneyBags, + 'singularname': TTLocalizer.SuitMoneyBagsS, + 'pluralname': TTLocalizer.SuitMoneyBagsP, + 'level': 5, + 'hp': (56, + 72, + 90, + 110, + 132), + 'def': (25, + 30, + 35, + 40, + 45), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('Liquidate', + (10, + 12, + 14, + 16, + 18), + (60, + 75, + 80, + 85, + 90), + (30, + 30, + 30, + 30, + 30)), ('MarketCrash', + (8, + 10, + 12, + 14, + 16), + (60, + 65, + 70, + 75, + 80), + (45, + 45, + 45, + 45, + 45)), ('PowerTie', + (6, + 7, + 8, + 9, + 10), + (60, + 65, + 75, + 85, + 90), + (25, + 25, + 25, + 25, + 25)))}, + 'ls': {'name': TTLocalizer.SuitLoanShark, + 'singularname': TTLocalizer.SuitLoanSharkS, + 'pluralname': TTLocalizer.SuitLoanSharkP, + 'level': 6, + 'hp': (72, + 90, + 110, + 132, + 156), + 'def': (30, + 35, + 40, + 45, + 50), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('Bite', + (10, + 11, + 13, + 15, + 16), + (60, + 75, + 80, + 85, + 90), + (30, + 30, + 30, + 30, + 30)), + ('Chomp', + (12, + 15, + 18, + 21, + 24), + (60, + 70, + 75, + 80, + 90), + (35, + 35, + 35, + 35, + 35)), + ('PlayHardball', + (9, + 11, + 12, + 13, + 15), + (55, + 65, + 75, + 85, + 95), + (20, + 20, + 20, + 20, + 20)), + ('WriteOff', + (6, + 8, + 10, + 12, + 14), + (70, + 75, + 80, + 85, + 95), + (15, + 15, + 15, + 15, + 15)))}, + 'rb': {'name': TTLocalizer.SuitRobberBaron, + 'singularname': TTLocalizer.SuitRobberBaronS, + 'pluralname': TTLocalizer.SuitRobberBaronP, + 'level': 7, + 'hp': (90, + 110, + 132, + 156, + 200), + 'def': (35, + 40, + 45, + 50, + 55), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('PowerTrip', + (11, + 14, + 16, + 18, + 21), + (60, + 65, + 70, + 75, + 80), + (33, + 33, + 33, + 33, + 33)), + ('CigarSmoke', + (10, + 12, + 15, + 18, + 20), + (55, + 65, + 75, + 85, + 95), + (33, + 33, + 33, + 33, + 33)), ('TeeOff', + (10, + 12, + 14, + 16, + 18), + (60, + 65, + 75, + 85, + 90), + (33, + 33, + 33, + 33, + 33)))}, + 'bf': {'name': TTLocalizer.SuitBottomFeeder, + 'singularname': TTLocalizer.SuitBottomFeederS, + 'pluralname': TTLocalizer.SuitBottomFeederP, + 'level': 0, + 'hp': (6, + 12, + 20, + 30, + 42), + 'def': (2, + 5, + 10, + 12, + 15), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('RubberStamp', + (2, + 3, + 4, + 5, + 6), + (75, + 80, + 85, + 90, + 95), + (20, + 20, + 20, + 20, + 20)), + ('Shred', + (2, + 4, + 6, + 8, + 10), + (50, + 55, + 60, + 65, + 70), + (20, + 20, + 20, + 20, + 20)), + ('Watercooler', + (3, + 4, + 5, + 6, + 7), + (95, + 95, + 95, + 95, + 95), + (10, + 10, + 10, + 10, + 10)), + ('PickPocket', + (1, + 1, + 2, + 2, + 3), + (25, + 30, + 35, + 40, + 45), + (50, + 50, + 50, + 50, + 50)))}, + 'b': {'name': TTLocalizer.SuitBloodsucker, + 'singularname': TTLocalizer.SuitBloodsuckerS, + 'pluralname': TTLocalizer.SuitBloodsuckerP, + 'level': 1, + 'hp': (12, + 20, + 30, + 42, + 56), + 'def': (5, + 10, + 15, + 20, + 25), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (45, + 50, + 55, + 60, + 65), + 'attacks': (('EvictionNotice', + (1, + 2, + 3, + 3, + 4), + (75, + 75, + 75, + 75, + 75), + (20, + 20, + 20, + 20, + 20)), + ('RedTape', + (2, + 3, + 4, + 6, + 9), + (75, + 75, + 75, + 75, + 75), + (20, + 20, + 20, + 20, + 20)), + ('Withdrawal', + (6, + 8, + 10, + 12, + 14), + (95, + 95, + 95, + 95, + 95), + (10, + 10, + 10, + 10, + 10)), + ('Liquidate', + (2, + 3, + 4, + 6, + 9), + (50, + 60, + 70, + 80, + 90), + (50, + 50, + 50, + 50, + 50)))}, + 'dt': {'name': TTLocalizer.SuitDoubleTalker, + 'singularname': TTLocalizer.SuitDoubleTalkerS, + 'pluralname': TTLocalizer.SuitDoubleTalkerP, + 'level': 2, + 'hp': (20, + 30, + 42, + 56, + 72), + 'def': (10, + 15, + 20, + 25, + 30), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (65, + 70, + 75, + 80, + 85), + 'attacks': (('RubberStamp', + (1, + 1, + 1, + 1, + 1), + (50, + 60, + 70, + 80, + 90), + (5, + 5, + 5, + 5, + 5)), + ('BounceCheck', + (1, + 1, + 1, + 1, + 1), + (50, + 60, + 70, + 80, + 90), + (5, + 5, + 5, + 5, + 5)), + ('BuzzWord', + (1, + 2, + 3, + 5, + 6), + (50, + 60, + 70, + 80, + 90), + (20, + 20, + 20, + 20, + 20)), + ('DoubleTalk', + (6, + 6, + 9, + 13, + 18), + (50, + 60, + 70, + 80, + 90), + (25, + 25, + 25, + 25, + 25)), + ('Jargon', + (3, + 4, + 6, + 9, + 12), + (50, + 60, + 70, + 80, + 90), + (25, + 25, + 25, + 25, + 25)), + ('MumboJumbo', + (3, + 4, + 6, + 9, + 12), + (50, + 60, + 70, + 80, + 90), + (20, + 20, + 20, + 20, + 20)))}, + 'ac': {'name': TTLocalizer.SuitAmbulanceChaser, + 'singularname': TTLocalizer.SuitAmbulanceChaserS, + 'pluralname': TTLocalizer.SuitAmbulanceChaserP, + 'level': 3, + 'hp': (30, + 42, + 56, + 72, + 90), + 'def': (15, + 20, + 25, + 30, + 35), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (65, + 70, + 75, + 80, + 85), + 'attacks': (('Shake', + (4, + 6, + 9, + 12, + 15), + (75, + 75, + 75, + 75, + 75), + (15, + 15, + 15, + 15, + 15)), + ('RedTape', + (6, + 8, + 12, + 15, + 19), + (75, + 75, + 75, + 75, + 75), + (30, + 30, + 30, + 30, + 30)), + ('Rolodex', + (3, + 4, + 5, + 6, + 7), + (75, + 75, + 75, + 75, + 75), + (20, + 20, + 20, + 20, + 20)), + ('HangUp', + (2, + 3, + 4, + 5, + 6), + (75, + 75, + 75, + 75, + 75), + (35, + 35, + 35, + 35, + 35)))}, + 'bs': {'name': TTLocalizer.SuitBackStabber, + 'singularname': TTLocalizer.SuitBackStabberS, + 'pluralname': TTLocalizer.SuitBackStabberP, + 'level': 4, + 'hp': (42, + 56, + 72, + 90, + 110), + 'def': (20, + 25, + 30, + 35, + 40), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('GuiltTrip', + (8, + 11, + 13, + 15, + 18), + (60, + 75, + 80, + 85, + 90), + (35, + 35, + 35, + 35, + 35)), ('RestrainingOrder', + (6, + 7, + 9, + 11, + 13), + (50, + 65, + 70, + 75, + 90), + (25, + 25, + 25, + 25, + 25)), ('CigarSmoke', + (10, + 12, + 15, + 18, + 20), + (55, + 65, + 75, + 85, + 95), + (15, + 15, + 15, + 15, + 15)), ('FingerWag', + (5, + 6, + 7, + 8, + 9), + (50, + 55, + 65, + 75, + 80), + (25, + 25, + 25, + 25, + 25)))}, + 'sd': {'name': TTLocalizer.SuitSpinDoctor, + 'singularname': TTLocalizer.SuitSpinDoctorS, + 'pluralname': TTLocalizer.SuitSpinDoctorP, + 'level': 5, + 'hp': (56, + 72, + 90, + 110, + 132), + 'def': (25, + 30, + 35, + 40, + 45), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('ParadigmShift', + (9, + 10, + 13, + 16, + 17), + (60, + 75, + 80, + 85, + 90), + (30, + 30, + 30, + 30, + 30)), + ('Quake', + (8, + 10, + 12, + 14, + 16), + (60, + 65, + 70, + 75, + 80), + (20, + 20, + 20, + 20, + 20)), + ('Spin', + (10, + 12, + 15, + 18, + 20), + (70, + 75, + 80, + 85, + 90), + (20, + 20, + 20, + 20, + 20)), + ('ReOrg', + (5, + 8, + 11, + 13, + 15), + (65, + 75, + 80, + 85, + 90), + (15, + 15, + 15, + 15, + 15)), + ('WriteOff', + (6, + 7, + 8, + 9, + 10), + (60, + 65, + 75, + 85, + 90), + (15, + 15, + 15, + 15, + 15)))}, + 'le': {'name': TTLocalizer.SuitLegalEagle, + 'singularname': TTLocalizer.SuitLegalEagleS, + 'pluralname': TTLocalizer.SuitLegalEagleP, + 'level': 6, + 'hp': (72, + 90, + 110, + 132, + 156), + 'def': (30, + 35, + 40, + 45, + 50), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('EvilEye', + (10, + 11, + 13, + 15, + 16), + (60, + 75, + 80, + 85, + 90), + (20, + 20, + 20, + 20, + 20)), + ('Jargon', + (7, + 9, + 11, + 13, + 15), + (60, + 70, + 75, + 80, + 90), + (15, + 15, + 15, + 15, + 15)), + ('Legalese', + (11, + 13, + 16, + 19, + 21), + (55, + 65, + 75, + 85, + 95), + (35, + 35, + 35, + 35, + 35)), + ('PeckingOrder', + (12, + 15, + 17, + 19, + 22), + (70, + 75, + 80, + 85, + 95), + (30, + 30, + 30, + 30, + 30)))}, + 'bw': {'name': TTLocalizer.SuitBigWig, + 'singularname': TTLocalizer.SuitBigWigS, + 'pluralname': TTLocalizer.SuitBigWigP, + 'level': 7, + 'hp': (90, + 110, + 132, + 156, + 200), + 'def': (35, + 40, + 45, + 50, + 55), + 'freq': (50, + 30, + 10, + 5, + 5), + 'acc': (35, + 40, + 45, + 50, + 55), + 'attacks': (('PowerTrip', + (10, + 11, + 13, + 15, + 16), + (75, + 80, + 85, + 90, + 95), + (33, + 33, + 33, + 33, + 33)), ('ThrowBook', + (14, + 16, + 18, + 20, + 22), + (70, + 75, + 85, + 90, + 95), + (33, + 33, + 33, + 33, + 33)), ('FingerWag', + (13, + 15, + 17, + 19, + 21), + (80, + 85, + 85, + 85, + 90), + (33, + 33, + 33, + 33, + 33)))}} +ATK_TGT_UNKNOWN = 1 +ATK_TGT_SINGLE = 2 +ATK_TGT_GROUP = 3 +SuitAttacks = {'Audit': ('phone', ATK_TGT_SINGLE), + 'Bite': ('throw-paper', ATK_TGT_SINGLE), + 'BounceCheck': ('throw-paper', ATK_TGT_SINGLE), + 'BrainStorm': ('effort', ATK_TGT_SINGLE), + 'BuzzWord': ('speak', ATK_TGT_SINGLE), + 'Calculate': ('phone', ATK_TGT_SINGLE), + 'Canned': ('throw-paper', ATK_TGT_SINGLE), + 'Chomp': ('throw-paper', ATK_TGT_SINGLE), + 'CigarSmoke': ('cigar-smoke', ATK_TGT_SINGLE), + 'ClipOnTie': ('throw-paper', ATK_TGT_SINGLE), + 'Crunch': ('throw-object', ATK_TGT_SINGLE), + 'Demotion': ('magic1', ATK_TGT_SINGLE), + 'DoubleTalk': ('speak', ATK_TGT_SINGLE), + 'Downsize': ('magic2', ATK_TGT_SINGLE), + 'EvictionNotice': ('throw-paper', ATK_TGT_SINGLE), + 'EvilEye': ('glower', ATK_TGT_SINGLE), + 'Filibuster': ('speak', ATK_TGT_SINGLE), + 'FillWithLead': ('pencil-sharpener', ATK_TGT_SINGLE), + 'FingerWag': ('finger-wag', ATK_TGT_SINGLE), + 'Fired': ('magic2', ATK_TGT_SINGLE), + 'FiveOClockShadow': ('glower', ATK_TGT_SINGLE), + 'FloodTheMarket': ('glower', ATK_TGT_SINGLE), + 'FountainPen': ('pen-squirt', ATK_TGT_SINGLE), + 'FreezeAssets': ('glower', ATK_TGT_SINGLE), + 'Gavel': ('gavel', ATK_TGT_SINGLE), + 'GlowerPower': ('glower', ATK_TGT_SINGLE), + 'GuiltTrip': ('magic1', ATK_TGT_GROUP), + 'HalfWindsor': ('throw-paper', ATK_TGT_SINGLE), + 'HangUp': ('phone', ATK_TGT_SINGLE), + 'HeadShrink': ('magic1', ATK_TGT_SINGLE), + 'HotAir': ('speak', ATK_TGT_SINGLE), + 'Jargon': ('speak', ATK_TGT_SINGLE), + 'Legalese': ('speak', ATK_TGT_SINGLE), + 'Liquidate': ('magic1', ATK_TGT_SINGLE), + 'MarketCrash': ('throw-paper', ATK_TGT_SINGLE), + 'MumboJumbo': ('speak', ATK_TGT_SINGLE), + 'ParadigmShift': ('magic2', ATK_TGT_GROUP), + 'PeckingOrder': ('throw-object', ATK_TGT_SINGLE), + 'PickPocket': ('pickpocket', ATK_TGT_SINGLE), + 'PinkSlip': ('throw-paper', ATK_TGT_SINGLE), + 'PlayHardball': ('throw-paper', ATK_TGT_SINGLE), + 'PoundKey': ('phone', ATK_TGT_SINGLE), + 'PowerTie': ('throw-paper', ATK_TGT_SINGLE), + 'PowerTrip': ('magic1', ATK_TGT_GROUP), + 'Quake': ('quick-jump', ATK_TGT_GROUP), + 'RazzleDazzle': ('smile', ATK_TGT_SINGLE), + 'RedTape': ('throw-object', ATK_TGT_SINGLE), + 'ReOrg': ('magic3', ATK_TGT_SINGLE), + 'RestrainingOrder': ('throw-paper', ATK_TGT_SINGLE), + 'Rolodex': ('roll-o-dex', ATK_TGT_SINGLE), + 'RubberStamp': ('rubber-stamp', ATK_TGT_SINGLE), + 'RubOut': ('hold-eraser', ATK_TGT_SINGLE), + 'Sacked': ('throw-paper', ATK_TGT_SINGLE), + 'SandTrap': ('golf-club-swing', ATK_TGT_SINGLE), + 'Schmooze': ('speak', ATK_TGT_SINGLE), + 'Shake': ('stomp', ATK_TGT_GROUP), + 'Shred': ('shredder', ATK_TGT_SINGLE), + 'SongAndDance': ('song-and-dance', ATK_TGT_SINGLE), + 'Spin': ('magic3', ATK_TGT_SINGLE), + 'Synergy': ('magic3', ATK_TGT_GROUP), + 'Tabulate': ('phone', ATK_TGT_SINGLE), + 'TeeOff': ('golf-club-swing', ATK_TGT_SINGLE), + 'ThrowBook': ('throw-object', ATK_TGT_SINGLE), + 'Tremor': ('stomp', ATK_TGT_GROUP), + 'Watercooler': ('watercooler', ATK_TGT_SINGLE), + 'Withdrawal': ('magic1', ATK_TGT_SINGLE), + 'WriteOff': ('hold-pencil', ATK_TGT_SINGLE)} +AUDIT = SuitAttacks.keys().index('Audit') +BITE = SuitAttacks.keys().index('Bite') +BOUNCE_CHECK = SuitAttacks.keys().index('BounceCheck') +BRAIN_STORM = SuitAttacks.keys().index('BrainStorm') +BUZZ_WORD = SuitAttacks.keys().index('BuzzWord') +CALCULATE = SuitAttacks.keys().index('Calculate') +CANNED = SuitAttacks.keys().index('Canned') +CHOMP = SuitAttacks.keys().index('Chomp') +CIGAR_SMOKE = SuitAttacks.keys().index('CigarSmoke') +CLIPON_TIE = SuitAttacks.keys().index('ClipOnTie') +CRUNCH = SuitAttacks.keys().index('Crunch') +DEMOTION = SuitAttacks.keys().index('Demotion') +DOWNSIZE = SuitAttacks.keys().index('Downsize') +DOUBLE_TALK = SuitAttacks.keys().index('DoubleTalk') +EVICTION_NOTICE = SuitAttacks.keys().index('EvictionNotice') +EVIL_EYE = SuitAttacks.keys().index('EvilEye') +FILIBUSTER = SuitAttacks.keys().index('Filibuster') +FILL_WITH_LEAD = SuitAttacks.keys().index('FillWithLead') +FINGER_WAG = SuitAttacks.keys().index('FingerWag') +FIRED = SuitAttacks.keys().index('Fired') +FIVE_O_CLOCK_SHADOW = SuitAttacks.keys().index('FiveOClockShadow') +FLOOD_THE_MARKET = SuitAttacks.keys().index('FloodTheMarket') +FOUNTAIN_PEN = SuitAttacks.keys().index('FountainPen') +FREEZE_ASSETS = SuitAttacks.keys().index('FreezeAssets') +GAVEL = SuitAttacks.keys().index('Gavel') +GLOWER_POWER = SuitAttacks.keys().index('GlowerPower') +GUILT_TRIP = SuitAttacks.keys().index('GuiltTrip') +HALF_WINDSOR = SuitAttacks.keys().index('HalfWindsor') +HANG_UP = SuitAttacks.keys().index('HangUp') +HEAD_SHRINK = SuitAttacks.keys().index('HeadShrink') +HOT_AIR = SuitAttacks.keys().index('HotAir') +JARGON = SuitAttacks.keys().index('Jargon') +LEGALESE = SuitAttacks.keys().index('Legalese') +LIQUIDATE = SuitAttacks.keys().index('Liquidate') +MARKET_CRASH = SuitAttacks.keys().index('MarketCrash') +MUMBO_JUMBO = SuitAttacks.keys().index('MumboJumbo') +PARADIGM_SHIFT = SuitAttacks.keys().index('ParadigmShift') +PECKING_ORDER = SuitAttacks.keys().index('PeckingOrder') +PICK_POCKET = SuitAttacks.keys().index('PickPocket') +PINK_SLIP = SuitAttacks.keys().index('PinkSlip') +PLAY_HARDBALL = SuitAttacks.keys().index('PlayHardball') +POUND_KEY = SuitAttacks.keys().index('PoundKey') +POWER_TIE = SuitAttacks.keys().index('PowerTie') +POWER_TRIP = SuitAttacks.keys().index('PowerTrip') +QUAKE = SuitAttacks.keys().index('Quake') +RAZZLE_DAZZLE = SuitAttacks.keys().index('RazzleDazzle') +RED_TAPE = SuitAttacks.keys().index('RedTape') +RE_ORG = SuitAttacks.keys().index('ReOrg') +RESTRAINING_ORDER = SuitAttacks.keys().index('RestrainingOrder') +ROLODEX = SuitAttacks.keys().index('Rolodex') +RUBBER_STAMP = SuitAttacks.keys().index('RubberStamp') +RUB_OUT = SuitAttacks.keys().index('RubOut') +SACKED = SuitAttacks.keys().index('Sacked') +SANDTRAP = SuitAttacks.keys().index('SandTrap') +SCHMOOZE = SuitAttacks.keys().index('Schmooze') +SHAKE = SuitAttacks.keys().index('Shake') +SHRED = SuitAttacks.keys().index('Shred') +SONG_AND_DANCE = SuitAttacks.keys().index('SongAndDance') +SPIN = SuitAttacks.keys().index('Spin') +SYNERGY = SuitAttacks.keys().index('Synergy') +TABULATE = SuitAttacks.keys().index('Tabulate') +TEE_OFF = SuitAttacks.keys().index('TeeOff') +THROW_BOOK = SuitAttacks.keys().index('ThrowBook') +TREMOR = SuitAttacks.keys().index('Tremor') +WATERCOOLER = SuitAttacks.keys().index('Watercooler') +WITHDRAWAL = SuitAttacks.keys().index('Withdrawal') +WRITE_OFF = SuitAttacks.keys().index('WriteOff') + +def getFaceoffTaunt(suitName, doId): + if suitName in SuitFaceoffTaunts: + taunts = SuitFaceoffTaunts[suitName] + else: + taunts = TTLocalizer.SuitFaceoffDefaultTaunts + return taunts[doId % len(taunts)] + + +SuitFaceoffTaunts = OTPLocalizer.SuitFaceoffTaunts + +def getAttackTauntIndexFromIndex(suit, attackIndex): + adict = getSuitAttack(suit.getStyleName(), suit.getLevel(), attackIndex) + return getAttackTauntIndex(adict['name']) + + +def getAttackTauntIndex(attackName): + if attackName in SuitAttackTaunts: + taunts = SuitAttackTaunts[attackName] + return random.randint(0, len(taunts) - 1) + else: + return 1 + + +def getAttackTaunt(attackName, index = None): + if attackName in SuitAttackTaunts: + taunts = SuitAttackTaunts[attackName] + else: + taunts = TTLocalizer.SuitAttackDefaultTaunts + if index != None: + if index >= len(taunts): + notify.warning('index exceeds length of taunts list in getAttackTaunt') + return TTLocalizer.SuitAttackDefaultTaunts[0] + return taunts[index] + else: + return random.choice(taunts) + return + + +SuitAttackTaunts = TTLocalizer.SuitAttackTaunts +DisabledAttacks = ('Gavel', 'SongAndDance', 'SandTrap', 'FloodTheMarket', 'FiveOClockShadow') + +def getAttacksByType(attributes): + groupAttacks = [] + singleAttacks = [] + + for attack in sorted(attributes['attacks'], key=lambda x: x[0]): + if attack[0] in DisabledAttacks: + continue + if SuitAttacks[attack[0]][1] == ATK_TGT_GROUP: + groupAttacks.append(attack) + else: + singleAttacks.append(attack) + + return groupAttacks, singleAttacks \ No newline at end of file diff --git a/toontown/battle/__init__.py b/toontown/battle/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/building/BoardingGroupShow.py b/toontown/building/BoardingGroupShow.py new file mode 100755 index 00000000..b89e08f2 --- /dev/null +++ b/toontown/building/BoardingGroupShow.py @@ -0,0 +1,220 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from toontown.toonbase import TTLocalizer +from toontown.effects import DustCloud +TRACK_TYPE_TELEPORT = 1 +TRACK_TYPE_RUN = 2 +TRACK_TYPE_POOF = 3 + +class BoardingGroupShow: + notify = DirectNotifyGlobal.directNotify.newCategory('BoardingGroupShow') + thresholdRunDistance = 25.0 + + def __init__(self, toon): + self.toon = toon + self.avId = self.toon.doId + self.dustCloudIval = None + return + + def cleanup(self): + if localAvatar.doId == self.avId: + self.__stopTimer() + self.clock.removeNode() + + def startTimer(self): + self.clockNode = TextNode('elevatorClock') + self.clockNode.setFont(ToontownGlobals.getSignFont()) + self.clockNode.setAlign(TextNode.ACenter) + self.clockNode.setTextColor(0.5, 0.5, 0.5, 1) + self.clockNode.setText(str(int(self.countdownDuration))) + self.clock = aspect2d.attachNewNode(self.clockNode) + self.clock.setPos(0, 0, -0.6) + self.clock.setScale(0.15, 0.15, 0.15) + self.__countdown(self.countdownDuration, self.__boardingElevatorTimerExpired) + + def __countdown(self, duration, callback): + self.countdownTask = Task(self.__timerTask) + self.countdownTask.duration = duration + self.countdownTask.callback = callback + taskMgr.remove(self.uniqueName(self.avId)) + return taskMgr.add(self.countdownTask, self.uniqueName(self.avId)) + + def __timerTask(self, task): + countdownTime = int(task.duration - task.time) + timeStr = self.timeWarningText + str(countdownTime) + if self.clockNode.getText() != timeStr: + self.clockNode.setText(timeStr) + if task.time >= task.duration: + if task.callback: + task.callback() + return Task.done + else: + return Task.cont + + def __boardingElevatorTimerExpired(self): + self.notify.debug('__boardingElevatorTimerExpired') + self.clock.removeNode() + + def __stopTimer(self): + if self.countdownTask: + self.countdownTask.callback = None + taskMgr.remove(self.countdownTask) + return + + def uniqueName(self, avId): + uniqueName = 'boardingElevatorTimerTask-' + str(avId) + return uniqueName + + def getBoardingTrack(self, elevatorModel, offset, offsetWrtRender, wantToonRotation): + self.timeWarningText = TTLocalizer.BoardingTimeWarning + self.countdownDuration = 6 + trackType = TRACK_TYPE_TELEPORT + boardingTrack = Sequence() + if self.toon: + if self.avId == localAvatar.doId: + boardingTrack.append(Func(self.startTimer)) + isInThresholdDist = self.__isInThresholdDist(elevatorModel, offset, self.thresholdRunDistance) + isRunPathClear = self.__isRunPathClear(elevatorModel, offsetWrtRender) + if isInThresholdDist and isRunPathClear: + boardingTrack.append(self.__getRunTrack(elevatorModel, offset, wantToonRotation)) + trackType = TRACK_TYPE_RUN + elif self.toon.isDisguised: + boardingTrack.append(self.__getPoofTeleportTrack(elevatorModel, offset, wantToonRotation)) + trackType = TRACK_TYPE_POOF + else: + boardingTrack.append(self.__getTeleportTrack(elevatorModel, offset, wantToonRotation)) + boardingTrack.append(Func(self.cleanup)) + return (boardingTrack, trackType) + + def __getOffsetPos(self, elevatorModel, offset): + dest = elevatorModel.getPos(self.toon.getParent()) + dest += Vec3(*offset) + return dest + + def __getTeleportTrack(self, elevatorModel, offset, wantToonRotation): + teleportTrack = Sequence() + if self.toon: + if wantToonRotation: + teleportTrack.append(Func(self.toon.headsUp, elevatorModel, offset)) + teleportTrack.append(Func(self.toon.setAnimState, 'TeleportOut')) + teleportTrack.append(Wait(3.5)) + teleportTrack.append(Func(self.toon.setPos, Point3(offset))) + teleportTrack.append(Func(self.toon.setAnimState, 'TeleportIn')) + teleportTrack.append(Wait(1)) + return teleportTrack + + def __getPoofTeleportTrack(self, elevatorModel, offset, wantToonRotation): + teleportTrack = Sequence() + if wantToonRotation: + teleportTrack.append(Func(self.toon.headsUp, elevatorModel, offset)) + + def getDustCloudPos(): + toonPos = self.toon.getPos(render) + return Point3(toonPos.getX(), toonPos.getY(), toonPos.getZ() + 3) + + def cleanupDustCloudIval(): + if self.dustCloudIval: + self.dustCloudIval.finish() + self.dustCloudIval = None + return + + def getDustCloudIval(): + cleanupDustCloudIval() + dustCloud = DustCloud.DustCloud(fBillboard=0, wantSound=1) + dustCloud.setBillboardAxis(2.0) + dustCloud.setZ(3) + dustCloud.setScale(0.4) + dustCloud.createTrack() + self.dustCloudIval = Sequence(Func(dustCloud.reparentTo, render), Func(dustCloud.setPos, getDustCloudPos()), dustCloud.track, Func(dustCloud.detachNode), Func(dustCloud.destroy), name='dustCloadIval') + self.dustCloudIval.start() + + if self.toon: + teleportTrack.append(Func(self.toon.setAnimState, 'neutral')) + teleportTrack.append(Wait(0.5)) + teleportTrack.append(Func(getDustCloudIval)) + teleportTrack.append(Wait(0.25)) + teleportTrack.append(Func(self.toon.hide)) + teleportTrack.append(Wait(1.5)) + teleportTrack.append(Func(self.toon.setPos, Point3(offset))) + teleportTrack.append(Func(getDustCloudIval)) + teleportTrack.append(Wait(0.25)) + teleportTrack.append(Func(self.toon.show)) + teleportTrack.append(Wait(0.5)) + teleportTrack.append(Func(cleanupDustCloudIval)) + return teleportTrack + + def __getRunTrack(self, elevatorModel, offset, wantToonRotation): + runTrack = Sequence() + if self.toon: + if wantToonRotation: + runTrack.append(Func(self.toon.headsUp, elevatorModel, offset)) + if self.toon.isDisguised: + runTrack.append(Func(self.toon.suit.loop, 'walk')) + else: + runTrack.append(Func(self.toon.setAnimState, 'run')) + runTrack.append(LerpPosInterval(self.toon, 1, Point3(offset))) + return runTrack + + def __isInThresholdDist(self, elevatorModel, offset, thresholdDist): + diff = Point3(offset) - self.toon.getPos() + if diff.length() > thresholdDist: + return False + else: + return True + + def __isRunPathClear(self, elevatorModel, offsetWrtRender): + pathClear = True + source = self.toon.getPos(render) + dest = offsetWrtRender + collSegment = CollisionSegment(source[0], source[1], source[2], dest[0], dest[1], dest[2]) + fromObject = render.attachNewNode(CollisionNode('runCollSegment')) + fromObject.node().addSolid(collSegment) + fromObject.node().setFromCollideMask(ToontownGlobals.WallBitmask) + fromObject.node().setIntoCollideMask(BitMask32.allOff()) + queue = CollisionHandlerQueue() + base.cTrav.addCollider(fromObject, queue) + base.cTrav.traverse(render) + queue.sortEntries() + if queue.getNumEntries(): + for entryNum in xrange(queue.getNumEntries()): + entry = queue.getEntry(entryNum) + hitObject = entry.getIntoNodePath() + if hitObject.getNetTag('pieCode') != '3': + pathClear = False + + base.cTrav.removeCollider(fromObject) + fromObject.removeNode() + return pathClear + + def getGoButtonShow(self, elevatorName): + self.elevatorName = elevatorName + self.timeWarningText = TTLocalizer.BoardingGoShow % self.elevatorName + self.countdownDuration = 4 + goButtonShow = Sequence() + if self.toon: + if self.avId == localAvatar.doId: + goButtonShow.append(Func(self.startTimer)) + goButtonShow.append(self.__getTeleportOutTrack()) + goButtonShow.append(Wait(3)) + goButtonShow.append(Func(self.cleanup)) + return goButtonShow + + def __getTeleportOutTrack(self): + teleportOutTrack = Sequence() + if self.toon and not self.toon.isDisguised: + teleportOutTrack.append(Func(self.toon.b_setAnimState, 'TeleportOut')) + return teleportOutTrack + + def getGoButtonPreShow(self): + self.timeWarningText = TTLocalizer.BoardingGoPreShow + self.countdownDuration = 4 + goButtonPreShow = Sequence() + if self.toon: + if self.avId == localAvatar.doId: + goButtonPreShow.append(Func(self.startTimer)) + goButtonPreShow.append(Wait(3)) + goButtonPreShow.append(Func(self.cleanup)) + return goButtonPreShow diff --git a/toontown/building/BoardingPartyBase.py b/toontown/building/BoardingPartyBase.py new file mode 100755 index 00000000..fe470a4d --- /dev/null +++ b/toontown/building/BoardingPartyBase.py @@ -0,0 +1,113 @@ +from otp.otpbase import OTPGlobals +from toontown.toonbase import ToontownGlobals +import copy +BOARDCODE_OKAY = 1 +BOARDCODE_MISSING = 0 +BOARDCODE_PROMOTION = -1 +BOARDCODE_BATTLE = -2 +BOARDCODE_SPACE = -3 +BOARDCODE_DIFF_GROUP = -4 +BOARDCODE_PENDING_INVITE = -5 +BOARDCODE_IN_ELEVATOR = -6 +BOARDCODE_GROUPS_TOO_LARGE = -7 # JBS +INVITE_ACCEPT_FAIL_GROUP_FULL = -1 + +class BoardingPartyBase: + + def __init__(self): + self.groupListDict = {} + self.avIdDict = {} + self.mergeDict = {} + + def cleanup(self): + del self.groupListDict + del self.avIdDict + del self.mergeDict + + def getGroupSize(self): + return self.maxSize + + def setGroupSize(self, groupSize): + self.maxSize = groupSize + + def getGroupLeader(self, avatarId): + if avatarId in self.avIdDict: + leaderId = self.avIdDict[avatarId] + return leaderId + else: + return None + return None + + def isGroupLeader(self, avatarId): + leaderId = self.getGroupLeader(avatarId) + if avatarId == leaderId: + return True + else: + return False + + def getGroupMemberList(self, avatarId): + if avatarId in self.avIdDict: + leaderId = self.avIdDict[avatarId] + group = self.groupListDict.get(leaderId) + if group: + returnList = copy.copy(group[0]) + if 0 in returnList: + returnList.remove(0) + return returnList + return [] + + def getGroupInviteList(self, avatarId): + if avatarId in self.avIdDict: + leaderId = self.avIdDict[avatarId] + group = self.groupListDict.get(leaderId) + if group: + returnList = copy.copy(group[1]) + if 0 in returnList: + returnList.remove(0) + return returnList + return [] + + def getGroupKickList(self, avatarId): + if avatarId in self.avIdDict: + leaderId = self.avIdDict[avatarId] + group = self.groupListDict.get(leaderId) + if group: + returnList = copy.copy(group[2]) + if 0 in returnList: + returnList.remove(0) + return returnList + return [] + + def hasActiveGroup(self, avatarId): + memberList = self.getGroupMemberList(avatarId) + if avatarId in memberList: + if len(memberList) > 1: + return True + return False + + def hasPendingInvite(self, avatarId): + pendingInvite = False + if avatarId in self.mergeDict: + return True + if avatarId in self.avIdDict: + leaderId = self.avIdDict[avatarId] + leaderInviteList = self.getGroupInviteList(leaderId) + if leaderId == avatarId: + if len(leaderInviteList): + pendingInvite = True + else: + pendingInvite = False + elif avatarId in leaderInviteList: + pendingInvite = True + else: + pendingInvite = False + if pendingInvite: + return True + else: + return False + + def isInGroup(self, memberId, leaderId): + if memberId in self.getGroupMemberList(leaderId) or memberId in self.getGroupInviteList(leaderId): + return True + else: + return False diff --git a/toontown/building/DistributedAnimatedProp.py b/toontown/building/DistributedAnimatedProp.py new file mode 100755 index 00000000..c3618c80 --- /dev/null +++ b/toontown/building/DistributedAnimatedProp.py @@ -0,0 +1,77 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.distributed import DistributedObject + +class DistributedAnimatedProp(DistributedObject.DistributedObject): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.fsm = ClassicFSM.ClassicFSM('DistributedAnimatedProp', [State.State('off', self.enterOff, self.exitOff, ['playing', 'attract']), State.State('attract', self.enterAttract, self.exitAttract, ['playing']), State.State('playing', self.enterPlaying, self.exitPlaying, ['attract'])], 'off', 'off') + self.fsm.enterInitialState() + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.setState(self.initialState, self.initialStateTimestamp) + del self.initialState + del self.initialStateTimestamp + + def disable(self): + self.fsm.request('off') + DistributedObject.DistributedObject.disable(self) + + def delete(self): + del self.fsm + DistributedObject.DistributedObject.delete(self) + + def setPropId(self, propId): + self.propId = propId + + def setAvatarInteract(self, avatarId): + self.avatarId = avatarId + + def setOwnerDoId(self, ownerDoId): + self.ownerDoId = ownerDoId + + def setState(self, state, timestamp): + if self.isGenerated(): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + else: + self.initialState = state + self.initialStateTimestamp = timestamp + + def enterTrigger(self, args = None): + messenger.send('DistributedAnimatedProp_enterTrigger') + self.sendUpdate('requestInteract') + + def exitTrigger(self, args = None): + messenger.send('DistributedAnimatedProp_exitTrigger') + self.sendUpdate('requestExit') + + def rejectInteract(self): + self.cr.playGame.getPlace().setState('walk') + + def avatarExit(self, avatarId): + pass + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterAttract(self, ts): + pass + + def exitAttract(self): + pass + + def enterPlaying(self, ts): + pass + + def exitPlaying(self): + pass diff --git a/toontown/building/DistributedAnimatedPropAI.py b/toontown/building/DistributedAnimatedPropAI.py new file mode 100755 index 00000000..43b10261 --- /dev/null +++ b/toontown/building/DistributedAnimatedPropAI.py @@ -0,0 +1,82 @@ +from direct.distributed import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from otp.ai.AIBaseGlobal import * + + +class DistributedAnimatedPropAI(DistributedObjectAI.DistributedObjectAI): + + def __init__(self, air, propId): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.fsm = ClassicFSM.ClassicFSM( + 'DistributedAnimatedPropAI', + [ + State.State('off', self.enterOff, self.exitOff, + ['playing']), + State.State('attract', self.enterAttract, self.exitAttract, + ['playing']), + State.State('playing', self.enterPlaying, self.exitPlaying, + ['attract']) + ], 'off', 'off') + self.fsm.enterInitialState() + self.propId = propId + self.avatarId = 0 + + def delete(self): + self.fsm.requestFinalState() + del self.fsm + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getPropId(self): + return self.propId + + def getAvatarInteract(self): + return self.avatarId + + def getInitialState(self): + return [self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()] + + def getOwnerDoId(self): + return self.ownerDoId + + def requestInteract(self): + avatarId = self.air.getAvatarIdFromSender() + stateName = self.fsm.getCurrentState().getName() + if stateName != 'playing': + self.sendUpdate('setAvatarInteract', [avatarId]) + self.avatarId = avatarId + self.fsm.request('playing') + else: + self.sendUpdateToAvatarId(avatarId, 'rejectInteract', []) + + def requestExit(self): + avatarId = self.air.getAvatarIdFromSender() + if avatarId == self.avatarId: + stateName = self.fsm.getCurrentState().getName() + if stateName == 'playing': + self.sendUpdate('avatarExit', [avatarId]) + self.fsm.request('attract') + + def getState(self): + return [self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()] + + def d_setState(self, state): + self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()]) + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterAttract(self): + self.d_setState('attract') + + def exitAttract(self): + pass + + def enterPlaying(self): + self.d_setState('playing') + + def exitPlaying(self): + pass diff --git a/toontown/building/DistributedBBElevator.py b/toontown/building/DistributedBBElevator.py new file mode 100755 index 00000000..f79a930b --- /dev/null +++ b/toontown/building/DistributedBBElevator.py @@ -0,0 +1,29 @@ +import DistributedElevator +import DistributedBossElevator +from ElevatorConstants import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer + +class DistributedBBElevator(DistributedBossElevator.DistributedBossElevator): + + def __init__(self, cr): + DistributedBossElevator.DistributedBossElevator.__init__(self, cr) + self.type = ELEVATOR_BB + self.countdownTime = ElevatorData[self.type]['countdown'] + self.elevatorPoints = BossbotElevatorPoints + + def setupElevator(self): + geom = base.cr.playGame.hood.loader.geom + self.elevatorModel = loader.loadModel('phase_12/models/bossbotHQ/BB_Elevator') + self.leftDoor = self.elevatorModel.find('**/left-door') + if self.leftDoor.isEmpty(): + self.leftDoor = self.elevatorModel.find('**/left_door') + self.rightDoor = self.elevatorModel.find('**/right-door') + if self.rightDoor.isEmpty(): + self.rightDoor = self.elevatorModel.find('**/right_door') + locator = geom.find('**/elevator_locator') + self.elevatorModel.reparentTo(locator) + DistributedElevator.DistributedElevator.setupElevator(self) + + def getDestName(self): + return TTLocalizer.ElevatorBossBotBoss diff --git a/toontown/building/DistributedBBElevatorAI.py b/toontown/building/DistributedBBElevatorAI.py new file mode 100755 index 00000000..f953e91e --- /dev/null +++ b/toontown/building/DistributedBBElevatorAI.py @@ -0,0 +1,9 @@ +from ElevatorConstants import * +import DistributedBossElevatorAI + +class DistributedBBElevatorAI(DistributedBossElevatorAI.DistributedBossElevatorAI): + + def __init__(self, air, bldg, zone): + DistributedBossElevatorAI.DistributedBossElevatorAI.__init__(self, air, bldg, zone) + self.type = ELEVATOR_BB + self.countdownTime = ElevatorData[self.type]['countdown'] \ No newline at end of file diff --git a/toontown/building/DistributedBoardingParty.py b/toontown/building/DistributedBoardingParty.py new file mode 100755 index 00000000..382b56b9 --- /dev/null +++ b/toontown/building/DistributedBoardingParty.py @@ -0,0 +1,567 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from panda3d.core import * + +import BoardingGroupShow +from toontown.building import BoardingPartyBase +from otp.nametag.NametagConstants import * +from otp.margins.WhisperPopup import * +from toontown.hood import ZoneUtil +from toontown.toon import BoardingGroupInviterPanels +from toontown.toon import GroupInvitee +from toontown.toon import GroupPanel +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog + + +class DistributedBoardingParty(DistributedObject.DistributedObject, BoardingPartyBase.BoardingPartyBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBoardingParty') + InvitationFailedTimeout = 60.0 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + BoardingPartyBase.BoardingPartyBase.__init__(self) + self.groupInviteePanel = None + self.groupPanel = None + self.inviterPanels = BoardingGroupInviterPanels.BoardingGroupInviterPanels() + self.lastInvitationFailedMessage = {} + self.goToPreShowTrack = None + self.goToShowTrack = None + return + + def generate(self): + self.load() + DistributedObject.DistributedObject.generate(self) + localAvatar.boardingParty = self + + def announceGenerate(self): + canonicalZoneId = ZoneUtil.getCanonicalZoneId(self.zoneId) + self.notify.debug('canonicalZoneId = %s' % canonicalZoneId) + localAvatar.chatMgr.chatInputSpeedChat.addBoardingGroupMenu(canonicalZoneId) + + def delete(self): + DistributedObject.DistributedObject.delete(self) + + def disable(self): + self.finishGoToPreShowTrack() + self.finishGoToShowTrack() + self.forceCleanupInviteePanel() + self.forceCleanupInviterPanels() + self.inviterPanels = None + if self.groupPanel: + self.groupPanel.cleanup() + self.groupPanel = None + DistributedObject.DistributedObject.disable(self) + BoardingPartyBase.BoardingPartyBase.cleanup(self) + localAvatar.boardingParty = None + localAvatar.chatMgr.chatInputSpeedChat.removeBoardingGroupMenu() + self.lastInvitationFailedMessage = {} + return + + def getElevatorIdList(self): + return self.elevatorIdList + + def setElevatorIdList(self, elevatorIdList): + self.notify.debug('setElevatorIdList') + self.elevatorIdList = elevatorIdList + + def load(self): + pass + + # this is a pretty brute force group check that will see if a toon + # (in the same zone) is already in a group. This might introduce + # lag in a very crowded district with hundreds of active groups + # (if we should be so lucky) + def isInGroup(self, avId): + if avId in self.groupListDict: + return True + for leader in self.groupListDict: + group = self.groupListDict.get(leader) + for member in group[0]: + if member == avId: + return True + return False + + def countInGroup(self, avId): + if avId in self.groupListDict: + group = self.groupListDict.get(avId) + return len(group[0]) + for leader in self.groupListDict: + group = self.groupListDict.get(leader) + for member in group[0]: + if member == avId: + return len(group[0]) + return 0 + + def postGroupInfo(self, leaderId, memberList, inviteeList, kickedList): + self.notify.debug('postgroupInfo') + isMyGroup = 0 + removedMemberIdList = [] + if leaderId in self.groupListDict: + oldGroupEntry = self.groupListDict[leaderId] + else: + oldGroupEntry = [[], [], []] + oldMemberList = oldGroupEntry[0] + newGroupEntry = [memberList, inviteeList, kickedList] + self.groupListDict[leaderId] = newGroupEntry + if not len(oldMemberList) == len(memberList): + for oldMember in oldMemberList: + if oldMember not in memberList: + if oldMember in self.avIdDict: + if self.avIdDict[oldMember] == leaderId: + self.avIdDict.pop(oldMember) + removedMemberIdList.append(oldMember) + + self.avIdDict[leaderId] = leaderId + if leaderId == localAvatar.doId: + isMyGroup = 1 + for memberId in memberList: + self.avIdDict[memberId] = leaderId + if memberId == localAvatar.doId: + isMyGroup = 1 + + if newGroupEntry[0] == [0] or not newGroupEntry[0]: + dgroup = self.groupListDict.pop(leaderId) + for memberId in dgroup[0]: + if memberId in self.avIdDict: + self.avIdDict.pop(memberId) + + if isMyGroup: + self.notify.debug('new info posted on my group') + # update the leaderId in case it has changed (group merge) + if self.groupPanel and self.groupPanel.leaderId == localAvatar.doId and leaderId != localAvatar.doId: + self.groupPanel.cleanup() + self.groupPanel = None + if not self.groupPanel: + self.groupPanel = GroupPanel.GroupPanel(self) + messenger.send('updateGroupStatus') + for removedMemberId in removedMemberIdList: + removedMember = base.cr.doId2do.get(removedMemberId) + if not removedMember: + removedMember = base.cr.identifyFriend(removedMemberId) + if removedMember: + removedMemberName = removedMember.name + messageText = TTLocalizer.BoardingMessageLeftGroup % removedMemberName + localAvatar.setSystemMessage(removedMemberId, messageText, WTToontownBoardingGroup) + + elif localAvatar.doId in oldMemberList and localAvatar.doId not in memberList: + messenger.send('updateGroupStatus') + if self.groupPanel: + self.groupPanel.cleanup() + self.groupPanel = None + else: + self.notify.debug('new info posted on some other group') + return + + def postInvite(self, leaderId, inviterId, merger): + self.notify.debug('post Invite') + if not base.localAvatar.isIgnored(inviterId): + inviter = base.cr.doId2do.get(inviterId) + if inviter: + if self.inviterPanels.isInvitingPanelUp() or self.inviterPanels.isInvitationRejectedPanelUp(): + self.inviterPanels.forceCleanup() + self.groupInviteePanel = GroupInvitee.GroupInvitee() + self.groupInviteePanel.make(self, inviter, leaderId, merger) + if base.config.GetBool('reject-boarding-group-invites', 0): + self.groupInviteePanel.forceCleanup() + self.groupInviteePanel = None + return + + def postKick(self, leaderId): + self.notify.debug('%s was kicked out of the Boarding Group by %s' % (localAvatar.doId, leaderId)) + localAvatar.setSystemMessage(leaderId, TTLocalizer.BoardingMessageKickedOut, WTToontownBoardingGroup) + + def postSizeReject(self, leaderId, inviterId, inviteeId): + self.notify.debug('%s was not invited because the group is full' % inviteeId) + + def postKickReject(self, leaderId, inviterId, inviteeId): + self.notify.debug('%s was not invited because %s has kicked them from the group' % (inviteeId, leaderId)) + + def postInviteDelcined(self, inviteeId): + self.notify.debug("%s delinced %s's Boarding Group invitation." % (inviteeId, localAvatar.doId)) + invitee = base.cr.doId2do.get(inviteeId) + if invitee: + self.inviterPanels.createInvitationRejectedPanel(self, inviteeId) + + def postInviteAccepted(self, inviteeId): + self.notify.debug("%s accepted %s's Boarding Group invitation." % (inviteeId, localAvatar.doId)) + if self.inviterPanels.isInvitingPanelIdCorrect(inviteeId): + self.inviterPanels.destroyInvitingPanel() + + def postInviteCanceled(self): + self.notify.debug('The invitation to the Boarding Group was cancelled') + if self.isInviteePanelUp(): + self.groupInviteePanel.cleanup() + self.groupInviteePanel = None + return + + def postInviteNotQualify(self, avId, reason): + messenger.send('updateGroupStatus') + rejectText = '' + if avId == localAvatar.doId: + if reason == BoardingPartyBase.BOARDCODE_PROMOTION: + rejectText = TTLocalizer.BoardingInvitePromotionInviter + else: + avatar = base.cr.doId2do.get(avId) + if avatar: + avatarNameText = avatar.name + else: + avatarNameText = '' + if reason == BoardingPartyBase.BOARDCODE_PROMOTION: + rejectText = TTLocalizer.BoardingInvitePromotionInvitee % avatarNameText + if reason == BoardingPartyBase.BOARDCODE_BATTLE: + rejectText = TTLocalizer.TeleportPanelNotAvailable % avatarNameText + if reason == BoardingPartyBase.BOARDCODE_DIFF_GROUP: + rejectText = TTLocalizer.BoardingInviteeInDiffGroup % avatarNameText + if reason == BoardingPartyBase.BOARDCODE_PENDING_INVITE: + rejectText = TTLocalizer.BoardingInviteePendingIvite % avatarNameText + if reason == BoardingPartyBase.BOARDCODE_IN_ELEVATOR: + rejectText = TTLocalizer.BoardingInviteeInElevator % avatarNameText + if reason == BoardingPartyBase.BOARDCODE_GROUPS_TOO_LARGE: # JBS + rejectText = TTLocalizer.BoardingGroupsTooLarge % avatarNameText + if self.inviterPanels.isInvitingPanelIdCorrect(avId) or avId == localAvatar.doId: + self.inviterPanels.destroyInvitingPanel() + self.showMe(rejectText) + + def postAlreadyInGroup(self): + self.showMe(TTLocalizer.BoardingAlreadyInGroup) + + def postGroupAlreadyFull(self): + self.showMe(TTLocalizer.BoardingGroupAlreadyFull) + + def postSomethingMissing(self): + self.showMe(TTLocalizer.BoardcodeMissing) + + def postRejectBoard(self, elevatorId, reason, avatarsFailingRequirements, avatarsInBattle): + self.showRejectMessage(elevatorId, reason, avatarsFailingRequirements, avatarsInBattle) + self.enableGoButton() + + def postRejectGoto(self, elevatorId, reason, avatarsFailingRequirements, avatarsInBattle): + self.showRejectMessage(elevatorId, reason, avatarsFailingRequirements, avatarsInBattle) + + def postMessageInvited(self, inviteeId, inviterId): + inviterName = '' + inviteeName = '' + inviter = base.cr.doId2do.get(inviterId) + if inviter: + inviterName = inviter.name + invitee = base.cr.doId2do.get(inviteeId) + if invitee: + inviteeName = invitee.name + messageText = TTLocalizer.BoardingMessageInvited % (inviterName, inviteeName) + localAvatar.setSystemMessage(inviteeId, messageText, WTToontownBoardingGroup) + + def postMessageInvitationFailed(self, inviterId): + inviterName = '' + inviter = base.cr.doId2do.get(inviterId) + if inviter: + inviterName = inviter.name + if self.invitationFailedMessageOk(inviterId): + messageText = TTLocalizer.BoardingMessageInvitationFailed % inviterName + localAvatar.setSystemMessage(inviterId, messageText, WTToontownBoardingGroup) + + def postMessageAcceptanceFailed(self, inviteeId, reason): + inviteeName = '' + messageText = '' + invitee = base.cr.doId2do.get(inviteeId) + if invitee: + inviteeName = invitee.name + if reason == BoardingPartyBase.INVITE_ACCEPT_FAIL_GROUP_FULL: + messageText = TTLocalizer.BoardingMessageGroupFull % inviteeName + localAvatar.setSystemMessage(inviteeId, messageText, WTToontownBoardingGroup) + if self.inviterPanels.isInvitingPanelIdCorrect(inviteeId): + self.inviterPanels.destroyInvitingPanel() + + def invitationFailedMessageOk(self, inviterId): + now = globalClock.getFrameTime() + lastTime = self.lastInvitationFailedMessage.get(inviterId, None) + if lastTime: + elapsedTime = now - lastTime + if elapsedTime < self.InvitationFailedTimeout: + return False + self.lastInvitationFailedMessage[inviterId] = now + return True + + def showRejectMessage(self, elevatorId, reason, avatarsFailingRequirements, avatarsInBattle): + leaderId = localAvatar.doId + rejectText = '' + + def getAvatarText(avIdList): + avatarText = '' + nameList = [] + for avId in avIdList: + avatar = base.cr.doId2do.get(avId) + if avatar: + nameList.append(avatar.name) + + if len(nameList) > 0: + lastName = nameList.pop() + avatarText = lastName + if len(nameList) > 0: + secondLastName = nameList.pop() + for name in nameList: + avatarText = name + ', ' + + avatarText += secondLastName + ' ' + TTLocalizer.And + ' ' + lastName + return avatarText + + if reason == BoardingPartyBase.BOARDCODE_PROMOTION: + self.notify.debug("%s 's group cannot board because it does not have enough promotion merits." % leaderId) + if leaderId in avatarsFailingRequirements: + rejectText = TTLocalizer.BoardcodePromotionLeader + else: + avatarNameText = getAvatarText(avatarsFailingRequirements) + if len(avatarsFailingRequirements) == 1: + rejectText = TTLocalizer.BoardcodePromotionNonLeaderSingular % avatarNameText + else: + rejectText = TTLocalizer.BoardcodePromotionNonLeaderPlural % avatarNameText + elif reason == BoardingPartyBase.BOARDCODE_BATTLE: + self.notify.debug("%s 's group cannot board because it is in a battle" % leaderId) + if leaderId in avatarsInBattle: + rejectText = TTLocalizer.BoardcodeBattleLeader + else: + avatarNameText = getAvatarText(avatarsInBattle) + if len(avatarsInBattle) == 1: + rejectText = TTLocalizer.BoardcodeBattleNonLeaderSingular % avatarNameText + else: + rejectText = TTLocalizer.BoardcodeBattleNonLeaderPlural % avatarNameText + elif reason == BoardingPartyBase.BOARDCODE_SPACE: + self.notify.debug("%s 's group cannot board there was not enough room" % leaderId) + rejectText = TTLocalizer.BoardcodeSpace + elif reason == BoardingPartyBase.BOARDCODE_MISSING: + self.notify.debug("%s 's group cannot board because something was missing" % leaderId) + rejectText = TTLocalizer.BoardcodeMissing + base.localAvatar.elevatorNotifier.showMe(rejectText) + + def postGroupDissolve(self, quitterId, leaderId, memberList, kick): + self.notify.debug('%s group has dissolved' % leaderId) + isMyGroup = 0 + if localAvatar.doId == quitterId or localAvatar.doId == leaderId: + isMyGroup = 1 + if leaderId in self.groupListDict: + if leaderId == localAvatar.doId: + isMyGroup = 1 + if leaderId in self.avIdDict: + self.avIdDict.pop(leaderId) + dgroup = self.groupListDict.pop(leaderId) + for memberId in memberList: + if memberId == localAvatar.doId: + isMyGroup = 1 + if memberId in self.avIdDict: + self.avIdDict.pop(memberId) + + if isMyGroup: + self.notify.debug('new info posted on my group') + messenger.send('updateGroupStatus') + groupFormed = False + if self.groupPanel: + groupFormed = True + self.groupPanel.cleanup() + self.groupPanel = None + if groupFormed: + if leaderId == quitterId: + if not localAvatar.doId == leaderId: + localAvatar.setSystemMessage(leaderId, TTLocalizer.BoardingMessageGroupDissolved, WTToontownBoardingGroup) + elif not kick: + if not localAvatar.doId == quitterId: + quitter = base.cr.doId2do.get(quitterId) + if quitter: + quitterName = quitter.name + messageText = TTLocalizer.BoardingMessageLeftGroup % quitterName + localAvatar.setSystemMessage(quitterId, messageText, WTToontownBoardingGroup) + else: + messageText = TTLocalizer.BoardingMessageGroupDisbandedGeneric + localAvatar.setSystemMessage(quitterId, messageText, WTToontownBoardingGroup) + return + + def requestInvite(self, inviteeId): + self.notify.debug('requestInvite %s' % inviteeId) + elevator = base.cr.doId2do.get(self.getElevatorIdList()[0]) + if elevator: + if inviteeId in self.getGroupKickList(localAvatar.doId): + if not self.isGroupLeader(localAvatar.doId): + avatar = base.cr.doId2do.get(inviteeId) + if avatar: + avatarNameText = avatar.name + else: + avatarNameText = '' + rejectText = TTLocalizer.BoardingInviteeInKickOutList % avatarNameText + self.showMe(rejectText) + return + if self.inviterPanels.isInvitingPanelUp(): + self.showMe(TTLocalizer.BoardingPendingInvite, pos=(0, 0, 0)) + elif len(self.getGroupMemberList(localAvatar.doId)) >= self.maxSize: + self.showMe(TTLocalizer.BoardingInviteGroupFull) + else: + invitee = base.cr.doId2do.get(inviteeId) + if invitee: + self.inviterPanels.createInvitingPanel(self, inviteeId) + self.sendUpdate('requestInvite', [inviteeId]) + + def requestCancelInvite(self, inviteeId): + self.sendUpdate('requestCancelInvite', [inviteeId]) + + def requestAcceptInvite(self, leaderId, inviterId): + self.notify.debug('requestAcceptInvite %s %s' % (leaderId, inviterId)) + self.sendUpdate('requestAcceptInvite', [leaderId, inviterId]) + + def requestRejectInvite(self, leaderId, inviterId): + self.sendUpdate('requestRejectInvite', [leaderId, inviterId]) + + def requestKick(self, kickId): + self.sendUpdate('requestKick', [kickId]) + + def requestLeave(self): + if self.goToShowTrack and self.goToShowTrack.isPlaying(): + return + place = base.cr.playGame.getPlace() + if place: + if not place.getState() == 'elevator': + if localAvatar.doId in self.avIdDict: + leaderId = self.avIdDict[localAvatar.doId] + self.sendUpdate('requestLeave', [leaderId]) + + def handleEnterElevator(self, elevator): + if self.getGroupLeader(localAvatar.doId) == localAvatar.doId: + if base.localAvatar.hp > 0: + self.cr.playGame.getPlace().detectedElevatorCollision(elevator) + self.sendUpdate('requestBoard', [elevator.doId]) + elevatorId = elevator.doId + if elevatorId in self.elevatorIdList: + offset = self.elevatorIdList.index(elevatorId) + if self.groupPanel: + self.groupPanel.scrollToDestination(offset) + self.informDestChange(offset) + self.disableGoButton() + + def informDestChange(self, offset): + self.sendUpdate('informDestinationInfo', [offset]) + + def postDestinationInfo(self, offset): + if self.groupPanel: + self.groupPanel.changeDestination(offset) + + def enableGoButton(self): + if self.groupPanel: + self.groupPanel.enableGoButton() + self.groupPanel.enableDestinationScrolledList() + + def disableGoButton(self): + if self.groupPanel: + self.groupPanel.disableGoButton() + self.groupPanel.disableDestinationScrolledList() + + def isInviteePanelUp(self): + if self.groupInviteePanel: + if not self.groupInviteePanel.isEmpty(): + return True + self.groupInviteePanel = None + return False + + def requestGoToFirstTime(self, elevatorId): + self.waitingForFirstResponse = True + self.firstRequestAccepted = False + self.sendUpdate('requestGoToFirstTime', [elevatorId]) + self.startGoToPreShow(elevatorId) + + def acceptGoToFirstTime(self, elevatorId): + self.waitingForFirstResponse = False + self.firstRequestAccepted = True + + def requestGoToSecondTime(self, elevatorId): + if not self.waitingForFirstResponse: + if self.firstRequestAccepted: + self.firstRequestAccepted = False + self.disableGoButton() + self.sendUpdate('requestGoToSecondTime', [elevatorId]) + else: + self.postRejectGoto(elevatorId, BoardingPartyBase.BOARDCODE_MISSING, [], []) + self.cancelGoToElvatorDest() + + def acceptGoToSecondTime(self, elevatorId): + self.startGoToShow(elevatorId) + + def rejectGoToRequest(self, elevatorId, reason, avatarsFailingRequirements, avatarsInBattle): + self.firstRequestAccepted = False + self.waitingForFirstResponse = False + self.cancelGoToElvatorDest() + self.postRejectGoto(elevatorId, reason, avatarsFailingRequirements, avatarsInBattle) + + def startGoToPreShow(self, elevatorId): + self.notify.debug('Starting Go Pre Show.') + place = base.cr.playGame.getPlace() + if place: + place.setState('stopped') + goButtonPreShow = BoardingGroupShow.BoardingGroupShow(localAvatar) + goButtonPreShowTrack = goButtonPreShow.getGoButtonPreShow() + if self.groupPanel: + self.groupPanel.changeGoToCancel() + self.groupPanel.disableQuitButton() + self.groupPanel.disableDestinationScrolledList() + self.finishGoToPreShowTrack() + self.goToPreShowTrack = Sequence() + self.goToPreShowTrack.append(goButtonPreShowTrack) + self.goToPreShowTrack.append(Func(self.requestGoToSecondTime, elevatorId)) + self.goToPreShowTrack.start() + + def finishGoToPreShowTrack(self): + if self.goToPreShowTrack: + self.goToPreShowTrack.finish() + self.goToPreShowTrack = None + return + + def startGoToShow(self, elevatorId): + self.notify.debug('Starting Go Show.') + localAvatar.boardingParty.forceCleanupInviterPanels() + elevatorName = self.__getDestName(elevatorId) + if self.groupPanel: + self.groupPanel.disableQuitButton() + goButtonShow = BoardingGroupShow.BoardingGroupShow(localAvatar) + place = base.cr.playGame.getPlace() + if place: + place.setState('stopped') + self.goToShowTrack = goButtonShow.getGoButtonShow(elevatorName) + self.goToShowTrack.start() + + def finishGoToShowTrack(self): + if self.goToShowTrack: + self.goToShowTrack.finish() + self.goToShowTrack = None + return + + def cancelGoToElvatorDest(self): + self.notify.debug('%s cancelled the GoTo Button.' % localAvatar.doId) + self.firstRequestAccepted = False + self.waitingForFirstResponse = False + self.finishGoToPreShowTrack() + place = base.cr.playGame.getPlace() + if place: + place.setState('walk') + if self.groupPanel: + self.groupPanel.changeCancelToGo() + self.groupPanel.enableGoButton() + self.groupPanel.enableQuitButton() + self.groupPanel.enableDestinationScrolledList() + + def __getDestName(self, elevatorId): + elevator = base.cr.doId2do.get(elevatorId) + destName = '' + if elevator: + destName = elevator.getDestName() + return destName + + def showMe(self, message, pos = None): + base.localAvatar.elevatorNotifier.showMeWithoutStopping(message, pos) + + def forceCleanupInviteePanel(self): + if self.isInviteePanelUp(): + self.groupInviteePanel.forceCleanup() + self.groupInviteePanel = None + return + + def forceCleanupInviterPanels(self): + if self.inviterPanels: + self.inviterPanels.forceCleanup() diff --git a/toontown/building/DistributedBoardingPartyAI.py b/toontown/building/DistributedBoardingPartyAI.py new file mode 100755 index 00000000..8110179d --- /dev/null +++ b/toontown/building/DistributedBoardingPartyAI.py @@ -0,0 +1,587 @@ +from otp.otpbase import OTPGlobals +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from ElevatorConstants import * +from direct.distributed import DistributedObjectAI +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +from direct.directnotify import DirectNotifyGlobal +from toontown.building import BoardingPartyBase +GROUPMEMBER = 0 +GROUPINVITE = 1 + +class DistributedBoardingPartyAI(DistributedObjectAI.DistributedObjectAI, BoardingPartyBase.BoardingPartyBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBoardingPartyAI') + + def __init__(self, air, elevatorList, maxSize = 4): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + BoardingPartyBase.BoardingPartyBase.__init__(self) + self.setGroupSize(maxSize) + self.elevatorIdList = elevatorList + self.visibleZones = [] + + def delete(self): + self.cleanup() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + for elevatorId in self.elevatorIdList: + elevator = simbase.air.doId2do.get(elevatorId) + elevator.setBoardingParty(self) + + ''' + store = simbase.air.dnaStoreMap.get(self.zoneId) + if store: + numVisGroups = store.getNumDNAVisGroupsAI() + myVisGroup = None + for index in xrange(numVisGroups): + if store.getDNAVisGroupAI(index).getName() == str(self.zoneId): + myVisGroup = store.getDNAVisGroupAI(index) + + if myVisGroup: + numVisibles = myVisGroup.getNumVisibles() + for index in xrange(numVisibles): + newVisible = myVisGroup.getVisibleName(index) + self.visibleZones.append(int(newVisible)) + + else: + self.visibleZones = [self.zoneId] + else: + self.visibleZones = [self.zoneId] + ''' + return + + def cleanup(self): + BoardingPartyBase.BoardingPartyBase.cleanup(self) + del self.elevatorIdList + del self.visibleZones + + def getElevatorIdList(self): + return self.elevatorIdList + + def setElevatorIdList(self, elevatorIdList): + self.elevatorIdList = elevatorIdList + + def addWacthAvStatus(self, avId): + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleAvatarDisco, extraArgs=[avId]) + self.accept(self.staticGetLogicalZoneChangeEvent(avId), self.handleAvatarZoneChange, extraArgs=[avId]) + messageToonAdded = 'Battle adding toon %s' % avId + self.accept(messageToonAdded, self.handleToonJoinedBattle) + messageToonReleased = 'Battle releasing toon %s' % avId + self.accept(messageToonReleased, self.handleToonLeftBattle) + + def handleToonJoinedBattle(self, avId): + self.notify.debug('handleToonJoinedBattle %s' % avId) + + def handleToonLeftBattle(self, avId): + self.notify.debug('handleToonLeftBattle %s' % avId) + + def removeWacthAvStatus(self, avId): + self.ignore(self.air.getAvatarExitEvent(avId)) + self.ignore(self.staticGetLogicalZoneChangeEvent(avId)) + + # BoardingParty data model + # + # groupList[0] - people in the group + # groupList[1] - people invited to the group + # groupList[2] - people kicked from the group + # + # avIdDict - lookup from player to the leader of the group they are in + # if you are in a group or have been invited to a group, you + # are in this dictionary with a pointer to the leader of the + # group. The only exception to this is if you were invited + # to merge groups. + # mergeDict - This is a link that points back to the original + # invitee before we mapped it to the leader of the other group. + + def requestInvite(self, inviteeId): + self.notify.debug('requestInvite %s' % inviteeId) + inviterId = self.air.getAvatarIdFromSender() + invitee = simbase.air.doId2do.get(inviteeId) + originalInviteeId = inviteeId + merger = False + if invitee and invitee.battleId != 0: + reason = BoardingPartyBase.BOARDCODE_BATTLE + self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason]) + self.sendUpdateToAvatarId(inviteeId, 'postMessageInvitationFailed', [inviterId]) + return + if self.hasPendingInvite(inviteeId): + reason = BoardingPartyBase.BOARDCODE_PENDING_INVITE + self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason]) + self.sendUpdateToAvatarId(inviteeId, 'postMessageInvitationFailed', [inviterId]) + return + if self.__isInElevator(inviteeId): + reason = BoardingPartyBase.BOARDCODE_IN_ELEVATOR + self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason]) + self.sendUpdateToAvatarId(inviteeId, 'postMessageInvitationFailed', [inviterId]) + return + if self.hasActiveGroup(inviteeId): + # We could make the assumption both are in the avIdDict but I'd prefer not to blow up the district + if self.hasActiveGroup(inviterId): + inviteeLeaderId = self.avIdDict[inviteeId] + leaderId = self.avIdDict[inviterId] + + # group merge already requested? + if self.hasPendingInvite(inviteeLeaderId): + reason = BoardingPartyBase.BOARDCODE_PENDING_INVITE + self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason]) + self.sendUpdateToAvatarId(inviteeId, 'postMessageInvitationFailed', [inviterId]) + return + + if ((len(self.getGroupMemberList(leaderId)) + len(self.getGroupMemberList(inviteeLeaderId))) <= self.maxSize): + # Lets send the invitation to the person in authority... + invitee = simbase.air.doId2do.get(inviteeLeaderId) + inviteeId = inviteeLeaderId + merger = True + else: + reason = BoardingPartyBase.BOARDCODE_GROUPS_TOO_LARGE + self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason]) + self.sendUpdateToAvatarId(inviteeId, 'postMessageInvitationFailed', [inviterId]) + return + else: + reason = BoardingPartyBase.BOARDCODE_DIFF_GROUP + self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason]) + self.sendUpdateToAvatarId(inviteeId, 'postMessageInvitationFailed', [inviterId]) + return + # Lets see what the invitee is currently doing + inviteeOkay = self.checkBoard(inviteeId, self.elevatorIdList[0]) + reason = 0 + # I know there is an unexpected issue here when we are merging groups... lets think about this really hard.. + if len(self.elevatorIdList) == 1: + if inviteeOkay: + if inviteeOkay == REJECT_PROMOTION: + reason = BoardingPartyBase.BOARDCODE_PROMOTION + self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason]) + return + else: + inviterOkay = self.checkBoard(inviterId, self.elevatorIdList[0]) + if inviterOkay: + if inviterOkay == REJECT_PROMOTION: + reason = BoardingPartyBase.BOARDCODE_PROMOTION + self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviterId, reason]) + return + # Is the inviter already in the avIdDict? It follows they either must be in a group or have a pending invite... + if inviterId in self.avIdDict: + self.notify.debug('old group') + # Everything is indexed by the leaders + leaderId = self.avIdDict[inviterId] + groupList = self.groupListDict.get(leaderId) + if groupList: # One would hope we have a group... + self.notify.debug('got group list') + # Only the leader of a group can invite somebody who was kicked out back in + if inviterId == leaderId: + # The invitee was kicked out so lets let them back in again + if inviteeId in groupList[2]: + groupList[2].remove(inviteeId) + # Is the group already oversized? + if len(self.getGroupMemberList(leaderId)) >= self.maxSize: + self.sendUpdate('postSizeReject', [leaderId, inviterId, inviteeId]) + elif merger: + # We cannot muck with the avIdDict because they are pointing to their original groups. + # We shall stash away the info into a different + # dictionary.. This way, if something goes wrong, + # the original groups with their original data + # structures are untouched. The mergeDict gives + # us a pointer back to where the original invite + # went to so that we can issue a notice to close + # the appropriate invite dialog + self.mergeDict[inviteeId] = originalInviteeId + self.sendUpdateToAvatarId(inviteeId, 'postInvite', [leaderId, inviterId, True]) + # notify everybody in the inviters group of the + # invitation.. + for memberId in groupList[0]: + if not memberId == inviterId: + self.sendUpdateToAvatarId(memberId, 'postMessageInvited', [inviteeId, inviterId]) + elif inviterId not in groupList[1] and inviterId not in groupList[2]: + # If the invitee isn't already in the group, add them.. + if inviteeId not in groupList[1]: + groupList[1].append(inviteeId) + self.groupListDict[leaderId] = groupList + if inviteeId in self.avIdDict: + self.notify.warning('inviter %s tried to invite %s who already exists in the avIdDict.' % (inviterId, inviteeId)) + self.air.writeServerEvent('suspicious: inviter', inviterId, ' tried to invite %s who already exists in the avIdDict.' % inviteeId) + self.avIdDict[inviteeId] = leaderId + self.sendUpdateToAvatarId(inviteeId, 'postInvite', [leaderId, inviterId, False]) + # notify everybody of the invitation.. + for memberId in groupList[0]: + if not memberId == inviterId: + self.sendUpdateToAvatarId(memberId, 'postMessageInvited', [inviteeId, inviterId]) + # The inviter was kicked.. so, we cannot let them back in since they are not the leader... + elif inviterId in groupList[2]: + self.sendUpdate('postKickReject', [leaderId, inviterId, inviteeId]) + else: + if inviteeId in self.avIdDict: + self.notify.warning('inviter %s tried to invite %s who already exists in avIdDict.' % (inviterId, inviteeId)) + self.air.writeServerEvent('suspicious: inviter', inviterId, ' tried to invite %s who already exists in the avIdDict.' % inviteeId) + self.notify.debug('new group') + # The inviter is now the leader of the new group + leaderId = inviterId + self.avIdDict[inviterId] = inviterId + self.avIdDict[inviteeId] = inviterId + self.groupListDict[leaderId] = [[leaderId], [inviteeId], []] + self.addWacthAvStatus(leaderId) + self.sendUpdateToAvatarId(inviteeId, 'postInvite', [leaderId, inviterId, False]) + + def requestCancelInvite(self, inviteeId): + inviterId = self.air.getAvatarIdFromSender() + if inviteeId in self.mergeDict: + inviteeId = self.mergeDict.pop(inviteeId) + self.sendUpdateToAvatarId(inviteeId, 'postInviteCanceled', []) + return + if inviterId in self.avIdDict: + leaderId = self.avIdDict[inviterId] + groupList = self.groupListDict.get(leaderId) + if groupList: + self.removeFromGroup(leaderId, inviteeId) + self.sendUpdateToAvatarId(inviteeId, 'postInviteCanceled', []) + + def requestAcceptInvite(self, leaderId, inviterId): + inviteeId = self.air.getAvatarIdFromSender() + self.notify.debug('requestAcceptInvite leader%s inviter%s invitee%s' % (leaderId, inviterId, inviteeId)) + if inviteeId in self.avIdDict: + if inviteeId in self.mergeDict: + # Clean things up in case we back this operation out + oldId = self.mergeDict.pop(inviteeId) + # Check the state of things to deal with odd race conditions + # both should still be in the avIdDict + if leaderId not in self.avIdDict or inviteeId not in self.avIdDict: + self.notify.warning('leaderId not in self.avIdDict or inviteeId not in self.avIdDict'); + self.sendUpdateToAvatarId(inviteeId, 'postSomethingMissing', []) + return + # Does the leader still have a group? + if leaderId not in self.groupListDict: + self.notify.warning('the leader does not have a group?'); + self.sendUpdateToAvatarId(inviteeId, 'postSomethingMissing', []) + return + # They should STILL be the leaders.. right? + if leaderId != self.avIdDict[leaderId]: + self.notify.warning('leaderId != self.avIdDict[leaderId]'); + self.sendUpdateToAvatarId(inviteeId, 'postSomethingMissing', []) + return + # They should STILL be the leaders.. right? + if inviteeId != self.avIdDict[inviteeId]: + self.notify.warning('inviteeId != self.avIdDict[inviteeId]'); + self.sendUpdateToAvatarId(inviteeId, 'postSomethingMissing', []) + return + # both should still have active groups + if not self.hasActiveGroup(inviteeId) or not self.hasActiveGroup(leaderId): + self.notify.warning('not self.hasActiveGroup(inviteeId) or not self.hasActiveGroup(leaderId)'); + self.sendUpdateToAvatarId(inviteeId, 'postSomethingMissing', []) + return + # Lets make sure we still CAN merge them in + if ((len(self.getGroupMemberList(leaderId)) + len(self.getGroupMemberList(inviteeId))) > self.maxSize): + reason = BoardingPartyBase.BOARDCODE_GROUPS_TOO_LARGE + self.sendUpdateToAvatarId(inviterId, 'postInviteNotQualify', [inviteeId, reason]) + self.sendUpdateToAvatarId(inviteeId, 'postMessageInvitationFailed', [inviterId]) + return + group = self.groupListDict.get(leaderId) + # Something is wonky + if group == None or (len(group) < 3): + self.notify.warning('the leader has a group but it is null or too short') + self.sendUpdateToAvatarId(inviteeId, 'postSomethingMissing', []) + return + # get the memberList of the invitee and add it into the leaders group + memberList = self.getGroupMemberList(inviteeId) + for memberId in memberList: + self.addToGroup(leaderId, memberId, 0) + # get rid of the old group (invitee is always the old leader) + self.groupListDict.pop(inviteeId) + # notify everybody of their new group info + self.sendUpdateToAvatarId(inviterId, 'postInviteAccepted', [oldId]) + self.sendUpdate('postGroupInfo', [leaderId, group[0], group[1], group[2]]) + return + if self.hasActiveGroup(inviteeId): + self.sendUpdateToAvatarId(inviteeId, 'postAlreadyInGroup', []) + return + if leaderId not in self.avIdDict or not self.isInGroup(inviteeId, leaderId): + self.sendUpdateToAvatarId(inviteeId, 'postSomethingMissing', []) + return + memberList = self.getGroupMemberList(leaderId) + if self.avIdDict[inviteeId]: + if self.avIdDict[inviteeId] == leaderId: + if inviteeId in memberList: + self.notify.debug('invitee already in group, aborting requestAcceptInvite') + return + else: + self.air.writeServerEvent('suspicious: ', inviteeId, " accepted a second invite from %s, in %s's group, while he was in alredy in %s's group." % (inviterId, leaderId, self.avIdDict[inviteeId])) + self.removeFromGroup(self.avIdDict[inviteeId], inviteeId, post=0) + if len(memberList) >= self.maxSize: + self.removeFromGroup(leaderId, inviteeId) + self.sendUpdateToAvatarId(inviterId, 'postMessageAcceptanceFailed', [inviteeId, BoardingPartyBase.INVITE_ACCEPT_FAIL_GROUP_FULL]) + self.sendUpdateToAvatarId(inviteeId, 'postGroupAlreadyFull', []) + return + self.sendUpdateToAvatarId(inviterId, 'postInviteAccepted', [inviteeId]) + self.addToGroup(leaderId, inviteeId) + else: + self.air.writeServerEvent('suspicious: ', inviteeId, " was invited to %s's group by %s, but the invitee didn't have an entry in the avIdDict." % (leaderId, inviterId)) + + def requestRejectInvite(self, leaderId, inviterId): + inviteeId = self.air.getAvatarIdFromSender() + if inviteeId in self.mergeDict: + inviteeId = self.mergeDict.pop(inviteeId) + else: + self.removeFromGroup(leaderId, inviteeId) + self.sendUpdateToAvatarId(inviterId, 'postInviteDelcined', [inviteeId]) + + def requestKick(self, kickId): + leaderId = self.air.getAvatarIdFromSender() + if kickId in self.avIdDict: + if self.avIdDict[kickId] == leaderId: + self.removeFromGroup(leaderId, kickId, kick=1) + self.sendUpdateToAvatarId(kickId, 'postKick', [leaderId]) + + def requestLeave(self, leaderId): + memberId = self.air.getAvatarIdFromSender() + if memberId in self.avIdDict: + if leaderId == self.avIdDict[memberId]: + self.removeFromGroup(leaderId, memberId) + + def checkBoard(self, avId, elevatorId): + elevator = simbase.air.doId2do.get(elevatorId) + avatar = simbase.air.doId2do.get(avId) + if avatar and elevator: + return elevator.checkBoard(avatar) + return REJECT_BOARDINGPARTY + + def testBoard(self, leaderId, elevatorId, needSpace = 0): + elevator = None + boardOkay = BoardingPartyBase.BOARDCODE_MISSING + avatarsFailingRequirements = [] + avatarsInBattle = [] + if elevatorId in self.elevatorIdList: + elevator = simbase.air.doId2do.get(elevatorId) + if elevator: + if leaderId in self.avIdDict: + if leaderId == self.avIdDict[leaderId]: + boardOkay = BoardingPartyBase.BOARDCODE_OKAY + for avId in self.getGroupMemberList(leaderId): + avatar = simbase.air.doId2do.get(avId) + if avatar: + if elevator.checkBoard(avatar) != 0: + if elevator.checkBoard(avatar) == REJECT_PROMOTION: + boardOkay = BoardingPartyBase.BOARDCODE_PROMOTION + avatarsFailingRequirements.append(avId) + elif avatar.battleId != 0: + boardOkay = BoardingPartyBase.BOARDCODE_BATTLE + avatarsInBattle.append(avId) + + groupSize = len(self.getGroupMemberList(leaderId)) + if groupSize > self.maxSize: + boardOkay = BoardingPartyBase.BOARDCODE_SPACE + if needSpace: + if groupSize > elevator.countOpenSeats(): + boardOkay = BoardingPartyBase.BOARDCODE_SPACE + if boardOkay != BoardingPartyBase.BOARDCODE_OKAY: + self.notify.debug('Something is wrong with the group board request') + if boardOkay == BoardingPartyBase.BOARDCODE_PROMOTION: + self.notify.debug('An avatar did not meet the elevator promotion requirements') + elif boardOkay == BoardingPartyBase.BOARDCODE_BATTLE: + self.notify.debug('An avatar is in battle') + return (boardOkay, avatarsFailingRequirements, avatarsInBattle) + + def requestBoard(self, elevatorId): + wantDisableGoButton = False + leaderId = self.air.getAvatarIdFromSender() + elevator = None + if elevatorId in self.elevatorIdList: + elevator = simbase.air.doId2do.get(elevatorId) + if elevator: + if leaderId in self.avIdDict: + if leaderId == self.avIdDict[leaderId]: + group = self.groupListDict.get(leaderId) + if group: + boardOkay, avatarsFailingRequirements, avatarsInBattle = self.testBoard(leaderId, elevatorId, needSpace=1) + if boardOkay == BoardingPartyBase.BOARDCODE_OKAY: + leader = simbase.air.doId2do.get(leaderId) + if leader: + elevator.partyAvatarBoard(leader) + wantDisableGoButton = True + for avId in group[0]: + if not avId == leaderId: + avatar = simbase.air.doId2do.get(avId) + if avatar: + elevator.partyAvatarBoard(avatar, wantBoardingShow=1) + + self.air.writeServerEvent('boarding_elevator', self.zoneId, '%s; Sending avatars %s' % (elevatorId, group[0])) + else: + self.sendUpdateToAvatarId(leaderId, 'postRejectBoard', [elevatorId, + boardOkay, + avatarsFailingRequirements, + avatarsInBattle]) + return + if not wantDisableGoButton: + self.sendUpdateToAvatarId(leaderId, 'postRejectBoard', [elevatorId, + BoardingPartyBase.BOARDCODE_MISSING, [], []]) + return + + def testGoButtonRequirements(self, leaderId, elevatorId): + if leaderId in self.avIdDict: + if leaderId == self.avIdDict[leaderId]: + if elevatorId in self.elevatorIdList: + elevator = simbase.air.doId2do.get(elevatorId) + if elevator: + boardOkay, avatarsFailingRequirements, avatarsInBattle = self.testBoard(leaderId, elevatorId, needSpace=0) + if boardOkay == BoardingPartyBase.BOARDCODE_OKAY: + avList = self.getGroupMemberList(leaderId) + if 0 in avList: + avList.remove(0) + if leaderId not in elevator.seats: + return True + else: + self.notify.warning('avId: %s has hacked his/her client.' % leaderId) + self.air.writeServerEvent('suspicious: ', leaderId, ' pressed the GO Button while inside the elevator.') + else: + self.sendUpdateToAvatarId(leaderId, 'rejectGoToRequest', [elevatorId, + boardOkay, + avatarsFailingRequirements, + avatarsInBattle]) + return False + + def requestGoToFirstTime(self, elevatorId): + callerId = self.air.getAvatarIdFromSender() + if self.testGoButtonRequirements(callerId, elevatorId): + self.sendUpdateToAvatarId(callerId, 'acceptGoToFirstTime', [elevatorId]) + + def requestGoToSecondTime(self, elevatorId): + callerId = self.air.getAvatarIdFromSender() + avList = self.getGroupMemberList(callerId) + if self.testGoButtonRequirements(callerId, elevatorId): + for avId in avList: + self.sendUpdateToAvatarId(avId, 'acceptGoToSecondTime', [elevatorId]) + + THREE_SECONDS = 3.0 + taskMgr.doMethodLater(THREE_SECONDS, self.sendAvatarsToDestinationTask, self.uniqueName('sendAvatarsToDestinationTask'), extraArgs=[elevatorId, avList], appendTask=True) + + def sendAvatarsToDestinationTask(self, elevatorId, avList, task): + self.notify.debug('entering sendAvatarsToDestinationTask') + if len(avList): + if elevatorId in self.elevatorIdList: + elevator = simbase.air.doId2do.get(elevatorId) + if elevator: + self.notify.warning('Sending avatars %s' % avList) + boardOkay, avatarsFailingRequirements, avatarsInBattle = self.testBoard(avList[0], elevatorId, needSpace=0) + if not boardOkay == BoardingPartyBase.BOARDCODE_OKAY: + for avId in avatarsFailingRequirements: + self.air.writeServerEvent('suspicious: ', avId, ' failed requirements after the second go button request.') + + for avId in avatarsInBattle: + self.air.writeServerEvent('suspicious: ', avId, ' joined battle after the second go button request.') + + self.air.writeServerEvent('boarding_go', self.zoneId, '%s; Sending avatars %s' % (elevatorId, avList)) + elevator.sendAvatarsToDestination(avList) + return Task.done + + def handleAvatarDisco(self, avId): + self.notify.debug('handleAvatarDisco %s' % avId) + if avId in self.mergeDict: + self.mergeDict.pop(avId) + if avId in self.avIdDict: + leaderId = self.avIdDict[avId] + self.removeFromGroup(leaderId, avId) + + def handleAvatarZoneChange(self, avId, zoneNew, zoneOld): + self.notify.debug('handleAvatarZoneChange %s new%s old%s bp%s' % (avId, zoneNew, zoneOld, self.zoneId)) + if zoneNew in self.visibleZones: + self.toonInZone(avId) + elif avId in self.avIdDict: + leaderId = self.avIdDict[avId] + self.removeFromGroup(leaderId, avId) + if avId in self.mergeDict: + self.mergeDict.pop(avId) + + def toonInZone(self, avId): + if avId in self.avIdDict: + leaderId = self.avIdDict[avId] + group = self.groupListDict.get(leaderId) + if leaderId and group: + self.notify.debug('Calling postGroupInfo from toonInZone') + + def addToGroup(self, leaderId, inviteeId, post = 1): + group = self.groupListDict.get(leaderId) + if group: + self.avIdDict[inviteeId] = leaderId + if inviteeId in group[1]: + group[1].remove(inviteeId) + if inviteeId not in group[0]: + group[0].append(inviteeId) + self.groupListDict[leaderId] = group + if post: + self.notify.debug('Calling postGroupInfo from addToGroup') + self.sendUpdate('postGroupInfo', [leaderId, group[0], group[1], group[2]]) + self.addWacthAvStatus(inviteeId) + else: + self.sendUpdate('postGroupDissolve', [leaderId, leaderId, [], 0]) + + def removeFromGroup(self, leaderId, memberId, kick = 0, post = 1): + self.notify.debug('') + self.notify.debug('removeFromGroup leaderId %s memberId %s' % (leaderId, memberId)) + self.notify.debug('Groups %s' % self.groupListDict) + self.notify.debug('avDict %s' % self.avIdDict) + if leaderId not in self.avIdDict: + self.sendUpdate('postGroupDissolve', [memberId, leaderId, [], kick]) + if memberId in self.avIdDict: + self.avIdDict.pop(memberId) + return + self.removeWacthAvStatus(memberId) + group = self.groupListDict.get(leaderId) + if group: + if memberId in group[0]: + group[0].remove(memberId) + if memberId in group[1]: + group[1].remove(memberId) + if memberId in group[2]: + group[2].remove(memberId) + if kick: + group[2].append(memberId) + else: + return + if memberId == leaderId or len(group[0]) < 2: + if leaderId in self.avIdDict: + self.avIdDict.pop(leaderId) + for inviteeId in group[1]: + if inviteeId in self.avIdDict: + self.avIdDict.pop(inviteeId) + self.sendUpdateToAvatarId(inviteeId, 'postInviteCanceled', []) + + dgroup = self.groupListDict.pop(leaderId) + for dMemberId in dgroup[0]: + if dMemberId in self.avIdDict: + self.avIdDict.pop(dMemberId) + + self.notify.debug('postGroupDissolve') + dgroup[0].insert(0, memberId) + self.sendUpdate('postGroupDissolve', [memberId, leaderId, dgroup[0], kick]) + else: + self.groupListDict[leaderId] = group + if post: + self.notify.debug('Calling postGroupInfo from removeFromGroup') + self.sendUpdate('postGroupInfo', [leaderId, group[0], group[1], group[2]]) + if memberId in self.avIdDict: + self.avIdDict.pop(memberId) + self.notify.debug('Remove from group END') + self.notify.debug('Groups %s' % self.groupListDict) + self.notify.debug('avDict %s' % self.avIdDict) + self.notify.debug('') + + def informDestinationInfo(self, offset): + leaderId = self.air.getAvatarIdFromSender() + if offset > len(self.elevatorIdList): + self.air.writeServerEvent('suspicious: ', leaderId, 'has requested to go to %s elevator which does not exist' % offset) + return + memberList = self.getGroupMemberList(leaderId) + for avId in memberList: + if avId != leaderId: + self.sendUpdateToAvatarId(avId, 'postDestinationInfo', [offset]) + + def __isInElevator(self, avId): + inElevator = False + for elevatorId in self.elevatorIdList: + elevator = simbase.air.doId2do.get(elevatorId) + if elevator: + if avId in elevator.seats: + inElevator = True + + return inElevator diff --git a/toontown/building/DistributedBossElevator.py b/toontown/building/DistributedBossElevator.py new file mode 100755 index 00000000..ec07fe34 --- /dev/null +++ b/toontown/building/DistributedBossElevator.py @@ -0,0 +1,91 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from ElevatorConstants import * +from ElevatorUtils import * +import DistributedElevator +import DistributedElevatorExt +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog + +class DistributedBossElevator(DistributedElevatorExt.DistributedElevatorExt): + + def __init__(self, cr): + DistributedElevatorExt.DistributedElevatorExt.__init__(self, cr) + self.elevatorPoints = BigElevatorPoints + self.openSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.ogg') + self.finalOpenSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.ogg') + self.closeSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.ogg') + self.finalCloseSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.ogg') + self.type = ELEVATOR_VP + self.countdownTime = ElevatorData[self.type]['countdown'] + + def disable(self): + DistributedElevator.DistributedElevator.disable(self) + + def generate(self): + DistributedElevatorExt.DistributedElevatorExt.generate(self) + + def delete(self): + self.elevatorModel.removeNode() + del self.elevatorModel + DistributedElevatorExt.DistributedElevatorExt.delete(self) + + def setupElevator(self): + self.elevatorModel = loader.loadModel('phase_9/models/cogHQ/cogHQ_elevator') + icon = self.elevatorModel.find('**/big_frame/') + icon.hide() + self.leftDoor = self.elevatorModel.find('**/left-door') + self.rightDoor = self.elevatorModel.find('**/right-door') + geom = base.cr.playGame.hood.loader.geom + locator = geom.find('**/elevator_locator') + self.elevatorModel.reparentTo(locator) + self.elevatorModel.setH(180) + DistributedElevator.DistributedElevator.setupElevator(self) + + def getElevatorModel(self): + return self.elevatorModel + + def gotBldg(self, buildingList): + return DistributedElevator.DistributedElevator.gotBldg(self, buildingList) + + def getZoneId(self): + return 0 + + def __doorsClosed(self, zoneId): + pass + + def setBossOfficeZone(self, zoneId): + if self.localToonOnBoard: + hoodId = self.cr.playGame.hood.hoodId + doneStatus = {'loader': 'cogHQLoader', + 'where': 'cogHQBossBattle', + 'how': 'movie', + 'zoneId': zoneId, + 'hoodId': hoodId} + self.cr.playGame.getPlace().elevator.signalDone(doneStatus) + + def setBossOfficeZoneForce(self, zoneId): + place = self.cr.playGame.getPlace() + if place: + place.fsm.request('elevator', [self]) + hoodId = self.cr.playGame.hood.hoodId + doneStatus = {'loader': 'cogHQLoader', + 'where': 'cogHQBossBattle', + 'how': 'movie', + 'zoneId': zoneId, + 'hoodId': hoodId} + if hasattr(place, 'elevator') and place.elevator: + place.elevator.signalDone(doneStatus) + else: + self.notify.warning("setMintInteriorZoneForce: Couldn't find playGame.getPlace().elevator, zoneId: %s" % zoneId) + else: + self.notify.warning("setBossOfficeZoneForce: Couldn't find playGame.getPlace(), zoneId: %s" % zoneId) + + def getDestName(self): + return TTLocalizer.ElevatorSellBotBoss diff --git a/toontown/building/DistributedBossElevatorAI.py b/toontown/building/DistributedBossElevatorAI.py new file mode 100755 index 00000000..bdb43f91 --- /dev/null +++ b/toontown/building/DistributedBossElevatorAI.py @@ -0,0 +1,72 @@ +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from ElevatorConstants import * +import DistributedElevatorAI +import DistributedElevatorExtAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task +from direct.directnotify import DirectNotifyGlobal +from toontown.suit import DistributedSellbotBossAI + +class DistributedBossElevatorAI(DistributedElevatorExtAI.DistributedElevatorExtAI): + + def __init__(self, air, bldg, zone): + DistributedElevatorExtAI.DistributedElevatorExtAI.__init__(self, air, bldg, numSeats=8) + self.zone = zone + self.type = ELEVATOR_VP + self.countdownTime = ElevatorData[self.type]['countdown'] + + def elevatorClosed(self): + numPlayers = self.countFullSeats() + if numPlayers > 0: + bossZone = self.bldg.createBossOffice(self.seats) + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.sendUpdateToAvatarId(avId, 'setBossOfficeZone', [bossZone]) + self.clearFullNow(seatIndex) + + else: + self.notify.warning('The elevator left, but was empty.') + self.fsm.request('closed') + + def sendAvatarsToDestination(self, avIdList): + if len(avIdList) > 0: + bossZone = self.bldg.createBossOffice(avIdList) + for avId in avIdList: + if avId: + self.sendUpdateToAvatarId(avId, 'setBossOfficeZoneForce', [bossZone]) + + def enterClosing(self): + DistributedElevatorAI.DistributedElevatorAI.enterClosing(self) + taskMgr.doMethodLater(ElevatorData[self.type]['closeTime'], self.elevatorClosedTask, self.uniqueName('closing-timer')) + + def enterClosed(self): + DistributedElevatorExtAI.DistributedElevatorExtAI.enterClosed(self) + self.fsm.request('opening') + + def enterOpening(self): + DistributedElevatorAI.DistributedElevatorAI.enterOpening(self) + taskMgr.doMethodLater(ElevatorData[self.type]['openTime'], self.waitEmptyTask, self.uniqueName('opening-timer')) + + def checkBoard(self, av): + dept = ToontownGlobals.cogHQZoneId2deptIndex(self.zone) + if not av.readyForPromotion(dept): + return REJECT_PROMOTION + return 0 + + def requestBoard(self, *args): + self.notify.debug('requestBoard') + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av: + boardResponse = self.checkBoard(av) + newArgs = (avId,) + args + (boardResponse,) + if boardResponse == 0: + self.acceptingBoardersHandler(*newArgs) + else: + self.rejectingBoardersHandler(*newArgs) + else: + self.notify.warning('avid: %s does not exist, but tried to board an elevator' % avId) diff --git a/toontown/building/DistributedBuilding.py b/toontown/building/DistributedBuilding.py new file mode 100755 index 00000000..8793aeb6 --- /dev/null +++ b/toontown/building/DistributedBuilding.py @@ -0,0 +1,940 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.directtools.DirectGeometry import * +from ElevatorConstants import * +from ElevatorUtils import * +from SuitBuildingGlobals import * +from direct.gui.DirectGui import * +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.distributed import DistributedObject +import random +from toontown.suit import SuitDNA +from toontown.toonbase import TTLocalizer +from toontown.distributed import DelayDelete +from toontown.toon import TTEmote +from otp.avatar import Emote +import sys +FO_DICT = {'s': 'tt_m_ara_cbe_fieldOfficeMoverShaker', + 'l': 'tt_m_ara_cbe_fieldOfficeLegalEagle', + 'm': 'tt_m_ara_cbe_fieldOfficeMoverShaker', + 'c': 'tt_m_ara_cbe_fieldOfficeMoverShaker'} + +class DistributedBuilding(DistributedObject.DistributedObject): + SUIT_INIT_HEIGHT = 125 + TAKEOVER_SFX_PREFIX = 'phase_5/audio/sfx/' + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.interactiveProp = None + self.suitDoorOrigin = None + self.elevatorModel = None + self.fsm = ClassicFSM.ClassicFSM('DistributedBuilding', [State.State('off', self.enterOff, self.exitOff, ['waitForVictors', + 'waitForVictorsFromCogdo', + 'becomingToon', + 'becomingToonFromCogdo', + 'toon', + 'clearOutToonInterior', + 'becomingSuit', + 'suit', + 'clearOutToonInteriorForCogdo', + 'becomingCogdo', + 'becomingCogdoFromCogdo', + 'cogdo']), + State.State('waitForVictors', self.enterWaitForVictors, self.exitWaitForVictors, ['becomingToon']), + State.State('waitForVictorsFromCogdo', self.enterWaitForVictorsFromCogdo, self.exitWaitForVictorsFromCogdo, ['becomingToonFromCogdo', 'becomingCogdoFromCogdo']), + State.State('becomingToon', self.enterBecomingToon, self.exitBecomingToon, ['toon']), + State.State('becomingToonFromCogdo', self.enterBecomingToonFromCogdo, self.exitBecomingToonFromCogdo, ['toon']), + State.State('toon', self.enterToon, self.exitToon, ['clearOutToonInterior', 'clearOutToonInteriorForCogdo']), + State.State('clearOutToonInterior', self.enterClearOutToonInterior, self.exitClearOutToonInterior, ['becomingSuit']), + State.State('becomingSuit', self.enterBecomingSuit, self.exitBecomingSuit, ['suit']), + State.State('suit', self.enterSuit, self.exitSuit, ['waitForVictors', 'becomingToon']), + State.State('clearOutToonInteriorForCogdo', self.enterClearOutToonInteriorForCogdo, self.exitClearOutToonInteriorForCogdo, ['becomingCogdo']), + State.State('becomingCogdo', self.enterBecomingCogdo, self.exitBecomingCogdo, ['cogdo']), + State.State('becomingCogdoFromCogdo', self.enterBecomingCogdoFromCogdo, self.exitBecomingCogdoFromCogdo, ['cogdo']), + State.State('cogdo', self.enterCogdo, self.exitCogdo, ['waitForVictorsFromCogdo', 'becomingToonFromCogdo'])], 'off', 'off') + self.fsm.enterInitialState() + self.bossLevel = 0 + self.transitionTrack = None + self.elevatorNodePath = None + self.victorList = [0, + 0, + 0, + 0] + self.waitingMessage = None + self.cogDropSound = None + self.cogLandSound = None + self.cogSettleSound = None + self.cogWeakenSound = None + self.toonGrowSound = None + self.toonSettleSound = None + self.leftDoor = None + return + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.mode = 'toon' + self.townTopLevel = self.cr.playGame.hood.loader.geom + + def disable(self): + self.fsm.request('off') + del self.townTopLevel + self.stopTransition() + DistributedObject.DistributedObject.disable(self) + + def delete(self): + if self.elevatorNodePath: + self.elevatorNodePath.removeNode() + del self.elevatorNodePath + del self.elevatorModel + if hasattr(self, 'cab'): + del self.cab + del self.leftDoor + del self.rightDoor + del self.suitDoorOrigin + self.cleanupSuitBuilding() + self.unloadSfx() + del self.fsm + DistributedObject.DistributedObject.delete(self) + + def setBlock(self, block, interiorZoneId): + self.block = block + self.interiorZoneId = interiorZoneId + + def setSuitData(self, suitTrack, difficulty, numFloors): + self.track = suitTrack + self.difficulty = difficulty + self.numFloors = numFloors + + def setState(self, state, timestamp): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def getSuitElevatorNodePath(self): + if self.mode != 'suit': + self.setToSuit() + return self.elevatorNodePath + + def getCogdoElevatorNodePath(self): + if self.mode != 'cogdo': + self.setToCogdo() + return self.elevatorNodePath + + def getSuitDoorOrigin(self): + if self.mode != 'suit': + self.setToSuit() + return self.suitDoorOrigin + + def getCogdoDoorOrigin(self): + if self.mode != 'cogdo': + self.setToCogdo() + return self.suitDoorOrigin + + def getBossLevel(self): + return self.bossLevel + + def setBossLevel(self, bossLevel): + self.bossLevel = bossLevel + + def setVictorList(self, victorList): + self.victorList = victorList + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterWaitForVictors(self, ts): + if self.mode != 'suit': + self.setToSuit() + victorCount = self.victorList.count(base.localAvatar.doId) + if victorCount == 1: + self.acceptOnce('insideVictorElevator', self.handleInsideVictorElevator) + camera.reparentTo(render) + camera.setPosHpr(self.elevatorNodePath, 0, -32.5, 9.4, 0, 348, 0) + base.camLens.setMinFov(52.0/(4./3.)) + anyOthers = 0 + for v in self.victorList: + if v != 0 and v != base.localAvatar.doId: + anyOthers = 1 + + if anyOthers: + self.waitingMessage = DirectLabel(text=TTLocalizer.WaitingForOtherToons, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.35), scale=0.1) + elif victorCount == 0: + pass + else: + self.error('localToon is on the victorList %d times' % victorCount) + closeDoors(self.leftDoor, self.rightDoor) + for light in self.floorIndicator: + if light != None: + light.setColor(LIGHT_OFF_COLOR) + + return + + def handleInsideVictorElevator(self): + self.notify.info('inside victor elevator') + self.sendUpdate('setVictorReady', []) + + def exitWaitForVictors(self): + self.ignore('insideVictorElevator') + if self.waitingMessage != None: + self.waitingMessage.destroy() + self.waitingMessage = None + return + + def enterWaitForVictorsFromCogdo(self, ts): + if self.mode != 'cogdo': + self.setToCogdo() + victorCount = self.victorList.count(base.localAvatar.doId) + if victorCount == 1: + self.acceptOnce('insideVictorElevator', self.handleInsideVictorElevatorFromCogdo) + camera.reparentTo(render) + camera.setPosHpr(self.elevatorNodePath, 0, -32.5, 9.4, 0, 348, 0) + base.camLens.setMinFov(52.0/(4./3.)) + anyOthers = 0 + for v in self.victorList: + if v != 0 and v != base.localAvatar.doId: + anyOthers = 1 + + if anyOthers: + self.waitingMessage = DirectLabel(text=TTLocalizer.WaitingForOtherToons, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.35), scale=0.1) + elif victorCount == 0: + pass + else: + self.error('localToon is on the victorList %d times' % victorCount) + closeDoors(self.leftDoor, self.rightDoor) + for light in self.floorIndicator: + if light != None: + light.setColor(LIGHT_OFF_COLOR) + + return + + def handleInsideVictorElevatorFromCogdo(self): + self.sendUpdate('setVictorReady', []) + + def exitWaitForVictorsFromCogdo(self): + self.ignore('insideVictorElevator') + if self.waitingMessage != None: + self.waitingMessage.destroy() + self.waitingMessage = None + return + + def enterBecomingToon(self, ts): + self.animToToon(ts) + + def exitBecomingToon(self): + pass + + def enterBecomingToonFromCogdo(self, ts): + self.animToToonFromCogdo(ts) + + def exitBecomingToonFromCogdo(self): + pass + + def enterToon(self, ts): + prop = self.getInteractiveProp() + + if prop: + prop.buildingLiberated(self.doId) + + self.setToToon() + + def exitToon(self): + pass + + def enterClearOutToonInterior(self, ts): + pass + + def exitClearOutToonInterior(self): + pass + + def enterBecomingSuit(self, ts): + self.animToSuit(ts) + + def exitBecomingSuit(self): + pass + + def enterSuit(self, ts): + prop = self.getInteractiveProp() + + if prop and not prop.state == 'Sad': + prop.gotoSad(self.doId) + + self.setToSuit() + + def exitSuit(self): + pass + + def enterClearOutToonInteriorForCogdo(self, ts): + pass + + def exitClearOutToonInteriorForCogdo(self): + pass + + def enterBecomingCogdo(self, ts): + self.animToCogdo(ts) + + def exitBecomingCogdo(self): + pass + + def enterBecomingCogdoFromCogdo(self, ts): + self.animToCogdoFromCogdo(ts) + + def exitBecomingCogdoFromCogdo(self): + pass + + def enterCogdo(self, ts): + self.setToCogdo() + + def exitCogdo(self): + pass + + def getNodePaths(self): + nodePath = [] + npc = self.townTopLevel.findAllMatches('**/?b' + str(self.block) + ':*_DNARoot;+s') + for i in xrange(npc.getNumPaths()): + nodePath.append(npc.getPath(i)) + return nodePath + + def loadElevator(self, newNP, cogdo = False): + self.floorIndicator = [None, None, None, None, None] + self.elevatorNodePath = hidden.attachNewNode('elevatorNodePath') + if cogdo: + self.elevatorModel = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_csa_elevatorB') + else: + self.elevatorModel = loader.loadModel('phase_4/models/modules/elevator') + npc = self.elevatorModel.findAllMatches('**/floor_light_?;+s') + for i in xrange(npc.getNumPaths()): + np = npc.getPath(i) + floor = int(np.getName()[-1:]) - 1 + self.floorIndicator[floor] = np + if floor < self.numFloors: + np.setColor(LIGHT_OFF_COLOR) + else: + np.hide() + + self.elevatorModel.reparentTo(self.elevatorNodePath) + if self.mode == 'suit': + self.cab = self.elevatorModel.find('**/elevator') + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + dept = chr(self.track) + if dept in SuitDNA.suitDeptModelPaths: + corpIcon = cogIcons.find(SuitDNA.suitDeptModelPaths[dept]).copyTo(self.cab) + corpIcon.setPos(0, 6.79, 6.8) + corpIcon.setScale(3) + from toontown.suit import Suit + corpIcon.setColor(Suit.Suit.medallionColors[dept]) + cogIcons.removeNode() + self.leftDoor = self.elevatorModel.find('**/left-door') + if self.leftDoor.isEmpty(): + self.leftDoor = self.elevatorModel.find('**/left_door') + self.rightDoor = self.elevatorModel.find('**/right-door') + if self.rightDoor.isEmpty(): + self.rightDoor = self.elevatorModel.find('**/right_door') + self.suitDoorOrigin = newNP.find('**/*_door_origin') + self.elevatorNodePath.reparentTo(self.suitDoorOrigin) + self.normalizeElevator() + return + + def loadAnimToSuitSfx(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: COGBUILDING: Cog Take Over') + if self.cogDropSound == None: + self.cogDropSound = base.loadSfx(self.TAKEOVER_SFX_PREFIX + 'cogbldg_drop.ogg') + self.cogLandSound = base.loadSfx(self.TAKEOVER_SFX_PREFIX + 'cogbldg_land.ogg') + self.cogSettleSound = base.loadSfx(self.TAKEOVER_SFX_PREFIX + 'cogbldg_settle.ogg') + self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.ogg') + return + + def loadAnimToToonSfx(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: COGBUILDING: Toon Take Over') + if self.cogWeakenSound == None: + self.cogWeakenSound = base.loadSfx(self.TAKEOVER_SFX_PREFIX + 'cogbldg_weaken.ogg') + self.toonGrowSound = base.loadSfx(self.TAKEOVER_SFX_PREFIX + 'toonbldg_grow.ogg') + self.toonSettleSound = base.loadSfx(self.TAKEOVER_SFX_PREFIX + 'toonbldg_settle.ogg') + self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.ogg') + return + + def unloadSfx(self): + if self.cogDropSound != None: + self.cogDropSound = None + self.cogLandSound = None + self.cogSettleSound = None + self.openSfx = None + if self.cogWeakenSound != None: + self.cogWeakenSound = None + self.toonGrowSound = None + self.toonSettleSound = None + self.openSfx = None + return + + def _deleteTransitionTrack(self): + if self.transitionTrack: + DelayDelete.cleanupDelayDeletes(self.transitionTrack) + self.transitionTrack = None + return + + def animToSuit(self, timeStamp): + self.stopTransition() + if self.mode != 'toon': + self.setToToon() + self.loadAnimToSuitSfx() + sideBldgNodes = self.getNodePaths() + nodePath = hidden.find(self.getSbSearchString()) + newNP = self.setupSuitBuilding(nodePath) + if not self.leftDoor: + return + closeDoors(self.leftDoor, self.rightDoor) + newNP.stash() + sideBldgNodes.append(newNP) + soundPlayed = 0 + tracks = Parallel(name=self.taskName('toSuitTrack')) + for i in sideBldgNodes: + name = i.getName() + timeForDrop = TO_SUIT_BLDG_TIME * 0.85 + if name[0] == 's': + showTrack = Sequence(name=self.taskName('ToSuitFlatsTrack') + '-' + str(sideBldgNodes.index(i))) + initPos = Point3(0, 0, self.SUIT_INIT_HEIGHT) + i.getPos() + showTrack.append(Func(i.setPos, initPos)) + showTrack.append(Func(i.unstash)) + if i == sideBldgNodes[len(sideBldgNodes) - 1]: + showTrack.append(Func(self.normalizeElevator)) + if not soundPlayed: + showTrack.append(Func(base.playSfx, self.cogDropSound, 0, 1, None, 0.0)) + showTrack.append(LerpPosInterval(i, timeForDrop, i.getPos(), name=self.taskName('ToSuitAnim') + '-' + str(sideBldgNodes.index(i)))) + if not soundPlayed: + showTrack.append(Func(base.playSfx, self.cogLandSound, 0, 1, None, 0.0)) + showTrack.append(self.createBounceTrack(i, 2, 0.65, TO_SUIT_BLDG_TIME - timeForDrop, slowInitBounce=1.0)) + if not soundPlayed: + showTrack.append(Func(base.playSfx, self.cogSettleSound, 0, 1, None, 0.0)) + tracks.append(showTrack) + if not soundPlayed: + soundPlayed = 1 + elif name[0] == 't': + hideTrack = Sequence(name=self.taskName('ToSuitToonFlatsTrack')) + timeTillSquish = (self.SUIT_INIT_HEIGHT - 20.0) / self.SUIT_INIT_HEIGHT + timeTillSquish *= timeForDrop + hideTrack.append(LerpFunctionInterval(self.adjustColorScale, fromData=1, toData=0.25, duration=timeTillSquish, extraArgs=[i])) + hideTrack.append(LerpScaleInterval(i, timeForDrop - timeTillSquish, Vec3(1, 1, 0.01))) + hideTrack.append(Func(i.stash)) + hideTrack.append(Func(i.setScale, Vec3(1))) + hideTrack.append(Func(i.clearColorScale)) + tracks.append(hideTrack) + + self.stopTransition() + self._deleteTransitionTrack() + self.transitionTrack = tracks + self.transitionTrack.start(timeStamp) + return + + def setupSuitBuilding(self, nodePath): + if nodePath.isEmpty(): + return + dnaStore = self.cr.playGame.dnaStore + level = int(self.difficulty / 2) + 1 + if level > 5: + self.notify.warning('Level is bigger than 5: %s' % level) + suitNP = dnaStore.findNode('suit_landmark_' + chr(self.track) + str(min(level, 5))) + zoneId = dnaStore.getZoneFromBlockNumber(self.block) + newParentNP = base.cr.playGame.hood.loader.zoneDict[zoneId] + suitBuildingNP = suitNP.copyTo(newParentNP) + buildingTitle = dnaStore.getTitleFromBlockNumber(self.block) + if not buildingTitle: + buildingTitle = TTLocalizer.CogsInc + else: + buildingTitle += TTLocalizer.CogsIncExt + buildingTitle += '\n%s' % SuitDNA.getDeptFullname(chr(self.track)) + textNode = TextNode('sign') + textNode.setTextColor(1.0, 1.0, 1.0, 1.0) + textNode.setFont(ToontownGlobals.getSuitFont()) + textNode.setAlign(TextNode.ACenter) + textNode.setWordwrap(17.0) + textNode.setText(buildingTitle) + textHeight = textNode.getHeight() + zScale = (textHeight + 2) / 3.0 + signOrigin = suitBuildingNP.find('**/sign_origin;+s') + backgroundNP = loader.loadModel('phase_5/models/modules/suit_sign') + backgroundNP.reparentTo(signOrigin) + backgroundNP.setPosHprScale(0.0, 0.0, textHeight * 0.8 / zScale, 0.0, 0.0, 0.0, 8.0, 8.0, 8.0 * zScale) + signTextNodePath = backgroundNP.attachNewNode(textNode.generate()) + signTextNodePath.setPosHprScale(0.0, 0.0, -0.21 + textHeight * 0.1 / zScale, 0.0, 0.0, 0.0, 0.1, 0.1, 0.1 / zScale) + signTextNodePath.setColor(1.0, 1.0, 1.0, 1.0) + frontNP = suitBuildingNP.find('**/*_front/+GeomNode;+s') + backgroundNP.wrtReparentTo(frontNP) + frontNP.node().setEffect(DecalEffect.make()) + signTextNodePath.setAttrib(DepthOffsetAttrib.make(1)) + suitBuildingNP.setName('sb' + str(self.block) + ':_landmark__DNARoot') + suitBuildingNP.setPosHprScale(nodePath, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0) + suitBuildingNP.flattenMedium() + self.loadElevator(suitBuildingNP) + return suitBuildingNP + + def cleanupSuitBuilding(self): + if hasattr(self, 'floorIndicator'): + del self.floorIndicator + + def adjustColorScale(self, scale, node): + node.setColorScale(scale, scale, scale, 1) + + def animToCogdo(self, timeStamp): + self.stopTransition() + if self.mode != 'toon': + self.setToToon() + self.loadAnimToSuitSfx() + sideBldgNodes = self.getNodePaths() + nodePath = hidden.find(self.getSbSearchString()) + newNP = self.setupCogdo(nodePath) + closeDoors(self.leftDoor, self.rightDoor) + newNP.stash() + sideBldgNodes.append(newNP) + for np in sideBldgNodes: + if not np.isEmpty(): + np.setColorScale(0.6, 0.6, 0.6, 1.0) + + soundPlayed = 0 + tracks = Parallel(name=self.taskName('toCogdoTrack')) + for i in sideBldgNodes: + name = i.getName() + timeForDrop = TO_SUIT_BLDG_TIME * 0.85 + if name[0] == 'c': + showTrack = Sequence(name=self.taskName('ToCogdoFlatsTrack') + '-' + str(sideBldgNodes.index(i))) + initPos = Point3(0, 0, self.SUIT_INIT_HEIGHT) + i.getPos() + showTrack.append(Func(i.setPos, initPos)) + showTrack.append(Func(i.unstash)) + if i == sideBldgNodes[len(sideBldgNodes) - 1]: + showTrack.append(Func(self.normalizeElevator)) + if not soundPlayed: + showTrack.append(Func(base.playSfx, self.cogDropSound, 0, 1, None, 0.0)) + showTrack.append(LerpPosInterval(i, timeForDrop, i.getPos(), name=self.taskName('ToCogdoAnim') + '-' + str(sideBldgNodes.index(i)))) + if not soundPlayed: + showTrack.append(Func(base.playSfx, self.cogLandSound, 0, 1, None, 0.0)) + showTrack.append(self.createBounceTrack(i, 2, 0.65, TO_SUIT_BLDG_TIME - timeForDrop, slowInitBounce=1.0)) + if not soundPlayed: + showTrack.append(Func(base.playSfx, self.cogSettleSound, 0, 1, None, 0.0)) + tracks.append(showTrack) + if not soundPlayed: + soundPlayed = 1 + elif name[0] == 't': + hideTrack = Sequence(name=self.taskName('ToCogdoToonFlatsTrack')) + timeTillSquish = (self.SUIT_INIT_HEIGHT - 20.0) / self.SUIT_INIT_HEIGHT + timeTillSquish *= timeForDrop + hideTrack.append(LerpFunctionInterval(self.adjustColorScale, fromData=1, toData=0.25, duration=timeTillSquish, extraArgs=[i])) + hideTrack.append(LerpScaleInterval(i, timeForDrop - timeTillSquish, Vec3(1, 1, 0.01))) + hideTrack.append(Func(i.stash)) + hideTrack.append(Func(i.setScale, Vec3(1))) + hideTrack.append(Func(i.clearColorScale)) + tracks.append(hideTrack) + + self.stopTransition() + self._deleteTransitionTrack() + self.transitionTrack = tracks + self.transitionTrack.start(timeStamp) + return + + def setupCogdo(self, nodePath): + dnaStore = self.cr.playGame.dnaStore + level = int(self.difficulty / 2) + 1 + suitNP = dnaStore.findNode(FO_DICT[chr(self.track)]) + if not suitNP: + suitNP = loader.loadModel('phase_5/models/cogdominium/%s' % FO_DICT[chr(self.track)]) + zoneId = dnaStore.getZoneFromBlockNumber(self.block) + newParentNP = base.cr.playGame.hood.loader.zoneDict[zoneId] + suitBuildingNP = suitNP.copyTo(newParentNP) + buildingTitle = dnaStore.getTitleFromBlockNumber(self.block) + if not buildingTitle: + buildingTitle = TTLocalizer.Cogdominiums + else: + buildingTitle += TTLocalizer.CogdominiumsExt + textNode = TextNode('sign') + textNode.setTextColor(1.0, 1.0, 1.0, 1.0) + textNode.setFont(ToontownGlobals.getSuitFont()) + textNode.setAlign(TextNode.ACenter) + textNode.setWordwrap(12.0) + textNode.setText(buildingTitle.decode(sys.getdefaultencoding())) + textHeight = textNode.getHeight() + zScale = (textHeight + 2) / 3.0 + signOrigin = suitBuildingNP.find('**/sign_origin;+s') + backgroundNP = loader.loadModel('phase_5/models/cogdominium/field_office_sign') + backgroundNP.reparentTo(signOrigin) + backgroundNP.setPosHprScale(0.0, 0.0, -1.2 + textHeight * 0.8 / zScale, 0.0, 0.0, 0.0, 20.0, 8.0, 8.0 * zScale) + signTextNodePath = backgroundNP.attachNewNode(textNode.generate()) + signTextNodePath.setPosHprScale(0.0, 0.0, -0.13 + textHeight * 0.1 / zScale, 0.0, 0.0, 0.0, 0.1 * 8.0 / 20.0, 0.1, 0.1 / zScale) + signTextNodePath.setColor(1.0, 1.0, 1.0, 1.0) + frontNP = suitBuildingNP.find('**/*_front') + backgroundNP.wrtReparentTo(frontNP) + signTextNodePath.setAttrib(DepthOffsetAttrib.make(1)) + suitBuildingNP.setName('cb' + str(self.block) + ':_landmark__DNARoot') + suitBuildingNP.setPosHprScale(nodePath, 15.463, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0) + suitBuildingNP.setColorScale(0.6, 0.6, 0.6, 1.0) + self.loadElevator(suitBuildingNP, cogdo=True) + return suitBuildingNP + + def animToToon(self, timeStamp): + self.stopTransition() + if self.mode != 'suit': + self.setToSuit() + self.loadAnimToToonSfx() + suitSoundPlayed = 0 + toonSoundPlayed = 0 + bldgNodes = self.getNodePaths() + tracks = Parallel() + for i in bldgNodes: + name = i.getName() + if name[0] == 's': + hideTrack = Sequence(name=self.taskName('ToToonSuitFlatsTrack')) + landmark = name.find('_landmark_') != -1 + if not suitSoundPlayed: + hideTrack.append(Func(base.playSfx, self.cogWeakenSound, 0, 1, None, 0.0)) + hideTrack.append(self.createBounceTrack(i, 3, 1.2, TO_TOON_BLDG_TIME * 0.05, slowInitBounce=0.0)) + hideTrack.append(self.createBounceTrack(i, 5, 0.8, TO_TOON_BLDG_TIME * 0.1, slowInitBounce=0.0)) + hideTrack.append(self.createBounceTrack(i, 7, 1.2, TO_TOON_BLDG_TIME * 0.17, slowInitBounce=0.0)) + hideTrack.append(self.createBounceTrack(i, 9, 1.2, TO_TOON_BLDG_TIME * 0.18, slowInitBounce=0.0)) + realScale = i.getScale() + hideTrack.append(LerpScaleInterval(i, TO_TOON_BLDG_TIME * 0.1, Vec3(realScale[0], realScale[1], 0.01))) + if landmark: + hideTrack.append(Func(i.removeNode)) + else: + hideTrack.append(Func(i.stash)) + hideTrack.append(Func(i.setScale, Vec3(1))) + if not suitSoundPlayed: + suitSoundPlayed = 1 + tracks.append(hideTrack) + elif name[0] == 't': + hideTrack = Sequence(name=self.taskName('ToToonFlatsTrack')) + hideTrack.append(Wait(TO_TOON_BLDG_TIME * 0.5)) + if not toonSoundPlayed: + hideTrack.append(Func(base.playSfx, self.toonGrowSound, 0, 1, None, 0.0)) + hideTrack.append(Func(i.unstash)) + hideTrack.append(Func(i.setScale, Vec3(1, 1, 0.01))) + if not toonSoundPlayed: + hideTrack.append(Func(base.playSfx, self.toonSettleSound, 0, 1, None, 0.0)) + hideTrack.append(self.createBounceTrack(i, 11, 1.2, TO_TOON_BLDG_TIME * 0.5, slowInitBounce=4.0)) + tracks.append(hideTrack) + if not toonSoundPlayed: + toonSoundPlayed = 1 + + self.stopTransition() + bldgMTrack = tracks + localToonIsVictor = self.localToonIsVictor() + if localToonIsVictor: + camTrack = self.walkOutCameraTrack() + victoryRunTrack, delayDeletes = self.getVictoryRunTrack() + trackName = self.taskName('toToonTrack') + self._deleteTransitionTrack() + if localToonIsVictor: + freedomTrack1 = Func(self.cr.playGame.getPlace().setState, 'walk') + freedomTrack2 = Func(base.localAvatar.d_setParent, ToontownGlobals.SPRender) + self.transitionTrack = Parallel(camTrack, Sequence(victoryRunTrack, bldgMTrack, freedomTrack1, freedomTrack2), name=trackName) + else: + self.transitionTrack = Sequence(victoryRunTrack, bldgMTrack, name=trackName) + self.transitionTrack.delayDeletes = delayDeletes + if localToonIsVictor: + self.transitionTrack.start(0) + else: + self.transitionTrack.start(timeStamp) + return + + def animToToonFromCogdo(self, timeStamp): + self.stopTransition() + if self.mode != 'cogdo': + self.setToCogdo() + self.loadAnimToToonSfx() + suitSoundPlayed = 0 + toonSoundPlayed = 0 + bldgNodes = self.getNodePaths() + tracks = Parallel() + for i in bldgNodes: + i.clearColorScale() + name = i.getName() + if name[0] == 'c': + hideTrack = Sequence(name=self.taskName('ToToonCogdoFlatsTrack')) + landmark = name.find('_landmark_') != -1 + if not suitSoundPlayed: + hideTrack.append(Func(base.playSfx, self.cogWeakenSound, 0, 1, None, 0.0)) + hideTrack.append(self.createBounceTrack(i, 3, 1.2, TO_TOON_BLDG_TIME * 0.05, slowInitBounce=0.0)) + hideTrack.append(self.createBounceTrack(i, 5, 0.8, TO_TOON_BLDG_TIME * 0.1, slowInitBounce=0.0)) + hideTrack.append(self.createBounceTrack(i, 7, 1.2, TO_TOON_BLDG_TIME * 0.17, slowInitBounce=0.0)) + hideTrack.append(self.createBounceTrack(i, 9, 1.2, TO_TOON_BLDG_TIME * 0.18, slowInitBounce=0.0)) + realScale = i.getScale() + hideTrack.append(LerpScaleInterval(i, TO_TOON_BLDG_TIME * 0.1, Vec3(realScale[0], realScale[1], 0.01))) + if landmark: + hideTrack.append(Func(i.removeNode)) + else: + hideTrack.append(Func(i.stash)) + hideTrack.append(Func(i.setScale, Vec3(1))) + if not suitSoundPlayed: + suitSoundPlayed = 1 + tracks.append(hideTrack) + elif name[0] == 't': + hideTrack = Sequence(name=self.taskName('ToToonFromCogdoFlatsTrack')) + hideTrack.append(Wait(TO_TOON_BLDG_TIME * 0.5)) + if not toonSoundPlayed: + hideTrack.append(Func(base.playSfx, self.toonGrowSound, 0, 1, None, 0.0)) + hideTrack.append(Func(i.unstash)) + hideTrack.append(Func(i.setScale, Vec3(1, 1, 0.01))) + if not toonSoundPlayed: + hideTrack.append(Func(base.playSfx, self.toonSettleSound, 0, 1, None, 0.0)) + hideTrack.append(self.createBounceTrack(i, 11, 1.2, TO_TOON_BLDG_TIME * 0.5, slowInitBounce=4.0)) + tracks.append(hideTrack) + if not toonSoundPlayed: + toonSoundPlayed = 1 + + self.stopTransition() + bldgMTrack = tracks + localToonIsVictor = self.localToonIsVictor() + if localToonIsVictor: + camTrack = self.walkOutCameraTrack() + victoryRunTrack, delayDeletes = self.getVictoryRunTrack() + trackName = self.taskName('toToonFromCogdoTrack') + self._deleteTransitionTrack() + if localToonIsVictor: + freedomTrack1 = Func(self.cr.playGame.getPlace().setState, 'walk') + freedomTrack2 = Func(base.localAvatar.d_setParent, ToontownGlobals.SPRender) + self.transitionTrack = Parallel(camTrack, Sequence(victoryRunTrack, bldgMTrack, freedomTrack1, freedomTrack2), name=trackName) + else: + self.transitionTrack = Sequence(victoryRunTrack, bldgMTrack, name=trackName) + self.transitionTrack.delayDeletes = delayDeletes + if localToonIsVictor: + self.transitionTrack.start(0) + else: + self.transitionTrack.start(timeStamp) + return + + def walkOutCameraTrack(self): + track = Sequence(Func(camera.reparentTo, render), Func(camera.setPosHpr, self.elevatorNodePath, 0, -32.5, 9.4, 0, 348, 0), Func(base.camLens.setMinFov, 52.0/(4./3.)), Wait(VICTORY_RUN_TIME), Func(camera.setPosHpr, self.elevatorNodePath, 0, -32.5, 17, 0, 347, 0), Func(base.camLens.setMinFov, 75.0/(4./3.)), Wait(TO_TOON_BLDG_TIME), Func(base.camLens.setMinFov, 52.0/(4./3.))) + return track + + def plantVictorsOutsideBldg(self): + retVal = 0 + for victor in self.victorList: + if victor != 0 and victor in self.cr.doId2do: + toon = self.cr.doId2do[victor] + toon.setPosHpr(self.elevatorModel, 0, -10, 0, 0, 0, 0) + toon.startSmooth() + if victor == base.localAvatar.getDoId(): + retVal = 1 + self.cr.playGame.getPlace().setState('walk') + + return retVal + + def getVictoryRunTrack(self): + origPosTrack = Sequence() + delayDeletes = [] + i = 0 + for victor in self.victorList: + if victor != 0 and victor in self.cr.doId2do: + toon = self.cr.doId2do[victor] + delayDeletes.append(DelayDelete.DelayDelete(toon, 'getVictoryRunTrack')) + toon.stopSmooth() + toon.setParent(ToontownGlobals.SPHidden) + origPosTrack.append(Func(toon.setPosHpr, self.elevatorNodePath, apply(Point3, ElevatorPoints[i]), Point3(180, 0, 0))) + origPosTrack.append(Func(toon.setParent, ToontownGlobals.SPRender)) + i += 1 + + openDoors = getOpenInterval(self, self.leftDoor, self.rightDoor, self.openSfx, None) + toonDoorPosHpr = self.cr.playGame.dnaStore.getDoorPosHprFromBlockNumber(self.block) + useFarExitPoints = toonDoorPosHpr.getPos(render).getZ() > 1.0 + runOutAll = Parallel() + i = 0 + for victor in self.victorList: + if victor != 0 and victor in self.cr.doId2do: + toon = self.cr.doId2do[victor] + p0 = Point3(0, 0, 0) + p1 = Point3(ElevatorPoints[i][0], ElevatorPoints[i][1] - 5.0, ElevatorPoints[i][2]) + if useFarExitPoints: + p2 = Point3(ElevatorOutPointsFar[i][0], ElevatorOutPointsFar[i][1], ElevatorOutPointsFar[i][2]) + else: + p2 = Point3(ElevatorOutPoints[i][0], ElevatorOutPoints[i][1], ElevatorOutPoints[i][2]) + runOutSingle = Sequence(Func(Emote.globalEmote.disableBody, toon, 'getVictory'), Func(toon.animFSM.request, 'run'), LerpPosInterval(toon, TOON_VICTORY_EXIT_TIME * 0.25, p1, other=self.elevatorNodePath), Func(toon.headsUp, self.elevatorNodePath, p2), LerpPosInterval(toon, TOON_VICTORY_EXIT_TIME * 0.5, p2, other=self.elevatorNodePath), LerpHprInterval(toon, TOON_VICTORY_EXIT_TIME * 0.25, Point3(0, 0, 0), other=self.elevatorNodePath), Func(toon.animFSM.request, 'neutral'), Func(toon.startSmooth), Func(Emote.globalEmote.releaseBody, toon, 'getVictory')) + runOutAll.append(runOutSingle) + i += 1 + + victoryRunTrack = Sequence(origPosTrack, openDoors, runOutAll) + return (victoryRunTrack, delayDeletes) + + def animToCogdoFromCogdo(self, timeStamp): + self.stopTransition() + if self.mode != 'cogdo': + self.setToCogdo() + self.loadAnimToToonSfx() + localToonIsVictor = self.localToonIsVictor() + if localToonIsVictor: + camTrack = self.walkOutCameraTrack() + victoryRunTrack, delayDeletes = self.getVictoryRunTrack() + trackName = self.taskName('toToonFromCogdoTrack') + self._deleteTransitionTrack() + if localToonIsVictor: + freedomTrack1 = Func(self.cr.playGame.getPlace().setState, 'walk') + freedomTrack2 = Func(base.localAvatar.d_setParent, ToontownGlobals.SPRender) + self.transitionTrack = Parallel(camTrack, Sequence(victoryRunTrack, freedomTrack1, freedomTrack2), name=trackName) + else: + self.transitionTrack = Sequence(victoryRunTrack, name=trackName) + self.transitionTrack.delayDeletes = delayDeletes + if localToonIsVictor: + self.transitionTrack.start(0) + else: + self.transitionTrack.start(timeStamp) + + def localToonIsVictor(self): + retVal = 0 + for victor in self.victorList: + if victor == base.localAvatar.getDoId(): + retVal = 1 + + return retVal + + def createBounceTrack(self, nodeObj, numBounces, startScale, totalTime, slowInitBounce = 0.0): + if not nodeObj or numBounces < 1 or startScale == 0.0 or totalTime == 0: + self.notify.warning('createBounceTrack called with invalid parameter') + return + result = Sequence() + numBounces += 1 + if slowInitBounce: + bounceTime = totalTime / (numBounces + slowInitBounce - 1.0) + else: + bounceTime = totalTime / float(numBounces) + if slowInitBounce: + currTime = bounceTime * float(slowInitBounce) + else: + currTime = bounceTime + realScale = nodeObj.getScale() + currScaleDiff = startScale - realScale[2] + for currBounceScale in xrange(numBounces): + if currBounceScale == numBounces - 1: + currScale = realScale[2] + elif currBounceScale % 2: + currScale = realScale[2] - currScaleDiff + else: + currScale = realScale[2] + currScaleDiff + result.append(LerpScaleInterval(nodeObj, currTime, Vec3(realScale[0], realScale[1], currScale), blendType='easeInOut')) + currScaleDiff *= 0.5 + currTime = bounceTime + + return result + + def stopTransition(self): + if self.transitionTrack: + self.transitionTrack.finish() + self._deleteTransitionTrack() + + def setToSuit(self): + self.stopTransition() + if self.mode == 'suit': + return + self.mode = 'suit' + nodes = self.getNodePaths() + for i in nodes: + name = i.getName() + if name[0] == 's': + if name.find('_landmark_') != -1: + i.removeNode() + else: + i.unstash() + elif name[0] == 't': + if name.find('_landmark_') != -1: + i.stash() + else: + i.stash() + elif name[0] == 'c': + if name.find('_landmark_') != -1: + i.removeNode() + else: + i.stash() + + npc = hidden.findAllMatches(self.getSbSearchString()) + for i in xrange(npc.getNumPaths()): + nodePath = npc.getPath(i) + self.adjustSbNodepathScale(nodePath) + self.notify.debug('net transform = %s' % str(nodePath.getNetTransform())) + self.setupSuitBuilding(nodePath) + + def setToCogdo(self): + self.stopTransition() + if self.mode == 'cogdo': + return + self.mode = 'cogdo' + nodes = self.getNodePaths() + for i in nodes: + name = i.getName() + if name[0] == 'c': + if name.find('_landmark_') != -1: + i.removeNode() + else: + i.unstash() + elif name[0] == 't': + if name.find('_landmark_') != -1: + i.stash() + else: + i.stash() + elif name[0] == 's': + if name.find('_landmark_') != -1: + i.removeNode() + else: + i.stash() + + for np in nodes: + if not np.isEmpty(): + np.setColorScale(0.6, 0.6, 0.6, 1.0) + + npc = hidden.findAllMatches(self.getSbSearchString()) + for i in xrange(npc.getNumPaths()): + nodePath = npc.getPath(i) + self.adjustSbNodepathScale(nodePath) + self.notify.debug('net transform = %s' % str(nodePath.getNetTransform())) + self.setupCogdo(nodePath) + + def setToToon(self): + self.stopTransition() + if self.mode == 'toon': + return + self.mode = 'toon' + self.suitDoorOrigin = None + nodes = self.getNodePaths() + for i in nodes: + i.clearColorScale() + name = i.getName() + if name[0] == 's': + if name.find('_landmark_') != -1: + i.removeNode() + else: + i.stash() + elif name[0] == 't': + if name.find('_landmark_') != -1: + i.unstash() + else: + i.unstash() + elif name[0] == 'c': + if name.find('_landmark_') != -1: + i.removeNode() + else: + i.stash() + + return + + def normalizeElevator(self): + self.elevatorNodePath.setScale(render, Vec3(1, 1, 1)) + self.elevatorNodePath.setPosHpr(0, 0, 0, 0, 0, 0) + + def getSbSearchString(self): + return 'landmarkBlocks/sb' + str(self.block) + ':*_landmark_*_DNARoot' + + def adjustSbNodepathScale(self, nodePath): + pass + + def getVisZoneId(self): + return base.cr.playGame.hood.dnaStore.getZoneFromBlockNumber(self.block) + + def getInteractiveProp(self): + if self.interactiveProp: + return self.interactiveProp + elif base.cr.playGame.hood: + loader = base.cr.playGame.hood.loader + + if hasattr(loader, 'getInteractiveProp'): + self.interactiveProp = base.cr.playGame.hood.loader.getInteractiveProp(self.getVisZoneId()) + + return self.interactiveProp + return None diff --git a/toontown/building/DistributedBuildingAI.py b/toontown/building/DistributedBuildingAI.py new file mode 100755 index 00000000..00f463d1 --- /dev/null +++ b/toontown/building/DistributedBuildingAI.py @@ -0,0 +1,560 @@ +import random +import time + +import DistributedDoorAI +import DistributedElevatorExtAI +import DistributedKnockKnockDoorAI +import DistributedSuitInteriorAI +import DistributedToonHallInteriorAI +import DistributedToonInteriorAI +import DoorTypes +import FADoorCodes +import SuitBuildingGlobals +import SuitPlannerInteriorAI +from direct.distributed import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.task.Task import Task +from otp.ai.AIBaseGlobal import * +from toontown.cogdominium.CogdoLayout import CogdoLayout +from toontown.cogdominium.DistributedCogdoElevatorExtAI import DistributedCogdoElevatorExtAI +from toontown.cogdominium.DistributedCogdoInteriorAI import DistributedCogdoInteriorAI +from toontown.cogdominium.CogdoLayout import CogdoLayout +from toontown.cogdominium.SuitPlannerCogdoInteriorAI import SuitPlannerCogdoInteriorAI +from toontown.hood import ZoneUtil +from toontown.toonbase.ToontownGlobals import ToonHall +from toontown.toonbase import ToontownGlobals + +class DistributedBuildingAI(DistributedObjectAI.DistributedObjectAI): + def __init__(self, air, blockNumber, zoneId, trophyMgr): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.block = blockNumber + self.zoneId = zoneId + self.canonicalZoneId = ZoneUtil.getCanonicalZoneId(zoneId) + self.trophyMgr = trophyMgr + self.victorResponses = None + self.fsm = ClassicFSM.ClassicFSM( + 'DistributedBuildingAI', + [ + State.State('off', self.enterOff, self.exitOff, + ['waitForVictors', 'becomingToon', 'toon', + 'clearOutToonInterior', 'becomingSuit', 'suit', + 'clearOutToonInteriorForCogdo', 'becomingCogdo', + 'cogdo']), + State.State('waitForVictors', self.enterWaitForVictors, self.exitWaitForVictors, + ['becomingToon']), + State.State('waitForVictorsFromCogdo', self.enterWaitForVictorsFromCogdo, self.exitWaitForVictorsFromCogdo, + ['becomingToonFromCogdo']), + State.State('becomingToon', self.enterBecomingToon, self.exitBecomingToon, + ['toon']), + State.State('becomingToonFromCogdo', self.enterBecomingToonFromCogdo, self.exitBecomingToonFromCogdo, + ['toon']), + State.State('toon', self.enterToon, self.exitToon, + ['clearOutToonInterior', 'clearOutToonInteriorForCogdo']), + State.State('clearOutToonInterior', self.enterClearOutToonInterior, self.exitClearOutToonInterior, + ['becomingSuit']), + State.State('becomingSuit', self.enterBecomingSuit, self.exitBecomingSuit, + ['suit']), + State.State('suit', self.enterSuit, self.exitSuit, + ['waitForVictors', 'becomingToon']), + State.State('clearOutToonInteriorForCogdo', self.enterClearOutToonInteriorForCogdo, self.exitClearOutToonInteriorForCogdo, + ['becomingCogdo']), + State.State('becomingCogdo', self.enterBecomingCogdo, self.exitBecomingCogdo, + ['cogdo']), + State.State('cogdo', self.enterCogdo, self.exitCogdo, + ['waitForVictorsFromCogdo', 'becomingToonFromCogdo']) + ], 'off', 'off') + self.fsm.enterInitialState() + self.track = 'c' + self.realTrack = 'c' + self.difficulty = 1 + self.numFloors = 0 + self.savedBy = None + self.becameSuitTime = 0 + self.frontDoorPoint = None + self.suitPlannerExt = None + + def announceGenerate(self): + DistributedObjectAI.DistributedObjectAI.announceGenerate(self) + (exteriorZoneId, interiorZoneId) = self.getExteriorAndInteriorZoneId() + self.air.buildingQueryMgr.buildings[exteriorZoneId] = self + self.air.buildingQueryMgr.buildings[interiorZoneId] = self + + def cleanup(self): + if self.isDeleted(): + return + self.fsm.requestFinalState() + if hasattr(self, 'interior'): + self.interior.requestDelete() + del self.interior + if hasattr(self, 'door'): + self.door.requestDelete() + del self.door + self.insideDoor.requestDelete() + del self.insideDoor + self.knockKnock.requestDelete() + del self.knockKnock + if hasattr(self, 'elevator'): + self.elevator.requestDelete() + del self.elevator + self.requestDelete() + + def delete(self): + self.cleanup() + taskMgr.remove(self.taskName('suitbldg-time-out')) + taskMgr.remove(self.taskName(str(self.block) + '_becomingToon-timer')) + taskMgr.remove(self.taskName(str(self.block) + '_becomingSuit-timer')) + DistributedObjectAI.DistributedObjectAI.delete(self) + del self.fsm + + def getPickleData(self): + pickleData = { + 'state': str(self.fsm.getCurrentState().getName()), + 'block': str(self.block), + 'track': str(self.track), + 'difficulty': str(self.difficulty), + 'numFloors': str(self.numFloors), + 'savedBy': self.savedBy, + 'becameSuitTime': self.becameSuitTime + } + return pickleData + + def _getMinMaxFloors(self, difficulty): + return SuitBuildingGlobals.SuitBuildingInfo[difficulty][0] + + def suitTakeOver(self, suitTrack, difficulty, buildingHeight): + if not self.isToonBlock(): + return + self.updateSavedBy(None) + difficulty = min(difficulty, len(SuitBuildingGlobals.SuitBuildingInfo) - 1) + (minFloors, maxFloors) = self._getMinMaxFloors(difficulty) + if buildingHeight is None: + numFloors = random.randint(minFloors, maxFloors) + else: + numFloors = buildingHeight + 1 + if (numFloors < minFloors) or (numFloors > maxFloors): + numFloors = random.randint(minFloors, maxFloors) + self.track = suitTrack + self.difficulty = difficulty + self.numFloors = numFloors + self.becameSuitTime = time.time() + self.fsm.request('clearOutToonInterior') + + def cogdoTakeOver(self, difficulty, buildingHeight, track = 's'): + if not self.isToonBlock(): + return None + + self.updateSavedBy(None) + self.track = track + self.realTrack = track + self.difficulty = difficulty + self.numFloors = 0 + self.becameSuitTime = time.time() + self.fsm.request('clearOutToonInteriorForCogdo') + + def toonTakeOver(self): + if 'cogdo' in self.fsm.getCurrentState().getName().lower(): + self.fsm.request('becomingToonFromCogdo') + else: + self.fsm.request('becomingToon') + if self.suitPlannerExt: + self.suitPlannerExt.recycleBuilding() + if hasattr(self, 'interior'): + self.interior.requestDelete() + del self.interior + + def getFrontDoorPoint(self): + return self.frontDoorPoint + + def setFrontDoorPoint(self, point): + self.frontDoorPoint = point + + def getBlock(self): + (dummy, interiorZoneId) = self.getExteriorAndInteriorZoneId() + return [self.block, interiorZoneId] + + def getSuitData(self): + return [ord(self.track), self.difficulty, self.numFloors] + + def getState(self): + return [self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()] + + def setState(self, state, timestamp=0): + self.fsm.request(state) + + def isSuitBuilding(self): + state = self.fsm.getCurrentState().getName() + return state in ('suit', 'becomingSuit', 'clearOutToonInterior') + + def isCogdo(self): + state = self.fsm.getCurrentState().getName() + return state in ('cogdo', 'becomingCogdo', 'clearOutToonInteriorForCogdo') + + def isSuitBlock(self): + return self.isSuitBuilding() or self.isCogdo() + + def isEstablishedSuitBlock(self): + state = self.fsm.getCurrentState().getName() + return state == 'suit' + + def isToonBlock(self): + state = self.fsm.getCurrentState().getName() + return state in ('toon', 'becomingToon', 'becomingToonFromCogdo') + + def getExteriorAndInteriorZoneId(self): + blockNumber = self.block + dnaStore = self.air.dnaStoreMap[self.canonicalZoneId] + zoneId = dnaStore.getZoneFromBlockNumber(blockNumber) + interiorZoneId = (zoneId - (zoneId%100)) + 500 + blockNumber + return (zoneId, interiorZoneId) + + def d_setState(self, state): + self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()]) + + def b_setVictorList(self, victorList): + self.setVictorList(victorList) + self.d_setVictorList(victorList) + + def d_setVictorList(self, victorList): + self.sendUpdate('setVictorList', [victorList]) + + def setVictorList(self, victorList): + self.victorList = victorList + + def findVictorIndex(self, avId): + for i in xrange(len(self.victorList)): + if self.victorList[i] == avId: + return i + + def recordVictorResponse(self, avId): + index = self.findVictorIndex(avId) + if index is None: + self.air.writeServerEvent('suspicious', avId, 'DistributedBuildingAI.setVictorReady from toon not in %s.' % self.victorList) + return + self.victorResponses[index] = avId + + def allVictorsResponded(self): + if self.victorResponses == self.victorList: + return 1 + else: + return 0 + + def setVictorReady(self): + avId = self.air.getAvatarIdFromSender() + if self.victorResponses is None: + self.air.writeServerEvent('suspicious', avId, 'DistributedBuildingAI.setVictorReady in state %s.' % self.fsm.getCurrentState().getName()) + return + self.recordVictorResponse(avId) + event = self.air.getAvatarExitEvent(avId) + self.ignore(event) + if self.allVictorsResponded(): + self.toonTakeOver() + + def setVictorExited(self, avId): + print 'victor %d exited unexpectedly for bldg %d' % (avId, self.doId) + self.recordVictorResponse(avId) + if self.allVictorsResponded(): + self.toonTakeOver() + + def enterOff(self): + pass + + def exitOff(self): + pass + + def getToon(self, toonId): + if toonId in self.air.doId2do: + return self.air.doId2do[toonId] + else: + self.notify.warning('getToon() - toon: %d not in repository!' % toonId) + + def updateSavedBy(self, savedBy): + if self.savedBy: + for (avId, name, dna) in self.savedBy: + self.trophyMgr.removeTrophy(avId, self.numFloors) + self.savedBy = savedBy + if self.savedBy: + for (avId, name, dna) in self.savedBy: + self.trophyMgr.addTrophy(avId, name, self.numFloors) + + def enterWaitForVictors(self, victorList, savedBy): + activeToons = [] + for t in victorList: + toon = None + if t: + toon = self.getToon(t) + if toon is not None: + activeToons.append(toon) + for t in victorList: + toon = None + if t: + toon = self.getToon(t) + self.air.writeServerEvent('buildingDefeated', t, '%s|%s|%s|%s' % (self.track, self.numFloors, self.zoneId, victorList)) + if toon is not None: + self.air.questManager.toonKilledBuilding(toon, self.track, self.difficulty, self.numFloors, self.zoneId, 0) + toon.addStat(ToontownGlobals.STAT_BLDG) + for i in xrange(0, 4): + victor = victorList[i] + if (victor is None) or (victor not in self.air.doId2do): + victorList[i] = 0 + continue + event = self.air.getAvatarExitEvent(victor) + self.accept(event, self.setVictorExited, extraArgs=[victor]) + self.b_setVictorList(victorList) + self.updateSavedBy(savedBy) + self.victorResponses = [0, 0, 0, 0] + self.d_setState('waitForVictors') + + def exitWaitForVictors(self): + self.victorResponses = None + for victor in self.victorList: + event = simbase.air.getAvatarExitEvent(victor) + self.ignore(event) + + def enterWaitForVictorsFromCogdo(self, victorList, savedBy): + activeToons = [] + for t in victorList: + toon = None + if t: + toon = self.getToon(t) + + if toon != None: + activeToons.append(toon) + continue + + for t in victorList: + toon = None + if t: + toon = self.getToon(t) + self.air.writeServerEvent('buildingDefeated', t, '%s|%s|%s|%s' % (self.track, self.numFloors, self.zoneId, victorList)) + + if toon != None: + self.air.questManager.toonKilledBuilding(toon, self.track, self.difficulty, 5, self.zoneId, 1) + toon.addStat(ToontownGlobals.STAT_COGDO) + continue + + victorList.extend([None, None, None, None]) + for i in xrange(0, 4): + victor = victorList[i] + if victor == None or not victor in self.air.doId2do: + victorList[i] = 0 + continue + event = self.air.getAvatarExitEvent(victor) + self.accept(event, self.setVictorExited, extraArgs = [ + victor]) + + self.b_setVictorList(victorList[:4]) + self.updateSavedBy(savedBy) + self.victorResponses = [ + 0, + 0, + 0, + 0] + self.d_setState('waitForVictorsFromCogdo') + + def exitWaitForVictorsFromCogdo(self): + self.victorResponses = None + for victor in self.victorList: + event = simbase.air.getAvatarExitEvent(victor) + self.ignore(event) + + def enterBecomingToon(self): + self.d_setState('becomingToon') + name = self.taskName(str(self.block) + '_becomingToon-timer') + taskMgr.doMethodLater(SuitBuildingGlobals.VICTORY_SEQUENCE_TIME, self.becomingToonTask, name) + + def exitBecomingToon(self): + name = self.taskName(str(self.block) + '_becomingToon-timer') + taskMgr.remove(name) + + def enterBecomingToonFromCogdo(self): + self.d_setState('becomingToonFromCogdo') + name = self.taskName(str(self.block) + '_becomingToonFromCogdo-timer') + taskMgr.doMethodLater(SuitBuildingGlobals.VICTORY_SEQUENCE_TIME, self.becomingToonTask, name) + + def exitBecomingToonFromCogdo(self): + name = self.taskName(str(self.block) + '_becomingToonFromCogdo-timer') + taskMgr.remove(name) + + def becomingToonTask(self, task): + self.fsm.request('toon') + self.suitPlannerExt.buildingMgr.save() + return Task.done + + def enterToon(self): + self.d_setState('toon') + (exteriorZoneId, interiorZoneId) = self.getExteriorAndInteriorZoneId() + if simbase.config.GetBool('want-new-toonhall', 1) and ZoneUtil.getCanonicalZoneId(interiorZoneId) == ToonHall: + self.interior = DistributedToonHallInteriorAI.DistributedToonHallInteriorAI(self.block, self.air, interiorZoneId, self) + else: + self.interior = DistributedToonInteriorAI.DistributedToonInteriorAI(self.block, self.air, interiorZoneId, self) + self.interior.generateWithRequired(interiorZoneId) + door = self.createExteriorDoor() + insideDoor = DistributedDoorAI.DistributedDoorAI(self.air, self.block, DoorTypes.INT_STANDARD) + door.setOtherDoor(insideDoor) + insideDoor.setOtherDoor(door) + door.zoneId = exteriorZoneId + insideDoor.zoneId = interiorZoneId + door.generateWithRequired(exteriorZoneId) + insideDoor.generateWithRequired(interiorZoneId) + self.door = door + self.insideDoor = insideDoor + self.becameSuitTime = 0 + self.knockKnock = DistributedKnockKnockDoorAI.DistributedKnockKnockDoorAI(self.air, self.block) + self.knockKnock.generateWithRequired(exteriorZoneId) + self.air.writeServerEvent('building-toon', self.doId, '%s|%s' % (self.zoneId, self.block)) + + def createExteriorDoor(self): + result = DistributedDoorAI.DistributedDoorAI(self.air, self.block, DoorTypes.EXT_STANDARD) + return result + + def exitToon(self): + self.door.setDoorLock(FADoorCodes.BUILDING_TAKEOVER) + + def enterClearOutToonInterior(self): + self.d_setState('clearOutToonInterior') + if hasattr(self, 'interior'): + self.interior.setState('beingTakenOver') + name = self.taskName(str(self.block) + '_clearOutToonInterior-timer') + taskMgr.doMethodLater(SuitBuildingGlobals.CLEAR_OUT_TOON_BLDG_TIME, self.clearOutToonInteriorTask, name) + + def exitClearOutToonInterior(self): + name = self.taskName(str(self.block) + '_clearOutToonInterior-timer') + taskMgr.remove(name) + + def clearOutToonInteriorTask(self, task): + self.fsm.request('becomingSuit') + return Task.done + + def enterBecomingSuit(self): + self.sendUpdate('setSuitData', [ord(self.track), self.difficulty, self.numFloors]) + self.d_setState('becomingSuit') + name = self.taskName(str(self.block) + '_becomingSuit-timer') + taskMgr.doMethodLater(SuitBuildingGlobals.TO_SUIT_BLDG_TIME, self.becomingSuitTask, name) + + def exitBecomingSuit(self): + name = self.taskName(str(self.block) + '_becomingSuit-timer') + taskMgr.remove(name) + if hasattr(self, 'interior'): + self.interior.requestDelete() + del self.interior + self.door.requestDelete() + del self.door + self.insideDoor.requestDelete() + del self.insideDoor + self.knockKnock.requestDelete() + del self.knockKnock + + def becomingSuitTask(self, task): + self.fsm.request('suit') + self.suitPlannerExt.buildingMgr.save() + return Task.done + + def enterSuit(self): + self.sendUpdate('setSuitData', [ord(self.track), self.difficulty, self.numFloors]) + (zoneId, interiorZoneId) = self.getExteriorAndInteriorZoneId() + self.planner = SuitPlannerInteriorAI.SuitPlannerInteriorAI(self.numFloors, self.difficulty, self.track, interiorZoneId) + self.d_setState('suit') + (exteriorZoneId, interiorZoneId) = self.getExteriorAndInteriorZoneId() + self.elevator = DistributedElevatorExtAI.DistributedElevatorExtAI(self.air, self) + self.elevator.generateWithRequired(exteriorZoneId) + self.air.writeServerEvent('building-cog', self.doId, '%s|%s|%s|%s' % (self.zoneId, self.block, self.track, self.numFloors)) + + def exitSuit(self): + del self.planner + if hasattr(self, 'elevator'): + self.elevator.requestDelete() + del self.elevator + + def enterClearOutToonInteriorForCogdo(self): + self.d_setState('clearOutToonInteriorForCogdo') + if hasattr(self, 'interior'): + self.interior.setState('beingTakenOver') + name = self.taskName(str(self.block) + '_clearOutToonInteriorForCogdo-timer') + taskMgr.doMethodLater(SuitBuildingGlobals.CLEAR_OUT_TOON_BLDG_TIME, self.clearOutToonInteriorForCogdoTask, name) + + def exitClearOutToonInteriorForCogdo(self): + name = self.taskName(str(self.block) + '_clearOutToonInteriorForCogdo-timer') + taskMgr.remove(name) + + def clearOutToonInteriorForCogdoTask(self, task): + self.fsm.request('becomingCogdo') + return Task.done + + def enterBecomingCogdo(self): + self.sendUpdate('setSuitData', [ + ord(self.realTrack), + self.difficulty, + self.numFloors]) + self.d_setState('becomingCogdo') + name = self.taskName(str(self.block) + '_becomingCogdo-timer') + taskMgr.doMethodLater(SuitBuildingGlobals.TO_SUIT_BLDG_TIME, self.becomingCogdoTask, name) + + def exitBecomingCogdo(self): + name = self.taskName(str(self.block) + '_becomingCogdo-timer') + taskMgr.remove(name) + if hasattr(self, 'interior'): + self.interior.requestDelete() + del self.interior + self.door.requestDelete() + del self.door + self.insideDoor.requestDelete() + del self.insideDoor + self.knockKnock.requestDelete() + del self.knockKnock + + def becomingCogdoTask(self, task): + self.fsm.request('cogdo') + self.suitPlannerExt.buildingMgr.save() + return Task.done + + def enterCogdo(self): + self.sendUpdate('setSuitData', [ + ord(self.realTrack), + self.difficulty, + self.numFloors]) + (zoneId, interiorZoneId) = self.getExteriorAndInteriorZoneId() + self._cogdoLayout = CogdoLayout(self.numFloors) + self.planner = SuitPlannerCogdoInteriorAI(self._cogdoLayout, self.difficulty, self.track, interiorZoneId) + self.d_setState('cogdo') + (exteriorZoneId, interiorZoneId) = self.getExteriorAndInteriorZoneId() + self.elevator = DistributedCogdoElevatorExtAI(self.air, self) + self.elevator.generateWithRequired(exteriorZoneId) + + def exitCogdo(self): + del self.planner + if hasattr(self, 'elevator'): + self.elevator.requestDelete() + del self.elevator + + def setSuitPlannerExt(self, planner): + self.suitPlannerExt = planner + + def _createSuitInterior(self): + return DistributedSuitInteriorAI.DistributedSuitInteriorAI(self.air, self.elevator) + + def _createCogdoInterior(self): + return DistributedCogdoInteriorAI(self.air, self) + + def createSuitInterior(self): + self.interior = self._createSuitInterior() + (dummy, interiorZoneId) = self.getExteriorAndInteriorZoneId() + self.interior.fsm.request('WaitForAllToonsInside') + self.interior.generateWithRequired(interiorZoneId) + + def createCogdoInterior(self): + self.interior = self._createCogdoInterior() + (dummy, interiorZoneId) = self.getExteriorAndInteriorZoneId() + self.interior.generateWithRequired(interiorZoneId) + self.interior.b_setState('WaitForAllToonsInside') + + def deleteSuitInterior(self): + if hasattr(self, 'interior'): + self.interior.requestDelete() + del self.interior + if hasattr(self, 'elevator'): + self.elevator.d_setFloor(-1) + self.elevator.open() + + def deleteCogdoInterior(self): + self.deleteSuitInterior() diff --git a/toontown/building/DistributedBuildingMgrAI.py b/toontown/building/DistributedBuildingMgrAI.py new file mode 100755 index 00000000..c4f4ff19 --- /dev/null +++ b/toontown/building/DistributedBuildingMgrAI.py @@ -0,0 +1,237 @@ +from direct.directnotify.DirectNotifyGlobal import * +import cPickle + +from otp.ai.AIBaseGlobal import * +from toontown.building import DistributedBuildingAI +from toontown.building import GagshopBuildingAI +from toontown.building import HQBuildingAI +from toontown.building import KartShopBuildingAI +from toontown.building import PetshopBuildingAI +from toontown.hood import ZoneUtil + + +class DistributedBuildingMgrAI: + notify = directNotify.newCategory('DistributedBuildingMgrAI') + + def __init__(self, air, branchId, dnaStore, trophyMgr): + self.air = air + self.branchId = branchId + self.canonicalBranchId = ZoneUtil.getCanonicalZoneId(self.branchId) + self.dnaStore = dnaStore + self.trophyMgr = trophyMgr + self.__buildings = {} + self.tableName = 'buildings_%s' % self.branchId + self.findAllLandmarkBuildings() + + def cleanup(self): + for building in self.__buildings.values(): + building.cleanup() + self.__buildings = {} + + def isValidBlockNumber(self, blockNumber): + return blockNumber in self.__buildings + + def isSuitBlock(self, blockNumber): + if not self.isValidBlockNumber(blockNumber): + return False + return self.__buildings[blockNumber].isSuitBlock() + + def getSuitBlocks(self): + blocks = [] + for blockNumber, building in self.__buildings.items(): + if building.isSuitBlock(): + blocks.append(blockNumber) + return blocks + + def getEstablishedSuitBlocks(self): + blocks = [] + for blockNumber, building in self.__buildings.items(): + if building.isEstablishedSuitBlock(): + blocks.append(blockNumber) + return blocks + + def getToonBlocks(self): + blocks = [] + for blockNumber, building in self.__buildings.items(): + if isinstance(building, HQBuildingAI.HQBuildingAI): + continue + if isinstance(building, GagshopBuildingAI.GagshopBuildingAI): + continue + if isinstance(building, PetshopBuildingAI.PetshopBuildingAI): + continue + if isinstance(building, KartShopBuildingAI.KartShopBuildingAI): + continue + if not building.isSuitBlock(): + blocks.append(blockNumber) + return blocks + + def getBuildings(self): + return self.__buildings.values() + + def getFrontDoorPoint(self, blockNumber): + if self.isValidBlockNumber(blockNumber): + return self.__buildings[blockNumber].getFrontDoorPoint() + + def getBuildingTrack(self, blockNumber): + if self.isValidBlockNumber(blockNumber): + return self.__buildings[blockNumber].track + + def getBuilding(self, blockNumber): + if self.isValidBlockNumber(blockNumber): + return self.__buildings[blockNumber] + + def setFrontDoorPoint(self, blockNumber, point): + if self.isValidBlockNumber(blockNumber): + return self.__buildings[blockNumber].setFrontDoorPoint(point) + + def getDNABlockLists(self): + blocks = [] + hqBlocks = [] + gagshopBlocks = [] + petshopBlocks = [] + kartshopBlocks = [] + for i in xrange(self.dnaStore.getNumBlockNumbers()): + blockNumber = self.dnaStore.getBlockNumberAt(i) + buildingType = self.dnaStore.getBlockBuildingType(blockNumber) + if buildingType == 'hq': + hqBlocks.append(blockNumber) + elif buildingType == 'gagshop': + gagshopBlocks.append(blockNumber) + elif buildingType == 'petshop': + if self.air.wantPets: + petshopBlocks.append(blockNumber) + elif buildingType == 'kartshop': + kartshopBlocks.append(blockNumber) + else: + blocks.append(blockNumber) + return (blocks, hqBlocks, gagshopBlocks, petshopBlocks, kartshopBlocks) + + def findAllLandmarkBuildings(self): + buildings = self.load() + (blocks, hqBlocks, gagshopBlocks, petshopBlocks, kartshopBlocks) = self.getDNABlockLists() + for blockNumber in blocks: + self.newBuilding(blockNumber, buildings.get(blockNumber, None)) + for blockNumber in hqBlocks: + self.newHQBuilding(blockNumber) + for blockNumber in gagshopBlocks: + self.newGagshopBuilding(blockNumber) + for block in petshopBlocks: + self.newPetshopBuilding(block) + for block in kartshopBlocks: + self.newKartShopBuilding(block) + + def newBuilding(self, blockNumber, blockData = None): + building = DistributedBuildingAI.DistributedBuildingAI(self.air, blockNumber, self.branchId, self.trophyMgr) + building.generateWithRequired(self.branchId) + if blockData: + building.track = blockData.get('track', 'c') + building.realTrack = blockData.get('track', 'c') + building.difficulty = int(blockData.get('difficulty', 1)) + building.numFloors = int(blockData.get('numFloors', 1)) + building.numFloors = max(1, min(5, building.numFloors)) + building.becameSuitTime = blockData.get('becameSuitTime', time.time()) + if blockData['state'] == 'suit': + building.setState('suit') + elif blockData['state'] == 'cogdo': + if simbase.air.wantCogdominiums: + building.setState('cogdo') + else: + building.setState('toon') + else: + building.setState('toon') + self.__buildings[blockNumber] = building + return building + + def newHQBuilding(self, blockNumber): + dnaStore = self.air.dnaStoreMap[self.canonicalBranchId] + exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber) + interiorZoneId = (self.branchId - (self.branchId%100)) + 500 + blockNumber + building = HQBuildingAI.HQBuildingAI( + self.air, exteriorZoneId, interiorZoneId, blockNumber) + self.__buildings[blockNumber] = building + return building + + def newGagshopBuilding(self, blockNumber): + dnaStore = self.air.dnaStoreMap[self.canonicalBranchId] + exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber) + interiorZoneId = (self.branchId - (self.branchId%100)) + 500 + blockNumber + building = GagshopBuildingAI.GagshopBuildingAI( + self.air, exteriorZoneId, interiorZoneId, blockNumber) + self.__buildings[blockNumber] = building + return building + + def newPetshopBuilding(self, blockNumber): + dnaStore = self.air.dnaStoreMap[self.canonicalBranchId] + exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber) + interiorZoneId = (self.branchId - (self.branchId%100)) + 500 + blockNumber + building = PetshopBuildingAI.PetshopBuildingAI( + self.air, exteriorZoneId, interiorZoneId, blockNumber) + self.__buildings[blockNumber] = building + return building + + def newKartShopBuilding(self, blockNumber): + dnaStore = self.air.dnaStoreMap[self.canonicalBranchId] + exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber) + interiorZoneId = (self.branchId - (self.branchId%100)) + 500 + blockNumber + building = KartShopBuildingAI.KartShopBuildingAI( + self.air, exteriorZoneId, interiorZoneId, blockNumber) + self.__buildings[blockNumber] = building + return building + + def save(self): + if self.air.dbConn: + buildings = [] + for i in self.__buildings.values(): + if isinstance(i, HQBuildingAI.HQBuildingAI): + continue + buildings.append(i.getPickleData()) + + street = {'ai': self.air.districtId, 'branch': self.branchId} + try: + self.air.dbGlobalCursor.streets.update(street, + {'$setOnInsert': street, + '$set': {'buildings': buildings}}, + upsert=True) + except: # Something happened to our DB, but we can reconnect and retry. + taskMgr.doMethodLater(config.GetInt('mongodb-retry-time', 2), self.save, 'retrySave', extraArgs=[]) + + else: + self.saveDev() + + def saveDev(self): + backups = {} + for blockNumber in self.getSuitBlocks(): + building = self.getBuilding(blockNumber) + backups[blockNumber] = building.getPickleData() + simbase.backups.save('block-info', (self.air.districtId, self.branchId), backups) + + def load(self): + if self.air.dbConn: + blocks = {} + + # Ensure that toontown.streets is indexed. Doing this at loading time + # is a fine way to make sure that we won't upset players with a + # lagspike while we wait for the backend to handle the index request. + self.air.dbGlobalCursor.streets.ensure_index([('ai', 1), + ('branch', 1)]) + + street = {'ai': self.air.districtId, 'branch': self.branchId} + try: + doc = self.air.dbGlobalCursor.streets.find_one(street) + except: # We're failing over - normally we'd wait to retry, but this is on AI startup so we might want to retry (or refactor the bldgMgr so we can sanely retry). + return blocks + + if not doc: + return blocks + + for building in doc.get('buildings', []): + blocks[int(building['block'])] = building + + return blocks + + else: + blocks = simbase.backups.load('block-info', (self.air.districtId, self.branchId), default={}) + return blocks + + + diff --git a/toontown/building/DistributedBuildingQueryMgr.py b/toontown/building/DistributedBuildingQueryMgr.py new file mode 100755 index 00000000..c38502e8 --- /dev/null +++ b/toontown/building/DistributedBuildingQueryMgr.py @@ -0,0 +1,31 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.distributed import DistributedObject + + +class DistributedBuildingQueryMgr(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedBuildingQueryMgr') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.__callbacks = {} + self.__context = 0 + self.cr = cr + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedObject.DistributedObject.announceGenerate(self) + self.cr.buildingQueryMgr = self + + def delete(self): + self.notify.debug('delete') + DistributedObject.DistributedObject.delete(self) + self.cr.buildingQueryMgr = None + + def d_isSuit(self, zoneId, callback): + self.__context = (self.__context + 1) % 255 + self.__callbacks[self.__context] = callback + self.sendUpdate('isSuit', [self.__context, zoneId]) + + def response(self, context, flag): + self.__callbacks.pop(context, lambda x: 0)(flag) diff --git a/toontown/building/DistributedBuildingQueryMgrAI.py b/toontown/building/DistributedBuildingQueryMgrAI.py new file mode 100755 index 00000000..15be996c --- /dev/null +++ b/toontown/building/DistributedBuildingQueryMgrAI.py @@ -0,0 +1,17 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.distributed import DistributedObjectAI + + +class DistributedBuildingQueryMgrAI(DistributedObjectAI.DistributedObjectAI): + notify = directNotify.newCategory('DistributedBuildingQueryMgrAI') + + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.buildings = {} + + def isSuit(self, context, zoneId): + avId = self.air.getAvatarIdFromSender() + if zoneId not in self.buildings: + self.sendUpdateToAvatarId(avId, 'response', [context, False]) + else: + self.sendUpdateToAvatarId(avId, 'response', [context, self.buildings[zoneId].isSuitBlock()]) diff --git a/toontown/building/DistributedCFOElevator.py b/toontown/building/DistributedCFOElevator.py new file mode 100755 index 00000000..a0c15366 --- /dev/null +++ b/toontown/building/DistributedCFOElevator.py @@ -0,0 +1,24 @@ +import DistributedElevator +import DistributedBossElevator +from ElevatorConstants import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer + +class DistributedCFOElevator(DistributedBossElevator.DistributedBossElevator): + + def __init__(self, cr): + DistributedBossElevator.DistributedBossElevator.__init__(self, cr) + self.type = ELEVATOR_CFO + self.countdownTime = ElevatorData[self.type]['countdown'] + + def setupElevator(self): + self.elevatorModel = loader.loadModel('phase_10/models/cogHQ/CFOElevator') + self.leftDoor = self.elevatorModel.find('**/left_door') + self.rightDoor = self.elevatorModel.find('**/right_door') + geom = base.cr.playGame.hood.loader.geom + locator = geom.find('**/elevator_locator') + self.elevatorModel.reparentTo(locator) + DistributedElevator.DistributedElevator.setupElevator(self) + + def getDestName(self): + return TTLocalizer.ElevatorCashBotBoss diff --git a/toontown/building/DistributedCFOElevatorAI.py b/toontown/building/DistributedCFOElevatorAI.py new file mode 100755 index 00000000..9d93acba --- /dev/null +++ b/toontown/building/DistributedCFOElevatorAI.py @@ -0,0 +1,9 @@ +from ElevatorConstants import * +import DistributedBossElevatorAI + +class DistributedCFOElevatorAI(DistributedBossElevatorAI.DistributedBossElevatorAI): + + def __init__(self, air, bldg, zone): + DistributedBossElevatorAI.DistributedBossElevatorAI.__init__(self, air, bldg, zone) + self.type = ELEVATOR_CFO + self.countdownTime = ElevatorData[self.type]['countdown'] diff --git a/toontown/building/DistributedCJElevator.py b/toontown/building/DistributedCJElevator.py new file mode 100755 index 00000000..26fb9b86 --- /dev/null +++ b/toontown/building/DistributedCJElevator.py @@ -0,0 +1,28 @@ +import DistributedElevator +import DistributedBossElevator +from ElevatorConstants import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer + +class DistributedCJElevator(DistributedBossElevator.DistributedBossElevator): + + def __init__(self, cr): + DistributedBossElevator.DistributedBossElevator.__init__(self, cr) + self.type = ELEVATOR_CJ + self.countdownTime = ElevatorData[self.type]['countdown'] + + def setupElevator(self): + self.elevatorModel = loader.loadModel('phase_11/models/lawbotHQ/LB_Elevator') + self.leftDoor = self.elevatorModel.find('**/left-door') + if self.leftDoor.isEmpty(): + self.leftDoor = self.elevatorModel.find('**/left_door') + self.rightDoor = self.elevatorModel.find('**/right-door') + if self.rightDoor.isEmpty(): + self.rightDoor = self.elevatorModel.find('**/right_door') + geom = base.cr.playGame.hood.loader.geom + locator = geom.find('**/elevator_locator') + self.elevatorModel.reparentTo(locator) + DistributedElevator.DistributedElevator.setupElevator(self) + + def getDestName(self): + return TTLocalizer.ElevatorLawBotBoss diff --git a/toontown/building/DistributedCJElevatorAI.py b/toontown/building/DistributedCJElevatorAI.py new file mode 100755 index 00000000..ce3e11fb --- /dev/null +++ b/toontown/building/DistributedCJElevatorAI.py @@ -0,0 +1,9 @@ +from ElevatorConstants import * +import DistributedBossElevatorAI + +class DistributedCJElevatorAI(DistributedBossElevatorAI.DistributedBossElevatorAI): + + def __init__(self, air, bldg, zone): + DistributedBossElevatorAI.DistributedBossElevatorAI.__init__(self, air, bldg, zone) + self.type = ELEVATOR_CJ + self.countdownTime = ElevatorData[self.type]['countdown'] diff --git a/toontown/building/DistributedClubElevator.py b/toontown/building/DistributedClubElevator.py new file mode 100755 index 00000000..7b1cf4e6 --- /dev/null +++ b/toontown/building/DistributedClubElevator.py @@ -0,0 +1,530 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from toontown.building import ElevatorConstants +from toontown.building import ElevatorUtils +from toontown.building import DistributedElevatorFSM +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from direct.fsm.FSM import FSM +from direct.task import Task +from toontown.distributed import DelayDelete +from direct.showbase import PythonUtil + +class DistributedClubElevator(DistributedElevatorFSM.DistributedElevatorFSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedClubElevator') + JumpOutOffsets = ((3, 5, 0), + (1.5, 4, 0), + (-1.5, 4, 0), + (-3, 4, 0)) + defaultTransitions = {'Off': ['Opening', 'Closed'], + 'Opening': ['WaitEmpty', + 'WaitCountdown', + 'Opening', + 'Closing'], + 'WaitEmpty': ['WaitCountdown', 'Closing', 'Off'], + 'WaitCountdown': ['WaitEmpty', + 'AllAboard', + 'Closing', + 'WaitCountdown'], + 'AllAboard': ['WaitEmpty', 'Closing'], + 'Closing': ['Closed', + 'WaitEmpty', + 'Closing', + 'Opening'], + 'Closed': ['Opening']} + id = 0 + + def __init__(self, cr): + DistributedElevatorFSM.DistributedElevatorFSM.__init__(self, cr) + FSM.__init__(self, 'ElevatorClub_%s_FSM' % self.id) + self.type = ElevatorConstants.ELEVATOR_COUNTRY_CLUB + self.countdownTime = ElevatorConstants.ElevatorData[self.type]['countdown'] + self.nametag = None + self.currentFloor = -1 + self.isLocked = 0 + self.isEntering = 0 + self.doorOpeningFlag = 0 + self.doorsNeedToClose = 0 + self.wantState = 0 + self.latch = None + self.lastState = self.state + self.kartModelPath = 'phase_12/models/bossbotHQ/Coggolf_cart3.bam' + self.leftDoor = None + self.rightDoor = None + self.__toonTracks = {} + return + + def setupElevator(self): + self.elevatorModel = loader.loadModel('phase_11/models/lawbotHQ/LB_ElevatorScaled') + if not self.elevatorModel: + self.notify.error('No Elevator Model in DistributedElevatorFloor.setupElevator. Please inform JML. Fool!') + self.leftDoor = self.elevatorModel.find('**/left-door') + if self.leftDoor.isEmpty(): + self.leftDoor = self.elevatorModel.find('**/left_door') + self.rightDoor = self.elevatorModel.find('**/right-door') + if self.rightDoor.isEmpty(): + self.rightDoor = self.elevatorModel.find('**/right_door') + DistributedElevatorFSM.DistributedElevatorFSM.setupElevator(self) + + def generate(self): + DistributedElevatorFSM.DistributedElevatorFSM.generate(self) + self.loader = self.cr.playGame.hood.loader + self.golfKart = render.attachNewNode('golfKartNode') + self.kart = loader.loadModel(self.kartModelPath) + self.kart.setPos(0, 0, 0) + self.kart.setScale(1) + self.kart.reparentTo(self.golfKart) + self.wheels = self.kart.findAllMatches('**/wheelNode*') + self.numWheels = self.wheels.getNumPaths() + self.setPosHpr(0, 0, 0, 0, 0, 0) + + def announceGenerate(self): + DistributedElevatorFSM.DistributedElevatorFSM.announceGenerate(self) + if self.latch: + self.notify.info('Setting latch in announce generate') + self.setLatch(self.latch) + angle = self.startingHpr[0] + angle -= 90 + radAngle = deg2Rad(angle) + unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0) + unitVec *= 45.0 + self.endPos = self.startingPos + unitVec + self.endPos.setZ(0.5) + dist = Vec3(self.endPos - self.enteringPos).length() + wheelAngle = dist / (4.8 * 1.4 * math.pi) * 360 + self.kartEnterAnimateInterval = Parallel(LerpHprInterval(self.wheels[0], 5.0, Vec3(self.wheels[0].getH(), wheelAngle, self.wheels[0].getR())), LerpHprInterval(self.wheels[1], 5.0, Vec3(self.wheels[1].getH(), wheelAngle, self.wheels[1].getR())), LerpHprInterval(self.wheels[2], 5.0, Vec3(self.wheels[2].getH(), wheelAngle, self.wheels[2].getR())), LerpHprInterval(self.wheels[3], 5.0, Vec3(self.wheels[3].getH(), wheelAngle, self.wheels[3].getR())), name='CogKartAnimate') + trolleyExitTrack1 = Parallel(LerpPosInterval(self.golfKart, 5.0, self.endPos), self.kartEnterAnimateInterval, name='CogKartExitTrack') + self.trolleyExitTrack = Sequence(trolleyExitTrack1) + self.trolleyEnterTrack = Sequence(LerpPosInterval(self.golfKart, 5.0, self.startingPos, startPos=self.enteringPos)) + self.closeDoors = Sequence(self.trolleyExitTrack, Func(self.onDoorCloseFinish)) + self.openDoors = Sequence(self.trolleyEnterTrack) + self.setPos(0, 0, 0) + + def __placeElevator(self): + self.notify.debug('PLACING ELEVATOR FOOL!!') + if self.isEntering: + elevatorNode = render.find('**/elevator_origin') + if not elevatorNode.isEmpty(): + self.elevatorModel.setPos(0, 0, 0) + self.elevatorModel.reparentTo(elevatorNode) + else: + self.notify.debug('NO NODE elevator_origin!!') + else: + elevatorNode = render.find('**/SlidingDoor') + if not elevatorNode.isEmpty(): + self.elevatorModel.setPos(0, 10, -15) + self.elevatorModel.setH(180) + self.elevatorModel.reparentTo(elevatorNode) + else: + self.notify.debug('NO NODE SlidingDoor!!') + + def setLatch(self, markerId): + self.notify.info('Setting latch') + marker = self.cr.doId2do.get(markerId) + self.latchRequest = self.cr.relatedObjectMgr.requestObjects([markerId], allCallback=self.set2Latch, timeout=5) + self.latch = markerId + + def set2Latch(self, taskMgrFooler = None): + self.latchRequest = None + if hasattr(self, 'cr'): + marker = self.cr.doId2do.get(self.latch) + if marker: + self.getElevatorModel().reparentTo(marker) + return + taskMgr.doMethodLater(10.0, self._repart2Marker, 'elevatorfloor-markerReparent') + self.notify.warning('Using backup, do method later version of latch') + return + + def _repart2Marker(self, taskFoolio = 0): + if hasattr(self, 'cr') and self.cr: + marker = self.cr.doId2do.get(self.latch) + if marker: + self.getElevatorModel().reparentTo(marker) + else: + self.notify.error('could not find latch even in defered try') + + def setPos(self, x, y, z): + self.getElevatorModel().setPos(x, y, z) + + def setH(self, H): + self.getElevatorModel().setH(H) + + def delete(self): + self.request('Off') + DistributedElevatorFSM.DistributedElevatorFSM.delete(self) + self.getElevatorModel().removeNode() + del self.golfKart + self.ignore('LawOffice_Spec_Loaded') + self.ignoreAll() + + def disable(self): + self.request('Off') + self.clearToonTracks() + DistributedElevatorFSM.DistributedElevatorFSM.disable(self) + + def setEntranceId(self, entranceId): + self.entranceId = entranceId + if self.entranceId == 0: + self.elevatorModel.setPosHpr(62.74, -85.31, 0.0, 2.0, 0.0, 0.0) + elif self.entranceId == 1: + self.elevatorModel.setPosHpr(-162.25, 26.43, 0.0, 269.0, 0.0, 0.0) + else: + self.notify.error('Invalid entranceId: %s' % entranceId) + + def gotBldg(self, buildingList): + pass + + def setFloor(self, floorNumber): + if self.currentFloor >= 0: + if self.bldg.floorIndicator[self.currentFloor]: + self.bldg.floorIndicator[self.currentFloor].setColor(LIGHT_OFF_COLOR) + if floorNumber >= 0: + if self.bldg.floorIndicator[floorNumber]: + self.bldg.floorIndicator[floorNumber].setColor(LIGHT_ON_COLOR) + self.currentFloor = floorNumber + + def handleEnterSphere(self, collEntry): + self.cr.playGame.getPlace().detectedElevatorCollision(self) + + def handleEnterElevator(self): + if base.localAvatar.hp > 0: + toon = base.localAvatar + self.sendUpdate('requestBoard', []) + else: + self.notify.warning('Tried to board elevator with hp: %d' % base.localAvatar.hp) + + def enterWaitEmpty(self, ts): + self.lastState = self.state + self.elevatorSphereNodePath.unstash() + self.forceDoorsOpen() + self.accept(self.uniqueName('enterelevatorSphere'), self.handleEnterSphere) + self.accept(self.uniqueName('enterElevatorOK'), self.handleEnterElevator) + DistributedElevatorFSM.DistributedElevatorFSM.enterWaitEmpty(self, ts) + + def exitWaitEmpty(self): + self.lastState = self.state + self.elevatorSphereNodePath.stash() + self.ignore(self.uniqueName('enterelevatorSphere')) + self.ignore(self.uniqueName('enterElevatorOK')) + DistributedElevatorFSM.DistributedElevatorFSM.exitWaitEmpty(self) + + def enterWaitCountdown(self, ts): + self.lastState = self.state + DistributedElevatorFSM.DistributedElevatorFSM.enterWaitCountdown(self, ts) + self.forceDoorsOpen() + self.accept(self.uniqueName('enterElevatorOK'), self.handleEnterElevator) + self.startCountdownClock(self.countdownTime, ts) + + def exitWaitCountdown(self): + self.lastState = self.state + self.ignore(self.uniqueName('enterElevatorOK')) + DistributedElevatorFSM.DistributedElevatorFSM.exitWaitCountdown(self) + + def enterClosing(self, ts): + self.lastState = self.state + taskMgr.doMethodLater(1.0, self._delayIris, 'delayedIris') + DistributedElevatorFSM.DistributedElevatorFSM.enterClosing(self, ts) + + def _delayIris(self, tskfooler = 0): + base.transitions.irisOut(1.0) + base.localAvatar.pauseGlitchKiller() + return Task.done + + def kickToonsOut(self): + if not self.localToonOnBoard: + zoneId = self.cr.playGame.hood.hoodId + self.cr.playGame.getPlace().fsm.request('teleportOut', [{'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1}]) + return + + def exitClosing(self): + self.lastState = self.state + DistributedElevatorFSM.DistributedElevatorFSM.exitClosing(self) + + def enterClosed(self, ts): + self.lastState = self.state + self.forceDoorsClosed() + self.__doorsClosed(self.getZoneId()) + + def exitClosed(self): + self.lastState = self.state + DistributedElevatorFSM.DistributedElevatorFSM.exitClosed(self) + + def enterOff(self): + self.lastState = self.state + if self.wantState == 'closed': + self.demand('Closing') + elif self.wantState == 'waitEmpty': + self.demand('WaitEmpty') + DistributedElevatorFSM.DistributedElevatorFSM.enterOff(self) + + def exitOff(self): + self.lastState = self.state + DistributedElevatorFSM.DistributedElevatorFSM.exitOff(self) + + def enterOpening(self, ts): + self.lastState = self.state + DistributedElevatorFSM.DistributedElevatorFSM.enterOpening(self, ts) + + def exitOpening(self): + DistributedElevatorFSM.DistributedElevatorFSM.exitOpening(self) + self.kickEveryoneOut() + + def getZoneId(self): + return 0 + + def setBldgDoId(self, bldgDoId): + self.bldg = None + self.setupElevatorKart() + return + + def setupElevatorKart(self): + collisionRadius = ElevatorConstants.ElevatorData[self.type]['collRadius'] + self.elevatorSphere = CollisionSphere(0, 0, 0, collisionRadius) + self.elevatorSphere.setTangible(1) + self.elevatorSphereNode = CollisionNode(self.uniqueName('elevatorSphere')) + self.elevatorSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.elevatorSphereNode.addSolid(self.elevatorSphere) + self.elevatorSphereNodePath = self.getElevatorModel().attachNewNode(self.elevatorSphereNode) + self.elevatorSphereNodePath.hide() + self.elevatorSphereNodePath.reparentTo(self.getElevatorModel()) + self.elevatorSphereNodePath.stash() + self.boardedAvIds = {} + self.finishSetup() + + def getElevatorModel(self): + return self.elevatorModel + + def kickEveryoneOut(self): + for avId, slot in self.boardedAvIds.items(): + self.emptySlot(slot, avId, globalClockDelta.getRealNetworkTime()) + if avId == base.localAvatar.doId: + pass + + def __doorsClosed(self, zoneId): + pass + + def onDoorCloseFinish(self): + pass + + def setLocked(self, locked): + self.isLocked = locked + if locked: + if self.state == 'WaitEmpty': + self.request('Closing') + if self.countFullSeats() == 0: + self.wantState = 'closed' + else: + self.wantState = 'opening' + else: + self.wantState = 'waitEmpty' + if self.state == 'Closed': + self.request('Opening') + + def getLocked(self): + return self.isLocked + + def setEntering(self, entering): + self.isEntering = entering + + def getEntering(self): + return self.isEntering + + def forceDoorsOpen(self): + pass + + def forceDoorsClosed(self): + pass + + def enterOff(self): + self.lastState = self.state + + def exitOff(self): + pass + + def setLawOfficeInteriorZone(self, zoneId): + if self.localToonOnBoard: + hoodId = self.cr.playGame.hood.hoodId + doneStatus = {'loader': 'cogHQLoader', + 'where': 'factoryInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'hoodId': hoodId} + self.cr.playGame.getPlace().elevator.signalDone(doneStatus) + + def getElevatorModel(self): + return self.golfKart + + def setPosHpr(self, x, y, z, h, p, r): + self.startingPos = Vec3(x, y, z) + self.enteringPos = Vec3(x, y, z - 10) + self.startingHpr = Vec3(h, 0, 0) + self.golfKart.setPosHpr(x, y, z, h, 0, 0) + + def fillSlot(self, index, avId): + self.notify.debug('%s.fillSlot(%s, %s, ...)' % (self.doId, index, avId)) + request = self.toonRequests.get(index) + if request: + self.cr.relatedObjectMgr.abortRequest(request) + del self.toonRequests[index] + if avId == 0: + pass + elif avId not in self.cr.doId2do: + func = PythonUtil.Functor(self.gotToon, index, avId) + self.toonRequests[index] = self.cr.relatedObjectMgr.requestObjects([avId], allCallback=func) + elif not self.isSetup: + self.deferredSlots.append((index, avId)) + else: + if avId == base.localAvatar.getDoId(): + self.localToonOnBoard = 1 + elevator = self.getPlaceElevator() + elevator.fsm.request('boarding', [self.getElevatorModel()]) + elevator.fsm.request('boarded') + toon = self.cr.doId2do[avId] + toon.stopSmooth() + toon.wrtReparentTo(self.golfKart) + sitStartDuration = toon.getDuration('sit-start') + jumpTrack = self.generateToonJumpTrack(toon, index) + track = Sequence(jumpTrack, Func(toon.setAnimState, 'Sit', 1.0), Func(self.clearToonTrack, avId), name=toon.uniqueName('fillElevator'), autoPause=1) + track.delayDelete = DelayDelete.DelayDelete(toon, 'fillSlot') + self.storeToonTrack(avId, track) + track.start() + self.boardedAvIds[avId] = None + return + + def generateToonJumpTrack(self, av, seatIndex): + av.pose('sit', 47) + hipOffset = av.getHipsParts()[2].getPos(av) + + def getToonJumpTrack(av, seatIndex): + + def getJumpDest(av = av, node = self.golfKart): + dest = Point3(0, 0, 0) + if hasattr(self, 'golfKart') and self.golfKart: + dest = Vec3(self.golfKart.getPos(av.getParent())) + seatNode = self.golfKart.find('**/seat' + str(seatIndex + 1)) + dest += seatNode.getPos(self.golfKart) + dna = av.getStyle() + dest -= hipOffset + if seatIndex < 2: + dest.setY(dest.getY() + 2 * hipOffset.getY()) + dest.setZ(dest.getZ() + 0.1) + else: + self.notify.warning('getJumpDestinvalid golfKart, returning (0,0,0)') + return dest + + def getJumpHpr(av = av, node = self.golfKart): + hpr = Point3(0, 0, 0) + if hasattr(self, 'golfKart') and self.golfKart: + hpr = self.golfKart.getHpr(av.getParent()) + if seatIndex < 2: + hpr.setX(hpr.getX() + 180) + else: + hpr.setX(hpr.getX()) + angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) + hpr.setX(angle) + else: + self.notify.warning('getJumpHpr invalid golfKart, returning (0,0,0)') + return hpr + + toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.43), Parallel(LerpHprInterval(av, hpr=getJumpHpr, duration=0.9), ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) + return toonJumpTrack + + def getToonSitTrack(av): + toonSitTrack = Sequence(ActorInterval(av, 'sit-start'), Func(av.loop, 'sit')) + return toonSitTrack + + toonJumpTrack = getToonJumpTrack(av, seatIndex) + toonSitTrack = getToonSitTrack(av) + jumpTrack = Sequence(Parallel(toonJumpTrack, Sequence(Wait(1), toonSitTrack)), Func(av.wrtReparentTo, self.golfKart)) + return jumpTrack + + def emptySlot(self, index, avId, timestamp, timeSent=0): + if avId == 0: + pass + elif not self.isSetup: + newSlots = [] + for slot in self.deferredSlots: + if slot[0] != index: + newSlots.append(slot) + + self.deferredSlots = newSlots + elif avId in self.cr.doId2do: + if hasattr(self, 'clockNode'): + if timestamp < self.countdownTime and timestamp >= 0: + self.countdown(self.countdownTime - timestamp) + else: + self.countdown(self.countdownTime) + toon = self.cr.doId2do[avId] + toon.stopSmooth() + sitStartDuration = toon.getDuration('sit-start') + jumpOutTrack = self.generateToonReverseJumpTrack(toon, index) + track = Sequence(jumpOutTrack, Func(self.clearToonTrack, avId), Func(self.notifyToonOffElevator, toon), name=toon.uniqueName('emptyElevator'), autoPause=1) + track.delayDelete = DelayDelete.DelayDelete(toon, 'ClubElevator.emptySlot') + self.storeToonTrack(avId, track) + track.start() + if avId == base.localAvatar.getDoId(): + messenger.send('exitElevator') + if avId in self.boardedAvIds: + del self.boardedAvIds[avId] + else: + self.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot exit the elevator!') + + def generateToonReverseJumpTrack(self, av, seatIndex): + self.notify.debug('av.getH() = %s' % av.getH()) + + def getToonJumpTrack(av, destNode): + + def getJumpDest(av = av, node = destNode): + dest = node.getPos(av.getParent()) + dest += Vec3(*self.JumpOutOffsets[seatIndex]) + return dest + + def getJumpHpr(av = av, node = destNode): + hpr = node.getHpr(av.getParent()) + hpr.setX(hpr.getX() + 180) + angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) + hpr.setX(angle) + return hpr + + toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.1), Parallel(ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) + return toonJumpTrack + + toonJumpTrack = getToonJumpTrack(av, self.golfKart) + jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render)) + return jumpTrack + + def startCountdownClock(self, countdownTime, ts): + DistributedElevatorFSM.DistributedElevatorFSM.startCountdownClock(self, countdownTime, ts) + self.clock.setH(self.clock.getH() + 180) + + def storeToonTrack(self, avId, track): + self.clearToonTrack(avId) + self.__toonTracks[avId] = track + + def clearToonTrack(self, avId): + oldTrack = self.__toonTracks.get(avId) + if oldTrack: + oldTrack.pause() + if self.__toonTracks.get(avId): + DelayDelete.cleanupDelayDeletes(self.__toonTracks[avId]) + del self.__toonTracks[avId] + + def clearToonTracks(self): + keyList = [] + for key in self.__toonTracks: + keyList.append(key) + + for key in keyList: + if key in self.__toonTracks: + self.clearToonTrack(key) diff --git a/toontown/building/DistributedClubElevatorAI.py b/toontown/building/DistributedClubElevatorAI.py new file mode 100755 index 00000000..9157e2d0 --- /dev/null +++ b/toontown/building/DistributedClubElevatorAI.py @@ -0,0 +1,293 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.fsm.FSM import FSM +from direct.task import Task +from otp.ai.AIBase import * +from toontown.building import DistributedElevatorFSMAI +from toontown.building import ElevatorConstants +from toontown.toonbase import ToontownGlobals + + +class DistributedClubElevatorAI(DistributedElevatorFSMAI.DistributedElevatorFSMAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorFloorAI') + defaultTransitions = { + 'Off': [ + 'Opening', + 'Closed'], + 'Opening': [ + 'WaitEmpty', + 'WaitCountdown', + 'Opening', + 'Closing'], + 'WaitEmpty': [ + 'WaitCountdown', + 'Closing', + 'WaitEmpty'], + 'WaitCountdown': [ + 'WaitEmpty', + 'AllAboard', + 'Closing', + 'WaitCountdown'], + 'AllAboard': [ + 'WaitEmpty', + 'Closing'], + 'Closing': [ + 'Closed', + 'WaitEmpty', + 'Closing', + 'Opening'], + 'Closed': [ + 'Opening'] } + id = 0 + DoBlockedRoomCheck = simbase.config.GetBool('elevator-blocked-rooms-check', 1) + + def __init__(self, air, lawOfficeId, bldg, avIds, markerId = None, numSeats = 4): + DistributedElevatorFSMAI.DistributedElevatorFSMAI.__init__(self, air, bldg, numSeats) + FSM.__init__(self, 'ElevatorFloor_%s_FSM' % self.id) + self.type = ElevatorConstants.ELEVATOR_COUNTRY_CLUB + self.countdownTime = ElevatorConstants.ElevatorData[self.type]['countdown'] + self.lawOfficeId = lawOfficeId + self.avIds = avIds + self.isEntering = 0 + self.isLocked = 0 + self.setLocked(0) + self.wantState = None + self.latchRoom = None + self.setLatch(markerId) + self.zoneId = bldg.zoneId + + def generate(self): + DistributedElevatorFSMAI.DistributedElevatorFSMAI.generate(self) + + def generateWithRequired(self, zoneId): + self.zoneId = zoneId + DistributedElevatorFSMAI.DistributedElevatorFSMAI.generateWithRequired(self, self.zoneId) + + def delete(self): + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.clearFullNow(seatIndex) + self.clearEmptyNow(seatIndex) + DistributedElevatorFSMAI.DistributedElevatorFSMAI.delete(self) + + def getEntranceId(self): + return self.entranceId + + def d_setFloor(self, floorNumber): + self.sendUpdate('setFloor', [floorNumber]) + + def avIsOKToBoard(self, av): + if av.hp > 0 and self.accepting: + pass + return not (self.isLocked) + + def acceptBoarder(self, avId, seatIndex): + DistributedElevatorFSMAI.DistributedElevatorFSMAI.acceptBoarder(self, avId, seatIndex) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self._DistributedClubElevatorAI__handleUnexpectedExit, extraArgs = [ + avId]) + if self.state == 'WaitEmpty' and self.countFullSeats() < self.countAvsInZone(): + self.request('WaitCountdown') + self.bldg.elevatorAlert(avId) + elif self.state in ('WaitCountdown', 'WaitEmpty') and self.countFullSeats() >= self.countAvsInZone(): + taskMgr.doMethodLater(ElevatorConstants.TOON_BOARD_ELEVATOR_TIME, self.goAllAboard, self.quickBoardTask) + + def countAvsInZone(self): + matchingZones = 0 + for avId in self.bldg.avIds: + av = self.air.doId2do.get(avId) + if av: + if av.zoneId == self.bldg.zoneId: + matchingZones += 1 + return matchingZones + + def goAllAboard(self, throwAway = 1): + self.request('Closing') + return Task.done + + def _DistributedClubElevatorAI__handleUnexpectedExit(self, avId): + self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly') + seatIndex = self.findAvatar(avId) + if seatIndex == None: + pass + self.clearFullNow(seatIndex) + self.clearEmptyNow(seatIndex) + if self.countFullSeats() == 0: + self.request('WaitEmpty') + + def acceptExiter(self, avId): + seatIndex = self.findAvatar(avId) + if seatIndex == None: + pass + self.clearFullNow(seatIndex) + self.resetCountdown() + self.sendUpdate('emptySlot' + str(seatIndex), [avId, globalClockDelta.getRealNetworkTime()]) + if self.countFullSeats() == 0: + self.request('WaitEmpty') + taskMgr.doMethodLater(ElevatorConstants.TOON_EXIT_ELEVATOR_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs = (seatIndex,)) + + def enterOpening(self): + self.d_setState('Opening') + DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterOpening(self) + taskMgr.doMethodLater(ElevatorConstants.ElevatorData[ElevatorConstants.ELEVATOR_NORMAL]['openTime'], self.waitEmptyTask, self.uniqueName('opening-timer')) + + def exitOpening(self): + DistributedElevatorFSMAI.DistributedElevatorFSMAI.exitOpening(self) + if self.isLocked: + self.wantState = 'closed' + if self.wantState == 'closed': + self.demand('Closing') + + def waitEmptyTask(self, task): + self.request('WaitEmpty') + return Task.done + + def enterWaitEmpty(self): + self.lastState = self.state + for i in xrange(len(self.seats)): + self.seats[i] = None + print self.seats + if self.wantState == 'closed': + self.demand('Closing') + else: + self.d_setState('WaitEmpty') + self.accepting = 1 + + def enterWaitCountdown(self): + self.lastState = self.state + DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterWaitCountdown(self) + taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer')) + if self.lastState == 'WaitCountdown': + pass + + def timeToGoTask(self, task): + if self.countFullSeats() > 0: + self.request('AllAboard') + else: + self.request('WaitEmpty') + return Task.done + + def resetCountdown(self): + taskMgr.remove(self.uniqueName('countdown-timer')) + taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer')) + + def enterAllAboard(self): + DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterAllAboard(self) + currentTime = globalClock.getRealTime() + elapsedTime = currentTime - self.timeOfBoarding + self.notify.debug('elapsed time: ' + str(elapsedTime)) + waitTime = max(ElevatorConstants.TOON_BOARD_ELEVATOR_TIME - elapsedTime, 0) + taskMgr.doMethodLater(waitTime, self.closeTask, self.uniqueName('waitForAllAboard')) + + def closeTask(self, task): + if self.countFullSeats() >= 1: + self.request('Closing') + else: + self.request('WaitEmpty') + return Task.done + + def enterClosing(self): + if self.countFullSeats() > 0: + self.sendUpdate('kickToonsOut') + DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterClosing(self) + taskMgr.doMethodLater(ElevatorConstants.ElevatorData[ElevatorConstants.ELEVATOR_STAGE]['closeTime'], self.elevatorClosedTask, self.uniqueName('closing-timer')) + self.d_setState('Closing') + + def elevatorClosedTask(self, task): + self.elevatorClosed() + return Task.done + + def elevatorClosed(self): + if self.isLocked: + self.request('Closed') + return None + numPlayers = self.countFullSeats() + if numPlayers > 0: + players = [] + for i in self.seats: + if i not in [None, 0]: + players.append(i) + sittingAvIds = [] + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + sittingAvIds.append(avId) + for avId in self.avIds: + if avId not in sittingAvIds: + continue + self.bldg.startNextFloor() + else: + self.notify.warning('The elevator left, but was empty.') + self.request('Closed') + + def setLocked(self, locked): + self.isLocked = locked + if locked: + if self.state == 'WaitEmpty': + self.request('Closing') + + if self.countFullSeats() == 0: + self.wantState = 'closed' + else: + self.wantState = 'opening' + else: + self.wantState = 'waitEmpty' + if self.state == 'Closed': + self.request('Opening') + + def getLocked(self): + return self.isLocked + + def unlock(self): + if self.isLocked: + self.setLocked(0) + + def lock(self): + if not self.isLocked: + self.setLocked(1) + + def start(self): + self.quickBoardTask = self.uniqueName('quickBoard') + self.request('Opening') + + def beClosed(self): + pass + + def setEntering(self, entering): + self.isEntering = entering + + def getEntering(self): + return self.isEntering + + def enterClosed(self): + DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterClosed(self) + if self.wantState == 'closed': + pass + self.demand('Opening') + + def enterOff(self): + self.lastState = self.state + if self.wantState == 'closed': + self.demand('Closing') + elif self.wantState == 'waitEmpty': + self.demand('WaitEmpty') + + def setPos(self, pointPos): + self.sendUpdate('setPos', [pointPos[0], pointPos[1], pointPos[2]]) + + def setH(self, H): + self.sendUpdate('setH', [H]) + + + def setLatch(self, markerId): + self.latch = markerId + + def getLatch(self): + return self.latch + + def checkBoard(self, av): + if self.DoBlockedRoomCheck and self.bldg: + if hasattr(self.bldg, 'blockedRooms'): + if self.bldg.blockedRooms: + return ElevatorConstants.REJECT_BLOCKED_ROOM + return 0 diff --git a/toontown/building/DistributedDoor.py b/toontown/building/DistributedDoor.py new file mode 100755 index 00000000..4c8bfc7c --- /dev/null +++ b/toontown/building/DistributedDoor.py @@ -0,0 +1,655 @@ +import DoorTypes +import FADoorCodes +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.interval.IntervalGlobal import * +from direct.task.Task import Task +from panda3d.core import * +from toontown.distributed import DelayDelete +from toontown.distributed.DelayDeletable import DelayDeletable +from toontown.hood import ZoneUtil +from toontown.suit import Suit +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toontowngui import TTDialog +from otp.nametag.NametagGroup import NametagGroup +from otp.nametag.Nametag import Nametag + + +class DistributedDoor(DistributedObject.DistributedObject, DelayDeletable): + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.openSfx = base.loadSfx('phase_3.5/audio/sfx/Door_Open_1.ogg') + self.closeSfx = base.loadSfx('phase_3.5/audio/sfx/Door_Close_1.ogg') + self.nametag = None + self.fsm = ClassicFSM.ClassicFSM( + 'DistributedDoor_right', + [ + State.State('off', self.enterOff, self.exitOff, + ['closing', 'closed', 'opening', 'open']), + State.State('closing', self.enterClosing, self.exitClosing, + ['closed', 'opening']), + State.State('closed', self.enterClosed, self.exitClosed, + ['opening']), + State.State('opening', self.enterOpening, self.exitOpening, + ['open']), + State.State('open', self.enterOpen, self.exitOpen, + ['closing', 'open']) + ], 'off', 'off') + self.fsm.enterInitialState() + self.exitDoorFSM = ClassicFSM.ClassicFSM( + 'DistributedDoor_left', + [ + State.State('off', self.exitDoorEnterOff, self.exitDoorExitOff, + ['closing', 'closed', 'opening', 'open']), + State.State('closing', self.exitDoorEnterClosing, self.exitDoorExitClosing, + ['closed', 'opening']), + State.State('closed', self.exitDoorEnterClosed, self.exitDoorExitClosed, + ['opening']), + State.State('opening', self.exitDoorEnterOpening, self.exitDoorExitOpening, + ['open']), + State.State('open', self.exitDoorEnterOpen, self.exitDoorExitOpen, + ['closing', 'open']) + ], 'off', 'off') + self.exitDoorFSM.enterInitialState() + self.specialDoorTypes = { + DoorTypes.EXT_HQ: 0, + DoorTypes.EXT_COGHQ: 0, + DoorTypes.INT_COGHQ: 0, + DoorTypes.EXT_KS: 0, + DoorTypes.INT_KS: 0 } + self.doorX = 1.5 + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.avatarTracks = [] + self.avatarExitTracks = [] + self.avatarIDList = [] + self.avatarExitIDList = [] + self.doorTrack = None + self.doorExitTrack = None + + def disable(self): + self.clearNametag() + taskMgr.remove(self.checkIsDoorHitTaskName()) + self.ignore(self.getEnterTriggerEvent()) + self.ignore(self.getExitTriggerEvent()) + self.ignore('clearOutToonInterior') + self.fsm.request('off') + self.exitDoorFSM.request('off') + if hasattr(self, 'building'): + del self.building + self.finishAllTracks() + self.avatarIDList = [] + self.avatarExitIDList = [] + if hasattr(self, 'tempDoorNodePath'): + self.tempDoorNodePath.removeNode() + del self.tempDoorNodePath + DistributedObject.DistributedObject.disable(self) + + def delete(self): + del self.fsm + del self.exitDoorFSM + del self.openSfx + del self.closeSfx + DistributedObject.DistributedObject.delete(self) + + def wantsNametag(self): + return not ZoneUtil.isInterior(self.zoneId) + + def setupNametag(self): + if not self.wantsNametag(): + return + if self.doorIndex != 0: + return + if self.nametag == None: + self.nametag = NametagGroup() + self.nametag.setFont(ToontownGlobals.getBuildingNametagFont()) + if TTLocalizer.BuildingNametagShadow: + self.nametag.setShadow(*TTLocalizer.BuildingNametagShadow) + self.nametag.setContents(Nametag.CName) + self.nametag.setColorCode(NametagGroup.CCToonBuilding) + self.nametag.setActive(0) + self.nametag.setAvatar(self.getDoorNodePath()) + self.nametag.setObjectCode(self.block) + name = self.cr.playGame.dnaStore.getTitleFromBlockNumber(self.block) + self.nametag.setName(name) + self.nametag.manage(base.marginManager) + + def clearNametag(self): + if self.nametag is not None: + self.nametag.unmanage(base.marginManager) + self.nametag.setAvatar(NodePath()) + self.nametag.destroy() + self.nametag = None + + def getTriggerName(self): + if (self.doorType == DoorTypes.INT_HQ) or (self.doorType in self.specialDoorTypes): + return 'door_trigger_' + str(self.block) + '_' + str(self.doorIndex) + else: + return 'door_trigger_' + str(self.block) + + def getTriggerName_wip(self): + name = 'door_trigger_%d' % (self.doId,) + return name + + def getEnterTriggerEvent(self): + return 'enter' + self.getTriggerName() + + def getExitTriggerEvent(self): + return 'exit' + self.getTriggerName() + + def hideDoorParts(self): + try: + self.findDoorNode('doorFrameHoleRight').hide() + self.findDoorNode('doorFrameHoleLeft').hide() + except: + pass + if self.doorType in self.specialDoorTypes: + self.hideIfHasFlat(self.findDoorNode('rightDoor')) + self.hideIfHasFlat(self.findDoorNode('leftDoor')) + + def setTriggerName(self): + if self.doorType in self.specialDoorTypes or self.doorType == DoorTypes.INT_HQ: + building = self.getBuilding() + doorTrigger = building.find('**/door_' + str(self.doorIndex) + '/**/door_trigger*') + try: + doorTrigger.setName(self.getTriggerName()) + except: + pass + + def setTriggerName_wip(self): + building = self.getBuilding() + doorTrigger = building.find('**/door_%d/**/door_trigger_%d' % (self.doorIndex, self.block)) + if doorTrigger.isEmpty(): + doorTrigger = building.find('**/door_trigger_%d' % (self.block,)) + if doorTrigger.isEmpty(): + doorTrigger = building.find('**/door_%d/**/door_trigger_*' % (self.doorIndex,)) + if doorTrigger.isEmpty(): + doorTrigger = building.find('**/door_trigger_*') + doorTrigger.node().setName(self.getTriggerName()) + + def setZoneIdAndBlock(self, zoneId, block): + self.zoneId = zoneId + self.block = block + + def setDoorType(self, doorType): + self.notify.debug('Door type = ' + str(doorType) + ' on door #' + str(self.doId)) + self.doorType = doorType + + def setDoorIndex(self, doorIndex): + self.doorIndex = doorIndex + + def setSwing(self, flags): + self.leftSwing = flags & 1 != 0 + self.rightSwing = flags & 2 != 0 + + def setOtherZoneIdAndDoId(self, zoneId, distributedObjectID): + self.otherZoneId = zoneId + self.otherDoId = distributedObjectID + + def setState(self, state, timestamp): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def setExitDoorState(self, state, timestamp): + self.exitDoorFSM.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.doPostAnnounceGenerate() + + def doPostAnnounceGenerate(self): + flatDoorTypes = [DoorTypes.INT_STANDARD, DoorTypes.INT_HQ] + if self.doorType in flatDoorTypes: + self.bHasFlat = True + else: + self.bHasFlat = not self.findDoorNode('door*flat', True).isEmpty() + self.hideDoorParts() + self.setTriggerName() + + if self.doorType == DoorTypes.EXT_HQ and ZoneUtil.getHoodId(self.zoneId) == ToontownGlobals.DonaldsDreamland: + building = self.getBuilding() + doorTrigger = building.find('**/%s' % self.getTriggerName()) + + if not doorTrigger.getTag('fixed'): + doorTrigger.setY(doorTrigger, 0.25 if not self.doorIndex else -0.25) + doorTrigger.setTag('fixed', 'true') + + self.accept(self.getEnterTriggerEvent(), self.doorTrigger) + self.acceptOnce('clearOutToonInterior', self.doorTrigger) + self.setupNametag() + + def getBuilding(self): + if not hasattr(self, 'building') or not self.building: + if self.doorType == DoorTypes.INT_STANDARD: + door = render.find('**/*leftDoor*') + self.building = door.getParent() + elif self.doorType == DoorTypes.INT_HQ: + door = render.find('**/door_0') + self.building = door.getParent() + elif self.doorType == DoorTypes.INT_KS: + self.building = render.find('**/KartShop_Interior*') + elif self.doorType == DoorTypes.EXT_STANDARD or self.doorType == DoorTypes.EXT_HQ or self.doorType == DoorTypes.EXT_KS: + self.building = self.cr.playGame.hood.loader.geom.find('**/??' + str(self.block) + ':*_landmark_*_DNARoot;+s') + if self.building.isEmpty(): + self.building = self.cr.playGame.hood.loader.geom.find('**/??' + str(self.block) + ':animated_building_*_DNARoot;+s') + elif self.doorType == DoorTypes.EXT_COGHQ or self.doorType == DoorTypes.INT_COGHQ: + self.building = self.cr.playGame.hood.loader.geom + else: + self.notify.error('No such door type as ' + str(self.doorType)) + return self.building + + def getBuilding_wip(self): + if not hasattr(self, 'building'): + if hasattr(self, 'block'): + self.building = self.cr.playGame.hood.loader.geom.find('**/??' + str(self.block) + ':*_landmark_*_DNARoot;+s') + else: + self.building = self.cr.playGame.hood.loader.geom + return self.building + + def readyToExit(self): + base.transitions.fadeScreen(1.0) + self.sendUpdate('requestExit') + + def avatarEnterDoorTrack(self, avatar, duration): + trackName = 'avatarEnterDoor-%d-%d' % (self.doId, avatar.doId) + track = Parallel(name = trackName) + otherNP = self.getDoorNodePath() + if hasattr(avatar, 'stopSmooth'): + avatar.stopSmooth() + if avatar.doId == base.localAvatar.doId: + track.append(LerpPosHprInterval(nodePath = camera, other = avatar, duration = duration, pos = Point3(0, -8, avatar.getHeight()), hpr = VBase3(0, 0, 0), blendType = 'easeInOut')) + finalPos = avatar.getParent().getRelativePoint(otherNP, Point3(self.doorX, 2, ToontownGlobals.FloorOffset)) + moveHere = Sequence(self.getAnimStateInterval(avatar, 'walk'), LerpPosInterval(nodePath = avatar, duration = duration, pos = finalPos, blendType = 'easeIn')) + track.append(moveHere) + if avatar.doId == base.localAvatar.doId: + track.append(Sequence(Wait(duration * 0.5), Func(base.transitions.irisOut, duration * 0.5), Wait(duration * 0.5), Func(avatar.b_setParent, ToontownGlobals.SPHidden))) + track.delayDelete = DelayDelete.DelayDelete(avatar, 'avatarEnterDoorTrack') + return track + + def avatarEnqueueTrack(self, avatar, duration): + if hasattr(avatar, 'stopSmooth'): + avatar.stopSmooth() + back = -5.0 - 2.0 * len(self.avatarIDList) + if back < -9.0: + back = -9.0 + offset = Point3(self.doorX, back, ToontownGlobals.FloorOffset) + otherNP = self.getDoorNodePath() + walkLike = ActorInterval(avatar, 'walk', startTime = 1, duration = duration, endTime = 0.0001) + standHere = Sequence(LerpPosHprInterval(nodePath = avatar, other = otherNP, duration = duration, pos = offset, hpr = VBase3(0, 0, 0), blendType = 'easeInOut'), self.getAnimStateInterval(avatar, 'neutral')) + trackName = 'avatarEnqueueDoor-%d-%d' % (self.doId, avatar.doId) + track = Parallel(walkLike, standHere, name = trackName) + track.delayDelete = DelayDelete.DelayDelete(avatar, 'avatarEnqueueTrack') + return track + + def getAnimStateInterval(self, avatar, animName): + isSuit = isinstance(avatar, Suit.Suit) + if isSuit: + return Func(avatar.loop, animName, 0) + else: + return Func(avatar.setAnimState, animName) + + def isDoorHit(self): + vec = base.localAvatar.getRelativeVector(self.currentDoorNp, self.currentDoorVec) + netScale = self.currentDoorNp.getNetTransform().getScale() + yToTest = vec.getY() / netScale[1] + return yToTest + + def enterDoor(self): + messenger.send('DistributedDoor_doorTrigger') + self.sendUpdate('requestEnter') + + def checkIsDoorHitTaskName(self): + return 'checkIsDoorHit' + self.getTriggerName() + + def checkIsDoorHitTask(self, task): + if self.isDoorHit(): + self.ignore(self.checkIsDoorHitTaskName()) + self.ignore(self.getExitTriggerEvent()) + self.enterDoor() + return Task.done + return Task.cont + + def cancelCheckIsDoorHitTask(self, args): + taskMgr.remove(self.checkIsDoorHitTaskName()) + del self.currentDoorNp + del self.currentDoorVec + self.ignore(self.getExitTriggerEvent()) + self.accept(self.getEnterTriggerEvent(), self.doorTrigger) + + def doorTrigger(self, args=None): + self.ignore(self.getEnterTriggerEvent()) + if args == None: + self.enterDoor() + else: + self.currentDoorNp = NodePath(args.getIntoNodePath()) + self.currentDoorVec = Vec3(args.getSurfaceNormal(self.currentDoorNp)) + if self.isDoorHit(): + self.enterDoor() + else: + self.accept(self.getExitTriggerEvent(), self.cancelCheckIsDoorHitTask) + taskMgr.add(self.checkIsDoorHitTask, self.checkIsDoorHitTaskName()) + + def avatarEnter(self, avatarID): + avatar = self.cr.doId2do.get(avatarID, None) + if avatar: + avatar.setAnimState('neutral') + track = self.avatarEnqueueTrack(avatar, 0.5) + track.start() + self.avatarTracks.append(track) + self.avatarIDList.append(avatarID) + + def rejectEnter(self, reason): + message = FADoorCodes.reasonDict[reason] + if message: + self.__faRejectEnter(message) + else: + self.__basicRejectEnter() + + def __basicRejectEnter(self): + self.accept(self.getEnterTriggerEvent(), self.doorTrigger) + if self.cr.playGame.getPlace(): + self.cr.playGame.getPlace().setState('walk') + + def __faRejectEnter(self, message): + self.rejectDialog = TTDialog.TTGlobalDialog(message = message, doneEvent = 'doorRejectAck', style = TTDialog.Acknowledge) + self.rejectDialog.show() + self.rejectDialog.delayDelete = DelayDelete.DelayDelete(self, '__faRejectEnter') + event = 'clientCleanup' + self.acceptOnce(event, self.__handleClientCleanup) + base.cr.playGame.getPlace().setState('stopped') + self.acceptOnce('doorRejectAck', self.__handleRejectAck) + self.acceptOnce('stoppedAsleep', self.__handleFallAsleepDoor) + + def __handleClientCleanup(self): + if hasattr(self, 'rejectDialog') and self.rejectDialog: + self.rejectDialog.doneStatus = 'ok' + self.__handleRejectAck() + + def __handleFallAsleepDoor(self): + self.rejectDialog.doneStatus = 'ok' + self.__handleRejectAck() + + def __handleRejectAck(self): + self.ignore('doorRejectAck') + self.ignore('stoppedAsleep') + self.ignore('clientCleanup') + doneStatus = self.rejectDialog.doneStatus + if doneStatus != 'ok': + self.notify.error('Unrecognized doneStatus: ' + str(doneStatus)) + self.__basicRejectEnter() + self.rejectDialog.delayDelete.destroy() + self.rejectDialog.cleanup() + del self.rejectDialog + + def getDoorNodePath(self): + if self.doorType == DoorTypes.INT_STANDARD: + otherNP = render.find('**/door_origin') + elif self.doorType == DoorTypes.EXT_STANDARD: + otherNP = self.getBuilding().find('**/*door_origin') + elif self.doorType in self.specialDoorTypes: + building = self.getBuilding() + otherNP = building.find('**/door_origin_' + str(self.doorIndex)) + elif self.doorType == DoorTypes.INT_HQ: + otherNP = render.find('**/door_origin_' + str(self.doorIndex)) + else: + self.notify.error('No such door type as ' + str(self.doorType)) + return otherNP + + def avatarExitTrack(self, avatar, duration): + if hasattr(avatar, 'stopSmooth'): + avatar.stopSmooth() + otherNP = self.getDoorNodePath() + trackName = 'avatarExitDoor-%d-%d' % (self.doId, avatar.doId) + track = Sequence(name=trackName) + track.append(self.getAnimStateInterval(avatar, 'walk')) + track.append( + PosHprInterval( + avatar, Point3(-self.doorX, 0, ToontownGlobals.FloorOffset), + VBase3(179, 0, 0), other=otherNP + ) + ) + track.append(Func(avatar.setParent, ToontownGlobals.SPRender)) + if avatar.doId == base.localAvatar.doId: + track.append( + PosHprInterval( + camera, VBase3(-self.doorX, 5, avatar.getHeight()), + VBase3(180, 0, 0), other=otherNP + ) + ) + if avatar.doId == base.localAvatar.doId: + finalPos = render.getRelativePoint( + otherNP, Point3(-self.doorX, -6, ToontownGlobals.FloorOffset) + ) + else: + finalPos = render.getRelativePoint( + otherNP, Point3(-self.doorX, -3, ToontownGlobals.FloorOffset) + ) + track.append( + LerpPosInterval( + nodePath=avatar, duration=duration, pos=finalPos, + blendType='easeInOut' + ) + ) + if avatar.doId == base.localAvatar.doId: + track.append(Func(self.exitCompleted)) + track.append(Func(base.transitions.irisIn)) + if hasattr(avatar, 'startSmooth'): + track.append(Func(avatar.startSmooth)) + track.delayDelete = DelayDelete.DelayDelete(avatar, 'DistributedDoor.avatarExitTrack') + return track + + def exitCompleted(self): + base.localAvatar.setAnimState('neutral') + place = self.cr.playGame.getPlace() + if place: + place.setState('walk') + base.localAvatar.d_setParent(ToontownGlobals.SPRender) + + def avatarExit(self, avatarID): + if avatarID in self.avatarIDList: + self.avatarIDList.remove(avatarID) + if avatarID == base.localAvatar.doId: + self.exitCompleted() + else: + self.avatarExitIDList.append(avatarID) + + def finishDoorTrack(self): + if self.doorTrack: + self.doorTrack.finish() + self.doorTrack = None + + def finishDoorExitTrack(self): + if self.doorExitTrack: + self.doorExitTrack.finish() + self.doorExitTrack = None + + def finishAllTracks(self): + self.finishDoorTrack() + self.finishDoorExitTrack() + for t in self.avatarTracks: + t.finish() + DelayDelete.cleanupDelayDeletes(t) + self.avatarTracks = [] + for t in self.avatarExitTracks: + t.finish() + DelayDelete.cleanupDelayDeletes(t) + self.avatarExitTracks = [] + + def enterOff(self): + pass + + def exitOff(self): + pass + + def getRequestStatus(self): + zoneId = self.otherZoneId + request = { + 'loader': ZoneUtil.getBranchLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'doorIn', + 'hoodId': ZoneUtil.getHoodId(zoneId), + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1, + 'allowRedirect': 0, + 'doorDoId': self.otherDoId + } + return request + + def enterClosing(self, ts): + doorFrameHoleRight = self.findDoorNode('doorFrameHoleRight') + if doorFrameHoleRight.isEmpty(): + self.notify.warning('enterClosing(): did not find doorFrameHoleRight') + return + rightDoor = self.findDoorNode('rightDoor') + if rightDoor.isEmpty(): + self.notify.warning('enterClosing(): did not find rightDoor') + return + otherNP = self.getDoorNodePath() + trackName = 'doorClose-%d' % self.doId + if self.rightSwing: + h = 100 + else: + h = -100 + self.finishDoorTrack() + self.doorTrack = Sequence(LerpHprInterval(nodePath = rightDoor, duration = 1.0, hpr = VBase3(0, 0, 0), startHpr = VBase3(h, 0, 0), other = otherNP, blendType = 'easeInOut'), Func(doorFrameHoleRight.hide), Func(self.hideIfHasFlat, rightDoor), SoundInterval(self.closeSfx, node = rightDoor), name = trackName) + self.doorTrack.start(ts) + if hasattr(self, 'done'): + request = self.getRequestStatus() + messenger.send('doorDoneEvent', [request]) + + def exitClosing(self): + pass + + def enterClosed(self, ts): + pass + + def exitClosed(self): + pass + + def enterOpening(self, ts): + doorFrameHoleRight = self.findDoorNode('doorFrameHoleRight') + if doorFrameHoleRight.isEmpty(): + self.notify.warning('enterOpening(): did not find doorFrameHoleRight') + return + rightDoor = self.findDoorNode('rightDoor') + if rightDoor.isEmpty(): + self.notify.warning('enterOpening(): did not find rightDoor') + return + otherNP = self.getDoorNodePath() + trackName = 'doorOpen-%d' % self.doId + if self.rightSwing: + h = 100 + else: + h = -100 + self.finishDoorTrack() + self.doorTrack = Parallel(SoundInterval(self.openSfx, node=rightDoor), Sequence(HprInterval(rightDoor, VBase3(0, 0, 0), other=otherNP), Wait(0.4), Func(rightDoor.show), Func(doorFrameHoleRight.show), LerpHprInterval(nodePath=rightDoor, duration=0.6, hpr=VBase3(h, 0, 0), startHpr=VBase3(0, 0, 0), other=otherNP, blendType='easeInOut')), name=trackName) + self.doorTrack.start(ts) + + def exitOpening(self): + pass + + def enterOpen(self, ts): + for avatarID in self.avatarIDList: + avatar = self.cr.doId2do.get(avatarID) + if avatar: + track = self.avatarEnterDoorTrack(avatar, 1.0) + track.start(ts) + self.avatarTracks.append(track) + if avatarID == base.localAvatar.doId: + self.done = 1 + self.avatarIDList = [] + + def exitOpen(self): + for track in self.avatarTracks: + track.finish() + DelayDelete.cleanupDelayDeletes(track) + self.avatarTracks = [] + + def exitDoorEnterOff(self): + pass + + def exitDoorExitOff(self): + pass + + def exitDoorEnterClosing(self, ts): + doorFrameHoleLeft = self.findDoorNode('doorFrameHoleLeft') + if doorFrameHoleLeft.isEmpty(): + self.notify.warning('enterOpening(): did not find flatDoors') + return + if self.leftSwing: + h = -100 + else: + h = 100 + leftDoor = self.findDoorNode('leftDoor') + if not leftDoor.isEmpty(): + otherNP = self.getDoorNodePath() + trackName = 'doorExitTrack-%d' % self.doId + self.finishDoorExitTrack() + self.doorExitTrack = Sequence(LerpHprInterval(nodePath = leftDoor, duration = 1.0, hpr = VBase3(0, 0, 0), startHpr = VBase3(h, 0, 0), other = otherNP, blendType = 'easeInOut'), Func(doorFrameHoleLeft.hide), Func(self.hideIfHasFlat, leftDoor), SoundInterval(self.closeSfx, node = leftDoor), name = trackName) + self.doorExitTrack.start(ts) + + def exitDoorExitClosing(self): + pass + + def exitDoorEnterClosed(self, ts): + pass + + def exitDoorExitClosed(self): + pass + + def exitDoorEnterOpening(self, ts): + doorFrameHoleLeft = self.findDoorNode('doorFrameHoleLeft') + if doorFrameHoleLeft.isEmpty(): + self.notify.warning('enterOpening(): did not find flatDoors') + return + leftDoor = self.findDoorNode('leftDoor') + if self.leftSwing: + h = -100 + else: + h = 100 + if not leftDoor.isEmpty(): + otherNP = self.getDoorNodePath() + trackName = 'doorDoorExitTrack-%d' % self.doId + self.finishDoorExitTrack() + self.doorExitTrack = Parallel(SoundInterval(self.openSfx, node = leftDoor), Sequence(Func(leftDoor.show), Func(doorFrameHoleLeft.show), LerpHprInterval(nodePath = leftDoor, duration = 0.59999999999999998, hpr = VBase3(h, 0, 0), startHpr = VBase3(0, 0, 0), other = otherNP, blendType = 'easeInOut')), name = trackName) + self.doorExitTrack.start(ts) + else: + self.notify.warning('exitDoorEnterOpening(): did not find leftDoor') + + def exitDoorExitOpening(self): + pass + + def exitDoorEnterOpen(self, ts): + for avatarID in self.avatarExitIDList: + avatar = self.cr.doId2do.get(avatarID) + if avatar: + track = self.avatarExitTrack(avatar, 0.2) + track.start() + self.avatarExitTracks.append(track) + self.avatarExitIDList = [] + + def exitDoorExitOpen(self): + for track in self.avatarExitTracks: + track.finish() + DelayDelete.cleanupDelayDeletes(track) + self.avatarExitTracks = [] + + def findDoorNode(self, string, allowEmpty = False): + building = self.getBuilding() + if not building: + self.notify.warning('getBuilding() returned None, avoiding crash, remark 896029') + foundNode = None + else: + foundNode = building.find('**/door_' + str(self.doorIndex) + '/**/' + string + '*;+s+i') + if foundNode.isEmpty(): + foundNode = building.find('**/' + string + '*;+s+i') + if allowEmpty: + return foundNode + return foundNode + + def hideIfHasFlat(self, node): + if self.bHasFlat: + node.hide() diff --git a/toontown/building/DistributedDoorAI.py b/toontown/building/DistributedDoorAI.py new file mode 100755 index 00000000..925bb994 --- /dev/null +++ b/toontown/building/DistributedDoorAI.py @@ -0,0 +1,271 @@ +from direct.distributed import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from otp.ai.AIBaseGlobal import * + + +class DistributedDoorAI(DistributedObjectAI.DistributedObjectAI): + def __init__(self, air, blockNumber, doorType, doorIndex=0, lockValue=0, swing=3): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.block = blockNumber + self.swing = swing + self.otherDoor = None + self.doorType = doorType + self.doorIndex = doorIndex + self.setDoorLock(lockValue) + self.fsm = ClassicFSM.ClassicFSM( + 'DistributedDoorAI_right', + [ + State.State('off', self.enterOff, self.exitOff, + ['closing', 'closed', 'opening', 'open']), + State.State('closing', self.enterClosing, self.exitClosing, + ['closed', 'opening']), + State.State('closed', self.enterClosed, self.exitClosed, + ['opening']), + State.State('opening', self.enterOpening, self.exitOpening, + ['open']), + State.State('open', self.enterOpen, self.exitOpen, + ['closing', 'open']) + ], 'off', 'off') + self.fsm.enterInitialState() + self.exitDoorFSM = ClassicFSM.ClassicFSM( + 'DistributedDoorAI_left', + [ + State.State('off', self.exitDoorEnterOff, self.exitDoorExitOff, + ['closing', 'closed', 'opening', 'open']), + State.State('closing', self.exitDoorEnterClosing, self.exitDoorExitClosing, + ['closed', 'opening']), + State.State('closed', self.exitDoorEnterClosed, self.exitDoorExitClosed, + ['opening']), + State.State('opening', self.exitDoorEnterOpening, self.exitDoorExitOpening, + ['open']), + State.State('open', self.exitDoorEnterOpen, self.exitDoorExitOpen, + ['closing', 'open']) + ], 'off', 'off') + self.exitDoorFSM.enterInitialState() + self.doLaterTask = None + self.exitDoorDoLaterTask = None + self.avatarsWhoAreEntering = {} + self.avatarsWhoAreExiting = {} + + def delete(self): + taskMgr.remove(self.uniqueName('door_opening-timer')) + taskMgr.remove(self.uniqueName('door_open-timer')) + taskMgr.remove(self.uniqueName('door_closing-timer')) + taskMgr.remove(self.uniqueName('exit_door_open-timer')) + taskMgr.remove(self.uniqueName('exit_door_closing-timer')) + taskMgr.remove(self.uniqueName('exit_door_opening-timer')) + self.ignoreAll() + del self.block + del self.swing + del self.doorType + del self.fsm + del self.exitDoorFSM + del self.otherDoor + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getDoorIndex(self): + return self.doorIndex + + def setSwing(self, swing): + self.swing = swing + + def getSwing(self): + return self.swing + + def getDoorType(self): + return self.doorType + + def getZoneIdAndBlock(self): + return [self.zoneId, self.block] + + def setOtherDoor(self, door): + self.otherDoor = door + + def getZoneId(self): + return self.zoneId + + def getState(self): + return [self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()] + + def getExitDoorState(self): + return [self.exitDoorFSM.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()] + + def isOpen(self): + state = self.fsm.getCurrentState().getName() + return state == 'open' or state == 'opening' + + def isClosed(self): + return not self.isOpen() + + def setDoorLock(self, locked): + self.lockedDoor = locked + + def isLockedDoor(self): + if simbase.config.GetBool('no-locked-doors', 0): + return 0 + else: + return self.lockedDoor + + def sendReject(self, avatarID, lockedVal): + self.sendUpdateToAvatarId(avatarID, 'rejectEnter', [lockedVal]) + + def requestEnter(self): + avatarID = self.air.getAvatarIdFromSender() + lockedVal = self.isLockedDoor() + if lockedVal: + self.sendReject(avatarID, lockedVal) + else: + self.enqueueAvatarIdEnter(avatarID) + self.sendUpdateToAvatarId( + avatarID, 'setOtherZoneIdAndDoId', + [self.otherDoor.getZoneId(), self.otherDoor.getDoId()]) + + def enqueueAvatarIdEnter(self, avatarID): + if avatarID not in self.avatarsWhoAreEntering: + self.avatarsWhoAreEntering[avatarID] = 1 + self.sendUpdate('avatarEnter', [avatarID]) + self.openDoor(self.fsm) + + def openDoor(self, doorFsm): + stateName = doorFsm.getCurrentState().getName() + if stateName == 'open': + doorFsm.request('open') + elif stateName != 'opening': + doorFsm.request('opening') + + def requestExit(self): + avatarID = self.air.getAvatarIdFromSender() + self.sendUpdate('avatarExit', [avatarID]) + self.enqueueAvatarIdExit(avatarID) + + def enqueueAvatarIdExit(self, avatarID): + if avatarID in self.avatarsWhoAreEntering: + del self.avatarsWhoAreEntering[avatarID] + elif avatarID not in self.avatarsWhoAreExiting: + self.avatarsWhoAreExiting[avatarID] = 1 + self.openDoor(self.exitDoorFSM) + + def requestSuitEnter(self, avatarID): + self.enqueueAvatarIdEnter(avatarID) + + def requestSuitExit(self, avatarID): + self.sendUpdate('avatarExit', [avatarID]) + self.enqueueAvatarIdExit(avatarID) + + def d_setState(self, state): + self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()]) + + def d_setExitDoorState(self, state): + self.sendUpdate('setExitDoorState', [state, globalClockDelta.getRealNetworkTime()]) + + def enterOff(self): + pass + + def exitOff(self): + pass + + def openTask(self, task): + self.fsm.request('closing') + return Task.done + + def enterClosing(self): + self.d_setState('closing') + self.doLaterTask = taskMgr.doMethodLater(1, self.closingTask, self.uniqueName('door_closing-timer')) + + def exitClosing(self): + if self.doLaterTask: + taskMgr.remove(self.doLaterTask) + self.doLaterTask = None + + def closingTask(self, task): + self.fsm.request('closed') + return Task.done + + def enterClosed(self): + self.d_setState('closed') + + def exitClosed(self): + pass + + def enterOpening(self): + self.d_setState('opening') + self.doLaterTask = taskMgr.doMethodLater( + 1, self.openingTask, self.uniqueName('door_opening-timer')) + + def exitOpening(self): + if self.doLaterTask: + taskMgr.remove(self.doLaterTask) + self.doLaterTask = None + + def openingTask(self, task): + self.fsm.request('open') + return Task.done + + def enterOpen(self): + self.d_setState('open') + self.avatarsWhoAreEntering = {} + self.doLaterTask = taskMgr.doMethodLater( + 1, self.openTask, self.uniqueName('door_open-timer')) + + def exitOpen(self): + if self.doLaterTask: + taskMgr.remove(self.doLaterTask) + self.doLaterTask = None + + def exitDoorEnterOff(self): + pass + + def exitDoorExitOff(self): + pass + + def exitDoorOpenTask(self, task): + self.exitDoorFSM.request('closing') + return Task.done + + def exitDoorEnterClosing(self): + self.d_setExitDoorState('closing') + self.exitDoorDoLaterTask = taskMgr.doMethodLater( + 1, self.exitDoorClosingTask, self.uniqueName('exit_door_closing-timer')) + + def exitDoorExitClosing(self): + if self.exitDoorDoLaterTask: + taskMgr.remove(self.exitDoorDoLaterTask) + self.exitDoorDoLaterTask = None + + def exitDoorClosingTask(self, task): + self.exitDoorFSM.request('closed') + return Task.done + + def exitDoorEnterClosed(self): + self.d_setExitDoorState('closed') + + def exitDoorExitClosed(self): + if self.exitDoorDoLaterTask: + taskMgr.remove(self.exitDoorDoLaterTask) + self.exitDoorDoLaterTask = None + + def exitDoorEnterOpening(self): + self.d_setExitDoorState('opening') + self.exitDoorDoLaterTask = taskMgr.doMethodLater( + 1, self.exitDoorOpeningTask, self.uniqueName('exit_door_opening-timer')) + + def exitDoorExitOpening(self): + if self.exitDoorDoLaterTask: + taskMgr.remove(self.exitDoorDoLaterTask) + self.exitDoorDoLaterTask = None + + def exitDoorOpeningTask(self, task): + self.exitDoorFSM.request('open') + return Task.done + + def exitDoorEnterOpen(self): + self.d_setExitDoorState('open') + self.avatarsWhoAreExiting = { } + self.exitDoorDoLaterTask = taskMgr.doMethodLater( + 1, self.exitDoorOpenTask, self.uniqueName('exit_door_open-timer')) + + def exitDoorExitOpen(self): + if self.exitDoorDoLaterTask: + taskMgr.remove(self.exitDoorDoLaterTask) + self.exitDoorDoLaterTask = None diff --git a/toontown/building/DistributedElevator.py b/toontown/building/DistributedElevator.py new file mode 100755 index 00000000..3a901b0d --- /dev/null +++ b/toontown/building/DistributedElevator.py @@ -0,0 +1,520 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from ElevatorConstants import * +from ElevatorUtils import * +from direct.showbase import PythonUtil +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.distributed import DistributedObject +from direct.fsm import State +from toontown.toonbase import TTLocalizer, ToontownGlobals +from direct.task.Task import Task +from toontown.distributed import DelayDelete +from toontown.hood import ZoneUtil +from toontown.building import BoardingGroupShow + +class DistributedElevator(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevator') + JumpOutOffsets = JumpOutOffsets + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.bldgRequest = None + self.toonRequests = {} + self.deferredSlots = [] + self.localToonOnBoard = 0 + self.boardedAvIds = {} + self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.ogg') + self.finalOpenSfx = None + self.closeSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_close.ogg') + self.elevatorFSM = None + self.finalCloseSfx = None + self.elevatorPoints = ElevatorPoints + self.fillSlotTrack = None + self.type = ELEVATOR_NORMAL + self.countdownTime = ElevatorData[self.type]['countdown'] + self.__toonTracks = {} + self.fsm = ClassicFSM.ClassicFSM('DistributedElevator', [State.State('off', self.enterOff, self.exitOff, ['opening', + 'waitEmpty', + 'waitCountdown', + 'closing', + 'closed']), + State.State('opening', self.enterOpening, self.exitOpening, ['waitEmpty', 'waitCountdown']), + State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), + State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty', 'closing']), + State.State('closing', self.enterClosing, self.exitClosing, ['closed', 'waitEmpty']), + State.State('closed', self.enterClosed, self.exitClosed, ['opening'])], 'off', 'off') + self.fsm.enterInitialState() + self.isSetup = 0 + self.__preSetupState = None + self.bigElevator = 0 + self.offsetNp = None + return + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def setupElevator(self): + collisionRadius = ElevatorData[self.type]['collRadius'] + self.elevatorSphere = CollisionSphere(0, 5, 0, collisionRadius) + self.elevatorSphere.setTangible(0) + self.elevatorSphereNode = CollisionNode(self.uniqueName('elevatorSphere')) + self.elevatorSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.elevatorSphereNode.addSolid(self.elevatorSphere) + self.elevatorSphereNodePath = self.getElevatorModel().attachNewNode(self.elevatorSphereNode) + self.elevatorSphereNodePath.reparentTo(self.getElevatorModel()) + self.elevatorSphereNodePath.stash() + self.boardedAvIds = {} + self.openDoors = getOpenInterval(self, self.leftDoor, self.rightDoor, self.openSfx, self.finalOpenSfx, self.type) + self.closeDoors = getCloseInterval(self, self.leftDoor, self.rightDoor, self.closeSfx, self.finalCloseSfx, self.type) + self.closeDoors = Sequence(self.closeDoors, Func(self.onDoorCloseFinish)) + self.finishSetup() + + def finishSetup(self): + self.isSetup = 1 + self.offsetNP = self.getElevatorModel().attachNewNode('dummyNP') + if self.__preSetupState: + self.fsm.request(self.__preSetupState, [0]) + self.__preSetupState = None + for slot in self.deferredSlots: + self.fillSlot(*slot) + + self.deferredSlots = [] + return + + def disable(self): + if self.bldgRequest: + self.cr.relatedObjectMgr.abortRequest(self.bldgRequest) + self.bldgRequest = None + for request in self.toonRequests.values(): + self.cr.relatedObjectMgr.abortRequest(request) + + self.toonRequests = {} + if hasattr(self, 'openDoors'): + self.openDoors.pause() + if hasattr(self, 'closeDoors'): + self.closeDoors.pause() + self.clearToonTracks() + self.fsm.request('off') + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + if self.isSetup: + self.elevatorSphereNodePath.removeNode() + del self.elevatorSphereNodePath + del self.elevatorSphereNode + del self.elevatorSphere + del self.bldg + if self.leftDoor: + del self.leftDoor + if self.rightDoor: + del self.rightDoor + if hasattr(self, 'openDoors'): + del self.openDoors + if hasattr(self, 'closeDoors'): + del self.closeDoors + del self.fsm + del self.openSfx + del self.closeSfx + self.isSetup = 0 + self.fillSlotTrack = None + if not self.offsetNp: + return + self.offsetNP.removeNode() + if hasattr(base.localAvatar, 'elevatorNotifier'): + base.localAvatar.elevatorNotifier.cleanup() + DistributedObject.DistributedObject.delete(self) + return + + def setBldgDoId(self, bldgDoId): + self.bldgDoId = bldgDoId + self.bldgRequest = self.cr.relatedObjectMgr.requestObjects([bldgDoId], allCallback=self.gotBldg, timeout=2) + + def gotBldg(self, buildingList): + self.bldgRequest = None + self.bldg = buildingList[0] + if not self.bldg: + self.notify.error('setBldgDoId: elevator %d cannot find bldg %d!' % (self.doId, self.bldgDoId)) + return + self.setupElevator() + return + + def gotToon(self, index, avId, toonList): + request = self.toonRequests.get(index) + if request: + del self.toonRequests[index] + self.fillSlot(index, avId) + else: + self.notify.error('gotToon: already had got toon in slot %s.' % index) + + def setState(self, state, timestamp): + if self.isSetup: + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + else: + self.__preSetupState = state + + def fillSlot0(self, avId, wantBoardingShow): + self.fillSlot(0, avId, wantBoardingShow) + + def fillSlot1(self, avId, wantBoardingShow): + self.fillSlot(1, avId, wantBoardingShow) + + def fillSlot2(self, avId, wantBoardingShow): + self.fillSlot(2, avId, wantBoardingShow) + + def fillSlot3(self, avId, wantBoardingShow): + self.fillSlot(3, avId, wantBoardingShow) + + def fillSlot4(self, avId, wantBoardingShow): + self.fillSlot(4, avId, wantBoardingShow) + + def fillSlot5(self, avId, wantBoardingShow): + self.fillSlot(5, avId, wantBoardingShow) + + def fillSlot6(self, avId, wantBoardingShow): + self.fillSlot(6, avId, wantBoardingShow) + + def fillSlot7(self, avId, wantBoardingShow): + self.fillSlot(7, avId, wantBoardingShow) + + def fillSlot(self, index, avId, wantBoardingShow = 0): + self.notify.debug('%s.fillSlot(%s, %s, ...)' % (self.doId, index, avId)) + request = self.toonRequests.get(index) + if request: + self.cr.relatedObjectMgr.abortRequest(request) + del self.toonRequests[index] + if avId == 0: + pass + elif avId not in self.cr.doId2do: + func = PythonUtil.Functor(self.gotToon, index, avId) + self.toonRequests[index] = self.cr.relatedObjectMgr.requestObjects([avId], allCallback=func) + elif not self.isSetup: + self.deferredSlots.append((index, avId, wantBoardingShow)) + else: + if avId == base.localAvatar.getDoId(): + place = base.cr.playGame.getPlace() + if not place: + return + place.detectedElevatorCollision(self) + elevator = self.getPlaceElevator() + if elevator == None: + if place.fsm.hasStateNamed('elevator'): + place.fsm.request('elevator') + elif place.fsm.hasStateNamed('Elevator'): + place.fsm.request('Elevator') + elevator = self.getPlaceElevator() + if not elevator: + return + self.localToonOnBoard = 1 + if hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty: + localAvatar.boardingParty.forceCleanupInviteePanel() + localAvatar.boardingParty.forceCleanupInviterPanels() + if hasattr(base.localAvatar, 'elevatorNotifier'): + base.localAvatar.elevatorNotifier.cleanup() + cameraTrack = Sequence() + cameraTrack.append(Func(elevator.fsm.request, 'boarding', [self.getElevatorModel()])) + cameraTrack.append(Func(elevator.fsm.request, 'boarded')) + toon = self.cr.doId2do[avId] + toon.stopSmooth() + if not wantBoardingShow: + toon.setZ(self.getElevatorModel(), self.elevatorPoints[index][2]) + toon.setShadowHeight(0) + if toon.isDisguised: + animInFunc = Sequence(Func(toon.suit.loop, 'walk')) + animFunc = Sequence(Func(toon.setAnimState, 'neutral', 1.0), Func(toon.suit.loop, 'neutral')) + else: + animInFunc = Sequence(Func(toon.setAnimState, 'run', 1.0)) + animFunc = Func(toon.setAnimState, 'neutral', 1.0) + toon.headsUp(self.getElevatorModel(), apply(Point3, self.elevatorPoints[index])) + track = Sequence(animInFunc, LerpPosInterval(toon, TOON_BOARD_ELEVATOR_TIME * 0.75, apply(Point3, self.elevatorPoints[index]), other=self.getElevatorModel()), LerpHprInterval(toon, TOON_BOARD_ELEVATOR_TIME * 0.25, Point3(180, 0, 0), other=self.getElevatorModel()), Func(self.clearToonTrack, avId), animFunc, name=toon.uniqueName('fillElevator'), autoPause=1) + if wantBoardingShow: + boardingTrack, boardingTrackType = self.getBoardingTrack(toon, index, False) + track = Sequence(boardingTrack, track) + if avId == base.localAvatar.getDoId(): + cameraWaitTime = 2.5 + if boardingTrackType == BoardingGroupShow.TRACK_TYPE_RUN: + cameraWaitTime = 0.5 + elif boardingTrackType == BoardingGroupShow.TRACK_TYPE_POOF: + cameraWaitTime = 1 + cameraTrack = Sequence(Wait(cameraWaitTime), cameraTrack) + if self.canHideBoardingQuitBtn(avId): + track = Sequence(Func(localAvatar.boardingParty.groupPanel.disableQuitButton), track) + if avId == base.localAvatar.getDoId(): + track = Parallel(cameraTrack, track) + track.delayDelete = DelayDelete.DelayDelete(toon, 'Elevator.fillSlot') + self.storeToonTrack(avId, track) + track.start() + self.fillSlotTrack = track + self.boardedAvIds[avId] = None + return + + def emptySlot0(self, avId, timestamp, time): + self.emptySlot(0, avId, timestamp, time) + + def emptySlot1(self, avId, timestamp, time): + self.emptySlot(1, avId, timestamp, time) + + def emptySlot2(self, avId, timestamp, time): + self.emptySlot(2, avId, timestamp, time) + + def emptySlot3(self, avId, timestamp, time): + self.emptySlot(3, avId, timestamp, time) + + def emptySlot4(self, avId, timestamp, time): + self.emptySlot(4, avId, timestamp, time) + + def emptySlot5(self, avId, timestamp, time): + self.emptySlot(5, avId, timestamp) + + def emptySlot6(self, avId, timestamp, time): + self.emptySlot(6, avId, timestamp, time) + + def emptySlot7(self, avId, timestamp, time): + self.emptySlot(7, avId, timestamp, time) + + def notifyToonOffElevator(self, toon): + toon.setAnimState('neutral', 1.0) + if toon == base.localAvatar: + doneStatus = {'where': 'exit'} + elevator = self.getPlaceElevator() + if elevator: + elevator.signalDone(doneStatus) + self.localToonOnBoard = 0 + else: + toon.startSmooth() + + def emptySlot(self, index, avId, timestamp, timeSent = 0): + if self.fillSlotTrack: + self.fillSlotTrack.finish() + self.fillSlotTrack = None + if avId == 0: + pass + elif not self.isSetup: + newSlots = [] + for slot in self.deferredSlots: + if slot[0] != index: + newSlots.append(slot) + + self.deferredSlots = newSlots + else: + timeToSet = self.countdownTime + if timeSent > 0: + timeToSet = timeSent + if avId in self.cr.doId2do: + if hasattr(self, 'clockNode'): + if timestamp < timeToSet and timestamp >= 0: + self.countdown(timeToSet - timestamp) + else: + self.countdown(timeToSet) + toon = self.cr.doId2do[avId] + toon.stopSmooth() + if toon.isDisguised: + toon.suit.loop('walk') + animFunc = Func(toon.suit.loop, 'neutral') + else: + toon.setAnimState('run', 1.0) + animFunc = Func(toon.setAnimState, 'neutral', 1.0) + track = Sequence(LerpPosInterval(toon, TOON_EXIT_ELEVATOR_TIME, Point3(*self.JumpOutOffsets[index]), other=self.getElevatorModel()), animFunc, Func(self.notifyToonOffElevator, toon), Func(self.clearToonTrack, avId), name=toon.uniqueName('emptyElevator'), autoPause=1) + if self.canHideBoardingQuitBtn(avId): + track.append(Func(localAvatar.boardingParty.groupPanel.enableQuitButton)) + track.append(Func(localAvatar.boardingParty.enableGoButton)) + track.delayDelete = DelayDelete.DelayDelete(toon, 'Elevator.emptySlot') + self.storeToonTrack(avId, track) + track.start() + if avId == base.localAvatar.getDoId(): + messenger.send('exitElevator') + if avId in self.boardedAvIds: + del self.boardedAvIds[avId] + else: + self.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot exit the elevator!') + return + + def handleEnterSphere(self, collEntry): + self.notify.debug('Entering Elevator Sphere....') + if base.localAvatar.hp > 0: + self.cr.playGame.getPlace().detectedElevatorCollision(self) + toon = base.localAvatar + self.sendUpdate('requestBoard', []) + + def rejectBoard(self, avId, reason = 0): + if hasattr(base.localAvatar, 'elevatorNotifier'): + if reason == REJECT_PROMOTION: + base.localAvatar.elevatorNotifier.showMe(TTLocalizer.BossElevatorRejectMessage) + doneStatus = {'where': 'reject'} + elevator = self.getPlaceElevator() + if elevator: + elevator.signalDone(doneStatus) + + def timerTask(self, task): + countdownTime = int(task.duration - task.time) + timeStr = str(countdownTime) + if self.clockNode.getText() != timeStr: + self.clockNode.setText(timeStr) + if task.time >= task.duration: + return Task.done + else: + return Task.cont + + def countdown(self, duration): + countdownTask = Task(self.timerTask) + countdownTask.duration = duration + taskMgr.remove(self.uniqueName('elevatorTimerTask')) + return taskMgr.add(countdownTask, self.uniqueName('elevatorTimerTask')) + + def handleExitButton(self): + self.sendUpdate('requestExit') + + def enterWaitCountdown(self, ts): + self.elevatorSphereNodePath.unstash() + self.accept(self.uniqueName('enterelevatorSphere'), self.handleEnterSphere) + self.accept('elevatorExitButton', self.handleExitButton) + + def exitWaitCountdown(self): + self.elevatorSphereNodePath.stash() + self.ignore(self.uniqueName('enterelevatorSphere')) + self.ignore('elevatorExitButton') + self.ignore('localToonLeft') + taskMgr.remove(self.uniqueName('elevatorTimerTask')) + self.clock.removeNode() + del self.clock + del self.clockNode + + def enterClosing(self, ts): + if self.localToonOnBoard: + elevator = self.getPlaceElevator() + if elevator: + elevator.fsm.request('elevatorClosing') + self.closeDoors.start(ts) + + def exitClosing(self): + pass + + def onDoorCloseFinish(self): + for avId in self.boardedAvIds.keys(): + av = self.cr.doId2do.get(avId) + if av is not None: + if av.getParent().compareTo(self.getElevatorModel()) == 0: + av.detachNode() + + self.boardedAvIds = {} + return + + def enterClosed(self, ts): + self.forceDoorsClosed() + self.__doorsClosed(self.getZoneId()) + + def exitClosed(self): + pass + + def forceDoorsOpen(self): + openDoors(self.leftDoor, self.rightDoor) + + def forceDoorsClosed(self): + self.closeDoors.finish() + closeDoors(self.leftDoor, self.rightDoor) + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterWaitEmpty(self, ts): + pass + + def exitWaitEmpty(self): + pass + + def enterOpening(self, ts): + self.openDoors.start(ts) + + def exitOpening(self): + pass + + def startCountdownClock(self, countdownTime, ts): + self.clockNode = TextNode('elevatorClock') + self.clockNode.setFont(ToontownGlobals.getSignFont()) + self.clockNode.setAlign(TextNode.ACenter) + self.clockNode.setTextColor(0.5, 0.5, 0.5, 1) + self.clockNode.setText(str(int(countdownTime))) + self.clock = self.getElevatorModel().attachNewNode(self.clockNode) + self.clock.setPosHprScale(0, 2.0, 7.5, 0, 0, 0, 2.0, 2.0, 2.0) + if ts < countdownTime: + self.countdown(countdownTime - ts) + + def _getDoorsClosedInfo(self): + return ('suitInterior', 'suitInterior') + + def __doorsClosed(self, zoneId): + if self.localToonOnBoard: + hoodId = ZoneUtil.getHoodId(zoneId) + loader, where = self._getDoorsClosedInfo() + doneStatus = {'loader': loader, + 'where': where, + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None} + elevator = self.elevatorFSM + del self.elevatorFSM + elevator.signalDone(doneStatus) + base.camLens.setMinFov(ToontownGlobals.CBElevatorFov/(4./3.)) + + def getElevatorModel(self): + self.notify.error('getElevatorModel: pure virtual -- inheritors must override') + + def getPlaceElevator(self): + place = self.cr.playGame.getPlace() + if place: + if hasattr(place, 'elevator'): + return place.elevator + else: + self.notify.warning("Place was in state '%s' instead of Elevator." % place.fsm.getCurrentState().getName()) + place.detectedElevatorCollision(self) + else: + self.notify.warning("Place didn't exist") + return None + + def storeToonTrack(self, avId, track): + self.clearToonTrack(avId) + self.__toonTracks[avId] = track + + def clearToonTrack(self, avId): + oldTrack = self.__toonTracks.get(avId) + if oldTrack: + oldTrack.pause() + if self.__toonTracks.get(avId): + DelayDelete.cleanupDelayDeletes(self.__toonTracks[avId]) + del self.__toonTracks[avId] + + def clearToonTracks(self): + keyList = [] + for key in self.__toonTracks: + keyList.append(key) + + for key in keyList: + if key in self.__toonTracks: + self.clearToonTrack(key) + + def getDestName(self): + return None + + def getOffsetPos(self, seatIndex = 0): + return self.JumpOutOffsets[seatIndex] + + def getOffsetPosWrtToonParent(self, toon, seatIndex = 0): + self.offsetNP.setPos(apply(Point3, self.getOffsetPos(seatIndex))) + return self.offsetNP.getPos(toon.getParent()) + + def getOffsetPosWrtRender(self, seatIndex = 0): + self.offsetNP.setPos(apply(Point3, self.getOffsetPos(seatIndex))) + return self.offsetNP.getPos(render) + + def canHideBoardingQuitBtn(self, avId): + return avId == localAvatar.doId and hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty and localAvatar.boardingParty.groupPanel + + def getBoardingTrack(self, toon, seatIndex, wantToonRotation): + self.boardingGroupShow = BoardingGroupShow.BoardingGroupShow(toon) + track, trackType = self.boardingGroupShow.getBoardingTrack(self.getElevatorModel(), self.getOffsetPosWrtToonParent(toon, seatIndex), self.getOffsetPosWrtRender(seatIndex), wantToonRotation) + return (track, trackType) diff --git a/toontown/building/DistributedElevatorAI.py b/toontown/building/DistributedElevatorAI.py new file mode 100755 index 00000000..4ba67609 --- /dev/null +++ b/toontown/building/DistributedElevatorAI.py @@ -0,0 +1,258 @@ +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from ElevatorConstants import * +from direct.distributed import DistributedObjectAI +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +from direct.directnotify import DirectNotifyGlobal + +class DistributedElevatorAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorAI') + + def __init__(self, air, bldg, numSeats = 4): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.type = ELEVATOR_NORMAL + self.countdownTime = ElevatorData[self.type]['countdown'] + self.bldg = bldg + self.bldgDoId = bldg.getDoId() + self.seats = [] + for seat in xrange(numSeats): + self.seats.append(None) + + self.accepting = 0 + self.fsm = ClassicFSM.ClassicFSM('DistributedElevatorAI', [State.State('off', self.enterOff, self.exitOff, ['opening', 'closed']), + State.State('opening', self.enterOpening, self.exitOpening, ['waitEmpty', 'waitCountdown']), + State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), + State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty', 'allAboard']), + State.State('allAboard', self.enterAllAboard, self.exitAllAboard, ['closing', 'waitEmpty', 'waitCountdown']), + State.State('closing', self.enterClosing, self.exitClosing, ['closed', 'waitEmpty']), + State.State('closed', self.enterClosed, self.exitClosed, ['opening'])], 'off', 'off') + self.fsm.enterInitialState() + self.boardingParty = None + return + + def delete(self): + self.fsm.requestFinalState() + del self.fsm + del self.bldg + self.ignoreAll() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def setBoardingParty(self, party): + self.boardingParty = party + + def generate(self): + self.start() + DistributedObjectAI.DistributedObjectAI.generate(self) + + def getBldgDoId(self): + return self.bldgDoId + + def findAvailableSeat(self): + for i in xrange(len(self.seats)): + if self.seats[i] == None: + return i + + def findAvatar(self, avId): + for i in xrange(len(self.seats)): + if self.seats[i] == avId: + return i + + def countFullSeats(self): + avCounter = 0 + for i in self.seats: + if i: + avCounter += 1 + return avCounter + + def countOpenSeats(self): + openSeats = 0 + for i in xrange(len(self.seats)): + if self.seats[i] is None: + openSeats += 1 + return openSeats + + def rejectingBoardersHandler(self, avId, reason = 0, wantBoardingShow = 0): + self.rejectBoarder(avId, reason) + + def rejectBoarder(self, avId, reason = 0): + self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId, reason]) + + def acceptingBoardersHandler(self, avId, reason = 0, wantBoardingShow = 0): + self.notify.debug('acceptingBoardersHandler') + seatIndex = self.findAvailableSeat() + if seatIndex == None: + self.rejectBoarder(avId, REJECT_NOSEAT) + else: + self.acceptBoarder(avId, seatIndex, wantBoardingShow) + return + + def acceptBoarder(self, avId, seatIndex, wantBoardingShow = 0): + self.notify.debug('acceptBoarder') + if self.findAvatar(avId) != None: + return + self.seats[seatIndex] = avId + self.timeOfBoarding = globalClock.getRealTime() + if wantBoardingShow: + self.timeOfGroupBoarding = globalClock.getRealTime() + self.sendUpdate('fillSlot' + str(seatIndex), [avId, wantBoardingShow]) + if self.fsm.getCurrentState().getName() == 'waitEmpty': + self.fsm.request('waitCountdown') + elif self.fsm.getCurrentState().getName() == 'waitCountdown' and self.findAvailableSeat() is None: + self.fsm.request('allAboard') + return + + def rejectingExitersHandler(self, avId): + self.rejectExiter(avId) + + def rejectExiter(self, avId): + pass + + def acceptingExitersHandler(self, avId): + self.acceptExiter(avId) + + def clearEmptyNow(self, seatIndex): + self.sendUpdate('emptySlot' + str(seatIndex), [0, + globalClockDelta.getRealNetworkTime(), + 0]) + + def clearFullNow(self, seatIndex): + avId = self.seats[seatIndex] + if avId == None: + self.notify.warning('Clearing an empty seat index: ' + str(seatIndex) + ' ... Strange...') + else: + self.seats[seatIndex] = None + self.sendUpdate('fillSlot' + str(seatIndex), [0, 0]) + self.ignore(self.air.getAvatarExitEvent(avId)) + return + + def d_setState(self, state): + self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()]) + + def getState(self): + return self.fsm.getCurrentState().getName() + + def avIsOKToBoard(self, av): + return self.accepting + + def checkBoard(self, av): + return 0 + + def requestBoard(self, *args): + self.notify.debug('requestBoard') + avId = self.air.getAvatarIdFromSender() + if self.findAvatar(avId) != None: + self.notify.warning('Ignoring multiple requests from %s to board.' % avId) + return + av = self.air.doId2do.get(avId) + if av: + boardResponse = self.checkBoard(av) + newArgs = (avId,) + args + (boardResponse,) + if self.boardingParty and self.boardingParty.hasActiveGroup(avId) and self.boardingParty.getGroupLeader(avId) != avId: + self.notify.warning('Rejecting %s from boarding the elevator because he is already part of a Boarding Group.' % avId) + self.rejectingBoardersHandler(*newArgs) + return + if boardResponse == 0: + self.acceptingBoardersHandler(*newArgs) + else: + self.rejectingBoardersHandler(*newArgs) + else: + self.notify.warning('avid: %s does not exist, but tried to board an elevator' % avId) + return + + def partyAvatarBoard(self, avatar, wantBoardingShow = 0): + av = avatar + avId = avatar.doId + if self.findAvatar(avId) != None: + self.notify.warning('Ignoring multiple requests from %s to board.' % avId) + return + if av: + boardResponse = self.checkBoard(av) + newArgs = (avId,) + (boardResponse,) + (wantBoardingShow,) + if boardResponse == 0: + self.acceptingBoardersHandler(*newArgs) + else: + self.rejectingBoardersHandler(*newArgs) + else: + self.notify.warning('avid: %s does not exist, but tried to board an elevator' % avId) + return + + def requestExit(self, *args): + self.notify.debug('requestExit') + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av: + newArgs = (avId,) + args + if self.accepting: + self.acceptingExitersHandler(*newArgs) + else: + self.rejectingExitersHandler(*newArgs) + else: + self.notify.warning('avId: %s does not exist, but tried to exit an elevator' % avId) + + def start(self): + self.open() + + def enterOff(self): + self.accepting = 0 + self.timeOfBoarding = None + self.timeOfGroupBoarding = None + if hasattr(self, 'doId'): + for seatIndex in xrange(len(self.seats)): + taskMgr.remove(self.uniqueName('clearEmpty-' + str(seatIndex))) + + return + + def exitOff(self): + self.accepting = 0 + + def open(self): + self.fsm.request('opening') + + def enterOpening(self): + self.d_setState('opening') + self.accepting = 0 + for seat in self.seats: + seat = None + + def exitOpening(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('opening-timer')) + + def enterWaitCountdown(self): + self.d_setState('waitCountdown') + self.accepting = 1 + + def exitWaitCountdown(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('countdown-timer')) + + def enterAllAboard(self): + self.accepting = 0 + + def exitAllAboard(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('waitForAllAboard')) + + def enterClosing(self): + self.d_setState('closing') + self.accepting = 0 + + def exitClosing(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('closing-timer')) + + def enterClosed(self): + self.d_setState('closed') + + def exitClosed(self): + pass + + def enterWaitEmpty(self): + self.d_setState('waitEmpty') + self.accepting = 1 + + def exitWaitEmpty(self): + self.accepting = 0 \ No newline at end of file diff --git a/toontown/building/DistributedElevatorExt.py b/toontown/building/DistributedElevatorExt.py new file mode 100755 index 00000000..2fc3a673 --- /dev/null +++ b/toontown/building/DistributedElevatorExt.py @@ -0,0 +1,135 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.interval.IntervalGlobal import * +from panda3d.core import * + +import DistributedElevator +from ElevatorConstants import * +from ElevatorUtils import * +from toontown.hood import ZoneUtil +from otp.nametag.NametagGroup import NametagGroup +from otp.nametag.Nametag import Nametag +from otp.nametag.NametagConstants import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals + +class DistributedElevatorExt(DistributedElevator.DistributedElevator): + + def __init__(self, cr): + DistributedElevator.DistributedElevator.__init__(self, cr) + self.nametag = None + self.currentFloor = -1 + return + + def setupElevator(self): + if self.isSetup: + self.elevatorSphereNodePath.removeNode() + self.leftDoor = self.bldg.leftDoor + self.rightDoor = self.bldg.rightDoor + DistributedElevator.DistributedElevator.setupElevator(self) + self.setupNametag() + + def disable(self): + self.clearNametag() + DistributedElevator.DistributedElevator.disable(self) + + def setupNametag(self): + if self.nametag == None: + self.nametag = NametagGroup() + self.nametag.setFont(ToontownGlobals.getBuildingNametagFont()) + if TTLocalizer.BuildingNametagShadow: + self.nametag.setShadow(*TTLocalizer.BuildingNametagShadow) + self.nametag.setContents(Nametag.CName) + self.nametag.setColorCode(NametagGroup.CCSuitBuilding) + self.nametag.setActive(0) + self.nametag.setAvatar(self.getElevatorModel()) + name = self.cr.playGame.dnaStore.getTitleFromBlockNumber(self.bldg.block) + if not name: + name = TTLocalizer.CogsInc + else: + name += TTLocalizer.CogsIncExt + self.nametag.setName(name) + self.nametag.manage(base.marginManager) + + def clearNametag(self): + if self.nametag != None: + self.nametag.unmanage(base.marginManager) + self.nametag.setAvatar(NodePath()) + self.nametag.destroy() + self.nametag = None + return + + def getBldgDoorOrigin(self): + return self.bldg.getSuitDoorOrigin() + + def gotBldg(self, buildingList): + self.bldgRequest = None + self.bldg = buildingList[0] + if not self.bldg: + self.notify.warning('setBldgDoId: elevator %d cannot find bldg %d!' % (self.doId, self.bldgDoId)) + return + if self.getBldgDoorOrigin(): + self.bossLevel = self.bldg.getBossLevel() + self.setupElevator() + else: + self.notify.warning('setBldgDoId: elevator %d cannot find suitDoorOrigin for bldg %d!' % (self.doId, self.bldgDoId)) + return + + def setFloor(self, floorNumber): + self.currentFloor = 0 + if hasattr(self, 'bldg'): + if self.currentFloor >= 0: + if self.bldg.floorIndicator[self.currentFloor]: + self.bldg.floorIndicator[self.currentFloor].setColor(LIGHT_OFF_COLOR) + if floorNumber >= 0: + if self.bldg.floorIndicator[floorNumber]: + self.bldg.floorIndicator[floorNumber].setColor(LIGHT_ON_COLOR) + self.currentFloor = floorNumber + + def handleEnterSphere(self, collEntry): + self.notify.debug('Entering Elevator Sphere....') + if hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty and localAvatar.boardingParty.getGroupLeader(localAvatar.doId) and localAvatar.boardingParty.getGroupLeader(localAvatar.doId) != localAvatar.doId: + base.localAvatar.elevatorNotifier.showMe(TTLocalizer.ElevatorGroupMember) + else: + self.cr.playGame.getPlace().detectedElevatorCollision(self) + + def handleEnterElevator(self): + if hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty and localAvatar.boardingParty.getGroupLeader(localAvatar.doId): + if localAvatar.boardingParty.getGroupLeader(localAvatar.doId) == localAvatar.doId: + localAvatar.boardingParty.handleEnterElevator(self) + elif base.localAvatar.hp > 0: + toon = base.localAvatar + self.sendUpdate('requestBoard', []) + else: + self.notify.warning('Tried to board elevator with hp: %d' % base.localAvatar.hp) + + def enterWaitEmpty(self, ts): + self.elevatorSphereNodePath.unstash() + self.forceDoorsOpen() + self.accept(self.uniqueName('enterelevatorSphere'), self.handleEnterSphere) + self.accept(self.uniqueName('enterElevatorOK'), self.handleEnterElevator) + DistributedElevator.DistributedElevator.enterWaitEmpty(self, ts) + + def exitWaitEmpty(self): + self.elevatorSphereNodePath.stash() + self.ignore(self.uniqueName('enterelevatorSphere')) + self.ignore(self.uniqueName('enterElevatorOK')) + DistributedElevator.DistributedElevator.exitWaitEmpty(self) + + def enterWaitCountdown(self, ts): + DistributedElevator.DistributedElevator.enterWaitCountdown(self, ts) + self.forceDoorsOpen() + self.accept(self.uniqueName('enterElevatorOK'), self.handleEnterElevator) + self.startCountdownClock(self.countdownTime, ts) + + def exitWaitCountdown(self): + self.ignore(self.uniqueName('enterElevatorOK')) + DistributedElevator.DistributedElevator.exitWaitCountdown(self) + + def getZoneId(self): + return self.bldg.interiorZoneId + + def getElevatorModel(self): + return self.bldg.getSuitElevatorNodePath() diff --git a/toontown/building/DistributedElevatorExtAI.py b/toontown/building/DistributedElevatorExtAI.py new file mode 100755 index 00000000..1bb9cd63 --- /dev/null +++ b/toontown/building/DistributedElevatorExtAI.py @@ -0,0 +1,169 @@ +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from ElevatorConstants import * +import DistributedElevatorAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task +from direct.directnotify import DirectNotifyGlobal + +class DistributedElevatorExtAI(DistributedElevatorAI.DistributedElevatorAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorExtAI') + + def __init__(self, air, bldg, numSeats = 4): + DistributedElevatorAI.DistributedElevatorAI.__init__(self, air, bldg, numSeats) + self.boardingParty = None + + def delete(self): + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.clearFullNow(seatIndex) + self.clearEmptyNow(seatIndex) + + DistributedElevatorAI.DistributedElevatorAI.delete(self) + + def d_setFloor(self, floorNumber): + self.sendUpdate('setFloor', [floorNumber]) + + def acceptBoarder(self, avId, seatIndex, wantBoardingShow = 0): + DistributedElevatorAI.DistributedElevatorAI.acceptBoarder(self, avId, seatIndex, wantBoardingShow) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + self.fsm.request('waitCountdown') + + def __handleUnexpectedExit(self, avId): + self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly') + seatIndex = self.findAvatar(avId) + if seatIndex == None: + pass + else: + self.clearFullNow(seatIndex) + self.clearEmptyNow(seatIndex) + if self.countFullSeats() == 0: + self.fsm.request('waitEmpty') + return + + def acceptExiter(self, avId): + seatIndex = self.findAvatar(avId) + if seatIndex == None: + pass + else: + self.clearFullNow(seatIndex) + self.resetCountdown() + timeToSend = self.countdownTime + self.sendUpdate('emptySlot' + str(seatIndex), [avId, + globalClockDelta.getRealNetworkTime(), + self.countdownTime]) + if self.countFullSeats() == 0: + self.fsm.request('waitEmpty') + taskMgr.doMethodLater(TOON_EXIT_ELEVATOR_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs=(seatIndex,)) + return + + def enterOpening(self): + DistributedElevatorAI.DistributedElevatorAI.enterOpening(self) + taskMgr.doMethodLater(ElevatorData[ELEVATOR_NORMAL]['openTime'], self.waitEmptyTask, self.uniqueName('opening-timer')) + + def waitEmptyTask(self, task): + self.fsm.request('waitEmpty') + return Task.done + + def enterWaitEmpty(self): + DistributedElevatorAI.DistributedElevatorAI.enterWaitEmpty(self) + + def enterWaitCountdown(self): + DistributedElevatorAI.DistributedElevatorAI.enterWaitCountdown(self) + taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer')) + + def timeToGoTask(self, task): + if self.countFullSeats() > 0: + self.fsm.request('allAboard') + else: + self.fsm.request('waitEmpty') + return Task.done + + def resetCountdown(self): + taskMgr.remove(self.uniqueName('countdown-timer')) + taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer')) + + def setCountdown(self, timeToSet): + taskMgr.remove(self.uniqueName('countdown-timer')) + taskMgr.doMethodLater(timeToSet, self.timeToGoTask, self.uniqueName('countdown-timer')) + + def enterAllAboard(self): + DistributedElevatorAI.DistributedElevatorAI.enterAllAboard(self) + currentTime = globalClock.getRealTime() + elapsedTime = currentTime - self.timeOfBoarding + self.notify.debug('elapsed time: ' + str(elapsedTime)) + waitTime = max(TOON_BOARD_ELEVATOR_TIME - elapsedTime, 0) + waitTime += self.getBoardingShowTimeLeft() + taskMgr.doMethodLater(waitTime, self.closeTask, self.uniqueName('waitForAllAboard')) + + def getBoardingShowTimeLeft(self): + currentTime = globalClock.getRealTime() + timeLeft = 0.0 + if hasattr(self, 'timeOfGroupBoarding') and self.timeOfGroupBoarding: + elapsedTime = currentTime - self.timeOfGroupBoarding + timeLeft = max(MAX_GROUP_BOARDING_TIME - elapsedTime, 0) + if timeLeft > MAX_GROUP_BOARDING_TIME: + timeLeft = 0.0 + return timeLeft + + def closeTask(self, task): + if self.countFullSeats() > 0: + self.fsm.request('closing') + else: + self.fsm.request('waitEmpty') + return Task.done + + def enterClosing(self): + DistributedElevatorAI.DistributedElevatorAI.enterClosing(self) + taskMgr.doMethodLater(ElevatorData[ELEVATOR_NORMAL]['closeTime'], self.elevatorClosedTask, self.uniqueName('closing-timer')) + + def elevatorClosedTask(self, task): + self.elevatorClosed() + return Task.done + + def _createInterior(self): + self.bldg.createSuitInterior() + + def elevatorClosed(self): + numPlayers = self.countFullSeats() + if numPlayers > 0: + self._createInterior() + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.clearFullNow(seatIndex) + + else: + self.notify.warning('The elevator left, but was empty.') + self.fsm.request('closed') + + def requestExit(self, *args): + self.notify.debug('requestExit') + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if self.boardingParty and self.boardingParty.getGroupLeader(avId) and avId: + if avId == self.boardingParty.getGroupLeader(avId): + memberIds = self.boardingParty.getGroupMemberList(avId) + for memberId in memberIds: + member = simbase.air.doId2do.get(memberId) + if member: + if self.accepting: + self.acceptingExitersHandler(memberId) + else: + self.rejectingExitersHandler(memberId) + + else: + self.rejectingExitersHandler(avId) + else: + if av: + newArgs = (avId,) + args + if self.accepting: + self.acceptingExitersHandler(*newArgs) + else: + self.rejectingExitersHandler(*newArgs) + else: + self.notify.warning('avId: %s does not exist, but tried to exit an elevator' % avId) + return diff --git a/toontown/building/DistributedElevatorFSM.py b/toontown/building/DistributedElevatorFSM.py new file mode 100755 index 00000000..5e0cb737 --- /dev/null +++ b/toontown/building/DistributedElevatorFSM.py @@ -0,0 +1,460 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from ElevatorConstants import * +from ElevatorUtils import * +from direct.showbase import PythonUtil +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.distributed import DistributedObject +from direct.fsm import State +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from direct.task.Task import Task +from toontown.hood import ZoneUtil +from direct.fsm.FSM import FSM + +class DistributedElevatorFSM(DistributedObject.DistributedObject, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevator') + defaultTransitions = {'Off': ['Opening', 'Closed', 'Off'], + 'Opening': ['WaitEmpty', + 'WaitCountdown', + 'Opening', + 'Closing'], + 'WaitEmpty': ['WaitCountdown', 'Closing', 'Off'], + 'WaitCountdown': ['WaitEmpty', 'AllAboard', 'Closing'], + 'AllAboard': ['WaitEmpty', 'Closing'], + 'Closing': ['Closed', + 'WaitEmpty', + 'Closing', + 'Opening'], + 'Closed': ['Opening']} + id = 0 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.__init__(self, 'Elevator_%s_FSM' % self.id) + self.bldgRequest = None + self.toonRequests = {} + self.deferredSlots = [] + self.localToonOnBoard = 0 + self.boardedAvIds = {} + self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.ogg') + self.finalOpenSfx = None + self.closeSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_close.ogg') + self.elevatorFSM = None + self.finalCloseSfx = None + self.elevatorPoints = ElevatorPoints + self.type = ELEVATOR_NORMAL + self.countdownTime = ElevatorData[self.type]['countdown'] + self.isSetup = 0 + self.__preSetupState = None + self.bigElevator = 0 + self.offTrack = [None, + None, + None, + None] + self.boardingParty = None + return + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def setBoardingParty(self, party): + self.boardingParty = party + + def setupElevator(self): + collisionRadius = ElevatorData[self.type]['collRadius'] + self.elevatorSphere = CollisionSphere(0, 5, 0, collisionRadius) + self.elevatorSphere.setTangible(0) + self.elevatorSphereNode = CollisionNode(self.uniqueName('elevatorSphere')) + self.elevatorSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.elevatorSphereNode.addSolid(self.elevatorSphere) + self.elevatorSphereNodePath = self.getElevatorModel().attachNewNode(self.elevatorSphereNode) + self.elevatorSphereNodePath.hide() + self.elevatorSphereNodePath.reparentTo(self.getElevatorModel()) + self.elevatorSphereNodePath.stash() + self.boardedAvIds = {} + self.openDoors = getOpenInterval(self, self.leftDoor, self.rightDoor, self.openSfx, self.finalOpenSfx, self.type) + self.closeDoors = getCloseInterval(self, self.leftDoor, self.rightDoor, self.closeSfx, self.finalCloseSfx, self.type) + self.openDoors = Sequence(self.openDoors, Func(self.onDoorOpenFinish)) + self.closeDoors = Sequence(self.closeDoors, Func(self.onDoorCloseFinish)) + self.finishSetup() + + def finishSetup(self): + self.isSetup = 1 + if self.__preSetupState: + self.request(self.__preSetupState, 0) + self.__preSetupState = None + for slot in self.deferredSlots: + self.fillSlot(*slot) + + self.deferredSlots = [] + return + + def disable(self): + for track in self.offTrack: + if track: + if track.isPlaying(): + track.pause() + track = None + + if self.bldgRequest: + self.cr.relatedObjectMgr.abortRequest(self.bldgRequest) + self.bldgRequest = None + for request in self.toonRequests.values(): + self.cr.relatedObjectMgr.abortRequest(request) + + self.toonRequests = {} + if hasattr(self, 'openDoors'): + self.openDoors.pause() + if hasattr(self, 'closeDoors'): + self.closeDoors.pause() + self.request('off') + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + for track in self.offTrack: + if track: + if track.isPlaying(): + track.pause() + track = None + + self.ignoreAll() + if self.isSetup: + self.elevatorSphereNodePath.removeNode() + del self.elevatorSphereNodePath + del self.elevatorSphereNode + del self.elevatorSphere + del self.bldg + del self.leftDoor + del self.rightDoor + del self.openDoors + del self.closeDoors + del self.openSfx + del self.closeSfx + self.isSetup = 0 + DistributedObject.DistributedObject.delete(self) + return + + def setBldgDoId(self, bldgDoId): + self.bldgDoId = bldgDoId + self.bldgRequest = self.cr.relatedObjectMgr.requestObjects([bldgDoId], allCallback=self.gotBldg, timeout=2) + + def gotBldg(self, buildingList): + self.bldgRequest = None + self.bldg = buildingList[0] + if not self.bldg: + self.notify.error('setBldgDoId: elevator %d cannot find bldg %d!' % (self.doId, self.bldgDoId)) + return + self.setupElevator() + return + + def gotToon(self, index, avId, toonList): + request = self.toonRequests.get(index) + if request: + del self.toonRequests[index] + self.fillSlot(index, avId) + else: + self.notify.error('gotToon: already had got toon in slot %s.' % index) + + def setState(self, state, timestamp): + if self.isSetup: + self.request(state, globalClockDelta.localElapsedTime(timestamp)) + else: + self.__preSetupState = state + + def fillSlot0(self, avId): + self.fillSlot(0, avId) + + def fillSlot1(self, avId): + self.fillSlot(1, avId) + + def fillSlot2(self, avId): + self.fillSlot(2, avId) + + def fillSlot3(self, avId): + self.fillSlot(3, avId) + + def fillSlot4(self, avId): + self.fillSlot(4, avId) + + def fillSlot5(self, avId): + self.fillSlot(5, avId) + + def fillSlot6(self, avId): + self.fillSlot(6, avId) + + def fillSlot7(self, avId): + self.fillSlot(7, avId) + + def fillSlot(self, index, avId): + self.notify.debug('%s.fillSlot(%s, %s, ...)' % (self.doId, index, avId)) + request = self.toonRequests.get(index) + if request: + self.cr.relatedObjectMgr.abortRequest(request) + del self.toonRequests[index] + if avId == 0: + pass + elif avId not in self.cr.doId2do: + func = PythonUtil.Functor(self.gotToon, index, avId) + self.toonRequests[index] = self.cr.relatedObjectMgr.requestObjects([avId], allCallback=func) + elif not self.isSetup: + self.deferredSlots.append((index, avId)) + else: + if avId == base.localAvatar.getDoId(): + self.localToonOnBoard = 1 + elevator = self.getPlaceElevator() + elevator.fsm.request('boarding', [self.getElevatorModel()]) + elevator.fsm.request('boarded') + toon = self.cr.doId2do[avId] + toon.stopSmooth() + toon.setZ(self.getElevatorModel(), self.getScaledPoint(index)[2]) + toon.setShadowHeight(0) + if toon.isDisguised: + toon.suit.loop('walk') + animFunc = Func(toon.suit.loop, 'neutral') + else: + toon.setAnimState('run', 1.0) + animFunc = Func(toon.setAnimState, 'neutral', 1.0) + toon.headsUp(self.getElevatorModel(), apply(Point3, self.getScaledPoint(index))) + track = Sequence(LerpPosInterval(toon, TOON_BOARD_ELEVATOR_TIME * 0.75, apply(Point3, self.getScaledPoint(index)), other=self.getElevatorModel()), LerpHprInterval(toon, TOON_BOARD_ELEVATOR_TIME * 0.25, Point3(180, 0, 0), other=self.getElevatorModel()), animFunc, name=toon.uniqueName('fillElevator'), autoPause=1) + track.start() + self.boardedAvIds[avId] = index + + def emptySlot0(self, avId, timestamp): + self.emptySlot(0, avId, timestamp) + + def emptySlot1(self, avId, timestamp): + self.emptySlot(1, avId, timestamp) + + def emptySlot2(self, avId, timestamp): + self.emptySlot(2, avId, timestamp) + + def emptySlot3(self, avId, timestamp): + self.emptySlot(3, avId, timestamp) + + def emptySlot4(self, avId, timestamp): + self.emptySlot(4, avId, timestamp) + + def emptySlot5(self, avId, timestamp): + self.emptySlot(5, avId, timestamp) + + def emptySlot6(self, avId, timestamp): + self.emptySlot(6, avId, timestamp) + + def emptySlot7(self, avId, timestamp): + self.emptySlot(7, avId, timestamp) + + def notifyToonOffElevator(self, toon): + if self.cr: + toon.setAnimState('neutral', 1.0) + if toon == base.localAvatar: + print 'moving the local toon off the elevator' + doneStatus = {'where': 'exit'} + elevator = self.getPlaceElevator() + elevator.signalDone(doneStatus) + self.localToonOnBoard = 0 + else: + toon.startSmooth() + return + + def emptySlot(self, index, avId, timestamp): + print 'Emptying slot: %d for %d' % (index, avId) + if avId == 0: + pass + elif not self.isSetup: + newSlots = [] + for slot in self.deferredSlots: + if slot[0] != index: + newSlots.append(slot) + + self.deferredSlots = newSlots + elif avId in self.cr.doId2do: + if hasattr(self, 'clockNode'): + if timestamp < self.countdownTime and timestamp >= 0: + self.countdown(self.countdownTime - timestamp) + else: + self.countdown(self.countdownTime) + toon = self.cr.doId2do[avId] + toon.stopSmooth() + if toon.isDisguised: + toon.suit.loop('walk') + animFunc = Func(toon.suit.loop, 'neutral') + else: + toon.setAnimState('run', 1.0) + animFunc = Func(toon.setAnimState, 'neutral', 1.0) + if self.offTrack[index]: + if self.offTrack[index].isPlaying(): + self.offTrack[index].finish() + self.offTrack[index] = None + self.offTrack[index] = Sequence(LerpPosInterval(toon, TOON_EXIT_ELEVATOR_TIME, Point3(0, -ElevatorData[self.type]['collRadius'], 0), startPos=apply(Point3, self.getScaledPoint(index)), other=self.getElevatorModel()), animFunc, Func(self.notifyToonOffElevator, toon), name=toon.uniqueName('emptyElevator'), autoPause=1) + if avId == base.localAvatar.getDoId(): + messenger.send('exitElevator') + scale = base.localAvatar.getScale() + self.offTrack[index].append(Func(base.camera.setScale, scale)) + self.offTrack[index].start() + if avId in self.boardedAvIds: + del self.boardedAvIds[avId] + else: + self.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot exit the elevator!') + return + + def handleEnterSphere(self, collEntry): + self.notify.debug('Entering Elevator Sphere....') + if base.localAvatar.hp > 0: + self.cr.playGame.getPlace().detectedElevatorCollision(self) + toon = base.localAvatar + self.sendUpdate('requestBoard', []) + + def rejectBoard(self, avId, reason = 0): + print 'rejectBoard %s' % reason + if hasattr(base.localAvatar, 'elevatorNotifier'): + if reason == REJECT_PROMOTION: + base.localAvatar.elevatorNotifier.showMe(TTLocalizer.BossElevatorRejectMessage) + elif reason == REJECT_BLOCKED_ROOM: + base.localAvatar.elevatorNotifier.showMe(TTLocalizer.ElevatorBlockedRoom) + doneStatus = {'where': 'reject'} + elevator = self.getPlaceElevator() + if elevator: + elevator.signalDone(doneStatus) + + def timerTask(self, task): + countdownTime = int(task.duration - task.time) + timeStr = str(countdownTime) + if self.clockNode.getText() != timeStr: + self.clockNode.setText(timeStr) + if task.time >= task.duration: + return Task.done + else: + return Task.cont + + def countdown(self, duration): + countdownTask = Task(self.timerTask) + countdownTask.duration = duration + taskMgr.remove(self.uniqueName('elevatorTimerTask')) + return taskMgr.add(countdownTask, self.uniqueName('elevatorTimerTask')) + + def handleExitButton(self): + self.sendUpdate('requestExit') + + def enterWaitCountdown(self, ts): + self.elevatorSphereNodePath.unstash() + self.accept(self.uniqueName('enterelevatorSphere'), self.handleEnterSphere) + self.accept('elevatorExitButton', self.handleExitButton) + self.lastState = self.state + + def exitWaitCountdown(self): + self.elevatorSphereNodePath.stash() + self.ignore(self.uniqueName('enterelevatorSphere')) + self.ignore('elevatorExitButton') + self.ignore('localToonLeft') + taskMgr.remove(self.uniqueName('elevatorTimerTask')) + self.clock.removeNode() + del self.clock + del self.clockNode + + def enterClosing(self, ts): + if self.localToonOnBoard: + elevator = self.getPlaceElevator() + if self.closeDoors.isPlaying() or self.lastState == 'closed' or self.openDoors.isPlaying(): + self.doorsNeedToClose = 1 + else: + self.doorsNeedToClose = 0 + self.closeDoors.start(ts) + + def exitClosing(self): + pass + + def onDoorOpenFinish(self): + pass + + def onDoorCloseFinish(self): + for avId in self.boardedAvIds.keys(): + av = self.cr.doId2do.get(avId) + if av is not None: + if av.getParent().compareTo(self.getElevatorModel()) == 0: + av.detachNode() + + return + + def enterClosed(self, ts): + self.__doorsClosed(self.getZoneId()) + + def exitClosed(self): + pass + + def forceDoorsOpen(self): + openDoors(self.leftDoor, self.rightDoor) + + def forceDoorsClosed(self): + self.closeDoors.finish() + closeDoors(self.leftDoor, self.rightDoor) + + def enterOff(self): + self.lastState = self.state + + def exitOff(self): + pass + + def enterWaitEmpty(self, ts): + self.lastState = self.state + + def exitWaitEmpty(self): + pass + + def enterOpening(self, ts): + self.openDoors.start(ts) + self.lastState = self.state + + def exitOpening(self): + pass + + def startCountdownClock(self, countdownTime, ts): + self.clockNode = TextNode('elevatorClock') + self.clockNode.setFont(ToontownGlobals.getSignFont()) + self.clockNode.setAlign(TextNode.ACenter) + self.clockNode.setTextColor(0.5, 0.5, 0.5, 1) + self.clockNode.setText(str(int(countdownTime))) + self.clock = self.getElevatorModel().attachNewNode(self.clockNode) + self.clock.setPosHprScale(0, 4.4, 6.0, 0, 0, 0, 2.0, 2.0, 2.0) + if ts < countdownTime: + self.countdown(countdownTime - ts) + + def __doorsClosed(self, zoneId): + if self.localToonOnBoard: + self.localAvatar.stopGlitchKiller() + hoodId = ZoneUtil.getHoodId(zoneId) + loader = 'suitInterior' + where = 'suitInterior' + if base.cr.wantCogdominiums: + loader = 'cogdoInterior' + where = 'cogdoInterior' + doneStatus = {'loader': loader, + 'where': where, + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None} + elevator = self.elevatorFSM + del self.elevatorFSM + elevator.signalDone(doneStatus) + return + + def getElevatorModel(self): + self.notify.error('getElevatorModel: pure virtual -- inheritors must override') + + def getPlaceElevator(self): + place = self.cr.playGame.getPlace() + if not hasattr(place, 'elevator'): + self.notify.warning("Place was in state '%s' instead of Elevator." % place.state) + place.detectedElevatorCollision(self) + return None + return place.elevator + + def getScaledPoint(self, index): + point = self.elevatorPoints[index] + return point + + def getDestName(self): + return None diff --git a/toontown/building/DistributedElevatorFSMAI.py b/toontown/building/DistributedElevatorFSMAI.py new file mode 100755 index 00000000..6ca7ba5a --- /dev/null +++ b/toontown/building/DistributedElevatorFSMAI.py @@ -0,0 +1,242 @@ +from ElevatorConstants import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm.FSM import FSM +from direct.task import Task +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals + + +class DistributedElevatorFSMAI(DistributedObjectAI.DistributedObjectAI, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorFSMAI') + defaultTransitions = { + 'Off': [ + 'Opening', + 'Closed'], + 'Opening': [ + 'WaitEmpty', + 'WaitCountdown', + 'Opening', + 'Closing'], + 'WaitEmpty': [ + 'WaitCountdown', + 'Closing'], + 'WaitCountdown': [ + 'WaitEmpty', + 'AllAboard', + 'Closing'], + 'AllAboard': [ + 'WaitEmpty', + 'Closing'], + 'Closing': [ + 'Closed', + 'WaitEmpty', + 'Closing', + 'Opening'], + 'Closed': [ + 'Opening'] } + id = 0 + + def __init__(self, air, bldg, numSeats = 4): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + FSM.__init__(self, 'Elevator_%s_FSM' % self.id) + self.type = ELEVATOR_NORMAL + self.countdownTime = ElevatorData[self.type]['countdown'] + self.bldg = bldg + self.bldgDoId = bldg.getDoId() + self.seats = [] + for seat in xrange(numSeats): + self.seats.append(None) + self.accepting = 0 + + def delete(self): + del self.bldg + self.ignoreAll() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.start() + + def getBldgDoId(self): + return self.bldgDoId + + def findAvailableSeat(self): + for i in xrange(len(self.seats)): + if self.seats[i] == None: + return i + + def findAvatar(self, avId): + for i in xrange(len(self.seats)): + if self.seats[i] == avId: + return i + + def countFullSeats(self): + avCounter = 0 + for i in self.seats: + if i: + avCounter += 1 + return avCounter + + def countOpenSeats(self): + openSeats = 0 + for i in xrange(len(self.seats)): + if self.seats[i] == None: + openSeats += 1 + return openSeats + + def rejectingBoardersHandler(self, avId, reason = 0): + self.rejectBoarder(avId, reason) + + def rejectBoarder(self, avId, reason = 0): + self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId, reason]) + + def acceptingBoardersHandler(self, avId, reason = 0): + self.notify.debug('acceptingBoardersHandler') + seatIndex = self.findAvailableSeat() + if seatIndex == None: + self.rejectBoarder(avId, REJECT_NOSEAT) + else: + self.acceptBoarder(avId, seatIndex) + + def acceptBoarder(self, avId, seatIndex): + self.notify.debug('acceptBoarder') + if self.findAvatar(avId) != None: + return None + self.seats[seatIndex] = avId + self.timeOfBoarding = globalClock.getRealTime() + self.sendUpdate('fillSlot' + str(seatIndex), [avId]) + + def rejectingExitersHandler(self, avId): + self.rejectExiter(avId) + + def rejectExiter(self, avId): + pass + + def acceptingExitersHandler(self, avId): + self.acceptExiter(avId) + + def clearEmptyNow(self, seatIndex): + self.sendUpdate('emptySlot' + str(seatIndex), [0, 0, globalClockDelta.getRealNetworkTime()]) + + def clearFullNow(self, seatIndex): + avId = self.seats[seatIndex] + if avId == None: + self.notify.warning('Clearing an empty seat index: ' + str(seatIndex) + ' ... Strange...') + else: + self.seats[seatIndex] = None + self.sendUpdate('fillSlot' + str(seatIndex), [0]) + self.ignore(self.air.getAvatarExitEvent(avId)) + + def d_setState(self, state): + self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()]) + + def getState(self): + return self.state + + def avIsOKToBoard(self, av): + return self.accepting + + def checkBoard(self, av): + return 0 + + def requestBoard(self, *args): + self.notify.debug('requestBoard') + avId = self.air.getAvatarIdFromSender() + if self.findAvatar(avId) != None: + self.notify.warning('Ignoring multiple requests from %s to board.' % avId) + return None + + av = self.air.doId2do.get(avId) + if av: + newArgs = (avId,) + args + boardResponse = self.checkBoard(av) + newArgs = (avId,) + args + (boardResponse,) + if boardResponse == 0: + self.acceptingBoardersHandler(*newArgs) + else: + self.rejectingBoardersHandler(*newArgs) + else: + self.notify.warning('avid: %s does not exist, but tried to board an elevator' % avId) + + def requestExit(self, *args): + if hasattr(self, 'air'): + self.notify.debug('requestExit') + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av: + newArgs = (avId,) + args + if self.accepting: + self.acceptingExitersHandler(*newArgs) + else: + self.rejectingExitersHandler(*newArgs) + else: + self.notify.warning('avId: %s does not exist, but tried to exit an elevator' % avId) + + def start(self): + self.open() + + def enterOff(self): + self.accepting = 0 + self.timeOfBoarding = None + if hasattr(self, 'doId'): + for seatIndex in xrange(len(self.seats)): + taskMgr.remove(self.uniqueName('clearEmpty-' + str(seatIndex))) + + def exitOff(self): + self.accepting = 0 + + def open(self): + self.request('Opening') + + def enterOpening(self): + self.d_setState('Opening') + self.accepting = 0 + for seat in self.seats: + seat = None + + def exitOpening(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('opening-timer')) + + def enterWaitCountdown(self): + self.d_setState('WaitCountdown') + self.accepting = 1 + + def exitWaitCountdown(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('countdown-timer')) + + def enterAllAboard(self): + self.accepting = 0 + + def exitAllAboard(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('waitForAllAboard')) + + def enterClosing(self): + self.d_setState('Closing') + self.accepting = 0 + + def exitClosing(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('closing-timer')) + + def enterClosed(self): + if hasattr(self, 'doId'): + print self.doId + self.d_setState('Closed') + + def exitClosed(self): + pass + + def enterWaitEmpty(self): + for i in xrange(len(self.seats)): + self.seats[i] = None + print self.seats + self.d_setState('WaitEmpty') + self.accepting = 1 + + def exitWaitEmpty(self): + self.accepting = 0 \ No newline at end of file diff --git a/toontown/building/DistributedElevatorFloor.py b/toontown/building/DistributedElevatorFloor.py new file mode 100755 index 00000000..f96b3af9 --- /dev/null +++ b/toontown/building/DistributedElevatorFloor.py @@ -0,0 +1,325 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from ElevatorConstants import * +from ElevatorUtils import * +import DistributedElevatorFSM +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from direct.fsm.FSM import FSM +from direct.task import Task + +class DistributedElevatorFloor(DistributedElevatorFSM.DistributedElevatorFSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorFloor') + defaultTransitions = {'Off': ['Opening', 'Closed'], + 'Opening': ['WaitEmpty', + 'WaitCountdown', + 'Opening', + 'Closing'], + 'WaitEmpty': ['WaitCountdown', 'Closing'], + 'WaitCountdown': ['WaitEmpty', + 'AllAboard', + 'Closing', + 'WaitCountdown'], + 'AllAboard': ['WaitEmpty', 'Closing'], + 'Closing': ['Closed', + 'WaitEmpty', + 'Closing', + 'Opening'], + 'Closed': ['Opening']} + id = 0 + + def __init__(self, cr): + DistributedElevatorFSM.DistributedElevatorFSM.__init__(self, cr) + FSM.__init__(self, 'ElevatorFloor_%s_FSM' % self.id) + self.type = ELEVATOR_STAGE + self.countdownTime = ElevatorData[self.type]['countdown'] + self.nametag = None + self.currentFloor = -1 + self.isLocked = 0 + self.isEntering = 0 + self.doorOpeningFlag = 0 + self.doorsNeedToClose = 0 + self.wantState = 0 + self.latch = None + self.lastState = self.state + return + + def setupElevator2(self): + self.elevatorModel = loader.loadModel('phase_4/models/modules/elevator') + self.elevatorModel.reparentTo(hidden) + self.elevatorModel.setScale(1.05) + self.leftDoor = self.elevatorModel.find('**/left-door') + self.rightDoor = self.elevatorModel.find('**/right-door') + self.elevatorModel.find('**/light_panel').removeNode() + self.elevatorModel.find('**/light_panel_frame').removeNode() + if self.isSetup: + self.elevatorSphereNodePath.removeNode() + DistributedElevatorFSM.DistributedElevatorFSM.setupElevator(self) + + def setupElevator(self): + self.elevatorModel = loader.loadModel('phase_11/models/lawbotHQ/LB_ElevatorScaled') + if not self.elevatorModel: + self.notify.error('No Elevator Model in DistributedElevatorFloor.setupElevator. Please inform JML. Fool!') + self.leftDoor = self.elevatorModel.find('**/left-door') + if self.leftDoor.isEmpty(): + self.leftDoor = self.elevatorModel.find('**/left_door') + self.rightDoor = self.elevatorModel.find('**/right-door') + if self.rightDoor.isEmpty(): + self.rightDoor = self.elevatorModel.find('**/right_door') + DistributedElevatorFSM.DistributedElevatorFSM.setupElevator(self) + + def generate(self): + DistributedElevatorFSM.DistributedElevatorFSM.generate(self) + + def announceGenerate(self): + DistributedElevatorFSM.DistributedElevatorFSM.announceGenerate(self) + if self.latch: + self.notify.info('Setting latch in announce generate') + self.setLatch(self.latch) + + def __placeElevator(self): + self.notify.debug('PLACING ELEVATOR FOOL!!') + if self.isEntering: + elevatorNode = render.find('**/elevator_origin') + if not elevatorNode.isEmpty(): + self.elevatorModel.setPos(0, 0, 0) + self.elevatorModel.reparentTo(elevatorNode) + else: + self.notify.debug('NO NODE elevator_origin!!') + else: + elevatorNode = render.find('**/SlidingDoor') + if not elevatorNode.isEmpty(): + self.elevatorModel.setPos(0, 10, -15) + self.elevatorModel.setH(180) + self.elevatorModel.reparentTo(elevatorNode) + else: + self.notify.debug('NO NODE SlidingDoor!!') + + def setLatch(self, markerId): + self.notify.info('Setting latch') + marker = self.cr.doId2do.get(markerId) + self.latchRequest = self.cr.relatedObjectMgr.requestObjects([markerId], allCallback=self.set2Latch, timeout=5) + self.latch = markerId + + def set2Latch(self, taskMgrFooler = None): + if hasattr(self, 'cr'): + marker = self.cr.doId2do.get(self.latch) + if marker: + self.elevatorModel.reparentTo(marker) + return + taskMgr.doMethodLater(10.0, self._repart2Marker, 'elevatorfloor-markerReparent') + self.notify.warning('Using backup, do method later version of latch') + + def _repart2Marker(self, taskFoolio = 0): + if hasattr(self, 'cr'): + marker = self.cr.doId2do.get(self.latch) + if marker: + self.elevatorModel.reparentTo(marker) + else: + self.notify.error('could not find latch even in defered try') + + def setPos(self, x, y, z): + self.elevatorModel.setPos(x, y, z) + + def setH(self, H): + self.elevatorModel.setH(H) + + def delete(self): + DistributedElevatorFSM.DistributedElevatorFSM.delete(self) + self.elevatorModel.removeNode() + del self.elevatorModel + self.ignore('LawOffice_Spec_Loaded') + self.ignoreAll() + + def disable(self): + DistributedElevatorFSM.DistributedElevatorFSM.disable(self) + + def setEntranceId(self, entranceId): + self.entranceId = entranceId + if self.entranceId == 0: + self.elevatorModel.setPosHpr(62.74, -85.31, 0.0, 2.0, 0.0, 0.0) + elif self.entranceId == 1: + self.elevatorModel.setPosHpr(-162.25, 26.43, 0.0, 269.0, 0.0, 0.0) + else: + self.notify.error('Invalid entranceId: %s' % entranceId) + + def gotBldg(self, buildingList): + pass + + def setFloor(self, floorNumber): + if self.currentFloor >= 0: + if self.bldg.floorIndicator[self.currentFloor]: + self.bldg.floorIndicator[self.currentFloor].setColor(LIGHT_OFF_COLOR) + if floorNumber >= 0: + if self.bldg.floorIndicator[floorNumber]: + self.bldg.floorIndicator[floorNumber].setColor(LIGHT_ON_COLOR) + self.currentFloor = floorNumber + + def handleEnterSphere(self, collEntry): + self.cr.playGame.getPlace().detectedElevatorCollision(self) + + def handleEnterElevator(self): + if base.localAvatar.hp > 0: + toon = base.localAvatar + self.sendUpdate('requestBoard', []) + else: + self.notify.warning('Tried to board elevator with hp: %d' % base.localAvatar.hp) + + def enterWaitEmpty(self, ts): + self.lastState = self.state + self.elevatorSphereNodePath.unstash() + self.forceDoorsOpen() + self.accept(self.uniqueName('enterelevatorSphere'), self.handleEnterSphere) + self.accept(self.uniqueName('enterElevatorOK'), self.handleEnterElevator) + DistributedElevatorFSM.DistributedElevatorFSM.enterWaitEmpty(self, ts) + + def exitWaitEmpty(self): + self.lastState = self.state + self.elevatorSphereNodePath.stash() + self.ignore(self.uniqueName('enterelevatorSphere')) + self.ignore(self.uniqueName('enterElevatorOK')) + DistributedElevatorFSM.DistributedElevatorFSM.exitWaitEmpty(self) + + def enterWaitCountdown(self, ts): + self.lastState = self.state + DistributedElevatorFSM.DistributedElevatorFSM.enterWaitCountdown(self, ts) + self.forceDoorsOpen() + self.accept(self.uniqueName('enterElevatorOK'), self.handleEnterElevator) + self.startCountdownClock(self.countdownTime, ts) + + def exitWaitCountdown(self): + self.lastState = self.state + self.ignore(self.uniqueName('enterElevatorOK')) + DistributedElevatorFSM.DistributedElevatorFSM.exitWaitCountdown(self) + + def enterClosing(self, ts): + self.lastState = self.state + taskMgr.doMethodLater(1.0, self._delayIris, 'delayedIris') + DistributedElevatorFSM.DistributedElevatorFSM.enterClosing(self, ts) + + def _delayIris(self, tskfooler = 0): + base.transitions.irisOut(1.0) + base.localAvatar.pauseGlitchKiller() + return Task.done + + def kickToonsOut(self): + if not self.localToonOnBoard: + zoneId = self.cr.playGame.hood.hoodId + self.cr.playGame.getPlace().fsm.request('teleportOut', [{'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1}]) + return + + def exitClosing(self): + self.lastState = self.state + DistributedElevatorFSM.DistributedElevatorFSM.exitClosing(self) + + def enterClosed(self, ts): + self.lastState = self.state + self.forceDoorsClosed() + self.__doorsClosed(self.getZoneId()) + + def exitClosed(self): + self.lastState = self.state + DistributedElevatorFSM.DistributedElevatorFSM.exitClosed(self) + + def enterOff(self): + self.lastState = self.state + if self.wantState == 'closed': + self.demand('Closing') + elif self.wantState == 'waitEmpty': + self.demand('WaitEmpty') + DistributedElevatorFSM.DistributedElevatorFSM.enterOff(self) + + def exitOff(self): + self.lastState = self.state + DistributedElevatorFSM.DistributedElevatorFSM.exitOff(self) + + def enterOpening(self, ts): + self.lastState = self.state + DistributedElevatorFSM.DistributedElevatorFSM.enterOpening(self, ts) + + def exitOpening(self): + DistributedElevatorFSM.DistributedElevatorFSM.exitOpening(self) + self.kickEveryoneOut() + + def getZoneId(self): + return 0 + + def setBldgDoId(self, bldgDoId): + self.bldg = None + self.setupElevator() + return + + def getElevatorModel(self): + return self.elevatorModel + + def kickEveryoneOut(self): + for avId, slot in self.boardedAvIds.items(): + self.emptySlot(slot, avId, globalClockDelta.getRealNetworkTime()) + if avId == base.localAvatar.doId: + pass + + def __doorsClosed(self, zoneId): + pass + + def onDoorCloseFinish(self): + pass + + def setLocked(self, locked): + self.isLocked = locked + if locked: + if self.state == 'WaitEmpty': + self.request('Closing') + if self.countFullSeats() == 0: + self.wantState = 'closed' + else: + self.wantState = 'opening' + else: + self.wantState = 'waitEmpty' + if self.state == 'Closed': + self.request('Opening') + + def getLocked(self): + return self.isLocked + + def setEntering(self, entering): + self.isEntering = entering + + def getEntering(self): + return self.isEntering + + def forceDoorsOpen(self): + openDoors(self.leftDoor, self.rightDoor) + + def forceDoorsClosed(self): + if self.openDoors.isPlaying(): + self.doorsNeedToClose = 1 + else: + self.closeDoors.finish() + closeDoors(self.leftDoor, self.rightDoor) + + def enterOff(self): + self.lastState = self.state + + def exitOff(self): + pass + + def setLawOfficeInteriorZone(self, zoneId): + if self.localToonOnBoard: + hoodId = self.cr.playGame.hood.hoodId + doneStatus = {'loader': 'cogHQLoader', + 'where': 'factoryInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'hoodId': hoodId} + self.cr.playGame.getPlace().elevator.signalDone(doneStatus) diff --git a/toontown/building/DistributedElevatorFloorAI.py b/toontown/building/DistributedElevatorFloorAI.py new file mode 100755 index 00000000..dcd7029b --- /dev/null +++ b/toontown/building/DistributedElevatorFloorAI.py @@ -0,0 +1,286 @@ +import DistributedElevatorFSMAI +from ElevatorConstants import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.fsm.FSM import FSM +from direct.task import Task +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals + + +class DistributedElevatorFloorAI(DistributedElevatorFSMAI.DistributedElevatorFSMAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorFloorAI') + defaultTransitions = { + 'Off': [ + 'Opening', + 'Closed'], + 'Opening': [ + 'WaitEmpty', + 'WaitCountdown', + 'Opening', + 'Closing'], + 'WaitEmpty': [ + 'WaitCountdown', + 'Closing', + 'WaitEmpty'], + 'WaitCountdown': [ + 'WaitEmpty', + 'AllAboard', + 'Closing', + 'WaitCountdown'], + 'AllAboard': [ + 'WaitEmpty', + 'Closing'], + 'Closing': [ + 'Closed', + 'WaitEmpty', + 'Closing', + 'Opening'], + 'Closed': [ + 'Opening'] } + id = 0 + + def __init__(self, air, lawOfficeId, bldg, avIds, markerId = None, numSeats = 4): + DistributedElevatorFSMAI.DistributedElevatorFSMAI.__init__(self, air, bldg, numSeats) + FSM.__init__(self, 'ElevatorFloor_%s_FSM' % self.id) + self.type = ELEVATOR_STAGE + self.countdownTime = ElevatorData[self.type]['countdown'] + self.lawOfficeId = lawOfficeId + self.avIds = avIds + self.isEntering = 0 + self.isLocked = 0 + self.setLocked(0) + self.wantState = None + self.latchRoom = None + self.setLatch(markerId) + self.zoneId = bldg.zoneId + + def generate(self): + DistributedElevatorFSMAI.DistributedElevatorFSMAI.generate(self) + + def generateWithRequired(self, zoneId): + self.zoneId = zoneId + DistributedElevatorFSMAI.DistributedElevatorFSMAI.generateWithRequired(self, self.zoneId) + + def delete(self): + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.clearFullNow(seatIndex) + self.clearEmptyNow(seatIndex) + DistributedElevatorFSMAI.DistributedElevatorFSMAI.delete(self) + + def getEntranceId(self): + return self.entranceId + + def d_setFloor(self, floorNumber): + self.sendUpdate('setFloor', [floorNumber]) + + def avIsOKToBoard(self, av): + if av.hp > 0 and self.accepting: + pass + return not (self.isLocked) + + def acceptBoarder(self, avId, seatIndex): + DistributedElevatorFSMAI.DistributedElevatorFSMAI.acceptBoarder(self, avId, seatIndex) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self._DistributedElevatorFloorAI__handleUnexpectedExit, extraArgs = [ + avId]) + if self.state == 'WaitEmpty' and self.countFullSeats() < self.countAvsInZone(): + self.request('WaitCountdown') + self.bldg.elevatorAlert(avId) + elif self.state in ('WaitCountdown', 'WaitEmpty') and self.countFullSeats() >= self.countAvsInZone(): + taskMgr.doMethodLater(TOON_BOARD_ELEVATOR_TIME, self.goAllAboard, self.quickBoardTask) + + def countAvsInZone(self): + matchingZones = 0 + for avId in self.bldg.avIds: + av = self.air.doId2do.get(avId) + if av: + if av.zoneId == self.bldg.zoneId: + matchingZones += 1 + return matchingZones + + def goAllAboard(self, throwAway = 1): + self.request('Closing') + return Task.done + + def _DistributedElevatorFloorAI__handleUnexpectedExit(self, avId): + self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly') + seatIndex = self.findAvatar(avId) + if seatIndex == None: + pass + self.clearFullNow(seatIndex) + self.clearEmptyNow(seatIndex) + if self.countFullSeats() == 0: + self.request('WaitEmpty') + + def acceptExiter(self, avId): + seatIndex = self.findAvatar(avId) + if seatIndex == None: + pass + self.clearFullNow(seatIndex) + self.resetCountdown() + self.sendUpdate('emptySlot' + str(seatIndex), [avId, globalClockDelta.getRealNetworkTime()]) + if self.countFullSeats() == 0: + self.request('WaitEmpty') + taskMgr.doMethodLater(TOON_EXIT_ELEVATOR_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs = (seatIndex,)) + + def enterOpening(self): + self.d_setState('Opening') + DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterOpening(self) + taskMgr.doMethodLater(ElevatorData[ELEVATOR_NORMAL]['openTime'], self.waitEmptyTask, self.uniqueName('opening-timer')) + + def exitOpening(self): + DistributedElevatorFSMAI.DistributedElevatorFSMAI.exitOpening(self) + if self.isLocked: + self.wantState = 'closed' + if self.wantState == 'closed': + self.demand('Closing') + + def waitEmptyTask(self, task): + self.request('WaitEmpty') + return Task.done + + def enterWaitEmpty(self): + self.lastState = self.state + for i in xrange(len(self.seats)): + self.seats[i] = None + print self.seats + if self.wantState == 'closed': + self.demand('Closing') + else: + self.d_setState('WaitEmpty') + self.accepting = 1 + + def enterWaitCountdown(self): + self.lastState = self.state + DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterWaitCountdown(self) + taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer')) + if self.lastState == 'WaitCountdown': + pass + + def timeToGoTask(self, task): + if self.countFullSeats() > 0: + self.request('AllAboard') + else: + self.request('WaitEmpty') + return Task.done + + def resetCountdown(self): + taskMgr.remove(self.uniqueName('countdown-timer')) + taskMgr.doMethodLater(self.countdownTime, self.timeToGoTask, self.uniqueName('countdown-timer')) + + def enterAllAboard(self): + DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterAllAboard(self) + currentTime = globalClock.getRealTime() + elapsedTime = currentTime - self.timeOfBoarding + self.notify.debug('elapsed time: ' + str(elapsedTime)) + waitTime = max(TOON_BOARD_ELEVATOR_TIME - elapsedTime, 0) + taskMgr.doMethodLater(waitTime, self.closeTask, self.uniqueName('waitForAllAboard')) + + def closeTask(self, task): + if self.countFullSeats() >= 1: + self.request('Closing') + else: + self.request('WaitEmpty') + return Task.done + + def enterClosing(self): + if self.countFullSeats() > 0: + self.sendUpdate('kickToonsOut') + DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterClosing(self) + taskMgr.doMethodLater(ElevatorData[ELEVATOR_STAGE]['closeTime'], self.elevatorClosedTask, self.uniqueName('closing-timer')) + self.d_setState('Closing') + + def elevatorClosedTask(self, task): + self.elevatorClosed() + return Task.done + + def elevatorClosed(self): + if self.isLocked: + self.request('Closed') + return None + numPlayers = self.countFullSeats() + if numPlayers > 0: + players = [] + for i in self.seats: + if i not in [None, 0]: + players.append(i) + continue + sittingAvIds = [] + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + sittingAvIds.append(avId) + continue + for avId in self.avIds: + if avId not in sittingAvIds: + continue + self.bldg.startNextFloor() + else: + self.notify.warning('The elevator left, but was empty.') + self.request('Closed') + + def setLocked(self, locked): + self.isLocked = locked + if locked: + if self.state == 'WaitEmpty': + self.request('Closing') + + if self.countFullSeats() == 0: + self.wantState = 'closed' + else: + self.wantState = 'opening' + else: + self.wantState = 'waitEmpty' + if self.state == 'Closed': + self.request('Opening') + + def getLocked(self): + return self.isLocked + + def unlock(self): + if self.isLocked: + self.setLocked(0) + + def lock(self): + if not self.isLocked: + self.setLocked(1) + + def start(self): + self.quickBoardTask = self.uniqueName('quickBoard') + self.request('Opening') + + def beClosed(self): + pass + + def setEntering(self, entering): + self.isEntering = entering + + def getEntering(self): + return self.isEntering + + def enterClosed(self): + DistributedElevatorFSMAI.DistributedElevatorFSMAI.enterClosed(self) + if self.wantState == 'closed': + pass + self.demand('Opening') + + def enterOff(self): + self.lastState = self.state + if self.wantState == 'closed': + self.demand('Closing') + elif self.wantState == 'waitEmpty': + self.demand('WaitEmpty') + + def setPos(self, pointPos): + self.sendUpdate('setPos', [pointPos[0], pointPos[1], pointPos[2]]) + + def setH(self, H): + self.sendUpdate('setH', [H]) + + def setLatch(self, markerId): + self.latch = markerId + + def getLatch(self): + return self.latch diff --git a/toontown/building/DistributedElevatorInt.py b/toontown/building/DistributedElevatorInt.py new file mode 100755 index 00000000..4e6f65d3 --- /dev/null +++ b/toontown/building/DistributedElevatorInt.py @@ -0,0 +1,57 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from ElevatorConstants import * +from ElevatorUtils import * +import DistributedElevator +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer + +from toontown.cogdominium.CogdoInterior import CogdoInterior + +class DistributedElevatorInt(DistributedElevator.DistributedElevator): + + def __init__(self, cr): + DistributedElevator.DistributedElevator.__init__(self, cr) + self.countdownTime = base.config.GetFloat('int-elevator-timeout', INTERIOR_ELEVATOR_COUNTDOWN_TIME) + + def setupElevator(self): + self.leftDoor = self.bldg.leftDoorOut + self.rightDoor = self.bldg.rightDoorOut + DistributedElevator.DistributedElevator.setupElevator(self) + + if isinstance(base.cr.playGame.getPlace(), CogdoInterior): + self.elevatorSphereNodePath.setY(self.elevatorSphereNodePath, -3) + + def forcedExit(self, avId): + target_sz = base.localAvatar.defaultZone + base.cr.playGame.getPlace().fsm.request('teleportOut', [{'loader': ZoneUtil.getLoaderName(target_sz), + 'where': ZoneUtil.getWhereName(target_sz, 1), + 'how': 'teleportIn', + 'hoodId': target_sz, + 'zoneId': target_sz, + 'shardId': None, + 'avId': -1}], force=1) + return + + def enterWaitCountdown(self, ts): + DistributedElevator.DistributedElevator.enterWaitCountdown(self, ts) + self.acceptOnce('localToonLeft', self.__handleTeleportOut) + self.startCountdownClock(self.countdownTime, ts) + + def __handleTeleportOut(self): + self.sendUpdate('requestBuildingExit', []) + + def exitWaitCountdown(self): + self.ignore('localToonLeft') + DistributedElevator.DistributedElevator.exitWaitCountdown(self) + + def getZoneId(self): + return self.bldg.getZoneId() + + def getElevatorModel(self): + return self.bldg.elevatorModelOut diff --git a/toontown/building/DistributedElevatorIntAI.py b/toontown/building/DistributedElevatorIntAI.py new file mode 100755 index 00000000..d8e6dfe8 --- /dev/null +++ b/toontown/building/DistributedElevatorIntAI.py @@ -0,0 +1,137 @@ +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from ElevatorConstants import * +import copy +import DistributedElevatorAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattleBase + +class DistributedElevatorIntAI(DistributedElevatorAI.DistributedElevatorAI): + + def __init__(self, air, bldg, avIds): + DistributedElevatorAI.DistributedElevatorAI.__init__(self, air, bldg) + self.countdownTime = simbase.config.GetFloat('int-elevator-timeout', INTERIOR_ELEVATOR_COUNTDOWN_TIME) + self.avIds = copy.copy(avIds) + for avId in avIds: + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleAllAvsUnexpectedExit, extraArgs = [ avId]) + + def checkBoard(self, av): + result = 0 + if av.doId not in self.avIds: + result = REJECT_NOSEAT + else: + result = DistributedElevatorAI.DistributedElevatorAI.checkBoard(self, av) + return result + + def acceptBoarder(self, avId, seatIndex, wantBoardingShow = 0): + DistributedElevatorAI.DistributedElevatorAI.acceptBoarder(self, avId, seatIndex, wantBoardingShow) + self.__closeIfNecessary() + + def __closeIfNecessary(self): + numFullSeats = self.countFullSeats() + if not numFullSeats <= len(self.avIds): + self.notify.warning('we are about to crash. self.seats=%s self.avIds=%s' % (self.seats, self.avIds)) + + if numFullSeats == len(self.avIds): + self.fsm.request('allAboard') + + def __handleAllAvsUnexpectedExit(self, avId): + self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly') + seatIndex = self.findAvatar(avId) + avIdCount = self.avIds.count(avId) + if avIdCount == 1: + self.avIds.remove(avId) + elif avIdCount == 0: + self.notify.warning("Strange... %d exited unexpectedly, but I don't have them on my list." % avId) + else: + self.notify.error('This list is screwed up! %s' % self.avIds) + if seatIndex == None: + self.notify.debug('%d is not boarded, but exited' % avId) + else: + self.clearFullNow(seatIndex) + self.clearEmptyNow(seatIndex) + self.__closeIfNecessary() + + def acceptExiter(self, avId): + seatIndex = self.findAvatar(avId) + if seatIndex == None: + pass + self.clearFullNow(seatIndex) + self.resetCountdown() + self.sendUpdate('emptySlot' + str(seatIndex), [avId, + globalClockDelta.getRealNetworkTime(), self.countdownTime]) + taskMgr.doMethodLater(TOON_EXIT_ELEVATOR_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs = (seatIndex,)) + + def d_forcedExit(self, avId): + self.sendUpdateToAvatarId(avId, 'forcedExit', [avId]) + + def requestBuildingExit(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('requestBuildingExit from %d' % avId) + if self.accepting: + if avId in self.avIds: + self.avIds.remove(avId) + self.__closeIfNecessary() + else: + self.notify.warning('avId: %s not known, but tried to exit the building' % avId) + + def enterOpening(self): + DistributedElevatorAI.DistributedElevatorAI.enterOpening(self) + taskMgr.doMethodLater(ElevatorData[ELEVATOR_NORMAL]['openTime'], self.waitCountdownTask, self.uniqueName('opening-timer')) + + def waitCountdownTask(self, task): + self.fsm.request('waitCountdown') + return Task.done + + def enterWaitCountdown(self): + DistributedElevatorAI.DistributedElevatorAI.enterWaitCountdown(self) + taskMgr.doMethodLater(self.countdownTime + BattleBase.SERVER_BUFFER_TIME, self.timeToGoTask, self.uniqueName('countdown-timer')) + + def timeToGoTask(self, task): + self.allAboard() + return Task.done + + def allAboard(self): + if len(self.avIds) == 0: + self.bldg.handleAllAboard([None, None, None, None]) + else: + self.fsm.request('allAboard') + + def enterAllAboard(self): + for avId in self.avIds: + if self.findAvatar(avId) == None: + self.d_forcedExit(avId) + continue + DistributedElevatorAI.DistributedElevatorAI.enterAllAboard(self) + if self.timeOfBoarding != None: + currentTime = globalClock.getRealTime() + elapsedTime = currentTime - self.timeOfBoarding + self.notify.debug('elapsed time: ' + str(elapsedTime)) + waitTime = max(TOON_BOARD_ELEVATOR_TIME - elapsedTime, 0) + taskMgr.doMethodLater(waitTime, self.closeTask, self.uniqueName('waitForAllAboard')) + else: + self.fsm.request('closing') + + def closeTask(self, task): + self.fsm.request('closing') + return Task.done + + def enterClosing(self): + DistributedElevatorAI.DistributedElevatorAI.enterClosing(self) + taskMgr.doMethodLater(ElevatorData[ELEVATOR_NORMAL]['closeTime'] + BattleBase.SERVER_BUFFER_TIME, self.elevatorClosedTask, self.uniqueName('closing-timer')) + + def elevatorClosedTask(self, task): + self.fsm.request('closed') + return Task.done + + def __doorsClosed(self): + self.bldg.handleAllAboard(self.seats) + self.fsm.request('closed') + + def enterClosed(self): + DistributedElevatorAI.DistributedElevatorAI.enterClosed(self) + self.__doorsClosed() diff --git a/toontown/building/DistributedGagshopInterior.py b/toontown/building/DistributedGagshopInterior.py new file mode 100755 index 00000000..f4376385 --- /dev/null +++ b/toontown/building/DistributedGagshopInterior.py @@ -0,0 +1,99 @@ +from toontown.toonbase.ToonBaseGlobal import * +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +import random +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +import ToonInteriorColors +from toontown.dna.DNAParser import * +from toontown.hood import ZoneUtil +from toontown.toon.DistributedNPCToonBase import DistributedNPCToonBase + +class DistributedGagshopInterior(DistributedObject.DistributedObject): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.dnaStore = cr.playGame.dnaStore + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.setup() + + def randomDNAItem(self, category, findFunc): + codeCount = self.dnaStore.getNumCatalogCodes(category) + index = self.randomGenerator.randint(0, codeCount - 1) + code = self.dnaStore.getCatalogCode(category, index) + return findFunc(code) + + def replaceRandomInModel(self, model): + baseTag = 'random_' + npc = model.findAllMatches('**/' + baseTag + '???_*') + for i in xrange(npc.getNumPaths()): + np = npc.getPath(i) + name = np.getName() + b = len(baseTag) + category = name[b + 4:] + key1 = name[b] + key2 = name[b + 1] + if key1 == 'm': + model = self.randomDNAItem(category, self.dnaStore.findNode) + newNP = model.copyTo(np) + if key2 == 'r': + self.replaceRandomInModel(newNP) + elif key1 == 't': + texture = self.randomDNAItem(category, self.dnaStore.findTexture) + np.setTexture(texture, 100) + newNP = np + if key2 == 'c': + if category == 'TI_wallpaper' or category == 'TI_wallpaper_border': + self.randomGenerator.seed(self.zoneId) + newNP.setColorScale(self.randomGenerator.choice(self.colors[category])) + else: + newNP.setColorScale(self.randomGenerator.choice(self.colors[category])) + + def setZoneIdAndBlock(self, zoneId, block): + self.zoneId = zoneId + self.block = block + + def chooseDoor(self): + doorModelName = 'door_double_round_ul' + if doorModelName[-1:] == 'r': + doorModelName = doorModelName[:-1] + 'l' + else: + doorModelName = doorModelName[:-1] + 'r' + door = self.dnaStore.findNode(doorModelName) + return door + + def setup(self): + self.dnaStore = base.cr.playGame.dnaStore + self.randomGenerator = random.Random() + self.randomGenerator.seed(self.zoneId) + self.interior = loader.loadModel('phase_4/models/modules/gagShop_interior') + self.interior.reparentTo(render) + hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId) + self.colors = ToonInteriorColors.colors[hoodId] + self.replaceRandomInModel(self.interior) + door = self.chooseDoor() + doorOrigin = render.find('**/door_origin;+s') + doorNP = door.copyTo(doorOrigin) + doorOrigin.setScale(0.8, 0.8, 0.8) + doorOrigin.setPos(doorOrigin, 0, -0.025, 0) + doorColor = self.randomGenerator.choice(self.colors['TI_door']) + setupDoor(doorNP, self.interior, doorOrigin, self.dnaStore, str(self.block), doorColor) + doorFrame = doorNP.find('door_*_flat') + doorFrame.wrtReparentTo(self.interior) + doorFrame.setColor(doorColor) + del self.colors + del self.dnaStore + del self.randomGenerator + self.interior.flattenMedium() + for npcToon in self.cr.doFindAllInstances(DistributedNPCToonBase): + npcToon.initToonState() + + def disable(self): + self.interior.removeNode() + del self.interior + DistributedObject.DistributedObject.disable(self) diff --git a/toontown/building/DistributedGagshopInteriorAI.py b/toontown/building/DistributedGagshopInteriorAI.py new file mode 100755 index 00000000..f6be0aa6 --- /dev/null +++ b/toontown/building/DistributedGagshopInteriorAI.py @@ -0,0 +1,11 @@ +from direct.distributed import DistributedObjectAI + + +class DistributedGagshopInteriorAI(DistributedObjectAI.DistributedObjectAI): + def __init__(self, block, air, zoneId): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.block = block + self.zoneId = zoneId + + def getZoneIdAndBlock(self): + return [self.zoneId, self.block] diff --git a/toontown/building/DistributedHQInterior.py b/toontown/building/DistributedHQInterior.py new file mode 100755 index 00000000..7fad04df --- /dev/null +++ b/toontown/building/DistributedHQInterior.py @@ -0,0 +1,214 @@ +import cPickle +import random +import ToonInteriorColors +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.task.Task import Task +from panda3d.core import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.dna.DNAParser import * +from toontown.toon.DistributedNPCToonBase import DistributedNPCToonBase + + +class DistributedHQInterior(DistributedObject.DistributedObject): + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.dnaStore = cr.playGame.dnaStore + self.leaderAvIds = [] + self.leaderNames = [] + self.leaderScores = [] + self.numLeaders = 10 + self.tutorial = 0 + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.interior = loader.loadModel('phase_3.5/models/modules/HQ_interior') + self.interior.reparentTo(render) + self.interior.find('**/cream').hide() + self.buildLeaderBoard() + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.setupDoors() + self.interior.flattenMedium() + emptyBoard = self.interior.find('**/empty_board') + self.leaderBoard.reparentTo(emptyBoard.getChild(0)) + for npcToon in self.cr.doFindAllInstances(DistributedNPCToonBase): + npcToon.initToonState() + + def setTutorial(self, flag): + if self.tutorial == flag: + return + else: + self.tutorial = flag + if self.tutorial: + self.interior.find('**/periscope').hide() + self.interior.find('**/speakers').hide() + else: + self.interior.find('**/periscope').show() + self.interior.find('**/speakers').show() + + def setZoneIdAndBlock(self, zoneId, block): + self.zoneId = zoneId + self.block = block + + def buildLeaderBoard(self): + self.leaderBoard = hidden.attachNewNode('leaderBoard') + self.leaderBoard.setPosHprScale(0.1, 0, 4.5, 90, 0, 0, 0.9, 0.9, 0.9) + z = 0 + row = self.buildTitleRow() + row.reparentTo(self.leaderBoard) + row.setPos(0, 0, z) + z -= 1 + self.nameTextNodes = [] + self.scoreTextNodes = [] + self.trophyStars = [] + for i in xrange(self.numLeaders): + (row, nameText, scoreText, trophyStar) = self.buildLeaderRow() + self.nameTextNodes.append(nameText) + self.scoreTextNodes.append(scoreText) + self.trophyStars.append(trophyStar) + row.reparentTo(self.leaderBoard) + row.setPos(0, 0, z) + z -= 1 + + def updateLeaderBoard(self): + taskMgr.remove(self.uniqueName('starSpinHQ')) + for i in xrange(len(self.leaderNames)): + name = self.leaderNames[i] + score = self.leaderScores[i] + self.nameTextNodes[i].setText(name) + self.scoreTextNodes[i].setText(str(score)) + self.updateTrophyStar(self.trophyStars[i], score) + for i in xrange(len(self.leaderNames), self.numLeaders): + self.nameTextNodes[i].setText('-') + self.scoreTextNodes[i].setText('-') + self.trophyStars[i].hide() + + def buildTitleRow(self): + row = hidden.attachNewNode('leaderRow') + nameText = TextNode('titleRow') + nameText.setFont(ToontownGlobals.getSignFont()) + nameText.setAlign(TextNode.ACenter) + nameText.setTextColor(0.5, 0.75, 0.7, 1) + nameText.setText(TTLocalizer.LeaderboardTitle) + namePath = row.attachNewNode(nameText) + namePath.setPos(0, 0, 0) + return row + + def buildLeaderRow(self): + row = hidden.attachNewNode('leaderRow') + nameText = TextNode('nameText') + nameText.setFont(ToontownGlobals.getToonFont()) + nameText.setAlign(TextNode.ALeft) + nameText.setTextColor(1, 1, 1, 0.7) + nameText.setText('-') + namePath = row.attachNewNode(nameText) + namePath.setPos(*TTLocalizer.DHQInamePathPos) + namePath.setScale(TTLocalizer.DHQInamePath) + scoreText = TextNode('scoreText') + scoreText.setFont(ToontownGlobals.getToonFont()) + scoreText.setAlign(TextNode.ARight) + scoreText.setTextColor(1, 1, 0.1, 0.7) + scoreText.setText('-') + scorePath = row.attachNewNode(scoreText) + scorePath.setPos(*TTLocalizer.DHQIscorePathPos) + trophyStar = self.buildTrophyStar() + trophyStar.reparentTo(row) + return (row, nameText, scoreText, trophyStar) + + def setLeaderBoard(self, leaderData): + (avIds, names, scores) = cPickle.loads(leaderData) + self.notify.debug('setLeaderBoard: avIds: %s, names: %s, scores: %s' % (avIds, names, scores)) + self.leaderAvIds = avIds + self.leaderNames = names + self.leaderScores = scores + self.updateLeaderBoard() + + def chooseDoor(self): + doorModelName = 'door_double_round_ul' + if doorModelName[-1:] == 'r': + doorModelName = doorModelName[:-1] + 'l' + else: + doorModelName = doorModelName[:-1] + 'r' + door = self.dnaStore.findNode(doorModelName) + return door + + def setupDoors(self): + self.randomGenerator = random.Random() + self.randomGenerator.seed(self.zoneId) + self.colors = ToonInteriorColors.colors[ToontownCentral] + door = self.chooseDoor() + doorOrigins = render.findAllMatches('**/door_origin*') + numDoorOrigins = doorOrigins.getNumPaths() + for npIndex in xrange(numDoorOrigins): + doorOrigin = doorOrigins[npIndex] + doorOriginNPName = doorOrigin.getName() + doorOriginIndexStr = doorOriginNPName[len('door_origin_'):] + newNode = ModelNode('door_' + doorOriginIndexStr) + newNodePath = NodePath(newNode) + newNodePath.reparentTo(self.interior) + doorNP = door.copyTo(newNodePath) + doorOrigin.setScale(0.8, 0.8, 0.8) + doorOrigin.setPos(doorOrigin, 0, -0.025, 0) + doorColor = self.randomGenerator.choice(self.colors['TI_door']) + triggerId = str(self.block) + '_' + doorOriginIndexStr + setupDoor(doorNP, newNodePath, doorOrigin, self.dnaStore, triggerId, doorColor) + doorFrame = doorNP.find('door_*_flat') + doorFrame.setColor(doorColor) + del self.dnaStore + del self.randomGenerator + + def disable(self): + self.leaderBoard.removeNode() + del self.leaderBoard + self.interior.removeNode() + del self.interior + del self.nameTextNodes + del self.scoreTextNodes + del self.trophyStars + taskMgr.remove(self.uniqueName('starSpinHQ')) + DistributedObject.DistributedObject.disable(self) + + def buildTrophyStar(self): + trophyStar = loader.loadModel('phase_3.5/models/gui/name_star') + trophyStar.hide() + trophyStar.setPos(*TTLocalizer.DHQItrophyStarPos) + return trophyStar + + def updateTrophyStar(self, trophyStar, score): + scale = 0.8 + if score >= ToontownGlobals.TrophyStarLevels[4]: + trophyStar.show() + trophyStar.setScale(scale) + trophyStar.setColor(ToontownGlobals.TrophyStarColors[4]) + if score >= ToontownGlobals.TrophyStarLevels[5]: + task = taskMgr.add(self.__starSpin, self.uniqueName('starSpinHQ')) + task.trophyStarSpeed = 15 + task.trophyStar = trophyStar + elif score >= ToontownGlobals.TrophyStarLevels[2]: + trophyStar.show() + trophyStar.setScale(0.75 * scale) + trophyStar.setColor(ToontownGlobals.TrophyStarColors[2]) + if score >= ToontownGlobals.TrophyStarLevels[3]: + task = taskMgr.add(self.__starSpin, self.uniqueName('starSpinHQ')) + task.trophyStarSpeed = 10 + task.trophyStar = trophyStar + elif score >= ToontownGlobals.TrophyStarLevels[0]: + trophyStar.show() + trophyStar.setScale(0.75 * scale) + trophyStar.setColor(ToontownGlobals.TrophyStarColors[0]) + if score >= ToontownGlobals.TrophyStarLevels[1]: + task = taskMgr.add(self.__starSpin, self.uniqueName('starSpinHQ')) + task.trophyStarSpeed = 8 + task.trophyStar = trophyStar + else: + trophyStar.hide() + + def __starSpin(self, task): + now = globalClock.getFrameTime() + r = now * task.trophyStarSpeed % 360.0 + task.trophyStar.setR(r) + return Task.cont diff --git a/toontown/building/DistributedHQInteriorAI.py b/toontown/building/DistributedHQInteriorAI.py new file mode 100755 index 00000000..62bde51f --- /dev/null +++ b/toontown/building/DistributedHQInteriorAI.py @@ -0,0 +1,52 @@ +from direct.distributed.DistributedObjectAI import DistributedObjectAI +import cPickle + + +class DistributedHQInteriorAI(DistributedObjectAI): + notify = directNotify.newCategory('DistributedHQInteriorAI') + + def __init__(self, block, air, zoneId): + DistributedObjectAI.__init__(self, air) + + self.block = block + self.zoneId = zoneId + self.tutorial = 0 + self.isDirty = False + self.accept('leaderboardChanged', self.leaderboardChanged) + self.accept('leaderboardFlush', self.leaderboardFlush) + + def delete(self): + self.ignore('leaderboardChanged') + self.ignore('leaderboardFlush') + self.ignore('setLeaderBoard') + self.ignore('AIStarted') + + DistributedObjectAI.delete(self) + + def getZoneIdAndBlock(self): + return [self.zoneId, self.block] + + def leaderboardChanged(self): + self.isDirty = True + + def leaderboardFlush(self): + if self.isDirty: + self.sendNewLeaderBoard() + + def sendNewLeaderBoard(self): + if self.air: + self.isDirty = False + self.sendUpdate('setLeaderBoard', + [cPickle.dumps(self.air.trophyMgr.getLeaderInfo(), 1)] + ) + + def getLeaderBoard(self): + return cPickle.dumps(self.air.trophyMgr.getLeaderInfo(), 1) + + def getTutorial(self): + return self.tutorial + + def setTutorial(self, flag): + if self.tutorial != flag: + self.tutorial = flag + self.sendUpdate('setTutorial', [self.tutorial]) diff --git a/toontown/building/DistributedKartShopInterior.py b/toontown/building/DistributedKartShopInterior.py new file mode 100755 index 00000000..bb511bf8 --- /dev/null +++ b/toontown/building/DistributedKartShopInterior.py @@ -0,0 +1,38 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObject import DistributedObject +from panda3d.core import * +from toontown.building import ToonInteriorColors +from toontown.hood import ZoneUtil +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.toon.DistributedNPCToonBase import DistributedNPCToonBase + +class DistributedKartShopInterior(DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedKartShopInterior') + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + self.dnaStore = cr.playGame.dnaStore + + def generate(self): + DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.announceGenerate(self) + self.__handleInteriorSetup() + + def disable(self): + self.interior.removeNode() + del self.interior + DistributedObject.disable(self) + + def setZoneIdAndBlock(self, zoneId, block): + self.zoneId = zoneId + self.block = block + + def __handleInteriorSetup(self): + self.interior = loader.loadModel('phase_6/models/karting/KartShop_Interior') + self.interior.reparentTo(render) + self.interior.flattenMedium() + for npcToon in self.cr.doFindAllInstances(DistributedNPCToonBase): + npcToon.initToonState() diff --git a/toontown/building/DistributedKartShopInteriorAI.py b/toontown/building/DistributedKartShopInteriorAI.py new file mode 100755 index 00000000..9c85312f --- /dev/null +++ b/toontown/building/DistributedKartShopInteriorAI.py @@ -0,0 +1,17 @@ +from direct.directnotify.DirectNotifyGlobal import * +from direct.distributed.DistributedObjectAI import DistributedObjectAI + + +class DistributedKartShopInteriorAI(DistributedObjectAI): + notify = directNotify.newCategory('DistributedKartShopInteriorAI') + + def __init__(self, block, air, zoneId): + DistributedObjectAI.__init__(self, air) + self.block = block + self.zoneId = zoneId + + def generate(self): + DistributedObjectAI.generate(self) + + def getZoneIdAndBlock(self): + return [self.zoneId, self.block] diff --git a/toontown/building/DistributedKnockKnockDoor.py b/toontown/building/DistributedKnockKnockDoor.py new file mode 100755 index 00000000..ac28c792 --- /dev/null +++ b/toontown/building/DistributedKnockKnockDoor.py @@ -0,0 +1,139 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from panda3d.core import * + +import DistributedAnimatedProp +from KnockKnockJokes import * +from toontown.distributed import DelayDelete +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from otp.nametag.NametagGroup import NametagGroup +from otp.nametag.NametagConstants import * +import random + +class DistributedKnockKnockDoor(DistributedAnimatedProp.DistributedAnimatedProp): + def __init__(self, cr): + DistributedAnimatedProp.DistributedAnimatedProp.__init__(self, cr) + + self.fsm.setName('DistributedKnockKnockDoor') + self.rimshot = None + self.knockSfx = None + + def generate(self): + DistributedAnimatedProp.DistributedAnimatedProp.generate(self) + + self.avatarTracks = [] + self.avatarId = 0 + + def announceGenerate(self): + DistributedAnimatedProp.DistributedAnimatedProp.announceGenerate(self) + + self.accept('exitKnockKnockDoorSphere_' + str(self.propId), self.exitTrigger) + self.acceptAvatar() + + def disable(self): + self.ignoreAll() + + DistributedAnimatedProp.DistributedAnimatedProp.disable(self) + + def delete(self): + DistributedAnimatedProp.DistributedAnimatedProp.delete(self) + + if self.rimshot: + self.rimshot = None + if self.knockSfx: + self.knockSfx = None + + def acceptAvatar(self): + self.acceptOnce('enterKnockKnockDoorSphere_' + str(self.propId), self.enterTrigger) + + def setAvatarInteract(self, avatarId): + DistributedAnimatedProp.DistributedAnimatedProp.setAvatarInteract(self, avatarId) + + def avatarExit(self, avatarId): + if avatarId == self.avatarId: + self.stopTracks() + + def knockKnockTrack(self, avatar, duration): + if avatar is None: + return + self.rimshot = base.loadSfx('phase_5/audio/sfx/AA_heal_telljoke.ogg') + self.knockSfx = base.loadSfx('phase_5/audio/sfx/GUI_knock_%s.ogg' % random.randint(1, 4)) + joke = KnockKnockJokes[self.propId % len(KnockKnockJokes)] + place = base.cr.playGame.getPlace() + doorName = TTLocalizer.DoorNametag + self.nametag = None + self.nametagNP = None + doorNP = render.find('**/KnockKnockDoorSphere_' + str(self.propId) + ';+s') + if doorNP.isEmpty(): + self.notify.warning('Could not find KnockKnockDoorSphere_%s' % self.propId) + return + self.nametag = NametagGroup() + self.nametag.setAvatar(doorNP) + self.nametag.setFont(ToontownGlobals.getToonFont()) + self.nametag.setSpeechFont(ToontownGlobals.getToonFont()) + self.nametag.setName(doorName) + self.nametag.setActive(0) + self.nametag.manage(base.marginManager) + self.nametag.getNametag3d().setBillboardOffset(4) + nametagNode = self.nametag.getNametag3d() + self.nametagNP = render.attachNewNode(nametagNode) + self.nametagNP.setName('knockKnockDoor_nt_' + str(self.propId)) + pos = doorNP.getBounds().getCenter() + self.nametagNP.setPos(pos + Vec3(0, 0, avatar.getHeight() + 2)) + d = duration * 0.125 + track = Sequence(Parallel(Sequence(Wait(d * 0.5), SoundInterval(self.knockSfx)), Func(self.nametag.setChat, TTLocalizer.DoorKnockKnock, CFSpeech), Wait(d)), Func(avatar.setChatAbsolute, TTLocalizer.DoorWhosThere, CFSpeech | CFTimeout, openEnded=0), Wait(d), Func(self.nametag.setChat, joke[0], CFSpeech), Wait(d), Func(avatar.setChatAbsolute, joke[0] + TTLocalizer.DoorWhoAppendix, CFSpeech | CFTimeout, openEnded=0), Wait(d), Func(self.nametag.setChat, joke[1], CFSpeech)) + if avatar == base.localAvatar: + track.append(Func(self.sendUpdate, 'requestToonup')) + track.append(Parallel(SoundInterval(self.rimshot, startTime=2.0), Wait(d * 4))) + track.append(Func(self.cleanupTrack)) + track.delayDelete = DelayDelete.DelayDelete(avatar, 'knockKnockTrack') + return track + + def cleanupTrack(self): + avatar = self.cr.doId2do.get(self.avatarId) + if avatar: + avatar.clearChat() + if self.nametag: + self.nametag.unmanage(base.marginManager) + self.nametagNP.removeNode() + self.nametag.destroy() + self.nametag = None + self.nametagNP = None + + def enterOff(self): + DistributedAnimatedProp.DistributedAnimatedProp.enterOff(self) + + def exitOff(self): + DistributedAnimatedProp.DistributedAnimatedProp.exitOff(self) + + def enterAttract(self, ts): + DistributedAnimatedProp.DistributedAnimatedProp.enterAttract(self, ts) + self.acceptAvatar() + + def exitAttract(self): + DistributedAnimatedProp.DistributedAnimatedProp.exitAttract(self) + + def enterPlaying(self, ts): + DistributedAnimatedProp.DistributedAnimatedProp.enterPlaying(self, ts) + if self.avatarId: + avatar = self.cr.doId2do.get(self.avatarId, None) + track = self.knockKnockTrack(avatar, 8) + if track != None: + track.start(ts) + self.avatarTracks.append(track) + return + + def exitPlaying(self): + DistributedAnimatedProp.DistributedAnimatedProp.exitPlaying(self) + self.stopTracks() + + def stopTracks(self): + for track in self.avatarTracks: + track.pause() + DelayDelete.cleanupDelayDeletes(track) + + self.cleanupTrack() + self.avatarTracks = [] + self.avatarId = 0 diff --git a/toontown/building/DistributedKnockKnockDoorAI.py b/toontown/building/DistributedKnockKnockDoorAI.py new file mode 100755 index 00000000..67c6983e --- /dev/null +++ b/toontown/building/DistributedKnockKnockDoorAI.py @@ -0,0 +1,49 @@ +from direct.task.Task import Task +from otp.ai.AIBaseGlobal import * +from toontown.toonbase import ToontownGlobals +import DistributedAnimatedPropAI +import time + +class DistributedKnockKnockDoorAI(DistributedAnimatedPropAI.DistributedAnimatedPropAI): + def __init__(self, air, propId): + DistributedAnimatedPropAI.DistributedAnimatedPropAI.__init__(self, air, propId) + self.fsm.setName('DistributedKnockKnockDoor') + self.propId = propId + self.doLaterTask = None + + def delete(self): + DistributedAnimatedPropAI.DistributedAnimatedPropAI.delete(self) + + def enterOff(self): + DistributedAnimatedPropAI.DistributedAnimatedPropAI.enterOff(self) + + def exitOff(self): + DistributedAnimatedPropAI.DistributedAnimatedPropAI.exitOff(self) + + def attractTask(self, task): + self.fsm.request('attract') + return Task.done + + def enterAttract(self): + DistributedAnimatedPropAI.DistributedAnimatedPropAI.enterAttract(self) + + def exitAttract(self): + DistributedAnimatedPropAI.DistributedAnimatedPropAI.exitAttract(self) + + def enterPlaying(self): + DistributedAnimatedPropAI.DistributedAnimatedPropAI.enterPlaying(self) + self.doLaterTask = taskMgr.doMethodLater(9, self.attractTask, self.uniqueName('knockKnock-timer')) + + def exitPlaying(self): + DistributedAnimatedPropAI.DistributedAnimatedPropAI.exitPlaying(self) + taskMgr.remove(self.doLaterTask) + self.doLaterTask = None + + def requestToonup(self): + av = self.air.doId2do.get(self.air.getAvatarIdFromSender()) + + if (not av) or av.getHp() == av.getMaxHp() or av.getNextKnockHeal() > time.time(): + return + + av.toonUp(ToontownGlobals.KnockKnockHeal) + av.b_setNextKnockHeal(int(time.time() + ToontownGlobals.KnockKnockCooldown)) diff --git a/toontown/building/DistributedPetshopInterior.py b/toontown/building/DistributedPetshopInterior.py new file mode 100755 index 00000000..c44a0241 --- /dev/null +++ b/toontown/building/DistributedPetshopInterior.py @@ -0,0 +1,110 @@ +from toontown.toonbase.ToonBaseGlobal import * +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +import random +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.actor import Actor +import ToonInteriorColors +from toontown.dna.DNAParser import * +from toontown.hood import ZoneUtil +from toontown.toon.DistributedNPCToonBase import DistributedNPCToonBase + +class DistributedPetshopInterior(DistributedObject.DistributedObject): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.dnaStore = cr.playGame.dnaStore + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.setup() + + def randomDNAItem(self, category, findFunc): + codeCount = self.dnaStore.getNumCatalogCodes(category) + index = self.randomGenerator.randint(0, codeCount - 1) + code = self.dnaStore.getCatalogCode(category, index) + return findFunc(code) + + def replaceRandomInModel(self, model): + baseTag = 'random_' + npc = model.findAllMatches('**/' + baseTag + '???_*') + for i in xrange(npc.getNumPaths()): + np = npc.getPath(i) + name = np.getName() + b = len(baseTag) + category = name[b + 4:] + key1 = name[b] + key2 = name[b + 1] + if key1 == 'm': + model = self.randomDNAItem(category, self.dnaStore.findNode) + newNP = model.copyTo(np) + if key2 == 'r': + self.replaceRandomInModel(newNP) + elif key1 == 't': + texture = self.randomDNAItem(category, self.dnaStore.findTexture) + np.setTexture(texture, 100) + newNP = np + if key2 == 'c': + if category == 'TI_wallpaper' or category == 'TI_wallpaper_border': + self.randomGenerator.seed(self.zoneId) + newNP.setColorScale(self.randomGenerator.choice(self.colors[category])) + else: + newNP.setColorScale(self.randomGenerator.choice(self.colors[category])) + + def setZoneIdAndBlock(self, zoneId, block): + self.zoneId = zoneId + self.block = block + + def chooseDoor(self): + doorModelName = 'door_double_round_ul' + if doorModelName[-1:] == 'r': + doorModelName = doorModelName[:-1] + 'l' + else: + doorModelName = doorModelName[:-1] + 'r' + door = self.dnaStore.findNode(doorModelName) + return door + + def setup(self): + self.dnaStore = base.cr.playGame.dnaStore + self.randomGenerator = random.Random() + self.randomGenerator.seed(self.zoneId) + self.interior = loader.loadModel('phase_4/models/modules/PetShopInterior') + self.interior.reparentTo(render) + self.fish = Actor.Actor('phase_4/models/props/interiorfish-zero', {'swim': 'phase_4/models/props/interiorfish-swim'}) + self.fish.reparentTo(self.interior) + self.fish.setColorScale(0.8, 0.9, 1, 0.8) + self.fish.setScale(0.8) + self.fish.setPos(0, 6, -4) + self.fish.setPlayRate(0.7, 'swim') + self.fish.loop('swim') + hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId) + self.colors = ToonInteriorColors.colors[hoodId] + self.replaceRandomInModel(self.interior) + door = self.chooseDoor() + doorOrigin = render.find('**/door_origin;+s') + doorNP = door.copyTo(doorOrigin) + doorOrigin.setScale(0.8, 0.8, 0.8) + doorOrigin.setPos(doorOrigin, 0, -0.25, 0) + doorColor = self.randomGenerator.choice(self.colors['TI_door']) + setupDoor(doorNP, self.interior, doorOrigin, self.dnaStore, str(self.block), doorColor) + doorFrame = doorNP.find('door_*_flat') + doorFrame.wrtReparentTo(self.interior) + doorFrame.setColor(doorColor) + del self.colors + del self.dnaStore + del self.randomGenerator + self.interior.flattenMedium() + for npcToon in self.cr.doFindAllInstances(DistributedNPCToonBase): + npcToon.initToonState() + + def disable(self): + self.fish.stop() + self.fish.cleanup() + del self.fish + self.interior.removeNode() + del self.interior + DistributedObject.DistributedObject.disable(self) diff --git a/toontown/building/DistributedPetshopInteriorAI.py b/toontown/building/DistributedPetshopInteriorAI.py new file mode 100755 index 00000000..2220f272 --- /dev/null +++ b/toontown/building/DistributedPetshopInteriorAI.py @@ -0,0 +1,14 @@ +from direct.distributed import DistributedObjectAI + +class DistributedPetshopInteriorAI(DistributedObjectAI.DistributedObjectAI): + + def __init__(self, block, air, zoneId): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.block = block + self.zoneId = zoneId + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + + def getZoneIdAndBlock(self): + return [self.zoneId, self.block] diff --git a/toontown/building/DistributedSuitInterior.py b/toontown/building/DistributedSuitInterior.py new file mode 100755 index 00000000..78386e6b --- /dev/null +++ b/toontown/building/DistributedSuitInterior.py @@ -0,0 +1,388 @@ +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from ElevatorConstants import * +import ElevatorUtils +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.distributed import DistributedObject +from direct.fsm import State +from toontown.battle import BattleBase +from toontown.hood import ZoneUtil +from toontown.suit import SuitDNA + +class DistributedSuitInterior(DistributedObject.DistributedObject): + id = 0 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.toons = [] + self.activeIntervals = {} + self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.ogg') + self.closeSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_close.ogg') + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.distBldgDoId = None + self.currentFloor = -1 + self.numFloors = None + self.elevatorName = self.__uniqueName('elevator') + self.floorModel = None + self.elevatorOutOpen = 0 + self.BottomFloor_SuitPositions = [Point3(0, 15, 0), + Point3(10, 20, 0), + Point3(-7, 24, 0), + Point3(-10, 0, 0)] + self.BottomFloor_SuitHs = [75, + 170, + -91, + -44] + self.Cubicle_SuitPositions = [Point3(0, 18, 0), + Point3(10, 12, 0), + Point3(-9, 11, 0), + Point3(-3, 13, 0)] + self.Cubicle_SuitHs = [170, + 56, + -52, + 10] + self.BossOffice_SuitPositions = [Point3(0, 15, 0), + Point3(10, 20, 0), + Point3(-10, 6, 0), + Point3(-17, 34, 11)] + self.BossOffice_SuitHs = [170, + 120, + 12, + 38] + self.waitMusic = base.loadMusic('phase_7/audio/bgm/encntr_toon_winning_indoor.ogg') + self.elevatorMusic = base.loadMusic('phase_7/audio/bgm/tt_elevator.ogg') + self.fsm = ClassicFSM.ClassicFSM('DistributedSuitInterior', [State.State('WaitForAllToonsInside', self.enterWaitForAllToonsInside, self.exitWaitForAllToonsInside, ['Elevator']), + State.State('Elevator', self.enterElevator, self.exitElevator, ['Battle']), + State.State('Battle', self.enterBattle, self.exitBattle, ['Resting', 'Reward', 'ReservesJoining']), + State.State('ReservesJoining', self.enterReservesJoining, self.exitReservesJoining, ['Battle']), + State.State('Resting', self.enterResting, self.exitResting, ['Elevator']), + State.State('Reward', self.enterReward, self.exitReward, ['Off']), + State.State('Off', self.enterOff, self.exitOff, ['Elevator', 'WaitForAllToonsInside', 'Battle'])], 'Off', 'Off') + self.fsm.enterInitialState() + return + + def __uniqueName(self, name): + DistributedSuitInterior.id += 1 + return name + '%d' % DistributedSuitInterior.id + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.announceGenerateName = self.uniqueName('generate') + self.accept(self.announceGenerateName, self.handleAnnounceGenerate) + self.elevatorModelIn = loader.loadModel('phase_4/models/modules/elevator') + self.leftDoorIn = self.elevatorModelIn.find('**/left-door') + self.rightDoorIn = self.elevatorModelIn.find('**/right-door') + self.elevatorModelOut = loader.loadModel('phase_4/models/modules/elevator') + self.leftDoorOut = self.elevatorModelOut.find('**/left-door') + self.rightDoorOut = self.elevatorModelOut.find('**/right-door') + + def setElevatorLights(self, elevatorModel): + npc = elevatorModel.findAllMatches('**/floor_light_?;+s') + for i in xrange(npc.getNumPaths()): + np = npc.getPath(i) + floor = int(np.getName()[-1:]) - 1 + if floor == self.currentFloor: + np.setColor(LIGHT_ON_COLOR) + elif floor < self.numFloors: + np.setColor(LIGHT_OFF_COLOR) + else: + np.hide() + + def handleAnnounceGenerate(self, obj): + self.ignore(self.announceGenerateName) + self.sendUpdate('setAvatarJoined', []) + + def disable(self): + self.fsm.requestFinalState() + self.__cleanupIntervals() + self.ignoreAll() + self.__cleanup() + DistributedObject.DistributedObject.disable(self) + + def delete(self): + del self.waitMusic + del self.elevatorMusic + del self.openSfx + del self.closeSfx + del self.fsm + base.localAvatar.inventory.setBattleCreditMultiplier(1) + DistributedObject.DistributedObject.delete(self) + + def __cleanup(self): + self.toons = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + if self.elevatorModelIn != None: + self.elevatorModelIn.removeNode() + if self.elevatorModelOut != None: + self.elevatorModelOut.removeNode() + if self.floorModel != None: + self.floorModel.removeNode() + self.leftDoorIn = None + self.rightDoorIn = None + self.leftDoorOut = None + self.rightDoorOut = None + return + + def __addToon(self, toon): + self.accept(toon.uniqueName('disable'), self.__handleUnexpectedExit, extraArgs=[toon]) + + def __handleUnexpectedExit(self, toon): + self.notify.warning('handleUnexpectedExit() - toon: %d' % toon.doId) + self.__removeToon(toon, unexpected=1) + + def __removeToon(self, toon, unexpected = 0): + if self.toons.count(toon) == 1: + self.toons.remove(toon) + self.ignore(toon.uniqueName('disable')) + + def __finishInterval(self, name): + if name in self.activeIntervals: + interval = self.activeIntervals[name] + if interval.isPlaying(): + interval.finish() + + def __cleanupIntervals(self): + for interval in self.activeIntervals.values(): + interval.finish() + + self.activeIntervals = {} + + def __closeInElevator(self): + self.leftDoorIn.setPos(3.5, 0, 0) + self.rightDoorIn.setPos(-3.5, 0, 0) + + def getZoneId(self): + return self.zoneId + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def getExtZoneId(self): + return self.extZoneId + + def setExtZoneId(self, extZoneId): + self.extZoneId = extZoneId + + def getDistBldgDoId(self): + return self.distBldgDoId + + def setDistBldgDoId(self, distBldgDoId): + self.distBldgDoId = distBldgDoId + + def setNumFloors(self, numFloors): + self.numFloors = numFloors + + def setToons(self, toonIds, hack): + self.toonIds = toonIds + oldtoons = self.toons + self.toons = [] + for toonId in toonIds: + if toonId != 0: + if toonId in self.cr.doId2do: + toon = self.cr.doId2do[toonId] + toon.stopSmooth() + self.toons.append(toon) + if oldtoons.count(toon) == 0: + self.__addToon(toon) + else: + self.notify.warning('setToons() - no toon: %d' % toonId) + + for toon in oldtoons: + if self.toons.count(toon) == 0: + self.__removeToon(toon) + + def setSuits(self, suitIds, reserveIds, values): + oldsuits = self.suits + self.suits = [] + self.joiningReserves = [] + for suitId in suitIds: + if suitId in self.cr.doId2do: + suit = self.cr.doId2do[suitId] + self.suits.append(suit) + suit.fsm.request('Battle') + suit.buildingSuit = 1 + suit.reparentTo(render) + if oldsuits.count(suit) == 0: + self.joiningReserves.append(suit) + else: + self.notify.warning('setSuits() - no suit: %d' % suitId) + + self.reserveSuits = [] + for index in xrange(len(reserveIds)): + suitId = reserveIds[index] + if suitId in self.cr.doId2do: + suit = self.cr.doId2do[suitId] + self.reserveSuits.append((suit, values[index])) + else: + self.notify.warning('setSuits() - no suit: %d' % suitId) + + if len(self.joiningReserves) > 0: + self.fsm.request('ReservesJoining') + + def setState(self, state, timestamp): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def d_elevatorDone(self): + self.sendUpdate('elevatorDone', []) + + def d_reserveJoinDone(self): + self.sendUpdate('reserveJoinDone', []) + + def enterOff(self, ts = 0): + return None + + def exitOff(self): + return None + + def enterWaitForAllToonsInside(self, ts = 0): + return None + + def exitWaitForAllToonsInside(self): + return None + + def __playElevator(self, ts, name, callback): + SuitHs = [] + SuitPositions = [] + DeptName = SuitDNA.suitDeptFilenames[self.suits[0].style.dept] + if self.floorModel: + self.floorModel.removeNode() + if self.currentFloor == 0: + self.floorModel = loader.loadModel('phase_7/models/modules/suit_interior_%s' % DeptName) + SuitHs = self.BottomFloor_SuitHs + SuitPositions = self.BottomFloor_SuitPositions + elif self.currentFloor == self.numFloors - 1: + self.floorModel = loader.loadModel('phase_7/models/modules/boss_suit_office_%s' % DeptName) + SuitHs = self.BossOffice_SuitHs + SuitPositions = self.BossOffice_SuitPositions + else: + self.floorModel = loader.loadModel('phase_7/models/modules/cubicle_room') + SuitHs = self.Cubicle_SuitHs + SuitPositions = self.Cubicle_SuitPositions + self.floorModel.reparentTo(render) + elevIn = self.floorModel.find('**/elevator-in') + elevOut = self.floorModel.find('**/elevator-out') + for index in xrange(len(self.suits)): + self.suits[index].setPos(SuitPositions[index]) + if len(self.suits) > 2: + self.suits[index].setH(SuitHs[index]) + else: + self.suits[index].setH(170) + self.suits[index].loop('neutral') + + for toon in self.toons: + toon.reparentTo(self.elevatorModelIn) + index = self.toonIds.index(toon.doId) + toon.setPos(ElevatorPoints[index][0], ElevatorPoints[index][1], ElevatorPoints[index][2]) + toon.setHpr(180, 0, 0) + toon.loop('neutral') + + self.elevatorModelIn.reparentTo(elevIn) + self.leftDoorIn.setPos(3.5, 0, 0) + self.rightDoorIn.setPos(-3.5, 0, 0) + self.elevatorModelOut.reparentTo(elevOut) + self.leftDoorOut.setPos(3.5, 0, 0) + self.rightDoorOut.setPos(-3.5, 0, 0) + camera.reparentTo(self.elevatorModelIn) + camera.setH(180) + camera.setPos(0, 14, 4) + base.playMusic(self.elevatorMusic, looping=1, volume=0.8) + track = Sequence(ElevatorUtils.getRideElevatorInterval(ELEVATOR_NORMAL), ElevatorUtils.getOpenInterval(self, self.leftDoorIn, self.rightDoorIn, self.openSfx, None, type=ELEVATOR_NORMAL), Func(camera.wrtReparentTo, render)) + for toon in self.toons: + track.append(Func(toon.wrtReparentTo, render)) + + track.append(Func(callback)) + track.start(ts) + self.activeIntervals[name] = track + return + + def enterElevator(self, ts = 0): + self.currentFloor += 1 + self.cr.playGame.getPlace().currentFloor = self.currentFloor + self.setElevatorLights(self.elevatorModelIn) + self.setElevatorLights(self.elevatorModelOut) + self.__playElevator(ts, self.elevatorName, self.__handleElevatorDone) + mult = ToontownBattleGlobals.getCreditMultiplier(self.currentFloor) + base.localAvatar.inventory.setBattleCreditMultiplier(mult) + + def __handleElevatorDone(self): + self.d_elevatorDone() + + def exitElevator(self): + self.elevatorMusic.stop() + self.__finishInterval(self.elevatorName) + return None + + def __playCloseElevatorOut(self, name): + track = Sequence(Wait(SUIT_LEAVE_ELEVATOR_TIME), Parallel(SoundInterval(self.closeSfx), LerpPosInterval(self.leftDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL), startPos=Point3(0, 0, 0), blendType='easeOut'), LerpPosInterval(self.rightDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL), startPos=Point3(0, 0, 0), blendType='easeOut'))) + track.start() + self.activeIntervals[name] = track + + def enterBattle(self, ts = 0): + if self.elevatorOutOpen == 1: + self.__playCloseElevatorOut(self.uniqueName('close-out-elevator')) + camera.setPos(0, -15, 6) + camera.headsUp(self.elevatorModelOut) + return None + + def exitBattle(self): + if self.elevatorOutOpen == 1: + self.__finishInterval(self.uniqueName('close-out-elevator')) + self.elevatorOutOpen = 0 + return None + + def __playReservesJoining(self, ts, name, callback): + index = 0 + for suit in self.joiningReserves: + suit.reparentTo(render) + suit.setPos(self.elevatorModelOut, Point3(ElevatorPoints[index][0], ElevatorPoints[index][1], ElevatorPoints[index][2])) + index += 1 + suit.setH(180) + suit.loop('neutral') + + track = Sequence(Func(camera.wrtReparentTo, self.elevatorModelOut), Func(camera.setPos, Point3(0, -8, 2)), Func(camera.setHpr, Vec3(0, 10, 0)), Parallel(SoundInterval(self.openSfx), LerpPosInterval(self.leftDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], Point3(0, 0, 0), startPos=ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL), blendType='easeOut'), LerpPosInterval(self.rightDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], Point3(0, 0, 0), startPos=ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL), blendType='easeOut')), Wait(SUIT_HOLD_ELEVATOR_TIME), Func(camera.wrtReparentTo, render), Func(callback)) + track.start(ts) + self.activeIntervals[name] = track + + def enterReservesJoining(self, ts = 0): + self.__playReservesJoining(ts, self.uniqueName('reserves-joining'), self.__handleReserveJoinDone) + return None + + def __handleReserveJoinDone(self): + self.joiningReserves = [] + self.elevatorOutOpen = 1 + self.d_reserveJoinDone() + + def exitReservesJoining(self): + self.__finishInterval(self.uniqueName('reserves-joining')) + return None + + def enterResting(self, ts = 0): + base.playMusic(self.waitMusic, looping=1, volume=0.7) + base.localAvatar.questMap.stop() + self.__closeInElevator() + + def exitResting(self): + self.waitMusic.stop() + + def enterReward(self, ts = 0): + base.localAvatar.b_setParent(ToontownGlobals.SPHidden) + request = {'loader': ZoneUtil.getBranchLoaderName(self.extZoneId), + 'where': ZoneUtil.getToonWhereName(self.extZoneId), + 'how': 'elevatorIn', + 'hoodId': ZoneUtil.getHoodId(self.extZoneId), + 'zoneId': self.extZoneId, + 'shardId': None, + 'avId': base.localAvatar.doId, + 'bldgDoId': self.distBldgDoId} + messenger.send('DSIDoneEvent', [request]) + return + + def exitReward(self): + return None diff --git a/toontown/building/DistributedSuitInteriorAI.py b/toontown/building/DistributedSuitInteriorAI.py new file mode 100755 index 00000000..e9f1dc9e --- /dev/null +++ b/toontown/building/DistributedSuitInteriorAI.py @@ -0,0 +1,441 @@ +import copy + +import DistributedElevatorIntAI +from ElevatorConstants import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Timer +from otp.ai.AIBaseGlobal import * +from toontown.battle import BattleBase +from toontown.battle import DistributedBattleBldgAI +from toontown.toonbase.ToontownBattleGlobals import * + + +class DistributedSuitInteriorAI(DistributedObjectAI.DistributedObjectAI): + def __init__(self, air, elevator): + self.air = air + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + (self.extZoneId, self.zoneId) = elevator.bldg.getExteriorAndInteriorZoneId() + self.numFloors = elevator.bldg.planner.numFloors + self.avatarExitEvents = [] + self.toons = [] + self.toonSkillPtsGained = {} + self.toonExp = {} + self.toonOrigQuests = {} + self.toonItems = {} + self.toonOrigMerits = {} + self.toonMerits = {} + self.toonParts = {} + self.helpfulToons = [] + self.currentFloor = 0 + self.topFloor = self.numFloors - 1 + self.bldg = elevator.bldg + self.elevator = elevator + self.suits = [] + self.activeSuits = [] + self.reserveSuits = [] + self.joinedReserves = [] + self.suitsKilled = [] + self.suitsKilledPerFloor = [] + self.battle = None + self.timer = Timer.Timer() + self.responses = {} + self.ignoreResponses = 0 + self.ignoreElevatorDone = 0 + self.ignoreReserveJoinDone = 0 + self.toonIds = copy.copy(elevator.seats) + for toonId in self.toonIds: + if toonId is not None: + self.__addToon(toonId) + self.savedByMap = {} + self.fsm = ClassicFSM.ClassicFSM( + 'DistributedSuitInteriorAI', + [ + State.State('WaitForAllToonsInside', + self.enterWaitForAllToonsInside, self.exitWaitForAllToonsInside, + ['Elevator']), + State.State('Elevator', + self.enterElevator, self.exitElevator, + ['Battle']), + State.State('Battle', + self.enterBattle, self.exitBattle, + ['ReservesJoining', 'BattleDone']), + State.State('ReservesJoining', + self.enterReservesJoining, self.exitReservesJoining, + ['Battle']), + State.State('BattleDone', + self.enterBattleDone, self.exitBattleDone, + ['Resting', 'Reward']), + State.State('Resting', + self.enterResting, self.exitResting, + ['Elevator']), + State.State('Reward', self.enterReward, self.exitReward, + ['Off']), + State.State('Off', + self.enterOff, self.exitOff, + ['WaitForAllToonsInside']) + ], 'Off', 'Off', onUndefTransition=ClassicFSM.ClassicFSM.ALLOW) + self.fsm.enterInitialState() + + def delete(self): + self.ignoreAll() + self.toons = [] + self.toonIds = [] + self.fsm.requestFinalState() + del self.fsm + del self.bldg + del self.elevator + self.timer.stop() + del self.timer + self.__cleanupFloorBattle() + taskName = self.taskName('deleteInterior') + taskMgr.remove(taskName) + DistributedObjectAI.DistributedObjectAI.delete(self) + + def __handleUnexpectedExit(self, toonId): + self.notify.warning('toon: %d exited unexpectedly' % toonId) + self.__removeToon(toonId) + if len(self.toons) == 0: + self.timer.stop() + if self.fsm.getCurrentState().getName() == 'Resting': + return + elif self.battle is None: + self.bldg.deleteSuitInterior() + + def __addToon(self, toonId): + if toonId not in self.air.doId2do: + self.notify.warning('addToon() - no toon for doId: %d' % toonId) + return + event = self.air.getAvatarExitEvent(toonId) + self.avatarExitEvents.append(event) + self.accept(event, self.__handleUnexpectedExit, extraArgs=[toonId]) + self.toons.append(toonId) + self.responses[toonId] = 0 + + def __removeToon(self, toonId): + if toonId in self.toons: + self.toons.remove(toonId) + if toonId in self.toonIds: + self.toonIds[self.toonIds.index(toonId)] = None + if toonId in self.responses: + del self.responses[toonId] + event = self.air.getAvatarExitEvent(toonId) + if self.avatarExitEvents.count(event): + self.avatarExitEvents.remove(event) + self.ignore(event) + + def __resetResponses(self): + self.responses = {} + for toon in self.toons: + self.responses[toon] = 0 + self.ignoreResponses = 0 + + def __allToonsResponded(self): + for toon in self.toons: + if self.responses[toon] == 0: + return 0 + self.ignoreResponses = 1 + return 1 + + def getZoneId(self): + return self.zoneId + + def getExtZoneId(self): + return self.extZoneId + + def getDistBldgDoId(self): + return self.bldg.getDoId() + + def getNumFloors(self): + return self.numFloors + + def d_setToons(self): + self.sendUpdate('setToons', self.getToons()) + + def getToons(self): + sendIds = [] + for toonId in self.toonIds: + if toonId is None: + sendIds.append(0) + else: + sendIds.append(toonId) + return [sendIds, 0] + + def d_setSuits(self): + self.sendUpdate('setSuits', self.getSuits()) + + def getSuits(self): + suitIds = [] + for suit in self.activeSuits: + suitIds.append(suit.doId) + reserveIds = [] + values = [] + for info in self.reserveSuits: + reserveIds.append(info[0].doId) + values.append(info[1]) + return [suitIds, reserveIds, values] + + def b_setState(self, state): + self.d_setState(state) + self.setState(state) + + def d_setState(self, state): + stime = globalClock.getRealTime() + BattleBase.SERVER_BUFFER_TIME + self.sendUpdate('setState', [ + state, globalClockDelta.localToNetworkTime(stime)]) + + def setState(self, state): + self.fsm.request(state) + + def getState(self): + return [self.fsm.getCurrentState().getName(), + globalClockDelta.getRealNetworkTime()] + + def setAvatarJoined(self): + avId = self.air.getAvatarIdFromSender() + if self.toons.count(avId) == 0: + self.air.writeServerEvent('suspicious', avId, 'DistributedSuitInteriorAI.setAvatarJoined from toon not in %s.' % self.toons) + self.notify.warning('setAvatarJoined() - av: %d not in list' % avId) + return + avatar = self.air.doId2do.get(avId) + if avatar is not None: + self.savedByMap[avId] = (avatar.getName(), avatar.dna.asTuple()) + self.responses[avId] += 1 + if self.__allToonsResponded(): + self.fsm.request('Elevator') + + def elevatorDone(self): + toonId = self.air.getAvatarIdFromSender() + if self.ignoreResponses == 1: + return + elif self.fsm.getCurrentState().getName() != 'Elevator': + self.notify.warning('elevatorDone() - in state: %s' % self.fsm.getCurrentState().getName()) + return + elif self.toons.count(toonId) == 0: + self.notify.warning('elevatorDone() - toon not in toon list: %d' % toonId) + return + self.responses[toonId] += 1 + if self.__allToonsResponded() and self.ignoreElevatorDone == 0: + self.b_setState('Battle') + + def reserveJoinDone(self): + toonId = self.air.getAvatarIdFromSender() + if self.ignoreResponses == 1: + return + elif self.fsm.getCurrentState().getName() != 'ReservesJoining': + self.notify.warning('reserveJoinDone() - in state: %s' % self.fsm.getCurrentState().getName()) + return + elif self.toons.count(toonId) == 0: + self.notify.warning('reserveJoinDone() - toon not in list: %d' % toonId) + return + self.responses[toonId] += 1 + if self.__allToonsResponded() and self.ignoreReserveJoinDone == 0: + self.b_setState('Battle') + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterWaitForAllToonsInside(self): + self.__resetResponses() + + def exitWaitForAllToonsInside(self): + self.__resetResponses() + + def enterElevator(self): + suitHandles = self.bldg.planner.genFloorSuits(self.currentFloor) + self.suits = suitHandles['activeSuits'] + self.activeSuits = [] + for suit in self.suits: + self.activeSuits.append(suit) + self.reserveSuits = suitHandles['reserveSuits'] + self.d_setToons() + self.d_setSuits() + self.__resetResponses() + self.d_setState('Elevator') + self.timer.startCallback(BattleBase.ELEVATOR_T + ElevatorData[ELEVATOR_NORMAL]['openTime'] + BattleBase.SERVER_BUFFER_TIME, self.__serverElevatorDone) + + def __serverElevatorDone(self): + self.ignoreElevatorDone = 1 + self.b_setState('Battle') + + def exitElevator(self): + self.timer.stop() + self.__resetResponses() + + def __createFloorBattle(self): + if self.currentFloor == self.topFloor: + bossBattle = 1 + else: + bossBattle = 0 + self.battle = DistributedBattleBldgAI.DistributedBattleBldgAI(self.air, self.zoneId, self.__handleRoundDone, self.__handleBattleDone, bossBattle = bossBattle) + self.battle.suitsKilled = self.suitsKilled + self.battle.suitsKilledPerFloor = self.suitsKilledPerFloor + self.battle.battleCalc.toonSkillPtsGained = self.toonSkillPtsGained + self.battle.toonExp = self.toonExp + self.battle.toonOrigQuests = self.toonOrigQuests + self.battle.toonItems = self.toonItems + self.battle.toonOrigMerits = self.toonOrigMerits + self.battle.toonMerits = self.toonMerits + self.battle.toonParts = self.toonParts + self.battle.helpfulToons = self.helpfulToons + self.battle.setInitialMembers(self.toons, self.suits) + self.battle.generateWithRequired(self.zoneId) + mult = getCreditMultiplier(self.currentFloor) + if self.air.suitInvasionManager.getInvading(): + mult *= getInvasionMultiplier() + self.battle.battleCalc.setSkillCreditMultiplier(mult) + + def __cleanupFloorBattle(self): + for suit in self.suits: + self.notify.debug('cleaning up floor suit: %d' % suit.doId) + if suit.isDeleted(): + self.notify.debug('whoops, suit %d is deleted.' % suit.doId) + continue + suit.requestDelete() + self.suits = [] + self.reserveSuits = [] + self.activeSuits = [] + if self.battle != None: + self.battle.requestDelete() + self.battle = None + + def __handleRoundDone(self, toonIds, totalHp, deadSuits): + totalMaxHp = 0 + for suit in self.suits: + totalMaxHp += suit.maxHP + + for suit in deadSuits: + self.activeSuits.remove(suit) + + if len(self.reserveSuits) > 0 and len(self.activeSuits) < 4: + self.joinedReserves = [] + hpPercent = 100 - (totalHp / totalMaxHp) * 100.0 + for info in self.reserveSuits: + if info[1] <= hpPercent and len(self.activeSuits) < 4: + self.suits.append(info[0]) + self.activeSuits.append(info[0]) + self.joinedReserves.append(info) + + for info in self.joinedReserves: + self.reserveSuits.remove(info) + + if len(self.joinedReserves) > 0: + self.fsm.request('ReservesJoining') + self.d_setSuits() + return + if len(self.activeSuits) == 0: + self.fsm.request('BattleDone', [toonIds]) + else: + self.battle.resume() + + def __handleBattleDone(self, zoneId, toonIds): + if len(toonIds) == 0: + taskName = self.taskName('deleteInterior') + taskMgr.doMethodLater(10, self.__doDeleteInterior, taskName) + elif self.currentFloor == self.topFloor: + self.setState('Reward') + else: + self.b_setState('Resting') + + def __doDeleteInterior(self, task): + self.bldg.deleteSuitInterior() + + def enterBattle(self): + if self.battle is None: + self.__createFloorBattle() + self.elevator.d_setFloor(self.currentFloor) + + def exitBattle(self): + pass + + def enterReservesJoining(self): + self.__resetResponses() + self.timer.startCallback(ElevatorData[ELEVATOR_NORMAL]['openTime'] + SUIT_HOLD_ELEVATOR_TIME + BattleBase.SERVER_BUFFER_TIME, self.__serverReserveJoinDone) + + def __serverReserveJoinDone(self): + self.ignoreReserveJoinDone = 1 + self.b_setState('Battle') + + def exitReservesJoining(self): + self.timer.stop() + self.__resetResponses() + for info in self.joinedReserves: + self.battle.suitRequestJoin(info[0]) + self.battle.resume() + self.joinedReserves = [] + + def enterBattleDone(self, toonIds): + if len(toonIds) != len(self.toons): + deadToons = [] + for toon in self.toons: + if toonIds.count(toon) == 0: + deadToons.append(toon) + continue + for toon in deadToons: + self.__removeToon(toon) + self.d_setToons() + if len(self.toons) == 0: + self.bldg.deleteSuitInterior() + elif self.currentFloor == self.topFloor: + self.battle.resume(self.currentFloor, topFloor = 1) + else: + self.battle.resume(self.currentFloor, topFloor = 0) + + def exitBattleDone(self): + self.__cleanupFloorBattle() + + def __handleEnterElevator(self): + self.fsm.request('Elevator') + + def enterResting(self): + self.intElevator = DistributedElevatorIntAI.DistributedElevatorIntAI(self.air, self, self.toons) + self.intElevator.generateWithRequired(self.zoneId) + + def handleAllAboard(self, seats): + if not hasattr(self, 'fsm'): + return + numOfEmptySeats = seats.count(None) + if numOfEmptySeats == 4: + self.bldg.deleteSuitInterior() + return + elif numOfEmptySeats >= 0 and numOfEmptySeats <= 3: + pass + else: + self.error('Bad number of empty seats: %s' % numOfEmptySeats) + for toon in self.toons: + if seats.count(toon) == 0: + self.__removeToon(toon) + continue + self.toonIds = copy.copy(seats) + self.toons = [] + for toonId in self.toonIds: + if toonId != None: + self.toons.append(toonId) + continue + self.d_setToons() + self.currentFloor += 1 + self.fsm.request('Elevator') + + def exitResting(self): + self.intElevator.requestDelete() + del self.intElevator + + def enterReward(self): + victors = self.toonIds[:] + savedBy = [] + for v in victors: + tuple = self.savedByMap.get(v) + if tuple: + savedBy.append([v, tuple[0], tuple[1]]) + continue + self.bldg.fsm.request('waitForVictors', [victors, savedBy]) + self.d_setState('Reward') + + def exitReward(self): + pass diff --git a/toontown/building/DistributedToonHallInterior.py b/toontown/building/DistributedToonHallInterior.py new file mode 100755 index 00000000..8fd4fb29 --- /dev/null +++ b/toontown/building/DistributedToonHallInterior.py @@ -0,0 +1,429 @@ +from direct.actor import Actor +from direct.interval.IntervalGlobal import * +from direct.showbase import Audio3DManager +from otp.ai.MagicWordGlobal import * +from toontown.dna.DNAParser import * +from toontown.hood import ZoneUtil +from toontown.toon import DistributedNPCToonBase +from DistributedToonInterior import DistributedToonInterior +import ToonInteriorColors, random + +class DistributedToonHallInterior(DistributedToonInterior): + + def setup(self): + self.dnaStore = base.cr.playGame.dnaStore + self.randomGenerator = random.Random() + self.randomGenerator.seed(self.zoneId) + interior = self.randomDNAItem('TI_hall', self.dnaStore.findNode) + self.interior = interior.copyTo(render) + hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId) + self.colors = ToonInteriorColors.colors[hoodId] + self.replaceRandomInModel(self.interior) + doorModelName = 'door_double_round_ul' + if doorModelName[-1:] == 'r': + doorModelName = doorModelName[:-1] + 'l' + else: + doorModelName = doorModelName[:-1] + 'r' + door = self.dnaStore.findNode(doorModelName) + door_origin = render.find('**/door_origin;+s') + doorNP = door.copyTo(door_origin) + door_origin.setScale(0.8, 0.8, 0.8) + door_origin.setPos(door_origin, 0, -0.025, 0) + color = self.randomGenerator.choice(self.colors['TI_door']) + setupDoor(doorNP, self.interior, door_origin, self.dnaStore, str(self.block), color) + doorFrame = doorNP.find('door_*_flat') + doorFrame.wrtReparentTo(self.interior) + doorFrame.setColor(color) + del self.colors + del self.dnaStore + del self.randomGenerator + self.interior.flattenMedium() + for npcToon in self.cr.doFindAllInstances(DistributedNPCToonBase.DistributedNPCToonBase): + npcToon.initToonState() + + self.createSillyMeter() + + def createSillyMeter(self): + ropes = loader.loadModel('phase_4/models/modules/tt_m_ara_int_ropes') + ropes.reparentTo(self.interior) + self.sillyMeter = Actor.Actor('phase_4/models/props/tt_a_ara_ttc_sillyMeter_default', {'arrowTube': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_arrowFluid', + 'phaseOne': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_phaseOne', + 'phaseTwo': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_phaseTwo', + 'phaseThree': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_phaseThree', + 'phaseFour': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_phaseFour', + 'phaseFourToFive': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_phaseFourToFive', + 'phaseFive': 'phase_4/models/props/tt_a_ara_ttc_sillyMeter_phaseFive'}) + self.sillyMeter.reparentTo(self.interior) + self.smPhase1 = self.sillyMeter.find('**/stage1') + self.smPhase2 = self.sillyMeter.find('**/stage2') + self.smPhase3 = self.sillyMeter.find('**/stage3') + self.smPhase4 = self.sillyMeter.find('**/stage4') + self.smPhase2.hide() + self.smPhase3.hide() + self.smPhase4.hide() + thermometerLocator = self.sillyMeter.findAllMatches('**/uvj_progressBar')[1] + thermometerMesh = self.sillyMeter.find('**/tube') + thermometerMesh.setTexProjector(thermometerMesh.findTextureStage('default'), thermometerLocator, self.sillyMeter) + self.sillyMeter.flattenMedium() + self.sillyMeter.makeSubpart('arrow', ['uvj_progressBar*', 'def_springA']) + self.sillyMeter.makeSubpart('meter', ['def_pivot'], ['uvj_progressBar*', 'def_springA']) + self.audio3d = Audio3DManager.Audio3DManager(base.sfxManagerList[0], camera) + self.phase1Sfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterPhaseOne.ogg') + self.phase1Sfx.setLoop(True) + self.phase2Sfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterPhaseTwo.ogg') + self.phase2Sfx.setLoop(True) + self.phase3Sfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterPhaseThree.ogg') + self.phase3Sfx.setLoop(True) + self.phase4Sfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterPhaseFour.ogg') + self.phase4Sfx.setLoop(True) + self.phase4To5Sfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterPhaseFourToFive.ogg') + self.phase4To5Sfx.setLoop(False) + self.phase5Sfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterPhaseFive.ogg') + self.phase5Sfx.setLoop(True) + self.arrowSfx = self.audio3d.loadSfx('phase_4/audio/sfx/tt_s_prp_sillyMeterArrow.ogg') + self.arrowSfx.setLoop(False) + self.audio3d.setDropOffFactor(0.1) + + self.startSillyMeter(config.GetInt('silly-meter-phase', 12)) + + def startSillyMeter(self, phase): + self.stopSillyMeter() + + if hasattr(self, 'enterPhase%s' % phase): + getattr(self, 'enterPhase%s' % phase)() + self.phase = phase + + def stopSillyMeter(self): + if hasattr(self, 'phase') and hasattr(self, 'exitPhase%s' % self.phase): + getattr(self, 'exitPhase%s' % self.phase)() + + def enterPhase0(self): + self.animSeq = Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=604800, constrainedLoop=1, startFrame=1, endFrame=30), Sequence(Func(self.phase1Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase1Sfx, self.sillyMeter))) + self.animSeq.start() + self.sillyMeter.loop('phaseOne', partName='meter') + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase0(self): + self.animSeq.finish() + del self.animSeq + self.audio3d.detachSound(self.phase1Sfx) + self.phase1Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase1(self): + self.audio3d.attachSoundToObject(self.phase1Sfx, self.sillyMeter) + self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=31, endFrame=42), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=604800, constrainedLoop=1, startFrame=42, endFrame=71), Sequence(Func(self.phase1Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase1Sfx, self.sillyMeter)))) + self.animSeq.start() + self.sillyMeter.loop('phaseOne', partName='meter') + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase1(self): + self.audio3d.detachSound(self.phase1Sfx) + self.phase1Sfx.stop() + self.animSeq.finish() + del self.animSeq + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase2(self): + self.animSeq = Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=604800, constrainedLoop=1, startFrame=42, endFrame=71), Sequence(Func(self.phase1Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase1Sfx, self.sillyMeter))) + self.animSeq.start() + self.smPhase2.show() + self.sillyMeter.loop('phaseOne', partName='meter') + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase2(self): + self.animSeq.finish() + del self.animSeq + self.smPhase2.hide() + self.audio3d.detachSound(self.phase1Sfx) + self.phase1Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase3(self): + self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=72, endFrame=83), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=604800, constrainedLoop=1, startFrame=83, endFrame=112), Sequence(Func(self.phase1Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase1Sfx, self.sillyMeter)))) + self.animSeq.start() + self.smPhase2.show() + self.sillyMeter.loop('phaseOne', partName='meter') + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase3(self): + self.animSeq.finish() + del self.animSeq + self.smPhase2.hide() + self.audio3d.detachSound(self.phase1Sfx) + self.phase1Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase4(self): + self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=113, endFrame=124), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=604800, constrainedLoop=1, startFrame=124, endFrame=153), Sequence(Func(self.phase1Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase1Sfx, self.sillyMeter)))) + self.animSeq.start() + self.smPhase2.show() + self.sillyMeter.loop('phaseOne', partName='meter') + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase4(self): + self.animSeq.finish() + del self.animSeq + self.smPhase2.hide() + self.audio3d.detachSound(self.phase1Sfx) + self.phase1Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase5(self): + self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=154, endFrame=165), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=604800, constrainedLoop=1, startFrame=165, endFrame=194), Sequence(Func(self.phase2Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase2Sfx, self.sillyMeter)))) + self.animSeq.start() + self.smPhase2.show() + self.sillyMeter.loop('phaseTwo', partName='meter') + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase5(self): + self.animSeq.finish() + del self.animSeq + self.smPhase2.hide() + self.audio3d.detachSound(self.phase2Sfx) + self.phase2Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase6(self): + self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=195, endFrame=206), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=604800, constrainedLoop=1, startFrame=206, endFrame=235), Sequence(Func(self.phase2Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase2Sfx, self.sillyMeter)))) + self.animSeq.start() + self.smPhase2.show() + self.sillyMeter.loop('phaseTwo', partName='meter') + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase6(self): + self.animSeq.finish() + del self.animSeq + self.smPhase2.hide() + self.audio3d.detachSound(self.phase2Sfx) + self.phase2Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase7(self): + self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=236, endFrame=247), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=604800, constrainedLoop=1, startFrame=247, endFrame=276), Sequence(Func(self.phase3Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase3Sfx, self.sillyMeter)))) + self.animSeq.start() + self.smPhase2.show() + self.smPhase3.show() + self.sillyMeter.loop('phaseThree', partName='meter') + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase7(self): + self.animSeq.finish() + del self.animSeq + self.smPhase2.hide() + self.smPhase3.hide() + self.audio3d.detachSound(self.phase3Sfx) + self.phase3Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase8(self): + self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=277, endFrame=288), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=604800, constrainedLoop=1, startFrame=288, endFrame=317), Sequence(Func(self.phase3Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase3Sfx, self.sillyMeter)))) + self.animSeq.start() + self.smPhase2.show() + self.smPhase3.show() + self.sillyMeter.loop('phaseThree', partName='meter') + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase8(self): + self.animSeq.finish() + del self.animSeq + self.smPhase2.hide() + self.smPhase3.hide() + self.audio3d.detachSound(self.phase3Sfx) + self.phase3Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase9(self): + self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=318, endFrame=329), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=604800, constrainedLoop=1, startFrame=329, endFrame=358), Sequence(Func(self.phase3Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase3Sfx, self.sillyMeter)))) + self.animSeq.start() + self.smPhase2.show() + self.smPhase3.show() + self.sillyMeter.loop('phaseThree', partName='meter') + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase9(self): + self.animSeq.finish() + del self.animSeq + self.smPhase2.hide() + self.smPhase3.hide() + self.audio3d.detachSound(self.phase3Sfx) + self.phase3Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase10(self): + self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=359, endFrame=370), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=604800, constrainedLoop=1, startFrame=370, endFrame=399), Sequence(Func(self.phase4Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase4Sfx, self.sillyMeter)))) + self.animSeq.start() + self.smPhase2.show() + self.smPhase3.show() + self.smPhase4.show() + self.sillyMeter.loop('phaseFour', partName='meter') + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase10(self): + self.animSeq.finish() + del self.animSeq + self.smPhase2.hide() + self.smPhase3.hide() + self.smPhase4.hide() + self.audio3d.detachSound(self.phase4Sfx) + self.phase4Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase11(self): + self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=400, endFrame=411), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=604800, constrainedLoop=1, startFrame=411, endFrame=440), Sequence(Func(self.phase4Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase4Sfx, self.sillyMeter)))) + self.animSeq.start() + self.smPhase2.show() + self.smPhase3.show() + self.smPhase4.show() + self.sillyMeter.loop('phaseFour', partName='meter') + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase11(self): + self.animSeq.finish() + del self.animSeq + self.smPhase2.hide() + self.smPhase3.hide() + self.smPhase4.hide() + self.audio3d.detachSound(self.phase4Sfx) + self.phase4Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase12(self): + self.animSeq = Sequence(Sequence(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', constrainedLoop=0, startFrame=441, endFrame=452), Func(self.arrowSfx.play)), Parallel(ActorInterval(self.sillyMeter, 'arrowTube', partName='arrow', duration=604800, constrainedLoop=1, startFrame=452, endFrame=481), Sequence(Func(self.phase4Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase4Sfx, self.sillyMeter)))) + self.animSeq.start() + self.smPhase2.show() + self.smPhase3.show() + self.smPhase4.show() + self.sillyMeter.loop('phaseFour', partName='meter') + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase12(self): + self.animSeq.finish() + del self.animSeq + self.smPhase2.hide() + self.smPhase3.hide() + self.smPhase4.hide() + self.audio3d.detachSound(self.phase4Sfx) + self.phase4Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase13(self): + self.animSeq = Sequence(Parallel(Func(self.phase4To5Sfx.play), ActorInterval(self.sillyMeter, 'phaseFourToFive', constrainedLoop=0, startFrame=1, endFrame=120)), Parallel(ActorInterval(self.sillyMeter, 'phaseFive', duration=604800, constrainedLoop=1, startFrame=1, endFrame=48), Sequence(Func(self.phase5Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase5Sfx, self.sillyMeter)))) + self.animSeq.start() + self.smPhase2.show() + self.smPhase3.show() + self.smPhase4.show() + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase13(self): + self.animSeq.finish() + del self.animSeq + self.smPhase2.hide() + self.smPhase3.hide() + self.smPhase4.hide() + self.audio3d.detachSound(self.phase5Sfx) + self.phase5Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterPhase14(self): + self.animSeq = Parallel(ActorInterval(self.sillyMeter, 'phaseFive', duration=604800, constrainedLoop=1, startFrame=1, endFrame=48), Sequence(Func(self.phase5Sfx.play), Func(self.audio3d.attachSoundToObject, self.phase5Sfx, self.sillyMeter))) + self.animSeq.start() + self.smPhase2.show() + self.smPhase3.show() + self.smPhase4.show() + self.accept('SillyMeterPhase', self.startSillyMeter) + + def exitPhase14(self): + self.animSeq.finish() + del self.animSeq + self.smPhase2.hide() + self.smPhase3.hide() + self.smPhase4.hide() + self.audio3d.detachSound(self.phase5Sfx) + self.phase5Sfx.stop() + self.sillyMeter.stop() + self.ignore('SillyMeterPhase') + + def enterOff(self): + self.ignore('SillyMeterPhase') + if hasattr(self, 'sillyMeter'): + del self.sillyMeter + if hasattr(self, 'smPhase1'): + del self.smPhase1 + if hasattr(self, 'smPhase2'): + del self.smPhase2 + if hasattr(self, 'smPhase3'): + del self.smPhase3 + if hasattr(self, 'smPhase4'): + del self.smPhase4 + self.cleanUpSounds() + + def exitOff(self): + pass + + def cleanUpSounds(self): + + def __cleanUpSound__(soundFile): + if soundFile.status() == soundFile.PLAYING: + soundFile.setLoop(False) + soundFile.stop() + + if hasattr(self, 'audio3d'): + self.audio3d.disable() + del self.audio3d + if hasattr(self, 'phase1Sfx'): + __cleanUpSound__(self.phase1Sfx) + del self.phase1Sfx + if hasattr(self, 'phase2Sfx'): + __cleanUpSound__(self.phase2Sfx) + del self.phase2Sfx + if hasattr(self, 'phase3Sfx'): + __cleanUpSound__(self.phase3Sfx) + del self.phase3Sfx + if hasattr(self, 'phase4Sfx'): + __cleanUpSound__(self.phase4Sfx) + del self.phase4Sfx + if hasattr(self, 'phase4To5Sfx'): + __cleanUpSound__(self.phase4To5Sfx) + del self.phase4To5Sfx + if hasattr(self, 'phase5Sfx'): + __cleanUpSound__(self.phase5Sfx) + del self.phase5Sfx + if hasattr(self, 'arrowSfx'): + __cleanUpSound__(self.arrowSfx) + del self.arrowSfx + + def disable(self): + self.ignoreAll() + self.stopSillyMeter() + self.enterOff() + DistributedToonInterior.disable(self) + + def delete(self): + DistributedToonInterior.delete(self) + +@magicWord(category=CATEGORY_CREATIVE, types=[int]) +def sillyPhase(phase): + """ + Set the silly meter phase. + """ + + if phase < -1 or phase > 14: + return 'Phase number must be between 0 and 14!' + + messenger.send('SillyMeterPhase', [phase]) + return 'Successfully set the silly meter phase!' diff --git a/toontown/building/DistributedToonHallInteriorAI.py b/toontown/building/DistributedToonHallInteriorAI.py new file mode 100755 index 00000000..39f004dd --- /dev/null +++ b/toontown/building/DistributedToonHallInteriorAI.py @@ -0,0 +1,4 @@ +from DistributedToonInteriorAI import * + +class DistributedToonHallInteriorAI(DistributedToonInteriorAI): + pass diff --git a/toontown/building/DistributedToonInterior.py b/toontown/building/DistributedToonInterior.py new file mode 100755 index 00000000..f410c26f --- /dev/null +++ b/toontown/building/DistributedToonInterior.py @@ -0,0 +1,216 @@ +import cPickle +import random + +import ToonInterior +import ToonInteriorColors +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.interval.IntervalGlobal import * +from otp.speedchat import SpeedChatGlobals +from panda3d.core import * +from toontown.dna.DNAParser import * +from toontown.hood import ZoneUtil +from toontown.toon import ToonDNA +from toontown.toon import ToonHead +from toontown.toon.DistributedNPCToonBase import DistributedNPCToonBase +from toontown.toonbase import ToontownGlobals +from toontown.toonbase.ToonBaseGlobal import * + + +SIGN_LEFT = -4 +SIGN_RIGHT = 4 +SIGN_BOTTOM = -3.5 +SIGN_TOP = 1.5 +FrameScale = 1.4 + +class DistributedToonInterior(DistributedObject.DistributedObject): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.fsm = ClassicFSM.ClassicFSM('DistributedToonInterior', [State.State('toon', self.enterToon, self.exitToon, ['beingTakenOver']), State.State('beingTakenOver', self.enterBeingTakenOver, self.exitBeingTakenOver, []), State.State('off', self.enterOff, self.exitOff, [])], 'toon', 'off') + self.fsm.enterInitialState() + + def getModelType(self, zoneId): + self.dnaStore = base.cr.playGame.dnaStore + self.randomGenerator = random.Random() + self.randomGenerator.seed(zoneId) + self.interior = self.randomDNAItem('TI_room', self.dnaStore.findNode) + self.interiorModel = str(self.interior.getName().split('.egg')[0]) + return self.interiorModel + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.setup() + + def disable(self): + self.interior.removeNode() + del self.interior + self.ignore(SpeedChatGlobals.SCStaticTextMsgEvent) + DistributedObject.DistributedObject.disable(self) + + def delete(self): + del self.fsm + self.ignore(SpeedChatGlobals.SCStaticTextMsgEvent) + DistributedObject.DistributedObject.delete(self) + + def randomDNAItem(self, category, findFunc): + codeCount = self.dnaStore.getNumCatalogCodes(category) + index = self.randomGenerator.randint(0, codeCount - 1) + code = self.dnaStore.getCatalogCode(category, index) + return findFunc(code) + + def replaceRandomInModel(self, model): + baseTag = 'random_' + npc = model.findAllMatches('**/' + baseTag + '???_*') + for i in xrange(npc.getNumPaths()): + np = npc.getPath(i) + name = np.getName() + b = len(baseTag) + category = name[b + 4:] + key1 = name[b] + key2 = name[b + 1] + if key1 == 'm': + model = self.randomDNAItem(category, self.dnaStore.findNode) + newNP = model.copyTo(np) + c = render.findAllMatches('**/collision') + c.stash() + if key2 == 'r': + self.replaceRandomInModel(newNP) + elif key1 == 't': + texture = self.randomDNAItem(category, self.dnaStore.findTexture) + np.setTexture(texture, 100) + newNP = np + if key2 == 'c': + if category == 'TI_wallpaper' or category == 'TI_wallpaper_border': + self.randomGenerator.seed(self.zoneId) + newNP.setColorScale(self.randomGenerator.choice(self.colors[category])) + else: + newNP.setColorScale(self.randomGenerator.choice(self.colors[category])) + + def setup(self): + self.dnaStore = base.cr.playGame.dnaStore + self.randomGenerator = random.Random() + self.randomGenerator.seed(self.zoneId) + interior = self.randomDNAItem('TI_room', self.dnaStore.findNode) + self.interior = interior.copyTo(render) + hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId) + self.colors = ToonInteriorColors.colors[hoodId] + self.replaceRandomInModel(self.interior) + doorModelName = 'door_double_round_ul' + if doorModelName[-1:] == 'r': + doorModelName = doorModelName[:-1] + 'l' + else: + doorModelName = doorModelName[:-1] + 'r' + door = self.dnaStore.findNode(doorModelName) + door_origin = render.find('**/door_origin;+s') + doorNP = door.copyTo(door_origin) + door_origin.setScale(0.8, 0.8, 0.8) + door_origin.setPos(door_origin, 0, -0.025, 0) + color = self.randomGenerator.choice(self.colors['TI_door']) + setupDoor(doorNP, self.interior, door_origin, self.dnaStore, str(self.block), color) + doorFrame = doorNP.find('door_*_flat') + doorFrame.wrtReparentTo(self.interior) + doorFrame.setColor(color) + sign = hidden.find('**/tb%s:*_landmark_*_DNARoot/**/sign;+s' % (self.block,)) + if not sign.isEmpty(): + signOrigin = self.interior.find('**/sign_origin;+s') + newSignNP = sign.copyTo(signOrigin) + newSignNP.setDepthWrite(1, 1) + newSignNP.flattenLight() + ll = Point3() + ur = Point3() + newSignNP.calcTightBounds(ll, ur) + width = ur[0] - ll[0] + height = ur[2] - ll[2] + if width != 0 and height != 0: + xScale = (SIGN_RIGHT - SIGN_LEFT) / width + zScale = (SIGN_TOP - SIGN_BOTTOM) / height + scale = min(xScale, zScale) + xCenter = (ur[0] + ll[0]) / 2.0 + zCenter = (ur[2] + ll[2]) / 2.0 + newSignNP.setPosHprScale((SIGN_RIGHT + SIGN_LEFT) / 2.0 - xCenter * scale, -0.1, (SIGN_TOP + SIGN_BOTTOM) / 2.0 - zCenter * scale, 0.0, 0.0, 0.0, scale, scale, scale) + trophyOrigin = self.interior.find('**/trophy_origin') + trophy = self.buildTrophy() + if trophy: + trophy.reparentTo(trophyOrigin) + del self.colors + del self.dnaStore + del self.randomGenerator + self.interior.flattenMedium() + for npcToon in self.cr.doFindAllInstances(DistributedNPCToonBase): + npcToon.initToonState() + + def setZoneIdAndBlock(self, zoneId, block): + self.zoneId = zoneId + self.block = block + + def setToonData(self, toonData): + savedBy = cPickle.loads(toonData) + self.savedBy = savedBy + + def buildTrophy(self): + if self.savedBy == None: + return + numToons = len(self.savedBy) + pos = 1.25 - 1.25 * numToons + trophy = hidden.attachNewNode('trophy') + for avId, name, dnaTuple in self.savedBy: + frame = self.buildFrame(name, dnaTuple) + frame.reparentTo(trophy) + frame.setPos(pos, 0, 0) + pos += 2.5 + + return trophy + + def buildFrame(self, name, dnaTuple): + frame = loader.loadModel('phase_3.5/models/modules/trophy_frame') + dna = ToonDNA.ToonDNA() + apply(dna.newToonFromProperties, dnaTuple) + head = ToonHead.ToonHead() + head.setupHead(dna) + head.setPosHprScale(0, -0.05, -0.05, 180, 0, 0, 0.55, 0.02, 0.55) + if dna.head[0] == 'r': + head.setZ(-0.15) + elif dna.head[0] == 'h': + head.setZ(0.05) + elif dna.head[0] == 'm': + head.setScale(0.45, 0.02, 0.45) + head.reparentTo(frame) + nameText = TextNode('trophy') + nameText.setFont(ToontownGlobals.getToonFont()) + nameText.setAlign(TextNode.ACenter) + nameText.setTextColor(0, 0, 0, 1) + nameText.setWordwrap(5.36 * FrameScale) + nameText.setText(name) + namePath = frame.attachNewNode(nameText.generate()) + namePath.setPos(0, -0.03, -.6) + namePath.setScale(0.186 / FrameScale) + frame.setScale(FrameScale, 1.0, FrameScale) + return frame + + def setState(self, state, timestamp): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterToon(self): + pass + + def exitToon(self): + pass + + def enterBeingTakenOver(self, ts): + messenger.send('clearOutToonInterior') + + def exitBeingTakenOver(self): + pass diff --git a/toontown/building/DistributedToonInteriorAI.py b/toontown/building/DistributedToonInteriorAI.py new file mode 100755 index 00000000..d23ec675 --- /dev/null +++ b/toontown/building/DistributedToonInteriorAI.py @@ -0,0 +1,67 @@ +import cPickle + +from direct.distributed import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from otp.ai.AIBaseGlobal import * +from toontown.toon import NPCToons +from toontown.toonbase.ToontownGlobals import * + + +class DistributedToonInteriorAI(DistributedObjectAI.DistributedObjectAI): + def __init__(self, block, air, zoneId, building): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.block = block + self.zoneId = zoneId + self.building = building + self.npcs = NPCToons.createNpcsInZone(air, zoneId) + self.fsm = ClassicFSM.ClassicFSM( + 'DistributedToonInteriorAI', + [ + State.State('toon', self.enterToon, self.exitToon, + ['beingTakenOver']), + State.State('beingTakenOver', self.enterBeingTakenOver, self.exitBeingTakenOver, []), + State.State('off', self.enterOff, self.exitOff, []) + ], 'toon', 'off') + self.fsm.enterInitialState() + + def delete(self): + self.ignoreAll() + for npc in self.npcs: + npc.requestDelete() + del self.npcs + del self.fsm + del self.building + del self.block + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getZoneIdAndBlock(self): + return [self.zoneId, self.block] + + def getToonData(self): + return cPickle.dumps(self.building.savedBy, 1) + + def getState(self): + return [self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()] + + def setState(self, state): + self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()]) + self.fsm.request(state) + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterToon(self): + pass + + def exitToon(self): + pass + + def enterBeingTakenOver(self): + pass + + def exitBeingTakenOver(self): + pass diff --git a/toontown/building/DistributedTrophyMgr.py b/toontown/building/DistributedTrophyMgr.py new file mode 100755 index 00000000..ef334a78 --- /dev/null +++ b/toontown/building/DistributedTrophyMgr.py @@ -0,0 +1,30 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer + +class DistributedTrophyMgr(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTrophyMgr') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + if base.cr.trophyManager != None: + base.cr.trophyManager.delete() + base.cr.trophyManager = self + DistributedObject.DistributedObject.generate(self) + return + + def disable(self): + base.cr.trophyManager = None + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + base.cr.trophyManager = None + DistributedObject.DistributedObject.delete(self) + return + + def d_requestTrophyScore(self): + self.sendUpdate('requestTrophyScore', []) diff --git a/toontown/building/DistributedTrophyMgrAI.py b/toontown/building/DistributedTrophyMgrAI.py new file mode 100755 index 00000000..4a1978bf --- /dev/null +++ b/toontown/building/DistributedTrophyMgrAI.py @@ -0,0 +1,91 @@ +from direct.directnotify.DirectNotifyGlobal import * +from direct.distributed.DistributedObjectAI import DistributedObjectAI + + +MAX_LISTING = 10 + +AV_ID_INDEX = 0 +NAME_INDEX = 1 +SCORE_INDEX = 2 + + +class DistributedTrophyMgrAI(DistributedObjectAI): + notify = directNotify.newCategory('DistributedTrophyMgrAI') + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + + # Load the leaderboard backup for this shard: + self.leaderInfo, self.trophyScores = simbase.backups.load( + 'trophy-mgr', (simbase.air.districtId,), default=(([], [], []), {})) + + def requestTrophyScore(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av is not None: + av.d_setTrophyScore(self.trophyScores.get(avId, 0)) + + def addTrophy(self, avId, name, numFloors): + if avId not in self.trophyScores: + self.trophyScores[avId] = 0 + trophyScore = self.trophyScores[avId] + numFloors + self.updateTrophyScore(avId, trophyScore) + + def removeTrophy(self, avId, numFloors): + if avId in self.trophyScores: + trophyScore = self.trophyScores[avId] - numFloors + self.updateTrophyScore(avId, trophyScore) + + def updateTrophyScore(self, avId, trophyScore): + av = self.air.doId2do.get(avId) + + if trophyScore <= 0: + # Take the player off the listing: + if avId in self.trophyScores: + del self.trophyScores[avId] + if avId in self.leaderInfo[AV_ID_INDEX]: + scoreIndex = self.leaderInfo[AV_ID_INDEX].index(avId) + del self.leaderInfo[AV_ID_INDEX][scoreIndex] + del self.leaderInfo[NAME_INDEX][scoreIndex] + del self.leaderInfo[SCORE_INDEX][scoreIndex] + else: + # Add the player to the listing if they haven't been. Otherwise, + # update their current trophy score: + self.trophyScores[avId] = trophyScore + if avId not in self.leaderInfo[AV_ID_INDEX]: + if av is None: + return + self.leaderInfo[AV_ID_INDEX].append(avId) + self.leaderInfo[NAME_INDEX].append(av.getName()) + self.leaderInfo[SCORE_INDEX].append(trophyScore) + else: + scoreIndex = self.leaderInfo[AV_ID_INDEX].index(avId) + self.leaderInfo[SCORE_INDEX][scoreIndex] = trophyScore + + # Truncate and reorganize the listing: + self.reorganize() + + # Update the listing in the various Toon HQs: + messenger.send('leaderboardChanged') + messenger.send('leaderboardFlush') + + if av is not None: + av.d_setTrophyScore(trophyScore) + + simbase.backups.save('trophy-mgr', (simbase.air.districtId,), + (self.leaderInfo, self.trophyScores)) + + def reorganize(self): + # Sort the leader info: + leaderInfo = zip(*reversed(self.leaderInfo)) + leaderInfo.sort(reverse=True) + + # Construct the new, truncated leader info: + self.leaderInfo = [[], [], []] + for trophyScore, name, avId in leaderInfo[:MAX_LISTING]: + self.leaderInfo[AV_ID_INDEX].append(avId) + self.leaderInfo[NAME_INDEX].append(name) + self.leaderInfo[SCORE_INDEX].append(trophyScore) + + def getLeaderInfo(self): + return self.leaderInfo diff --git a/toontown/building/DistributedTutorialInterior.py b/toontown/building/DistributedTutorialInterior.py new file mode 100755 index 00000000..3889e464 --- /dev/null +++ b/toontown/building/DistributedTutorialInterior.py @@ -0,0 +1,164 @@ +from toontown.toonbase.ToonBaseGlobal import * +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownGlobals +from toontown.dna.DNAParser import * +import ToonInterior +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +import random +import ToonInteriorColors +from toontown.hood import ZoneUtil +from toontown.suit import SuitDNA +from toontown.suit import Suit +from toontown.quest import QuestParser +from toontown.toon import DistributedNPCSpecialQuestGiver +from toontown.toonbase import TTLocalizer +from otp.nametag.NametagConstants import CFSpeech + + +class DistributedTutorialInterior(DistributedObject.DistributedObject): + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + + if not base.cr.doFindAllInstances(DistributedNPCSpecialQuestGiver.DistributedNPCSpecialQuestGiver): + self.acceptOnce('doneTutorialSetup', self.setup) + else: + self.setup() + + def disable(self): + self.interior.removeNode() + del self.interior + self.street.removeNode() + del self.street + self.sky.removeNode() + del self.sky + self.suitWalkTrack.finish() + del self.suitWalkTrack + self.suit.delete() + del self.suit + self.ignore('enterTutorialInterior') + + DistributedObject.DistributedObject.disable(self) + + def randomDNAItem(self, category, findFunc): + codeCount = self.dnaStore.getNumCatalogCodes(category) + index = self.randomGenerator.randint(0, codeCount - 1) + code = self.dnaStore.getCatalogCode(category, index) + return findFunc(code) + + def replaceRandomInModel(self, model): + baseTag = 'random_' + npc = model.findAllMatches('**/' + baseTag + '???_*') + for i in xrange(npc.getNumPaths()): + np = npc.getPath(i) + name = np.getName() + b = len(baseTag) + category = name[b + 4:] + key1 = name[b] + key2 = name[b + 1] + if key1 == 'm': + model = self.randomDNAItem(category, self.dnaStore.findNode) + newNP = model.copyTo(np) + c = render.findAllMatches('**/collision') + c.stash() + if key2 == 'r': + self.replaceRandomInModel(newNP) + elif key1 == 't': + texture = self.randomDNAItem(category, self.dnaStore.findTexture) + np.setTexture(texture, 100) + newNP = np + if key2 == 'c': + if category == 'TI_wallpaper' or category == 'TI_wallpaper_border': + self.randomGenerator.seed(self.zoneId) + newNP.setColorScale(self.randomGenerator.choice(self.colors[category])) + else: + newNP.setColorScale(self.randomGenerator.choice(self.colors[category])) + + def setup(self): + self.dnaStore = base.cr.playGame.dnaStore + self.randomGenerator = random.Random() + self.randomGenerator.seed(self.zoneId) + self.interior = loader.loadModel('phase_3.5/models/modules/toon_interior_tutorial') + self.interior.reparentTo(render) + dnaStore = DNAStorage() + node = loader.loadDNAFile(self.cr.playGame.hood.dnaStore, 'phase_3.5/dna/tutorial_street.pdna') + self.street = render.attachNewNode(node) + self.street.flattenMedium() + self.street.setPosHpr(-17, 42, -0.5, 180, 0, 0) + self.street.find('**/tb2:toon_landmark_TT_A1_DNARoot').stash() + self.street.find('**/tb1:toon_landmark_hqTT_DNARoot/**/door_flat_0').stash() + self.street.findAllMatches('**/+CollisionNode').stash() + self.skyFile = 'phase_3.5/models/props/TT_sky' + self.sky = loader.loadModel(self.skyFile) + self.sky.setScale(0.8) + self.sky.reparentTo(render) + self.sky.setDepthTest(0) + self.sky.setDepthWrite(0) + self.sky.setBin('background', 100) + self.sky.find('**/Sky').reparentTo(self.sky, -1) + hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId) + self.colors = ToonInteriorColors.colors[hoodId] + self.replaceRandomInModel(self.interior) + doorModelName = 'door_double_round_ul' + if doorModelName[-1:] == 'r': + doorModelName = doorModelName[:-1] + 'l' + else: + doorModelName = doorModelName[:-1] + 'r' + door = self.dnaStore.findNode(doorModelName) + door_origin = render.find('**/door_origin;+s') + doorNP = door.copyTo(door_origin) + door_origin.setScale(0.8, 0.8, 0.8) + door_origin.setPos(door_origin, 0, -0.025, 0) + color = self.randomGenerator.choice(self.colors['TI_door']) + setupDoor(doorNP, self.interior, door_origin, self.dnaStore, str(self.block), color) + doorFrame = doorNP.find('door_*_flat') + doorFrame.wrtReparentTo(self.interior) + doorFrame.setColor(color) + del self.colors + del self.dnaStore + del self.randomGenerator + self.interior.flattenMedium() + npcOrigin = self.interior.find('**/npc_origin_' + `(self.cr.doId2do[self.npcId].posIndex)`) + if not npcOrigin.isEmpty(): + self.cr.doId2do[self.npcId].reparentTo(npcOrigin) + self.cr.doId2do[self.npcId].clearMat() + self.createSuit() + base.localAvatar.setPosHpr(-2, 12, 0, -10, 0, 0) + self.cr.doId2do[self.npcId].setChatAbsolute(TTLocalizer.QuestScript101_0, CFSpeech) + place = base.cr.playGame.getPlace() + if place and hasattr(place, 'fsm') and place.fsm.getCurrentState().getName(): + self.notify.info('Tutorial movie: Place ready.') + self.playMovie() + else: + self.notify.info('Tutorial movie: Waiting for place=%s, has fsm=%s' % (place, hasattr(place, 'fsm'))) + if hasattr(place, 'fsm'): + self.notify.info('Tutorial movie: place state=%s' % place.fsm.getCurrentState().getName()) + self.acceptOnce('enterTutorialInterior', self.playMovie) + + def playMovie(self): + self.notify.info('Tutorial movie: Play.') + + def createSuit(self): + self.suit = Suit.Suit() + suitDNA = SuitDNA.SuitDNA() + suitDNA.newSuit('f') + self.suit.setDNA(suitDNA) + self.suit.nametag3d.stash() + self.suit.nametag.destroy() + self.suit.loop('neutral') + self.suit.setPosHpr(-20, 8, 0, 0, 0, 0) + self.suit.reparentTo(self.interior) + self.suitWalkTrack = Sequence(self.suit.hprInterval(0.1, Vec3(0, 0, 0)), Func(self.suit.loop, 'walk'), self.suit.posInterval(2, Point3(-20, 20, 0)), Func(self.suit.loop, 'neutral'), Wait(1.0), self.suit.hprInterval(0.1, Vec3(180, 0, 0)), Func(self.suit.loop, 'walk'), self.suit.posInterval(2, Point3(-20, 10, 0)), Func(self.suit.loop, 'neutral'), Wait(1.0)) + self.suitWalkTrack.loop() + + def setZoneIdAndBlock(self, zoneId, block): + self.zoneId = zoneId + self.block = block + + def setTutorialNpcId(self, npcId): + self.npcId = npcId + + def getTutorialNpc(self): + return self.cr.doId2do[self.npcId] diff --git a/toontown/building/DistributedTutorialInteriorAI.py b/toontown/building/DistributedTutorialInteriorAI.py new file mode 100755 index 00000000..8fbf936b --- /dev/null +++ b/toontown/building/DistributedTutorialInteriorAI.py @@ -0,0 +1,26 @@ +from direct.directnotify.DirectNotifyGlobal import * +from direct.distributed.DistributedObjectAI import DistributedObjectAI + + +class DistributedTutorialInteriorAI(DistributedObjectAI): + notify = directNotify.newCategory('DistributedTutorialInteriorAI') + + def __init__(self, air, block, zoneId, tutorialNpcId): + DistributedObjectAI.__init__(self, air) + + self.zoneId = zoneId + self.block = block + self.tutorialNpcId = tutorialNpcId + + def setZoneIdAndBlock(self, zoneId, block): + self.zoneId = zoneId + self.block = block + + def getZoneIdAndBlock(self): + return [self.zoneId, self.block] + + def setTutorialNpcId(self, npcId): + self.tutorialNpcId = npcId + + def getTutorialNpcId(self): + return self.tutorialNpcId diff --git a/toontown/building/DistributedVPElevator.py b/toontown/building/DistributedVPElevator.py new file mode 100755 index 00000000..9ba22039 --- /dev/null +++ b/toontown/building/DistributedVPElevator.py @@ -0,0 +1,27 @@ +import DistributedElevator +import DistributedBossElevator +from ElevatorConstants import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer + +class DistributedVPElevator(DistributedBossElevator.DistributedBossElevator): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedVPElevator') + def __init__(self, cr): + DistributedBossElevator.DistributedBossElevator.__init__(self, cr) + self.type = ELEVATOR_VP + self.countdownTime = ElevatorData[self.type]['countdown'] + + def setupElevator(self): + self.elevatorModel = loader.loadModel('phase_9/models/cogHQ/cogHQ_elevator') + icon = self.elevatorModel.find('**/big_frame/') + icon.hide() + self.leftDoor = self.elevatorModel.find('**/left-door') + self.rightDoor = self.elevatorModel.find('**/right-door') + geom = base.cr.playGame.hood.loader.geom + locator = geom.find('**/elevator_locator') + self.elevatorModel.reparentTo(locator) + self.elevatorModel.setH(180) + DistributedElevator.DistributedElevator.setupElevator(self) + + def getDestName(self): + return TTLocalizer.ElevatorSellBotBoss diff --git a/toontown/building/DistributedVPElevatorAI.py b/toontown/building/DistributedVPElevatorAI.py new file mode 100755 index 00000000..2eaa9d3a --- /dev/null +++ b/toontown/building/DistributedVPElevatorAI.py @@ -0,0 +1,9 @@ +from ElevatorConstants import * +import DistributedBossElevatorAI + +class DistributedVPElevatorAI(DistributedBossElevatorAI.DistributedBossElevatorAI): + + def __init__(self, air, bldg, zone): + DistributedBossElevatorAI.DistributedBossElevatorAI.__init__(self, air, bldg, zone) + self.type = ELEVATOR_VP + self.countdownTime = ElevatorData[self.type]['countdown'] diff --git a/toontown/building/DoorTypes.py b/toontown/building/DoorTypes.py new file mode 100755 index 00000000..97e85de4 --- /dev/null +++ b/toontown/building/DoorTypes.py @@ -0,0 +1,10 @@ +EXT_STANDARD = 1 +INT_STANDARD = 2 +EXT_HQ = 3 +INT_HQ = 4 +EXT_HOUSE = 5 +INT_HOUSE = 6 +EXT_COGHQ = 7 +INT_COGHQ = 8 +EXT_KS = 9 +INT_KS = 10 diff --git a/toontown/building/Elevator.py b/toontown/building/Elevator.py new file mode 100755 index 00000000..e2f6c7ab --- /dev/null +++ b/toontown/building/Elevator.py @@ -0,0 +1,123 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil + +class Elevator(StateData.StateData): + + def __init__(self, elevatorState, doneEvent, distElevator): + StateData.StateData.__init__(self, doneEvent) + self.fsm = ClassicFSM.ClassicFSM('Elevator', [State.State('start', self.enterStart, self.exitStart, ['requestBoard', 'final']), + State.State('requestBoard', self.enterRequestBoard, self.exitRequestBoard, ['boarding']), + State.State('boarding', self.enterBoarding, self.exitBoarding, ['boarded']), + State.State('boarded', self.enterBoarded, self.exitBoarded, ['requestExit', 'elevatorClosing', 'final']), + State.State('requestExit', self.enterRequestExit, self.exitRequestExit, ['exiting', 'elevatorClosing']), + State.State('elevatorClosing', self.enterElevatorClosing, self.exitElevatorClosing, ['final']), + State.State('exiting', self.enterExiting, self.exitExiting, ['final']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.elevatorState = elevatorState + self.distElevator = distElevator + distElevator.elevatorFSM = self + self.reverseBoardingCamera = False + + def load(self): + self.elevatorState.addChild(self.fsm) + self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + self.upButton = self.buttonModels.find('**//InventoryButtonUp') + self.downButton = self.buttonModels.find('**/InventoryButtonDown') + self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover') + + def unload(self): + self.elevatorState.removeChild(self.fsm) + self.distElevator.elevatorFSM = None + del self.distElevator + del self.fsm + del self.elevatorState + self.buttonModels.removeNode() + del self.buttonModels + del self.upButton + del self.downButton + del self.rolloverButton + return + + def enter(self): + self.fsm.enterInitialState() + self.fsm.request('requestBoard') + + def exit(self): + self.ignoreAll() + + def signalDone(self, doneStatus): + messenger.send(self.doneEvent, [doneStatus]) + + def enterStart(self): + pass + + def exitStart(self): + pass + + def enterRequestBoard(self): + messenger.send(self.distElevator.uniqueName('enterElevatorOK')) + + def exitRequestBoard(self): + pass + + def enterBoarding(self, nodePath): + camera.wrtReparentTo(nodePath) + if self.reverseBoardingCamera: + heading = PythonUtil.fitDestAngle2Src(camera.getH(nodePath), 180) + self.cameraBoardTrack = LerpPosHprInterval(camera, 1.5, Point3(0, 18, 8), Point3(heading, -10, 0)) + else: + self.cameraBoardTrack = LerpPosHprInterval(camera, 1.5, Point3(0, -16, 5.5), Point3(0, 0, 0)) + self.cameraBoardTrack.start() + + def exitBoarding(self): + self.ignore('boardedElevator') + + def enterBoarded(self): + self.enableExitButton() + + def exitBoarded(self): + self.cameraBoardTrack.finish() + self.disableExitButton() + + def enableExitButton(self): + self.exitButton = DirectButton(relief=None, text=TTLocalizer.ElevatorHopOff, text_fg=(0.9, 0.9, 0.9, 1), text_pos=(0, -0.23), text_scale=TTLocalizer.EexitButton, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(0.5, 0.5, 0.5, 1), image_scale=(20, 1, 11), pos=(0, 0, 0.8), scale=0.15, command=lambda self = self: self.fsm.request('requestExit')) + if hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty and localAvatar.boardingParty.getGroupLeader(localAvatar.doId) and localAvatar.boardingParty.getGroupLeader(localAvatar.doId) != localAvatar.doId: + self.exitButton['command'] = None + self.exitButton.hide() + + def disableExitButton(self): + self.exitButton.destroy() + + def enterRequestExit(self): + messenger.send('elevatorExitButton') + + def exitRequestExit(self): + pass + + def enterElevatorClosing(self): + pass + + def exitElevatorClosing(self): + pass + + def enterExiting(self): + pass + + def exitExiting(self): + pass + + def enterFinal(self): + pass + + def exitFinal(self): + pass + + def setReverseBoardingCamera(self, newVal): + self.reverseBoardingCamera = newVal diff --git a/toontown/building/ElevatorConstants.py b/toontown/building/ElevatorConstants.py new file mode 100755 index 00000000..0578a269 --- /dev/null +++ b/toontown/building/ElevatorConstants.py @@ -0,0 +1,126 @@ +from panda3d.core import * +ELEVATOR_NORMAL = 0 +ELEVATOR_VP = 1 +ELEVATOR_MINT = 2 +ELEVATOR_CFO = 3 +ELEVATOR_CJ = 4 +ELEVATOR_OFFICE = 5 +ELEVATOR_STAGE = 6 +ELEVATOR_BB = 7 +ELEVATOR_COUNTRY_CLUB = 8 +ELEVATOR_FIELD = 9 +REJECT_NOREASON = 0 +REJECT_NOSEAT = 1 +REJECT_PROMOTION = 2 +REJECT_BLOCKED_ROOM = 3 +REJECT_BOARDINGPARTY = 4 +MAX_GROUP_BOARDING_TIME = 6.0 +ElevatorData = {ELEVATOR_NORMAL: {'openTime': 2.0, + 'closeTime': 2.0, + 'width': 3.5, + 'countdown': bboard.get('elevatorCountdown', 15.0), + 'sfxVolume': 1.0, + 'collRadius': 5}, + ELEVATOR_VP: {'openTime': 4.0, + 'closeTime': 4.0, + 'width': 11.5, + 'countdown': bboard.get('elevatorCountdown', 30.0), + 'sfxVolume': 0.7, + 'collRadius': 7.5}, + ELEVATOR_MINT: {'openTime': 2.0, + 'closeTime': 2.0, + 'width': 5.875, + 'countdown': bboard.get('elevatorCountdown', 15.0), + 'sfxVolume': 1.0, + 'collRadius': 5}, + ELEVATOR_OFFICE: {'openTime': 2.0, + 'closeTime': 2.0, + 'width': 5.875, + 'countdown': bboard.get('elevatorCountdown', 15.0), + 'sfxVolume': 1.0, + 'collRadius': 5}, + ELEVATOR_CFO: {'openTime': 3.0, + 'closeTime': 3.0, + 'width': 8.166, + 'countdown': bboard.get('elevatorCountdown', 30.0), + 'sfxVolume': 0.7, + 'collRadius': 7.5}, + ELEVATOR_CJ: {'openTime': 4.0, + 'closeTime': 4.0, + 'width': 15.8, + 'countdown': bboard.get('elevatorCountdown', 30.0), + 'sfxVolume': 0.7, + 'collRadius': 7.5}, + ELEVATOR_STAGE: {'openTime': 4.0, + 'closeTime': 4.0, + 'width': 6.5, + 'countdown': bboard.get('elevatorCountdown', 42.0), + 'sfxVolume': 1.0, + 'collRadius': 9.5}, + ELEVATOR_BB: {'openTime': 4.0, + 'closeTime': 4.0, + 'width': 6.3, + 'countdown': bboard.get('elevatorCountdown', 30.0), + 'sfxVolume': 0.7, + 'collRadius': 7.5}, + ELEVATOR_COUNTRY_CLUB: {'openTime': 2.0, + 'closeTime': 2.0, + 'width': 5.875, + 'countdown': bboard.get('elevatorCountdown', 15.0), + 'sfxVolume': 1.0, + 'collRadius': 4}, + ELEVATOR_FIELD: {'openTime': 2.0, + 'closeTime': 2.0, + 'width': 3.5, + 'countdown': bboard.get('elevatorCountdown', 15.0), + 'sfxVolume': 1.0, + 'collRadius': 5.6}} + +TOON_BOARD_ELEVATOR_TIME = 1.0 +TOON_EXIT_ELEVATOR_TIME = 1.0 +TOON_VICTORY_EXIT_TIME = 1.0 +SUIT_HOLD_ELEVATOR_TIME = 1.0 +SUIT_LEAVE_ELEVATOR_TIME = 2.0 +INTERIOR_ELEVATOR_COUNTDOWN_TIME = 90 +LIGHT_OFF_COLOR = Vec4(0.5, 0.5, 0.5, 1.0) +LIGHT_ON_COLOR = Vec4(1.0, 1.0, 1.0, 1.0) +ElevatorPoints = [[-1.5, 5, 0.1], + [1.5, 5, 0.1], + [-2.5, 3, 0.1], + [2.5, 3, 0.1], + [-3.5, 5, 0.1], + [3.5, 5, 0.1], + [-4, 3, 0.1], + [4, 3, 0.1]] +JumpOutOffsets = [[-1.5, -5, -0], + [1.5, -5, -0], + [-2.5, -7, -0], + [2.5, -7, -0], + [-3.5, -5, -0], + [3.5, -5, -0], + [-4, -7, -0], + [4, -7, -0]] +BigElevatorPoints = [[-2.5, 9, 0.1], + [2.5, 9, 0.1], + [-8.0, 9, 0.1], + [8.0, 9, 0.1], + [-2.5, 4, 0.1], + [2.5, 4, 0.1], + [-8.0, 4, 0.1], + [8.0, 4, 0.1]] +BossbotElevatorPoints = [[-2.5, 7.5, 0.1], + [2.5, 7.5, 0.1], + [-5.5, 7.5, 0.1], + [5.5, 7.5, 0.1], + [-2.5, 3.5, 0.1], + [2.5, 3.5, 0.1], + [-5.5, 3.5, 0.1], + [5.5, 3.5, 0.1]] +ElevatorOutPoints = [[-4.6, -5.2, 0.1], + [4.6, -5.2, 0.1], + [-1.6, -6.2, 0.1], + [1.6, -6.2, 0.1]] +ElevatorOutPointsFar = [[-4.6, -12.2, 0.1], + [4.6, -12.2, 0.1], + [-1.6, -13.2, 0.1], + [1.6, -13.2, 0.1]] diff --git a/toontown/building/ElevatorUtils.py b/toontown/building/ElevatorUtils.py new file mode 100755 index 00000000..762c460d --- /dev/null +++ b/toontown/building/ElevatorUtils.py @@ -0,0 +1,111 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from ElevatorConstants import * + +def getLeftClosePoint(type): + width = ElevatorData[type]['width'] + return Point3(width, 0, 0) + + +def getRightClosePoint(type): + width = ElevatorData[type]['width'] + return Point3(-width, 0, 0) + + +def getLeftOpenPoint(type): + return Point3(0, 0, 0) + + +def getRightOpenPoint(type): + return Point3(0, 0, 0) + + +def closeDoors(leftDoor, rightDoor, type = ELEVATOR_NORMAL): + closedPosLeft = getLeftClosePoint(type) + closedPosRight = getRightClosePoint(type) + leftDoor.setPos(closedPosLeft) + rightDoor.setPos(closedPosRight) + + +def openDoors(leftDoor, rightDoor, type = ELEVATOR_NORMAL): + openPosLeft = getLeftOpenPoint(type) + openPosRight = getRightOpenPoint(type) + leftDoor.setPos(openPosLeft) + rightDoor.setPos(openPosRight) + + +def getLeftOpenInterval(distObj, leftDoor, type): + openTime = ElevatorData[type]['openTime'] + closedPos = getLeftClosePoint(type) + openPos = getLeftOpenPoint(type) + leftOpenInterval = LerpPosInterval(leftDoor, openTime, openPos, startPos=closedPos, blendType='easeOut', name=distObj.uniqueName('leftDoorOpen')) + return leftOpenInterval + + +def getRightOpenInterval(distObj, rightDoor, type): + openTime = ElevatorData[type]['openTime'] + closedPos = getRightClosePoint(type) + openPos = getRightOpenPoint(type) + rightOpenInterval = LerpPosInterval(rightDoor, openTime, openPos, startPos=closedPos, blendType='easeOut', name=distObj.uniqueName('rightDoorOpen')) + return rightOpenInterval + + +def getOpenInterval(distObj, leftDoor, rightDoor, openSfx, finalOpenSfx, type = ELEVATOR_NORMAL): + left = getLeftOpenInterval(distObj, leftDoor, type) + right = getRightOpenInterval(distObj, rightDoor, type) + openDuration = left.getDuration() + sfxVolume = ElevatorData[type]['sfxVolume'] + if finalOpenSfx: + sound = Sequence(SoundInterval(openSfx, duration=openDuration, volume=sfxVolume, node=leftDoor), SoundInterval(finalOpenSfx, volume=sfxVolume, node=leftDoor)) + else: + sound = SoundInterval(openSfx, volume=sfxVolume, node=leftDoor) + return Parallel(sound, left, right) + + +def getLeftCloseInterval(distObj, leftDoor, type): + closeTime = ElevatorData[type]['closeTime'] + closedPos = getLeftClosePoint(type) + openPos = getLeftOpenPoint(type) + leftCloseInterval = LerpPosInterval(leftDoor, closeTime, closedPos, startPos=openPos, blendType='easeOut', name=distObj.uniqueName('leftDoorClose')) + return leftCloseInterval + + +def getRightCloseInterval(distObj, rightDoor, type): + closeTime = ElevatorData[type]['closeTime'] + closedPos = getRightClosePoint(type) + openPos = getRightOpenPoint(type) + rightCloseInterval = LerpPosInterval(rightDoor, closeTime, closedPos, startPos=openPos, blendType='easeOut', name=distObj.uniqueName('rightDoorClose')) + return rightCloseInterval + + +def getCloseInterval(distObj, leftDoor, rightDoor, closeSfx, finalCloseSfx, type = ELEVATOR_NORMAL): + left = getLeftCloseInterval(distObj, leftDoor, type) + right = getRightCloseInterval(distObj, rightDoor, type) + closeDuration = left.getDuration() + sfxVolume = ElevatorData[type]['sfxVolume'] + if finalCloseSfx: + sound = Sequence(SoundInterval(closeSfx, duration=closeDuration, volume=sfxVolume, node=leftDoor), SoundInterval(finalCloseSfx, volume=sfxVolume, node=leftDoor)) + else: + sound = SoundInterval(closeSfx, volume=sfxVolume, node=leftDoor) + return Parallel(sound, left, right) + + +def getRideElevatorInterval(type = ELEVATOR_NORMAL): + if type == ELEVATOR_VP or type == ELEVATOR_CFO or type == ELEVATOR_CJ: + yValue = 30 + zMin = 7.8 + zMid = 8 + zMax = 8.2 + elif type == ELEVATOR_BB: + yValue = 21 + zMin = 7 + zMid = 7.2 + zMax = 7.4 + if type in (ELEVATOR_VP, + ELEVATOR_CFO, + ELEVATOR_CJ, + ELEVATOR_BB): + ival = Sequence(Wait(0.5), LerpPosInterval(camera, 0.5, Point3(0, yValue, zMin), startPos=Point3(0, yValue, zMid), blendType='easeOut'), LerpPosInterval(camera, 0.5, Point3(0, yValue, zMid), startPos=Point3(0, yValue, zMin)), Wait(1.0), LerpPosInterval(camera, 0.5, Point3(0, yValue, zMax), startPos=Point3(0, yValue, zMid), blendType='easeOut'), LerpPosInterval(camera, 1.0, Point3(0, yValue, zMid), startPos=Point3(0, yValue, zMax))) + else: + ival = Sequence(Wait(0.5), LerpPosInterval(camera, 0.5, Point3(0, 14, 3.8), startPos=Point3(0, 14, 4), blendType='easeOut'), LerpPosInterval(camera, 0.5, Point3(0, 14, 4), startPos=Point3(0, 14, 3.8)), Wait(1.0), LerpPosInterval(camera, 0.5, Point3(0, 14, 4.2), startPos=Point3(0, 14, 4), blendType='easeOut'), LerpPosInterval(camera, 1.0, Point3(0, 14, 4), startPos=Point3(0, 14, 4.2))) + return ival diff --git a/toontown/building/FADoorCodes.py b/toontown/building/FADoorCodes.py new file mode 100755 index 00000000..c483d030 --- /dev/null +++ b/toontown/building/FADoorCodes.py @@ -0,0 +1,29 @@ +from toontown.toonbase import TTLocalizer +UNLOCKED = 0 +TALK_TO_TOM = 1 +DEFEAT_FLUNKY_HQ = 2 +TALK_TO_HQ = 3 +WRONG_DOOR_HQ = 4 +GO_TO_PLAYGROUND = 5 +DEFEAT_FLUNKY_TOM = 6 +TALK_TO_HQ_TOM = 7 +SUIT_APPROACHING = 8 +BUILDING_TAKEOVER = 9 +SB_DISGUISE_INCOMPLETE = 10 +CB_DISGUISE_INCOMPLETE = 11 +LB_DISGUISE_INCOMPLETE = 12 +BB_DISGUISE_INCOMPLETE = 13 +reasonDict = {UNLOCKED: TTLocalizer.FADoorCodes_UNLOCKED, + TALK_TO_TOM: TTLocalizer.FADoorCodes_TALK_TO_TOM, + DEFEAT_FLUNKY_HQ: TTLocalizer.FADoorCodes_DEFEAT_FLUNKY_HQ, + TALK_TO_HQ: TTLocalizer.FADoorCodes_TALK_TO_HQ, + WRONG_DOOR_HQ: TTLocalizer.FADoorCodes_WRONG_DOOR_HQ, + GO_TO_PLAYGROUND: TTLocalizer.FADoorCodes_GO_TO_PLAYGROUND, + DEFEAT_FLUNKY_TOM: TTLocalizer.FADoorCodes_DEFEAT_FLUNKY_TOM, + TALK_TO_HQ_TOM: TTLocalizer.FADoorCodes_TALK_TO_HQ_TOM, + SUIT_APPROACHING: TTLocalizer.FADoorCodes_SUIT_APPROACHING, + BUILDING_TAKEOVER: TTLocalizer.FADoorCodes_BUILDING_TAKEOVER, + SB_DISGUISE_INCOMPLETE: TTLocalizer.FADoorCodes_SB_DISGUISE_INCOMPLETE, + CB_DISGUISE_INCOMPLETE: TTLocalizer.FADoorCodes_CB_DISGUISE_INCOMPLETE, + LB_DISGUISE_INCOMPLETE: TTLocalizer.FADoorCodes_LB_DISGUISE_INCOMPLETE, + BB_DISGUISE_INCOMPLETE: TTLocalizer.FADoorCodes_BB_DISGUISE_INCOMPLETE} diff --git a/toontown/building/GagshopBuildingAI.py b/toontown/building/GagshopBuildingAI.py new file mode 100755 index 00000000..02f69cba --- /dev/null +++ b/toontown/building/GagshopBuildingAI.py @@ -0,0 +1,44 @@ +import DistributedDoorAI +import DistributedGagshopInteriorAI +import DoorTypes +from panda3d.core import * +from toontown.toon import NPCToons + + +class GagshopBuildingAI: + def __init__(self, air, exteriorZone, interiorZone, blockNumber): + self.air = air + self.exteriorZone = exteriorZone + self.interiorZone = interiorZone + self.setup(blockNumber) + + def cleanup(self): + for npc in self.npcs: + npc.requestDelete() + del self.npcs + self.door.requestDelete() + del self.door + self.insideDoor.requestDelete() + del self.insideDoor + self.interior.requestDelete() + del self.interior + + def setup(self, blockNumber): + self.interior = DistributedGagshopInteriorAI.DistributedGagshopInteriorAI( + blockNumber, self.air, self.interiorZone) + self.interior.generateWithRequired(self.interiorZone) + + self.npcs = NPCToons.createNpcsInZone(self.air, self.interiorZone) + + door = DistributedDoorAI.DistributedDoorAI( + self.air, blockNumber, DoorTypes.EXT_STANDARD) + insideDoor = DistributedDoorAI.DistributedDoorAI( + self.air, blockNumber, DoorTypes.INT_STANDARD) + door.setOtherDoor(insideDoor) + insideDoor.setOtherDoor(door) + door.zoneId = self.exteriorZone + insideDoor.zoneId = self.interiorZone + door.generateWithRequired(self.exteriorZone) + insideDoor.generateWithRequired(self.interiorZone) + self.door = door + self.insideDoor = insideDoor diff --git a/toontown/building/HQBuildingAI.py b/toontown/building/HQBuildingAI.py new file mode 100755 index 00000000..af1d2798 --- /dev/null +++ b/toontown/building/HQBuildingAI.py @@ -0,0 +1,76 @@ +import DistributedDoorAI +import DistributedHQInteriorAI +import DoorTypes +from panda3d.core import * +from toontown.toon import NPCToons + + +class HQBuildingAI: + def __init__(self, air, exteriorZone, interiorZone, blockNumber): + self.air = air + self.exteriorZone = exteriorZone + self.interiorZone = interiorZone + self.setup(blockNumber) + + def cleanup(self): + for npc in self.npcs: + npc.requestDelete() + del self.npcs + self.door0.requestDelete() + del self.door0 + self.door1.requestDelete() + del self.door1 + self.insideDoor0.requestDelete() + del self.insideDoor0 + self.insideDoor1.requestDelete() + del self.insideDoor1 + self.interior.requestDelete() + del self.interior + + def setup(self, blockNumber): + self.interior = DistributedHQInteriorAI.DistributedHQInteriorAI( + blockNumber, self.air, self.interiorZone) + self.interior.generateWithRequired(self.interiorZone) + + self.npcs = NPCToons.createNpcsInZone(self.air, self.interiorZone) + + door0 = DistributedDoorAI.DistributedDoorAI( + self.air, blockNumber, DoorTypes.EXT_HQ, doorIndex=0) + door1 = DistributedDoorAI.DistributedDoorAI( + self.air, blockNumber, DoorTypes.EXT_HQ, doorIndex=1) + insideDoor0 = DistributedDoorAI.DistributedDoorAI( + self.air, blockNumber, DoorTypes.INT_HQ, doorIndex=0) + insideDoor1 = DistributedDoorAI.DistributedDoorAI( + self.air, blockNumber, DoorTypes.INT_HQ, doorIndex=1) + door0.setOtherDoor(insideDoor0) + insideDoor0.setOtherDoor(door0) + door1.setOtherDoor(insideDoor1) + insideDoor1.setOtherDoor(door1) + door0.zoneId = self.exteriorZone + door1.zoneId = self.exteriorZone + insideDoor0.zoneId = self.interiorZone + insideDoor1.zoneId = self.interiorZone + door0.generateWithRequired(self.exteriorZone) + door1.generateWithRequired(self.exteriorZone) + door0.sendUpdate('setDoorIndex', [door0.getDoorIndex()]) + door1.sendUpdate('setDoorIndex', [door1.getDoorIndex()]) + insideDoor0.generateWithRequired(self.interiorZone) + insideDoor1.generateWithRequired(self.interiorZone) + insideDoor0.sendUpdate('setDoorIndex', [insideDoor0.getDoorIndex()]) + insideDoor1.sendUpdate('setDoorIndex', [insideDoor1.getDoorIndex()]) + self.door0 = door0 + self.door1 = door1 + self.insideDoor0 = insideDoor0 + self.insideDoor1 = insideDoor1 + + def isSuitBlock(self): + return 0 + + def isSuitBuilding(self): + return 0 + + def isCogdo(self): + return 0 + + def isEstablishedSuitBlock(self): + return 0 diff --git a/toontown/building/KartShopBuildingAI.py b/toontown/building/KartShopBuildingAI.py new file mode 100755 index 00000000..72f725b9 --- /dev/null +++ b/toontown/building/KartShopBuildingAI.py @@ -0,0 +1,62 @@ +from direct.directnotify.DirectNotifyGlobal import * +from panda3d.core import * +from toontown.building import DoorTypes +from toontown.building.DistributedDoorAI import DistributedDoorAI +from toontown.building.DistributedKartShopInteriorAI import DistributedKartShopInteriorAI +from toontown.toon import NPCToons + +class KartShopBuildingAI: + notify = directNotify.newCategory('KartShopBuildingAI') + + def __init__(self, air, exteriorZone, interiorZone, blockNumber): + self.air = air + self.exteriorZone = exteriorZone + self.interiorZone = interiorZone + self.setup(blockNumber) + + def cleanup(self): + for npc in self.npcs: + npc.requestDelete() + del self.npcs + self.outsideDoor0.requestDelete() + self.outsideDoor1.requestDelete() + self.insideDoor0.requestDelete() + self.insideDoor1.requestDelete() + del self.outsideDoor0 + del self.insideDoor0 + del self.outsideDoor1 + del self.insideDoor1 + self.kartShopInterior.requestDelete() + del self.kartShopInterior + + def setup(self, blockNumber): + self.kartShopInterior = DistributedKartShopInteriorAI( + blockNumber, self.air, self.interiorZone) + self.kartShopInterior.generateWithRequired(self.interiorZone) + + self.npcs = NPCToons.createNpcsInZone(self.air, self.interiorZone) + + self.outsideDoor0 = DistributedDoorAI( + self.air, blockNumber, DoorTypes.EXT_KS, doorIndex=1) + self.outsideDoor1 = DistributedDoorAI( + self.air, blockNumber, DoorTypes.EXT_KS, doorIndex=2) + self.insideDoor0 = DistributedDoorAI( + self.air, blockNumber, DoorTypes.INT_KS, doorIndex=1) + self.insideDoor1 = DistributedDoorAI( + self.air, blockNumber, DoorTypes.INT_KS, doorIndex=2) + self.outsideDoor0.setOtherDoor(self.insideDoor0) + self.outsideDoor1.setOtherDoor(self.insideDoor1) + self.insideDoor0.setOtherDoor(self.outsideDoor0) + self.insideDoor1.setOtherDoor(self.outsideDoor1) + self.outsideDoor0.zoneId = self.exteriorZone + self.outsideDoor1.zoneId = self.exteriorZone + self.insideDoor0.zoneId = self.interiorZone + self.insideDoor1.zoneId = self.interiorZone + self.outsideDoor0.generateWithRequired(self.exteriorZone) + self.outsideDoor1.generateWithRequired(self.exteriorZone) + self.insideDoor0.generateWithRequired(self.interiorZone) + self.insideDoor1.generateWithRequired(self.interiorZone) + self.outsideDoor0.sendUpdate('setDoorIndex', [self.outsideDoor0.getDoorIndex()]) + self.outsideDoor1.sendUpdate('setDoorIndex', [self.outsideDoor1.getDoorIndex()]) + self.insideDoor0.sendUpdate('setDoorIndex', [self.insideDoor0.getDoorIndex()]) + self.insideDoor1.sendUpdate('setDoorIndex', [self.insideDoor1.getDoorIndex()]) diff --git a/toontown/building/KnockKnockJokes.py b/toontown/building/KnockKnockJokes.py new file mode 100755 index 00000000..c8a9eded --- /dev/null +++ b/toontown/building/KnockKnockJokes.py @@ -0,0 +1,2 @@ +from toontown.toonbase import TTLocalizer +KnockKnockJokes = TTLocalizer.KnockKnockJokes diff --git a/toontown/building/PetshopBuildingAI.py b/toontown/building/PetshopBuildingAI.py new file mode 100755 index 00000000..dc5c1689 --- /dev/null +++ b/toontown/building/PetshopBuildingAI.py @@ -0,0 +1,53 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +import DistributedDoorAI +import DistributedPetshopInteriorAI +import FADoorCodes +import DoorTypes +from toontown.toon import NPCToons +from toontown.toonbase import ToontownGlobals +from toontown.quest import Quests +from toontown.hood import ZoneUtil + +class PetshopBuildingAI: + notify = DirectNotifyGlobal.directNotify.newCategory('PetshopBuildingAI') + + def __init__(self, air, exteriorZone, interiorZone, blockNumber): + self.air = air + self.exteriorZone = exteriorZone + self.interiorZone = interiorZone + self.setup(blockNumber) + + def cleanup(self): + for npc in self.npcs: + npc.requestDelete() + del self.npcs + self.door.requestDelete() + del self.door + self.insideDoor.requestDelete() + del self.insideDoor + self.interior.requestDelete() + del self.interior + + def setup(self, blockNumber): + self.interior = DistributedPetshopInteriorAI.DistributedPetshopInteriorAI( + blockNumber, self.air, self.interiorZone) + self.interior.generateWithRequired(self.interiorZone) + + self.npcs = NPCToons.createNpcsInZone(self.air, self.interiorZone) + + door = DistributedDoorAI.DistributedDoorAI( + self.air, blockNumber, DoorTypes.EXT_STANDARD) + insideDoor = DistributedDoorAI.DistributedDoorAI( + self.air, blockNumber, DoorTypes.INT_STANDARD) + door.setOtherDoor(insideDoor) + insideDoor.setOtherDoor(door) + door.zoneId = self.exteriorZone + insideDoor.zoneId = self.interiorZone + door.generateWithRequired(self.exteriorZone) + insideDoor.generateWithRequired(self.interiorZone) + self.door = door + self.insideDoor = insideDoor + + def createPet(self, ownerId, seed): + return diff --git a/toontown/building/SuitBuildingGlobals.py b/toontown/building/SuitBuildingGlobals.py new file mode 100755 index 00000000..6cd5a753 --- /dev/null +++ b/toontown/building/SuitBuildingGlobals.py @@ -0,0 +1,236 @@ +from ElevatorConstants import * +from toontown.toonbase import ToontownGlobals + + +try: + config = base.config +except: + config = simbase.config + +SuitBuildingInfo = (((1, 1), + (1, 3), + (4, 4), + (8, 10), + (1,)), + ((1, 2), + (2, 4), + (5, 5), + (8, 10), + (1, 1.2)), + ((1, 3), + (3, 5), + (6, 6), + (8, 10), + (1, 1.3, 1.6)), + ((2, 3), + (4, 6), + (7, 7), + (8, 10), + (1, 1.4, 1.8)), + ((2, 4), + (5, 7), + (8, 8), + (8, 10), + (1, + 1.6, + 1.8, + 2)), + ((3, 4), + (6, 8), + (9, 9), + (10, 12), + (1, + 1.6, + 2, + 2.4)), + ((3, 5), + (7, 9), + (10, 10), + (10, 14), + (1, + 1.6, + 1.8, + 2.2, + 2.4)), + ((4, 5), + (8, 10), + (11, 11), + (12, 16), + (1, + 1.8, + 2.4, + 3, + 3.2)), + ((5, 5), + (9, 11), + (12, 12), + (14, 20), + (1.4, + 1.8, + 2.6, + 3.4, + 4)), + ((1, 1), + (1, 12), + (12, 12), + (67, 67), + (1, + 1, + 1, + 1, + 1)), + ((1, 1), + (8, 12), + (12, 12), + (100, 100), + (1, + 1, + 1, + 1, + 1)), + ((1, 1), + (1, 12), + (12, 12), + (100, 100), + (1, + 1, + 1, + 1, + 1)), + ((1, 1), + (8, 12), + (12, 12), + (150, 150), + (1, + 1, + 1, + 1, + 1)), + ((1, 1), + (8, 12), + (12, 12), + (275, 275), + (1, + 1, + 1, + 1, + 1)), + ((1, 1), + (9, 12), + (12, 12), + (206, 206), + (1, + 1, + 1, + 1, + 1), + (1,)), + ((1, 1), + (1, 5), + (5, 5), + (33, 33), + (1, + 1, + 1, + 1, + 1)), + ((1, 1), + (4, 5), + (5, 5), + (50, 50), + (1, + 1, + 1, + 1, + 1)), + ((1, 1), + (11, 12), + (12, 12), + (206, 206), + (1, + 1, + 1, + 1, + 1), + (1,))) +SUIT_BLDG_INFO_FLOORS = 0 +SUIT_BLDG_INFO_SUIT_LVLS = 1 +SUIT_BLDG_INFO_BOSS_LVLS = 2 +SUIT_BLDG_INFO_LVL_POOL = 3 +SUIT_BLDG_INFO_LVL_POOL_MULTS = 4 +SUIT_BLDG_INFO_REVIVES = 5 +VICTORY_RUN_TIME = ElevatorData[ELEVATOR_NORMAL]['openTime'] + TOON_VICTORY_EXIT_TIME +TO_TOON_BLDG_TIME = 8 +VICTORY_SEQUENCE_TIME = VICTORY_RUN_TIME + TO_TOON_BLDG_TIME +CLEAR_OUT_TOON_BLDG_TIME = 4 +TO_SUIT_BLDG_TIME = 8 + +buildingMinMax = { + ToontownGlobals.SillyStreet: [config.GetInt('silly-street-building-min', 0), + config.GetInt('silly-street-building-max', 3)], + ToontownGlobals.LoopyLane: [config.GetInt('loopy-lane-building-min', 0), + config.GetInt('loopy-lane-building-max', 3)], + ToontownGlobals.PunchlinePlace: [config.GetInt('punchline-place-building-min', 0), + config.GetInt('punchline-place-building-max', 3)], + ToontownGlobals.BarnacleBoulevard: [config.GetInt('barnacle-boulevard-building-min', 1), + config.GetInt('barnacle-boulevard-building-max', 5)], + ToontownGlobals.SeaweedStreet: [config.GetInt('seaweed-street-building-min', 1), + config.GetInt('seaweed-street-building-max', 5)], + ToontownGlobals.LighthouseLane: [config.GetInt('lighthouse-lane-building-min', 1), + config.GetInt('lighthouse-lane-building-max', 5)], + ToontownGlobals.ElmStreet: [config.GetInt('elm-street-building-min', 2), + config.GetInt('elm-street-building-max', 6)], + ToontownGlobals.MapleStreet: [config.GetInt('maple-street-building-min', 2), + config.GetInt('maple-street-building-max', 6)], + ToontownGlobals.OakStreet: [config.GetInt('oak-street-building-min', 2), + config.GetInt('oak-street-building-max', 6)], + ToontownGlobals.AltoAvenue: [config.GetInt('alto-avenue-building-min', 3), + config.GetInt('alto-avenue-building-max', 7)], + ToontownGlobals.BaritoneBoulevard: [config.GetInt('baritone-boulevard-building-min', 3), + config.GetInt('baritone-boulevard-building-max', 7)], + ToontownGlobals.TenorTerrace: [config.GetInt('tenor-terrace-building-min', 3), + config.GetInt('tenor-terrace-building-max', 7)], + ToontownGlobals.WalrusWay: [config.GetInt('walrus-way-building-min', 5), + config.GetInt('walrus-way-building-max', 10)], + ToontownGlobals.SleetStreet: [config.GetInt('sleet-street-building-min', 5), + config.GetInt('sleet-street-building-max', 10)], + ToontownGlobals.PolarPlace: [config.GetInt('polar-place-building-min', 5), + config.GetInt('polar-place-building-max', 10)], + ToontownGlobals.LullabyLane: [config.GetInt('lullaby-lane-building-min', 6), + config.GetInt('lullaby-lane-building-max', 12)], + ToontownGlobals.PajamaPlace: [config.GetInt('pajama-place-building-min', 6), + config.GetInt('pajama-place-building-max', 12)], + ToontownGlobals.BedtimeBoulevard: [config.GetInt('bedtime-boulevard-building-min', 6), + config.GetInt('bedtime-boulevard-building-max', 12)], + ToontownGlobals.SellbotHQ: [0, 0], + ToontownGlobals.SellbotFactoryExt: [0, 0], + ToontownGlobals.CashbotHQ: [0, 0], + ToontownGlobals.LawbotHQ: [0, 0], + ToontownGlobals.BossbotHQ: [0, 0] +} + +buildingChance = { + ToontownGlobals.SillyStreet: config.GetFloat('silly-street-building-chance', 2.0), + ToontownGlobals.LoopyLane: config.GetFloat('loopy-lane-building-chance', 2.0), + ToontownGlobals.PunchlinePlace: config.GetFloat('punchline-place-building-chance', 2.0), + ToontownGlobals.BarnacleBoulevard: config.GetFloat('barnacle-boulevard-building-chance', 75.0), + ToontownGlobals.SeaweedStreet: config.GetFloat('seaweed-street-building-chance', 75.0), + ToontownGlobals.LighthouseLane: config.GetFloat('lighthouse-lane-building-chance', 75.0), + ToontownGlobals.ElmStreet: config.GetFloat('elm-street-building-chance', 90.0), + ToontownGlobals.MapleStreet: config.GetFloat('maple-street-building-chance', 90.0), + ToontownGlobals.OakStreet: config.GetFloat('oak-street-building-chance', 90.0), + ToontownGlobals.AltoAvenue: config.GetFloat('alto-avenue-building-chance', 95.0), + ToontownGlobals.BaritoneBoulevard: config.GetFloat('baritone-boulevard-building-chance', 95.0), + ToontownGlobals.TenorTerrace: config.GetFloat('tenor-terrace-building-chance', 95.0), + ToontownGlobals.WalrusWay: config.GetFloat('walrus-way-building-chance', 100.0), + ToontownGlobals.SleetStreet: config.GetFloat('sleet-street-building-chance', 100.0), + ToontownGlobals.PolarPlace: config.GetFloat('polar-place-building-chance', 100.0), + ToontownGlobals.LullabyLane: config.GetFloat('lullaby-lane-building-chance', 100.0), + ToontownGlobals.PajamaPlace: config.GetFloat('pajama-place-building-chance', 100.0), + ToontownGlobals.BedtimeBoulevard: config.GetFloat('bedtime-boulevard-building-chance', 100.0), + ToontownGlobals.SellbotHQ: 0.0, + ToontownGlobals.SellbotFactoryExt: 0.0, + ToontownGlobals.CashbotHQ: 0.0, + ToontownGlobals.LawbotHQ: 0.0, + ToontownGlobals.BossbotHQ: 0.0 +} diff --git a/toontown/building/SuitInterior.py b/toontown/building/SuitInterior.py new file mode 100755 index 00000000..19be637c --- /dev/null +++ b/toontown/building/SuitInterior.py @@ -0,0 +1,201 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import Place +from direct.showbase import DirectObject +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.town import TownBattle +from toontown.suit import Suit +import Elevator +from direct.task.Task import Task +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals + +class SuitInterior(Place.Place): + notify = DirectNotifyGlobal.directNotify.newCategory('SuitInterior') + + def __init__(self, loader, parentFSM, doneEvent): + Place.Place.__init__(self, loader, doneEvent) + self.fsm = ClassicFSM.ClassicFSM('SuitInterior', [State.State('entrance', self.enterEntrance, self.exitEntrance, ['battle', 'walk']), + State.State('Elevator', self.enterElevator, self.exitElevator, ['battle', 'walk']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'died']), + State.State('walk', self.enterWalk, self.exitWalk, ['stickerBook', + 'stopped', + 'sit', + 'died', + 'teleportOut', + 'Elevator']), + State.State('sit', self.enterSit, self.exitSit, ['walk']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'stopped', + 'sit', + 'died', + 'teleportOut', + 'Elevator']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'elevatorOut']), + State.State('died', self.enterDied, self.exitDied, []), + State.State('elevatorOut', self.enterElevatorOut, self.exitElevatorOut, [])], 'entrance', 'elevatorOut') + self.parentFSM = parentFSM + self.elevatorDoneEvent = 'elevatorDoneSI' + self.currentFloor = 0 + + def enter(self, requestStatus): + self.fsm.enterInitialState() + self._telemLimiter = TLGatherAllAvs('SuitInterior', RotationLimitToH) + self.zoneId = requestStatus['zoneId'] + self.accept('DSIDoneEvent', self.handleDSIDoneEvent) + + def exit(self): + self.ignoreAll() + self._telemLimiter.destroy() + del self._telemLimiter + + def load(self): + Place.Place.load(self) + self.parentFSM.getStateNamed('suitInterior').addChild(self.fsm) + self.townBattle = TownBattle.TownBattle('town-battle-done') + self.townBattle.load() + for i in xrange(1, 3): + Suit.loadSuits(i) + + def unload(self): + Place.Place.unload(self) + self.parentFSM.getStateNamed('suitInterior').removeChild(self.fsm) + del self.parentFSM + del self.fsm + self.ignoreAll() + ModelPool.garbageCollect() + TexturePool.garbageCollect() + self.townBattle.unload() + self.townBattle.cleanup() + del self.townBattle + for i in xrange(1, 3): + Suit.unloadSuits(i) + + def setState(self, state, battleEvent = None): + if battleEvent: + self.fsm.request(state, [battleEvent]) + else: + self.fsm.request(state) + + def getZoneId(self): + return self.zoneId + + def enterZone(self, zoneId): + pass + + def handleDSIDoneEvent(self, requestStatus): + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def enterEntrance(self): + pass + + def exitEntrance(self): + pass + + def enterElevator(self, distElevator): + self.accept(self.elevatorDoneEvent, self.handleElevatorDone) + self.elevator = Elevator.Elevator(self.fsm.getStateNamed('Elevator'), self.elevatorDoneEvent, distElevator) + self.elevator.load() + self.elevator.enter() + base.localAvatar.cantLeaveGame = 1 + + def exitElevator(self): + base.localAvatar.cantLeaveGame = 0 + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + del self.elevator + return None + + def detectedElevatorCollision(self, distElevator): + self.fsm.request('Elevator', [distElevator]) + return None + + def handleElevatorDone(self, doneStatus): + self.notify.debug('handling elevator done event') + where = doneStatus['where'] + if where == 'reject': + if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen(): + pass + else: + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'suitInterior': + pass + else: + self.notify.error('Unknown mode: ' + +' in handleElevatorDone') + + def enterBattle(self, event): + mult = ToontownBattleGlobals.getCreditMultiplier(self.currentFloor) + self.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult) + base.localAvatar.b_setAnimState('off', 1) + base.localAvatar.cantLeaveGame = 1 + + def exitBattle(self): + self.townBattle.exit() + base.localAvatar.cantLeaveGame = 0 + + def enterWalk(self, teleportIn = 0): + Place.Place.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterStickerBook(self, page = None): + Place.Place.enterStickerBook(self, page) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterSit(self): + Place.Place.enterSit(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterStopped(self): + Place.Place.enterStopped(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterTeleportIn(self, requestStatus): + base.localAvatar.setPosHpr(2.5, 11.5, ToontownGlobals.FloorOffset, 45.0, 0.0, 0.0) + Place.Place.enterTeleportIn(self, requestStatus) + + def enterTeleportOut(self, requestStatus): + Place.Place.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + hoodId = requestStatus['hoodId'] + if hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + messenger.send('localToonLeft') + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def exitTeleportOut(self): + Place.Place.exitTeleportOut(self) + + def goHomeFailed(self, task): + self.notifyUserGoHomeFailed() + self.ignore('setLocalEstateZone') + self.doneStatus['avId'] = -1 + self.doneStatus['zoneId'] = self.getZoneId() + self.fsm.request('teleportIn', [self.doneStatus]) + return Task.done + + def enterElevatorOut(self): + return None + + def __elevatorOutDone(self, requestStatus): + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def exitElevatorOut(self): + return None diff --git a/toontown/building/SuitPlannerInteriorAI.py b/toontown/building/SuitPlannerInteriorAI.py new file mode 100755 index 00000000..7ac9e6bc --- /dev/null +++ b/toontown/building/SuitPlannerInteriorAI.py @@ -0,0 +1,195 @@ +from direct.directnotify import DirectNotifyGlobal +import random +import types + +import SuitBuildingGlobals +from otp.ai.AIBaseGlobal import * +from toontown.suit import DistributedSuitAI +from toontown.suit import SuitDNA +from toontown.suit.SuitInvasionGlobals import IFSkelecog, IFWaiter, IFV2 + + +class SuitPlannerInteriorAI: + notify = DirectNotifyGlobal.directNotify.newCategory('SuitPlannerInteriorAI') + + def __init__(self, numFloors, bldgLevel, bldgTrack, zone): + self.dbg_4SuitsPerFloor = config.GetBool('4-suits-per-floor', 0) + self.dbg_1SuitPerFloor = config.GetBool('1-suit-per-floor', 0) + self.zoneId = zone + self.numFloors = numFloors + self.respectInvasions = 1 + dbg_defaultSuitName = simbase.config.GetString('suit-type', 'random') + if dbg_defaultSuitName == 'random': + self.dbg_defaultSuitType = None + else: + self.dbg_defaultSuitType = SuitDNA.getSuitType(dbg_defaultSuitName) + if isinstance(bldgLevel, types.StringType): + self.notify.warning('bldgLevel is a string!') + bldgLevel = int(bldgLevel) + self._genSuitInfos(numFloors, bldgLevel, bldgTrack) + return + + def __genJoinChances(self, num): + joinChances = [] + for currChance in xrange(num): + joinChances.append(random.randint(1, 100)) + + joinChances.sort(cmp) + return joinChances + + def _genSuitInfos(self, numFloors, bldgLevel, bldgTrack): + self.suitInfos = [] + self.notify.debug('\n\ngenerating suitsInfos with numFloors (' + str(numFloors) + ') bldgLevel (' + str(bldgLevel) + '+1) and bldgTrack (' + str(bldgTrack) + ')') + for currFloor in xrange(numFloors): + infoDict = {} + lvls = self.__genLevelList(bldgLevel, currFloor, numFloors) + activeDicts = [] + if self.dbg_4SuitsPerFloor: + numActive = 4 + else: + numActive = random.randint(1, min(4, len(lvls))) + if currFloor + 1 == numFloors and len(lvls) > 1: + origBossSpot = len(lvls) - 1 + if numActive == 1: + newBossSpot = numActive - 1 + else: + newBossSpot = numActive - 2 + tmp = lvls[newBossSpot] + lvls[newBossSpot] = lvls[origBossSpot] + lvls[origBossSpot] = tmp + bldgInfo = SuitBuildingGlobals.SuitBuildingInfo[bldgLevel] + if len(bldgInfo) > SuitBuildingGlobals.SUIT_BLDG_INFO_REVIVES: + revives = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_REVIVES][0] + else: + revives = 0 + for currActive in xrange(numActive - 1, -1, -1): + level = lvls[currActive] + type = self.__genNormalSuitType(level) + activeDict = {} + activeDict['type'] = type + activeDict['track'] = bldgTrack + activeDict['level'] = level + activeDict['revives'] = revives + activeDicts.append(activeDict) + + infoDict['activeSuits'] = activeDicts + reserveDicts = [] + numReserve = len(lvls) - numActive + joinChances = self.__genJoinChances(numReserve) + for currReserve in xrange(numReserve): + level = lvls[currReserve + numActive] + type = self.__genNormalSuitType(level) + reserveDict = {} + reserveDict['type'] = type + reserveDict['track'] = bldgTrack + reserveDict['level'] = level + reserveDict['revives'] = revives + reserveDict['joinChance'] = joinChances[currReserve] + reserveDicts.append(reserveDict) + + infoDict['reserveSuits'] = reserveDicts + self.suitInfos.append(infoDict) + + def __genNormalSuitType(self, lvl): + if self.dbg_defaultSuitType != None: + return self.dbg_defaultSuitType + return SuitDNA.getRandomSuitType(lvl) + + def __genLevelList(self, bldgLevel, currFloor, numFloors): + bldgInfo = SuitBuildingGlobals.SuitBuildingInfo[bldgLevel] + if self.dbg_1SuitPerFloor: + return [1] + elif self.dbg_4SuitsPerFloor: + return [5, + 6, + 7, + 10] + lvlPoolRange = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_LVL_POOL] + maxFloors = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_FLOORS][1] + lvlPoolMults = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_LVL_POOL_MULTS] + floorIdx = min(currFloor, maxFloors - 1) + lvlPoolMin = lvlPoolRange[0] * lvlPoolMults[floorIdx] + lvlPoolMax = lvlPoolRange[1] * lvlPoolMults[floorIdx] + lvlPool = random.randint(int(lvlPoolMin), int(lvlPoolMax)) + lvlMin = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_SUIT_LVLS][0] + lvlMax = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_SUIT_LVLS][1] + self.notify.debug('Level Pool: ' + str(lvlPool)) + lvlList = [] + while lvlPool >= lvlMin: + newLvl = random.randint(lvlMin, min(lvlPool, lvlMax)) + lvlList.append(newLvl) + lvlPool -= newLvl + + if currFloor + 1 == numFloors: + bossLvlRange = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_BOSS_LVLS] + newLvl = random.randint(bossLvlRange[0], bossLvlRange[1]) + lvlList.append(newLvl) + lvlList.sort(cmp) + self.notify.debug('LevelList: ' + repr(lvlList)) + return lvlList + + def __setupSuitInfo(self, suit, bldgTrack, suitLevel, suitType): + suitDeptIndex, suitTypeIndex, flags = simbase.air.suitInvasionManager.getInvadingCog() + if self.respectInvasions: + if suitDeptIndex is not None: + bldgTrack = SuitDNA.suitDepts[suitDeptIndex] + if suitTypeIndex is not None: + suitName = SuitDNA.getSuitName(suitDeptIndex, suitTypeIndex) + suitType = SuitDNA.getSuitType(suitName) + suitLevel = min(max(suitLevel, suitType), suitType + 4) + dna = SuitDNA.SuitDNA() + dna.newSuitRandom(suitType, bldgTrack) + suit.dna = dna + suit.setLevel(suitLevel) + return flags + + def __genSuitObject(self, suitZone, suitType, bldgTrack, suitLevel, revives = 0): + newSuit = DistributedSuitAI.DistributedSuitAI(simbase.air, None) + flags = self.__setupSuitInfo(newSuit, bldgTrack, suitLevel, suitType) + if flags & IFSkelecog: + newSuit.setSkelecog(1) + newSuit.setSkeleRevives(revives) + newSuit.generateWithRequired(suitZone) + if flags & IFWaiter: + newSuit.b_setWaiter(1) + if flags & IFV2: + newSuit.b_setSkeleRevives(1) + newSuit.node().setName('suit-%s' % newSuit.doId) + return newSuit + + def myPrint(self): + self.notify.info('Generated suits for building: ') + for currInfo in suitInfos: + whichSuitInfo = suitInfos.index(currInfo) + 1 + self.notify.debug(' Floor ' + str(whichSuitInfo) + ' has ' + str(len(currInfo[0])) + ' active suits.') + for currActive in xrange(len(currInfo[0])): + self.notify.debug(' Active suit ' + str(currActive + 1) + ' is of type ' + str(currInfo[0][currActive][0]) + ' and of track ' + str(currInfo[0][currActive][1]) + ' and of level ' + str(currInfo[0][currActive][2])) + + self.notify.debug(' Floor ' + str(whichSuitInfo) + ' has ' + str(len(currInfo[1])) + ' reserve suits.') + for currReserve in xrange(len(currInfo[1])): + self.notify.debug(' Reserve suit ' + str(currReserve + 1) + ' is of type ' + str(currInfo[1][currReserve][0]) + ' and of track ' + str(currInfo[1][currReserve][1]) + ' and of lvel ' + str(currInfo[1][currReserve][2]) + ' and has ' + str(currInfo[1][currReserve][3]) + '% join restriction.') + + def genFloorSuits(self, floor): + suitHandles = {} + floorInfo = self.suitInfos[floor] + activeSuits = [] + for activeSuitInfo in floorInfo['activeSuits']: + suit = self.__genSuitObject(self.zoneId, activeSuitInfo['type'], activeSuitInfo['track'], activeSuitInfo['level'], activeSuitInfo['revives']) + activeSuits.append(suit) + + suitHandles['activeSuits'] = activeSuits + reserveSuits = [] + for reserveSuitInfo in floorInfo['reserveSuits']: + suit = self.__genSuitObject(self.zoneId, reserveSuitInfo['type'], reserveSuitInfo['track'], reserveSuitInfo['level'], reserveSuitInfo['revives']) + reserveSuits.append((suit, reserveSuitInfo['joinChance'])) + + suitHandles['reserveSuits'] = reserveSuits + return suitHandles + + def genSuits(self): + suitHandles = [] + for floor in xrange(len(self.suitInfos)): + floorSuitHandles = self.genFloorSuits(floor) + suitHandles.append(floorSuitHandles) + + return suitHandles diff --git a/toontown/building/ToonInterior.py b/toontown/building/ToonInterior.py new file mode 100755 index 00000000..fb2f05d8 --- /dev/null +++ b/toontown/building/ToonInterior.py @@ -0,0 +1,217 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from direct.showbase import DirectObject +from direct.task import Task +from panda3d.core import * +import DistributedToonInterior +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.hood import Place +from toontown.hood import ZoneUtil +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals +from toontown.toon import HealthForceAcknowledge +from toontown.toon import NPCForceAcknowledge +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase.ToonBaseGlobal import * + +##[x, y, h] +InteriorTypes = {'toon_interior':[1.0, 13.0, 8.0], + 'toon_interior_2':[1.0, 13.0, 8.0], + 'toon_interior_L':[10.0, 7.5, -70.0], + 'toon_interior_T':[-1.0, 5.0, 14.0] +} + +class ToonInterior(Place.Place): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonInterior') + + def __init__(self, loader, parentFSMState, doneEvent): + Place.Place.__init__(self, loader, doneEvent) + self.dnaFile = 'phase_7/models/modules/toon_interior' + self.isInterior = 1 + self.hfaDoneEvent = 'hfaDoneEvent' + self.npcfaDoneEvent = 'npcfaDoneEvent' + self.fsm = ClassicFSM.ClassicFSM('ToonInterior', [State.State('start', self.enterStart, self.exitStart, ['doorIn', 'teleportIn', 'tutorial']), + State.State('walk', self.enterWalk, self.exitWalk, ['sit', + 'stickerBook', + 'doorOut', + 'teleportOut', + 'quest', + 'purchase', + 'phone', + 'stopped', + 'pet', + 'NPCFA']), + State.State('sit', self.enterSit, self.exitSit, ['walk']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'sit', + 'doorOut', + 'teleportOut', + 'quest', + 'purchase', + 'phone', + 'stopped', + 'pet', + 'NPCFA']), + State.State('NPCFA', self.enterNPCFA, self.exitNPCFA, ['NPCFAReject', 'HFA', 'teleportOut', 'doorOut']), + State.State('NPCFAReject', self.enterNPCFAReject, self.exitNPCFAReject, ['walk']), + State.State('HFA', self.enterHFA, self.exitHFA, ['HFAReject', 'teleportOut', 'tunnelOut']), + State.State('HFAReject', self.enterHFAReject, self.exitHFAReject, ['walk']), + State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk', 'stopped']), + State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk', 'stopped']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn']), + State.State('quest', self.enterQuest, self.exitQuest, ['walk', 'doorOut']), + State.State('tutorial', self.enterTutorial, self.exitTutorial, ['walk', 'quest']), + State.State('purchase', self.enterPurchase, self.exitPurchase, ['walk', 'doorOut']), + State.State('pet', self.enterPet, self.exitPet, ['walk']), + State.State('phone', self.enterPhone, self.exitPhone, ['walk', 'doorOut']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'doorOut']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.parentFSMState = parentFSMState + + def load(self): + Place.Place.load(self) + self.parentFSMState.addChild(self.fsm) + + def unload(self): + Place.Place.unload(self) + self.parentFSMState.removeChild(self.fsm) + del self.parentFSMState + del self.fsm + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def enter(self, requestStatus): + self.zoneId = requestStatus['zoneId'] + self.fsm.enterInitialState() + messenger.send('enterToonInterior') + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + volume = requestStatus.get('musicVolume', 0.7) + base.playMusic(self.loader.activityMusic, looping=1, volume=volume) + self._telemLimiter = TLGatherAllAvs('ToonInterior', RotationLimitToH) + NametagGlobals.setMasterArrowsOn(1) + self.fsm.request(requestStatus['how'], [requestStatus]) + + def exit(self): + self.ignoreAll() + messenger.send('exitToonInterior') + self._telemLimiter.destroy() + del self._telemLimiter + NametagGlobals.setMasterArrowsOn(0) + self.loader.activityMusic.stop() + + def setState(self, state): + self.fsm.request(state) + + def enterTutorial(self, requestStatus): + self.fsm.request('walk') + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + globalClock.tick() + base.transitions.irisIn() + messenger.send('enterTutorialInterior') + + def exitTutorial(self): + pass + + def doRequestLeave(self, requestStatus): + self.fsm.request('NPCFA', [requestStatus]) + + def enterNPCFA(self, requestStatus): + self.acceptOnce(self.npcfaDoneEvent, self.enterNPCFACallback, [requestStatus]) + self.npcfa = NPCForceAcknowledge.NPCForceAcknowledge(self.npcfaDoneEvent) + self.npcfa.enter() + + def exitNPCFA(self): + self.ignore(self.npcfaDoneEvent) + + def enterNPCFACallback(self, requestStatus, doneStatus): + self.npcfa.exit() + del self.npcfa + if doneStatus['mode'] == 'complete': + outHow = {'teleportIn': 'teleportOut', + 'tunnelIn': 'tunnelOut', + 'doorIn': 'doorOut'} + self.fsm.request(outHow[requestStatus['how']], [requestStatus]) + elif doneStatus['mode'] == 'incomplete': + self.fsm.request('NPCFAReject') + else: + self.notify.error('Unknown done status for NPCForceAcknowledge: ' + `doneStatus`) + + def enterNPCFAReject(self): + self.fsm.request('walk') + + def exitNPCFAReject(self): + pass + + def enterHFA(self, requestStatus): + self.acceptOnce(self.hfaDoneEvent, self.enterHFACallback, [requestStatus]) + self.hfa = HealthForceAcknowledge.HealthForceAcknowledge(self.hfaDoneEvent) + self.hfa.enter(1) + + def exitHFA(self): + self.ignore(self.hfaDoneEvent) + + def enterHFACallback(self, requestStatus, doneStatus): + self.hfa.exit() + del self.hfa + if doneStatus['mode'] == 'complete': + outHow = {'teleportIn': 'teleportOut', + 'tunnelIn': 'tunnelOut', + 'doorIn': 'doorOut'} + self.fsm.request(outHow[requestStatus['how']], [requestStatus]) + elif doneStatus['mode'] == 'incomplete': + self.fsm.request('HFAReject') + else: + self.notify.error('Unknown done status for HealthForceAcknowledge: ' + `doneStatus`) + + def enterHFAReject(self): + self.fsm.request('walk') + + def exitHFAReject(self): + pass + + def enterTeleportIn(self, requestStatus): + modelType = DistributedToonInterior.DistributedToonInterior(base.cr).getModelType(self.getZoneId()) + if self.zoneId == ToontownGlobals.ToonHall: + base.localAvatar.setPosHpr(-63.5, 30.5, ToontownGlobals.FloorOffset, 90.0, 0.0, 0.0) + elif ZoneUtil.isHQ(self.zoneId): + base.localAvatar.setPosHpr(-5.5, -1.5, ToontownGlobals.FloorOffset, 0.0, 0.0, 0.0) + elif ZoneUtil.isPetshop(self.zoneId): + base.localAvatar.setPosHpr(0, 0, ToontownGlobals.FloorOffset, 45.0, 0.0, 0.0) + elif modelType in InteriorTypes: + area = InteriorTypes[modelType] + base.localAvatar.setPosHpr(area[0], area[1], ToontownGlobals.FloorOffset, area[2], 0.0, 0.0) + else: + base.localAvatar.setPosHpr(2.5, 11.5, ToontownGlobals.FloorOffset, 45.0, 0.0, 0.0) + Place.Place.enterTeleportIn(self, requestStatus) + + def enterTeleportOut(self, requestStatus): + Place.Place.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + shardId = requestStatus['shardId'] + if hoodId == self.loader.hood.id and zoneId == self.zoneId and shardId == None: + self.fsm.request('teleportIn', [requestStatus]) + elif hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + def goHomeFailed(self, task): + self.notifyUserGoHomeFailed() + self.ignore('setLocalEstateZone') + self.doneStatus['avId'] = -1 + self.doneStatus['zoneId'] = self.getZoneId() + self.fsm.request('teleportIn', [self.doneStatus]) + return Task.done + + def exitTeleportOut(self): + Place.Place.exitTeleportOut(self) diff --git a/toontown/building/ToonInteriorColors.py b/toontown/building/ToonInteriorColors.py new file mode 100755 index 00000000..fccef5bf --- /dev/null +++ b/toontown/building/ToonInteriorColors.py @@ -0,0 +1,42 @@ +from toontown.toonbase.ToontownGlobals import * +wainscottingBase = [Vec4(0.8, 0.5, 0.3, 1.0), Vec4(0.699, 0.586, 0.473, 1.0), Vec4(0.473, 0.699, 0.488, 1.0)] +wallpaperBase = [Vec4(1.0, 1.0, 0.7, 1.0), + Vec4(0.8, 1.0, 0.7, 1.0), + Vec4(0.4, 0.5, 0.4, 1.0), + Vec4(0.5, 0.7, 0.6, 1.0)] +wallpaperBorderBase = [Vec4(1.0, 1.0, 0.7, 1.0), + Vec4(0.8, 1.0, 0.7, 1.0), + Vec4(0.4, 0.5, 0.4, 1.0), + Vec4(0.5, 0.7, 0.6, 1.0)] +doorBase = [Vec4(1.0, 1.0, 0.7, 1.0)] +floorBase = [Vec4(0.746, 1.0, 0.477, 1.0), Vec4(1.0, 0.684, 0.477, 1.0)] +baseScheme = {'TI_wainscotting': wainscottingBase, + 'TI_wallpaper': wallpaperBase, + 'TI_wallpaper_border': wallpaperBorderBase, + 'TI_door': doorBase, + 'TI_floor': floorBase} +colors = {DonaldsDock: {'TI_wainscotting': wainscottingBase, + 'TI_wallpaper': wallpaperBase, + 'TI_wallpaper_border': wallpaperBorderBase, + 'TI_door': doorBase, + 'TI_floor': floorBase}, + ToontownCentral: {'TI_wainscotting': wainscottingBase, + 'TI_wallpaper': wallpaperBase, + 'TI_wallpaper_border': wallpaperBorderBase, + 'TI_door': doorBase + [Vec4(0.8, 0.5, 0.3, 1.0)], + 'TI_floor': floorBase}, + TheBrrrgh: baseScheme, + MinniesMelodyland: baseScheme, + DaisyGardens: baseScheme, + GoofySpeedway: baseScheme, + DonaldsDreamland: {'TI_wainscotting': wainscottingBase, + 'TI_wallpaper': wallpaperBase, + 'TI_wallpaper_border': wallpaperBorderBase, + 'TI_door': doorBase, + 'TI_floor': floorBase}, + Tutorial: {'TI_wainscotting': wainscottingBase, + 'TI_wallpaper': wallpaperBase, + 'TI_wallpaper_border': wallpaperBorderBase, + 'TI_door': doorBase + [Vec4(0.8, 0.5, 0.3, 1.0)], + 'TI_floor': floorBase}, + MyEstate: baseScheme} diff --git a/toontown/building/TutorialBuildingAI.py b/toontown/building/TutorialBuildingAI.py new file mode 100755 index 00000000..a0bc4dab --- /dev/null +++ b/toontown/building/TutorialBuildingAI.py @@ -0,0 +1,35 @@ +from toontown.building import DistributedDoorAI +from toontown.building import DoorTypes +from toontown.building.DistributedTutorialInteriorAI import DistributedTutorialInteriorAI + + +class TutorialBuildingAI: + def __init__(self, air, exteriorZone, interiorZone, blockNumber, tutorialNpcId): + self.air = air + self.exteriorZone = exteriorZone + self.interiorZone = interiorZone + self.blockNumber = blockNumber + self.tutorialNpcId = tutorialNpcId + + self.interior = DistributedTutorialInteriorAI( + self.air, self.blockNumber, self.interiorZone, self.tutorialNpcId) + self.interior.generateWithRequired(self.interiorZone) + + self.door = DistributedDoorAI.DistributedDoorAI( + self.air, blockNumber, DoorTypes.EXT_STANDARD) + self.insideDoor = DistributedDoorAI.DistributedDoorAI( + self.air, blockNumber, DoorTypes.INT_STANDARD) + self.door.setOtherDoor(self.insideDoor) + self.insideDoor.setOtherDoor(self.door) + self.door.zoneId = self.exteriorZone + self.insideDoor.zoneId = self.interiorZone + self.door.generateWithRequired(self.exteriorZone) + self.insideDoor.generateWithRequired(self.interiorZone) + + def cleanup(self): + self.door.requestDelete() + del self.door + self.insideDoor.requestDelete() + del self.insideDoor + self.interior.requestDelete() + del self.interior diff --git a/toontown/building/__init__.py b/toontown/building/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/catalog/CatalogAccessoryItem.py b/toontown/catalog/CatalogAccessoryItem.py new file mode 100755 index 00000000..d8f371e1 --- /dev/null +++ b/toontown/catalog/CatalogAccessoryItem.py @@ -0,0 +1,360 @@ +import CatalogItem +from CatalogAccessoryItemGlobals import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toon import ToonDNA +import random, types +from direct.showbase import PythonUtil +from direct.gui.DirectGui import * +from panda3d.core import * + +class CatalogAccessoryItem(CatalogItem.CatalogItem): + + def makeNewItem(self, accessoryType, isSpecial = False): + self.accessoryType = accessoryType + self.isSpecial = isSpecial + CatalogItem.CatalogItem.makeNewItem(self) + + def getPurchaseLimit(self): + return 1 + + def reachedPurchaseLimit(self, avatar): + if avatar.onOrder.count(self) != 0: + return 1 + if avatar.onGiftOrder.count(self) != 0: + return 1 + if avatar.mailboxContents.count(self) != 0: + return 1 + str = AccessoryTypes[self.accessoryType][ATString] + if self.isHat(): + defn = ToonDNA.HatStyles[str] + hat = avatar.getHat() + if hat[0] == defn[0] and hat[1] == defn[1] and hat[2] == defn[2]: + return 1 + l = avatar.hatList + for i in xrange(0, len(l), 3): + if l[i] == defn[0] and l[i + 1] == defn[1] and l[i + 2] == defn[2]: + return 1 + + elif self.areGlasses(): + defn = ToonDNA.GlassesStyles[str] + glasses = avatar.getGlasses() + if glasses[0] == defn[0] and glasses[1] == defn[1] and glasses[2] == defn[2]: + return 1 + l = avatar.glassesList + for i in xrange(0, len(l), 3): + if l[i] == defn[0] and l[i + 1] == defn[1] and l[i + 2] == defn[2]: + return 1 + + elif self.isBackpack(): + defn = ToonDNA.BackpackStyles[str] + backpack = avatar.getBackpack() + if backpack[0] == defn[0] and backpack[1] == defn[1] and backpack[2] == defn[2]: + return 1 + l = avatar.backpackList + for i in xrange(0, len(l), 3): + if l[i] == defn[0] and l[i + 1] == defn[1] and l[i + 2] == defn[2]: + return 1 + + else: + defn = ToonDNA.ShoesStyles[str] + shoes = avatar.getShoes() + if shoes[0] == defn[0] and shoes[1] == defn[1] and shoes[2] == defn[2]: + return 1 + l = avatar.shoesList + for i in xrange(0, len(l), 3): + if l[i] == defn[0] and l[i + 1] == defn[1] and l[i + 2] == defn[2]: + return 1 + + return 0 + + def getTypeName(self): + return TTLocalizer.AccessoryTypeName + + def getName(self): + typeName = TTLocalizer.AccessoryTypeNames.get(self.accessoryType, 0) + if typeName: + return typeName + else: + article = AccessoryTypes[self.accessoryType][ATArticle] + return TTLocalizer.AccessoryArticleNames[article] + + def recordPurchase(self, avatar, optional): + if avatar.isTrunkFull(): + return ToontownGlobals.P_NoRoomForItem + str = AccessoryTypes[self.accessoryType][ATString] + if self.isHat(): + defn = ToonDNA.HatStyles[str] + if not avatar.checkAccessorySanity(ToonDNA.HAT, defn[0], defn[1], defn[2]): + self.notify.warning('Avatar %s lost hat %d %d %d; invalid item.' % (avatar.doId, + defn[0], + defn[1], + defn[2])) + return ToontownGlobals.P_ItemAvailable + hat = avatar.getHat() + added = avatar.addToAccessoriesList(ToonDNA.HAT, defn[0], defn[1], defn[2]) + if added: + avatar.b_setHatList(avatar.getHatList()) + self.notify.info('Avatar %s put hat %d,%d,%d in trunk.' % (avatar.doId, + defn[0], + defn[1], + defn[2])) + else: + self.notify.warning('Avatar %s lost current hat %d %d %d; trunk full.' % (avatar.doId, + defn[0], + defn[1], + defn[2])) + avatar.b_setHat(defn[0], defn[1], defn[2]) + elif self.areGlasses(): + defn = ToonDNA.GlassesStyles[str] + if not avatar.checkAccessorySanity(ToonDNA.GLASSES, defn[0], defn[1], defn[2]): + self.notify.warning('Avatar %s lost glasses %d %d %d; invalid item.' % (avatar.doId, + defn[0], + defn[1], + defn[2])) + return ToontownGlobals.P_ItemAvailable + glasses = avatar.getGlasses() + added = avatar.addToAccessoriesList(ToonDNA.GLASSES, defn[0], defn[1], defn[2]) + if added: + avatar.b_setGlassesList(avatar.getGlassesList()) + self.notify.info('Avatar %s put glasses %d,%d,%d in trunk.' % (avatar.doId, + defn[0], + defn[1], + defn[2])) + else: + self.notify.warning('Avatar %s lost current glasses %d %d %d; trunk full.' % (avatar.doId, + defn[0], + defn[1], + defn[2])) + avatar.b_setGlasses(defn[0], defn[1], defn[2]) + elif self.isBackpack(): + defn = ToonDNA.BackpackStyles[str] + if not avatar.checkAccessorySanity(ToonDNA.BACKPACK, defn[0], defn[1], defn[2]): + self.notify.warning('Avatar %s lost backpack %d %d %d; invalid item.' % (avatar.doId, + defn[0], + defn[1], + defn[2])) + return ToontownGlobals.P_ItemAvailable + backpack = avatar.getBackpack() + added = avatar.addToAccessoriesList(ToonDNA.BACKPACK, defn[0], defn[1], defn[2]) + if added: + avatar.b_setBackpackList(avatar.getBackpackList()) + self.notify.info('Avatar %s put backpack %d,%d,%d in trunk.' % (avatar.doId, + defn[0], + defn[1], + defn[2])) + else: + self.notify.warning('Avatar %s lost current backpack %d %d %d; trunk full.' % (avatar.doId, + defn[0], + defn[1], + defn[2])) + avatar.b_setBackpack(defn[0], defn[1], defn[2]) + else: + defn = ToonDNA.ShoesStyles[str] + if not avatar.checkAccessorySanity(ToonDNA.SHOES, defn[0], defn[1], defn[2]): + self.notify.warning('Avatar %s lost shoes %d %d %d; invalid item.' % (avatar.doId, + defn[0], + defn[1], + defn[2])) + return ToontownGlobals.P_ItemAvailable + shoes = avatar.getShoes() + added = avatar.addToAccessoriesList(ToonDNA.SHOES, defn[0], defn[1], defn[2]) + if added: + avatar.b_setShoesList(avatar.getShoesList()) + self.notify.info('Avatar %s put shoes %d,%d,%d in trunk.' % (avatar.doId, + defn[0], + defn[1], + defn[2])) + else: + self.notify.warning('Avatar %s lost current shoes %d %d %d; trunk full.' % (avatar.doId, + defn[0], + defn[1], + defn[2])) + avatar.b_setShoes(defn[0], defn[1], defn[2]) + avatar.d_catalogGenAccessories() + return ToontownGlobals.P_ItemAvailable + + def getDeliveryTime(self): + return 60 + + def getPicture(self, avatar): + model = self.loadModel() + spin = 1 + model.setBin('unsorted', 0, 1) + self.hasPicture = True + return self.makeFrameModel(model, spin) + + def applyColor(self, model, color): + if model == None or color == None: + return + if isinstance(color, types.StringType): + tex = loader.loadTexture(color) + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + model.setTexture(tex, 1) + else: + needsAlpha = color[3] != 1 + color = VBase4(color[0], color[1], color[2], color[3]) + model.setColorScale(color, 1) + if needsAlpha: + model.setTransparency(1) + + def loadModel(self): + modelPath = self.getFilename() + if self.areShoes(): + str = AccessoryTypes[self.accessoryType][ATString] + defn = ToonDNA.ShoesStyles[str] + legModel = loader.loadModel('phase_3/models/char/tt_a_chr_dgm_shorts_legs_1000') + model = legModel.find('**/' + modelPath) + else: + model = loader.loadModel(modelPath) + texture = self.getTexture() + if texture: + self.applyColor(model, texture) + colorVec4 = self.getColor() + if colorVec4: + modelColor = (colorVec4.getX(), colorVec4.getY(), colorVec4.getZ()) + self.applyColor(model, modelColor) + model.flattenLight() + return model + + def requestPurchase(self, phone, callback): + from toontown.toontowngui import TTDialog + avatar = base.localAvatar + accessoriesOnOrder = 0 + for item in avatar.onOrder + avatar.mailboxContents + avatar.onGiftOrder: + if hasattr(item, 'isHat'): + accessoriesOnOrder += 1 + + if avatar.isTrunkFull(accessoriesOnOrder): + self.requestPurchaseCleanup() + buttonCallback = PythonUtil.Functor(self.__handleFullPurchaseDialog, phone, callback) + text = TTLocalizer.CatalogPurchaseTrunkFull + self.dialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=text, text_wordwrap=15, command=buttonCallback) + self.dialog.show() + else: + CatalogItem.CatalogItem.requestPurchase(self, phone, callback) + + def requestPurchaseCleanup(self): + if hasattr(self, 'dialog'): + self.dialog.cleanup() + del self.dialog + + def __handleFullPurchaseDialog(self, phone, callback, buttonValue): + from toontown.toontowngui import TTDialog + self.requestPurchaseCleanup() + if buttonValue == DGG.DIALOG_OK: + CatalogItem.CatalogItem.requestPurchase(self, phone, callback) + else: + callback(ToontownGlobals.P_UserCancelled, self) + + def getAcceptItemErrorText(self, retcode): + if retcode == ToontownGlobals.P_ItemAvailable: + if self.isHat(): + return TTLocalizer.CatalogAcceptHat + elif self.areGlasses(): + return TTLocalizer.CatalogAcceptGlasses + elif self.isBackpack(): + return TTLocalizer.CatalogAcceptBackpack + else: + return TTLocalizer.CatalogAcceptShoes + elif retcode == ToontownGlobals.P_NoRoomForItem: + return TTLocalizer.CatalogAcceptTrunkFull + return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode) + + def isHat(self): + return AccessoryTypes[self.accessoryType][ATArticle] == AHat + + def areGlasses(self): + return AccessoryTypes[self.accessoryType][ATArticle] == AGlasses + + def isBackpack(self): + return AccessoryTypes[self.accessoryType][ATArticle] == ABackpack + + def areShoes(self): + return AccessoryTypes[self.accessoryType][ATArticle] == AShoes + + def output(self, store = -1): + return 'CatalogAccessoryItem(%s%s)' % (self.accessoryType, self.formatOptionalData(store)) + + def getFilename(self): + str = AccessoryTypes[self.accessoryType][ATString] + if self.isHat(): + defn = ToonDNA.HatStyles[str] + modelPath = ToonDNA.HatModels[defn[0]] + elif self.areGlasses(): + defn = ToonDNA.GlassesStyles[str] + modelPath = ToonDNA.GlassesModels[defn[0]] + elif self.isBackpack(): + defn = ToonDNA.BackpackStyles[str] + modelPath = ToonDNA.BackpackModels[defn[0]] + else: + defn = ToonDNA.ShoesStyles[str] + modelPath = ToonDNA.ShoesModels[defn[0]] + return modelPath + + def getTexture(self): + str = AccessoryTypes[self.accessoryType][ATString] + if self.isHat(): + defn = ToonDNA.HatStyles[str] + modelPath = ToonDNA.HatTextures[defn[1]] + elif self.areGlasses(): + defn = ToonDNA.GlassesStyles[str] + modelPath = ToonDNA.GlassesTextures[defn[1]] + elif self.isBackpack(): + defn = ToonDNA.BackpackStyles[str] + modelPath = ToonDNA.BackpackTextures[defn[1]] + else: + defn = ToonDNA.ShoesStyles[str] + modelPath = ToonDNA.ShoesTextures[defn[1]] + return modelPath + + def getColor(self): + return None + + def compareTo(self, other): + return self.accessoryType - other.accessoryType + + def getHashContents(self): + return self.accessoryType + + def getBasePrice(self): + return AccessoryTypes[self.accessoryType][ATBasePrice] + + def getEmblemPrices(self): + result = () + info = AccessoryTypes[self.accessoryType] + if ATEmblemPrices <= len(info) - 1: + result = info[ATEmblemPrices] + return result + + def decodeDatagram(self, di, versionNumber, store): + CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store) + self.accessoryType = di.getUint16() + self.isSpecial = di.getBool() + str = AccessoryTypes[self.accessoryType][ATString] + if self.isHat(): + defn = ToonDNA.HatStyles[str] + elif self.areGlasses(): + defn = ToonDNA.GlassesStyles[str] + elif self.isBackpack(): + defn = ToonDNA.BackpackStyles[str] + else: + defn = ToonDNA.ShoesStyles[str] + color = ToonDNA.ClothesColors[defn[2]] + + def encodeDatagram(self, dg, store): + CatalogItem.CatalogItem.encodeDatagram(self, dg, store) + dg.addUint16(self.accessoryType) + dg.addBool(self.isSpecial) + + def isGift(self): + return not self.getEmblemPrices() + +def getAllAccessories(*accessoryTypes): + list = [] + for accessoryType in accessoryTypes: + base = CatalogAccessoryItem(accessoryType) + list.append(base) + + return list diff --git a/toontown/catalog/CatalogAccessoryItemGlobals.py b/toontown/catalog/CatalogAccessoryItemGlobals.py new file mode 100755 index 00000000..f72765a5 --- /dev/null +++ b/toontown/catalog/CatalogAccessoryItemGlobals.py @@ -0,0 +1,737 @@ +ATArticle = 0 +ATString = 1 +ATBasePrice = 2 +ATReleased = 3 +ATEmblemPrices = 4 +AHat = 0 +AGlasses = 1 +ABackpack = 2 +AShoes = 3 +APriceBasic = 250 +APriceBasicPlus = 400 +APriceCool = 800 +APriceAwesome = 1500 +AccessoryTypes = {101: (AHat, + 'hbb1', + APriceBasic, + 1), + 102: (AHat, + 'hsf1', + APriceCool, + 5), + 103: (AHat, + 'hrb1', + APriceBasic, + 1), + 104: (AHat, + 'hsf2', + APriceCool, + 0), + 105: (AHat, + 'hsf3', + APriceCool, + 0), + 106: (AHat, + 'hrb2', + APriceBasicPlus, + 3), + 107: (AHat, + 'hrb3', + APriceBasicPlus, + 0), + 108: (AHat, + 'hht1', + APriceCool, + 4), + 109: (AHat, + 'hht2', + APriceCool, + 3), + 110: (AHat, + 'htp1', + APriceCool, + 3), + 111: (AHat, + 'htp2', + APriceCool, + 0), + 112: (AHat, + 'hav1', + 3500, + 0), + 113: (AHat, + 'hfp1', + 3500, + 0), + 114: (AHat, + 'hsg1', + 3500, + 0), + 115: (AHat, + 'hwt1', + 3500, + 0), + 116: (AHat, + 'hfz1', + APriceCool, + 5), + 117: (AHat, + 'hgf1', + APriceCool, + 1), + 118: (AHat, + 'hpt1', + APriceBasicPlus, + 1), + 119: (AHat, + 'hpb1', + APriceBasicPlus, + 6), + 120: (AHat, + 'hcr1', + 10000, + 5), + 121: (AHat, + 'hbb2', + APriceBasic, + 2), + 122: (AHat, + 'hbb3', + APriceBasic, + 2), + 123: (AHat, + 'hcw1', + APriceCool, + 1), + 124: (AHat, + 'hpr1', + APriceAwesome, + 1), + 125: (AHat, + 'hpp1', + APriceBasicPlus, + 1), + 126: (AHat, + 'hfs1', + APriceCool, + 1), + 127: (AHat, + 'hsb1', + APriceAwesome, + 1), + 128: (AHat, + 'hst1', + APriceBasicPlus, + 1), + 129: (AHat, + 'hsu1', + APriceCool, + 1), + 130: (AHat, + 'hrb4', + APriceBasic, + 1), + 131: (AHat, + 'hrb5', + APriceBasicPlus, + 4), + 132: (AHat, + 'hrb6', + APriceBasic, + 2), + 133: (AHat, + 'hrb7', + APriceBasicPlus, + 6), + 134: (AHat, + 'hat1', + APriceCool, + 2), + 135: (AHat, + 'hhd1', + APriceCool, + 2), + 136: (AHat, + 'hbw1', + APriceCool, + 6), + 137: (AHat, + 'hch1', + APriceCool, + 5), + 138: (AHat, + 'hdt1', + APriceAwesome, + 6), + 139: (AHat, + 'hft1', + APriceCool, + 4), + 140: (AHat, + 'hfd1', + APriceCool, + 6), + 141: (AHat, + 'hmk1', + APriceAwesome, + 2), + 142: (AHat, + 'hft2', + APriceCool, + 6), + 143: (AHat, + 'hhd2', + APriceCool, + 3), + 144: (AHat, + 'hpc1', + APriceCool, + 5), + 145: (AHat, + 'hrh1', + APriceCool, + 2), + 146: (AHat, + 'hhm1', + 2500, + 2), + 147: (AHat, + 'hat2', + APriceCool, + 2), + 148: (AHat, + 'htr1', + 10000, + 3), + 149: (AHat, + 'hhm2', + APriceAwesome, + 2), + 150: (AHat, + 'hwz1', + APriceCool, + 2), + 151: (AHat, + 'hwz2', + APriceCool, + 2), + 152: (AHat, + 'hhm3', + APriceAwesome, + 6), + 153: (AHat, + 'hhm4', + APriceAwesome, + 5), + 154: (AHat, + 'hfp2', + APriceCool, + 5), + 155: (AHat, + 'hhm5', + APriceAwesome, + 4), + 156: (AHat, + 'hnp1', + APriceAwesome, + 6), + 157: (AHat, + 'hpc2', + APriceAwesome, + 3), + 158: (AHat, + 'hph1', + APriceAwesome, + 4), + 159: (AHat, + 'hwg1', + APriceCool, + 5), + 160: (AHat, + 'hbb4', + APriceBasic, + 5), + 161: (AHat, + 'hbb5', + APriceBasic, + 2), + 162: (AHat, + 'hbb6', + APriceBasic, + 5), + 163: (AHat, + 'hsl1', + APriceCool, + 5), + 164: (AHat, + 'hfr1', + 3000, + 4), + 165: (AHat, + 'hby1', + APriceAwesome, + 5), + 166: (AHat, + 'hrb8', + APriceBasicPlus, + 6), + 167: (AHat, + 'hjh1', + APriceAwesome, + 3), + 168: (AHat, + 'hbb7', + APriceBasic, + 6), + 169: (AHat, + 'hrb9', + APriceBasicPlus, + 6), + 170: (AHat, + 'hwt2', + APriceAwesome, + 4), + 171: (AHat, + 'hhw1', + APriceBasicPlus, + 7), + 172: (AHat, + 'hhw2', + 900, + 7), + 173: (AHat, + 'hob1', + APriceAwesome, + 6), + 174: (AHat, + 'hbn1', + APriceAwesome, + 8), + 175: (AHat, + 'hpt2', + APriceCool, + 9), + 176: (AHat, + 'kmh1', + APriceAwesome, + 8), + 201: (AGlasses, + 'grd1', + APriceBasicPlus, + 0), + 202: (AGlasses, + 'gmb1', + APriceCool, + 1), + 203: (AGlasses, + 'gnr1', + APriceCool, + 0), + 204: (AGlasses, + 'gst1', + APriceBasicPlus, + 1), + 205: (AGlasses, + 'g3d1', + APriceCool, + 1), + 206: (AGlasses, + 'gav1', + APriceCool, + 1), + 207: (AGlasses, + 'gce1', + APriceCool, + 2), + 208: (AGlasses, + 'gdk1', + APriceBasic, + 1), + 209: (AGlasses, + 'gjo1', + APriceBasicPlus, + 1), + 210: (AGlasses, + 'gsb1', + APriceAwesome, + 1), + 211: (AGlasses, + 'ggl1', + APriceCool, + 6), + 212: (AGlasses, + 'ggm1', + APriceBasicPlus, + 2), + 213: (AGlasses, + 'ghg1', + APriceAwesome, + 3), + 214: (AGlasses, + 'gie1', + APriceCool, + 2), + 215: (AGlasses, + 'gmt1', + APriceCool, + 2), + 216: (AGlasses, + 'gmt2', + APriceCool, + 2), + 217: (AGlasses, + 'gmt3', + 3500, + 5), + 218: (AGlasses, + 'gmt4', + 3500, + 5), + 219: (AGlasses, + 'gmt5', + 3500, + 5), + 220: (AGlasses, + 'gmn1', + APriceAwesome, + 6), + 221: (AGlasses, + 'gmo1', + APriceAwesome, + 4), + 222: (AGlasses, + 'gsr1', + APriceBasicPlus, + 5), + 223: (AGlasses, + 'ghw1', + APriceBasic, + 0), + 224: (AGlasses, + 'ghw2', + APriceBasic, + 7), + 225: (AGlasses, + 'gag1', + APriceAwesome, + 8), + 226: (AGlasses, + 'ghy1', + APriceAwesome, + 8), + 301: (ABackpack, + 'bpb1', + APriceBasic, + 4), + 302: (ABackpack, + 'bpb2', + APriceBasic, + 1), + 303: (ABackpack, + 'bpb3', + APriceBasic, + 5), + 304: (ABackpack, + 'bpd1', + APriceBasicPlus, + 4), + 305: (ABackpack, + 'bpd2', + APriceBasicPlus, + 5), + 306: (ABackpack, + 'bwg1', + APriceCool, + 2), + 307: (ABackpack, + 'bwg2', + APriceCool, + 2), + 308: (ABackpack, + 'bwg3', + APriceCool, + 1), + 309: (ABackpack, + 'bst1', + APriceAwesome, + 1), + 310: (ABackpack, + 'bfn1', + APriceCool, + 1), + 311: (ABackpack, + 'baw1', + APriceCool, + 3), + 312: (ABackpack, + 'baw2', + APriceAwesome, + 2), + 313: (ABackpack, + 'bwt1', + 3000, + 3), + 314: (ABackpack, + 'bwg4', + APriceAwesome, + 6), + 315: (ABackpack, + 'bwg5', + 3000, + 5), + 316: (ABackpack, + 'bwg6', + 3000, + 4), + 317: (ABackpack, + 'bjp1', + 3000, + 1), + 318: (ABackpack, + 'blg1', + APriceCool, + 2), + 319: (ABackpack, + 'bsa1', + 2500, + 5), + 320: (ABackpack, + 'bwg7', + APriceAwesome, + 6), + 321: (ABackpack, + 'bsa2', + 2000, + 2), + 322: (ABackpack, + 'bsa3', + 2000, + 2), + 323: (ABackpack, + 'bap1', + 5000, + 4), + 324: (ABackpack, + 'bhw1', + 900, + 7), + 325: (ABackpack, + 'bhw2', + APriceBasicPlus, + 7), + 326: (ABackpack, + 'bhw3', + APriceBasicPlus, + 7), + 327: (ABackpack, + 'bhw4', + 900, + 7), + 328: (ABackpack, + 'bob1', + 3000, + 6), + 329: (ABackpack, + 'bfg1', + 3000, + 6), + 330: (ABackpack, + 'bfl1', + APriceAwesome, + 8), + 401: (AShoes, + 'sat1', + APriceBasic, + 3), + 402: (AShoes, + 'sat2', + APriceBasic, + 1), + 403: (AShoes, + 'smb1', + APriceAwesome, + 1), + 404: (AShoes, + 'scs1', + APriceBasicPlus, + 6), + 405: (AShoes, + 'swt1', + APriceBasicPlus, + 1), + 406: (AShoes, + 'smj1', + APriceBasicPlus, + 1), + 407: (AShoes, + 'sdk1', + APriceBasic, + 1), + 408: (AShoes, + 'sat3', + APriceBasic, + 1), + 409: (AShoes, + 'scs2', + APriceBasicPlus, + 1), + 410: (AShoes, + 'scs3', + APriceBasicPlus, + 1), + 411: (AShoes, + 'scs4', + APriceBasicPlus, + 1), + 412: (AShoes, + 'scb1', + APriceAwesome, + 1), + 413: (AShoes, + 'sfb1', + APriceCool, + 1), + 414: (AShoes, + 'sht1', + APriceAwesome, + 4), + 415: (AShoes, + 'smj2', + APriceBasicPlus, + 3), + 416: (AShoes, + 'smj3', + APriceBasicPlus, + 4), + 417: (AShoes, + 'ssb1', + APriceAwesome, + 2), + 418: (AShoes, + 'sts1', + APriceBasic, + 5), + 419: (AShoes, + 'sts2', + APriceBasic, + 4), + 420: (AShoes, + 'scs5', + APriceBasicPlus, + 4), + 421: (AShoes, + 'smb2', + APriceAwesome, + 3), + 422: (AShoes, + 'smb3', + APriceAwesome, + 2), + 423: (AShoes, + 'smb4', + APriceAwesome, + 5), + 424: (AShoes, + 'sfb2', + 2000, + 6), + 425: (AShoes, + 'sfb3', + 2000, + 4), + 426: (AShoes, + 'sfb4', + 2000, + 3), + 427: (AShoes, + 'sfb5', + 2000, + 5), + 428: (AShoes, + 'sfb6', + 2000, + 4), + 429: (AShoes, + 'slf1', + APriceBasicPlus, + 3), + 430: (AShoes, + 'smj4', + APriceBasicPlus, + 2), + 431: (AShoes, + 'smt1', + APriceAwesome, + 4), + 432: (AShoes, + 'sox1', + APriceAwesome, + 5), + 433: (AShoes, + 'srb1', + APriceAwesome, + 6), + 434: (AShoes, + 'sst1', + 3000, + 3), + 435: (AShoes, + 'swb1', + APriceCool, + 3), + 436: (AShoes, + 'swb2', + APriceCool, + 4), + 437: (AShoes, + 'swk1', + APriceAwesome, + 3), + 438: (AShoes, + 'scs6', + APriceBasicPlus, + 0), + 439: (AShoes, + 'smb5', + APriceAwesome, + 3), + 440: (AShoes, + 'sht2', + APriceAwesome, + 4), + 441: (AShoes, + 'srb2', + APriceAwesome, + 3), + 442: (AShoes, + 'sts3', + APriceBasic, + 6), + 443: (AShoes, + 'sts4', + APriceBasic, + 3), + 444: (AShoes, + 'sts5', + APriceBasic, + 2), + 445: (AShoes, + 'srb3', + APriceCool, + 5), + 446: (AShoes, + 'srb4', + APriceCool, + 3), + 447: (AShoes, + 'sat4', + APriceBasic, + 3), + 448: (AShoes, + 'shw1', + APriceCool, + 7), + 449: (AShoes, + 'shw2', + APriceCool, + 7)} diff --git a/toontown/catalog/CatalogAnimatedFurnitureItem.py b/toontown/catalog/CatalogAnimatedFurnitureItem.py new file mode 100755 index 00000000..81848929 --- /dev/null +++ b/toontown/catalog/CatalogAnimatedFurnitureItem.py @@ -0,0 +1,27 @@ +from CatalogFurnitureItem import * +FTAnimRate = 6 +AnimatedFurnitureItemKeys = (10020, 270, 990, 460, 470, 480, 490, 491, 492) + +class CatalogAnimatedFurnitureItem(CatalogFurnitureItem): + + def loadModel(self): + model = CatalogFurnitureItem.loadModel(self) + self.setAnimRate(model, self.getAnimRate()) + return model + + def getAnimRate(self): + item = FurnitureTypes[self.furnitureType] + if FTAnimRate < len(item): + animRate = item[FTAnimRate] + if not animRate == None: + return item[FTAnimRate] + else: + return 1 + else: + return 1 + return + + def setAnimRate(self, model, rate): + seqNodes = model.findAllMatches('**/seqNode*') + for seqNode in seqNodes: + seqNode.node().setPlayRate(rate) diff --git a/toontown/catalog/CatalogAtticItem.py b/toontown/catalog/CatalogAtticItem.py new file mode 100755 index 00000000..44cd6e76 --- /dev/null +++ b/toontown/catalog/CatalogAtticItem.py @@ -0,0 +1,65 @@ +import CatalogItem +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil +from direct.gui.DirectGui import * +from toontown.toonbase import ToontownGlobals + +class CatalogAtticItem(CatalogItem.CatalogItem): + + def storedInAttic(self): + return 1 + + def isDeletable(self): + return 1 + + def getHouseInfo(self, avatar): + houseId = avatar.houseId + if not houseId: + self.notify.warning('Avatar %s has no houseId associated.' % avatar.doId) + return (None, ToontownGlobals.P_InvalidIndex) + house = simbase.air.doId2do.get(houseId) + if not house: + self.notify.warning('House %s (for avatar %s) not instantiated.' % (houseId, avatar.doId)) + return (None, ToontownGlobals.P_InvalidIndex) + numAtticItems = len(house.atticItems) + len(house.atticWallpaper) + len(house.atticWindows) + numHouseItems = numAtticItems + len(house.interiorItems) + if numHouseItems >= ToontownGlobals.MaxHouseItems and not self.replacesExisting(): + return (house, ToontownGlobals.P_NoRoomForItem) + return (house, ToontownGlobals.P_ItemAvailable) + + def requestPurchase(self, phone, callback): + from toontown.toontowngui import TTDialog + avatar = base.localAvatar + itemsOnOrder = 0 + for item in avatar.onOrder + avatar.mailboxContents: + if item.storedInAttic() and not item.replacesExisting(): + itemsOnOrder += 1 + + numHouseItems = phone.numHouseItems + itemsOnOrder + if numHouseItems >= ToontownGlobals.MaxHouseItems and not self.replacesExisting(): + self.requestPurchaseCleanup() + buttonCallback = PythonUtil.Functor(self.__handleFullPurchaseDialog, phone, callback) + self.dialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.CatalogPurchaseHouseFull, text_wordwrap=15, command=buttonCallback) + self.dialog.show() + else: + CatalogItem.CatalogItem.requestPurchase(self, phone, callback) + + def requestPurchaseCleanup(self): + if hasattr(self, 'dialog'): + self.dialog.cleanup() + del self.dialog + + def __handleFullPurchaseDialog(self, phone, callback, buttonValue): + from toontown.toontowngui import TTDialog + self.requestPurchaseCleanup() + if buttonValue == DGG.DIALOG_OK: + CatalogItem.CatalogItem.requestPurchase(self, phone, callback) + else: + callback(ToontownGlobals.P_UserCancelled, self) + + def getAcceptItemErrorText(self, retcode): + if retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.CatalogAcceptInAttic + elif retcode == ToontownGlobals.P_NoRoomForItem: + return TTLocalizer.CatalogAcceptHouseFull + return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode) diff --git a/toontown/catalog/CatalogBeanItem.py b/toontown/catalog/CatalogBeanItem.py new file mode 100755 index 00000000..5ae535e7 --- /dev/null +++ b/toontown/catalog/CatalogBeanItem.py @@ -0,0 +1,73 @@ +import CatalogItem +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +from direct.interval.IntervalGlobal import * + +class CatalogBeanItem(CatalogItem.CatalogItem): + sequenceNumber = 0 + + def makeNewItem(self, beanAmount, tagCode = 1): + self.beanAmount = beanAmount + self.giftCode = tagCode + CatalogItem.CatalogItem.makeNewItem(self) + + def getPurchaseLimit(self): + return 0 + + def reachedPurchaseLimit(self, avatar): + return self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder + + def getAcceptItemErrorText(self, retcode): + if retcode == ToontownGlobals.P_ItemAvailable: + if self.giftCode == ToontownGlobals.GIFT_RAT: + return TTLocalizer.CatalogAcceptRATBeans + elif self.giftCode == ToontownGlobals.GIFT_partyrefund: + return TTLocalizer.CatalogAcceptPartyRefund + else: + return TTLocalizer.CatalogAcceptBeans + return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode) + + def saveHistory(self): + return 0 + + def getTypeName(self): + return TTLocalizer.BeanTypeName + + def getName(self): + name = '%s %s' % (self.beanAmount, TTLocalizer.BeanTypeName) + return name + + def recordPurchase(self, avatar, optional): + if avatar: + avatar.addMoney(self.beanAmount) + return ToontownGlobals.P_ItemAvailable + + def getPicture(self, avatar): + beanJar = loader.loadModel('phase_3.5/models/gui/jar_gui') + frame = self.makeFrame() + beanJar.reparentTo(frame) + beanJar.setPos(0, 0, 0) + beanJar.setScale(2.5) + self.hasPicture = True + return (frame, None) + + def output(self, store = -1): + return 'CatalogBeanItem(%s%s)' % (self.beanAmount, self.formatOptionalData(store)) + + def compareTo(self, other): + return self.beanAmount - other.beanAmount + + def getHashContents(self): + return self.beanAmount + + def getBasePrice(self): + return self.beanAmount + + def decodeDatagram(self, di, versionNumber, store): + CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store) + self.beanAmount = di.getUint16() + + def encodeDatagram(self, dg, store): + CatalogItem.CatalogItem.encodeDatagram(self, dg, store) + dg.addUint16(self.beanAmount) diff --git a/toontown/catalog/CatalogChatItem.py b/toontown/catalog/CatalogChatItem.py new file mode 100755 index 00000000..9e5f8ecc --- /dev/null +++ b/toontown/catalog/CatalogChatItem.py @@ -0,0 +1,149 @@ +from panda3d.core import * +import CatalogItem +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPLocalizer +from toontown.toonbase import TTLocalizer +bannedPhrases = [11009] + +class CatalogChatItem(CatalogItem.CatalogItem): + + def makeNewItem(self, customIndex): + self.customIndex = customIndex + CatalogItem.CatalogItem.makeNewItem(self) + + def getPurchaseLimit(self): + return 1 + + def reachedPurchaseLimit(self, avatar): + return self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder or avatar.customMessages.count(self.customIndex) != 0 + + def getTypeName(self): + return TTLocalizer.ChatTypeName + + def getName(self): + return TTLocalizer.ChatItemQuotes % OTPLocalizer.CustomSCStrings[self.customIndex] + + def getDisplayName(self): + return OTPLocalizer.CustomSCStrings[self.customIndex] + + def getDeliveryTime(self): + return 0 + + def recordPurchase(self, avatar, optional): + if avatar.customMessages.count(self.customIndex) != 0: + return ToontownGlobals.P_ReachedPurchaseLimit + if len(avatar.customMessages) >= ToontownGlobals.MaxCustomMessages: + if optional >= 0 and optional < len(avatar.customMessages): + del avatar.customMessages[optional] + if len(avatar.customMessages) >= ToontownGlobals.MaxCustomMessages: + return ToontownGlobals.P_NoRoomForItem + avatar.customMessages.append(self.customIndex) + avatar.d_setCustomMessages(avatar.customMessages) + return ToontownGlobals.P_ItemAvailable + + def getAcceptItemErrorText(self, retcode): + if retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.CatalogAcceptChat + return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode) + + def output(self, store = -1): + return 'CatalogChatItem(%s%s)' % (self.customIndex, self.formatOptionalData(store)) + + def compareTo(self, other): + return self.customIndex - other.customIndex + + def getHashContents(self): + return self.customIndex + + def getBasePrice(self): + if self.customIndex >= 10000: + return 150 + return 100 + + def decodeDatagram(self, di, versionNumber, store): + CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store) + self.customIndex = di.getUint16() + text = OTPLocalizer.CustomSCStrings[self.customIndex] + + def encodeDatagram(self, dg, store): + CatalogItem.CatalogItem.encodeDatagram(self, dg, store) + dg.addUint16(self.customIndex) + + def acceptItem(self, mailbox, index, callback): + if len(base.localAvatar.customMessages) < ToontownGlobals.MaxCustomMessages: + mailbox.acceptItem(self, index, callback) + else: + self.showMessagePickerOnAccept(mailbox, index, callback) + + def requestPurchase(self, phone, callback): + if len(base.localAvatar.customMessages) < ToontownGlobals.MaxCustomMessages: + CatalogItem.CatalogItem.requestPurchase(self, phone, callback) + else: + self.showMessagePicker(phone, callback) + + def showMessagePicker(self, phone, callback): + self.phone = phone + self.callback = callback + import CatalogChatItemPicker + self.messagePicker = CatalogChatItemPicker.CatalogChatItemPicker(self.__handlePickerDone, self.customIndex) + self.messagePicker.show() + + def showMessagePickerOnAccept(self, mailbox, index, callback): + self.mailbox = mailbox + self.callback = callback + self.index = index + import CatalogChatItemPicker + self.messagePicker = CatalogChatItemPicker.CatalogChatItemPicker(self.__handlePickerOnAccept, self.customIndex) + self.messagePicker.show() + + def __handlePickerOnAccept(self, status, pickedMessage = None): + print 'Picker Status%s' % status + if status == 'pick': + self.mailbox.acceptItem(self, self.index, self.callback, pickedMessage) + else: + print 'picker canceled' + self.callback(ToontownGlobals.P_UserCancelled, None, self.index) + self.messagePicker.hide() + self.messagePicker.destroy() + del self.messagePicker + del self.callback + del self.mailbox + return + + def __handlePickerDone(self, status, pickedMessage = None): + if status == 'pick': + CatalogItem.CatalogItem.requestPurchase(self, self.phone, self.callback, pickedMessage) + self.messagePicker.hide() + self.messagePicker.destroy() + del self.messagePicker + del self.callback + del self.phone + + def getPicture(self, avatar): + chatBalloon = loader.loadModel('phase_3/models/props/chatbox') + chatBalloon.find('**/top').setPos(1, 0, 5) + chatBalloon.find('**/middle').setScale(1, 1, 3) + frame = self.makeFrame() + chatBalloon.reparentTo(frame) + chatBalloon.setPos(-2.19, 0, -1.74) + chatBalloon.setScale(0.4) + self.hasPicture = True + return (frame, None) + + +def getChatRange(fromIndex, toIndex, *otherRanges): + list = [] + froms = [fromIndex] + tos = [toIndex] + i = 0 + while i < len(otherRanges): + froms.append(otherRanges[i]) + tos.append(otherRanges[i + 1]) + i += 2 + + for chatId in OTPLocalizer.CustomSCStrings.keys(): + for fromIndex, toIndex in zip(froms, tos): + if chatId >= fromIndex and chatId <= toIndex and chatId not in bannedPhrases: + list.append(CatalogChatItem(chatId)) + + return list diff --git a/toontown/catalog/CatalogChatItemPicker.py b/toontown/catalog/CatalogChatItemPicker.py new file mode 100755 index 00000000..c98188c4 --- /dev/null +++ b/toontown/catalog/CatalogChatItemPicker.py @@ -0,0 +1,91 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.showbase import DirectObject +import CatalogItem +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +from toontown.toontowngui import TTDialog +NUM_ITEMS_SHOWN = 15 + +class CatalogChatItemPicker(DirectObject.DirectObject): + + def __init__(self, callback, newMsg): + self.confirmDelete = None + self.doneCallback = callback + self.panel = DirectFrame(relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.4, 1, 1.6), text=TTLocalizer.MessagePickerTitle % OTPLocalizer.CustomSCStrings[newMsg], text_pos=(0, 0.68), text_scale=0.05, text_wordwrap=24, pos=(0, 0, 0)) + msgStrings = [] + for msg in base.localAvatar.customMessages: + msgStrings.append(OTPLocalizer.CustomSCStrings[msg]) + + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.picker = DirectScrolledList(parent=self.panel, relief=None, pos=(0, 0, 0), incButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_scale=(1.3, 1.3, -1.3), incButton_pos=(0, 0, -0.5), incButton_image3_color=Vec4(1, 1, 1, 0.2), decButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_scale=(1.3, 1.3, 1.3), decButton_pos=(0, 0, 0.5), decButton_image3_color=Vec4(1, 1, 1, 0.2), itemFrame_pos=(0, 0, 0.39), itemFrame_scale=1.0, itemFrame_relief=DGG.SUNKEN, itemFrame_frameSize=(-0.55, + 0.55, + -0.85, + 0.06), itemFrame_frameColor=(0.85, 0.95, 1, 1), itemFrame_borderWidth=(0.01, 0.01), itemMakeFunction=self.makeMessageButton, itemMakeExtraArgs=[base.localAvatar.customMessages], numItemsVisible=NUM_ITEMS_SHOWN, items=msgStrings) + clipper = PlaneNode('clipper') + clipper.setPlane(Plane(Vec3(-1, 0, 0), Point3(0.55, 0, 0))) + clipNP = self.picker.attachNewNode(clipper) + self.picker.setClipPlane(clipNP) + gui.removeNode() + buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + upButton = buttonModels.find('**/InventoryButtonUp') + downButton = buttonModels.find('**/InventoryButtonDown') + rolloverButton = buttonModels.find('**/InventoryButtonRollover') + exitButton = DirectButton(parent=self.panel, relief=None, pos=(0, 0, -0.7), text=TTLocalizer.MessagePickerCancel, text_scale=TTLocalizer.CCIPexitButton, text_pos=(-0.005, -0.01), text_fg=Vec4(1, 1, 1, 1), textMayChange=0, image=(upButton, downButton, rolloverButton), image_scale=1.1, image_color=(0, 0.6, 1, 1), command=self.__handleCancel) + buttonModels.removeNode() + return + + def hide(self): + base.transitions.noTransitions() + self.panel.hide() + + def show(self): + base.transitions.fadeScreen(0.5) + self.panel.setBin('gui-popup', 0) + self.panel.show() + + def destroy(self): + base.transitions.noTransitions() + self.panel.destroy() + del self.panel + del self.picker + del self.doneCallback + if self.confirmDelete: + self.confirmDelete.cleanup() + del self.confirmDelete + self.confirmDelete = None + return + + def makeMessageButton(self, name, number, *extraArgs): + msg = extraArgs[0][0][number] + return DirectButton(relief=None, text=OTPLocalizer.CustomSCStrings[msg], text_pos=(-0.5, 0, 0), text_scale=0.05, text_align=TextNode.ALeft, text1_bg=Vec4(1, 1, 0, 1), text2_bg=Vec4(0.5, 0.9, 1, 1), text3_fg=Vec4(0.4, 0.8, 0.4, 1), command=self.__handleDelete, extraArgs=[msg]) + + def __handleDelete(self, msg): + self.confirmDelete = TTDialog.TTGlobalDialog(doneEvent='confirmDelete', message=TTLocalizer.MessageConfirmDelete % OTPLocalizer.CustomSCStrings[msg], style=TTDialog.TwoChoice) + self.confirmDelete.msg = msg + self.hide() + self.confirmDelete.show() + self.accept('confirmDelete', self.__handleDeleteConfirm) + + def __handleCancel(self): + self.doneCallback('cancel') + + def __handleDeleteConfirm(self): + status = self.confirmDelete.doneStatus + msg = self.confirmDelete.msg + self.ignore('confirmDelete') + self.confirmDelete.cleanup() + del self.confirmDelete + self.confirmDelete = None + if status == 'ok': + self.doneCallback('pick', base.localAvatar.customMessages.index(msg)) + else: + self.show() + return diff --git a/toontown/catalog/CatalogClothingItem.py b/toontown/catalog/CatalogClothingItem.py new file mode 100755 index 00000000..b1373508 --- /dev/null +++ b/toontown/catalog/CatalogClothingItem.py @@ -0,0 +1,581 @@ +import CatalogItem +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toon import ToonDNA +import random +from direct.showbase import PythonUtil +from direct.gui.DirectGui import * +from panda3d.core import * +CTArticle = 0 +CTString = 1 +CTBasePrice = 2 +CTEmblemPrices = 3 +ABoysShirt = 0 +AGirlsShirt = 1 +AShirt = 2 +ABoysShorts = 3 +AGirlsShorts = 4 +AGirlsSkirt = 5 +AShorts = 6 +ClothingTypes = {101: (ABoysShirt, 'bss1', 40), + 102: (ABoysShirt, 'bss2', 40), + 103: (ABoysShirt, 'bss3', 40), + 105: (ABoysShirt, 'bss4', 40), + 104: (ABoysShirt, 'bss5', 40), + 106: (ABoysShirt, 'bss6', 40), + 107: (ABoysShirt, 'bss7', 40), + 108: (ABoysShirt, 'bss8', 40), + 109: (ABoysShirt, 'bss9', 40), + 111: (ABoysShirt, 'bss11', 40), + 115: (ABoysShirt, 'bss15', 40), + 116: (ABoysShirt, 'c_ss1', 80), + 117: (ABoysShirt, 'c_ss2', 80), + 118: (ABoysShirt, 'c_bss1', 80), + 119: (ABoysShirt, 'c_bss2', 80), + 120: (ABoysShirt, 'c_ss3', 80), + 121: (ABoysShirt, 'c_bss3', 80), + 122: (ABoysShirt, 'c_bss4', 80), + 123: (ABoysShirt, 'c_ss4', 120), + 124: (ABoysShirt, 'c_ss5', 120), + 125: (AShirt, 'c_ss6', 120), + 126: (AShirt, 'c_ss7', 120), + 127: (AShirt, 'c_ss8', 120), + 128: (AShirt, 'c_ss9', 120), + 129: (AShirt, 'c_ss10', 120), + 130: (AShirt, 'c_ss11', 120), + 131: (ABoysShirt, 'c_ss12', 160), + 201: (AGirlsShirt, 'gss1', 40), + 202: (AGirlsShirt, 'gss2', 40), + 203: (AGirlsShirt, 'gss3', 40), + 205: (AGirlsShirt, 'gss4', 40), + 204: (AGirlsShirt, 'gss5', 40), + 206: (AGirlsShirt, 'gss6', 40), + 207: (AGirlsShirt, 'gss7', 40), + 208: (AGirlsShirt, 'gss8', 40), + 209: (AGirlsShirt, 'gss9', 40), + 211: (AGirlsShirt, 'gss11', 40), + 215: (AGirlsShirt, 'gss15', 40), + 216: (AGirlsShirt, 'c_ss1', 80), + 217: (AGirlsShirt, 'c_ss2', 80), + 218: (AGirlsShirt, 'c_gss1', 80), + 219: (AGirlsShirt, 'c_gss2', 80), + 220: (AGirlsShirt, 'c_ss3', 80), + 221: (AGirlsShirt, 'c_gss3', 80), + 222: (AGirlsShirt, 'c_gss4', 80), + 223: (AGirlsShirt, 'c_gss5', 80), + 224: (AGirlsShirt, 'c_ss4', 120), + 225: (AGirlsShirt, 'c_ss13', 160), + 301: (ABoysShorts, 'bbs1', 50), + 302: (ABoysShorts, 'bbs2', 50), + 303: (ABoysShorts, 'bbs3', 50), + 304: (ABoysShorts, 'bbs4', 50), + 305: (ABoysShorts, 'bbs5', 50), + 308: (ABoysShorts, 'bbs8', 50), + 310: (ABoysShorts, 'c_bs1', 120), + 311: (ABoysShorts, 'c_bs2', 120), + 312: (ABoysShorts, 'c_bs3', 120), + 313: (ABoysShorts, 'c_bs4', 120), + 314: (ABoysShorts, 'c_bs5', 160), + 401: (AGirlsSkirt, 'gsk1', 50), + 403: (AGirlsSkirt, 'gsk3', 50), + 404: (AGirlsSkirt, 'gsk4', 50), + 405: (AGirlsSkirt, 'gsk5', 50), + 407: (AGirlsSkirt, 'gsk7', 50), + 408: (AGirlsSkirt, 'c_gsk1', 100), + 409: (AGirlsSkirt, 'c_gsk2', 100), + 410: (AGirlsSkirt, 'c_gsk3', 100), + 411: (AGirlsSkirt, 'c_gsk4', 120), + 412: (AGirlsSkirt, 'c_gsk5', 120), + 413: (AGirlsSkirt, 'c_gsk6', 120), + 414: (AGirlsSkirt, 'c_gsk7', 160), + 451: (AGirlsShorts, 'gsh1', 50), + 452: (AGirlsShorts, 'gsh2', 50), + 453: (AGirlsShorts, 'gsh3', 50), + 1001: (AShirt, 'hw_ss1', 200), + 1002: (AShirt, 'hw_ss2', 200), + 1100: (AShirt, 'wh_ss1', 200), + 1101: (AShirt, 'wh_ss2', 200), + 1102: (AShirt, 'wh_ss3', 200), + 1103: (AShirt, 'wh_ss4', 200), + 1104: (ABoysShorts, 'wh_bs1', 200), + 1105: (ABoysShorts, 'wh_bs2', 200), + 1106: (ABoysShorts, 'wh_bs3', 200), + 1107: (ABoysShorts, 'wh_bs4', 200), + 1108: (AGirlsSkirt, 'wh_gsk1', 200), + 1109: (AGirlsSkirt, 'wh_gsk2', 200), + 1110: (AGirlsSkirt, 'wh_gsk3', 200), + 1111: (AGirlsSkirt, 'wh_gsk4', 200), + 1112: (AShirt, 'hw_ss5', 200), + 1113: (AShirt, 'hw_ss6', 300), + 1114: (AShirt, 'hw_ss7', 200), + 1115: (AShirt, 'hw_ss8', 200), + 1116: (AShirt, 'hw_ss9', 300), + 1117: (ABoysShorts, 'hw_bs1', 200), + 1118: (ABoysShorts, 'hw_bs2', 300), + 1119: (ABoysShorts, 'hw_bs5', 200), + 1120: (ABoysShorts, 'hw_bs6', 200), + 1121: (ABoysShorts, 'hw_bs7', 300), + 1122: (AGirlsShorts, 'hw_gs1', 200), + 1123: (AGirlsShorts, 'hw_gs2', 300), + 1124: (AGirlsShorts, 'hw_gs5', 200), + 1125: (AGirlsShorts, 'hw_gs6', 200), + 1126: (AGirlsShorts, 'hw_gs7', 300), + 1127: (AGirlsSkirt, 'hw_gsk1', 300), + 1200: (AGirlsShirt, 'vd_ss1', 200), + 1201: (AShirt, 'vd_ss2', 200), + 1202: (ABoysShirt, 'vd_ss3', 200), + 1203: (AGirlsShirt, 'vd_ss4', 200), + 1204: (AGirlsSkirt, 'vd_gs1', 200), + 1205: (ABoysShorts, 'vd_bs1', 200), + 1206: (AShirt, 'vd_ss5', 200), + 1207: (AShirt, 'vd_ss6', 200), + 1208: (ABoysShorts, 'vd_bs2', 200), + 1209: (ABoysShorts, 'vd_bs3', 200), + 1210: (AGirlsSkirt, 'vd_gs2', 200), + 1211: (AGirlsSkirt, 'vd_gs3', 200), + 1212: (AShirt, 'vd_ss7', 200), + 1300: (AShirt, 'sd_ss1', 200), + 1301: (AShirt, 'sd_ss2', 225), + 1302: (AGirlsShorts, 'sd_gs1', 200), + 1303: (ABoysShorts, 'sd_bs1', 200), + 1304: (AShirt, 'sd_ss3', 25), + 1305: (ABoysShorts, 'sd_bs2', 25), + 1306: (AGirlsSkirt, 'sd_gs2', 25), + 1400: (AShirt, 'tc_ss1', 200), + 1401: (AShirt, 'tc_ss2', 200), + 1402: (AShirt, 'tc_ss3', 200), + 1403: (AShirt, 'tc_ss4', 200), + 1404: (AShirt, 'tc_ss5', 200), + 1405: (AShirt, 'tc_ss6', 200), + 1406: (AShirt, 'tc_ss7', 200), + 1500: (AShirt, 'j4_ss1', 200), + 1501: (AShirt, 'j4_ss2', 200), + 1502: (ABoysShorts, 'j4_bs1', 200), + 1503: (AGirlsSkirt, 'j4_gs1', 200), + 1600: (AShirt, 'pj_ss1', 500), + 1601: (AShirt, 'pj_ss2', 500), + 1602: (AShirt, 'pj_ss3', 500), + 1603: (ABoysShorts, 'pj_bs1', 500), + 1604: (ABoysShorts, 'pj_bs2', 500), + 1605: (ABoysShorts, 'pj_bs3', 500), + 1606: (AGirlsShorts, 'pj_gs1', 500), + 1607: (AGirlsShorts, 'pj_gs2', 500), + 1608: (AGirlsShorts, 'pj_gs3', 500), + 1700: (AShirt, 'sa_ss1', 200), + 1701: (AShirt, 'sa_ss2', 200), + 1702: (AShirt, 'sa_ss3', 200), + 1703: (AShirt, 'sa_ss4', 200), + 1704: (AShirt, 'sa_ss5', 200), + 1705: (AShirt, 'sa_ss6', 200), + 1706: (AShirt, 'sa_ss7', 200), + 1707: (AShirt, 'sa_ss8', 200), + 1708: (AShirt, 'sa_ss9', 200), + 1709: (AShirt, 'sa_ss10', 200), + 1710: (AShirt, 'sa_ss11', 200), + 1711: (ABoysShorts, 'sa_bs1', 200), + 1712: (ABoysShorts, 'sa_bs2', 200), + 1713: (ABoysShorts, 'sa_bs3', 200), + 1714: (ABoysShorts, 'sa_bs4', 200), + 1715: (ABoysShorts, 'sa_bs5', 200), + 1716: (AGirlsSkirt, 'sa_gs1', 200), + 1717: (AGirlsSkirt, 'sa_gs2', 200), + 1718: (AGirlsSkirt, 'sa_gs3', 200), + 1719: (AGirlsSkirt, 'sa_gs4', 200), + 1720: (AGirlsSkirt, 'sa_gs5', 200), + 1721: (AShirt, 'sa_ss12', 200), + 1722: (AShirt, 'sa_ss13', 200), + 1723: (AShirt, 'sa_ss14', 250), + 1724: (AShirt, 'sa_ss15', 250), + 1725: (AShirt, 'sa_ss16', 200), + 1726: (AShirt, 'sa_ss17', 200), + 1727: (AShirt, 'sa_ss18', 200), + 1728: (AShirt, 'sa_ss19', 200), + 1729: (AShirt, 'sa_ss20', 200), + 1730: (AShirt, 'sa_ss21', 200), + 1731: (AShirt, 'sa_ss22', 200), + 1732: (AShirt, 'sa_ss23', 200), + 1733: (ABoysShorts, 'sa_bs6', 200), + 1734: (ABoysShorts, 'sa_bs7', 250), + 1735: (ABoysShorts, 'sa_bs8', 250), + 1736: (ABoysShorts, 'sa_bs9', 200), + 1737: (ABoysShorts, 'sa_bs10', 200), + 1738: (AGirlsSkirt, 'sa_gs6', 200), + 1739: (AGirlsSkirt, 'sa_gs7', 250), + 1740: (AGirlsSkirt, 'sa_gs8', 250), + 1741: (AGirlsSkirt, 'sa_gs9', 200), + 1742: (AGirlsSkirt, 'sa_gs10', 200), + 1743: (AShirt, 'sa_ss24', 250), + 1744: (AShirt, 'sa_ss25', 250), + 1745: (ABoysShorts, 'sa_bs11', 250), + 1746: (ABoysShorts, 'sa_bs12', 250), + 1747: (AGirlsSkirt, 'sa_gs11', 250), + 1748: (AGirlsSkirt, 'sa_gs12', 250), + 1749: (AShirt, 'sil_1', 1), + 1750: (AShirt, 'sil_2', 1), + 1751: (AShirt, 'sil_3', 1), + 1752: (AShirt, 'sil_4', 5000), + 1753: (AShirt, 'sil_5', 5000), + 1754: (AShirt, 'sil_6', 1), + 1755: (ABoysShorts, 'sil_bs1', 1), + 1756: (AGirlsShorts, 'sil_gs1', 1), + 1757: (AShirt, 'sil_7', 20), + 1758: (AShirt, 'sil_8', 20), + 1759: (AShirt, + 'emb_us1', + 0, + (20, 5)), + 1760: (AShirt, + 'emb_us2', + 234, + (0, 7)), + 1761: (AShirt, + 'emb_us3', + 345, + (8, 0)), + 1762: (AShirt, 'sa_ss26', 5000), + 1763: (AShirt, 'sb_1', 20), + 1764: (AShirt, 'sa_ss27', 5000), + 1765: (AShirt, 'sa_ss28', 5000), + 1766: (ABoysShorts, 'sa_bs13', 5000), + 1767: (AGirlsShorts, 'sa_gs13', 5000), + 1768: (AShirt, 'jb_1', 20), + 1769: (AShirt, 'jb_2', 20), + 1770: (AShirt, 'hw_ss3', 250), + 1771: (AShirt, 'hw_ss4', 250), + 1772: (ABoysShorts, 'hw_bs3', 250), + 1773: (AGirlsShorts, 'hw_gs3', 250), + 1774: (ABoysShorts, 'hw_bs4', 250), + 1775: (AGirlsShorts, 'hw_gs4', 250), + 1776: (AShirt, 'ugcms', 15000), + 1777: (AShirt, 'lb_1', 20), + 1778: (AShirt, 'sa_ss29', 5000), + 1779: (AShirt, 'sa_ss30', 5000), + 1780: (ABoysShorts, 'sa_bs14', 5000), + 1781: (AGirlsShorts, 'sa_gs14', 5000), + 1782: (AShirt, 'sa_ss31', 5000), + 1783: (ABoysShorts, 'sa_bs15', 5000), + 1784: (AGirlsSkirt, 'sa_gs15', 5000), + 1785: (AShirt, 'sa_ss32', 5000), + 1786: (AShirt, 'sa_ss33', 5000), + 1787: (AShirt, 'sa_ss34', 5000), + 1788: (AShirt, 'sa_ss35', 5000), + 1789: (AShirt, 'sa_ss36', 5000), + 1790: (AShirt, 'sa_ss37', 5000), + 1791: (ABoysShorts, 'sa_bs16', 5000), + 1792: (ABoysShorts, 'sa_bs17', 5000), + 1793: (AGirlsSkirt, 'sa_gs16', 5000), + 1794: (AGirlsSkirt, 'sa_gs17', 5000), + 1795: (AShirt, 'sa_ss38', 5000), + 1796: (AShirt, 'sa_ss39', 5000), + 1797: (ABoysShorts, 'sa_bs18', 5000), + 1798: (AGirlsSkirt, 'sa_gs18', 5000), + 1799: (AShirt, 'sa_ss40', 5000), + 1800: (AShirt, 'sa_ss41', 5000), + 1801: (AShirt, 'sa_ss42', 250), + 1802: (AGirlsShirt, 'sa_ss43', 250), + 1803: (AShirt, 'sa_ss44', 5000), + 1804: (AShirt, 'sa_ss45', 5000), + 1805: (AShirt, 'sa_ss46', 5000), + 1806: (AShirt, 'sa_ss47', 5000), + 1807: (AShirt, 'sa_ss48', 5000), + 1808: (AShirt, 'sa_ss49', 5000), + 1809: (AShirt, 'sa_ss50', 5000), + 1810: (AShirt, 'sa_ss51', 5000), + 1811: (AShirt, 'sa_ss52', 5000), + 1812: (AShirt, 'sa_ss53', 5000), + 1813: (AShirt, 'sa_ss54', 5000), + 1814: (ABoysShorts, 'sa_bs19', 5000), + 1815: (ABoysShorts, 'sa_bs20', 5000), + 1816: (ABoysShorts, 'sa_bs21', 5000), + 1817: (AGirlsSkirt, 'sa_gs19', 5000), + 1818: (AGirlsSkirt, 'sa_gs20', 5000), + 1819: (AGirlsSkirt, 'sa_gs21', 5000), + 1820: (AShirt, 'sa_ss55', 5000), + 1821: (AShirt, 'flannel', 300)} + +class CatalogClothingItem(CatalogItem.CatalogItem): + + def makeNewItem(self, clothingType, colorIndex, isSpecial = False): + self.clothingType = clothingType + self.colorIndex = colorIndex + self.isSpecial = isSpecial + CatalogItem.CatalogItem.makeNewItem(self) + + def storedInCloset(self): + return 1 + + def notOfferedTo(self, avatar): + article = ClothingTypes[self.clothingType][CTArticle] + if article == AShirt or article == AShorts: + return 0 + forBoys = (article == ABoysShirt or article == ABoysShorts) + if avatar.getStyle().getGender() == 'm': + return not forBoys + else: + return forBoys + + def forBoysOnly(self): + article = ClothingTypes[self.clothingType][CTArticle] + if article == ABoysShirt or article == ABoysShorts: + return 1 + else: + return 0 + + def forGirlsOnly(self): + article = ClothingTypes[self.clothingType][CTArticle] + if article == AGirlsShirt or article == AGirlsSkirt or article == AGirlsShorts: + return 1 + else: + return 0 + + def getPurchaseLimit(self): + return 1 + + def reachedPurchaseLimit(self, avatar): + if avatar.onOrder.count(self) != 0: + return 1 + if avatar.onGiftOrder.count(self) != 0: + return 1 + if avatar.mailboxContents.count(self) != 0: + return 1 + str = ClothingTypes[self.clothingType][CTString] + dna = avatar.getStyle() + if self.isShirt(): + defn = ToonDNA.ShirtStyles[str] + if dna.topTex == defn[0] and dna.topTexColor == defn[2][self.colorIndex][0] and dna.sleeveTex == defn[1] and dna.sleeveTexColor == defn[2][self.colorIndex][1]: + return 1 + l = avatar.clothesTopsList + for i in xrange(0, len(l), 4): + if l[i] == defn[0] and l[i + 1] == defn[2][self.colorIndex][0] and l[i + 2] == defn[1] and l[i + 3] == defn[2][self.colorIndex][1]: + return 1 + + else: + defn = ToonDNA.BottomStyles[str] + if dna.botTex == defn[0] and dna.botTexColor == defn[1][self.colorIndex]: + return 1 + l = avatar.clothesBottomsList + for i in xrange(0, len(l), 2): + if l[i] == defn[0] and l[i + 1] == defn[1][self.colorIndex]: + return 1 + + return 0 + + def getTypeName(self): + return TTLocalizer.ClothingTypeName + + def getName(self): + typeName = TTLocalizer.ClothingTypeNames.get(self.clothingType, 0) + if typeName: + return typeName + else: + article = ClothingTypes[self.clothingType][CTArticle] + return TTLocalizer.ClothingArticleNames[article] + + def recordPurchase(self, avatar, optional): + if avatar.isClosetFull(1): + return ToontownGlobals.P_NoRoomForItem + str = ClothingTypes[self.clothingType][CTString] + dna = avatar.getStyle() + if self.isShirt(): + added = avatar.addToClothesTopsList(dna.topTex, dna.topTexColor, dna.sleeveTex, dna.sleeveTexColor) + if added: + avatar.b_setClothesTopsList(avatar.getClothesTopsList()) + self.notify.info('Avatar %s put shirt %d,%d,%d,%d in closet.' % (avatar.doId, + dna.topTex, + dna.topTexColor, + dna.sleeveTex, + dna.sleeveTexColor)) + else: + self.notify.warning('Avatar %s %s lost current shirt; closet full.' % (avatar.doId, dna.asTuple())) + defn = ToonDNA.ShirtStyles[str] + dna.topTex = defn[0] + dna.topTexColor = defn[2][self.colorIndex][0] + dna.sleeveTex = defn[1] + dna.sleeveTexColor = defn[2][self.colorIndex][1] + else: + added = avatar.addToClothesBottomsList(dna.botTex, dna.botTexColor) + if added: + avatar.b_setClothesBottomsList(avatar.getClothesBottomsList()) + self.notify.info('Avatar %s put bottoms %d,%d in closet.' % (avatar.doId, dna.botTex, dna.botTexColor)) + else: + self.notify.warning('Avatar %s %s lost current bottoms; closet full.' % (avatar.doId, dna.asTuple())) + defn = ToonDNA.BottomStyles[str] + dna.botTex = defn[0] + dna.botTexColor = defn[1][self.colorIndex] + if dna.getGender() == 'f': + try: + bottomPair = ToonDNA.GirlBottoms[dna.botTex] + except: + bottomPair = ToonDNA.GirlBottoms[0] + + if dna.torso[1] == 's' and bottomPair[1] == ToonDNA.SKIRT: + dna.torso = dna.torso[0] + 'd' + elif dna.torso[1] == 'd' and bottomPair[1] == ToonDNA.SHORTS: + dna.torso = dna.torso[0] + 's' + avatar.b_setDNAString(dna.makeNetString()) + avatar.d_catalogGenClothes() + return ToontownGlobals.P_ItemAvailable + + def getDeliveryTime(self): + return 60 + + def getPicture(self, avatar): + from toontown.toon import Toon + self.hasPicture = True + dna = ToonDNA.ToonDNA(type='t', dna=avatar.style) + str = ClothingTypes[self.clothingType][CTString] + if self.isShirt(): + defn = ToonDNA.ShirtStyles[str] + dna.topTex = defn[0] + dna.topTexColor = defn[2][self.colorIndex][0] + dna.sleeveTex = defn[1] + dna.sleeveTexColor = defn[2][self.colorIndex][1] + pieceNames = ('**/1000/**/torso-top', '**/1000/**/sleeves') + else: + defn = ToonDNA.BottomStyles[str] + dna.botTex = defn[0] + dna.botTexColor = defn[1][self.colorIndex] + pieceNames = ('**/1000/**/torso-bot',) + toon = Toon.Toon() + toon.setDNA(dna) + model = NodePath('clothing') + for name in pieceNames: + for piece in toon.findAllMatches(name): + piece.wrtReparentTo(model) + + model.setH(180) + toon.delete() + return self.makeFrameModel(model) + + def requestPurchase(self, phone, callback): + from toontown.toontowngui import TTDialog + avatar = base.localAvatar + clothesOnOrder = 0 + for item in avatar.onOrder + avatar.mailboxContents: + if item.storedInCloset(): + clothesOnOrder += 1 + + if avatar.isClosetFull(clothesOnOrder): + self.requestPurchaseCleanup() + buttonCallback = PythonUtil.Functor(self.__handleFullPurchaseDialog, phone, callback) + self.dialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.CatalogPurchaseClosetFull, text_wordwrap=15, command=buttonCallback) + self.dialog.show() + else: + CatalogItem.CatalogItem.requestPurchase(self, phone, callback) + + def requestPurchaseCleanup(self): + if hasattr(self, 'dialog'): + self.dialog.cleanup() + del self.dialog + + def __handleFullPurchaseDialog(self, phone, callback, buttonValue): + from toontown.toontowngui import TTDialog + self.requestPurchaseCleanup() + if buttonValue == DGG.DIALOG_OK: + CatalogItem.CatalogItem.requestPurchase(self, phone, callback) + else: + callback(ToontownGlobals.P_UserCancelled, self) + + def getAcceptItemErrorText(self, retcode): + if retcode == ToontownGlobals.P_ItemAvailable: + if self.isShirt(): + return TTLocalizer.CatalogAcceptShirt + elif self.isSkirt(): + return TTLocalizer.CatalogAcceptSkirt + else: + return TTLocalizer.CatalogAcceptShorts + elif retcode == ToontownGlobals.P_NoRoomForItem: + return TTLocalizer.CatalogAcceptClosetFull + return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode) + + def getColorChoices(self): + str = ClothingTypes[self.clothingType][CTString] + if self.isShirt(): + return ToonDNA.ShirtStyles[str][2] + else: + return ToonDNA.BottomStyles[str][1] + + def isShirt(self): + article = ClothingTypes[self.clothingType][CTArticle] + return article < ABoysShorts + + def isSkirt(self): + article = ClothingTypes[self.clothingType][CTArticle] + return article == AGirlsSkirt + + def output(self, store = -1): + return 'CatalogClothingItem(%s, %s%s)' % (self.clothingType, self.colorIndex, self.formatOptionalData(store)) + + def getFilename(self): + str = ClothingTypes[self.clothingType][CTString] + if self.isShirt(): + defn = ToonDNA.ShirtStyles[str] + topTex = defn[0] + return ToonDNA.Shirts[topTex] + else: + defn = ToonDNA.BottomStyles[str] + botTex = defn[0] + article = ClothingTypes[self.clothingType][CTArticle] + if article == ABoysShorts: + return ToonDNA.BoyShorts[botTex] + else: + return ToonDNA.GirlBottoms[botTex][0] + + def getColor(self): + str = ClothingTypes[self.clothingType][CTString] + if self.isShirt(): + defn = ToonDNA.ShirtStyles[str] + topTexColor = defn[2][self.colorIndex][0] + return ToonDNA.ClothesColors[topTexColor] + else: + defn = ToonDNA.BottomStyles[str] + botTexColor = defn[1][self.colorIndex] + return ToonDNA.ClothesColors[botTexColor] + + def compareTo(self, other): + if self.clothingType != other.clothingType: + return self.clothingType - other.clothingType + return self.colorIndex - other.colorIndex + + def getHashContents(self): + return (self.clothingType, self.colorIndex) + + def getBasePrice(self): + return ClothingTypes[self.clothingType][CTBasePrice] + + def getEmblemPrices(self): + result = () + info = ClothingTypes[self.clothingType] + if CTEmblemPrices <= len(info) - 1: + result = info[CTEmblemPrices] + return result + + def decodeDatagram(self, di, versionNumber, store): + CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store) + self.clothingType = di.getUint16() + self.colorIndex = di.getUint8() + self.isSpecial = di.getBool() + str = ClothingTypes[self.clothingType][CTString] + if self.isShirt(): + color = ToonDNA.ShirtStyles[str][2][self.colorIndex] + else: + color = ToonDNA.BottomStyles[str][1][self.colorIndex] + + def encodeDatagram(self, dg, store): + CatalogItem.CatalogItem.encodeDatagram(self, dg, store) + dg.addUint16(self.clothingType) + dg.addUint8(self.colorIndex) + dg.addBool(self.isSpecial) + + def isGift(self): + return not self.getEmblemPrices() + +def getAllClothes(*clothingTypes): + list = [] + for clothingType in clothingTypes: + base = CatalogClothingItem(clothingType, 0) + list.append(base) + for n in xrange(1, len(base.getColorChoices())): + list.append(CatalogClothingItem(clothingType, n)) + + return list diff --git a/toontown/catalog/CatalogEmoteItem.py b/toontown/catalog/CatalogEmoteItem.py new file mode 100755 index 00000000..2849a58c --- /dev/null +++ b/toontown/catalog/CatalogEmoteItem.py @@ -0,0 +1,127 @@ +import CatalogItem +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +from direct.interval.IntervalGlobal import * + +class CatalogEmoteItem(CatalogItem.CatalogItem): + sequenceNumber = 0 + pictureToon = None + + def makeNewItem(self, emoteIndex, isSpecial = False): + self.emoteIndex = emoteIndex + self.isSpecial = isSpecial + CatalogItem.CatalogItem.makeNewItem(self) + + def getPurchaseLimit(self): + return 1 + + def reachedPurchaseLimit(self, avatar): + if self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder: + return 1 + if self.emoteIndex >= len(avatar.emoteAccess): + return 0 + return avatar.emoteAccess[self.emoteIndex] != 0 + + def getAcceptItemErrorText(self, retcode): + if retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.CatalogAcceptEmote + return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode) + + def saveHistory(self): + return 1 + + def getTypeName(self): + return TTLocalizer.EmoteTypeName + + def getName(self): + return OTPLocalizer.EmoteList[self.emoteIndex] + + def recordPurchase(self, avatar, optional): + if self.emoteIndex < 0 or self.emoteIndex > len(avatar.emoteAccess): + self.notify.warning('Invalid emote access: %s for avatar %s' % (self.emoteIndex, avatar.doId)) + return ToontownGlobals.P_InvalidIndex + avatar.emoteAccess[self.emoteIndex] = 1 + avatar.d_setEmoteAccess(avatar.emoteAccess) + return ToontownGlobals.P_ItemAvailable + + def getPicture(self, avatar): + from toontown.toon import Toon + from toontown.toon import ToonHead + from toontown.toon import TTEmote + from otp.avatar import Emote + self.hasPicture = True + if self.emoteIndex in Emote.globalEmote.getHeadEmotes(): + toon = ToonHead.ToonHead() + toon.setupHead(avatar.style, forGui=1) + else: + toon = Toon.Toon() + toon.setDNA(avatar.style) + toon.loop('neutral') + toon.setH(180) + model, ival = self.makeFrameModel(toon, 0) + track, duration = Emote.globalEmote.doEmote(toon, self.emoteIndex, volume=self.volume) + if duration == None: + duration = 0 + name = 'emote-item-%s' % self.sequenceNumber + CatalogEmoteItem.sequenceNumber += 1 + if track != None: + track = Sequence(Sequence(track, duration=0), Wait(duration + 2), name=name) + else: + track = Sequence(Func(Emote.globalEmote.doEmote, toon, self.emoteIndex), Wait(duration + 4), name=name) + self.pictureToon = toon + return (model, track) + + def changeIval(self, volume): + from toontown.toon import Toon + from toontown.toon import ToonHead + from toontown.toon import TTEmote + from otp.avatar import Emote + self.volume = volume + if not hasattr(self, 'pictureToon'): + return Sequence() + track, duration = Emote.globalEmote.doEmote(self.pictureToon, self.emoteIndex, volume=self.volume) + if duration == None: + duration = 0 + name = 'emote-item-%s' % self.sequenceNumber + CatalogEmoteItem.sequenceNumber += 1 + if track != None: + track = Sequence(Sequence(track, duration=0), Wait(duration + 2), name=name) + else: + track = Sequence(Func(Emote.globalEmote.doEmote, toon, self.emoteIndex), Wait(duration + 4), name=name) + return track + + def cleanupPicture(self): + CatalogItem.CatalogItem.cleanupPicture(self) + self.pictureToon.emote.finish() + self.pictureToon.emote = None + self.pictureToon.delete() + self.pictureToon = None + return + + def output(self, store = -1): + return 'CatalogEmoteItem(%s%s)' % (self.emoteIndex, self.formatOptionalData(store)) + + def compareTo(self, other): + return self.emoteIndex - other.emoteIndex + + def getHashContents(self): + return self.emoteIndex + + def getBasePrice(self): + return 550 + + def decodeDatagram(self, di, versionNumber, store): + CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store) + self.emoteIndex = di.getUint8() + self.isSpecial = di.getBool() + if self.emoteIndex > len(OTPLocalizer.EmoteList): + raise ValueError + + def encodeDatagram(self, dg, store): + CatalogItem.CatalogItem.encodeDatagram(self, dg, store) + dg.addUint8(self.emoteIndex) + dg.addBool(self.isSpecial) + + def isGift(self): + return not self.getEmblemPrices() diff --git a/toontown/catalog/CatalogFlooringItem.py b/toontown/catalog/CatalogFlooringItem.py new file mode 100755 index 00000000..795388bc --- /dev/null +++ b/toontown/catalog/CatalogFlooringItem.py @@ -0,0 +1,169 @@ +from CatalogSurfaceItem import * +FTTextureName = 0 +FTColor = 1 +FTBasePrice = 2 +FlooringTypes = {1000: ('phase_5.5/maps/floor_wood_neutral.jpg', CTBasicWoodColorOnWhite, 150), + 1010: ('phase_5.5/maps/flooring_carpetA_neutral.jpg', CTFlatColorDark, 150), + 1020: ('phase_4/maps/flooring_tile_neutral.jpg', CTFlatColorDark, 150), + 1030: ('phase_5.5/maps/flooring_tileB2.jpg', None, 150), + 1040: ('phase_4/maps/grass.jpg', None, 150), + 1050: ('phase_4/maps/floor_tile_brick_diagonal2.jpg', None, 150), + 1060: ('phase_4/maps/floor_tile_brick_diagonal.jpg', None, 150), + 1070: ('phase_4/maps/plazz_tile.jpg', None, 150), + 1080: ('phase_4/maps/sidewalk.jpg', CTFlatColorDark, 150), + 1090: ('phase_3.5/maps/boardwalk_floor.jpg', None, 150), + 1100: ('phase_3.5/maps/dustroad.jpg', None, 150), + 1110: ('phase_5.5/maps/floor_woodtile_neutral.jpg', CTBasicWoodColorOnWhite, 150), + 1120: ('phase_5.5/maps/floor_tile_neutral.jpg', CTBasicWoodColorOnWhite + CTFlatColorDark, 150), + 1130: ('phase_5.5/maps/floor_tile_honeycomb_neutral.jpg', CTBasicWoodColorOnWhite, 150), + 1140: ('phase_5.5/maps/UWwaterFloor1.jpg', None, 150), + 1150: ('phase_5.5/maps/UWtileFloor4.jpg', None, 150), + 1160: ('phase_5.5/maps/UWtileFloor3.jpg', None, 150), + 1170: ('phase_5.5/maps/UWtileFloor2.jpg', None, 150), + 1180: ('phase_5.5/maps/UWtileFloor1.jpg', None, 150), + 1190: ('phase_5.5/maps/UWsandyFloor1.jpg', None, 150), + 10000: ('phase_5.5/maps/floor_icecube.jpg', CTWhite, 225), + 10010: ('phase_5.5/maps/floor_snow.jpg', CTWhite, 225), + 11000: ('phase_5.5/maps/StPatsFloor1.jpg', CTWhite, 225), + 11010: ('phase_5.5/maps/StPatsFloor2.jpg', CTWhite, 225)} + +class CatalogFlooringItem(CatalogSurfaceItem): + + def makeNewItem(self, patternIndex, colorIndex = None): + self.patternIndex = patternIndex + self.colorIndex = colorIndex + CatalogSurfaceItem.makeNewItem(self) + + def needsCustomize(self): + return self.colorIndex == None + + def getTypeName(self): + return TTLocalizer.SurfaceNames[STFlooring] + + def getName(self): + name = TTLocalizer.FlooringNames.get(self.patternIndex) + if name: + return name + return self.getTypeName() + + def getSurfaceType(self): + return STFlooring + + def getPicture(self, avatar): + frame = self.makeFrame() + sample = loader.loadModel('phase_5.5/models/estate/wallpaper_sample') + a = sample.find('**/a') + b = sample.find('**/b') + c = sample.find('**/c') + a.setTexture(self.loadTexture(), 1) + a.setColorScale(*self.getColor()) + b.setTexture(self.loadTexture(), 1) + b.setColorScale(*self.getColor()) + c.setTexture(self.loadTexture(), 1) + c.setColorScale(*self.getColor()) + sample.reparentTo(frame) + self.hasPicture = True + return (frame, None) + + def output(self, store = -1): + return 'CatalogFlooringItem(%s, %s%s)' % (self.patternIndex, self.colorIndex, self.formatOptionalData(store)) + + def getFilename(self): + return FlooringTypes[self.patternIndex][FTTextureName] + + def compareTo(self, other): + if self.patternIndex != other.patternIndex: + return self.patternIndex - other.patternIndex + return 0 + + def getHashContents(self): + return self.patternIndex + + def getBasePrice(self): + return FlooringTypes[self.patternIndex][FTBasePrice] + + def loadTexture(self): + from pandac.PandaModules import Texture + filename = FlooringTypes[self.patternIndex][FTTextureName] + texture = loader.loadTexture(filename) + texture.setMinfilter(Texture.FTLinearMipmapLinear) + texture.setMagfilter(Texture.FTLinear) + return texture + + def getColor(self): + if self.colorIndex == None: + colorIndex = 0 + else: + colorIndex = self.colorIndex + colors = FlooringTypes[self.patternIndex][FTColor] + if colors: + if colorIndex < len(colors): + return colors[colorIndex] + else: + print 'Warning: colorIndex not in colors. Returning white.' + return CT_WHITE + else: + return CT_WHITE + return + + def decodeDatagram(self, di, versionNumber, store): + CatalogAtticItem.CatalogAtticItem.decodeDatagram(self, di, versionNumber, store) + self.patternIndex = di.getUint16() + if store & CatalogItem.Customization: + self.colorIndex = di.getUint8() + else: + self.colorIndex = 0 + wtype = FlooringTypes[self.patternIndex] + return + + def encodeDatagram(self, dg, store): + CatalogAtticItem.CatalogAtticItem.encodeDatagram(self, dg, store) + dg.addUint16(self.patternIndex) + if store & CatalogItem.Customization: + dg.addUint8(self.colorIndex) + + +def getFloorings(*indexList): + list = [] + for index in indexList: + list.append(CatalogFlooringItem(index)) + + return list + + +def getAllFloorings(*indexList): + list = [] + for index in indexList: + colors = FlooringTypes[index][FTColor] + if colors: + for n in xrange(len(colors)): + list.append(CatalogFlooringItem(index, n)) + + else: + list.append(CatalogFlooringItem(index, 0)) + + return list + + +def getFlooringRange(fromIndex, toIndex, *otherRanges): + list = [] + froms = [fromIndex] + tos = [toIndex] + i = 0 + while i < len(otherRanges): + froms.append(otherRanges[i]) + tos.append(otherRanges[i + 1]) + i += 2 + + for patternIndex in FlooringTypes.keys(): + for fromIndex, toIndex in zip(froms, tos): + if patternIndex >= fromIndex and patternIndex <= toIndex: + colors = FlooringTypes[patternIndex][FTColor] + if colors: + for n in xrange(len(colors)): + list.append(CatalogFlooringItem(patternIndex, n)) + + else: + list.append(CatalogFlooringItem(patternIndex, 0)) + + return list diff --git a/toontown/catalog/CatalogFurnitureItem.py b/toontown/catalog/CatalogFurnitureItem.py new file mode 100755 index 00000000..adb8a9e2 --- /dev/null +++ b/toontown/catalog/CatalogFurnitureItem.py @@ -0,0 +1,1215 @@ +from direct.interval.IntervalGlobal import * +import CatalogAtticItem +import CatalogItem +import random, glob +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from panda3d.core import * +FTModelName = 0 +FTColor = 1 +FTColorOptions = 2 +FTBasePrice = 3 +FTFlags = 4 +FTScale = 5 +FLBank = 1 +FLCloset = 2 +FLRug = 4 +FLPainting = 8 +FLOnTable = 16 +FLIsTable = 32 +FLBillboard = 64 +FLPhone = 128 +FLCrate = 256 +FLChair = 512 +FLTV = 1024 +FLTrunk = 2048 +FLBoysOnly = 4096 +FLGirlsOnly = 8192 +furnitureColors = [ + (0.792, 0.353, 0.29, 1.0), + (0.176, 0.592, 0.439, 1.0), + (0.439, 0.424, 0.682, 1.0), + (0.325, 0.58, 0.835, 1.0), + (0.753, 0.345, 0.557, 1.0), + (0.992, 0.843, 0.392, 1.0) +] +woodColors = [ + (0.933, 0.773, 0.569, 1.0), + (0.9333, 0.6785, 0.055, 1.0), + (0.545, 0.451, 0.333, 1.0), + (0.541, 0.0, 0.0, 1.0), + (0.5451, 0.2706, 0.0745, 1.0), + (0.5451, 0.4118, 0.4118, 1.0) +] +BankToMoney = { + 1300: 10000, + 1310: 15000, + 1320: 20000, + 1330: 25000, + 1340: 30000 +} +MoneyToBank = {} +for bankId, maxMoney in BankToMoney.items(): + MoneyToBank[maxMoney] = bankId + +MaxBankId = 1340 +ClosetToClothes = { + 500: 10, + 502: 15, + 504: 20, + 506: 25, + 508: 50, + 510: 10, + 512: 15, + 514: 20, + 516: 25, + 518: 50 +} +ClothesToCloset = {} +for closetId, maxClothes in ClosetToClothes.items(): + if not maxClothes in ClothesToCloset: + ClothesToCloset[maxClothes] = (closetId,) + else: + ClothesToCloset[maxClothes] += (closetId,) + +MaxClosetIds = (508, 518) + +TvToPosScale = { + 1530: ((-1.15, -0.5, 1.1), (2.5, 1.7, 1.4)), + 1531: ((-2.3, -0.2, 2.522), (5, 3.75, 3.187)), + 1532: ((-7, -0.2, 2.8), (15, 10, 7.8)) +} + +ChairToPosHpr = { + 100: ((0, -3.9, 0.88), (180, 0, 0), (0, -4.9, 0), -3.0), + 105: ((0, -3.9, 0.88), (180, 0, 0), (0, -4.9, 0), -3.0), + 110: ((0, -1.6, 0.5), (180, 0, 0), (0, -2.6, 0), 0.0), + 120: ((0, -1.6, 0.5), (180, 0, 0), (0, -2.6, 0), 0.0), + 130: ((0, -2.8, 0.5), (180, 0, 0), (0, -3.8, 0), -2.0), + 140: ((0, -1.6, 0.5), (180, 0, 0), (0, -2.6, 0), 0.0), + 145: ((0, -2.1, 0.2), (180, 0, 0), (0, -3.1, 0), 0.0), + 160: ((-1.7, 0, 0.9), (90, 0, 0), (-2.7, 0, 0), 0.0), + 170: ((0, 1.8, 0.4), (0, 0, 0), (0, 2.8, 0), 0.0), + 700: ((0, -1.2, 0.5), (180, 0, 0), (0, -2.2, 0), 0.0), + 705: ((0, -1.2, 0.5), (180, 0, 0), (0, -2.2, 0), 0.0), + 710: ((0, -1.1, 0.4), (180, 0, 0), (0, -2.1, 0), 0.0), + 715: ((0, -1.1, 0.4), (180, 0, 0), (0, -2.1, 0), 0.0), + 720: ((0, -2.7, 0.2), (180, 0, 0), (0, -3.7, 0), -3.0) +} + +FurnitureTypes = { + 100: ('phase_5.5/models/estate/chairA', # Model + None, # Color + None, # Color Options + 80, # Base Price + FLChair), # Flags + # Scale + 105: ('phase_5.5/models/estate/chairAdesat', + None, + {0: (('**/cushion*', furnitureColors[0]), ('**/arm*', furnitureColors[0])), + 1: (('**/cushion*', furnitureColors[1]), ('**/arm*', furnitureColors[1])), + 2: (('**/cushion*', furnitureColors[2]), ('**/arm*', furnitureColors[2])), + 3: (('**/cushion*', furnitureColors[3]), ('**/arm*', furnitureColors[3])), + 4: (('**/cushion*', furnitureColors[4]), ('**/arm*', furnitureColors[4])), + 5: (('**/cushion*', furnitureColors[5]), ('**/arm*', furnitureColors[5]))}, + 160, + FLChair), + 110: ('phase_3.5/models/modules/chair', + None, + None, + 40, + FLChair), + 120: ('phase_5.5/models/estate/deskChair', + None, + None, + 60, + FLChair), + 130: ('phase_5.5/models/estate/BugRoomChair', + None, + None, + 160, + FLChair), + 140: ('phase_5.5/models/estate/UWlobsterChair', + None, + None, + 200, + FLChair), + 145: ('phase_5.5/models/estate/UWlifeSaverChair', + None, + None, + 200, + FLChair), + 150: ('phase_5.5/models/estate/West_saddleStool2', + None, + None, + 160), + 160: ('phase_5.5/models/estate/West_nativeChair', + None, + None, + 160, + FLChair), + 170: ('phase_5.5/models/estate/cupcakeChair', + None, + None, + 240, + FLChair), + 200: ('phase_5.5/models/estate/regular_bed', + None, + None, + 400), + 205: ('phase_5.5/models/estate/regular_bed_desat', + None, + {0: (('**/bar*', woodColors[0]), + ('**/post*', woodColors[0]), + ('**/*support', woodColors[0]), + ('**/top', woodColors[0]), + ('**/bottom', woodColors[0]), + ('**/pPlane*', woodColors[0])), + 1: (('**/bar*', woodColors[1]), + ('**/post*', woodColors[1]), + ('**/*support', woodColors[1]), + ('**/top', woodColors[1]), + ('**/bottom', woodColors[1]), + ('**/pPlane*', woodColors[1])), + 2: (('**/bar*', woodColors[2]), + ('**/post*', woodColors[2]), + ('**/*support', woodColors[2]), + ('**/top', woodColors[2]), + ('**/bottom', woodColors[2]), + ('**/pPlane*', woodColors[2])), + 3: (('**/bar*', woodColors[3]), + ('**/post*', woodColors[3]), + ('**/*support', woodColors[3]), + ('**/top', woodColors[3]), + ('**/bottom', woodColors[3]), + ('**/pPlane*', woodColors[3])), + 4: (('**/bar*', woodColors[4]), + ('**/post*', woodColors[4]), + ('**/*support', woodColors[4]), + ('**/top', woodColors[4]), + ('**/bottom', woodColors[4]), + ('**/pPlane*', woodColors[4])), + 5: (('**/bar*', woodColors[5]), + ('**/post*', woodColors[5]), + ('**/*support', woodColors[5]), + ('**/top', woodColors[5]), + ('**/bottom', woodColors[5]), + ('**/pPlane*', woodColors[5]))}, + 800), + 210: ('phase_5.5/models/estate/girly_bed', + None, + None, + 450, + FLGirlsOnly), + 220: ('phase_5.5/models/estate/bathtub_bed', + None, + None, + 550), + 230: ('phase_5.5/models/estate/bugRoomBed', + None, + None, + 600), + 240: ('phase_5.5/models/estate/UWBoatBed', + None, + None, + 600), + 250: ('phase_5.5/models/estate/West_cactusHammoc', + None, + None, + 550), + 260: ('phase_5.5/models/estate/icecreamBed', + None, + None, + 700), + 270: ('phase_5.5/models/estate/trolley_bed', + None, + None, + 1200, + None, + None, + 0.25), + 300: ('phase_5.5/models/estate/Piano', + None, + None, + 1000, + FLIsTable), + 310: ('phase_5.5/models/estate/Organ', + None, + None, + 2500), + 400: ('phase_5.5/models/estate/FireplaceSq', + None, + None, + 800), + 410: ('phase_5.5/models/estate/FireplaceGirlee', + None, + None, + 800, + FLGirlsOnly), + 420: ('phase_5.5/models/estate/FireplaceRound', + None, + None, + 800), + 430: ('phase_5.5/models/estate/bugRoomFireplace', + None, + None, + 800), + 440: ('phase_5.5/models/estate/CarmelAppleFireplace', + None, + None, + 800), + 450: ('phase_5.5/models/estate/fireplace_coral', + None, + None, + 950), + 460: ('phase_5.5/models/estate/tt_m_prp_int_fireplace_coral', + None, + None, + 1250, + None, + None, + 0.5), + 470: ('phase_5.5/models/estate/tt_m_prp_int_fireplace_square', + None, + None, + 1100, + None, + None, + 0.5), + 480: ('phase_5.5/models/estate/tt_m_prp_int_fireplace_round', + None, + None, + 1100, + None, + None, + 0.5), + 490: ('phase_5.5/models/estate/tt_m_prp_int_fireplace_girlee', + None, + None, + 1100, + FLGirlsOnly, + None, + 0.5), + 491: ('phase_5.5/models/estate/tt_m_prp_int_fireplace_bugRoom', + None, + None, + 1100, + None, + None, + 0.5), + 492: ('phase_5.5/models/estate/tt_m_prp_int_fireplace_caramelApple', + None, + None, + 1100, + None, + None, + 0.5), + 500: ('phase_5.5/models/estate/closetBoy', + None, + None, + 500, + FLCloset, + 0.85), + 502: ('phase_5.5/models/estate/closetBoy', + None, + None, + 500, + FLCloset, + 1.0), + 504: ('phase_5.5/models/estate/closetBoy', + None, + None, + 500, + FLCloset, + 1.15), + 506: ('phase_5.5/models/estate/closetBoy', + None, + None, + 500, + FLCloset, + 1.3), + 508: ('phase_5.5/models/estate/closetBoy', + None, + None, + 500, + FLCloset, + 1.3), + 510: ('phase_5.5/models/estate/closetGirl', + None, + None, + 500, + FLCloset, + 0.85), + 512: ('phase_5.5/models/estate/closetGirl', + None, + None, + 500, + FLCloset, + 1.0), + 514: ('phase_5.5/models/estate/closetGirl', + None, + None, + 500, + FLCloset, + 1.15), + 516: ('phase_5.5/models/estate/closetGirl', + None, + None, + 500, + FLCloset, + 1.3), + 518: ('phase_5.5/models/estate/closetGirl', + None, + None, + 500, + FLCloset, + 1.3), + 600: ('phase_3.5/models/modules/lamp_short', + None, + None, + 45, + FLOnTable), + 610: ('phase_3.5/models/modules/lamp_tall', + None, + None, + 45), + 620: ('phase_5.5/models/estate/lampA', + None, + None, + 35, + FLOnTable), + 625: ('phase_5.5/models/estate/lampADesat', + None, + {0: (('**/top', furnitureColors[0]),), + 1: (('**/top', furnitureColors[1]),), + 2: (('**/top', furnitureColors[2]),), + 3: (('**/top', furnitureColors[3]),), + 4: (('**/top', furnitureColors[4]),), + 5: (('**/top', furnitureColors[5]),)}, + 70, + FLOnTable), + 630: ('phase_5.5/models/estate/bugRoomDaisyLamp1', + None, + None, + 55), + 640: ('phase_5.5/models/estate/bugRoomDaisyLamp2', + None, + None, + 55), + 650: ('phase_5.5/models/estate/UWlamp_jellyfish', + None, + None, + 55, + FLOnTable), + 660: ('phase_5.5/models/estate/UWlamps_jellyfishB', + None, + None, + 55, + FLOnTable), + 670: ('phase_5.5/models/estate/West_cowboyLamp', + None, + None, + 55, + FLOnTable), + 680: ('phase_5.5/models/estate/tt_m_ara_int_candlestick', + None, + {0: (('**/candlestick/candlestick', (1.0, + 1.0, + 1.0, + 1.0)),), + 1: (('**/candlestick/candlestick', furnitureColors[1]),), + 2: (('**/candlestick/candlestick', furnitureColors[2]),), + 3: (('**/candlestick/candlestick', furnitureColors[3]),), + 4: (('**/candlestick/candlestick', furnitureColors[4]),), + 5: (('**/candlestick/candlestick', furnitureColors[5]),), + 6: (('**/candlestick/candlestick', furnitureColors[0]),)}, + 20, + FLOnTable), + 681: ('phase_5.5/models/estate/tt_m_ara_int_candlestickLit', + None, + {0: (('**/candlestick/candlestick', (1.0, + 1.0, + 1.0, + 1.0)),), + 1: (('**/candlestickLit/candlestick', furnitureColors[1]),), + 2: (('**/candlestickLit/candlestick', furnitureColors[2]),), + 3: (('**/candlestickLit/candlestick', furnitureColors[3]),), + 4: (('**/candlestickLit/candlestick', furnitureColors[4]),), + 5: (('**/candlestickLit/candlestick', furnitureColors[5]),), + 6: (('**/candlestickLit/candlestick', furnitureColors[0]),)}, + 25, + FLOnTable), + 700: ('phase_3.5/models/modules/couch_1person', + None, + None, + 230, + FLChair), + 705: ('phase_5.5/models/estate/couch_1personDesat', + None, + {0: (('**/*couch', furnitureColors[0]),), + 1: (('**/*couch', furnitureColors[1]),), + 2: (('**/*couch', furnitureColors[2]),), + 3: (('**/*couch', furnitureColors[3]),), + 4: (('**/*couch', furnitureColors[4]),), + 5: (('**/*couch', furnitureColors[5]),)}, + 460, + FLChair), + 710: ('phase_3.5/models/modules/couch_2person', + None, + None, + 230, + FLChair), + 715: ('phase_5.5/models/estate/couch_2personDesat', + None, + {0: (('**/*couch', furnitureColors[0]),), + 1: (('**/*couch', furnitureColors[1]),), + 2: (('**/*couch', furnitureColors[2]),), + 3: (('**/*couch', furnitureColors[3]),), + 4: (('**/*couch', furnitureColors[4]),), + 5: (('**/*couch', furnitureColors[5]),)}, + 460, + FLChair), + 720: ('phase_5.5/models/estate/West_HayCouch', + None, + None, + 420, + FLChair), + 730: ('phase_5.5/models/estate/twinkieCouch', + None, + None, + 480), + 800: ('phase_3.5/models/modules/desk_only_wo_phone', + None, + None, + 65, + FLIsTable), + 810: ('phase_5.5/models/estate/BugRoomDesk', + None, + None, + 125, + FLIsTable), + 900: ('phase_3.5/models/modules/umbrella_stand', + None, + None, + 30), + 910: ('phase_3.5/models/modules/coatrack', + None, + None, + 75), + 920: ('phase_3.5/models/modules/paper_trashcan', + None, + None, + 30), + 930: ('phase_5.5/models/estate/BugRoomRedMushroomPot', + None, + None, + 60), + 940: ('phase_5.5/models/estate/BugRoomYellowMushroomPot', + None, + None, + 60), + 950: ('phase_5.5/models/estate/UWcoralClothRack', + None, + None, + 75), + 960: ('phase_5.5/models/estate/west_barrelStand', + None, + None, + 75), + 970: ('phase_5.5/models/estate/West_fatCactus', + None, + None, + 75), + 980: ('phase_5.5/models/estate/West_Tepee', + None, + None, + 150), + 990: ('phase_5.5/models/estate/gag_fan', + None, + None, + 500, + None, + None, + 0.5), + 1000: ('phase_3.5/models/modules/rug', + None, + None, + 75, + FLRug), + 1010: ('phase_5.5/models/estate/rugA', + None, + None, + 75, + FLRug), + 1015: ('phase_5.5/models/estate/rugADesat', + None, + {0: (('**/pPlane*', furnitureColors[0]),), + 1: (('**/pPlane*', furnitureColors[1]),), + 2: (('**/pPlane*', furnitureColors[2]),), + 3: (('**/pPlane*', furnitureColors[3]),), + 4: (('**/pPlane*', furnitureColors[4]),), + 5: (('**/pPlane*', furnitureColors[5]),)}, + 150, + FLRug), + 1020: ('phase_5.5/models/estate/rugB', + None, + None, + 75, + FLRug, + 2.5), + 1030: ('phase_5.5/models/estate/bugRoomLeafMat', + None, + None, + 75, + FLRug), + 1040: ('phase_5.5/models/estate/tt_m_ara_int_presents', + None, + None, + 300), + 1050: ('phase_5.5/models/estate/tt_m_ara_int_sled', + None, + None, + 400), + 1100: ('phase_5.5/models/estate/cabinetRwood', + None, + None, + 825), + 1110: ('phase_5.5/models/estate/cabinetYwood', + None, + None, + 825), + 1120: ('phase_3.5/models/modules/bookcase', + None, + None, + 650, + FLIsTable), + 1130: ('phase_3.5/models/modules/bookcase_low', + None, + None, + 650, + FLIsTable), + 1140: ('phase_5.5/models/estate/icecreamChest', + None, + None, + 750), + 1200: ('phase_3.5/models/modules/ending_table', + None, + None, + 60, + FLIsTable), + 1210: ('phase_5.5/models/estate/table_radio', + None, + None, + 60, + FLIsTable, + 50.0), + 1215: ('phase_5.5/models/estate/table_radioDesat', + None, + {0: (('**/RADIOTABLE_*', woodColors[0]),), + 1: (('**/RADIOTABLE_*', woodColors[1]),), + 2: (('**/RADIOTABLE_*', woodColors[2]),), + 3: (('**/RADIOTABLE_*', woodColors[3]),), + 4: (('**/RADIOTABLE_*', woodColors[4]),), + 5: (('**/RADIOTABLE_*', woodColors[5]),)}, + 120, + FLIsTable, + 50.0), + 1220: ('phase_5.5/models/estate/coffeetableSq', + None, + None, + 180, + FLIsTable), + 1230: ('phase_5.5/models/estate/coffeetableSq_BW', + None, + None, + 180, + FLIsTable), + 1240: ('phase_5.5/models/estate/UWtable', + None, + None, + 180, + FLIsTable), + 1250: ('phase_5.5/models/estate/cookieTableA', + None, + None, + 220, + FLIsTable), + 1260: ('phase_5.5/models/estate/TABLE_Bedroom_Desat', + None, + {0: (('**/Bedroom_Table', woodColors[0]),), + 1: (('**/Bedroom_Table', woodColors[1]),), + 2: (('**/Bedroom_Table', woodColors[2]),), + 3: (('**/Bedroom_Table', woodColors[3]),), + 4: (('**/Bedroom_Table', woodColors[4]),), + 5: (('**/Bedroom_Table', woodColors[5]),)}, + 220, + FLIsTable), + 1300: ('phase_5.5/models/estate/jellybeanBank', + None, + None, + 5000, + FLBank, + 1.0), + 1310: ('phase_5.5/models/estate/jellybeanBank', + None, + None, + 7500, + FLBank, + 1.0), + 1320: ('phase_5.5/models/estate/jellybeanBank', + None, + None, + 10000, + FLBank, + 1.0), + 1330: ('phase_5.5/models/estate/jellybeanBank', + None, + None, + 12500, + FLBank, + 1.0), + 1340: ('phase_5.5/models/estate/jellybeanBank', + None, + None, + 15000, + FLBank, + 1.0), + 1399: ('phase_5.5/models/estate/prop_phone-mod', + None, + None, + 0, + FLPhone), + 1400: ('phase_5.5/models/estate/cezanne_toon', + None, + None, + 425, + FLPainting, + 2.0), + 1410: ('phase_5.5/models/estate/flowers', + None, + None, + 425, + FLPainting, + 2.0), + 1420: ('phase_5.5/models/estate/modernistMickey', + None, + None, + 425, + FLPainting, + 2.0), + 1430: ('phase_5.5/models/estate/rembrandt_toon', + None, + None, + 425, + FLPainting, + 2.0), + 1440: ('phase_5.5/models/estate/landscape', + None, + None, + 425, + FLPainting, + 100.0), + 1441: ('phase_5.5/models/estate/whistler-horse', + None, + None, + 425, + FLPainting, + 2.0), + 1442: ('phase_5.5/models/estate/degasHorseStar', + None, + None, + 425, + FLPainting, + 2.5), + 1443: ('phase_5.5/models/estate/MagPie', + None, + None, + 425, + FLPainting, + 2.0), + 1450: ('phase_5.5/models/estate/tt_m_prp_int_painting_valentine', + None, + None, + 425, + FLPainting), + 1500: ('phase_5.5/models/estate/RADIO_A', + None, + None, + 25, + FLOnTable, + 15.0), + 1510: ('phase_5.5/models/estate/RADIO_B', + None, + None, + 25, + FLOnTable, + 15.0), + 1520: ('phase_5.5/models/estate/radio_c', + None, + None, + 25, + FLOnTable, + 15.0), + 1530: ('phase_5.5/models/estate/bugRoomTV', + None, + None, + 675, + FLTV), + 1531: ('phase_5.5/models/estate/bugRoomTV_50inch', + None, + None, + 1250, + FLTV), + 1532: ('phase_5.5/models/estate/bugRoomTV_100inch', + None, + None, + 5000, + FLTV), + 1600: ('phase_5.5/models/estate/vaseA_short', + None, + None, + 120, + FLOnTable), + 1610: ('phase_5.5/models/estate/vaseA_tall', + None, + None, + 120, + FLOnTable), + 1620: ('phase_5.5/models/estate/vaseB_short', + None, + None, + 120, + FLOnTable), + 1630: ('phase_5.5/models/estate/vaseB_tall', + None, + None, + 120, + FLOnTable), + 1640: ('phase_5.5/models/estate/vaseC_short', + None, + None, + 120, + FLOnTable), + 1650: ('phase_5.5/models/estate/vaseD_short', + None, + None, + 120, + FLOnTable), + 1660: ('phase_5.5/models/estate/UWcoralVase', + None, + None, + 120, + FLOnTable | FLBillboard), + 1661: ('phase_5.5/models/estate/UWshellVase', + None, + None, + 120, + FLOnTable | FLBillboard), + 1670: ('phase_5.5/models/estate/tt_m_prp_int_roseVase_valentine', + None, + None, + 200, + FLOnTable), + 1680: ('phase_5.5/models/estate/tt_m_prp_int_roseWatercan_valentine', + None, + None, + 200, + FLOnTable), + 1700: ('phase_5.5/models/estate/popcornCart', + None, + None, + 400), + 1710: ('phase_5.5/models/estate/bugRoomLadyBug', + None, + None, + 260), + 1720: ('phase_5.5/models/estate/UWfountain', + None, + None, + 450), + 1725: ('phase_5.5/models/estate/UWOceanDryer', + None, + None, + 400), + 1800: ('phase_5.5/models/estate/UWskullBowl', + None, + None, + 120, + FLOnTable), + 1810: ('phase_5.5/models/estate/UWlizardBowl', + None, + None, + 120, + FLOnTable), + 1900: ('phase_5.5/models/estate/UWswordFish', + None, + None, + 425, + FLPainting, + 0.5), + 1910: ('phase_5.5/models/estate/UWhammerhead', + None, + None, + 425, + FLPainting), + 1920: ('phase_5.5/models/estate/West_hangingHorns', + None, + None, + 475, + FLPainting), + 1930: ('phase_5.5/models/estate/West_Sombrero', + None, + None, + 425, + FLPainting), + 1940: ('phase_5.5/models/estate/West_fancySombrero', + None, + None, + 450, + FLPainting), + 1950: ('phase_5.5/models/estate/West_CoyotePawdecor', + None, + None, + 475, + FLPainting), + 1960: ('phase_5.5/models/estate/West_Horseshoe', + None, + None, + 475, + FLPainting), + 1970: ('phase_5.5/models/estate/West_bisonPortrait', + None, + None, + 475, + FLPainting), + 2000: ('phase_5.5/models/estate/candySwingSet', + None, + None, + 300), + 2010: ('phase_5.5/models/estate/cakeSlide', + None, + None, + 200), + 3000: ('phase_5.5/models/estate/BanannaSplitShower', + None, + None, + 400), + 4000: ('phase_5.5/models/estate/tt_m_ara_est_accessoryTrunkBoy', + None, + None, + 0, + FLTrunk, + 0.9), + 4010: ('phase_5.5/models/estate/tt_m_ara_est_accessoryTrunkGirl', + None, + None, + 0, + FLTrunk, + 0.9), + 10000: ('phase_4/models/estate/pumpkin_short', + None, + None, + 200, + FLOnTable), + 10010: ('phase_4/models/estate/pumpkin_tall', + None, + None, + 250, + FLOnTable), + 10020: ('phase_5.5/models/estate/tt_m_prp_int_winter_tree', + None, + None, + 500, + None, + None, + 0.1), + 10030: ('phase_5.5/models/estate/tt_m_prp_int_winter_wreath', + None, + None, + 200, + FLPainting), + 10040: ('phase_10/models/cashbotHQ/CBWoodCrate', + None, + None, + 0, + FLCrate, + 0.5) +} + +class CatalogFurnitureItem(CatalogAtticItem.CatalogAtticItem): + + def makeNewItem(self, furnitureType, colorOption = None, posHpr = None): + self.furnitureType = furnitureType + self.colorOption = colorOption + self.posHpr = posHpr + CatalogAtticItem.CatalogAtticItem.makeNewItem(self) + + def needsCustomize(self): + return self.colorOption == None and FurnitureTypes[self.furnitureType][FTColorOptions] != None + + def saveHistory(self): + return 1 + + def replacesExisting(self): + return self.getFlags() & (FLCloset | FLBank) != 0 + + def hasExisting(self): + return 1 + + def getYourOldDesc(self): + if self.getFlags() & FLCloset: + return TTLocalizer.FurnitureYourOldCloset + elif self.getFlags() & FLBank: + return TTLocalizer.FurnitureYourOldBank + else: + return None + return None + + def notOfferedTo(self, avatar): + if self.getFlags() & FLCloset: + decade = self.furnitureType - self.furnitureType % 10 + forBoys = decade == 500 + if avatar.getStyle().getGender() == 'm': + return not forBoys + else: + return forBoys + if self.forBoysOnly(): + if avatar.getStyle().getGender() == 'm': + return 0 + else: + return 1 + elif self.forGirlsOnly(): + if avatar.getStyle().getGender() == 'f': + return 0 + else: + return 1 + return 0 + + def forBoysOnly(self): + return self.getFlags() & FLBoysOnly > 0 + + def forGirlsOnly(self): + return self.getFlags() & FLGirlsOnly > 0 + + def isDeletable(self): + return self.getFlags() & (FLBank | FLCloset | FLPhone | FLTrunk) == 0 + + def getMaxBankMoney(self): + return BankToMoney.get(self.furnitureType) + + def getMaxClothes(self): + return ClosetToClothes[self.furnitureType] + + def reachedPurchaseLimit(self, avatar): + if self.getFlags() & FLBank: + if self.getMaxBankMoney() <= avatar.getMaxBankMoney(): + return 1 + if self in avatar.onOrder or self in avatar.mailboxContents: + return 1 + if self.getFlags() & FLCloset: + if self.getMaxClothes() <= avatar.getMaxClothes(): + return 1 + if self in avatar.onOrder or self in avatar.mailboxContents: + return 1 + return 0 + + def getTypeName(self): + flags = self.getFlags() + if flags & FLPainting: + return TTLocalizer.PaintingTypeName + else: + return TTLocalizer.FurnitureTypeName + + def getName(self): + return TTLocalizer.FurnitureNames[self.furnitureType] + + def getFlags(self): + defn = FurnitureTypes[self.furnitureType] + if FTFlags < len(defn): + flag = defn[FTFlags] + if flag == None: + return 0 + else: + return flag + else: + return 0 + return + + def isGift(self): + if self.getEmblemPrices(): + return 0 + if self.getFlags() & (FLCloset | FLBank): + return 0 + else: + return 1 + + def recordPurchase(self, avatar, optional): + house, retcode = self.getHouseInfo(avatar) + if retcode >= 0: + if self.getFlags() & FLCloset: + if avatar.getMaxClothes() > self.getMaxClothes(): + return ToontownGlobals.P_AlreadyOwnBiggerCloset + avatar.b_setMaxClothes(self.getMaxClothes()) + if self.getFlags() & FLBank: + avatar.b_setMaxBankMoney(self.getMaxBankMoney()) + house.addAtticItem(self) + return retcode + + def getDeliveryTime(self): + return 24 * 60 + + def getPicture(self, avatar): + model = self.loadModel() + spin = 1 + flags = self.getFlags() + if flags & FLRug: + spin = 0 + model.setP(90) + elif flags & FLPainting: + spin = 0 + elif flags & FLBillboard: + spin = 0 + model.setBin('unsorted', 0, 1) + self.hasPicture = True + return self.makeFrameModel(model, spin) + + def output(self, store = -1): + return 'CatalogFurnitureItem(%s%s)' % (self.furnitureType, self.formatOptionalData(store)) + + def getFilename(self): + type = FurnitureTypes[self.furnitureType] + return type[FTModelName] + + def compareTo(self, other): + return self.furnitureType - other.furnitureType + + def getHashContents(self): + return self.furnitureType + + def getSalePrice(self): + if self.furnitureType in [508, 518]: + return 50 + else: + return CatalogItem.CatalogItem.getSalePrice(self) + + def getBasePrice(self): + return FurnitureTypes[self.furnitureType][FTBasePrice] + + def loadModel(self): + type = FurnitureTypes[self.furnitureType] + model = loader.loadModel(type[FTModelName]) + self.applyColor(model, type[FTColor]) + if type[FTColorOptions] != None: + if self.colorOption == None: + option = random.choice(type[FTColorOptions].values()) + else: + option = type[FTColorOptions].get(self.colorOption) + self.applyColor(model, option) + if FTScale < len(type): + scale = type[FTScale] + if not scale == None: + model.setScale(scale) + model.flattenLight() + + return model + + def decodeDatagram(self, di, versionNumber, store): + CatalogAtticItem.CatalogAtticItem.decodeDatagram(self, di, versionNumber, store) + self.furnitureType = di.getInt16() + self.colorOption = None + type = FurnitureTypes[self.furnitureType] + if type[FTColorOptions]: + if store & CatalogItem.Customization: + self.colorOption = di.getUint8() + option = type[FTColorOptions][self.colorOption] + return + + def encodeDatagram(self, dg, store): + CatalogAtticItem.CatalogAtticItem.encodeDatagram(self, dg, store) + dg.addInt16(self.furnitureType) + if FurnitureTypes[self.furnitureType][FTColorOptions]: + if store & CatalogItem.Customization: + dg.addUint8(self.colorOption) + + def getAcceptItemErrorText(self, retcode): + if retcode == ToontownGlobals.P_AlreadyOwnBiggerCloset: + return TTLocalizer.CatalogAcceptClosetError + return CatalogAtticItem.CatalogAtticItem.getAcceptItemErrorText(self, retcode) + + +def nextAvailableCloset(avatar, duplicateItems): + if avatar.getStyle().getGender() == 'm': + index = 0 + else: + index = 1 + if not hasattr(avatar, 'maxClothes'): + return None + closetIds = ClothesToCloset.get(avatar.getMaxClothes()) + closetIds = list(closetIds) + closetIds.sort() + closetId = closetIds[index] + if closetId == None or closetId == MaxClosetIds[index]: + return + closetId += 2 + item = CatalogFurnitureItem(closetId) + while item in avatar.onOrder or item in avatar.mailboxContents: + closetId += 2 + if closetId > MaxClosetIds[index]: + return + item = CatalogFurnitureItem(closetId) + + return item + +def nextAvailableBank(avatar, duplicateItems): + if not avatar.getMaxBankMoney() in MoneyToBank: + return CatalogFurnitureItem(1300) + + currentBank = MoneyToBank[avatar.getMaxBankMoney()] + + if currentBank == MaxBankId: + return + + return CatalogFurnitureItem(currentBank + 10) + +def get50ItemCloset(avatar, duplicateItems): + if avatar.getStyle().getGender() == 'm': + index = 0 + else: + index = 1 + closetId = MaxClosetIds[index] + item = CatalogFurnitureItem(closetId) + if item in avatar.onOrder or item in avatar.mailboxContents: + return None + return item + + +def getMaxClosets(): + list = [] + for closetId in MaxClosetIds: + list.append(CatalogFurnitureItem(closetId)) + + return list + + +def getAllClosets(): + list = [] + for closetId in ClosetToClothes.keys(): + list.append(CatalogFurnitureItem(closetId)) + + return list + +def getAllBanks(): + list = [] + + for bankId in BankToMoney.keys(): + list.append(CatalogFurnitureItem(bankId)) + + return list + +def getAllFurnitures(index): + list = [] + colors = FurnitureTypes[index][FTColorOptions] + for n in xrange(len(colors)): + list.append(CatalogFurnitureItem(index, n)) + + return list diff --git a/toontown/catalog/CatalogGardenItem.py b/toontown/catalog/CatalogGardenItem.py new file mode 100755 index 00000000..fba420ad --- /dev/null +++ b/toontown/catalog/CatalogGardenItem.py @@ -0,0 +1,169 @@ +import CatalogItem +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +from direct.interval.IntervalGlobal import * +from toontown.estate import GardenGlobals +from direct.actor import Actor +from pandac.PandaModules import NodePath + +class CatalogGardenItem(CatalogItem.CatalogItem): + sequenceNumber = 0 + + def makeNewItem(self, itemIndex = 0, count = 3, tagCode = 1): + self.gardenIndex = itemIndex + self.numItems = count + self.giftCode = tagCode + CatalogItem.CatalogItem.makeNewItem(self) + + def getPurchaseLimit(self): + if self.gardenIndex == GardenGlobals.GardenAcceleratorSpecial: + return 1 + else: + return 100 + + def getAcceptItemErrorText(self, retcode): + if retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.CatalogAcceptGarden + return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode) + + def saveHistory(self): + return 1 + + def getTypeName(self): + return TTLocalizer.GardenTypeName + + def getName(self): + return GardenGlobals.Specials[self.gardenIndex]['photoName'] + + def recordPurchase(self, avatar, optional): + if avatar: + avatar.addGardenItem(self.gardenIndex, self.numItems) + return ToontownGlobals.P_ItemAvailable + + def getPicture(self, avatar): + photoModel = GardenGlobals.Specials[self.gardenIndex]['photoModel'] + + if 'photoAnimation' in GardenGlobals.Specials[self.gardenIndex]: + modelPath = photoModel + GardenGlobals.Specials[self.gardenIndex]['photoAnimation'][0] + animationName = GardenGlobals.Specials[self.gardenIndex]['photoAnimation'][1] + animationPath = photoModel + animationName + self.model = Actor.Actor() + self.model.loadModel(modelPath) + self.model.loadAnims(dict([[animationName, animationPath]])) + frame, ival = self.makeFrameModel(self.model, 0) + ival = ActorInterval(self.model, animationName, 2.0) + photoPos = GardenGlobals.Specials[self.gardenIndex]['photoPos'] + frame.setPos(photoPos) + photoScale = GardenGlobals.Specials[self.gardenIndex]['photoScale'] + self.model.setScale(photoScale) + self.hasPicture = True + return (frame, ival) + else: + self.model = loader.loadModel(photoModel) + frame = self.makeFrame() + self.model.reparentTo(frame) + photoPos = GardenGlobals.Specials[self.gardenIndex]['photoPos'] + self.model.setPos(*photoPos) + photoScale = GardenGlobals.Specials[self.gardenIndex]['photoScale'] + self.model.setScale(photoScale) + self.hasPicture = True + return (frame, None) + + def cleanupPicture(self): + CatalogItem.CatalogItem.cleanupPicture(self) + if hasattr(self, 'model') and self.model: + self.model.detachNode() + self.model = None + return + + def output(self, store = -1): + return 'CatalogGardenItem(%s%s)' % (self.gardenIndex, self.formatOptionalData(store)) + + def compareTo(self, other): + return 0 + + def getHashContents(self): + return self.gardenIndex + + def getBasePrice(self): + beanCost = GardenGlobals.Specials[self.gardenIndex]['beanCost'] + return beanCost + + def decodeDatagram(self, di, versionNumber, store): + CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store) + self.gardenIndex = di.getUint8() + self.numItems = di.getUint8() + + def encodeDatagram(self, dg, store): + CatalogItem.CatalogItem.encodeDatagram(self, dg, store) + dg.addUint8(self.gardenIndex) + dg.addUint8(self.numItems) + + def getRequestPurchaseErrorText(self, retcode): + retval = CatalogItem.CatalogItem.getRequestPurchaseErrorText(self, retcode) + origText = retval + if retval == TTLocalizer.CatalogPurchaseItemAvailable or retval == TTLocalizer.CatalogPurchaseItemOnOrder: + recipeKey = GardenGlobals.getRecipeKeyUsingSpecial(self.gardenIndex) + if not recipeKey == -1: + retval += GardenGlobals.getPlantItWithString(self.gardenIndex) + if self.gardenIndex == GardenGlobals.GardenAcceleratorSpecial: + if GardenGlobals.ACCELERATOR_USED_FROM_SHTIKER_BOOK: + retval = origText + retval += TTLocalizer.UseFromSpecialsTab + retval += TTLocalizer.MakeSureWatered + return retval + + def getRequestPurchaseErrorTextTimeout(self): + return 20 + + def getDeliveryTime(self): + if self.gardenIndex == GardenGlobals.GardenAcceleratorSpecial: + return 24 * 60 + else: + return 0 + + def getPurchaseLimit(self): + if self.gardenIndex == GardenGlobals.GardenAcceleratorSpecial: + return 1 + else: + return 0 + + def compareTo(self, other): + if self.gardenIndex != other.gardenIndex: + return self.gardenIndex - other.gardenIndex + return self.gardenIndex - other.gardenIndex + + def reachedPurchaseLimit(self, avatar): + if avatar.onOrder.count(self) != 0: + return 1 + if avatar.mailboxContents.count(self) != 0: + return 1 + for specials in avatar.getGardenSpecials(): + if specials[0] == self.gardenIndex: + if self.gardenIndex == GardenGlobals.GardenAcceleratorSpecial: + return 1 + + return 0 + + def isSkillTooLow(self, avatar): + recipeKey = GardenGlobals.getRecipeKeyUsingSpecial(self.gardenIndex) + recipe = GardenGlobals.Recipes[recipeKey] + numBeansRequired = len(recipe['beans']) + canPlant = avatar.getBoxCapability() + result = False + if canPlant < numBeansRequired: + result = True + if not result and self.gardenIndex in GardenGlobals.Specials and 'minSkill' in GardenGlobals.Specials[self.gardenIndex]: + minSkill = GardenGlobals.Specials[self.gardenIndex]['minSkill'] + if avatar.shovelSkill < minSkill: + result = True + else: + result = False + return result + + def noGarden(self, avatar): + return not avatar.getGardenStarted() + + def isGift(self): + return 0 diff --git a/toontown/catalog/CatalogGardenStarterItem.py b/toontown/catalog/CatalogGardenStarterItem.py new file mode 100755 index 00000000..d33e5903 --- /dev/null +++ b/toontown/catalog/CatalogGardenStarterItem.py @@ -0,0 +1,105 @@ +import CatalogItem +import time +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +from direct.interval.IntervalGlobal import * +from toontown.toontowngui import TTDialog +from toontown.estate import GardenTutorial + +class CatalogGardenStarterItem(CatalogItem.CatalogItem): + + def makeNewItem(self): + CatalogItem.CatalogItem.makeNewItem(self) + + def getPurchaseLimit(self): + return 0 + + def reachedPurchaseLimit(self, avatar): + if self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder or self in avatar.awardMailboxContents or self in avatar.onAwardOrder or hasattr(avatar, 'gardenStarted') and avatar.getGardenStarted(): + return 1 + return 0 + + def saveHistory(self): + return 1 + + def getTypeName(self): + return TTLocalizer.GardenStarterTypeName + + def getName(self): + return TTLocalizer.GardenStarterTypeName + + def recordPurchase(self, avatar, optional): + if avatar: + self.notify.debug('rental -- has avatar') + estate = simbase.air.estateManager._lookupEstate(avatar) + if estate: + self.notify.debug('rental -- has estate') + estate.placeStarterGarden(avatar.doId) + else: + self.notify.warning('rental -- something not there') + return ToontownGlobals.P_ItemAvailable + + def getPicture(self, avatar): + self.hasPicture = True + scale = 1 + heading = 0 + pitch = 30 + roll = 0 + spin = 1 + down = -1 + modelParent = loader.loadModel('phase_5.5/models/estate/watering_cans') + model = modelParent.find('**/water_canA') + scale = 0.5 + heading = 45 + return self.makeFrameModel(model, spin) + + def output(self, store = -1): + return 'CatalogGardenStarterItem(%s)' % self.formatOptionalData(store) + + def compareTo(self, other): + return 0 + + def getHashContents(self): + return 0 + + def getBasePrice(self): + return 50 + + def decodeDatagram(self, di, versionNumber, store): + CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store) + + def encodeDatagram(self, dg, store): + CatalogItem.CatalogItem.encodeDatagram(self, dg, store) + + def getDeliveryTime(self): + return 1 + + def isRental(self): + return 0 + + def isGift(self): + return 0 + + def acceptItem(self, mailbox, index, callback): + self.confirmGarden = TTDialog.TTGlobalDialog(doneEvent='confirmGarden', message=TTLocalizer.MessageConfirmGarden, command=Functor(self.handleGardenConfirm, mailbox, index, callback), style=TTDialog.TwoChoice) + self.confirmGarden.show() + + def handleGardenConfirm(self, mailbox, index, callback, choice): + if choice > 0: + + def handleTutorialDone(): + self.gardenTutorial.destroy() + self.gardenTutorial = None + return + + self.gardenTutorial = GardenTutorial.GardenTutorial(callback=handleTutorialDone) + if hasattr(mailbox, 'mailboxGui') and mailbox.mailboxGui: + mailbox.acceptItem(self, index, callback) + mailbox.mailboxGui.justExit() + else: + callback(ToontownGlobals.P_UserCancelled, self, index) + if self.confirmGarden: + self.confirmGarden.cleanup() + self.confirmGarden = None + return diff --git a/toontown/catalog/CatalogGenerator.py b/toontown/catalog/CatalogGenerator.py new file mode 100755 index 00000000..2de0d885 --- /dev/null +++ b/toontown/catalog/CatalogGenerator.py @@ -0,0 +1,1777 @@ +from direct.directnotify import DirectNotifyGlobal +import CatalogItem +import CatalogItemList +from CatalogFurnitureItem import CatalogFurnitureItem, nextAvailableCloset, nextAvailableBank, getAllClosets, get50ItemCloset, getMaxClosets, getAllBanks +from CatalogAnimatedFurnitureItem import CatalogAnimatedFurnitureItem +from CatalogClothingItem import CatalogClothingItem, getAllClothes +from CatalogChatItem import CatalogChatItem, getChatRange +from CatalogEmoteItem import CatalogEmoteItem +from CatalogWallpaperItem import CatalogWallpaperItem, getWallpapers +from CatalogFlooringItem import CatalogFlooringItem, getFloorings +from CatalogMouldingItem import CatalogMouldingItem, getAllMouldings +from CatalogWainscotingItem import CatalogWainscotingItem, getAllWainscotings +from CatalogWindowItem import CatalogWindowItem +from CatalogPoleItem import nextAvailablePole, getAllPoles +from CatalogPetTrickItem import CatalogPetTrickItem, getAllPetTricks +from CatalogTankItem import nextAvailableTank, getAllTanks +from CatalogGardenItem import CatalogGardenItem +from CatalogToonStatueItem import CatalogToonStatueItem +from CatalogRentalItem import CatalogRentalItem +from CatalogGardenStarterItem import CatalogGardenStarterItem +from CatalogNametagItem import CatalogNametagItem +from CatalogAccessoryItem import CatalogAccessoryItem +from direct.actor import Actor +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +import types +import random +import time +from pandac.PandaModules import * +MetaItems = {100: getAllClothes(101, 102, 103, 104, 105, 106, 107, 108, 109, 109, 111, 115, 201, 202, 203, 204, 205, 206, 207, 208, 209, 209, 211, 215), + 300: getAllClothes(301, 302, 303, 304, 305, 308, 401, 403, 404, 405, 407, 451, 452, 453), + 2000: getChatRange(0, 1999), + 2010: getChatRange(2000, 2999), + 2020: getChatRange(3000, 3999), + 2030: getChatRange(4000, 4999), + 2040: getChatRange(6000, 6999), + 2050: getChatRange(7000, 7999), + 2900: getChatRange(10000, 10002, 10005, 10005, 10007, 10008, 10010, 10099), + 2910: getChatRange(11000, 11005, 11008, 11008, 11012, 11015, 11017, 11019, 11021, 11022), + 2920: getChatRange(12000, 12049), + 2921: getChatRange(12050, 12099), + 2930: getChatRange(13000, 13099), + 2940: getChatRange(14000, 14099), + 3000: getWallpapers(1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2100), + 3010: getWallpapers(2200, 2300, 2400, 2500, 2600, 2700, 2800), + 3020: getWallpapers(2900, 3000, 3100, 3200, 3300, 3400, 3500, 3600), + 3030: getWallpapers(3700, 3800, 3900), + 3500: getAllWainscotings(1000, 1010), + 3510: getAllWainscotings(1020), + 3520: getAllWainscotings(1030), + 3530: getAllWainscotings(1040), + 4000: getFloorings(1000, 1010, 1020, 1030, 1040, 1050, 1060, 1070, 1080, 1090, 1100), + 4010: getFloorings(1110, 1120, 1130), + 4020: getFloorings(1140, 1150, 1160, 1170, 1180, 1190), + 4500: getAllMouldings(1000, 1010), + 4510: getAllMouldings(1020, 1030, 1040), + 4520: getAllMouldings(1070), + 5000: getAllPetTricks()} +MetaItemChatKeysSold = (2000, + 2010, + 2020, + 2030, + 2040, + 2050, + 2900, + 2910, + 2920, + 2921, + 2930) + +def getAllChatItemsSold(): + result = [] + for key in MetaItemChatKeysSold: + result += MetaItems[key] + + return result + + +class Sale: + + def __init__(self, *args): + self.args = args + + +MonthlySchedule = ((7, + 1, + 8, + 31, + (CatalogAccessoryItem(101), + CatalogAccessoryItem(103), + CatalogAccessoryItem(112), + CatalogAccessoryItem(113), + CatalogAccessoryItem(114), + CatalogAccessoryItem(115), + CatalogAccessoryItem(117), + CatalogAccessoryItem(118), + CatalogAccessoryItem(123), + CatalogAccessoryItem(124), + CatalogAccessoryItem(125), + CatalogAccessoryItem(126), + CatalogAccessoryItem(127), + CatalogAccessoryItem(128), + CatalogAccessoryItem(129), + CatalogAccessoryItem(130), + CatalogAccessoryItem(201), + CatalogAccessoryItem(202), + CatalogAccessoryItem(204), + CatalogAccessoryItem(205), + CatalogAccessoryItem(206), + CatalogAccessoryItem(208), + CatalogAccessoryItem(209), + CatalogAccessoryItem(210), + CatalogAccessoryItem(302), + CatalogAccessoryItem(308), + CatalogAccessoryItem(309), + CatalogAccessoryItem(310), + CatalogAccessoryItem(317), + CatalogAccessoryItem(402), + CatalogAccessoryItem(403), + CatalogAccessoryItem(405), + CatalogAccessoryItem(406), + CatalogAccessoryItem(407), + CatalogAccessoryItem(408), + CatalogAccessoryItem(409), + CatalogAccessoryItem(410), + CatalogAccessoryItem(411), + CatalogAccessoryItem(412), + CatalogAccessoryItem(413))), + (9, + 1, + 10, + 31, + (CatalogAccessoryItem(306), + CatalogAccessoryItem(318), + CatalogAccessoryItem(121), + CatalogAccessoryItem(212), + CatalogAccessoryItem(214), + CatalogAccessoryItem(312), + CatalogAccessoryItem(150), + CatalogAccessoryItem(151), + CatalogAccessoryItem(147), + CatalogAccessoryItem(422), + CatalogAccessoryItem(141), + CatalogAccessoryItem(146), + CatalogAccessoryItem(444), + CatalogAccessoryItem(122), + CatalogAccessoryItem(430), + CatalogAccessoryItem(145), + CatalogAccessoryItem(132), + CatalogAccessoryItem(161), + CatalogAccessoryItem(134), + CatalogAccessoryItem(149), + CatalogAccessoryItem(207), + CatalogAccessoryItem(215), + CatalogAccessoryItem(216), + CatalogAccessoryItem(417), + CatalogAccessoryItem(222), + CatalogAccessoryItem(321), + CatalogAccessoryItem(322), + CatalogAccessoryItem(307), + CatalogAccessoryItem(135), + CatalogAccessoryItem(174))), + (11, + 1, + 12, + 31, + (CatalogAccessoryItem(434), + CatalogAccessoryItem(435), + CatalogAccessoryItem(441), + CatalogAccessoryItem(446), + CatalogAccessoryItem(429), + CatalogAccessoryItem(110), + CatalogAccessoryItem(148), + CatalogAccessoryItem(443), + CatalogAccessoryItem(426), + CatalogAccessoryItem(439), + CatalogAccessoryItem(143), + CatalogAccessoryItem(313), + CatalogAccessoryItem(311), + CatalogAccessoryItem(437), + CatalogAccessoryItem(415), + CatalogAccessoryItem(167), + CatalogAccessoryItem(157), + CatalogAccessoryItem(106), + CatalogAccessoryItem(109), + CatalogAccessoryItem(421), + CatalogAccessoryItem(401), + CatalogAccessoryItem(447), + CatalogAccessoryItem(213), + CatalogAccessoryItem(330))), + (1, + 1, + 2, + 29, + (CatalogAccessoryItem(440), + CatalogAccessoryItem(425), + CatalogAccessoryItem(158), + CatalogAccessoryItem(431), + CatalogAccessoryItem(420), + CatalogAccessoryItem(155), + CatalogAccessoryItem(419), + CatalogAccessoryItem(436), + CatalogAccessoryItem(428), + CatalogAccessoryItem(304), + CatalogAccessoryItem(301), + CatalogAccessoryItem(416), + CatalogAccessoryItem(414), + CatalogAccessoryItem(164), + CatalogAccessoryItem(323), + CatalogAccessoryItem(108), + CatalogAccessoryItem(139), + CatalogAccessoryItem(316), + CatalogAccessoryItem(131), + CatalogAccessoryItem(170), + CatalogAccessoryItem(221), + CatalogAccessoryItem(225))), + (3, + 1, + 4, + 30, + (CatalogAccessoryItem(305), + CatalogAccessoryItem(303), + CatalogAccessoryItem(144), + CatalogAccessoryItem(120), + CatalogAccessoryItem(116), + CatalogAccessoryItem(217), + CatalogAccessoryItem(218), + CatalogAccessoryItem(219), + CatalogAccessoryItem(445), + CatalogAccessoryItem(418), + CatalogAccessoryItem(432), + CatalogAccessoryItem(427), + CatalogAccessoryItem(423), + CatalogAccessoryItem(137), + CatalogAccessoryItem(163), + CatalogAccessoryItem(165), + CatalogAccessoryItem(153), + CatalogAccessoryItem(319), + CatalogAccessoryItem(154), + CatalogAccessoryItem(159), + CatalogAccessoryItem(162), + CatalogAccessoryItem(315), + CatalogAccessoryItem(160), + CatalogAccessoryItem(102))), + (5, + 1, + 6, + 30, + (CatalogAccessoryItem(119), + CatalogAccessoryItem(136), + CatalogAccessoryItem(169), + CatalogAccessoryItem(140), + CatalogAccessoryItem(168), + CatalogAccessoryItem(138), + CatalogAccessoryItem(220), + CatalogAccessoryItem(433), + CatalogAccessoryItem(442), + CatalogAccessoryItem(424), + CatalogAccessoryItem(404), + CatalogAccessoryItem(156), + CatalogAccessoryItem(142), + CatalogAccessoryItem(152), + CatalogAccessoryItem(133), + CatalogAccessoryItem(166), + CatalogAccessoryItem(211), + CatalogAccessoryItem(314), + CatalogAccessoryItem(320), + CatalogAccessoryItem(173), + CatalogAccessoryItem(328), + CatalogAccessoryItem(329))), + (10, + 3, + 11, + 2, + ((3, 2900), + CatalogChatItem(10003), + CatalogClothingItem(1001, 0), + CatalogClothingItem(1002, 0), + CatalogWallpaperItem(10100), + CatalogWallpaperItem(10200), + CatalogFurnitureItem(10000), + CatalogFurnitureItem(10010), + CatalogNametagItem(9))), + (10, + 3, + 11, + 2, + (CatalogClothingItem(1744, 0), + CatalogClothingItem(1745, 0), + CatalogClothingItem(1748, 0), + CatalogClothingItem(1771, 0), + CatalogClothingItem(1774, 0), + CatalogClothingItem(1775, 0), + CatalogClothingItem(1743, 0), + CatalogClothingItem(1746, 0), + CatalogClothingItem(1747, 0), + CatalogClothingItem(1112, 0), + CatalogClothingItem(1113, 0), + CatalogClothingItem(1114, 0), + CatalogClothingItem(1115, 0), + CatalogClothingItem(1116, 0), + CatalogClothingItem(1117, 0), + CatalogClothingItem(1118, 0), + CatalogClothingItem(1119, 0), + CatalogClothingItem(1120, 0), + CatalogClothingItem(1121, 0), + CatalogClothingItem(1122, 0), + CatalogClothingItem(1123, 0), + CatalogClothingItem(1124, 0), + CatalogClothingItem(1125, 0), + CatalogClothingItem(1126, 0), + CatalogClothingItem(1127, 0), + CatalogAccessoryItem(171), + CatalogAccessoryItem(172), + CatalogAccessoryItem(224), + CatalogAccessoryItem(324), + CatalogAccessoryItem(325), + CatalogAccessoryItem(326), + CatalogAccessoryItem(327), + CatalogAccessoryItem(448), + CatalogAccessoryItem(449), + CatalogClothingItem(1801, 0))), + (2, + 1, + 2, + 28, + ((3, 2920), + (2, 2921), + CatalogClothingItem(1200, 0), + CatalogClothingItem(1201, 0), + CatalogClothingItem(1202, 0), + CatalogClothingItem(1203, 0), + CatalogClothingItem(1204, 0), + CatalogClothingItem(1205, 0), + CatalogWallpaperItem(12000), + CatalogWallpaperItem(12100), + CatalogWallpaperItem(12200), + CatalogWallpaperItem(12300), + CatalogWainscotingItem(1030, 0), + CatalogWainscotingItem(1030, 1), + CatalogMouldingItem(1060, 0), + CatalogMouldingItem(1060, 1), + CatalogClothingItem(1206, 0), + CatalogClothingItem(1207, 0), + CatalogClothingItem(1208, 0), + CatalogClothingItem(1209, 0), + CatalogClothingItem(1210, 0), + CatalogClothingItem(1211, 0), + CatalogClothingItem(1212, 0), + CatalogFurnitureItem(1670), + CatalogFurnitureItem(1680), + CatalogFurnitureItem(1450), + CatalogMouldingItem(1100, 0), + CatalogMouldingItem(1110, 0), + CatalogMouldingItem(1120, 0))), + (3, + 1, + 3, + 20, + ((3, 2930), + CatalogClothingItem(1300, 0), + CatalogClothingItem(1301, 0), + CatalogClothingItem(1302, 0), + CatalogClothingItem(1303, 0), + CatalogClothingItem(1304, 0), + CatalogClothingItem(1305, 0), + CatalogClothingItem(1306, 0), + CatalogWallpaperItem(13000), + CatalogWallpaperItem(13100), + CatalogWallpaperItem(13200), + CatalogWallpaperItem(13300), + CatalogFlooringItem(11000), + CatalogFlooringItem(11010))), + (5, + 25, + 6, + 25, + (CatalogClothingItem(1400, 0), CatalogClothingItem(1401, 0), CatalogClothingItem(1402, 0))), + (8, + 1, + 8, + 31, + (CatalogClothingItem(1403, 0), + CatalogClothingItem(1404, 0), + CatalogClothingItem(1405, 0), + CatalogClothingItem(1406, 0))), + (9, + 24, + 10, + 24, + (CatalogFurnitureItem(450), + CatalogAnimatedFurnitureItem(460), + CatalogAnimatedFurnitureItem(270), + CatalogAnimatedFurnitureItem(990))), + (6, + 15, + 8, + 15, + 2010, + 2010, + ((4, 2940),)), + (9, + 1, + 9, + 30, + (CatalogGardenItem(135, 1),)), + (1, + 1, + 1, + 31, + (CatalogGardenItem(135, 1),)), + (4, + 1, + 4, + 30, + (CatalogGardenItem(135, 1),)), + (6, + 1, + 6, + 30, + (CatalogGardenItem(135, 1),)), + (6, + 26, + 7, + 16, + (CatalogClothingItem(1500, 0), + CatalogClothingItem(1501, 0), + CatalogClothingItem(1502, 0), + CatalogClothingItem(1503, 0))), + (12, + 4, + 1, + 4, + ((3, 2910),)), + (12, + 4, + 1, + 4, + (CatalogFurnitureItem(680), + CatalogFurnitureItem(681), + CatalogGardenItem(130, 1), + CatalogGardenItem(131, 1), + CatalogAnimatedFurnitureItem(10020), + CatalogFurnitureItem(10030, 0))), + (12, + 4, + 1, + 4, + (CatalogWallpaperItem(11000), + CatalogWallpaperItem(11100), + CatalogFlooringItem(10010), + CatalogMouldingItem(1090, 0), + CatalogClothingItem(1100, 0), + CatalogClothingItem(1101, 0), + CatalogClothingItem(1104, 0), + CatalogClothingItem(1105, 0), + CatalogClothingItem(1108, 0), + CatalogClothingItem(1109, 0), + CatalogClothingItem(1802, 0))), + (12, + 11, + 1, + 4, + (CatalogFurnitureItem(1040), + CatalogFurnitureItem(1050), + CatalogWallpaperItem(11200), + CatalogFlooringItem(10000), + CatalogMouldingItem(1080, 0), + CatalogMouldingItem(1085, 0), + CatalogClothingItem(1102, 0), + CatalogClothingItem(1103, 0), + CatalogClothingItem(1106, 0), + CatalogClothingItem(1107, 0), + CatalogClothingItem(1110, 0), + CatalogClothingItem(1111, 0))), + (6, + 9, + 7, + 15, + 2010, + 2010, + (CatalogClothingItem(1751, 0),)), + (6, + 14, + 7, + 15, + 2010, + 2010, + (CatalogClothingItem(1754, 0), CatalogClothingItem(1755, 0), CatalogClothingItem(1756, 0))), + (7, + 21, + 8, + 17, + 2010, + 2010, + (CatalogClothingItem(1749, 0), + CatalogClothingItem(1750, 0), + CatalogClothingItem(1757, 0), + CatalogClothingItem(1758, 0))), + (8, + 25, + 9, + 21, + 2010, + 2010, + (CatalogClothingItem(1763, 0),)), + (6, + 5, + 7, + 1, + (CatalogClothingItem(1768, 0), CatalogClothingItem(1769, 0))), + (1, + 1, + 12, + 31, + (CatalogGardenItem(100, 1), + CatalogGardenItem(101, 1), + CatalogGardenItem(103, 1), + CatalogGardenItem(104, 1), + CatalogToonStatueItem(105, endPoseIndex=108), + CatalogRentalItem(1, 2880, 1000), + CatalogGardenStarterItem(), + CatalogFurnitureItem(1530), + CatalogFurnitureItem(1531), + CatalogFurnitureItem(1532), + CatalogNametagItem(15), + CatalogNametagItem(16), + CatalogNametagItem(17), + CatalogClothingItem(1608, 0, True), + CatalogClothingItem(1605, 0, True), + CatalogClothingItem(1602, 0, True), + CatalogClothingItem(1607, 0, True), + CatalogClothingItem(1604, 0, True), + CatalogClothingItem(1601, 0, True), + CatalogClothingItem(1606, 0, True), + CatalogClothingItem(1603, 0, True), + CatalogClothingItem(1600, 0, True), + CatalogEmoteItem(20, True), + CatalogEmoteItem(21, True), + CatalogEmoteItem(22, True), + CatalogEmoteItem(23, True), + CatalogEmoteItem(24, True), + CatalogEmoteItem(25, True), + CatalogClothingItem(1821, 0, True))), + (5, + 26, + 6, + 30, + 2013, + 2013, + (CatalogAccessoryItem(175),)), + (8, + 27, + 9, + 5, + 2013, + 2013, + ((3, 2900), + CatalogChatItem(10003), + CatalogClothingItem(1001, 0), + CatalogClothingItem(1002, 0), + CatalogWallpaperItem(10100), + CatalogWallpaperItem(10200), + CatalogFurnitureItem(10000), + CatalogFurnitureItem(10010), + CatalogNametagItem(9), + CatalogClothingItem(1744, 0), + CatalogClothingItem(1745, 0), + CatalogClothingItem(1748, 0), + CatalogClothingItem(1771, 0), + CatalogClothingItem(1774, 0), + CatalogClothingItem(1775, 0), + CatalogClothingItem(1743, 0), + CatalogClothingItem(1746, 0), + CatalogClothingItem(1747, 0), + CatalogClothingItem(1112, 0), + CatalogClothingItem(1113, 0), + CatalogClothingItem(1114, 0), + CatalogClothingItem(1115, 0), + CatalogClothingItem(1116, 0), + CatalogClothingItem(1117, 0), + CatalogClothingItem(1118, 0), + CatalogClothingItem(1119, 0), + CatalogClothingItem(1120, 0), + CatalogClothingItem(1121, 0), + CatalogClothingItem(1122, 0), + CatalogClothingItem(1123, 0), + CatalogClothingItem(1124, 0), + CatalogClothingItem(1125, 0), + CatalogClothingItem(1126, 0), + CatalogClothingItem(1127, 0), + CatalogAccessoryItem(171), + CatalogAccessoryItem(172), + CatalogAccessoryItem(224), + CatalogAccessoryItem(324), + CatalogAccessoryItem(325), + CatalogAccessoryItem(326), + CatalogAccessoryItem(327), + CatalogAccessoryItem(448), + CatalogAccessoryItem(449), + CatalogClothingItem(1801, 0), + CatalogAccessoryItem(175))), + (9, + 3, + 9, + 12, + 2013, + 2013, + ((3, 2910), + CatalogFurnitureItem(680), + CatalogFurnitureItem(681), + CatalogGardenItem(130, 1), + CatalogGardenItem(131, 1), + CatalogAnimatedFurnitureItem(10020), + CatalogFurnitureItem(10030, 0), + CatalogWallpaperItem(11000), + CatalogWallpaperItem(11100), + CatalogFlooringItem(10010), + CatalogMouldingItem(1090, 0), + CatalogClothingItem(1100, 0), + CatalogClothingItem(1101, 0), + CatalogClothingItem(1104, 0), + CatalogClothingItem(1105, 0), + CatalogClothingItem(1108, 0), + CatalogClothingItem(1109, 0), + CatalogClothingItem(1802, 0), + CatalogFurnitureItem(1040), + CatalogFurnitureItem(1050), + CatalogWallpaperItem(11200), + CatalogFlooringItem(10000), + CatalogMouldingItem(1080, 0), + CatalogMouldingItem(1085, 0), + CatalogClothingItem(1102, 0), + CatalogClothingItem(1103, 0), + CatalogClothingItem(1106, 0), + CatalogClothingItem(1107, 0), + CatalogClothingItem(1110, 0), + CatalogClothingItem(1111, 0))), + (8, + 20, + 9, + 19, + 2013, + 2013, + (CatalogAccessoryItem(101), + CatalogAccessoryItem(103), + CatalogAccessoryItem(117), + CatalogAccessoryItem(118), + CatalogAccessoryItem(123), + CatalogAccessoryItem(124), + CatalogAccessoryItem(125), + CatalogAccessoryItem(126), + CatalogAccessoryItem(127), + CatalogAccessoryItem(128), + CatalogAccessoryItem(129), + CatalogAccessoryItem(130), + CatalogAccessoryItem(202), + CatalogAccessoryItem(204), + CatalogAccessoryItem(205), + CatalogAccessoryItem(206), + CatalogAccessoryItem(208), + CatalogAccessoryItem(209), + CatalogAccessoryItem(210), + CatalogAccessoryItem(302), + CatalogAccessoryItem(308), + CatalogAccessoryItem(309), + CatalogAccessoryItem(310), + CatalogAccessoryItem(317), + CatalogAccessoryItem(402), + CatalogAccessoryItem(403), + CatalogAccessoryItem(405), + CatalogAccessoryItem(406), + CatalogAccessoryItem(407), + CatalogAccessoryItem(408), + CatalogAccessoryItem(409), + CatalogAccessoryItem(410), + CatalogAccessoryItem(411), + CatalogAccessoryItem(412), + CatalogAccessoryItem(413), + CatalogAccessoryItem(306), + CatalogAccessoryItem(318), + CatalogAccessoryItem(121), + CatalogAccessoryItem(212), + CatalogAccessoryItem(214), + CatalogAccessoryItem(312), + CatalogAccessoryItem(150), + CatalogAccessoryItem(151), + CatalogAccessoryItem(147), + CatalogAccessoryItem(422), + CatalogAccessoryItem(141), + CatalogAccessoryItem(146), + CatalogAccessoryItem(444), + CatalogAccessoryItem(122), + CatalogAccessoryItem(430), + CatalogAccessoryItem(145), + CatalogAccessoryItem(132), + CatalogAccessoryItem(161), + CatalogAccessoryItem(134), + CatalogAccessoryItem(149), + CatalogAccessoryItem(207), + CatalogAccessoryItem(215), + CatalogAccessoryItem(216), + CatalogAccessoryItem(417), + CatalogAccessoryItem(222), + CatalogAccessoryItem(321), + CatalogAccessoryItem(322), + CatalogAccessoryItem(307), + CatalogAccessoryItem(135), + CatalogAccessoryItem(174), + CatalogAccessoryItem(434), + CatalogAccessoryItem(435), + CatalogAccessoryItem(441), + CatalogAccessoryItem(446), + CatalogAccessoryItem(429), + CatalogAccessoryItem(110), + CatalogAccessoryItem(148), + CatalogAccessoryItem(443), + CatalogAccessoryItem(426), + CatalogAccessoryItem(439), + CatalogAccessoryItem(143), + CatalogAccessoryItem(313), + CatalogAccessoryItem(311), + CatalogAccessoryItem(437), + CatalogAccessoryItem(415), + CatalogAccessoryItem(167), + CatalogAccessoryItem(157), + CatalogAccessoryItem(106), + CatalogAccessoryItem(109), + CatalogAccessoryItem(421), + CatalogAccessoryItem(401), + CatalogAccessoryItem(447), + CatalogAccessoryItem(213), + CatalogAccessoryItem(330), + CatalogAccessoryItem(440), + CatalogAccessoryItem(425), + CatalogAccessoryItem(158), + CatalogAccessoryItem(431), + CatalogAccessoryItem(420), + CatalogAccessoryItem(155), + CatalogAccessoryItem(419), + CatalogAccessoryItem(436), + CatalogAccessoryItem(428), + CatalogAccessoryItem(304), + CatalogAccessoryItem(301), + CatalogAccessoryItem(416), + CatalogAccessoryItem(414), + CatalogAccessoryItem(164), + CatalogAccessoryItem(323), + CatalogAccessoryItem(108), + CatalogAccessoryItem(139), + CatalogAccessoryItem(316), + CatalogAccessoryItem(131), + CatalogAccessoryItem(170), + CatalogAccessoryItem(221), + CatalogAccessoryItem(225), + CatalogAccessoryItem(305), + CatalogAccessoryItem(303), + CatalogAccessoryItem(144), + CatalogAccessoryItem(120), + CatalogAccessoryItem(116), + CatalogAccessoryItem(217), + CatalogAccessoryItem(218), + CatalogAccessoryItem(219), + CatalogAccessoryItem(445), + CatalogAccessoryItem(418), + CatalogAccessoryItem(432), + CatalogAccessoryItem(427), + CatalogAccessoryItem(423), + CatalogAccessoryItem(137), + CatalogAccessoryItem(163), + CatalogAccessoryItem(165), + CatalogAccessoryItem(153), + CatalogAccessoryItem(319), + CatalogAccessoryItem(154), + CatalogAccessoryItem(159), + CatalogAccessoryItem(162), + CatalogAccessoryItem(315), + CatalogAccessoryItem(160), + CatalogAccessoryItem(102), + CatalogAccessoryItem(119), + CatalogAccessoryItem(136), + CatalogAccessoryItem(169), + CatalogAccessoryItem(140), + CatalogAccessoryItem(168), + CatalogAccessoryItem(138), + CatalogAccessoryItem(220), + CatalogAccessoryItem(433), + CatalogAccessoryItem(442), + CatalogAccessoryItem(424), + CatalogAccessoryItem(404), + CatalogAccessoryItem(156), + CatalogAccessoryItem(142), + CatalogAccessoryItem(152), + CatalogAccessoryItem(133), + CatalogAccessoryItem(166), + CatalogAccessoryItem(211), + CatalogAccessoryItem(314), + CatalogAccessoryItem(320), + CatalogAccessoryItem(173), + CatalogAccessoryItem(328), + CatalogAccessoryItem(329)))) +WeeklySchedule = ((100, + (5, 2000), + 3000, + 3500, + 4000, + 4500, + CatalogEmoteItem(5), + CatalogFurnitureItem(210, 0), + CatalogFurnitureItem(220, 0)), + (100, + (5, 2000), + CatalogFurnitureItem(1400), + 3000, + 3500, + 4000, + 4500, + CatalogFurnitureItem(600), + CatalogFurnitureItem(610), + CatalogClothingItem(116, 0), + CatalogClothingItem(216, 0)), + (300, + (5, 2000), + CatalogFurnitureItem(1410), + 3000, + 3500, + 4000, + 4500, + CatalogFurnitureItem(1100), + CatalogFurnitureItem(1020), + CatalogClothingItem(408, 0), + 5000), + (100, + (5, 2000), + CatalogWindowItem(40), + 3000, + 3500, + 4000, + 4500, + CatalogFurnitureItem(110), + CatalogFurnitureItem(100)), + (100, + (5, 2000), + CatalogFurnitureItem(1420), + CatalogEmoteItem(9), + 3000, + 3500, + 4000, + 4500, + CatalogFurnitureItem(700), + CatalogFurnitureItem(710)), + (300, + (5, 2000), + 3000, + 3500, + 4000, + 4500, + CatalogFurnitureItem(410), + CatalogAnimatedFurnitureItem(490), + CatalogFurnitureItem(1000), + CatalogClothingItem(117, 0), + CatalogClothingItem(217, 0)), + (100, + (5, 2000), + CatalogFurnitureItem(1430), + 3000, + 3500, + 4000, + 4500, + CatalogFurnitureItem(1510), + CatalogFurnitureItem(1610), + 5000, + CatalogNametagItem(1)), + (100, + (5, 2000), + CatalogWindowItem(70), + 3000, + 3500, + 4000, + 4500, + CatalogFurnitureItem(1210), + CatalogClothingItem(409, 0)), + (300, + (5, 2000), + CatalogEmoteItem(13), + 3000, + 3500, + 4000, + 4500, + CatalogFurnitureItem(1200), + CatalogFurnitureItem(900)), + (100, + (5, 2000), + 3000, + 3500, + 4000, + 4500, + CatalogFurnitureItem(910), + CatalogFurnitureItem(1600), + CatalogClothingItem(118, 0), + CatalogClothingItem(218, 0)), + (100, + (5, 2000), + 3000, + 3500, + 4000, + 4500, + CatalogFurnitureItem(800), + CatalogFurnitureItem(1010), + CatalogClothingItem(410, 0), + 5000), + (300, + (5, 2000), + 3000, + 3500, + 4000, + 4500, + CatalogFurnitureItem(620)), + (300, + (5, 2000), + 3000, + 3500, + 4000, + 4500, + CatalogClothingItem(119, 0), + CatalogClothingItem(219, 0)), + (100, + (2, 2000), + (3, 2010), + 3010, + 3510, + 4010, + 4510, + CatalogFurnitureItem(1110), + CatalogFurnitureItem(630), + CatalogFurnitureItem(1630), + CatalogEmoteItem(11), + CatalogNametagItem(11)), + (100, + (2, 2000), + (3, 2010), + 3010, + 3510, + 4010, + 4510, + CatalogFurnitureItem(230), + CatalogFurnitureItem(920), + CatalogFurnitureItem(1440)), + (300, + (2, 2000), + (3, 2010), + 3010, + 3510, + 4010, + 4510, + CatalogFurnitureItem(420), + CatalogAnimatedFurnitureItem(480), + CatalogFurnitureItem(120), + CatalogClothingItem(120, 0), + CatalogClothingItem(220, 0), + 5000), + (100, + (2, 2000), + (3, 2010), + 3010, + 3510, + 4010, + 4510, + CatalogFurnitureItem(1700), + CatalogFurnitureItem(640), + CatalogWindowItem(50)), + (100, + (2, 2000), + (3, 2010), + 3010, + 3510, + 4010, + 4510, + CatalogFurnitureItem(1120), + CatalogFurnitureItem(930), + CatalogFurnitureItem(1500), + CatalogEmoteItem(6)), + (300, + (2, 2000), + (3, 2010), + 3010, + 3510, + 4010, + 4510, + CatalogFurnitureItem(430), + CatalogAnimatedFurnitureItem(491), + CatalogFurnitureItem(1620), + CatalogFurnitureItem(1442)), + (100, + (2, 2000), + (3, 2010), + 3010, + 3510, + 4010, + 4510, + CatalogFurnitureItem(610), + CatalogFurnitureItem(940), + CatalogClothingItem(121, 0), + CatalogClothingItem(221, 0), + 5000), + (100, + (2, 2000), + (3, 2010), + 3010, + 3510, + 4010, + 4510, + CatalogFurnitureItem(1710), + CatalogFurnitureItem(1030), + CatalogWindowItem(60), + CatalogNametagItem(7)), + (300, + (2, 2000), + (3, 2010), + 3010, + 3510, + 4010, + 4510, + CatalogFurnitureItem(1130), + CatalogFurnitureItem(130), + CatalogEmoteItem(8)), + (100, + (2, 2000), + (3, 2010), + 3010, + 3510, + 4010, + 4510, + CatalogFurnitureItem(1640), + CatalogFurnitureItem(1441)), + (100, + (2, 2000), + (3, 2010), + 3010, + 3510, + 4010, + 4510, + CatalogFurnitureItem(300), + CatalogFurnitureItem(1220), + 5000), + (300, + (2, 2000), + (3, 2010), + 3010, + 3510, + 4010, + 4510, + CatalogFurnitureItem(810), + CatalogFurnitureItem(1230), + CatalogFurnitureItem(1443)), + (300, + (2, 2000), + (3, 2010), + 3010, + 3510, + 4010, + 4510, + CatalogFurnitureItem(310), + CatalogFurnitureItem(1520), + CatalogFurnitureItem(1650), + CatalogWindowItem(80), + CatalogClothingItem(222, 0)), + (100, + (1, 2000), + (2, 2010), + (3, 2020), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(1240), + CatalogFurnitureItem(1661), + CatalogEmoteItem(5)), + (100, + (1, 2000), + (2, 2010), + (3, 2020), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(1800), + CatalogFurnitureItem(240), + CatalogFurnitureItem(1200), + CatalogNametagItem(12)), + (300, + (1, 2000), + (2, 2010), + (3, 2020), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(145), + CatalogClothingItem(123, 0), + CatalogClothingItem(224, 0), + 5000), + (100, + (1, 2000), + (2, 2010), + (3, 2020), + 3020, + 3530, + 4020, + 4520, + CatalogWindowItem(100), + CatalogFurnitureItem(1810)), + (100, + (1, 2000), + (2, 2010), + (3, 2020), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(650), + CatalogFurnitureItem(1900)), + (300, + (1, 2000), + (2, 2010), + (3, 2020), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(1725)), + (100, + (1, 2000), + (2, 2010), + (3, 2020), + 3020, + 3530, + 4020, + 4520, + CatalogWindowItem(90), + CatalogClothingItem(124, 0), + CatalogClothingItem(411, 0)), + (100, + (1, 2000), + (2, 2010), + (3, 2020), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(140), + CatalogFurnitureItem(1020), + CatalogEmoteItem(13)), + (300, + (1, 2000), + (2, 2010), + (3, 2020), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(950), + CatalogFurnitureItem(1660), + CatalogClothingItem(310, 0), + CatalogNametagItem(2)), + (100, + (1, 2000), + (2, 2010), + (3, 2020), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(400), + CatalogAnimatedFurnitureItem(470), + CatalogFurnitureItem(660), + CatalogFurnitureItem(1200), + 5000), + (100, + (1, 2000), + (2, 2010), + (3, 2020), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(1910), + CatalogFurnitureItem(1000)), + (300, + (1, 2000), + (2, 2010), + (3, 2020), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(1720), + CatalogEmoteItem(9)), + (300, + (1, 2000), + (2, 2010), + (3, 2020), + 3020, + 3530, + 4020, + 4520, + CatalogWindowItem(110), + CatalogClothingItem(311, 0)), + (100, + (1, 2010), + (2, 2020), + (3, 2030), + 3020, + 3530, + 4020, + 4520, + CatalogWindowItem(120), + CatalogClothingItem(125, 0), + 5000), + (300, + (1, 2010), + (2, 2020), + (3, 2030), + 3020, + 3530, + 4020, + 4520, + CatalogClothingItem(412, 0), + CatalogClothingItem(312, 0), + CatalogFurnitureItem(1920)), + (100, + (1, 2010), + (2, 2020), + (3, 2030), + 3020, + 3530, + 4020, + 4520, + CatalogWallpaperItem(3900), + CatalogFurnitureItem(980), + CatalogNametagItem(13)), + (300, + (1, 2010), + (2, 2020), + (3, 2030), + 3020, + 3530, + 4020, + 4520, + CatalogClothingItem(130, 0), + CatalogFurnitureItem(150), + CatalogNametagItem(14)), + (100, + (1, 2010), + (2, 2020), + (3, 2030), + 3020, + 3530, + 4020, + 4520, + CatalogClothingItem(128, 0), + CatalogWallpaperItem(3700), + CatalogFurnitureItem(160)), + (300, + (1, 2010), + (2, 2020), + (3, 2030), + 3020, + 3530, + 4020, + 4520, + CatalogClothingItem(313, 0), + CatalogClothingItem(413, 0), + CatalogFurnitureItem(960), + CatalogEmoteItem(7)), + (100, + (1, 2010), + (2, 2020), + (3, 2030), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(1930), + CatalogFurnitureItem(670)), + (300, + (1, 2010), + (2, 2020), + (3, 2030), + 3020, + 3530, + 4020, + 4520, + CatalogClothingItem(126, 0), + CatalogFurnitureItem(1970), + 5000), + (100, + (1, 2010), + (2, 2020), + (3, 2030), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(720), + CatalogFurnitureItem(970)), + (300, + (1, 2010), + (2, 2020), + (3, 2030), + 3020, + 3530, + 4020, + 4520, + CatalogClothingItem(127, 0), + CatalogFurnitureItem(1950), + CatalogNametagItem(4)), + (100, + (1, 2010), + (2, 2020), + (3, 2030), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(1940), + CatalogWindowItem(130)), + (300, + (1, 2010), + (2, 2020), + (3, 2030), + 3020, + 3530, + 4020, + 4520, + CatalogWallpaperItem(3800), + CatalogClothingItem(129, 0), + CatalogEmoteItem(10)), + (100, + (1, 2010), + (2, 2020), + (3, 2030), + 3020, + 3530, + 4020, + 4520, + CatalogFurnitureItem(250), + CatalogFurnitureItem(1960)), + Sale(CatalogFurnitureItem(210, 0), CatalogFurnitureItem(220, 0), CatalogFurnitureItem(1100), CatalogFurnitureItem(110), CatalogFurnitureItem(100), CatalogFurnitureItem(700), CatalogFurnitureItem(710), CatalogFurnitureItem(410), CatalogAnimatedFurnitureItem(490), CatalogFurnitureItem(1210), CatalogFurnitureItem(1200), CatalogFurnitureItem(800), CatalogFurnitureItem(1110), CatalogFurnitureItem(230), CatalogFurnitureItem(420), CatalogAnimatedFurnitureItem(480), CatalogFurnitureItem(120), CatalogFurnitureItem(1700), CatalogFurnitureItem(1120), CatalogFurnitureItem(430), CatalogAnimatedFurnitureItem(491), CatalogFurnitureItem(1130), CatalogFurnitureItem(130), CatalogFurnitureItem(300), CatalogFurnitureItem(1220), CatalogFurnitureItem(810), CatalogFurnitureItem(1230), CatalogFurnitureItem(310), CatalogFurnitureItem(1240), CatalogFurnitureItem(240), CatalogFurnitureItem(145), CatalogFurnitureItem(1725), CatalogFurnitureItem(140), CatalogFurnitureItem(950), CatalogFurnitureItem(1720)), + Sale(CatalogClothingItem(116, 0), CatalogClothingItem(216, 0), CatalogClothingItem(408, 0), CatalogClothingItem(117, 0), CatalogClothingItem(217, 0), CatalogClothingItem(409, 0), CatalogClothingItem(118, 0), CatalogClothingItem(218, 0), CatalogClothingItem(410, 0), CatalogClothingItem(119, 0), CatalogClothingItem(219, 0), CatalogClothingItem(120, 0), CatalogClothingItem(220, 0), CatalogClothingItem(121, 0), CatalogClothingItem(221, 0), CatalogClothingItem(222, 0), CatalogClothingItem(123, 0), CatalogClothingItem(224, 0), CatalogClothingItem(411, 0), CatalogClothingItem(311, 0), CatalogClothingItem(310, 0)), + Sale(CatalogWindowItem(40), CatalogWindowItem(70), CatalogWindowItem(50), CatalogWindowItem(60), CatalogWindowItem(80), CatalogWindowItem(100), CatalogWindowItem(90), CatalogWindowItem(110)), + Sale(CatalogEmoteItem(5), CatalogEmoteItem(9), CatalogEmoteItem(13), CatalogEmoteItem(11), CatalogEmoteItem(6), CatalogEmoteItem(8), CatalogNametagItem(10)), + Sale(CatalogFurnitureItem(600), CatalogFurnitureItem(610), CatalogFurnitureItem(620), CatalogFurnitureItem(630), CatalogFurnitureItem(640), CatalogFurnitureItem(650), CatalogFurnitureItem(660), CatalogFurnitureItem(900), CatalogFurnitureItem(910), CatalogFurnitureItem(920), CatalogFurnitureItem(930), CatalogFurnitureItem(940), CatalogFurnitureItem(1000), CatalogFurnitureItem(1010), CatalogFurnitureItem(1020), CatalogFurnitureItem(1030), CatalogFurnitureItem(1400), CatalogFurnitureItem(1410), CatalogFurnitureItem(1420), CatalogFurnitureItem(1430), CatalogFurnitureItem(1440), CatalogFurnitureItem(1441), CatalogFurnitureItem(1442), CatalogFurnitureItem(1443), CatalogFurnitureItem(1500), CatalogFurnitureItem(1510), CatalogFurnitureItem(1520), CatalogFurnitureItem(1530), CatalogFurnitureItem(1600), CatalogFurnitureItem(1531), CatalogFurnitureItem(1532), CatalogFurnitureItem(1610), CatalogFurnitureItem(1620), CatalogFurnitureItem(1630), CatalogFurnitureItem(1640), CatalogFurnitureItem(1650), CatalogFurnitureItem(1660), CatalogFurnitureItem(1661), CatalogFurnitureItem(1710), CatalogFurnitureItem(1800), CatalogFurnitureItem(1810), CatalogFurnitureItem(1900), CatalogFurnitureItem(1910)), + (300, + (1, 2020), + (2, 2030), + (3, 2040), + CatalogFurnitureItem(730)), + (100, + (1, 2020), + (2, 2030), + (3, 2040), + CatalogFurnitureItem(260)), + (300, + (1, 2020), + (2, 2030), + (3, 2040), + CatalogFurnitureItem(440), + CatalogAnimatedFurnitureItem(492), + 5000), + (100, + (1, 2020), + (2, 2030), + (3, 2040), + CatalogFurnitureItem(170), + CatalogFurnitureItem(1250)), + (300, + (1, 2020), + (2, 2030), + (3, 2040), + CatalogFurnitureItem(1140)), + (100, + (1, 2020), + (2, 2030), + (3, 2040), + CatalogFurnitureItem(2010), + CatalogNametagItem(8)), + (300, + (1, 2020), + (2, 2030), + (3, 2040), + CatalogFurnitureItem(2000), + 5000), + (100, + (1, 2020), + (2, 2030), + (3, 2040), + CatalogFurnitureItem(3000)), + (300, + (1, 2030), + (2, 2040), + (3, 2050), + CatalogClothingItem(131, 0), + CatalogClothingItem(225, 0)), + (300, + (1, 2030), + (2, 2040), + (3, 2050), + CatalogFurnitureItem(105)), + (300, + (1, 2030), + (2, 2040), + (3, 2050), + CatalogFurnitureItem(205)), + (300, + (1, 2030), + (2, 2040), + (3, 2050), + CatalogFurnitureItem(625)), + (300, + (1, 2030), + (2, 2040), + (3, 2050), + CatalogEmoteItem(12), + CatalogNametagItem(5)), + (300, + (1, 2030), + (2, 2040), + (3, 2050), + CatalogClothingItem(314, 0), + CatalogClothingItem(414, 0)), + (300, + (1, 2030), + (2, 2040), + (3, 2050), + CatalogFurnitureItem(715)), + (300, + (1, 2030), + (2, 2040), + (3, 2050), + CatalogFurnitureItem(1015), + CatalogNametagItem(6)), + (300, + (1, 2030), + (2, 2040), + (3, 2050), + CatalogFurnitureItem(1215)), + (300, + (1, 2030), + (2, 2040), + (3, 2050), + CatalogEmoteItem(14)), + (300, + (1, 2030), + (2, 2040), + (3, 2050), + CatalogFurnitureItem(1260)), + (300, + (1, 2030), + (2, 2040), + (3, 2050), + CatalogFurnitureItem(705), + CatalogNametagItem(3)), + (300, + (1, 2030), + (2, 2040), + (3, 2050))) + +class CatalogGenerator: + notify = DirectNotifyGlobal.directNotify.newCategory('CatalogGenerator') + + def __init__(self): + self.__itemLists = {} + self.__releasedItemLists = {} + + def getReleasedCatalogList(self, weekStart): + dayNumber = int(weekStart / (24 * 60)) + itemLists = self.__getReleasedItemLists(dayNumber, weekStart) + return itemLists + + def generateMonthlyCatalog(self, avatar, weekStart): + dayNumber = int(weekStart / (24 * 60)) + itemLists = self.__getMonthlyItemLists(dayNumber, weekStart) + monthlyCatalog = CatalogItemList.CatalogItemList() + for list in itemLists: + saleItem = 0 + if isinstance(list, Sale): + list = list.args + saleItem = 1 + for item in list: + monthlyCatalog += self.__selectItem(avatar, item, [], saleItem=saleItem) + + return monthlyCatalog + + def generateWeeklyCatalog(self, avatar, week, monthlyCatalog): + weeklyCatalog = CatalogItemList.CatalogItemList() + self.notify.debug('Generating catalog for %s for week %s.' % (avatar.doId, week)) + if week >= 1 and week <= len(WeeklySchedule): + saleItem = 0 + schedule = WeeklySchedule[week - 1] + if isinstance(schedule, Sale): + schedule = schedule.args + saleItem = 1 + for item in schedule: + weeklyCatalog += self.__selectItem(avatar, item, monthlyCatalog, saleItem=saleItem) + + if nextAvailableCloset not in schedule: + weeklyCatalog += self.__selectItem(avatar, nextAvailableCloset, monthlyCatalog, saleItem=0) + if nextAvailableBank not in schedule: + weeklyCatalog += self.__selectItem(avatar, nextAvailableBank, monthlyCatalog, saleItem = 0) + if nextAvailableTank not in schedule: + weeklyCatalog += self.__selectItem(avatar, nextAvailableTank, monthlyCatalog, saleItem = 0) + if nextAvailablePole not in schedule: + weeklyCatalog += self.__selectItem(avatar, nextAvailablePole, monthlyCatalog, saleItem = 0) + + def hasPetTrick(catalog): + for item in catalog: + if isinstance(item, CatalogPetTrickItem): + return 1 + + return 0 + + if not hasPetTrick(weeklyCatalog) and not hasPetTrick(avatar.weeklyCatalog) and not hasPetTrick(avatar.backCatalog): + self.notify.debug('Artificially adding pet trick to catalog') + weeklyCatalog += self.__selectItem(avatar, 5000, monthlyCatalog, saleItem=saleItem) + + self.notify.debug('Generated catalog: %s' % weeklyCatalog) + return weeklyCatalog + + def generateBackCatalog(self, avatar, week, previousWeek, weeklyCatalog): + backCatalog = CatalogItemList.CatalogItemList() + lastBackCatalog = avatar.backCatalog[:] + thisWeek = min(len(WeeklySchedule), week - 1) + lastWeek = min(len(WeeklySchedule), previousWeek) + for week in xrange(thisWeek, lastWeek, -1): + self.notify.debug('Adding items from week %s to back catalog' % week) + schedule = WeeklySchedule[week - 1] + if not isinstance(schedule, Sale): + for item in schedule: + for item in self.__selectItem(avatar, item, weeklyCatalog + backCatalog): + item.putInBackCatalog(backCatalog, lastBackCatalog) + + if previousWeek < week: + self.notify.debug('Adding current items from week %s to back catalog' % previousWeek) + for item in avatar.weeklyCatalog: + item.putInBackCatalog(backCatalog, lastBackCatalog) + + backCatalog += lastBackCatalog + for item in weeklyCatalog: + while item in backCatalog: + backCatalog.remove(item) + + return backCatalog + + def __getReleasedItemLists(self, dayNumber, weekStart): + itemLists = self.__releasedItemLists.get(dayNumber) + if itemLists != None: + return itemLists + else: + self.__releasedItemLists.clear() + testDaysAhead = simbase.config.GetInt('test-server-holiday-days-ahead', 0) + nowtuple = time.localtime(weekStart * 60 + testDaysAhead * 24 * 60 * 60) + year = nowtuple[0] + month = nowtuple[1] + day = nowtuple[2] + itemLists = [] + for monthlyItems in MonthlySchedule: + startMM = monthlyItems[0] + startDD = monthlyItems[1] + endMM = monthlyItems[2] + endDD = monthlyItems[3] + if len(monthlyItems) == 7: + startYYYY = monthlyItems[4] + endYYYY = monthlyItems[5] + list = monthlyItems[6] + else: + startYYYY = 1969 + endYYYY = year + list = monthlyItems[4] + pastStart = year > startYYYY or (year == startYYYY and (month > startMM or (month == startMM and day >= startDD))) + if pastStart: + itemLists.append(list) + + self.__releasedItemLists[dayNumber] = itemLists + return itemLists + + def __getMonthlyItemLists(self, dayNumber, weekStart): + itemLists = self.__itemLists.get(dayNumber) + if itemLists != None: + return itemLists + testDaysAhead = simbase.config.GetInt('test-server-holiday-days-ahead', 0) + nowtuple = time.localtime(weekStart * 60 + testDaysAhead * 24 * 60 * 60) + year = nowtuple[0] + month = nowtuple[1] + day = nowtuple[2] + self.notify.debug('Generating seasonal itemLists for %s/%s.' % (month, day)) + itemLists = [] + for monthlyItems in MonthlySchedule: + startMM = monthlyItems[0] + startDD = monthlyItems[1] + endMM = monthlyItems[2] + endDD = monthlyItems[3] + if len(monthlyItems) == 7: + startYYYY = monthlyItems[4] + endYYYY = monthlyItems[5] + list = monthlyItems[6] + else: + startYYYY = 1969 + endYYYY = year + list = monthlyItems[4] + pastStart = year >= startYYYY and (month > startMM or (month == startMM and day >= startDD)) + beforeEnd = year <= endYYYY and (month < endMM or (month == endMM and day <= endDD)) + if endMM < startMM: + if pastStart or beforeEnd: + itemLists.append(list) + elif pastStart and beforeEnd: + itemLists.append(list) + + self.__itemLists[dayNumber] = itemLists + return itemLists + + def __selectItem(self, avatar, item, duplicateItems, saleItem = 0): + chooseCount = 1 + if isinstance(item, Sale): + item = item.args[0] + saleItem = 1 + if callable(item): + item = item(avatar, duplicateItems) + if isinstance(item, types.TupleType): + chooseCount, item = item + if isinstance(item, types.IntType): + item = MetaItems[item] + selection = [] + if isinstance(item, CatalogItem.CatalogItem): + if not item.notOfferedTo(avatar): + item.saleItem = saleItem + selection.append(item) + elif item != None: + list = item[:] + for i in xrange(chooseCount): + if len(list) == 0: + return selection + item = self.__chooseFromList(avatar, list, duplicateItems) + if item != None: + item.saleItem = saleItem + selection.append(item) + + return selection + + def __chooseFromList(self, avatar, list, duplicateItems): + index = random.randrange(len(list)) + item = list[index] + del list[index] + while item.notOfferedTo(avatar) or item.reachedPurchaseLimit(avatar) or item in duplicateItems or item in avatar.backCatalog or item in avatar.weeklyCatalog: + if len(list) == 0: + return None + index = random.randrange(len(list)) + item = list[index] + del list[index] + + return item + + def outputSchedule(self, filename): + out = open(Filename(filename).toOsSpecific(), 'w') + sched = self.generateScheduleDictionary() + items = sched.keys() + items.sort() + for item in items: + weeklist, maybeWeeklist = sched[item] + color = self.__formatColor(item.getColor()) + seriesDict = {} + self.__determineSeries(seriesDict, weeklist) + self.__determineSeries(seriesDict, maybeWeeklist) + seriesList = seriesDict.keys() + seriesList.sort() + series = str(seriesList)[1:-1] + week = self.__formatWeeklist(weeklist) + maybeWeek = self.__formatWeeklist(maybeWeeklist) + line = '"%s"\t"%s"\t"%s"\t%s\t"%s"\t"%s"\t"%s"\t"%s"\t"%s"' % (item.output(store=0), + item.getTypeName(), + item.getDisplayName(), + item.getBasePrice(), + item.getFilename(), + color, + series, + week, + maybeWeek) + out.write(line + '\n') + + out.close() + + def __formatColor(self, color): + if color == None: + return '' + else: + return '(%0.2f, %0.2f, %0.2f)' % (color[0], color[1], color[2]) + return + + def __determineSeries(self, seriesDict, weeklist): + for week in weeklist: + if isinstance(week, types.IntType): + series = (week - 1) / ToontownGlobals.CatalogNumWeeksPerSeries + 1 + seriesDict[series] = None + + return + + def __formatWeeklist(self, weeklist): + str = '' + for week in weeklist: + str += ', %s' % week + + return str[2:] + + def generateScheduleDictionary(self): + sched = {} + for index in xrange(len(WeeklySchedule)): + week = index + 1 + schedule = WeeklySchedule[index] + if isinstance(schedule, Sale): + schedule = schedule.args + self.__recordSchedule(sched, week, schedule) + + for monthlyItems in MonthlySchedule: + startMM = monthlyItems[0] + startDD = monthlyItems[1] + endMM = monthlyItems[2] + endDD = monthlyItems[3] + if len(monthlyItems) == 7: + list = monthlyItems[6] + else: + list = monthlyItems[4] + string = '%02d/%02d - %02d/%02d' % (startMM, + startDD, + endMM, + endDD) + self.__recordSchedule(sched, string, list) + + return sched + + def __recordSchedule(self, sched, weekCode, schedule): + if isinstance(schedule, Sale): + schedule = schedule.args + for item in schedule: + if callable(item): + if item == nextAvailablePole: + item = getAllPoles() + elif item == nextAvailableCloset: + item = getAllClosets() + elif item == nextAvailableBank: + item = getAllBanks() + elif item == nextAvailableTank: + item == getAllTanks() + elif item == get50ItemCloset: + item = getMaxClosets() + else: + self.notify.warning("Don't know how to interpret function " % repr(name)) + item = None + elif isinstance(item, types.TupleType): + item = item[1] + if isinstance(item, types.IntType): + item = MetaItems[item] + if isinstance(item, CatalogItem.CatalogItem): + self.__recordScheduleItem(sched, weekCode, None, item) + elif item != None: + for i in item: + self.__recordScheduleItem(sched, None, weekCode, i) + + return + + def __recordScheduleItem(self, sched, weekCode, maybeWeekCode, item): + if not item in sched: + sched[item] = [[], []] + if weekCode != None: + sched[item][0].append(weekCode) + if maybeWeekCode != None: + sched[item][1].append(maybeWeekCode) + return diff --git a/toontown/catalog/CatalogHouseItem.py b/toontown/catalog/CatalogHouseItem.py new file mode 100755 index 00000000..35b539ed --- /dev/null +++ b/toontown/catalog/CatalogHouseItem.py @@ -0,0 +1,71 @@ +import CatalogItem +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil +from direct.gui.DirectGui import * +from toontown.toonbase import ToontownGlobals +from toontown.estate import HouseGlobals + +class CatalogHouseItem(CatalogItem.CatalogItem): + def makeNewItem(self, houseId): + self.houseId = houseId + CatalogItem.CatalogItem.makeNewItem(self) + + def notOfferedTo(self, avatar): + return 1 + + def requestPurchase(self, phone, callback): + from toontown.toontowngui import TTDialog + avatar = base.localAvatar + + self.requestPurchaseCleanup() + buttonCallback = PythonUtil.Functor(self.__handleFullPurchaseDialog, phone, callback) + self.dialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.CatalogPurchaseHouseType, text_wordwrap=15, command=buttonCallback) + self.dialog.show() + + def requestPurchaseCleanup(self): + if hasattr(self, 'dialog'): + self.dialog.cleanup() + del self.dialog + + def __handleFullPurchaseDialog(self, phone, callback, buttonValue): + from toontown.toontowngui import TTDialog + self.requestPurchaseCleanup() + if buttonValue == DGG.DIALOG_OK: + CatalogItem.CatalogItem.requestPurchase(self, phone, callback) + else: + callback(ToontownGlobals.P_UserCancelled, self) + + def getTypeName(self): + return "House Type" + + def getName(self): + return TTLocalizer.HouseNames[self.houseId] + + def getDeliveryTime(self): + return 0 + + def getEmblemPrices(self): + return HouseGlobals.HouseEmblemPrices[self.houseId] + + def getPicture(self, avatar): + model = loader.loadModel(HouseGlobals.houseModels[self.houseId]) + model.setBin('unsorted', 0, 1) + self.hasPicture = True + return self.makeFrameModel(model) + + def decodeDatagram(self, di, versionNumber, store): + CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store) + self.houseId = di.getUint8() + + def encodeDatagram(self, dg, store): + CatalogItem.CatalogItem.encodeDatagram(self, dg, store) + dg.addUint8(self.houseId) + + def recordPurchase(self, av, optional): + house = simbase.air.doId2do.get(av.getHouseId()) + if house: + house.b_setHouseType(self.houseId) + return ToontownGlobals.P_ItemAvailable + +def getAllHouses(): + return [CatalogHouseItem(i) for i in xrange(6)] diff --git a/toontown/catalog/CatalogInvalidItem.py b/toontown/catalog/CatalogInvalidItem.py new file mode 100755 index 00000000..15ee9b41 --- /dev/null +++ b/toontown/catalog/CatalogInvalidItem.py @@ -0,0 +1,15 @@ +import CatalogItem +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil +from toontown.toonbase import ToontownGlobals + +class CatalogInvalidItem(CatalogItem.CatalogItem): + + def requestPurchase(self, phone, callback): + self.notify.error('Attempt to purchase invalid item.') + + def acceptItem(self, mailbox, index, callback): + self.notify.error('Attempt to accept invalid item.') + + def output(self, store = -1): + return 'CatalogInvalidItem()' diff --git a/toontown/catalog/CatalogItem.py b/toontown/catalog/CatalogItem.py new file mode 100755 index 00000000..f4633050 --- /dev/null +++ b/toontown/catalog/CatalogItem.py @@ -0,0 +1,439 @@ +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from direct.interval.IntervalGlobal import * +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.PyDatagramIterator import PyDatagramIterator +import types +import sys +CatalogReverseType = None +CatalogItemVersion = 0 +CatalogBackorderMarkup = 1.2 +CatalogSaleMarkdown = 0.75 +Customization = 1 +DeliveryDate = 2 +Location = 4 +WindowPlacement = 8 +GiftTag = 16 +CatalogTypeUnspecified = 0 +CatalogTypeWeekly = 1 +CatalogTypeBackorder = 2 +CatalogTypeMonthly = 3 +CatalogTypeSpecial = 4 + +class CatalogItem: + notify = DirectNotifyGlobal.directNotify.newCategory('CatalogItem') + + def __init__(self, *args, **kw): + self.saleItem = 0 + self.deliveryDate = None + self.posHpr = None + self.giftTag = None + self.giftCode = 0 + self.hasPicture = False + self.isSpecial = False + self.volume = 0 + self.specialEventId = 0 + if len(args) >= 1 and isinstance(args[0], DatagramIterator): + self.decodeDatagram(*args, **kw) + else: + self.makeNewItem(*args, **kw) + return + + def isAward(self): + #result = self.specialEventId != 0 + return False + + def makeNewItem(self): + pass + + def needsCustomize(self): + return 0 + + def saveHistory(self): + return 0 + + def getBackSticky(self): + itemType = 0 + numSticky = 0 + return (itemType, numSticky) + + def putInBackCatalog(self, backCatalog, lastBackCatalog): + if self.saveHistory() and not self.isSaleItem(): + if self not in backCatalog: + if self in lastBackCatalog: + lastBackCatalog.remove(self) + backCatalog.append(self) + + def replacesExisting(self): + return 0 + + def hasExisting(self): + return 0 + + def getYourOldDesc(self): + return None + + def storedInCloset(self): + return 0 + + def storedInAttic(self): + return 0 + + def notOfferedTo(self, avatar): + return 0 + + def getPurchaseLimit(self): + return 0 + + def reachedPurchaseLimit(self, avatar): + return 0 + + def hasBeenGifted(self, avatar): + if avatar.onGiftOrder.count(self) != 0: + return 1 + return 0 + + def getTypeName(self): + return 'Unknown Type Item' + + def getName(self): + return 'Unnamed Item' + + def getDisplayName(self): + return self.getName() + + def recordPurchase(self, avatar, optional): + self.notify.warning('%s has no purchase method.' % self) + return ToontownGlobals.P_NoPurchaseMethod + + def isSaleItem(self): + return self.saleItem + + def isGift(self): + if self.getEmblemPrices(): + return 0 + return 1 + + def isRental(self): + return 0 + + def forBoysOnly(self): + return 0 + + def forGirlsOnly(self): + return 0 + + def getIsSpecial(self): + return self.isSpecial + + def getPrice(self, catalogType): + if catalogType == CatalogTypeBackorder: + return self.getBackPrice() + elif self.isSaleItem(): + return self.getSalePrice() + else: + return self.getCurrentPrice() + + def getCurrentPrice(self): + return int(self.getBasePrice()) + + def getBackPrice(self): + return int(self.getBasePrice() * CatalogBackorderMarkup) + + def getSalePrice(self): + return int(self.getBasePrice() * CatalogSaleMarkdown) + + def getDeliveryTime(self): + return 1 + + def getPicture(self, avatar): + self.hasPicture = True + return (None, None) + + def cleanupPicture(self): + self.hasPicture = False + + def requestPurchase(self, phone, callback, optional = -1): + phone.requestPurchase(self, callback, optional) + + def requestGiftPurchase(self, phone, targetDoID, callback, optional = -1): + phone.requestGiftPurchase(self, targetDoID, callback, optional) + + def requestPurchaseCleanup(self): + pass + + def getRequestPurchaseErrorText(self, retcode): + if retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.CatalogPurchaseItemAvailable + elif retcode == ToontownGlobals.P_ItemOnOrder: + return TTLocalizer.CatalogPurchaseItemOnOrder + elif retcode == ToontownGlobals.P_MailboxFull: + return TTLocalizer.CatalogPurchaseMailboxFull + elif retcode == ToontownGlobals.P_OnOrderListFull: + return TTLocalizer.CatalogPurchaseOnOrderListFull + else: + return TTLocalizer.CatalogPurchaseGeneralError % retcode + + def getRequestGiftPurchaseErrorText(self, retcode): + if retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.CatalogPurchaseGiftItemAvailable + elif retcode == ToontownGlobals.P_ItemOnOrder: + return TTLocalizer.CatalogPurchaseGiftItemOnOrder + elif retcode == ToontownGlobals.P_MailboxFull: + return TTLocalizer.CatalogPurchaseGiftMailboxFull + elif retcode == ToontownGlobals.P_OnOrderListFull: + return TTLocalizer.CatalogPurchaseGiftOnOrderListFull + elif retcode == ToontownGlobals.P_NotAGift: + return TTLocalizer.CatalogPurchaseGiftNotAGift + elif retcode == ToontownGlobals.P_WillNotFit: + return TTLocalizer.CatalogPurchaseGiftWillNotFit + elif retcode == ToontownGlobals.P_ReachedPurchaseLimit: + return TTLocalizer.CatalogPurchaseGiftLimitReached + elif retcode == ToontownGlobals.P_NotEnoughMoney: + return TTLocalizer.CatalogPurchaseGiftNotEnoughMoney + elif retcode == ToontownGlobals.P_TooFast: + return TTLocalizer.CatalogPurchaseGiftTooFast + else: + return TTLocalizer.CatalogPurchaseGiftGeneralError % {'friend': '%s', + 'error': retcode} + + def acceptItem(self, mailbox, index, callback): + mailbox.acceptItem(self, index, callback) + + def discardItem(self, mailbox, index, callback): + print 'Item discardItem' + mailbox.discardItem(self, index, callback) + + def acceptItemCleanup(self): + pass + + def getAcceptItemErrorText(self, retcode): + if retcode == ToontownGlobals.P_NoRoomForItem: + return TTLocalizer.CatalogAcceptRoomError + elif retcode == ToontownGlobals.P_ReachedPurchaseLimit: + return TTLocalizer.CatalogAcceptLimitError + elif retcode == ToontownGlobals.P_WillNotFit: + return TTLocalizer.CatalogAcceptFitError + elif retcode == ToontownGlobals.P_InvalidIndex: + return TTLocalizer.CatalogAcceptInvalidError + else: + return TTLocalizer.CatalogAcceptGeneralError % retcode + + def output(self, store = -1): + return 'CatalogItem' + + def getFilename(self): + return '' + + def getColor(self): + return None + + def formatOptionalData(self, store = -1): + result = '' + if store & Location and self.posHpr != None: + result += ', posHpr = (%s, %s, %s, %s, %s, %s)' % self.posHpr + return result + + def __str__(self): + return self.output() + + def __repr__(self): + return self.output() + + def compareTo(self, other): + return 0 + + def getHashContents(self): + return None + + def __cmp__(self, other): + c = cmp(self.__class__, other.__class__) + if c != 0: + return c + return self.compareTo(other) + + def __hash__(self): + return hash((self.__class__, self.getHashContents())) + + def getBasePrice(self): + return 0 + + def getEmblemPrices(self): + return () + + def hasEmblemPrices(self): + return len(self.getEmblemPrices()) >= ToontownGlobals.NumEmblemTypes + + def loadModel(self): + return None + + def decodeDatagram(self, di, versionNumber, store): + if store & DeliveryDate: + self.deliveryDate = di.getUint32() + if store & Location: + x = di.getArg(STInt16, 10) + y = di.getArg(STInt16, 10) + z = di.getArg(STInt16, 100) + h = di.getArg(STInt16, 256.0 / 360.0) + p = di.getArg(STInt16, 256.0 / 360.0) + r = di.getArg(STInt16, 256.0 / 360.0) + self.posHpr = (x, + y, + z, + h, + p, + r) + if store & GiftTag: + self.giftTag = di.getUint32() + self.specialEventId = di.getUint8() + + def encodeDatagram(self, dg, store): + if store & DeliveryDate: + dg.addUint32(self.deliveryDate) + if store & Location: + dg.putArg(self.posHpr[0], STInt16, 10) + dg.putArg(self.posHpr[1], STInt16, 10) + dg.putArg(self.posHpr[2], STInt16, 100) + dg.putArg(self.posHpr[3], STInt16, 256.0 / 360.0) + dg.putArg(self.posHpr[4], STInt16, 256.0 / 360.0) + dg.putArg(self.posHpr[5], STInt16, 256.0 / 360.0) + if store & GiftTag: + dg.addString(self.giftTag) + dg.addUint8(self.specialEventId) + + def getTypeCode(self): + import CatalogItemTypes + return CatalogItemTypes.CatalogItemTypes[self.__class__] + + def applyColor(self, model, colorDesc): + if model == None or colorDesc == None: + return + for partName, color in colorDesc: + matches = model.findAllMatches(partName) + if color == None: + matches.hide() + elif isinstance(color, types.StringType): + tex = loader.loadTexture(color) + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + for i in xrange(matches.getNumPaths()): + matches.getPath(i).setTexture(tex, 1) + + else: + needsAlpha = color[3] != 1 + color = VBase4(color[0], color[1], color[2], color[3]) + for i in xrange(matches.getNumPaths()): + matches.getPath(i).setColorScale(color, 1) + if needsAlpha: + matches.getPath(i).setTransparency(1) + + return + + def makeFrame(self): + from direct.gui.DirectGui import DirectFrame + frame = DirectFrame(parent=hidden, frameSize=(-1.0, 1.0, -1.0, 1.0), relief=None) + return frame + + def makeFrameModel(self, model, spin = 1): + frame = self.makeFrame() + ival = None + if model: + model.setDepthTest(1) + model.setDepthWrite(1) + if spin: + pitch = frame.attachNewNode('pitch') + rotate = pitch.attachNewNode('rotate') + scale = rotate.attachNewNode('scale') + model.reparentTo(scale) + bMin, bMax = model.getTightBounds() + center = (bMin + bMax) / 2.0 + model.setPos(-center[0], -center[1], -center[2]) + pitch.setP(20) + bMin, bMax = pitch.getTightBounds() + center = (bMin + bMax) / 2.0 + corner = Vec3(bMax - center) + scale.setScale(1.0 / max(corner[0], corner[1], corner[2])) + pitch.setY(2) + ival = LerpHprInterval(rotate, 10, VBase3(-270, 0, 0), startHpr=VBase3(90, 0, 0)) + else: + scale = frame.attachNewNode('scale') + model.reparentTo(scale) + bMin, bMax = model.getTightBounds() + center = (bMin + bMax) / 2.0 + model.setPos(-center[0], 2, -center[2]) + corner = Vec3(bMax - center) + scale.setScale(1.0 / max(corner[0], corner[1], corner[2])) + return (frame, ival) + + def getBlob(self, store = 0): + dg = PyDatagram() + dg.addUint8(CatalogItemVersion) + encodeCatalogItem(dg, self, store) + return dg.getMessage() + + def getRequestPurchaseErrorTextTimeout(self): + return 6 + +def encodeCatalogItem(dg, item, store): + import CatalogItemTypes + flags = item.getTypeCode() + if item.isSaleItem(): + flags |= CatalogItemTypes.CatalogItemSaleFlag + if item.giftTag != None: + flags |= CatalogItemTypes.CatalogItemGiftTag + dg.addUint8(flags) + if item.giftTag != None: + dg.addUint32(item.giftTag) + if not item.giftCode: + item.giftCode = 0 + dg.addUint8(item.giftCode) + item.encodeDatagram(dg, store) + return + + +def decodeCatalogItem(di, versionNumber, store): + global CatalogReverseType + import CatalogItemTypes + if CatalogReverseType == None: + CatalogReverseType = {} + for itemClass, index in CatalogItemTypes.CatalogItemTypes.items(): + CatalogReverseType[index] = itemClass + + startIndex = di.getCurrentIndex() + try: + flags = di.getUint8() + typeIndex = flags & CatalogItemTypes.CatalogItemTypeMask + gift = None + code = None + if flags & CatalogItemTypes.CatalogItemGiftTag: + gift = di.getUint32() + code = di.getUint8() + itemClass = CatalogReverseType[typeIndex] + item = itemClass(di, versionNumber, store=store) + except Exception, e: + CatalogItem.notify.warning('Invalid catalog item in stream: %s, %s' % (sys.exc_info()[0], e)) + d = Datagram(di.getDatagram().getMessage()[startIndex:]) + d.dumpHex(Notify.out()) + import CatalogInvalidItem + return CatalogInvalidItem.CatalogInvalidItem() + + if flags & CatalogItemTypes.CatalogItemSaleFlag: + item.saleItem = 1 + item.giftTag = gift + item.giftCode = code + return item + + +def getItem(blob, store = 0): + dg = PyDatagram(blob) + di = PyDatagramIterator(dg) + try: + versionNumber = di.getUint8() + return decodeCatalogItem(di, versionNumber, store) + except Exception, e: + CatalogItem.notify.warning('Invalid catalog item: %s, %s' % (sys.exc_info()[0], e)) + dg.dumpHex(Notify.out()) + import CatalogInvalidItem + return CatalogInvalidItem.CatalogInvalidItem() diff --git a/toontown/catalog/CatalogItemList.py b/toontown/catalog/CatalogItemList.py new file mode 100755 index 00000000..c6f15724 --- /dev/null +++ b/toontown/catalog/CatalogItemList.py @@ -0,0 +1,256 @@ +import CatalogItem +from panda3d.core import * +import types +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.PyDatagramIterator import PyDatagramIterator + +class CatalogItemList: + + def __init__(self, source = None, store = 0): + self.store = store + self.__blob = None + self.__list = None + if isinstance(source, types.StringType): + self.__blob = source + elif isinstance(source, types.ListType): + self.__list = source[:] + elif isinstance(source, CatalogItemList): + if source.store == store: + if source.__list != None: + self.__list = source.__list[:] + self.__blob = source.__blob + else: + self.__list = source[:] + return + + def markDirty(self): + if self.__list: + self.__blob = None + return + + def getBlob(self, store = None): + if store == None or store == self.store: + if self.__blob == None: + self.__encodeList() + return self.__blob + return self.__makeBlob(store) + + def getNextDeliveryDate(self): + if len(self) == 0: + return + nextDeliveryDate = None + for item in self: + if item: + if nextDeliveryDate == None or item.deliveryDate < nextDeliveryDate: + nextDeliveryDate = item.deliveryDate + + return nextDeliveryDate + + def getNextDeliveryItem(self): + if len(self) == 0: + return + nextDeliveryDate = None + nextDeliveryItem = None + for item in self: + if item: + if nextDeliveryDate == None or item.deliveryDate < nextDeliveryDate: + nextDeliveryDate = item.deliveryDate + nextDeliveryItem = item + + return nextDeliveryItem + + def extractDeliveryItems(self, cutoffTime): + beforeTime = [] + afterTime = [] + for item in self: + if item.deliveryDate <= cutoffTime: + beforeTime.append(item) + else: + afterTime.append(item) + + return (CatalogItemList(beforeTime, store=self.store), CatalogItemList(afterTime, store=self.store)) + + def extractOldestItems(self, count): + return (self[0:count], self[count:]) + + def __encodeList(self): + self.__blob = self.__makeBlob(self.store) + + def __makeBlob(self, store): + dg = PyDatagram() + if self.__list: + dg.addUint8(CatalogItem.CatalogItemVersion) + for item in self.__list: + CatalogItem.encodeCatalogItem(dg, item, store) + + return dg.getMessage() + + def generateList(self): + if self.__list: + return self.__list + self.__list = self.__makeList(self.store) + return self.__list + + def __decodeList(self): + self.__list = self.__makeList(self.store) + + def __makeList(self, store): + list = [] + if self.__blob: + dg = PyDatagram(self.__blob) + di = PyDatagramIterator(dg) + versionNumber = di.getUint8() + while di.getRemainingSize() > 0: + item = CatalogItem.decodeCatalogItem(di, versionNumber, store) + list.append(item) + + return list + + def append(self, item): + if self.__list == None: + self.__decodeList() + self.__list.append(item) + self.__blob = None + return + + def extend(self, items): + self += items + + def count(self, item): + if self.__list == None: + self.__decodeList() + return self.__list.count(item) + + def index(self, item): + if self.__list == None: + self.__decodeList() + return self.__list.index(item) + + def insert(self, index, item): + if self.__list == None: + self.__decodeList() + self.__list.insert(index, item) + self.__blob = None + return + + def pop(self, index = None): + if self.__list == None: + self.__decodeList() + self.__blob = None + if index == None: + return self.__list.pop() + else: + return self.__list.pop(index) + return + + def remove(self, item): + if self.__list == None: + self.__decodeList() + self.__list.remove(item) + self.__blob = None + return + + def reverse(self): + if self.__list == None: + self.__decodeList() + self.__list.reverse() + self.__blob = None + return + + def sort(self, cmpfunc = None): + if self.__list == None: + self.__decodeList() + if cmpfunc == None: + self.__list.sort() + else: + self.__list.sort(cmpfunc) + self.__blob = None + return + + def __len__(self): + if self.__list == None: + self.__decodeList() + return len(self.__list) + + def __getitem__(self, index): + if self.__list == None: + self.__decodeList() + return self.__list[index] + + def __setitem__(self, index, item): + if self.__list == None: + self.__decodeList() + self.__list[index] = item + self.__blob = None + return + + def __delitem__(self, index): + if self.__list == None: + self.__decodeList() + del self.__list[index] + self.__blob = None + return + + def __getslice__(self, i, j): + if self.__list == None: + self.__decodeList() + return CatalogItemList(self.__list[i:j], store=self.store) + + def __setslice__(self, i, j, s): + if self.__list == None: + self.__decodeList() + if isinstance(s, CatalogItemList): + self.__list[i:j] = s.__list + else: + self.__list[i:j] = s + self.__blob = None + return + + def __delslice__(self, i, j): + if self.__list == None: + self.__decodeList() + del self.__list[i:j] + self.__blob = None + return + + def __iadd__(self, other): + if self.__list == None: + self.__decodeList() + self.__list += list(other) + self.__blob = None + return self + + def __add__(self, other): + copy = CatalogItemList(self, store=self.store) + copy += other + return copy + + def __repr__(self): + return self.output() + + def __str__(self): + return self.output() + + def getList(self): + return self.__list + + def output(self, store = -1): + if self.__list == None: + self.__decodeList() + inner = '' + for item in self.__list: + inner += ', %s' % item.output(store) + + return 'CatalogItemList([%s])' % inner[2:] + + def removeDuplicates(self, flags): + if not self.__list: + self.generateList() + + found = False + for item in self.__list: + if item.getFlags() == flags: + if found: + self.__list.remove(item) + continue + found = True diff --git a/toontown/catalog/CatalogItemPanel.py b/toontown/catalog/CatalogItemPanel.py new file mode 100755 index 00000000..0513e36d --- /dev/null +++ b/toontown/catalog/CatalogItemPanel.py @@ -0,0 +1,488 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +import CatalogItemTypes +import CatalogItem +from CatalogWallpaperItem import getAllWallpapers +from CatalogFlooringItem import getAllFloorings +from CatalogMouldingItem import getAllMouldings +from CatalogWainscotingItem import getAllWainscotings +from CatalogFurnitureItem import getAllFurnitures +from otp.otpbase import OTPGlobals +CATALOG_PANEL_WORDWRAP = 10 +CATALOG_PANEL_CHAT_WORDWRAP = 9 +CATALOG_PANEL_ACCESSORY_WORDWRAP = 11 + +class CatalogItemPanel(DirectFrame): + notify = directNotify.newCategory('CatalogItemPanel') + + def __init__(self, parent = aspect2d, parentCatalogScreen = None, **kw): + optiondefs = (('item', None, DGG.INITOPT), ('type', CatalogItem.CatalogTypeUnspecified, DGG.INITOPT), ('relief', None, None)) + self.parentCatalogScreen = parentCatalogScreen + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.loaded = 0 + self.initialiseoptions(CatalogItemPanel) + return + + def load(self): + if self.loaded: + return + self.loaded = 1 + self.verify = None + self.pictureFrame = self.attachNewNode('pictureFrame') + self.pictureFrame.setScale(0.15) + self.itemIndex = 0 + self.ival = None + typeCode = self['item'].getTypeCode() + if self['item'].needsCustomize() and (typeCode == CatalogItemTypes.WALLPAPER_ITEM or typeCode == CatalogItemTypes.FLOORING_ITEM or typeCode == CatalogItemTypes.MOULDING_ITEM or typeCode == CatalogItemTypes.FURNITURE_ITEM or typeCode == CatalogItemTypes.WAINSCOTING_ITEM or typeCode == CatalogItemTypes.TOON_STATUE_ITEM): + if typeCode == CatalogItemTypes.WALLPAPER_ITEM: + self.items = getAllWallpapers(self['item'].patternIndex) + elif typeCode == CatalogItemTypes.FLOORING_ITEM: + self.items = getAllFloorings(self['item'].patternIndex) + elif typeCode == CatalogItemTypes.MOULDING_ITEM: + self.items = getAllMouldings(self['item'].patternIndex) + elif typeCode == CatalogItemTypes.FURNITURE_ITEM: + self.items = getAllFurnitures(self['item'].furnitureType) + elif typeCode == CatalogItemTypes.TOON_STATUE_ITEM: + self.items = self['item'].getAllToonStatues() + elif typeCode == CatalogItemTypes.WAINSCOTING_ITEM: + self.items = getAllWainscotings(self['item'].patternIndex) + self.numItems = len(self.items) + if self.numItems > 1: + guiItems = loader.loadModel('phase_5.5/models/gui/catalog_gui') + nextUp = guiItems.find('**/arrow_up') + nextRollover = guiItems.find('**/arrow_Rollover') + nextDown = guiItems.find('**/arrow_Down') + prevUp = guiItems.find('**/arrowUp') + prevDown = guiItems.find('**/arrowDown1') + prevRollover = guiItems.find('**/arrowRollover') + self.nextVariant = DirectButton(parent=self, relief=None, image=(nextUp, + nextDown, + nextRollover, + nextUp), image3_color=(1, 1, 1, 0.4), pos=(0.13, 0, 0), command=self.showNextVariant) + self.prevVariant = DirectButton(parent=self, relief=None, image=(prevUp, + prevDown, + prevRollover, + prevUp), image3_color=(1, 1, 1, 0.4), pos=(-0.13, 0, 0), command=self.showPrevVariant, state=DGG.DISABLED) + self.variantPictures = [(None, None)] * self.numItems + else: + self.variantPictures = [(None, None)] + self.showCurrentVariant() + else: + picture, self.ival = self['item'].getPicture(base.localAvatar) + if picture: + picture.reparentTo(self) + picture.setScale(0.15) + self.items = [self['item']] + self.variantPictures = [(picture, self.ival)] + if self.ival: + self.ival.loop() + self.typeLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.24), scale=TTLocalizer.CIPtypeLabel, text=self['item'].getTypeName(), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=CATALOG_PANEL_WORDWRAP) + self.auxText = DirectLabel(parent=self, relief=None, scale=0.05, pos=(-0.2, 0, 0.16)) + self.auxText.setHpr(0, 0, -30) + self.nameLabel = DirectLabel(parent=self, relief=None, text=self['item'].getDisplayName(), text_fg=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), text_scale=TTLocalizer.CIPnameLabel, text_wordwrap=CATALOG_PANEL_WORDWRAP + TTLocalizer.CIPwordwrapOffset) + if self['item'].getTypeCode() == CatalogItemTypes.CHAT_ITEM: + self.nameLabel['text_wordwrap'] = CATALOG_PANEL_CHAT_WORDWRAP + numRows = self.nameLabel.component('text0').textNode.getNumRows() + if numRows == 1: + namePos = (0, 0, -0.06) + elif numRows == 2: + namePos = (0, 0, -0.03) + else: + namePos = (0, 0, 0) + nameScale = 0.063 + elif self['item'].getTypeCode() == CatalogItemTypes.ACCESSORY_ITEM: + self.nameLabel['text_wordwrap'] = CATALOG_PANEL_ACCESSORY_WORDWRAP + namePos = (0, 0, -.22) + nameScale = 0.06 + else: + namePos = (0, 0, -.22) + nameScale = 0.06 + self.nameLabel.setPos(*namePos) + self.nameLabel.setScale(nameScale) + numericBeanPrice = self['item'].getPrice(self['type']) + priceStr = str(numericBeanPrice) + ' ' + TTLocalizer.CatalogCurrency + priceScale = 0.07 + if self['item'].isSaleItem(): + priceStr = TTLocalizer.CatalogSaleItem + priceStr + priceScale = 0.06 + self.priceLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.3), scale=priceScale, text=priceStr, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ACenter) + self.createEmblemPrices(numericBeanPrice) + buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + upButton = buttonModels.find('**/InventoryButtonUp') + downButton = buttonModels.find('**/InventoryButtonDown') + rolloverButton = buttonModels.find('**/InventoryButtonRollover') + buyText = TTLocalizer.CatalogBuyText + buyTextScale = TTLocalizer.CIPbuyButton + if self['item'].isRental(): + buyText = TTLocalizer.CatalogRentText + self.buyButton = DirectButton(parent=self, relief=None, pos=(0.2, 0, 0.15), scale=(0.7, 1, 0.8), text=buyText, text_scale=buyTextScale, text_pos=(-0.005, -0.01), image=(upButton, + downButton, + rolloverButton, + upButton), image_color=(1.0, 0.2, 0.2, 1), image0_color=Vec4(1.0, 0.4, 0.4, 1), image3_color=Vec4(1.0, 0.4, 0.4, 0.4), command=self.__handlePurchaseRequest) + soundIcons = loader.loadModel('phase_5.5/models/gui/catalogSoundIcons') + soundOn = soundIcons.find('**/sound07') + soundOff = soundIcons.find('**/sound08') + self.soundOnButton = DirectButton(parent=self, relief=None, pos=(0.2, 0, -0.15), scale=(0.7, 1, 0.8), text_scale=buyTextScale, text_pos=(-0.005, -0.01), image=(upButton, + downButton, + rolloverButton, + upButton), image_color=(0.2, 0.5, 0.2, 1), image0_color=Vec4(0.4, 0.5, 0.4, 1), image3_color=Vec4(0.4, 0.5, 0.4, 0.4), command=self.handleSoundOnButton) + self.soundOnButton.hide() + soundOn.setScale(0.1) + soundOn.reparentTo(self.soundOnButton) + self.soundOffButton = DirectButton(parent=self, relief=None, pos=(0.2, 0, -0.15), scale=(0.7, 1, 0.8), text_scale=buyTextScale, text_pos=(-0.005, -0.01), image=(upButton, + downButton, + rolloverButton, + upButton), image_color=(0.2, 1.0, 0.2, 1), image0_color=Vec4(0.4, 1.0, 0.4, 1), image3_color=Vec4(0.4, 1.0, 0.4, 0.4), command=self.handleSoundOffButton) + self.soundOffButton.hide() + soundOff = self.soundOffButton.attachNewNode('soundOff') + soundOn.copyTo(soundOff) + soundOff.reparentTo(self.soundOffButton) + upGButton = buttonModels.find('**/InventoryButtonUp') + downGButton = buttonModels.find('**/InventoryButtonDown') + rolloverGButton = buttonModels.find('**/InventoryButtonRollover') + self.giftButton = DirectButton(parent=self, relief=None, pos=(0.2, 0, 0.15), scale=(0.7, 1, 0.8), text=TTLocalizer.CatalogGiftText, text_scale=buyTextScale, text_pos=(-0.005, -0.01), image=(upButton, + downButton, + rolloverButton, + upButton), image_color=(1.0, 0.2, 0.2, 1), image0_color=Vec4(1.0, 0.4, 0.4, 1), image3_color=Vec4(1.0, 0.4, 0.4, 0.4), command=self.__handleGiftRequest) + self.updateButtons() + return + + def createEmblemPrices(self, numericBeanPrice): + priceScale = 0.07 + emblemPrices = self['item'].getEmblemPrices() + if emblemPrices: + if numericBeanPrice: + self.priceLabel.hide() + beanModel = loader.loadModel('phase_5.5/models/estate/jellyBean') + beanModel.setColorScale(1, 0, 0, 1) + self.beanPriceLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.3), scale=priceScale, image=beanModel, image_pos=(-0.4, 0, 0.4), text=str(numericBeanPrice), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft) + else: + self.priceLabel.hide() + goldPrice = 0 + silverPrice = 0 + emblemIcon = loader.loadModel('phase_3.5/models/gui/tt_m_gui_gen_emblemIcons') + silverModel = emblemIcon.find('**/tt_t_gui_gen_emblemSilver') + goldModel = emblemIcon.find('**/tt_t_gui_gen_emblemGold') + if ToontownGlobals.EmblemTypes.Silver < len(emblemPrices): + silverPrice = emblemPrices[ToontownGlobals.EmblemTypes.Silver] + if silverPrice: + self.silverPriceLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.3), scale=priceScale, image=silverModel, image_pos=(-0.4, 0, 0.4), text=str(silverPrice), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft) + if ToontownGlobals.EmblemTypes.Gold < len(emblemPrices): + goldPrice = emblemPrices[ToontownGlobals.EmblemTypes.Gold] + if goldPrice: + self.goldPriceLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.3), scale=priceScale, image=goldModel, image_pos=(-0.4, 0, 0.4), text=str(goldPrice), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft) + numPrices = 0 + if numericBeanPrice: + numPrices += 1 + if silverPrice: + numPrices += 1 + if goldPrice: + numPrices += 1 + if numPrices == 2: + if not numericBeanPrice: + self.silverPriceLabel.setX(-0.15) + self.goldPriceLabel.setX(0.15) + if not silverPrice: + self.goldPriceLabel.setX(-0.15) + self.beanPriceLabel.setX(0.15) + if not goldPrice: + self.silverPriceLabel.setX(-0.15) + self.beanPriceLabel.setX(0.15) + elif numPrices == 3: + self.silverPriceLabel.setX(-0.2) + self.goldPriceLabel.setX(0) + self.beanPriceLabel.setX(0.15) + return + + def showNextVariant(self): + messenger.send('wakeup') + self.hideCurrentVariant() + self.itemIndex += 1 + if self.itemIndex >= self.numItems - 1: + self.itemIndex = self.numItems - 1 + self.nextVariant['state'] = DGG.DISABLED + else: + self.nextVariant['state'] = DGG.NORMAL + self.prevVariant['state'] = DGG.NORMAL + self.showCurrentVariant() + + def showPrevVariant(self): + messenger.send('wakeup') + self.hideCurrentVariant() + self.itemIndex -= 1 + if self.itemIndex < 0: + self.itemIndex = 0 + self.prevVariant['state'] = DGG.DISABLED + else: + self.prevVariant['state'] = DGG.NORMAL + self.nextVariant['state'] = DGG.NORMAL + self.showCurrentVariant() + + def showCurrentVariant(self): + newPic, self.ival = self.variantPictures[self.itemIndex] + if self.ival: + self.ival.finish() + if not newPic: + variant = self.items[self.itemIndex] + newPic, self.ival = variant.getPicture(base.localAvatar) + self.variantPictures[self.itemIndex] = (newPic, self.ival) + newPic.reparentTo(self.pictureFrame) + if self.ival: + self.ival.loop() + if self['item'].getTypeCode() == CatalogItemTypes.TOON_STATUE_ITEM: + if hasattr(self, 'nameLabel'): + self.nameLabel['text'] = self.items[self.itemIndex].getDisplayName() + self['item'].gardenIndex = self.items[self.itemIndex].gardenIndex + + def hideCurrentVariant(self): + currentPic = self.variantPictures[self.itemIndex][0] + if currentPic: + currentPic.detachNode() + + def unload(self): + if not self.loaded: + DirectFrame.destroy(self) + return + self.loaded = 0 + if self['item'].getTypeCode() == CatalogItemTypes.TOON_STATUE_ITEM: + self['item'].deleteAllToonStatues() + self['item'].gardenIndex = self['item'].startPoseIndex + self.nameLabel['text'] = self['item'].getDisplayName() + self['item'].requestPurchaseCleanup() + for picture, ival in self.variantPictures: + if picture: + picture.destroy() + if ival: + ival.finish() + + self.variantPictures = None + if self.ival: + self.ival.finish() + self.ival = None + if len(self.items): + self.items[0].cleanupPicture() + self.pictureFrame.removeNode() + self.pictureFrame = None + self.items = [] + if self.verify: + self.verify.cleanup() + DirectFrame.destroy(self) + return + + def destroy(self): + self.parentCatalogScreen = None + self.unload() + return + + def updateBuyButton(self): + if not self.loaded: + return + self.buyButton.show() + typeCode = self['item'].getTypeCode() + orderCount = base.localAvatar.onOrder.count(self['item']) + if orderCount > 0: + if orderCount > 1: + auxText = '%d %s' % (orderCount, TTLocalizer.CatalogOnOrderText) + else: + auxText = TTLocalizer.CatalogOnOrderText + else: + auxText = '' + if self['item'].reachedPurchaseLimit(base.localAvatar): + max = self['item'].getPurchaseLimit() + if max <= 1: + auxText = TTLocalizer.CatalogPurchasedText + if self['item'].hasBeenGifted(base.localAvatar): + auxText = TTLocalizer.CatalogGiftedText + else: + auxText = TTLocalizer.CatalogPurchasedMaxText + self.buyButton['state'] = DGG.DISABLED + elif hasattr(self['item'], 'noGarden') and self['item'].noGarden(base.localAvatar): + auxText = TTLocalizer.NoGarden + self.buyButton['state'] = DGG.DISABLED + elif hasattr(self['item'], 'isSkillTooLow') and self['item'].isSkillTooLow(base.localAvatar): + auxText = TTLocalizer.SkillTooLow + self.buyButton['state'] = DGG.DISABLED + elif self['item'].getEmblemPrices() and not base.localAvatar.isEnoughMoneyAndEmblemsToBuy(self['item'].getPrice(self['type']), self['item'].getEmblemPrices()): + self.buyButton['state'] = DGG.DISABLED + elif hasattr(self['item'], 'houseId') and self['item'].houseId == localAvatar.houseType: + auxText = TTLocalizer.CatalogPurchasedMaxText + elif self['item'].getPrice(self['type']) <= base.localAvatar.getMoney() + base.localAvatar.getBankMoney(): + self.buyButton['state'] = DGG.NORMAL + self.buyButton.show() + else: + self.buyButton['state'] = DGG.DISABLED + self.buyButton.show() + self.auxText['text'] = auxText + + def __handlePurchaseRequest(self): + if self['item'].replacesExisting() and self['item'].hasExisting(): + message = TTLocalizer.CatalogOnlyOnePurchase % {'old': self['item'].getYourOldDesc(), + 'item': self['item'].getName(), + 'price': self['item'].getPrice(self['type'])} + elif self['item'].isRental(): + message = TTLocalizer.CatalogVerifyRent % {'item': self['item'].getName(), + 'price': self['item'].getPrice(self['type'])} + else: + emblemPrices = self['item'].getEmblemPrices() + if emblemPrices: + silver = emblemPrices[ToontownGlobals.EmblemTypes.Silver] + gold = emblemPrices[ToontownGlobals.EmblemTypes.Gold] + price = self['item'].getPrice(self['type']) + if price and silver and gold: + message = TTLocalizer.CatalogVerifyPurchaseBeanSilverGold % {'item': self['item'].getName(), + 'price': self['item'].getPrice(self['type']), + 'silver': silver, + 'gold': gold} + elif price and silver: + message = TTLocalizer.CatalogVerifyPurchaseBeanSilver % {'item': self['item'].getName(), + 'price': self['item'].getPrice(self['type']), + 'silver': silver, + 'gold': gold} + elif price and gold: + message = TTLocalizer.CatalogVerifyPurchaseBeanGold % {'item': self['item'].getName(), + 'price': self['item'].getPrice(self['type']), + 'silver': silver, + 'gold': gold} + elif silver and gold: + message = TTLocalizer.CatalogVerifyPurchaseSilverGold % {'item': self['item'].getName(), + 'price': self['item'].getPrice(self['type']), + 'silver': silver, + 'gold': gold} + elif silver: + message = TTLocalizer.CatalogVerifyPurchaseSilver % {'item': self['item'].getName(), + 'price': self['item'].getPrice(self['type']), + 'silver': silver, + 'gold': gold} + elif gold: + message = TTLocalizer.CatalogVerifyPurchaseGold % {'item': self['item'].getName(), + 'price': self['item'].getPrice(self['type']), + 'silver': silver, + 'gold': gold} + else: + self.notify.warning('is this a completely free item %s?' % self['item'].getName()) + message = TTLocalizer.CatalogVerifyPurchase % {'item': self['item'].getName(), + 'price': self['item'].getPrice(self['type'])} + else: + message = TTLocalizer.CatalogVerifyPurchase % {'item': self['item'].getName(), + 'price': self['item'].getPrice(self['type'])} + self.verify = TTDialog.TTGlobalDialog(doneEvent='verifyDone', message=message, style=TTDialog.TwoChoice) + self.verify.show() + self.accept('verifyDone', self.__handleVerifyPurchase) + + def __handleVerifyPurchase(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: CATALOG: Order item') + status = self.verify.doneStatus + self.ignore('verifyDone') + self.verify.cleanup() + del self.verify + self.verify = None + if status == 'ok': + item = self.items[self.itemIndex] + messenger.send('CatalogItemPurchaseRequest', [item]) + self.buyButton['state'] = DGG.DISABLED + return + + def __handleGiftRequest(self): + if self['item'].replacesExisting() and self['item'].hasExisting(): + message = TTLocalizer.CatalogOnlyOnePurchase % {'old': self['item'].getYourOldDesc(), + 'item': self['item'].getName(), + 'price': self['item'].getPrice(self['type'])} + else: + message = TTLocalizer.CatalogVerifyGift % {'item': self['item'].getName(), + 'price': self['item'].getPrice(self['type']), + 'friend': self.parentCatalogScreen.friendName if self.parentCatalogScreen.friendName else TTLocalizer.CatalogGiftError} + self.verify = TTDialog.TTGlobalDialog(doneEvent='verifyGiftDone', message=message, style=TTDialog.TwoChoice) + self.verify.show() + self.accept('verifyGiftDone', self.__handleVerifyGift) + + def __handleVerifyGift(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: CATALOG: Gift item') + status = self.verify.doneStatus + self.ignore('verifyGiftDone') + self.verify.cleanup() + del self.verify + self.verify = None + if status == 'ok': + self.giftButton['state'] = DGG.DISABLED + item = self.items[self.itemIndex] + messenger.send('CatalogItemGiftPurchaseRequest', [item]) + return + + def updateButtons(self, giftActivate = 0): + if self.parentCatalogScreen.gifting == -1: + self.updateBuyButton() + if self.loaded: + self.giftButton.hide() + else: + self.updateGiftButton(giftActivate) + if self.loaded: + self.buyButton.hide() + + def updateGiftButton(self, giftUpdate = 0): + if not self.loaded: + return + + self.giftButton.show() + + if giftUpdate == 0: + return + + self.auxText['text'] = '' + self.giftButton['state'] = DGG.DISABLED + self.giftButton.show() + + if self['item'].isGift() <= 0: + self.auxText['text'] = TTLocalizer.CatalogNotAGift + return + + if not self.parentCatalogScreen.friend: + return + + avatar = self.parentCatalogScreen.friend + + if self['item'].forBoysOnly() and avatar.getStyle().getGender() == 'f' or self['item'].forGirlsOnly() and avatar.getStyle().getGender() == 'm': + self.auxText['text'] = TTLocalizer.CatalogNoFit + elif self['item'].reachedPurchaseLimit(avatar): + self.auxText['text'] = TTLocalizer.CatalogPurchasedGiftText + elif len(avatar.mailboxContents) + len(avatar.onOrder) + len(avatar.onGiftOrder) + 1 >= ToontownGlobals.MaxMailboxContents: + self.auxText['text'] = TTLocalizer.CatalogMailboxFull + else: + orderCount = avatar.onGiftOrder.count(self['item']) + + if orderCount: + self.auxText['text'] = TTLocalizer.CatalogOnOrderText if orderCount == 1 else '%d %s' % (orderCount, TTLocalizer.CatalogOnOrderText) + if self['item'].getPrice(self['type']) <= base.localAvatar.getMoney() + base.localAvatar.getBankMoney(): + self.giftButton['state'] = DGG.NORMAL + + def handleSoundOnButton(self): + item = self.items[self.itemIndex] + self.soundOnButton.hide() + self.soundOffButton.show() + if hasattr(item, 'changeIval'): + if self.ival: + self.ival.finish() + self.ival = None + self.ival = item.changeIval(volume=1) + self.ival.loop() + return + + def handleSoundOffButton(self): + item = self.items[self.itemIndex] + self.soundOffButton.hide() + self.soundOnButton.show() + if hasattr(item, 'changeIval'): + if self.ival: + self.ival.finish() + self.ival = None + self.ival = item.changeIval(volume=0) + self.ival.loop() + return + + def lockItem(self): + self.buyButton['state'] = DGG.DISABLED diff --git a/toontown/catalog/CatalogItemTypes.py b/toontown/catalog/CatalogItemTypes.py new file mode 100755 index 00000000..8fa3e898 --- /dev/null +++ b/toontown/catalog/CatalogItemTypes.py @@ -0,0 +1,92 @@ +import CatalogFurnitureItem +import CatalogChatItem +import CatalogClothingItem +import CatalogEmoteItem +import CatalogWallpaperItem +import CatalogFlooringItem +import CatalogWainscotingItem +import CatalogMouldingItem +import CatalogWindowItem +import CatalogPoleItem +import CatalogPetTrickItem +import CatalogBeanItem +import CatalogGardenItem +import CatalogInvalidItem +import CatalogRentalItem +import CatalogGardenStarterItem +import CatalogNametagItem +import CatalogToonStatueItem +import CatalogAnimatedFurnitureItem +import CatalogAccessoryItem +import CatalogHouseItem +import CatalogTankItem +INVALID_ITEM = 0 +FURNITURE_ITEM = 1 +CHAT_ITEM = 2 +CLOTHING_ITEM = 3 +EMOTE_ITEM = 4 +WALLPAPER_ITEM = 5 +WINDOW_ITEM = 6 +FLOORING_ITEM = 7 +MOULDING_ITEM = 8 +WAINSCOTING_ITEM = 9 +POLE_ITEM = 10 +PET_TRICK_ITEM = 11 +BEAN_ITEM = 12 +GARDEN_ITEM = 13 +RENTAL_ITEM = 14 +GARDENSTARTER_ITEM = 15 +NAMETAG_ITEM = 16 +TOON_STATUE_ITEM = 17 +ANIMATED_FURNITURE_ITEM = 18 +ACCESSORY_ITEM = 19 +HOUSE_ITEM = 20 +TANK_ITEM = 21 +NonPermanentItemTypes = (RENTAL_ITEM,) +CatalogItemTypes = {CatalogInvalidItem.CatalogInvalidItem: INVALID_ITEM, + CatalogFurnitureItem.CatalogFurnitureItem: FURNITURE_ITEM, + CatalogChatItem.CatalogChatItem: CHAT_ITEM, + CatalogClothingItem.CatalogClothingItem: CLOTHING_ITEM, + CatalogEmoteItem.CatalogEmoteItem: EMOTE_ITEM, + CatalogWallpaperItem.CatalogWallpaperItem: WALLPAPER_ITEM, + CatalogWindowItem.CatalogWindowItem: WINDOW_ITEM, + CatalogFlooringItem.CatalogFlooringItem: FLOORING_ITEM, + CatalogMouldingItem.CatalogMouldingItem: MOULDING_ITEM, + CatalogWainscotingItem.CatalogWainscotingItem: WAINSCOTING_ITEM, + CatalogPoleItem.CatalogPoleItem: POLE_ITEM, + CatalogPetTrickItem.CatalogPetTrickItem: PET_TRICK_ITEM, + CatalogBeanItem.CatalogBeanItem: BEAN_ITEM, + CatalogGardenItem.CatalogGardenItem: GARDEN_ITEM, + CatalogRentalItem.CatalogRentalItem: RENTAL_ITEM, + CatalogGardenStarterItem.CatalogGardenStarterItem: GARDENSTARTER_ITEM, + CatalogNametagItem.CatalogNametagItem: NAMETAG_ITEM, + CatalogToonStatueItem.CatalogToonStatueItem: TOON_STATUE_ITEM, + CatalogAnimatedFurnitureItem.CatalogAnimatedFurnitureItem: ANIMATED_FURNITURE_ITEM, + CatalogAccessoryItem.CatalogAccessoryItem: ACCESSORY_ITEM, + CatalogHouseItem.CatalogHouseItem: HOUSE_ITEM, + CatalogTankItem.CatalogTankItem: TANK_ITEM} +CatalogItemType2multipleAllowed = {INVALID_ITEM: False, + FURNITURE_ITEM: True, + CHAT_ITEM: False, + CLOTHING_ITEM: False, + EMOTE_ITEM: False, + WALLPAPER_ITEM: True, + WINDOW_ITEM: True, + FLOORING_ITEM: True, + MOULDING_ITEM: True, + WAINSCOTING_ITEM: True, + POLE_ITEM: False, + PET_TRICK_ITEM: False, + BEAN_ITEM: False, + GARDEN_ITEM: False, + RENTAL_ITEM: False, + GARDENSTARTER_ITEM: False, + NAMETAG_ITEM: False, + TOON_STATUE_ITEM: False, + ANIMATED_FURNITURE_ITEM: True, + ACCESSORY_ITEM: False, + HOUSE_ITEM: False, + TANK_ITEM: False} +CatalogItemTypeMask = 31 +CatalogItemSaleFlag = 128 +CatalogItemGiftTag = 64 diff --git a/toontown/catalog/CatalogManager.py b/toontown/catalog/CatalogManager.py new file mode 100755 index 00000000..c5588791 --- /dev/null +++ b/toontown/catalog/CatalogManager.py @@ -0,0 +1,31 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal + +class CatalogManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('CatalogManager') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + if base.cr.catalogManager != None: + base.cr.catalogManager.delete() + base.cr.catalogManager = self + DistributedObject.DistributedObject.generate(self) + if hasattr(base.localAvatar, 'catalogScheduleNextTime') and base.localAvatar.catalogScheduleNextTime == 0: + self.d_startCatalog() + return + + def disable(self): + base.cr.catalogManager = None + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + base.cr.catalogManager = None + DistributedObject.DistributedObject.delete(self) + return + + def d_startCatalog(self): + self.sendUpdate('startCatalog', []) diff --git a/toontown/catalog/CatalogManagerAI.py b/toontown/catalog/CatalogManagerAI.py new file mode 100755 index 00000000..f1b12ccb --- /dev/null +++ b/toontown/catalog/CatalogManagerAI.py @@ -0,0 +1,27 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from CatalogGenerator import CatalogGenerator +from toontown.toonbase import ToontownGlobals +import time + +class CatalogManagerAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("CatalogManagerAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.catalogGenerator = CatalogGenerator() + + def startCatalog(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av: + self.deliverCatalogFor(av) + + def deliverCatalogFor(self, av): + monthlyCatalog = self.catalogGenerator.generateMonthlyCatalog(av, time.time() / 60) + newWeek = (av.catalogScheduleCurrentWeek + 1) % ToontownGlobals.CatalogNumWeeks + weeklyCatalog = self.catalogGenerator.generateWeeklyCatalog(av, newWeek, monthlyCatalog) + backCatalog = self.catalogGenerator.generateBackCatalog(av, newWeek, av.catalogScheduleCurrentWeek, monthlyCatalog) + av.b_setCatalog(monthlyCatalog, weeklyCatalog, backCatalog) + av.b_setCatalogSchedule(newWeek, int((time.time() + 604800)/60)) + av.b_setCatalogNotify(ToontownGlobals.NewItems, av.mailboxNotify) diff --git a/toontown/catalog/CatalogMouldingItem.py b/toontown/catalog/CatalogMouldingItem.py new file mode 100755 index 00000000..bd8e1005 --- /dev/null +++ b/toontown/catalog/CatalogMouldingItem.py @@ -0,0 +1,149 @@ +from CatalogSurfaceItem import * +MTTextureName = 0 +MTColor = 1 +MTBasePrice = 2 +MouldingTypes = {1000: ('phase_3.5/maps/molding_wood1.jpg', CTBasicWoodColorOnWhite, 150), + 1010: ('phase_5.5/maps/bd_grey_border1.jpg', CTFlatColorDark, 150), + 1020: ('phase_5.5/maps/dental_Border_wood_neutral.jpg', CTFlatColorDark, 150), + 1030: ('phase_5.5/maps/littleFlowers_border.jpg', CTWhite, 150), + 1040: ('phase_5.5/maps/littleFlowers_border_neutral.jpg', CTFlatColorDark, 150), + 1050: ('phase_5.5/maps/ladybugs2_Border.jpg', CTFlatColorDark, 150), + 1060: ('phase_5.5/maps/bd_grey_border1.jpg', CTValentinesColors, 150), + 1070: ('phase_5.5/maps/bd_grey_border1.jpg', CTUnderwaterColors, 150), + 1080: ('phase_5.5/maps/tt_t_ara_int_border_winterLights1.jpg', CTWhite, 150), + 1085: ('phase_5.5/maps/tt_t_ara_int_border_winterLights2.jpg', CTWhite, 150), + 1090: ('phase_5.5/maps/tt_t_ara_int_border_winterLights3.jpg', CTWhite, 150), + 1100: ('phase_5.5/maps/tt_t_ara_int_border_valentine_cupid.jpg', CTWhite, 150), + 1110: ('phase_5.5/maps/tt_t_ara_int_border_valentine_heart1.jpg', CTWhite, 150), + 1120: ('phase_5.5/maps/tt_t_ara_int_border_valentine_heart2.jpg', CTWhite, 150)} + +class CatalogMouldingItem(CatalogSurfaceItem): + + def makeNewItem(self, patternIndex, colorIndex): + self.patternIndex = patternIndex + self.colorIndex = colorIndex + CatalogSurfaceItem.makeNewItem(self) + + def getTypeName(self): + return TTLocalizer.SurfaceNames[STMoulding] + + def getName(self): + name = TTLocalizer.MouldingNames.get(self.patternIndex) + if name: + return name + return self.getTypeName() + + def getSurfaceType(self): + return STMoulding + + def getPicture(self, avatar): + self.hasPicture = True + frame = self.makeFrame() + sample = loader.loadModel('phase_5.5/models/estate/wallpaper_sample') + a = sample.find('**/a') + b = sample.find('**/b') + c = sample.find('**/c') + a.setTexture(self.loadTexture(), 1) + a.setColorScale(*self.getColor()) + b.hide() + c.hide() + sample.reparentTo(frame) + return (frame, None) + + def output(self, store = -1): + return 'CatalogMouldingItem(%s, %s%s)' % (self.patternIndex, self.colorIndex, self.formatOptionalData(store)) + + def getFilename(self): + return MouldingTypes[self.patternIndex][MTTextureName] + + def compareTo(self, other): + if self.patternIndex != other.patternIndex: + return self.patternIndex - other.patternIndex + return self.colorIndex - other.colorIndex + + def getHashContents(self): + return (self.patternIndex, self.colorIndex) + + def getBasePrice(self): + return MouldingTypes[self.patternIndex][MTBasePrice] + + def loadTexture(self): + from pandac.PandaModules import Texture + filename = MouldingTypes[self.patternIndex][MTTextureName] + texture = loader.loadTexture(filename) + texture.setMinfilter(Texture.FTLinearMipmapLinear) + texture.setMagfilter(Texture.FTLinear) + return texture + + def getColor(self): + if self.colorIndex == None: + colorIndex = 0 + else: + colorIndex = self.colorIndex + colors = MouldingTypes[self.patternIndex][MTColor] + if colors: + if colorIndex < len(colors): + return colors[colorIndex] + else: + print 'Warning: colorIndex not in colors. Returning white.' + return CT_WHITE + else: + return CT_WHITE + return + + def decodeDatagram(self, di, versionNumber, store): + CatalogAtticItem.CatalogAtticItem.decodeDatagram(self, di, versionNumber, store) + self.patternIndex = di.getUint16() + self.colorIndex = di.getUint8() + wtype = MouldingTypes[self.patternIndex] + + def encodeDatagram(self, dg, store): + CatalogAtticItem.CatalogAtticItem.encodeDatagram(self, dg, store) + dg.addUint16(self.patternIndex) + dg.addUint8(self.colorIndex) + + +def getMouldings(*indexList): + list = [] + for index in indexList: + list.append(CatalogMouldingItem(index)) + + return list + + +def getAllMouldings(*indexList): + list = [] + for index in indexList: + colors = MouldingTypes[index][MTColor] + if colors: + for n in xrange(len(colors)): + list.append(CatalogMouldingItem(index, n)) + + else: + list.append(CatalogMouldingItem(index, 0)) + + return list + + +def getMouldingRange(fromIndex, toIndex, *otherRanges): + list = [] + froms = [fromIndex] + tos = [toIndex] + i = 0 + while i < len(otherRanges): + froms.append(otherRanges[i]) + tos.append(otherRanges[i + 1]) + i += 2 + + for patternIndex in MouldingTypes.keys(): + for fromIndex, toIndex in zip(froms, tos): + if patternIndex >= fromIndex and patternIndex <= toIndex: + colors = MouldingTypes[patternIndex][MTColor] + if colors: + for n in xrange(len(colors)): + list.append(CatalogMouldingItem(patternIndex, n)) + + else: + list.append(CatalogMouldingItem(patternIndex, 0)) + + return list diff --git a/toontown/catalog/CatalogNametagItem.py b/toontown/catalog/CatalogNametagItem.py new file mode 100755 index 00000000..675660bb --- /dev/null +++ b/toontown/catalog/CatalogNametagItem.py @@ -0,0 +1,82 @@ +import CatalogItem +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +from direct.interval.IntervalGlobal import * +from direct.gui.DirectGui import * + +class CatalogNametagItem(CatalogItem.CatalogItem): + sequenceNumber = 0 + + def makeNewItem(self, nametagStyle, isSpecial = False): + self.nametagStyle = nametagStyle + self.isSpecial = isSpecial + CatalogItem.CatalogItem.makeNewItem(self) + + def getPurchaseLimit(self): + return 1 + + def reachedPurchaseLimit(self, avatar): + return self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder or self.nametagStyle in avatar.nametagStyles + + def getAcceptItemErrorText(self, retcode): + if retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.CatalogAcceptNametag + return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode) + + def saveHistory(self): + return 1 + + def getTypeName(self): + return TTLocalizer.NametagTypeName + + def getName(self): + name = TTLocalizer.NametagFontNames[self.nametagStyle] + if TTLocalizer.NametagReverse: + name = TTLocalizer.NametagLabel + name + else: + name = name + TTLocalizer.NametagLabel + return name + + def recordPurchase(self, avatar, optional): + if avatar: + avatar.b_setNametagStyle(self.nametagStyle) + avatar.addNametagStyle(self.nametagStyle) + return ToontownGlobals.P_ItemAvailable + + def getDeliveryTime(self): + return 0 + + def getPicture(self, avatar): + frame = self.makeFrame() + inFont = ToontownGlobals.getNametagFont(self.nametagStyle) + nameTagDemo = DirectLabel(parent=frame, relief=None, pos=(0, 0, 0.24), scale=0.5, text=base.localAvatar.getName(), text_fg=(1.0, 1.0, 1.0, 1), text_shadow=(0, 0, 0, 1), text_font=inFont, text_wordwrap=9) + self.hasPicture = True + return (frame, None) + + def output(self, store = -1): + return 'CatalogNametagItem(%s%s)' % (self.nametagStyle, self.formatOptionalData(store)) + + def compareTo(self, other): + return self.nametagStyle - other.nametagStyle + + def getHashContents(self): + return self.nametagStyle + + def getBasePrice(self): + return 500 + + def decodeDatagram(self, di, versionNumber, store): + CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store) + self.nametagStyle = di.getUint16() + self.isSpecial = di.getBool() + + def encodeDatagram(self, dg, store): + CatalogItem.CatalogItem.encodeDatagram(self, dg, store) + dg.addUint16(self.nametagStyle) + dg.addBool(self.isSpecial) + + def getBackSticky(self): + itemType = 1 + numSticky = 4 + return (itemType, numSticky) diff --git a/toontown/catalog/CatalogNotifyDialog.py b/toontown/catalog/CatalogNotifyDialog.py new file mode 100755 index 00000000..196cb0ff --- /dev/null +++ b/toontown/catalog/CatalogNotifyDialog.py @@ -0,0 +1,47 @@ +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.gui.DirectGui import * +from panda3d.core import * + +class CatalogNotifyDialog: + notify = directNotify.newCategory('CatalogNotifyDialog') + + def __init__(self, message): + self.message = message + self.messageIndex = 0 + self.frame = DirectFrame(relief=None, parent=base.a2dTopRight, sortOrder=DGG.BACKGROUND_SORT_INDEX - 2, image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.2, 1.0, 0.4), text=message[0], text_wordwrap=16, text_scale=0.06, text_pos=(-0.1, 0.1), pos=(-0.93, 0, -0.22)) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.nextButton = DirectButton(parent=self.frame, relief=None, image=okImageList, command=self.handleButton, pos=(0, 0, -0.14)) + self.doneButton = DirectButton(parent=self.frame, relief=None, image=cancelImageList, command=self.handleButton, pos=(0, 0, -0.14)) + if len(message) == 1: + self.nextButton.hide() + else: + self.doneButton.hide() + return + + def handleButton(self): + self.messageIndex += 1 + if self.messageIndex >= len(self.message): + self.cleanup() + return + self.frame['text'] = self.message[self.messageIndex] + if self.messageIndex + 1 == len(self.message): + self.nextButton.hide() + self.doneButton.show() + + def cleanup(self): + if self.frame: + self.frame.destroy() + self.frame = None + if self.nextButton: + self.nextButton.destroy() + self.nextButton = None + if self.doneButton: + self.doneButton.destroy() + self.doneButton = None + return + + def __handleButton(self, value): + self.cleanup() diff --git a/toontown/catalog/CatalogPetTrickItem.py b/toontown/catalog/CatalogPetTrickItem.py new file mode 100755 index 00000000..c1372855 --- /dev/null +++ b/toontown/catalog/CatalogPetTrickItem.py @@ -0,0 +1,103 @@ +import CatalogItem +from toontown.pets import PetTricks +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +from direct.interval.IntervalGlobal import * + +class CatalogPetTrickItem(CatalogItem.CatalogItem): + sequenceNumber = 0 + petPicture = None + + def makeNewItem(self, trickId): + self.trickId = trickId + CatalogItem.CatalogItem.makeNewItem(self) + + def getPurchaseLimit(self): + return 1 + + def reachedPurchaseLimit(self, avatar): + if self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder or not hasattr(avatar, 'petTrickPhrases'): + return 1 + return self.trickId in avatar.petTrickPhrases + + def getAcceptItemErrorText(self, retcode): + if retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.CatalogAcceptPet + return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode) + + def saveHistory(self): + return 1 + + def getTypeName(self): + return TTLocalizer.PetTrickTypeName + + def getName(self): + phraseId = PetTricks.TrickId2scIds[self.trickId][0] + return OTPLocalizer.SpeedChatStaticText[phraseId] + + def recordPurchase(self, avatar, optional): + avatar.petTrickPhrases.append(self.trickId) + avatar.d_setPetTrickPhrases(avatar.petTrickPhrases) + return ToontownGlobals.P_ItemAvailable + + def getPicture(self, avatar): + from toontown.pets import PetDNA, Pet + pet = Pet.Pet(forGui=1) + dna = avatar.getPetDNA() + if dna == None: + dna = PetDNA.getRandomPetDNA() + pet.setDNA(dna) + pet.setH(180) + pet.setScale(1.25) + model, ival = self.makeFrameModel(pet, 0) + pet.setP(-40) + track = PetTricks.getTrickIval(pet, self.trickId) + name = 'petTrick-item-%s' % self.sequenceNumber + CatalogPetTrickItem.sequenceNumber += 1 + if track != None: + track = Sequence(Sequence(track), ActorInterval(pet, 'neutral', duration=2), name=name) + else: + pet.animFSM.request('neutral') + track = Sequence(Wait(4), name=name) + self.petPicture = pet + self.hasPicture = True + return (model, track) + + def cleanupPicture(self): + CatalogItem.CatalogItem.cleanupPicture(self) + self.petPicture.delete() + self.petPicture = None + return + + def output(self, store = -1): + return 'CatalogPetTrickItem(%s%s)' % (self.trickId, self.formatOptionalData(store)) + + def compareTo(self, other): + return self.trickId - other.trickId + + def getHashContents(self): + return self.trickId + + def getBasePrice(self): + return 500 + + def decodeDatagram(self, di, versionNumber, store): + CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store) + self.trickId = di.getUint8() + self.dna = None + if self.trickId not in PetTricks.TrickId2scIds: + raise ValueError + return + + def encodeDatagram(self, dg, store): + CatalogItem.CatalogItem.encodeDatagram(self, dg, store) + dg.addUint8(self.trickId) + + +def getAllPetTricks(): + list = [] + for trickId in PetTricks.TrickId2scIds.keys(): + list.append(CatalogPetTrickItem(trickId)) + + return list diff --git a/toontown/catalog/CatalogPoleItem.py b/toontown/catalog/CatalogPoleItem.py new file mode 100755 index 00000000..e277f1cf --- /dev/null +++ b/toontown/catalog/CatalogPoleItem.py @@ -0,0 +1,112 @@ +import CatalogItem +from toontown.toonbase import ToontownGlobals +from toontown.fishing import FishGlobals +from direct.actor import Actor +from toontown.toonbase import TTLocalizer +from direct.interval.IntervalGlobal import * + +class CatalogPoleItem(CatalogItem.CatalogItem): + sequenceNumber = 0 + + def makeNewItem(self, rodId): + self.rodId = rodId + CatalogItem.CatalogItem.makeNewItem(self) + + def getPurchaseLimit(self): + return 1 + + def reachedPurchaseLimit(self, avatar): + return avatar.getMaxFishingRod() >= self.rodId or self in avatar.onOrder or self in avatar.mailboxContents + + def saveHistory(self): + return 1 + + def getTypeName(self): + return TTLocalizer.PoleTypeName + + def getName(self): + return TTLocalizer.FishingRod % TTLocalizer.FishingRodNameDict[self.rodId] + + def recordPurchase(self, avatar, optional): + if self.rodId < 0 or self.rodId > FishGlobals.MaxRodId: + self.notify.warning('Invalid fishing pole: %s for avatar %s' % (self.rodId, avatar.doId)) + return ToontownGlobals.P_InvalidIndex + if self.rodId < avatar.getMaxFishingRod(): + self.notify.warning('Avatar already has pole: %s for avatar %s' % (self.rodId, avatar.doId)) + return ToontownGlobals.P_ItemUnneeded + avatar.b_setFishingRod(self.rodId) + avatar.b_setMaxFishingRod(self.rodId) + return ToontownGlobals.P_ItemAvailable + + def isGift(self): + return 0 + + def getDeliveryTime(self): + return 24 * 60 + + def getPicture(self, avatar): + rodPath = FishGlobals.RodFileDict.get(self.rodId) + pole = Actor.Actor(rodPath, {'cast': 'phase_4/models/props/fishing-pole-chan'}) + pole.setPosHpr(1.47, 0, -1.67, 90, 55, -90) + pole.setScale(0.8) + pole.setDepthTest(1) + pole.setDepthWrite(1) + frame = self.makeFrame() + frame.attachNewNode(pole.node()) + name = 'pole-item-%s' % self.sequenceNumber + CatalogPoleItem.sequenceNumber += 1 + track = Sequence(Func(pole.pose, 'cast', 130), Wait(100), name=name) + self.hasPicture = True + return (frame, track) + + def getAcceptItemErrorText(self, retcode): + if retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.CatalogAcceptPole + elif retcode == ToontownGlobals.P_ItemUnneeded: + return TTLocalizer.CatalogAcceptPoleUnneeded + return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode) + + def output(self, store = -1): + return 'CatalogPoleItem(%s%s)' % (self.rodId, self.formatOptionalData(store)) + + def getFilename(self): + return FishGlobals.RodFileDict.get(self.rodId) + + def compareTo(self, other): + return self.rodId - other.rodId + + def getHashContents(self): + return self.rodId + + def getBasePrice(self): + return FishGlobals.RodPriceDict[self.rodId] + + def decodeDatagram(self, di, versionNumber, store): + CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store) + self.rodId = di.getUint8() + + def encodeDatagram(self, dg, store): + CatalogItem.CatalogItem.encodeDatagram(self, dg, store) + dg.addUint8(self.rodId) + + +def nextAvailablePole(avatar, duplicateItems): + rodId = avatar.getMaxFishingRod() + 1 + if rodId > FishGlobals.MaxRodId: + return None + item = CatalogPoleItem(rodId) + while item in avatar.onOrder or item in avatar.mailboxContents: + rodId += 1 + if rodId > FishGlobals.MaxRodId: + return None + item = CatalogPoleItem(rodId) + + return item + + +def getAllPoles(): + list = [] + for rodId in xrange(0, FishGlobals.MaxRodId + 1): + list.append(CatalogPoleItem(rodId)) + + return list diff --git a/toontown/catalog/CatalogRentalItem.py b/toontown/catalog/CatalogRentalItem.py new file mode 100755 index 00000000..40998d83 --- /dev/null +++ b/toontown/catalog/CatalogRentalItem.py @@ -0,0 +1,131 @@ +import CatalogItem +import time +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +from direct.interval.IntervalGlobal import * +from toontown.toontowngui import TTDialog + +class CatalogRentalItem(CatalogItem.CatalogItem): + + def makeNewItem(self, typeIndex, duration, cost): + self.typeIndex = typeIndex + self.duration = duration + self.cost = cost + CatalogItem.CatalogItem.makeNewItem(self) + + def getRentalType(self): + return self.typeIndex + + def getDuration(self): + return self.duration + + def getPurchaseLimit(self): + return 0 + + def reachedPurchaseLimit(self, avatar): + return self in avatar.onOrder or self in avatar.mailboxContents or self in avatar.onGiftOrder + + def saveHistory(self): + return 1 + + def getTypeName(self): + return TTLocalizer.RentalTypeName + + def getName(self): + hours = int(self.duration / 60) + if self.typeIndex == ToontownGlobals.RentalCannon: + return '%s %s %s %s' % (hours, + TTLocalizer.RentalHours, + TTLocalizer.RentalOf, + TTLocalizer.RentalCannon) + elif self.typeIndex == ToontownGlobals.RentalGameTable: + return '%s %s %s' % (hours, TTLocalizer.RentalHours, TTLocalizer.RentalGameTable) + else: + return TTLocalizer.RentalTypeName + + def recordPurchase(self, avatar, optional): + if avatar: + self.notify.debug('rental -- has avatar') + estate = simbase.air.estateManager._lookupEstate(avatar) + if estate: + self.notify.debug('rental -- has estate') + estate.rentItem(self.typeIndex, self.duration) + else: + self.notify.warning('rental -- something not there') + return ToontownGlobals.P_ItemAvailable + + def getPicture(self, avatar): + scale = 1 + heading = 0 + pitch = 30 + roll = 0 + spin = 1 + down = -1 + if self.typeIndex == ToontownGlobals.RentalCannon: + model = loader.loadModel('phase_4/models/minigames/toon_cannon') + scale = 0.5 + heading = 45 + elif self.typeIndex == ToontownGlobals.RentalGameTable: + model = loader.loadModel('phase_6/models/golf/game_table') + self.hasPicture = True + return self.makeFrameModel(model, spin) + + def output(self, store = -1): + return 'CatalogRentalItem(%s%s)' % (self.typeIndex, self.formatOptionalData(store)) + + def compareTo(self, other): + return self.typeIndex - other.typeIndex + + def getHashContents(self): + return self.typeIndex + + def getBasePrice(self): + if self.typeIndex == ToontownGlobals.RentalCannon: + return self.cost + elif self.typeIndex == ToontownGlobals.RentalGameTable: + return self.cost + else: + return 50 + + def decodeDatagram(self, di, versionNumber, store): + CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store) + self.cost = di.getUint16() + self.duration = di.getUint16() + self.typeIndex = di.getUint16() + + def encodeDatagram(self, dg, store): + CatalogItem.CatalogItem.encodeDatagram(self, dg, store) + dg.addUint16(self.cost) + dg.addUint16(self.duration) + dg.addUint16(self.typeIndex) + + def getDeliveryTime(self): + return 1 + + def isRental(self): + return 1 + + def acceptItem(self, mailbox, index, callback): + self.confirmRent = TTDialog.TTGlobalDialog(doneEvent='confirmRent', message=TTLocalizer.MessageConfirmRent, command=Functor(self.handleRentConfirm, mailbox, index, callback), style=TTDialog.TwoChoice) + self.confirmRent.show() + + def handleRentConfirm(self, mailbox, index, callback, choice): + if choice > 0: + mailbox.acceptItem(self, index, callback) + else: + callback(ToontownGlobals.P_UserCancelled, self, index) + if self.confirmRent: + self.confirmRent.cleanup() + self.confirmRent = None + return + + +def getAllRentalItems(): + list = [] + for rentalType in (ToontownGlobals.RentalCannon,): + list.append(CatalogRentalItem(rentalType, 2880, 1000)) + for rentalType in (ToontownGlobals.RentalGameTable,): + list.append(CatalogRentalItem(rentalType, 2890, 1000)) + + return list diff --git a/toontown/catalog/CatalogScreen.py b/toontown/catalog/CatalogScreen.py new file mode 100755 index 00000000..0278f052 --- /dev/null +++ b/toontown/catalog/CatalogScreen.py @@ -0,0 +1,1064 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from direct.gui.DirectScrolledList import * +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog +from toontown.friends import FriendHandle +import CatalogItem +import CatalogInvalidItem +import CatalogFurnitureItem +from toontown.toonbase import TTLocalizer +import CatalogItemPanel +import CatalogItemTypes +from direct.actor import Actor +import random +from toontown.toon import DistributedToon +from direct.directnotify import DirectNotifyGlobal +from otp.nametag.ChatBalloon import ChatBalloon +from otp.nametag import NametagGroup, NametagConstants + +NUM_CATALOG_ROWS = 3 +NUM_CATALOG_COLS = 2 +CatalogPanelCenters = [[Point3(-0.95, 0, 0.91), Point3(-0.275, 0, 0.91)], [Point3(-0.95, 0, 0.275), Point3(-0.275, 0, 0.275)], [Point3(-0.95, 0, -0.4), Point3(-0.275, 0, -0.4)]] +CatalogPanelColors = {CatalogItemTypes.FURNITURE_ITEM: Vec4(0.733, 0.78, 0.933, 1.0), + CatalogItemTypes.CHAT_ITEM: Vec4(0.922, 0.922, 0.753, 1.0), + CatalogItemTypes.CLOTHING_ITEM: Vec4(0.918, 0.69, 0.69, 1.0), + CatalogItemTypes.EMOTE_ITEM: Vec4(0.922, 0.922, 0.753, 1.0), + CatalogItemTypes.WALLPAPER_ITEM: Vec4(0.749, 0.984, 0.608, 1.0), + CatalogItemTypes.WINDOW_ITEM: Vec4(0.827, 0.91, 0.659, 1.0)} + +class CatalogScreen(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('CatalogScreen') + + def __init__(self, parent = aspect2d, **kw): + self.gifting = -1 + guiItems = loader.loadModel('phase_5.5/models/gui/catalog_gui') + background = guiItems.find('**/catalog_background') + background.setBin("background", 10) + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + guiBack = loader.loadModel('phase_5.5/models/gui/package_delivery_panel') + optiondefs = (('scale', 0.667, None), + ('pos', (0, 1, 0.025), None), + ('phone', None, None), + ('doneEvent', None, None), + ('image', background, None), + ('relief', None, None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.friend = None + self.friendAvId = None + self.friendName = None + self.friendList = [] + self.friends = [] + self.load(guiItems, guiButton, guiBack) + self.initialiseoptions(CatalogScreen) + self.enableBackorderCatalogButton() + self.setMaxPageIndex(self.numNewPages) + self.setPageIndex(-1) + self.showPageItems() + self.hide() + self.clarabelleChatNP = None + self.clarabelleChatBalloon = None + self.createdGiftGui = None + self.viewing = None + + def show(self): + self.accept('CatalogItemPurchaseRequest', self.__handlePurchaseRequest) + self.accept('CatalogItemGiftPurchaseRequest', self.__handleGiftPurchaseRequest) + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.accept(localAvatar.uniqueName('bankMoneyChange'), self.__bankMoneyChange) + self.accept(localAvatar.uniqueName('emblemsChange'), self.__emblemChange) + deliveryText = 'setDeliverySchedule-%s' % base.localAvatar.doId + self.accept(deliveryText, self.remoteUpdate) + base.setBackgroundColor(Vec4(0.529, 0.290, 0.286, 1)) + render.hide() + DirectFrame.show(self) + + def clarabelleGreeting(task): + self.setClarabelleChat(TTLocalizer.CatalogGreeting, type='greeting') + + def clarabelleHelpText1(task): + self.setClarabelleChat(TTLocalizer.CatalogHelpText1) + + taskMgr.doMethodLater(1.0, clarabelleGreeting, 'clarabelleGreeting') + taskMgr.doMethodLater(12.0, clarabelleHelpText1, 'clarabelleHelpText1') + + def hide(self): + self.ignore('CatalogItemPurchaseRequest') + self.ignore('CatalogItemGiftPurchaseRequest') + self.ignore(localAvatar.uniqueName('moneyChange')) + self.ignore(localAvatar.uniqueName('bankMoneyChange')) + self.ignore(localAvatar.uniqueName('emblemsChange')) + deliveryText = 'setDeliverySchedule-%s' % base.localAvatar.doId + self.ignore(deliveryText) + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + render.show() + DirectFrame.hide(self) + + def setNumNewPages(self, numNewPages): + self.numNewPages = numNewPages + + def setNumBackPages(self, numBackPages): + self.numBackPages = numBackPages + + def setNumSpecialPages(self, numSpecialPages): + self.numSpecialPages = numSpecialPages + + def setNumEmblemPages(self, numEmblemPages): + self.numEmblemPages = numEmblemPages + + def setPageIndex(self, index): + self.pageIndex = index + + def setMaxPageIndex(self, numPages): + self.maxPageIndex = max(numPages - 1, -1) + + def enableBackorderCatalogButton(self): + self.backCatalogButton['state'] = DGG.NORMAL + self.newCatalogButton['state'] = DGG.DISABLED + self.specialCatalogButton['state'] = DGG.DISABLED + self.emblemCatalogButton['state'] = DGG.DISABLED + + def enableNewCatalogButton(self): + self.backCatalogButton['state'] = DGG.DISABLED + self.newCatalogButton['state'] = DGG.NORMAL + self.specialCatalogButton['state'] = DGG.DISABLED + self.emblemCatalogButton['state'] = DGG.DISABLED + + def enableSpecialCatalogButton(self): + self.backCatalogButton['state'] = DGG.DISABLED + self.newCatalogButton['state'] = DGG.DISABLED + self.specialCatalogButton['state'] = DGG.NORMAL + self.emblemCatalogButton['state'] = DGG.DISABLED + + def enableEmblemCatalogButton(self): + self.backCatalogButton['state'] = DGG.DISABLED + self.newCatalogButton['state'] = DGG.DISABLED + self.specialCatalogButton['state'] = DGG.DISABLED + self.emblemCatalogButton['state'] = DGG.NORMAL + + def modeBackorderCatalog(self): + self.backCatalogButton['state'] = DGG.DISABLED + self.newCatalogButton['state'] = DGG.NORMAL + self.specialCatalogButton['state'] = DGG.NORMAL + self.emblemCatalogButton['state'] = DGG.NORMAL + + def modeNewCatalog(self): + self.backCatalogButton['state'] = DGG.NORMAL + self.newCatalogButton['state'] = DGG.DISABLED + self.specialCatalogButton['state'] = DGG.NORMAL + self.emblemCatalogButton['state'] = DGG.NORMAL + + def modeSpecialCatalog(self): + self.backCatalogButton['state'] = DGG.NORMAL + self.newCatalogButton['state'] = DGG.NORMAL + self.specialCatalogButton['state'] = DGG.DISABLED + self.emblemCatalogButton['state'] = DGG.NORMAL + + def modeEmblemCatalog(self): + self.backCatalogButton['state'] = DGG.NORMAL + self.newCatalogButton['state'] = DGG.NORMAL + self.specialCatalogButton['state'] = DGG.NORMAL + self.emblemCatalogButton['state'] = DGG.DISABLED + + def showNewItems(self, index = None): + if config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: CATALOG: New item') + taskMgr.remove('clarabelleHelpText1') + messenger.send('wakeup') + self.viewing = 'New' + self.modeNewCatalog() + self.setMaxPageIndex(self.numNewPages) + if self.numNewPages == 0: + self.setPageIndex(-1) + elif index is not None: + self.setPageIndex(index) + else: + self.setPageIndex(0) + self.showPageItems() + return + + def showBackorderItems(self, index = None): + if config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: CATALOG: Backorder item') + taskMgr.remove('clarabelleHelpText1') + messenger.send('wakeup') + self.viewing = 'Backorder' + self.modeBackorderCatalog() + self.setMaxPageIndex(self.numBackPages) + if self.numBackPages == 0: + self.setPageIndex(-1) + elif index is not None: + self.setPageIndex(index) + else: + self.setPageIndex(0) + self.showPageItems() + return + + def showSpecialItems(self, index = None): + if config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: CATALOG: Special item') + taskMgr.remove('clarabelleHelpText1') + messenger.send('wakeup') + self.viewing = 'Special' + self.modeSpecialCatalog() + self.setMaxPageIndex(self.numSpecialPages) + if self.numSpecialPages == 0: + self.setPageIndex(-1) + elif index is not None: + self.setPageIndex(index) + else: + self.setPageIndex(0) + self.showPageItems() + return + + def showEmblemItems(self, index = None): + if config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: CATALOG: Emblem item') + taskMgr.remove('clarabelleHelpText1') + messenger.send('wakeup') + self.viewing = 'Emblem' + self.modeEmblemCatalog() + self.setMaxPageIndex(self.numEmblemPages) + if self.numEmblemPages == 0: + self.setPageIndex(-1) + elif index is not None: + self.setPageIndex(index) + else: + self.setPageIndex(0) + self.showPageItems() + return + + def showNextPage(self): + taskMgr.remove('clarabelleHelpText1') + messenger.send('wakeup') + self.pageIndex = self.pageIndex + 1 + if self.viewing == None: + self.modeNewCatalog() + self.viewing == 'New' + if self.viewing == 'New' and self.pageIndex > self.maxPageIndex and self.numBackPages > 0: + self.showBackorderItems() + if self.viewing == 'New' and self.pageIndex > self.maxPageIndex and self.numSpecialPages > 0: + self.showSpecialItems() + elif self.viewing == 'Backorder' and self.pageIndex > self.maxPageIndex and self.numSpecialPages > 0: + self.showSpecialItems() + elif self.viewing == 'Special' and self.pageIndex > self.maxPageIndex and self.numEmblemPages > 0: + self.showEmblemItems() + else: + self.pageIndex = min(self.pageIndex, self.maxPageIndex) + self.showPageItems() + return + + def showBackPage(self): + taskMgr.remove('clarabelleHelpText1') + messenger.send('wakeup') + self.pageIndex = self.pageIndex - 1 + if self.viewing == 'Backorder' and self.pageIndex < 0 and self.numNewPages > 0: + self.showNewItems(self.numNewPages - 1) + elif self.viewing == 'Special' and self.pageIndex < 0 and self.numBackPages > 0: + self.showBackorderItems(self.numBackPages - 1) + elif self.viewing == 'Emblem' and self.pageIndex < 0 and self.numSpecialPages > 0: + self.showSpecialItems(self.numSpecialPages - 1) + else: + self.pageIndex = max(self.pageIndex, -1) + self.showPageItems() + + def showPageItems(self): + self.hidePages() + if self.viewing == None: + self.viewing = 'New' + if self.pageIndex < 0: + self.closeCover() + else: + if self.pageIndex == 0: + self.openCover() + if self.viewing == 'New': + page = self.pageList[self.pageIndex] + newOrBackOrSpecial = 0 + elif self.viewing == 'Backorder': + page = self.backPageList[self.pageIndex] + newOrBackOrSpecial = 1 + elif self.viewing == 'Special': + page = self.specialPageList[self.pageIndex] + newOrBackOrSpecial = 2 + elif self.viewing == 'Emblem': + page = self.emblemPageList[self.pageIndex] + newOrBackOrSpecial = 3 + page.show() + for panel in self.panelDict[page.get_key()]: + panel.load() + if panel.ival: + panel.ival.loop() + self.visiblePanels.append(panel) + + pIndex = 0 + randGen = random.Random() + randGen.seed(base.localAvatar.catalogScheduleCurrentWeek + (self.pageIndex << 8) + (newOrBackOrSpecial << 16)) + for i in xrange(NUM_CATALOG_ROWS): + for j in xrange(NUM_CATALOG_COLS): + if pIndex < len(self.visiblePanels): + type = self.visiblePanels[pIndex]['item'].getTypeCode() + self.squares[i][j].setColor(CatalogPanelColors.values()[randGen.randint(0, len(CatalogPanelColors) - 1)]) + cs = 0.7 + 0.3 * randGen.random() + self.squares[i][j].setColorScale(0.7 + 0.3 * randGen.random(), 0.7 + 0.3 * randGen.random(), 0.7 + 0.3 * randGen.random(), 1) + else: + self.squares[i][j].setColor(CatalogPanelColors[CatalogItemTypes.CHAT_ITEM]) + self.squares[i][j].clearColorScale() + pIndex += 1 + + if self.viewing == 'New': + text = TTLocalizer.CatalogNew + elif self.viewing == 'Special': + text = TTLocalizer.CatalogSpecial + elif self.viewing == 'Backorder': + text = TTLocalizer.CatalogBackorder + elif self.viewing == 'Emblem': + text = TTLocalizer.CatalogEmblem + self.pageLabel['text'] = text + ' - %d' % (self.pageIndex + 1) + if self.pageIndex < self.maxPageIndex: + self.nextPageButton.show() + elif self.viewing == 'New' and self.numBackPages == 0 and self.numSpecialPages == 0: + self.nextPageButton.hide() + elif self.viewing == 'Backorder' and self.numSpecialPages == 0: + self.nextPageButton.hide() + elif self.viewing == 'Special' and self.numEmblemPages == 0: + self.nextPageButton.hide() + elif self.viewing == 'Special' and self.numEmblemPages > 0: + self.nextPageButton.show() + elif self.viewing == 'Emblem': + self.nextPageButton.hide() + self.adjustForSound() + self.update() + return + + def adjustForSound(self): + numEmoteItems = 0 + emotePanels = [] + for visIndex in xrange(len(self.visiblePanels)): + panel = self.visiblePanels[visIndex] + item = panel['item'] + catalogType = item.getTypeCode() + if catalogType == CatalogItemTypes.EMOTE_ITEM: + numEmoteItems += 1 + emotePanels.append(panel) + else: + panel.soundOnButton.hide() + panel.soundOffButton.hide() + + if numEmoteItems == 1: + emotePanels[0].handleSoundOnButton() + elif numEmoteItems > 1: + for panel in emotePanels: + panel.handleSoundOffButton() + + def hidePages(self): + for page in self.pageList: + page.hide() + + for page in self.backPageList: + page.hide() + + for page in self.specialPageList: + page.hide() + + for page in self.emblemPageList: + page.hide() + + for panel in self.visiblePanels: + if panel.ival: + panel.ival.finish() + + self.visiblePanels = [] + + def openCover(self): + self.cover.hide() + self.hideDummyTabs() + self.backPageButton.show() + self.pageLabel.show() + + def closeCover(self): + self.cover.show() + self.showDummyTabs() + self.nextPageButton.show() + self.backPageButton.hide() + self.pageLabel.hide() + self.hidePages() + + def showDummyTabs(self): + if self.numNewPages > 0: + self.newCatalogButton2.show() + if self.numBackPages > 0: + self.backCatalogButton2.show() + if self.numSpecialPages > 0: + self.specialCatalogButton2.show() + if self.numEmblemPages > 0: + self.emblemCatalogButton2.show() + self.newCatalogButton.hide() + self.backCatalogButton.hide() + self.specialCatalogButton.hide() + self.emblemCatalogButton.hide() + + def hideDummyTabs(self): + self.newCatalogButton2.hide() + self.backCatalogButton2.hide() + self.specialCatalogButton2.hide() + self.emblemCatalogButton2.hide() + if self.numNewPages > 0: + self.newCatalogButton.show() + if self.numBackPages > 0: + self.backCatalogButton.show() + if self.numSpecialPages > 0: + self.specialCatalogButton.show() + if self.numEmblemPages > 0: + self.emblemCatalogButton.show() + + def packPages(self, panelList, pageList, prefix): + i = 0 + j = 0 + numPages = 0 + pageName = prefix + '_page%d' % numPages + for item in panelList: + if i == 0 and j == 0: + numPages += 1 + pageName = prefix + '_page%d' % numPages + page = self.base.attachNewNode(pageName) + pageList.append(page) + item.reparentTo(page) + item.setPos(CatalogPanelCenters[i][j]) + itemList = self.panelDict.get(page.get_key(), []) + itemList.append(item) + self.panelDict[page.get_key()] = itemList + j += 1 + if j == NUM_CATALOG_COLS: + j = 0 + i += 1 + if i == NUM_CATALOG_ROWS: + i = 0 + + return numPages + + def load(self, guiItems, guiButton, guiBack): + self.pageIndex = -1 + self.maxPageIndex = 0 + self.numNewPages = 0 + self.numBackPages = 5 + self.numSpecialPages = 0 + self.viewing = 'New' + self.panelList = [] + self.backPanelList = [] + self.pageList = [] + self.backPageList = [] + self.specialPanelList = [] + self.specialPageList = [] + self.emblemPanelList = [] + self.emblemPageList = [] + self.panelDict = {} + self.visiblePanels = [] + self.responseDialog = None + catalogBase = guiItems.find('**/catalog_base') + self.base = DirectLabel(self, relief=None, image=catalogBase) + newDown = guiItems.find('**/new1') + newUp = guiItems.find('**/new2') + backDown = guiItems.find('**/previous2') + backUp = guiItems.find('**/previous1') + giftToggleUp = guiItems.find('**/giftButtonUp') + giftToggleDown = guiItems.find('**/giftButtonDown') + giftFriends = guiItems.find('**/gift_names') + oldLift = 0.4 + lift = 0.4 + liftDiff = lift - oldLift + lift2 = 0.05 + smash = 0.75 + priceScale = 0.15 + emblemIcon = loader.loadModel('phase_3.5/models/gui/tt_m_gui_gen_emblemIcons') + silverModel = emblemIcon.find('**/tt_t_gui_gen_emblemSilver') + goldModel = emblemIcon.find('**/tt_t_gui_gen_emblemGold') + self.silverLabel = DirectLabel(parent=self, relief=None, pos=(1.05, 0, -0.6), scale=priceScale, image=silverModel, image_pos=(-0.4, 0, 0.4), text=str(localAvatar.emblems[ToontownGlobals.EmblemTypes.Silver]), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft) + base.silverLabel = self.silverLabel + self.goldLabel = DirectLabel(parent=self, relief=None, pos=(1.05, 0, -0.8), scale=priceScale, image=goldModel, image_pos=(-0.4, 0, 0.4), text=str(localAvatar.emblems[ToontownGlobals.EmblemTypes.Gold]), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft) + base.goldLabel = self.goldLabel + if not base.cr.wantEmblems: + self.hideEmblems() + self.newCatalogButton = DirectButton(self.base, relief=None, pos=(0, 0, 0.17), frameSize=(-0.2, + 0.25, + 0.45, + 1.2), image=[newDown, + newDown, + newDown, + newUp], image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, lift), pressEffect=0, command=self.showNewItems, text=TTLocalizer.CatalogNew, text_font=ToontownGlobals.getSignFont(), text_pos=(-0.4 - lift, 0.13), text3_pos=(-0.4 - lift, 0.1), text_scale=0.08, text_fg=(0.353, 0.627, 0.627, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0)) + self.newCatalogButton.hide() + self.newCatalogButton2 = DirectButton(self.base, relief=None, pos=(0, 0, 0.17), frameSize=(-0.2, + 0.25, + 0.45, + 1.2), image=newDown, image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, lift), pressEffect=0, command=self.showNewItems, text=TTLocalizer.CatalogNew, text_font=ToontownGlobals.getSignFont(), text_pos=(-0.4 - lift, 0.13), text_scale=0.08, text_fg=(0.353, 0.627, 0.627, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0)) + self.newCatalogButton2.hide() + self.backCatalogButton = DirectButton(self.base, relief=None, pos=(0, 0, 0.269), frameSize=(-0.2, + 0.25, + -0.2, + 0.4), image=[backDown, + backDown, + backDown, + backUp], image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, lift), pressEffect=0, command=self.showBackorderItems, text=TTLocalizer.CatalogBackorder, text_font=ToontownGlobals.getSignFont(), text_pos=(0.25 - lift, 0.132), text3_pos=(0.25 - lift, 0.112), text_scale=TTLocalizer.CSbackCatalogButton, text_fg=(0.392, 0.549, 0.627, 1.0), text2_fg=(0.392, 0.349, 0.427, 1.0)) + self.backCatalogButton.hide() + self.backCatalogButton2 = DirectButton(self.base, relief=None, pos=(0, 0, 0.269), frameSize=(-0.2, + 0.25, + -0.2, + 0.4), image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, lift), image=backDown, pressEffect=0, command=self.showBackorderItems, text=TTLocalizer.CatalogBackorder, text_font=ToontownGlobals.getSignFont(), text_pos=(0.25 - lift, 0.132), text_scale=TTLocalizer.CSbackCatalogButton, text_fg=(0.392, 0.549, 0.627, 1.0), text2_fg=(0.392, 0.349, 0.427, 1.0)) + self.backCatalogButton2.hide() + self.specialCatalogButton = DirectButton(self.base, relief=None, pos=(0, 0, 0.469), frameSize=(-0.2, + 0.25, + -0.85, + -0.3), image=[newDown, + newDown, + newDown, + newUp], image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, -1.4 + lift), pressEffect=0, command=self.showSpecialItems, text=TTLocalizer.CatalogSpecial, text_font=ToontownGlobals.getSignFont(), text_pos=(1.0 - lift, 0.132), text3_pos=(1.0 - lift, 0.112), text_scale=0.065, text_fg=(0.353, 0.627, 0.627, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0)) + self.specialCatalogButton.hide() + self.specialCatalogButton2 = DirectButton(self.base, relief=None, pos=(0, 0, 0.469), frameSize=(-0.2, + 0.25, + -0.85, + -0.3), image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, -1.4 + lift), image=newDown, pressEffect=0, command=self.showSpecialItems, text=TTLocalizer.CatalogSpecial, text_font=ToontownGlobals.getSignFont(), text_pos=(1.0 - lift, 0.132), text_scale=0.065, text_fg=(0.353, 0.627, 0.627, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0)) + self.specialCatalogButton2.hide() + self.emblemCatalogButton = DirectButton(self.base, relief=None, pos=(0, 0, 1.05), frameSize=(-0.2, + 0.25, + -2.0, + -1.45), image=[backDown, + backDown, + backDown, + backUp], image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, -1.9 + lift), pressEffect=0, command=self.showEmblemItems, text=TTLocalizer.CatalogEmblem, text_font=ToontownGlobals.getSignFont(), text_pos=(1.75, 0.132), text3_pos=(1.75, 0.112), text_scale=0.065, text_fg=(0.353, 0.627, 0.627, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0)) + self.emblemCatalogButton.hide() + self.emblemCatalogButton2 = DirectButton(self.base, relief=None, pos=(0, 0, 1.05), frameSize=(-0.2, + 0.25, + -2.0, + -1.45), image_scale=(1.0, 1.0, smash), image_pos=(0.0, 0.0, -1.9 + lift), image=backDown, pressEffect=0, command=self.showEmblemItems, text=TTLocalizer.CatalogEmblem, text_font=ToontownGlobals.getSignFont(), text_pos=(1.75, 0.132), text_scale=0.065, text_fg=(0.353, 0.627, 0.627, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0)) + self.emblemCatalogButton2.hide() + self.__makeFriendList() + if len(self.friendList) > 0: + if config.GetBool('want-gifting', True): + self.giftToggle = DirectButton(self.base, relief=None, pressEffect=0, image=(giftToggleUp, giftToggleDown, giftToggleUp), image_scale=(1.0, 1, 0.7), command=self.__giftToggle, text=TTLocalizer.CatalogGiftToggleOff, text_font=ToontownGlobals.getSignFont(), text_pos=TTLocalizer.CSgiftTogglePos, text_scale=TTLocalizer.CSgiftToggle, text_fg=(0.353, 0.627, 0.627, 1.0), text3_fg=(0.15, 0.3, 0.3, 1.0), text2_fg=(0.353, 0.427, 0.427, 1.0), image_color=Vec4(1.0, 1.0, 0.2, 1.0), image1_color=Vec4(0.9, 0.85, 0.2, 1.0), image2_color=Vec4(0.9, 0.85, 0.2, 1.0), image3_color=Vec4(0.5, 0.45, 0.2, 1.0)) + self.giftToggle.setPos(0.0, 0, -0.035) + self.giftLabel = DirectLabel(self.base, relief=None, image=giftFriends, image_scale=(1.15, 1, 1.14), text=' ', text_font=ToontownGlobals.getSignFont(), text_pos=(1.2, -0.97), text_scale=0.07, text_fg=(0.392, 0.549, 0.627, 1.0), sortOrder=100, textMayChange=1) + self.giftLabel.setPos(-0.15, 0, 0.08) + self.giftLabel.hide() + self.friendLabel = DirectLabel(self.base, relief=None, text=TTLocalizer.CatalogGiftChoose, text_font=ToontownGlobals.getSignFont(), text_pos=(-0.25, 0.132), text_scale=0.068, text_align=TextNode.ALeft, text_fg=(0.992, 0.949, 0.327, 1.0), sortOrder=100, textMayChange=1) + self.friendLabel.setPos(0.5, 0, -0.42) + self.friendLabel.hide() + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.scrollList = DirectScrolledList(parent=self, relief=None, incButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_pos=(0.0, 0.0, -0.316), incButton_image1_color=Vec4(1.0, 0.9, 0.4, 1.0), incButton_image3_color=Vec4(1.0, 1.0, 0.6, 0.5), incButton_scale=(1.0, 1.0, -1.0), decButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_pos=(0.0, 0.0, 0.117), decButton_image1_color=Vec4(1.0, 1.0, 0.6, 1.0), decButton_image3_color=Vec4(1.0, 1.0, 0.6, 0.6), itemFrame_pos=(-0.17, 0.0, 0.06), itemFrame_relief=None, numItemsVisible=8, items=[]) + self.scrollList.setPos(1.2, 0, -0.58) + self.scrollList.setScale(1.5) + self.scrollList.hide() + clipper = PlaneNode('clipper') + clipper.setPlane(Plane(Vec3(-1, 0, 0), Point3(0.17, 0, 0))) + clipNP = self.scrollList.attachNewNode(clipper) + self.scrollList.setClipPlane(clipNP) + self.__makeScrollList() + self.createdGiftGui = 1 + for i in xrange(4): + self.newCatalogButton.component('text%d' % i).setR(90) + self.newCatalogButton2.component('text%d' % i).setR(90) + self.backCatalogButton.component('text%d' % i).setR(90) + self.backCatalogButton2.component('text%d' % i).setR(90) + self.specialCatalogButton.component('text%d' % i).setR(90) + self.specialCatalogButton2.component('text%d' % i).setR(90) + self.emblemCatalogButton.component('text%d' % i).setR(90) + self.emblemCatalogButton2.component('text%d' % i).setR(90) + + self.squares = [[], + [], + [], + []] + for i in xrange(NUM_CATALOG_ROWS): + for j in xrange(NUM_CATALOG_COLS): + square = guiItems.find('**/square%d%db' % (i + 1, j + 1)) + label = DirectLabel(self.base, image=square, relief=None, state='normal') + self.squares[i].append(label) + + def priceSort(a, b, type): + priceA = a.getPrice(type) + priceB = b.getPrice(type) + return priceB - priceA + + itemList = base.localAvatar.monthlyCatalog + base.localAvatar.weeklyCatalog + itemList.sort(lambda a, b: priceSort(a, b, CatalogItem.CatalogTypeWeekly)) + itemList.reverse() + allClosetItems = CatalogFurnitureItem.getAllClosets() + allBankItems = CatalogFurnitureItem.getAllBanks() + isMaxClosetOfferred = False + isMaxBankOffered = False + for item in itemList: + if item in allClosetItems and item.furnitureType in CatalogFurnitureItem.MaxClosetIds: + isMaxClosetOfferred = True + break + + for item in itemList: + if item in allBankItems and item.furnitureType == CatalogFurnitureItem.MaxBankId: + isMaxBankOffered = True + break + + for item in itemList: + if isinstance(item, CatalogInvalidItem.CatalogInvalidItem): + self.notify.warning('skipping catalog invalid item %s' % item) + continue + if isMaxClosetOfferred and item in allClosetItems and item.furnitureType not in CatalogFurnitureItem.MaxClosetIds: + continue + if isMaxBankOffered and item in allBankItems and item.furnitureType != CatalogFurnitureItem.MaxBankId: + continue + if item.getIsSpecial(): + self.specialPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeSpecial, parentCatalogScreen=self)) + elif item.getEmblemPrices(): + self.emblemPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeWeekly, parentCatalogScreen=self)) + else: + self.panelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeWeekly, parentCatalogScreen=self)) + + itemList = base.localAvatar.backCatalog + itemList.sort(lambda a, b: priceSort(a, b, CatalogItem.CatalogTypeBackorder)) + itemList.reverse() + for item in itemList: + if isinstance(item, CatalogInvalidItem.CatalogInvalidItem): + self.notify.warning('skipping catalog invalid item %s' % item) + continue + if isMaxClosetOfferred and item in allClosetItems and item.furnitureType not in CatalogFurnitureItem.MaxClosetIds: + continue + if isMaxBankOffered and item in allBankItems and item.furnitureType != CatalogFurnitureItem.MaxBankId: + continue + if item.getIsSpecial(): + self.specialPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeSpecial, parentCatalogScreen=self)) + elif item.getEmblemPrices(): + self.emblemPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeBackorder, parentCatalogScreen=self)) + else: + self.backPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeBackorder, parentCatalogScreen=self)) + + numPages = self.packPages(self.panelList, self.pageList, 'new') + self.setNumNewPages(numPages) + numPages = self.packPages(self.backPanelList, self.backPageList, 'back') + self.setNumBackPages(numPages) + numPages = self.packPages(self.specialPanelList, self.specialPageList, 'special') + self.setNumSpecialPages(numPages) + numPages = self.packPages(self.emblemPanelList, self.emblemPageList, 'emblem') + self.setNumEmblemPages(numPages) + currentWeek = base.localAvatar.catalogScheduleCurrentWeek - 1 + if currentWeek < 57: + seriesNumber = currentWeek / ToontownGlobals.CatalogNumWeeksPerSeries + 1 + weekNumber = currentWeek % ToontownGlobals.CatalogNumWeeksPerSeries + 1 + elif currentWeek < 65: + seriesNumber = 6 + weekNumber = currentWeek - 56 + else: + seriesNumber = currentWeek / ToontownGlobals.CatalogNumWeeksPerSeries + 2 + weekNumber = currentWeek % ToontownGlobals.CatalogNumWeeksPerSeries + 1 + geom = NodePath('cover') + cover = guiItems.find('**/cover') + maxSeries = ToontownGlobals.CatalogNumWeeks / ToontownGlobals.CatalogNumWeeksPerSeries + 1 + coverSeries = (seriesNumber - 1) % maxSeries + 1 + coverPicture = cover.find('**/cover_picture%s' % coverSeries) + if not coverPicture.isEmpty(): + coverPicture.reparentTo(geom) + bottomSquare = cover.find('**/cover_bottom') + topSquare = guiItems.find('**/square12b2') + if seriesNumber == 1: + topSquare.setColor(0.651, 0.404, 0.322, 1.0) + bottomSquare.setColor(0.655, 0.522, 0.263, 1.0) + else: + topSquare.setColor(0.651, 0.404, 0.322, 1.0) + bottomSquare.setColor(0.655, 0.522, 0.263, 1.0) + bottomSquare.reparentTo(geom) + topSquare.reparentTo(geom) + cover.find('**/clarabelle_text').reparentTo(geom) + cover.find('**/blue_circle').reparentTo(geom) + cover.find('**/clarabelle').reparentTo(geom) + cover.find('**/circle_green').reparentTo(geom) + self.cover = DirectLabel(self.base, relief=None, geom=geom) + self.catalogNumber = DirectLabel(self.cover, relief=None, scale=0.2, pos=(-0.22, 0, -0.33), text='#%d' % weekNumber, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont()) + self.catalogSeries = DirectLabel(self.cover, relief=None, scale=(0.22, 1, 0.18), pos=(-0.76, 0, -0.9), text=TTLocalizer.CatalogSeriesLabel % seriesNumber, text_fg=(0.9, 0.9, 0.4, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont()) + self.catalogSeries.setShxz(0.4) + self.rings = DirectLabel(self.base, relief=None, geom=guiItems.find('**/rings')) + self.clarabelleFrame = DirectLabel(self, relief=None, image=guiItems.find('**/clarabelle_frame')) + hangupGui = guiItems.find('**/hangup') + hangupRolloverGui = guiItems.find('**/hangup_rollover') + self.hangup = DirectButton(self, relief=None, pos=(-0.158, 0, 0.14), scale=(0.7, 0.7, 0.7), image=[hangupGui, + hangupRolloverGui, + hangupRolloverGui, + hangupGui], text=['', TTLocalizer.CatalogHangUp, TTLocalizer.CatalogHangUp], text_fg=Vec4(1), text_scale=0.07, text_pos=(0.0, 0.14), command=self.hangUp) + self.hangup.reparentTo(base.a2dBottomRight) + self.beanBank = DirectLabel(self, relief=None, image=guiItems.find('**/bean_bank'), text=str(base.localAvatar.getMoney() + base.localAvatar.getBankMoney()), text_align=TextNode.ARight, text_scale=0.11, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0.75, -0.81), text_font=ToontownGlobals.getSignFont()) + nextUp = guiItems.find('**/arrow_up') + nextRollover = guiItems.find('**/arrow_Rollover') + nextDown = guiItems.find('**/arrow_Down') + prevUp = guiItems.find('**/arrowUp') + prevDown = guiItems.find('**/arrowDown1') + prevRollover = guiItems.find('**/arrowRollover') + self.nextPageButton = DirectButton(self, relief=None, pos=(-0.1, 0, -0.9), image=[nextUp, + nextDown, + nextRollover, + nextUp], image_color=(0.9, 0.9, 0.9, 1), image2_color=(1, 1, 1, 1), command=self.showNextPage) + self.backPageButton = DirectButton(self, relief=None, pos=(-0.1, 0, -0.9), image=[prevUp, + prevDown, + prevRollover, + prevUp], image_color=(0.9, 0.9, 0.9, 1), image2_color=(1, 1, 1, 1), command=self.showBackPage) + self.backPageButton.hide() + self.pageLabel = DirectLabel(self.base, relief=None, pos=(-1.33, 0, -0.9), scale=0.06, text=TTLocalizer.CatalogPagePrefix, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft) + self.loadClarabelle() + return + + def loadClarabelle(self): + self.cRender = NodePath('cRender') + self.cCamera = self.cRender.attachNewNode('cCamera') + self.cCamNode = Camera('cCam') + self.cLens = PerspectiveLens() + self.cLens.setFov(40, 40) + self.cLens.setNear(0.1) + self.cLens.setFar(100.0) + self.cCamNode.setLens(self.cLens) + self.cCamNode.setScene(self.cRender) + self.cCam = self.cCamera.attachNewNode(self.cCamNode) + self.cDr = base.win.makeDisplayRegion(0.56, 0.81, 0.52, 0.85) + self.cDr.setSort(1) + self.cDr.setClearDepthActive(1) + self.cDr.setClearColorActive(1) + self.cDr.setClearColor(Vec4(0.3, 0.3, 0.3, 1)) + self.cDr.setCamera(self.cCam) + self.clarabelle = Actor.Actor('phase_5.5/models/char/Clarabelle-zero', {'listen': 'phase_5.5/models/char/Clarabelle-listens'}) + self.clarabelle.loop('listen') + self.clarabelle.find('**/eyes').setBin('fixed', 0) + self.clarabelle.find('**/pupilL').setBin('fixed', 1) + self.clarabelle.find('**/pupilR').setBin('fixed', 1) + self.clarabelle.find('**/glassL').setBin('fixed', 2) + self.clarabelle.find('**/glassR').setBin('fixed', 2) + switchboard = loader.loadModel('phase_5.5/models/estate/switchboard') + switchboard.reparentTo(self.clarabelle) + switchboard.setPos(1, -1.6, 0) + switchboard.setH(30) + room = loader.loadModel('phase_3/models/makeatoon/tt_m_ara_mat_room.bam') + room.reparentTo(self.clarabelle) + room.find('**/genderProps').removeNode() + room.find('**/bodyWalls').removeNode() + room.find('**/bodyProps').removeNode() + room.find('**/colorWalls').removeNode() + room.find('**/colorProps').removeNode() + room.find('**/clothWalls').removeNode() + room.find('**/nameWalls').removeNode() + room.find('**/nameProps').removeNode() + room.find('**/spotlight').removeNode() + room.setPos(5.5, 1.25, 0) + room.setH(330) + self.clarabelle.reparentTo(self.cRender) + self.clarabelle.setPosHprScale(-0.52, 6.13, -3.81, 85, 0.0, 0.0, 1.0, 1.0, 1.0) + self.clarabelleFrame.setPosHprScale(-0.01, 0.0, -0.01, 0.0, 0.0, 0.0, 1.02, 1.0, 1.02) + + def reload(self): + for panel in self.panelList + self.backPanelList + self.specialPanelList + self.emblemPanelList: + panel.destroy() + + def priceSort(a, b, type): + priceA = a.getPrice(type) + priceB = b.getPrice(type) + return priceB - priceA + + self.pageIndex = -1 + self.maxPageIndex = 0 + self.numNewPages = 0 + self.numBackPages = 5 + self.numSpecialPages = 0 + self.viewing = 'New' + self.panelList = [] + self.backPanelList = [] + self.specialList = [] + self.pageList = [] + self.backPageList = [] + self.specialPanelList = [] + self.specialPageList = [] + self.panelDict = {} + self.visiblePanels = [] + itemList = base.localAvatar.monthlyCatalog + base.localAvatar.weeklyCatalog + itemList.sort(lambda a, b: priceSort(a, b, CatalogItem.CatalogTypeWeekly)) + itemList.reverse() + for item in itemList: + if item.getIsSpecial(): + self.specialPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeSpecial, parentCatalogScreen=self)) + else: + self.panelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeWeekly)) + + itemList = base.localAvatar.backCatalog + itemList.sort(lambda a, b: priceSort(a, b, CatalogItem.CatalogTypeBackorder)) + itemList.reverse() + for item in itemList: + if item.getIsSpecial(): + self.specialPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeSpecial, parentCatalogScreen=self)) + else: + self.backPanelList.append(CatalogItemPanel.CatalogItemPanel(parent=hidden, item=item, type=CatalogItem.CatalogTypeBackorder)) + + numPages = self.packPages(self.panelList, self.pageList, 'new') + self.setNumNewPages(numPages) + numPages = self.packPages(self.backPanelList, self.backPageList, 'back') + self.setNumBackPages(numPages) + numPages = self.packPages(self.specialPanelList, self.specialPageList, 'special') + self.setNumSpecialPages(numPages) + seriesNumber = (base.localAvatar.catalogScheduleCurrentWeek - 1) / ToontownGlobals.CatalogNumWeeksPerSeries + 1 + self.catalogSeries['text'] = Localizer.CatalogSeriesLabel % seriesNumber + weekNumber = (base.localAvatar.catalogScheduleCurrentWeek - 1) % ToontownGlobals.CatalogNumWeeksPerSeries + 1 + self.catalogNumber['text'] = '#%d' % weekNumber + self.enableBackorderCatalogButton() + self.setMaxPageIndex(self.numNewPages) + self.setPageIndex(-1) + self.showPageItems() + + def unload(self): + taskMgr.remove('clearClarabelleChat') + taskMgr.remove('postGoodbyeHangUp') + taskMgr.remove('clarabelleGreeting') + taskMgr.remove('clarabelleHelpText1') + taskMgr.remove('clarabelleAskAnythingElse') + taskMgr.remove('friendButtonsReady') + self.hide() + self.hangup.hide() + self.destroy() + del self.base + del self.squares + for panel in self.panelList + self.backPanelList + self.specialPanelList + self.emblemPanelList: + panel.destroy() + + del self.panelList + del self.backPanelList + del self.cover + del self.rings + del self.clarabelleFrame + del self.hangup + del self.beanBank + del self.silverLabel + del self.goldLabel + del self.nextPageButton + del self.backPageButton + del self.newCatalogButton + del self.newCatalogButton2 + del self.backCatalogButton + del self.backCatalogButton2 + del self.specialCatalogButton + del self.specialCatalogButton2 + del self.pageLabel + if self.createdGiftGui: + del self.giftToggle + del self.giftLabel + del self.friendLabel + del self.scrollList + del self.friend + del self.friends + self.unloadClarabelle() + if self.responseDialog: + self.responseDialog.cleanup() + self.responseDialog = None + + def unloadClarabelle(self): + base.win.removeDisplayRegion(self.cDr) + del self.cRender + del self.cCamera + del self.cCamNode + del self.cLens + del self.cCam + del self.cDr + self.clarabelle.cleanup() + del self.clarabelle + + if self.clarabelleChatBalloon: + self.clarabelleChatBalloon.removeNode() + del self.clarabelleChatBalloon + + def hangUp(self): + self.setClarabelleChat(random.choice(TTLocalizer.CatalogGoodbyeList), type='goodbye') + self.setPageIndex(-1) + self.showPageItems() + self.nextPageButton.hide() + self.backPageButton.hide() + self.newCatalogButton.hide() + self.newCatalogButton2.hide() + self.backCatalogButton.hide() + self.backCatalogButton2.hide() + self.specialCatalogButton.hide() + self.specialCatalogButton2.hide() + self.emblemCatalogButton.hide() + self.emblemCatalogButton2.hide() + self.hangup.hide() + taskMgr.remove('clarabelleGreeting') + taskMgr.remove('clarabelleHelpText1') + taskMgr.remove('clarabelleAskAnythingElse') + + def postGoodbyeHangUp(task): + messenger.send(self['doneEvent']) + self.unload() + + taskMgr.doMethodLater(1.5, postGoodbyeHangUp, 'postGoodbyeHangUp') + + def remoteUpdate(self): + self.update() + + def update(self, task = None): + if (not self.friend) and self.gifting == 1: + self.__giftToggle() + if hasattr(self, 'beanBank'): + self.beanBank['text'] = str(base.localAvatar.getTotalMoney()) + for item in self.panelList + self.backPanelList + self.specialPanelList + self.emblemPanelList: + if type(item) != type(''): + item.updateButtons(self.gifting) + + def __handlePurchaseRequest(self, item): + item.requestPurchase(self['phone'], self.__handlePurchaseResponse) + taskMgr.remove('clarabelleAskAnythingElse') + + def __handleGiftPurchaseRequest(self, item): + item.requestGiftPurchase(self['phone'], self.friendAvId, self.__handleGiftPurchaseResponse) + taskMgr.remove('clarabelleAskAnythingElse') + + def __handlePurchaseResponse(self, retCode, item): + if retCode == ToontownGlobals.P_UserCancelled: + self.update() + return + + if hasattr(item, 'houseId') and retCode == ToontownGlobals.P_ItemAvailable: + localAvatar.houseType = item.houseId + + taskMgr.doMethodLater(0.5, self.update, 'purchaseUpdate') + self.setClarabelleChat(item.getRequestPurchaseErrorText(retCode), item.getRequestPurchaseErrorTextTimeout()) + + def __handleGiftPurchaseResponse(self, retCode, item): + if retCode == ToontownGlobals.P_UserCancelled: + return + if self.isEmpty() or self.isHidden(): + return + self.setClarabelleChat(item.getRequestGiftPurchaseErrorText(retCode) % self.friendName) + self.__loadFriend() + + def askAnythingElse(task): + self.setClarabelleChat(TTLocalizer.CatalogAnythingElse) + + if retCode >= 0: + taskMgr.doMethodLater(8, askAnythingElse, 'clarabelleAskAnythingElse') + + def __clearDialog(self, event): + self.responseDialog.cleanup() + self.responseDialog = None + return + + def setClarabelleChat(self, str, timeout = 6, type = None): + self.clearClarabelleChat() + + if not self.clarabelleChatBalloon: + self.clarabelleChatBalloon = loader.loadModel('phase_3/models/props/chatbox') + + self.clarabelleChat = ChatBalloon(self.clarabelleChatBalloon) + chatNode = self.clarabelleChat.generate(str, ToontownGlobals.getInterfaceFont())[0] + self.clarabelleChatNP = self.attachNewNode(chatNode.node(), 1000) + self.clarabelleChatNP.setScale(0.08) + self.clarabelleChatNP.setPos(0.7, 0, 0.6) + + if timeout: + taskMgr.doMethodLater(timeout, self.clearClarabelleChat, 'clearClarabelleChat') + + def clearClarabelleChat(self, task = None): + taskMgr.remove('clearClarabelleChat') + if self.clarabelleChatNP: + self.clarabelleChatNP.removeNode() + self.clarabelleChatNP = None + del self.clarabelleChat + return + + def __moneyChange(self, money): + self.update() + + def __bankMoneyChange(self, bankMoney): + self.update() + + def __emblemChange(self, newEmblems): + self.silverLabel['text'] = str(newEmblems[0]) + self.goldLabel['text'] = str(newEmblems[1]) + + def showEmblems(self): + if base.cr.wantEmblems: + self.silverLabel.show() + self.goldLabel.show() + + def hideEmblems(self): + self.silverLabel.hide() + self.goldLabel.hide() + + def __makeFriendList(self): + for av in base.cr.avList: + if av.id != base.localAvatar.doId: + self.friendList.append((av.id, av.name, NametagGroup.CCNonPlayer)) + + for id, handle in base.cr.friendsMap.items(): + if isinstance(handle, FriendHandle.FriendHandle): + self.friendList.append((id, handle.getName(), NametagConstants.getFriendColor(handle))) + + def __makeScrollList(self): + for friend in self.friendList: + button = self.makeFriendButton(*friend) + self.scrollList.addItem(button, refresh=0) + self.friends.append(button) + + self.scrollList.refresh() + + def makeFriendButton(self, avId, name, colorCode): + color = NametagConstants.NAMETAG_COLORS[colorCode] + + return DirectButton(relief=None, text=name, text_scale=0.04, text_align=TextNode.ALeft, text_fg=color[0][0], text1_bg=(1, 1, 0, 1), + text2_bg=(0.5, 0.9, 1, 1), text3_fg=(0.4, 0.8, 0.4, 1), command=self.__chooseFriend, extraArgs=[avId, name]) + + def __chooseFriend(self, avId, name): + messenger.send('wakeup') + + if self.friendAvId == avId: + return + + self.friendAvId = avId + self.friendName = name + self.__loadFriend() + + def __loadFriend(self): + if not self.friendAvId: + return + + for friendButton in self.friends: + friendButton['state'] = DGG.DISABLED + + self.friend = None + self.friendLabel['text'] = TTLocalizer.CatalogGiftUpdating + taskMgr.remove('friendButtonsReady') + self['phone'].requestGiftAvatar(self.friendAvId) + + def setFriendReady(self, friend): + self.friend = friend + self.friendLabel['text'] = TTLocalizer.CatalogGiftTo % self.friendName + taskMgr.doMethodLater(1.5, self.setFriendButtonsReady, 'friendButtonsReady') + self.update() + + def setFriendButtonsReady(self, task=None): + for friendButton in self.friends: + friendButton['state'] = DGG.NORMAL + + def __giftToggle(self): + messenger.send('wakeup') + if self.gifting == -1: + self.gifting = 1 + self.giftLabel.show() + self.friendLabel.show() + self.scrollList.show() + self.hideEmblems() + self.giftToggle['text'] = TTLocalizer.CatalogGiftToggleOn + self.friendLabel['text'] = TTLocalizer.CatalogGiftChoose + self.__loadFriend() + else: + self.friend = None + self.friendAvId = 0 + self.friendName = None + self.gifting = -1 + self.giftLabel.hide() + self.friendLabel.hide() + self.scrollList.hide() + self.showEmblems() + self.giftToggle['text'] = TTLocalizer.CatalogGiftToggleOff + self.update() \ No newline at end of file diff --git a/toontown/catalog/CatalogSurfaceColors.py b/toontown/catalog/CatalogSurfaceColors.py new file mode 100755 index 00000000..16378bc7 --- /dev/null +++ b/toontown/catalog/CatalogSurfaceColors.py @@ -0,0 +1,75 @@ +CT_WHITE = (1.0, 1.0, 1.0, 1.0) +CT_RED = (1.0, 0.5, 0.5, 1.0) +CT_BROWN = (0.641, 0.355, 0.27, 1.0) +CT_CANTELOPE = (0.839, 0.651, 0.549, 1.0) +CT_TAN = (0.996, 0.695, 0.512, 1.0) +CT_ORANGE = (0.992, 0.48, 0.168, 1.0) +CT_CORAL = (0.832, 0.5, 0.297, 1.0) +CT_PEACH = (1.0, 0.82, 0.7, 1.0) +CT_BEIGE = (1.0, 0.8, 0.6, 1.0) +CT_TAN2 = (0.808, 0.678, 0.51, 1.0) +CT_SIENNA = (0.57, 0.449, 0.164, 1.0) +CT_YELLOW = (0.996, 0.898, 0.32, 1.0) +CT_CREAM = (0.996, 0.957, 0.598, 1.0) +CT_BEIGE2 = (1.0, 1.0, 0.6, 1.0) +CT_YELLOW2 = (1.0, 1.0, 0.7, 1.0) +CT_CITRINE = (0.855, 0.934, 0.492, 1.0) +CT_FOREST_GREEN = (0.5, 0.586, 0.4, 1.0) +CT_LINE = (0.551, 0.824, 0.324, 1.0) +CT_PALE_GREEN = (0.789, 1.0, 0.7, 1.0) +CT_GREEN = (0.305, 0.969, 0.402, 1.0) +CT_TEAL = (0.6, 1.0, 0.8, 1.0) +CT_SEA_GREEN = (0.242, 0.742, 0.516, 1.0) +CT_LIGHT_BLUE = (0.434, 0.906, 0.836, 1.0) +CT_AQUA = (0.348, 0.82, 0.953, 1.0) +CT_BLUE = (0.191, 0.563, 0.773, 1.0) +CT_LIGHT_BLUE2 = (0.875, 0.937, 1.0, 1.0) +CT_PERIWINKLE = (0.559, 0.59, 0.875, 1.0) +CT_ROYAL_BLUE = (0.285, 0.328, 0.727, 1.0) +CT_GREY = (0.7, 0.7, 0.8, 1.0) +CT_BLUE2 = (0.6, 0.6, 1.0, 1.0) +CT_SLATE_BLUE = (0.461, 0.379, 0.824, 1.0) +CT_PURPLE = (0.547, 0.281, 0.75, 1.0) +CT_LAVENDER = (0.727, 0.473, 0.859, 1.0) +CT_PINK = (0.898, 0.617, 0.906, 1.0) +CT_PINK2 = (1.0, 0.6, 1.0, 1.0) +CT_MAROON = (0.711, 0.234, 0.438, 1.0) +CT_PEACH2 = (0.969, 0.691, 0.699, 1.0) +CT_RED2 = (0.863, 0.406, 0.418, 1.0) +CT_BRIGHT_RED = (0.934, 0.266, 0.281, 1.0) +CT_DARK_WOOD = (0.69, 0.741, 0.71, 1.0) +CT_DARK_WALNUT = (0.549, 0.412, 0.259, 1.0) +CT_GENERIC_DARK = (0.443, 0.333, 0.176, 1.0) +CT_PINE = (1.0, 0.812, 0.49, 1.0) +CT_CHERRY = (0.71, 0.408, 0.267, 1.0) +CT_BEECH = (0.961, 0.659, 0.4, 1.0) +CTFlatColor = [CT_BEIGE, + CT_TEAL, + CT_BLUE2, + CT_PINK2, + CT_BEIGE2, + CT_RED] +CTValentinesColors = [CT_PINK2, CT_RED] +CTUnderwaterColors = [CT_WHITE, + CT_TEAL, + CT_SEA_GREEN, + CT_LIGHT_BLUE, + CT_PALE_GREEN, + CT_AQUA, + CT_CORAL, + CT_PEACH] +CTFlatColorDark = [] +tint = 0.75 +for color in CTFlatColor: + CTFlatColorDark.append((color[0] * tint, + color[1] * tint, + color[2] * tint, + 1.0)) + +CTFlatColorAll = CTFlatColor + CTFlatColorDark +CTBasicWoodColorOnWhite = [CT_DARK_WALNUT, + CT_GENERIC_DARK, + CT_PINE, + CT_CHERRY, + CT_BEECH] +CTWhite = [CT_WHITE] diff --git a/toontown/catalog/CatalogSurfaceItem.py b/toontown/catalog/CatalogSurfaceItem.py new file mode 100755 index 00000000..83d6ac54 --- /dev/null +++ b/toontown/catalog/CatalogSurfaceItem.py @@ -0,0 +1,33 @@ +import CatalogItem +import CatalogAtticItem +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from CatalogSurfaceColors import * +STWallpaper = 0 +STMoulding = 1 +STFlooring = 2 +STWainscoting = 3 +NUM_ST_TYPES = 4 + +class CatalogSurfaceItem(CatalogAtticItem.CatalogAtticItem): + + def makeNewItem(self): + CatalogAtticItem.CatalogAtticItem.makeNewItem(self) + + def setPatternIndex(self, patternIndex): + self.patternIndex = patternIndex + + def setColorIndex(self, colorIndex): + self.colorIndex = colorIndex + + def saveHistory(self): + return 1 + + def recordPurchase(self, avatar, optional): + house, retcode = self.getHouseInfo(avatar) + if retcode >= 0: + house.addWallpaper(self) + return retcode + + def getDeliveryTime(self): + return 60 diff --git a/toontown/catalog/CatalogTankItem.py b/toontown/catalog/CatalogTankItem.py new file mode 100644 index 00000000..c4cc612c --- /dev/null +++ b/toontown/catalog/CatalogTankItem.py @@ -0,0 +1,95 @@ +import CatalogItem +from toontown.toonbase import ToontownGlobals +from toontown.fishing import FishGlobals +from direct.actor import Actor +from toontown.toonbase import TTLocalizer +from direct.interval.IntervalGlobal import * + +class CatalogTankItem(CatalogItem.CatalogItem): + sequenceNumber = 0 + + def makeNewItem(self, maxTank): + self.maxTank = maxTank + CatalogItem.CatalogItem.makeNewItem(self) + + def getPurchaseLimit(self): + return 1 + + def reachedPurchaseLimit(self, avatar): + return avatar.getMaxFishTank() >= self.maxTank or self in avatar.onOrder or self in avatar.mailboxContents + + def saveHistory(self): + return 1 + + def getTypeName(self): + return TTLocalizer.TankTypeName + + def getName(self): + return TTLocalizer.FishTank % TTLocalizer.FishTankNameDict[self.maxTank] + + def recordPurchase(self, avatar, optional): + if self.maxTank < 0 or self.maxTank > FishGlobals.MaxTank: + return ToontownGlobals.P_InvalidIndex + if self.maxTank <= avatar.getMaxFishTank(): + return ToontownGlobals.P_ItemUnneeded + avatar.b_setMaxFishTank(self.maxTank) + return ToontownGlobals.P_ItemOnOrder + + def isGift(self): + return 0 + + def getDeliveryTime(self): + return 1 + + def getPicture(self, avatar): + gui = loader.loadModel('phase_4/models/gui/fishingGui') + bucket = gui.find('**/bucket') + bucket.setScale(2.7) + bucket.setPos(-3.15, 0, 3.2) + + frame = self.makeFrame() + bucket.reparentTo(frame) + gui.removeNode() + return (frame, None) + + def getAcceptItemErrorText(self, retcode): + if retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.CatalogAcceptTank + elif retcode == ToontownGlobals.P_ItemUnneeded: + return TTLocalizer.CatalogAcceptTankUnneeded + return CatalogItem.CatalogItem.getAcceptItemErrorText(self, retcode) + + def output(self, store = -1): + return 'CatalogTankItem(%s%s)' % (self.maxTank, self.formatOptionalData(store)) + + def compareTo(self, other): + return self.maxTank - other.maxTank + + def getHashContents(self): + return self.maxTank + + def getBasePrice(self): + return FishGlobals.TankPriceDict[self.maxTank] + + def decodeDatagram(self, di, versionNumber, store): + CatalogItem.CatalogItem.decodeDatagram(self, di, versionNumber, store) + self.maxTank = di.getUint8() + + def encodeDatagram(self, dg, store): + CatalogItem.CatalogItem.encodeDatagram(self, dg, store) + dg.addUint8(self.maxTank) + +def nextAvailableTank(avatar, duplicateItems): + tank = avatar.getMaxFishTank() + + if not tank in FishGlobals.NextTank: + return None + + return CatalogTankItem(FishGlobals.NextTank[tank]) + +def getAllTanks(): + list = [] + for old, new in FishGlobals.NextTank.iteritems(): + list.append(CatalogTankItem(new)) + + return list diff --git a/toontown/catalog/CatalogToonStatueItem.py b/toontown/catalog/CatalogToonStatueItem.py new file mode 100755 index 00000000..dcd4fe99 --- /dev/null +++ b/toontown/catalog/CatalogToonStatueItem.py @@ -0,0 +1,64 @@ +import CatalogGardenItem +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +from direct.interval.IntervalGlobal import * +from toontown.estate import GardenGlobals + +class CatalogToonStatueItem(CatalogGardenItem.CatalogGardenItem): + pictureToonStatue = None + + def makeNewItem(self, itemIndex = 105, count = 1, tagCode = 1, endPoseIndex = 108): + self.startPoseIndex = itemIndex + self.endPoseIndex = endPoseIndex + CatalogGardenItem.CatalogGardenItem.makeNewItem(self, itemIndex, count, tagCode) + + def needsCustomize(self): + return self.endPoseIndex - self.startPoseIndex > 0 + + def getPicture(self, avatar): + from toontown.estate import DistributedToonStatuary + toonStatuary = DistributedToonStatuary.DistributedToonStatuary(None) + toonStatuary.setupStoneToon(base.localAvatar.style) + toonStatuary.poseToonFromSpecialsIndex(self.gardenIndex) + toonStatuary.toon.setZ(0) + model, ival = self.makeFrameModel(toonStatuary.toon, 1) + self.pictureToonStatue = toonStatuary + self.hasPicture = True + toonStatuary.toon.setBin('gui-popup', 60) + return (model, ival) + + def cleanupPicture(self): + self.pictureToonStatue.deleteToon() + self.pictureToonStatue = None + CatalogGardenItem.CatalogGardenItem.cleanupPicture(self) + return + + def decodeDatagram(self, di, versionNumber, store): + CatalogGardenItem.CatalogGardenItem.decodeDatagram(self, di, versionNumber, store) + self.startPoseIndex = di.getUint8() + self.endPoseIndex = di.getUint8() + + def encodeDatagram(self, dg, store): + CatalogGardenItem.CatalogGardenItem.encodeDatagram(self, dg, store) + dg.addUint8(self.startPoseIndex) + dg.addUint8(self.endPoseIndex) + + def compareTo(self, other): + if self.gardenIndex >= self.startPoseIndex and self.gardenIndex <= self.endPoseIndex: + return 0 + return 1 + + def getAllToonStatues(self): + self.statueList = [] + for index in xrange(self.startPoseIndex, self.endPoseIndex + 1): + self.statueList.append(CatalogToonStatueItem(index, 1, endPoseIndex=index)) + + return self.statueList + + def deleteAllToonStatues(self): + while len(self.statueList): + item = self.statueList[0] + if item.pictureToonStatue: + item.pictureToonStatue.deleteToon() + self.statueList.remove(item) diff --git a/toontown/catalog/CatalogWainscotingItem.py b/toontown/catalog/CatalogWainscotingItem.py new file mode 100755 index 00000000..0d619cc5 --- /dev/null +++ b/toontown/catalog/CatalogWainscotingItem.py @@ -0,0 +1,140 @@ +from CatalogSurfaceItem import * +WSTTextureName = 0 +WSTColor = 1 +WSTBasePrice = 2 +WainscotingTypes = {1000: ('phase_3.5/maps/wall_paper_b3.jpg', CTFlatColorDark, 200), + 1010: ('phase_5.5/maps/wall_paper_b4_greyscale.jpg', CTBasicWoodColorOnWhite, 200), + 1020: ('phase_5.5/maps/wainscotings_neutral.jpg', CTBasicWoodColorOnWhite, 200), + 1030: ('phase_3.5/maps/wall_paper_b3.jpg', CTValentinesColors, 200), + 1040: ('phase_3.5/maps/wall_paper_b3.jpg', CTUnderwaterColors, 200)} + +class CatalogWainscotingItem(CatalogSurfaceItem): + + def makeNewItem(self, patternIndex, colorIndex): + self.patternIndex = patternIndex + self.colorIndex = colorIndex + CatalogSurfaceItem.makeNewItem(self) + + def getTypeName(self): + return TTLocalizer.SurfaceNames[STWainscoting] + + def getName(self): + name = TTLocalizer.WainscotingNames.get(self.patternIndex) + if name: + return name + return self.getTypeName() + + def getSurfaceType(self): + return STWainscoting + + def getPicture(self, avatar): + frame = self.makeFrame() + sample = loader.loadModel('phase_5.5/models/estate/wallpaper_sample') + a = sample.find('**/a') + b = sample.find('**/b') + c = sample.find('**/c') + a.hide() + b.hide() + c.setTexture(self.loadTexture(), 1) + c.setColorScale(*self.getColor()) + sample.reparentTo(frame) + self.hasPicture = True + return (frame, None) + + def output(self, store = -1): + return 'CatalogWainscotingItem(%s, %s%s)' % (self.patternIndex, self.colorIndex, self.formatOptionalData(store)) + + def getFilename(self): + return WainscotingTypes[self.patternIndex][WSTTextureName] + + def compareTo(self, other): + if self.patternIndex != other.patternIndex: + return self.patternIndex - other.patternIndex + return self.colorIndex - other.colorIndex + + def getHashContents(self): + return (self.patternIndex, self.colorIndex) + + def getBasePrice(self): + return WainscotingTypes[self.patternIndex][WSTBasePrice] + + def loadTexture(self): + from pandac.PandaModules import Texture + filename = WainscotingTypes[self.patternIndex][WSTTextureName] + texture = loader.loadTexture(filename) + texture.setMinfilter(Texture.FTLinearMipmapLinear) + texture.setMagfilter(Texture.FTLinear) + return texture + + def getColor(self): + if self.colorIndex == None: + colorIndex = 0 + else: + colorIndex = self.colorIndex + colors = WainscotingTypes[self.patternIndex][WSTColor] + if colors: + if colorIndex < len(colors): + return colors[colorIndex] + else: + print 'Warning: colorIndex not in colors. Returning white.' + return CT_WHITE + else: + return CT_WHITE + return + + def decodeDatagram(self, di, versionNumber, store): + CatalogAtticItem.CatalogAtticItem.decodeDatagram(self, di, versionNumber, store) + self.patternIndex = di.getUint16() + self.colorIndex = di.getUint8() + wtype = WainscotingTypes[self.patternIndex] + + def encodeDatagram(self, dg, store): + CatalogAtticItem.CatalogAtticItem.encodeDatagram(self, dg, store) + dg.addUint16(self.patternIndex) + dg.addUint8(self.colorIndex) + + +def getWainscotings(*indexList): + list = [] + for index in indexList: + list.append(CatalogWainscotingItem(index)) + + return list + + +def getAllWainscotings(*indexList): + list = [] + for index in indexList: + colors = WainscotingTypes[index][WSTColor] + if colors: + for n in xrange(len(colors)): + list.append(CatalogWainscotingItem(index, n)) + + else: + list.append(CatalogWainscotingItem(index, 0)) + + return list + + +def getWainscotingRange(fromIndex, toIndex, *otherRanges): + list = [] + froms = [fromIndex] + tos = [toIndex] + i = 0 + while i < len(otherRanges): + froms.append(otherRanges[i]) + tos.append(otherRanges[i + 1]) + i += 2 + + for patternIndex in WainscotingTypes.keys(): + for fromIndex, toIndex in zip(froms, tos): + if patternIndex >= fromIndex and patternIndex <= toIndex: + colors = WainscotingTypes[patternIndex][WSTColor] + if colors: + for n in xrange(len(colors)): + list.append(CatalogWainscotingItem(patternIndex, n)) + + else: + list.append(CatalogWainscotingItem(patternIndex, 0)) + + return list diff --git a/toontown/catalog/CatalogWallpaperItem.py b/toontown/catalog/CatalogWallpaperItem.py new file mode 100755 index 00000000..11ce4647 --- /dev/null +++ b/toontown/catalog/CatalogWallpaperItem.py @@ -0,0 +1,740 @@ +from CatalogSurfaceItem import * +WTTextureName = 0 +WTColor = 1 +WTBorderList = 2 +WTBasePrice = 3 +BDTextureName = 0 +BDColor = 1 +All = (1000, + 1010, + 1020, + 1030, + 1040, + 1050, + 1060, + 1070) +WallpaperTypes = {1000: ('phase_5.5/maps/flat_wallpaper1.jpg', + CTFlatColor, + (0, 1000), + 180), + 1100: ('phase_5.5/maps/big_stripes1.jpg', + CTWhite, + (0, 1010), + 180), + 1110: ('phase_5.5/maps/big_stripes2.jpg', + CTWhite, + (0, 1040), + 180), + 1120: ('phase_5.5/maps/big_stripes3.jpg', + CTWhite, + (0, 1030), + 180), + 1130: ('phase_5.5/maps/big_stripes4.jpg', + CTWhite, + (0, 1010), + 180), + 1140: ('phase_5.5/maps/big_stripes5.jpg', + CTWhite, + (0, 1020), + 180), + 1150: ('phase_5.5/maps/big_stripes6.jpg', + CTWhite, + (0, 1020), + 180), + 1200: ('phase_5.5/maps/stripeB1.jpg', + CTWhite, + (0, 1000), + 180), + 1210: ('phase_5.5/maps/stripeB2.jpg', + CTWhite, + (0, 1000), + 180), + 1220: ('phase_5.5/maps/stripeB3.jpg', + CTWhite, + (0, 1000), + 180), + 1230: ('phase_5.5/maps/stripeB4.jpg', + CTWhite, + (0, 1000), + 180), + 1240: ('phase_3.5/maps/stripeB5.jpg', + CTWhite, + (0, 1000), + 180), + 1250: ('phase_5.5/maps/stripeB6.jpg', + CTWhite, + (0, 1000), + 180), + 1260: ('phase_5.5/maps/stripeB7.jpg', + CTWhite, + (0, 1000), + 180), + 1300: ('phase_5.5/maps/squiggle1.jpg', + CTWhite, + (0,), + 180), + 1310: ('phase_5.5/maps/squiggle2.jpg', + CTWhite, + (0,), + 180), + 1320: ('phase_5.5/maps/squiggle3.jpg', + CTWhite, + (0,), + 180), + 1330: ('phase_5.5/maps/squiggle4.jpg', + CTWhite, + (0,), + 180), + 1340: ('phase_5.5/maps/squiggle5.jpg', + CTWhite, + (0,), + 180), + 1350: ('phase_5.5/maps/squiggle6.jpg', + CTWhite, + (0,), + 180), + 1400: ('phase_5.5/maps/stripes_cyan.jpg', + CTWhite, + (0, 1000), + 180), + 1410: ('phase_5.5/maps/stripes_green.jpg', + CTWhite, + (0, 1000), + 180), + 1420: ('phase_5.5/maps/stripes_magenta.jpg', + CTWhite, + (0, 1000), + 180), + 1430: ('phase_5.5/maps/two_stripes1.jpg', + CTWhite, + (0, 1000), + 180), + 1440: ('phase_5.5/maps/two_stripes2.jpg', + CTWhite, + (0, 1000), + 180), + 1450: ('phase_5.5/maps/two_stripes3.jpg', + CTWhite, + (0, 1000), + 180), + 1500: ('phase_5.5/maps/leaves1.jpg', + CTWhite, + (0,), + 180), + 1510: ('phase_5.5/maps/leaves2.jpg', + CTWhite, + (0,), + 180), + 1520: ('phase_5.5/maps/leaves3.jpg', + CTWhite, + (0,), + 180), + 1600: ('phase_5.5/maps/diamonds2_cherries.jpg', + CTWhite, + (0, 1000), + 180), + 1610: ('phase_5.5/maps/diamonds3_cherries.jpg', + CTWhite, + (0, 1000), + 180), + 1620: ('phase_5.5/maps/diamonds3_cherry.jpg', + CTWhite, + (0, 1000), + 180), + 1630: ('phase_5.5/maps/diamonds4_cherries.jpg', + CTWhite, + (0, 1000), + 180), + 1640: ('phase_5.5/maps/diamonds4_cherry.jpg', + CTWhite, + (0, 1000), + 180), + 1650: ('phase_5.5/maps/diamonds5_cherries.jpg', + CTWhite, + (0, 1000), + 180), + 1660: ('phase_5.5/maps/diamonds6_cherry.jpg', + CTWhite, + (0, 1000), + 180), + 1700: ('phase_5.5/maps/moon1.jpg', + CTWhite, + (0,), + 180), + 1710: ('phase_5.5/maps/moon2.jpg', + CTWhite, + (0,), + 180), + 1720: ('phase_5.5/maps/moon3.jpg', + CTWhite, + (0,), + 180), + 1730: ('phase_5.5/maps/moon4.jpg', + CTWhite, + (0,), + 180), + 1740: ('phase_5.5/maps/moon5.jpg', + CTWhite, + (0,), + 180), + 1750: ('phase_5.5/maps/moon6.jpg', + CTWhite, + (0,), + 180), + 1760: ('phase_5.5/maps/moon7.jpg', + CTWhite, + (0,), + 180), + 1800: ('phase_5.5/maps/stars1.jpg', + CTWhite, + (0,), + 180), + 1810: ('phase_5.5/maps/stars2.jpg', + (CT_BLUE2, CT_PINK2, CT_RED), + (0,), + 180), + 1820: ('phase_5.5/maps/stars3.jpg', + (CT_BLUE2, + CT_PINK2, + CT_RED, + CT_WHITE), + (0,), + 180), + 1830: ('phase_5.5/maps/stars4.jpg', + CTWhite, + (0,), + 180), + 1840: ('phase_5.5/maps/stars5.jpg', + CTWhite, + (0,), + 180), + 1850: ('phase_5.5/maps/stars6.jpg', + CTWhite, + (0,), + 180), + 1860: ('phase_5.5/maps/stars7.jpg', + (CT_BEIGE2, CT_WHITE), + (0,), + 180), + 1900: ('phase_5.5/maps/wall_paper_flower1.jpg', + CTWhite, + (0, 1000), + 180), + 1910: ('phase_5.5/maps/wall_paper_flower2.jpg', + CTWhite, + (0, 1000), + 180), + 1920: ('phase_5.5/maps/wall_paper_flower3.jpg', + CTWhite, + (0, 1000), + 180), + 1930: ('phase_5.5/maps/wall_paper_flower4.jpg', + CTWhite, + (0, 1000), + 180), + 1940: ('phase_5.5/maps/wall_paper_flower5.jpg', + CTWhite, + (0, 1000), + 180), + 1950: ('phase_5.5/maps/wall_paper_flower6.jpg', + CTWhite, + (0, 1000), + 180), + 2000: ('phase_5.5/maps/flat_wallpaper1.jpg', + (CT_BEIGE, CT_BEIGE2, CT_RED), + (1050,), + 180), + 2010: ('phase_5.5/maps/flat_wallpaper1.jpg', + (CT_BLUE2, CT_PINK2), + (1060,), + 180), + 2020: ('phase_5.5/maps/flat_wallpaper1.jpg', + (CT_BEIGE2, + CT_BLUE2, + CT_PINK2, + CT_BEIGE, + CT_RED), + (1070,), + 180), + 2100: ('phase_5.5/maps/big_stripes1.jpg', + CTWhite, + (1050,), + 180), + 2110: ('phase_5.5/maps/big_stripes2.jpg', + CTWhite, + (1050,), + 180), + 2120: ('phase_5.5/maps/big_stripes3.jpg', + CTWhite, + (1060,), + 180), + 2130: ('phase_5.5/maps/big_stripes3.jpg', + CTWhite, + (1070,), + 180), + 2140: ('phase_5.5/maps/big_stripes6.jpg', + CTWhite, + (1070,), + 180), + 2200: ('phase_5.5/maps/wall_paper_car.jpg', + CTWhite, + (0, 1000), + 180), + 2210: ('phase_5.5/maps/wall_paper_car_neutral.jpg', + CTFlatColor, + (0, 1000), + 180), + 2300: ('phase_5.5/maps/wall_paper_football_neutral.jpg', + CTFlatColor, + (0, 1080), + 180), + 2400: ('phase_5.5/maps/wall_paper_clouds.jpg', + CTWhite, + (0, 1000), + 180), + 2500: ('phase_5.5/maps/wall_paper_vine_neutral.jpg', + CTFlatColorAll, + (0, 1090), + 180), + 2600: ('phase_5.5/maps/basket.jpg', + CTWhite, + (0, 1000), + 180), + 2610: ('phase_5.5/maps/basket_neutral.jpg', + CTFlatColor, + (0, 1000), + 180), + 2700: ('phase_5.5/maps/doll.jpg', + CTWhite, + (0, 1000, 1110), + 180), + 2710: ('phase_5.5/maps/doll_neutral.jpg', + CTFlatColor, + (0, 1100, 1110), + 180), + 2800: ('phase_5.5/maps/littleFlowers.jpg', + CTWhite, + (0, 1000), + 180), + 2810: ('phase_5.5/maps/littleFlowers_neutral.jpg', + CTFlatColor, + (0, 1000), + 180), + 2900: ('phase_5.5/maps/UWwallPaperAngelFish.jpg', + CTWhite, + (0, 1120, 1160), + 180), + 2910: ('phase_5.5/maps/UWwallPaperAngelFishColor.jpg', + CTWhite, + (0, 1120, 1160), + 180), + 2920: ('phase_5.5/maps/UWwallPaperBubbles.jpg', + CTWhite, + (0, 1120, 1160), + 180), + 2930: ('phase_5.5/maps/UWwallPaperBubbles2.jpg', + CTWhite, + (0, 1120, 1160), + 180), + 2940: ('phase_5.5/maps/UWwallPaperGreenFish.jpg', + CTWhite, + (0, 1120, 1160), + 180), + 2950: ('phase_5.5/maps/UWwallPaperRedFish.jpg', + CTWhite, + (0, 1120, 1160), + 180), + 2960: ('phase_5.5/maps/UWwallPaperSea_horse.jpg', + CTWhite, + (0, 1120, 1160), + 180), + 2970: ('phase_5.5/maps/UWwallPaperShells.jpg', + CTWhite, + (0, 1140, 1150), + 180), + 2980: ('phase_5.5/maps/UWwaterFloor1.jpg', + (CT_WHITE, CT_PALE_GREEN, CT_LIGHT_BLUE), + (0,), + 180), + 3000: ('phase_5.5/maps/UWwallPaperBubbles.jpg', + CTWhite, + (0, 1120, 1160), + 180), + 3100: ('phase_5.5/maps/UWwallPaperBubbles2.jpg', + CTWhite, + (0, 1120, 1160), + 180), + 3200: ('phase_5.5/maps/UWwallPaperGreenFish.jpg', + CTWhite, + (0, 1120, 1160), + 180), + 3300: ('phase_5.5/maps/UWwallPaperRedFish.jpg', + CTWhite, + (0, 1120, 1160), + 180), + 3400: ('phase_5.5/maps/UWwallPaperSea_horse.jpg', + CTWhite, + (0, 1120, 1160), + 180), + 3500: ('phase_5.5/maps/UWwallPaperShells.jpg', + (CT_WHITE, CT_SEA_GREEN, CT_LIGHT_BLUE), + (0, 1140, 1150), + 180), + 3600: ('phase_5.5/maps/UWwaterFloor1.jpg', + (CT_WHITE, CT_PALE_GREEN, CT_LIGHT_BLUE), + (0,), + 180), + 3700: ('phase_5.5/maps/WesternBootWallpaper1.jpg', + CTWhite, + (0, 1170, 1180), + 180), + 3800: ('phase_5.5/maps/WesternCactusWallpaper1.jpg', + CTWhite, + (0, 1170, 1180), + 180), + 3900: ('phase_5.5/maps/WesternHatWallpaper1.jpg', + CTWhite, + (0, 1170, 1180), + 180), + 10100: ('phase_5.5/maps/cats1.jpg', + CTWhite, + (0, 10010, 10020), + 400), + 10200: ('phase_5.5/maps/bats2.jpg', + CTWhite, + (0, 10010, 10020), + 400), + 11000: ('phase_5.5/maps/wall_paper_snowflakes.jpg', + CTWhite, + (0, 11000, 11010), + 400), + 11100: ('phase_5.5/maps/wall_paper_hollyleaf.jpg', + CTWhite, + (0, 11000, 11010), + 400), + 11200: ('phase_5.5/maps/wall_paper_snowman.jpg', + CTWhite, + (0, 11000, 11010), + 400), + 12000: ('phase_5.5/maps/VdayWall1.jpg', + CTWhite, + (0, + 12000, + 12010, + 12020), + 400), + 12100: ('phase_5.5/maps/VdayWall2.jpg', + CTWhite, + (0, + 12000, + 12010, + 12020), + 400), + 12200: ('phase_5.5/maps/VdayWall3.jpg', + CTWhite, + (0, + 12000, + 12010, + 12020), + 400), + 12300: ('phase_5.5/maps/VdayWall4.jpg', + CTWhite, + (0, + 12000, + 12010, + 12020), + 400), + 13000: ('phase_5.5/maps/StPatWallpaper1.jpg', + CTWhite, + (0, 13000), + 400), + 13100: ('phase_5.5/maps/StPatWallpaper2.jpg', + CTWhite, + (0, 13000), + 400), + 13200: ('phase_5.5/maps/StPatWallpaper3.jpg', + CTWhite, + (0, 13000), + 400), + 13300: ('phase_5.5/maps/StPatWallpaper4.jpg', + CTWhite, + (0, 13000), + 400)} +WallpaperGroups = {1100: (1100, + 1110, + 1120, + 1130, + 1140, + 1150), + 1200: (1200, + 1210, + 1220, + 1230, + 1240, + 1250, + 1260), + 1300: (1300, + 1310, + 1320, + 1330, + 1340, + 1350), + 1400: (1400, + 1410, + 1420, + 1430, + 1440, + 1450), + 1500: (1500, 1510, 1520), + 1600: (1600, + 1610, + 1620, + 1630, + 1640, + 1650, + 1660), + 1700: (1700, + 1710, + 1720, + 1730, + 1740, + 1750, + 1760), + 1800: (1800, + 1810, + 1820, + 1830, + 1840, + 1850, + 1860), + 1900: (1900, + 1910, + 1920, + 1930, + 1940, + 1950), + 2000: (2000, 2010, 2020), + 2100: (2100, + 2110, + 2120, + 2130, + 2140), + 2200: (2200, 2210), + 2600: (2600, 2610), + 2700: (2700, 2710), + 2800: (2800, 2810), + 2900: (2900, 2910)} +BorderTypes = {1000: ('phase_5.5/maps/bd_grey_border1.jpg', CTFlatColorDark), + 1010: ('phase_5.5/maps/diamonds_border2.jpg', CTWhite), + 1020: ('phase_5.5/maps/diamonds_border2ch.jpg', CTWhite), + 1030: ('phase_5.5/maps/diamonds_border3ch.jpg', CTWhite), + 1040: ('phase_5.5/maps/diamonds_border4ch.jpg', CTWhite), + 1050: ('phase_5.5/maps/flower_border2.jpg', CTWhite), + 1060: ('phase_5.5/maps/flower_border5.jpg', CTWhite), + 1070: ('phase_5.5/maps/flower_border6.jpg', CTWhite), + 1080: ('phase_5.5/maps/football_border_neutral.jpg', CTFlatColorDark), + 1090: ('phase_5.5/maps/vine_border1.jpg', CTFlatColorDark), + 1100: ('phase_5.5/maps/doll_board.jpg', CTWhite), + 1110: ('phase_5.5/maps/doll_board_neutral.jpg', CTFlatColorDark), + 1120: ('phase_5.5/maps/UWwallPaperPlantBorder.jpg', CTWhite), + 1130: ('phase_5.5/maps/UWwallPaperSea_horseBorder.jpg', CTWhite), + 1140: ('phase_5.5/maps/UWwallPaperShellBorder1.jpg', CTWhite), + 1150: ('phase_5.5/maps/UWwallPaperShellBorder2.jpg', CTWhite), + 1160: ('phase_5.5/maps/UWwallPaperWaveBorder.jpg', CTWhite), + 1170: ('phase_5.5/maps/WesternSkullBorder.jpg', CTWhite), + 1180: ('phase_5.5/maps/WesternStarBorder.jpg', CTWhite), + 10010: ('phase_5.5/maps/border_ScarryMoon1.jpg', CTWhite), + 10020: ('phase_5.5/maps/border_candy1.jpg', CTWhite), + 11000: ('phase_5.5/maps/flakes_border.jpg', CTWhite), + 11010: ('phase_5.5/maps/hollyleaf_border.jpg', CTWhite), + 12000: ('phase_5.5/maps/Vborder1a.jpg', CTWhite), + 12010: ('phase_5.5/maps/Vborder1b.jpg', CTWhite), + 12020: ('phase_5.5/maps/Vborder2b.jpg', CTWhite), + 13000: ('phase_5.5/maps/StPatBorder1.jpg', CTWhite)} + +class CatalogWallpaperItem(CatalogSurfaceItem): + + def makeNewItem(self, patternIndex, colorIndex = None, borderIndex = 0, borderColorIndex = 0): + self.patternIndex = patternIndex + self.colorIndex = colorIndex + self.borderIndex = borderIndex + self.borderColorIndex = borderColorIndex + CatalogSurfaceItem.makeNewItem(self) + + def needsCustomize(self): + return self.colorIndex == None or self.borderIndex == None + + def getTypeName(self): + return TTLocalizer.SurfaceNames[STWallpaper] + + def getName(self): + name = TTLocalizer.WallpaperNames.get(self.patternIndex) + if name == None: + century = self.patternIndex - self.patternIndex % 100 + name = TTLocalizer.WallpaperNames.get(century) + if name: + return name + return self.getTypeName() + + def getSurfaceType(self): + return STWallpaper + + def getPicture(self, avatar): + frame = self.makeFrame() + sample = loader.loadModel('phase_5.5/models/estate/wallpaper_sample') + a = sample.find('**/a') + b = sample.find('**/b') + c = sample.find('**/c') + a.setTexture(self.loadTexture(), 1) + a.setColorScale(*self.getColor()) + b.setTexture(self.loadTexture(), 1) + b.setColorScale(*self.getColor()) + c.setTexture(self.loadBorderTexture(), 1) + c.setColorScale(*self.getBorderColor()) + sample.reparentTo(frame) + self.hasPicture = True + return (frame, None) + + def output(self, store = -1): + return 'CatalogWallpaperItem(%s, %s, %s, %s%s)' % (self.patternIndex, + self.colorIndex, + self.borderIndex, + self.borderColorIndex, + self.formatOptionalData(store)) + + def getFilename(self): + return WallpaperTypes[self.patternIndex][WTTextureName] + + def compareTo(self, other): + if self.patternIndex != other.patternIndex: + century = self.patternIndex - self.patternIndex % 100 + otherCentury = other.patternIndex - other.patternIndex % 100 + return century - otherCentury + return 0 + + def getHashContents(self): + century = self.patternIndex - self.patternIndex % 100 + return century + + def getBasePrice(self): + return WallpaperTypes[self.patternIndex][WTBasePrice] + + def loadTexture(self): + from pandac.PandaModules import Texture + filename = WallpaperTypes[self.patternIndex][WTTextureName] + texture = loader.loadTexture(filename) + texture.setMinfilter(Texture.FTLinearMipmapLinear) + texture.setMagfilter(Texture.FTLinear) + return texture + + def getColor(self): + if self.colorIndex == None: + colorIndex = 0 + else: + colorIndex = self.colorIndex + colors = WallpaperTypes[self.patternIndex][WTColor] + if colorIndex < len(colors): + return colors[colorIndex] + else: + print 'Warning: colorIndex > len(colors). Returning white.' + return CT_WHITE + return + + def loadBorderTexture(self): + from pandac.PandaModules import Texture + if self.borderIndex == None or self.borderIndex == 0: + return self.loadTexture() + borderInfo = BorderTypes[self.borderIndex] + filename = borderInfo[BDTextureName] + texture = loader.loadTexture(filename) + texture.setMinfilter(Texture.FTLinearMipmapLinear) + texture.setMagfilter(Texture.FTLinear) + return texture + + def getBorderColor(self): + if self.borderIndex == None or self.borderIndex == 0: + return self.getColor() + else: + colors = BorderTypes[self.borderIndex][BDColor] + if self.borderColorIndex < len(colors): + return colors[self.borderColorIndex] + else: + return CT_WHITE + return + + def decodeDatagram(self, di, versionNumber, store): + CatalogAtticItem.CatalogAtticItem.decodeDatagram(self, di, versionNumber, store) + self.colorIndex = 0 + self.borderIndex = 0 + self.borderColorIndex = 0 + self.patternIndex = di.getUint16() + if store & CatalogItem.Customization: + self.colorIndex = di.getUint8() + self.borderIndex = di.getUint16() + self.borderColorIndex = di.getUint8() + wtype = WallpaperTypes[self.patternIndex] + return + + def encodeDatagram(self, dg, store): + CatalogAtticItem.CatalogAtticItem.encodeDatagram(self, dg, store) + dg.addUint16(self.patternIndex) + if store & CatalogItem.Customization: + dg.addUint8(self.colorIndex) + dg.addUint16(self.borderIndex) + dg.addUint8(self.borderColorIndex) + + +def getWallpapers(*typeList): + list = [] + for type in typeList: + list.append(CatalogWallpaperItem(type)) + + return list + + +def getAllWallpapers(*typeList): + list = [] + for type in typeList: + group = WallpaperGroups.get(type, [type]) + for index in group: + borderKeys = WallpaperTypes[index][WTBorderList] + for borderKey in borderKeys: + borderData = BorderTypes.get(borderKey) + if borderData: + numBorderColors = len(borderData[BDColor]) + else: + numBorderColors = 1 + for borderColorIndex in xrange(numBorderColors): + colors = WallpaperTypes[index][WTColor] + for n in xrange(len(colors)): + list.append(CatalogWallpaperItem(index, n, borderKey, borderColorIndex)) + + return list + + +def getWallpaperRange(fromIndex, toIndex, *otherRanges): + list = [] + froms = [fromIndex] + tos = [toIndex] + i = 0 + while i < len(otherRanges): + froms.append(otherRanges[i]) + tos.append(otherRanges[i + 1]) + i += 2 + + for patternIndex in WallpaperTypes.keys(): + for fromIndex, toIndex in zip(froms, tos): + if patternIndex >= fromIndex and patternIndex <= toIndex: + borderKeys = WallpaperTypes[patternIndex][WTBorderList] + for borderKey in borderKeys: + borderData = BorderTypes.get(borderKey) + if borderData: + numBorderColors = len(borderData[BDColor]) + else: + numBorderColors = 1 + for borderColorIndex in xrange(numBorderColors): + colors = WallpaperTypes[patternIndex][WTColor] + for n in xrange(len(colors)): + list.append(CatalogWallpaperItem(patternIndex, n, borderKey, borderColorIndex)) + + return list diff --git a/toontown/catalog/CatalogWindowItem.py b/toontown/catalog/CatalogWindowItem.py new file mode 100755 index 00000000..e9d0308d --- /dev/null +++ b/toontown/catalog/CatalogWindowItem.py @@ -0,0 +1,119 @@ +from panda3d.core import * +import CatalogAtticItem +import CatalogItem +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +WVTModelName = 0 +WVTBasePrice = 1 +WVTSkyName = 2 +WindowViewTypes = {10: ('phase_5.5/models/estate/Garden1', 900, None), + 20: ('phase_5.5/models/estate/GardenA', 900, None), + 30: ('phase_5.5/models/estate/GardenB', 900, None), + 40: ('phase_5.5/models/estate/cityView', 900, None), + 50: ('phase_5.5/models/estate/westernView', 900, None), + 60: ('phase_5.5/models/estate/underwaterView', 900, None), + 70: ('phase_5.5/models/estate/tropicView', 900, None), + 80: ('phase_5.5/models/estate/spaceView', 900, None), + 90: ('phase_5.5/models/estate/PoolView', 900, None), + 100: ('phase_5.5/models/estate/SnowView', 900, None), + 110: ('phase_5.5/models/estate/FarmView', 900, None), + 120: ('phase_5.5/models/estate/IndianView', 900, None), + 130: ('phase_5.5/models/estate/WesternMainStreetView', 900, None)} + +class CatalogWindowItem(CatalogAtticItem.CatalogAtticItem): + + def makeNewItem(self, windowType, placement = None): + self.windowType = windowType + self.placement = placement + CatalogAtticItem.CatalogAtticItem.makeNewItem(self) + + def saveHistory(self): + return 1 + + def getTypeName(self): + return TTLocalizer.WindowViewTypeName + + def getName(self): + return TTLocalizer.WindowViewNames.get(self.windowType) + + def recordPurchase(self, avatar, optional): + house, retcode = self.getHouseInfo(avatar) + if retcode >= 0: + house.addWindow(self) + return retcode + + def getDeliveryTime(self): + return 4 * 60 + + def getPicture(self, avatar): + frame = self.makeFrame() + model = self.loadModel() + model.setDepthTest(1) + model.setDepthWrite(1) + clipperLeft = PlaneNode('clipper') + clipperRight = PlaneNode('clipper') + clipperTop = PlaneNode('clipper') + clipperBottom = PlaneNode('clipper') + clipperLeft.setPlane(Plane(Vec3(1, 0, 0), Point3(-1, 0, 0))) + clipperRight.setPlane(Plane(Vec3(-1, 0, 0), Point3(1, 0, 0))) + clipperTop.setPlane(Plane(Vec3(0, 0, -1), Point3(0, 0, 1))) + clipperBottom.setPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, -1))) + model.setClipPlane(frame.attachNewNode(clipperLeft)) + model.setClipPlane(frame.attachNewNode(clipperRight)) + model.setClipPlane(frame.attachNewNode(clipperTop)) + model.setClipPlane(frame.attachNewNode(clipperBottom)) + bgName = WindowViewTypes[self.windowType][WVTSkyName] + if bgName: + bgNodePath = model.find('**/' + bgName) + if not bgNodePath.isEmpty(): + bgNodePath.reparentTo(model, -1) + windowFrame = model.find('**/frame') + if not windowFrame.isEmpty(): + windowFrame.removeNode() + model.setPos(0, 2, 0) + model.setScale(0.4) + model.reparentTo(frame) + self.hasPicture = True + return (frame, None) + + def output(self, store = -1): + return 'CatalogWindowItem(%s%s)' % (self.windowType, self.formatOptionalData(store)) + + def getFilename(self): + type = WindowViewTypes[self.windowType] + return type[WVTModelName] + + def formatOptionalData(self, store = -1): + result = CatalogAtticItem.CatalogAtticItem.formatOptionalData(self, store) + if store & CatalogItem.WindowPlacement and self.placement != None: + result += ', placement = %s' % self.placement + return result + + def compareTo(self, other): + return self.windowType - other.windowType + + def getHashContents(self): + return self.windowType + + def getBasePrice(self): + return WindowViewTypes[self.windowType][WVTBasePrice] + + def loadModel(self): + type = WindowViewTypes[self.windowType] + model = loader.loadModel(type[WVTModelName]) + return model + + def decodeDatagram(self, di, versionNumber, store): + CatalogAtticItem.CatalogAtticItem.decodeDatagram(self, di, versionNumber, store) + self.placement = None + if store & CatalogItem.WindowPlacement: + self.placement = di.getUint8() + self.windowType = di.getUint8() + wvtype = WindowViewTypes[self.windowType] + return + + def encodeDatagram(self, dg, store): + CatalogAtticItem.CatalogAtticItem.encodeDatagram(self, dg, store) + if store & CatalogItem.WindowPlacement: + dg.addUint8(self.placement) + dg.addUint8(self.windowType) diff --git a/toontown/catalog/GiftAvatar.py b/toontown/catalog/GiftAvatar.py new file mode 100644 index 00000000..9905726f --- /dev/null +++ b/toontown/catalog/GiftAvatar.py @@ -0,0 +1,98 @@ +from toontown.toon import ToonDNA +import CatalogItem, CatalogItemList +import json, time + +def createFromJson(jsonData): + return createFromFields(json.loads(jsonData)) + +def createFromFields(fields): + avatar = GiftAvatar() + + for key, value in fields.iteritems(): + getattr(avatar, key)(value) + + return avatar + +class GiftAvatar: + + def getStyle(self): + return self.style + + def getHat(self): + return self.hat + + def getGlasses(self): + return self.glasses + + def getBackpack(self): + return self.backpack + + def getShoes(self): + return self.shoes + + def getGiftScheduleBlob(self): + return self.onGiftOrder.getBlob(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + + def setDNAString(self, dnaString): + self.style = ToonDNA.ToonDNA() + self.style.makeFromNetString(dnaString.decode('base64')) + + def setMailboxContents(self, contents): + self.mailboxContents = CatalogItemList.CatalogItemList(contents.decode('base64'), store=CatalogItem.Customization) + + def setGiftSchedule(self, onOrder): + self.onGiftOrder = CatalogItemList.CatalogItemList(onOrder.decode('base64'), store=CatalogItem.Customization | CatalogItem.DeliveryDate) + + def setDeliverySchedule(self, onOrder): + self.onOrder = CatalogItemList.CatalogItemList(onOrder.decode('base64'), store=CatalogItem.Customization | CatalogItem.DeliveryDate) + + def setHat(self, hat): + self.hat = hat + + def setGlasses(self, glasses): + self.glasses = glasses + + def setBackpack(self, backpack): + self.backpack = backpack + + def setShoes(self, shoes): + self.shoes = shoes + + def setHatList(self, list): + self.hatList = list[0] + + def setGlassesList(self, list): + self.glassesList = list[0] + + def setBackpackList(self, list): + self.backpackList = list[0] + + def setShoesList(self, list): + self.shoesList = list[0] + + def setCustomMessages(self, customMessages): + self.customMessages = customMessages[0] + + def setClothesTopsList(self, clothesList): + self.clothesTopsList = clothesList[0] + + def setClothesBottomsList(self, clothesList): + self.clothesBottomsList = clothesList[0] + + def setEmoteAccess(self, emoteAccess): + self.emoteAccess = emoteAccess[0] + + def setPetTrickPhrases(self, tricks): + self.petTrickPhrases = tricks[0] + + def setNametagStyles(self, nametagStyles): + self.nametagStyles = nametagStyles[0] + + def addToGiftSchedule(self, avId, targetId, item, minutes=0): + if config.GetBool('want-instant-delivery', False): + minutes = 0 + + item.giftTag = avId + item.deliveryDate = int(time.time() / 60. + minutes + .5) + self.onGiftOrder.append(item) + simbase.air.send(simbase.air.dclassesByName['DistributedToonAI'].aiFormatUpdate('setGiftSchedule', targetId, targetId, simbase.air.ourChannel, [self.getGiftScheduleBlob()])) \ No newline at end of file diff --git a/toontown/catalog/MailboxScreen.py b/toontown/catalog/MailboxScreen.py new file mode 100755 index 00000000..d9ae600d --- /dev/null +++ b/toontown/catalog/MailboxScreen.py @@ -0,0 +1,519 @@ +from direct.directnotify.DirectNotifyGlobal import * +from direct.gui.DirectGui import * +from direct.showbase import DirectObject, PythonUtil +from panda3d.core import * +from toontown.parties import PartyGlobals +from toontown.parties.InviteInfo import InviteInfoBase +from toontown.parties.PartyGlobals import InviteStatus +from toontown.parties.SimpleMailBase import SimpleMailBase +from toontown.toonbase import TTLocalizer, ToontownGlobals +from toontown.toontowngui import TTDialog +from toontown.parties.InviteVisual import InviteVisual +import CatalogItem +from direct.showbase.PythonUtil import StackTrace + +class MailboxScreen(DirectObject.DirectObject): + notify = directNotify.newCategory('MailboxScreen') + + def __init__(self, mailbox, avatar, doneEvent = None): + self.mailbox = mailbox + self.avatar = avatar + self.items = self.getItems() + self.doneEvent = doneEvent + self.itemIndex = 0 + self.itemPanel = None + self.itemPicture = None + self.ival = None + self.itemText = None + self.giftTag = None + self.acceptingIndex = None + self.numAtticAccepted = 0 + self.dialogBox = None + self.load() + self.hide() + + def show(self): + self.frame.show() + self.__showCurrentItem() + + def hide(self): + self.ignore('friendsListChanged') + if hasattr(self, 'frame'): + self.frame.hide() + else: + self.notify.warning('hide called, but frame is deleted, self.frame deleted in:') + if hasattr(self, 'frameDelStackTrace'): + print self.frameDelStackTrace + self.notify.warning('current stackTrace =') + print StackTrace() + self.notify.warning('crash averted, but root cause unknown') + + def load(self): + self.accept('setMailboxContents-%s' % base.localAvatar.doId, self.__refreshItems) + self.accept('setAwardMailboxContents-%s' % base.localAvatar.doId, self.__refreshItems) + model = loader.loadModel('phase_5.5/models/gui/package_delivery_panel') + background = model.find('**/bg') + itemBoard = model.find('**/item_board') + self.frame = DirectFrame(scale=1.1, relief=DGG.FLAT, frameSize=(-0.5, + 0.5, + -0.45, + -0.05), frameColor=(0.737, 0.573, 0.345, 1.0)) + self.background = DirectFrame(self.frame, image=background, image_scale=0.05, relief=None, pos=(0, 1, 0)) + self.itemBoard = DirectFrame(parent=self.frame, image=itemBoard, image_scale=0.05, image_color=(0.922, 0.922, 0.753, 1), relief=None, pos=(0, 1, 0)) + self.itemCountLabel = DirectLabel(parent=self.frame, relief=None, text=self.__getNumberOfItemsText(), text_wordwrap=16, pos=(0.0, 0.0, 0.7), scale=0.09) + exitUp = model.find('**/bu_return_rollover') + exitDown = model.find('**/bu_return_rollover') + exitRollover = model.find('**/bu_return_rollover') + exitUp.setP(-90) + exitDown.setP(-90) + exitRollover.setP(-90) + self.DiscardButton = DirectButton(parent=self.frame, relief=None, image=(exitUp, + exitDown, + exitRollover, + exitUp), pos=(-0.01, 1.0, -0.36), scale=0.048, text=('', + TTLocalizer.MailBoxDiscard, + TTLocalizer.MailBoxDiscard, + ''), text_scale=1.0, text_pos=(0, -0.08), textMayChange=1, command=self.__makeDiscardInterface) + gui2 = loader.loadModel('phase_3/models/gui/quit_button') + self.quitButton = DirectButton(parent=self.frame, relief=None, image=(gui2.find('**/QuitBtn_UP'), gui2.find('**/QuitBtn_DN'), gui2.find('**/QuitBtn_RLVR')), pos=(0.5, 1.0, -0.42), scale=0.9, text=TTLocalizer.MailboxExitButton, text_font=ToontownGlobals.getSignFont(), text0_fg=(0.152, 0.75, 0.258, 1), text1_fg=(0.152, 0.75, 0.258, 1), text2_fg=(0.977, 0.816, 0.133, 1), text_scale=0.045, text_pos=(0, -0.01), command=self.__handleExit) + self.gettingText = DirectLabel(parent=self.frame, relief=None, text='', text_wordwrap=10, pos=(0.0, 0.0, 0.32), scale=0.09) + self.gettingText.hide() + self.giftTagPanel = DirectLabel(parent=self.frame, relief=None, text=TTLocalizer.MailboxGiftTag % TTLocalizer.MailboxGiftTagAnonymous, text_wordwrap=16, pos=(0.0, 0.0, 0.01), scale=0.06) + self.giftTagPanel.hide() + self.itemText = DirectLabel(parent=self.frame, relief=None, text='', text_wordwrap=16, pos=(0.0, 0.0, -0.022), scale=0.07) + self.itemText.hide() + acceptUp = model.find('**/bu_check_up') + acceptDown = model.find('**/bu_check_down') + acceptRollover = model.find('**/bu_check_rollover') + acceptUp.setP(-90) + acceptDown.setP(-90) + acceptRollover.setP(-90) + self.acceptButton = DirectButton(parent=self.frame, relief=None, image=(acceptUp, + acceptDown, + acceptRollover, + acceptUp), image3_color=(0.8, 0.8, 0.8, 0.6), pos=(-0.01, 1.0, -0.16), scale=0.048, text=('', + TTLocalizer.MailboxAcceptButton, + TTLocalizer.MailboxAcceptButton, + ''), text_scale=1.0, text_pos=(0, -0.09), textMayChange=1, command=self.__handleAccept, state=DGG.DISABLED) + nextUp = model.find('**/bu_next_up') + nextUp.setP(-90) + nextDown = model.find('**/bu_next_down') + nextDown.setP(-90) + nextRollover = model.find('**/bu_next_rollover') + nextRollover.setP(-90) + self.nextButton = DirectButton(parent=self.frame, relief=None, image=(nextUp, + nextDown, + nextRollover, + nextUp), image3_color=(0.8, 0.8, 0.8, 0.6), pos=(0.31, 1.0, -0.26), scale=0.05, text=('', + TTLocalizer.MailboxItemNext, + TTLocalizer.MailboxItemNext, + ''), text_scale=1, text_pos=(-0.2, 0.3), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), textMayChange=0, command=self.__nextItem, state=DGG.DISABLED) + prevUp = model.find('**/bu_previous_up') + prevUp.setP(-90) + prevDown = model.find('**/bu_previous_down') + prevDown.setP(-90) + prevRollover = model.find('**/bu_previous_rollover') + prevRollover.setP(-90) + self.prevButton = DirectButton(parent=self.frame, relief=None, image=(prevUp, + prevDown, + prevRollover, + prevUp), pos=(-0.35, 1, -0.26), scale=0.05, image3_color=(0.8, 0.8, 0.8, 0.6), text=('', + TTLocalizer.MailboxItemPrev, + TTLocalizer.MailboxItemPrev, + ''), text_scale=1, text_pos=(0, 0.3), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), textMayChange=0, command=self.__prevItem, state=DGG.DISABLED) + self.currentItem = None + self.partyInviteVisual = InviteVisual(self.frame) + self.partyInviteVisual.setScale(0.73) + self.partyInviteVisual.setPos(0.0, 0.0, 0.48) + self.partyInviteVisual.stash() + if self.avatar: + self.avatar.applyCheesyEffect(ToontownGlobals.CENormal) + return + + def unload(self): + if self.avatar: + self.avatar.reconsiderCheesyEffect() + self.__clearCurrentItem() + if hasattr(self, 'frame'): + self.frame.destroy() + del self.frame + self.frameDelStackTrace = StackTrace() + else: + self.notify.warning('unload, no self.frame') + if hasattr(self, 'mailbox'): + del self.mailbox + else: + self.notify.warning('unload, no self.mailbox') + if self.dialogBox: + self.dialogBox.cleanup() + self.dialogBox = None + for item in self.items: + if isinstance(item, CatalogItem.CatalogItem): + item.acceptItemCleanup() + + self.ignoreAll() + return + + def justExit(self): + self.__acceptExit() + + def __handleExit(self): + if self.numAtticAccepted == 0: + self.__acceptExit() + elif self.numAtticAccepted == 1: + self.dialogBox = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.CatalogAcceptInAttic, text_wordwrap=15, command=self.__acceptExit) + self.dialogBox.show() + else: + self.dialogBox = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.CatalogAcceptInAtticP, text_wordwrap=15, command=self.__acceptExit) + self.dialogBox.show() + + def __acceptExit(self, buttonValue = None): + if hasattr(self, 'frame'): + self.hide() + self.unload() + messenger.send(self.doneEvent) + + def __handleAccept(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: MAILBOX: Accept item') + if self.acceptingIndex != None: + return + item = self.items[self.itemIndex] + isAward = False + if isinstance(item, CatalogItem.CatalogItem): + isAward = item.isAward() + self.acceptingIndex = self.itemIndex + self.acceptButton['state'] = DGG.DISABLED + self.__showCurrentItem() + item = self.items[self.itemIndex] + item.acceptItem(self.mailbox, self.acceptingIndex, self.__acceptItemCallback) + return + + def __handleDiscard(self, buttonValue = None): + if self.acceptingIndex != None: + return + elif buttonValue == -1: + if self.dialogBox: + self.dialogBox.cleanup() + self.dialogBox = None + self.__showCurrentItem() + else: + self.acceptingIndex = self.itemIndex + self.acceptButton['state'] = DGG.DISABLED + self.__showCurrentItem() + item = self.items[self.itemIndex] + item.discardItem(self.mailbox, self.acceptingIndex, self.__discardItemCallback) + return + + def __discardItemCallback(self, retcode, item, index): + if not hasattr(self, 'frame'): + return + if self.dialogBox: + self.dialogBox.cleanup() + self.dialogBox = None + self.acceptingIndex = None + self.__updateItems() + if isinstance(item, InviteInfoBase): + callback = self.__incIndexRemoveDialog + self.dialogBox = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=item.getDiscardItemErrorText(retcode), text_wordwrap=15, command=callback) + self.dialogBox.show() + return + + def __makeDiscardInterface(self): + if self.itemIndex >= 0 and self.itemIndex < len(self.items): + item = self.items[self.itemIndex] + if isinstance(item, InviteInfoBase): + itemText = TTLocalizer.MailBoxRejectVerify % self.getItemName(item) + yesText = TTLocalizer.MailboxReject + else: + itemText = TTLocalizer.MailBoxDiscardVerify % self.getItemName(item) + yesText = TTLocalizer.MailboxDiscard + self.dialogBox = TTDialog.TTDialog(style=TTDialog.TwoChoiceCustom, text=itemText, text_wordwrap=15, command=self.__handleDiscard, buttonText=[yesText, TTLocalizer.MailboxLeave]) + self.dialogBox.show() + + def __acceptItemCallback(self, retcode, item, index): + needtoUpdate = 0 + if self.acceptingIndex == None: + needtoUpdate = 1 + if not hasattr(self, 'frame'): + return + if retcode == ToontownGlobals.P_UserCancelled: + print 'mailbox screen user canceled' + self.acceptingIndex = None + self.__updateItems() + return + if self.acceptingIndex != index: + self.notify.warning('Got unexpected callback for index %s, expected %s.' % (index, self.acceptingIndex)) + return + self.acceptingIndex = None + if retcode < 0: + self.notify.info('Could not take item %s: retcode %s' % (item, retcode)) + self.dialogBox = TTDialog.TTDialog(style=TTDialog.TwoChoiceCustom, text=item.getAcceptItemErrorText(retcode), text_wordwrap=15, command=self.__handleDiscard, buttonText=[TTLocalizer.MailboxDiscard, TTLocalizer.MailboxLeave]) + self.dialogBox.show() + elif hasattr(item, 'storedInAttic') and item.storedInAttic(): + self.numAtticAccepted += 1 + self.itemIndex += 1 + if needtoUpdate == 1: + self.__updateItems() + else: + if isinstance(item, InviteInfoBase): + self.__updateItems() + callback = self.__incIndexRemoveDialog + self.dialogBox = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=item.getAcceptItemErrorText(retcode), text_wordwrap=15, command=callback) + self.dialogBox.show() + return + + def __acceptError(self, buttonValue = None): + self.dialogBox.cleanup() + self.dialogBox = None + self.__showCurrentItem() + return + + def __incIndexRemoveDialog(self, junk = 0): + self.__incIndex() + self.dialogBox.cleanup() + self.dialogBox = None + self.__showCurrentItem() + return + + def __incIndex(self, junk = 0): + self.itemIndex += 1 + + def __acceptOk(self, index, buttonValue = None): + self.acceptingIndex = None + if self.dialogBox: + self.dialogBox.cleanup() + self.dialogBox = None + self.items = self.getItems() + if self.itemIndex > index or self.itemIndex >= len(self.items): + print 'adjusting item index -1' + self.itemIndex -= 1 + if len(self.items) < 1: + self.__handleExit() + return + self.itemCountLabel['text'] = (self.__getNumberOfItemsText(),) + self.__showCurrentItem() + return + + def __refreshItems(self): + self.acceptingIndex = None + self.__updateItems() + return + + def __updateItems(self): + if self.dialogBox: + self.dialogBox.cleanup() + self.dialogBox = None + self.items = self.getItems() + if self.itemIndex >= len(self.items): + print 'adjusting item index -1' + self.itemIndex = len(self.items) - 1 + if len(self.items) == 0: + print 'exiting due to lack of items' + self.__handleExit() + return + self.itemCountLabel['text'] = (self.__getNumberOfItemsText(),) + self.__showCurrentItem() + return + + def __getNumberOfItemsText(self): + if len(self.items) == 1: + return TTLocalizer.MailboxOneItem + else: + return TTLocalizer.MailboxNumberOfItems % len(self.items) + + def __clearCurrentItem(self): + if self.itemPanel: + self.itemPanel.destroy() + self.itemPanel = None + if self.ival: + self.ival.finish() + self.ival = None + if not self.gettingText.isEmpty(): + self.gettingText.hide() + if not self.itemText.isEmpty(): + self.itemText.hide() + if not self.giftTagPanel.isEmpty(): + self.giftTagPanel.hide() + if not self.acceptButton.isEmpty(): + self.acceptButton['state'] = DGG.DISABLED + if self.currentItem: + if isinstance(self.currentItem, CatalogItem.CatalogItem): + self.currentItem.cleanupPicture() + self.currentItem = None + return + + def __showCurrentItem(self): + self.__clearCurrentItem() + if len(self.items) < 1: + self.__handleExit() + return + self.partyInviteVisual.stash() + if self.itemIndex + 1 > len(self.items): + self.itemIndex = len(self.items) - 1 + item = self.items[self.itemIndex] + if self.itemIndex == self.acceptingIndex: + self.gettingText['text'] = TTLocalizer.MailboxGettingItem % self.getItemName(item) + self.gettingText.show() + return + self.itemText['text'] = self.getItemName(item) + self.currentItem = item + if isinstance(item, CatalogItem.CatalogItem): + self.acceptButton['text'] = ('', + TTLocalizer.MailboxAcceptButton, + TTLocalizer.MailboxAcceptButton, + '') + self.DiscardButton['text'] = ('', + TTLocalizer.MailBoxDiscard, + TTLocalizer.MailBoxDiscard, + '') + if item.isAward(): + self.giftTagPanel['text'] = TTLocalizer.SpecialEventMailboxStrings[item.specialEventId] + elif item.giftTag != None: + nameOfSender = self.getSenderName(item.giftTag) + if item.giftCode == ToontownGlobals.GIFT_RAT: + self.giftTagPanel['text'] = TTLocalizer.CatalogAcceptRATBeans + elif item.giftCode == ToontownGlobals.GIFT_partyrefund: + self.giftTagPanel['text'] = TTLocalizer.CatalogAcceptPartyRefund + else: + self.giftTagPanel['text'] = TTLocalizer.MailboxGiftTag % nameOfSender + else: + self.giftTagPanel['text'] = '' + self.itemPanel, self.ival = item.getPicture(base.localAvatar) + elif isinstance(item, SimpleMailBase): + self.acceptButton['text'] = ('', + TTLocalizer.MailboxAcceptButton, + TTLocalizer.MailboxAcceptButton, + '') + self.DiscardButton['text'] = ('', + TTLocalizer.MailBoxDiscard, + TTLocalizer.MailBoxDiscard, + '') + senderId = item.senderId + nameOfSender = self.getSenderName(senderId) + self.giftTagPanel['text'] = TTLocalizer.MailFromTag % nameOfSender + self.itemText['text'] = item.body + elif isinstance(item, InviteInfoBase): + self.acceptButton['text'] = ('', + TTLocalizer.MailboxAcceptInvite, + TTLocalizer.MailboxAcceptInvite, + '') + self.DiscardButton['text'] = ('', + TTLocalizer.MailBoxRejectInvite, + TTLocalizer.MailBoxRejectInvite, + '') + partyInfo = None + for party in self.avatar.partiesInvitedTo: + if party.partyId == item.partyId: + partyInfo = party + break + else: + MailboxScreen.notify.error('Unable to find party with id %d to match invitation %s' % (item.partyId, item)) + + if self.mailbox: + if item.status == PartyGlobals.InviteStatus.NotRead: + self.mailbox.sendInviteReadButNotReplied(item.inviteKey) + senderId = partyInfo.hostId + nameOfSender = self.getSenderName(senderId) + self.giftTagPanel['text'] = '' + self.itemText['text'] = '' + self.partyInviteVisual.updateInvitation(nameOfSender, partyInfo) + self.partyInviteVisual.unstash() + self.itemPanel = None + self.ival = None + else: + self.acceptButton['text'] = ('', + TTLocalizer.MailboxAcceptButton, + TTLocalizer.MailboxAcceptButton, + '') + self.DiscardButton['text'] = ('', + TTLocalizer.MailBoxDiscard, + TTLocalizer.MailBoxDiscard, + '') + self.giftTagPanel['text'] = ' ' + self.itemPanel = None + self.ival = None + self.itemText.show() + self.giftTagPanel.show() + if self.itemPanel and item.getTypeName() != TTLocalizer.ChatTypeName: + self.itemPanel.reparentTo(self.itemBoard, -1) + self.itemPanel.setPos(0, 0, 0.4) + self.itemPanel.setScale(0.21) + self.itemText['text_wordwrap'] = 16 + self.itemText.setPos(0.0, 0.0, 0.075) + elif isinstance(item, CatalogItem.CatalogItem) and item.getTypeName() == TTLocalizer.ChatTypeName: + self.itemPanel.reparentTo(self.itemBoard, -1) + self.itemPanel.setPos(0, 0, 0.35) + self.itemPanel.setScale(0.21) + self.itemText['text_wordwrap'] = 10 + self.itemText.setPos(0, 0, 0.3) + else: + self.itemText.setPos(0, 0, 0.3) + if self.ival: + self.ival.loop() + if self.acceptingIndex == None: + self.acceptButton['state'] = DGG.NORMAL + if self.itemIndex > 0: + self.prevButton['state'] = DGG.NORMAL + else: + self.prevButton['state'] = DGG.DISABLED + if self.itemIndex + 1 < len(self.items): + self.nextButton['state'] = DGG.NORMAL + else: + self.nextButton['state'] = DGG.DISABLED + return + + def __nextItem(self): + messenger.send('wakeup') + if self.itemIndex + 1 < len(self.items): + self.itemIndex += 1 + self.__showCurrentItem() + + def __prevItem(self): + messenger.send('wakeup') + if self.itemIndex > 0: + self.itemIndex -= 1 + self.__showCurrentItem() + + def getItemName(self, item): + if isinstance(item, CatalogItem.CatalogItem): + return item.getName() + elif isinstance(item, str): + return TTLocalizer.MailSimpleMail + elif isinstance(item, InviteInfoBase): + return TTLocalizer.InviteInvitation + else: + return '' + + def getItems(self): + result = [] + result = self.avatar.awardMailboxContents[:] + result += self.avatar.mailboxContents[:] + if self.avatar.mail: + result += self.avatar.mail + mailboxInvites = self.avatar.getInvitesToShowInMailbox() + if mailboxInvites: + result += mailboxInvites + return result + + def getNumberOfAwardItems(self): + result = 0 + for item in self.items: + if isinstance(item, CatalogItem.CatalogItem) and item.specialEventId > 0: + result += 1 + else: + break + + return result + + def getSenderName(self, avId): + sender = base.cr.identifyFriend(avId) + + if sender: + return sender.getName() + + for av in base.cr.avList: + if av.id == avId: + return av.name + + return TTLocalizer.MailboxGiftTagAnonymous diff --git a/toontown/catalog/__init__.py b/toontown/catalog/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/chat/ResistanceChat.py b/toontown/chat/ResistanceChat.py new file mode 100755 index 00000000..99bb6800 --- /dev/null +++ b/toontown/chat/ResistanceChat.py @@ -0,0 +1,248 @@ +from direct.interval.IntervalGlobal import * +from panda3d.core import * +import random, copy + +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownBattleGlobals +from toontown.suit import SuitDNA + + +if process == 'client': + from toontown.battle import BattleParticles + + +try: + config = base.config +except: + config = simbase.config + +EFFECT_RADIUS = 30 +RESISTANCE_TOONUP = 0 +RESISTANCE_RESTOCK = 1 +RESISTANCE_MONEY = 2 +RESISTANCE_TICKETS = 3 +RESISTANCE_MERITS = 4 +resistanceMenu = [RESISTANCE_TOONUP, RESISTANCE_RESTOCK, RESISTANCE_MONEY, RESISTANCE_TICKETS, RESISTANCE_MERITS] +randomResistanceMenu = [RESISTANCE_TOONUP, RESISTANCE_RESTOCK, RESISTANCE_MONEY, RESISTANCE_TICKETS] +resistanceDict = { + RESISTANCE_TOONUP: { + 'menuName': TTLocalizer.ResistanceToonupMenu, + 'itemText': TTLocalizer.ResistanceToonupItem, + 'chatText': TTLocalizer.ResistanceToonupChat, + 'values': [10, 20, 40, 80, -1], + 'items': [0, 1, 2, 3, 4] + }, + RESISTANCE_MONEY: { + 'menuName': TTLocalizer.ResistanceMoneyMenu, + 'itemText': TTLocalizer.ResistanceMoneyItem, + 'chatText': TTLocalizer.ResistanceMoneyChat, + 'values': [100, 200, 350, 600, 1200, 2400], + 'items': [0, 1, 2, 3, 4, 5] + }, + RESISTANCE_RESTOCK: { + 'menuName': TTLocalizer.ResistanceRestockMenu, + 'itemText': TTLocalizer.ResistanceRestockItem, + 'chatText': TTLocalizer.ResistanceRestockChat, + 'values': [ + ToontownBattleGlobals.HEAL_TRACK, + ToontownBattleGlobals.TRAP_TRACK, + ToontownBattleGlobals.LURE_TRACK, + ToontownBattleGlobals.SOUND_TRACK, + ToontownBattleGlobals.THROW_TRACK, + ToontownBattleGlobals.SQUIRT_TRACK, + ToontownBattleGlobals.DROP_TRACK, + -1 + ], + 'extra': [ + TTLocalizer.MovieNPCSOSHeal, + TTLocalizer.MovieNPCSOSTrap, + TTLocalizer.MovieNPCSOSLure, + TTLocalizer.MovieNPCSOSSound, + TTLocalizer.MovieNPCSOSThrow, + TTLocalizer.MovieNPCSOSSquirt, + TTLocalizer.MovieNPCSOSDrop, + TTLocalizer.MovieNPCSOSAll + ], + 'items': [0, 1, 2, 3, 4, 5, 6, 7] + }, + RESISTANCE_MERITS: { + 'menuName': TTLocalizer.ResistanceMeritsMenu, + 'itemText': TTLocalizer.ResistanceMeritsItem, + 'chatText': TTLocalizer.ResistanceMeritsChat, + 'values': range(len(SuitDNA.suitDepts)) + [-1], + 'extra': TTLocalizer.RewardPanelMeritBarLabels + [TTLocalizer.MovieNPCSOSAll], + 'items': range(len(SuitDNA.suitDepts) + 1) + }, + RESISTANCE_TICKETS: { + 'menuName': TTLocalizer.ResistanceTicketsMenu, + 'itemText': TTLocalizer.ResistanceTicketsItem, + 'chatText': TTLocalizer.ResistanceTicketsChat, + 'values': [200, 400, 600, 800, 1200], + 'items': [0, 1, 2, 3, 4] + } +} + + +def encodeId(menuIndex, itemIndex): + textId = menuIndex * 100 + textId += resistanceDict[menuIndex]['items'][itemIndex] + return textId + + +def decodeId(textId): + menuIndex = int(textId / 100) + itemIndex = textId % 100 + return (menuIndex, itemIndex) + + +def validateId(textId): + if textId < 0: + return 0 + menuIndex, itemIndex = decodeId(textId) + if menuIndex not in resistanceDict: + return 0 + if itemIndex >= len(resistanceDict[menuIndex]['values']): + return 0 + return 1 + + +def getItems(menuIndex): + return resistanceDict[menuIndex]['items'] + + +def getMenuName(textId): + menuIndex, _ = decodeId(textId) + return resistanceDict[menuIndex]['menuName'] + + +def getItemText(textId): + menuIndex, itemIndex = decodeId(textId) + resistance = resistanceDict[menuIndex] + value = resistance['values'][itemIndex] + text = resistance['itemText'] + if menuIndex is RESISTANCE_TOONUP: + if value is -1: + value = TTLocalizer.ResistanceToonupItemMax + elif 'extra' in resistance: + value = resistance['extra'][itemIndex] + return text % str(value) + + +def getChatText(textId): + menuIndex, _ = decodeId(textId) + return resistanceDict[menuIndex]['chatText'] + + +def getItemValue(textId): + menuIndex, itemIndex = decodeId(textId) + return resistanceDict[menuIndex]['values'][itemIndex] + + +def getRandomId(): + menuIndex = random.choice(randomResistanceMenu) + itemIndex = random.choice(getItems(menuIndex)) + return encodeId(menuIndex, itemIndex) + + +def doEffect(textId, speakingToon, nearbyToons): + menuIndex, _ = decodeId(textId) + itemValue = getItemValue(textId) + if menuIndex == RESISTANCE_TOONUP: + effect = BattleParticles.loadParticleFile('resistanceEffectSparkle.ptf') + fadeColor = VBase4(1, 0.5, 1, 1) + elif menuIndex == RESISTANCE_MONEY: + effect = BattleParticles.loadParticleFile('resistanceEffectBean.ptf') + bean = loader.loadModel('phase_4/models/props/jellybean4.bam') + bean = bean.find('**/jellybean') + colors = { + 'particles-1': (1, 1, 0, 1), + 'particles-2': (1, 0, 0, 1), + 'particles-3': (0, 1, 0, 1), + 'particles-4': (0, 0, 1, 1), + 'particles-5': (1, 0, 1, 1) + } + for name, color in colors.items(): + node = bean.copyTo(NodePath()) + node.setColorScale(*color) + p = effect.getParticlesNamed(name) + p.renderer.setGeomNode(node.node()) + fadeColor = VBase4(0, 1, 0, 1) + elif menuIndex == RESISTANCE_RESTOCK: + effect = BattleParticles.loadParticleFile('resistanceEffectSprite.ptf') + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + invModel.setScale(4) + invModel.flattenLight() + icons = [] + if itemValue != -1: + for item in xrange(6): + iconName = ToontownBattleGlobals.AvPropsNew[itemValue][item] + icons.append(invModel.find('**/%s' % iconName)) + else: + tracks = range(7) + random.shuffle(tracks) + for i in xrange(6): + track = tracks[i] + item = random.randint(0, 5) + iconName = ToontownBattleGlobals.AvPropsNew[track][item] + icons.append(invModel.find('**/%s' % iconName)) + iconDict = { + 'particles-1': icons[0], + 'particles-2': icons[1], + 'particles-3': icons[2], + 'particles-4': icons[3], + 'particles-5': icons[4], + 'particles-6': icons[5] + } + for name, icon in iconDict.items(): + p = effect.getParticlesNamed(name) + p.renderer.setFromNode(icon) + fadeColor = VBase4(0, 0, 1, 1) + elif menuIndex == RESISTANCE_MERITS: + effect = BattleParticles.loadParticleFile('resistanceEffectSprite.ptf') + cogModel = loader.loadModel('phase_3/models/gui/cog_icons') + cogModel.setScale(0.75) + cogModel.flattenLight() + + if itemValue != -1: + iconDict = {'particles-1': cogModel.find(SuitDNA.suitDeptModelPaths[itemValue])} + else: + iconDict = {} + + for i in xrange(len(SuitDNA.suitDepts)): + iconDict['particles-%s' % (i + 1)] = cogModel.find(SuitDNA.suitDeptModelPaths[i]) + + for name, icon in iconDict.items(): + p = effect.getParticlesNamed(name) + p.renderer.setFromNode(icon) + + fadeColor = VBase4(0.7, 0.7, 0.7, 1.0) + cogModel.removeNode() + elif menuIndex == RESISTANCE_TICKETS: + effect = BattleParticles.loadParticleFile('resistanceEffectSprite.ptf') + model = loader.loadModel('phase_6/models/karting/tickets') + model.flattenLight() + iconDict = {'particles-1': model} + + for name, icon in iconDict.items(): + p = effect.getParticlesNamed(name) + p.renderer.setFromNode(icon) + + fadeColor = VBase4(1, 1, 0, 1) + else: + return + recolorToons = Parallel() + for toonId in nearbyToons: + toon = base.cr.doId2do.get(toonId) + if toon and (not toon.ghostMode): + i = Sequence( + toon.doToonColorScale(fadeColor, 0.3), + toon.doToonColorScale(toon.defaultColorScale, 0.3), + Func(toon.restoreDefaultColorScale) + ) + recolorToons.append(i) + i = Parallel( + ParticleInterval(effect, speakingToon, worldRelative=0, duration=3, cleanup=True), + Sequence(Wait(0.2), recolorToons), + autoFinish=1 + ) + i.start() diff --git a/toontown/chat/TTChatInputSpeedChat.py b/toontown/chat/TTChatInputSpeedChat.py new file mode 100755 index 00000000..a1e9d065 --- /dev/null +++ b/toontown/chat/TTChatInputSpeedChat.py @@ -0,0 +1,914 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from otp.speedchat.SpeedChatTypes import * +from toontown.speedchat.TTSpeedChatTypes import * +from otp.speedchat.SpeedChat import SpeedChat +from otp.speedchat import SpeedChatGlobals +from toontown.speedchat import TTSpeedChatGlobals +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import string +from otp.otpbase import OTPLocalizer +from otp.otpbase import OTPGlobals +from toontown.shtiker.OptionsPage import speedChatStyles +from toontown.toonbase import TTLocalizer +from toontown.parties.PartyGlobals import ActivityIds, DecorationIds +from toontown.toonbase import ToontownGlobals +scStructure = [ + [OTPLocalizer.SCMenuHello, + {100: 0}, + {101: 0}, + {102: 0}, + {103: 0}, + {104: 0}, + {105: 0}, + 106, + 107, + 108, + 109], + [OTPLocalizer.SCMenuBye, + {200: 0}, + {201: 0}, + {202: 0}, + 203, + 204, + 205, + 206, + 208, + 209, + 207], + [OTPLocalizer.SCMenuHappy, + {300: 1}, + {301: 1}, + {302: 1}, + 303, + {304: 1}, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + {312: 1}, + {313: 1}, + {314: 1}, + 315], + [OTPLocalizer.SCMenuSad, + {400: 2}, + {401: 2}, + {402: 2}, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410], + [OTPLocalizer.SCMenuFriendly, + [OTPLocalizer.SCMenuFriendlyYou, + 600, + 601, + 602, + 603], + [OTPLocalizer.SCMenuFriendlyILike, + 700, + 701, + 702, + 703, + 704, + 705], + 500, + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 515, + 511, + 512, + 513, + 514], + [OTPLocalizer.SCMenuSorry, + 801, + 800, + 802, + 803, + 804, + 811, + 814, + 815, + 817, + 812, + 813, + 818, + 805, + 806, + 807, + 816, + 808, + {809: 5}, + 810], + [OTPLocalizer.SCMenuStinky, + {900: 3}, + {901: 3}, + {902: 3}, + {903: 3}, + 904, + {905: 3}, + 907], + [OTPLocalizer.SCMenuPlaces, + [OTPLocalizer.SCMenuPlacesPlayground, + 1100, + 1101, + 1105, + 1106, + 1107, + 1108, + 1109, + 1110, + 1111, + 1117, + 1125, + 1126], + [OTPLocalizer.SCMenuPlacesCogs, + 1102, + 1103, + 1104, + 1114, + 1115, + 1116, + 1119, + 1120, + 1121, + 1122, + 1123, + 1124, + 1127, + 1128, + 1129, + 1130], + [OTPLocalizer.SCMenuPlacesEstate, + 1112, + 1113, + 1013, + 1118, + 1016], + [OTPLocalizer.SCMenuParties, + 5300, + 5301, + 5302, + 5303], + [OTPLocalizer.SCMenuPlacesWait, + 1015, + 1007, + 1008, + 1010, + 1011, + 1014, + 1017], + 1000, + 1001, + 1002, + 1003, + 1004, + 1005, + 1006, + 1009, + 1012], + [OTPLocalizer.SCMenuToontasks, + [TTSCToontaskMenu, OTPLocalizer.SCMenuToontasksMyTasks], + [OTPLocalizer.SCMenuToontasksYouShouldChoose, + 1300, + 1301, + 1302, + 1303, + 1304], + [OTPLocalizer.SCMenuToontasksINeedMore, + 1206, + 1210, + 1211, + 1212, + 1207, + 1213, + 1214, + 1215], + 1200, + 1201, + 1202, + 1208, + 1203, + 1209, + 1204, + 1205], + [OTPLocalizer.SCMenuBattle, + [OTPLocalizer.SCMenuBattleGags, + 1500, + 1501, + 1502, + 1503, + 1504, + 1505, + 1506, + 1401, + 1402, + 1413], + [OTPLocalizer.SCMenuBattleTaunts, + 1403, + 1406, + 1520, + 1521, + 1522, + 1523, + 1524, + 1525, + 1526, + 1407, + 1408], + [OTPLocalizer.SCMenuBattleStrategy, + 1414, + 1550, + 1551, + 1552, + 1415, + 1553, + 1554, + 1555, + 1556, + 1557, + 1558, + 1559], + 1400, + 1416, + 1404, + 1405, + 1409, + 1410, + 1411, + 1412], + [OTPLocalizer.SCMenuGagShop, + 1600, + 1601, + 1602, + 1603, + 1604, + 1605, + 1606], + {1: 17}, + {2: 18}, + 3] +if hasattr(base, 'wantPets'): + scPetMenuStructure = base.wantPets and [[OTPLocalizer.SCMenuPets, + [TTSCPetTrickMenu, OTPLocalizer.SCMenuPetTricks], + 21000, + 21001, + 21002, + 21003, + 21004, + 21005, + 21006]] +cfoMenuStructure = [[OTPLocalizer.SCMenuCFOBattleCranes, + 2100, + 2101, + 2102, + 2103, + 2104, + 2105, + 2106, + 2107, + 2108, + 2109, + 2110], + [OTPLocalizer.SCMenuCFOBattleGoons, + 2120, + 2121, + 2122, + 2123, + 2124, + 2125, + 2126], + 2130, + 2131, + 2132, + 2133, + 1410] +cjMenuStructure = [2200, + 2201, + 2202, + 2203, + 2204, + 2205, + 2206, + 2207, + 2208, + 2209, + 2210] +ceoMenuStructure = [2300, + 2301, + 2302, + 2303, + 2304, + 2305, + 2306, + 2307, + 2312, + 2313, + 2314, + 2315, + 2308, + 2309, + 2310, + 2311, + 2316, + 2317] + +class TTChatInputSpeedChat(DirectObject.DirectObject): + DefaultSCColorScheme = SCColorScheme() + + def __init__(self, chatMgr): + self.chatMgr = chatMgr + self.firstTime = 0 + self.whisperAvatarId = None + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.emoteNoAccessPanel = DirectFrame(parent=hidden, relief=None, state='normal', text=OTPLocalizer.SCEmoteNoAccessMsg, frameSize=(-1, 1, -1, 1), geom=DGG.getDefaultDialogGeom(), geom_color=OTPGlobals.GlobalDialogColor, geom_scale=(0.92, 1, 0.6), geom_pos=(0, 0, -.08), text_scale=0.08) + self.okButton = DirectButton(parent=self.emoteNoAccessPanel, image=okButtonImage, relief=None, text=OTPLocalizer.SCEmoteNoAccessOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.2), command=self.handleEmoteNoAccessDone) + self.insidePartiesMenu = None + self.createSpeedChat() + self.whiteList = None + self.allowWhiteListSpeedChat = base.config.GetBool('white-list-speed-chat', 0) + if self.allowWhiteListSpeedChat: + self.addWhiteList() + self.factoryMenu = None + self.kartRacingMenu = None + self.cogMenu = None + self.cfoMenu = None + self.cjMenu = None + self.ceoMenu = None + self.golfMenu = None + self.boardingGroupMenu = None + self.aprilToonsMenu = None + self.victoryPartiesMenu = None + self.sillyPhaseOneMenu = None + self.sillyPhaseTwoMenu = None + self.sillyPhaseThreeMenu = None + self.sillyPhaseFourMenu = None + self.sillyPhaseFiveMenu = None + self.sellbotNerfMenu = None + self.jellybeanJamMenu = None + self.halloweenMenu = None + self.winterMenu = None + self.sellbotInvasionMenu = None + self.sellbotFieldOfficeMenu = None + self.idesOfMarchMenu = None + + def listenForSCEvent(eventBaseName, handler, self = self): + eventName = self.speedChat.getEventName(eventBaseName) + self.accept(eventName, handler) + + listenForSCEvent(SpeedChatGlobals.SCTerminalLinkedEmoteEvent, self.handleLinkedEmote) + listenForSCEvent(SpeedChatGlobals.SCStaticTextMsgEvent, self.handleStaticTextMsg) + listenForSCEvent(SpeedChatGlobals.SCCustomMsgEvent, self.handleCustomMsg) + listenForSCEvent(SpeedChatGlobals.SCEmoteMsgEvent, self.handleEmoteMsg) + listenForSCEvent(SpeedChatGlobals.SCEmoteNoAccessEvent, self.handleEmoteNoAccess) + listenForSCEvent(TTSpeedChatGlobals.TTSCToontaskMsgEvent, self.handleToontaskMsg) + listenForSCEvent(TTSpeedChatGlobals.TTSCResistanceMsgEvent, self.handleResistanceMsg) + listenForSCEvent('SpeedChatStyleChange', self.handleSpeedChatStyleChange) + self.fsm = ClassicFSM.ClassicFSM('SpeedChat', [State.State('off', self.enterOff, self.exitOff, ['active']), State.State('active', self.enterActive, self.exitActive, ['off'])], 'off', 'off') + self.fsm.enterInitialState() + return + + def delete(self): + self.ignoreAll() + self.removeWhiteList() + self.okButton.destroy() + self.emoteNoAccessPanel.destroy() + del self.emoteNoAccessPanel + self.speedChat.destroy() + del self.speedChat + del self.fsm + del self.chatMgr + + def show(self, whisperAvatarId = None): + self.whisperAvatarId = whisperAvatarId + self.fsm.request('active') + + def hide(self): + self.fsm.request('off') + + def createSpeedChat(self): + structure = [] + structure.append([SCEmoteMenu, OTPLocalizer.SCMenuEmotions]) + structure.append([SCCustomMenu, OTPLocalizer.SCMenuCustom]) + structure.append([TTSCResistanceMenu, OTPLocalizer.SCMenuResistance]) + if hasattr(base, 'wantPets') and base.wantPets: + structure += scPetMenuStructure + structure += scStructure + self.createSpeedChatObject(structure) + self.enterActive() + + def enterOff(self): + pass + + def exitOff(self): + pass + + + def enterActive(self): + + def handleCancel(self = self): + self.chatMgr.fsm.request('mainMenu') + + self.accept('mouse1', handleCancel) + + def selectionMade(self = self): + self.chatMgr.fsm.request('mainMenu') + + self.terminalSelectedEvent = self.speedChat.getEventName(SpeedChatGlobals.SCTerminalSelectedEvent) + if base.config.GetBool('want-sc-auto-hide', 1): + self.accept(self.terminalSelectedEvent, selectionMade) + self.speedChat.reparentTo(base.a2dpTopLeft, DGG.FOREGROUND_SORT_INDEX) + scZ = -0.04 + self.speedChat.setPos(0.283, 0, scZ) + if not self.firstTime: + self.speedChat.setPos(-99, -99, -99) + self.firstTime = 1 + self.speedChat.setWhisperMode(self.whisperAvatarId != None) + self.speedChat.enter() + return + + def exitActive(self): + self.ignore('mouse1') + self.ignore(self.terminalSelectedEvent) + self.speedChat.exit() + self.speedChat.reparentTo(hidden) + self.emoteNoAccessPanel.reparentTo(hidden) + + def handleLinkedEmote(self, emoteId): + if self.whisperAvatarId is None: + lt = base.localAvatar + lt.b_setEmoteState(emoteId, animMultiplier=lt.animMultiplier) + return + + def handleStaticTextMsg(self, textId): + if self.whisperAvatarId is None: + self.chatMgr.sendSCChatMessage(textId) + else: + self.chatMgr.sendSCWhisperMessage(textId, self.whisperAvatarId) + + def handleCustomMsg(self, textId): + if self.whisperAvatarId is None: + self.chatMgr.sendSCCustomChatMessage(textId) + else: + self.chatMgr.sendSCCustomWhisperMessage(textId, self.whisperAvatarId) + + def handleEmoteMsg(self, emoteId): + if self.whisperAvatarId is None: + self.chatMgr.sendSCEmoteChatMessage(emoteId) + else: + self.chatMgr.sendSCEmoteWhisperMessage(emoteId, self.whisperAvatarId) + + def handleEmoteNoAccess(self): + if self.whisperAvatarId is None: + self.emoteNoAccessPanel.setPos(0, 0, 0) + else: + self.emoteNoAccessPanel.setPos(0.37, 0, 0) + self.emoteNoAccessPanel.reparentTo(aspect2d) + return + + def handleEmoteNoAccessDone(self): + self.emoteNoAccessPanel.reparentTo(hidden) + + def handleToontaskMsg(self, taskId, toNpcId, toonProgress, msgIndex): + if self.whisperAvatarId is None: + self.chatMgr.sendSCToontaskChatMessage(taskId, toNpcId, toonProgress, msgIndex) + else: + self.chatMgr.sendSCToontaskWhisperMessage(taskId, toNpcId, toonProgress, msgIndex, self.whisperAvatarId) + + def handleResistanceMsg(self, textId): + self.chatMgr.sendSCResistanceChatMessage(textId) + + def handleSpeedChatStyleChange(self): + nameKey, arrowColor, rolloverColor, frameColor = speedChatStyles[base.localAvatar.getSpeedChatStyleIndex()] + newSCColorScheme = SCColorScheme(arrowColor=arrowColor, rolloverColor=rolloverColor, frameColor=frameColor) + self.speedChat.setColorScheme(newSCColorScheme) + + def createSpeedChatObject(self, structure): + if hasattr(self, 'speedChat'): + self.speedChat.exit() + self.speedChat.destroy() + del self.speedChat + self.speedChat = SpeedChat(structure=structure, backgroundModelName='phase_3/models/gui/ChatPanel', guiModelName='phase_3.5/models/gui/speedChatGui') + self.speedChat.setScale(TTLocalizer.TTCISCspeedChat) + self.speedChat.setBin('gui-popup', 0) + self.speedChat.setTopLevelOverlap(TTLocalizer.TTCISCtopLevelOverlap) + self.speedChat.setColorScheme(self.DefaultSCColorScheme) + self.speedChat.finalizeAll() + + def addFactoryMenu(self): + if self.factoryMenu == None: + menu = TTSCFactoryMenu() + self.factoryMenu = SCMenuHolder(OTPLocalizer.SCMenuFactory, menu=menu) + self.speedChat[2:2] = [self.factoryMenu] + return + + def removeFactoryMenu(self): + if self.factoryMenu: + i = self.speedChat.index(self.factoryMenu) + del self.speedChat[i] + self.factoryMenu.destroy() + self.factoryMenu = None + return + + def addKartRacingMenu(self): + if self.kartRacingMenu == None: + self.kartRacingMenu = SCMenuHolder(OTPLocalizer.SCMenuKartRacing, menu=SCSpecialMenu(KartRacingMenu)) + self.speedChat[2:2] = [self.kartRacingMenu] + return + + def removeKartRacingMenu(self): + if self.kartRacingMenu: + i = self.speedChat.index(self.kartRacingMenu) + del self.speedChat[i] + self.kartRacingMenu.destroy() + self.kartRacingMenu = None + return + + def addCogMenu(self, indices): + if self.cogMenu == None: + menu = TTSCCogMenu(indices) + self.cogMenu = SCMenuHolder(OTPLocalizer.SCMenuCog, menu=menu) + self.speedChat[2:2] = [self.cogMenu] + return + + def removeCogMenu(self): + if self.cogMenu: + i = self.speedChat.index(self.cogMenu) + del self.speedChat[i] + self.cogMenu.destroy() + self.cogMenu = None + return + + def addCFOMenu(self): + if self.cfoMenu == None: + menu = SCMenu() + menu.rebuildFromStructure(cfoMenuStructure) + self.cfoMenu = SCMenuHolder(OTPLocalizer.SCMenuCFOBattle, menu=menu) + self.speedChat[2:2] = [self.cfoMenu] + return + + def removeCFOMenu(self): + if self.cfoMenu: + i = self.speedChat.index(self.cfoMenu) + del self.speedChat[i] + self.cfoMenu.destroy() + self.cfoMenu = None + return + + def addCJMenu(self, bonusWeight = -1): + if self.cjMenu == None: + menu = SCMenu() + myMenuCopy = cjMenuStructure[:] + if bonusWeight >= 0: + myMenuCopy.append(2211 + bonusWeight) + menu.rebuildFromStructure(myMenuCopy) + self.cjMenu = SCMenuHolder(OTPLocalizer.SCMenuCJBattle, menu=menu) + self.speedChat[2:2] = [self.cjMenu] + return + + def removeCJMenu(self): + if self.cjMenu: + i = self.speedChat.index(self.cjMenu) + del self.speedChat[i] + self.cjMenu.destroy() + self.cjMenu = None + return + + def addCEOMenu(self): + if self.ceoMenu == None: + menu = SCMenu() + menu.rebuildFromStructure(ceoMenuStructure) + self.ceoMenu = SCMenuHolder(OTPLocalizer.SCMenuCEOBattle, menu=menu) + self.speedChat[2:2] = [self.ceoMenu] + return + + def removeCEOMenu(self): + if self.ceoMenu: + i = self.speedChat.index(self.ceoMenu) + del self.speedChat[i] + self.ceoMenu.destroy() + self.ceoMenu = None + return + + def addInsidePartiesMenu(self): + + def isActivityInParty(activityId): + activityList = base.distributedParty.partyInfo.activityList + for activity in activityList: + if activity.activityId == activityId: + return True + + return False + + def isDecorInParty(decorId): + decorList = base.distributedParty.partyInfo.decors + for decor in decorList: + if decor.decorId == decorId: + return True + + return False + + insidePartiesMenuStructure = [5305, + 5306, + 5307, + 5308, + 5309] + if self.insidePartiesMenu == None: + menu = SCMenu() + if hasattr(base, 'distributedParty') and base.distributedParty: + if base.distributedParty.partyInfo.hostId == localAvatar.doId: + insidePartiesMenuStructure.insert(0, 5304) + if isActivityInParty(0): + insidePartiesMenuStructure.extend([5310, 5311]) + if isActivityInParty(1): + insidePartiesMenuStructure.append(5312) + if isActivityInParty(2): + insidePartiesMenuStructure.extend([5313, 5314]) + if isActivityInParty(3): + insidePartiesMenuStructure.append(5315) + if isActivityInParty(4): + insidePartiesMenuStructure.extend([5316, 5317]) + if isActivityInParty(5): + insidePartiesMenuStructure.append(5318) + if isActivityInParty(6): + insidePartiesMenuStructure.extend([5319, 5320]) + if len(base.distributedParty.partyInfo.decors): + insidePartiesMenuStructure.append(5321) + if isDecorInParty(3): + insidePartiesMenuStructure.append(5322) + menu.rebuildFromStructure(insidePartiesMenuStructure) + self.insidePartiesMenu = SCMenuHolder(OTPLocalizer.SCMenuParties, menu=menu) + self.speedChat[2:2] = [self.insidePartiesMenu] + return + + def removeInsidePartiesMenu(self): + if self.insidePartiesMenu: + i = self.speedChat.index(self.insidePartiesMenu) + del self.speedChat[i] + self.insidePartiesMenu.destroy() + self.insidePartiesMenu = None + return + + def addGolfMenu(self): + if self.golfMenu == None: + self.golfMenu = SCMenuHolder(OTPLocalizer.SCMenuGolf, menu=SCSpecialMenu(GolfMenu)) + self.speedChat[2:2] = [self.golfMenu] + return + + def removeGolfMenu(self): + if self.golfMenu: + i = self.speedChat.index(self.golfMenu) + del self.speedChat[i] + self.golfMenu.destroy() + self.golfMenu = None + return + + def addBoardingGroupMenu(self, zoneId): + if self.boardingGroupMenu == None: + menu = TTSCBoardingMenu(zoneId) + self.boardingGroupMenu = SCMenuHolder(OTPLocalizer.SCMenuBoardingGroup, menu=menu) + self.speedChat[2:2] = [self.boardingGroupMenu] + return + + def removeBoardingGroupMenu(self): + if self.boardingGroupMenu: + i = self.speedChat.index(self.boardingGroupMenu) + del self.speedChat[i] + self.boardingGroupMenu.destroy() + self.boardingGroupMenu = None + return + + def addAprilToonsMenu(self): + if self.aprilToonsMenu == None: + self.aprilToonsMenu = SCMenuHolder(OTPLocalizer.SCMenuAprilToons, menu=SCSpecialMenu(AprilToonsMenu)) + self.speedChat[3:3] = [self.aprilToonsMenu] + return + + def removeAprilToonsMenu(self): + if self.aprilToonsMenu: + i = self.speedChat.index(self.aprilToonsMenu) + del self.speedChat[i] + self.aprilToonsMenu.destroy() + self.aprilToonsMenu = None + return + + def addSillyPhaseOneMenu(self): + if self.sillyPhaseOneMenu == None: + self.sillyPhaseOneMenu = SCMenuHolder(OTPLocalizer.SCMenuSillyHoliday, menu=SCSpecialMenu(SillyPhaseOneMenu)) + self.speedChat[3:3] = [self.sillyPhaseOneMenu] + return + + def removeSillyPhaseOneMenu(self): + if self.sillyPhaseOneMenu: + i = self.speedChat.index(self.sillyPhaseOneMenu) + del self.speedChat[i] + self.sillyPhaseOneMenu.destroy() + self.sillyPhaseOneMenu = None + return + + def addSillyPhaseTwoMenu(self): + if self.sillyPhaseTwoMenu == None: + self.sillyPhaseTwoMenu = SCMenuHolder(OTPLocalizer.SCMenuSillyHoliday, menu=SCSpecialMenu(SillyPhaseTwoMenu)) + self.speedChat[3:3] = [self.sillyPhaseTwoMenu] + return + + def removeSillyPhaseTwoMenu(self): + if self.sillyPhaseTwoMenu: + i = self.speedChat.index(self.sillyPhaseTwoMenu) + del self.speedChat[i] + self.sillyPhaseTwoMenu.destroy() + self.sillyPhaseTwoMenu = None + return + + def addSillyPhaseThreeMenu(self): + if self.sillyPhaseThreeMenu == None: + self.sillyPhaseThreeMenu = SCMenuHolder(OTPLocalizer.SCMenuSillyHoliday, menu=SCSpecialMenu(SillyPhaseThreeMenu)) + self.speedChat[3:3] = [self.sillyPhaseThreeMenu] + return + + def removeSillyPhaseThreeMenu(self): + if self.sillyPhaseThreeMenu: + i = self.speedChat.index(self.sillyPhaseThreeMenu) + del self.speedChat[i] + self.sillyPhaseThreeMenu.destroy() + self.sillyPhaseThreeMenu = None + return + + def addSillyPhaseFourMenu(self): + if self.sillyPhaseFourMenu == None: + self.sillyPhaseFourMenu = SCMenuHolder(OTPLocalizer.SCMenuSillyHoliday, menu=SCSpecialMenu(SillyPhaseFourMenu)) + self.speedChat[3:3] = [self.sillyPhaseFourMenu] + return + + def removeSillyPhaseFourMenu(self): + if self.sillyPhaseFourMenu: + i = self.speedChat.index(self.sillyPhaseFourMenu) + del self.speedChat[i] + self.sillyPhaseFourMenu.destroy() + self.sillyPhaseFourMenu = None + return + + def addSillyPhaseFiveMenu(self): + if self.sillyPhaseFiveMenu == None: + self.sillyPhaseFiveMenu = SCMenuHolder(OTPLocalizer.SCMenuSillyHoliday, menu=SCSpecialMenu(SillyPhaseFiveMenu)) + self.speedChat[3:3] = [self.sillyPhaseFiveMenu] + return + + def removeSillyPhaseFiveMenu(self): + if self.sillyPhaseFiveMenu: + i = self.speedChat.index(self.sillyPhaseFiveMenu) + del self.speedChat[i] + self.sillyPhaseFiveMenu.destroy() + self.sillyPhaseFiveMenu = None + return + + def addVictoryPartiesMenu(self): + if self.victoryPartiesMenu == None: + self.victoryPartiesMenu = SCMenuHolder(OTPLocalizer.SCMenuVictoryParties, menu=SCSpecialMenu(VictoryPartiesMenu)) + self.speedChat[3:3] = [self.victoryPartiesMenu] + return + + def removeVictoryPartiesMenu(self): + if self.victoryPartiesMenu: + i = self.speedChat.index(self.victoryPartiesMenu) + del self.speedChat[i] + self.victoryPartiesMenu.destroy() + self.victoryPartiesMenu = None + return + + def addSellbotNerfMenu(self): + if self.sellbotNerfMenu == None: + self.sellbotNerfMenu = SCMenuHolder(OTPLocalizer.SCMenuSellbotNerf, menu=SCSpecialMenu(SellbotNerfMenu)) + self.speedChat[2:2] = [self.sellbotNerfMenu] + return + + def removeSellbotNerfMenu(self): + if self.sellbotNerfMenu: + i = self.speedChat.index(self.sellbotNerfMenu) + del self.speedChat[i] + self.sellbotNerfMenu.destroy() + self.sellbotNerfMenu = None + return + + def addJellybeanJamMenu(self): + if self.jellybeanJamMenu == None: + self.jellybeanJamMenu = SCMenuHolder(OTPLocalizer.SCMenuJellybeanJam, menu=SCSpecialMenu(JellybeanJamMenu)) + self.speedChat[2:2] = [self.jellybeanJamMenu] + return + + def removeJellybeanJamMenu(self): + if self.jellybeanJamMenu: + i = self.speedChat.index(self.jellybeanJamMenu) + del self.speedChat[i] + self.jellybeanJamMenu.destroy() + self.jellybeanJamMenu = None + return + + def addHalloweenMenu(self): + if self.halloweenMenu == None: + self.halloweenMenu = SCMenuHolder(OTPLocalizer.SCMenuHalloween, menu=SCSpecialMenu(HalloweenMenu)) + self.speedChat[2:2] = [self.halloweenMenu] + return + + def removeHalloweenMenu(self): + if self.halloweenMenu: + i = self.speedChat.index(self.halloweenMenu) + del self.speedChat[i] + self.halloweenMenu.destroy() + self.halloweenMenu = None + return + + def addWinterMenu(self, carol = False): + if self.winterMenu == None: + self.winterMenu = SCMenuHolder(OTPLocalizer.SCMenuWinter, menu=SCSpecialMenu(WinterMenu)) + self.speedChat[2:2] = [self.winterMenu] + return + + def removeWinterMenu(self): + if self.winterMenu: + i = self.speedChat.index(self.winterMenu) + del self.speedChat[i] + self.winterMenu.destroy() + self.winterMenu = None + return + + def addCarolMenu(self): + self.removeWinterMenu() + self.addWinterMenu(carol=True) + + def removeCarolMenu(self): + pass + + def addWhiteList(self): + if self.whiteList == None: + from toontown.chat.TTSCWhiteListTerminal import TTSCWhiteListTerminal + self.whiteList = TTSCWhiteListTerminal(4, self) + self.speedChat[1:1] = [self.whiteList] + return + + def removeWhiteList(self): + if self.whiteList: + i = self.speedChat.index(self.whiteList) + del self.speedChat[i] + self.whiteList.destroy() + self.whiteList = None + return + + def addSellbotInvasionMenu(self): + if self.sellbotInvasionMenu == None: + self.sellbotInvasionMenu = SCMenuHolder(OTPLocalizer.SCMenuSellbotInvasion, menu=SCSpecialMenu(SellbotInvasionMenu)) + self.speedChat[2:2] = [self.sellbotInvasionMenu] + return + + def removeSellbotInvasionMenu(self): + if self.sellbotInvasionMenu: + i = self.speedChat.index(self.sellbotInvasionMenu) + del self.speedChat[i] + self.sellbotInvasionMenu.destroy() + self.sellbotInvasionMenu = None + return + + def addSellbotFieldOfficeMenu(self): + if self.sellbotFieldOfficeMenu == None: + self.sellbotFieldOfficeMenu = SCMenuHolder(OTPLocalizer.SCMenuFieldOffice, menu=SCSpecialMenu(SellbotFieldOfficeMenu)) + self.speedChat[2:2] = [self.sellbotFieldOfficeMenu] + return + + def removeSellbotFieldOfficeMenu(self): + if self.sellbotFieldOfficeMenu: + i = self.speedChat.index(self.sellbotFieldOfficeMenu) + del self.speedChat[i] + self.sellbotFieldOfficeMenu.destroy() + self.sellbotFieldOfficeMenu = None + return + + def addIdesOfMarchMenu(self): + if self.idesOfMarchMenu == None: + self.idesOfMarchMenu = SCMenuHolder(OTPLocalizer.SCMenuIdesOfMarch, menu=SCSpecialMenu(IdesOfMarchMenu)) + self.speedChat[2:2] = [self.idesOfMarchMenu] + return + + def removeIdesOfMarchMenu(self): + if self.idesOfMarchMenu: + i = self.speedChat.index(self.idesOfMarchMenu) + del self.speedChat[i] + self.idesOfMarchMenu.destroy() + self.idesOfMarchMenu = None + return diff --git a/toontown/chat/TTChatInputWhiteList.py b/toontown/chat/TTChatInputWhiteList.py new file mode 100755 index 00000000..292de02f --- /dev/null +++ b/toontown/chat/TTChatInputWhiteList.py @@ -0,0 +1,154 @@ +from otp.chat.ChatInputWhiteListFrame import ChatInputWhiteListFrame +from direct.showbase import DirectObject +from otp.otpbase import OTPGlobals +import sys +from direct.gui.DirectGui import * +from panda3d.core import * +from otp.chat import ChatUtil +from otp.otpbase import OTPLocalizer +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals + +class TTChatInputWhiteList(ChatInputWhiteListFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('TTChatInputWhiteList') + TFToggleKey = base.config.GetString('true-friend-toggle-key', 'alt') + TFToggleKeyUp = TFToggleKey + '-up' + + def __init__(self, parent = None, **kw): + entryOptions = {'parent': self, + 'relief': DGG.SUNKEN, + 'scale': 0.05, + 'frameColor': (0.9, 0.9, 0.85, 0.0), + 'pos': (-0.2, 0, 0.11), + 'entryFont': OTPGlobals.getInterfaceFont(), + 'width': 8.6, + 'numLines': 3, + 'cursorKeys': 0, + 'backgroundFocus': 0, + 'suppressKeys': 0, + 'suppressMouse': 1, + 'command': self.sendChat, + 'focus': 0, + 'text': '', + 'sortOrder': DGG.FOREGROUND_SORT_INDEX} + ChatInputWhiteListFrame.__init__(self, entryOptions, parent, **kw) + base.ttwl = self + self.autoOff = 1 + self.sendBy = 'Data' + self.prefilter = 0 + self.promoteWhiteList = 1 + self.typeGrabbed = 0 + self.deactivate() + gui = loader.loadModel('phase_3.5/models/gui/chat_input_gui') + self.chatFrame = DirectFrame(parent=self, image=gui.find('**/Chat_Bx_FNL'), relief=None, pos=(0.0, 0, 0.0), state=DGG.NORMAL) + self.chatButton = DirectButton(parent=self.chatFrame, image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=(0.182, 0, -0.088), relief=None, text=('', OTPLocalizer.ChatInputNormalSayIt, OTPLocalizer.ChatInputNormalSayIt), text_scale=0.06, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.chatButtonPressed) + self.cancelButton = DirectButton(parent=self.chatFrame, image=(gui.find('**/CloseBtn_UP'), gui.find('**/CloseBtn_DN'), gui.find('**/CloseBtn_Rllvr')), pos=(-0.151, 0, -0.088), relief=None, text=('', OTPLocalizer.ChatInputNormalCancel, OTPLocalizer.ChatInputNormalCancel), text_scale=0.06, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.cancelButtonPressed) + self.whisperLabel = DirectLabel(parent=self.chatFrame, pos=(0.02, 0, 0.23), relief=DGG.FLAT, frameColor=(1, 1, 0.5, 1), frameSize=(-0.23, + 0.23, + -0.07, + 0.05), text=OTPLocalizer.ChatInputNormalWhisper, text_scale=0.04, text_fg=Vec4(0, 0, 0, 1), text_wordwrap=9.5, textMayChange=1) + self.chatEntry.bind(DGG.OVERFLOW, self.chatOverflow) + self.chatEntry.bind(DGG.TYPE, self.typeCallback) + self.trueFriendChat = 0 + if base.config.GetBool('whisper-to-nearby-true-friends', 1): + self.accept(self.TFToggleKey, self.shiftPressed) + return + + def shiftPressed(self): + self.ignore(self.TFToggleKey) + self.trueFriendChat = 1 + self.accept(self.TFToggleKeyUp, self.shiftReleased) + + def shiftReleased(self): + self.ignore(self.TFToggleKeyUp) + self.trueFriendChat = 0 + self.accept(self.TFToggleKey, self.shiftPressed) + + def handleTypeGrab(self): + self.ignore('typeEntryGrab') + self.accept('typeEntryRelease', self.handleTypeRelease) + self.typeGrabbed = 1 + + def handleTypeRelease(self): + self.ignore('typeEntryRelease') + self.accept('typeEntryGrab', self.handleTypeGrab) + self.typeGrabbed = 0 + + def typeCallback(self, extraArgs): + if self.typeGrabbed: + return + self.applyFilter(extraArgs) + if localAvatar.chatMgr.chatInputWhiteList.isActive(): + return + messenger.send('wakeup') + messenger.send('enterNormalChat') + + def destroy(self): + self.chatEntry.destroy() + self.chatFrame.destroy() + self.ignoreAll() + ChatInputWhiteListFrame.destroy(self) + + def delete(self): + ChatInputWhiteListFrame.delete(self) + return + + def sendChat(self, text, overflow = False): + if self.typeGrabbed: + return + else: + ChatInputWhiteListFrame.sendChat(self, self.chatEntry.get()) + + def sendChatByData(self, text): + if self.trueFriendChat: + for friendId in base.localAvatar.friendsList: + if base.localAvatar.isTrueFriends(friendId): + self.sendWhisperByFriend(friendId, text) + elif self.receiverId: + base.talkAssistant.sendWhisperTalk(text, self.receiverId) + else: + base.talkAssistant.sendOpenTalk(text) + + def sendWhisperByFriend(self, avatarId, text): + online = 0 + if avatarId in base.cr.doId2do: + online = 1 + avatarUnderstandable = 0 + av = None + if avatarId: + av = base.cr.identifyAvatar(avatarId) + if av != None: + avatarUnderstandable = av.isUnderstandable() + if avatarUnderstandable and online: + base.talkAssistant.sendWhisperTalk(text, avatarId) + return + + def chatButtonPressed(self): + self.sendChat(self.chatEntry.get()) + + def cancelButtonPressed(self): + self.requestMode('Off') + localAvatar.chatMgr.fsm.request('mainMenu') + + def enterAllChat(self): + ChatInputWhiteListFrame.enterAllChat(self) + self.whisperLabel.hide() + + def exitAllChat(self): + ChatInputWhiteListFrame.exitAllChat(self) + + def enterAvatarWhisper(self): + ChatInputWhiteListFrame.enterAvatarWhisper(self) + self.labelWhisper() + + def exitAvatarWhisper(self): + ChatInputWhiteListFrame.exitAvatarWhisper(self) + self.whisperLabel.hide() + + def labelWhisper(self): + if self.receiverId: + self.whisperName = ChatUtil.findAvatarName(self.receiverId) + self.whisperLabel['text'] = OTPLocalizer.ChatInputWhisperLabel % self.whisperName + self.whisperLabel.show() + else: + self.whisperLabel.hide() diff --git a/toontown/chat/TTSCWhiteListTerminal.py b/toontown/chat/TTSCWhiteListTerminal.py new file mode 100755 index 00000000..caa42b5f --- /dev/null +++ b/toontown/chat/TTSCWhiteListTerminal.py @@ -0,0 +1,19 @@ +from otp.speedchat.SCTerminal import SCTerminal +from otp.otpbase.OTPLocalizer import SpeedChatStaticText +SCStaticTextMsgEvent = 'SCStaticTextMsg' + +class TTSCWhiteListTerminal(SCTerminal): + + def __init__(self, textId, parentMenu = None): + SCTerminal.__init__(self) + self.parentClass = parentMenu + self.textId = textId + self.text = SpeedChatStaticText[self.textId] + print 'SpeedText %s %s' % (self.textId, self.text) + + def handleSelect(self): + SCTerminal.handleSelect(self) + if not self.parentClass.whisperAvatarId: + base.localAvatar.chatMgr.fsm.request('whiteListOpenChat') + else: + base.localAvatar.chatMgr.fsm.request('whiteListAvatarChat', [self.parentClass.whisperAvatarId]) diff --git a/toontown/chat/TTTalkAssistant.py b/toontown/chat/TTTalkAssistant.py new file mode 100755 index 00000000..da29592f --- /dev/null +++ b/toontown/chat/TTTalkAssistant.py @@ -0,0 +1,10 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.chat.TalkAssistant import TalkAssistant +from otp.chat.ChatGlobals import * + +class TTTalkAssistant(TalkAssistant): + notify = DirectNotifyGlobal.directNotify.newCategory('TTTalkAssistant') + + def sendToonTaskSpeedChat(self, taskId, toNpcId, toonProgress, msgIndex): + messenger.send(SCChatEvent) + messenger.send('chatUpdateSCToontask', [taskId, toNpcId, toonProgress, msgIndex]) diff --git a/toontown/chat/ToonChatGarbler.py b/toontown/chat/ToonChatGarbler.py new file mode 100755 index 00000000..cec03bbb --- /dev/null +++ b/toontown/chat/ToonChatGarbler.py @@ -0,0 +1,16 @@ +from otp.chat import ChatGarbler +from toontown.toonbase import TTLocalizer + +class ToonChatGarbler(ChatGarbler.ChatGarbler): + + def __init__(self): + self.messages = {'dog': TTLocalizer.ChatGarblerDog, + 'cat': TTLocalizer.ChatGarblerCat, + 'mouse': TTLocalizer.ChatGarblerMouse, + 'horse': TTLocalizer.ChatGarblerHorse, + 'rabbit': TTLocalizer.ChatGarblerRabbit, + 'duck': TTLocalizer.ChatGarblerDuck, + 'monkey': TTLocalizer.ChatGarblerMonkey, + 'bear': TTLocalizer.ChatGarblerBear, + 'pig': TTLocalizer.ChatGarblerPig, + 'default': TTLocalizer.ChatGarblerDefault} diff --git a/toontown/chat/ToontownChatManager.py b/toontown/chat/ToontownChatManager.py new file mode 100755 index 00000000..b0b89382 --- /dev/null +++ b/toontown/chat/ToontownChatManager.py @@ -0,0 +1,177 @@ +from direct.showbase import DirectObject +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPLocalizer +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from panda3d.core import * +from otp.chat import ChatManager +from TTChatInputSpeedChat import TTChatInputSpeedChat +from TTChatInputWhiteList import TTChatInputWhiteList + +class ToontownChatManager(ChatManager.ChatManager): + notify = DirectNotifyGlobal.directNotify.newCategory('ToontownChatManager') + + def __init__(self, cr, localAvatar): + gui = loader.loadModel('phase_3.5/models/gui/chat_input_gui') + self.normalButton = DirectButton(image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=(0.0683, 0, -0.072), parent=base.a2dTopLeft, scale=1.179, relief=None, image_color=Vec4(1, 1, 1, 1), text=('', OTPLocalizer.ChatManagerChat, OTPLocalizer.ChatManagerChat), text_align=TextNode.ALeft, text_scale=TTLocalizer.TCMnormalButton, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(-0.0525, -0.09), textMayChange=0, sortOrder=DGG.FOREGROUND_SORT_INDEX, command=self.__normalButtonPressed) + self.normalButton.hide() + self.openScSfx = loader.loadSfx('phase_3.5/audio/sfx/GUI_quicktalker.ogg') + self.openScSfx.setVolume(0.6) + self.scButton = DirectButton(image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=TTLocalizer.TCMscButtonPos, parent=base.a2dTopLeft, scale=1.179, relief=None, image_color=Vec4(0.75, 1, 0.6, 1), text=('', OTPLocalizer.GlobalSpeedChatName, OTPLocalizer.GlobalSpeedChatName), text_scale=TTLocalizer.TCMscButton, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, sortOrder=DGG.FOREGROUND_SORT_INDEX, command=self.__scButtonPressed, clickSound=self.openScSfx) + self.scButton.hide() + self.whisperFrame = DirectFrame(parent=base.a2dTopLeft, relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(0.77, 0.70, 0.20), image_color=OTPGlobals.GlobalDialogColor, pos=(0.40, 0, -0.105), text=OTPLocalizer.ChatManagerWhisperTo, text_wordwrap=6.5, text_scale=TTLocalizer.TCMwhisperFrame, text_fg=Vec4(0, 0, 0, 1), text_pos=(0.18, 0.04), textMayChange=1, sortOrder=DGG.FOREGROUND_SORT_INDEX) + self.whisperFrame.hide() + self.whisperButton = DirectButton(parent=self.whisperFrame, image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=(-0.33, 0, 0.033), scale=1.179, relief=None, image_color=Vec4(1, 1, 1, 1), text=('', + OTPLocalizer.ChatManagerChat, + OTPLocalizer.ChatManagerChat, + ''), image3_color=Vec4(0.6, 0.6, 0.6, 0.6), text_scale=TTLocalizer.TCMwhisperButton, text_fg=(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.__whisperButtonPressed) + self.whisperScButton = DirectButton(parent=self.whisperFrame, image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=(-0.195, 0, 0.033), scale=1.179, relief=None, image_color=Vec4(0.75, 1, 0.6, 1), text=('', + OTPLocalizer.GlobalSpeedChatName, + OTPLocalizer.GlobalSpeedChatName, + ''), image3_color=Vec4(0.6, 0.6, 0.6, 0.6), text_scale=TTLocalizer.TCMwhisperScButton, text_fg=(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.__whisperScButtonPressed) + self.whisperCancelButton = DirectButton(parent=self.whisperFrame, image=(gui.find('**/CloseBtn_UP'), gui.find('**/CloseBtn_DN'), gui.find('**/CloseBtn_Rllvr')), pos=(-0.06, 0, 0.033), scale=1.179, relief=None, text=('', OTPLocalizer.ChatManagerCancel, OTPLocalizer.ChatManagerCancel), text_scale=0.05, text_fg=(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.__whisperCancelPressed) + gui.removeNode() + ChatManager.ChatManager.__init__(self, cr, localAvatar) + self.chatInputSpeedChat = TTChatInputSpeedChat(self) + self.normalPos = Vec3(0.25, 0, -0.196) + self.whisperPos = Vec3(0.25, 0, -0.28) + self.speedChatPlusPos = Vec3(-0.35, 0, 0.71) + self.SCWhisperPos = Vec3(0, 0, 0) + self.chatInputWhiteList = TTChatInputWhiteList() + self.chatInputNormal = self.chatInputWhiteList + self.chatInputNormal.setPos(self.normalPos) + self.chatInputNormal.desc = 'chatInputNormal' + self.chatInputWhiteList.setPos(self.speedChatPlusPos) + self.chatInputWhiteList.reparentTo(base.a2dTopLeft) + self.chatInputWhiteList.desc = 'chatInputWhiteList' + + def delete(self): + ChatManager.ChatManager.delete(self) + loader.unloadModel('phase_3.5/models/gui/chat_input_gui') + self.normalButton.destroy() + del self.normalButton + self.scButton.destroy() + del self.scButton + del self.openScSfx + self.whisperFrame.destroy() + del self.whisperFrame + self.whisperButton.destroy() + del self.whisperButton + self.whisperScButton.destroy() + del self.whisperScButton + self.whisperCancelButton.destroy() + del self.whisperCancelButton + self.chatInputWhiteList.destroy() + del self.chatInputWhiteList + + def sendSCResistanceChatMessage(self, textId): + messenger.send('chatUpdateSCResistance', [textId]) + self.announceSCChat() + + def sendSCToontaskChatMessage(self, taskId, toNpcId, toonProgress, msgIndex): + messenger.send('chatUpdateSCToontask', [taskId, + toNpcId, + toonProgress, + msgIndex]) + self.announceSCChat() + + def sendSCToontaskWhisperMessage(self, taskId, toNpcId, toonProgress, msgIndex, whisperAvatarId): + messenger.send('whisperUpdateSCToontask', [taskId, + toNpcId, + toonProgress, + msgIndex, + whisperAvatarId]) + + def enterMainMenu(self): + self.chatInputNormal.setPos(self.normalPos) + self.chatInputNormal.reparentTo(base.a2dTopLeft) + if self.chatInputWhiteList.isActive(): + self.notify.debug('enterMainMenu calling checkObscured') + ChatManager.ChatManager.checkObscurred(self) + else: + ChatManager.ChatManager.enterMainMenu(self) + + def enterNoTrueFriends(self): + if self.noTrueFriends == None: + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.noTrueFriends = DirectFrame(parent=aspect2dp, pos=(0.0, 0.1, 0.2), relief=None, image=DGG.getDefaultDialogGeom(), image_color=OTPGlobals.GlobalDialogColor, image_scale=(1.4, 1.0, 1.1), text=OTPLocalizer.NoTrueFriends, text_wordwrap=20, textMayChange=0, text_scale=0.06, text_pos=(0, 0.3)) + DirectLabel(parent=self.noTrueFriends, relief=None, pos=(0, 0, 0.4), text=OTPLocalizer.NoTrueFriendsTitle, textMayChange=0, text_scale=0.08) + DirectButton(self.noTrueFriends, image=okButtonImage, relief=None, text=OTPLocalizer.NoTrueFriendsOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.4), command=self.__handleNoTrueFriendsOK) + buttons.removeNode() + self.noTrueFriends.show() + return + + def exitNoTrueFriends(self): + self.noTrueFriends.hide() + + def __normalButtonPressed(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: CHAT: Speedchat Plus') + messenger.send('wakeup') + if not base.cr.wantTypedChat(): + self.fsm.request('noSpeedchatPlus') + return + self.fsm.request('normalChat') + + def __scButtonPressed(self): + messenger.send('wakeup') + if self.fsm.getCurrentState().getName() == 'speedChat': + self.fsm.request('mainMenu') + else: + self.fsm.request('speedChat') + + def __whisperButtonPressed(self, avatarName, avatarId): + messenger.send('wakeup') + if not base.cr.wantTypedChat(): + self.fsm.request('noSpeedchatPlus') + return + if avatarId: + self.enterWhisperChat(avatarName, avatarId) + self.whisperFrame.hide() + + def enterNormalChat(self): + if not base.cr.wantTypedChat() or not base.localAvatar.getTutorialAck() or not ChatManager.ChatManager.enterNormalChat(self): + self.fsm.request('mainMenu') + + def enterWhisperChat(self, avatarName, avatarId): + if not base.cr.wantTypedChat(): + self.fsm.request('mainMenu') + return + result = ChatManager.ChatManager.enterWhisperChat(self, avatarName, avatarId) + self.chatInputNormal.setPos(self.whisperPos) + if result == None: + self.notify.warning('something went wrong in enterWhisperChat, falling back to main menu') + self.fsm.request('mainMenu') + + def enterNoSpeedchatPlus(self): + if self.noSpeedchatPlus == None: + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.noSpeedchatPlus = DirectFrame(parent=aspect2dp, pos=(0.0, 0.1, 0.05), relief=None, image=DGG.getDefaultDialogGeom(), image_color=OTPGlobals.GlobalDialogColor, image_scale=(1.4, 1.0, 1.58), text=OTPLocalizer.NoSpeedchatPlus, text_wordwrap=20, textMayChange=0, text_scale=0.06, text_pos=(0, 0.55)) + DirectLabel(parent=self.noSpeedchatPlus, relief=None, pos=(0, 0, 0.67), text=OTPLocalizer.NoSpeedchatPlusTitle, textMayChange=0, text_scale=0.08) + DirectButton(self.noSpeedchatPlus, image=okButtonImage, relief=None, text=OTPLocalizer.NoTrueFriendsOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.64), command=self.__handleNoTrueFriendsOK) + buttons.removeNode() + self.noSpeedchatPlus.show() + return + + def exitNoSpeedchatPlus(self): + self.noSpeedchatPlus.hide() + + def __whisperScButtonPressed(self, avatarName, avatarId): + messenger.send('wakeup') + if avatarId: + if self.fsm.getCurrentState().getName() == 'whisperSpeedChat': + self.fsm.request('whisper', [avatarName, avatarId]) + else: + self.fsm.request('whisperSpeedChat', [avatarId]) + + def __whisperCancelPressed(self): + self.fsm.request('mainMenu') + + def __handleNoTrueFriendsOK(self): + self.fsm.request('mainMenu') + + def messageSent(self): + pass diff --git a/toontown/chat/__init__.py b/toontown/chat/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/coderedemption/TTCodeRedemptionMgr.py b/toontown/coderedemption/TTCodeRedemptionMgr.py new file mode 100755 index 00000000..aa77db2d --- /dev/null +++ b/toontown/coderedemption/TTCodeRedemptionMgr.py @@ -0,0 +1,25 @@ +from direct.distributed.DistributedObject import DistributedObject +from direct.directnotify.DirectNotifyGlobal import directNotify + +class TTCodeRedemptionMgr(DistributedObject): + neverDisable = 1 + notify = directNotify.newCategory('TTCodeRedemptionMgr') + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + + def announceGenerate(self): + DistributedObject.announceGenerate(self) + base.cr.codeRedemptionMgr = self + + def delete(self): + if hasattr(base.cr, 'codeRedemptionMgr'): + del base.cr.codeRedemptionMgr + DistributedObject.delete(self) + + def redeemCode(self, code, callback): + self.callback = callback + self.sendUpdate('redeemCode', [code]) + + def redeemCodeResult(self, result): + self.callback(result) diff --git a/toontown/coderedemption/TTCodeRedemptionMgrAI.py b/toontown/coderedemption/TTCodeRedemptionMgrAI.py new file mode 100755 index 00000000..6a0befa8 --- /dev/null +++ b/toontown/coderedemption/TTCodeRedemptionMgrAI.py @@ -0,0 +1,118 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.catalog import CatalogAccessoryItem +from toontown.catalog import CatalogClothingItem +from toontown.catalog import CatalogNametagItem +from toontown.catalog import CatalogChatItem +from toontown.catalog import CatalogEmoteItem +from toontown.catalog import CatalogGardenItem +from toontown.catalog import CatalogGardenStarterItem +from toontown.catalog import CatalogMouldingItem +from toontown.catalog import CatalogRentalItem +from toontown.catalog import CatalogFurnitureItem +from toontown.catalog import CatalogAnimatedFurnitureItem +from toontown.catalog import CatalogFlooringItem +from toontown.catalog import CatalogPetTrickItem +from toontown.catalog import CatalogWainscotingItem +from toontown.catalog import CatalogToonStatueItem +from toontown.catalog import CatalogWallpaperItem +from toontown.catalog import CatalogWindowItem +from toontown.toonbase import ToontownGlobals +from datetime import datetime, timedelta +import time + +""" +Code example: + +'codeName': { + 'items': [ + CatalogTypeItem.CatalogTypeItem(arguments) + ], + 'expirationDate': datetime(2020, 1, 30), + 'month': 1, + 'day': 30, + 'year': 2000' +} + +Expiration date, month, day and year are optional fields. + +If you for some reason are not familiar with arrays or lists, you +only include the comma if there are multiple arguments. +""" + +class TTCodeRedemptionMgrAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("TTCodeRedemptionMgrAI") + codes = { + 'gardening': { + 'items': [ + CatalogGardenStarterItem.CatalogGardenStarterItem() + ] + }, + 'sillymeter': { + 'items': [ + CatalogClothingItem.CatalogClothingItem(1753, 0) + ] + }, + 'toonstatue': { + 'items': [ + CatalogToonStatueItem.CatalogToonStatueItem(105, endPoseIndex=108) + ] + }, + 'donaldstatue': { + 'items': [ + CatalogGardenItem.CatalogGardenItem(100, 1) + ] + } + } + + def announceGenerate(self): + DistributedObjectAI.announceGenerate(self) + + def redeemCode(self, code): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + + if not av: + return + + code = code.lower() + + if code in self.codes: + if av.isCodeRedeemed(code): + self.sendUpdateToAvatarId(avId, 'redeemCodeResult', [4]) + self.air.writeServerEvent('suspicious', avId, 'Toon tried to redeem already redeemed code %s' % code) + return + + codeInfo = self.codes[code] + date = datetime.now() + + if ('year' in codeInfo and date.year is not codeInfo['year']) and date.year > codeInfo['year'] or ('expirationDate' in codeInfo and codeInfo['expirationDate'] - date < timedelta(hours = 1)): + self.sendUpdateToAvatarId(avId, 'redeemCodeResult', [2]) + self.air.writeServerEvent('suspicious', avId, 'Toon attempted to redeem code %s but it was expired!' % code) + return + elif ('year' in codeInfo and date.year is not codeInfo['year']) and date.year < codeInfo['year'] or ('month' in codeInfo and date.month is not codeInfo['month']) or ('day' in codeInfo and date.day is not codeInfo['day']): + self.sendUpdateToAvatarId(avId, 'redeemCodeResult', [5]) + self.air.writeServerEvent('suspicious', avId, "Toon attempted to redeem code %s but it wasn't usable yet!" % code) + return + + av.redeemCode(code) + self.requestCodeRedeem(avId, av, codeInfo['items']) + self.air.writeServerEvent('code-redeemed', avId, 'Toon successfully redeemed %s' % code) + else: + self.sendUpdateToAvatarId(avId, 'redeemCodeResult', [1]) + self.air.writeServerEvent('suspicious', avId, 'Toon tried to redeem non-existent code %s' % code) + + def requestCodeRedeem(self, avId, av, items): + if len(av.mailboxContents) + len(av.onOrder) + len(av.onGiftOrder) + len(items) >= ToontownGlobals.MaxMailboxContents: + self.sendUpdateToAvatarId(avId, 'redeemCodeResult', [3]) + return + + for item in items: + if item in av.onOrder: + continue + + av.addToDeliverySchedule(item) + + av.b_setDeliverySchedule(av.onOrder) + self.sendUpdateToAvatarId(avId, 'redeemCodeResult', [0]) + self.air.writeServerEvent('code-redeemed', avId, 'Toon is being sent %s from redeemed code' % items) diff --git a/toontown/coderedemption/__init__.py b/toontown/coderedemption/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/cogdominium/CogdoBarrelRoom.py b/toontown/cogdominium/CogdoBarrelRoom.py new file mode 100755 index 00000000..2d1ab2d1 --- /dev/null +++ b/toontown/cogdominium/CogdoBarrelRoom.py @@ -0,0 +1,178 @@ +import random +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals, ToontownTimer +from toontown.cogdominium import CogdoBarrelRoomConsts, CogdoBarrelRoomRewardPanel +from toontown.distributed import DelayDelete + +class CogdoBarrelRoom: + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogdoBarrelRoom') + + def __init__(self): + self.timer = None + self.model = None + self._isLoaded = False + self.dummyElevInNode = None + self.cogdoBarrelsNode = None + self.entranceNode = None + self.nearBattleNode = None + self.rewardUi = None + self.rewardUiTaskName = 'CogdoBarrelRoom-RewardUI' + self.rewardCameraTaskName = 'CogdoBarrelRoom-RewardCamera' + self.fog = None + self.defaultFar = None + self.stomperSfx = None + + def destroy(self): + self.unload() + + def load(self): + if self._isLoaded: + return + self.timer = ToontownTimer.ToontownTimer() + self.timer.stash() + self.model = loader.loadModel(CogdoBarrelRoomConsts.BarrelRoomModel) + self.model.setPos(*CogdoBarrelRoomConsts.BarrelRoomModelPos) + self.model.reparentTo(render) + self.model.stash() + self.entranceNode = self.model.attachNewNode('door-entrance') + self.entranceNode.setPos(0, -65, 0) + self.nearBattleNode = self.model.attachNewNode('near-battle') + self.nearBattleNode.setPos(0, -25, 0) + self.rewardUi = CogdoBarrelRoomRewardPanel.CogdoBarrelRoomRewardPanel() + self.hideRewardUi() + self.stomperSfx = base.loadSfx(CogdoBarrelRoomConsts.StomperSound) + self.fog = Fog('barrel-room-fog') + self.fog.setColor(CogdoBarrelRoomConsts.BarrelRoomFogColor) + self.fog.setLinearRange(*CogdoBarrelRoomConsts.BarrelRoomFogLinearRange) + self.brBarrel = render.attachNewNode('@@CogdoBarrels') + for i in xrange(len(CogdoBarrelRoomConsts.BarrelProps)): + self.bPath = self.brBarrel.attachNewNode('%s%s'% (CogdoBarrelRoomConsts.BarrelPathName, i)) + self.bPath.setPos(CogdoBarrelRoomConsts.BarrelProps[i]['pos']) + self.bPath.setH(CogdoBarrelRoomConsts.BarrelProps[i]['heading']) + self._isLoaded = True + + def unload(self): + if self.model: + self.model.removeNode() + self.model = None + if self.timer: + self.timer.destroy() + self.timer = None + if self.rewardUi: + self.rewardUi.destroy() + self.rewardUi = None + if hasattr(self, 'fog'): + if self.fog: + render.setFogOff() + del self.fog + taskMgr.remove(self.rewardUiTaskName) + taskMgr.remove(self.rewardCameraTaskName) + self._isLoaded = False + + def isLoaded(self): + return self._isLoaded + + def show(self): + if not self.cogdoBarrelsNode: + self.cogdoBarrelsNode = render.find('**/@@CogdoBarrels') + if not self.cogdoBarrelsNode.isEmpty(): + self.cogdoBarrelsNode.reparentTo(self.model) + self.cogdoBarrelsNode.unstash() + base.localAvatar.b_setAnimState('neutral') + self.defaultFar = base.camLens.getFar() + base.camLens.setFar(CogdoBarrelRoomConsts.BarrelRoomCameraFar) + base.camLens.setMinFov(settings['fov']/(4./3.)) + self.showBattleAreaLight(True) + render.setFog(self.fog) + self.model.unstash() + + def hide(self): + self.model.stash() + if self.defaultFar is not None: + base.camLens.setFar(self.defaultFar) + return + + def activate(self): + self.notify.info('Activating barrel room: %d sec timer.' % CogdoBarrelRoomConsts.CollectionTime) + self.timer.unstash() + self.timer.posAboveShtikerBook() + self.timer.countdown(CogdoBarrelRoomConsts.CollectionTime) + base.cr.playGame.getPlace().fsm.request('walk') + + def deactivate(self): + self.notify.info('Deactivating barrel room.') + self.timer.stop() + self.timer.stash() + + def placeToonsAtEntrance(self, toons): + for i in xrange(len(toons)): + toons[i].setPosHpr(self.entranceNode, *CogdoBarrelRoomConsts.BarrelRoomPlayerSpawnPoints[i]) + + def placeToonsNearBattle(self, toons): + for i in xrange(len(toons)): + toons[i].setPosHpr(self.nearBattleNode, *CogdoBarrelRoomConsts.BarrelRoomPlayerSpawnPoints[i]) + + def showBattleAreaLight(self, visible = True): + lightConeNode = self.model.find('**/battleCone') + if lightConeNode != None and not lightConeNode.isEmpty(): + if visible: + lightConeNode.show() + else: + lightConeNode.hide() + return + + def getIntroInterval(self): + avatar = base.localAvatar + trackName = '__introBarrelRoom-%d' % avatar.doId + track = Parallel(name=trackName) + track.append(self.__stomperIntervals()) + track.append(Sequence(Func(camera.reparentTo, render), Func(camera.setPosHpr, self.model, -20.0, -87.9, 12.0, -30, 0, 0), Func(base.transitions.irisIn, 0.5), Wait(1.0), LerpHprInterval(camera, duration=2.0, startHpr=Vec3(-30, 0, 0), hpr=Vec3(0, 0, 0), blendType='easeInOut'), Wait(2.5), LerpHprInterval(camera, duration=3.0, startHpr=Vec3(0, 0, 0), hpr=Vec3(-45, 0, 0), blendType='easeInOut'), Wait(2.5))) + track.delayDelete = DelayDelete.DelayDelete(avatar, 'introBarrelRoomTrack') + track.setDoneEvent(trackName) + return (track, trackName) + + def __stomperIntervals(self): + ivals = [SoundInterval(self.stomperSfx)] + i = 0 + for stomperDef in CogdoBarrelRoomConsts.StomperProps: + stomperNode = render.find(stomperDef['path']) + if stomperNode: + maxZ = random.uniform(10, 20) + minZ = maxZ - 10 + if stomperDef['motion'] == 'up': + startZ, destZ = minZ, maxZ + else: + startZ, destZ = maxZ, minZ + stomperNode.setPos(Point3(0, 0, startZ)) + ivals.append(LerpPosInterval(stomperNode, CogdoBarrelRoomConsts.StomperHaltTime, Point3(0, 0, destZ), blendType='easeOut')) + i += 1 + + return Parallel(*tuple(ivals)) + + def __rewardUiTimeout(self, callback): + self.hideRewardUi() + if callback is not None: + callback() + return + + def __rewardCamera(self): + trackName = 'cogdoBarrelRoom-RewardCamera' + track = Sequence(Func(camera.reparentTo, render), Func(camera.setPosHpr, self.model, 0, 0, 11.0, 0, -14, 0), Func(self.showBattleAreaLight, False), name=trackName) + return (track, trackName) + + def showRewardUi(self, callback = None): + track, trackName = self.__rewardCamera() + if CogdoBarrelRoomConsts.ShowRewardUI: + self.rewardUi.setRewards() + self.rewardUi.unstash() + taskMgr.doMethodLater(CogdoBarrelRoomConsts.RewardUiTime, self.__rewardUiTimeout, self.rewardUiTaskName, extraArgs=[callback]) + return (track, trackName) + + def setRewardResults(self, results): + self.rewardUi.setRewards(results) + + def hideRewardUi(self): + self.rewardUi.stash() + taskMgr.remove(self.rewardUiTaskName) diff --git a/toontown/cogdominium/CogdoBarrelRoomConsts.py b/toontown/cogdominium/CogdoBarrelRoomConsts.py new file mode 100755 index 00000000..32c77eab --- /dev/null +++ b/toontown/cogdominium/CogdoBarrelRoomConsts.py @@ -0,0 +1,101 @@ +from panda3d.core import * +CollectionTime = 30 +BarrelRoomIntroTimeout = 12.0 +RewardUiTime = 5.0 +EndWithAllBarrelsCollected = True +ShowRewardUI = True +AllBarrelsCollectedTime = 5.0 +ToonUp = (2, 4) +BarrelProps = [{'pos': (-10, -66, 0), + 'heading': 9}, + {'pos': (-7.8, -54.5, 0), + 'heading': 12}, + {'pos': (-10.5, -44, 0), + 'heading': 166}, + {'pos': (-8.9, -33.5, 0), + 'heading': 142}, + {'pos': (-9.6, -19.8, 0), + 'heading': 94}, + {'pos': (7.8, -63.9, 0), + 'heading': 169}, + {'pos': (10, -44.5, 0), + 'heading': 120}, + {'pos': (7.4, -36.6, 0), + 'heading': 127}, + {'pos': (10.6, -27.5, 0), + 'heading': 141}, + {'pos': (10, -14.4, 0), + 'heading': 2}] +StomperProps = [{'path': '**/stomper_GRP_01/stomper_cylinder_034', + 'motion': 'up'}, + {'path': '**/stomper_GRP_02/stomper_cylinder_034', + 'motion': 'down'}, + {'path': '**/stomper_GRP_03/stomper_cylinder_034', + 'motion': 'up'}, + {'path': '**/stomper_GRP_04/stomper_cylinder_034', + 'motion': 'up'}, + {'path': '**/stomper_GRP_05/stomper_cylinder_034', + 'motion': 'down'}, + {'path': '**/stomper_GRP_06/stomper_cylinder_034', + 'motion': 'up'}, + {'path': '**/stomper_GRP_07/stomper_cylinder_034', + 'motion': 'down'}, + {'path': '**/stomper_GRP_08/stomper_cylinder_034', + 'motion': 'up'}, + {'path': '**/stomper_GRP_09/stomper_cylinder_034', + 'motion': 'up'}, + {'path': '**/stomper_GRP_10/stomper_cylinder_034', + 'motion': 'down'}, + {'path': '**/stomper_GRP_11/stomper_cylinder_034', + 'motion': 'up'}, + {'path': '**/stomper_GRP_12/stomper_cylinder_034', + 'motion': 'up'}] +StomperHaltTime = 7.3 +StomperSound = 'phase_9/audio/sfx/CHQ_FACT_stomper_raise.ogg' +MaxToons = 4 +BarrelRoomModel = 'phase_5/models/cogdominium/tt_m_ara_cbr_barrelRoom' +BarrelRoomModelPos = (0, 0, 0) +BarrelRoomElevatorInPath = '**/elevatorIn_locator' +BarrelRoomElevatorOutPath = '**/elevatorOut_locator' +BarrelRoomPlayerSpawnPoints = [(-4, + 0, + 0, + 0, + 0, + 0), + (-2, + 0, + 0, + 0, + 0, + 0), + (0, + 0, + 0, + 0, + 0, + 0), + (2, + 0, + 0, + 0, + 0, + 0)] +BarrelRoomCameraFar = 525.0 +BarrelRoomFogColor = Vec4(0.65, 0.21, 0, 1.0) +BarrelRoomFogLinearRange = (0.0, 800.0) +BarrelPathName = 'CogdoBarrel_' +BarrelModel = 'phase_5/models/cogdominium/tt_m_ara_cbr_laughBarrel' +BarrelModelScale = 1.0 +BarrelCollParams = (0, + 0, + 2, + 2.0) +BarrelBumpSound = 'phase_4/audio/sfx/Golf_Hit_Barrier_2.ogg' +BarrelGrabSound = 'phase_4/audio/sfx/SZ_DD_treasure.ogg' +BarrelAvailableTexture = 'phase_5/maps/tt_t_ara_cbr_Barrel_notUsed.jpg' +BarrelUsedTexture = 'phase_5/maps/tt_t_ara_cbr_Barrel_Used.jpg' +StateHidden, StateAvailable, StateUsed, StateCrushed = range(4) + +def numBarrels(): + return len(BarrelProps) diff --git a/toontown/cogdominium/CogdoBarrelRoomMovies.py b/toontown/cogdominium/CogdoBarrelRoomMovies.py new file mode 100755 index 00000000..651993b9 --- /dev/null +++ b/toontown/cogdominium/CogdoBarrelRoomMovies.py @@ -0,0 +1,119 @@ +from pandac.PandaModules import NodePath, Point3, PlaneNode, TextNode +from direct.interval.IntervalGlobal import * +from direct.showbase.ShowBase import Plane +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.RandomNumGen import RandomNumGen +from direct.interval.MetaInterval import Sequence, Parallel +from direct.interval.FunctionInterval import Func, Wait +from direct.gui.DirectGui import * +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase import TTLocalizer +from toontown.suit import Suit, SuitDNA +from toontown.toon import Toon, ToonHead, ToonDNA +from DistributedCogdoInterior import * +from CogdoUtil import CogdoGameMovie +import CogdoBarrelRoomConsts, CogdoUtil + +class CogdoBarrelRoomIntro(CogdoGameMovie): + notify = DirectNotifyGlobal.directNotify.newCategory('CogdoElevatorMovie') + + def __init__(self): + CogdoGameMovie.__init__(self) + self._toonDialogueSfx = None + self.toonHead = None + self.frame = None + return + + def displayLine(self, text): + self.notify.debug('displayLine') + self._dialogueLabel.node().setText(text) + self._dialogueLabel.setPos(0.32, 0, -0.72) + self.toonHead.reparentTo(aspect2d) + self._toonDialogueSfx.play() + self.toonHead.setClipPlane(self.clipPlane) + + def makeSuit(self, suitType): + self.notify.debug('makeSuit()') + suit = Suit.Suit() + dna = SuitDNA.SuitDNA() + dna.newSuit(suitType) + suit.setStyle(dna) + suit.isDisguised = 1 + suit.generateSuit() + suit.setScale(1.05, 1.05, 2.05) + suit.setPos(0, 0, -4.4) + suit.reparentTo(self.toonHead) + for part in suit.getHeadParts(): + part.hide() + + suit.loop('neutral') + + def load(self): + self.notify.debug('load()') + CogdoGameMovie.load(self) + backgroundGui = loader.loadModel('phase_5/models/cogdominium/tt_m_gui_csa_flyThru') + self.bg = backgroundGui.find('**/background') + self.chatBubble = backgroundGui.find('**/chatBubble') + self.chatBubble.setScale(6.5, 6.5, 7.3) + self.chatBubble.setPos(0.32, 0, -0.78) + self.bg.setScale(5.2) + self.bg.setPos(0.14, 0, -0.6667) + self.bg.reparentTo(aspect2d) + self.chatBubble.reparentTo(aspect2d) + self.frame = DirectFrame(geom=self.bg, relief=None, pos=(0.2, 0, -0.6667)) + self.bg.wrtReparentTo(self.frame) + self.gameTitleText = DirectLabel(parent=self.frame, text=TTLocalizer.CogdoBarrelRoomTitle, scale=TTLocalizer.MRPgameTitleText * 0.8, text_align=TextNode.ACenter, text_font=getSignFont(), text_fg=(1.0, 0.33, 0.33, 1.0), pos=TTLocalizer.MRgameTitleTextPos, relief=None) + self.chatBubble.wrtReparentTo(self.frame) + self.frame.hide() + backgroundGui.removeNode() + self.toonDNA = ToonDNA.ToonDNA() + self.toonDNA.newToonFromProperties('dss', 'ss', 'm', 'm', 2, 0, 2, 2, 1, 8, 1, 8, 1, 14) + self.toonHead = Toon.Toon() + self.toonHead.setDNA(self.toonDNA) + self.makeSuit('sc') + self.toonHead.getGeomNode().setDepthWrite(1) + self.toonHead.getGeomNode().setDepthTest(1) + self.toonHead.loop('neutral') + self.toonHead.setPosHprScale(-0.73, 0, -1.27, 180, 0, 0, 0.18, 0.18, 0.18) + self.toonHead.reparentTo(hidden) + self.toonHead.startBlink() + self.clipPlane = self.toonHead.attachNewNode(PlaneNode('clip')) + self.clipPlane.node().setPlane(Plane(0, 0, 1, 0)) + self.clipPlane.setPos(0, 0, 2.45) + self._toonDialogueSfx = loader.loadSfx('phase_3.5/audio/dial/AV_dog_long.ogg') + self._camHelperNode = NodePath('CamHelperNode') + self._camHelperNode.reparentTo(render) + dialogue = TTLocalizer.CogdoBarrelRoomIntroDialog + + def start(): + self.frame.show() + base.setCellsAvailable(base.bottomCells + base.leftCells + base.rightCells, 0) + + def end(): + self._dialogueLabel.reparentTo(hidden) + self.toonHead.reparentTo(hidden) + self.frame.hide() + base.setCellsAvailable(base.bottomCells + base.leftCells + base.rightCells, 1) + self._stopUpdateTask() + + self._ival = Sequence(Func(start), Func(self.displayLine, dialogue), Wait(CogdoBarrelRoomConsts.BarrelRoomIntroTimeout), Func(end)) + self._startUpdateTask() + return + + def _updateTask(self, task): + dt = globalClock.getDt() + return task.cont + + def unload(self): + self.frame.destroy() + del self.frame + self.bg.removeNode() + del self.bg + self.chatBubble.removeNode() + del self.chatBubble + self.toonHead.stopBlink() + self.toonHead.stop() + self.toonHead.removeNode() + self.toonHead.delete() + del self.toonHead + CogdoGameMovie.unload(self) diff --git a/toontown/cogdominium/CogdoBarrelRoomRewardPanel.py b/toontown/cogdominium/CogdoBarrelRoomRewardPanel.py new file mode 100755 index 00000000..dac75726 --- /dev/null +++ b/toontown/cogdominium/CogdoBarrelRoomRewardPanel.py @@ -0,0 +1,37 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from toontown.toon import DistributedToon +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.cogdominium import CogdoBarrelRoomConsts + +class CogdoBarrelRoomRewardPanel(DirectFrame): + + def __init__(self): + DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=TTLocalizer.RPdirectFrame, pos=(0, 0, -0.587)) + self.initialiseoptions(CogdoBarrelRoomRewardPanel) + self.avNameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.3), text='Toon Ups', text_scale=0.08) + self.rewardLines = [] + for i in xrange(CogdoBarrelRoomConsts.MaxToons): + rewardLine = {} + rewardLine['frame'] = DirectFrame(parent=self, relief=None, frameSize=(-0.5, + 0.5, + -0.045, + 0.042), pos=(0, 0, 0.1 + -0.09 * i)) + rewardLine['name'] = DirectLabel(parent=rewardLine['frame'], relief=None, text='', text_scale=TTLocalizer.RPtrackLabels, text_align=TextNode.ALeft, pos=(-0.4, 0, 0), text_pos=(0, -0.02)) + rewardLine['laff'] = DirectLabel(parent=rewardLine['frame'], relief=None, text='', text_scale=0.05, text_align=TextNode.ARight, pos=(0.4, 0, 0), text_pos=(0, -0.02)) + self.rewardLines.append(rewardLine) + + return + + def setRewards(self): + RewardLineIndex = 0 + for doId in base.cr.doId2do: + toon = base.cr.doId2do.get(doId) + if isinstance(toon, DistributedToon.DistributedToon): + self.rewardLines[RewardLineIndex]['name'].setProp('text', toon.getName()) + self.rewardLines[RewardLineIndex]['laff'].setProp('text', '%s/%s' % (str(toon.hp), str(toon.maxHp))) + if doId == base.localAvatar.getDoId(): + self.rewardLines[RewardLineIndex]['frame'].setProp('relief', DGG.RIDGE) + self.rewardLines[RewardLineIndex]['frame'].setProp('borderWidth', (0.01, 0.01)) + self.rewardLines[RewardLineIndex]['frame'].setProp('frameColor', (1, 1, 1, 0.5)) + RewardLineIndex += 1 diff --git a/toontown/cogdominium/CogdoBoardroomGameBase.py b/toontown/cogdominium/CogdoBoardroomGameBase.py new file mode 100755 index 00000000..1c62c647 --- /dev/null +++ b/toontown/cogdominium/CogdoBoardroomGameBase.py @@ -0,0 +1,10 @@ +from toontown.cogdominium import CogdoBoardroomGameSpec +from toontown.cogdominium import CogdoBoardroomGameConsts as Consts + +class CogdoBoardroomGameBase: + + def getConsts(self): + return Consts + + def getSpec(self): + return CogdoBoardroomGameSpec diff --git a/toontown/cogdominium/CogdoBoardroomGameConsts.py b/toontown/cogdominium/CogdoBoardroomGameConsts.py new file mode 100755 index 00000000..7e09e9b1 --- /dev/null +++ b/toontown/cogdominium/CogdoBoardroomGameConsts.py @@ -0,0 +1,6 @@ +from direct.fsm.StatePush import StateVar +from otp.level.EntityStateVarSet import EntityStateVarSet +from toontown.cogdominium.CogdoEntityTypes import CogdoBoardroomGameSettings +Settings = EntityStateVarSet(CogdoBoardroomGameSettings) +GameDuration = StateVar(60.0) +FinishDuration = StateVar(10.0) diff --git a/toontown/cogdominium/CogdoBoardroomGameSpec.py b/toontown/cogdominium/CogdoBoardroomGameSpec.py new file mode 100755 index 00000000..4e785d87 --- /dev/null +++ b/toontown/cogdominium/CogdoBoardroomGameSpec.py @@ -0,0 +1,21 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'modelFilename': 'phase_10/models/cogHQ/EndVault.bam'}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10000: {'type': 'cogdoBoardroomGameSettings', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'TimerScale': 0.5028702719683693}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/cogdominium/CogdoCraneGame.py b/toontown/cogdominium/CogdoCraneGame.py new file mode 100755 index 00000000..70e06f19 --- /dev/null +++ b/toontown/cogdominium/CogdoCraneGame.py @@ -0,0 +1,74 @@ +import CogdoCraneGameConsts as Globals +from CogdoGameAudioManager import CogdoGameAudioManager + +class CogdoCraneGame(DirectObject): + notify = directNotify.newCategory('CogdoFlyingGame') + + def __init__(self, distGame): + self.distGame = distGame + self.toonId2Player = {} + self.players = [] + + def _initAudio(self): + self._audioMgr = CogdoGameAudioManager(Globals.MusicFiles, Globals.SfxFiles, camera, cutoff=Globals.AudioCutoff) + + def _destroyAudio(self): + self._audioMgr.destroy() + del self._audioMgr + + def load(self): + self._initAudio() + + def unload(self): + self._destroyAudio() + del self.toonId2Player + + def onstage(self): + pass + + def offstage(self): + pass + + def startIntro(self): + self._audioMgr.playMusic('normal') + + def endIntro(self): + for player in self.players: + self.placePlayer(player) + if player.toon is localAvatar: + localAvatar.sendCurrentPosition() + player.request('Ready') + + def startFinish(self): + pass + + def endFinish(self): + pass + + def start(self): + for player in self.players: + player.handleGameStart() + player.request('Normal') + + def exit(self): + for player in self.players: + player.request('Done') + + def _addPlayer(self, player): + self.players.append(player) + self.toonId2Player[player.toon.doId] = player + + def _removePlayer(self, player): + if player in self.players: + self.players.remove(player) + else: + for cPlayer in self.players: + if cPlayer.toon == player.toon: + self.players.remove(cPlayer) + break + + if player.toon.doId in self.toonId2Player: + del self.toonId2Player[player.toon.doId] + + def handleToonLeft(self, toonId): + self._removePlayer(self.toonId2Player[toonId]) diff --git a/toontown/cogdominium/CogdoCraneGameBase.py b/toontown/cogdominium/CogdoCraneGameBase.py new file mode 100755 index 00000000..095643aa --- /dev/null +++ b/toontown/cogdominium/CogdoCraneGameBase.py @@ -0,0 +1,10 @@ +from toontown.cogdominium import CogdoCraneGameSpec +from toontown.cogdominium import CogdoCraneGameConsts as Consts + +class CogdoCraneGameBase: + + def getConsts(self): + return Consts + + def getSpec(self): + return CogdoCraneGameSpec diff --git a/toontown/cogdominium/CogdoCraneGameConsts.py b/toontown/cogdominium/CogdoCraneGameConsts.py new file mode 100755 index 00000000..3f75ac51 --- /dev/null +++ b/toontown/cogdominium/CogdoCraneGameConsts.py @@ -0,0 +1,67 @@ +from direct.fsm.StatePush import StateVar +from otp.level.EntityStateVarSet import EntityStateVarSet +from CogdoUtil import VariableContainer +from toontown.cogdominium.CogdoEntityTypes import CogdoCraneGameSettings, CogdoCraneCogSettings +Gameplay = VariableContainer() +Gameplay.SecondsUntilGameOver = 60.0 * 3.0 +Gameplay.TimeRunningOutSeconds = 45.0 +Audio = VariableContainer() +Audio.MusicFiles = {'normal': 'phase_9/audio/bgm/CHQ_FACT_bg.ogg', + 'end': 'phase_7/audio/bgm/encntr_toon_winning_indoor.ogg', + 'timeRunningOut': 'phase_7/audio/bgm/encntr_suit_winning_indoor.ogg'} +Settings = EntityStateVarSet(CogdoCraneGameSettings) +CogSettings = EntityStateVarSet(CogdoCraneCogSettings) +CranePosHprs = [(13.4, -136.6, 6, -45, 0, 0), + (13.4, -91.4, 6, -135, 0, 0), + (58.6, -91.4, 6, 135, 0, 0), + (58.6, -136.6, 6, 45, 0, 0)] +MoneyBagPosHprs = [[77.2 - 84, + -329.3 + 201, + 0, + -90, + 0, + 0], + [77.1 - 84, + -302.7 + 201, + 0, + -90, + 0, + 0], + [165.7 - 84, + -326.4 + 201, + 0, + 90, + 0, + 0], + [165.5 - 84, + -302.4 + 201, + 0, + 90, + 0, + 0], + [107.8 - 84, + -359.1 + 201, + 0, + 0, + 0, + 0], + [133.9 - 84, + -359.1 + 201, + 0, + 0, + 0, + 0], + [107.0 - 84, + -274.7 + 201, + 0, + 180, + 0, + 0], + [134.2 - 84, + -274.7 + 201, + 0, + 180, + 0, + 0]] +for i in xrange(len(MoneyBagPosHprs)): + MoneyBagPosHprs[i][2] += 6 diff --git a/toontown/cogdominium/CogdoCraneGameSpec.py b/toontown/cogdominium/CogdoCraneGameSpec.py new file mode 100755 index 00000000..f124ee0e --- /dev/null +++ b/toontown/cogdominium/CogdoCraneGameSpec.py @@ -0,0 +1,35 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'modelFilename': 'phase_10/models/cogHQ/EndVault.bam'}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10001: {'type': 'cogdoCraneCogSettings', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'CogFlyAwayDuration': 4.0, + 'CogFlyAwayHeight': 50.0, + 'CogMachineInteractDuration': 2.0, + 'CogSpawnPeriod': 10.0, + 'CogWalkSpeed': 12.07161265369133}, + 10000: {'type': 'cogdoCraneGameSettings', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'EmptyFrictionCoef': 0.1, + 'GameDuration': 180.0, + 'Gravity': -32, + 'MagnetMass': 1.0, + 'MoneyBagGrabHeight': -4.1, + 'RopeLinkMass': 1.0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/cogdominium/CogdoElevatorMovie.py b/toontown/cogdominium/CogdoElevatorMovie.py new file mode 100755 index 00000000..63a04afa --- /dev/null +++ b/toontown/cogdominium/CogdoElevatorMovie.py @@ -0,0 +1,119 @@ +from pandac.PandaModules import NodePath, Point3, PlaneNode, TextNode +from direct.interval.IntervalGlobal import * +from direct.showbase.ShowBase import Plane +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.RandomNumGen import RandomNumGen +from direct.interval.MetaInterval import Sequence, Parallel +from direct.interval.FunctionInterval import Func, Wait +from direct.gui.DirectGui import * +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase import TTLocalizer +from toontown.suit import Suit, SuitDNA +from toontown.toon import Toon, ToonHead, ToonDNA +from DistributedCogdoInterior import * +from CogdoUtil import CogdoGameMovie +import CogdoUtil + +class CogdoElevatorMovie(CogdoGameMovie): + notify = DirectNotifyGlobal.directNotify.newCategory('CogdoElevatorMovie') + elevatorDuration = 5 + + def __init__(self): + CogdoGameMovie.__init__(self) + self._toonDialogueSfx = None + self.toonHead = None + self.frame = None + return + + def displayLine(self, text): + self.notify.debug('displayLine') + self._dialogueLabel.node().setText(text) + self.toonHead.reparentTo(aspect2d) + self._toonDialogueSfx.play() + self.toonHead.setClipPlane(self.clipPlane) + + def makeSuit(self, suitType): + self.notify.debug('makeSuit()') + suit = Suit.Suit() + dna = SuitDNA.SuitDNA() + dna.newSuit(suitType) + suit.setStyle(dna) + suit.isDisguised = 1 + suit.generateSuit() + suit.setScale(1.05, 1.05, 2.05) + suit.setPos(0, 0, -4.4) + suit.reparentTo(self.toonHead) + for part in suit.getHeadParts(): + part.hide() + + suit.loop('neutral') + + def load(self): + self.notify.debug('load()') + CogdoGameMovie.load(self) + backgroundGui = loader.loadModel('phase_5/models/cogdominium/tt_m_gui_csa_flyThru') + self.bg = backgroundGui.find('**/background') + self.chatBubble = backgroundGui.find('**/chatBubble') + self.chatBubble.setScale(6.5, 6.5, 7.3) + self.chatBubble.setPos(0.32, 0, -0.78) + self.bg.setScale(5.2) + self.bg.setPos(0.14, 0, -0.6667) + self.bg.reparentTo(aspect2d) + self.chatBubble.reparentTo(aspect2d) + self.frame = DirectFrame(geom=self.bg, relief=None, pos=(0.2, 0, -0.6667)) + self.bg.wrtReparentTo(self.frame) + self.gameTitleText = DirectLabel(parent=self.frame, text=TTLocalizer.CogdoExecutiveSuiteTitle, scale=TTLocalizer.MRPgameTitleText * 0.8, text_align=TextNode.ACenter, text_font=getSignFont(), text_fg=(1.0, 0.33, 0.33, 1.0), pos=TTLocalizer.MRgameTitleTextPos, relief=None) + self.chatBubble.wrtReparentTo(self.frame) + self.frame.hide() + backgroundGui.removeNode() + self.toonDNA = ToonDNA.ToonDNA() + self.toonDNA.newToonFromProperties('dss', 'ss', 'm', 'm', 2, 0, 2, 2, 1, 8, 1, 8, 1, 14) + self.toonHead = Toon.Toon() + self.toonHead.setDNA(self.toonDNA) + self.makeSuit('sc') + self.toonHead.getGeomNode().setDepthWrite(1) + self.toonHead.getGeomNode().setDepthTest(1) + self.toonHead.loop('neutral') + self.toonHead.setPosHprScale(-0.73, 0, -1.27, 180, 0, 0, 0.18, 0.18, 0.18) + self.toonHead.reparentTo(hidden) + self.toonHead.startBlink() + self.clipPlane = self.toonHead.attachNewNode(PlaneNode('clip')) + self.clipPlane.node().setPlane(Plane(0, 0, 1, 0)) + self.clipPlane.setPos(0, 0, 2.45) + self._toonDialogueSfx = loader.loadSfx('phase_3.5/audio/dial/AV_dog_long.ogg') + self._camHelperNode = NodePath('CamHelperNode') + self._camHelperNode.reparentTo(render) + dialogue = TTLocalizer.CogdoMazeGameElevatorRewardLaff + + def start(): + self.frame.show() + base.setCellsAvailable(base.bottomCells + base.leftCells + base.rightCells, 0) + + def end(): + self._dialogueLabel.reparentTo(hidden) + self.toonHead.reparentTo(hidden) + self.frame.hide() + base.setCellsAvailable(base.bottomCells + base.leftCells + base.rightCells, 1) + self._stopUpdateTask() + + self._ival = Sequence(Func(start), Func(self.displayLine, dialogue), Wait(self.elevatorDuration), Func(end)) + self._startUpdateTask() + return + + def _updateTask(self, task): + dt = globalClock.getDt() + return task.cont + + def unload(self): + self.frame.destroy() + del self.frame + self.bg.removeNode() + del self.bg + self.chatBubble.removeNode() + del self.chatBubble + self.toonHead.stopBlink() + self.toonHead.stop() + self.toonHead.removeNode() + self.toonHead.delete() + del self.toonHead + CogdoGameMovie.unload(self) diff --git a/toontown/cogdominium/CogdoEntityCreator.py b/toontown/cogdominium/CogdoEntityCreator.py new file mode 100755 index 00000000..c9d37159 --- /dev/null +++ b/toontown/cogdominium/CogdoEntityCreator.py @@ -0,0 +1,20 @@ +from otp.level import EntityCreator +from toontown.cogdominium import CogdoCraneGameConsts +from toontown.cogdominium.CogdoLevelMgr import CogdoLevelMgr +from toontown.cogdominium import CogdoBoardroomGameConsts +from toontown.cogdominium import CogdoCraneGameConsts + +class CogdoEntityCreator(EntityCreator.EntityCreator): + + def __init__(self, level): + EntityCreator.EntityCreator.__init__(self, level) + nothing = EntityCreator.nothing + nonlocal = EntityCreator.nonlocal + self.privRegisterTypes({'levelMgr': CogdoLevelMgr, + 'cogdoBoardroomGameSettings': Functor(self._createCogdoSettings, CogdoBoardroomGameConsts.Settings), + 'cogdoCraneGameSettings': Functor(self._createCogdoSettings, CogdoCraneGameConsts.Settings), + 'cogdoCraneCogSettings': Functor(self._createCogdoSettings, CogdoCraneGameConsts.CogSettings)}) + + def _createCogdoSettings(self, ent, level, entId): + ent.initializeEntity(level, entId) + return ent diff --git a/toontown/cogdominium/CogdoEntityCreatorAI.py b/toontown/cogdominium/CogdoEntityCreatorAI.py new file mode 100755 index 00000000..fb8ffe01 --- /dev/null +++ b/toontown/cogdominium/CogdoEntityCreatorAI.py @@ -0,0 +1,18 @@ +from direct.showbase.PythonUtil import Functor +from otp.level import EntityCreatorAI +from toontown.cogdominium.CogdoLevelMgrAI import CogdoLevelMgrAI +from toontown.cogdominium import CogdoCraneGameConsts + +class CogdoEntityCreatorAI(EntityCreatorAI.EntityCreatorAI): + + def __init__(self, level): + EntityCreatorAI.EntityCreatorAI.__init__(self, level) + cDE = EntityCreatorAI.createDistributedEntity + cLE = EntityCreatorAI.createLocalEntity + nothing = EntityCreatorAI.nothing + self.privRegisterTypes({'levelMgr': Functor(cLE, CogdoLevelMgrAI), + 'cogdoCraneGameSettings': Functor(cLE, self._createCogdoSettings)}) + + def _createCogdoSettings(self, level, entId): + CogdoCraneGameConsts.Settings.initializeEntity(level, entId) + return CogdoCraneGameConsts.Settings diff --git a/toontown/cogdominium/CogdoEntityTypes.py b/toontown/cogdominium/CogdoEntityTypes.py new file mode 100755 index 00000000..53b395f4 --- /dev/null +++ b/toontown/cogdominium/CogdoEntityTypes.py @@ -0,0 +1,28 @@ +from otp.level.EntityTypes import * + +class CogdoLevelMgr(LevelMgr): + type = 'levelMgr' + + +class CogdoBoardroomGameSettings(Entity): + type = 'cogdoBoardroomGameSettings' + attribs = (('TimerScale', 1.0, 'float'),) + + +class CogdoCraneGameSettings(Entity): + type = 'cogdoCraneGameSettings' + attribs = (('GameDuration', 180.0, 'float'), + ('EmptyFrictionCoef', 0.1, 'float'), + ('Gravity', -32, 'int'), + ('RopeLinkMass', 1.0, 'float'), + ('MagnetMass', 1.0, 'float'), + ('MoneyBagGrabHeight', -8.2, 'float')) + + +class CogdoCraneCogSettings(Entity): + type = 'cogdoCraneCogSettings' + attribs = (('CogSpawnPeriod', 10.0, 'float'), + ('CogWalkSpeed', 2.0, 'float'), + ('CogMachineInteractDuration', 2.0, 'float'), + ('CogFlyAwayHeight', 50.0, 'float'), + ('CogFlyAwayDuration', 4.0, 'float')) diff --git a/toontown/cogdominium/CogdoExecutiveSuiteMovies.py b/toontown/cogdominium/CogdoExecutiveSuiteMovies.py new file mode 100755 index 00000000..0227f849 --- /dev/null +++ b/toontown/cogdominium/CogdoExecutiveSuiteMovies.py @@ -0,0 +1,142 @@ +from pandac.PandaModules import NodePath, Point3, PlaneNode, TextNode +from direct.interval.IntervalGlobal import * +from direct.showbase.ShowBase import Plane +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.RandomNumGen import RandomNumGen +from direct.interval.MetaInterval import Sequence, Parallel +from direct.interval.FunctionInterval import Func, Wait +from direct.gui.DirectGui import * +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase import TTLocalizer +from toontown.suit import Suit, SuitDNA +from toontown.toon import Toon, ToonHead, ToonDNA +from CogdoUtil import CogdoGameMovie +import CogdoUtil + +class CogdoExecutiveSuiteIntro(CogdoGameMovie): + notify = DirectNotifyGlobal.directNotify.newCategory('CogdoExecutiveSuiteIntro') + introDuration = 7 + cameraMoveDuration = 3 + + def __init__(self, shopOwner): + CogdoGameMovie.__init__(self) + self._shopOwner = shopOwner + self._lookAtCamTarget = False + self._camTarget = None + self._camHelperNode = None + self._toonDialogueSfx = None + self.toonHead = None + self.frame = None + return + + def displayLine(self, text): + self.notify.debug('displayLine') + self._dialogueLabel.node().setText(text) + self.toonHead.reparentTo(aspect2d) + self._toonDialogueSfx.play() + self.toonHead.setClipPlane(self.clipPlane) + + def makeSuit(self, suitType): + self.notify.debug('makeSuit()') + suit = Suit.Suit() + dna = SuitDNA.SuitDNA() + dna.newSuit(suitType) + suit.setStyle(dna) + suit.isDisguised = 1 + suit.generateSuit() + suit.setScale(1.05, 1.05, 2.05) + suit.setPos(0, 0, -4.4) + suit.reparentTo(self.toonHead) + for part in suit.getHeadParts(): + part.hide() + + suit.loop('neutral') + + def load(self): + self.notify.debug('load()') + CogdoGameMovie.load(self) + backgroundGui = loader.loadModel('phase_5/models/cogdominium/tt_m_gui_csa_flyThru') + self.bg = backgroundGui.find('**/background') + self.chatBubble = backgroundGui.find('**/chatBubble') + self.chatBubble.setScale(6.5, 6.5, 7.3) + self.chatBubble.setPos(0.32, 0, -0.78) + self.bg.setScale(5.2) + self.bg.setPos(0.14, 0, -0.6667) + self.bg.reparentTo(aspect2d) + self.chatBubble.reparentTo(aspect2d) + self.frame = DirectFrame(geom=self.bg, relief=None, pos=(0.2, 0, -0.6667)) + self.bg.wrtReparentTo(self.frame) + self.gameTitleText = DirectLabel(parent=self.frame, text=TTLocalizer.CogdoExecutiveSuiteTitle, scale=TTLocalizer.MRPgameTitleText * 0.8, text_align=TextNode.ACenter, text_font=getSignFont(), text_fg=(1.0, 0.33, 0.33, 1.0), pos=TTLocalizer.MRgameTitleTextPos, relief=None) + self.chatBubble.wrtReparentTo(self.frame) + self.frame.hide() + backgroundGui.removeNode() + self.toonDNA = ToonDNA.ToonDNA() + self.toonDNA.newToonFromProperties('dss', 'ss', 'm', 'm', 2, 0, 2, 2, 1, 8, 1, 8, 1, 14) + self.toonHead = Toon.Toon() + self.toonHead.setDNA(self.toonDNA) + self.makeSuit('sc') + self.toonHead.getGeomNode().setDepthWrite(1) + self.toonHead.getGeomNode().setDepthTest(1) + self.toonHead.loop('neutral') + self.toonHead.setPosHprScale(-0.73, 0, -1.27, 180, 0, 0, 0.18, 0.18, 0.18) + self.toonHead.reparentTo(hidden) + self.toonHead.startBlink() + self.clipPlane = self.toonHead.attachNewNode(PlaneNode('clip')) + self.clipPlane.node().setPlane(Plane(0, 0, 1, 0)) + self.clipPlane.setPos(0, 0, 2.45) + self._toonDialogueSfx = loader.loadSfx('phase_3.5/audio/dial/AV_dog_long.ogg') + self._camHelperNode = NodePath('CamHelperNode') + self._camHelperNode.reparentTo(render) + dialogue = TTLocalizer.CogdoExecutiveSuiteIntroMessage + + def start(): + self.frame.show() + base.setCellsAvailable(base.bottomCells + base.leftCells + base.rightCells, 0) + + def showShopOwner(): + self._setCamTarget(self._shopOwner, -10, offset=Point3(0, 0, 5)) + + def end(): + self._dialogueLabel.reparentTo(hidden) + self.toonHead.reparentTo(hidden) + self.frame.hide() + base.setCellsAvailable(base.bottomCells + base.leftCells + base.rightCells, 1) + self._stopUpdateTask() + + self._ival = Sequence(Func(start), Func(self.displayLine, dialogue), Func(showShopOwner), ParallelEndTogether(camera.posInterval(self.cameraMoveDuration, Point3(8, 0, 13), blendType='easeInOut'), camera.hprInterval(0.5, self._camHelperNode.getHpr(), blendType='easeInOut')), Wait(self.introDuration), Func(end)) + self._startUpdateTask() + return + + def _setCamTarget(self, targetNP, distance, offset = Point3(0, 0, 0), angle = Point3(0, 0, 0)): + camera.wrtReparentTo(render) + self._camTarget = targetNP + self._camOffset = offset + self._camAngle = angle + self._camDistance = distance + self._camHelperNode.setPos(self._camTarget, self._camOffset) + self._camHelperNode.setHpr(self._camTarget, 180 + self._camAngle[0], self._camAngle[1], self._camAngle[2]) + self._camHelperNode.setPos(self._camHelperNode, 0, self._camDistance, 0) + + def _updateTask(self, task): + dt = globalClock.getDt() + return task.cont + + def unload(self): + self._shopOwner = None + self._camTarget = None + if hasattr(self, '_camHelperNode') and self._camHelperNode: + self._camHelperNode.removeNode() + del self._camHelperNode + self.frame.destroy() + del self.frame + self.bg.removeNode() + del self.bg + self.chatBubble.removeNode() + del self.chatBubble + self.toonHead.stopBlink() + self.toonHead.stop() + self.toonHead.removeNode() + self.toonHead.delete() + del self.toonHead + CogdoGameMovie.unload(self) + return diff --git a/toontown/cogdominium/CogdoFlyingCameraManager.py b/toontown/cogdominium/CogdoFlyingCameraManager.py new file mode 100755 index 00000000..9ce0e81e --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingCameraManager.py @@ -0,0 +1,190 @@ +import math +from pandac.PandaModules import NodePath, Vec3 +from pandac.PandaModules import CollisionTraverser, CollisionHandlerQueue +from pandac.PandaModules import CollisionRay, CollisionNode +from math import pi, sin, cos +from direct.showbase.PythonUtil import bound as clamp +from otp.otpbase import OTPGlobals +from toontown.toonbase import ToontownGlobals +import CogdoFlyingGameGlobals as Globals +INVERSE_E = 1.0 / math.e + +def smooth(old, new): + return old * 0.7 + new * 0.3 + + +class CogdoFlyingCameraManager: + + def __init__(self, cam, parent, player, level): + self._toon = player.toon + self._camera = cam + self._parent = parent + self._player = player + self._level = level + self._enabled = False + + def enable(self): + if self._enabled: + return + self._toon.detachCamera() + self._prevToonY = 0.0 + levelBounds = self._level.getBounds() + l = Globals.Camera.LevelBoundsFactor + self._bounds = ((levelBounds[0][0] * l[0], levelBounds[0][1] * l[0]), (levelBounds[1][0] * l[1], levelBounds[1][1] * l[1]), (levelBounds[2][0] * l[2], levelBounds[2][1] * l[2])) + self._lookAtZ = self._toon.getHeight() + Globals.Camera.LookAtToonHeightOffset + self._camParent = NodePath('CamParent') + self._camParent.reparentTo(self._parent) + self._camParent.setPos(self._toon, 0, 0, 0) + self._camParent.setHpr(180, Globals.Camera.Angle, 0) + self._camera.reparentTo(self._camParent) + self._camera.setPos(0, Globals.Camera.Distance, 0) + self._camera.lookAt(self._toon, 0, 0, self._lookAtZ) + self._cameraLookAtNP = NodePath('CameraLookAt') + self._cameraLookAtNP.reparentTo(self._camera.getParent()) + self._cameraLookAtNP.setPosHpr(self._camera.getPos(), self._camera.getHpr()) + self._levelBounds = self._level.getBounds() + self._enabled = True + self._frozen = False + self._initCollisions() + + def _initCollisions(self): + self._camCollRay = CollisionRay() + camCollNode = CollisionNode('CameraToonRay') + camCollNode.addSolid(self._camCollRay) + camCollNode.setFromCollideMask(OTPGlobals.WallBitmask | OTPGlobals.CameraBitmask | ToontownGlobals.FloorEventBitmask | ToontownGlobals.CeilingBitmask) + camCollNode.setIntoCollideMask(0) + self._camCollNP = self._camera.attachNewNode(camCollNode) + self._camCollNP.show() + self._collOffset = Vec3(0, 0, 0.5) + self._collHandler = CollisionHandlerQueue() + self._collTrav = CollisionTraverser() + self._collTrav.addCollider(self._camCollNP, self._collHandler) + self._betweenCamAndToon = {} + self._transNP = NodePath('trans') + self._transNP.reparentTo(render) + self._transNP.setTransparency(True) + self._transNP.setAlphaScale(Globals.Camera.AlphaBetweenToon) + self._transNP.setBin('fixed', 10000) + + def _destroyCollisions(self): + self._collTrav.removeCollider(self._camCollNP) + self._camCollNP.removeNode() + del self._camCollNP + del self._camCollRay + del self._collHandler + del self._collOffset + del self._betweenCamAndToon + self._transNP.removeNode() + del self._transNP + + def freeze(self): + self._frozen = True + + def unfreeze(self): + self._frozen = False + + def disable(self): + if not self._enabled: + return + self._destroyCollisions() + self._camera.wrtReparentTo(render) + self._cameraLookAtNP.removeNode() + del self._cameraLookAtNP + self._camParent.removeNode() + del self._camParent + del self._prevToonY + del self._lookAtZ + del self._bounds + del self._frozen + self._enabled = False + + def update(self, dt = 0.0): + self._updateCam(dt) + self._updateCollisions() + + def _updateCam(self, dt): + toonPos = self._toon.getPos() + camPos = self._camParent.getPos() + x = camPos[0] + z = camPos[2] + toonWorldX = self._toon.getX(render) + maxX = Globals.Camera.MaxSpinX + toonWorldX = clamp(toonWorldX, -1.0 * maxX, maxX) + spinAngle = Globals.Camera.MaxSpinAngle * toonWorldX * toonWorldX / (maxX * maxX) + newH = 180.0 + spinAngle + self._camParent.setH(newH) + spinAngle = spinAngle * (pi / 180.0) + distBehindToon = Globals.Camera.SpinRadius * cos(spinAngle) + distToRightOfToon = Globals.Camera.SpinRadius * sin(spinAngle) + d = self._camParent.getX() - clamp(toonPos[0], *self._bounds[0]) + if abs(d) > Globals.Camera.LeewayX: + if d > Globals.Camera.LeewayX: + x = toonPos[0] + Globals.Camera.LeewayX + else: + x = toonPos[0] - Globals.Camera.LeewayX + x = self._toon.getX(render) + distToRightOfToon + boundToonZ = min(toonPos[2], self._bounds[2][1]) + d = z - boundToonZ + if d > Globals.Camera.MinLeewayZ: + if self._player.velocity[2] >= 0 and toonPos[1] != self._prevToonY or self._player.velocity[2] > 0: + z = boundToonZ + d * INVERSE_E ** (dt * Globals.Camera.CatchUpRateZ) + elif d > Globals.Camera.MaxLeewayZ: + z = boundToonZ + Globals.Camera.MaxLeewayZ + elif d < -Globals.Camera.MinLeewayZ: + z = boundToonZ - Globals.Camera.MinLeewayZ + if self._frozen: + y = camPos[1] + else: + y = self._toon.getY(render) - distBehindToon + self._camParent.setPos(x, smooth(camPos[1], y), smooth(camPos[2], z)) + if toonPos[2] < self._bounds[2][1]: + h = self._cameraLookAtNP.getH() + if d >= Globals.Camera.MinLeewayZ: + self._cameraLookAtNP.lookAt(self._toon, 0, 0, self._lookAtZ) + elif d <= -Globals.Camera.MinLeewayZ: + self._cameraLookAtNP.lookAt(self._camParent, 0, 0, self._lookAtZ) + self._cameraLookAtNP.setHpr(h, self._cameraLookAtNP.getP(), 0) + self._camera.setHpr(smooth(self._camera.getHpr(), self._cameraLookAtNP.getHpr())) + self._prevToonY = toonPos[1] + + def _updateCollisions(self): + pos = self._toon.getPos(self._camera) + self._collOffset + self._camCollRay.setOrigin(pos) + direction = -Vec3(pos) + direction.normalize() + self._camCollRay.setDirection(direction) + self._collTrav.traverse(render) + nodesInBetween = {} + if self._collHandler.getNumEntries() > 0: + self._collHandler.sortEntries() + for entry in self._collHandler.getEntries(): + name = entry.getIntoNode().getName() + if name.find('col_') >= 0: + np = entry.getIntoNodePath().getParent() + if not np in nodesInBetween: + nodesInBetween[np] = np.getParent() + + for np in nodesInBetween.keys(): + if np in self._betweenCamAndToon: + del self._betweenCamAndToon[np] + else: + np.setTransparency(True) + np.wrtReparentTo(self._transNP) + if np.getName().find('lightFixture') >= 0: + if not np.find('**/*floor_mesh').isEmpty(): + np.find('**/*floor_mesh').hide() + elif np.getName().find('platform') >= 0: + if not np.find('**/*Floor').isEmpty(): + np.find('**/*Floor').hide() + + for np, parent in self._betweenCamAndToon.items(): + np.wrtReparentTo(parent) + np.setTransparency(False) + if np.getName().find('lightFixture') >= 0: + if not np.find('**/*floor_mesh').isEmpty(): + np.find('**/*floor_mesh').show() + elif np.getName().find('platform') >= 0: + if not np.find('**/*Floor').isEmpty(): + np.find('**/*Floor').show() + + self._betweenCamAndToon = nodesInBetween diff --git a/toontown/cogdominium/CogdoFlyingCollisions.py b/toontown/cogdominium/CogdoFlyingCollisions.py new file mode 100755 index 00000000..23fb76b9 --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingCollisions.py @@ -0,0 +1,132 @@ +from direct.controls.GravityWalker import GravityWalker +from pandac.PandaModules import CollisionSphere, CollisionNode, BitMask32, CollisionHandlerEvent, CollisionRay, CollisionHandlerGravity, CollisionHandlerFluidPusher, CollisionHandlerPusher +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals + +class CogdoFlyingCollisions(GravityWalker): + wantFloorSphere = 0 + + def __init__(self): + GravityWalker.__init__(self, gravity=0.0) + + def initializeCollisions(self, collisionTraverser, avatarNodePath, avatarRadius = 1.4, floorOffset = 1.0, reach = 1.0): + self.cHeadSphereNodePath = None + self.cFloorEventSphereNodePath = None + self.setupHeadSphere(avatarNodePath) + self.setupFloorEventSphere(avatarNodePath, ToontownGlobals.FloorEventBitmask, avatarRadius) + GravityWalker.initializeCollisions(self, collisionTraverser, avatarNodePath, avatarRadius, floorOffset, reach) + return + + def setupWallSphere(self, bitmask, avatarRadius): + self.avatarRadius = avatarRadius + cSphere = CollisionSphere(0.0, 0.0, self.avatarRadius + 0.75, self.avatarRadius) + cSphereNode = CollisionNode('Flyer.cWallSphereNode') + cSphereNode.addSolid(cSphere) + cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode) + cSphereNode.setFromCollideMask(bitmask) + cSphereNode.setIntoCollideMask(BitMask32.allOff()) + if config.GetBool('want-fluid-pusher', 0): + self.pusher = CollisionHandlerFluidPusher() + else: + self.pusher = CollisionHandlerPusher() + self.pusher.addCollider(cSphereNodePath, self.avatarNodePath) + self.cWallSphereNodePath = cSphereNodePath + + def setupEventSphere(self, bitmask, avatarRadius): + self.avatarRadius = avatarRadius + cSphere = CollisionSphere(0.0, 0.0, self.avatarRadius + 0.75, self.avatarRadius * 1.04) + cSphere.setTangible(0) + cSphereNode = CollisionNode('Flyer.cEventSphereNode') + cSphereNode.addSolid(cSphere) + cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode) + cSphereNode.setFromCollideMask(bitmask) + cSphereNode.setIntoCollideMask(BitMask32.allOff()) + self.event = CollisionHandlerEvent() + self.event.addInPattern('enter%in') + self.event.addOutPattern('exit%in') + self.cEventSphereNodePath = cSphereNodePath + + def setupRay(self, bitmask, floorOffset, reach): + cRay = CollisionRay(0.0, 0.0, 3.0, 0.0, 0.0, -1.0) + cRayNode = CollisionNode('Flyer.cRayNode') + cRayNode.addSolid(cRay) + self.cRayNodePath = self.avatarNodePath.attachNewNode(cRayNode) + cRayNode.setFromCollideMask(bitmask) + cRayNode.setIntoCollideMask(BitMask32.allOff()) + self.lifter = CollisionHandlerGravity() + self.lifter.setLegacyMode(self._legacyLifter) + self.lifter.setGravity(self.getGravity(0)) + self.lifter.addInPattern('%fn-enter-%in') + self.lifter.addAgainPattern('%fn-again-%in') + self.lifter.addOutPattern('%fn-exit-%in') + self.lifter.setOffset(floorOffset) + self.lifter.setReach(reach) + self.lifter.addCollider(self.cRayNodePath, self.avatarNodePath) + + def setupHeadSphere(self, avatarNodePath): + collSphere = CollisionSphere(0, 0, 0, 1) + collSphere.setTangible(1) + collNode = CollisionNode('Flyer.cHeadCollSphere') + collNode.setFromCollideMask(ToontownGlobals.CeilingBitmask) + collNode.setIntoCollideMask(BitMask32.allOff()) + collNode.addSolid(collSphere) + self.cHeadSphereNodePath = avatarNodePath.attachNewNode(collNode) + self.cHeadSphereNodePath.setZ(base.localAvatar.getHeight() + 1.0) + self.headCollisionEvent = CollisionHandlerEvent() + self.headCollisionEvent.addInPattern('%fn-enter-%in') + self.headCollisionEvent.addOutPattern('%fn-exit-%in') + base.cTrav.addCollider(self.cHeadSphereNodePath, self.headCollisionEvent) + + def setupFloorEventSphere(self, avatarNodePath, bitmask, avatarRadius): + cSphere = CollisionSphere(0.0, 0.0, 0.0, 0.75) + cSphereNode = CollisionNode('Flyer.cFloorEventSphere') + cSphereNode.addSolid(cSphere) + cSphereNodePath = avatarNodePath.attachNewNode(cSphereNode) + cSphereNode.setFromCollideMask(bitmask) + cSphereNode.setIntoCollideMask(BitMask32.allOff()) + self.floorCollisionEvent = CollisionHandlerEvent() + self.floorCollisionEvent.addInPattern('%fn-enter-%in') + self.floorCollisionEvent.addAgainPattern('%fn-again-%in') + self.floorCollisionEvent.addOutPattern('%fn-exit-%in') + base.cTrav.addCollider(cSphereNodePath, self.floorCollisionEvent) + self.cFloorEventSphereNodePath = cSphereNodePath + + def deleteCollisions(self): + GravityWalker.deleteCollisions(self) + if self.cHeadSphereNodePath != None: + base.cTrav.removeCollider(self.cHeadSphereNodePath) + self.cHeadSphereNodePath.detachNode() + self.cHeadSphereNodePath = None + self.headCollisionsEvent = None + if self.cFloorEventSphereNodePath != None: + base.cTrav.removeCollider(self.cFloorEventSphereNodePath) + self.cFloorEventSphereNodePath.detachNode() + self.cFloorEventSphereNodePath = None + self.floorCollisionEvent = None + self.cRayNodePath.detachNode() + del self.cRayNodePath + self.cEventSphereNodePath.detachNode() + del self.cEventSphereNodePath + return + + def setCollisionsActive(self, active = 1): + if self.collisionsActive != active: + if self.cHeadSphereNodePath != None: + base.cTrav.removeCollider(self.cHeadSphereNodePath) + if active: + base.cTrav.addCollider(self.cHeadSphereNodePath, self.headCollisionEvent) + if self.cFloorEventSphereNodePath != None: + base.cTrav.removeCollider(self.cFloorEventSphereNodePath) + if active: + base.cTrav.addCollider(self.cFloorEventSphereNodePath, self.floorCollisionEvent) + GravityWalker.setCollisionsActive(self, active) + return + + def enableAvatarControls(self): + pass + + def disableAvatarControls(self): + pass + + def handleAvatarControls(self, task): + pass diff --git a/toontown/cogdominium/CogdoFlyingGame.py b/toontown/cogdominium/CogdoFlyingGame.py new file mode 100755 index 00000000..a7ee4f45 --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingGame.py @@ -0,0 +1,464 @@ +from direct.showbase.DirectObject import DirectObject +from direct.task.Task import Task +from direct.showbase.RandomNumGen import RandomNumGen +from direct.interval.FunctionInterval import Wait +from direct.interval.IntervalGlobal import Func +from direct.interval.MetaInterval import Sequence, Parallel +from toontown.toonbase import TTLocalizer, ToontownGlobals +import CogdoFlyingGameGlobals as Globals +from CogdoFlyingLocalPlayer import CogdoFlyingLocalPlayer +from CogdoGameAudioManager import CogdoGameAudioManager +from CogdoFlyingPlayer import CogdoFlyingPlayer +from CogdoFlyingObjects import CogdoFlyingGatherable +from CogdoFlyingObstacles import CogdoFlyingObstacle +from CogdoFlyingLegalEagle import CogdoFlyingLegalEagle +from CogdoFlyingGuiManager import CogdoFlyingGuiManager +from CogdoFlyingLevel import CogdoFlyingLevelFactory +from CogdoFlyingGameMovies import CogdoFlyingGameIntro, CogdoFlyingGameFinish + +class CogdoFlyingGame(DirectObject): + notify = directNotify.newCategory('CogdoFlyingGame') + UpdateTaskName = 'CogdoFlyingGameUpdate' + FirstPressOfCtrlTaskName = 'FirstPressOfCtrlTask' + + def __init__(self, distGame): + self.distGame = distGame + self.toonId2Player = {} + self.players = [] + self.index2LegalEagle = {} + self.legalEagles = [] + self.isGameComplete = False + self.localPlayer = None + self._hints = {'targettedByEagle': False, + 'invulnerable': False} + + def _initLegalEagles(self): + nestIndex = 1 + nests = self.level.root.findAllMatches('**/%s;+s' % Globals.Level.LegalEagleNestName) + for nest in nests: + legalEagle = CogdoFlyingLegalEagle(nest, nestIndex) + self.legalEagles.append(legalEagle) + self.index2LegalEagle[nestIndex] = legalEagle + nestIndex += 1 + + def initPlayers(self): + for toonId in self.distGame.getToonIds(): + toon = self.distGame.getToon(toonId) + i = self.distGame.getToonIds().index(toon.doId) + if toon is not None: + if toon.isLocal(): + player = CogdoFlyingLocalPlayer(toon, self, self.level, self.guiMgr) + self.localPlayer = player + self.localPlayer.setPlayerNumber(i) + else: + player = CogdoFlyingPlayer(toon) + player.enable() + self._addPlayer(player) + self.guiMgr.addToonToProgressMeter(toon) + + return + + def placeEntranceElevator(self, elevator): + loc = self.level.startPlatform._model.find('**/door_loc') + offset = loc.getPos(render) + elevator.setPos(render, offset) + + def load(self): + self.accept(self.distGame.getRemoteActionEventName(), self.handleRemoteAction) + self.audioMgr = CogdoGameAudioManager(Globals.Audio.MusicFiles, Globals.Audio.SfxFiles, base.localAvatar, cutoff=Globals.Audio.Cutoff) + factory = CogdoFlyingLevelFactory(render, Globals.Level.QuadLengthUnits, Globals.Level.QuadVisibilityAhead, Globals.Level.QuadVisibilityBehind, rng=RandomNumGen(self.distGame.doId)) + self.level = factory.createLevel(self.distGame.getSafezoneId()) + self.level.setCamera(camera) + self.guiMgr = CogdoFlyingGuiManager(self.level) + self.levelFog = factory.createLevelFog() + self._initLegalEagles() + + def unload(self): + self.guiMgr.destroy() + del self.guiMgr + self.__stopUpdateTask() + for player in self.players: + player.unload() + + del self.players[:] + self.toonId2Player.clear() + del self.localPlayer + for eagle in self.legalEagles: + eagle.destroy() + + del self.legalEagles[:] + self.index2LegalEagle.clear() + self.levelFog.destroy() + del self.levelFog + self.level.destroy() + del self.level + self.audioMgr.destroy() + del self.audioMgr + self.ignoreAll() + del self.distGame + + def onstage(self): + self.level.onstage() + self.levelFog.setVisible(True) + for eagle in self.legalEagles: + eagle.onstage() + + self.localPlayer.request('Inactive') + + def offstage(self): + self.__stopUpdateTask() + for eagle in self.legalEagles: + eagle.offstage() + + self.level.offstage() + self.levelFog.setVisible(False) + + def startIntro(self): + self._movie = CogdoFlyingGameIntro(self.level, RandomNumGen(self.distGame.doId)) + self._movie.load() + self._movie.play() + self.audioMgr.playMusic('normal') + + def endIntro(self): + self._movie.end() + self._movie.unload() + del self._movie + base.camLens.setMinFov(ToontownGlobals.CogdoFov/(4./3.)) + self.localPlayer.ready() + self.level.update(0.0) + + def startFinish(self): + self._movie = CogdoFlyingGameFinish(self.level, self.players) + self._movie.load() + self._movie.play() + self.audioMgr.playMusic('end') + + def endFinish(self): + self._movie.end() + self._movie.unload() + del self._movie + self.audioMgr.stopMusic() + + def start(self): + self.level.start(self.distGame.getStartTime()) + self.accept(CogdoFlyingObstacle.EnterEventName, self.handleLocalToonEnterObstacle) + self.accept(CogdoFlyingObstacle.ExitEventName, self.handleLocalToonExitObstacle) + self.accept(CogdoFlyingGatherable.EnterEventName, self.handleLocalToonEnterGatherable) + self.accept(CogdoFlyingLegalEagle.RequestAddTargetEventName, self.handleLocalToonEnterLegalEagleInterest) + self.accept(CogdoFlyingLegalEagle.RequestAddTargetAgainEventName, self.handleLocalToonAgainLegalEagleInterest) + self.accept(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, self.handleLocalToonExitLegalEagleInterest) + self.accept(CogdoFlyingLegalEagle.EnterLegalEagle, self.handleLocalToonEnterLegalEagle) + self.accept(CogdoFlyingLegalEagle.ChargingToAttackEventName, self.handlePlayerBackpackAttacked) + self.accept(CogdoFlyingLegalEagle.LockOnToonEventName, self.handlePlayerBackpackTargeted) + self.accept(CogdoFlyingLegalEagle.CooldownEventName, self.handlePlayerBackpackNormal) + self.accept(CogdoFlyingGuiManager.EagleTargetingLocalPlayerEventName, self.handleLocalPlayerTargetedByEagle) + self.accept(CogdoFlyingGuiManager.EagleAttackingLocalPlayerEventName, self.handleLocalPlayerAttackedByEagle) + self.accept(CogdoFlyingGuiManager.ClearMessageDisplayEventName, self.handleClearGuiMessage) + self.accept(CogdoFlyingGuiManager.InvulnerableEventName, self.handleLocalPlayerInvulnerable) + self.acceptOnce(CogdoFlyingGuiManager.FirstPressOfCtrlEventName, self.handleLocalPlayerFirstPressOfCtrl) + self.acceptOnce(CogdoFlyingGuiManager.PickedUpFirstPropellerEventName, self.handleLocalPlayerPickedUpFirstPropeller) + self.acceptOnce(CogdoFlyingGuiManager.StartRunningOutOfTimeMusicEventName, self.handleTimeRunningOut) + self.acceptOnce(CogdoFlyingLocalPlayer.PlayWaitingMusicEventName, self.handlePlayWaitingMusic) + self.acceptOnce(CogdoFlyingLocalPlayer.RanOutOfTimeEventName, self.handleLocalPlayerRanOutOfTime) + self.__startUpdateTask() + self.isGameComplete = False + + for eagle in self.legalEagles: + eagle.gameStart(self.distGame.getStartTime()) + + for player in self.players: + player.start() + + self.guiMgr.onstage() + if not Globals.Dev.InfiniteTimeLimit: + self.guiMgr.startTimer(Globals.Gameplay.SecondsUntilGameOver, self._handleTimerExpired) + + def exit(self): + self.ignore(CogdoFlyingObstacle.EnterEventName) + self.ignore(CogdoFlyingObstacle.ExitEventName) + self.ignore(CogdoFlyingGatherable.EnterEventName) + self.ignore(CogdoFlyingLegalEagle.ChargingToAttackEventName) + self.ignore(CogdoFlyingLegalEagle.LockOnToonEventName) + self.ignore(CogdoFlyingLegalEagle.CooldownEventName) + self.ignore(CogdoFlyingLocalPlayer.RanOutOfTimeEventName) + taskMgr.remove(CogdoFlyingGame.FirstPressOfCtrlTaskName) + self.__stopUpdateTask() + self.ignore(CogdoFlyingLegalEagle.EnterLegalEagle) + self.ignore(CogdoFlyingLegalEagle.RequestAddTargetEventName) + self.ignore(CogdoFlyingLegalEagle.RequestAddTargetAgainEventName) + self.ignore(CogdoFlyingLegalEagle.RequestRemoveTargetEventName) + self.ignore(CogdoFlyingLocalPlayer.PlayWaitingMusicEventName) + self.level.update(0.0) + for eagle in self.legalEagles: + eagle.gameEnd() + + for player in self.players: + player.exit() + + self.guiMgr.offstage() + + def _handleTimerExpired(self): + self.localPlayer.handleTimerExpired() + + def _addPlayer(self, player): + self.players.append(player) + self.toonId2Player[player.toon.doId] = player + toon = player.toon + self.accept(toon.uniqueName('disable'), self._removePlayer, extraArgs=[toon.doId]) + + def _removePlayer(self, toonId): + if toonId in self.toonId2Player: + player = self.toonId2Player[toonId] + self.players.remove(player) + del self.toonId2Player[toonId] + self.guiMgr.removeToonFromProgressMeter(player.toon) + player.exit() + player.unload() + + def setToonSad(self, toonId): + player = self.toonId2Player[toonId] + self.forceClearLegalEagleInterestInToon(toonId) + if player == self.localPlayer: + player.goSad() + self.exit() + else: + player.exit() + + def setToonDisconnect(self, toonId): + player = self.toonId2Player[toonId] + self.forceClearLegalEagleInterestInToon(toonId) + if player == self.localPlayer: + self.exit() + else: + player.exit() + + def handleRemoteAction(self, action, data): + if data != self.localPlayer.toon.doId: + if action == Globals.AI.GameActions.GotoWinState: + if self.localPlayer.state in ['WaitingForWin']: + self.localPlayer.request('Win') + + def toonDied(self, toonId, elapsedTime): + player = self.toonId2Player[toonId] + if player is not None: + player.died(elapsedTime) + return + + def toonSpawn(self, toonId, elapsedTime): + player = self.toonId2Player[toonId] + if player is not None: + player.spawn(elapsedTime) + return + + def toonResetBlades(self, toonId): + player = self.toonId2Player[toonId] + if player is not None: + player.resetBlades() + return + + def toonSetBlades(self, toonId, fuelState): + player = self.toonId2Player[toonId] + if player is not None: + player.setBlades(fuelState) + return + + def toonBladeLost(self, toonId): + player = self.toonId2Player[toonId] + if player is not None: + player.bladeLost() + return + + def handleLocalToonEnterGatherable(self, gatherable): + if gatherable.wasPickedUp(): + return + if gatherable.isPowerUp() and gatherable.wasPickedUpByToon(self.localPlayer.toon): + return + if gatherable.type in [Globals.Level.GatherableTypes.LaffPowerup, Globals.Level.GatherableTypes.InvulPowerup]: + self.distGame.d_sendRequestPickup(gatherable.serialNum, gatherable.type) + elif gatherable.type == Globals.Level.GatherableTypes.Memo: + self.distGame.d_sendRequestPickup(gatherable.serialNum, gatherable.type) + gatherable.disable() + elif gatherable.type == Globals.Level.GatherableTypes.Propeller and self.localPlayer.fuel < 1.0: + self.distGame.d_sendRequestPickup(gatherable.serialNum, gatherable.type) + + def pickUp(self, toonId, pickupNum, elapsedTime = 0.0): + self.notify.debugCall() + player = self.toonId2Player[toonId] + gatherable = self.level.getGatherable(pickupNum) + if gatherable is not None: + if not gatherable.isPowerUp() and not gatherable.wasPickedUp() or gatherable.isPowerUp() and not gatherable.wasPickedUpByToon(player.toon): + gatherable.pickUp(player.toon, elapsedTime) + player.handleEnterGatherable(gatherable, elapsedTime) + if gatherable.type in [Globals.Level.GatherableTypes.InvulPowerup]: + if player.toon.isLocal(): + self.audioMgr.playMusic('invul') + taskMgr.doMethodLater(30, lambda task: self.debuffPowerup(toonId, gatherable.type, elapsedTime), 'gatherable-timeout') + else: + self.notify.warning('Trying to pickup gatherable nonetype:%s' % pickupNum) + return + + def debuffPowerup(self, toonId, pickupType, elapsedTime): + self.notify.debugCall() + if toonId in self.toonId2Player: + player = self.toonId2Player[toonId] + if player.isBuffActive(pickupType): + if pickupType in [Globals.Level.GatherableTypes.InvulPowerup]: + if self.guiMgr.isTimeRunningOut(): + self.audioMgr.playMusic('timeRunningOut') + else: + self.audioMgr.playMusic('normal') + player.handleDebuffPowerup(pickupType, elapsedTime) + + def handleLocalToonEnterLegalEagle(self, eagle, collEntry): + if not self.localPlayer.isEnemyHitting() and not self.localPlayer.isInvulnerable(): + collPos = collEntry.getSurfacePoint(render) + self.localPlayer.handleEnterEnemyHit(eagle, collPos) + self.distGame.d_sendRequestAction(Globals.AI.GameActions.HitLegalEagle, 0) + + def handleLocalToonEnterObstacle(self, obstacle, collEntry): + if self.localPlayer.isInvulnerable(): + return + if obstacle.type == Globals.Level.ObstacleTypes.Whirlwind: + self.localPlayer.handleEnterWhirlwind(obstacle) + self.distGame.d_sendRequestAction(Globals.AI.GameActions.HitWhirlwind, 0) + if obstacle.type == Globals.Level.ObstacleTypes.Fan: + self.localPlayer.handleEnterFan(obstacle) + if obstacle.type == Globals.Level.ObstacleTypes.Minion: + if not self.localPlayer.isEnemyHitting(): + collPos = collEntry.getSurfacePoint(render) + self.localPlayer.handleEnterEnemyHit(obstacle, collPos) + self.distGame.d_sendRequestAction(Globals.AI.GameActions.HitMinion, 0) + + def handleLocalToonExitObstacle(self, obstacle, collEntry): + if obstacle.type == Globals.Level.ObstacleTypes.Fan: + self.localPlayer.handleExitFan(obstacle) + + def __startUpdateTask(self): + self.__stopUpdateTask() + taskMgr.add(self.__updateTask, CogdoFlyingGame.UpdateTaskName, 45) + + def __stopUpdateTask(self): + taskMgr.remove(CogdoFlyingGame.UpdateTaskName) + + def handleTimeRunningOut(self): + if self.localPlayer.state not in ['WaitingForWin']: + self.audioMgr.playMusic('timeRunningOut') + self.guiMgr.presentTimerGui() + self.guiMgr.setTemporaryMessage(TTLocalizer.CogdoFlyingGameTimeIsRunningOut) + + def handlePlayWaitingMusic(self): + self.audioMgr.playMusic('waiting') + + def handleLocalPlayerFirstPressOfCtrl(self): + self.doMethodLater(3.0, self.guiMgr.setMessage, CogdoFlyingGame.FirstPressOfCtrlTaskName, extraArgs=['']) + + def handleLocalPlayerRanOutOfTime(self): + self.guiMgr.setMemoCount(0) + self.distGame.d_sendRequestAction(Globals.AI.GameActions.RanOutOfTimePenalty, 0) + self.guiMgr.setMessage(TTLocalizer.CogdoFlyingGameTakingMemos) + + def handleClearGuiMessage(self): + if not self.localPlayer.isInvulnerable(): + self.guiMgr.setMessage('') + + def handleLocalPlayerInvulnerable(self): + if not self._hints['invulnerable']: + self.guiMgr.setMessage(TTLocalizer.CogdoFlyingGameYouAreInvincible) + self._hints['invulnerable'] = True + + def handleLocalPlayerPickedUpFirstPropeller(self): + self.guiMgr.setMessage(TTLocalizer.CogdoFlyingGamePressCtrlToFly) + self.guiMgr.presentRefuelGui() + + def handleLocalPlayerTargetedByEagle(self): + if not self.localPlayer.isInvulnerable() and not self._hints['targettedByEagle']: + self.guiMgr.setMessage(TTLocalizer.CogdoFlyingGameLegalEagleTargeting) + self._hints['targettedByEagle'] = True + + def handleLocalPlayerAttackedByEagle(self): + if not self.localPlayer.isInvulnerable(): + self.guiMgr.setMessage(TTLocalizer.CogdoFlyingGameLegalEagleAttacking) + + def handlePlayerBackpackAttacked(self, toonId): + if toonId in self.toonId2Player: + player = self.toonId2Player[toonId] + player.setBackpackState(Globals.Gameplay.BackpackStates.Attacked) + + def handlePlayerBackpackTargeted(self, toonId): + if toonId in self.toonId2Player: + player = self.toonId2Player[toonId] + player.setBackpackState(Globals.Gameplay.BackpackStates.Targeted) + + def handlePlayerBackpackNormal(self, toonId): + if toonId in self.toonId2Player: + player = self.toonId2Player[toonId] + player.setBackpackState(Globals.Gameplay.BackpackStates.Normal) + + def handleLocalToonEnterLegalEagleInterest(self, index): + if index in self.index2LegalEagle: + legalEagle = self.index2LegalEagle[index] + if not self.localPlayer.isLegalEagleInterestRequestSent(index) and not self.localPlayer.isLegalEagleTarget(): + if self.localPlayer.state in ['WaitingForWin', 'Win']: + return + self.localPlayer.setLegalEagleInterestRequest(index) + self.distGame.d_sendRequestAction(Globals.AI.GameActions.RequestEnterEagleInterest, index) + else: + self.notify.warning("Legal eagle %i isn't in index2LegalEagle dict" % index) + + def handleLocalToonAgainLegalEagleInterest(self, index): + self.handleLocalToonEnterLegalEagleInterest(index) + + def handleLocalToonExitLegalEagleInterest(self, index): + if index in self.index2LegalEagle: + legalEagle = self.index2LegalEagle[index] + if self.localPlayer.isLegalEagleInterestRequestSent(index): + self.localPlayer.clearLegalEagleInterestRequest(index) + self.distGame.d_sendRequestAction(Globals.AI.GameActions.RequestExitEagleInterest, index) + else: + self.notify.warning("Legal eagle %i isn't in index2LegalEagle dict" % index) + + def forceClearLegalEagleInterestInToon(self, toonId): + player = self.toonId2Player[toonId] + if player: + for legalEagle in self.legalEagles: + index = legalEagle.index + if player.toon.isLocal(): + if self.localPlayer.isLegalEagleInterestRequestSent(index): + self.localPlayer.clearLegalEagleInterestRequest(index) + self.distGame.d_sendRequestAction(Globals.AI.GameActions.RequestExitEagleInterest, index) + self.toonClearAsEagleTarget(toonId, index, 0) + + def toonSetAsEagleTarget(self, toonId, eagleId, elapsedTime): + if eagleId in self.index2LegalEagle: + legalEagle = self.index2LegalEagle[eagleId] + if toonId in self.toonId2Player: + player = self.toonId2Player[toonId] + player.setAsLegalEagleTarget(legalEagle) + legalEagle.setTarget(player.toon, elapsedTime) + + def toonClearAsEagleTarget(self, toonId, eagleId, elapsedTime): + if eagleId in self.index2LegalEagle: + legalEagle = self.index2LegalEagle[eagleId] + if toonId in self.toonId2Player: + player = self.toonId2Player[toonId] + player.removeAsLegalEagleTarget(legalEagle) + if legalEagle.getTarget() == player.toon: + legalEagle.clearTarget(elapsedTime) + + def eagleExitCooldown(self, eagleId, elapsedTime): + if eagleId in self.index2LegalEagle: + legalEagle = self.index2LegalEagle[eagleId] + legalEagle.leaveCooldown(elapsedTime) + + def gameComplete(self): + self.localPlayer.request('Win') + + def __updateTask(self, task): + dt = globalClock.getDt() + self.localPlayer.update(dt) + self.level.update(dt) + for eagle in self.legalEagles: + eagle.update(dt, self.localPlayer) + + self.guiMgr.update() + return Task.cont diff --git a/toontown/cogdominium/CogdoFlyingGameGlobals.py b/toontown/cogdominium/CogdoFlyingGameGlobals.py new file mode 100755 index 00000000..f4323429 --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingGameGlobals.py @@ -0,0 +1,263 @@ +from direct.showbase import PythonUtil +from pandac.PandaModules import VBase4, Vec3, Point3 +from CogdoUtil import VariableContainer, DevVariableContainer +AI = VariableContainer() +AI.GameActions = PythonUtil.Enum(('LandOnWinPlatform', 'WinStateFinished', 'GotoWinState', 'HitWhirlwind', 'HitLegalEagle', 'HitMinion', 'DebuffInvul', 'RequestEnterEagleInterest', 'RequestExitEagleInterest', 'RanOutOfTimePenalty', 'Died', 'Spawn', 'SetBlades', 'BladeLost')) +AI.BroadcastPeriod = 0.3 +AI.SafezoneId2DeathDamage = {2000: 1, + 1000: 2, + 5000: 4, + 4000: 8, + 3000: 12, + 9000: 16, + 7000: 20} +AI.SafezoneId2WhirlwindDamage = {2000: 1, + 1000: 2, + 5000: 4, + 4000: 8, + 3000: 12, + 9000: 16, + 7000: 20} +AI.SafezoneId2LegalEagleDamage = {2000: 2, + 1000: 4, + 5000: 8, + 4000: 16, + 3000: 24, + 9000: 32, + 7000: 36} +AI.SafezoneId2MinionDamage = {2000: 1, + 1000: 2, + 5000: 4, + 4000: 8, + 3000: 12, + 9000: 16, + 7000: 20} +Camera = VariableContainer() +Camera.Angle = 12.5 +Camera.Distance = 20 +Camera.LookAtToonHeightOffset = 1.0 +Camera.LeewayX = 0.5 +Camera.MinLeewayZ = 0.5 +Camera.MaxLeewayZ = 15.0 +Camera.CatchUpRateZ = 3.0 +Camera.LevelBoundsFactor = (0.6, 1.0, 0.9) +Camera.AlphaBetweenToon = 0.35 +Camera.SpinRadius = 9.0 +Camera.MaxSpinAngle = 20.0 +Camera.MaxSpinX = 16.0 +Camera.GameCameraFar = 400.0 +Gameplay = VariableContainer() +Gameplay.SecondsUntilGameOver = 60.0 * 3.0 +Gameplay.TimeRunningOutSeconds = 45.0 +Gameplay.IntroDurationSeconds = 24.0 +Gameplay.FinishDurationSeconds = 10.0 +Gameplay.GatherableFlashTime = 1.0 +Gameplay.ToonAcceleration = {'forward': 60.0, + 'backward': 60.0, + 'turning': 60.0, + 'boostUp': 20.0, + 'fall': 15.0, + 'activeDropDown': 20.0, + 'activeDropBack': 40.0, + 'fan': 80.0} +Gameplay.ToonDeceleration = {'forward': 10.0, + 'backward': 10.0, + 'turning': 10.0, + 'fan': 25.0} +Gameplay.ToonVelMax = {'forward': 20.0, + 'backward': 12.0, + 'turning': 15.0, + 'boost': 5.5, + 'fall': 10.0, + 'fallNoFuel': 70.0, + 'fan': 55.0} +Gameplay.ToonTurning = {'turningSpeed': 15.0, + 'maxTurningAngle': 45.0} +Gameplay.RayPlatformCollisionThreshold = 0.2 +Gameplay.UseVariableFanPower = True +Gameplay.FanMaxPower = 1.0 +Gameplay.FanMinPower = 0.4 +Gameplay.FanCollisionTubeRadius = 4.0 +Gameplay.FanCollisionTubeHeight = 35.0 +Gameplay.FanStreamerMinDuration = 0.2 +Gameplay.FanStreamerMaxDuration = 0.5 +Gameplay.WhirlwindCollisionTubeHeight = 100 +Gameplay.WhirlwindCollisionTubeRadius = 4.0 +Gameplay.WhirlwindMoveBackDist = 15.0 +Gameplay.WhirlwindSpinTime = 1.0 +Gameplay.WhirlwindMoveBackTime = 0.5 +Gameplay.FlyingMinionCollisionRadius = 2.5 +Gameplay.FlyingMinionCollisionHeightOffset = 5.0 +Gameplay.FlyingMinionFloatOffset = 1.0 +Gameplay.FlyingMinionFloatTime = 1.0 +Gameplay.WalkingMinionCollisionRadius = 2.5 +Gameplay.WalkingMinionCollisionHeightOffset = 2.0 +Gameplay.MemoCollisionRadius = 1.5 +Gameplay.MemoSpinRate = 60.0 +Gameplay.DoesToonDieWithFuel = True +Gameplay.SafezoneId2LaffPickupHealAmount = {2000: 1, + 1000: 2, + 5000: 4, + 4000: 8, + 3000: 12, + 9000: 16, + 7000: 20} +Gameplay.InvulBuffTime = 15.0 +Gameplay.InvulBlinkTime = 5.0 +Gameplay.InvulSingleBlinkTime = 0.5 +Gameplay.PropellerCollisionRadius = 3.0 +Gameplay.PropellerRespawnTime = 5.0 +Gameplay.FuelBurnRate = 0.1 +Gameplay.DepleteFuelStates = ['FlyingUp'] +Gameplay.FuelNormalAmt = 1.0 +Gameplay.FuelLowAmt = 0.66 +Gameplay.FuelVeryLowAmt = 0.33 +Gameplay.FuelStates = PythonUtil.Enum(('FuelNoPropeller', 'FuelEmpty', 'FuelVeryLow', 'FuelLow', 'FuelNormal')) +Gameplay.RefuelPropSpeed = 5.0 +Gameplay.OverdrivePropSpeed = 2.5 +Gameplay.NormalPropSpeed = 1.5 +Gameplay.TargetedWarningLabelScale = 3.5 +Gameplay.TargetedWarningSingleBlinkTime = 0.25 +Gameplay.TargetedWarningBlinkTime = 3.0 +Gameplay.HitKnockbackDist = 15.0 +Gameplay.HitKnockbackTime = 0.5 +Gameplay.HitCooldownTime = 2.0 +Gameplay.BackpackStates = PythonUtil.Enum(('Normal', 'Targeted', 'Attacked', 'Refuel')) +Gameplay.BackpackRefuelDuration = 4.0 +Gameplay.BackpackState2TextureName = {Gameplay.BackpackStates.Normal: 'tt_t_ara_cfg_propellerPack', + Gameplay.BackpackStates.Targeted: 'tt_t_ara_cfg_propellerPack_eagleTarget', + Gameplay.BackpackStates.Attacked: 'tt_t_ara_cfg_propellerPack_eagleAttack', + Gameplay.BackpackStates.Refuel: 'tt_t_ara_cfg_propellerPack_flash'} +Gameplay.MinionDnaName = 'bf' +Gameplay.MinionScale = 0.8 +Gui = VariableContainer() +Gui.FuelNumBladesPos2D = (-0.005, -0.017) +Gui.FuelNumBladesScale = 0.07 +Gui.FuelPos2D = (0.143, 0.760) +Gui.NumBlades2FuelColor = {0: (0.9, 0.15, 0.15, 1.0), + 1: (0.9, 0.15, 0.15, 1.0), + 2: (0.9, 0.9, 0.15, 1.0), + 3: (0.25, 0.65, 0.25, 1.0)} +Gui.FuelNormalColor = (0.25, 0.65, 0.25, 1.0) +Gui.FuelLowColor = (0.9, 0.9, 0.15, 1.0) +Gui.FuelVeryLowColor = (0.9, 0.15, 0.15, 1.0) +Gui.ProgressPos2D = (-0.183, 0.85) +Gui.ProgressHPos2D = (0, 0.82) +Gui.MarkerScale = 0.03 +Gui.LocalMarkerScale = 0.0425 +LegalEagle = VariableContainer() +LegalEagle.EagleAndTargetDistCameraTrackThreshold = 30.0 +LegalEagle.InterestConeLength = 80 +LegalEagle.InterestConeOffset = 5.0 +LegalEagle.InterestConeAngle = 60 +LegalEagle.DamageSphereRadius = 3.0 +LegalEagle.OnNestDamageSphereRadius = 6.0 +LegalEagle.VerticalOffset = -6.0 +LegalEagle.PlatformVerticalOffset = 0.0 +LegalEagle.LiftOffTime = 0.5 +LegalEagle.LiftOffHeight = 5.0 +LegalEagle.LockOnSpeed = 3.0 +LegalEagle.LockOnTime = 2.0 +LegalEagle.ExtraPostCooldownTime = 2.0 +LegalEagle.LockOnDistanceFromNest = -7.0 +LegalEagle.ChargeUpTime = 0.75 +LegalEagle.RetreatToNestTime = 2.0 +LegalEagle.PreAttackTime = 0.75 +LegalEagle.PostAttackTime = 0.5 +LegalEagle.RetreatToSkyTime = 1.25 +LegalEagle.EagleAttackShouldXCorrect = True +LegalEagle.AttackRateOfChangeVec = Vec3(1.0, 1.0, 2.0) +LegalEagle.PostAttackLength = 5.0 +LegalEagle.CooldownTime = 5.0 +LegalEagle.PostCooldownHeightOffNest = 40.0 +Dev = DevVariableContainer('cogdoflying') +Dev.DisableDeath = False +Dev.InfiniteFuel = False +Dev.InfiniteTimeLimit = False +Dev.Invincibility = False +Dev.NoLegalEagleAttacks = False +Audio = VariableContainer() +Audio.Cutoff = 75.0 +Audio.MusicFiles = {'normal': 'phase_9/audio/bgm/CHQ_FACT_bg.ogg', + 'end': 'phase_7/audio/bgm/encntr_toon_winning_indoor.ogg', + 'waiting': 'phase_7/audio/bgm/encntr_toon_winning_indoor.ogg', + 'invul': 'phase_9/audio/bgm/encntr_toon_winning.ogg', + 'timeRunningOut': 'phase_7/audio/bgm/encntr_suit_winning_indoor.ogg'} +Audio.SfxFiles = {'propeller': 'phase_4/audio/sfx/TB_propeller.ogg', + 'propeller_damaged': 'phase_5/audio/sfx/tt_s_ara_cfg_propellers_damaged.ogg', + 'fan': 'phase_4/audio/sfx/target_wind_float_loop.ogg', + 'getMemo': 'phase_4/audio/sfx/MG_maze_pickup.ogg', + 'getLaff': 'phase_4/audio/sfx/avatar_emotion_laugh.ogg', + 'getRedTape': 'phase_5/audio/sfx/SA_red_tape.ogg', + 'invulBuff': 'phase_4/audio/sfx/ring_get.ogg', + 'invulDebuff': 'phase_4/audio/sfx/ring_miss.ogg', + 'refuel': 'phase_5/audio/sfx/LB_receive_evidence.ogg', + 'bladeBreak': 'phase_5/audio/sfx/tt_s_ara_cfg_propellerBreaks.ogg', + 'popupHelpText': 'phase_3/audio/sfx/GUI_balloon_popup.ogg', + 'collide': 'phase_3.5/audio/sfx/AV_collision.ogg', + 'whirlwind': 'phase_5/audio/sfx/tt_s_ara_cfg_whirlwind.ogg', + 'toonInWhirlwind': 'phase_5/audio/sfx/tt_s_ara_cfg_toonInWhirlwind.ogg', + 'death': 'phase_5/audio/sfx/tt_s_ara_cfg_toonFalls.ogg', + 'legalEagleScream': 'phase_5/audio/sfx/tt_s_ara_cfg_eagleCry.ogg', + 'toonHit': 'phase_5/audio/sfx/tt_s_ara_cfg_toonHit.ogg', + 'lose': 'phase_4/audio/sfx/MG_lose.ogg', + 'win': 'phase_4/audio/sfx/MG_win.ogg', + 'refuelSpin': 'phase_4/audio/sfx/clock12.ogg', + 'cogDialogue': 'phase_3.5/audio/dial/COG_VO_statement.ogg', + 'toonDialogue': 'phase_3.5/audio/dial/AV_dog_long.ogg'} +Level = VariableContainer() +Level.GatherableTypes = PythonUtil.Enum(('Memo', 'Propeller', 'LaffPowerup', 'InvulPowerup')) +Level.ObstacleTypes = PythonUtil.Enum(('Whirlwind', 'Fan', 'Minion')) +Level.PlatformTypes = PythonUtil.Enum(('Platform', 'StartPlatform', 'EndPlatform')) +Level.PlatformType2SpawnOffset = {Level.PlatformTypes.Platform: 2.5, + Level.PlatformTypes.StartPlatform: 5.0, + Level.PlatformTypes.EndPlatform: 5.0} +Level.QuadLengthUnits = 170 +Level.QuadVisibilityAhead = 1 +Level.QuadVisibilityBehind = 0 +Level.StartPlatformLength = 55 +Level.StartPlatformHeight = 20 +Level.EndPlatformLength = 55 +Level.EndPlatformHeight = 0 +Level.FogColor = VBase4(0.18, 0.19, 0.32, 1.0) +Level.RenderFogStartFactor = 0.22 +Level.GatherablesDefaultSpread = 1.0 +Level.NumMemosInRing = 6 +Level.PropellerSpinDuration = 10.0 +Level.QuadsByDifficulty = {1: (2, 4, 5), + 2: (1, 3, 7), + 3: (6, 8)} +Level.DifficultyOrder = {2000: (1, 1, 1, 2, 1), + 1000: (1, 1, 2, 2, 1), + 5000: (1, 2, 1, 2, 2), + 4000: (1, 2, 1, 2, 3, 2), + 3000: (1, 2, 2, 3, 2, 3), + 9000: (2, 3, 2, 3, 2, 3, 2), + 7000: (2, 3, 2, 3, 2, 3, 2)} +Dev.WantTempLevel = True +Dev.DevQuadsOrder = (1, 2, 3, 4, 5, 6, 7, 8) +Level.AddSparkleToPowerups = False +Level.AddParticlesToStreamers = False +Level.IgnoreLaffPowerups = False +Level.SpawnLaffPowerupsInNests = True +Level.LaffPowerupNestOffset = Point3(0.0, 2.0, 3.0) +Level.PlatformName = '*lightFixture' +Level.GatherablesPathName = 'gatherables_path*' +Level.GatherablesRingName = 'gatherables_ring_path*' +Level.PropellerName = '*propeller_loc*' +Level.PowerupType2Loc = {Level.GatherableTypes.LaffPowerup: 'laff_powerup_loc*', + Level.GatherableTypes.InvulPowerup: 'invul_powerup_loc*'} +Level.PowerupType2Model = {Level.GatherableTypes.LaffPowerup: 'legalEagleFeather', + Level.GatherableTypes.InvulPowerup: 'redTapePickup'} +Level.PowerupType2Node = {Level.GatherableTypes.LaffPowerup: 'feather', + Level.GatherableTypes.InvulPowerup: 'redTape'} +Level.GatherableType2TextureName = {Level.GatherableTypes.LaffPowerup: 'tt_t_ara_cfg_legalEagleFeather_flash', + Level.GatherableTypes.InvulPowerup: 'tt_t_ara_cfg_redTapePickup_flash', + Level.GatherableTypes.Memo: 'tt_t_ara_csa_memo_flash', + Level.GatherableTypes.Propeller: 'tt_t_ara_cfg_propellers_flash'} +Level.WhirlwindName = '*whirlwindPlaceholder' +Level.WhirlwindPathName = '_path*' +Level.StreamerName = '*streamerPlaceholder' +Level.MinionWalkingPathName = '*minion_walking_path*' +Level.MinionFlyingPathName = '*minion_flying_path*' +Level.LegalEagleNestName = '*eagleNest_loc*' diff --git a/toontown/cogdominium/CogdoFlyingGameGuis.py b/toontown/cogdominium/CogdoFlyingGameGuis.py new file mode 100755 index 00000000..9b91b334 --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingGameGuis.py @@ -0,0 +1,205 @@ +from direct.interval.IntervalGlobal import LerpFunctionInterval +from direct.gui.DirectGui import DirectLabel, DirectFrame, DGG +from direct.showbase.PythonUtil import bound as clamp +from pandac.PandaModules import TextNode, NodePath +from toontown.toonbase import ToontownGlobals +import CogdoUtil +import CogdoFlyingGameGlobals as Globals + +class CogdoFlyingProgressGui(DirectFrame): + + def __init__(self, parent, level, pos2d = Globals.Gui.ProgressPos2D): + DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX) + self._parent = parent + self._level = level + self.reparentTo(self._parent) + self.setPos(pos2d[0], 0.0, pos2d[1]) + self._levelStartY = self._level.startPlatform.getModel().getY() + self._levelEndY = self._level.endPlatform.getModel().getY() + self._levelDistance = abs(self._levelEndY - self._levelStartY) + self._toonMarkers = {} + self._initModel() + return + + def destroy(self): + self._laffMeterModel.removeNode() + del self._laffMeterModel + DirectFrame.destroy(self) + + def _initModel(self): + self._laffMeterModel = loader.loadModel('phase_3/models/gui/laff_o_meter') + self._model = CogdoUtil.loadFlyingModel('progressMeter', group='gui') + self._model.reparentTo(self) + self._model.setBin('fixed', 0) + self._lineStart = self._model.find('**/start_loc').getZ() + self._lineEnd = self._model.find('**/end_loc').getZ() + self._lineDistance = abs(self._lineEnd - self._lineStart) + + def addToon(self, toon): + marker = NodePath('toon_marker-%i' % toon.doId) + marker.reparentTo(self) + self._getToonMarker(toon).copyTo(marker) + marker.setColor(toon.style.getHeadColor()) + if toon.isLocal(): + marker.setScale(Globals.Gui.LocalMarkerScale) + marker.setBin('fixed', 10) + else: + marker.setScale(Globals.Gui.MarkerScale) + marker.setBin('fixed', 5) + marker.flattenStrong() + self._toonMarkers[toon] = marker + + def removeToon(self, toon): + marker = self._toonMarkers.get(toon, None) + if marker is not None: + marker.removeNode() + del self._toonMarkers[toon] + return + + def _getToonMarker(self, toon): + type = self._laffMeterModel.find('**/' + toon.style.getType() + 'head') + if type.isEmpty(): + type = self._laffMeterModel.find('**/bunnyhead') + return type + + def update(self): + for toon, marker in self._toonMarkers.items(): + progress = clamp((toon.getY() - self._levelStartY) / self._levelDistance, self._levelStartY, self._levelEndY) + marker.setZ(clamp(self._lineStart + self._lineDistance * progress, self._lineStart, self._lineEnd)) + + +class CogdoFlyingFuelGui(DirectFrame): + + def __init__(self, parent): + DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX) + self.reparentTo(parent) + self.active = 0 + self._initModel() + self._initIntervals() + return + + def _initModel(self): + self.setPos(Globals.Gui.FuelPos2D[0], 0.0, Globals.Gui.FuelPos2D[1]) + self.gui = CogdoUtil.loadFlyingModel('propellerMeter', group='gui') + self.gui.reparentTo(self) + self.gui.setBin('fixed', 0) + self.healthBar = self.gui.find('**/healthBar') + self.healthBar.setBin('fixed', 1) + self.healthBar.setColor(*Globals.Gui.FuelNormalColor) + bottomBarLocator = self.gui.find('**/bottomOfBar_loc') + bottomBarPos = bottomBarLocator.getPos(render) + topBarLocator = self.gui.find('**/topOfBar_loc') + topBarPos = topBarLocator.getPos(render) + zDist = topBarPos.getZ() - bottomBarPos.getZ() + self.fuelLowIndicator = self.gui.find('**/fuelLowIndicator') + self.fuelLowIndicator.setBin('fixed', 2) + pos = self.fuelLowIndicator.getPos(render) + newPos = pos + newPos.setZ(bottomBarPos.getZ() + zDist * Globals.Gameplay.FuelLowAmt) + self.fuelLowIndicator.setPos(render, newPos) + self.fuelVeryLowIndicator = self.gui.find('**/fuelVeryLowIndicator') + self.fuelVeryLowIndicator.setBin('fixed', 2) + pos = self.fuelVeryLowIndicator.getPos(render) + newPos = pos + newPos.setZ(bottomBarPos.getZ() + zDist * Globals.Gameplay.FuelVeryLowAmt) + self.fuelVeryLowIndicator.setPos(render, newPos) + self.propellerMain = self.gui.find('**/propellers') + self.propellerMain.setBin('fixed', 3) + self.propellerHead = self.gui.find('**/propellerHead') + self.propellerHead.setBin('fixed', 4) + self.blades = [] + self.activeBlades = [] + index = 1 + blade = self.propellerMain.find('**/propeller%d' % index) + while not blade.isEmpty(): + self.blades.append(blade) + index += 1 + blade = self.propellerMain.find('**/propeller%d' % index) + + for blade in self.blades: + self.activeBlades.append(blade) + + self.bladeNumberLabel = DirectLabel(parent=self.propellerHead, relief=None, pos=(Globals.Gui.FuelNumBladesPos2D[0], 0, Globals.Gui.FuelNumBladesPos2D[1]), scale=Globals.Gui.FuelNumBladesScale, text=str(len(self.activeBlades)), text_align=TextNode.ACenter, text_fg=(0.0, + 0.0, + -0.002, + 1), text_shadow=(0.75, 0.75, 0.75, 1), text_font=ToontownGlobals.getInterfaceFont()) + self.bladeNumberLabel.setBin('fixed', 5) + return + + def _initIntervals(self): + self._healthIval = LerpFunctionInterval(self.healthBar.setSz, fromData=0.0, toData=1.0, duration=2.0) + self.baseSpinDuration = 2.0 + self._spinIval = LerpFunctionInterval(self.propellerMain.setR, fromData=0.0, toData=-360.0, duration=self.baseSpinDuration) + + def show(self): + DirectFrame.show(self) + self._spinIval.loop() + + def hide(self): + DirectFrame.hide(self) + self._spinIval.pause() + + def resetBlades(self): + self.setBlades(len(self.blades)) + + def setBlades(self, fuelState): + if fuelState not in Globals.Gameplay.FuelStates: + return + numBlades = fuelState - 1 + if len(self.activeBlades) != numBlades: + for i in xrange(len(self.activeBlades)): + blade = self.activeBlades.pop() + blade.stash() + + if numBlades > len(self.blades): + numBlades = len(self.blades) + for i in xrange(numBlades): + blade = self.blades[i] + self.activeBlades.append(blade) + blade.unstash() + + self.bladeNumberLabel['text'] = str(len(self.activeBlades)) + self.bladeNumberLabel.setText() + self.updateHealthBarColor() + + def bladeLost(self): + if len(self.activeBlades) > 0: + blade = self.activeBlades.pop() + blade.stash() + self.bladeNumberLabel['text'] = str(len(self.activeBlades)) + self.bladeNumberLabel.setText() + self.updateHealthBarColor() + + def updateHealthBarColor(self): + color = Globals.Gui.NumBlades2FuelColor[len(self.activeBlades)] + self.healthBar.setColor(*color) + + def setPropellerSpinRate(self, newRate): + self._spinIval.setPlayRate(newRate) + + def setRefuelLerpFromData(self): + startScale = self.healthBar.getSz() + self._healthIval.fromData = startScale + + def setFuel(self, fuel): + self.fuel = fuel + + def update(self): + self.healthBar.setSz(self.fuel) + + def destroy(self): + self.bladeNumberLabel.removeNode() + self.bladeNumberLabel = None + self._healthIval.clearToInitial() + del self._healthIval + self.healthBar = None + self.fuelLowIndicator = None + self.fuelVeryLowIndicator = None + self.propellerMain = None + self.propellerHead = None + del self.blades[:] + del self.activeBlades[:] + self.gui.detachNode() + self.gui = None + DirectFrame.destroy(self) + return diff --git a/toontown/cogdominium/CogdoFlyingGameMovies.py b/toontown/cogdominium/CogdoFlyingGameMovies.py new file mode 100755 index 00000000..3d32c9cb --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingGameMovies.py @@ -0,0 +1,146 @@ +from pandac.PandaModules import Point3, PlaneNode +from direct.showbase.ShowBase import Plane +from direct.showbase.RandomNumGen import RandomNumGen +from direct.interval.MetaInterval import Sequence, Parallel +from direct.interval.FunctionInterval import Func, Wait +from toontown.toonbase import TTLocalizer +from toontown.toon import Toon, ToonHead, ToonDNA +from toontown.suit import Suit, SuitDNA +import CogdoFlyingGameGlobals as Globals +from CogdoUtil import CogdoGameMovie +import CogdoUtil + +class CogdoFlyingGameIntro(CogdoGameMovie): + + def __init__(self, level, rng): + CogdoGameMovie.__init__(self) + self._level = level + self._rng = RandomNumGen(rng) + self._exit = self._level.getExit() + + def _getRandomLine(self, lineList): + return CogdoUtil.getRandomDialogueLine(lineList, self._rng) + + def displayLine(self, who, text): + self._dialogueLabel.node().setText(text) + self._dialogueLabel.setPos(0.32, 0, -0.724) + if who == 'toon': + self.toonHead.reparentTo(aspect2d) + self.cogHead.reparentTo(hidden) + self._toonDialogueSfx.play() + self.toonHead.setClipPlane(self.clipPlane) + else: + self.toonHead.reparentTo(hidden) + self.cogHead.reparentTo(aspect2d) + self._cogDialogueSfx.play() + self.cogHead.setClipPlane(self.clipPlane) + + def makeSuit(self, suitType): + suit = Suit.Suit() + dna = SuitDNA.SuitDNA() + dna.newSuit(suitType) + suit.setStyle(dna) + suit.isDisguised = 1 + suit.generateSuit() + suit.setScale(1.05, 1.05, 2.05) + suit.setPos(0, 0, -4.4) + suit.reparentTo(self.toonHead) + for part in suit.getHeadParts(): + part.hide() + + suit.loop('neutral') + + def load(self): + CogdoGameMovie.load(self) + self.toonDNA = ToonDNA.ToonDNA() + self.toonDNA.newToonFromProperties('dss', 'ss', 'm', 'm', 2, 0, 2, 2, 1, 8, 1, 8, 1, 14) + self.toonHead = Toon.Toon() + self.toonHead.setDNA(self.toonDNA) + self.makeSuit('sc') + self.toonHead.getGeomNode().setDepthWrite(1) + self.toonHead.getGeomNode().setDepthTest(1) + self.toonHead.loop('neutral') + self.toonHead.setPosHprScale(-0.73, 0, -1.27, 180, 0, 0, 0.18, 0.18, 0.18) + self.toonHead.reparentTo(hidden) + self.toonHead.startBlink() + self.cogHead = Suit.Suit() + self.cogDNA = SuitDNA.SuitDNA() + self.cogDNA.newSuit('le') + self.cogHead.setDNA(self.cogDNA) + self.cogHead.getGeomNode().setDepthWrite(1) + self.cogHead.getGeomNode().setDepthTest(1) + self.cogHead.loop('neutral') + self.cogHead.setPosHprScale(-0.74, 0, -1.79, 180, 0, 0, 0.12, 0.14, 0.14) + self.cogHead.reparentTo(hidden) + self.cogHead.nametag3d.hide() + self.clipPlane = self.toonHead.attachNewNode(PlaneNode('clip')) + self.clipPlane.node().setPlane(Plane(0, 0, 1, 0)) + self.clipPlane.setPos(0, 0, 2.45) + audioMgr = base.cogdoGameAudioMgr + self._cogDialogueSfx = audioMgr.createSfx('cogDialogue') + self._toonDialogueSfx = audioMgr.createSfx('toonDialogue') + + def start(): + camera.wrtReparentTo(render) + self._startUpdateTask() + + def end(): + self._stopUpdateTask() + + introDuration = Globals.Gameplay.IntroDurationSeconds + dialogue = TTLocalizer.CogdoFlyingIntroMovieDialogue + waitDur = introDuration / len(dialogue) + flyDur = introDuration - waitDur * 0.5 + flyThroughIval = Parallel(camera.posInterval(flyDur, self._exit.getPos(render) + Point3(0, -22, 1), blendType='easeInOut'), camera.hprInterval(flyDur, Point3(0, 5, 0), blendType='easeInOut')) + self._ival = Sequence(Func(start), Parallel(flyThroughIval, Sequence(Func(self.displayLine, 'cog', self._getRandomLine(dialogue[0])), Wait(waitDur), Func(self.displayLine, 'toon', self._getRandomLine(dialogue[1])), Wait(waitDur), Func(self.displayLine, 'cog', self._getRandomLine(dialogue[2])), Wait(waitDur))), Func(end)) + + def _updateTask(self, task): + dt = globalClock.getDt() + self._level.update(dt) + return task.cont + + def unload(self): + CogdoGameMovie.unload(self) + del self._cogDialogueSfx + del self._toonDialogueSfx + self.toonHead.stopBlink() + self.toonHead.stop() + self.toonHead.removeNode() + self.toonHead.delete() + del self.toonHead + self.cogHead.stop() + self.cogHead.removeNode() + self.cogHead.delete() + del self.cogHead + self._exit = None + self._level = None + return + + +class CogdoFlyingGameFinish(CogdoGameMovie): + + def __init__(self, level, players): + CogdoGameMovie.__init__(self) + self._exit = level.getExit() + self._players = players + + def load(self): + CogdoGameMovie.load(self) + + def showDoor(): + camera.wrtReparentTo(render) + camera.setPos(self._exit, 0, -55, 40) + camera.lookAt(self._exit, 0, 0, -20) + self._exit.open() + + exitDur = 1.0 + showExitIval = Sequence(Func(camera.wrtReparentTo, render), Parallel(camera.posInterval(exitDur, Point3(0, -55, 40), other=self._exit, blendType='easeInOut'), camera.hprInterval(exitDur, Point3(0, -45, 0), blendType='easeInOut'))) + + def showPlayersLeaving(): + for player in self._players: + self._exit.toonEnters(player.toon) + + self._ival = Sequence(showExitIval, Func(self._exit.open), Func(showPlayersLeaving), Wait(Globals.Gameplay.FinishDurationSeconds - exitDur - 1.0), Func(base.transitions.irisOut), Wait(1.0)) + + def unload(self): + CogdoGameMovie.unload(self) diff --git a/toontown/cogdominium/CogdoFlyingGuiManager.py b/toontown/cogdominium/CogdoFlyingGuiManager.py new file mode 100755 index 00000000..eb31d70b --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingGuiManager.py @@ -0,0 +1,181 @@ +from pandac.PandaModules import NodePath +from toontown.toonbase import ToontownIntervals +from toontown.toonbase.ToontownTimer import ToontownTimer +from CogdoFlyingGameGuis import CogdoFlyingFuelGui, CogdoFlyingProgressGui +from CogdoGameMessageDisplay import CogdoGameMessageDisplay +from CogdoMemoGui import CogdoMemoGui +import CogdoFlyingGameGlobals as Globals + +class CogdoFlyingGuiManager: + ClearMessageDisplayEventName = 'ClearMessageDisplayEvent' + EagleTargetingLocalPlayerEventName = 'EagleTargetingLocalPlayerEvent' + EagleAttackingLocalPlayerEventName = 'EagleAttackingLocalPlayerEvent' + FirstPressOfCtrlEventName = 'FirstPressOfCtrlEvent' + PickedUpFirstPropellerEventName = 'PickedUpFirstPropellerEvent' + InvulnerableEventName = 'InvulnerableEvent' + StartRunningOutOfTimeMusicEventName = 'StartRunningOutOfTimeEvent' + + def __init__(self, level): + self._level = level + self.root = NodePath('CogdoFlyingGui') + self.root.reparentTo(aspect2d) + self.root.stash() + self.fuelGui = NodePath('CogdoFlyingFuelGui') + self.fuelGui.reparentTo(base.a2dBottomLeft) + self.fuelGui.stash() + self.progressGui = NodePath('CogdoFlyingProgressGui') + self.progressGui.reparentTo(base.a2dBottomRight) + self.progressGui.stash() + self._initTimer() + self._initHud() + self._initMessageDisplay() + self.sentTimeRunningOutMessage = False + self._refuelGui = CogdoFlyingFuelGui(self.fuelGui) + self._progressGui = CogdoFlyingProgressGui(self.progressGui, self._level) + + def _initHud(self): + self._memoGui = CogdoMemoGui(self.root, 'memo_card') + self._memoGui.posNextToLaffMeter() + + def _initTimer(self): + self._timer = ToontownTimer() + self._timer.hide() + self._timer.posInTopRightCorner() + + def _initMessageDisplay(self): + audioMgr = base.cogdoGameAudioMgr + sound = audioMgr.createSfx('popupHelpText') + self._messageDisplay = CogdoGameMessageDisplay('CogdoFlyingMessageDisplay', self.root, sfx=sound) + + def destroyTimer(self): + if self._timer is not None: + self._timer.stop() + self._timer.destroy() + self._timer = None + return + + def onstage(self): + self.root.unstash() + self.fuelGui.unstash() + self.progressGui.unstash() + self._refuelGui.hide() + self._progressGui.hide() + + def presentProgressGui(self): + ToontownIntervals.start(ToontownIntervals.getPresentGuiIval(self._progressGui, 'present_progress_gui')) + + def presentRefuelGui(self): + ToontownIntervals.start(ToontownIntervals.getPresentGuiIval(self._refuelGui, 'present_fuel_gui')) + + def presentTimerGui(self): + ToontownIntervals.start(ToontownIntervals.getPresentGuiIval(self._timer, 'present_timer_gui')) + + def presentMemoGui(self): + ToontownIntervals.start(ToontownIntervals.getPresentGuiIval(self._memoGui, 'present_memo_gui')) + + def offstage(self): + self.root.stash() + self.fuelGui.stash() + self.progressGui.stash() + self._refuelGui.hide() + self._progressGui.hide() + self.hideTimer() + + def getTimeLeft(self): + return Globals.Gameplay.SecondsUntilGameOver - self._timer.getElapsedTime() + + def isTimeRunningOut(self): + return self.getTimeLeft() < Globals.Gameplay.TimeRunningOutSeconds + + def startTimer(self, duration, timerExpiredCallback = None, keepHidden = False): + if self._timer is None: + self._initTimer() + self._timer.setTime(duration) + self._timer.countdown(duration, timerExpiredCallback) + if keepHidden: + self.hideTimer() + else: + self.showTimer() + return + + def stopTimer(self): + if hasattr(self, '_timer') and self._timer is not None: + self.hideTimer() + self._timer.stop() + return + + def showTimer(self): + self._timer.show() + + def hideTimer(self): + self._timer.hide() + + def forceTimerDone(self): + if self._timer.countdownTask != None: + self._timer.countdownTask.duration = 0 + return + + def showRefuelGui(self): + self._refuelGui.show() + + def hideRefuelGui(self): + self._refuelGui.hide() + + def setMessage(self, text, color = None, transition = 'fade'): + self._messageDisplay.updateMessage(text, color, transition) + + def setTemporaryMessage(self, text, duration = 3.0, color = None): + self._messageDisplay.showMessageTemporarily(text, duration, color) + + def setFuel(self, fuel): + self._refuelGui.setFuel(fuel) + + def resetBlades(self): + self._refuelGui.resetBlades() + + def setBlades(self, fuelState): + self._refuelGui.setBlades(fuelState) + + def bladeLost(self): + self._refuelGui.bladeLost() + + def setPropellerSpinRate(self, newRate): + self._refuelGui.setPropellerSpinRate(newRate) + + def setMemoCount(self, score): + self._memoGui.setCount(score) + + def addToonToProgressMeter(self, toon): + self._progressGui.addToon(toon) + + def removeToonFromProgressMeter(self, toon): + self._progressGui.removeToon(toon) + + def update(self): + if self.isTimeRunningOut() and not self.sentTimeRunningOutMessage: + messenger.send(CogdoFlyingGuiManager.StartRunningOutOfTimeMusicEventName) + self.sentTimeRunningOutMessage = True + self._refuelGui.update() + self._progressGui.update() + + def destroy(self): + ToontownIntervals.cleanup('present_fuel_gui') + ToontownIntervals.cleanup('present_timer_gui') + ToontownIntervals.cleanup('present_memo_gui') + ToontownIntervals.cleanup('present_progress_gui') + self._refuelGui.destroy() + self._refuelGui = None + self._memoGui.destroy() + self._memoGui = None + self._progressGui.destroy() + self._progressGui = None + self.destroyTimer() + self._messageDisplay.destroy() + self._messageDisplay = None + self.root.removeNode() + self.root = None + self.fuelGui.removeNode() + self.fuelGui = None + self.progressGui.removeNode() + self.progressGui = None + return diff --git a/toontown/cogdominium/CogdoFlyingInputManager.py b/toontown/cogdominium/CogdoFlyingInputManager.py new file mode 100755 index 00000000..d771f4c5 --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingInputManager.py @@ -0,0 +1,42 @@ +from pandac.PandaModules import CollisionSphere, CollisionNode, BitMask32, CollisionHandlerEvent, CollisionRay +from toontown.minigame import ArrowKeys + +class CogdoFlyingInputManager: + + def __init__(self): + self.arrowKeys = ArrowKeys.ArrowKeys() + self.arrowKeys.disable() + + def enable(self): + self.arrowKeys.setPressHandlers([self.__upArrowPressed, + self.__downArrowPressed, + self.__leftArrowPressed, + self.__rightArrowPressed, + self.__controlPressed]) + self.arrowKeys.enable() + + def disable(self): + self.arrowKeys.clearPressHandlers() + self.arrowKeys.disable() + + def destroy(self): + self.disable() + self.arrowKeys.destroy() + self.arrowKeys = None + self.refuelLerp = None + return + + def __upArrowPressed(self): + pass + + def __downArrowPressed(self): + pass + + def __leftArrowPressed(self): + pass + + def __rightArrowPressed(self): + pass + + def __controlPressed(self): + pass diff --git a/toontown/cogdominium/CogdoFlyingLegalEagle.py b/toontown/cogdominium/CogdoFlyingLegalEagle.py new file mode 100755 index 00000000..880259ca --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingLegalEagle.py @@ -0,0 +1,608 @@ +import math +from direct.showbase.DirectObject import DirectObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm.FSM import FSM +from direct.task.Task import Task +from direct.interval.IntervalGlobal import Sequence, Parallel, LerpScaleInterval, LerpFunctionInterval, Func, Wait, LerpFunc, SoundInterval, ParallelEndTogether, LerpPosInterval, ActorInterval, LerpPosHprInterval, LerpHprInterval +from direct.directutil import Mopath +from direct.showbase.PythonUtil import bound as clamp +from pandac.PandaModules import CollisionSphere, CollisionNode, CollisionTube, CollisionPolygon, Vec3, Point3 +from toontown.suit import Suit +from toontown.suit import SuitDNA +from toontown.toonbase import ToontownGlobals +from toontown.battle import BattleProps +from CogdoFlyingUtil import swapAvatarShadowPlacer +import CogdoUtil +import CogdoFlyingGameGlobals as Globals + +class CogdoFlyingLegalEagle(DirectObject, FSM): + CollSphereName = 'CogdoFlyingLegalEagleSphere' + CollisionEventName = 'CogdoFlyingLegalEagleCollision' + InterestCollName = 'CogdoFlyingLegalEagleInterestCollision' + RequestAddTargetEventName = 'CogdoFlyingLegalEagleRequestTargetEvent' + RequestAddTargetAgainEventName = 'CogdoFlyingLegalEagleRequestTargetAgainEvent' + RequestRemoveTargetEventName = 'CogdoFlyingLegalEagleRemoveTargetEvent' + ForceRemoveTargetEventName = 'CogdoFlyingLegalEagleForceRemoveTargetEvent' + EnterLegalEagle = 'CogdoFlyingLegalEagleDamageToon' + ChargingToAttackEventName = 'LegalEagleChargingToAttack' + LockOnToonEventName = 'LegalEagleLockOnToon' + CooldownEventName = 'LegalEagleCooldown' + notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingLegalEagle') + + def __init__(self, nest, index, suitDnaName = 'le'): + FSM.__init__(self, 'CogdoFlyingLegalEagle') + self.defaultTransitions = {'Off': ['Roost'], + 'Roost': ['TakeOff', 'Off'], + 'TakeOff': ['LockOnToon', 'LandOnNest', 'Off'], + 'LockOnToon': ['RetreatToNest', 'ChargeUpAttack', 'Off'], + 'ChargeUpAttack': ['RetreatToNest', 'Attack', 'Off'], + 'Attack': ['RetreatToSky', 'Off'], + 'RetreatToSky': ['Cooldown', 'Off'], + 'Cooldown': ['LockOnToon', 'LandOnNest', 'Off'], + 'RetreatToNest': ['LandOnNest', 'Off'], + 'LandOnNest': ['Roost', 'Off']} + self.index = index + self.nest = nest + self.target = None + self.isEagleInterested = False + self.collSphere = None + self.suit = Suit.Suit() + d = SuitDNA.SuitDNA() + d.newSuit(suitDnaName) + self.suit.setDNA(d) + self.suit.reparentTo(render) + swapAvatarShadowPlacer(self.suit, 'legalEagle-%sShadowPlacer' % index) + self.suit.setPos(self.nest.getPos(render)) + self.suit.setHpr(-180, 0, 0) + self.suit.stash() + self.prop = None + self.attachPropeller() + head = self.suit.find('**/joint_head') + self.interestConeOrigin = self.nest.attachNewNode('fakeHeadNodePath') + self.interestConeOrigin.setPos(render, head.getPos(render) + Vec3(0, Globals.LegalEagle.InterestConeOffset, 0)) + self.attackTargetPos = None + self.startOfRetreatToSkyPos = None + pathModel = CogdoUtil.loadFlyingModel('legalEaglePaths') + self.chargeUpMotionPath = Mopath.Mopath(name='chargeUpMotionPath-%i' % self.index) + self.chargeUpMotionPath.loadNodePath(pathModel.find('**/charge_path')) + self.retreatToSkyMotionPath = Mopath.Mopath(name='retreatToSkyMotionPath-%i' % self.index) + self.retreatToSkyMotionPath.loadNodePath(pathModel.find('**/retreat_path')) + audioMgr = base.cogdoGameAudioMgr + self._screamSfx = audioMgr.createSfx('legalEagleScream', self.suit) + self.initIntervals() + self.suit.nametag3d.stash() + self.suit.nametag.destroy() + return + + def attachPropeller(self): + if self.prop == None: + self.prop = BattleProps.globalPropPool.getProp('propeller') + head = self.suit.find('**/joint_head') + self.prop.reparentTo(head) + return + + def detachPropeller(self): + if self.prop: + self.prop.cleanup() + self.prop.removeNode() + self.prop = None + return + + def _getAnimationIval(self, animName, startFrame = 0, endFrame = None, duration = 1): + if endFrame == None: + self.suit.getNumFrames(animName) - 1 + frames = endFrame - startFrame + frameRate = self.suit.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + ival = Sequence(ActorInterval(self.suit, animName, playRate=playRate)) + return ival + + def initIntervals(self): + dur = Globals.LegalEagle.LiftOffTime + nestPos = self.nest.getPos(render) + airPos = nestPos + Vec3(0.0, 0.0, Globals.LegalEagle.LiftOffHeight) + self.takeOffSeq = Sequence(Parallel(Sequence(Wait(dur * 0.6), LerpPosInterval(self.suit, dur * 0.4, startPos=nestPos, pos=airPos, blendType='easeInOut'))), Wait(1.5), Func(self.request, 'next'), name='%s.takeOffSeq-%i' % (self.__class__.__name__, self.index)) + self.landOnNestPosLerp = LerpPosInterval(self.suit, 1.0, startPos=airPos, pos=nestPos, blendType='easeInOut') + self.landingSeq = Sequence(Func(self.updateLandOnNestPosLerp), Parallel(self.landOnNestPosLerp), Func(self.request, 'next'), name='%s.landingSeq-%i' % (self.__class__.__name__, self.index)) + dur = Globals.LegalEagle.ChargeUpTime + self.chargeUpPosLerp = LerpFunc(self.moveAlongChargeUpMopathFunc, fromData=0.0, toData=self.chargeUpMotionPath.getMaxT(), duration=dur, blendType='easeInOut') + self.chargeUpAttackSeq = Sequence(Func(self.updateChargeUpPosLerp), self.chargeUpPosLerp, Func(self.request, 'next'), name='%s.chargeUpAttackSeq-%i' % (self.__class__.__name__, self.index)) + dur = Globals.LegalEagle.RetreatToNestTime + self.retreatToNestPosLerp = LerpPosInterval(self.suit, dur, startPos=Vec3(0, 0, 0), pos=airPos, blendType='easeInOut') + self.retreatToNestSeq = Sequence(Func(self.updateRetreatToNestPosLerp), self.retreatToNestPosLerp, Func(self.request, 'next'), name='%s.retreatToNestSeq-%i' % (self.__class__.__name__, self.index)) + dur = Globals.LegalEagle.RetreatToSkyTime + self.retreatToSkyPosLerp = LerpFunc(self.moveAlongRetreatMopathFunc, fromData=0.0, toData=self.retreatToSkyMotionPath.getMaxT(), duration=dur, blendType='easeOut') + self.retreatToSkySeq = Sequence(Func(self.updateRetreatToSkyPosLerp), self.retreatToSkyPosLerp, Func(self.request, 'next'), name='%s.retreatToSkySeq-%i' % (self.__class__.__name__, self.index)) + dur = Globals.LegalEagle.PreAttackTime + self.preAttackLerpXY = LerpFunc(self.updateAttackXY, fromData=0.0, toData=1.0, duration=dur) + self.preAttackLerpZ = LerpFunc(self.updateAttackZ, fromData=0.0, toData=1.0, duration=dur, blendType='easeOut') + dur = Globals.LegalEagle.PostAttackTime + self.postAttackPosLerp = LerpPosInterval(self.suit, dur, startPos=Vec3(0, 0, 0), pos=Vec3(0, 0, 0)) + self.attackSeq = Sequence(Parallel(self.preAttackLerpXY, self.preAttackLerpZ), Func(self.updatePostAttackPosLerp), self.postAttackPosLerp, Func(self.request, 'next'), name='%s.attackSeq-%i' % (self.__class__.__name__, self.index)) + dur = Globals.LegalEagle.CooldownTime + self.cooldownSeq = Sequence(Wait(dur), Func(self.request, 'next'), name='%s.cooldownSeq-%i' % (self.__class__.__name__, self.index)) + self.propTrack = Sequence(ActorInterval(self.prop, 'propeller', startFrame=0, endFrame=14)) + self.hoverOverNestSeq = Sequence(ActorInterval(self.suit, 'landing', startFrame=10, endFrame=20, playRate=0.5), ActorInterval(self.suit, 'landing', startFrame=20, endFrame=10, playRate=0.5)) + + def initCollision(self): + self.collSphere = CollisionSphere(0, 0, 0, 0) + self.collSphere.setTangible(0) + self.collNode = CollisionNode('%s-%s' % (self.CollSphereName, self.index)) + self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.suit.attachNewNode(self.collNode) + self.collNodePath.hide() + self.accept('enter%s-%s' % (self.CollSphereName, self.index), self.handleEnterSphere) + self.setCollSphereToNest() + + def getInterestConeLength(self): + return Globals.LegalEagle.InterestConeLength + Globals.LegalEagle.InterestConeOffset + + def isToonInView(self, toon): + distanceThreshold = self.getInterestConeLength() + angleThreshold = Globals.LegalEagle.InterestConeAngle + toonPos = toon.getPos(render) + nestPos = self.nest.getPos(render) + distance = toon.getDistance(self.interestConeOrigin) + if distance > distanceThreshold: + return False + if toonPos[1] > nestPos[1]: + return False + a = toon.getPos(render) - self.interestConeOrigin.getPos(render) + a.normalize() + b = Vec3(0, -1, 0) + dotProduct = a.dot(b) + angle = math.degrees(math.acos(dotProduct)) + if angle <= angleThreshold / 2.0: + return True + else: + return False + + def update(self, dt, localPlayer): + if Globals.Dev.NoLegalEagleAttacks: + return + inView = self.isToonInView(localPlayer.toon) + if inView and not self.isEagleInterested: + self.handleEnterInterest() + elif inView and self.isEagleInterested: + self.handleAgainInterest() + elif not inView and self.isEagleInterested: + self.handleExitInterest() + + def updateLockOnTask(self): + dt = globalClock.getDt() + targetPos = self.target.getPos(render) + suitPos = self.suit.getPos(render) + nestPos = self.nest.getPos(render) + attackPos = Vec3(targetPos) + attackPos[1] = nestPos[1] + Globals.LegalEagle.LockOnDistanceFromNest + attackPos[2] += Globals.LegalEagle.VerticalOffset + if attackPos[2] < nestPos[2]: + attackPos[2] = nestPos[2] + attackChangeVec = (attackPos - suitPos) * Globals.LegalEagle.LockOnSpeed + self.suit.setPos(suitPos + attackChangeVec * dt) + return Task.cont + + def updateAttackXY(self, value): + if Globals.LegalEagle.EagleAttackShouldXCorrect: + x = self.readyToAttackPos.getX() + (self.attackTargetPos.getX() - self.readyToAttackPos.getX()) * value + self.suit.setX(x) + y = self.readyToAttackPos.getY() + (self.attackTargetPos.getY() - self.readyToAttackPos.getY()) * value + self.suit.setY(y) + + def updateAttackZ(self, value): + z = self.readyToAttackPos.getZ() + (self.attackTargetPos.getZ() - self.readyToAttackPos.getZ()) * value + self.suit.setZ(z) + + def moveAlongChargeUpMopathFunc(self, value): + self.chargeUpMotionPath.goTo(self.suit, value) + self.suit.setPos(self.suit.getPos() + self.startOfChargeUpPos) + + def moveAlongRetreatMopathFunc(self, value): + self.retreatToSkyMotionPath.goTo(self.suit, value) + self.suit.setPos(self.suit.getPos() + self.startOfRetreatToSkyPos) + + def updateChargeUpPosLerp(self): + self.startOfChargeUpPos = self.suit.getPos(render) + + def updateLandOnNestPosLerp(self): + self.landOnNestPosLerp.setStartPos(self.suit.getPos()) + + def updateRetreatToNestPosLerp(self): + self.retreatToNestPosLerp.setStartPos(self.suit.getPos()) + + def updateRetreatToSkyPosLerp(self): + self.startOfRetreatToSkyPos = self.suit.getPos(render) + + def updatePostAttackPosLerp(self): + suitPos = self.suit.getPos(render) + finalPos = suitPos + Vec3(0, -Globals.LegalEagle.PostAttackLength, 0) + self.postAttackPosLerp.setStartPos(suitPos) + self.postAttackPosLerp.setEndPos(finalPos) + + def handleEnterSphere(self, collEntry): + self.notify.debug('handleEnterSphere:%i' % self.index) + messenger.send(CogdoFlyingLegalEagle.EnterLegalEagle, [self, collEntry]) + + def handleEnterInterest(self): + self.notify.debug('handleEnterInterestColl:%i' % self.index) + self.isEagleInterested = True + messenger.send(CogdoFlyingLegalEagle.RequestAddTargetEventName, [self.index]) + + def handleAgainInterest(self): + self.isEagleInterested = True + messenger.send(CogdoFlyingLegalEagle.RequestAddTargetAgainEventName, [self.index]) + + def handleExitInterest(self): + self.notify.debug('handleExitInterestSphere:%i' % self.index) + self.isEagleInterested = False + messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [self.index]) + + def hasTarget(self): + if self.target != None: + return True + else: + return False + return + + def setTarget(self, toon, elapsedTime = 0.0): + self.notify.debug('Setting eagle %i to target: %s, elapsed time: %s' % (self.index, toon.getName(), elapsedTime)) + self.target = toon + if self.state == 'Roost': + self.request('next', elapsedTime) + if self.state == 'ChargeUpAttack': + messenger.send(CogdoFlyingLegalEagle.ChargingToAttackEventName, [self.target.doId]) + + def clearTarget(self, elapsedTime = 0.0): + self.notify.debug('Clearing target from eagle %i, elapsed time: %s' % (self.index, elapsedTime)) + messenger.send(CogdoFlyingLegalEagle.CooldownEventName, [self.target.doId]) + self.target = None + if self.state in ['LockOnToon']: + self.request('next', elapsedTime) + return + + def leaveCooldown(self, elapsedTime = 0.0): + if self.state in ['Cooldown']: + self.request('next', elapsedTime) + + def shouldBeInFrame(self): + if self.state in ['TakeOff', 'LockOnToon', 'ChargeUpAttack']: + return True + elif self.state == 'Attack': + distance = self.suit.getDistance(self.target) + threshold = Globals.LegalEagle.EagleAndTargetDistCameraTrackThreshold + suitPos = self.suit.getPos(render) + targetPos = self.target.getPos(render) + if distance > threshold and suitPos[1] > targetPos[1]: + return True + return False + + def getTarget(self): + return self.target + + def onstage(self): + self.suit.unstash() + self.request('Roost') + + def offstage(self): + self.suit.stash() + self.request('Off') + + def gameStart(self, gameStartTime): + self.gameStartTime = gameStartTime + self.initCollision() + + def gameEnd(self): + self.shutdownCollisions() + + def shutdownCollisions(self): + self.ignoreAll() + if self.collSphere != None: + del self.collSphere + self.collSphere = None + if self.collNodePath != None: + self.collNodePath.removeNode() + del self.collNodePath + self.collNodePath = None + if self.collNode != None: + del self.collNode + self.collNode = None + return + + def destroy(self): + self.request('Off') + self.detachPropeller() + del self._screamSfx + self.suit.cleanup() + self.suit.removeNode() + self.suit.delete() + self.interestConeOrigin.removeNode() + del self.interestConeOrigin + self.nest = None + self.target = None + taskMgr.remove('updateLockOnTask-%i' % self.index) + taskMgr.remove('exitLockOnToon-%i' % self.index) + self.propTrack.clearToInitial() + del self.propTrack + del self.chargeUpMotionPath + del self.retreatToSkyMotionPath + self.takeOffSeq.clearToInitial() + del self.takeOffSeq + del self.landOnNestPosLerp + self.landingSeq.clearToInitial() + del self.landingSeq + del self.chargeUpPosLerp + self.chargeUpAttackSeq.clearToInitial() + del self.chargeUpAttackSeq + del self.retreatToNestPosLerp + self.retreatToNestSeq.clearToInitial() + del self.retreatToNestSeq + del self.retreatToSkyPosLerp + self.retreatToSkySeq.clearToInitial() + del self.retreatToSkySeq + del self.postAttackPosLerp + self.attackSeq.clearToInitial() + del self.attackSeq + self.cooldownSeq.clearToInitial() + del self.cooldownSeq + self.hoverOverNestSeq.clearToInitial() + del self.hoverOverNestSeq + del self.preAttackLerpXY + del self.preAttackLerpZ + return + + def requestNext(self): + self.request('next') + + def setCollSphereToNest(self): + if hasattr(self, 'collSphere') and self.collSphere is not None: + radius = Globals.LegalEagle.OnNestDamageSphereRadius + self.collSphere.setCenter(Point3(0.0, -Globals.Level.LaffPowerupNestOffset[1], self.suit.getHeight() / 2.0)) + self.collSphere.setRadius(radius) + return + + def setCollSphereToTargeting(self): + if hasattr(self, 'collSphere') and self.collSphere is not None: + radius = Globals.LegalEagle.DamageSphereRadius + self.collSphere.setCenter(Point3(0, 0, radius * 2)) + self.collSphere.setRadius(radius) + return + + def enterRoost(self): + self.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + self.hoverOverNestSeq.loop() + self.propTrack.loop() + self.setCollSphereToNest() + + def filterRoost(self, request, args): + self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) + if request == self.state: + return None + elif request == 'next': + return 'TakeOff' + else: + return self.defaultFilter(request, args) + return None + + def exitRoost(self): + self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + self.hoverOverNestSeq.pause() + self.setCollSphereToTargeting() + + def enterTakeOff(self, elapsedTime = 0.0): + self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, + self.oldState, + self.newState, + elapsedTime)) + self.takeOffSeq.start(elapsedTime) + self.hoverOverNestSeq.loop() + + def filterTakeOff(self, request, args): + self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) + if request == self.state: + return None + elif request == 'next': + if self.hasTarget(): + return 'LockOnToon' + else: + return 'LandOnNest' + else: + return self.defaultFilter(request, args) + return None + + def exitTakeOff(self): + self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + self.takeOffSeq.clearToInitial() + self.hoverOverNestSeq.pause() + + def enterLockOnToon(self, elapsedTime = 0.0): + self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, + self.oldState, + self.newState, + elapsedTime)) + taskName = 'updateLockOnTask-%i' % self.index + taskMgr.add(self.updateLockOnTask, taskName, 45, extraArgs=[]) + messenger.send(CogdoFlyingLegalEagle.LockOnToonEventName, [self.target.doId]) + range = self.target.getDistance(self.interestConeOrigin) / self.getInterestConeLength() + range = clamp(range, 0.0, 1.0) + dur = Globals.LegalEagle.LockOnTime + if self.oldState == 'TakeOff': + dur *= range + else: + dur += Globals.LegalEagle.ExtraPostCooldownTime + taskName = 'exitLockOnToon-%i' % self.index + taskMgr.doMethodLater(dur, self.requestNext, taskName, extraArgs=[]) + + def filterLockOnToon(self, request, args): + self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) + if request == self.state: + return None + elif request == 'next': + if self.hasTarget(): + return 'ChargeUpAttack' + else: + return 'RetreatToNest' + else: + return self.defaultFilter(request, args) + return None + + def exitLockOnToon(self): + self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + taskMgr.remove('updateLockOnTask-%i' % self.index) + taskMgr.remove('exitLockOnToon-%i' % self.index) + + def enterChargeUpAttack(self, elapsedTime = 0.0): + self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, + self.oldState, + self.newState, + elapsedTime)) + self.chargeUpAttackSeq.start(elapsedTime) + messenger.send(CogdoFlyingLegalEagle.ChargingToAttackEventName, [self.target.doId]) + + def filterChargeUpAttack(self, request, args): + self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) + if request == self.state: + return None + elif request == 'next': + if self.hasTarget(): + return 'Attack' + else: + return 'RetreatToNest' + else: + return self.defaultFilter(request, args) + return None + + def exitChargeUpAttack(self): + self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + self.chargeUpAttackSeq.clearToInitial() + + def enterAttack(self, elapsedTime = 0.0): + self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, + self.oldState, + self.newState, + elapsedTime)) + self.attackTargetPos = self.target.getPos(render) + targetState = self.target.animFSM.getCurrentState().getName() + self._screamSfx.play() + if targetState == 'jumpAirborne': + self.attackTargetPos[2] += Globals.LegalEagle.VerticalOffset + else: + self.attackTargetPos[2] += Globals.LegalEagle.PlatformVerticalOffset + self.readyToAttackPos = self.suit.getPos(render) + self.attackSeq.start(elapsedTime) + + def filterAttack(self, request, args): + self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) + if request == self.state: + return None + elif request == 'next': + return 'RetreatToSky' + else: + return self.defaultFilter(request, args) + return None + + def exitAttack(self): + self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + self.attackSeq.clearToInitial() + taskMgr.remove('updateAttackPosTask-%i' % self.index) + + def enterRetreatToSky(self, elapsedTime = 0.0): + self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, + self.oldState, + self.newState, + elapsedTime)) + self.retreatToSkySeq.start(elapsedTime) + + def filterRetreatToSky(self, request, args): + self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) + if request == self.state: + return None + elif request == 'next': + return 'Cooldown' + else: + return self.defaultFilter(request, args) + return None + + def exitRetreatToSky(self): + self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + self.retreatToSkySeq.clearToInitial() + + def enterCooldown(self): + if self.target != None: + messenger.send(CogdoFlyingLegalEagle.CooldownEventName, [self.target.doId]) + self.suit.stash() + self.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + return + + def filterCooldown(self, request, args): + self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) + if request == self.state: + return None + elif request == 'next': + if self.hasTarget(): + return 'LockOnToon' + else: + return 'LandOnNest' + else: + return self.defaultFilter(request, args) + return None + + def exitCooldown(self): + self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + self.suit.unstash() + self.cooldownSeq.clearToInitial() + if self.newState != 'Off': + heightOffNest = Globals.LegalEagle.PostCooldownHeightOffNest + nestPos = self.nest.getPos(render) + if self.newState in ['LandOnNest']: + self.suit.setPos(nestPos + Vec3(0, 0, heightOffNest)) + else: + targetPos = self.target.getPos(render) + attackPos = Vec3(targetPos) + attackPos[1] = nestPos[1] + attackPos[2] = nestPos[2] + heightOffNest + self.suit.setPos(attackPos) + + def enterRetreatToNest(self, elapsedTime = 0.0): + self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, + self.oldState, + self.newState, + elapsedTime)) + self.retreatToNestSeq.start(elapsedTime) + + def filterRetreatToNest(self, request, args): + self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) + if request == self.state: + return None + elif request == 'next': + return 'LandOnNest' + else: + return self.defaultFilter(request, args) + return None + + def exitRetreatToNest(self): + self.retreatToNestSeq.clearToInitial() + + def enterLandOnNest(self, elapsedTime = 0.0): + self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, + self.oldState, + self.newState, + elapsedTime)) + self.landingSeq.start(elapsedTime) + + def filterLandOnNest(self, request, args): + self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) + if request == self.state: + return None + elif request == 'next': + if self.hasTarget(): + return 'TakeOff' + else: + return 'Roost' + else: + return self.defaultFilter(request, args) + return None + + def exitLandOnNest(self): + self.landingSeq.clearToInitial() diff --git a/toontown/cogdominium/CogdoFlyingLevel.py b/toontown/cogdominium/CogdoFlyingLevel.py new file mode 100755 index 00000000..288ff719 --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingLevel.py @@ -0,0 +1,243 @@ +from pandac.PandaModules import NodePath, Plane, Vec3, Point3 +from pandac.PandaModules import CollisionPlane, CollisionNode +from direct.showbase.RandomNumGen import RandomNumGen +from direct.showbase.DirectObject import DirectObject +from direct.showbase.PythonUtil import bound as clamp +import CogdoUtil +import CogdoFlyingGameGlobals as Globals +from CogdoFlyingLevelQuadrant import CogdoFlyingLevelQuadrant +from CogdoFlyingObjects import CogdoFlyingGatherableFactory, CogdoFlyingPlatform, CogdoFlyingLevelFog +from CogdoFlyingObstacles import CogdoFlyingObstacleFactory +from CogdoGameExit import CogdoGameExit +from otp.otpbase import OTPGlobals + +class CogdoFlyingLevel(DirectObject): + notify = directNotify.newCategory('CogdoFlyingLevel') + + def __init__(self, parent, frameModel, startPlatformModel, endPlatformModel, quadLengthUnits, quadVisibilityAhead, quadVisibiltyBehind): + self.parent = parent + self.quadLengthUnits = quadLengthUnits + self._halfQuadLengthUnits = quadLengthUnits / 2.0 + self.quadVisibiltyAhead = quadVisibilityAhead + self.quadVisibiltyBehind = quadVisibiltyBehind + self._frameModel = frameModel + self.root = NodePath('CogdoFlyingLevel') + self.quadrantRoot = NodePath('QuadrantsRoot') + self.quadrantRoot.reparentTo(self.root) + self._startPlatformModel = startPlatformModel + self._startPlatformModel.reparentTo(self.root) + self._startPlatformModel.setZ(Globals.Level.StartPlatformHeight) + self._endPlatformModel = endPlatformModel + self._endPlatformModel.reparentTo(self.root) + self._endPlatformModel.setZ(Globals.Level.EndPlatformHeight) + self.wallR = self._frameModel.find('**/wallR') + self.wallL = self._frameModel.find('**/wallL') + self._exit = CogdoGameExit() + self._exit.reparentTo(self._endPlatformModel) + loc = self._endPlatformModel.find('**/exit_loc') + offset = loc.getPos(render) + self._exit.setPos(render, offset) + self.quadrants = [] + self.visibleQuadIndices = [] + self._numQuads = 0 + self._currentQuadNum = -1 + self._camera = None + self._initCollisions() + self.upLimit = self._frameModel.find('**/limit_up').getZ(render) + self.downLimit = self._frameModel.find('**/limit_down').getZ(render) + self.leftLimit = self._frameModel.find('**/limit_left').getX(render) - 30.0 + self.rightLimit = self._frameModel.find('**/limit_right').getX(render) + 30.0 + self.backLimit = -self.quadLengthUnits + self.forwardLimit = self.quadLengthUnits * 20 + self._frameModel.flattenStrong() + self.gatherableFactory = CogdoFlyingGatherableFactory() + self.obstacleFactory = CogdoFlyingObstacleFactory() + return + + def getExit(self): + return self._exit + + def getBounds(self): + return ((self.leftLimit, self.rightLimit), (self.backLimit, self.forwardLimit), (self.downLimit, self.upLimit)) + + def getGatherable(self, serialNum): + for quadrant in self.quadrants: + for gatherable in quadrant.gatherables: + if gatherable.serialNum == serialNum: + return gatherable + + return None + + def ready(self): + self.gatherableFactory.destroy() + del self.gatherableFactory + self.obstacleFactory.destroy() + del self.obstacleFactory + self._initStartEndPlatforms() + self._frameModel.reparentTo(self.root) + self.root.reparentTo(self.parent) + self.root.stash() + + def _initStartEndPlatforms(self): + self.startPlatform = CogdoFlyingPlatform(self._startPlatformModel, Globals.Level.PlatformTypes.StartPlatform) + self.endPlatform = CogdoFlyingPlatform(self._endPlatformModel, Globals.Level.PlatformTypes.EndPlatform) + self._endPlatformModel.setY(self.convertQuadNumToY(self._numQuads)) + self.backLimit = self._startPlatformModel.getY(render) - Globals.Level.StartPlatformLength * 0.7 + self.forwardLimit = self._endPlatformModel.getY(render) + Globals.Level.EndPlatformLength * 0.7 + + def _initCollisions(self): + self.collPlane = CollisionPlane(Plane(Vec3(0, 0, 1.0), Point3(0, 0, 10))) + self.collPlane.setTangible(0) + self.collNode = CollisionNode('fogPlane') + self.collNode.setIntoCollideMask(OTPGlobals.FloorBitmask) + self.collNode.addSolid(self.collPlane) + self.collNodePath = self.root.attachNewNode(self.collNode) + self.collNodePath.hide() + + def destroy(self): + del self.collPlane + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + for quadrant in self.quadrants: + quadrant.destroy() + + self._exit.destroy() + del self._exit + self.root.removeNode() + del self.root + + def onstage(self): + self.root.unstash() + self.update(0.0) + + def offstage(self): + self.root.stash() + + def start(self, startTime = 0.0): + self._startTime = startTime + + def stop(self): + pass + + def getLength(self): + return self.quadLengthUnits * self.getNumQuadrants() + + def appendQuadrant(self, model): + quadrant = CogdoFlyingLevelQuadrant(self._numQuads, model, self, self.root) + if self._numQuads == 0: + quadrant.generateGatherables(self._startPlatformModel) + quadrant.offstage() + self.quadrants.append(quadrant) + self._numQuads = len(self.quadrants) + + def getNumQuadrants(self): + return self._numQuads + + def setCamera(self, camera): + self._camera = camera + + def getCameraActualQuadrant(self): + camY = self._camera.getY(render) + y = self.root.getY(render) + return self.convertYToQuadNum(camY - y) + + def update(self, dt = 0.0): + if self._camera is None: + return + quadNum = clamp(self.getCameraActualQuadrant(), 0, self._numQuads - 1) + if quadNum < self._numQuads: + self.quadrants[quadNum].update(dt) + if quadNum + 1 < self._numQuads: + self.quadrants[quadNum + 1].update(dt) + if quadNum != self._currentQuadNum: + self._switchToQuadrant(quadNum) + return + + def _switchToQuadrant(self, quadNum): + self.visibleQuadIndices = [] + if quadNum >= 0: + if quadNum > 0: + self.quadrants[max(quadNum - self.quadVisibiltyBehind, 0)].onstage() + for i in xrange(quadNum, min(quadNum + self.quadVisibiltyAhead + 1, self._numQuads)): + self.quadrants[i].onstage() + self.visibleQuadIndices.append(i) + if i == 0: + self.startPlatform.onstage() + elif i == self._numQuads - 1: + self.endPlatform.onstage() + + self._currentQuadNum = quadNum + for i in range(0, max(self._currentQuadNum - self.quadVisibiltyBehind, 0)) + range(min(self._currentQuadNum + self.quadVisibiltyAhead + 1, self._numQuads), self._numQuads): + self.quadrants[i].offstage() + if i == 0: + self.startPlatform.offstage() + elif i == self._numQuads - 1: + self.endPlatform.offstage() + + def convertQuadNumToY(self, quadNum): + return quadNum * self.quadLengthUnits + + def convertYToQuadNum(self, y): + return int(y / self.quadLengthUnits) + + def convertCenterYToQuadNum(self, y): + return self.convertYToQuadNum(y + self._halfQuadLengthUnits) + + +class CogdoFlyingLevelFactory: + + def __init__(self, parent, quadLengthUnits, quadVisibilityAhead, quadVisibiltyBehind, rng = None): + self.parent = parent + self.quadLengthUnits = quadLengthUnits + self.quadVisibiltyAhead = quadVisibilityAhead + self.quadVisibiltyBehind = quadVisibiltyBehind + self._rng = rng or RandomNumGen(1) + self._level = None + return + + def loadAndBuildLevel(self, safezoneId): + levelNode = NodePath('level') + frameModel = CogdoUtil.loadFlyingModel('level') + startPlatformModel = CogdoUtil.loadFlyingModel('levelStart') + endPlatformModel = CogdoUtil.loadFlyingModel('levelEnd') + for fan in frameModel.findAllMatches('**/*wallFan'): + fan.flattenStrong() + + frameModel.find('**/fogOpaque').setBin('background', 1) + frameModel.find('**/ceiling').setBin('background', 2) + frameModel.find('**/fogTranslucent_bm').setBin('fixed', 1) + frameModel.find('**/wallR').setBin('opaque', 2) + frameModel.find('**/wallL').setBin('opaque', 2) + frameModel.find('**/fogTranslucent_top').setBin('fixed', 2) + frameModel.getChildren().reparentTo(levelNode) + levelNode.hide() + self._level = CogdoFlyingLevel(self.parent, levelNode, startPlatformModel, endPlatformModel, self.quadLengthUnits, self.quadVisibiltyAhead, self.quadVisibiltyBehind) + if Globals.Dev.WantTempLevel: + quads = Globals.Dev.DevQuadsOrder + else: + levelInfo = Globals.Level.DifficultyOrder[safezoneId] + quads = [] + for difficulty in levelInfo: + quadList = Globals.Level.QuadsByDifficulty[difficulty] + quads.append(quadList[self._rng.randint(0, len(quadList) - 1)]) + + for i in quads: + filePath = CogdoUtil.getModelPath('quadrant%i' % i, 'flying') + quadModel = loader.loadModel(filePath) + for np in quadModel.findAllMatches('**/*lightCone*'): + CogdoUtil.initializeLightCone(np, 'fixed', 3) + + self._level.appendQuadrant(quadModel) + + self._level.ready() + + def createLevel(self, safezoneId = 2000): + if self._level is None: + self.loadAndBuildLevel(safezoneId) + return self._level + + def createLevelFog(self): + if self._level is None: + self.loadAndBuildLevel() + return CogdoFlyingLevelFog(self._level) diff --git a/toontown/cogdominium/CogdoFlyingLevelQuadrant.py b/toontown/cogdominium/CogdoFlyingLevelQuadrant.py new file mode 100755 index 00000000..b2635d7a --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingLevelQuadrant.py @@ -0,0 +1,265 @@ +import math +from direct.directutil import Mopath +from pandac.PandaModules import NodePath, Point3, Vec4 +from CogdoFlyingObjects import CogdoFlyingPlatform +import CogdoFlyingGameGlobals as Globals + +class CogdoFlyingLevelQuadrant: + notify = directNotify.newCategory('CogdoFlyingLevelQuadrant') + + def __init__(self, serialNum, model, level, parent): + self.serialNum = serialNum + self._model = model + self._level = level + self._root = NodePath('Quadrant' + `serialNum`) + self._model.reparentTo(self._root) + self._root.reparentTo(parent) + self._visible = True + self.platforms = {} + self.gatherables = [] + self.obstacles = [] + self._playing = False + self._obstaclesRoot = NodePath('obstacles') + self._obstaclesRoot.reparentTo(self._root) + self._initObstacles(self._obstaclesRoot) + self._gatherablesRoot = NodePath('gatherables') + self._gatherablesRoot.reparentTo(self._root) + self._initGatherables(self._gatherablesRoot) + self._platformsRoot = NodePath('platforms') + self._platformsRoot.reparentTo(self._model) + self._initPlatforms(self._platformsRoot) + self._optimize() + self.place() + + def _optimize(self): + lightCones = NodePath('lightCones') + for np in self._platformsRoot.findAllMatches('**/*lightCone*'): + np.wrtReparentTo(lightCones) + + lightCones.reparentTo(self._model) + node = self._model.find('**/ducts') + if not node.isEmpty(): + node.flattenStrong() + for np in node.getChildren(): + np.wrtReparentTo(self._model) + + node = self._model.find('**/nests') + if not node.isEmpty(): + for np in node.getChildren(): + np.flattenStrong() + np.wrtReparentTo(self._model) + + for np in self._model.findAllMatches('**/*LayerStack*'): + np.wrtReparentTo(self._model) + + if not self._model.find('**/static').isEmpty(): + for np in self._model.find('**/static').getChildren(): + np.wrtReparentTo(self._model) + + self._model.flattenMedium() + + def _initPlatforms(self, parent): + platformModels = self._model.findAllMatches('**/%s' % Globals.Level.PlatformName) + for platformModel in platformModels: + platform = CogdoFlyingPlatform(platformModel, parent=parent) + self.platforms[platform.getName()] = platform + + def _destroyPlatforms(self): + for platform in self.platforms.values(): + platform.destroy() + + del self.platforms + + def _initGatherables(self, parent): + self.generateGatherables(self._model, parent=parent) + if Globals.Level.SpawnLaffPowerupsInNests: + self.generateNestPowerups(self._model, parent=parent) + + def generateNestPowerups(self, gatherableModel, parent): + nests = gatherableModel.findAllMatches('**/%s;+s' % Globals.Level.LegalEagleNestName) + for nest in nests: + offset = Globals.Level.LaffPowerupNestOffset + pickup = self._level.gatherableFactory.createPowerup(Globals.Level.GatherableTypes.LaffPowerup) + pickup.reparentTo(parent) + pickup.setPos(parent, nest.getPos(parent) + offset) + if Globals.Level.AddSparkleToPowerups: + sparkles = self._level.gatherableFactory.createSparkles(Vec4(1, 1, 1, 1), Vec4(1, 1, 0, 1), 10.0) + sparkles.reparentTo(pickup) + sparkles.setPos(0, 0, 1) + sparkles.start() + self.gatherables.append(pickup) + + def generateGatherables(self, gatherableModel, parent = None, spread = Globals.Level.GatherablesDefaultSpread): + parent = parent or self._root + mopath = Mopath.Mopath(name='gatherables') + + def generateMemos(): + gatherPaths = gatherableModel.findAllMatches('**/%s' % Globals.Level.GatherablesPathName) + for gatherPath in gatherPaths: + mopath.loadNodePath(gatherPath) + t = 0.0 + while t < mopath.getMaxT(): + pickup = self._level.gatherableFactory.createMemo() + pickup.reparentTo(parent) + mopath.goTo(pickup, t) + self.gatherables.append(pickup) + t += spread + + gatherPath.removeNode() + + angleSpread = 360.0 / Globals.Level.NumMemosInRing + gatherPaths = gatherableModel.findAllMatches('**/%s' % Globals.Level.GatherablesRingName) + for gatherPath in gatherPaths: + mopath.loadNodePath(gatherPath) + t = 0.0 + while t < mopath.getMaxT(): + angle = 0 + r = 3 + while angle < 360.0: + pickup = self._level.gatherableFactory.createMemo() + pickup.reparentTo(parent) + mopath.goTo(pickup, t) + pickup.setX(parent, pickup.getX() + r * math.cos(math.radians(angle))) + pickup.setZ(parent, pickup.getZ() + r * math.sin(math.radians(angle))) + self.gatherables.append(pickup) + angle += angleSpread + + t += spread + 0.5 + + gatherPath.removeNode() + + def generatePropellers(): + gatherables = gatherableModel.findAllMatches('**/%s' % Globals.Level.PropellerName) + for gatherable in gatherables: + pickup = self._level.gatherableFactory.createPropeller() + pickup.reparentTo(gatherable.getParent()) + pickup.setPos(parent, gatherable.getPos(parent)) + self.gatherables.append(pickup) + gatherable.removeNode() + + def generatePowerUps(): + for powerupType, locName in Globals.Level.PowerupType2Loc.iteritems(): + gatherables = gatherableModel.findAllMatches('**/%s' % locName) + for gatherable in gatherables: + pickup = self._level.gatherableFactory.createPowerup(powerupType) + pickup.reparentTo(parent) + pickup.setPos(parent, gatherable.getPos(parent)) + if Globals.Level.AddSparkleToPowerups: + sparkles = self._level.gatherableFactory.createSparkles(Vec4(1, 1, 1, 1), Vec4(1, 1, 0, 1), 10.0) + sparkles.reparentTo(pickup) + sparkles.setPos(0, 0, 1) + sparkles.start() + self.gatherables.append(pickup) + gatherable.removeNode() + + generateMemos() + generatePropellers() + generatePowerUps() + + def _initObstacles(self, parent): + + def initWhirlwinds(): + obstacles = self._root.findAllMatches('**/%s' % Globals.Level.WhirlwindName) + for obstacleLoc in obstacles: + motionPath = self._model.find('**/%s%s' % (obstacleLoc.getName(), Globals.Level.WhirlwindPathName)) + if motionPath.isEmpty(): + motionPath = None + obstacle = self._level.obstacleFactory.createWhirlwind(motionPath=motionPath) + obstacle.model.reparentTo(parent) + obstacle.model.setPos(parent, obstacleLoc.getPos(parent)) + self.obstacles.append(obstacle) + obstacleLoc.removeNode() + + return + + def initStreamers(): + obstacles = self._model.findAllMatches('**/%s' % Globals.Level.StreamerName) + for obstacleLoc in obstacles: + obstacle = self._level.obstacleFactory.createFan() + obstacle.model.reparentTo(parent) + obstacle.model.setPos(parent, obstacleLoc.getPos(parent)) + obstacle.model.setHpr(parent, obstacleLoc.getHpr(parent)) + obstacle.model.setScale(parent, obstacleLoc.getScale(parent)) + obstacle.setBlowDirection() + if Globals.Level.AddParticlesToStreamers: + particles = self._level.obstacleFactory.createStreamerParticles(Vec4(1, 1, 1, 1), Vec4(1, 1, 1, 1), 10.0) + particles.reparentTo(obstacle.model) + particles.start() + self.obstacles.append(obstacle) + obstacleLoc.removeNode() + + def initWalkingMinions(): + motionPaths = self._model.findAllMatches('**/%s' % Globals.Level.MinionWalkingPathName) + for motionPath in motionPaths: + obstacle = self._level.obstacleFactory.createWalkingMinion(motionPath=motionPath) + obstacle.model.reparentTo(parent) + obstacle.model.setPos(parent, motionPath.getPos(parent)) + self.obstacles.append(obstacle) + + def initFlyingMinions(): + motionPaths = self._model.findAllMatches('**/%s' % Globals.Level.MinionFlyingPathName) + for motionPath in motionPaths: + obstacle = self._level.obstacleFactory.createFlyingMinion(motionPath=motionPath) + obstacle.model.reparentTo(parent) + obstacle.model.setPos(parent, motionPath.getPos(parent)) + self.obstacles.append(obstacle) + + initWhirlwinds() + initStreamers() + initWalkingMinions() + initFlyingMinions() + + def place(self): + self._root.setPos(0, self._level.convertQuadNumToY(self.serialNum), 0) + + def destroy(self): + if self._visible: + self.offstage() + self._destroyPlatforms() + for obstacle in self.obstacles: + obstacle.destroy() + + for gatherable in self.gatherables: + gatherable.destroy() + + self._root.removeNode() + del self._root + del self._gatherablesRoot + del self._obstaclesRoot + del self._platformsRoot + del self._level + + def onstage(self, elapsedTime = 0.0): + if self._visible: + return + self._root.unstash() + for obstacle in self.obstacles: + obstacle.startMoving(elapsedTime) + + for gatherable in self.gatherables: + gatherable.show() + + self._visible = True + + def offstage(self): + if not self._visible: + return + self._root.stash() + for obstacle in self.obstacles: + obstacle.stopMoving() + + for gatherable in self.gatherables: + gatherable.hide() + + self._visible = False + + def update(self, dt): + if self._visible: + for gatherable in self.gatherables: + gatherable.update(dt) + + for obstacle in self.obstacles: + obstacle.update(dt) + + def getModel(self): + return self._root diff --git a/toontown/cogdominium/CogdoFlyingLocalPlayer.py b/toontown/cogdominium/CogdoFlyingLocalPlayer.py new file mode 100755 index 00000000..f976824b --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingLocalPlayer.py @@ -0,0 +1,1110 @@ +import math +import random +from pandac.PandaModules import Vec3 +from direct.showbase import PythonUtil +from direct.directnotify import DirectNotifyGlobal +from direct.task.Task import Task +from direct.interval.FunctionInterval import Wait +from direct.interval.IntervalGlobal import Func, LerpFunc, LerpPosInterval, LerpHprInterval, LerpFunctionInterval +from direct.interval.MetaInterval import Sequence, Parallel +from direct.showbase.PythonUtil import bound as clamp +from direct.distributed.ClockDelta import globalClockDelta +from otp.otpbase import OTPGlobals +from toontown.minigame.OrthoDrive import OrthoDrive +from toontown.minigame.OrthoWalk import OrthoWalk +from toontown.toonbase import TTLocalizer +from CogdoFlyingCollisions import CogdoFlyingCollisions +from CogdoFlyingPlayer import CogdoFlyingPlayer +from CogdoFlyingGuiManager import CogdoFlyingGuiManager +from CogdoFlyingInputManager import CogdoFlyingInputManager +from CogdoFlyingCameraManager import CogdoFlyingCameraManager +from CogdoFlyingObjects import CogdoFlyingPlatform, CogdoFlyingGatherable +from CogdoFlyingLegalEagle import CogdoFlyingLegalEagle +import CogdoFlyingGameGlobals as Globals + +class CogdoFlyingLocalPlayer(CogdoFlyingPlayer): + notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingLocalPlayer') + BroadcastPosTask = 'CogdoFlyingLocalPlayerBroadcastPos' + PlayWaitingMusicEventName = 'PlayWaitingMusicEvent' + RanOutOfTimeEventName = 'RanOutOfTimeEvent' + PropStates = PythonUtil.Enum(('Normal', 'Overdrive', 'Off')) + + def __init__(self, toon, game, level, guiMgr): + CogdoFlyingPlayer.__init__(self, toon) + self.defaultTransitions = {'Inactive': ['FreeFly', 'Running'], + 'FreeFly': ['Inactive', + 'OutOfTime', + 'Death', + 'FlyingUp', + 'Running', + 'HitWhileFlying', + 'InWhirlwind'], + 'FlyingUp': ['Inactive', + 'OutOfTime', + 'Death', + 'FreeFly', + 'Running', + 'HitWhileFlying', + 'InWhirlwind', + 'WaitingForWin'], + 'InWhirlwind': ['Inactive', + 'OutOfTime', + 'Death', + 'FreeFly', + 'HitWhileFlying'], + 'HitWhileFlying': ['Inactive', + 'OutOfTime', + 'Death', + 'FreeFly', + 'InWhirlwind'], + 'Death': ['Inactive', 'OutOfTime', 'Spawn'], + 'Running': ['Inactive', + 'OutOfTime', + 'FreeFly', + 'FlyingUp', + 'Refuel', + 'WaitingForWin', + 'HitWhileRunning'], + 'HitWhileRunning': ['Inactive', + 'OutOfTime', + 'Death', + 'Running', + 'FreeFly'], + 'Spawn': ['Inactive', + 'OutOfTime', + 'Running', + 'WaitingForWin'], + 'OutOfTime': ['Inactive', 'Spawn'], + 'WaitingForWin': ['Inactive', 'Win'], + 'Win': ['Inactive']} + self.game = game + self._level = level + self._guiMgr = guiMgr + self._inputMgr = CogdoFlyingInputManager() + self._cameraMgr = CogdoFlyingCameraManager(camera, render, self, self._level) + self.velocity = Vec3(0.0, 0.0, 0.0) + self.instantaneousVelocity = Vec3(0.0, 0.0, 0.0) + self.controlVelocity = Vec3(0.0, 0.0, 0.0) + self.fanVelocity = Vec3(0.0, 0.0, 0.0) + self.activeFans = [] + self.fansStillHavingEffect = [] + self.fanIndex2ToonVelocity = {} + self.legalEagleInterestRequest = {} + self.activeWhirlwind = None + self.oldPos = Vec3(0.0, 0.0, 0.0) + self.checkpointPlatform = None + self.isHeadInCeiling = False + self.isToonOnFloor = False + self.fuel = 0.0 + self.score = 0 + self.postSpawnState = 'Running' + self.didTimeRunOut = False + self.hasPressedCtrlYet = False + self.hasPickedUpFirstPropeller = False + self.surfacePoint = None + self.legalEagleHitting = False + self.propState = None + self.broadcastPeriod = Globals.AI.BroadcastPeriod + self.initSfx() + self.initLocalPlayerIntervals() + self.initCollisions() + self.initOrthoWalker() + self.playerNumber = -1 + self.fuel = 0.0 + self._guiMgr.setFuel(self.fuel) + self.setCheckpointPlatform(self._level.startPlatform) + + def initSfx(self): + audioMgr = base.cogdoGameAudioMgr + self._deathSfx = audioMgr.createSfx('death') + self._hitByWhirlwindSfx = audioMgr.createSfx('toonInWhirlwind') + self._bladeBreakSfx = audioMgr.createSfx('bladeBreak') + self._collideSfx = audioMgr.createSfx('collide') + self._toonHitSfx = audioMgr.createSfx('toonHit') + self._getMemoSfx = audioMgr.createSfx('getMemo') + self._getLaffSfx = audioMgr.createSfx('getLaff') + self._getRedTapeSfx = audioMgr.createSfx('getRedTape') + self._refuelSfx = audioMgr.createSfx('refuel') + self._fanSfx = audioMgr.createSfx('fan') + self._invulDebuffSfx = audioMgr.createSfx('invulDebuff') + self._invulBuffSfx = audioMgr.createSfx('invulBuff') + self._winSfx = audioMgr.createSfx('win') + self._loseSfx = audioMgr.createSfx('lose') + self._refuelSpinSfx = audioMgr.createSfx('refuelSpin') + self._propellerSfx = audioMgr.createSfx('propeller', self.toon) + + def destroySfx(self): + del self._deathSfx + del self._hitByWhirlwindSfx + del self._bladeBreakSfx + del self._collideSfx + del self._toonHitSfx + del self._propellerSfx + del self._getMemoSfx + del self._getLaffSfx + del self._refuelSfx + del self._fanSfx + del self._invulBuffSfx + del self._invulDebuffSfx + del self._getRedTapeSfx + del self._refuelSpinSfx + + def setPlayerNumber(self, num): + self.playerNumber = num + + def getPlayerNumber(self): + return self.playerNumber + + def initOrthoWalker(self): + orthoDrive = OrthoDrive(9.778, maxFrameMove=0.5, wantSound=True) + self.orthoWalk = OrthoWalk(orthoDrive, broadcast=False, collisions=False, broadcastPeriod=Globals.AI.BroadcastPeriod) + + def initLocalPlayerIntervals(self): + self.coolDownAfterHitInterval = Sequence(Wait(Globals.Gameplay.HitCooldownTime), Func(self.setEnemyHitting, False), name='coolDownAfterHitInterval-%i' % self.toon.doId) + self.deathInterval = Sequence(Func(self.resetVelocities), Parallel(Parallel(Func(self._deathSfx.play), LerpHprInterval(self.toon, 1.0, Vec3(720, 0, 0)), LerpFunctionInterval(self.toon.setScale, fromData=1.0, toData=0.1, duration=1.0), self.toon.posInterval(0.5, Vec3(0, 0, -25), other=self.toon)), Sequence(Wait(0.5), Func(base.transitions.irisOut))), Func(self.toon.stash), Wait(1.0), Func(self.toonSpawnFunc), name='%s.deathInterval' % self.__class__.__name__) + self.outOfTimeInterval = Sequence(Func(messenger.send, CogdoFlyingLocalPlayer.PlayWaitingMusicEventName), Func(self._loseSfx.play), Func(base.transitions.irisOut), Wait(1.0), Func(self.resetVelocities), Func(self._guiMgr.setMessage, '', transition=None), Func(self.toon.stash), Func(self.toonSpawnFunc), name='%s.outOfTimeInterval' % self.__class__.__name__) + self.spawnInterval = Sequence(Func(self.resetToonFunc), Func(self._cameraMgr.update, 0.0), Func(self._level.update), Func(self.toon.cnode.broadcastPosHprFull), Func(base.transitions.irisIn), Wait(0.5), Func(self.toon.setAnimState, 'TeleportIn'), Func(self.toon.unstash), Wait(1.5), Func(self.requestPostSpawnState), name='%s.spawnInterval' % self.__class__.__name__) + self.waitingForWinInterval = Sequence(Func(self._guiMgr.setMessage, TTLocalizer.WaitingForOtherToonsDots % '.'), Wait(1.5), Func(self._guiMgr.setMessage, TTLocalizer.WaitingForOtherToonsDots % '..'), Wait(1.5), Func(self._guiMgr.setMessage, TTLocalizer.WaitingForOtherToonsDots % '...'), Wait(1.5), name='%s.waitingForWinInterval' % self.__class__.__name__) + self.waitingForWinSeq = Sequence(Func(self.setWaitingForWinState), Wait(4.0), Func(self.removeAllMemos), Wait(2.0), Func(self.game.distGame.d_sendRequestAction, Globals.AI.GameActions.LandOnWinPlatform, 0), Func(self.playWaitingForWinInterval), name='%s.waitingForWinSeq' % self.__class__.__name__) + self.winInterval = Sequence(Func(self._guiMgr.setMessage, ''), Wait(4.0), Func(self.game.distGame.d_sendRequestAction, Globals.AI.GameActions.WinStateFinished, 0), name='%s.winInterval' % self.__class__.__name__) + self.goSadSequence = Sequence(Wait(2.5), Func(base.transitions.irisOut, 1.5), name='%s.goSadSequence' % self.__class__.__name__) + self.introGuiSeq = Sequence(Wait(0.5), Parallel(Func(self._guiMgr.setTemporaryMessage, TTLocalizer.CogdoFlyingGameMinimapIntro, duration=5.0), Sequence(Wait(1.0), Func(self._guiMgr.presentProgressGui))), Wait(5.0), Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGamePickUpAPropeller), name='%s.introGuiSeq' % self.__class__.__name__) + return + + def goSad(self): + self.goSadSequence.start() + + def setWaitingForWinState(self): + if self.didTimeRunOut: + self.toon.b_setAnimState('Sad') + self._guiMgr.setMessage(TTLocalizer.CogdoFlyingGameOutOfTime, transition='blink') + else: + self._winSfx.play() + messenger.send(CogdoFlyingLocalPlayer.PlayWaitingMusicEventName) + self.toon.b_setAnimState('victory') + self._guiMgr.setMessage(TTLocalizer.CogdoFlyingGameYouMadeIt) + + def removeAllMemos(self): + if self.didTimeRunOut: + messenger.send(CogdoFlyingLocalPlayer.RanOutOfTimeEventName) + + def playWaitingForWinInterval(self): + if not self.game.distGame.isSinglePlayer(): + self.waitingForWinInterval.loop() + + def resetToonFunc(self): + self.resetToon(resetFuel=self.hasPickedUpFirstPropeller) + + def _loopPropellerSfx(self, playRate = 1.0, volume = 1.0): + self._propellerSfx.loop(playRate=playRate, volume=1.0) + + def initCollisions(self): + avatarRadius = 2.0 + reach = 4.0 + self.flyerCollisions = CogdoFlyingCollisions() + self.flyerCollisions.setWallBitMask(OTPGlobals.WallBitmask) + self.flyerCollisions.setFloorBitMask(OTPGlobals.FloorBitmask) + self.flyerCollisions.initializeCollisions(base.cTrav, self.toon, avatarRadius, OTPGlobals.FloorOffset, reach) + self.flyerCollisions.setCollisionsActive(0) + floorColl = CogdoFlyingPlatform.FloorCollName + ceilingColl = CogdoFlyingPlatform.CeilingCollName + self.accept('Flyer.cHeadCollSphere-enter-%s' % ceilingColl, self.__handleHeadCollisionIntoCeiling) + self.accept('Flyer.cHeadCollSphere-exit-%s' % ceilingColl, self.__handleHeadCollisionExitCeiling) + self.accept('Flyer.cFloorEventSphere-exit-%s' % floorColl, self.__handleEventCollisionExitFloor) + self.accept('Flyer.cRayNode-enter-%s' % floorColl, self.__handleRayCollisionEnterFloor) + self.accept('Flyer.cRayNode-again-%s' % floorColl, self.__handleRayCollisionAgainFloor) + + def enable(self): + CogdoFlyingPlayer.enable(self) + self.toon.hideName() + + def disable(self): + CogdoFlyingPlayer.disable(self) + + def isLegalEagleInterestRequestSent(self, index): + if index in self.legalEagleInterestRequest: + return True + else: + return False + + def setLegalEagleInterestRequest(self, index): + if index not in self.legalEagleInterestRequest: + self.legalEagleInterestRequest[index] = True + else: + CogdoFlyingLocalPlayer.notify.warning('Attempting to set an legal eagle interest request when one already exists:%s' % index) + + def clearLegalEagleInterestRequest(self, index): + if index in self.legalEagleInterestRequest: + del self.legalEagleInterestRequest[index] + + def setBackpackState(self, state): + if state == self.backpackState: + return + CogdoFlyingPlayer.setBackpackState(self, state) + if state in Globals.Gameplay.BackpackStates: + if state == Globals.Gameplay.BackpackStates.Normal: + messenger.send(CogdoFlyingGuiManager.ClearMessageDisplayEventName) + elif state == Globals.Gameplay.BackpackStates.Targeted: + messenger.send(CogdoFlyingGuiManager.EagleTargetingLocalPlayerEventName) + elif state == Globals.Gameplay.BackpackStates.Attacked: + messenger.send(CogdoFlyingGuiManager.EagleAttackingLocalPlayerEventName) + + def requestPostSpawnState(self): + self.request(self.postSpawnState) + + def toonSpawnFunc(self): + self.game.distGame.b_toonSpawn(self.toon.doId) + + def __handleHeadCollisionIntoCeiling(self, collEntry): + self.isHeadInCeiling = True + self.surfacePoint = self.toon.getPos() + self._collideSfx.play() + if self.controlVelocity[2] > 0.0: + self.controlVelocity[2] = -self.controlVelocity[2] / 2.0 + + def __handleHeadCollisionExitCeiling(self, collEntry): + self.isHeadInCeiling = False + self.surfacePoint = None + return + + def landOnPlatform(self, collEntry): + surfacePoint = collEntry.getSurfacePoint(render) + intoNodePath = collEntry.getIntoNodePath() + platform = CogdoFlyingPlatform.getFromNode(intoNodePath) + if platform is not None: + if not platform.isStartOrEndPlatform(): + taskMgr.doMethodLater(0.5, self.delayedLandOnPlatform, 'delayedLandOnPlatform', extraArgs=[platform]) + elif platform.isEndPlatform(): + taskMgr.doMethodLater(1.0, self.delayedLandOnWinPlatform, 'delayedLandOnWinPlatform', extraArgs=[platform]) + self.isToonOnFloor = True + self.controlVelocity = Vec3(0.0, 0.0, 0.0) + self.toon.setPos(render, surfacePoint) + self.toon.setHpr(0, 0, 0) + self.request('Running') + return + + def __handleRayCollisionEnterFloor(self, collEntry): + fromNodePath = collEntry.getFromNodePath() + intoNodePath = collEntry.getIntoNodePath() + intoName = intoNodePath.getName() + fromName = fromNodePath.getName() + toonPos = self.toon.getPos(render) + collPos = collEntry.getSurfacePoint(render) + if toonPos.getZ() < collPos.getZ() + Globals.Gameplay.RayPlatformCollisionThreshold: + if not self.isToonOnFloor and self.state in ['FreeFly', 'FlyingUp']: + self.landOnPlatform(collEntry) + + def __handleRayCollisionAgainFloor(self, collEntry): + fromNodePath = collEntry.getFromNodePath() + intoNodePath = collEntry.getIntoNodePath() + intoName = intoNodePath.getName() + fromName = fromNodePath.getName() + toonPos = self.toon.getPos(render) + collPos = collEntry.getSurfacePoint(render) + if toonPos.getZ() < collPos.getZ() + Globals.Gameplay.RayPlatformCollisionThreshold: + if not self.isToonOnFloor and self.state in ['FreeFly', 'FlyingUp']: + self.landOnPlatform(collEntry) + + def __handleEventCollisionExitFloor(self, collEntry): + fromNodePath = collEntry.getFromNodePath() + intoNodePath = collEntry.getIntoNodePath() + intoName = intoNodePath.getName() + fromName = fromNodePath.getName() + if self.isToonOnFloor: + self.notify.debug('~~~Exit Floor:%s -> %s' % (intoName, fromName)) + self.isToonOnFloor = False + taskMgr.remove('delayedLandOnPlatform') + taskMgr.remove('delayedLandOnWinPlatform') + if self.state not in ['FlyingUp', 'Spawn']: + self.notify.debug('Exited floor') + self.request('FreeFly') + + def delayedLandOnPlatform(self, platform): + self.setCheckpointPlatform(platform) + return Task.done + + def delayedLandOnWinPlatform(self, platform): + self.setCheckpointPlatform(self._level.endPlatform) + self.request('WaitingForWin') + return Task.done + + def handleTimerExpired(self): + if self.state not in ['WaitingForWin', 'Win']: + self.setCheckpointPlatform(self._level.endPlatform) + self.postSpawnState = 'WaitingForWin' + self.didTimeRunOut = True + if self.state not in ['Death']: + self.request('OutOfTime') + + def ready(self): + self.resetToon(resetFuel=False) + self._cameraMgr.enable() + self._cameraMgr.update() + + def start(self): + CogdoFlyingPlayer.start(self) + self.toon.collisionsOff() + self.flyerCollisions.setAvatar(self.toon) + self.flyerCollisions.setCollisionsActive(1) + self._levelBounds = self._level.getBounds() + self.introGuiSeq.start() + self.request('Running') + + def exit(self): + self.request('Inactive') + CogdoFlyingPlayer.exit(self) + self._cameraMgr.disable() + self.flyerCollisions.setCollisionsActive(0) + self.flyerCollisions.setAvatar(None) + taskMgr.remove('delayedLandOnFuelPlatform') + taskMgr.remove('delayedLandOnWinPlatform') + self.ignoreAll() + return + + def unload(self): + self.toon.showName() + self.toon.collisionsOn() + self._destroyEventIval() + self._destroyEnemyHitIval() + CogdoFlyingPlayer.unload(self) + self._fanSfx.stop() + self.flyerCollisions.deleteCollisions() + del self.flyerCollisions + self.ignoreAll() + taskMgr.remove('delayedLandOnPlatform') + taskMgr.remove('delayedLandOnWinPlatform') + self.checkpointPlatform = None + self._cameraMgr.disable() + del self._cameraMgr + del self.game + self._inputMgr.destroy() + del self._inputMgr + self.introGuiSeq.clearToInitial() + del self.introGuiSeq + if self.goSadSequence: + self.goSadSequence.clearToInitial() + del self.goSadSequence + if self.coolDownAfterHitInterval: + self.coolDownAfterHitInterval.clearToInitial() + del self.coolDownAfterHitInterval + if self.deathInterval: + self.deathInterval.clearToInitial() + del self.deathInterval + if self.spawnInterval: + self.spawnInterval.clearToInitial() + del self.spawnInterval + if self.outOfTimeInterval: + self.outOfTimeInterval.clearToInitial() + del self.outOfTimeInterval + if self.winInterval: + self.winInterval.clearToInitial() + del self.winInterval + if self.waitingForWinInterval: + self.waitingForWinInterval.clearToInitial() + del self.waitingForWinInterval + if self.waitingForWinSeq: + self.waitingForWinSeq.clearToInitial() + del self.waitingForWinSeq + del self.activeFans[:] + del self.fansStillHavingEffect[:] + self.fanIndex2ToonVelocity.clear() + self.orthoWalk.stop() + self.orthoWalk.destroy() + del self.orthoWalk + self.destroySfx() + return + + def setCheckpointPlatform(self, platform): + self.checkpointPlatform = platform + + def resetVelocities(self): + self.fanVelocity = Vec3(0.0, 0.0, 0.0) + self.controlVelocity = Vec3(0.0, 0.0, 0.0) + self.velocity = Vec3(0.0, 0.0, 0.0) + + def resetToon(self, resetFuel = True): + CogdoFlyingPlayer.resetToon(self) + self.resetVelocities() + del self.activeFans[:] + del self.fansStillHavingEffect[:] + self.fanIndex2ToonVelocity.clear() + self._fanSfx.stop() + spawnPos = self.checkpointPlatform.getSpawnPosForPlayer(self.getPlayerNumber(), render) + self.activeWhirlwind = None + self.toon.setPos(render, spawnPos) + self.toon.setHpr(render, 0, 0, 0) + if resetFuel: + self.resetFuel() + self.isHeadInCeiling = False + self.isToonOnFloor = True + return + + def activateFlyingBroadcast(self): + self.timeSinceLastPosBroadcast = 0.0 + self.lastPosBroadcast = self.toon.getPos() + self.lastHprBroadcast = self.toon.getHpr() + toon = self.toon + toon.d_clearSmoothing() + toon.sendCurrentPosition() + taskMgr.remove(self.BroadcastPosTask) + taskMgr.add(self.doBroadcast, self.BroadcastPosTask) + + def shutdownFlyingBroadcast(self): + taskMgr.remove(self.BroadcastPosTask) + + def doBroadcast(self, task): + dt = globalClock.getDt() + self.timeSinceLastPosBroadcast += dt + if self.timeSinceLastPosBroadcast >= self.broadcastPeriod: + self.timeSinceLastPosBroadcast = 0.0 + self.toon.cnode.broadcastPosHprFull() + return Task.cont + + def died(self, timestamp): + self.request('Death') + + def spawn(self, timestamp): + self.request('Spawn') + + def updateToonFlyingState(self, dt): + leftPressed = self._inputMgr.arrowKeys.leftPressed() + rightPressed = self._inputMgr.arrowKeys.rightPressed() + upPressed = self._inputMgr.arrowKeys.upPressed() + downPressed = self._inputMgr.arrowKeys.downPressed() + jumpPressed = self._inputMgr.arrowKeys.jumpPressed() + if not self.hasPressedCtrlYet and jumpPressed and self.isFuelLeft(): + self.hasPressedCtrlYet = True + messenger.send(CogdoFlyingGuiManager.FirstPressOfCtrlEventName) + if jumpPressed and self.isFuelLeft(): + if self.state == 'FreeFly' and self.isInTransition() == False: + self.notify.debug('FreeFly -> FlyingUp') + self.request('FlyingUp') + elif self.state == 'FlyingUp' and self.isInTransition() == False: + self.notify.debug('FlyingUp -> FreeFly') + self.request('FreeFly') + if leftPressed and not rightPressed: + self.toon.setH(self.toon, Globals.Gameplay.ToonTurning['turningSpeed'] * dt) + max = Globals.Gameplay.ToonTurning['maxTurningAngle'] + if self.toon.getH() > max: + self.toon.setH(max) + elif rightPressed and not leftPressed: + self.toon.setH(self.toon, -1.0 * Globals.Gameplay.ToonTurning['turningSpeed'] * dt) + min = -1.0 * Globals.Gameplay.ToonTurning['maxTurningAngle'] + if self.toon.getH() < min: + self.toon.setH(min) + + def updateControlVelocity(self, dt): + leftPressed = self._inputMgr.arrowKeys.leftPressed() + rightPressed = self._inputMgr.arrowKeys.rightPressed() + upPressed = self._inputMgr.arrowKeys.upPressed() + downPressed = self._inputMgr.arrowKeys.downPressed() + jumpPressed = self._inputMgr.arrowKeys.jumpPressed() + if leftPressed: + self.controlVelocity[0] -= Globals.Gameplay.ToonAcceleration['turning'] * dt + if rightPressed: + self.controlVelocity[0] += Globals.Gameplay.ToonAcceleration['turning'] * dt + if upPressed: + self.controlVelocity[1] += Globals.Gameplay.ToonAcceleration['forward'] * dt + if downPressed: + self.controlVelocity[2] -= Globals.Gameplay.ToonAcceleration['activeDropDown'] * dt + self.controlVelocity[1] -= Globals.Gameplay.ToonAcceleration['activeDropBack'] * dt + if jumpPressed and self.isFuelLeft(): + self.controlVelocity[2] += Globals.Gameplay.ToonAcceleration['boostUp'] * dt + minVal = -Globals.Gameplay.ToonVelMax['turning'] + maxVal = Globals.Gameplay.ToonVelMax['turning'] + if not leftPressed and not rightPressed or self.controlVelocity[0] > maxVal or self.controlVelocity[0] < minVal: + x = self.dampenVelocityVal(self.controlVelocity[0], 'turning', 'turning', minVal, maxVal, dt) + self.controlVelocity[0] = x + minVal = -Globals.Gameplay.ToonVelMax['backward'] + maxVal = Globals.Gameplay.ToonVelMax['forward'] + if not upPressed and not downPressed or self.controlVelocity[1] > maxVal or self.controlVelocity[1] < minVal: + y = self.dampenVelocityVal(self.controlVelocity[1], 'backward', 'forward', minVal, maxVal, dt) + self.controlVelocity[1] = y + if self.isFuelLeft(): + minVal = -Globals.Gameplay.ToonVelMax['fall'] + else: + minVal = -Globals.Gameplay.ToonVelMax['fallNoFuel'] + maxVal = Globals.Gameplay.ToonVelMax['boost'] + if self.controlVelocity[2] > minVal: + if (not self._inputMgr.arrowKeys.jumpPressed() or not self.isFuelLeft()) and not self.isToonOnFloor: + self.controlVelocity[2] -= Globals.Gameplay.ToonAcceleration['fall'] * dt + if self.controlVelocity[2] < 0.0 and self.isToonOnFloor: + self.controlVelocity[2] = 0.0 + minVal = -Globals.Gameplay.ToonVelMax['turning'] + maxVal = Globals.Gameplay.ToonVelMax['turning'] + self.controlVelocity[0] = clamp(self.controlVelocity[0], minVal, maxVal) + minVal = -Globals.Gameplay.ToonVelMax['backward'] + maxVal = Globals.Gameplay.ToonVelMax['forward'] + self.controlVelocity[1] = clamp(self.controlVelocity[1], minVal, maxVal) + if self.isFuelLeft(): + minVal = -Globals.Gameplay.ToonVelMax['fall'] + else: + minVal = -Globals.Gameplay.ToonVelMax['fallNoFuel'] + maxVal = Globals.Gameplay.ToonVelMax['boost'] + self.controlVelocity[2] = clamp(self.controlVelocity[2], minVal, maxVal) + + def updateFanVelocity(self, dt): + fanHeight = Globals.Gameplay.FanCollisionTubeHeight + min = Globals.Gameplay.FanMinPower + max = Globals.Gameplay.FanMaxPower + powerRange = max - min + for fan in self.activeFans: + blowVec = fan.getBlowDirection() + blowVec *= Globals.Gameplay.ToonAcceleration['fan'] * dt + if Globals.Gameplay.UseVariableFanPower: + distance = fan.model.getDistance(self.toon) + power = math.fabs(distance / fanHeight - 1.0) * powerRange + min + power = clamp(power, min, max) + blowVec *= power + if fan.index in self.fanIndex2ToonVelocity: + fanVelocity = self.fanIndex2ToonVelocity[fan.index] + fanVelocity += blowVec + + removeList = [] + for fan in self.fansStillHavingEffect: + if fan not in self.activeFans: + blowVec = fan.getBlowDirection() + blowVec *= Globals.Gameplay.ToonDeceleration['fan'] * dt + if fan.index in self.fanIndex2ToonVelocity: + fanVelocity = Vec3(self.fanIndex2ToonVelocity[fan.index]) + lastLen = fanVelocity.length() + fanVelocity -= blowVec + if fanVelocity.length() > lastLen: + removeList.append(fan) + else: + self.fanIndex2ToonVelocity[fan.index] = fanVelocity + + for fan in removeList: + self.fansStillHavingEffect.remove(fan) + if fan.index in self.fanIndex2ToonVelocity: + del self.fanIndex2ToonVelocity[fan.index] + + self.fanVelocity = Vec3(0.0, 0.0, 0.0) + for fan in self.fansStillHavingEffect: + if fan.index in self.fanIndex2ToonVelocity: + self.fanVelocity += self.fanIndex2ToonVelocity[fan.index] + + minVal = -Globals.Gameplay.ToonVelMax['fan'] + maxVal = Globals.Gameplay.ToonVelMax['fan'] + self.fanVelocity[0] = clamp(self.fanVelocity[0], minVal, maxVal) + self.fanVelocity[1] = clamp(self.fanVelocity[1], minVal, maxVal) + self.fanVelocity[2] = clamp(self.fanVelocity[2], minVal, maxVal) + + def dampenVelocityVal(self, velocityVal, typeNeg, typePos, minVal, maxVal, dt): + if velocityVal > 0.0: + velocityVal -= Globals.Gameplay.ToonDeceleration[typePos] * dt + velocityVal = clamp(velocityVal, 0.0, maxVal) + elif velocityVal < 0.0: + velocityVal += Globals.Gameplay.ToonDeceleration[typeNeg] * dt + velocityVal = clamp(velocityVal, minVal, 0.0) + return velocityVal + + def allowFuelDeath(self): + if Globals.Gameplay.DoesToonDieWithFuel: + return True + else: + return not self.isFuelLeft() + + def updateToonPos(self, dt): + toonWorldY = self.toon.getY(render) + if self.hasPickedUpFirstPropeller == False: + if toonWorldY > -7.6: + self.toon.setY(-7.6) + elif toonWorldY < -35.0: + self.toon.setY(-35.0) + return + self.velocity = self.controlVelocity + self.fanVelocity + vel = self.velocity * dt + self.toon.setPos(self.toon, vel[0], vel[1], vel[2]) + toonPos = self.toon.getPos() + if Globals.Dev.DisableDeath: + pass + elif toonPos[2] < 0.0 and self.state in ['FreeFly', 'FlyingUp'] and self.allowFuelDeath(): + self.postSpawnState = 'Running' + self.game.distGame.b_toonDied(self.toon.doId) + if toonPos[2] > self._levelBounds[2][1]: + self.controlVelocity[2] = 0.0 + self.fanVelocity[2] = 0.0 + toonPos = Vec3(clamp(toonPos[0], self._levelBounds[0][0], self._levelBounds[0][1]), clamp(toonPos[1], self._levelBounds[1][0], self._levelBounds[1][1]), clamp(toonPos[2], self._levelBounds[2][0], self._levelBounds[2][1])) + if self.isHeadInCeiling and toonPos[2] > self.surfacePoint[2]: + toonPos[2] = self.surfacePoint[2] + self.toon.setPos(toonPos) + if self.toon.getY(render) < -10: + self.toon.setY(-10.0) + + def printFanInfo(self, string): + if len(self.fanIndex2ToonVelocity) > 0: + self.notify.info('==AFTER %s==' % string) + self.notify.info('Fan velocity:%s' % self.fanVelocity) + if len(self.activeFans) > 0: + self.notify.info('%s' % self.activeFans) + if len(self.fanIndex2ToonVelocity) > 0: + self.notify.info('%s' % self.fanIndex2ToonVelocity) + if len(self.fansStillHavingEffect) > 0: + self.notify.info('%s' % self.fansStillHavingEffect) + + def resetFuel(self): + self.setFuel(Globals.Gameplay.FuelNormalAmt) + + def isFuelLeft(self): + return self.fuel > 0.0 + + def setFuel(self, fuel): + self.fuel = fuel + self._guiMgr.setFuel(fuel) + if self.fuel <= 0.0: + fuelState = Globals.Gameplay.FuelStates.FuelEmpty + elif self.fuel < Globals.Gameplay.FuelVeryLowAmt: + fuelState = Globals.Gameplay.FuelStates.FuelVeryLow + elif self.fuel < Globals.Gameplay.FuelLowAmt: + fuelState = Globals.Gameplay.FuelStates.FuelLow + else: + fuelState = Globals.Gameplay.FuelStates.FuelNormal + if fuelState > self.fuelState: + self.game.distGame.b_toonSetBlades(self.toon.doId, fuelState) + if fuelState < self.fuelState: + if self.state in ['FlyingUp', 'FreeFly', 'Running']: + self.game.distGame.b_toonBladeLost(self.toon.doId) + + def resetBlades(self): + CogdoFlyingPlayer.resetBlades(self) + self._guiMgr.resetBlades() + + def setBlades(self, fuelState): + CogdoFlyingPlayer.setBlades(self, fuelState) + self._guiMgr.setBlades(fuelState) + + def bladeLost(self): + CogdoFlyingPlayer.bladeLost(self) + self._bladeBreakSfx.play(volume=0.35) + self._guiMgr.bladeLost() + + def updateFuel(self, dt): + if Globals.Dev.InfiniteFuel: + self.setFuel(Globals.Gameplay.FuelNormalAmt) + elif self.state in Globals.Gameplay.DepleteFuelStates and self.fuel > 0.0: + self.setFuel(self.fuel - Globals.Gameplay.FuelBurnRate * dt) + elif self.fuel < 0.0: + self.setFuel(0.0) + + def update(self, dt = 0.0): + self.instantaneousVelocity = (self.toon.getPos() - self.oldPos) / dt + self.oldPos = self.toon.getPos() + self.updateFuel(dt) + if self.isFlying(): + self.updateToonFlyingState(dt) + if self.state in ['FreeFly', 'FlyingUp', 'Death']: + self.updateControlVelocity(dt) + self.updateFanVelocity(dt) + self.updateToonPos(dt) + self._cameraMgr.update(dt) + + def isFlying(self): + if self.state in ['FreeFly', 'FlyingUp']: + return True + else: + return False + + def pressedControlWhileRunning(self): + if self.isFuelLeft() and self.state == 'Running': + self.notify.debug('Pressed Control and have fuel') + self.request('FlyingUp') + else: + self.ignore('control') + self.ignore('lcontrol') + self.acceptOnce('control', self.pressedControlWhileRunning) + self.acceptOnce('lcontrol', self.pressedControlWhileRunning) + + def setPropellerState(self, propState): + if not self.hasPickedUpFirstPropeller: + propState = CogdoFlyingLocalPlayer.PropStates.Off + if self.propState != propState: + oldState = self.propState + self.propState = propState + if self.propState == CogdoFlyingLocalPlayer.PropStates.Normal: + if not self.propellerSpinLerp.isPlaying(): + self.propellerSpinLerp.loop() + self.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed) + self._guiMgr.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed) + self._loopPropellerSfx(playRate=0.7, volume=0.8) + elif self.propState == CogdoFlyingLocalPlayer.PropStates.Overdrive: + if not self.propellerSpinLerp.isPlaying(): + self.propellerSpinLerp.loop() + self.setPropellerSpinRate(Globals.Gameplay.OverdrivePropSpeed) + self._guiMgr.setPropellerSpinRate(Globals.Gameplay.OverdrivePropSpeed) + self._loopPropellerSfx(playRate=1.1) + elif self.propState == CogdoFlyingLocalPlayer.PropStates.Off: + self.propellerSpinLerp.pause() + self._propellerSfx.stop() + + def enterInactive(self): + CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + self._inputMgr.disable() + self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off) + self.shutdownFlyingBroadcast() + + def filterInactive(self, request, args): + if request == self.state: + return None + else: + return self.defaultFilter(request, args) + return None + + def exitInactive(self): + CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + self._inputMgr.enable() + self.activateFlyingBroadcast() + + def enterSpawn(self): + CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + self.toon.b_setAnimState('Happy', 1.0) + self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) + self.spawnInterval.start() + + def filterSpawn(self, request, args): + if request == self.state: + return None + else: + return self.defaultFilter(request, args) + return None + + def exitSpawn(self): + CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + + def enterFreeFly(self): + CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) + if self.oldState in ['Running', 'HitWhileRunning']: + self.toon.jumpStart() + self.toon.setHpr(render, 0, 0, 0) + + def filterFreeFly(self, request, args): + if request == self.state: + return None + else: + return self.defaultFilter(request, args) + return None + + def exitFreeFly(self): + CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + + def enterFlyingUp(self): + CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Overdrive) + if self.oldState in ['Running']: + self.toon.jumpStart() + self.toon.setHpr(render, 0, 0, 0) + + def filterFlyingUp(self, request, args): + if request == self.state: + return None + else: + return self.defaultFilter(request, args) + return None + + def exitFlyingUp(self): + CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + + def enterHitWhileFlying(self, elapsedTime = 0.0): + CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + self.setEnemyHitting(True) + self._toonHitSfx.play() + self.startHitFlyingToonInterval() + self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) + + def filterHitWhileFlying(self, request, args): + if request == self.state: + return None + else: + return self.defaultFilter(request, args) + return None + + def exitHitWhileFlying(self): + CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + self.enemyHitIval.clearToInitial() + self.coolDownAfterHitInterval.clearToInitial() + self.coolDownAfterHitInterval.start() + + def enterInWhirlwind(self, elapsedTime = 0.0): + CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + self._hitByWhirlwindSfx.play() + self.startHitByWhirlwindInterval() + self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) + + def filterInWhirlwind(self, request, args): + if request == self.state: + return None + else: + return self.defaultFilter(request, args) + return None + + def exitInWhirlwind(self): + CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + self.eventIval.clearToInitial() + + def enterHitWhileRunning(self, elapsedTime = 0.0): + CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + self.setEnemyHitting(True) + self._toonHitSfx.play() + self.toon.b_setAnimState('FallDown') + self.startHitRunningToonInterval() + self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) + + def filterHitWhileRunning(self, request, args): + if request == self.state: + return None + else: + return self.defaultFilter(request, args) + return None + + def exitHitWhileRunning(self): + CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + self.enemyHitIval.clearToInitial() + self.coolDownAfterHitInterval.clearToInitial() + self.coolDownAfterHitInterval.start() + + def enterRunning(self): + CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + self.toon.b_setAnimState('Happy', 1.0) + if self.oldState not in ['Spawn', 'HitWhileRunning', 'Inactive']: + self.toon.jumpHardLand() + self._collideSfx.play() + self.orthoWalk.start() + self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) + self.ignore('control') + self.ignore('lcontrol') + self.acceptOnce('control', self.pressedControlWhileRunning) + self.acceptOnce('lcontrol', self.pressedControlWhileRunning) + + def filterRunning(self, request, args): + if request == self.state: + return None + else: + return self.defaultFilter(request, args) + return None + + def exitRunning(self): + CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + self.orthoWalk.stop() + self.ignore('control') + self.ignore('lcontrol') + + def enterOutOfTime(self): + CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + if self.spawnInterval.isPlaying(): + self.spawnInterval.clearToInitial() + self.ignoreAll() + self.introGuiSeq.clearToInitial() + self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off) + if not Globals.Dev.NoLegalEagleAttacks: + for eagle in self.legalEaglesTargeting: + messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [eagle.index]) + + taskMgr.remove('delayedLandOnPlatform') + taskMgr.remove('delayedLandOnWinPlatform') + self.outOfTimeInterval.start() + + def filterOutOfTime(self, request, args): + if request == self.state: + return None + else: + return self.defaultFilter(request, args) + return None + + def exitOutOfTime(self): + CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + + def enterDeath(self): + CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + self.propellerSmoke.stop() + self.deathInterval.start() + self.toon.b_setAnimState('jumpAirborne', 1.0) + self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off) + if not Globals.Dev.NoLegalEagleAttacks: + for eagle in self.legalEaglesTargeting: + messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [eagle.index]) + + def filterDeath(self, request, args): + if request == self.state: + return None + else: + return self.defaultFilter(request, args) + return None + + def exitDeath(self): + CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + self.deathInterval.clearToInitial() + + def enterWaitingForWin(self): + CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + self.resetFuel() + self._guiMgr.hideRefuelGui() + self.waitingForWinSeq.start() + self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) + if not Globals.Dev.NoLegalEagleAttacks: + self.game.forceClearLegalEagleInterestInToon(self.toon.doId) + + def filterWaitingForWin(self, request, args): + if request == self.state: + return None + else: + return self.defaultFilter(request, args) + return None + + def exitWaitingForWin(self): + CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + self.waitingForWinSeq.finish() + self.waitingForWinInterval.clearToInitial() + + def enterWin(self): + CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) + self._guiMgr.stopTimer() + self.winInterval.start() + self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) + + def filterWin(self, request, args): + if request == self.state: + return None + else: + return self.defaultFilter(request, args) + return None + + def exitWin(self): + CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) + + def _destroyEventIval(self): + if hasattr(self, 'eventIval'): + self.eventIval.clearToInitial() + del self.eventIval + + def startEventIval(self, ival): + self._destroyEventIval() + self.eventIval = ival + self.eventIval.start() + + def _destroyEnemyHitIval(self): + if hasattr(self, 'enemyHitIval'): + self.enemyHitIval.clearToInitial() + del self.enemyHitIval + + def startEnemyHitIval(self, ival): + self._destroyEnemyHitIval() + self.enemyHitIval = ival + self.enemyHitIval.start() + + def isEnemyHitting(self): + return self.legalEagleHitting + + def setEnemyHitting(self, value): + self.legalEagleHitting = value + + def shouldLegalEagleBeInFrame(self): + if not self.isLegalEagleTarget(): + return False + else: + index = len(self.legalEaglesTargeting) - 1 + eagle = self.legalEaglesTargeting[index] + return eagle.shouldBeInFrame() + + def startHitRunningToonInterval(self): + dur = self.toon.getDuration('slip-backward') + self.startEnemyHitIval(Sequence(Wait(dur), Func(self.request, 'Running'), name='hitByLegalEagleIval-%i' % self.toon.doId)) + + def startHitFlyingToonInterval(self): + hitByEnemyPos = self.toon.getPos(render) + collVec = hitByEnemyPos - self.collPos + collVec[2] = 0.0 + collVec.normalize() + collVec *= Globals.Gameplay.HitKnockbackDist + + def spinPlayer(t, rand): + if rand == 0: + self.toon.setH(-(t * 720.0)) + else: + self.toon.setH(t * 720.0) + + direction = random.randint(0, 1) + self.startEnemyHitIval(Sequence(Parallel(LerpFunc(spinPlayer, fromData=0.0, toData=1.0, duration=Globals.Gameplay.HitKnockbackTime, blendType='easeInOut', extraArgs=[direction]), LerpPosInterval(self.toon, duration=Globals.Gameplay.HitKnockbackTime, pos=hitByEnemyPos + collVec, blendType='easeOut')), Func(self.request, 'FreeFly'), name='hitByLegalEagleIval-%i' % self.toon.doId)) + + def startHitByWhirlwindInterval(self): + + def spinPlayer(t): + self.controlVelocity[2] = 1.0 + angle = math.radians(t * (720.0 * 2 - 180)) + self.toon.setPos(self.activeWhirlwind.model.getX(self.game.level.root) + math.cos(angle) * 2, self.activeWhirlwind.model.getY(self.game.level.root) + math.sin(angle) * 2, self.toon.getZ()) + + def movePlayerBack(t): + self.toon.setY(self.activeWhirlwind.model.getY(self.game.level.root) - t * Globals.Gameplay.WhirlwindMoveBackDist) + + self.startEventIval(Sequence(Func(self._cameraMgr.freeze), Func(self.activeWhirlwind.disable), LerpFunc(spinPlayer, fromData=0.0, toData=1.0, duration=Globals.Gameplay.WhirlwindSpinTime), LerpFunc(movePlayerBack, fromData=0.0, toData=1.0, duration=Globals.Gameplay.WhirlwindMoveBackTime, blendType='easeOut'), Func(self.activeWhirlwind.enable), Func(self._cameraMgr.unfreeze), Func(self.request, 'FreeFly'), name='spinPlayerIval-%i' % self.toon.doId)) + + def handleEnterWhirlwind(self, whirlwind): + self.activeWhirlwind = whirlwind + self.request('InWhirlwind') + + def handleEnterEnemyHit(self, enemy, collPos): + self.collPos = collPos + if self.state in ['FlyingUp', 'FreeFly']: + self.request('HitWhileFlying') + elif self.state in ['Running']: + self.request('HitWhileRunning') + + def handleEnterFan(self, fan): + if fan in self.activeFans: + return + if len(self.activeFans) == 0: + self._fanSfx.loop() + self.activeFans.append(fan) + if fan.index not in self.fanIndex2ToonVelocity: + self.fanIndex2ToonVelocity[fan.index] = Vec3(0.0, 0.0, 0.0) + if fan not in self.fansStillHavingEffect: + self.fansStillHavingEffect.append(fan) + + def handleExitFan(self, fan): + if fan in self.activeFans: + self.activeFans.remove(fan) + if len(self.activeFans) == 0: + self._fanSfx.stop() + + def handleDebuffPowerup(self, pickupType, elapsedTime): + self._invulDebuffSfx.play() + CogdoFlyingPlayer.handleDebuffPowerup(self, pickupType, elapsedTime) + messenger.send(CogdoFlyingGuiManager.ClearMessageDisplayEventName) + + def handleEnterGatherable(self, gatherable, elapsedTime): + CogdoFlyingPlayer.handleEnterGatherable(self, gatherable, elapsedTime) + if gatherable.type == Globals.Level.GatherableTypes.Memo: + self.handleEnterMemo(gatherable) + elif gatherable.type == Globals.Level.GatherableTypes.Propeller: + self.handleEnterPropeller(gatherable) + elif gatherable.type == Globals.Level.GatherableTypes.LaffPowerup: + self.handleEnterLaffPowerup(gatherable) + elif gatherable.type == Globals.Level.GatherableTypes.InvulPowerup: + self.handleEnterInvulPowerup(gatherable) + + def handleEnterMemo(self, gatherable): + self.score += 1 + if self.score == 1: + self._guiMgr.presentMemoGui() + self._guiMgr.setTemporaryMessage(TTLocalizer.CogdoFlyingGameMemoIntro, 4.0) + self._guiMgr.setMemoCount(self.score) + self._getMemoSfx.play() + + def handleEnterPropeller(self, gatherable): + if self.fuel < 1.0: + if not self.hasPickedUpFirstPropeller: + messenger.send(CogdoFlyingGuiManager.PickedUpFirstPropellerEventName) + self.introGuiSeq.clearToInitial() + self.hasPickedUpFirstPropeller = True + self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) + self.setFuel(1.0) + self._guiMgr.update() + self._refuelSfx.play() + self._refuelSpinSfx.play(volume=0.15) + + def handleEnterLaffPowerup(self, gatherable): + self._getLaffSfx.play() + + def handleEnterInvulPowerup(self, gatherable): + messenger.send(CogdoFlyingGuiManager.InvulnerableEventName) + self._getRedTapeSfx.play() diff --git a/toontown/cogdominium/CogdoFlyingObjects.py b/toontown/cogdominium/CogdoFlyingObjects.py new file mode 100755 index 00000000..c4ed46a2 --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingObjects.py @@ -0,0 +1,444 @@ +import random +from panda3d.core import * +from direct.interval.IntervalGlobal import Sequence, Func, Parallel, Wait, LerpHprInterval, LerpScaleInterval, LerpFunctionInterval +from otp.otpbase import OTPGlobals +from toontown.toonbase import ToontownGlobals +from CogdoGameGatherable import CogdoGameGatherable, CogdoMemo +import CogdoFlyingGameGlobals as Globals +import CogdoUtil +from direct.particles import ParticleEffect +from direct.particles import Particles +from direct.particles import ForceGroup + +class CogdoFlyingGatherableFactory: + + def __init__(self): + self._serialNum = -1 + self._memoModel = CogdoUtil.loadModel('memo', 'shared').find('**/memo') + self._propellerModel = CogdoUtil.loadFlyingModel('propellers').find('**/mesh') + self._powerUpModels = {} + for type, modelName in Globals.Level.PowerupType2Model.items(): + model = CogdoUtil.loadFlyingModel(modelName).find('**/' + Globals.Level.PowerupType2Node[type]) + self._powerUpModels[type] = model + model.setTransparency(True) + model.setScale(0.5) + + def createMemo(self): + self._serialNum += 1 + return CogdoFlyingMemo(self._serialNum, self._memoModel) + + def createPropeller(self): + self._serialNum += 1 + return CogdoFlyingPropeller(self._serialNum, self._propellerModel) + + def createPowerup(self, type): + self._serialNum += 1 + return CogdoFlyingPowerup(self._serialNum, type, self._powerUpModels[type]) + + def createSparkles(self, color1, color2, amp): + self.f = ParticleEffect.ParticleEffect('particleEffect_sparkles') + p0 = Particles.Particles('particles-1') + p0.setFactory('PointParticleFactory') + p0.setRenderer('SparkleParticleRenderer') + p0.setEmitter('RingEmitter') + p0.setPoolSize(15) + p0.setBirthRate(0.1) + p0.setLitterSize(100) + p0.setLitterSpread(0) + p0.factory.setLifespanBase(0.6) + p0.factory.setLifespanSpread(0.1) + p0.factory.setMassBase(1.0) + p0.factory.setMassSpread(0.0) + p0.factory.setTerminalVelocityBase(3.0) + p0.factory.setTerminalVelocitySpread(1.0) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.0) + p0.renderer.setCenterColor(color1) + p0.renderer.setEdgeColor(color2) + p0.renderer.setBirthRadius(0.3) + p0.renderer.setDeathRadius(0.3) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(0) + p0.emitter.setAmplitudeSpread(0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -10.0), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + p0.emitter.setRadius(2.0) + self.f.addParticles(p0) + self.f.setPos(0, 0, 0) + self.f.setHpr(0, random.random() * 180, random.random() * 180) + return self.f + + def destroy(self): + self._memoModel.removeNode() + del self._memoModel + self._propellerModel.removeNode() + del self._propellerModel + for model in self._powerUpModels.values(): + model.removeNode() + + del self._powerUpModels + if Globals.Level.AddSparkleToPowerups: + self.f.cleanup() + del self.f + + +class CogdoFlyingGatherableBase: + + def __init__(self, type): + self.type = type + self.initFlash() + + def initFlash(self): + model = CogdoUtil.loadFlyingModel('gatherableFlash_card') + texName = Globals.Level.GatherableType2TextureName[self.type] + tex = model.findTexture(texName) + tex.setWrapU(Texture.WMRepeat) + tex.setWrapV(Texture.WMRepeat) + del model + self.ts = TextureStage('ts') + self.ts.setMode(TextureStage.MCombine) + self.ts.setSort(1) + self.ts.setCombineRgb(TextureStage.CMInterpolate, TextureStage.CSPrevious, TextureStage.COSrcColor, TextureStage.CSTexture, TextureStage.COSrcColor, TextureStage.CSConstant, TextureStage.COSrcColor) + self.ts.setCombineAlpha(TextureStage.CMInterpolate, TextureStage.CSPrevious, TextureStage.COSrcAlpha, TextureStage.CSTexture, TextureStage.COSrcAlpha, TextureStage.CSConstant, TextureStage.COSrcAlpha) + self._model.setTexture(self.ts, tex) + dur = Globals.Gameplay.GatherableFlashTime + self.flashLoop = Sequence(LerpFunctionInterval(self.setTextureAlphaFunc, fromData=1.0, toData=0.25, duration=dur / 2.0, blendType='easeInOut'), LerpFunctionInterval(self.setTextureAlphaFunc, fromData=0.25, toData=1.0, duration=dur / 2.0, blendType='easeInOut'), Wait(1.0), name='%s.flashLoop-%s' % (self.__class__.__name__, self.serialNum)) + + def show(self): + self.enableFlash() + + def hide(self): + self.disableFlash() + + def enable(self): + pass + + def disable(self): + pass + + def enableFlash(self): + self.flashLoop.loop() + + def disableFlash(self): + self.flashLoop.clearToInitial() + + def destroy(self): + self.disableFlash() + del self.flashLoop + del self.ts + + def setTextureAlphaFunc(self, value): + self.ts.setColor(Vec4(value, value, value, value)) + + def isPowerUp(self): + return False + + +class CogdoFlyingGatherable(CogdoGameGatherable, CogdoFlyingGatherableBase): + + def __init__(self, type, serialNum, modelToInstance, triggerRadius, animate = True): + CogdoGameGatherable.__init__(self, serialNum, modelToInstance, triggerRadius, animate=animate) + CogdoFlyingGatherableBase.__init__(self, type) + + def enable(self): + CogdoGameGatherable.enable(self) + CogdoFlyingGatherableBase.enable(self) + + def disable(self): + CogdoGameGatherable.disable(self) + CogdoFlyingGatherableBase.disable(self) + + def show(self): + CogdoGameGatherable.show(self) + CogdoFlyingGatherableBase.show(self) + + def hide(self): + CogdoGameGatherable.hide(self) + CogdoFlyingGatherableBase.hide(self) + + def destroy(self): + CogdoGameGatherable.destroy(self) + CogdoFlyingGatherableBase.destroy(self) + + +class CogdoFlyingMemo(CogdoFlyingGatherableBase, CogdoMemo): + + def __init__(self, serialNum, model): + CogdoMemo.__init__(self, serialNum, triggerRadius=Globals.Gameplay.MemoCollisionRadius, spinRate=Globals.Gameplay.MemoSpinRate, model=model) + CogdoFlyingGatherableBase.__init__(self, Globals.Level.GatherableTypes.Memo) + self.floatTimer = 0.0 + self.floatSpeed = 1.0 + self.floatDuration = 2.0 + + def _handleEnterCollision(self, collEntry): + CogdoGameGatherable._handleEnterCollision(self, collEntry) + + def enable(self): + CogdoFlyingGatherableBase.enable(self) + CogdoMemo.enable(self) + + def disable(self): + CogdoFlyingGatherableBase.disable(self) + CogdoMemo.disable(self) + + def show(self): + CogdoFlyingGatherableBase.show(self) + CogdoMemo.show(self) + + def hide(self): + CogdoFlyingGatherableBase.hide(self) + CogdoMemo.hide(self) + + def destroy(self): + CogdoFlyingGatherableBase.destroy(self) + CogdoMemo.destroy(self) + + def update(self, dt): + self.floatTimer += dt + if self.floatTimer < self.floatDuration: + self.setPos(self.getPos() + Vec3(0, 0, dt * self.floatSpeed)) + elif self.floatTimer < self.floatDuration * 2.0: + self.setPos(self.getPos() - Vec3(0, 0, dt * self.floatSpeed)) + else: + self.floatTimer = 0.0 + self.floatSpeed = random.uniform(0.5, 1.0) + self.floatDuration = random.uniform(1.9, 2.1) + + +class CogdoFlyingPowerup(CogdoFlyingGatherable): + + def __init__(self, serialNum, powerupType, model): + self._pickedUpList = [] + self._isToonLocal = False + CogdoFlyingGatherable.__init__(self, powerupType, serialNum, model, Globals.Gameplay.MemoCollisionRadius) + self.initInterval() + + def initInterval(self): + bouncePercent = 1.2 + scale = self._model.getScale() + shrinkPowerupLerp = LerpScaleInterval(self._model, 0.5, 0.0, startScale=0.0, blendType='easeInOut') + growPowerupLerp = LerpScaleInterval(self._model, 0.5, scale * bouncePercent, startScale=0.0, blendType='easeInOut') + bouncePowerupLerp = LerpScaleInterval(self._model, 0.25, scale, startScale=scale * bouncePercent, blendType='easeInOut') + self.pickUpSeq = Sequence(Func(self.updateLerpStartScale, shrinkPowerupLerp, self._model), shrinkPowerupLerp, Func(self.ghostPowerup), growPowerupLerp, bouncePowerupLerp, name='%s.pickUpSeq-%s' % (self.__class__.__name__, self.serialNum)) + + def isPowerUp(self): + return True + + def updateLerpStartScale(self, lerp, nodepath): + lerp.setStartScale(nodepath.getScale()) + + def wasPickedUpByToon(self, toon): + if toon.doId in self._pickedUpList: + return True + return False + + def ghostPowerup(self): + if self._isToonLocal: + self._model.setAlphaScale(0.5) + if Globals.Level.AddSparkleToPowerups: + self.f = self.find('**/particleEffect_sparkles') + if not self.f.isEmpty(): + self.f.hide() + + def pickUp(self, toon, elapsedSeconds = 0.0): + if self.wasPickedUpByToon(toon) == True: + return + self._pickedUpList.append(toon.doId) + self._isToonLocal = toon.isLocal() + if self._animate: + self.pickUpSeq.clearToInitial() + self.pickUpSeq.start() + else: + self.ghostPowerup() + + def destroy(self): + del self._pickedUpList[:] + self.pickUpSeq.clearToInitial() + del self.pickUpSeq + CogdoFlyingGatherable.destroy(self) + + def update(self, dt): + self._model.setH(self._model.getH() + Globals.Gameplay.MemoSpinRate * dt) + + +class CogdoFlyingPropeller(CogdoFlyingGatherable): + + def __init__(self, serialNum, model): + CogdoFlyingGatherable.__init__(self, Globals.Level.GatherableTypes.Propeller, serialNum, model, Globals.Gameplay.PropellerCollisionRadius, animate=False) + self.activePropellers = [] + self.usedPropellers = [] + propellers = self._model.findAllMatches('**/propeller*') + for prop in propellers: + self.activePropellers.append(prop) + + self.initIntervals() + + def initIntervals(self): + self.animatedPropellerIval = Parallel(name='%s.object-%i-animatePropellerIval' % (self.__class__.__name__, self.serialNum)) + for propeller in self.activePropellers: + self.animatedPropellerIval.append(LerpHprInterval(propeller, duration=Globals.Level.PropellerSpinDuration, startHpr=Vec3(0.0, 0.0, 0.0), hpr=Vec3(360.0, 0.0, 0.0))) + + def show(self): + self.animatedPropellerIval.loop() + CogdoFlyingGatherable.show(self) + + def hide(self): + self.animatedPropellerIval.clearToInitial() + CogdoFlyingGatherable.hide(self) + + def destroy(self): + taskMgr.removeTasksMatching('propeller-respawn-*') + self.animatedPropellerIval.clearToInitial() + del self.animatedPropellerIval + CogdoFlyingGatherable.destroy(self) + + def pickUp(self, toon, elapsedSeconds = 0.0): + prop = self.removePropeller() + if prop != None: + respawnTime = Globals.Gameplay.PropellerRespawnTime + if elapsedSeconds < respawnTime: + taskMgr.doMethodLater(respawnTime - elapsedSeconds, self.addPropeller, 'propeller-respawn-%i' % self.serialNum, extraArgs=[prop]) + else: + self.addPropeller(prop) + else: + self.disable() + return + + def addPropeller(self, prop): + if len(self.usedPropellers) > 0: + if len(self.activePropellers) == 0: + self.enable() + self.usedPropellers.remove(prop) + prop.show() + self.activePropellers.append(prop) + self._wasPickedUp = False + + def removePropeller(self): + if len(self.activePropellers) > 0: + prop = self.activePropellers.pop() + prop.hide() + self.usedPropellers.append(prop) + if len(self.activePropellers) == 0: + self._wasPickedUp = True + return prop + return None + + def isPropeller(self): + if len(self.activePropellers) > 0: + return True + else: + return False + + +class CogdoFlyingLevelFog: + + def __init__(self, level, color = Globals.Level.FogColor): + self._level = level + self.color = color + self.defaultFar = None + fogDistance = self._level.quadLengthUnits * max(1, self._level.quadVisibiltyAhead * 0.2) + self.fog = Fog('RenderFog') + self.fog.setColor(self.color) + self.fog.setLinearRange(fogDistance * Globals.Level.RenderFogStartFactor, fogDistance) + self._visible = False + self._clearColor = Vec4(base.win.getClearColor()) + self._clearColor.setW(1.0) + self.defaultFar = base.camLens.getFar() + base.camLens.setFar(Globals.Camera.GameCameraFar) + base.setBackgroundColor(self.color) + + def destroy(self): + self.setVisible(False) + if hasattr(self, 'fog'): + del self.fog + if self.defaultFar is not None: + base.camLens.setFar(self.defaultFar) + + def isVisible(self): + return self._visible + + def setVisible(self, visible): + self._visible = visible + if self._visible: + base.win.setClearColor(self.color) + render.setFog(self.fog) + else: + base.win.setClearColor(self._clearColor) + render.clearFog() + + +class CogdoFlyingPlatform: + CeilingCollName = 'col_ceiling' + FloorCollName = 'col_floor' + + def __init__(self, model, type = Globals.Level.PlatformTypes.Platform, parent = None): + self._model = model + self._type = type + if parent is not None: + self._model.reparentTo(parent) + self._initCollisions() + return + + def __str__(self): + return '<%s model=%s, type=%s>' % (self.__class__.__name__, self._model, self._type) + + def destroy(self): + self._floorColl.clearPythonTag('platform') + self._model.removeNode() + del self._model + del self._type + del self._floorColl + del self._ceilingColl + + def onstage(self): + self._model.unstash() + + def offstage(self): + self._model.stash() + + def _initCollisions(self): + self._floorColl = self._model.find('**/*%s' % CogdoFlyingPlatform.FloorCollName) + self._floorColl.setName(CogdoFlyingPlatform.FloorCollName) + self._floorColl.node().setIntoCollideMask(ToontownGlobals.FloorEventBitmask | OTPGlobals.FloorBitmask) + self._floorColl.setPythonTag('platform', self) + self._ceilingColl = self._model.find('**/*%s' % CogdoFlyingPlatform.CeilingCollName) + self._ceilingColl.setName(CogdoFlyingPlatform.CeilingCollName) + self._ceilingColl.node().setIntoCollideMask(ToontownGlobals.CeilingBitmask) + + def getType(self): + return self._type + + def getName(self): + return self._model.getName() + + def getModel(self): + return self._model + + def isStartPlatform(self): + return self._type == Globals.Level.PlatformTypes.StartPlatform + + def isEndPlatform(self): + return self._type == Globals.Level.PlatformTypes.EndPlatform + + def isStartOrEndPlatform(self): + return self.isStartPlatform() or self.isEndPlatform() + + def getSpawnPosForPlayer(self, playerNum, parent): + offset = Globals.Level.PlatformType2SpawnOffset[self._type] + spawnLoc = self._model.find('**/spawn_loc') + x = (playerNum - 2.0) % 2 * offset + y = (playerNum - 1.0) % 2 * offset + if not spawnLoc.isEmpty(): + spawnPos = spawnLoc.getPos(parent) + Vec3(x, y, 0.0) + else: + spawnPos = self._floorColl.getPos(parent) + Vec3(x, y, 0.0) + return spawnPos + + @staticmethod + def getFromNode(node): + return node.getPythonTag('platform') diff --git a/toontown/cogdominium/CogdoFlyingObstacles.py b/toontown/cogdominium/CogdoFlyingObstacles.py new file mode 100755 index 00000000..dd09e100 --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingObstacles.py @@ -0,0 +1,393 @@ +import random +from direct.showbase.DirectObject import DirectObject +from direct.interval.IntervalGlobal import LerpFunc, ActorInterval, LerpPosInterval +from direct.interval.MetaInterval import Sequence +from direct.directutil import Mopath +from direct.showbase import PythonUtil +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from toontown.suit import Suit +from toontown.suit import SuitDNA +from toontown.battle import BattleProps +import CogdoUtil +import CogdoFlyingGameGlobals as Globals +from CogdoFlyingUtil import swapAvatarShadowPlacer +from direct.particles import ParticleEffect +from direct.particles import Particles +from direct.particles import ForceGroup + +class CogdoFlyingObstacleFactory: + + def __init__(self): + self._index = -1 + self._whirlwindModel = CogdoUtil.loadFlyingModel('whirlwind').find('**/whirlwind') + self._fanModel = CogdoUtil.loadFlyingModel('streamer').find('**/streamer') + + def destroy(self): + self._whirlwindModel.removeNode() + del self._whirlwindModel + self._fanModel.removeNode() + del self._fanModel + if Globals.Level.AddParticlesToStreamers: + if hasattr(self, 'f'): + self.f.cleanup() + del self.f + + def createFan(self): + self._index += 1 + return CogdoFlyingFan(self._index, self._fanModel) + + def createFlyingMinion(self, motionPath = None): + self._index += 1 + return CogdoFlyingMinionFlying(self._index, motionPath=motionPath) + + def createWalkingMinion(self, motionPath = None): + self._index += 1 + return CogdoFlyingMinionWalking(self._index, motionPath=motionPath) + + def createWhirlwind(self, motionPath = None): + self._index += 1 + return CogdoFlyingWhirlwind(self._index, self._whirlwindModel, motionPath=motionPath) + + def createStreamerParticles(self, color1, color2, amp): + self.f = ParticleEffect.ParticleEffect('streamer_particles') + p0 = Particles.Particles('particles-1') + p0.setFactory('PointParticleFactory') + p0.setRenderer('SparkleParticleRenderer') + p0.setEmitter('RingEmitter') + p0.setPoolSize(80) + p0.setBirthRate(0.05) + p0.setLitterSize(100) + p0.setLitterSpread(0) + p0.factory.setLifespanBase(3.0) + p0.factory.setLifespanSpread(0.5) + p0.factory.setMassBase(1.0) + p0.factory.setMassSpread(0.0) + p0.factory.setTerminalVelocityBase(5.0) + p0.factory.setTerminalVelocitySpread(1.0) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.0) + p0.renderer.setCenterColor(color1) + p0.renderer.setEdgeColor(color2) + p0.renderer.setBirthRadius(0.3) + p0.renderer.setDeathRadius(0.3) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(0) + p0.emitter.setAmplitudeSpread(0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, 10.0), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + p0.emitter.setRadius(5.0) + self.f.addParticles(p0) + self.f.setPos(0, 0, 0) + self.f.setHpr(0, 0, 0) + return self.f + + +class CogdoFlyingObstacle(DirectObject): + EnterEventName = 'CogdoFlyingObstacle_Enter' + ExitEventName = 'CogdoFlyingObstacle_Exit' + MotionTypes = PythonUtil.Enum(('BackForth', 'Loop')) + + def __init__(self, type, index, model, collSolid, motionPath = None, motionPattern = None, blendMotion = True, instanceModel = True): + self.type = type + self.index = index + name = 'CogdoFlyingObstacle-%s-%i' % (self.type, self.index) + if instanceModel: + self.model = NodePath(name) + model.instanceTo(self.model) + else: + self.model = model + self.model.setName(name) + self.currentT = 0.0 + self.direction = 1.0 + self.collNode = None + self._initCollisions(name, collSolid) + self.motionPath = motionPath + self.motionPattern = motionPattern + self.motionSequence = None + if blendMotion: + blendType = 'easeInOut' + else: + blendType = 'noBlend' + if motionPath is not None: + + def moveObstacle(value): + self.motionPath.goTo(self.model, value) + + self.motionPath = Mopath.Mopath(name='obstacle-%i' % self.index) + self.motionPath.loadNodePath(motionPath) + dur = self.motionPath.getMaxT() + self.motionSequence = Sequence(name='%s.obstacle-%i-motionSequence' % (self.__class__.__name__, self.index)) + movePart1 = LerpFunc(moveObstacle, fromData=0.0, toData=self.motionPath.getMaxT(), duration=dur, blendType=blendType) + self.motionSequence.append(movePart1) + if self.motionPattern == CogdoFlyingObstacle.MotionTypes.BackForth: + movePart2 = LerpFunc(moveObstacle, fromData=self.motionPath.getMaxT(), toData=0.0, duration=dur, blendType=blendType) + self.motionSequence.append(movePart2) + return + + def _initCollisions(self, name, collSolid): + self.collName = name + self.collSolid = collSolid + self.collSolid.setTangible(0) + self.collNode = CollisionNode(self.collName) + self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSolid) + self.collNodePath = self.model.attachNewNode(self.collNode) + self.collNodePath.hide() + self.accept('enter' + self.collName, self._handleEnterCollision) + self.accept('exit' + self.collName, self._handleExitCollision) + + def disable(self): + if self.collNode is not None: + self.collNode.setIntoCollideMask(BitMask32(0)) + return + + def enable(self): + if self.collNode is not None: + self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + return + + def startMoving(self, elapsedTime = 0.0): + if self.motionSequence is not None: + self.motionSequence.loop() + self.motionSequence.setT(elapsedTime % self.motionSequence.getDuration()) + return + + def stopMoving(self): + if self.motionSequence is not None: + self.motionSequence.pause() + return + + def destroy(self): + self.ignoreAll() + if self.motionSequence is not None: + self.motionSequence.clearToInitial() + del self.motionSequence + del self.collSolid + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + self.model.removeNode() + del self.model + del self.motionPath + return + + def update(self, dt): + pass + + def hide(self): + self.ignoreAll() + self.model.hide() + self.collNode.setIntoCollideMask(BitMask32(0)) + + def _handleEnterCollision(self, collEntry): + messenger.send(CogdoFlyingObstacle.EnterEventName, [self, collEntry]) + + def _handleExitCollision(self, collEntry): + messenger.send(CogdoFlyingObstacle.ExitEventName, [self, collEntry]) + + +from pandac.PandaModules import TransformState + +class CogdoFlyingWhirlwind(CogdoFlyingObstacle): + + def __init__(self, index, model, motionPath = None): + collSolid = CollisionTube(0, 0, 0, 0, 0, Globals.Gameplay.WhirlwindCollisionTubeHeight, Globals.Gameplay.WhirlwindCollisionTubeRadius) + CogdoFlyingObstacle.__init__(self, Globals.Level.ObstacleTypes.Whirlwind, index, model, collSolid, motionPath=motionPath, motionPattern=CogdoFlyingObstacle.MotionTypes.BackForth) + self.t = 0.0 + self._initModel() + + def _initModel(self): + self.model.setDepthWrite(False) + self._texStage = self.model.findTextureStage('*') + self._soundIval = base.cogdoGameAudioMgr.createSfxIval('whirlwind', source=self.model) + self.model.setBin('transparent', self.index) + + def startMoving(self, elapsedTime): + CogdoFlyingObstacle.startMoving(self, elapsedTime) + self.t = 0.0 + self._soundIval.loop() + + def update(self, dt): + self.t += dt + trans = TransformState.makePos((self.t, -self.t, 0)) + self.model.setTexTransform(self._texStage, trans) + trans = TransformState.makePos((self.t * 2.0, -self.t * 2.0, 0)) + + def stopMoving(self): + CogdoFlyingObstacle.stopMoving(self) + self._soundIval.pause() + + def destroy(self): + self._soundIval.clearToInitial() + del self._soundIval + CogdoFlyingObstacle.destroy(self) + + +class CogdoFlyingMinion(CogdoFlyingObstacle): + + def __init__(self, index, collSolid, motionPath = None): + self.prop = None + self.suit = Suit.Suit() + d = SuitDNA.SuitDNA() + d.newSuit(Globals.Gameplay.MinionDnaName) + self.suit.setDNA(d) + self.suit.setScale(Globals.Gameplay.MinionScale) + self.suit.nametag3d.stash() + self.suit.nametag.destroy() + swapAvatarShadowPlacer(self.suit, 'minion-%sShadowPlacer' % index) + self.mopathNodePath = NodePath('mopathNodePath') + self.suit.reparentTo(self.mopathNodePath) + CogdoFlyingObstacle.__init__(self, Globals.Level.ObstacleTypes.Minion, index, self.mopathNodePath, collSolid, motionPath=motionPath, motionPattern=CogdoFlyingObstacle.MotionTypes.Loop, blendMotion=False, instanceModel=False) + self.lastPos = None + self.suit.loop('neutral') + return + + def attachPropeller(self): + if self.prop is None: + self.prop = BattleProps.globalPropPool.getProp('propeller') + head = self.suit.find('**/joint_head') + self.prop.reparentTo(head) + return + + def detachPropeller(self): + if self.prop: + self.prop.cleanup() + self.prop.removeNode() + self.prop = None + return + + def startMoving(self, elapsedTime): + CogdoFlyingObstacle.startMoving(self, elapsedTime) + + def stopMoving(self): + CogdoFlyingObstacle.stopMoving(self) + + def update(self, dt): + CogdoFlyingObstacle.update(self, dt) + self.currPos = self.mopathNodePath.getPos() + if self.lastPos != None: + vec = self.currPos - self.lastPos + self.mopathNodePath.lookAt(self.currPos + vec) + self.mopathNodePath.setP(0) + self.lastPos = self.mopathNodePath.getPos() + return + + def destroy(self): + self.mopathNodePath.removeNode() + del self.mopathNodePath + self.suit.cleanup() + self.suit.removeNode() + self.suit.delete() + CogdoFlyingObstacle.destroy(self) + + +class CogdoFlyingMinionFlying(CogdoFlyingMinion): + + def __init__(self, index, motionPath = None): + radius = Globals.Gameplay.FlyingMinionCollisionRadius + offset = Globals.Gameplay.FlyingMinionCollisionHeightOffset + collSolid = CollisionSphere(0, 0, offset, radius) + CogdoFlyingMinion.__init__(self, index, collSolid, motionPath) + self.attachPropeller() + self.propTrack = Sequence(ActorInterval(self.prop, 'propeller', startFrame=0, endFrame=14)) + dur = Globals.Gameplay.FlyingMinionFloatTime + offset = Globals.Gameplay.FlyingMinionFloatOffset + suitPos = self.suit.getPos() + upperPos = suitPos + Point3(0.0, 0.0, offset / 2.0) + lowerPos = suitPos + Point3(0.0, 0.0, -offset / 2.0) + self.floatSequence = Sequence(LerpPosInterval(self.suit, dur / 4.0, startPos=suitPos, pos=upperPos, blendType='easeInOut'), LerpPosInterval(self.suit, dur / 2.0, startPos=upperPos, pos=lowerPos, blendType='easeInOut'), LerpPosInterval(self.suit, dur / 4.0, startPos=lowerPos, pos=suitPos, blendType='easeInOut'), name='%s.floatSequence%i' % (self.__class__.__name__, self.index)) + + def startMoving(self, elapsedTime): + CogdoFlyingMinion.startMoving(self, elapsedTime) + self.floatSequence.loop(elapsedTime) + self.propTrack.loop(elapsedTime) + self.suit.pose('landing', 0) + + def stopMoving(self): + CogdoFlyingMinion.stopMoving(self) + self.floatSequence.clearToInitial() + self.propTrack.pause() + + def destroy(self): + self.floatSequence.clearToInitial() + del self.floatSequence + self.propTrack.clearToInitial() + del self.propTrack + CogdoFlyingMinion.destroy(self) + + +class CogdoFlyingMinionWalking(CogdoFlyingMinion): + + def __init__(self, index, motionPath = None): + radius = Globals.Gameplay.WalkingMinionCollisionRadius + offset = Globals.Gameplay.WalkingMinionCollisionHeightOffset + collSolid = CollisionSphere(0, 0, offset, radius) + CogdoFlyingMinion.__init__(self, index, collSolid, motionPath) + + def startMoving(self, elapsedTime): + CogdoFlyingMinion.startMoving(self, elapsedTime) + self.suit.loop('walk') + + def stopMoving(self): + CogdoFlyingMinion.stopMoving(self) + self.suit.loop('neutral') + + +class CogdoFlyingFan(CogdoFlyingObstacle): + + def __init__(self, index, model, motionPath = None): + collSolid = CollisionTube(0, 0, 0, 0, 0, Globals.Gameplay.FanCollisionTubeHeight, Globals.Gameplay.FanCollisionTubeRadius) + CogdoFlyingObstacle.__init__(self, Globals.Level.ObstacleTypes.Fan, index, model, collSolid) + self.streamers = self.model.findAllMatches('**/streamer*') + self._initIntervals() + + def _initIntervals(self): + self.streamerIvals = [] + minDur = Globals.Gameplay.FanStreamerMinDuration + maxDur = Globals.Gameplay.FanStreamerMaxDuration + for streamer in self.streamers: + dur = random.uniform(minDur, maxDur) + streamerLerp = LerpFunc(streamer.setH, fromData=0.0, toData=360.0, duration=dur, name='%s.streamerLerp%i-%s' % (self.__class__.__name__, self.index, streamer.getName())) + self.streamerIvals.append(streamerLerp) + + def startMoving(self, elapsedTime = 0.0): + CogdoFlyingObstacle.startMoving(self, elapsedTime) + timeDelay = 0.0 + maxDur = Globals.Gameplay.FanStreamerMaxDuration + for ival in self.streamerIvals: + taskName = 'delayedStreamerSpinTask-fan-%i-%s' % (self.index, ival.getName()) + taskMgr.doMethodLater(timeDelay, ival.loop, taskName, extraArgs=[]) + timeDelay += maxDur / (len(self.streamers) - 1) + + def stopMoving(self): + CogdoFlyingObstacle.stopMoving(self) + taskMgr.removeTasksMatching('delayedStreamerSpinTask-fan-%i*' % self.index) + for streamerLerp in self.streamerIvals: + streamerLerp.pause() + + def setBlowDirection(self): + tempNodePath = NodePath('temp') + tempNodePath.reparentTo(self.model) + tempNodePath.setPos(0, 0, 1) + self.blowVec = tempNodePath.getPos(render) - self.model.getPos(render) + self.blowVec.normalize() + tempNodePath.removeNode() + del tempNodePath + + def getBlowDirection(self): + return Vec3(self.blowVec) + + def destroy(self): + taskMgr.removeTasksMatching('delayedStreamerSpinTask-fan-%i*' % self.index) + for streamerLerp in self.streamerIvals: + streamerLerp.clearToInitial() + + del self.streamerIvals[:] + CogdoFlyingObstacle.destroy(self) + del self.blowVec diff --git a/toontown/cogdominium/CogdoFlyingPlayer.py b/toontown/cogdominium/CogdoFlyingPlayer.py new file mode 100755 index 00000000..f4cae889 --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingPlayer.py @@ -0,0 +1,365 @@ +from pandac.PandaModules import DepthOffsetAttrib, NodePath, Vec3, Vec4, TextNode +from direct.directnotify import DirectNotifyGlobal +from direct.fsm.FSM import FSM +from direct.interval.FunctionInterval import Wait +from direct.interval.IntervalGlobal import Func, LerpHprInterval, LerpScaleInterval, LerpFunctionInterval +from direct.interval.MetaInterval import Sequence, Parallel +from direct.distributed.ClockDelta import globalClockDelta +from toontown.toonbase import ToontownGlobals +from toontown.effects import DustCloud +import CogdoFlyingGameGlobals as Globals +import CogdoUtil +from CogdoFlyingObjects import CogdoFlyingGatherable +from CogdoFlyingUtil import swapAvatarShadowPlacer + +class CogdoFlyingPlayer(FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingPlayer') + + def __init__(self, toon): + FSM.__init__(self, 'CogdoFlyingPlayer') + self.toon = toon + self.toon.reparentTo(render) + self.legalEaglesTargeting = [] + self.activeBuffs = [] + self.initModels() + self.initIntervals() + self.netTimeSentToStartDeath = 0 + self.backpackState = -1 + self.lastBackpackState = -1 + self.lastPropellerSpinRate = Globals.Gameplay.NormalPropSpeed + self.propellerSpinRate = Globals.Gameplay.NormalPropSpeed + self.setFuelState(Globals.Gameplay.FuelStates.FuelNoPropeller) + self.setOldFuelState(self.fuelState) + CogdoFlyingPlayer.setBlades(self, Globals.Gameplay.FuelStates.FuelNoPropeller) + self.setBackpackState(Globals.Gameplay.BackpackStates.Normal) + + def initModels(self): + self.createPropeller() + self.createRedTapeRing() + + def createPropeller(self): + self.propellerSmoke = DustCloud.DustCloud(self.toon, wantSound=False) + self.propellerSmoke.setBillboardPointEye() + self.propellerSmoke.setBin('fixed', 5002) + self.backpack = CogdoUtil.loadFlyingModel('propellerPack') + self.backpack.setScale(1.3) + self.backpack.setHpr(180.0, 0.0, 0.0) + self.backpackInstances = [] + self.backpackTextureCard = CogdoUtil.loadFlyingModel('propellerPack_card') + parts = self.toon.getTorsoParts() + for part in parts: + backpackInstance = part.attachNewNode('backpackInstance') + animal = self.toon.style.getAnimal() + bodyScale = ToontownGlobals.toonBodyScales[animal] + backpackHeight = ToontownGlobals.torsoHeightDict[self.toon.style.torso] * bodyScale - 0.5 + backpackInstance.setPos(0.0, -0.325, backpackHeight) + self.backpackInstances.append(backpackInstance) + self.backpack.instanceTo(backpackInstance) + + self.propInstances = [] + self.propeller = CogdoUtil.loadFlyingModel('toonPropeller') + for part in self.backpackInstances: + propInstance = part.attachNewNode('propInstance') + propInstance.setPos(0.0, -0.275, 0.0) + propInstance.setHpr(0.0, 20.0, 0.0) + propInstance.setScale(1.0, 1.0, 1.25) + self.propInstances.append(propInstance) + self.propeller.instanceTo(propInstance) + + self.blades = [] + self.activeBlades = [] + index = 1 + blade = self.propeller.find('**/propeller%d' % index) + while not blade.isEmpty(): + self.blades.append(blade) + index += 1 + blade = self.propeller.find('**/propeller%d' % index) + + for blade in self.blades: + self.activeBlades.append(blade) + + def createRedTapeRing(self): + self.redTapeRing = CogdoUtil.loadFlyingModel('redTapeRing') + self.redTapeRing.setTwoSided(True) + self.redTapeRing.reparentTo(self.toon) + self.redTapeRing.hide() + self.redTapeRing.setScale(1.25) + self.redTapeRing.setZ(self.toon.getHeight() / 2.0) + + def initIntervals(self): + self.baseSpinDuration = 1.0 + self.propellerSpinLerp = LerpFunctionInterval(self.propeller.setH, fromData=0.0, toData=360.0, duration=self.baseSpinDuration, name='%s.propellerSpinLerp-%s' % (self.__class__.__name__, self.toon.doId)) + singleBlinkTime = Globals.Gameplay.TargetedWarningSingleBlinkTime + blinkTime = Globals.Gameplay.TargetedWarningBlinkTime + self.blinkLoop = Sequence(Wait(singleBlinkTime / 2.0), Func(self.setBackpackTexture, Globals.Gameplay.BackpackStates.Attacked), Wait(singleBlinkTime / 2.0), Func(self.setBackpackTexture, Globals.Gameplay.BackpackStates.Targeted), name='%s.blinkLoop-%s' % (self.__class__.__name__, self.toon.doId)) + self.blinkWarningSeq = Sequence(Func(self.blinkLoop.loop), Wait(blinkTime), Func(self.blinkLoop.clearToInitial), name='%s.blinkWarningSeq-%s' % (self.__class__.__name__, self.toon.doId)) + dur = Globals.Gameplay.BackpackRefuelDuration + self.refuelSeq = Sequence(Func(self.setPropellerSpinRate, Globals.Gameplay.RefuelPropSpeed), Wait(dur), Func(self.returnBackpackToLastStateFunc), name='%s.refuelSeq-%s' % (self.__class__.__name__, self.toon.doId)) + scale = self.redTapeRing.getScale() + pulseTime = 1.0 + self.pulseBubbleSeq = Parallel(Sequence(LerpFunctionInterval(self.redTapeRing.setScale, fromData=scale, toData=scale * 1.1, duration=pulseTime / 2.0, blendType='easeInOut'), LerpFunctionInterval(self.redTapeRing.setScale, fromData=scale * 1.1, toData=scale, duration=pulseTime / 2.0, blendType='easeInOut')), LerpHprInterval(self.redTapeRing, pulseTime, Vec3(360, 0, 0), startHpr=Vec3(0, 0, 0)), name='%s.pulseBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId)) + bouncePercent = 1.2 + scaleTime = 0.5 + scaleBounceTime = 0.25 + self.popUpBubbleLerp = LerpScaleInterval(self.redTapeRing, scaleTime, scale * bouncePercent, startScale=0.0, blendType='easeInOut') + self.popUpBubbleSeq = Sequence(Func(self.updateLerpStartScale, self.popUpBubbleLerp, self.redTapeRing), Func(self.redTapeRing.show), self.popUpBubbleLerp, LerpScaleInterval(self.redTapeRing, scaleBounceTime, scale, startScale=scale * bouncePercent, blendType='easeInOut'), Func(self.pulseBubbleSeq.loop), name='%s.popUpBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId)) + self.removeBubbleLerp = LerpScaleInterval(self.redTapeRing, scaleBounceTime, scale * bouncePercent, startScale=scale, blendType='easeInOut') + self.removeBubbleSeq = Sequence(Func(self.pulseBubbleSeq.clearToInitial), Func(self.updateLerpStartScale, self.removeBubbleLerp, self.redTapeRing), self.removeBubbleLerp, LerpScaleInterval(self.redTapeRing, scaleTime, 0.0, startScale=scale * bouncePercent, blendType='easeInOut'), Func(self.redTapeRing.hide), name='%s.removeBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId)) + self.redTapeRing.setScale(0.0) + self.deathInterval = Sequence(Parallel(LerpHprInterval(self.toon, 1.0, Vec3(720, 0, 0)), LerpFunctionInterval(self.toon.setScale, fromData=1.0, toData=0.1, duration=1.0)), Func(self.toon.stash), name='%s.deathInterval-%s' % (self.__class__.__name__, self.toon.doId)) + self.spawnInterval = Sequence(Func(self.toon.stash), Func(self.resetToon), Wait(1.0), Func(self.toon.setAnimState, 'TeleportIn'), Func(self.toon.unstash), name='%s.spawnInterval-%s' % (self.__class__.__name__, self.toon.doId)) + singleBlinkTime = Globals.Gameplay.InvulSingleBlinkTime + blinkTime = Globals.Gameplay.InvulBlinkTime + invulBuffTime = Globals.Gameplay.InvulBuffTime + self.blinkBubbleLoop = Sequence(LerpFunctionInterval(self.redTapeRing.setAlphaScale, fromData=1.0, toData=0.0, duration=singleBlinkTime / 2.0, blendType='easeInOut'), LerpFunctionInterval(self.redTapeRing.setAlphaScale, fromData=0.0, toData=1.0, duration=singleBlinkTime / 2.0, blendType='easeInOut'), name='%s.blinkBubbleLoop-%s' % (self.__class__.__name__, self.toon.doId)) + self.blinkBubbleSeq = Sequence(Wait(invulBuffTime - blinkTime), Func(self.blinkBubbleLoop.loop), Wait(blinkTime), Func(self.blinkBubbleLoop.finish), name='%s.blinkBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId)) + + def returnBackpackToLastStateFunc(self): + if self.backpackState == Globals.Gameplay.BackpackStates.Refuel: + self.returnBackpackToLastState() + + def setPropellerSpinRateFunc(self): + if self.propellerSpinRate == Globals.Gameplay.RefuelPropSpeed: + self.setPropellerSpinRate(self.lastPropellerSpinRate) + + def returnBackpackToLastState(self): + self.setBackpackState(self.lastBackpackState) + + def setBackpackState(self, state): + if state == self.backpackState: + return + self.lastBackpackState = self.backpackState + self.backpackState = state + self.blinkWarningSeq.clearToInitial() + self.refuelSeq.clearToInitial() + self.blinkLoop.clearToInitial() + if self.lastBackpackState == Globals.Gameplay.BackpackStates.Refuel: + self.setPropellerSpinRateFunc() + if state in Globals.Gameplay.BackpackStates: + if state == Globals.Gameplay.BackpackStates.Normal: + pass + elif state == Globals.Gameplay.BackpackStates.Targeted: + pass + elif state == Globals.Gameplay.BackpackStates.Refuel: + self.refuelSeq.start() + elif state == Globals.Gameplay.BackpackStates.Attacked: + self.blinkWarningSeq.start() + self.setBackpackTexture(state) + + def setBackpackTexture(self, state): + texName = Globals.Gameplay.BackpackState2TextureName[state] + tex = self.backpackTextureCard.findTexture(texName) + self.backpack.setTexture(tex, 1) + + def updateLerpStartScale(self, lerp, nodepath): + lerp.setStartScale(nodepath.getScale()) + + def handleEnterGatherable(self, gatherable, elapsedTime): + if gatherable.type == Globals.Level.GatherableTypes.InvulPowerup: + self.blinkBubbleSeq.clearToInitial() + self.blinkBubbleSeq.start(elapsedTime) + self.removeBubbleSeq.clearToInitial() + self.popUpBubbleSeq.start() + if gatherable.type not in self.activeBuffs: + self.activeBuffs.append(gatherable.type) + elif gatherable.type == Globals.Level.GatherableTypes.Propeller: + self.setBackpackState(Globals.Gameplay.BackpackStates.Refuel) + + def handleDebuffPowerup(self, pickupType, elapsedTime): + if pickupType == Globals.Level.GatherableTypes.InvulPowerup: + self.blinkBubbleSeq.finish() + self.popUpBubbleSeq.clearToInitial() + self.removeBubbleSeq.start() + if pickupType in self.activeBuffs: + self.activeBuffs.remove(pickupType) + + def isBuffActive(self, pickupType): + if pickupType in self.activeBuffs: + return True + return False + + def isInvulnerable(self): + if Globals.Level.GatherableTypes.InvulPowerup in self.activeBuffs: + return True + return False + + def setFuelState(self, fuelState): + self.fuelState = fuelState + + def setOldFuelState(self, fuelState): + self.oldFuelState = fuelState + + def hasFuelStateChanged(self): + if self.fuelState != self.oldFuelState: + return True + else: + return False + + def updatePropellerSmoke(self): + if not self.hasFuelStateChanged(): + return + if self.fuelState in [Globals.Gameplay.FuelStates.FuelNoPropeller, Globals.Gameplay.FuelStates.FuelNormal]: + self.propellerSmoke.stop() + elif self.fuelState in [Globals.Gameplay.FuelStates.FuelVeryLow, Globals.Gameplay.FuelStates.FuelEmpty]: + self.propellerSmoke.stop() + self.propellerSmoke.setScale(0.25) + self.propellerSmoke.setZ(self.toon.getHeight() + 2.5) + self.propellerSmoke.loop(rate=48) + elif self.fuelState in [Globals.Gameplay.FuelStates.FuelLow]: + self.propellerSmoke.stop() + self.propellerSmoke.setScale(0.0825) + self.propellerSmoke.setZ(self.toon.getHeight() + 2.0) + self.propellerSmoke.loop(rate=24) + + def resetBlades(self): + self.setBlades(len(self.blades)) + + def setAsLegalEagleTarget(self, legalEagle): + if legalEagle not in self.legalEaglesTargeting: + self.legalEaglesTargeting.append(legalEagle) + + def removeAsLegalEagleTarget(self, legalEagle): + if legalEagle in self.legalEaglesTargeting: + self.legalEaglesTargeting.remove(legalEagle) + + def isLegalEagleTarget(self): + if len(self.legalEaglesTargeting) > 0: + return True + else: + return False + + def setBlades(self, fuelState): + if fuelState not in Globals.Gameplay.FuelStates: + return + numBlades = fuelState - 1 + if len(self.activeBlades) != numBlades: + for i in xrange(len(self.activeBlades)): + blade = self.activeBlades.pop() + blade.stash() + + if numBlades > len(self.blades): + numBlades = len(self.blades) + if numBlades > 0: + for i in xrange(numBlades): + blade = self.blades[i] + self.activeBlades.append(blade) + blade.unstash() + + if fuelState == Globals.Gameplay.FuelStates.FuelNoPropeller: + for prop in self.propInstances: + prop.hide() + + else: + for prop in self.propInstances: + prop.show() + + self.setFuelState(fuelState) + self.updatePropellerSmoke() + self.setOldFuelState(self.fuelState) + + def bladeLost(self): + if len(self.activeBlades) > 0: + blade = self.activeBlades.pop() + blade.stash() + self.setFuelState(len(self.activeBlades) + 1) + self.updatePropellerSmoke() + self.setOldFuelState(self.fuelState) + + def setPropellerSpinRate(self, newRate): + self.lastPropellerSpinRate = self.propellerSpinRate + self.propellerSpinRate = newRate + self.notify.debug('(%s) New propeller speed:%s, old propeller speed:%s' % (self.toon.doId, self.propellerSpinRate, self.lastPropellerSpinRate)) + self.propellerSpinLerp.setPlayRate(newRate) + + def died(self, elapsedTime): + self.deathInterval.start(elapsedTime) + self.propellerSmoke.stop() + + def spawn(self, elapsedTime): + self.spawnInterval.start(elapsedTime) + + def resetToon(self): + self.toon.setScale(1.0) + + def enable(self): + self.toon.setAnimState('Happy', 1.0) + self.toon.setForceJumpIdle(True) + self.toon.setSpeed(0, 0) + self.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed) + self.propellerSpinLerp.loop() + + def disable(self): + pass + + def unload(self): + self.ignoreAll() + if self.toon: + self.toon.showName() + self.backpackTextureCard.removeNode() + del self.backpackTextureCard + self.refuelSeq.clearToInitial() + del self.refuelSeq + self.pulseBubbleSeq.clearToInitial() + del self.pulseBubbleSeq + self.blinkBubbleLoop.clearToInitial() + del self.blinkBubbleLoop + self.blinkBubbleSeq.clearToInitial() + del self.blinkBubbleSeq + self.popUpBubbleLerp.clearToInitial() + del self.popUpBubbleLerp + self.popUpBubbleSeq.clearToInitial() + del self.popUpBubbleSeq + self.removeBubbleLerp.clearToInitial() + del self.removeBubbleLerp + self.removeBubbleSeq.clearToInitial() + del self.removeBubbleSeq + self.propellerSmoke.destroy() + del self.propellerSmoke + self.blinkWarningSeq.clearToInitial() + del self.blinkWarningSeq + self.blinkLoop.clearToInitial() + del self.blinkLoop + self.redTapeRing.removeNode() + del self.redTapeRing + self.propellerSpinLerp.clearToInitial() + del self.propellerSpinLerp + for prop in self.propInstances: + prop.removeNode() + + del self.propInstances[:] + self.propeller.removeNode() + del self.propeller + for backpack in self.backpackInstances: + backpack.removeNode() + + del self.backpackInstances[:] + self.backpack.removeNode() + del self.backpack + del self.activeBuffs[:] + del self.legalEaglesTargeting[:] + del self.toon + self.toon = None + if self.deathInterval: + self.deathInterval.clearToInitial() + self.deathInterval = None + if self.spawnInterval: + self.spawnInterval.clearToInitial() + self.spawnInterval = None + return + + def start(self): + swapAvatarShadowPlacer(self.toon, self.toon.uniqueName('toonShadowPlacer')) + self.toon.startSmooth() + + def exit(self): + self.toon.setForceJumpIdle(False) + self.propellerSmoke.reparentTo(hidden) + self.propellerSmoke.stop() + if self.toon: + CogdoFlyingPlayer.resetToon(self) + self.toon.setActiveShadow(0) + self.toon.deleteDropShadow() + self.toon.initializeDropShadow() + self.toon.setActiveShadow(1) + else: + self.notify.warning("There's no toon in offstage, this is bad!") diff --git a/toontown/cogdominium/CogdoFlyingShadowPlacer.py b/toontown/cogdominium/CogdoFlyingShadowPlacer.py new file mode 100755 index 00000000..5f6ce59c --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingShadowPlacer.py @@ -0,0 +1,68 @@ +from direct.controls.ControlManager import CollisionHandlerRayStart +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * +from direct.task.Task import Task +from direct.showbase.DirectObject import DirectObject +from direct.showbase.ShadowPlacer import ShadowPlacer + +class CogdoFlyingShadowPlacer(ShadowPlacer): + + def __init__(self, cTrav, shadowNodePath, wallCollideMask, floorCollideMask, name): + ShadowPlacer.__init__(self, cTrav, shadowNodePath, wallCollideMask, floorCollideMask) + self.name = name + + def setup(self, cTrav, shadowNodePath, wallCollideMask, floorCollideMask): + if not cTrav: + base.initShadowTrav() + cTrav = base.shadowTrav + self.cTrav = cTrav + self.shadowNodePath = shadowNodePath + floorOffset = 0.025 + self.cRay = CollisionRay(0.0, 0.0, 1.0, 0.0, 0.0, -1.0) + cRayNode = CollisionNode('shadowPlacer') + cRayNode.addSolid(self.cRay) + self.cRayNodePath = NodePath(cRayNode) + self.cRayBitMask = floorCollideMask + cRayNode.setFromCollideMask(self.cRayBitMask) + cRayNode.setIntoCollideMask(BitMask32.allOff()) + self.queue = CollisionHandlerQueue() + + def update(self): + self.cTrav.traverse(render) + self.queue.sortEntries() + if self.queue.getNumEntries() > 0: + entry = self.queue.getEntry(0) + pos = entry.getSurfacePoint(render) + self.shadowNodePath.setPos(render, pos) + return Task.cont + + def delete(self): + self.off() + del self.cTrav + del self.shadowNodePath + del self.cRay + self.cRayNodePath.removeNode() + del self.cRayNodePath + del self.queue + + def on(self): + if self.isActive: + return + self.cRayNodePath.reparentTo(self.shadowNodePath.getParent()) + self.cTrav.addCollider(self.cRayNodePath, self.queue) + self.isActive = 1 + taskMgr.add(self.update, 'ShadowPlacer.update.%s' % self.name, -45, extraArgs=[]) + + def off(self): + if not self.isActive: + return + didIt = self.cTrav.removeCollider(self.cRayNodePath) + self.oneTimeCollide() + self.cRayNodePath.detachNode() + self.isActive = 0 + taskMgr.remove('ShadowPlacer.update.%s' % self.name) + + def oneTimeCollide(self): + tempCTrav = CollisionTraverser('oneTimeCollide') + tempCTrav.addCollider(self.cRayNodePath, self.queue) + tempCTrav.traverse(render) diff --git a/toontown/cogdominium/CogdoFlyingUtil.py b/toontown/cogdominium/CogdoFlyingUtil.py new file mode 100755 index 00000000..e411e2b0 --- /dev/null +++ b/toontown/cogdominium/CogdoFlyingUtil.py @@ -0,0 +1,24 @@ +from otp.otpbase import OTPGlobals +from CogdoFlyingShadowPlacer import CogdoFlyingShadowPlacer + +def loadMockup(fileName, dmodelsAlt = 'coffin'): + try: + model = loader.loadModel(fileName) + except IOError: + model = loader.loadModel('phase_4/models/props/%s' % dmodelsAlt) + + return model + + +def swapAvatarShadowPlacer(avatar, name): + avatar.setActiveShadow(0) + avatar.deleteDropShadow() + avatar.initializeDropShadow() + if avatar.shadowPlacer: + avatar.shadowPlacer.delete() + avatar.shadowPlacer = None + shadowPlacer = CogdoFlyingShadowPlacer(base.shadowTrav, avatar.dropShadow, OTPGlobals.WallBitmask, OTPGlobals.FloorBitmask, name) + avatar.shadowPlacer = shadowPlacer + avatar.setActiveShadow(0) + avatar.setActiveShadow(1) + return diff --git a/toontown/cogdominium/CogdoGameAudioManager.py b/toontown/cogdominium/CogdoGameAudioManager.py new file mode 100755 index 00000000..9e0242cd --- /dev/null +++ b/toontown/cogdominium/CogdoGameAudioManager.py @@ -0,0 +1,130 @@ +from pandac.PandaModules import AudioSound +from direct.interval.SoundInterval import SoundInterval + +class CogdoGameSfx: + + def __init__(self, audioSound, audioMgr, source = None): + self._audioSound = audioSound + self._audioMgr = audioMgr + self._source = source + + def destroy(self): + self._audioMgr.removeSound(self._audioSound) + del self._audioSound + del self._audioMgr + del self._source + + def getAudioSound(self): + return self._audioSound + + def play(self, loop = False, playRate = 1.0, volume = 1.0, source = None): + if source is None: + source = self._source + self._audioMgr.playSound(self._audioSound, loop=loop, source=source, playRate=playRate, volume=volume) + return + + def loop(self, playRate = 1.0, volume = 1.0, source = None): + if source is None: + source = self._source + self.play(loop=True, source=source, playRate=playRate, volume=volume) + return + + def stop(self): + self._audioMgr.stopSound(self._audioSound) + + +class CogdoGameAudioManager: + notify = directNotify.newCategory('CogdoGameAudioManager') + + def __init__(self, musicFiles, sfxFiles, listener, cutoff = 75): + self._sfxFiles = sfxFiles + self._listener = listener + self._cutoff = cutoff + self.currentMusic = None + self._music = {} + for name, filePath in musicFiles.items(): + self._music[name] = base.loadMusic(filePath) + + self._audioSounds = [] + self._soundIvals = {} + base.cogdoGameAudioMgr = self + return + + def destroy(self): + del base.cogdoGameAudioMgr + self.stopAll() + self.currentMusic = None + del self.currentMusic + self._music.clear() + del self._sfxFiles + del self._audioSounds + return + + def stopMusic(self): + if self.currentMusic is not None: + self.currentMusic.stop() + return + + def playMusic(self, name, loop = True, swap = False): + time = 0.0 + if self.currentMusic is not None: + if swap: + time = self.currentMusic.getTime() + self.stopMusic() + self.currentMusic = self._music[name] + self.currentMusic.setTime(time) + self.currentMusic.setLoop(loop) + self.currentMusic.play() + return + + def createSfx(self, name, source = None): + sound = loader.loadSfx(self._sfxFiles[name]) + self._audioSounds.append(sound) + gameSfx = CogdoGameSfx(sound, self, source) + return gameSfx + + def removeSound(self, audioSound): + self._audioSounds.remove(audioSound) + + def _cleanupSoundIval(self, audioSound): + if audioSound in self._soundIvals: + ival = self._soundIvals[audioSound] + if ival.isPlaying(): + ival.finish() + del self._soundIvals[audioSound] + + def createSfxIval(self, sfxName, volume = 1.0, duration = 0.0, startTime = 0.0, source = None, cutoff = None): + sound = loader.loadSfx(self._sfxFiles[sfxName]) + self._audioSounds.append(sound) + return self._createSoundIval(sound, volume=volume, startTime=startTime, duration=duration, source=source, cutoff=cutoff) + + def _createSoundIval(self, audioSound, volume = 1.0, duration = 0.0, startTime = 0.0, source = None, register = False, cutoff = None): + if cutoff == None: + cutoff = self._cutoff + ival = SoundInterval(audioSound, node=source, duration=duration, startTime=startTime, cutOff=cutoff, seamlessLoop=True, listenerNode=self._listener) + return ival + + def playSound(self, audioSound, loop = False, source = None, playRate = 1.0, volume = 1.0): + audioSound.setPlayRate(playRate) + if source is not None and loop: + self._cleanupSoundIval(audioSound) + ival = self._createSoundIval(audioSound, volume=volume, source=source) + self._soundIvals[audioSound] = ival + ival.loop() + else: + base.playSfx(audioSound, looping=loop, node=source, volume=volume, listener=self._listener, cutoff=self._cutoff) + return + + def stopSound(self, audioSound): + if audioSound in self._soundIvals: + self._cleanupSoundIval(audioSound) + elif audioSound.status() == AudioSound.PLAYING: + audioSound.stop() + + def stopAllSfx(self): + for audioSound in self._audioSounds: + self.stopSound(audioSound) + + def stopAll(self): + self.stopMusic() + self.stopAllSfx() diff --git a/toontown/cogdominium/CogdoGameConsts.py b/toontown/cogdominium/CogdoGameConsts.py new file mode 100755 index 00000000..474d3ece --- /dev/null +++ b/toontown/cogdominium/CogdoGameConsts.py @@ -0,0 +1,13 @@ +from toontown.minigame.MinigameGlobals import getSafezoneId, DifficultyOverrideMult, QuantizeDifficultyOverride, NoDifficultyOverride, getDifficulty +from toontown.minigame.MinigameGlobals import NoTrolleyZoneOverride as NoExteriorZoneOverride +MaxPlayers = 4 +MessageLabelBlinkTime = 0.25 +MessageLabelFadeTime = 0.25 +MemoGuiTextScale = 0.11 +MemoGuiTextColor = (1, 1, 0, 1) +ExitDoorMoveDuration = 1.0 +LaffRewardMin = 25 +LaffRewardRange = 112 +LaffPenalty = 50 +PenthouseElevatorInPath = '**/elevatorIN_node' +PenthouseElevatorOutPath = '**/elevatorOUT_node' diff --git a/toontown/cogdominium/CogdoGameExit.py b/toontown/cogdominium/CogdoGameExit.py new file mode 100755 index 00000000..4af9f6a0 --- /dev/null +++ b/toontown/cogdominium/CogdoGameExit.py @@ -0,0 +1,123 @@ +from pandac.PandaModules import NodePath, Point3 +from direct.interval.MetaInterval import Parallel, Sequence +from direct.interval.SoundInterval import SoundInterval +from direct.interval.FunctionInterval import Wait, Func +from toontown.building import ElevatorConstants +from toontown.building import ElevatorUtils +import CogdoUtil +import CogdoGameConsts + +class CogdoGameExit(NodePath): + + def __init__(self, openSfx = None, closeSfx = None): + NodePath.__init__(self, 'CogdoGameExit') + self._model = CogdoUtil.loadModel('exitDoor') + self._model.reparentTo(self) + self._leftDoor = self._model.find('**/left_door') + self._rightDoor = self._model.find('**/right_door') + self._openSfx = openSfx or base.loadSfx('phase_9/audio/sfx/CHQ_VP_door_open.ogg') + self._closeSfx = closeSfx or base.loadSfx('phase_9/audio/sfx/CHQ_VP_door_close.ogg') + self._elevatorPoints = [] + for point in ElevatorConstants.ElevatorPoints: + self._elevatorPoints.append(point[0]) + + self._currentSlot = 0 + self._ival = None + self._open = True + self._toon2track = {} + self.close(animate=False) + return + + def destroy(self): + self._cleanToonTracks() + if self._ival is not None: + self._ival.clearToInitial() + del self._ival + self._model.removeNode() + del self._leftDoor + del self._rightDoor + del self._model + del self._openSfx + del self._closeSfx + del self._elevatorPoints + return + + def isOpen(self): + return self._open + + def uniqueName(self, name): + return self.getName() + name + + def open(self, animate = True): + if self._open: + return + if animate: + self._finishIval() + self._ival = Sequence(Parallel(SoundInterval(self._closeSfx), self._leftDoor.posInterval(self.getOpenCloseDuration(), ElevatorUtils.getLeftOpenPoint(ElevatorConstants.ELEVATOR_NORMAL), startPos=ElevatorUtils.getLeftClosePoint(ElevatorConstants.ELEVATOR_NORMAL), blendType='easeInOut'), self._rightDoor.posInterval(self.getOpenCloseDuration(), ElevatorUtils.getRightOpenPoint(ElevatorConstants.ELEVATOR_NORMAL), startPos=ElevatorUtils.getRightClosePoint(ElevatorConstants.ELEVATOR_NORMAL), blendType='easeInOut'))) + self._ival.start() + else: + ElevatorUtils.openDoors(self._leftDoor, self._rightDoor, type=ElevatorConstants.ELEVATOR_NORMAL) + self._open = True + + def getOpenCloseDuration(self): + return CogdoGameConsts.ExitDoorMoveDuration + + def close(self, animate = True): + if not self._open: + return + if animate: + self._finishIval() + self._ival = Sequence(Parallel(SoundInterval(self._closeSfx), self._leftDoor.posInterval(self.getOpenCloseDuration(), ElevatorUtils.getLeftClosePoint(ElevatorConstants.ELEVATOR_NORMAL), startPos=ElevatorUtils.getLeftOpenPoint(ElevatorConstants.ELEVATOR_NORMAL), blendType='easeIn'), self._rightDoor.posInterval(self.getOpenCloseDuration(), ElevatorUtils.getRightClosePoint(ElevatorConstants.ELEVATOR_NORMAL), startPos=ElevatorUtils.getRightOpenPoint(ElevatorConstants.ELEVATOR_NORMAL), blendType='easeIn'))) + self._ival.start() + else: + ElevatorUtils.closeDoors(self._leftDoor, self._rightDoor, type=ElevatorConstants.ELEVATOR_NORMAL) + self._open = False + + def _finishIval(self): + if self._ival is not None and self._ival.isPlaying(): + self._ival.finish() + return + + def toonEnters(self, toon, goInside = True): + self._runToonThroughSlot(toon, self._currentSlot, goInside=goInside) + self._currentSlot += 1 + if self._currentSlot > len(self._elevatorPoints): + self._currentSlot = 0 + + def _runToonThroughSlot(self, toon, slot, goInside = True): + helperNode = NodePath('helper') + helperNode.reparentTo(toon.getParent()) + helperNode.lookAt(self) + lookAtH = helperNode.getH(self._model) + toonH = toon.getH(self._model) + hDiff = abs(lookAtH - toonH) + distanceFromElev = toon.getDistance(self._model) + moveSpeed = 9.778 + anim = 'run' + if toon.animFSM.getCurrentState() == 'Sad': + moveSpeed *= 0.5 + anim = 'sad-walk' + runInsideDistance = 20 + track = Sequence(Func(toon.stopSmooth), Func(toon.loop, anim, 1.0), Parallel(toon.hprInterval(hDiff / 360.0, Point3(lookAtH, 0, 0), other=self._model, blendType='easeIn'), toon.posInterval(distanceFromElev / moveSpeed, Point3(self._elevatorPoints[slot], 0, 0), other=self._model, blendType='easeIn')), name=toon.uniqueName('runThroughExit'), autoPause=1) + if goInside: + track.append(Parallel(toon.hprInterval(lookAtH / 360.0, Point3(0, 0, 0), other=self._model, blendType='easeOut'), toon.posInterval(runInsideDistance / moveSpeed, Point3(self._elevatorPoints[slot], runInsideDistance, 0), other=self._model, blendType='easeOut'))) + track.append(Func(self._clearToonTrack, toon)) + track.append(Func(toon.setAnimState, 'Happy', 1.0)) + self._storeToonTrack(toon, track) + track.start() + + def _cleanToonTracks(self): + toons = [ toon for toon in self._toon2track ] + for toon in toons: + self._clearToonTrack(toon) + + def _clearToonTrack(self, toon): + oldTrack = self._toon2track.get(toon) + if oldTrack is not None: + oldTrack.pause() + del self._toon2track[toon] + return + + def _storeToonTrack(self, toon, track): + self._clearToonTrack(toon) + self._toon2track[toon] = track diff --git a/toontown/cogdominium/CogdoGameGatherable.py b/toontown/cogdominium/CogdoGameGatherable.py new file mode 100755 index 00000000..1bbf4720 --- /dev/null +++ b/toontown/cogdominium/CogdoGameGatherable.py @@ -0,0 +1,120 @@ +from pandac.PandaModules import CollisionSphere, CollisionNode +from pandac.PandaModules import NodePath, BitMask32 +from direct.showbase.DirectObject import DirectObject +from direct.interval.MetaInterval import Sequence +from direct.interval.FunctionInterval import Func, Wait +from direct.interval.IntervalGlobal import LerpFunc +from toontown.toonbase import ToontownGlobals +import CogdoUtil + +class CogdoGameGatherable(NodePath, DirectObject): + EnterEventName = 'CogdoGameGatherable_Enter' + + def __init__(self, serialNum, model, triggerRadius, triggerOffset = (0, 0, 0), animate = True, animDuration = 0.2, instanceModel = True, name = 'CogdoGameGatherable'): + NodePath.__init__(self, '%s-%d' % (name, serialNum)) + self.serialNum = serialNum + self._animate = animate + if instanceModel: + model.instanceTo(self) + self._model = self + else: + self._model = model + self._model.reparentTo(self) + self._model.setPosHpr(0, 0, 0, 0, 0, 0) + self._animDuration = animDuration + self._animSeq = None + self._initCollisions(triggerRadius, triggerOffset) + self._update = None + self._wasPickedUp = False + return + + def _initCollisions(self, triggerRadius, triggerOffset): + self.collSphere = CollisionSphere(triggerOffset[0], triggerOffset[1], triggerOffset[2], triggerRadius) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.getName()) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.attachNewNode(self.collNode) + + def destroy(self): + self.disable() + del self._model + if self._animSeq is not None: + self._animSeq.finish() + self._animSeq = None + self.collNodePath.removeNode() + self.removeNode() + return + + def enable(self): + self.accept('enter' + self.getName(), self._handleEnterCollision) + self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + + def disable(self): + self.ignoreAll() + self.collNode.setIntoCollideMask(BitMask32(0)) + + def show(self): + if not self.wasPickedUp(): + NodePath.show(self) + self.enable() + + def hide(self): + self.disable() + NodePath.hide(self) + + def _handleEnterCollision(self, collEntry): + messenger.send(CogdoGameGatherable.EnterEventName, [self]) + + def wasPickedUp(self): + return self._wasPickedUp + + def wasPickedUpByToon(self): + pass + + def update(self, dt): + pass + + def getModel(self): + return self._model + + def pickUp(self, toon, elapsedSeconds = 0.0): + self._wasPickedUp = True + if self._animSeq is not None: + self._animSeq.finish() + self._animSeq = None + if self._animate: + + def lerpFlyToToon(t): + vec = toon.getPos(render) - self.getPos(render) + vec[2] += toon.getHeight() + self.setPos(self.getPos() + vec * t) + self.setScale(1.0 - t * 0.8) + + self._animSeq = Sequence(LerpFunc(lerpFlyToToon, fromData=0.0, toData=1.0, duration=self._animDuration), Wait(0.1), Func(self.hide)) + self._animSeq.start(elapsedSeconds) + else: + self.hide() + return + + +class CogdoMemo(CogdoGameGatherable): + EnterEventName = 'CogdoMemo_Enter' + + def __init__(self, serialNum, model = None, pitch = 0, triggerRadius = 1.0, spinRate = 60): + if model is None: + model = CogdoUtil.loadModel('joke', 'shared') + model.setP(pitch) + self._spinRate = spinRate + CogdoGameGatherable.__init__(self, serialNum, model, triggerRadius, name='CogdoMemo') + return + + def destroy(self): + del self._spinRate + CogdoGameGatherable.destroy(self) + + def update(self, dt): + CogdoGameGatherable.update(self, dt) + self.setH(self.getH() + self._spinRate * dt) + + def _handleEnterCollision(self, collEntry): + messenger.send(CogdoMemo.EnterEventName, [self]) diff --git a/toontown/cogdominium/CogdoGameMessageDisplay.py b/toontown/cogdominium/CogdoGameMessageDisplay.py new file mode 100755 index 00000000..af7c2fb5 --- /dev/null +++ b/toontown/cogdominium/CogdoGameMessageDisplay.py @@ -0,0 +1,76 @@ +from direct.interval.FunctionInterval import Func +from direct.interval.LerpInterval import LerpFunc +from direct.interval.MetaInterval import Sequence +from pandac.PandaModules import TextNode +from toontown.toonbase import ToontownGlobals +import CogdoGameConsts + +class CogdoGameMessageDisplay: + UpdateMessageTaskName = 'MessageDisplay.updateMessage' + + def __init__(self, name, parent, pos = (0.0, 0.0, -0.5), scale = 0.09, color = (1.0, 1.0, 0, 1), sfx = None): + self.color = color + self._displaySfx = sfx + textNode = TextNode('messageLabel.' + name) + textNode.setTextColor(self.color) + textNode.setAlign(TextNode.ACenter) + textNode.setFont(ToontownGlobals.getSignFont()) + textNode.setShadow(0.06, 0.06) + textNode.setShadowColor(0.5, 0.5, 0.5, 1.0) + self.pos = pos + self.scale = scale + self.messageLabel = parent.attachNewNode(textNode) + self.messageLabel.setPos(self.pos) + self.messageLabel.setScale(self.scale) + self.messageLabel.stash() + self.transitionInterval = Sequence(name='%s.transitionInterval' % self.__class__.__name__) + + def destroy(self): + taskMgr.remove(CogdoGameMessageDisplay.UpdateMessageTaskName) + self.transitionInterval.finish() + self.transitionInterval.clearIntervals() + del self.transitionInterval.pythonIvals[:] + del self.transitionInterval[:] + self.messageLabel.removeNode() + del self.messageLabel + if self._displaySfx != None: + del self._displaySfx + return + + def updateMessage(self, message = '', color = None, transition = 'fade'): + taskMgr.remove(CogdoGameMessageDisplay.UpdateMessageTaskName) + if color is None: + color = self.color + self.transitionInterval.finish() + self.transitionInterval.clearIntervals() + del self.transitionInterval.pythonIvals[:] + del self.transitionInterval[:] + if message == '': + if transition in ('fade', 'blink'): + self.transitionInterval.append(LerpFunc(self.messageLabel.setAlphaScale, fromData=1.0, toData=0.0, duration=CogdoGameConsts.MessageLabelFadeTime, extraArgs=[])) + self.transitionInterval.append(Func(self.messageLabel.stash)) + else: + if self.messageLabel.isStashed(): + self.transitionInterval.append(Func(self.messageLabel.setAlphaScale, 0.0)) + self.transitionInterval.append(Func(self.messageLabel.unstash)) + elif transition in ('fade', 'blink'): + self.transitionInterval.append(LerpFunc(self.messageLabel.setAlphaScale, fromData=1.0, toData=0.0, duration=CogdoGameConsts.MessageLabelFadeTime, extraArgs=[])) + self.transitionInterval.append(Func(self.messageLabel.setPos, self.pos)) + self.transitionInterval.append(Func(self.messageLabel.node().setText, message)) + self.transitionInterval.append(Func(self.messageLabel.node().setTextColor, color)) + if self._displaySfx != None: + self.transitionInterval.append(Func(self._displaySfx.play)) + if transition == 'fade': + self.transitionInterval.append(LerpFunc(self.messageLabel.setAlphaScale, fromData=0.0, toData=1.0, duration=CogdoGameConsts.MessageLabelFadeTime, extraArgs=[])) + elif transition == 'blink': + self.transitionInterval.append(LerpFunc(self.messageLabel.setAlphaScale, fromData=0.0, toData=1.0, duration=CogdoGameConsts.MessageLabelBlinkTime, extraArgs=[])) + self.transitionInterval.append(LerpFunc(self.messageLabel.setAlphaScale, fromData=1.0, toData=0.0, duration=CogdoGameConsts.MessageLabelBlinkTime, extraArgs=[])) + self.transitionInterval.append(LerpFunc(self.messageLabel.setAlphaScale, fromData=0.0, toData=1.0, duration=CogdoGameConsts.MessageLabelBlinkTime, extraArgs=[])) + else: + self.transitionInterval.append(Func(self.messageLabel.setAlphaScale, 1.0)) + self.transitionInterval.start() + return + + def showMessageTemporarily(self, message = '', duration = 3.0, color = None): + self.updateMessage(message, color) + taskMgr.doMethodLater(duration, self.updateMessage, CogdoGameMessageDisplay.UpdateMessageTaskName, extraArgs=[]) diff --git a/toontown/cogdominium/CogdoGameRulesPanel.py b/toontown/cogdominium/CogdoGameRulesPanel.py new file mode 100755 index 00000000..0483d377 --- /dev/null +++ b/toontown/cogdominium/CogdoGameRulesPanel.py @@ -0,0 +1,68 @@ +from direct.task import Task +from direct.fsm import StateData +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import ToontownTimer +from toontown.toonbase import TTLocalizer +from toontown.minigame import MinigameGlobals + +class CogdoGameRulesPanel(StateData.StateData): + hiddenNode = NodePath('hiddenNode') + + def __init__(self, panelName, gameTitle, instructions, doneEvent, timeout = MinigameGlobals.rulesDuration): + StateData.StateData.__init__(self, doneEvent) + self.gameTitle = gameTitle + self.instructions = instructions + self.TIMEOUT = timeout + + def load(self): + minigameGui = loader.loadModel('phase_5/models/cogdominium/tt_m_gui_csa_flyThru') + self.bg = minigameGui.find('**/background') + self.chatBubble = minigameGui.find('**/chatBubble') + self.chatBubble.setScale(6.5, 6.5, 7.3) + self.chatBubble.setPos(0.32, 0, -0.78) + self.bg.setScale(5.2) + self.bg.setPos(0.14, 0, -0.6667) + self.bg.reparentTo(aspect2d) + self.chatBubble.reparentTo(aspect2d) + self.frame = DirectFrame(geom=self.bg, relief=None, pos=(0.2, 0, -0.6667)) + self.gameTitleText = DirectLabel(parent=self.frame, text=self.gameTitle, scale=TTLocalizer.CRPgameTitleText, text_align=TextNode.ACenter, text_font=getSignFont(), text_fg=(1.0, 0.33, 0.33, 1.0), pos=TTLocalizer.CRPgameTitleTextPos, relief=None) + self.instructionsText = DirectLabel(parent=self.frame, text=self.instructions, scale=TTLocalizer.MRPinstructionsText, text_align=TextNode.ACenter, text_wordwrap=TTLocalizer.MRPinstructionsTextWordwrap, pos=TTLocalizer.MRPinstructionsTextPos, relief=None) + self.playButton = DirectButton(parent=self.frame, relief=None, geom=(minigameGui.find('**/buttonUp'), minigameGui.find('**/buttonDown'), minigameGui.find('**/buttonHover')), pos=(0.74, 0, 0.1), scale=(4.2, 5, 5), command=self.playCallback) + minigameGui.removeNode() + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(base.a2dTopRight) + self.timer.setScale(0.4) + self.timer.setPos(-0.155, 0, -0.155) + self.frame.hide() + return + + def unload(self): + self.frame.destroy() + self.timer.destroy() + del self.frame + del self.gameTitleText + del self.instructionsText + self.playButton.destroy() + del self.playButton + del self.timer + self.bg.reparentTo(self.hiddenNode) + del self.bg + self.chatBubble.reparentTo(self.hiddenNode) + del self.chatBubble + + def enter(self): + self.frame.show() + self.timer.countdown(self.TIMEOUT, self.playCallback) + self.accept('enter', self.playCallback) + + def exit(self): + self.frame.hide() + self.timer.stop() + self.ignore('enter') + self.bg.hide() + self.chatBubble.hide() + + def playCallback(self): + messenger.send(self.doneEvent) diff --git a/toontown/cogdominium/CogdoInterior.py b/toontown/cogdominium/CogdoInterior.py new file mode 100755 index 00000000..0d007dc7 --- /dev/null +++ b/toontown/cogdominium/CogdoInterior.py @@ -0,0 +1,220 @@ +from pandac.PandaModules import ModelPool, TexturePool +from direct.task.Task import Task +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from toontown.hood import Place +from toontown.toonbase.ToonBaseGlobal import * +from toontown.town import TownBattle +from toontown.suit import Suit +from toontown.building import Elevator +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals + +class CogdoInterior(Place.Place): + notify = DirectNotifyGlobal.directNotify.newCategory('CogdoInterior') + + def __init__(self, loader, parentFSM, doneEvent): + Place.Place.__init__(self, loader, doneEvent) + self.fsm = ClassicFSM.ClassicFSM('CogdoInterior', [State.State('entrance', self.enterEntrance, self.exitEntrance, ['Game', 'walk']), + State.State('Elevator', self.enterElevator, self.exitElevator, ['Game', + 'battle', + 'walk', + 'crane']), + State.State('Game', self.enterGame, self.exitGame, ['battle', + 'died', + 'crane', + 'walk']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'died']), + State.State('crane', self.enterCrane, self.exitCrane, ['walk', + 'battle', + 'finalBattle', + 'died', + 'ouch', + 'squished']), + State.State('walk', self.enterWalk, self.exitWalk, ['stickerBook', + 'stopped', + 'battle', + 'sit', + 'died', + 'teleportOut', + 'Elevator', + 'crane']), + State.State('sit', self.enterSit, self.exitSit, ['walk']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'stopped', + 'sit', + 'died', + 'teleportOut', + 'Elevator']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'elevatorOut', 'battle']), + State.State('died', self.enterDied, self.exitDied, []), + State.State('elevatorOut', self.enterElevatorOut, self.exitElevatorOut, [])], 'entrance', 'elevatorOut') + self.parentFSM = parentFSM + self.elevatorDoneEvent = 'elevatorDoneSI' + self.currentFloor = 0 + + def enter(self, requestStatus): + self.fsm.enterInitialState() + self.zoneId = requestStatus['zoneId'] + self.accept('DSIDoneEvent', self.handleDSIDoneEvent) + + def exit(self): + self.ignoreAll() + + def load(self): + Place.Place.load(self) + self.parentFSM.getStateNamed('cogdoInterior').addChild(self.fsm) + self.townBattle = TownBattle.TownBattle('town-battle-done') + self.townBattle.load() + for i in xrange(1, 3): + Suit.loadSuits(i) + + def unload(self): + Place.Place.unload(self) + self.parentFSM.getStateNamed('cogdoInterior').removeChild(self.fsm) + del self.parentFSM + del self.fsm + self.ignoreAll() + ModelPool.garbageCollect() + TexturePool.garbageCollect() + self.townBattle.unload() + self.townBattle.cleanup() + del self.townBattle + for i in xrange(1, 3): + Suit.unloadSuits(i) + + def setState(self, state, battleEvent = None): + if battleEvent: + self.fsm.request(state, [battleEvent]) + else: + self.fsm.request(state) + + def getZoneId(self): + return self.zoneId + + def enterZone(self, zoneId): + pass + + def handleDSIDoneEvent(self, requestStatus): + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def enterEntrance(self): + pass + + def exitEntrance(self): + pass + + def enterElevator(self, distElevator): + self.accept(self.elevatorDoneEvent, self.handleElevatorDone) + self.elevator = Elevator.Elevator(self.fsm.getStateNamed('Elevator'), self.elevatorDoneEvent, distElevator) + self.elevator.load() + self.elevator.enter() + base.localAvatar.cantLeaveGame = 1 + + def exitElevator(self): + base.localAvatar.cantLeaveGame = 0 + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + del self.elevator + return None + + def detectedElevatorCollision(self, distElevator): + self.fsm.request('Elevator', [distElevator]) + return None + + def handleElevatorDone(self, doneStatus): + self.notify.debug('handling elevator done event') + where = doneStatus['where'] + if where == 'reject': + if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen(): + pass + else: + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'cogdoInterior': + pass + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') + + def enterGame(self): + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.laffMeter.start() + + def exitGame(self): + base.localAvatar.laffMeter.stop() + + def enterBattle(self, event): + mult = ToontownBattleGlobals.getCreditMultiplier(self.currentFloor) + self.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult) + base.localAvatar.b_setAnimState('off', 1) + base.localAvatar.cantLeaveGame = 1 + + def exitBattle(self): + self.townBattle.exit() + base.localAvatar.cantLeaveGame = 0 + + def enterCrane(self): + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.laffMeter.start() + base.localAvatar.collisionsOn() + + def exitCrane(self): + base.localAvatar.collisionsOff() + base.localAvatar.laffMeter.stop() + + def enterWalk(self, teleportIn = 0): + Place.Place.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterStickerBook(self, page = None): + Place.Place.enterStickerBook(self, page) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterSit(self): + Place.Place.enterSit(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterTeleportIn(self, requestStatus): + base.localAvatar.setPosHpr(2.5, 11.5, ToontownGlobals.FloorOffset, 45.0, 0.0, 0.0) + Place.Place.enterTeleportIn(self, requestStatus) + + def enterTeleportOut(self, requestStatus): + Place.Place.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + hoodId = requestStatus['hoodId'] + if hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + messenger.send('localToonLeft') + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def exitTeleportOut(self): + Place.Place.exitTeleportOut(self) + + def goHomeFailed(self, task): + self.notifyUserGoHomeFailed() + self.ignore('setLocalEstateZone') + self.doneStatus['avId'] = -1 + self.doneStatus['zoneId'] = self.getZoneId() + self.fsm.request('teleportIn', [self.doneStatus]) + return Task.done + + def enterElevatorOut(self): + return None + + def __elevatorOutDone(self, requestStatus): + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def exitElevatorOut(self): + return None diff --git a/toontown/cogdominium/CogdoLayout.py b/toontown/cogdominium/CogdoLayout.py new file mode 100755 index 00000000..39dccfc5 --- /dev/null +++ b/toontown/cogdominium/CogdoLayout.py @@ -0,0 +1,24 @@ +from direct.directnotify import DirectNotifyGlobal + +class CogdoLayout: + notify = DirectNotifyGlobal.directNotify.newCategory('CogdoLayout') + + def __init__(self, numFloors): + self._numFloors = numFloors + + def getNumGameFloors(self): + return self._numFloors + + def hasBossBattle(self): + return self._numFloors >= 1 + + def getNumFloors(self): + if self.hasBossBattle(): + return self._numFloors + 1 + else: + return self._numFloors + + def getBossBattleFloor(self): + if not self.hasBossBattle(): + self.notify.error('getBossBattleFloor(): cogdo has no boss battle') + return self.getNumFloors() - 1 diff --git a/toontown/cogdominium/CogdoLevelMgr.py b/toontown/cogdominium/CogdoLevelMgr.py new file mode 100755 index 00000000..92a534dd --- /dev/null +++ b/toontown/cogdominium/CogdoLevelMgr.py @@ -0,0 +1,6 @@ +from otp.level import LevelMgr +from direct.showbase.PythonUtil import Functor +from toontown.toonbase import ToontownGlobals + +class CogdoLevelMgr(LevelMgr.LevelMgr): + pass diff --git a/toontown/cogdominium/CogdoLevelMgrAI.py b/toontown/cogdominium/CogdoLevelMgrAI.py new file mode 100755 index 00000000..9bb6f7a9 --- /dev/null +++ b/toontown/cogdominium/CogdoLevelMgrAI.py @@ -0,0 +1,4 @@ +from otp.level import LevelMgrAI + +class CogdoLevelMgrAI(LevelMgrAI.LevelMgrAI): + pass diff --git a/toontown/cogdominium/CogdoMaze.py b/toontown/cogdominium/CogdoMaze.py new file mode 100755 index 00000000..add4dd24 --- /dev/null +++ b/toontown/cogdominium/CogdoMaze.py @@ -0,0 +1,306 @@ +from pandac.PandaModules import NodePath, VBase4 +from direct.showbase.DirectObject import DirectObject +from direct.showbase.RandomNumGen import RandomNumGen +from toontown.minigame.MazeBase import MazeBase +import CogdoMazeGameGlobals as Globals +from CogdoMazeGameObjects import CogdoMazeWaterCooler +import CogdoMazeData +import CogdoUtil + +class CogdoMaze(MazeBase, DirectObject): + + def __init__(self, model, data, cellWidth): + MazeBase.__init__(self, model, data, cellWidth) + self._initWaterCoolers() + self.elevatorPos = self.maze.find('**/elevator_loc').getPos(render) + self.exitPos = self.maze.find('**/exit_loc').getPos(render) + self.maze.flattenStrong() + self._clearColor = VBase4(base.win.getClearColor()) + self._clearColor.setW(1.0) + base.win.setClearColor(VBase4(0.0, 0.0, 0.0, 1.0)) + + def _initWaterCoolers(self): + self._waterCoolers = [] + self._waterCoolerRoot = NodePath('WaterCoolerRoot') + self._waterCoolerRoot.reparentTo(render) + models = [] + for model in self.maze.findAllMatches('**/*waterCooler'): + model.wrtReparentTo(render) + models.append((model.getPos(self.maze), model.getHpr(self.maze), model)) + + models.sort() + i = 0 + for pos, hpr, model in models: + wc = CogdoMazeWaterCooler(i, model) + wc.wrtReparentTo(self._waterCoolerRoot) + wc.setPos(pos) + wc.setHpr(hpr) + self._waterCoolers.append(wc) + i += 1 + + self._waterCoolerRoot.stash() + + def getWaterCoolers(self): + return self._waterCoolers + + def isAccessible(self, tX, tY): + if tX < 0 or tY < 0 or tX >= self.width or tY >= self.height: + return 0 + return self.collisionTable[tY][tX] != 1 + + def destroy(self): + for waterCooler in self._waterCoolers: + waterCooler.destroy() + + del self._waterCoolers + self._waterCoolerRoot.removeNode() + del self._waterCoolerRoot + base.win.setClearColor(self._clearColor) + del self._clearColor + MazeBase.destroy(self) + + def onstage(self): + MazeBase.onstage(self) + self._waterCoolerRoot.unstash() + + def offstage(self): + self._waterCoolerRoot.stash() + MazeBase.offstage(self) + + +BARRIER_DATA_RIGHT = 1 +BARRIER_DATA_TOP = 1 + +class CogdoMazeFactory: + + def __init__(self, randomNumGen, width, height, frameWallThickness = Globals.FrameWallThickness, cogdoMazeData = CogdoMazeData): + self._rng = RandomNumGen(randomNumGen) + self.width = width + self.height = height + self.frameWallThickness = frameWallThickness + self._cogdoMazeData = cogdoMazeData + self.quadrantSize = self._cogdoMazeData.QuadrantSize + self.cellWidth = self._cogdoMazeData.QuadrantCellWidth + + def getMazeData(self): + if not hasattr(self, '_data'): + self._generateMazeData() + return self._data + + def createCogdoMaze(self, flattenModel = True): + if not hasattr(self, '_maze'): + self._loadAndBuildMazeModel(flatten=flattenModel) + return CogdoMaze(self._model, self._data, self.cellWidth) + + def _gatherQuadrantData(self): + self.openBarriers = [] + barrierItems = range(Globals.TotalBarriers) + self._rng.shuffle(barrierItems) + for i in barrierItems[0:len(barrierItems) - Globals.NumBarriers]: + self.openBarriers.append(i) + + self.quadrantData = [] + quadrantKeys = self._cogdoMazeData.QuadrantCollisions.keys() + self._rng.shuffle(quadrantKeys) + i = 0 + for y in xrange(self.height): + for x in xrange(self.width): + key = quadrantKeys[i] + collTable = self._cogdoMazeData.QuadrantCollisions[key] + angle = self._cogdoMazeData.QuadrantAngles[self._rng.randint(0, len(self._cogdoMazeData.QuadrantAngles) - 1)] + self.quadrantData.append((key, collTable[angle], angle)) + i += 1 + if x * y >= self._cogdoMazeData.NumQuadrants: + i = 0 + + def _generateBarrierData(self): + data = [] + for y in xrange(self.height): + data.append([]) + for x in xrange(self.width): + if x == self.width - 1: + ax = -1 + else: + ax = 1 + if y == self.height - 1: + ay = -1 + else: + ay = 1 + data[y].append([ax, ay]) + + dirUp = 0 + dirDown = 1 + dirLeft = 2 + dirRight = 3 + + def getAvailableDirections(ax, ay, ignore = None): + dirs = [] + if ax - 1 >= 0 and data[ay][ax - 1][BARRIER_DATA_RIGHT] == 1 and (ax, ay) != ignore: + dirs.append(dirLeft) + if ax + 1 < self.width and data[ay][ax][BARRIER_DATA_RIGHT] == 1 and (ax, ay) != ignore: + dirs.append(dirRight) + if ay - 1 >= 0 and data[ay - 1][ax][BARRIER_DATA_TOP] == 1 and (ax, ay) != ignore: + dirs.append(dirDown) + if ay + 1 < self.height and data[ay][ax][BARRIER_DATA_TOP] == 1 and (ax, ay) != ignore: + dirs.append(dirUp) + return dirs + + visited = [] + + def tryVisitNeighbor(ax, ay, ad): + if ad == dirUp: + if data[ay][ax] in visited: + return None + visited.append(data[ay][ax]) + data[ay][ax][BARRIER_DATA_TOP] = 0 + ay += 1 + elif ad == dirDown: + if data[ay - 1][ax] in visited: + return None + visited.append(data[ay - 1][ax]) + data[ay - 1][ax][BARRIER_DATA_TOP] = 0 + ay -= 1 + elif ad == dirLeft: + if data[ay][ax - 1] in visited: + return None + visited.append(data[ay][ax - 1]) + data[ay][ax - 1][BARRIER_DATA_RIGHT] = 0 + ax -= 1 + elif ad == dirRight: + if data[ay][ax] in visited: + return None + visited.append(data[ay][ax]) + data[ay][ax][BARRIER_DATA_RIGHT] = 0 + ax += 1 + return (ax, ay) + + def openBarriers(x, y): + dirs = getAvailableDirections(x, y) + for dir in dirs: + next = tryVisitNeighbor(x, y, dir) + if next is not None: + openBarriers(*next) + + return + + x = self._rng.randint(0, self.width - 1) + y = self._rng.randint(0, self.height - 1) + openBarriers(x, y) + self._barrierData = data + return + + def _generateMazeData(self): + if not hasattr(self, 'quadrantData'): + self._gatherQuadrantData() + self._data = {} + self._data['width'] = (self.width + 1) * self.frameWallThickness + self.width * self.quadrantSize + self._data['height'] = (self.height + 1) * self.frameWallThickness + self.height * self.quadrantSize + self._data['originX'] = int(self._data['width'] / 2) + self._data['originY'] = int(self._data['height'] / 2) + collisionTable = [] + horizontalWall = [ 1 for x in xrange(self._data['width']) ] + collisionTable.append(horizontalWall) + for i in xrange(0, len(self.quadrantData), self.width): + for y in xrange(self.quadrantSize): + row = [1] + for x in xrange(i, i + self.width): + if x == 1 and y < self.quadrantSize / 2 - 2: + newData = [] + for j in self.quadrantData[x][1][y]: + if j == 0: + newData.append(2) + else: + newData.append(j + 0) + + row += newData + [1] + else: + row += self.quadrantData[x][1][y] + [1] + + collisionTable.append(row) + + collisionTable.append(horizontalWall[:]) + + barriers = Globals.MazeBarriers + for i in xrange(len(barriers)): + for coords in barriers[i]: + collisionTable[coords[1]][coords[0]] = 0 + + y = self._data['originY'] + for x in xrange(len(collisionTable[y])): + if collisionTable[y][x] == 0: + collisionTable[y][x] = 2 + + x = self._data['originX'] + for y in xrange(len(collisionTable)): + if collisionTable[y][x] == 0: + collisionTable[y][x] = 2 + + self._data['collisionTable'] = collisionTable + + def _loadAndBuildMazeModel(self, flatten = False): + self.getMazeData() + self._model = NodePath('CogdoMazeModel') + levelModel = CogdoUtil.loadMazeModel('level') + self.quadrants = [] + quadrantUnitSize = int(self.quadrantSize * self.cellWidth) + frameActualSize = self.frameWallThickness * self.cellWidth + size = quadrantUnitSize + frameActualSize + halfWidth = int(self.width / 2) + halfHeight = int(self.height / 2) + i = 0 + for y in xrange(self.height): + for x in xrange(self.width): + ax = (x - halfWidth) * size + ay = (y - halfHeight) * size + extension = '' + if hasattr(getBase(), 'air'): + extension = '.bam' + filepath = self.quadrantData[i][0] + extension + angle = self.quadrantData[i][2] + m = self._createQuadrant(filepath, i, angle, quadrantUnitSize) + m.setPos(ax, ay, 0) + m.reparentTo(self._model) + self.quadrants.append(m) + i += 1 + + quadrantHalfUnitSize = quadrantUnitSize * 0.5 + barrierModel = CogdoUtil.loadMazeModel('grouping_blockerDivider').find('**/divider') + y = 3 + for x in xrange(self.width): + if x == (self.width - 1) / 2: + continue + ax = (x - halfWidth) * size + ay = (y - halfHeight) * size - quadrantHalfUnitSize - (self.cellWidth - 0.5) + b = NodePath('barrier') + barrierModel.instanceTo(b) + b.setPos(ax, ay, 0) + b.reparentTo(self._model) + + offset = self.cellWidth - 0.5 + for x in (0, 3): + for y in xrange(self.height): + ax = (x - halfWidth) * size - quadrantHalfUnitSize - frameActualSize + offset + ay = (y - halfHeight) * size + b = NodePath('barrier') + barrierModel.instanceTo(b) + b.setPos(ax, ay, 0) + b.setH(90) + b.reparentTo(self._model) + + offset -= 2.0 + + barrierModel.removeNode() + levelModel.getChildren().reparentTo(self._model) + for np in self._model.findAllMatches('**/*lightCone*'): + CogdoUtil.initializeLightCone(np, 'fixed', 3) + + if flatten: + self._model.flattenStrong() + return self._model + + def _createQuadrant(self, filepath, serialNum, angle, size): + root = NodePath('QuadrantRoot-%i' % serialNum) + quadrant = loader.loadModel(filepath) + quadrant.getChildren().reparentTo(root) + root.setH(angle) + return root diff --git a/toontown/cogdominium/CogdoMazeCameraManager.py b/toontown/cogdominium/CogdoMazeCameraManager.py new file mode 100755 index 00000000..8d48d2d8 --- /dev/null +++ b/toontown/cogdominium/CogdoMazeCameraManager.py @@ -0,0 +1,135 @@ +from direct.showbase.PythonUtil import bound as clamp +import CogdoMazeGameGlobals as Globals +import math +import random + +class CogdoMazeCameraManager: + toonJumpSpeed = 30.0 + toonJumpDir = 1.0 + toonMaxHeight = 1.8 + maxHeightOffset = 1.5 + savedShakeStrength = 0.0 + toonIsShaking = False + rumbleOffDuration = 2.0 + rumbleOnDuration = 1.5 + rumbleOffTimer = 0.0 + rumbleOnTimer = -1 + savedCamZ = 0.0 + savedCamX = 0.0 + shakeStrengthThreshold = 2.8 + savedRumbleTimer = 0.0 + rumbleFreq = 0.1 + rumbleSmall = 0.05 + rumbleBig = 0.16 + rumbleMagnitude = rumbleSmall + + def __init__(self, toon, maze, cam, root): + self.toon = toon + self.maze = maze + self.camera = cam + self.root = root + self.shakeStrength = 0.0 + self.shakeOffset = 0.0 + self.shakeTime = 0.0 + self.defaultHeight = 0.0 + self.minPos = self.maze.tile2world(3, 5) + self.maxPos = self.maze.tile2world(self.maze.width - 3, self.maze.height - 3) + self._camAngle = Globals.CameraAngle + self._camDistance = Globals.CameraMinDistance + self._camTargetDistance = self._camDistance + + def enable(self): + self.parent = self.root.attachNewNode('GameCamParent') + self.parent.setPos(self.toon, 0, 0, 0) + self.parent.setHpr(self.root, 180, self._camAngle, 0) + self.camera.reparentTo(self.parent) + self.camera.setPos(0, self._camDistance, 0) + self.camera.lookAt(self.toon) + self.defaultHeight = self.parent.getZ() + self.update(0) + + def setCameraTargetDistance(self, distance): + self._camTargetDistance = distance + + def disable(self): + self.camera.wrtReparentTo(render) + self.parent.removeNode() + del self.parent + + def update(self, dt): + toonPos = self.toon.getPos() + self.parent.setPos(self.toon.getParent(), clamp(toonPos.getX(), self.minPos[0], self.maxPos[0]), clamp(toonPos.getY(), self.minPos[1], self.maxPos[1]), 0) + if self._camDistance != self._camTargetDistance: + self._updateCameraDistance() + if self.shakeOffset > 0 or self.shakeStrength > 0: + self.updateShake(dt) + self.updateRumble(dt) + + def _updateCameraDistance(self): + if self._camDistance < self._camTargetDistance: + self._camDistance += min(0.4 * (self._camDistance / self._camTargetDistance), self._camTargetDistance - self._camDistance) + elif self._camDistance > self._camTargetDistance: + self._camDistance += max(-0.4 * (self._camDistance / self._camTargetDistance), self._camTargetDistance - self._camDistance) + self.camera.setY(self._camDistance) + + def updateShake(self, dt): + if self.shakeStrength > 0: + if self.shakeStrength > Globals.CameraShakeMax: + self.shakeStrength = Globals.CameraShakeMax + height = self.defaultHeight + self.shakeStrength + self.shakeStrength = self.shakeStrength - Globals.CameraShakeFalloff * dt + if self.shakeStrength < 0.0: + self.shakeStrength = 0.0 + else: + height = self.shakeStrength + + def shake(self, strength): + self.shakeStrength += strength * 1.5 + self.toonIsShaking = True + + def updateToonShake(self, dt): + if self.toonIsShaking: + newHeight = self.toon.getZ() + self.toonJumpDir * dt * self.savedShakeStrength * self.toonJumpSpeed + maxHeight = self.savedShakeStrength * self.maxHeightOffset + if maxHeight > self.toonMaxHeight: + maxHeight = self.toonMaxHeight + if newHeight >= maxHeight: + newHeight = maxHeight + self.toonJumpDir = -1.0 + elif newHeight <= 0.0: + newHeight = 0.0 + self.toonJumpDir = 1.0 + self.toonIsShaking = False + self.toon.setZ(newHeight) + + def updateRumble(self, dt): + if self.rumbleOnTimer == -1: + self.rumbleOffTimer += dt + if self.rumbleOffTimer > self.rumbleOffDuration: + self.rumbleOnTimer = 0.0 + self.savedRumbleTimer = 0.0 + self.rumbleOffTimer = -1 + self.rumbleOnDuration = 1.5 + self.savedCamZ = self.camera.getZ() + self.savedCamX = self.camera.getX() + else: + if self.rumbleOnTimer > self.savedRumbleTimer + self.rumbleFreq: + self.savedRumbleTimer += self.rumbleFreq + self.rumble(self.rumbleMagnitude) + self.rumbleOnTimer += dt + if self.rumbleOnTimer > self.rumbleOnDuration: + self.rumbleOffTimer = 0.0 + self.rumbleOnTimer = -1 + if self.shakeStrength > self.shakeStrengthThreshold: + self.rumbleOffDuration = 0.5 + self.rumbleMagnitude = self.rumbleBig + else: + self.rumbleOffDuration = random.uniform(1.0, 3.0) + self.rumbleMagnitude = self.rumbleSmall + + def rumble(self, magnitude): + self.rumbleMagnitude *= -1.0 + dz = self.rumbleMagnitude + self.camera.setZ(self.savedCamZ + dz) + dx = dz + self.camera.setZ(self.savedCamX + dx) diff --git a/toontown/cogdominium/CogdoMazeData.py b/toontown/cogdominium/CogdoMazeData.py new file mode 100755 index 00000000..3b8f633a --- /dev/null +++ b/toontown/cogdominium/CogdoMazeData.py @@ -0,0 +1,9244 @@ +FirstQuadrant = 1 +NumQuadrants = 9 +QuadrantCellWidth = 3 +QuadrantSize = 16 +QuadrantOriginXY = 8 +QuadrantAngles = (0, + 90, + 180, + 270) +QuadrantCollisions = {} +QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant1'] = {} +collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant1'] +collTable[0] = [[0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1], + [0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1], + [0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1]] +collTable[90] = [[1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1]] +collTable[180] = [[1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0], + [1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0], + [1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0]] +collTable[270] = [[1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1]] +QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant2'] = {} +collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant2'] +collTable[0] = [[1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1], + [0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1]] +collTable[90] = [[1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1]] +collTable[180] = [[1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0], + [1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1]] +collTable[270] = [[1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1]] +QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant3'] = {} +collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant3'] +collTable[0] = [[1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1]] +collTable[90] = [[1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1]] +collTable[180] = [[1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1]] +collTable[270] = [[1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1]] +QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant4'] = {} +collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant4'] +collTable[0] = [[1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1]] +collTable[90] = [[1, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1], + [0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0], + [1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1]] +collTable[180] = [[1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1]] +collTable[270] = [[1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1], + [0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0], + [1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 1]] +QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant5'] = {} +collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant5'] +collTable[0] = [[1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1]] +collTable[90] = [[1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1], + [1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1]] +collTable[180] = [[1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 1, + 1]] +collTable[270] = [[1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1], + [1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1]] +QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant6'] = {} +collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant6'] +collTable[0] = [[0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1]] +collTable[90] = [[0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1]] +collTable[180] = [[1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0]] +collTable[270] = [[1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0]] +QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant7'] = {} +collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant7'] +collTable[0] = [[1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1]] +collTable[90] = [[1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1], + [1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1]] +collTable[180] = [[1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1]] +collTable[270] = [[1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1], + [1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1]] +QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant8'] = {} +collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant8'] +collTable[0] = [[1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1]] +collTable[90] = [[1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1]] +collTable[180] = [[1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1]] +collTable[270] = [[1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1]] +QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant9'] = {} +collTable = QuadrantCollisions['phase_5/models/cogdominium/tt_m_ara_cmg_quadrant9'] +collTable[0] = [[1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0]] +collTable[90] = [[1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0]] +collTable[180] = [[0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1]] +collTable[270] = [[0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1]] diff --git a/toontown/cogdominium/CogdoMazeGame.py b/toontown/cogdominium/CogdoMazeGame.py new file mode 100755 index 00000000..d0b5af19 --- /dev/null +++ b/toontown/cogdominium/CogdoMazeGame.py @@ -0,0 +1,582 @@ +from pandac.PandaModules import Point3, CollisionSphere, CollisionNode +from direct.showbase.DirectObject import DirectObject +from direct.showbase.PythonUtil import Functor +from direct.showbase.RandomNumGen import RandomNumGen +from direct.task.Task import Task +from toontown.minigame.MazeSuit import MazeSuit +from toontown.toonbase import ToontownGlobals +from CogdoGameGatherable import CogdoMemo +from CogdoMazePlayer import CogdoMazePlayer +from CogdoMazeLocalPlayer import CogdoMazeLocalPlayer +from CogdoMazeGuiManager import CogdoMazeGuiManager +from CogdoGameAudioManager import CogdoGameAudioManager +from CogdoMazeGameObjects import CogdoMazeExit, CogdoMazeDrop +from CogdoMazeSuits import CogdoMazeSuit, CogdoMazeSlowMinionSuit, CogdoMazeFastMinionSuit, CogdoMazeBossSuit +from CogdoMazeGameMovies import CogdoMazeGameIntro, CogdoMazeGameFinish +import CogdoMazeGameGlobals as Globals +import CogdoUtil +import math +import random + +class CogdoMazeGame(DirectObject): + notify = directNotify.newCategory('CogdoMazeGame') + UpdateTaskName = 'CogdoMazeGameUpdateTask' + RemoveGagTaskName = 'CogdoMazeGameRemoveGag' + PlayerCoolerCollision = '%s-into-%s' % (Globals.LocalPlayerCollisionName, Globals.WaterCoolerCollisionName) + PlayerDropCollision = '%s-into-%s' % (Globals.LocalPlayerCollisionName, Globals.DropCollisionName) + + def __init__(self, distGame): + self.distGame = distGame + self._allowSuitsHitToons = base.config.GetBool('cogdomaze-suits-hit-toons', True) + + def load(self, cogdoMazeFactory, numSuits, bossCode): + self._initAudio() + self.maze = cogdoMazeFactory.createCogdoMaze() + suitSpawnSpot = self.maze.createRandomSpotsList(numSuits, self.distGame.randomNumGen) + self.guiMgr = CogdoMazeGuiManager(self.maze, bossCode) + self.suits = [] + self.suitsById = {} + self.shakers = [] + self.toonsThatRevealedDoor = [] + self.quake = 0 + self.dropCounter = 0 + self.drops = {} + self.gagCounter = 0 + self.gags = [] + self.hackTemp = False + self.dropGen = RandomNumGen(self.distGame.doId) + self.gagTimeoutTasks = [] + self.finished = False + self.lastBalloonTimestamp = None + difficulty = self.distGame.getDifficulty() + serialNum = 0 + for i in xrange(numSuits[0]): + suitRng = RandomNumGen(self.distGame.doId + serialNum * 10) + suit = CogdoMazeBossSuit(serialNum, self.maze, suitRng, difficulty, startTile=suitSpawnSpot[0][i]) + self.addSuit(suit) + self.guiMgr.mazeMapGui.addSuit(suit.suit) + serialNum += 1 + + for i in xrange(numSuits[1]): + suitRng = RandomNumGen(self.distGame.doId + serialNum * 10) + suit = CogdoMazeFastMinionSuit(serialNum, self.maze, suitRng, difficulty, startTile=suitSpawnSpot[1][i]) + self.addSuit(suit) + serialNum += 1 + + for i in xrange(numSuits[2]): + suitRng = RandomNumGen(self.distGame.doId + serialNum * 10) + suit = CogdoMazeSlowMinionSuit(serialNum, self.maze, suitRng, difficulty, startTile=suitSpawnSpot[2][i]) + self.addSuit(suit) + serialNum += 1 + + self.toonId2Door = {} + self.keyIdToKey = {} + self.players = [] + self.toonId2Player = {} + cellPos = (int(self.maze.width / 2), self.maze.height - 1) + pos = self.maze.tile2world(*cellPos) + self._exit = CogdoMazeExit() + self._exit.reparentTo(render) + self._exit.setPos(self.maze.exitPos) + self._exit.stash() + self.guiMgr.mazeMapGui.placeExit(*cellPos) + self._collNode2waterCooler = {} + for waterCooler in self.maze.getWaterCoolers(): + pos = waterCooler.getPos(render) + tpos = self.maze.world2tile(pos[0], pos[1]) + self.guiMgr.mazeMapGui.addWaterCooler(*tpos) + self._collNode2waterCooler[waterCooler.collNode] = waterCooler + + self.pickups = [] + self.gagModel = CogdoUtil.loadMazeModel('waterBalloon') + self._movie = CogdoMazeGameIntro(self.maze, self._exit, self.distGame.randomNumGen) + self._movie.load() + return + + def _initAudio(self): + self._audioMgr = CogdoGameAudioManager(Globals.MusicFiles, Globals.SfxFiles, camera, cutoff=Globals.AudioCutoff) + self._quakeSfx1 = self._audioMgr.createSfx('quake') + self._quakeSfx2 = self._audioMgr.createSfx('quake') + + def _destroyAudio(self): + self._quakeSfx1.destroy() + self._quakeSfx2.destroy() + del self._quakeSfx1 + del self._quakeSfx2 + self._audioMgr.destroy() + del self._audioMgr + + def addSuit(self, suit): + id = suit.serialNum + self.suits.append(suit) + if suit.type == Globals.SuitTypes.Boss: + self.shakers.append(suit) + self.suitsById[id] = suit + + def removeSuit(self, suit): + id = suit.serialNum + del self.suitsById[id] + if suit.type == Globals.SuitTypes.Boss: + self.shakers.remove(suit) + self.guiMgr.showBossCode(id) + self.guiMgr.mazeMapGui.removeSuit(suit.suit) + self.suits.remove(suit) + suit.destroy() + + def unload(self): + self.toonsThatRevealedDoor = [] + for suit in self.suits: + suit.destroy() + + del self.suits + for id in self.drops.keys(): + self.cleanupDrop(id) + + self.__stopUpdateTask() + self.ignoreAll() + self.maze.destroy() + del self.maze + self._exit.destroy() + del self._exit + self.guiMgr.destroy() + del self.guiMgr + for player in self.players: + player.destroy() + + del self.players + del self.toonId2Player + del self.localPlayer + for pickup in self.pickups: + pickup.destroy() + + self._destroyAudio() + del self.distGame + + def onstage(self): + self._exit.onstage() + self.maze.onstage() + + def offstage(self): + self.maze.offstage() + self._exit.offstage() + for suit in self.suits: + suit.offstage() + + def startIntro(self): + self._movie.play() + self._audioMgr.playMusic('normal') + + def endIntro(self): + self._movie.end() + self._movie.unload() + del self._movie + base.camLens.setMinFov(ToontownGlobals.CogdoFov/(4./3.)) + for player in self.players: + self.placePlayer(player) + if player.toon is localAvatar: + localAvatar.sendCurrentPosition() + player.request('Ready') + + def startFinish(self): + self._movie = CogdoMazeGameFinish(self.localPlayer, self._exit) + self._movie.load() + self._movie.play() + + def endFinish(self): + self._movie.end() + self._movie.unload() + del self._movie + + def placeEntranceElevator(self, elevator): + pos = self.maze.elevatorPos + elevator.setPos(pos) + tpos = self.maze.world2tile(pos[0], pos[1]) + self.guiMgr.mazeMapGui.placeEntrance(*tpos) + + def initPlayers(self): + for toonId in self.distGame.getToonIds(): + toon = self.distGame.getToon(toonId) + if toon is not None: + if toon.isLocal(): + player = CogdoMazeLocalPlayer(len(self.players), base.localAvatar, self, self.guiMgr) + self.localPlayer = player + else: + player = CogdoMazePlayer(len(self.players), toon) + self._addPlayer(player) + + return + + def start(self): + self.accept(self.PlayerDropCollision, self.handleLocalToonMeetsDrop) + self.accept(self.PlayerCoolerCollision, self.handleLocalToonMeetsWaterCooler) + self.accept(CogdoMazeExit.EnterEventName, self.handleLocalToonEntersDoor) + self.accept(CogdoMemo.EnterEventName, self.handleLocalToonMeetsPickup) + if self._allowSuitsHitToons: + self.accept(CogdoMazeSuit.COLLISION_EVENT_NAME, self.handleLocalToonMeetsSuit) + self.accept(CogdoMazePlayer.GagHitEventName, self.handleToonMeetsGag) + self.accept(CogdoMazeSuit.GagHitEventName, self.handleLocalSuitMeetsGag) + self.accept(CogdoMazeSuit.DeathEventName, self.handleSuitDeath) + self.accept(CogdoMazeSuit.ThinkEventName, self.handleSuitThink) + self.accept(CogdoMazeBossSuit.ShakeEventName, self.handleBossShake) + self.accept(CogdoMazePlayer.RemovedEventName, self._removePlayer) + self.__startUpdateTask() + for player in self.players: + player.handleGameStart() + player.request('Normal') + + for suit in self.suits: + suit.onstage() + suit.gameStart(self.distGame.getStartTime()) + + def exit(self): + self._quakeSfx1.stop() + self._quakeSfx2.stop() + for suit in self.suits: + suit.gameEnd() + + self.ignore(self.PlayerDropCollision) + self.ignore(self.PlayerCoolerCollision) + self.ignore(CogdoMazeExit.EnterEventName) + self.ignore(CogdoMemo.EnterEventName) + self.ignore(CogdoMazeSuit.COLLISION_EVENT_NAME) + self.ignore(CogdoMazePlayer.GagHitEventName) + self.ignore(CogdoMazeSuit.GagHitEventName) + self.ignore(CogdoMazeSuit.DeathEventName) + self.ignore(CogdoMazeSuit.ThinkEventName) + self.ignore(CogdoMazeBossSuit.ShakeEventName) + self.ignore(CogdoMazePlayer.RemovedEventName) + self.__stopUpdateTask() + for timeoutTask in self.gagTimeoutTasks: + taskMgr.remove(timeoutTask) + + self.finished = True + self.localPlayer.handleGameExit() + for player in self.players: + player.request('Done') + + self.guiMgr.hideTimer() + self.guiMgr.hideMazeMap() + self.guiMgr.hideBossGui() + + def _addPlayer(self, player): + self.players.append(player) + self.toonId2Player[player.toon.doId] = player + + def _removePlayer(self, player): + if player in self.players: + self.players.remove(player) + else: + for cPlayer in self.players: + if cPlayer.toon == player.toon: + self.players.remove(cPlayer) + break + + if player.toon.doId in self.toonId2Player: + del self.toonId2Player[player.toon.doId] + self.guiMgr.mazeMapGui.removeToon(player.toon) + + def handleToonLeft(self, toonId): + self._removePlayer(self.toonId2Player[toonId]) + + def __startUpdateTask(self): + self.__stopUpdateTask() + taskMgr.add(self.__updateTask, CogdoMazeGame.UpdateTaskName, 45) + + def __stopUpdateTask(self): + taskMgr.remove(CogdoMazeGame.UpdateTaskName) + taskMgr.remove('loopSecondQuakeSound') + + def __updateTask(self, task): + dt = globalClock.getDt() + self.localPlayer.update(dt) + for player in self.players: + curTX, curTY = self.maze.world2tileClipped(player.toon.getX(), player.toon.getY()) + self.guiMgr.mazeMapGui.updateToon(player.toon, curTX, curTY) + + self.__updateGags() + if Globals.QuakeSfxEnabled: + self.__updateQuakeSound() + for pickup in self.pickups: + pickup.update(dt) + + MazeSuit.thinkSuits(self.suits, self.distGame.getStartTime()) + return Task.cont + + def __updateGags(self): + remove = [] + for i in xrange(len(self.gags)): + balloon = self.gags[i] + if balloon.isSingleton(): + remove.append(i) + elif balloon.getParent() == render: + loc = self.maze.world2tile(balloon.getX(), balloon.getY()) + if not self.maze.isAccessible(loc[0], loc[1]): + self.removeGag(balloon) + + remove.reverse() + for i in remove: + self.gags.pop(i) + + def __updateQuakeSound(self): + shake = self.localPlayer.getCameraShake() + if shake < 1.0: + shake = 1.0 + volume = 3.0 * shake / Globals.CameraShakeMax + if volume > 3.0: + volume = 3.0 + if self._quakeSfx1.getAudioSound().status() != self._quakeSfx1.getAudioSound().PLAYING: + self._quakeSfx1.loop(volume=volume) + else: + self._quakeSfx1.getAudioSound().setVolume(volume) + volume = shake * shake / Globals.CameraShakeMax + if not self.hackTemp and self._quakeSfx2.getAudioSound().status() != self._quakeSfx2.getAudioSound().PLAYING: + taskMgr.doMethodLater(1.5, self._quakeSfx2.loop, 'loopSecondQuakeSound', extraArgs=[]) + self.hackTemp = True + else: + self._quakeSfx2.getAudioSound().setVolume(volume) + + def handleLocalToonMeetsSuit(self, suitType, suitNum): + if self.localPlayer.state == 'Normal' and not self.localPlayer.invulnerable: + self.distGame.b_toonHitBySuit(suitType, suitNum) + + def toonHitBySuit(self, toonId, suitType, suitNum, elapsedTime = 0.0): + player = self.toonId2Player[toonId] + if player.state == 'Normal': + player.request('Hit', elapsedTime) + + def handleSuitDeath(self, suitType, suitNum): + suit = self.suitsById[suitNum] + self.dropMemos(suit) + self.removeSuit(suit) + + def dropMemos(self, suit): + numDrops = suit.memos + if numDrops > 0: + start = math.radians(random.randint(0, 360)) + step = math.radians(360.0 / numDrops) + radius = 2.0 + for i in xrange(numDrops): + angle = start + i * step + x = radius * math.cos(angle) + suit.suit.getX() + y = radius * math.sin(angle) + suit.suit.getY() + self.generatePickup(x, y) + + def handleSuitThink(self, suit, TX, TY): + if suit in self.shakers: + self.guiMgr.mazeMapGui.updateSuit(suit.suit, TX, TY) + suit.dropTimer += 1 + if suit.dropTimer >= Globals.DropFrequency: + self.doDrop(suit) + suit.dropTimer = 0 + + def doDrop(self, boss): + dropLoc = boss.pickRandomValidSpot() + self.generateDrop(dropLoc[0], dropLoc[1]) + + def handleBossShake(self, suit, strength): + if Globals.BossShakeEnabled: + self.shakeCamera(suit.suit, strength, Globals.BossMaxDistance) + + def randomDrop(self, centerTX, centerTY, radius): + dropArray = [] + for i in xrange(1, distance): + dropArray.append(i) + dropArray.append(-1 * i) + + offsetTX = self.distGame.randomNumGen.choice(dropArray) + offsetTY = self.distGame.randomNumGen.choice(dropArray) + dropTX = sourceTX + offsetTX + dropTY = sourceTY + offsetTY + if self.maze.isWalkable(dropTX, dropTY): + self.generateDrop(dropTX, dropTY) + + def generateDrop(self, TX, TY): + drop = self.maze.tile2world(TX, TY) + ival = self.createDrop(drop[0], drop[1]) + ival.start() + + def createDrop(self, x, y): + self.dropCounter = self.dropCounter + 1 + id = self.dropCounter + drop = CogdoMazeDrop(self, id, x, y) + self.drops[id] = drop + return drop.getDropIval() + + def cleanupDrop(self, id): + if id in self.drops.keys(): + drop = self.drops[id] + drop.destroy() + del self.drops[id] + + def dropHit(self, node, id): + if self.finished: + return + if Globals.DropShakeEnabled: + self.shakeCamera(node, Globals.DropShakeStrength, Globals.DropMaxDistance) + + def shakeCamera(self, node, strength, distanceCutoff = 60.0): + distance = self.localPlayer.toon.getDistance(node) + shake = strength * (1 - distance / distanceCutoff) + if shake > 0: + self.localPlayer.shakeCamera(shake) + + def handleLocalToonMeetsDrop(self, collEntry): + fcabinet = collEntry.getIntoNodePath() + if fcabinet.getTag('isFalling') == str('False'): + return + if self.localPlayer.state == 'Normal' and not self.localPlayer.invulnerable: + self.distGame.b_toonHitByDrop() + + def toonHitByDrop(self, toonId): + player = self.toonId2Player[toonId] + player.hitByDrop() + + def handleLocalToonMeetsGagPickup(self, collEntry): + if self.localPlayer.equippedGag != None: + return + into = collEntry.getIntoNodePath() + if into.hasPythonTag('id'): + id = into.getPythonTag('id') + self.distGame.d_sendRequestGagPickUp(id) + return + + def hasGag(self, toonId, elapsedTime = 0.0): + player = self.toonId2Player[toonId] + player.equipGag() + + def handleLocalToonMeetsWaterCooler(self, collEntry): + if self.localPlayer.equippedGag != None: + return + if self.lastBalloonTimestamp and globalClock.getFrameTime() - self.lastBalloonTimestamp < Globals.BalloonDelay: + return + collNode = collEntry.getIntoNode() + waterCooler = self._collNode2waterCooler[collNode] + self.lastBalloonTimestamp = globalClock.getFrameTime() + self.distGame.d_sendRequestGag(waterCooler.serialNum) + return + + def requestUseGag(self, x, y, h): + self.distGame.b_toonUsedGag(x, y, h) + + def toonUsedGag(self, toonId, x, y, h, elapsedTime = 0.0): + player = self.toonId2Player[toonId] + heading = h + pos = Point3(x, y, 0) + gag = player.showToonThrowingGag(heading, pos) + if gag is not None: + self.gags.append(gag) + return + + def handleToonMeetsGag(self, avId, gag): + self.removeGag(gag) + self.distGame.b_toonHitByGag(avId) + + def toonHitByGag(self, toonId, hitToon, elapsedTime = 0.0): + if toonId not in self.toonId2Player.keys() or hitToon not in self.toonId2Player.keys(): + return + player = self.toonId2Player[hitToon] + player.hitByGag() + + def handleLocalSuitMeetsGag(self, suitType, suitNum, gag): + self.removeGag(gag) + self.localPlayer.hitSuit(suitType) + self.distGame.b_suitHitByGag(suitType, suitNum) + + def suitHitByGag(self, toonId, suitType, suitNum, elapsedTime = 0.0): + if suitType == Globals.SuitTypes.Boss: + self.guiMgr.showBossHit(suitNum) + if suitNum in self.suitsById.keys(): + suit = self.suitsById[suitNum] + suit.hitByGag() + + def removeGag(self, gag): + gag.detachNode() + + def toonRevealsDoor(self, toonId): + self._exit.revealed = True + + def openDoor(self, timeLeft): + self._audioMgr.playMusic('timeRunningOut') + if not self._exit.isOpen(): + self._exit.open() + self.localPlayer.handleOpenDoor(self._exit) + self.guiMgr.showTimer(timeLeft, self._handleTimerExpired) + self.guiMgr.mazeMapGui.showExit() + self.guiMgr.mazeMapGui.revealAll() + + def countdown(self, timeLeft): + self.guiMgr.showTimer(timeLeft, self._handleTimerExpired) + + def timeAlert(self): + self._audioMgr.playMusic('timeRunningOut') + + def _handleTimerExpired(self): + self.localPlayer.request('Done') + + def toonEntersDoor(self, toonId): + player = self.toonId2Player[toonId] + self.guiMgr.mazeMapGui.removeToon(player.toon) + self._exit.playerEntersDoor(player) + self.localPlayer.handleToonEntersDoor(toonId, self._exit) + player.request('Done') + + def generatePickup(self, x, y): + pickup = CogdoMemo(len(self.pickups), pitch=-90) + self.pickups.append(pickup) + pickup.reparentTo(self.maze.maze) + pickup.setPos(x, y, 1) + pickup.enable() + + def handleLocalToonMeetsPickup(self, pickup): + pickup.disable() + self.distGame.d_sendRequestPickUp(pickup.serialNum) + + def pickUp(self, toonId, pickupNum, elapsedTime = 0.0): + self.notify.debugCall() + player = self.toonId2Player[toonId] + pickup = self.pickups[pickupNum] + if not pickup.wasPickedUp(): + pickup.pickUp(player.toon, elapsedTime) + self.localPlayer.handlePickUp(toonId) + + def placePlayer(self, player): + toonIds = self.distGame.getToonIds() + if player.toon.doId not in toonIds: + return + i = toonIds.index(player.toon.doId) + x = int(self.maze.width / 2.0) - int(len(toonIds) / 2.0) + i + y = 2 + while not self.maze.isAccessible(x, y): + y += 1 + + pos = self.maze.tile2world(x, y) + player.toon.setPos(pos[0], pos[1], 0) + self.guiMgr.mazeMapGui.addToon(player.toon, x, y) + + def handleLocalToonEntersDoor(self, door): + localToonId = self.localPlayer.toon.doId + if self._exit.isOpen(): + self.distGame.d_sendRequestAction(Globals.GameActions.EnterDoor, 0) + else: + if localToonId not in self.toonsThatRevealedDoor: + self.toonsThatRevealedDoor.append(localToonId) + self.localPlayer.handleToonRevealsDoor(localToonId, self._exit) + if not self._exit.revealed: + self.toonRevealsDoor(localToonId) + self.distGame.d_sendRequestAction(Globals.GameActions.RevealDoor, 0) + + def handleToonWentSad(self, toonId): + if toonId == self.localPlayer.toon.doId: + for player in self.players: + player.removeGag() + + elif toonId in self.toonId2Player.keys(): + player = self.toonId2Player[toonId] + player.removeGag() + + def handleToonDisconnected(self, toonId): + if toonId == self.localPlayer.toon.doId: + pass + elif toonId in self.toonId2Player.keys(): + player = self.toonId2Player[toonId] + self._removePlayer(player) diff --git a/toontown/cogdominium/CogdoMazeGameGlobals.py b/toontown/cogdominium/CogdoMazeGameGlobals.py new file mode 100755 index 00000000..53da6449 --- /dev/null +++ b/toontown/cogdominium/CogdoMazeGameGlobals.py @@ -0,0 +1,235 @@ +from direct.showbase import PythonUtil +from pandac.PandaModules import VBase4 +GameActions = PythonUtil.Enum(('EnterDoor', + 'RevealDoor', + 'OpenDoor', + 'Countdown', + 'TimeAlert')) +SecondsUntilTimeout = 4.0 * 60.0 +SecondsUntilGameEnds = 60.0 +SecondsForTimeAlert = 60.0 +MaxPlayers = 4 +IntroDurationSeconds = 24.0 +FinishDurationSeconds = 5.0 +PlayerCollisionName = 'CogdoMazePlayer_Collision' +LocalPlayerCollisionName = 'CogdoMazeLocalPlayer_Collision' +PlayerCollisionRadius = 1.0 +HitCooldownTime = 2.0 +HintTimeout = 6.0 +NumQuadrants = (3, 3) +FrameWallThickness = 1 +QuadrantUnitGap = 3 +TotalBarriers = 12 +NumBarriers = 3 +MazeBarriers = ([(7, 34), + (8, 34), + (9, 34), + (10, 34)], + [(24, 34), + (25, 34), + (26, 34), + (27, 34)], + [(41, 34), + (42, 34), + (43, 34), + (44, 34)], + [(7, 17), + (8, 17), + (9, 17), + (10, 17)], + [(24, 17), + (25, 17), + (26, 17), + (27, 17)], + [(41, 17), + (42, 17), + (43, 17), + (44, 17)], + [(17, 41), + (17, 42), + (17, 43), + (17, 44)], + [(17, 24), + (17, 25), + (17, 26), + (17, 27)], + [(17, 7), + (17, 8), + (17, 9), + (17, 10)], + [(34, 41), + (34, 42), + (34, 43), + (34, 44)], + [(34, 24), + (34, 25), + (34, 26), + (34, 27)], + [(34, 7), + (34, 8), + (34, 9), + (34, 10)]) +ToonRunSpeed = 11.2 +CameraAngle = 60 +CameraRemoteToonRadius = 6 +CameraMinDistance = 40 +CameraMaxDistance = 61 +CamCutoffFactor = 1.34 +ToonAnimationInfo = {'hit': ('slip-backward', 2.25, 12)} +NumPickups = 256 +PickupsUntilDoorOpens = int(NumPickups * 0.6) +SuitCollisionName = 'CogdoMazeSuit_Collision' +SuitWalkSameDirectionProb = 1 +SuitWalkTurnAroundProb = 100 +SuitTypes = PythonUtil.Enum(('Boss', 'FastMinion', 'SlowMinion')) +SuitData = {} +SuitData[SuitTypes.Boss] = {'dnaName': 'ms', + 'cellWalkPeriod': 192, + 'toonDamage': 3.0, + 'scale': 2.5, + 'hp': 2, + 'memos': 0} +SuitData[SuitTypes.FastMinion] = {'dnaName': 'nd', + 'cellWalkPeriod': 64, + 'toonDamage': 1.0, + 'scale': 1.3, + 'hp': 1, + 'memos': 3} +SuitData[SuitTypes.SlowMinion] = {'dnaName': 'cc', + 'cellWalkPeriod': 160, + 'toonDamage': 2.0, + 'scale': 1.33, + 'hp': 1, + 'memos': 2} +NumSuits = (4, 5, 5) +BossSpinTime = 1.0 +BossSpinCount = 2 +BlinkFrequency = 1.0 +BlinkSpeed = 0.5 +BlinkColor = VBase4(1.0, 0.4, 0.4, 1.0) +SuitsModifier = (0, 6, 9) +DamageModifier = 9.0 +DropShakeEnabled = True +BossShakeEnabled = True +DropShakeStrength = 4.0 +DropMaxDistance = 20.0 +BossShakeStrength = 1.2 +BossMaxDistance = 25.0 +BossShakeTime = 0.53 +BossStompSfxCutoff = 70.0 +BossCogStompAnimationPlayrateFactor = 0.75 +CameraShakeFalloff = 2.2 +CameraShakeMax = 5.0 +QuakeSfxFalloff = 0.01 +QuakeSfxMax = 2.0 +QuakeSfxEnabled = True +DropFrequency = 3 +DropDamage = 0 +DropTime = 1.0 +ShadowTime = 2.0 +DropHeight = 70 +DropFadeTime = 1.0 +DropCollisionRadius = 1.0 +DropCollisionName = 'DropCollision' +DroppedCollisionRadius = 2.0 +DropChance = 0.25 +GagChance = 0.5 +GagSitTime = 15.0 +BalloonDelay = 1.2 +ThrowDistance = 18 +ThrowDuration = 0.5 +ThrowStartFrame = 61 +ThrowEndFrame = 64 +ThrowPlayRate = 1.5 +GagPickupScale = 2.0 +GagPickupCollisionRadius = 1.0 +GagPickupCollisionName = 'PickUpCollision' +GagColors = ((1.0, + 0.27, + 0.27, + 1.0), + (1.0, + 0.66, + 0.15, + 1.0), + (0.31, + 1.0, + 0.29, + 1.0), + (0.31, + 0.62, + 1.0, + 1.0), + (0.91, + 0.32, + 1.0, + 1.0)) +GagCollisionName = 'Gag_Collision' +WaterCoolerTriggerRadius = 2.5 +WaterCoolerTriggerOffset = (0, -1.5, 0) +WaterCoolerCollisionName = 'WaterCooler_Collision' +WaterCoolerShowEventName = 'CogdoMazeWaterCooler_Show' +WaterCoolerHideEventName = 'CogdoMazeWaterCooler_Hide' +AudioCutoff = 75.0 +MusicFiles = {'normal': 'phase_9/audio/bgm/CHQ_FACT_bg.ogg', + 'timeRunningOut': 'phase_7/audio/bgm/encntr_suit_winning_indoor.ogg'} +SfxFiles = {'toonHitByDrop': 'phase_5/audio/sfx/tt_s_ara_cmg_toonHit.ogg', + 'toonHit': 'phase_4/audio/sfx/MG_cannon_hit_dirt.ogg', + 'getMemo': 'phase_4/audio/sfx/MG_maze_pickup.ogg', + 'drop': 'phase_5/audio/sfx/tt_s_ara_cmg_itemHitsFloor.ogg', + 'throw': 'phase_3.5/audio/sfx/AA_pie_throw_only.ogg', + 'splat': 'phase_5/audio/sfx/SA_watercooler_spray_only.ogg', + 'cogSpin': 'phase_3.5/audio/sfx/Cog_Death.ogg', + 'cogDeath': 'phase_3.5/audio/sfx/ENC_cogfall_apart_1.ogg', + 'bossCogAngry': 'phase_5/audio/sfx/tt_s_ara_cmg_bossCogAngry.ogg', + 'cogStomp': 'phase_5/audio/sfx/tt_s_ara_cmg_cogStomp.ogg', + 'quake': 'phase_5/audio/sfx/tt_s_ara_cmg_groundquake.ogg', + 'waterCoolerFill': 'phase_5/audio/sfx/tt_s_ara_cmg_waterCoolerFill.ogg', + 'lose': 'phase_4/audio/sfx/MG_lose.ogg', + 'win': 'phase_4/audio/sfx/MG_win.ogg', + 'cogDialogue': 'phase_3.5/audio/dial/COG_VO_statement.ogg', + 'toonDialogue': 'phase_3.5/audio/dial/AV_dog_long.ogg'} +MessageLabelPos = (0.0, 0.0, -0.4) +MemoGuiPos = (-0.85, 0, -0.9) +MemoGuiTextScale = 0.1 +MemoGuiTextColor = (0.95, + 0.95, + 0, + 1) +MapGuiBgColor = (0.9, 0.9, 0.9) +MapGuiFgColor = (0.5, + 0.5, + 0.5, + 1) +MapGuiPos = (-0.283, 0, 0.29) +MapGuiScale = 0.225 +MapGuiSuitMarkerFlashColor = (1.0, 0.0, 0.0) +MapGuiSuitMarkerSize = 0.075 +MapGuiWaterCoolerMarkerSize = 0.08 +QuestArrowScale = 5 +QuestArrowColor = (1, + 1, + 0, + 1) +CoolerArrowScale = 8 +CoolerArrowColor = (1, + 1, + 0, + 1) +CoolerArrowZ = 10 +CoolerArrowBounce = 2 +CoolerArrowSpeed = 2 +BossGuiScale = 0.8 +BossGuiPos = (0, 0, -0.83) +BossGuiTitleLabelScale = 0.055 +BossCodeFrameWidth = 0.13 +BossCodeFrameGap = 0.005 +BossCodeFrameLabelScale = 0.12 +BossCodeFrameLabelNormalColor = (0, + 0, + 0, + 1) +BossCodeFrameLabelHighlightColor = (0, + 0.5, + 0, + 1) diff --git a/toontown/cogdominium/CogdoMazeGameGuis.py b/toontown/cogdominium/CogdoMazeGameGuis.py new file mode 100755 index 00000000..45e26a26 --- /dev/null +++ b/toontown/cogdominium/CogdoMazeGameGuis.py @@ -0,0 +1,267 @@ +from direct.gui.DirectLabel import DirectLabel +from direct.gui.DirectGui import * +from direct.task.Task import Task +from direct.interval.MetaInterval import Sequence, Parallel +from direct.interval.FunctionInterval import Wait, Func +from pandac.PandaModules import TextNode, NodePath, Point3, CardMaker +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownIntervals +from toontown.minigame.MazeMapGui import MazeMapGui +import CogdoMazeGameGlobals as Globals +import CogdoUtil + +class CogdoMazeMapGui(MazeMapGui): + + def __init__(self, mazeCollTable): + MazeMapGui.__init__(self, mazeCollTable, bgColor=Globals.MapGuiBgColor, fgColor=Globals.MapGuiFgColor) + self._suit2marker = {} + self._initModel() + self.setPos(*Globals.MapGuiPos) + self.setScale(Globals.MapGuiScale) + self.reparentTo(base.a2dBottomRight) + + def destroy(self): + for marker in self._suit2marker.values(): + marker.removeNode() + + del self._suit2marker + self._entrance.removeNode() + del self._entrance + self._exit.removeNode() + del self._exit + del self._exitOpen + del self._exitClosed + self._suitMarkerTemplate.removeNode() + del self._suitMarkerTemplate + self._waterCoolerTemplate.removeNode() + del self._waterCoolerTemplate + MazeMapGui.destroy(self) + + def _initModel(self): + baseName = '**/tt_t_gui_cmg_miniMap_' + cardModel = CogdoUtil.loadMazeModel('miniMap_cards', group='gui') + cm = CardMaker('bg') + cm.setFrame(-1.1, 1.1, -1.1, 1.1) + bg = self.attachNewNode(cm.generate()) + bg.setColor(*self._bgColor) + bg.setBin('fixed', 0) + frame = cardModel.find(baseName + 'frame') + frame.reparentTo(self) + frame.setScale(2.5) + frame.setPos(0.01, 0, -0.01) + self._entrance = cardModel.find(baseName + 'entrance') + self._entrance.reparentTo(self) + self._entrance.setScale(0.35) + self._entrance.hide() + self._exit = NodePath('exit') + self._exit.setScale(0.35) + self._exit.reparentTo(self) + self._exitOpen = cardModel.find(baseName + 'exitOpen') + self._exitOpen.reparentTo(self._exit) + self._exitClosed = cardModel.find(baseName + 'exitClosed') + self._exitClosed.reparentTo(self._exit) + self._suitMarkerTemplate = cardModel.find(baseName + 'cogIcon') + self._suitMarkerTemplate.detachNode() + self._suitMarkerTemplate.setScale(0.225) + self._waterCoolerTemplate = cardModel.find(baseName + 'waterDrop') + self._waterCoolerTemplate.detachNode() + self._waterCoolerTemplate.setScale(0.225) + self._exit.hide() + cardModel.removeNode() + + def addWaterCooler(self, tX, tY): + marker = NodePath('WaterCoolerMarker-%i-%i' % (tX, tY)) + self._waterCoolerTemplate.copyTo(marker) + marker.reparentTo(self.maskedLayer) + x, y = self.tile2gui(tX, tY) + marker.setPos(*self.gui2pos(x, y)) + + def addSuit(self, suit): + marker = NodePath('SuitMarker-%i' % len(self._suit2marker)) + self._suitMarkerTemplate.copyTo(marker) + marker.reparentTo(self) + self._suit2marker[suit] = marker + + def removeSuit(self, suit): + self._suit2marker[suit].removeNode() + del self._suit2marker[suit] + + def updateSuit(self, suit, tX, tY): + x, y = self.tile2gui(tX, tY) + self._suit2marker[suit].setPos(*self.gui2pos(x, y)) + + def showExit(self): + self._exit.show() + self._exitClosed.hide() + + def hideExit(self): + self._exit.hide() + + def placeExit(self, tX, tY): + x, y = self.tile2gui(tX, tY) + self._exit.setPos(*self.gui2pos(x, y)) + self._exit.setZ(self._exit, 0.3) + + def placeEntrance(self, tX, tY): + x, y = self.tile2gui(tX, tY) + self._entrance.setPos(*self.gui2pos(x, y)) + self._entrance.setZ(self._entrance, -0.35) + self._entrance.show() + + +class CogdoMazeBossCodeFrame(DirectFrame): + + def __init__(self, id, code, modelToCopy): + DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX) + self._id = id + self._model = modelToCopy.copyTo(self) + self._model.setPos(0, 0, 0) + self._bg = self._model.find('**/bossBackground') + self._bossIcon = self._model.find('**/bossIcon') + self._bossIconX = self._model.find('**/bossIconX') + self._bossIconX.reparentTo(self._bossIcon) + self._bossIconX.hide() + self._bg.hide() + self._bossIcon.setBin('fixed', 2) + self._bg.setBin('fixed', 3) + self._label = DirectLabel(parent=self._bg, relief=None, scale=Globals.BossCodeFrameLabelScale, text=code, pos=(0, 0, -0.03), text_align=TextNode.ACenter, text_fg=Globals.BossCodeFrameLabelNormalColor, text_shadow=(0, 0, 0, 0), text_font=ToontownGlobals.getSuitFont()) + return + + def destroy(self): + ToontownIntervals.cleanup('boss_code%i' % self._id) + DirectFrame.destroy(self) + + def showNumber(self): + self.setHit(False) + self._bossIconX.show() + ToontownIntervals.cleanup('boss_code%i' % self._id) + ToontownIntervals.start(Sequence(Parallel(ToontownIntervals.getPresentGuiIval(self._bossIcon, '', startPos=(0, 0, -0.15))), Wait(1.0), ToontownIntervals.getPulseLargerIval(self._bg, ''), name='boss_code%i' % self._id)) + + def setHit(self, hit): + if hit: + self._model.setColorScale(Globals.BlinkColor) + else: + self._model.setColorScale(1.0, 1.0, 1.0, 1.0) + + def highlightNumber(self): + self._label['text_fg'] = Globals.BossCodeFrameLabelHighlightColor + + +class CogdoMazeBossGui(DirectFrame): + + def __init__(self, code): + DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX) + self._code = str(code) + self._codeLength = len(self._code) + self._markersShown = 0 + self._markers = [] + self._initModel() + self.setPos(*Globals.BossGuiPos) + self.setScale(Globals.BossGuiScale) + self.hide() + return + + def destroy(self): + ToontownIntervals.cleanup('bosscodedoor') + self._model.removeNode() + del self._model + self._titleLabel.removeNode() + del self._titleLabel + for marker in self._markers: + marker.destroy() + + del self._markers + DirectFrame.destroy(self) + + def _initModel(self): + codeFrameGap = Globals.BossCodeFrameGap + codeFrameWidth = Globals.BossCodeFrameWidth + self._model = CogdoUtil.loadMazeModel('bossCog', group='gui') + self._model.reparentTo(self) + self._model.find('**/frame').setBin('fixed', 1) + titleLabelPos = self._model.find('**/title_label_loc').getPos() + self._titleLabel = DirectLabel(parent=self, relief=None, scale=Globals.BossGuiTitleLabelScale, text=TTLocalizer.CogdoMazeGameBossGuiTitle.upper(), pos=titleLabelPos, text_align=TextNode.ACenter, text_fg=(0, 0, 0, 1), text_shadow=(0, 0, 0, 0), text_font=ToontownGlobals.getSuitFont()) + self._titleLabel.setBin('fixed', 1) + bossCard = self._model.find('**/bossCard') + self._openDoor = self._model.find('**/doorOpen') + self._closedDoor = self._model.find('**/doorClosed') + self._openDoor.stash() + spacingX = codeFrameWidth + codeFrameGap + startX = -0.5 * ((self._codeLength - 1) * spacingX - codeFrameGap) + for i in xrange(self._codeLength): + marker = CogdoMazeBossCodeFrame(i, self._code[i], bossCard) + marker.reparentTo(self) + marker.setPos(bossCard, startX + spacingX * i, 0, 0) + self._markers.append(marker) + + bossCard.removeNode() + return + + def showHit(self, bossIndex): + self._markers[bossIndex].setHit(True) + + def showNumber(self, bossIndex): + self._markers[bossIndex].setHit(False) + self._markers[bossIndex].showNumber() + self._markersShown += 1 + if self._markersShown == self._codeLength: + self._openDoor.unstash() + self._closedDoor.stash() + ToontownIntervals.start(ToontownIntervals.getPulseLargerIval(self._openDoor, 'bosscodedoor')) + + +class CogdoMazeHud: + + def __init__(self): + self._update = None + self._initQuestArrow() + return + + def _initQuestArrow(self): + matchingGameGui = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + arrow = matchingGameGui.find('**/minnieArrow') + arrow.setScale(Globals.QuestArrowScale) + arrow.setColor(*Globals.QuestArrowColor) + arrow.setHpr(90, -90, 0) + self._questArrow = NodePath('Arrow') + arrow.reparentTo(self._questArrow) + self._questArrow.reparentTo(render) + self.hideQuestArrow() + matchingGameGui.removeNode() + + def destroy(self): + self.__stopUpdateTask() + self._questArrow.removeNode() + self._questArrow = None + return + + def showQuestArrow(self, parent, nodeToPoint, offset = Point3(0, 0, 0)): + self._questArrowNodeToPoint = nodeToPoint + self._questArrowParent = parent + self._questArrowOffset = offset + self._questArrow.unstash() + self._questArrowVisible = True + self.__startUpdateTask() + + def hideQuestArrow(self): + self._questArrow.stash() + self.__stopUpdateTask() + self._questArrowVisible = False + self._questArrowNodeToPoint = None + return + + def __startUpdateTask(self): + self.__stopUpdateTask() + self._update = taskMgr.add(self._updateTask, 'CogdoMazeHud_Update', 45) + + def __stopUpdateTask(self): + if self._update is not None: + taskMgr.remove(self._update) + return + + def _updateTask(self, task): + if self._questArrowVisible: + self._questArrow.setPos(self._questArrowParent, self._questArrowOffset) + self._questArrow.lookAt(self._questArrowNodeToPoint) + return Task.cont diff --git a/toontown/cogdominium/CogdoMazeGameMovies.py b/toontown/cogdominium/CogdoMazeGameMovies.py new file mode 100755 index 00000000..a38d31e6 --- /dev/null +++ b/toontown/cogdominium/CogdoMazeGameMovies.py @@ -0,0 +1,198 @@ +from pandac.PandaModules import NodePath, Point3, PlaneNode +from direct.showbase.ShowBase import Plane +from direct.showbase.RandomNumGen import RandomNumGen +from direct.interval.MetaInterval import Sequence, Parallel +from direct.interval.FunctionInterval import Func, Wait +from toontown.toonbase import TTLocalizer +from toontown.suit import Suit, SuitDNA +from toontown.toon import Toon, ToonHead, ToonDNA +from CogdoUtil import CogdoGameMovie +import CogdoMazeGameGlobals as Globals +import CogdoUtil + +class CogdoMazeGameIntro(CogdoGameMovie): + + def __init__(self, maze, exit, rng): + CogdoGameMovie.__init__(self) + self._maze = maze + self._exit = exit + self._rng = RandomNumGen(rng) + self._camTarget = None + self._state = 0 + self._suits = [] + return + + def _getRandomLine(self, lineList): + return CogdoUtil.getRandomDialogueLine(lineList, self._rng) + + def displayLine(self, who, text): + self._dialogueLabel.node().setText(text) + if who == 'toon': + self.toonHead.reparentTo(aspect2d) + self.cogHead.reparentTo(hidden) + self._toonDialogueSfx.play() + self.toonHead.setClipPlane(self.clipPlane) + else: + self.toonHead.reparentTo(hidden) + self.cogHead.reparentTo(aspect2d) + self._cogDialogueSfx.play() + self.cogHead.setClipPlane(self.clipPlane) + + def makeSuit(self, suitType): + suit = Suit.Suit() + dna = SuitDNA.SuitDNA() + dna.newSuit(suitType) + suit.setStyle(dna) + suit.isDisguised = 1 + suit.generateSuit() + suit.setScale(1.05, 1.05, 2.05) + suit.setPos(0, 0, -4.4) + suit.reparentTo(self.toonHead) + for part in suit.getHeadParts(): + part.hide() + + suit.loop('neutral') + + def load(self): + CogdoGameMovie.load(self) + self.toonDNA = ToonDNA.ToonDNA() + self.toonDNA.newToonFromProperties('dss', 'ss', 'm', 'm', 2, 0, 2, 2, 1, 8, 1, 8, 1, 14) + self.toonHead = Toon.Toon() + self.toonHead.setDNA(self.toonDNA) + self.makeSuit('sc') + self.toonHead.getGeomNode().setDepthWrite(1) + self.toonHead.getGeomNode().setDepthTest(1) + self.toonHead.loop('neutral') + self.toonHead.setPosHprScale(-0.73, 0, -1.27, 180, 0, 0, 0.18, 0.18, 0.18) + self.toonHead.reparentTo(hidden) + self.toonHead.startBlink() + self.cogHead = Suit.Suit() + self.cogDNA = SuitDNA.SuitDNA() + self.cogDNA.newSuit('ms') + self.cogHead.setDNA(self.cogDNA) + self.cogHead.getGeomNode().setDepthWrite(1) + self.cogHead.getGeomNode().setDepthTest(1) + self.cogHead.loop('neutral') + self.cogHead.setPosHprScale(-0.73, 0, -1.46, 180, 0, 0, 0.14, 0.14, 0.14) + self.cogHead.reparentTo(hidden) + self.cogHead.nametag3d.hide() + self.clipPlane = self.toonHead.attachNewNode(PlaneNode('clip')) + self.clipPlane.node().setPlane(Plane(0, 0, 1, 0)) + self.clipPlane.setPos(0, 0, 2.45) + audioMgr = base.cogdoGameAudioMgr + self._cogDialogueSfx = audioMgr.createSfx('cogDialogue') + self._toonDialogueSfx = audioMgr.createSfx('toonDialogue') + suitData = Globals.SuitData[Globals.SuitTypes.Boss] + bossSuit = Suit.Suit() + d = SuitDNA.SuitDNA() + d.newSuit(suitData['dnaName']) + bossSuit.setDNA(d) + bossSuit.nametag3d.stash() + bossSuit.nametag.destroy() + bossSuit.setScale(suitData['scale']) + bossSuit.loop('neutral') + bossSuit.reparentTo(render) + bossSuit.setPos(self._exit, -5, -5, 0) + bossSuit.lookAt(self._exit) + self._suits.append(bossSuit) + self._camHelperNode = NodePath('CamHelperNode') + self._camHelperNode.reparentTo(render) + dialogue = TTLocalizer.CogdoMazeIntroMovieDialogue + introDuration = Globals.IntroDurationSeconds + waitDuration = introDuration / len(dialogue) + + def start(): + camera.wrtReparentTo(render) + self._exit.open(animate=False) + + def showBoss(): + self._setCamTarget(bossSuit, 20, offset=Point3(0, 0, 7), angle=Point3(0, 15, 0)) + bossSuit.loop('victory') + self._state = 1 + + def showExit(): + self._setCamTarget(self._exit, 10, offset=Point3(0, 0, 0), angle=Point3(0, 60, 0)) + self._exit.close() + self._state = 2 + + showExitIval = Parallel(camera.posInterval(waitDuration * 0.5, (10, -25, 20), other=self._exit, blendType='easeInOut'), Sequence(Wait(waitDuration * 0.25), Func(bossSuit.play, 'effort'), camera.hprInterval(waitDuration * 0.25, (30, -30, 0), blendType='easeInOut'), Func(self._exit.close), Wait(waitDuration * 0.5))) + + def showWaterCooler(): + wc = self._maze.getWaterCoolers()[0] + self._setCamTarget(wc, 25, angle=Point3(-30, 60, 0)) + camera.wrtReparentTo(self._camHelperNode) + self._state = 3 + + def end(): + self._stopUpdateTask() + + self._ival = Sequence(Func(start), Func(self.displayLine, 'toon', self._getRandomLine(dialogue[0])), showExitIval, Func(showWaterCooler), Func(self.displayLine, 'toon', self._getRandomLine(dialogue[1])), Wait(waitDuration), Func(showBoss), bossSuit.hprInterval(1.0, bossSuit.getHpr() + Point3(180, 0, 0), blendType='easeInOut'), Func(self.displayLine, 'toon', self._getRandomLine(dialogue[2])), Wait(waitDuration - 1.0), Func(end)) + self._startUpdateTask() + + def _setCamTarget(self, targetNP, distance, offset = Point3(0, 0, 0), angle = Point3(0, 0, 0)): + camera.wrtReparentTo(render) + self._camTarget = targetNP + self._camOffset = offset + self._camAngle = angle + self._camDistance = distance + self._camHelperNode.setPos(self._camTarget, self._camOffset) + self._camHelperNode.setHpr(self._camTarget, 180 + self._camAngle[0], self._camAngle[1], self._camAngle[2]) + camera.setPos(self._camHelperNode, 0, self._camDistance, 0) + + def _updateTask(self, task): + dt = globalClock.getDt() + if self._state == 1: + self._camHelperNode.setPos(self._camTarget.getPos() + self._camOffset) + camera.setPos(self._camHelperNode, 0, self._camDistance, 0) + camera.lookAt(self._camTarget, 0, 0, 4) + elif self._state == 2: + camera.lookAt(self._camTarget, 0, 0, 5) + elif self._state == 3: + self._camHelperNode.setHpr(self._camHelperNode, dt, dt, 0) + camera.setY(camera, 0.8 * dt) + camera.lookAt(self._camTarget, 0, 0, 3) + return task.cont + + def unload(self): + self._exit = None + self._camTarget = None + self._camHelperNode.removeNode() + del self._camHelperNode + for suit in self._suits: + suit.cleanup() + suit.removeNode() + suit.delete() + + self._suits = [] + CogdoGameMovie.unload(self) + del self._cogDialogueSfx + del self._toonDialogueSfx + self.toonHead.stopBlink() + self.toonHead.stop() + self.toonHead.removeNode() + self.toonHead.delete() + del self.toonHead + self.cogHead.stop() + self.cogHead.removeNode() + self.cogHead.delete() + del self.cogHead + return + + +class CogdoMazeGameFinish(CogdoGameMovie): + + def __init__(self, localPlayer, exit): + CogdoGameMovie.__init__(self) + self._localPlayer = localPlayer + self._exit = exit + + def load(self): + CogdoGameMovie.load(self) + self._ival = Sequence() + if not self._exit.hasPlayer(self._localPlayer): + loseSfx = base.cogdoGameAudioMgr.createSfx('lose') + self._ival.append(Sequence(Func(loseSfx.play), Func(self._localPlayer.toon.setAnimState, 'Sad'))) + self._ival.append(Sequence(Wait(Globals.FinishDurationSeconds - 1.0), Func(base.transitions.irisOut), Wait(1.0))) + + def unload(self): + CogdoGameMovie.unload(self) diff --git a/toontown/cogdominium/CogdoMazeGameObjects.py b/toontown/cogdominium/CogdoMazeGameObjects.py new file mode 100755 index 00000000..4503284a --- /dev/null +++ b/toontown/cogdominium/CogdoMazeGameObjects.py @@ -0,0 +1,266 @@ +from pandac.PandaModules import CollisionSphere, CollisionTube, CollisionNode +from pandac.PandaModules import NodePath, BitMask32 +from pandac.PandaModules import Point3, Point4, WaitInterval, Vec3, Vec4 +from direct.interval.IntervalGlobal import LerpScaleInterval, LerpColorScaleInterval, LerpPosInterval, LerpFunc +from direct.interval.IntervalGlobal import Func, Sequence, Parallel +from direct.showbase.DirectObject import DirectObject +from direct.task.Task import Task +from toontown.toonbase import ToontownGlobals +import CogdoMazeGameGlobals as Globals +from CogdoGameExit import CogdoGameExit +import CogdoUtil +import math +import random + +class CogdoMazeSplattable: + + def __init__(self, object, name, collisionRadius): + self.object = object + self.splat = CogdoUtil.loadMazeModel('splash') + self.splat.setBillboardPointEye() + self.splat.setBin('fixed', 40) + self.splat.setDepthTest(False) + self.splat.setDepthWrite(False) + self.splatTrack = None + self._splatSfxIval = base.cogdoGameAudioMgr.createSfxIval('splat') + self.initGagCollision(name, collisionRadius) + return + + def destroy(self): + self.disableGagCollision() + if self._splatSfxIval.isPlaying(): + self._splatSfxIval.finish() + del self._splatSfxIval + + def initGagCollision(self, name, radius): + self.gagCollisionName = name + collision = CollisionTube(0, 0, 0, 0, 0, 4, radius) + collision.setTangible(1) + self.gagCollNode = CollisionNode(self.gagCollisionName) + self.gagCollNode.setIntoCollideMask(ToontownGlobals.PieBitmask) + self.gagCollNode.addSolid(collision) + self.gagCollNodePath = self.object.attachNewNode(self.gagCollNode) + + def disableGagCollision(self): + self.gagCollNodePath.removeNode() + + def doSplat(self): + if self.splatTrack and self.splatTrack.isPlaying(): + self.splatTrack.finish() + self.splat.reparentTo(render) + self.splat.setPos(self.object, 0, 0, 3.0) + self.splat.setY(self.splat.getY() - 1.0) + self._splatSfxIval.node = self.splat + self.splatTrack = Parallel(self._splatSfxIval, Sequence(Func(self.splat.showThrough), LerpScaleInterval(self.splat, duration=0.5, scale=6, startScale=1, blendType='easeOut'), Func(self.splat.hide))) + self.splatTrack.start() + + +class CogdoMazeDrop(NodePath, DirectObject): + + def __init__(self, game, id, x, y): + NodePath.__init__(self, 'dropNode%s' % id) + self.game = game + self.id = id + self.reparentTo(hidden) + self.setPos(x, y, 0) + shadow = loader.loadModel('phase_3/models/props/square_drop_shadow') + shadow.setZ(0.2) + shadow.setBin('ground', 10) + shadow.setColor(1, 1, 1, 1) + shadow.reparentTo(self) + self.shadow = shadow + drop = CogdoUtil.loadMazeModel('cabinetSmFalling') + roll = random.randint(-15, 15) + drop.setHpr(0, 0, roll) + drop.setZ(Globals.DropHeight) + self.collTube = CollisionTube(0, 0, 0, 0, 0, 4, Globals.DropCollisionRadius) + self.collTube.setTangible(0) + name = Globals.DropCollisionName + self.collNode = CollisionNode(name) + self.collNode.addSolid(self.collTube) + self.collNodePath = drop.attachNewNode(self.collNode) + self.collNodePath.hide() + self.collNodePath.setTag('isFalling', str('True')) + drop.reparentTo(self) + self.drop = drop + self._dropSfx = base.cogdoGameAudioMgr.createSfxIval('drop', volume=0.6) + + def disableCollisionDamage(self): + self.collTube.setTangible(1) + self.collTube.setRadius(Globals.DroppedCollisionRadius) + self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.collNodePath.setTag('isFalling', str('False')) + + def getDropIval(self): + shadow = self.shadow + drop = self.drop + id = self.id + hangTime = Globals.ShadowTime + dropTime = Globals.DropTime + dropHeight = Globals.DropHeight + targetShadowScale = 0.5 + targetShadowAlpha = 0.4 + shadowScaleIval = LerpScaleInterval(shadow, dropTime, targetShadowScale, startScale=0) + shadowAlphaIval = LerpColorScaleInterval(shadow, hangTime, Point4(1, 1, 1, targetShadowAlpha), startColorScale=Point4(1, 1, 1, 0)) + shadowIval = Parallel(shadowScaleIval, shadowAlphaIval) + startPos = Point3(0, 0, dropHeight) + drop.setPos(startPos) + dropIval = LerpPosInterval(drop, dropTime, Point3(0, 0, 0), startPos=startPos, blendType='easeIn') + dropSoundIval = self._dropSfx + dropSoundIval.node = self + self.drop.setTransparency(1) + + def _setRandScale(t): + self.drop.setScale(self, 1 - random.random() / 16, 1 - random.random() / 16, 1 - random.random() / 4) + + scaleChange = 0.4 + random.random() / 4 + dropShakeSeq = Sequence( + LerpScaleInterval(self.drop, 0.25, Vec3(1.0 + scaleChange, 1.0 + scaleChange / 2, 1.0 - scaleChange), blendType='easeInOut'), + LerpScaleInterval(self.drop, 0.25, Vec3(1.0, 1.0, 1.0), blendType='easeInOut'), Func(self.disableCollisionDamage), + LerpScaleInterval(self.drop, 0.2, Vec3(1.0 + scaleChange / 8, 1.0 + scaleChange / 8, 1.0 - scaleChange / 8), blendType='easeInOut'), + LerpScaleInterval(self.drop, 0.2, Vec3(1.0, 1.0, 1.0), blendType='easeInOut'), + LerpScaleInterval(self.drop, 0.15, Vec3(1.0 + scaleChange / 16, 1.0 + scaleChange / 16, 1.0 - scaleChange / 16), blendType='easeInOut'), + LerpScaleInterval(self.drop, 0.15, Vec3(1.0, 1.0, 1.0), blendType='easeInOut'), + LerpScaleInterval(self.drop, 0.1, Vec3(1.0 + scaleChange / 16, 1.0 + scaleChange / 8, 1.0 - scaleChange / 16), blendType='easeInOut'), + LerpColorScaleInterval(self.drop, Globals.DropFadeTime, Vec4(1.0, 1.0, 1.0, 0.0))) + ival = Sequence( + Func(self.reparentTo, render), + Parallel(Sequence(WaitInterval(hangTime), dropIval), shadowIval), + Parallel(Func(self.game.dropHit, self, id), dropSoundIval, dropShakeSeq), + Func(self.game.cleanupDrop, id), name='drop%s' % id) + self.ival = ival + return ival + + def destroy(self): + self.ival.pause() + self.ival = None + self._dropSfx.pause() + self._dropSfx = None + self.collTube = None + self.collNode = None + self.collNodePath.removeNode() + self.collNodePath = None + self.removeNode() + return + + +class CogdoMazeExit(CogdoGameExit, DirectObject): + EnterEventName = 'CogdoMazeDoor_Enter' + + def __init__(self): + CogdoGameExit.__init__(self) + self.revealed = False + self._players = [] + self._initCollisions() + + def _initCollisions(self): + collSphere = CollisionSphere(0, 0, 0, 3.0) + collSphere.setTangible(0) + self.collNode = CollisionNode(self.getName()) + self.collNode.addSolid(collSphere) + self.collNP = self.attachNewNode(self.collNode) + + def destroy(self): + self.ignoreAll() + CogdoGameExit.destroy(self) + + def enable(self): + self.collNode.setFromCollideMask(ToontownGlobals.WallBitmask) + self.accept('enter' + self.getName(), self._handleEnterCollision) + + def disable(self): + self.ignore('enter' + self.getName()) + self.collNode.setFromCollideMask(BitMask32(0)) + + def _handleEnterCollision(self, collEntry): + messenger.send(CogdoMazeExit.EnterEventName, [self]) + + def onstage(self): + self.unstash() + self.enable() + + def offstage(self): + self.stash() + self.disable() + + def playerEntersDoor(self, player): + if player not in self._players: + self._players.append(player) + self.toonEnters(player.toon) + + def getPlayerCount(self): + return len(self._players) + + def hasPlayer(self, player): + return player in self._players + + +class CogdoMazeWaterCooler(NodePath, DirectObject): + UpdateTaskName = 'CogdoMazeWaterCooler_Update' + + def __init__(self, serialNum, model): + NodePath.__init__(self, 'CogdoMazeWaterCooler-%i' % serialNum) + self.serialNum = serialNum + self._model = model + self._model.reparentTo(self) + self._model.setPosHpr(0, 0, 0, 0, 0, 0) + self._initCollisions() + self._initArrow() + self._update = None + self.__startUpdateTask() + return + + def destroy(self): + self.ignoreAll() + self.__stopUpdateTask() + self.collNodePath.removeNode() + self.removeNode() + + def _initCollisions(self): + offset = Globals.WaterCoolerTriggerOffset + self.collSphere = CollisionSphere(offset[0], offset[1], offset[2], Globals.WaterCoolerTriggerRadius) + self.collSphere.setTangible(0) + name = Globals.WaterCoolerCollisionName + self.collNode = CollisionNode(name) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.attachNewNode(self.collNode) + + def _initArrow(self): + matchingGameGui = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + arrow = matchingGameGui.find('**/minnieArrow') + arrow.setScale(Globals.CoolerArrowScale) + arrow.setColor(*Globals.CoolerArrowColor) + arrow.setPos(0, 0, Globals.CoolerArrowZ) + arrow.setHpr(0, 0, 90) + arrow.setBillboardAxis() + self._arrow = NodePath('Arrow') + arrow.reparentTo(self._arrow) + self._arrow.reparentTo(self) + self._arrowTime = 0 + self.accept(Globals.WaterCoolerShowEventName, self.showArrow) + self.accept(Globals.WaterCoolerHideEventName, self.hideArrow) + matchingGameGui.removeNode() + + def showArrow(self): + self._arrow.unstash() + + def hideArrow(self): + self._arrow.stash() + + def update(self, dt): + newZ = math.sin(globalClock.getFrameTime() * Globals.CoolerArrowSpeed) * Globals.CoolerArrowBounce + self._arrow.setZ(newZ) + + def __startUpdateTask(self): + self.__stopUpdateTask() + self._update = taskMgr.add(self._updateTask, self.UpdateTaskName, 45) + + def __stopUpdateTask(self): + if self._update is not None: + taskMgr.remove(self._update) + return + + def _updateTask(self, task): + dt = globalClock.getDt() + self.update(dt) + return Task.cont diff --git a/toontown/cogdominium/CogdoMazeGuiManager.py b/toontown/cogdominium/CogdoMazeGuiManager.py new file mode 100755 index 00000000..a3a7ac08 --- /dev/null +++ b/toontown/cogdominium/CogdoMazeGuiManager.py @@ -0,0 +1,137 @@ +from pandac.PandaModules import NodePath +from direct.interval.MetaInterval import Sequence +from direct.interval.FunctionInterval import Func +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.toonbase import ToontownIntervals +from CogdoMazeGameGuis import CogdoMazeHud, CogdoMazeMapGui, CogdoMazeBossGui +from CogdoGameMessageDisplay import CogdoGameMessageDisplay +import CogdoMazeGameGlobals as Globals +from CogdoMemoGui import CogdoMemoGui + +class CogdoMazeGuiManager: + + def __init__(self, maze, bossCode): + self.maze = maze + self.root = NodePath('CogdoMazeGui') + self.root.reparentTo(aspect2d) + self.mazeMapGui = CogdoMazeMapGui(self.maze.collisionTable) + if bossCode is not None: + self._bossGui = CogdoMazeBossGui(bossCode) + else: + self._bossGui = None + self._memoGui = CogdoMemoGui(self.root) + self._memoGui.posNextToLaffMeter() + self._presentGuiIval = None + self._presentTimerIval = None + self._hud = CogdoMazeHud() + self._timer = None + self._initMessageDisplay() + return + + def _initTimer(self): + self._timer = ToontownTimer() + self._timer.hide() + self._timer.posInTopRightCorner() + + def _initMessageDisplay(self): + self.messageDisplay = CogdoGameMessageDisplay('CogdoMazeMessageDisplay', self.root, pos=Globals.MessageLabelPos) + + def destroy(self): + ToontownIntervals.cleanup('present_gui') + ToontownIntervals.cleanup('present_timer') + ToontownIntervals.cleanup('present_memo') + self._hud.destroy() + self._hud = None + self._memoGui.destroy() + self._memoGui = None + if self._bossGui is not None: + self._bossGui.destroy() + self._bossGui = None + self.messageDisplay.destroy() + self.messageDisplay = None + self.destroyMazeMap() + self.destroyTimer() + if self._presentGuiIval: + self._presentGuiIval.pause() + self._presentGuiIval = None + if self._presentTimerIval: + self._presentTimerIval.pause() + self._presentTimerIval = None + return + + def destroyMazeMap(self): + if hasattr(self, 'mazeMapGui') and self.mazeMapGui is not None: + self.mazeMapGui.destroy() + del self.mazeMapGui + return + + def destroyTimer(self): + if self._timer is not None: + self._timer.stop() + self._timer.destroy() + self._timer = None + return + + def showPickupCounter(self): + ToontownIntervals.start(ToontownIntervals.getPresentGuiIval(self._memoGui, 'present_memo')) + + def startGame(self, firstMessage): + self._presentGuiIval = ToontownIntervals.start(Sequence(ToontownIntervals.getPresentGuiIval(self._bossGui, '', startPos=(0, 0, -0.15)), Func(self.mazeMapGui.show), ToontownIntervals.getPulseLargerIval(self.mazeMapGui, '', scale=self.mazeMapGui.getScale()), Func(self.setMessage, firstMessage), name='present_gui')) + + def hideMazeMap(self): + self.mazeMapGui.hide() + + def showBossGui(self): + if self._bossGui is not None: + self._bossGui.show() + return + + def hideBossGui(self): + if self._bossGui is not None: + self._bossGui.hide() + return + + def revealMazeMap(self): + self.mazeMapGui.revealAll() + + def hideLock(self, lockIndex): + self.mazeMapGui.hideLock(lockIndex) + + def showTimer(self, duration, timerExpiredCallback = None): + if self._timer is None: + self._initTimer() + self._timer.setTime(duration) + self._timer.countdown(duration, timerExpiredCallback) + self._timer.show() + self._presentTimerIval = ToontownIntervals.start(ToontownIntervals.getPresentGuiIval(self._timer, 'present_timer', startPos=(0, 0, 0.35))) + return + + def hideTimer(self): + if hasattr(self, 'timer') and self._timer is not None: + self._timer.hide() + self._timer.stop() + return + + def setMessage(self, text, color = None, transition = 'fade'): + self.messageDisplay.updateMessage(text, color, transition) + + def setMessageTemporary(self, text, time = 3.0): + self.messageDisplay.showMessageTemporarily(text, time) + + def clearMessage(self): + self.messageDisplay.updateMessage('') + + def setPickupCount(self, count): + self._memoGui.setCount(count) + + def showBossCode(self, bossIndex): + self._bossGui.showNumber(bossIndex) + + def showBossHit(self, bossIndex): + self._bossGui.showHit(bossIndex) + + def showQuestArrow(self, toon, target, offset): + self._hud.showQuestArrow(toon, target, offset) + + def hideQuestArrow(self): + self._hud.hideQuestArrow() diff --git a/toontown/cogdominium/CogdoMazeLocalPlayer.py b/toontown/cogdominium/CogdoMazeLocalPlayer.py new file mode 100755 index 00000000..8d79ce6b --- /dev/null +++ b/toontown/cogdominium/CogdoMazeLocalPlayer.py @@ -0,0 +1,269 @@ +from pandac.PandaModules import Point3, CollisionNode, CollisionSphere, CollisionHandlerEvent +from direct.interval.IntervalGlobal import Func, Sequence, Wait +from direct.showbase.PythonUtil import bound as clamp +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer +from toontown.minigame.OrthoDrive import OrthoDrive +from toontown.minigame.OrthoWalk import OrthoWalk +from toontown.toonbase import ToontownGlobals +import CogdoGameConsts +import CogdoMazeGameGlobals as Globals +from CogdoMazePlayer import CogdoMazePlayer +from CogdoMazeCameraManager import CogdoMazeCameraManager + +class CogdoMazeLocalPlayer(CogdoMazePlayer): + notify = directNotify.newCategory('CogdoMazeLocalPlayer') + + def __init__(self, id, toon, game, guiMgr): + CogdoMazePlayer.__init__(self, id, toon) + self.disableGagCollision() + self.game = game + self.maze = self.game.maze + self._guiMgr = guiMgr + self.cameraMgr = CogdoMazeCameraManager(self.toon, self.maze, camera, render) + self._proximityRadius = self.maze.cellWidth * Globals.CameraRemoteToonRadius + orthoDrive = OrthoDrive(Globals.ToonRunSpeed, maxFrameMove=self.maze.cellWidth / 2, customCollisionCallback=self.maze.doOrthoCollisions, wantSound=True) + self.orthoWalk = OrthoWalk(orthoDrive) + self._audioMgr = base.cogdoGameAudioMgr + self._getMemoSfx = self._audioMgr.createSfx('getMemo', source=self.toon) + self._waterCoolerFillSfx = self._audioMgr.createSfx('waterCoolerFill', source=self.toon) + self._hitByDropSfx = self._audioMgr.createSfx('toonHitByDrop', source=self.toon) + self._winSfx = self._audioMgr.createSfx('win') + self._loseSfx = self._audioMgr.createSfx('lose') + self.enabled = False + self.pickupCount = 0 + self.numEntered = 0 + self.throwPending = False + self.coolDownAfterHitInterval = Sequence(Wait(Globals.HitCooldownTime), Func(self.setInvulnerable, False), name='coolDownAfterHitInterval-%i' % self.toon.doId) + self.invulnerable = False + self.gagHandler = CollisionHandlerEvent() + self.gagHandler.addInPattern('%fn-into-%in') + self.exited = False + self.hints = {'find': False, + 'throw': False, + 'squashed': False, + 'boss': False, + 'minion': False} + self.accept('control', self.controlKeyPressed) + + def destroy(self): + self.toon.showName() + self.ignoreAll() + self.coolDownAfterHitInterval.clearToInitial() + del self.coolDownAfterHitInterval + del self._getMemoSfx + del self._waterCoolerFillSfx + del self._hitByDropSfx + del self._winSfx + self.orthoWalk.stop() + self.orthoWalk.destroy() + del self.orthoWalk + CogdoMazePlayer.destroy(self) + + def __initCollisions(self): + collSphere = CollisionSphere(0, 0, 0, Globals.PlayerCollisionRadius) + collSphere.setTangible(0) + self.mazeCollisionName = Globals.LocalPlayerCollisionName + collNode = CollisionNode(self.mazeCollisionName) + collNode.addSolid(collSphere) + collNodePath = self.toon.attachNewNode(collNode) + collNodePath.hide() + handler = CollisionHandlerEvent() + handler.addInPattern('%fn-into-%in') + base.cTrav.addCollider(collNodePath, handler) + self.handler = handler + self._collNodePath = collNodePath + + def clearCollisions(self): + self.handler.clear() + + def __disableCollisions(self): + self._collNodePath.removeNode() + del self._collNodePath + + def _isNearPlayer(self, player): + return self.toon.getDistance(player.toon) <= self._proximityRadius + + def update(self, dt): + if self.getCurrentOrNextState() != 'Off': + self._updateCamera(dt) + + def _updateCamera(self, dt): + numPlayers = 0.0 + for player in self.game.players: + if player != self and player.toon and self._isNearPlayer(player): + numPlayers += 1 + + d = clamp(Globals.CameraMinDistance + numPlayers / (CogdoGameConsts.MaxPlayers - 1) * (Globals.CameraMaxDistance - Globals.CameraMinDistance), Globals.CameraMinDistance, Globals.CameraMaxDistance) + self.cameraMgr.setCameraTargetDistance(d) + self.cameraMgr.update(dt) + + def enterOff(self): + CogdoMazePlayer.enterOff(self) + + def exitOff(self): + CogdoMazePlayer.exitOff(self) + self.toon.hideName() + + def enterReady(self): + CogdoMazePlayer.enterReady(self) + self.cameraMgr.enable() + + def exitReady(self): + CogdoMazePlayer.enterReady(self) + + def enterNormal(self): + CogdoMazePlayer.enterNormal(self) + self.orthoWalk.start() + + def exitNormal(self): + CogdoMazePlayer.exitNormal(self) + self.orthoWalk.stop() + + def enterHit(self, elapsedTime = 0.0): + CogdoMazePlayer.enterHit(self, elapsedTime) + self.setInvulnerable(True) + + def exitHit(self): + CogdoMazePlayer.exitHit(self) + self.coolDownAfterHitInterval.clearToInitial() + self.coolDownAfterHitInterval.start() + + def enterDone(self): + CogdoMazePlayer.enterDone(self) + self._guiMgr.hideQuestArrow() + self.ignore('control') + self._guiMgr.setMessage('') + if self.exited == False: + self.lostMemos() + + def exitDone(self): + CogdoMazePlayer.exitDone(self) + + def hitByDrop(self): + if self.equippedGag is not None and not self.hints['squashed']: + self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeSquashHint, Globals.HintTimeout) + self.hints['squashed'] = True + self._hitByDropSfx.play() + CogdoMazePlayer.hitByDrop(self) + return + + def equipGag(self): + CogdoMazePlayer.equipGag(self) + self._waterCoolerFillSfx.play() + messenger.send(Globals.WaterCoolerHideEventName, []) + if not self.hints['throw']: + self._guiMgr.setMessage(TTLocalizer.CogdoMazeThrowHint) + self.hints['throw'] = True + + def hitSuit(self, suitType): + if suitType == Globals.SuitTypes.Boss and not self.hints['boss']: + self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeBossHint, Globals.HintTimeout) + self.hints['boss'] = True + if suitType != Globals.SuitTypes.Boss and not self.hints['minion']: + self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeMinionHint, Globals.HintTimeout) + self.hints['minion'] = True + + def createThrowGag(self, gag): + throwGag = CogdoMazePlayer.createThrowGag(self, gag) + collSphere = CollisionSphere(0, 0, 0, 0.5) + collSphere.setTangible(0) + name = Globals.GagCollisionName + collNode = CollisionNode(name) + collNode.setFromCollideMask(ToontownGlobals.PieBitmask) + collNode.addSolid(collSphere) + colNp = throwGag.attachNewNode(collNode) + base.cTrav.addCollider(colNp, self.gagHandler) + return throwGag + + def showToonThrowingGag(self, heading, pos): + self._guiMgr.clearMessage() + return CogdoMazePlayer.showToonThrowingGag(self, heading, pos) + + def removeGag(self): + if self.equippedGag is None: + return + CogdoMazePlayer.removeGag(self) + self.throwPending = False + messenger.send(Globals.WaterCoolerShowEventName, []) + return + + def controlKeyPressed(self): + if self.game.finished or self.throwPending or self.getCurrentOrNextState() == 'Hit' or self.equippedGag == None: + return + self.throwPending = True + heading = self.toon.getH() + pos = self.toon.getPos() + self.game.requestUseGag(pos.getX(), pos.getY(), heading) + return + + def completeThrow(self): + self.clearCollisions() + CogdoMazePlayer.completeThrow(self) + + def shakeCamera(self, strength): + self.cameraMgr.shake(strength) + + def getCameraShake(self): + return self.cameraMgr.shakeStrength + + def setInvulnerable(self, bool): + self.invulnerable = bool + + def handleGameStart(self): + self.numEntered = len(self.game.players) + self.__initCollisions() + self._guiMgr.startGame(TTLocalizer.CogdoMazeFindHint) + self.hints['find'] = True + self.notify.info('toonId:%d laff:%d/%d %d player(s) started maze game' % (self.toon.doId, + self.toon.hp, + self.toon.maxHp, + len(self.game.players))) + + def handleGameExit(self): + self.cameraMgr.disable() + self.__disableCollisions() + + def handlePickUp(self, toonId): + if toonId == self.toon.doId: + self.pickupCount += 1 + self._guiMgr.setPickupCount(self.pickupCount) + if self.pickupCount == 1: + self._guiMgr.showPickupCounter() + self._getMemoSfx.play() + + def handleOpenDoor(self, door): + self._guiMgr.setMessage(TTLocalizer.CogdoMazeGameDoorOpens) + self._guiMgr.showQuestArrow(self.toon, door, Point3(0, 0, self.toon.getHeight() + 2)) + + def handleTimeAlert(self): + self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeGameTimeAlert) + + def handleToonRevealsDoor(self, toonId, door): + if toonId == self.toon.doId: + self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeGameLocalToonFoundExit) + + def handleToonEntersDoor(self, toonId, door): + self.exited = True + message = '' + if door.getPlayerCount() < len(self.game.players): + message = TTLocalizer.WaitingForOtherToons + if toonId == self.toon.doId: + self._guiMgr.setMessage(message) + self._winSfx.play() + self._audioMgr.stopMusic() + self.notify.info('toonId:%d laff:%d/%d %d player(s) succeeded in maze game. Going to the executive suit building.' % (toonId, + self.toon.hp, + self.toon.maxHp, + len(self.game.players))) + if self.numEntered > len(self.game.players): + self.notify.info('%d player(s) failed in maze game' % (self.numEntered - len(self.game.players))) + + def lostMemos(self): + self.pickupCount = 0 + self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeGameTimeOut) + self._guiMgr.setPickupCount(self.pickupCount) + self.notify.info('toonId:%d laff:%d/%d %d player(s) failed in maze game' % (self.toon.doId, + self.toon.hp, + self.toon.maxHp, + len(self.game.players))) diff --git a/toontown/cogdominium/CogdoMazePlayer.py b/toontown/cogdominium/CogdoMazePlayer.py new file mode 100755 index 00000000..c94dc516 --- /dev/null +++ b/toontown/cogdominium/CogdoMazePlayer.py @@ -0,0 +1,190 @@ +from pandac.PandaModules import Point3, NodePath +from direct.fsm.FSM import FSM +from direct.interval.IntervalGlobal import ProjectileInterval, Track, ActorInterval +from direct.interval.IntervalGlobal import Func, Sequence, Parallel +from CogdoMazeGameObjects import CogdoMazeSplattable +import CogdoMazeGameGlobals as Globals +import CogdoUtil +import random + +class CogdoMazePlayer(FSM, CogdoMazeSplattable): + notify = directNotify.newCategory('CogdoMazePlayer') + _key = None + GagHitEventName = 'CogdoMazePlayer_GagHit' + RemovedEventName = 'CogdoMazePlayer_Removed' + + def __init__(self, id, toon): + FSM.__init__(self, 'CogdoMazePlayer') + CogdoMazeSplattable.__init__(self, toon, '%s-%i' % (Globals.PlayerCollisionName, id), 0.5) + self.id = id + self.toon = toon + self.defaultTransitions = {'Off': ['Ready'], + 'Ready': ['Normal', 'Off'], + 'Normal': ['Hit', 'Done', 'Off'], + 'Hit': ['Normal', 'Done', 'Off'], + 'Done': ['Off']} + self.toon.reparentTo(render) + self.gagModel = CogdoUtil.loadMazeModel('waterBalloon') + self.equippedGag = None + self._toonHitSfx = base.cogdoGameAudioMgr.createSfx('toonHit', self.toon) + self._throwSfx = base.cogdoGameAudioMgr.createSfxIval('throw') + self.accept(toon.getDisableEvent(), self.removed) + self.request('Off') + return + + def destroy(self): + if self.equippedGag: + self.removeGag() + del self._toonHitSfx + del self._throwSfx + CogdoMazeSplattable.destroy(self) + + def enterOff(self): + self.toon.setAnimState('Happy', 1.0) + self.toon.setSpeed(0, 0) + + def exitOff(self): + pass + + def enterReady(self): + pass + + def exitReady(self): + pass + + def enterNormal(self): + self.toon.startSmooth() + if self.equippedGag is not None: + self.equippedGag.unstash() + return + + def exitNormal(self): + self.toon.stopSmooth() + if self.equippedGag is not None: + self.equippedGag.stash() + return + + def enterHit(self, elapsedTime = 0.0): + animationInfo = Globals.ToonAnimationInfo['hit'] + self._hitIval = self._getToonAnimationIval(animationInfo[0], duration=animationInfo[1], startFrame=animationInfo[2], nextState='Normal') + self._hitIval.start(elapsedTime) + self._toonHitSfx.play() + + def exitHit(self): + self._hitIval.pause() + del self._hitIval + if self.equippedGag is not None: + self.toon.setAnimState('Catching', 1.0) + else: + self.toon.setAnimState('Happy', 1.0) + return + + def enterDone(self): + self.toon.setAnimState('off', 1.0) + + def filterDone(self, request, args): + if request == 'Done': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitDone(self): + pass + + def handleGameStart(self): + self.accept(Globals.GagCollisionName + '-into-' + self.gagCollisionName, self.handleGagHit) + + def hitByDrop(self): + if self.state == 'Normal': + self.request('Hit') + if self.equippedGag != None: + self.removeGag() + return + + def handleGagHit(self, collEntry): + gagNodePath = collEntry.getFromNodePath().getParent() + messenger.send(self.GagHitEventName, [self.toon.doId, gagNodePath]) + + def hitByGag(self): + self.doSplat() + + def equipGag(self): + if self.equippedGag != None: + return + self.toon.setAnimState('Catching') + holdingGag = self.gagModel.copyTo(self.toon.leftHand) + holdingGag.setScale(Globals.GagPickupScale) + color = random.choice(Globals.GagColors) + holdingGag.setColorScale(color) + holdingGag.setZ(-0.2) + holdingGag.setX(self.toon, 0) + holdingGag.setHpr(0, 0, 0) + self.equippedGag = holdingGag + return + + def removeGag(self): + if self.equippedGag is None: + return + self.toon.setAnimState('Happy') + self.equippedGag.detachNode() + self.equippedGag = None + return + + def createThrowGag(self, gag): + throwGag = gag.copyTo(NodePath('gag')) + return throwGag + + def removed(self): + messenger.send(self.RemovedEventName, [self]) + + def showToonThrowingGag(self, heading, pos): + gag = self.equippedGag + if gag is None: + return + self.removeGag() + tossTrack, flyTrack, object = self.getThrowInterval(gag, pos[0], pos[1], pos[2], heading, 0, 0) + + def matchRunningAnim(toon = self.toon): + toon.playingAnim = None + toon.setSpeed(toon.forwardSpeed, toon.rotateSpeed) + return + + newTossTrack = Sequence(tossTrack, Func(matchRunningAnim)) + throwTrack = Parallel(newTossTrack, flyTrack) + throwTrack.start(0) + return object + + def completeThrow(self): + self.toon.loop('neutral') + + def getThrowInterval(self, gag, x, y, z, h, p, r): + toon = self.toon + flyGag = self.createThrowGag(gag) + throwSoundIval = self._throwSfx + if throwSoundIval.isPlaying(): + throwSoundIval.finish() + throwSoundIval.node = toon + toonThrowIval1 = ActorInterval(toon, 'throw', startFrame=Globals.ThrowStartFrame, endFrame=Globals.ThrowEndFrame, playRate=Globals.ThrowPlayRate, partName='torso') + toss = Track((0, Sequence(Func(toon.setPosHpr, x, y, z, h, p, r), Func(gag.reparentTo, toon.rightHand), Func(gag.setPosHpr, 0, 0, 0, 0, 0, 0), toonThrowIval1)), (toonThrowIval1.getDuration(), Parallel(Func(gag.detachNode), Sequence(ActorInterval(toon, 'throw', startFrame=Globals.ThrowEndFrame + 1, playRate=Globals.ThrowPlayRate, partName='torso'), Func(self.completeThrow))))) + + def getEndPos(toon = toon): + return render.getRelativePoint(toon, Point3(0, Globals.ThrowDistance, 0)) + + fly = Track((0, throwSoundIval), (toonThrowIval1.getDuration(), Sequence(Func(flyGag.reparentTo, render), Func(flyGag.setPosHpr, toon, 0.52, 0.97, 2.24, 0, -45, 0), ProjectileInterval(flyGag, endPos=getEndPos, duration=Globals.ThrowDuration), Func(flyGag.detachNode)))) + return (toss, fly, flyGag) + + def _getToonAnimationIval(self, animName, startFrame = 0, duration = 1, nextState = None): + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.toon.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + ival = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate)) + if nextState is not None: + + def done(): + self.request(nextState) + + ival.append(Func(done)) + return ival diff --git a/toontown/cogdominium/CogdoMazeSuits.py b/toontown/cogdominium/CogdoMazeSuits.py new file mode 100755 index 00000000..69d6715c --- /dev/null +++ b/toontown/cogdominium/CogdoMazeSuits.py @@ -0,0 +1,264 @@ +from pandac.PandaModules import Point3, VBase4 +from direct.fsm.FSM import FSM +from direct.interval.IntervalGlobal import Sequence, Parallel, ActorInterval, Func, Wait, ParticleInterval, Track, LerpColorScaleInterval, LerpScaleInterval, LerpHprInterval +from direct.task.Task import Task +from toontown.battle import BattleParticles +from toontown.battle import MovieUtil +from toontown.minigame.MazeSuit import MazeSuit +from CogdoMazeGameObjects import CogdoMazeSplattable +import CogdoMazeGameGlobals as Globals +import random + +class CogdoMazeSuit(MazeSuit, FSM, CogdoMazeSplattable): + GagHitEventName = 'CogdoMazeSuit_GagHit' + DeathEventName = 'CogdoMazeSuit_Death' + ThinkEventName = 'CogdoMazeSuit_Think' + + def __init__(self, serialNum, maze, randomNumGen, difficulty, startTile, cogdoSuitType, walkAnimName = None): + data = Globals.SuitData[cogdoSuitType] + MazeSuit.__init__(self, serialNum, maze, randomNumGen, data['cellWalkPeriod'], difficulty, data['dnaName'], startTile=startTile, walkSameDirectionProb=Globals.SuitWalkSameDirectionProb, walkTurnAroundProb=Globals.SuitWalkTurnAroundProb, uniqueRandomNumGen=False, walkAnimName=walkAnimName) + FSM.__init__(self, 'CogdoMazeSuit') + CogdoMazeSplattable.__init__(self, self.suit, '%s-%i' % (Globals.SuitCollisionName, self.serialNum), 1.5) + if 'scale' in data: + self.suit.setScale(data['scale']) + self.hp = data['hp'] + self.type = cogdoSuitType + self.memos = data['memos'] + self.deathSuit = self.suit.getLoseActor() + self.deathSuit.pose('lose', 0) + BattleParticles.loadParticles() + self._initSfx() + + def _initSfx(self): + audioMgr = base.cogdoGameAudioMgr + self._deathSoundIval = Sequence(audioMgr.createSfxIval('cogSpin', duration=1.6, startTime=0.6, volume=0.8, source=self.deathSuit), audioMgr.createSfxIval('cogDeath', volume=0.32, source=self.deathSuit)) + + def _destroySfx(self): + if self._deathSoundIval.isPlaying(): + self._deathSoundIval.finish() + del self._deathSoundIval + + def destroy(self): + BattleParticles.unloadParticles() + self.ignoreAll() + self._destroySfx() + CogdoMazeSplattable.destroy(self) + MazeSuit.destroy(self) + + def handleEnterSphere(self, collEntry): + messenger.send(self.COLLISION_EVENT_NAME, [self.type, self.serialNum]) + + def gameStart(self, gameStartTime): + MazeSuit.gameStart(self, gameStartTime) + self.accept(Globals.GagCollisionName + '-into-' + self.gagCollisionName, self.handleGagHit) + messenger.send(self.ThinkEventName, [self, self.TX, self.TY]) + + def initCollisions(self): + MazeSuit.initCollisions(self) + self.collNodePath.setScale(0.75) + self.accept(self.uniqueName('again' + self.COLL_SPHERE_NAME), self.handleEnterSphere) + + def think(self, curTic, curT, unwalkables): + MazeSuit.think(self, curTic, curT, unwalkables) + messenger.send(self.ThinkEventName, [self, self.TX, self.TY]) + + def handleGagHit(self, collEntry): + gagNodePath = collEntry.getFromNodePath().getParent() + messenger.send(self.GagHitEventName, [self.type, self.serialNum, gagNodePath]) + + def _getSuitAnimationIval(self, animName, startFrame = 0, duration = 1, partName = None, nextState = None): + totalFrames = self.suit.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.suit.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + ival = Sequence(ActorInterval(self.suit, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate, partName=partName)) + if nextState is not None: + + def done(): + self.request(nextState) + + ival.append(Func(done)) + return ival + + def hitByGag(self): + self.hp = self.hp - 1 + self.doSplat() + if self.hp <= 0: + self.explode() + + def explode(self): + self.doDeathTrack() + messenger.send(self.DeathEventName, [self.type, self.serialNum]) + + def doDeathTrack(self): + + def removeDeathSuit(suit, deathSuit): + if not deathSuit.isEmpty(): + deathSuit.detachNode() + suit.cleanupLoseActor() + + self.deathSuit.reparentTo(self.suit.getParent()) + self.deathSuit.setScale(self.suit.getScale()) + self.deathSuit.setPos(render, self.suit.getPos(render)) + self.deathSuit.setHpr(render, self.suit.getHpr(render)) + self.suit.hide() + self.collNodePath.reparentTo(self.deathSuit) + gearPoint = Point3(0, 0, self.suit.height / 2.0 + 2.0) + smallGears = BattleParticles.createParticleEffect(file='gearExplosionSmall') + singleGear = BattleParticles.createParticleEffect('GearExplosion', numParticles=1) + smallGearExplosion = BattleParticles.createParticleEffect('GearExplosion', numParticles=10) + bigGearExplosion = BattleParticles.createParticleEffect('BigGearExplosion', numParticles=30) + smallGears.setPos(gearPoint) + singleGear.setPos(gearPoint) + smallGearExplosion.setPos(gearPoint) + bigGearExplosion.setPos(gearPoint) + smallGears.setDepthWrite(False) + singleGear.setDepthWrite(False) + smallGearExplosion.setDepthWrite(False) + bigGearExplosion.setDepthWrite(False) + suitTrack = Sequence(Func(self.collNodePath.stash), ActorInterval(self.deathSuit, 'lose', startFrame=80, endFrame=140), Func(removeDeathSuit, self.suit, self.deathSuit, name='remove-death-suit')) + explosionTrack = Sequence(Wait(1.5), MovieUtil.createKapowExplosionTrack(self.deathSuit, explosionPoint=gearPoint)) + gears1Track = Sequence(ParticleInterval(smallGears, self.deathSuit, worldRelative=0, duration=4.3, cleanup=True), name='gears1Track') + gears2MTrack = Track((0.0, explosionTrack), (0.7, ParticleInterval(singleGear, self.deathSuit, worldRelative=0, duration=5.7, cleanup=True)), (5.2, ParticleInterval(smallGearExplosion, self.deathSuit, worldRelative=0, duration=1.2, cleanup=True)), (5.4, ParticleInterval(bigGearExplosion, self.deathSuit, worldRelative=0, duration=1.0, cleanup=True)), name='gears2MTrack') + + def removeParticle(particle): + if particle and hasattr(particle, 'renderParent'): + particle.cleanup() + del particle + + removeParticles = Sequence(Func(removeParticle, smallGears), Func(removeParticle, singleGear), Func(removeParticle, smallGearExplosion), Func(removeParticle, bigGearExplosion)) + self.deathTrack = Sequence(Parallel(suitTrack, gears2MTrack, gears1Track, self._deathSoundIval), removeParticles) + self.deathTrack.start() + + +class CogdoMazeSlowMinionSuit(CogdoMazeSuit): + + def __init__(self, serialNum, maze, randomNumGen, difficulty, startTile = None): + CogdoMazeSuit.__init__(self, serialNum, maze, randomNumGen, difficulty, startTile, Globals.SuitTypes.SlowMinion) + self.defaultTransitions = {'Off': ['Normal'], + 'Normal': ['Attack', 'Off'], + 'Attack': ['Normal']} + + def gameStart(self, gameStartTime): + CogdoMazeSuit.gameStart(self, gameStartTime) + self.request('Normal') + + def enterNormal(self): + self.startWalkAnim() + + def exitNormal(self): + pass + + def enterAttack(self, elapsedTime): + self._attackIval = self._getSuitAnimationIval('finger-wag', duration=2.0, nextState='Normal') + self._attackIval.start(elapsedTime) + + def filterAttack(self, request, args): + if request == 'Attack': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitAttack(self): + self._attackIval.pause() + del self._attackIval + + +class CogdoMazeFastMinionSuit(CogdoMazeSuit): + + def __init__(self, serialNum, maze, randomNumGen, difficulty, startTile = None): + CogdoMazeSuit.__init__(self, serialNum, maze, randomNumGen, difficulty, startTile, Globals.SuitTypes.FastMinion) + + +class CogdoMazeBossSuit(CogdoMazeSuit): + BlinkTaskName = 'CogdoMazeBossBlinkTask' + ShakeTaskName = 'CogdoMazeBossShakeTask' + StartWalkTaskName = 'CogdoMazeBossStartWalkTask' + ShakeEventName = 'CogdoMazeSuitShake' + + def __init__(self, serialNum, maze, randomNumGen, difficulty, startTile = None): + CogdoMazeSuit.__init__(self, serialNum, maze, randomNumGen, difficulty, startTile, Globals.SuitTypes.Boss, walkAnimName='stomp') + self.dropTimer = 0 + self._walkSpeed = float(self.maze.cellWidth) / self.cellWalkDuration * 0.5 + + def _initSfx(self): + CogdoMazeSuit._initSfx(self) + audioMgr = base.cogdoGameAudioMgr + self._stompSfxIval = audioMgr.createSfxIval('cogStomp', source=self.suit, cutoff=Globals.BossStompSfxCutoff, volume=0.3) + self._hitSfx = audioMgr.createSfx('bossCogAngry', self.suit) + + def _destroySfx(self): + del self._hitSfx + if self._stompSfxIval.isPlaying(): + self._stompSfxIval.finish() + del self._stompSfxIval + CogdoMazeSuit._destroySfx(self) + + def spin(self): + part = self.suit + time = Globals.BossSpinTime + degrees = 360 * Globals.BossSpinCount + spinIval = LerpHprInterval(part, time, (self.suit.getH() + degrees, 0, 0), blendType='easeOut') + spinIval.start() + + def hitByGag(self): + if self.hp >= 2: + self._hitSfx.play() + self.spin() + self.suit.setColorScale(Globals.BlinkColor) + self.__startBlinkTask() + elif self.hp == 1: + self.__stopBlinkTask() + CogdoMazeSuit.hitByGag(self) + + def gameStart(self, gameStartTime): + CogdoMazeSuit.gameStart(self, gameStartTime) + + def startWalkAnim(self): + self.suit.loop(self._walkAnimName, fromFrame=43, toFrame=81) + self.suit.setPlayRate(self._walkSpeed * Globals.BossCogStompAnimationPlayrateFactor, self._walkAnimName) + self.__startShakeTask() + + def destroy(self): + CogdoMazeSuit.destroy(self) + self.__stopShakeTask() + self.__stopBlinkTask() + + def pickRandomValidSpot(self, r = 5): + validSpots = [] + for x in xrange(self.TX - r, self.TX + r): + for y in xrange(self.TY - r, self.TY + r): + if self.maze.isWalkable(x, y): + validSpots.append([x, y]) + + return self.rng.choice(validSpots) + + def __startShakeTask(self): + self.__stopShakeTask() + taskMgr.doMethodLater(Globals.BossShakeTime, self.__shake, self.uniqueName(CogdoMazeBossSuit.ShakeTaskName)) + self.bossShakeLastTime = 0 + + def __stopShakeTask(self): + taskMgr.remove(self.uniqueName(CogdoMazeBossSuit.ShakeTaskName)) + + def __shake(self, task): + if task.time - self.bossShakeLastTime > Globals.BossShakeTime: + self.suit.setPlayRate(self._walkSpeed * Globals.BossCogStompAnimationPlayrateFactor, self._walkAnimName) + self._stompSfxIval.start() + messenger.send(self.ShakeEventName, [self, Globals.BossShakeStrength]) + self.bossShakeLastTime = task.time + return task.cont + + def __startBlinkTask(self): + self.__stopBlinkTask() + taskMgr.doMethodLater(Globals.BlinkFrequency, self.__blink, CogdoMazeBossSuit.BlinkTaskName) + + def __stopBlinkTask(self): + taskMgr.remove(CogdoMazeBossSuit.BlinkTaskName) + + def __blink(self, task): + blink = Sequence(LerpColorScaleInterval(self.suit, Globals.BlinkSpeed, VBase4(1.0, 1.0, 1.0, 1.0)), LerpColorScaleInterval(self.suit, Globals.BlinkSpeed, Globals.BlinkColor)) + blink.start() + return Task.again diff --git a/toontown/cogdominium/CogdoMemoGui.py b/toontown/cogdominium/CogdoMemoGui.py new file mode 100755 index 00000000..52541266 --- /dev/null +++ b/toontown/cogdominium/CogdoMemoGui.py @@ -0,0 +1,41 @@ +from direct.gui.DirectGui import DGG, DirectFrame, DirectLabel +from pandac.PandaModules import TextNode +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownIntervals +from toontown.toonbase import TTLocalizer +import CogdoUtil +import CogdoGameConsts +MEMOICON_SCALE = 0.2 + +class CogdoMemoGui(DirectFrame): + + def __init__(self, parent, type='joke_card'): + DirectFrame.__init__(self, parent=parent, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX) + self._initModel(type) + self.hide() + return + + def destroy(self): + ToontownIntervals.cleanup('memocount_pulse') + self._countLabel.removeNode() + del self._countLabel + self._memoIcon.removeNode() + del self._memoIcon + DirectFrame.destroy(self) + + def posNextToLaffMeter(self): + self.reparentTo(base.a2dBottomLeft) + self.setPos(0.358, 0, 0.125) + + def _initModel(self, type='joke_card'): + self._countLabel = DirectLabel(parent=self, relief=None, pos=(0.0625, 0, -0.025), scale=CogdoGameConsts.MemoGuiTextScale, text=str(0), text_fg=CogdoGameConsts.MemoGuiTextColor, text_shadow=(0.2, 0.2, 0.2, 1), text_align=TextNode.ALeft, text_font=ToontownGlobals.getToonFont()) + self._memoIcon = CogdoUtil.loadModel(type, game='shared', group='gui') + self._memoIcon.reparentTo(self) + self._memoIcon.setScale(MEMOICON_SCALE) + return + + def setCount(self, count): + self._countLabel['text'] = str(count) + self._countLabel.setText() + ToontownIntervals.cleanup('memocount_pulse') + ToontownIntervals.start(ToontownIntervals.getPulseLargerIval(self._memoIcon, 'memocount_pulse', scale=MEMOICON_SCALE)) diff --git a/toontown/cogdominium/CogdoUtil.py b/toontown/cogdominium/CogdoUtil.py new file mode 100755 index 00000000..85027363 --- /dev/null +++ b/toontown/cogdominium/CogdoUtil.py @@ -0,0 +1,170 @@ +from pandac.PandaModules import ColorBlendAttrib +from toontown.suit import SuitDNA +import random + +ModelPhase = 5 +ModelTypes = {'animation': 'a', + 'model': 'm', + 'rig': 'r'} +ModelGroups = {'area': 'ara', + 'gui': 'gui'} +Games = {'flying': 'cfg', + 'maze': 'cmg', + 'shared': 'csa'} + +def getAllowedTracks(): + tracks = [] + + if config.GetBool('want-sellbot-cogdo', True): + tracks.append('s') + if config.GetBool('want-lawbot-cogdo', True): + tracks.append('l') + + return tracks + +def getCogdoTrack(suitName): + tracks = getAllowedTracks() + + if not tracks: + return None + + track = SuitDNA.getSuitDept(suitName) + + return track if track in tracks else random.choice(tracks) + +def loadFlyingModel(baseName, type = 'model', group = 'area'): + return loadModel(baseName, 'flying', type=type, group=group) + + +def loadMazeModel(baseName, type = 'model', group = 'area'): + return loadModel(baseName, 'maze', type=type, group=group) + + +def getModelPath(baseName, game = 'shared', type = 'model', group = 'area'): + extension = '' + if hasattr(getBase(), 'air'): + extension = '.bam' + return 'phase_%i/models/cogdominium/tt_%s_%s_%s_%s%s' % (ModelPhase, + ModelTypes[type], + ModelGroups[group], + Games[game], + baseName, + extension) + + +def loadModel(baseName, game = 'shared', type = 'model', group = 'area'): + return loader.loadModel(getModelPath(baseName, game, type, group)) + + +class VariableContainer: + pass + +class DevVariableContainer: + + def __init__(self, name): + self.__dict__['_enabled'] = config.GetBool('%s-dev' % name, False) + + def __setattr__(self, name, value): + self.__dict__[name] = self._enabled and value + + +def getRandomDialogueLine(lineList, rng): + return lineList[rng.randint(0, len(lineList) - 1)] + + +class CogdoGameMovie: + + def __init__(self): + self._ival = None + self._task = None + return + + def load(self): + from toontown.toonbase import ToontownGlobals + from pandac.PandaModules import TextNode + textNode = TextNode('moviedialogue') + textNode.setTextColor(0, 0, 0, 1) + textNode.setCardColor(1, 1, 1, 1) + textNode.setCardAsMargin(0, 0, 0, 0) + textNode.setCardDecal(True) + textNode.setWordwrap(27.0) + textNode.setAlign(TextNode.ACenter) + textNode.setFont(ToontownGlobals.getToonFont()) + self._dialogueLabel = aspect2d.attachNewNode(textNode) + self._dialogueLabel.setScale(0.06, 0.06, 0.06) + self._dialogueLabel.setPos(0.32, 0, -0.75) + self._dialogueLabel.reparentTo(hidden) + + def unload(self): + if self._ival is not None and self._ival.isPlaying(): + self.finish() + self._ival = None + self._dialogueLabel.removeNode() + del self._dialogueLabel + return + + def getIval(self): + return self._ival + + def play(self, elapsedTime = 0.0): + self._dialogueLabel.reparentTo(aspect2d) + self._ival.start(elapsedTime) + + def _startUpdateTask(self): + self._task = taskMgr.add(self._updateTask, 'CogdoGameMovie_updateTask', 45) + + def _stopUpdateTask(self): + if self._task is not None: + taskMgr.remove(self._task) + self._task = None + return + + def _updateTask(self, task): + return task.cont + + def end(self): + self._ival.finish() + + +def initializeLightCone(np, bin = 'fixed', sorting = 3): + np.node().setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + if bin: + np.setBin(bin, sorting) + np.setDepthWrite(False) + np.setTwoSided(True, 10000) + + +ROTATE_TABLE_ALLOWED_ANGLES = (0, 90, 180, 270) + +def rotateTable(table, angle): + if angle == 0: + t = table[:] + elif angle == 90: + t = [] + width = len(table[0]) + height = len(table) + for j in xrange(width): + row = [] + for i in xrange(height): + row.append(table[height - 1 - i][j]) + + t.append(row) + + elif angle == 180: + t = table[:] + for row in t: + row.reverse() + + t.reverse() + elif angle == 270: + t = [] + width = len(table[0]) + height = len(table) + for j in xrange(width): + row = [] + for i in xrange(height): + row.append(table[i][width - 1 - j]) + + t.append(row) + + return t diff --git a/toontown/cogdominium/DistCogdoBoardroomGame.py b/toontown/cogdominium/DistCogdoBoardroomGame.py new file mode 100755 index 00000000..6865f467 --- /dev/null +++ b/toontown/cogdominium/DistCogdoBoardroomGame.py @@ -0,0 +1,47 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from toontown.cogdominium.DistCogdoLevelGame import DistCogdoLevelGame +from toontown.cogdominium.CogdoBoardroomGameBase import CogdoBoardroomGameBase +from toontown.cogdominium import CogdoBoardroomGameConsts as Consts +from toontown.toonbase import ToontownTimer +from toontown.toonbase import TTLocalizer as TTL + +class DistCogdoBoardroomGame(CogdoBoardroomGameBase, DistCogdoLevelGame): + notify = directNotify.newCategory('DistCogdoBoardroomGame') + + def __init__(self, cr): + DistCogdoLevelGame.__init__(self, cr) + + def getTitle(self): + return TTL.BoardroomGameTitle + + def getInstructions(self): + return TTL.BoardroomGameInstructions + + def announceGenerate(self): + DistCogdoLevelGame.announceGenerate(self) + self.timer = ToontownTimer.ToontownTimer() + self.timer.setScale(Consts.Settings.TimerScale.get()) + self.timer.stash() + + def disable(self): + self.timer.destroy() + self.timer = None + DistCogdoLevelGame.disable(self) + return + + def enterGame(self): + DistCogdoLevelGame.enterGame(self) + timeLeft = Consts.GameDuration.get() - (globalClock.getRealTime() - self.getStartTime()) + self.timer.setTime(timeLeft) + self.timer.countdown(timeLeft, self.timerExpired) + self.timer.unstash() + + def enterFinish(self): + DistCogdoLevelGame.enterFinish(self) + timeLeft = Consts.FinishDuration.get() - (globalClock.getRealTime() - self.getFinishTime()) + self.timer.setTime(timeLeft) + self.timer.countdown(timeLeft, self.timerExpired) + self.timer.unstash() + + def timerExpired(self): + pass diff --git a/toontown/cogdominium/DistCogdoBoardroomGameAI.py b/toontown/cogdominium/DistCogdoBoardroomGameAI.py new file mode 100755 index 00000000..d3710d50 --- /dev/null +++ b/toontown/cogdominium/DistCogdoBoardroomGameAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.cogdominium.DistCogdoLevelGameAI import DistCogdoLevelGameAI + +class DistCogdoBoardroomGameAI(DistCogdoLevelGameAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistCogdoBoardroomGameAI") diff --git a/toontown/cogdominium/DistCogdoCrane.py b/toontown/cogdominium/DistCogdoCrane.py new file mode 100755 index 00000000..a9ed88aa --- /dev/null +++ b/toontown/cogdominium/DistCogdoCrane.py @@ -0,0 +1,872 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.fsm import FSM +from direct.distributed import DistributedObject +from direct.showutil import Rope +from direct.showbase import PythonUtil +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPGlobals +from toontown.cogdominium import CogdoCraneGameConsts as GameConsts +import random + +class DistCogdoCrane(DistributedObject.DistributedObject, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistCogdoCrane') + firstMagnetBit = 21 + craneMinY = 8 + craneMaxY = 25 + armMinH = -45 + armMaxH = 45 + shadowOffset = 7 + emptySlideSpeed = 10 + emptyRotateSpeed = 20 + lookAtPoint = Point3(0.3, 0, 0.1) + lookAtUp = Vec3(0, -1, 0) + neutralStickHinge = VBase3(0, 90, 0) + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistCogdoCrane') + self.craneGame = None + self.index = None + self.avId = 0 + self.cableLength = 20 + self.numLinks = 3 + self.initialArmPosition = (0, 20, 0) + self.slideSpeed = self.emptySlideSpeed + self.rotateSpeed = self.emptyRotateSpeed + self.changeSeq = 0 + self.lastChangeSeq = 0 + self.moveSound = None + self.links = [] + self.activeLinks = [] + self.collisions = NodePathCollection() + self.physicsActivated = 0 + self.snifferActivated = 0 + self.magnetOn = 0 + self.root = NodePath('root') + self.hinge = self.root.attachNewNode('hinge') + self.hinge.setPos(0, -17.6, 38.5) + self.controls = self.root.attachNewNode('controls') + self.controls.setPos(0, -4.9, 0) + self.arm = self.hinge.attachNewNode('arm') + self.crane = self.arm.attachNewNode('crane') + self.cable = self.hinge.attachNewNode('cable') + self.topLink = self.crane.attachNewNode('topLink') + self.topLink.setPos(0, 0, -1) + self.shadow = None + self.p0 = Point3(0, 0, 0) + self.v1 = Vec3(1, 1, 1) + self.armSmoother = SmoothMover() + self.armSmoother.setSmoothMode(SmoothMover.SMOn) + self.linkSmoothers = [] + self.smoothStarted = 0 + self.__broadcastPeriod = 0.2 + self.cable.node().setFinal(1) + self.crane.setPos(*self.initialArmPosition) + self.heldObject = None + self.craneAdviceLabel = None + self.magnetAdviceLabel = None + self.atLimitSfx = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.ogg') + self.magnetOnSfx = base.loadSfx('phase_10/audio/sfx/CBHQ_CFO_magnet_on.ogg') + self.magnetLoopSfx = base.loadSfx('phase_10/audio/sfx/CBHQ_CFO_magnet_loop.ogg') + self.magnetSoundInterval = Parallel(SoundInterval(self.magnetOnSfx), Sequence(Wait(0.5), Func(base.playSfx, self.magnetLoopSfx, looping=1))) + self.craneMoveSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_elevator_up_down.ogg') + self.fadeTrack = None + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.name = 'crane-%s' % self.doId + self.root.setName(self.name) + self.root.setPosHpr(*GameConsts.CranePosHprs[self.index]) + self.rotateLinkName = self.uniqueName('rotateLink') + self.snifferEvent = self.uniqueName('sniffer') + self.triggerName = self.uniqueName('trigger') + self.triggerEvent = 'enter%s' % self.triggerName + self.shadowName = self.uniqueName('shadow') + self.flickerName = self.uniqueName('flicker') + self.smoothName = self.uniqueName('craneSmooth') + self.posHprBroadcastName = self.uniqueName('craneBroadcast') + self.craneAdviceName = self.uniqueName('craneAdvice') + self.magnetAdviceName = self.uniqueName('magnetAdvice') + self.controlModel = self.craneGame.controls.copyTo(self.controls) + self.cc = NodePath('cc') + column = self.controlModel.find('**/column') + column.getChildren().reparentTo(self.cc) + self.cc.reparentTo(column) + self.stickHinge = self.cc.attachNewNode('stickHinge') + self.stick = self.craneGame.stick.copyTo(self.stickHinge) + self.stickHinge.setHpr(self.neutralStickHinge) + self.stick.setHpr(0, -90, 0) + self.stick.flattenLight() + self.bottom = self.controlModel.find('**/bottom') + self.bottom.wrtReparentTo(self.cc) + self.bottomPos = self.bottom.getPos() + cs = CollisionSphere(0, -5, -2, 3) + cs.setTangible(0) + cn = CollisionNode(self.triggerName) + cn.addSolid(cs) + cn.setIntoCollideMask(OTPGlobals.WallBitmask) + self.trigger = self.root.attachNewNode(cn) + self.trigger.stash() + cs = CollisionTube(0, 2.7, 0, 0, 2.7, 3, 1.2) + cn = CollisionNode('tube') + cn.addSolid(cs) + cn.setIntoCollideMask(OTPGlobals.WallBitmask) + self.tube = self.controlModel.attachNewNode(cn) + cs = CollisionSphere(0, 0, 2, 3) + cn = CollisionNode('safetyBubble') + cn.addSolid(cs) + cn.setIntoCollideMask(ToontownGlobals.PieBitmask) + self.controls.attachNewNode(cn) + arm = self.craneGame.craneArm.copyTo(self.crane) + self.craneGame.cranes[self.index] = self + + def disable(self): + DistributedObject.DistributedObject.disable(self) + del self.craneGame.cranes[self.index] + self.cleanup() + + def cleanup(self): + if self.state != 'Off': + self.demand('Off') + self.craneGame = None + return + + def accomodateToon(self, toon): + origScale = self.controlModel.getSz() + origCcPos = self.cc.getPos() + origBottomPos = self.bottom.getPos() + origStickHingeHpr = self.stickHinge.getHpr() + scale = toon.getGeomNode().getChild(0).getSz(render) + self.controlModel.setScale(scale) + self.cc.setPos(0, 0, 0) + toon.setPosHpr(self.controls, 0, 0, 0, 0, 0, 0) + toon.pose('leverNeutral', 0) + toon.update() + pos = toon.rightHand.getPos(self.cc) + self.cc.setPos(pos[0], pos[1], pos[2] - 1) + self.bottom.setZ(toon, 0.0) + self.bottom.setPos(self.bottomPos[0], self.bottomPos[1], self.bottom.getZ()) + self.stickHinge.lookAt(toon.rightHand, self.lookAtPoint, self.lookAtUp) + lerpTime = 0.5 + return Parallel(self.controlModel.scaleInterval(lerpTime, scale, origScale, blendType='easeInOut'), self.cc.posInterval(lerpTime, self.cc.getPos(), origCcPos, blendType='easeInOut'), self.bottom.posInterval(lerpTime, self.bottom.getPos(), origBottomPos, blendType='easeInOut'), self.stickHinge.quatInterval(lerpTime, self.stickHinge.getHpr(), origStickHingeHpr, blendType='easeInOut')) + + def getRestoreScaleInterval(self): + lerpTime = 1 + return Parallel(self.controlModel.scaleInterval(lerpTime, 1, blendType='easeInOut'), self.cc.posInterval(lerpTime, Point3(0, 0, 0), blendType='easeInOut'), self.bottom.posInterval(lerpTime, self.bottomPos, blendType='easeInOut'), self.stickHinge.quatInterval(lerpTime, self.neutralStickHinge, blendType='easeInOut')) + + def makeToonGrabInterval(self, toon): + origPos = toon.getPos() + origHpr = toon.getHpr() + a = self.accomodateToon(toon) + newPos = toon.getPos() + newHpr = toon.getHpr() + origHpr.setX(PythonUtil.fitSrcAngle2Dest(origHpr[0], newHpr[0])) + toon.setPosHpr(origPos, origHpr) + walkTime = 0.2 + reach = ActorInterval(toon, 'leverReach') + if reach.getDuration() < walkTime: + reach = Sequence(ActorInterval(toon, 'walk', loop=1, duration=walkTime - reach.getDuration()), reach) + i = Sequence(Parallel(toon.posInterval(walkTime, newPos, origPos), toon.hprInterval(walkTime, newHpr, origHpr), reach), Func(self.startWatchJoystick, toon)) + i = Parallel(i, a) + return i + + def __toonPlayWithCallback(self, animName, numFrames): + duration = numFrames / 24.0 + self.toon.play(animName) + taskMgr.doMethodLater(duration, self.__toonPlayCallback, self.uniqueName('toonPlay')) + + def __toonPlayCallback(self, task): + if self.changeSeq == self.lastChangeSeq: + self.__toonPlayWithCallback('leverNeutral', 40) + else: + self.__toonPlayWithCallback('leverPull', 40) + self.lastChangeSeq = self.changeSeq + + def startWatchJoystick(self, toon): + self.toon = toon + taskMgr.add(self.__watchJoystick, self.uniqueName('watchJoystick')) + self.__toonPlayWithCallback('leverNeutral', 40) + self.accept(toon.uniqueName('disable'), self.__handleUnexpectedExit, extraArgs=[toon.doId]) + + def stopWatchJoystick(self): + taskMgr.remove(self.uniqueName('toonPlay')) + taskMgr.remove(self.uniqueName('watchJoystick')) + if self.toon: + self.ignore(self.toon.uniqueName('disable')) + self.toon = None + return + + def __watchJoystick(self, task): + self.toon.setPosHpr(self.controls, 0, 0, 0, 0, 0, 0) + self.toon.update() + self.stickHinge.lookAt(self.toon.rightHand, self.lookAtPoint, self.lookAtUp) + return Task.cont + + def __handleUnexpectedExit(self, toonId): + self.notify.warning('%s: unexpected exit for %s' % (self.doId, toonId)) + if self.toon and self.toon.doId == toonId: + self.stopWatchJoystick() + + def __activatePhysics(self): + if not self.physicsActivated: + for an, anp, cnp in self.activeLinks: + self.craneGame.physicsMgr.attachPhysicalNode(an) + base.cTrav.addCollider(cnp, self.handler) + + self.collisions.unstash() + self.physicsActivated = 1 + + def __deactivatePhysics(self): + if self.physicsActivated: + for an, anp, cnp in self.activeLinks: + self.craneGame.physicsMgr.removePhysicalNode(an) + base.cTrav.removeCollider(cnp) + + self.collisions.stash() + self.physicsActivated = 0 + + def __straightenCable(self): + for linkNum in xrange(self.numLinks): + an, anp, cnp = self.activeLinks[linkNum] + an.getPhysicsObject().setVelocity(0, 0, 0) + z = float(linkNum + 1) / float(self.numLinks) * self.cableLength + anp.setPos(self.crane.getPos(self.cable)) + anp.setZ(-z) + + def setCableLength(self, length): + self.cableLength = length + linkWidth = float(length) / float(self.numLinks) + self.shell.setRadius(linkWidth + 1) + + def setupCable(self): + activated = self.physicsActivated + self.clearCable() + self.handler = PhysicsCollisionHandler() + self.handler.setStaticFrictionCoef(0.1) + self.handler.setDynamicFrictionCoef(GameConsts.Settings.EmptyFrictionCoef.get()) + linkWidth = float(self.cableLength) / float(self.numLinks) + self.shell = CollisionInvSphere(0, 0, 0, linkWidth + 1) + self.links = [] + self.links.append((self.topLink, Point3(0, 0, 0))) + anchor = self.topLink + for linkNum in xrange(self.numLinks): + anchor = self.__makeLink(anchor, linkNum) + + self.collisions.stash() + self.bottomLink = self.links[-1][0] + self.middleLink = self.links[-2][0] + self.magnet = self.bottomLink.attachNewNode('magnet') + self.wiggleMagnet = self.magnet.attachNewNode('wiggleMagnet') + taskMgr.add(self.__rotateMagnet, self.rotateLinkName) + magnetModel = self.craneGame.magnet.copyTo(self.wiggleMagnet) + magnetModel.setHpr(90, 45, 90) + self.gripper = magnetModel.attachNewNode('gripper') + self.gripper.setPos(0, 0, -4) + cn = CollisionNode('sniffer') + self.sniffer = magnetModel.attachNewNode(cn) + self.sniffer.stash() + cs = CollisionSphere(0, 0, -10, 6) + cs.setTangible(0) + cn.addSolid(cs) + cn.setIntoCollideMask(BitMask32(0)) + cn.setFromCollideMask(ToontownGlobals.CashbotBossObjectBitmask) + self.snifferHandler = CollisionHandlerEvent() + self.snifferHandler.addInPattern(self.snifferEvent) + self.snifferHandler.addAgainPattern(self.snifferEvent) + rope = self.makeSpline() + rope.reparentTo(self.cable) + rope.setTexture(self.craneGame.cableTex) + ts = TextureStage.getDefault() + rope.setTexScale(ts, 0.15, 0.13) + rope.setTexOffset(ts, 0.83, 0.01) + if activated: + self.__activatePhysics() + + def clearCable(self): + self.__deactivatePhysics() + taskMgr.remove(self.rotateLinkName) + self.links = [] + self.activeLinks = [] + self.linkSmoothers = [] + self.collisions.clear() + self.cable.getChildren().detach() + self.topLink.getChildren().detach() + self.gripper = None + return + + def makeSpline(self): + rope = Rope.Rope() + rope.setup(min(len(self.links), 4), self.links) + rope.curve.normalizeKnots() + rn = rope.ropeNode + rn.setRenderMode(RopeNode.RMTube) + rn.setNumSlices(3) + rn.setTubeUp(Vec3(0, -1, 0)) + rn.setUvMode(RopeNode.UVParametric) + rn.setUvDirection(1) + rn.setThickness(0.5) + return rope + + def startShadow(self): + self.shadow = self.craneGame.geomRoot.attachNewNode('%s-shadow' % self.name) + self.shadow.setColor(1, 1, 1, 0.3) + self.shadow.setDepthWrite(0) + self.shadow.setTransparency(1) + self.shadow.setBin('shadow', 0) + self.shadow.node().setFinal(1) + self.magnetShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.magnetShadow.reparentTo(self.shadow) + self.craneShadow = loader.loadModel('phase_3/models/props/square_drop_shadow') + self.craneShadow.setScale(0.5, 4, 1) + self.craneShadow.setPos(0, -12, 0) + self.craneShadow.flattenLight() + self.craneShadow.reparentTo(self.shadow) + taskMgr.add(self.__followShadow, self.shadowName) + rope = self.makeSpline() + rope.reparentTo(self.shadow) + rope.setColor(1, 1, 1, 0.2) + tex = self.craneShadow.findTexture('*') + rope.setTexture(tex) + rn = rope.ropeNode + rn.setRenderMode(RopeNode.RMTape) + rn.setNumSubdiv(6) + rn.setThickness(0.8) + rn.setTubeUp(Vec3(0, 0, 1)) + rn.setMatrix(Mat4.translateMat(0, 0, self.shadowOffset) * Mat4.scaleMat(1, 1, 0.01)) + + def stopShadow(self): + if self.shadow: + self.shadow.removeNode() + self.shadow = None + self.magnetShadow = None + self.craneShadow = None + taskMgr.remove(self.shadowName) + return + + def __followShadow(self, task): + p = self.magnet.getPos(self.craneGame.geomRoot) + self.magnetShadow.setPos(p[0], p[1], self.shadowOffset) + self.craneShadow.setPosHpr(self.crane, 0, 0, 0, 0, 0, 0) + self.craneShadow.setZ(self.shadowOffset) + return Task.cont + + def __makeLink(self, anchor, linkNum): + an = ActorNode('link%s' % linkNum) + an.getPhysicsObject().setMass(GameConsts.Settings.RopeLinkMass.get()) + anp = NodePath(an) + cn = CollisionNode('cn') + sphere = CollisionSphere(0, 0, 0, 1) + cn.addSolid(sphere) + cnp = anp.attachNewNode(cn) + self.handler.addCollider(cnp, anp) + self.activeLinks.append((an, anp, cnp)) + self.linkSmoothers.append(SmoothMover()) + anp.reparentTo(self.cable) + z = float(linkNum + 1) / float(self.numLinks) * self.cableLength + anp.setPos(self.crane.getPos()) + anp.setZ(-z) + mask = BitMask32.bit(self.firstMagnetBit + linkNum) + cn.setFromCollideMask(mask) + cn.setIntoCollideMask(BitMask32(0)) + shellNode = CollisionNode('shell%s' % linkNum) + shellNode.addSolid(self.shell) + shellNP = anchor.attachNewNode(shellNode) + shellNode.setIntoCollideMask(mask) + self.collisions.addPath(shellNP) + self.collisions.addPath(cnp) + self.links.append((anp, Point3(0, 0, 0))) + return anp + + def __rotateMagnet(self, task): + self.magnet.lookAt(self.middleLink, self.p0, self.v1) + return Task.cont + + def __enableControlInterface(self): + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.accept('control', self.__controlPressed) + self.accept('control-up', self.__controlReleased) + self.accept('InputState-forward', self.__upArrow) + self.accept('InputState-reverse', self.__downArrow) + self.accept('InputState-turnLeft', self.__leftArrow) + self.accept('InputState-turnRight', self.__rightArrow) + taskMgr.add(self.__watchControls, 'watchCraneControls') + taskMgr.doMethodLater(5, self.__displayCraneAdvice, self.craneAdviceName) + taskMgr.doMethodLater(10, self.__displayMagnetAdvice, self.magnetAdviceName) + NametagGlobals.setOnscreenChatForced(1) + self.arrowVert = 0 + self.arrowHorz = 0 + + def __disableControlInterface(self): + self.__turnOffMagnet() + self.__cleanupCraneAdvice() + self.__cleanupMagnetAdvice() + self.ignore('escape') + self.ignore('control') + self.ignore('control-up') + self.ignore('InputState-forward') + self.ignore('InputState-reverse') + self.ignore('InputState-turnLeft') + self.ignore('InputState-turnRight') + self.arrowVert = 0 + self.arrowHorz = 0 + NametagGlobals.setOnscreenChatForced(0) + taskMgr.remove('watchCraneControls') + self.__setMoveSound(None) + return + + def __displayCraneAdvice(self, task): + if self.craneAdviceLabel == None: + self.craneAdviceLabel = DirectLabel(text=TTLocalizer.CashbotCraneAdvice, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.69), scale=0.1) + return + + def __cleanupCraneAdvice(self): + if self.craneAdviceLabel: + self.craneAdviceLabel.destroy() + self.craneAdviceLabel = None + taskMgr.remove(self.craneAdviceName) + return + + def __displayMagnetAdvice(self, task): + if self.magnetAdviceLabel == None: + self.magnetAdviceLabel = DirectLabel(text=TTLocalizer.CashbotMagnetAdvice, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.55), scale=0.1) + return + + def __cleanupMagnetAdvice(self): + if self.magnetAdviceLabel: + self.magnetAdviceLabel.destroy() + self.magnetAdviceLabel = None + taskMgr.remove(self.magnetAdviceName) + return + + def __watchControls(self, task): + if self.arrowHorz or self.arrowVert: + self.__moveCraneArcHinge(self.arrowHorz, self.arrowVert) + else: + self.__setMoveSound(None) + return Task.cont + + def __incrementChangeSeq(self): + self.changeSeq = self.changeSeq + 1 & 255 + + def __controlPressed(self): + self.__cleanupMagnetAdvice() + self.__turnOnMagnet() + + def __controlReleased(self): + self.__turnOffMagnet() + + def __turnOnMagnet(self): + if not self.magnetOn: + self.__incrementChangeSeq() + self.magnetOn = 1 + if not self.heldObject: + self.__activateSniffer() + + def __turnOffMagnet(self): + if self.magnetOn: + self.magnetOn = 0 + self.__deactivateSniffer() + self.releaseObject() + + def __upArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupCraneAdvice() + if pressed: + self.arrowVert = 1 + elif self.arrowVert > 0: + self.arrowVert = 0 + + def __downArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupCraneAdvice() + if pressed: + self.arrowVert = -1 + elif self.arrowVert < 0: + self.arrowVert = 0 + + def __rightArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupCraneAdvice() + if pressed: + self.arrowHorz = 1 + elif self.arrowHorz > 0: + self.arrowHorz = 0 + + def __leftArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupCraneAdvice() + if pressed: + self.arrowHorz = -1 + elif self.arrowHorz < 0: + self.arrowHorz = 0 + + def __moveCraneArcHinge(self, xd, yd): + dt = globalClock.getDt() + h = self.arm.getH() - xd * self.rotateSpeed * dt + limitH = max(min(h, self.armMaxH), self.armMinH) + self.arm.setH(limitH) + y = self.crane.getY() + yd * self.slideSpeed * dt + limitY = max(min(y, self.craneMaxY), self.craneMinY) + atLimit = (limitH != h or limitY != y) + if atLimit: + now = globalClock.getFrameTime() + x = math.sin(now * 79) * 0.05 + z = math.sin(now * 70) * 0.02 + self.crane.setPos(x, limitY, z) + self.__setMoveSound(self.atLimitSfx) + else: + self.crane.setPos(0, limitY, 0) + self.__setMoveSound(self.craneMoveSfx) + + def __setMoveSound(self, sfx): + if sfx != self.moveSound: + if self.moveSound: + self.moveSound.stop() + self.moveSound = sfx + if self.moveSound: + base.playSfx(self.moveSound, looping=1, volume=0.5) + + def __activateSniffer(self): + if not self.snifferActivated: + self.sniffer.unstash() + base.cTrav.addCollider(self.sniffer, self.snifferHandler) + self.accept(self.snifferEvent, self.__sniffedSomething) + self.startFlicker() + self.snifferActivated = 1 + + def __deactivateSniffer(self): + if self.snifferActivated: + base.cTrav.removeCollider(self.sniffer) + self.sniffer.stash() + self.ignore(self.snifferEvent) + self.stopFlicker() + self.snifferActivated = 0 + + def startFlicker(self): + self.magnetSoundInterval.start() + self.lightning = [] + for i in xrange(4): + t = float(i) / 3.0 - 0.5 + l = self.craneGame.lightning.copyTo(self.gripper) + l.setScale(random.choice([1, -1]), 1, 5) + l.setZ(random.uniform(-5, -5.5)) + l.flattenLight() + l.setTwoSided(1) + l.setBillboardAxis() + l.setScale(random.uniform(0.5, 1.0)) + if t < 0: + l.setX(t - 0.7) + else: + l.setX(t + 0.7) + l.setR(-20 * t) + l.setP(random.uniform(-20, 20)) + self.lightning.append(l) + + taskMgr.add(self.__flickerLightning, self.flickerName) + + def stopFlicker(self): + self.magnetSoundInterval.finish() + self.magnetLoopSfx.stop() + taskMgr.remove(self.flickerName) + for l in self.lightning: + l.detachNode() + + self.lightning = None + return + + def __flickerLightning(self, task): + for l in self.lightning: + if random.random() < 0.5: + l.hide() + else: + l.show() + + return Task.cont + + def __sniffedSomething(self, entry): + np = entry.getIntoNodePath() + doId = int(np.getNetTag('object')) + obj = base.cr.doId2do.get(doId) + if obj and obj.state != 'LocalDropped' and (obj.state != 'Dropped' or obj.craneId != self.doId): + obj.d_requestGrab() + obj.demand('LocalGrabbed', localAvatar.doId, self.doId) + + def grabObject(self, obj): + if self.state == 'Off': + return + if self.heldObject != None: + self.releaseObject() + self.__deactivateSniffer() + obj.wrtReparentTo(self.gripper) + if obj.lerpInterval: + obj.lerpInterval.finish() + obj.lerpInterval = Parallel(obj.posInterval(ToontownGlobals.CashbotBossToMagnetTime, Point3(*obj.grabPos)), obj.quatInterval(ToontownGlobals.CashbotBossToMagnetTime, VBase3(obj.getH(), 0, 0)), obj.toMagnetSoundInterval) + obj.lerpInterval.start() + self.heldObject = obj + self.handler.setDynamicFrictionCoef(obj.craneFrictionCoef) + self.slideSpeed = obj.craneSlideSpeed + self.rotateSpeed = obj.craneRotateSpeed + if self.avId == localAvatar.doId and not self.magnetOn: + self.releaseObject() + return + + def dropObject(self, obj): + if obj.lerpInterval: + obj.lerpInterval.finish() + obj.wrtReparentTo(render) + obj.lerpInterval = Parallel(obj.quatInterval(ToontownGlobals.CashbotBossFromMagnetTime, VBase3(obj.getH(), 0, 0), blendType='easeOut')) + obj.lerpInterval.start() + p1 = self.bottomLink.node().getPhysicsObject() + v = render.getRelativeVector(self.bottomLink, p1.getVelocity()) + obj.physicsObject.setVelocity(v * 1.5) + if self.heldObject == obj: + self.heldObject = None + self.handler.setDynamicFrictionCoef(GameConsts.Settings.EmptyFrictionCoef.get()) + self.slideSpeed = self.emptySlideSpeed + self.rotateSpeed = self.emptyRotateSpeed + return + + def releaseObject(self): + if self.heldObject: + obj = self.heldObject + obj.d_requestDrop() + if obj.state == 'Grabbed': + obj.demand('LocalDropped', localAvatar.doId, self.doId) + + def __hitTrigger(self, event): + pass + + def setCraneGameId(self, craneGameId): + self.craneGameId = craneGameId + self.craneGame = base.cr.doId2do[craneGameId] + + def setIndex(self, index): + self.index = index + + def setState(self, state, avId): + if state == 'C': + self.demand('Controlled', avId) + elif state == 'F': + self.demand('Free') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def d_requestControl(self): + self.sendUpdate('requestControl') + + def d_requestFree(self): + self.sendUpdate('requestFree') + + def b_clearSmoothing(self): + self.d_clearSmoothing() + self.clearSmoothing() + + def d_clearSmoothing(self): + self.sendUpdate('clearSmoothing', [0]) + + def clearSmoothing(self, bogus = None): + self.armSmoother.clearPositions(1) + for smoother in self.linkSmoothers: + smoother.clearPositions(1) + + def reloadPosition(self): + self.armSmoother.clearPositions(0) + self.armSmoother.setPos(self.crane.getPos()) + self.armSmoother.setHpr(self.arm.getHpr()) + self.armSmoother.setPhonyTimestamp() + for linkNum in xrange(self.numLinks): + smoother = self.linkSmoothers[linkNum] + an, anp, cnp = self.activeLinks[linkNum] + smoother.clearPositions(0) + smoother.setPos(anp.getPos()) + smoother.setPhonyTimestamp() + + def doSmoothTask(self, task): + self.armSmoother.computeAndApplySmoothPosHpr(self.crane, self.arm) + for linkNum in xrange(self.numLinks): + smoother = self.linkSmoothers[linkNum] + anp = self.activeLinks[linkNum][1] + smoother.computeAndApplySmoothPos(anp) + + return Task.cont + + def startSmooth(self): + if not self.smoothStarted: + taskName = self.smoothName + taskMgr.remove(taskName) + self.reloadPosition() + taskMgr.add(self.doSmoothTask, taskName) + self.smoothStarted = 1 + + def stopSmooth(self): + if self.smoothStarted: + taskName = self.smoothName + taskMgr.remove(taskName) + self.forceToTruePosition() + self.smoothStarted = 0 + + def forceToTruePosition(self): + if self.armSmoother.getLatestPosition(): + self.armSmoother.applySmoothPos(self.crane) + self.armSmoother.applySmoothHpr(self.arm) + self.armSmoother.clearPositions(1) + for linkNum in xrange(self.numLinks): + smoother = self.linkSmoothers[linkNum] + an, anp, cnp = self.activeLinks[linkNum] + if smoother.getLatestPosition(): + smoother.applySmoothPos(anp) + smoother.clearPositions(1) + + def setCablePos(self, changeSeq, y, h, links, timestamp): + self.changeSeq = changeSeq + if self.smoothStarted: + now = globalClock.getFrameTime() + local = globalClockDelta.networkToLocalTime(timestamp, now) + self.armSmoother.setY(y) + self.armSmoother.setH(h) + self.armSmoother.setTimestamp(local) + self.armSmoother.markPosition() + for linkNum in xrange(self.numLinks): + smoother = self.linkSmoothers[linkNum] + lp = links[linkNum] + smoother.setPos(*lp) + smoother.setTimestamp(local) + smoother.markPosition() + + else: + self.crane.setY(y) + self.arm.setH(h) + + def d_sendCablePos(self): + timestamp = globalClockDelta.getFrameNetworkTime() + links = [] + for linkNum in xrange(self.numLinks): + an, anp, cnp = self.activeLinks[linkNum] + p = anp.getPos() + links.append((p[0], p[1], p[2])) + + self.sendUpdate('setCablePos', [self.changeSeq, + self.crane.getY(), + self.arm.getH(), + links, + timestamp]) + + def stopPosHprBroadcast(self): + taskName = self.posHprBroadcastName + taskMgr.remove(taskName) + + def startPosHprBroadcast(self): + taskName = self.posHprBroadcastName + self.b_clearSmoothing() + self.d_sendCablePos() + taskMgr.remove(taskName) + taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) + + def __posHprBroadcast(self, task): + self.d_sendCablePos() + taskName = self.posHprBroadcastName + taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) + return Task.done + + def enterOff(self): + self.clearCable() + self.root.detachNode() + + def exitOff(self): + if self.craneGame: + self.setupCable() + self.root.reparentTo(render) + + def enterControlled(self, avId): + self.avId = avId + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.grabTrack = self.makeToonGrabInterval(toon) + if avId == localAvatar.doId: + self.craneGame.toCraneMode() + camera.reparentTo(self.hinge) + camera.setPosHpr(0, -20, -5, 0, -20, 0) + self.tube.stash() + localAvatar.setPosHpr(self.controls, 0, 0, 0, 0, 0, 0) + localAvatar.sendCurrentPosition() + self.__activatePhysics() + self.__enableControlInterface() + self.startPosHprBroadcast() + self.startShadow() + else: + self.startSmooth() + toon.stopSmooth() + self.grabTrack = Sequence(self.grabTrack, Func(toon.startSmooth)) + self.grabTrack.start() + + def exitControlled(self): + self.grabTrack.finish() + del self.grabTrack + if self.toon and not self.toon.isDisabled(): + self.toon.loop('neutral') + self.toon.startSmooth() + self.stopWatchJoystick() + self.stopPosHprBroadcast() + self.stopShadow() + self.stopSmooth() + if self.avId == localAvatar.doId: + self.__disableControlInterface() + self.__deactivatePhysics() + self.tube.unstash() + camera.reparentTo(base.localAvatar) + camera.setPos(base.localAvatar.cameraPositions[0][0]) + camera.setHpr(0, 0, 0) + self.__straightenCable() + + def enterFree(self): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + self.restoreScaleTrack = Sequence(Wait(6), self.getRestoreScaleInterval()) + self.restoreScaleTrack.start() + if self.avId == localAvatar.doId: + self.controlModel.setAlphaScale(0.3) + self.controlModel.setTransparency(1) + taskMgr.doMethodLater(5, self.__allowDetect, self.triggerName) + self.fadeTrack = Sequence(Func(self.controlModel.setTransparency, 1), self.controlModel.colorScaleInterval(0.2, VBase4(1, 1, 1, 0.3))) + self.fadeTrack.start() + else: + self.trigger.unstash() + self.accept(self.triggerEvent, self.__hitTrigger) + self.avId = 0 + return + + def __allowDetect(self, task): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = Sequence(self.controlModel.colorScaleInterval(0.2, VBase4(1, 1, 1, 1)), Func(self.controlModel.clearColorScale), Func(self.controlModel.clearTransparency)) + self.fadeTrack.start() + self.trigger.unstash() + self.accept(self.triggerEvent, self.__hitTrigger) + + def exitFree(self): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + self.restoreScaleTrack.pause() + del self.restoreScaleTrack + taskMgr.remove(self.triggerName) + self.controlModel.clearColorScale() + self.controlModel.clearTransparency() + self.trigger.stash() + self.ignore(self.triggerEvent) + return + + def enterMovie(self): + self.__activatePhysics() + + def exitMovie(self): + self.__deactivatePhysics() + self.__straightenCable() diff --git a/toontown/cogdominium/DistCogdoCraneAI.py b/toontown/cogdominium/DistCogdoCraneAI.py new file mode 100755 index 00000000..aa94e885 --- /dev/null +++ b/toontown/cogdominium/DistCogdoCraneAI.py @@ -0,0 +1,50 @@ +from panda3d.core import * +from direct.distributed import DistributedObjectAI +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from direct.fsm import FSM + +class DistCogdoCraneAI(DistributedObjectAI.DistributedObjectAI, FSM.FSM): + + def __init__(self, air, craneGame, index): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistCogdoCraneAI') + self.craneGame = craneGame + self.index = index + self.avId = 0 + self.objectId = 0 + + def getCraneGameId(self): + return self.craneGame.doId + + def getIndex(self): + return self.index + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.request('Free') + + def d_setState(self, state, avId): + self.sendUpdate('setState', [state, avId]) + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterControlled(self, avId): + self.avId = avId + self.d_setState('C', avId) + + def exitControlled(self): + if self.objectId: + obj = self.air.doId2do[self.objectId] + obj.request('Dropped', self.avId, self.doId) + + def enterFree(self): + self.avId = 0 + self.d_setState('F', 0) + + def exitFree(self): + pass diff --git a/toontown/cogdominium/DistCogdoCraneCog.py b/toontown/cogdominium/DistCogdoCraneCog.py new file mode 100755 index 00000000..b22c8e8f --- /dev/null +++ b/toontown/cogdominium/DistCogdoCraneCog.py @@ -0,0 +1,61 @@ +from pandac import PandaModules as PM +from direct.distributed.ClockDelta import globalClockDelta +from direct.distributed.DistributedObject import DistributedObject +from direct.interval import IntervalGlobal as IG +from toontown.cogdominium import CogdoCraneGameConsts as GameConsts +from toontown.suit.Suit import Suit + +class DistCogdoCraneCog(Suit, DistributedObject): + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + Suit.__init__(self) + self._moveIval = None + return + + def setGameId(self, gameId): + self._gameId = gameId + + def getGame(self): + return self.cr.doId2do.get(self._gameId) + + def setSpawnInfo(self, entranceId, timestamp): + self._startMoveIval(entranceId, globalClockDelta.networkToLocalTime(timestamp)) + + def _startMoveIval(self, entranceId, startT): + self._stopMoveIval() + unitVecs = (PM.Vec3(1, 0, 0), + PM.Vec3(0, 1, 0), + PM.Vec3(-1, 0, 0), + PM.Vec3(0, -1, 0)) + machineDistance = 4 + entranceDistance = 60 + startPos = unitVecs[entranceId] * entranceDistance + endPos = unitVecs[entranceId] * machineDistance + walkDur = (endPos - startPos).length() / GameConsts.CogSettings.CogWalkSpeed.get() + sceneRoot = self.getGame().getSceneRoot() + moveIval = IG.Sequence(IG.Func(self.reparentTo, sceneRoot), IG.Func(self.setPos, startPos), IG.Func(self.lookAt, sceneRoot), IG.Func(self.loop, 'walk'), IG.LerpPosInterval(self, walkDur, endPos, startPos=startPos)) + interactIval = IG.Sequence(IG.Func(self.loop, 'neutral'), IG.Wait(GameConsts.CogSettings.CogMachineInteractDuration.get())) + flyIval = IG.Sequence(IG.Func(self.pose, 'landing', 0), IG.LerpPosInterval(self, GameConsts.CogSettings.CogFlyAwayDuration.get(), self._getFlyAwayDest, blendType='easeIn')) + self._moveIval = IG.Sequence(moveIval, interactIval, flyIval) + self._moveIval.start(globalClock.getFrameTime() - startT) + + def _getFlyAwayDest(self): + return self.getPos() + PM.Vec3(0, 0, GameConsts.CogSettings.CogFlyAwayHeight.get()) + + def _stopMoveIval(self): + if self._moveIval: + self._moveIval.finish() + self._moveIval = None + return + + def disable(self): + self._stopMoveIval() + DistributedObject.disable(self) + + def delete(self): + Suit.delete(self) + DistributedObject.delete(self) + + def setDNAString(self, dnaString): + Suit.setDNAString(self, dnaString) diff --git a/toontown/cogdominium/DistCogdoCraneCogAI.py b/toontown/cogdominium/DistCogdoCraneCogAI.py new file mode 100755 index 00000000..12865c67 --- /dev/null +++ b/toontown/cogdominium/DistCogdoCraneCogAI.py @@ -0,0 +1,14 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI + +class DistCogdoCraneCogAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistCogdoCraneCogAI") + + def setGameId(self, todo0): + pass + + def setDNAString(self, todo0): + pass + + def setSpawnInfo(self, todo0, todo1): + pass diff --git a/toontown/cogdominium/DistCogdoCraneGame.py b/toontown/cogdominium/DistCogdoCraneGame.py new file mode 100755 index 00000000..58bcfe61 --- /dev/null +++ b/toontown/cogdominium/DistCogdoCraneGame.py @@ -0,0 +1,177 @@ +from pandac import PandaModules as PM +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.task.Task import Task +from otp.level import LevelConstants +from otp.otpbase import OTPGlobals +from toontown.cogdominium.DistCogdoLevelGame import DistCogdoLevelGame +from toontown.cogdominium import CogdoCraneGameConsts as GameConsts +from toontown.cogdominium.CogdoCraneGameBase import CogdoCraneGameBase +from toontown.toonbase import ToontownTimer +from toontown.toonbase import TTLocalizer as TTL +from toontown.toonbase import ToontownGlobals + +class DistCogdoCraneGame(CogdoCraneGameBase, DistCogdoLevelGame): + notify = directNotify.newCategory('DistCogdoCraneGame') + + def __init__(self, cr): + DistCogdoLevelGame.__init__(self, cr) + self.cranes = {} + self.moneyBags = {} + + def getTitle(self): + return TTL.CogdoCraneGameTitle + + def getInstructions(self): + return TTL.CogdoCraneGameInstructions + + def announceGenerate(self): + DistCogdoLevelGame.announceGenerate(self) + self.timer = ToontownTimer.ToontownTimer() + self.timer.stash() + + def disable(self): + self.timer.destroy() + self.timer = None + DistCogdoLevelGame.disable(self) + return + + def enterLoaded(self): + DistCogdoLevelGame.enterLoaded(self) + self.lightning = loader.loadModel('phase_10/models/cogHQ/CBLightning.bam') + self.magnet = loader.loadModel('phase_10/models/cogHQ/CBMagnet.bam') + self.craneArm = loader.loadModel('phase_10/models/cogHQ/CBCraneArm.bam') + self.controls = loader.loadModel('phase_10/models/cogHQ/CBCraneControls.bam') + self.stick = loader.loadModel('phase_10/models/cogHQ/CBCraneStick.bam') + self.cableTex = self.craneArm.findTexture('MagnetControl') + self.moneyBag = loader.loadModel('phase_10/models/cashbotHQ/MoneyBag') + self.geomRoot = PM.NodePath('geom') + self.sceneRoot = self.geomRoot.attachNewNode('sceneRoot') + self.sceneRoot.setPos(35.84, -115.46, 6.46) + self.physicsMgr = PM.PhysicsManager() + integrator = PM.LinearEulerIntegrator() + self.physicsMgr.attachLinearIntegrator(integrator) + fn = PM.ForceNode('gravity') + self.fnp = self.geomRoot.attachNewNode(fn) + gravity = PM.LinearVectorForce(0, 0, GameConsts.Settings.Gravity.get()) + fn.addForce(gravity) + self.physicsMgr.addLinearForce(gravity) + self._gravityForce = gravity + self._gravityForceNode = fn + + def getSceneRoot(self): + return self.sceneRoot + + def privGotSpec(self, levelSpec): + DistCogdoLevelGame.privGotSpec(self, levelSpec) + levelMgr = self.getEntity(LevelConstants.LevelMgrEntId) + self.endVault = levelMgr.geom + self.endVault.reparentTo(self.geomRoot) + self.endVault.findAllMatches('**/MagnetArms').detach() + self.endVault.findAllMatches('**/Safes').detach() + self.endVault.findAllMatches('**/MagnetControlsAll').detach() + cn = self.endVault.find('**/wallsCollision').node() + cn.setIntoCollideMask(OTPGlobals.WallBitmask | ToontownGlobals.PieBitmask | PM.BitMask32.lowerOn(3) << 21) + walls = self.endVault.find('**/RollUpFrameCillison') + walls.detachNode() + self.evWalls = self.replaceCollisionPolysWithPlanes(walls) + self.evWalls.reparentTo(self.endVault) + self.evWalls.stash() + floor = self.endVault.find('**/EndVaultFloorCollision') + floor.detachNode() + self.evFloor = self.replaceCollisionPolysWithPlanes(floor) + self.evFloor.reparentTo(self.endVault) + self.evFloor.setName('floor') + plane = PM.CollisionPlane(PM.Plane(PM.Vec3(0, 0, 1), PM.Point3(0, 0, -50))) + planeNode = PM.CollisionNode('dropPlane') + planeNode.addSolid(plane) + planeNode.setCollideMask(ToontownGlobals.PieBitmask) + self.geomRoot.attachNewNode(planeNode) + + def replaceCollisionPolysWithPlanes(self, model): + newCollisionNode = PM.CollisionNode('collisions') + newCollideMask = PM.BitMask32(0) + planes = [] + collList = model.findAllMatches('**/+CollisionNode') + if not collList: + collList = [model] + for cnp in collList: + cn = cnp.node() + if not isinstance(cn, PM.CollisionNode): + self.notify.warning('Not a collision node: %s' % repr(cnp)) + break + newCollideMask = newCollideMask | cn.getIntoCollideMask() + for i in xrange(cn.getNumSolids()): + solid = cn.getSolid(i) + if isinstance(solid, PM.CollisionPolygon): + plane = PM.Plane(solid.getPlane()) + planes.append(plane) + else: + self.notify.warning('Unexpected collision solid: %s' % repr(solid)) + newCollisionNode.addSolid(plane) + + newCollisionNode.setIntoCollideMask(newCollideMask) + threshold = 0.1 + planes.sort(lambda p1, p2: p1.compareTo(p2, threshold)) + lastPlane = None + for plane in planes: + if lastPlane == None or plane.compareTo(lastPlane, threshold) != 0: + cp = PM.CollisionPlane(plane) + newCollisionNode.addSolid(cp) + lastPlane = plane + + return PM.NodePath(newCollisionNode) + + def exitLoaded(self): + self.fnp.removeNode() + self.physicsMgr.clearLinearForces() + self.geomRoot.removeNode() + self._gravityForce = None + self._gravityForceNode = None + DistCogdoLevelGame.exitLoaded(self) + return + + def toCraneMode(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.setState('crane') + + def enterVisible(self): + DistCogdoLevelGame.enterVisible(self) + self.geomRoot.reparentTo(render) + + def placeEntranceElev(self, elev): + elev.setPos(-10.63, -113.64, 6.03) + elev.setHpr(90, 0, 0) + + def enterGame(self): + DistCogdoLevelGame.enterGame(self) + self._physicsTask = taskMgr.add(self._doPhysics, self.uniqueName('physics'), priority=25) + self.evWalls.stash() + self._startTimer() + + def _startTimer(self): + timeLeft = GameConsts.Settings.GameDuration.get() - self.getCurrentGameTime() + self.timer.posInTopRightCorner() + self.timer.setTime(timeLeft) + self.timer.countdown(timeLeft, self.timerExpired) + self.timer.unstash() + + def _doPhysics(self, task): + dt = globalClock.getDt() + self.physicsMgr.doPhysics(dt) + return Task.cont + + def exitGame(self): + DistCogdoLevelGame.exitGame(self) + self._physicsTask.remove() + + def enterFinish(self): + DistCogdoLevelGame.enterFinish(self) + timeLeft = 10 - (globalClock.getRealTime() - self.getFinishTime()) + self.timer.setTime(timeLeft) + self.timer.countdown(timeLeft, self.timerExpired) + self.timer.unstash() + + def timerExpired(self): + pass diff --git a/toontown/cogdominium/DistCogdoCraneGameAI.py b/toontown/cogdominium/DistCogdoCraneGameAI.py new file mode 100755 index 00000000..5486b268 --- /dev/null +++ b/toontown/cogdominium/DistCogdoCraneGameAI.py @@ -0,0 +1,61 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from toontown.cogdominium.DistCogdoLevelGameAI import DistCogdoLevelGameAI +from toontown.cogdominium.DistCogdoCraneAI import DistCogdoCraneAI +from toontown.cogdominium import CogdoCraneGameConsts as GameConsts +from toontown.cogdominium.CogdoCraneGameBase import CogdoCraneGameBase + +class DistCogdoCraneGameAI(DistCogdoLevelGameAI, CogdoCraneGameBase): + notify = directNotify.newCategory('DistCogdoCraneGameAI') + + def __init__(self, air, interior): + DistCogdoLevelGameAI.__init__(self, air, interior) + self._cranes = [ + None] * self.MaxPlayers + + def enterLoaded(self): + DistCogdoLevelGameAI.enterLoaded(self) + for i in xrange(self.MaxPlayers): + crane = DistCogdoCraneAI(self.air, self, i) + crane.generateWithRequired(self.zoneId) + self._cranes[i] = crane + + def exitLoaded(self): + for i in xrange(self.MaxPlayers): + if self._cranes[i]: + self._cranes[i].requestDelete() + self._cranes[i] = None + continue + DistCogdoLevelGameAI.exitLoaded(self) + + def enterGame(self): + DistCogdoLevelGameAI.enterGame(self) + for i in xrange(self.getNumPlayers()): + self._cranes[i].request('Controlled', self.getToonIds()[i]) + self._scheduleGameDone() + + def _scheduleGameDone(self): + timeLeft = GameConsts.Settings.GameDuration.get() - globalClock.getRealTime() - self.getStartTime() + if timeLeft > 0: + self._gameDoneEvent = taskMgr.doMethodLater(timeLeft, self._gameDoneDL, self.uniqueName('boardroomGameDone')) + else: + self._gameDoneDL() + + def exitGame(self): + taskMgr.remove(self._gameDoneEvent) + self._gameDoneEvent = None + + def _gameDoneDL(self, task = None): + self._handleGameFinished() + return task.done + + def enterFinish(self): + DistCogdoLevelGameAI.enterFinish(self) + self._finishDoneEvent = taskMgr.doMethodLater(10.0, self._finishDoneDL, self.uniqueName('boardroomFinishDone')) + + def exitFinish(self): + taskMgr.remove(self._finishDoneEvent) + self._finishDoneEvent = None + + def _finishDoneDL(self, task): + self.announceGameDone() + return task.done diff --git a/toontown/cogdominium/DistCogdoCraneMoneyBag.py b/toontown/cogdominium/DistCogdoCraneMoneyBag.py new file mode 100755 index 00000000..ecff3161 --- /dev/null +++ b/toontown/cogdominium/DistCogdoCraneMoneyBag.py @@ -0,0 +1,83 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from toontown.cogdominium.DistCogdoCraneObject import DistCogdoCraneObject +from toontown.cogdominium import CogdoCraneGameConsts as GameConsts + +class DistCogdoCraneMoneyBag(DistCogdoCraneObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistCogdoCraneMoneyBag') + grabPos = (0, 0, GameConsts.Settings.MoneyBagGrabHeight.get()) + craneFrictionCoef = 0.2 + craneSlideSpeed = 11 + craneRotateSpeed = 16 + wantsWatchDrift = 0 + + def __init__(self, cr): + DistCogdoCraneObject.__init__(self, cr) + NodePath.__init__(self, 'object') + self.index = None + self.flyToMagnetSfx = loader.loadSfx('phase_5/audio/sfx/TL_rake_throw_only.ogg') + self.hitMagnetSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_safe.ogg') + self.toMagnetSoundInterval = Parallel(SoundInterval(self.flyToMagnetSfx, duration=ToontownGlobals.CashbotBossToMagnetTime, node=self), Sequence(Wait(ToontownGlobals.CashbotBossToMagnetTime - 0.02), SoundInterval(self.hitMagnetSfx, duration=1.0, node=self))) + self.hitFloorSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_bigweight_miss.ogg') + self.hitFloorSoundInterval = SoundInterval(self.hitFloorSfx, node=self) + return + + def announceGenerate(self): + DistCogdoCraneObject.announceGenerate(self) + self.name = 'moneyBag-%s' % self.doId + self.setName(self.name) + self.craneGame.moneyBag.copyTo(self) + self.shadow = NodePath('notAShadow') + self.collisionNode.setName('moneyBag') + cs = CollisionSphere(0, 0, 4, 4) + self.collisionNode.addSolid(cs) + self.craneGame.moneyBags[self.index] = self + self.setupPhysics('moneyBag') + self.resetToInitialPosition() + + def disable(self): + del self.craneGame.moneyBags[self.index] + DistCogdoCraneObject.disable(self) + + def hideShadows(self): + self.shadow.hide() + + def showShadows(self): + self.shadow.show() + + def getMinImpact(self): + if self.craneGame.heldObject: + return ToontownGlobals.CashbotBossSafeKnockImpact + else: + return ToontownGlobals.CashbotBossSafeNewImpact + + def resetToInitialPosition(self): + posHpr = GameConsts.MoneyBagPosHprs[self.index] + self.setPosHpr(*posHpr) + self.physicsObject.setVelocity(0, 0, 0) + + def fellOut(self): + self.deactivatePhysics() + self.d_requestInitial() + + def setIndex(self, index): + self.index = index + + def setObjectState(self, state, avId, craneId): + if state == 'I': + self.demand('Initial') + else: + DistCogdoCraneObject.setObjectState(self, state, avId, craneId) + + def d_requestInitial(self): + self.sendUpdate('requestInitial') + + def enterInitial(self): + self.resetToInitialPosition() + self.showShadows() + + def exitInitial(self): + pass diff --git a/toontown/cogdominium/DistCogdoCraneMoneyBagAI.py b/toontown/cogdominium/DistCogdoCraneMoneyBagAI.py new file mode 100755 index 00000000..a2bd3049 --- /dev/null +++ b/toontown/cogdominium/DistCogdoCraneMoneyBagAI.py @@ -0,0 +1,11 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI + +class DistCogdoCraneMoneyBagAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistCogdoCraneMoneyBagAI") + + def setIndex(self, todo0): + pass + + def requestInitial(self): + pass diff --git a/toontown/cogdominium/DistCogdoCraneObject.py b/toontown/cogdominium/DistCogdoCraneObject.py new file mode 100755 index 00000000..7d7b2f5a --- /dev/null +++ b/toontown/cogdominium/DistCogdoCraneObject.py @@ -0,0 +1,322 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedSmoothNode +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from direct.fsm import FSM +from direct.task import Task +smileyDoId = 1 + +class DistCogdoCraneObject(DistributedSmoothNode.DistributedSmoothNode, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistCogdoCraneObject') + wantsWatchDrift = 1 + + def __init__(self, cr): + DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr) + FSM.FSM.__init__(self, 'DistCogdoCraneObject') + self.craneGame = None + self.avId = 0 + self.craneId = 0 + self.cleanedUp = 0 + self.collisionNode = CollisionNode('object') + self.collisionNode.setIntoCollideMask(ToontownGlobals.PieBitmask | OTPGlobals.WallBitmask | ToontownGlobals.CashbotBossObjectBitmask | OTPGlobals.CameraBitmask) + self.collisionNode.setFromCollideMask(ToontownGlobals.PieBitmask | OTPGlobals.FloorBitmask) + self.collisionNodePath = NodePath(self.collisionNode) + self.physicsActivated = 0 + self.toMagnetSoundInterval = Sequence() + self.hitFloorSoundInterval = Sequence() + self.hitBossSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_safe_miss.ogg') + self.hitBossSoundInterval = SoundInterval(self.hitBossSfx) + self.touchedBossSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_sandbag.ogg') + self.touchedBossSoundInterval = SoundInterval(self.touchedBossSfx, duration=0.8) + self.lerpInterval = None + return + + def disable(self): + self.cleanup() + self.stopSmooth() + DistributedSmoothNode.DistributedSmoothNode.disable(self) + + def cleanup(self): + if self.cleanedUp: + return + else: + self.cleanedUp = 1 + self.demand('Off') + self.detachNode() + self.toMagnetSoundInterval.finish() + self.hitFloorSoundInterval.finish() + self.hitBossSoundInterval.finish() + self.touchedBossSoundInterval.finish() + del self.toMagnetSoundInterval + del self.hitFloorSoundInterval + del self.hitBossSoundInterval + del self.touchedBossSoundInterval + self.craneGame = None + return + + def setupPhysics(self, name): + an = ActorNode('%s-%s' % (name, self.doId)) + anp = NodePath(an) + if not self.isEmpty(): + self.reparentTo(anp) + NodePath.assign(self, anp) + self.physicsObject = an.getPhysicsObject() + self.setTag('object', str(self.doId)) + self.collisionNodePath.reparentTo(self) + self.handler = PhysicsCollisionHandler() + self.handler.addCollider(self.collisionNodePath, self) + self.collideName = self.uniqueName('collide') + self.handler.addInPattern(self.collideName + '-%in') + self.handler.addAgainPattern(self.collideName + '-%in') + self.watchDriftName = self.uniqueName('watchDrift') + + def activatePhysics(self): + if not self.physicsActivated: + self.craneGame.physicsMgr.attachPhysicalNode(self.node()) + base.cTrav.addCollider(self.collisionNodePath, self.handler) + self.physicsActivated = 1 + self.accept(self.collideName + '-floor', self.__hitFloor) + self.accept(self.collideName + '-goon', self.__hitGoon) + self.acceptOnce(self.collideName + '-headTarget', self.__hitBoss) + self.accept(self.collideName + '-dropPlane', self.__hitDropPlane) + + def deactivatePhysics(self): + if self.physicsActivated: + self.craneGame.physicsMgr.removePhysicalNode(self.node()) + base.cTrav.removeCollider(self.collisionNodePath) + self.physicsActivated = 0 + self.ignore(self.collideName + '-floor') + self.ignore(self.collideName + '-goon') + self.ignore(self.collideName + '-headTarget') + self.ignore(self.collideName + '-dropPlane') + + def hideShadows(self): + pass + + def showShadows(self): + pass + + def stashCollisions(self): + self.collisionNodePath.stash() + + def unstashCollisions(self): + self.collisionNodePath.unstash() + + def __hitFloor(self, entry): + if self.state == 'Dropped' or self.state == 'LocalDropped': + self.d_hitFloor() + self.demand('SlidingFloor', localAvatar.doId) + + def __hitGoon(self, entry): + if self.state == 'Dropped' or self.state == 'LocalDropped': + goonId = int(entry.getIntoNodePath().getNetTag('doId')) + goon = self.cr.doId2do.get(goonId) + if goon: + self.doHitGoon(goon) + + def doHitGoon(self, goon): + pass + + def __hitBoss(self, entry): + if (self.state == 'Dropped' or self.state == 'LocalDropped') and self.craneId != self.craneGame.doId: + vel = self.physicsObject.getVelocity() + vel = self.crane.root.getRelativeVector(render, vel) + vel.normalize() + impact = vel[1] + if impact >= self.getMinImpact(): + print 'hit! %s' % impact + self.hitBossSoundInterval.start() + self.doHitBoss(impact) + else: + self.touchedBossSoundInterval.start() + print '--not hard enough: %s' % impact + + def doHitBoss(self, impact): + self.d_hitBoss(impact) + + def __hitDropPlane(self, entry): + self.notify.info('%s fell out of the world.' % self.doId) + self.fellOut() + + def fellOut(self): + raise StandardError, 'fellOut unimplented' + + def getMinImpact(self): + return 0 + + def __watchDrift(self, task): + v = self.physicsObject.getVelocity() + if abs(v[0]) < 0.0001 and abs(v[1]) < 0.0001: + self.d_requestFree() + self.demand('Free') + return Task.cont + + def prepareGrab(self): + pass + + def prepareRelease(self): + pass + + def setCraneGameId(self, craneGameId): + self.craneGameId = craneGameId + self.craneGame = base.cr.doId2do[craneGameId] + + def setObjectState(self, state, avId, craneId): + if state == 'G': + self.demand('Grabbed', avId, craneId) + elif state == 'D': + if self.state != 'Dropped': + self.demand('Dropped', avId, craneId) + elif state == 's': + if self.state != 'SlidingFloor': + self.demand('SlidingFloor', avId) + elif state == 'F': + self.demand('Free') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def d_requestGrab(self): + self.sendUpdate('requestGrab') + + def rejectGrab(self): + if self.state == 'LocalGrabbed': + self.demand('LocalDropped', self.avId, self.craneId) + + def d_requestDrop(self): + self.sendUpdate('requestDrop') + + def d_hitFloor(self): + self.sendUpdate('hitFloor') + + def d_requestFree(self): + self.sendUpdate('requestFree', [self.getX(), + self.getY(), + self.getZ(), + self.getH()]) + + def d_hitBoss(self, impact): + self.sendUpdate('hitBoss', [impact]) + + def defaultFilter(self, request, args): + if self.craneGame == None: + raise FSM.RequestDenied, request + return FSM.FSM.defaultFilter(self, request, args) + + def enterOff(self): + self.detachNode() + if self.lerpInterval: + self.lerpInterval.finish() + self.lerpInterval = None + return + + def exitOff(self): + self.reparentTo(render) + + def enterLocalGrabbed(self, avId, craneId): + self.avId = avId + self.craneId = craneId + self.crane = self.cr.doId2do.get(craneId) + self.hideShadows() + self.prepareGrab() + self.crane.grabObject(self) + + def exitLocalGrabbed(self): + if self.newState != 'Grabbed': + self.crane.dropObject(self) + self.prepareRelease() + del self.crane + self.showShadows() + + def enterGrabbed(self, avId, craneId): + if self.oldState == 'LocalGrabbed': + if craneId == self.craneId: + return + else: + self.crane.dropObject(self) + self.prepareRelease() + self.avId = avId + self.craneId = craneId + self.crane = self.cr.doId2do.get(craneId) + self.hideShadows() + self.prepareGrab() + self.crane.grabObject(self) + + def exitGrabbed(self): + self.crane.dropObject(self) + self.prepareRelease() + self.showShadows() + del self.crane + + def enterLocalDropped(self, avId, craneId): + self.avId = avId + self.craneId = craneId + self.crane = self.cr.doId2do.get(craneId) + self.activatePhysics() + self.startPosHprBroadcast() + self.hideShadows() + self.handler.setStaticFrictionCoef(0) + self.handler.setDynamicFrictionCoef(0) + + def exitLocalDropped(self): + if self.newState != 'SlidingFloor' and self.newState != 'Dropped': + self.deactivatePhysics() + self.stopPosHprBroadcast() + del self.crane + self.showShadows() + + def enterDropped(self, avId, craneId): + self.avId = avId + self.craneId = craneId + self.crane = self.cr.doId2do.get(craneId) + if self.avId == base.localAvatar.doId: + self.activatePhysics() + self.startPosHprBroadcast() + self.handler.setStaticFrictionCoef(0) + self.handler.setDynamicFrictionCoef(0) + else: + self.startSmooth() + self.hideShadows() + + def exitDropped(self): + if self.avId == base.localAvatar.doId: + if self.newState != 'SlidingFloor': + self.deactivatePhysics() + self.stopPosHprBroadcast() + else: + self.stopSmooth() + del self.crane + self.showShadows() + + def enterSlidingFloor(self, avId): + self.avId = avId + if self.lerpInterval: + self.lerpInterval.finish() + self.lerpInterval = None + if self.avId == base.localAvatar.doId: + self.activatePhysics() + self.startPosHprBroadcast() + self.handler.setStaticFrictionCoef(0.9) + self.handler.setDynamicFrictionCoef(0.5) + if self.wantsWatchDrift: + taskMgr.add(self.__watchDrift, self.watchDriftName) + else: + self.startSmooth() + self.hitFloorSoundInterval.start() + return + + def exitSlidingFloor(self): + if self.avId == base.localAvatar.doId: + taskMgr.remove(self.watchDriftName) + self.deactivatePhysics() + self.stopPosHprBroadcast() + else: + self.stopSmooth() + + def enterFree(self): + self.avId = 0 + self.craneId = 0 + + def exitFree(self): + pass diff --git a/toontown/cogdominium/DistCogdoFlyingGame.py b/toontown/cogdominium/DistCogdoFlyingGame.py new file mode 100755 index 00000000..475d8ed7 --- /dev/null +++ b/toontown/cogdominium/DistCogdoFlyingGame.py @@ -0,0 +1,153 @@ +from direct.distributed.ClockDelta import globalClockDelta +from toontown.toonbase import TTLocalizer +from CogdoFlyingGame import CogdoFlyingGame +from DistCogdoGame import DistCogdoGame +import CogdoFlyingGameGlobals +import CogdoFlyingGameGlobals as Globals + +class DistCogdoFlyingGame(DistCogdoGame): + notify = directNotify.newCategory('DistCogdoFlyingGame') + + def __init__(self, cr): + DistCogdoGame.__init__(self, cr) + self.game = CogdoFlyingGame(self) + + def delete(self): + del self.game + DistCogdoGame.delete(self) + + def getTitle(self): + return TTLocalizer.CogdoFlyingGameTitle + + def getInstructions(self): + return TTLocalizer.CogdoFlyingGameInstructions + + def placeEntranceElev(self, elev): + self.game.placeEntranceElevator(elev) + + def d_sendRequestAction(self, action, data): + self.sendUpdate('requestAction', [action, data]) + + def doAction(self, action, data): + messenger.send(self.getRemoteActionEventName(), [action, data]) + + def toonSetAsEagleTarget(self, toonId, eagleId, networkTime): + self.notify.debugCall() + elapsedTime = globalClockDelta.localElapsedTime(networkTime) + self.game.toonSetAsEagleTarget(toonId, eagleId, elapsedTime) + + def toonClearAsEagleTarget(self, toonId, eagleId, networkTime): + self.notify.debugCall() + elapsedTime = globalClockDelta.localElapsedTime(networkTime) + self.game.toonClearAsEagleTarget(toonId, eagleId, elapsedTime) + + def eagleExitCooldown(self, eagleId, networkTime): + self.notify.debugCall() + elapsedTime = globalClockDelta.localElapsedTime(networkTime) + self.game.eagleExitCooldown(eagleId, elapsedTime) + + def debuffPowerup(self, toonId, pickupType, networkTime): + elapsedTime = globalClockDelta.localElapsedTime(networkTime) + self.game.debuffPowerup(toonId, pickupType, elapsedTime) + + def toonDied(self, toonId, networkTime): + if toonId != base.localAvatar.doId: + elapsedTime = globalClockDelta.localElapsedTime(networkTime) + self.game.toonDied(toonId, elapsedTime) + + def b_toonDied(self, toonId): + self.game.toonDied(toonId, 0) + self.d_sendRequestAction(Globals.AI.GameActions.Died, 0) + + def toonSpawn(self, toonId, networkTime): + if toonId != base.localAvatar.doId: + elapsedTime = globalClockDelta.localElapsedTime(networkTime) + self.game.toonSpawn(toonId, elapsedTime) + + def b_toonSpawn(self, toonId): + self.game.toonSpawn(toonId, 0) + self.d_sendRequestAction(Globals.AI.GameActions.Spawn, 0) + + def pickUp(self, toonId, pickupNum, networkTime): + if not self.getToon(base.localAvatar.doId): + return + elapsedTime = globalClockDelta.localElapsedTime(networkTime) + self.game.pickUp(toonId, pickupNum, elapsedTime) + + def d_sendRequestPickup(self, pickupNum, pickupType): + self.sendUpdate('requestPickUp', [pickupNum, pickupType]) + + def toonSetBlades(self, toonId, fuelState): + if toonId != base.localAvatar.doId: + self.game.toonSetBlades(toonId, fuelState) + + def b_toonSetBlades(self, toonId, fuelState): + self.game.toonSetBlades(toonId, fuelState) + self.d_sendRequestAction(Globals.AI.GameActions.SetBlades, fuelState) + + def toonBladeLost(self, toonId): + if toonId != base.localAvatar.doId: + self.game.toonBladeLost(toonId) + + def b_toonBladeLost(self, toonId): + self.game.toonBladeLost(toonId) + self.d_sendRequestAction(Globals.AI.GameActions.BladeLost, 0) + + def getRemoteActionEventName(self): + return self._remoteActionEventName + + def setToonSad(self, toonId): + self.game.setToonSad(toonId) + DistCogdoGame.setToonSad(self, toonId) + + def setToonDisconnect(self, toonId): + self.game.setToonDisconnect(toonId) + DistCogdoGame.setToonDisconnect(self, toonId) + + def __handleUnexpectedExit(self, toonId): + self.notify.warning('%s: unexpected exit for %s' % (self.doId, toonId)) + self.game.removePlayer(toonId) + + def enterLoaded(self): + DistCogdoGame.enterLoaded(self) + self._remoteActionEventName = self.uniqueName('doAction') + self.game.load() + self.game.initPlayers() + + def exitLoaded(self): + self.ignoreAll() + self.game.unload() + DistCogdoGame.exitLoaded(self) + + def enterVisible(self): + DistCogdoGame.enterVisible(self) + self.game.onstage() + + def exitVisible(self): + DistCogdoGame.exitVisible(self) + + def enterIntro(self): + DistCogdoGame.enterIntro(self, Globals.Gameplay.IntroDurationSeconds) + self.game.startIntro() + + def exitIntro(self): + DistCogdoGame.exitIntro(self) + self.game.endIntro() + self.stashEntranceElevator() + + def enterGame(self): + DistCogdoGame.enterGame(self) + self.game.start() + + def exitGame(self): + self.game.exit() + DistCogdoGame.exitGame(self) + + def enterFinish(self): + DistCogdoGame.enterFinish(self) + self.game.startFinish() + + def exitFinish(self): + self.game.endFinish() + self.game.offstage() + DistCogdoGame.exitFinish(self) diff --git a/toontown/cogdominium/DistCogdoFlyingGameAI.py b/toontown/cogdominium/DistCogdoFlyingGameAI.py new file mode 100755 index 00000000..500004b8 --- /dev/null +++ b/toontown/cogdominium/DistCogdoFlyingGameAI.py @@ -0,0 +1,117 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from toontown.battle import BattleBase +from toontown.building.ElevatorConstants import * +from toontown.cogdominium.DistCogdoGameAI import DistCogdoGameAI +import CogdoFlyingGameGlobals as Globals + +class DistCogdoFlyingGameAI(DistCogdoGameAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistCogdoFlyingGameAI") + delayIntro = BattleBase.ELEVATOR_T + ElevatorData[ELEVATOR_FIELD]['openTime'] + + def __init__(self, air): + DistCogdoGameAI.__init__(self, air) + self.completed = [] + self.eagles = {} + self.totalMemos = 0 + + def requestAction(self, action, data): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if not av: + return + + if action == Globals.AI.GameActions.LandOnWinPlatform: + self.completed.append(avId) + for toon in self.toons: + if toon not in self.completed: + return + self.gameDone() + + elif action == Globals.AI.GameActions.BladeLost: + self.sendUpdate("toonBladeLost", [avId]) + elif action == Globals.AI.GameActions.SetBlades: + self.sendUpdate("toonSetBlades", [avId, data]) + elif action == Globals.AI.GameActions.Died: + damage = Globals.AI.SafezoneId2DeathDamage[self.getSafezoneId()] + self.__damage(av, damage) + self.sendUpdate("toonDied", [avId, globalClockDelta.getRealNetworkTime()]) + elif action == Globals.AI.GameActions.Spawn: + self.sendUpdate("toonSpawn", [avId, globalClockDelta.getRealNetworkTime()]) + elif action == Globals.AI.GameActions.RequestEnterEagleInterest: + if not self.eagles.get(data): + self.eagles[data] = avId + self.sendUpdate("toonSetAsEagleTarget", [avId, data, globalClockDelta.getRealNetworkTime()]) + elif action == Globals.AI.GameActions.RequestExitEagleInterest: + if self.eagles.get(data) == avId: + self.eagles[data] = 0 + self.sendUpdate("toonClearAsEagleTarget", [avId, data, globalClockDelta.getRealNetworkTime()]) + elif action == Globals.AI.GameActions.HitLegalEagle: + damage = Globals.AI.SafezoneId2LegalEagleDamage[self.getSafezoneId()] + self.__damage(av, damage) + elif action == Globals.AI.GameActions.HitMinion: + damage = Globals.AI.SafezoneId2MinionDamage[self.getSafezoneId()] + self.__damage(av, damage) + elif action == Globals.AI.GameActions.HitWhirlwind: + damage = Globals.AI.SafezoneId2WhirlwindDamage[self.getSafezoneId()] + self.__damage(av, damage) + elif action == Globals.AI.GameActions.RanOutOfTimePenalty: + damage = int(20 * self.getDifficulty()) + self.__damage(av, damage) + else: + self.notify.warning('Client requested unknown action \'%s\'' %action) + + def requestPickUp(self, pickupNum, pickupType): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if not av: + return + + if pickupType <= len(Globals.Level.GatherableTypes): + self.sendUpdate("pickUp", [avId, pickupNum, globalClockDelta.getRealNetworkTime()]) + if pickupType == Globals.Level.GatherableTypes.LaffPowerup: + av.toonUp(int(27 * self.getDifficulty()) + 3) + if pickupType == Globals.Level.GatherableTypes.Memo: + self.totalMemos += 1 + else: + self.notify.warning('Client requested unknown pickup \'%s\'' % pickupType) + + def handleStart(self): + for toon in self.toons: + self.acceptOnce(self.air.getAvatarExitEvent(toon), self.__handleAvExit, [toon]) + + def __handleAvExit(self, toon): + if self.air: + if toon in self.toons: + self.toons.remove(toon) + self.ignore(self.air.getAvatarExitEvent(toon)) + if not self.toons: + self.gameDone(failed=True) + + def requestDelete(self): + DistCogdoGameAI.requestDelete(self) + self.ignoreAll() + + def __removeToon(self, avId): + if avId not in self.toons: + return + + self.toons.pop(self.toons.index(avId)) + if len(self.toons) == 0: + self.gameDone(failed=True) + + def __damage(self, av, damage): + av.takeDamage(damage) + if av.getHp() < 1: + self.__removeToon(av.doId) + + def getTotalMemos(self): + return self.totalMemos + +from otp.ai.MagicWordGlobal import * +@magicWord(category=CATEGORY_PROGRAMMER) +def endFly(): + if hasattr(simbase.air, 'cogdoGame'): + game = simbase.air.cogdoGame + game.requestAction(Globals.AI.GameActions.LandOnWinPlatform, 0) + return 'Finished cogdo flying game!' diff --git a/toontown/cogdominium/DistCogdoGame.py b/toontown/cogdominium/DistCogdoGame.py new file mode 100755 index 00000000..20bb2eee --- /dev/null +++ b/toontown/cogdominium/DistCogdoGame.py @@ -0,0 +1,255 @@ +from pandac.PandaModules import VBase4 +from direct.gui.DirectGui import DirectLabel +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.distributed.ClockDelta import globalClockDelta +from direct.distributed.DistributedObject import DistributedObject +from direct.fsm import ClassicFSM, State +from direct.fsm.StatePush import StateVar, FunctionCall +from toontown.cogdominium import CogdoGameConsts +from toontown.cogdominium.DistCogdoGameBase import DistCogdoGameBase +from toontown.minigame.MinigameRulesPanel import MinigameRulesPanel +from toontown.cogdominium.CogdoGameRulesPanel import CogdoGameRulesPanel +from toontown.minigame import MinigameGlobals +from toontown.toonbase import TTLocalizer as TTL + +class DistCogdoGame(DistCogdoGameBase, DistributedObject): + notify = directNotify.newCategory('DistCogdoGame') + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + base.cogdoGame = self + cr.cogdoGame = self + self._waitingStartLabel = DirectLabel(text=TTL.MinigameWaitingForOtherToons, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075) + self._waitingStartLabel.hide() + self.loadFSM = ClassicFSM.ClassicFSM('DistCogdoGame.loaded', [State.State('NotLoaded', self.enterNotLoaded, self.exitNotLoaded, ['Loaded']), State.State('Loaded', self.enterLoaded, self.exitLoaded, ['NotLoaded'])], 'NotLoaded', 'NotLoaded') + self.loadFSM.enterInitialState() + self.fsm = ClassicFSM.ClassicFSM('DistCogdoGame', [State.State('Visible', self.enterVisible, self.exitVisible, ['Intro']), + State.State('Intro', self.enterIntro, self.exitIntro, ['WaitServerStart']), + State.State('WaitServerStart', self.enterWaitServerStart, self.exitWaitServerStart, ['Game']), + State.State('Game', self.enterGame, self.exitGame, ['Finish']), + State.State('Finish', self.enterFinish, self.exitFinish, ['Off']), + State.State('Off', self.enterOff, self.exitOff, ['Visible'])], 'Off', 'Off') + self.fsm.enterInitialState() + self.difficultyOverride = None + self.exteriorZoneOverride = None + self._gotInterior = StateVar(False) + self._toonsInEntranceElev = StateVar(False) + self._wantStashElevator = StateVar(False) + self._stashElevatorFC = FunctionCall(self._doStashElevator, self._toonsInEntranceElev, self._gotInterior, self._wantStashElevator) + return + + def getTitle(self): + pass + + def getInstructions(self): + pass + + def setInteriorId(self, interiorId): + self._interiorId = interiorId + + def setExteriorZone(self, exteriorZone): + self.exteriorZone = exteriorZone + + def setDifficultyOverrides(self, difficultyOverride, exteriorZoneOverride): + if difficultyOverride != CogdoGameConsts.NoDifficultyOverride: + self.difficultyOverride = difficultyOverride / float(CogdoGameConsts.DifficultyOverrideMult) + if exteriorZoneOverride != CogdoGameConsts.NoExteriorZoneOverride: + self.exteriorZoneOverride = exteriorZoneOverride + + def getInterior(self): + return self.cr.getDo(self._interiorId) + + def getEntranceElevator(self, callback): + return self.getInterior().getEntranceElevator(callback) + + def getToonIds(self): + interior = self.getInterior() + if interior is not None: + return interior.getToonIds() + else: + return [] + return + + def getToon(self, toonId): + if toonId in self.cr.doId2do: + return self.cr.doId2do[toonId] + else: + return None + return None + + def getNumPlayers(self): + return len(self.getToonIds()) + + def isSinglePlayer(self): + if self.getNumPlayers() == 1: + return 1 + else: + return 0 + + def announceGenerate(self): + DistributedObject.announceGenerate(self) + self.loadFSM.request('Loaded') + self._requestInterior() + self.notify.info('difficulty: %s, safezoneId: %s' % (self.getDifficulty(), self.getSafezoneId())) + + def _requestInterior(self): + self.cr.relatedObjectMgr.requestObjects([self._interiorId], allCallback=self._handleGotInterior) + + def _handleGotInterior(self, objs): + self._gotInterior.set(True) + self.getEntranceElevator(self.placeEntranceElev) + + def stashEntranceElevator(self): + self._wantStashElevator.set(True) + + def placeEntranceElev(self, elev): + pass + + def _doStashElevator(self, toonsInEntranceElev, gotInterior, wantStashElevator): + if gotInterior: + interior = self.getInterior() + if interior: + if not toonsInEntranceElev and wantStashElevator: + interior.stashElevatorIn() + else: + interior.stashElevatorIn(False) + + def disable(self): + base.cogdoGame = None + self.cr.cogdoGame = None + self.fsm.requestFinalState() + self.loadFSM.requestFinalState() + self.fsm = None + self.loadFSM = None + DistributedObject.disable(self) + return + + def delete(self): + self._stashElevatorFC.destroy() + self._wantStashElevator.destroy() + self._toonsInEntranceElev.destroy() + self._gotInterior.destroy() + self._waitingStartLabel.destroy() + self._waitingStartLabel = None + DistributedObject.delete(self) + return + + def getDifficulty(self): + if self.difficultyOverride is not None: + return self.difficultyOverride + if hasattr(base, 'cogdoGameDifficulty'): + return float(base.cogdoGameDifficulty) + return CogdoGameConsts.getDifficulty(self.getSafezoneId()) + + def getSafezoneId(self): + if self.exteriorZoneOverride is not None: + return self.exteriorZoneOverride + if hasattr(base, 'cogdoGameSafezoneId'): + return CogdoGameConsts.getSafezoneId(base.cogdoGameSafezoneId) + return CogdoGameConsts.getSafezoneId(self.exteriorZone) + + def enterNotLoaded(self): + pass + + def exitNotLoaded(self): + pass + + def enterLoaded(self): + pass + + def exitLoaded(self): + pass + + def enterOff(self): + pass + + def exitOff(self): + pass + + def setVisible(self): + self.fsm.request('Visible') + + def setIntroStart(self): + self.fsm.request('Intro') + + def enterVisible(self): + self._toonsInEntranceElev.set(True) + + def exitVisible(self): + pass + + def enterIntro(self, duration = MinigameGlobals.rulesDuration): + base.cr.playGame.getPlace().fsm.request('Game') + self._rulesDoneEvent = self.uniqueName('cogdoGameRulesDone') + self.accept(self._rulesDoneEvent, self._handleRulesDone) + self._rulesPanel = CogdoGameRulesPanel('CogdoGameRulesPanel', self.getTitle(), '', self._rulesDoneEvent, timeout=duration) + self._rulesPanel.load() + self._rulesPanel.enter() + + def exitIntro(self): + self._toonsInEntranceElev.set(False) + self.ignore(self._rulesDoneEvent) + if self._rulesPanel: + self._rulesPanel.exit() + self._rulesPanel.unload() + self._rulesPanel = None + return + + def _handleRulesDone(self): + self.ignore(self._rulesDoneEvent) + self._rulesPanel.exit() + self._rulesPanel.unload() + self._rulesPanel = None + self.fsm.request('WaitServerStart') + self.d_setAvatarReady() + return + + def d_setAvatarReady(self): + self.sendUpdate('setAvatarReady', []) + + def enterWaitServerStart(self): + numToons = 1 + interior = self.getInterior() + if interior: + numToons = len(interior.getToonIds()) + if numToons > 1: + msg = TTL.MinigameWaitingForOtherToons + else: + msg = TTL.MinigamePleaseWait + self._waitingStartLabel['text'] = msg + self._waitingStartLabel.show() + + def exitWaitServerStart(self): + self._waitingStartLabel.hide() + + def setGameStart(self, timestamp): + self._startTime = globalClockDelta.networkToLocalTime(timestamp) + self.fsm.request('Game') + + def getStartTime(self): + return self._startTime + + def enterGame(self): + pass + + def exitGame(self): + pass + + def setGameFinish(self, timestamp): + self._finishTime = globalClockDelta.networkToLocalTime(timestamp) + self.fsm.request('Finish') + + def getFinishTime(self): + return self._finishTime + + def enterFinish(self): + pass + + def exitFinish(self): + pass + + def setToonSad(self, toonId): + pass + + def setToonDisconnect(self, toonId): + pass diff --git a/toontown/cogdominium/DistCogdoGameAI.py b/toontown/cogdominium/DistCogdoGameAI.py new file mode 100755 index 00000000..bc6a38d7 --- /dev/null +++ b/toontown/cogdominium/DistCogdoGameAI.py @@ -0,0 +1,105 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.distributed.ClockDelta import * +import CogdoGameConsts + +class DistCogdoGameAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistCogdoGameAI") + delayIntro = .1 + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + air.cogdoGame = self + self.interiorId = 0 + self.exteriorZone = 0 + self.difficultyOverrides = [2147483647, -1] + self.requests = {} + self.toons = [] + + def announceGenerate(self): + DistributedObjectAI.announceGenerate(self) + self.finishEvent = self.uniqueName('CogdoMazeGameDone') + self.gameOverEvent = self.uniqueName('CogdoMazeGameLose') + self.resetRequests() + + def d_startIntro(self): + self.sendUpdate('setVisible', []) + taskMgr.doMethodLater(self.delayIntro, self.__startIntro, self.taskName('CogdoStartIntro')) + + def getInterior(self): + return self.air.doId2do.get(self.interiorId) + + def resetRequests(self): + interior = self.getInterior() + toons = interior.getToons()[0] + for toon in toons: + self.requests[toon] = 0 + + def __startIntro(self, task = None): + self.sendUpdate('setIntroStart', []) + if task: + return task.done + + def setAvatarReady(self): + avId = self.air.getAvatarIdFromSender() + self.requests[avId] = 1 + if not avId in self.toons: self.toons.append(avId) + if self.allToonsReady(): + self.handleStart() + self.sendUpdate('setGameStart', [globalClockDelta.getRealNetworkTime()]) + + def allToonsReady(self): + interior = self.getInterior() + toons = interior.getToons()[0] + for toon in toons: + if self.requests.get(toon, 0) == 0: + return 0 + return 1 + + def handleStart(self): + pass + + def setInteriorId(self, id): + self.interiorId = id + + def getInteriorId(self): + return self.interiorId + + def setExteriorZone(self, zone): + self.exteriorZone = zone + + def getExteriorZone(self): + return self.exteriorZone + + def setDifficultyOverrides(self, difficulty, exteriorDifficulty): + self.difficultyOverrides = [difficulty, exteriorDifficulty] + + def getDifficultyOverrides(self): + return self.difficultyOverrides + + def toonWentSad(self, avId): + self.sendUpdate('setToonSad', [avId]) + + def setToons(self, toons): + self.toons = toons + + def disable(self): + DistributedObjectAI.disable(self) + self.air.cogdoGame = None + del self.air.cogdoGame + + def gameDone(self, failed=False): + if not failed: + if len(self.toons) == 0: + failed = True + + if not failed: + messenger.send(self.finishEvent, [self.toons]) + else: + messenger.send(self.gameOverEvent) + + def getDifficulty(self): + return CogdoGameConsts.getDifficulty(self.getSafezoneId()) + + def getSafezoneId(self): + return CogdoGameConsts.getSafezoneId(self.exteriorZone) diff --git a/toontown/cogdominium/DistCogdoGameBase.py b/toontown/cogdominium/DistCogdoGameBase.py new file mode 100755 index 00000000..d15234af --- /dev/null +++ b/toontown/cogdominium/DistCogdoGameBase.py @@ -0,0 +1,11 @@ + +class DistCogdoGameBase: + + def local2GameTime(self, timestamp): + return timestamp - self._startTime + + def game2LocalTime(self, timestamp): + return timestamp + self._startTime + + def getCurrentGameTime(self): + return self.local2GameTime(globalClock.getFrameTime()) diff --git a/toontown/cogdominium/DistCogdoLevelGame.py b/toontown/cogdominium/DistCogdoLevelGame.py new file mode 100755 index 00000000..7ea6e03f --- /dev/null +++ b/toontown/cogdominium/DistCogdoLevelGame.py @@ -0,0 +1,45 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from otp.level.DistributedLevel import DistributedLevel +from otp.level import LevelConstants +from otp.level.LevelSpec import LevelSpec +from toontown.cogdominium.DistCogdoGame import DistCogdoGame +from toontown.cogdominium.CogdoEntityCreator import CogdoEntityCreator + +class DistCogdoLevelGame(DistCogdoGame, DistributedLevel): + notify = directNotify.newCategory('DistCogdoLevelGame') + + def __init__(self, cr): + DistributedLevel.__init__(self, cr) + DistCogdoGame.__init__(self, cr) + + def generate(self): + DistributedLevel.generate(self) + DistCogdoGame.generate(self) + + def announceGenerate(self): + DistributedLevel.announceGenerate(self) + DistCogdoGame.announceGenerate(self) + + def createEntityCreator(self): + return CogdoEntityCreator(level=self) + + def levelAnnounceGenerate(self): + self.notify.debug('levelAnnounceGenerate') + DistributedLevel.levelAnnounceGenerate(self) + DistributedLevel.initializeLevel(self, LevelSpec(self.getSpec())) + + def initVisibility(self): + levelMgr = self.getEntity(LevelConstants.LevelMgrEntId) + levelMgr.geom.reparentTo(render) + DistributedLevel.initVisibility(self) + + def placeLocalToon(self): + DistributedLevel.placeLocalToon(self, moveLocalAvatar=False) + + def disable(self): + DistCogdoGame.disable(self) + DistributedLevel.disable(self) + + def delete(self): + DistCogdoGame.delete(self) + DistributedLevel.delete(self) diff --git a/toontown/cogdominium/DistCogdoLevelGameAI.py b/toontown/cogdominium/DistCogdoLevelGameAI.py new file mode 100755 index 00000000..faf82237 --- /dev/null +++ b/toontown/cogdominium/DistCogdoLevelGameAI.py @@ -0,0 +1,33 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from otp.level.DistributedLevelAI import DistributedLevelAI +from toontown.cogdominium.DistCogdoGameAI import DistCogdoGameAI +from toontown.cogdominium.CogdoEntityCreatorAI import CogdoEntityCreatorAI + +class DistCogdoLevelGameAI(DistributedLevelAI, DistCogdoGameAI): + notify = directNotify.newCategory('DistCogdoLevelGameAI') + + def __init__(self, air, interior): + DistCogdoGameAI.__init__(self, air, interior) + DistributedLevelAI.__init__(self, air, self.zoneId, 0, self.getToonIds()) + + def createEntityCreator(self): + return CogdoEntityCreatorAI(level=self) + + def generate(self): + self.notify.info('loading spec') + spec = self.getLevelSpec() + if __dev__: + self.notify.info('creating entity type registry') + typeReg = self.getEntityTypeReg() + spec.setEntityTypeReg(typeReg) + DistributedLevelAI.generate(self, spec) + DistCogdoGameAI.generate(self) + self.startHandleEdits() + + def requestDelete(self): + DistCogdoGameAI.requestDelete(self) + + def delete(self): + self.stopHandleEdits() + DistCogdoGameAI.delete(self) + DistributedLevelAI.delete(self, deAllocZone=False) diff --git a/toontown/cogdominium/DistCogdoMazeGame.py b/toontown/cogdominium/DistCogdoMazeGame.py new file mode 100755 index 00000000..0460d5d5 --- /dev/null +++ b/toontown/cogdominium/DistCogdoMazeGame.py @@ -0,0 +1,238 @@ +from direct.distributed.ClockDelta import globalClockDelta +from toontown.toonbase import TTLocalizer +from DistCogdoGame import DistCogdoGame +from toontown.cogdominium.DistCogdoMazeGameBase import DistCogdoMazeGameBase +from CogdoMazeGame import CogdoMazeGame +from CogdoMaze import CogdoMazeFactory +import CogdoMazeGameGlobals +import CogdoMazeGameGlobals as Globals + +class DistCogdoMazeGame(DistCogdoGame, DistCogdoMazeGameBase): + notify = directNotify.newCategory('DistCogdoMazeGame') + + def __init__(self, cr): + DistCogdoGame.__init__(self, cr) + self.game = CogdoMazeGame(self) + self._numSuits = (0, 0, 0) + + def delete(self): + del self.randomNumGen + del self.game + DistCogdoGame.delete(self) + + def getTitle(self): + return TTLocalizer.CogdoMazeGameTitle + + def getInstructions(self): + return TTLocalizer.CogdoMazeGameInstructions + + def generate(self): + self.randomNumGen = self.createRandomNumGen() + DistCogdoGame.generate(self) + + def placeEntranceElev(self, elev): + DistCogdoGame.placeEntranceElev(self, elev) + self.game.placeEntranceElevator(elev) + + def _gameInProgress(self): + return self.fsm.getCurrentState().getName() == 'Game' + + def enterLoaded(self): + DistCogdoGame.enterLoaded(self) + mazeFactory = self.createMazeFactory(self.createRandomNumGen()) + bossCode = None + if self._numSuits[0] > 0: + bossCode = '' + for u in xrange(self._numSuits[0]): + bossCode += '%X' % self.randomNumGen.randint(0, 15) + + self.game.load(mazeFactory, self._numSuits, bossCode) + return + + def exitLoaded(self): + self.game.unload() + self.ignoreAll() + DistCogdoGame.exitLoaded(self) + + def enterVisible(self): + DistCogdoGame.enterVisible(self) + self.game.initPlayers() + self.game.onstage() + + def exitVisible(self): + DistCogdoGame.exitVisible(self) + + def enterIntro(self): + DistCogdoGame.enterIntro(self, Globals.IntroDurationSeconds) + self.game.startIntro() + + def exitIntro(self): + DistCogdoGame.exitIntro(self) + self.game.endIntro() + self.stashEntranceElevator() + + def enterGame(self): + DistCogdoGame.enterGame(self) + self.game.start() + + def exitGame(self): + DistCogdoGame.exitGame(self) + self.game.exit() + + def enterFinish(self): + DistCogdoGame.enterFinish(self) + self.game.startFinish() + + def exitFinish(self): + DistCogdoGame.exitFinish(self) + self.game.endFinish() + self.game.offstage() + + def setNumSuits(self, numSuits): + self._numSuits = numSuits + + def d_sendRequestAction(self, action, data): + self.sendUpdate('requestAction', [action, data]) + + def toonUsedGag(self, toonId, x, y, h, networkTime): + if not self._gameInProgress(): + return + if not self.getToon(base.localAvatar.doId): + return + if toonId == localAvatar.doId: + return + elapsedTime = globalClockDelta.localElapsedTime(networkTime) + self.game.toonUsedGag(toonId, x, y, h, elapsedTime) + + def d_requestUseGag(self, x, y, h): + networkTime = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.sendUpdate('requestUseGag', [x, + y, + h, + networkTime]) + + def b_toonUsedGag(self, x, y, h): + self.d_requestUseGag(x, y, h) + self.game.toonUsedGag(base.localAvatar.doId, x, y, h) + + def suitHitByGag(self, toonId, suitType, suitNum): + if not self._gameInProgress(): + return + if not self.getToon(base.localAvatar.doId): + return + if toonId == localAvatar.doId: + return + self.game.suitHitByGag(toonId, suitType, suitNum) + + def d_requestSuitHitByGag(self, suitType, suitNum): + self.sendUpdate('requestSuitHitByGag', [suitType, suitNum]) + + def b_suitHitByGag(self, suitType, suitNum): + self.d_requestSuitHitByGag(suitType, suitNum) + self.game.suitHitByGag(base.localAvatar.doId, suitType, suitNum) + + def toonHitByGag(self, toonId, hitToon, networkTime): + if not self._gameInProgress(): + return + if not self.getToon(base.localAvatar.doId): + return + if toonId == localAvatar.doId: + return + elapsedTime = globalClockDelta.localElapsedTime(networkTime) + self.game.toonHitByGag(toonId, hitToon, elapsedTime) + + def d_broadcastSendToonHitByGag(self, toonId): + pass + + def b_toonHitByGag(self, toonId): + self.d_broadcastSendToonHitByGag(toonId) + self.game.toonHitByGag(base.localAvatar.doId, toonId) + + def toonHitBySuit(self, toonId, suitType, suitNum, networkTime): + if not self._gameInProgress(): + return + if not self.getToon(base.localAvatar.doId): + return + if toonId == localAvatar.doId: + return + elapsedTime = globalClockDelta.localElapsedTime(networkTime) + self.game.toonHitBySuit(toonId, suitType, suitNum, elapsedTime) + + def d_requestHitBySuit(self, suitType, suitNum): + networkTime = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.sendUpdate('requestHitBySuit', [suitType, suitNum, networkTime]) + + def b_toonHitBySuit(self, suitType, suitNum): + self.d_requestHitBySuit(suitType, suitNum) + self.game.toonHitBySuit(base.localAvatar.doId, suitType, suitNum) + + def toonHitByDrop(self, toonId): + if not self._gameInProgress(): + return + if not self.getToon(base.localAvatar.doId): + return + if toonId == localAvatar.doId: + return + self.game.toonHitByDrop(toonId) + + def d_requestHitByDrop(self): + self.sendUpdate('requestHitByDrop', []) + + def b_toonHitByDrop(self): + self.d_requestHitByDrop() + self.game.toonHitByDrop(base.localAvatar.doId) + + def d_sendRequestPickUp(self, pickupNum): + self.sendUpdate('requestPickUp', [pickupNum]) + + def pickUp(self, toonId, pickupNum, networkTime): + if not self._gameInProgress(): + return + if not self.getToon(base.localAvatar.doId): + return + elapsedTime = globalClockDelta.localElapsedTime(networkTime) + self.game.pickUp(toonId, pickupNum, elapsedTime) + + def d_sendRequestGag(self, waterCoolerIndex): + self.game.localPlayer.orthoWalk.sendCurrentPosition() + self.sendUpdate('requestGag', [waterCoolerIndex]) + + def hasGag(self, toonId, networkTime): + if not self._gameInProgress(): + return + if not self.getToon(base.localAvatar.doId): + return + elapsedTime = globalClockDelta.localElapsedTime(networkTime) + self.game.hasGag(toonId, elapsedTime) + + def doAction(self, action, data, networkTime): + if not self._gameInProgress(): + return + if action == Globals.GameActions.RevealDoor: + self.game.toonRevealsDoor(data) + elif action == Globals.GameActions.EnterDoor: + self.game.toonEntersDoor(data) + elif action == Globals.GameActions.OpenDoor: + timeLeft = Globals.SecondsUntilGameEnds - globalClockDelta.localElapsedTime(networkTime) + self.game.openDoor(timeLeft) + elif action == Globals.GameActions.Countdown: + countdownTimeLeft = Globals.SecondsUntilTimeout + self.game.countdown(countdownTimeLeft) + elif action == Globals.GameActions.TimeAlert: + self.game.timeAlert() + + def setToonSad(self, toonId): + DistCogdoGame.setToonSad(self, toonId) + self.game.handleToonWentSad(toonId) + + def setToonDisconnect(self, toonId): + DistCogdoGame.setToonDisconnect(self, toonId) + self.game.handleToonDisconnected(toonId) + +from otp.ai.MagicWordGlobal import * +@magicWord(category=CATEGORY_PROGRAMMER) +def revealMap(): + if hasattr(base.cr, 'cogdoGame'): + game = base.cr.cogdoGame + game.game.guiMgr.mazeMapGui.showExit() + game.game.guiMgr.mazeMapGui.revealAll() diff --git a/toontown/cogdominium/DistCogdoMazeGameAI.py b/toontown/cogdominium/DistCogdoMazeGameAI.py new file mode 100755 index 00000000..edecb895 --- /dev/null +++ b/toontown/cogdominium/DistCogdoMazeGameAI.py @@ -0,0 +1,239 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.cogdominium.DistCogdoGameAI import DistCogdoGameAI +import CogdoMazeGameGlobals +from direct.distributed.ClockDelta import * +from direct.task import Timer +from toontown.battle import BattleBase +from toontown.building.ElevatorConstants import * + +ALL_ABOARD_LAG = 3.7 + +BASE_TOON_UP = 10 +JOKE_TOON_UP = 5 + +class DistCogdoMazeGameAI(DistCogdoGameAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistCogdoMazeGameAI") + delayIntro = BattleBase.ELEVATOR_T + ElevatorData[ELEVATOR_FIELD]['openTime'] + + def __init__(self, air): + DistCogdoGameAI.__init__(self, air) + self.numSuits = (0,0,0) + self.timer = Timer.Timer() + self.doorRevealed = False + self.toonsInDoor = [] + self.bosses = {} + self.fastMinions = {} + self.slowMinions = {} + self.suitTypes = [self.bosses, self.fastMinions, self.slowMinions] + self.numJokes = {} + + def announceGenerate(self): + DistCogdoGameAI.announceGenerate(self) + self.setupSuitsAI() + + def setupSuitsAI(self): + bossHp = CogdoMazeGameGlobals.SuitData[0]['hp'] + fastMiniHp = CogdoMazeGameGlobals.SuitData[1]['hp'] + slowMiniHp = CogdoMazeGameGlobals.SuitData[2]['hp'] + + serialNum = 0 + for i in xrange(self.numSuits[0]): + self.bosses[serialNum] = bossHp + serialNum += 1 + for i in xrange(self.numSuits[1]): + self.fastMinions[serialNum] = fastMiniHp + serialNum += 1 + for i in xrange(self.numSuits[2]): + self.slowMinions[serialNum] = slowMiniHp + serialNum += 1 + + def setNumSuits(self, num): + self.numSuits = num + + def getNumSuits(self): + return self.numSuits + + def requestUseGag(self, x, y, h, timestamp): + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('toonUsedGag', [avId, x, y, h, globalClockDelta.getRealNetworkTime()]) + + def requestSuitHitByGag(self, suitType, suitNum): + hitAI = self.hitSuitAI(suitType, suitNum) + if not hitAI: + self.notify.warning('Cannot hit suit!') + return + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('suitHitByGag', [avId, suitType, suitNum]) + + def requestHitBySuit(self, suitType, suitNum, nettime): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av: + lostHp = CogdoMazeGameGlobals.SuitData[suitType]['toonDamage'] * self.getDifficulty() * 10 + av.takeDamage(lostHp) + networkTime = globalClockDelta.getRealNetworkTime() + self.sendUpdate('toonHitBySuit', [avId, suitType, suitNum, networkTime]) + if av.getHp() < 1: + self.toonWentSad(avId) + + def requestHitByDrop(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av: + lostHp = CogdoMazeGameGlobals.DropDamage + av.takeDamage(lostHp) + self.sendUpdate('toonHitByDrop', [avId]) + + def requestPickUp(self, pickupNum): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av: + now = globalClockDelta.getRealNetworkTime() + + if avId in self.numJokes: + self.numJokes[avId] += 1 + else: + self.numJokes[avId] = 1 + + self.sendUpdate('pickUp', [avId, pickupNum, now]) + + def requestGag(self, coolerIndex): + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('hasGag', [avId, globalClockDelta.getRealNetworkTime()]) + + def hitSuitAI(self, suitType, suitNum): + cogKey = None + for cogNum in self.suitTypes[suitType].keys(): + if cogNum == suitNum: + cogKey = cogNum + break + if cogKey == None: + return 0 + cogHp = self.suitTypes[suitType][cogKey] + cogHp -= 1 + self.suitTypes[suitType][cogKey] = cogHp + if cogHp <= 0: + del self.suitTypes[suitType][cogKey] + return 1 + + def handleStart(self): + taskMgr.add(self.__checkGameDone, self.taskName('check-game-done')) + taskMgr.add(self.__checkPlayersTask, self.taskName('check-players-task')) + serverDelay = 1.0 + self.timer.startCallback(CogdoMazeGameGlobals.SecondsUntilTimeout + serverDelay, self.__handleGameOver) + taskMgr.doMethodLater(serverDelay, self.clientCountdown, self.taskName('client_countdown')) + taskMgr.add(self.__timeWarningTask, self.taskName('time-warning-task')) + + def clientCountdown(self, task): + self.doAction(CogdoMazeGameGlobals.GameActions.Countdown, 0) + return task.done + + def __handleGameOver(self): + self.removeAll() + self.gameDone(failed=True) + + def __checkGameDone(self, task): + bossesLeft = self.bosses + if len(bossesLeft) == 0: + self.timer.stop() + self.doAction(CogdoMazeGameGlobals.GameActions.OpenDoor, 0) + self.__startTimeout() + return task.done + return task.again + + def __startTimeout(self): + self.timer.startCallback(CogdoMazeGameGlobals.SecondsUntilGameEnds, self.__handleTimeout) + + def __handleTimeout(self): + for toon in self.toons: + if not toon in self.toonsInDoor: + self.killToon(toon) + self.removeAll() + self.gameDone() + + def __timeWarningTask(self, task): + if self.timer.getT() <= CogdoMazeGameGlobals.SecondsForTimeAlert: + self.doAction(CogdoMazeGameGlobals.GameActions.TimeAlert, 0) + return task.done + return task.again + + def killToon(self, avId): + av = self.air.doId2do.get(avId) + if av: + if av.getHp() > 0: + av.takeDamage(av.getHp()) + self.toonWentSad(avId) + self.__playerDisconnected(avId) + + def __checkPlayersTask(self, task): + for toonId in self.toons: + toon = self.air.doId2do.get(toonId) + if not toon: + self.__playerDisconnected(toonId) + return task.again + + def __playerDisconnected(self, avId): + self.sendUpdate('setToonDisconnect', [avId]) + self.toons.pop(self.toons.index(avId)) + if len(self.toons) == 0: + self.removeAll() + self.gameDone(failed=True) + + def doAction(self, action, data): + self.sendUpdate('doAction', [action, data, globalClockDelta.getRealNetworkTime()]) + + def requestAction(self, action, data): + Globals = CogdoMazeGameGlobals + avId = self.air.getAvatarIdFromSender() + if action == Globals.GameActions.RevealDoor: + if not self.doorRevealed: + self.doAction(action, avId) + self.doorRevealed = True + else: + self.notify.warning('Toon tried to reveal door but it\'s already revealed! Ignoring.') + elif action == Globals.GameActions.EnterDoor: + if not avId in self.toonsInDoor: + self.doAction(action, avId) + self.toonsInDoor.append(avId) + self.toonUpToon(avId) + else: + self.notify.warning('Toon tried to enter into door but already entered! Ignoring.') + return + + if len(self.toonsInDoor) >= len(self.toons): + self.__handleAllAboard() + else: + self.notify.warning('Client requested unknown action \'%s\'' %action) + + def __handleAllAboard(self): + if len(self.toonsInDoor) != len(self.toons): + self.notify.warning('__handleAllAboard expect all toons aboard!') + return + self.removeAll() + taskMgr.doMethodLater(ALL_ABOARD_LAG, lambda t: self.gameDone(), self.taskName('all-aboard-delay')) + + def toonUpToon(self, toonId): + if toonId in self.toonsInDoor: + toon = self.air.doId2do.get(toonId) + if toon: + val = min(BASE_TOON_UP + JOKE_TOON_UP * self.numJokes.get(toonId, 0), toon.getMaxHp()) + toon.toonUp(val) + + def removeAll(self): + taskMgr.remove(self.taskName('check-game-done')) + taskMgr.remove(self.taskName('check-players-task')) + taskMgr.remove(self.taskName('time-warning-task')) + taskMgr.remove(self.taskName('all-aboard-delay')) + self.timer.stop() + + def disable(self): + DistCogdoGameAI.disable(self) + self.removeAll() + +from otp.ai.MagicWordGlobal import * +@magicWord(category=CATEGORY_PROGRAMMER) +def endMaze(): + if hasattr(simbase.air, 'cogdoGame'): + maze = simbase.air.cogdoGame + maze.doAction(CogdoMazeGameGlobals.GameActions.OpenDoor, 0) + return 'Finished cogdo maze game!' diff --git a/toontown/cogdominium/DistCogdoMazeGameBase.py b/toontown/cogdominium/DistCogdoMazeGameBase.py new file mode 100755 index 00000000..9b6294b5 --- /dev/null +++ b/toontown/cogdominium/DistCogdoMazeGameBase.py @@ -0,0 +1,11 @@ +from direct.showbase.RandomNumGen import RandomNumGen +from toontown.cogdominium.CogdoMaze import CogdoMazeFactory +import CogdoMazeGameGlobals as Globals + +class DistCogdoMazeGameBase: + + def createRandomNumGen(self): + return RandomNumGen(self.doId) + + def createMazeFactory(self, randomNumGen): + return CogdoMazeFactory(randomNumGen, Globals.NumQuadrants[0], Globals.NumQuadrants[1]) diff --git a/toontown/cogdominium/DistributedCogdoBarrel.py b/toontown/cogdominium/DistributedCogdoBarrel.py new file mode 100755 index 00000000..2c590600 --- /dev/null +++ b/toontown/cogdominium/DistributedCogdoBarrel.py @@ -0,0 +1,132 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from toontown.toonbase import ToontownGlobals, ToontownIntervals +from toontown.cogdominium import CogdoBarrelRoomConsts +from toontown.cogdominium import CogdoBarrelRoom +import random + +class DistributedCogdoBarrel(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogdoBarrel') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.barrelRoom = CogdoBarrelRoom.CogdoBarrelRoom() + self.index = None + self.state = None + self.model = None + self.collSphere = None + self.collNode = None + self.collNodePath = None + self.availableTex = None + self.usedTex = None + self.brLaff = 0 + return + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.bumpSound = base.loadSfx(CogdoBarrelRoomConsts.BarrelBumpSound) + self.grabSound = base.loadSfx(CogdoBarrelRoomConsts.BarrelGrabSound) + + def __setModel(self): + self.model = loader.loadModel(CogdoBarrelRoomConsts.BarrelModel) + self.model.setScale(CogdoBarrelRoomConsts.BarrelModelScale) + self.model.setPos(self.__getProp('pos')) + self.model.setH(self.__getProp('heading')) + cogdoBarrelsNode = render.find('@@CogdoBarrels') + if not cogdoBarrelsNode or cogdoBarrelsNode.isEmpty(): + cogdoBarrelsNode = render.attachNewNode('CogdoBarrels') + self.model.reparentTo(cogdoBarrelsNode) + self.availableTex = loader.loadTexture('phase_5/maps/tt_t_ara_cbr_Barrel_notUsed.jpg') + self.usedTex = loader.loadTexture('phase_5/maps/tt_t_ara_cbr_Barrel_Used.jpg') + self.model.setTexture(self.availableTex, 100) + + def __addCollision(self): + if not self.collSphere: + ax, ay, az = (0, 0, 0.5) + bx, by, bz = (0, 0, 2.5) + radius = 1 + self.collTube = CollisionTube(ax, ay, az, bx, by, bz, radius) + self.collTube.setTangible(1) + self.collSphere = CollisionSphere(*CogdoBarrelRoomConsts.BarrelCollParams) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.uniqueName('barrelSphere')) + self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNode.addSolid(self.collTube) + self.collNodePath = self.model.attachNewNode(self.collNode) + self.collNodePath.hide() + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.__setModel() + self.__addCollision() + + def disable(self): + DistributedObject.DistributedObject.disable(self) + + def delete(self): + DistributedObject.DistributedObject.delete(self) + ToontownIntervals.cleanup(self.__pulseIvalName()) + if self.model: + self.model.removeNode() + del self.model + self.model = None + self.ignore(self.uniqueName('enterbarrelSphere')) + return + + def setIndex(self, index): + self.index = index + + def __getProp(self, prop): + return CogdoBarrelRoomConsts.BarrelProps[self.index][prop] + + def setState(self, state): + self.state = state + self.__updateState() + + def __updateState(self): + if self.state == CogdoBarrelRoomConsts.StateAvailable: + if self.model: + self.model.unstash() + self.model.setTexture(self.availableTex, 100) + self.accept(self.uniqueName('enterbarrelSphere'), self.handleEnterSphere) + elif self.state == CogdoBarrelRoomConsts.StateUsed: + if self.model: + self.model.unstash() + self.model.setTexture(self.usedTex, 100) + self.ignore(self.uniqueName('enterbarrelSphere')) + elif self.state == CogdoBarrelRoomConsts.StateHidden or self.state == CogdoBarrelRoomConsts.StateCrushed: + if self.model: + self.model.stash() + self.ignore(self.uniqueName('enterbarrelSphere')) + else: + if self.model: + self.model.stash() + self.ignore(self.uniqueName('enterbarrelSphere')) + + def handleEnterSphere(self, collEntry = None): + base.playSfx(self.bumpSound, volume=0.35, node=self.model, listener=camera) + self.d_requestGrab() + + def d_requestGrab(self): + self.sendUpdate('requestGrab', []) + + def setGrab(self, avId): + toonup = CogdoBarrelRoomConsts.ToonUp + if avId == base.localAvatar.doId: + ToontownIntervals.start(ToontownIntervals.getPulseIval(self.model, self.__pulseIvalName(), 1.15, duration=0.2)) + self.setState(CogdoBarrelRoomConsts.StateUsed) + self.brLaff = random.randint(toonup[0], toonup[1]) + + def setReject(self): + pass + + def getBarrelLaff(self): + return self.brLaff + + def __pulseIvalName(self): + return 'DistributedCogdoBarrelPulse%s' % self.doId + + def __str__(self): + return 'Barrel %s' % self.index diff --git a/toontown/cogdominium/DistributedCogdoBarrelAI.py b/toontown/cogdominium/DistributedCogdoBarrelAI.py new file mode 100755 index 00000000..fd4b7821 --- /dev/null +++ b/toontown/cogdominium/DistributedCogdoBarrelAI.py @@ -0,0 +1,35 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObjectAI +import CogdoBarrelRoomConsts +import random + +class DistributedCogdoBarrelAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedCogdoBarrelAI") + + def __init__(self, air, index): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.index = index + self.state = CogdoBarrelRoomConsts.StateAvailable + self.brLaff = 0 + + def requestGrab(self): + toonup = CogdoBarrelRoomConsts.ToonUp + if self.state == CogdoBarrelRoomConsts.StateAvailable: + self.state = CogdoBarrelRoomConsts.StateUsed + self.sendUpdate("setState", [CogdoBarrelRoomConsts.StateUsed]) + self.sendUpdate("setGrab", [self.air.getAvatarIdFromSender()]) + self.brLaff = random.randint(toonup[0], toonup[1]) + self.recieveToonUp() + + def getIndex(self): + return self.index + + def getState(self): + return self.state + + def recieveToonUp(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if not av: + return + av.toonUp(self.brLaff) diff --git a/toontown/cogdominium/DistributedCogdoBattleBldg.py b/toontown/cogdominium/DistributedCogdoBattleBldg.py new file mode 100755 index 00000000..aaaa9af5 --- /dev/null +++ b/toontown/cogdominium/DistributedCogdoBattleBldg.py @@ -0,0 +1,12 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer +from toontown.battle import DistributedBattleBldg + +class DistributedCogdoBattleBldg(DistributedBattleBldg.DistributedBattleBldg): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogdoBattleBldg') + + def __init__(self, cr): + DistributedBattleBldg.DistributedBattleBldg.__init__(self, cr) + + def getBossBattleTaunt(self): + return TTLocalizer.CogdoBattleBldgBossTaunt diff --git a/toontown/cogdominium/DistributedCogdoBattleBldgAI.py b/toontown/cogdominium/DistributedCogdoBattleBldgAI.py new file mode 100755 index 00000000..c82964b4 --- /dev/null +++ b/toontown/cogdominium/DistributedCogdoBattleBldgAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.battle.DistributedBattleBldgAI import DistributedBattleBldgAI + +class DistributedCogdoBattleBldgAI(DistributedBattleBldgAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedCogdoBattleBldgAI") diff --git a/toontown/cogdominium/DistributedCogdoElevatorExt.py b/toontown/cogdominium/DistributedCogdoElevatorExt.py new file mode 100755 index 00000000..0d2346c0 --- /dev/null +++ b/toontown/cogdominium/DistributedCogdoElevatorExt.py @@ -0,0 +1,18 @@ +from toontown.building.DistributedElevatorExt import DistributedElevatorExt +from toontown.building.ElevatorConstants import * +from toontown.building.ElevatorUtils import * +from toontown.toonbase import ToontownGlobals + +class DistributedCogdoElevatorExt(DistributedElevatorExt): + def __init__(self, cr): + DistributedElevatorExt.__init__(self, cr) + self.type = ELEVATOR_FIELD + + def getElevatorModel(self): + return self.bldg.getCogdoElevatorNodePath() + + def getBldgDoorOrigin(self): + return self.bldg.getCogdoDoorOrigin() + + def _getDoorsClosedInfo(self): + return ('cogdoInterior', 'cogdoInterior') diff --git a/toontown/cogdominium/DistributedCogdoElevatorExtAI.py b/toontown/cogdominium/DistributedCogdoElevatorExtAI.py new file mode 100755 index 00000000..7c108dec --- /dev/null +++ b/toontown/cogdominium/DistributedCogdoElevatorExtAI.py @@ -0,0 +1,8 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.building.DistributedElevatorExtAI import DistributedElevatorExtAI + +class DistributedCogdoElevatorExtAI(DistributedElevatorExtAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogdoElevatorExtAI') + + def _createInterior(self): + self.bldg.createCogdoInterior() diff --git a/toontown/cogdominium/DistributedCogdoElevatorInt.py b/toontown/cogdominium/DistributedCogdoElevatorInt.py new file mode 100755 index 00000000..6294013e --- /dev/null +++ b/toontown/cogdominium/DistributedCogdoElevatorInt.py @@ -0,0 +1,6 @@ +from toontown.building.DistributedElevatorInt import DistributedElevatorInt + +class DistributedCogdoElevatorInt(DistributedElevatorInt): + + def _getDoorsClosedInfo(self): + return ('cogdoInterior', 'cogdoInterior') diff --git a/toontown/cogdominium/DistributedCogdoElevatorIntAI.py b/toontown/cogdominium/DistributedCogdoElevatorIntAI.py new file mode 100755 index 00000000..912cdb05 --- /dev/null +++ b/toontown/cogdominium/DistributedCogdoElevatorIntAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.building.DistributedElevatorIntAI import DistributedElevatorIntAI + +class DistributedCogdoElevatorIntAI(DistributedElevatorIntAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedCogdoElevatorIntAI") diff --git a/toontown/cogdominium/DistributedCogdoInterior.py b/toontown/cogdominium/DistributedCogdoInterior.py new file mode 100755 index 00000000..c599e426 --- /dev/null +++ b/toontown/cogdominium/DistributedCogdoInterior.py @@ -0,0 +1,811 @@ +import random +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.building.ElevatorConstants import * +from toontown.toon import NPCToons +from pandac.PandaModules import NodePath +from toontown.building import ElevatorUtils +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.distributed import DistributedObject +from direct.fsm import State +from direct.fsm.StatePush import StateVar, FunctionCall +from toontown.battle import BattleBase +from toontown.hood import ZoneUtil +from toontown.cogdominium.CogdoLayout import CogdoLayout +from toontown.cogdominium import CogdoGameConsts +from toontown.cogdominium import CogdoBarrelRoom, CogdoBarrelRoomConsts +from toontown.distributed import DelayDelete +from toontown.toonbase import TTLocalizer +from CogdoExecutiveSuiteMovies import CogdoExecutiveSuiteIntro +from CogdoBarrelRoomMovies import CogdoBarrelRoomIntro +from CogdoElevatorMovie import CogdoElevatorMovie +SUITE_DICT = {'s': 'tt_m_ara_crg_penthouse_sell', + 'l': 'tt_m_ara_crg_penthouse_law', + 'm': 'tt_m_ara_crg_penthouse_sell', + 'c': 'tt_m_ara_crg_penthouse_sell'} +PAINTING_DICT = {'s': 'tt_m_ara_crg_paintingMoverShaker', + 'l': 'tt_m_ara_crg_paintingLegalEagle', + 'm': 'tt_m_ara_crg_paintingMoverShaker', + 'c': 'tt_m_ara_crg_paintingMoverShaker'} + +from otp.nametag.NametagConstants import * + +class DistributedCogdoInterior(DistributedObject.DistributedObject): + id = 0 + cageHeights = [11.36, 0.01] + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.toons = [] + self.activeIntervals = {} + self.openSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_open.ogg') + self.closeSfx = base.loadSfx('phase_5/audio/sfx/elevator_door_close.ogg') + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.distBldgDoId = None + self._CogdoGameRepeat = config.GetBool('cogdo-game-repeat', 0) + self.currentFloor = -1 + self.elevatorName = self.__uniqueName('elevator') + self.floorModel = None + self.elevatorOutOpen = 0 + self.BottomFloor_SuitPositions = [Point3(0, 15, 0), + Point3(10, 20, 0), + Point3(-7, 24, 0), + Point3(-10, 0, 0)] + self.BottomFloor_SuitHs = [75, + 170, + -91, + -44] + self.Cubicle_SuitPositions = [Point3(0, 18, 0), + Point3(10, 12, 0), + Point3(-9, 11, 0), + Point3(-3, 13, 0)] + self.Cubicle_SuitHs = [170, + 56, + -52, + 10] + self.BossOffice_SuitPositions = [Point3(0, 15, 0), + Point3(10, 20, 0), + Point3(-10, 6, 0), + Point3(-17, 30, 0)] + self.BossOffice_SuitHs = [170, + 120, + 12, + 38] + self._wantBarrelRoom = config.GetBool('cogdo-want-barrel-room', 1) + self.barrelRoom = CogdoBarrelRoom.CogdoBarrelRoom() + self.brResults = [[], []] + self.barrelRoomIntroTrack = None + self.penthouseOutroTrack = None + self.penthouseOutroChatDoneTrack = None + self.penthouseIntroTrack = None + self.waitMusic = base.loadMusic('phase_7/audio/bgm/encntr_toon_winning_indoor.ogg') + self.elevatorMusic = base.loadMusic('phase_7/audio/bgm/tt_elevator.ogg') + self.fsm = ClassicFSM.ClassicFSM('DistributedCogdoInterior', [State.State('WaitForAllToonsInside', self.enterWaitForAllToonsInside, self.exitWaitForAllToonsInside, ['Elevator']), + State.State('Elevator', self.enterElevator, self.exitElevator, ['Game', 'BattleIntro', 'BarrelRoomIntro']), + State.State('Game', self.enterGame, self.exitGame, ['Resting', 'Failed', 'BattleIntro', 'BarrelRoomIntro', 'Elevator']), + State.State('BarrelRoomIntro', self.enterBarrelRoomIntro, self.exitBarrelRoomIntro, ['CollectBarrels', 'Off']), + State.State('CollectBarrels', self.enterCollectBarrels, self.exitCollectBarrels, ['BarrelRoomReward', 'Off']), + State.State('BarrelRoomReward', self.enterBarrelRoomReward, self.exitBarrelRoomReward, ['Battle', + 'ReservesJoining', + 'BattleIntro', + 'Off']), + State.State('BattleIntro', self.enterBattleIntro, self.exitBattleIntro, ['Battle', 'ReservesJoining', 'Off']), + State.State('Battle', self.enterBattle, self.exitBattle, ['Resting', 'Reward', 'ReservesJoining']), + State.State('ReservesJoining', self.enterReservesJoining, self.exitReservesJoining, ['Battle']), + State.State('Resting', self.enterResting, self.exitResting, ['Elevator']), + State.State('Reward', self.enterReward, self.exitReward, ['Off']), + State.State('Failed', self.enterFailed, self.exitFailed, ['Off']), + State.State('Off', self.enterOff, self.exitOff, ['Elevator', 'WaitForAllToonsInside', 'Battle'])], 'Off', 'Off') + self.fsm.enterInitialState() + self._haveEntranceElevator = StateVar(False) + self._stashEntranceElevator = StateVar(False) + self._stashEntranceElevatorFC = FunctionCall(self._doStashEntranceElevator, self._haveEntranceElevator, self._stashEntranceElevator) + self._entranceElevCallbacks = [] + self._doEntranceElevCallbacksFC = FunctionCall(self._doEntranceElevCallbacks, self._haveEntranceElevator) + self.cage = None + self.shopOwnerNpcId = None + self.shopOwnerNpc = None + self._movie = None + self.SOSToonName = None + self.FOType = None + + def setShopOwnerNpcId(self, npcId): + self.shopOwnerNpcId = npcId + + def setSOSNpcId(self, npcId): + self.SOSToonName = NPCToons.getNPCName(npcId) + + def setFOType(self, typeId): + self.FOType = chr(typeId) + + def getFOType(self): + return self.FOType + + def __uniqueName(self, name): + DistributedCogdoInterior.id += 1 + return name + '%d' % DistributedCogdoInterior.id + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.announceGenerateName = self.uniqueName('generate') + self.accept(self.announceGenerateName, self.handleAnnounceGenerate) + self.elevatorModelIn = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_csa_elevatorB') + self.leftDoorIn = self.elevatorModelIn.find('**/left_door') + self.rightDoorIn = self.elevatorModelIn.find('**/right_door') + self.elevatorModelOut = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_csa_elevator') + self.leftDoorOut = self.elevatorModelOut.find('**/left_door') + self.rightDoorOut = self.elevatorModelOut.find('**/right_door') + + def __makeShopOwnerNpc(self): + if self.shopOwnerNpc: + return + self.shopOwnerNpc = NPCToons.createLocalNPC(self.shopOwnerNpcId) + if not self.shopOwnerNpc: + self.notify.warning('No shopkeeper in this cogdominium, using FunnyFarm Sellbot FO NPCToons') + random.seed(self.doId) + shopkeeper = random.randint(7001, 7009) + self.shopOwnerNpc = NPCToons.createLocalNPC(shopkeeper) + self.shopOwnerNpc.addActive() + self.shopOwnerNpc.reparentTo(self.cage) + self.shopOwnerNpc.setPosHpr(0, -2, 0, 180, 0, 0) + self.shopOwnerNpc.loop('neutral') + + def setElevatorLights(self, elevatorModel): + npc = elevatorModel.findAllMatches('**/floor_light_?;+s') + for i in xrange(npc.getNumPaths()): + np = npc.getPath(i) + np.setDepthOffset(120) + floor = int(np.getName()[-1:]) - 1 + if floor == self.currentFloor: + np.setColor(LIGHT_ON_COLOR) + elif floor < self.layout.getNumGameFloors() + (1 if self.FOType != "s" else 0): + if self.isBossFloor(self.currentFloor): + np.setColor(LIGHT_ON_COLOR) + else: + np.setColor(LIGHT_OFF_COLOR) + else: + np.hide() + + def startAlertElevatorLightIval(self, elevatorModel): + light = elevatorModel.find('**/floor_light_%s' % (self.currentFloor + 1)) + track = Sequence(Func(light.setColor, Vec4(1.0, 0.6, 0.6, 1.0)), Wait(0.9), Func(light.setColor, LIGHT_ON_COLOR), Wait(0.9)) + self.activeIntervals['alertElevatorLight'] = track + track.loop() + + def stopAlertElevatorLightIval(self, elevatorModel): + self.__finishInterval('alertElevatorLight') + self.setElevatorLights(elevatorModel) + + def handleAnnounceGenerate(self, obj): + self.ignore(self.announceGenerateName) + self.cageDoorSfx = loader.loadSfx('phase_5/audio/sfx/CHQ_SOS_cage_door.ogg') + self.cageLowerSfx = loader.loadSfx('phase_5/audio/sfx/CHQ_SOS_cage_lower.ogg') + self.sendUpdate('setAvatarJoined', []) + + def disable(self): + self.fsm.requestFinalState() + self.__cleanupIntervals() + self.ignoreAll() + self.__cleanup() + self.__cleanupShopOwnerNpc() + self.__cleanupPenthouseIntro() + DistributedObject.DistributedObject.disable(self) + + def __cleanupShopOwnerNpc(self): + if self.shopOwnerNpc: + self.shopOwnerNpc.removeActive() + self.shopOwnerNpc.delete() + self.shopOwnerNpc = None + + def __cleanupPenthouseIntro(self): + if hasattr(self, '_movie') and self._movie: + self._movie.unload() + self._movie = None + + def delete(self): + self._stashEntranceElevatorFC.destroy() + self._doEntranceElevCallbacksFC.destroy() + self._haveEntranceElevator.destroy() + self._stashEntranceElevator.destroy() + self._entranceElevCallbacks = None + del self.waitMusic + del self.elevatorMusic + del self.openSfx + del self.closeSfx + del self.fsm + base.localAvatar.inventory.setBattleCreditMultiplier(1) + DistributedObject.DistributedObject.delete(self) + + def isBossFloor(self, floorNum): + return self.layout.hasBossBattle() and (self.layout.getBossBattleFloor() + 0) == floorNum + + def __cleanup(self): + self.toons = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + if self.elevatorModelIn != None: + self.elevatorModelIn.removeNode() + if self.elevatorModelOut != None: + self.elevatorModelOut.removeNode() + if self.floorModel != None: + self.floorModel.removeNode() + if self.cage != None: + self.cage = None + if self.barrelRoom != None: + self.barrelRoom.destroy() + self.barrelRoom = None + self.leftDoorIn = None + self.rightDoorIn = None + self.leftDoorOut = None + self.rightDoorOut = None + + def __addToon(self, toon): + self.accept(toon.uniqueName('disable'), self.__handleUnexpectedExit, extraArgs=[toon]) + + def __handleUnexpectedExit(self, toon): + self.notify.warning('handleUnexpectedExit() - toon: %d' % toon.doId) + self.__removeToon(toon, unexpected=1) + + def __removeToon(self, toon, unexpected = 0): + if self.toons.count(toon) == 1: + self.toons.remove(toon) + self.ignore(toon.uniqueName('disable')) + + def __finishInterval(self, name): + if name in self.activeIntervals: + interval = self.activeIntervals[name] + if interval.isPlaying(): + interval.finish() + + def __cleanupIntervals(self): + for interval in self.activeIntervals.values(): + interval.finish() + + self.activeIntervals = {} + + def __closeInElevator(self): + self.leftDoorIn.setPos(3.5, 0, 0) + self.rightDoorIn.setPos(-3.5, 0, 0) + + def getZoneId(self): + return self.zoneId + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def getExtZoneId(self): + return self.extZoneId + + def setExtZoneId(self, extZoneId): + self.extZoneId = extZoneId + + def getDistBldgDoId(self): + return self.distBldgDoId + + def setDistBldgDoId(self, distBldgDoId): + self.distBldgDoId = distBldgDoId + + def setNumFloors(self, numFloors): + self.layout = CogdoLayout(numFloors) + + def getToonIds(self): + toonIds = [] + for toon in self.toons: + toonIds.append(toon.doId) + + return toonIds + + def setToons(self, toonIds, hack): + self.toonIds = toonIds + oldtoons = self.toons + self.toons = [] + for toonId in toonIds: + if toonId != 0: + if toonId in self.cr.doId2do: + toon = self.cr.doId2do[toonId] + toon.stopSmooth() + self.toons.append(toon) + if oldtoons.count(toon) == 0: + self.__addToon(toon) + else: + self.notify.warning('setToons() - no toon: %d' % toonId) + + for toon in oldtoons: + if self.toons.count(toon) == 0: + self.__removeToon(toon) + + def setSuits(self, suitIds, reserveIds, values): + oldsuits = self.suits + self.suits = [] + self.joiningReserves = [] + for suitId in suitIds: + if suitId in self.cr.doId2do: + suit = self.cr.doId2do[suitId] + self.suits.append(suit) + suit.fsm.request('Battle') + suit.buildingSuit = 1 + suit.reparentTo(render) + if oldsuits.count(suit) == 0: + self.joiningReserves.append(suit) + + if 'Elevator' in repr(self.fsm): + # Fix the position. + pos, h = BattleBase.BattleBase.suitPoints[len(suitIds) - 1][suitIds.index(suitId)] + suit.setPos(pos) + suit.setH(h) + else: + self.notify.warning('setSuits() - no suit: %d' % suitId) + + self.reserveSuits = [] + for index in xrange(len(reserveIds)): + suitId = reserveIds[index] + if suitId in self.cr.doId2do: + suit = self.cr.doId2do[suitId] + self.reserveSuits.append((suit, values[index])) + else: + self.notify.warning('setSuits() - no suit: %d' % suitId) + + if len(self.joiningReserves) > 0: + self.fsm.request('ReservesJoining') + + def setState(self, state, timestamp): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def stashElevatorIn(self, stash = True): + self._stashEntranceElevator.set(stash) + + def getEntranceElevator(self, callback): + if self._haveEntranceElevator.get(): + callback(self.elevIn) + else: + self._entranceElevCallbacks.append(callback) + + def _doEntranceElevCallbacks(self, haveElev): + if haveElev: + while len(self._entranceElevCallbacks): + cbs = self._entranceElevCallbacks[:] + self._entranceElevCallbacks = [] + for callback in cbs: + callback(self.elevIn) + + def _doStashEntranceElevator(self, haveElev, doStash): + if haveElev: + if doStash: + self.elevIn.stash() + else: + self.elevIn.unstash() + + def d_elevatorDone(self): + self.sendUpdate('elevatorDone', []) + + def d_reserveJoinDone(self): + self.sendUpdate('reserveJoinDone', []) + + def enterOff(self, ts = 0): + messenger.send('sellbotFieldOfficeChanged', [False]) + return None + + def exitOff(self): + return None + + def enterWaitForAllToonsInside(self, ts = 0): + base.transitions.fadeOut(0) + + def exitWaitForAllToonsInside(self): + return None + + def enterGame(self, ts = 0): + base.cr.forbidCheesyEffects(1) + + def exitGame(self): + base.cr.forbidCheesyEffects(0) + + def __playElevator(self, ts, name, callback): + SuitHs = [] + SuitPositions = [] + + if self.floorModel: + self.floorModel.removeNode() + self.floorModel = None + + if self.cage: + self.cage = None + + if self.currentFloor == 0: + SuitHs = self.BottomFloor_SuitHs + SuitPositions = self.BottomFloor_SuitPositions + + if self.isBossFloor(self.currentFloor): + self.notify.info('__playElevator: currentFloor %s is boss' % self.currentFloor) + self.barrelRoom.unload() + if self.FOType: + penthouseName = SUITE_DICT.get(self.FOType) + for i in xrange(4): + self.floorModel = loader.loadModel('phase_5/models/cogdominium/%s' % penthouseName) + + self.cage = self.floorModel.find('**/cage') + pos = self.cage.getPos() + self.cagePos = [] + for height in self.cageHeights: + self.cagePos.append(Point3(pos[0], pos[1], height)) + + self.cageDoor = self.floorModel.find('**/cage_door') + self.cageDoor.wrtReparentTo(self.cage) + if self.FOType: + paintingModelName = PAINTING_DICT.get(self.FOType) + for i in xrange(4): + paintingModel = loader.loadModel('phase_5/models/cogdominium/%s' % paintingModelName) + loc = self.floorModel.find('**/loc_painting%d' % (i + 1)) + paintingModel.reparentTo(loc) + + if not self.floorModel.find('**/trophyCase').isEmpty(): + for i in xrange(4): + goldEmblem = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_crg_goldTrophy.bam') + loc = self.floorModel.find('**/gold_0%d' % (i + 1)) + goldEmblem.reparentTo(loc) + for i in xrange(20): + silverEmblem = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_crg_silverTrophy.bam') + loc = self.floorModel.find('**/silver_0%d' % (i + 1)) + silverEmblem.reparentTo(loc) + + SuitHs = self.BossOffice_SuitHs + SuitPositions = self.BossOffice_SuitPositions + self.__makeShopOwnerNpc() + else: + if self._wantBarrelRoom: + self.barrelRoom.load() + self.barrelRoom.hide() + SuitHs = self.Cubicle_SuitHs + SuitPositions = self.Cubicle_SuitPositions + + if self.floorModel: + self.floorModel.reparentTo(render) + if self.isBossFloor(self.currentFloor): + self.notify.info('Load boss_suit_office') + elevIn = self.floorModel.find(CogdoGameConsts.PenthouseElevatorInPath).copyTo(render) + elevOut = self.floorModel.find(CogdoGameConsts.PenthouseElevatorOutPath) + frame = self.elevatorModelOut.find('**/frame') + + if not frame.isEmpty(): + frame.hide() + + frame = self.elevatorModelIn.find('**/frame') + + if not frame.isEmpty(): + frame.hide() + + self.elevatorModelOut.reparentTo(elevOut) + self.elevatorModelOut.setY(0) + else: + elevIn = self.floorModel.find('**/elevator-in') + elevOut = self.floorModel.find('**/elevator-out') + elif self._wantBarrelRoom and self.barrelRoom.isLoaded() and self.currentFloor == 2 and self.FOType == 'l': #i know this is really ugly + elevIn = self.barrelRoom.model.find(CogdoBarrelRoomConsts.BarrelRoomElevatorInPath) + elevOut = self.barrelRoom.model.find(CogdoBarrelRoomConsts.BarrelRoomElevatorOutPath) + y = elevOut.getY(render) + elevOut = elevOut.copyTo(render) + elevOut.setY(render, y - 0.75) + else: + floorModel = loader.loadModel('phase_7/models/modules/boss_suit_office') + elevIn = floorModel.find('**/elevator-in').copyTo(render) + elevOut = floorModel.find('**/elevator-out').copyTo(render) + floorModel.removeNode() + + self.elevIn = elevIn + self.elevOut = elevOut + self._haveEntranceElevator.set(True) + for index in xrange(len(self.suits)): + if not self.suits[index].isEmpty(): + self.suits[index].setPos(SuitPositions[index]) + if len(self.suits) > 2: + self.suits[index].setH(SuitHs[index]) + else: + self.suits[index].setH(170) + self.suits[index].loop('neutral') + + for toon in self.toons: + toon.reparentTo(self.elevatorModelIn) + index = self.toonIds.index(toon.doId) + toon.setPos(ElevatorPoints[index][0], ElevatorPoints[index][1], ElevatorPoints[index][2]) + toon.setHpr(180, 0, 0) + toon.loop('neutral') + + self.elevatorModelIn.reparentTo(elevIn) + self.leftDoorIn.setPos(3.5, 0, 0) + self.rightDoorIn.setPos(-3.5, 0, 0) + camera.reparentTo(self.elevatorModelIn) + camera.setH(180) + camera.setP(0) + camera.setPos(0, 14, 4) + base.playMusic(self.elevatorMusic, looping=1, volume=0.8) + track = Sequence(Func(base.transitions.noTransitions), ElevatorUtils.getRideElevatorInterval(ELEVATOR_NORMAL), ElevatorUtils.getOpenInterval(self, self.leftDoorIn, self.rightDoorIn, self.openSfx, None, type=ELEVATOR_NORMAL), Func(camera.wrtReparentTo, render)) + for toon in self.toons: + track.append(Func(toon.wrtReparentTo, render)) + + track.append(Func(callback)) + track.start(ts) + self.activeIntervals[name] = track + + def enterElevator(self, ts = 0): + if not self._CogdoGameRepeat: + self.currentFloor += 1 + self.cr.playGame.getPlace().currentFloor = self.currentFloor + self.setElevatorLights(self.elevatorModelIn) + self.setElevatorLights(self.elevatorModelOut) + if not self.isBossFloor(self.currentFloor): + self.elevatorModelOut.detachNode() + messenger.send('sellbotFieldOfficeChanged', [True]) + else: + if self.FOType == 's': + self._movie = CogdoElevatorMovie() + self._movie.load() + self._movie.play() + self.__playElevator(ts, self.elevatorName, self.__handleElevatorDone) + mult = ToontownBattleGlobals.getCreditMultiplier(self.currentFloor) + base.localAvatar.inventory.setBattleCreditMultiplier(mult) + + def __handleElevatorDone(self): + self.d_elevatorDone() + + def exitElevator(self): + self.elevatorMusic.stop() + if self._movie: + self._movie.end() + self.__cleanupPenthouseIntro() + self.__finishInterval(self.elevatorName) + + def __setupBarrelRoom(self): + self.currentFloor += 1 + base.transitions.irisOut(0.0) + self.elevatorModelOut.setY(-12) + self.elevatorModelIn.reparentTo(self.barrelRoom.model.find(CogdoBarrelRoomConsts.BarrelRoomElevatorInPath)) + self.leftDoorIn.setPos(3.5, 0, 0) + self.rightDoorIn.setPos(-3.5, 0, 0) + self._showExitElevator() + self.barrelRoom.show() + self.barrelRoom.placeToonsAtEntrance(self.toons) + self.setElevatorLights(self.elevatorModelOut) + + def barrelRoomIntroDone(self): + self.sendUpdate('toonBarrelRoomIntroDone', []) + + def enterBarrelRoomIntro(self, ts = 0): + if not self.isBossFloor(self.currentFloor): + if self._wantBarrelRoom: + self.__setupBarrelRoom() + self.barrelRoomIntroTrack, trackName = self.barrelRoom.getIntroInterval() + self.barrelRoomIntroDoneEvent = trackName + self.accept(self.barrelRoomIntroDoneEvent, self.barrelRoomIntroDone) + self.activeIntervals[trackName] = self.barrelRoomIntroTrack + self.barrelRoomIntroTrack.start(ts) + self._movie = CogdoBarrelRoomIntro() + self._movie.load() + self._movie.play() + else: + self._showExitElevator() + + def exitBarrelRoomIntro(self): + if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor): + self.ignore(self.barrelRoomIntroDoneEvent) + if self.barrelRoomIntroTrack: + self.barrelRoomIntroTrack.finish() + DelayDelete.cleanupDelayDeletes(self.barrelRoomIntroTrack) + self.barrelRoomIntroTrack = None + + def __handleLocalToonLeftBarrelRoom(self): + self.notify.info('Local toon teleported out of barrel room.') + self.sendUpdate('toonLeftBarrelRoom', []) + self.barrelRoom.deactivate() + + def enterCollectBarrels(self, ts = 0): + if not self.isBossFloor(self.currentFloor): + if self._wantBarrelRoom: + self.acceptOnce('localToonLeft', self.__handleLocalToonLeftBarrelRoom) + self.barrelRoom.activate() + base.playMusic(self.waitMusic, looping=1, volume=0.7) + base.localAvatar.questMap.stop() + + def exitCollectBarrels(self): + if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor): + self.ignore('localToonLeft') + self.barrelRoom.deactivate() + self.waitMusic.stop() + + def __brRewardDone(self, task = None): + self.notify.info('Toon finished watching the barrel room reward.') + self.sendUpdate('toonBarrelRoomRewardDone', []) + self.fsm.request('Battle') + + def enterBarrelRoomReward(self, ts = 0): + if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor): + base.cr.playGame.getPlace().fsm.request('stopped') + self.startAlertElevatorLightIval(self.elevatorModelOut) + track, trackName = self.barrelRoom.showRewardUi(callback=self.__brRewardDone) + self.activeIntervals[trackName] = track + track.start() + self.barrelRoom.placeToonsNearBattle(self.toons) + + def exitBarrelRoomReward(self): + if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor): + base.cr.playGame.getPlace().fsm.request('walk') + self.stopAlertElevatorLightIval(self.elevatorModelOut) + self.barrelRoom.hideRewardUi() + + def enterBattleIntro(self, ts = 0): + self._movie = CogdoExecutiveSuiteIntro(self.shopOwnerNpc) + self._movie.load() + self._movie.play() + + def exitBattleIntro(self): + self._movie.end() + self.__cleanupPenthouseIntro() + + def __playCloseElevatorOut(self, name, delay = 0): + track = Sequence(Wait(delay + SUIT_LEAVE_ELEVATOR_TIME), Parallel(SoundInterval(self.closeSfx), LerpPosInterval(self.leftDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL), startPos=Point3(0, 0, 0), blendType='easeOut'), LerpPosInterval(self.rightDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL), startPos=Point3(0, 0, 0), blendType='easeOut'))) + track.start() + self.activeIntervals[name] = track + + def enterBattle(self, ts = 0): + if self._wantBarrelRoom and self.elevatorOutOpen == 1: + self.__playCloseElevatorOut(self.uniqueName('close-out-elevator'), delay=2) + camera.setPos(0, -15, 6) + camera.headsUp(self.elevatorModelOut) + + def _showExitElevator(self): + self.elevatorModelOut.reparentTo(self.elevOut) + self.leftDoorOut.setPos(3.5, 0, 0) + self.rightDoorOut.setPos(-3.5, 0, 0) + if not self._wantBarrelRoom and self.elevatorOutOpen == 1: + self.__playCloseElevatorOut(self.uniqueName('close-out-elevator')) + camera.setPos(0, -15, 6) + camera.headsUp(self.elevatorModelOut) + + def exitBattle(self): + if self.elevatorOutOpen == 1: + self.__finishInterval(self.uniqueName('close-out-elevator')) + self.elevatorOutOpen = 0 + + def __playReservesJoining(self, ts, name, callback): + index = 0 + for suit in self.joiningReserves: + suit.reparentTo(render) + suit.setPos(self.elevatorModelOut, Point3(ElevatorPoints[index][0], ElevatorPoints[index][1], ElevatorPoints[index][2])) + index += 1 + suit.setH(180) + suit.loop('neutral') + + track = Sequence(Func(camera.wrtReparentTo, self.elevatorModelOut), Func(camera.setPos, Point3(0, -8, 2)), Func(camera.setHpr, Vec3(0, 10, 0)), Parallel(SoundInterval(self.openSfx), LerpPosInterval(self.leftDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], Point3(0, 0, 0), startPos=ElevatorUtils.getLeftClosePoint(ELEVATOR_NORMAL), blendType='easeOut'), LerpPosInterval(self.rightDoorOut, ElevatorData[ELEVATOR_NORMAL]['closeTime'], Point3(0, 0, 0), startPos=ElevatorUtils.getRightClosePoint(ELEVATOR_NORMAL), blendType='easeOut')), Wait(SUIT_HOLD_ELEVATOR_TIME), Func(camera.wrtReparentTo, render), Func(callback)) + track.start(ts) + self.activeIntervals[name] = track + + def enterReservesJoining(self, ts = 0): + self.__playReservesJoining(ts, self.uniqueName('reserves-joining'), self.__handleReserveJoinDone) + + def __handleReserveJoinDone(self): + self.joiningReserves = [] + self.elevatorOutOpen = 1 + self.d_reserveJoinDone() + + def exitReservesJoining(self): + self.__finishInterval(self.uniqueName('reserves-joining')) + + def enterResting(self, ts = 0): + self._showExitElevator() + self._setAvPosFDC = FrameDelayedCall('setAvPos', self._setAvPosToExit) + if self._wantBarrelRoom: + self.barrelRoom.showBattleAreaLight(True) + base.playMusic(self.waitMusic, looping=1, volume=0.7) + self.__closeInElevator() + self._haveEntranceElevator.set(False) + self._stashEntranceElevator.set(False) + + def _setAvPosToExit(self): + base.localAvatar.setPos(self.elevOut, 0, -22, 0) + base.localAvatar.setHpr(self.elevOut, 0, 0, 0) + base.cr.playGame.getPlace().fsm.request('walk') + + def exitResting(self): + self._setAvPosFDC.destroy() + self.waitMusic.stop() + + def enterReward(self, ts = 0): + if self.isBossFloor(self.currentFloor): + self.penthouseOutroTrack = self.__outroPenthouse() + self.penthouseOutroTrack.start(ts) + else: + self.exitCogdoBuilding() + + def exitReward(self): + self.notify.debug('exitReward') + if self.penthouseOutroTrack: + self.penthouseOutroTrack.finish() + DelayDelete.cleanupDelayDeletes(self.penthouseOutroTrack) + self.penthouseOutroTrack = None + if not self.penthouseOutroChatDoneTrack: + self.notify.debug('exitReward: instanting outroPenthouseChatDone track') + self.__outroPenthouseChatDone() + self.penthouseOutroChatDoneTrack.finish() + self.penthouseOutroChatDoneTrack = None + + def enterFailed(self, ts = 0): + self.exitCogdoBuilding() + + def exitFailed(self): + self.notify.debug('exitFailed()') + self.exitCogdoBuilding() + + def exitCogdoBuilding(self): + if base.localAvatar.hp < 0: + return + base.localAvatar.b_setParent(ToontownGlobals.SPHidden) + request = {'loader': ZoneUtil.getBranchLoaderName(self.extZoneId), + 'where': ZoneUtil.getToonWhereName(self.extZoneId), + 'how': 'elevatorIn', + 'hoodId': ZoneUtil.getHoodId(self.extZoneId), + 'zoneId': self.extZoneId, + 'shardId': None, + 'avId': -1, + 'bldgDoId': self.distBldgDoId} + messenger.send('DSIDoneEvent', [request]) + + def displayBadges(self): + numFloors = self.layout.getNumGameFloors() + if numFloors > 5 or numFloors < 3: + pass + else: + self.notify.warning('Invalid floor number for display badges.') + for player in xrange(len(self.toons)): + goldBadge = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_crg_goldTrophy') + goldBadge.setScale(1.2) + goldNode = render.find('**/gold_0' + str(player + 1)) + goldBadge.reparentTo(goldNode) + for floor in xrange(numFloors): + silverBadge = loader.loadModel('phase_5/models/cogdominium/tt_m_ara_crg_silverTrophy.bam') + silverBadge.setScale(1.2) + silverNode = render.find('**/silver_0' + str(floor * 4 + (player + 1))) + silverBadge.reparentTo(silverNode) + + def __outroPenthouse(self): + avatar = base.localAvatar + trackName = '__outroPenthouse-%d' % avatar.doId + track = Parallel(name=trackName) + base.cr.playGame.getPlace().fsm.request('stopped') + + if self.FOType == 'l': + speech = TTLocalizer.CogdoExecutiveSuiteToonThankYouLawbot + else: + speech = TTLocalizer.CogdoExecutiveSuiteToonThankYou % self.SOSToonName + + track.append(Sequence(Func(camera.wrtReparentTo, localAvatar), + Func(camera.setPos, 0, -9, 9), + Func(camera.lookAt, Point3(5, 15, 0)), + Parallel(self.cage.posInterval(0.75, self.cagePos[1], blendType='easeOut'), + SoundInterval(self.cageLowerSfx, duration=0.5)), + Parallel(self.cageDoor.hprInterval(0.5, VBase3(0, 90, 0), blendType='easeOut'), + Sequence(SoundInterval(self.cageDoorSfx), duration=0)), + Wait(0.25), + Func(self.shopOwnerNpc.wrtReparentTo, render), + Func(self.shopOwnerNpc.setScale, 1), + Func(self.shopOwnerNpc.loop, 'walk'), + Func(self.shopOwnerNpc.headsUp, Point3(0, 10, 0)), + ParallelEndTogether(self.shopOwnerNpc.posInterval(1.5, Point3(0, 10, 0)), self.shopOwnerNpc.hprInterval(0.5, VBase3(180, 0, 0), blendType='easeInOut')), + Func(self.shopOwnerNpc.setChatAbsolute, TTLocalizer.CagedToonYippee, CFSpeech), ActorInterval(self.shopOwnerNpc, 'jump'), + Func(self.shopOwnerNpc.loop, 'neutral'), Func(self.shopOwnerNpc.headsUp, localAvatar), + Func(self.shopOwnerNpc.setLocalPageChat, speech, 0), + Func(camera.lookAt, self.shopOwnerNpc, Point3(0, 0, 2)))) + self.activeIntervals[trackName] = track + self.accept('doneChatPage', self.__outroPenthouseChatDone) + return track + + def __outroPenthouseChatDone(self, elapsed = None): + self.shopOwnerNpc.setChatAbsolute(TTLocalizer.CogdoExecutiveSuiteToonBye, CFSpeech) + self.ignore('doneChatPage') + track = Parallel(Sequence(ActorInterval(self.shopOwnerNpc, 'wave'), Func(self.shopOwnerNpc.loop, 'neutral')), Sequence(Wait(2.0), Func(self.exitCogdoBuilding), Func(base.camLens.setFov, settings['fov']))) + track.start() + self.penthouseOutroChatDoneTrack = track diff --git a/toontown/cogdominium/DistributedCogdoInteriorAI.py b/toontown/cogdominium/DistributedCogdoInteriorAI.py new file mode 100755 index 00000000..daa24ee2 --- /dev/null +++ b/toontown/cogdominium/DistributedCogdoInteriorAI.py @@ -0,0 +1,533 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm import FSM +from direct.task import Timer +from toontown.battle import BattleBase +from toontown.building.ElevatorConstants import * +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase.ToontownBattleGlobals import * +import DistCogdoMazeGameAI, CogdoMazeGameGlobals, DistributedCogdoElevatorIntAI +import DistCogdoFlyingGameAI, DistributedCogdoBarrelAI +from DistributedCogdoBattleBldgAI import DistributedCogdoBattleBldgAI +from SuitPlannerCogdoInteriorAI import SuitPlannerCogdoInteriorAI +from toontown.cogdominium import CogdoBarrelRoomConsts + +from toontown.toon import NPCToons +from toontown.quest import Quests +import random, math + +NUM_FLOORS_DICT = { + 's': 1, + 'l': 2, + 'm':1, + 'c': 1 + } + +BATTLE_INTRO_DURATION = 10 +BARREL_INTRO_DURATION = 12 +BARREL_ROOM_DURATION = 30 +BARREL_ROOM_REWARD_DURATION = 7 + +class DistributedCogdoInteriorAI(DistributedObjectAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedCogdoInteriorAI") + + def __init__(self, air, exterior): + DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'CogdoInteriorAIFSM') + self.toons = filter(None, exterior.elevator.seats[:]) + self.responses = {} + self.bldgDoId = exterior.doId + self.numFloors = NUM_FLOORS_DICT[exterior.track] + self.sosNPC = self.__generateSOS(exterior.difficulty) + self.shopOwnerNpcId = 0 + self.extZoneId, self.zoneId = exterior.getExteriorAndInteriorZoneId() + npcIdList = NPCToons.zone2NpcDict.get(self.zoneId, []) + + if len(npcIdList) == 0: + self.notify.info('No NPC in taken cogdo at %s' % self.zoneId) + else: + if len(npcIdList) > 1: + self.notify.warning('Multiple NPCs in taken cogdo at %s' % self.zoneId) + + self.shopOwnerNpcId = npcIdList[0] + + self.gameDone = 0 + self.bossBattleDone = 0 + self.curFloor = 0 + self.topFloor = 2 + self.timer = Timer.Timer() + self.exterior = exterior + self.planner = self.exterior.planner + self.savedByMap = { } + self.battle = None + self.FOType = exterior.track + self.gameFloor = 1 + self.battleFloor = 2 + self.barrelFloor = -1 + + if self.FOType == 'l': + self.battleFloor = 3 + self.barrelFloor = 2 + self.topFloor += 1 + + self.toonSkillPtsGained = { } + self.toonExp = { } + self.toonOrigQuests = { } + self.toonItems = { } + self.toonOrigMerits = { } + self.toonMerits = { } + self.toonParts = { } + self.helpfulToons = [] + self.barrels = [] + self.suits = [] + self.activeSuits = [] + self.reserveSuits = [] + self.joinedReserves = [] + self.suitsKilled = [] + self.suitsKilledPerFloor = [] + self.ignoreResponses = 0 + self.ignoreElevatorDone = 0 + self.ignoreReserveJoinDone = 0 + + def __generateSOS(self, difficulty): + g = lambda: random.choice(NPCToons.FOnpcFriends.keys()) + v = g() + + getStars = lambda x: NPCToons.getNPCTrackLevelHpRarity(x)[-1] + + maxStars = min(2, int(math.ceil(difficulty / 5.))) + minStars = max(0, maxStars - 1) + + while not (minStars <= getStars(v) <= maxStars): + v = g() + + self.notify.info('selected SOS %s (stars = %s)' % (v, getStars(v))) + return v + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def getZoneId(self): + return self.zoneId + + def setExtZoneId(self, extZoneId): + self.extZoneId = extZoneId + + def getExtZoneId(self): + return self.extZoneId + + def setDistBldgDoId(self, bldgDoId): + self.bldgDoId = bldgDoId + + def getDistBldgDoId(self): + return self.bldgDoId + + def setNumFloors(self, numFloors): + self.numFloors = numFloors + + def getNumFloors(self): + return self.numFloors + + def setShopOwnerNpcId(self, id): + self.shopOwnerNpcId = id + + def getShopOwnerNpcId(self): + return self.shopOwnerNpcId + + def setState(self, state, timestamp): + self.request(state) + + def getState(self): + timestamp = globalClockDelta.getRealNetworkTime() + return [self.state, timestamp] + + def b_setState(self, state): + self.setState(state, 0) + self.d_setState(state) + + def d_setState(self, state): + timestamp = globalClockDelta.getRealNetworkTime() + self.sendUpdate('setState', [state, timestamp]) + + def reserveJoinDone(self): + toonId = self.air.getAvatarIdFromSender() + if self.ignoreResponses == 1: + return None + elif self.toons.count(toonId) == 0: + self.notify.warning('reserveJoinDone() - toon not in list: %d' % toonId) + return None + self.b_setState('Battle') + + def elevatorDone(self): + toonId = self.air.getAvatarIdFromSender() + if self.ignoreResponses == 1: + return None + elif self.toons.count(toonId) == 0: + self.notify.warning('elevatorDone() - toon not in toon list: %d' % toonId) + + def enterWaitForAllToonsInside(self): + self.resetResponses() + + if self.FOType == "s": + self.game = DistCogdoMazeGameAI.DistCogdoMazeGameAI(self.air) + self.game.setNumSuits(CogdoMazeGameGlobals.NumSuits) + elif self.FOType == "l": + self.game = DistCogdoFlyingGameAI.DistCogdoFlyingGameAI(self.air) + elif self.FOType == "m": + self.game = DistCogdoCraneGameAI.DistCogdoCraneGameAI(self.air) + + self.sendUpdate("setSOSNpcId", [self.sosNPC]) + self.sendUpdate("setFOType", [ord(self.FOType)]) + + def resetResponses(self): + for toon in self.toons: + self.responses[toon] = 0 + + def setAvatarJoined(self): + avId = self.air.getAvatarIdFromSender() + self.responses[avId] = 1 + avatar = self.air.doId2do.get(avId) + if avatar != None: + self.savedByMap[avId] = (avatar.getName(), avatar.dna.asTuple()) + self.addToon(avId) + if self.allToonsJoined(): + self.request('Elevator') + + def addToon(self, avId): + if not avId in self.toons: + self.toons.append(avId) + + if avId in self.air.doId2do: + event = self.air.getAvatarExitEvent(avId) + self.accept(event, self.__handleUnexpectedExit, [avId]) + + def __handleUnexpectedExit(self, avId): + self.removeToon(avId) + if len(self.toons) == 0: + self.exterior.deleteSuitInterior() + if self.battle: + self.battle.requestDelete() + self.battle = None + + def removeToon(self, avId): + if avId in self.toons: self.toons.pop(avId) + + def enterElevator(self): + self.curFloor += 1 + self.d_setToons() + self.resetResponses() + + if self.curFloor == self.gameFloor: + self.enterGame() + + self.d_setState('Elevator') + self.timer.stop() + self.timer.startCallback(BattleBase.ELEVATOR_T + ElevatorData[ELEVATOR_FIELD]['openTime'], self.serverElevatorDone) + + if self.curFloor == self.battleFloor: + self.planner.myPrint() + suitHandles = self.planner.genFloorSuits(0) + self.suits = suitHandles['activeSuits'] + self.activeSuits = self.suits[:] + self.reserveSuits = suitHandles['reserveSuits'] + self.d_setSuits() + + def exitElevator(self): + self.timer.stop() + + def serverElevatorDone(self): + if self.curFloor == self.gameFloor: + self.d_setState('Game') + elif self.curFloor == self.battleFloor: + self.b_setState('BattleIntro') + self.timer.startCallback(BATTLE_INTRO_DURATION, self.battleIntroDone) + else: + self.notify.warning('Unknown floor %s (track=%s)' % (self.curFloor, self.FOType)) + + def battleIntroDone(self): + if self.air: + self.createBattle() + self.b_setState('Battle') + + def barrelIntroDone(self): + if not self.air: + return + + self.b_setState('CollectBarrels') + for i in xrange(len(CogdoBarrelRoomConsts.BarrelProps)): + barrel = DistributedCogdoBarrelAI.DistributedCogdoBarrelAI(self.air, i) + barrel.generateWithRequired(self.zoneId) + self.barrels.append(barrel) + self.timer.startCallback(BARREL_ROOM_DURATION, self.barrelReward) + + def barrelReward(self): + if not self.air: + return + + self.b_setState('BarrelRoomReward') + for i in self.barrels: + i.requestDelete() + self.timer.startCallback(BARREL_ROOM_REWARD_DURATION, self.barrelRewardDone) + + def barrelRewardDone(self): + if not self.air: + return + barrelPlanner = SuitPlannerCogdoInteriorAI(self.exterior._cogdoLayout, max(0, self.exterior.difficulty - 5), + self.FOType, self.exterior.getExteriorAndInteriorZoneId()[1]) + barrelPlanner.myPrint() + suitHandles = barrelPlanner.genFloorSuits(0) + self.suits = suitHandles['activeSuits'] + self.activeSuits = self.suits[:] + self.reserveSuits = suitHandles['reserveSuits'] + self.d_setSuits() + self.battleIntroDone() + + def handleAllAboard(self, seats): + if not hasattr(self, 'air') or not self.air: + return None + + numOfEmptySeats = seats.count(None) + if numOfEmptySeats == 4: + self.exterior.deleteSuitInterior() + return + elif not 0 <= numOfEmptySeats <= 3: + self.notify.error('Bad number of empty seats: %s' % numOfEmptySeats) + + for toon in self.toons: + if toon not in seats: + self.removeToon(toon) + + self.toons = filter(None, seats) + self.d_setToons() + self.request('Elevator') + + def enterGame(self): + self.game.setToons(self.toons) + self.game.setInteriorId(self.doId) + self.game.setExteriorZone(self.exterior.zoneId) + self.game.setDifficultyOverrides(2147483647, -1) + self.game.generateWithRequired(self.zoneId) + self.game.d_startIntro() + self.accept(self.game.finishEvent, self.__handleGameDone) + self.accept(self.game.gameOverEvent, self.__handleGameOver) + + def __handleGameDone(self, toons): + self.game.requestDelete() + self.gameDone = 1 + self.toons = toons + if self.curFloor == self.barrelFloor - 1: + self.curFloor += 1 + self.d_setToons() + self.resetResponses() + self.b_setState('BarrelRoomIntro') + self.timer.startCallback(BARREL_INTRO_DURATION, self.barrelIntroDone) + else: + self.request('Elevator') + + def __handleGameOver(self): + self.game.requestDelete() + self.exterior.deleteSuitInterior() + + def createBattle(self): + isBoss = self.curFloor == self.topFloor + self.battle = DistributedCogdoBattleBldgAI(self.air, self.zoneId, self.__handleRoundDone, self.__handleBattleDone, bossBattle = isBoss) + self.battle.suitsKilled = self.suitsKilled + self.battle.suitsKilledPerFloor = self.suitsKilledPerFloor + self.battle.battleCalc.toonSkillPtsGained = self.toonSkillPtsGained + self.battle.toonExp = self.toonExp + self.battle.toonOrigQuests = self.toonOrigQuests + self.battle.toonItems = self.toonItems + self.battle.toonOrigMerits = self.toonOrigMerits + self.battle.toonMerits = self.toonMerits + self.battle.toonParts = self.toonParts + self.battle.helpfulToons = self.helpfulToons + self.battle.setInitialMembers(self.toons, self.suits) + self.battle.generateWithRequired(self.zoneId) + mult = getCreditMultiplier(self.curFloor) + self.battle.battleCalc.setSkillCreditMultiplier(self.battle.battleCalc.getSkillCreditMultiplier() * mult) + + def enterBattleDone(self, toonIds): + toonIds = toonIds[0] + if len(toonIds) != len(self.toons): + deadToons = [] + for toon in self.toons: + if toonIds.count(toon) == 0: + deadToons.append(toon) + continue + for toon in deadToons: + self.removeToon(toon) + + self.d_setToons() + if len(self.toons) == 0: + self.exterior.deleteSuitInterior() + elif self.curFloor == self.topFloor: + self.battle.resume(self.curFloor, topFloor = 1) + else: + self.battle.resume(self.curFloor, topFloor = 0) + + def __doDeleteInterior(self, task): + self.exterior.deleteSuitInterior() + return task.done + + def exitBattleDone(self): + self.cleanupFloorBattle() + + def cleanupFloorBattle(self): + for suit in self.suits: + if suit.isDeleted(): + continue + suit.requestDelete() + + self.suits = [] + self.reserveSuits = [] + self.activeSuits = [] + if self.battle != None: + self.battle.requestDelete() + + self.battle = None + + def __handleRoundDone(self, toonIds, totalHp, deadSuits): + totalMaxHp = 0 + for suit in self.suits: + totalMaxHp += suit.maxHP + + for suit in deadSuits: + self.activeSuits.remove(suit) + + if len(self.reserveSuits) > 0 and len(self.activeSuits) < 4: + self.joinedReserves = [] + hpPercent = 100 - (totalHp / totalMaxHp) * 100.0 + for info in self.reserveSuits: + if info[1] <= hpPercent and len(self.activeSuits) < 4: + self.suits.append(info[0]) + self.activeSuits.append(info[0]) + self.joinedReserves.append(info) + continue + + for info in self.joinedReserves: + self.reserveSuits.remove(info) + + if len(self.joinedReserves) > 0: + self.d_setSuits() + self.request('ReservesJoining') + return + + if len(self.activeSuits) == 0: + self.request('BattleDone', [ + toonIds]) + else: + self.battle.resume() + + def enterReservesJoining(self): + self.resetResponses() + self.timer.startCallback(ElevatorData[ELEVATOR_FIELD]['openTime'] + SUIT_HOLD_ELEVATOR_TIME + BattleBase.SERVER_BUFFER_TIME, self.serverReserveJoinDone) + + def exitReservesJoining(self): + self.timer.stop() + self.resetResponses() + for info in self.joinedReserves: + self.battle.suitRequestJoin(info[0]) + + self.battle.resume() + self.joinedReserves = [] + + def serverReserveJoinDone(self): + self.ignoreReserveJoinDone = 1 + self.b_setState('Battle') + + def __handleBattleDone(self, zoneId, toonIds): + if len(toonIds) == 0: + taskMgr.doMethodLater(10, self.__doDeleteInterior, self.taskName('deleteInterior')) + elif self.curFloor == self.topFloor: + self.request('Reward') + else: + self.b_setState('Resting') + + def enterResting(self): + self.intElevator = DistributedCogdoElevatorIntAI.DistributedCogdoElevatorIntAI(self.air, self, self.toons) + self.intElevator.generateWithRequired(self.zoneId) + + def exitResting(self): + self.intElevator.requestDelete() + + def enterReward(self): + victors = self.toons[:] + savedBy = [] + for v in victors: + tuple = self.savedByMap.get(v) + if tuple: + savedBy.append([ + v, + tuple[0], + tuple[1]]) + + toon = self.air.doId2do.get(v) + if toon: + if self.FOType == 's': + if not toon.attemptAddNPCFriend(self.sosNPC, Quests.InFO): + self.notify.info('%s unable to add NPCFriend %s to %s.' % (self.doId, self.sosNPC, v)) + elif self.FOType == 'l': + reward = self.getEmblemsReward() + toon.addEmblems(reward) + else: + self.notify.warning('%s unable to reward %s: unknown reward for track %s' % (self.doId, v, self.FOType)) + + self.exterior.fsm.request('waitForVictorsFromCogdo', [ + victors, + savedBy]) + self.d_setState('Reward') + + def removeToon(self, toonId): + if self.toons.count(toonId): + self.toons.remove(toonId) + + def d_setToons(self): + self.sendUpdate('setToons', self.getToons()) + + def getToons(self): + return [self.toons, 0] + + def d_setSuits(self): + self.sendUpdate('setSuits', self.getSuits()) + + def getSuits(self): + suitIds = [] + for suit in self.activeSuits: + suitIds.append(suit.doId) + + reserveIds = [] + values = [] + for info in self.reserveSuits: + reserveIds.append(info[0].doId) + values.append(info[1]) + + return [ + suitIds, + reserveIds, + values] + + def allToonsJoined(self): + for toon in self.toons: + if self.responses[toon] == 0: + return 0 + return 1 + + def delete(self): + DistributedObjectAI.delete(self) + self.timer.stop() + + def getEmblemsReward(self): + hoodIdMap = {2: .5, # Toontown Central + 1: 1., # Donald's Dock + 5: 1.5, # Daisy Gardens + 4: 2., # Minnie's Melodyland + 3: 2.7, # The Brrrgh + 9: 3.5 # Donald's Dreamland + } + + hoodValue = hoodIdMap[int(self.exterior.zoneId // 1000)] + diff = max(self.exterior.difficulty, 1) + memos = self.game.getTotalMemos() + E = (hoodValue * max(memos, 1) * diff) / 2.5 + return divmod(E, 100)[::-1] diff --git a/toontown/cogdominium/SuitPlannerCogdoInteriorAI.py b/toontown/cogdominium/SuitPlannerCogdoInteriorAI.py new file mode 100755 index 00000000..197fe84e --- /dev/null +++ b/toontown/cogdominium/SuitPlannerCogdoInteriorAI.py @@ -0,0 +1,211 @@ +from otp.ai.AIBaseGlobal import * +from toontown.suit import SuitDNA +from direct.directnotify import DirectNotifyGlobal +from toontown.suit import DistributedSuitAI +from toontown.building import SuitBuildingGlobals +from toontown.suit.SuitInvasionGlobals import IFSkelecog, IFWaiter, IFV2 +import types, math, random + +BASE_RESERVE = 10 + +MAX_RESERVES = { + 's': BASE_RESERVE * .9, + 'm': BASE_RESERVE * 1.1, + 'l': BASE_RESERVE * 1.25, + 'c': BASE_RESERVE * 1.5, + } + +def filterReviveChance(track, revive): + if revive >= 0: + return revive + + return random.randint(config.GetInt('min-lt-vs', 0), config.GetInt('max-lt-vs', 2)) + # Implements difficulty 19 / LT. + +def getMaxReserves(track): + return int(math.ceil(MAX_RESERVES[track])) + +class SuitPlannerCogdoInteriorAI: + notify = DirectNotifyGlobal.directNotify.newCategory('SuitPlannerCogdoInteriorAI') + + def __init__(self, layout, difficulty, track, zoneId, numFloors = 1): + self.zoneId = zoneId + self.numFloors = layout.getNumFloors() + difficulty = min(difficulty + 4, len(SuitBuildingGlobals.SuitBuildingInfo) - 1) + self.respectInvasions = 1 + + if isinstance(difficulty, types.StringType): + self.notify.warning('difficulty is a string!') + difficulty = int(difficulty) + + self._genSuitInfos(numFloors, difficulty, track) + + def __genJoinChances(self, num): + joinChances = [] + for currChance in xrange(num): + joinChances.append(random.randint(1, 100)) + + joinChances.sort(cmp) + return joinChances + + def _genSuitInfos(self, numFloors, difficulty, bldgTrack): + self.suitInfos = [] + self.notify.debug('\n\ngenerating suitsInfos with numFloors (' + str(numFloors) + ') difficulty (' + str(difficulty) + '+1) and bldgTrack (' + str(bldgTrack) + ')') + for currFloor in xrange(numFloors): + infoDict = {} + lvls = self.__genLevelList(difficulty, currFloor, numFloors) + activeDicts = [] + numActive = random.randint(1, min(4, len(lvls))) + + if currFloor + 1 == numFloors and len(lvls) > 1: + origBossSpot = len(lvls) - 1 + + if numActive == 1: + newBossSpot = numActive - 1 + else: + newBossSpot = numActive - 2 + + tmp = lvls[newBossSpot] + lvls[newBossSpot] = lvls[origBossSpot] + lvls[origBossSpot] = tmp + + bldgInfo = SuitBuildingGlobals.SuitBuildingInfo[difficulty] + + if len(bldgInfo) > SuitBuildingGlobals.SUIT_BLDG_INFO_REVIVES: + revives = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_REVIVES][0] + else: + revives = 0 + + for currActive in xrange(numActive - 1, -1, -1): + level = lvls[currActive] + type = self.__genNormalSuitType(level) + activeDict = {} + activeDict['type'] = type + activeDict['track'] = bldgTrack + activeDict['level'] = level + activeDict['revives'] = filterReviveChance(bldgTrack, revives) + activeDicts.append(activeDict) + + infoDict['activeSuits'] = activeDicts + reserveDicts = [] + numReserve = min(len(lvls) - numActive, getMaxReserves(bldgTrack)) + joinChances = self.__genJoinChances(numReserve) + for currReserve in xrange(numReserve): + level = lvls[currReserve + numActive] + type = self.__genNormalSuitType(level) + reserveDict = {} + reserveDict['type'] = type + reserveDict['track'] = bldgTrack + reserveDict['level'] = level + reserveDict['revives'] = filterReviveChance(bldgTrack, revives) + reserveDict['joinChance'] = joinChances[currReserve] + reserveDicts.append(reserveDict) + + infoDict['reserveSuits'] = reserveDicts + self.suitInfos.append(infoDict) + + def __genNormalSuitType(self, lvl): + return SuitDNA.getRandomSuitType(lvl) + + def __genLevelList(self, difficulty, currFloor, numFloors): + bldgInfo = SuitBuildingGlobals.SuitBuildingInfo[difficulty] + + lvlPoolRange = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_LVL_POOL] + maxFloors = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_FLOORS][1] + lvlPoolMults = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_LVL_POOL_MULTS] + floorIdx = min(currFloor, maxFloors - 1) + lvlPoolMin = lvlPoolRange[0] * lvlPoolMults[floorIdx] + lvlPoolMax = lvlPoolRange[1] * lvlPoolMults[floorIdx] + lvlPool = random.randint(int(lvlPoolMin), int(lvlPoolMax)) + lvlMin = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_SUIT_LVLS][0] + lvlMax = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_SUIT_LVLS][1] + self.notify.debug('Level Pool: ' + str(lvlPool)) + lvlList = [] + while lvlPool >= lvlMin: + newLvl = random.randint(lvlMin, min(lvlPool, lvlMax)) + lvlList.append(newLvl) + lvlPool -= newLvl + + if currFloor + 1 == numFloors: + bossLvlRange = bldgInfo[SuitBuildingGlobals.SUIT_BLDG_INFO_BOSS_LVLS] + newLvl = random.randint(bossLvlRange[0], bossLvlRange[1]) + lvlList.append(newLvl) + lvlList.sort(cmp) + self.notify.debug('LevelList: ' + repr(lvlList)) + return lvlList + + def __setupSuitInfo(self, suit, bldgTrack, suitLevel, suitType): + suitDeptIndex, suitTypeIndex, flags = simbase.air.suitInvasionManager.getInvadingCog() + if self.respectInvasions: + if suitDeptIndex is not None: + bldgTrack = SuitDNA.suitDepts[suitDeptIndex] + if suitTypeIndex is not None: + suitName = SuitDNA.getSuitName(suitDeptIndex, suitTypeIndex) + suitType = SuitDNA.getSuitType(suitName) + suitLevel = min(max(suitLevel, suitType), suitType + 4) + dna = SuitDNA.SuitDNA() + dna.newSuitRandom(suitType, bldgTrack) + suit.dna = dna + self.notify.debug('Creating suit type ' + suit.dna.name + ' of level ' + str(suitLevel) + ' from type ' + str(suitType) + ' and track ' + str(bldgTrack)) + suit.setLevel(suitLevel) + return flags + + def __genSuitObject(self, suitZone, suitType, bldgTrack, suitLevel, revives = 0): + newSuit = DistributedSuitAI.DistributedSuitAI(simbase.air, None) + flags = self.__setupSuitInfo(newSuit, bldgTrack, suitLevel, suitType) + if flags & IFSkelecog: + newSuit.setSkelecog(1) + newSuit.setSkeleRevives(revives) + newSuit.generateWithRequired(suitZone) + if flags & IFWaiter: + newSuit.b_setWaiter(1) + if flags & IFV2: + newSuit.b_setSkeleRevives(1) + newSuit.node().setName('suit-%s' % newSuit.doId) + return newSuit + + def myPrint(self): + print 'Generated suits for cogdo: ' + + for floor, currInfo in enumerate(self.suitInfos): + floor += 1 + + actives = currInfo['activeSuits'] + reserves = currInfo['reserveSuits'] + + print ' Floor %d has %d active suits.' % (floor, len(actives)) + print ' Floor %d has %d reserve suits.' % (floor, len(reserves)) + + for idx, currActive in enumerate(actives): + type, track, level, revives = map(lambda x: currActive[x], ('type', 'track', 'level', 'revives')) + + print '-- Active suit %d is %s, %s and level %d and revives is %d' % (idx, type, track, level, revives) + + for idx, currReserve in enumerate(reserves): + type, track, level, revives, res = map(lambda x: currReserve[x], ('type', 'track', 'level', 'revives', 'joinChance')) + print '- Reserve suit %d is %s, %s and level %d and JC = %d and revives is %d' % (idx, type, track, level, res, revives) + + def genFloorSuits(self, floor): + suitHandles = {} + floorInfo = self.suitInfos[floor] + activeSuits = [] + for activeSuitInfo in floorInfo['activeSuits']: + suit = self.__genSuitObject(self.zoneId, activeSuitInfo['type'], activeSuitInfo['track'], activeSuitInfo['level'], activeSuitInfo['revives']) + activeSuits.append(suit) + + suitHandles['activeSuits'] = activeSuits + reserveSuits = [] + for reserveSuitInfo in floorInfo['reserveSuits']: + suit = self.__genSuitObject(self.zoneId, reserveSuitInfo['type'], reserveSuitInfo['track'], reserveSuitInfo['level'], reserveSuitInfo['revives']) + reserveSuits.append((suit, reserveSuitInfo['joinChance'])) + + suitHandles['reserveSuits'] = reserveSuits + return suitHandles + + def genSuits(self): + suitHandles = [] + for floor in xrange(len(self.suitInfos)): + floorSuitHandles = self.genFloorSuits(floor) + suitHandles.append(floorSuitHandles) + + return suitHandles diff --git a/toontown/cogdominium/__init__.py b/toontown/cogdominium/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/coghq/ActiveCell.py b/toontown/coghq/ActiveCell.py new file mode 100755 index 00000000..6463f6a0 --- /dev/null +++ b/toontown/coghq/ActiveCell.py @@ -0,0 +1,34 @@ +from panda3d.core import * +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal + +class ActiveCell(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('ActiveCell') + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.occupantId = -1 + self.state = 0 + + def announceGenerate(self): + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.loadModel() + + def loadModel(self): + if 0 and __debug__: + grid = self.level.entities.get(self.gridId, None) + if grid: + pos = grid.getPos() + Vec3(self.col * grid.cellSize, self.row * grid.cellSize, 0) + model = loader.loadModel('phase_5/models/modules/suit_walls.bam') + model.setScale(grid.cellSize, 1, grid.cellSize) + model.setP(-90) + model.flattenMedium() + model.setZ(0.05) + model.setColorScale(1, 0, 0, 0.5) + model.copyTo(self) + self.setPos(pos) + return + + def setState(self, state, objId): + self.state = state + self.occupantId = objId diff --git a/toontown/coghq/ActiveCellAI.py b/toontown/coghq/ActiveCellAI.py new file mode 100755 index 00000000..aeb34862 --- /dev/null +++ b/toontown/coghq/ActiveCellAI.py @@ -0,0 +1,55 @@ +from otp.level import DistributedEntityAI +from direct.directnotify import DirectNotifyGlobal + +class ActiveCellAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('ActiveCellAI') + + def __init__(self, level, entId): + self.state = 0 + self.grid = None + self.occupantIds = [] + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + + def setGrid(gridId = self.gridId, self = self): + self.grid = self.level.entities.get(gridId, None) + if self.grid: + self.grid.addActiveCell(self) + return 1 + return 0 + + if not setGrid(): + self.accept(self.level.getEntityCreateEvent(self.gridId), setGrid) + return + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + + def delete(self): + self.notify.debug('delete') + self.ignoreAll() + DistributedEntityAI.DistributedEntityAI.delete(self) + + def getState(self): + return self.state + + def b_setState(self, state, objId = None): + self.setState(state, objId) + self.d_setState(state, objId) + + def d_setState(self, state, objId = None): + if not objId: + objId = 0 + self.sendUpdate('setState', [state, objId]) + + def setState(self, state, objId = None): + self.state = state + if state: + self.occupantIds.append(objId) + else: + try: + self.occupantIds.remove(objId) + except: + self.notify.warning("couldn't remove %s from active cell" % objId) + + def getRowCol(self): + return [self.row, self.col] diff --git a/toontown/coghq/BanquetTableBase.py b/toontown/coghq/BanquetTableBase.py new file mode 100755 index 00000000..0659460a --- /dev/null +++ b/toontown/coghq/BanquetTableBase.py @@ -0,0 +1,9 @@ + + +class BanquetTableBase: + HUNGRY = 1 + DEAD = 0 + EATING = 2 + ANGRY = 3 + HIDDEN = 4 + INACTIVE = 5 diff --git a/toontown/coghq/BarrelBase.py b/toontown/coghq/BarrelBase.py new file mode 100755 index 00000000..a269455b --- /dev/null +++ b/toontown/coghq/BarrelBase.py @@ -0,0 +1,31 @@ +import random + +class BarrelBase: + + def getRng(self): + return random.Random(self.entId * self.level.doId) + + def getRewardPerGrab(self): + if not hasattr(self, '_reward'): + if self.rewardPerGrabMax > self.rewardPerGrab: + self._reward = self.getRng().randrange(self.rewardPerGrab, self.rewardPerGrabMax + 1) + else: + self._reward = self.rewardPerGrab + return self._reward + + def getGagLevel(self): + if not hasattr(self, '_gagLevel'): + if self.gagLevelMax > self.gagLevel: + self._gagLevel = self.getRng().randrange(self.gagLevel, self.gagLevelMax + 1) + else: + self._gagLevel = self.gagLevel + return self._gagLevel + + def getGagTrack(self): + if not hasattr(self, '_gagTrack'): + if self.gagTrack == 'random': + tracks = (0, 1, 2, 3, 4, 4, 5, 5, 6) + self._gagTrack = self.getRng().choice(tracks) + else: + self._gagTrack = self.gagTrack + return self._gagTrack diff --git a/toontown/coghq/BattleBlocker.py b/toontown/coghq/BattleBlocker.py new file mode 100755 index 00000000..223d973b --- /dev/null +++ b/toontown/coghq/BattleBlocker.py @@ -0,0 +1,88 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from otp.level import BasicEntities +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal + +class BattleBlocker(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('BattleBlocker') + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.suitIds = [] + self.battleId = None + return + + def setActive(self, active): + self.active = active + + def announceGenerate(self): + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.initCollisionGeom() + + def disable(self): + self.ignoreAll() + self.unloadCollisionGeom() + BasicEntities.DistributedNodePathEntity.disable(self) + + def destroy(self): + BasicEntities.DistributedNodePathEntity.destroy(self) + + def setSuits(self, suitIds): + self.suitIds = suitIds + + def setBattle(self, battleId): + self.battleId = battleId + + def setBattleFinished(self): + self.ignoreAll() + + def initCollisionGeom(self): + self.cSphere = CollisionSphere(0, 0, 0, self.radius) + self.cSphereNode = CollisionNode('battleBlocker-%s-%s' % (self.level.getLevelId(), self.entId)) + self.cSphereNode.addSolid(self.cSphere) + self.cSphereNodePath = self.attachNewNode(self.cSphereNode) + self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + self.cSphere.setTangible(0) + self.enterEvent = 'enter' + self.cSphereNode.getName() + self.accept(self.enterEvent, self.__handleToonEnter) + + def unloadCollisionGeom(self): + if hasattr(self, 'cSphereNodePath'): + self.ignore(self.enterEvent) + del self.cSphere + del self.cSphereNode + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + + def __handleToonEnter(self, collEntry): + self.notify.debug('__handleToonEnter, %s' % self.entId) + self.startBattle() + + def startBattle(self): + if not self.active: + return + callback = None + if self.battleId != None and self.battleId in base.cr.doId2do: + battle = base.cr.doId2do.get(self.battleId) + if battle: + self.notify.debug('act like we collided with battle %d' % self.battleId) + callback = battle.handleBattleBlockerCollision + elif len(self.suitIds) > 0: + for suitId in self.suitIds: + suit = base.cr.doId2do.get(suitId) + if suit: + self.notify.debug('act like we collided with Suit %d ( in state %s )' % (suitId, suit.fsm.getCurrentState().getName())) + callback = suit.handleBattleBlockerCollision + break + + self.showReaction(callback) + return + + def showReaction(self, callback = None): + if not base.localAvatar.wantBattles: + return + track = Sequence() + if callback: + track.append(Func(callback)) + track.start() diff --git a/toontown/coghq/BattleBlockerAI.py b/toontown/coghq/BattleBlockerAI.py new file mode 100755 index 00000000..04e1b4b5 --- /dev/null +++ b/toontown/coghq/BattleBlockerAI.py @@ -0,0 +1,66 @@ +from otp.level import DistributedEntityAI +from direct.directnotify import DirectNotifyGlobal + +class BattleBlockerAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('BattleBlockerAI') + + def __init__(self, level, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.suitIds = [] + self.active = 1 + + def destroy(self): + self.notify.debug('delete') + self.ignoreAll() + DistributedEntityAI.DistributedEntityAI.destroy(self) + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + self.accept('plannerCreated-' + str(self.level.doId), self.registerBlocker) + + def registerBlocker(self): + if hasattr(self.level, 'planner'): + self.level.planner.battleMgr.addBattleBlocker(self, self.cellId) + + def deactivate(self): + if self.isDeleted(): + return + self.active = 0 + self.sendUpdate('setActive', [self.active]) + + def getActive(self): + return self.active + + def addSuit(self, suit): + self.suitIds.append(suit.doId) + self.d_setSuits() + + def removeSuit(self, suit): + try: + self.suitIds.remove(suit.doId) + self.d_setSuits() + except: + self.notify.debug("didn't have suitId %d" % suit.doId) + + def d_setSuits(self): + self.sendUpdate('setSuits', [self.suitIds]) + + def b_setBattle(self, battleId): + self.battle = battleId + self.d_setBattle(battleId) + + def d_setBattle(self, battleId): + self.sendUpdate('setBattle', [battleId]) + + def b_setBattleFinished(self): + self.deactivate() + self.setBattleFinished() + self.d_setBattleFinished() + + def setBattleFinished(self): + self.notify.debug('setBattleFinished: %s' % self.entId) + messenger.send('battleBlockerFinished-' + str(self.entId)) + messenger.send(self.getOutputEventName(), [1]) + + def d_setBattleFinished(self): + self.sendUpdate('setBattleFinished', []) diff --git a/toontown/coghq/BattleExperienceAggregatorAI.py b/toontown/coghq/BattleExperienceAggregatorAI.py new file mode 100755 index 00000000..bdbacae7 --- /dev/null +++ b/toontown/coghq/BattleExperienceAggregatorAI.py @@ -0,0 +1,27 @@ + + +class BattleExperienceAggregatorAI: + + def __init__(self): + self.suitsKilled = [] + self.suitsKilledPerFloor = [] + self.toonSkillPtsGained = {} + self.toonExp = {} + self.toonOrigQuests = {} + self.toonItems = {} + self.toonOrigMerits = {} + self.toonMerits = {} + self.toonParts = {} + self.helpfulToons = [] + + def attachToBattle(self, battle): + battle.suitsKilled = self.suitsKilled + battle.suitsKilledPerFloor = self.suitsKilledPerFloor + battle.battleCalc.toonSkillPtsGained = self.toonSkillPtsGained + battle.toonExp = self.toonExp + battle.toonOrigQuests = self.toonOrigQuests + battle.toonItems = self.toonItems + battle.toonOrigMerits = self.toonOrigMerits + battle.toonMerits = self.toonMerits + battle.toonParts = self.toonParts + battle.helpfulToons = self.helpfulToons diff --git a/toontown/coghq/BossLobbyGui.py b/toontown/coghq/BossLobbyGui.py new file mode 100644 index 00000000..66e8bca9 --- /dev/null +++ b/toontown/coghq/BossLobbyGui.py @@ -0,0 +1,378 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class BossLobbyGui(DirectFrame): + + class InitialFrame(DirectFrame): + frame = 0 + + class LobbySelection(DirectButton): + + def __init__(self, parent, **kw): + optiondefs = ( + ('relief', None, None), + ('image_scale', 0.55, None), + ('text_pos', (0.3, -0.0225), None), + ('text_scale', 0.075, None), + ) + self.defineoptions(kw, optiondefs) + DirectButton.__init__(self, relief=None) + self.initialiseoptions(BossLobbyGui.InitialFrame.LobbySelection) + + def __init__(self, parent, callback, **kw): + optiondefs = ( + ('relief', None, None), + ('state', DGG.NORMAL, None), + ('image', DGG.getDefaultDialogGeom(), None), + ('image_scale', (1.0, 1.0, 0.75), None), + ('image_color', ToontownGlobals.GlobalDialogColor, None), + ('pos', (0, 0, 0), None), + ) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(BossLobbyGui.InitialFrame) + self.callback = callback + self.selection = -1 + self.load() + + def destroy(self): + if hasattr(self, 'title') and self.title: + self.title.destroy() + del self.title + if hasattr(self, 'buttons') and len(self.buttons): + for button in self.buttons: + button.destroy() + del self.buttons + if hasattr(self, 'okButton') and self.okButton: + self.okButton.destroy() + del self.okButton + if hasattr(self, 'cancelButton') and self.cancelButton: + self.cancelButton.destroy() + del self.cancelButton + DirectFrame.destroy(self) + + def load(self): + empty = loader.loadModel("phase_3.5/models/gui/matching_game_gui.bam") + buttons = loader.loadModel("phase_3/models/gui/dialog_box_buttons_gui") + self.emptyList = (empty.find("**/minnieCircle"), empty.find("**/minnieCircle"), empty.find("**/minnieCircle")) + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + empty.removeNode() + buttons.removeNode() + self.title = DirectLabel(parent=self, relief=None, text="Select a Lobby", textMayChange=1, text_scale=0.1, pos=(0, 0, 0.25)) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, pos=(-0.1, 0, -0.275), command=self.nextFrame, extraArgs=[True]) + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(0.1, 0, -0.275), command=self.nextFrame, extraArgs=[False]) + self.buttons = [] + public = BossLobbyGui.InitialFrame.LobbySelection(self, image=self.emptyList, pos=(-0.35, 0, 0.075), text="Public", command=self.setSelection, extraArgs=[0]) + private = BossLobbyGui.InitialFrame.LobbySelection(self, image=self.emptyList, pos=(-0.3475, 0, -0.075), text="Private", command=self.setSelection, extraArgs=[1]) + self.buttons.extend([ + public, + private + ]) + + def setSelection(self, buttonId): + newSelection = self.buttons[buttonId] + if newSelection: + for button in self.buttons: + button.setColor(1, 1, 1, 1) + newSelection.setColor(0, 1, 0, 1) + self.selection = buttonId + + def getSelection(self): + return self.selection + + def nextFrame(self, status): + if status and self.getSelection() >= 0: + options = { + 'lobbyType': self.getSelection() + } + self.callback(self.frame + 1, options) + else: + self.callback(-1) + + class SecondaryFrame(DirectFrame): + frame = 1 + + class LobbyList(DirectScrolledList): + + def __init__(self, parent, **kw): + buttons = loader.loadModel("phase_3/models/gui/tt_m_gui_mat_mainGui") + arrowGui = (buttons.find('**/tt_t_gui_mat_arrowUp'), buttons.find('**/tt_t_gui_mat_arrowDown'), buttons.find('**/tt_t_gui_mat_arrowDisabled')) + buttons.removeNode() + optiondefs = ( + ('relief', None, None), + ('pos', (-0.375, 0, -0.045), None), + ('numItemsVisible', 4, None), + ('forceHeight', 0.12, None), + ('itemFrame_relief', DGG.SUNKEN, None), + ('itemFrame_pos', (0, 0, 0), None), + ('itemFrame_scale', 1.0, None), + ('itemFrame_borderWidth', (0.015, 0.015), None), + ('itemFrame_frameSize', (-0.325, 0.225, -0.325, 0.2), None), + ('itemFrame_frameColor', (0.85, 0.95, 1, 1), None), + ('decButton_image', arrowGui, None), + ('decButton_relief', None, None), + ('decButton_pos', (0.31, 0, 0.025), None), + ('decButton_hpr', (0, 0, -90), None), + ('decButton_scale', 0.5, None), + ('incButton_image', arrowGui, None), + ('incButton_relief', None, None), + ('incButton_pos', (0.31, 0, -0.175), None), + ('incButton_hpr', (0, 0, 90), None), + ('incButton_scale', 0.5, None), + ) + self.defineoptions(kw, optiondefs) + DirectScrolledList.__init__(self, relief=None) + self.initialiseoptions(BossLobbyGui.SecondaryFrame.LobbyList) + + class LobbyListItem(DirectFrame): + + def __init__(self, parent, itemText, callback, **kw): + optiondefs = ( + ('relief', None, None), + ('frameColor', (0.85, 0.95, 1, 1), None), + ('frameSize', (-0.31, 0.21, 0.055, 0.185), None), + ) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(BossLobbyGui.SecondaryFrame.LobbyListItem) + self.button = DirectButton( + parent=self, + relief=None, + text=itemText, + text_align=TextNode.ALeft, + text_fg=Vec4(0, 0, 0, 1), + text3_fg=(0.4, 0.8, 0.4, 1), + text1_bg=(1, 1, 0, 1), + text2_bg=(0.5, 0.9, 1, 1), + pos=(-0.28, 0, 0.105), + scale=0.065, + command=callback, + extraArgs=[itemText], + ) + + def destroy(self): + if hasattr(self, 'button') and self.button: + self.button.destroy() + DirectFrame.destroy(self) + + class LobbyEntry(DirectEntry): + + def __init__(self, parent, **kw): + optiondefs = ( + ('relief', DGG.SUNKEN, None), + ('borderWidth', (0.25, 0.25), None), + ('pos', (-0.675, 0, 0.285), None), + ('scale', (0.05, 0.055, 0.055), None), + ('numLines', 1, None), + ('focus', 1, None), + ('frameColor', (0.85, 0.95, 1, 1), None), + ) + self.defineoptions(kw, optiondefs) + DirectEntry.__init__(self, relief=None) + self.initialiseoptions(BossLobbyGui.SecondaryFrame.LobbyEntry) + + def __init__(self, parent, callback, **kw): + optiondefs = ( + ('relief', None, None), + ('state', DGG.NORMAL, None), + ('image', DGG.getDefaultDialogGeom(), None), + ('image_scale', (1.6, 1.0, 1.3), None), + ('image_color', ToontownGlobals.GlobalDialogColor, None), + ('pos', (0, 0, 0), None), + ) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(BossLobbyGui.SecondaryFrame) + self.callback = callback + self.items = [] + self.selection = None + self.friendsOnly = False + self.laffLimit = False + self.lobbyName = None + self.isCreating = False + self.load() + + def destroy(self): + if hasattr(self, 'titleLeft') and self.titleLeft: + self.titleLeft.destroy() + del self.titleLeft + if hasattr(self, 'lobbies') and self.lobbies: + self.lobbies.destroy() + del self.lobbies + if hasattr(self, 'entry') and self.entry: + self.entry.destroy() + del self.entry + if hasattr(self, 'cancelButton') and self.cancelButton: + self.cancelButton.destroy() + del self.cancelButton + if hasattr(self, 'nextButton') and self.nextButton: + self.nextButton.destroy() + del self.nextButton + if hasattr(self, 'nameLabel') and self.nameLabel: + self.nameLabel.destroy() + del self.nameLabel + if hasattr(self, 'nameEntry') and self.nameEntry: + self.nameEntry.destroy() + del self.nameEntry + if hasattr(self, 'friendLabel') and self.friendLabel: + self.friendLabel.destroy() + del self.friendLabel + if hasattr(self, 'friendCheckbox') and self.friendCheckbox: + self.friendCheckbox.destroy() + del self.friendCheckbox + if hasattr(self, 'laffLabel') and self.laffLabel: + self.laffLabel.destroy() + del self.laffLabel + if hasattr(self, 'laffCheckbox') and self.laffCheckbox: + self.laffCheckbox.destroy() + del self.laffCheckbox + DirectFrame.destroy(self) + + def load(self): + empty = loader.loadModel("phase_3.5/models/gui/matching_game_gui.bam") + buttons = loader.loadModel("phase_3/models/gui/tt_m_gui_mat_mainGui") + cancelImageList = (buttons.find('**/tt_t_gui_mat_closeUp'), buttons.find('**/tt_t_gui_mat_closeDown'), buttons.find('**/tt_t_gui_mat_closeDown')) + nextImageList = (buttons.find('**/tt_t_gui_mat_nextUp'), buttons.find('**/tt_t_gui_mat_nextDown'), buttons.find('**/tt_t_gui_mat_nextDown')) + emptyList = (empty.find("**/minnieCircle"), empty.find("**/minnieCircle"), empty.find("**/minnieCircle")) + empty.removeNode() + buttons.removeNode() + + self.titleLeft = DirectLabel(parent=self, relief=None, text="Select a Lobby", textMayChange=1, text_scale=0.08, pos=(-0.435, 0, 0.475)) + self.titleRight = DirectLabel(parent=self, relief=None, text="Create a Lobby", textMayChange=1, text_scale=0.08, pos=(0.39, 0, 0.475)) + + self.lobbies = BossLobbyGui.SecondaryFrame.LobbyList(self) + self.entry = BossLobbyGui.SecondaryFrame.LobbyEntry(self, command=self.loadItemsToList) + + self.items = [ + "Loudrob", + "Jake", + "Voltage", + "Daniel", + "Mel", + ] + + self.nameLabel = DirectLabel(parent=self, relief=None, text="Name:", text_scale=0.06, pos=(0.125, 0, 0.285)) + self.nameEntry = BossLobbyGui.SecondaryFrame.LobbyEntry(self, command=self.setLobbyName, pos=(0.27, 0, 0.285), width=9) + + self.friendLabel = DirectLabel(parent=self, relief=None, text="Friends Only?", text_scale=0.06, pos=(0.221, 0, 0.085)) + self.friendCheckbox = DirectButton(parent=self, relief=None, image=emptyList, pos=(0.62, 0, 0.095), scale=0.55, color=(1, 0, 0, 1), command=self.toggleFriendsOnly) + + self.laffLabel = DirectLabel(parent=self, relief=None, text="70+ Laff Only?", text_scale=0.06, pos=(0.251, 0, -0.115)) + self.laffCheckbox = DirectButton(parent=self, relief=None, image=emptyList, pos=(0.62, 0, -0.105), scale=0.55, color=(1, 0, 0, 1), command=self.toggleLaffLimit) + + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(-0.65, 0, -0.535), scale=0.57, command=self.nextFrame, extraArgs=[False]) + self.nextButton = DirectButton(parent=self, relief=None, image=nextImageList, pos=(0.65, 0, -0.535), scale=0.3, command=self.nextFrame, extraArgs=[True]) + + def loadItemsToList(self, entryText): + if hasattr(self, 'lobbies') and self.lobbies: + self.lobbies.destroy() + self.lobbies = BossLobbyGui.SecondaryFrame.LobbyList(self) + toAdd = [] + for i in self.items: + if i.lower().startswith(entryText.lower()): + toAdd.append(i) + for i in sorted(toAdd): + newItem = BossLobbyGui.SecondaryFrame.LobbyListItem(self, i, self.setSelection) + self.lobbies.addItem(newItem) + + def setSelection(self, selection): + self.selection = selection + + def getSelection(self): + return self.selection + + def toggleFriendsOnly(self): + if self.friendsOnly: + self.friendsOnly = False + self.friendCheckbox.setColor(1, 0, 0, 1) + else: + self.friendsOnly = True + self.friendCheckbox.setColor(0, 1, 0, 1) + + def getFriendsOnly(self): + return self.friendsOnly + + def toggleLaffLimit(self): + if self.laffLimit: + self.laffLimit = False + self.laffCheckbox.setColor(1, 0, 0, 1) + else: + self.laffLimit = True + self.laffCheckbox.setColor(0, 1, 0, 1) + + def getLaffLimit(self): + return self.laffLimit + + def setLobbyName(self, name): + self.isCreating = bool(name) + self.lobbyName = name + + def getLobbyName(self): + return self.lobbyName + + def nextFrame(self, status): + if status: + if self.getSelection(): + options = { + 'selected': self.getSelection() + } + self.callback(self.frame + 1, options) + elif self.isCreating: + options = { + 'name': self.lobbyName, + 'friends': self.getFriendsOnly(), + 'laff': self.getLaffLimit(), + } + self.callback(self.frame + 1, options) + else: + self.callback(-1) + else: + self.callback(-1) + + def __init__(self, callback, av): + DirectFrame.__init__(self) + self.callback = callback + self.avatar = av + self.frame = None + + def destroy(self): + if hasattr(self, 'frame') and self.frame: + self.frame.destroy() + del self.frame + DirectFrame.destroy(self) + + def loadFrame(self, frameNum, args={}): + if hasattr(self, 'frame') and self.frame: + self.frame.destroy() + if frameNum == -1: + self.callback(self.avatar, False) + elif frameNum == 0: + self.frame = BossLobbyGui.InitialFrame(self, self.loadFrame) + elif frameNum == 1 and args.get('lobbyType') is not None: + lobby = args.get('lobbyType') + if lobby == 0: + self.callback(self.avatar, True) + elif lobby == 1: + self.frame = BossLobbyGui.SecondaryFrame(self, self.loadFrame) + elif frameNum == 2: + selection = args.get('selected') + name = args.get('name') + if selection: + self.callback(self.avatar, True) + elif name: + friendsOnly = args.get('friends') + laffLimit = args.get('laff') + self.callback(self.avatar, True) + else: + self.callback(self.avatar, False) + +# The following is made for use with the GUI editor. +GUI_EDITOR = """ +from toontown.coghq.BossLobbyGui import BossLobbyGui + +test = BossLobbyGui(None, None) +test.loadFrame(1, {'lobbyType': 1}) +""" diff --git a/toontown/coghq/BossbotCogHQLoader.py b/toontown/coghq/BossbotCogHQLoader.py new file mode 100755 index 00000000..d1e6acea --- /dev/null +++ b/toontown/coghq/BossbotCogHQLoader.py @@ -0,0 +1,135 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +import CogHQLoader +from toontown.toonbase import ToontownGlobals +from direct.gui import DirectGui +from toontown.toonbase import TTLocalizer +from toontown.toon import Toon +from direct.fsm import State +from toontown.coghq import BossbotHQExterior +from toontown.coghq import BossbotHQBossBattle +from toontown.coghq import BossbotOfficeExterior +from toontown.coghq import CountryClubInterior +from pandac.PandaModules import DecalEffect, TextEncoder +import random +aspectSF = 0.7227 + +class BossbotCogHQLoader(CogHQLoader.CogHQLoader): + notify = DirectNotifyGlobal.directNotify.newCategory('BossbotCogHQLoader') + + def __init__(self, hood, parentFSMState, doneEvent): + CogHQLoader.CogHQLoader.__init__(self, hood, parentFSMState, doneEvent) + self.fsm.addState(State.State('countryClubInterior', self.enterCountryClubInterior, self.exitCountryClubInterior, ['quietZone', 'cogHQExterior'])) + for stateName in ['start', 'cogHQExterior', 'quietZone']: + state = self.fsm.getStateNamed(stateName) + state.addTransition('countryClubInterior') + + self.musicFile = random.choice(['phase_12/audio/bgm/Bossbot_Entry_v1.ogg', 'phase_12/audio/bgm/Bossbot_Entry_v2.ogg', 'phase_12/audio/bgm/Bossbot_Entry_v3.ogg']) + self.cogHQExteriorModelPath = 'phase_12/models/bossbotHQ/CogGolfHub' + self.factoryExteriorModelPath = 'phase_11/models/lawbotHQ/LB_DA_Lobby' + self.cogHQLobbyModelPath = 'phase_12/models/bossbotHQ/CogGolfCourtyard' + self.geom = None + + def load(self, zoneId): + CogHQLoader.CogHQLoader.load(self, zoneId) + Toon.loadBossbotHQAnims() + + def unloadPlaceGeom(self): + if self.geom: + self.geom.removeNode() + self.geom = None + CogHQLoader.CogHQLoader.unloadPlaceGeom(self) + + def loadPlaceGeom(self, zoneId): + self.notify.info('loadPlaceGeom: %s' % zoneId) + zoneId = zoneId - zoneId % 100 + self.notify.debug('zoneId = %d ToontownGlobals.BossbotHQ=%d' % (zoneId, ToontownGlobals.BossbotHQ)) + if zoneId == ToontownGlobals.BossbotHQ: + self.geom = loader.loadModel(self.cogHQExteriorModelPath) + gzLinkTunnel = self.geom.find('**/LinkTunnel1') + gzLinkTunnel.setName('linktunnel_gz_17000_DNARoot') + self.makeSigns() + top = self.geom.find('**/TunnelEntrance') + origin = top.find('**/tunnel_origin') + origin.setH(-33.33) + self.geom.flattenMedium() + elif zoneId == ToontownGlobals.BossbotLobby: + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: COGHQ: Visit BossbotLobby') + self.notify.debug('cogHQLobbyModelPath = %s' % self.cogHQLobbyModelPath) + self.geom = loader.loadModel(self.cogHQLobbyModelPath) + self.geom.flattenMedium() + else: + self.notify.warning('loadPlaceGeom: unclassified zone %s' % zoneId) + CogHQLoader.CogHQLoader.loadPlaceGeom(self, zoneId) + + def makeSigns(self): + + def makeSign(topStr, signStr, textId): + top = self.geom.find('**/' + topStr) + sign = top.find('**/' + signStr) + locator = top.find('**/sign_origin') + signText = DirectGui.OnscreenText(text=TextEncoder.upper(TTLocalizer.GlobalStreetNames[textId][-1]), font=ToontownGlobals.getSuitFont(), scale=TTLocalizer.BCHQLsignText, fg=(0, 0, 0, 1), parent=sign) + signText.setPosHpr(locator, 0, -0.1, -0.25, 0, 0, 0) + signText.setDepthWrite(0) + + makeSign('Gate_2', 'Sign_6', 10700) + makeSign('TunnelEntrance', 'Sign_2', 1000) + makeSign('Gate_3', 'Sign_3', 10600) + makeSign('Gate_4', 'Sign_4', 10500) + makeSign('GateHouse', 'Sign_5', 10200) + + def unload(self): + CogHQLoader.CogHQLoader.unload(self) + Toon.unloadSellbotHQAnims() + + def enterStageInterior(self, requestStatus): + self.placeClass = StageInterior.StageInterior + self.stageId = requestStatus['stageId'] + self.enterPlace(requestStatus) + + def exitStageInterior(self): + self.exitPlace() + self.placeClass = None + return + + def getExteriorPlaceClass(self): + self.notify.debug('getExteriorPlaceClass') + return BossbotHQExterior.BossbotHQExterior + + def getBossPlaceClass(self): + self.notify.debug('getBossPlaceClass') + return BossbotHQBossBattle.BossbotHQBossBattle + + def enterFactoryExterior(self, requestStatus): + self.placeClass = BossbotOfficeExterior.BossbotOfficeExterior + self.enterPlace(requestStatus) + + def exitFactoryExterior(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + self.exitPlace() + self.placeClass = None + return + + def enterCogHQBossBattle(self, requestStatus): + self.notify.debug('BossbotCogHQLoader.enterCogHQBossBattle') + CogHQLoader.CogHQLoader.enterCogHQBossBattle(self, requestStatus) + base.cr.forbidCheesyEffects(1) + + def exitCogHQBossBattle(self): + self.notify.debug('BossbotCogHQLoader.exitCogHQBossBattle') + CogHQLoader.CogHQLoader.exitCogHQBossBattle(self) + base.cr.forbidCheesyEffects(0) + + def enterCountryClubInterior(self, requestStatus): + self.placeClass = CountryClubInterior.CountryClubInterior + self.notify.info('enterCountryClubInterior, requestStatus=%s' % requestStatus) + self.countryClubId = requestStatus['countryClubId'] + self.enterPlace(requestStatus) + + def exitCountryClubInterior(self): + self.exitPlace() + self.placeClass = None + del self.countryClubId + return diff --git a/toontown/coghq/BossbotCountryClubEntrance_Action00.py b/toontown/coghq/BossbotCountryClubEntrance_Action00.py new file mode 100755 index 00000000..617a9b9a --- /dev/null +++ b/toontown/coghq/BossbotCountryClubEntrance_Action00.py @@ -0,0 +1,36 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_12/models/bossbotHQ/BossbotEntranceRoom', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10000: {'type': 'entrancePoint', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 6, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'entranceId': 0, + 'radius': 15, + 'theta': 20}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/BossbotCountryClubFairwayRoom_Battle00.py b/toontown/coghq/BossbotCountryClubFairwayRoom_Battle00.py new file mode 100755 index 00000000..80cf9ad2 --- /dev/null +++ b/toontown/coghq/BossbotCountryClubFairwayRoom_Battle00.py @@ -0,0 +1,62 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_12/models/bossbotHQ/BossbotFairwayRoom_A', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110200: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(70, 0, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'radius': 10}, + 110202: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 110001, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 0, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 110200, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 110001: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(101.07, 0, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/BossbotCountryClubFairwayRoom_Battle00_Cogs.py b/toontown/coghq/BossbotCountryClubFairwayRoom_Battle00_Cogs.py new file mode 100755 index 00000000..366237db --- /dev/null +++ b/toontown/coghq/BossbotCountryClubFairwayRoom_Battle00_Cogs.py @@ -0,0 +1,44 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 110200 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/BossbotCountryClubGreenRoom_Action00.py b/toontown/coghq/BossbotCountryClubGreenRoom_Action00.py new file mode 100755 index 00000000..70d73ff1 --- /dev/null +++ b/toontown/coghq/BossbotCountryClubGreenRoom_Action00.py @@ -0,0 +1,65 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_12/models/bossbotHQ/BossbotGreenRoom_A', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110301: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 110303, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 0, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 110302, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 110302: {'type': 'golfGreenGame', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'cellId': 0, + 'puzzleBase': 3, + 'puzzlePerPlayer': 1, + 'switchId': 0, + 'timeToPlay': 120}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 110303: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(40.9635, 2, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/BossbotCountryClubGreenRoom_Action01.py b/toontown/coghq/BossbotCountryClubGreenRoom_Action01.py new file mode 100755 index 00000000..c65cbef1 --- /dev/null +++ b/toontown/coghq/BossbotCountryClubGreenRoom_Action01.py @@ -0,0 +1,65 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_12/models/bossbotHQ/BossbotGreenRoom_A', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110301: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 110303, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 0, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 110302, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 110302: {'type': 'golfGreenGame', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'puzzleBase': 3, + 'puzzlePerPlayer': 2, + 'timeToPlay': 140, + 'cellId': 0, + 'switchId': 0}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 110303: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(40.9635, 2, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/BossbotCountryClubGreenRoom_Action02.py b/toontown/coghq/BossbotCountryClubGreenRoom_Action02.py new file mode 100755 index 00000000..157c91ed --- /dev/null +++ b/toontown/coghq/BossbotCountryClubGreenRoom_Action02.py @@ -0,0 +1,65 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_12/models/bossbotHQ/BossbotGreenRoom_A', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110301: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 110303, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 0, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 110302, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 110302: {'type': 'golfGreenGame', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'puzzleBase': 3, + 'puzzlePerPlayer': 3, + 'timeToPlay': 180, + 'cellId': 0, + 'switchId': 0}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 110303: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(40.9635, 2, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/BossbotCountryClubKartRoom_Battle00.py b/toontown/coghq/BossbotCountryClubKartRoom_Battle00.py new file mode 100755 index 00000000..56820805 --- /dev/null +++ b/toontown/coghq/BossbotCountryClubKartRoom_Battle00.py @@ -0,0 +1,50 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_12/models/bossbotHQ/BossbotKartBoardingRm', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110400: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(4, 0, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'radius': 10}, + 110000: {'type': 'elevatorMarker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(26.854, 0, 0), + 'hpr': Vec3(90, 0, 0), + 'scale': Vec3(1, 1, 1), + 'modelPath': 0}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 110401: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(101.07, 0, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/BossbotCountryClubKartRoom_Battle00_Cogs.py b/toontown/coghq/BossbotCountryClubKartRoom_Battle00_Cogs.py new file mode 100755 index 00000000..d5bb9dbb --- /dev/null +++ b/toontown/coghq/BossbotCountryClubKartRoom_Battle00_Cogs.py @@ -0,0 +1,45 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 110400 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'revives': 1, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle00.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle00.py new file mode 100755 index 00000000..bbd86bf9 --- /dev/null +++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle00.py @@ -0,0 +1,71 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_12/models/bossbotHQ/BossbotMazex1_C', + 'removeNodes': ['**/clubHouse'], + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110000: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-131.21, 84.92, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'radius': 10}, + 110202: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 110001, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 0, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 110000, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 110002: {'type': 'maze', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-141.563, -78.8353, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'numSections': 1}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 110001: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-106.91, 82.6953, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle00_Cogs.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle00_Cogs.py new file mode 100755 index 00000000..fd11c5d1 --- /dev/null +++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle00_Cogs.py @@ -0,0 +1,44 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 110000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}] +ReserveCogData = [] diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle01.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle01.py new file mode 100755 index 00000000..5f449043 --- /dev/null +++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle01.py @@ -0,0 +1,71 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_12/models/bossbotHQ/BossbotMazex2_straight_C', + 'removeNodes': ['**/clubHouse'], + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110000: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(40.29, 84.9249, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'radius': 10}, + 110202: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 110001, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 0, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 110000, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 110002: {'type': 'maze', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-141.563, -78.8353, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'numSections': 2}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 110001: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(61.31, 82.0083, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle01_Cogs.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle01_Cogs.py new file mode 100755 index 00000000..f9262dc8 --- /dev/null +++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle01_Cogs.py @@ -0,0 +1,44 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 110000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle02.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle02.py new file mode 100755 index 00000000..2f608a76 --- /dev/null +++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle02.py @@ -0,0 +1,71 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_12/models/bossbotHQ/BossbotMazex4_C', + 'removeNodes': ['**/clubHouse'], + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110000: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(42.8475, 84.9249, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'radius': 10}, + 110202: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 110001, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 0, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 110000, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 110002: {'type': 'maze', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-141.563, -78.8353, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'numSections': 3}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 110001: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(60.0276, 82.0315, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle02_Cogs.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle02_Cogs.py new file mode 100755 index 00000000..3ae0128b --- /dev/null +++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle02_Cogs.py @@ -0,0 +1,44 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 110000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/BossbotCountryClubMazeRoom_Battle03_Cogs.py b/toontown/coghq/BossbotCountryClubMazeRoom_Battle03_Cogs.py new file mode 100755 index 00000000..a8099614 --- /dev/null +++ b/toontown/coghq/BossbotCountryClubMazeRoom_Battle03_Cogs.py @@ -0,0 +1,44 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 110000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel - 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/BossbotCountryClubPresidentRoom_Battle00.py b/toontown/coghq/BossbotCountryClubPresidentRoom_Battle00.py new file mode 100755 index 00000000..00e67b3b --- /dev/null +++ b/toontown/coghq/BossbotCountryClubPresidentRoom_Battle00.py @@ -0,0 +1,42 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_12/models/bossbotHQ/BossbotPresidentsRm', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110400: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(4, 0, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'radius': 10}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 110401: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(101.07, 0, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/BossbotCountryClubPresidentRoom_Battle00_Cogs.py b/toontown/coghq/BossbotCountryClubPresidentRoom_Battle00_Cogs.py new file mode 100755 index 00000000..bc625404 --- /dev/null +++ b/toontown/coghq/BossbotCountryClubPresidentRoom_Battle00_Cogs.py @@ -0,0 +1,46 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 110400 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 1, + 'level': ToontownGlobals.BossbotCountryClubCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.BossbotCountryClubCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}] +ReserveCogData = [] diff --git a/toontown/coghq/BossbotCountryClubTeeOffRoom_Action00.py b/toontown/coghq/BossbotCountryClubTeeOffRoom_Action00.py new file mode 100755 index 00000000..d84b9c83 --- /dev/null +++ b/toontown/coghq/BossbotCountryClubTeeOffRoom_Action00.py @@ -0,0 +1,67 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_12/models/bossbotHQ/BossbotTeeOffRoom', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110100: {'type': 'door', + 'name': 'TeeOffExitDoor', + 'comment': '', + 'parentEntId': 110001, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 0, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 110102, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 110102: {'type': 'moleField', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-38.6164, -26.2922, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'numSquaresX': 6, + 'numSquaresY': 6, + 'spacingX': 10.0, + 'spacingY': 10.0, + 'timeToPlay': 60, + 'molesBase': 4, + 'molesPerPlayer': 1}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 110001: {'type': 'nodepath', + 'name': 'doorParent', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(60.2682, 0.55914, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/BossbotCountryClubTeeOffRoom_Action01.py b/toontown/coghq/BossbotCountryClubTeeOffRoom_Action01.py new file mode 100755 index 00000000..7fe025cd --- /dev/null +++ b/toontown/coghq/BossbotCountryClubTeeOffRoom_Action01.py @@ -0,0 +1,67 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_12/models/bossbotHQ/BossbotTeeOffRoom', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110100: {'type': 'door', + 'name': 'TeeOffExitDoor', + 'comment': '', + 'parentEntId': 110001, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 0, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 110102, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 110102: {'type': 'moleField', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-38.6164, -26.2922, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'numSquaresX': 6, + 'numSquaresY': 6, + 'spacingX': 10.0, + 'spacingY': 10.0, + 'timeToPlay': 60, + 'molesBase': 4, + 'molesPerPlayer': 2}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 110001: {'type': 'nodepath', + 'name': 'doorParent', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(60.2682, 0.55914, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/BossbotCountryClubTeeOffRoom_Action02.py b/toontown/coghq/BossbotCountryClubTeeOffRoom_Action02.py new file mode 100755 index 00000000..2a427b75 --- /dev/null +++ b/toontown/coghq/BossbotCountryClubTeeOffRoom_Action02.py @@ -0,0 +1,67 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_12/models/bossbotHQ/BossbotTeeOffRoom', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110100: {'type': 'door', + 'name': 'TeeOffExitDoor', + 'comment': '', + 'parentEntId': 110001, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 0, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 110102, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 110102: {'type': 'moleField', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-38.6164, -26.2922, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'numSquaresX': 6, + 'numSquaresY': 6, + 'spacingX': 10.0, + 'spacingY': 10.0, + 'timeToPlay': 60, + 'molesBase': 4, + 'molesPerPlayer': 3}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 110001: {'type': 'nodepath', + 'name': 'doorParent', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(60.2682, 0.55914, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/BossbotHQBossBattle.py b/toontown/coghq/BossbotHQBossBattle.py new file mode 100755 index 00000000..cd81c041 --- /dev/null +++ b/toontown/coghq/BossbotHQBossBattle.py @@ -0,0 +1,34 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.suit import DistributedBossbotBoss +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import CogHQBossBattle + +class BossbotHQBossBattle(CogHQBossBattle.CogHQBossBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('BossbotHQBossBattle') + + def __init__(self, loader, parentFSM, doneEvent): + CogHQBossBattle.CogHQBossBattle.__init__(self, loader, parentFSM, doneEvent) + self.teleportInPosHpr = (88, -214, 0, 210, 0, 0) + for stateName in ['movie']: + state = self.fsm.getStateNamed(stateName) + state.addTransition('crane') + + state = self.fsm.getStateNamed('finalBattle') + state.addTransition('finalBattle') + + def load(self): + CogHQBossBattle.CogHQBossBattle.load(self) + + def unload(self): + CogHQBossBattle.CogHQBossBattle.unload(self) + + def enter(self, requestStatus): + CogHQBossBattle.CogHQBossBattle.enter(self, requestStatus, DistributedBossbotBoss.OneBossCog) + + def exit(self): + CogHQBossBattle.CogHQBossBattle.exit(self) + + def exitCrane(self): + CogHQBossBattle.CogHQBossBattle.exitCrane(self) + messenger.send('exitCrane') diff --git a/toontown/coghq/BossbotHQExterior.py b/toontown/coghq/BossbotHQExterior.py new file mode 100755 index 00000000..71162a60 --- /dev/null +++ b/toontown/coghq/BossbotHQExterior.py @@ -0,0 +1,83 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from panda3d.core import * + +from toontown.battle import BattlePlace +from toontown.building import Elevator +from toontown.coghq import CogHQExterior +from toontown.dna.DNAParser import loadDNAFileAI, DNAStorage +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals + + +class BossbotHQExterior(CogHQExterior.CogHQExterior): + notify = DirectNotifyGlobal.directNotify.newCategory('BossbotHQExterior') + + def __init__(self, loader, parentFSM, doneEvent): + CogHQExterior.CogHQExterior.__init__(self, loader, parentFSM, doneEvent) + + self.elevatorDoneEvent = 'elevatorDone' + self.trains = None + self.fsm.addState(State.State('elevator', self.enterElevator, self.exitElevator, ['walk', 'stopped'])) + state = self.fsm.getStateNamed('walk') + state.addTransition('elevator') + state = self.fsm.getStateNamed('stopped') + state.addTransition('elevator') + state = self.fsm.getStateNamed('stickerBook') + state.addTransition('elevator') + + def enterElevator(self, distElevator): + self.accept(self.elevatorDoneEvent, self.handleElevatorDone) + self.elevator = Elevator.Elevator(self.fsm.getStateNamed('elevator'), self.elevatorDoneEvent, distElevator) + self.elevator.setReverseBoardingCamera(True) + self.elevator.load() + self.elevator.enter() + + def exitElevator(self): + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + del self.elevator + + def detectedElevatorCollision(self, distElevator): + self.fsm.request('elevator', [distElevator]) + + def handleElevatorDone(self, doneStatus): + self.notify.debug('handling elevator done event') + where = doneStatus['where'] + if where == 'reject': + if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen(): + pass + else: + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'countryClubInterior': + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') + + def enter(self, requestStatus): + CogHQExterior.CogHQExterior.enter(self, requestStatus) + + # Load the CogHQ DNA file: + dnaStore = DNAStorage() + dnaFileName = self.genDNAFileName(self.zoneId) + loadDNAFileAI(dnaStore, dnaFileName) + + # Collect all of the vis group zone IDs: + self.zoneVisDict = {} + for i in xrange(dnaStore.getNumDNAVisGroupsAI()): + groupFullName = dnaStore.getDNAVisGroupName(i) + visGroup = dnaStore.getDNAVisGroupAI(i) + visZoneId = int(base.cr.hoodMgr.extractGroupName(groupFullName)) + visibles = [] + for i in xrange(visGroup.getNumVisibles()): + visibles.append(int(visGroup.getVisible(i))) + visibles.append(ZoneUtil.getBranchZone(visZoneId)) + self.zoneVisDict[visZoneId] = visibles + + # Next, we want interest in all vis groups due to this being a Cog HQ: + base.cr.sendSetZoneMsg(self.zoneId, self.zoneVisDict.values()[0]) diff --git a/toontown/coghq/BossbotOfficeExterior.py b/toontown/coghq/BossbotOfficeExterior.py new file mode 100755 index 00000000..b9093e30 --- /dev/null +++ b/toontown/coghq/BossbotOfficeExterior.py @@ -0,0 +1,15 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.toonbase import ToontownGlobals +from toontown.building import Elevator +from panda3d.core import * +import FactoryExterior + +class BossbotOfficeExterior(FactoryExterior.FactoryExterior): + notify = DirectNotifyGlobal.directNotify.newCategory('LawbotOfficeExterior') + + def enterWalk(self, teleportIn = 0): + FactoryExterior.FactoryExterior.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) diff --git a/toontown/coghq/CashbotCogHQLoader.py b/toontown/coghq/CashbotCogHQLoader.py new file mode 100755 index 00000000..2941f9ad --- /dev/null +++ b/toontown/coghq/CashbotCogHQLoader.py @@ -0,0 +1,80 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +import CogHQLoader, MintInterior +from toontown.toonbase import ToontownGlobals +from direct.gui import DirectGui +from toontown.toonbase import TTLocalizer +from toontown.toon import Toon +from direct.fsm import State +import CashbotHQExterior +import CashbotHQBossBattle +from pandac.PandaModules import DecalEffect + +class CashbotCogHQLoader(CogHQLoader.CogHQLoader): + notify = DirectNotifyGlobal.directNotify.newCategory('CashbotCogHQLoader') + + def __init__(self, hood, parentFSMState, doneEvent): + CogHQLoader.CogHQLoader.__init__(self, hood, parentFSMState, doneEvent) + self.fsm.addState(State.State('mintInterior', self.enterMintInterior, self.exitMintInterior, ['quietZone', 'cogHQExterior'])) + for stateName in ['start', 'cogHQExterior', 'quietZone']: + state = self.fsm.getStateNamed(stateName) + state.addTransition('mintInterior') + + self.musicFile = 'phase_9/audio/bgm/encntr_suit_CBHQ_nbrhood.ogg' + self.cogHQExteriorModelPath = 'phase_10/models/cogHQ/CashBotShippingStation' + self.cogHQLobbyModelPath = 'phase_10/models/cogHQ/VaultLobby' + self.geom = None + + def load(self, zoneId): + CogHQLoader.CogHQLoader.load(self, zoneId) + Toon.loadCashbotHQAnims() + + def unloadPlaceGeom(self): + if self.geom: + self.geom.removeNode() + self.geom = None + CogHQLoader.CogHQLoader.unloadPlaceGeom(self) + + def loadPlaceGeom(self, zoneId): + self.notify.info('loadPlaceGeom: %s' % zoneId) + zoneId = zoneId - zoneId % 100 + if zoneId == ToontownGlobals.CashbotHQ: + self.geom = loader.loadModel(self.cogHQExteriorModelPath) + ddLinkTunnel = self.geom.find('**/LinkTunnel1') + ddLinkTunnel.setName('linktunnel_dl_9252_DNARoot') + locator = self.geom.find('**/sign_origin') + backgroundGeom = self.geom.find('**/EntranceFrameFront') + backgroundGeom.node().setEffect(DecalEffect.make()) + signText = DirectGui.OnscreenText(text=TTLocalizer.DonaldsDreamland[-1], font=ToontownGlobals.getSuitFont(), scale=3, fg=(0.87, 0.87, 0.87, 1), mayChange=False, parent=backgroundGeom) + signText.setPosHpr(locator, 0, 0, 0, 0, 0, 0) + signText.setDepthWrite(0) + self.geom.flattenMedium() + elif zoneId == ToontownGlobals.CashbotLobby: + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: COGHQ: Visit CashbotLobby') + self.geom = loader.loadModel(self.cogHQLobbyModelPath) + self.geom.flattenMedium() + else: + self.notify.warning('loadPlaceGeom: unclassified zone %s' % zoneId) + CogHQLoader.CogHQLoader.loadPlaceGeom(self, zoneId) + + def unload(self): + CogHQLoader.CogHQLoader.unload(self) + Toon.unloadCashbotHQAnims() + + def enterMintInterior(self, requestStatus): + self.placeClass = MintInterior.MintInterior + self.mintId = requestStatus['mintId'] + self.enterPlace(requestStatus) + + def exitMintInterior(self): + self.exitPlace() + self.placeClass = None + del self.mintId + return + + def getExteriorPlaceClass(self): + return CashbotHQExterior.CashbotHQExterior + + def getBossPlaceClass(self): + return CashbotHQBossBattle.CashbotHQBossBattle diff --git a/toontown/coghq/CashbotHQBossBattle.py b/toontown/coghq/CashbotHQBossBattle.py new file mode 100755 index 00000000..fde43f73 --- /dev/null +++ b/toontown/coghq/CashbotHQBossBattle.py @@ -0,0 +1,28 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.suit import DistributedCashbotBoss +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import CogHQBossBattle + +class CashbotHQBossBattle(CogHQBossBattle.CogHQBossBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('CashbotHQBossBattle') + + def __init__(self, loader, parentFSM, doneEvent): + CogHQBossBattle.CogHQBossBattle.__init__(self, loader, parentFSM, doneEvent) + self.teleportInPosHpr = (88, -214, 0, 210, 0, 0) + + def load(self): + CogHQBossBattle.CogHQBossBattle.load(self) + + def unload(self): + CogHQBossBattle.CogHQBossBattle.unload(self) + + def enter(self, requestStatus): + CogHQBossBattle.CogHQBossBattle.enter(self, requestStatus, DistributedCashbotBoss.OneBossCog) + + def exit(self): + CogHQBossBattle.CogHQBossBattle.exit(self) + + def exitCrane(self): + CogHQBossBattle.CogHQBossBattle.exitCrane(self) + messenger.send('exitCrane') diff --git a/toontown/coghq/CashbotHQExterior.py b/toontown/coghq/CashbotHQExterior.py new file mode 100755 index 00000000..811d2105 --- /dev/null +++ b/toontown/coghq/CashbotHQExterior.py @@ -0,0 +1,115 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import State +from direct.interval.IntervalGlobal import * +from panda3d.core import * +from toontown.building import Elevator +from toontown.coghq import CogHQExterior +from toontown.dna.DNAParser import loadDNAFileAI, DNAStorage +from toontown.hood import ZoneUtil +from toontown.safezone import Train + + +class CashbotHQExterior(CogHQExterior.CogHQExterior): + notify = DirectNotifyGlobal.directNotify.newCategory('CashbotHQExterior') + TrackZ = -67 + TrainTracks = [{'start': Point3(-1000, -54.45, TrackZ), + 'end': Point3(2200, -54.45, TrackZ)}, + {'start': Point3(1800, -133.45, TrackZ), + 'end': Point3(-1200, -133.45, TrackZ)}, + {'start': Point3(-1000, -212.45, TrackZ), + 'end': Point3(2200, -212.45, TrackZ)}, + {'start': Point3(1800, -291.45, TrackZ), + 'end': Point3(-1200, -291.45, TrackZ)}] + + def __init__(self, loader, parentFSM, doneEvent): + CogHQExterior.CogHQExterior.__init__(self, loader, parentFSM, doneEvent) + self.elevatorDoneEvent = 'elevatorDone' + self.trains = None + self.fsm.addState(State.State('elevator', self.enterElevator, self.exitElevator, ['walk', 'stopped'])) + state = self.fsm.getStateNamed('walk') + state.addTransition('elevator') + state = self.fsm.getStateNamed('stopped') + state.addTransition('elevator') + state = self.fsm.getStateNamed('stickerBook') + state.addTransition('elevator') + state = self.fsm.getStateNamed('squished') + state.addTransition('elevator') + return + + def load(self): + CogHQExterior.CogHQExterior.load(self) + if not self.trains: + self.trains = [] + for track in self.TrainTracks: + train = Train.Train(track['start'], track['end'], self.TrainTracks.index(track), len(self.TrainTracks)) + self.trains.append(train) + + def unload(self): + CogHQExterior.CogHQExterior.unload(self) + for train in self.trains: + train.delete() + + self.trains = None + return + + def enter(self, requestStatus): + CogHQExterior.CogHQExterior.enter(self, requestStatus) + + for train in self.trains: + train.show() + + # Load the CogHQ DNA file: + dnaStore = DNAStorage() + dnaFileName = self.genDNAFileName(self.zoneId) + loadDNAFileAI(dnaStore, dnaFileName) + + # Collect all of the vis group zone IDs: + self.zoneVisDict = {} + for i in xrange(dnaStore.getNumDNAVisGroupsAI()): + groupFullName = dnaStore.getDNAVisGroupName(i) + visGroup = dnaStore.getDNAVisGroupAI(i) + visZoneId = int(base.cr.hoodMgr.extractGroupName(groupFullName)) + visibles = [] + for i in xrange(visGroup.getNumVisibles()): + visibles.append(int(visGroup.getVisible(i))) + visibles.append(ZoneUtil.getBranchZone(visZoneId)) + self.zoneVisDict[visZoneId] = visibles + + # Next, we want interest in all vis groups due to this being a Cog HQ: + base.cr.sendSetZoneMsg(self.zoneId, self.zoneVisDict.values()[0]) + + def exit(self): + CogHQExterior.CogHQExterior.exit(self) + for train in self.trains: + train.hide() + + def enterElevator(self, distElevator): + self.accept(self.elevatorDoneEvent, self.handleElevatorDone) + self.elevator = Elevator.Elevator(self.fsm.getStateNamed('elevator'), self.elevatorDoneEvent, distElevator) + self.elevator.load() + self.elevator.enter() + + def exitElevator(self): + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + del self.elevator + + def detectedElevatorCollision(self, distElevator): + self.fsm.request('elevator', [distElevator]) + + def handleElevatorDone(self, doneStatus): + self.notify.debug('handling elevator done event') + where = doneStatus['where'] + if where == 'reject': + if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen(): + pass + else: + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'mintInterior': + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') diff --git a/toontown/coghq/CashbotMintBoilerRoom_Action00.py b/toontown/coghq/CashbotMintBoilerRoom_Action00.py new file mode 100755 index 00000000..e27f2e7a --- /dev/null +++ b/toontown/coghq/CashbotMintBoilerRoom_Action00.py @@ -0,0 +1,615 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE08a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10055: {'type': 'attribModifier', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'attribName': 'modelPath', + 'recursive': 1, + 'typeName': 'model', + 'value': ''}, + 10045: {'type': 'gagBarrel', + 'name': 'gag', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(1.36976861954, 0.773027420044, 0.0), + 'hpr': Vec3(51.1066703796, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 'random', + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10047: {'type': 'gagBarrel', + 'name': 'gag', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(0.137291625142, 2.83575630188, 0.0), + 'hpr': Vec3(-210.47303772, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 'random', + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10054: {'type': 'gagBarrel', + 'name': 'gag', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-2.34864091873, 2.16795802116, 0.0), + 'hpr': Vec3(-141.715744019, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 'random', + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10020: {'type': 'gear', + 'name': 'upper', + 'comment': '', + 'parentEntId': 10044, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.60000002384), + 'degreesPerSec': -5.0, + 'gearScale': 24.3, + 'modelType': 'mint', + 'orientation': 'horizontal', + 'phaseShift': 0}, + 10004: {'type': 'healBarrel', + 'name': 'heal', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(0.0, -0.748414576054, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 6, + 'rewardPerGrabMax': 8}, + 10005: {'type': 'healBarrel', + 'name': 'heal', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-2.20195555687, -0.384303599596, 0.0), + 'hpr': Vec3(-64.4312591553, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 6, + 'rewardPerGrabMax': 8}, + 10037: {'type': 'healBarrel', + 'name': 'atTheEnd', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(64.282081604, 42.8509597778, 0.0), + 'hpr': Vec3(274.906707764, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10010: {'type': 'mintShelf', + 'name': 'shelf0', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12700}, + 10021: {'type': 'mintShelf', + 'name': 'copy of shelf0', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(-13.4654359818, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10022: {'type': 'mintShelf', + 'name': 'copy of shelf0 (2)', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(-26.8826961517, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10025: {'type': 'mintShelf', + 'name': 'shelf0', + 'comment': '', + 'parentEntId': 10024, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10026: {'type': 'mintShelf', + 'name': 'copy of shelf0', + 'comment': '', + 'parentEntId': 10024, + 'pos': Point3(-13.4654359818, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10027: {'type': 'mintShelf', + 'name': 'copy of shelf0 (2)', + 'comment': '', + 'parentEntId': 10024, + 'pos': Point3(-26.8826961517, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10000: {'type': 'model', + 'name': 'crate', + 'comment': '', + 'parentEntId': 10009, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10007: {'type': 'model', + 'name': 'upper', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0.0, 2.0, 5.5), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10008: {'type': 'model', + 'name': 'crate', + 'comment': '', + 'parentEntId': 10009, + 'pos': Point3(0.0, -5.79679441452, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10012: {'type': 'model', + 'name': 'copy of crate', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0.0, -5.79679441452, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10013: {'type': 'model', + 'name': 'copy of crate (2)', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10014: {'type': 'model', + 'name': 'copy of crate (2)', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-5.65285158157, -11.6494598389, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10015: {'type': 'model', + 'name': 'copy of crate (2)', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-5.80570077896, -5.79679441452, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10016: {'type': 'model', + 'name': 'copy of crate (3)', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-3.93829965591, -17.6477527618, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10018: {'type': 'model', + 'name': 'copy of upper', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0.0, -3.83362102509, 5.5), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10019: {'type': 'model', + 'name': 'copy of upper (2)', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0.0, -9.69304847717, 5.5), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10030: {'type': 'model', + 'name': 'lastCrateStack', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(47.9848709106, 27.71052742, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10031: {'type': 'model', + 'name': 'upper', + 'comment': '', + 'parentEntId': 10030, + 'pos': Point3(0.0, 0.0, 5.5), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10033: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(-41.8699073792, -36.9582328796, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D1.bam'}, + 10034: {'type': 'model', + 'name': 'crateStack', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(47.9848709106, -3.09666919708, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10035: {'type': 'model', + 'name': 'upper', + 'comment': '', + 'parentEntId': 10034, + 'pos': Point3(0.0, 0.0, 5.5), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10036: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0.0, -41.4516029358, 30.2685108185), + 'hpr': Vec3(180.0, 0.0, 180.0), + 'scale': Vec3(0.850346446037, 0.850346446037, 0.850346446037), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10041: {'type': 'model', + 'name': 'crateStack', + 'comment': '', + 'parentEntId': 10040, + 'pos': Point3(36.5904769897, -31.6758518219, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10042: {'type': 'model', + 'name': 'upper', + 'comment': '', + 'parentEntId': 10041, + 'pos': Point3(0.0, 0.0, 5.5), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10043: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(19.5017147064, 84.0786056519, 10.0058736801), + 'hpr': Vec3(171.253845215, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/boiler_B1.bam'}, + 10048: {'type': 'model', + 'name': 'crate', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(0.0, 0.0, 8.25758934021), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.29999995232, 1.29999995232, 1.64999997616), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10050: {'type': 'model', + 'name': 'support', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/gears_C2.bam'}, + 10052: {'type': 'model', + 'name': 'crate', + 'comment': '', + 'parentEntId': 10051, + 'pos': Point3(0.0, 0.0, 8.25758934021), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.29999995232, 1.29999995232, 1.64999997616), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10053: {'type': 'model', + 'name': 'support', + 'comment': '', + 'parentEntId': 10051, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/gears_C2.bam'}, + 10056: {'type': 'model', + 'name': 'collision', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-0.62570387125, 0.824797034264, 0.0), + 'hpr': Vec3(318.366455078, 0.0, 0.0), + 'scale': Vec3(0.644617915154, 0.639999985695, 1.28725671768), + 'collisionsOnly': 1, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'}, + 10057: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10044, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(11.0614013672, 11.0614013672, 11.0614013672), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/RoundShadow.bam'}, + 10058: {'type': 'model', + 'name': 'shelf', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(62.9968643188, 21.712474823, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/shelf_A1.bam'}, + 10062: {'type': 'model', + 'name': 'copy of upper', + 'comment': '', + 'parentEntId': 10061, + 'pos': Point3(0.0, -3.83362102509, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10063: {'type': 'model', + 'name': 'copy of upper (2)', + 'comment': '', + 'parentEntId': 10061, + 'pos': Point3(0.0, -9.69304847717, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10064: {'type': 'model', + 'name': 'upper', + 'comment': '', + 'parentEntId': 10061, + 'pos': Point3(0.0, 2.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10001: {'type': 'nodepath', + 'name': 'crates', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.29999995232, 1.29999995232, 1.64892423153)}, + 10002: {'type': 'nodepath', + 'name': 'rewardBarrels', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-0.719733536243, 56.9690589905, 10.0021047592), + 'hpr': Vec3(61.6992454529, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10003: {'type': 'nodepath', + 'name': 'upperWall', + 'comment': 'TODO: replace with lines of shelves', + 'parentEntId': 0, + 'pos': Point3(-20.3202514648, 52.6549415588, 9.9087305069), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.11429846287, 1.11429846287, 1.11429846287)}, + 10009: {'type': 'nodepath', + 'name': 'toGear0', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(-26.5593318939, 31.8559513092, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10011: {'type': 'nodepath', + 'name': 'toGear1', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(-25.88397789, 13.6748971939, 0.0), + 'hpr': Vec3(41.6335411072, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10023: {'type': 'nodepath', + 'name': 'leftWall', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10024: {'type': 'nodepath', + 'name': 'rightWall', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-26.7111759186, 6.85981559753, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10028: {'type': 'nodepath', + 'name': 'lowerPuzzle', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0500000007451), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10029: {'type': 'nodepath', + 'name': 'entranceWall', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10032: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10038: {'type': 'nodepath', + 'name': 'archStompers', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10040: {'type': 'nodepath', + 'name': 'backWall', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10044: {'type': 'nodepath', + 'name': 'gear', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(11.8500003815, -11.3800001144, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10046: {'type': 'nodepath', + 'name': 'supportedCrateBackWall', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(34.904460907, -34.058883667, -1.51686680317), + 'hpr': Vec3(63.4349479675, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10051: {'type': 'nodepath', + 'name': 'supportedCrateEntrance', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(50.5076904298, 7.75915336609, 0.35789707303), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10059: {'type': 'nodepath', + 'name': 'largeStack', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(47.9799995422, -16.9799995422, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10061: {'type': 'nodepath', + 'name': 'lower', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10049: {'type': 'stomper', + 'name': 'second', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(62.3684997559, -19.4456634521, 18.1217155457), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(3.79999995232, 4.30000019073, 3.79999995232), + 'modelPath': 0, + 'motion': 3, + 'period': 3.0, + 'phaseShift': 0.34, + 'range': 7.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 1, + 'shaftScale': Point3(1.71000003815, 2.78999996185, 1.71000003815), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 1, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintBoilerRoom_Battle00.py b/toontown/coghq/CashbotMintBoilerRoom_Battle00.py new file mode 100755 index 00000000..d756c426 --- /dev/null +++ b/toontown/coghq/CashbotMintBoilerRoom_Battle00.py @@ -0,0 +1,441 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE08a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10001: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-1.02925205231, 87.0907745361, 11.8959827423), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'cellId': 0, + 'radius': 10.0}, + 10006: {'type': 'battleBlocker', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-60.9065246582, -3.26905798912, 0.117109239101), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'cellId': 1, + 'radius': 15.0}, + 10047: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 10013, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 0.20000000298, 1.0), + 'cellId': 2, + 'radius': 20.0}, + 10041: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10033, + 'pos': Point3(5.40611028671, 0.0, 0.0), + 'hpr': Vec3(199.440032959, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 'random', + 'rewardPerGrab': 4, + 'rewardPerGrabMax': 6}, + 10034: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10033, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(163.300750732, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 7, + 'rewardPerGrabMax': 9}, + 10015: {'type': 'mintProductPallet', + 'name': '', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10016: {'type': 'mintProductPallet', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0.0, 13.6865262985, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10017: {'type': 'mintProductPallet', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0.0, 27.3799991608, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10018: {'type': 'mintProductPallet', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0.0, 41.0699996948, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10019: {'type': 'mintProductPallet', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0.0, 54.7599983215, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10020: {'type': 'mintProductPallet', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0.0, 68.4499969482, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10022: {'type': 'mintProductPallet', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(0.0, 11.766998291, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10025: {'type': 'mintProductPallet', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(0.0, 54.7599983215, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10026: {'type': 'mintProductPallet', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(0.0, 68.4499969482, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10036: {'type': 'mintProductPallet', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(0.0, 13.6865262985, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10037: {'type': 'mintProductPallet', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(0.0, 27.3799991608, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10038: {'type': 'mintProductPallet', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(0.0, 41.0699996948, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10043: {'type': 'mintProductPallet', + 'name': '', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(-36.662399292, -39.0314712524, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10044: {'type': 'mintProductPallet', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(0.0, 25.4739685059, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10004: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(0.0, -1.09804749489, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(2.0, 2.0, 2.0), + 'collisionsOnly': 0, + 'flattenType': 'strong', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10009: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(-3.9962117672, 0.695078849792, 0.0113303475082), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.20000004768, 1.20000004768, 1.20000004768), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'}, + 10010: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(48.0530014038, -0.531660735607, -0.327078670263), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'}, + 10012: {'type': 'model', + 'name': 'rightCrates', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(36.0373382568, 71.3546981812, 9.99835586548), + 'hpr': Vec3(315.0, 0.0, 0.0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'}, + 10024: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(-3.7328555584, 27.1218452454, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(2.0, 2.0, 2.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10027: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(-11.9349050522, 38.9528312683, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(2.0, 2.0, 2.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10029: {'type': 'model', + 'name': 'crate', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(0.0, 0.863602340221, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(2.0, 2.0, 2.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10030: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10031: {'type': 'model', + 'name': 'copy of crate', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(0.0, 0.0, 5.46999979019), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10032: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, -5.92218112946, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10039: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(-9.23663234711, 0.821143984795, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'}, + 10042: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(3.0, -11.8400001526, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10048: {'type': 'model', + 'name': 'cratesAgainstWall', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(-37.0983123779, 70.2133865356, 10.0), + 'hpr': Vec3(225.0, 0.0, 0.0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0.0, 66.1200027466, 10.1833248138), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10002: {'type': 'nodepath', + 'name': 'battle', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': 1}, + 10003: {'type': 'nodepath', + 'name': 'cogs2', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-53.9246749878, -22.7616195679, 0.0), + 'hpr': Point3(45.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10005: {'type': 'nodepath', + 'name': 'battle', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10007: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10008: {'type': 'nodepath', + 'name': 'topWall', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0.0, 48.0299987793, 10.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10011: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10013: {'type': 'nodepath', + 'name': 'frontCogs', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(25.3957309723, -12.3005743027, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10014: {'type': 'nodepath', + 'name': 'frontPalletWall', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(45.5494384766, 38.2237281799, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10021: {'type': 'nodepath', + 'name': 'middlePalletWallLeft', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(6.0, -37.9928665161, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10023: {'type': 'nodepath', + 'name': 'crateIsland', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(-23.1813278198, 7.08758449554, 0.00999999977648), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(2.0, 2.0, 2.0)}, + 10028: {'type': 'nodepath', + 'name': 'rewardCulDeSac', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(-8.26172065735, 38.377407074, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10033: {'type': 'nodepath', + 'name': 'barrels', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(-4.75077962875, 34.1425209045, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10035: {'type': 'nodepath', + 'name': 'backPalletWall', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(-47.6501731873, 40.006893158, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10040: {'type': 'nodepath', + 'name': 'centerCogs', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-23.9375743866, 28.353269577, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10045: {'type': 'nodepath', + 'name': 'middlePalletWallRight', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(17.4200000763, -38.2999992371, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10046: {'type': 'nodepath', + 'name': 'middlePalletWall', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintBoilerRoom_Battle00_Cogs.py b/toontown/coghq/CashbotMintBoilerRoom_Battle00_Cogs.py new file mode 100755 index 00000000..991eecac --- /dev/null +++ b/toontown/coghq/CashbotMintBoilerRoom_Battle00_Cogs.py @@ -0,0 +1,165 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +LowerCogParent = 10003 +BattleParent = 10002 +LowerBattleParent = 10005 +FrontCogParent = 10013 +CenterCogParent = 10040 +BattleCellId = 0 +LowerBattleCellId = 1 +FrontBattleCellId = 2 +CenterBattleCellId = 3 +BattleCells = {BattleCellId: {'parentEntId': BattleParent, + 'pos': Point3(0, 0, 0)}, + LowerBattleCellId: {'parentEntId': LowerBattleParent, + 'pos': Point3(0, 0, 0)}, + FrontBattleCellId: {'parentEntId': FrontCogParent, + 'pos': Point3(0, 0, 0)}, + CenterBattleCellId: {'parentEntId': CenterCogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': LowerCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': LowerBattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': LowerCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': LowerBattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': LowerCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': LowerBattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': LowerCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': LowerBattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CenterCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': CenterBattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CenterCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': CenterBattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CenterCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': CenterBattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CenterCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': CenterBattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintBoilerRoom_Battle01.py b/toontown/coghq/CashbotMintBoilerRoom_Battle01.py new file mode 100755 index 00000000..898cd205 --- /dev/null +++ b/toontown/coghq/CashbotMintBoilerRoom_Battle01.py @@ -0,0 +1,394 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE08a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10006: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10005, + 'pos': Point3(-23.8955783844, -29.8914642334, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 'random', + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 7}, + 10007: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10005, + 'pos': Point3(-7.71000003815, 6.03817367554, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 8}, + 10001: {'type': 'locator', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'searchPath': '**/EXIT'}, + 10014: {'type': 'mintProduct', + 'name': '', + 'comment': '', + 'parentEntId': 10013, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10015: {'type': 'mintProduct', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10013, + 'pos': Point3(9.73605537415, 0.935430526733, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10016: {'type': 'mintProduct', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10013, + 'pos': Point3(-11.0564117432, 0.213024124503, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10028: {'type': 'mintShelf', + 'name': 'shelfPair', + 'comment': '', + 'parentEntId': 10027, + 'pos': Point3(12.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10029: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(0.167918920517, 6.80000019073, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10030: {'type': 'mintShelf', + 'name': 'shelfPair', + 'comment': '', + 'parentEntId': 10027, + 'pos': Point3(-12.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10031: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10030, + 'pos': Point3(0.167918920517, 6.80000019073, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10033: {'type': 'mintShelf', + 'name': 'shelfPair', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10034: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10033, + 'pos': Point3(0.167918920517, 6.80000019073, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10035: {'type': 'mintShelf', + 'name': 'shelfPair', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(24.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10036: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(0.167918920517, 6.80000019073, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10037: {'type': 'mintShelf', + 'name': 'shelfPair', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(-24.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10038: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(0.167918920517, 6.80000019073, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10040: {'type': 'mintShelf', + 'name': 'shelfPair', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(12.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10041: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10040, + 'pos': Point3(0.167918920517, 6.80000019073, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10044: {'type': 'mintShelf', + 'name': 'shelfPair', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(-12.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10045: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10044, + 'pos': Point3(0.167918920517, 6.80000019073, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10046: {'type': 'mintShelf', + 'name': 'shelfPair', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(-36.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10047: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(0.167918920517, 6.80000019073, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10049: {'type': 'mintShelf', + 'name': 'shelfPair', + 'comment': '', + 'parentEntId': 10048, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10050: {'type': 'mintShelf', + 'name': 'shelfPair', + 'comment': '', + 'parentEntId': 10048, + 'pos': Point3(24.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10051: {'type': 'mintShelf', + 'name': 'shelfPair', + 'comment': '', + 'parentEntId': 10048, + 'pos': Point3(-24.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10052: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10049, + 'pos': Point3(0.167918920517, 6.80000019073, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10053: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(0.167918920517, 6.80000019073, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10054: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10051, + 'pos': Point3(0.167918920517, 6.80000019073, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10056: {'type': 'mintShelf', + 'name': 'shelfPair', + 'comment': '', + 'parentEntId': 10055, + 'pos': Point3(12.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10057: {'type': 'mintShelf', + 'name': 'shelfPair', + 'comment': '', + 'parentEntId': 10055, + 'pos': Point3(-12.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10058: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10056, + 'pos': Point3(0.167918920517, 6.80000019073, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10059: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10057, + 'pos': Point3(0.167918920517, 6.80000019073, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10002: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'}, + 10011: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(34.3037414551, 6.2506942749, 0.0), + 'hpr': Vec3(306.869903564, 0.0, 0.0), + 'scale': Vec3(1.22879016399, 1.22879016399, 1.22879016399), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/boiler_A2.bam'}, + 10012: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(-37.5963821411, 0.68013381958, 0.0), + 'hpr': Vec3(45.0, 0.0, 0.0), + 'scale': Vec3(0.761251866817, 0.761251866817, 0.761251866817), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D1.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 68.932258606, 9.97146701813), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10003: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': 'lower', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10005: {'type': 'nodepath', + 'name': 'barrels', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10008: {'type': 'nodepath', + 'name': 'upperLevel', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 65.4967575073, 9.99451065063), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10013: {'type': 'nodepath', + 'name': 'product', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0.0, 17.8199996948, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10023: {'type': 'nodepath', + 'name': 'shelves', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 1.89410364628, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10027: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, -32.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10032: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, -14.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10039: {'type': 'nodepath', + 'name': 'row3', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, 4.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10048: {'type': 'nodepath', + 'name': 'row4', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, 22.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10055: {'type': 'nodepath', + 'name': 'row5', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, 40.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintBoilerRoom_Battle01_Cogs.py b/toontown/coghq/CashbotMintBoilerRoom_Battle01_Cogs.py new file mode 100755 index 00000000..61883ec4 --- /dev/null +++ b/toontown/coghq/CashbotMintBoilerRoom_Battle01_Cogs.py @@ -0,0 +1,43 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 1, + 'level': ToontownGlobals.CashbotMintBossLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintControlRoom_Battle00.py b/toontown/coghq/CashbotMintControlRoom_Battle00.py new file mode 100755 index 00000000..b445ae40 --- /dev/null +++ b/toontown/coghq/CashbotMintControlRoom_Battle00.py @@ -0,0 +1,130 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE31a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10013: {'type': 'mintProduct', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(0.0, 16.5455417633, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10014: {'type': 'mintProduct', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(9.33731842041, 15.9028654099, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10015: {'type': 'mintProduct', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(-9.30014419556, 11.6405067444, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10003: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(19.5716362, 16.3833560944, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10004: {'type': 'mintShelf', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(19.5716362, 2.93304467201, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10005: {'type': 'mintShelf', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(19.5716362, -10.4780406952, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10006: {'type': 'mintShelf', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-19.5699996948, 16.3833560944, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10007: {'type': 'mintShelf', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-19.5699996948, 2.93304467201, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10008: {'type': 'mintShelf', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-19.5699996948, -10.4780406952, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10001: {'type': 'model', + 'name': 'vaultDoor', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 22.9976291656, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': 1}, + 10002: {'type': 'nodepath', + 'name': 'shelves', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10011: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10012: {'type': 'nodepath', + 'name': 'product', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintControlRoom_Battle00_Cogs.py b/toontown/coghq/CashbotMintControlRoom_Battle00_Cogs.py new file mode 100755 index 00000000..61883ec4 --- /dev/null +++ b/toontown/coghq/CashbotMintControlRoom_Battle00_Cogs.py @@ -0,0 +1,43 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 1, + 'level': ToontownGlobals.CashbotMintBossLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintDuctRoom_Action00.py b/toontown/coghq/CashbotMintDuctRoom_Action00.py new file mode 100755 index 00000000..a43b8fdd --- /dev/null +++ b/toontown/coghq/CashbotMintDuctRoom_Action00.py @@ -0,0 +1,243 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE15a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10015: {'type': 'healBarrel', + 'name': 'heal', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(16.9929084778, 7.15916633606, 0.0), + 'hpr': Vec3(107.078933716, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 6, + 'rewardPerGrabMax': 0}, + 10004: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(41.5774269104, -16.0394973755, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10005: {'type': 'mintShelf', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(41.5774269104, 15.5885248184, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10009: {'type': 'model', + 'name': 'crateColl0', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(-21.0479602814, -8.71147918701, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.6654573679, 4.67459440231, 4.99637460709), + 'collisionsOnly': 1, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'}, + 10013: {'type': 'model', + 'name': 'crate0', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(-21.0, 0.735621452332, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.60000002384, 1.60000002384, 1.60000002384), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10016: {'type': 'model', + 'name': 'copy of crate0', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(-21.0, -8.74976444244, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.60000002384, 1.60000002384, 1.60000002384), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10017: {'type': 'model', + 'name': 'copy of crate0 (2)', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(-21.0, -18.1307086945, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.60000002384, 1.60000002384, 1.60000002384), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10019: {'type': 'model', + 'name': 'copy of crate0', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(-21.0, -8.74976444244, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.60000002384, 1.60000002384, 1.60000002384), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10020: {'type': 'model', + 'name': 'crateColl0', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(-21.0479602814, -8.71147918701, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.69406318665, 4.75488471985, 5.08219003677), + 'collisionsOnly': 1, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'}, + 10021: {'type': 'model', + 'name': 'crate0', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(-21.0, 0.735621452332, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.60000002384, 1.60000002384, 1.60000002384), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10022: {'type': 'model', + 'name': 'copy of crate0 (2)', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(-21.0, -18.1307086945, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.60000002384, 1.60000002384, 1.60000002384), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10024: {'type': 'model', + 'name': 'hider', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(17.0452461243, -0.882949709892, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.54636645317, 1.54636645317, 1.54636645317), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10003: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10010: {'type': 'nodepath', + 'name': 'crates0', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(1.2899544239, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10018: {'type': 'nodepath', + 'name': 'crates1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-13.2792396545, 0.0, 0.0), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10023: {'type': 'nodepath', + 'name': 'heal', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10000: {'type': 'stomper', + 'name': 'stomper0', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-23.0840358734, 13.8124275208, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(8.0, 8.0, 8.0), + 'crushCellId': None, + 'damage': 8, + 'headScale': Vec3(1.0, 1.0, 1.0), + 'modelPath': 0, + 'motion': 3, + 'period': 3.0, + 'phaseShift': 0.0, + 'range': 1.6, + 'shaftScale': Point3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10001: {'type': 'stomper', + 'name': 'stomper1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-5.92516326904, -0.618411839008, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(8.0, 8.0, 8.0), + 'crushCellId': None, + 'damage': 8, + 'headScale': Vec3(1.0, 1.0, 1.0), + 'modelPath': 0, + 'motion': 3, + 'period': 3.0, + 'phaseShift': 0.33, + 'range': 1.6, + 'shaftScale': Point3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10002: {'type': 'stomper', + 'name': 'stomper2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(11.6394100189, -14.1471977234, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(8.0, 8.0, 8.0), + 'crushCellId': None, + 'damage': 8, + 'headScale': Vec3(1.0, 1.0, 1.0), + 'modelPath': 0, + 'motion': 3, + 'period': 3.0, + 'phaseShift': 0.66, + 'range': 1.6, + 'shaftScale': Point3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintDuctRoom_Battle00.py b/toontown/coghq/CashbotMintDuctRoom_Battle00.py new file mode 100755 index 00000000..c68ada2a --- /dev/null +++ b/toontown/coghq/CashbotMintDuctRoom_Battle00.py @@ -0,0 +1,129 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE15a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10001: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(44.257774353, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'cellId': 0, + 'radius': 10.0}, + 10003: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(39.3964080811, 22.2593421936, 16.0659122467), + 'hpr': Vec3(270.0, 0.0, 90.0), + 'scale': Vec3(0.560864269733, 0.560864269733, 0.560864269733), + 'collisionsOnly': 0, + 'loadType': 'loadModel', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10005: {'type': 'model', + 'name': 'farLeft', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(41.8226966858, 16.5434036255, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(2.28777551651, 2.28777551651, 2.28777551651), + 'collisionsOnly': 1, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'}, + 10006: {'type': 'model', + 'name': 'farRight', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(41.8226966858, -16.5400009155, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(2.28777551651, 2.28777551651, 2.28777551651), + 'collisionsOnly': 1, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'}, + 10007: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(39.3192749023, -22.8234348297, 13.6092739105), + 'hpr': Vec3(270.0, 0.0, 270.0), + 'scale': Point3(0.560000002384, 0.560864269733, 0.560864269733), + 'collisionsOnly': 0, + 'loadType': 'loadModel', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10008: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-39.3800811768, -22.8855381012, 16.0659122467), + 'hpr': Point3(90.0, 0.0, 90.0), + 'scale': Vec3(0.560864269733, 0.560864269733, 0.560864269733), + 'collisionsOnly': 0, + 'loadType': 'loadModel', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10009: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-39.3062858582, 22.1807098389, 13.6092739105), + 'hpr': Point3(90.0, 0.0, 270.0), + 'scale': Point3(0.560000002384, 0.560864269733, 0.560864269733), + 'collisionsOnly': 0, + 'loadType': 'loadModel', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10010: {'type': 'model', + 'name': 'nearLeft', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-41.8199996948, 16.5434036255, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(2.28777551651, 2.28777551651, 2.28777551651), + 'collisionsOnly': 1, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'}, + 10011: {'type': 'model', + 'name': 'nearRight', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-41.8199996948, -16.5400009155, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(2.28777551651, 2.28777551651, 2.28777551651), + 'collisionsOnly': 1, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': 'collisions', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintDuctRoom_Battle00_Cogs.py b/toontown/coghq/CashbotMintDuctRoom_Battle00_Cogs.py new file mode 100755 index 00000000..017caac8 --- /dev/null +++ b/toontown/coghq/CashbotMintDuctRoom_Battle00_Cogs.py @@ -0,0 +1,43 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintDuctRoom_Battle01.py b/toontown/coghq/CashbotMintDuctRoom_Battle01.py new file mode 100755 index 00000000..64820963 --- /dev/null +++ b/toontown/coghq/CashbotMintDuctRoom_Battle01.py @@ -0,0 +1,132 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE15a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10001: {'type': 'locator', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'searchPath': '**/EXIT'}, + 10009: {'type': 'mintProduct', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(37.843006134, 8.74360656738, 0.0), + 'hpr': Point3(-90.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10010: {'type': 'mintProduct', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(36.8768577576, -10.2692861557, 0.0), + 'hpr': Point3(-90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10011: {'type': 'mintProduct', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(26.2384986877, -16.2085189819, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10012: {'type': 'mintProduct', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(38.4032859802, -3.16089892387, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10002: {'type': 'model', + 'name': 'vaultDoor', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'}, + 10004: {'type': 'model', + 'name': 'backRight', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(42.1358146667, -20.2375278473, 0.275439172983), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_A.bam'}, + 10005: {'type': 'model', + 'name': 'backLeft', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(38.4724159241, 18.6007175446, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.44939172268, 1.44939172268, 1.44939172268), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'}, + 10006: {'type': 'model', + 'name': 'frontRight', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-42.3298530579, -20.0590000153, 0.0695294439793), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.40563333035, 1.40563333035, 1.40563333035), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_D.bam'}, + 10007: {'type': 'model', + 'name': 'frontLeft', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-42.3731651306, 18.05443573, 0.0688875243068), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_G1.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10003: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10008: {'type': 'nodepath', + 'name': 'product', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintDuctRoom_Battle01_Cogs.py b/toontown/coghq/CashbotMintDuctRoom_Battle01_Cogs.py new file mode 100755 index 00000000..61883ec4 --- /dev/null +++ b/toontown/coghq/CashbotMintDuctRoom_Battle01_Cogs.py @@ -0,0 +1,43 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 1, + 'level': ToontownGlobals.CashbotMintBossLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintEntrance_Action00.py b/toontown/coghq/CashbotMintEntrance_Action00.py new file mode 100755 index 00000000..4442c494 --- /dev/null +++ b/toontown/coghq/CashbotMintEntrance_Action00.py @@ -0,0 +1,89 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE03a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10000: {'type': 'entrancePoint', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'entranceId': 0, + 'radius': 15, + 'theta': 20}, + 10001: {'type': 'mintProduct', + 'name': '', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-11.4890069962, 20.1173057556, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10003: {'type': 'mintProduct', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-20.4286708832, 12.2706327438, 0.0), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10007: {'type': 'mintProduct', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-19.2144012451, 20.1173057556, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12700}, + 10006: {'type': 'model', + 'name': 'crateStack', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(10.5386743546, 18.1184597015, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_G1.bam'}, + 10008: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(13.8522205353, -20.3127307892, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': 'product', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintGearRoom_Action00.py b/toontown/coghq/CashbotMintGearRoom_Action00.py new file mode 100755 index 00000000..2564fba3 --- /dev/null +++ b/toontown/coghq/CashbotMintGearRoom_Action00.py @@ -0,0 +1,226 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE07a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10007: {'type': 'attribModifier', + 'name': 'goonStrength', + 'comment': '', + 'parentEntId': 0, + 'attribName': 'strength', + 'recursive': 1, + 'typeName': 'goon', + 'value': '10'}, + 10002: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 10, + 'velocity': 4.0}, + 10004: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 10, + 'velocity': 4}, + 10006: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10005, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 10, + 'velocity': 4}, + 10009: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 10, + 'velocity': 4}, + 10011: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(2.15899157524, 2.29615116119, 5.45938539505), + 'hpr': Vec3(331.109100342, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 8, + 'rewardPerGrabMax': 0}, + 10012: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(20.9361133575, 13.8672618866, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.920000016689, 0.920000016689, 0.920000016689), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'}, + 10013: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(57.0218696594, 5.15023899078, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(0.660517215729, 0.660517215729, 0.660517215729), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10015: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-25.9598789215, 59.4411621094, 9.73551368713), + 'hpr': Vec3(274.089996338, 0.0, 0.0), + 'scale': Vec3(1.53790044785, 1.53790044785, 1.53790044785), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'}, + 10016: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(33.3394889832, -18.3643035889, 0.0), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(0.660000026226, 0.660000026226, 0.660000026226), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D1.bam'}, + 10017: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(169.699996948, 0.0, 0.0), + 'scale': Vec3(0.902469694614, 0.902469694614, 0.902469694614), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D4.bam'}, + 10020: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(-12.071434021, 0.0, 0.0), + 'hpr': Vec3(288.434936523, 0.0, 0.0), + 'scale': Vec3(0.902469694614, 0.902469694614, 0.902469694614), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D4.bam'}, + 10022: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-5.97179174423, -60.3133621216, 0.0), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(0.869391143322, 0.869391143322, 0.869391143322), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10000: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10010: {'type': 'nodepath', + 'name': 'healPuzzle', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(43.1796302795, 0.0, 0.0), + 'hpr': Point3(-90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10018: {'type': 'nodepath', + 'name': 'rightVertPipes', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-16.4536571503, -45.3981781006, -8.39999961853), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(0.649999976158, 0.649999976158, 1.55999994278)}, + 10021: {'type': 'nodepath', + 'name': 'rightPipes', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10001: {'type': 'path', + 'name': 'nearPace', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-59.7391967773, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'pathIndex': 3, + 'pathScale': 1.0}, + 10003: {'type': 'path', + 'name': 'bowtie', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-40.0336875916, 0.0, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'pathIndex': 2, + 'pathScale': 1.0}, + 10005: {'type': 'path', + 'name': 'bridgePace', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-8.80618190765, -1.5122487545, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'pathIndex': 3, + 'pathScale': 1.0}, + 10008: {'type': 'path', + 'name': 'farPace', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(7.5265827179, 7.56240034103, 0.0), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'pathIndex': 3, + 'pathScale': 1.0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintGearRoom_Battle00.py b/toontown/coghq/CashbotMintGearRoom_Battle00.py new file mode 100755 index 00000000..c639b2de --- /dev/null +++ b/toontown/coghq/CashbotMintGearRoom_Battle00.py @@ -0,0 +1,72 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE07a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10001: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-27.3600006104, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'cellId': 0, + 'radius': 10.0}, + 10002: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(57.0218696594, 3.79224324226, 0.0), + 'hpr': Vec3(111.037513733, 0.0, 0.0), + 'scale': Vec3(1.72596073151, 1.72596073151, 1.72596073151), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/boiler_B1.bam'}, + 10004: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-7.67323350906, -61.4041023254, 0.207314386964), + 'hpr': Vec3(169.695159912, 0.0, 0.0), + 'scale': Vec3(1.9143627882, 1.9143627882, 1.9143627882), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/boiler_A2.bam'}, + 10005: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-25.9598789215, 44.8260116577, 9.73551368713), + 'hpr': Vec3(94.0856170654, 0.0, 0.0), + 'scale': Vec3(1.53790044785, 1.53790044785, 1.53790044785), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-52.7907714844, 0.0, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10003: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintGearRoom_Battle00_Cogs.py b/toontown/coghq/CashbotMintGearRoom_Battle00_Cogs.py new file mode 100755 index 00000000..218ebaaa --- /dev/null +++ b/toontown/coghq/CashbotMintGearRoom_Battle00_Cogs.py @@ -0,0 +1,43 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintGearRoom_Battle01.py b/toontown/coghq/CashbotMintGearRoom_Battle01.py new file mode 100755 index 00000000..00dc0ec8 --- /dev/null +++ b/toontown/coghq/CashbotMintGearRoom_Battle01.py @@ -0,0 +1,187 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE07a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10015: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10009, + 'pos': Point3(0.35536468029, 1.03268241882, 0.0), + 'hpr': Point3(99.4599990845, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 'random', + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 10}, + 10007: {'type': 'gear', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(2.0, -65.0800018311, 11.0900001526), + 'hpr': Vec3(0.0, 90.0, 0.0), + 'scale': Point3(10.0, 10.0, 10.0), + 'degreesPerSec': 10.0, + 'gearScale': 1, + 'modelType': 'mint', + 'orientation': 'horizontal', + 'phaseShift': 0}, + 10008: {'type': 'gear', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-15.0, -65.0800018311, 15.0), + 'hpr': Vec3(0.0, 90.0, 0.0), + 'scale': Point3(10.0, 10.0, 10.0), + 'degreesPerSec': -10.0, + 'gearScale': 1, + 'modelType': 'mint', + 'orientation': 'horizontal', + 'phaseShift': 0.26}, + 10016: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10009, + 'pos': Point3(2.0886592865, -5.19625711441, 0.0), + 'hpr': Vec3(80.5376815796, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 7, + 'rewardPerGrabMax': 10}, + 10001: {'type': 'locator', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'searchPath': '**/EXIT'}, + 10012: {'type': 'mintProduct', + 'name': '', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10013: {'type': 'mintProduct', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(33.1602783203, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10014: {'type': 'mintProduct', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0.0, -26.4398918152, 0.0), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10002: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'}, + 10004: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(28.4207668304, 0.886719465256, 0.0890568122268), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.42410159111, 1.42410159111, 1.42410159111), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_D.bam'}, + 10005: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(1.02179563046, -5.71805810928, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(0.912700533867, 0.912700533867, 0.912700533867), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_G1.bam'}, + 10006: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(28.0896816254, 6.65470600128, 0.0692733898759), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'}, + 10010: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-30.3416347504, -52.2014503479, 4.94085407257), + 'hpr': Vec3(180.0, 270.0, 270.0), + 'scale': Vec3(0.496759712696, 0.496759712696, 0.496759712696), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10017: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-47.7702331543, -15.7725839615, 0.0), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(0.75, 0.75, 0.75), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-16.2589473724, 49.1446685791, 10.2881116867), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10003: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10009: {'type': 'nodepath', + 'name': 'barrels', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(39.311466217, 1.05693364143, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10011: {'type': 'nodepath', + 'name': 'product', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-24.7985076904, 59.8468132019, 10.0710220337), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintGearRoom_Battle01_Cogs.py b/toontown/coghq/CashbotMintGearRoom_Battle01_Cogs.py new file mode 100755 index 00000000..61883ec4 --- /dev/null +++ b/toontown/coghq/CashbotMintGearRoom_Battle01_Cogs.py @@ -0,0 +1,43 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 1, + 'level': ToontownGlobals.CashbotMintBossLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintLavaRoomFoyer_Action00.py b/toontown/coghq/CashbotMintLavaRoomFoyer_Action00.py new file mode 100755 index 00000000..739037c5 --- /dev/null +++ b/toontown/coghq/CashbotMintLavaRoomFoyer_Action00.py @@ -0,0 +1,196 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE18a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10009: {'type': 'attribModifier', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'attribName': 'modelPath', + 'recursive': 1, + 'typeName': 'model', + 'value': ''}, + 10017: {'type': 'attribModifier', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'attribName': 'scale', + 'recursive': 1, + 'typeName': 'model', + 'value': 'Vec3(.955,1,1)'}, + 10015: {'type': 'crate', + 'name': '', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0.0, 0.0, 0.0), + 'scale': 0.92, + 'crushCellId': None, + 'gridId': 10014, + 'modelType': 1, + 'pushable': 1}, + 10014: {'type': 'grid', + 'name': 'crateGrid', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-6.73230838776, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'cellSize': 3.0, + 'numCol': 4, + 'numRow': 2}, + 10005: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(19.0611743927, -20.78266716, 0.0), + 'hpr': Vec3(160.016891479, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 8, + 'rewardPerGrabMax': 0}, + 10001: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-7.89672088623, 21.0129165649, 0.0), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'}, + 10002: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-17.8739471436, 16.2802295685, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'}, + 10006: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(20.9172992706, 20.2094459534, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'}, + 10007: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-18.3651504517, -19.2698841095, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'}, + 10018: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.954999983311, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10019: {'type': 'model', + 'name': 'copy of middle', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(-5.72357320786, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.954999983311, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10020: {'type': 'model', + 'name': 'copy of middle', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(5.71999979019, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.954999983311, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10021: {'type': 'model', + 'name': 'copy of middle', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(11.4399995804, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.954999983311, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10000: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10003: {'type': 'nodepath', + 'name': 'cratePuzzle', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': 1}, + 10008: {'type': 'nodepath', + 'name': 'wall', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(13.4399995804, 6.57999992371, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': Vec3(1.95812249184, 1.5, 1.79999995232)}, + 10016: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(-4.04936361313, 3.45528435707, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'crushCellId': None, + 'damage': 6, + 'headScale': Point3(4.0, 3.0, 4.0), + 'modelPath': 0, + 'motion': 3, + 'period': 5.0, + 'phaseShift': 0.0, + 'range': 15.0, + 'shaftScale': Point3(0.75, 10.0, 0.75), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 1, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintLavaRoomFoyer_Action01.py b/toontown/coghq/CashbotMintLavaRoomFoyer_Action01.py new file mode 100755 index 00000000..bb606886 --- /dev/null +++ b/toontown/coghq/CashbotMintLavaRoomFoyer_Action01.py @@ -0,0 +1,242 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE18a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10000: {'type': 'attribModifier', + 'name': '', + 'comment': '', + 'parentEntId': 10004, + 'attribName': 'modelPath', + 'recursive': 1, + 'typeName': 'model', + 'value': ''}, + 10001: {'type': 'attribModifier', + 'name': '', + 'comment': '', + 'parentEntId': 10004, + 'attribName': 'scale', + 'recursive': 1, + 'typeName': 'model', + 'value': 'Vec3(.955,1,1)'}, + 10019: {'type': 'attribModifier', + 'name': '', + 'comment': '', + 'parentEntId': 10015, + 'attribName': 'modelPath', + 'recursive': 1, + 'typeName': 'model', + 'value': ''}, + 10006: {'type': 'gear', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'degreesPerSec': -4.0, + 'gearScale': 14.193780914463838, + 'modelType': 'mint', + 'orientation': 'horizontal', + 'phaseShift': 0}, + 10007: {'type': 'gear', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 4.28999996185), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'degreesPerSec': 4.0, + 'gearScale': 14.193780914463838, + 'modelType': 'mint', + 'orientation': 'horizontal', + 'phaseShift': 0}, + 10009: {'type': 'gear', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 8.57999992371), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'degreesPerSec': -4.0, + 'gearScale': 14.193780914463838, + 'modelType': 'mint', + 'orientation': 'horizontal', + 'phaseShift': 0.055}, + 10014: {'type': 'gear', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 12.8699998856), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'degreesPerSec': 4.0, + 'gearScale': 14.193780914463838, + 'modelType': 'mint', + 'orientation': 'horizontal', + 'phaseShift': 0.06}, + 10018: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10017, + 'pos': Point3(-2.03643107414, 2.34967470169, 5.46433734894), + 'hpr': Vec3(34.1522636414, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10002: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(6.5, 6.5, 6.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/RoundShadow.bam'}, + 10005: {'type': 'model', + 'name': 'doorwayCrate', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(27.0090961456, 0.850000023842, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10008: {'type': 'model', + 'name': 'shaft', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 7.25891637802), + 'hpr': Vec3(0.0, 0.0, 180.0), + 'scale': Vec3(5.35842609406, 5.35842609406, 5.35842609406), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModel', + 'modelPath': 'phase_10/models/cashbotHQ/MintGearPost.bam'}, + 10010: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.954999983311, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10011: {'type': 'model', + 'name': 'copy of middle', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-5.72357320786, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.954999983311, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10012: {'type': 'model', + 'name': 'copy of middle', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(5.71999979019, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.954999983311, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10013: {'type': 'model', + 'name': 'copy of middle', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(11.4399995804, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.954999983311, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10015: {'type': 'model', + 'name': 'crateStack', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-18.0376968384, 20.2023410797, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10016: {'type': 'model', + 'name': 'upper', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(0.0, 0.0, 5.42841148376), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10017: {'type': 'model', + 'name': 'copy of upper', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(0.0, 0.0, 5.43412637711), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10021: {'type': 'model', + 'name': 'crateStack', + 'comment': '', + 'parentEntId': 10020, + 'pos': Point3(21.064825058, 20.1899757385, 9.87216758728), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'}, + 10003: {'type': 'nodepath', + 'name': 'gears', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-3.18650078773, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10004: {'type': 'nodepath', + 'name': 'wall', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(19.5468139648, 6.37875938416, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': Vec3(1.95812249184, 1.5, 1.79999995232)}, + 10020: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintLavaRoomFoyer_Battle00.py b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle00.py new file mode 100755 index 00000000..46de9114 --- /dev/null +++ b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle00.py @@ -0,0 +1,82 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE18a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10004: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(23.908908844, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'cellId': 0, + 'radius': 10}, + 10002: {'type': 'model', + 'name': 'crates', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(17.3283443451, 20.1608715057, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'}, + 10003: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(-14.04317379, 20.9443073273, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'}, + 10006: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-3.16324114799, -0.608929097652, 5.57751512527), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': 1}, + 10001: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10005: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(-90.0, 0.0, 0.0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintLavaRoomFoyer_Battle00_Cogs.py b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle00_Cogs.py new file mode 100755 index 00000000..fe40eb3e --- /dev/null +++ b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle00_Cogs.py @@ -0,0 +1,44 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleParent = 10005 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': BattleParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintLavaRoomFoyer_Battle01.py b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle01.py new file mode 100755 index 00000000..7cb5a727 --- /dev/null +++ b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle01.py @@ -0,0 +1,149 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE18a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10004: {'type': 'locator', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'searchPath': '**/EXIT1'}, + 10013: {'type': 'mintProduct', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(-3.94549632072, 18.2319583893, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10014: {'type': 'mintProduct', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(3.85402703285, 18.2319583893, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10015: {'type': 'mintProduct', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(-18.5567684174, 14.1500225067, 6.5729341507), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10001: {'type': 'model', + 'name': 'vaultDoor', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'}, + 10003: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(13.2311220169, 20.3564720154, 0.305192321539), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.21849691868, 1.21849691868, 1.21849691868), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_A.bam'}, + 10007: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-17.5481491089, 20.8210849762, 0.00756931304932), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.30483365059, 1.30483365059, 1.30483365059), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'}, + 10008: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(-1.55398654938, -4.84950685501, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.913593888283, 0.913593888283, 0.913593888283), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'}, + 10009: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-19.0412902832, -18.4314842224, 0.00867449026555), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_G1.bam'}, + 10010: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(18.6662273407, -13.083732605, 0.00570194004104), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': 1}, + 10002: {'type': 'nodepath', + 'name': 'crates', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10005: {'type': 'nodepath', + 'name': 'battle', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(-90.0, 0.0, 0.0), + 'scale': 1}, + 10011: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(-90.0, 0.0, 0.0), + 'scale': 1}, + 10012: {'type': 'nodepath', + 'name': 'product', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintLavaRoomFoyer_Battle01_Cogs.py b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle01_Cogs.py new file mode 100755 index 00000000..505d6def --- /dev/null +++ b/toontown/coghq/CashbotMintLavaRoomFoyer_Battle01_Cogs.py @@ -0,0 +1,44 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleParent = 10005 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': BattleParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 1, + 'level': ToontownGlobals.CashbotMintBossLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintLavaRoom_Action00.py b/toontown/coghq/CashbotMintLavaRoom_Action00.py new file mode 100755 index 00000000..65064c60 --- /dev/null +++ b/toontown/coghq/CashbotMintLavaRoom_Action00.py @@ -0,0 +1,101 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE19a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10005: {'type': 'attribModifier', + 'name': 'sinkDuration', + 'comment': '', + 'parentEntId': 10002, + 'attribName': 'sinkDuration', + 'recursive': 1, + 'typeName': 'sinkingPlatform', + 'value': '2.0'}, + 10006: {'type': 'attribModifier', + 'name': 'riseDuration', + 'comment': '', + 'parentEntId': 10002, + 'attribName': 'riseDuration', + 'recursive': 1, + 'typeName': 'sinkingPlatform', + 'value': '2.0'}, + 10004: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-52.5099983215, -3.81641983986, 4.99661874771), + 'hpr': Vec3(100.165977478, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 8, + 'rewardPerGrabMax': 0}, + 10008: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(-24.928899765, -4.86700963974, -1.70696532726), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'loadType': 'loadModel', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A5.bam'}, + 10002: {'type': 'nodepath', + 'name': 'sinkingPlatforms', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(2.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.864239275455, 0.864239275455, 0.864239275455)}, + 10007: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10000: {'type': 'sinkingPlatform', + 'name': 'plat1', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'pauseBeforeRise': 1, + 'riseDuration': 2.0, + 'sinkDuration': 2.0, + 'verticalRange': 3.0}, + 10001: {'type': 'sinkingPlatform', + 'name': 'plat0', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(20.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'pauseBeforeRise': 1, + 'riseDuration': 2.0, + 'sinkDuration': 2.0, + 'verticalRange': 3.0}, + 10003: {'type': 'sinkingPlatform', + 'name': 'plat2', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-20.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'pauseBeforeRise': 1, + 'riseDuration': 2.0, + 'sinkDuration': 2.0, + 'verticalRange': 3.0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintLobby_Action00.py b/toontown/coghq/CashbotMintLobby_Action00.py new file mode 100755 index 00000000..e38dee00 --- /dev/null +++ b/toontown/coghq/CashbotMintLobby_Action00.py @@ -0,0 +1,1789 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE04a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10005: {'type': 'attribModifier', + 'name': 'stomperPeriod', + 'comment': '', + 'parentEntId': 10046, + 'attribName': 'period', + 'recursive': 1, + 'typeName': 'stomper', + 'value': '2.2'}, + 10015: {'type': 'attribModifier', + 'name': 'stomperShaftScale', + 'comment': '', + 'parentEntId': 10046, + 'attribName': 'shaftScale', + 'recursive': 1, + 'typeName': 'stomper', + 'value': 'Vec3(1,5,1)'}, + 10067: {'type': 'attribModifier', + 'name': 'stomperDamage', + 'comment': '', + 'parentEntId': 10000, + 'attribName': 'damage', + 'recursive': 1, + 'typeName': 'stomper', + 'value': '8'}, + 10130: {'type': 'attribModifier', + 'name': '', + 'comment': '', + 'parentEntId': 10153, + 'attribName': 'modelPath', + 'recursive': 1, + 'typeName': 'model', + 'value': "'phase_10/models/cogHQ/CBMetalCrate2.bam'"}, + 10145: {'type': 'attribModifier', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10007, + 'attribName': 'modelPath', + 'recursive': 1, + 'typeName': 'model', + 'value': "'phase_10/models/cogHQ/CBMetalCrate2.bam'"}, + 10173: {'type': 'attribModifier', + 'name': 'copy of stomperShaftScale', + 'comment': '', + 'parentEntId': 10154, + 'attribName': 'shaftScale', + 'recursive': 1, + 'typeName': 'stomper', + 'value': 'Vec3(1,5,1)'}, + 10169: {'type': 'gagBarrel', + 'name': 'gag', + 'comment': '', + 'parentEntId': 10170, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(1.0, 0.0, 0.0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 'random', + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 6}, + 10171: {'type': 'gagBarrel', + 'name': 'gagLeft', + 'comment': '', + 'parentEntId': 10170, + 'pos': Point3(-3.0, 0.0, 0.0), + 'hpr': Vec3(-6.0, 0.0, 0.0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 'random', + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 6}, + 10172: {'type': 'gagBarrel', + 'name': 'gagRight', + 'comment': '', + 'parentEntId': 10170, + 'pos': Point3(3.0, 0.0, 0.0), + 'hpr': Vec3(9.0, 0.0, 0.0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 'random', + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 6}, + 10177: {'type': 'healBarrel', + 'name': 'healLeft', + 'comment': '', + 'parentEntId': 10170, + 'pos': Point3(-1.05319225788, 0.0, 4.12134313583), + 'hpr': Vec3(20.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 6}, + 10178: {'type': 'healBarrel', + 'name': 'healRight', + 'comment': '', + 'parentEntId': 10170, + 'pos': Point3(2.16605138779, 0.0, 4.12134313583), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 6}, + 10181: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-2.92873573303, 110.585220337, 5.00378036499), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 10, + 'rewardPerGrabMax': 0}, + 10023: {'type': 'mintShelf', + 'name': 'left', + 'comment': '', + 'parentEntId': 10017, + 'pos': Point3(-11.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10026: {'type': 'mintShelf', + 'name': 'right', + 'comment': '', + 'parentEntId': 10017, + 'pos': Point3(11.0, 0.0, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10060: {'type': 'mintShelf', + 'name': 'left', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(-11.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10061: {'type': 'mintShelf', + 'name': 'right', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(11.0, 0.0, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10064: {'type': 'mintShelf', + 'name': 'left', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(-11.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10066: {'type': 'mintShelf', + 'name': 'right', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(11.0, 0.0, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10068: {'type': 'mintShelf', + 'name': 'right', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(18.0, -1.0, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Point3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10069: {'type': 'mintShelf', + 'name': 'left', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(-18.0, -1.0, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Point3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10093: {'type': 'mintShelf', + 'name': 'left', + 'comment': '', + 'parentEntId': 10092, + 'pos': Point3(-11.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10094: {'type': 'mintShelf', + 'name': 'right', + 'comment': '', + 'parentEntId': 10092, + 'pos': Point3(11.0, 0.0, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10096: {'type': 'mintShelf', + 'name': 'left', + 'comment': '', + 'parentEntId': 10095, + 'pos': Point3(-11.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10097: {'type': 'mintShelf', + 'name': 'right', + 'comment': '', + 'parentEntId': 10095, + 'pos': Point3(11.0, 0.0, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10099: {'type': 'mintShelf', + 'name': 'left', + 'comment': '', + 'parentEntId': 10098, + 'pos': Point3(-11.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10100: {'type': 'mintShelf', + 'name': 'right', + 'comment': '', + 'parentEntId': 10098, + 'pos': Point3(11.0, 0.0, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10103: {'type': 'mintShelf', + 'name': 'right', + 'comment': '', + 'parentEntId': 10101, + 'pos': Point3(11.0, 0.0, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10159: {'type': 'mintShelf', + 'name': 'right', + 'comment': '', + 'parentEntId': 10157, + 'pos': Point3(11.0, 0.0, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10161: {'type': 'mintShelf', + 'name': 'left', + 'comment': '', + 'parentEntId': 10160, + 'pos': Point3(-11.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10162: {'type': 'mintShelf', + 'name': 'right', + 'comment': '', + 'parentEntId': 10160, + 'pos': Point3(11.0, 0.0, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10164: {'type': 'mintShelf', + 'name': 'left', + 'comment': '', + 'parentEntId': 10163, + 'pos': Point3(-11.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10165: {'type': 'mintShelf', + 'name': 'right', + 'comment': '', + 'parentEntId': 10163, + 'pos': Point3(11.0, 0.0, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'mintId': 12500}, + 10001: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10002: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10003: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10008: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10009: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10011: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10012: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10013: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10014: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10024: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10020, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10025: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10020, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10027: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10028: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10029: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10030: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10036: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10033, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10037: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10033, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10038: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10033, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10039: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10034, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10040: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10034, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10041: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10034, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10042: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10043: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10048: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10044, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10049: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10044, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10050: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10051: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10052: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10047, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10053: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10047, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10076: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10075, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10077: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10075, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10078: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10075, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10079: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10047, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10084: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10081, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10085: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10081, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10086: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10081, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10087: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10082, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10088: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10082, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10089: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10082, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10090: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10083, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10091: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10083, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10102: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10072, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10108: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10105, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10109: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10105, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10110: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10072, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10111: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10106, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10112: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10106, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10113: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10072, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10114: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10107, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10115: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10107, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10116: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10107, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10121: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10118, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10122: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10118, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10123: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10118, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10124: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10119, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10125: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10119, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10126: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10119, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10127: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10120, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10128: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10120, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10129: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10073, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10131: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10073, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10132: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10074, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10133: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10074, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10134: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10074, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10139: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10136, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10140: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10136, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10141: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10136, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10143: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10137, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10146: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10137, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10147: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10138, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10148: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10138, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10149: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10144, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10150: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10144, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10151: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10144, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10152: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10138, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10158: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10157, + 'pos': Point3(-11.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/shelf_A1.bam'}, + 10000: {'type': 'nodepath', + 'name': 'crateField', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, -51.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10004: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10006: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10007: {'type': 'nodepath', + 'name': 'crates', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 0.800000011921)}, + 10010: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10017: {'type': 'nodepath', + 'name': 'wall5', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 90.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10018: {'type': 'nodepath', + 'name': 'crateSquare0', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10019: {'type': 'nodepath', + 'name': 'crateSquare1', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 18.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10020: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10021: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10022: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10031: {'type': 'nodepath', + 'name': 'crateSquare3', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 54.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10032: {'type': 'nodepath', + 'name': 'crateSquare2', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 36.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10033: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10034: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10035: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10044: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10045: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10046: {'type': 'nodepath', + 'name': 'stompers', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-1.0, 0.0, 4.40000009537), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10047: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10059: {'type': 'nodepath', + 'name': 'wall6', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 108.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10062: {'type': 'nodepath', + 'name': 'wall7', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 124.5, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.39999997616, 1.0)}, + 10063: {'type': 'nodepath', + 'name': 'walls', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0.0, -0.019999999553, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10065: {'type': 'nodepath', + 'name': 'wall0', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10070: {'type': 'nodepath', + 'name': 'leftBranch', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-17.8978881836, 72.0, 0.0), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10071: {'type': 'nodepath', + 'name': 'crateSquare0', + 'comment': 'Y=N*18', + 'parentEntId': 10153, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10072: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10071, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10073: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10071, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10074: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10071, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10075: {'type': 'nodepath', + 'name': 'frontCrateRow', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0.0, -12.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.844285488129, 1.0, 1.0)}, + 10080: {'type': 'nodepath', + 'name': 'crateSquare4', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 72.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10081: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10080, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10082: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10080, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10083: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10080, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10092: {'type': 'nodepath', + 'name': 'wall1', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 18.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10095: {'type': 'nodepath', + 'name': 'wall2', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 36.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10098: {'type': 'nodepath', + 'name': 'wall3', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 54.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10101: {'type': 'nodepath', + 'name': 'wall4', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 72.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10104: {'type': 'nodepath', + 'name': 'crateSquare5', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 90.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10105: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10104, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10106: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10104, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10107: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10104, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10117: {'type': 'nodepath', + 'name': 'crateSquare6', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 108.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10118: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10117, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10119: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10117, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10120: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10117, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10135: {'type': 'nodepath', + 'name': 'crateSquare1', + 'comment': 'Y=N*18', + 'parentEntId': 10153, + 'pos': Point3(0.0, 18.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10136: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10135, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10137: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10135, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10138: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10135, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10142: {'type': 'nodepath', + 'name': 'crateSquare7', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 126.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10144: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10142, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10153: {'type': 'nodepath', + 'name': 'crates', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 0.800000011921)}, + 10154: {'type': 'nodepath', + 'name': 'stompers', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(-1.0, 0.0, 4.40000009537), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10156: {'type': 'nodepath', + 'name': 'walls', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10157: {'type': 'nodepath', + 'name': 'wall0', + 'comment': '', + 'parentEntId': 10156, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10160: {'type': 'nodepath', + 'name': 'wall1', + 'comment': '', + 'parentEntId': 10156, + 'pos': Point3(0.0, 18.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10163: {'type': 'nodepath', + 'name': 'wall2', + 'comment': '', + 'parentEntId': 10156, + 'pos': Point3(0.0, 36.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10168: {'type': 'nodepath', + 'name': 'safeArea', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(0.0, 40.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10170: {'type': 'nodepath', + 'name': 'barrels', + 'comment': '', + 'parentEntId': 10168, + 'pos': Point3(0.0, 9.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10016: {'type': 'stomper', + 'name': 'stomper6', + 'comment': 'Y=N*18', + 'parentEntId': 10046, + 'pos': Point3(1.0, 108.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10054: {'type': 'stomper', + 'name': 'stomper1', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(1.0, 18.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10055: {'type': 'stomper', + 'name': 'stomper0', + 'comment': 'Y=N*18', + 'parentEntId': 10046, + 'pos': Point3(1.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10056: {'type': 'stomper', + 'name': 'stomper2', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(1.0, 36.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10057: {'type': 'stomper', + 'name': 'stomper3', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(1.0, 54.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10058: {'type': 'stomper', + 'name': 'stomper4', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(1.0, 72.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10155: {'type': 'stomper', + 'name': 'stomper5', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(1.0, 90.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10166: {'type': 'stomper', + 'name': 'stomper0', + 'comment': 'Y=N*18', + 'parentEntId': 10154, + 'pos': Point3(1.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10167: {'type': 'stomper', + 'name': 'stomper1', + 'comment': 'Y=N*18', + 'parentEntId': 10154, + 'pos': Point3(1.0, 18.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintLobby_Battle00.py b/toontown/coghq/CashbotMintLobby_Battle00.py new file mode 100755 index 00000000..3a425e7c --- /dev/null +++ b/toontown/coghq/CashbotMintLobby_Battle00.py @@ -0,0 +1,422 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE04a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10001: {'type': 'battleBlocker', + 'name': 'exitBlocker', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 76.2264404297, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'cellId': 0, + 'radius': 10.0}, + 10021: {'type': 'battleBlocker', + 'name': 'middleBlocker', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(9.79564476013, 7.17855405807, 0.0), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.61347305775, 0.225867271423, 1.99822974205), + 'cellId': 1, + 'radius': 10.0}, + 10061: {'type': 'battleBlocker', + 'name': 'frontBlocker', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-45.6075019836, -22.7538051605, 0.0), + 'hpr': Vec3(45.0, 0.0, 0.0), + 'scale': Vec3(1.61347305775, 0.225867271423, 1.99822974205), + 'cellId': 2, + 'radius': 10.0}, + 10025: {'type': 'mintProductPallet', + 'name': '', + 'comment': '', + 'parentEntId': 10024, + 'pos': Point3(0.0, 7.96000003815, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10031: {'type': 'mintProductPallet', + 'name': '', + 'comment': '', + 'parentEntId': 10024, + 'pos': Point3(-32.7900009155, -5.48999977112, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10032: {'type': 'mintProductPallet', + 'name': '', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(-25.0, 9.05000019073, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10033: {'type': 'mintProductPallet', + 'name': '', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(26.8400001526, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10003: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-60.8400001526, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10004: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10005: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-47.4124145508, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10006: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10005, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10007: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-33.9436340332, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10008: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10009: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-20.4898967743, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10010: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10009, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10011: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(60.7196426392, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10012: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10013: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(33.2060928345, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10014: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10013, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10015: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(19.7813663483, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10016: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10017: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-7.05515527725, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10018: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10017, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10019: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(6.35370635986, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10020: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10042: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10041, + 'pos': Point3(-7.05515527725, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10043: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10041, + 'pos': Point3(-60.8400001526, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10044: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10041, + 'pos': Point3(6.35370635986, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10045: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10041, + 'pos': Point3(-47.4124145508, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10046: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10041, + 'pos': Point3(-33.9436340332, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10047: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10041, + 'pos': Point3(-20.4898967743, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10048: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10041, + 'pos': Point3(60.7196426392, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10049: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10041, + 'pos': Point3(33.2060928345, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10050: {'type': 'mintShelf', + 'name': 'bookshelf', + 'comment': '', + 'parentEntId': 10041, + 'pos': Point3(19.7813663483, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10051: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10042, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10052: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10043, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10053: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10044, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10054: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10055: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10056: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10047, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10057: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10048, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10058: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10049, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10059: {'type': 'mintShelf', + 'name': 'copy of bookshelf', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(0.180000007153, 6.85808324814, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 58.7970542908, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10002: {'type': 'nodepath', + 'name': 'backWall', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 22.0885009766, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10022: {'type': 'nodepath', + 'name': 'middleCogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 7.5760216713, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10023: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10024: {'type': 'nodepath', + 'name': 'frontMoney', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(22.4126205444, -39.3388214111, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10028: {'type': 'nodepath', + 'name': 'backMoney', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, 48.498249054, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10041: {'type': 'nodepath', + 'name': 'backWall', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, -6.69597911835, 0.0), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10060: {'type': 'nodepath', + 'name': 'frontCogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-28.8658733368, -31.173248291, 0.0), + 'hpr': Vec3(51.3401908875, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintLobby_Battle00_Cogs.py b/toontown/coghq/CashbotMintLobby_Battle00_Cogs.py new file mode 100755 index 00000000..768dd1fb --- /dev/null +++ b/toontown/coghq/CashbotMintLobby_Battle00_Cogs.py @@ -0,0 +1,123 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +MidCogParent = 10022 +FrontCogParent = 10060 +BattleCellId = 0 +MidBattleCellId = 1 +FrontBattleCellId = 2 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}, + MidBattleCellId: {'parentEntId': MidCogParent, + 'pos': Point3(0, 0, 0)}, + FrontBattleCellId: {'parentEntId': FrontCogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': MidCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': MidBattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': MidCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': MidBattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': MidCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': MidBattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': MidCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': MidBattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintLobby_Battle01.py b/toontown/coghq/CashbotMintLobby_Battle01.py new file mode 100755 index 00000000..6068d846 --- /dev/null +++ b/toontown/coghq/CashbotMintLobby_Battle01.py @@ -0,0 +1,214 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE04a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10001: {'type': 'locator', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'searchPath': '**/DGG.EXIT'}, + 10014: {'type': 'mintProduct', + 'name': '', + 'comment': '', + 'parentEntId': 10013, + 'pos': Point3(-7.80045461655, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10015: {'type': 'mintProduct', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10013, + 'pos': Point3(15.7111005783, -2.0225315094, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10016: {'type': 'mintProduct', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10013, + 'pos': Point3(6.66144704819, -1.54405653477, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10017: {'type': 'mintProductPallet', + 'name': '', + 'comment': '', + 'parentEntId': 10020, + 'pos': Point3(34.8923339844, 32.3863983154, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10021: {'type': 'mintProductPallet', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10020, + 'pos': Point3(-34.8899993896, 32.3863983154, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12600}, + 10022: {'type': 'mintProductPallet', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10020, + 'pos': Point3(34.8923339844, -23.3355064392, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12600}, + 10023: {'type': 'mintProductPallet', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10020, + 'pos': Point3(-34.8899993896, -23.3355064392, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12600}, + 10002: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'}, + 10005: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(26.7393283844, 70.7373962402, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'}, + 10006: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(63.8051605225, 33.5718955994, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'}, + 10007: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(63.7982444763, -19.1328792572, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'}, + 10008: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(26.769536972, -56.2981185913, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'}, + 10009: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-26.0181350708, -56.2504425049, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'}, + 10010: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-63.1796150208, -19.1894207001, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'}, + 10011: {'type': 'model', + 'name': 'copy of (6)', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-63.194984436, 33.6143226624, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'}, + 10012: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-26.161315918, 70.7677078247, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': 1}, + 10003: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': 'posts', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10013: {'type': 'nodepath', + 'name': 'productByVault', + 'comment': '', + 'parentEntId': 10020, + 'pos': Point3(0.0, 67.5628051758, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10020: {'type': 'nodepath', + 'name': 'product', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintLobby_Battle01_Cogs.py b/toontown/coghq/CashbotMintLobby_Battle01_Cogs.py new file mode 100755 index 00000000..61883ec4 --- /dev/null +++ b/toontown/coghq/CashbotMintLobby_Battle01_Cogs.py @@ -0,0 +1,43 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 1, + 'level': ToontownGlobals.CashbotMintBossLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintOilRoom_Battle00.py b/toontown/coghq/CashbotMintOilRoom_Battle00.py new file mode 100755 index 00000000..f0a561dc --- /dev/null +++ b/toontown/coghq/CashbotMintOilRoom_Battle00.py @@ -0,0 +1,36 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE22a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10001: {'type': 'model', + 'name': 'vaultDoor', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(7.2503657341, -35.8064537048, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintOilRoom_Battle00_Cogs.py b/toontown/coghq/CashbotMintOilRoom_Battle00_Cogs.py new file mode 100755 index 00000000..61883ec4 --- /dev/null +++ b/toontown/coghq/CashbotMintOilRoom_Battle00_Cogs.py @@ -0,0 +1,43 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 1, + 'level': ToontownGlobals.CashbotMintBossLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintPaintMixerReward_Battle00.py b/toontown/coghq/CashbotMintPaintMixerReward_Battle00.py new file mode 100755 index 00000000..b8502595 --- /dev/null +++ b/toontown/coghq/CashbotMintPaintMixerReward_Battle00.py @@ -0,0 +1,165 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE11a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10024: {'type': 'mintProduct', + 'name': '', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(-4.0706076622, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10003: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12600}, + 10005: {'type': 'mintShelf', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12600}, + 10007: {'type': 'mintShelf', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(13.4306573868, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12600}, + 10009: {'type': 'mintShelf', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(-13.4300003052, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12600}, + 10013: {'type': 'mintShelf', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-13.4300003052, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12600}, + 10014: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12600}, + 10015: {'type': 'mintShelf', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12600}, + 10016: {'type': 'mintShelf', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(13.4306573868, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12600}, + 10001: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 22.2368240356, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': 'backWall', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(0.0, 19.0782051086, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10006: {'type': 'nodepath', + 'name': 'rightShelves', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(17.2106304169, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10008: {'type': 'nodepath', + 'name': 'leftShelves', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-17.4771251678, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10010: {'type': 'nodepath', + 'name': 'frontWall', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(0.0, -19.1254119873, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10011: {'type': 'nodepath', + 'name': 'leftShelves', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(-17.4771251678, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10012: {'type': 'nodepath', + 'name': 'rightShelves', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(17.2106304169, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10023: {'type': 'nodepath', + 'name': 'byVaultDoor', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(0.0, 16.0373382568, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintPaintMixerReward_Battle00_Cogs.py b/toontown/coghq/CashbotMintPaintMixerReward_Battle00_Cogs.py new file mode 100755 index 00000000..61883ec4 --- /dev/null +++ b/toontown/coghq/CashbotMintPaintMixerReward_Battle00_Cogs.py @@ -0,0 +1,43 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 1, + 'level': ToontownGlobals.CashbotMintBossLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintPaintMixer_Action00.py b/toontown/coghq/CashbotMintPaintMixer_Action00.py new file mode 100755 index 00000000..4dd032db --- /dev/null +++ b/toontown/coghq/CashbotMintPaintMixer_Action00.py @@ -0,0 +1,92 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE10a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10009: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(63.9741363525, -10.9343223572, 9.97696113586), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 8, + 'rewardPerGrabMax': 0}, + 10010: {'type': 'healBarrel', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10009, + 'pos': Point3(0.0, 0.0, 4.13999986649), + 'hpr': Vec3(349.358764648, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 8, + 'rewardPerGrabMax': 0}, + 10000: {'type': 'nodepath', + 'name': 'mixers', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-19.2397289276, 0.0, 5.53999996185), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.758001744747, 0.758001744747, 0.758001744747)}, + 10004: {'type': 'paintMixer', + 'name': 'mixer0', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0.0, 10.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Vec3(1.0, 1.0, 1.0), + 'motion': 'easeInOut', + 'offset': Point3(20.0, 20.0, 0.0), + 'period': 8.0, + 'phaseShift': 0.0, + 'shaftScale': 1, + 'waitPercent': 0.1}, + 10005: {'type': 'paintMixer', + 'name': 'mixer1', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(29.0, 10.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Vec3(1.0, 1.0, 1.0), + 'motion': 'easeInOut', + 'offset': Point3(0.0, -20.0, 0.0), + 'period': 8.0, + 'phaseShift': 0.5, + 'shaftScale': 1, + 'waitPercent': 0.1}, + 10006: {'type': 'paintMixer', + 'name': 'mixer2', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(58.0, -8.94072246552, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Vec3(1.0, 1.0, 1.0), + 'motion': 'easeInOut', + 'offset': Point3(-20.0, -20.0, 0.0), + 'period': 8.0, + 'phaseShift': 0.5, + 'shaftScale': 1, + 'waitPercent': 0.1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintPipeRoom_Action00.py b/toontown/coghq/CashbotMintPipeRoom_Action00.py new file mode 100755 index 00000000..89cf0e20 --- /dev/null +++ b/toontown/coghq/CashbotMintPipeRoom_Action00.py @@ -0,0 +1,578 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE13a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10025: {'type': 'attribModifier', + 'name': 'strength', + 'comment': '', + 'parentEntId': 10002, + 'attribName': 'strength', + 'recursive': 1, + 'typeName': 'goon', + 'value': '10'}, + 10001: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 10, + 'velocity': 4}, + 10005: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 10, + 'velocity': 4}, + 10006: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 10, + 'velocity': 4}, + 10014: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10013, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 10, + 'velocity': 4}, + 10016: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 10, + 'velocity': 4}, + 10018: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10017, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 10, + 'velocity': 4}, + 10021: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10020, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 10, + 'velocity': 4}, + 10024: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 10, + 'velocity': 4}, + 10035: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(-56.3795814514, 0.0, 0.0), + 'hpr': Vec3(106.821411133, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 7, + 'rewardPerGrabMax': 8}, + 10036: {'type': 'healBarrel', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(15.3852472305, 21.0357513428, 0.0), + 'hpr': Vec3(52.4314079285, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 7, + 'rewardPerGrabMax': 8}, + 10029: {'type': 'model', + 'name': 'rightPillar', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0.0, -22.3441867828, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10030: {'type': 'model', + 'name': 'leftPillar', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0.0, 21.9451503754, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10033: {'type': 'model', + 'name': 'backPillar', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(41.4432792664, 0.0, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10034: {'type': 'model', + 'name': 'frontPillar', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(-41.0848464966, 0.0, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10039: {'type': 'model', + 'name': 'rightPillar', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0.0, -66.8615875244, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10040: {'type': 'model', + 'name': 'leftPillar', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0.0, 67.0966033936, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10042: {'type': 'model', + 'name': 'frontRightPillar', + 'comment': '', + 'parentEntId': 10043, + 'pos': Point3(0.0, -22.5711078644, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10044: {'type': 'model', + 'name': 'frontLeftPillar', + 'comment': '', + 'parentEntId': 10043, + 'pos': Point3(0.0, 22.1686630249, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10046: {'type': 'model', + 'name': 'frontRightPillar', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(0.0, -22.5711078644, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10047: {'type': 'model', + 'name': 'frontLeftPillar', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(0.0, 22.1686630249, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10049: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10048, + 'pos': Point3(0.949898421764, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.29060935974, 1.29060935974, 1.29060935974), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'}, + 10050: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10048, + 'pos': Point3(-13.1818971634, -7.17138242722, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'}, + 10051: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10048, + 'pos': Point3(0.968334257603, -13.3785037994, 0.0), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'}, + 10053: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10052, + 'pos': Point3(0.606362164021, -12.1353359222, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_G1.bam'}, + 10054: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10052, + 'pos': Point3(7.85215950012, 20.0426883698, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.16659212112, 1.16659212112, 1.16659212112), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'}, + 10055: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10052, + 'pos': Point3(13.5166940689, -0.819138884544, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.51914477348, 1.51914477348, 1.51914477348), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_D.bam'}, + 10056: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10052, + 'pos': Point3(10.8745326996, 4.61703014374, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'}, + 10057: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10052, + 'pos': Point3(31.8470001221, -14.5645837784, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.1728117466, 1.1728117466, 1.1728117466), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_A.bam'}, + 10058: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10052, + 'pos': Point3(31.9369258881, 14.3037395477, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.1728117466, 1.1728117466, 1.1728117466), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_A.bam'}, + 10002: {'type': 'nodepath', + 'name': 'goons', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10007: {'type': 'nodepath', + 'name': 'rightElbow', + 'comment': '', + 'parentEntId': 10027, + 'pos': Point3(43.3083152771, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10008: {'type': 'nodepath', + 'name': 'leftElbow', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(-38.578956604, -2.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10009: {'type': 'nodepath', + 'name': 'nearRight', + 'comment': '', + 'parentEntId': 10027, + 'pos': Point3(25.6041526794, -41.3753585815, 0.0), + 'hpr': Vec3(320.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10010: {'type': 'nodepath', + 'name': 'nearLeft', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(-25.6000003815, -41.3800010681, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10011: {'type': 'nodepath', + 'name': 'farRight', + 'comment': '', + 'parentEntId': 10027, + 'pos': Point3(25.6000003815, 41.3800010681, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10012: {'type': 'nodepath', + 'name': 'farLeft', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(-25.6000003815, 41.3800010681, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10019: {'type': 'nodepath', + 'name': 'entrance', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(0.0, -82.5020980835, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10022: {'type': 'nodepath', + 'name': 'exit', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(0.0, 88.4478759766, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10026: {'type': 'nodepath', + 'name': 'left', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10027: {'type': 'nodepath', + 'name': 'right', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10028: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, -1.80477809906, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10031: {'type': 'nodepath', + 'name': 'pillars', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10032: {'type': 'nodepath', + 'name': 'centerPillars', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10037: {'type': 'nodepath', + 'name': 'barrels', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(102.779998779, -1.24000000954, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10038: {'type': 'nodepath', + 'name': 'outerPillars', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10043: {'type': 'nodepath', + 'name': 'frontPillars', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(-89.9665527344, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10045: {'type': 'nodepath', + 'name': 'backPillars', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(89.9700012207, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10048: {'type': 'nodepath', + 'name': 'frontProps', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(-100.412567139, -10.8835134506, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.66847121716, 1.66847121716, 1.66847121716)}, + 10052: {'type': 'nodepath', + 'name': 'backProps', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(100.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10000: {'type': 'path', + 'name': 'triangle', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'pathIndex': 1, + 'pathScale': 1.5}, + 10003: {'type': 'path', + 'name': 'square', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 10004: {'type': 'path', + 'name': 'bowtie', + 'comment': '', + 'parentEntId': 10009, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'pathIndex': 2, + 'pathScale': 1.0}, + 10013: {'type': 'path', + 'name': 'square', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 10015: {'type': 'path', + 'name': 'square', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 10017: {'type': 'path', + 'name': 'square', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 10020: {'type': 'path', + 'name': 'pace', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'pathIndex': 3, + 'pathScale': 1.0}, + 10023: {'type': 'path', + 'name': 'pace', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'pathIndex': 3, + 'pathScale': 1.0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintPipeRoom_Battle00.py b/toontown/coghq/CashbotMintPipeRoom_Battle00.py new file mode 100755 index 00000000..647c58ec --- /dev/null +++ b/toontown/coghq/CashbotMintPipeRoom_Battle00.py @@ -0,0 +1,112 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE13a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10001: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(135.90512085, -2.06722736359, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'cellId': 0, + 'radius': 10.0}, + 10003: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-86.7350921631, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.378204703331, 2.94127488136, 2.80858683586), + 'cellId': 1, + 'radius': 10}, + 10005: {'type': 'model', + 'name': 'farLeft', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(45.6502952576, 31.8647651672, 0.228899106383), + 'hpr': Vec3(333.434936523, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10006: {'type': 'model', + 'name': 'farRight', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(50.0559577942, -32.7609710693, 0.228899106383), + 'hpr': Vec3(206.565048218, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10008: {'type': 'model', + 'name': 'nearLeft', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-47.9696311951, 31.8647651672, 0.228899106383), + 'hpr': Vec3(26.5650520325, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D1.bam'}, + 10009: {'type': 'model', + 'name': 'nearRight', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-43.6387481689, -36.8883552551, 0.228899106383), + 'hpr': Vec3(154.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D1.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(102.420700073, 0.0, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10002: {'type': 'nodepath', + 'name': 'frontCogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-94.4453353882, 0.0, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10004: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10007: {'type': 'nodepath', + 'name': 'leftCogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 40.9584655762, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10010: {'type': 'nodepath', + 'name': 'rightCogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, -40.9599990845, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintPipeRoom_Battle00_Cogs.py b/toontown/coghq/CashbotMintPipeRoom_Battle00_Cogs.py new file mode 100755 index 00000000..2797752b --- /dev/null +++ b/toontown/coghq/CashbotMintPipeRoom_Battle00_Cogs.py @@ -0,0 +1,163 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +FrontCogParent = 10002 +LeftCogParent = 10007 +RightCogParent = 10010 +BattleCellId = 0 +FrontBattleCellId = 1 +LeftBattleCellId = 2 +RightBattleCellId = 3 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}, + FrontBattleCellId: {'parentEntId': FrontCogParent, + 'pos': Point3(0, 0, 0)}, + LeftBattleCellId: {'parentEntId': LeftCogParent, + 'pos': Point3(0, 0, 0)}, + RightBattleCellId: {'parentEntId': RightCogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': FrontBattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': FrontBattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': LeftCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': LeftBattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': LeftCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': LeftBattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': LeftCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': LeftBattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': LeftCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': LeftBattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': RightCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': RightBattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': RightCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': RightBattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': RightCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': RightBattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': RightCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': RightBattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintPipeRoom_Battle01.py b/toontown/coghq/CashbotMintPipeRoom_Battle01.py new file mode 100755 index 00000000..d7ecab2c --- /dev/null +++ b/toontown/coghq/CashbotMintPipeRoom_Battle01.py @@ -0,0 +1,357 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE13a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10001: {'type': 'locator', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'searchPath': '**/EXIT'}, + 10020: {'type': 'mintProduct', + 'name': '', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12600}, + 10021: {'type': 'mintProduct', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(13.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12600}, + 10022: {'type': 'mintProduct', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12600}, + 10023: {'type': 'mintProduct', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(-13.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12600}, + 10025: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10027, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12600}, + 10029: {'type': 'mintShelf', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10027, + 'pos': Point3(13.4300003052, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12600}, + 10030: {'type': 'mintShelf', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12600}, + 10031: {'type': 'mintShelf', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(-13.4300003052, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12600}, + 10033: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10034: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(29.7916145325, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10036: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'mintId': 12500}, + 10037: {'type': 'mintShelf', + 'name': '', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(-37.7522773743, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10002: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/VaultDoorCover.bam'}, + 10009: {'type': 'model', + 'name': 'backPillar', + 'comment': '', + 'parentEntId': 10005, + 'pos': Point3(41.4432792664, 0.0, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10010: {'type': 'model', + 'name': 'frontPillar', + 'comment': '', + 'parentEntId': 10005, + 'pos': Point3(-41.0848464966, 0.0, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10011: {'type': 'model', + 'name': 'rightPillar', + 'comment': '', + 'parentEntId': 10005, + 'pos': Point3(0.0, -22.3441867828, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10012: {'type': 'model', + 'name': 'leftPillar', + 'comment': '', + 'parentEntId': 10005, + 'pos': Point3(0.0, 21.9451503754, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10013: {'type': 'model', + 'name': 'frontRightPillar', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0.0, -22.5711078644, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10014: {'type': 'model', + 'name': 'frontLeftPillar', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0.0, 22.1686630249, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10015: {'type': 'model', + 'name': 'frontRightPillar', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0.0, -22.5711078644, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10016: {'type': 'model', + 'name': 'frontLeftPillar', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0.0, 22.1686630249, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10017: {'type': 'model', + 'name': 'leftPillar', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0.0, 67.0966033936, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10018: {'type': 'model', + 'name': 'rightPillar', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0.0, -66.8615875244, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_A1'}, + 10039: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(107.260322571, 0.0, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10003: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, -1.79999995232, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': 'pillars', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10005: {'type': 'nodepath', + 'name': 'centerPillars', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10006: {'type': 'nodepath', + 'name': 'frontPillars', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-89.9665527344, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10007: {'type': 'nodepath', + 'name': 'backPillars', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(89.9700012207, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10008: {'type': 'nodepath', + 'name': 'outerPillars', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10019: {'type': 'nodepath', + 'name': 'byVaultDoor', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(127.898963928, 0.0, 0.0), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10024: {'type': 'nodepath', + 'name': 'shelves', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10026: {'type': 'nodepath', + 'name': 'nearVault', + 'comment': '', + 'parentEntId': 10024, + 'pos': Point3(101.76008606, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10027: {'type': 'nodepath', + 'name': 'left', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0.0, 19.3625793457, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10028: {'type': 'nodepath', + 'name': 'right', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0.0, -19.3600006104, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': 1}, + 10032: {'type': 'nodepath', + 'name': 'rightNear', + 'comment': '', + 'parentEntId': 10024, + 'pos': Point3(-15.3249912262, -55.9873504639, 0.0), + 'hpr': Vec3(152.447189331, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10035: {'type': 'nodepath', + 'name': 'leftNear', + 'comment': '', + 'parentEntId': 10024, + 'pos': Point3(-17.6631221771, 55.3191757202, 0.0), + 'hpr': Vec3(26.3582077026, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10038: {'type': 'nodepath', + 'name': 'nearEntrance', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-107.089996338, -1.71000003815, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintPipeRoom_Battle01_Cogs.py b/toontown/coghq/CashbotMintPipeRoom_Battle01_Cogs.py new file mode 100755 index 00000000..61883ec4 --- /dev/null +++ b/toontown/coghq/CashbotMintPipeRoom_Battle01_Cogs.py @@ -0,0 +1,43 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 1, + 'level': ToontownGlobals.CashbotMintBossLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/CashbotMintStomperAlley_Action00.py b/toontown/coghq/CashbotMintStomperAlley_Action00.py new file mode 100755 index 00000000..e275475b --- /dev/null +++ b/toontown/coghq/CashbotMintStomperAlley_Action00.py @@ -0,0 +1,387 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE17a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10020: {'type': 'attribModifier', + 'name': 'range', + 'comment': '', + 'parentEntId': 10016, + 'attribName': 'range', + 'recursive': 1, + 'typeName': 'stomper', + 'value': '7.3'}, + 10026: {'type': 'attribModifier', + 'name': 'period', + 'comment': '', + 'parentEntId': 10016, + 'attribName': 'period', + 'recursive': 1, + 'typeName': 'stomper', + 'value': '6'}, + 10011: {'type': 'conveyorBelt', + 'name': 'conveyorBelt', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(3.37029790878, 24.4200267792, 13.000869751), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'floorName': 'platformcollision', + 'length': 42.0, + 'speed': 2.0, + 'treadLength': 10.0, + 'treadModelPath': 'phase_9/models/cogHQ/platform1', + 'widthScale': 1.0}, + 10014: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10013, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.2, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 8, + 'velocity': 4}, + 10025: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.2, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 8, + 'velocity': 4}, + 10002: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-1.80931818485, 65.0541229248, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10008: {'type': 'mintShelf', + 'name': 'frontShelf', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0.75479054451, -116.741004944, 0.0), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10009: {'type': 'mintShelf', + 'name': 'backShelf1', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(5.09813165665, 63.6601753235, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10010: {'type': 'mintShelf', + 'name': 'copy of backShelf1', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(-3.80197167397, 81.7602081299, 0.0), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10000: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0.0, -9.59561252594, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10003: {'type': 'model', + 'name': 'downLift', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(4.43959569931, 36.9850654602, 0.880278944969), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(0.550000011921, 0.550000011921, 0.550000011921), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer.bam'}, + 10004: {'type': 'model', + 'name': 'upLift', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-3.25595784187, -35.5523605347, 0.880278944969), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(0.550000011921, 0.550000011921, 0.550000011921), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer.bam'}, + 10012: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0.0, 10.1246709824, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10023: {'type': 'model', + 'name': 'penaltyLift', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-4.46345901489, -13.5, 0.880278944969), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(0.34999999404, 0.34999999404, 0.34999999404), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer.bam'}, + 10027: {'type': 'model', + 'name': 'shadow', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 0.0, -1.56577277184), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(3.5935189724, 3.5935189724, 3.5935189724), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModel', + 'modelPath': 'phase_10/models/cogHQ/RoundShadow.bam'}, + 10028: {'type': 'model', + 'name': 'shadow', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, -1.56577277184), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(3.5935189724, 3.5935189724, 3.5935189724), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModel', + 'modelPath': 'phase_10/models/cogHQ/RoundShadow.bam'}, + 10029: {'type': 'model', + 'name': 'copy of shadow', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, 0.0, -2.47000002861), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(3.5935189724, 3.5935189724, 3.5935189724), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModel', + 'modelPath': 'phase_10/models/cogHQ/RoundShadow.bam'}, + 10001: {'type': 'nodepath', + 'name': 'crates', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.660497009754, 0.0, 0.0133484890684), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(2.50575017929, 2.50575017929, 2.50575017929)}, + 10007: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10016: {'type': 'nodepath', + 'name': 'stompers', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(5.0, 10.4380426407, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10021: {'type': 'nodepath', + 'name': 'goons', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10005: {'type': 'paintMixer', + 'name': '', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 0.0, 3.56454277039), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Vec3(1.0, 1.0, 1.0), + 'motion': 'noBlend', + 'offset': Point3(0.0, 0.0, 20.0), + 'period': 10.0, + 'phaseShift': 0.0, + 'shaftScale': 2.5, + 'waitPercent': 0.4}, + 10006: {'type': 'paintMixer', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 3.56454277039), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Vec3(1.0, 1.0, 1.0), + 'motion': 'noBlend', + 'offset': Point3(0.0, 0.0, 20.0), + 'period': 5.0, + 'phaseShift': 0.0, + 'shaftScale': 2.5, + 'waitPercent': 0.4}, + 10024: {'type': 'paintMixer', + 'name': '', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, 0.0, 3.56454277039), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Vec3(1.0, 1.0, 1.0), + 'motion': 'noBlend', + 'offset': Point3(0.0, 0.0, 33.0), + 'period': 10.0, + 'phaseShift': 0.5, + 'shaftScale': 4.0, + 'waitPercent': 0.4}, + 10013: {'type': 'path', + 'name': 'right', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(4.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': 1, + 'pathIndex': 3, + 'pathScale': 1.0}, + 10022: {'type': 'path', + 'name': 'left', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-4.0, 2.0, 0.0), + 'hpr': Point3(-90.0, 0.0, 0.0), + 'scale': 1, + 'pathIndex': 3, + 'pathScale': 1.0}, + 10015: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(4.0, 2.0, 4.0), + 'modelPath': 0, + 'motion': 1, + 'period': 6, + 'phaseShift': 0.0, + 'range': 7.3, + 'shaftScale': Point3(1.89999997616, 7.5, 1.89999997616), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'horizontal', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10017: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(0.0, 8.0, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(4.0, 2.0, 4.0), + 'modelPath': 0, + 'motion': 1, + 'period': 6, + 'phaseShift': 0.75, + 'range': 7.3, + 'shaftScale': Point3(1.89999997616, 7.5, 1.89999997616), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'horizontal', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10018: {'type': 'stomper', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(0.0, 16.0, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(4.0, 2.0, 4.0), + 'modelPath': 0, + 'motion': 1, + 'period': 6, + 'phaseShift': 0.25, + 'range': 7.3, + 'shaftScale': Point3(1.89999997616, 7.5, 1.89999997616), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'horizontal', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10019: {'type': 'stomper', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(0.0, 24.0, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(4.0, 2.0, 4.0), + 'modelPath': 0, + 'motion': 1, + 'period': 6, + 'phaseShift': 0.5, + 'range': 7.3, + 'shaftScale': Point3(1.89999997616, 7.5, 1.89999997616), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'horizontal', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CogDisguiseGlobals.py b/toontown/coghq/CogDisguiseGlobals.py new file mode 100755 index 00000000..fc5c991e --- /dev/null +++ b/toontown/coghq/CogDisguiseGlobals.py @@ -0,0 +1,509 @@ +from toontown.suit import SuitDNA +import types +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil +from otp.otpbase import OTPGlobals +PartsPerSuit = (17, + 14, + 12, + 10) +PartsPerSuitBitmasks = (131071, + 130175, + 56447, + 56411) +AllBits = 131071 +MinPartLoss = 2 +MaxPartLoss = 4 +MeritsPerLevel = ((100, + 130, + 160, + 190, + 800), + (160, + 210, + 260, + 310, + 1300), + (260, + 340, + 420, + 500, + 2100), + (420, + 550, + 680, + 810, + 3400), + (680, + 890, + 1100, + 1310, + 5500), + (1100, + 1440, + 1780, + 2120, + 8900), + (1780, + 2330, + 2880, + 3430, + 14400), + (2880, + 3770, + 4660, + 5500, + 23300, + 2880, + 23300, + 2880, + 3770, + 4660, + 5500, + 23300, + 2880, + 3770, + 4660, + 5500, + 6440, + 7330, + 8220, + 9110, + 10000, + 23300, + 2880, + 3770, + 4660, + 5500, + 6440, + 7330, + 8220, + 9110, + 10000, + 23300, + 2880, + 3770, + 4660, + 5500, + 6440, + 7330, + 8220, + 9110, + 10000, + 23300, + 0), + (60, + 80, + 100, + 120, + 500), + (100, + 130, + 160, + 190, + 800), + (160, + 210, + 260, + 310, + 1300), + (260, + 340, + 420, + 500, + 2100), + (420, + 550, + 680, + 810, + 3400), + (680, + 890, + 1100, + 1310, + 5500), + (1100, + 1440, + 1780, + 2120, + 8900), + (1780, + 2330, + 2880, + 3430, + 14400, + 1780, + 14400, + 1780, + 2330, + 2880, + 3430, + 14400, + 1780, + 2330, + 2880, + 3430, + 3980, + 4530, + 5080, + 5630, + 6180, + 14400, + 1780, + 2330, + 2880, + 3430, + 3980, + 4530, + 5080, + 5630, + 6180, + 14400, + 1780, + 2330, + 2880, + 3430, + 3980, + 4530, + 5080, + 5630, + 6180, + 14400, + 0), + (40, + 50, + 60, + 70, + 300), + (60, + 80, + 100, + 120, + 500), + (100, + 130, + 160, + 190, + 800), + (160, + 210, + 260, + 310, + 1300), + (260, + 340, + 420, + 500, + 2100), + (420, + 550, + 680, + 810, + 3400), + (680, + 890, + 1100, + 1310, + 5500), + (1100, + 1440, + 1780, + 2120, + 8900, + 1100, + 8900, + 1100, + 1440, + 1780, + 2120, + 8900, + 1100, + 1440, + 1780, + 2120, + 2460, + 2800, + 3140, + 3480, + 3820, + 8900, + 1100, + 1440, + 1780, + 2120, + 2460, + 2800, + 3140, + 3480, + 3820, + 8900, + 1100, + 1440, + 1780, + 2120, + 2460, + 2800, + 3140, + 3480, + 3820, + 8900, + 0), + (20, + 30, + 40, + 50, + 200), + (40, + 50, + 60, + 70, + 300), + (60, + 80, + 100, + 120, + 500), + (100, + 130, + 160, + 190, + 800), + (160, + 210, + 260, + 310, + 1300), + (260, + 340, + 420, + 500, + 2100), + (420, + 550, + 680, + 810, + 3400), + (680, + 890, + 1100, + 1310, + 5500, + 680, + 5500, + 680, + 890, + 1100, + 1310, + 5500, + 680, + 890, + 1100, + 1310, + 1520, + 1730, + 1940, + 2150, + 2360, + 5500, + 680, + 890, + 1100, + 1310, + 1520, + 1730, + 1940, + 2150, + 2360, + 5500, + 680, + 890, + 1100, + 1310, + 1520, + 1730, + 1940, + 2150, + 2360, + 5500, + 0)) +leftLegUpper = 1 +leftLegLower = 2 +leftLegFoot = 4 +rightLegUpper = 8 +rightLegLower = 16 +rightLegFoot = 32 +torsoLeftShoulder = 64 +torsoRightShoulder = 128 +torsoChest = 256 +torsoHealthMeter = 512 +torsoPelvis = 1024 +leftArmUpper = 2048 +leftArmLower = 4096 +leftArmHand = 8192 +rightArmUpper = 16384 +rightArmLower = 32768 +rightArmHand = 65536 +upperTorso = torsoLeftShoulder +leftLegIndex = 0 +rightLegIndex = 1 +torsoIndex = 2 +leftArmIndex = 3 +rightArmIndex = 4 +PartsQueryShifts = (leftLegUpper, + rightLegUpper, + torsoLeftShoulder, + leftArmUpper, + rightArmUpper) +PartsQueryMasks = (leftLegFoot + leftLegLower + leftLegUpper, + rightLegFoot + rightLegLower + rightLegUpper, + torsoPelvis + torsoHealthMeter + torsoChest + torsoRightShoulder + torsoLeftShoulder, + leftArmHand + leftArmLower + leftArmUpper, + rightArmHand + rightArmLower + rightArmUpper) +PartNameStrings = TTLocalizer.CogPartNames +SimplePartNameStrings = TTLocalizer.CogPartNamesSimple +PartsQueryNames = ({1: PartNameStrings[0], + 2: PartNameStrings[1], + 4: PartNameStrings[2], + 8: PartNameStrings[3], + 16: PartNameStrings[4], + 32: PartNameStrings[5], + 64: PartNameStrings[6], + 128: PartNameStrings[7], + 256: PartNameStrings[8], + 512: PartNameStrings[9], + 1024: PartNameStrings[10], + 2048: PartNameStrings[11], + 4096: PartNameStrings[12], + 8192: PartNameStrings[13], + 16384: PartNameStrings[14], + 32768: PartNameStrings[15], + 65536: PartNameStrings[16]}, + {1: PartNameStrings[0], + 2: PartNameStrings[1], + 4: PartNameStrings[2], + 8: PartNameStrings[3], + 16: PartNameStrings[4], + 32: PartNameStrings[5], + 64: SimplePartNameStrings[0], + 128: SimplePartNameStrings[0], + 256: SimplePartNameStrings[0], + 512: SimplePartNameStrings[0], + 1024: PartNameStrings[10], + 2048: PartNameStrings[11], + 4096: PartNameStrings[12], + 8192: PartNameStrings[13], + 16384: PartNameStrings[14], + 32768: PartNameStrings[15], + 65536: PartNameStrings[16]}, + {1: PartNameStrings[0], + 2: PartNameStrings[1], + 4: PartNameStrings[2], + 8: PartNameStrings[3], + 16: PartNameStrings[4], + 32: PartNameStrings[5], + 64: SimplePartNameStrings[0], + 128: SimplePartNameStrings[0], + 256: SimplePartNameStrings[0], + 512: SimplePartNameStrings[0], + 1024: PartNameStrings[10], + 2048: PartNameStrings[11], + 4096: PartNameStrings[12], + 8192: PartNameStrings[12], + 16384: PartNameStrings[14], + 32768: PartNameStrings[15], + 65536: PartNameStrings[15]}, + {1: PartNameStrings[0], + 2: PartNameStrings[1], + 4: PartNameStrings[1], + 8: PartNameStrings[3], + 16: PartNameStrings[4], + 32: PartNameStrings[4], + 64: SimplePartNameStrings[0], + 128: SimplePartNameStrings[0], + 256: SimplePartNameStrings[0], + 512: SimplePartNameStrings[0], + 1024: PartNameStrings[10], + 2048: PartNameStrings[11], + 4096: PartNameStrings[12], + 8192: PartNameStrings[12], + 16384: PartNameStrings[14], + 32768: PartNameStrings[15], + 65536: PartNameStrings[15]}) +suitTypes = PythonUtil.Enum(('NoSuit', 'NoMerits', 'FullSuit')) + +def getNextPart(parts, partIndex, dept): + dept = dept2deptIndex(dept) + needMask = PartsPerSuitBitmasks[dept] & PartsQueryMasks[partIndex] + haveMask = parts[dept] & PartsQueryMasks[partIndex] + nextPart = ~needMask | haveMask + nextPart = nextPart ^ nextPart + 1 + nextPart = nextPart + 1 >> 1 + return nextPart + + +def getPartName(partArray): + index = 0 + for part in partArray: + if part: + return PartsQueryNames[index][part] + index += 1 + + +def isSuitComplete(parts, dept): + dept = dept2deptIndex(dept) + for p in xrange(len(PartsQueryMasks)): + if getNextPart(parts, p, dept): + return 0 + + return 1 + +def getTotalMerits(toon, index): + from toontown.battle import SuitBattleGlobals + cogIndex = toon.cogTypes[index] + SuitDNA.suitsPerDept * index + cogTypeStr = SuitDNA.suitHeadTypes[cogIndex] + cogBaseLevel = SuitBattleGlobals.SuitAttributes[cogTypeStr]['level'] + cogLevel = toon.cogLevels[index] - cogBaseLevel + cogLevel = max(min(cogLevel, len(MeritsPerLevel[cogIndex]) - 1), 0) + return MeritsPerLevel[cogIndex][cogLevel] + + +def getTotalParts(bitString, shiftWidth = 32): + sum = 0 + for shift in xrange(0, shiftWidth): + sum = sum + (bitString >> shift & 1) + + return sum + + +def asBitstring(number): + array = [] + shift = 0 + if number == 0: + array.insert(0, '0') + while pow(2, shift) <= number: + if number >> shift & 1: + array.insert(0, '1') + else: + array.insert(0, '0') + shift += 1 + + str = '' + for i in xrange(0, len(array)): + str = str + array[i] + + return str + + +def asNumber(bitstring): + num = 0 + for i in xrange(0, len(bitstring)): + if bitstring[i] == '1': + num += pow(2, len(bitstring) - 1 - i) + + return num + + +def dept2deptIndex(dept): + if type(dept) == types.StringType: + dept = SuitDNA.suitDepts.index(dept) + return dept diff --git a/toontown/coghq/CogHQBossBattle.py b/toontown/coghq/CogHQBossBattle.py new file mode 100755 index 00000000..d9de04a7 --- /dev/null +++ b/toontown/coghq/CogHQBossBattle.py @@ -0,0 +1,226 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import Place +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.battle import BattlePlace +from toontown.suit import Suit +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals +import math + +class CogHQBossBattle(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('CogHQBossBattle') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.bossCog = None + self.teleportInPosHpr = (0, 0, 0, 0, 0, 0) + self.fsm = ClassicFSM.ClassicFSM('CogHQBossBattle', [State.State('start', self.enterStart, self.exitStart, ['walk', + 'tunnelIn', + 'teleportIn', + 'movie']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'died', 'movie']), + State.State('finalBattle', self.enterFinalBattle, self.exitFinalBattle, ['walk', + 'stickerBook', + 'teleportOut', + 'died', + 'tunnelOut', + 'battle', + 'movie', + 'ouch', + 'crane', + 'WaitForBattle', + 'squished']), + State.State('movie', self.enterMovie, self.exitMovie, ['walk', + 'battle', + 'finalBattle', + 'died', + 'teleportOut']), + State.State('ouch', self.enterOuch, self.exitOuch, ['walk', + 'battle', + 'finalBattle', + 'died', + 'crane']), + State.State('crane', self.enterCrane, self.exitCrane, ['walk', + 'battle', + 'finalBattle', + 'died', + 'ouch', + 'squished']), + State.State('walk', self.enterWalk, self.exitWalk, ['stickerBook', + 'teleportOut', + 'died', + 'tunnelOut', + 'battle', + 'movie', + 'ouch', + 'crane', + 'finalBattle', + 'WaitForBattle']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'WaitForBattle', + 'movie', + 'battle', + 'tunnelOut', + 'teleportOut']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', 'walk', 'movie']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', 'final', 'WaitForBattle']), + State.State('died', self.enterDied, self.exitDied, ['final']), + State.State('tunnelIn', self.enterTunnelIn, self.exitTunnelIn, ['walk']), + State.State('tunnelOut', self.enterTunnelOut, self.exitTunnelOut, ['final']), + State.State('squished', self.enterSquished, self.exitSquished, ['finalBattle', + 'crane', + 'died', + 'teleportOut']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + return + + def load(self): + BattlePlace.BattlePlace.load(self) + self.parentFSM.getStateNamed('cogHQBossBattle').addChild(self.fsm) + self.townBattle = self.loader.townBattle + for i in xrange(1, 3): + Suit.loadSuits(i) + + def unload(self): + BattlePlace.BattlePlace.unload(self) + self.parentFSM.getStateNamed('cogHQBossBattle').removeChild(self.fsm) + del self.parentFSM + del self.fsm + self.ignoreAll() + for i in xrange(1, 3): + Suit.unloadSuits(i) + + def getTaskZoneId(self): + return base.cr.playGame.hood.id + + def enter(self, requestStatus, bossCog): + self.zoneId = requestStatus['zoneId'] + BattlePlace.BattlePlace.enter(self) + self.fsm.enterInitialState() + self.bossCog = bossCog + if self.bossCog: + self.bossCog.d_avatarEnter() + self._telemLimiter = TLGatherAllAvs('CogHQBossBattle', RotationLimitToH) + NametagGlobals.setMasterArrowsOn(1) + base.localAvatar.inventory.setRespectInvasions(0) + self.fsm.request(requestStatus['how'], [requestStatus]) + + def exit(self): + self.fsm.requestFinalState() + base.localAvatar.inventory.setRespectInvasions(1) + if self.bossCog: + self.bossCog.d_avatarExit() + self.bossCog = None + self._telemLimiter.destroy() + del self._telemLimiter + BattlePlace.BattlePlace.exit(self) + return + + def enterBattle(self, event): + mult = 1 + if self.bossCog: + mult = ToontownBattleGlobals.getBossBattleCreditMultiplier(self.bossCog.battleNumber) + self.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult) + base.localAvatar.b_setAnimState('off', 1) + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.cantLeaveGame = 1 + + def exitBattle(self): + self.townBattle.exit() + + def enterFinalBattle(self): + self.walkStateData.enter() + self.walkStateData.fsm.request('walking') + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.setTeleportAllowed(0) + base.localAvatar.cantLeaveGame = 0 + base.localAvatar.book.hideButton() + self.ignore(ToontownGlobals.StickerBookHotkey) + self.ignore('enterStickerBook') + self.ignore(ToontownGlobals.OptionsPageHotkey) + + def exitFinalBattle(self): + self.walkStateData.exit() + base.localAvatar.setTeleportAllowed(1) + + def enterMovie(self, requestStatus = None): + base.localAvatar.setTeleportAvailable(0) + + def exitMovie(self): + pass + + def enterOuch(self): + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.laffMeter.start() + + def exitOuch(self): + base.localAvatar.laffMeter.stop() + + def enterCrane(self): + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.laffMeter.start() + base.localAvatar.collisionsOn() + + def exitCrane(self): + base.localAvatar.collisionsOff() + base.localAvatar.laffMeter.stop() + + def enterWalk(self, teleportIn = 0): + BattlePlace.BattlePlace.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.setTeleportAllowed(0) + base.localAvatar.book.hideButton() + self.ignore(ToontownGlobals.StickerBookHotkey) + self.ignore('enterStickerBook') + self.ignore(ToontownGlobals.OptionsPageHotkey) + self.ignore(self.walkDoneEvent) + + def exitWalk(self): + BattlePlace.BattlePlace.exitWalk(self) + base.localAvatar.setTeleportAllowed(1) + + def enterStickerBook(self, page = None): + BattlePlace.BattlePlace.enterStickerBook(self, page) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterSit(self): + BattlePlace.BattlePlace.enterSit(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterTeleportIn(self, requestStatus): + base.localAvatar.detachNode() + base.localAvatar.setPosHpr(*self.teleportInPosHpr) + BattlePlace.BattlePlace.enterTeleportIn(self, requestStatus) + + def enterTeleportOut(self, requestStatus): + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + hoodId = requestStatus['hoodId'] + if hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def enterSquished(self): + base.localAvatar.laffMeter.start() + base.localAvatar.b_setAnimState('Flattened') + + def handleSquishDone(self, extraArgs = []): + base.cr.playGame.getPlace().setState('walk') + + def exitSquished(self): + taskMgr.remove(base.localAvatar.uniqueName('finishSquishTask')) + base.localAvatar.laffMeter.stop() diff --git a/toontown/coghq/CogHQExterior.py b/toontown/coghq/CogHQExterior.py new file mode 100755 index 00000000..62a87610 --- /dev/null +++ b/toontown/coghq/CogHQExterior.py @@ -0,0 +1,148 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattlePlace +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import ToontownGlobals +from panda3d.core import * +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from otp.nametag import NametagGlobals + +class CogHQExterior(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('CogHQExterior') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.fsm = ClassicFSM.ClassicFSM('CogHQExterior', [State.State('start', self.enterStart, self.exitStart, ['walk', + 'tunnelIn', + 'teleportIn', + 'doorIn']), + State.State('walk', self.enterWalk, self.exitWalk, ['stickerBook', + 'purchase', + 'teleportOut', + 'tunnelOut', + 'doorOut', + 'died', + 'stopped', + 'WaitForBattle', + 'battle', + 'squished', + 'stopped']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut', 'stickerBook']), + State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk', 'stopped']), + State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk', 'stopped']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'WaitForBattle', + 'battle', + 'tunnelOut', + 'teleportOut', + 'doorOut', + 'squished', + 'died']), + State.State('purchase', self.enterPurchase, self.exitPurchase, ['walk']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', 'walk']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('squished', self.enterSquished, self.exitSquished, ['walk', 'died', 'teleportOut']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', 'WaitForBattle', 'battle']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', 'final', 'WaitForBattle']), + State.State('died', self.enterDied, self.exitDied, ['quietZone']), + State.State('tunnelIn', self.enterTunnelIn, self.exitTunnelIn, ['walk', 'WaitForBattle', 'battle']), + State.State('tunnelOut', self.enterTunnelOut, self.exitTunnelOut, ['final']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + self.parentFSM.getStateNamed('cogHQExterior').addChild(self.fsm) + BattlePlace.BattlePlace.load(self) + + def unload(self): + self.parentFSM.getStateNamed('cogHQExterior').removeChild(self.fsm) + del self.fsm + BattlePlace.BattlePlace.unload(self) + + def enter(self, requestStatus): + self.zoneId = requestStatus['zoneId'] + BattlePlace.BattlePlace.enter(self) + self.fsm.enterInitialState() + base.playMusic(self.loader.music, looping=1, volume=0.8) + self.loader.geom.reparentTo(render) + self.nodeList = [self.loader.geom] + self._telemLimiter = TLGatherAllAvs('CogHQExterior', RotationLimitToH) + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + NametagGlobals.setMasterArrowsOn(1) + self.tunnelOriginList = base.cr.hoodMgr.addLinkTunnelHooks(self, self.nodeList) + how = requestStatus['how'] + self.fsm.request(how, [requestStatus]) + + def exit(self): + self.fsm.requestFinalState() + self._telemLimiter.destroy() + del self._telemLimiter + self.loader.music.stop() + for node in self.tunnelOriginList: + node.removeNode() + + del self.tunnelOriginList + if self.loader.geom: + self.loader.geom.reparentTo(hidden) + self.ignoreAll() + BattlePlace.BattlePlace.exit(self) + + def enterPurchase(self): + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + base.localAvatar.obscureMoveFurnitureButton(1) + + def exitPurchase(self): + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(0) + + def enterTunnelOut(self, requestStatus): + fromZoneId = self.zoneId - self.zoneId % 100 + tunnelName = base.cr.hoodMgr.makeLinkTunnelName(self.loader.hood.id, fromZoneId) + requestStatus['tunnelName'] = tunnelName + BattlePlace.BattlePlace.enterTunnelOut(self, requestStatus) + + def enterTeleportIn(self, requestStatus): + x, y, z, h, p, r = base.cr.hoodMgr.getPlaygroundCenterFromId(self.loader.hood.id) + base.localAvatar.setPosHpr(render, x, y, z, h, p, r) + BattlePlace.BattlePlace.enterTeleportIn(self, requestStatus) + + def enterTeleportOut(self, requestStatus, callback = None): + if 'battle' in requestStatus: + self.__teleportOutDone(requestStatus) + else: + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + avId = requestStatus['avId'] + shardId = requestStatus['shardId'] + if hoodId == self.loader.hood.hoodId and zoneId == self.loader.hood.hoodId and shardId == None: + self.fsm.request('teleportIn', [requestStatus]) + elif hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + def exitTeleportOut(self): + BattlePlace.BattlePlace.exitTeleportOut(self) + + def enterSquished(self): + base.localAvatar.laffMeter.start() + base.localAvatar.b_setAnimState('Squish') + taskMgr.doMethodLater(2.0, self.handleSquishDone, base.localAvatar.uniqueName('finishSquishTask')) + + def handleSquishDone(self, extraArgs = []): + base.cr.playGame.getPlace().setState('walk') + + def exitSquished(self): + taskMgr.remove(base.localAvatar.uniqueName('finishSquishTask')) + base.localAvatar.laffMeter.stop() diff --git a/toontown/coghq/CogHQLoader.py b/toontown/coghq/CogHQLoader.py new file mode 100755 index 00000000..232e8bf1 --- /dev/null +++ b/toontown/coghq/CogHQLoader.py @@ -0,0 +1,171 @@ +import CogHQLobby +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from panda3d.core import * +from toontown.hood import QuietZoneState +from toontown.hood import ZoneUtil +from toontown.suit import Suit +from toontown.town import TownBattle + + +class CogHQLoader(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('CogHQLoader') + + def __init__(self, hood, parentFSMState, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.hood = hood + self.parentFSMState = parentFSMState + self.placeDoneEvent = 'cogHQLoaderPlaceDone' + self.townBattleDoneEvent = 'town-battle-done' + self.fsm = ClassicFSM.ClassicFSM( + 'CogHQLoader', [State.State('start', + None, + None, + ['quietZone', 'cogHQExterior', 'cogHQBossBattle']), + State.State('cogHQExterior', + self.enterCogHQExterior, + self.exitCogHQExterior, + ['quietZone', 'cogHQLobby']), + State.State('cogHQLobby', + self.enterCogHQLobby, + self.exitCogHQLobby, + ['quietZone', 'cogHQExterior', 'cogHQBossBattle']), + State.State('cogHQBossBattle', + self.enterCogHQBossBattle, + self.exitCogHQBossBattle, + ['quietZone']), + State.State('quietZone', + self.enterQuietZone, + self.exitQuietZone, + ['cogHQExterior', 'cogHQLobby', 'cogHQBossBattle']), + State.State('final', + None, + None, + ['start'])], + 'start', + 'final') + + def load(self, zoneId): + self.parentFSMState.addChild(self.fsm) + self.music = base.loadMusic(self.musicFile) + self.battleMusic = base.loadMusic('phase_9/audio/bgm/encntr_suit_winning.ogg') + self.townBattle = TownBattle.TownBattle(self.townBattleDoneEvent) + self.townBattle.load() + Suit.loadSuits(3) + self.loadPlaceGeom(zoneId) + + def loadPlaceGeom(self, zoneId): + pass + + def unloadPlaceGeom(self): + pass + + def unload(self): + self.unloadPlaceGeom() + self.parentFSMState.removeChild(self.fsm) + del self.parentFSMState + del self.fsm + self.townBattle.unload() + self.townBattle.cleanup() + del self.townBattle + del self.battleMusic + Suit.unloadSuits(3) + Suit.unloadSkelDialog() + del self.hood + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def enter(self, requestStatus): + self.fsm.enterInitialState() + self.fsm.request(requestStatus['where'], [requestStatus]) + + def exit(self): + self.ignoreAll() + + def enterQuietZone(self, requestStatus): + self.quietZoneDoneEvent = uniqueName('quietZoneDone') + self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone) + self.quietZoneStateData = QuietZoneState.QuietZoneState(self.quietZoneDoneEvent) + self.quietZoneStateData.load() + self.quietZoneStateData.enter(requestStatus) + + def exitQuietZone(self): + self.ignore(self.quietZoneDoneEvent) + del self.quietZoneDoneEvent + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + return + + def handleQuietZoneDone(self): + status = self.quietZoneStateData.getRequestStatus() + self.fsm.request(status['where'], [status]) + + def enterPlace(self, requestStatus): + self.acceptOnce(self.placeDoneEvent, self.placeDone) + self.place = self.placeClass(self, self.fsm, self.placeDoneEvent) + base.cr.playGame.setPlace(self.place) + self.place.load() + self.place.enter(requestStatus) + + def exitPlace(self): + self.ignore(self.placeDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def placeDone(self): + self.requestStatus = self.place.doneStatus + status = self.place.doneStatus + if status.get('shardId') == None and self.isInThisHq(status): + self.unloadPlaceGeom() + zoneId = status['zoneId'] + self.loadPlaceGeom(zoneId) + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + messenger.send(self.doneEvent) + return + + def isInThisHq(self, status): + if ZoneUtil.isDynamicZone(status['zoneId']): + return status['hoodId'] == self.hood.hoodId + else: + return ZoneUtil.getHoodId(status['zoneId']) == self.hood.hoodId + + def enterCogHQExterior(self, requestStatus): + self.placeClass = self.getExteriorPlaceClass() + self.enterPlace(requestStatus) + self.hood.spawnTitleText(requestStatus['zoneId']) + + def exitCogHQExterior(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + self.exitPlace() + self.placeClass = None + return + + def enterCogHQLobby(self, requestStatus): + self.placeClass = CogHQLobby.CogHQLobby + self.enterPlace(requestStatus) + self.hood.spawnTitleText(requestStatus['zoneId']) + + def exitCogHQLobby(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + self.exitPlace() + self.placeClass = None + return + + def enterCogHQBossBattle(self, requestStatus): + self.placeClass = self.getBossPlaceClass() + self.enterPlace(requestStatus) + + def exitCogHQBossBattle(self): + self.exitPlace() + self.placeClass = None + return diff --git a/toontown/coghq/CogHQLobby.py b/toontown/coghq/CogHQLobby.py new file mode 100755 index 00000000..a198421c --- /dev/null +++ b/toontown/coghq/CogHQLobby.py @@ -0,0 +1,103 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.hood import Place +from toontown.building import Elevator +from toontown.toonbase import ToontownGlobals +from panda3d.core import * +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs + +class CogHQLobby(Place.Place): + notify = DirectNotifyGlobal.directNotify.newCategory('CogHQLobby') + + def __init__(self, hood, parentFSM, doneEvent): + Place.Place.__init__(self, hood, doneEvent) + self.parentFSM = parentFSM + self.elevatorDoneEvent = 'elevatorDone' + self.fsm = ClassicFSM.ClassicFSM('CogHQLobby', [State.State('start', self.enterStart, self.exitStart, ['walk', + 'tunnelIn', + 'teleportIn', + 'doorIn']), + State.State('walk', self.enterWalk, self.exitWalk, ['elevator', + 'doorOut', + 'stopped']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut', 'elevator']), + State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk', 'stopped']), + State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk', 'stopped']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']), + State.State('elevator', self.enterElevator, self.exitElevator, ['walk', 'stopped']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + self.parentFSM.getStateNamed('cogHQLobby').addChild(self.fsm) + Place.Place.load(self) + + def unload(self): + self.parentFSM.getStateNamed('cogHQLobby').removeChild(self.fsm) + Place.Place.unload(self) + self.fsm = None + return + + def enter(self, requestStatus): + self.zoneId = requestStatus['zoneId'] + Place.Place.enter(self) + self.fsm.enterInitialState() + base.playMusic(self.loader.music, looping=1, volume=0.8) + self.loader.geom.reparentTo(render) + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + how = requestStatus['how'] + self.fsm.request(how, [requestStatus]) + self._telemLimiter = TLGatherAllAvs('CogHQLobby', RotationLimitToH) + + def exit(self): + self._telemLimiter.destroy() + del self._telemLimiter + self.fsm.requestFinalState() + self.ignoreAll() + self.loader.music.stop() + if self.loader.geom != None: + self.loader.geom.reparentTo(hidden) + Place.Place.exit(self) + return + + def enterWalk(self, teleportIn = 0): + Place.Place.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterElevator(self, distElevator): + self.accept(self.elevatorDoneEvent, self.handleElevatorDone) + self.elevator = Elevator.Elevator(self.fsm.getStateNamed('elevator'), self.elevatorDoneEvent, distElevator) + distElevator.elevatorFSM = self.elevator + self.elevator.load() + self.elevator.enter() + + def exitElevator(self): + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + del self.elevator + + def detectedElevatorCollision(self, distElevator): + self.fsm.request('elevator', [distElevator]) + + def handleElevatorDone(self, doneStatus): + self.notify.debug('handling elevator done event') + where = doneStatus['where'] + if where == 'reject': + if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen(): + pass + else: + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'cogHQBossBattle': + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') + + def enterTeleportIn(self, requestStatus): + base.localAvatar.setPosHpr(render, 0, 0, 0, 0, 0, 0) + Place.Place.enterTeleportIn(self, requestStatus) diff --git a/toontown/coghq/ConveyorBelt.py b/toontown/coghq/ConveyorBelt.py new file mode 100755 index 00000000..ba6f3d12 --- /dev/null +++ b/toontown/coghq/ConveyorBelt.py @@ -0,0 +1,101 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +import MovingPlatform +from otp.level import BasicEntities + +class ConveyorBelt(BasicEntities.NodePathEntity): + UseClipPlanes = 1 + + def __init__(self, level, entId): + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.initBelt() + + def destroy(self): + self.destroyBelt() + BasicEntities.NodePathEntity.destroy(self) + + def initBelt(self): + treadModel = loader.loadModel(self.treadModelPath) + treadModel.setSx(self.widthScale) + treadModel.flattenLight() + self.numTreads = int(self.length / self.treadLength) + 3 + self.beltNode = self.attachNewNode('belt') + self.treads = [] + for i in xrange(self.numTreads): + mp = MovingPlatform.MovingPlatform() + mp.parentingNode = render.attachNewNode('parentTarget') + mp.setupCopyModel('conv%s-%s' % (self.getParentToken(), i), treadModel, self.floorName, parentingNode=mp.parentingNode) + mp.parentingNode.reparentTo(mp) + mp.reparentTo(self.beltNode) + self.treads.append(mp) + + self.start() + + def destroyBelt(self): + self.stop() + for tread in self.treads: + tread.destroy() + tread.parentingNode.removeNode() + del tread.parentingNode + + del self.treads + self.beltNode.removeNode() + del self.beltNode + + def start(self): + startTime = self.level.startTime + treadsIval = Parallel(name='treads') + treadPeriod = self.treadLength / abs(self.speed) + startY = -self.treadLength + for i in xrange(self.numTreads): + periodsToEnd = self.numTreads - i + periodsFromStart = self.numTreads - periodsToEnd + ival = Sequence() + if periodsToEnd != 0: + ival.append(LerpPosInterval(self.treads[i], duration=treadPeriod * periodsToEnd, pos=Point3(0, startY + self.numTreads * self.treadLength, 0), startPos=Point3(0, startY + i * self.treadLength, 0), fluid=1)) + + def dumpContents(tread = self.treads[i]): + tread.releaseLocalToon() + + ival.append(Sequence(Func(dumpContents), Func(self.treads[i].setPos, Point3(0, startY + self.numTreads * self.treadLength, 0)))) + if periodsFromStart != 0: + ival.append(LerpPosInterval(self.treads[i], duration=treadPeriod * periodsFromStart, pos=Point3(0, startY + i * self.treadLength, 0), startPos=Point3(0, startY, 0), fluid=1)) + treadsIval.append(ival) + + self.beltIval = Sequence(treadsIval, name='ConveyorBelt-%s' % self.entId) + playRate = 1.0 + startT = 0.0 + endT = self.beltIval.getDuration() + if self.speed < 0.0: + playRate = -1.0 + temp = startT + startT = endT + endT = temp + self.beltIval.loop(playRate=playRate) + self.beltIval.setT(globalClock.getFrameTime() - startTime) + if ConveyorBelt.UseClipPlanes: + headClip = PlaneNode('headClip') + tailClip = PlaneNode('tailClip') + self.headClipPath = self.beltNode.attachNewNode(headClip) + self.headClipPath.setP(-90) + self.tailClipPath = self.beltNode.attachNewNode(tailClip) + self.tailClipPath.setY(self.length) + self.tailClipPath.setP(90) + self.beltNode.setClipPlane(self.headClipPath) + self.beltNode.setClipPlane(self.tailClipPath) + for tread in self.treads: + tread.parentingNode.setClipPlaneOff(self.headClipPath) + tread.parentingNode.setClipPlaneOff(self.tailClipPath) + + def stop(self): + if hasattr(self, 'beltIval'): + self.beltIval.pause() + del self.beltIval + if ConveyorBelt.UseClipPlanes: + self.headClipPath.removeNode() + del self.headClipPath + self.tailClipPath.removeNode() + del self.tailClipPath + self.clearClipPlane() + for tread in self.treads: + tread.parentingNode.clearClipPlane() diff --git a/toontown/coghq/CountryClubInterior.py b/toontown/coghq/CountryClubInterior.py new file mode 100755 index 00000000..37833f7e --- /dev/null +++ b/toontown/coghq/CountryClubInterior.py @@ -0,0 +1,286 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattlePlace +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.showbase import BulletinBoardWatcher +from panda3d.core import * +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toon import Toon +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownBattleGlobals +from toontown.coghq import DistributedCountryClub +from toontown.building import Elevator +from otp.nametag import NametagGlobals +import random + +class CountryClubInterior(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('CountryClubInterior') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.zoneId = loader.countryClubId + self.elevatorDoneEvent = 'elevatorDone' + self.fsm = ClassicFSM.ClassicFSM('CountryClubInterior', [State.State('start', self.enterStart, self.exitStart, ['walk', 'teleportIn', 'fallDown']), + State.State('walk', self.enterWalk, self.exitWalk, ['push', + 'sit', + 'stickerBook', + 'WaitForBattle', + 'battle', + 'died', + 'teleportOut', + 'squished', + 'fallDown', + 'stopped', + 'elevator']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut', 'stickerBook']), + State.State('sit', self.enterSit, self.exitSit, ['walk', 'died', 'teleportOut']), + State.State('push', self.enterPush, self.exitPush, ['walk', 'died', 'teleportOut']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'battle', + 'WaitForBattle', + 'died', + 'teleportOut']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', + 'walk', + 'died', + 'teleportOut']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('fallDown', self.enterFallDown, self.exitFallDown, ['walk', 'died', 'teleportOut']), + State.State('squished', self.enterSquished, self.exitSquished, ['walk', 'died', 'teleportOut']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', + 'teleportOut', + 'quietZone', + 'died']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', + 'FLA', + 'quietZone', + 'WaitForBattle']), + State.State('died', self.enterDied, self.exitDied, ['teleportOut']), + State.State('FLA', self.enterFLA, self.exitFLA, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['teleportIn']), + State.State('elevator', self.enterElevator, self.exitElevator, ['walk']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + self.parentFSM.getStateNamed('countryClubInterior').addChild(self.fsm) + BattlePlace.BattlePlace.load(self) + musicName = random.choice(['phase_12/audio/bgm/Bossbot_Factory_v1.ogg', 'phase_12/audio/bgm/Bossbot_Factory_v2.ogg', 'phase_12/audio/bgm/Bossbot_Factory_v3.ogg']) + self.music = base.loadMusic(musicName) + + def unload(self): + self.parentFSM.getStateNamed('countryClubInterior').removeChild(self.fsm) + del self.music + del self.fsm + del self.parentFSM + BattlePlace.BattlePlace.unload(self) + + def enter(self, requestStatus): + self.fsm.enterInitialState() + base.transitions.fadeOut(t=0) + base.localAvatar.inventory.setRespectInvasions(0) + base.cr.forbidCheesyEffects(1) + self._telemLimiter = TLGatherAllAvs('CountryClubInterior', RotationLimitToH) + + def commence(self = self): + NametagGlobals.setMasterArrowsOn(1) + self.fsm.request(requestStatus['how'], [requestStatus]) + base.playMusic(self.music, looping=1, volume=0.8) + base.transitions.irisIn() + CountryClub = bboard.get(DistributedCountryClub.DistributedCountryClub.ReadyPost) + self.loader.hood.spawnTitleText(CountryClub.countryClubId) + + self.CountryClubReadyWatcher = BulletinBoardWatcher.BulletinBoardWatcher('CountryClubReady', DistributedCountryClub.DistributedCountryClub.ReadyPost, commence) + self.CountryClubDefeated = 0 + self.acceptOnce(DistributedCountryClub.DistributedCountryClub.WinEvent, self.handleCountryClubWinEvent) + if __debug__ and 0: + self.accept('f10', lambda : messenger.send(DistributedCountryClub.DistributedCountryClub.WinEvent)) + self.confrontedBoss = 0 + + def handleConfrontedBoss(self = self): + self.confrontedBoss = 1 + + self.acceptOnce('localToonConfrontedCountryClubBoss', handleConfrontedBoss) + + def exit(self): + NametagGlobals.setMasterArrowsOn(0) + bboard.remove(DistributedCountryClub.DistributedCountryClub.ReadyPost) + self._telemLimiter.destroy() + del self._telemLimiter + base.cr.forbidCheesyEffects(0) + base.localAvatar.inventory.setRespectInvasions(1) + self.fsm.requestFinalState() + self.loader.music.stop() + self.music.stop() + self.ignoreAll() + del self.CountryClubReadyWatcher + + def enterStopped(self): + BattlePlace.BattlePlace.enterStopped(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterWalk(self, teleportIn = 0): + BattlePlace.BattlePlace.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterPush(self): + BattlePlace.BattlePlace.enterPush(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterWaitForBattle(self): + CountryClubInterior.notify.debug('enterWaitForBattle') + BattlePlace.BattlePlace.enterWaitForBattle(self) + if base.localAvatar.getParent() != render: + base.localAvatar.wrtReparentTo(render) + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + + def exitWaitForBattle(self): + CountryClubInterior.notify.debug('exitWaitForBattle') + BattlePlace.BattlePlace.exitWaitForBattle(self) + + def enterBattle(self, event): + CountryClubInterior.notify.debug('enterBattle') + self.music.stop() + BattlePlace.BattlePlace.enterBattle(self, event) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterTownBattle(self, event): + mult = ToontownBattleGlobals.getCountryClubCreditMultiplier(self.zoneId) + base.localAvatar.inventory.setBattleCreditMultiplier(mult) + self.loader.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult) + + def exitBattle(self): + CountryClubInterior.notify.debug('exitBattle') + BattlePlace.BattlePlace.exitBattle(self) + self.loader.music.stop() + base.playMusic(self.music, looping=1, volume=0.8) + + def enterStickerBook(self, page = None): + BattlePlace.BattlePlace.enterStickerBook(self, page) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterSit(self): + BattlePlace.BattlePlace.enterSit(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterZone(self, zoneId): + pass + + def enterTeleportOut(self, requestStatus): + CountryClubInterior.notify.debug('enterTeleportOut()') + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __processLeaveRequest(self, requestStatus): + hoodId = requestStatus['hoodId'] + if hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def __teleportOutDone(self, requestStatus): + CountryClubInterior.notify.debug('__teleportOutDone()') + messenger.send('leavingCountryClub') + messenger.send('localToonLeft') + if self.CountryClubDefeated and not self.confrontedBoss: + self.fsm.request('FLA', [requestStatus]) + else: + self.__processLeaveRequest(requestStatus) + + def exitTeleportOut(self): + CountryClubInterior.notify.debug('exitTeleportOut()') + BattlePlace.BattlePlace.exitTeleportOut(self) + + def handleCountryClubWinEvent(self): + CountryClubInterior.notify.debug('handleCountryClubWinEvent') + + if base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'died': + return + + self.CountryClubDefeated = 1 + + if 1: + zoneId = ZoneUtil.getHoodId(self.zoneId) + else: + zoneId = ZoneUtil.getSafeZoneId(base.localAvatar.defaultZone) + + self.fsm.request('teleportOut', [{ + 'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1, + }]) + + def enterDied(self, requestStatus, callback = None): + CountryClubInterior.notify.debug('enterDied') + + def diedDone(requestStatus, self = self, callback = callback): + if callback is not None: + callback() + messenger.send('leavingCountryClub') + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + BattlePlace.BattlePlace.enterDied(self, requestStatus, diedDone) + + def enterFLA(self, requestStatus): + CountryClubInterior.notify.debug('enterFLA') + self.flaDialog = TTDialog.TTGlobalDialog(message=TTLocalizer.ForcedLeaveCountryClubAckMsg, doneEvent='FLADone', style=TTDialog.Acknowledge, fadeScreen=1) + + def continueExit(self = self, requestStatus = requestStatus): + self.__processLeaveRequest(requestStatus) + + self.accept('FLADone', continueExit) + self.flaDialog.show() + + def exitFLA(self): + CountryClubInterior.notify.debug('exitFLA') + if hasattr(self, 'flaDialog'): + self.flaDialog.cleanup() + del self.flaDialog + + def detectedElevatorCollision(self, distElevator): + self.fsm.request('elevator', [distElevator]) + + def enterElevator(self, distElevator): + self.accept(self.elevatorDoneEvent, self.handleElevatorDone) + self.elevator = Elevator.Elevator(self.fsm.getStateNamed('elevator'), self.elevatorDoneEvent, distElevator) + self.elevator.setReverseBoardingCamera(True) + distElevator.elevatorFSM = self.elevator + self.elevator.load() + self.elevator.enter() + + def exitElevator(self): + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + + def handleElevatorDone(self, doneStatus): + self.notify.debug('handling elevator done event') + where = doneStatus['where'] + if where == 'reject': + if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen(): + pass + else: + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'factoryInterior' or where == 'suitInterior': + self.doneStatus = doneStatus + self.doneEvent = 'lawOfficeFloorDone' + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') diff --git a/toontown/coghq/CountryClubLayout.py b/toontown/coghq/CountryClubLayout.py new file mode 100755 index 00000000..83fb00d2 --- /dev/null +++ b/toontown/coghq/CountryClubLayout.py @@ -0,0 +1,278 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.PythonUtil import invertDictLossless +from toontown.coghq import CountryClubRoomSpecs +from toontown.toonbase import ToontownGlobals +from direct.showbase.PythonUtil import normalDistrib, lerp +import random + +def printAllBossbotInfo(): + print 'roomId: roomName' + for roomId, roomName in CountryClubRoomSpecs.BossbotCountryClubRoomId2RoomName.items(): + print '%s: %s' % (roomId, roomName) + + print '\nroomId: numBattles' + for roomId, numBattles in CountryClubRoomSpecs.roomId2numBattles.items(): + print '%s: %s' % (roomId, numBattles) + + print '\ncountryClubId floor roomIds' + printCountryClubRoomIds() + print '\ncountryClubId floor numRooms' + printNumRooms() + print '\ncountryClubId floor numForcedBattles' + printNumBattles() + + +def iterateBossbotCountryClubs(func): + from toontown.toonbase import ToontownGlobals + for countryClubId in [ToontownGlobals.BossbotCountryClubIntA, ToontownGlobals.BossbotCountryClubIntB, ToontownGlobals.BossbotCountryClubIntC]: + for floorNum in xrange(ToontownGlobals.CountryClubNumFloors[countryClubId]): + func(CountryClubLayout(countryClubId, floorNum)) + + +def printCountryClubInfo(): + + def func(ml): + print ml + + iterateBossbotCountryClubs(func) + + +def printCountryClubRoomIds(): + + def func(ml): + print ml.getCountryClubId(), ml.getFloorNum(), ml.getRoomIds() + + iterateBossbotCountryClubs(func) + + +def printCountryClubRoomNames(): + + def func(ml): + print ml.getCountryClubId(), ml.getFloorNum(), ml.getRoomNames() + + iterateBossbotCountryClubs(func) + + +def printNumRooms(): + + def func(ml): + print ml.getCountryClubId(), ml.getFloorNum(), ml.getNumRooms() + + iterateBossbotCountryClubs(func) + + +def printNumBattles(): + + def func(ml): + print ml.getCountryClubId(), ml.getFloorNum(), ml.getNumBattles() + + iterateBossbotCountryClubs(func) + + +ClubLayout3_0 = [(0, 2, 5, 9, 17), (0, 2, 4, 9, 17), (0, 2, 5, 9, 18)] +ClubLayout3_1 = [(0, 2, 5, 9, 17), (0, 2, 4, 9, 17), (0, 2, 5, 9, 18)] +ClubLayout3_2 = [(0, 2, 4, 9, 17), (0, 2, 4, 9, 17), (0, 2, 6, 9, 18)] +ClubLayout6_0 = [(0, 22, 4, 29, 17), + (0, 22, 5, 29, 17), + (0, 22, 6, 29, 17), + (0, 22, 5, 29, 17), + (0, 22, 6, 29, 17), + (0, 22, 5, 29, 18)] +ClubLayout6_1 = [(0, 22, 4, 29, 17), + (0, 22, 6, 29, 17), + (0, 22, 4, 29, 17), + (0, 22, 6, 29, 17), + (0, 22, 4, 29, 17), + (0, 22, 6, 29, 18)] +ClubLayout6_2 = [(0, 22, 4, 29, 17), + (0, 22, 6, 29, 17), + (0, 22, 5, 29, 17), + (0, 22, 6, 29, 17), + (0, 22, 5, 29, 17), + (0, 22, 7, 29, 18)] +ClubLayout9_0 = [(0, 32, 4, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 7, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 7, 39, 17), + (0, 32, 7, 39, 17), + (0, 32, 6, 39, 18)] +ClubLayout9_1 = [(0, 32, 4, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 7, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 7, 39, 17), + (0, 32, 7, 39, 17), + (0, 32, 7, 39, 18)] +ClubLayout9_2 = [(0, 32, 5, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 7, 39, 18)] +countryClubLayouts = [ClubLayout3_0, + ClubLayout3_1, + ClubLayout3_2, + ClubLayout6_0, + ClubLayout6_1, + ClubLayout6_2, + ClubLayout9_0, + ClubLayout9_1, + ClubLayout9_2] +testLayout = [ClubLayout3_0, + ClubLayout3_0, + ClubLayout3_0, + ClubLayout6_0, + ClubLayout6_0, + ClubLayout6_0, + ClubLayout9_0, + ClubLayout9_0, + ClubLayout9_0] +countryClubLayouts = testLayout + +class CountryClubLayout: + notify = DirectNotifyGlobal.directNotify.newCategory('CountryClubLayout') + + def __init__(self, countryClubId, floorNum, layoutIndex): + self.countryClubId = countryClubId + self.floorNum = floorNum + self.layoutIndex = layoutIndex + self.roomIds = [] + self.hallways = [] + self.numRooms = 1 + ToontownGlobals.CountryClubNumRooms[self.countryClubId][0] + self.numHallways = self.numRooms - 1 + 1 + self.roomIds = countryClubLayouts[layoutIndex][floorNum] + hallwayRng = self.getRng() + connectorRoomNames = CountryClubRoomSpecs.BossbotCountryClubConnectorRooms + for i in xrange(self.numHallways): + self.hallways.append(hallwayRng.choice(connectorRoomNames)) + + def _genFloorLayout(self): + rng = self.getRng() + startingRoomIDs = CountryClubRoomSpecs.BossbotCountryClubEntranceIDs + middleRoomIDs = CountryClubRoomSpecs.BossbotCountryClubMiddleRoomIDs + finalRoomIDs = CountryClubRoomSpecs.BossbotCountryClubFinalRoomIDs + + numBattlesLeft = ToontownGlobals.CountryClubNumBattles[self.countryClubId] + + finalRoomId = rng.choice(finalRoomIDs) + numBattlesLeft -= CountryClubRoomSpecs.getNumBattles(finalRoomId) + + middleRoomIds = [] + middleRoomsLeft = self.numRooms - 2 + + numBattles2middleRoomIds = invertDictLossless(CountryClubRoomSpecs.middleRoomId2numBattles) + + allBattleRooms = [] + for num, roomIds in numBattles2middleRoomIds.items(): + if num > 0: + allBattleRooms.extend(roomIds) + while 1: + allBattleRoomIds = list(allBattleRooms) + rng.shuffle(allBattleRoomIds) + battleRoomIds = self._chooseBattleRooms(numBattlesLeft, + allBattleRoomIds) + if battleRoomIds is not None: + break + + CountryClubLayout.notify.info('could not find a valid set of battle rooms, trying again') + + middleRoomIds.extend(battleRoomIds) + middleRoomsLeft -= len(battleRoomIds) + + if middleRoomsLeft > 0: + actionRoomIds = numBattles2middleRoomIds[0] + for i in xrange(middleRoomsLeft): + roomId = rng.choice(actionRoomIds) + actionRoomIds.remove(roomId) + middleRoomIds.append(roomId) + + roomIds = [] + + roomIds.append(rng.choice(startingRoomIDs)) + + middleRoomIds.sort() + print 'middleRoomIds=%s' % middleRoomIds + roomIds.extend(middleRoomIds) + + roomIds.append(finalRoomId) + + return roomIds + + def getNumRooms(self): + return len(self.roomIds) + + def getRoomId(self, n): + return self.roomIds[n] + + def getRoomIds(self): + return self.roomIds[:] + + def getRoomNames(self): + names = [] + for roomId in self.roomIds: + names.append(CountryClubRoomSpecs.BossbotCountryClubRoomId2RoomName[roomId]) + + return names + + def getNumHallways(self): + return len(self.hallways) + + def getHallwayModel(self, n): + return self.hallways[n] + + def getNumBattles(self): + numBattles = 0 + for roomId in self.getRoomIds(): + numBattles += CountryClubRoomSpecs.roomId2numBattles[roomId] + + return numBattles + + def getCountryClubId(self): + return self.countryClubId + + def getFloorNum(self): + return self.floorNum + + def getRng(self): + return random.Random(self.countryClubId * self.floorNum) + + def _chooseBattleRooms(self, numBattlesLeft, allBattleRoomIds, baseIndex = 0, chosenBattleRooms = None): + if chosenBattleRooms is None: + chosenBattleRooms = [] + while baseIndex < len(allBattleRoomIds): + nextRoomId = allBattleRoomIds[baseIndex] + baseIndex += 1 + newNumBattlesLeft = numBattlesLeft - CountryClubRoomSpecs.middleRoomId2numBattles[nextRoomId] + if newNumBattlesLeft < 0: + continue + elif newNumBattlesLeft == 0: + chosenBattleRooms.append(nextRoomId) + return chosenBattleRooms + chosenBattleRooms.append(nextRoomId) + result = self._chooseBattleRooms(newNumBattlesLeft, allBattleRoomIds, baseIndex, chosenBattleRooms) + if result is not None: + return result + else: + del chosenBattleRooms[-1:] + else: + return + + return + + def __str__(self): + return 'CountryClubLayout: id=%s, layoutIndex=%s, floor=%s, numRooms=%s, numBattles=%s' % (self.countryClubId, + self.layoutIndex, + self.floorNum, + self.getNumRooms(), + self.getNumBattles()) + + def __repr__(self): + return str(self) diff --git a/toontown/coghq/CountryClubManagerAI.py b/toontown/coghq/CountryClubManagerAI.py new file mode 100755 index 00000000..14a4f181 --- /dev/null +++ b/toontown/coghq/CountryClubManagerAI.py @@ -0,0 +1,59 @@ +import random + +from toontown.coghq import DistributedCountryClubAI +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import DirectObject +from toontown.coghq import CountryClubLayout +from toontown.toonbase import ToontownGlobals + + +CountryClubId2Layouts = { + ToontownGlobals.BossbotCountryClubIntA: (0, 1, 2), + ToontownGlobals.BossbotCountryClubIntB: (3, 4, 5), + ToontownGlobals.BossbotCountryClubIntC: (6, 7, 8) +} + + +class CountryClubManagerAI(DirectObject.DirectObject): + notify = directNotify.newCategory('CountryClubManagerAI') + + countryClubId = None + + def __init__(self, air): + DirectObject.DirectObject.__init__(self) + self.air = air + + def getDoId(self): + return 0 + + def createCountryClub(self, countryClubId, players): + for avId in players: + if bboard.has('countryClubId-%s' % avId): + countryClubId = bboard.get('countryClubId-%s' % avId) + break + numFloors = 1 + layoutIndex = None + floor = 0 + for avId in players: + if bboard.has('countryClubFloor-%s' % avId): + floor = bboard.get('countryClubFloor-%s' % avId) + floor = max(0, floor) + floor = min(floor, numFloors - 1) + break + for avId in players: + if bboard.has('countryClubRoom-%s' % avId): + roomId = bboard.get('countryClubRoom-%s' % avId) + for i in xrange(numFloors): + layout = CountryClubLayout.CountryClubLayout(countryClubId, i) + if roomId in layout.getRoomIds(): + floor = i + else: + CountryClubRoomSpecs = CountryClubRoomSpecs + roomName = CountryClubRoomSpecs.BossbotCountryClubRoomId2RoomName[roomId] + CountryClubManagerAI.notify.warning('room %s (%s) not found in any floor of countryClub %s' % (roomId, roomName, countryClubId)) + countryClubZone = self.air.allocateZone() + if layoutIndex is None: + layoutIndex = random.choice(CountryClubId2Layouts[countryClubId]) + countryClub = DistributedCountryClubAI.DistributedCountryClubAI(self.air, countryClubId, countryClubZone, floor, players, layoutIndex) + countryClub.generateWithRequired(countryClubZone) + return countryClubZone diff --git a/toontown/coghq/CountryClubRoom.py b/toontown/coghq/CountryClubRoom.py new file mode 100755 index 00000000..af3e1179 --- /dev/null +++ b/toontown/coghq/CountryClubRoom.py @@ -0,0 +1,120 @@ +from panda3d.core import * +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals +from toontown.coghq import CountryClubRoomSpecs +from direct.directnotify import DirectNotifyGlobal +import random + +class CountryClubRoom(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('CountryClubRoom') + FloorCollPrefix = 'mintFloorColl' + CashbotMintDoorFrame = 'phase_10/models/cashbotHQ/DoorFrame' + + def __init__(self, path = None): + if path is not None: + if path in CountryClubRoomSpecs.BossbotCountryClubConnectorRooms: + loadFunc = loader.loadModelCopy + else: + loadFunc = loader.loadModel + self.setGeom(loadFunc(path)) + self.localToonFSM = ClassicFSM.ClassicFSM('CountryClubRoomLocalToonPresent', [State.State('off', self.enterLtOff, self.exitLtOff, ['notPresent']), State.State('notPresent', self.enterLtNotPresent, self.exitLtNotPresent, ['present']), State.State('present', self.enterLtPresent, self.exitLtPresent, ['notPresent'])], 'notPresent', 'notPresent') + self.localToonFSM.enterInitialState() + return + + def delete(self): + del self.localToonFSM + + def enter(self): + self.localToonFSM.request('notPresent') + + def exit(self): + self.localToonFSM.requestFinalState() + + def setRoomNum(self, num): + self.roomNum = num + + def getRoomNum(self): + return self.roomNum + + def setGeom(self, geom): + self.__geom = geom + return + + def getGeom(self): + return self.__geom + + def _getEntrances(self): + return self.__geom.findAllMatches('**/ENTRANCE*') + + def _getExits(self): + return self.__geom.findAllMatches('**/EXIT*') + + def attachTo(self, other, rng): + otherExits = other._getExits() + entrances = self._getEntrances() + otherDoor = otherExits[0] + thisDoor = rng.choice(entrances) + geom = self.getGeom() + otherGeom = other.getGeom() + self.notify.debug('thisDoor = %s' % thisDoor) + self.notify.debug('otherDoor = %s' % otherDoor) + self.notify.debug('thisGeom = %s' % geom) + self.notify.debug('otherGeom = %s' % otherGeom) + debugAxis1 = None + if debugAxis1: + debugAxis1.reparentTo(thisDoor) + debugAxis2 = None + if debugAxis2: + debugAxis2.reparentTo(otherDoor) + debugAxis2.setColorScale(0.5, 0.5, 0.5, 1) + tempNode = otherDoor.attachNewNode('tempRotNode') + geom.reparentTo(tempNode) + geom.clearMat() + newGeomPos = Vec3(0) - thisDoor.getPos(geom) + self.notify.debug('newGeomPos = %s' % newGeomPos) + geom.setPos(newGeomPos) + newTempNodeH = -thisDoor.getH(otherDoor) + self.notify.debug('newTempNodeH =%s' % newTempNodeH) + tempNode.setH(newTempNodeH) + geom.wrtReparentTo(otherGeom.getParent()) + tempNode.removeNode() + return + + def getFloorCollName(self): + return '%s%s' % (CountryClubRoom.FloorCollPrefix, self.roomNum) + + def initFloorCollisions(self): + allColls = self.getGeom().findAllMatches('**/+CollisionNode') + floorColls = [] + for coll in allColls: + bitmask = coll.node().getIntoCollideMask() + if not (bitmask & ToontownGlobals.FloorBitmask).isZero(): + floorColls.append(coll) + + if len(floorColls) > 0: + floorCollName = self.getFloorCollName() + others = self.getGeom().findAllMatches('**/%s' % floorCollName) + for other in others: + other.setName('%s_renamed' % floorCollName) + + for floorColl in floorColls: + floorColl.setName(floorCollName) + + def enterLtOff(self): + pass + + def exitLtOff(self): + pass + + def enterLtNotPresent(self): + pass + + def exitLtNotPresent(self): + pass + + def enterLtPresent(self): + pass + + def exitLtPresent(self): + pass diff --git a/toontown/coghq/CountryClubRoomBase.py b/toontown/coghq/CountryClubRoomBase.py new file mode 100755 index 00000000..cac432ac --- /dev/null +++ b/toontown/coghq/CountryClubRoomBase.py @@ -0,0 +1,16 @@ +from toontown.toonbase import ToontownGlobals + +class CountryClubRoomBase: + + def __init__(self): + pass + + def setCountryClubId(self, countryClubId): + self.countryClubId = countryClubId + self.cogTrack = ToontownGlobals.cogHQZoneId2dept(countryClubId) + + def setRoomId(self, roomId): + self.roomId = roomId + + def getCogTrack(self): + return self.cogTrack diff --git a/toontown/coghq/CountryClubRoomSpecs.py b/toontown/coghq/CountryClubRoomSpecs.py new file mode 100755 index 00000000..4580bd35 --- /dev/null +++ b/toontown/coghq/CountryClubRoomSpecs.py @@ -0,0 +1,81 @@ +from direct.showbase.PythonUtil import invertDict +from toontown.toonbase import ToontownGlobals +from toontown.coghq import BossbotCountryClubFairwayRoom_Battle00_Cogs +from toontown.coghq import BossbotCountryClubMazeRoom_Battle00_Cogs +from toontown.coghq import BossbotCountryClubMazeRoom_Battle01_Cogs +from toontown.coghq import BossbotCountryClubMazeRoom_Battle02_Cogs +from toontown.coghq import BossbotCountryClubMazeRoom_Battle03_Cogs +from toontown.coghq import NullCogs +from toontown.coghq import BossbotCountryClubKartRoom_Battle00_Cogs +from toontown.coghq import BossbotCountryClubPresidentRoom_Battle00_Cogs + +# Explicit imports... +from toontown.coghq import BossbotCountryClubEntrance_Action00 +from toontown.coghq import BossbotCountryClubTeeOffRoom_Action00 +from toontown.coghq import BossbotCountryClubFairwayRoom_Battle00 +from toontown.coghq import BossbotCountryClubMazeRoom_Battle00 +from toontown.coghq import BossbotCountryClubMazeRoom_Battle01 +from toontown.coghq import BossbotCountryClubMazeRoom_Battle02 +from toontown.coghq import BossbotCountryClubGreenRoom_Action00 +from toontown.coghq import BossbotCountryClubKartRoom_Battle00 +from toontown.coghq import BossbotCountryClubPresidentRoom_Battle00 +from toontown.coghq import BossbotCountryClubTeeOffRoom_Action01 +from toontown.coghq import BossbotCountryClubTeeOffRoom_Action02 +from toontown.coghq import BossbotCountryClubGreenRoom_Action01 +from toontown.coghq import BossbotCountryClubGreenRoom_Action02 + +def getCountryClubRoomSpecModule(roomId): + return CashbotMintSpecModules[roomId] + + +def getCogSpecModule(roomId): + roomName = BossbotCountryClubRoomId2RoomName[roomId] + return CogSpecModules.get(roomName, NullCogs) + + +def getNumBattles(roomId): + return roomId2numBattles[roomId] + + +BossbotCountryClubRoomId2RoomName = {0: 'BossbotCountryClubEntrance_Action00', + 2: 'BossbotCountryClubTeeOffRoom_Action00', + 4: 'BossbotCountryClubFairwayRoom_Battle00', + 5: 'BossbotCountryClubMazeRoom_Battle00', + 6: 'BossbotCountryClubMazeRoom_Battle01', + 7: 'BossbotCountryClubMazeRoom_Battle02', + 9: 'BossbotCountryClubGreenRoom_Action00', + 17: 'BossbotCountryClubKartRoom_Battle00', + 18: 'BossbotCountryClubPresidentRoom_Battle00', + 22: 'BossbotCountryClubTeeOffRoom_Action01', + 32: 'BossbotCountryClubTeeOffRoom_Action02', + 29: 'BossbotCountryClubGreenRoom_Action01', + 39: 'BossbotCountryClubGreenRoom_Action02'} +BossbotCountryClubRoomName2RoomId = invertDict(BossbotCountryClubRoomId2RoomName) +BossbotCountryClubEntranceIDs = (0,) +BossbotCountryClubMiddleRoomIDs = (2, 5, 6) +BossbotCountryClubFinalRoomIDs = (18,) +BossbotCountryClubConnectorRooms = ('phase_12/models/bossbotHQ/Connector_Tunnel_A', 'phase_12/models/bossbotHQ/Connector_Tunnel_B') +CashbotMintSpecModules = {} +for roomName, roomId in BossbotCountryClubRoomName2RoomId.items(): + CashbotMintSpecModules[roomId] = locals()[roomName] + +CogSpecModules = {'BossbotCountryClubFairwayRoom_Battle00': BossbotCountryClubFairwayRoom_Battle00_Cogs, + 'BossbotCountryClubMazeRoom_Battle00': BossbotCountryClubMazeRoom_Battle00_Cogs, + 'BossbotCountryClubMazeRoom_Battle01': BossbotCountryClubMazeRoom_Battle01_Cogs, + 'BossbotCountryClubMazeRoom_Battle02': BossbotCountryClubMazeRoom_Battle02_Cogs, + 'BossbotCountryClubKartRoom_Battle00': BossbotCountryClubKartRoom_Battle00_Cogs, + 'BossbotCountryClubPresidentRoom_Battle00': BossbotCountryClubPresidentRoom_Battle00_Cogs} +roomId2numBattles = {} +for roomName, roomId in BossbotCountryClubRoomName2RoomId.items(): + if roomName not in CogSpecModules: + roomId2numBattles[roomId] = 0 + else: + cogSpecModule = CogSpecModules[roomName] + roomId2numBattles[roomId] = len(cogSpecModule.BattleCells) + +name2id = BossbotCountryClubRoomName2RoomId +roomId2numBattles[name2id['BossbotCountryClubTeeOffRoom_Action00']] = 1 +del name2id +middleRoomId2numBattles = {} +for roomId in BossbotCountryClubMiddleRoomIDs: + middleRoomId2numBattles[roomId] = roomId2numBattles[roomId] diff --git a/toontown/coghq/CrateGlobals.py b/toontown/coghq/CrateGlobals.py new file mode 100755 index 00000000..b1625bd9 --- /dev/null +++ b/toontown/coghq/CrateGlobals.py @@ -0,0 +1,23 @@ +from panda3d.core import * +CRATE_CLEAR = 0 +CRATE_POWERUP = 1 +CRATE_PUSH = 2 +CrateNormals = [Vec3(1, 0, 0), + Vec3(-1, 0, 0), + Vec3(0, 1, 0), + Vec3(0, -1, 0)] +CrateHprs = [Vec3(90, 0, 0), + Vec3(270, 0, 0), + Vec3(180, 0, 0), + Vec3(0, 0, 0)] +T_PUSH = 1.5 +T_PAUSE = 0.1 +TorsoToOffset = {'ss': 0.17, + 'ms': 0.18, + 'ls': 0.75, + 'sd': 0.17, + 'md': 0.18, + 'ld': 0.75, + 's': 0.17, + 'm': 0.18, + 'l': 0.75} diff --git a/toontown/coghq/CrusherCell.py b/toontown/coghq/CrusherCell.py new file mode 100755 index 00000000..36a0ee85 --- /dev/null +++ b/toontown/coghq/CrusherCell.py @@ -0,0 +1,8 @@ +import ActiveCell +from direct.directnotify import DirectNotifyGlobal + +class CrusherCell(ActiveCell.ActiveCell): + notify = DirectNotifyGlobal.directNotify.newCategory('CrusherCell') + + def __init__(self, cr): + ActiveCell.ActiveCell.__init__(self, cr) diff --git a/toontown/coghq/CrusherCellAI.py b/toontown/coghq/CrusherCellAI.py new file mode 100755 index 00000000..ea86425f --- /dev/null +++ b/toontown/coghq/CrusherCellAI.py @@ -0,0 +1,63 @@ +import ActiveCellAI +from direct.directnotify import DirectNotifyGlobal + +class CrusherCellAI(ActiveCellAI.ActiveCellAI): + notify = DirectNotifyGlobal.directNotify.newCategory('CrusherCellAI') + + def __init__(self, level, entId): + ActiveCellAI.ActiveCellAI.__init__(self, level, entId) + self.crushers = [] + self.crushables = [] + + def destroy(self): + self.notify.info('destroy entity %s' % self.entId) + for entId in self.crushers: + self.unregisterCrusher(entId) + + ActiveCellAI.ActiveCellAI.destroy(self) + + def registerCrusher(self, entId): + if entId not in self.crushers: + ent = self.level.entities.get(entId, None) + if ent: + self.crushers.append(entId) + self.accept(ent.crushMsg, self.doCrush) + return + + def unregisterCrusher(self, entId): + if entId in self.crushers: + self.crushers.remove(entId) + if not hasattr(self, 'level'): + self.notify.error("unregisterCrusher(%s): CrusherCellAI %s has no attrib 'level'" % (entId, self.entId)) + ent = self.level.entities.get(entId, None) + if ent: + self.ignore(ent.crushMsg) + return + + def registerCrushable(self, entId): + if entId not in self.crushables: + self.crushables.append(entId) + + def unregisterCrushable(self, entId): + if entId in self.crushables: + self.crushables.remove(entId) + + def doCrush(self, crusherId, axis): + self.notify.debug('doCrush %s' % crusherId) + for occupantId in self.occupantIds: + if occupantId in self.crushables: + crushObj = self.level.entities.get(occupantId, None) + if crushObj: + crushObj.doCrush(crusherId, axis) + else: + self.notify.warning("couldn't find crushable object %d" % self.occupantId) + + return + + def updateCrushables(self): + for id in self.crushables: + crushable = self.level.entities.get(id, None) + if crushable: + crushable.updateGrid() + + return diff --git a/toontown/coghq/DinerStatusIndicator.py b/toontown/coghq/DinerStatusIndicator.py new file mode 100755 index 00000000..15ff9c31 --- /dev/null +++ b/toontown/coghq/DinerStatusIndicator.py @@ -0,0 +1,113 @@ +from pandac.PandaModules import NodePath, BillboardEffect, Vec3, Point3, TextureStage, TransparencyAttrib, DecalEffect, VBase4 +from direct.fsm import FSM +from direct.gui.DirectGui import DirectFrame, DGG +from direct.interval.IntervalGlobal import LerpScaleInterval, LerpColorScaleInterval, Parallel, Sequence, Wait + +class DinerStatusIndicator(NodePath, FSM.FSM): + + def __init__(self, parent, pos = None, scale = None): + NodePath.__init__(self, 'DinerStatusIndicator') + if parent: + self.reparentTo(parent) + if pos: + self.setPos(pos) + if scale: + self.setScale(scale) + self.loadAssets() + FSM.FSM.__init__(self, 'DinerStatusIndicator') + self.activeIval = None + return + + def delete(self): + if self.activeIval: + self.activeIval.pause() + self.activeIval = None + self.angryIcon.removeNode() + self.hungryIcon.removeNode() + self.eatingIcon.removeNode() + self.removeNode() + return + + def loadAssets(self): + iconsFile = loader.loadModel('phase_12/models/bossbotHQ/BanquetIcons') + self.angryIcon, self.angryMeter = self.loadIcon(iconsFile, '**/Anger') + self.hungryIcon, self.hungryMeter = self.loadIcon(iconsFile, '**/Hunger') + self.eatingIcon, self.eatingMeter = self.loadIcon(iconsFile, '**/Food') + self.angryMeter.hide() + iconsFile.removeNode() + + def loadIcon(self, iconsFile, name): + retVal = iconsFile.find(name) + retVal.setBillboardAxis() + retVal.reparentTo(self) + dark = retVal.copyTo(NodePath()) + dark.reparentTo(retVal) + dark.setColor(0.5, 0.5, 0.5, 1) + retVal.setEffect(DecalEffect.make()) + retVal.setTransparency(TransparencyAttrib.MAlpha, 1) + ll, ur = dark.getTightBounds() + center = retVal.attachNewNode('center') + center.setPos(0, 0, ll[2]) + dark.wrtReparentTo(center) + dark.setTexProjector(TextureStage.getDefault(), center, retVal) + retVal.hide() + return (retVal, center) + + def enterEating(self, timeToFinishFood): + self.eatingIcon.show() + self.activeIval = self.createMeterInterval(self.eatingIcon, self.eatingMeter, timeToFinishFood) + self.activeIval.start() + + def exitEating(self): + if self.activeIval: + self.activeIval.finish() + self.activeIval = None + self.eatingIcon.hide() + return + + def enterHungry(self, timeToFinishFood): + self.hungryIcon.show() + self.activeIval = self.createMeterInterval(self.hungryIcon, self.hungryMeter, timeToFinishFood) + self.activeIval.start() + + def exitHungry(self): + if self.activeIval: + self.activeIval.finish() + self.activeIval = None + self.hungryIcon.hide() + return + + def enterAngry(self): + self.angryIcon.show() + + def exitAngry(self): + self.angryIcon.hide() + if self.activeIval: + self.activeIval.finish() + self.activeIval = None + return + + def enterDead(self): + pass + + def exitDead(self): + pass + + def enterInactive(self): + pass + + def exitInactive(self): + pass + + def createMeterInterval(self, icon, meter, time): + ivalDarkness = LerpScaleInterval(meter, time, scale=Vec3(1, 1, 1), startScale=Vec3(1, 0.001, 0.001)) + flashingTrack = Sequence() + flashDuration = 10 + if time > flashDuration: + flashingTrack.append(Wait(time - flashDuration)) + for i in xrange(10): + flashingTrack.append(Parallel(LerpColorScaleInterval(icon, 0.5, VBase4(1, 0, 0, 1)), icon.scaleInterval(0.5, 1.25))) + flashingTrack.append(Parallel(LerpColorScaleInterval(icon, 0.5, VBase4(1, 1, 1, 1)), icon.scaleInterval(0.5, 1))) + + retIval = Parallel(ivalDarkness, flashingTrack) + return retIval diff --git a/toontown/coghq/DirectionalCell.py b/toontown/coghq/DirectionalCell.py new file mode 100755 index 00000000..33574dac --- /dev/null +++ b/toontown/coghq/DirectionalCell.py @@ -0,0 +1,8 @@ +import ActiveCell +from direct.directnotify import DirectNotifyGlobal + +class DirectionalCell(ActiveCell.ActiveCell): + notify = DirectNotifyGlobal.directNotify.newCategory('DirectionalCell') + + def __init__(self, cr): + ActiveCell.ActiveCell.__init__(self, cr) diff --git a/toontown/coghq/DirectionalCellAI.py b/toontown/coghq/DirectionalCellAI.py new file mode 100755 index 00000000..2393f191 --- /dev/null +++ b/toontown/coghq/DirectionalCellAI.py @@ -0,0 +1,46 @@ +from direct.directnotify import DirectNotifyGlobal +import ActiveCellAI +import CrateGlobals +from direct.task import Task + +class DirectionalCellAI(ActiveCellAI.ActiveCellAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DirectionalCellAI') + + def __init__(self, level, entId): + self.dir = [0, 0] + ActiveCellAI.ActiveCellAI.__init__(self, level, entId) + self.moveTrack = None + return + + def delete(self): + if self.moveTrack: + self.moveTrack.pause() + del self.moveTrack + self.moveTrack = None + taskMgr.remove(self.taskName('moveTask')) + return + + def setState(self, state, objId = None): + ActiveCellAI.ActiveCellAI.setState(self, state, objId) + self.startMoveTask() + + def taskName(self, name): + return self.level.taskName(name) + '-' + str(self.entId) + + def startMoveTask(self): + taskMgr.remove(self.taskName('moveTask')) + taskMgr.doMethodLater(CrateGlobals.T_PUSH + CrateGlobals.T_PAUSE, self.moveTask, self.taskName('moveTask')) + + def moveTask(self, task): + oldPos = self.grid.getObjPos(self.occupantId) + if self.grid.doMove(self.occupantId, self.dir[0], self.dir[1]): + newPos = self.grid.getObjPos(self.occupantId) + crate = simbase.air.doId2do.get(self.occupantId) + if crate: + crate.sendUpdate('setMoveTo', [oldPos[0], + oldPos[1], + oldPos[2], + newPos[0], + newPos[1], + newPos[2]]) + return Task.done diff --git a/toontown/coghq/DistributedBanquetTable.py b/toontown/coghq/DistributedBanquetTable.py new file mode 100755 index 00000000..fdb8d76c --- /dev/null +++ b/toontown/coghq/DistributedBanquetTable.py @@ -0,0 +1,1149 @@ +import math +import random +from pandac.PandaModules import NodePath, Point3, VBase4, TextNode, Vec3, deg2Rad, CollisionSegment, CollisionHandlerQueue, CollisionNode, BitMask32, SmoothMover +from direct.fsm import FSM +from direct.distributed import DistributedObject +from direct.distributed.ClockDelta import globalClockDelta +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import Sequence, ProjectileInterval, Parallel, LerpHprInterval, ActorInterval, Func, Wait, SoundInterval, LerpPosHprInterval, LerpScaleInterval +from direct.gui.DirectGui import DGG, DirectButton, DirectLabel, DirectWaitBar +from direct.task import Task +from toontown.suit import Suit +from toontown.suit import SuitDNA +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.coghq import BanquetTableBase +from toontown.coghq import DinerStatusIndicator +from toontown.battle import MovieUtil + +class DistributedBanquetTable(DistributedObject.DistributedObject, FSM.FSM, BanquetTableBase.BanquetTableBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBanquetTable') + rotationsPerSeatIndex = [90, + 90, + 0, + 0, + -90, + -90, + 180, + 180] + pitcherMinH = -360 + pitcherMaxH = 360 + rotateSpeed = 30 + waterPowerSpeed = base.config.GetDouble('water-power-speed', 15) + waterPowerExponent = base.config.GetDouble('water-power-exponent', 0.75) + useNewAnimations = True + TugOfWarControls = False + OnlyUpArrow = True + if OnlyUpArrow: + BASELINE_KEY_RATE = 3 + else: + BASELINE_KEY_RATE = 6 + UPDATE_KEY_PRESS_RATE_TASK = 'BanquetTableUpdateKeyPressRateTask' + YELLOW_POWER_THRESHOLD = 0.75 + RED_POWER_THRESHOLD = 0.97 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedBanquetTable') + self.boss = None + self.index = -1 + self.diners = {} + self.dinerStatus = {} + self.serviceLocs = {} + self.chairLocators = {} + self.sitLocators = {} + self.activeIntervals = {} + self.dinerStatusIndicators = {} + self.preparedForPhaseFour = False + self.avId = 0 + self.toon = None + self.pitcherSmoother = SmoothMover() + self.pitcherSmoother.setSmoothMode(SmoothMover.SMOn) + self.smoothStarted = 0 + self.__broadcastPeriod = 0.2 + self.changeSeq = 0 + self.lastChangeSeq = 0 + self.pitcherAdviceLabel = None + self.fireLength = 250 + self.fireTrack = None + self.hitObject = None + self.setupPowerBar() + self.aimStart = None + self.toonPitcherPosition = Point3(0, -2, 0) + self.allowLocalRequestControl = True + self.fadeTrack = None + self.grabTrack = None + self.gotHitByBoss = False + self.keyTTL = [] + self.keyRate = 0 + self.buttons = [0, 1] + self.lastPowerFired = 0 + self.moveSound = None + self.releaseTrack = None + return + + def disable(self): + DistributedObject.DistributedObject.disable(self) + taskMgr.remove(self.triggerName) + taskMgr.remove(self.smoothName) + taskMgr.remove(self.watchControlsName) + taskMgr.remove(self.pitcherAdviceName) + taskMgr.remove(self.posHprBroadcastName) + taskMgr.remove(self.waterPowerTaskName) + if self.releaseTrack: + self.releaseTrack.finish() + self.releaseTrack = None + if self.fireTrack: + self.fireTrack.finish() + self.fireTrack = None + self.cleanupIntervals() + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.boss = None + self.ignoreAll() + for indicator in self.dinerStatusIndicators.values(): + indicator.delete() + + self.dinerStatusIndicators = {} + for diner in self.diners.values(): + diner.delete() + + self.diners = {} + self.powerBar.destroy() + self.powerBar = None + self.pitcherMoveSfx.stop() + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.loadAssets() + self.smoothName = self.uniqueName('pitcherSmooth') + self.pitcherAdviceName = self.uniqueName('pitcherAdvice') + self.posHprBroadcastName = self.uniqueName('pitcherBroadcast') + self.waterPowerTaskName = self.uniqueName('updateWaterPower') + self.triggerName = self.uniqueName('trigger') + self.watchControlsName = self.uniqueName('watchControls') + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + self.boss.setTable(self, self.index) + + def setIndex(self, index): + self.index = index + + def setState(self, state, avId, extraInfo): + self.gotHitByBoss = extraInfo + if state == 'F': + self.demand('Off') + elif state == 'N': + self.demand('On') + elif state == 'I': + self.demand('Inactive') + elif state == 'R': + self.demand('Free') + elif state == 'C': + self.demand('Controlled', avId) + elif state == 'L': + self.demand('Flat', avId) + else: + self.notify.error('Invalid state from AI: %s' % state) + + def setNumDiners(self, numDiners): + self.numDiners = numDiners + + def setDinerInfo(self, hungryDurations, eatingDurations, dinerLevels): + self.dinerInfo = {} + for i in xrange(len(hungryDurations)): + hungryDur = hungryDurations[i] + eatingDur = eatingDurations[i] + dinerLevel = dinerLevels[i] + self.dinerInfo[i] = (hungryDur, eatingDur, dinerLevel) + + def loadAssets(self): + self.tableGroup = loader.loadModel('phase_12/models/bossbotHQ/BanquetTableChairs') + tableLocator = self.boss.geom.find('**/TableLocator_%d' % (self.index + 1)) + if tableLocator.isEmpty(): + self.tableGroup.reparentTo(render) + self.tableGroup.setPos(0, 75, 0) + else: + self.tableGroup.reparentTo(tableLocator) + self.tableGeom = self.tableGroup.find('**/Geometry') + self.setupDiners() + self.setupChairCols() + self.squirtSfx = loader.loadSfx('phase_4/audio/sfx/AA_squirt_seltzer_miss.ogg') + self.hitBossSfx = loader.loadSfx('phase_5/audio/sfx/SA_watercooler_spray_only.ogg') + self.hitBossSoundInterval = SoundInterval(self.hitBossSfx, node=self.boss, volume=1.0) + self.serveFoodSfx = loader.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_bell_for_trolley.ogg') + self.pitcherMoveSfx = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.ogg') + + def setupDiners(self): + for i in xrange(self.numDiners): + newDiner = self.createDiner(i) + self.diners[i] = newDiner + self.dinerStatus[i] = self.HUNGRY + + def createDiner(self, i): + diner = Suit.Suit() + diner.dna = SuitDNA.SuitDNA() + level = self.dinerInfo[i][2] + level -= 4 + diner.dna.newSuitRandom(level=level, dept='c') + diner.setDNA(diner.dna) + diner.nametag3d.stash() + diner.nametag.destroy() + if self.useNewAnimations: + diner.loop('sit', fromFrame=i) + else: + diner.pose('landing', 0) + locator = self.tableGroup.find('**/chair_%d' % (i + 1)) + locatorScale = locator.getNetTransform().getScale()[0] + correctHeadingNp = locator.attachNewNode('correctHeading') + self.chairLocators[i] = correctHeadingNp + heading = self.rotationsPerSeatIndex[i] + correctHeadingNp.setH(heading) + sitLocator = correctHeadingNp.attachNewNode('sitLocator') + base.sitLocator = sitLocator + pos = correctHeadingNp.getPos(render) + if SuitDNA.getSuitBodyType(diner.dna.name) == 'c': + sitLocator.setPos(0.5, 3.65, -3.75) + else: + sitLocator.setZ(-2.4) + sitLocator.setY(2.5) + sitLocator.setX(0.5) + self.sitLocators[i] = sitLocator + diner.setScale(1.0 / locatorScale) + diner.reparentTo(sitLocator) + newLoc = NodePath('serviceLoc-%d-%d' % (self.index, i)) + newLoc.reparentTo(correctHeadingNp) + newLoc.setPos(0, 3.0, 1) + self.serviceLocs[i] = newLoc + base.serviceLoc = newLoc + head = diner.find('**/joint_head') + newIndicator = DinerStatusIndicator.DinerStatusIndicator(parent=head, pos=Point3(0, 0, 3.5), scale=5.0) + newIndicator.wrtReparentTo(diner) + self.dinerStatusIndicators[i] = newIndicator + return diner + + def setupChairCols(self): + for i in xrange(self.numDiners): + chairCol = self.tableGroup.find('**/collision_chair_%d' % (i + 1)) + colName = 'ChairCol-%d-%d' % (self.index, i) + chairCol.setTag('chairIndex', str(i)) + chairCol.setName(colName) + chairCol.setCollideMask(ToontownGlobals.WallBitmask) + self.accept('enter' + colName, self.touchedChair) + + def touchedChair(self, colEntry): + chairIndex = int(colEntry.getIntoNodePath().getTag('chairIndex')) + if chairIndex in self.dinerStatus: + status = self.dinerStatus[chairIndex] + if status in (self.HUNGRY, self.ANGRY): + self.boss.localToonTouchedChair(self.index, chairIndex) + + def serveFood(self, food, chairIndex): + self.removeFoodModel(chairIndex) + serviceLoc = self.serviceLocs.get(chairIndex) + if not food or food.isEmpty(): + foodModel = loader.loadModel('phase_12/models/bossbotHQ/canoffood') + foodModel.setScale(ToontownGlobals.BossbotFoodModelScale) + foodModel.reparentTo(serviceLoc) + else: + food.wrtReparentTo(serviceLoc) + tray = food.find('**/tray') + if not tray.isEmpty(): + tray.hide() + ivalDuration = 1.5 + foodMoveIval = Parallel(SoundInterval(self.serveFoodSfx, node=food), ProjectileInterval(food, duration=ivalDuration, startPos=food.getPos(serviceLoc), endPos=serviceLoc.getPos(serviceLoc)), LerpHprInterval(food, ivalDuration, Point3(0, -360, 0))) + intervalName = 'serveFood-%d-%d' % (self.index, chairIndex) + foodMoveIval.start() + self.activeIntervals[intervalName] = foodMoveIval + + def setDinerStatus(self, chairIndex, status): + if chairIndex in self.dinerStatus: + oldStatus = self.dinerStatus[chairIndex] + self.dinerStatus[chairIndex] = status + if oldStatus != status: + if status == self.EATING: + self.changeDinerToEating(chairIndex) + elif status == self.HUNGRY: + self.changeDinerToHungry(chairIndex) + elif status == self.ANGRY: + self.changeDinerToAngry(chairIndex) + elif status == self.DEAD: + self.changeDinerToDead(chairIndex) + elif status == self.HIDDEN: + self.changeDinerToHidden(chairIndex) + + def removeFoodModel(self, chairIndex): + serviceLoc = self.serviceLocs.get(chairIndex) + if serviceLoc: + for i in xrange(serviceLoc.getNumChildren()): + serviceLoc.getChild(0).removeNode() + + def changeDinerToEating(self, chairIndex): + indicator = self.dinerStatusIndicators.get(chairIndex) + eatingDuration = self.dinerInfo[chairIndex][1] + if indicator: + indicator.request('Eating', eatingDuration) + diner = self.diners[chairIndex] + intervalName = 'eating-%d-%d' % (self.index, chairIndex) + eatInTime = 32.0 / 24.0 + eatOutTime = 21.0 / 24.0 + eatLoopTime = 19 / 24.0 + rightHand = diner.getRightHand() + waitTime = 5 + loopDuration = eatingDuration - eatInTime - eatOutTime - waitTime + serviceLoc = self.serviceLocs[chairIndex] + + def foodAttach(self = self, diner = diner): + if self.serviceLocs[chairIndex].getNumChildren() < 1: + return + foodModel = self.serviceLocs[chairIndex].getChild(0) + (foodModel.reparentTo(diner.getRightHand()),) + (foodModel.setHpr(Point3(0, -94, 0)),) + (foodModel.setPos(Point3(-0.15, -0.7, -0.4)),) + scaleAdj = 1 + if SuitDNA.getSuitBodyType(diner.dna.name) == 'c': + scaleAdj = 0.6 + (foodModel.setPos(Point3(0.1, -0.25, -0.31)),) + else: + scaleAdj = 0.8 + (foodModel.setPos(Point3(-0.25, -0.85, -0.34)),) + oldScale = foodModel.getScale() + newScale = oldScale * scaleAdj + foodModel.setScale(newScale) + + def foodDetach(self = self, diner = diner): + if diner.getRightHand().getNumChildren() < 1: + return + foodModel = diner.getRightHand().getChild(0) + (foodModel.reparentTo(serviceLoc),) + (foodModel.setPosHpr(0, 0, 0, 0, 0, 0),) + scaleAdj = 1 + if SuitDNA.getSuitBodyType(diner.dna.name) == 'c': + scaleAdj = 0.6 + else: + scakeAdj = 0.8 + oldScale = foodModel.getScale() + newScale = oldScale / scaleAdj + foodModel.setScale(newScale) + + eatIval = Sequence(ActorInterval(diner, 'sit', duration=waitTime), ActorInterval(diner, 'sit-eat-in', startFrame=0, endFrame=6), Func(foodAttach), ActorInterval(diner, 'sit-eat-in', startFrame=6, endFrame=32), ActorInterval(diner, 'sit-eat-loop', duration=loopDuration, loop=1), ActorInterval(diner, 'sit-eat-out', startFrame=0, endFrame=12), Func(foodDetach), ActorInterval(diner, 'sit-eat-out', startFrame=12, endFrame=21)) + eatIval.start() + self.activeIntervals[intervalName] = eatIval + + def changeDinerToHungry(self, chairIndex): + intervalName = 'eating-%d-%d' % (self.index, chairIndex) + if intervalName in self.activeIntervals: + self.activeIntervals[intervalName].finish() + self.removeFoodModel(chairIndex) + indicator = self.dinerStatusIndicators.get(chairIndex) + if indicator: + indicator.request('Hungry', self.dinerInfo[chairIndex][0]) + diner = self.diners[chairIndex] + if random.choice([0, 1]): + diner.loop('sit-hungry-left') + else: + diner.loop('sit-hungry-right') + + def changeDinerToAngry(self, chairIndex): + self.removeFoodModel(chairIndex) + indicator = self.dinerStatusIndicators.get(chairIndex) + if indicator: + indicator.request('Angry') + diner = self.diners[chairIndex] + diner.loop('sit-angry') + + def changeDinerToDead(self, chairIndex): + + def removeDeathSuit(suit, deathSuit): + if not deathSuit.isEmpty(): + deathSuit.detachNode() + suit.cleanupLoseActor() + + self.removeFoodModel(chairIndex) + indicator = self.dinerStatusIndicators.get(chairIndex) + if indicator: + indicator.request('Dead') + diner = self.diners[chairIndex] + deathSuit = diner + locator = self.tableGroup.find('**/chair_%d' % (chairIndex + 1)) + deathSuit = diner.getLoseActor() + ival = Sequence(Func(self.notify.debug, 'before actorinterval sit-lose'), ActorInterval(diner, 'sit-lose'), Func(self.notify.debug, 'before deathSuit.setHpr'), Func(deathSuit.setHpr, diner.getHpr()), Func(self.notify.debug, 'before diner.hide'), Func(diner.hide), Func(self.notify.debug, 'before deathSuit.reparentTo'), Func(deathSuit.reparentTo, self.chairLocators[chairIndex]), Func(self.notify.debug, 'befor ActorInterval lose'), ActorInterval(deathSuit, 'lose', duration=MovieUtil.SUIT_LOSE_DURATION), Func(self.notify.debug, 'before remove deathsuit'), Func(removeDeathSuit, diner, deathSuit, name='remove-death-suit-%d-%d' % (chairIndex, self.index)), Func(self.notify.debug, 'diner.stash'), Func(diner.stash)) + spinningSound = base.loadSfx('phase_3.5/audio/sfx/Cog_Death.ogg') + deathSound = base.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart_%s.ogg' % random.randint(1, 6)) + deathSoundTrack = Sequence(Wait(0.8), SoundInterval(spinningSound, duration=1.2, startTime=1.5, volume=0.2, node=deathSuit), SoundInterval(spinningSound, duration=3.0, startTime=0.6, volume=0.8, node=deathSuit), SoundInterval(deathSound, volume=0.32, node=deathSuit)) + intervalName = 'dinerDie-%d-%d' % (self.index, chairIndex) + deathIval = Parallel(ival, deathSoundTrack) + deathIval.start() + self.activeIntervals[intervalName] = deathIval + + def changeDinerToHidden(self, chairIndex): + self.removeFoodModel(chairIndex) + indicator = self.dinerStatusIndicators.get(chairIndex) + if indicator: + indicator.request('Inactive') + diner = self.diners[chairIndex] + diner.hide() + + def setAllDinersToSitNeutral(self): + startFrame = 0 + for diner in self.diners.values(): + if not diner.isHidden(): + diner.loop('sit', fromFrame=startFrame) + startFrame += 1 + + def cleanupIntervals(self): + for interval in self.activeIntervals.values(): + interval.finish() + + self.activeIntervals = {} + + def clearInterval(self, name, finish = 1): + if name in self.activeIntervals: + ival = self.activeIntervals[name] + if finish: + ival.finish() + else: + ival.pause() + if name in self.activeIntervals: + del self.activeIntervals[name] + else: + self.notify.debug('interval: %s already cleared' % name) + + def finishInterval(self, name): + if name in self.activeIntervals: + interval = self.activeIntervals[name] + interval.finish() + + def getNotDeadInfo(self): + notDeadList = [] + for i in xrange(self.numDiners): + if self.dinerStatus[i] != self.DEAD: + notDeadList.append((self.index, i, 12)) + + return notDeadList + + def enterOn(self): + pass + + def exitOn(self): + pass + + def enterInactive(self): + for chairIndex in xrange(self.numDiners): + indicator = self.dinerStatusIndicators.get(chairIndex) + if indicator: + indicator.request('Inactive') + self.removeFoodModel(chairIndex) + + def exitInactive(self): + pass + + def enterFree(self): + self.resetPowerBar() + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + self.prepareForPhaseFour() + if self.avId == localAvatar.doId: + self.tableGroup.setAlphaScale(0.3) + self.tableGroup.setTransparency(1) + taskMgr.doMethodLater(5, self.__allowDetect, self.triggerName) + self.fadeTrack = Sequence(Func(self.tableGroup.setTransparency, 1), self.tableGroup.colorScaleInterval(0.2, VBase4(1, 1, 1, 0.3))) + self.fadeTrack.start() + self.allowLocalRequestControl = False + else: + self.allowLocalRequestControl = True + self.avId = 0 + return + + def exitFree(self): + pass + + def touchedTable(self, colEntry): + tableIndex = int(colEntry.getIntoNodePath().getTag('tableIndex')) + if self.state == 'Free' and self.avId == 0 and self.allowLocalRequestControl: + self.d_requestControl() + + def prepareForPhaseFour(self): + if not self.preparedForPhaseFour: + for i in xrange(8): + chair = self.tableGroup.find('**/chair_%d' % (i + 1)) + if not chair.isEmpty(): + chair.hide() + colChairs = self.tableGroup.findAllMatches('**/ChairCol*') + for i in xrange(colChairs.getNumPaths()): + col = colChairs.getPath(i) + col.stash() + + colChairs = self.tableGroup.findAllMatches('**/collision_chair*') + for i in xrange(colChairs.getNumPaths()): + col = colChairs.getPath(i) + col.stash() + + tableCol = self.tableGroup.find('**/collision_table') + colName = 'TableCol-%d' % self.index + tableCol.setTag('tableIndex', str(self.index)) + tableCol.setName(colName) + tableCol.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.BanquetTableBitmask) + self.accept('enter' + colName, self.touchedTable) + self.preparedForPhaseFour = True + self.waterPitcherModel = loader.loadModel('phase_12/models/bossbotHQ/tt_m_ara_bhq_seltzerBottle') + lampNode = self.tableGroup.find('**/lamp_med_5') + pos = lampNode.getPos(self.tableGroup) + lampNode.hide() + bottleLocator = self.tableGroup.find('**/bottle_locator') + pos = bottleLocator.getPos(self.tableGroup) + self.waterPitcherNode = self.tableGroup.attachNewNode('pitcherNode') + self.waterPitcherNode.setPos(pos) + self.waterPitcherModel.reparentTo(self.waterPitcherNode) + self.nozzle = self.waterPitcherModel.find('**/nozzle_tip') + self.handLocator = self.waterPitcherModel.find('**/hand_locator') + self.handPos = self.handLocator.getPos() + + def d_requestControl(self): + self.sendUpdate('requestControl') + + def d_requestFree(self, gotHitByBoss): + self.sendUpdate('requestFree', [gotHitByBoss]) + + def enterControlled(self, avId): + self.prepareForPhaseFour() + self.avId = avId + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.toon = toon + self.grabTrack = self.makeToonGrabInterval(toon) + self.notify.debug('grabTrack=%s' % self.grabTrack) + self.pitcherCamPos = Point3(0, -50, 40) + self.pitcherCamHpr = Point3(0, -21, 0) + if avId == localAvatar.doId: + self.boss.toMovieMode() + self.__enableControlInterface() + self.startPosHprBroadcast() + self.grabTrack = Sequence(self.grabTrack, Func(camera.wrtReparentTo, localAvatar), LerpPosHprInterval(camera, 1, self.pitcherCamPos, self.pitcherCamHpr), Func(self.boss.toCraneMode)) + if self.TugOfWarControls: + self.__spawnUpdateKeyPressRateTask() + self.accept('exitCrane', self.gotBossZapped) + else: + self.startSmooth() + toon.stopSmooth() + self.grabTrack.start() + + def exitControlled(self): + self.ignore('exitCrane') + if self.grabTrack: + self.grabTrack.finish() + self.grabTrack = None + nextState = self.getCurrentOrNextState() + self.notify.debug('nextState=%s' % nextState) + if nextState == 'Flat': + place = base.cr.playGame.getPlace() + self.notify.debug('%s' % place.fsm) + if self.avId == localAvatar.doId: + self.__disableControlInterface() + else: + if self.toon and not self.toon.isDisabled(): + self.toon.loop('neutral') + self.toon.startSmooth() + self.releaseTrack = self.makeToonReleaseInterval(self.toon) + self.stopPosHprBroadcast() + self.stopSmooth() + if self.avId == localAvatar.doId: + localAvatar.wrtReparentTo(render) + self.__disableControlInterface() + camera.reparentTo(base.localAvatar) + camera.setPos(base.localAvatar.cameraPositions[0][0]) + camera.setHpr(0, 0, 0) + self.goToFinalBattle() + self.safeBossToFinalBattleMode() + else: + toon = base.cr.doId2do.get(self.avId) + if toon: + toon.wrtReparentTo(render) + self.releaseTrack.start() + return + + def safeBossToFinalBattleMode(self): + if self.boss: + self.boss.toFinalBattleMode() + + def goToFinalBattle(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + if place.fsm.getCurrentState().getName() == 'crane': + place.setState('finalBattle') + + def makeToonGrabInterval(self, toon): + toon.pose('leverNeutral', 0) + toon.update() + rightHandPos = toon.rightHand.getPos(toon) + self.toonPitcherPosition = Point3(self.handPos[0] - rightHandPos[0], self.handPos[1] - rightHandPos[1], 0) + destZScale = rightHandPos[2] / self.handPos[2] + grabIval = Sequence(Func(toon.wrtReparentTo, self.waterPitcherNode), Func(toon.loop, 'neutral'), Parallel(ActorInterval(toon, 'jump'), Sequence(Wait(0.43), Parallel(ProjectileInterval(toon, duration=0.9, startPos=toon.getPos(self.waterPitcherNode), endPos=self.toonPitcherPosition), LerpHprInterval(toon, 0.9, Point3(0, 0, 0)), LerpScaleInterval(self.waterPitcherModel, 0.9, Point3(1, 1, destZScale))))), Func(toon.setPos, self.toonPitcherPosition), Func(toon.loop, 'leverNeutral')) + return grabIval + + def makeToonReleaseInterval(self, toon): + temp1 = self.waterPitcherNode.attachNewNode('temp1') + temp1.setPos(self.toonPitcherPosition) + temp2 = self.waterPitcherNode.attachNewNode('temp2') + temp2.setPos(0, -10, -self.waterPitcherNode.getZ()) + startPos = temp1.getPos(render) + endPos = temp2.getPos(render) + temp1.removeNode() + temp2.removeNode() + + def getSlideToPos(toon = toon): + return render.getRelativePoint(toon, Point3(0, -10, 0)) + + if self.gotHitByBoss: + self.notify.debug('creating zap interval instead') + grabIval = Sequence(Func(toon.loop, 'neutral'), Func(toon.wrtReparentTo, render), Parallel(ActorInterval(toon, 'slip-backward'), toon.posInterval(0.5, getSlideToPos, fluid=1))) + else: + grabIval = Sequence(Func(toon.loop, 'neutral'), Func(toon.wrtReparentTo, render), Parallel(ActorInterval(toon, 'jump'), Sequence(Wait(0.43), ProjectileInterval(toon, duration=0.9, startPos=startPos, endPos=endPos)))) + return grabIval + + def b_clearSmoothing(self): + self.d_clearSmoothing() + self.clearSmoothing() + + def d_clearSmoothing(self): + self.sendUpdate('clearSmoothing', [0]) + + def clearSmoothing(self, bogus = None): + self.pitcherSmoother.clearPositions(1) + + def doSmoothTask(self, task): + self.pitcherSmoother.computeAndApplySmoothHpr(self.waterPitcherNode) + return Task.cont + + def startSmooth(self): + if not self.smoothStarted: + taskName = self.smoothName + taskMgr.remove(taskName) + self.reloadPosition() + taskMgr.add(self.doSmoothTask, taskName) + self.smoothStarted = 1 + + def stopSmooth(self): + if self.smoothStarted: + taskName = self.smoothName + taskMgr.remove(taskName) + self.forceToTruePosition() + self.smoothStarted = 0 + + def __enableControlInterface(self): + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.closeButton = DirectButton(image=(gui.find('**/CloseBtn_UP'), + gui.find('**/CloseBtn_DN'), + gui.find('**/CloseBtn_Rllvr'), + gui.find('**/CloseBtn_UP')), relief=None, scale=2, text=TTLocalizer.BossbotPitcherLeave, text_scale=0.04, text_pos=(0, -0.07), text_fg=VBase4(1, 1, 1, 1), pos=(1.05, 0, -0.82), command=self.__exitPitcher) + self.accept('escape', self.__exitPitcher) + self.accept('control', self.__controlPressed) + self.accept('control-up', self.__controlReleased) + self.accept('InputState-forward', self.__upArrow) + self.accept('InputState-reverse', self.__downArrow) + self.accept('InputState-turnLeft', self.__leftArrow) + self.accept('InputState-turnRight', self.__rightArrow) + self.accept('arrow_up', self.__upArrowKeyPressed) + self.accept('arrow_down', self.__downArrowKeyPressed) + taskMgr.add(self.__watchControls, self.watchControlsName) + taskMgr.doMethodLater(5, self.__displayPitcherAdvice, self.pitcherAdviceName) + self.arrowVert = 0 + self.arrowHorz = 0 + self.powerBar.show() + return + + def __disableControlInterface(self): + if self.closeButton: + self.closeButton.destroy() + self.closeButton = None + self.__cleanupPitcherAdvice() + self.ignore('escape') + self.ignore('control') + self.ignore('control-up') + self.ignore('InputState-forward') + self.ignore('InputState-reverse') + self.ignore('InputState-turnLeft') + self.ignore('InputState-turnRight') + self.ignore('arrow_up') + self.ignore('arrow_down') + self.arrowVert = 0 + self.arrowHorz = 0 + taskMgr.remove(self.watchControlsName) + taskMgr.remove(self.waterPowerTaskName) + self.resetPowerBar() + self.aimStart = None + self.powerBar.hide() + if self.TugOfWarControls: + self.__killUpdateKeyPressRateTask() + self.keyTTL = [] + self.__setMoveSound(None) + return + + def __displayPitcherAdvice(self, task): + if self.pitcherAdviceLabel == None: + self.pitcherAdviceLabel = DirectLabel(text=TTLocalizer.BossbotPitcherAdvice, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.69), scale=0.1) + return + + def __cleanupPitcherAdvice(self): + if self.pitcherAdviceLabel: + self.pitcherAdviceLabel.destroy() + self.pitcherAdviceLabel = None + taskMgr.remove(self.pitcherAdviceName) + return + + def showExiting(self): + if self.closeButton: + self.closeButton.destroy() + self.closeButton = DirectLabel(relief=None, text=TTLocalizer.BossbotPitcherLeaving, pos=(1.05, 0, -0.88), text_pos=(0, 0), text_scale=0.06, text_fg=VBase4(1, 1, 1, 1)) + self.__cleanupPitcherAdvice() + return + + def __exitPitcher(self): + self.showExiting() + self.d_requestFree(False) + + def __controlPressed(self): + self.__cleanupPitcherAdvice() + if self.TugOfWarControls: + if self.power: + self.aimStart = 1 + self.__endFireWater() + elif self.state == 'Controlled': + self.__beginFireWater() + + def __controlReleased(self): + if self.TugOfWarControls: + pass + elif self.state == 'Controlled': + self.__endFireWater() + + def __upArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupPitcherAdvice() + if pressed: + self.arrowVert = 1 + elif self.arrowVert > 0: + self.arrowVert = 0 + + def __downArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupPitcherAdvice() + if pressed: + self.arrowVert = -1 + elif self.arrowVert < 0: + self.arrowVert = 0 + + def __rightArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupPitcherAdvice() + if pressed: + self.arrowHorz = 1 + elif self.arrowHorz > 0: + self.arrowHorz = 0 + + def __leftArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupPitcherAdvice() + if pressed: + self.arrowHorz = -1 + elif self.arrowHorz < 0: + self.arrowHorz = 0 + + def __incrementChangeSeq(self): + self.changeSeq = self.changeSeq + 1 & 255 + + def stopPosHprBroadcast(self): + taskName = self.posHprBroadcastName + taskMgr.remove(taskName) + + def startPosHprBroadcast(self): + taskName = self.posHprBroadcastName + self.b_clearSmoothing() + self.d_sendPitcherPos() + taskMgr.remove(taskName) + taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) + + def __posHprBroadcast(self, task): + self.d_sendPitcherPos() + taskName = self.posHprBroadcastName + taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) + return Task.done + + def d_sendPitcherPos(self): + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setPitcherPos', [self.changeSeq, self.waterPitcherNode.getH(), timestamp]) + + def setPitcherPos(self, changeSeq, h, timestamp): + self.changeSeq = changeSeq + if self.smoothStarted: + now = globalClock.getFrameTime() + local = globalClockDelta.networkToLocalTime(timestamp, now) + self.pitcherSmoother.setH(h) + self.pitcherSmoother.setTimestamp(local) + self.pitcherSmoother.markPosition() + else: + self.waterPitcherNode.setH(h) + + def __watchControls(self, task): + if self.arrowHorz: + self.__movePitcher(self.arrowHorz) + else: + self.__setMoveSound(None) + return Task.cont + + def __movePitcher(self, xd): + dt = globalClock.getDt() + h = self.waterPitcherNode.getH() - xd * self.rotateSpeed * dt + h %= 360 + self.notify.debug('rotSpeed=%.2f curH=%.2f xd =%.2f, dt = %.2f, h=%.2f' % (self.rotateSpeed, + self.waterPitcherNode.getH(), + xd, + dt, + h)) + limitH = h + self.waterPitcherNode.setH(limitH) + if xd: + self.__setMoveSound(self.pitcherMoveSfx) + + def reloadPosition(self): + self.pitcherSmoother.clearPositions(0) + self.pitcherSmoother.setHpr(self.waterPitcherNode.getHpr()) + self.pitcherSmoother.setPhonyTimestamp() + + def forceToTruePosition(self): + if self.pitcherSmoother.getLatestPosition(): + self.pitcherSmoother.applySmoothHpr(self.waterPitcherNode) + self.pitcherSmoother.clearPositions(1) + + def getSprayTrack(self, color, origin, target, dScaleUp, dHold, dScaleDown, horizScale = 1.0, vertScale = 1.0, parent = render): + track = Sequence() + SPRAY_LEN = 1.5 + sprayProp = MovieUtil.globalPropPool.getProp('spray') + sprayScale = hidden.attachNewNode('spray-parent') + sprayRot = hidden.attachNewNode('spray-rotate') + spray = sprayRot + spray.setColor(color) + if color[3] < 1.0: + spray.setTransparency(1) + + def showSpray(sprayScale, sprayRot, sprayProp, origin, target, parent): + if callable(origin): + origin = origin() + if callable(target): + target = target() + sprayRot.reparentTo(parent) + sprayRot.clearMat() + sprayScale.reparentTo(sprayRot) + sprayScale.clearMat() + sprayProp.reparentTo(sprayScale) + sprayProp.clearMat() + sprayRot.setPos(origin) + sprayRot.lookAt(Point3(target)) + + track.append(Func(showSpray, sprayScale, sprayRot, sprayProp, origin, target, parent)) + + def calcTargetScale(target = target, origin = origin, horizScale = horizScale, vertScale = vertScale): + if callable(target): + target = target() + if callable(origin): + origin = origin() + distance = Vec3(target - origin).length() + yScale = distance / SPRAY_LEN + targetScale = Point3(yScale * horizScale, yScale, yScale * vertScale) + return targetScale + + track.append(LerpScaleInterval(sprayScale, dScaleUp, calcTargetScale, startScale=Point3(0.01, 0.01, 0.01))) + track.append(Func(self.checkHitObject)) + track.append(Wait(dHold)) + + def prepareToShrinkSpray(spray, sprayProp, origin, target): + if callable(target): + target = target() + if callable(origin): + origin = origin() + sprayProp.setPos(Point3(0.0, -SPRAY_LEN, 0.0)) + spray.setPos(target) + + track.append(Func(prepareToShrinkSpray, spray, sprayProp, origin, target)) + track.append(LerpScaleInterval(sprayScale, dScaleDown, Point3(0.01, 0.01, 0.01))) + + def hideSpray(spray, sprayScale, sprayRot, sprayProp, propPool): + sprayProp.detachNode() + MovieUtil.removeProp(sprayProp) + sprayRot.removeNode() + sprayScale.removeNode() + + track.append(Func(hideSpray, spray, sprayScale, sprayRot, sprayProp, MovieUtil.globalPropPool)) + return track + + def checkHitObject(self): + if not self.hitObject: + return + if self.avId != base.localAvatar.doId: + return + tag = self.hitObject.getNetTag('pieCode') + pieCode = int(tag) + if pieCode == ToontownGlobals.PieCodeBossCog: + self.hitBossSoundInterval.start() + self.sendUpdate('waterHitBoss', [self.index]) + if self.TugOfWarControls: + damage = 1 + if self.lastPowerFired < self.YELLOW_POWER_THRESHOLD: + damage = 1 + elif self.lastPowerFired < self.RED_POWER_THRESHOLD: + damage = 2 + else: + damage = 3 + self.boss.d_hitBoss(damage) + else: + damage = 1 + if self.lastPowerFired < self.YELLOW_POWER_THRESHOLD: + damage = 1 + elif self.lastPowerFired < self.RED_POWER_THRESHOLD: + damage = 2 + else: + damage = 3 + self.boss.d_hitBoss(damage) + + def waterHitBoss(self, tableIndex): + if self.index == tableIndex: + self.hitBossSoundInterval.start() + + def setupPowerBar(self): + self.powerBar = DirectWaitBar(pos=(0.0, 0, -0.94), relief=DGG.SUNKEN, frameSize=(-2.0, + 2.0, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=0.25, range=1, sortOrder=50, frameColor=(0.5, 0.5, 0.5, 0.5), barColor=(0.75, 0.75, 1.0, 0.8), text='', text_scale=0.26, text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05)) + self.power = 0 + self.powerBar['value'] = self.power + self.powerBar.hide() + + def resetPowerBar(self): + self.power = 0 + self.powerBar['value'] = self.power + self.powerBar['text'] = '' + self.keyTTL = [] + + def __beginFireWater(self): + if self.fireTrack and self.fireTrack.isPlaying(): + return + if self.aimStart != None: + return + if not self.state == 'Controlled': + return + if not self.avId == localAvatar.doId: + return + time = globalClock.getFrameTime() + self.aimStart = time + messenger.send('wakeup') + taskMgr.add(self.__updateWaterPower, self.waterPowerTaskName) + return + + def __endFireWater(self): + if self.aimStart == None: + return + if not self.state == 'Controlled': + return + if not self.avId == localAvatar.doId: + return + taskMgr.remove(self.waterPowerTaskName) + messenger.send('wakeup') + self.aimStart = None + origin = self.nozzle.getPos(render) + target = self.boss.getPos(render) + angle = deg2Rad(self.waterPitcherNode.getH() + 90) + x = math.cos(angle) + y = math.sin(angle) + fireVector = Point3(x, y, 0) + if self.power < 0.001: + self.power = 0.001 + self.lastPowerFired = self.power + fireVector *= self.fireLength * self.power + target = origin + fireVector + segment = CollisionSegment(origin[0], origin[1], origin[2], target[0], target[1], target[2]) + fromObject = render.attachNewNode(CollisionNode('pitcherColNode')) + fromObject.node().addSolid(segment) + fromObject.node().setFromCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask | ToontownGlobals.FloorBitmask) + fromObject.node().setIntoCollideMask(BitMask32.allOff()) + queue = CollisionHandlerQueue() + base.cTrav.addCollider(fromObject, queue) + base.cTrav.traverse(render) + queue.sortEntries() + self.hitObject = None + if queue.getNumEntries(): + entry = queue.getEntry(0) + target = entry.getSurfacePoint(render) + self.hitObject = entry.getIntoNodePath() + base.cTrav.removeCollider(fromObject) + fromObject.removeNode() + self.d_firingWater(origin, target) + self.fireWater(origin, target) + self.resetPowerBar() + return + + def __updateWaterPower(self, task): + if not self.powerBar: + print '### no power bar!!!' + return task.done + newPower = self.__getWaterPower(globalClock.getFrameTime()) + self.power = newPower + self.powerBar['value'] = newPower + if self.power < self.YELLOW_POWER_THRESHOLD: + self.powerBar['barColor'] = VBase4(0.75, 0.75, 1.0, 0.8) + elif self.power < self.RED_POWER_THRESHOLD: + self.powerBar['barColor'] = VBase4(1.0, 1.0, 0.0, 0.8) + else: + self.powerBar['barColor'] = VBase4(1.0, 0.0, 0.0, 0.8) + return task.cont + + def __getWaterPower(self, time): + elapsed = max(time - self.aimStart, 0.0) + t = elapsed / self.waterPowerSpeed + exponent = self.waterPowerExponent + if t > 1: + t = t % 1 + power = 1 - math.pow(1 - t, exponent) + if power > 1.0: + power = 1.0 + return power + + def d_firingWater(self, origin, target): + self.sendUpdate('firingWater', [origin[0], + origin[1], + origin[2], + target[0], + target[1], + target[2]]) + + def firingWater(self, startX, startY, startZ, endX, endY, endZ): + origin = Point3(startX, startY, startZ) + target = Point3(endX, endY, endZ) + self.fireWater(origin, target) + + def fireWater(self, origin, target): + color = VBase4(0.75, 0.75, 1, 0.8) + dScaleUp = 0.1 + dHold = 0.3 + dScaleDown = 0.1 + horizScale = 0.1 + vertScale = 0.1 + sprayTrack = self.getSprayTrack(color, origin, target, dScaleUp, dHold, dScaleDown, horizScale, vertScale) + duration = self.squirtSfx.length() + if sprayTrack.getDuration() < duration: + duration = sprayTrack.getDuration() + soundTrack = SoundInterval(self.squirtSfx, node=self.waterPitcherModel, duration=duration) + self.fireTrack = Parallel(sprayTrack, soundTrack) + self.fireTrack.start() + + def getPos(self, wrt = render): + return self.tableGroup.getPos(wrt) + + def getLocator(self): + return self.tableGroup + + def enterFlat(self, avId): + self.prepareForPhaseFour() + self.resetPowerBar() + self.notify.debug('enterFlat %d' % self.index) + if self.avId: + toon = base.cr.doId2do.get(self.avId) + if toon: + toon.wrtReparentTo(render) + toon.setZ(0) + self.tableGroup.setScale(1, 1, 0.01) + if self.avId and self.avId == localAvatar.doId: + localAvatar.b_squish(ToontownGlobals.BossCogDamageLevels[ToontownGlobals.BossCogMoveAttack]) + + def exitFlat(self): + self.tableGroup.setScale(1.0) + if self.avId: + toon = base.cr.doId2do.get(self.avId) + if toon: + if toon == localAvatar: + self.boss.toCraneMode() + toon.b_setAnimState('neutral') + toon.setAnimState('neutral') + toon.loop('leverNeutral') + + def __allowDetect(self, task): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = Sequence(self.tableGroup.colorScaleInterval(0.2, VBase4(1, 1, 1, 1)), Func(self.tableGroup.clearColorScale), Func(self.tableGroup.clearTransparency)) + self.fadeTrack.start() + self.allowLocalRequestControl = True + + def gotBossZapped(self): + self.showExiting() + self.d_requestFree(True) + + def __upArrowKeyPressed(self): + if self.TugOfWarControls: + self.__pressHandler(0) + + def __downArrowKeyPressed(self): + if self.TugOfWarControls: + self.__pressHandler(1) + + def __pressHandler(self, index): + if index == self.buttons[0]: + self.keyTTL.insert(0, 1.0) + if not self.OnlyUpArrow: + self.buttons.reverse() + + def __spawnUpdateKeyPressRateTask(self): + taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK)) + taskMgr.doMethodLater(0.1, self.__updateKeyPressRateTask, self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK)) + + def __killUpdateKeyPressRateTask(self): + taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK)) + + def __updateKeyPressRateTask(self, task): + if self.state not in 'Controlled': + return Task.done + for i in xrange(len(self.keyTTL)): + self.keyTTL[i] -= 0.1 + + for i in xrange(len(self.keyTTL)): + if self.keyTTL[i] <= 0: + a = self.keyTTL[0:i] + del self.keyTTL + self.keyTTL = a + break + + self.keyRate = len(self.keyTTL) + keyRateDiff = self.keyRate - self.BASELINE_KEY_RATE + diffPower = keyRateDiff / 300.0 + if self.power < 1 and diffPower > 0: + diffPower = diffPower * math.pow(1 - self.power, 1.25) + newPower = self.power + diffPower + if newPower > 1: + newPower = 1 + elif newPower < 0: + newPower = 0 + self.notify.debug('diffPower=%.2f keyRate = %d, newPower=%.2f' % (diffPower, self.keyRate, newPower)) + self.power = newPower + self.powerBar['value'] = newPower + if self.power < self.YELLOW_POWER_THRESHOLD: + self.powerBar['barColor'] = VBase4(0.75, 0.75, 1.0, 0.8) + elif self.power < self.RED_POWER_THRESHOLD: + self.powerBar['barColor'] = VBase4(1.0, 1.0, 0.0, 0.8) + else: + self.powerBar['barColor'] = VBase4(1.0, 0.0, 0.0, 0.8) + self.__spawnUpdateKeyPressRateTask() + return Task.done + + def __setMoveSound(self, sfx): + if sfx != self.moveSound: + if self.moveSound: + self.moveSound.stop() + self.moveSound = sfx + if self.moveSound: + base.playSfx(self.moveSound, looping=1, volume=0.5) diff --git a/toontown/coghq/DistributedBanquetTableAI.py b/toontown/coghq/DistributedBanquetTableAI.py new file mode 100755 index 00000000..da76c31b --- /dev/null +++ b/toontown/coghq/DistributedBanquetTableAI.py @@ -0,0 +1,241 @@ +import random +from direct.distributed import DistributedObjectAI +from direct.fsm import FSM +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import BanquetTableBase +from toontown.toonbase import ToontownGlobals + +class DistributedBanquetTableAI(DistributedObjectAI.DistributedObjectAI, FSM.FSM, BanquetTableBase.BanquetTableBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBanquetTableAI') + + def __init__(self, air, boss, index, numDiners, dinerLevel): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedBanquetTableAI') + self.boss = boss + self.index = index + self.numDiners = numDiners + self.numChairs = 8 + self.dinerStatus = {} + self.dinerInfo = {} + for i in xrange(self.numDiners): + self.dinerStatus[i] = self.INACTIVE + diffSettings = ToontownGlobals.BossbotBossDifficultySettings[self.boss.battleDifficulty] + hungryDuration = diffSettings[4] + eatingDuration = diffSettings[5] + hungryDuration += random.uniform(-5, 5) + eatingDuration += random.uniform(-5, 5) + level = 12 + if type(dinerLevel) == type(0): + level = dinerLevel + else: + level = random.choice(dinerLevel) + self.dinerInfo[i] = (hungryDuration, eatingDuration, level) + + self.transitionTasks = {} + self.numFoodEaten = {} + self.avId = 0 + + def delete(self): + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def getNumDiners(self): + return self.numDiners + + def getDinerStatus(self, chairIndex): + retval = self.DEAD + if chairIndex in self.dinerStatus: + retval = self.dinerStatus[chairIndex] + return retval + + def setDinerStatus(self, chairIndex, newStatus): + self.dinerStatus[chairIndex] = newStatus + + def getDinerInfo(self): + hungryDurations = [] + eatingDurations = [] + dinerLevels = [] + for i in xrange(self.numDiners): + hungryDurations.append(self.dinerInfo[i][0]) + eatingDurations.append(self.dinerInfo[i][1]) + dinerLevels.append(self.dinerInfo[i][2]) + + return (hungryDurations, eatingDurations, dinerLevels) + + def d_setDinerStatus(self, chairIndex, newStatus): + self.sendUpdate('setDinerStatus', [chairIndex, newStatus]) + + def b_setDinerStatus(self, chairIndex, newStatus): + self.setDinerStatus(chairIndex, newStatus) + self.d_setDinerStatus(chairIndex, newStatus) + + def setState(self, state): + self.request(state) + + def d_setState(self, state, avId = 0, extraInfo = 0): + newState = state + if state == 'On': + newState = 'N' + elif state == 'Off': + newState = 'F' + elif state == 'Inactive': + newState = 'I' + elif state == 'Free': + newState = 'R' + elif state == 'Controlled': + newState = 'C' + elif state == 'Flat': + newState = 'L' + self.sendUpdate('setState', [newState, avId, extraInfo]) + + def b_setState(self, state, avId = 0, extraInfo = 0): + if state == 'Controlled' or state == 'Flat': + self.request(state, avId) + else: + self.request(state) + self.d_setState(state, avId, extraInfo) + + def turnOn(self): + self.b_setState('On') + + def turnOff(self): + self.b_setState('Off') + + def foodServed(self, chairIndex): + self.b_setDinerStatus(chairIndex, self.EATING) + eatingDur = self.dinerInfo[chairIndex][1] + if chairIndex in self.transitionTasks: + self.removeTask(self.transitionTasks[chairIndex]) + taskName = self.uniqueName('transition-%d' % chairIndex) + newTask = self.doMethodLater(eatingDur, self.finishedEating, taskName, extraArgs=[chairIndex]) + self.transitionTasks[chairIndex] = newTask + + def finishedEating(self, chairIndex): + if chairIndex in self.transitionTasks: + self.removeTask(self.transitionTasks[chairIndex]) + self.incrementFoodEaten(chairIndex) + if self.numFoodEaten[chairIndex] >= ToontownGlobals.BossbotNumFoodToExplode: + self.b_setDinerStatus(chairIndex, self.DEAD) + self.boss.incrementDinersExploded() + else: + self.b_setDinerStatus(chairIndex, self.HUNGRY) + taskName = self.uniqueName('transition-%d' % chairIndex) + hungryDur = self.dinerInfo[chairIndex][0] + newTask = self.doMethodLater(hungryDur, self.finishedHungry, taskName, extraArgs=[chairIndex]) + self.transitionTasks[chairIndex] = newTask + + def incrementFoodEaten(self, chairIndex): + numFood = 0 + if chairIndex in self.numFoodEaten: + numFood = self.numFoodEaten[chairIndex] + self.numFoodEaten[chairIndex] = numFood + 1 + + def finishedHungry(self, chairIndex): + self.b_setDinerStatus(chairIndex, self.ANGRY) + self.numFoodEaten[chairIndex] = 0 + if chairIndex in self.transitionTasks: + self.removeTask(self.transitionTasks[chairIndex]) + + def goInactive(self): + self.b_setState('Inactive') + + def goFree(self): + self.b_setState('Free') + + def goFlat(self): + self.b_setState('Flat', self.avId) + + def getNotDeadInfo(self): + notDeadList = [] + for i in xrange(self.numDiners): + if self.dinerStatus[i] != self.DEAD: + notDeadList.append((self.index, i, self.dinerInfo[i][2])) + + return notDeadList + + def requestControl(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.boss.involvedToons and self.avId == 0 and self.state == 'Free': + tableId = self.__getTableId(avId) + if tableId == 0: + grantRequest = True + if self.boss and not self.boss.isToonRoaming(avId): + grantRequest = False + if grantRequest: + self.b_setState('Controlled', avId) + + def forceControl(self, avId): + self.notify.debug('forceContrl tableIndex=%d avId=%d' % (self.index, avId)) + tableId = self.__getTableId(avId) + if tableId == self.doId: + if self.state == 'Flat': + self.b_setState('Controlled', avId) + else: + self.notify.warning('invalid forceControl from state %s' % self.state) + else: + self.notify.warning('tableId %d != self.doId %d ' % (tableId, self.doId)) + + def requestFree(self, gotHitByBoss): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + if self.state == 'Controlled': + self.b_setState('Free', extraInfo=gotHitByBoss) + if self.boss: + self.boss.toonLeftTable(self.index) + else: + self.notify.debug('requestFree denied in state %s' % self.state) + + def __getTableId(self, avId): + if self.boss and self.boss.tables != None: + for table in self.boss.tables: + if table.avId == avId: + return table.doId + + return 0 + + def enterOn(self): + for i in xrange(self.numDiners): + self.b_setDinerStatus(i, self.HUNGRY) + + def exitOn(slef): + pass + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterInactive(self): + for task in self.transitionTasks.values(): + self.removeTask(task) + + self.transitionTasks = {} + + def exitInactive(self): + pass + + def enterFree(self): + self.notify.debug('enterFree tableIndex=%d' % self.index) + self.avId = 0 + + def exitFree(self): + pass + + def enterControlled(self, avId): + self.notify.debug('enterControlled tableIndex=%d' % self.index) + self.avId = avId + + def exitControlled(self): + pass + + def enterFlat(self, avId): + self.notify.debug('enterFlat tableIndex=%d' % self.index) + + def exitFlat(self): + pass diff --git a/toontown/coghq/DistributedBarrelBase.py b/toontown/coghq/DistributedBarrelBase.py new file mode 100755 index 00000000..d1011c32 --- /dev/null +++ b/toontown/coghq/DistributedBarrelBase.py @@ -0,0 +1,101 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.coghq import BarrelBase +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal + +class DistributedBarrelBase(BasicEntities.DistributedNodePathEntity, BarrelBase.BarrelBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBarrelBase') + + def __init__(self, cr): + self.rewardPerGrabMax = 0 + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.grabSoundPath = 'phase_4/audio/sfx/SZ_DD_treasure.ogg' + self.rejectSoundPath = 'phase_4/audio/sfx/ring_miss.ogg' + self.animTrack = None + self.shadow = 0 + self.barrelScale = 0.5 + self.sphereRadius = 3.5 + self.playSoundForRemoteToons = 1 + self.gagNode = None + self.gagModel = None + self.barrel = None + return + + def disable(self): + BasicEntities.DistributedNodePathEntity.disable(self) + self.ignoreAll() + if self.animTrack: + self.animTrack.pause() + self.animTrack = None + return + + def generate(self): + BasicEntities.DistributedNodePathEntity.generate(self) + + def delete(self): + BasicEntities.DistributedNodePathEntity.delete(self) + self.gagNode.removeNode() + del self.gagNode + if self.barrel: + self.barrel.removeNode() + del self.barrel + self.barrel = None + return + + def announceGenerate(self): + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.setTag('doId', str(self.getDoId())) + self.loadModel() + self.collSphere = CollisionSphere(0, 0, 0, self.sphereRadius) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.uniqueName('barrelSphere')) + self.collNode.setIntoCollideMask(WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.barrel.attachNewNode(self.collNode) + self.collNodePath.hide() + self.applyLabel() + self.accept(self.uniqueName('enterbarrelSphere'), self.handleEnterSphere) + + def loadModel(self): + self.grabSound = base.loadSfx(self.grabSoundPath) + self.rejectSound = base.loadSfx(self.rejectSoundPath) + self.barrel = loader.loadModel('phase_4/models/cogHQ/gagTank') + self.barrel.setScale(self.barrelScale) + self.barrel.reparentTo(self) + dcsNode = self.barrel.find('**/gagLabelDCS') + dcsNode.setColor(0.15, 0.15, 0.1) + self.gagNode = self.barrel.attachNewNode('gagNode') + self.gagNode.setPosHpr(0.0, -2.62, 4.0, 0, 0, 0) + self.gagNode.setColorScale(0.7, 0.7, 0.6, 1) + + def handleEnterSphere(self, collEntry = None): + localAvId = base.localAvatar.getDoId() + self.d_requestGrab() + + def d_requestGrab(self): + self.sendUpdate('requestGrab', []) + + def setGrab(self, avId): + self.notify.debug('handleGrab %s' % avId) + self.avId = avId + if avId == base.localAvatar.doId: + self.ignore(self.uniqueName('entertreasureSphere')) + self.barrel.setColorScale(0.5, 0.5, 0.5, 1) + if self.playSoundForRemoteToons or self.avId == base.localAvatar.getDoId(): + base.playSfx(self.grabSound) + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + flytime = 1.0 + self.animTrack = Sequence(LerpScaleInterval(self.barrel, 0.2, 1.1 * self.barrelScale, blendType='easeInOut'), LerpScaleInterval(self.barrel, 0.2, self.barrelScale, blendType='easeInOut'), Func(self.resetBarrel), name=self.uniqueName('animTrack')) + self.animTrack.start() + return + + def setReject(self): + self.notify.debug('I was rejected!!!!!') + + def resetBarrel(self): + self.barrel.setScale(self.barrelScale) + self.accept(self.uniqueName('entertreasureSphere'), self.handleEnterSphere) diff --git a/toontown/coghq/DistributedBarrelBaseAI.py b/toontown/coghq/DistributedBarrelBaseAI.py new file mode 100755 index 00000000..06e3f24b --- /dev/null +++ b/toontown/coghq/DistributedBarrelBaseAI.py @@ -0,0 +1,30 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.level import DistributedEntityAI +from direct.task import Task +from toontown.coghq import BarrelBase + +class DistributedBarrelBaseAI(DistributedEntityAI.DistributedEntityAI, BarrelBase.BarrelBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBarrelBaseAI') + + def __init__(self, level, entId): + self.rewardPerGrabMax = 0 + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.usedAvIds = [] + + def delete(self): + taskMgr.remove(self.taskName('resetGags')) + del self.usedAvIds + del self.pos + DistributedEntityAI.DistributedEntityAI.delete(self) + + def requestGrab(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('requestGrab %s' % avId) + if avId not in self.usedAvIds: + self.usedAvIds.append(avId) + self.d_setGrab(avId) + else: + self.sendUpdate('setReject') + + def d_setGrab(self, avId): + self.sendUpdate('setGrab', [avId]) diff --git a/toontown/coghq/DistributedBattleFactory.py b/toontown/coghq/DistributedBattleFactory.py new file mode 100755 index 00000000..1396bccb --- /dev/null +++ b/toontown/coghq/DistributedBattleFactory.py @@ -0,0 +1,49 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleBase import * +from toontown.coghq import DistributedLevelBattle +from direct.directnotify import DirectNotifyGlobal +from toontown.toon import TTEmote +from otp.avatar import Emote +from toontown.battle import SuitBattleGlobals +import random +from toontown.suit import SuitDNA +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals + +class DistributedBattleFactory(DistributedLevelBattle.DistributedLevelBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleFactory') + + def __init__(self, cr): + DistributedLevelBattle.DistributedLevelBattle.__init__(self, cr) + self.fsm.addState(State.State('FactoryReward', self.enterFactoryReward, self.exitFactoryReward, ['Resume'])) + offState = self.fsm.getStateNamed('Off') + offState.addTransition('FactoryReward') + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('FactoryReward') + + def enterFactoryReward(self, ts): + self.notify.info('enterFactoryReward()') + self.disableCollision() + self.delayDeleteMembers() + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + if self.bossBattle: + messenger.send('localToonConfrontedForeman') + self.movie.playReward(ts, self.uniqueName('building-reward'), self.__handleFactoryRewardDone, noSkip=True) + + def __handleFactoryRewardDone(self): + self.notify.info('Factory reward done') + if self.hasLocalToon(): + self.d_rewardDone(base.localAvatar.doId) + self.movie.resetReward() + self.fsm.request('Resume') + + def exitFactoryReward(self): + self.notify.info('exitFactoryReward()') + self.movie.resetReward(finish=1) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) diff --git a/toontown/coghq/DistributedBattleFactoryAI.py b/toontown/coghq/DistributedBattleFactoryAI.py new file mode 100755 index 00000000..7bbd6794 --- /dev/null +++ b/toontown/coghq/DistributedBattleFactoryAI.py @@ -0,0 +1,58 @@ +from toontown.coghq import DistributedLevelBattleAI +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.battle.BattleBase import * +import CogDisguiseGlobals +from direct.showbase.PythonUtil import addListsByValue + +class DistributedBattleFactoryAI(DistributedLevelBattleAI.DistributedLevelBattleAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleFactoryAI') + + def __init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, roundCallback = None, finishCallback = None, maxSuits = 4): + DistributedLevelBattleAI.DistributedLevelBattleAI.__init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, 'FactoryReward', roundCallback, finishCallback, maxSuits) + self.battleCalc.setSkillCreditMultiplier(1) + if self.bossBattle: + self.level.d_setForemanConfronted(toonId) + self.fsm.addState(State.State('FactoryReward', self.enterFactoryReward, self.exitFactoryReward, ['Resume'])) + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('FactoryReward') + + def getTaskZoneId(self): + return self.level.factoryId + + def handleToonsWon(self, toons): + self.handleCrateReward(toons) + for toon in toons: + recovered, notRecovered = self.air.questManager.recoverItems(toon, self.suitsKilled, self.getTaskZoneId()) + self.toonItems[toon.doId][0].extend(recovered) + self.toonItems[toon.doId][1].extend(notRecovered) + meritArray = self.air.promotionMgr.recoverMerits( + toon, self.suitsKilled, self.getTaskZoneId(), + getFactoryMeritMultiplier(self.getTaskZoneId()) * 2.0, addInvasion=False) + if toon.doId in self.helpfulToons: + self.toonMerits[toon.doId] = addListsByValue(self.toonMerits[toon.doId], meritArray) + else: + self.notify.debug('toon %d not helpful, skipping merits' % toon.doId) + if self.bossBattle: + self.toonParts[toon.doId] = self.air.cogSuitMgr.recoverPart(toon, self.level.factoryType, self.suitTrack) + self.notify.debug('toonParts = %s' % self.toonParts) + + + def enterFactoryReward(self): + self.joinableFsm.request('Unjoinable') + self.runableFsm.request('Unrunable') + self.resetResponses() + self.assignRewards() + self.bossDefeated = 1 + self.level.setVictors(self.activeToons[:]) + self.timer.startCallback(BUILDING_REWARD_TIMEOUT, self.serverRewardDone) + return None + + def exitFactoryReward(self): + return None + + def enterResume(self): + DistributedLevelBattleAI.DistributedLevelBattleAI.enterResume(self) + if self.bossBattle and self.bossDefeated: + self.battleMgr.level.b_setDefeated() diff --git a/toontown/coghq/DistributedBeanBarrel.py b/toontown/coghq/DistributedBeanBarrel.py new file mode 100755 index 00000000..86bf9d04 --- /dev/null +++ b/toontown/coghq/DistributedBeanBarrel.py @@ -0,0 +1,32 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +import DistributedBarrelBase + +class DistributedBeanBarrel(DistributedBarrelBase.DistributedBarrelBase): + + def __init__(self, cr): + DistributedBarrelBase.DistributedBarrelBase.__init__(self, cr) + self.numGags = 0 + self.gagScale = 3.0 + + def disable(self): + DistributedBarrelBase.DistributedBarrelBase.disable(self) + self.ignoreAll() + + def delete(self): + self.gagModel.removeNode() + del self.gagModel + DistributedBarrelBase.DistributedBarrelBase.delete(self) + + def applyLabel(self): + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + self.gagModel = purchaseModels.find('**/Jar') + self.gagModel.reparentTo(self.gagNode) + self.gagModel.setScale(self.gagScale) + self.gagModel.setPos(0, -0.1, 0) + purchaseModels.removeNode() + + def setGrab(self, avId): + DistributedBarrelBase.DistributedBarrelBase.setGrab(self, avId) diff --git a/toontown/coghq/DistributedBeanBarrelAI.py b/toontown/coghq/DistributedBeanBarrelAI.py new file mode 100755 index 00000000..12efd867 --- /dev/null +++ b/toontown/coghq/DistributedBeanBarrelAI.py @@ -0,0 +1,16 @@ +import DistributedBarrelBaseAI +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task + +class DistributedBeanBarrelAI(DistributedBarrelBaseAI.DistributedBarrelBaseAI): + + def __init__(self, level, entityId): + x = y = z = h = 0 + DistributedBarrelBaseAI.DistributedBarrelBaseAI.__init__(self, level, entityId) + + def d_setGrab(self, avId): + self.notify.debug('d_setGrab %s' % avId) + self.sendUpdate('setGrab', [avId]) + av = self.air.doId2do.get(avId) + if av: + av.addMoney(self.rewardPerGrab) diff --git a/toontown/coghq/DistributedButton.py b/toontown/coghq/DistributedButton.py new file mode 100755 index 00000000..702f7160 --- /dev/null +++ b/toontown/coghq/DistributedButton.py @@ -0,0 +1,136 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +import MovingPlatform +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +import DistributedSwitch +from toontown.toonbase import TTLocalizer + +class DistributedButton(DistributedSwitch.DistributedSwitch): + countdownSeconds = 3.0 + + def __init__(self, cr): + self.countdownTrack = None + DistributedSwitch.DistributedSwitch.__init__(self, cr) + return + + def setSecondsOn(self, secondsOn): + self.secondsOn = secondsOn + + def avatarExit(self, avatarId): + DistributedSwitch.DistributedSwitch.avatarExit(self, avatarId) + if self.secondsOn != -1.0 and self.secondsOn > 0.0 and self.countdownSeconds > 0.0 and self.countdownSeconds < self.secondsOn and self.fsm.getCurrentState().getName() == 'playing': + track = self.switchCountdownTrack() + if track is not None: + track.start(0.0) + self.countdownTrack = track + return + + def setupSwitch(self): + model = loader.loadModel('phase_9/models/cogHQ/CogDoor_Button') + if model: + buttonBase = model.find('**/buttonBase') + change = render.attachNewNode('changePos') + buttonBase.reparentTo(change) + rootNode = render.attachNewNode(self.getName() + '-buttonBase_root') + change.reparentTo(rootNode) + self.buttonFrameNode = rootNode + self.buttonFrameNode.show() + button = model.find('**/button') + change = render.attachNewNode('change') + button.reparentTo(change) + rootNode = render.attachNewNode(self.getName() + '-button_root') + rootNode.setColor(self.color) + change.reparentTo(rootNode) + self.buttonNode = rootNode + self.buttonNode.show() + self.buttonFrameNode.reparentTo(self) + self.buttonNode.reparentTo(self) + if 1: + radius = 0.5 + cSphere = CollisionSphere(0.0, 0.0, radius, radius) + cSphere.setTangible(0) + cSphereNode = CollisionNode(self.getName()) + cSphereNode.addSolid(cSphere) + cSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + self.cSphereNodePath = rootNode.attachNewNode(cSphereNode) + if 1: + collisionFloor = button.find('**/collision_floor') + if collisionFloor.isEmpty(): + top = 0.475 + size = 0.5 + floor = CollisionPolygon(Point3(-size, -size, top), + Point3( size, -size, top), + Point3( size, size, top), + Point3(-size, size, top)) + floor.setTangible(1) + floorNode = CollisionNode('collision_floor') + floorNode.addSolid(floor) + collisionFloor = button.attachNewNode(floorNode) + else: + change = collisionFloor.getParent().attachNewNode('changeFloor') + change.setScale(0.5, 0.5, 1.0) + collisionFloor.reparentTo(change) + collisionFloor.node().setFromCollideMask(BitMask32.allOff()) + collisionFloor.node().setIntoCollideMask(ToontownGlobals.FloorBitmask) + self.buttonFrameNode.flattenMedium() + self.buttonNode.flattenMedium() + + def delete(self): + DistributedSwitch.DistributedSwitch.delete(self) + + def enterTrigger(self, args = None): + DistributedSwitch.DistributedSwitch.enterTrigger(self, args) + + def exitTrigger(self, args = None): + DistributedSwitch.DistributedSwitch.exitTrigger(self, args) + + def switchOnTrack(self): + onSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_switch_pressed.ogg') + duration = 0.8 + halfDur = duration * 0.5 + pos = Vec3(0.0, 0.0, -0.2) + color = Vec4(0.0, 1.0, 0.0, 1.0) + track = Sequence(Func(self.setIsOn, 1), Parallel(SoundInterval(onSfx, node=self.node, volume=0.9), LerpPosInterval(nodePath=self.buttonNode, duration=duration, pos=pos, blendType='easeInOut'), Sequence(Wait(halfDur), LerpColorInterval(nodePath=self.buttonNode, duration=halfDur, color=color, override=1, blendType='easeOut')))) + return track + + def switchCountdownTrack(self): + wait = self.secondsOn - self.countdownSeconds + countDownSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_switch_depressed.ogg') + track = Parallel( + SoundInterval(countDownSfx), + Sequence( + Wait(wait), + Wait(0.5), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=self.color, override=1, blendType='easeIn'), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=Vec4(0.0, 1.0, 0.0, 1.0), override=1, blendType='easeOut'), + Wait(0.5), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=self.color, override=1, blendType='easeIn'), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=Vec4(0.0, 1.0, 0.0, 1.0), override=1, blendType='easeOut'), + Wait(0.4), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=self.color, override=1, blendType='easeIn'), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=Vec4(0.0, 1.0, 0.0, 1.0), override=1, blendType='easeOut'), + Wait(0.3), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=self.color, override=1, blendType='easeIn'), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=Vec4(0.0, 1.0, 0.0, 1.0), override=1, blendType='easeOut'), + Wait(0.2), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=self.color, override=1, blendType='easeIn'), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, override=1, color=Vec4(0.0, 1.0, 0.0, 1.0), blendType='easeOut'), + Wait(0.1))) + return track + + def switchOffTrack(self): + offSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_switch_popup.ogg') + duration = 1.0 + halfDur = duration * 0.5 + pos = Vec3(0.0) + track = Sequence(Parallel(SoundInterval(offSfx, node=self.node, volume=1.0), LerpPosInterval(nodePath=self.buttonNode, duration=duration, pos=pos, blendType='easeInOut'), Sequence(Wait(halfDur), LerpColorInterval(nodePath=self.buttonNode, duration=halfDur, color=self.color, override=1, blendType='easeIn'))), Func(self.setIsOn, 0)) + return track + + def exitPlaying(self): + if self.countdownTrack: + self.countdownTrack.finish() + self.countdownTrack = None + DistributedSwitch.DistributedSwitch.exitPlaying(self) diff --git a/toontown/coghq/DistributedButtonAI.py b/toontown/coghq/DistributedButtonAI.py new file mode 100755 index 00000000..a8410794 --- /dev/null +++ b/toontown/coghq/DistributedButtonAI.py @@ -0,0 +1,8 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +import DistributedSwitchBase +import DistributedSwitchAI + +class DistributedButtonAI(DistributedSwitchAI.DistributedSwitchAI): + setColor = DistributedSwitchBase.stubFunction + setModel = DistributedSwitchBase.stubFunction diff --git a/toontown/coghq/DistributedCashbotBossCrane.py b/toontown/coghq/DistributedCashbotBossCrane.py new file mode 100755 index 00000000..21dfdc33 --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossCrane.py @@ -0,0 +1,912 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.fsm import FSM +from direct.distributed import DistributedObject +from direct.showutil import Rope +from direct.showbase import PythonUtil +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPGlobals +from otp.nametag import NametagGlobals +import random + +class DistributedCashbotBossCrane(DistributedObject.DistributedObject, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCashbotBossCrane') + firstMagnetBit = 21 + craneMinY = 8 + craneMaxY = 25 + armMinH = -45 + armMaxH = 45 + shadowOffset = 1 + emptyFrictionCoef = 0.1 + emptySlideSpeed = 10 + emptyRotateSpeed = 20 + lookAtPoint = Point3(0.3, 0, 0.1) + lookAtUp = Vec3(0, -1, 0) + neutralStickHinge = VBase3(0, 90, 0) + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedCashbotBossCrane') + self.boss = None + self.index = None + self.avId = 0 + self.cableLength = 20 + self.numLinks = 3 + self.initialArmPosition = (0, 20, 0) + self.slideSpeed = self.emptySlideSpeed + self.rotateSpeed = self.emptyRotateSpeed + self.changeSeq = 0 + self.lastChangeSeq = 0 + self.moveSound = None + self.links = [] + self.activeLinks = [] + self.collisions = NodePathCollection() + self.physicsActivated = 0 + self.snifferActivated = 0 + self.magnetOn = 0 + self.root = NodePath('root') + self.hinge = self.root.attachNewNode('hinge') + self.hinge.setPos(0, -17.6, 38.5) + self.controls = self.root.attachNewNode('controls') + self.controls.setPos(0, -4.9, 0) + self.arm = self.hinge.attachNewNode('arm') + self.crane = self.arm.attachNewNode('crane') + self.cable = self.hinge.attachNewNode('cable') + self.topLink = self.crane.attachNewNode('topLink') + self.topLink.setPos(0, 0, -1) + self.shadow = None + self.p0 = Point3(0, 0, 0) + self.v1 = Vec3(1, 1, 1) + self.armSmoother = SmoothMover() + self.armSmoother.setSmoothMode(SmoothMover.SMOn) + self.linkSmoothers = [] + self.smoothStarted = 0 + self.__broadcastPeriod = 0.2 + self.cable.node().setFinal(1) + self.crane.setPos(*self.initialArmPosition) + self.heldObject = None + self.closeButton = None + self.craneAdviceLabel = None + self.magnetAdviceLabel = None + self.atLimitSfx = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.ogg') + self.magnetOnSfx = base.loadSfx('phase_10/audio/sfx/CBHQ_CFO_magnet_on.ogg') + self.magnetLoopSfx = base.loadSfx('phase_10/audio/sfx/CBHQ_CFO_magnet_loop.ogg') + self.magnetSoundInterval = Parallel(SoundInterval(self.magnetOnSfx), Sequence(Wait(0.5), Func(base.playSfx, self.magnetLoopSfx, looping=1))) + self.craneMoveSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_elevator_up_down.ogg') + self.fadeTrack = None + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.name = 'crane-%s' % self.doId + self.root.setName(self.name) + self.root.setPosHpr(*ToontownGlobals.CashbotBossCranePosHprs[self.index]) + self.rotateLinkName = self.uniqueName('rotateLink') + self.snifferEvent = self.uniqueName('sniffer') + self.triggerName = self.uniqueName('trigger') + self.triggerEvent = 'enter%s' % self.triggerName + self.shadowName = self.uniqueName('shadow') + self.flickerName = self.uniqueName('flicker') + self.smoothName = self.uniqueName('craneSmooth') + self.posHprBroadcastName = self.uniqueName('craneBroadcast') + self.craneAdviceName = self.uniqueName('craneAdvice') + self.magnetAdviceName = self.uniqueName('magnetAdvice') + self.controlModel = self.boss.controls.copyTo(self.controls) + self.cc = NodePath('cc') + column = self.controlModel.find('**/column') + column.getChildren().reparentTo(self.cc) + self.cc.reparentTo(column) + self.stickHinge = self.cc.attachNewNode('stickHinge') + self.stick = self.boss.stick.copyTo(self.stickHinge) + self.stickHinge.setHpr(self.neutralStickHinge) + self.stick.setHpr(0, -90, 0) + self.stick.flattenLight() + self.bottom = self.controlModel.find('**/bottom') + self.bottom.wrtReparentTo(self.cc) + self.bottomPos = self.bottom.getPos() + cs = CollisionSphere(0, -5, -2, 3) + cs.setTangible(0) + cn = CollisionNode(self.triggerName) + cn.addSolid(cs) + cn.setIntoCollideMask(OTPGlobals.WallBitmask) + self.trigger = self.root.attachNewNode(cn) + self.trigger.stash() + cs = CollisionTube(0, 2.7, 0, 0, 2.7, 3, 1.2) + cn = CollisionNode('tube') + cn.addSolid(cs) + cn.setIntoCollideMask(OTPGlobals.WallBitmask) + self.tube = self.controlModel.attachNewNode(cn) + cs = CollisionSphere(0, 0, 2, 3) + cn = CollisionNode('safetyBubble') + cn.addSolid(cs) + cn.setIntoCollideMask(ToontownGlobals.PieBitmask) + self.controls.attachNewNode(cn) + arm = self.boss.craneArm.copyTo(self.crane) + self.boss.cranes[self.index] = self + + def disable(self): + DistributedObject.DistributedObject.disable(self) + del self.boss.cranes[self.index] + self.cleanup() + + def cleanup(self): + if self.state != 'Off': + self.demand('Off') + self.boss = None + return + + def accomodateToon(self, toon): + origScale = self.controlModel.getSz() + origCcPos = self.cc.getPos() + origBottomPos = self.bottom.getPos() + origStickHingeHpr = self.stickHinge.getHpr() + scale = toon.getGeomNode().getChild(0).getSz(render) + self.controlModel.setScale(scale) + self.cc.setPos(0, 0, 0) + toon.setPosHpr(self.controls, 0, 0, 0, 0, 0, 0) + toon.pose('leverNeutral', 0) + toon.update() + pos = toon.rightHand.getPos(self.cc) + self.cc.setPos(pos[0], pos[1], pos[2] - 1) + self.bottom.setZ(toon, 0.0) + self.bottom.setPos(self.bottomPos[0], self.bottomPos[1], self.bottom.getZ()) + self.stickHinge.lookAt(toon.rightHand, self.lookAtPoint, self.lookAtUp) + lerpTime = 0.5 + return Parallel(self.controlModel.scaleInterval(lerpTime, scale, origScale, blendType='easeInOut'), self.cc.posInterval(lerpTime, self.cc.getPos(), origCcPos, blendType='easeInOut'), self.bottom.posInterval(lerpTime, self.bottom.getPos(), origBottomPos, blendType='easeInOut'), self.stickHinge.quatInterval(lerpTime, self.stickHinge.getHpr(), origStickHingeHpr, blendType='easeInOut')) + + def getRestoreScaleInterval(self): + lerpTime = 1 + return Parallel(self.controlModel.scaleInterval(lerpTime, 1, blendType='easeInOut'), self.cc.posInterval(lerpTime, Point3(0, 0, 0), blendType='easeInOut'), self.bottom.posInterval(lerpTime, self.bottomPos, blendType='easeInOut'), self.stickHinge.quatInterval(lerpTime, self.neutralStickHinge, blendType='easeInOut')) + + def makeToonGrabInterval(self, toon): + origPos = toon.getPos() + origHpr = toon.getHpr() + a = self.accomodateToon(toon) + newPos = toon.getPos() + newHpr = toon.getHpr() + origHpr.setX(PythonUtil.fitSrcAngle2Dest(origHpr[0], newHpr[0])) + toon.setPosHpr(origPos, origHpr) + walkTime = 0.2 + reach = ActorInterval(toon, 'leverReach') + if reach.getDuration() < walkTime: + reach = Sequence(ActorInterval(toon, 'walk', loop=1, duration=walkTime - reach.getDuration()), reach) + i = Sequence(Parallel(toon.posInterval(walkTime, newPos, origPos), toon.hprInterval(walkTime, newHpr, origHpr), reach), Func(self.startWatchJoystick, toon)) + i = Parallel(i, a) + return i + + def __toonPlayWithCallback(self, animName, numFrames): + duration = numFrames / 24.0 + self.toon.play(animName) + taskMgr.doMethodLater(duration, self.__toonPlayCallback, self.uniqueName('toonPlay')) + + def __toonPlayCallback(self, task): + if self.changeSeq == self.lastChangeSeq: + self.__toonPlayWithCallback('leverNeutral', 40) + else: + self.__toonPlayWithCallback('leverPull', 40) + self.lastChangeSeq = self.changeSeq + + def startWatchJoystick(self, toon): + self.toon = toon + taskMgr.add(self.__watchJoystick, self.uniqueName('watchJoystick')) + self.__toonPlayWithCallback('leverNeutral', 40) + self.accept(toon.uniqueName('disable'), self.__handleUnexpectedExit, extraArgs=[toon.doId]) + + def stopWatchJoystick(self): + taskMgr.remove(self.uniqueName('toonPlay')) + taskMgr.remove(self.uniqueName('watchJoystick')) + if self.toon: + self.ignore(self.toon.uniqueName('disable')) + self.toon = None + return + + def __watchJoystick(self, task): + self.toon.setPosHpr(self.controls, 0, 0, 0, 0, 0, 0) + self.toon.update() + self.stickHinge.lookAt(self.toon.rightHand, self.lookAtPoint, self.lookAtUp) + return Task.cont + + def __handleUnexpectedExit(self, toonId): + self.notify.warning('%s: unexpected exit for %s' % (self.doId, toonId)) + if self.toon and self.toon.doId == toonId: + self.stopWatchJoystick() + + def __activatePhysics(self): + if not self.physicsActivated: + for an, anp, cnp in self.activeLinks: + self.boss.physicsMgr.attachPhysicalNode(an) + base.cTrav.addCollider(cnp, self.handler) + + self.collisions.unstash() + self.physicsActivated = 1 + + def __deactivatePhysics(self): + if self.physicsActivated: + for an, anp, cnp in self.activeLinks: + self.boss.physicsMgr.removePhysicalNode(an) + base.cTrav.removeCollider(cnp) + + self.collisions.stash() + self.physicsActivated = 0 + + def __straightenCable(self): + for linkNum in xrange(self.numLinks): + an, anp, cnp = self.activeLinks[linkNum] + an.getPhysicsObject().setVelocity(0, 0, 0) + z = float(linkNum + 1) / float(self.numLinks) * self.cableLength + anp.setPos(self.crane.getPos(self.cable)) + anp.setZ(-z) + + def setCableLength(self, length): + self.cableLength = length + linkWidth = float(length) / float(self.numLinks) + self.shell.setRadius(linkWidth + 1) + + def setupCable(self): + activated = self.physicsActivated + self.clearCable() + self.handler = PhysicsCollisionHandler() + self.handler.setStaticFrictionCoef(0.1) + self.handler.setDynamicFrictionCoef(self.emptyFrictionCoef) + linkWidth = float(self.cableLength) / float(self.numLinks) + self.shell = CollisionInvSphere(0, 0, 0, linkWidth + 1) + self.links = [] + self.links.append((self.topLink, Point3(0, 0, 0))) + anchor = self.topLink + for linkNum in xrange(self.numLinks): + anchor = self.__makeLink(anchor, linkNum) + + self.collisions.stash() + self.bottomLink = self.links[-1][0] + self.middleLink = self.links[-2][0] + self.magnet = self.bottomLink.attachNewNode('magnet') + self.wiggleMagnet = self.magnet.attachNewNode('wiggleMagnet') + taskMgr.add(self.__rotateMagnet, self.rotateLinkName) + magnetModel = self.boss.magnet.copyTo(self.wiggleMagnet) + magnetModel.setHpr(90, 45, 90) + self.gripper = magnetModel.attachNewNode('gripper') + self.gripper.setPos(0, 0, -4) + cn = CollisionNode('sniffer') + self.sniffer = magnetModel.attachNewNode(cn) + self.sniffer.stash() + cs = CollisionSphere(0, 0, -10, 6) + cs.setTangible(0) + cn.addSolid(cs) + cn.setIntoCollideMask(BitMask32(0)) + cn.setFromCollideMask(ToontownGlobals.CashbotBossObjectBitmask) + self.snifferHandler = CollisionHandlerEvent() + self.snifferHandler.addInPattern(self.snifferEvent) + self.snifferHandler.addAgainPattern(self.snifferEvent) + rope = self.makeSpline() + rope.reparentTo(self.cable) + rope.setTexture(self.boss.cableTex) + ts = TextureStage.getDefault() + rope.setTexScale(ts, 0.15, 0.13) + rope.setTexOffset(ts, 0.83, 0.01) + if activated: + self.__activatePhysics() + + def clearCable(self): + self.__deactivatePhysics() + taskMgr.remove(self.rotateLinkName) + self.links = [] + self.activeLinks = [] + self.linkSmoothers = [] + self.collisions.clear() + self.cable.getChildren().detach() + self.topLink.getChildren().detach() + self.gripper = None + return + + def makeSpline(self): + rope = Rope.Rope() + rope.setup(min(len(self.links), 4), self.links) + rope.curve.normalizeKnots() + rn = rope.ropeNode + rn.setRenderMode(RopeNode.RMTube) + rn.setNumSlices(3) + rn.setTubeUp(Vec3(0, -1, 0)) + rn.setUvMode(RopeNode.UVParametric) + rn.setUvDirection(1) + rn.setThickness(0.5) + return rope + + def startShadow(self): + self.shadow = self.boss.geom.attachNewNode('%s-shadow' % self.name) + self.shadow.setColor(1, 1, 1, 0.3) + self.shadow.setDepthWrite(0) + self.shadow.setTransparency(1) + self.shadow.setBin('shadow', 0) + self.shadow.node().setFinal(1) + self.magnetShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.magnetShadow.reparentTo(self.shadow) + self.craneShadow = loader.loadModel('phase_3/models/props/square_drop_shadow') + self.craneShadow.setScale(0.5, 4, 1) + self.craneShadow.setPos(0, -12, 0) + self.craneShadow.flattenLight() + self.craneShadow.reparentTo(self.shadow) + taskMgr.add(self.__followShadow, self.shadowName) + rope = self.makeSpline() + rope.reparentTo(self.shadow) + rope.setColor(1, 1, 1, 0.2) + tex = self.craneShadow.findTexture('*') + rope.setTexture(tex) + rn = rope.ropeNode + rn.setRenderMode(RopeNode.RMTape) + rn.setNumSubdiv(6) + rn.setThickness(0.8) + rn.setTubeUp(Vec3(0, 0, 1)) + rn.setMatrix(Mat4.translateMat(0, 0, self.shadowOffset) * Mat4.scaleMat(1, 1, 0.01)) + + def stopShadow(self): + if self.shadow: + self.shadow.removeNode() + self.shadow = None + self.magnetShadow = None + self.craneShadow = None + taskMgr.remove(self.shadowName) + return + + def __followShadow(self, task): + p = self.magnet.getPos(self.boss.geom) + self.magnetShadow.setPos(p[0], p[1], self.shadowOffset) + self.craneShadow.setPosHpr(self.crane, 0, 0, 0, 0, 0, 0) + self.craneShadow.setZ(self.shadowOffset) + return Task.cont + + def __makeLink(self, anchor, linkNum): + an = ActorNode('link%s' % linkNum) + anp = NodePath(an) + cn = CollisionNode('cn') + sphere = CollisionSphere(0, 0, 0, 1) + cn.addSolid(sphere) + cnp = anp.attachNewNode(cn) + self.handler.addCollider(cnp, anp) + self.activeLinks.append((an, anp, cnp)) + self.linkSmoothers.append(SmoothMover()) + anp.reparentTo(self.cable) + z = float(linkNum + 1) / float(self.numLinks) * self.cableLength + anp.setPos(self.crane.getPos()) + anp.setZ(-z) + mask = BitMask32.bit(self.firstMagnetBit + linkNum) + cn.setFromCollideMask(mask) + cn.setIntoCollideMask(BitMask32(0)) + shellNode = CollisionNode('shell%s' % linkNum) + shellNode.addSolid(self.shell) + shellNP = anchor.attachNewNode(shellNode) + shellNode.setIntoCollideMask(mask) + self.collisions.addPath(shellNP) + self.collisions.addPath(cnp) + self.links.append((anp, Point3(0, 0, 0))) + return anp + + def __rotateMagnet(self, task): + self.magnet.lookAt(self.middleLink, self.p0, self.v1) + return Task.cont + + def __enableControlInterface(self): + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.closeButton = DirectButton( + parent=base.a2dBottomRight, + image=(gui.find('**/CloseBtn_UP'), gui.find('**/CloseBtn_DN'), + gui.find('**/CloseBtn_Rllvr'), gui.find('**/CloseBtn_UP')), + relief=None, scale=2, text=TTLocalizer.CashbotCraneLeave, text_scale=0.04, + text_pos=(0, -0.07), text_fg=VBase4(1, 1, 1, 1), pos=(-0.25, 0, 0.175), + command=self.__exitCrane) + self.accept('escape', self.__exitCrane) + self.accept('control', self.__controlPressed) + self.accept('control-up', self.__controlReleased) + self.accept('InputState-forward', self.__upArrow) + self.accept('InputState-reverse', self.__downArrow) + self.accept('InputState-turnLeft', self.__leftArrow) + self.accept('InputState-turnRight', self.__rightArrow) + taskMgr.add(self.__watchControls, 'watchCraneControls') + taskMgr.doMethodLater(5, self.__displayCraneAdvice, self.craneAdviceName) + taskMgr.doMethodLater(10, self.__displayMagnetAdvice, self.magnetAdviceName) + NametagGlobals.setOnscreenChatForced(1) + self.arrowVert = 0 + self.arrowHorz = 0 + return + + def __disableControlInterface(self): + self.__turnOffMagnet() + if self.closeButton: + self.closeButton.destroy() + self.closeButton = None + self.__cleanupCraneAdvice() + self.__cleanupMagnetAdvice() + self.ignore('escape') + self.ignore('control') + self.ignore('control-up') + self.ignore('InputState-forward') + self.ignore('InputState-reverse') + self.ignore('InputState-turnLeft') + self.ignore('InputState-turnRight') + self.arrowVert = 0 + self.arrowHorz = 0 + NametagGlobals.setOnscreenChatForced(0) + taskMgr.remove('watchCraneControls') + self.__setMoveSound(None) + return + + def __displayCraneAdvice(self, task): + if self.craneAdviceLabel == None: + self.craneAdviceLabel = DirectLabel(text=TTLocalizer.CashbotCraneAdvice, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.69), scale=0.1) + return + + def __cleanupCraneAdvice(self): + if self.craneAdviceLabel: + self.craneAdviceLabel.destroy() + self.craneAdviceLabel = None + taskMgr.remove(self.craneAdviceName) + return + + def __displayMagnetAdvice(self, task): + if self.magnetAdviceLabel == None: + self.magnetAdviceLabel = DirectLabel(text=TTLocalizer.CashbotMagnetAdvice, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.55), scale=0.1) + return + + def __cleanupMagnetAdvice(self): + if self.magnetAdviceLabel: + self.magnetAdviceLabel.destroy() + self.magnetAdviceLabel = None + taskMgr.remove(self.magnetAdviceName) + return + + def __watchControls(self, task): + if self.arrowHorz or self.arrowVert: + self.__moveCraneArcHinge(self.arrowHorz, self.arrowVert) + else: + self.__setMoveSound(None) + return Task.cont + + def __exitCrane(self): + if self.closeButton: + self.closeButton.destroy() + self.closeButton = DirectLabel( + parent=base.a2dBottomRight, relief=None, + text=TTLocalizer.CashbotCraneLeaving, pos=(-0.25, 0, 0.125), + text_pos=(0, 0), text_scale=0.06, text_fg=VBase4(1, 1, 1, 1)) + self.__cleanupCraneAdvice() + self.__cleanupMagnetAdvice() + self.d_requestFree() + + def __incrementChangeSeq(self): + self.changeSeq = self.changeSeq + 1 & 255 + + def __controlPressed(self): + self.__cleanupMagnetAdvice() + self.__turnOnMagnet() + + def __controlReleased(self): + self.__turnOffMagnet() + + def __turnOnMagnet(self): + if not self.magnetOn: + self.__incrementChangeSeq() + self.magnetOn = 1 + if not self.heldObject: + self.__activateSniffer() + + def __turnOffMagnet(self): + if self.magnetOn: + self.magnetOn = 0 + self.__deactivateSniffer() + self.releaseObject() + + def __upArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupCraneAdvice() + if pressed: + self.arrowVert = 1 + elif self.arrowVert > 0: + self.arrowVert = 0 + + def __downArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupCraneAdvice() + if pressed: + self.arrowVert = -1 + elif self.arrowVert < 0: + self.arrowVert = 0 + + def __rightArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupCraneAdvice() + if pressed: + self.arrowHorz = 1 + elif self.arrowHorz > 0: + self.arrowHorz = 0 + + def __leftArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupCraneAdvice() + if pressed: + self.arrowHorz = -1 + elif self.arrowHorz < 0: + self.arrowHorz = 0 + + def __moveCraneArcHinge(self, xd, yd): + dt = globalClock.getDt() + h = self.arm.getH() - xd * self.rotateSpeed * dt + limitH = max(min(h, self.armMaxH), self.armMinH) + self.arm.setH(limitH) + y = self.crane.getY() + yd * self.slideSpeed * dt + limitY = max(min(y, self.craneMaxY), self.craneMinY) + atLimit = limitH != h or limitY != y + if atLimit: + now = globalClock.getFrameTime() + x = math.sin(now * 79) * 0.05 + z = math.sin(now * 70) * 0.02 + self.crane.setPos(x, limitY, z) + self.__setMoveSound(self.atLimitSfx) + else: + self.crane.setPos(0, limitY, 0) + self.__setMoveSound(self.craneMoveSfx) + + def __setMoveSound(self, sfx): + if sfx != self.moveSound: + if self.moveSound: + self.moveSound.stop() + self.moveSound = sfx + if self.moveSound: + base.playSfx(self.moveSound, looping=1, volume=0.5) + + def __activateSniffer(self): + if not self.snifferActivated: + self.sniffer.unstash() + base.cTrav.addCollider(self.sniffer, self.snifferHandler) + self.accept(self.snifferEvent, self.__sniffedSomething) + self.startFlicker() + self.snifferActivated = 1 + + def __deactivateSniffer(self): + if self.snifferActivated: + base.cTrav.removeCollider(self.sniffer) + self.sniffer.stash() + self.ignore(self.snifferEvent) + self.stopFlicker() + self.snifferActivated = 0 + + def startFlicker(self): + self.magnetSoundInterval.start() + self.lightning = [] + for i in xrange(4): + t = float(i) / 3.0 - 0.5 + l = self.boss.lightning.copyTo(self.gripper) + l.setScale(random.choice([1, -1]), 1, 5) + l.setZ(random.uniform(-5, -5.5)) + l.flattenLight() + l.setTwoSided(1) + l.setBillboardAxis() + l.setScale(random.uniform(0.5, 1.0)) + if t < 0: + l.setX(t - 0.7) + else: + l.setX(t + 0.7) + l.setR(-20 * t) + l.setP(random.uniform(-20, 20)) + self.lightning.append(l) + + taskMgr.add(self.__flickerLightning, self.flickerName) + + def stopFlicker(self): + self.magnetSoundInterval.finish() + self.magnetLoopSfx.stop() + taskMgr.remove(self.flickerName) + for l in self.lightning: + l.detachNode() + + self.lightning = None + return + + def __flickerLightning(self, task): + for l in self.lightning: + if random.random() < 0.5: + l.hide() + else: + l.show() + + return Task.cont + + def __sniffedSomething(self, entry): + np = entry.getIntoNodePath() + if np.hasNetTag('object'): + doId = int(np.getNetTag('object')) + else: + self.notify.warning("%s missing 'object' tag" % np) + return + self.notify.debug('__sniffedSomething %d' % doId) + obj = base.cr.doId2do.get(doId) + if obj and obj.state != 'LocalDropped' and (obj.state != 'Dropped' or obj.craneId != self.doId): + obj.d_requestGrab() + obj.demand('LocalGrabbed', localAvatar.doId, self.doId) + + def grabObject(self, obj): + if self.state == 'Off': + return + if self.heldObject != None: + self.releaseObject() + self.__deactivateSniffer() + obj.wrtReparentTo(self.gripper) + if obj.lerpInterval: + obj.lerpInterval.finish() + obj.lerpInterval = Parallel(obj.posInterval(ToontownGlobals.CashbotBossToMagnetTime, Point3(*obj.grabPos)), obj.quatInterval(ToontownGlobals.CashbotBossToMagnetTime, VBase3(obj.getH(), 0, 0)), obj.toMagnetSoundInterval) + obj.lerpInterval.start() + self.heldObject = obj + self.handler.setDynamicFrictionCoef(obj.craneFrictionCoef) + self.slideSpeed = obj.craneSlideSpeed + self.rotateSpeed = obj.craneRotateSpeed + if self.avId == localAvatar.doId and not self.magnetOn: + self.releaseObject() + return + + def dropObject(self, obj): + if obj.lerpInterval: + obj.lerpInterval.finish() + obj.wrtReparentTo(render) + obj.lerpInterval = Parallel(obj.quatInterval(ToontownGlobals.CashbotBossFromMagnetTime, VBase3(obj.getH(), 0, 0), blendType='easeOut')) + obj.lerpInterval.start() + p1 = self.bottomLink.node().getPhysicsObject() + v = render.getRelativeVector(self.bottomLink, p1.getVelocity()) + obj.physicsObject.setVelocity(v * 1.5) + if self.heldObject == obj: + self.heldObject = None + self.handler.setDynamicFrictionCoef(self.emptyFrictionCoef) + self.slideSpeed = self.emptySlideSpeed + self.rotateSpeed = self.emptyRotateSpeed + return + + def releaseObject(self): + if self.heldObject: + obj = self.heldObject + obj.d_requestDrop() + if obj.state == 'Grabbed': + obj.demand('LocalDropped', localAvatar.doId, self.doId) + + def __hitTrigger(self, event): + self.d_requestControl() + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + + def setIndex(self, index): + self.index = index + + def setState(self, state, avId): + if state == 'C': + self.demand('Controlled', avId) + elif state == 'F': + self.demand('Free') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def d_requestControl(self): + self.sendUpdate('requestControl') + + def d_requestFree(self): + self.sendUpdate('requestFree') + + def b_clearSmoothing(self): + self.d_clearSmoothing() + self.clearSmoothing() + + def d_clearSmoothing(self): + self.sendUpdate('clearSmoothing', [0]) + + def clearSmoothing(self, bogus = None): + self.armSmoother.clearPositions(1) + for smoother in self.linkSmoothers: + smoother.clearPositions(1) + + def reloadPosition(self): + self.armSmoother.clearPositions(0) + self.armSmoother.setPos(self.crane.getPos()) + self.armSmoother.setHpr(self.arm.getHpr()) + self.armSmoother.setPhonyTimestamp() + for linkNum in xrange(self.numLinks): + smoother = self.linkSmoothers[linkNum] + an, anp, cnp = self.activeLinks[linkNum] + smoother.clearPositions(0) + smoother.setPos(anp.getPos()) + smoother.setPhonyTimestamp() + + def doSmoothTask(self, task): + self.armSmoother.computeAndApplySmoothPosHpr(self.crane, self.arm) + for linkNum in xrange(self.numLinks): + smoother = self.linkSmoothers[linkNum] + anp = self.activeLinks[linkNum][1] + smoother.computeAndApplySmoothPos(anp) + + return Task.cont + + def startSmooth(self): + if not self.smoothStarted: + taskName = self.smoothName + taskMgr.remove(taskName) + self.reloadPosition() + taskMgr.add(self.doSmoothTask, taskName) + self.smoothStarted = 1 + + def stopSmooth(self): + if self.smoothStarted: + taskName = self.smoothName + taskMgr.remove(taskName) + self.forceToTruePosition() + self.smoothStarted = 0 + + def forceToTruePosition(self): + if self.armSmoother.getLatestPosition(): + self.armSmoother.applySmoothPos(self.crane) + self.armSmoother.applySmoothHpr(self.arm) + self.armSmoother.clearPositions(1) + for linkNum in xrange(self.numLinks): + smoother = self.linkSmoothers[linkNum] + an, anp, cnp = self.activeLinks[linkNum] + if smoother.getLatestPosition(): + smoother.applySmoothPos(anp) + smoother.clearPositions(1) + + def setCablePos(self, changeSeq, y, h, links, timestamp): + self.changeSeq = changeSeq + if self.smoothStarted: + if len(links) > self.numLinks: + self.notify.warning('Links passed in is greater than total number of links') + return + now = globalClock.getFrameTime() + local = globalClockDelta.networkToLocalTime(timestamp, now) + self.armSmoother.setY(y) + self.armSmoother.setH(h) + self.armSmoother.setTimestamp(local) + self.armSmoother.markPosition() + for linkNum in xrange(self.numLinks): + smoother = self.linkSmoothers[linkNum] + lp = links[linkNum] + smoother.setPos(*lp) + smoother.setTimestamp(local) + smoother.markPosition() + + else: + self.crane.setY(y) + self.arm.setH(h) + + def d_sendCablePos(self): + timestamp = globalClockDelta.getFrameNetworkTime() + links = [] + for linkNum in xrange(self.numLinks): + an, anp, cnp = self.activeLinks[linkNum] + p = anp.getPos() + links.append((p[0], p[1], p[2])) + + self.sendUpdate('setCablePos', [self.changeSeq, + self.crane.getY(), + self.arm.getH(), + links, + timestamp]) + + def stopPosHprBroadcast(self): + taskName = self.posHprBroadcastName + taskMgr.remove(taskName) + + def startPosHprBroadcast(self): + taskName = self.posHprBroadcastName + self.b_clearSmoothing() + self.d_sendCablePos() + taskMgr.remove(taskName) + taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) + + def __posHprBroadcast(self, task): + self.d_sendCablePos() + taskName = self.posHprBroadcastName + taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) + return Task.done + + def enterOff(self): + self.clearCable() + self.root.detachNode() + + def exitOff(self): + if self.boss: + self.setupCable() + self.root.reparentTo(render) + + def enterControlled(self, avId): + self.avId = avId + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.grabTrack = self.makeToonGrabInterval(toon) + if avId == localAvatar.doId: + self.boss.toCraneMode() + camera.reparentTo(self.hinge) + camera.setPosHpr(0, -20, -5, 0, -20, 0) + self.tube.stash() + localAvatar.setPosHpr(self.controls, 0, 0, 0, 0, 0, 0) + localAvatar.sendCurrentPosition() + self.__activatePhysics() + self.__enableControlInterface() + self.startPosHprBroadcast() + self.startShadow() + self.accept('exitCrane', self.__exitCrane) + else: + self.startSmooth() + toon.stopSmooth() + self.grabTrack = Sequence(self.grabTrack, Func(toon.startSmooth)) + self.grabTrack.start() + + def exitControlled(self): + self.ignore('exitCrane') + self.grabTrack.finish() + del self.grabTrack + if self.toon and not self.toon.isDisabled(): + self.toon.loop('neutral') + self.toon.startSmooth() + self.stopWatchJoystick() + self.stopPosHprBroadcast() + self.stopShadow() + self.stopSmooth() + if self.avId == localAvatar.doId: + self.__disableControlInterface() + self.__deactivatePhysics() + self.tube.unstash() + camera.reparentTo(base.localAvatar) + camera.setPos(base.localAvatar.cameraPositions[0][0]) + camera.setHpr(0, 0, 0) + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + if place.fsm.getCurrentState().getName() == 'crane': + place.setState('finalBattle') + self.boss.toFinalBattleMode() + self.__straightenCable() + + def enterFree(self): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + self.restoreScaleTrack = Sequence(Wait(6), self.getRestoreScaleInterval()) + self.restoreScaleTrack.start() + if self.avId == localAvatar.doId: + self.controlModel.setAlphaScale(0.3) + self.controlModel.setTransparency(1) + taskMgr.doMethodLater(5, self.__allowDetect, self.triggerName) + self.fadeTrack = Sequence(Func(self.controlModel.setTransparency, 1), self.controlModel.colorScaleInterval(0.2, VBase4(1, 1, 1, 0.3))) + self.fadeTrack.start() + else: + self.trigger.unstash() + self.accept(self.triggerEvent, self.__hitTrigger) + self.avId = 0 + return + + def __allowDetect(self, task): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = Sequence(self.controlModel.colorScaleInterval(0.2, VBase4(1, 1, 1, 1)), Func(self.controlModel.clearColorScale), Func(self.controlModel.clearTransparency)) + self.fadeTrack.start() + self.trigger.unstash() + self.accept(self.triggerEvent, self.__hitTrigger) + + def exitFree(self): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + self.restoreScaleTrack.pause() + del self.restoreScaleTrack + taskMgr.remove(self.triggerName) + self.controlModel.clearColorScale() + self.controlModel.clearTransparency() + self.trigger.stash() + self.ignore(self.triggerEvent) + return + + def enterMovie(self): + self.__activatePhysics() + + def exitMovie(self): + self.__deactivatePhysics() + self.__straightenCable() diff --git a/toontown/coghq/DistributedCashbotBossCraneAI.py b/toontown/coghq/DistributedCashbotBossCraneAI.py new file mode 100755 index 00000000..45ff12d9 --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossCraneAI.py @@ -0,0 +1,75 @@ +from panda3d.core import * +from direct.distributed import DistributedObjectAI +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from direct.fsm import FSM + +class DistributedCashbotBossCraneAI(DistributedObjectAI.DistributedObjectAI, FSM.FSM): + + def __init__(self, air, boss, index): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedCashbotBossCraneAI') + self.boss = boss + self.index = index + cn = CollisionNode('controls') + cs = CollisionSphere(0, -6, 0, 6) + cn.addSolid(cs) + self.goonShield = NodePath(cn) + self.goonShield.setPosHpr(*ToontownGlobals.CashbotBossCranePosHprs[self.index]) + self.avId = 0 + self.objectId = 0 + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def d_setState(self, state, avId): + self.sendUpdate('setState', [state, avId]) + + def requestControl(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.boss.involvedToons and self.avId == 0: + craneId = self.__getCraneId(avId) + if craneId == 0: + self.request('Controlled', avId) + + def requestFree(self): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + self.request('Free') + + def removeToon(self, avId): + if avId == self.avId: + self.request('Free') + + def __getCraneId(self, avId): + if self.boss and self.boss.cranes != None: + for crane in self.boss.cranes: + if crane.avId == avId: + return crane.doId + + return 0 + + def enterOff(self): + self.goonShield.detachNode() + + def exitOff(self): + self.goonShield.reparentTo(self.boss.scene) + + def enterControlled(self, avId): + self.avId = avId + self.d_setState('C', avId) + + def exitControlled(self): + if self.objectId: + obj = self.air.doId2do[self.objectId] + obj.request('Dropped', self.avId, self.doId) + + def enterFree(self): + self.avId = 0 + self.d_setState('F', 0) + + def exitFree(self): + pass diff --git a/toontown/coghq/DistributedCashbotBossObject.py b/toontown/coghq/DistributedCashbotBossObject.py new file mode 100755 index 00000000..027709c4 --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossObject.py @@ -0,0 +1,322 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedSmoothNode +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from direct.fsm import FSM +from direct.task import Task +smileyDoId = 1 + +class DistributedCashbotBossObject(DistributedSmoothNode.DistributedSmoothNode, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCashbotBossObject') + wantsWatchDrift = 1 + + def __init__(self, cr): + DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedCashbotBossObject') + self.boss = None + self.avId = 0 + self.craneId = 0 + self.cleanedUp = 0 + self.collisionNode = CollisionNode('object') + self.collisionNode.setIntoCollideMask(ToontownGlobals.PieBitmask | OTPGlobals.WallBitmask | ToontownGlobals.CashbotBossObjectBitmask | OTPGlobals.CameraBitmask) + self.collisionNode.setFromCollideMask(ToontownGlobals.PieBitmask | OTPGlobals.FloorBitmask) + self.collisionNodePath = NodePath(self.collisionNode) + self.physicsActivated = 0 + self.toMagnetSoundInterval = Sequence() + self.hitFloorSoundInterval = Sequence() + self.hitBossSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_safe_miss.ogg') + self.hitBossSoundInterval = SoundInterval(self.hitBossSfx) + self.touchedBossSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_sandbag.ogg') + self.touchedBossSoundInterval = SoundInterval(self.touchedBossSfx, duration=0.8) + self.lerpInterval = None + return + + def disable(self): + self.cleanup() + self.stopSmooth() + DistributedSmoothNode.DistributedSmoothNode.disable(self) + + def cleanup(self): + if self.cleanedUp: + return + else: + self.cleanedUp = 1 + self.demand('Off') + self.detachNode() + self.toMagnetSoundInterval.finish() + self.hitFloorSoundInterval.finish() + self.hitBossSoundInterval.finish() + self.touchedBossSoundInterval.finish() + del self.toMagnetSoundInterval + del self.hitFloorSoundInterval + del self.hitBossSoundInterval + del self.touchedBossSoundInterval + self.boss = None + return + + def setupPhysics(self, name): + an = ActorNode('%s-%s' % (name, self.doId)) + anp = NodePath(an) + if not self.isEmpty(): + self.reparentTo(anp) + NodePath.assign(self, anp) + self.physicsObject = an.getPhysicsObject() + self.setTag('object', str(self.doId)) + self.collisionNodePath.reparentTo(self) + self.handler = PhysicsCollisionHandler() + self.handler.addCollider(self.collisionNodePath, self) + self.collideName = self.uniqueName('collide') + self.handler.addInPattern(self.collideName + '-%in') + self.handler.addAgainPattern(self.collideName + '-%in') + self.watchDriftName = self.uniqueName('watchDrift') + + def activatePhysics(self): + if not self.physicsActivated: + self.boss.physicsMgr.attachPhysicalNode(self.node()) + base.cTrav.addCollider(self.collisionNodePath, self.handler) + self.physicsActivated = 1 + self.accept(self.collideName + '-floor', self.__hitFloor) + self.accept(self.collideName + '-goon', self.__hitGoon) + self.acceptOnce(self.collideName + '-headTarget', self.__hitBoss) + self.accept(self.collideName + '-dropPlane', self.__hitDropPlane) + + def deactivatePhysics(self): + if self.physicsActivated: + self.boss.physicsMgr.removePhysicalNode(self.node()) + base.cTrav.removeCollider(self.collisionNodePath) + self.physicsActivated = 0 + self.ignore(self.collideName + '-floor') + self.ignore(self.collideName + '-goon') + self.ignore(self.collideName + '-headTarget') + self.ignore(self.collideName + '-dropPlane') + + def hideShadows(self): + pass + + def showShadows(self): + pass + + def stashCollisions(self): + self.collisionNodePath.stash() + + def unstashCollisions(self): + self.collisionNodePath.unstash() + + def __hitFloor(self, entry): + if self.state == 'Dropped' or self.state == 'LocalDropped': + self.d_hitFloor() + self.demand('SlidingFloor', localAvatar.doId) + + def __hitGoon(self, entry): + if self.state == 'Dropped' or self.state == 'LocalDropped': + goonId = int(entry.getIntoNodePath().getNetTag('doId')) + goon = self.cr.doId2do.get(goonId) + if goon: + self.doHitGoon(goon) + + def doHitGoon(self, goon): + pass + + def __hitBoss(self, entry): + if (self.state == 'Dropped' or self.state == 'LocalDropped') and self.craneId != self.boss.doId: + vel = self.physicsObject.getVelocity() + vel = self.crane.root.getRelativeVector(render, vel) + vel.normalize() + impact = vel[1] + if impact >= self.getMinImpact(): + print 'hit! %s' % impact + self.hitBossSoundInterval.start() + self.doHitBoss(impact) + else: + self.touchedBossSoundInterval.start() + print '--not hard enough: %s' % impact + + def doHitBoss(self, impact): + self.d_hitBoss(impact) + + def __hitDropPlane(self, entry): + self.notify.info('%s fell out of the world.' % self.doId) + self.fellOut() + + def fellOut(self): + raise StandardError, 'fellOut unimplented' + + def getMinImpact(self): + return 0 + + def __watchDrift(self, task): + v = self.physicsObject.getVelocity() + if abs(v[0]) < 0.0001 and abs(v[1]) < 0.0001: + self.d_requestFree() + self.demand('Free') + return Task.cont + + def prepareGrab(self): + pass + + def prepareRelease(self): + pass + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + + def setObjectState(self, state, avId, craneId): + if state == 'G': + self.demand('Grabbed', avId, craneId) + elif state == 'D': + if self.state != 'Dropped': + self.demand('Dropped', avId, craneId) + elif state == 's': + if self.state != 'SlidingFloor': + self.demand('SlidingFloor', avId) + elif state == 'F': + self.demand('Free') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def d_requestGrab(self): + self.sendUpdate('requestGrab') + + def rejectGrab(self): + if self.state == 'LocalGrabbed': + self.demand('LocalDropped', self.avId, self.craneId) + + def d_requestDrop(self): + self.sendUpdate('requestDrop') + + def d_hitFloor(self): + self.sendUpdate('hitFloor') + + def d_requestFree(self): + self.sendUpdate('requestFree', [self.getX(), + self.getY(), + self.getZ(), + self.getH()]) + + def d_hitBoss(self, impact): + self.sendUpdate('hitBoss', [impact]) + + def defaultFilter(self, request, args): + if self.boss == None: + raise FSM.RequestDenied, request + return FSM.FSM.defaultFilter(self, request, args) + + def enterOff(self): + self.detachNode() + if self.lerpInterval: + self.lerpInterval.finish() + self.lerpInterval = None + return + + def exitOff(self): + self.reparentTo(render) + + def enterLocalGrabbed(self, avId, craneId): + self.avId = avId + self.craneId = craneId + self.crane = self.cr.doId2do.get(craneId) + self.hideShadows() + self.prepareGrab() + self.crane.grabObject(self) + + def exitLocalGrabbed(self): + if self.newState != 'Grabbed': + self.crane.dropObject(self) + self.prepareRelease() + del self.crane + self.showShadows() + + def enterGrabbed(self, avId, craneId): + if self.oldState == 'LocalGrabbed': + if craneId == self.craneId: + return + else: + self.crane.dropObject(self) + self.prepareRelease() + self.avId = avId + self.craneId = craneId + self.crane = self.cr.doId2do.get(craneId) + self.hideShadows() + self.prepareGrab() + self.crane.grabObject(self) + + def exitGrabbed(self): + self.crane.dropObject(self) + self.prepareRelease() + self.showShadows() + del self.crane + + def enterLocalDropped(self, avId, craneId): + self.avId = avId + self.craneId = craneId + self.crane = self.cr.doId2do.get(craneId) + self.activatePhysics() + self.startPosHprBroadcast() + self.hideShadows() + self.handler.setStaticFrictionCoef(0) + self.handler.setDynamicFrictionCoef(0) + + def exitLocalDropped(self): + if self.newState != 'SlidingFloor' and self.newState != 'Dropped': + self.deactivatePhysics() + self.stopPosHprBroadcast() + del self.crane + self.showShadows() + + def enterDropped(self, avId, craneId): + self.avId = avId + self.craneId = craneId + self.crane = self.cr.doId2do.get(craneId) + if self.avId == base.localAvatar.doId: + self.activatePhysics() + self.startPosHprBroadcast() + self.handler.setStaticFrictionCoef(0) + self.handler.setDynamicFrictionCoef(0) + else: + self.startSmooth() + self.hideShadows() + + def exitDropped(self): + if self.avId == base.localAvatar.doId: + if self.newState != 'SlidingFloor': + self.deactivatePhysics() + self.stopPosHprBroadcast() + else: + self.stopSmooth() + del self.crane + self.showShadows() + + def enterSlidingFloor(self, avId): + self.avId = avId + if self.lerpInterval: + self.lerpInterval.finish() + self.lerpInterval = None + if self.avId == base.localAvatar.doId: + self.activatePhysics() + self.startPosHprBroadcast() + self.handler.setStaticFrictionCoef(0.9) + self.handler.setDynamicFrictionCoef(0.5) + if self.wantsWatchDrift: + taskMgr.add(self.__watchDrift, self.watchDriftName) + else: + self.startSmooth() + self.hitFloorSoundInterval.start() + return + + def exitSlidingFloor(self): + if self.avId == base.localAvatar.doId: + taskMgr.remove(self.watchDriftName) + self.deactivatePhysics() + self.stopPosHprBroadcast() + else: + self.stopSmooth() + + def enterFree(self): + self.avId = 0 + self.craneId = 0 + + def exitFree(self): + pass diff --git a/toontown/coghq/DistributedCashbotBossObjectAI.py b/toontown/coghq/DistributedCashbotBossObjectAI.py new file mode 100755 index 00000000..1100ff6e --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossObjectAI.py @@ -0,0 +1,140 @@ +from panda3d.core import * +from direct.distributed import DistributedSmoothNodeAI +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from direct.fsm import FSM +from direct.task import Task + +class DistributedCashbotBossObjectAI(DistributedSmoothNodeAI.DistributedSmoothNodeAI, FSM.FSM): + wantsWatchDrift = 1 + + def __init__(self, air, boss): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedCashbotBossObjectAI') + self.boss = boss + self.reparentTo(self.boss.scene) + self.avId = 0 + self.craneId = 0 + + def cleanup(self): + self.detachNode() + self.stopWaitFree() + + def delete(self): + self.cleanup() + DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self) + + def startWaitFree(self, delayTime): + waitFreeEvent = self.uniqueName('waitFree') + taskMgr.remove(waitFreeEvent) + taskMgr.doMethodLater(delayTime, self.doFree, waitFreeEvent) + + def stopWaitFree(self): + waitFreeEvent = self.uniqueName('waitFree') + taskMgr.remove(waitFreeEvent) + + def doFree(self, task): + if not self.isDeleted(): + self.demand('Free') + p = self.getPos() + h = self.getH() + self.d_setPosHpr(p[0], p[1], 0, h, 0, 0) + return Task.done + + def getBossCogId(self): + return self.boss.doId + + def d_setObjectState(self, state, avId, craneId): + self.sendUpdate('setObjectState', [state, avId, craneId]) + + def requestGrab(self): + avId = self.air.getAvatarIdFromSender() + if self.state != 'Grabbed' and self.state != 'Off': + craneId, objectId = self.__getCraneAndObject(avId) + if craneId != 0 and objectId == 0: + self.demand('Grabbed', avId, craneId) + return + self.sendUpdateToAvatarId(avId, 'rejectGrab', []) + + def requestDrop(self): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId and self.state == 'Grabbed': + craneId, objectId = self.__getCraneAndObject(avId) + if craneId != 0 and objectId == self.doId: + self.demand('Dropped', avId, craneId) + + def hitFloor(self): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId and self.state == 'Dropped': + self.demand('SlidingFloor', avId) + + def requestFree(self, x, y, z, h): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + self.setPosHpr(x, y, 0, h, 0, 0) + self.demand('WaitFree') + + def hitBoss(self, impact): + pass + + def removeToon(self, avId): + if avId == self.avId: + self.doFree(None) + return + + def __getCraneAndObject(self, avId): + if self.boss and self.boss.cranes != None: + for crane in self.boss.cranes: + if crane.avId == avId: + return (crane.doId, crane.objectId) + + return (0, 0) + + def __setCraneObject(self, craneId, objectId): + if self.air: + crane = self.air.doId2do.get(craneId) + if crane: + crane.objectId = objectId + + def enterGrabbed(self, avId, craneId): + self.avId = avId + self.craneId = craneId + self.__setCraneObject(self.craneId, self.doId) + self.d_setObjectState('G', avId, craneId) + + def exitGrabbed(self): + self.__setCraneObject(self.craneId, 0) + + def enterDropped(self, avId, craneId): + self.avId = avId + self.craneId = craneId + self.d_setObjectState('D', avId, craneId) + self.startWaitFree(10) + + def exitDropped(self): + self.stopWaitFree() + + def enterSlidingFloor(self, avId): + self.avId = avId + self.d_setObjectState('s', avId, 0) + if self.wantsWatchDrift: + self.startWaitFree(5) + + def exitSlidingFloor(self): + self.stopWaitFree() + + def enterWaitFree(self): + self.avId = 0 + self.craneId = 0 + self.startWaitFree(1) + + def exitWaitFree(self): + self.stopWaitFree() + + def enterFree(self): + self.avId = 0 + self.craneId = 0 + self.d_setObjectState('F', 0, 0) + + def exitFree(self): + pass diff --git a/toontown/coghq/DistributedCashbotBossSafe.py b/toontown/coghq/DistributedCashbotBossSafe.py new file mode 100755 index 00000000..727412c9 --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossSafe.py @@ -0,0 +1,91 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +import DistributedCashbotBossObject + +class DistributedCashbotBossSafe(DistributedCashbotBossObject.DistributedCashbotBossObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCashbotBossSafe') + grabPos = (0, 0, -8.2) + craneFrictionCoef = 0.2 + craneSlideSpeed = 11 + craneRotateSpeed = 16 + wantsWatchDrift = 0 + + def __init__(self, cr): + DistributedCashbotBossObject.DistributedCashbotBossObject.__init__(self, cr) + NodePath.__init__(self, 'object') + self.index = None + self.flyToMagnetSfx = loader.loadSfx('phase_5/audio/sfx/TL_rake_throw_only.ogg') + self.hitMagnetSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_safe.ogg') + self.toMagnetSoundInterval = Parallel(SoundInterval(self.flyToMagnetSfx, duration=ToontownGlobals.CashbotBossToMagnetTime, node=self), Sequence(Wait(ToontownGlobals.CashbotBossToMagnetTime - 0.02), SoundInterval(self.hitMagnetSfx, duration=1.0, node=self))) + self.hitFloorSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_bigweight_miss.ogg') + self.hitFloorSoundInterval = SoundInterval(self.hitFloorSfx, node=self) + return + + def announceGenerate(self): + DistributedCashbotBossObject.DistributedCashbotBossObject.announceGenerate(self) + self.name = 'safe-%s' % self.doId + self.setName(self.name) + self.boss.safe.copyTo(self) + self.shadow = self.find('**/shadow') + self.collisionNode.setName('safe') + cs = CollisionSphere(0, 0, 4, 4) + self.collisionNode.addSolid(cs) + if self.index == 0: + self.collisionNode.setIntoCollideMask(ToontownGlobals.PieBitmask | OTPGlobals.WallBitmask) + self.collisionNode.setFromCollideMask(ToontownGlobals.PieBitmask) + self.boss.safes[self.index] = self + self.setupPhysics('safe') + self.resetToInitialPosition() + + def disable(self): + del self.boss.safes[self.index] + DistributedCashbotBossObject.DistributedCashbotBossObject.disable(self) + + def hideShadows(self): + self.shadow.hide() + + def showShadows(self): + self.shadow.show() + + def getMinImpact(self): + if self.boss.heldObject: + return ToontownGlobals.CashbotBossSafeKnockImpact + else: + return ToontownGlobals.CashbotBossSafeNewImpact + + def doHitGoon(self, goon): + goon.b_destroyGoon() + + def resetToInitialPosition(self): + posHpr = ToontownGlobals.CashbotBossSafePosHprs[self.index] + self.setPosHpr(*posHpr) + self.physicsObject.setVelocity(0, 0, 0) + + def fellOut(self): + self.deactivatePhysics() + self.d_requestInitial() + + def setIndex(self, index): + self.index = index + + def setObjectState(self, state, avId, craneId): + if state == 'I': + self.demand('Initial') + else: + DistributedCashbotBossObject.DistributedCashbotBossObject.setObjectState(self, state, avId, craneId) + + def d_requestInitial(self): + self.sendUpdate('requestInitial') + + def enterInitial(self): + self.resetToInitialPosition() + self.showShadows() + if self.index == 0: + self.stash() + + def exitInitial(self): + if self.index == 0: + self.unstash() diff --git a/toontown/coghq/DistributedCashbotBossSafeAI.py b/toontown/coghq/DistributedCashbotBossSafeAI.py new file mode 100755 index 00000000..91d1e9d4 --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossSafeAI.py @@ -0,0 +1,71 @@ +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +import DistributedCashbotBossObjectAI + +class DistributedCashbotBossSafeAI(DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI): + wantsWatchDrift = 0 + + def __init__(self, air, boss, index): + DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.__init__(self, air, boss) + self.index = index + self.avoidHelmet = 0 + cn = CollisionNode('sphere') + cs = CollisionSphere(0, 0, 0, 6) + cn.addSolid(cs) + self.attachNewNode(cn) + + def resetToInitialPosition(self): + posHpr = ToontownGlobals.CashbotBossSafePosHprs[self.index] + self.setPosHpr(*posHpr) + + def getIndex(self): + return self.index + + def hitBoss(self, impact): + avId = self.air.getAvatarIdFromSender() + self.validate(avId, impact <= 1.0, 'invalid hitBoss impact %s' % impact) + if avId not in self.boss.involvedToons: + return + if self.state != 'Dropped' and self.state != 'Grabbed': + return + if self.avoidHelmet or self == self.boss.heldObject: + return + if self.boss.heldObject == None: + if self.boss.attackCode == ToontownGlobals.BossCogDizzy: + damage = int(impact * 50) + self.boss.recordHit(max(damage, 2)) + elif self.boss.acceptHelmetFrom(avId): + self.demand('Grabbed', self.boss.doId, self.boss.doId) + self.boss.heldObject = self + elif impact >= ToontownGlobals.CashbotBossSafeKnockImpact: + self.boss.heldObject.demand('Dropped', avId, self.boss.doId) + self.boss.heldObject.avoidHelmet = 1 + self.boss.heldObject = None + self.avoidHelmet = 1 + self.boss.waitForNextHelmet() + return + + def requestInitial(self): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + self.demand('Initial') + + def enterGrabbed(self, avId, craneId): + DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.enterGrabbed(self, avId, craneId) + self.avoidHelmet = 0 + + def enterInitial(self): + self.avoidHelmet = 0 + self.resetToInitialPosition() + if self.index == 0: + self.stash() + self.d_setObjectState('I', 0, 0) + + def exitInitial(self): + if self.index == 0: + self.unstash() + + def enterFree(self): + DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.enterFree(self) + self.avoidHelmet = 0 diff --git a/toontown/coghq/DistributedCashbotBossTreasure.py b/toontown/coghq/DistributedCashbotBossTreasure.py new file mode 100755 index 00000000..aafab8dc --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossTreasure.py @@ -0,0 +1,13 @@ +from toontown.safezone import DistributedTreasure +from toontown.toonbase import ToontownGlobals +from direct.interval.IntervalGlobal import * +from pandac.PandaModules import Point3 +Models = {ToontownGlobals.ToontownCentral: 'phase_4/models/props/icecream', + ToontownGlobals.DonaldsDock: 'phase_6/models/props/starfish_treasure', + ToontownGlobals.TheBrrrgh: 'phase_8/models/props/snowflake_treasure', + ToontownGlobals.MinniesMelodyland: 'phase_6/models/props/music_treasure', + ToontownGlobals.DaisyGardens: 'phase_8/models/props/flower_treasure', + ToontownGlobals.DonaldsDreamland: 'phase_8/models/props/zzz_treasure'} + +class DistributedCashbotBossTreasure(DistributedTreasure.DistributedTreasure): + pass # TBD diff --git a/toontown/coghq/DistributedCashbotBossTreasureAI.py b/toontown/coghq/DistributedCashbotBossTreasureAI.py new file mode 100755 index 00000000..238b19ce --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossTreasureAI.py @@ -0,0 +1,58 @@ +from toontown.safezone import DistributedTreasureAI +from toontown.safezone import TreasureGlobals + +class DistributedCashbotBossTreasureAI(DistributedTreasureAI.DistributedTreasureAI): + + def __init__(self, air, boss, goon, style, fx, fy, fz): + pos = goon.getPos() + type = TreasureGlobals.SafeZoneTreasureSpawns[style][0] + DistributedTreasureAI.DistributedTreasureAI.__init__(self, air, boss, type, pos[0], pos[1], 0) + self.goonId = goon.doId + self.style = style + self.finalPosition = (fx, fy, fz) + + def validAvatar(self, av): + if av.getHp() < av.getMaxHp() and av.getHp() > 0: + av.toonUp(self.healAmount) + return True + else: + return False + + def getGoonId(self): + return self.goonId + + def setGoonId(self, goonId): + self.goonId = goonId + + def b_setGoonId(self, goonId): + self.setGoonId(goonId) + self.d_setGoonId(goonId) + + def d_setGoonId(self, goonId): + self.sendUpdate('setGoonId', [goonId]) + + def getStyle(self): + return self.style + + def setStyle(self, hoodId): + self.style = hoodId + + def b_setStyle(self, hoodId): + self.setStyle(hoodId) + self.d_setStyle(hoodId) + + def d_setStyle(self, hoodId): + self.sendUpdate('setStyle', [hoodId]) + + def getFinalPosition(self): + return self.finalPosition + + def setFinalPosition(self, x, y, z): + self.finalPosition = (x, y, z) + + def b_setFinalPosition(self, x, y, z): + self.setFinalPosition(x, y, z) + self.d_setFinalPosition(x, y, z) + + def d_setFinalPosition(self, x, y, z): + self.sendUpdate('setFinalPosition', [x, y, z]) diff --git a/toontown/coghq/DistributedCogHQDoor.py b/toontown/coghq/DistributedCogHQDoor.py new file mode 100755 index 00000000..76fd2205 --- /dev/null +++ b/toontown/coghq/DistributedCogHQDoor.py @@ -0,0 +1,91 @@ +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from toontown.building import DistributedDoor +from toontown.hood import ZoneUtil +from toontown.building import FADoorCodes +from toontown.building import DoorTypes +from toontown.toonbase import TTLocalizer + +class DistributedCogHQDoor(DistributedDoor.DistributedDoor): + + def __init__(self, cr): + DistributedDoor.DistributedDoor.__init__(self, cr) + self.openSfx = base.loadSfx('phase_9/audio/sfx/CHQ_door_open.ogg') + self.closeSfx = base.loadSfx('phase_9/audio/sfx/CHQ_door_close.ogg') + + def wantsNametag(self): + return 0 + + def getRequestStatus(self): + zoneId = self.otherZoneId + request = {'loader': ZoneUtil.getBranchLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'doorIn', + 'hoodId': ZoneUtil.getHoodId(zoneId), + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1, + 'allowRedirect': 0, + 'doorDoId': self.otherDoId} + return request + + def enterClosing(self, ts): + doorFrameHoleRight = self.findDoorNode('doorFrameHoleRight') + if doorFrameHoleRight.isEmpty(): + self.notify.warning('enterClosing(): did not find doorFrameHoleRight') + return + rightDoor = self.findDoorNode('rightDoor') + if rightDoor.isEmpty(): + self.notify.warning('enterClosing(): did not find rightDoor') + return + otherNP = self.getDoorNodePath() + trackName = 'doorClose-%d' % self.doId + if self.rightSwing: + h = 100 + else: + h = -100 + self.finishDoorTrack() + self.doorTrack = Parallel(Sequence(LerpHprInterval(nodePath=rightDoor, duration=1.0, hpr=VBase3(0, 0, 0), startHpr=VBase3(h, 0, 0), other=otherNP, blendType='easeInOut'), Func(doorFrameHoleRight.hide), Func(self.hideIfHasFlat, rightDoor)), Sequence(Wait(0.5), SoundInterval(self.closeSfx, node=rightDoor)), name=trackName) + self.doorTrack.start(ts) + if hasattr(self, 'done'): + request = self.getRequestStatus() + messenger.send('doorDoneEvent', [request]) + + def exitDoorEnterClosing(self, ts): + doorFrameHoleLeft = self.findDoorNode('doorFrameHoleLeft') + if doorFrameHoleLeft.isEmpty(): + self.notify.warning('enterOpening(): did not find flatDoors') + return + if ZoneUtil.isInterior(self.zoneId): + doorFrameHoleLeft.setColor(1.0, 1.0, 1.0, 1.0) + if self.leftSwing: + h = -100 + else: + h = 100 + leftDoor = self.findDoorNode('leftDoor') + if not leftDoor.isEmpty(): + otherNP = self.getDoorNodePath() + trackName = 'doorExitTrack-%d' % self.doId + self.doorExitTrack = Parallel(Sequence(LerpHprInterval(nodePath=leftDoor, duration=1.0, hpr=VBase3(0, 0, 0), startHpr=VBase3(h, 0, 0), other=otherNP, blendType='easeInOut'), Func(doorFrameHoleLeft.hide), Func(self.hideIfHasFlat, leftDoor)), Sequence(Wait(0.5), SoundInterval(self.closeSfx, node=leftDoor)), name=trackName) + self.doorExitTrack.start(ts) + + def setZoneIdAndBlock(self, zoneId, block): + self.zoneId = zoneId + self.block = block + canonicalZoneId = ZoneUtil.getCanonicalZoneId(zoneId) + if canonicalZoneId in (ToontownGlobals.BossbotHQ, ToontownGlobals.BossbotLobby): + self.doorX = 1.0 + + def enterDoor(self): + messenger.send('DistributedDoor_doorTrigger') + self.sendUpdate('requestEnter') + + def doorTrigger(self, args = None): + if localAvatar.hasActiveBoardingGroup(): + rejectText = TTLocalizer.BoardingCannotLeaveZone + localAvatar.boardingParty.showMe(rejectText) + return + DistributedDoor.DistributedDoor.doorTrigger(self, args) diff --git a/toontown/coghq/DistributedCogHQDoorAI.py b/toontown/coghq/DistributedCogHQDoorAI.py new file mode 100755 index 00000000..6e5eb16e --- /dev/null +++ b/toontown/coghq/DistributedCogHQDoorAI.py @@ -0,0 +1,52 @@ +from otp.ai.AIBaseGlobal import * +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from toontown.building import DistributedDoorAI +from direct.fsm import State +from toontown.toonbase import ToontownGlobals +import CogDisguiseGlobals +from toontown.building import FADoorCodes +from toontown.building import DoorTypes + +class DistributedCogHQDoorAI(DistributedDoorAI.DistributedDoorAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogHQDoorAI') + + def __init__(self, air, blockNumber, doorType, destinationZone, doorIndex = 0, lockValue = FADoorCodes.SB_DISGUISE_INCOMPLETE, swing = 3): + DistributedDoorAI.DistributedDoorAI.__init__(self, air, blockNumber, doorType, doorIndex, lockValue, swing) + self.destinationZone = destinationZone + + def requestEnter(self): + avatarID = self.air.getAvatarIdFromSender() + dept = ToontownGlobals.cogHQZoneId2deptIndex(self.destinationZone) + av = self.air.doId2do.get(avatarID) + if av: + if self.doorType == DoorTypes.EXT_COGHQ and self.isLockedDoor(): + parts = av.getCogParts() + if CogDisguiseGlobals.isSuitComplete(parts, dept): + allowed = 1 + else: + allowed = 0 + else: + allowed = 1 + if not allowed: + self.sendReject(avatarID, self.isLockedDoor()) + else: + self.enqueueAvatarIdEnter(avatarID) + self.sendUpdateToAvatarId(avatarID, 'setOtherZoneIdAndDoId', [self.destinationZone, self.otherDoor.getDoId()]) + + def requestExit(self): + avatarID = self.air.getAvatarIdFromSender() + if avatarID in self.avatarsWhoAreEntering: + del self.avatarsWhoAreEntering[avatarID] + if avatarID not in self.avatarsWhoAreExiting: + dept = ToontownGlobals.cogHQZoneId2deptIndex(self.destinationZone) + self.avatarsWhoAreExiting[avatarID] = 1 + self.sendUpdate('avatarExit', [avatarID]) + self.openDoor(self.exitDoorFSM) + if self.lockedDoor: + av = self.air.doId2do[avatarID] + if self.doorType == DoorTypes.EXT_COGHQ: + av.b_setCogIndex(-1) + else: + av.b_setCogIndex(dept) diff --git a/toontown/coghq/DistributedCogHQExteriorDoor.py b/toontown/coghq/DistributedCogHQExteriorDoor.py new file mode 100644 index 00000000..d70d2ce1 --- /dev/null +++ b/toontown/coghq/DistributedCogHQExteriorDoor.py @@ -0,0 +1,25 @@ +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +import DistributedCogHQDoor +from toontown.hood import ZoneUtil +from BossLobbyGui import BossLobbyGui + +class DistributedCogHQExteriorDoor(DistributedCogHQDoor.DistributedCogHQDoor): + + def __init__(self, cr): + DistributedCogHQDoor.DistributedCogHQDoor.__init__(self, cr) + self.lobbyGui = None + + def selectLobby(self, avId): + print("********\nCreating Lobby GUI...\n********") + self.lobbyGui = BossLobbyGui(self.sendConfirmation, avId) + self.lobbyGui.loadFrame(0) + + def sendConfirmation(self, avId, status): + self.lobbyGui.destroy() + self.lobbyGui = None + print("********\nGUI Complete.\nSending Confirmation...\n********") + self.sendUpdate('confirmEntrance', [avId, status]) diff --git a/toontown/coghq/DistributedCogHQExteriorDoorAI.py b/toontown/coghq/DistributedCogHQExteriorDoorAI.py new file mode 100644 index 00000000..ee410e7c --- /dev/null +++ b/toontown/coghq/DistributedCogHQExteriorDoorAI.py @@ -0,0 +1,44 @@ +from otp.ai.AIBaseGlobal import * +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +import DistributedCogHQDoorAI +from direct.fsm import State +from toontown.toonbase import ToontownGlobals +import CogDisguiseGlobals +from toontown.building import FADoorCodes +from toontown.building import DoorTypes + +class DistributedCogHQExteriorDoorAI(DistributedCogHQDoorAI.DistributedCogHQDoorAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogHQExteriorDoorAI') + + def __init__(self, air, blockNumber, doorType, destinationZone, doorIndex = 0, lockValue = FADoorCodes.SB_DISGUISE_INCOMPLETE, swing = 3): + DistributedCogHQDoorAI.DistributedCogHQDoorAI.__init__(self, air, blockNumber, doorType, destinationZone, doorIndex, lockValue, swing) + + def requestEnter(self): + avId = self.air.getAvatarIdFromSender() + dept = ToontownGlobals.cogHQZoneId2deptIndex(self.destinationZone) + av = self.air.doId2do.get(avId) + if av: + if self.doorType == DoorTypes.EXT_COGHQ and self.isLockedDoor(): + parts = av.getCogParts() + if CogDisguiseGlobals.isSuitComplete(parts, dept): + allowed = 1 + else: + allowed = 0 + else: + allowed = 1 + if not allowed: + self.sendReject(avId, self.isLockedDoor()) + else: + print("********\nRequesting Lobby GUI...\n********") + self.sendUpdate('selectLobby', [avId]) + + def confirmEntrance(self, avId, status): + if status: + print("********\nAvatar Heading to Lobby...\n********") + self.enqueueAvatarIdEnter(avId) + self.sendUpdateToAvatarId(avId, 'setOtherZoneIdAndDoId', [self.destinationZone, self.otherDoor.getDoId()]) + else: + print("********\nAvatar Canceled Entrance.\n********") + self.sendReject(avId, 0) diff --git a/toontown/coghq/DistributedCogKart.py b/toontown/coghq/DistributedCogKart.py new file mode 100755 index 00000000..f20c1dc5 --- /dev/null +++ b/toontown/coghq/DistributedCogKart.py @@ -0,0 +1,369 @@ +import math +from pandac.PandaModules import CollisionSphere, CollisionNode, Vec3, Point3, deg2Rad +from direct.interval.IntervalGlobal import Sequence, Func, Parallel, ActorInterval, Wait, Parallel, LerpHprInterval, ProjectileInterval, LerpPosInterval +from direct.directnotify import DirectNotifyGlobal +from toontown.building import ElevatorConstants +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.safezone import DistributedGolfKart +from toontown.building import DistributedElevatorExt +from toontown.building import ElevatorConstants +from toontown.distributed import DelayDelete +from direct.showbase import PythonUtil +from toontown.building import BoardingGroupShow + +class DistributedCogKart(DistributedElevatorExt.DistributedElevatorExt): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogKart') + JumpOutOffsets = ((6.5, -2, -0.025), + (-6.5, -2, -0.025), + (3.75, 5, -0.025), + (-3.75, 5, -0.025)) + + def __init__(self, cr): + DistributedElevatorExt.DistributedElevatorExt.__init__(self, cr) + self.type = ElevatorConstants.ELEVATOR_COUNTRY_CLUB + self.kartModelPath = 'phase_12/models/bossbotHQ/Coggolf_cart3.bam' + self.leftDoor = None + self.rightDoor = None + self.fillSlotTrack = None + return + + def generate(self): + DistributedElevatorExt.DistributedElevatorExt.generate(self) + self.loader = self.cr.playGame.hood.loader + if self.loader: + self.notify.debug('Loader has been loaded') + self.notify.debug(str(self.loader)) + else: + self.notify.debug('Loader has not been loaded') + self.golfKart = render.attachNewNode('golfKartNode') + self.kart = loader.loadModel(self.kartModelPath) + self.kart.setPos(0, 0, 0) + self.kart.setScale(1) + self.kart.reparentTo(self.golfKart) + self.golfKart.reparentTo(self.loader.geom) + self.wheels = self.kart.findAllMatches('**/wheelNode*') + self.numWheels = self.wheels.getNumPaths() + + def announceGenerate(self): + DistributedElevatorExt.DistributedElevatorExt.announceGenerate(self) + angle = self.startingHpr[0] + angle -= 90 + radAngle = deg2Rad(angle) + unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0) + unitVec *= 45.0 + self.endPos = self.startingPos + unitVec + self.endPos.setZ(0.5) + dist = Vec3(self.endPos - self.enteringPos).length() + wheelAngle = dist / (4.8 * 1.4 * math.pi) * 360 + self.kartEnterAnimateInterval = Parallel(LerpHprInterval(self.wheels[0], 5.0, Vec3(self.wheels[0].getH(), wheelAngle, self.wheels[0].getR())), LerpHprInterval(self.wheels[1], 5.0, Vec3(self.wheels[1].getH(), wheelAngle, self.wheels[1].getR())), LerpHprInterval(self.wheels[2], 5.0, Vec3(self.wheels[2].getH(), wheelAngle, self.wheels[2].getR())), LerpHprInterval(self.wheels[3], 5.0, Vec3(self.wheels[3].getH(), wheelAngle, self.wheels[3].getR())), name='CogKartAnimate') + trolleyExitTrack1 = Parallel(LerpPosInterval(self.golfKart, 5.0, self.endPos), self.kartEnterAnimateInterval, name='CogKartExitTrack') + self.trolleyExitTrack = Sequence(trolleyExitTrack1) + self.trolleyEnterTrack = Sequence(LerpPosInterval(self.golfKart, 5.0, self.startingPos, startPos=self.enteringPos)) + self.closeDoors = Sequence(self.trolleyExitTrack, Func(self.onDoorCloseFinish)) + self.openDoors = Sequence(self.trolleyEnterTrack) + + def delete(self): + DistributedElevatorExt.DistributedElevatorExt.delete(self) + if hasattr(self, 'elevatorFSM'): + del self.elevatorFSM + + def setBldgDoId(self, bldgDoId): + self.bldg = None + self.setupElevatorKart() + return + + def setupElevatorKart(self): + collisionRadius = ElevatorConstants.ElevatorData[self.type]['collRadius'] + self.elevatorSphere = CollisionSphere(0, 0, 0, collisionRadius) + self.elevatorSphere.setTangible(1) + self.elevatorSphereNode = CollisionNode(self.uniqueName('elevatorSphere')) + self.elevatorSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.elevatorSphereNode.addSolid(self.elevatorSphere) + self.elevatorSphereNodePath = self.getElevatorModel().attachNewNode(self.elevatorSphereNode) + self.elevatorSphereNodePath.hide() + self.elevatorSphereNodePath.reparentTo(self.getElevatorModel()) + self.elevatorSphereNodePath.stash() + self.boardedAvIds = {} + self.finishSetup() + + def setColor(self, r, g, b): + pass + + def getElevatorModel(self): + return self.golfKart + + def enterWaitEmpty(self, ts): + DistributedElevatorExt.DistributedElevatorExt.enterWaitEmpty(self, ts) + + def exitWaitEmpty(self): + DistributedElevatorExt.DistributedElevatorExt.exitWaitEmpty(self) + + def forceDoorsOpen(self): + pass + + def forceDoorsClosed(self): + pass + + def setPosHpr(self, x, y, z, h, p, r): + self.startingPos = Vec3(x, y, z) + self.enteringPos = Vec3(x, y, z - 10) + self.startingHpr = Vec3(h, 0, 0) + self.golfKart.setPosHpr(x, y, z, h, 0, 0) + + def enterClosing(self, ts): + if self.localToonOnBoard: + elevator = self.getPlaceElevator() + if elevator: + elevator.fsm.request('elevatorClosing') + self.closeDoors.start(ts) + + def enterClosed(self, ts): + self.forceDoorsClosed() + self.kartDoorsClosed(self.getZoneId()) + + def kartDoorsClosed(self, zoneId): + if self.localToonOnBoard: + hoodId = ZoneUtil.getHoodId(zoneId) + doneStatus = {'loader': 'suitInterior', + 'where': 'suitInterior', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None} + elevator = self.elevatorFSM + del self.elevatorFSM + elevator.signalDone(doneStatus) + return + + def setCountryClubInteriorZone(self, zoneId): + if self.localToonOnBoard: + hoodId = self.cr.playGame.hood.hoodId + countryClubId = self.countryClubId + if bboard.has('countryClubIdOverride'): + countryClubId = bboard.get('countryClubIdOverride') + doneStatus = {'loader': 'cogHQLoader', + 'where': 'countryClubInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'countryClubId': self.countryClubId, + 'hoodId': hoodId} + self.cr.playGame.getPlace().elevator.signalDone(doneStatus) + + def setCountryClubInteriorZoneForce(self, zoneId): + place = self.cr.playGame.getPlace() + if place: + place.fsm.request('elevator', [self]) + hoodId = self.cr.playGame.hood.hoodId + countryClubId = self.countryClubId + if bboard.has('countryClubIdOverride'): + countryClubId = bboard.get('countryClubIdOverride') + doneStatus = {'loader': 'cogHQLoader', + 'where': 'countryClubInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'countryClubId': self.countryClubId, + 'hoodId': hoodId} + if hasattr(place, 'elevator') and place.elevator: + place.elevator.signalDone(doneStatus) + else: + self.notify.warning("setMintInteriorZoneForce: Couldn't find playGame.getPlace().elevator, zoneId: %s" % zoneId) + else: + self.notify.warning("setCountryClubInteriorZoneForce: Couldn't find playGame.getPlace(), zoneId: %s" % zoneId) + + def setCountryClubId(self, countryClubId): + self.countryClubId = countryClubId + + def getZoneId(self): + return 0 + + def fillSlot(self, index, avId, wantBoardingShow = 0): + self.notify.debug('%s.fillSlot(%s, %s, ... %s)' % (self.doId, + index, + avId, + globalClock.getRealTime())) + request = self.toonRequests.get(index) + if request: + self.cr.relatedObjectMgr.abortRequest(request) + del self.toonRequests[index] + if avId == 0: + pass + elif avId not in self.cr.doId2do: + func = PythonUtil.Functor(self.gotToon, index, avId) + self.toonRequests[index] = self.cr.relatedObjectMgr.requestObjects([avId], allCallback=func) + elif not self.isSetup: + self.deferredSlots.append((index, avId, wantBoardingShow)) + else: + if avId == base.localAvatar.getDoId(): + place = base.cr.playGame.getPlace() + if not place: + return + elevator = self.getPlaceElevator() + if elevator == None: + place.fsm.request('elevator') + elevator = self.getPlaceElevator() + if not elevator: + return + self.localToonOnBoard = 1 + if hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty: + localAvatar.boardingParty.forceCleanupInviteePanel() + localAvatar.boardingParty.forceCleanupInviterPanels() + if hasattr(base.localAvatar, 'elevatorNotifier'): + base.localAvatar.elevatorNotifier.cleanup() + cameraTrack = Sequence() + cameraTrack.append(Func(elevator.fsm.request, 'boarding', [self.getElevatorModel()])) + cameraTrack.append(Func(elevator.fsm.request, 'boarded')) + toon = self.cr.doId2do[avId] + toon.stopSmooth() + toon.wrtReparentTo(self.golfKart) + sitStartDuration = toon.getDuration('sit-start') + jumpTrack = self.generateToonJumpTrack(toon, index) + track = Sequence(jumpTrack, Func(toon.setAnimState, 'Sit', 1.0), Func(self.clearToonTrack, avId), name=toon.uniqueName('fillElevator'), autoPause=1) + if wantBoardingShow: + boardingTrack, boardingTrackType = self.getBoardingTrack(toon, index, True) + track = Sequence(boardingTrack, track) + if avId == base.localAvatar.getDoId(): + cameraWaitTime = 2.5 + if boardingTrackType == BoardingGroupShow.TRACK_TYPE_RUN: + cameraWaitTime = 0.5 + cameraTrack = Sequence(Wait(cameraWaitTime), cameraTrack) + if self.canHideBoardingQuitBtn(avId): + track = Sequence(Func(localAvatar.boardingParty.groupPanel.disableQuitButton), track) + if avId == base.localAvatar.getDoId(): + track = Parallel(cameraTrack, track) + track.delayDelete = DelayDelete.DelayDelete(toon, 'CogKart.fillSlot') + self.storeToonTrack(avId, track) + track.start() + self.fillSlotTrack = track + self.boardedAvIds[avId] = None + return + + def generateToonJumpTrack(self, av, seatIndex): + av.pose('sit', 47) + hipOffset = av.getHipsParts()[2].getPos(av) + + def getToonJumpTrack(av, seatIndex): + + def getJumpDest(av = av, node = self.golfKart): + dest = Point3(0, 0, 0) + if hasattr(self, 'golfKart') and self.golfKart: + dest = Vec3(self.golfKart.getPos(av.getParent())) + seatNode = self.golfKart.find('**/seat' + str(seatIndex + 1)) + dest += seatNode.getPos(self.golfKart) + dna = av.getStyle() + dest -= hipOffset + if seatIndex < 2: + dest.setY(dest.getY() + 2 * hipOffset.getY()) + dest.setZ(dest.getZ() + 0.1) + else: + self.notify.warning('getJumpDestinvalid golfKart, returning (0,0,0)') + return dest + + def getJumpHpr(av = av, node = self.golfKart): + hpr = Point3(0, 0, 0) + if hasattr(self, 'golfKart') and self.golfKart: + hpr = self.golfKart.getHpr(av.getParent()) + if seatIndex < 2: + hpr.setX(hpr.getX() + 180) + else: + hpr.setX(hpr.getX()) + angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) + hpr.setX(angle) + else: + self.notify.warning('getJumpHpr invalid golfKart, returning (0,0,0)') + return hpr + + toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.43), Parallel(LerpHprInterval(av, hpr=getJumpHpr, duration=0.9), ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) + return toonJumpTrack + + def getToonSitTrack(av): + toonSitTrack = Sequence(ActorInterval(av, 'sit-start'), Func(av.loop, 'sit')) + return toonSitTrack + + toonJumpTrack = getToonJumpTrack(av, seatIndex) + toonSitTrack = getToonSitTrack(av) + jumpTrack = Sequence(Parallel(toonJumpTrack, Sequence(Wait(1), toonSitTrack))) + return jumpTrack + + def emptySlot(self, index, avId, bailFlag, timestamp, timeSent = 0): + if self.fillSlotTrack: + self.fillSlotTrack.finish() + self.fillSlotTrack = None + if avId == 0: + pass + elif not self.isSetup: + newSlots = [] + for slot in self.deferredSlots: + if slot[0] != index: + newSlots.append(slot) + + self.deferredSlots = newSlots + elif avId in self.cr.doId2do: + if bailFlag == 1 and hasattr(self, 'clockNode'): + if timestamp < self.countdownTime and timestamp >= 0: + self.countdown(self.countdownTime - timestamp) + else: + self.countdown(self.countdownTime) + toon = self.cr.doId2do[avId] + toon.stopSmooth() + sitStartDuration = toon.getDuration('sit-start') + jumpOutTrack = self.generateToonReverseJumpTrack(toon, index) + track = Sequence(jumpOutTrack, Func(self.notifyToonOffElevator, toon), Func(self.clearToonTrack, avId), name=toon.uniqueName('emptyElevator'), autoPause=1) + if self.canHideBoardingQuitBtn(avId): + track.append(Func(localAvatar.boardingParty.groupPanel.enableQuitButton)) + track.append(Func(localAvatar.boardingParty.enableGoButton)) + track.delayDelete = DelayDelete.DelayDelete(toon, 'CogKart.emptySlot') + self.storeToonTrack(toon.doId, track) + track.start() + if avId == base.localAvatar.getDoId(): + messenger.send('exitElevator') + if avId in self.boardedAvIds: + del self.boardedAvIds[avId] + else: + self.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot exit the elevator!') + return + + def generateToonReverseJumpTrack(self, av, seatIndex): + self.notify.debug('av.getH() = %s' % av.getH()) + + def getToonJumpTrack(av, destNode): + + def getJumpDest(av = av, node = destNode): + dest = node.getPos(av.getParent()) + dest += Vec3(*self.JumpOutOffsets[seatIndex]) + return dest + + def getJumpHpr(av = av, node = destNode): + hpr = node.getHpr(av.getParent()) + hpr.setX(hpr.getX() + 180) + angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) + hpr.setX(angle) + return hpr + + toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.1), Parallel(ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) + return toonJumpTrack + + toonJumpTrack = getToonJumpTrack(av, self.golfKart) + jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render)) + return jumpTrack + + def startCountdownClock(self, countdownTime, ts): + DistributedElevatorExt.DistributedElevatorExt.startCountdownClock(self, countdownTime, ts) + self.clock.setH(self.clock.getH() + 180) + + def rejectBoard(self, avId, reason = 0): + print 'rejectBoard %s' % reason + if hasattr(base.localAvatar, 'elevatorNotifier'): + if reason == ElevatorConstants.REJECT_PROMOTION: + base.localAvatar.elevatorNotifier.showMe(TTLocalizer.BossElevatorRejectMessage) + doneStatus = {'where': 'reject'} + elevator = self.getPlaceElevator() + if elevator: + elevator.signalDone(doneStatus) + + def getDestName(self): + if self.countryClubId == ToontownGlobals.BossbotCountryClubIntA: + return TTLocalizer.ElevatorBossBotCourse0 + elif self.countryClubId == ToontownGlobals.BossbotCountryClubIntB: + return TTLocalizer.ElevatorBossBotCourse1 + elif self.countryClubId == ToontownGlobals.BossbotCountryClubIntC: + return TTLocalizer.ElevatorBossBotCourse2 diff --git a/toontown/coghq/DistributedCogKartAI.py b/toontown/coghq/DistributedCogKartAI.py new file mode 100755 index 00000000..c0eaa3c7 --- /dev/null +++ b/toontown/coghq/DistributedCogKartAI.py @@ -0,0 +1,58 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.building import DistributedElevatorExtAI +from toontown.building import ElevatorConstants +from toontown.safezone import DistributedGolfKartAI +from toontown.toonbase import ToontownGlobals + + +class DistributedCogKartAI(DistributedElevatorExtAI.DistributedElevatorExtAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogKartAI') + + def __init__(self, air, index, x, y, z, h, p, r, bldg): + self.posHpr = (x, y, z, h, p, r) + DistributedElevatorExtAI.DistributedElevatorExtAI.__init__(self, air, bldg) + self.type = ElevatorConstants.ELEVATOR_COUNTRY_CLUB + self.courseIndex = index + if self.courseIndex == 0: + self.countryClubId = ToontownGlobals.BossbotCountryClubIntA + elif self.courseIndex == 1: + self.countryClubId = ToontownGlobals.BossbotCountryClubIntB + elif self.courseIndex == 2: + self.countryClubId = ToontownGlobals.BossbotCountryClubIntC + else: + self.countryClubId = 12500 + + def getPosHpr(self): + return self.posHpr + + def elevatorClosed(self): + numPlayers = self.countFullSeats() + if numPlayers > 0: + players = [] + for i in self.seats: + if i not in [None, 0]: + players.append(i) + countryClubZone = self.bldg.createCountryClub(self.countryClubId, players) + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.sendUpdateToAvatarId(avId, 'setCountryClubInteriorZone', [ + countryClubZone]) + self.clearFullNow(seatIndex) + else: + self.notify.warning('The elevator left, but was empty.') + self.fsm.request('closed') + + def sendAvatarsToDestination(self, avIdList): + if len(avIdList) > 0: + countryClubZone = self.bldg.createCountryClub(self.countryClubId, avIdList) + for avId in avIdList: + if avId: + self.sendUpdateToAvatarId(avId, 'setCountryClubInteriorZoneForce', [countryClubZone]) + + def getCountryClubId(self): + return self.countryClubId + + def enterClosed(self): + DistributedElevatorExtAI.DistributedElevatorExtAI.enterClosed(self) + self.fsm.request('opening') diff --git a/toontown/coghq/DistributedCountryClub.py b/toontown/coghq/DistributedCountryClub.py new file mode 100755 index 00000000..fdeaa9be --- /dev/null +++ b/toontown/coghq/DistributedCountryClub.py @@ -0,0 +1,331 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import BulletinBoardWatcher +from otp.otpbase import OTPGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.coghq import CountryClubLayout +from toontown.coghq import DistributedCountryClubRoom +from toontown.coghq import CountryClubRoom +from toontown.coghq import FactoryCameraViews +from direct.gui import OnscreenText +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * + +class DistributedCountryClub(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCountryClub') + ReadyPost = 'CountryClubReady' + WinEvent = 'CountryClubWinEvent' + doBlockRooms = base.config.GetBool('block-country-club-rooms', 1) + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.lastCamEnterRoom = 0 + self.titleColor = (1, 1, 1, 1) + self.titleText = OnscreenText.OnscreenText('', fg=self.titleColor, shadow=(0, 0, 0, 1), font=ToontownGlobals.getSignFont(), pos=(0, -0.5), scale=0.1, drawOrder=0, mayChange=1) + self.titleSequence = None + return + + def generate(self): + self.notify.debug('generate: %s' % self.doId) + DistributedObject.DistributedObject.generate(self) + bboard.post('countryClub', self) + self.roomWatcher = None + self.geom = None + self.rooms = [] + self.hallways = [] + self.allRooms = [] + self.curToonRoomNum = None + self.allBlockedRooms = [] + base.localAvatar.setCameraCollisionsCanMove(1) + base.localAvatar.reparentTo(render) + base.localAvatar.setPosHpr(0, 0, 0, 0, 0, 0) + self.accept('SOSPanelEnter', self.handleSOSPanel) + self.factoryViews = FactoryCameraViews.FactoryCameraViews(self) + base.localAvatar.chatMgr.chatInputSpeedChat.addFactoryMenu() + self.__setupHighSky() + return + + def startSky(self): + self.sky = loader.loadModel('phase_12/models/bossbotHQ/BossTestSkyBox') + self.sky.reparentTo(camera) + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) + self.sky.setBin('background', 0) + + def stopSky(self): + taskMgr.remove('skyTrack') + self.sky.removeNode() + + def __setupHighSky(self): + self.startSky() + sky = self.sky + sky.setH(150) + sky.setZ(-100) + + def __cleanupHighSky(self): + sky = self.sky + sky.setH(0) + sky.setZ(0) + self.stopSky() + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def setCountryClubId(self, id): + DistributedCountryClub.notify.debug('setCountryClubId: %s' % id) + self.countryClubId = id + + def setFloorNum(self, num): + DistributedCountryClub.notify.debug('floorNum: %s' % num) + self.floorNum = num + self.layout = CountryClubLayout.CountryClubLayout(self.countryClubId, self.floorNum, self.layoutIndex) + + def setLayoutIndex(self, layoutIndex): + self.layoutIndex = layoutIndex + + def getLayoutIndex(self): + return self.layoutIndex + + def setRoomDoIds(self, roomDoIds): + self.roomDoIds = roomDoIds + self.roomWatcher = BulletinBoardWatcher.BulletinBoardWatcher('roomWatcher-%s' % self.doId, [ DistributedCountryClubRoom.getCountryClubRoomReadyPostName(doId) for doId in self.roomDoIds ], self.gotAllRooms) + + def gotAllRooms(self): + self.notify.debug('countryClub %s: got all rooms' % self.doId) + if self.roomWatcher: + self.roomWatcher.destroy() + self.roomWatcher = None + self.geom = render.attachNewNode('countryClub%s' % self.doId) + for doId in self.roomDoIds: + self.rooms.append(base.cr.doId2do[doId]) + self.rooms[-1].setCountryClub(self) + + self.notify.info('countryClubId %s, floor %s, %s' % (self.countryClubId, self.floorNum, self.rooms[0].avIdList)) + rng = self.layout.getRng() + numRooms = self.layout.getNumRooms() + for i, room in enumerate(self.rooms): + if i == 0: + room.getGeom().reparentTo(self.geom) + else: + room.attachTo(self.hallways[i - 1], rng) + self.allRooms.append(room) + self.listenForFloorEvents(room) + if i < numRooms - 1: + hallway = CountryClubRoom.CountryClubRoom(self.layout.getHallwayModel(i)) + hallway.attachTo(room, rng) + hallway.setRoomNum(i * 2 + 1) + hallway.initFloorCollisions() + hallway.enter() + self.hallways.append(hallway) + self.allRooms.append(hallway) + self.listenForFloorEvents(hallway) + + def handleCameraRayFloorCollision(collEntry, self = self): + name = collEntry.getIntoNode().getName() + self.notify.debug('camera floor ray collided with: %s' % name) + prefix = CountryClubRoom.CountryClubRoom.FloorCollPrefix + prefixLen = len(prefix) + if name[:prefixLen] == prefix: + try: + roomNum = int(name[prefixLen:]) + except: + DistributedLevel.notify.warning('Invalid zone floor collision node: %s' % name) + else: + self.camEnterRoom(roomNum) + + self.accept('on-floor', handleCameraRayFloorCollision) + if bboard.has('countryClubRoom'): + self.warpToRoom(bboard.get('countryClubRoom')) + firstSetZoneDoneEvent = self.cr.getNextSetZoneDoneEvent() + + def handleFirstSetZoneDone(): + self.notify.debug('countryClubHandleFirstSetZoneDone') + self.accept('takingScreenshot', self.handleScreenshot) + base.transitions.irisIn() + bboard.post(DistributedCountryClub.ReadyPost, self) + + self.acceptOnce(firstSetZoneDoneEvent, handleFirstSetZoneDone) + zoneList = [OTPGlobals.UberZone, self.zoneId] + for room in self.rooms: + zoneList.extend(room.zoneIds) + + base.cr.sendSetZoneMsg(self.zoneId, zoneList) + return + + def listenForFloorEvents(self, room): + roomNum = room.getRoomNum() + floorCollName = room.getFloorCollName() + + def handleZoneEnter(collisionEntry, self = self, roomNum = roomNum): + self.toonEnterRoom(roomNum) + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + room = self.allRooms[roomNum] + ouchLevel = room.getFloorOuchLevel() + room.startOuch(ouchLevel) + + self.accept('enter%s' % floorCollName, handleZoneEnter) + + def handleZoneExit(collisionEntry, self = self, roomNum = roomNum): + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + self.allRooms[roomNum].stopOuch() + + self.accept('exit%s' % floorCollName, handleZoneExit) + + def getAllRoomsTimeout(self): + self.notify.warning('countryClub %s: timed out waiting for room objs' % self.doId) + + def toonEnterRoom(self, roomNum): + self.notify.debug('toonEnterRoom: %s' % roomNum) + if roomNum != self.curToonRoomNum: + if self.curToonRoomNum is not None: + self.allRooms[self.curToonRoomNum].localToonFSM.request('notPresent') + self.allRooms[roomNum].localToonFSM.request('present') + self.curToonRoomNum = roomNum + return + + def camEnterRoom(self, roomNum): + self.notify.debug('camEnterRoom: %s' % roomNum) + blockRoomsAboveThisNumber = len(self.allRooms) + if self.allBlockedRooms and self.doBlockRooms: + blockRoomsAboveThisNumber = self.allBlockedRooms[0] + if roomNum % 2 == 1: + minVis = roomNum - 2 + maxVis = roomNum + 2 + else: + minVis = roomNum - 1 + maxVis = roomNum + 1 + for i, room in enumerate(self.allRooms): + if i < minVis or i > maxVis: + if not room.getGeom().isEmpty(): + room.getGeom().stash() + elif i <= blockRoomsAboveThisNumber: + if not room.getGeom().isEmpty(): + room.getGeom().unstash() + elif not room.getGeom().isEmpty(): + room.getGeom().stash() + + self.lastCamEnterRoom = roomNum + + def setBossConfronted(self, avId): + if avId == base.localAvatar.doId: + return + av = base.cr.identifyFriend(avId) + if av is None: + return + base.localAvatar.setSystemMessage(avId, TTLocalizer.CountryClubBossConfrontedMsg % av.getName()) + return + + def warpToRoom(self, roomId): + for i in xrange(len(self.rooms)): + room = self.rooms[i] + if room.roomId == roomId: + break + else: + return False + + base.localAvatar.setPosHpr(room.getGeom(), 0, 0, 0, 0, 0, 0) + self.camEnterRoom(i * 2) + return True + + def disable(self): + self.notify.debug('disable') + if self.titleSequence: + self.titleSequence.finish() + self.titleSequence = None + self.__cleanupHighSky() + self.ignoreAll() + for hallway in self.hallways: + hallway.exit() + + self.rooms = [] + for hallway in self.hallways: + hallway.delete() + + self.hallways = [] + self.allRooms = [] + if self.roomWatcher: + self.roomWatcher.destroy() + self.roomWatcher = None + if self.geom is not None: + self.geom.removeNode() + self.geom = None + base.localAvatar.setCameraCollisionsCanMove(0) + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.ignore('SOSPanelEnter') + bboard.remove('countryClub') + base.localAvatar.chatMgr.chatInputSpeedChat.removeFactoryMenu() + self.factoryViews.delete() + del self.factoryViews + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.rooms[0].avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def handleScreenshot(self): + base.addScreenshotString('countryClubId: %s, floor (from 1): %s' % (self.countryClubId, self.floorNum + 1)) + if hasattr(self, 'currentRoomName'): + base.addScreenshotString('%s' % self.currentRoomName) + + def setBlockedRooms(self, blockedRooms): + self.blockedRooms = blockedRooms + self.computeBlockedRoomsAndHallways() + self.camEnterRoom(self.lastCamEnterRoom) + + def computeBlockedRoomsAndHallways(self): + self.allBlockedRooms = [] + for roomIndex in self.blockedRooms: + self.allBlockedRooms.append(roomIndex * 2) + + self.allBlockedRooms.sort() + self.notify.debug('self.allBlockedRooms =%s' % self.allBlockedRooms) + + def setCountryClubZone(self, zoneId): + base.cr.sendSetZoneMsg(zoneId) + base.cr.playGame.getPlace().fsm.request('walk') + scale = base.localAvatar.getScale() + base.camera.setScale(scale) + + def elevatorAlert(self, avId): + if base.localAvatar.doId != avId: + name = base.cr.doId2do[avId].getName() + self.showInfoText(TTLocalizer.CountryClubToonEnterElevator % name) + + def showInfoText(self, text = 'hello world'): + description = text + if description and description != '': + self.titleText.setText(description) + self.titleText.setColor(Vec4(*self.titleColor)) + self.titleText.setColorScale(1, 1, 1, 1) + self.titleText.setFg(self.titleColor) + if self.titleSequence: + self.titleSequence.finish() + self.titleSequence = None + self.titleSequence = Sequence(Func(self.showTitleText), Wait(3.1), LerpColorScaleInterval(self.titleText, duration=0.5, colorScale=Vec4(1, 1, 1, 0.0)), Func(self.hideTitleText)) + self.titleSequence.start() + return + + def showTitleText(self): + if self.titleText: + self.titleText.show() + + def hideTitleText(self): + if self.titleText or 1: + self.titleText.hide() diff --git a/toontown/coghq/DistributedCountryClubAI.py b/toontown/coghq/DistributedCountryClubAI.py new file mode 100755 index 00000000..2b43796b --- /dev/null +++ b/toontown/coghq/DistributedCountryClubAI.py @@ -0,0 +1,118 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObjectAI +from otp.level import DistributedLevelAI +from toontown.building import DistributedClubElevatorAI +from toontown.coghq import BattleExperienceAggregatorAI +from toontown.coghq import CountryClubLayout, DistributedCountryClubRoomAI +from toontown.toonbase import ToontownGlobals + + +class DistributedCountryClubAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCountryClubAI') + + def __init__(self, air, countryClubId, zoneId, floorNum, avIds, layoutIndex, battleExpAggreg = None): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.countryClubId = countryClubId + self.zoneId = zoneId + self.floorNum = floorNum + self.avIds = avIds + self.layoutIndex = layoutIndex + self.blockedRooms = [] + self.elevatorList = [] + self.battleExpAggreg = battleExpAggreg + self.layout = CountryClubLayout.CountryClubLayout(self.countryClubId, self.floorNum, self.layoutIndex) + for i in xrange(self.layout.getNumRooms()): + if i: + self.blockedRooms.append(i) + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.notify.info('generate %s, id=%s, floor=%s' % (self.doId, self.countryClubId, self.floorNum)) + self.rooms = [] + if self.battleExpAggreg is None: + self.battleExpAggreg = BattleExperienceAggregatorAI.BattleExperienceAggregatorAI() + for i in xrange(self.layout.getNumRooms()): + room = DistributedCountryClubRoomAI.DistributedCountryClubRoomAI(self.air, self.countryClubId, self.doId, self.zoneId, self.layout.getRoomId(i), i * 2, self.avIds, self.battleExpAggreg) + room.generateWithRequired(self.zoneId) + self.rooms.append(room) + roomDoIds = [] + for room in self.rooms: + roomDoIds.append(room.doId) + self.sendUpdate('setRoomDoIds', [ + roomDoIds]) + self.placeElevatorsOnMarkers() + description = '%s|%s|%s' % (self.countryClubId, self.floorNum, self.avIds) + for avId in self.avIds: + self.air.writeServerEvent('countryClubEntered', avId, description) + + def requestDelete(self): + self.notify.info('requestDelete: %s' % self.doId) + for room in self.rooms: + room.requestDelete() + if hasattr(self, 'elevatorList'): + for elevator in self.elevatorList: + elevator.requestDelete() + DistributedObjectAI.DistributedObjectAI.requestDelete(self) + + def delete(self): + self.notify.info('delete: %s' % self.doId) + del self.rooms + del self.layout + del self.battleExpAggreg + if hasattr(self, 'elevatorList'): + del self.elevatorList + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getTaskZoneId(self): + return self.countryClubId + + def allToonsGone(self): + self.notify.info('allToonsGone') + self.requestDelete() + + def getZoneId(self): + return self.zoneId + + def getCountryClubId(self): + return self.countryClubId + + def getFloorNum(self): + return self.floorNum + + def getBlockedRooms(self): + self.notify.debug('getBlockedRooms returning %s' % self.blockedRooms) + return self.blockedRooms + + def roomDefeated(self, room): + if room in self.rooms: + roomIndex = self.rooms.index(room) + if roomIndex in self.blockedRooms: + self.blockedRooms.remove(roomIndex) + self.sendUpdate('setBlockedRooms', [self.blockedRooms]) + + def placeElevatorsOnMarkers(self): + for room in self.rooms: + if room.entType2ids['elevatorMarker']: + for markerId in room.entType2ids['elevatorMarker']: + marker = room.getEntity(markerId) + newElevator = DistributedClubElevatorAI.DistributedClubElevatorAI(self.air, self.doId, self, self.avIds, marker.doId) + newElevator.generateWithRequired(self.zoneId) + self.elevatorList.append(newElevator) + + def startNextFloor(self): + floor = self.floorNum + 1 + countryClubZone = self.air.allocateZone() + countryClub = DistributedCountryClubAI(self.air, self.countryClubId, countryClubZone, floor, self.avIds, self.layoutIndex, self.battleExpAggreg) + countryClub.generateWithRequired(countryClubZone) + for avId in self.avIds: + self.sendUpdateToAvatarId(avId, 'setCountryClubZone', [countryClubZone]) + self.requestDelete() + + def elevatorAlert(self, avId): + self.sendUpdate('elevatorAlert', [avId]) + + def setLayoutIndex(self, layoutIndex): + self.layoutIndex = layoutIndex + + def getLayoutIndex(self): + return self.layoutIndex diff --git a/toontown/coghq/DistributedCountryClubBattle.py b/toontown/coghq/DistributedCountryClubBattle.py new file mode 100755 index 00000000..68d99544 --- /dev/null +++ b/toontown/coghq/DistributedCountryClubBattle.py @@ -0,0 +1,50 @@ +import random + +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.interval.IntervalGlobal import * +from otp.avatar import Emote +from otp.nametag import NametagGlobals +from panda3d.core import * +from toontown.battle import SuitBattleGlobals +from toontown.battle.BattleBase import * +from toontown.coghq import DistributedLevelBattle +from toontown.suit import SuitDNA +from toontown.toon import TTEmote +from toontown.toonbase import ToontownGlobals + + +class DistributedCountryClubBattle(DistributedLevelBattle.DistributedLevelBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCountryClubBattle') + + def __init__(self, cr): + DistributedLevelBattle.DistributedLevelBattle.__init__(self, cr) + self.fsm.addState(State.State('CountryClubReward', self.enterCountryClubReward, self.exitCountryClubReward, ['Resume'])) + offState = self.fsm.getStateNamed('Off') + offState.addTransition('CountryClubReward') + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('CountryClubReward') + + def enterCountryClubReward(self, ts): + self.notify.debug('enterCountryClubReward()') + self.disableCollision() + self.delayDeleteMembers() + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + if self.bossBattle: + messenger.send('localToonConfrontedCountryClubBoss') + self.movie.playReward(ts, self.uniqueName('building-reward'), self.__handleCountryClubRewardDone, noSkip=True) + + def __handleCountryClubRewardDone(self): + self.notify.debug('countryClub reward done') + if self.hasLocalToon(): + self.d_rewardDone(base.localAvatar.doId) + self.movie.resetReward() + self.fsm.request('Resume') + + def exitCountryClubReward(self): + self.notify.debug('exitCountryClubReward()') + self.movie.resetReward(finish=1) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) diff --git a/toontown/coghq/DistributedCountryClubBattleAI.py b/toontown/coghq/DistributedCountryClubBattleAI.py new file mode 100755 index 00000000..1ca68d69 --- /dev/null +++ b/toontown/coghq/DistributedCountryClubBattleAI.py @@ -0,0 +1,65 @@ +import CogDisguiseGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.showbase.PythonUtil import addListsByValue +from toontown.battle.BattleBase import * +from toontown.coghq import DistributedLevelBattleAI +from toontown.toonbase import ToontownGlobals +from toontown.toonbase.ToontownBattleGlobals import getCountryClubCreditMultiplier + + +class DistributedCountryClubBattleAI(DistributedLevelBattleAI.DistributedLevelBattleAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCountryClubBattleAI') + + def __init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, roundCallback = None, finishCallback = None, maxSuits = 4): + DistributedLevelBattleAI.DistributedLevelBattleAI.__init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, 'CountryClubReward', roundCallback, finishCallback, maxSuits) + self.battleCalc.setSkillCreditMultiplier(1) + if self.bossBattle: + self.level.d_setBossConfronted(toonId) + self.fsm.addState(State.State('CountryClubReward', self.enterCountryClubReward, self.exitCountryClubReward, ['Resume'])) + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('CountryClubReward') + + def getTaskZoneId(self): + return self.level.countryClubId + + def handleToonsWon(self, toons): + extraMerits = [0, 0, 0, 0] + amount = ToontownGlobals.CountryClubCogBuckRewards[self.level.countryClubId] + index = ToontownGlobals.cogHQZoneId2deptIndex(self.level.countryClubId) + extraMerits[index] = amount + self.handleCrateReward(toons) + for toon in toons: + recovered, notRecovered = self.air.questManager.recoverItems(toon, self.suitsKilled, self.getTaskZoneId()) + self.toonItems[toon.doId][0].extend(recovered) + self.toonItems[toon.doId][1].extend(notRecovered) + meritArray = self.air.promotionMgr.recoverMerits(toon, self.suitsKilled, self.getTaskZoneId(), getCountryClubCreditMultiplier(self.getTaskZoneId()) * 2.0, extraMerits=extraMerits, addInvasion=False) + if toon.doId in self.helpfulToons: + self.toonMerits[toon.doId] = addListsByValue(self.toonMerits[toon.doId], meritArray) + else: + self.notify.debug('toon %d not helpful list, skipping merits' % toon.doId) + + def enterCountryClubReward(self): + self.joinableFsm.request('Unjoinable') + self.runableFsm.request('Unrunable') + self.resetResponses() + self.assignRewards() + self.bossDefeated = 1 + self.level.setVictors(self.activeToons[:]) + self.timer.startCallback(BUILDING_REWARD_TIMEOUT, self.serverRewardDone) + + def exitCountryClubReward(self): + pass + + def enterResume(self): + DistributedLevelBattleAI.DistributedLevelBattleAI.enterResume(self) + if self.bossBattle and self.bossDefeated: + self.battleMgr.level.b_setDefeated() + + def enterReward(self): + DistributedLevelBattleAI.DistributedLevelBattleAI.enterReward(self) + roomDoId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomDoId) + if room: + room.challengeDefeated() diff --git a/toontown/coghq/DistributedCountryClubRoom.py b/toontown/coghq/DistributedCountryClubRoom.py new file mode 100755 index 00000000..9ed19ba5 --- /dev/null +++ b/toontown/coghq/DistributedCountryClubRoom.py @@ -0,0 +1,213 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import random +from otp.level import DistributedLevel +from direct.directnotify import DirectNotifyGlobal +import CountryClubRoomBase, CountryClubRoom +import FactoryEntityCreator +import CountryClubRoomSpecs +from otp.level import LevelSpec, LevelConstants +from otp.nametag.NametagConstants import * +from toontown.toonbase import TTLocalizer + +def getCountryClubRoomReadyPostName(doId): + return 'countryClubRoomReady-%s' % doId + + +class DistributedCountryClubRoom(DistributedLevel.DistributedLevel, CountryClubRoomBase.CountryClubRoomBase, CountryClubRoom.CountryClubRoom): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCountryClubRoom') + EmulateEntrancePoint = False + + def __init__(self, cr): + DistributedLevel.DistributedLevel.__init__(self, cr) + CountryClubRoomBase.CountryClubRoomBase.__init__(self) + CountryClubRoom.CountryClubRoom.__init__(self) + self.suitIds = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.suitsInitialized = 0 + self.goonClipPlanes = {} + self.countryClub = None + return + + def createEntityCreator(self): + return FactoryEntityCreator.FactoryEntityCreator(level=self) + + def generate(self): + self.notify.debug('generate') + DistributedLevel.DistributedLevel.generate(self) + + def delete(self): + del self.countryClub + DistributedLevel.DistributedLevel.delete(self) + CountryClubRoom.CountryClubRoom.delete(self) + self.ignoreAll() + + def setCountryClubId(self, countryClubId): + self.notify.debug('countryClubId: %s' % countryClubId) + CountryClubRoomBase.CountryClubRoomBase.setCountryClubId(self, countryClubId) + + def setRoomId(self, roomId): + self.notify.debug('roomId: %s' % roomId) + CountryClubRoomBase.CountryClubRoomBase.setRoomId(self, roomId) + + def setRoomNum(self, num): + self.notify.debug('roomNum: %s' % num) + CountryClubRoom.CountryClubRoom.setRoomNum(self, num) + + def levelAnnounceGenerate(self): + self.notify.debug('levelAnnounceGenerate') + DistributedLevel.DistributedLevel.levelAnnounceGenerate(self) + specModule = CountryClubRoomSpecs.getCountryClubRoomSpecModule(self.roomId) + roomSpec = LevelSpec.LevelSpec(specModule) + DistributedLevel.DistributedLevel.initializeLevel(self, roomSpec) + + def getReadyPostName(self): + return getCountryClubRoomReadyPostName(self.doId) + + def privGotSpec(self, levelSpec): + DistributedLevel.DistributedLevel.privGotSpec(self, levelSpec) + base.localAvatar.setH(-90) + CountryClubRoom.CountryClubRoom.enter(self) + self.acceptOnce('leavingCountryClub', self.announceLeaving) + bboard.post(self.getReadyPostName()) + + def fixupLevelModel(self): + CountryClubRoom.CountryClubRoom.setGeom(self, self.geom) + CountryClubRoom.CountryClubRoom.initFloorCollisions(self) + + def setCountryClub(self, countryClub): + self.countryClub = countryClub + + def setBossConfronted(self, avId): + self.countryClub.setBossConfronted(avId) + + def setDefeated(self): + self.notify.info('setDefeated') + from toontown.coghq import DistributedCountryClub + messenger.send(DistributedCountryClub.DistributedCountryClub.WinEvent) + + def initVisibility(self, *args, **kw): + pass + + def shutdownVisibility(self, *args, **kw): + pass + + def lockVisibility(self, *args, **kw): + pass + + def unlockVisibility(self, *args, **kw): + pass + + def enterZone(self, *args, **kw): + pass + + def updateVisibility(self, *args, **kw): + pass + + def setVisibility(self, *args, **kw): + pass + + def resetVisibility(self, *args, **kw): + pass + + def handleVisChange(self, *args, **kw): + pass + + def forceSetZoneThisFrame(self, *args, **kw): + pass + + def getParentTokenForEntity(self, entId): + return 1000000 * self.roomNum + entId + + def enterLtNotPresent(self): + CountryClubRoom.CountryClubRoom.enterLtNotPresent(self) + self.ignore('f2') + + def enterLtPresent(self): + CountryClubRoom.CountryClubRoom.enterLtPresent(self) + if self.countryClub is not None: + self.countryClub.currentRoomName = CountryClubRoomSpecs.BossbotCountryClubRoomId2RoomName[self.roomId] + + def printPos(self = self): + thisZone = self.getZoneNode(LevelConstants.UberZoneEntId) + pos = base.localAvatar.getPos(thisZone) + h = base.localAvatar.getH(thisZone) + roomName = CountryClubRoomSpecs.BossbotCountryClubRoomId2RoomName[self.roomId] + print 'countryClub pos: %s, h: %s, room: %s' % (repr(pos), h, roomName) + if self.countryClub is not None: + floorNum = self.countryClub.floorNum + else: + floorNum = '???' + posStr = 'X: %.3f' % pos[0] + '\nY: %.3f' % pos[1] + '\nZ: %.3f' % pos[2] + '\nH: %.3f' % h + '\ncountryClubId: %s' % self.countryClubId + '\nfloor: %s' % floorNum + '\nroomId: %s' % self.roomId + '\nroomName: %s' % roomName + base.localAvatar.setChatAbsolute(posStr, CFThought | CFTimeout) + return + + self.accept('f2', printPos) + return + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def disable(self): + self.notify.debug('disable') + CountryClubRoom.CountryClubRoom.exit(self) + if hasattr(self, 'suits'): + del self.suits + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + bboard.remove(self.getReadyPostName()) + DistributedLevel.DistributedLevel.disable(self) + + def setSuits(self, suitIds, reserveSuitIds): + oldSuitIds = list(self.suitIds) + self.suitIds = suitIds + self.reserveSuitIds = reserveSuitIds + + def reservesJoining(self): + pass + + def getCogSpec(self, cogId): + cogSpecModule = CountryClubRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.CogData[cogId] + + def getReserveCogSpec(self, cogId): + cogSpecModule = CountryClubRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.ReserveCogData[cogId] + + def getBattleCellSpec(self, battleCellId): + cogSpecModule = CountryClubRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.BattleCells[battleCellId] + + def getFloorOuchLevel(self): + return 8 + + def getTaskZoneId(self): + return self.countryClubId + + def getBossTaunt(self): + return TTLocalizer.CountryClubBossTaunt + + def getBossBattleTaunt(self): + return TTLocalizer.CountryClubBossBattleTaunt + + def __str__(self): + if hasattr(self, 'roomId'): + return '%s %s: %s' % (self.__class__.__name__, self.roomId, CountryClubRoomSpecs.BossbotCountryClubRoomId2RoomName[self.roomId]) + else: + return 'DistributedCountryClubRoom' + + def __repr__(self): + return str(self) + + def forceOuch(self, penalty): + self.setOuch(penalty) diff --git a/toontown/coghq/DistributedCountryClubRoomAI.py b/toontown/coghq/DistributedCountryClubRoomAI.py new file mode 100755 index 00000000..2fa1add5 --- /dev/null +++ b/toontown/coghq/DistributedCountryClubRoomAI.py @@ -0,0 +1,129 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from otp.level import DistributedLevelAI, LevelSpec +from otp.level import LevelSpec +from toontown.coghq import CountryClubRoomBase, LevelSuitPlannerAI +from toontown.coghq import DistributedCountryClubBattleAI +from toontown.coghq import FactoryEntityCreatorAI, CountryClubRoomSpecs +from toontown.suit import DistributedMintSuitAI +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals + + +class DistributedCountryClubRoomAI(DistributedLevelAI.DistributedLevelAI, CountryClubRoomBase.CountryClubRoomBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCountryClubRoomAI') + + def __init__(self, air, countryClubId, countryClubDoId, zoneId, roomId, roomNum, avIds, battleExpAggreg): + DistributedLevelAI.DistributedLevelAI.__init__(self, air, zoneId, 0, avIds) + CountryClubRoomBase.CountryClubRoomBase.__init__(self) + self.setCountryClubId(countryClubId) + self.countryClubId = countryClubId + self.setRoomId(roomId) + self.roomNum = roomNum + self.countryClubDoId = countryClubDoId + self.battleExpAggreg = battleExpAggreg + + def createEntityCreator(self): + return FactoryEntityCreatorAI.FactoryEntityCreatorAI(level = self) + + def getBattleCreditMultiplier(self): + return ToontownBattleGlobals.getCountryClubCreditMultiplier(self.countryClubId) + + def generate(self): + self.notify.debug('generate %s: room=%s' % (self.doId, self.roomId)) + self.notify.debug('loading spec') + specModule = CountryClubRoomSpecs.getCountryClubRoomSpecModule(self.roomId) + roomSpec = LevelSpec.LevelSpec(specModule) + self.notify.debug('creating entities') + DistributedLevelAI.DistributedLevelAI.generate(self, roomSpec) + self.notify.debug('creating cogs') + cogSpecModule = CountryClubRoomSpecs.getCogSpecModule(self.roomId) + self.planner = LevelSuitPlannerAI.LevelSuitPlannerAI(self.air, self, DistributedMintSuitAI.DistributedMintSuitAI, DistributedCountryClubBattleAI.DistributedCountryClubBattleAI, cogSpecModule.CogData, cogSpecModule.ReserveCogData, cogSpecModule.BattleCells, battleExpAggreg = self.battleExpAggreg) + suitHandles = self.planner.genSuits() + messenger.send('plannerCreated-' + str(self.doId)) + self.suits = suitHandles['activeSuits'] + self.reserveSuits = suitHandles['reserveSuits'] + self.d_setSuits() + self.notify.debug('finish mint room %s %s creation' % (self.roomId, self.doId)) + + def delete(self): + self.notify.debug('delete: %s' % self.doId) + suits = self.suits + for reserve in self.reserveSuits: + suits.append(reserve[0]) + self.planner.destroy() + del self.planner + for suit in suits: + if not suit.isDeleted(): + suit.factoryIsGoingDown() + suit.requestDelete() + del self.battleExpAggreg + DistributedLevelAI.DistributedLevelAI.delete(self, deAllocZone = False) + + def getCountryClubId(self): + return self.countryClubId + + def getRoomId(self): + return self.roomId + + def getRoomNum(self): + return self.roomNum + + def getCogLevel(self): + return self.cogLevel + + def d_setSuits(self): + self.sendUpdate('setSuits', [self.getSuits(), self.getReserveSuits()]) + + def getSuits(self): + suitIds = [] + for suit in self.suits: + suitIds.append(suit.doId) + return suitIds + + def getReserveSuits(self): + suitIds = [] + for suit in self.reserveSuits: + suitIds.append(suit[0].doId) + return suitIds + + def d_setBossConfronted(self, toonId): + if toonId not in self.avIdList: + self.notify.warning('d_setBossConfronted: %s not in list of participants' % toonId) + return None + self.sendUpdate('setBossConfronted', [toonId]) + + def setVictors(self, victorIds): + activeVictors = [] + activeVictorIds = [] + for victorId in victorIds: + toon = self.air.doId2do.get(victorId) + if toon is not None: + activeVictors.append(toon) + activeVictorIds.append(victorId) + description = '%s|%s' % (self.countryClubId, activeVictorIds) + for avId in activeVictorIds: + self.air.writeServerEvent('mintDefeated', avId, description) + + def b_setDefeated(self): + self.d_setDefeated() + self.setDefeated() + + def d_setDefeated(self): + self.sendUpdate('setDefeated') + + def setDefeated(self): + pass + + def allToonsGone(self, toonsThatCleared): + DistributedLevelAI.DistributedLevelAI.allToonsGone(self, toonsThatCleared) + if self.roomNum == 0: + mint = simbase.air.doId2do.get(self.countryClubDoId) + if mint is not None: + mint.allToonsGone() + else: + self.notify.warning('no mint %s in allToonsGone' % self.countryClubDoId) + + def challengeDefeated(self): + countryClub = simbase.air.doId2do.get(self.countryClubDoId) + if countryClub: + countryClub.roomDefeated(self) diff --git a/toontown/coghq/DistributedCrate.py b/toontown/coghq/DistributedCrate.py new file mode 100755 index 00000000..4614882b --- /dev/null +++ b/toontown/coghq/DistributedCrate.py @@ -0,0 +1,300 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from CrateGlobals import * +from direct.showbase.PythonUtil import fitSrcAngle2Dest +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +import MovingPlatform +from direct.task.Task import Task +import DistributedCrushableEntity + +class DistributedCrate(DistributedCrushableEntity.DistributedCrushableEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCrate') + UP_KEY = 'arrow_up' + DOWN_KEY = 'arrow_down' + LEFT_KEY = 'arrow_left' + RIGHT_KEY = 'arrow_right' + ModelPaths = ('phase_9/models/cogHQ/woodCrateB', 'phase_10/models/cashbotHQ/CBWoodCrate') + + def __init__(self, cr): + DistributedCrushableEntity.DistributedCrushableEntity.__init__(self, cr) + self.initNodePath() + self.modelType = 0 + self.crate = None + self.gridSize = 3.0 + self.tContact = 0 + self.tStick = 0.01 + self.moveTrack = None + self.avMoveTrack = None + self.avPushTrack = None + self.crate = None + self.crushTrack = None + self.isLocalToon = 0 + self.stuckToCrate = 0 + self.upPressed = 0 + self.isPushing = 0 + self.creakSound = loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_crate_effort.ogg') + self.pushSound = loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_crate_sliding.ogg') + return + + def disable(self): + self.ignoreAll() + if self.moveTrack: + self.moveTrack.pause() + del self.moveTrack + if self.avMoveTrack: + self.avMoveTrack.pause() + del self.avMoveTrack + if self.avPushTrack: + self.avPushTrack.pause() + del self.avPushTrack + if self.crate: + self.crate.destroy() + del self.crate + if self.crushTrack: + self.crushTrack.pause() + del self.crushTrack + taskMgr.remove(self.taskName('crushTask')) + if self.pushable: + self.__listenForCollisions(0) + self.ignore('arrow_up') + self.ignore('arrow_up-up') + DistributedCrushableEntity.DistributedCrushableEntity.disable(self) + + def delete(self): + DistributedCrushableEntity.DistributedCrushableEntity.delete(self) + del self.creakSound + del self.pushSound + + def generateInit(self): + DistributedCrushableEntity.DistributedCrushableEntity.generateInit(self) + + def generate(self): + DistributedCrushableEntity.DistributedCrushableEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedCrushableEntity.DistributedCrushableEntity.announceGenerate(self) + self.loadModel() + self.modCrateCollisions() + if self.pushable: + self.__listenForCollisions(1) + self.accept('arrow_up', self.__upKeyPressed) + + def modCrateCollisions(self): + cNode = self.find('**/wall') + cNode.setName(self.uniqueName('crateCollision')) + cNode.setZ(-.8) + colNode = self.find('**/collision') + floor = colNode.find('**/MovingPlatform*') + floor2 = floor.copyTo(colNode) + floor2.setZ(-.8) + + def __upKeyPressed(self): + self.ignore('arrow_up') + self.accept('arrow_up-up', self.__upKeyReleased) + self.upPressed = 1 + + def __upKeyReleased(self): + self.ignore('arrow_up-up') + self.accept('arrow_up', self.__upKeyPressed) + self.upPressed = 0 + if self.stuckToCrate: + self.__resetStick() + + def loadModel(self): + crateModel = loader.loadModel(DistributedCrate.ModelPaths[self.modelType]) + self.crate = MovingPlatform.MovingPlatform() + self.crate.setupCopyModel(self.getParentToken(), crateModel, 'floor') + self.setScale(1.0) + self.crate.setScale(self.scale) + self.crate.reparentTo(self) + self.crate.flattenLight() + + def setScale(self, scale): + if self.crate: + self.crate.setScale(scale) + + def __listenForCollisions(self, on): + if on: + self.accept(self.uniqueName('entercrateCollision'), self.handleCollision) + else: + self.ignore(self.uniqueName('entercrateCollision')) + + def setPosition(self, x, y, z): + self.setPos(x, y, z) + + def handleCollision(self, collEntry = None): + if not self.upPressed: + return + crateNormal = Vec3(collEntry.getSurfaceNormal(self)) + relativeVec = base.localAvatar.getRelativeVector(self, crateNormal) + relativeVec.normalize() + worldVec = render.getRelativeVector(self, crateNormal) + worldVec.normalize() + offsetVec = Vec3(base.localAvatar.getPos(render) - self.getPos(render)) + offsetVec.normalize() + offsetDot = offsetVec[0] * worldVec[0] + offsetVec[1] * worldVec[1] + self.notify.debug('offsetDot = %s, world = %s, rel = %s' % (offsetDot, worldVec, offsetVec)) + if relativeVec.getY() < -0.7 and offsetDot > 0.9 and offsetVec.getZ() < 0.05: + self.getCrateSide(crateNormal) + self.tContact = globalClock.getFrameTime() + self.__listenForCollisions(0) + self.__listenForCancelEvents(1) + self.__startStickTask(crateNormal, base.localAvatar.getPos(render)) + + def setReject(self): + self.notify.debug('setReject') + self.sentRequest = 0 + if self.stuckToCrate: + self.__resetStick() + + def __startStickTask(self, crateNormal, toonPos): + self.__killStickTask() + self.stuckToCrate = 1 + sTask = Task(self.__stickTask) + sTask.crateNormal = crateNormal + sTask.toonPos = toonPos + taskMgr.add(sTask, self.taskName('stickTask')) + + def __killStickTask(self): + taskMgr.remove(self.taskName('stickTask')) + + def __stickTask(self, task): + tElapsed = globalClock.getFrameTime() - self.tContact + if tElapsed > self.tStick: + lToon = base.localAvatar + self.isLocalToon = 1 + crateNormal = task.crateNormal + crateWidth = 2.75 * self.scale + offset = crateWidth + 1.5 + TorsoToOffset[lToon.style.torso] + newPos = crateNormal * offset + if self.avPushTrack: + self.avPushTrack.pause() + place = base.cr.playGame.getPlace() + newHpr = CrateHprs[self.crateSide] + h = lToon.getH(self) + h = fitSrcAngle2Dest(h, newHpr[0]) + startHpr = Vec3(h, 0, 0) + self.avPushTrack = Sequence(LerpPosHprInterval(lToon, 0.25, newPos, newHpr, startHpr=startHpr, other=self, blendType='easeInOut'), Func(place.fsm.request, 'push'), Func(self.__sendPushRequest, task.crateNormal), SoundInterval(self.creakSound, node=self)) + self.avPushTrack.start() + return Task.done + else: + pos = task.toonPos + base.localAvatar.setPos(task.toonPos) + return Task.cont + + def getCrateSide(self, crateNormal): + for i in xrange(len(CrateNormals)): + dotP = CrateNormals[i].dot(crateNormal) + if dotP > 0.9: + self.crateSide = i + + def __sendPushRequest(self, crateNormal): + self.notify.debug('__sendPushRequest') + if self.crateSide != None: + self.sentRequest = 1 + self.sendUpdate('requestPush', [self.crateSide]) + else: + self.notify.debug("didn't send request") + return + + def __listenForCancelEvents(self, on): + self.notify.debug('%s, __listenForCancelEvents(%s)' % (self.doId, on)) + if on: + self.accept('arrow_down', self.__resetStick) + self.accept('arrow_left', self.__resetStick) + self.accept('arrow_right', self.__resetStick) + else: + self.ignore('arrow_down') + self.ignore('arrow_left') + self.ignore('arrow_right') + + def setMoveTo(self, avId, x0, y0, z0, x1, y1, z1): + self.notify.debug('setMoveTo') + self.__moveCrateTo(Vec3(x0, y0, z0), Vec3(x1, y1, z1)) + isLocal = base.localAvatar.doId == avId + if isLocal and self.stuckToCrate or not isLocal: + self.__moveAvTo(avId, Vec3(x0, y0, z0), Vec3(x1, y1, z1)) + + def __moveCrateTo(self, startPos, endPos): + if self.moveTrack: + self.moveTrack.finish() + self.moveTrack = None + self.moveTrack = Parallel(Sequence(LerpPosInterval(self, T_PUSH, endPos, startPos=startPos, fluid=1)), SoundInterval(self.creakSound, node=self), SoundInterval(self.pushSound, node=self, duration=T_PUSH, volume=0.2)) + self.moveTrack.start() + return + + def __moveAvTo(self, avId, startPos, endPos): + if self.avMoveTrack: + self.avMoveTrack.finish() + self.avMoveTrack = None + av = base.cr.doId2do.get(avId) + if av: + avMoveTrack = Sequence() + moveDir = endPos - startPos + crateNormal = startPos - endPos + crateNormal.normalize() + crateWidth = 2.75 * self.scale + offset = crateWidth + 1.5 + TorsoToOffset[av.style.torso] + toonOffset = crateNormal * offset + avMoveTrack.append(Sequence(LerpPosInterval(av, T_PUSH, toonOffset, startPos=toonOffset, other=self))) + self.avMoveTrack = avMoveTrack + self.avMoveTrack.start() + return + + def __resetStick(self): + self.notify.debug('__resetStick') + self.__killStickTask() + self.__listenForCancelEvents(0) + self.__listenForCollisions(1) + self.sendUpdate('setDone') + if self.avPushTrack: + self.avPushTrack.pause() + del self.avPushTrack + self.avPushTrack = None + if self.avMoveTrack: + self.avMoveTrack.pause() + del self.avMoveTrack + self.avMoveTrack = None + base.cr.playGame.getPlace().fsm.request('walk') + self.crateSide = None + self.crateNormal = None + self.isLocalToon = 0 + self.stuckToCrate = 0 + return + + def playCrushMovie(self, crusherId, axis): + self.notify.debug('playCrushMovie') + taskMgr.remove(self.taskName('crushTask')) + taskMgr.add(self.crushTask, self.taskName('crushTask'), extraArgs=(crusherId, axis), priority=25) + + def crushTask(self, crusherId, axis): + crusher = self.level.entities.get(crusherId, None) + if crusher: + crusherHeight = crusher.model.getPos(self)[2] + maxHeight = self.pos[2] + self.scale + minHeight = crusher.getPos(self)[2] + minScale = minHeight / maxHeight + self.notify.debug('cHeight= %s' % crusherHeight) + if crusherHeight < maxHeight and crusherHeight >= minHeight: + if crusherHeight == minHeight: + self.setScale(Vec3(1.2, 1.2, minScale)) + taskMgr.doMethodLater(2, self.setScale, 'resetScale', extraArgs=(1,)) + return Task.done + else: + k = crusherHeight / maxHeight + sx = min(1 / k, 0.2) + self.setScale(Vec3(1 + sx, 1 + sx, k)) + return Task.cont + + def originalTry(self, axis): + tSquash = 0.4 + if self.crushTrack: + self.crushTrack.finish() + del self.crushTrack + self.crushTrack = None + self.crushTrack = Sequence(LerpScaleInterval(self, tSquash, VBase3(1.2, 1.2, 0.25), blendType='easeInOut'), LerpColorScaleInterval(self, 2.0, VBase4(1, 1, 1, 0), blendType='easeInOut'), Wait(2.0), LerpScaleInterval(self, 0.1, VBase3(1, 1, 1), blendType='easeInOut'), LerpColorScaleInterval(self, 0.1, VBase4(1, 1, 1, 0), blendType='easeInOut')) + self.crushTrack.start() + return diff --git a/toontown/coghq/DistributedCrateAI.py b/toontown/coghq/DistributedCrateAI.py new file mode 100755 index 00000000..1d4dbd97 --- /dev/null +++ b/toontown/coghq/DistributedCrateAI.py @@ -0,0 +1,87 @@ +from CrateGlobals import * +from direct.directnotify import DirectNotifyGlobal +import DistributedCrushableEntityAI +from direct.task import Task +import CrateGlobals + +class DistributedCrateAI(DistributedCrushableEntityAI.DistributedCrushableEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCrateAI') + + def __init__(self, level, entId): + DistributedCrushableEntityAI.DistributedCrushableEntityAI.__init__(self, level, entId) + self.grid = None + self.avId = 0 + self.tPowerUp = 0 + self.width = 2 + return + + def generate(self): + DistributedCrushableEntityAI.DistributedCrushableEntityAI.generate(self) + + def delete(self): + taskMgr.remove(self.taskName('sendPush')) + DistributedCrushableEntityAI.DistributedCrushableEntityAI.delete(self) + + def requestPush(self, side): + self.notify.debug('requestPush') + avId = self.air.getAvatarIdFromSender() + if side not in [0, + 1, + 2, + 3]: + self.air.writeServerEvent('suspicious', avId, 'DistributedCrateAI.requestPush given invalid side arg') + return + if not self.avId and self.grid.checkPush(self.entId, side): + self.avId = avId + self.side = side + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + taskMgr.remove(self.taskName('sendPush')) + taskMgr.doMethodLater(self.tPowerUp, self.sendPushTask, self.taskName('sendPush')) + else: + self.sendUpdateToAvatarId(avId, 'setReject', []) + + def setDone(self): + self.notify.debug('setDone') + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + taskMgr.remove(self.taskName('sendPush')) + self.avId = 0 + + def sendPushTask(self, task): + self.notify.debug('sendPushTask') + oldPos = self.grid.getObjPos(self.entId) + if self.grid.doPush(self.entId, self.side): + newPos = self.grid.getObjPos(self.entId) + self.sendUpdate('setMoveTo', [self.avId, + oldPos[0], + oldPos[1], + oldPos[2], + newPos[0], + newPos[1], + newPos[2]]) + taskMgr.doMethodLater(CrateGlobals.T_PUSH + CrateGlobals.T_PAUSE, self.sendPushTask, self.taskName('sendPush')) + else: + taskMgr.remove(self.taskName('sendPush')) + self.sendUpdateToAvatarId(self.avId, 'setReject', []) + self.avId = 0 + return Task.done + + def updateGrid(self): + pass + + def doCrush(self, crusherId, axis): + DistributedCrushableEntityAI.DistributedCrushableEntityAI.doCrush(self, crusherId, axis) + + def setGridId(self, gridId): + self.gridId = gridId + grid = self.level.entities.get(gridId, None) + if grid: + self.grid = grid + self.grid.addObjectByPos(self.entId, self.pos, width=2) + self.b_setPosition(self.getPosition()) + return + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + if self.avId == avId: + self.avId = 0 diff --git a/toontown/coghq/DistributedCrushableEntity.py b/toontown/coghq/DistributedCrushableEntity.py new file mode 100755 index 00000000..cd13f124 --- /dev/null +++ b/toontown/coghq/DistributedCrushableEntity.py @@ -0,0 +1,37 @@ +from otp.level import DistributedEntity +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath +from otp.level import BasicEntities + +class DistributedCrushableEntity(DistributedEntity.DistributedEntity, NodePath, BasicEntities.NodePathAttribs): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCrushableEntity') + + def __init__(self, cr): + DistributedEntity.DistributedEntity.__init__(self, cr) + node = hidden.attachNewNode('DistributedNodePathEntity') + + def initNodePath(self): + node = hidden.attachNewNode('DistributedNodePathEntity') + NodePath.__init__(self, node) + + def announceGenerate(self): + DistributedEntity.DistributedEntity.announceGenerate(self) + BasicEntities.NodePathAttribs.initNodePathAttribs(self) + + def disable(self): + self.reparentTo(hidden) + BasicEntities.NodePathAttribs.destroy(self) + DistributedEntity.DistributedEntity.disable(self) + + def delete(self): + self.removeNode() + DistributedEntity.DistributedEntity.delete(self) + + def setPosition(self, x, y, z): + self.setPos(x, y, z) + + def setCrushed(self, crusherId, axis): + self.playCrushMovie(crusherId, axis) + + def playCrushMovie(self, crusherId, axis): + pass diff --git a/toontown/coghq/DistributedCrushableEntityAI.py b/toontown/coghq/DistributedCrushableEntityAI.py new file mode 100755 index 00000000..613efeda --- /dev/null +++ b/toontown/coghq/DistributedCrushableEntityAI.py @@ -0,0 +1,88 @@ +from otp.level import DistributedEntityAI +from direct.directnotify import DirectNotifyGlobal + +class DistributedCrushableEntityAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCrushableEntityAI') + + def __init__(self, level, entId): + self.isCrushable = 0 + self.crushCellId = None + self.crushCell = None + self.grid = None + self.width = 1 + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + return + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + self.setActiveCrushCell() + if self.level: + self.attachToGrid() + + def delete(self): + self.ignoreAll() + DistributedEntityAI.DistributedEntityAI.delete(self) + + def destroy(self): + if self.crushCell != None: + self.crushCell.unregisterCrushable(self.entId) + self.crushCell = None + DistributedEntityAI.DistributedEntityAI.destroy(self) + return + + def attachToGrid(self): + if self.gridId is not None: + + def setGrid(gridId = self.gridId, self = self): + grid = self.level.entities.get(gridId, None) + if grid: + self.grid = grid + self.grid.addObjectByPos(self.entId, self.pos, self.width) + self.b_setPosition(self.getPosition()) + self.initGridDependents() + return 1 + return 0 + + self.level.setEntityCreateCallback(self.gridId, setGrid) + return + + def initGridDependents(self): + pass + + def setActiveCrushCell(self): + if self.crushCellId != None: + self.notify.debug('setActiveCrushCell, entId: %d' % self.entId) + self.crushCell = self.level.entities.get(self.crushCellId, None) + if self.crushCell == None: + self.accept(self.level.getEntityCreateEvent(self.crushCellId), self.setActiveCrushCell) + else: + self.isCrushable = 1 + self.crushCell.registerCrushable(self.entId) + return + + def b_setPosition(self, pos): + self.d_setPosition(pos) + self.setPosition(pos) + + def d_setPosition(self, pos): + self.sendUpdate('setPosition', [pos[0], pos[1], pos[2]]) + + def setPosition(self, pos): + self.pos = pos + + def getPosition(self): + return self.grid.getObjPos(self.entId) + + def updateGrid(self): + pass + + def doCrush(self, crusherId, axis): + pass + + def setGridId(self, gridId): + self.gridId = gridId + self.attachToGrid() + + def setCrushCellId(self, crushCellId): + self.crushCellId = crushCellId + self.setActiveCrushCell() diff --git a/toontown/coghq/DistributedCrusherEntity.py b/toontown/coghq/DistributedCrusherEntity.py new file mode 100755 index 00000000..4aea0b2d --- /dev/null +++ b/toontown/coghq/DistributedCrusherEntity.py @@ -0,0 +1,12 @@ +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal + +class DistributedCrusherEntity(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCrusherEntity') + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + + def announceGenerate(self): + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.crushMsg = self.getUniqueName('crushMsg') diff --git a/toontown/coghq/DistributedCrusherEntityAI.py b/toontown/coghq/DistributedCrusherEntityAI.py new file mode 100755 index 00000000..19a5a5d3 --- /dev/null +++ b/toontown/coghq/DistributedCrusherEntityAI.py @@ -0,0 +1,57 @@ +# File: D (Python 2.4) + +from otp.level import DistributedEntityAI +from direct.directnotify import DirectNotifyGlobal + +class DistributedCrusherEntityAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCrusherEntityAI') + + def __init__(self, level, entId): + self.isCrusher = 0 + self.crushCell = None + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.crushMsg = self.getUniqueName('crusherDoCrush') + + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + self.setActiveCrushCell() + + + def delete(self): + self.ignoreAll() + DistributedEntityAI.DistributedEntityAI.delete(self) + + + def destroy(self): + self.notify.info('destroy entity %s' % self.entId) + if self.crushCell != None: + self.crushCell.unregisterCrusher(self.entId) + self.crushCell = None + + DistributedEntityAI.DistributedEntityAI.destroy(self) + + + def setActiveCrushCell(self): + self.notify.debug('setActiveCrushCell, entId: %d' % self.entId) + if self.crushCellId != None: + self.crushCell = self.level.entities.get(self.crushCellId, None) + if self.crushCell == None: + self.accept(self.level.getEntityCreateEvent(self.crushCellId), self.setActiveCrushCell) + else: + self.isCrusher = 1 + self.crushCell.registerCrusher(self.entId) + + + + def sendCrushMsg(self, axis = 0): + if self.isCrusher: + messenger.send(self.crushMsg, [ + self.entId, + axis]) + + + + def getPosition(self): + if hasattr(self, 'pos'): + return self.pos diff --git a/toontown/coghq/DistributedDoorEntity.py b/toontown/coghq/DistributedDoorEntity.py new file mode 100755 index 00000000..d313e032 --- /dev/null +++ b/toontown/coghq/DistributedDoorEntity.py @@ -0,0 +1,444 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +import DistributedDoorEntityBase +from direct.fsm import FourState +from direct.fsm import ClassicFSM +from otp.level import DistributedEntity +from toontown.toonbase import TTLocalizer +from otp.level import BasicEntities +from direct.fsm import State +from otp.level import VisibilityBlocker + +class DistributedDoorEntityLock(DistributedDoorEntityBase.LockBase, FourState.FourState): + slideLeft = Vec3(-7.5, 0.0, 0.0) + slideRight = Vec3(7.5, 0.0, 0.0) + + def __init__(self, door, lockIndex, lockedNodePath, leftNodePath, rightNodePath, stateIndex): + self.door = door + self.lockIndex = lockIndex + self.lockedNodePath = lockedNodePath + self.leftNodePath = leftNodePath + self.rightNodePath = rightNodePath + self.initialStateIndex = stateIndex + FourState.FourState.__init__(self, self.stateNames, self.stateDurations) + + def delete(self): + self.takedown() + del self.door + + def setup(self): + self.setLockState(self.initialStateIndex) + del self.initialStateIndex + + def takedown(self): + if self.track is not None: + self.track.pause() + self.track = None + for i in self.states.keys(): + del self.states[i] + + self.states = [] + self.fsm = None + return + + def setLockState(self, stateIndex): + if self.stateIndex != stateIndex: + state = self.states.get(stateIndex) + if state is not None: + self.fsm.request(state) + return + + def isUnlocked(self): + return self.isOn() + + def enterState1(self): + FourState.FourState.enterState1(self) + beat = self.duration * 0.05 + slideSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_arms_retracting.ogg') + self.setTrack(Sequence(Wait(beat * 2.0), Parallel(SoundInterval(slideSfx, node=self.door.node, volume=0.8), Sequence(ShowInterval(self.leftNodePath), ShowInterval(self.rightNodePath), Parallel(LerpPosInterval(nodePath=self.leftNodePath, other=self.lockedNodePath, duration=beat * 16.0, pos=Vec3(0.0), blendType='easeIn'), LerpPosInterval(nodePath=self.rightNodePath, other=self.lockedNodePath, duration=beat * 16.0, pos=Vec3(0.0), blendType='easeIn')), HideInterval(self.leftNodePath), HideInterval(self.rightNodePath), ShowInterval(self.lockedNodePath))))) + + def enterState2(self): + FourState.FourState.enterState2(self) + self.setTrack(None) + self.leftNodePath.setPos(self.lockedNodePath, Vec3(0.0)) + self.rightNodePath.setPos(self.lockedNodePath, Vec3(0.0)) + self.leftNodePath.hide() + self.rightNodePath.hide() + self.lockedNodePath.show() + return + + def enterState3(self): + FourState.FourState.enterState3(self) + unlockSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_unlock.ogg') + slideSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_arms_retracting.ogg') + beat = self.duration * 0.05 + self.setTrack(Sequence(Wait(beat * 2), Parallel(SoundInterval(unlockSfx, node=self.door.node, volume=0.8), SoundInterval(slideSfx, node=self.door.node, volume=0.8), Sequence(HideInterval(self.lockedNodePath), ShowInterval(self.leftNodePath), ShowInterval(self.rightNodePath), Parallel(LerpPosInterval(nodePath=self.leftNodePath, other=self.lockedNodePath, duration=beat * 16, pos=self.slideLeft, blendType='easeOut'), LerpPosInterval(nodePath=self.rightNodePath, other=self.lockedNodePath, duration=beat * 16, pos=self.slideRight, blendType='easeOut')), HideInterval(self.leftNodePath), HideInterval(self.rightNodePath))))) + + def enterState4(self): + FourState.FourState.enterState4(self) + self.setTrack(None) + self.leftNodePath.setPos(self.lockedNodePath, self.slideLeft) + self.rightNodePath.setPos(self.lockedNodePath, self.slideRight) + self.leftNodePath.hide() + self.rightNodePath.hide() + self.lockedNodePath.hide() + return + + +class DistributedDoorEntity(DistributedDoorEntityBase.DistributedDoorEntityBase, DistributedEntity.DistributedEntity, BasicEntities.NodePathAttribsProxy, FourState.FourState, VisibilityBlocker.VisibilityBlocker): + + def __init__(self, cr): + self.innerDoorsTrack = None + self.isVisReady = 0 + self.isOuterDoorOpen = 0 + DistributedEntity.DistributedEntity.__init__(self, cr) + FourState.FourState.__init__(self, self.stateNames, self.stateDurations) + VisibilityBlocker.VisibilityBlocker.__init__(self) + self.locks = [] + return + + def generate(self): + DistributedEntity.DistributedEntity.generate(self) + + def announceGenerate(self): + self.doorNode = hidden.attachNewNode('door-%s' % self.entId) + DistributedEntity.DistributedEntity.announceGenerate(self) + BasicEntities.NodePathAttribsProxy.initNodePathAttribs(self) + self.setup() + + def disable(self): + self.takedown() + self.doorNode.removeNode() + del self.doorNode + DistributedEntity.DistributedEntity.disable(self) + + def delete(self): + DistributedEntity.DistributedEntity.delete(self) + + def setup(self): + self.setupDoor() + for i in self.locks: + i.setup() + + self.accept('exit%s' % (self.getName(),), self.exitTrigger) + self.acceptAvatar() + + def takedown(self): + self.ignoreAll() + if self.track is not None: + self.track.finish() + self.track = None + if self.innerDoorsTrack is not None: + self.innerDoorsTrack.finish() + self.innerDoorsTrack = None + for i in self.locks: + i.takedown() + + self.locks = [] + self.fsm = None + for i in self.states.keys(): + del self.states[i] + + self.states = [] + return + + setUnlock0Event = DistributedDoorEntityBase.stubFunction + setUnlock1Event = DistributedDoorEntityBase.stubFunction + setUnlock2Event = DistributedDoorEntityBase.stubFunction + setUnlock3Event = DistributedDoorEntityBase.stubFunction + setIsOpenEvent = DistributedDoorEntityBase.stubFunction + setIsLock0Unlocked = DistributedDoorEntityBase.stubFunction + setIsLock1Unlocked = DistributedDoorEntityBase.stubFunction + setIsLock2Unlocked = DistributedDoorEntityBase.stubFunction + setIsLock3Unlocked = DistributedDoorEntityBase.stubFunction + setIsOpen = DistributedDoorEntityBase.stubFunction + setSecondsOpen = DistributedDoorEntityBase.stubFunction + + def acceptAvatar(self): + self.accept('enter%s' % (self.getName(),), self.enterTrigger) + + def rejectInteract(self): + DistributedEntity.DistributedEntity.rejectInteract(self) + self.acceptAvatar() + + def avatarExit(self, avatarId): + DistributedEntity.DistributedEntity.avatarExit(self, avatarId) + self.acceptAvatar() + + def enterTrigger(self, args = None): + messenger.send('DistributedInteractiveEntity_enterTrigger') + self.sendUpdate('requestOpen') + + def exitTrigger(self, args = None): + messenger.send('DistributedInteractiveEntity_exitTrigger') + + def okToUnblockVis(self): + VisibilityBlocker.VisibilityBlocker.okToUnblockVis(self) + self.isVisReady = 1 + self.openInnerDoors() + + def changedOnState(self, isOn): + messenger.send(self.getOutputEventName(), [not isOn]) + + def setLocksState(self, stateBits): + lock0 = stateBits & 15 + lock1 = (stateBits & 240) >> 4 + lock2 = (stateBits & 3840) >> 8 + if self.isGenerated(): + self.locks[0].setLockState(lock0) + self.locks[1].setLockState(lock1) + self.locks[2].setLockState(lock2) + else: + self.initialLock0StateIndex = lock0 + self.initialLock1StateIndex = lock1 + self.initialLock2StateIndex = lock2 + + def setDoorState(self, stateIndex, timeStamp): + self.stateTime = globalClockDelta.localElapsedTime(timeStamp) + if self.isGenerated(): + if self.stateIndex != stateIndex: + state = self.states.get(stateIndex) + if state is not None: + self.fsm.request(state) + else: + self.initialState = stateIndex + self.initialStateTimestamp = timeStamp + return + + def getName(self): + return 'switch-%s' % str(self.entId) + + def getNodePath(self): + if hasattr(self, 'doorNode'): + return self.doorNode + return None + + def setupDoor(self): + model = loader.loadModel('phase_9/models/cogHQ/CogDoorHandShake') + if model: + doorway = model.find('**/Doorway1') + rootNode = self.doorNode.attachNewNode(self.getName() + '-root') + rootNode.setPos(self.pos) + rootNode.setHpr(self.hpr) + rootNode.setScale(self.scale) + rootNode.setColor(self.color) + change = rootNode.attachNewNode('changePos') + doorway.reparentTo(change) + self.node = rootNode + self.node.show() + self.locks.append(DistributedDoorEntityLock(self, + 0, + doorway.find('**/Slide_One_Closed'), + doorway.find('**/Slide_One_Left_Open'), + doorway.find('**/Slide_One_Right_Open'), + self.initialLock0StateIndex)) + self.locks.append(DistributedDoorEntityLock(self, + 1, + doorway.find('**/Slide_Two_Closed'), + doorway.find('**/Slide_Two_Left_Open'), + doorway.find('**/Slide_Two_Right_Open'), + self.initialLock1StateIndex)) + self.locks.append(DistributedDoorEntityLock(self, + 2, + doorway.find('**/Slide_Three_Closed'), + doorway.find('**/Slide_Three_Left_Open'), + doorway.find('**/Slide_Three_Right_Open'), + self.initialLock2StateIndex)) + + del self.initialLock0StateIndex + del self.initialLock1StateIndex + del self.initialLock2StateIndex + + door = doorway.find('doortop') + if door.isEmpty(): + print 'doortop hack' + door = doorway.attachNewNode('doortop') + doorway.find('doortop1').reparentTo(door) + doorway.find('doortop2').reparentTo(door) + + rootNode = self.doorNode.attachNewNode(self.getName() + '-topDoor') + rootNode.setPos(self.pos) + rootNode.setHpr(self.hpr) + rootNode.setScale(self.scale) + rootNode.setColor(self.color) + change = rootNode.attachNewNode('changePos') + door.reparentTo(change) + self.doorTop = rootNode + self.doorTop.show() + + rootNode = self.doorTop.getParent().attachNewNode(self.getName() + '-leftDoor') + change = rootNode.attachNewNode('change') + door = doorway.find('**/doorLeft') + door = door.reparentTo(change) + self.doorLeft = rootNode + self.doorLeft.show() + change.setPos(self.pos) + change.setHpr(self.hpr) + change.setScale(self.scale) + change.setColor(self.color) + + door = doorway.find('doorbottom') + if door.isEmpty(): + print 'doorbottom hack' + door = doorway.attachNewNode('doorbottom') + doorway.find('doorbottom1').reparentTo(door) + doorway.find('doorbottom2').reparentTo(door) + + change = render.attachNewNode('changePos') + door.reparentTo(change) + rootNode = self.doorNode.attachNewNode(self.getName() + '-bottomDoor') + rootNode.setPos(self.pos) + rootNode.setHpr(self.hpr) + rootNode.setScale(self.scale) + rootNode.setColor(self.color) + change.reparentTo(rootNode) + self.doorBottom = rootNode + self.doorBottom.show() + + rootNode = self.doorTop.getParent().attachNewNode(self.getName() + '-rightDoor') + change = rootNode.attachNewNode('change') + door = doorway.find('**/doorRight') + door = door.reparentTo(change) + self.doorRight = rootNode + self.doorRight.show() + change.setPos(self.pos) + change.setHpr(self.hpr) + change.setScale(self.scale) + change.setColor(self.color) + + collision = self.doorLeft.find('**/doorLeft_collision1') + collision.setName(self.getName()) + collision = self.doorLeft.find('**/doorLeft_collision2') + collision.setName(self.getName()) + collision = self.doorRight.find('**/doorRight_collision1') + collision.setName(self.getName()) + collision = self.doorRight.find('**/doorRight_collision2') + collision.setName(self.getName()) + collision = self.doorLeft.find('**/doorLeft_innerCollision') + collision.setName(self.getName()) + self.leftInnerCollision = collision + collision = self.doorRight.find('**/doorRight_innerCollision') + collision.setName(self.getName()) + self.rightInnerCollision = collision + + if 1: + pass + else: + radius = 8.0 + cSphere = CollisionSphere(0.0, 0.0, 0.0, radius) + cSphere.setTangible(0) + cSphereNode = CollisionNode(self.getName()) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(BitMask32.allOff()) + cSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.cSphereNodePath = self.node.attachNewNode(cSphereNode) + + if 1: + self.node.flattenMedium() + self.doorTop.flattenMedium() + self.doorBottom.flattenMedium() + self.doorLeft.flattenMedium() + self.doorRight.flattenMedium() + + self.setDoorState(self.initialState, self.initialStateTimestamp) + del self.initialState + del self.initialStateTimestamp + + def setInnerDoorsTrack(self, track): + if self.innerDoorsTrack is not None: + self.innerDoorsTrack.pause() + self.innerDoorsTrack = None + if track is not None: + track.start(0.0) + self.innerDoorsTrack = track + return + + def openInnerDoors(self): + if not self.level.complexVis() or self.isOuterDoorOpen and (not self.isVisBlocker or self.isVisReady): + duration = self.duration + slideSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.ogg') + finalSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.ogg') + moveDistance = 8.0 + self.setInnerDoorsTrack(Sequence(Func(self.leftInnerCollision.unstash), Func(self.rightInnerCollision.unstash), Parallel(SoundInterval(slideSfx, node=self.node, duration=duration * 0.4, volume=0.8), LerpPosInterval(nodePath=self.doorLeft, duration=duration * 0.4, pos=Vec3(-moveDistance, 0.0, 0.0), blendType='easeOut'), LerpPosInterval(nodePath=self.doorRight, duration=duration * 0.4, pos=Vec3(moveDistance, 0.0, 0.0), blendType='easeOut'), Sequence(Wait(duration * 0.375), SoundInterval(finalSfx, node=self.node, duration=1.0, volume=0.8))), Func(self.doorLeft.stash), Func(self.doorRight.stash))) + + def closeInnerDoors(self): + duration = self.duration + slideSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.ogg') + finalSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.ogg') + moveDistance = 8.0 + self.setInnerDoorsTrack(Sequence(Func(self.doorLeft.unstash), Func(self.doorRight.unstash), Parallel(SoundInterval(slideSfx, node=self.node, duration=duration * 0.4, volume=0.8), LerpPosInterval(nodePath=self.doorLeft, duration=duration * 0.4, pos=Vec3(0.0), blendType='easeIn'), LerpPosInterval(nodePath=self.doorRight, duration=duration * 0.4, pos=Vec3(0.0), blendType='easeIn'), Sequence(Wait(duration * 0.375), SoundInterval(finalSfx, node=self.node, duration=1.0, volume=0.8))), Func(self.leftInnerCollision.stash), Func(self.rightInnerCollision.stash))) + + def setisOuterDoorOpen(self, isOpen): + self.isOuterDoorOpen = isOpen + + def enterState1(self): + FourState.FourState.enterState1(self) + self.isOuterDoorOpen = 0 + if self.isVisBlocker: + if not self.isVisReady: + self.requestUnblockVis() + else: + self.okToUnblockVis() + duration = self.duration + slideSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.ogg') + finalSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.ogg') + moveDistance = 8.0 + self.setTrack(Sequence(Wait(duration * 0.1), Parallel(SoundInterval(slideSfx, node=self.node, duration=duration * 0.4, volume=0.8), LerpPosInterval(nodePath=self.doorTop, duration=duration * 0.4, pos=Vec3(0.0, 0.0, moveDistance), blendType='easeOut'), LerpPosInterval(nodePath=self.doorBottom, duration=duration * 0.4, pos=Vec3(0.0, 0.0, -moveDistance), blendType='easeOut'), Sequence(Wait(duration * 0.375), SoundInterval(finalSfx, node=self.node, duration=1.0, volume=0.8))), Func(self.doorTop.stash), Func(self.doorBottom.stash), Func(self.setisOuterDoorOpen, 1), Func(self.openInnerDoors))) + + def enterState2(self): + FourState.FourState.enterState2(self) + self.isOuterDoorOpen = 1 + self.setTrack(None) + moveDistance = 7.5 + (self.doorTop.setPos(Vec3(0.0, 0.0, moveDistance)),) + (self.doorBottom.setPos(Vec3(0.0, 0.0, -moveDistance)),) + self.doorTop.stash() + self.doorBottom.stash() + if not self.isVisBlocker or not self.isWaitingForUnblockVis(): + self.setInnerDoorsTrack(None) + self.doorLeft.setPos(Vec3(-moveDistance, 0.0, 0.0)) + self.doorRight.setPos(Vec3(moveDistance, 0.0, 0.0)) + self.doorLeft.stash() + self.doorRight.stash() + return + + def exitState2(self): + FourState.FourState.exitState2(self) + self.cancelUnblockVis() + + def enterState3(self): + FourState.FourState.enterState3(self) + duration = self.duration + slideSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.ogg') + finalSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.ogg') + self.setTrack(Sequence(Wait(duration * 0.1), + Func(self.closeInnerDoors), + Wait(duration * 0.4), + Func(self.doorTop.unstash), + Func(self.doorBottom.unstash), + Parallel(SoundInterval(slideSfx, node=self.node, duration=duration*0.4, volume=0.8), + LerpPosInterval(nodePath=self.doorTop, duration=duration*0.4, pos=Vec3(0.0), blendType='easeIn'), + LerpPosInterval(nodePath=self.doorBottom, duration=duration*0.4, pos=Vec3(0.0), blendType='easeIn'), + Sequence(Wait(duration*0.375), + SoundInterval(finalSfx, node=self.node, duration=duration*0.4, volume=0.8))), + Func(self.setisOuterDoorOpen, 0))) + + def enterState4(self): + FourState.FourState.enterState4(self) + self.setisOuterDoorOpen(0) + self.isVisReady = 0 + self.setTrack(None) + self.doorTop.unstash() + self.doorBottom.unstash() + self.doorTop.setPos(Vec3(0.0)) + self.doorBottom.setPos(Vec3(0.0)) + self.setInnerDoorsTrack(None) + self.leftInnerCollision.stash() + self.rightInnerCollision.stash() + self.doorLeft.unstash() + self.doorRight.unstash() + self.doorLeft.setPos(Vec3(0.0)) + self.doorRight.setPos(Vec3(0.0)) diff --git a/toontown/coghq/DistributedDoorEntityAI.py b/toontown/coghq/DistributedDoorEntityAI.py new file mode 100755 index 00000000..443c7253 --- /dev/null +++ b/toontown/coghq/DistributedDoorEntityAI.py @@ -0,0 +1,209 @@ +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import DirectObject +import DistributedDoorEntityBase +from direct.distributed import DistributedObjectAI +from otp.level import DistributedEntityAI +from direct.fsm import FourStateAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task + +class Lock(DistributedDoorEntityBase.LockBase, DirectObject.DirectObject, FourStateAI.FourStateAI): + + def __init__(self, door, lockIndex, event, isUnlocked): + self.door = door + self.stateIndex = 0 + self.lockIndex = lockIndex + self.stateIndex = lockIndex + FourStateAI.FourStateAI.__init__(self, self.stateNames, durations=self.stateDurations) + self.unlockEvent = None + self.setUnlockEvent(event) + self.setIsUnlocked(isUnlocked) + return + + def getLockState(self): + return self.stateIndex + + def setup(self): + pass + + def takedown(self): + self.ignoreAll() + FourStateAI.FourStateAI.delete(self) + del self.door + + def setUnlockEvent(self, event): + if self.unlockEvent: + self.ignore(self.unlockEvent) + self.unlockEvent = self.door.getOutputEventName(event) + if self.unlockEvent: + self.accept(self.unlockEvent, self.setIsUnlocked) + + def distributeStateChange(self): + self.door.sendLocksState() + + def setIsUnlocked(self, isUnlocked): + self.setIsOn(isUnlocked) + if not isUnlocked: + self.door.locking() + + def setLockState(self, stateIndex): + if self.stateIndex != stateIndex: + self.fsm.request(self.states[stateIndex]) + + def isUnlocked(self): + return self.isOn() + + +class DistributedDoorEntityAI(DistributedDoorEntityBase.DistributedDoorEntityBase, DistributedEntityAI.DistributedEntityAI, FourStateAI.FourStateAI): + + def __init__(self, level, entId, zoneId = None): + self.entId = entId + self._isGenerated = 0 + self.isOpenInput = None + self.stateIndex = 0 + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.stateDurations[2] = self.secondsOpen + FourStateAI.FourStateAI.__init__(self, self.stateNames, durations=self.stateDurations) + self.setup() + if zoneId is not None: + self.generateWithRequired(zoneId) + return + + def generateWithRequired(self, zoneId): + DistributedEntityAI.DistributedEntityAI.generateWithRequired(self, zoneId) + self._isGenerated = 1 + + def delete(self): + self.takedown() + FourStateAI.FourStateAI.delete(self) + DistributedEntityAI.DistributedEntityAI.delete(self) + + def getLocksState(self): + stateBits = 0 + if hasattr(self, 'locks'): + stateBits = self.locks[0].getLockState() & 15 | self.locks[1].getLockState() << 4 & 240 | self.locks[2].getLockState() << 8 & 3840 + return stateBits + + def sendLocksState(self): + if self._isGenerated: + self.sendUpdate('setLocksState', [self.getLocksState()]) + + def getDoorState(self): + r = (self.stateIndex, globalClockDelta.getRealNetworkTime()) + return r + + def getName(self): + return 'door-%s' % (self.entId,) + + def setup(self): + if not hasattr(self, 'unlock0Event'): + self.unlock0Event = None + if not hasattr(self, 'unlock1Event'): + self.unlock1Event = None + if not hasattr(self, 'unlock2Event'): + self.unlock2Event = None + if not hasattr(self, 'unlock3Event'): + self.unlock3Event = None + if not hasattr(self, 'isLock0Unlocked'): + self.isLock0Unlocked = None + if not hasattr(self, 'isLock1Unlocked'): + self.isLock1Unlocked = None + if not hasattr(self, 'isLock2Unlocked'): + self.isLock2Unlocked = None + if not hasattr(self, 'isLock3Unlocked'): + self.isLock3Unlocked = None + self.locks = [Lock(self, 0, self.unlock0Event, self.isLock0Unlocked), Lock(self, 1, self.unlock1Event, self.isLock1Unlocked), Lock(self, 2, self.unlock2Event, self.isLock2Unlocked)] + del self.unlock0Event + del self.unlock1Event + del self.unlock2Event + del self.unlock3Event + del self.isLock0Unlocked + del self.isLock1Unlocked + del self.isLock2Unlocked + del self.isLock3Unlocked + if hasattr(self, 'isOpenEvent'): + self.setIsOpenEvent(self.isOpenEvent) + del self.isOpenEvent + if hasattr(self, 'isOpen'): + self.setIsOpen(self.isOpen) + del self.isOpen + return + + def takedown(self): + self.ignoreAll() + for i in self.locks: + i.takedown() + + del self.locks + + setScale = DistributedDoorEntityBase.stubFunction + setColor = DistributedDoorEntityBase.stubFunction + setModel = DistributedDoorEntityBase.stubFunction + + def setIsOpenEvent(self, event): + if self.isOpenEvent: + self.ignore(self.isOpenEvent) + self.isOpenEvent = self.getOutputEventName(event) + if self.isOpenEvent: + self.accept(self.isOpenEvent, self.setIsOpen) + + def changedOnState(self, isOn): + if hasattr(self, 'entId'): + messenger.send(self.getOutputEventName(), [not isOn]) + + def setIsOpen(self, isOpen): + self.setIsOn(not isOpen) + + def getIsOpen(self): + return not self.getIsOn() + + def setSecondsOpen(self, secondsOpen): + if self.secondsOpen != secondsOpen: + self.secondsOpen = secondsOpen + if secondsOpen < 0.0: + secondsOpen = None + self.stateDurations[2] = secondsOpen + return + + def locking(self): + if self.stateIndex == 1 or self.stateIndex == 2: + self.fsm.request(self.states[3]) + + def setUnlock0Event(self, event): + self.locks[0].setUnlockEvent(event) + + def setUnlock1Event(self, event): + self.locks[1].setUnlockEvent(event) + + def setUnlock2Event(self, event): + self.locks[2].setUnlockEvent(event) + + def setUnlock3Event(self, event): + pass + + def setIsLock0Unlocked(self, unlocked): + self.locks[0].setIsUnlocked(unlocked) + + def setIsLock1Unlocked(self, unlocked): + self.locks[1].setIsUnlocked(unlocked) + + def setIsLock2Unlocked(self, unlocked): + self.locks[2].setIsUnlocked(unlocked) + + def setIsLock3Unlocked(self, unlocked): + pass + + def isUnlocked(self): + isUnlocked = self.locks[0].isUnlocked() and self.locks[1].isUnlocked() and self.locks[2].isUnlocked() + return isUnlocked + + def distributeStateChange(self): + if self._isGenerated: + self.sendUpdate('setDoorState', self.getDoorState()) + + def requestOpen(self): + if self.isUnlocked(): + if self.fsm.getCurrentState() is not self.states[2]: + self.fsm.request(self.states[1]) diff --git a/toontown/coghq/DistributedDoorEntityBase.py b/toontown/coghq/DistributedDoorEntityBase.py new file mode 100755 index 00000000..3f66941b --- /dev/null +++ b/toontown/coghq/DistributedDoorEntityBase.py @@ -0,0 +1,30 @@ + + +def stubFunction(*args): + pass + + +class LockBase: + stateNames = ['off', + 'locking', + 'locked', + 'unlocking', + 'unlocked'] + stateDurations = [None, + 3.5, + None, + 4.0, + None] + + +class DistributedDoorEntityBase: + stateNames = ['off', + 'opening', + 'open', + 'closing', + 'closed'] + stateDurations = [None, + 5.0, + 1.0, + 6.0, + None] diff --git a/toontown/coghq/DistributedElevatorMarker.py b/toontown/coghq/DistributedElevatorMarker.py new file mode 100755 index 00000000..1395140b --- /dev/null +++ b/toontown/coghq/DistributedElevatorMarker.py @@ -0,0 +1,53 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from StomperGlobals import * +from direct.distributed import ClockDelta +from direct.showbase.PythonUtil import lerp +import math +from otp.level import DistributedEntity +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath +from otp.level import BasicEntities +from direct.task import Task +from toontown.toonbase import ToontownGlobals + +class DistributedElevatorMarker(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorMarker') + elevatorMarkerModels = ['phase_9/models/cogHQ/square_stomper'] + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.loadModel() + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + DistributedEntity.DistributedEntity.disable(self) + + def delete(self): + self.notify.debug('delete') + self.unloadModel() + BasicEntities.DistributedNodePathEntity.delete(self) + + def loadModel(self): + self.rotateNode = self.attachNewNode('rotate') + self.model = None + + def unloadModel(self): + if self.model: + self.model.removeNode() + del self.model + self.model = None + return diff --git a/toontown/coghq/DistributedElevatorMarkerAI.py b/toontown/coghq/DistributedElevatorMarkerAI.py new file mode 100755 index 00000000..bf069545 --- /dev/null +++ b/toontown/coghq/DistributedElevatorMarkerAI.py @@ -0,0 +1,28 @@ +from otp.ai.AIBase import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from direct.task import Task +from otp.level import DistributedEntityAI +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal + +class DistributedElevatorMarkerAI(DistributedEntityAI.DistributedEntityAI, NodePath, BasicEntities.NodePathAttribs): + + def __init__(self, level, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + node = hidden.attachNewNode('DistributedElevatorMarkerAI') + NodePath.__init__(self, node) + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + self.setPos(self.pos) + self.setHpr(self.hpr) + + def delete(self): + self.ignoreAll() + DistributedEntityAI.DistributedEntityAI.delete(self) + + def destroy(self): + self.notify.info('destroy entity(elevatorMaker) %s' % self.entId) + DistributedEntityAI.DistributedEntityAI.destroy(self) diff --git a/toontown/coghq/DistributedFactory.py b/toontown/coghq/DistributedFactory.py new file mode 100755 index 00000000..c32e2d80 --- /dev/null +++ b/toontown/coghq/DistributedFactory.py @@ -0,0 +1,172 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import random +from otp.level import DistributedLevel +from direct.directnotify import DirectNotifyGlobal +import FactoryBase +import FactoryEntityCreator +import FactorySpecs +from otp.level import LevelSpec +from otp.level import LevelConstants +from toontown.toonbase import TTLocalizer +from toontown.coghq import FactoryCameraViews +from direct.controls.ControlManager import CollisionHandlerRayStart +from otp.nametag.NametagConstants import * +from otp.ai.MagicWordGlobal import * + +class DistributedFactory(DistributedLevel.DistributedLevel, FactoryBase.FactoryBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFactory') + + def __init__(self, cr): + DistributedLevel.DistributedLevel.__init__(self, cr) + FactoryBase.FactoryBase.__init__(self) + self.suitIds = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.suitsInitialized = 0 + self.goonClipPlanes = {} + + def createEntityCreator(self): + return FactoryEntityCreator.FactoryEntityCreator(level=self) + + def generate(self): + self.notify.debug('generate') + DistributedLevel.DistributedLevel.generate(self) + self.factoryViews = FactoryCameraViews.FactoryCameraViews(self) + base.localAvatar.chatMgr.chatInputSpeedChat.addFactoryMenu() + self.accept('SOSPanelEnter', self.handleSOSPanel) + base.factory = self + + def delete(self): + DistributedLevel.DistributedLevel.delete(self) + base.localAvatar.chatMgr.chatInputSpeedChat.removeFactoryMenu() + self.factoryViews.delete() + del self.factoryViews + del base.factory + self.ignore('SOSPanelEnter') + + def setFactoryId(self, id): + FactoryBase.FactoryBase.setFactoryId(self, id) + + def setForemanConfronted(self, avId): + if avId == base.localAvatar.doId: + return + av = base.cr.identifyFriend(avId) + if av is None: + return + base.localAvatar.setSystemMessage(avId, TTLocalizer.ForemanConfrontedMsg % av.getName()) + return + + def setDefeated(self): + self.notify.info('setDefeated') + messenger.send('FactoryWinEvent') + + def levelAnnounceGenerate(self): + self.notify.debug('levelAnnounceGenerate') + DistributedLevel.DistributedLevel.levelAnnounceGenerate(self) + specModule = FactorySpecs.getFactorySpecModule(self.factoryId) + factorySpec = LevelSpec.LevelSpec(specModule) + DistributedLevel.DistributedLevel.initializeLevel(self, factorySpec) + + def privGotSpec(self, levelSpec): + firstSetZoneDoneEvent = self.cr.getNextSetZoneDoneEvent() + + def handleFirstSetZoneDone(): + base.factoryReady = 1 + messenger.send('FactoryReady') + + self.acceptOnce(firstSetZoneDoneEvent, handleFirstSetZoneDone) + modelCount = len(levelSpec.getAllEntIds()) + loader.beginBulkLoad('factory', TTLocalizer.HeadingToFactoryTitle % TTLocalizer.FactoryNames[self.factoryId], modelCount, 1, TTLocalizer.TIP_COGHQ, self.factoryId) + DistributedLevel.DistributedLevel.privGotSpec(self, levelSpec) + loader.endBulkLoad('factory') + + def printPos(self = self): + pos = base.localAvatar.getPos(self.getZoneNode(self.lastToonZone)) + h = base.localAvatar.getH(self.getZoneNode(self.lastToonZone)) + print 'factory pos: %s, h: %s, zone %s' % (repr(pos), h, self.lastToonZone) + posStr = 'X: %.3f' % pos[0] + '\nY: %.3f' % pos[1] + '\nZ: %.3f' % pos[2] + '\nH: %.3f' % h + '\nZone: %s' % str(self.lastToonZone) + base.localAvatar.setChatAbsolute(posStr, CFThought | CFTimeout) + + self.accept('f2', printPos) + base.localAvatar.setCameraCollisionsCanMove(1) + self.acceptOnce('leavingFactory', self.announceLeaving) + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def disable(self): + self.notify.debug('disable') + base.localAvatar.setCameraCollisionsCanMove(0) + if hasattr(self, 'suits'): + del self.suits + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + DistributedLevel.DistributedLevel.disable(self) + + def setSuits(self, suitIds, reserveSuitIds): + oldSuitIds = list(self.suitIds) + self.suitIds = suitIds + self.reserveSuitIds = reserveSuitIds + newSuitIds = [] + for suitId in self.suitIds: + if suitId not in oldSuitIds: + newSuitIds.append(suitId) + + if len(newSuitIds): + + def bringOutOfReserve(suits): + for suit in suits: + if suit: + suit.comeOutOfReserve() + + self.relatedObjectMgrRequest = self.cr.relatedObjectMgr.requestObjects(newSuitIds, bringOutOfReserve) + + def reservesJoining(self): + pass + + def getCogSpec(self, cogId): + cogSpecModule = FactorySpecs.getCogSpecModule(self.factoryId) + return cogSpecModule.CogData[cogId] + + def getReserveCogSpec(self, cogId): + cogSpecModule = FactorySpecs.getCogSpecModule(self.factoryId) + return cogSpecModule.ReserveCogData[cogId] + + def getBattleCellSpec(self, battleCellId): + cogSpecModule = FactorySpecs.getCogSpecModule(self.factoryId) + return cogSpecModule.BattleCells[battleCellId] + + def getFloorOuchLevel(self): + return 2 + + def getGoonPathId(self): + return 'sellbotFactory' + + def getTaskZoneId(self): + return self.factoryId + + def getBossTaunt(self): + return TTLocalizer.FactoryBossTaunt + + def getBossBattleTaunt(self): + return TTLocalizer.FactoryBossBattleTaunt + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def factoryWarp(zoneNum): + """ + Warp to a specific factory zone. + """ + if not hasattr(base, 'factory'): + return 'You must be in a factory!' + base.factory.warpToZone(zoneNum) + return 'Warped to zone: %d' % zoneNum diff --git a/toontown/coghq/DistributedFactoryAI.py b/toontown/coghq/DistributedFactoryAI.py new file mode 100755 index 00000000..a4229b8f --- /dev/null +++ b/toontown/coghq/DistributedFactoryAI.py @@ -0,0 +1,119 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from otp.level import DistributedLevelAI, LevelSpec +from toontown.suit import DistributedFactorySuitAI +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals +from toontown.coghq import DistributedBattleFactoryAI +import FactoryBase, FactoryEntityCreatorAI, FactorySpecs, LevelSuitPlannerAI + +class DistributedFactoryAI(DistributedLevelAI.DistributedLevelAI, FactoryBase.FactoryBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFactoryAI') + + def __init__(self, air, factoryId, zoneId, entranceId, avIds): + DistributedLevelAI.DistributedLevelAI.__init__(self, air, zoneId, entranceId, avIds) + FactoryBase.FactoryBase.__init__(self) + self.setFactoryId(factoryId) + + def createEntityCreator(self): + return FactoryEntityCreatorAI.FactoryEntityCreatorAI(level=self) + + def getBattleCreditMultiplier(self): + return ToontownBattleGlobals.getFactoryCreditMultiplier(self.factoryId) + + def generate(self): + self.notify.info('generate') + self.notify.info('start factory %s %s creation, frame=%s' % (self.factoryId, self.doId, globalClock.getFrameCount())) + self.notify.info('loading spec') + specModule = FactorySpecs.getFactorySpecModule(self.factoryId) + factorySpec = LevelSpec.LevelSpec(specModule) + self.notify.info('creating entities') + DistributedLevelAI.DistributedLevelAI.generate(self, factorySpec) + self.notify.info('creating cogs') + cogSpecModule = FactorySpecs.getCogSpecModule(self.factoryId) + self.planner = LevelSuitPlannerAI.LevelSuitPlannerAI(self.air, self, DistributedFactorySuitAI.DistributedFactorySuitAI, DistributedBattleFactoryAI.DistributedBattleFactoryAI, cogSpecModule.CogData, cogSpecModule.ReserveCogData, cogSpecModule.BattleCells) + suitHandles = self.planner.genSuits() + messenger.send('plannerCreated-' + str(self.doId)) + self.suits = suitHandles['activeSuits'] + self.reserveSuits = suitHandles['reserveSuits'] + self.d_setSuits() + scenario = 0 + description = '%s|%s|%s|%s' % (self.factoryId, + self.entranceId, + scenario, + self.avIdList) + for avId in self.avIdList: + self.air.writeServerEvent('factoryEntered', avId, description) + + self.notify.info('finish factory %s %s creation' % (self.factoryId, self.doId)) + + def delete(self): + self.notify.info('delete: %s' % self.doId) + suits = self.suits + for reserve in self.reserveSuits: + suits.append(reserve[0]) + + self.planner.destroy() + del self.planner + for suit in suits: + if not suit.isDeleted(): + suit.factoryIsGoingDown() + suit.requestDelete() + + DistributedLevelAI.DistributedLevelAI.delete(self) + + def getTaskZoneId(self): + return self.factoryId + + def getFactoryId(self): + return self.factoryId + + def d_setForemanConfronted(self, avId): + if avId in self.avIdList: + self.sendUpdate('setForemanConfronted', [avId]) + else: + self.notify.warning('%s: d_setForemanConfronted: av %s not in av list %s' % (self.doId, avId, self.avIdList)) + + def setVictors(self, victorIds): + activeVictors = [] + activeVictorIds = [] + for victorId in victorIds: + toon = self.air.doId2do.get(victorId) + if toon is not None: + activeVictors.append(toon) + activeVictorIds.append(victorId) + scenario = 0 + description = '%s|%s|%s|%s' % (self.factoryId, self.entranceId, scenario, activeVictorIds) + for avId in activeVictorIds: + self.air.writeServerEvent('factoryDefeated', avId, description) + for toon in activeVictors: + simbase.air.questManager.toonDefeatedFactory(toon, self.factoryId) + + def b_setDefeated(self): + self.d_setDefeated() + self.setDefeated() + + def d_setDefeated(self): + self.sendUpdate('setDefeated') + + def setDefeated(self): + pass + + def getCogLevel(self): + return self.cogLevel + + def d_setSuits(self): + self.sendUpdate('setSuits', [self.getSuits(), self.getReserveSuits()]) + + def getSuits(self): + suitIds = [] + for suit in self.suits: + suitIds.append(suit.doId) + + return suitIds + + def getReserveSuits(self): + suitIds = [] + for suit in self.reserveSuits: + suitIds.append(suit[0].doId) + + return suitIds diff --git a/toontown/coghq/DistributedFactoryElevatorExt.py b/toontown/coghq/DistributedFactoryElevatorExt.py new file mode 100755 index 00000000..8729e964 --- /dev/null +++ b/toontown/coghq/DistributedFactoryElevatorExt.py @@ -0,0 +1,95 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from toontown.building.ElevatorConstants import * +from toontown.building.ElevatorUtils import * +from toontown.building import DistributedElevatorExt +from toontown.building import DistributedElevator +from toontown.toonbase import ToontownGlobals +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer + +class DistributedFactoryElevatorExt(DistributedElevatorExt.DistributedElevatorExt): + + def __init__(self, cr): + DistributedElevatorExt.DistributedElevatorExt.__init__(self, cr) + + def generate(self): + DistributedElevatorExt.DistributedElevatorExt.generate(self) + + def delete(self): + self.elevatorModel.removeNode() + del self.elevatorModel + DistributedElevatorExt.DistributedElevatorExt.delete(self) + + def setEntranceId(self, entranceId): + self.entranceId = entranceId + if self.entranceId == 0: + self.elevatorModel.setPosHpr(62.74, -85.31, 0.0, 2.0, 0.0, 0.0) + elif self.entranceId == 1: + self.elevatorModel.setPosHpr(-162.25, 26.43, 0.0, 269.0, 0.0, 0.0) + elif self.entranceId == 2 and base.config.GetBool('want-megacorp', True): + self.elevatorModel.setPosHpr(64.793, -2.34, 0.0, -900.0, 0.0, 0.0) + else: + self.notify.error('Invalid entranceId: %s' % entranceId) + + def setupElevator(self): + self.elevatorModel = loader.loadModel('phase_4/models/modules/elevator') + self.elevatorModel.reparentTo(render) + self.elevatorModel.setScale(1.05) + self.leftDoor = self.elevatorModel.find('**/left-door') + self.rightDoor = self.elevatorModel.find('**/right-door') + self.elevatorModel.find('**/light_panel').removeNode() + self.elevatorModel.find('**/light_panel_frame').removeNode() + DistributedElevator.DistributedElevator.setupElevator(self) + + def getElevatorModel(self): + return self.elevatorModel + + def setBldgDoId(self, bldgDoId): + self.bldg = None + self.setupElevator() + return + + def getZoneId(self): + return 0 + + def __doorsClosed(self, zoneId): + pass + + def setFactoryInteriorZone(self, zoneId): + if self.localToonOnBoard: + hoodId = self.cr.playGame.hood.hoodId + doneStatus = {'loader': 'cogHQLoader', + 'where': 'factoryInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'hoodId': hoodId} + self.cr.playGame.getPlace().elevator.signalDone(doneStatus) + + def setFactoryInteriorZoneForce(self, zoneId): + place = self.cr.playGame.getPlace() + if place: + place.fsm.request('elevator', [self]) + hoodId = self.cr.playGame.hood.hoodId + doneStatus = {'loader': 'cogHQLoader', + 'where': 'factoryInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'hoodId': hoodId} + if hasattr(place, 'elevator') and place.elevator: + place.elevator.signalDone(doneStatus) + else: + self.notify.warning("setMintInteriorZoneForce: Couldn't find playGame.getPlace().elevator, zoneId: %s" % zoneId) + else: + self.notify.warning("setFactoryInteriorZoneForce: Couldn't find playGame.getPlace(), zoneId: %s" % zoneId) + + def getDestName(self): + if self.entranceId == 0: + return TTLocalizer.ElevatorSellBotFactory0 + elif self.entranceId == 1: + return TTLocalizer.ElevatorSellBotFactory1 + elif self.entranceId == 2: + return TTLocalizer.ElevatorSellBotFactory2 diff --git a/toontown/coghq/DistributedFactoryElevatorExtAI.py b/toontown/coghq/DistributedFactoryElevatorExtAI.py new file mode 100755 index 00000000..3211466a --- /dev/null +++ b/toontown/coghq/DistributedFactoryElevatorExtAI.py @@ -0,0 +1,49 @@ +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from toontown.building.ElevatorConstants import * +from toontown.building import DistributedElevatorExtAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task + +class DistributedFactoryElevatorExtAI(DistributedElevatorExtAI.DistributedElevatorExtAI): + + def __init__(self, air, bldg, factoryId, entranceId): + DistributedElevatorExtAI.DistributedElevatorExtAI.__init__(self, air, bldg) + self.factoryId = factoryId + self.entranceId = entranceId + + def getEntranceId(self): + return self.entranceId + + def elevatorClosed(self): + numPlayers = self.countFullSeats() + if numPlayers > 0: + players = [] + for i in self.seats: + if i not in [None, 0]: + players.append(i) + + factoryZone = self.bldg.createFactory(self.factoryId, self.entranceId, players) + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.sendUpdateToAvatarId(avId, 'setFactoryInteriorZone', [factoryZone]) + self.clearFullNow(seatIndex) + + else: + self.notify.warning('The elevator left, but was empty.') + self.fsm.request('closed') + return + + def enterClosed(self): + DistributedElevatorExtAI.DistributedElevatorExtAI.enterClosed(self) + self.fsm.request('opening') + + def sendAvatarsToDestination(self, avIdList): + if len(avIdList) > 0: + factoryZone = self.bldg.createFactory(self.factoryId, self.entranceId, avIdList) + for avId in avIdList: + if avId: + self.sendUpdateToAvatarId(avId, 'setFactoryInteriorZoneForce', [factoryZone]) diff --git a/toontown/coghq/DistributedFoodBelt.py b/toontown/coghq/DistributedFoodBelt.py new file mode 100755 index 00000000..99eb89a6 --- /dev/null +++ b/toontown/coghq/DistributedFoodBelt.py @@ -0,0 +1,352 @@ +from pandac.PandaModules import NodePath, Point3, CollisionTube, CollisionNode +from direct.fsm import FSM +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import Sequence, Wait, LerpPosInterval, ProjectileInterval, Func, SoundInterval +from direct.actor import Actor +from toontown.toonbase import ToontownGlobals +from toontown.coghq.FoodBeltBase import FoodBeltBase + +class DistributedFoodBelt(DistributedObject.DistributedObject, FSM.FSM, FoodBeltBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFoodBelt') + BeltSpeed = 5 + OnDuration = 300 + ToonupBeltSpeed = 1.0 + BeltActorPlayRate = 5.35 + ToonupBeltActorPlayRate = BeltActorPlayRate * ToonupBeltSpeed / BeltSpeed + ToonupModels = ['phase_6/models/golf/picnic_apple.bam', + 'phase_6/models/golf/picnic_cupcake.bam', + 'phase_6/models/golf/picnic_sandwich.bam', + 'phase_6/models/golf/picnic_chocolate_cake.bam'] + ToonupScales = [5, + 5, + 5, + 4] + ToonupZOffsets = [-0.25, + -0.25, + -0, + -0.25] + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedFoodBelt') + self.boss = None + self.bossCogId = 0 + self.index = -1 + self.foodNodes = [] + self.foodIvals = [] + self.foodWaitTimes = [] + self.foodModelDict = {} + self.foodNum = 0 + self.beltActor = None + self.toonupIvals = [] + self.toonupWaitTimes = [] + self.toonupModelDict = {} + self.toonupNum = 0 + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.cleanup() + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + if self.boss: + self.boss.setBelt(self, self.index) + self.loadAssets() + else: + self.notify.warning('announceGenerate self.boss is None, self.bossCogId = %d' % self.bossCogId) + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do.get(bossCogId) + + def setIndex(self, index): + self.index = index + + def setState(self, state): + if state == 'F': + self.demand('Off') + elif state == 'N': + self.demand('On') + elif state == 'I': + self.demand('Inactive') + elif state == 'T': + self.demand('Toonup') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def enterOn(self): + self.beltSoundInterval.loop() + for i in xrange(len(self.foodNodes)): + self.doMethodLater(self.foodWaitTimes[i], self.startFoodMoving, 'start-%d-%d' % (self.index, i), extraArgs=[i]) + + def exitOn(self): + self.beltSoundInterval.finish() + for i in xrange(len(self.foodNodes)): + taskName = 'start-%d-%d' % (self.index, i) + self.removeTask(taskName) + + def enterToonup(self): + self.beltSound.setPlayRate(self.ToonupBeltSpeed / self.BeltSpeed) + self.beltSoundInterval.loop() + for i in xrange(len(self.foodNodes)): + self.removeFood(i) + self.beltActor.setPlayRate(self.ToonupBeltActorPlayRate, 'idle') + self.doMethodLater(self.toonupWaitTimes[i], self.startToonupMoving, 'startToonup-%d-%d' % (self.index, i), extraArgs=[i]) + + def exitToonup(self): + self.beltSoundInterval.finish() + for i in xrange(len(self.foodNodes)): + taskName = 'startToonup-%d-%d' % (self.index, i) + self.removeTask(taskName) + + def enterInactive(self): + for ival in self.foodIvals: + ival.finish() + + for ival in self.toonupIvals: + ival.finish() + + for i in xrange(len(self.foodNodes)): + self.removeFood(i) + self.removeToonup(i) + + if self.beltActor: + self.beltActor.stop() + + def exitInactive(self): + pass + + def startFoodMoving(self, foodIndex): + if foodIndex < len(self.foodIvals): + self.foodIvals[foodIndex].loop() + else: + self.notify.warning('startFoodMoving invalid index %d' % foodIndex) + if self.beltActor: + self.beltActor.loop('idle') + + def startToonupMoving(self, toonupIndex): + if toonupIndex < len(self.toonupIvals): + self.toonupIvals[toonupIndex].loop() + else: + self.notify.warning('startToonupMoving invalid index %d' % toonupIndex) + if self.beltActor: + self.beltActor.loop('idle') + + def loadAssets(self): + self.beltModel = NodePath('beltModel') + self.beltModel.reparentTo(self.boss.geom) + self.startLocator = self.boss.geom.find('**/conveyer_belt_start_%d' % (self.index + 1)) + self.endLocator = self.boss.geom.find('**/conveyer_belt_end_%d' % (self.index + 1)) + center = (self.startLocator.getPos() + self.endLocator.getPos()) / 2.0 + self.beltHeight = center.getZ() + self.beltHeight += 0.1 + center.setZ(0) + self.beltLength = (self.endLocator.getPos() - self.startLocator.getPos()).length() + self.distBetweenFoodNodes = self.beltLength / self.NumFoodNodes + self.notify.debug('setting beltModelPos to %s' % center) + self.beltModel.setPos(center) + self.setupFoodNodes() + self.setupFoodIvals() + self.setupToonupIvals() + if self.index == 0: + self.beltActorModel = loader.loadModel('phase_12/models/bossbotHQ/food_belt1_model') + else: + self.beltActorModel = loader.loadModel('phase_12/models/bossbotHQ/food_belt2_model') + if self.beltActorModel: + self.beltActor = Actor.Actor(self.beltActorModel) + if self.index == 0: + self.beltActor.loadAnims({'idle': 'phase_12/models/bossbotHQ/food_belt1'}) + else: + self.beltActor.loadAnims({'idle': 'phase_12/models/bossbotHQ/food_belt2'}) + self.beltActor.reparentTo(render) + self.beltActor.setPlayRate(self.BeltActorPlayRate, 'idle') + mesh = self.beltActor.find('**/mesh_tide1') + joint = self.beltActor.find('**/uvj_WakeWhiteTide1') + mesh.setTexProjector(mesh.findTextureStage('default'), joint, self.beltActor) + self.beltActor.setPos(self.startLocator.getPos()) + self.beltSound = base.loadSfx('phase_12/audio/sfx/CHQ_FACT_conveyor_belt.ogg') + self.beltSound.setLoop(1) + self.beltSoundInterval = SoundInterval(self.beltSound, node=self.beltModel, listenerNode=base.localAvatar, seamlessLoop=True, volume=0.25, cutOff=100) + + def cleanup(self): + for i in xrange(len(self.foodNodes)): + taskName = 'start-%d-%d' % (self.index, i) + self.removeTask(taskName) + + for i in xrange(len(self.foodNodes)): + taskName = 'startToonup-%d-%d' % (self.index, i) + self.removeTask(taskName) + + for ival in self.foodIvals: + ival.finish() + + self.foodIvals = [] + for ival in self.toonupIvals: + ival.finish() + + self.toonupIvals = [] + self.beltSoundInterval.finish() + self.beltActor.delete() + self.beltModel = None + self.removeAllTasks() + self.ignoreAll() + return + + def setupFoodNodes(self): + for i in xrange(self.NumFoodNodes): + newPosIndex = self.NumFoodNodes - 1 - i + yPos = -(self.beltLength / 2.0) + newPosIndex * self.distBetweenFoodNodes + newFoodNode = NodePath('foodNode-%d-%d' % (self.index, i)) + newFoodNode.reparentTo(self.beltModel) + newFoodNode.setPos(0, yPos, self.beltHeight) + debugFood = None + if debugFood: + debugFood.setScale(0.1) + debugFood.reparentTo(newFoodNode) + newFoodNode.setH(180) + self.foodNodes.append(newFoodNode) + + return + + def setupFoodIvals(self): + for i in xrange(len(self.foodNodes)): + foodIval = self.createOneFoodIval(self.foodNodes[i]) + self.foodIvals.append(foodIval) + + def createOneFoodIval(self, foodNode): + foodIndex = self.foodNodes.index(foodNode) + waitTimeForOne = self.distBetweenFoodNodes / self.BeltSpeed + waitTime = waitTimeForOne * foodIndex + self.foodWaitTimes.append(waitTime) + totalTimeToTraverseBelt = self.beltLength / self.BeltSpeed + startPosY = -(self.beltLength / 2.0) + endPosY = self.beltLength / 2.0 + retval = Sequence(Func(self.loadFood, foodIndex), LerpPosInterval(foodNode, duration=totalTimeToTraverseBelt, startPos=Point3(0, startPosY, self.beltHeight), pos=Point3(0, endPosY, self.beltHeight)), ProjectileInterval(foodNode, startPos=Point3(0, endPosY, self.beltHeight), startVel=Point3(0, self.BeltSpeed, 0), endZ=0), Func(self.removeFood, foodIndex)) + return retval + + def loadFood(self, foodIndex): + self.foodNum += 1 + if foodIndex in self.foodModelDict: + foodModel = self.foodModelDict[foodIndex] + foodModel.reparentTo(self.foodNodes[foodIndex]) + colNp = foodModel.find('**/FoodCol*') + colNp.setTag('foodNum', str(self.foodNum)) + else: + foodModelScale = ToontownGlobals.BossbotFoodModelScale + foodModel = loader.loadModel('phase_12/models/bossbotHQ/canoffood') + foodModel.setScale(foodModelScale) + foodModel.reparentTo(self.foodNodes[foodIndex]) + target = CollisionTube(4, 0, 0, -4, 0, 0, 2) + target.setTangible(0) + colName = 'FoodCol-%d-%d' % (self.index, foodIndex) + targetNode = CollisionNode(colName) + targetNode.addSolid(target) + targetNode.setCollideMask(ToontownGlobals.WallBitmask) + targetNodePath = foodModel.attachNewNode(targetNode) + targetNodePath.setScale(1.0 / foodModelScale) + targetNodePath.setTag('foodIndex', str(foodIndex)) + targetNodePath.setTag('beltIndex', str(self.index)) + targetNodePath.setTag('foodNum', str(self.foodNum)) + targetNodePath.setZ(targetNodePath.getZ() - 1.5) + self.accept('enter' + colName, self.touchedFood) + self.foodModelDict[foodIndex] = foodModel + + def removeFood(self, foodIndex): + if foodIndex in self.foodModelDict: + foodModel = self.foodModelDict[foodIndex] + foodModel.stash() + + def touchedFood(self, colEntry): + into = colEntry.getIntoNodePath() + try: + beltIndex = int(into.getTag('beltIndex')) + except: + beltIndex = 0 + + try: + foodIndex = int(into.getTag('foodIndex')) + except: + foodIndex = 0 + + try: + foodNum = int(into.getTag('foodNum')) + except: + foodNum = 0 + + if self.boss: + self.boss.localToonTouchedBeltFood(beltIndex, foodIndex, foodNum) + + def setupToonupIvals(self): + for i in xrange(len(self.foodNodes)): + toonupIval = self.createOneToonupIval(self.foodNodes[i]) + self.toonupIvals.append(toonupIval) + + def createOneToonupIval(self, foodNode): + toonupIndex = self.foodNodes.index(foodNode) + waitTimeForOne = self.distBetweenFoodNodes / self.ToonupBeltSpeed + waitTime = waitTimeForOne * toonupIndex + self.toonupWaitTimes.append(waitTime) + totalTimeToTraverseBelt = self.beltLength / self.ToonupBeltSpeed + startPosY = -(self.beltLength / 2.0) + endPosY = self.beltLength / 2.0 + retval = Sequence(Func(self.loadToonup, toonupIndex), LerpPosInterval(foodNode, duration=totalTimeToTraverseBelt, startPos=Point3(0, startPosY, self.beltHeight), pos=Point3(0, endPosY, self.beltHeight)), ProjectileInterval(foodNode, startPos=Point3(0, endPosY, self.beltHeight), startVel=Point3(0, self.BeltSpeed, 0), endZ=0), Func(self.removeToonup, toonupIndex)) + return retval + + def loadToonup(self, toonupIndex): + self.toonupNum += 1 + if toonupIndex in self.toonupModelDict: + toonupModel = self.toonupModelDict[toonupIndex] + toonupModel.reparentTo(self.foodNodes[toonupIndex]) + colNp = toonupModel.find('**/ToonupCol*') + colNp.setTag('toonupNum', str(self.toonupNum)) + else: + toonupModelScale = self.ToonupScales[toonupIndex] + modelName = self.ToonupModels[toonupIndex] + toonupModel = loader.loadModel(modelName) + self.foodNodes[toonupIndex].setZ(self.beltHeight - 0.1) + toonupModel.setZ(self.ToonupZOffsets[toonupIndex]) + toonupModel.setScale(toonupModelScale) + toonupModel.reparentTo(self.foodNodes[toonupIndex]) + target = CollisionTube(4, 0, 0, -4, 0, 0, 2) + target.setTangible(0) + colName = 'ToonupCol-%d-%d' % (self.index, toonupIndex) + targetNode = CollisionNode(colName) + targetNode.addSolid(target) + targetNode.setCollideMask(ToontownGlobals.WallBitmask) + targetNodePath = toonupModel.attachNewNode(targetNode) + targetNodePath.setScale(1.0 / toonupModelScale) + targetNodePath.setTag('toonupIndex', str(toonupIndex)) + targetNodePath.setTag('beltIndex', str(self.index)) + targetNodePath.setTag('toonupNum', str(self.toonupNum)) + targetNodePath.setZ(targetNodePath.getZ() - 1.5 / toonupModelScale) + self.accept('enter' + colName, self.touchedToonup) + self.toonupModelDict[toonupIndex] = toonupModel + + def removeToonup(self, toonupIndex): + if toonupIndex in self.toonupModelDict: + toonupModel = self.toonupModelDict[toonupIndex] + toonupModel.stash() + + def touchedToonup(self, colEntry): + if base.localAvatar.hp >= base.localAvatar.maxHp: + return + into = colEntry.getIntoNodePath() + try: + beltIndex = int(into.getTag('beltIndex')) + except: + beltIndex = 0 + + try: + toonupIndex = int(into.getTag('toonupIndex')) + except: + toonupIndex = 0 + + try: + toonupNum = int(into.getTag('toonupNum')) + except: + toonupNum = 0 + + if self.boss: + self.boss.localToonTouchedBeltToonup(beltIndex, toonupIndex, toonupNum) diff --git a/toontown/coghq/DistributedFoodBeltAI.py b/toontown/coghq/DistributedFoodBeltAI.py new file mode 100755 index 00000000..7cd1cce7 --- /dev/null +++ b/toontown/coghq/DistributedFoodBeltAI.py @@ -0,0 +1,68 @@ +from direct.distributed import DistributedObjectAI +from direct.fsm import FSM +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import FoodBeltBase + +class DistributedFoodBeltAI(DistributedObjectAI.DistributedObjectAI, FSM.FSM, FoodBeltBase.FoodBeltBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFoodBeltAI') + + def __init__(self, air, boss, index): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedFoodBeltAI') + self.boss = boss + self.index = index + + def delete(self): + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def setState(self, state): + self.request(state) + + def d_setState(self, state): + newState = state + if state == 'On': + newState = 'N' + elif state == 'Off': + newState = 'F' + elif state == 'Inactive': + newState = 'I' + elif state == 'Toonup': + newState = 'T' + self.sendUpdate('setState', [newState]) + + def b_setState(self, state): + self.request(state) + self.d_setState(state) + + def turnOn(self): + self.b_setState('On') + + def goInactive(self): + self.b_setState('Inactive') + + def goToonup(self): + self.b_setState('Toonup') + + def enterOn(self): + pass + + def exitOn(slef): + pass + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterInactive(self): + pass + + def exitInactive(slef): + pass diff --git a/toontown/coghq/DistributedGagBarrel.py b/toontown/coghq/DistributedGagBarrel.py new file mode 100755 index 00000000..93063ba9 --- /dev/null +++ b/toontown/coghq/DistributedGagBarrel.py @@ -0,0 +1,63 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +import DistributedBarrelBase + +class DistributedGagBarrel(DistributedBarrelBase.DistributedBarrelBase): + + def __init__(self, cr): + self.gagLevelMax = 0 + DistributedBarrelBase.DistributedBarrelBase.__init__(self, cr) + self.numGags = 0 + self.gagScale = 13.0 + + def disable(self): + DistributedBarrelBase.DistributedBarrelBase.disable(self) + self.ignoreAll() + + def delete(self): + if hasattr(self, 'gagModel') and self.gagModel: + self.gagModel.removeNode() + del self.gagModel + DistributedBarrelBase.DistributedBarrelBase.delete(self) + + def applyLabel(self): + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + self.invModels = [] + from toontown.toonbase import ToontownBattleGlobals + for gagTrack in xrange(len(ToontownBattleGlobals.AvPropsNew)): + itemList = [] + for item in xrange(len(ToontownBattleGlobals.AvPropsNew[gagTrack])): + itemList.append(invModel.find('**/' + ToontownBattleGlobals.AvPropsNew[gagTrack][item])) + + self.invModels.append(itemList) + + invModel.removeNode() + del invModel + try: + gagTrack = self.getGagTrack() + gagLevel = self.getGagLevel() + self.notify.debug('gagTrack = %s, gagLevel = %s' % (gagTrack, gagLevel)) + self.gagModel = self.invModels[gagTrack][gagLevel] + self.gagModel.reparentTo(self.gagNode) + self.gagModel.setScale(self.gagScale) + self.gagModel.setPos(0, -0.1, 0) + except AttributeError: + self.notify.warning("Gag barrel is missing an attribute, can't apply label.") + + def setNumGags(self, num): + self.numGags = num + if hasattr(self, 'gagModel') and self.gagModel: + if self.numGags == 0: + self.gagModel.setColorScale(0.5, 0.5, 0.5, 1) + else: + self.gagModel.clearColorScale() + + def setGrab(self, avId): + DistributedBarrelBase.DistributedBarrelBase.setGrab(self, avId) + + def resetBarrel(self): + DistributedBarrelBase.DistributedBarrelBase.resetBarrel(self) + if hasattr(self, 'gagModel') and self.gagModel: + self.gagModel.setScale(self.gagScale) diff --git a/toontown/coghq/DistributedGagBarrelAI.py b/toontown/coghq/DistributedGagBarrelAI.py new file mode 100755 index 00000000..c7804029 --- /dev/null +++ b/toontown/coghq/DistributedGagBarrelAI.py @@ -0,0 +1,33 @@ +from toontown.toonbase.ToontownBattleGlobals import * +import DistributedBarrelBaseAI +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task + +class DistributedGagBarrelAI(DistributedBarrelBaseAI.DistributedBarrelBaseAI): + + def __init__(self, level, entId): + x = y = z = h = 0 + self.gagLevelMax = 0 + DistributedBarrelBaseAI.DistributedBarrelBaseAI.__init__(self, level, entId) + + def d_setGrab(self, avId): + self.notify.debug('d_setGrab %s' % avId) + self.sendUpdate('setGrab', [avId]) + av = self.air.doId2do.get(avId) + if av: + if not av.hasTrackAccess(self.getGagTrack()): + return + track = self.getGagTrack() + level = self.getGagLevel() + maxGags = av.getMaxCarry() + av.inventory.calcTotalProps() + numGags = av.inventory.totalProps + numReward = min(self.getRewardPerGrab(), maxGags - numGags) + while numReward > 0 and level >= 0: + result = av.inventory.addItem(track, level) + if result <= 0: + level -= 1 + else: + numReward -= 1 + + av.d_setInventory(av.inventory.makeNetString()) diff --git a/toontown/coghq/DistributedGolfGreenGame.py b/toontown/coghq/DistributedGolfGreenGame.py new file mode 100755 index 00000000..25d4c63f --- /dev/null +++ b/toontown/coghq/DistributedGolfGreenGame.py @@ -0,0 +1,1463 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect +from StomperGlobals import * +from direct.distributed import ClockDelta +from direct.showbase.PythonUtil import lerp +from otp.level import DistributedEntity +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath +from otp.level import BasicEntities +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from toontown.coghq import BattleBlocker +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownBattleGlobals +from direct.distributed.ClockDelta import * +from toontown.golf import BuildGeometry +from direct.gui.DirectGui import * +from direct.showbase import RandomNumGen +from toontown.distributed import DelayDelete +from toontown.toon import ToonHeadFrame +from toontown.battle import BattleParticles +from toontown.battle import MovieUtil +from toontown.toonbase import ToontownTimer +from math import pi +import GameSprite3D, math, random + +class DistributedGolfGreenGame(BattleBlocker.BattleBlocker): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGolfGreenGame') + + def __init__(self, cr): + BattleBlocker.BattleBlocker.__init__(self, cr) + self.blankColor = Vec4(1.0, 1.0, 1.0, 1.0) + self.fullColor = Vec4(0.6, 0.6, 0.6, 1.0) + self.neighborColor = Vec4(0.8, 0.8, 0.8, 1.0) + self.outColor = Vec4(0.0, 0.0, 0.0, 0.0) + self.blackColor = Vec4(0.0, 0.0, 0.0, 1.0) + self.acceptErrorDialog = None + self.doneEvent = 'game Done' + self.sprites = [] + self.controlSprite = None + self.standbySprite = None + self.setupFlag = 0 + self.colorGridFlag = 0 + self.boardIndex = None + self.board = None + self.attackPattern = None + self.tooLowFlag = 0 + self.toonPoints = (Point3(3.0, 13.0, 0.0), + Point3(6.0, 13.0, 0.0), + Point3(-3.0, 13.0, 0.0), + Point3(-6.0, 13.0, 0.0)) + self.joinedToons = [] + self.everJoinedToons = [] + self.flagNextLevel = 0 + self.wildIndex = 8 + self.bombIndex = 7 + self.sizeMult = 1.4 + self.cellSizeX = 1.0 * self.sizeMult + self.cellSizeZ = self.cellSizeX * 0.8 + self.radiusBall = 0.5 * self.cellSizeX + self.gridDimX = 9 + self.gridDimZ = 15 + self.minX = -1.0 * (self.gridDimX + 0.3751) * 0.5 * self.cellSizeX + self.minZ = -self.gridDimZ * 0.1 * self.cellSizeZ + self.newBallX = 0.0 + self.newBallZ = self.minZ + 0.1 * self.sizeMult + self.rangeX = (self.gridDimX + 0.5) * self.cellSizeX + self.rangeZ = self.gridDimZ * self.cellSizeZ + self.maxX = self.minX + self.rangeX + self.maxZ = self.minZ + self.rangeZ + self.sizeX = self.rangeX + self.sizeZ = self.rangeZ + self.isActive = 0 + self.boardsLeft = 0 + self.timeLeft = 0 + self.timeStart = None + self.timeTotal = None + self.timerTask = None + self.timerTaskName = 'golfgreengame timer task' + self.giftId = None + self.holdGiftId = None + self.rollTrack = None + self.zGap = 0.092 + self.screenSizeX = base.a2dRight - base.a2dLeft + self.screenSizeZ = base.a2dTop - base.a2dBottom + self.XtoZ = self.screenSizeX / (self.screenSizeZ * (1.0 - self.zGap * 1.0)) + self.countTimeOld = None + self.countDownRunning = 0 + self.timer = None + self.hasEntered = 0 + self.trackClosed = 0 + self.running = 0 + self.finished = 0 + self.__toonTracks = {} + return + + def disable(self): + self.unload() + self.clearToonTracks() + BattleBlocker.BattleBlocker.disable(self) + + def updateSpritePos(self): + if self.spriteNode.isEmpty(): + return + self.spriteNode.setZ(-self.spriteNotchPos * self.cellSizeZ) + if self.controlSprite: + if not self.controlSprite.isActive: + pass + self.colorGridFlag = 1 + + def lerpSpritePos(self): + if self.spriteNode.isEmpty(): + return + x = self.spriteNode.getX() + y = self.spriteNode.getY() + self.rollTrack = Sequence(LerpPosInterval(self.spriteNode, 0.5, Point3(x, y, -self.spriteNotchPos * self.cellSizeZ))) + if self.controlSprite: + if not self.controlSprite.isActive: + pass + self.colorGridFlag = 1 + self.rollTrack.start() + if self.soundMove: + self.soundMove.play() + messenger.send('wakeup') + + def findLowestSprite(self): + lowest = 100 + for sprite in self.sprites: + if sprite.gridPosZ: + if sprite.gridPosZ < lowest: + lowest = sprite.gridPosZ + + return lowest + + def setup(self): + if not self.setupFlag: + self.setupFlag = 1 + else: + return + self.updateSpritePos() + self.spriteNode.setY(self.radiusBall) + thing = self.model.find('**/item_board') + self.block = self.model1.find('**/minnieCircle') + self.colorRed = (1, 0, 0, 1) + self.colorBlue = (0, 0, 1, 1) + self.colorGreen = (0, 1, 0, 1) + self.colorGhostRed = (1, 0, 0, 0.5) + self.colorGhostBlue = (0, 0, 1, 0.5) + self.colorGhostGreen = (0, 1, 0, 0.5) + self.colorWhite = (1, 1, 1, 1) + self.colorBlack = (0, 0, 0, 1.0) + self.colorShadow = (0, 0, 0, 0.5) + self.lastTime = None + self.running = 0 + self.massCount = 0 + self.foundCount = 0 + self.controlOffsetX = 0.0 + self.controlOffsetZ = 0.0 + self.grid = [] + for countX in xrange(0, self.gridDimX): + newRow = [] + for countZ in xrange(self.gridDimZ): + offset = 0 + margin = self.cellSizeX * 0.4375 + if countZ % 2 == 0: + offset = self.cellSizeX * 0.5 + newCell = [None, + countX * self.cellSizeX + self.minX + offset + margin, + countZ * self.cellSizeZ + self.minZ, + countX, + countZ, + None] + groundCircle = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_hole') + groundCircle.reparentTo(self.spriteNode) + groundCircle.setTransparency(TransparencyAttrib.MAlpha) + groundCircle.setPos(newCell[1], -self.radiusBall, newCell[2]) + groundCircle.setScale(1.2) + groundCircle.setR(90) + groundCircle.setH(-90) + newCell[5] = groundCircle + newCell[5].setColorScale(self.blankColor) + newRow.append(newCell) + + self.grid.append(newRow) + + self.cogSprite = self.addUnSprite(self.block, posX=0.25, posZ=0.5) + self.cogSprite.setColor(self.colorShadow) + self.cogSprite.nodeObj.hide() + self.standbySprite = self.addUnSprite(self.block, posX=0.0, posZ=-3.0) + self.standbySprite.setColor(self.colorShadow) + self.standbySprite.spriteBase.reparentTo(self.frame) + self.standbySprite.spriteBase.setY(self.radiusBall) + self.standbySprite.nodeObj.hide() + self.boardData = [((1, 0, 0), + (4, 0, 1), + (6, 0, 2), + (1, 1, 0)), ((1, 0, 1), + (4, 0, 1), + (6, 0, 1), + (1, 1, 1)), ((1, 0, 2), + (4, 0, 2), + (6, 0, 2), + (1, 1, 2))] + self.attackPatterns = [(0, 1, 2), (0, 0, 1, 1, 2, 2), (0, 1, 0, 2)] + self.winCounter = 0 + self.matchList = [] + self.newBallTime = 5.0 + self.newBallCountUp = 0.0 + self.cogX = 0 + self.cogZ = 0 + self.aimRadian = 0.0 + self.ballLoaded = 0.0 + self.countTime = 10 + self.countDown = self.countTime + return + + def printGrid(self): + printout = ' ' + for columnIndex in xrange(self.gridDimX - 1, -1, -1): + if columnIndex < 10: + printout += '%s ' % columnIndex + else: + printout += '%s ' % columnIndex + + print printout + for rowIndex in xrange(self.gridDimZ - 1, -1, -1): + if rowIndex < 10: + printout = 'row %s ' % rowIndex + else: + printout = 'row %s ' % rowIndex + for columnIndex in xrange(self.gridDimX - 1, -1, -1): + hasSprite = '_' + if self.grid[columnIndex][rowIndex][0]: + hasSprite = 'X' + if rowIndex < 10: + printout += '%s ' % hasSprite + else: + printout += '%s ' % hasSprite + + print printout + + count = 0 + for sprite in self.sprites: + print 'count %s X %s Z %s Color %s' % (count, + sprite.gridPosX, + sprite.gridPosZ, + sprite.colorType) + count += 1 + + def pickLevelPattern(self): + self.boardIndex = random.choice(range(0, len(self.boardData))) + self.board = self.boardData[self.boardIndex] + self.attackPattern = self.attackPatterns[self.boardIndex] + self.attackCounter = 0 + self.spriteNotchPos = 0 + for ball in self.board: + newSprite = self.addSprite(self.block, found=1, color=ball[2]) + self.placeIntoGrid(newSprite, ball[0], self.gridDimZ - 1 - ball[1]) + + self.colorGridFlag = 1 + self.updateSpritePos() + + def load(self): + BattleParticles.loadParticles() + model = loader.loadModel('phase_5.5/models/gui/package_delivery_panel') + model1 = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + self.invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + self.model = model + self.model1 = model1 + self.soundFire = base.loadSfx('phase_6/audio/sfx/Golf_Hit_Ball.ogg') + self.soundLand = base.loadSfx('phase_4/audio/sfx/MG_maze_pickup.ogg') + self.soundBurst = base.loadSfx('phase_5/audio/sfx/Toon_bodyfall_synergy.ogg') + self.soundBomb = base.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.ogg') + self.soundLose = base.loadSfx('phase_11/audio/sfx/LB_capacitor_discharge_3.ogg') + self.soundWin = base.loadSfx('phase_4/audio/sfx/MG_pairing_match_bonus_both.ogg') + self.soundDone = base.loadSfx('phase_3/audio/sfx/GUI_create_toon_back.ogg') + self.soundMove = base.loadSfx('phase_3.5/audio/sfx/SA_shred.ogg') + background = model.find('**/bg') + itemBoard = model.find('**/item_board') + self.focusPoint = self.baseNode.attachNewNode('GolfGreenGameFrame') + self.frame2D = DirectFrame(scale=1.1, relief=DGG.FLAT, frameSize=(-0.1, + 0.1, + -0.1, + -0.1), frameColor=(0.737, 0.573, 0.345, 0.3)) + gui2 = loader.loadModel('phase_3/models/gui/quit_button') + self.quitButton = DirectButton(parent=self.frame2D, relief=None, image=(gui2.find('**/QuitBtn_UP'), gui2.find('**/QuitBtn_DN'), gui2.find('**/QuitBtn_RLVR')), pos=(0.95, 1.3, -0.69), image_scale=(0.9, 1.0, 1.0), text=TTLocalizer.BustACogExit, text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text0_shadow=(0, 0, 0, 1), text1_fg=(1, 1, 1, 1), text2_fg=(1, 1, 1, 1), text_scale=TTLocalizer.DGGGquitButton, text_pos=(0, -0.01), command=self.__leaveGame) + self.quitButton.hide() + self.instructions = DirectFrame(parent=self.frame2D, relief=None, image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.2, 1.0, 1.0), text=TTLocalizer.GolfGreenGameDirections, text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft, text_wordwrap=16, text_scale=0.06, text_pos=(-0.5, 0.3), pos=(0.0, 0, -0.0)) + self.instructions.hide() + imageCogBall = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_ball_cog') + imageCogBall.setHpr(0, 90, 0) + self.instCogBall = DirectFrame(parent=self.instructions, relief=None, image=imageCogBall, image_color=ToontownGlobals.GlobalDialogColor, image_scale=(0.12, 0.12, 0.12), pos=(0.0, 0, -0.2)) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.doneButton = DirectButton(parent=self.instructions, relief=None, image=cancelImageList, command=self.instructions.hide, pos=(0, 0, -0.4)) + self.howToButton = DirectButton(parent=self.frame2D, relief=None, image=(gui2.find('**/QuitBtn_UP'), gui2.find('**/QuitBtn_DN'), gui2.find('**/QuitBtn_RLVR')), pos=(0.95, 1.3, -0.82), image_scale=(0.9, 1.0, 1.0), text=TTLocalizer.BustACogHowto, text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text0_shadow=(0, 0, 0, 1), text1_fg=(1, 1, 1, 1), text2_fg=(1, 1, 1, 1), text_scale=TTLocalizer.DGGGhowToButton, text_pos=(0, -0.01), command=self.instructions.show) + self.howToButton.hide() + self.timerLabel = DirectLabel(parent=self.frame2D, relief=None, image=gui2.find('**/QuitBtn_UP'), pos=(0.9, 1.3, -0.42), image_scale=(0.5, 1.0, 1.0), text='Timer', text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text_scale=0.045, text_pos=(0, -0.01)) + self.timerLabel.hide() + self.headPanel = loader.loadModel('phase_6/models/golf/headPanel') + self.scoreBoard = DirectFrame(scale=1.0, pos=(0.0, 0, 0.9), relief=DGG.FLAT, parent=aspect2d, frameSize=(-0.35, + 0.35, + -0.05, + 0.05), frameColor=(0.737, 0.573, 0.345, 0.3)) + self.scoreLabel = DirectLabel(parent=self.scoreBoard, relief=None, pos=(0, 0, 0), scale=1.0, text='', text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text0_shadow=(0.0, 0.0, 0.0, 1), text_scale=TTLocalizer.DGGGscoreLabel, text_pos=(0, -0.02)) + self.scoreBoard.hide() + self.bonusBoard = DirectFrame(parent=self.frame2D, relief=None, image_pos=(0, 0, 0.0), image_scale=(0.4, 1, 0.4), image_color=(1, 1, 1, 1), pos=(0.0, 1.5, 0.67), scale=1.0, text='You gotsa bonus fool!', text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text0_shadow=(0.0, 0.0, 0.0, 1), text_scale=0.055, text_pos=(0, -0.1), textMayChange=1) + self.bonusBoard.hide() + self.backBoard = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_background') + self.backBoard.setCollideMask(BitMask32.allOff()) + self.backBoard.reparentTo(self.frame) + self.backBoard.setScale(0.3, 0.2, 0.25) + self.backBoard.setHpr(0, -90, 0) + self.backBoard.setPos(0, -1.5, 8.0) + self.backBoard.hide() + base.bb = self.backBoard + self.aimbase = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_shooter') + self.aimbase.setHpr(90, 0, 90) + self.aimbase.setScale(0.3, 0.3, 0.15) + self.aimbase.reparentTo(self.frame) + self.aimbase.setPos(0.0, 0.0, self.minZ + 0.1) + self.aimer = self.aimbase.attachNewNode('GolfGreenGameBase') + aimer = self.aimbase.find('**/moving*') + aimer.reparentTo(self.aimer) + aimer.setPos(0.0, 0.0, 0.0) + base.gi = aimer + self.aimbase.hide() + self.toonPanels = {} + return + + def addToonHeadPanel(self, toon): + tPanels = ToonHeadFrame.ToonHeadFrame(toon, (0.4, 0.4, 0.4, 0.6), self.headPanel) + tPanels.extraData['text_fg'] = (1.0, 1.0, 1.0, 1.0) + tPanels.extraData['text_shadow'] = (0.0, 0.0, 0.0, 1.0) + tPanels.extraData.show() + tPanels.setScale(0.3, 1, 0.7) + tPanels.head.setPos(0, 10, 0.18) + tPanels.head.setScale(0.47, 0.2, 0.2) + tPanels.tag1.setPos(0.3, 10, 0.18) + tPanels.tag1.setScale(0.1283, 0.055, 0.055) + tPanels.tag2.setPos(0, 10, 0.43) + tPanels.tag2.setScale(0.117, 0.05, 0.05) + tPanels.hide() + self.toonPanels[toon.doId] = tPanels + self.arrangeToonHeadPanels() + + def removeToonHeadPanel(self, avId): + if avId in self.toonPanels: + self.toonPanels[avId].destroy() + del self.toonPanels[avId] + self.arrangeToonHeadPanels() + + def arrangeToonHeadPanels(self): + toonPanelsStart = 0.0 + whichToon = 0 + color = 0 + tpDiff = -0.45 + for panelKey in self.toonPanels: + panel = self.toonPanels[panelKey] + if self.isActive: + panel.show() + else: + panel.hide() + if whichToon <= 1: + panel.setPos(-1, 0, toonPanelsStart + whichToon * tpDiff) + else: + panel.setPos(1, 0, toonPanelsStart + (whichToon - 2) * tpDiff) + whichToon += 1 + + def unload(self): + self.cleanupTimer() + for panelKey in self.toonPanels: + self.toonPanels[panelKey].destroy() + + self.headPanel.removeNode() + self.toonPanels = None + self.soundFire = None + self.soundLand = None + self.soundBurst = None + self.soundBomb = None + self.soundLose = None + self.soundWin = None + self.soundDone = None + self.soundMove = None + self.scoreBoard.destroy() + self.instructions.destroy() + self.frame2D.destroy() + self.baseNode.removeNode() + del self.baseNode + if self.acceptErrorDialog: + self.acceptErrorDialog.cleanup() + self.acceptErrorDialog = None + self.stopCountDown() + self.__stop() + self.ignoreAll() + return + + def show(self): + self.frame.show() + + def hide(self): + self.frame.hide() + + def __handleExit(self): + self.__acceptExit() + + def __startGame(self): + if not self.setupFlag: + self.setup() + self.quitButton.show() + self.howToButton.show() + self.backBoard.show() + self.aimbase.show() + self.squareNode.show() + self.scoreBoard.show() + self.standbySprite.nodeObj.show() + self.groundFlag.hide() + self.isActive = 1 + self.__setCamera() + self.spriteNode.show() + base.setCellsAvailable([base.bottomCells[1], base.bottomCells[2], base.bottomCells[3]], 0) + self.setupFlag = 1 + + def startBoard(self, board, attackPattern): + if self.finished: + return + self.clearGrid() + self.board = board + self.attackPattern = attackPattern + self.attackCounter = 0 + self.spriteNotchPos = 0 + self.countDown = self.countTime + self.tooLowFlag = 0 + for ball in self.board: + newSprite = self.addSprite(self.block, found=1, color=ball[2]) + self.placeIntoGrid(newSprite, ball[0], self.gridDimZ - 1 - ball[1]) + + self.colorGridFlag = 1 + self.tooLowFlag = 0 + self.startCountDown() + self.updateSpritePos() + self.killSprite(self.controlSprite) + self.accept('mouse1', self.__handleMouseClick) + self.__run() + + def startCountDown(self): + if self.countDownRunning == 0: + taskMgr.add(self.doCountDown, 'GolfGreenGame countdown') + self.countDownRunning = 1 + + def stopCountDown(self): + taskMgr.remove('GolfGreenGame countdown') + self.countDownRunning = 0 + self.countTimeOld = None + return + + def doCountDown(self, task): + currentTime = globalClock.getFrameTime() + if self.countTimeOld == None: + self.countTimeOld = currentTime + if currentTime - self.countTimeOld < 1.0: + return task.cont + else: + self.countTimeOld = currentTime + self.countDown -= 1 + if self.countDown in [3, 2, 1]: + for sprite in self.sprites: + sprite.warningBump() + + elif self.countDown == 0: + self.countDown = self.countTime + self.spriteNotchPos += 1 + self.lerpSpritePos() + self.checkForTooLow() + self.timerLabel['text'] = '%s' % self.countDown + return task.cont + return + + def checkForTooLow(self): + low = self.findLowestSprite() + if low <= self.spriteNotchPos: + self.doFail() + + def doFail(self): + self.tooLowFlag = 1 + taskMgr.doMethodLater(1.0, self.failBoard, 'finishing Failure') + for sprite in self.sprites: + sprite.setColorType(4) + + self.__stop() + self.ignore('mouse1') + + def failBoard(self, task = None): + self.__finishBoard(0) + + def __handleWin(self): + self.__handleExit() + + def __finishBoard(self, success = 1): + if self.rollTrack: + self.rollTrack.finish() + self.countDown = self.countTime + if success: + if self.soundWin: + self.soundWin.play() + elif self.soundLose: + self.soundLose.play() + self.giftId = None + self.attackPattern = None + self.stopCountDown() + self.clearGrid() + self.spriteNotchPos = 0 + self.updateSpritePos() + self.__stop() + self.ignore('mouse1') + if not self.tooLowFlag or 1: + self.sendUpdate('requestBoard', [success]) + return + + def __acceptExit(self, buttonValue = None): + if hasattr(self, 'frame'): + self.hide() + self.unload() + messenger.send(self.doneEvent) + camera.reparentTo(base.localAvatar) + base.localAvatar.startUpdateSmartCamera() + + def __removeGame(self): + self.spriteNode.removeNode() + self.setupFlag = 0 + + def __leaveGame(self): + taskMgr.remove('GolfGreenGameTask') + self.stopCountDown() + taskMgr.remove(self.timerTaskName) + self.ignore('mouse1') + camera.reparentTo(base.localAvatar) + base.localAvatar.startUpdateSmartCamera() + base.cr.playGame.getPlace().fsm.request('walk') + for sprite in self.sprites: + sprite.delete() + + self.sprites = [] + if self.spriteNode: + self.spriteNode.hide() + self.controlSprite = None + self.running = 0 + self.timerLabel.hide() + self.quitButton.hide() + self.howToButton.hide() + self.backBoard.hide() + self.aimbase.hide() + self.squareNode.hide() + self.groundFlag.show() + self.instructions.hide() + self.isActive = 0 + if self.standbySprite: + self.standbySprite.nodeObj.hide() + base.setCellsAvailable([base.bottomCells[1], base.bottomCells[2], base.bottomCells[3]], 1) + self.sendUpdate('leaveGame', []) + return + + def findGrid(self, x, z, force = 0): + currentClosest = None + currentDist = 10000000 + for countX in xrange(self.gridDimX): + for countZ in xrange(self.gridDimZ): + testDist = self.testPointDistanceSquare(x, z, self.grid[countX][countZ][1], self.grid[countX][countZ][2]) + if self.grid[countX][countZ][0] == None and testDist < currentDist and (force or self.hasNeighbor(countX, countZ) != None): + currentClosest = self.grid[countX][countZ] + self.closestX = countX + self.closestZ = countZ + currentDist = testDist + + return currentClosest + + def hasNeighbor(self, cellX, cellZ): + gotNeighbor = None + if cellZ % 2 == 0: + if self.testGridfull(self.getValidGrid(cellX - 1, cellZ)): + gotNeighbor = cellZ + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ)): + gotNeighbor = cellZ + elif self.testGridfull(self.getValidGrid(cellX, cellZ + 1)): + gotNeighbor = cellZ + 1 + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ + 1)): + gotNeighbor = cellZ + 1 + elif self.testGridfull(self.getValidGrid(cellX, cellZ - 1)): + gotNeighbor = cellZ - 1 + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ - 1)): + gotNeighbor = cellZ - 1 + elif self.testGridfull(self.getValidGrid(cellX - 1, cellZ)): + gotNeighbor = cellZ + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ)): + gotNeighbor = cellZ + elif self.testGridfull(self.getValidGrid(cellX, cellZ + 1)): + gotNeighbor = cellZ + 1 + elif self.testGridfull(self.getValidGrid(cellX - 1, cellZ + 1)): + gotNeighbor = cellZ + 1 + elif self.testGridfull(self.getValidGrid(cellX, cellZ - 1)): + gotNeighbor = cellZ - 1 + elif self.testGridfull(self.getValidGrid(cellX - 1, cellZ - 1)): + gotNeighbor = cellZ - 1 + return gotNeighbor + + def clearFloaters(self): + self.grounded = [] + self.unknown = [] + groundZ = self.gridDimZ - 1 + for indexX in xrange(0, self.gridDimX): + gridCell = self.grid[indexX][groundZ] + if gridCell[0]: + self.grounded.append((indexX, groundZ)) + + for column in self.grid: + for cell in column: + if cell[0] != None: + cellData = (cell[3], cell[4]) + if cellData not in self.grounded: + self.unknown.append(cellData) + + lastUnknownCount = 0 + while len(self.unknown) != lastUnknownCount: + lastUnknownCount = len(self.unknown) + for cell in self.unknown: + if self.hasGroundedNeighbor(cell[0], cell[1]): + self.unknown.remove(cell) + self.grounded.append(cell) + + for entry in self.unknown: + gridEntry = self.grid[entry[0]][entry[1]] + sprite = gridEntry[0] + self.killSprite(sprite) + + return + + def explodeBombs(self): + didBomb = 0 + for column in self.grid: + for cell in column: + if cell[0] != None: + if cell[0].colorType == self.bombIndex: + self.killSprite(cell[0]) + didBomb += 1 + + if didBomb: + self.soundBomb.play() + return + + def hasGroundedNeighbor(self, cellX, cellZ): + gotNeighbor = None + if cellZ % 2 == 0: + if (cellX - 1, cellZ) in self.grounded: + gotNeighbor = cellZ + elif (cellX + 1, cellZ) in self.grounded: + gotNeighbor = cellZ + elif (cellX, cellZ + 1) in self.grounded: + gotNeighbor = cellZ + 1 + elif (cellX + 1, cellZ + 1) in self.grounded: + gotNeighbor = cellZ + 1 + elif (cellX, cellZ - 1) in self.grounded: + gotNeighbor = cellZ - 1 + elif (cellX + 1, cellZ - 1) in self.grounded: + gotNeighbor = cellZ - 1 + elif (cellX - 1, cellZ) in self.grounded: + gotNeighbor = cellZ + elif (cellX + 1, cellZ) in self.grounded: + gotNeighbor = cellZ + elif (cellX, cellZ + 1) in self.grounded: + gotNeighbor = cellZ + 1 + elif (cellX - 1, cellZ + 1) in self.grounded: + gotNeighbor = cellZ + 1 + elif (cellX, cellZ - 1) in self.grounded: + gotNeighbor = cellZ - 1 + elif (cellX - 1, cellZ - 1) in self.grounded: + gotNeighbor = cellZ - 1 + return gotNeighbor + + def clearMatchList(self, typeClear = 0): + self.soundBurst.play() + for entry in self.matchList: + gridEntry = self.grid[entry[0]][entry[1]] + sprite = gridEntry[0] + if typeClear == self.wildIndex: + self.questionSprite(sprite) + elif typeClear == 0: + pass + self.killSprite(sprite) + + def shakeList(self, neighbors): + for entry in neighbors: + gridEntry = self.grid[entry[0]][entry[1]] + sprite = gridEntry[0] + self.shakeSprite(sprite) + + def createMatchList(self, x, z): + self.matchList = [] + self.fillMatchList(x, z) + + def matchWild(self, x, z, color): + spriteType = self.getColorType(x, z) + if not self.getBreakable(x, z): + return 0 + elif spriteType != -1 and spriteType == self.wildIndex: + return 1 + elif spriteType != -1 and color == self.wildIndex: + return 1 + else: + return 0 + + def bombNeighbors(self, cellX, cellZ): + self.soundBomb.play() + self.matchList = [] + if cellZ % 2 == 0: + if self.getColorType(cellX - 1, cellZ) != -1: + self.addToMatchList(cellX - 1, cellZ) + if self.getColorType(cellX + 1, cellZ) != -1: + self.addToMatchList(cellX + 1, cellZ) + if self.getColorType(cellX, cellZ + 1) != -1: + self.addToMatchList(cellX, cellZ + 1) + if self.getColorType(cellX + 1, cellZ + 1) != -1: + self.addToMatchList(cellX + 1, cellZ + 1) + if self.getColorType(cellX, cellZ - 1) != -1: + self.addToMatchList(cellX, cellZ - 1) + if self.getColorType(cellX + 1, cellZ - 1) != -1: + self.addToMatchList(cellX + 1, cellZ - 1) + else: + if self.getColorType(cellX - 1, cellZ) != -1: + self.addToMatchList(cellX - 1, cellZ) + if self.getColorType(cellX + 1, cellZ) != -1: + self.addToMatchList(cellX + 1, cellZ) + if self.getColorType(cellX, cellZ + 1) != -1: + self.addToMatchList(cellX, cellZ + 1) + if self.getColorType(cellX - 1, cellZ + 1) != -1: + self.addToMatchList(cellX - 1, cellZ + 1) + if self.getColorType(cellX, cellZ - 1) != -1: + self.addToMatchList(cellX, cellZ - 1) + if self.getColorType(cellX - 1, cellZ - 1) != -1: + self.addToMatchList(cellX - 1, cellZ - 1) + + def addToMatchList(self, posX, posZ): + if self.getBreakable(posX, posZ) > 0: + self.matchList.append((posX, posZ)) + + def getNeighbors(self, cellX, cellZ): + neighborList = [] + if cellZ % 2 == 0: + if self.getColorType(cellX - 1, cellZ) != -1: + neighborList.append((cellX - 1, cellZ)) + if self.getColorType(cellX + 1, cellZ) != -1: + neighborList.append((cellX + 1, cellZ)) + if self.getColorType(cellX, cellZ + 1) != -1: + neighborList.append((cellX, cellZ + 1)) + if self.getColorType(cellX + 1, cellZ + 1) != -1: + neighborList.append((cellX + 1, cellZ + 1)) + if self.getColorType(cellX, cellZ - 1) != -1: + neighborList.append((cellX, cellZ - 1)) + if self.getColorType(cellX + 1, cellZ - 1) != -1: + neighborList.append((cellX + 1, cellZ - 1)) + else: + if self.getColorType(cellX - 1, cellZ) != -1: + neighborList.append((cellX - 1, cellZ)) + if self.getColorType(cellX + 1, cellZ) != -1: + neighborList.append((cellX + 1, cellZ)) + if self.getColorType(cellX, cellZ + 1) != -1: + neighborList.append((cellX, cellZ + 1)) + if self.getColorType(cellX - 1, cellZ + 1) != -1: + neighborList.append((cellX - 1, cellZ + 1)) + if self.getColorType(cellX, cellZ - 1) != -1: + neighborList.append((cellX, cellZ - 1)) + if self.getColorType(cellX - 1, cellZ - 1) != -1: + neighborList.append((cellX - 1, cellZ - 1)) + return neighborList + + def fillMatchList(self, cellX, cellZ): + if (cellX, cellZ) in self.matchList: + return + self.matchList.append((cellX, cellZ)) + colorType = self.grid[cellX][cellZ][0].colorType + if colorType == 4: + return + if cellZ % 2 == 0: + if self.getColorType(cellX - 1, cellZ) == colorType or self.matchWild(cellX - 1, cellZ, colorType): + self.fillMatchList(cellX - 1, cellZ) + if self.getColorType(cellX + 1, cellZ) == colorType or self.matchWild(cellX + 1, cellZ, colorType): + self.fillMatchList(cellX + 1, cellZ) + if self.getColorType(cellX, cellZ + 1) == colorType or self.matchWild(cellX, cellZ + 1, colorType): + self.fillMatchList(cellX, cellZ + 1) + if self.getColorType(cellX + 1, cellZ + 1) == colorType or self.matchWild(cellX + 1, cellZ + 1, colorType): + self.fillMatchList(cellX + 1, cellZ + 1) + if self.getColorType(cellX, cellZ - 1) == colorType or self.matchWild(cellX, cellZ - 1, colorType): + self.fillMatchList(cellX, cellZ - 1) + if self.getColorType(cellX + 1, cellZ - 1) == colorType or self.matchWild(cellX + 1, cellZ - 1, colorType): + self.fillMatchList(cellX + 1, cellZ - 1) + else: + if self.getColorType(cellX - 1, cellZ) == colorType or self.matchWild(cellX - 1, cellZ, colorType): + self.fillMatchList(cellX - 1, cellZ) + if self.getColorType(cellX + 1, cellZ) == colorType or self.matchWild(cellX + 1, cellZ, colorType): + self.fillMatchList(cellX + 1, cellZ) + if self.getColorType(cellX, cellZ + 1) == colorType or self.matchWild(cellX, cellZ + 1, colorType): + self.fillMatchList(cellX, cellZ + 1) + if self.getColorType(cellX - 1, cellZ + 1) == colorType or self.matchWild(cellX - 1, cellZ + 1, colorType): + self.fillMatchList(cellX - 1, cellZ + 1) + if self.getColorType(cellX, cellZ - 1) == colorType or self.matchWild(cellX, cellZ - 1, colorType): + self.fillMatchList(cellX, cellZ - 1) + if self.getColorType(cellX - 1, cellZ - 1) == colorType or self.matchWild(cellX - 1, cellZ - 1, colorType): + self.fillMatchList(cellX - 1, cellZ - 1) + + def testGridfull(self, cell): + if not cell: + return 0 + elif cell[0] != None: + return 1 + else: + return 0 + return + + def getValidGrid(self, x, z): + if x < 0 or x >= self.gridDimX: + return None + elif z < 0 or z >= self.gridDimZ: + return None + else: + return self.grid[x][z] + return None + + def getColorType(self, x, z): + if x < 0 or x >= self.gridDimX: + return -1 + elif z < 0 or z >= self.gridDimZ: + return -1 + elif self.grid[x][z][0] == None: + return -1 + else: + return self.grid[x][z][0].colorType + return + + def getBreakable(self, x, z): + if x < 0 or x >= self.gridDimX: + return -1 + elif z < 0 or z >= self.gridDimZ: + return -1 + elif self.grid[x][z][0] == None: + return -1 + else: + return self.grid[x][z][0].breakable + return + + def findGridCog(self): + self.cogX = 0 + self.cogZ = 0 + self.massCount = 0 + for row in self.grid: + for cell in row: + if cell[0] != None: + self.cogX += cell[1] + self.cogZ += cell[2] + self.massCount += 1 + + if self.massCount > 0: + self.cogX = self.cogX / self.massCount + self.cogZ = self.cogZ / self.massCount + self.cogSprite.setX(self.cogX) + self.cogSprite.setZ(self.cogZ) + return + + def doOnClearGrid(self): + self.winCounter += 1 + self.clearGrid() + self.flagNextLevel = 1 + if self.winCounter > 4: + self.__handleWin() + + def clearGrid(self): + for row in self.grid: + for cell in row: + if cell[0] != None: + self.killSprite(cell[0]) + cell[5].setColorScale(self.blankColor) + + self.killSprite(self.controlSprite) + return + + def killSprite(self, sprite): + if sprite == None: + return + if sprite.giftId != None: + self.giftId = sprite.giftId + if sprite.foundation: + self.foundCount -= 1 + if self.controlSprite == sprite: + self.controlSprite = None + if sprite in self.sprites: + self.sprites.remove(sprite) + if sprite.gridPosX != None: + self.grid[sprite.gridPosX][sprite.gridPosZ][0] = None + self.grid[sprite.gridPosX][sprite.gridPosZ][5].setColorScale(self.blankColor) + sprite.deathEffect() + sprite.delete() + self.hasChanged = 1 + return + + def shakeSprite(self, sprite): + if sprite == None: + return + sprite.shake() + return + + def questionSprite(self, sprite): + newSprite = self.addSprite(self.block, found=0, color=1) + newSprite.setX(sprite.getX()) + newSprite.setZ(sprite.getZ()) + newSprite.wildEffect() + + def colorGrid(self): + for row in self.grid: + for cell in row: + if cell[0] != None: + if cell[0].colorType == 3: + cell[5].setColorScale(self.blackColor) + else: + cell[5].setColorScale(self.fullColor) + elif cell[4] <= self.spriteNotchPos: + cell[5].setColorScale(self.outColor) + elif self.hasNeighbor(cell[3], cell[4]): + cell[5].setColorScale(self.neighborColor) + else: + cell[5].setColorScale(self.blankColor) + + return + + def findPos(self, x, z): + return (self.grid[x][z][1], self.grid[x][z][2]) + + def placeIntoGrid(self, sprite, x, z): + if self.grid[x][z][0] == None: + self.grid[x][z][0] = sprite + sprite.gridPosX = x + sprite.gridPosZ = z + sprite.setActive(0) + newX, newZ = self.findPos(x, z) + sprite.setX(newX) + sprite.setZ(newZ) + if sprite == self.controlSprite: + self.controlSprite = None + self.colorGridFlag = 1 + self.hasChanged = 1 + self.findGridCog() + self.checkForTooLow() + else: + self.placeIntoGrid(sprite, x + 1, z - 1) + return + + def stickInGrid(self, sprite, force = 0): + if sprite.isActive: + gridCell = self.findGrid(sprite.getX(), sprite.getZ(), force) + if gridCell: + colorType = sprite.colorType + sprite.setActive(0) + self.soundLand.play() + self.placeIntoGrid(sprite, gridCell[3], gridCell[4]) + if colorType == self.bombIndex: + kapow = MovieUtil.createKapowExplosionTrack(render, sprite.nodeObj.getPos(render)) + kapow.start() + self.bombNeighbors(self.closestX, self.closestZ) + allNeighbors = [] + for entry in self.matchList: + neighbors = self.getNeighbors(entry[0], entry[1]) + for neighbor in neighbors: + if neighbor not in allNeighbors and neighbor not in self.matchList: + allNeighbors.append(neighbor) + + self.shakeList(allNeighbors) + self.clearMatchList() + else: + self.createMatchList(self.closestX, self.closestZ) + if len(self.matchList) >= 3: + clearType = 0 + self.clearMatchList(colorType) + else: + neighbors = self.getNeighbors(self.closestX, self.closestZ) + self.shakeList(neighbors) + + def addSprite(self, image, size = 3.0, posX = 0, posZ = 0, found = 0, color = None): + spriteBase = self.spriteNode.attachNewNode('sprite base') + size = self.radiusBall * 2.0 + facing = 1 + if color == None: + colorChoice = random.choice(range(0, 3)) + else: + colorChoice = color + newSprite = GameSprite3D.GameSprite(spriteBase, size, colorChoice, found, facing) + newSprite.setX(posX) + newSprite.setZ(posZ) + self.sprites.append(newSprite) + if found: + self.foundCount += 1 + return newSprite + + def addControlSprite(self, x = 0.0, z = 0.0, color = None): + newSprite = self.addSprite(self.block, posX=x, posZ=z, color=color, found=1) + newSprite.spriteBase.reparentTo(self.frame) + newSprite.spriteBase.setPos(0.0, 0.7, -1.54) + self.controlSprite = newSprite + + def addUnSprite(self, image, size = 3.0, posX = 0, posZ = 0): + size = self.radiusBall * 2.0 + spriteBase = self.spriteNode.attachNewNode('sprite base') + newSprite = GameSprite3D.GameSprite(spriteBase, size) + newSprite.setX(posX) + newSprite.setZ(posZ) + return newSprite + + def __handleMouseClick(self): + if self.ballLoaded == 2: + pass + if self.ballLoaded and self.controlSprite: + self.controlSprite.spriteBase.wrtReparentTo(self.spriteNode) + self.controlSprite.setAccel(14.0, pi * 0.0 - self.aimRadian) + self.controlSprite.setActive(1) + self.soundFire.play() + self.ballLoaded = 0 + + def __run(self, cont = 1): + if cont and not self.running: + taskMgr.add(self.__run, 'GolfGreenGameTask') + self.running = 1 + if self.lastTime == None: + self.lastTime = globalClock.getRealTime() + timeDelta = globalClock.getRealTime() - self.lastTime + self.lastTime = globalClock.getRealTime() + self.newBallCountUp += timeDelta + if base.mouseWatcherNode.hasMouse(): + inputX = base.mouseWatcherNode.getMouseX() + inputZ = base.mouseWatcherNode.getMouseY() + outputZ = inputZ + self.screenSizeZ * (0.5 - self.zGap) + if outputZ <= 0.0: + outputZ = 0.0001 + if inputX > 0.0: + self.aimRadian = -1.0 * pi + math.atan(outputZ / (inputX * self.XtoZ)) + elif inputX < 0.0: + self.aimRadian = math.atan(outputZ / (inputX * self.XtoZ)) + else: + self.aimRadian = pi * -0.5 + margin = 0.2 + if self.aimRadian >= -margin: + self.aimRadian = -margin + elif self.aimRadian <= margin - pi: + self.aimRadian = margin - pi + degrees = self.__toDegrees(self.aimRadian) + self.aimer.setH(degrees) + self.wallMaxX = self.maxX - self.radiusBall + self.wallMinX = self.minX + self.radiusBall + self.wallMaxZ = self.maxZ - self.radiusBall + self.wallMinZ = self.minZ + self.radiusBall + if self.controlSprite and self.controlSprite.nodeObj.isEmpty(): + self.controlSprite = None + if self.giftId: + self.ballLoaded = 2 + self.updateSpritePos() + self.standbySprite.holdType = self.giftId + self.standbySprite.setBallType(self.giftId, 1) + self.standbySprite.face() + self.giftId = None + while self.controlSprite == None and self.attackPattern: + if self.attackCounter > len(self.attackPattern) - 1: + self.attackCounter = 0 + print 'Pattern %s Place %s Type %s' % (self.attackPattern, self.attackCounter, self.attackPattern[self.attackCounter]) + if self.standbySprite.holdType != None: + color = self.standbySprite.holdType + sprite = self.addControlSprite(self.newBallX, self.newBallZ + self.spriteNotchPos * self.cellSizeZ, color) + self.ballLoaded = 1 + self.updateSpritePos() + newColor = self.predictAttackPattern(0) + self.standbySprite.holdType = newColor + self.standbySprite.setBallType(newColor, 1) + self.standbySprite.face() + self.attackCounter += 1 + + self.standbySprite.runColor() + for sprite in self.sprites: + if sprite.deleteFlag: + self.sprites.remove(sprite) + else: + sprite.run(timeDelta) + if sprite.getX() > self.wallMaxX: + sprite.setX(self.wallMaxX) + sprite.reflectX() + if sprite.getX() < self.wallMinX: + sprite.setX(self.wallMinX) + sprite.reflectX() + if sprite.getZ() > self.wallMaxZ: + self.stickInGrid(sprite, 1) + if sprite.getZ() < self.wallMinZ: + pass + + self.__colTest() + if self.hasChanged and self.running: + self.clearFloaters() + self.explodeBombs() + self.findGridCog() + spriteCount = 0 + whiteCount = 0 + for row in self.grid: + for cell in row: + if cell[0] != None: + self.cogX += cell[1] + self.cogZ += cell[2] + spriteCount += 1 + if cell[0].colorType == 3: + whiteCount += 1 + + if whiteCount == 0: + self.__finishBoard() + self.flagNextLevel = 0 + self.killSprite(self.controlSprite) + self.standbySprite.holdType = None + self.colorGridFlag = 1 + self.hasChanged = 0 + if self.colorGridFlag: + self.colorGridFlag = 0 + self.colorGrid() + return Task.cont + + def predictAttackPattern(self, numSteps = 1): + predict = self.attackCounter + numSteps + predict = predict % len(self.attackPattern) + return self.attackPattern[predict] + + def __stop(self): + taskMgr.remove('GolfGreenGameTask') + self.running = 0 + + def __testWin(self): + gridCount = 0 + for column in self.grid: + for cell in column: + if cell[0]: + gridCount += 1 + + if gridCount == 0: + self.__handleWin() + + def __toRadians(self, angle): + return angle * 2.0 * math.pi / 360.0 + + def __toDegrees(self, angle): + return angle * 360.0 / (2.0 * math.pi) + + def __colTest(self): + if not hasattr(self, 'tick'): + self.tick = 0 + self.tick += 1 + if self.tick > 5: + self.tick = 0 + sizeSprites = len(self.sprites) + for movingSpriteIndex in xrange(len(self.sprites)): + for testSpriteIndex in xrange(movingSpriteIndex, len(self.sprites)): + movingSprite = self.getSprite(movingSpriteIndex) + testSprite = self.getSprite(testSpriteIndex) + if testSprite and movingSprite: + if movingSpriteIndex != testSpriteIndex and (movingSprite.isActive or testSprite.isActive): + if self.testDistance(movingSprite.spriteBase, testSprite.spriteBase) < self.radiusBall * 1.65: + if not (movingSprite.isActive and testSprite.isActive): + if movingSprite.canCollide and testSprite.canCollide: + self.__collide(movingSprite, testSprite) + if self.tick == 5: + pass + + def getSprite(self, spriteIndex): + if spriteIndex >= len(self.sprites) or self.sprites[spriteIndex].markedForDeath: + return None + else: + return self.sprites[spriteIndex] + return None + + def testDistance(self, nodeA, nodeB): + if nodeA.isEmpty() or nodeB.isEmpty(): + return 10000 + distX = nodeA.getX() - nodeB.getX() + distZ = nodeA.getZ() - nodeB.getZ() + distC = distX * distX + distZ * distZ + dist = math.sqrt(distC) + return dist + + def testPointDistance(self, x1, z1, x2, z2): + distX = x1 - x2 + distZ = z1 - z2 + distC = distX * distX + distZ * distZ + dist = math.sqrt(distC) + if dist == 0: + dist = 1e-10 + return dist + + def testPointDistanceSquare(self, x1, z1, x2, z2): + distX = x1 - x2 + distZ = z1 - z2 + distC = distX * distX + distZ * distZ + if distC == 0: + distC = 1e-10 + return distC + + def angleTwoSprites(self, sprite1, sprite2): + x1 = sprite1.getX() + z1 = sprite1.getZ() + x2 = sprite2.getX() + z2 = sprite2.getZ() + x = x2 - x1 + z = z2 - z1 + angle = math.atan2(-x, z) + return angle + pi * 0.5 + + def angleTwoPoints(self, x1, z1, x2, z2): + x = x2 - x1 + z = z2 - z1 + angle = math.atan2(-x, z) + return angle + pi * 0.5 + + def __collide(self, move, test): + test.velX = 0 + test.velZ = 0 + move.velX = 0 + move.velZ = 0 + test.collide() + move.collide() + self.stickInGrid(move) + self.stickInGrid(test) + + def generateInit(self): + self.notify.debug('generateInit') + BattleBlocker.BattleBlocker.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BattleBlocker.BattleBlocker.announceGenerate(self) + self.baseNode = self.attachNewNode('GolfGreenGameBase') + self.frame = self.baseNode.attachNewNode('GolfGreenGameFrame') + self.spriteNode = self.frame.attachNewNode('GolfGreenGameSpriteNode') + self.frame.setScale(1.0) + self.frame.setP(90) + self.spriteNotchPos = 0 + self.frame.setY(10.0) + self.frame.setZ(2.0) + self.spriteNode.setY(0.5) + self.hasChanged = 0 + self.squareNode = self.frame.attachNewNode('GolfGreenGameBase') + groundCircle = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_golf_green') + groundCircle.reparentTo(self.baseNode) + groundCircle.setScale(0.24) + self.groundFlag = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_golf_flag') + self.groundFlag.reparentTo(self.baseNode) + self.groundFlag.setScale(0.5) + self.groundFlag.setH(-45) + self.groundFlag.setPos(3.0, 4.0, 0.0) + groundSquare = BuildGeometry.addSquareGeom(self.squareNode, self.sizeX, self.sizeZ, color=Vec4(0.4, 0.4, 0.4, 0.5)) + self.centerZ = (self.minZ + self.maxZ) * 0.5 + self.squareNode.setZ((self.minZ + self.maxZ) * 0.5) + self.squareNode.setP(-90) + groundCircle.setDepthWrite(False) + groundCircle.setDepthTest(True) + groundCircle.setBin('ground', 1) + groundSquare[0].setDepthWrite(False) + groundSquare[0].setDepthTest(False) + groundSquare[0].setBin('ground', 2) + self.squareNode.hide() + self.load() + + def initCollisionGeom(self): + self.actSphere = CollisionSphere(0, 0, 0, 11.5) + self.actSphereNode = CollisionNode('gridgame-%s-%s' % (self.level.getLevelId(), self.entId)) + self.actSphereNode.addSolid(self.actSphere) + self.actSphereNodePath = self.attachNewNode(self.actSphereNode) + self.actSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + self.actSphere.setTangible(0) + self.enterEvent = 'enter' + self.actSphereNode.getName() + self.accept(self.enterEvent, self.__handleToonEnter) + + def __handleToonEnter(self, collEntry): + self.sendUpdate('requestJoin', []) + + def __setCamera(self): + camHeight = base.localAvatar.getClampedAvatarHeight() + heightScaleFactor = camHeight * 0.3333333333 + defLookAt = Point3(0.0, 1.5, camHeight) + cameraPoint = Point3(0.0, -16.0, 16.0) + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.stopUpdateSmartCamera() + basePos = self.frame.getPos(render) + modPos = Point3(basePos[0] + 0.0, basePos[1] + 12.0, basePos[2] + 12.0) + camera.setPos(0, 0, 0) + camera.setH(0) + camera.setP(-70) + camera.reparentTo(self.focusPoint) + base.camLens.setMinFov(60/(4./3.)) + self.focusPoint.setPos(0, 12, 27) + self.focusPoint.setH(180) + + def acceptJoin(self, time, timeStamp, avIds): + self.timeStart = timeStamp + timePassed = globalClockDelta.localElapsedTime(self.timeStart) + timeleft = time - timePassed + self.timeTotal = time + if localAvatar.doId in avIds and localAvatar.doId not in self.joinedToons: + self.__startGame() + base.cr.playGame.getPlace().fsm.request('stopped') + self.sendUpdate('requestBoard', [0]) + if not self.hasEntered: + self.level.countryClub.showInfoText(TTLocalizer.BustACogInstruction) + self.hasEntered = 1 + for avId in self.joinedToons: + if avId not in avIds: + self.joinedToons.remove(avId) + self.removeToonHeadPanel(avId) + toon = base.cr.doId2do.get(avId) + if toon: + toon.startSmooth() + + for avId in avIds: + if avId and avId not in self.joinedToons: + if avId not in self.everJoinedToons: + self.everJoinedToons.append(avId) + self.joinedToons.append(avId) + index = self.everJoinedToons.index(avId) + if index > 3: + print 'ERROR! green game has had more than 4 players, we are about to crash\n %s' % self.everJoinedToons + print 'Joining Toon is %s index is %s' % (avId, index) + toon = base.cr.doId2do.get(avId) + selfPos = self.getPos(render) + offset = self.toonPoints[index] + if index > 3: + print 'odd... we should have crashed by now' + standPoint = render.getRelativePoint(self, offset) + if toon: + toon.stopSmooth() + self.addToonHeadPanel(toon) + toon.setAnimState('run', 1.0) + animFunc = Func(toon.setAnimState, 'neutral', 1.0) + track = Sequence(LerpPosInterval(toon, 0.75, standPoint), LerpHprInterval(toon, 0.25, Point3(180, 0, 0)), animFunc, Func(self.clearToonTrack, avId), name=toon.uniqueName('gggEnter'), autoPause=1) + track.delayDelete = DelayDelete.DelayDelete(toon, 'GolfGreenGame.acceptJoin') + self.storeToonTrack(avId, track) + track.start() + + def signalDone(self, success): + self.finished = 1 + self.soundDone.play() + self.__leaveGame() + self.__removeGame() + self.scoreBoard.hide() + self.cleanupTimer() + if success: + self.level.countryClub.showInfoText(TTLocalizer.BustACogSuccess) + else: + self.level.countryClub.showInfoText(TTLocalizer.BustACogFailure) + + def boardCleared(self, avId): + self.doFail() + + def setTimerStart(self, time, timeStamp): + if self.timer == None: + self.timeStart = timeStamp + timePassed = globalClockDelta.localElapsedTime(self.timeStart) + timeleft = time - timePassed + self.timeTotal = time + self.cleanupTimer() + self.timer = ToontownTimer.ToontownTimer() + self.timer.posBelowTopRightCorner() + self.timer.setTime(timeleft) + self.timer.countdown(timeleft, self.timerExpired) + return + + def cleanupTimer(self): + if self.timer: + self.timer.stop() + self.timer.destroy() + self.timer = None + return + + def timerExpired(self): + self.cleanupTimer() + + def useTime(self, time = None): + if time != None: + self.timeLeft = time + if self.timerTask != None: + taskMgr.remove(self.timerTaskName) + if time != None and time > 0.0 and self.isActive: + self.timerTask = taskMgr.doMethodLater(1.0, self.gameCountDown, self.timerTaskName) + self.scoreLabel['text'] = TTLocalizer.GolfGreenGameScoreString % (self.boardsLeft, int(self.timeLeft)) + return + + def gameCountDown(self, task): + self.timeLeft = self.timeTotal - globalClockDelta.localElapsedTime(self.timeStart) + return task.done + + def scoreData(self, total = 2, closed = 1, scoreList = 'hello world'): + self.boardsLeft = total - closed + for panelIndex in self.toonPanels: + panel = self.toonPanels[panelIndex] + panel.extraData['text'] = TTLocalizer.GolfGreenGamePlayerScore % 0 + + for entryIndex in xrange(len(scoreList)): + entry = scoreList[entryIndex] + if entry[0] in self.toonPanels: + panel = self.toonPanels[entry[0]] + panel.extraData['text'] = TTLocalizer.GolfGreenGamePlayerScore % entry[1] + + self.scoreLabel['text'] = TTLocalizer.GolfGreenGameScoreString % self.boardsLeft + + def informGag(self, track, level): + self.bonusBoard.show() + self.bonusBoard['text'] = TTLocalizer.GolfGreenGameBonusGag % TTLocalizer.BattleGlobalAvPropStringsSingular[track][level] + iconName = ToontownBattleGlobals.AvPropsNew[track][level] + icon = self.invModel.find('**/%s' % iconName) + self.bonusBoard['image'] = icon + self.bonusBoard['image_scale'] = (1.0, 1, 1.0) + taskMgr.doMethodLater(4.0, self.hideBonusBoard, 'hide bonus') + + def helpOthers(self, avId): + if not avId == localAvatar.doId and self.running: + self.giftId = 7 + toonName = '' + toon = base.cr.doId2do[avId] + if toon: + toonName = toon.getName() + self.bonusBoard['text'] = TTLocalizer.GolfGreenGameGotHelp % toonName + imageBall = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_ball_fire') + imageBall.setHpr(0, 90, 0) + self.bonusBoard['image'] = imageBall + self.bonusBoard['image_scale'] = 0.13 + self.bonusBoard.show() + taskMgr.doMethodLater(4.0, self.hideBonusBoard, 'hide bonus') + + def hideBonusBoard(self, task): + if self.bonusBoard: + if not self.bonusBoard.isEmpty(): + self.bonusBoard.hide() + + def storeToonTrack(self, avId, track): + self.clearToonTrack(avId) + self.__toonTracks[avId] = track + + def clearToonTrack(self, avId): + oldTrack = self.__toonTracks.get(avId) + if oldTrack: + oldTrack.pause() + if self.__toonTracks.get(avId): + DelayDelete.cleanupDelayDeletes(self.__toonTracks[avId]) + del self.__toonTracks[avId] + + def clearToonTracks(self): + keyList = [] + for key in self.__toonTracks: + keyList.append(key) + + for key in keyList: + if key in self.__toonTracks: + self.clearToonTrack(key) diff --git a/toontown/coghq/DistributedGolfGreenGameAI.py b/toontown/coghq/DistributedGolfGreenGameAI.py new file mode 100755 index 00000000..e24e9900 --- /dev/null +++ b/toontown/coghq/DistributedGolfGreenGameAI.py @@ -0,0 +1,342 @@ +from otp.ai.AIBase import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from direct.task import Task +from otp.level import DistributedEntityAI +from otp.level import BasicEntities +from toontown.coghq import BattleBlockerAI +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownBattleGlobals +from GolfGreenGameGlobals import * +import random +import time + +class DistributedGolfGreenGameAI(BattleBlockerAI.BattleBlockerAI, NodePath, BasicEntities.NodePathAttribs): + + def __init__(self, level, entId): + BattleBlockerAI.BattleBlockerAI.__init__(self, level, entId) + random.seed(time.time() * entId) + node = hidden.attachNewNode('DistributedLaserFieldAI') + NodePath.__init__(self, node) + if not hasattr(self, 'switchId'): + self.switchId = 0 + self.gridScale = 1 + self.enabled = 1 + self.hasShownSuits = 0 + self.healReady = 1 + self.playedSound = 0 + self.canButton = 1 + self.allBoardsClear = 0 + self.challengeDefeated = False + self.title = 'MemTag: This is a golfGreenGame %s' % random.random() + self.translateData = {} + self.translateData['r'] = 0 + self.translateData['b'] = 1 + self.translateData['g'] = 2 + self.translateData['w'] = 3 + self.translateData['k'] = 4 + self.translateData['l'] = 5 + self.translateData['y'] = 6 + self.translateData['o'] = 7 + self.translateData['a'] = 8 + self.translateData['s'] = 9 + self.translateData['R'] = 10 + self.translateData['B'] = 11 + self.preData = [] + self.boardList = [] + self.joinedToons = [] + self.everJoinedToons = [] + self.startTime = None + self.totalTime = 1180.0 + self.DamageOnFailure = 20.0 + return + + def announceGenerate(self): + BattleBlockerAI.BattleBlockerAI.announceGenerate(self) + self.totalTime = self.timeToPlay + numToons = 0 + if hasattr(self, 'level'): + numToons = len(self.level.presentAvIds) + numBoards = self.puzzleBase + numToons * self.puzzlePerPlayer + boardSelect = range(0, len(gameBoards)) + didGetLast = 1 + for index in xrange(numBoards): + choice = random.choice(boardSelect) + if not didGetLast: + didGetLast = 1 + choice = len(gameBoards) - 1 + self.preData.append(gameBoards[choice]) + boardSelect.remove(choice) + self.boardList.append([[], + index, + index, + None]) + + self.boardData = [] + self.attackPatterns = [] + self.processPreData() + return + + def processPreData(self): + for board in self.preData: + x = [] + for rowIndex in xrange(1, len(board)): + for columnIndex in xrange(len(board[rowIndex])): + color = self.translateData.get(board[rowIndex][columnIndex]) + if color != None: + x.append((len(board[rowIndex]) - (columnIndex + 1), rowIndex - 1, color)) + + self.boardData.append(x) + + for board in self.preData: + attackString = board[0] + attackPattern = [] + for ball in attackString: + color = self.translateData.get(ball) + if color or color == 0: + place = random.choice(range(0, len(attackPattern) + 1)) + attackPattern.insert(place, color) + place = random.choice(range(0, len(attackPattern) + 1)) + attackPattern.insert(place, color) + + self.attackPatterns.append(attackPattern) + + return + + def startTimer(self): + self.startTime = globalClockDelta.getFrameNetworkTime() + taskMgr.doMethodLater(self.totalTime, self.__handleTimeOut, self.taskName('GolfGreenGameTimeout')) + self.sendUpdate('setTimerStart', [self.totalTime, self.startTime]) + + def __printTime(self, task): + print 'Time Left %s' % self.getTimeLeft() + taskMgr.doMethodLater(1.0, self.__printTime, self.taskName('GolfGreenGameTimeout Print')) + return task.done + + def __handleTimeOut(self, task = None): + taskMgr.remove(self.taskName('GolfGreenGameTimeout')) + self.__handleFinsihed(0) + return task.done + + def getTimeLeft(self): + if self.startTime == None: + return self.totalTime + else: + timePassed = globalClockDelta.localElapsedTime(self.startTime) + timeLeft = self.totalTime - timePassed + return timeLeft + return + + def choosePattern(self): + dataSize = len(self.boardData) + indexChoice = int(random.random() * dataSize) + boardToAssign = None + for boardIndex in xrange(len(self.boardList)): + board = self.boardList[boardIndex] + if self.boardList[boardIndex][0] == 'closed': + pass + elif boardToAssign == None or len(self.boardList[boardIndex][0]) < len(self.boardList[boardToAssign][0]): + boardToAssign = boardIndex + elif len(self.boardList[boardIndex][0]) == len(self.boardList[boardToAssign][0]): + choice = random.choice(range(2)) + if choice: + boardToAssign = boardIndex + + if boardToAssign == None: + pass + return boardToAssign + + def checkForAssigned(self, avId): + for index in xrange(len(self.boardList)): + board = self.boardList[index] + if board[0] == 'closed': + pass + elif avId in board[0]: + return index + + return None + + def leaveGame(self): + senderId = self.air.getAvatarIdFromSender() + if senderId in self.joinedToons: + self.joinedToons.remove(senderId) + self.sendUpdate('acceptJoin', [self.totalTime, self.startTime, self.joinedToons]) + for boardDatum in self.boardList: + if boardDatum[0] == 'closed': + pass + elif senderId in boardDatum[0]: + boardDatum[0].remove(senderId) + + def requestJoin(self): + if self.allBoardsClear: + self.sendUpdate('acceptJoin', [0.0, 0.0, [0]]) + return + senderId = self.air.getAvatarIdFromSender() + if senderId not in self.joinedToons: + if self.startTime == None: + self.startTimer() + self.joinedToons.append(senderId) + if senderId not in self.everJoinedToons: + self.everJoinedToons.append(senderId) + self.sendUpdate('acceptJoin', [self.totalTime, self.startTime, self.joinedToons]) + self.sendScoreData() + return + + def requestBoard(self, boardVerify): + senderId = self.air.getAvatarIdFromSender() + assigned = self.checkForAssigned(senderId) + if assigned != None: + if self.boardList[assigned][0] == 'closed': + return + if boardVerify: + toon = simbase.air.doId2do.get(senderId) + if toon: + self.addGag(senderId) + self.sendUpdate('helpOthers', [senderId]) + for avId in self.boardList[assigned][0]: + if avId != senderId: + self.sendUpdateToAvatarId(avId, 'boardCleared', [senderId]) + + self.boardList[assigned][0] = 'closed' + self.boardList[assigned][3] = senderId + self.sendScoreData() + else: + self.boardList[assigned][0].remove(senderId) + boardIndex = self.choosePattern() + if boardIndex == None: + self.__handleFinsihed(1) + else: + self.boardList[boardIndex][0].append(senderId) + self.sendUpdateToAvatarId(senderId, 'startBoard', [self.boardData[self.boardList[boardIndex][1]], self.attackPatterns[self.boardList[boardIndex][2]]]) + return + + def addGag(self, avId): + av = simbase.air.doId2do.get(avId) + if av: + level = ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + track = int(random.random() * ToontownBattleGlobals.NUM_GAG_TRACKS) + while not av.hasTrackAccess(track): + track = int(random.random() * ToontownBattleGlobals.NUM_GAG_TRACKS) + + maxGags = av.getMaxCarry() + av.inventory.calcTotalProps() + numGags = av.inventory.totalProps + numReward = min(1, maxGags - numGags) + while numReward > 0 and level >= 0: + result = av.inventory.addItem(track, level) + if result <= 0: + level -= 1 + else: + numReward -= 1 + self.sendUpdateToAvatarId(avId, 'informGag', [track, level]) + + av.d_setInventory(av.inventory.makeNetString()) + + def __handleFinsihed(self, success): + self.allBoardsClear = 1 + self.sendUpdate('signalDone', [success]) + self.switchFire() + taskMgr.remove(self.taskName('GolfGreenGameTimeout')) + if success: + for avId in self.joinedToons: + self.addGag(avId) + toon = simbase.air.doId2do.get(avId) + timeleft = int(self.getTimeLeft()) + if toon and timeleft > 0: + toon.toonUp(timeleft) + + else: + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + if room: + avIds = self.everJoinedToons + for avId in avIds: + av = simbase.air.doId2do.get(avId) + if av: + av.takeDamage(self.DamageOnFailure, quietly=0) + room.sendUpdate('forceOuch', [self.DamageOnFailure]) + + if not self.challengeDefeated: + self.challengeDefeated = True + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + if room: + self.challengeDefeated = True + room.challengeDefeated() + eventName = self.getOutputEventName() + messenger.send(eventName, [1]) + + def switchFire(self): + if self.switchId != 0: + switch = self.level.getEntity(self.switchId) + if switch: + switch.setIsOn(1) + + def generate(self): + BattleBlockerAI.BattleBlockerAI.generate(self) + if self.switchId != 0: + self.accept(self.getOutputEventName(self.switchId), self.reactToSwitch) + self.detectName = 'golfGreenGame %s' % self.doId + taskMgr.doMethodLater(1.0, self.__detect, self.detectName) + self.setPos(self.pos) + self.setHpr(self.hpr) + + def registerBlocker(self): + BattleBlockerAI.BattleBlockerAI.registerBlocker(self) + if hasattr(self, 'hideSuits'): + self.hideSuits() + + def delete(self): + taskMgr.remove(self.detectName) + self.ignoreAll() + BattleBlockerAI.BattleBlockerAI.delete(self) + + def destroy(self): + self.notify.info('destroy entity(laserField) %s' % self.entId) + BattleBlockerAI.BattleBlockerAI.destroy(self) + + def __detect(self, task): + isThereAnyToons = False + if hasattr(self, 'level'): + toonInRange = 0 + for avId in self.level.presentAvIds: + if avId in self.air.doId2do: + av = self.air.doId2do[avId] + isThereAnyToons = True + distance = self.getDistance(av) + + if isThereAnyToons: + taskMgr.doMethodLater(1.0, self.__detect, self.detectName) + self.__run() + return Task.done + + def __run(self): + pass + + def reactToSwitch(self, on): + pass + + def setBattleFinished(self): + BattleBlockerAI.BattleBlockerAI.setBattleFinished(self) + messenger.send(self.getOutputEventName(), [1]) + self.switchFire() + + def sendScoreData(self): + total = len(self.boardList) + closed = 0 + scoreDict = {} + for board in self.boardList: + if board[0] == 'closed': + closed += 1 + if board[3] not in scoreDict: + scoreDict[board[3]] = 1 + else: + scoreDict[board[3]] += 1 + + outList = [] + for key in scoreDict: + score = scoreDict[key] + outList.append([key, score]) + + self.sendUpdate('scoreData', [total, closed, outList]) diff --git a/toontown/coghq/DistributedGolfSpot.py b/toontown/coghq/DistributedGolfSpot.py new file mode 100755 index 00000000..46f983b7 --- /dev/null +++ b/toontown/coghq/DistributedGolfSpot.py @@ -0,0 +1,757 @@ +import math +from pandac.PandaModules import Point3, CollisionSphere, CollisionNode, CollisionHandlerEvent, TextNode, VBase4, SmoothMover, NodePath, BitMask32 +from direct.fsm import FSM +from direct.distributed import DistributedObject +from direct.distributed.ClockDelta import globalClockDelta +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import DGG, DirectButton, DirectLabel, DirectWaitBar +from direct.interval.IntervalGlobal import Sequence, Wait, ActorInterval, Parallel, Func, LerpPosInterval, LerpHprInterval, ProjectileInterval, LerpScaleInterval, SoundInterval +from direct.showbase import PythonUtil +from direct.task import Task +from toontown.golf import GolfGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class DistributedGolfSpot(DistributedObject.DistributedObject, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGolfSpot') + positions = ((-45, 100, GolfGlobals.GOLF_BALL_RADIUS), + (-15, 100, GolfGlobals.GOLF_BALL_RADIUS), + (15, 100, GolfGlobals.GOLF_BALL_RADIUS), + (45, 100, GolfGlobals.GOLF_BALL_RADIUS)) + toonGolfOffsetPos = Point3(-2, 0, -GolfGlobals.GOLF_BALL_RADIUS) + toonGolfOffsetHpr = Point3(-90, 0, 0) + rotateSpeed = 20 + golfPowerSpeed = base.config.GetDouble('golf-power-speed', 3) + golfPowerExponent = base.config.GetDouble('golf-power-exponent', 0.75) + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedGolfSpot') + self.boss = None + self.index = None + self.avId = 0 + self.toon = None + self.golfSpotSmoother = SmoothMover() + self.golfSpotSmoother.setSmoothMode(SmoothMover.SMOn) + self.smoothStarted = 0 + self.__broadcastPeriod = 0.2 + if self.index > len(self.positions): + self.notify.error('Invalid index %d' % index) + self.fadeTrack = None + self.setupPowerBar() + self.aimStart = None + self.golfSpotAdviceLabel = None + self.changeSeq = 0 + self.lastChangeSeq = 0 + self.controlKeyAllowed = False + self.flyBallTracks = {} + self.splatTracks = {} + self.__flyBallBubble = None + self.flyBallHandler = None + self.__flyBallSequenceNum = 0 + self.swingInterval = None + self.lastHitSequenceNum = -1 + self.goingToReward = False + self.gotHitByBoss = False + self.releaseTrack = None + self.grabTrack = None + self.restoreScaleTrack = None + return + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + self.boss.setGolfSpot(self, self.index) + + def setIndex(self, index): + self.index = index + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.ignoreAll() + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.ignoreAll() + self.boss = None + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.triggerName = self.uniqueName('trigger') + self.triggerEvent = 'enter%s' % self.triggerName + self.smoothName = self.uniqueName('golfSpotSmooth') + self.golfSpotAdviceName = self.uniqueName('golfSpotAdvice') + self.posHprBroadcastName = self.uniqueName('golfSpotBroadcast') + self.ballPowerTaskName = self.uniqueName('updateGolfPower') + self.adjustClubTaskName = self.uniqueName('adjustClub') + self.loadAssets() + self.accept('flyBallHit-%d' % self.index, self.__flyBallHit) + + def loadAssets(self): + self.root = render.attachNewNode('golfSpot-%d' % self.index) + self.root.setPos(*self.positions[self.index]) + self.ballModel = loader.loadModel('phase_6/models/golf/golf_ball') + self.ballColor = VBase4(1, 1, 1, 1) + if self.index < len(GolfGlobals.PlayerColors): + self.ballColor = VBase4(*GolfGlobals.PlayerColors[self.index]) + self.ballModel.setColorScale(self.ballColor) + self.ballModel.reparentTo(self.root) + self.club = loader.loadModel('phase_6/models/golf/putter') + self.clubLookatSpot = self.root.attachNewNode('clubLookat') + self.clubLookatSpot.setY(-(GolfGlobals.GOLF_BALL_RADIUS + 0.1)) + cs = CollisionSphere(0, 0, 0, 1) + cs.setTangible(0) + cn = CollisionNode(self.triggerName) + cn.addSolid(cs) + cn.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.trigger = self.root.attachNewNode(cn) + self.trigger.stash() + self.hitBallSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hit_Ball.ogg') + + def cleanup(self): + if self.swingInterval: + self.swingInterval.finish() + self.swingInterval = None + if self.releaseTrack: + self.releaseTrack.finish() + self.releaseTrack = None + flyTracks = self.flyBallTracks.values() + for track in flyTracks: + track.finish() + + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + if self.restoreScaleTrack: + self.restoreScaleTrack.finish() + self.restoreScaleTrack = None + self.root.removeNode() + self.ballModel.removeNode() + self.club.removeNode() + if self.powerBar: + self.powerBar.destroy() + self.powerBar = None + taskMgr.remove(self.triggerName) + self.boss = None + return + + def setState(self, state, avId, extraInfo): + if not self.isDisabled(): + self.gotHitByBoss = extraInfo + if state == 'C': + self.demand('Controlled', avId) + elif state == 'F': + self.demand('Free') + elif state == 'O': + self.demand('Off') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterFree(self): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + self.restoreScaleTrack = Sequence(Wait(6), self.getRestoreScaleInterval(), name='restoreScaleTrack') + self.restoreScaleTrack.start() + if self.avId == localAvatar.doId: + if not self.isDisabled(): + self.ballModel.setAlphaScale(0.3) + self.ballModel.setTransparency(1) + taskMgr.doMethodLater(5, self.__allowDetect, self.triggerName) + self.fadeTrack = Sequence(Func(self.ballModel.setTransparency, 1), self.ballModel.colorScaleInterval(0.2, VBase4(1, 1, 1, 0.3)), name='fadeTrack-enterFree') + self.fadeTrack.start() + else: + self.trigger.unstash() + self.accept(self.triggerEvent, self.__hitTrigger) + self.avId = 0 + return + + def exitFree(self): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + self.restoreScaleTrack.finish() + self.restoreScaleTrack = None + taskMgr.remove(self.triggerName) + self.ballModel.clearTransparency() + self.trigger.stash() + self.ignore(self.triggerEvent) + return + + def enterControlled(self, avId): + self.avId = avId + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.enableControlKey() + self.toon = toon + self.grabTrack = self.makeToonGrabInterval(toon) + if avId == localAvatar.doId: + self.boss.toCraneMode() + camera.reparentTo(self.root) + camera.setPosHpr(0, -10, 3, 0, 0, 0) + localAvatar.setPos(self.root, self.toonGolfOffsetPos) + localAvatar.setHpr(self.root, self.toonGolfOffsetHpr) + localAvatar.sendCurrentPosition() + self.__enableControlInterface() + self.startPosHprBroadcast() + self.accept('exitCrane', self.gotBossZapped) + self.grabTrack.start() + + def exitControlled(self): + self.grabTrack.finish() + del self.grabTrack + if self.swingInterval: + self.swingInterval.finish() + self.swingInterval = None + if not self.ballModel.isEmpty(): + if self.ballModel.isHidden(): + self.notify.debug('ball is hidden scale =%s' % self.ballModel.getScale()) + else: + self.notify.debug('ball is showing scale=%s' % self.ballModel.getScale()) + if self.toon and not self.toon.isDisabled(): + self.toon.startSmooth() + self.releaseTrack = self.makeToonReleaseInterval(self.toon) + self.stopPosHprBroadcast() + self.stopSmooth() + if self.avId == localAvatar.doId: + self.__disableControlInterface() + if not self.goingToReward: + camera.reparentTo(base.localAvatar) + camera.setPos(base.localAvatar.cameraPositions[0][0]) + camera.setHpr(0, 0, 0) + self.stopAdjustClubTask() + self.releaseTrack.start() + self.enableControlKey() + return + + def __allowDetect(self, task): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = Sequence(self.ballModel.colorScaleInterval(0.2, self.ballColor), Func(self.ballModel.clearTransparency), name='fadeTrack-allowDetect') + self.fadeTrack.start() + self.trigger.unstash() + self.accept(self.triggerEvent, self.__hitTrigger) + + def __hitTrigger(self, event): + self.d_requestControl() + + def getRestoreScaleInterval(self): + return Sequence() + + def d_requestControl(self): + self.sendUpdate('requestControl') + + def d_requestFree(self, gotHitByBoss): + self.sendUpdate('requestFree', [gotHitByBoss]) + + def makeToonGrabInterval(self, toon): + origPos = toon.getPos(self.root) + origHpr = toon.getHpr(self.root) + a = self.accomodateToon(toon) + newPos = toon.getPos() + newHpr = toon.getHpr() + origHpr.setX(PythonUtil.fitSrcAngle2Dest(origHpr[0], newHpr[0])) + self.notify.debug('toon.setPosHpr %s %s' % (origPos, origHpr)) + toon.setPosHpr(origPos, origHpr) + walkTime = 0.2 + reach = Sequence() + if reach.getDuration() < walkTime: + reach = Sequence(ActorInterval(toon, 'walk', loop=1, duration=walkTime - reach.getDuration()), reach) + i = Sequence(Parallel(toon.posInterval(walkTime, newPos, origPos), toon.hprInterval(walkTime, newHpr, origHpr), reach), Func(toon.stopLookAround)) + if toon == base.localAvatar: + i.append(Func(self.switchToAnimState, 'GolfPuttLoop')) + i.append(Func(self.startAdjustClubTask)) + i = Parallel(i, a) + return i + + def accomodateToon(self, toon): + toon.wrtReparentTo(self.root) + toon.setPos(self.toonGolfOffsetPos) + toon.setHpr(self.toonGolfOffsetHpr) + return Sequence() + + def switchToAnimState(self, animStateName, forced = False): + curAnimState = base.localAvatar.animFSM.getCurrentState() + curAnimStateName = '' + if curAnimState: + curAnimStateName = curAnimState.getName() + if curAnimStateName != animStateName or forced: + base.localAvatar.b_setAnimState(animStateName) + + def __enableControlInterface(self): + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.closeButton = DirectButton(image=(gui.find('**/CloseBtn_UP'), + gui.find('**/CloseBtn_DN'), + gui.find('**/CloseBtn_Rllvr'), + gui.find('**/CloseBtn_UP')), relief=None, scale=2, text=TTLocalizer.BossbotGolfSpotLeave, text_scale=0.04, text_pos=(0, -0.07), text_fg=VBase4(1, 1, 1, 1), pos=(1.05, 0, -0.82), command=self.__exitGolfSpot) + self.accept('escape', self.__exitGolfSpot) + self.accept('control', self.__controlPressed) + self.accept('control-up', self.__controlReleased) + self.accept('InputState-forward', self.__upArrow) + self.accept('InputState-reverse', self.__downArrow) + self.accept('InputState-turnLeft', self.__leftArrow) + self.accept('InputState-turnRight', self.__rightArrow) + taskMgr.add(self.__watchControls, 'watchGolfSpotControls') + taskMgr.doMethodLater(5, self.__displayGolfSpotAdvice, self.golfSpotAdviceName) + self.arrowVert = 0 + self.arrowHorz = 0 + if self.powerBar: + self.powerBar.show() + return + + def __disableControlInterface(self): + if self.closeButton: + self.closeButton.destroy() + self.closeButton = None + self.__cleanupGolfSpotAdvice() + self.ignore('escape') + self.ignore('control') + self.ignore('control-up') + self.ignore('InputState-forward') + self.ignore('InputState-reverse') + self.ignore('InputState-turnLeft') + self.ignore('InputState-turnRight') + self.arrowVert = 0 + self.arrowHorz = 0 + taskMgr.remove('watchGolfSpotControls') + if self.powerBar: + self.powerBar.hide() + else: + self.notify.debug('self.powerBar is none') + return + + def setupPowerBar(self): + self.powerBar = DirectWaitBar(pos=(0.0, 0, -0.94), relief=DGG.SUNKEN, frameSize=(-2.0, + 2.0, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=0.25, range=100, sortOrder=50, frameColor=(0.5, 0.5, 0.5, 0.5), barColor=(1.0, 0.0, 0.0, 1.0), text='', text_scale=0.26, text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05)) + self.power = 0 + self.powerBar['value'] = self.power + self.powerBar.hide() + + def resetPowerBar(self): + self.power = 0 + self.powerBar['value'] = self.power + self.powerBar['text'] = '' + + def __displayGolfSpotAdvice(self, task): + if self.golfSpotAdviceLabel == None: + self.golfSpotAdviceLabel = DirectLabel(text=TTLocalizer.BossbotGolfSpotAdvice, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.69), scale=0.1) + return + + def __cleanupGolfSpotAdvice(self): + if self.golfSpotAdviceLabel: + self.golfSpotAdviceLabel.destroy() + self.golfSpotAdviceLabel = None + taskMgr.remove(self.golfSpotAdviceName) + return + + def showExiting(self): + if self.closeButton: + self.closeButton.destroy() + self.closeButton = DirectLabel(relief=None, text=TTLocalizer.BossbotGolfSpotLeaving, pos=(1.05, 0, -0.88), text_pos=(0, 0), text_scale=0.06, text_fg=VBase4(1, 1, 1, 1)) + self.__cleanupGolfSpotAdvice() + return + + def __exitGolfSpot(self): + self.d_requestFree(False) + + def __controlPressed(self): + if self.controlKeyAllowed: + self.__beginFireBall() + + def __controlReleased(self): + if self.controlKeyAllowed: + self.__endFireBall() + + def __upArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupGolfSpotAdvice() + if pressed: + self.arrowVert = 1 + elif self.arrowVert > 0: + self.arrowVert = 0 + + def __downArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupGolfSpotAdvice() + if pressed: + self.arrowVert = -1 + elif self.arrowVert < 0: + self.arrowVert = 0 + + def __rightArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupGolfSpotAdvice() + if pressed: + self.arrowHorz = 1 + self.switchToAnimState('GolfRotateLeft') + elif self.arrowHorz > 0: + self.arrowHorz = 0 + self.switchToAnimState('GolfPuttLoop') + + def __leftArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupGolfSpotAdvice() + if pressed: + self.arrowHorz = -1 + self.switchToAnimState('GolfRotateRight') + elif self.arrowHorz < 0: + self.arrowHorz = 0 + self.switchToAnimState('GolfPuttLoop') + + def __watchControls(self, task): + if self.arrowHorz: + self.__moveGolfSpot(self.arrowHorz) + return Task.cont + + def __moveGolfSpot(self, xd): + dt = globalClock.getDt() + h = self.root.getH() - xd * self.rotateSpeed * dt + h %= 360 + limitH = h + self.root.setH(limitH) + + def __incrementChangeSeq(self): + self.changeSeq = self.changeSeq + 1 & 255 + + def __beginFireBall(self): + if self.aimStart != None: + return + if not self.state == 'Controlled': + return + if not self.avId == localAvatar.doId: + return + time = globalClock.getFrameTime() + self.aimStart = time + messenger.send('wakeup') + taskMgr.add(self.__updateBallPower, self.ballPowerTaskName) + return + + def __endFireBall(self): + if self.aimStart == None: + return + if not self.state == 'Controlled': + return + if not self.avId == localAvatar.doId: + return + taskMgr.remove(self.ballPowerTaskName) + self.disableControlKey() + messenger.send('wakeup') + self.aimStart = None + power = self.power + angle = self.root.getH() + self.notify.debug('incrementing self.__flyBallSequenceNum') + self.__flyBallSequenceNum = (self.__flyBallSequenceNum + 1) % 255 + self.sendSwingInfo(power, angle, self.__flyBallSequenceNum) + self.setSwingInfo(power, angle, self.__flyBallSequenceNum) + self.resetPowerBar() + return + + def __updateBallPower(self, task): + if not self.powerBar: + print '### no power bar!!!' + return task.done + newPower = self.__getBallPower(globalClock.getFrameTime()) + self.power = newPower + self.powerBar['value'] = newPower + return task.cont + + def __getBallPower(self, time): + elapsed = max(time - self.aimStart, 0.0) + t = elapsed / self.golfPowerSpeed + t = math.pow(t, self.golfPowerExponent) + power = int(t * 100) % 200 + if power > 100: + power = 200 - power + return power + + def stopPosHprBroadcast(self): + taskName = self.posHprBroadcastName + taskMgr.remove(taskName) + + def startPosHprBroadcast(self): + taskName = self.posHprBroadcastName + self.b_clearSmoothing() + self.d_sendGolfSpotPos() + taskMgr.remove(taskName) + taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) + + def __posHprBroadcast(self, task): + self.d_sendGolfSpotPos() + taskName = self.posHprBroadcastName + taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) + return Task.done + + def d_sendGolfSpotPos(self): + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setGolfSpotPos', [self.changeSeq, self.root.getH(), timestamp]) + + def setGolfSpotPos(self, changeSeq, h, timestamp): + self.changeSeq = changeSeq + if self.smoothStarted: + now = globalClock.getFrameTime() + local = globalClockDelta.networkToLocalTime(timestamp, now) + self.golfSpotSmoother.setH(h) + self.golfSpotSmoother.setTimestamp(local) + self.golfSpotSmoother.markPosition() + else: + self.root.setH(h) + + def b_clearSmoothing(self): + self.d_clearSmoothing() + self.clearSmoothing() + + def d_clearSmoothing(self): + self.sendUpdate('clearSmoothing', [0]) + + def clearSmoothing(self, bogus = None): + self.golfSpotSmoother.clearPositions(1) + + def doSmoothTask(self, task): + self.golfSpotSmoother.computeAndApplySmoothHpr(self.root) + return Task.cont + + def startSmooth(self): + if not self.smoothStarted: + taskName = self.smoothName + taskMgr.remove(taskName) + self.reloadPosition() + taskMgr.add(self.doSmoothTask, taskName) + self.smoothStarted = 1 + + def stopSmooth(self): + if self.smoothStarted: + taskName = self.smoothName + taskMgr.remove(taskName) + self.forceToTruePosition() + self.smoothStarted = 0 + + def makeToonReleaseInterval(self, toon): + + def getSlideToPos(toon = toon): + return render.getRelativePoint(toon, Point3(0, -5, 0)) + + if self.gotHitByBoss: + grabIval = Sequence(Func(self.detachClub), name='makeToonReleaseInterval-gotHitByBoss') + if not toon.isEmpty(): + toonIval = Sequence(Func(toon.wrtReparentTo, render), Parallel(ActorInterval(toon, 'slip-backward'), toon.posInterval(0.5, getSlideToPos, fluid=1)), name='makeToonReleaseInterval-toonIval') + grabIval.append(toonIval) + else: + grabIval = Sequence(Func(self.detachClub)) + if not toon.isEmpty(): + toonIval = Sequence(Parallel(ActorInterval(toon, 'walk', duration=1.0, playRate=-1.0), LerpPosInterval(toon, duration=1.0, pos=Point3(-10, 0, 0))), Func(toon.wrtReparentTo, render)) + grabIval.append(toonIval) + if localAvatar.doId == toon.doId: + if not self.goingToReward and toon.hp > 0: + grabIval.append(Func(self.goToFinalBattle)) + grabIval.append(Func(self.notify.debug, 'goingToFinalBattlemode')) + grabIval.append(Func(self.safeBossToFinalBattleMode)) + return grabIval + + def safeBossToFinalBattleMode(self): + if self.boss: + self.boss.toFinalBattleMode() + + def goToFinalBattle(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + curState = place.fsm.getCurrentState().getName() + if place.fsm.getCurrentState().getName() == 'crane': + place.setState('finalBattle') + else: + self.notify.debug('NOT going to final battle, state=%s' % curState) + + def attachClub(self, avId, pointToBall = False): + club = self.club + if club: + av = base.cr.doId2do.get(avId) + if av: + av.useLOD(1000) + lHand = av.getLeftHands()[0] + club.setPos(0, 0, 0) + club.reparentTo(lHand) + netScale = club.getNetTransform().getScale()[1] + counterActToonScale = lHand.find('**/counteractToonScale') + if counterActToonScale.isEmpty(): + counterActToonScale = lHand.attachNewNode('counteractToonScale') + counterActToonScale.setScale(1 / netScale) + self.notify.debug('creating counterActToonScale for %s' % av.getName()) + club.reparentTo(counterActToonScale) + club.setX(-0.25 * netScale) + if pointToBall: + club.lookAt(self.clubLookatSpot) + + def detachClub(self): + if not self.club.isEmpty(): + self.club.reparentTo(self.root) + self.club.setZ(-20) + self.club.setScale(1) + + def adjustClub(self): + club = self.club + if club: + distance = club.getDistance(self.clubLookatSpot) + scaleFactor = distance / 2.058 + club.setScale(1, scaleFactor, 1) + + def startAdjustClubTask(self): + taskMgr.add(self.adjustClubTask, self.adjustClubTaskName) + + def stopAdjustClubTask(self): + taskMgr.remove(self.adjustClubTaskName) + + def adjustClubTask(self, task): + self.attachClub(self.avId, True) + self.adjustClub() + return task.cont + + def enableControlKey(self): + self.controlKeyAllowed = True + + def disableControlKey(self): + self.controlKeyAllowed = False + + def sendSwingInfo(self, power, angle, sequenceNum): + self.sendUpdate('setSwingInfo', [power, angle, sequenceNum]) + + def startBallPlayback(self, power, angle, sequenceNum): + flyBall = self.ballModel.copyTo(NodePath()) + flyBall.setScale(1.0) + flyBallBubble = self.getFlyBallBubble().instanceTo(NodePath()) + flyBallBubble.reparentTo(flyBall) + flyBall.setTag('pieSequence', str(sequenceNum)) + flyBall.setTag('throwerId', str(self.avId)) + t = power / 100.0 + t = 1.0 - t + dist = 300 - 200 * t + time = 1.5 + 0.5 * t + proj = ProjectileInterval(None, startPos=Point3(0, 0, 0), endPos=Point3(0, dist, 0), duration=time) + relVel = proj.startVel + + def getVelocity(root = self.root, relVel = relVel): + return render.getRelativeVector(root, relVel) + + fly = Sequence(Func(flyBall.reparentTo, render), Func(flyBall.setPosHpr, self.root, 0, 0, 0, 0, 0, 0), Func(base.cTrav.addCollider, flyBallBubble, self.flyBallHandler), ProjectileInterval(flyBall, startVel=getVelocity, duration=3), Func(flyBall.detachNode), Func(base.cTrav.removeCollider, flyBallBubble), Func(self.notify.debug, 'removed collider'), Func(self.flyBallFinishedFlying, sequenceNum)) + flyWithSound = Parallel(fly, SoundInterval(self.hitBallSfx, node=self.root), name='flyWithSound') + self.notify.debug('starting flyball track') + flyWithSound.start() + self.flyBallTracks[sequenceNum] = flyWithSound + return + + def setSwingInfo(self, power, angle, sequenceNum): + av = base.cr.doId2do.get(self.avId) + self.swingInterval = Sequence() + if av: + self.stopAdjustClubTask() + self.swingInterval = Sequence(ActorInterval(av, 'swing-putt', startFrame=0, endFrame=GolfGlobals.BALL_CONTACT_FRAME), Func(self.startBallPlayback, power, angle, sequenceNum), Func(self.ballModel.hide), ActorInterval(av, 'swing-putt', startFrame=GolfGlobals.BALL_CONTACT_FRAME, endFrame=24), Func(self.ballModel.setScale, 0.1), Func(self.ballModel.show), LerpScaleInterval(self.ballModel, 1.0, Point3(1, 1, 1)), Func(self.enableControlKey)) + if av == localAvatar: + self.swingInterval.append(Func(self.switchToAnimState, 'GolfPuttLoop', True)) + self.swingInterval.start() + + def getFlyBallBubble(self): + if self.__flyBallBubble == None: + bubble = CollisionSphere(0, 0, 0, GolfGlobals.GOLF_BALL_RADIUS) + node = CollisionNode('flyBallBubble') + node.addSolid(bubble) + node.setFromCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask | ToontownGlobals.FloorBitmask) + node.setIntoCollideMask(BitMask32.allOff()) + self.__flyBallBubble = NodePath(node) + self.flyBallHandler = CollisionHandlerEvent() + self.flyBallHandler.addInPattern('flyBallHit-%d' % self.index) + return self.__flyBallBubble + + def __flyBallHit(self, entry): + print entry + + def flyBallFinishedFlying(self, sequence): + if sequence in self.flyBallTracks: + del self.flyBallTracks[sequence] + + def __finishFlyBallTrack(self, sequence): + if sequence in self.flyBallTracks: + flyBallTrack = self.flyBallTracks[sequence] + del self.flyBallTracks[sequence] + flyBallTrack.finish() + + def flyBallFinishedSplatting(self, sequence): + if sequence in self.splatTracks: + del self.splatTracks[sequence] + + def __flyBallHit(self, entry): + if not entry.hasSurfacePoint() or not entry.hasInto(): + return + if not entry.getInto().isTangible(): + return + sequence = int(entry.getFromNodePath().getNetTag('pieSequence')) + self.__finishFlyBallTrack(sequence) + if sequence in self.splatTracks: + splatTrack = self.splatTracks[sequence] + del self.splatTracks[sequence] + splatTrack.finish() + flyBallCode = 0 + flyBallCodeStr = entry.getIntoNodePath().getNetTag('pieCode') + if flyBallCodeStr: + flyBallCode = int(flyBallCodeStr) + pos = entry.getSurfacePoint(render) + timestamp32 = globalClockDelta.getFrameNetworkTime(bits=32) + throwerId = int(entry.getFromNodePath().getNetTag('throwerId')) + splat = self.getFlyBallSplatInterval(pos[0], pos[1], pos[2], flyBallCode, throwerId) + splat = Sequence(splat, Func(self.flyBallFinishedSplatting, sequence)) + self.splatTracks[sequence] = splat + splat.start() + self.notify.debug('doId=%d into=%s flyBallCode=%d, throwerId=%d' % (self.doId, + entry.getIntoNodePath(), + flyBallCode, + throwerId)) + if flyBallCode == ToontownGlobals.PieCodeBossCog and self.avId == localAvatar.doId and self.lastHitSequenceNum != self.__flyBallSequenceNum: + self.lastHitSequenceNum = self.__flyBallSequenceNum + self.boss.d_ballHitBoss(10) + elif flyBallCode == ToontownGlobals.PieCodeToon and self.avId == localAvatar.doId and self.lastHitSequenceNum != self.__flyBallSequenceNum: + self.lastHitSequenceNum = self.__flyBallSequenceNum + avatarDoId = entry.getIntoNodePath().getNetTag('avatarDoId') + if avatarDoId == '': + self.notify.warning('Toon %s has no avatarDoId tag.' % repr(entry.getIntoNodePath())) + return + doId = int(avatarDoId) + if doId != localAvatar.doId: + pass + + def getFlyBallSplatInterval(self, x, y, z, flyBallCode, throwerId): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + splatName = 'dust' + splat = BattleProps.globalPropPool.getProp(splatName) + splat.setBillboardPointWorld(2) + color = ToontownGlobals.PieCodeColors.get(flyBallCode) + if color: + splat.setColor(*color) + if flyBallCode == ToontownGlobals.PieCodeBossCog: + self.notify.debug('changing color to %s' % self.ballColor) + splat.setColor(self.ballColor) + sound = loader.loadSfx('phase_11/audio/sfx/LB_evidence_miss.ogg') + vol = 1.0 + if flyBallCode == ToontownGlobals.PieCodeBossCog: + sound = loader.loadSfx('phase_4/audio/sfx/Golf_Hit_Barrier_1.ogg') + soundIval = SoundInterval(sound, node=splat, volume=vol) + if flyBallCode == ToontownGlobals.PieCodeBossCog and localAvatar.doId == throwerId: + vol = 1.0 + soundIval = SoundInterval(sound, node=localAvatar, volume=vol) + ival = Parallel(Func(splat.reparentTo, render), Func(splat.setPos, x, y, z), soundIval, Sequence(ActorInterval(splat, splatName), Func(splat.detachNode))) + return ival + + def setGoingToReward(self): + self.goingToReward = True + + def gotBossZapped(self): + self.showExiting() + self.d_requestFree(True) diff --git a/toontown/coghq/DistributedGolfSpotAI.py b/toontown/coghq/DistributedGolfSpotAI.py new file mode 100755 index 00000000..e00f76fc --- /dev/null +++ b/toontown/coghq/DistributedGolfSpotAI.py @@ -0,0 +1,82 @@ +from direct.distributed import DistributedObjectAI +from direct.fsm import FSM + +class DistributedGolfSpotAI(DistributedObjectAI.DistributedObjectAI, FSM.FSM): + + def __init__(self, air, boss, index): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedGolfSpotAI') + self.boss = boss + self.index = index + self.avId = 0 + self.allowControl = True + + def delete(self): + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def d_setState(self, state, avId, extraInfo = 0): + self.sendUpdate('setState', [state, avId, extraInfo]) + + def requestControl(self): + if not self.allowControl: + return + avId = self.air.getAvatarIdFromSender() + if avId in self.boss.involvedToons and self.avId == 0 and self.state != 'Off': + golfSpotId = self.__getGolfSpotId(avId) + if golfSpotId == 0: + grantRequest = True + if self.boss and not self.boss.isToonRoaming(avId): + grantRequest = False + if grantRequest: + self.request('Controlled', avId) + + def requestFree(self, gotHitByBoss): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId and self.state == 'Controlled': + self.request('Free', gotHitByBoss) + + def forceFree(self): + self.request('Free', 0) + + def removeToon(self, avId): + if avId == self.avId: + self.request('Free') + + def __getGolfSpotId(self, avId): + if self.boss and self.boss.golfSpots != None: + for golfSpot in self.boss.golfSpots: + if golfSpot.avId == avId: + return golfSpot.doId + + return 0 + + def turnOff(self): + self.request('Off') + self.allowControl = False + + def enterOff(self): + self.sendUpdate('setGoingToReward', []) + self.d_setState('O', 0) + + def exitOff(self): + pass + + def enterControlled(self, avId): + self.avId = avId + self.d_setState('C', avId) + + def exitControlled(self): + pass + + def enterFree(self, gotHitByBoss): + self.avId = 0 + self.d_setState('F', 0, gotHitByBoss) + + def exitFree(self): + pass diff --git a/toontown/coghq/DistributedGrid.py b/toontown/coghq/DistributedGrid.py new file mode 100755 index 00000000..b76a7288 --- /dev/null +++ b/toontown/coghq/DistributedGrid.py @@ -0,0 +1,69 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from CrateGlobals import * +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal + +class DistributedGrid(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGrid') + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.model = None + return + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.loadModel() + + def disable(self): + self.notify.debug('disable') + BasicEntities.DistributedNodePathEntity.disable(self) + self.unloadModel() + self.ignoreAll() + + def delete(self): + BasicEntities.DistributedNodePathEntity.delete(self) + + def loadModel(self): + self.notify.debug('loadModel') + texSize = 6.0 + scale = self.cellSize / texSize + self.model = loader.loadModel('phase_9/models/cogHQ/FloorWear.bam') + self.model.reparentTo(self) + long = self.numCol + short = self.numRow + h = 0 + if self.numCol < self.numRow: + long = self.numRow + short = self.numCol + h = 90 + self.model.setScale(scale * long, scale * short, 1) + self.model.setHpr(h, 180, 0) + self.model.setPos(self.cellSize * self.numCol / 2.0, self.cellSize * self.numRow / 2.0, 0.025) + self.model.setColor(0.588, 0.588, 0.459, 0.4) + + def unloadModel(self): + if self.model: + self.model.removeNode() + del self.model + + def setNumRow(self, rows): + self.numRow = rows + self.unloadModel() + self.loadModel() + + def setNumCol(self, cols): + self.numCol = cols + self.unloadModel() + self.loadModel() diff --git a/toontown/coghq/DistributedGridAI.py b/toontown/coghq/DistributedGridAI.py new file mode 100755 index 00000000..8b620f49 --- /dev/null +++ b/toontown/coghq/DistributedGridAI.py @@ -0,0 +1,250 @@ +from CrateGlobals import * +from otp.level import DistributedEntityAI +from direct.directnotify import DirectNotifyGlobal + +class DistributedGridAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGridAI') + + def __init__(self, level, entId): + self.initialized = 0 + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.activeCellList = [] + + def delete(self): + DistributedEntityAI.DistributedEntityAI.delete(self) + del self.activeCellList + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + + def initializeGrid(self): + if not self.initialized: + self.objPos = {} + self.gridCells = [None] * self.numRow + for i in xrange(len(self.gridCells)): + self.gridCells[i] = [None] * self.numCol + for j in xrange(len(self.gridCells[i])): + self.gridCells[i][j] = [] + + self.initialized = 1 + return + + def addActiveCell(self, cell): + self.activeCellList.append(cell) + + def getObjPos(self, objId): + objPos = self.objPos.get(objId, None) + if objPos: + row, col = objPos + if row >= 0 and row < self.numRow and col >= 0 and col < self.numCol: + return [(col + 1) * self.cellSize, (row + 1) * self.cellSize, 0] + else: + self.notify.debug('row/col out of range %s/%s' % (row, col)) + else: + self.notify.debug("didn't have record of obj") + return + + def addObjectByPos(self, objId, pos, width = 1): + if not self.initialized: + self.initializeGrid() + if self.objPos.get(objId, None): + return 1 + x, y = pos[0], pos[1] + col = min(int(x / self.cellSize), self.numCol - width) + row = min(int(y / self.cellSize), self.numRow - width) + self.notify.debug('attempt add %d at %s, row,col = %d,%d' % (objId, + pos, + row, + col)) + while col >= 0 and col < self.numCol: + while row >= 0 and row < self.numRow: + if self.addObjectByRowCol(objId, row, col): + return 1 + else: + row += 2 + else: + row = 0 + col += 2 + + else: + self.notify.debug('requestObjPos: row/col out of range %s/%s' % (row, col)) + row = min(row, self.numRow) + row = max(0, row) + col = min(col, self.numRow) + col = max(0, col) + return self.addObjectByRowCol(objId, row, col) + + return + + def addObjectByRowCol(self, objId, row, col): + if row >= 0 and row < self.numRow - 1 and col >= 0 and col < self.numCol - 1: + self.notify.debug('adding obj %s to grid cell %s,%s' % (objId, row, col)) + self.gridCells[row][col].append(objId) + self.gridCells[row + 1][col].append(objId) + self.gridCells[row][col + 1].append(objId) + self.gridCells[row + 1][col + 1].append(objId) + self.objPos[objId] = [row, col] + self.__setChangedActiveCells(onList=[[row, col], + [row + 1, col], + [row, col + 1], + [row + 1, col + 1]], objId=objId) + return 1 + self.notify.debug("couldn't obj to grid cell %s,%s" % (row, col)) + return 0 + + def removeObject(self, objId): + objPos = self.objPos.get(objId) + if not objPos: + return + row, col = objPos + self.notify.debug('removing obj %s from %s, %s' % (objId, row, col)) + self.gridCells[row][col].remove(objId) + self.gridCells[row + 1][col].remove(objId) + self.gridCells[row][col + 1].remove(objId) + self.gridCells[row + 1][col + 1].remove(objId) + del self.objPos[objId] + self.__setChangedActiveCells(offList=[[row, col], + [row + 1, col], + [row, col + 1], + [row + 1, col + 1]], objId=objId) + + def checkMoveDir(self, objId, h): + if h > 225 and h < 315: + return self.checkMove(objId, 0, 1) + elif h > 45 and h < 135: + return self.checkMove(objId, 0, -1) + elif h < 45 or h > 315: + return self.checkMove(objId, 1, 0) + elif h > 135 and h < 225: + return self.checkMove(objId, -1, 0) + + def doMoveDir(self, objId, h): + if h > 225 and h < 315: + return self.doMove(objId, 0, 1) + elif h > 45 and h < 135: + return self.doMove(objId, 0, -1) + elif h < 45 or h > 315: + return self.doMove(objId, 1, 0) + elif h > 135 and h < 225: + return self.doMove(objId, -1, 0) + + def checkPush(self, objId, side): + if side == 0: + return self.checkMove(objId, 0, -1) + elif side == 1: + return self.checkMove(objId, 0, 1) + elif side == 2: + return self.checkMove(objId, -1, 0) + elif side == 3: + return self.checkMove(objId, 1, 0) + + def doPush(self, objId, side): + if side == 0: + return self.doMove(objId, 0, -1) + elif side == 1: + return self.doMove(objId, 0, 1) + elif side == 2: + return self.doMove(objId, -1, 0) + elif side == 3: + return self.doMove(objId, 1, 0) + + def checkMove(self, objId, dRow, dCol): + objPos = self.objPos.get(objId) + if not objPos: + return + row, col = objPos + validMove = 1 + if dRow < 0: + validMove = validMove & self.__isEmpty(row - 1, col) & self.__isEmpty(row - 1, col + 1) + elif dRow > 0: + validMove = validMove & self.__isEmpty(row + 2, col) & self.__isEmpty(row + 2, col + 1) + if dCol < 0: + validMove = validMove & self.__isEmpty(row, col - 1) & self.__isEmpty(row + 1, col - 1) + elif dCol > 0: + validMove = validMove & self.__isEmpty(row, col + 2) & self.__isEmpty(row + 1, col + 2) + return validMove + + def doMove(self, objId, dRow, dCol): + objPos = self.objPos.get(objId) + if not objPos: + return 0 + row, col = objPos + validMove = self.checkMove(objId, dRow, dCol) + if validMove: + self.gridCells[row][col].remove(objId) + self.gridCells[row + 1][col].remove(objId) + self.gridCells[row][col + 1].remove(objId) + self.gridCells[row + 1][col + 1].remove(objId) + newRow = row + dRow + newCol = col + dCol + self.gridCells[newRow][newCol].append(objId) + self.gridCells[newRow + 1][newCol].append(objId) + self.gridCells[newRow][newCol + 1].append(objId) + self.gridCells[newRow + 1][newCol + 1].append(objId) + self.objPos[objId] = [newRow, newCol] + self.updateActiveCells(objId, row, col, dRow, dCol) + return validMove + + def updateActiveCells(self, objId, row, col, dRow, dCol): + newRow = row + dRow + newCol = col + dCol + newCells = [[newRow, newCol], + [newRow + 1, newCol], + [newRow, newCol + 1], + [newRow + 1, newCol + 1]] + oldCells = [[row, col], + [row + 1, col], + [row, col + 1], + [row + 1, col + 1]] + onList = [] + offList = [] + for cell in newCells: + if cell not in oldCells: + onList.append(cell) + + for cell in oldCells: + if cell not in newCells: + offList.append(cell) + + self.__setChangedActiveCells(onList, offList, objId) + + def __setChangedActiveCells(self, onList = [], offList = [], objId = None): + for cell in self.activeCellList: + self.notify.debug('onList = %s, offList = %s, cell = %s' % (onList, offList, cell.getRowCol())) + if cell.getRowCol() in onList: + cell.b_setState(1, objId) + elif cell.getRowCol() in offList: + cell.b_setState(0, objId) + + def __isEmpty(self, row, col): + if row < 0 or row >= self.numRow or col < 0 or col >= self.numCol: + return 0 + if len(self.gridCells[row][col]) > 0: + return 0 + return 1 + + def printGrid(self): + if not __debug__: + return + for i in xrange(len(self.gridCells)): + str = '' + for j in xrange(len(self.gridCells[i])): + col = self.gridCells[i][j] + active = 0 + for cell in self.activeCellList: + if cell.getRowCol() == [i, j]: + active = 1 + + if len(col) > 0: + if active: + str += '[X]' + else: + str += ' X ' + elif active: + str += '[.]' + else: + str += ' . ' + + print str + ' : %d' % i + + print '' diff --git a/toontown/coghq/DistributedGridGoonAI.py b/toontown/coghq/DistributedGridGoonAI.py new file mode 100755 index 00000000..9c21a4ba --- /dev/null +++ b/toontown/coghq/DistributedGridGoonAI.py @@ -0,0 +1,52 @@ +from otp.ai.AIBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import SuitBattleGlobals +import DistributedGoonAI +from direct.task.Task import Task +from toontown.coghq import DistributedCrushableEntityAI +import random + +class DistributedGridGoonAI(DistributedGoonAI.DistributedGoonAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGridGoonAI') + + def __init__(self, level, entId): + self.grid = None + self.h = 0 + DistributedGoonAI.DistributedGoonAI.__init__(self, level, entId) + return + + def generate(self): + self.notify.debug('generate') + DistributedCrushableEntityAI.DistributedCrushableEntityAI.generate(self) + + def initGridDependents(self): + taskMgr.doMethodLater(2, self.goToNextPoint, self.taskName('walkTask')) + + def getPosition(self): + if self.grid: + return self.grid.getObjPos(self.entId) + + def getH(self): + return self.h + + def goToNextPoint(self, task): + if not self.grid: + self.notify.warning("couldn't find grid, not starting") + return + if self.grid.checkMoveDir(self.entId, self.h): + ptA = Point3(*self.getPosition()) + self.grid.doMoveDir(self.entId, self.h) + ptB = Point3(*self.getPosition()) + self.sendUpdate('setPathPts', [ptA[0], + ptA[1], + ptA[2], + ptB[0], + ptB[1], + ptB[2]]) + tPathSegment = Vec3(ptA - ptB).length() / self.velocity + else: + turn = int(random.randrange(1, 4) * 90) + self.h = (self.h + turn) % 360 + tPathSegment = 0.1 + taskMgr.doMethodLater(tPathSegment, self.goToNextPoint, self.taskName('walkTask')) + return Task.done diff --git a/toontown/coghq/DistributedHealBarrel.py b/toontown/coghq/DistributedHealBarrel.py new file mode 100755 index 00000000..ffcfdb8b --- /dev/null +++ b/toontown/coghq/DistributedHealBarrel.py @@ -0,0 +1,31 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +import DistributedBarrelBase + +class DistributedHealBarrel(DistributedBarrelBase.DistributedBarrelBase): + + def __init__(self, cr): + DistributedBarrelBase.DistributedBarrelBase.__init__(self, cr) + self.numGags = 0 + self.gagScale = 0.6 + + def disable(self): + DistributedBarrelBase.DistributedBarrelBase.disable(self) + self.ignoreAll() + + def delete(self): + self.gagModel.removeNode() + del self.gagModel + DistributedBarrelBase.DistributedBarrelBase.delete(self) + + def applyLabel(self): + self.gagModel = loader.loadModel('phase_4/models/props/icecream') + self.gagModel.reparentTo(self.gagNode) + self.gagModel.find('**/p1_2').clearBillboard() + self.gagModel.setScale(self.gagScale) + self.gagModel.setPos(0, -0.1, -.1 - self.gagScale) + + def setGrab(self, avId): + DistributedBarrelBase.DistributedBarrelBase.setGrab(self, avId) diff --git a/toontown/coghq/DistributedHealBarrelAI.py b/toontown/coghq/DistributedHealBarrelAI.py new file mode 100755 index 00000000..2b1c01a5 --- /dev/null +++ b/toontown/coghq/DistributedHealBarrelAI.py @@ -0,0 +1,16 @@ +import DistributedBarrelBaseAI +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task + +class DistributedHealBarrelAI(DistributedBarrelBaseAI.DistributedBarrelBaseAI): + + def __init__(self, level, entId): + x = y = z = h = 0 + DistributedBarrelBaseAI.DistributedBarrelBaseAI.__init__(self, level, entId) + + def d_setGrab(self, avId): + self.notify.debug('d_setGrab %s' % avId) + self.sendUpdate('setGrab', [avId]) + av = self.air.doId2do.get(avId) + if av: + av.toonUp(self.getRewardPerGrab()) diff --git a/toontown/coghq/DistributedLaserField.py b/toontown/coghq/DistributedLaserField.py new file mode 100755 index 00000000..370e0265 --- /dev/null +++ b/toontown/coghq/DistributedLaserField.py @@ -0,0 +1,712 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from StomperGlobals import * +from direct.distributed import ClockDelta +from direct.showbase.PythonUtil import lerp +import math +from otp.level import DistributedEntity +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath +from otp.level import BasicEntities +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from toontown.coghq import BattleBlocker +from toontown.toonbase import TTLocalizer +import random + +class DistributedLaserField(BattleBlocker.BattleBlocker): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLaserField') + laserFieldModels = ['phase_9/models/cogHQ/square_stomper'] + + def __init__(self, cr): + BattleBlocker.BattleBlocker.__init__(self, cr) + node = hidden.attachNewNode('DistributedNodePathEntity') + if not hasattr(self, 'radius'): + self.radius = 5 + if not hasattr(self, 'blockerX'): + self.blockerX = 0.0 + if not hasattr(self, 'blockerY'): + self.blockerY = 0.0 + self.blockerZ = -10000 + self.gridWireGN = None + self.gridBeamGN = None + self.traceWireGN = None + self.traceBeamGN = None + self.gridSeed = 0 + self.gridScaleX = 2.0 + self.gridScaleY = 2.0 + self.zFloat = 0.05 + self.projector = Point3(0, 0, 25) + self.tracePath = [] + self.gridData = [[0, 0]] * 2 + self.gridNumX = 2 + self.gridNumY = 2 + self.tracePath.append((0.51, 0.51, 1)) + self.tracePath.append((0.59, 0.51, 1)) + self.tracePath.append((0.55, 0.59, 1)) + self.tracePath.append((0.51, 0.51, 1)) + self.gridSymbols = [] + self.makeGridSymbols() + self.isToonIn = 0 + self.toonX = -1 + self.toonY = -1 + self.isToonInRange = 0 + self.detectCount = 0 + self.cameraHold = None + self.gridGame = 'some game' + self.gridGameText = ' ' + self.activeLF = 1 + self.successSound = loader.loadSfx('phase_11/audio/sfx/LB_capacitor_discharge_3.ogg') + self.successTrack = Parallel(SoundInterval(self.successSound, node=self, volume=0.8)) + self.failSound = loader.loadSfx('phase_11/audio/sfx/LB_sparks_1.ogg') + self.failTrack = Parallel(SoundInterval(self.failSound, node=self, volume=0.8)) + return + + def generateInit(self): + self.notify.debug('generateInit') + BattleBlocker.BattleBlocker.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BattleBlocker.BattleBlocker.announceGenerate(self) + self.gridWireNode = self.attachNewNode('grid Wire Node') + self.gridWireGN = GeomNode('grid wire') + self.gridWireNode.attachNewNode(self.gridWireGN) + self.gridWireNode.setRenderModeWireframe() + self.gridWireNode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.gridWireNode.setTwoSided(True) + self.gridWireNode.setBin('fixed', 1) + self.gridWireNode.setDepthWrite(False) + self.gridBeamNode = self.attachNewNode('grid Beam Node') + self.gridBeamGN = GeomNode('grid beam') + self.gridBeamNode.attachNewNode(self.gridBeamGN) + self.gridBeamNode.setTransparency(TransparencyAttrib.MAlpha) + self.gridBeamNode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.gridBeamNode.setTwoSided(True) + self.gridBeamNode.setBin('fixed', 1) + self.gridBeamNode.setDepthWrite(False) + self.traceWireNode = self.attachNewNode('trace Wire Node') + self.traceWireGN = GeomNode('trace Wire') + self.traceWireNode.attachNewNode(self.traceWireGN) + self.traceWireNode.setRenderModeWireframe() + self.traceWireNode.setTransparency(TransparencyAttrib.MAlpha) + self.traceWireNode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.traceWireNode.setTwoSided(True) + self.traceWireNode.setBin('fixed', 1) + self.traceWireNode.setDepthWrite(False) + self.traceBeamNode = self.attachNewNode('trace Beam Node') + self.traceBeamGN = GeomNode('trace Beam') + self.traceBeamNode.attachNewNode(self.traceBeamGN) + self.traceBeamNode.setTransparency(TransparencyAttrib.MAlpha) + self.traceBeamNode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.traceBeamNode.setTwoSided(True) + self.traceBeamNode.setBin('fixed', 1) + self.traceBeamNode.setDepthWrite(False) + self.loadModel() + self.detectName = 'laserField %s' % self.doId + taskMgr.doMethodLater(0.1, self.__detect, self.detectName) + + def setGridGame(self, gameName): + self.gridGame = gameName + self.gridGameText = gameName + if gameName == 'MineSweeper': + self.gridGameText = TTLocalizer.LaserGameMine + elif gameName == 'Roll': + self.gridGameText = TTLocalizer.LaserGameRoll + elif gameName == 'Avoid': + self.gridGameText = TTLocalizer.LaserGameAvoid + elif gameName == 'Drag': + self.gridGameText = TTLocalizer.LaserGameDrag + else: + self.gridGameText = TTLocalizer.LaserGameDefault + + def setActiveLF(self, active = 1): + if active: + self.activeLF = 1 + else: + self.activeLF = 0 + + def setSuccess(self, success): + if success: + self.successTrack.start() + else: + self.startBattle() + self.failTrack.start() + self.cSphereNodePath.setPos(self.blockerX, self.blockerY, 0) + + def makeGridSymbols(self): + symbolBlank = [0, (1.0, 0.0, 0.0), ()] + symbolOne = [None, (1.0, 0.0, 0.0), ((0.45, 0.8), + (0.55, 0.8), + (0.55, 0.2), + (0.45, 0.2), + (0.45, 0.8))] + symbolTwo = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.7, 0.8), + (0.7, 0.45), + (0.4, 0.45), + (0.4, 0.3), + (0.7, 0.3), + (0.7, 0.2), + (0.3, 0.2), + (0.3, 0.55), + (0.6, 0.55), + (0.6, 0.7), + (0.3, 0.7), + (0.3, 0.8))] + symbolThree = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.3, 0.2), + (0.3, 0.3), + (0.6, 0.3), + (0.6, 0.45), + (0.4, 0.45), + (0.4, 0.55), + (0.6, 0.55), + (0.6, 0.7), + (0.3, 0.7), + (0.3, 0.8))] + symbolFour = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.4, 0.8), + (0.4, 0.6), + (0.6, 0.6), + (0.6, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.6, 0.2), + (0.6, 0.5), + (0.3, 0.5), + (0.3, 0.8))] + symbolFive = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.4, 0.8), + (0.4, 0.6), + (0.6, 0.6), + (0.6, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.6, 0.2), + (0.6, 0.5), + (0.3, 0.5), + (0.3, 0.8))] + symbolSix = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.4, 0.8), + (0.4, 0.6), + (0.6, 0.6), + (0.6, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.6, 0.2), + (0.6, 0.5), + (0.3, 0.5), + (0.3, 0.8))] + symbolSeven = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.4, 0.8), + (0.4, 0.6), + (0.6, 0.6), + (0.6, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.6, 0.2), + (0.6, 0.5), + (0.3, 0.5), + (0.3, 0.8))] + symbolEight = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.4, 0.8), + (0.4, 0.6), + (0.6, 0.6), + (0.6, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.6, 0.2), + (0.6, 0.5), + (0.3, 0.5), + (0.3, 0.8))] + symbolNine = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.4, 0.8), + (0.4, 0.6), + (0.6, 0.6), + (0.6, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.6, 0.2), + (0.6, 0.5), + (0.3, 0.5), + (0.3, 0.8))] + symbolSquare = [None, (1.0, 0.0, 0.0), ((0.1, 0.9), + (0.9, 0.9), + (0.9, 0.1), + (0.1, 0.1), + (0.1, 0.9))] + symbolTriangle = [None, (0.0, 1.0, 0.0), ((0.1, 0.1), + (0.5, 0.9), + (0.9, 0.1), + (0.1, 0.1))] + symbolBlueSquare = [None, (0.3, 0.3, 1.0), ((0.1, 0.9), + (0.9, 0.9), + (0.9, 0.1), + (0.1, 0.1), + (0.1, 0.9))] + symbolHiddenBomb = [self.sendFail, (1.0, 0.0, 0.0), ((0.1, 0.9), + (0.9, 0.9), + (0.9, 0.1), + (0.1, 0.1), + (0.1, 0.9))] + symbolBomb = [self.sendFail, (1.0, 0.0, 0.0), ((0.8, 1.0), + (1.0, 0.8), + (0.8, 0.6), + (0.8, 0.4), + (0.6, 0.2), + (0.4, 0.2), + (0.2, 0.4), + (0.2, 0.6), + (0.4, 0.8), + (0.6, 0.8), + (0.8, 1.0))] + symbolSkull = [self.sendFail, (1.0, 0.0, 0.0), ((0.5, 0.9), + (0.7, 0.8), + (0.7, 0.6), + (0.6, 0.5), + (0.6, 0.4), + (0.5, 0.4), + (0.5, 0.3), + (0.7, 0.4), + (0.8, 0.4), + (0.8, 0.3), + (0.7, 0.3), + (0.6, 0.25), + (0.7, 0.2), + (0.8, 0.2), + (0.8, 0.1), + (0.7, 0.1), + (0.5, 0.2), + (0.3, 0.1), + (0.2, 0.1), + (0.2, 0.2), + (0.3, 0.2), + (0.4, 0.25), + (0.3, 0.3), + (0.2, 0.3), + (0.2, 0.4), + (0.3, 0.4), + (0.5, 0.3), + (0.5, 0.4), + (0.4, 0.4), + (0.4, 0.5), + (0.3, 0.6), + (0.3, 0.8), + (0.5, 0.9))] + symbolDot = [None, (1.0, 0.0, 0.0), ((0.4, 0.6), + (0.6, 0.6), + (0.6, 0.4), + (0.4, 0.4), + (0.4, 0.6))] + symbolRedX = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.5, 0.6), + (0.7, 0.8), + (0.8, 0.7), + (0.6, 0.5), + (0.8, 0.3), + (0.7, 0.2), + (0.5, 0.4), + (0.3, 0.2), + (0.2, 0.3), + (0.4, 0.5), + (0.2, 0.7), + (0.3, 0.8))] + self.symbolSelect = [(1.0, 0.0, 0.0), ((0.05, 0.95), + (0.95, 0.95), + (0.95, 0.05), + (0.05, 0.05), + (0.05, 0.95))] + symbolBlueDot = [None, (0.5, 0.5, 1.0), ((0.5, 0.7), + (0.7, 0.5), + (0.5, 0.3), + (0.3, 0.5), + (0.5, 0.7))] + self.gridSymbols = [] + self.gridSymbols.append(symbolBlank) + self.gridSymbols.append(symbolOne) + self.gridSymbols.append(symbolTwo) + self.gridSymbols.append(symbolThree) + self.gridSymbols.append(symbolFour) + self.gridSymbols.append(symbolFive) + self.gridSymbols.append(symbolSix) + self.gridSymbols.append(symbolSeven) + self.gridSymbols.append(symbolEight) + self.gridSymbols.append(symbolNine) + self.gridSymbols.append(symbolSquare) + self.gridSymbols.append(symbolHiddenBomb) + self.gridSymbols.append(symbolSkull) + self.gridSymbols.append(symbolTriangle) + self.gridSymbols.append(symbolDot) + self.gridSymbols.append(symbolBlueSquare) + self.gridSymbols.append(symbolRedX) + self.gridSymbols.append(symbolBlueDot) + return None + + def sendFail(self): + self.sendUpdate('trapFire', []) + + def __detect(self, task): + distance = self.centerNode.getDistance(localAvatar) + greaterDim = self.gridScaleX + if self.gridScaleY > self.gridScaleX: + greaterDim = self.gridScaleY + self.detectCount += 1 + if self.detectCount > 5: + self.detectCount = 0 + if distance < greaterDim * 0.75: + if not self.isToonInRange: + self.doToonInRange() + if localAvatar.getPos(self)[0] > 0 and localAvatar.getPos(self)[0] < self.gridScaleX and localAvatar.getPos(self)[1] > 0 and localAvatar.getPos(self)[1] < self.gridScaleY: + self.__toonHit() + else: + if self.isToonIn: + self.toonX = -1 + self.toonY = -1 + self.isToonIn = 0 + self.genGrid() + self.isToonIn = 0 + elif self.isToonInRange: + self.doToonOutOfRange() + taskMgr.doMethodLater(0.1, self.__detect, self.detectName) + return Task.done + + def doToonInRange(self): + self.isToonInRange = 1 + if self.activeLF: + camHeight = base.localAvatar.getClampedAvatarHeight() + heightScaleFactor = camHeight * 0.3333333333 + defLookAt = Point3(0.0, 1.5, camHeight) + cameraPoint = Point3(0.0, -20.0 * heightScaleFactor, camHeight + 8.0) + base.localAvatar.setIdealCameraPos(cameraPoint) + self.level.stage.showInfoText(self.gridGameText) + + def doToonOutOfRange(self): + self.isToonInRange = 0 + base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex) + self.cameraHold = None + return + + def __toonHit(self): + posX = localAvatar.getPos(self)[0] + posY = localAvatar.getPos(self)[1] + tileX = int(posX / (self.gridScaleX / self.gridNumX)) + tileY = int(posY / (self.gridScaleY / self.gridNumY)) + oldX = self.toonX + oldY = self.toonY + if self.toonX != tileX or self.toonY != tileY or not self.isToonIn: + self.toonX = tileX + self.toonY = tileY + self.isToonIn = 1 + if self.gridData[tileX][tileY] < len(self.gridSymbols): + tileFunction = self.gridSymbols[self.gridData[tileX][tileY]][0] + if tileFunction: + tileFunction() + self.sendHit(self.toonX, self.toonY, oldX, oldY) + self.genGrid() + self.isToonIn = 1 + + def __testTile(self): + if self.toonX >= 0 and self.toonY >= 0 and self.toonX < self.gridNumX and self.toonY < self.gridNumY: + if self.gridData[self.toonX][self.toonY] < len(self.gridSymbols): + tileFunction = self.gridSymbols[self.gridData[self.toonX][self.toonY]][0] + if tileFunction: + tileFunction() + + def sendHit(self, newX, newY, oldX, oldY): + if self.toonX >= 0: + self.sendUpdate('hit', [newX, + newY, + oldX, + oldY]) + + def disable(self): + self.notify.debug('disable') + if self.failTrack.isPlaying(): + self.failTrack.finish() + if self.successTrack.isPlaying(): + self.successTrack.finish() + self.ignoreAll() + taskMgr.remove(self.detectName) + BattleBlocker.BattleBlocker.disable(self) + + def delete(self): + self.notify.debug('delete') + self.unloadModel() + BattleBlocker.BattleBlocker.delete(self) + + def loadModel(self): + self.rotateNode = self.attachNewNode('rotate') + self.model = None + self.centerNode = self.attachNewNode('center') + self.centerNode.setPos(self.gridScaleX * 0.5, self.gridScaleY * 0.5, 0.0) + self.genGrid() + return + + def unloadModel(self): + if self.model: + self.model.removeNode() + del self.model + self.model = None + return + + def genTrace(self): + wireRed = 1.0 + wireGreen = 0.0 + wireBlue = 0.0 + wireAlpha = 0.5 + beamRed = 0.04 + beamGreen = 0.0 + beamBlue = 0.0 + beamAlpha = 0.1 + self.gFormat = GeomVertexFormat.getV3cp() + self.traceWireVertexData = GeomVertexData('holds my vertices', self.gFormat, Geom.UHDynamic) + self.traceWireVertexWriter = GeomVertexWriter(self.traceWireVertexData, 'vertex') + self.traceWireColorWriter = GeomVertexWriter(self.traceWireVertexData, 'color') + self.traceBeamVertexData = GeomVertexData('holds my vertices', self.gFormat, Geom.UHDynamic) + self.traceBeamVertexWriter = GeomVertexWriter(self.traceBeamVertexData, 'vertex') + self.traceBeamColorWriter = GeomVertexWriter(self.traceBeamVertexData, 'color') + self.traceWireVertexWriter.addData3f(self.projector[0], self.projector[1], self.projector[2]) + self.traceWireColorWriter.addData4f(wireRed, wireGreen, wireBlue, wireAlpha) + self.traceBeamVertexWriter.addData3f(self.projector[0], self.projector[1], self.projector[2]) + self.traceBeamColorWriter.addData4f(0.0, 0.0, 0.0, 0.0) + for vertex in self.tracePath: + self.traceWireVertexWriter.addData3f(vertex[0] * self.gridScaleX, vertex[1] * self.gridScaleY, self.zFloat) + self.traceWireColorWriter.addData4f(wireRed, wireGreen, wireBlue, wireAlpha) + self.traceBeamVertexWriter.addData3f(vertex[0] * self.gridScaleX, vertex[1] * self.gridScaleY, self.zFloat) + self.traceBeamColorWriter.addData4f(beamRed, beamGreen, beamBlue, beamAlpha) + + self.traceBeamTris = GeomTriangles(Geom.UHStatic) + self.traceWireTris = GeomLinestrips(Geom.UHStatic) + vertexCounter = 1 + sizeTrace = len(self.tracePath) + chainTrace = sizeTrace - 1 + previousTrace = 1 + for countVertex in xrange(0, sizeTrace): + self.traceWireTris.addVertex(countVertex + 1) + + self.traceWireTris.closePrimitive() + for countVertex in xrange(0, chainTrace): + self.traceBeamTris.addVertex(0) + self.traceBeamTris.addVertex(countVertex + 1) + self.traceBeamTris.addVertex(countVertex + 2) + self.traceBeamTris.closePrimitive() + + self.traceWireGeom = Geom(self.traceWireVertexData) + self.traceWireGeom.addPrimitive(self.traceWireTris) + self.traceWireGN.addGeom(self.traceWireGeom) + self.traceBeamGeom = Geom(self.traceBeamVertexData) + self.traceBeamGeom.addPrimitive(self.traceBeamTris) + self.traceBeamGN.addGeom(self.traceBeamGeom) + + def createGrid(self): + gridScaleX = self.gridScaleX / self.gridNumX + gridScaleY = self.gridScaleY / self.gridNumY + red = 0.25 + green = 0.25 + blue = 0.25 + alpha = 0.5 + beamRed = 0.04 + beamGreen = 0.04 + beamBlue = 0.04 + beamAlpha = 0.1 + self.gFormat = GeomVertexFormat.getV3cp() + self.gridVertexData = GeomVertexData('holds my vertices', self.gFormat, Geom.UHDynamic) + self.gridVertexWriter = GeomVertexWriter(self.gridVertexData, 'vertex') + self.gridColorWriter = GeomVertexWriter(self.gridVertexData, 'color') + self.beamVertexData = GeomVertexData('holds my vertices', self.gFormat, Geom.UHDynamic) + self.beamVertexWriter = GeomVertexWriter(self.beamVertexData, 'vertex') + self.beamColorWriter = GeomVertexWriter(self.beamVertexData, 'color') + self.gridVertexWriter.addData3f(self.projector[0], self.projector[1], self.projector[2]) + self.gridColorWriter.addData4f(red, green, blue, 0.0) + self.beamVertexWriter.addData3f(self.projector[0], self.projector[1], self.projector[2]) + self.beamColorWriter.addData4f(0.0, 0.0, 0.0, 0.0) + border = 0.4 + for column in xrange(0, self.gridNumX): + columnLeft = 0.0 + gridScaleX * column + columnRight = columnLeft + gridScaleX + rowBottom = 0 + for row in xrange(0, self.gridNumY): + rowTop = rowBottom + gridScaleY + if self.gridData[column][row] and self.gridData[column][row] < len(self.gridSymbols): + gridColor = self.gridSymbols[self.gridData[column][row]][1] + gridSymbol = self.gridSymbols[self.gridData[column][row]][2] + sizeSymbol = len(gridSymbol) + for iVertex in xrange(sizeSymbol): + vertex = gridSymbol[iVertex] + self.gridVertexWriter.addData3f(columnLeft + vertex[0] * gridScaleX, rowBottom + vertex[1] * gridScaleY, self.zFloat) + self.gridColorWriter.addData4f(gridColor[0] * red, gridColor[1] * green, gridColor[2] * blue, alpha) + self.beamVertexWriter.addData3f(columnLeft + vertex[0] * gridScaleX, rowBottom + vertex[1] * gridScaleY, self.zFloat) + self.beamColorWriter.addData4f(gridColor[0] * beamRed, gridColor[1] * beamGreen, gridColor[2] * beamBlue, beamAlpha) + + rowBottom = rowTop + + if self.isToonIn: + gridSymbol = self.symbolSelect[1] + gridColor = self.symbolSelect[0] + sizeSymbol = len(gridSymbol) + for iVertex in xrange(sizeSymbol): + vertex = gridSymbol[iVertex] + self.gridVertexWriter.addData3f(self.toonX * gridScaleX + vertex[0] * gridScaleX, self.toonY * gridScaleY + vertex[1] * gridScaleY, self.zFloat) + self.gridColorWriter.addData4f(gridColor[0] * red, gridColor[1] * green, gridColor[2] * blue, alpha) + self.beamVertexWriter.addData3f(self.toonX * gridScaleX + vertex[0] * gridScaleX, self.toonY * gridScaleY + vertex[1] * gridScaleY, self.zFloat) + self.beamColorWriter.addData4f(gridColor[0] * beamRed, gridColor[1] * beamGreen, gridColor[2] * beamBlue, beamAlpha) + + self.gridTris = GeomLinestrips(Geom.UHDynamic) + self.beamTris = GeomTriangles(Geom.UHDynamic) + vertexCounter = 1 + for column in xrange(0, self.gridNumX): + for row in xrange(0, self.gridNumY): + if self.gridData[column][row] and self.gridData[column][row] < len(self.gridSymbols): + gridSymbol = self.gridSymbols[self.gridData[column][row]][2] + sizeSymbol = len(gridSymbol) + for iVertex in xrange(sizeSymbol): + self.gridTris.addVertex(vertexCounter + iVertex) + + self.gridTris.closePrimitive() + for iVertex in xrange(sizeSymbol - 1): + self.beamTris.addVertex(0) + self.beamTris.addVertex(vertexCounter + iVertex + 0) + self.beamTris.addVertex(vertexCounter + iVertex + 1) + self.beamTris.closePrimitive() + + vertexCounter += sizeSymbol + + if self.isToonIn: + gridSymbol = self.symbolSelect[1] + sizeSymbol = len(gridSymbol) + for iVertex in xrange(sizeSymbol): + self.gridTris.addVertex(vertexCounter + iVertex) + + self.gridTris.closePrimitive() + for iVertex in xrange(sizeSymbol - 1): + self.beamTris.addVertex(0) + self.beamTris.addVertex(vertexCounter + iVertex + 0) + self.beamTris.addVertex(vertexCounter + iVertex + 1) + self.beamTris.closePrimitive() + + self.wireGeom = Geom(self.gridVertexData) + self.wireGeom.addPrimitive(self.gridTris) + self.gridWireGN.addGeom(self.wireGeom) + self.beamGeom = Geom(self.beamVertexData) + self.beamGeom.addPrimitive(self.beamTris) + self.gridBeamGN.addGeom(self.beamGeom) + + def setGrid(self, gridNumX, gridNumY): + self.gridNumX = gridNumX + self.gridNumY = gridNumY + self.gridData = [] + for i in xrange(0, gridNumX): + self.gridData.append([0] * gridNumY) + + self.genGrid() + + def getGrid(self): + return (self.gridNumX, self.gridNumY) + + def setGridScaleX(self, gridScale): + self.gridScaleX = gridScale + self.centerNode.setPos(self.gridScaleX * 0.5, self.gridScaleY * 0.5, 0.0) + self.blockerX = self.gridScaleX * 0.5 + self.cSphereNodePath.setPos(self.blockerX, self.blockerY, self.blockerZ) + self.genGrid() + + def setGridScaleY(self, gridScale): + self.gridScaleY = gridScale + self.centerNode.setPos(self.gridScaleX * 0.5, self.gridScaleY * 0.5, 0.0) + self.blockerY = self.gridScaleY + self.cSphereNodePath.setPos(self.blockerX, self.blockerY, self.blockerZ) + self.genGrid() + + def setField(self, fieldData): + if fieldData[0] != self.gridNumX or fieldData[1] != self.gridNumY: + self.setGrid(fieldData[0], fieldData[1]) + fieldCounter = 2 + for column in xrange(0, self.gridNumX): + for row in xrange(0, self.gridNumY): + if len(fieldData) > fieldCounter: + self.gridData[column][row] = fieldData[fieldCounter] + fieldCounter += 1 + + self.genGrid() + self.__testTile() + + def getField(self): + fieldData.append(self.game.gridNumX) + fieldData.append(self.game.gridNumY) + fieldData = [] + for column in xrange(0, self.game.gridNumX): + for row in xrange(0, self.game.gridNumY): + fieldData.append(self.game.gridData[column][row]) + + return fieldData + + def setSeed(self, seed): + self.gridSeed = seed + random.seed(seed) + for column in xrange(0, self.gridNumX): + for row in xrange(0, self.gridNumY): + rint = random.randint(0, 2) + self.gridData[column][row] = rint + + self.genGrid() + + def getSeed(self): + return self.gridSeed + + def setMode(self, mode): + self.mode = mode + + def getMode(self): + return self.mode + + def setProjector(self, projPoint): + self.projector = projPoint + if self.gridWireGN and self.gridBeamGN: + self.genGrid() + + def genGrid(self): + if self.activeLF: + if self.gridWireGN: + self.gridWireGN.removeAllGeoms() + if self.gridBeamGN: + self.gridBeamGN.removeAllGeoms() + if self.gridWireGN and self.gridBeamGN: + self.createGrid() + + def hideSuit(self, suitIdarray): + for suitId in suitIdarray: + suit = base.cr.doId2do.get(suitId) + if suit: + suit.stash() + + def showSuit(self, suitIdarray): + for suitId in suitIdarray: + suit = base.cr.doId2do.get(suitId) + if suit: + suit.unstash() + suit.setVirtual() + + def initCollisionGeom(self): + print 'Laser Field initCollisionGeom' + self.blockerX = self.gridScaleX * 0.5 + self.blockerY = self.gridScaleY + self.cSphere = CollisionSphere(0, 0, 0, self.blockerX) + self.cSphereNode = CollisionNode('battleBlocker-%s-%s' % (self.level.getLevelId(), self.entId)) + self.cSphereNode.addSolid(self.cSphere) + self.cSphereNodePath = self.attachNewNode(self.cSphereNode) + self.cSphereNodePath.setPos(self.blockerX, self.blockerY, self.blockerZ) + self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + self.cSphere.setTangible(0) + self.enterEvent = 'enter' + self.cSphereNode.getName() + self.accept(self.enterEvent, self.__handleToonEnter) + + def __handleToonEnter(self, collEntry): + self.notify.debug('__handleToonEnter, %s' % self.entId) + self.sendFail() diff --git a/toontown/coghq/DistributedLaserFieldAI.py b/toontown/coghq/DistributedLaserFieldAI.py new file mode 100755 index 00000000..17ec5888 --- /dev/null +++ b/toontown/coghq/DistributedLaserFieldAI.py @@ -0,0 +1,201 @@ +import random + +from direct.directnotify import DirectNotifyGlobal +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from direct.interval.IntervalGlobal import * +from direct.task import Task +from otp.ai.AIBase import * +from otp.level import BasicEntities +from otp.level import DistributedEntityAI +from toontown.coghq import BattleBlockerAI +from toontown.coghq import LaserGameAvoid +from toontown.coghq import LaserGameDrag +from toontown.coghq import LaserGameMineSweeper +from toontown.coghq import LaserGameRoll + + +class DistributedLaserFieldAI(BattleBlockerAI.BattleBlockerAI, NodePath, BasicEntities.NodePathAttribs): + def __init__(self, level, entId): + BattleBlockerAI.BattleBlockerAI.__init__(self, level, entId) + node = hidden.attachNewNode('DistributedLaserFieldAI') + NodePath.__init__(self, node) + if not hasattr(self, 'switchId'): + self.switchId = 0 + self.gridScale = 1 + self.game = LaserGameRoll.LaserGameRoll(self.trapDisable, self.trapFire, self.sendField, self.setGrid) + if not hasattr(self, 'gridGame'): + self.gridGame = 'Roll' + self.enabled = 1 + self.hasShownSuits = 0 + self.healReady = 1 + self.playedSound = 0 + self.canButton = 1 + self.title = 'MemTag: This is a laserField %s' % random.random() + + def setGridGame(self, gameName): + if gameName == 'Random': + gameName = random.choice(['MineSweeper', 'Roll', 'Avoid', 'Drag']) + self.gridGame = gameName + if hasattr(self, 'game'): + self.game.delete() + self.game = None + if gameName == 'Drag': + self.game = LaserGameDrag.LaserGameDrag(self.trapDisable, self.trapFire, self.sendField, self.setGrid) + elif gameName == 'MineSweeper': + self.game = LaserGameMineSweeper.LaserGameMineSweeper(self.trapDisable, self.trapFire, self.sendField, self.setGrid) + elif gameName == 'Roll': + self.game = LaserGameRoll.LaserGameRoll(self.trapDisable, self.trapFire, self.sendField, self.setGrid) + elif gameName == 'Avoid': + self.game = LaserGameAvoid.LaserGameAvoid(self.trapDisable, self.trapFire, self.sendField, self.setGrid) + else: + self.game = LaserGameMineSweeper.LaserGameMineSweeper(self.trapDisable, self.trapFire, self.sendField, self.setGrid) + self.game.startGrid() + self.sendField() + self.sendUpdate('setGridGame', [gameName]) + + def generate(self): + BattleBlockerAI.BattleBlockerAI.generate(self) + if self.switchId != 0: + self.accept(self.getOutputEventName(self.switchId), self.reactToSwitch) + self.detectName = 'laserField %s' % self.doId + taskMgr.doMethodLater(1.0, self._DistributedLaserFieldAI__detect, self.detectName) + self.setPos(self.pos) + self.setHpr(self.hpr) + self.setGridGame(self.gridGame) + + def registerBlocker(self): + BattleBlockerAI.BattleBlockerAI.registerBlocker(self) + taskMgr.doMethodLater(1, self.hideSuits, 'hide-suits') + + def delete(self): + taskMgr.remove(self.detectName) + self.ignoreAll() + self.game.delete() + self.game = None + BattleBlockerAI.BattleBlockerAI.delete(self) + + def destroy(self): + self.notify.info('destroy entity(laserField) %s' % self.entId) + BattleBlockerAI.BattleBlockerAI.destroy(self) + + def setGrid(self, gridNumX, gridNumY): + self.game.setGridSize(gridNumX, gridNumY) + + def getGrid(self): + return (self.game.gridNumX, self.game.gridNumY) + + def getField(self): + fieldData = [] + fieldData.append(self.game.gridNumX) + fieldData.append(self.game.gridNumY) + for column in xrange(0, self.game.gridNumX): + for row in xrange(0, self.game.gridNumY): + fieldData.append(self.game.gridData[column][row]) + return fieldData + + def sendField(self): + self.sendUpdate('setField', [self.getField()]) + + def _DistributedLaserFieldAI__detect(self, task): + isThereAnyToons = False + if hasattr(self, 'level'): + toonInRange = 0 + for avId in self.level.presentAvIds: + if avId in self.air.doId2do: + av = self.air.doId2do[avId] + isThereAnyToons = True + distance = self.getDistance(av) + if isThereAnyToons: + taskMgr.doMethodLater(1.0, self._DistributedLaserFieldAI__detect, self.detectName) + self._DistributedLaserFieldAI__run() + return Task.done + + def hit(self, hitX, hitY, oldX, oldY): + if self.enabled: + self.game.hit(hitX, hitY, oldX, oldY) + + def _DistributedLaserFieldAI__run(self): + pass + + def _DistributedLaserFieldAI__toonHit(self): + self.gridNumX = random.randint(1, 4) + self.gridNumY = random.randint(1, 4) + self.gridScale = random.randint(1, 4) + self.sendUpdate('setGrid', [self.gridNumX, self.gridNumY, self.gridScale]) + + def reactToSwitch(self, on): + if on and self.canButton: + self.trapDisable() + self.game.win() + + def trapFire(self): + self.game.lose() + self.showSuits() + stage = self.air.getDo(self.level.stageDoId) + stage.resetPuzzelReward() + self.healReady = 0 + self.canButton = 0 + self.sendUpdate('setActiveLF', [0]) + if not self.playedSound: + self.sendUpdate('setSuccess', [0]) + self.playedSound = 1 + + def setBattleFinished(self): + print 'battle Finished' + BattleBlockerAI.BattleBlockerAI.setBattleFinished(self) + messenger.send(self.getOutputEventName(), [1]) + self.switchFire() + + def switchFire(self): + print 'switchFire' + if self.switchId != 0: + switch = self.level.getEntity(self.switchId) + if switch: + switch.setIsOn(1) + + def trapDisable(self): + self.enabled = 0 + suits = self.level.planner.battleCellId2suits.get(self.cellId) + messenger.send(self.getOutputEventName(), [1]) + if self.hasShownSuits == 0: + for suit in suits: + suit.requestRemoval() + self.sendUpdate('setActiveLF', [0]) + stage = self.air.getDo(self.level.stageDoId) + reward = stage.getPuzzelReward() + if self.healReady: + for avId in self.level.presentAvIds: + av = self.air.doId2do.get(avId) + if av: + av.toonUp(reward) + continue + stage.increasePuzzelReward() + self.healReady = 0 + if not self.playedSound: + self.sendUpdate('setSuccess', [1]) + self.playedSound = 1 + self.switchFire() + + def hideSuits(self, taskName): + suits = self.level.planner.battleCellId2suits.get(self.cellId) + suitArray = [] + for suit in suits: + suitArray.append(suit.doId) + if suitArray: + self.sendUpdate('hideSuit', [suitArray]) + + def showSuits(self): + if self.hasShownSuits == 0: + suits = self.level.planner.battleCellId2suits.get(self.cellId) + suitArray = [] + for suit in suits: + suit.setVirtual() + suitArray.append(suit.doId) + if suitArray: + self.sendUpdate('showSuit', [suitArray]) + self.hasShownSuits = 1 + + def addSuit(self, suit): + print 'Adding Suit %s' % suit.doId + BattleBlockerAI.BattleBlockerAI.addSuit(self, suit) diff --git a/toontown/coghq/DistributedLawOffice.py b/toontown/coghq/DistributedLawOffice.py new file mode 100755 index 00000000..a1d1b871 --- /dev/null +++ b/toontown/coghq/DistributedLawOffice.py @@ -0,0 +1,67 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import random +from otp.level import DistributedLevel +from direct.directnotify import DirectNotifyGlobal +import LawOfficeBase +import FactoryEntityCreator +import FactorySpecs +from otp.level import LevelSpec +from otp.level import LevelConstants +from toontown.toonbase import TTLocalizer +from toontown.coghq import FactoryCameraViews +from direct.distributed.DistributedObject import DistributedObject + +class DistributedLawOffice(DistributedObject, LawOfficeBase.LawOfficeBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawOffice') + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + LawOfficeBase.LawOfficeBase.__init__(self) + self.suitIds = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.suitsInitialized = 0 + self.goonClipPlanes = {} + self.level = None + return + + def generate(self): + self.notify.debug('generate') + self.accept('lawOfficeFloorDone', self.handleFloorDone) + + def delete(self): + base.localAvatar.chatMgr.chatInputSpeedChat.removeFactoryMenu() + self.ignore('lawOfficeFloorDone') + + def setLawOfficeId(self, id): + LawOfficeBase.LawOfficeBase.setLawOfficeId(self, id) + + def levelAnnounceGenerate(self): + self.notify.debug('levelAnnounceGenerate') + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def handleFloorDone(self): + self.sendUpdate('readyForNextFloor') + + def disable(self): + self.notify.debug('disable') + base.localAvatar.setCameraCollisionsCanMove(0) + + def getTaskZoneId(self): + return self.lawOfficeId + + def startSignal(self): + base.camera.setScale(base.localAvatar.getScale()) + localAvatar.setCameraFov(settings['fov']) + base.camera.clearMat() diff --git a/toontown/coghq/DistributedLawOfficeAI.py b/toontown/coghq/DistributedLawOfficeAI.py new file mode 100755 index 00000000..1e54fb41 --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeAI.py @@ -0,0 +1,134 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import * +from direct.showbase import PythonUtil +from direct.task import Task +from otp.level import DistributedLevelAI +from otp.level import LevelSpec +from toontown.ai.ToonBarrier import * +from toontown.building import DistributedElevatorFloorAI +from toontown.coghq import DistributedBattleFactoryAI, DistributedLawOfficeElevatorIntAI, DistributedLawOfficeFloorAI, LawOfficeLayout +from toontown.suit import DistributedFactorySuitAI +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals +import FactoryEntityCreatorAI, FactorySpecs, LawOfficeBase, LevelSuitPlannerAI + +class DistributedLawOfficeAI(DistributedObjectAI, LawOfficeBase.LawOfficeBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawOfficeAI') + + def __init__(self, air, lawOfficeId, zoneId, entranceId, avIds): + DistributedObjectAI.__init__(self, air) + LawOfficeBase.LawOfficeBase.__init__(self) + self.setLawOfficeId(lawOfficeId) + self.layout = None + self.currentFloor = 0 + self.elevator = None + self.elevatorB = None + self.activeElevator = 0 + self.level = None + self.avIds = avIds + self.entranceId = entranceId + + def createEntityCreator(self): + return FactoryEntityCreatorAI.FactoryEntityCreatorAI(level = self) + + def getBattleCreditMultiplier(self): + return ToontownBattleGlobals.getFactoryCreditMultiplier(self.lawOfficeId) + + def generate(self): + self.notify.info('generate') + self.notify.info('start factory %s %s creation, frame=%s' % (self.lawOfficeId, self.doId, globalClock.getFrameCount())) + DistributedObjectAI.generate(self) + self.layout = LawOfficeLayout.LawOfficeLayout(self.lawOfficeId) + self.exitEvents = { } + for avId in self.avIds: + self.exitEvents[avId] = simbase.air.getAvatarExitEvent(avId) + self.accept(self.exitEvents[avId], Functor(self.handleAvatarExit, avId)) + + self.startOffice() + + def handleAvatarExit(self, toonId): + pass + + def readyForNextFloor(self): + toonId = self.air.getAvatarIdFromSender() + + def generateWithRequired(self, zone): + DistributedObjectAI.generateWithRequired(self, zone) + + def startOffice(self): + self.notify.info('loading spec') + specModule = self.layout.getFloorSpec(self.currentFloor) + self.level = DistributedLawOfficeFloorAI.DistributedLawOfficeFloorAI(self.air, self.lawOfficeId, self.zoneId, self.entranceId, self.avIds, specModule) + self.level.setLevelSpec(LevelSpec.LevelSpec(specModule)) + self.notify.info('creating entities') + self.level.generateWithRequired(self.zoneId) + self.elevator = DistributedElevatorFloorAI.DistributedElevatorFloorAI(self.air, self.doId, self, self.avIds) + self.elevator.setEntering(0) + self.elevator.generateWithRequired(self.zoneId) + self.elevatorB = DistributedElevatorFloorAI.DistributedElevatorFloorAI(self.air, self.doId, self, self.avIds) + self.elevatorB.setEntering(1) + self.elevatorB.generateWithRequired(self.zoneId) + self.exchangeElevators() + + def delete(self): + self.notify.info('delete: %s' % self.doId) + if self.elevator: + del self.elevator + if self.level: + self.level.requestDelete() + self.level = None + self.ignoreAll() + + def exchangeElevators(self): + if self.activeElevator == 0: + self.elevator.lock() + self.elevatorB.unlock() + self.activeElevator = 1 + else: + self.elevator.unlock() + self.elevatorB.lock() + self.activeElevator = 0 + + def startNextFloor(self): + if self.avIds: + print self.avIds + self.currentFloor += 1 + specModule = self.layout.getFloorSpec(self.currentFloor) + self.level.requestDelete() + self.level = DistributedLawOfficeFloorAI.DistributedLawOfficeFloorAI(self.air, self.lawOfficeId, self.zoneId, self.entranceId, self.avIds, specModule) + self.level.setLevelSpec(LevelSpec.LevelSpec(specModule)) + self.level.generateWithRequired(self.zoneId) + print 'exchanging elevators' + self.exchangeElevators() + self.startSignal() + + def startSignal(self): + self.sendUpdate('startSignal') + + def dumpEveryone(self, optArg = None): + pass + + def getTaskZoneId(self): + return self.lawOfficeId + + def getLawOfficeId(self): + return self.lawOfficeId + + def getCogLevel(self): + return self.level.cogLevel + + def d_setSuits(self): + self.sendUpdate('setSuits', [ + self.getSuits(), + self.getReserveSuits()]) + + def getSuits(self): + suitIds = [] + for suit in self.suits: + suitIds.append(suit.doId) + return suitIds + + def getReserveSuits(self): + suitIds = [] + for suit in self.reserveSuits: + suitIds.append(suit[0].doId) + return suitIds diff --git a/toontown/coghq/DistributedLawOfficeElevatorExt.py b/toontown/coghq/DistributedLawOfficeElevatorExt.py new file mode 100755 index 00000000..bbace6fc --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeElevatorExt.py @@ -0,0 +1,107 @@ +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from toontown.building.ElevatorConstants import * +from toontown.building.ElevatorUtils import * +from toontown.building import DistributedElevatorExt +from toontown.building import DistributedElevator +from toontown.toonbase import ToontownGlobals +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.gui import DirectGui +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer + +class DistributedLawOfficeElevatorExt(DistributedElevatorExt.DistributedElevatorExt): + + def __init__(self, cr): + DistributedElevatorExt.DistributedElevatorExt.__init__(self, cr) + self.type = ELEVATOR_OFFICE + + def generate(self): + DistributedElevatorExt.DistributedElevatorExt.generate(self) + + def delete(self): + self.elevatorModel.removeNode() + del self.elevatorModel + DistributedElevatorExt.DistributedElevatorExt.delete(self) + + def setEntranceId(self, entranceId): + self.entranceId = entranceId + geom = self.cr.playGame.hood.loader.geom + locator = geom.find('**/elevator_origin_%s' % entranceId) + if locator: + self.elevatorModel.setPosHpr(locator, 0, 0, 0, 0, 0, 0) + else: + self.notify.error('No origin found for originId: %s' % entranceId) + entranceId2zoneId = {0: ToontownGlobals.LawbotStageIntA, + 1: ToontownGlobals.LawbotStageIntB, + 2: ToontownGlobals.LawbotStageIntC, + 3: ToontownGlobals.LawbotStageIntD} + self.intZoneId = entranceId2zoneId[entranceId] + locator = geom.find('**/elevator_signorigin_%s' % entranceId) + backgroundGeom = geom.find('**/ElevatorFrameFront_%d' % entranceId) + backgroundGeom.node().setEffect(DecalEffect.make()) + signText = DirectGui.OnscreenText(text=TextEncoder.upper(TTLocalizer.GlobalStreetNames[self.intZoneId][-1]), font=ToontownGlobals.getSuitFont(), scale=2, fg=(0.87, 0.87, 0.87, 1), mayChange=False, parent=backgroundGeom) + signText.setPosHpr(locator, 0, 0, 0, 0, 0, 0) + signText.setDepthWrite(0) + + def setupElevator(self): + self.elevatorModel = loader.loadModel('phase_10/models/cogHQ/mintElevator') + self.elevatorModel.reparentTo(render) + self.leftDoor = self.elevatorModel.find('**/left_door') + self.rightDoor = self.elevatorModel.find('**/right_door') + DistributedElevator.DistributedElevator.setupElevator(self) + self.elevatorSphereNodePath.setY(-1.42) + + def getElevatorModel(self): + return self.elevatorModel + + def setBldgDoId(self, bldgDoId): + self.bldg = None + self.setupElevator() + return + + def getZoneId(self): + return 0 + + def __doorsClosed(self, zoneId): + pass + + def setLawOfficeInteriorZone(self, zoneId): + if self.localToonOnBoard: + hoodId = self.cr.playGame.hood.hoodId + doneStatus = {'loader': 'cogHQLoader', + 'where': 'stageInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'hoodId': hoodId, + 'stageId': self.intZoneId} + self.cr.playGame.getPlace().elevator.signalDone(doneStatus) + + def setLawOfficeInteriorZoneForce(self, zoneId): + place = self.cr.playGame.getPlace() + if place: + place.fsm.request('elevator', [self]) + hoodId = self.cr.playGame.hood.hoodId + doneStatus = {'loader': 'cogHQLoader', + 'where': 'stageInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'hoodId': hoodId, + 'stageId': self.intZoneId} + if hasattr(place, 'elevator') and place.elevator: + place.elevator.signalDone(doneStatus) + else: + self.notify.warning("setMintInteriorZoneForce: Couldn't find playGame.getPlace().elevator, zoneId: %s" % zoneId) + else: + self.notify.warning("setLawOfficeInteriorZoneForce: Couldn't find playGame.getPlace(), zoneId: %s" % zoneId) + + def getDestName(self): + if self.intZoneId == ToontownGlobals.LawbotStageIntA: + return TTLocalizer.ElevatorLawBotCourse0 + elif self.intZoneId == ToontownGlobals.LawbotStageIntB: + return TTLocalizer.ElevatorLawBotCourse1 + elif self.intZoneId == ToontownGlobals.LawbotStageIntC: + return TTLocalizer.ElevatorLawBotCourse2 + elif self.intZoneId == ToontownGlobals.LawbotStageIntD: + return TTLocalizer.ElevatorLawBotCourse3 diff --git a/toontown/coghq/DistributedLawOfficeElevatorExtAI.py b/toontown/coghq/DistributedLawOfficeElevatorExtAI.py new file mode 100755 index 00000000..be0a1bde --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeElevatorExtAI.py @@ -0,0 +1,49 @@ +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task +from otp.ai.AIBase import * +from toontown.building import DistributedElevatorExtAI +from toontown.building.ElevatorConstants import * +from toontown.toonbase import ToontownGlobals + + +class DistributedLawOfficeElevatorExtAI(DistributedElevatorExtAI.DistributedElevatorExtAI): + def __init__(self, air, bldg, lawOfficeId, entranceId): + DistributedElevatorExtAI.DistributedElevatorExtAI.__init__(self, air, bldg) + self.lawOfficeId = lawOfficeId + self.entranceId = entranceId + + def getEntranceId(self): + return self.entranceId + + def elevatorClosed(self): + numPlayers = self.countFullSeats() + if numPlayers > 0: + players = [] + for i in self.seats: + if i not in [ + None, + 0]: + players.append(i) + continue + lawOfficeZone = self.bldg.createLawOffice(self.lawOfficeId, self.entranceId, players) + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.sendUpdateToAvatarId(avId, 'setLawOfficeInteriorZone', [lawOfficeZone]) + self.clearFullNow(seatIndex) + else: + self.notify.warning('The elevator left, but was empty.') + self.fsm.request('closed') + + def enterClosed(self): + DistributedElevatorExtAI.DistributedElevatorExtAI.enterClosed(self) + self.fsm.request('opening') + + def sendAvatarsToDestination(self, avIdList): + if len(avIdList) > 0: + officeZone = self.bldg.createLawOffice(self.lawOfficeId, self.entranceId, avIdList) + for avId in avIdList: + if avId: + self.sendUpdateToAvatarId(avId, 'setLawOfficeInteriorZoneForce', [officeZone]) diff --git a/toontown/coghq/DistributedLawOfficeElevatorInt.py b/toontown/coghq/DistributedLawOfficeElevatorInt.py new file mode 100755 index 00000000..c38ebea7 --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeElevatorInt.py @@ -0,0 +1,87 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from toontown.building.ElevatorConstants import * +from toontown.building.ElevatorUtils import * +from toontown.building import DistributedElevatorFloor +from toontown.building import DistributedElevator +from toontown.toonbase import ToontownGlobals +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer + +class DistributedLawOfficeElevatorInt(DistributedElevatorFloor.DistributedElevatorFloor): + + def __init__(self, cr): + DistributedElevatorFloor.DistributedElevatorFloor.__init__(self, cr) + + def generate(self): + DistributedElevator.DistributedElevator.generate(self) + self.accept('LawOffice_Spec_Loaded', self.__placeElevator) + + def delete(self): + self.elevatorModel.removeNode() + del self.elevatorModel + DistributedElevatorFloor.DistributedElevatorFloor.delete(self) + self.ignore('LawOffice_Spec_Loaded') + + def setEntranceId(self, entranceId): + self.entranceId = entranceId + if self.entranceId == 0: + self.elevatorModel.setPosHpr(62.74, -85.31, 0.0, 2.0, 0.0, 0.0) + elif self.entranceId == 1: + self.elevatorModel.setPosHpr(-162.25, 26.43, 0.0, 269.0, 0.0, 0.0) + else: + self.notify.error('Invalid entranceId: %s' % entranceId) + + def setupElevator(self): + self.elevatorModel = loader.loadModel('phase_4/models/modules/elevator') + self.elevatorModel.reparentTo(render) + self.elevatorModel.setScale(1.05) + self.leftDoor = self.elevatorModel.find('**/left-door') + self.rightDoor = self.elevatorModel.find('**/right-door') + self.elevatorModel.find('**/light_panel').removeNode() + self.elevatorModel.find('**/light_panel_frame').removeNode() + DistributedElevatorFloor.DistributedElevatorFloor.setupElevator(self) + + def __placeElevator(self): + self.notify.debug('PLACING ELEVATOR FOOL!!') + if self.isEntering: + elevatorNode = render.find('**/elevator_origin') + if not elevatorNode.isEmpty(): + self.elevatorModel.setPos(0, 0, 0) + self.elevatorModel.reparentTo(elevatorNode) + else: + self.notify.debug('NO NODE elevator_origin!!') + else: + elevatorNode = render.find('**/SlidingDoor') + if not elevatorNode.isEmpty(): + self.elevatorModel.setPos(0, 0, -15) + self.elevatorModel.reparentTo(elevatorNode) + else: + self.notify.debug('NO NODE SlidingDoor!!') + + def getElevatorModel(self): + return self.elevatorModel + + def setBldgDoId(self, bldgDoId): + self.bldg = None + self.setupElevator() + return + + def getZoneId(self): + return 0 + + def __doorsClosed(self, zoneId): + pass + + def setLawOfficeInteriorZone(self, zoneId): + if self.localToonOnBoard: + hoodId = self.cr.playGame.hood.hoodId + doneStatus = {'loader': 'cogHQLoader', + 'where': 'factoryInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'hoodId': hoodId} + self.cr.playGame.getPlace().elevator.signalDone(doneStatus) diff --git a/toontown/coghq/DistributedLawOfficeElevatorIntAI.py b/toontown/coghq/DistributedLawOfficeElevatorIntAI.py new file mode 100755 index 00000000..269539f8 --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeElevatorIntAI.py @@ -0,0 +1,45 @@ +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task +from otp.ai.AIBase import * +from toontown.building import DistributedElevatorFloorAI +from toontown.building.ElevatorConstants import * +from toontown.toonbase import ToontownGlobals + + +class DistributedLawOfficeElevatorIntAI(DistributedElevatorFloorAI.DistributedElevatorFloorAI): + def __init__(self, air, lawOfficeId, bldg, avIds): + DistributedElevatorFloorAI.DistributedElevatorFloorAI.__init__(self, air, bldg, avIds) + self.lawOfficeId = lawOfficeId + + def getEntranceId(self): + return self.entranceId + + def elevatorClosed(self): + numPlayers = self.countFullSeats() + if numPlayers > 0: + players = [] + for i in self.seats: + if i not in [None, 0]: + players.append(i) + sittingAvIds = [] + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + sittingAvIds.append(avId) + for avId in self.avIds: + if avId not in sittingAvIds: + print 'THIS AV ID %s IS NOT ON BOARD' % avId + self.bldg.startNextFloor() + else: + self.notify.warning('The elevator left, but was empty.') + self.fsm.request('closed') + + def enterClosed(self): + print 'DistributedLawOfficeElevatorIntAI.elevatorClosed %s' % self.doId + DistributedElevatorFloorAI.DistributedElevatorFloorAI.enterClosed(self) + if not (self.hasOpenedLocked) or not (self.isLocked): + self.fsm.request('opening') + if self.isLocked: + self.hasOpenedLocked = 1 diff --git a/toontown/coghq/DistributedLawOfficeFloor.py b/toontown/coghq/DistributedLawOfficeFloor.py new file mode 100755 index 00000000..298d1c5a --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeFloor.py @@ -0,0 +1,180 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import random +from otp.level import DistributedLevel +from direct.directnotify import DirectNotifyGlobal +import LawOfficeBase +import FactoryEntityCreator +import FactorySpecs +from otp.level import LevelSpec +from otp.level import LevelConstants +from toontown.toonbase import TTLocalizer +from toontown.coghq import FactoryCameraViews + +class DistributedLawOfficeFloor(DistributedLevel.DistributedLevel, LawOfficeBase.LawOfficeBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawOffice') + + def __init__(self, cr): + DistributedLevel.DistributedLevel.__init__(self, cr) + LawOfficeBase.LawOfficeBase.__init__(self) + self.suitIds = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.suitsInitialized = 0 + self.goonClipPlanes = {} + + def createEntityCreator(self): + return FactoryEntityCreator.FactoryEntityCreator(level=self) + + def generate(self): + self.notify.debug('generate') + DistributedLevel.DistributedLevel.generate(self) + self.factoryViews = FactoryCameraViews.FactoryCameraViews(self) + base.localAvatar.chatMgr.chatInputSpeedChat.addFactoryMenu() + self.accept('SOSPanelEnter', self.handleSOSPanel) + + def delete(self): + DistributedLevel.DistributedLevel.delete(self) + base.localAvatar.chatMgr.chatInputSpeedChat.removeFactoryMenu() + self.factoryViews.delete() + del self.factoryViews + self.ignore('SOSPanelEnter') + + def setLawOfficeId(self, id): + LawOfficeBase.LawOfficeBase.setLawOfficeId(self, id) + + def setForemanConfronted(self, avId): + if avId == base.localAvatar.doId: + return + av = base.cr.identifyFriend(avId) + if av is None: + return + base.localAvatar.setSystemMessage(avId, TTLocalizer.ForemanConfrontedMsg % av.getName()) + return + + def setDefeated(self): + self.notify.info('setDefeated') + messenger.send('FactoryWinEvent') + + def levelAnnounceGenerate(self): + self.notify.debug('levelAnnounceGenerate') + DistributedLevel.DistributedLevel.levelAnnounceGenerate(self) + specModule = FactorySpecs.getFactorySpecModule(self.lawOfficeId) + factorySpec = LevelSpec.LevelSpec(specModule) + DistributedLevel.DistributedLevel.initializeLevel(self, factorySpec) + + def privGotSpec(self, levelSpec): + firstSetZoneDoneEvent = self.cr.getNextSetZoneDoneEvent() + + def handleFirstSetZoneDone(): + base.factoryReady = 1 + messenger.send('FactoryReady') + + self.acceptOnce(firstSetZoneDoneEvent, handleFirstSetZoneDone) + modelCount = len(levelSpec.getAllEntIds()) + loader.beginBulkLoad('factory', TTLocalizer.HeadingToFactoryTitle % TTLocalizer.FactoryNames[self.lawOfficeId], modelCount, 1, TTLocalizer.TIP_COGHQ, LawbotOfficeInt) + DistributedLevel.DistributedLevel.privGotSpec(self, levelSpec) + loader.endBulkLoad('factory') + messenger.send('LawOffice_Spec_Loaded') + + def printPos(self = self): + pos = base.localAvatar.getPos(self.getZoneNode(self.lastToonZone)) + h = base.localAvatar.getH(self.getZoneNode(self.lastToonZone)) + print 'factory pos: %s, h: %s, zone %s' % (repr(pos), h, self.lastToonZone) + posStr = 'X: %.3f' % pos[0] + '\nY: %.3f' % pos[1] + '\nZ: %.3f' % pos[2] + '\nH: %.3f' % h + '\nZone: %s' % str(self.lastToonZone) + base.localAvatar.setChatAbsolute(posStr, CFThought) + + self.accept('f2', printPos) + base.localAvatar.setCameraCollisionsCanMove(1) + self.acceptOnce('leavingFactory', self.announceLeaving) + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def handleFloorDone(self): + self.sendUpdate('readyForNextFloor') + + def disable(self): + self.notify.debug('disable') + base.localAvatar.setCameraCollisionsCanMove(0) + if hasattr(self, 'suits'): + del self.suits + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + DistributedLevel.DistributedLevel.disable(self) + + def setSuits(self, suitIds, reserveSuitIds): + oldSuitIds = list(self.suitIds) + self.suitIds = suitIds + self.reserveSuitIds = reserveSuitIds + newSuitIds = [] + for suitId in self.suitIds: + if suitId not in oldSuitIds: + newSuitIds.append(suitId) + + if len(newSuitIds): + + def bringOutOfReserve(suits): + for suit in suits: + suit.comeOutOfReserve() + + self.relatedObjectMgrRequest = self.cr.relatedObjectMgr.requestObjects(newSuitIds, bringOutOfReserve) + + def reservesJoining(self): + pass + + def getCogSpec(self, cogId): + cogSpecModule = FactorySpecs.getCogSpecModule(self.lawOfficeId) + return cogSpecModule.CogData[cogId] + + def getReserveCogSpec(self, cogId): + cogSpecModule = FactorySpecs.getCogSpecModule(self.lawOfficeId) + return cogSpecModule.ReserveCogData[cogId] + + def getBattleCellSpec(self, battleCellId): + cogSpecModule = FactorySpecs.getCogSpecModule(self.lawOfficeId) + return cogSpecModule.BattleCells[battleCellId] + + def getFloorOuchLevel(self): + return 2 + + def getGoonPathId(self): + return 'sellbotFactory' + + def getTaskZoneId(self): + return self.lawOfficeId + + def getBossTaunt(self): + return TTLocalizer.FactoryBossTaunt + + def getBossBattleTaunt(self): + return TTLocalizer.FactoryBossBattleTaunt + + def placeLocalToon(self): + initialZoneEnt = None + if self.entranceId in self.entranceId2entity: + epEnt = self.entranceId2entity[self.entranceId] + initialZoneEnt = self.getEntity(epEnt.getZoneEntId()) + elif self.EmulateEntrancePoint: + self.notify.debug('unknown entranceId %s' % self.entranceId) + self.notify.debug('showing all zones') + self.setColorZones(1) + zoneEntIds = list(self.entType2ids['zone']) + zoneEntIds.remove(LevelConstants.UberZoneEntId) + if len(zoneEntIds): + zoneEntId = random.choice(zoneEntIds) + initialZoneEnt = self.getEntity(zoneEntId) + else: + initialZoneEnt = self.getEntity(LevelConstants.UberZoneEntId) + if initialZoneEnt is not None: + self.enterZone(initialZoneEnt.entId) + return diff --git a/toontown/coghq/DistributedLawOfficeFloorAI.py b/toontown/coghq/DistributedLawOfficeFloorAI.py new file mode 100755 index 00000000..2334bd11 --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeFloorAI.py @@ -0,0 +1,127 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObjectAI +from direct.task import Task +from otp.level import DistributedLevelAI, LevelSpec +from toontown.ai.ToonBarrier import * +from toontown.coghq import DistributedBattleFactoryAI, DistributedLawOfficeElevatorIntAI, LawOfficeLayout +from toontown.suit import DistributedFactorySuitAI +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals +import FactoryEntityCreatorAI, FactorySpecs, LawOfficeBase, LevelSuitPlannerAI + +class DistributedLawOfficeFloorAI(DistributedLevelAI.DistributedLevelAI, LawOfficeBase.LawOfficeBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawOfficeAI') + + def __init__(self, air, lawOfficeId, zoneId, entranceId, avIds, spec): + DistributedLevelAI.DistributedLevelAI.__init__(self, air, zoneId, entranceId, avIds) + LawOfficeBase.LawOfficeBase.__init__(self) + self.setLawOfficeId(lawOfficeId) + self.layout = None + self.elevator = None + self.level = None + self.spec = spec + + def createEntityCreator(self): + return FactoryEntityCreatorAI.FactoryEntityCreatorAI(level = self) + + def getBattleCreditMultiplier(self): + return ToontownBattleGlobals.getFactoryCreditMultiplier(self.lawOfficeId) + + def generate(self): + self.notify.info('generate') + self.notify.info('start factory %s %s creation, frame=%s' % (self.lawOfficeId, self.doId, globalClock.getFrameCount())) + self.layout = LawOfficeLayout.LawOfficeLayout(self.lawOfficeId) + self.startFloor() + + def startFloor(self): + self.notify.info('loading spec') + self.factorySpec = LevelSpec.LevelSpec(self.spec) + self.notify.info('creating entities') + DistributedLevelAI.DistributedLevelAI.generate(self, self.factorySpec) + self.notify.info('creating cogs') + cogSpecModule = FactorySpecs.getCogSpecModule(self.lawOfficeId) + self.planner = LevelSuitPlannerAI.LevelSuitPlannerAI(self.air, self, DistributedFactorySuitAI.DistributedFactorySuitAI, DistributedBattleFactoryAI.DistributedBattleFactoryAI, cogSpecModule.CogData, cogSpecModule.ReserveCogData, cogSpecModule.BattleCells) + suitHandles = self.planner.genSuits() + messenger.send('plannerCreated-' + str(self.doId)) + self.suits = suitHandles['activeSuits'] + self.reserveSuits = suitHandles['reserveSuits'] + self.d_setSuits() + scenario = 0 + description = '%s|%s|%s|%s' % (self.lawOfficeId, self.entranceId, scenario, self.avIdList) + for avId in self.avIdList: + self.air.writeServerEvent('DAOffice Entered', avId, description) + self.notify.info('finish factory %s %s creation' % (self.lawOfficeId, self.doId)) + + def delete(self): + self.notify.info('delete: %s' % self.doId) + suits = self.suits + for reserve in self.reserveSuits: + suits.append(reserve[0]) + self.planner.destroy() + del self.planner + for suit in suits: + if not suit.isDeleted(): + suit.factoryIsGoingDown() + suit.requestDelete() + DistributedLevelAI.DistributedLevelAI.delete(self, False) + + def readyForNextFloor(self): + toonId = self.air.getAvatarIdFromSender() + self._DistributedLawOfficeFloorAI__barrier.clear(toonId) + + def dumpEveryone(self): + pass + + def getTaskZoneId(self): + return self.lawOfficeId + + def getLawOfficeId(self): + return self.lawOfficeId + + def d_setForemanConfronted(self, avId): + if avId in self.avIdList: + self.sendUpdate('setForemanConfronted', [avId]) + else: + self.notify.warning('%s: d_setForemanConfronted: av %s not in av list %s' % (self.doId, avId, self.avIdList)) + + def setVictors(self, victorIds): + activeVictors = [] + activeVictorIds = [] + for victorId in victorIds: + toon = self.air.doId2do.get(victorId) + if toon is not None: + activeVictors.append(toon) + activeVictorIds.append(victorId) + scenario = 0 + description = '%s|%s|%s|%s' % (self.lawOfficeId, self.entranceId, scenario, activeVictorIds) + for avId in activeVictorIds: + self.air.writeServerEvent('DAOffice Defeated', avId, description) + for toon in activeVictors: + simbase.air.questManager.toonDefeatedFactory(toon, self.lawOfficeId) + + def b_setDefeated(self): + self.d_setDefeated() + self.setDefeated() + + def d_setDefeated(self): + self.sendUpdate('setDefeated') + + def setDefeated(self): + pass + + def getCogLevel(self): + return self.cogLevel + + def d_setSuits(self): + self.sendUpdate('setSuits', [self.getSuits(), self.getReserveSuits()]) + + def getSuits(self): + suitIds = [] + for suit in self.suits: + suitIds.append(suit.doId) + return suitIds + + def getReserveSuits(self): + suitIds = [] + for suit in self.reserveSuits: + suitIds.append(suit[0].doId) + return suitIds diff --git a/toontown/coghq/DistributedLawbotBossGavel.py b/toontown/coghq/DistributedLawbotBossGavel.py new file mode 100755 index 00000000..ea03f429 --- /dev/null +++ b/toontown/coghq/DistributedLawbotBossGavel.py @@ -0,0 +1,166 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.fsm import FSM +from direct.distributed import DistributedObject +from direct.showutil import Rope +from direct.showbase import PythonUtil +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from direct.actor import Actor + +class DistributedLawbotBossGavel(DistributedObject.DistributedObject, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotBossGavel') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedLawbotBossGavel') + self.boss = None + self.index = None + self.avId = 0 + self.modelPath = 'phase_11/models/lawbotHQ/LB_gavel' + self.modelFindString = None + self.nodePath = None + self.ival = None + self.origHpr = Point3(0, 0, 0) + self.downTime = 0.5 + self.upTime = 5 + self.gavelSfx = None + return + + def announceGenerate(self): + self.notify.debug('announceGenerate: %s' % self.doId) + DistributedObject.DistributedObject.announceGenerate(self) + self.name = 'gavel-%s' % self.doId + self.loadModel(self.modelPath, self.modelFindString) + self.nodePath.wrtReparentTo(render) + self.gavelSfx = loader.loadSfx('phase_11/audio/sfx/LB_gavel.ogg') + tempTuple = ToontownGlobals.LawbotBossGavelPosHprs[self.index] + self.nodePath.setPosHpr(*tempTuple) + self.origHpr = Point3(tempTuple[3], tempTuple[4], tempTuple[5]) + self.downTime = ToontownGlobals.LawbotBossGavelTimes[self.index][0] + self.upTime = ToontownGlobals.LawbotBossGavelTimes[self.index][1] + self.stayDownTime = ToontownGlobals.LawbotBossGavelTimes[self.index][2] + self.boss.gavels[self.index] = self + + def delete(self): + DistributedObject.DistributedObject.delete(self) + loader.unloadModel(self.modelPath) + self.nodePath.removeNode() + + def loadModel(self, modelPath, modelFindString = None): + if self.nodePath == None: + self.makeNodePath() + else: + self.gavel.getChildren().detach() + model = loader.loadModel(modelPath) + if modelFindString != None: + modTel = model.find('**/' + modelFindString) + parts = model.findAllMatches('**/gavel*') + gavelTop = model.find('**/top*') + gavelHandle = model.find('**/handle*') + model.instanceTo(self.gavel) + self.attachColTube() + self.scale = 3.0 + self.nodePath.setScale(self.scale) + return + + def attachColTube(self): + gavelTop = self.nodePath.find('**/top*') + self.gavelTop = gavelTop + gavelHandle = self.nodePath.find('**/handle*') + collNode = CollisionNode(self.uniqueName('headSphere')) + topBounds = gavelTop.getBounds() + center = topBounds.getCenter() + radius = topBounds.getRadius() + tube1 = CollisionTube(0, -1, center.getZ(), 0, 1, center.getZ(), 1) + tube1.setTangible(0) + collNode.addSolid(tube1) + collNode.setTag('attackCode', str(ToontownGlobals.BossCogGavelStomp)) + collNode.setName('GavelZap') + self.collNodePath = self.nodePath.attachNewNode(collNode) + handleBounds = gavelHandle.getBounds() + handleCenter = handleBounds.getCenter() + handleRadius = handleBounds.getRadius() + tube2 = CollisionTube(0, 0, handleCenter.getZ() + handleRadius, 0, 0, handleCenter.getZ() - handleRadius, 0.25) + tube2.setTangible(0) + handleCollNode = CollisionNode(self.uniqueName('gavelHandle')) + handleCollNode.addSolid(tube2) + handleCollNode.setTag('attackCode', str(ToontownGlobals.BossCogGavelHandle)) + handleCollNode.setName('GavelHandleZap') + self.handleCollNodePath = self.nodePath.attachNewNode(handleCollNode) + + def makeNodePath(self): + self.nodePath = Actor.Actor() + self.gavel = self.nodePath.attachNewNode('myGavel') + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.nodePath.detachNode() + if self.ival: + self.ival.finish() + self.ival = None + self.ignoreAll() + del self.boss.gavels[self.index] + self.cleanup() + return + + def cleanup(self): + self.boss = None + return + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + + def setIndex(self, index): + self.index = index + + def setState(self, state): + avId = 0 + if state == 'C': + self.demand('Controlled', avId) + elif state == 'F': + self.demand('Free') + elif state == 'N': + self.demand('On') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def enterOn(self): + self.notify.debug('enterOn for gavel %d' % self.index) + myHeadings = ToontownGlobals.LawbotBossGavelHeadings[self.index] + seqName = 'LawbotBossGavel-%s' % self.doId + self.ival = Sequence(name=seqName) + downAngle = -80 + for index in xrange(len(myHeadings)): + nextIndex = index + 1 + if nextIndex == len(myHeadings): + nextIndex = 0 + goingDown = self.nodePath.hprInterval(self.downTime, Point3(myHeadings[index] + self.origHpr[0], downAngle, self.origHpr[2]), startHpr=Point3(myHeadings[index] + self.origHpr[0], 0, self.origHpr[2])) + self.ival.append(goingDown) + self.ival.append(SoundInterval(self.gavelSfx, node=self.gavelTop)) + self.ival.append(Wait(self.stayDownTime)) + goingUp = self.nodePath.hprInterval(self.upTime, Point3(myHeadings[nextIndex] + self.origHpr[0], 0, self.origHpr[2]), startHpr=Point3(myHeadings[index] + self.origHpr[0], downAngle, self.origHpr[2])) + self.ival.append(goingUp) + + self.ival.loop() + self.accept('enterGavelZap', self.__touchedGavel) + self.accept('enterGavelHandleZap', self.__touchedGavelHandle) + + def enterOff(self): + if self.ival: + self.ival.finish() + tempTuple = ToontownGlobals.LawbotBossGavelPosHprs[self.index] + self.nodePath.setPosHpr(*tempTuple) + + def __touchedGavel(self, entry): + self.notify.debug('__touchedGavel') + self.notify.debug('self=%s entry=%s' % (self, entry)) + self.boss.touchedGavel(self, entry) + + def __touchedGavelHandle(self, entry): + self.notify.debug('__touchedGavelHandle') + self.boss.touchedGavelHandle(self, entry) diff --git a/toontown/coghq/DistributedLawbotBossGavelAI.py b/toontown/coghq/DistributedLawbotBossGavelAI.py new file mode 100755 index 00000000..f1f94f61 --- /dev/null +++ b/toontown/coghq/DistributedLawbotBossGavelAI.py @@ -0,0 +1,98 @@ +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from direct.fsm import FSM + +class DistributedLawbotBossGavelAI(DistributedObjectAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotBossGavelAI') + + def __init__(self, air, boss, index): + DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedLawbotBossGavelAI') + self.boss = boss + self.index = index + cn = CollisionNode('controls') + cs = CollisionSphere(0, -6, 0, 6) + cn.addSolid(cs) + self.goonShield = NodePath(cn) + self.goonShield.setPosHpr(*ToontownGlobals.LawbotBossGavelPosHprs[self.index]) + self.avId = 0 + self.objectId = 0 + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def setState(self, state): + self.request(state) + + def d_setState(self, state): + newState = state + if state == 'On': + newState = 'N' + elif state == 'Off': + newState = 'F' + self.sendUpdate('setState', [newState]) + + def b_setState(self, state): + self.request(state) + self.d_setState(state) + + def turnOn(self): + self.b_setState('On') + + def requestControl(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.boss.involvedToons and self.avId == 0: + craneId = self.__getCraneId(avId) + if craneId == 0: + self.request('Controlled', avId) + + def requestFree(self): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + self.request('Free') + + def removeToon(self, avId): + if avId == self.avId: + self.request('Free') + + def __getCraneId(self, avId): + if self.boss and self.boss.cranes != None: + for crane in self.boss.cranes: + if crane.avId == avId: + return crane.doId + + return 0 + + def enterOn(self): + pass + + def exitOn(slef): + pass + + def enterOff(self): + self.goonShield.detachNode() + + def exitOff(self): + pass + + def enterControlled(self, avId): + self.avId = avId + self.d_setState('C') + + def exitControlled(self): + if self.objectId: + obj = self.air.doId2do[self.objectId] + obj.request('Dropped', self.avId, self.doId) + + def enterFree(self): + self.avId = 0 + self.d_setState('F') + + def exitFree(self): + pass diff --git a/toontown/coghq/DistributedLawbotCannon.py b/toontown/coghq/DistributedLawbotCannon.py new file mode 100755 index 00000000..11d0e6d3 --- /dev/null +++ b/toontown/coghq/DistributedLawbotCannon.py @@ -0,0 +1,876 @@ +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import CollisionSphere, CollisionNode +from toontown.toonbase import ToontownGlobals +from toontown.estate import DistributedCannon +from toontown.estate import CannonGlobals +from otp.nametag import NametagGlobals +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toon import NPCToons +from toontown.toon import ToonHead +from toontown.toonbase import TTLocalizer +from toontown.minigame import Trajectory +from toontown.effects import DustCloud +GROUND_PLANE_MIN = -15 +CANNON_ROTATION_MIN = -55 +CANNON_ROTATION_MAX = 50 +CANNON_ROTATION_VEL = 15.0 +CANNON_ANGLE_MIN = 10 +CANNON_ANGLE_MAX = 85 +CANNON_ANGLE_VEL = 15.0 +INITIAL_VELOCITY = 80 +CANNON_MOVE_UPDATE_FREQ = 0.5 +CAMERA_PULLBACK_MIN = 20 +CAMERA_PULLBACK_MAX = 40 + +class DistributedLawbotCannon(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotCannon') + LOCAL_CANNON_MOVE_TASK = 'localCannonMoveTask' + FIRE_KEY = 'control' + UP_KEY = 'arrow_up' + DOWN_KEY = 'arrow_down' + LEFT_KEY = 'arrow_left' + RIGHT_KEY = 'arrow_right' + HIT_GROUND = 0 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.index = None + self.avId = 0 + self.av = None + self.localToonShooting = 0 + self.cannonsActive = 0 + self.cannonLocation = None + self.cannonPostion = None + self.cannon = None + self.madeGui = 0 + self.jurorToon = None + self.toonModel = None + self.toonHead = None + self.toonScale = None + self.dustCloud = None + self.hitBumper = 0 + self.hitTarget = 0 + self.lastPos = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.vel = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + self.hitTrack = None + self.flyColNode = None + self.flyColNodePath = None + self.localAvId = base.localAvatar.doId + self.model_Created = 0 + return + + def disable(self): + taskMgr.remove(self.uniqueName('fireCannon')) + taskMgr.remove(self.uniqueName('shootTask')) + self.__stopFlyTask(self.avId) + taskMgr.remove(self.uniqueName('flyTask')) + self.ignoreAll() + self.setMovie(CannonGlobals.CANNON_MOVIE_CLEAR, 0, 0) + self.nodePath.detachNode() + self.__unmakeGui() + if self.hitTrack: + self.hitTrack.finish() + del self.hitTrack + self.hitTrack = None + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + self.offstage() + self.unload() + DistributedObject.DistributedObject.delete(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.boss.cannons[self.index] = self + + def generateInit(self): + DistributedObject.DistributedObject.generateInit(self) + self.nodePath = NodePath(self.uniqueName('Cannon')) + self.load() + self.activateCannons() + + def setPosHpr(self, x, y, z, h, p, r): + self.nodePath.setPosHpr(x, y, z, h, p, r) + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + + def getSphereRadius(self): + return 1.5 + + def getParentNodePath(self): + return render + + def setIndex(self, index): + self.index = index + + def load(self): + self.cannon = loader.loadModel('phase_4/models/minigames/toon_cannon') + self.collSphere = CollisionSphere(0, 0, 0, self.getSphereRadius()) + self.dustCloud = DustCloud.DustCloud(render) + self.dustCloud.setBillboardPointEye() + self.collSphere.setTangible(1) + self.collNode = CollisionNode(self.uniqueName('CannonSphere')) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.nodePath.attachNewNode(self.collNode) + self.cannon.reparentTo(self.nodePath) + self.kartColNode = CollisionNode(self.uniqueName('KartColNode')) + self.kartNode = self.nodePath.attachNewNode(self.kartColNode) + self.sndCannonMove = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.ogg') + self.sndCannonFire = base.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.ogg') + self.sndHitGround = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg') + self.sndHitChair = base.loadSfx('phase_11/audio/sfx/LB_toon_jury.ogg') + self.cannon.hide() + self.flashingLabel = None + return + + def unload(self): + if self.cannon: + self.cannon.removeNode() + del self.cannon + if self.dustCloud != None: + self.dustCloud.destroy() + del self.dustCloud + del self.sndCannonMove + del self.sndCannonFire + del self.sndHitGround + del self.sndHitChair + if self.av: + self.__resetToon(self.av) + self.av.loop('neutral') + self.av.setPlayRate(1.0, 'run') + if self.toonHead != None: + self.toonHead.stopBlink() + self.toonHead.stopLookAroundNow() + self.toonHead.delete() + del self.toonHead + if self.toonModel != None: + self.toonModel.removeNode() + del self.toonModel + if self.jurorToon != None: + self.jurorToon.delete() + del self.jurorToon + del self.toonScale + return + + def activateCannons(self): + if not self.cannonsActive: + self.cannonsActive = 1 + self.onstage() + self.nodePath.reparentTo(self.getParentNodePath()) + self.accept(self.uniqueName('enterCannonSphere'), self.__handleEnterSphere) + + def onstage(self): + self.__createCannon() + self.cannon.reparentTo(self.nodePath) + self.dustCloud.reparentTo(render) + + def offstage(self): + if self.cannon: + self.cannon.reparentTo(hidden) + if self.dustCloud: + self.dustCloud.reparentTo(hidden) + self.dustCloud.stop() + + def __createCannon(self): + self.barrel = self.cannon.find('**/cannon') + self.cannonLocation = Point3(0, 0, 0.025) + self.cannonPosition = [0, CANNON_ANGLE_MIN] + self.cannon.setPos(self.cannonLocation) + self.__updateCannonPosition(self.avId) + + def updateCannonPosition(self, avId, zRot, angle): + if avId != self.localAvId: + self.cannonPosition = [zRot, angle] + self.__updateCannonPosition(avId) + + def __updateCannonPosition(self, avId): + self.cannon.setHpr(self.cannonPosition[0], 0.0, 0.0) + self.barrel.setHpr(0.0, self.cannonPosition[1], 0.0) + maxP = 90 + newP = self.barrel.getP() + yScale = 1 - 0.5 * float(newP) / maxP + shadow = self.cannon.find('**/square_drop_shadow') + shadow.setScale(1, yScale, 1) + + def __handleEnterSphere(self, collEntry): + self.d_requestEnter() + + def d_requestEnter(self): + self.sendUpdate('requestEnter', []) + + def setMovie(self, mode, avId, extraInfo): + wasLocalToon = self.localToonShooting + self.avId = avId + if mode == CannonGlobals.CANNON_MOVIE_CLEAR: + self.setLanded() + elif mode == CannonGlobals.CANNON_MOVIE_LANDED: + self.setLanded() + elif mode == CannonGlobals.CANNON_MOVIE_FORCE_EXIT: + self.exitCannon(self.avId) + self.setLanded() + elif mode == CannonGlobals.CANNON_MOVIE_LOAD: + if self.avId == base.localAvatar.doId: + self.cannonBallsLeft = extraInfo + base.cr.playGame.getPlace().setState('crane') + base.localAvatar.setTeleportAvailable(0) + self.localToonShooting = 1 + self.__makeGui() + camera.reparentTo(self.barrel) + camera.setPos(0.5, -2, 2.5) + camera.setHpr(0, 0, 0) + self.boss.toonEnteredCannon(self.avId, self.index) + if self.avId in self.cr.doId2do: + self.av = self.cr.doId2do[self.avId] + self.acceptOnce(self.av.uniqueName('disable'), self.__avatarGone) + self.av.loop('neutral') + self.av.stopSmooth() + self.__destroyToonModels() + self.__createToonModels() + self.av.setPosHpr(3, 0, 0, 90, 0, 0) + self.av.reparentTo(self.cannon) + else: + self.notify.warning('Unknown avatar %d in cannon %d' % (self.avId, self.doId)) + else: + self.notify.warning('unhandled case, mode = %d' % mode) + + def __avatarGone(self): + self.setMovie(CannonGlobals.CANNON_MOVIE_CLEAR, 0, 0) + + def __makeGui(self): + if self.madeGui: + return + NametagGlobals.setMasterArrowsOn(0) + guiModel = 'phase_4/models/gui/cannon_game_gui' + cannonGui = loader.loadModel(guiModel) + self.aimPad = DirectFrame(image=cannonGui.find('**/CannonFire_PAD'), relief=None, pos=(0.7, 0, -0.553333), scale=0.8) + cannonGui.removeNode() + self.fireButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Fire_Btn_UP'), (guiModel, '**/Fire_Btn_DN'), (guiModel, '**/Fire_Btn_RLVR')), relief=None, pos=(0.0115741, 0, 0.00505051), scale=1.0, command=self.__firePressed) + self.upButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0115741, 0, 0.221717)) + self.downButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0136112, 0, -0.210101), image_hpr=(0, 0, 180)) + self.leftButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(-0.199352, 0, -0.000505269), image_hpr=(0, 0, -90)) + self.rightButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.219167, 0, -0.00101024), image_hpr=(0, 0, 90)) + guiClose = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + cannonBallText = '%d/%d' % (self.cannonBallsLeft, ToontownGlobals.LawbotBossCannonBallMax) + self.cannonBallLabel = DirectLabel(parent=self.aimPad, text=cannonBallText, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0.475, 0.0, -0.35), scale=0.25) + if self.cannonBallsLeft < 5: + if self.flashingLabel: + self.flashingLabel.stop() + flashingTrack = Sequence() + for i in xrange(10): + flashingTrack.append(LerpColorScaleInterval(self.cannonBallLabel, 0.5, VBase4(1, 0, 0, 1))) + flashingTrack.append(LerpColorScaleInterval(self.cannonBallLabel, 0.5, VBase4(1, 1, 1, 1))) + + self.flashingLabel = flashingTrack + self.flashingLabel.start() + self.aimPad.setColor(1, 1, 1, 0.9) + + def bindButton(button, upHandler, downHandler): + button.bind(DGG.B1PRESS, lambda x, handler = upHandler: handler()) + button.bind(DGG.B1RELEASE, lambda x, handler = downHandler: handler()) + + bindButton(self.upButton, self.__upPressed, self.__upReleased) + bindButton(self.downButton, self.__downPressed, self.__downReleased) + bindButton(self.leftButton, self.__leftPressed, self.__leftReleased) + bindButton(self.rightButton, self.__rightPressed, self.__rightReleased) + self.__enableAimInterface() + self.madeGui = 1 + return + + def __unmakeGui(self): + self.notify.debug('__unmakeGui') + if not self.madeGui: + return + if self.flashingLabel: + self.flashingLabel.finish() + self.flashingLabel = None + NametagGlobals.setMasterArrowsOn(1) + self.__disableAimInterface() + self.upButton.unbind(DGG.B1PRESS) + self.upButton.unbind(DGG.B1RELEASE) + self.downButton.unbind(DGG.B1PRESS) + self.downButton.unbind(DGG.B1RELEASE) + self.leftButton.unbind(DGG.B1PRESS) + self.leftButton.unbind(DGG.B1RELEASE) + self.rightButton.unbind(DGG.B1PRESS) + self.rightButton.unbind(DGG.B1RELEASE) + self.aimPad.destroy() + del self.aimPad + del self.fireButton + del self.upButton + del self.downButton + del self.leftButton + del self.rightButton + self.madeGui = 0 + return + + def __enableAimInterface(self): + self.aimPad.show() + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + self.accept(self.UP_KEY, self.__upKeyPressed) + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__spawnLocalCannonMoveTask() + + def __disableAimInterface(self): + self.aimPad.hide() + self.ignore(self.FIRE_KEY) + self.ignore(self.UP_KEY) + self.ignore(self.DOWN_KEY) + self.ignore(self.LEFT_KEY) + self.ignore(self.RIGHT_KEY) + self.ignore(self.FIRE_KEY + '-up') + self.ignore(self.UP_KEY + '-up') + self.ignore(self.DOWN_KEY + '-up') + self.ignore(self.LEFT_KEY + '-up') + self.ignore(self.RIGHT_KEY + '-up') + self.__killLocalCannonMoveTask() + + def __fireKeyPressed(self): + self.ignore(self.FIRE_KEY) + self.accept(self.FIRE_KEY + '-up', self.__fireKeyReleased) + self.__firePressed() + + def __upKeyPressed(self): + self.ignore(self.UP_KEY) + self.accept(self.UP_KEY + '-up', self.__upKeyReleased) + self.__upPressed() + + def __downKeyPressed(self): + self.ignore(self.DOWN_KEY) + self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased) + self.__downPressed() + + def __leftKeyPressed(self): + self.ignore(self.LEFT_KEY) + self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased) + self.__leftPressed() + + def __rightKeyPressed(self): + self.ignore(self.RIGHT_KEY) + self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased) + self.__rightPressed() + + def __fireKeyReleased(self): + self.ignore(self.FIRE_KEY + '-up') + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + + def __leftKeyReleased(self): + self.ignore(self.LEFT_KEY + '-up') + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.__leftReleased() + + def __rightKeyReleased(self): + self.ignore(self.RIGHT_KEY + '-up') + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__rightReleased() + + def __upKeyReleased(self): + self.ignore(self.UP_KEY + '-up') + self.accept(self.UP_KEY, self.__upKeyPressed) + self.__upReleased() + + def __downKeyReleased(self): + self.ignore(self.DOWN_KEY + '-up') + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.__downReleased() + + def __leaveCannon(self): + self.notify.debug('__leaveCannon') + self.sendUpdate('requestLeave') + + def __firePressed(self): + self.notify.debug('fire pressed') + if not self.boss.state == 'BattleTwo': + self.notify.debug('boss is in state=%s, not firing' % self.boss.state) + return + self.__broadcastLocalCannonPosition() + self.__unmakeGui() + self.sendUpdate('setCannonLit', [self.cannonPosition[0], self.cannonPosition[1]]) + + def __upPressed(self): + self.notify.debug('up pressed') + self.upPressed = self.__enterControlActive(self.upPressed) + + def __downPressed(self): + self.notify.debug('down pressed') + self.downPressed = self.__enterControlActive(self.downPressed) + + def __leftPressed(self): + self.notify.debug('left pressed') + self.leftPressed = self.__enterControlActive(self.leftPressed) + + def __rightPressed(self): + self.notify.debug('right pressed') + self.rightPressed = self.__enterControlActive(self.rightPressed) + + def __upReleased(self): + self.notify.debug('up released') + self.upPressed = self.__exitControlActive(self.upPressed) + + def __downReleased(self): + self.notify.debug('down released') + self.downPressed = self.__exitControlActive(self.downPressed) + + def __leftReleased(self): + self.notify.debug('left released') + self.leftPressed = self.__exitControlActive(self.leftPressed) + + def __rightReleased(self): + self.notify.debug('right released') + self.rightPressed = self.__exitControlActive(self.rightPressed) + + def __enterControlActive(self, control): + return control + 1 + + def __exitControlActive(self, control): + return max(0, control - 1) + + def __spawnLocalCannonMoveTask(self): + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.cannonMoving = 0 + task = Task(self.__localCannonMoveTask) + task.lastPositionBroadcastTime = 0.0 + taskMgr.add(task, self.LOCAL_CANNON_MOVE_TASK) + + def __killLocalCannonMoveTask(self): + taskMgr.remove(self.LOCAL_CANNON_MOVE_TASK) + if self.cannonMoving: + self.sndCannonMove.stop() + + def __localCannonMoveTask(self, task): + pos = self.cannonPosition + oldRot = pos[0] + oldAng = pos[1] + rotVel = 0 + if self.leftPressed: + rotVel += CANNON_ROTATION_VEL + if self.rightPressed: + rotVel -= CANNON_ROTATION_VEL + pos[0] += rotVel * globalClock.getDt() + if pos[0] < CANNON_ROTATION_MIN: + pos[0] = CANNON_ROTATION_MIN + elif pos[0] > CANNON_ROTATION_MAX: + pos[0] = CANNON_ROTATION_MAX + angVel = 0 + if self.upPressed: + angVel += CANNON_ANGLE_VEL + if self.downPressed: + angVel -= CANNON_ANGLE_VEL + pos[1] += angVel * globalClock.getDt() + if pos[1] < CANNON_ANGLE_MIN: + pos[1] = CANNON_ANGLE_MIN + elif pos[1] > CANNON_ANGLE_MAX: + pos[1] = CANNON_ANGLE_MAX + if oldRot != pos[0] or oldAng != pos[1]: + if self.cannonMoving == 0: + self.cannonMoving = 1 + base.playSfx(self.sndCannonMove, looping=1) + self.__updateCannonPosition(self.localAvId) + if task.time - task.lastPositionBroadcastTime > CANNON_MOVE_UPDATE_FREQ: + task.lastPositionBroadcastTime = task.time + self.__broadcastLocalCannonPosition() + elif self.cannonMoving: + self.cannonMoving = 0 + self.sndCannonMove.stop() + self.__broadcastLocalCannonPosition() + return Task.cont + + def __broadcastLocalCannonPosition(self): + self.sendUpdate('setCannonPosition', [self.cannonPosition[0], self.cannonPosition[1]]) + + def __updateCannonPosition(self, avId): + self.cannon.setHpr(self.cannonPosition[0], 0.0, 0.0) + self.barrel.setHpr(0.0, self.cannonPosition[1], 0.0) + maxP = 90 + newP = self.barrel.getP() + yScale = 1 - 0.5 * float(newP) / maxP + shadow = self.cannon.find('**/square_drop_shadow') + shadow.setScale(1, yScale, 1) + + def __createToonModels(self): + self.model_Created = 1 + self.jurorToon = NPCToons.createLocalNPC(ToontownGlobals.LawbotBossBaseJurorNpcId + self.index) + self.toonScale = self.jurorToon.getScale() + jurorToonParent = render.attachNewNode('toonOriginChange') + self.jurorToon.wrtReparentTo(jurorToonParent) + self.jurorToon.setPosHpr(0, 0, -(self.jurorToon.getHeight() / 2.0), 0, -90, 0) + self.toonModel = jurorToonParent + self.toonHead = ToonHead.ToonHead() + self.toonHead.setupHead(self.jurorToon.style) + self.toonHead.reparentTo(hidden) + self.__loadToonInCannon() + + def __destroyToonModels(self): + if (0): + self.av.dropShadow.show() + if self.dropShadow != None: + self.dropShadow.removeNode() + self.dropShadow = None + self.hitBumper = 0 + self.hitTarget = 0 + self.angularVel = 0 + self.vel = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.lastPos = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + self.av = None + self.lastWakeTime = 0 + self.localToonShooting = 0 + if self.toonHead != None: + self.toonHead.reparentTo(hidden) + self.toonHead.stopBlink() + self.toonHead.stopLookAroundNow() + self.toonHead = None + if self.toonModel != None: + self.toonModel.removeNode() + self.toonModel = None + if self.jurorToon != None: + self.jurorToon.delete() + self.jurorToon = None + self.model_Created = 0 + return + + def __loadToonInCannon(self): + self.toonModel.reparentTo(hidden) + self.toonHead.startBlink() + self.toonHead.startLookAround() + self.toonHead.reparentTo(self.barrel) + self.toonHead.setPosHpr(0, 6, 0, 0, -45, 0) + sc = self.toonScale + self.toonHead.setScale(render, sc[0], sc[1], sc[2]) + + def exitCannon(self, avId): + self.__unmakeGui() + if self.avId == avId: + self.av.reparentTo(render) + self.__resetToonToCannon(self.av) + + def __resetToonToCannon(self, avatar): + pos = None + if not avatar: + if self.avId: + avatar = base.cr.doId2do.get(self.avId, None) + if avatar: + if hasattr(self, 'cannon') and self.cannon: + avatar.reparentTo(self.cannon) + avatar.setPosHpr(3, 0, 0, 90, 0, 0) + avatar.wrtReparentTo(render) + self.__resetToon(avatar) + return + + def __resetToon(self, avatar, pos = None): + if avatar: + self.__stopCollisionHandler(avatar) + self.__setToonUpright(avatar, pos) + if self.localToonShooting: + self.notify.debug('toon setting position to %s' % pos) + if pos: + base.localAvatar.setPos(pos) + camera.reparentTo(avatar) + camera.setPos(self.av.cameraPositions[0][0]) + place = base.cr.playGame.getPlace() + if place: + place.setState('finalBattle') + self.b_setLanded() + + def __stopCollisionHandler(self, avatar): + if avatar: + avatar.loop('neutral') + if self.flyColNode: + self.flyColNode = None + if avatar == base.localAvatar: + avatar.collisionsOn() + self.flyColSphere = None + if self.flyColNodePath: + base.cTrav.removeCollider(self.flyColNodePath) + self.flyColNodePath.removeNode() + self.flyColNodePath = None + self.handler = None + return + + def __setToonUpright(self, avatar, pos = None): + if avatar: + if not pos: + pos = avatar.getPos(render) + avatar.setPos(render, pos) + avatar.loop('neutral') + + def b_setLanded(self): + self.d_setLanded() + + def d_setLanded(self): + if self.localToonShooting: + self.sendUpdate('setLanded', []) + + def setLanded(self): + self.removeAvFromCannon() + + def removeAvFromCannon(self): + if self.av != None: + self.__stopCollisionHandler(self.av) + self.av.resetLOD() + place = base.cr.playGame.getPlace() + if self.av == base.localAvatar: + if place: + place.setState('finalBattle') + self.av.loop('neutral') + self.av.setPlayRate(1.0, 'run') + if self.av.getParent().getName() == 'toonOriginChange': + self.av.wrtReparentTo(render) + self.__setToonUpright(self.av) + if self.av == base.localAvatar: + self.av.startPosHprBroadcast() + self.av.startSmooth() + self.av.setScale(1, 1, 1) + self.ignore(self.av.uniqueName('disable')) + self.__destroyToonModels() + return + + def setCannonWillFire(self, avId, fireTime, zRot, angle, timestamp): + self.notify.debug('setCannonWillFire: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle) + ', time=' + str(fireTime)) + if not self.model_Created: + self.notify.warning("We walked into the zone mid-flight, so we won't see it") + return + self.cannonPosition[0] = zRot + self.cannonPosition[1] = angle + self.__updateCannonPosition(avId) + task = Task(self.__fireCannonTask) + task.avId = avId + ts = globalClockDelta.localElapsedTime(timestamp) + task.fireTime = fireTime - ts + if task.fireTime < 0.0: + task.fireTime = 0.0 + taskMgr.add(task, self.taskName('fireCannon')) + + def __fireCannonTask(self, task): + launchTime = task.fireTime + avId = task.avId + if self.toonHead == None or not self.boss.state == 'BattleTwo': + return Task.done + startPos, startHpr, startVel, trajectory, timeOfImpact, hitWhat = self.__calcFlightResults(avId, launchTime) + + self.notify.debug('start position: ' + str(startPos)) + self.notify.debug('start velocity: ' + str(startVel)) + self.notify.debug('time of launch: ' + str(launchTime)) + self.notify.debug('time of impact: ' + str(timeOfImpact)) + self.notify.debug('location of impact: ' + str(trajectory.getPos(timeOfImpact))) + head = self.toonHead + head.stopBlink() + head.stopLookAroundNow() + head.reparentTo(hidden) + juror = self.toonModel + juror.reparentTo(render) + juror.setPos(startPos) + barrelHpr = self.barrel.getHpr(render) + juror.setHpr(startHpr) + self.jurorToon.loop('swim') + self.jurorToon.setPosHpr(0, 0, -(self.jurorToon.getHeight() / 2.0), 0, 0, 0) + info = {} + info['avId'] = avId + info['trajectory'] = trajectory + info['launchTime'] = launchTime + info['timeOfImpact'] = timeOfImpact + info['hitWhat'] = hitWhat + info['toon'] = self.toonModel + info['hRot'] = self.cannonPosition[0] + info['haveWhistled'] = 0 + info['maxCamPullback'] = CAMERA_PULLBACK_MIN + if self.localToonShooting: + camera.reparentTo(juror) + camera.setP(45.0) + camera.setZ(-10.0) + self.flyColSphere = CollisionSphere(0, 0, self.av.getHeight() / 2.0, 1.0) + self.flyColNode = CollisionNode(self.uniqueName('flySphere')) + self.flyColNode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.FloorBitmask | ToontownGlobals.PieBitmask) + self.flyColNode.addSolid(self.flyColSphere) + self.flyColNodePath = self.jurorToon.attachNewNode(self.flyColNode) + self.flyColNodePath.setColor(1, 0, 0, 1) + self.handler = CollisionHandlerEvent() + self.handler.setInPattern(self.uniqueName('cannonHit')) + base.cTrav.addCollider(self.flyColNodePath, self.handler) + self.accept(self.uniqueName('cannonHit'), self.__handleCannonHit) + shootTask = Task(self.__shootTask, self.taskName('shootTask')) + flyTask = Task(self.__flyTask, self.taskName('flyTask')) + shootTask.info = info + flyTask.info = info + seqTask = Task.sequence(shootTask, flyTask) + taskMgr.add(seqTask, self.taskName('flyingToon') + '-' + str(avId)) + self.acceptOnce(self.uniqueName('stopFlyTask'), self.__stopFlyTask) + return Task.done + + def __toRadians(self, angle): + return angle * 2.0 * math.pi / 360.0 + + def __toDegrees(self, angle): + return angle * 360.0 / (2.0 * math.pi) + + def __calcFlightResults(self, avId, launchTime): + head = self.toonHead + startPos = head.getPos(render) + startHpr = head.getHpr(render) + hpr = self.barrel.getHpr(render) + rotation = self.__toRadians(hpr[0]) + angle = self.__toRadians(hpr[1]) + horizVel = INITIAL_VELOCITY * math.cos(angle) + xVel = horizVel * -math.sin(rotation) + yVel = horizVel * math.cos(rotation) + zVel = INITIAL_VELOCITY * math.sin(angle) + startVel = Vec3(xVel, yVel, zVel) + trajectory = Trajectory.Trajectory(launchTime, startPos, startVel) + self.trajectory = trajectory + timeOfImpact, hitWhat = self.__calcToonImpact(trajectory) + return startPos, startHpr, startVel, trajectory, 3 * timeOfImpact, hitWhat + + def __calcToonImpact(self, trajectory): + t_groundImpact = trajectory.checkCollisionWithGround(GROUND_PLANE_MIN) + if t_groundImpact >= trajectory.getStartTime(): + return (t_groundImpact, self.HIT_GROUND) + else: + self.notify.error('__calcToonImpact: toon never impacts ground?') + return (0.0, self.HIT_GROUND) + + def __handleCannonHit(self, collisionEntry): + if self.av == None or self.flyColNode == None: + return + interPt = collisionEntry.getSurfacePoint(render) + hitNode = collisionEntry.getIntoNode().getName() + fromNodePath = collisionEntry.getFromNodePath() + intoNodePath = collisionEntry.getIntoNodePath() + ignoredHits = ['NearBoss'] + for nodeName in ignoredHits: + if hitNode == nodeName: + return + + self.__stopFlyTask(self.avId) + self.__stopCollisionHandler(self.jurorToon) + if self.localToonShooting: + camera.wrtReparentTo(render) + pos = interPt + hpr = self.jurorToon.getHpr() + track = Sequence() + if self.localToonShooting: + pass + chairlist = ['trigger-chair'] + for index in xrange(len(ToontownGlobals.LawbotBossChairPosHprs)): + chairlist.append('Chair-%s' % index) + + if hitNode in chairlist: + track.append(Func(self.__hitChair, self.jurorToon, pos)) + track.append(Wait(1.0)) + track.append(Func(self.__setToonUpright, self.av)) + if self.av == base.localAvatar: + strs = hitNode.split('-') + chairNum = int(strs[1]) + self.boss.sendUpdate('hitChair', [chairNum, self.index]) + else: + track.append(Func(self.__hitGround, self.jurorToon, pos)) + track.append(Wait(1.0)) + track.append(Func(self.__setToonUpright, self.av)) + track.append(Func(self.b_setLanded)) + if self.localToonShooting: + pass + if self.hitTrack: + self.hitTrack.finish() + self.hitTrack = track + self.hitTrack.start() + return + + def enterCannonHit(self, collisionEntry): + pass + + def __shootTask(self, task): + base.playSfx(self.sndCannonFire) + return Task.done + + def __flyTask(self, task): + toon = task.info['toon'] + if toon.isEmpty(): + self.__resetToonToCannon(self.av) + return Task.done + curTime = task.time + task.info['launchTime'] + t = min(curTime, task.info['timeOfImpact']) + self.lastT = self.t + self.t = t + deltaT = self.t - self.lastT + self.deltaT = deltaT + if t >= task.info['timeOfImpact']: + self.__resetToonToCannon(self.av) + return Task.done + pos = task.info['trajectory'].getPos(t) + toon.setFluidPos(pos) + vel = task.info['trajectory'].getVel(t) + run = math.sqrt(vel[0] * vel[0] + vel[1] * vel[1]) + rise = vel[2] + theta = self.__toDegrees(math.atan(rise / run)) + toon.setHpr(self.cannon.getH(render), -90 + theta, 0) + view = 2 + lookAt = task.info['toon'].getPos(render) + hpr = task.info['toon'].getHpr(render) + if self.localToonShooting: + if view == 0: + camera.wrtReparentTo(render) + camera.lookAt(lookAt) + elif view == 1: + camera.reparentTo(render) + camera.setPos(render, 100, 100, 35.25) + camera.lookAt(render, lookAt) + elif view == 2: + if hpr[1] > -90: + camera.setPos(0, 0, -30) + if camera.getZ() < lookAt[2]: + camera.setZ(render, lookAt[2] + 10) + camera.lookAt(Point3(0, 0, 0)) + return Task.cont + + def __stopFlyTask(self, avId): + taskMgr.remove(self.taskName('flyingToon') + '-' + str(avId)) + + def __hitGround(self, avatar, pos, extraArgs = []): + hitP = avatar.getPos(render) + h = self.barrel.getH(render) + avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + avatar.setHpr(h, -135, 0) + self.dustCloud.setPos(render, pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + self.dustCloud.setScale(0.35) + self.dustCloud.play() + base.playSfx(self.sndHitGround) + avatar.hide() + + def __hitChair(self, avatar, pos, extraArgs = []): + hitP = avatar.getPos(render) + h = self.barrel.getH(render) + avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + avatar.setHpr(h, -135, 0) + self.dustCloud.setPos(render, pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + self.dustCloud.setScale(0.35) + self.dustCloud.play() + base.playSfx(self.sndHitGround) + base.playSfx(self.sndHitChair) + avatar.hide() + + def generateCannonAppearTrack(self, avatar): + self.cannon.setScale(0.1) + self.cannon.show() + kartTrack = Parallel(Sequence(ActorInterval(avatar, 'feedPet'), Func(avatar.loop, 'neutral')), Sequence(Func(self.cannon.reparentTo, avatar.rightHand), Wait(2.1), Func(self.cannon.wrtReparentTo, render), Func(self.cannon.setShear, 0, 0, 0), Parallel(LerpHprInterval(self.cannon, hpr=self.nodePath.getHpr(render), duration=1.2), ProjectileInterval(self.cannon, endPos=self.nodePath.getPos(render), duration=1.2, gravityMult=0.45)), Wait(0.2), Sequence(LerpScaleInterval(self.cannon, scale=Point3(1.1, 1.1, 0.1), duration=0.2), LerpScaleInterval(self.cannon, scale=Point3(0.9, 0.9, 0.1), duration=0.1), LerpScaleInterval(self.cannon, scale=Point3(1.0, 1.0, 0.1), duration=0.1), LerpScaleInterval(self.cannon, scale=Point3(1.0, 1.0, 1.1), duration=0.2), LerpScaleInterval(self.cannon, scale=Point3(1.0, 1.0, 0.9), duration=0.1), LerpScaleInterval(self.cannon, scale=Point3(1.0, 1.0, 1.0), duration=0.1), Func(self.cannon.wrtReparentTo, self.nodePath)))) + return kartTrack diff --git a/toontown/coghq/DistributedLawbotCannonAI.py b/toontown/coghq/DistributedLawbotCannonAI.py new file mode 100755 index 00000000..73d1bece --- /dev/null +++ b/toontown/coghq/DistributedLawbotCannonAI.py @@ -0,0 +1,108 @@ +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObjectAI +from toontown.minigame import Trajectory +from toontown.estate import DistributedCannonAI +from toontown.estate import CannonGlobals +from toontown.minigame import CannonGameGlobals + +class DistributedLawbotCannonAI(DistributedObjectAI.DistributedObjectAI): + notify = directNotify.newCategory('DistributedLawbotCannonAI') + + def __init__(self, air, lawbotBoss, index, x, y, z, h, p, r): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.index = index + self.posHpr = [x, + y, + z, + h, + p, + r] + self.boss = lawbotBoss + self.bossId = lawbotBoss.doId + self.avId = 0 + + def delete(self): + self.ignoreAll() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def getPosHpr(self): + return self.posHpr + + def canEnterCannon(self): + avId = self.air.getAvatarIdFromSender() + if self.boss.getCannonBallsLeft(avId) == 0: + return False + if not self.boss.state == 'BattleTwo': + return False + if not (self.avId == 0 or self.avId == avId): + return False + return True + + def requestEnter(self): + avId = self.air.getAvatarIdFromSender() + if not self.canEnterCannon(): + return + if self.avId == 0 or self.avId == avId: + self.avId = avId + self.boss.toonEnteredCannon(self.avId, self.index) + cannonBallsLeft = self.boss.getCannonBallsLeft(avId) + self.setMovie(CannonGlobals.CANNON_MOVIE_LOAD, self.avId, cannonBallsLeft) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + else: + self.air.writeServerEvent('suspicious', avId, 'DistributedCannonAI.requestEnter cannon already occupied') + self.notify.warning('requestEnter() - cannon already occupied') + + def setMovie(self, mode, avId, extraInfo): + self.avId = avId + self.sendUpdate('setMovie', [mode, avId, extraInfo]) + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.__doExit() + + def __doExit(self): + self.setMovie(CannonGlobals.CANNON_MOVIE_FORCE_EXIT, self.avId, 0) + self.avId = 0 + + def requestLeave(self): + avId = self.air.getAvatarIdFromSender() + if self.avId != 0: + self.__doExit() + else: + self.air.writeServerEvent('suspicious', avId, 'DistributedCannonAI.requestLeave cannon not occupied') + self.notify.warning('requestLeave() - cannon not occupied') + + def setCannonPosition(self, zRot, angle): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setCannonPosition: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle)) + self.sendUpdate('updateCannonPosition', [avId, zRot, angle]) + + def setLanded(self): + self.ignore(self.air.getAvatarExitEvent(self.avId)) + if self.canEnterCannon(): + self.requestEnter() + else: + self.setMovie(CannonGlobals.CANNON_MOVIE_LANDED, 0, 0) + + def setCannonLit(self, zRot, angle): + if not self.boss.state == 'BattleTwo': + self.notify.debug('ignoring setCannonList since boss in state %s' % self.boss.state) + return + avId = self.air.getAvatarIdFromSender() + if self.boss.getCannonBallsLeft(avId) == 0: + self.notify.debug('ignoring setCannonList since no balls left for %s' % avId) + return + self.notify.debug('setCannonLit: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle)) + fireTime = CannonGameGlobals.FUSE_TIME + self.sendUpdate('setCannonWillFire', [avId, + fireTime, + zRot, + angle, + globalClockDelta.getRealNetworkTime()]) + self.boss.decrementCannonBallsLeft(avId) diff --git a/toontown/coghq/DistributedLawbotChair.py b/toontown/coghq/DistributedLawbotChair.py new file mode 100755 index 00000000..4058dc22 --- /dev/null +++ b/toontown/coghq/DistributedLawbotChair.py @@ -0,0 +1,353 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.fsm import FSM +from direct.distributed import DistributedObject +from direct.showutil import Rope +from direct.showbase import PythonUtil +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from direct.actor import Actor +from toontown.suit import Suit +from toontown.suit import SuitDNA +import random +from toontown.battle import BattleProps +from toontown.toon import NPCToons + +class DistributedLawbotChair(DistributedObject.DistributedObject, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotChair') + chairCushionSurface = Point3(0, -0.75, 2.25) + landingPt = Point3(0, -1.5, 0) + courtroomCeiling = 30 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedLawbotBossChair') + self.boss = None + self.index = None + self.avId = 0 + self.modelPath = 'phase_11/models/lawbotHQ/JuryBoxChair' + self.modelFindString = None + self.nodePath = None + self.ival = None + self.origHpr = Point3(0, 0, 0) + self.downTime = 0.5 + self.upTime = 5 + self.cogJuror = None + self.propInSound = None + self.propOutSound = None + self.propTrack = None + self.cogJurorTrack = None + self.cogJurorSound = None + self.toonJurorIndex = -1 + self.toonJuror = None + return + + def announceGenerate(self): + self.notify.debug('announceGenerate: %s' % self.doId) + DistributedObject.DistributedObject.announceGenerate(self) + self.name = 'Chair-%s' % self.doId + self.loadModel(self.modelPath, self.modelFindString) + self.randomGenerator = random.Random() + self.randomGenerator.seed(self.doId) + self.loadSounds() + self.loadCogJuror() + self.cogJuror.stash() + origPos = self.computePos() + self.nodePath.setPos(origPos) + self.nodePath.setHpr(-90, 0, 0) + chairParent = self.boss.getChairParent() + self.nodePath.wrtReparentTo(chairParent) + self.boss.chairs[self.index] = self + + def delete(self): + DistributedObject.DistributedObject.delete(self) + loader.unloadModel(self.modelPath) + self.unloadSounds() + self.nodePath.removeNode() + + def loadModel(self, modelPath, modelFindString = None): + if self.nodePath == None: + self.makeNodePath() + else: + self.chair.getChildren().detach() + model = loader.loadModel(modelPath) + if modelFindString != None: + model = model.find('**/' + modelFindString) + model.instanceTo(self.chair) + trigger_chair = self.chair.find('**/trigger_chair') + if not trigger_chair.isEmpty(): + trigger_chair.stash() + collision_chair = self.chair.find('**/collision_chair') + if not collision_chair.isEmpty(): + collision_chair.stash() + shadow = self.chair.find('**/shadow') + if not shadow.isEmpty(): + pass + self.scale = 0.5 + self.chair.setScale(self.scale) + self.attachColSphere() + return + + def loadSounds(self): + if self.propInSound == None: + self.propInSound = base.loadSfx('phase_5/audio/sfx/ENC_propeller_in.ogg') + if self.propOutSound == None: + self.propOutSound = base.loadSfx('phase_5/audio/sfx/ENC_propeller_out.ogg') + if self.cogJurorSound == None: + self.cogJurorSound = base.loadSfx('phase_11/audio/sfx/LB_cog_jury.ogg') + return + + def unloadSounds(self): + if self.propInSound: + del self.propInSound + self.propInSound = None + if self.propOutSound: + del self.propOutSound + self.propOutSound = None + if self.cogJurorSound: + del self.cogJurorSound + self.cogJurorSound = None + return + + def loadCogJuror(self): + self.cleanupCogJuror() + self.cogJuror = Suit.Suit() + level = self.randomGenerator.randrange(len(SuitDNA.suitsPerLevel)) + self.cogJuror.dna = SuitDNA.SuitDNA() + self.cogJuror.dna.newSuitRandom(level=level, dept='l') + self.cogJuror.setDNA(self.cogJuror.dna) + self.cogJuror.pose('landing', 0) + self.cogJuror.reparentTo(self.nodePath) + self.cogJuror.prop = None + if self.cogJuror.prop == None: + self.cogJuror.prop = BattleProps.globalPropPool.getProp('propeller') + head = self.cogJuror.find('**/joint_head') + self.cogJuror.prop.reparentTo(head) + self.propTrack = Sequence(ActorInterval(self.cogJuror.prop, 'propeller', startFrame=8, endFrame=25)) + return + + def attachColSphere(self): + chairTop = self.nodePath.find('**/top*') + chairHandle = self.nodePath.find('**/handle*') + collNode = CollisionNode(self.uniqueName('headSphere')) + topBounds = self.chair.getBounds() + center = topBounds.getCenter() + radius = topBounds.getRadius() + radius *= 0.65 + adjustedZ = center[2] + adjustedZ += 0.6 + sphere1 = CollisionSphere(center[0], center[1], adjustedZ, radius) + sphere1.setTangible(1) + collNode.addSolid(sphere1) + collNode.setName('Chair-%s' % self.index) + self.collNodePath = self.nodePath.attachNewNode(collNode) + + def makeNodePath(self): + self.nodePath = Actor.Actor() + self.chair = self.nodePath.attachNewNode('myChair') + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.nodePath.detachNode() + if self.ival: + self.ival.finish() + self.ival = None + self.ignoreAll() + del self.boss.chairs[self.index] + self.cleanup() + if self.propTrack: + self.propTrack.finish() + self.propTrack = None + if self.cogJurorTrack: + self.cogJurorTrack.finish() + self.cogJurorTrack = None + self.cleanupCogJuror() + self.cleanupToonJuror() + return + + def stopCogsFlying(self): + if self.ival: + self.ival.finish() + self.ival = None + if self.propTrack: + self.propTrack.finish() + self.propTrack = None + if self.cogJurorTrack: + self.cogJurorTrack.finish() + self.cogJurorTrack = None + return + + def cleanupCogJuror(self): + if self.cogJuror: + self.cogJuror.detachNode() + self.cogJuror.delete() + del self.cogJuror + self.cogJuror = None + return + + def cleanupToonJuror(self): + if self.toonJuror: + self.toonJuror.detachNode() + self.toonJuror.delete() + del self.toonJuror + self.toonJuror = None + return + + def cleanup(self): + self.boss = None + return + + def startCogJuror(self, duration, y): + if self.cogJurorTrack: + self.cogJurorTrack.finish() + self.loadCogJuror() + self.cogJuror.stash() + x = 0 + curPos = self.nodePath.getPos(render) + z = self.courtroomCeiling - curPos[2] + self.notify.debug('curPos =%s\nz=%f' % (curPos, z)) + cogTrack = Sequence(Func(self.cogJuror.setPos, x, y, z), Func(self.cogJuror.unstash), Func(self.propTrack.loop), self.cogJuror.posInterval(duration, self.landingPt, Point3(x, y, z)), Func(self.propTrack.finish), Func(self.stashCogJuror)) + audioTrack = SoundInterval(self.propInSound, duration=duration, node=self.cogJuror, loop=1) + self.cogJurorTrack = Parallel(audioTrack, cogTrack) + self.cogJurorTrack.start() + + def stashCogJuror(self): + if self.cogJuror and not self.cogJuror.isEmpty(): + self.cogJuror.stash() + + def putCogJurorOnSeat(self): + self.stopCogsFlying() + if self.cogJuror and not self.cogJuror.isEmpty(): + base.playSfx(self.cogJurorSound, node=self.chair) + self.cogJuror.unstash() + self.cogJuror.prop.stash() + self.cogJuror.pose('landing', 47) + self.cogJuror.setH(180) + self.cogJuror.setPos(0, -1.25, 0.95) + if self.toonJuror: + self.toonJuror.hide() + else: + self.notify.warning('putCogJurorOnSeat invalid cogJuror') + + def putToonJurorOnSeat(self): + if self.toonJuror and not self.toonJuror.isEmpty(): + self.toonJuror.show() + self.toonJuror.reparentTo(self.nodePath) + self.toonJuror.setH(180) + self.toonJuror.setPos(0, -2.5, 0.95) + self.toonJuror.animFSM.request('Sit') + else: + self.notify.warning('putToonJurorOnSeat invalid toonJuror') + + def showCogJurorFlying(self): + self.notify.debug('showCogJurorFlying') + self.startCogJuror(ToontownGlobals.LawbotBossCogJurorFlightTime, -ToontownGlobals.LawbotBossCogJurorDistance) + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + + def setIndex(self, index): + self.index = index + + def setState(self, state): + avId = 0 + if state == 'C': + self.demand('Controlled', avId) + elif state == 'F': + self.demand('Free') + elif state == 'N': + self.demand('On') + elif state == 'T': + self.demand('ToonJuror') + elif state == 'S': + self.demand('SuitJuror') + elif state == 'E': + self.demand('EmptyJuror') + elif state == 'E': + self.demand('StopCogs') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def __touchedChair(self, entry): + self.notify.debug('__touchedChair') + self.notify.debug('self=%s entry=%s' % (self, entry)) + self.boss.touchedChair(self, entry) + + def __touchedChairHandle(self, entry): + self.notify.debug('__touchedChairHandle') + self.boss.touchedChairHandle(self, entry) + + def enterToonJuror(self): + self.chair.setColorScale(0.2, 0.2, 1.0, 1.0) + self.boss.countToonJurors() + if not self.cogJurorTrack: + self.cogJuror.stash() + self.putToonJurorOnSeat() + + def enterSuitJuror(self): + self.chair.setColorScale(0.5, 0.5, 0.5, 1.0) + self.boss.countToonJurors() + if self.toonJuror: + self.toonJuror.hide() + self.putCogJurorOnSeat() + + def enterEmptyJuror(self): + self.chair.setColorScale(1.0, 1.0, 1.0, 1.0) + + def enterStopCogs(self): + self.stopCogs() + + def exitStopCogs(self): + pass + + def enterOn(self): + self.notify.debug('enterOn for chair %d' % self.index) + myHeadings = ToontownGlobals.LawbotBossChairHeadings[self.index] + seqName = 'LawbotBossChair-%s' % self.doId + self.ival = Sequence(name=seqName) + downAngle = -80 + for index in xrange(len(myHeadings)): + nextIndex = index + 1 + if nextIndex == len(myHeadings): + nextIndex = 0 + goingDown = self.nodePath.hprInterval(self.downTime, Point3(myHeadings[index] + self.origHpr[0], downAngle, self.origHpr[2]), startHpr=Point3(myHeadings[index] + self.origHpr[0], 0, self.origHpr[2])) + self.ival.append(goingDown) + self.ival.append(Wait(self.stayDownTime)) + goingUp = self.nodePath.hprInterval(self.upTime, Point3(myHeadings[nextIndex] + self.origHpr[0], 0, self.origHpr[2]), startHpr=Point3(myHeadings[index] + self.origHpr[0], downAngle, self.origHpr[2])) + self.ival.append(goingUp) + + self.ival.loop() + self.accept('enterChairZap', self.__touchedChair) + self.accept('enterChairHandleZap', self.__touchedChairHandle) + + def computePos(self): + rowIndex = self.index % 6 + if self.index < 6: + startPt = Point3(*ToontownGlobals.LawbotBossChairRow1PosA) + endPt = Point3(*ToontownGlobals.LawbotBossChairRow1PosB) + else: + startPt = Point3(*ToontownGlobals.LawbotBossChairRow2PosA) + endPt = Point3(*ToontownGlobals.LawbotBossChairRow2PosB) + totalDisplacement = endPt - startPt + stepDisplacement = totalDisplacement / (6 - 1) + newPos = stepDisplacement * rowIndex + self.notify.debug('curDisplacement = %s' % newPos) + newPos += startPt + self.notify.debug('newPos before offset = %s' % newPos) + newPos -= Point3(*ToontownGlobals.LawbotBossJuryBoxRelativeEndPos) + self.notify.debug('newPos = %s' % newPos) + return newPos + + def loadToonJuror(self): + self.cleanupToonJuror() + self.toonJuror = NPCToons.createLocalNPC(ToontownGlobals.LawbotBossBaseJurorNpcId + self.toonJurorIndex) + self.toonJuror.hide() + + def setToonJurorIndex(self, newVal): + if not self.toonJurorIndex == newVal: + self.toonJurorIndex = newVal + self.loadToonJuror() diff --git a/toontown/coghq/DistributedLawbotChairAI.py b/toontown/coghq/DistributedLawbotChairAI.py new file mode 100755 index 00000000..de1a0ef5 --- /dev/null +++ b/toontown/coghq/DistributedLawbotChairAI.py @@ -0,0 +1,183 @@ +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * +from direct.distributed import DistributedObjectAI +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from direct.fsm import FSM +import random + +class DistributedLawbotChairAI(DistributedObjectAI.DistributedObjectAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotChairAI') + + def __init__(self, air, boss, index): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedLawbotBossChairAI') + self.boss = boss + self.index = index + cn = CollisionNode('controls') + cs = CollisionSphere(0, -6, 0, 6) + cn.addSolid(cs) + self.goonShield = NodePath(cn) + self.goonShield.setPosHpr(*ToontownGlobals.LawbotBossChairPosHprs[self.index]) + self.avId = 0 + self.objectId = 0 + self.changeToCogTask = None + self.startCogFlyTask = None + self.toonJurorIndex = -1 + return + + def delete(self): + self.ignoreAll() + DistributedObjectAI.DistributedObjectAI.delete(self) + taskName = self.uniqueName('startCogFlyTask') + taskMgr.remove(taskName) + changeTaskName = self.uniqueName('changeToCogJuror') + taskMgr.remove(changeTaskName) + + def stopCogs(self): + taskName = self.uniqueName('startCogFlyTask') + taskMgr.remove(taskName) + changeTaskName = self.uniqueName('changeToCogJuror') + taskMgr.remove(changeTaskName) + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def getToonJurorIndex(self): + return self.toonJurorIndex + + def setToonJurorIndex(self, newVal): + self.toonJurorIndex = newVal + + def b_setToonJurorIndex(self, newVal): + self.setToonJurorIndex(newVal) + self.d_setToonJurorIndex(newVal) + + def d_setToonJurorIndex(self, newVal): + self.sendUpdate('setToonJurorIndex', [newVal]) + + def setState(self, state): + self.request(state) + + def d_setState(self, state): + newState = state + if state == 'On': + newState = 'N' + elif state == 'Off': + newState = 'F' + elif state == 'ToonJuror': + newState = 'T' + elif state == 'SuitJuror': + newState = 'S' + elif state == 'EmptyJuror': + newState = 'E' + elif state == 'StopCogs': + newState = 'C' + self.sendUpdate('setState', [newState]) + + def b_setState(self, state): + self.request(state) + self.d_setState(state) + + def turnOn(self): + self.b_setState('On') + + def requestStopCogs(self): + self.b_setState('StopCogs') + + def requestControl(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.boss.involvedToons and self.avId == 0: + craneId = self.__getCraneId(avId) + if craneId == 0: + self.request('Controlled', avId) + + def requestFree(self): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + self.request('Free') + + def removeToon(self, avId): + if avId == self.avId: + self.request('Free') + + def __getCraneId(self, avId): + if self.boss and self.boss.cranes != None: + for crane in self.boss.cranes: + if crane.avId == avId: + return crane.doId + + return 0 + + def requestToonJuror(self): + self.b_setState('ToonJuror') + if self.changeToCogTask == None: + if self.startCogFlyTask == None: + delayTime = random.randrange(9, 19) + self.startCogFlyTask = taskMgr.doMethodLater(delayTime, self.cogFlyAndSit, self.uniqueName('startCogFlyTask')) + return + + def requestSuitJuror(self): + self.b_setState('SuitJuror') + + def requestEmptyJuror(self): + self.b_setState('EmptyJuror') + delayTime = random.randrange(1, 20) + self.startCogFlyTask = taskMgr.doMethodLater(delayTime, self.cogFlyAndSit, self.uniqueName('startCogFlyTask')) + + def cogFlyAndSit(self, taskName = None): + self.notify.debug('cogFlyAndSit') + self.sendUpdate('showCogJurorFlying', []) + self.changeToCogTask = taskMgr.doMethodLater(ToontownGlobals.LawbotBossCogJurorFlightTime, self.changeToCogJuror, self.uniqueName('changeToCogJuror')) + if self.startCogFlyTask: + self.startCogFlyTask = None + return + + def changeToCogJuror(self, task): + self.notify.debug('changeToCogJuror') + self.requestSuitJuror() + self.changeToCogTask = None + return + + def enterOn(self): + pass + + def exitOn(slef): + pass + + def enterOff(self): + self.goonShield.detachNode() + + def exitOff(self): + pass + + def enterControlled(self, avId): + self.avId = avId + self.d_setState('C') + + def exitControlled(self): + if self.objectId: + obj = self.air.doId2do[self.objectId] + obj.request('Dropped', self.avId, self.doId) + + def enterFree(self): + self.avId = 0 + self.d_setState('F') + + def exitFree(self): + pass + + def enterToonJuror(self): + pass + + def exitToonJuror(self): + pass + + def enterStopCogs(self): + self.__stopCogs() + + def exitStopCogs(self): + pass diff --git a/toontown/coghq/DistributedLevelBattle.py b/toontown/coghq/DistributedLevelBattle.py new file mode 100755 index 00000000..2d94a34e --- /dev/null +++ b/toontown/coghq/DistributedLevelBattle.py @@ -0,0 +1,242 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.interval.IntervalGlobal import * +from panda3d.core import * +import random + +from otp.avatar import Emote +from toontown.battle import DistributedBattle +from toontown.battle import SuitBattleGlobals +from toontown.battle.BattleBase import * +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals +from toontown.suit import SuitDNA +from toontown.toon import TTEmote +from toontown.toonbase import ToontownGlobals + + +class DistributedLevelBattle(DistributedBattle.DistributedBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevelBattle') + + def __init__(self, cr): + DistributedBattle.DistributedBattle.__init__(self, cr) + self.levelRequest = None + self.levelBattle = 1 + return + + def setLevelDoId(self, levelDoId): + self.levelDoId = levelDoId + + def setBattleCellId(self, battleCellId): + self.battleCellId = battleCellId + + def doPlacement(levelList, self = self): + self.levelRequest = None + self.level = levelList[0] + spec = self.level.getBattleCellSpec(self.battleCellId) + self.level.requestReparent(self, spec['parentEntId']) + self.setPos(spec['pos']) + print 'spec = %s' % spec + print 'h = %s' % spec.get('h') + self.wrtReparentTo(render) + return + + level = base.cr.doId2do.get(self.levelDoId) + if level is None: + self.notify.warning('level %s not in doId2do yet, battle %s will be mispositioned.' % self.levelDoId, self.doId) + self.levelRequest = self.cr.relatedObjectMgr.requestObjects([self.levelDoId], doPlacement) + else: + doPlacement([level]) + return + + def setPosition(self, *args): + pass + + def setInitialSuitPos(self, x, y, z): + self.initialSuitPos = Point3(x, y, z) + + def disable(self): + if self.hasLocalToon(): + self.unlockLevelViz() + if self.levelRequest is not None: + self.cr.relatedObjectMgr.abortRequest(self.levelRequest) + self.levelRequest = None + DistributedBattle.DistributedBattle.disable(self) + return + + def delete(self): + self.ignoreAll() + DistributedBattle.DistributedBattle.delete(self) + + def handleBattleBlockerCollision(self): + messenger.send(self.getCollisionName(), [None]) + return + + def lockLevelViz(self): + level = base.cr.doId2do.get(self.levelDoId) + if level: + level.lockVisibility(zoneId=self.zoneId) + else: + self.notify.warning("lockLevelViz: couldn't find level %s" % self.levelDoId) + + def unlockLevelViz(self): + level = base.cr.doId2do.get(self.levelDoId) + if level: + level.unlockVisibility() + else: + self.notify.warning("unlockLevelViz: couldn't find level %s" % self.levelDoId) + + def onWaitingForJoin(self): + self.lockLevelViz() + + def announceCrateReward(self): + track = Sequence() + + for i, message in enumerate(TTLocalizer.CrateRewardMessages): + track.append(Func(base.localAvatar.setSystemMessage, 0, message)) + track.append(Wait(1.5)) + + track.start() + + def __faceOff(self, ts, name, callback): + if len(self.suits) == 0: + self.notify.warning('__faceOff(): no suits.') + return + if len(self.toons) == 0: + self.notify.warning('__faceOff(): no toons.') + return + toon = self.toons[0] + point = self.toonPoints[0][0] + toonPos = point[0] + toonHpr = VBase3(point[1], 0.0, 0.0) + p = toon.getPos(self) + toon.setPos(self, p[0], p[1], 0.0) + toon.setShadowHeight(0) + if len(self.suits) == 1: + leaderIndex = 0 + elif self.bossBattle == 1: + for suit in self.suits: + if suit.boss: + leaderIndex = self.suits.index(suit) + break + + else: + maxTypeNum = -1 + for suit in self.suits: + suitTypeNum = SuitDNA.getSuitType(suit.dna.name) + if maxTypeNum < suitTypeNum: + maxTypeNum = suitTypeNum + leaderIndex = self.suits.index(suit) + + delay = FACEOFF_TAUNT_T + suitTrack = Parallel() + suitLeader = None + for suit in self.suits: + suit.setState('Battle') + suitIsLeader = 0 + oneSuitTrack = Sequence() + oneSuitTrack.append(Func(suit.loop, 'neutral')) + oneSuitTrack.append(Func(suit.headsUp, toonPos)) + if self.suits.index(suit) == leaderIndex: + suitLeader = suit + suitIsLeader = 1 + if self.bossBattle == 1 and self.levelDoId in base.cr.doId2do: + level = base.cr.doId2do[self.levelDoId] + if suit.boss: + taunt = level.getBossTaunt() + else: + taunt = level.getBossBattleTaunt() + else: + taunt = SuitBattleGlobals.getFaceoffTaunt(suit.getStyleName(), suit.doId) + oneSuitTrack.append(Func(suit.setChatAbsolute, taunt, CFSpeech | CFTimeout)) + destPos, destHpr = self.getActorPosHpr(suit, self.suits) + oneSuitTrack.append(Wait(delay)) + if suitIsLeader == 1: + oneSuitTrack.append(Func(suit.clearChat)) + oneSuitTrack.append(self.createAdjustInterval(suit, destPos, destHpr)) + suitTrack.append(oneSuitTrack) + + suitHeight = suitLeader.getHeight() + suitOffsetPnt = Point3(0, 0, suitHeight) + toonTrack = Parallel() + for toon in self.toons: + oneToonTrack = Sequence() + destPos, destHpr = self.getActorPosHpr(toon, self.toons) + oneToonTrack.append(Wait(delay)) + oneToonTrack.append(self.createAdjustInterval(toon, destPos, destHpr, toon=1, run=1)) + toonTrack.append(oneToonTrack) + + if self.hasLocalToon(): + MidTauntCamHeight = suitHeight * 0.66 + MidTauntCamHeightLim = suitHeight - 1.8 + if MidTauntCamHeight < MidTauntCamHeightLim: + MidTauntCamHeight = MidTauntCamHeightLim + TauntCamY = 18 + TauntCamX = 0 + TauntCamHeight = random.choice((MidTauntCamHeight, 1, 11)) + camTrack = Sequence() + camTrack.append(Func(camera.reparentTo, suitLeader)) + camTrack.append(Func(base.camLens.setMinFov, self.camFOFov/(4./3.))) + camTrack.append(Func(camera.setPos, TauntCamX, TauntCamY, TauntCamHeight)) + camTrack.append(Func(camera.lookAt, suitLeader, suitOffsetPnt)) + camTrack.append(Wait(delay)) + camTrack.append(Func(base.camLens.setMinFov, self.camFov/(4./3.))) + camTrack.append(Func(camera.wrtReparentTo, self)) + camTrack.append(Func(camera.setPos, self.camFOPos)) + camTrack.append(Func(camera.lookAt, suit)) + mtrack = Parallel(suitTrack, toonTrack) + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + mtrack = Parallel(mtrack, camTrack) + done = Func(callback) + track = Sequence(mtrack, done, name=name) + track.start(ts) + self.storeInterval(track, name) + return + + def enterFaceOff(self, ts): + if len(self.toons) > 0 and base.localAvatar == self.toons[0]: + Emote.globalEmote.disableAll(self.toons[0], 'dbattlebldg, enterFaceOff') + self.delayDeleteMembers() + self.__faceOff(ts, self.faceOffName, self.__handleFaceOffDone) + + def __handleFaceOffDone(self): + self.notify.debug('FaceOff done') + self.d_faceOffDone(base.localAvatar.doId) + + def exitFaceOff(self): + self.notify.debug('exitFaceOff()') + if len(self.toons) > 0 and base.localAvatar == self.toons[0]: + Emote.globalEmote.releaseAll(self.toons[0], 'dbattlebldg exitFaceOff') + self.clearInterval(self.faceOffName) + self._removeMembersKeep() + + def __playReward(self, ts, callback): + toonTracks = Parallel() + for toon in self.toons: + toonTracks.append(Sequence(Func(toon.loop, 'victory'), Wait(FLOOR_REWARD_TIMEOUT), Func(toon.loop, 'neutral'))) + + name = self.uniqueName('floorReward') + track = Sequence(toonTracks, Func(callback), name=name) + camera.setPos(0, 0, 1) + camera.setHpr(180, 10, 0) + self.storeInterval(track, name) + track.start(ts) + + def enterReward(self, ts): + self.notify.info('enterReward()') + self.disableCollision() + self.delayDeleteMembers() + self.__playReward(ts, self.__handleFloorRewardDone) + + def __handleFloorRewardDone(self): + pass + + def exitReward(self): + self.notify.info('exitReward()') + self.clearInterval(self.uniqueName('floorReward')) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) + for toon in self.toons: + toon.startSmooth() diff --git a/toontown/coghq/DistributedLevelBattleAI.py b/toontown/coghq/DistributedLevelBattleAI.py new file mode 100755 index 00000000..6eac9f16 --- /dev/null +++ b/toontown/coghq/DistributedLevelBattleAI.py @@ -0,0 +1,161 @@ +from toontown.battle import DistributedBattleAI +from toontown.battle import DistributedBattleBaseAI +from toontown.catalog import CatalogFurnitureItem +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import State +from direct.fsm import ClassicFSM +from toontown.battle.BattleBase import * +import CogDisguiseGlobals +from direct.showbase.PythonUtil import addListsByValue + +class DistributedLevelBattleAI(DistributedBattleAI.DistributedBattleAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevelBattleAI') + + def __init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, winState, roundCallback = None, finishCallback = None, maxSuits = 4): + self.blocker = None + self.level = level + self.battleCellId = battleCellId + self.winState = winState + self.roundCallback = roundCallback + self.suitTrack = suit.dna.dept + DistributedBattleAI.DistributedBattleAI.__init__(self, air, battleMgr, pos, suit, toonId, zoneId, finishCallback, maxSuits, tutorialFlag=0, levelFlag=1) + isBossBattle = 0 + for suit in self.battleMgr.level.planner.battleCellId2suits[battleCellId]: + if suit.boss: + isBossBattle = 1 + break + + self.setBossBattle(isBossBattle) + self.bossDefeated = 0 + return + + def generate(self): + DistributedBattleAI.DistributedBattleAI.generate(self) + battleBlocker = self.battleMgr.battleBlockers.get(self.battleCellId) + if battleBlocker: + self.blocker = battleBlocker + battleBlocker.b_setBattle(self.doId) + + def getLevelDoId(self): + return self.level.doId + + def getBattleCellId(self): + return self.battleCellId + + def getTaskZoneId(self): + pass + + def localMovieDone(self, needUpdate, deadToons, deadSuits, lastActiveSuitDied): + self.timer.stop() + self.resumeNeedUpdate = needUpdate + self.resumeDeadToons = deadToons + self.resumeDeadSuits = deadSuits + self.resumeLastActiveSuitDied = lastActiveSuitDied + if len(self.toons) == 0: + self.d_setMembers() + self.b_setState('Resume') + else: + totalHp = 0 + for suit in self.suits: + if suit.currHP > 0: + totalHp += suit.currHP + + self.roundCallback(self.battleCellId, self.activeToons, totalHp, deadSuits) + + def storeSuitsKilledThisBattle(self): + self.suitsKilledPerFloor.append(self.suitsKilledThisBattle) + + def resume(self, topFloor = 0): + if len(self.suits) == 0: + avList = [] + for toonId in self.activeToons: + toon = self.getToon(toonId) + if toon: + avList.append(toon) + + self.d_setMembers() + self.storeSuitsKilledThisBattle() + if self.bossBattle == 0: + self.b_setState('Reward') + else: + self.handleToonsWon(avList) + self.d_setBattleExperience() + self.b_setState(self.winState) + if self.blocker: + if len(self.activeToons): + self.blocker.b_setBattleFinished() + else: + if self.resumeNeedUpdate == 1: + self.d_setMembers() + if len(self.resumeDeadSuits) > 0 and self.resumeLastActiveSuitDied == 0 or len(self.resumeDeadToons) > 0: + self.needAdjust = 1 + self.setState('WaitForJoin') + self.resumeNeedUpdate = 0 + self.resumeDeadToons = [] + self.resumeDeadSuits = [] + self.resumeLastActiveSuitDied = 0 + + def handleToonsWon(self, toons): + pass + + def handleCrateReward(self, toons): + if not (config.GetBool('get-crate-reward-always', False) or random.random() <= 0.25): + return + + self.sendUpdate('announceCrateReward') + item = CatalogFurnitureItem.CatalogFurnitureItem(10040) + + for toon in toons: + toon.addToDeliverySchedule(item) + + def enterFaceOff(self): + self.notify.debug('DistributedLevelBattleAI.enterFaceOff()') + self.joinableFsm.request('Joinable') + self.runableFsm.request('Unrunable') + self.suits[0].releaseControl() + faceOffTime = self.calcToonMoveTime(self.pos, self.initialSuitPos) + FACEOFF_TAUNT_T + SERVER_BUFFER_TIME + self.notify.debug('faceOffTime = %s' % faceOffTime) + self.timer.startCallback(faceOffTime, self.__serverFaceOffDone) + return None + + def __serverFaceOffDone(self): + self.notify.debug('faceoff timed out on server') + self.ignoreFaceOffDone = 1 + self.handleFaceOffDone() + + def exitFaceOff(self): + self.notify.debug('DistributedLevelBattleAI.exitFaceOff()') + self.timer.stop() + return None + + def faceOffDone(self): + toonId = self.air.getAvatarIdFromSender() + if self.ignoreFaceOffDone == 1: + self.notify.debug('faceOffDone() - ignoring toon: %d' % toonId) + return + elif self.fsm.getCurrentState().getName() != 'FaceOff': + self.notify.warning('faceOffDone() - in state: %s' % self.fsm.getCurrentState().getName()) + return + elif self.toons.count(toonId) == 0: + self.notify.warning('faceOffDone() - toon: %d not in toon list' % toonId) + return + self.notify.debug('toon: %d done facing off' % toonId) + if not self.ignoreFaceOffDone: + self.handleFaceOffDone() + + def suitRequestJoin(self, suit): + self.notify.debug('DistributedLevelBattleAI.suitRequestJoin(%d)' % suit.getDoId()) + if suit in self.suits: + self.notify.warning('suit %s already in this battle' % suit.getDoId()) + return 0 + DistributedBattleBaseAI.DistributedBattleBaseAI.suitRequestJoin(self, suit) + + def enterReward(self): + self.joinableFsm.request('Unjoinable') + self.runableFsm.request('Unrunable') + self.timer.startCallback(FLOOR_REWARD_TIMEOUT, self.serverRewardDone) + return None + + def exitReward(self): + self.timer.stop() + return None diff --git a/toontown/coghq/DistributedLift.py b/toontown/coghq/DistributedLift.py new file mode 100755 index 00000000..476d7380 --- /dev/null +++ b/toontown/coghq/DistributedLift.py @@ -0,0 +1,202 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import LiftConstants +import MovingPlatform + +class DistributedLift(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLift') + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + self.moveSnd = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_elevator_up_down.ogg') + self.fsm = ClassicFSM.ClassicFSM('DistributedLift', [State.State('off', self.enterOff, self.exitOff, ['moving']), State.State('moving', self.enterMoving, self.exitMoving, ['waiting']), State.State('waiting', self.enterWaiting, self.exitWaiting, ['moving'])], 'off', 'off') + self.fsm.enterInitialState() + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + self.platform = self.attachNewNode('platParent') + + def setStateTransition(self, toState, fromState, arrivalTimestamp): + self.notify.debug('setStateTransition: %s->%s' % (fromState, toState)) + if not self.isGenerated(): + self.initialState = toState + self.initialFromState = fromState + self.initialStateTimestamp = arrivalTimestamp + else: + self.fsm.request('moving', [toState, fromState, arrivalTimestamp]) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.initPlatform() + self.state = None + self.fsm.request('moving', [self.initialState, self.initialFromState, self.initialStateTimestamp]) + del self.initialState + del self.initialStateTimestamp + return + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + self.fsm.requestFinalState() + BasicEntities.DistributedNodePathEntity.disable(self) + + def delete(self): + self.notify.debug('delete') + del self.moveSnd + del self.fsm + self.destroyPlatform() + self.platform.removeNode() + del self.platform + BasicEntities.DistributedNodePathEntity.delete(self) + + def initPlatform(self): + model = loader.loadModel(self.modelPath) + if model is None: + return + model.setScale(self.modelScale) + if self.floorName is None: + return + self.platformModel = MovingPlatform.MovingPlatform() + self.platformModel.setupCopyModel(self.getParentToken(), model, self.floorName) + self.accept(self.platformModel.getEnterEvent(), self.localToonEntered) + self.accept(self.platformModel.getExitEvent(), self.localToonLeft) + self.startGuard = None + self.endGuard = None + zoneNp = self.getZoneNode() + if len(self.startGuardName): + self.startGuard = zoneNp.find('**/%s' % self.startGuardName) + if len(self.endGuardName): + self.endGuard = zoneNp.find('**/%s' % self.endGuardName) + side2srch = {'front': '**/wall_front', + 'back': '**/wall_back', + 'left': '**/wall_left', + 'right': '**/wall_right'} + for side in side2srch.values(): + np = self.platformModel.find(side) + if not np.isEmpty(): + np.setScale(1.0, 1.0, 2.0) + np.setZ(-10) + np.flattenLight() + + self.startBoardColl = NodePathCollection() + self.endBoardColl = NodePathCollection() + for side in self.startBoardSides: + np = self.platformModel.find(side2srch[side]) + if np.isEmpty(): + DistributedLift.warning("couldn't find %s board collision" % side) + else: + self.startBoardColl.addPath(np) + + for side in self.endBoardSides: + np = self.platformModel.find(side2srch[side]) + if np.isEmpty(): + DistributedLift.warning("couldn't find %s board collision" % side) + else: + self.endBoardColl.addPath(np) + + self.platformModel.reparentTo(self.platform) + return + + def destroyPlatform(self): + if hasattr(self, 'platformModel'): + self.ignore(self.platformModel.getEnterEvent()) + self.ignore(self.platformModel.getExitEvent()) + self.platformModel.destroy() + del self.platformModel + if self.startGuard is not None: + self.startGuard.unstash() + if self.endGuard is not None: + self.endGuard.unstash() + del self.startGuard + del self.endGuard + del self.startBoardColl + del self.endBoardColl + return + + def localToonEntered(self): + self.sendUpdate('setAvatarEnter') + + def localToonLeft(self): + self.sendUpdate('setAvatarLeave') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def getPosition(self, state): + if state is LiftConstants.Down: + return self.startPos + else: + return self.endPos + + def getGuard(self, state): + if state is LiftConstants.Down: + return self.startGuard + else: + return self.endGuard + + def getBoardColl(self, state): + if state is LiftConstants.Down: + return self.startBoardColl + else: + return self.endBoardColl + + def enterMoving(self, toState, fromState, arrivalTimestamp): + self.notify.debug('enterMoving, %s->%s' % (fromState, toState)) + if self.state == toState: + self.notify.warning('already in state %s' % toState) + startPos = self.getPosition(fromState) + endPos = self.getPosition(toState) + startGuard = self.getGuard(fromState) + endGuard = self.getGuard(toState) + startBoardColl = self.getBoardColl(fromState) + endBoardColl = self.getBoardColl(toState) + + def startMoving(self = self, guard = startGuard, boardColl = startBoardColl): + if guard is not None and not guard.isEmpty(): + guard.unstash() + boardColl.unstash() + self.soundIval = SoundInterval(self.moveSnd, node=self.platform) + self.soundIval.loop() + return + + def doneMoving(self = self, guard = endGuard, boardColl = endBoardColl, newState = toState): + self.state = newState + if hasattr(self, 'soundIval'): + self.soundIval.pause() + del self.soundIval + if guard is not None and not guard.isEmpty(): + guard.stash() + boardColl.stash() + self.fsm.request('waiting') + return + + self.moveIval = Sequence(Func(startMoving), LerpPosInterval(self.platform, self.duration, endPos, startPos=startPos, blendType='easeInOut', name='lift-%s-move' % self.entId, fluid=1), Func(doneMoving)) + ivalStartT = globalClockDelta.networkToLocalTime(arrivalTimestamp, bits=32) - self.moveIval.getDuration() + self.moveIval.start(globalClock.getFrameTime() - ivalStartT) + + def exitMoving(self): + if hasattr(self, 'soundIval'): + self.soundIval.pause() + del self.soundIval + self.moveIval.pause() + del self.moveIval + + def enterWaiting(self): + self.notify.debug('enterWaiting') + + def exitWaiting(self): + pass diff --git a/toontown/coghq/DistributedLiftAI.py b/toontown/coghq/DistributedLiftAI.py new file mode 100755 index 00000000..c1805751 --- /dev/null +++ b/toontown/coghq/DistributedLiftAI.py @@ -0,0 +1,127 @@ +from direct.distributed.ClockDelta import * +from otp.level import DistributedEntityAI +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import LiftConstants + +class DistributedLiftAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLiftAI') + + def __init__(self, level, entId, initialState = LiftConstants.Down): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.name = 'Lift%s:%s' % (self.levelDoId, self.entId) + self.startMoveTaskName = '%s-StartMove' % self.name + self.moveDoneTaskName = '%s-MoveDone' % self.name + self.state = initialState + self.fromState = initialState + self.stateTimestamp = globalClock.getFrameTime() + self.boardedAvs = [] + + def generate(self): + self.notify.debug('generate') + DistributedEntityAI.DistributedEntityAI.generate(self) + self.fsm = ClassicFSM.ClassicFSM('DistributedLiftAI', [State.State('off', self.enterOff, self.exitOff, ['waiting']), State.State('waiting', self.enterWaiting, self.exitWaiting, ['moving', 'waiting']), State.State('moving', self.enterMoving, self.exitMoving, ['waiting'])], 'off', 'off') + self.fsm.enterInitialState() + self.fsm.request('waiting') + + def delete(self): + self.notify.debug('delete') + DistributedEntityAI.DistributedEntityAI.delete(self) + self.ignoreAll() + taskMgr.remove(self.startMoveTaskName) + taskMgr.remove(self.moveDoneTaskName) + del self.fsm + + def b_setStateTransition(self, toState, fromState, arrivalTimestamp): + self.d_setStateTransition(toState, fromState, arrivalTimestamp) + self.setStateTransition(toState, fromState, arrivalTimestamp) + + def d_setStateTransition(self, toState, fromState, arrivalTimestamp): + self.sendUpdate('setStateTransition', [toState, fromState, arrivalTimestamp]) + + def setStateTransition(self, toState, fromState, arrivalTimestamp): + self.state = toState + self.fromState = fromState + self.stateTimestamp = arrivalTimestamp + + def getStateTransition(self): + return (self.state, self.fromState, self.stateTimestamp) + + def setAvatarEnter(self): + avId = self.air.getAvatarIdFromSender() + avatar = self.air.doId2do.get(avId) + if not avatar: + self.air.writeServerEvent('suspicious', avId, 'LiftAI.setAvatarEnter avId not valid') + return + self.notify.debug('setAvatarEnter: %s' % avId) + if avId in self.boardedAvs: + self.notify.warning('avatar %s already in list' % avId) + else: + self.boardedAvs.append(avId) + + def handleExitedAvatar(self = self, avId = avId): + self.notify.debug('avatar %s exited' % avId) + self.avatarLeft(avId) + + self.acceptOnce(self.air.getAvatarExitEvent(avId), handleExitedAvatar) + self.setMoveLater(self.moveDelay) + + def setAvatarLeave(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setAvatarLeave: %s' % avId) + self.avatarLeft(avId) + + def avatarLeft(self, avId): + if avId in self.boardedAvs: + self.boardedAvs.remove(avId) + self.ignore(self.air.getAvatarExitEvent(avId)) + if len(self.boardedAvs) == 0: + if self.fsm.getCurrentState().getName() == 'waiting': + self.fsm.request('waiting') + else: + self.notify.warning('avatar %s tried to leave, but is not in list' % avId) + + def setMoveLater(self, delay): + + def startMoving(task, self = self): + targetState = LiftConstants.oppositeState(self.state) + self.fsm.request('moving', [targetState]) + return Task.done + + self.cancelMoveLater() + taskMgr.doMethodLater(delay, startMoving, self.startMoveTaskName) + + def cancelMoveLater(self): + taskMgr.remove(self.startMoveTaskName) + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterWaiting(self): + self.notify.debug('enterWaiting') + self.setMoveLater(self.autoMoveDelay) + + def exitWaiting(self): + self.cancelMoveLater() + + def enterMoving(self, targetState): + self.notify.debug('enterMoving, target=%s' % targetState) + if self.state == targetState: + self.notify.warning('already in state %s' % targetState) + return + arriveDelay = 1.0 + self.duration + self.b_setStateTransition(targetState, self.state, globalClockDelta.localToNetworkTime(globalClock.getFrameTime() + arriveDelay, bits=32)) + + def doneMoving(task, self = self): + self.fsm.request('waiting') + return Task.done + + taskMgr.doMethodLater(arriveDelay, doneMoving, self.moveDoneTaskName) + + def exitMoving(self): + pass diff --git a/toontown/coghq/DistributedMaze.py b/toontown/coghq/DistributedMaze.py new file mode 100755 index 00000000..ce2f14de --- /dev/null +++ b/toontown/coghq/DistributedMaze.py @@ -0,0 +1,235 @@ +from otp.level.BasicEntities import DistributedNodePathEntity +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +import random +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import globalClockDelta +import DistributedBarrelBase +from otp.level.BasicEntities import DistributedNodePathEntity +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownTimer +from direct.task import Task +from direct.gui.DirectGui import DGG, DirectFrame, DirectLabel + +class DistributedMaze(DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMaze') + ScheduleTaskName = 'mazeScheduler' + RemoveBlocksDict = {2: ('HedgeBlock_0_1',), + 4: (('HedgeBlock_0_1', 'HedgeBlock_1_3', 'HedgeBlock_2_3'), ('HedgeBlock_0_2', 'HedgeBlock_2_3', 'HedgeBlock_1_3'), ('HedgeBlock_0_1', 'HedgeBlock_0_2', 'HedgeBlock_1_3', 'HedgeBlock_2_3'))} + + def __init__(self, cr): + DistributedNodePathEntity.__init__(self, cr) + self.numSections = 0 + self.GameDuration = 35.0 + self.numSections * 15.0 + self.timer = None + self.frame2D = None + self.gameLabel = None + self.gameStarted = 0 + self.finished = 0 + self.timedOut = 0 + self.toonFinishedText = TTLocalizer.toonFinishedHedgeMaze + self.toonEnteredText = TTLocalizer.enterHedgeMaze + return + + def announceGenerate(self): + DistributedNodePathEntity.announceGenerate(self) + self.addHints(self.roomHold) + self.loadGui() + + def disable(self): + DistributedNodePathEntity.disable(self) + self.unloadGui() + self.cleanupTimer() + self.ignoreAll() + + def delete(self): + self.cleanupTimer() + DistributedNodePathEntity.delete(self) + + def setRoomDoId(self, roomDoId): + self.roomDoId = roomDoId + room = self.cr.doId2do.get(roomDoId) + if room: + self.gotRoom([room]) + else: + self.roomRequest = self.cr.relatedObjectMgr.requestObjects([roomDoId], allCallback=self.gotRoom, timeout=5) + + def gotRoom(self, rooms): + self.roomRequest = None + room = rooms[0] + self.roomHold = room + rotations = [0, + 0, + 90, + 90, + 180, + 180, + 270, + 270] + self.getRng().shuffle(rotations) + self.numSections = 0 + for i in xrange(0, 4): + maze = room.getGeom().find('**/Maze_Inside_%d' % i) + if not maze.isEmpty(): + self.numSections += 1 + if rotations: + maze.setH(rotations.pop()) + + self.GameDuration = 35.0 + self.numSections * 15.0 + self.removeHedgeBlocks(room) + return + + def addHints(self, room): + self.focusPoint = self.attachNewNode('GolfGreenGameFrame') + hintList = room.getGeom().findAllMatches('**/dead*') + for hint in hintList: + self.actSphere = CollisionSphere(0, 0, 0, 7.0) + self.actSphereNode = CollisionNode('mazegame_hint-%s-%s' % (self.level.getLevelId(), self.entId)) + self.actSphereNode.addSolid(self.actSphere) + self.actSphereNodePath = hint.attachNewNode(self.actSphereNode) + self.actSphereNode.setCollideMask(WallBitmask) + self.actSphere.setTangible(0) + self.enterEvent = 'enter' + self.actSphereNode.getName() + self.accept(self.enterEvent, self.__handleToonEnterHint) + self.exitEvent = 'exit' + self.actSphereNode.getName() + self.accept(self.exitEvent, self.__handleToonExitHint) + + enterance = room.getGeom().find('**/ENTRANCE') + self.enterSphere = CollisionSphere(0, 0, 0, 8.0) + self.enterSphereNode = CollisionNode('mazegame_enter-%s-%s' % (self.level.getLevelId(), self.entId)) + self.enterSphereNode.addSolid(self.enterSphere) + self.enterSphereNodePath = enterance.attachNewNode(self.enterSphereNode) + self.enterSphereNode.setCollideMask(WallBitmask) + self.enterSphere.setTangible(0) + self.enteranceEvent = 'enter' + self.enterSphereNode.getName() + self.accept(self.enteranceEvent, self.__handleToonEnterance) + finish = room.getGeom().find('**/finish') + self.finishSphere = CollisionSphere(0, 0, 0, 15.0) + self.finishSphereNode = CollisionNode('mazegame_finish-%s-%s' % (self.level.getLevelId(), self.entId)) + self.finishSphereNode.addSolid(self.finishSphere) + self.finishSphereNodePath = finish.attachNewNode(self.finishSphereNode) + self.finishSphereNode.setCollideMask(WallBitmask) + self.finishSphere.setTangible(0) + self.finishEvent = 'enter' + self.finishSphereNode.getName() + self.accept(self.finishEvent, self.__handleToonFinish) + + def __handleToonEnterance(self, collEntry): + if not self.gameStarted: + self.notify.debug('sending clientTriggered for %d' % self.doId) + self.sendUpdate('setClientTriggered', []) + self.level.countryClub.showInfoText(self.toonEnteredText) + + def __handleToonFinish(self, collEntry): + self.sendUpdate('setFinishedMaze', []) + self.finished = 1 + + def __handleToonEnterHint(self, collEntry): + camHeight = base.localAvatar.getClampedAvatarHeight() + heightScaleFactor = camHeight * 0.3333333333 + defLookAt = Point3(0.0, 1.5, camHeight) + cameraPoint = Point3(0.0, -22.0 * heightScaleFactor, camHeight + 54.0) + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.startUpdateSmartCamera(push=0) + base.localAvatar.setIdealCameraPos(cameraPoint) + + def __handleToonExitHint(self, collEntry): + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.startUpdateSmartCamera() + base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex) + self.cameraHold = None + return + + def getRng(self): + return random.Random(self.entId * self.doId) + + def removeHedgeBlocks(self, room): + if self.numSections in self.RemoveBlocksDict: + blocksToRemove = self.getRng().choice(self.RemoveBlocksDict[self.numSections]) + for blockName in blocksToRemove: + block = room.getGeom().find('**/%s' % blockName) + if not block.isEmpty(): + block.removeNode() + + def setGameStart(self, timestamp): + self.notify.debug('%d setGameStart: Starting game' % self.doId) + self.gameStartTime = globalClockDelta.networkToLocalTime(timestamp) + self.gameStarted = True + curGameTime = self.getCurrentGameTime() + timeLeft = self.GameDuration - curGameTime + self.cleanupTimer() + self.timer = ToontownTimer.ToontownTimer() + self.timer.posBelowTopRightCorner() + self.timer.setTime(timeLeft) + self.timer.countdown(timeLeft, self.timerExpired) + self.startScheduleTask() + self.frame2D.show() + + def setGameOver(self): + self.timedOut = 1 + if not self.finished: + self.sendUpdate('damageMe', []) + roomNum = self.level.roomNum + club = self.level.countryClub + self.gameOverTrack = Sequence() + self.gameOverTrack.append(localAvatar.getTeleportOutTrack()) + self.gameOverTrack.append(Func(localAvatar.setPos, self.finishSphereNodePath.getPos(render))) + self.gameOverTrack.append(Func(localAvatar.play, 'jump')) + self.gameOverTrack.append(Func(self.level.countryClub.camEnterRoom, roomNum)) + self.gameOverTrack.start() + self.timerExpired() + + def local2GameTime(self, timestamp): + return timestamp - self.gameStartTime + + def game2LocalTime(self, timestamp): + return timestamp + self.gameStartTime + + def getCurrentGameTime(self): + return self.local2GameTime(globalClock.getFrameTime()) + + def startScheduleTask(self): + taskMgr.add(self.scheduleTask, self.ScheduleTaskName) + + def stopScheduleTask(self): + taskMgr.remove(self.ScheduleTaskName) + + def scheduleTask(self, task): + curTime = self.getCurrentGameTime() + + def cleanupTimer(self): + if self.timer: + self.timer.stop() + self.timer.destroy() + self.timer = None + return + + def timerExpired(self): + self.cleanupTimer() + self.unloadGui() + + def loadGui(self): + self.frame2D = DirectFrame(scale=1.0, pos=(0.0, 0, 0.9), relief=DGG.FLAT, parent=aspect2d, frameSize=(-0.3, + 0.3, + -0.05, + 0.05), frameColor=(0.737, 0.573, 0.345, 0.3)) + self.frame2D.hide() + self.gameLabel = DirectLabel(parent=self.frame2D, relief=None, pos=(0, 0, 0), scale=1.0, text=TTLocalizer.mazeLabel, text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text_scale=0.075, text_pos=(0, -0.02)) + return + + def unloadGui(self): + if self.frame2D: + self.frame2D.destroy() + self.frame2D = None + if self.gameLabel: + self.gameLabel.destroy() + self.gameLabel = None + return + + def toonFinished(self, avId, place, lastToon): + toon = base.cr.doId2do.get(avId) + if toon and not self.timedOut: + self.level.countryClub.showInfoText(self.toonFinishedText % (toon.getName(), TTLocalizer.hedgeMazePlaces[place])) + if lastToon: + self.setGameOver() diff --git a/toontown/coghq/DistributedMazeAI.py b/toontown/coghq/DistributedMazeAI.py new file mode 100755 index 00000000..d1ebdff2 --- /dev/null +++ b/toontown/coghq/DistributedMazeAI.py @@ -0,0 +1,87 @@ +from otp.level import DistributedEntityAI +import DistributedBarrelBaseAI +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import globalClockDelta +from direct.task import Task + +class DistributedMazeAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMazeAI') + + def __init__(self, level, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.roomDoId = level.doId + self.GameDuration = 60.0 + self.DamageOnFailure = 20 + self.finishedList = [] + + def delete(self): + self.removeAllTasks() + DistributedEntityAI.DistributedEntityAI.delete(self) + + def announceGenerate(self): + DistributedEntityAI.DistributedEntityAI.announceGenerate(self) + self.mazeEndTimeTaskName = self.uniqueName('mazeEndTime') + + def getRoomDoId(self): + return self.roomDoId + + def setClientTriggered(self): + if not hasattr(self, 'gameStartTime'): + self.gameStartTime = globalClock.getRealTime() + self.b_setGameStart(globalClockDelta.localToNetworkTime(self.gameStartTime)) + + def b_setGameStart(self, timestamp): + self.d_setGameStart(timestamp) + self.setGameStart(timestamp) + + def d_setGameStart(self, timestamp): + self.notify.debug('BASE: Sending setGameStart') + self.sendUpdate('setGameStart', [timestamp]) + + def setGameStart(self, timestamp): + self.notify.debug('BASE: setGameStart') + self.GameDuration = 35.0 + self.numSections * 15.0 + self.prepareForGameStartOrRestart() + + def prepareForGameStartOrRestart(self): + self.doMethodLater(self.GameDuration, self.gameEndingTimeHit, self.mazeEndTimeTaskName) + + def setFinishedMaze(self): + senderId = self.air.getAvatarIdFromSender() + if senderId not in self.finishedList: + toon = simbase.air.doId2do.get(senderId) + if toon: + if len(self.finishedList) < 1: + toon.toonUp(200.0) + else: + toon.toonUp(20.0) + lastToon = 0 + if hasattr(self, 'level'): + numToons = len(self.level.presentAvIds) + if numToons == len(self.finishedList) + 1: + lastToon = 1 + self.sendUpdate('toonFinished', [senderId, len(self.finishedList), lastToon]) + self.finishedList.append(senderId) + + def gameEndingTimeHit(self, task): + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + if room: + avIds = room.presentAvIds + for avId in avIds: + av = simbase.air.doId2do.get(avId) + if av and avId not in self.finishedList: + self.finishedList.append(avId) + + self.sendUpdate('setGameOver', []) + + def damageMe(self): + senderId = self.air.getAvatarIdFromSender() + av = simbase.air.doId2do.get(senderId) + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + if room: + avIds = room.presentAvIds + if av and senderId in avIds: + av.takeDamage(self.DamageOnFailure, quietly=0) + room.sendUpdate('forceOuch', [self.DamageOnFailure]) diff --git a/toontown/coghq/DistributedMegaCorp.py b/toontown/coghq/DistributedMegaCorp.py new file mode 100755 index 00000000..6d35f2d0 --- /dev/null +++ b/toontown/coghq/DistributedMegaCorp.py @@ -0,0 +1,11 @@ +from direct.directnotify import DirectNotifyGlobal +import DistributedFactory + +class DistributedMegaCorp(DistributedFactory.DistributedFactory): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMegaCorp') + + def __init__(self, cr): + DistributedFactory.DistributedFactory.__init__(self, cr) + + def getFloorOuchLevel(self): + return 8 diff --git a/toontown/coghq/DistributedMegaCorpAI.py b/toontown/coghq/DistributedMegaCorpAI.py new file mode 100755 index 00000000..a105caf1 --- /dev/null +++ b/toontown/coghq/DistributedMegaCorpAI.py @@ -0,0 +1,8 @@ +from direct.directnotify import DirectNotifyGlobal +import DistributedFactoryAI + +class DistributedMegaCorpAI(DistributedFactoryAI.DistributedFactoryAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBrutalFactoryAI') + + def __init__(self, air, factoryId, zoneId, entranceId, avIds): + DistributedFactoryAI.DistributedFactoryAI.__init__(self, air, factoryId, zoneId, entranceId, avIds) diff --git a/toontown/coghq/DistributedMint.py b/toontown/coghq/DistributedMint.py new file mode 100755 index 00000000..80f66141 --- /dev/null +++ b/toontown/coghq/DistributedMint.py @@ -0,0 +1,221 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import BulletinBoardWatcher +from otp.otpbase import OTPGlobals +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase import TTLocalizer +from toontown.coghq import DistributedMintRoom, MintLayout, MintRoom + +class DistributedMint(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMint') + ReadyPost = 'MintReady' + WinEvent = 'MintWinEvent' + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + self.notify.debug('generate: %s' % self.doId) + DistributedObject.DistributedObject.generate(self) + bboard.post('mint', self) + self.roomWatcher = None + self.geom = None + self.rooms = [] + self.hallways = [] + self.allRooms = [] + self.curToonRoomNum = None + base.localAvatar.setCameraCollisionsCanMove(1) + base.localAvatar.reparentTo(render) + base.localAvatar.setPosHpr(0, 0, 0, 0, 0, 0) + self.accept('SOSPanelEnter', self.handleSOSPanel) + return + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def setMintId(self, id): + DistributedMint.notify.debug('setMintId: %s' % id) + self.mintId = id + + def setFloorNum(self, num): + DistributedMint.notify.debug('floorNum: %s' % num) + self.floorNum = num + self.layout = MintLayout.MintLayout(self.mintId, self.floorNum) + + def setRoomDoIds(self, roomDoIds): + self.roomDoIds = roomDoIds + self.roomWatcher = BulletinBoardWatcher.BulletinBoardWatcher('roomWatcher-%s' % self.doId, [ DistributedMintRoom.getMintRoomReadyPostName(doId) for doId in self.roomDoIds ], self.gotAllRooms) + + def gotAllRooms(self): + self.notify.debug('mint %s: got all rooms' % self.doId) + if self.roomWatcher: + self.roomWatcher.destroy() + self.roomWatcher = None + self.geom = render.attachNewNode('mint%s' % self.doId) + for doId in self.roomDoIds: + self.rooms.append(base.cr.doId2do[doId]) + self.rooms[-1].setMint(self) + + self.notify.info('mintId %s, floor %s, %s' % (self.mintId, self.floorNum, self.rooms[0].avIdList)) + rng = self.layout.getRng() + numRooms = self.layout.getNumRooms() + for i, room in enumerate(self.rooms): + if i == 0: + room.getGeom().reparentTo(self.geom) + else: + room.attachTo(self.hallways[i - 1], rng) + self.allRooms.append(room) + self.listenForFloorEvents(room) + if i < numRooms - 1: + hallway = MintRoom.MintRoom(self.layout.getHallwayModel(i)) + hallway.attachTo(room, rng) + hallway.setRoomNum(i * 2 + 1) + hallway.initFloorCollisions() + hallway.enter() + self.hallways.append(hallway) + self.allRooms.append(hallway) + self.listenForFloorEvents(hallway) + + def handleCameraRayFloorCollision(collEntry, self = self): + name = collEntry.getIntoNode().getName() + self.notify.debug('camera floor ray collided with: %s' % name) + prefix = MintRoom.MintRoom.FloorCollPrefix + prefixLen = len(prefix) + if name[:prefixLen] == prefix: + try: + roomNum = int(name[prefixLen:]) + except: + DistributedLevel.notify.warning('Invalid zone floor collision node: %s' % name) + else: + self.camEnterRoom(roomNum) + + self.accept('on-floor', handleCameraRayFloorCollision) + if bboard.has('mintRoom'): + self.warpToRoom(bboard.get('mintRoom')) + firstSetZoneDoneEvent = self.cr.getNextSetZoneDoneEvent() + + def handleFirstSetZoneDone(): + self.notify.debug('mintHandleFirstSetZoneDone') + bboard.post(DistributedMint.ReadyPost, self) + + self.acceptOnce(firstSetZoneDoneEvent, handleFirstSetZoneDone) + zoneList = [OTPGlobals.UberZone, self.zoneId] + for room in self.rooms: + zoneList.extend(room.zoneIds) + + base.cr.sendSetZoneMsg(self.zoneId, zoneList) + self.accept('takingScreenshot', self.handleScreenshot) + return + + def listenForFloorEvents(self, room): + roomNum = room.getRoomNum() + floorCollName = room.getFloorCollName() + + def handleZoneEnter(collisionEntry, self = self, roomNum = roomNum): + self.toonEnterRoom(roomNum) + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + room = self.allRooms[roomNum] + ouchLevel = room.getFloorOuchLevel() + room.startOuch(ouchLevel) + + self.accept('enter%s' % floorCollName, handleZoneEnter) + + def handleZoneExit(collisionEntry, self = self, roomNum = roomNum): + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + self.allRooms[roomNum].stopOuch() + + self.accept('exit%s' % floorCollName, handleZoneExit) + + def getAllRoomsTimeout(self): + self.notify.warning('mint %s: timed out waiting for room objs' % self.doId) + + def toonEnterRoom(self, roomNum): + self.notify.debug('toonEnterRoom: %s' % roomNum) + if roomNum != self.curToonRoomNum: + if self.curToonRoomNum is not None: + self.allRooms[self.curToonRoomNum].localToonFSM.request('notPresent') + self.allRooms[roomNum].localToonFSM.request('present') + self.curToonRoomNum = roomNum + return + + def camEnterRoom(self, roomNum): + self.notify.debug('camEnterRoom: %s' % roomNum) + if roomNum % 2 == 1: + minVis = roomNum - 2 + maxVis = roomNum + 2 + else: + minVis = roomNum - 1 + maxVis = roomNum + 1 + for i, room in enumerate(self.allRooms): + if i < minVis or i > maxVis: + room.getGeom().stash() + else: + room.getGeom().unstash() + + def setBossConfronted(self, avId): + if avId == base.localAvatar.doId: + return + av = base.cr.identifyFriend(avId) + if av is None: + return + base.localAvatar.setSystemMessage(avId, TTLocalizer.MintBossConfrontedMsg % av.getName()) + return + + def warpToRoom(self, roomId): + for i in xrange(len(self.rooms)): + room = self.rooms[i] + if room.roomId == roomId: + break + else: + return False + + base.localAvatar.setPosHpr(room.getGeom(), 0, 0, 0, 0, 0, 0) + self.camEnterRoom(i * 2) + return True + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + for hallway in self.hallways: + hallway.exit() + + self.rooms = [] + for hallway in self.hallways: + hallway.delete() + + self.hallways = [] + self.allRooms = [] + if self.roomWatcher: + self.roomWatcher.destroy() + self.roomWatcher = None + if self.geom is not None: + self.geom.removeNode() + self.geom = None + base.localAvatar.setCameraCollisionsCanMove(0) + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.ignore('SOSPanelEnter') + bboard.remove('mint') + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.rooms[0].avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def handleScreenshot(self): + base.addScreenshotString('mintId: %s, floor (from 1): %s' % (self.mintId, self.floorNum + 1)) + if hasattr(self, 'currentRoomName'): + base.addScreenshotString('%s' % self.currentRoomName) diff --git a/toontown/coghq/DistributedMintAI.py b/toontown/coghq/DistributedMintAI.py new file mode 100755 index 00000000..ddaff882 --- /dev/null +++ b/toontown/coghq/DistributedMintAI.py @@ -0,0 +1,66 @@ +from direct.distributed import DistributedObjectAI +from otp.level import DistributedLevelAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.coghq import MintLayout, DistributedMintRoomAI +from toontown.coghq import BattleExperienceAggregatorAI + +class DistributedMintAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintAI') + + def __init__(self, air, mintId, zoneId, floorNum, avIds): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.mintId = mintId + self.zoneId = zoneId + self.floorNum = floorNum + self.avIds = avIds + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.notify.info('generate %s, id=%s, floor=%s' % (self.doId, self.mintId, self.floorNum)) + self.layout = MintLayout.MintLayout(self.mintId, self.floorNum) + self.rooms = [] + self.battleExpAggreg = BattleExperienceAggregatorAI.BattleExperienceAggregatorAI() + for i in xrange(self.layout.getNumRooms()): + room = DistributedMintRoomAI.DistributedMintRoomAI(self.air, self.mintId, self.doId, self.zoneId, self.layout.getRoomId(i), i * 2, self.avIds, self.battleExpAggreg) + room.generateWithRequired(self.zoneId) + self.rooms.append(room) + + roomDoIds = [] + for room in self.rooms: + roomDoIds.append(room.doId) + + self.sendUpdate('setRoomDoIds', [roomDoIds]) + description = '%s|%s|%s' % (self.mintId, self.floorNum, self.avIds) + for avId in self.avIds: + self.air.writeServerEvent('mintEntered', avId, description) + + def requestDelete(self): + self.notify.info('requestDelete: %s' % self.doId) + for room in self.rooms: + room.requestDelete() + + DistributedObjectAI.DistributedObjectAI.requestDelete(self) + + def delete(self): + self.notify.info('delete: %s' % self.doId) + del self.rooms + del self.layout + del self.battleExpAggreg + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getTaskZoneId(self): + return self.mintId + + def allToonsGone(self): + self.notify.info('allToonsGone') + self.requestDelete() + + def getZoneId(self): + return self.zoneId + + def getMintId(self): + return self.mintId + + def getFloorNum(self): + return self.floorNum diff --git a/toontown/coghq/DistributedMintBattle.py b/toontown/coghq/DistributedMintBattle.py new file mode 100755 index 00000000..24300f82 --- /dev/null +++ b/toontown/coghq/DistributedMintBattle.py @@ -0,0 +1,48 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleBase import * +from toontown.coghq import DistributedLevelBattle +from direct.directnotify import DirectNotifyGlobal +from toontown.toon import TTEmote +from otp.avatar import Emote +from toontown.battle import SuitBattleGlobals +import random +from toontown.suit import SuitDNA +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals +from otp.nametag import NametagGlobals + +class DistributedMintBattle(DistributedLevelBattle.DistributedLevelBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintBattle') + + def __init__(self, cr): + DistributedLevelBattle.DistributedLevelBattle.__init__(self, cr) + self.fsm.addState(State.State('MintReward', self.enterMintReward, self.exitMintReward, ['Resume'])) + offState = self.fsm.getStateNamed('Off') + offState.addTransition('MintReward') + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('MintReward') + + def enterMintReward(self, ts): + self.notify.debug('enterMintReward()') + self.disableCollision() + self.delayDeleteMembers() + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + if self.bossBattle: + messenger.send('localToonConfrontedMintBoss') + self.movie.playReward(ts, self.uniqueName('building-reward'), self.__handleMintRewardDone, noSkip=True) + + def __handleMintRewardDone(self): + self.notify.debug('mint reward done') + if self.hasLocalToon(): + self.d_rewardDone(base.localAvatar.doId) + self.movie.resetReward() + self.fsm.request('Resume') + + def exitMintReward(self): + self.notify.debug('exitMintReward()') + self.movie.resetReward(finish=1) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) diff --git a/toontown/coghq/DistributedMintBattleAI.py b/toontown/coghq/DistributedMintBattleAI.py new file mode 100755 index 00000000..6d2ba1de --- /dev/null +++ b/toontown/coghq/DistributedMintBattleAI.py @@ -0,0 +1,58 @@ +from toontown.toonbase import ToontownGlobals +from toontown.coghq import DistributedLevelBattleAI +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.battle.BattleBase import * +import CogDisguiseGlobals +from toontown.toonbase.ToontownBattleGlobals import getMintCreditMultiplier +from direct.showbase.PythonUtil import addListsByValue + +class DistributedMintBattleAI(DistributedLevelBattleAI.DistributedLevelBattleAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintBattleAI') + + def __init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, roundCallback = None, finishCallback = None, maxSuits = 4): + DistributedLevelBattleAI.DistributedLevelBattleAI.__init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, 'MintReward', roundCallback, finishCallback, maxSuits) + self.battleCalc.setSkillCreditMultiplier(1) + if self.bossBattle: + self.level.d_setBossConfronted(toonId) + self.fsm.addState(State.State('MintReward', self.enterMintReward, self.exitMintReward, ['Resume'])) + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('MintReward') + + def getTaskZoneId(self): + return self.level.mintId + + def handleToonsWon(self, toons): + extraMerits = [0, 0, 0, 0] + amount = ToontownGlobals.MintCogBuckRewards[self.level.mintId] + index = ToontownGlobals.cogHQZoneId2deptIndex(self.level.mintId) + extraMerits[index] = amount + self.handleCrateReward(toons) + for toon in toons: + recovered, notRecovered = self.air.questManager.recoverItems(toon, self.suitsKilled, self.getTaskZoneId()) + self.toonItems[toon.doId][0].extend(recovered) + self.toonItems[toon.doId][1].extend(notRecovered) + meritArray = self.air.promotionMgr.recoverMerits(toon, self.suitsKilled, self.getTaskZoneId(), getMintCreditMultiplier(self.getTaskZoneId()) * 2.0, extraMerits=extraMerits, addInvasion=False) + if toon.doId in self.helpfulToons: + self.toonMerits[toon.doId] = addListsByValue(self.toonMerits[toon.doId], meritArray) + else: + self.notify.debug('toon %d not helpful list, skipping merits' % toon.doId) + + def enterMintReward(self): + self.joinableFsm.request('Unjoinable') + self.runableFsm.request('Unrunable') + self.resetResponses() + self.assignRewards() + self.bossDefeated = 1 + self.level.setVictors(self.activeToons[:]) + self.timer.startCallback(BUILDING_REWARD_TIMEOUT, self.serverRewardDone) + return None + + def exitMintReward(self): + return None + + def enterResume(self): + DistributedLevelBattleAI.DistributedLevelBattleAI.enterResume(self) + if self.bossBattle and self.bossDefeated: + self.battleMgr.level.b_setDefeated() diff --git a/toontown/coghq/DistributedMintElevatorExt.py b/toontown/coghq/DistributedMintElevatorExt.py new file mode 100755 index 00000000..05f49a85 --- /dev/null +++ b/toontown/coghq/DistributedMintElevatorExt.py @@ -0,0 +1,125 @@ +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from toontown.building.ElevatorConstants import * +from toontown.building.ElevatorUtils import * +from toontown.building import DistributedElevatorExt +from toontown.building import DistributedElevator +from toontown.toonbase import ToontownGlobals +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.gui import DirectGui +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +import CogDisguiseGlobals + +class DistributedMintElevatorExt(DistributedElevatorExt.DistributedElevatorExt): + + def __init__(self, cr): + DistributedElevatorExt.DistributedElevatorExt.__init__(self, cr) + self.type = ELEVATOR_MINT + self.countdownTime = ElevatorData[self.type]['countdown'] + + def generate(self): + DistributedElevatorExt.DistributedElevatorExt.generate(self) + + def delete(self): + self.elevatorModel.removeNode() + del self.elevatorModel + DistributedElevatorExt.DistributedElevatorExt.delete(self) + + def setMintId(self, mintId): + self.mintId = mintId + mintId2originId = {ToontownGlobals.CashbotMintIntA: 1, + ToontownGlobals.CashbotMintIntB: 2, + ToontownGlobals.CashbotMintIntC: 0} + originId = mintId2originId[self.mintId] + geom = self.cr.playGame.hood.loader.geom + locator = geom.find('**/elevator_origin_%s' % originId) + if locator: + self.elevatorModel.setPosHpr(locator, 0, 0, 0, 0, 0, 0) + else: + self.notify.error('No origin found for originId: %s' % originId) + locator = geom.find('**/elevator_signorigin_%s' % originId) + backgroundGeom = geom.find('**/ElevatorFrameFront_%d' % originId) + backgroundGeom.node().setEffect(DecalEffect.make()) + signText = DirectGui.OnscreenText(text=TextEncoder.upper(TTLocalizer.GlobalStreetNames[mintId][-1]), font=ToontownGlobals.getSuitFont(), scale=TTLocalizer.DMEEsignText, fg=(0.87, 0.87, 0.87, 1), mayChange=False, parent=backgroundGeom) + signText.setPosHpr(locator, 0, 0, 0, 0, 0, 0) + signText.setDepthWrite(0) + + def setupElevator(self): + self.elevatorModel = loader.loadModel('phase_10/models/cogHQ/mintElevator') + self.elevatorModel.reparentTo(render) + self.leftDoor = self.elevatorModel.find('**/left_door') + self.rightDoor = self.elevatorModel.find('**/right_door') + DistributedElevator.DistributedElevator.setupElevator(self) + self.elevatorSphereNodePath.setY(-1.42) + + def getElevatorModel(self): + return self.elevatorModel + + def setBldgDoId(self, bldgDoId): + self.bldg = None + self.setupElevator() + return + + def getZoneId(self): + return 0 + + def __doorsClosed(self, zoneId): + pass + + def setMintInteriorZone(self, zoneId): + if self.localToonOnBoard: + hoodId = self.cr.playGame.hood.hoodId + mintId = self.mintId + if bboard.has('mintIdOverride'): + mintId = bboard.get('mintIdOverride') + doneStatus = {'loader': 'cogHQLoader', + 'where': 'mintInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'mintId': self.mintId, + 'hoodId': hoodId} + self.cr.playGame.getPlace().elevator.signalDone(doneStatus) + + def setMintInteriorZoneForce(self, zoneId): + place = self.cr.playGame.getPlace() + if place: + place.fsm.request('elevator', [self]) + hoodId = self.cr.playGame.hood.hoodId + mintId = self.mintId + if bboard.has('mintIdOverride'): + mintId = bboard.get('mintIdOverride') + doneStatus = {'loader': 'cogHQLoader', + 'where': 'mintInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'mintId': self.mintId, + 'hoodId': hoodId} + if hasattr(place, 'elevator') and place.elevator: + place.elevator.signalDone(doneStatus) + else: + self.notify.warning("setMintInteriorZoneForce: Couldn't find playGame.getPlace().elevator, zoneId: %s" % zoneId) + else: + self.notify.warning("setMintInteriorZoneForce: Couldn't find playGame.getPlace(), zoneId: %s" % zoneId) + + def rejectBoard(self, avId, reason = 0): + DistributedElevatorExt.DistributedElevatorExt.rejectBoard(self, avId, reason) + + def __handleRejectAck(self): + doneStatus = self.rejectDialog.doneStatus + if doneStatus != 'ok': + self.notify.error('Unrecognized doneStatus: ' + str(doneStatus)) + doneStatus = {'where': 'reject'} + self.cr.playGame.getPlace().elevator.signalDone(doneStatus) + self.rejectDialog.cleanup() + del self.rejectDialog + + def getDestName(self): + if self.mintId == ToontownGlobals.CashbotMintIntA: + return TTLocalizer.ElevatorCashBotMint0 + elif self.mintId == ToontownGlobals.CashbotMintIntB: + return TTLocalizer.ElevatorCashBotMint1 + elif self.mintId == ToontownGlobals.CashbotMintIntC: + return TTLocalizer.ElevatorCashBotMint2 diff --git a/toontown/coghq/DistributedMintElevatorExtAI.py b/toontown/coghq/DistributedMintElevatorExtAI.py new file mode 100755 index 00000000..568f61ca --- /dev/null +++ b/toontown/coghq/DistributedMintElevatorExtAI.py @@ -0,0 +1,57 @@ +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from toontown.building.ElevatorConstants import * +from toontown.building import DistributedElevatorExtAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task +import CogDisguiseGlobals + +class DistributedMintElevatorExtAI(DistributedElevatorExtAI.DistributedElevatorExtAI): + + def __init__(self, air, bldg, mintId): + DistributedElevatorExtAI.DistributedElevatorExtAI.__init__(self, air, bldg) + self.mintId = mintId + self.cogDept = ToontownGlobals.cogHQZoneId2deptIndex(self.mintId) + self.type = ELEVATOR_MINT + self.countdownTime = ElevatorData[self.type]['countdown'] + + def getMintId(self): + return self.mintId + + def avIsOKToBoard(self, av): + if not DistributedElevatorExtAI.DistributedElevatorExtAI.avIsOKToBoard(self, av): + return False + return True + + def elevatorClosed(self): + numPlayers = self.countFullSeats() + if numPlayers > 0: + players = [] + for i in self.seats: + if i not in [None, 0]: + players.append(i) + + mintZone = self.bldg.createMint(self.mintId, players) + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.sendUpdateToAvatarId(avId, 'setMintInteriorZone', [mintZone]) + self.clearFullNow(seatIndex) + + else: + self.notify.warning('The elevator left, but was empty.') + self.fsm.request('closed') + return + + def enterClosed(self): + DistributedElevatorExtAI.DistributedElevatorExtAI.enterClosed(self) + self.fsm.request('opening') + + def sendAvatarsToDestination(self, avIdList): + if len(avIdList) > 0: + mintZone = self.bldg.createMint(self.mintId, avIdList) + for avId in avIdList: + if avId: + self.sendUpdateToAvatarId(avId, 'setMintInteriorZoneForce', [mintZone]) diff --git a/toontown/coghq/DistributedMintRoom.py b/toontown/coghq/DistributedMintRoom.py new file mode 100755 index 00000000..f720f90e --- /dev/null +++ b/toontown/coghq/DistributedMintRoom.py @@ -0,0 +1,210 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from panda3d.core import * +import random + +import FactoryEntityCreator +import MintRoomBase, MintRoom +import MintRoomSpecs +from otp.level import DistributedLevel +from otp.level import LevelSpec, LevelConstants +from otp.nametag.NametagConstants import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase.ToontownGlobals import * + +def getMintRoomReadyPostName(doId): + return 'mintRoomReady-%s' % doId + + +class DistributedMintRoom(DistributedLevel.DistributedLevel, MintRoomBase.MintRoomBase, MintRoom.MintRoom): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintRoom') + EmulateEntrancePoint = False + + def __init__(self, cr): + DistributedLevel.DistributedLevel.__init__(self, cr) + MintRoomBase.MintRoomBase.__init__(self) + MintRoom.MintRoom.__init__(self) + self.suitIds = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.suitsInitialized = 0 + self.goonClipPlanes = {} + self.mint = None + return + + def createEntityCreator(self): + return FactoryEntityCreator.FactoryEntityCreator(level=self) + + def generate(self): + self.notify.debug('generate') + DistributedLevel.DistributedLevel.generate(self) + + def delete(self): + del self.mint + DistributedLevel.DistributedLevel.delete(self) + MintRoom.MintRoom.delete(self) + self.ignoreAll() + + def setMintId(self, mintId): + self.notify.debug('mintId: %s' % mintId) + MintRoomBase.MintRoomBase.setMintId(self, mintId) + + def setRoomId(self, roomId): + self.notify.debug('roomId: %s' % roomId) + MintRoomBase.MintRoomBase.setRoomId(self, roomId) + + def setRoomNum(self, num): + self.notify.debug('roomNum: %s' % num) + MintRoom.MintRoom.setRoomNum(self, num) + + def levelAnnounceGenerate(self): + self.notify.debug('levelAnnounceGenerate') + DistributedLevel.DistributedLevel.levelAnnounceGenerate(self) + specModule = MintRoomSpecs.getMintRoomSpecModule(self.roomId) + roomSpec = LevelSpec.LevelSpec(specModule) + DistributedLevel.DistributedLevel.initializeLevel(self, roomSpec) + + def getReadyPostName(self): + return getMintRoomReadyPostName(self.doId) + + def privGotSpec(self, levelSpec): + DistributedLevel.DistributedLevel.privGotSpec(self, levelSpec) + MintRoom.MintRoom.enter(self) + self.acceptOnce('leavingMint', self.announceLeaving) + bboard.post(self.getReadyPostName()) + + def fixupLevelModel(self): + MintRoom.MintRoom.setGeom(self, self.geom) + MintRoom.MintRoom.initFloorCollisions(self) + + def setMint(self, mint): + self.mint = mint + + def setBossConfronted(self, avId): + self.mint.setBossConfronted(avId) + + def setDefeated(self): + self.notify.info('setDefeated') + from toontown.coghq import DistributedMint + messenger.send(DistributedMint.DistributedMint.WinEvent) + + def initVisibility(self, *args, **kw): + pass + + def shutdownVisibility(self, *args, **kw): + pass + + def lockVisibility(self, *args, **kw): + pass + + def unlockVisibility(self, *args, **kw): + pass + + def enterZone(self, *args, **kw): + pass + + def updateVisibility(self, *args, **kw): + pass + + def setVisibility(self, *args, **kw): + pass + + def resetVisibility(self, *args, **kw): + pass + + def handleVisChange(self, *args, **kw): + pass + + def forceSetZoneThisFrame(self, *args, **kw): + pass + + def getParentTokenForEntity(self, entId): + return 1000000 * self.roomNum + entId + + def enterLtNotPresent(self): + MintRoom.MintRoom.enterLtNotPresent(self) + self.ignore('f2') + + def enterLtPresent(self): + MintRoom.MintRoom.enterLtPresent(self) + if self.mint is not None: + self.mint.currentRoomName = MintRoomSpecs.CashbotMintRoomId2RoomName[self.roomId] + + def printPos(self = self): + thisZone = self.getZoneNode(LevelConstants.UberZoneEntId) + pos = base.localAvatar.getPos(thisZone) + h = base.localAvatar.getH(thisZone) + roomName = MintRoomSpecs.CashbotMintRoomId2RoomName[self.roomId] + print 'mint pos: %s, h: %s, room: %s' % (repr(pos), h, roomName) + if self.mint is not None: + floorNum = self.mint.floorNum + else: + floorNum = '???' + posStr = 'X: %.3f' % pos[0] + '\nY: %.3f' % pos[1] + '\nZ: %.3f' % pos[2] + '\nH: %.3f' % h + '\nmintId: %s' % self.mintId + '\nfloor: %s' % floorNum + '\nroomId: %s' % self.roomId + '\nroomName: %s' % roomName + base.localAvatar.setChatAbsolute(posStr, CFThought | CFTimeout) + return + + self.accept('f2', printPos) + return + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def disable(self): + self.notify.debug('disable') + MintRoom.MintRoom.exit(self) + if hasattr(self, 'suits'): + del self.suits + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + bboard.remove(self.getReadyPostName()) + DistributedLevel.DistributedLevel.disable(self) + + def setSuits(self, suitIds, reserveSuitIds): + oldSuitIds = list(self.suitIds) + self.suitIds = suitIds + self.reserveSuitIds = reserveSuitIds + + def reservesJoining(self): + pass + + def getCogSpec(self, cogId): + cogSpecModule = MintRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.CogData[cogId] + + def getReserveCogSpec(self, cogId): + cogSpecModule = MintRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.ReserveCogData[cogId] + + def getBattleCellSpec(self, battleCellId): + cogSpecModule = MintRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.BattleCells[battleCellId] + + def getFloorOuchLevel(self): + return 8 + + def getTaskZoneId(self): + return self.mintId + + def getBossTaunt(self): + return TTLocalizer.MintBossTaunt + + def getBossBattleTaunt(self): + return TTLocalizer.MintBossBattleTaunt + + def __str__(self): + if hasattr(self, 'roomId'): + return '%s %s: %s' % (self.__class__.__name__, self.roomId, MintRoomSpecs.CashbotMintRoomId2RoomName[self.roomId]) + else: + return 'DistributedMintRoom' + + def __repr__(self): + return str(self) diff --git a/toontown/coghq/DistributedMintRoomAI.py b/toontown/coghq/DistributedMintRoomAI.py new file mode 100755 index 00000000..db88c9ad --- /dev/null +++ b/toontown/coghq/DistributedMintRoomAI.py @@ -0,0 +1,131 @@ +from otp.level import DistributedLevelAI, LevelSpec +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from otp.level import LevelSpec +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals +from toontown.coghq import FactoryEntityCreatorAI, MintRoomSpecs +from toontown.coghq import MintRoomBase, LevelSuitPlannerAI +from toontown.coghq import DistributedMintBattleAI +from toontown.suit import DistributedMintSuitAI + +class DistributedMintRoomAI(DistributedLevelAI.DistributedLevelAI, MintRoomBase.MintRoomBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintRoomAI') + + def __init__(self, air, mintId, mintDoId, zoneId, roomId, roomNum, avIds, battleExpAggreg): + DistributedLevelAI.DistributedLevelAI.__init__(self, air, zoneId, 0, avIds) + MintRoomBase.MintRoomBase.__init__(self) + self.setMintId(mintId) + self.setRoomId(roomId) + self.roomNum = roomNum + self.mintDoId = mintDoId + self.battleExpAggreg = battleExpAggreg + + def createEntityCreator(self): + return FactoryEntityCreatorAI.FactoryEntityCreatorAI(level=self) + + def getBattleCreditMultiplier(self): + return ToontownBattleGlobals.getMintCreditMultiplier(self.mintId) + + def generate(self): + self.notify.debug('generate %s: room=%s' % (self.doId, self.roomId)) + self.notify.debug('loading spec') + specModule = MintRoomSpecs.getMintRoomSpecModule(self.roomId) + roomSpec = LevelSpec.LevelSpec(specModule) + self.notify.debug('creating entities') + DistributedLevelAI.DistributedLevelAI.generate(self, roomSpec) + self.notify.debug('creating cogs') + cogSpecModule = MintRoomSpecs.getCogSpecModule(self.roomId) + self.planner = LevelSuitPlannerAI.LevelSuitPlannerAI(self.air, self, DistributedMintSuitAI.DistributedMintSuitAI, DistributedMintBattleAI.DistributedMintBattleAI, cogSpecModule.CogData, cogSpecModule.ReserveCogData, cogSpecModule.BattleCells, battleExpAggreg=self.battleExpAggreg) + suitHandles = self.planner.genSuits() + messenger.send('plannerCreated-' + str(self.doId)) + self.suits = suitHandles['activeSuits'] + self.reserveSuits = suitHandles['reserveSuits'] + self.d_setSuits() + self.notify.debug('finish mint room %s %s creation' % (self.roomId, self.doId)) + + def delete(self): + self.notify.debug('delete: %s' % self.doId) + suits = self.suits + for reserve in self.reserveSuits: + suits.append(reserve[0]) + + self.planner.destroy() + del self.planner + for suit in suits: + if not suit.isDeleted(): + suit.factoryIsGoingDown() + suit.requestDelete() + + del self.battleExpAggreg + DistributedLevelAI.DistributedLevelAI.delete(self, deAllocZone=False) + + def getMintId(self): + return self.mintId + + def getRoomId(self): + return self.roomId + + def getRoomNum(self): + return self.roomNum + + def getCogLevel(self): + return self.cogLevel + + def d_setSuits(self): + self.sendUpdate('setSuits', [self.getSuits(), self.getReserveSuits()]) + + def getSuits(self): + suitIds = [] + for suit in self.suits: + suitIds.append(suit.doId) + + return suitIds + + def getReserveSuits(self): + suitIds = [] + for suit in self.reserveSuits: + suitIds.append(suit[0].doId) + + return suitIds + + def d_setBossConfronted(self, toonId): + if toonId not in self.avIdList: + self.notify.warning('d_setBossConfronted: %s not in list of participants' % toonId) + return + self.sendUpdate('setBossConfronted', [toonId]) + + def setVictors(self, victorIds): + activeVictors = [] + activeVictorIds = [] + for victorId in victorIds: + toon = self.air.doId2do.get(victorId) + if toon is not None: + activeVictors.append(toon) + activeVictorIds.append(victorId) + + description = '%s|%s' % (self.mintId, activeVictorIds) + for avId in activeVictorIds: + self.air.writeServerEvent('mintDefeated', avId, description) + + for toon in activeVictors: + simbase.air.questManager.toonDefeatedMint(toon, self.mintId) + + def b_setDefeated(self): + self.d_setDefeated() + self.setDefeated() + + def d_setDefeated(self): + self.sendUpdate('setDefeated') + + def setDefeated(self): + pass + + def allToonsGone(self, toonsThatCleared): + DistributedLevelAI.DistributedLevelAI.allToonsGone(self, toonsThatCleared) + if self.roomNum == 0: + mint = simbase.air.doId2do.get(self.mintDoId) + if mint is not None: + mint.allToonsGone() + else: + self.notify.warning('no mint %s in allToonsGone' % self.mintDoId) + return diff --git a/toontown/coghq/DistributedMoleField.py b/toontown/coghq/DistributedMoleField.py new file mode 100755 index 00000000..a4b1f2fd --- /dev/null +++ b/toontown/coghq/DistributedMoleField.py @@ -0,0 +1,472 @@ +from panda3d.core import * +from otp.level.BasicEntities import DistributedNodePathEntity +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import MoleHill +from toontown.coghq import MoleFieldBase +from direct.distributed.ClockDelta import globalClockDelta +from toontown.toonbase import ToontownTimer +from direct.gui.DirectGui import DGG, DirectFrame, DirectLabel +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.task import Task +import random +from toontown.minigame import Trajectory +from direct.interval.IntervalGlobal import * +from toontown.battle import MovieUtil + +class DistributedMoleField(DistributedNodePathEntity, MoleFieldBase.MoleFieldBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMoleField') + ScheduleTaskName = 'moleFieldScheduler' + + def __init__(self, cr): + DistributedNodePathEntity.__init__(self, cr) + self.gameStarted = False + self.moleHills = [] + self.numMolesWhacked = 0 + self.timer = None + self.frame2D = None + self.isToonInRange = 0 + self.detectCount = 0 + self.cameraHold = None + self.activeField = 1 + self.dimensionX = 0.0 + self.dimensionY = 0.0 + self.gameText = TTLocalizer.MolesInstruction + self.winText = TTLocalizer.MolesFinished + self.pityWinText = TTLocalizer.MolesPityWin + self.restartedText = TTLocalizer.MolesRestarted + self.toonHitTracks = {} + self.hasRestarted = 0 + self.hasEntered = 0 + self.MolesWhackedTarget = 1000 + self.GameDuration = 1000 + return + + def disable(self): + self.cleanupTimer() + for ival in self.toonHitTracks.values(): + ival.finish() + + self.toonHitTracks = {} + DistributedNodePathEntity.disable(self) + taskMgr.remove(self.detectName) + self.ignoreAll() + + def delete(self): + self.soundBomb = None + self.soundBomb2 = None + self.soundCog = None + DistributedNodePathEntity.delete(self) + self.stopScheduleTask() + for mole in self.moleHills: + mole.destroy() + + self.moleHills = [] + self.cleanupTimer() + self.unloadGui() + return + + def announceGenerate(self): + DistributedNodePathEntity.announceGenerate(self) + self.loadModel() + self.loadGui() + self.detectName = 'moleField %s' % self.doId + taskMgr.doMethodLater(0.1, self.__detect, self.detectName) + self.calcDimensions() + self.notify.debug('announceGenerate doId=%d entId=%d' % (self.doId, self.entId)) + + def setNumSquaresX(self, num): + self.numSquaresX = num + self.calcDimensions() + + def setNumSquaresY(self, num): + self.numSquaresY = num + self.calcDimensions() + + def setSpacingX(self, num): + self.spacingX = num + self.calcDimensions() + + def setSpacingY(self, num): + self.spacingY = num + self.calcDimensions() + + def calcDimensions(self): + self.dimensionX = self.numSquaresX * self.spacingX + self.dimensionY = self.numSquaresY * self.spacingY + self.centerCenterNode() + + def loadModel(self): + moleIndex = 0 + self.moleHills = [] + for indexY in xrange(self.numSquaresY): + for indexX in xrange(self.numSquaresX): + xPos = indexX * self.spacingX + yPos = indexY * self.spacingY + newMoleHill = MoleHill.MoleHill(xPos, yPos, 0, self, moleIndex) + newMoleHill.reparentTo(self) + self.moleHills.append(newMoleHill) + moleIndex += 1 + + self.numMoles = len(self.moleHills) + self.centerNode = self.attachNewNode('center') + self.centerCenterNode() + self.soundBomb = base.loadSfx('phase_12/audio/sfx/Mole_Surprise.ogg') + self.soundBomb2 = base.loadSfx('phase_3.5/audio/dial/AV_pig_howl.ogg') + self.soundCog = base.loadSfx('phase_12/audio/sfx/Mole_Stomp.ogg') + self.soundUp = base.loadSfx('phase_4/audio/sfx/MG_Tag_C.ogg') + self.soundDown = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg') + upInterval = SoundInterval(self.soundUp, loop=0) + downInterval = SoundInterval(self.soundDown, loop=0) + self.soundIUpDown = Sequence(upInterval, downInterval) + + def centerCenterNode(self): + self.centerNode.setPos(self.dimensionX * 0.5, self.dimensionY * 0.5, 0.0) + + def loadGui(self): + self.frame2D = DirectFrame(scale=1.0, pos=(0.0, 0, 0.9), relief=DGG.FLAT, parent=aspect2d, frameSize=(-0.3, + 0.3, + -0.05, + 0.05), frameColor=(0.737, 0.573, 0.345, 0.3)) + self.scoreLabel = DirectLabel(parent=self.frame2D, relief=None, pos=(0, 0, 0), scale=1.0, text='', text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text_scale=0.075, text_pos=(0, -0.02)) + self.updateGuiScore() + self.frame2D.hide() + return + + def unloadGui(self): + self.frame2D.destroy() + self.frame2D = None + return + + def setGameStart(self, timestamp, molesWhackTarget, totalTime): + self.GameDuration = totalTime + self.MolesWhackedTarget = molesWhackTarget + self.activeField = 1 + self.isToonInRange = 0 + self.scheduleMoles() + self.notify.debug('%d setGameStart: Starting game' % self.doId) + self.gameStartTime = globalClockDelta.networkToLocalTime(timestamp) + self.gameStarted = True + for hill in self.moleHills: + hill.setGameStartTime(self.gameStartTime) + + curGameTime = self.getCurrentGameTime() + timeLeft = self.GameDuration - curGameTime + self.cleanupTimer() + self.timer = ToontownTimer.ToontownTimer() + self.timer.posBelowTopRightCorner() + self.timer.setTime(timeLeft) + self.timer.countdown(timeLeft, self.timerExpired) + self.startScheduleTask() + self.frame2D.show() + if self.hasRestarted: + self.level.countryClub.showInfoText(self.restartedText) + self.sendUpdate('damageMe', []) + else: + self.hasRestarted = 1 + self.updateGuiScore() + + def local2GameTime(self, timestamp): + return timestamp - self.gameStartTime + + def game2LocalTime(self, timestamp): + return timestamp + self.gameStartTime + + def getCurrentGameTime(self): + return self.local2GameTime(globalClock.getFrameTime()) + + def startScheduleTask(self): + taskMgr.add(self.scheduleTask, self.ScheduleTaskName) + + def stopScheduleTask(self): + taskMgr.remove(self.ScheduleTaskName) + + def scheduleTask(self, task): + curTime = self.getCurrentGameTime() + while self.schedule and self.schedule[0][0] <= curTime and self.activeField: + popupInfo = self.schedule[0] + self.schedule = self.schedule[1:] + startTime, moleIndex, curMoveUpTime, curStayUpTime, curMoveDownTime, moleType = popupInfo + hill = self.moleHills[moleIndex] + hill.doMolePop(startTime, curMoveUpTime, curStayUpTime, curMoveDownTime, moleType) + + if self.schedule: + return task.cont + else: + return task.done + + def handleEnterHill(self, colEntry): + if not self.gameStarted: + self.notify.debug('sending clientTriggered for %d' % self.doId) + self.sendUpdate('setClientTriggered', []) + + def handleEnterMole(self, colEntry): + if not self.gameStarted: + self.notify.debug('sending clientTriggered for %d' % self.doId) + self.sendUpdate('setClientTriggered', []) + surfaceNormal = colEntry.getSurfaceNormal(render) + self.notify.debug('surfaceNormal=%s' % surfaceNormal) + into = colEntry.getIntoNodePath() + moleIndex = int(into.getName().split('-')[-1]) + self.notify.debug('hit mole %d' % moleIndex) + moleHill = self.moleHills[moleIndex] + moleHill.stashMoleCollision() + popupNum = moleHill.getPopupNum() + if moleHill.hillType == MoleFieldBase.HILL_MOLE: + timestamp = globalClockDelta.getFrameNetworkTime() + moleHill.setHillType(MoleFieldBase.HILL_WHACKED) + self.sendUpdate('whackedBomb', [moleIndex, popupNum, timestamp]) + self.__showToonHitByBomb(localAvatar.doId, moleIndex, timestamp) + elif moleHill.hillType == MoleFieldBase.HILL_BOMB: + moleHill.setHillType(MoleFieldBase.HILL_COGWHACKED) + self.soundCog.play() + self.sendUpdate('whackedMole', [moleIndex, popupNum]) + + def updateMole(self, moleIndex, status): + if status == self.WHACKED: + moleHill = self.moleHills[moleIndex] + if not moleHill.hillType == MoleFieldBase.HILL_COGWHACKED: + moleHill.setHillType(MoleFieldBase.HILL_COGWHACKED) + self.soundCog.play() + moleHill.doMoleDown() + + def updateGuiScore(self): + molesLeft = self.MolesWhackedTarget - self.numMolesWhacked + if self.frame2D and hasattr(self, 'scoreLabel') and molesLeft >= 0: + newText = TTLocalizer.MolesLeft % molesLeft + self.scoreLabel['text'] = newText + + def setScore(self, score): + self.notify.debug('score=%d' % score) + self.numMolesWhacked = score + self.updateGuiScore() + molesLeft = self.MolesWhackedTarget - self.numMolesWhacked + if molesLeft == 0: + self.gameWon() + + def cleanupTimer(self): + if self.timer: + self.timer.stop() + self.timer.destroy() + self.timer = None + return + + def timerExpired(self): + self.cleanupTimer() + self.cleanDetect() + + def gameWon(self): + for hill in self.moleHills: + hill.forceMoleDown() + + self.cleanupTimer() + self.frame2D.hide() + self.level.countryClub.showInfoText(self.winText) + self.cleanDetect() + + def setPityWin(self): + for hill in self.moleHills: + hill.forceMoleDown() + + self.cleanupTimer() + self.frame2D.hide() + self.level.countryClub.showInfoText(self.pityWinText) + self.cleanDetect() + + def cleanDetect(self): + self.activeField = 0 + self.doToonOutOfRange() + + def __detect(self, task): + distance = self.centerNode.getDistance(localAvatar) + greaterDim = self.dimensionX + if self.dimensionY > self.dimensionX: + greaterDim = self.gridScaleY + self.detectCount += 1 + if self.detectCount > 5: + self.detectCount = 0 + if distance < greaterDim * 0.75: + if not self.isToonInRange: + self.doToonInRange() + elif self.isToonInRange: + self.doToonOutOfRange() + taskMgr.doMethodLater(0.1, self.__detect, self.detectName) + return Task.done + + def doToonInRange(self): + if not self.gameStarted: + self.notify.debug('sending clientTriggered for %d' % self.doId) + self.sendUpdate('setClientTriggered', []) + self.isToonInRange = 1 + if self.activeField: + self.setUpCamera() + if not self.hasEntered: + self.level.countryClub.showInfoText(self.gameText) + self.hasEntered = 1 + + def setUpCamera(self): + camHeight = base.localAvatar.getClampedAvatarHeight() + heightScaleFactor = camHeight * 0.3333333333 + defLookAt = Point3(0.0, 1.5, camHeight) + cameraPoint = Point3(0.0, -22.0 * heightScaleFactor, camHeight + 12.0) + base.localAvatar.setIdealCameraPos(cameraPoint) + + def doToonOutOfRange(self): + self.isToonInRange = 0 + base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex) + self.cameraHold = None + return + + def reportToonHitByBomb(self, avId, moleIndex, timestamp): + if avId != localAvatar.doId: + self.__showToonHitByBomb(avId, moleIndex, timestamp) + moleHill = self.moleHills[moleIndex] + if not moleHill.hillType == MoleFieldBase.HILL_WHACKED: + moleHill.setHillType(MoleFieldBase.HILL_WHACKED) + self.soundCog.play() + moleHill.doMoleDown() + + def __showToonHitByBomb(self, avId, moleIndex, timestamp = 0): + toon = base.cr.doId2do.get(avId) + moleHill = self.moleHills[moleIndex] + if toon == None: + return + rng = random.Random(timestamp) + curPos = toon.getPos(render) + oldTrack = self.toonHitTracks.get(avId) + if oldTrack: + if oldTrack.isPlaying(): + oldTrack.finish() + toon.setPos(curPos) + toon.setZ(self.getZ()) + parentNode = render.attachNewNode('mazeFlyToonParent-' + `avId`) + parentNode.setPos(toon.getPos(render)) + toon.reparentTo(parentNode) + toon.setPos(0, 0, 0) + startPos = parentNode.getPos() + dropShadow = toon.dropShadow.copyTo(parentNode) + dropShadow.setScale(toon.dropShadow.getScale(render)) + trajectory = Trajectory.Trajectory(0, Point3(0, 0, 0), Point3(0, 0, 50), gravMult=1.0) + flyDur = trajectory.calcTimeOfImpactOnPlane(0.0) + endTile = [rng.randint(0, self.numSquaresX - 1), rng.randint(0, self.numSquaresY - 1)] + endWorldCoords = (self.getX(render) + endTile[0] * self.spacingX, self.getY(render) + endTile[1] * self.spacingY) + endPos = Point3(endWorldCoords[0], endWorldCoords[1], startPos[2]) + + def flyFunc(t, trajectory, startPos = startPos, endPos = endPos, dur = flyDur, moveNode = parentNode, flyNode = toon): + u = t / dur + moveNode.setX(startPos[0] + u * (endPos[0] - startPos[0])) + moveNode.setY(startPos[1] + u * (endPos[1] - startPos[1])) + if flyNode and not flyNode.isEmpty(): + flyNode.setPos(trajectory.getPos(t)) + + def safeSetHpr(node, hpr): + if node and not node.isEmpty(): + node.setHpr(hpr) + + flyTrack = Sequence(LerpFunctionInterval(flyFunc, fromData=0.0, toData=flyDur, duration=flyDur, extraArgs=[trajectory]), name=toon.uniqueName('hitBySuit-fly')) + if avId != localAvatar.doId: + cameraTrack = Sequence() + else: + base.localAvatar.stopUpdateSmartCamera() + self.camParentHold = camera.getParent() + self.camParent = base.localAvatar.attachNewNode('iCamParent') + self.camParent.setPos(self.camParentHold.getPos()) + self.camParent.setHpr(self.camParentHold.getHpr()) + camera.reparentTo(self.camParent) + self.camParent.reparentTo(parentNode) + startCamPos = camera.getPos() + destCamPos = camera.getPos() + zenith = trajectory.getPos(flyDur / 2.0)[2] + destCamPos.setZ(zenith * 1.3) + destCamPos.setY(destCamPos[1] * 0.3) + + def camTask(task, zenith = zenith, flyNode = toon, startCamPos = startCamPos, camOffset = destCamPos - startCamPos): + u = flyNode.getZ() / zenith + camera.lookAt(toon) + return Task.cont + + camTaskName = 'mazeToonFlyCam-' + `avId` + taskMgr.add(camTask, camTaskName, priority=20) + + def cleanupCamTask(self = self, toon = toon, camTaskName = camTaskName, startCamPos = startCamPos): + taskMgr.remove(camTaskName) + self.camParent.reparentTo(toon) + camera.setPos(startCamPos) + camera.lookAt(toon) + camera.reparentTo(self.camParentHold) + base.localAvatar.startUpdateSmartCamera() + self.setUpCamera() + + cameraTrack = Sequence(Wait(flyDur), Func(cleanupCamTask), name='hitBySuit-cameraLerp') + geomNode = toon.getGeomNode() + startHpr = geomNode.getHpr() + destHpr = Point3(startHpr) + hRot = rng.randrange(1, 8) + if rng.choice([0, 1]): + hRot = -hRot + destHpr.setX(destHpr[0] + hRot * 360) + spinHTrack = Sequence(LerpHprInterval(geomNode, flyDur, destHpr, startHpr=startHpr), Func(safeSetHpr, geomNode, startHpr), name=toon.uniqueName('hitBySuit-spinH')) + parent = geomNode.getParent() + rotNode = parent.attachNewNode('rotNode') + geomNode.reparentTo(rotNode) + rotNode.setZ(toon.getHeight() / 2.0) + oldGeomNodeZ = geomNode.getZ() + geomNode.setZ(-toon.getHeight() / 2.0) + startHpr = rotNode.getHpr() + destHpr = Point3(startHpr) + pRot = rng.randrange(1, 3) + if rng.choice([0, 1]): + pRot = -pRot + destHpr.setY(destHpr[1] + pRot * 360) + spinPTrack = Sequence(LerpHprInterval(rotNode, flyDur, destHpr, startHpr=startHpr), Func(safeSetHpr, rotNode, startHpr), name=toon.uniqueName('hitBySuit-spinP')) + soundTrack = Sequence() + + def preFunc(self = self, avId = avId, toon = toon, dropShadow = dropShadow): + forwardSpeed = toon.forwardSpeed + rotateSpeed = toon.rotateSpeed + if avId == localAvatar.doId: + toon.stopSmooth() + base.cr.playGame.getPlace().fsm.request('stopped') + else: + toon.stopSmooth() + if forwardSpeed or rotateSpeed: + toon.setSpeed(forwardSpeed, rotateSpeed) + toon.dropShadow.hide() + + def postFunc(self = self, avId = avId, oldGeomNodeZ = oldGeomNodeZ, dropShadow = dropShadow, parentNode = parentNode): + if avId == localAvatar.doId: + base.localAvatar.setPos(endPos) + if hasattr(self, 'orthoWalk'): + self.orthoWalk.start() + dropShadow.removeNode() + del dropShadow + if toon and toon.dropShadow: + toon.dropShadow.show() + geomNode = toon.getGeomNode() + rotNode = geomNode.getParent() + baseNode = rotNode.getParent() + geomNode.reparentTo(baseNode) + rotNode.removeNode() + del rotNode + geomNode.setZ(oldGeomNodeZ) + toon.reparentTo(render) + toon.setPos(endPos) + parentNode.removeNode() + del parentNode + if avId == localAvatar.doId: + toon.startSmooth() + place = base.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.fsm.request('walk') + else: + toon.startSmooth() + + preFunc() + hitTrack = Sequence(Func(toon.setPos, Point3(0.0, 0.0, 0.0)), Wait(0.25), Parallel(flyTrack, cameraTrack, self.soundIUpDown, spinHTrack, spinPTrack, soundTrack), Func(postFunc), name=toon.uniqueName('hitBySuit')) + self.toonHitTracks[avId] = hitTrack + hitTrack.start() + posM = moleHill.getPos(render) + posN = Point3(posM[0], posM[1], posM[2] + 4.0) + self.soundBomb.play() + self.soundBomb2.play() + return diff --git a/toontown/coghq/DistributedMoleFieldAI.py b/toontown/coghq/DistributedMoleFieldAI.py new file mode 100755 index 00000000..90501deb --- /dev/null +++ b/toontown/coghq/DistributedMoleFieldAI.py @@ -0,0 +1,123 @@ +from otp.level import DistributedEntityAI +from toontown.coghq import MoleFieldBase +from direct.distributed.ClockDelta import globalClockDelta +from direct.directnotify import DirectNotifyGlobal + +class DistributedMoleFieldAI(DistributedEntityAI.DistributedEntityAI, MoleFieldBase.MoleFieldBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMoleFieldAI') + + def __init__(self, level, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.whackedMoles = {} + self.numMolesWhacked = 0 + self.roundsFailed = 0 + self.started = 0 + self.challengeDefeated = False + + def announceGenerate(self): + DistributedEntityAI.DistributedEntityAI.announceGenerate(self) + self.numMoles = self.numSquaresX * self.numSquaresY + self.moleFieldEndTimeTaskName = self.uniqueName('moleFieldEndTime') + self.GameDuration = self.timeToPlay + numToons = 0 + if hasattr(self, 'level'): + numToons = len(self.level.presentAvIds) + self.moleTarget = self.molesBase + self.molesPerPlayer * numToons + + def delete(self): + DistributedEntityAI.DistributedEntityAI.delete(self) + self.removeAllTasks() + + def setClientTriggered(self): + if not hasattr(self, 'gameStartTime'): + self.gameStartTime = globalClock.getRealTime() + if not self.started: + self.b_setGameStart(globalClockDelta.localToNetworkTime(self.gameStartTime), self.moleTarget, self.timeToPlay) + self.started = 1 + + def b_setGameStart(self, timestamp, moleTarget, timeToPlay): + self.d_setGameStart(timestamp, moleTarget, timeToPlay) + self.setGameStart(timestamp) + + def d_setGameStart(self, timestamp, moleTarget, timeToPlay): + self.notify.debug('BASE: Sending setGameStart') + self.sendUpdate('setGameStart', [timestamp, moleTarget, timeToPlay]) + + def setGameStart(self, timestamp): + self.GameDuration = self.timeToPlay + self.notify.debug('BASE: setGameStart') + self.prepareForGameStartOrRestart() + + def prepareForGameStartOrRestart(self): + self.GameDuration = self.timeToPlay + self.scheduleMoles() + self.whackedMoles = {} + self.doMethodLater(self.timeToPlay, self.gameEndingTimeHit, self.moleFieldEndTimeTaskName) + + def whackedMole(self, moleIndex, popupNum): + validMoleWhack = False + if moleIndex in self.whackedMoles: + if self.whackedMoles[moleIndex] < popupNum: + validMoleWhack = True + else: + self.whackedMoles[moleIndex] = popupNum + validMoleWhack = True + if validMoleWhack: + self.numMolesWhacked += 1 + self.sendUpdate('updateMole', [moleIndex, self.WHACKED]) + self.sendUpdate('setScore', [self.numMolesWhacked]) + self.checkForTargetReached() + + def whackedBomb(self, moleIndex, popupNum, timestamp): + senderId = self.air.getAvatarIdFromSender() + self.sendUpdate('reportToonHitByBomb', [senderId, moleIndex, timestamp]) + + def checkForTargetReached(self): + if self.numMolesWhacked >= self.moleTarget: + if not self.challengeDefeated: + self.forceChallengeDefeated() + + def forceChallengeDefeated(self, pityWin = False): + self.challengeDefeated = True + self.removeTask(self.moleFieldEndTimeTaskName) + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + if room: + self.challengeDefeated = True + room.challengeDefeated() + eventName = self.getOutputEventName() + messenger.send(eventName, [1]) + if pityWin: + self.sendUpdate('setPityWin') + + def gameEndingTimeHit(self, task): + if self.numMolesWhacked < self.moleTarget and self.roundsFailed < 4: + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + self.roundsFailed += 1 + self.restartGame() + elif self.roundsFailed >= 4: + if not self.challengeDefeated: + self.forceChallengeDefeated(pityWin=True) + + def damageMe(self): + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + if not room: + return + senderId = self.air.getAvatarIdFromSender() + av = simbase.air.doId2do.get(senderId) + avIds = room.presentAvIds + if av and senderId in avIds: + av.takeDamage(self.DamageOnFailure, quietly=0) + room.sendUpdate('forceOuch', [self.DamageOnFailure]) + + def restartGame(self): + if not hasattr(self, 'entId'): + return + self.gameStartTime = globalClock.getRealTime() + self.started = 0 + self.b_setGameStart(globalClockDelta.localToNetworkTime(self.gameStartTime), self.moleTarget, self.timeToPlay) + + def getScore(self): + return self.numMolesWhacked diff --git a/toontown/coghq/DistributedMover.py b/toontown/coghq/DistributedMover.py new file mode 100755 index 00000000..6100432e --- /dev/null +++ b/toontown/coghq/DistributedMover.py @@ -0,0 +1,125 @@ +from math import * +import math +import random + +from StomperGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from direct.interval.IntervalGlobal import * +from direct.showbase.PythonUtil import lerp +from direct.task import Task +from otp.level import BasicEntities +from panda3d.core import * +from pandac.PandaModules import NodePath + + +class DistributedMover(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMover') + laserFieldModels = ['phase_9/models/cogHQ/square_stomper'] + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.attachedEnt = None + self.oldParent = None + self.entity2Move = None + self.moveTarget = None + self.pos0Wait = 1.0 + self.pos0Move = 1.0 + self.pos1Wait = 1.0 + self.pos1Move = 1.0 + self.moverIval = None + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.loadModel() + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + taskMgr.remove(self.taskName) + BasicEntities.DistributedNodePathEntity.disable(self) + + def delete(self): + self.notify.debug('delete') + if self.moverIval: + self.moverIval.finish() + self.unloadModel() + if self.taskName: + taskMgr.remove(self.taskName) + BasicEntities.DistributedNodePathEntity.delete(self) + + def loadModel(self): + self.moverNode = self.attachNewNode('mover') + self.rotateNode = self.attachNewNode('rotate') + self.model = None + if self.entity2Move: + self.setEntity2Move(self.entity2Move) + self.taskName = 'moverUpdate %s' % self.doId + return + + def unloadModel(self): + if self.model: + self.model.removeNode() + del self.model + self.model = None + return + + def setEntity2Move(self, entId): + self.entity2Move = entId + if entId: + ent = self.level.getEntity(entId) + if ent: + if self.attachedEnt and not self.attachedEnt.isEmpty(): + self.attachedEnt.reparentTo(self.oldParent) + self.oldParent = ent.getParent() + ent.reparentTo(self.moverNode) + self.attachedEnt = ent + + def startMove(self, timeStamp): + currentTime = ClockDelta.globalClockDelta.getRealNetworkTime() + timeDiff = (currentTime - timeStamp) / 1000.0 + target = self.level.getEntity(self.moveTarget) + if not target: + return + okay2Play = 1 + if self.moverIval: + self.moverIval.finish() + if self.moverIval.isPlaying(): + okay2Play = 0 + if okay2Play and self.moveTarget: + childList = self.getChildren() + for child in childList: + if child != self.moverNode: + child.reparentTo(self.moverNode) + + timeLag = 0.0 + timeJump = self.pos0Move - timeDiff + if timeJump < 0 or self.cycleType in 'linear': + timeJump = self.pos0Move + timeLag = timeDiff + myBlend = 'easeInOut' + if self.cycleType in 'linear': + myBlend = 'noBlend' + self.moverIval = Sequence() + firstIVal = LerpPosHprInterval(self.moverNode, timeJump, Vec3(target.getPos(self)[0], target.getPos(self)[1], target.getPos(self)[2]), Vec3(target.getHpr(self)[0], target.getHpr(self)[1], target.getHpr(self)[2]), blendType=myBlend, fluid=1) + self.moverIval.append(firstIVal) + if self.cycleType in 'linear': + for linearCycle in xrange(10): + self.moverIval.append(firstIVal) + + if self.cycleType != 'oneWay': + self.moverIval.append(Wait(self.pos1Wait)) + self.moverIval.append(LerpPosHprInterval(self.moverNode, self.pos1Move, Vec3(0, 0, 0), Vec3(0, 0, 0), blendType=myBlend, fluid=1)) + if self.cycleType == 'loop': + self.moverIval.append(Wait(self.pos0Wait)) + self.moverIval.start() + self.moverIval.setT(timeLag) diff --git a/toontown/coghq/DistributedMoverAI.py b/toontown/coghq/DistributedMoverAI.py new file mode 100755 index 00000000..aa099c8b --- /dev/null +++ b/toontown/coghq/DistributedMoverAI.py @@ -0,0 +1,107 @@ +import random + +from direct.directnotify import DirectNotifyGlobal +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from direct.interval.IntervalGlobal import * +from direct.task import Task +from otp.ai.AIBase import * +from otp.level import BasicEntities +from otp.level import DistributedEntityAI +from toontown.coghq import BattleBlockerAI +from toontown.coghq import LaserGameMineSweeper +from toontown.coghq import LaserGameRoll + + +class DistributedMoverAI(DistributedEntityAI.DistributedEntityAI, NodePath, BasicEntities.NodePathAttribs): + def __init__(self, level, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + node = hidden.attachNewNode('DistributedMoverAI') + NodePath.__init__(self, node) + if not hasattr(self, 'switchId'): + self.switchId = 0 + if not hasattr(self, 'pos0Wait'): + self.pos0Wait = 1.0 + if not hasattr(self, 'pos0Move'): + self.pos0Move = 1.0 + if not hasattr(self, 'pos1Wait'): + self.pos1Wait = 1.0 + if not hasattr(self, 'pos1Move'): + self.pos1Move = 1.0 + if not hasattr(self, 'startOn'): + self.startOn = 0 + if not hasattr(self, 'cycleType'): + self.cycleType = 'return' + self.moveTime = {} + self.setTimes() + self.oK2Play = 1 + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + if self.switchId != 0: + self.accept(self.getOutputEventName(self.switchId), self.reactToSwitch) + + self.timerName = 'mover %s' % self.doId + self.setPos(self.pos) + self.setHpr(self.hpr) + self.setTimes() + if self.startOn: + self.sendMove() + + def delete(self): + taskMgr.remove(self.timerName) + self.ignoreAll() + DistributedEntityAI.DistributedEntityAI.delete(self) + + def destroy(self): + self.notify.info('destroy entity(laserField) %s' % self.entId) + DistributedEntityAI.DistributedEntityAI.destroy(self) + + def reactToSwitch(self, on): + if on: + self.sendMove() + + def setPos0Move(self, time): + self.pos0Move = time + self.setTimes() + + def setPos1Move(self, time): + self.pos1Move = time + self.setTimes() + + def setPos0Wait(self, time): + self.pos0Wait = time + self.setTimes() + + def setPos1Wait(self, time): + self.pos1Wait = time + self.setTimes() + + def setTimes(self): + self.moveTime = { } + self.moveTime['return'] = self.pos0Move + self.pos1Wait + self.pos1Move + self.moveTime['loop'] = self.pos0Wait + self.pos0Move + self.pos1Wait + self.pos1Move + self.moveTime['oneWay'] = self.pos0Move + self.moveTime['linear'] = self.pos0Move * 8 + + def setCycleType(self, type): + self.cycleType = type + self.oK2Play = 1 + + def setStartOn(self, on): + self.startOn = on + self.sendMove() + + def sendMove(self): + timeStamp = ClockDelta.globalClockDelta.getRealNetworkTime() + if self.oK2Play: + self.sendUpdate('startMove', [timeStamp]) + taskMgr.doMethodLater(self.moveTime[self.cycleType], self._DistributedMoverAI__resetTimer, self.timerName) + self.oK2Play = 0 + + def _DistributedMoverAI__resetTimer(self, taskMgrFooler = 1): + if not self.cycleType == 'oneWay': + self.oK2Play = 1 + if self.cycleType in ('loop', 'linear') or self.startOn: + self.sendMove() + return Task.done diff --git a/toontown/coghq/DistributedSecurityCamera.py b/toontown/coghq/DistributedSecurityCamera.py new file mode 100755 index 00000000..a4780a64 --- /dev/null +++ b/toontown/coghq/DistributedSecurityCamera.py @@ -0,0 +1,388 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from StomperGlobals import * +from direct.distributed import ClockDelta +from direct.showbase.PythonUtil import lerp +import math +from otp.level import DistributedEntity +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath +from otp.level import BasicEntities +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from toontown.coghq import BattleBlocker +import random +from math import * + +def circleX(angle, radius, centerX, centerY): + x = radius * cos(angle) + centerX + return x + + +def circleY(angle, radius, centerX, centerY): + y = radius * sin(angle) + centerY + return y + + +def getCirclePoints(segCount, centerX, centerY, radius, wideX = 1.0, wideY = 1.0): + returnShape = [] + for seg in xrange(0, int(segCount)): + coordX = wideX * circleX(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + + coordX = wideX * circleX(pi * 2.0 * float(0 / segCount), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(0 / segCount), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + return returnShape + + +class DistributedSecurityCamera(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSecurityCamera') + laserFieldModels = ['phase_9/models/cogHQ/square_stomper'] + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + node = hidden.attachNewNode('DistributedNodePathEntity') + self.trackBeamGN = None + self.trackFloorGN = None + self.trackX = 10.0 + self.trackY = -5.0 + self.radius = 5.0 + self.trackShape = [] + self.trackShape = getCirclePoints(7, 0.0, 0.0, self.radius) + self.trackShapeFloor = [] + self.trackShapeFloor = getCirclePoints(16, 0.0, 0.0, self.radius) + self.zFloat = 0.05 + self.projector = Point3(0, 0, 25) + self.isToonIn = 0 + self.toonX = 0 + self.toonY = 0 + self.canDamage = 1 + self.accel = 0.5 + self.maxVel = 1.0 + self.vX = 0.0 + self.vY = 0.0 + self.targetX = self.trackX + self.targetY = self.trackY + self.target = 0 + self.trackTargetList = [None, + None, + None, + None] + self.lastTime = 0.0 + self.currentTime = 0.0 + self.delta = 0.0 + self.Norm = {} + self.Norm['Red'] = 0.2 + self.Norm['Green'] = 0.2 + self.Norm['Blue'] = 0.2 + self.Norm['Alpha'] = 1.0 + self.Alert = {} + self.Alert['Red'] = 1.0 + self.Alert['Green'] = 0.0 + self.Alert['Blue'] = 0.0 + self.Alert['Alpha'] = 1.0 + self.attackSound = loader.loadSfx('phase_9/audio/sfx/CHQ_GOON_tractor_beam_alarmed.ogg') + self.onSound = loader.loadSfx('phase_11/audio/sfx/LB_camera_shutter_2.ogg') + self.attackTrack = Parallel(SoundInterval(self.attackSound, node=self, volume=0.8), SoundInterval(self.onSound, node=self, volume=0.8)) + self.moveStartSound = loader.loadSfx('phase_11/audio/sfx/LB_laser_beam_on_2.ogg') + self.moveStartTrack = Parallel(SoundInterval(self.moveStartSound, node=self, volume=0.4)) + self.moveLoopSound = loader.loadSfx('phase_11/audio/sfx/LB_laser_beam_hum_2.ogg') + self.moveLoopSound.setLoop() + self.moveLoopTrack = Parallel(SoundInterval(self.moveLoopSound, node=self, volume=0.4)) + self.moveStopSound = loader.loadSfx('phase_11/audio/sfx/LB_laser_beam_off_2.ogg') + self.moveStopTrack = Parallel(SoundInterval(self.moveStopSound, node=self, volume=0.4)) + self.taskName = None + return + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.trackBeamNode = self.attachNewNode('tracking Beam Node') + self.trackBeamGN = GeomNode('tracking Beam') + self.trackBeamNode.attachNewNode(self.trackBeamGN) + self.trackBeamNode.setTransparency(TransparencyAttrib.MAlpha) + self.trackBeamNode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.trackBeamNode.setTwoSided(False) + self.trackBeamNode.setDepthWrite(False) + self.trackFloorNode = self.attachNewNode('tracking floor Node') + self.trackFloorGN = GeomNode('tracking Floor') + self.trackFloorNode.attachNewNode(self.trackFloorGN) + self.trackFloorNode.setTransparency(TransparencyAttrib.MAlpha) + self.trackFloorNode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.trackFloorNode.setTwoSided(False) + self.trackFloorNode.setDepthWrite(False) + if not hasattr(self, 'trackTarget1'): + self.trackTarget1 = None + else: + self.trackTargetList[1] = self.trackTarget1 + if not hasattr(self, 'trackTarget2'): + self.trackTarget2 = None + else: + self.trackTargetList[2] = self.trackTarget2 + if not hasattr(self, 'trackTarget3'): + self.trackTarget3 = None + else: + self.trackTargetList[3] = self.trackTarget3 + self.loadModel() + self.detectName = 'securityCamera %s' % self.doId + return + + def initCollisionGeom(self): + pass + + def sendFail(self): + self.battleStart() + self.sendUpdate('trapFire', []) + + def battleStart(self): + pass + + def setTarget(self, targetHash): + targetCount = targetHash % 3 + sanity = 10 + self.target = None + while targetCount >= 0 and sanity > 0: + sanity -= 1 + for index in xrange(1, 4): + if self.trackTargetList[index]: + if targetCount == 0: + self.target = self.trackTargetList[index] + targetCount -= 1 + + return + + def setTrackTarget1(self, targetId): + self.trackTarget1 = targetId + self.trackTargetList[1] = targetId + + def setTrackTarget2(self, targetId): + self.trackTarget2 = targetId + self.trackTargetList[2] = targetId + + def setTrackTarget3(self, targetId): + self.trackTarget3 = targetId + self.trackTargetList[3] = targetId + + def __detect(self, task): + distance = self.getDistance(localAvatar) + greaterDim = self.gridScaleX + if self.gridScaleY > self.gridScaleX: + greaterDim = self.gridScaleY + if distance < greaterDim * 1.6: + if localAvatar.getPos(self)[0] > 0 and localAvatar.getPos(self)[0] < self.gridScaleX and localAvatar.getPos(self)[1] > 0 and localAvatar.getPos(self)[1] < self.gridScaleY: + self.__toonHit() + else: + if self.isToonIn: + self.isToonIn = 0 + self.isToonIn = 0 + taskMgr.doMethodLater(0.1, self.__detect, self.detectName) + return Task.done + + def __toonHit(self): + posX = localAvatar.getPos(self)[0] + posY = localAvatar.getPos(self)[1] + tileX = int(posX / (self.gridScaleX / self.gridNumX)) + tileY = int(posY / (self.gridScaleY / self.gridNumY)) + if self.toonX != tileX or self.toonY != tileY or not self.isToonIn: + self.toonX = tileX + self.toonY = tileY + self.isToonIn = 1 + if self.gridData[tileX][tileY] < len(self.gridSymbols): + tileFunction = self.gridSymbols[self.gridData[tileX][tileY]][0] + if tileFunction: + tileFunction() + self.sendHit() + self.isToonIn = 1 + + def sendHit(self): + self.sendUpdate('hit', [self.toonX, self.toonY]) + + def disable(self): + self.notify.debug('disable') + if self.moveLoopTrack.isPlaying(): + self.moveLoopTrack.finish() + if self.attackTrack.isPlaying(): + self.attackTrack.finish() + if self.moveStartTrack.isPlaying(): + self.moveStartTrack.finish() + if self.moveStopTrack.isPlaying(): + self.moveStopTrack.finish() + self.ignoreAll() + taskMgr.remove(self.detectName) + BasicEntities.DistributedNodePathEntity.disable(self) + + def delete(self): + self.notify.debug('delete') + self.unloadModel() + if self.taskName: + taskMgr.remove(self.taskName) + BasicEntities.DistributedNodePathEntity.delete(self) + + def loadModel(self): + self.rotateNode = self.attachNewNode('rotate') + self.model = loader.loadModel(self.laserFieldModels[self.modelPath]) + self.model.reparentTo(self.rotateNode) + self.model.setPos(0, 1, 0) + self.taskName = 'securityCameraupdate %s' % self.doId + taskMgr.add(self.__updateTrack, self.taskName, priority=25) + + def unloadModel(self): + if self.model: + self.model.removeNode() + del self.model + self.model = None + return + + def __updateTrack(self, task): + if self.target and self.level and hasattr(self.level, 'entities'): + thing = self.level.entities.get(self.target, None) + self.targetX = thing.getPos(self)[0] + self.targetY = thing.getPos(self)[1] + else: + return Task.cont + self.rotateNode.setPos(self.projector) + self.rotateNode.lookAt(Point3(self.trackX, self.trackY, 0.0)) + dt = globalClock.getDt() + deccel = 1.0 - 1.0 * (dt * 7.0) + if deccel < 0: + deccel = 0.0 + dirX = 0.0 + dirY = 0.0 + distX = self.targetX - self.trackX + distY = self.targetY - self.trackY + trigDist = math.sqrt(distX * distX + distY * distY) + totalDist = abs(distX) + abs(distY) + propX = abs(distX) / (totalDist + 0.01) + propY = abs(distY) / (totalDist + 0.01) + if self.targetX != self.trackX: + dirX = distX / abs(distX) + if self.targetY != self.trackY: + dirY = distY / abs(distY) + if trigDist < self.radius * 0.5 + 1.0: + self.vX = self.vX * deccel + self.vY = self.vY * deccel + self.moveStopTrack.start() + self.moveLoopTrack.finish() + else: + if not self.moveLoopTrack.isPlaying(): + self.moveLoopTrack.start() + self.moveStartTrack.start() + self.vX += dirX * self.accel * propX + self.vY += dirY * self.accel * propY + if self.vX > self.maxVel: + self.vX = self.maxVel + if self.vX < -self.maxVel: + self.vX = -self.maxVel + if self.vY > self.maxVel: + self.vY = self.maxVel + if self.vY < -self.maxVel: + self.vY = -self.maxVel + self.trackX += self.vX * dt + self.trackY += self.vY * dt + self.genTrack() + dist = self.getDist(base.localAvatar) + if dist < self.radius and self.canDamage: + self.canDamage = 0 + self.sendUpdate('trapFire', []) + taskMgr.doMethodLater(2.0, self._resetDam, 'reset Damage') + self.attackTrack.start() + return Task.cont + + def _resetDam(self, task = None): + self.canDamage = 1 + + def getDist(self, thing): + posTX = thing.getX() + posTY = thing.getY() + dx = thing.getPos(self)[0] - self.trackX + dy = thing.getPos(self)[1] - self.trackY + return sqrt(dx * dx + dy * dy) + + def genTrack(self): + dist = self.getDist(base.localAvatar) + draw = 1.0 / (0.01 + float(pow(dist, 0.4))) + self.trackShape = [] + wideX = 1 + wideY = 1 + self.trackShape = getCirclePoints(5 + draw * 12.0, 0.0, 0.0, self.radius, wideX, wideY) + self.trackShapeFloor = [] + self.trackShapeFloor = getCirclePoints(5 + draw * 50.0, 0.0, 0.0, self.radius, wideX, wideY) + if self.trackBeamGN: + self.trackBeamGN.removeAllGeoms() + if self.trackFloorGN: + self.trackFloorGN.removeAllGeoms() + beamRed = 0.0 + beamGreen = 0.0 + beamBlue = 0.0 + beamAlpha = 1.0 + origin = {} + origin['Red'] = 0.2 + origin['Green'] = 0.2 + origin['Blue'] = 0.2 + origin['Alpha'] = 1.0 + if self.canDamage: + origin = self.Norm + else: + origin = self.Alert + self.gFormat = GeomVertexFormat.getV3cp() + self.trackBeamVertexData = GeomVertexData('holds my vertices', self.gFormat, Geom.UHDynamic) + self.trackBeamVertexWriter = GeomVertexWriter(self.trackBeamVertexData, 'vertex') + self.trackBeamColorWriter = GeomVertexWriter(self.trackBeamVertexData, 'color') + self.trackFloorVertexData = GeomVertexData('holds my vertices', self.gFormat, Geom.UHDynamic) + self.trackFloorVertexWriter = GeomVertexWriter(self.trackFloorVertexData, 'vertex') + self.trackFloorColorWriter = GeomVertexWriter(self.trackFloorVertexData, 'color') + self.trackBeamVertexWriter.addData3f(self.projector[0], self.projector[1], self.projector[2]) + self.trackBeamColorWriter.addData4f(origin['Red'], origin['Green'], origin['Blue'], origin['Alpha']) + self.trackFloorVertexWriter.addData3f(self.trackX, self.trackY, self.zFloat) + self.trackFloorColorWriter.addData4f(origin['Red'], origin['Green'], origin['Blue'], origin['Alpha']) + for vertex in self.trackShape: + self.trackBeamVertexWriter.addData3f(self.trackX + vertex[0], self.trackY + vertex[1], self.zFloat) + self.trackBeamColorWriter.addData4f(beamRed, beamGreen, beamBlue, beamAlpha) + + for vertex in self.trackShapeFloor: + self.trackFloorVertexWriter.addData3f(self.trackX + vertex[0], self.trackY + vertex[1], self.zFloat) + self.trackFloorColorWriter.addData4f(origin['Red'], origin['Green'], origin['Blue'], origin['Alpha']) + + self.trackBeamTris = GeomTrifans(Geom.UHStatic) + self.trackFloorTris = GeomTrifans(Geom.UHStatic) + sizeTrack = len(self.trackShape) + self.trackBeamTris.addVertex(0) + for countVertex in xrange(1, sizeTrack + 1): + self.trackBeamTris.addVertex(countVertex) + + self.trackBeamTris.addVertex(1) + self.trackBeamTris.closePrimitive() + self.trackBeamGeom = Geom(self.trackBeamVertexData) + self.trackBeamGeom.addPrimitive(self.trackBeamTris) + self.trackBeamGN.addGeom(self.trackBeamGeom) + sizeTrack = len(self.trackShapeFloor) + self.trackFloorTris.addVertex(0) + for countVertex in xrange(1, sizeTrack + 1): + self.trackFloorTris.addVertex(countVertex) + + self.trackFloorTris.addVertex(1) + self.trackFloorTris.closePrimitive() + self.trackFloorGeom = Geom(self.trackFloorVertexData) + self.trackFloorGeom.addPrimitive(self.trackFloorTris) + self.trackFloorGN.addGeom(self.trackFloorGeom) + + def setProjector(self, projPoint): + self.projector = projPoint + if self.trackBeamGN and self.trackFloorGN: + self.genTrack() + + def setHideModel(self, flag): + if flag: + self.model.stash() + else: + self.model.unstash() diff --git a/toontown/coghq/DistributedSecurityCameraAI.py b/toontown/coghq/DistributedSecurityCameraAI.py new file mode 100755 index 00000000..d042a2da --- /dev/null +++ b/toontown/coghq/DistributedSecurityCameraAI.py @@ -0,0 +1,88 @@ +import random + +from direct.directnotify import DirectNotifyGlobal +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from direct.interval.IntervalGlobal import * +from direct.task import Task +from otp.ai.AIBase import * +from otp.level import BasicEntities +from otp.level import DistributedEntityAI +from toontown.coghq import BattleBlockerAI +from toontown.coghq import LaserGameMineSweeper +from toontown.coghq import LaserGameRoll + + +class DistributedSecurityCameraAI(DistributedEntityAI.DistributedEntityAI, NodePath, BasicEntities.NodePathAttribs): + def __init__(self, level, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + node = hidden.attachNewNode('DistributedSecurityCameraAI') + NodePath.__init__(self, node) + if not hasattr(self, 'switchId'): + self.switchId = 0 + + if not hasattr(self, 'damPow'): + self.damPow = 1 + + self.enabled = 1 + self.detectName = None + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + if self.switchId != 0: + self.accept(self.getOutputEventName(self.switchId), self.reactToSwitch) + + self.detectName = 'laserField %s' % self.doId + taskMgr.doMethodLater(3.0, self._DistributedSecurityCameraAI__detect, self.detectName) + self.setPos(self.pos) + self.setHpr(self.hpr) + + def delete(self): + if self.detectName: + taskMgr.remove(self.detectName) + + self.ignoreAll() + DistributedEntityAI.DistributedEntityAI.delete(self) + + def destroy(self): + self.notify.info('destroy entity(laserField) %s' % self.entId) + DistributedEntityAI.DistributedEntityAI.destroy(self) + + def _DistributedSecurityCameraAI__detect(self, task): + isThereAnyToons = False + if hasattr(self, 'level'): + toonInRange = 0 + for avId in self.level.presentAvIds: + if avId in self.air.doId2do: + av = self.air.doId2do[avId] + isThereAnyToons = True + distance = self.getDistance(av) + continue + + if isThereAnyToons: + randTime = float(random.randint(1, 6)) * 0.5 + taskMgr.doMethodLater(randTime, self._DistributedSecurityCameraAI__detect, self.detectName) + randTarget = random.randint(0, 100) + self.sendUpdate('setTarget', [ + randTarget]) + + + return Task.done + + def hit(self, hitX, hitY): + if self.enabled: + self.game.hit(hitX, hitY) + + def reactToSwitch(self, on): + if on: + self.trapDisable() + self.game.win() + + def trapFire(self): + avId = self.air.getAvatarIdFromSender() + toon = self.air.doId2do[avId] + if toon: + toon.takeDamage(self.damPow) + + def trapDisable(self): + self.enabled = 0 diff --git a/toontown/coghq/DistributedSellbotHQDoor.py b/toontown/coghq/DistributedSellbotHQDoor.py new file mode 100755 index 00000000..fc17ca13 --- /dev/null +++ b/toontown/coghq/DistributedSellbotHQDoor.py @@ -0,0 +1,24 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import DistributedCogHQDoor +from toontown.toonbase import TTLocalizer +import CogDisguiseGlobals + +class DistributedSellbotHQDoor(DistributedCogHQDoor.DistributedCogHQDoor): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSellbotHQDoor') + + def __init__(self, cr): + DistributedCogHQDoor.DistributedCogHQDoor.__init__(self, cr) + + def informPlayer(self, suitType): + self.notify.debugStateCall(self) + if suitType == CogDisguiseGlobals.suitTypes.NoSuit: + popupMsg = TTLocalizer.SellbotRentalSuitMessage + elif suitType == CogDisguiseGlobals.suitTypes.NoMerits: + popupMsg = TTLocalizer.SellbotCogSuitNoMeritsMessage + elif suitType == CogDisguiseGlobals.suitTypes.FullSuit: + popupMsg = TTLocalizer.SellbotCogSuitHasMeritsMessage + else: + popupMsg = TTLocalizer.FADoorCodes_SB_DISGUISE_INCOMPLETE + localAvatar.elevatorNotifier.showMeWithoutStopping(popupMsg, pos=(0, 0, 0.26), ttDialog=True) + localAvatar.elevatorNotifier.setOkButton() + localAvatar.elevatorNotifier.doneButton.setZ(-0.3) diff --git a/toontown/coghq/DistributedSellbotHQDoorAI.py b/toontown/coghq/DistributedSellbotHQDoorAI.py new file mode 100755 index 00000000..0bfb895c --- /dev/null +++ b/toontown/coghq/DistributedSellbotHQDoorAI.py @@ -0,0 +1,8 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq.DistributedCogHQDoorAI import DistributedCogHQDoorAI + +class DistributedSellbotHQDoorAI(DistributedCogHQDoorAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedSellbotHQDoorAI") + + def informPlayer(self, todo0): + pass diff --git a/toontown/coghq/DistributedSinkingPlatform.py b/toontown/coghq/DistributedSinkingPlatform.py new file mode 100755 index 00000000..70a235fe --- /dev/null +++ b/toontown/coghq/DistributedSinkingPlatform.py @@ -0,0 +1,132 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from otp.level import BasicEntities +import MovingPlatform +from direct.distributed import DistributedObject +import SinkingPlatformGlobals +from direct.directnotify import DirectNotifyGlobal + +class DistributedSinkingPlatform(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSinkingPlatform') + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.moveIval = None + return + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + self.fsm = ClassicFSM.ClassicFSM('DistributedSinkingPlatform', [State.State('off', self.enterOff, self.exitOff, ['sinking']), State.State('sinking', self.enterSinking, self.exitSinking, ['rising']), State.State('rising', self.enterRising, self.exitRising, ['sinking', 'off'])], 'off', 'off') + self.fsm.enterInitialState() + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.loadModel() + self.accept(self.platform.getEnterEvent(), self.localToonEntered) + self.accept(self.platform.getExitEvent(), self.localToonLeft) + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + self.fsm.requestFinalState() + BasicEntities.DistributedNodePathEntity.disable(self) + + def delete(self): + self.notify.debug('delete') + self.ignoreAll() + if self.moveIval: + self.moveIval.pause() + del self.moveIval + self.platform.destroy() + del self.platform + BasicEntities.DistributedNodePathEntity.delete(self) + + def loadModel(self): + self.notify.debug('loadModel') + model = loader.loadModel('phase_9/models/cogHQ/platform1') + self.platform = MovingPlatform.MovingPlatform() + self.platform.setupCopyModel(self.getParentToken(), model, 'platformcollision') + self.platform.reparentTo(self) + self.platform.setPos(0, 0, 0) + + def localToonEntered(self): + ts = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.sendUpdate('setOnOff', [1, ts]) + + def localToonLeft(self): + ts = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.sendUpdate('setOnOff', [0, ts]) + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + self.notify.debug('exitOff') + + def enterSinking(self, ts = 0): + self.notify.debug('enterSinking') + self.startMoving(SinkingPlatformGlobals.SINKING, ts) + + def exitSinking(self): + self.notify.debug('exitSinking') + if self.moveIval: + self.moveIval.pause() + del self.moveIval + self.moveIval = None + return + + def enterRising(self, ts = 0): + self.notify.debug('enterRising') + self.startMoving(SinkingPlatformGlobals.RISING, ts) + + def exitRising(self): + self.notify.debug('exitRising') + if self.moveIval: + self.moveIval.pause() + del self.moveIval + self.moveIval = None + return + + def setSinkMode(self, avId, mode, ts): + self.notify.debug('setSinkMode %s' % mode) + if mode == SinkingPlatformGlobals.OFF: + self.fsm.requestInitialState() + elif mode == SinkingPlatformGlobals.RISING: + self.fsm.request('rising', [ts]) + elif mode == SinkingPlatformGlobals.SINKING: + self.fsm.request('sinking', [ts]) + + def startMoving(self, direction, ts): + if direction == SinkingPlatformGlobals.RISING: + endPos = Vec3(0, 0, 0) + pause = self.pauseBeforeRise + duration = self.riseDuration + else: + endPos = Vec3(0, 0, -self.verticalRange) + pause = None + duration = self.sinkDuration + startT = globalClockDelta.networkToLocalTime(ts, bits=32) + curT = globalClock.getFrameTime() + ivalTime = curT - startT + if ivalTime < 0: + ivalTime = 0 + elif ivalTime > duration: + ivalTime = duration + duration = duration - ivalTime + duration = max(0.0, duration) + moveNode = self.platform + self.moveIval = Sequence() + if pause is not None: + self.moveIval.append(WaitInterval(pause)) + self.moveIval.append(LerpPosInterval(moveNode, duration, endPos, startPos=moveNode.getPos(), blendType='easeInOut', name='%s-move' % self.platform.name, fluid=1)) + self.moveIval.start() + return diff --git a/toontown/coghq/DistributedSinkingPlatformAI.py b/toontown/coghq/DistributedSinkingPlatformAI.py new file mode 100755 index 00000000..cb33c456 --- /dev/null +++ b/toontown/coghq/DistributedSinkingPlatformAI.py @@ -0,0 +1,24 @@ +from otp.ai.AIBase import * +from direct.directnotify import DirectNotifyGlobal +from otp.level import DistributedEntityAI +import SinkingPlatformGlobals + +class DistributedSinkingPlatformAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSinkingPlatformAI') + + def __init__(self, levelDoId, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, levelDoId, entId) + self.numStanding = 0 + + def setOnOff(self, on, timestamp): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setOnOff %s' % on) + if on: + self.numStanding += 1 + else: + self.numStanding -= 1 + self.notify.debug('numStanding = %s' % self.numStanding) + if self.numStanding > 0: + self.sendUpdate('setSinkMode', [avId, SinkingPlatformGlobals.SINKING, timestamp]) + else: + self.sendUpdate('setSinkMode', [avId, SinkingPlatformGlobals.RISING, timestamp]) diff --git a/toontown/coghq/DistributedStage.py b/toontown/coghq/DistributedStage.py new file mode 100755 index 00000000..1f44ec69 --- /dev/null +++ b/toontown/coghq/DistributedStage.py @@ -0,0 +1,306 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import BulletinBoardWatcher +from otp.otpbase import OTPGlobals +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase import TTLocalizer +from direct.gui import OnscreenText +from toontown.toonbase import ToontownGlobals +from toontown.coghq import DistributedStageRoom, StageLayout, StageRoom +import random +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * + +class DistributedStage(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStage') + ReadyPost = 'StageReady' + WinEvent = 'StageWinEvent' + FloorNum = 'StageFloorNum' + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.titleColor = (1, 1, 1, 1) + self.titleText = OnscreenText.OnscreenText('', fg=self.titleColor, shadow=(0, 0, 0, 1), font=ToontownGlobals.getSuitFont(), pos=(0, -0.5), scale=0.1, drawOrder=0, mayChange=1) + self.titleSequence = None + self.pendingZoneChange = 0 + return + + def generate(self): + self.notify.debug('generate: %s' % self.doId) + DistributedObject.DistributedObject.generate(self) + bboard.post('stage', self) + self.roomWatcher = None + self.geom = None + self.rooms = [] + self.hallways = [] + self.allRooms = [] + self.curToonRoomNum = None + base.localAvatar.setCameraCollisionsCanMove(1) + self.accept('SOSPanelEnter', self.handleSOSPanel) + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + + def placeToon(self): + pX = random.randint(-2, 2) + pY = random.randint(-2, 2) + base.localAvatar.reparentTo(render) + base.localAvatar.setPosHpr(pX, pY, 0, 0, 0, 0) + self.camEnterRoom(0) + + def setLayoutIndex(self, layoutIndex): + self.layoutIndex = layoutIndex + + def getLayoutIndex(self): + return self.layoutIndex + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def setStageId(self, id): + DistributedStage.notify.debug('setStageId: %s' % id) + self.stageId = id + + def setFloorNum(self, num): + DistributedStage.notify.debug('floorNum: %s' % num) + self.floorNum = num + bboard.post(DistributedStage.FloorNum, num) + self.layout = StageLayout.StageLayout(self.stageId, self.floorNum, self.layoutIndex) + + def setRoomDoIds(self, roomDoIds): + self.roomDoIds = roomDoIds + self.roomWatcher = BulletinBoardWatcher.BulletinBoardWatcher('roomWatcher-%s' % self.doId, [ DistributedStageRoom.getStageRoomReadyPostName(doId) for doId in self.roomDoIds ], self.gotAllRooms) + + def gotAllRooms(self): + self.notify.debug('stage %s: got all rooms' % self.doId) + if self.roomWatcher: + self.roomWatcher.destroy() + self.roomWatcher = None + self.geom = render.attachNewNode('stage%s' % self.doId) + for doId in self.roomDoIds: + self.rooms.append(base.cr.doId2do[doId]) + self.rooms[-1].setStage(self) + + self.notify.info('stageId %s, floor %s, %s' % (self.stageId, self.floorNum, self.rooms[0].avIdList)) + rng = self.layout.getRng() + numRooms = self.layout.getNumRooms() + for i, room in enumerate(self.rooms): + if i == 0: + room.getGeom().reparentTo(self.geom) + else: + room.attachTo(self.hallways[i - 1], rng) + self.allRooms.append(room) + self.listenForFloorEvents(room) + if i < numRooms - 1: + hallway = StageRoom.StageRoom(self.layout.getHallwayModel(i)) + hallway.attachTo(room, rng) + hallway.setRoomNum(i * 2 + 1) + hallway.initFloorCollisions() + hallway.enter() + self.hallways.append(hallway) + self.allRooms.append(hallway) + self.listenForFloorEvents(hallway) + + self.placeToon() + + def handleCameraRayFloorCollision(collEntry, self = self): + name = collEntry.getIntoNode().getName() + self.notify.debug('camera floor ray collided with: %s' % name) + prefix = StageRoom.StageRoom.FloorCollPrefix + prefixLen = len(prefix) + if name[:prefixLen] == prefix: + try: + roomNum = int(name[prefixLen:]) + except: + DistributedLevel.notify.warning('Invalid zone floor collision node: %s' % name) + else: + self.camEnterRoom(roomNum) + print collEntry + print + + self.accept('on-floor', handleCameraRayFloorCollision) + if bboard.has('stageRoom'): + self.warpToRoom(bboard.get('stageRoom')) + firstSetZoneDoneEvent = self.cr.getNextSetZoneDoneEvent() + + def handleFirstSetZoneDone(): + self.notify.debug('stageHandleFirstSetZoneDone') + bboard.post(DistributedStage.ReadyPost, self) + + self.acceptOnce(firstSetZoneDoneEvent, handleFirstSetZoneDone) + zoneList = [OTPGlobals.UberZone, self.zoneId] + for room in self.rooms: + zoneList.extend(room.zoneIds) + + base.cr.sendSetZoneMsg(self.zoneId, zoneList) + self.accept('takingScreenshot', self.handleScreenshot) + base.transitions.irisIn() + taskMgr.doMethodLater(0.25, self._delayedInit, 'delayedInit') + return + + def _delayedInit(self, taskFooler = 0): + self.camEnterRoom(0) + base.localAvatar.unpauseGlitchKiller() + try: + fsm = base.cr.playGame.getPlace().fsm + fsm.forceTransition('walk') + except: + pass + + return Task.done + + def listenForFloorEvents(self, room): + roomNum = room.getRoomNum() + floorCollName = room.getFloorCollName() + + def handleZoneEnter(collisionEntry, self = self, roomNum = roomNum): + self.toonEnterRoom(roomNum) + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + room = self.allRooms[roomNum] + ouchLevel = room.getFloorOuchLevel() + room.startOuch(ouchLevel) + + self.accept('enter%s' % floorCollName, handleZoneEnter) + + def handleZoneExit(collisionEntry, self = self, roomNum = roomNum): + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + self.allRooms[roomNum].stopOuch() + + self.accept('exit%s' % floorCollName, handleZoneExit) + + def getAllRoomsTimeout(self): + self.notify.warning('stage %s: timed out waiting for room objs' % self.doId) + + def toonEnterRoom(self, roomNum): + self.notify.debug('toonEnterRoom: %s' % roomNum) + if roomNum != self.curToonRoomNum: + if self.curToonRoomNum is not None: + self.allRooms[self.curToonRoomNum].localToonFSM.request('notPresent') + self.allRooms[roomNum].localToonFSM.request('present') + self.curToonRoomNum = roomNum + return + + def camEnterRoom(self, roomNum): + self.notify.debug('camEnterRoom: %s' % roomNum) + self.notify.info('CAMENTERROOM doID%s num%s' % (self.doId, roomNum)) + self.notify.info('av: %s, cam: %s' % (localAvatar.getPos(), camera.getPos())) + if roomNum % 2 == 1: + minVis = roomNum - 2 + maxVis = roomNum + 2 + else: + minVis = roomNum - 1 + maxVis = roomNum + 1 + for i, room in enumerate(self.allRooms): + if i < minVis or i > maxVis: + room.getGeom().stash() + else: + room.getGeom().unstash() + + def setBossConfronted(self, avId): + if avId == base.localAvatar.doId: + return + av = base.cr.identifyFriend(avId) + if av is None: + return + base.localAvatar.setSystemMessage(avId, TTLocalizer.StageBossConfrontedMsg % av.getName()) + return + + def warpToRoom(self, roomId): + for i in xrange(len(self.rooms)): + room = self.rooms[i] + if room.roomId == roomId: + break + else: + return False + + base.localAvatar.setPosHpr(room.getGeom(), 0, 0, 0, 0, 0, 0) + self.camEnterRoom(i * 2) + return True + + def disable(self): + self.notify.debug('disable') + if self.titleSequence: + self.titleSequence.finish() + self.titleSequence = None + self.ignoreAll() + if self.titleText: + self.titleText.cleanup() + self.titleText = None + for hallway in self.hallways: + hallway.exit() + + self.rooms = [] + for hallway in self.hallways: + hallway.delete() + + self.hallways = [] + self.allRooms = [] + if self.roomWatcher: + self.roomWatcher.destroy() + self.roomWatcher = None + if self.geom is not None: + self.geom.removeNode() + self.geom = None + base.localAvatar.setCameraCollisionsCanMove(0) + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.ignore('SOSPanelEnter') + bboard.remove('stage') + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.rooms[0].avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def handleScreenshot(self): + base.addScreenshotString('stageId: %s, floor (from 1): %s' % (self.stageId, self.floorNum + 1)) + if hasattr(self, 'currentRoomName'): + base.addScreenshotString('%s' % self.currentRoomName) + + def setStageZone(self, zoneId): + base.cr.sendSetZoneMsg(zoneId) + base.cr.playGame.getPlace().fsm.request('walk') + scale = base.localAvatar.getScale() + base.camera.setScale(scale) + + def showInfoText(self, text = 'hello world'): + description = text + if description and description != '': + self.titleText.setText(description) + self.titleText.setColor(Vec4(*self.titleColor)) + self.titleText.setColorScale(1, 1, 1, 1) + self.titleText.setFg(self.titleColor) + if self.titleSequence: + self.titleSequence.finish() + self.titleSequence = None + self.titleSequence = Sequence(Func(self.showTitleText), Wait(3.1), LerpColorScaleInterval(self.titleText, duration=0.5, colorScale=Vec4(1, 1, 1, 0.0)), Func(self.hideTitleText)) + self.titleSequence.start() + return + + def showTitleText(self): + if self.titleText: + self.titleText.show() + + def hideTitleText(self): + if self.titleText or 1: + self.titleText.hide() + self.titleText.setText('') + + def elevatorAlert(self, avId): + if base.localAvatar.doId != avId and avId in base.cr.doId2do: + name = base.cr.doId2do[avId].getName() + self.showInfoText(TTLocalizer.StageToonEnterElevator % name) diff --git a/toontown/coghq/DistributedStageAI.py b/toontown/coghq/DistributedStageAI.py new file mode 100755 index 00000000..e6b4953d --- /dev/null +++ b/toontown/coghq/DistributedStageAI.py @@ -0,0 +1,121 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObjectAI +from otp.level import DistributedLevelAI +from panda3d.core import * +from toontown.building import DistributedElevatorFloorAI +from toontown.coghq import BattleExperienceAggregatorAI +from toontown.coghq import StageLayout, DistributedStageRoomAI +from toontown.toonbase import ToontownGlobals + + +class DistributedStageAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageAI') + + def __init__(self, air, stageId, zoneId, floorNum, avIds, layoutIndex, battleExpAggreg = None): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.stageId = stageId + self.zoneId = zoneId + self.floorNum = floorNum + self.avIds = avIds + self.elevatorList = [] + self.setLayoutIndex(layoutIndex) + self.battleExpAggreg = battleExpAggreg + self.puzzelReward = 5 + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.notify.info('generate %s, id=%s, floor=%s' % (self.doId, self.stageId, self.floorNum)) + self.layout = StageLayout.StageLayout(self.stageId, self.floorNum, self.layoutIndex) + self.rooms = [] + if self.battleExpAggreg is None: + self.battleExpAggreg = BattleExperienceAggregatorAI.BattleExperienceAggregatorAI() + for i in xrange(self.layout.getNumRooms()): + room = DistributedStageRoomAI.DistributedStageRoomAI(self.air, self.stageId, self.doId, self.zoneId, self.layout.getRoomId(i), i * 2, self.avIds, self.battleExpAggreg) + room.generateWithRequired(self.zoneId) + self.rooms.append(room) + roomDoIds = [] + for room in self.rooms: + roomDoIds.append(room.doId) + self.sendUpdate('setRoomDoIds', [ + roomDoIds]) + self.placeElevatorsOnMarkers() + description = '%s|%s|%s' % (self.stageId, self.floorNum, self.avIds) + for avId in self.avIds: + self.air.writeServerEvent('stageEntered', avId, description) + + def requestDelete(self): + self.notify.info('requestDelete: %s' % self.doId) + if hasattr(self, 'rooms'): + for room in self.rooms: + room.requestDelete() + if hasattr(self, 'elevatorList'): + for elevator in self.elevatorList: + elevator.requestDelete() + DistributedObjectAI.DistributedObjectAI.requestDelete(self) + + def delete(self): + self.notify.info('delete: %s' % self.doId) + self.air.deallocateZone(self.zoneId) + if hasattr(self, 'elevatorList'): + del self.elevatorList + if hasattr(self, 'rooms'): + del self.rooms + if hasattr(self, 'layout'): + del self.layout + if hasattr(self, 'battleExpAggreg'): + del self.battleExpAggreg + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getTaskZoneId(self): + return self.stageId + + def placeElevatorsOnMarkers(self): + for room in self.rooms: + if room.entType2ids['elevatorMarker']: + for markerId in room.entType2ids['elevatorMarker']: + marker = room.getEntity(markerId) + newElevator = DistributedElevatorFloorAI.DistributedElevatorFloorAI(self.air, self.doId, self, self.avIds, marker.doId) + newElevator.generateWithRequired(self.zoneId) + self.elevatorList.append(newElevator) + + def allToonsGone(self): + self.notify.info('allToonsGone') + self.requestDelete() + + def getZoneId(self): + return self.zoneId + + def getStageId(self): + return self.stageId + + def getFloorNum(self): + return self.floorNum + + def setLayoutIndex(self, layoutIndex): + self.layoutIndex = layoutIndex + + def getLayoutIndex(self): + return self.layoutIndex + + def startNextFloor(self): + floor = self.floorNum + 1 + StageZone = self.air.allocateZone() + Stage = DistributedStageAI(self.air, self.stageId, StageZone, floor, self.avIds, self.layoutIndex, self.battleExpAggreg) + Stage.generateWithRequired(StageZone) + for avId in self.avIds: + self.sendUpdateToAvatarId(avId, 'setStageZone', [StageZone]) + self.requestDelete() + + def elevatorAlert(self, avId): + self.sendUpdate('elevatorAlert', [avId]) + + def increasePuzzelReward(self): + self.puzzelReward += 5 + if self.puzzelReward > 10: + self.puzzelReward = 10 + + def resetPuzzelReward(self): + self.puzzelReward = 5 + + def getPuzzelReward(self): + return self.puzzelReward diff --git a/toontown/coghq/DistributedStageBattle.py b/toontown/coghq/DistributedStageBattle.py new file mode 100755 index 00000000..178c2edc --- /dev/null +++ b/toontown/coghq/DistributedStageBattle.py @@ -0,0 +1,48 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleBase import * +from toontown.coghq import DistributedLevelBattle +from direct.directnotify import DirectNotifyGlobal +from toontown.toon import TTEmote +from otp.avatar import Emote +from toontown.battle import SuitBattleGlobals +import random +from toontown.suit import SuitDNA +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals +from otp.nametag import NametagGlobals + +class DistributedStageBattle(DistributedLevelBattle.DistributedLevelBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageBattle') + + def __init__(self, cr): + DistributedLevelBattle.DistributedLevelBattle.__init__(self, cr) + self.fsm.addState(State.State('StageReward', self.enterStageReward, self.exitStageReward, ['Resume'])) + offState = self.fsm.getStateNamed('Off') + offState.addTransition('StageReward') + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('StageReward') + + def enterStageReward(self, ts): + self.notify.debug('enterStageReward()') + self.disableCollision() + self.delayDeleteMembers() + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + if self.bossBattle: + messenger.send('localToonConfrontedStageBoss') + self.movie.playReward(ts, self.uniqueName('building-reward'), self.__handleStageRewardDone, noSkip=True) + + def __handleStageRewardDone(self): + self.notify.debug('stage reward done') + if self.hasLocalToon(): + self.d_rewardDone(base.localAvatar.doId) + self.movie.resetReward() + self.fsm.request('Resume') + + def exitStageReward(self): + self.notify.debug('exitStageReward()') + self.movie.resetReward(finish=1) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) diff --git a/toontown/coghq/DistributedStageBattleAI.py b/toontown/coghq/DistributedStageBattleAI.py new file mode 100755 index 00000000..44f3e41c --- /dev/null +++ b/toontown/coghq/DistributedStageBattleAI.py @@ -0,0 +1,65 @@ +import CogDisguiseGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.showbase.PythonUtil import addListsByValue +from toontown.battle.BattleBase import * +from toontown.coghq import DistributedLevelBattleAI +from toontown.toonbase import ToontownGlobals +from toontown.toonbase.ToontownBattleGlobals import getStageCreditMultiplier + + +class DistributedStageBattleAI(DistributedLevelBattleAI.DistributedLevelBattleAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageBattleAI') + + def __init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, roundCallback = None, finishCallback = None, maxSuits = 4): + DistributedLevelBattleAI.DistributedLevelBattleAI.__init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, 'StageReward', roundCallback, finishCallback, maxSuits) + self.battleCalc.setSkillCreditMultiplier(1) + if self.bossBattle: + self.level.d_setBossConfronted(toonId) + self.fsm.addState(State.State('StageReward', self.enterStageReward, self.exitStageReward, ['Resume'])) + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('StageReward') + + def getTaskZoneId(self): + return self.level.stageId + + def storeSuitsKilledThisBattle(self): + floor = self.level.getFloorNum() + if len(self.suitsKilledPerFloor) < floor + 1: + self.suitsKilledPerFloor.append(self.suitsKilledThisBattle) + else: + self.suitsKilledPerFloor[floor].extend(self.suitsKilledThisBattle) + + def handleToonsWon(self, toons): + extraMerits = [0, 0, 0, 0] + amount = ToontownGlobals.StageNoticeRewards[self.level.stageId] + index = ToontownGlobals.cogHQZoneId2deptIndex(self.level.stageId) + extraMerits[index] = amount + self.handleCrateReward(toons) + for toon in toons: + recovered, notRecovered = self.air.questManager.recoverItems(toon, self.suitsKilled, self.getTaskZoneId()) + self.toonItems[toon.doId][0].extend(recovered) + self.toonItems[toon.doId][1].extend(notRecovered) + meritArray = self.air.promotionMgr.recoverMerits(toon, self.suitsKilled, self.getTaskZoneId(), getStageCreditMultiplier(self.level.getFloorNum()) * 2.0, extraMerits=extraMerits, addInvasion=False) + if toon.doId in self.helpfulToons: + self.toonMerits[toon.doId] = addListsByValue(self.toonMerits[toon.doId], meritArray) + else: + self.notify.debug('toon %d not helpful list, skipping merits' % toon.doId) + + def enterStageReward(self): + self.joinableFsm.request('Unjoinable') + self.runableFsm.request('Unrunable') + self.resetResponses() + self.assignRewards() + self.bossDefeated = 1 + self.level.setVictors(self.activeToons[:]) + self.timer.startCallback(BUILDING_REWARD_TIMEOUT, self.serverRewardDone) + + def exitStageReward(self): + pass + + def enterResume(self): + DistributedLevelBattleAI.DistributedLevelBattleAI.enterResume(self) + if self.bossBattle and self.bossDefeated: + self.battleMgr.level.b_setDefeated() diff --git a/toontown/coghq/DistributedStageRoom.py b/toontown/coghq/DistributedStageRoom.py new file mode 100755 index 00000000..6cd61902 --- /dev/null +++ b/toontown/coghq/DistributedStageRoom.py @@ -0,0 +1,224 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import random +from otp.level import DistributedLevel +from direct.directnotify import DirectNotifyGlobal +import StageRoomBase, StageRoom +import FactoryEntityCreator +import StageRoomSpecs +from otp.level import LevelSpec, LevelConstants +from otp.nametag.NametagConstants import * +from toontown.toonbase import TTLocalizer + +def getStageRoomReadyPostName(doId): + return 'stageRoomReady-%s' % doId + + +class DistributedStageRoom(DistributedLevel.DistributedLevel, StageRoomBase.StageRoomBase, StageRoom.StageRoom): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageRoom') + EmulateEntrancePoint = False + + def __init__(self, cr): + DistributedLevel.DistributedLevel.__init__(self, cr) + StageRoomBase.StageRoomBase.__init__(self) + StageRoom.StageRoom.__init__(self) + self.suitIds = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.suitsInitialized = 0 + self.goonClipPlanes = {} + self.stage = None + return + + def createEntityCreator(self): + return FactoryEntityCreator.FactoryEntityCreator(level=self) + + def generate(self): + self.notify.debug('generate') + DistributedLevel.DistributedLevel.generate(self) + + def delete(self): + del self.stage + DistributedLevel.DistributedLevel.delete(self) + StageRoom.StageRoom.delete(self) + self.ignoreAll() + + def setStageId(self, stageId): + self.notify.debug('stageId: %s' % stageId) + StageRoomBase.StageRoomBase.setStageId(self, stageId) + + def setRoomId(self, roomId): + self.notify.debug('roomId: %s' % roomId) + StageRoomBase.StageRoomBase.setRoomId(self, roomId) + + def setRoomNum(self, num): + self.notify.debug('roomNum: %s' % num) + StageRoom.StageRoom.setRoomNum(self, num) + + def levelAnnounceGenerate(self): + self.notify.debug('levelAnnounceGenerate') + DistributedLevel.DistributedLevel.levelAnnounceGenerate(self) + specModule = StageRoomSpecs.getStageRoomSpecModule(self.roomId) + roomSpec = LevelSpec.LevelSpec(specModule) + DistributedLevel.DistributedLevel.initializeLevel(self, roomSpec) + + def getReadyPostName(self): + return getStageRoomReadyPostName(self.doId) + + def privGotSpec(self, levelSpec): + DistributedLevel.DistributedLevel.privGotSpec(self, levelSpec) + StageRoom.StageRoom.enter(self) + self.acceptOnce('leavingStage', self.announceLeaving) + bboard.post(self.getReadyPostName()) + + def fixupLevelModel(self): + StageRoom.StageRoom.setGeom(self, self.geom) + StageRoom.StageRoom.initFloorCollisions(self) + + def setStage(self, stage): + self.stage = stage + + def setBossConfronted(self, avId): + self.stage.setBossConfronted(avId) + + def setDefeated(self): + self.notify.info('setDefeated') + from toontown.coghq import DistributedStage + messenger.send(DistributedStage.DistributedStage.WinEvent) + + def initVisibility(self, *args, **kw): + pass + + def shutdownVisibility(self, *args, **kw): + pass + + def lockVisibility(self, *args, **kw): + pass + + def unlockVisibility(self, *args, **kw): + pass + + def enterZone(self, *args, **kw): + pass + + def updateVisibility(self, *args, **kw): + pass + + def setVisibility(self, *args, **kw): + pass + + def resetVisibility(self, *args, **kw): + pass + + def handleVisChange(self, *args, **kw): + pass + + def forceSetZoneThisFrame(self, *args, **kw): + pass + + def getParentTokenForEntity(self, entId): + return 1000000 * self.roomNum + entId + + def enterLtNotPresent(self): + StageRoom.StageRoom.enterLtNotPresent(self) + self.ignore('f2') + + def enterLtPresent(self): + StageRoom.StageRoom.enterLtPresent(self) + if self.stage is not None: + self.stage.currentRoomName = StageRoomSpecs.CashbotStageRoomId2RoomName[self.roomId] + + def printPos(self = self): + thisZone = self.getZoneNode(LevelConstants.UberZoneEntId) + pos = base.localAvatar.getPos(thisZone) + h = base.localAvatar.getH(thisZone) + roomName = StageRoomSpecs.CashbotStageRoomId2RoomName[self.roomId] + print 'stage pos: %s, h: %s, room: %s' % (repr(pos), h, roomName) + if self.stage is not None: + floorNum = self.stage.floorNum + else: + floorNum = '???' + posStr = 'X: %.3f' % pos[0] + '\nY: %.3f' % pos[1] + '\nZ: %.3f' % pos[2] + '\nH: %.3f' % h + '\nstageId: %s' % self.stageId + '\nfloor: %s' % floorNum + '\nroomId: %s' % self.roomId + '\nroomName: %s' % roomName + base.localAvatar.setChatAbsolute(posStr, CFThought | CFTimeout) + return + + self.accept('f2', printPos) + return + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def disable(self): + self.notify.debug('disable') + StageRoom.StageRoom.exit(self) + if hasattr(self, 'suits'): + del self.suits + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + bboard.remove(self.getReadyPostName()) + DistributedLevel.DistributedLevel.disable(self) + + def setSuits(self, suitIds, reserveSuitIds): + oldSuitIds = list(self.suitIds) + self.suitIds = suitIds + self.reserveSuitIds = reserveSuitIds + newSuitIds = [] + for suitId in self.suitIds: + if suitId not in oldSuitIds: + newSuitIds.append(suitId) + + if len(newSuitIds): + + def bringOutOfReserve(suits): + for suit in suits: + suit.comeOutOfReserve() + + self.relatedObjectMgrRequest = self.cr.relatedObjectMgr.requestObjects(newSuitIds, bringOutOfReserve) + + def reservesJoining(self): + pass + + def getCogSpec(self, cogId): + cogSpecModule = StageRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.CogData[cogId] + + def getReserveCogSpec(self, cogId): + cogSpecModule = StageRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.ReserveCogData[cogId] + + def getBattleCellSpec(self, battleCellId): + cogSpecModule = StageRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.BattleCells[battleCellId] + + def getFloorOuchLevel(self): + return 8 + + def getTaskZoneId(self): + return self.stageId + + def getBossTaunt(self): + return TTLocalizer.StageBossTaunt + + def getBossBattleTaunt(self): + return TTLocalizer.StageBossBattleTaunt + + def __str__(self): + if hasattr(self, 'roomId'): + return '%s %s: %s' % (self.__class__.__name__, self.roomId, StageRoomSpecs.CashbotStageRoomId2RoomName[self.roomId]) + else: + return 'DistributedStageRoom' + + def __repr__(self): + return str(self) + + def complexVis(self): + return 0 diff --git a/toontown/coghq/DistributedStageRoomAI.py b/toontown/coghq/DistributedStageRoomAI.py new file mode 100755 index 00000000..5c0ce1d5 --- /dev/null +++ b/toontown/coghq/DistributedStageRoomAI.py @@ -0,0 +1,133 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from otp.level import DistributedLevelAI, LevelSpec +from otp.level import LevelSpec +from toontown.coghq import DistributedStageBattleAI +from toontown.coghq import FactoryEntityCreatorAI, StageRoomSpecs +from toontown.coghq import StageRoomBase, LevelSuitPlannerAI +from toontown.suit import DistributedStageSuitAI +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals + + +class DistributedStageRoomAI(DistributedLevelAI.DistributedLevelAI, StageRoomBase.StageRoomBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageRoomAI') + + def __init__(self, air, stageId, stageDoId, zoneId, roomId, roomNum, avIds, battleExpAggreg): + DistributedLevelAI.DistributedLevelAI.__init__(self, air, zoneId, 0, avIds) + StageRoomBase.StageRoomBase.__init__(self) + self.setStageId(stageId) + self.setRoomId(roomId) + self.roomNum = roomNum + self.stageDoId = stageDoId + self.battleExpAggreg = battleExpAggreg + + def createEntityCreator(self): + return FactoryEntityCreatorAI.FactoryEntityCreatorAI(level = self) + + def getBattleCreditMultiplier(self): + return ToontownBattleGlobals.getStageCreditMultiplier(self.getFloorNum()) + + def getFloorNum(self): + stage = self.air.getDo(self.stageDoId) + if stage is None: + self.notify.warning('getFloorNum: could not find stage %s' % self.stageDoId) + return 0 + return stage.floorNum + + def generate(self): + self.notify.debug('generate %s: room=%s' % (self.doId, self.roomId)) + self.notify.debug('loading spec') + specModule = StageRoomSpecs.getStageRoomSpecModule(self.roomId) + roomSpec = LevelSpec.LevelSpec(specModule) + self.notify.debug('creating entities') + DistributedLevelAI.DistributedLevelAI.generate(self, roomSpec) + self.notify.debug('creating cogs') + cogSpecModule = StageRoomSpecs.getCogSpecModule(self.roomId) + self.planner = LevelSuitPlannerAI.LevelSuitPlannerAI(self.air, self, DistributedStageSuitAI.DistributedStageSuitAI, DistributedStageBattleAI.DistributedStageBattleAI, cogSpecModule.CogData, cogSpecModule.ReserveCogData, cogSpecModule.BattleCells, battleExpAggreg = self.battleExpAggreg) + suitHandles = self.planner.genSuits() + messenger.send('plannerCreated-' + str(self.doId)) + self.suits = suitHandles['activeSuits'] + self.reserveSuits = suitHandles['reserveSuits'] + self.d_setSuits() + self.notify.debug('finish stage room %s %s creation' % (self.roomId, self.doId)) + + def delete(self): + self.notify.debug('delete: %s' % self.doId) + suits = self.suits + for reserve in self.reserveSuits: + suits.append(reserve[0]) + self.planner.destroy() + del self.planner + for suit in suits: + if not suit.isDeleted(): + suit.factoryIsGoingDown() + suit.requestDelete() + del self.battleExpAggreg + DistributedLevelAI.DistributedLevelAI.delete(self, deAllocZone = False) + + def getStageId(self): + return self.stageId + + def getRoomId(self): + return self.roomId + + def getRoomNum(self): + return self.roomNum + + def getCogLevel(self): + return self.cogLevel + + def d_setSuits(self): + self.sendUpdate('setSuits', [self.getSuits(), self.getReserveSuits()]) + + def getSuits(self): + suitIds = [] + for suit in self.suits: + suitIds.append(suit.doId) + return suitIds + + def getReserveSuits(self): + suitIds = [] + for suit in self.reserveSuits: + suitIds.append(suit[0].doId) + return suitIds + + def d_setBossConfronted(self, toonId): + if toonId not in self.avIdList: + self.notify.warning('d_setBossConfronted: %s not in list of participants' % toonId) + return None + self.sendUpdate('setBossConfronted', [toonId]) + + def setVictors(self, victorIds): + activeVictors = [] + activeVictorIds = [] + for victorId in victorIds: + toon = self.air.doId2do.get(victorId) + if toon is not None: + activeVictors.append(toon) + activeVictorIds.append(victorId) + continue + description = '%s|%s' % (self.stageId, activeVictorIds) + for avId in activeVictorIds: + self.air.writeServerEvent('stageDefeated', avId, description) + for toon in activeVictors: + simbase.air.questManager.toonDefeatedStage(toon, self.stageId) + + def b_setDefeated(self): + self.d_setDefeated() + self.setDefeated() + + def d_setDefeated(self): + self.sendUpdate('setDefeated') + + def setDefeated(self): + pass + + def allToonsGone(self, toonsThatCleared): + DistributedLevelAI.DistributedLevelAI.allToonsGone(self, toonsThatCleared) + if self.roomNum == 0: + stage = simbase.air.doId2do.get(self.stageDoId) + if stage is not None: + stage.allToonsGone() + else: + self.notify.warning('no stage %s in allToonsGone' % self.stageDoId) diff --git a/toontown/coghq/DistributedStomper.py b/toontown/coghq/DistributedStomper.py new file mode 100755 index 00000000..0eac945d --- /dev/null +++ b/toontown/coghq/DistributedStomper.py @@ -0,0 +1,347 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from StomperGlobals import * +from direct.distributed import ClockDelta +from direct.showbase.PythonUtil import lerp +import math +import DistributedCrusherEntity +import MovingPlatform +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from toontown.toonbase import ToontownGlobals + +class DistributedStomper(DistributedCrusherEntity.DistributedCrusherEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStomper') + stomperSounds = ['phase_4/audio/sfx/CHQ_FACT_stomper_small.ogg', 'phase_9/audio/sfx/CHQ_FACT_stomper_med.ogg', 'phase_9/audio/sfx/CHQ_FACT_stomper_large.ogg'] + stomperModels = ['phase_9/models/cogHQ/square_stomper'] + + def __init__(self, cr): + self.stomperModels = ['phase_9/models/cogHQ/square_stomper'] + self.lastPos = Point3(0, 0, 0) + self.model = None + self.smokeTrack = None + self.ival = None + self.smoke = None + self.shadow = None + self.sound = None + self.crushSurface = None + self.cogStyle = 0 + self.loaded = 0 + self.crushedList = [] + self.sounds = [] + self.wantSmoke = 1 + self.wantShadow = 1 + self.animateShadow = 1 + self.removeHeadFloor = 0 + self.removeCamBarrierCollisions = 0 + for s in self.stomperSounds: + self.sounds.append(loader.loadSfx(s)) + + DistributedCrusherEntity.DistributedCrusherEntity.__init__(self, cr) + return + + def generateInit(self): + self.notify.debug('generateInit') + DistributedCrusherEntity.DistributedCrusherEntity.generateInit(self) + + def generate(self): + self.notify.debug('generate') + DistributedCrusherEntity.DistributedCrusherEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedCrusherEntity.DistributedCrusherEntity.announceGenerate(self) + self.loadModel() + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + if self.ival: + self.ival.pause() + del self.ival + self.ival = None + if self.smokeTrack: + self.smokeTrack.pause() + del self.smokeTrack + self.smokeTrack = None + DistributedCrusherEntity.DistributedCrusherEntity.disable(self) + return + + def delete(self): + self.notify.debug('delete') + self.unloadModel() + taskMgr.remove(self.taskName('smokeTask')) + DistributedCrusherEntity.DistributedCrusherEntity.delete(self) + + def loadModel(self): + self.loaded = 1 + self.stomperModels = ['phase_9/models/cogHQ/square_stomper'] + if self.cogStyle == 1: + self.stomperModels = ['phase_11/models/lawbotHQ/LB_square_stomper'] + self.notify.debug('loadModel') + shadow = None + self.sound = self.sounds[self.soundPath] + self.rotateNode = self.attachNewNode('rotate') + stomperModel = loader.loadModel(self.stomperModels[self.modelPath]) + if self.style == 'vertical': + model = stomperModel + self.rotateNode.setP(-90) + sideList = model.findAllMatches('**/collSide') + for side in sideList: + side.stash() + + upList = model.findAllMatches('**/collUp') + for up in upList: + up.stash() + + head = model.find('**/head') + shaft = model.find('**/shaft') + self.crushSurface = head.find('**/collDownWalls') + self.shadow = None + if self.wantShadow: + shadow = loader.loadModel('phase_3/models/props/square_drop_shadow').getChild(0) + shadow.setScale(0.3 * self.headScale[0], 0.3 * self.headScale[2], 1) + shadow.setAlphaScale(0.8) + shadow.flattenMedium() + shadow.reparentTo(self) + shadow.setPos(0, 0, 0.025) + shadow.setTransparency(1) + self.shadow = shadow + floorHeadNp = model.find('**/head_collisions/**/collDownFloor') + floorHead = floorHeadNp.node() + if self.removeHeadFloor: + floorHeadNp.stash() + else: + for i in xrange(floorHead.getNumSolids()): + floorHead.modifySolid(i).setEffectiveNormal(Vec3(0.0, -1.0, 0.0)) + + floorShaft = model.find('**/shaft_collisions/**/collDownFloor').node() + for i in xrange(floorShaft.getNumSolids()): + floorShaft.modifySolid(i).setEffectiveNormal(Vec3(0.0, -1.0, 0.0)) + + self.accept(self.crushMsg, self.checkSquashedToon) + elif self.style == 'horizontal': + model = MovingPlatform.MovingPlatform() + model.setupCopyModel(self.getParentToken(), stomperModel, 'collSideFloor') + head = model.find('**/head') + head.node().setPreserveTransform(0) + head.setZ(1.0) + for child in head.findAllMatches('+ModelNode'): + child.node().setPreserveTransform(ModelNode.PTNet) + + model.flattenLight() + upList = model.findAllMatches('**/collUp') + for up in upList: + up.stash() + + downList = model.findAllMatches('**/collDown') + for down in downList: + down.stash() + + self.crushSurface = model.find('**/head_collisions/**/collSideWalls') + if self.removeCamBarrierCollisions: + walls = model.findAllMatches('**/collDownWalls') + for wall in walls: + node = wall.node() + bitmask = node.getIntoCollideMask() + invBitmask = BitMask32(ToontownGlobals.CameraBitmask) + invBitmask.invertInPlace() + bitmask &= invBitmask + node.setIntoCollideMask(bitmask) + + shaft = model.find('**/shaft') + shaft.setScale(self.shaftScale) + head.setScale(self.headScale) + model.find('**/shaft').node().setPreserveTransform(0) + model.flattenLight() + self.model = model + if self.motion == MotionSwitched: + self.model.setPos(0, -self.range, 0) + self.model.reparentTo(self.rotateNode) + if self.wantSmoke: + self.smoke = loader.loadModel('phase_4/models/props/test_clouds') + self.smoke.setColor(0.8, 0.7, 0.5, 1) + self.smoke.setBillboardPointEye() + return + + def stashCrushSurface(self, isStunned): + self.notify.debug('stashCrushSurface(%s)' % isStunned) + if self.crushSurface and not self.crushSurface.isEmpty(): + if isStunned: + self.crushSurface.stash() + else: + self.crushSurface.unstash() + + def unloadModel(self): + if self.ival: + self.ival.pause() + del self.ival + self.ival = None + if self.smoke: + self.smoke.removeNode() + del self.smoke + self.smoke = None + if self.shadow: + self.shadow.removeNode() + del self.shadow + self.shadow = None + if self.model: + if isinstance(self.model, MovingPlatform.MovingPlatform): + self.model.destroy() + else: + self.model.removeNode() + del self.model + self.model = None + return + + def sendStompToon(self): + messenger.send(self.crushMsg) + + def doCrush(self): + self.notify.debug('doCrush, crushedList = %s' % self.crushedList) + for crushableId in self.crushedList: + crushable = self.level.entities.get(crushableId) + if crushable: + if self.style == 'vertical': + axis = 2 + else: + axis = 0 + crushable.playCrushMovie(self.entId, axis) + + self.crushedList = [] + + def getMotionIval(self, mode = STOMPER_START): + if self.range == 0.0: + return (None, 0) + wantSound = self.soundOn + if self.motion is MotionLinear: + motionIval = Sequence(LerpPosInterval(self.model, self.period / 2.0, Point3(0, -self.range, 0), startPos=Point3(0, 0, 0), fluid=1), WaitInterval(self.period / 4.0), LerpPosInterval(self.model, self.period / 4.0, Point3(0, 0, 0), startPos=Point3(0, -self.range, 0), fluid=1)) + elif self.motion is MotionSinus: + + def sinusFunc(t, self = self): + theta = math.pi + t * 2.0 * math.pi + c = math.cos(theta) + self.model.setFluidY((0.5 + c * 0.5) * -self.range) + + motionIval = Sequence(LerpFunctionInterval(sinusFunc, duration=self.period)) + elif self.motion is MotionSlowFast: + + def motionFunc(t, self = self): + stickTime = 0.2 + turnaround = 0.95 + t = t % 1 + if t < stickTime: + self.model.setFluidY(0) + elif t < turnaround: + self.model.setFluidY((t - stickTime) * -self.range / (turnaround - stickTime)) + elif t > turnaround: + self.model.setFluidY(-self.range + (t - turnaround) * self.range / (1 - turnaround)) + + motionIval = Sequence(LerpFunctionInterval(motionFunc, duration=self.period)) + elif self.motion is MotionCrush: + + def motionFunc(t, self = self): + stickTime = 0.2 + pauseAtTopTime = 0.5 + turnaround = 0.85 + t = t % 1 + if t < stickTime: + self.model.setFluidY(0) + elif t <= turnaround - pauseAtTopTime: + self.model.setFluidY((t - stickTime) * -self.range / (turnaround - pauseAtTopTime - stickTime)) + elif t > turnaround - pauseAtTopTime and t <= turnaround: + self.model.setFluidY(-self.range) + elif t > turnaround: + self.model.setFluidY(-self.range + (t - turnaround) * self.range / (1 - turnaround)) + + tStick = 0.2 * self.period + tUp = 0.45 * self.period + tPause = 0.2 * self.period + tDown = 0.15 * self.period + motionIval = Sequence(Wait(tStick), LerpPosInterval(self.model, tUp, Vec3(0, -self.range, 0), blendType='easeInOut', fluid=1), Wait(tPause), Func(self.doCrush), LerpPosInterval(self.model, tDown, Vec3(0, 0, 0), blendType='easeInOut', fluid=1)) + elif self.motion is MotionSwitched: + if mode == STOMPER_STOMP: + motionIval = Sequence(Func(self.doCrush), LerpPosInterval(self.model, 0.35, Vec3(0, 0, 0), blendType='easeInOut', fluid=1)) + elif mode == STOMPER_RISE: + motionIval = Sequence(LerpPosInterval(self.model, 0.5, Vec3(0, -self.range, 0), blendType='easeInOut', fluid=1)) + wantSound = 0 + else: + motionIval = None + else: + + def halfSinusFunc(t, self = self): + self.model.setFluidY(math.sin(t * math.pi) * -self.range) + + motionIval = Sequence(LerpFunctionInterval(halfSinusFunc, duration=self.period)) + return (motionIval, wantSound) + + def startStomper(self, startTime, mode = STOMPER_START): + if self.ival: + self.ival.pause() + del self.ival + self.ival = None + motionIval, wantSound = self.getMotionIval(mode) + if motionIval == None: + return + self.ival = Parallel(Sequence(motionIval, Func(self.__startSmokeTask), Func(self.sendStompToon)), name=self.uniqueName('Stomper')) + if wantSound: + sndDur = motionIval.getDuration() + self.ival.append(Sequence(Wait(sndDur), Func(base.playSfx, self.sound, node=self.model, volume=0.45))) + if self.shadow is not None and self.animateShadow: + + def adjustShadowScale(t, self = self): + modelY = self.model.getY() + maxHeight = 10 + a = min(-modelY / maxHeight, 1.0) + self.shadow.setScale(lerp(1, 0.2, a)) + self.shadow.setAlphaScale(lerp(1, 0.2, a)) + + self.ival.append(LerpFunctionInterval(adjustShadowScale, duration=self.period)) + if mode == STOMPER_START: + self.ival.loop() + self.ival.setT(globalClock.getFrameTime() - self.level.startTime + self.period * self.phaseShift) + else: + self.ival.start(startTime) + return + + def stopStomper(self): + if self.ival: + self.ival.pause() + if self.smokeTrack: + self.smokeTrack.finish() + del self.smokeTrack + self.smokeTrack = None + return + + def setMovie(self, mode, timestamp, crushedList): + self.notify.debug('setMovie %d' % mode) + timestamp = ClockDelta.globalClockDelta.networkToLocalTime(timestamp) + now = globalClock.getFrameTime() + if mode == STOMPER_START or mode == STOMPER_RISE or mode == STOMPER_STOMP: + self.crushedList = crushedList + self.startStomper(timestamp, mode) + + def __startSmokeTask(self): + taskMgr.remove(self.taskName('smokeTask')) + if self.wantSmoke: + taskMgr.add(self.__smokeTask, self.taskName('smokeTask')) + + def __smokeTask(self, task): + self.smoke.reparentTo(self) + self.smoke.setScale(1) + if self.smokeTrack: + self.smokeTrack.finish() + del self.smokeTrack + self.smokeTrack = Sequence(Parallel(LerpScaleInterval(self.smoke, 0.2, Point3(4, 1, 4)), LerpColorScaleInterval(self.smoke, 1, Vec4(1, 1, 1, 0))), Func(self.smoke.reparentTo, hidden), Func(self.smoke.clearColorScale)) + self.smokeTrack.start() + return Task.done + + def checkSquashedToon(self): + if self.style == 'vertical': + tPos = base.localAvatar.getPos(self.rotateNode) + zRange = self.headScale[2] + xRange = self.headScale[0] + yRange = 5 + if tPos[2] < zRange and tPos[2] > -zRange and tPos[0] < xRange and tPos[0] > -xRange and tPos[1] < yRange / 10.0 and tPos[1] > -yRange: + self.level.b_setOuch(self.damage, 'Squish') + base.localAvatar.setZ(self.getZ(render) + 0.025) diff --git a/toontown/coghq/DistributedStomperAI.py b/toontown/coghq/DistributedStomperAI.py new file mode 100755 index 00000000..0f1033c7 --- /dev/null +++ b/toontown/coghq/DistributedStomperAI.py @@ -0,0 +1,42 @@ +from otp.ai.AIBase import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +import DistributedCrusherEntityAI +import StomperGlobals +from direct.distributed import ClockDelta +from toontown.coghq import CrusherCellAI + +class DistributedStomperAI(DistributedCrusherEntityAI.DistributedCrusherEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStomperAI') + + def __init__(self, level, entId, pairId = -1): + DistributedCrusherEntityAI.DistributedCrusherEntityAI.__init__(self, level, entId) + self.pairId = pairId + + def generate(self): + DistributedCrusherEntityAI.DistributedCrusherEntityAI.generate(self) + if self.switchId != 0: + self.accept(self.getOutputEventName(self.switchId), self.reactToSwitch) + self.d_startStomper() + + def delete(self): + del self.pos + self.ignoreAll() + DistributedCrusherEntityAI.DistributedCrusherEntityAI.delete(self) + + def d_startStomper(self): + self.sendUpdate('setMovie', [StomperGlobals.STOMPER_START, ClockDelta.globalClockDelta.getRealNetworkTime(), []]) + + def reactToSwitch(self, on): + if on: + crushedList = [] + if self.crushCell: + self.crushCell.updateCrushables() + for id in self.crushCell.occupantIds: + if id in self.crushCell.crushables: + crushedList.append(id) + + self.sendCrushMsg() + self.sendUpdate('setMovie', [StomperGlobals.STOMPER_STOMP, ClockDelta.globalClockDelta.getRealNetworkTime(), crushedList]) + else: + self.sendUpdate('setMovie', [StomperGlobals.STOMPER_RISE, ClockDelta.globalClockDelta.getRealNetworkTime(), []]) diff --git a/toontown/coghq/DistributedStomperPair.py b/toontown/coghq/DistributedStomperPair.py new file mode 100755 index 00000000..e772dc33 --- /dev/null +++ b/toontown/coghq/DistributedStomperPair.py @@ -0,0 +1,44 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +import math +import StomperGlobals +from direct.directnotify import DirectNotifyGlobal +from otp.level import BasicEntities + +class DistributedStomperPair(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStomperPair') + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.children = None + return + + def delete(self): + BasicEntities.DistributedNodePathEntity.delete(self) + self.ignoreAll() + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.listenForChildren() + + def listenForChildren(self): + if self.stomperIds: + for entId in self.stomperIds: + self.accept(self.getUniqueName('crushMsg', entId), self.checkSquashedToon) + + def checkSquashedToon(self): + tPos = base.localAvatar.getPos(self) + print 'tpos = %s' % tPos + yRange = 3.0 + xRange = 3.0 + if tPos[1] < yRange and tPos[1] > -yRange and tPos[0] < xRange and tPos[0] > -xRange: + self.level.b_setOuch(3) diff --git a/toontown/coghq/DistributedStomperPairAI.py b/toontown/coghq/DistributedStomperPairAI.py new file mode 100755 index 00000000..5ea9204c --- /dev/null +++ b/toontown/coghq/DistributedStomperPairAI.py @@ -0,0 +1,32 @@ +from otp.ai.AIBase import * +from direct.directnotify import DirectNotifyGlobal +from otp.level import DistributedEntityAI +import StomperGlobals +from direct.distributed import ClockDelta + +class DistributedStomperPairAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStomperAI') + + def __init__(self, level, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.stompers = [None, None] + self.hitPtsTaken = 3 + return + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + + def delete(self): + DistributedEntityAI.DistributedEntityAI.delete(self) + + def setChildren(self, doIds): + for id in doIds: + self.children = simbase.air.doId2do[id] + + self.sendUpdate('setChildren', [doIds]) + + def setSquash(self): + avId = self.air.getAvatarIdFromSender() + av = simbase.air.doId2do.get(avId) + if av: + av.takeDamage(self.hitPtsTaken) diff --git a/toontown/coghq/DistributedSwitch.py b/toontown/coghq/DistributedSwitch.py new file mode 100755 index 00000000..843c3c26 --- /dev/null +++ b/toontown/coghq/DistributedSwitch.py @@ -0,0 +1,123 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from otp.level import BasicEntities +import DistributedSwitchBase +import MovingPlatform +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from otp.level import DistributedEntity + +class DistributedSwitch(DistributedSwitchBase.DistributedSwitchBase, BasicEntities.DistributedNodePathEntity): + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.fsm = ClassicFSM.ClassicFSM('DistributedSwitch', [State.State('off', self.enterOff, self.exitOff, ['playing', 'attract']), State.State('attract', self.enterAttract, self.exitAttract, ['playing']), State.State('playing', self.enterPlaying, self.exitPlaying, ['attract'])], 'off', 'off') + self.fsm.enterInitialState() + self.node = None + self.triggerName = '' + return + + def setup(self): + self.setupSwitch() + self.setState(self.initialState, self.initialStateTimestamp) + del self.initialState + del self.initialStateTimestamp + self.accept('exit%s' % (self.getName(),), self.exitTrigger) + self.acceptAvatar() + + def takedown(self): + pass + + setIsOnEvent = DistributedSwitchBase.stubFunction + setIsOn = DistributedSwitchBase.stubFunction + setSecondsOn = DistributedSwitchBase.stubFunction + + def generate(self): + BasicEntities.DistributedNodePathEntity.generate(self) + self.track = None + return + + def announceGenerate(self): + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.setup() + + def disable(self): + self.ignoreAll() + self.fsm.request('off') + BasicEntities.DistributedNodePathEntity.disable(self) + self.takedown() + + def delete(self): + del self.fsm + BasicEntities.DistributedNodePathEntity.delete(self) + + def acceptAvatar(self): + self.acceptOnce('enter%s' % (self.getName(),), self.enterTrigger) + + def rejectInteract(self): + self.acceptAvatar() + + def avatarExit(self, avatarId): + self.acceptAvatar() + + def getName(self): + return 'switch-%s' % (self.entId,) + + def setupSwitch(self): + pass + + def switchOnTrack(self): + pass + + def switchOffTrack(self): + pass + + def setAvatarInteract(self, avatarId): + self.avatarId = avatarId + + def setState(self, state, timestamp): + if self.isGenerated(): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + else: + self.initialState = state + self.initialStateTimestamp = timestamp + + def enterTrigger(self, args = None): + self.sendUpdate('requestInteract') + + def exitTrigger(self, args = None): + self.sendUpdate('requestExit') + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterAttract(self, ts): + track = self.switchOffTrack() + if track is not None: + track.start(ts) + self.track = track + return + + def exitAttract(self): + if self.track: + self.track.finish() + self.track = None + return + + def enterPlaying(self, ts): + track = self.switchOnTrack() + if track is not None: + track.start(ts) + self.track = track + return + + def exitPlaying(self): + if self.track: + self.track.finish() + self.track = None + return diff --git a/toontown/coghq/DistributedSwitchAI.py b/toontown/coghq/DistributedSwitchAI.py new file mode 100755 index 00000000..2b5a8f03 --- /dev/null +++ b/toontown/coghq/DistributedSwitchAI.py @@ -0,0 +1,110 @@ +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +import DistributedSwitchBase +from direct.task import Task +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from otp.level import DistributedEntityAI + +class DistributedSwitchAI(DistributedSwitchBase.DistributedSwitchBase, DistributedEntityAI.DistributedEntityAI): + + def __init__(self, level, entId, zoneId = None): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.fsm = ClassicFSM.ClassicFSM('DistributedSwitch', [State.State('off', self.enterOff, self.exitOff, ['playing']), State.State('attract', self.enterAttract, self.exitAttract, ['playing']), State.State('playing', self.enterPlaying, self.exitPlaying, ['attract'])], 'off', 'off') + self.fsm.enterInitialState() + self.avatarId = 0 + self.doLaterTask = None + if zoneId is not None: + self.generateWithRequired(zoneId) + return + + def setup(self): + pass + + def takedown(self): + pass + + setScale = DistributedSwitchBase.stubFunction + + def delete(self): + if self.doLaterTask: + self.doLaterTask.remove() + self.doLaterTask = None + del self.fsm + DistributedEntityAI.DistributedEntityAI.delete(self) + return + + def getAvatarInteract(self): + return self.avatarId + + def getState(self): + r = [self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()] + return r + + def sendState(self): + self.sendUpdate('setState', self.getState()) + + def setIsOn(self, isOn): + if self.isOn != isOn: + self.isOn = isOn + stateName = self.fsm.getCurrentState().getName() + if isOn: + if stateName != 'playing': + self.fsm.request('playing') + elif stateName != 'attract': + self.fsm.request('attract') + messenger.send(self.getOutputEventName(), [isOn]) + + def getIsOn(self): + return self.isOn + + def getName(self): + return 'switch-%s' % (self.entId,) + + def switchOffTask(self, task): + self.setIsOn(0) + self.fsm.request('attract') + return Task.done + + def requestInteract(self): + avatarId = self.air.getAvatarIdFromSender() + stateName = self.fsm.getCurrentState().getName() + if stateName != 'playing': + self.sendUpdate('setAvatarInteract', [avatarId]) + self.avatarId = avatarId + self.fsm.request('playing') + else: + self.sendUpdateToAvatarId(avatarId, 'rejectInteract', []) + + def requestExit(self): + avatarId = self.air.getAvatarIdFromSender() + if self.avatarId and avatarId == self.avatarId: + stateName = self.fsm.getCurrentState().getName() + if stateName == 'playing': + self.sendUpdate('avatarExit', [avatarId]) + self.avatarId = None + if self.isOn and self.secondsOn != -1.0 and self.secondsOn >= 0.0: + self.doLaterTask = taskMgr.doMethodLater(self.secondsOn, self.switchOffTask, self.uniqueName('switch-timer')) + return + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterAttract(self): + self.sendState() + + def exitAttract(self): + pass + + def enterPlaying(self): + self.sendState() + self.setIsOn(1) + + def exitPlaying(self): + if self.doLaterTask: + self.doLaterTask.remove() + self.doLaterTask = None + return diff --git a/toontown/coghq/DistributedSwitchBase.py b/toontown/coghq/DistributedSwitchBase.py new file mode 100755 index 00000000..bfee36e5 --- /dev/null +++ b/toontown/coghq/DistributedSwitchBase.py @@ -0,0 +1,8 @@ + + +def stubFunction(*args): + pass + + +class DistributedSwitchBase: + pass diff --git a/toontown/coghq/DistributedTrigger.py b/toontown/coghq/DistributedTrigger.py new file mode 100755 index 00000000..979d4f3d --- /dev/null +++ b/toontown/coghq/DistributedTrigger.py @@ -0,0 +1,40 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +import MovingPlatform +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +import DistributedSwitch +from toontown.toonbase import TTLocalizer + +class DistributedTrigger(DistributedSwitch.DistributedSwitch): + + def setupSwitch(self): + radius = 1.0 + cSphere = CollisionSphere(0.0, 0.0, 0.0, radius) + cSphere.setTangible(0) + cSphereNode = CollisionNode(self.getName()) + cSphereNode.addSolid(cSphere) + self.cSphereNodePath = self.attachNewNode(cSphereNode) + cSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + self.flattenMedium() + + def delete(self): + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + DistributedSwitch.DistributedSwitch.delete(self) + + def enterTrigger(self, args = None): + DistributedSwitch.DistributedSwitch.enterTrigger(self, args) + self.setIsOn(1) + + def exitTrigger(self, args = None): + DistributedSwitch.DistributedSwitch.exitTrigger(self, args) + self.setIsOn(0) + + def getName(self): + if self.triggerName != '': + return self.triggerName + else: + return DistributedSwitch.DistributedSwitch.getName(self) diff --git a/toontown/coghq/DistributedTriggerAI.py b/toontown/coghq/DistributedTriggerAI.py new file mode 100755 index 00000000..a5d80ccd --- /dev/null +++ b/toontown/coghq/DistributedTriggerAI.py @@ -0,0 +1,6 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +import DistributedSwitchAI + +class DistributedTriggerAI(DistributedSwitchAI.DistributedSwitchAI): + pass diff --git a/toontown/coghq/FactoryBase.py b/toontown/coghq/FactoryBase.py new file mode 100755 index 00000000..c4ec6afe --- /dev/null +++ b/toontown/coghq/FactoryBase.py @@ -0,0 +1,17 @@ +from toontown.toonbase import ToontownGlobals + +class FactoryBase: + + def __init__(self): + pass + + def setFactoryId(self, factoryId): + self.factoryId = factoryId + self.factoryType = ToontownGlobals.factoryId2factoryType[factoryId] + self.cogTrack = ToontownGlobals.cogHQZoneId2dept(factoryId) + + def getCogTrack(self): + return self.cogTrack + + def getFactoryType(self): + return self.factoryType diff --git a/toontown/coghq/FactoryCameraViews.py b/toontown/coghq/FactoryCameraViews.py new file mode 100755 index 00000000..b5f80576 --- /dev/null +++ b/toontown/coghq/FactoryCameraViews.py @@ -0,0 +1,68 @@ +from panda3d.core import * +from direct.showbase.PythonUtil import Functor +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal + +class FactoryCameraViews: + notify = DirectNotifyGlobal.directNotify.newCategory('FactoryCameraViews') + + def __init__(self, factory): + self.factory = factory + av = base.localAvatar + self.currentCamPos = None + self.views = [['signatureRoomView', (Point3(0.0, -14.8419799805, 13.212685585), + Point3(0.0, -13.9563484192, 12.749215126), + Point3(0.0, 1.5, 15.75), + Point3(0.0, 1.5, -3.9375), + 1), ['localToonLeftBattle']], ['lookoutTrigger', (Point3(0, -17.7, 28.8), + Point3(0, 10, 0), + Point3(0.0, 1.5, 15.75), + Point3(0.0, 1.5, -3.9375), + 1), []], ['moleFieldView', (Point3(0, -17.7, 28.8), + Point3(0, 10, 0), + Point3(0.0, 1.5, 15.75), + Point3(0.0, 1.5, -3.9375), + 1), []]] + camHeight = av.getClampedAvatarHeight() + for i in xrange(len(self.views)): + camPos = self.views[i][1] + av.auxCameraPositions.append(camPos) + factory.accept('enter' + self.views[i][0], Functor(self.switchCamPos, i)) + for msg in self.views[i][2]: + factory.accept(msg, self.checkCamPos) + + return + + def delete(self): + for i in xrange(len(self.views)): + base.localAvatar.auxCameraPositions.remove(self.views[i][1]) + self.factory.ignore('enter' + self.views[i][0]) + self.factory.ignore('exit' + self.views[i][0]) + for msg in self.views[i][2]: + self.factory.ignore(msg) + + base.localAvatar.resetCameraPosition() + del self.views + + def switchCamPos(self, viewIndex, colEntry = None): + av = base.localAvatar + prevView = av.cameraIndex + self.currentCamPos = viewIndex + av.accept('exit' + self.views[viewIndex][0], Functor(self.prevCamPos, prevView)) + self.notify.info('auto-switching to camera position %s' % viewIndex) + av.setCameraSettings(self.views[viewIndex][1]) + + def prevCamPos(self, index, colEntry = None): + av = base.localAvatar + if len(av.cameraPositions) > index: + av.setCameraPositionByIndex(index) + self.currentCamPos = None + return + + def checkCamPos(self): + if self.currentCamPos != None: + av = base.localAvatar + viewIndex = self.currentCamPos + self.notify.info('returning to camera position %s' % viewIndex) + av.setCameraSettings(self.views[viewIndex][1]) + return diff --git a/toontown/coghq/FactoryEntityCreator.py b/toontown/coghq/FactoryEntityCreator.py new file mode 100755 index 00000000..6a0f10ed --- /dev/null +++ b/toontown/coghq/FactoryEntityCreator.py @@ -0,0 +1,55 @@ +from otp.level import EntityCreator +import FactoryLevelMgr +import PlatformEntity +import ConveyorBelt +import GearEntity +import PaintMixer +import GoonClipPlane +import MintProduct +import MintProductPallet +import MintShelf +import PathMasterEntity +import RenderingEntity + +class FactoryEntityCreator(EntityCreator.EntityCreator): + + def __init__(self, level): + EntityCreator.EntityCreator.__init__(self, level) + nothing = EntityCreator.nothing + nonlocal = EntityCreator.nonlocal + self.privRegisterTypes({'activeCell': nonlocal, + 'crusherCell': nonlocal, + 'battleBlocker': nonlocal, + 'beanBarrel': nonlocal, + 'button': nonlocal, + 'conveyorBelt': ConveyorBelt.ConveyorBelt, + 'crate': nonlocal, + 'door': nonlocal, + 'directionalCell': nonlocal, + 'gagBarrel': nonlocal, + 'gear': GearEntity.GearEntity, + 'goon': nonlocal, + 'gridGoon': nonlocal, + 'golfGreenGame': nonlocal, + 'goonClipPlane': GoonClipPlane.GoonClipPlane, + 'grid': nonlocal, + 'healBarrel': nonlocal, + 'levelMgr': FactoryLevelMgr.FactoryLevelMgr, + 'lift': nonlocal, + 'mintProduct': MintProduct.MintProduct, + 'mintProductPallet': MintProductPallet.MintProductPallet, + 'mintShelf': MintShelf.MintShelf, + 'mover': nonlocal, + 'paintMixer': PaintMixer.PaintMixer, + 'pathMaster': PathMasterEntity.PathMasterEntity, + 'rendering': RenderingEntity.RenderingEntity, + 'platform': PlatformEntity.PlatformEntity, + 'sinkingPlatform': nonlocal, + 'stomper': nonlocal, + 'stomperPair': nonlocal, + 'laserField': nonlocal, + 'securityCamera': nonlocal, + 'elevatorMarker': nonlocal, + 'trigger': nonlocal, + 'moleField': nonlocal, + 'maze': nonlocal}) diff --git a/toontown/coghq/FactoryEntityCreatorAI.py b/toontown/coghq/FactoryEntityCreatorAI.py new file mode 100755 index 00000000..4a2d77eb --- /dev/null +++ b/toontown/coghq/FactoryEntityCreatorAI.py @@ -0,0 +1,72 @@ +from otp.level import EntityCreatorAI +from direct.showbase.PythonUtil import Functor +import DistributedBeanBarrelAI +import DistributedButtonAI +import DistributedCrateAI +import DistributedLiftAI +import DistributedDoorEntityAI +import DistributedGagBarrelAI +import DistributedGridAI +from toontown.suit import DistributedGridGoonAI +from toontown.suit import DistributedGoonAI +import DistributedHealBarrelAI +import DistributedStomperPairAI +import DistributedTriggerAI +import DistributedStomperAI +import DistributedLaserFieldAI +import DistributedSecurityCameraAI +import DistributedMoverAI +import DistributedElevatorMarkerAI +import DistributedSinkingPlatformAI +import ActiveCellAI +import CrusherCellAI +import DirectionalCellAI +import FactoryLevelMgrAI +import BattleBlockerAI +import DistributedGolfGreenGameAI +from toontown.coghq import DistributedMoleFieldAI +from toontown.coghq import DistributedMazeAI + +class FactoryEntityCreatorAI(EntityCreatorAI.EntityCreatorAI): + + def __init__(self, level): + EntityCreatorAI.EntityCreatorAI.__init__(self, level) + cDE = EntityCreatorAI.createDistributedEntity + cLE = EntityCreatorAI.createLocalEntity + nothing = EntityCreatorAI.nothing + self.privRegisterTypes({'activeCell': Functor(cDE, ActiveCellAI.ActiveCellAI), + 'crusherCell': Functor(cDE, CrusherCellAI.CrusherCellAI), + 'battleBlocker': Functor(cDE, BattleBlockerAI.BattleBlockerAI), + 'beanBarrel': Functor(cDE, DistributedBeanBarrelAI.DistributedBeanBarrelAI), + 'button': DistributedButtonAI.DistributedButtonAI, + 'conveyorBelt': nothing, + 'crate': Functor(cDE, DistributedCrateAI.DistributedCrateAI), + 'directionalCell': Functor(cDE, DirectionalCellAI.DirectionalCellAI), + 'door': DistributedDoorEntityAI.DistributedDoorEntityAI, + 'gagBarrel': Functor(cDE, DistributedGagBarrelAI.DistributedGagBarrelAI), + 'gear': nothing, + 'goon': Functor(cDE, DistributedGoonAI.DistributedGoonAI), + 'gridGoon': Functor(cDE, DistributedGridGoonAI.DistributedGridGoonAI), + 'golfGreenGame': Functor(cDE, DistributedGolfGreenGameAI.DistributedGolfGreenGameAI), + 'goonClipPlane': nothing, + 'grid': Functor(cDE, DistributedGridAI.DistributedGridAI), + 'healBarrel': Functor(cDE, DistributedHealBarrelAI.DistributedHealBarrelAI), + 'levelMgr': Functor(cLE, FactoryLevelMgrAI.FactoryLevelMgrAI), + 'lift': Functor(cDE, DistributedLiftAI.DistributedLiftAI), + 'mintProduct': nothing, + 'mintProductPallet': nothing, + 'mintShelf': nothing, + 'mover': Functor(cDE, DistributedMoverAI.DistributedMoverAI), + 'paintMixer': nothing, + 'pathMaster': nothing, + 'rendering': nothing, + 'platform': nothing, + 'sinkingPlatform': Functor(cDE, DistributedSinkingPlatformAI.DistributedSinkingPlatformAI), + 'stomper': Functor(cDE, DistributedStomperAI.DistributedStomperAI), + 'stomperPair': Functor(cDE, DistributedStomperPairAI.DistributedStomperPairAI), + 'laserField': Functor(cDE, DistributedLaserFieldAI.DistributedLaserFieldAI), + 'securityCamera': Functor(cDE, DistributedSecurityCameraAI.DistributedSecurityCameraAI), + 'elevatorMarker': Functor(cDE, DistributedElevatorMarkerAI.DistributedElevatorMarkerAI), + 'trigger': DistributedTriggerAI.DistributedTriggerAI, + 'moleField': Functor(cDE, DistributedMoleFieldAI.DistributedMoleFieldAI), + 'maze': Functor(cDE, DistributedMazeAI.DistributedMazeAI)}) diff --git a/toontown/coghq/FactoryExterior.py b/toontown/coghq/FactoryExterior.py new file mode 100755 index 00000000..f1957ada --- /dev/null +++ b/toontown/coghq/FactoryExterior.py @@ -0,0 +1,178 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from otp.nametag import NametagGlobals +from panda3d.core import * +from toontown.battle import BattlePlace +from toontown.building import Elevator +from toontown.dna.DNAParser import loadDNAFileAI, DNAStorage +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals + + +class FactoryExterior(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('FactoryExterior') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.elevatorDoneEvent = 'elevatorDone' + + def load(self): + self.fsm = ClassicFSM.ClassicFSM('FactoryExterior', [State.State('start', self.enterStart, self.exitStart, ['walk', + 'tunnelIn', + 'teleportIn', + 'doorIn']), + State.State('walk', self.enterWalk, self.exitWalk, ['stickerBook', + 'teleportOut', + 'tunnelOut', + 'doorOut', + 'elevator', + 'stopped', + 'WaitForBattle', + 'battle']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut', 'elevator']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'WaitForBattle', + 'battle', + 'elevator', + 'tunnelOut', + 'teleportOut']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', 'walk']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', 'final', 'WaitForBattle']), + State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk', 'stopped']), + State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk', 'stopped']), + State.State('died', self.enterDied, self.exitDied, ['quietZone']), + State.State('tunnelIn', self.enterTunnelIn, self.exitTunnelIn, ['walk']), + State.State('tunnelOut', self.enterTunnelOut, self.exitTunnelOut, ['final']), + State.State('elevator', self.enterElevator, self.exitElevator, ['walk', 'stopped']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.parentFSM.getStateNamed('factoryExterior').addChild(self.fsm) + BattlePlace.BattlePlace.load(self) + + def unload(self): + self.parentFSM.getStateNamed('factoryExterior').removeChild(self.fsm) + del self.fsm + BattlePlace.BattlePlace.unload(self) + + def enter(self, requestStatus): + self.zoneId = requestStatus['zoneId'] + + # Load the CogHQ DNA file: + dnaStore = DNAStorage() + dnaFileName = self.genDNAFileName(self.zoneId) + + if not dnaFileName.endswith('13200.pdna'): + + loadDNAFileAI(dnaStore, dnaFileName) + + # Collect all of the vis group zone IDs: + self.zoneVisDict = {} + for i in xrange(dnaStore.getNumDNAVisGroupsAI()): + groupFullName = dnaStore.getDNAVisGroupName(i) + visGroup = dnaStore.getDNAVisGroupAI(i) + visZoneId = int(base.cr.hoodMgr.extractGroupName(groupFullName)) + visibles = [] + for i in xrange(visGroup.getNumVisibles()): + visibles.append(int(visGroup.getVisible(i))) + visibles.append(ZoneUtil.getBranchZone(visZoneId)) + self.zoneVisDict[visZoneId] = visibles + + # Next, we want interest in all vis groups due to this being a Cog HQ: + base.cr.sendSetZoneMsg(self.zoneId, self.zoneVisDict.values()[0]) + + BattlePlace.BattlePlace.enter(self) + self.fsm.enterInitialState() + base.playMusic(self.loader.music, looping=1, volume=0.8) + self.loader.geom.reparentTo(render) + self.nodeList = [self.loader.geom] + self.loader.hood.startSky() + self._telemLimiter = TLGatherAllAvs('FactoryExterior', RotationLimitToH) + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + NametagGlobals.setMasterArrowsOn(1) + self.tunnelOriginList = base.cr.hoodMgr.addLinkTunnelHooks(self, self.nodeList) + how = requestStatus['how'] + self.fsm.request(how, [requestStatus]) + + def exit(self): + self._telemLimiter.destroy() + del self._telemLimiter + self.loader.hood.stopSky() + self.fsm.requestFinalState() + self.loader.music.stop() + for node in self.tunnelOriginList: + node.removeNode() + + del self.tunnelOriginList + del self.nodeList + self.ignoreAll() + BattlePlace.BattlePlace.exit(self) + + def enterTunnelOut(self, requestStatus): + fromZoneId = self.zoneId - self.zoneId % 100 + tunnelName = base.cr.hoodMgr.makeLinkTunnelName(self.loader.hood.id, fromZoneId) + requestStatus['tunnelName'] = tunnelName + BattlePlace.BattlePlace.enterTunnelOut(self, requestStatus) + + def enterTeleportIn(self, requestStatus): + base.localAvatar.setPosHpr(-34, -350, 0, -28, 0, 0) + BattlePlace.BattlePlace.enterTeleportIn(self, requestStatus) + + def enterTeleportOut(self, requestStatus): + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + avId = requestStatus['avId'] + shardId = requestStatus['shardId'] + if hoodId == self.loader.hood.hoodId and zoneId == self.zoneId and shardId == None: + self.fsm.request('teleportIn', [requestStatus]) + elif hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + def exitTeleportOut(self): + BattlePlace.BattlePlace.exitTeleportOut(self) + + def enterElevator(self, distElevator): + self.accept(self.elevatorDoneEvent, self.handleElevatorDone) + self.elevator = Elevator.Elevator(self.fsm.getStateNamed('elevator'), self.elevatorDoneEvent, distElevator) + distElevator.elevatorFSM = self.elevator + self.elevator.load() + self.elevator.enter() + + def exitElevator(self): + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + del self.elevator + + def detectedElevatorCollision(self, distElevator): + self.fsm.request('elevator', [distElevator]) + + def handleElevatorDone(self, doneStatus): + self.notify.debug('handling elevator done event') + where = doneStatus['where'] + if where == 'reject': + if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen(): + pass + else: + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'factoryInterior': + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + elif where == 'stageInterior': + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') diff --git a/toontown/coghq/FactoryInterior.py b/toontown/coghq/FactoryInterior.py new file mode 100755 index 00000000..0a1b9eee --- /dev/null +++ b/toontown/coghq/FactoryInterior.py @@ -0,0 +1,274 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattlePlace +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from panda3d.core import * +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toon import Toon +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownBattleGlobals +from toontown.building import Elevator +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals + +class FactoryInterior(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('FactoryInterior') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.zoneId = ToontownGlobals.SellbotFactoryInt + self.elevatorDoneEvent = 'elevatorDone' + + def load(self): + self.fsm = ClassicFSM.ClassicFSM('FactoryInterior', [State.State('start', self.enterStart, self.exitStart, ['walk', 'teleportIn', 'fallDown']), + State.State('walk', self.enterWalk, self.exitWalk, ['push', + 'sit', + 'stickerBook', + 'WaitForBattle', + 'battle', + 'died', + 'teleportOut', + 'squished', + 'fallDown', + 'elevator']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut']), + State.State('sit', self.enterSit, self.exitSit, ['walk', 'died', 'teleportOut']), + State.State('push', self.enterPush, self.exitPush, ['walk', 'died', 'teleportOut']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'battle', + 'WaitForBattle', + 'died', + 'teleportOut']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', + 'walk', + 'died', + 'teleportOut']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('fallDown', self.enterFallDown, self.exitFallDown, ['walk', 'died', 'teleportOut']), + State.State('squished', self.enterSquished, self.exitSquished, ['walk', 'died', 'teleportOut']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', + 'teleportOut', + 'quietZone', + 'died']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', + 'FLA', + 'quietZone', + 'WaitForBattle']), + State.State('died', self.enterDied, self.exitDied, ['teleportOut']), + State.State('FLA', self.enterFLA, self.exitFLA, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['teleportIn']), + State.State('elevator', self.enterElevator, self.exitElevator, ['walk']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.parentFSM.getStateNamed('factoryInterior').addChild(self.fsm) + BattlePlace.BattlePlace.load(self) + self.music = base.loadMusic('phase_9/audio/bgm/CHQ_FACT_bg.ogg') + + def unload(self): + self.parentFSM.getStateNamed('factoryInterior').removeChild(self.fsm) + del self.fsm + del self.music + BattlePlace.BattlePlace.unload(self) + + def enter(self, requestStatus): + self.fsm.enterInitialState() + base.transitions.fadeOut(t=0) + base.localAvatar.inventory.setRespectInvasions(0) + self._telemLimiter = TLGatherAllAvs('FactoryInterior', RotationLimitToH) + + def commence(self = self): + NametagGlobals.setMasterArrowsOn(1) + self.fsm.request(requestStatus['how'], [requestStatus]) + base.playMusic(self.music, looping=1, volume=0.8) + base.transitions.irisIn() + + if hasattr(base, 'factoryReady'): + commence() + else: + self.acceptOnce('FactoryReady', commence) + self.factoryDefeated = 0 + self.acceptOnce('FactoryWinEvent', self.handleFactoryWinEvent) + if __debug__ and 0: + self.accept('f10', lambda : messenger.send('FactoryWinEvent')) + self.confrontedForeman = 0 + + def handleConfrontedForeman(self = self): + self.confrontedForeman = 1 + + self.acceptOnce('localToonConfrontedForeman', handleConfrontedForeman) + + def exit(self): + NametagGlobals.setMasterArrowsOn(0) + self._telemLimiter.destroy() + del self._telemLimiter + if hasattr(base, 'factoryReady'): + del base.factoryReady + base.localAvatar.inventory.setRespectInvasions(1) + self.fsm.requestFinalState() + self.loader.music.stop() + self.music.stop() + self.ignoreAll() + + def enterWalk(self, teleportIn = 0): + BattlePlace.BattlePlace.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterPush(self): + BattlePlace.BattlePlace.enterPush(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterWaitForBattle(self): + FactoryInterior.notify.info('enterWaitForBattle') + BattlePlace.BattlePlace.enterWaitForBattle(self) + if base.localAvatar.getParent() != render: + base.localAvatar.wrtReparentTo(render) + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + + def exitWaitForBattle(self): + FactoryInterior.notify.info('exitWaitForBattle') + BattlePlace.BattlePlace.exitWaitForBattle(self) + + def enterBattle(self, event): + FactoryInterior.notify.info('enterBattle') + self.music.stop() + BattlePlace.BattlePlace.enterBattle(self, event) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterTownBattle(self, event): + mult = ToontownBattleGlobals.getFactoryCreditMultiplier(self.zoneId) + base.localAvatar.inventory.setBattleCreditMultiplier(mult) + self.loader.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult) + + def exitBattle(self): + FactoryInterior.notify.info('exitBattle') + BattlePlace.BattlePlace.exitBattle(self) + self.loader.music.stop() + base.playMusic(self.music, looping=1, volume=0.8) + + def enterStickerBook(self, page = None): + BattlePlace.BattlePlace.enterStickerBook(self, page) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterSit(self): + BattlePlace.BattlePlace.enterSit(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterZone(self, zoneId): + pass + + def enterTeleportOut(self, requestStatus): + FactoryInterior.notify.info('enterTeleportOut()') + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __processLeaveRequest(self, requestStatus): + hoodId = requestStatus['hoodId'] + if hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def __teleportOutDone(self, requestStatus): + FactoryInterior.notify.info('__teleportOutDone()') + messenger.send('leavingFactory') + messenger.send('localToonLeft') + if self.factoryDefeated and not self.confrontedForeman: + self.fsm.request('FLA', [requestStatus]) + else: + self.__processLeaveRequest(requestStatus) + + def exitTeleportOut(self): + FactoryInterior.notify.info('exitTeleportOut()') + BattlePlace.BattlePlace.exitTeleportOut(self) + + def detectedElevatorCollision(self, distElevator): + self.fsm.request('elevator', [distElevator]) + + def enterElevator(self, distElevator): + self.accept(self.elevatorDoneEvent, self.handleElevatorDone) + self.elevator = Elevator.Elevator(self.fsm.getStateNamed('elevator'), self.elevatorDoneEvent, distElevator) + distElevator.elevatorFSM = self.elevator + self.elevator.load() + self.elevator.enter() + + def exitElevator(self): + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + + def handleElevatorDone(self, doneStatus): + self.notify.debug('handling elevator done event') + where = doneStatus['where'] + if where == 'reject': + if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen(): + pass + else: + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'factoryInterior' or where == 'suitInterior': + self.doneStatus = doneStatus + self.doneEvent = 'lawOfficeFloorDone' + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') + + def handleFactoryWinEvent(self): + FactoryInterior.notify.info('handleFactoryWinEvent') + + if base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'died': + return + + self.factoryDefeated = 1 + + if 1: + zoneId = ZoneUtil.getHoodId(self.zoneId) + else: + zoneId = ZoneUtil.getSafeZoneId(base.localAvatar.defaultZone) + + self.fsm.request('teleportOut', [{ + 'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1, + }]) + + def enterDied(self, requestStatus, callback = None): + FactoryInterior.notify.info('enterDied') + + def diedDone(requestStatus, self = self, callback = callback): + if callback is not None: + callback() + messenger.send('leavingFactory') + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + BattlePlace.BattlePlace.enterDied(self, requestStatus, diedDone) + + def enterFLA(self, requestStatus): + FactoryInterior.notify.info('enterFLA') + self.flaDialog = TTDialog.TTGlobalDialog(message=TTLocalizer.ForcedLeaveFactoryAckMsg, doneEvent='FLADone', style=TTDialog.Acknowledge, fadeScreen=1) + + def continueExit(self = self, requestStatus = requestStatus): + self.__processLeaveRequest(requestStatus) + + self.accept('FLADone', continueExit) + self.flaDialog.show() + + def exitFLA(self): + FactoryInterior.notify.info('exitFLA') + if hasattr(self, 'flaDialog'): + self.flaDialog.cleanup() + del self.flaDialog diff --git a/toontown/coghq/FactoryLevelMgr.py b/toontown/coghq/FactoryLevelMgr.py new file mode 100755 index 00000000..16914472 --- /dev/null +++ b/toontown/coghq/FactoryLevelMgr.py @@ -0,0 +1,36 @@ +from otp.level import LevelMgr +import FactoryUtil +from direct.showbase.PythonUtil import Functor +from toontown.toonbase import ToontownGlobals + +class FactoryLevelMgr(LevelMgr.LevelMgr): + InterestingLocations = [(((-866, -272, -40), -101), + ((-662, -242, 7.5), 0), + ((-20, -180, 20), 0), + ((-249, 258, 111), 0), + ((318, 241, 115), -16), + ((-251, 241, 109), -180), + ((296, 292, 703), 56), + ((-740, 122, 28), 90), + ((210, -270, 38), -90)), (((20, 21, 0), 0), ((3, 404, 39), -16), ((-496, 358, 5), 0))] + + def __init__(self, level, entId): + LevelMgr.LevelMgr.__init__(self, level, entId) + if base.config.GetBool('want-factory-lifter', 0): + self.toonLifter = FactoryUtil.ToonLifter('f3') + self.callSetters('farPlaneDistance') + self.geom.reparentTo(render) + oilRoomOil = self.geom.find('**/oilroom/room/geometry_oilroom/*oil') + oilRoomFloor = self.geom.find('**/oilroom/room/geometry_oilroom/*platform') + if oilRoomOil and not oilRoomOil.isEmpty() and oilRoomFloor and not oilRoomFloor.isEmpty(): + oilRoomOil.setBin('background', 10) + oilRoomFloor.setBin('background', 11) + + def destroy(self): + if hasattr(self, 'toonLifter'): + self.toonLifter.destroy() + del self.toonLifter + LevelMgr.LevelMgr.destroy(self) + + def setFarPlaneDistance(self, farPlaneDistance): + base.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, farPlaneDistance) diff --git a/toontown/coghq/FactoryLevelMgrAI.py b/toontown/coghq/FactoryLevelMgrAI.py new file mode 100755 index 00000000..04eebfab --- /dev/null +++ b/toontown/coghq/FactoryLevelMgrAI.py @@ -0,0 +1,10 @@ +from otp.level import LevelMgrAI + +class FactoryLevelMgrAI(LevelMgrAI.LevelMgrAI): + + def __init__(self, level, entId): + LevelMgrAI.LevelMgrAI.__init__(self, level, entId) + self.callSettersAndDelete('cogLevel') + + def setCogLevel(self, cogLevel): + self.level.cogLevel = cogLevel diff --git a/toontown/coghq/FactoryManagerAI.py b/toontown/coghq/FactoryManagerAI.py new file mode 100755 index 00000000..17e947a6 --- /dev/null +++ b/toontown/coghq/FactoryManagerAI.py @@ -0,0 +1,27 @@ +from direct.directnotify import DirectNotifyGlobal +import DistributedMegaCorpAI +import DistributedFactoryAI +from toontown.toonbase import ToontownGlobals +from direct.showbase import DirectObject + +class FactoryManagerAI(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('FactoryManagerAI') + factoryId = None + + def __init__(self, air): + DirectObject.DirectObject.__init__(self) + self.air = air + + def getDoId(self): + return 0 + + def createFactory(self, factoryId, entranceId, players): + factoryZone = self.air.allocateZone() + if FactoryManagerAI.factoryId is not None: + factoryId = FactoryManagerAI.factoryId + if entranceId == 2: + factory = DistributedMegaCorpAI.DistributedMegaCorpAI(self.air, factoryId, factoryZone, entranceId, players) + else: + factory = DistributedFactoryAI.DistributedFactoryAI(self.air, factoryId, factoryZone, entranceId, players) + factory.generateWithRequired(factoryZone) + return factoryZone diff --git a/toontown/coghq/FactorySpecs.py b/toontown/coghq/FactorySpecs.py new file mode 100755 index 00000000..b753cacb --- /dev/null +++ b/toontown/coghq/FactorySpecs.py @@ -0,0 +1,24 @@ +from toontown.toonbase import ToontownGlobals +import SellbotLegFactorySpec +import SellbotLegFactoryCogs +import LawbotLegFactorySpec +import LawbotLegFactoryCogs + +def getFactorySpecModule(factoryId): + return FactorySpecModules[factoryId] + + +def getCogSpecModule(factoryId): + return CogSpecModules[factoryId] + + +FactorySpecModules = {ToontownGlobals.SellbotFactoryInt: SellbotLegFactorySpec, + ToontownGlobals.LawbotOfficeInt: LawbotLegFactorySpec} +CogSpecModules = {ToontownGlobals.SellbotFactoryInt: SellbotLegFactoryCogs, + ToontownGlobals.LawbotOfficeInt: LawbotLegFactoryCogs} + +if config.GetBool('want-brutal-factory', True): + import SellbotMegaCorpLegSpec + import SellbotMegaCorpLegCogs + FactorySpecModules[ToontownGlobals.SellbotMegaCorpInt] = SellbotMegaCorpLegSpec + CogSpecModules[ToontownGlobals.SellbotMegaCorpInt] = SellbotMegaCorpLegCogs diff --git a/toontown/coghq/FactoryUtil.py b/toontown/coghq/FactoryUtil.py new file mode 100755 index 00000000..05dea08e --- /dev/null +++ b/toontown/coghq/FactoryUtil.py @@ -0,0 +1,77 @@ +from panda3d.core import * +from direct.showbase import DirectObject +from direct.interval.IntervalGlobal import * +from toontown.toonbase import ToontownGlobals +import MovingPlatform +from direct.task.Task import Task +from toontown.suit import Suit +from toontown.suit import SuitDNA + +class Ouch(DirectObject.DirectObject): + + def __init__(self, keyEvent, callback): + DirectObject.DirectObject.__init__(self) + self.accept(keyEvent, callback) + + def destroy(self): + self.ignoreAll() + + +class CyclePlacer(DirectObject.DirectObject): + + def __init__(self, locations, keyEvent, startIndex = 0): + DirectObject.DirectObject.__init__(self) + self.locations = locations + self.index = startIndex + self.accept(keyEvent, self.gotoNextLocation) + + def destroy(self): + self.locations = None + self.ignoreAll() + return + + def gotoNextLocation(self): + self.index = (self.index + 1) % len(self.locations) + self.gotoLocation() + + def gotoLocation(self, index = None): + if index is None: + index = self.index + pos, h = self.locations[index] + base.localAvatar.reparentTo(render) + base.localAvatar.setPos(*pos) + base.localAvatar.setH(h) + return + + +class ToonLifter(DirectObject.DirectObject): + SerialNum = 0 + + def __init__(self, keyDownEvent, speed = 2): + DirectObject.DirectObject.__init__(self) + self.serialNum = ToonLifter.SerialNum + ToonLifter.SerialNum += 1 + self.taskName = 'ToonLifter%s' % self.serialNum + self.keyDownEvent = keyDownEvent + self.keyUpEvent = self.keyDownEvent + '-up' + self.speed = speed + self.accept(self.keyDownEvent, self.startLifting) + + def destroy(self): + self.ignoreAll() + taskMgr.remove(self.taskName) + + def startLifting(self): + + def liftTask(task, self = self): + base.localAvatar.setZ(base.localAvatar.getZ() + self.speed) + return Task.cont + + def stopLifting(self = self): + taskMgr.remove(self.taskName) + self.ignore(self.keyUpEvent) + self.accept(self.keyDownEvent, self.startLifting) + + self.ignore(self.keyDownEvent) + self.accept(self.keyUpEvent, stopLifting) + taskMgr.add(liftTask, self.taskName) diff --git a/toontown/coghq/FoodBeltBase.py b/toontown/coghq/FoodBeltBase.py new file mode 100755 index 00000000..ffeeddc6 --- /dev/null +++ b/toontown/coghq/FoodBeltBase.py @@ -0,0 +1,4 @@ + + +class FoodBeltBase: + NumFoodNodes = 4 diff --git a/toontown/coghq/GameSprite3D.py b/toontown/coghq/GameSprite3D.py new file mode 100755 index 00000000..e0c3b61b --- /dev/null +++ b/toontown/coghq/GameSprite3D.py @@ -0,0 +1,364 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +import math +import random + +class GameSprite: + colorRed = Vec4(1, 0.2, 0.2, 1) + colorBlue = Vec4(0.7, 0.8, 1, 1) + colorGreen = Vec4(0, 1, 0, 1) + colorGhostRed = Vec4(1, 0.2, 0.2, 0.5) + colorGhostBlue = Vec4(0.7, 0.8, 1, 0.5) + colorGhostGreen = Vec4(0, 1, 0, 0.5) + colorDisolveRed = Vec4(1, 0.2, 0.2, 0.0) + colorDisolveBlue = Vec4(0.7, 0.8, 1, 0.0) + colorDisolveGreen = Vec4(0, 1, 0, 0.0) + colorWhite = Vec4(1, 1, 1, 1) + colorBlack = Vec4(0, 0, 0, 1.0) + colorDisolveWhite = Vec4(1, 1, 1, 0.0) + colorDisolveBlack = Vec4(0, 0, 0, 0.0) + colorShadow = Vec4(0, 0, 0, 0.5) + colorPurple = Vec4(1.0, 0, 1.0, 1.0) + colorDisolvePurple = Vec4(1.0, 0, 1.0, 0.0) + colorYellow = Vec4(1.0, 1.0, 0.0, 1.0) + colorDisolveYellow = Vec4(1.0, 1.0, 0.0, 0.0) + colorOrange = Vec4(1.0, 0.5, 0.0, 1.0) + colorDisolveOrange = Vec4(1.0, 0.5, 0.0, 0.0) + colorAqua = Vec4(0.0, 1.0, 1.0, 1.0) + colorDisolveAqua = Vec4(0.0, 1.0, 1.0, 0.0) + colorSteel = Vec4(0.5, 0.5, 0.5, 1.0) + colorSteelDissolve = Vec4(0.5, 0.5, 0.5, 0.0) + colorList = (colorRed, + colorBlue, + colorGreen, + colorWhite, + colorBlack, + colorPurple, + colorYellow, + colorOrange, + colorAqua, + colorSteel) + disolveList = (colorDisolveRed, + colorDisolveBlue, + colorDisolveGreen, + colorDisolveWhite, + colorDisolveBlack, + colorDisolvePurple, + colorDisolveYellow, + colorDisolveOrange, + colorDisolveAqua, + colorSteelDissolve) + + def __init__(self, spriteBase, size, colorType = 0, foundation = 0, facing = 0): + self.colorType = colorType + self.spriteBase = spriteBase + self.frame = self.spriteBase.getParent() + self.foundation = foundation + self.sizeMult = 1.4 + self.velX = 0 + self.velZ = 0 + self.prevX = 0 + self.prevZ = 0 + self.isActive = 0 + self.canCollide = 1 + self.accX = None + self.accZ = None + self.delayRemove = 0 + self.giftId = None + self.holdType = None + self.multiColor = 0 + self.multiColorList = [0, + 1, + 2, + 6] + self.multiColorIndex = 0 + self.multiColorNext = 1 + self.multiColorLevel = 0.0 + self.multiColorStep = 0.025 + self.facing = facing + self.breakable = 1 + self.deleteFlag = 0 + self.nodeObj = None + self.inputSize = size + myColor = GameSprite.colorWhite + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_white' + self.setBallType(colorType) + self.size = 0.4 * self.sizeMult + self.isQue = 0 + self.nodeObj.setTransparency(TransparencyAttrib.MAlpha) + self.markedForDeath = 0 + self.gridPosX = None + self.gridPosZ = None + return + + def setBallType(self, type, solidOverride = 0): + if not self.nodeObj or self.nodeObj.isEmpty(): + self.nodeObj = None + else: + self.nodeObj.removeNode() + colorType = type + self.multiColor = 0 + self.breakable = 1 + solid = self.foundation + if solidOverride: + solid = 1 + myColor = GameSprite.colorWhite + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_white' + if not solid or colorType > 9: + if colorType == 0: + myColor = GameSprite.colorGhostRed + elif colorType == 1: + myColor = GameSprite.colorGhostBlue + elif colorType == 2: + myColor = GameSprite.colorGhostGreen + elif colorType == 3: + myColor = GameSprite.colorWhite + elif colorType == 4: + myColor = GameSprite.colorBlack + elif colorType == 5: + myColor = GameSprite.colorPurple + elif colorType == 6: + myColor = GameSprite.colorYellow + elif colorType == 7: + myColor = GameSprite.colorOrange + self.multiColor = 1 + self.multiColorList = [7, 4] + self.multiColorIndex = 0 + self.multiColorNext = 1 + self.multiColorLevel = 0.0 + self.multiColorStep = 0.1 + elif colorType == 8: + myColor = GameSprite.colorAqua + self.multiColor = 1 + self.multiColorList = [0, + 1, + 2, + 6] + self.multiColorIndex = 0 + self.multiColorNext = 1 + self.multiColorLevel = 0.0 + self.multiColorStep = 0.025 + elif colorType == 9: + myColor = GameSprite.colorSteel + self.breakable = 0 + elif colorType == 10: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_fire' + self.giftId = 7 + self.colorType = 0 + elif colorType == 11: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_unknown' + self.giftId = 8 + self.colorType = 1 + elif colorType == 0: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_red' + elif colorType == 1: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_blue' + elif colorType == 2: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_green' + elif colorType == 3: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_cog' + elif colorType == 4: + myColor = GameSprite.colorBlack + elif colorType == 5: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_purple' + elif colorType == 6: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_yello' + elif colorType == 7: + myColor = GameSprite.colorOrange + self.multiColor = 1 + self.multiColorList = [7, 4] + self.multiColorIndex = 0 + self.multiColorNext = 1 + self.multiColorLevel = 0.0 + self.multiColorStep = 0.15 + elif colorType == 8: + myColor = GameSprite.colorAqua + self.multiColor = 1 + self.multiColorList = [0, + 1, + 2, + 6] + self.multiColorIndex = 0 + self.multiColorNext = 1 + self.multiColorLevel = 0.0 + self.multiColorStep = 0.1 + elif colorType == 9: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_steel' + self.breakable = 0 + elif colorType == 10: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_fire' + self.giftId = 7 + self.colorType = 0 + elif colorType == 11: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_unknown' + self.giftId = 8 + self.colorType = 1 + self.nodeObj = loader.loadModel(myModel) + self.nodeObj.setScale(self.inputSize) + self.nodeObj.reparentTo(self.spriteBase) + self.setColor(myColor) + return + + def removeDelay(self): + self.delayRemove = 0 + + def delete(self): + if not self.delayRemove: + self.spriteBase.removeNode() + self.deleteFlag = 1 + + def face(self): + frameZ = self.frame.getZ() + tilt = -95.0 + (self.getZ() + frameZ) * 2.0 + self.nodeObj.setP(-tilt) + + def runColor(self): + if self.multiColor: + c1 = GameSprite.colorList[self.multiColorList[self.multiColorIndex]] + c2 = GameSprite.colorList[self.multiColorList[self.multiColorNext]] + iLevel = 1.0 - self.multiColorLevel + mixColor = c1 * iLevel + c2 * self.multiColorLevel + self.nodeObj.setColorScale(mixColor) + self.multiColorLevel += self.multiColorStep + if self.multiColorLevel > 1.0: + self.multiColorLevel = 0.0 + self.multiColorIndex += 1 + if self.multiColorIndex >= len(self.multiColorList): + self.multiColorIndex = 0 + self.multiColorNext = self.multiColorIndex + 1 + if self.multiColorNext >= len(self.multiColorList): + self.multiColorNext = 0 + + def run(self, timeDelta): + if self.facing: + self.face() + self.runColor() + if self.isActive and not self.isQue: + self.prevX = self.spriteBase.getX() + self.prevZ = self.spriteBase.getZ() + self.setX(self.getX() + self.velX * timeDelta) + self.setZ(self.getZ() + self.velZ * timeDelta) + self.velX = self.velX * (1 - timeDelta * 4) + self.velZ = self.velZ * (1 - timeDelta * 4) + if self.accX != None: + self.velX = self.accX + self.velZ = self.accZ + if self.nodeObj.isEmpty(): + self.markedForDeath = 1 + return + + def reflectX(self): + self.velX = -self.velX + if self.accX != None: + self.accX = -self.accX + return + + def reflectZ(self): + self.velZ = -self.velZ + if self.accZ != None: + self.accZ = -self.accZ + return + + def warningBump(self): + num1 = random.random() * 2.0 + num2 = random.random() * 2.0 + num3 = random.random() * 2.0 + curr = self.nodeObj.getPos() + dest = Point3(0 + curr[0], 0 + curr[1], 1.0 + curr[2]) + track = Sequence(Wait(num1 * 0.1), LerpPosInterval(self.nodeObj, num2 * 0.1, Point3(0.0, 0.0, 0.5)), LerpPosInterval(self.nodeObj, num3 * 0.1, Point3(0.0, 0.0, 0.0)), LerpPosInterval(self.nodeObj, num2 * 0.1, Point3(0.0, 0.0, 0.5)), LerpPosInterval(self.nodeObj, num1 * 0.1, Point3(0.0, 0.0, 0.0))) + track.start() + + def shake(self): + num1 = random.random() * 1.0 + num2 = random.random() * 1.0 + curr = self.nodeObj.getPos() + dest = Point3(0 + curr[0], 0 + curr[1], 1.0 + curr[2]) + track = Sequence(LerpPosInterval(self.nodeObj, num2 * 0.1, Point3(0.0, 0.0, 0.25)), LerpPosInterval(self.nodeObj, num1 * 0.1, Point3(0.0, 0.0, 0.0))) + track.start() + + def deathEffect(self): + if self.spriteBase.isEmpty(): + return + self.spriteBase.wrtReparentTo(render) + num1 = (random.random() - 0.5) * 1.0 + num2 = random.random() * 1.0 + num3 = random.random() * 1.0 + notNum3 = 1.0 - num3 + curr = self.spriteBase.getPos() + self.delayRemove = 1 + self.canCollide = 0 + track = Sequence(Parallel(ProjectileInterval(self.spriteBase, startVel=Vec3(-20.0 + notNum3 * 40.0, -20.0 + num3 * 40.0, 30), duration=0.5 + num2 * 1.0, gravityMult=2.0), LerpColorScaleInterval(self.spriteBase, duration=0.5 + num2 * 1.0, startColorScale=GameSprite.colorList[self.colorType], colorScale=GameSprite.disolveList[self.colorType])), Func(self.removeDelay), Func(self.delete)) + track.start() + + def wildEffect(self): + if self.spriteBase.isEmpty(): + return + num1 = (random.random() - 0.5) * 1.0 + num2 = random.random() * 1.0 + num3 = random.random() * 1.0 + notNum3 = 1.0 - num3 + curr = self.spriteBase.getPos() + self.delayRemove = 1 + self.canCollide = 0 + track = Sequence(Parallel(LerpScaleInterval(self.spriteBase, 1.0, 1.5, startScale=1.0), LerpColorScaleInterval(self.spriteBase, duration=1.0, startColorScale=GameSprite.colorList[self.colorType], colorScale=Vec4(0, 0, 0, 0.0))), Func(self.removeDelay), Func(self.delete)) + track.start() + + def setActive(self, active): + if active: + self.isActive = 1 + else: + self.isActive = 0 + self.velX = 0 + self.velZ = 0 + self.accX = None + self.accZ = None + return + + def getX(self): + if self.nodeObj.isEmpty(): + return None + return self.spriteBase.getX() + + def getZ(self): + if self.nodeObj.isEmpty(): + return None + return self.spriteBase.getZ() + + def setX(self, x): + if self.nodeObj.isEmpty(): + return None + self.prevX = self.spriteBase.getX() + self.spriteBase.setX(x) + return None + + def setZ(self, z): + if self.nodeObj.isEmpty(): + return None + self.prevZ = self.spriteBase.getZ() + self.spriteBase.setZ(z) + return None + + def addForce(self, force, direction): + if self.isActive: + forceX = math.cos(direction) * force + forceZ = math.sin(direction) * force + self.velX += forceX + self.velZ += forceZ + + def setAccel(self, accel, direction): + accelX = math.cos(direction) * accel + accelZ = math.sin(direction) * accel + self.accX = accelX + self.accZ = accelZ + + def setColorType(self, typeIndex): + self.colorType = typeIndex + self.setColor(GameSprite.colorList[typeIndex]) + + def setColor(self, trip): + self.nodeObj.setColorScale(trip[0], trip[1], trip[2], trip[3]) + + def collide(self): + if self.isActive: + self.setX(self.prevX) + self.setZ(self.prevZ) diff --git a/toontown/coghq/GearEntity.py b/toontown/coghq/GearEntity.py new file mode 100755 index 00000000..27569f1d --- /dev/null +++ b/toontown/coghq/GearEntity.py @@ -0,0 +1,85 @@ +from direct.interval.IntervalGlobal import * +from otp.level import BasicEntities +import MovingPlatform +from pandac.PandaModules import Vec3 + +class GearEntity(BasicEntities.NodePathEntity): + ModelPaths = {'factory': 'phase_9/models/cogHQ/FactoryGearB', + 'mint': 'phase_10/models/cashbotHQ/MintGear'} + + def __init__(self, level, entId): + self.modelType = 'factory' + self.entInitialized = False + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.entInitialized = True + self.initGear() + + def destroy(self): + self.destroyGear() + BasicEntities.NodePathEntity.destroy(self) + + def initGear(self): + if hasattr(self, 'in_initGear'): + return + self.in_initGear = True + self.destroyGear() + model = loader.loadModel(GearEntity.ModelPaths[self.modelType]) + self.gearParent = self.attachNewNode('gearParent-%s' % self.entId) + if self.orientation == 'horizontal': + vertNodes = model.findAllMatches('**/VerticalCollisions') + for node in vertNodes: + node.stash() + + mPlat = MovingPlatform.MovingPlatform() + mPlat.setupCopyModel(self.getParentToken(), model, 'HorizontalFloor') + model = mPlat + else: + horizNodes = model.findAllMatches('**/HorizontalCollisions') + for node in horizNodes: + node.stash() + + model.setZ(0.15) + model.flattenLight() + model.setScale(self.gearScale) + model.flattenLight() + model.setScale(self.getScale()) + self.setScale(1) + model.flattenLight() + if self.orientation == 'vertical': + self.gearParent.setP(-90) + self.model = model + self.model.reparentTo(self.gearParent) + self.startRotate() + del self.in_initGear + + def destroyGear(self): + self.stopRotate() + if hasattr(self, 'model'): + if isinstance(self.model, MovingPlatform.MovingPlatform): + self.model.destroy() + else: + self.model.removeNode() + del self.model + if hasattr(self, 'gearParent'): + self.gearParent.removeNode() + del self.gearParent + + def startRotate(self): + self.stopRotate() + try: + ivalDur = 360.0 / self.degreesPerSec + except ZeroDivisionError: + pass + else: + hOffset = 360.0 + if ivalDur < 0.0: + ivalDur = -ivalDur + hOffset = -hOffset + self.rotateIval = LerpHprInterval(self.model, ivalDur, Vec3(hOffset, 0, 0), startHpr=Vec3(0, 0, 0), name='gearRot-%s' % self.entId) + self.rotateIval.loop() + self.rotateIval.setT(globalClock.getFrameTime() - self.level.startTime + ivalDur * self.phaseShift) + + def stopRotate(self): + if hasattr(self, 'rotateIval'): + self.rotateIval.pause() + del self.rotateIval diff --git a/toontown/coghq/GolfGreenGameGlobals.py b/toontown/coghq/GolfGreenGameGlobals.py new file mode 100755 index 00000000..09255e15 --- /dev/null +++ b/toontown/coghq/GolfGreenGameGlobals.py @@ -0,0 +1,18 @@ +gameBoards = [ + ('rbygl', 'r_______b', 'byyywgggr', 'r_______b', 'bggyyyggr', 'r_______b', 'byylllyyr', 'r_______b', 'Blllllllr'), + ('rgby', 'rrrgggbbb', 'rwrgggbwb', 'rrryyybbb', 'bByyyyyRr'), + ('bygr', 'b_y_b_y__', 'b_y_b_yw_', '_b_y_b_y_', '_b_y_b_g_', '__b_g_r__', '__b_g_r__', '___b_g_r_', '___b_g_R_'), + ('rgb', '_b__g____', '_r__g____', '_r__r____', '_w__R____'), + ('lbryg', '___bbb___', '___bwb___', '__lgggr__', 'rrgggggll', '_ybbbbby_', '__ryyyr__', '_________', '_________'), + ('lbryg', 'l_rr__b__', 'l__w__b__', 'b__b__b__', 'b__b__r__', 'l__g__r__', 'l__g__b__', 'y__y__B__', '_________'), + ('byr', 'R_______y', 'ygggwgggR', 'B_______B', 'Rgggggggy', 'y_______R', 'BgggggggB', 'R_______y', 'BgggggggR'), + ('bygr', '____bb___', '___bwb___', '_y__y____', '_y__y____', '_yggg_bbb', '____y____', '____y____', '__b_y__b_', '__b_y__b_', '__rrRrrr_'), + ('bryg', 'b_yr_by_r', 'by_rb_yr_', 'r_gbwrg_y', 'rg_br_gy_', 'b_yg_yr_b', 'by_gy_Rb_', '_________', '_________'), + ('lyg', '__lyyyyl_', '__lywyl__', '___lyyl__', '___lyl___', '____ll___', '__ygyg___', 'lyl___lyl', '_________'), + ('rgbyl', 'l_______r', 'brbrbw__r', 'r_______r', 'r__ylblbl', 'r_______b', 'lglgly__b', '________b', '___yrgrgr'), + ('rgbyl', 'b_______r', 'bbbw_wrrr', '___b__r__', '__b__r___', '___b__r__', '__g__y___', '___y__g__', 'rrrr_bbbb', 'lbyy_ggrl'), + ('yrbg', 'ry_____yb', '_yrwwby__', '___yyy___', '_rl__gb__', 'lr_____bg', 'ylyg_gyly', '_________', '_________'), + ('bylr', 'rrr_r_r_r', '_w__r_r_r', '____r_r_r', 'ggggR_r_r', '______r_r', 'ggggggR_r', '________r', 'ggggggggR', '_________'), + ('rgbl', '__y_bb_y_', '_y__w_y__', 'yyy____yy', 'y_gyyyr_y', 'y_______y', 'byyyyyyl_', '_________', '_________', '_________', '_________'), + ('o', 'b_bb_bb_b', 'wb_bb_bw_', 'b_bbbbb_b', 'bb_bb_bb_', 'b_bb_bb_b', 'bb_bb_bb_', '_________', '_________'), + ('oa', 's________', 'sw_______', 's________', 's________', 's________', 's________', 'ssssss___', '_________')] diff --git a/toontown/coghq/GoonClipPlane.py b/toontown/coghq/GoonClipPlane.py new file mode 100755 index 00000000..31bfb0ad --- /dev/null +++ b/toontown/coghq/GoonClipPlane.py @@ -0,0 +1,38 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from otp.level import BasicEntities + +class GoonClipPlane(BasicEntities.NodePathEntity): + + def __init__(self, level, entId): + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.zoneNum = self.getZoneEntity().getZoneNum() + self.initPlane() + self.registerWithFactory() + + def destroy(self): + self.unregisterWithFactory() + BasicEntities.NodePathEntity.destroy(self) + self.removeNode() + + def registerWithFactory(self): + clipList = self.level.goonClipPlanes.get(self.zoneNum) + if clipList: + if self.entId not in clipList: + clipList.append(self.entId) + else: + self.level.goonClipPlanes[self.zoneNum] = [self.entId] + + def unregisterWithFactory(self): + clipList = self.level.goonClipPlanes.get(self.zoneNum) + if clipList: + if self.entId in clipList: + clipList.remove(self.entId) + + def initPlane(self): + self.coneClip = PlaneNode('coneClip') + self.coneClip.setPlane(Plane(Vec3(1, 0, 0), Point3(0, 0, 0))) + self.coneClipPath = self.attachNewNode(self.coneClip) + + def getPlane(self): + return self.coneClipPath diff --git a/toontown/coghq/LaserGameAvoid.py b/toontown/coghq/LaserGameAvoid.py new file mode 100755 index 00000000..314e8753 --- /dev/null +++ b/toontown/coghq/LaserGameAvoid.py @@ -0,0 +1,81 @@ +import random + +from direct.distributed import ClockDelta +from direct.task import Task +from toontown.coghq import LaserGameBase + + +class LaserGameAvoid(LaserGameBase.LaserGameBase): + def __init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid): + LaserGameBase.LaserGameBase.__init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid) + self.setGridSize(8, 8) + self.blankGrid() + self.cycleName = simbase.air.trueUniqueName('AvoidGame') + + + def delete(self): + LaserGameBase.LaserGameBase.delete(self) + self.endTask() + + + def win(self): + if not self.finshed: + self.blankGrid() + self.funcSendGrid() + self.endTask() + + LaserGameBase.LaserGameBase.win(self) + + + def lose(self): + self.endTask() + self.blankGrid() + self.funcSendGrid() + LaserGameBase.LaserGameBase.lose(self) + + + def endTask(self): + taskMgr.remove(self.cycleName) + + + def startGrid(self): + LaserGameBase.LaserGameBase.startGrid(self) + for column in xrange(0, self.gridNumX): + for row in xrange(0, self.gridNumY): + tile = random.choice([ + 0, + 14, + 12]) + self.gridData[column][row] = tile + + + taskMgr.doMethodLater(2.5, self._LaserGameAvoid__cycle, self.cycleName) + + + def _LaserGameAvoid__cycle(self, taskMgrFooler = 0): + if not hasattr(self, 'gridNumX'): + return Task.done + + for column in xrange(0, self.gridNumX): + for row in xrange(0, self.gridNumY): + if self.gridData[column][row] == 0: + tile = random.choice([ + 0, + 14]) + self.gridData[column][row] = tile + continue + if self.gridData[column][row] == 14: + tile = 12 + self.gridData[column][row] = tile + continue + if self.gridData[column][row] == 12: + tile = 0 + self.gridData[column][row] = tile + continue + + + if not self.finshed: + taskMgr.doMethodLater(2.5, self._LaserGameAvoid__cycle, self.cycleName) + self.funcSendGrid() + + return Task.done diff --git a/toontown/coghq/LaserGameBase.py b/toontown/coghq/LaserGameBase.py new file mode 100755 index 00000000..16e44eac --- /dev/null +++ b/toontown/coghq/LaserGameBase.py @@ -0,0 +1,67 @@ +import random + +from direct.distributed import ClockDelta +from direct.task import Task + + +class LaserGameBase: + def __init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid): + self.funcSuccess = funcSuccess + self.funcFail = funcFail + self.funcSendGrid = funcSendGrid + self.funcSetGrid = funcSetGrid + self.setGridSize(2, 2) + self.blankGrid() + self.finshed = 0 + + + def delete(self): + funcSuccess = None + funcFail = None + funcSendGrid = None + funcSetGrid = None + + + def setGridSize(self, x, y): + self.gridNumX = x + self.gridNumY = y + + + def blankGrid(self): + self.gridData = [] + for i in xrange(0, self.gridNumX): + self.gridData.append([ + 0] * self.gridNumY) + + + + def win(self): + if not self.finshed: + self.finshed = 1 + self.funcSuccess() + + + + def lose(self): + if not self.finshed: + self.finshed = 1 + self.funcFail() + + + + def startGrid(self): + self.blankGrid() + + + def hit(self, hitX, hitY, oldx = -1, oldy = -1): + if self.finshed: + return None + + if self.checkForWin(): + self.win() + else: + self.funcSendGrid() + + + def checkForWin(self): + return 0 diff --git a/toontown/coghq/LaserGameDrag.py b/toontown/coghq/LaserGameDrag.py new file mode 100755 index 00000000..7ec8479e --- /dev/null +++ b/toontown/coghq/LaserGameDrag.py @@ -0,0 +1,117 @@ +from toontown.coghq import LaserGameBase +from direct.distributed import ClockDelta +from direct.task import Task +import random + +class LaserGameDrag(LaserGameBase.LaserGameBase): + + def __init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid): + LaserGameBase.LaserGameBase.__init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid) + self.setGridSize(6, 6) + self.blankGrid() + self.symbolList = [16, 13, 17] + + + def win(self): + if not self.finshed: + self.blankGrid() + self.funcSendGrid() + + LaserGameBase.LaserGameBase.win(self) + + + def lose(self): + self.blankGrid() + self.funcSendGrid() + LaserGameBase.LaserGameBase.lose(self) + + + def startGrid(self): + LaserGameBase.LaserGameBase.startGrid(self) + for column in xrange(0, self.gridNumX): + for row in xrange(0, self.gridNumY): + tile = 0 + self.gridData[column][row] = tile + for column in xrange(0, self.gridNumX): + self.gridData[column][self.gridNumY - 1] = 12 + for symbol in self.symbolList: + finished = 0 + while finished == 0: + numTris = 4 + tris = 0 + sanity = 1000 + if numTris >= 1: + while tris < numTris and sanity: + sanity -= 1 + column = random.randint(0, self.gridNumX - 1) + row = random.randint(1, self.gridNumY - 1) + if self.gridData[column][row] == 0: + self.gridData[column][row] = symbol + tris += 1 + continue + if self.checkFor3(symbol): + self.clearIndex(symbol) + finished = 0 + else: + finished = 1 + + def hit(self, hitX, hitY, oldx = -1, oldy = -1): + if self.finshed: + return + if oldx >= 0 and oldy >= 0: + if self.gridData[hitX][hitY] == 0: + if self.gridData[oldx][oldy] in self.symbolList: + self.gridData[hitX][hitY] = self.gridData[oldx][oldy] + self.gridData[oldx][oldy] = 0 + else: + pass + for index in self.symbolList: + if self.checkFor3(index): + self.clearIndex(index) + if self.checkForWin(): + self.win() + else: + self.funcSendGrid() + + def checkFor3(self, index): + numInARow = 0 + for posX in xrange(0, self.gridNumX): + for posY in xrange(0, self.gridNumY): + if self.gridData[posX][posY] == index: + numInARow += 1 + if numInARow >= 3: + return 1 + else: + numInARow = 0 + numInARow = 0 + + numInARow = 0 + for posY in xrange(0, self.gridNumY): + for posX in xrange(0, self.gridNumX): + if self.gridData[posX][posY] == index: + numInARow += 1 + if numInARow >= 3: + return 1 + else: + numInARow = 0 + numInARow = 0 + return 0 + + def clearIndex(self, index): + for posX in xrange(0, self.gridNumX): + for posY in xrange(0, self.gridNumY): + if self.gridData[posX][posY] == index: + self.gridData[posX][posY] = 0 + + def checkForClear(self, index): + for posX in xrange(0, self.gridNumX): + for posY in xrange(0, self.gridNumY): + if self.gridData[posX][posY] == index: + return 0 + return 1 + + def checkForWin(self): + for symbol in self.symbolList: + if not self.checkForClear(symbol): + return 0 + return 1 diff --git a/toontown/coghq/LaserGameMineSweeper.py b/toontown/coghq/LaserGameMineSweeper.py new file mode 100755 index 00000000..4cb079f1 --- /dev/null +++ b/toontown/coghq/LaserGameMineSweeper.py @@ -0,0 +1,169 @@ +import random + +from direct.distributed import ClockDelta +from direct.task import Task +from toontown.coghq import LaserGameBase + + +class LaserGameMineSweeper(LaserGameBase.LaserGameBase): + def __init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid): + LaserGameBase.LaserGameBase.__init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid) + self.setGridSize(7, 7) + self.blankGrid() + + + def win(self): + if not self.finshed: + self.blankGrid() + self.funcSendGrid() + + LaserGameBase.LaserGameBase.win(self) + + + def lose(self): + if not self.finshed: + self.revealAll() + self.funcSendGrid() + + LaserGameBase.LaserGameBase.lose(self) + + + def startGrid(self): + LaserGameBase.LaserGameBase.startGrid(self) + self.hiddenData = [] + for i in xrange(0, self.gridNumX): + self.hiddenData.append([ + 0] * self.gridNumY) + + numBombs = int(self.gridNumX * self.gridNumY / 8) + numBombs += 1 + bomb = 0 + sanity = 1000 + if numBombs > 1: + while bomb < numBombs and sanity: + sanity -= 1 + column = random.randint(0, self.gridNumX - 1) + row = random.randint(1, self.gridNumY - 1) + if self.hiddenData[column][row] != 12 and self.neighborSum(column, row) < 2 and self.rowSum(row) < numBombs / 3: + self.hiddenData[column][row] = 12 + bomb += 1 + continue + + for column in xrange(0, self.gridNumX): + for row in xrange(0, self.gridNumY): + if self.hiddenData[column][row] == 12: + self.gridData[column][row] = 11 + continue + self.gridData[column][row] = 10 + + + + + def hit(self, hitX, hitY, oldx = -1, oldy = -1): + if self.finshed: + return None + + if self.hiddenData[hitX][hitY] == 12: + self.gridData[hitX][hitY] = 12 + else: + self.neighborReveal(hitX, hitY) + self.funcSendGrid() + + + def revealAll(self): + for column in xrange(0, self.gridNumX): + for row in xrange(0, self.gridNumY): + self.neighborReveal(column, row, 1) + + + + + def neighborReveal(self, hitX, hitY, showBomb = 0): + if showBomb and self.gridData[hitX][hitY] == 11: + self.gridData[hitX][hitY] = 12 + + if self.gridData[hitX][hitY] != 10: + return None + + self.gridData[hitX][hitY] = self.neighborSum(hitX, hitY) + if self.neighborSum(hitX, hitY) == 0: + if hitX > 0 and hitY > 0: + self.neighborReveal(hitX - 1, hitY - 1) + + if hitY > 0: + self.neighborReveal(hitX, hitY - 1) + + if hitX < self.gridNumX - 1 and hitY > 0: + self.neighborReveal(hitX + 1, hitY - 1) + + if hitX > 0: + self.neighborReveal(hitX - 1, hitY) + + if hitX < self.gridNumX - 1: + self.neighborReveal(hitX + 1, hitY) + + if hitX > 0 and hitY < self.gridNumY - 1: + self.neighborReveal(hitX - 1, hitY + 1) + + if hitY < self.gridNumY - 1: + self.neighborReveal(hitX, hitY + 1) + + if hitX < self.gridNumX - 1 and hitY < self.gridNumY - 1: + self.neighborReveal(hitX + 1, hitY + 1) + + + + + def rowSum(self, y): + sum = 0 + for i in xrange(0, self.gridNumX - 1): + if self.hiddenData[i][y] == 12: + sum += 1 + continue + + return sum + + + def neighborSum(self, hitX, hitY): + sum = 0 + if hitX > 0 and hitY > 0: + if self.hiddenData[hitX - 1][hitY - 1] == 12: + sum += 1 + + + if hitY > 0: + if self.hiddenData[hitX][hitY - 1] == 12: + sum += 1 + + + if hitX < self.gridNumX - 1 and hitY > 0: + if self.hiddenData[hitX + 1][hitY - 1] == 12: + sum += 1 + + + if hitX > 0: + if self.hiddenData[hitX - 1][hitY] == 12: + sum += 1 + + + if hitX < self.gridNumX - 1: + if self.hiddenData[hitX + 1][hitY] == 12: + sum += 1 + + + if hitX > 0 and hitY < self.gridNumY - 1: + if self.hiddenData[hitX - 1][hitY + 1] == 12: + sum += 1 + + + if hitY < self.gridNumY - 1: + if self.hiddenData[hitX][hitY + 1] == 12: + sum += 1 + + + if hitX < self.gridNumX - 1 and hitY < self.gridNumY - 1: + if self.hiddenData[hitX + 1][hitY + 1] == 12: + sum += 1 + + + return sum diff --git a/toontown/coghq/LaserGameRoll.py b/toontown/coghq/LaserGameRoll.py new file mode 100755 index 00000000..085fc0fe --- /dev/null +++ b/toontown/coghq/LaserGameRoll.py @@ -0,0 +1,75 @@ +import random + +from direct.distributed import ClockDelta +from direct.task import Task +from toontown.coghq import LaserGameBase + + +class LaserGameRoll(LaserGameBase.LaserGameBase): + def __init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid): + LaserGameBase.LaserGameBase.__init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid) + self.setGridSize(5, 5) + self.blankGrid() + + + def win(self): + if not self.finshed: + self.blankGrid() + self.funcSendGrid() + + LaserGameBase.LaserGameBase.win(self) + + + def lose(self): + self.blankGrid() + self.funcSendGrid() + LaserGameBase.LaserGameBase.lose(self) + + + def startGrid(self): + LaserGameBase.LaserGameBase.startGrid(self) + for column in xrange(0, self.gridNumX): + for row in xrange(0, self.gridNumY): + tile = random.choice([ + 10, + 13]) + self.gridData[column][row] = tile + + + for column in xrange(0, self.gridNumX): + self.gridData[column][self.gridNumY - 1] = 12 + + + + def hit(self, hitX, hitY, oldx = -1, oldy = -1): + if self.finshed: + return None + + if self.gridData[hitX][hitY] == 10: + self.gridData[hitX][hitY] = 13 + elif self.gridData[hitX][hitY] == 13: + self.gridData[hitX][hitY] = 10 + + if self.checkForWin(): + self.win() + else: + self.funcSendGrid() + + + def checkForWin(self): + count1 = 0 + count2 = 0 + for column in xrange(0, self.gridNumX): + for row in xrange(0, self.gridNumY): + if self.gridData[column][row] == 10: + count1 += 1 + continue + if self.gridData[column][row] == 13: + count2 += 1 + continue + + + if count1 and count2: + return 0 + else: + return 1 diff --git a/toontown/coghq/LawOfficeBase.py b/toontown/coghq/LawOfficeBase.py new file mode 100755 index 00000000..2384db82 --- /dev/null +++ b/toontown/coghq/LawOfficeBase.py @@ -0,0 +1,19 @@ +import FactorySpecs +from otp.level import LevelSpec +from toontown.toonbase import ToontownGlobals + +class LawOfficeBase: + + def __init__(self): + pass + + def setLawOfficeId(self, factoryId): + self.lawOfficeId = factoryId + self.factoryType = ToontownGlobals.factoryId2factoryType[factoryId] + self.cogTrack = ToontownGlobals.cogHQZoneId2dept(factoryId) + + def getCogTrack(self): + return self.cogTrack + + def getFactoryType(self): + return self.factoryType diff --git a/toontown/coghq/LawOfficeLayout.py b/toontown/coghq/LawOfficeLayout.py new file mode 100755 index 00000000..5de7ac5a --- /dev/null +++ b/toontown/coghq/LawOfficeLayout.py @@ -0,0 +1,43 @@ +import random + +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.PythonUtil import invertDictLossless +from direct.showbase.PythonUtil import normalDistrib, lerp +from toontown.coghq import MintRoomSpecs +from toontown.toonbase import ToontownGlobals + + +OfficeBuildingFloorSequences = { + 13300: [ + (0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0)] } +Index2Spec = { + 0: 'LawOffice_Spec_Tier0_a', + 1: 'LawOffice_Spec_Tier0_b' } +LawbotFloorSpecs = {} +for floorIndex, floorSpec in Index2Spec.items(): + LawbotFloorSpecs[floorIndex] = __import__('toontown.coghq.' + floorSpec) + + +class LawOfficeLayout: + notify = DirectNotifyGlobal.directNotify.newCategory('MintLayout') + + def __init__(self, lawOfficeId): + self.lawOfficeId = lawOfficeId + self.floorIds = [] + if self.lawOfficeId in OfficeBuildingFloorSequences: + self.floorIds = OfficeBuildingFloorSequences[self.lawOfficeId][random.randint(0, len(OfficeBuildingFloorSequences[self.lawOfficeId])) - 1] + else: + self.notify.warning('no layout for Law Office ID: using defaults') + self.floorIds = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + + + def getFloorId(self, index): + return self.floorIds[index] + + + def getFloorSpec(self, index): + return LawbotFloorSpecs[self.floorIds[index]] + + + def getFloorIds(self): + return self.floorIds diff --git a/toontown/coghq/LawOfficeManagerAI.py b/toontown/coghq/LawOfficeManagerAI.py new file mode 100755 index 00000000..b407918c --- /dev/null +++ b/toontown/coghq/LawOfficeManagerAI.py @@ -0,0 +1,55 @@ +import random + +import DistributedLawOfficeAI +import DistributedStageAI +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import DirectObject +from toontown.coghq import StageLayout +from toontown.toonbase import ToontownGlobals + + +StageId2Layouts = { + ToontownGlobals.LawbotStageIntA: (0, 1, 2), + ToontownGlobals.LawbotStageIntB: (3, 4, 5), + ToontownGlobals.LawbotStageIntC: (6, 7, 8), + ToontownGlobals.LawbotStageIntD: (9, 10, 11) +} + + +class LawOfficeManagerAI(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('LawOfficeManagerAI') + lawOfficeId = None + + def __init__(self, air): + DirectObject.DirectObject.__init__(self) + self.air = air + + def getDoId(self): + return 0 + + def createLawOffice(self, StageId, entranceId, players): + for avId in players: + if bboard.has('StageId-%s' % avId): + StageId = bboard.get('StageId-%s' % avId) + break + floor = 0 + layoutIndex = None + for avId in players: + if bboard.has('stageRoom-%s' % avId): + roomId = bboard.get('stageRoom-%s' % avId) + for lt in StageId2Layouts[StageId]: + for i in xrange(StageLayout.getNumFloors(lt)): + layout = StageLayout.StageLayout(StageId, i, stageLayout = lt) + if roomId in layout.getRoomIds(): + layoutIndex = lt + floor = i + else: + StageRoomSpecs = StageRoomSpecs + roomName = StageRoomSpecs.CashbotStageRoomId2RoomName[roomId] + LawOfficeManagerAI.notify.warning('room %s (%s) not found in any floor of Stage %s' % (roomId, roomName, StageId)) + StageZone = self.air.allocateZone() + if layoutIndex is None: + layoutIndex = random.choice(StageId2Layouts[StageId]) + Stage = DistributedStageAI.DistributedStageAI(self.air, StageId, StageZone, floor, players, layoutIndex) + Stage.generateWithRequired(StageZone) + return StageZone diff --git a/toontown/coghq/LawOffice_Spec_Tier0_a.py b/toontown/coghq/LawOffice_Spec_Tier0_a.py new file mode 100755 index 00000000..853b8df6 --- /dev/null +++ b/toontown/coghq/LawOffice_Spec_Tier0_a.py @@ -0,0 +1,36 @@ +from toontown.coghq.SpecImports import * + + +GlobalEntities = { + 1000: { + 'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/lawbotHQ/LawbotCourtroom3', + 'wantDoors': 1 }, + 0: { + 'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [] }, + 100000: { + 'type': 'laserField', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(68.722499999999997, -27.415400000000002, 71), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'laserFactor': 3, + 'modelPath': 0 } } +Scenario0 = { } +levelSpec = { + 'globalEntities': GlobalEntities, + 'scenarios': [ + Scenario0] } diff --git a/toontown/coghq/LawOffice_Spec_Tier0_b.py b/toontown/coghq/LawOffice_Spec_Tier0_b.py new file mode 100755 index 00000000..ea9296be --- /dev/null +++ b/toontown/coghq/LawOffice_Spec_Tier0_b.py @@ -0,0 +1,54 @@ +from toontown.coghq.SpecImports import * + + +GlobalEntities = { + 1000: { + 'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/lawbotHQ/LawbotCourtroom3', + 'wantDoors': 1 }, + 0: { + 'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [] }, + 100000: { + 'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(3.8002199999999999, -32.657400000000003, 70), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Vec3(1, 1, 1), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Vec3(1, 1, 1), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0 } } +Scenario0 = { } +levelSpec = { + 'globalEntities': GlobalEntities, + 'scenarios': [ + Scenario0] } diff --git a/toontown/coghq/LawbotCogHQLoader.py b/toontown/coghq/LawbotCogHQLoader.py new file mode 100755 index 00000000..6526bc5d --- /dev/null +++ b/toontown/coghq/LawbotCogHQLoader.py @@ -0,0 +1,116 @@ +import CogHQLoader +import LawbotHQBossBattle +import LawbotHQExterior +import LawbotOfficeExterior +import StageInterior +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import State +from direct.fsm import StateData +from direct.gui import DirectGui +from toontown.toon import Toon +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals + + +aspectSF = 0.7227 + +class LawbotCogHQLoader(CogHQLoader.CogHQLoader): + notify = DirectNotifyGlobal.directNotify.newCategory('LawbotCogHQLoader') + + def __init__(self, hood, parentFSMState, doneEvent): + CogHQLoader.CogHQLoader.__init__(self, hood, parentFSMState, doneEvent) + self.fsm.addState(State.State('stageInterior', self.enterStageInterior, self.exitStageInterior, ['quietZone', 'cogHQExterior'])) + self.fsm.addState(State.State('factoryExterior', self.enterFactoryExterior, self.exitFactoryExterior, ['quietZone', 'cogHQExterior'])) + for stateName in ['start', 'cogHQExterior', 'quietZone']: + state = self.fsm.getStateNamed(stateName) + state.addTransition('stageInterior') + + for stateName in ['quietZone']: + state = self.fsm.getStateNamed(stateName) + state.addTransition('factoryExterior') + + self.musicFile = 'phase_11/audio/bgm/LB_courtyard.ogg' + self.cogHQExteriorModelPath = 'phase_11/models/lawbotHQ/LawbotPlaza' + self.factoryExteriorModelPath = 'phase_11/models/lawbotHQ/LB_DA_Lobby' + self.cogHQLobbyModelPath = 'phase_11/models/lawbotHQ/LB_CH_Lobby' + self.geom = None + + def load(self, zoneId): + CogHQLoader.CogHQLoader.load(self, zoneId) + Toon.loadSellbotHQAnims() + + def unloadPlaceGeom(self): + if self.geom: + self.geom.removeNode() + self.geom = None + CogHQLoader.CogHQLoader.unloadPlaceGeom(self) + + def loadPlaceGeom(self, zoneId): + self.notify.info('loadPlaceGeom: %s' % zoneId) + zoneId = zoneId - zoneId % 100 + self.notify.debug('zoneId = %d ToontownGlobals.LawbotHQ=%d' % (zoneId, ToontownGlobals.LawbotHQ)) + if zoneId == ToontownGlobals.LawbotHQ: + self.geom = loader.loadModel(self.cogHQExteriorModelPath) + ug = self.geom.find('**/underground') + ug.setBin('ground', -10) + brLinkTunnel = self.geom.find('**/TunnelEntrance1') + brLinkTunnel.setName('linktunnel_br_3326_DNARoot') + elif zoneId == ToontownGlobals.LawbotOfficeExt: + self.geom = loader.loadModel(self.factoryExteriorModelPath) + ug = self.geom.find('**/underground') + ug.setBin('ground', -10) + self.geom.flattenMedium() + elif zoneId == ToontownGlobals.LawbotLobby: + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: COGHQ: Visit LawbotLobby') + self.notify.debug('cogHQLobbyModelPath = %s' % self.cogHQLobbyModelPath) + self.geom = loader.loadModel(self.cogHQLobbyModelPath) + ug = self.geom.find('**/underground') + ug.setBin('ground', -10) + else: + self.notify.warning('loadPlaceGeom: unclassified zone %s' % zoneId) + CogHQLoader.CogHQLoader.loadPlaceGeom(self, zoneId) + + def unload(self): + CogHQLoader.CogHQLoader.unload(self) + Toon.unloadSellbotHQAnims() + + def enterStageInterior(self, requestStatus): + self.placeClass = StageInterior.StageInterior + self.stageId = requestStatus['stageId'] + self.enterPlace(requestStatus) + + def exitStageInterior(self): + self.exitPlace() + self.placeClass = None + return + + def getExteriorPlaceClass(self): + self.notify.debug('getExteriorPlaceClass') + return LawbotHQExterior.LawbotHQExterior + + def getBossPlaceClass(self): + self.notify.debug('getBossPlaceClass') + return LawbotHQBossBattle.LawbotHQBossBattle + + def enterFactoryExterior(self, requestStatus): + self.placeClass = LawbotOfficeExterior.LawbotOfficeExterior + self.enterPlace(requestStatus) + self.hood.spawnTitleText(requestStatus['zoneId']) + + def exitFactoryExterior(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + self.exitPlace() + self.placeClass = None + return + + def enterCogHQBossBattle(self, requestStatus): + self.notify.debug('LawbotCogHQLoader.enterCogHQBossBattle') + CogHQLoader.CogHQLoader.enterCogHQBossBattle(self, requestStatus) + base.cr.forbidCheesyEffects(1) + + def exitCogHQBossBattle(self): + self.notify.debug('LawbotCogHQLoader.exitCogHQBossBattle') + CogHQLoader.CogHQLoader.exitCogHQBossBattle(self) + base.cr.forbidCheesyEffects(0) diff --git a/toontown/coghq/LawbotHQBossBattle.py b/toontown/coghq/LawbotHQBossBattle.py new file mode 100755 index 00000000..51805d49 --- /dev/null +++ b/toontown/coghq/LawbotHQBossBattle.py @@ -0,0 +1,24 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.suit import DistributedLawbotBoss +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import CogHQBossBattle + +class LawbotHQBossBattle(CogHQBossBattle.CogHQBossBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('LawbotHQBossBattle') + + def __init__(self, loader, parentFSM, doneEvent): + CogHQBossBattle.CogHQBossBattle.__init__(self, loader, parentFSM, doneEvent) + self.teleportInPosHpr = (88, -214, 0, 210, 0, 0) + + def load(self): + CogHQBossBattle.CogHQBossBattle.load(self) + + def unload(self): + CogHQBossBattle.CogHQBossBattle.unload(self) + + def enter(self, requestStatus): + CogHQBossBattle.CogHQBossBattle.enter(self, requestStatus, DistributedLawbotBoss.OneBossCog) + + def exit(self): + CogHQBossBattle.CogHQBossBattle.exit(self) diff --git a/toontown/coghq/LawbotHQExterior.py b/toontown/coghq/LawbotHQExterior.py new file mode 100755 index 00000000..ce8205a5 --- /dev/null +++ b/toontown/coghq/LawbotHQExterior.py @@ -0,0 +1,37 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from panda3d.core import * +from toontown.battle import BattlePlace +from toontown.building import Elevator +from toontown.coghq import CogHQExterior +from toontown.dna.DNAParser import loadDNAFileAI, DNAStorage +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals + + +class LawbotHQExterior(CogHQExterior.CogHQExterior): + notify = DirectNotifyGlobal.directNotify.newCategory('LawbotHQExterior') + + def enter(self, requestStatus): + CogHQExterior.CogHQExterior.enter(self, requestStatus) + + # Load the CogHQ DNA file: + dnaStore = DNAStorage() + dnaFileName = self.genDNAFileName(self.zoneId) + loadDNAFileAI(dnaStore, dnaFileName) + + # Collect all of the vis group zone IDs: + self.zoneVisDict = {} + for i in xrange(dnaStore.getNumDNAVisGroupsAI()): + groupFullName = dnaStore.getDNAVisGroupName(i) + visGroup = dnaStore.getDNAVisGroupAI(i) + visZoneId = int(base.cr.hoodMgr.extractGroupName(groupFullName)) + visibles = [] + for i in xrange(visGroup.getNumVisibles()): + visibles.append(int(visGroup.getVisible(i))) + visibles.append(ZoneUtil.getBranchZone(visZoneId)) + self.zoneVisDict[visZoneId] = visibles + + # Next, we want interest in all vis groups due to this being a Cog HQ: + base.cr.sendSetZoneMsg(self.zoneId, self.zoneVisDict.values()[0]) diff --git a/toontown/coghq/LawbotLegFactoryCogs.py b/toontown/coghq/LawbotLegFactoryCogs.py new file mode 100755 index 00000000..eadb9917 --- /dev/null +++ b/toontown/coghq/LawbotLegFactoryCogs.py @@ -0,0 +1,6 @@ +from SpecImports import * +LobbyParent = 10014 +LobbyCell = 0 +BattleCells = {} +ReserveCogData = [] +CogData = [] diff --git a/toontown/coghq/LawbotLegFactorySpec.py b/toontown/coghq/LawbotLegFactorySpec.py new file mode 100755 index 00000000..f6a2f821 --- /dev/null +++ b/toontown/coghq/LawbotLegFactorySpec.py @@ -0,0 +1,19 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/lawbotHQ/LawbotCourtroom3', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeBoilerRoom_Action01.py b/toontown/coghq/LawbotOfficeBoilerRoom_Action01.py new file mode 100755 index 00000000..5637b9cc --- /dev/null +++ b/toontown/coghq/LawbotOfficeBoilerRoom_Action01.py @@ -0,0 +1,756 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone08a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10010: {'type': 'attribModifier', + 'name': 'goonStrength', + 'comment': '', + 'parentEntId': 100021, + 'attribName': 'strength', + 'recursive': 1, + 'typeName': 'goon', + 'value': '15'}, + 10015: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0, 2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'length': 3.0, + 'radius': 3.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10017: {'type': 'collisionSolid', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0, 2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'length': 3.0, + 'radius': 3.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10026: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10024, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'length': 3.0, + 'radius': 1.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10027: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(51.033, -2.36694, -0.821283), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 10.0, + 'radius': 7.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10028: {'type': 'collisionSolid', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(14.9923, 28.704, -0.821283), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 10.0, + 'radius': 7.518188505180132, + 'showSolid': 0, + 'solidType': 'tube'}, + 10038: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(-2.32046, 0.439226, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 6.0, + 'radius': 0.8, + 'showSolid': 0, + 'solidType': 'tube'}, + 10041: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10040, + 'pos': Point3(2.4512, 1.72783, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 10.0, + 'radius': 4.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10001: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10008: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10012: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10020: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10047: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10052: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10051, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10057: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10056, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10007: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10009, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(164.055, 0, 0), + 'scale': Vec3(7, 7, 7), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'hardhat', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/char/Cog_Goonie-zero'}, + 10018: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(215, 0, 0), + 'scale': Vec3(7, 7, 7), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'hardhat', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/char/Cog_Goonie-zero'}, + 10024: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10025, + 'pos': Point3(39.8396, -3.38811, 2), + 'hpr': Vec3(45, 280, 0), + 'scale': Vec3(10, 10, 10), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'hardhat', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/char/Cog_Goonie-zero'}, + 10031: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10032: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(1.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10033: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(3.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10034: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(-2, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA.bam'}, + 10036: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(-2.6, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA.bam'}, + 10040: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(14.6642, -1.01767, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.32128, 1.32128, 1.32128), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks.bam'}, + 10044: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10043, + 'pos': Point3(1.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10045: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10043, + 'pos': Point3(3.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10060: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10043, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10062: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10061, + 'pos': Point3(1.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10063: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10061, + 'pos': Point3(3.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10064: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10061, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10066: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(8.93303, 26.855, 0), + 'hpr': Vec3(315, 0, 0), + 'scale': Vec3(1.28721, 1.28721, 1.28721), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks.bam'}, + 10067: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(40.4297, -10.7197, 0), + 'hpr': Vec3(41.1859, 0, 0), + 'scale': Vec3(1.38665, 1.38665, 1.38665), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks.bam'}, + 10002: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-30, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10005: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10006: {'type': 'nodepath', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0, 25, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10013: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10014: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-40, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10021: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10022: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10023: {'type': 'nodepath', + 'name': 'upperFloor', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 60, 10.6166), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10029: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10030: {'type': 'nodepath', + 'name': 'leftWall', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(0, -43.0808, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Vec3(2.5, 2.5, 2.5)}, + 10035: {'type': 'nodepath', + 'name': 'plants', + 'comment': '', + 'parentEntId': 10030, + 'pos': Point3(0, -0.9, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(5, 5, 5)}, + 10037: {'type': 'nodepath', + 'name': 'filingCabinets', + 'comment': '', + 'parentEntId': 10030, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.4, 1.4, 1.4)}, + 10039: {'type': 'nodepath', + 'name': 'paperStacks', + 'comment': '', + 'parentEntId': 10030, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10042: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10043: {'type': 'nodepath', + 'name': 'filingCabinets', + 'comment': '', + 'parentEntId': 10042, + 'pos': Point3(-40.2501, 9.25953, 0), + 'hpr': Vec3(41.5, 0, 0), + 'scale': Vec3(1.4, 1.4, 1.4)}, + 10048: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10049: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10050: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(10, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10053: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10051, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10054: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10051, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10055: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10051, + 'pos': Point3(10, 15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10058: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10056, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10059: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10056, + 'pos': Point3(20, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10061: {'type': 'nodepath', + 'name': 'filingCabinets', + 'comment': '', + 'parentEntId': 10042, + 'pos': Point3(-29.6959, 18.2379, 0), + 'hpr': Point3(41.5, 0, 0), + 'scale': Vec3(1.4, 1.4, 1.4)}, + 10065: {'type': 'nodepath', + 'name': 'paperStacks', + 'comment': '', + 'parentEntId': 10042, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100021: {'type': 'nodepath', + 'name': 'gauntlet', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(30, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10000: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(10, 35, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10002, + 'pathTarget1': 10004, + 'pathTarget2': 0, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10003: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(0, -7, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10005, + 'pathTarget1': 10006, + 'pathTarget2': 0, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10011: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(0, -35, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10013, + 'pathTarget1': 10014, + 'pathTarget2': 0, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10019: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(-90, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10021, + 'pathTarget1': 10022, + 'pathTarget2': 0, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10046: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(-30, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10048, + 'pathTarget1': 10049, + 'pathTarget2': 10050, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10051: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(-60, 20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10053, + 'pathTarget1': 10054, + 'pathTarget2': 10055, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10056: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(-80, -20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10058, + 'pathTarget1': 10059, + 'pathTarget2': 0, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10009: {'type': 'rendering', + 'name': 'farCornerGoon', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(-60.62, -37.7557, 0), + 'hpr': Vec3(330, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 0.2, + 'colorG': 0.2, + 'colorR': 0.5, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 10016: {'type': 'rendering', + 'name': 'nearCornerGoon', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(60, -37.76, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 0.2, + 'colorG': 0.2, + 'colorR': 0.5, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 10025: {'type': 'rendering', + 'name': '', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 0.2, + 'colorG': 0.2, + 'colorR': 0.5, + 'fogOn': 0, + 'renderBin': 'transparent'}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeBoilerRoom_Battle00.py b/toontown/coghq/LawbotOfficeBoilerRoom_Battle00.py new file mode 100755 index 00000000..7a34a23a --- /dev/null +++ b/toontown/coghq/LawbotOfficeBoilerRoom_Battle00.py @@ -0,0 +1,329 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone8a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10001: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-1.02925205231, 87.0907745361, 11.8959827423), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'cellId': 0, + 'radius': 10.0}, + 10006: {'type': 'battleBlocker', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-60.9065246582, -3.26905798912, 0.117109239101), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'cellId': 1, + 'radius': 15.0}, + 10047: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 10013, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 0.20000000298, 1.0), + 'cellId': 2, + 'radius': 20.0}, + 10041: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10033, + 'pos': Point3(5.40611028671, 0.0, 0.0), + 'hpr': Vec3(199.440032959, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 'random', + 'rewardPerGrab': 4, + 'rewardPerGrabMax': 6}, + 10034: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10033, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(163.300750732, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 7, + 'rewardPerGrabMax': 9}, + 10004: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(0.0, -1.09804749489, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(2.0, 2.0, 2.0), + 'collisionsOnly': 0, + 'flattenType': 'strong', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10009: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(-3.9962117672, 0.695078849792, 0.0113303475082), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.20000004768, 1.20000004768, 1.20000004768), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'}, + 10010: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(48.0530014038, -0.531660735607, -0.327078670263), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_C1.bam'}, + 10012: {'type': 'model', + 'name': 'rightCrates', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(36.0373382568, 71.3546981812, 9.99835586548), + 'hpr': Vec3(315.0, 0.0, 0.0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'}, + 10024: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(-3.7328555584, 27.1218452454, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(2.0, 2.0, 2.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10027: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(-11.9349050522, 38.9528312683, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(2.0, 2.0, 2.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10029: {'type': 'model', + 'name': 'crate', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(0.0, 0.863602340221, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(2.0, 2.0, 2.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10030: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10031: {'type': 'model', + 'name': 'copy of crate', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(0.0, 0.0, 5.46999979019), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10032: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, -5.92218112946, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10039: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(-9.23663234711, 0.821143984795, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'}, + 10042: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(3.0, -11.8400001526, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10048: {'type': 'model', + 'name': 'cratesAgainstWall', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(-37.0983123779, 70.2133865356, 10.0), + 'hpr': Vec3(225.0, 0.0, 0.0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_E.bam'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0.0, 66.1200027466, 10.1833248138), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10002: {'type': 'nodepath', + 'name': 'battle', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': 1}, + 10003: {'type': 'nodepath', + 'name': 'cogs2', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-53.9246749878, -22.7616195679, 0.0), + 'hpr': Point3(45.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10005: {'type': 'nodepath', + 'name': 'battle', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10007: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10008: {'type': 'nodepath', + 'name': 'topWall', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0.0, 48.0299987793, 10.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10011: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10013: {'type': 'nodepath', + 'name': 'frontCogs', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(25.3957309723, -12.3005743027, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10014: {'type': 'nodepath', + 'name': 'frontPalletWall', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(45.5494384766, 38.2237281799, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10021: {'type': 'nodepath', + 'name': 'middlePalletWallLeft', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(6.0, -37.9928665161, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10023: {'type': 'nodepath', + 'name': 'crateIsland', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(-23.1813278198, 7.08758449554, 0.00999999977648), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(2.0, 2.0, 2.0)}, + 10028: {'type': 'nodepath', + 'name': 'rewardCulDeSac', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(-8.26172065735, 38.377407074, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10033: {'type': 'nodepath', + 'name': 'barrels', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(-4.75077962875, 34.1425209045, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10035: {'type': 'nodepath', + 'name': 'backPalletWall', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(-47.6501731873, 40.006893158, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10040: {'type': 'nodepath', + 'name': 'centerCogs', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-23.9375743866, 28.353269577, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10045: {'type': 'nodepath', + 'name': 'middlePalletWallRight', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(17.4200000763, -38.2999992371, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10046: {'type': 'nodepath', + 'name': 'middlePalletWall', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeBoilerRoom_Battle00_Cogs.py b/toontown/coghq/LawbotOfficeBoilerRoom_Battle00_Cogs.py new file mode 100755 index 00000000..991eecac --- /dev/null +++ b/toontown/coghq/LawbotOfficeBoilerRoom_Battle00_Cogs.py @@ -0,0 +1,165 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +LowerCogParent = 10003 +BattleParent = 10002 +LowerBattleParent = 10005 +FrontCogParent = 10013 +CenterCogParent = 10040 +BattleCellId = 0 +LowerBattleCellId = 1 +FrontBattleCellId = 2 +CenterBattleCellId = 3 +BattleCells = {BattleCellId: {'parentEntId': BattleParent, + 'pos': Point3(0, 0, 0)}, + LowerBattleCellId: {'parentEntId': LowerBattleParent, + 'pos': Point3(0, 0, 0)}, + FrontBattleCellId: {'parentEntId': FrontCogParent, + 'pos': Point3(0, 0, 0)}, + CenterBattleCellId: {'parentEntId': CenterCogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': LowerCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': LowerBattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': LowerCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': LowerBattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': LowerCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': LowerBattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': LowerCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': LowerBattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': FrontCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': FrontBattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CenterCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': CenterBattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CenterCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': CenterBattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CenterCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintSkelecogLevel, + 'battleCell': CenterBattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CenterCogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': CenterBattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/LawbotOfficeBoilerRoom_Security00.py b/toontown/coghq/LawbotOfficeBoilerRoom_Security00.py new file mode 100755 index 00000000..785fdac1 --- /dev/null +++ b/toontown/coghq/LawbotOfficeBoilerRoom_Security00.py @@ -0,0 +1,503 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone08a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 100014: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-25.1777, 5.83836, 0.03), + 'hpr': Vec3(92.8624, 0, 0), + 'scale': Vec3(1, 1, 1), + 'gagLevel': 5, + 'gagLevelMax': 5, + 'gagTrack': 'random', + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 100035: {'type': 'gagBarrel', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(16.7354, -42.9601, 0.03), + 'hpr': Vec3(151.049, 0, 0), + 'scale': Vec3(1, 1, 1), + 'gagLevel': 5, + 'gagLevelMax': 5, + 'gagTrack': 'random', + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 100013: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 100012, + 'pos': Point3(0, 0, 0.591849), + 'hpr': Vec3(147.995, 0, 0), + 'scale': Vec3(1, 1, 1), + 'rewardPerGrab': 15, + 'rewardPerGrabMax': 0}, + 100016: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(1.77609, -41.5342, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100018: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(27.5451, -41.4709, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100019: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(36.4846, -38.3301, 0), + 'hpr': Vec3(270.526, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100020: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(10.7887, -37.8558, 0), + 'hpr': Vec3(270.526, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100022: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(32.4792, -42.2737, 4.71821), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.6, 1.6, 1.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampA'}, + 100023: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(5.52344, -42.2737, 4.71821), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.6, 1.6, 1.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampA'}, + 100024: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-39.2286, -39.5741, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBoxX2'}, + 100025: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-62.8751, -40.0794, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100026: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-39.2286, -33.027, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100027: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-31.2652, -39.7321, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100028: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-22.0578, -39.3922, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100029: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(1.77609, -21.8266, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100030: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(10.7887, -18.0704, 0), + 'hpr': Vec3(270.526, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100031: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(36.6874, -16.1212, 0), + 'hpr': Vec3(270.526, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100032: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(27.5451, -20.5329, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100033: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(5.52344, -22.4424, 4.71821), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.6, 1.6, 1.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampA'}, + 100034: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(32.445, -22.0763, 4.71821), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.6, 1.6, 1.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampA'}, + 100036: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(15.0906, 40.376, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100037: {'type': 'model', + 'name': 'copy of (6)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(23.8805, 40.376, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100038: {'type': 'model', + 'name': 'copy of (7)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(31.7416, 40.376, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100039: {'type': 'model', + 'name': 'copy of (8)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(31.7416, 33.2113, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBoxX2'}, + 100003: {'type': 'nodepath', + 'name': 'light target 2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-46.465, -27.1019, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100004: {'type': 'nodepath', + 'name': 'light target1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(45.4612, -33.6397, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100006: {'type': 'nodepath', + 'name': 'copy of light target 2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-46.465, 31.2292, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100007: {'type': 'nodepath', + 'name': 'copy of light target1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(22.3708, 14.195, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100008: {'type': 'nodepath', + 'name': 'stompergroup', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-45.2964, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100015: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100011: {'type': 'platform', + 'name': '', + 'comment': '', + 'parentEntId': 100008, + 'pos': Point3(-8.92462, 5.26364, 19.9994), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'floorName': 'platformcollision', + 'modelPath': 'phase_9/models/cogHQ/platform1', + 'modelScale': Vec3(1, 1, 1), + 'motion': 'noBlend', + 'offset': Point3(-10, 0, 0), + 'period': 8.0, + 'phaseShift': 0.0, + 'waitPercent': 0.1}, + 100012: {'type': 'platform', + 'name': '', + 'comment': '', + 'parentEntId': 100008, + 'pos': Point3(-18.1468, -5.11, 20), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'floorName': 'platformcollision', + 'modelPath': 'phase_9/models/cogHQ/platform1', + 'modelScale': Vec3(1, 1, 1), + 'motion': 'noBlend', + 'offset': Point3(0, 0, 0), + 'period': 2, + 'phaseShift': 0.0, + 'waitPercent': 0.1}, + 100002: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-5.84843, -50.8043, 0.1), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 5.0, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 10.0, + 'switchId': 0, + 'trackTarget1': 100004, + 'trackTarget2': 100003, + 'trackTarget3': 0}, + 100005: {'type': 'securityCamera', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-5.38565, 34.1311, 0.1), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 10.0, + 'switchId': 0, + 'trackTarget1': 100006, + 'trackTarget2': 100007, + 'trackTarget3': 0}, + 100000: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 100008, + 'pos': Point3(5, 5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(8, 4, 8), + 'modelPath': 0, + 'motion': 4, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 18.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 10, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 100001: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100008, + 'pos': Point3(34.0608, 5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(8, 4, 8), + 'modelPath': 0, + 'motion': 4, + 'period': 2.0, + 'phaseShift': 0.75, + 'range': 12.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 10, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 100009: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100008, + 'pos': Point3(19.6858, 21.6045, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(8, 4, 8), + 'modelPath': 0, + 'motion': 4, + 'period': 2.0, + 'phaseShift': 0.5, + 'range': 12.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 10, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 100010: {'type': 'stomper', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100008, + 'pos': Point3(19.6858, -11.5601, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(8, 4, 8), + 'modelPath': 0, + 'motion': 4, + 'period': 2.0, + 'phaseShift': 0.25, + 'range': 12.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 10, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeBoilerRoom_Trap00.py b/toontown/coghq/LawbotOfficeBoilerRoom_Trap00.py new file mode 100755 index 00000000..4a2118b9 --- /dev/null +++ b/toontown/coghq/LawbotOfficeBoilerRoom_Trap00.py @@ -0,0 +1,395 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE08a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10055: {'type': 'attribModifier', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'attribName': 'modelPath', + 'recursive': 1, + 'typeName': 'model', + 'value': ''}, + 100023: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 100022, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 6.0, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 90.0, + 'strength': 15, + 'velocity': 4}, + 100000: {'type': 'model', + 'name': 'wall', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(51.4205, -2.29817, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100002: {'type': 'model', + 'name': 'copy of wall', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(36.415, -2.3, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100005: {'type': 'model', + 'name': 'copy of wall (2)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(23.2853, -0.371783, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100006: {'type': 'model', + 'name': 'copy of wall (3)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(23.2853, -15.2326, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100007: {'type': 'model', + 'name': 'copy of wall (4)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(23.2853, -30.3458, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100008: {'type': 'model', + 'name': 'copy of wall (5)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(8.38448, 29.6127, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100009: {'type': 'model', + 'name': 'copy of wall (6)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(8.38448, 14.8894, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100010: {'type': 'model', + 'name': 'copy of wall (7)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(8.38448, -0.135276, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100011: {'type': 'model', + 'name': 'copy of wall (8)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-8.54444, 0.0828297, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100012: {'type': 'model', + 'name': 'copy of wall (9)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-23.6854, 0.0828297, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100013: {'type': 'model', + 'name': 'copy of wall (10)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-36.5, -45.2411, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100014: {'type': 'model', + 'name': 'copy of wall (11)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-36.5, -30.557, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100015: {'type': 'model', + 'name': 'copy of wall (12)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-36.5, -15.612, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100016: {'type': 'model', + 'name': 'copy of wall (13)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-36.5, -0.453893, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100017: {'type': 'model', + 'name': 'copy of wall (14)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-36.5, 14.6685, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100018: {'type': 'model', + 'name': 'copy of wall (15)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-36.5, 14.6685, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100019: {'type': 'model', + 'name': 'copy of wall (16)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-21.5858, 14.6685, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 10001: {'type': 'nodepath', + 'name': 'crates', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.3, 1.3, 1.64892)}, + 10002: {'type': 'nodepath', + 'name': 'rewardBarrels', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-0.719734, 56.9691, 10.0021), + 'hpr': Vec3(61.6992, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10003: {'type': 'nodepath', + 'name': 'upperWall', + 'comment': 'TODO: replace with lines of shelves', + 'parentEntId': 0, + 'pos': Point3(-20.3203, 52.6549, 9.90873), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1.1143, 1.1143, 1.1143)}, + 10009: {'type': 'nodepath', + 'name': 'toGear0', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(-26.5593, 31.856, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10011: {'type': 'nodepath', + 'name': 'toGear1', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(-25.884, 13.6749, 0), + 'hpr': Vec3(41.6335, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10023: {'type': 'nodepath', + 'name': 'leftWall', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10024: {'type': 'nodepath', + 'name': 'rightWall', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-26.7112, 6.85982, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10028: {'type': 'nodepath', + 'name': 'lowerPuzzle', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10029: {'type': 'nodepath', + 'name': 'entranceWall', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10032: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10038: {'type': 'nodepath', + 'name': 'archStompers', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10040: {'type': 'nodepath', + 'name': 'backWall', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10044: {'type': 'nodepath', + 'name': 'gear', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(11.85, -11.38, 12.528), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10046: {'type': 'nodepath', + 'name': 'supportedCrateBackWall', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(34.9045, -34.0589, -1.51687), + 'hpr': Vec3(63.4349, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10051: {'type': 'nodepath', + 'name': 'supportedCrateEntrance', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(48.5077, 7.75915, 0.357897), + 'hpr': Point3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10059: {'type': 'nodepath', + 'name': 'largeStack', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(47.98, -16.98, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10061: {'type': 'nodepath', + 'name': 'lower', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100001: {'type': 'nodepath', + 'name': 'trap1 cog node', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100004: {'type': 'nodepath', + 'name': 'maze', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100021: {'type': 'nodepath', + 'name': 'Goon Parent', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100003: {'type': 'path', + 'name': 'test goon path', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-50.4808, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'pathIndex': 0, + 'pathScale': 1.0}, + 100020: {'type': 'path', + 'name': 'GoonPath1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 100022: {'type': 'path', + 'name': 'GoonPath1', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(-10, -30, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'pathIndex': 0, + 'pathScale': 2.0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeBoilerRoom_Trap00_Cogs.py b/toontown/coghq/LawbotOfficeBoilerRoom_Trap00_Cogs.py new file mode 100755 index 00000000..384552a3 --- /dev/null +++ b/toontown/coghq/LawbotOfficeBoilerRoom_Trap00_Cogs.py @@ -0,0 +1,8 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 100001 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [] +ReserveCogData = [] diff --git a/toontown/coghq/LawbotOfficeDiamondRoom_Action00.py b/toontown/coghq/LawbotOfficeDiamondRoom_Action00.py new file mode 100755 index 00000000..ee03d077 --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Action00.py @@ -0,0 +1,929 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone13a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10005: {'type': 'attribModifier', + 'name': 'stomperDamage', + 'comment': '', + 'parentEntId': 0, + 'attribName': 'damage', + 'recursive': 1, + 'typeName': 'stomper', + 'value': '10'}, + 10009: {'type': 'attribModifier', + 'name': 'spotlightDamage', + 'comment': '', + 'parentEntId': 0, + 'attribName': 'damPow', + 'recursive': 1, + 'typeName': 'securityCamera', + 'value': '10'}, + 10032: {'type': 'attribModifier', + 'name': 'radius', + 'comment': '', + 'parentEntId': 10059, + 'attribName': 'radius', + 'recursive': 1, + 'typeName': 'securityCamera', + 'value': '7'}, + 10036: {'type': 'attribModifier', + 'name': 'accel', + 'comment': '', + 'parentEntId': 10059, + 'attribName': 'accel', + 'recursive': 1, + 'typeName': 'securityCamera', + 'value': '2'}, + 10066: {'type': 'attribModifier', + 'name': 'maxVel', + 'comment': '', + 'parentEntId': 10059, + 'attribName': 'maxVel', + 'recursive': 1, + 'typeName': 'securityCamera', + 'value': '10'}, + 10000: {'type': 'nodepath', + 'name': 'front', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-80, -1.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10001: {'type': 'nodepath', + 'name': 'left', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 40, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10002: {'type': 'nodepath', + 'name': 'right', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, -40, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10003: {'type': 'nodepath', + 'name': 'back', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(80, 0, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10007: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10008: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10011: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10012: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10017: {'type': 'nodepath', + 'name': 'stompers', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, -6, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10019: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10020: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10021: {'type': 'nodepath', + 'name': 'leftFront', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(-29.5779, 0, 0), + 'hpr': Point3(27, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10026: {'type': 'nodepath', + 'name': 'projectors', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-15, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10027: {'type': 'nodepath', + 'name': 'left', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0, 8, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10028: {'type': 'nodepath', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0, -15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10029: {'type': 'nodepath', + 'name': 'right', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0, -28, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10031: {'type': 'nodepath', + 'name': 'nodepaths', + 'comment': '', + 'parentEntId': 10057, + 'pos': Point3(0, -2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10033: {'type': 'nodepath', + 'name': 'backLeft', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0, 20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10034: {'type': 'nodepath', + 'name': 'backCenter', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10035: {'type': 'nodepath', + 'name': 'backRight', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0, -20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10037: {'type': 'nodepath', + 'name': 'rightFront', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-20.7505, 15.0698, 0), + 'hpr': Point3(333, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10038: {'type': 'nodepath', + 'name': 'projectors', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(-15, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10043: {'type': 'nodepath', + 'name': 'left', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, 8, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10044: {'type': 'nodepath', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, -15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10045: {'type': 'nodepath', + 'name': 'frontLeft', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(-15, 20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10046: {'type': 'nodepath', + 'name': 'frontCenter', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(-15, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10047: {'type': 'nodepath', + 'name': 'frontRight', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(-15, -20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10048: {'type': 'nodepath', + 'name': 'right', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, -28, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10049: {'type': 'nodepath', + 'name': 'rightBack', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(20, 4, 0), + 'hpr': Point3(21, 0, 0), + 'scale': 1}, + 10050: {'type': 'nodepath', + 'name': 'leftBack', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(20, -7, 0), + 'hpr': Point3(334, 0, 0), + 'scale': 1}, + 10057: {'type': 'nodepath', + 'name': 'discoFloor', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(36.354, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10059: {'type': 'nodepath', + 'name': 'cameras', + 'comment': '', + 'parentEntId': 10057, + 'pos': Point3(-7, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10006: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(15, 15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10007, + 'trackTarget2': 10008, + 'trackTarget3': 0}, + 10010: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(15, -15, 0), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10011, + 'trackTarget2': 10012, + 'trackTarget3': 0}, + 10018: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(15, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 1, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10019, + 'trackTarget2': 10020, + 'trackTarget3': 0}, + 10024: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 1, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10027, + 'trackTarget2': 10028, + 'trackTarget3': 0}, + 10025: {'type': 'securityCamera', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0, -20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 1, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10028, + 'trackTarget2': 10029, + 'trackTarget3': 0}, + 10041: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 1, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10043, + 'trackTarget2': 10044, + 'trackTarget3': 0}, + 10042: {'type': 'securityCamera', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, -20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 1, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10044, + 'trackTarget2': 10048, + 'trackTarget3': 0}, + 10060: {'type': 'securityCamera', + 'name': 'backRow', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 2, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 10, + 'modelPath': 0, + 'projector': Point3(0, 0, 25), + 'radius': 7, + 'switchId': 0, + 'trackTarget1': 10033, + 'trackTarget2': 10034, + 'trackTarget3': 0}, + 10062: {'type': 'securityCamera', + 'name': 'frontRow', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 2, + 'damPow': 10, + 'hideModel': 1, + 'maxVel': 10, + 'modelPath': 0, + 'projector': Point3(0, 0, 25), + 'radius': 7, + 'switchId': 0, + 'trackTarget1': 10045, + 'trackTarget2': 10046, + 'trackTarget3': 0}, + 10063: {'type': 'securityCamera', + 'name': 'backRow2', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 2, + 'damPow': 10, + 'hideModel': 1, + 'maxVel': 10, + 'modelPath': 0, + 'projector': Point3(0, 0, 25), + 'radius': 7, + 'switchId': 0, + 'trackTarget1': 0, + 'trackTarget2': 10034, + 'trackTarget3': 10035}, + 10065: {'type': 'securityCamera', + 'name': 'frontRow2', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 2, + 'damPow': 10, + 'hideModel': 1, + 'maxVel': 10, + 'modelPath': 0, + 'projector': Point3(0, 0, 25), + 'radius': 7, + 'switchId': 0, + 'trackTarget1': 0, + 'trackTarget2': 10046, + 'trackTarget3': 10047}, + 10004: {'type': 'stomper', + 'name': '3', + 'comment': '', + 'parentEntId': 10017, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 6.25), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.25, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10013: {'type': 'stomper', + 'name': '2', + 'comment': '', + 'parentEntId': 10017, + 'pos': Point3(0, 12.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 6.25), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.5, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10014: {'type': 'stomper', + 'name': '1', + 'comment': '', + 'parentEntId': 10017, + 'pos': Point3(0, 25, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 6.25), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10015: {'type': 'stomper', + 'name': 'copy of 4 (2)', + 'comment': '', + 'parentEntId': 10049, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10016: {'type': 'stomper', + 'name': '4', + 'comment': '', + 'parentEntId': 10017, + 'pos': Point3(0, -12.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 6.25), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.75, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10022: {'type': 'stomper', + 'name': 'copy of 3', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(10, 6, 10), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.0, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10023: {'type': 'stomper', + 'name': 'copy of 3 (2)', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(0, -20, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(10, 6, 10), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.4, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10030: {'type': 'stomper', + 'name': 'copy of 4 (3)', + 'comment': '', + 'parentEntId': 10049, + 'pos': Point3(10, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10039: {'type': 'stomper', + 'name': 'copy of 3', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(10, 6, 10), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.0, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10040: {'type': 'stomper', + 'name': 'copy of 3 (2)', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(0, -20, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(10, 6, 10), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.4, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10051: {'type': 'stomper', + 'name': 'copy of 4 (4)', + 'comment': '', + 'parentEntId': 10049, + 'pos': Point3(20, -5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10052: {'type': 'stomper', + 'name': 'copy of 4 (5)', + 'comment': '', + 'parentEntId': 10049, + 'pos': Point3(35, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10053: {'type': 'stomper', + 'name': 'copy of 4', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10054: {'type': 'stomper', + 'name': 'copy of 4 (2)', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(10, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10055: {'type': 'stomper', + 'name': 'copy of 4 (3)', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(20, -5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10056: {'type': 'stomper', + 'name': 'copy of 4 (4)', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(35, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeDiamondRoom_Action01.py b/toontown/coghq/LawbotOfficeDiamondRoom_Action01.py new file mode 100755 index 00000000..74773ffa --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Action01.py @@ -0,0 +1,637 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone13a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10074: {'type': 'attribModifier', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'attribName': 'velocity', + 'recursive': 1, + 'typeName': 'goon', + 'value': '10.0'}, + 10005: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 10.0}, + 10011: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 10.0}, + 10017: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 10.0}, + 10022: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 10.0}, + 10037: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10057, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10039: {'type': 'model', + 'name': 'jumpCabinet', + 'comment': '', + 'parentEntId': 10057, + 'pos': Point3(0, 1.75, 0), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10040: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(0, 0, 4), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10041: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10057, + 'pos': Point3(0, 3.5, 0), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10042: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10041, + 'pos': Point3(0, 0, 4), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10043: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10052, + 'pos': Point3(1.95, -4, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10044: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10051, + 'pos': Point3(1.95, 7, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10045: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10054, + 'pos': Point3(0, -7.8, 0), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10046: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10054, + 'pos': Point3(0, -9.6, 0), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10047: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10054, + 'pos': Point3(0, -11.4, 0), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10048: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10054, + 'pos': Point3(0, -7.8, 4), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabB.bam'}, + 10049: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10054, + 'pos': Point3(0, -9.6, 4), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10050: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 10054, + 'pos': Point3(0, -11.4, 4), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabB.bam'}, + 10056: {'type': 'model', + 'name': 'stepCrate', + 'comment': '', + 'parentEntId': 10053, + 'pos': Point3(-2.08696, 1.52947, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(0.4, 0.4, 0.4), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_metal_crate.bam'}, + 10059: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(-65.7043, 26.7723, 0), + 'hpr': Vec3(30.2564, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10060: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(-22.3997, 48.3291, 0), + 'hpr': Vec3(30.2564, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10061: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(-30.0586, 11.8711, 0), + 'hpr': Vec3(30.2564, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10066: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(-35.5539, -11.1795, 0), + 'hpr': Vec3(326.31, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2.bam'}, + 10067: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(-68.8196, -28.185, 0), + 'hpr': Vec3(329.74, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10068: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(-12.0298, -56.6682, 0), + 'hpr': Vec3(326.31, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2.bam'}, + 10069: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(37.402, -43.7359, 0), + 'hpr': Vec3(33.69, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2.bam'}, + 10070: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(23.2824, -14.8496, 0), + 'hpr': Vec3(28.6105, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3.bam'}, + 10071: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 10064, + 'pos': Point3(23.28, 14.85, 0), + 'hpr': Point3(-28.61, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3.bam'}, + 10072: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10064, + 'pos': Point3(19.9879, 48.3291, 0), + 'hpr': Point3(-30.26, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10073: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10064, + 'pos': Point3(66.6957, 25.7694, 0), + 'hpr': Vec3(333.435, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10075: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(-7.27334, -24.2132, 0), + 'hpr': Vec3(326.31, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2.bam'}, + 10076: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(66.6957, -29.1363, 0), + 'hpr': Vec3(26.57, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10000: {'type': 'nodepath', + 'name': 'goons', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10002: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(-74.9317, -1.26934, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10003: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0, -40.588, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10004: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(-44.8133, -38.3892, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10007: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0, 30, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10008: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(-10, 40, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10009: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(-50, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10010: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(-40, 30, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10013: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(15, -30, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10014: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(20, -45, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10015: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(40, -15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10016: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(55, -30, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10019: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(20, 35, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10020: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(65, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10021: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(55, 25, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10023: {'type': 'nodepath', + 'name': 'front', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-110, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10038: {'type': 'nodepath', + 'name': 'filingCabinetWall', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0, 1, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2, 2, 2)}, + 10051: {'type': 'nodepath', + 'name': 'leftCrate', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10052: {'type': 'nodepath', + 'name': 'rightCrate', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10053: {'type': 'nodepath', + 'name': 'leftCabinets', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10054: {'type': 'nodepath', + 'name': 'rightCabinets', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10057: {'type': 'nodepath', + 'name': 'cabinets', + 'comment': '', + 'parentEntId': 10053, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1)}, + 10058: {'type': 'nodepath', + 'name': 'obstacles', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10062: {'type': 'nodepath', + 'name': 'leftFront', + 'comment': '', + 'parentEntId': 10058, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10063: {'type': 'nodepath', + 'name': 'rightFront', + 'comment': '', + 'parentEntId': 10058, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10064: {'type': 'nodepath', + 'name': 'backLeft', + 'comment': '', + 'parentEntId': 10058, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10065: {'type': 'nodepath', + 'name': 'backRight', + 'comment': '', + 'parentEntId': 10058, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10001: {'type': 'pathMaster', + 'name': 'rightFrontGoon', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10002, + 'pathTarget1': 10003, + 'pathTarget2': 10004, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10006: {'type': 'pathMaster', + 'name': 'leftFrontGoon', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10007, + 'pathTarget1': 10008, + 'pathTarget2': 10009, + 'pathTarget3': 10010, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10012: {'type': 'pathMaster', + 'name': 'rightRearGoon', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10013, + 'pathTarget1': 10014, + 'pathTarget2': 10015, + 'pathTarget3': 10016, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10018: {'type': 'pathMaster', + 'name': 'leftRearGoon', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10019, + 'pathTarget1': 10020, + 'pathTarget2': 10021, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeDiamondRoom_Battle00.py b/toontown/coghq/LawbotOfficeDiamondRoom_Battle00.py new file mode 100755 index 00000000..3761fdf2 --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Battle00.py @@ -0,0 +1,310 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone13a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10001: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(140, -2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'cellId': 0, + 'radius': 15.0}, + 10002: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-96.7274, 20.5323, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 10.0, + 'radius': 6.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10003: {'type': 'collisionSolid', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-96.7274, -23.0117, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 10.0, + 'radius': 6.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 100001: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-4.75302, 55.148, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100002: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-4.75302, -55.15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100003: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-24.8072, -17.6762, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100004: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-24.8072, 17.68, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100005: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-93.6953, 17.68, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100006: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-99.5488, 17.68, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100007: {'type': 'model', + 'name': 'copy of (6)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-99.5488, -20.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100008: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-93.6953, -20.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100009: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(13.2635, -58.812, 0), + 'hpr': Vec3(211.701, 0, 0), + 'scale': Vec3(1.8, 1.8, 1.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100010: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(32.4825, -48.4216, 0), + 'hpr': Vec3(211.701, 0, 0), + 'scale': Vec3(1.8, 1.8, 1.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100011: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(53.5274, -37.2339, 0), + 'hpr': Vec3(204.775, 0, 0), + 'scale': Vec3(1.8, 1.8, 1.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100012: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(14.898, 55.5914, 0), + 'hpr': Vec3(336.371, 0, 0), + 'scale': Vec3(1.8, 1.8, 1.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100013: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(37.3381, 45.4534, 0), + 'hpr': Vec3(335.225, 0, 0), + 'scale': Vec3(1.8, 1.8, 1.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100014: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(60.4494, 34.2323, 0), + 'hpr': Vec3(334.011, 0, 0), + 'scale': Vec3(1.8, 1.8, 1.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(100, 0, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100000: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100015: {'type': 'nodepath', + 'name': 'lights', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100017: {'type': 'nodepath', + 'name': 'cameratarget1', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-67.0138, 21.058, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100018: {'type': 'nodepath', + 'name': 'copy of cameratarget1', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-3.89516, 39.263, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100019: {'type': 'nodepath', + 'name': 'copy of cameratarget1 (2)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-23.9228, 20.3744, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100020: {'type': 'nodepath', + 'name': 'lights2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 4.9793, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100022: {'type': 'nodepath', + 'name': 'camtar1', + 'comment': '', + 'parentEntId': 100020, + 'pos': Point3(16.2876, -35.7071, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100023: {'type': 'nodepath', + 'name': 'copy of camtar1', + 'comment': '', + 'parentEntId': 100020, + 'pos': Point3(-52.2915, -8.50529, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100024: {'type': 'nodepath', + 'name': 'copy of camtar1 (2)', + 'comment': '', + 'parentEntId': 100020, + 'pos': Point3(-52.2915, -37.5322, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100016: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-45.8708, 39.2215, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 12.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100017, + 'trackTarget2': 100018, + 'trackTarget3': 100019}, + 100021: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 100020, + 'pos': Point3(-45.2862, -55.4163, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 12.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100022, + 'trackTarget2': 100023, + 'trackTarget3': 100024}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeDiamondRoom_Battle00_Cogs.py b/toontown/coghq/LawbotOfficeDiamondRoom_Battle00_Cogs.py new file mode 100755 index 00000000..c10ac737 --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Battle00_Cogs.py @@ -0,0 +1,44 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattlePlace1 = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': BattlePlace1, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}] +ReserveCogData = [] diff --git a/toontown/coghq/LawbotOfficeDiamondRoom_Security00.py b/toontown/coghq/LawbotOfficeDiamondRoom_Security00.py new file mode 100755 index 00000000..c62e8284 --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Security00.py @@ -0,0 +1,78 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE13a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 100001: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 100002: {'type': 'nodepath', + 'name': 'pathmasta1', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(32.0525, 0.204516, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100003: {'type': 'nodepath', + 'name': 'copy of pathmasta1', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-28.2492, 5.56589, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100004: {'type': 'nodepath', + 'name': 'copy of pathmasta1', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-10.8179, 14.6481, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100005: {'type': 'nodepath', + 'name': 'pathmastachild', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-59.4987, 29.1448, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100000: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-17.2957, -37.5269, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 100002, + 'pathTarget1': 100003, + 'pathTarget2': 100004, + 'pathTarget3': 100005, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeDiamondRoom_Trap00.py b/toontown/coghq/LawbotOfficeDiamondRoom_Trap00.py new file mode 100755 index 00000000..f2b2969d --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Trap00.py @@ -0,0 +1,417 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone13a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 100002: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-10.796, 37.9181, 0), + 'hpr': Vec3(23.9625, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 1, 1, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 100003: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-11.023, -40.7364, 0), + 'hpr': Vec3(338.199, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 1, 1, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 100006: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 100010, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(1.02303, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 0, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 0, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 100000, + 'unlock1Event': 0, + 'unlock2Event': 100001, + 'unlock3Event': 0}, + 100000: {'type': 'laserField', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-57.9165, 36.4902, 0.05), + 'hpr': Vec3(296.565, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'gridGame': 'Random', + 'gridScaleX': 40.0, + 'gridScaleY': 40.0, + 'laserFactor': 3, + 'modelPath': 0, + 'projector': Point3(21, 21, 25), + 'switchId': 100002}, + 100001: {'type': 'laserField', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-40.0571, -3.38424, 0.07), + 'hpr': Vec3(243.435, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 1, + 'gridGame': 'Random', + 'gridScaleX': 37.0, + 'gridScaleY': 38.0, + 'laserFactor': 3, + 'modelPath': 0, + 'projector': Point3(21, 21, 25), + 'switchId': 100003}, + 100008: {'type': 'model', + 'name': 'testMoverModel', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.6, 0.6, 0.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2'}, + 100011: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-66.4031, -28.3337, 0), + 'hpr': Vec3(330.945, 0, 0), + 'scale': Point3(0.6, 0.6, 0.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_stacks'}, + 100012: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-61.3414, 28.3839, 0), + 'hpr': Vec3(330.945, 0, 0), + 'scale': Point3(0.6, 0.6, 0.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_stacks'}, + 100013: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-73.0019, 23.1112, 0), + 'hpr': Vec3(311.424, 0, 0), + 'scale': Point3(0.6, 0.6, 0.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_stacks'}, + 100014: {'type': 'model', + 'name': 'critbox4', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-10.5395, 55.5366, 0), + 'hpr': Vec3(26.5651, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100015: {'type': 'model', + 'name': 'critbox3', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-2.51556, -64.2189, 0), + 'hpr': Vec3(333.435, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100016: {'type': 'model', + 'name': 'critbox2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-0.559084, -27.465, 0.05), + 'hpr': Vec3(333.435, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2'}, + 100019: {'type': 'model', + 'name': 'copy of crit box', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(3.82422, 21.9664, 0), + 'hpr': Vec3(60.9454, 0, 0), + 'scale': Point3(0.55, 0.55, 0.9), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100021: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100017, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.8, 0.8, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2'}, + 100022: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100017, + 'pos': Point3(-1.72022, 8.94383, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.8, 0.8, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2'}, + 100023: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100017, + 'pos': Point3(-1.27251, -8.84256, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.8, 0.8, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2'}, + 100026: {'type': 'model', + 'name': 'crit box', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(1.3729, 25.0434, 0), + 'hpr': Vec3(26.5651, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100028: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100027, + 'pos': Point3(-0.758049, -10.1966, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100029: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100027, + 'pos': Point3(1.82438, -2.94686, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100030: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100027, + 'pos': Point3(0.164113, 4.44244, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100031: {'type': 'model', + 'name': 'copy of (6)', + 'comment': '', + 'parentEntId': 100027, + 'pos': Point3(5.13217, 10.0594, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100035: {'type': 'model', + 'name': 'copy of crit box (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(3.01721, -23.3741, 0), + 'hpr': Vec3(13.2405, 0, 0), + 'scale': Point3(0.55, 0.55, 0.9), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100036: {'type': 'model', + 'name': 'copy of crit box (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-8.58923, 61.9548, 0), + 'hpr': Vec3(26.5651, 0, 0), + 'scale': Point3(0.55, 0.55, 0.9), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100018: {'type': 'mover', + 'name': 'testMover', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-64.3817, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cycleType': 'return', + 'entity2Move': 100008, + 'modelPath': 0, + 'moveTarget': 100034, + 'pos0Move': 2, + 'pos0Wait': 2, + 'pos1Move': 2, + 'pos1Wait': 2, + 'startOn': 0, + 'switchId': 0}, + 100024: {'type': 'mover', + 'name': 'paperwall2mover', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, -46.3957, 0), + 'hpr': Vec3(333.435, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cycleType': 'oneWay', + 'entity2Move': 100017, + 'modelPath': 0, + 'moveTarget': 100025, + 'pos0Move': 2, + 'pos0Wait': 2, + 'pos1Move': 2, + 'pos1Wait': 2, + 'startOn': 0, + 'switchId': 100001}, + 100032: {'type': 'mover', + 'name': 'paperwall1mover', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-1.54332, 42.0157, 0), + 'hpr': Vec3(26.5651, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cycleType': 'oneWay', + 'entity2Move': 100027, + 'modelPath': 0, + 'moveTarget': 100033, + 'pos0Move': 2, + 'pos0Wait': 2, + 'pos1Move': 2, + 'pos1Wait': 2, + 'startOn': 0, + 'switchId': 100000}, + 100004: {'type': 'nodepath', + 'name': 'BattlePos1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-31.9689, 28.7456, 0), + 'hpr': Vec3(22.6199, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100005: {'type': 'nodepath', + 'name': 'BattlePos2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-31.0858, -34.9835, 0), + 'hpr': Vec3(331.39, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100007: {'type': 'nodepath', + 'name': 'copy of Cog Parent1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-11.0871, 37.4001, 0), + 'hpr': Vec3(299.249, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100009: {'type': 'nodepath', + 'name': 'Cog Parent1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-10.2246, -41.5943, 0), + 'hpr': Vec3(246.214, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100010: {'type': 'nodepath', + 'name': 'doorparent', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(133.9, -1.81291, 0), + 'hpr': Vec3(265.74, 0, 0), + 'scale': Point3(1, 1, 1)}, + 100017: {'type': 'nodepath', + 'name': 'PaperDoor2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100020: {'type': 'nodepath', + 'name': 'movertarget', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-84.0273, -14.7227, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100025: {'type': 'nodepath', + 'name': 'papperwall2target', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(14.2998, -46.0911, 0), + 'hpr': Vec3(336.038, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100027: {'type': 'nodepath', + 'name': 'PaperWall1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100033: {'type': 'nodepath', + 'name': 'paperwall1movertarget', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(13.6684, 42.7864, 0), + 'hpr': Vec3(41.1859, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100034: {'type': 'nodepath', + 'name': 'test mover target', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-51.1549, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeDiamondRoom_Trap00_Cogs.py b/toontown/coghq/LawbotOfficeDiamondRoom_Trap00_Cogs.py new file mode 100755 index 00000000..177b36a7 --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Trap00_Cogs.py @@ -0,0 +1,85 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 100007 +CogParent1 = 100009 +BattlePlace1 = 100004 +BattlePlace2 = 100005 +BattleCellId = 0 +BattleCellId1 = 1 +BattleCells = {BattleCellId: {'parentEntId': BattlePlace1, + 'pos': Point3(0, 0, 0)}, + BattleCellId1: {'parentEntId': BattlePlace2, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId1, + 'pos': Point3(-8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId1, + 'pos': Point3(-3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId1, + 'pos': Point3(3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId1, + 'pos': Point3(8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}] +ReserveCogData = [] diff --git a/toontown/coghq/LawbotOfficeEntrance_Action00.py b/toontown/coghq/LawbotOfficeEntrance_Action00.py new file mode 100755 index 00000000..6deaa3ea --- /dev/null +++ b/toontown/coghq/LawbotOfficeEntrance_Action00.py @@ -0,0 +1,253 @@ +from toontown.coghq.SpecImports import * +import random +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone03a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10000: {'type': 'entrancePoint', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 6, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'entranceId': 0, + 'radius': 15, + 'theta': 20}, + 100000: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100001: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100002, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 180), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100004: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100003, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100006: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100005, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 180), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100008: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100007, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks'}, + 100010: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100009, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(180, 0, 180), + 'scale': Point3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks'}, + 100012: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100013: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100014, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 180), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100016: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA'}, + 100017: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 180), + 'scale': Point3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA'}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': 'lamp', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-10.1845, 16.3439, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100003: {'type': 'nodepath', + 'name': 'lamp2', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(10.3093, 14.7794, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100007: {'type': 'nodepath', + 'name': 'paper', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(14.3907, 11.4389, 0), + 'hpr': Vec3(300.379, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100011: {'type': 'nodepath', + 'name': 'box', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-18.5313, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100015: {'type': 'nodepath', + 'name': 'couch', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(14.3585, -9.43671, 0), + 'hpr': Vec3(270.699, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100002: {'type': 'rendering', + 'name': '', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'default'}, + 100005: {'type': 'rendering', + 'name': '', + 'comment': '', + 'parentEntId': 100003, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'default'}, + 100009: {'type': 'rendering', + 'name': '', + 'comment': '', + 'parentEntId': 100007, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'default'}, + 100014: {'type': 'rendering', + 'name': '', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'default'}, + 100018: {'type': 'rendering', + 'name': '', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'default'}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0], + 'titleString': 'MemTag: LawbotOfficeEntrance_Action00 %s' % random.random()} diff --git a/toontown/coghq/LawbotOfficeExterior.py b/toontown/coghq/LawbotOfficeExterior.py new file mode 100755 index 00000000..c6895e1d --- /dev/null +++ b/toontown/coghq/LawbotOfficeExterior.py @@ -0,0 +1,15 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.toonbase import ToontownGlobals +from toontown.building import Elevator +from panda3d.core import * +import FactoryExterior + +class LawbotOfficeExterior(FactoryExterior.FactoryExterior): + notify = DirectNotifyGlobal.directNotify.newCategory('LawbotOfficeExterior') + + def enterWalk(self, teleportIn = 0): + FactoryExterior.FactoryExterior.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) diff --git a/toontown/coghq/LawbotOfficeGearRoom_Action00.py b/toontown/coghq/LawbotOfficeGearRoom_Action00.py new file mode 100755 index 00000000..a73b84fc --- /dev/null +++ b/toontown/coghq/LawbotOfficeGearRoom_Action00.py @@ -0,0 +1,226 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone7av2', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10007: {'type': 'attribModifier', + 'name': 'goonStrength', + 'comment': '', + 'parentEntId': 0, + 'attribName': 'strength', + 'recursive': 1, + 'typeName': 'goon', + 'value': '10'}, + 10002: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4.0}, + 10004: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10006: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10005, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10009: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10011: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(2.15899157524, 2.29615116119, 5.45938539505), + 'hpr': Vec3(331.109100342, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 8, + 'rewardPerGrabMax': 0}, + 10012: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(20.9361133575, 13.8672618866, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.920000016689, 0.920000016689, 0.920000016689), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/CBMetalCrate.bam'}, + 10013: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(57.0218696594, 5.15023899078, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(0.660517215729, 0.660517215729, 0.660517215729), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10015: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-25.9598789215, 59.4411621094, 9.73551368713), + 'hpr': Vec3(274.089996338, 0.0, 0.0), + 'scale': Vec3(1.53790044785, 1.53790044785, 1.53790044785), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/crates_F1.bam'}, + 10016: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(33.3394889832, -18.3643035889, 0.0), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(0.660000026226, 0.660000026226, 0.660000026226), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D1.bam'}, + 10017: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(169.699996948, 0.0, 0.0), + 'scale': Vec3(0.902469694614, 0.902469694614, 0.902469694614), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D4.bam'}, + 10020: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(-12.071434021, 0.0, 0.0), + 'hpr': Vec3(288.434936523, 0.0, 0.0), + 'scale': Vec3(0.902469694614, 0.902469694614, 0.902469694614), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D4.bam'}, + 10022: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-5.97179174423, -60.3133621216, 0.0), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(0.869391143322, 0.869391143322, 0.869391143322), + 'collisionsOnly': 0, + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10000: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10010: {'type': 'nodepath', + 'name': 'healPuzzle', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(43.1796302795, 0.0, 0.0), + 'hpr': Point3(-90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10018: {'type': 'nodepath', + 'name': 'rightVertPipes', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-16.4536571503, -45.3981781006, -8.39999961853), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(0.649999976158, 0.649999976158, 1.55999994278)}, + 10021: {'type': 'nodepath', + 'name': 'rightPipes', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10001: {'type': 'path', + 'name': 'nearPace', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-59.7391967773, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'pathIndex': 3, + 'pathScale': 1.0}, + 10003: {'type': 'path', + 'name': 'bowtie', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-40.0336875916, 0.0, 0.0), + 'hpr': Point3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'pathIndex': 2, + 'pathScale': 1.0}, + 10005: {'type': 'path', + 'name': 'bridgePace', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-8.80618190765, -1.5122487545, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'pathIndex': 3, + 'pathScale': 1.0}, + 10008: {'type': 'path', + 'name': 'farPace', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(7.5265827179, 7.56240034103, 0.0), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'pathIndex': 3, + 'pathScale': 1.0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeGearRoom_Battle00.py b/toontown/coghq/LawbotOfficeGearRoom_Battle00.py new file mode 100755 index 00000000..a3d28188 --- /dev/null +++ b/toontown/coghq/LawbotOfficeGearRoom_Battle00.py @@ -0,0 +1,284 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone7a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10001: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(8.03298, 23.7306, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'radius': 15.0}, + 100001: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(15.6666, -22.49, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100002: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-8.21365, 18.3459, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100003: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-17.6094, 17.5472, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100004: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(51.1181, -21.6279, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100005: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(39.917, -25.2425, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabB'}, + 100006: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(36.8762, -25.0662, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA'}, + 100007: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(42.9283, -25.1704, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA'}, + 100008: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(55.8945, -14.0806, 0.05), + 'hpr': Vec3(90, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100009: {'type': 'model', + 'name': 'copy of (6)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(55.8945, 5.46793, 0), + 'hpr': Vec3(90, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100010: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(51.1181, -2.86719, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100011: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(49.0929, 21.7054, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100012: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-8.21365, -63.6967, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100013: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-8.21365, -57.4073, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100014: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-15.1195, -63.5076, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100015: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-0.987966, -63.5446, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100016: {'type': 'model', + 'name': 'copy of (6)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(6.21987, -63.5446, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100017: {'type': 'model', + 'name': 'copy of (7)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(6.21987, -63.5446, 5.45351), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100018: {'type': 'model', + 'name': 'copy of (7)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-38.1114, -20.025, 0), + 'hpr': Vec3(175.135, 0, 0), + 'scale': Vec3(1.81307, 1.81307, 1.81307), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100019: {'type': 'model', + 'name': 'copy of (8)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-38.1114, 16.2551, 0), + 'hpr': Vec3(3.01279, 0, 0), + 'scale': Vec3(1.44025, 1.44025, 1.44025), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_chairA'}, + 100020: {'type': 'model', + 'name': 'copy of (9)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-55.9616, 16.2551, 0), + 'hpr': Vec3(3.01279, 0, 0), + 'scale': Vec3(1.44025, 1.44025, 1.44025), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_chairA'}, + 100021: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-45.4842, 16.387, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100022: {'type': 'model', + 'name': 'copy of (8)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(12.856, -33.9782, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1.81307, 1.81307, 1.81307), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 10000: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(270, 0, 0), + 'scale': 1}, + 100000: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeGearRoom_Battle00_Cogs.py b/toontown/coghq/LawbotOfficeGearRoom_Battle00_Cogs.py new file mode 100755 index 00000000..c10ac737 --- /dev/null +++ b/toontown/coghq/LawbotOfficeGearRoom_Battle00_Cogs.py @@ -0,0 +1,44 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattlePlace1 = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': BattlePlace1, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}] +ReserveCogData = [] diff --git a/toontown/coghq/LawbotOfficeGearRoom_Platform00.py b/toontown/coghq/LawbotOfficeGearRoom_Platform00.py new file mode 100755 index 00000000..3269ae39 --- /dev/null +++ b/toontown/coghq/LawbotOfficeGearRoom_Platform00.py @@ -0,0 +1,274 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone7a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 100008: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(9.21172, -65.1636, 0), + 'hpr': Vec3(218.48, 0, 0), + 'scale': Vec3(1, 1, 1), + 'rewardPerGrab': 20, + 'rewardPerGrabMax': 0}, + 100000: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(-7.80516, 13.4827, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.5, 2.5, 2.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBoxX2'}, + 100002: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(-22.7622, 13.4827, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.5, 2.5, 2.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBoxX3'}, + 100003: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(-22.5819, -16.4761, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.5, 2.5, 2.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100004: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(7.45035, -18.4214, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.5, 2.5, 2.75), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBoxX2'}, + 100005: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(-7.74191, -16.7512, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.5, 2.5, 2.75), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBoxX2'}, + 100009: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(7.38913, -1.56591, 31.7727), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.3, 2.9, 2.62459), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_metal_crate'}, + 100017: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(-7.94704, -1.56591, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.3, 2.92, 2.92), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100012: {'type': 'mover', + 'name': 'mover', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cycleType': 'loop', + 'entity2Move': 100014, + 'modelPath': 0, + 'moveTarget': 100013, + 'pos0Move': 2, + 'pos0Wait': 2, + 'pos1Move': 2, + 'pos1Wait': 2, + 'startOn': 1, + 'switchId': 0}, + 100001: {'type': 'nodepath', + 'name': 'crateMaster', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100011: {'type': 'nodepath', + 'name': 'spotlightMover', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(29.8179, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100013: {'type': 'nodepath', + 'name': 'mover target', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, 10, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100015: {'type': 'nodepath', + 'name': 'lightTarget1', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100016: {'type': 'nodepath', + 'name': 'copy of lightTarget1', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100010: {'type': 'platform', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-22.5404, -1.15634, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.18055, 1.18055, 1.18055), + 'floorName': 'platformcollision', + 'modelPath': 'phase_9/models/cogHQ/platform1', + 'modelScale': Point3(1, 1, 2), + 'motion': 'noBlend', + 'offset': Point3(0, 0, 15), + 'period': 4.0, + 'phaseShift': 0.0, + 'waitPercent': 0.1}, + 100014: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 10.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 30.0, + 'modelPath': 0, + 'projector': Point3(0, 0, 25), + 'radius': 7.0, + 'switchId': 0, + 'trackTarget1': 100015, + 'trackTarget2': 100016, + 'trackTarget3': 0}, + 100006: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-22.9004, -31.939, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(7, 3, 7), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.0, + 'range': 15.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 20, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 100007: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(6.83388, -1.26706, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(7, 3, 7), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.3, + 'range': 15.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 20, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 100018: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-37.5869, -1.26706, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(7, 3, 7), + 'modelPath': 0, + 'motion': 4, + 'period': 2.5, + 'phaseShift': 0.6, + 'range': 15.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 20, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeGearRoom_Security00.py b/toontown/coghq/LawbotOfficeGearRoom_Security00.py new file mode 100755 index 00000000..5a02a82c --- /dev/null +++ b/toontown/coghq/LawbotOfficeGearRoom_Security00.py @@ -0,0 +1,559 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone7a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10013: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(57.0219, 5.15024, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(0.660517, 0.660517, 0.660517), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10015: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-25.74, 58.3575, 9.73551), + 'hpr': Vec3(95.4403, 0, 0), + 'scale': Vec3(1.5379, 1.5379, 1.5379), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 10016: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(33.3395, -18.3643, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(0.66, 0.66, 0.66), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D1.bam'}, + 10017: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(169.7, 0, 0), + 'scale': Vec3(0.90247, 0.90247, 0.90247), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D4.bam'}, + 10020: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(-12.0714, 0, 0), + 'hpr': Vec3(288.435, 0, 0), + 'scale': Vec3(0.90247, 0.90247, 0.90247), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D4.bam'}, + 10022: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-5.97179, -60.3134, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(0.869391, 0.869391, 0.869391), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 100015: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(-1.31696, 0, 0.1), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.6, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100016: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(-12.7478, -11.9991, 0.05), + 'hpr': Vec3(180.47, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA'}, + 100017: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(-17.0503, 0, 0.1), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100019: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(0.897832, -12.2053, 0.05), + 'hpr': Vec3(180.47, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA'}, + 100020: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(6.3491, -6.57612, 0.05), + 'hpr': Vec3(90, 0, 0), + 'scale': Point3(1, 1, 0.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA'}, + 100021: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(-20.9336, -5.07158, 0.05), + 'hpr': Vec3(90, 0, 0), + 'scale': Vec3(1.00449, 1.00449, 1.00449), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks'}, + 100022: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(4.96172, -5.07158, 0.05), + 'hpr': Vec3(272.49, 0, 0), + 'scale': Vec3(1.00449, 1.00449, 1.00449), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks'}, + 100023: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(-20.5363, -8.42755, 0.05), + 'hpr': Vec3(90, 0, 0), + 'scale': Vec3(1.00449, 1.00449, 1.00449), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_chairA'}, + 100024: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(-4.9392, -12.3495, 0.05), + 'hpr': Vec3(180.47, 0, 0), + 'scale': Vec3(3.79099, 3.79099, 3.79099), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampA'}, + 100026: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(16.7866, 12.9562, 0.1), + 'hpr': Vec3(185.194, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100027: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(-21.2469, 12.8535, 0.0929851), + 'hpr': Vec3(187.125, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100028: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(5.20127, 12.8535, 0.0929851), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100029: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(-8.51009, 13.1118, 0.0929851), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100030: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(-15.7803, 1.79844, 0.0929851), + 'hpr': Vec3(188.13, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100031: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(14.449, 2.90238, 0.0929851), + 'hpr': Vec3(184.764, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100032: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(12.2409, -22.0432, 0.0929851), + 'hpr': Vec3(184.764, 0, 0), + 'scale': Point3(1.4, 1.4, 1.4), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100033: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(-1.1837, 1.79844, 0.0929851), + 'hpr': Vec3(170.538, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 10000: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, -2), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10018: {'type': 'nodepath', + 'name': 'rightVertPipes', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-16.4537, -45.3982, -8.4), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.65, 0.65, 1.56)}, + 10021: {'type': 'nodepath', + 'name': 'rightPipes', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100001: {'type': 'nodepath', + 'name': 'cameraTarget1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(10, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100002: {'type': 'nodepath', + 'name': 'copy of cameraTarget1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(30, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100003: {'type': 'nodepath', + 'name': 'copy of cameraTarget1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(40, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100005: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-30, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100006: {'type': 'nodepath', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-60, 15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100007: {'type': 'nodepath', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-60, -15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100009: {'type': 'nodepath', + 'name': 'camera3 target', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(25, -2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100010: {'type': 'nodepath', + 'name': 'copy of camera3 target', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-10, -2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100011: {'type': 'nodepath', + 'name': 'copy of camera3 target (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-50, -2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100013: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-10, 60, 10), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100014: {'type': 'nodepath', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-10, 40, 10), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100018: {'type': 'nodepath', + 'name': 'wall1parent', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-7.53236, 20.7488, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100025: {'type': 'nodepath', + 'name': 'wall2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-7.36698, -23.6933, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100035: {'type': 'nodepath', + 'name': 'targ1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-27.7132, -17.0199, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100036: {'type': 'nodepath', + 'name': 'copy of targ1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(9.37401, -17.0199, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100038: {'type': 'nodepath', + 'name': 'tegrat', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(8.31643, -40.4532, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100039: {'type': 'nodepath', + 'name': 'copy of tegrat', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-27.6613, -37.0841, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100040: {'type': 'nodepath', + 'name': 'copy of tegrat (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-6.48412, -29.8115, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100042: {'type': 'nodepath', + 'name': 'gettar', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-7.92397, 14.3026, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100043: {'type': 'nodepath', + 'name': 'copy of gettar', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-23.1978, 15.1905, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100000: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(33.4453, -2.27555, 0), + 'hpr': Point3(-3, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 6.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 10.0, + 'modelPath': 0, + 'projector': Point3(-3, -3, 25), + 'radius': 6.0, + 'switchId': 0, + 'trackTarget1': 100001, + 'trackTarget2': 100002, + 'trackTarget3': 100003}, + 100004: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-58.4773, 4.03197, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 4.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100005, + 'trackTarget2': 100006, + 'trackTarget3': 100007}, + 100008: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-22.5923, -33.41, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 17.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 20.0, + 'modelPath': 0, + 'projector': Point3(12, 16, 32), + 'radius': 7.0, + 'switchId': 0, + 'trackTarget1': 100009, + 'trackTarget2': 100010, + 'trackTarget3': 100011}, + 100012: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-9.20073, 65.6563, 8.45), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 7.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(0, 0, 17), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100014, + 'trackTarget2': 100013, + 'trackTarget3': 0}, + 100034: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, -10.5537, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 40.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 20.0, + 'modelPath': 0, + 'projector': Point3(10, 0, 25), + 'radius': 4.0, + 'switchId': 0, + 'trackTarget1': 100035, + 'trackTarget2': 100036, + 'trackTarget3': 0}, + 100037: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-28.9964, -30.2849, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 12.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100039, + 'trackTarget2': 100038, + 'trackTarget3': 100040}, + 100041: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-32.9569, 19.6137, 0.0470875), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 12.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(12, 0, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100042, + 'trackTarget2': 100043, + 'trackTarget3': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeLobby_Action00.py b/toontown/coghq/LawbotOfficeLobby_Action00.py new file mode 100755 index 00000000..97f1600d --- /dev/null +++ b/toontown/coghq/LawbotOfficeLobby_Action00.py @@ -0,0 +1,1629 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone04a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10005: {'type': 'attribModifier', + 'name': 'stomperPeriod', + 'comment': '', + 'parentEntId': 10046, + 'attribName': 'period', + 'recursive': 1, + 'typeName': 'stomper', + 'value': '2.2'}, + 10015: {'type': 'attribModifier', + 'name': 'stomperShaftScale', + 'comment': '', + 'parentEntId': 10046, + 'attribName': 'shaftScale', + 'recursive': 1, + 'typeName': 'stomper', + 'value': 'Vec3(1,5,1)'}, + 10067: {'type': 'attribModifier', + 'name': 'stomperDamage', + 'comment': '', + 'parentEntId': 10000, + 'attribName': 'damage', + 'recursive': 1, + 'typeName': 'stomper', + 'value': '8'}, + 10130: {'type': 'attribModifier', + 'name': '', + 'comment': '', + 'parentEntId': 10153, + 'attribName': 'modelPath', + 'recursive': 1, + 'typeName': 'model', + 'value': "'phase_10/models/cogHQ/CBMetalCrate2.bam'"}, + 10145: {'type': 'attribModifier', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10007, + 'attribName': 'modelPath', + 'recursive': 1, + 'typeName': 'model', + 'value': "'phase_10/models/cogHQ/CBMetalCrate2.bam'"}, + 10173: {'type': 'attribModifier', + 'name': 'copy of stomperShaftScale', + 'comment': '', + 'parentEntId': 10154, + 'attribName': 'shaftScale', + 'recursive': 1, + 'typeName': 'stomper', + 'value': 'Vec3(1,5,1)'}, + 10169: {'type': 'gagBarrel', + 'name': 'gag', + 'comment': '', + 'parentEntId': 10170, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(1.0, 0.0, 0.0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 'random', + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 6}, + 10171: {'type': 'gagBarrel', + 'name': 'gagLeft', + 'comment': '', + 'parentEntId': 10170, + 'pos': Point3(-3.0, 0.0, 0.0), + 'hpr': Vec3(-6.0, 0.0, 0.0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 'random', + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 6}, + 10172: {'type': 'gagBarrel', + 'name': 'gagRight', + 'comment': '', + 'parentEntId': 10170, + 'pos': Point3(3.0, 0.0, 0.0), + 'hpr': Vec3(9.0, 0.0, 0.0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 'random', + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 6}, + 10177: {'type': 'healBarrel', + 'name': 'healLeft', + 'comment': '', + 'parentEntId': 10170, + 'pos': Point3(-1.05319225788, 0.0, 4.12134313583), + 'hpr': Vec3(20.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 6}, + 10178: {'type': 'healBarrel', + 'name': 'healRight', + 'comment': '', + 'parentEntId': 10170, + 'pos': Point3(2.16605138779, 0.0, 4.12134313583), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 6}, + 10181: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-2.92873573303, 110.585220337, 5.00378036499), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 10, + 'rewardPerGrabMax': 0}, + 10001: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10002: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10003: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10008: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10009: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10011: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10012: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10013: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10014: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10024: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10020, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10025: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10020, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10027: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10028: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10029: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10030: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10036: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10033, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10037: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10033, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10038: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10033, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10039: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10034, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10040: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10034, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10041: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10034, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10042: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10043: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10048: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10044, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10049: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10044, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10050: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10051: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10052: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10047, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10053: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10047, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10076: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10075, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10077: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10075, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10078: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10075, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10079: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10047, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10084: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10081, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10085: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10081, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10086: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10081, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10087: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10082, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10088: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10082, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10089: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10082, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10090: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10083, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10091: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10083, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10102: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10072, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10108: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10105, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10109: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10105, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10110: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10072, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10111: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10106, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10112: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10106, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10113: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10072, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10114: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10107, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10115: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10107, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10116: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10107, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10121: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10118, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10122: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10118, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10123: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10118, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10124: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10119, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10125: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10119, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10126: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10119, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10127: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10120, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10128: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10120, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10129: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10073, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10131: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10073, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10132: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10074, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10133: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10074, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10134: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10074, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10139: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10136, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10140: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10136, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10141: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10136, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10143: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10137, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10146: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10137, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10147: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10138, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10148: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10138, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10149: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10144, + 'pos': Point3(-6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10150: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10144, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10151: {'type': 'model', + 'name': 'right', + 'comment': '', + 'parentEntId': 10144, + 'pos': Point3(6.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10152: {'type': 'model', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10138, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cogHQ/CBMetalCrate2.bam'}, + 10158: {'type': 'model', + 'name': 'left', + 'comment': '', + 'parentEntId': 10157, + 'pos': Point3(-11.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(1.34000003338, 1.34000003338, 1.34000003338), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/shelf_A1.bam'}, + 10000: {'type': 'nodepath', + 'name': 'crateField', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, -51.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10004: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10006: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10007: {'type': 'nodepath', + 'name': 'crates', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 0.800000011921)}, + 10010: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10017: {'type': 'nodepath', + 'name': 'wall5', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 90.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10018: {'type': 'nodepath', + 'name': 'crateSquare0', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10019: {'type': 'nodepath', + 'name': 'crateSquare1', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 18.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10020: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10021: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10022: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10031: {'type': 'nodepath', + 'name': 'crateSquare3', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 54.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10032: {'type': 'nodepath', + 'name': 'crateSquare2', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 36.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10033: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10034: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10035: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10044: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10045: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10046: {'type': 'nodepath', + 'name': 'stompers', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-1.0, 0.0, 4.40000009537), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10047: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10059: {'type': 'nodepath', + 'name': 'wall6', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 108.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10062: {'type': 'nodepath', + 'name': 'wall7', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 124.5, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.39999997616, 1.0)}, + 10063: {'type': 'nodepath', + 'name': 'walls', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0.0, -0.019999999553, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10065: {'type': 'nodepath', + 'name': 'wall0', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10070: {'type': 'nodepath', + 'name': 'leftBranch', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-17.8978881836, 72.0, 0.0), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10071: {'type': 'nodepath', + 'name': 'crateSquare0', + 'comment': 'Y=N*18', + 'parentEntId': 10153, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10072: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10071, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10073: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10071, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10074: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10071, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10075: {'type': 'nodepath', + 'name': 'frontCrateRow', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0.0, -12.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(0.844285488129, 1.0, 1.0)}, + 10080: {'type': 'nodepath', + 'name': 'crateSquare4', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 72.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10081: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10080, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10082: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10080, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10083: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10080, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10092: {'type': 'nodepath', + 'name': 'wall1', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 18.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10095: {'type': 'nodepath', + 'name': 'wall2', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 36.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10098: {'type': 'nodepath', + 'name': 'wall3', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 54.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10101: {'type': 'nodepath', + 'name': 'wall4', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(0.0, 72.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10104: {'type': 'nodepath', + 'name': 'crateSquare5', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 90.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10105: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10104, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10106: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10104, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10107: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10104, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10117: {'type': 'nodepath', + 'name': 'crateSquare6', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 108.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10118: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10117, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10119: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10117, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10120: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10117, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10135: {'type': 'nodepath', + 'name': 'crateSquare1', + 'comment': 'Y=N*18', + 'parentEntId': 10153, + 'pos': Point3(0.0, 18.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10136: {'type': 'nodepath', + 'name': 'row2', + 'comment': '', + 'parentEntId': 10135, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10137: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10135, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10138: {'type': 'nodepath', + 'name': 'row1', + 'comment': '', + 'parentEntId': 10135, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10142: {'type': 'nodepath', + 'name': 'crateSquare7', + 'comment': 'Y=N*18', + 'parentEntId': 10007, + 'pos': Point3(0.0, 126.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10144: {'type': 'nodepath', + 'name': 'row0', + 'comment': '', + 'parentEntId': 10142, + 'pos': Point3(0.0, -6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10153: {'type': 'nodepath', + 'name': 'crates', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 0.800000011921)}, + 10154: {'type': 'nodepath', + 'name': 'stompers', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(-1.0, 0.0, 4.40000009537), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10156: {'type': 'nodepath', + 'name': 'walls', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(0.0, 6.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10157: {'type': 'nodepath', + 'name': 'wall0', + 'comment': '', + 'parentEntId': 10156, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10160: {'type': 'nodepath', + 'name': 'wall1', + 'comment': '', + 'parentEntId': 10156, + 'pos': Point3(0.0, 18.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10163: {'type': 'nodepath', + 'name': 'wall2', + 'comment': '', + 'parentEntId': 10156, + 'pos': Point3(0.0, 36.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Point3(1.0, 1.0, 1.0)}, + 10168: {'type': 'nodepath', + 'name': 'safeArea', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(0.0, 40.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10170: {'type': 'nodepath', + 'name': 'barrels', + 'comment': '', + 'parentEntId': 10168, + 'pos': Point3(0.0, 9.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10016: {'type': 'stomper', + 'name': 'stomper6', + 'comment': 'Y=N*18', + 'parentEntId': 10046, + 'pos': Point3(1.0, 108.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10054: {'type': 'stomper', + 'name': 'stomper1', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(1.0, 18.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10055: {'type': 'stomper', + 'name': 'stomper0', + 'comment': 'Y=N*18', + 'parentEntId': 10046, + 'pos': Point3(1.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10056: {'type': 'stomper', + 'name': 'stomper2', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(1.0, 36.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10057: {'type': 'stomper', + 'name': 'stomper3', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(1.0, 54.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10058: {'type': 'stomper', + 'name': 'stomper4', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(1.0, 72.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10155: {'type': 'stomper', + 'name': 'stomper5', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(1.0, 90.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10166: {'type': 'stomper', + 'name': 'stomper0', + 'comment': 'Y=N*18', + 'parentEntId': 10154, + 'pos': Point3(1.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10167: {'type': 'stomper', + 'name': 'stomper1', + 'comment': 'Y=N*18', + 'parentEntId': 10154, + 'pos': Point3(1.0, 18.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1, + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 8, + 'headScale': Point3(9.0, 9.0, 9.0), + 'modelPath': 0, + 'motion': 2, + 'period': 2.2, + 'phaseShift': 0.0, + 'range': 13.0, + 'removeCamBarrierCollisions': 1, + 'removeHeadFloor': 1, + 'shaftScale': Vec3(1.0, 5.0, 1.0), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeLobby_Action01.py b/toontown/coghq/LawbotOfficeLobby_Action01.py new file mode 100755 index 00000000..65b116a1 --- /dev/null +++ b/toontown/coghq/LawbotOfficeLobby_Action01.py @@ -0,0 +1,839 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone04a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10055: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'length': 20.0, + 'radius': 15.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10058: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(29.3404, 25.876, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 6.0, + 'radius': 3.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10059: {'type': 'collisionSolid', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(-13.34, 25.88, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 6.0, + 'radius': 3.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10001: {'type': 'model', + 'name': 'middlePillar', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(4.40185, 4.40185, 4.40185), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pillar.bam'}, + 10002: {'type': 'model', + 'name': 'leftCouch', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-12.4308, 0, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA.bam'}, + 10003: {'type': 'model', + 'name': 'rightCouch', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(12.43, 0, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA.bam'}, + 10004: {'type': 'model', + 'name': 'frontChair', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, -13, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_chairA.bam'}, + 10005: {'type': 'model', + 'name': 'backChair', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 13, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_chairA.bam'}, + 10006: {'type': 'model', + 'name': 'frontlamp', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-10, -14, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB.bam'}, + 10007: {'type': 'model', + 'name': 'backLamp', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(10, 14, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB.bam'}, + 10009: {'type': 'model', + 'name': 'movingShelf', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0.1, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 0.7, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10012: {'type': 'model', + 'name': 'leftShelf', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10013: {'type': 'model', + 'name': 'rightShelf', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(15.8, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10015: {'type': 'model', + 'name': 'copy of leftShelf', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10017: {'type': 'model', + 'name': 'copy of leftShelf (2)', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(8.7, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10018: {'type': 'model', + 'name': 'copy of leftShelf (3)', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(17.4, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10019: {'type': 'model', + 'name': 'copy of leftShelf', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(-8.7, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10020: {'type': 'model', + 'name': 'copy of rightShelf', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(24.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10023: {'type': 'model', + 'name': 'copy of rightShelf (2)', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-10.66, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10024: {'type': 'model', + 'name': 'copy of rightShelf (3)', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-1.97, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10025: {'type': 'model', + 'name': 'copy of rightShelf (4)', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(6.65, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10026: {'type': 'model', + 'name': 'copy of rightShelf (3)', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10027: {'type': 'model', + 'name': 'copy of rightShelf (4)', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-8.7173, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10028: {'type': 'model', + 'name': 'copy of rightShelf (5)', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-17.416, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10030: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA.bam'}, + 10031: {'type': 'model', + 'name': 'moveWall', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(-8.7, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(1, 0.7, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA.bam'}, + 10033: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA.bam'}, + 10034: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(8.7, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(1, 0.7, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA.bam'}, + 10046: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10068, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10047: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10068, + 'pos': Point3(14.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10048: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10068, + 'pos': Point3(-14.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10049: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10068, + 'pos': Point3(-29.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10051: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10069, + 'pos': Point3(-14.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10052: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10069, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10053: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10069, + 'pos': Point3(14.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10054: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 10069, + 'pos': Point3(29.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10010: {'type': 'mover', + 'name': 'shelfMover', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'cycleType': 'loop', + 'entity2Move': 10009, + 'modelPath': 0, + 'moveTarget': 10011, + 'pos0Move': 2.0, + 'pos0Wait': 0.0, + 'pos1Move': 2, + 'pos1Wait': 3.0, + 'startOn': 1, + 'switchId': 0}, + 10035: {'type': 'mover', + 'name': 'wallMover', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'cycleType': 'loop', + 'entity2Move': 10031, + 'modelPath': 0, + 'moveTarget': 10036, + 'pos0Move': 2, + 'pos0Wait': 6.0, + 'pos1Move': 2, + 'pos1Wait': 2, + 'startOn': 1, + 'switchId': 0}, + 10037: {'type': 'mover', + 'name': 'copy of wallMover', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'cycleType': 'loop', + 'entity2Move': 10034, + 'modelPath': 0, + 'moveTarget': 10038, + 'pos0Move': 2, + 'pos0Wait': 6.0, + 'pos1Move': 2, + 'pos1Wait': 2.0, + 'startOn': 1, + 'switchId': 0}, + 10000: {'type': 'nodepath', + 'name': 'center', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10008: {'type': 'nodepath', + 'name': 'maze', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-11.32, 35.7767, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.4, 1.4, 1.4)}, + 10011: {'type': 'nodepath', + 'name': 'movetarget', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(7, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10014: {'type': 'nodepath', + 'name': 'frontDoor', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0, 0.75, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10016: {'type': 'nodepath', + 'name': 'firstInnerWall', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(-1.4, 9.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10021: {'type': 'nodepath', + 'name': 'rightWall', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(28.038, 16.5374, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10022: {'type': 'nodepath', + 'name': 'leftWall', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(-12.3934, 5.78821, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10029: {'type': 'nodepath', + 'name': 'rearRightWall', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(19.8603, 23.5345, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10032: {'type': 'nodepath', + 'name': 'rearLeftWall', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(-5.06744, 23.3692, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10036: {'type': 'nodepath', + 'name': 'moveTarget', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(8.7, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10038: {'type': 'nodepath', + 'name': 'copy of moveTarget', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(-8.7, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10039: {'type': 'nodepath', + 'name': 'stompers', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10044: {'type': 'nodepath', + 'name': 'walls', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10045: {'type': 'nodepath', + 'name': 'leftWall', + 'comment': '', + 'parentEntId': 10044, + 'pos': Point3(-40, -2, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Point3(1, 1, 1)}, + 10050: {'type': 'nodepath', + 'name': 'rightWall', + 'comment': '', + 'parentEntId': 10044, + 'pos': Point3(40, -2, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(1, 1, 1)}, + 10062: {'type': 'nodepath', + 'name': 'cameras', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10063: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(5, 6, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10065: {'type': 'nodepath', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(-10, 6, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10066: {'type': 'nodepath', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(5, 17, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10067: {'type': 'nodepath', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(-10, 17, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10068: {'type': 'nodepath', + 'name': 'scaleNode', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.7, 1, 1)}, + 10069: {'type': 'nodepath', + 'name': 'scaleNode', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.7, 1, 1)}, + 10070: {'type': 'nodepath', + 'name': 'cameras', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(4, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10073: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(5, 6, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10074: {'type': 'nodepath', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(-10, 6, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10075: {'type': 'nodepath', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(5, 17, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10076: {'type': 'nodepath', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(-10, 17, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10056: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 1, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(0, 0, 28), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10063, + 'trackTarget2': 10065, + 'trackTarget3': 10066}, + 10057: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(-10, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 1, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(0, 0, 28), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10065, + 'trackTarget2': 10066, + 'trackTarget3': 10067}, + 10071: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 1, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(0, 0, 28), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10073, + 'trackTarget2': 10074, + 'trackTarget3': 10075}, + 10072: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(-10, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 1, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(0, 0, 28), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10074, + 'trackTarget2': 10075, + 'trackTarget3': 10076}, + 10040: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(0, 5.05514, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2.5, 2.5, 2.5), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Vec3(1, 1, 1), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 3.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 12, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10041: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(20, 5.06, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2.5, 2.5, 2.5), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Vec3(1, 1, 1), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 3.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 12, 1), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10042: {'type': 'stomper', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(23.7219, 13.3088, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2.5, 2.5, 2.5), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Vec3(1, 1, 1), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 3.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 8, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10043: {'type': 'stomper', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(-8.59782, 13.3088, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2.5, 2.5, 2.5), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Vec3(1, 1, 1), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 3.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 8, 1), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeLobby_Lights00.py b/toontown/coghq/LawbotOfficeLobby_Lights00.py new file mode 100755 index 00000000..5e1e9c23 --- /dev/null +++ b/toontown/coghq/LawbotOfficeLobby_Lights00.py @@ -0,0 +1,229 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone04a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 100016: {'type': 'beanBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-1.17415, -2.82071, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'rewardPerGrab': 42, + 'rewardPerGrabMax': 0}, + 100017: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(1.70419, 4.05857, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'gagLevel': 5, + 'gagLevelMax': 5, + 'gagTrack': 'random', + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 100018: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-6.14262, 5.87553, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'gagLevel': 5, + 'gagLevelMax': 5, + 'gagTrack': 'random', + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 100011: {'type': 'mover', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'cycleType': 'loop', + 'entity2Move': 100000, + 'modelPath': 0, + 'moveTarget': 100012, + 'pos0Move': 60.0, + 'pos0Wait': 10.0, + 'pos1Move': 60.0, + 'pos1Wait': 10.0, + 'startOn': 1, + 'switchId': 0}, + 100000: {'type': 'nodepath', + 'name': 'cameraRing', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100006: {'type': 'nodepath', + 'name': 'centerTarget', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100007: {'type': 'nodepath', + 'name': 'sidetarget1', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(8.39848, 43.7828, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100008: {'type': 'nodepath', + 'name': 'sidetarget2', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(7.54716, -38.4515, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100009: {'type': 'nodepath', + 'name': 'sidetarget3', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-39.4668, 1.83033, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100010: {'type': 'nodepath', + 'name': 'sidetarget4', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(39.47, 1.83033, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100012: {'type': 'nodepath', + 'name': 'moverTarget', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(360, 0, 0), + 'scale': 1}, + 100013: {'type': 'nodepath', + 'name': 'target exit', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.535633, 68.0682, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100014: {'type': 'nodepath', + 'name': 'targetoff1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(47.1742, 5.53207, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100015: {'type': 'nodepath', + 'name': 'targetoff2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-47.17, 5.53207, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100001: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(24.3, -17, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(5, 5, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100006, + 'trackTarget2': 100013, + 'trackTarget3': 100008}, + 100002: {'type': 'securityCamera', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(28.5, 9.27, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(5, 5, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100006, + 'trackTarget2': 100008, + 'trackTarget3': 100013}, + 100003: {'type': 'securityCamera', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(0, 30, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(5, 5, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100006, + 'trackTarget2': 100009, + 'trackTarget3': 100014}, + 100004: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-24.3, -17, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100006, + 'trackTarget2': 100010, + 'trackTarget3': 100007}, + 100005: {'type': 'securityCamera', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-28.5, 9.27, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(5, 5, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100006, + 'trackTarget2': 100007, + 'trackTarget3': 100015}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeLobby_Trap00.py b/toontown/coghq/LawbotOfficeLobby_Trap00.py new file mode 100755 index 00000000..1738326c --- /dev/null +++ b/toontown/coghq/LawbotOfficeLobby_Trap00.py @@ -0,0 +1,565 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone04a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 100018: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(54.7666, 7.03896, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 1, 1, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 100019: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-58.0835, 7.37219, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 1, 1, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 100015: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 100005, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 0, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 0, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 100016, + 'unlock1Event': 0, + 'unlock2Event': 100017, + 'unlock3Event': 0}, + 100016: {'type': 'laserField', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-15.1345, -13.2285, 0.25), + 'hpr': Point3(90, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'gridGame': 'Random', + 'gridScaleX': 42.0, + 'gridScaleY': 40.0, + 'laserFactor': 3, + 'modelPath': 0, + 'projector': Point3(20, 40, 45), + 'switchId': 100019}, + 100017: {'type': 'laserField', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(11.2941, 28.7739, 0.28), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 1, + 'gridGame': 'Random', + 'gridScaleX': 42.0, + 'gridScaleY': 40.0, + 'laserFactor': 3, + 'modelPath': 0, + 'projector': Point3(20, 40, 45), + 'switchId': 100018}, + 100001: {'type': 'model', + 'name': 'copy of partition (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-8.98508, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100002: {'type': 'model', + 'name': 'copy of partition (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(5.36486, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100003: {'type': 'model', + 'name': 'copy of partition (5)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(20.1513, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100004: {'type': 'model', + 'name': 'copy of partition (6)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(34.9439, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100007: {'type': 'model', + 'name': 'copy of partition', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100008: {'type': 'model', + 'name': 'copy of partition (2)', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(-14.9029, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100009: {'type': 'model', + 'name': 'copy of partition (3)', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(-29.7119, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100010: {'type': 'model', + 'name': 'copy of partition (4)', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(-44.4821, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100012: {'type': 'model', + 'name': 'copy of partition (3)', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100013: {'type': 'model', + 'name': 'copy of partition (4)', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(-14.9149, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100014: {'type': 'model', + 'name': 'copy of partition (5)', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(-29.7289, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100023: {'type': 'model', + 'name': 'copy of partition (6)', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(-44.4361, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100025: {'type': 'model', + 'name': 'copy of partition (7)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(42.3323, -38.4749, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100026: {'type': 'model', + 'name': 'copy of partition (8)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(42.3323, -25.7861, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100027: {'type': 'model', + 'name': 'copy of partition (5)', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(-52.4944, -39.8953, 0), + 'hpr': Vec3(270.818, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100028: {'type': 'model', + 'name': 'copy of partition (6)', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(-52.4944, -25.343, 0), + 'hpr': Vec3(270.818, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100029: {'type': 'model', + 'name': 'scenary', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-38.0023, -20.533, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA.bam'}, + 100030: {'type': 'model', + 'name': 'copy of scenary', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-28.9763, -28.2505, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA.bam'}, + 100031: {'type': 'model', + 'name': 'copy of scenary (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-51.8392, -28.2505, 0), + 'hpr': Vec3(131.894, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 100032: {'type': 'model', + 'name': 'copy of scenary (3)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-44.1843, -37.0309, 0), + 'hpr': Vec3(131.894, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 100033: {'type': 'model', + 'name': 'copy of scenary (4)', + 'comment': '', + 'parentEntId': 100034, + 'pos': Point3(16.5447, -16.1896, 0), + 'hpr': Vec3(358.668, 0, 0), + 'scale': Point3(2, 2, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 100037: {'type': 'model', + 'name': 'partition', + 'comment': '', + 'parentEntId': 100036, + 'pos': Point3(-14.6846, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100038: {'type': 'model', + 'name': 'copy of partition', + 'comment': '', + 'parentEntId': 100036, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100039: {'type': 'model', + 'name': 'copy of partition', + 'comment': '', + 'parentEntId': 100036, + 'pos': Point3(15.087, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100040: {'type': 'model', + 'name': 'copy of partition (2)', + 'comment': '', + 'parentEntId': 100036, + 'pos': Point3(-29.9877, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100041: {'type': 'model', + 'name': 'copy of scenary (5)', + 'comment': '', + 'parentEntId': 100034, + 'pos': Point3(20.0815, -16.1896, 0), + 'hpr': Vec3(358.668, 0, 0), + 'scale': Point3(2, 2, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 100042: {'type': 'model', + 'name': 'copy of scenary (6)', + 'comment': '', + 'parentEntId': 100034, + 'pos': Point3(31.1973, -15.8974, 0), + 'hpr': Vec3(358.668, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA.bam'}, + 100043: {'type': 'model', + 'name': 'box', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-10.7296, -3.52948, 0), + 'hpr': Vec3(270.924, 0, 0), + 'scale': Point3(2, 8, 2), + 'collisionsOnly': 1, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100044: {'type': 'model', + 'name': 'copy of box', + 'comment': '', + 'parentEntId': 100036, + 'pos': Point3(-32.0389, 7.09736, 0), + 'hpr': Vec3(90, 0, 0), + 'scale': Point3(1, 8, 2), + 'collisionsOnly': 1, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100045: {'type': 'model', + 'name': 'copy of box (2)', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(2.05698, -7.92436, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Point3(2, 8, 2), + 'collisionsOnly': 1, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100046: {'type': 'model', + 'name': 'copy of box (3)', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(1.66589, 6.97288, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Point3(2, 8, 2), + 'collisionsOnly': 1, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10000: {'type': 'nodepath', + 'name': '0', + 'comment': '', + 'parentEntId': 100035, + 'pos': Point3(-64.0541, 7.6285, 0), + 'hpr': Vec3(90, 0, 0), + 'scale': Point3(1, 1, 1)}, + 100020: {'type': 'nodepath', + 'name': 'battle place 1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-35.0928, 6.21518, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100021: {'type': 'nodepath', + 'name': 'cogparent2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(64.2291, 5.46719, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100022: {'type': 'nodepath', + 'name': 'BattlePlace2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(32.4809, 7.68949, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100035: {'type': 'nodepath', + 'name': 'cogparent1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100000: {'type': 'rendering', + 'name': 'PartitionWall2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-53.71, -14.4718, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 100005: {'type': 'rendering', + 'name': 'doorparent', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-0.845594, 73.9927, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 0.75, + 'colorG': 0.75, + 'colorR': 0.8, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 100006: {'type': 'rendering', + 'name': 'wallparent3', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(61.7299, -14.6471, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 100011: {'type': 'rendering', + 'name': 'wallprent4', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(62.3555, 30.4101, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 100024: {'type': 'rendering', + 'name': 'cogrenderingparent2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(64.2291, 5.46719, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Additive', + 'colorA': 1.0, + 'colorB': 0.0, + 'colorG': 0.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'fixed'}, + 100034: {'type': 'rendering', + 'name': 'scenaryLight', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 0.5, + 'colorG': 0.5, + 'colorR': 0.6, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 100036: {'type': 'rendering', + 'name': 'partitionwall1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-33.8745, 29.3783, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'transparent'}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeLobby_Trap00_Cogs.py b/toontown/coghq/LawbotOfficeLobby_Trap00_Cogs.py new file mode 100755 index 00000000..157e76f6 --- /dev/null +++ b/toontown/coghq/LawbotOfficeLobby_Trap00_Cogs.py @@ -0,0 +1,85 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +CogParent1 = 100021 +BattlePlace1 = 100020 +BattlePlace2 = 100022 +BattleCellId = 0 +BattleCellId1 = 1 +BattleCells = {BattleCellId: {'parentEntId': BattlePlace1, + 'pos': Point3(0, 0, 0)}, + BattleCellId1: {'parentEntId': BattlePlace2, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId1, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId1, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId1, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId1, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}] +ReserveCogData = [] diff --git a/toontown/coghq/LawbotOfficeOilRoom_Battle00.py b/toontown/coghq/LawbotOfficeOilRoom_Battle00.py new file mode 100755 index 00000000..9e52c6ee --- /dev/null +++ b/toontown/coghq/LawbotOfficeOilRoom_Battle00.py @@ -0,0 +1,56 @@ +from toontown.coghq.SpecImports import * +import random +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone22a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 100030: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-0.124318, -27.1644, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'radius': 25.0}, + 100000: {'type': 'elevatorMarker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.199988, -31.3479, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': Point3(1, 1, 1), + 'modelPath': 0}, + 100001: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, -30.3883, 13.5561), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(2.6262, 2.6262, 2.6262), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0], + 'titleString': 'MemTag: LawbotOfficeOilRoom_Battle00 %s' % random.random()} diff --git a/toontown/coghq/LawbotOfficeOilRoom_Battle00_Cogs.py b/toontown/coghq/LawbotOfficeOilRoom_Battle00_Cogs.py new file mode 100755 index 00000000..ceb3cfcc --- /dev/null +++ b/toontown/coghq/LawbotOfficeOilRoom_Battle00_Cogs.py @@ -0,0 +1,43 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintBossLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/LawbotOfficeOilRoom_Battle01.py b/toontown/coghq/LawbotOfficeOilRoom_Battle01.py new file mode 100755 index 00000000..0384a1f5 --- /dev/null +++ b/toontown/coghq/LawbotOfficeOilRoom_Battle01.py @@ -0,0 +1,66 @@ +from toontown.coghq.SpecImports import * +import random +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone22a', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 100030: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-0.942754, -16.0554, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'radius': 20.0}, + 100001: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-0.345429, -31.6315, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(2.65, 2.55, 3.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB'}, + 100002: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(0, -31.6418, 20.1296), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(2.65, 2.52, 3.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': 1}, + 100000: {'type': 'nodepath', + 'name': 'extra cases', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0], + 'titleString': 'MemTag: LawbotOfficeOilRoom_Battle01 %s' % random.random()} diff --git a/toontown/coghq/LawbotOfficeOilRoom_Battle01_Cogs.py b/toontown/coghq/LawbotOfficeOilRoom_Battle01_Cogs.py new file mode 100755 index 00000000..61883ec4 --- /dev/null +++ b/toontown/coghq/LawbotOfficeOilRoom_Battle01_Cogs.py @@ -0,0 +1,43 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 1, + 'level': ToontownGlobals.CashbotMintBossLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [] diff --git a/toontown/coghq/LevelBattleManagerAI.py b/toontown/coghq/LevelBattleManagerAI.py new file mode 100755 index 00000000..5cedf6f2 --- /dev/null +++ b/toontown/coghq/LevelBattleManagerAI.py @@ -0,0 +1,49 @@ +from toontown.battle import BattleManagerAI +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import BattleExperienceAggregatorAI + +class LevelBattleManagerAI(BattleManagerAI.BattleManagerAI): + notify = DirectNotifyGlobal.directNotify.newCategory('LevelBattleManagerAI') + + def __init__(self, air, level, battleCtor, battleExpAggreg = None): + BattleManagerAI.BattleManagerAI.__init__(self, air) + self.battleCtor = battleCtor + self.level = level + self.battleBlockers = {} + if battleExpAggreg is None: + battleExpAggreg = BattleExperienceAggregatorAI.BattleExperienceAggregatorAI() + self.battleExpAggreg = battleExpAggreg + return + + def destroyBattleMgr(self): + battles = self.cellId2battle.values() + for battle in battles: + self.destroy(battle) + + for cellId, battleBlocker in self.battleBlockers.items(): + if battleBlocker is not None: + battleBlocker.deactivate() + + del self.battleBlockers + del self.cellId2battle + del self.battleExpAggreg + return + + def newBattle(self, cellId, zoneId, pos, suit, toonId, roundCallback = None, finishCallback = None, maxSuits = 4): + battle = self.cellId2battle.get(cellId, None) + if battle != None: + self.notify.debug('battle already created by battle blocker, add toon %d' % toonId) + battle.signupToon(toonId, pos[0], pos[1], pos[2]) + return battle + else: + battle = self.battleCtor(self.air, self, pos, suit, toonId, zoneId, self.level, cellId, roundCallback, finishCallback, maxSuits) + self.battleExpAggreg.attachToBattle(battle) + battle.battleCalc.setSkillCreditMultiplier(self.level.getBattleCreditMultiplier()) + battle.addToon(toonId) + battle.generateWithRequired(zoneId) + self.cellId2battle[cellId] = battle + return battle + + def addBattleBlocker(self, blocker, cellId): + self.battleBlockers[cellId] = blocker + messenger.send(self.level.planner.getBattleBlockerEvent(cellId)) diff --git a/toontown/coghq/LevelSuitPlannerAI.py b/toontown/coghq/LevelSuitPlannerAI.py new file mode 100755 index 00000000..fb1bb8c9 --- /dev/null +++ b/toontown/coghq/LevelSuitPlannerAI.py @@ -0,0 +1,217 @@ +from panda3d.core import * +from direct.showbase import DirectObject +from toontown.suit import SuitDNA +from direct.directnotify import DirectNotifyGlobal +import LevelBattleManagerAI +import types +import random + +class LevelSuitPlannerAI(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('LevelSuitPlannerAI') + + def __init__(self, air, level, cogCtor, battleCtor, cogSpecs, reserveCogSpecs, battleCellSpecs, battleExpAggreg = None): + self.air = air + self.level = level + self.cogCtor = cogCtor + self.cogSpecs = cogSpecs + if simbase.config.GetBool('level-reserve-suits', 0): + self.reserveCogSpecs = reserveCogSpecs + else: + self.reserveCogSpecs = [] + self.battleCellSpecs = battleCellSpecs + self.__genSuitInfos(self.level.getCogLevel(), self.level.getCogTrack()) + self.battleMgr = LevelBattleManagerAI.LevelBattleManagerAI(self.air, self.level, battleCtor, battleExpAggreg) + self.battleCellId2suits = {} + for id in self.battleCellSpecs.keys(): + self.battleCellId2suits[id] = [] + + def destroy(self): + self.battleMgr.destroyBattleMgr() + del self.battleMgr + self.battleCellId2suits = {} + self.ignoreAll() + del self.cogSpecs + del self.cogCtor + del self.level + del self.air + + def __genJoinChances(self, num): + joinChances = [] + for currChance in xrange(num): + joinChances.append(random.randint(1, 100)) + + joinChances.sort(cmp) + return joinChances + + def __genSuitInfos(self, level, track): + def getSuitDict(spec, cogId, level = level, track = track): + suitDict = {} + suitDict['track'] = track + suitDict.update(spec) + suitDict['zoneId'] = self.level.getEntityZoneId(spec['parentEntId']) + suitDict['level'] += level + suitDict['cogId'] = cogId + return suitDict + + self.suitInfos = {} + self.suitInfos['activeSuits'] = [] + for i in xrange(len(self.cogSpecs)): + spec = self.cogSpecs[i] + self.suitInfos['activeSuits'].append(getSuitDict(spec, i)) + + numReserve = len(self.reserveCogSpecs) + joinChances = self.__genJoinChances(numReserve) + self.suitInfos['reserveSuits'] = [] + for i in xrange(len(self.reserveCogSpecs)): + spec = self.reserveCogSpecs[i] + suitDict = getSuitDict(spec, i) + suitDict['joinChance'] = joinChances[i] + self.suitInfos['reserveSuits'].append(suitDict) + + def __genSuitObject(self, suitDict, reserve): + suit = self.cogCtor(simbase.air, self) + dna = SuitDNA.SuitDNA() + dna.newSuitRandom(level=SuitDNA.getRandomSuitType(suitDict['level']), dept=suitDict['track']) + suit.dna = dna + suit.setLevel(suitDict['level']) + suit.setSkeleRevives(suitDict.get('revives')) + suit.setLevelDoId(self.level.doId) + suit.setCogId(suitDict['cogId']) + suit.setReserve(reserve) + if suitDict['skeleton']: + suit.setSkelecog(1) + suit.generateWithRequired(suitDict['zoneId']) + suit.boss = suitDict['boss'] + return suit + + def genSuits(self): + suitHandles = {} + activeSuits = [] + for activeSuitInfo in self.suitInfos['activeSuits']: + suit = self.__genSuitObject(activeSuitInfo, 0) + suit.setBattleCellIndex(activeSuitInfo['battleCell']) + activeSuits.append(suit) + + suitHandles['activeSuits'] = activeSuits + reserveSuits = [] + for reserveSuitInfo in self.suitInfos['reserveSuits']: + suit = self.__genSuitObject(reserveSuitInfo, 1) + reserveSuits.append([suit, reserveSuitInfo['joinChance'], reserveSuitInfo['battleCell']]) + + suitHandles['reserveSuits'] = reserveSuits + return suitHandles + + def __suitCanJoinBattle(self, cellId): + battle = self.battleMgr.getBattle(cellId) + if not battle.suitCanJoin(): + return 0 + return 1 + + def requestBattle(self, suit, toonId): + cellIndex = suit.getBattleCellIndex() + cellSpec = self.battleCellSpecs[cellIndex] + pos = cellSpec['pos'] + zone = self.level.getZoneId(self.level.getEntityZoneEntId(cellSpec['parentEntId'])) + maxSuits = 4 + self.battleMgr.newBattle(cellIndex, zone, pos, suit, toonId, self.__handleRoundFinished, self.__handleBattleFinished, maxSuits) + for otherSuit in self.battleCellId2suits[cellIndex]: + if otherSuit is not suit: + if self.__suitCanJoinBattle(cellIndex): + self.battleMgr.requestBattleAddSuit(cellIndex, otherSuit) + else: + battle = self.battleMgr.getBattle(cellIndex) + if battle: + self.notify.warning('battle not joinable: numSuits=%s, joinable=%s, fsm=%s, toonId=%s' % (len(battle.suits), + battle.isJoinable(), + battle.fsm.getCurrentState().getName(), + toonId)) + else: + self.notify.warning('battle not joinable: no battle for cell %s, toonId=%s' % (cellIndex, toonId)) + return 0 + + return 1 + + def __handleRoundFinished(self, cellId, toonIds, totalHp, deadSuits): + totalMaxHp = 0 + level = self.level + battle = self.battleMgr.cellId2battle[cellId] + for suit in battle.suits: + totalMaxHp += suit.maxHP + + for suit in deadSuits: + level.suits.remove(suit) + + cellReserves = [] + for info in level.reserveSuits: + if info[2] == cellId: + cellReserves.append(info) + + numSpotsAvailable = 4 - len(battle.suits) + if len(cellReserves) > 0 and numSpotsAvailable > 0: + self.joinedReserves = [] + if len(battle.suits) == 0: + hpPercent = 100 + else: + hpPercent = 100 - totalHp / totalMaxHp * 100.0 + for info in cellReserves: + if info[1] <= hpPercent and len(self.joinedReserves) < numSpotsAvailable: + level.suits.append(info[0]) + self.joinedReserves.append(info) + info[0].setBattleCellIndex(cellId) + + for info in self.joinedReserves: + level.reserveSuits.remove(info) + + if len(self.joinedReserves) > 0: + self.reservesJoining(battle) + level.d_setSuits() + return + if len(battle.suits) == 0: + if battle: + battle.resume() + else: + battle = self.battleMgr.cellId2battle.get(cellId) + if battle: + battle.resume() + + def __handleBattleFinished(self, zoneId): + pass + + def reservesJoining(self, battle): + for info in self.joinedReserves: + battle.suitRequestJoin(info[0]) + + battle.resume() + self.joinedReserves = [] + + def getDoId(self): + return 0 + + def removeSuit(self, suit): + suit.requestDelete() + + def suitBattleCellChange(self, suit, oldCell, newCell): + if oldCell is not None: + if oldCell in self.battleCellId2suits: + self.battleCellId2suits[oldCell].remove(suit) + else: + self.notify.warning('FIXME crash bandaid suitBattleCellChange suit.doId =%s, oldCell=%s not in battleCellId2Suits.keys %s' % (suit.doId, oldCell, self.battleCellId2suits.keys())) + blocker = self.battleMgr.battleBlockers.get(oldCell) + if blocker: + blocker.removeSuit(suit) + if newCell is not None: + self.battleCellId2suits[newCell].append(suit) + + def addSuitToBlocker(self = self): + blocker = self.battleMgr.battleBlockers.get(newCell) + if blocker: + blocker.addSuit(suit) + return 1 + return 0 + + if not addSuitToBlocker(): + self.accept(self.getBattleBlockerEvent(newCell), addSuitToBlocker) + return + + def getBattleBlockerEvent(self, cellId): + return 'battleBlockerAdded-' + str(self.level.doId) + '-' + str(cellId) diff --git a/toontown/coghq/LiftConstants.py b/toontown/coghq/LiftConstants.py new file mode 100755 index 00000000..ed21fdda --- /dev/null +++ b/toontown/coghq/LiftConstants.py @@ -0,0 +1,8 @@ +Down = 0 +Up = 1 + +def oppositeState(state): + if state is Down: + return Up + else: + return Down diff --git a/toontown/coghq/LobbyManager.py b/toontown/coghq/LobbyManager.py new file mode 100755 index 00000000..34316f61 --- /dev/null +++ b/toontown/coghq/LobbyManager.py @@ -0,0 +1,27 @@ +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer + +class LobbyManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('LobbyManager') + SetFactoryZoneMsg = 'setFactoryZone' + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + self.notify.debug('generate') + DistributedObject.DistributedObject.generate(self) + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + DistributedObject.DistributedObject.disable(self) + + def getSuitDoorOrigin(self): + return 1 + + def getBossLevel(self): + return 0 diff --git a/toontown/coghq/LobbyManagerAI.py b/toontown/coghq/LobbyManagerAI.py new file mode 100755 index 00000000..0f459082 --- /dev/null +++ b/toontown/coghq/LobbyManagerAI.py @@ -0,0 +1,39 @@ +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals + +class LobbyManagerAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('LobbyManagerAI') + + def __init__(self, air, bossConstructor): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.air = air + self.bossConstructor = bossConstructor + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.notify.debug('generate') + + def delete(self): + self.notify.debug('delete') + self.ignoreAll() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def createBossOffice(self, avIdList): + bossZone = self.air.allocateZone() + self.notify.info('createBossOffice: %s' % bossZone) + bossCog = self.bossConstructor(self.air) + bossCog.generateWithRequired(bossZone) + self.acceptOnce(bossCog.uniqueName('BossDone'), self.destroyBossOffice, extraArgs=[bossCog]) + for avId in avIdList: + if avId: + bossCog.addToon(avId) + + bossCog.b_setState('WaitForToons') + return bossZone + + def destroyBossOffice(self, bossCog): + bossZone = bossCog.zoneId + self.notify.info('destroyBossOffice: %s' % bossZone) + bossCog.requestDelete() + self.air.deallocateZone(bossZone) diff --git a/toontown/coghq/MegaCorpInterior.py b/toontown/coghq/MegaCorpInterior.py new file mode 100755 index 00000000..142939ad --- /dev/null +++ b/toontown/coghq/MegaCorpInterior.py @@ -0,0 +1,9 @@ +from direct.directnotify import DirectNotifyGlobal +import FactoryInterior + +class MegaCorpInterior(FactoryInterior.FactoryInterior): + notify = DirectNotifyGlobal.directNotify.newCategory('FactoryInterior') + + def __init__(self, loader, parentFSM, doneEvent): + FactoryInterior.FactoryInterior.__init__(self, loader, parentFSM, doneEvent) + self.zoneId = ToontownGlobals.SellbotMegaCorpInt diff --git a/toontown/coghq/MintInterior.py b/toontown/coghq/MintInterior.py new file mode 100755 index 00000000..80157cfb --- /dev/null +++ b/toontown/coghq/MintInterior.py @@ -0,0 +1,236 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattlePlace +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.showbase import BulletinBoardWatcher +from panda3d.core import * +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toon import Toon +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownBattleGlobals +from toontown.coghq import DistributedMint +from otp.nametag import NametagGlobals + +class MintInterior(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('MintInterior') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.zoneId = loader.mintId + self.fsm = ClassicFSM.ClassicFSM('MintInterior', [State.State('start', self.enterStart, self.exitStart, ['walk', 'teleportIn', 'fallDown']), + State.State('walk', self.enterWalk, self.exitWalk, ['push', + 'sit', + 'stickerBook', + 'WaitForBattle', + 'battle', + 'died', + 'teleportOut', + 'squished', + 'fallDown', + 'stopped']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut', 'stickerBook']), + State.State('sit', self.enterSit, self.exitSit, ['walk', 'died', 'teleportOut']), + State.State('push', self.enterPush, self.exitPush, ['walk', 'died', 'teleportOut']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'battle', + 'WaitForBattle', + 'died', + 'teleportOut']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', + 'walk', + 'died', + 'teleportOut']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('fallDown', self.enterFallDown, self.exitFallDown, ['walk', 'died', 'teleportOut']), + State.State('squished', self.enterSquished, self.exitSquished, ['walk', 'died', 'teleportOut']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', + 'teleportOut', + 'quietZone', + 'died']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', + 'FLA', + 'quietZone', + 'WaitForBattle']), + State.State('died', self.enterDied, self.exitDied, ['teleportOut']), + State.State('FLA', self.enterFLA, self.exitFLA, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['teleportIn']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + self.parentFSM.getStateNamed('mintInterior').addChild(self.fsm) + BattlePlace.BattlePlace.load(self) + self.music = base.loadMusic('phase_9/audio/bgm/CBHQ_Mint_bg.ogg') + + def unload(self): + self.parentFSM.getStateNamed('mintInterior').removeChild(self.fsm) + del self.music + del self.fsm + del self.parentFSM + BattlePlace.BattlePlace.unload(self) + + def enter(self, requestStatus): + self.fsm.enterInitialState() + base.transitions.fadeOut(t=0) + base.localAvatar.inventory.setRespectInvasions(0) + base.cr.forbidCheesyEffects(1) + self._telemLimiter = TLGatherAllAvs('MintInterior', RotationLimitToH) + + def commence(self = self): + NametagGlobals.setMasterArrowsOn(1) + self.fsm.request(requestStatus['how'], [requestStatus]) + base.playMusic(self.music, looping=1, volume=0.8) + base.transitions.irisIn() + mint = bboard.get(DistributedMint.DistributedMint.ReadyPost) + self.loader.hood.spawnTitleText(mint.mintId, mint.floorNum) + + self.mintReadyWatcher = BulletinBoardWatcher.BulletinBoardWatcher('MintReady', DistributedMint.DistributedMint.ReadyPost, commence) + self.mintDefeated = 0 + self.acceptOnce(DistributedMint.DistributedMint.WinEvent, self.handleMintWinEvent) + if __debug__ and 0: + self.accept('f10', lambda : messenger.send(DistributedMint.DistributedMint.WinEvent)) + self.confrontedBoss = 0 + + def handleConfrontedBoss(self = self): + self.confrontedBoss = 1 + + self.acceptOnce('localToonConfrontedMintBoss', handleConfrontedBoss) + + def exit(self): + NametagGlobals.setMasterArrowsOn(0) + bboard.remove(DistributedMint.DistributedMint.ReadyPost) + self._telemLimiter.destroy() + del self._telemLimiter + base.cr.forbidCheesyEffects(0) + base.localAvatar.inventory.setRespectInvasions(1) + self.fsm.requestFinalState() + self.loader.music.stop() + self.music.stop() + self.ignoreAll() + del self.mintReadyWatcher + + def enterWalk(self, teleportIn = 0): + BattlePlace.BattlePlace.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterPush(self): + BattlePlace.BattlePlace.enterPush(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterWaitForBattle(self): + MintInterior.notify.debug('enterWaitForBattle') + BattlePlace.BattlePlace.enterWaitForBattle(self) + if base.localAvatar.getParent() != render: + base.localAvatar.wrtReparentTo(render) + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + + def exitWaitForBattle(self): + MintInterior.notify.debug('exitWaitForBattle') + BattlePlace.BattlePlace.exitWaitForBattle(self) + + def enterBattle(self, event): + MintInterior.notify.debug('enterBattle') + self.music.stop() + BattlePlace.BattlePlace.enterBattle(self, event) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterTownBattle(self, event): + mult = ToontownBattleGlobals.getMintCreditMultiplier(self.zoneId) + base.localAvatar.inventory.setBattleCreditMultiplier(mult) + self.loader.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult) + + def exitBattle(self): + MintInterior.notify.debug('exitBattle') + BattlePlace.BattlePlace.exitBattle(self) + self.loader.music.stop() + base.playMusic(self.music, looping=1, volume=0.8) + + def enterStickerBook(self, page = None): + BattlePlace.BattlePlace.enterStickerBook(self, page) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterSit(self): + BattlePlace.BattlePlace.enterSit(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterZone(self, zoneId): + pass + + def enterTeleportOut(self, requestStatus): + MintInterior.notify.debug('enterTeleportOut()') + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __processLeaveRequest(self, requestStatus): + hoodId = requestStatus['hoodId'] + if hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def __teleportOutDone(self, requestStatus): + MintInterior.notify.debug('__teleportOutDone()') + messenger.send('leavingMint') + messenger.send('localToonLeft') + if self.mintDefeated and not self.confrontedBoss: + self.fsm.request('FLA', [requestStatus]) + else: + self.__processLeaveRequest(requestStatus) + + def exitTeleportOut(self): + MintInterior.notify.debug('exitTeleportOut()') + BattlePlace.BattlePlace.exitTeleportOut(self) + + def handleMintWinEvent(self): + MintInterior.notify.debug('handleMintWinEvent') + if base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'died': + return + self.mintDefeated = 1 + if 1: + zoneId = ZoneUtil.getHoodId(self.zoneId) + else: + zoneId = ZoneUtil.getSafeZoneId(base.localAvatar.defaultZone) + self.fsm.request('teleportOut', [{'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1}]) + + def enterDied(self, requestStatus, callback = None): + MintInterior.notify.debug('enterDied') + + def diedDone(requestStatus, self = self, callback = callback): + if callback is not None: + callback() + messenger.send('leavingMint') + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + BattlePlace.BattlePlace.enterDied(self, requestStatus, diedDone) + + def enterFLA(self, requestStatus): + MintInterior.notify.debug('enterFLA') + self.flaDialog = TTDialog.TTGlobalDialog(message=TTLocalizer.ForcedLeaveMintAckMsg, doneEvent='FLADone', style=TTDialog.Acknowledge, fadeScreen=1) + + def continueExit(self = self, requestStatus = requestStatus): + self.__processLeaveRequest(requestStatus) + + self.accept('FLADone', continueExit) + self.flaDialog.show() + + def exitFLA(self): + MintInterior.notify.debug('exitFLA') + if hasattr(self, 'flaDialog'): + self.flaDialog.cleanup() + del self.flaDialog diff --git a/toontown/coghq/MintLayout.py b/toontown/coghq/MintLayout.py new file mode 100755 index 00000000..32a3f67f --- /dev/null +++ b/toontown/coghq/MintLayout.py @@ -0,0 +1,848 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.PythonUtil import invertDictLossless +from toontown.coghq import MintRoomSpecs +from toontown.toonbase import ToontownGlobals +from direct.showbase.PythonUtil import normalDistrib, lerp +import random + +def printAllCashbotInfo(): + print 'roomId: roomName' + for roomId, roomName in MintRoomSpecs.CashbotMintRoomId2RoomName.items(): + print '%s: %s' % (roomId, roomName) + + print '\nroomId: numBattles' + for roomId, numBattles in MintRoomSpecs.roomId2numBattles.items(): + print '%s: %s' % (roomId, numBattles) + + print '\nmintId floor roomIds' + printMintRoomIds() + print '\nmintId floor numRooms' + printNumRooms() + print '\nmintId floor numForcedBattles' + printNumBattles() + + +def iterateCashbotMints(func): + from toontown.toonbase import ToontownGlobals + for mintId in [ToontownGlobals.CashbotMintIntA, ToontownGlobals.CashbotMintIntB, ToontownGlobals.CashbotMintIntC]: + for floorNum in xrange(ToontownGlobals.MintNumFloors[mintId]): + func(MintLayout(mintId, floorNum)) + + +def printMintInfo(): + + def func(ml): + print ml + + iterateCashbotMints(func) + + +def printMintRoomIds(): + + def func(ml): + print ml.getMintId(), ml.getFloorNum(), ml.getRoomIds() + + iterateCashbotMints(func) + + +def printMintRoomNames(): + + def func(ml): + print ml.getMintId(), ml.getFloorNum(), ml.getRoomNames() + + iterateCashbotMints(func) + + +def printNumRooms(): + + def func(ml): + print ml.getMintId(), ml.getFloorNum(), ml.getNumRooms() + + iterateCashbotMints(func) + + +def printNumBattles(): + + def func(ml): + print ml.getMintId(), ml.getFloorNum(), ml.getNumBattles() + + iterateCashbotMints(func) + + +BakedFloorLayouts = {12500: {0: (0, + 4, + 9, + 6, + 5, + 8, + 17), + 1: (0, + 15, + 13, + 16, + 7, + 6, + 22), + 2: (0, + 4, + 11, + 3, + 9, + 6, + 14, + 19), + 3: (0, + 1, + 3, + 4, + 16, + 14, + 15, + 24), + 4: (0, + 15, + 5, + 8, + 9, + 11, + 10, + 21), + 5: (0, + 13, + 12, + 8, + 7, + 16, + 10, + 18), + 6: (0, + 16, + 13, + 5, + 12, + 7, + 1, + 23), + 7: (0, + 10, + 12, + 7, + 3, + 13, + 16, + 8, + 20), + 8: (0, + 3, + 5, + 7, + 6, + 1, + 4, + 9, + 25), + 9: (0, + 6, + 9, + 10, + 13, + 16, + 8, + 4, + 22), + 10: (0, + 13, + 1, + 7, + 2, + 16, + 11, + 3, + 19), + 11: (0, + 3, + 1, + 6, + 4, + 14, + 8, + 9, + 24), + 12: (0, + 7, + 14, + 2, + 1, + 8, + 5, + 10, + 11, + 21), + 13: (0, + 13, + 6, + 4, + 11, + 3, + 9, + 10, + 8, + 17), + 14: (0, + 15, + 5, + 1, + 14, + 10, + 4, + 7, + 16, + 23), + 15: (0, + 16, + 10, + 11, + 2, + 1, + 3, + 14, + 5, + 20), + 16: (0, + 5, + 8, + 10, + 6, + 3, + 15, + 14, + 7, + 25), + 17: (0, + 12, + 13, + 5, + 8, + 14, + 11, + 7, + 16, + 10, + 22), + 18: (0, + 11, + 3, + 15, + 7, + 16, + 14, + 6, + 1, + 5, + 18), + 19: (0, + 10, + 16, + 11, + 3, + 5, + 12, + 13, + 7, + 14, + 24)}, + 12600: {0: (0, + 8, + 1, + 6, + 14, + 2, + 5, + 9, + 17), + 1: (0, + 4, + 14, + 7, + 2, + 13, + 8, + 9, + 18), + 2: (0, + 7, + 9, + 6, + 5, + 14, + 12, + 3, + 20), + 3: (0, + 6, + 2, + 13, + 16, + 7, + 5, + 3, + 9, + 22), + 4: (0, + 15, + 4, + 9, + 8, + 6, + 13, + 5, + 11, + 23), + 5: (0, + 13, + 7, + 14, + 15, + 11, + 3, + 2, + 8, + 25), + 6: (0, + 5, + 14, + 2, + 11, + 7, + 16, + 10, + 15, + 18), + 7: (0, + 10, + 9, + 5, + 4, + 2, + 7, + 13, + 11, + 19), + 8: (0, + 11, + 4, + 12, + 6, + 1, + 13, + 7, + 3, + 21), + 9: (0, + 15, + 16, + 5, + 13, + 9, + 14, + 4, + 6, + 3, + 23), + 10: (0, + 16, + 15, + 7, + 6, + 8, + 3, + 4, + 9, + 10, + 24), + 11: (0, + 5, + 8, + 4, + 12, + 13, + 9, + 11, + 16, + 3, + 17), + 12: (0, + 13, + 16, + 7, + 4, + 12, + 3, + 6, + 5, + 1, + 19), + 13: (0, + 14, + 6, + 12, + 13, + 7, + 10, + 3, + 16, + 9, + 20), + 14: (0, + 9, + 15, + 13, + 5, + 6, + 3, + 14, + 11, + 4, + 22), + 15: (0, + 13, + 14, + 3, + 12, + 16, + 11, + 9, + 4, + 5, + 7, + 24), + 16: (0, + 3, + 6, + 1, + 7, + 5, + 10, + 9, + 4, + 13, + 15, + 25), + 17: (0, + 3, + 6, + 14, + 4, + 13, + 16, + 12, + 8, + 5, + 7, + 18), + 18: (0, + 11, + 13, + 4, + 1, + 15, + 6, + 3, + 8, + 9, + 16, + 20), + 19: (0, + 11, + 5, + 8, + 7, + 2, + 6, + 13, + 3, + 14, + 9, + 21)}, + 12700: {0: (0, + 16, + 14, + 6, + 1, + 5, + 9, + 2, + 15, + 8, + 17), + 1: (0, + 3, + 2, + 12, + 14, + 8, + 13, + 6, + 10, + 7, + 23), + 2: (0, + 15, + 9, + 5, + 12, + 7, + 4, + 11, + 14, + 16, + 21), + 3: (0, + 2, + 13, + 7, + 6, + 8, + 15, + 4, + 1, + 11, + 19), + 4: (0, + 12, + 7, + 4, + 6, + 10, + 14, + 13, + 16, + 15, + 11, + 17), + 5: (0, + 10, + 2, + 9, + 13, + 4, + 8, + 1, + 15, + 14, + 11, + 23), + 6: (0, + 2, + 14, + 4, + 10, + 16, + 15, + 1, + 3, + 8, + 6, + 21), + 7: (0, + 14, + 11, + 1, + 7, + 9, + 10, + 12, + 8, + 5, + 2, + 19), + 8: (0, + 9, + 11, + 8, + 5, + 1, + 4, + 3, + 7, + 15, + 2, + 17), + 9: (0, + 2, + 9, + 7, + 11, + 16, + 10, + 15, + 3, + 8, + 6, + 23), + 10: (0, + 4, + 10, + 6, + 8, + 7, + 15, + 2, + 1, + 3, + 13, + 21), + 11: (0, + 10, + 14, + 8, + 6, + 9, + 15, + 5, + 1, + 2, + 13, + 19), + 12: (0, + 16, + 5, + 12, + 10, + 6, + 9, + 11, + 3, + 15, + 13, + 17), + 13: (0, + 1, + 3, + 6, + 14, + 4, + 10, + 12, + 15, + 13, + 16, + 24), + 14: (0, + 8, + 7, + 14, + 9, + 1, + 2, + 6, + 16, + 10, + 15, + 13, + 21), + 15: (0, + 4, + 1, + 8, + 11, + 12, + 3, + 10, + 16, + 13, + 6, + 15, + 19), + 16: (0, + 6, + 3, + 10, + 4, + 1, + 2, + 13, + 11, + 5, + 15, + 16, + 17), + 17: (0, + 6, + 16, + 5, + 12, + 11, + 1, + 8, + 14, + 15, + 9, + 10, + 24), + 18: (0, + 15, + 8, + 12, + 10, + 1, + 7, + 11, + 9, + 16, + 4, + 5, + 21), + 19: (0, + 10, + 2, + 16, + 5, + 6, + 11, + 13, + 7, + 12, + 1, + 3, + 19)}} + +class MintLayout: + notify = DirectNotifyGlobal.directNotify.newCategory('MintLayout') + + def __init__(self, mintId, floorNum): + self.mintId = mintId + self.floorNum = floorNum + self.roomIds = [] + self.hallways = [] + self.numRooms = 1 + ToontownGlobals.MintNumRooms[self.mintId][self.floorNum] + self.numHallways = self.numRooms - 1 + if self.mintId in BakedFloorLayouts and self.floorNum in BakedFloorLayouts[self.mintId]: + self.roomIds = list(BakedFloorLayouts[self.mintId][self.floorNum]) + else: + self.roomIds = self._genFloorLayout() + hallwayRng = self.getRng() + connectorRoomNames = MintRoomSpecs.CashbotMintConnectorRooms + for i in xrange(self.numHallways): + self.hallways.append(hallwayRng.choice(connectorRoomNames)) + + def _genFloorLayout(self): + rng = self.getRng() + startingRoomIDs = MintRoomSpecs.CashbotMintEntranceIDs + middleRoomIDs = MintRoomSpecs.CashbotMintMiddleRoomIDs + finalRoomIDs = MintRoomSpecs.CashbotMintFinalRoomIDs + + numBattlesLeft = ToontownGlobals.MintNumBattles[self.mintId] + + finalRoomId = rng.choice(finalRoomIDs) + numBattlesLeft -= MintRoomSpecs.getNumBattles(finalRoomId) + + middleRoomIds = [] + middleRoomsLeft = self.numRooms - 2 + + numBattles2middleRoomIds = invertDictLossless(MintRoomSpecs.middleRoomId2numBattles) + + allBattleRooms = [] + for num, roomIds in numBattles2middleRoomIds.items(): + if num > 0: + allBattleRooms.extend(roomIds) + while 1: + allBattleRoomIds = list(allBattleRooms) + rng.shuffle(allBattleRoomIds) + battleRoomIds = self._chooseBattleRooms(numBattlesLeft, + allBattleRoomIds) + if battleRoomIds is not None: + break + + MintLayout.notify.info('could not find a valid set of battle rooms, trying again') + + middleRoomIds.extend(battleRoomIds) + middleRoomsLeft -= len(battleRoomIds) + + if middleRoomsLeft > 0: + actionRoomIds = numBattles2middleRoomIds[0] + for i in xrange(middleRoomsLeft): + roomId = rng.choice(actionRoomIds) + actionRoomIds.remove(roomId) + middleRoomIds.append(roomId) + + roomIds = [] + + roomIds.append(rng.choice(startingRoomIDs)) + + rng.shuffle(middleRoomIds) + roomIds.extend(middleRoomIds) + + roomIds.append(finalRoomId) + + return roomIds + + def getNumRooms(self): + return len(self.roomIds) + + def getRoomId(self, n): + return self.roomIds[n] + + def getRoomIds(self): + return self.roomIds[:] + + def getRoomNames(self): + names = [] + for roomId in self.roomIds: + names.append(MintRoomSpecs.CashbotMintRoomId2RoomName[roomId]) + + return names + + def getNumHallways(self): + return len(self.hallways) + + def getHallwayModel(self, n): + return self.hallways[n] + + def getNumBattles(self): + numBattles = 0 + for roomId in self.getRoomIds(): + numBattles += MintRoomSpecs.roomId2numBattles[roomId] + + return numBattles + + def getMintId(self): + return self.mintId + + def getFloorNum(self): + return self.floorNum + + def getRng(self): + return random.Random(self.mintId * self.floorNum) + + def _chooseBattleRooms(self, numBattlesLeft, allBattleRoomIds, baseIndex = 0, chosenBattleRooms = None): + if chosenBattleRooms is None: + chosenBattleRooms = [] + while baseIndex < len(allBattleRoomIds): + nextRoomId = allBattleRoomIds[baseIndex] + baseIndex += 1 + newNumBattlesLeft = numBattlesLeft - MintRoomSpecs.middleRoomId2numBattles[nextRoomId] + if newNumBattlesLeft < 0: + continue + elif newNumBattlesLeft == 0: + chosenBattleRooms.append(nextRoomId) + return chosenBattleRooms + chosenBattleRooms.append(nextRoomId) + result = self._chooseBattleRooms(newNumBattlesLeft, allBattleRoomIds, baseIndex, chosenBattleRooms) + if result is not None: + return result + else: + del chosenBattleRooms[-1:] + else: + return + + return + + def __str__(self): + return 'MintLayout: id=%s, floor=%s, numRooms=%s, numBattles=%s' % (self.mintId, + self.floorNum, + self.getNumRooms(), + self.getNumBattles()) + + def __repr__(self): + return str(self) diff --git a/toontown/coghq/MintManagerAI.py b/toontown/coghq/MintManagerAI.py new file mode 100755 index 00000000..3b5fcbd1 --- /dev/null +++ b/toontown/coghq/MintManagerAI.py @@ -0,0 +1,49 @@ +from direct.directnotify import DirectNotifyGlobal +import DistributedMintAI +from toontown.toonbase import ToontownGlobals +from toontown.coghq import MintLayout +from direct.showbase import DirectObject +import random + +class MintManagerAI(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('MintManagerAI') + mintId = None + + def __init__(self, air): + DirectObject.DirectObject.__init__(self) + self.air = air + + def getDoId(self): + return 0 + + def createMint(self, mintId, players): + for avId in players: + if bboard.has('mintId-%s' % avId): + mintId = bboard.get('mintId-%s' % avId) + break + + numFloors = ToontownGlobals.MintNumFloors[mintId] + floor = random.randrange(numFloors) + for avId in players: + if bboard.has('mintFloor-%s' % avId): + floor = bboard.get('mintFloor-%s' % avId) + floor = max(0, floor) + floor = min(floor, numFloors - 1) + break + + for avId in players: + if bboard.has('mintRoom-%s' % avId): + roomId = bboard.get('mintRoom-%s' % avId) + for i in xrange(numFloors): + layout = MintLayout.MintLayout(mintId, i) + if roomId in layout.getRoomIds(): + floor = i + else: + from toontown.coghq import MintRoomSpecs + roomName = MintRoomSpecs.CashbotMintRoomId2RoomName[roomId] + MintManagerAI.notify.warning('room %s (%s) not found in any floor of mint %s' % (roomId, roomName, mintId)) + + mintZone = self.air.allocateZone() + mint = DistributedMintAI.DistributedMintAI(self.air, mintId, mintZone, floor, players) + mint.generateWithRequired(mintZone) + return mintZone diff --git a/toontown/coghq/MintProduct.py b/toontown/coghq/MintProduct.py new file mode 100755 index 00000000..3d06a308 --- /dev/null +++ b/toontown/coghq/MintProduct.py @@ -0,0 +1,33 @@ +from toontown.toonbase.ToontownGlobals import * +from otp.level import BasicEntities + +class MintProduct(BasicEntities.NodePathEntity): + Models = {CashbotMintIntA: 'phase_10/models/cashbotHQ/MoneyBag', + CashbotMintIntB: 'phase_10/models/cashbotHQ/MoneyStackPallet', + CashbotMintIntC: 'phase_10/models/cashbotHQ/GoldBarStack'} + Scales = {CashbotMintIntA: 0.98, + CashbotMintIntB: 0.38, + CashbotMintIntC: 0.6} + + def __init__(self, level, entId): + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.model = None + self.mintId = self.level.mintId + self.loadModel() + return + + def destroy(self): + if self.model: + self.model.removeNode() + del self.model + BasicEntities.NodePathEntity.destroy(self) + + def loadModel(self): + if self.model: + self.model.removeNode() + self.model = None + self.model = loader.loadModel(self.Models[self.mintId]) + self.model.setScale(self.Scales[self.mintId]) + self.model.flattenStrong() + if self.model: + self.model.reparentTo(self) diff --git a/toontown/coghq/MintProductPallet.py b/toontown/coghq/MintProductPallet.py new file mode 100755 index 00000000..9cc74718 --- /dev/null +++ b/toontown/coghq/MintProductPallet.py @@ -0,0 +1,10 @@ +from toontown.toonbase.ToontownGlobals import * +from toontown.coghq import MintProduct + +class MintProductPallet(MintProduct.MintProduct): + Models = {CashbotMintIntA: 'phase_10/models/cashbotHQ/DoubleCoinStack.bam', + CashbotMintIntB: 'phase_10/models/cogHQ/DoubleMoneyStack.bam', + CashbotMintIntC: 'phase_10/models/cashbotHQ/DoubleGoldStack.bam'} + Scales = {CashbotMintIntA: 1.0, + CashbotMintIntB: 1.0, + CashbotMintIntC: 1.0} diff --git a/toontown/coghq/MintRoom.py b/toontown/coghq/MintRoom.py new file mode 100755 index 00000000..c29f4931 --- /dev/null +++ b/toontown/coghq/MintRoom.py @@ -0,0 +1,103 @@ +from panda3d.core import * +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals +from toontown.coghq import MintRoomSpecs +import random + +class MintRoom(DirectObject.DirectObject): + FloorCollPrefix = 'mintFloorColl' + CashbotMintDoorFrame = 'phase_10/models/cashbotHQ/DoorFrame' + + def __init__(self, path = None): + if path is not None: + if path in MintRoomSpecs.CashbotMintConnectorRooms: + loadFunc = loader.loadModelCopy + else: + loadFunc = loader.loadModel + self.setGeom(loadFunc(path)) + self.localToonFSM = ClassicFSM.ClassicFSM('MintRoomLocalToonPresent', [State.State('off', self.enterLtOff, self.exitLtOff, ['notPresent']), State.State('notPresent', self.enterLtNotPresent, self.exitLtNotPresent, ['present']), State.State('present', self.enterLtPresent, self.exitLtPresent, ['notPresent'])], 'notPresent', 'notPresent') + self.localToonFSM.enterInitialState() + return + + def delete(self): + del self.localToonFSM + + def enter(self): + self.localToonFSM.request('notPresent') + + def exit(self): + self.localToonFSM.requestFinalState() + + def setRoomNum(self, num): + self.roomNum = num + + def getRoomNum(self): + return self.roomNum + + def setGeom(self, geom): + self.__geom = geom + + def getGeom(self): + return self.__geom + + def _getEntrances(self): + return self.__geom.findAllMatches('**/ENTRANCE*') + + def _getExits(self): + return self.__geom.findAllMatches('**/EXIT*') + + def attachTo(self, other, rng): + otherExits = other._getExits() + entrances = self._getEntrances() + otherDoor = otherExits[0] + thisDoor = rng.choice(entrances) + geom = self.getGeom() + otherGeom = other.getGeom() + tempNode = otherDoor.attachNewNode('tempRotNode') + geom.reparentTo(tempNode) + geom.clearMat() + geom.setPos(Vec3(0) - thisDoor.getPos(geom)) + tempNode.setH(-thisDoor.getH(otherDoor)) + geom.wrtReparentTo(otherGeom.getParent()) + tempNode.removeNode() + doorFrame = loader.loadModel(MintRoom.CashbotMintDoorFrame) + doorFrame.reparentTo(thisDoor) + + def getFloorCollName(self): + return '%s%s' % (MintRoom.FloorCollPrefix, self.roomNum) + + def initFloorCollisions(self): + allColls = self.getGeom().findAllMatches('**/+CollisionNode') + floorColls = [] + for coll in allColls: + bitmask = coll.node().getIntoCollideMask() + if not (bitmask & ToontownGlobals.FloorBitmask).isZero(): + floorColls.append(coll) + + if len(floorColls) > 0: + floorCollName = self.getFloorCollName() + others = self.getGeom().findAllMatches('**/%s' % floorCollName) + for other in others: + other.setName('%s_renamed' % floorCollName) + + for floorColl in floorColls: + floorColl.setName(floorCollName) + + def enterLtOff(self): + pass + + def exitLtOff(self): + pass + + def enterLtNotPresent(self): + pass + + def exitLtNotPresent(self): + pass + + def enterLtPresent(self): + pass + + def exitLtPresent(self): + pass diff --git a/toontown/coghq/MintRoomBase.py b/toontown/coghq/MintRoomBase.py new file mode 100755 index 00000000..c1fcbf2a --- /dev/null +++ b/toontown/coghq/MintRoomBase.py @@ -0,0 +1,16 @@ +from toontown.toonbase import ToontownGlobals + +class MintRoomBase: + + def __init__(self): + pass + + def setMintId(self, mintId): + self.mintId = mintId + self.cogTrack = ToontownGlobals.cogHQZoneId2dept(mintId) + + def setRoomId(self, roomId): + self.roomId = roomId + + def getCogTrack(self): + return self.cogTrack diff --git a/toontown/coghq/MintRoomSpecs.py b/toontown/coghq/MintRoomSpecs.py new file mode 100755 index 00000000..c7bebf86 --- /dev/null +++ b/toontown/coghq/MintRoomSpecs.py @@ -0,0 +1,125 @@ +from direct.showbase.PythonUtil import invertDict +from toontown.toonbase import ToontownGlobals +from toontown.coghq import NullCogs +from toontown.coghq import CashbotMintBoilerRoom_Battle00_Cogs +from toontown.coghq import CashbotMintBoilerRoom_Battle01_Cogs +from toontown.coghq import CashbotMintControlRoom_Battle00_Cogs +from toontown.coghq import CashbotMintDuctRoom_Battle00_Cogs +from toontown.coghq import CashbotMintDuctRoom_Battle01_Cogs +from toontown.coghq import CashbotMintGearRoom_Battle00_Cogs +from toontown.coghq import CashbotMintGearRoom_Battle01_Cogs +from toontown.coghq import CashbotMintLavaRoomFoyer_Battle00_Cogs +from toontown.coghq import CashbotMintLavaRoomFoyer_Battle01_Cogs +from toontown.coghq import CashbotMintLobby_Battle00_Cogs +from toontown.coghq import CashbotMintLobby_Battle01_Cogs +from toontown.coghq import CashbotMintOilRoom_Battle00_Cogs +from toontown.coghq import CashbotMintPaintMixerReward_Battle00_Cogs +from toontown.coghq import CashbotMintPipeRoom_Battle00_Cogs +from toontown.coghq import CashbotMintPipeRoom_Battle01_Cogs + +# Explicit imports for the below room modules: +from toontown.coghq import CashbotMintEntrance_Action00 +from toontown.coghq import CashbotMintBoilerRoom_Action00 +from toontown.coghq import CashbotMintBoilerRoom_Battle00 +from toontown.coghq import CashbotMintDuctRoom_Action00 +from toontown.coghq import CashbotMintDuctRoom_Battle00 +from toontown.coghq import CashbotMintGearRoom_Action00 +from toontown.coghq import CashbotMintGearRoom_Battle00 +from toontown.coghq import CashbotMintLavaRoomFoyer_Action00 +from toontown.coghq import CashbotMintLavaRoomFoyer_Action01 +from toontown.coghq import CashbotMintLavaRoomFoyer_Battle00 +from toontown.coghq import CashbotMintLavaRoom_Action00 +from toontown.coghq import CashbotMintLobby_Action00 +from toontown.coghq import CashbotMintLobby_Battle00 +from toontown.coghq import CashbotMintPaintMixer_Action00 +from toontown.coghq import CashbotMintPipeRoom_Action00 +from toontown.coghq import CashbotMintPipeRoom_Battle00 +from toontown.coghq import CashbotMintStomperAlley_Action00 +from toontown.coghq import CashbotMintBoilerRoom_Battle01 +from toontown.coghq import CashbotMintControlRoom_Battle00 +from toontown.coghq import CashbotMintDuctRoom_Battle01 +from toontown.coghq import CashbotMintGearRoom_Battle01 +from toontown.coghq import CashbotMintLavaRoomFoyer_Battle01 +from toontown.coghq import CashbotMintOilRoom_Battle00 +from toontown.coghq import CashbotMintLobby_Battle01 +from toontown.coghq import CashbotMintPaintMixerReward_Battle00 +from toontown.coghq import CashbotMintPipeRoom_Battle01 + +def getMintRoomSpecModule(roomId): + return CashbotMintSpecModules[roomId] + + +def getCogSpecModule(roomId): + roomName = CashbotMintRoomId2RoomName[roomId] + return CogSpecModules.get(roomName, NullCogs) + + +def getNumBattles(roomId): + return roomId2numBattles[roomId] + + +CashbotMintRoomId2RoomName = {0: 'CashbotMintEntrance_Action00', + 1: 'CashbotMintBoilerRoom_Action00', + 2: 'CashbotMintBoilerRoom_Battle00', + 3: 'CashbotMintDuctRoom_Action00', + 4: 'CashbotMintDuctRoom_Battle00', + 5: 'CashbotMintGearRoom_Action00', + 6: 'CashbotMintGearRoom_Battle00', + 7: 'CashbotMintLavaRoomFoyer_Action00', + 8: 'CashbotMintLavaRoomFoyer_Action01', + 9: 'CashbotMintLavaRoomFoyer_Battle00', + 10: 'CashbotMintLavaRoom_Action00', + 11: 'CashbotMintLobby_Action00', + 12: 'CashbotMintLobby_Battle00', + 13: 'CashbotMintPaintMixer_Action00', + 14: 'CashbotMintPipeRoom_Action00', + 15: 'CashbotMintPipeRoom_Battle00', + 16: 'CashbotMintStomperAlley_Action00', + 17: 'CashbotMintBoilerRoom_Battle01', + 18: 'CashbotMintControlRoom_Battle00', + 19: 'CashbotMintDuctRoom_Battle01', + 20: 'CashbotMintGearRoom_Battle01', + 21: 'CashbotMintLavaRoomFoyer_Battle01', + 22: 'CashbotMintOilRoom_Battle00', + 23: 'CashbotMintLobby_Battle01', + 24: 'CashbotMintPaintMixerReward_Battle00', + 25: 'CashbotMintPipeRoom_Battle01'} +CashbotMintRoomName2RoomId = invertDict(CashbotMintRoomId2RoomName) +CashbotMintEntranceIDs = (0,) +CashbotMintMiddleRoomIDs = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) +CashbotMintFinalRoomIDs = (17, 18, 19, 20, 21, 22, 23, 24, 25) +CashbotMintConnectorRooms = ('phase_10/models/cashbotHQ/connector_7cubeL2', 'phase_10/models/cashbotHQ/connector_7cubeR2') +CashbotMintSpecModules = {} +for roomName, roomId in CashbotMintRoomName2RoomId.items(): + CashbotMintSpecModules[roomId] = locals()[roomName] + +CogSpecModules = {'CashbotMintBoilerRoom_Battle00': CashbotMintBoilerRoom_Battle00_Cogs, + 'CashbotMintBoilerRoom_Battle01': CashbotMintBoilerRoom_Battle01_Cogs, + 'CashbotMintControlRoom_Battle00': CashbotMintControlRoom_Battle00_Cogs, + 'CashbotMintDuctRoom_Battle00': CashbotMintDuctRoom_Battle00_Cogs, + 'CashbotMintDuctRoom_Battle01': CashbotMintDuctRoom_Battle01_Cogs, + 'CashbotMintGearRoom_Battle00': CashbotMintGearRoom_Battle00_Cogs, + 'CashbotMintGearRoom_Battle01': CashbotMintGearRoom_Battle01_Cogs, + 'CashbotMintLavaRoomFoyer_Battle00': CashbotMintLavaRoomFoyer_Battle00_Cogs, + 'CashbotMintLavaRoomFoyer_Battle01': CashbotMintLavaRoomFoyer_Battle01_Cogs, + 'CashbotMintLobby_Battle00': CashbotMintLobby_Battle00_Cogs, + 'CashbotMintLobby_Battle01': CashbotMintLobby_Battle01_Cogs, + 'CashbotMintOilRoom_Battle00': CashbotMintOilRoom_Battle00_Cogs, + 'CashbotMintPaintMixerReward_Battle00': CashbotMintPaintMixerReward_Battle00_Cogs, + 'CashbotMintPipeRoom_Battle00': CashbotMintPipeRoom_Battle00_Cogs, + 'CashbotMintPipeRoom_Battle01': CashbotMintPipeRoom_Battle01_Cogs} +roomId2numBattles = {} +for roomName, roomId in CashbotMintRoomName2RoomId.items(): + if roomName not in CogSpecModules: + roomId2numBattles[roomId] = 0 + else: + cogSpecModule = CogSpecModules[roomName] + roomId2numBattles[roomId] = len(cogSpecModule.BattleCells) + +name2id = CashbotMintRoomName2RoomId +roomId2numBattles[name2id['CashbotMintBoilerRoom_Battle00']] = 3 +roomId2numBattles[name2id['CashbotMintPipeRoom_Battle00']] = 2 +del name2id +middleRoomId2numBattles = {} +for roomId in CashbotMintMiddleRoomIDs: + middleRoomId2numBattles[roomId] = roomId2numBattles[roomId] diff --git a/toontown/coghq/MintShelf.py b/toontown/coghq/MintShelf.py new file mode 100755 index 00000000..99908be4 --- /dev/null +++ b/toontown/coghq/MintShelf.py @@ -0,0 +1,10 @@ +from toontown.toonbase.ToontownGlobals import * +from toontown.coghq import MintProduct + +class MintShelf(MintProduct.MintProduct): + Models = {CashbotMintIntA: 'phase_10/models/cashbotHQ/shelf_A1MoneyBags', + CashbotMintIntB: 'phase_10/models/cashbotHQ/shelf_A1Money', + CashbotMintIntC: 'phase_10/models/cashbotHQ/shelf_A1Gold'} + Scales = {CashbotMintIntA: 1.0, + CashbotMintIntB: 1.0, + CashbotMintIntC: 1.0} diff --git a/toontown/coghq/MoleFieldBase.py b/toontown/coghq/MoleFieldBase.py new file mode 100755 index 00000000..275e09be --- /dev/null +++ b/toontown/coghq/MoleFieldBase.py @@ -0,0 +1,92 @@ +import random +HILL_MOLE = 0 +HILL_BOMB = 1 +HILL_WHACKED = 2 +HILL_COGWHACKED = 3 + +class MoleFieldBase: + WHACKED = 1 + MoveUpTimeMax = 1 + MoveUpTimeMultiplier = 0.95 + MoveUpTimeMin = 0.5 + StayUpTimeMax = 7 + StayUpTimeMultiplier = 0.95 + StayUpTimeMin = 3 + MoveDownTimeMax = 1 + MoveDownTimeMultiplier = 0.95 + MoveDownTimeMin = 0.5 + TimeBetweenPopupMax = 1.5 + TimeBetweenPopupMultiplier = 0.95 + TimeBetweenPopupMin = 0.25 + DamageOnFailure = 20 + + def getRng(self): + return random.Random(self.entId * self.level.doId) + + def scheduleMoles(self): + self.schedule = [] + totalTime = 0 + curMoveUpTime = self.MoveUpTimeMax + curMoveDownTime = self.MoveDownTimeMax + curTimeBetweenPopup = self.TimeBetweenPopupMax + curStayUpTime = self.StayUpTimeMax + curTime = 3 + eligibleMoles = range(self.numMoles) + self.getRng().shuffle(eligibleMoles) + usedMoles = [] + self.notify.debug('eligibleMoles=%s' % eligibleMoles) + self.endingTime = 0 + randOb = random.Random(self.entId * self.level.doId) + while self.endingTime < self.GameDuration: + if len(eligibleMoles) == 0: + eligibleMoles = usedMoles + self.getRng().shuffle(usedMoles) + usedMoles = [] + self.notify.debug('eligibleMoles=%s' % eligibleMoles) + moleIndex = eligibleMoles[0] + eligibleMoles.remove(moleIndex) + usedMoles.append(moleIndex) + moleType = randOb.choice([HILL_MOLE, + HILL_MOLE, + HILL_MOLE, + HILL_BOMB]) + self.schedule.append((curTime, + moleIndex, + curMoveUpTime, + curStayUpTime, + curMoveDownTime, + moleType)) + curTime += curTimeBetweenPopup + curMoveUpTime = self.calcNextMoveUpTime(curTime, curMoveUpTime) + curStayUpTime = self.calcNextStayUpTime(curTime, curStayUpTime) + curMoveDownTime = self.calcNextMoveDownTime(curTime, curMoveDownTime) + curTimeBetweenPopup = self.calcNextTimeBetweenPopup(curTime, curTimeBetweenPopup) + self.endingTime = curTime + curMoveUpTime + curStayUpTime + curMoveDownTime + + self.schedule.pop() + self.endingTime = self.schedule[-1][0] + self.schedule[-1][2] + self.schedule[-1][3] + self.schedule[-1][4] + self.notify.debug('schedule length = %d, endingTime=%f' % (len(self.schedule), self.endingTime)) + + def calcNextMoveUpTime(self, curTime, curMoveUpTime): + newMoveUpTime = curMoveUpTime * self.MoveUpTimeMultiplier + if newMoveUpTime < self.MoveDownTimeMin: + newMoveUpTime = self.MoveDownTimeMin + return newMoveUpTime + + def calcNextStayUpTime(self, curTime, curStayUpTime): + newStayUpTime = curStayUpTime * self.StayUpTimeMultiplier + if newStayUpTime < self.StayUpTimeMin: + newStayUpTime = self.StayUpTimeMin + return newStayUpTime + + def calcNextMoveDownTime(self, curTime, curMoveDownTime): + newMoveDownTime = curMoveDownTime * self.MoveDownTimeMultiplier + if newMoveDownTime < self.MoveDownTimeMin: + newMoveDownTime = self.MoveDownTimeMin + return newMoveDownTime + + def calcNextTimeBetweenPopup(self, curTime, curTimeBetweenPopup): + newTimeBetweenPopup = curTimeBetweenPopup * self.TimeBetweenPopupMultiplier + if newTimeBetweenPopup < self.TimeBetweenPopupMin: + newTimeBetweenPopup = self.TimeBetweenPopupMin + return newTimeBetweenPopup diff --git a/toontown/coghq/MoleHill.py b/toontown/coghq/MoleHill.py new file mode 100755 index 00000000..47d071ac --- /dev/null +++ b/toontown/coghq/MoleHill.py @@ -0,0 +1,152 @@ +from pandac.PandaModules import NodePath, Point3, CollisionTube, CollisionNode, Vec4 +from direct.interval.IntervalGlobal import Sequence, LerpPosInterval, Parallel, LerpScaleInterval, Track, ParticleInterval, Wait, Func +from toontown.toonbase import ToontownGlobals +from toontown.coghq import MoleFieldBase +from direct.particles import ParticleEffect +from toontown.battle import BattleParticles +from toontown.battle import BattleProps + +class MoleHill(NodePath): + + def __init__(self, x, y, z, moleField, index): + NodePath.__init__(self, 'MoleHill-%d' % index) + self.moleField = moleField + self.index = index + self.loadModel() + self.setPos(x, y, z) + self.schedule = [] + self.popIval = None + self.downIval = None + self.popupNum = 0 + self.hillType = None + self.isUp = 0 + return + + def loadModel(self): + self.hill = loader.loadModel('phase_12/models/bossbotHQ/mole_hole') + self.hill.setZ(0.0) + self.hill.reparentTo(self) + self.hillColName = 'moleHillCol-%d-%d' % (self.moleField.doId, self.index) + self.moleField.accept('enter' + self.hillColName, self.moleField.handleEnterHill) + self.mole = self.attachNewNode('mole') + self.mole.reparentTo(self) + self.mole.setScale(0.75) + self.mole.setZ(-2.5) + self.moleHead = loader.loadModel('phase_12/models/bossbotHQ/mole_norm') + self.moleHead.reparentTo(self.mole) + moleColName = 'moleCol-%d-%s' % (self.moleField.doId, self.index) + moleSphere = CollisionTube(0, 0, 0, 0, 0, 1, 1) + collNode = CollisionNode(moleColName) + collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + collNode.addSolid(moleSphere) + self.moleColNodePath = self.mole.attachNewNode(collNode) + self.moleColNodePath.stash() + self.moleColNodePath.setScale(1.0) + self.moleField.accept('enter' + moleColName, self.moleField.handleEnterMole) + + def destroy(self): + if self.popIval: + self.popIval.pause() + self.popIval = None + if self.downIval: + self.downIval.pause() + self.downIval = None + self.removeNode() + return + + def switchUp(self): + self.isUp = 1 + + def switchDown(self): + self.isUp = 0 + + def setHillType(self, type): + if self.isUp and (self.hillType == MoleFieldBase.HILL_MOLE and type == MoleFieldBase.HILL_BOMB or self.hillType == MoleFieldBase.HILL_BOMB and type == MoleFieldBase.HILL_MOLE): + return + self.hillType = type + self.moleHead.removeNode() + if type == MoleFieldBase.HILL_MOLE: + self.moleHead = loader.loadModel('phase_12/models/bossbotHQ/mole_norm') + self.moleColNodePath.setScale(3.0) + self.moleHead.setH(0) + self.mole.setBillboardAxis(localAvatar, 0) + if type == MoleFieldBase.HILL_BOMB or type == MoleFieldBase.HILL_COGWHACKED: + self.moleHead = loader.loadModel('phase_12/models/bossbotHQ/mole_cog') + self.moleColNodePath.setScale(1.0) + self.mole.setBillboardAxis(localAvatar, 0) + if type == MoleFieldBase.HILL_COGWHACKED: + self.doMoleDown() + BattleParticles.loadParticles() + singleGear = BattleParticles.createParticleEffect('GearExplosion', numParticles=1) + smallGearExplosion = BattleParticles.createParticleEffect('GearExplosion', numParticles=10) + bigGearExplosion = BattleParticles.createParticleEffect('BigGearExplosion', numParticles=30) + gears2MTrack = Track((0.0, ParticleInterval(singleGear, self.hill, worldRelative=1, duration=5.7, cleanup=True)), (0.0, ParticleInterval(smallGearExplosion, self.hill, worldRelative=0, duration=1.2, cleanup=True)), (0.3, ParticleInterval(bigGearExplosion, self.hill, worldRelative=0, duration=1.0, cleanup=True)), name='gears2MTrack') + gears2MTrack.start() + self.popIval = Sequence(Parallel(Sequence(LerpPosInterval(self.moleHead, 0.05, Point3(0.28, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, -0.23, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.28)), LerpPosInterval(self.moleHead, 0.05, Point3(-0.35, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.28, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.31, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, -0.32, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.48)), LerpPosInterval(self.moleHead, 0.05, Point3(-0.28, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.29, 0.0)))), LerpPosInterval(self.mole, 0.5, Point3(0, 0, -2.5)), Func(self.setHillType, MoleFieldBase.HILL_BOMB)) + self.popIval.start() + else: + self.moleHead.setH(0) + if type == MoleFieldBase.HILL_WHACKED: + self.moleHead = loader.loadModel('phase_12/models/bossbotHQ/mole_hit') + self.mole.setBillboardAxis(0) + self.moleColNodePath.setScale(0.0) + if self.popIval: + self.popIval.finish() + if self.downIval: + self.downIval.finish() + self.mole.setPos(0.0, 0.0, 0.0) + self.popIval = Sequence(Parallel(Sequence(LerpPosInterval(self.moleHead, 0.05, Point3(0.18, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, -0.13, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.18)), LerpPosInterval(self.moleHead, 0.05, Point3(-0.15, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.18, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.11, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, -0.12, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.18)), LerpPosInterval(self.moleHead, 0.05, Point3(-0.18, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.13, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.18, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, -0.15, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.18)), LerpPosInterval(self.moleHead, 0.05, Point3(-0.16, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.18, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.11, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, -0.18, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.17)), LerpPosInterval(self.moleHead, 0.05, Point3(-0.18, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.0))), Sequence(LerpScaleInterval(self.moleHead, 0.5, 3.5), LerpScaleInterval(self.moleHead, 0.5, 1.0))), LerpPosInterval(self.mole, 0.5, Point3(0, 0, -2.5)), Func(self.setHillType, MoleFieldBase.HILL_MOLE)) + self.popIval.start() + self.moleHead.reparentTo(self.mole) + + def doMolePop(self, startTime, timeToMoveUp, timeToStayUp, timeToMoveDown, moleType): + if self.hillType == MoleFieldBase.HILL_WHACKED or self.hillType == MoleFieldBase.HILL_COGWHACKED: + return + if self.popIval: + self.popIval.pause() + if self.downIval: + self.downIval.pause() + self.downIval = None + moleColor = None + self.switchUp() + self.popupNum += 1 + if self.hillType == MoleFieldBase.HILL_BOMB: + self.popIval = Sequence(Func(self.setHillType, moleType), Func(self.moleColNodePath.unstash), LerpPosInterval(self.mole, timeToMoveUp, Point3(0, 0, 0.0)), Wait(timeToStayUp), LerpPosInterval(self.mole, timeToMoveDown, Point3(0, 0, -2.5)), Func(self.stashMoleCollision), Func(self.switchDown)) + else: + self.popIval = Sequence(Func(self.setHillType, moleType), LerpPosInterval(self.mole, timeToMoveUp, Point3(0, 0, 0.0)), Func(self.moleColNodePath.unstash), Wait(timeToStayUp), Func(self.stashMoleCollision), LerpPosInterval(self.mole, timeToMoveDown, Point3(0, 0, -2.5)), Func(self.switchDown)) + self.popIval.start() + return + + def setGameStartTime(self, gameStartTime): + self.gameStartTime = gameStartTime + self.popupNum = 0 + + def stashMoleCollision(self): + self.moleColNodePath.stash() + + def getPopupNum(self): + return self.popupNum + + def doMoleDown(self): + if self.hillType == MoleFieldBase.HILL_WHACKED or self.hillType == MoleFieldBase.HILL_COGWHACKED: + return + if self.popIval: + self.popIval.pause() + self.popIval = None + if self.downIval: + self.downIval.pause() + self.downIval = Sequence(Func(self.stashMoleCollision), LerpPosInterval(self.mole, 1, Point3(0, 0, -2.5)), Func(self.switchDown)) + self.downIval.start() + return + + def forceMoleDown(self): + if self.popIval: + self.popIval.pause() + self.popIval = None + if self.downIval: + self.downIval.pause() + self.downIval = None + self.stashMoleCollision() + self.switchDown() + self.mole.setPos(0, 0, -2.5) + return diff --git a/toontown/coghq/MovingPlatform.py b/toontown/coghq/MovingPlatform.py new file mode 100755 index 00000000..cd38643d --- /dev/null +++ b/toontown/coghq/MovingPlatform.py @@ -0,0 +1,88 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.showbase import DirectObject +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +import types + +class MovingPlatform(DirectObject.DirectObject, NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('MovingPlatform') + + def __init__(self): + self.hasLt = 0 + DirectObject.DirectObject.__init__(self) + NodePath.__init__(self) + + def setupCopyModel(self, parentToken, model, floorNodeName = None, parentingNode = None): + if floorNodeName is None: + floorNodeName = 'floor' + if type(parentToken) == types.IntType: + parentToken = ToontownGlobals.SPDynamic + parentToken + self.parentToken = parentToken + self.name = 'MovingPlatform-%s' % parentToken + self.assign(hidden.attachNewNode(self.name)) + self.model = model.copyTo(self) + self.ownsModel = 1 + floorList = self.model.findAllMatches('**/%s' % floorNodeName) + if len(floorList) == 0: + MovingPlatform.notify.warning('no floors in model') + return + for floor in floorList: + floor.setName(self.name) + + if parentingNode == None: + parentingNode = self + base.cr.parentMgr.registerParent(self.parentToken, parentingNode) + self.parentingNode = parentingNode + self.accept('enter%s' % self.name, self.__handleEnter) + self.accept('exit%s' % self.name, self.__handleExit) + return + + def destroy(self): + base.cr.parentMgr.unregisterParent(self.parentToken) + self.ignoreAll() + if self.hasLt: + self.__releaseLt() + if self.ownsModel: + self.model.removeNode() + del self.model + if hasattr(self, 'parentingNode') and self.parentingNode is self: + del self.parentingNode + + def getEnterEvent(self): + return '%s-enter' % self.name + + def getExitEvent(self): + return '%s-exit' % self.name + + def releaseLocalToon(self): + if self.hasLt: + self.__releaseLt() + + def __handleEnter(self, collEntry): + self.notify.debug('on movingPlatform %s' % self.name) + self.__grabLt() + messenger.send(self.getEnterEvent()) + + def __handleExit(self, collEntry): + self.notify.debug('off movingPlatform %s' % self.name) + self.__releaseLt() + messenger.send(self.getExitEvent()) + + def __handleOnFloor(self, collEntry): + if collEntry.getIntoNode().getName() == self.name: + self.__handleEnter(collEntry) + + def __handleOffFloor(self, collEntry): + if collEntry.getIntoNode().getName() == self.name: + self.__handleExit(collEntry) + + def __grabLt(self): + base.localAvatar.b_setParent(self.parentToken) + self.hasLt = 1 + + def __releaseLt(self): + if base.localAvatar.getParent().compareTo(self.parentingNode) == 0: + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + base.localAvatar.controlManager.currentControls.doDeltaPos() + self.hasLt = 0 diff --git a/toontown/coghq/NullCogs.py b/toontown/coghq/NullCogs.py new file mode 100755 index 00000000..78bd47eb --- /dev/null +++ b/toontown/coghq/NullCogs.py @@ -0,0 +1,4 @@ +from SpecImports import * +BattleCells = {} +CogData = [] +ReserveCogData = [] diff --git a/toontown/coghq/PaintMixer.py b/toontown/coghq/PaintMixer.py new file mode 100755 index 00000000..1bd9357b --- /dev/null +++ b/toontown/coghq/PaintMixer.py @@ -0,0 +1,13 @@ +import PlatformEntity + +class PaintMixer(PlatformEntity.PlatformEntity): + + def start(self): + PlatformEntity.PlatformEntity.start(self) + model = self.platform.model + shaft = model.find('**/PaintMixerBase1') + shaft.setSz(self.shaftScale) + shaft.node().setPreserveTransform(0) + shaftChild = shaft.find('**/PaintMixerBase') + shaftChild.node().setPreserveTransform(0) + model.flattenMedium() diff --git a/toontown/coghq/PathMasterEntity.py b/toontown/coghq/PathMasterEntity.py new file mode 100755 index 00000000..8e6a1d60 --- /dev/null +++ b/toontown/coghq/PathMasterEntity.py @@ -0,0 +1,152 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.suit import GoonPathData +from otp.level import PathEntity + +class PathMasterEntity(PathEntity.PathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('PathMasterEntity') + + def __init__(self, level, entId): + self.pathScale = 1.0 + PathEntity.PathEntity.__init__(self, level, entId) + self.setPathIndex(self.pathIndex) + self.initPath() + + def initPath(self): + self.pathTargetList = [None, + None, + None, + None, + None, + None, + None, + None] + if not hasattr(self, 'pathTarget0'): + self.pathTarget0 = None + else: + self.pathTargetList[0] = self.pathTarget0 + if not hasattr(self, 'pathTarget1'): + self.pathTarget1 = None + else: + self.pathTargetList[1] = self.pathTarget1 + if not hasattr(self, 'pathTarget2'): + self.pathTarget2 = None + else: + self.pathTargetList[2] = self.pathTarget2 + if not hasattr(self, 'pathTarget3'): + self.pathTarget3 = None + else: + self.pathTargetList[3] = self.pathTarget3 + if not hasattr(self, 'pathTarget4'): + self.pathTarget4 = None + else: + self.pathTargetList[4] = self.pathTarget4 + if not hasattr(self, 'pathTarget5'): + self.pathTarget5 = None + else: + self.pathTargetList[5] = self.pathTarget5 + if not hasattr(self, 'pathTarget6'): + self.pathTarget6 = None + else: + self.pathTargetList[6] = self.pathTarget6 + if not hasattr(self, 'pathTarget7'): + self.pathTarget7 = None + else: + self.pathTargetList[7] = self.pathTarget7 + return + + def destroy(self): + PathEntity.PathEntity.destroy(self) + + def setPathTarget0(self, targetId): + self.pathTarget0 = targetId + self.pathTargetList[0] = targetId + + def setPathTarget1(self, targetId): + self.pathTarget1 = targetId + self.pathTargetList[1] = targetId + + def setPathTarget2(self, targetId): + self.pathTarget2 = targetId + self.pathTargetList[2] = targetId + + def setPathTarget3(self, targetId): + self.pathTarget3 = targetId + self.pathTargetList[3] = targetId + + def setPathTarget4(self, targetId): + self.pathTarget4 = targetId + self.pathTargetList[4] = targetId + + def setPathTarget5(self, targetId): + self.pathTarget5 = targetId + self.pathTargetList[5] = targetId + + def setPathTarget6(self, targetId): + self.pathTarget6 = targetId + self.pathTargetList[6] = targetId + + def setPathTarget7(self, targetId): + self.pathTarget7 = targetId + self.pathTargetList[7] = targetId + + def getReducedPath(self): + returnPath = [] + for entityId in self.pathTargetList: + if self.level and entityId != 0: + thing = self.level.entities.get(entityId, None) + returnPath.append(thing.getPos(self)) + + return returnPath + + def setPathIndex(self, pathIndex): + self.pathIndex = pathIndex + pathTableId = GoonPathData.taskZoneId2pathId[self.level.getTaskZoneId()] + if self.pathIndex in GoonPathData.Paths[pathTableId]: + self.path = GoonPathData.Paths[pathTableId][self.pathIndex] + else: + PathEntity.notify.warning('invalid pathIndex: %s' % pathIndex) + self.path = None + return + + def makePathTrack(self, node, velocity, name, turnTime = 1, lookAroundNode = None): + track = Sequence(name=name) + self.path = self.getReducedPath() + if self.path is None or len(self.path) < 1: + track.append(WaitInterval(1.0)) + return track + path = self.path + [self.path[0]] + for pointIndex in xrange(len(path) - 1): + startPoint = Point3(path[pointIndex]) * self.pathScale + endPoint = Point3(path[pointIndex + 1]) * self.pathScale + v = startPoint - endPoint + node.setPos(startPoint[0], startPoint[1], startPoint[2]) + node.headsUp(endPoint[0], endPoint[1], endPoint[2]) + theta = node.getH() % 360 + track.append(LerpHprInterval(node, turnTime, Vec3(theta, 0, 0))) + distance = Vec3(v).length() + duration = distance / velocity + track.append(LerpPosInterval(node, duration=duration, pos=endPoint, startPos=startPoint)) + + return track + + def makePathTrackBak(self, node, velocity, name, turnTime = 1, lookAroundNode = None): + track = Sequence(name=name) + if self.path is None: + track.append(WaitInterval(1.0)) + return track + path = self.path + [self.path[0]] + for pointIndex in xrange(len(path) - 1): + startPoint = Point3(path[pointIndex]) * self.pathScale + endPoint = Point3(path[pointIndex + 1]) * self.pathScale + v = startPoint - endPoint + node.setPos(startPoint[0], startPoint[1], startPoint[2]) + node.headsUp(endPoint[0], endPoint[1], endPoint[2]) + theta = node.getH() % 360 + track.append(LerpHprInterval(node, turnTime, Vec3(theta, 0, 0))) + distance = Vec3(v).length() + duration = distance / velocity + track.append(LerpPosInterval(node, duration=duration, pos=endPoint, startPos=startPoint)) + + return track diff --git a/toontown/coghq/PlatformEntity.py b/toontown/coghq/PlatformEntity.py new file mode 100755 index 00000000..5b162fc8 --- /dev/null +++ b/toontown/coghq/PlatformEntity.py @@ -0,0 +1,43 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from otp.level import BasicEntities +import MovingPlatform + +class PlatformEntity(BasicEntities.NodePathEntity): + + def __init__(self, level, entId): + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.start() + + def destroy(self): + self.stop() + BasicEntities.NodePathEntity.destroy(self) + + def start(self): + model = loader.loadModel(self.modelPath) + if model is None: + return + if len(self.floorName) == 0: + return + model.setScale(self.modelScale) + model.flattenMedium() + self.platform = MovingPlatform.MovingPlatform() + self.platform.setupCopyModel(self.getParentToken(), model, self.floorName) + self.platform.reparentTo(self) + startPos = Point3(0, 0, 0) + endPos = self.offset + distance = Vec3(self.offset).length() + waitDur = self.period * self.waitPercent + moveDur = self.period - waitDur + self.moveIval = Sequence(WaitInterval(waitDur * 0.5), LerpPosInterval(self.platform, moveDur * 0.5, endPos, startPos=startPos, name='platformOut%s' % self.entId, blendType=self.motion, fluid=1), WaitInterval(waitDur * 0.5), LerpPosInterval(self.platform, moveDur * 0.5, startPos, startPos=endPos, name='platformBack%s' % self.entId, blendType=self.motion, fluid=1), name=self.getUniqueName('platformIval')) + self.moveIval.loop() + self.moveIval.setT(globalClock.getFrameTime() - self.level.startTime + self.period * self.phaseShift) + return + + def stop(self): + if hasattr(self, 'moveIval'): + self.moveIval.pause() + del self.moveIval + if hasattr(self, 'platform'): + self.platform.destroy() + del self.platform diff --git a/toontown/coghq/RenderingEntity.py b/toontown/coghq/RenderingEntity.py new file mode 100755 index 00000000..367c506c --- /dev/null +++ b/toontown/coghq/RenderingEntity.py @@ -0,0 +1,71 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.suit import GoonPathData +from otp.level import BasicEntities + +class RenderingEntity(BasicEntities.NodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('PathMasterEntity') + + def __init__(self, level, entId): + BasicEntities.NodePathEntity.__init__(self, level, entId) + if hasattr(self, 'colorR'): + self.setColorScale(self.colorR, self.colorG, self.colorB, self.colorA) + if hasattr(self, 'blending'): + self.setBlending(self.blending) + if hasattr(self, 'fogOn'): + self.setFogOn(self.fogOn) + + def destroy(self): + BasicEntities.NodePathEntity.destroy(self) + + def setColorR(self, newColor): + self.colorR = newColor + self.setColorScale(self.colorR, self.colorG, self.colorB, self.colorA) + + def setColorG(self, newColor): + self.colorG = newColor + self.setColorScale(self.colorR, self.colorG, self.colorB, self.colorA) + + def setColorB(self, newColor): + self.colorB = newColor + self.setColorScale(self.colorR, self.colorG, self.colorB, self.colorA) + + def setColorA(self, newColor): + self.colorA = newColor + self.setColorScale(self.colorR, self.colorG, self.colorB, self.colorA) + + def setColorScale(self, R, G, B, A): + BasicEntities.NodePathEntity.setColorScale(self, R, G, B, A) + self.chooseBin() + self.setBlending(self.blending) + + def chooseBin(self): + if not hasattr(self, 'renderBin'): + self.renderBin = 'default' + self.setBin(self.renderBin, 0) + + def setBlending(self, blending): + self.blending = blending + if blending == 'Additive': + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.setDepthWrite(False) + elif blending == 'Alpha': + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MNone)) + self.setDepthWrite(True) + self.setTransparency(1) + else: + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MNone)) + self.setDepthWrite(True) + self.setTransparency(0) + self.chooseBin() + + def setFogOn(self, fog): + self.fogOn = fog + if self.fogOn: + myFog = Fog('Fog Rendering Fog of Rendering') + myFog.setColor(1.0, 0.0, 0.0) + myFog.setExpDensity(0.5) + self.setFog(myFog) + else: + self.clearFog() diff --git a/toontown/coghq/SellbotCogHQLoader.py b/toontown/coghq/SellbotCogHQLoader.py new file mode 100755 index 00000000..36fb38c1 --- /dev/null +++ b/toontown/coghq/SellbotCogHQLoader.py @@ -0,0 +1,196 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +import CogHQLoader +from toontown.toonbase import ToontownGlobals +from direct.gui import DirectGui +from toontown.toonbase import TTLocalizer +from toontown.toon import Toon +from direct.fsm import State +from direct.actor.Actor import Actor +import MegaCorpInterior +import FactoryExterior +import FactoryInterior +import SellbotHQExterior +import SellbotHQBossBattle +from pandac.PandaModules import DecalEffect, NodePath +aspectSF = 0.7227 + +class SellbotCogHQLoader(CogHQLoader.CogHQLoader): + notify = DirectNotifyGlobal.directNotify.newCategory('SellbotCogHQLoader') + + def __init__(self, hood, parentFSMState, doneEvent): + CogHQLoader.CogHQLoader.__init__(self, hood, parentFSMState, doneEvent) + self.fsm.addState(State.State('factoryExterior', self.enterFactoryExterior, self.exitFactoryExterior, ['quietZone', 'factoryInterior', 'cogHQExterior'])) + for stateName in ['start', 'cogHQExterior', 'quietZone']: + state = self.fsm.getStateNamed(stateName) + state.addTransition('factoryExterior') + + self.fsm.addState(State.State('factoryInterior', self.enterFactoryInterior, self.exitFactoryInterior, ['quietZone', 'factoryExterior'])) + self.fsm.addState(State.State('megaCorpInterior', self.enterMegaCorpInterior, self.exitMegaCorpInterior, ['quietZone', 'factoryExterior'])) + for stateName in ['quietZone']: + state = self.fsm.getStateNamed(stateName) + state.addTransition('factoryInterior') + + self.musicFile = 'phase_9/audio/bgm/encntr_suit_HQ_nbrhood.ogg' + self.cogHQExteriorModelPath = 'phase_9/models/cogHQ/SellbotHQExterior' + self.cogHQLobbyModelPath = 'phase_9/models/cogHQ/SellbotHQLobby' + self.factoryExteriorModelPath = 'phase_9/models/cogHQ/SellbotFactoryExterior' + self.geom = None + + def load(self, zoneId): + CogHQLoader.CogHQLoader.load(self, zoneId) + Toon.loadSellbotHQAnims() + + def unloadPlaceGeom(self): + if self.geom: + self.geom.removeNode() + self.geom = None + CogHQLoader.CogHQLoader.unloadPlaceGeom(self) + + def loadPlaceGeom(self, zoneId): + self.notify.info('loadPlaceGeom: %s' % zoneId) + zoneId = zoneId - zoneId % 100 + if zoneId == ToontownGlobals.SellbotHQ: + self.geom = loader.loadModel(self.cogHQExteriorModelPath) + dgLinkTunnel = self.geom.find('**/Tunnel1') + dgLinkTunnel.setName('linktunnel_dg_5316_DNARoot') + factoryLinkTunnel = self.geom.find('**/Tunnel2') + factoryLinkTunnel.setName('linktunnel_sellhq_11200_DNARoot') + cogSignModel = loader.loadModel('phase_4/models/props/sign_sellBotHeadHQ') + cogSign = cogSignModel.find('**/sign_sellBotHeadHQ').copyTo(NodePath()) + cogSign.flattenStrong() + cogSignModel.removeNode() + cogSignSF = 23 + dgSign = cogSign.copyTo(dgLinkTunnel) + dgSign.setPosHprScale(0.0, -291.5, 29, 180.0, 0.0, 0.0, cogSignSF, cogSignSF, cogSignSF * aspectSF) + dgSign.node().setEffect(DecalEffect.make()) + dgText = DirectGui.OnscreenText(text=TTLocalizer.DaisyGardens[-1], font=ToontownGlobals.getSuitFont(), pos=(0, -0.3), scale=TTLocalizer.SCHQLdgText, mayChange=False, parent=dgSign) + dgText.setDepthWrite(0) + dgText.flattenStrong() + factorySign = cogSign.copyTo(factoryLinkTunnel) + factorySign.setPosHprScale(148.625, -155, 27, -90.0, 0.0, 0.0, cogSignSF, cogSignSF, cogSignSF * aspectSF) + factorySign.node().setEffect(DecalEffect.make()) + factoryTypeText = DirectGui.OnscreenText(text=TTLocalizer.Sellbot, font=ToontownGlobals.getSuitFont(), pos=TTLocalizer.SellbotFactoryPosPart1, scale=TTLocalizer.SellbotFactoryScalePart1, mayChange=False, parent=factorySign) + factoryTypeText.setDepthWrite(0) + factoryTypeText.flattenStrong() + factoryText = DirectGui.OnscreenText(text=TTLocalizer.Factory, font=ToontownGlobals.getSuitFont(), pos=TTLocalizer.SellbotFactoryPosPart2, scale=TTLocalizer.SellbotFactoryScalePart2, mayChange=False, parent=factorySign) + factoryText.setDepthWrite(0) + factoryText.flattenStrong() + doors = self.geom.find('**/doors') + door0 = doors.find('**/door_0') + door1 = doors.find('**/door_1') + door2 = doors.find('**/door_2') + door3 = doors.find('**/door_3') + for door in [door0, door1, door2, door3]: + doorFrame = door.find('**/doorDoubleFlat/+GeomNode') + door.find('**/doorFrameHoleLeft').wrtReparentTo(doorFrame) + door.find('**/doorFrameHoleRight').wrtReparentTo(doorFrame) + doorTrigger = door.find('**/door_trigger*') + doorTrigger.setY(doorTrigger.getY() - 1.5) + doorFrame.node().setEffect(DecalEffect.make()) + doorFrame.flattenStrong() + door.flattenMedium() + cogSign.removeNode() + self.geom.flattenMedium() + self.botcam1 = Actor("phase_9/models/char/BotCam-zero.bam",{"botcamneutral":"phase_9/models/char/BotCam-neutral.bam"}) + self.botcam1.reparentTo(self.geom) + self.botcam1.setPos(-0.01,-39.3,24) + self.botcam1.loop('botcamneutral') + elif zoneId == ToontownGlobals.SellbotFactoryExt: + self.geom = loader.loadModel(self.factoryExteriorModelPath) + factoryLinkTunnel = self.geom.find('**/tunnel_group2') + factoryLinkTunnel.setName('linktunnel_sellhq_11000_DNARoot') + factoryLinkTunnel.find('**/tunnel_sphere').setName('tunnel_trigger') + cogSignModel = loader.loadModel('phase_4/models/props/sign_sellBotHeadHQ') + cogSign = cogSignModel.find('**/sign_sellBotHeadHQ').copyTo(NodePath()) + cogSign.flattenStrong() + cogSignModel.removeNode() + cogSignSF = 23 + elevatorSignSF = 15 + hqSign = cogSign.copyTo(factoryLinkTunnel) + hqSign.setPosHprScale(0.0, -353, 27.5, -180.0, 0.0, 0.0, cogSignSF, cogSignSF, cogSignSF * aspectSF) + hqSign.node().setEffect(DecalEffect.make()) + hqTypeText = DirectGui.OnscreenText(text=TTLocalizer.Sellbot, font=ToontownGlobals.getSuitFont(), pos=(0, -0.25), scale=0.075, mayChange=False, parent=hqSign) + hqTypeText.setDepthWrite(0) + hqTypeText.flattenStrong() + hqText = DirectGui.OnscreenText(text=TTLocalizer.Headquarters, font=ToontownGlobals.getSuitFont(), pos=(0, -0.34), scale=0.1, mayChange=False, parent=hqSign) + hqText.setDepthWrite(0) + hqText.flattenStrong() + frontDoor = self.geom.find('**/doorway1') + fdSign = cogSign.copyTo(frontDoor) + fdSign.setPosHprScale(62.74, -87.99, 17.26, 2.72, 0.0, 0.0, elevatorSignSF, elevatorSignSF, elevatorSignSF * aspectSF) + fdSign.node().setEffect(DecalEffect.make()) + fdTypeText = DirectGui.OnscreenText(text=TTLocalizer.Factory, font=ToontownGlobals.getSuitFont(), pos=(0, -0.25), scale=TTLocalizer.SCHQLfdTypeText, mayChange=False, parent=fdSign) + fdTypeText.setDepthWrite(0) + fdTypeText.flattenStrong() + fdText = DirectGui.OnscreenText(text=TTLocalizer.SellbotFrontEntrance, font=ToontownGlobals.getSuitFont(), pos=(0, -0.34), scale=TTLocalizer.SCHQLdgText, mayChange=False, parent=fdSign) + fdText.setDepthWrite(0) + fdText.flattenStrong() + sideDoor = self.geom.find('**/doorway2') + sdSign = cogSign.copyTo(sideDoor) + sdSign.setPosHprScale(-164.78, 26.28, 17.25, -89.89, 0.0, 0.0, elevatorSignSF, elevatorSignSF, elevatorSignSF * aspectSF) + sdSign.node().setEffect(DecalEffect.make()) + sdTypeText = DirectGui.OnscreenText(text=TTLocalizer.Factory, font=ToontownGlobals.getSuitFont(), pos=(0, -0.25), scale=0.075, mayChange=False, parent=sdSign) + sdTypeText.setDepthWrite(0) + sdTypeText.flattenStrong() + sdText = DirectGui.OnscreenText(text=TTLocalizer.SellbotSideEntrance, font=ToontownGlobals.getSuitFont(), pos=(0, -0.34), scale=0.1, mayChange=False, parent=sdSign) + sdText.setDepthWrite(0) + sdText.flattenStrong() + cogSign.removeNode() + self.geom.flattenMedium() + elif zoneId == ToontownGlobals.SellbotLobby: + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: COGHQ: Visit SellbotLobby') + self.geom = loader.loadModel(self.cogHQLobbyModelPath) + front = self.geom.find('**/frontWall') + front.node().setEffect(DecalEffect.make()) + door = self.geom.find('**/door_0') + parent = door.getParent() + door.wrtReparentTo(front) + doorFrame = door.find('**/doorDoubleFlat/+GeomNode') + door.find('**/doorFrameHoleLeft').wrtReparentTo(doorFrame) + door.find('**/doorFrameHoleRight').wrtReparentTo(doorFrame) + doorFrame.node().setEffect(DecalEffect.make()) + door.find('**/leftDoor').wrtReparentTo(parent) + door.find('**/rightDoor').wrtReparentTo(parent) + self.geom.flattenStrong() + else: + self.notify.warning('loadPlaceGeom: unclassified zone %s' % zoneId) + CogHQLoader.CogHQLoader.loadPlaceGeom(self, zoneId) + + def unload(self): + CogHQLoader.CogHQLoader.unload(self) + Toon.unloadSellbotHQAnims() + + def enterFactoryExterior(self, requestStatus): + self.placeClass = FactoryExterior.FactoryExterior + self.enterPlace(requestStatus) + self.hood.spawnTitleText(requestStatus['zoneId']) + + def exitFactoryExterior(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + self.exitPlace() + self.placeClass = None + + def enterMegaCorpInterior(self, requestStatus): + self.placeClass = MegaCorpInterior.MegaCorpInterior + self.enterPlace(requestStatus) + + def exitMegaCorpInterior(self): + self.exitPlace() + self.placeClass = None + + def enterFactoryInterior(self, requestStatus): + self.placeClass = FactoryInterior.FactoryInterior + self.enterPlace(requestStatus) + + def exitFactoryInterior(self): + self.exitPlace() + self.placeClass = None + + def getExteriorPlaceClass(self): + return SellbotHQExterior.SellbotHQExterior + + def getBossPlaceClass(self): + return SellbotHQBossBattle.SellbotHQBossBattle diff --git a/toontown/coghq/SellbotHQBossBattle.py b/toontown/coghq/SellbotHQBossBattle.py new file mode 100755 index 00000000..411b0674 --- /dev/null +++ b/toontown/coghq/SellbotHQBossBattle.py @@ -0,0 +1,38 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.suit import DistributedSellbotBoss +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import CogHQBossBattle + +class SellbotHQBossBattle(CogHQBossBattle.CogHQBossBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('SellbotHQBossBattle') + + def __init__(self, loader, parentFSM, doneEvent): + CogHQBossBattle.CogHQBossBattle.__init__(self, loader, parentFSM, doneEvent) + self.teleportInPosHpr = (0, 95, 18, 180, 0, 0) + + def load(self): + CogHQBossBattle.CogHQBossBattle.load(self) + + def unload(self): + CogHQBossBattle.CogHQBossBattle.unload(self) + + def enter(self, requestStatus): + CogHQBossBattle.CogHQBossBattle.enter(self, requestStatus, DistributedSellbotBoss.OneBossCog) + self.__setupHighSky() + + def exit(self): + CogHQBossBattle.CogHQBossBattle.exit(self) + self.__cleanupHighSky() + + def __setupHighSky(self): + self.loader.hood.startSky() + sky = self.loader.hood.sky + sky.setH(150) + sky.setZ(-100) + + def __cleanupHighSky(self): + self.loader.hood.stopSky() + sky = self.loader.hood.sky + sky.setH(0) + sky.setZ(0) diff --git a/toontown/coghq/SellbotHQExterior.py b/toontown/coghq/SellbotHQExterior.py new file mode 100755 index 00000000..f7d28087 --- /dev/null +++ b/toontown/coghq/SellbotHQExterior.py @@ -0,0 +1,37 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import CogHQExterior +from toontown.dna.DNAParser import loadDNAFileAI, DNAStorage +from toontown.hood import ZoneUtil + + +class SellbotHQExterior(CogHQExterior.CogHQExterior): + notify = DirectNotifyGlobal.directNotify.newCategory('SellbotHQExterior') + + def enter(self, requestStatus): + CogHQExterior.CogHQExterior.enter(self, requestStatus) + + self.loader.hood.startSky() + + # Load the CogHQ DNA file: + dnaStore = DNAStorage() + dnaFileName = self.genDNAFileName(self.zoneId) + loadDNAFileAI(dnaStore, dnaFileName) + + # Collect all of the vis group zone IDs: + self.zoneVisDict = {} + for i in xrange(dnaStore.getNumDNAVisGroupsAI()): + groupFullName = dnaStore.getDNAVisGroupName(i) + visGroup = dnaStore.getDNAVisGroupAI(i) + visZoneId = int(base.cr.hoodMgr.extractGroupName(groupFullName)) + visibles = [] + for i in xrange(visGroup.getNumVisibles()): + visibles.append(int(visGroup.getVisible(i))) + visibles.append(ZoneUtil.getBranchZone(visZoneId)) + self.zoneVisDict[visZoneId] = visibles + + # Next, we want interest in all vis groups due to this being a Cog HQ: + base.cr.sendSetZoneMsg(self.zoneId, self.zoneVisDict.values()[0]) + + def exit(self): + self.loader.hood.stopSky() + CogHQExterior.CogHQExterior.exit(self) diff --git a/toontown/coghq/SellbotLegFactoryCogs.py b/toontown/coghq/SellbotLegFactoryCogs.py new file mode 100755 index 00000000..6889603c --- /dev/null +++ b/toontown/coghq/SellbotLegFactoryCogs.py @@ -0,0 +1,601 @@ +from SpecImports import * +LobbyParent = 10014 +BoilerParent = 10030 +PipeLeftParent = 10023 +PipeRightParent = 10032 +OilParent = 10034 +ControlParent = 10037 +DuctParent = 10036 +CenterSiloBattleCellParent = 10064 +CenterSiloParent = 20095 +SigRoomParent = 20058 +WestSiloParent = 20094 +WestSiloBattleCellParent = 10047 +EastSiloParent = 20096 +EastSiloBattleCellParent = 10068 +LobbyCell = 0 +BoilerCell = 1 +PipeLeftCell = 2 +PipeRightCell = 3 +OilCell = 4 +ControlCell = 5 +DuctCell = 6 +CenterSiloCell = 7 +SigRoomCell = 8 +WestSiloCell = 9 +EastSiloCell = 10 +BattleCells = {LobbyCell: {'parentEntId': LobbyParent, + 'pos': Point3(0, 0, 0)}, + BoilerCell: {'parentEntId': BoilerParent, + 'pos': Point3(0, 0, 0)}, + OilCell: {'parentEntId': OilParent, + 'pos': Point3(0, 0, 0)}, + ControlCell: {'parentEntId': ControlParent, + 'pos': Point3(0, 0, 0)}, + CenterSiloCell: {'parentEntId': CenterSiloBattleCellParent, + 'pos': Point3(0, 0, 0)}, + PipeLeftCell: {'parentEntId': PipeLeftParent, + 'pos': Point3(0, 0, 0)}, + PipeRightCell: {'parentEntId': PipeRightParent, + 'pos': Point3(0, 0, 0)}, + DuctCell: {'parentEntId': DuctParent, + 'pos': Point3(0, 0, 0)}, + SigRoomCell: {'parentEntId': SigRoomParent, + 'pos': Point3(0, 0, 0)}, + WestSiloCell: {'parentEntId': WestSiloBattleCellParent, + 'pos': Point3(0, 0, 0)}, + EastSiloCell: {'parentEntId': EastSiloBattleCellParent, + 'pos': Point3(-20, -10, 0)}} +CogData = [{'type': 'tm', + 'parentEntId': LobbyParent, + 'boss': 0, + 'level': 4, + 'battleCell': LobbyCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20078, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': LobbyParent, + 'boss': 0, + 'level': 6, + 'battleCell': LobbyCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20009, + 'skeleton': 0}, + {'type': 'cc', + 'parentEntId': LobbyParent, + 'boss': 0, + 'level': 4, + 'battleCell': LobbyCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20079, + 'skeleton': 0}, + {'type': 'cc', + 'parentEntId': BoilerParent, + 'boss': 0, + 'level': 4, + 'battleCell': BoilerCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'walk', + 'path': 20076, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': BoilerParent, + 'boss': 0, + 'level': 6, + 'battleCell': BoilerCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'walk', + 'path': 20077, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': BoilerParent, + 'boss': 0, + 'level': 7, + 'battleCell': BoilerCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': None, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': OilParent, + 'boss': 0, + 'level': 5, + 'battleCell': OilCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 60133, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': OilParent, + 'boss': 0, + 'level': 6, + 'battleCell': OilCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 60134, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': OilParent, + 'boss': 0, + 'level': 6, + 'battleCell': OilCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 60135, + 'skeleton': 0}, + {'type': 'cc', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 6, + 'battleCell': ControlCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20039, + 'skeleton': 1}, + {'type': 'cc', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 6, + 'battleCell': ControlCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20049, + 'skeleton': 1}, + {'type': 'cc', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 7, + 'battleCell': ControlCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20075, + 'skeleton': 1}, + {'type': 'nd', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 7, + 'battleCell': CenterSiloCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20103, + 'skeleton': 0}, + {'type': 'gh', + 'parentEntId': CenterSiloParent, + 'boss': 1, + 'level': 11, + 'battleCell': CenterSiloCell, + 'pos': Point3(0, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 7, + 'battleCell': CenterSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20104, + 'skeleton': 0}, + {'type': 'tm', # mingler or gladhander + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 8, + 'battleCell': CenterSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20105, + 'skeleton': 0}, + {'type': 'nd', + 'parentEntId': WestSiloParent, + 'boss': 0, + 'level': 7, + 'battleCell': WestSiloCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20097, + 'skeleton': 0}, + {'type': 'gh', + 'parentEntId': WestSiloParent, + 'boss': 0, + 'level': 8, + 'battleCell': WestSiloCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20098, + 'skeleton': 0}, + {'type': 'nd', + 'parentEntId': WestSiloParent, + 'boss': 0, + 'level': 9, + 'battleCell': WestSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20099, + 'skeleton': 0}, + {'type': 'nd', + 'parentEntId': EastSiloParent, + 'boss': 0, + 'level': 7, + 'battleCell': EastSiloCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20100, + 'skeleton': 0}, + {'type': 'gh', + 'parentEntId': EastSiloParent, + 'boss': 0, + 'level': 8, + 'battleCell': EastSiloCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20101, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': EastSiloParent, + 'boss': 0, + 'level': 9, + 'battleCell': EastSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20102, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': PipeLeftParent, + 'boss': 0, + 'level': 5, + 'battleCell': PipeLeftCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20109, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeLeftParent, + 'boss': 0, + 'level': 5, + 'battleCell': PipeLeftCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20110, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeLeftParent, + 'boss': 0, + 'level': 6, + 'battleCell': PipeLeftCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20111, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeRightParent, + 'boss': 0, + 'level': 5, + 'battleCell': PipeRightCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20106, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeRightParent, + 'boss': 0, + 'level': 5, + 'battleCell': PipeRightCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20107, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeRightParent, + 'boss': 0, + 'level': 6, + 'battleCell': PipeRightCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20108, + 'skeleton': 1}, + {'type': 'cc', + 'parentEntId': DuctParent, + 'boss': 0, + 'level': 4, + 'battleCell': DuctCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20038, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': DuctParent, + 'boss': 0, + 'level': 5, + 'battleCell': DuctCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20067, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': DuctParent, + 'boss': 0, + 'level': 6, + 'battleCell': DuctCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20068, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 5, + 'battleCell': SigRoomCell, + 'pos': Point3(5, -10.75, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 5, + 'battleCell': SigRoomCell, + 'pos': Point3(5, -3.25, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 6, + 'battleCell': SigRoomCell, + 'pos': Point3(5, 3.25, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'type': 'nd', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 7, + 'battleCell': SigRoomCell, + 'pos': Point3(5, 10.75, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [{'type': 'cc', + 'parentEntId': LobbyParent, + 'boss': 0, + 'level': 2, + 'battleCell': LobbyCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20009, + 'skeleton': 0, + 'joinParent': 20018}, + {'type': 'cc', + 'parentEntId': BoilerParent, + 'boss': 0, + 'level': 2, + 'battleCell': BoilerCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'walk', + 'path': 20076, + 'skeleton': 0, + 'joinParent': 20019}, + {'type': 'tm', + 'parentEntId': BoilerParent, + 'boss': 0, + 'level': 2, + 'battleCell': BoilerCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'walk', + 'path': 20077, + 'skeleton': 0, + 'joinParent': 20019}, + {'type': 'tm', + 'parentEntId': OilParent, + 'boss': 0, + 'level': 3, + 'battleCell': OilCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 60133, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': OilParent, + 'boss': 0, + 'level': 4, + 'battleCell': OilCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 60135, + 'skeleton': 0}, + {'type': 'cc', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 2, + 'battleCell': ControlCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20039, + 'skeleton': 1}, + {'type': 'cc', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 2, + 'battleCell': ControlCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20049, + 'skeleton': 1}, + {'type': 'cc', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 2, + 'battleCell': ControlCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'walk', + 'path': 20075, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 3, + 'battleCell': CenterSiloCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20103, + 'skeleton': 0}, + {'type': 'gh', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 5, + 'battleCell': CenterSiloCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': None, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 3, + 'battleCell': CenterSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20104, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 3, + 'battleCell': CenterSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20105, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': PipeLeftParent, + 'boss': 0, + 'level': 3, + 'battleCell': PipeLeftCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': None, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeLeftParent, + 'boss': 0, + 'level': 4, + 'battleCell': PipeLeftCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': None, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeRightParent, + 'boss': 0, + 'level': 3, + 'battleCell': PipeRightCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': None, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeRightParent, + 'boss': 0, + 'level': 4, + 'battleCell': PipeRightCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': None, + 'skeleton': 1}, + {'type': 'cc', + 'parentEntId': DuctParent, + 'boss': 0, + 'level': 2, + 'battleCell': DuctCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20038, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': DuctParent, + 'boss': 0, + 'level': 2, + 'battleCell': DuctCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20067, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 3, + 'battleCell': SigRoomCell, + 'pos': Point3(5, -10.75, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 4, + 'battleCell': SigRoomCell, + 'pos': Point3(5, -3.25, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] diff --git a/toontown/coghq/SellbotLegFactorySpec.py b/toontown/coghq/SellbotLegFactorySpec.py new file mode 100755 index 00000000..7356fb61 --- /dev/null +++ b/toontown/coghq/SellbotLegFactorySpec.py @@ -0,0 +1,4765 @@ +from toontown.toonbase import TTLocalizer +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500.0, + 'modelFilename': 'phase_9/models/cogHQ/SelbotLegFactory', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 3: {'type': 'zone', + 'name': 'Main Entrance', + 'comment': '', + 'parentEntId': 0, + 'scale': Vec3(1, 1, 1), + 'description': TTLocalizer.SellbotLegFactorySpecMainEntrance, + 'visibility': [114]}, + 4: {'type': 'zone', + 'name': 'Lobby', + 'comment': '', + 'parentEntId': 0, + 'scale': Vec3(1, 1, 1), + 'description': TTLocalizer.SellbotLegFactorySpecLobby, + 'visibility': [113, 114]}, + 5: {'type': 'zone', + 'name': 'hallwayFromLobby', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [113, 116]}, + 6: {'type': 'zone', + 'name': 'hallwayToBoiler/Control/Lookout', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLobbyHallway, + 'visibility': [109, + 116, + 117, + 118]}, + 7: {'type': 'zone', + 'name': 'GearRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecGearRoom, + 'visibility': [109, 110]}, + 8: {'type': 'zone', + 'name': 'BoilerRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecBoilerRoom, + 'visibility': [108, 117]}, + 9: {'type': 'zone', + 'name': 'EastCatwalk', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastCatwalk, + 'visibility': [23, + 25, + 26, + 33, + 34, + 35, + 38, + 41, + 53, + 110, + 112, + 115, + 124, + 200, + 222]}, + 10: {'type': 'zone', + 'name': 'PaintMixer', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecPaintMixer, + 'visibility': [11, 111, 112]}, + 11: {'type': 'zone', + 'name': 'PaintMixerRewardRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecPaintMixerStorageRoom, + 'visibility': [10, 111, 112]}, + 12: {'type': 'zone', + 'name': 'WestSiloCatwalk', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestSiloCatwalk, + 'visibility': [21, + 26, + 33, + 34, + 35, + 36, + 37, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 119, + 120, + 125, + 127, + 128, + 129, + 130, + 200]}, + 13: {'type': 'zone', + 'name': 'PipeRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecPipeRoom, + 'visibility': [119, 121]}, + 14: {'type': 'zone', + 'name': 'StairsToPipeRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [17, + 18, + 121, + 126, + 131]}, + 15: {'type': 'zone', + 'name': 'DuctRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecDuctRoom, + 'visibility': [106, 126]}, + 16: {'type': 'zone', + 'name': 'Side Entrance', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecSideEntrance, + 'visibility': [106]}, + 17: {'type': 'zone', + 'name': 'StomperAlley', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecStomperAlley, + 'visibility': [14, + 121, + 126, + 131]}, + 18: {'type': 'zone', + 'name': 'LavaRoomFoyer', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLavaRoomFoyer, + 'visibility': [19, + 20, + 102, + 103, + 105, + 131]}, + 19: {'type': 'zone', + 'name': 'LavaRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLavaRoom, + 'visibility': [17, + 18, + 20, + 105, + 131]}, + 20: {'type': 'zone', + 'name': 'LavaRewardRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLavaStorageRoom, + 'visibility': [18, 19, 105]}, + 21: {'type': 'zone', + 'name': 'WestCatwalk', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestCatwalk, + 'visibility': [12, + 23, + 26, + 33, + 34, + 35, + 40, + 41, + 53, + 60, + 108, + 119, + 120, + 125, + 127, + 200]}, + 22: {'type': 'zone', + 'name': 'OilRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecOilRoom, + 'visibility': [107]}, + 23: {'type': 'zone', + 'name': 'Lookout', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLookout, + 'visibility': [24, + 39, + 115, + 118, + 120, + 123, + 124, + 125]}, + 24: {'type': 'zone', + 'name': 'Warehouse', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWarehouse, + 'visibility': [23, + 39, + 115, + 120, + 123, + 124, + 125]}, + 25: {'type': 'zone', + 'name': 'PaintMixerExterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 26: {'type': 'zone', + 'name': 'WarehouseExterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 27: {'type': 'zone', + 'name': 'OilRoomHallway', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecOilRoomHallway, + 'visibility': [105, 107, 127]}, + 30: {'type': 'zone', + 'name': 'EastSiloControlRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastSiloControlRoom, + 'visibility': [130]}, + 31: {'type': 'zone', + 'name': 'WestSiloControlRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestSiloControlRoom, + 'visibility': [128]}, + 32: {'type': 'zone', + 'name': 'CenterSiloControlRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecCenterSiloControlRoom, + 'visibility': [129]}, + 33: {'type': 'zone', + 'name': 'EastSilo', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastSilo, + 'visibility': [9, + 12, + 21, + 25, + 26, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 124, + 128, + 129, + 130, + 200, + 222]}, + 34: {'type': 'zone', + 'name': 'WestSilo', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestSilo, + 'visibility': [9, + 12, + 21, + 25, + 26, + 33, + 35, + 36, + 37, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 120, + 125, + 127, + 128, + 129, + 130, + 200]}, + 35: {'type': 'zone', + 'name': 'CenterSilo', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecCenterSilo, + 'visibility': [9, + 21, + 25, + 26, + 33, + 34, + 36, + 37, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 128, + 129, + 130, + 200]}, + 36: {'type': 'zone', + 'name': 'WestSiloBridge', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [9, + 12, + 21, + 25, + 26, + 33, + 34, + 35, + 36, + 37, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 127, + 128, + 129, + 130, + 200]}, + 37: {'type': 'zone', + 'name': 'EastSiloBridge', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [9, + 12, + 21, + 25, + 26, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 128, + 129, + 130, + 200, + 222]}, + 38: {'type': 'zone', + 'name': 'EastSiloCatwalk', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastSiloCatwalk, + 'visibility': [9, + 25, + 26, + 33, + 34, + 35, + 36, + 37, + 41, + 53, + 60, + 110, + 112, + 115, + 124, + 200, + 222]}, + 39: {'type': 'zone', + 'name': 'WarehouseCeiling', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 40: {'type': 'zone', + 'name': 'WestExterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 41: {'type': 'zone', + 'name': 'EastExterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 53: {'type': 'zone', + 'name': 'ExteriorFloor', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 60: {'type': 'zone', + 'name': 'WestElevatorShaft', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestElevatorShaft, + 'visibility': [12, 34]}, + 61: {'type': 'zone', + 'name': 'EastElevatorShaft', + 'comment': 'no geom or DCS', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastElevatorShaft, + 'visibility': [33, 38]}, + 101: {'type': 'zone', + 'name': 'dwToLavaRewardRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 102: {'type': 'zone', + 'name': 'dwToLavaRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 103: {'type': 'zone', + 'name': 'dwToLavaRoomHallway', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 105: {'type': 'zone', + 'name': 'dwToOilRoomCatwalks', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 106: {'type': 'zone', + 'name': 'dwToDuctRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 107: {'type': 'zone', + 'name': 'dwToOilRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 108: {'type': 'zone', + 'name': 'dwFromBoilerRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 109: {'type': 'zone', + 'name': 'dwToGearRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110: {'type': 'zone', + 'name': 'dwFromGearRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 111: {'type': 'zone', + 'name': 'dwToPaintMixerRewardRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 112: {'type': 'zone', + 'name': 'dwToPaintMixer', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 113: {'type': 'zone', + 'name': 'dwFromLobby', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 114: {'type': 'zone', + 'name': 'dwToLobby', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 115: {'type': 'zone', + 'name': 'dwToWarehouseFromRight', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 116: {'type': 'zone', + 'name': 'dwFromLobbyFar', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 117: {'type': 'zone', + 'name': 'dwToBoilerRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 118: {'type': 'zone', + 'name': 'dwToLookout', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 119: {'type': 'zone', + 'name': 'dwFromPipeRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 120: {'type': 'zone', + 'name': 'dwToWarehouseFromLeft', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 121: {'type': 'zone', + 'name': 'dwToPipeRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 122: {'type': 'zone', + 'name': 'dwToWarehouseControlRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 123: {'type': 'zone', + 'name': 'dwFromWarehouseFloor', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 124: {'type': 'zone', + 'name': 'dwFromWarehouseRight', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 125: {'type': 'zone', + 'name': 'dwFromWarehouseLeft', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 126: {'type': 'zone', + 'name': 'dwFromDuctRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 127: {'type': 'zone', + 'name': 'dwFromOilRoomHallway', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 128: {'type': 'zone', + 'name': 'dwToWestSiloRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 129: {'type': 'zone', + 'name': 'dwToCenterSiloRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 130: {'type': 'zone', + 'name': 'dwToEastSiloRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 131: {'type': 'zone', + 'name': 'dwFromStomperAlley', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 200: {'type': 'zone', + 'name': 'sky', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 201: {'type': 'zone', + 'name': 'extraZone201', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 202: {'type': 'zone', + 'name': 'extraZone202', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 203: {'type': 'zone', + 'name': 'extraZone203', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 204: {'type': 'zone', + 'name': 'extraZone204', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 205: {'type': 'zone', + 'name': 'extraZone205', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 206: {'type': 'zone', + 'name': 'extraZone206', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 207: {'type': 'zone', + 'name': 'extraZone207', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 208: {'type': 'zone', + 'name': 'extraZone208', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 209: {'type': 'zone', + 'name': 'extraZone209', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 210: {'type': 'zone', + 'name': 'extraZone210', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 211: {'type': 'zone', + 'name': 'extraZone211', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 212: {'type': 'zone', + 'name': 'extraZone212', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 213: {'type': 'zone', + 'name': 'extraZone213', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 214: {'type': 'zone', + 'name': 'extraZone214', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 215: {'type': 'zone', + 'name': 'extraZone215', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 216: {'type': 'zone', + 'name': 'extraZone216', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 217: {'type': 'zone', + 'name': 'extraZone217', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 218: {'type': 'zone', + 'name': 'extraZone218', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 219: {'type': 'zone', + 'name': 'extraZone219', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 220: {'type': 'zone', + 'name': 'extraZone220', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 221: {'type': 'zone', + 'name': 'extraZone221', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 222: {'type': 'zone', + 'name': 'dwToEastSiloInterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10010: {'type': 'ambientSound', + 'name': 'westWind', + 'comment': '', + 'parentEntId': 35, + 'pos': Point3(-52.7549, -38.8374, 53.3758), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'enabled': 1, + 'soundPath': 'phase_9/audio/sfx/CHQ_FACT_whistling_wind.ogg', + 'volume': 1}, + 10016: {'type': 'ambientSound', + 'name': 'sndConveyorBelt', + 'comment': '', + 'parentEntId': 10056, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'enabled': 1, + 'soundPath': 'phase_9/audio/sfx/CHQ_FACT_conveyor_belt.ogg', + 'volume': 0.5}, + 10053: {'type': 'ambientSound', + 'name': 'eastWind', + 'comment': '', + 'parentEntId': 35, + 'pos': Point3(52.75, -38.84, 53.38), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'enabled': 1, + 'soundPath': 'phase_9/audio/sfx/CHQ_FACT_whistling_wind.ogg', + 'volume': 1}, + 10055: {'type': 'ambientSound', + 'name': 'sndGears', + 'comment': '', + 'parentEntId': 10056, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'enabled': 1, + 'soundPath': 'phase_9/audio/sfx/CHQ_FACT_gears_turning.ogg', + 'volume': 1}, + 10031: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 8, + 'pos': Point3(-1, 79, 10), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.75, 1, 1), + 'cellId': 1, + 'radius': 10.0}, + 10035: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 4, + 'radius': 10.0}, + 10038: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(0, -28.04, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 5, + 'radius': 10.0}, + 20048: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0.973602, 71.7, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 0.2, 1), + 'cellId': 0, + 'radius': 15.0}, + 20063: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 20033, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 8, + 'radius': 1}, + 20064: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 20034, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 8, + 'radius': 1}, + 20065: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 20035, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 8, + 'radius': 1}, + 20066: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 20036, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 8, + 'radius': 1}, + 20086: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 33, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1, 1), + 'cellId': 6, + 'radius': 12.0}, + 20112: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(-10.0936, -9.55975, 4), + 'hpr': Point3(45, 0, 0), + 'scale': Point3(10, 1, 5), + 'cellId': 10, + 'radius': 5.0}, + 20113: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(9.08399, 4.42157, 0), + 'hpr': Point3(-50, 0, 0), + 'scale': Point3(10, 2, 6), + 'cellId': 9, + 'radius': 5.0}, + 20114: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 60103, + 'pos': Point3(0, 0, 1), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 0.5), + 'cellId': 8, + 'radius': 3.0}, + 10003: {'type': 'beanBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(1.25458, 19.2471, 0.0249529), + 'hpr': Vec3(-8.28434, 0, 0), + 'scale': 1, + 'rewardPerGrab': 25, + 'rewardPerGrabMax': 0}, + 10011: {'type': 'beanBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(16.344, -9.73, 0.025), + 'hpr': Vec3(-79.8888, 0, 0), + 'scale': 1, + 'rewardPerGrab': 25, + 'rewardPerGrabMax': 0}, + 20017: {'type': 'beanBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(20.0035, 2.94232, 0), + 'hpr': Vec3(-31.6033, 0, 0), + 'scale': 1, + 'rewardPerGrab': 35, + 'rewardPerGrabMax': 0}, + 10039: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(-7, 29, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(4, 4, 4), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 20033: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(0.862745, 0.517647, 0.0941177, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1}, + 20034: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(7.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(0.862745, 0.517647, 0.0941177, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1}, + 20035: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(15, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(0.862745, 0.517647, 0.0941177, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1}, + 20036: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(22.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(0.862745, 0.517647, 0.0941177, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1}, + 30040: {'type': 'button', + 'name': 'door button', + 'comment': 'Entrance door unlock', + 'parentEntId': 3, + 'pos': Point3(0, 6.75, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 30076: {'type': 'button', + 'name': 'open door 113', + 'comment': 'Lobby door unlock', + 'parentEntId': 4, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1}, + 60102: {'type': 'button', + 'name': 'door button', + 'comment': 'Entrance Door Unlock', + 'parentEntId': 16, + 'pos': Point3(4, 8, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 60103: {'type': 'button', + 'name': 'door button', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(25, -7, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(4, 4, 4), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 60104: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 31, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(5, 5, 4), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 60105: {'type': 'button', + 'name': 'door button', + 'comment': '', + 'parentEntId': 30, + 'pos': Point3(-4, 7, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(5, 5, 4), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 60118: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 10005: {'type': 'conveyorBelt', + 'name': 'belt', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 45.2024, 7.24937), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'floorName': 'platformcollision', + 'length': 78.81881352704218, + 'speed': 2.0, + 'treadLength': 10.0, + 'treadModelPath': 'phase_9/models/cogHQ/platform1', + 'widthScale': 0.85}, + 20081: {'type': 'crate', + 'name': '', + 'comment': '', + 'parentEntId': 20080, + 'pos': Point3(0, 0, 0), + 'scale': 0.920000016689, + 'crushCellId': None, + 'gridId': 20080, + 'modelType': 0, + 'pushable': 1}, + 20091: {'type': 'crate', + 'name': '', + 'comment': '', + 'parentEntId': 20090, + 'pos': Point3(0, 23, 0), + 'scale': 0.920000016689, + 'crushCellId': None, + 'gridId': 20090, + 'modelType': 0, + 'pushable': 1}, + 20024: {'type': 'crusherCell', + 'name': '', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'col': 1, + 'gridId': 20025, + 'row': 14}, + 20026: {'type': 'crusherCell', + 'name': '', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'col': 10, + 'gridId': 20025, + 'row': 14}, + 20027: {'type': 'crusherCell', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(1, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'col': 21, + 'gridId': 20025, + 'row': 14}, + 20028: {'type': 'crusherCell', + 'name': 'copy of copy of ', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(2, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'col': 28, + 'gridId': 20025, + 'row': 14}, + 30078: {'type': 'cutScene', + 'name': 'button door', + 'comment': '', + 'parentEntId': 114, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'duration': 4.0, + 'effect': 'irisInOut', + 'motion': 'doorUnlock', + 'startStopEvent': 30077}, + 10002: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 128, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 10052: {'type': 'door', + 'name': 'door 127', + 'comment': '', + 'parentEntId': 127, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 10039, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30000: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 114, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60132, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30001: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 105, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1.0, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30002: {'type': 'door', + 'name': 'door 106', + 'comment': '', + 'parentEntId': 106, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60132, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30003: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 107, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30004: {'type': 'door', + 'name': 'doorFromBoilerRoom', + 'comment': '', + 'parentEntId': 108, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30005: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 109, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30006: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 110, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30008: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 112, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30009: {'type': 'door', + 'name': 'door 113', + 'comment': '', + 'parentEntId': 113, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60119, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30010: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 115, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30011: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 116, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30012: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 117, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30013: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 118, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30014: {'type': 'door', + 'name': 'doorFromPipeRoom 119', + 'comment': '', + 'parentEntId': 119, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30015: {'type': 'door', + 'name': 'door 120', + 'comment': '', + 'parentEntId': 120, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30016: {'type': 'door', + 'name': 'door 121', + 'comment': '', + 'parentEntId': 121, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30017: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 122, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30018: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 123, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 0, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60103, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30019: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 124, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30020: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 125, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30021: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 126, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60119, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 60088: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 131, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1.0, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 60094: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 129, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 0, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1.0, + 'unlock0Event': 0, + 'unlock1Event': 60104, + 'unlock2Event': 60105, + 'unlock3Event': 0}, + 60095: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 130, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 60101: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 222, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 10049: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 3}, + 10051: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 4}, + 60000: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 5}, + 60001: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 6}, + 60002: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 7}, + 60003: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 9}, + 60004: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 10}, + 60005: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 8}, + 60006: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 21}, + 60007: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 24}, + 60009: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 38}, + 60011: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 12}, + 60013: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 13}, + 60014: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 14}, + 60015: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 17}, + 60016: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 15}, + 60017: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 16}, + 60018: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 19}, + 60019: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 18}, + 60024: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 22}, + 60031: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 23}, + 60044: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 33}, + 60066: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 11}, + 60067: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 27}, + 60096: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 30}, + 60108: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 34}, + 60111: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 36}, + 60114: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 37}, + 60121: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 31}, + 60126: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 35}, + 60130: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 32}, + 10028: {'type': 'entrancePoint', + 'name': 'entrance1', + 'comment': '', + 'parentEntId': 3, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'entranceId': 0, + 'radius': 15, + 'theta': 20}, + 10029: {'type': 'entrancePoint', + 'name': 'entrance2', + 'comment': '', + 'parentEntId': 16, + 'pos': Point3(0, 10, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'entranceId': 1, + 'radius': 15, + 'theta': 20}, + 10021: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-2.02081, 0, 0), + 'hpr': Vec3(337.477, 0, 0), + 'scale': 1, + 'gagLevel': 2, + 'gagLevelMax': 0, + 'gagTrack': 0, + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 10024: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(20.3012, -26.3219, 0), + 'hpr': Vec3(233.187, 0, 0), + 'scale': 1, + 'gagLevel': 4, + 'gagLevelMax': 0, + 'gagTrack': 4, + 'rewardPerGrab': 4, + 'rewardPerGrabMax': 0}, + 10025: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-47.312, 7.22571, 0), + 'hpr': Vec3(19.1524, 0, 0), + 'scale': 1, + 'gagLevel': 0, + 'gagLevelMax': 0, + 'gagTrack': 0, + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 10026: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-11.2037, 5.43514, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'gagLevel': 4, + 'gagLevelMax': 0, + 'gagTrack': 5, + 'rewardPerGrab': 4, + 'rewardPerGrabMax': 0}, + 20020: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(-23.0209, 0, 0), + 'hpr': Vec3(126.676, 0, 0), + 'scale': 1, + 'gagLevel': 4, + 'gagLevelMax': 0, + 'gagTrack': 3, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 20021: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(-31.3225, 14.1021, 0), + 'hpr': Vec3(-136.57, 0, 0), + 'scale': 1, + 'gagLevel': 4, + 'gagLevelMax': 0, + 'gagTrack': 5, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 20085: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 5, + 'pos': Point3(3.14, 12.6703, 10.12), + 'hpr': Vec3(-24.8105, 0, 0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 4, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 20093: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20087, + 'pos': Point3(2.4, -1, 7), + 'hpr': Vec3(-151.532, 0, 0), + 'scale': 1, + 'gagLevel': 0, + 'gagLevelMax': 0, + 'gagTrack': 0, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10006: {'type': 'gear', + 'name': 'first', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 0, 26.0634), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'degreesPerSec': 20.0, + 'gearScale': 25.0, + 'modelType': 'factory', + 'orientation': 'vertical', + 'phaseShift': 0}, + 10007: {'type': 'gear', + 'name': 'second', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 15, 26.06), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'degreesPerSec': 30.0, + 'gearScale': 25.0, + 'modelType': 'factory', + 'orientation': 'vertical', + 'phaseShift': 0}, + 10008: {'type': 'gear', + 'name': 'third', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 30, 26.06), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'degreesPerSec': 40.0, + 'gearScale': 25.0, + 'modelType': 'factory', + 'orientation': 'vertical', + 'phaseShift': 0}, + 10009: {'type': 'gear', + 'name': 'fourth', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 45, 26.06), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'degreesPerSec': 47.0, + 'gearScale': 25.0, + 'modelType': 'factory', + 'orientation': 'vertical', + 'phaseShift': 0}, + 20013: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20012, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.25, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 7, + 'velocity': 4}, + 20014: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20010, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.25, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 7, + 'velocity': 4}, + 20016: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20015, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.25, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 7, + 'velocity': 4}, + 20041: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20040, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20026, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 80.0, + 'strength': 10, + 'velocity': 6.0}, + 20043: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20042, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20024, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 80.0, + 'strength': 10, + 'velocity': 5.0}, + 20046: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20044, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20024, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 70, + 'strength': 10, + 'velocity': 6.0}, + 20047: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20045, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20026, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 80.0, + 'strength': 10, + 'velocity': 6.0}, + 20052: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20051, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 12.0, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 3, + 'velocity': 4.0}, + 20054: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20053, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20027, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 80.0, + 'strength': 10, + 'velocity': 5.5}, + 20056: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20055, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20028, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 70, + 'strength': 10, + 'velocity': 6.0}, + 20060: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20059, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20028, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 90.0, + 'strength': 10, + 'velocity': 6.5}, + 20062: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20061, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20027, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 70, + 'strength': 10, + 'velocity': 7.5}, + 20071: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20070, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 7, + 'velocity': 6.0}, + 20072: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20069, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 80.0, + 'strength': 7, + 'velocity': 6.0}, + 20074: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20073, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.25, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 7, + 'velocity': 4}, + 20089: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20084, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 7, + 'velocity': 4}, + 20115: {'type': 'goonClipPlane', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, -7.4, 0), + 'hpr': Point3(-90, 0, 0), + 'scale': Point3(5, 5, 5), + 'goonId': 20052}, + 20116: {'type': 'goonClipPlane', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, -58, 0), + 'hpr': Point3(90, 0, 0), + 'scale': 1, + 'goonId': None}, + 20117: {'type': 'goonClipPlane', + 'name': '', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(0, -29, 0), + 'hpr': Point3(90, 0, 0), + 'scale': 1, + 'goonId': None}, + 20118: {'type': 'goonClipPlane', + 'name': '', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(-52, 0, 5), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'goonId': None}, + 20025: {'type': 'grid', + 'name': '', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(-48.4442, -24.9385, 0), + 'scale': 1, + 'cellSize': 3, + 'numCol': 30, + 'numRow': 16}, + 20080: {'type': 'grid', + 'name': '', + 'comment': '', + 'parentEntId': 5, + 'pos': Point3(1.5, -10.7, 0), + 'scale': 1, + 'cellSize': 3, + 'numCol': 2, + 'numRow': 5}, + 20090: {'type': 'grid', + 'name': '', + 'comment': '', + 'parentEntId': 17, + 'pos': Point3(-6.5, -111, 0), + 'scale': 1, + 'cellSize': 3, + 'numCol': 2, + 'numRow': 9}, + 20011: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(-2.06235, 20.2198, 0.025), + 'hpr': Vec3(-19.2153, 0, 0), + 'scale': 1, + 'rewardPerGrab': 10, + 'rewardPerGrabMax': 0}, + 20092: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20087, + 'pos': Point3(-1, -1.5, 7), + 'hpr': Vec3(-191.79, 0, 0), + 'scale': 1, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10041: {'type': 'lift', + 'name': 'westLift', + 'comment': '', + 'parentEntId': 60, + 'pos': Point3(0, 0, 0.0641994), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'autoMoveDelay': 5, + 'duration': 7.0, + 'endBoardSides': ['back'], + 'endGuardName': 'topGuard', + 'endPos': Point3(0, 0, 165), + 'floorName': 'elevator_floor', + 'modelPath': 'phase_9/models/cogHQ/Elevator.bam', + 'modelScale': Vec3(1, 1, 1), + 'moveDelay': 1, + 'startBoardSides': ['front'], + 'startGuardName': 'bottomGuard', + 'startPos': Point3(0, 0, 0)}, + 10048: {'type': 'lift', + 'name': 'eastLift', + 'comment': '', + 'parentEntId': 61, + 'pos': Point3(0, -0.684064, 0.589322), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'autoMoveDelay': 5.0, + 'duration': 7.0, + 'endBoardSides': ['front', + 'back', + 'left', + 'right'], + 'endGuardName': 'topGuard', + 'endPos': Point3(0, 0, 165), + 'floorName': 'elevator_floor', + 'modelPath': 'phase_9/models/cogHQ/Elevator.bam', + 'modelScale': Vec3(1, 1, 1), + 'moveDelay': 1, + 'startBoardSides': ['front'], + 'startGuardName': 'bottomGuard', + 'startPos': Point3(0, 0, 0)}, + 10057: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 10043, + 'input1Event': 30009, + 'input2Event': 30000, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 10059: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 10058, + 'input1Event': 10057, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 10061: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 10060, + 'input1Event': 10059, + 'input2Event': 30013, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 10063: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 10062, + 'input1Event': 60033, + 'input2Event': 30009, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 30068: {'type': 'logicGate', + 'name': 'door 116 and door 118', + 'comment': '', + 'parentEntId': 30069, + 'input1Event': 30013, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60023: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60021, + 'input1Event': 30011, + 'input2Event': 30009, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60025: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60022, + 'input1Event': 60023, + 'input2Event': 30013, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60028: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60026, + 'input1Event': 30011, + 'input2Event': 30005, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60029: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60027, + 'input1Event': 30011, + 'input2Event': 30012, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60030: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 30071, + 'input1Event': 30011, + 'input2Event': 30009, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60033: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 30073, + 'input1Event': 30013, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60034: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 30075, + 'input1Event': 30013, + 'input2Event': 30005, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60035: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60032, + 'input1Event': 30013, + 'input2Event': 30012, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60037: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60036, + 'input1Event': 30005, + 'input2Event': 30012, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60039: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60038, + 'input1Event': 30012, + 'input2Event': 30005, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60041: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60040, + 'input1Event': 30020, + 'input2Event': 30019, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60043: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60042, + 'input1Event': 30019, + 'input2Event': 30020, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60047: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60045, + 'input1Event': 10002, + 'input2Event': 30019, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60049: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60048, + 'input1Event': 30003, + 'input2Event': 10052, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60051: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60050, + 'input1Event': 30001, + 'input2Event': 10052, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60053: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60052, + 'input1Event': 30021, + 'input2Event': 30016, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60055: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60054, + 'input1Event': 30002, + 'input2Event': 30021, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60057: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60056, + 'input1Event': 30016, + 'input2Event': 30021, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60059: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60058, + 'input1Event': 30012, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60061: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60060, + 'input1Event': 30012, + 'input2Event': 30013, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60064: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60062, + 'input1Event': 30005, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60065: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60063, + 'input1Event': 30005, + 'input2Event': 30013, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60074: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60072, + 'input1Event': 10052, + 'input2Event': 30003, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60075: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60073, + 'input1Event': 10052, + 'input2Event': 30001, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60076: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60020, + 'input1Event': 30021, + 'input2Event': 30002, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60078: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60077, + 'input1Event': 30021, + 'input2Event': 30002, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60080: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60079, + 'input1Event': 60057, + 'input2Event': 30002, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60082: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60081, + 'input1Event': 60055, + 'input2Event': 30016, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60084: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60083, + 'input1Event': 30004, + 'input2Event': 30014, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60086: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60085, + 'input1Event': 30006, + 'input2Event': 30008, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60091: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60087, + 'input1Event': 60088, + 'input2Event': 30001, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60093: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60092, + 'input1Event': 30001, + 'input2Event': 60088, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60100: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60099, + 'input1Event': 60095, + 'input2Event': 10002, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60119: {'type': 'logicGate', + 'name': 'open sesame Duct & Lobby', + 'comment': 'links together the Duct Room and Lobby buttons', + 'parentEntId': 0, + 'input1Event': 30076, + 'input2Event': 60118, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'or'}, + 60132: {'type': 'logicGate', + 'name': 'open sesame Entrances', + 'comment': 'links together the buttons in the two entrances', + 'parentEntId': 0, + 'input1Event': 30040, + 'input2Event': 60102, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'or'}, + 60138: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60137, + 'input1Event': 60095, + 'input2Event': 60094, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60141: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60139, + 'input1Event': 10002, + 'input2Event': 60094, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60142: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60140, + 'input1Event': 10002, + 'input2Event': 60095, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 10001: {'type': 'model', + 'name': 'dropshadow', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0, 0, -25), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1.5, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_3/models/props/drop_shadow.bam'}, + 10012: {'type': 'model', + 'name': 'backCrate', + 'comment': '', + 'parentEntId': 10067, + 'pos': Point3(0, -5.81496, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 10033: {'type': 'model', + 'name': 'dropshadow', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0, 0, -25), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1.5, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_3/models/props/drop_shadow.bam'}, + 10045: {'type': 'model', + 'name': 'dropshadow', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0, 0, -25), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1.5, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_3/models/props/drop_shadow.bam'}, + 10046: {'type': 'model', + 'name': 'dropshadow', + 'comment': '', + 'parentEntId': 10009, + 'pos': Point3(0, 0, -25), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1.5, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_3/models/props/drop_shadow.bam'}, + 10050: {'type': 'model', + 'name': 'sky', + 'comment': '', + 'parentEntId': 200, + 'pos': Point3(-142.02, 437.227, 0.922491), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(2.5, 2.5, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/cog_sky.bam'}, + 10066: {'type': 'model', + 'name': 'frontCrate', + 'comment': '', + 'parentEntId': 10067, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 10069: {'type': 'model', + 'name': 'backCrate', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(0, -5.81496, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 10070: {'type': 'model', + 'name': 'frontCrate', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 20082: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 5, + 'pos': Point3(4.50815, 11.6508, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.92, 0.92, 0.92), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 20083: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 20082, + 'pos': Point3(0, 0, 5.5), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 20088: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 20087, + 'pos': Point3(1, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.3, 1, 1.3), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 10000: {'type': 'nodepath', + 'name': 'gearGauntletObstacle', + 'comment': '', + 'parentEntId': 10027, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': 'gearGauntlet', + 'comment': 'gears are staggered 15 ft in Y', + 'parentEntId': 10000, + 'pos': Point3(0, -23.25, 6.85), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10014: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, 34.07, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10015: {'type': 'nodepath', + 'name': 'paint mixer platforms', + 'comment': '', + 'parentEntId': 10, + 'pos': Point3(0, 5.15136, -2), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10022: {'type': 'nodepath', + 'name': 'gagBarrels', + 'comment': '', + 'parentEntId': 11, + 'pos': Point3(11.2328, 14.7959, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10023: {'type': 'nodepath', + 'name': 'leftCogs', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(-42.0363, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10027: {'type': 'nodepath', + 'name': 'zoneNodeCompensate', + 'comment': 'I think the ZoneNode was moved.', + 'parentEntId': 19, + 'pos': Point3(-0.426482, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10030: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 8, + 'pos': Point3(2.5, 62.5, 10), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10032: {'type': 'nodepath', + 'name': 'rightCogs', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(46.88, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10034: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': 1}, + 10036: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(5.5, 0, 0), + 'hpr': Point3(161, 0, 0), + 'scale': 1}, + 10037: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(3.1, -48.27, 0.05), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10040: {'type': 'nodepath', + 'name': 'FactoryBoss', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(0, 68.4457, 9.5669), + 'hpr': Point3(180, 0, 0), + 'scale': 1}, + 10047: {'type': 'nodepath', + 'name': 'battleCell', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10056: {'type': 'nodepath', + 'name': 'sounds', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 15), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10064: {'type': 'nodepath', + 'name': 'battleCell', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, -5.20447, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10065: {'type': 'nodepath', + 'name': 'backSteps', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 56.2652, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1.5, 1.3, 0.73)}, + 10067: {'type': 'nodepath', + 'name': 'frontSteps', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, -44.7196, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(1.5, 1.3, 0.729057)}, + 10068: {'type': 'nodepath', + 'name': 'battleCell', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20000: {'type': 'nodepath', + 'name': 'stompers', + 'comment': '', + 'parentEntId': 17, + 'pos': Point3(0.75, 0, 0.5), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20018: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0, -24, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20019: {'type': 'nodepath', + 'name': 'cogsJoin', + 'comment': '', + 'parentEntId': 10030, + 'pos': Point3(16, 2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20022: {'type': 'nodepath', + 'name': 'StomperButtonsNodepath', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(-11.75, -35.8, 14.9), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20023: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20037: {'type': 'nodepath', + 'name': 'SignatureGoonNP', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(-48.4442, -24.9385, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20058: {'type': 'nodepath', + 'name': 'SigRoomCogs', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(-1.0928, -45, 14.99), + 'hpr': Point3(90, 0, 0), + 'scale': 1}, + 20087: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 17, + 'pos': Point3(-4, -117, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20094: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(-0.720506, 27.5461, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20095: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 20096: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(4.84921, 8.74482, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10017: {'type': 'paintMixer', + 'name': 'fifth', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(5.24, 23.52, 8), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(-12, -6, 0), + 'period': 8.0, + 'phaseShift': 0.5, + 'shaftScale': 1, + 'waitPercent': 0.1}, + 10018: {'type': 'paintMixer', + 'name': 'fourth', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(-12.1, 3, 8), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(0, -6, 15), + 'period': 8.0, + 'phaseShift': 0.0, + 'shaftScale': 2.5, + 'waitPercent': 0.1}, + 10019: {'type': 'paintMixer', + 'name': 'third', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(-3.85419, -7.75751, 22.5836), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(7, 0, 0), + 'period': 8.0, + 'phaseShift': 0.0, + 'shaftScale': 2.5, + 'waitPercent': 0.1}, + 10020: {'type': 'paintMixer', + 'name': 'second', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(16.01, -6.47, 23), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(-4, -8, -15), + 'period': 8.0, + 'phaseShift': 0.0, + 'shaftScale': 2.5, + 'waitPercent': 0.1}, + 10054: {'type': 'paintMixer', + 'name': 'first', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(-10, -26.1, 8), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(15, 0, 0), + 'period': 8.0, + 'phaseShift': 0.0, + 'shaftScale': 1, + 'waitPercent': 0.1}, + 20008: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 20009: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 17, + 'pathScale': 1.0}, + 20010: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 21, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 36, + 'pathScale': 1.0}, + 20012: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 21, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 34, + 'pathScale': 1.0}, + 20015: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 21, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 37, + 'pathScale': 1.0}, + 20038: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 38, + 'pathScale': 1.0}, + 20039: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 12, + 'pathScale': 1.0}, + 20040: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(41.5, 33.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 20042: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(15, 34, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 20044: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(1.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'pathIndex': 6, + 'pathScale': 1.0}, + 20045: {'type': 'path', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'pathIndex': 7, + 'pathScale': 1.0}, + 20049: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 13, + 'pathScale': 1.0}, + 20051: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(1, -24, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 42, + 'pathScale': 1.0}, + 20053: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 8, + 'pathScale': 1.0}, + 20055: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 9, + 'pathScale': 1.0}, + 20059: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 10, + 'pathScale': 1.0}, + 20061: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 11, + 'pathScale': 1.0}, + 20067: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 39, + 'pathScale': 1.0}, + 20068: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 40, + 'pathScale': 1.0}, + 20069: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 9, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 5, + 'pathScale': 1.0}, + 20070: {'type': 'path', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 9, + 'pos': Point3(1, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 5, + 'pathScale': 1.0}, + 20073: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 21, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 35, + 'pathScale': 1.0}, + 20075: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(4, 4, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 14, + 'pathScale': 1.0}, + 20076: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 8, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 15, + 'pathScale': 1.0}, + 20077: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 8, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 16, + 'pathScale': 1.0}, + 20078: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 18, + 'pathScale': 1.0}, + 20079: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 20084: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 9, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 41, + 'pathScale': 1.0}, + 20097: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 19, + 'pathScale': 1.0}, + 20098: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 20, + 'pathScale': 1.0}, + 20099: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 21, + 'pathScale': 1.0}, + 20100: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 22, + 'pathScale': 1.0}, + 20101: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 23, + 'pathScale': 1.0}, + 20102: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 24, + 'pathScale': 1.0}, + 20103: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 25, + 'pathScale': 1.0}, + 20104: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 26, + 'pathScale': 1.0}, + 20105: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 27, + 'pathScale': 1.0}, + 20106: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 28, + 'pathScale': 1.0}, + 20107: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 29, + 'pathScale': 1.0}, + 20108: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 30, + 'pathScale': 1.0}, + 20109: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 31, + 'pathScale': 1.0}, + 20110: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 32, + 'pathScale': 1.0}, + 20111: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 33, + 'pathScale': 1.0}, + 60133: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(-10, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 60134: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 60135: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(10, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 10042: {'type': 'propSpinner', + 'name': '', + 'comment': '', + 'parentEntId': 7}, + 20001: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(7, 5, 7), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.0, + 'range': 30.0, + 'shaftScale': Point3(0.5, 12, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20002: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(0, -14.3294, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(7, 5, 7), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 10.0, + 'shaftScale': Point3(0.5, 12, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20003: {'type': 'stomper', + 'name': 'copy of copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(0, -28.3252, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(7, 5, 7), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.5, + 'range': 10.0, + 'shaftScale': Point3(0.5, 12, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20004: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(-3.5, 16.2588, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(3.5, 5, 3.5), + 'modelPath': 0, + 'motion': 3, + 'period': 3.0001373423482587, + 'phaseShift': 0.0, + 'range': 15.0, + 'shaftScale': Point3(0.71, 12, 0.71), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20005: {'type': 'stomper', + 'name': 'copy of copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(3.5, 16.2588, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(3.5, 5, 3.5), + 'modelPath': 0, + 'motion': 3, + 'period': 1.5, + 'phaseShift': 0.0, + 'range': 15.0, + 'shaftScale': Point3(0.71, 12, 0.71), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 1, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20006: {'type': 'stomper', + 'name': 'copy of copy of copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(-3.5, 23.4392, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(3.5, 5, 3.5), + 'modelPath': 0, + 'motion': 3, + 'period': 1.5, + 'phaseShift': 0.5, + 'range': 15.0, + 'shaftScale': Point3(0.71, 12, 0.71), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20007: {'type': 'stomper', + 'name': 'copy of copy of copy of copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(3.5, 23.4392, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(3.5, 5, 3.5), + 'modelPath': 0, + 'motion': 3, + 'period': 3.0, + 'phaseShift': 0.5, + 'range': 15.0, + 'shaftScale': Point3(0.71, 12, 0.71), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20029: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 20025, + 'pos': Point3(4.5, 43.5, 0.25), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 0, + 'crushCellId': 20024, + 'damage': 3, + 'headScale': Point3(3, 2, 3), + 'modelPath': 0, + 'motion': 5, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 12.0, + 'shaftScale': Point3(0.66, 37.5, 0.66), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 20033, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20030: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20025, + 'pos': Point3(31.5, 43.5, 0.25), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 0, + 'crushCellId': 20026, + 'damage': 3, + 'headScale': Point3(3, 2, 3), + 'modelPath': 0, + 'motion': 5, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 12.0, + 'shaftScale': Point3(0.66, 37.5, 0.66), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 20034, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20031: {'type': 'stomper', + 'name': 'copy of copy of ', + 'comment': '', + 'parentEntId': 20025, + 'pos': Point3(64.5, 43.5, 0.25), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 0, + 'crushCellId': 20027, + 'damage': 3, + 'headScale': Point3(3, 2, 3), + 'modelPath': 0, + 'motion': 5, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 12.0, + 'shaftScale': Point3(0.66, 37.5, 0.66), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 20035, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20032: {'type': 'stomper', + 'name': 'copy of copy of copy of ', + 'comment': '', + 'parentEntId': 20025, + 'pos': Point3(85.5, 43.5, 0.25), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 0, + 'crushCellId': 20028, + 'damage': 3, + 'headScale': Point3(3, 2, 3), + 'modelPath': 0, + 'motion': 5, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 12.0, + 'shaftScale': Point3(0.66, 37.5, 0.66), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 20036, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20050: {'type': 'trigger', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(10, 0, 10), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(20, 20, 20), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1, + 'triggerName': 'signatureRoomView'}, + 20057: {'type': 'trigger', + 'name': '', + 'comment': '', + 'parentEntId': 23, + 'pos': Point3(3, -8.8, 15.5091), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(25, 25, 25), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1, + 'triggerName': 'lookoutTrigger'}, + 30077: {'type': 'trigger', + 'name': 'button cutscene', + 'comment': '', + 'parentEntId': 3, + 'pos': Point3(-4, 8, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1, + 'triggerName': ''}, + 10013: {'type': 'visibilityExtender', + 'name': 'intoEastSilo', + 'comment': '', + 'parentEntId': 60009, + 'event': 60101, + 'newZones': [61]}, + 10043: {'type': 'visibilityExtender', + 'name': 'beyondLobby', + 'comment': '', + 'parentEntId': 10049, + 'event': 10057, + 'newZones': [5, 116]}, + 10044: {'type': 'visibilityExtender', + 'name': 'intoEntrance1', + 'comment': '', + 'parentEntId': 10051, + 'event': 30000, + 'newZones': [3]}, + 10058: {'type': 'visibilityExtender', + 'name': 'intoFarHallway', + 'comment': '', + 'parentEntId': 10049, + 'event': 10059, + 'newZones': [6, 118]}, + 10060: {'type': 'visibilityExtender', + 'name': 'intoLookout', + 'comment': '', + 'parentEntId': 10049, + 'event': 10061, + 'newZones': [23]}, + 10062: {'type': 'visibilityExtender', + 'name': 'intoLobby', + 'comment': '', + 'parentEntId': 60031, + 'event': 10063, + 'newZones': [4, 114]}, + 30022: {'type': 'visibilityExtender', + 'name': 'intoLobby', + 'comment': '', + 'parentEntId': 10049, + 'event': 30000, + 'newZones': [4, 113]}, + 30023: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60017, + 'event': 30002, + 'newZones': [15, 126]}, + 30024: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60016, + 'event': 30002, + 'newZones': [16]}, + 30025: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60016, + 'event': 30021, + 'newZones': [14, 17, 121]}, + 30026: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60015, + 'event': 30016, + 'newZones': [13, 119]}, + 30027: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60015, + 'event': 30021, + 'newZones': [15, 106]}, + 30029: {'type': 'visibilityExtender', + 'name': 'beyondLobby', + 'comment': '', + 'parentEntId': 10051, + 'event': 30009, + 'newZones': [5, 116]}, + 30030: {'type': 'visibilityExtender', + 'name': 'beyond door 113', + 'comment': '', + 'parentEntId': 60000, + 'event': 30009, + 'newZones': [4, 114]}, + 30031: {'type': 'visibilityExtender', + 'name': 'beyond door 116', + 'comment': '', + 'parentEntId': 60000, + 'event': 30011, + 'newZones': [6, + 109, + 117, + 118]}, + 30032: {'type': 'visibilityExtender', + 'name': 'intoHallwayFromLobby', + 'comment': '', + 'parentEntId': 60001, + 'event': 30011, + 'newZones': [5, 113]}, + 30033: {'type': 'visibilityExtender', + 'name': 'intoBoilerRoom', + 'comment': '', + 'parentEntId': 60001, + 'event': 30012, + 'newZones': [8]}, + 30034: {'type': 'visibilityExtender', + 'name': 'intoLookout', + 'comment': '', + 'parentEntId': 60001, + 'event': 30013, + 'newZones': [23, 39]}, + 30035: {'type': 'visibilityExtender', + 'name': 'intoGearRoom', + 'comment': '', + 'parentEntId': 60001, + 'event': 30005, + 'newZones': [7]}, + 30036: {'type': 'visibilityExtender', + 'name': 'beyond door 109', + 'comment': '', + 'parentEntId': 60002, + 'event': 30005, + 'newZones': [6, + 116, + 117, + 118]}, + 30037: {'type': 'visibilityExtender', + 'name': 'beyond door 110', + 'comment': '', + 'parentEntId': 60002, + 'event': 30006, + 'newZones': [9, + 25, + 26, + 33, + 34, + 35, + 38, + 41, + 53, + 112, + 115, + 200]}, + 30038: {'type': 'visibilityExtender', + 'name': 'beyond door 117', + 'comment': '', + 'parentEntId': 60005, + 'event': 30012, + 'newZones': [6, + 109, + 116, + 118]}, + 30039: {'type': 'visibilityExtender', + 'name': 'beyond door 108', + 'comment': '', + 'parentEntId': 60005, + 'event': 30004, + 'newZones': [12, + 21, + 26, + 34, + 35, + 40, + 41, + 53, + 60, + 119, + 120, + 200]}, + 30041: {'type': 'visibilityExtender', + 'name': 'beyond door 110', + 'comment': '', + 'parentEntId': 60003, + 'event': 30006, + 'newZones': [7]}, + 30042: {'type': 'visibilityExtender', + 'name': 'beyond door 112', + 'comment': '', + 'parentEntId': 60003, + 'event': 30008, + 'newZones': [10, 11]}, + 30043: {'type': 'visibilityExtender', + 'name': 'intoWarehouse', + 'comment': '', + 'parentEntId': 60003, + 'event': 30010, + 'newZones': [24, 39]}, + 30044: {'type': 'visibilityExtender', + 'name': 'beyond door 112', + 'comment': '', + 'parentEntId': 60004, + 'event': 30008, + 'newZones': [9, + 25, + 26, + 33, + 34, + 35, + 38, + 41, + 53, + 110, + 115, + 200]}, + 30046: {'type': 'visibilityExtender', + 'name': 'beyond door 112', + 'comment': '', + 'parentEntId': 60066, + 'event': 30008, + 'newZones': [9, + 25, + 26, + 41, + 200]}, + 30049: {'type': 'visibilityExtender', + 'name': 'beyond door 119', + 'comment': '', + 'parentEntId': 60013, + 'event': 30014, + 'newZones': [12, + 21, + 23, + 26, + 33, + 34, + 35, + 41, + 53, + 60, + 108, + 112, + 120, + 200]}, + 30050: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60013, + 'event': 30016, + 'newZones': [14, 17, 126]}, + 30051: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60014, + 'event': 30016, + 'newZones': [13, 119]}, + 30052: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60014, + 'event': 30021, + 'newZones': [15, 106]}, + 30055: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60019, + 'event': 30001, + 'newZones': [27, 127]}, + 30056: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60018, + 'event': 30001, + 'newZones': [27, 127]}, + 30057: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60018, + 'event': 60088, + 'newZones': [17]}, + 30059: {'type': 'visibilityExtender', + 'name': 'beyond door 108', + 'comment': '', + 'parentEntId': 60006, + 'event': 30004, + 'newZones': [8, 117]}, + 30060: {'type': 'visibilityExtender', + 'name': 'beyond door 119', + 'comment': '', + 'parentEntId': 60006, + 'event': 30014, + 'newZones': [13, 121]}, + 30061: {'type': 'visibilityExtender', + 'name': 'intoWarehouse', + 'comment': '', + 'parentEntId': 60006, + 'event': 30015, + 'newZones': [24, 39]}, + 30062: {'type': 'visibilityExtender', + 'name': 'beyond door 107', + 'comment': '', + 'parentEntId': 60024, + 'event': 30003, + 'newZones': [27, 127]}, + 30063: {'type': 'visibilityExtender', + 'name': 'intoHallway', + 'comment': '', + 'parentEntId': 60031, + 'event': 30013, + 'newZones': [6, + 109, + 116, + 117]}, + 30064: {'type': 'visibilityExtender', + 'name': 'beyondLowerWestDoor', + 'comment': '', + 'parentEntId': 60007, + 'event': 30015, + 'newZones': [12, + 21, + 26, + 34, + 40, + 41, + 53, + 200]}, + 30066: {'type': 'visibilityExtender', + 'name': 'beyondLowerEastDoor', + 'comment': '', + 'parentEntId': 60007, + 'event': 30010, + 'newZones': [9, + 25, + 26, + 33, + 38, + 41, + 200]}, + 30067: {'type': 'visibilityExtender', + 'name': 'beyondUpperEastDoor', + 'comment': '', + 'parentEntId': 60007, + 'event': 30019, + 'newZones': [9, + 33, + 38, + 41, + 200, + 222]}, + 30069: {'type': 'visibilityExtender', + 'name': 'beyond door 118', + 'comment': '', + 'parentEntId': 60000, + 'event': 30068, + 'newZones': [23]}, + 30071: {'type': 'visibilityExtender', + 'name': 'intoLobby', + 'comment': '', + 'parentEntId': 60001, + 'event': 60030, + 'newZones': [4, 114]}, + 30073: {'type': 'visibilityExtender', + 'name': 'intoLobbyHallway', + 'comment': '', + 'parentEntId': 60031, + 'event': 60033, + 'newZones': [5, 113]}, + 30075: {'type': 'visibilityExtender', + 'name': 'intoGearRoom', + 'comment': '', + 'parentEntId': 60031, + 'event': 60034, + 'newZones': [7]}, + 60008: {'type': 'visibilityExtender', + 'name': 'beyondUpperWestDoor', + 'comment': '', + 'parentEntId': 60007, + 'event': 30020, + 'newZones': [12, + 21, + 34, + 40, + 41, + 60, + 127, + 200]}, + 60010: {'type': 'visibilityExtender', + 'name': 'intoWarehouse', + 'comment': '', + 'parentEntId': 60009, + 'event': 30019, + 'newZones': [24, 39, 125]}, + 60012: {'type': 'visibilityExtender', + 'name': 'beyond door 125', + 'comment': '', + 'parentEntId': 60011, + 'event': 30020, + 'newZones': [24, 39, 124]}, + 60020: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60015, + 'event': 60076, + 'newZones': [16]}, + 60021: {'type': 'visibilityExtender', + 'name': 'beyond door 116', + 'comment': '', + 'parentEntId': 10051, + 'event': 60023, + 'newZones': [6, 118]}, + 60022: {'type': 'visibilityExtender', + 'name': 'beyond door 118', + 'comment': '', + 'parentEntId': 10051, + 'event': 60025, + 'newZones': [23]}, + 60026: {'type': 'visibilityExtender', + 'name': 'beyond door 109', + 'comment': '', + 'parentEntId': 60000, + 'event': 60028, + 'newZones': [7]}, + 60027: {'type': 'visibilityExtender', + 'name': 'beyond door 117', + 'comment': '', + 'parentEntId': 60000, + 'event': 60029, + 'newZones': [8]}, + 60032: {'type': 'visibilityExtender', + 'name': 'intoBoilerRoom', + 'comment': '', + 'parentEntId': 60031, + 'event': 60035, + 'newZones': [8]}, + 60036: {'type': 'visibilityExtender', + 'name': 'beyond door 117', + 'comment': '', + 'parentEntId': 60002, + 'event': 60037, + 'newZones': [8]}, + 60038: {'type': 'visibilityExtender', + 'name': 'beyond door 109', + 'comment': '', + 'parentEntId': 60005, + 'event': 60039, + 'newZones': [7]}, + 60040: {'type': 'visibilityExtender', + 'name': 'beyond door 124', + 'comment': '', + 'parentEntId': 60011, + 'event': 60041, + 'newZones': [38]}, + 60042: {'type': 'visibilityExtender', + 'name': 'beyondWarehouse', + 'comment': '', + 'parentEntId': 60009, + 'event': 60043, + 'newZones': [12, 200]}, + 60045: {'type': 'visibilityExtender', + 'name': 'beyond door 124', + 'comment': '', + 'parentEntId': 60044, + 'event': 60047, + 'newZones': [24]}, + 60046: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60044, + 'event': 10002, + 'newZones': [31]}, + 60048: {'type': 'visibilityExtender', + 'name': 'beyond door 127', + 'comment': '', + 'parentEntId': 60024, + 'event': 60049, + 'newZones': [21, 200]}, + 60050: {'type': 'visibilityExtender', + 'name': 'beyond door 127', + 'comment': '', + 'parentEntId': 60019, + 'event': 60051, + 'newZones': [21, 34, 200]}, + 60052: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60016, + 'event': 60053, + 'newZones': [13, 119]}, + 60054: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60017, + 'event': 60055, + 'newZones': [14, 17, 121]}, + 60056: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60013, + 'event': 60057, + 'newZones': [15, 106]}, + 60058: {'type': 'visibilityExtender', + 'name': 'beyond door 116', + 'comment': '', + 'parentEntId': 60005, + 'event': 60059, + 'newZones': [5]}, + 60060: {'type': 'visibilityExtender', + 'name': 'beyond door 118', + 'comment': '', + 'parentEntId': 60005, + 'event': 60061, + 'newZones': [23]}, + 60062: {'type': 'visibilityExtender', + 'name': 'beyond door 116', + 'comment': '', + 'parentEntId': 60002, + 'event': 60064, + 'newZones': [5]}, + 60063: {'type': 'visibilityExtender', + 'name': 'beyond door 118', + 'comment': '', + 'parentEntId': 60002, + 'event': 60065, + 'newZones': [23]}, + 60068: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60067, + 'event': 30001, + 'newZones': [18, + 19, + 20, + 131]}, + 60069: {'type': 'visibilityExtender', + 'name': 'beyond door 107', + 'comment': '', + 'parentEntId': 60067, + 'event': 30003, + 'newZones': [22]}, + 60070: {'type': 'visibilityExtender', + 'name': 'beyond door 127', + 'comment': '', + 'parentEntId': 60067, + 'event': 10052, + 'newZones': [12, + 21, + 26, + 34, + 35, + 40, + 41, + 53, + 60, + 200]}, + 60071: {'type': 'visibilityExtender', + 'name': 'beyond door 127', + 'comment': '', + 'parentEntId': 60006, + 'event': 10052, + 'newZones': [27, 105, 107]}, + 60072: {'type': 'visibilityExtender', + 'name': 'beyond door 107', + 'comment': '', + 'parentEntId': 60006, + 'event': 60074, + 'newZones': [22]}, + 60073: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60006, + 'event': 60075, + 'newZones': [18]}, + 60077: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60014, + 'event': 60078, + 'newZones': [16]}, + 60079: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60013, + 'event': 60080, + 'newZones': [16]}, + 60081: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60017, + 'event': 60082, + 'newZones': [13]}, + 60083: {'type': 'visibilityExtender', + 'name': 'beyond door 119', + 'comment': '', + 'parentEntId': 60005, + 'event': 60084, + 'newZones': [13]}, + 60085: {'type': 'visibilityExtender', + 'name': 'beyond door 112', + 'comment': '', + 'parentEntId': 60002, + 'event': 60086, + 'newZones': [10]}, + 60087: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60015, + 'event': 60091, + 'newZones': [27]}, + 60089: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60019, + 'event': 60088, + 'newZones': [17]}, + 60090: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60015, + 'event': 60088, + 'newZones': [18, 19, 105]}, + 60092: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60067, + 'event': 60093, + 'newZones': [17]}, + 60097: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60096, + 'event': 60095, + 'newZones': [33, + 34, + 35, + 36, + 37, + 60, + 61, + 128, + 129, + 200]}, + 60098: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60044, + 'event': 60095, + 'newZones': [30]}, + 60099: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60096, + 'event': 60100, + 'newZones': [31]}, + 60106: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60011, + 'event': 60094, + 'newZones': [32]}, + 60107: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60011, + 'event': 60095, + 'newZones': [30]}, + 60109: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60108, + 'event': 60094, + 'newZones': [32]}, + 60110: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60108, + 'event': 60095, + 'newZones': [30]}, + 60112: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60111, + 'event': 60094, + 'newZones': [32]}, + 60113: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60111, + 'event': 60095, + 'newZones': [30]}, + 60115: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60114, + 'event': 60094, + 'newZones': [32]}, + 60116: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60114, + 'event': 60095, + 'newZones': [30]}, + 60117: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60014, + 'event': 60088, + 'newZones': [18]}, + 60120: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60108, + 'event': 10002, + 'newZones': [31]}, + 60122: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60121, + 'event': 10002, + 'newZones': [33, + 34, + 35, + 36, + 37, + 60, + 61, + 128, + 129, + 130, + 200]}, + 60123: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60111, + 'event': 10002, + 'newZones': []}, + 60124: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60114, + 'event': 10002, + 'newZones': [31]}, + 60125: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60011, + 'event': 10002, + 'newZones': [31]}, + 60127: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60126, + 'event': 10002, + 'newZones': [31]}, + 60128: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60126, + 'event': 60094, + 'newZones': [32]}, + 60129: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60126, + 'event': 60095, + 'newZones': [30]}, + 60131: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60130, + 'event': 60094, + 'newZones': [33, + 34, + 35, + 36, + 37, + 60, + 61, + 128, + 130, + 200]}, + 60136: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60044, + 'event': 60094, + 'newZones': [32]}, + 60137: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60096, + 'event': 60138, + 'newZones': [32]}, + 60139: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60121, + 'event': 60141, + 'newZones': [32]}, + 60140: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60121, + 'event': 60142, + 'newZones': [30]}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/SellbotMegaCorpLegCogs.py b/toontown/coghq/SellbotMegaCorpLegCogs.py new file mode 100755 index 00000000..5fabe13f --- /dev/null +++ b/toontown/coghq/SellbotMegaCorpLegCogs.py @@ -0,0 +1,549 @@ +from SpecImports import * +LobbyParent = 10014 +BoilerParent = 10030 +PipeLeftParent = 10023 +PipeRightParent = 10032 +OilParent = 10034 +ControlParent = 10037 +DuctParent = 10036 +PaintMixerStorageParent = 10660 +EastCatwalkParent = 10661 +WestCatwalkParent = 10662 +CenterSiloOutsideBattleCellParent = 10663 +CenterSiloBattleCellParent = 10064 +CenterSiloParent = 20095 +SigRoomParent = 20058 +WestSiloParent = 20094 +WestSiloBattleCellParent = 10047 +EastSiloParent = 20096 +EastSiloBattleCellParent = 10068 +LobbyCell = 0 +BoilerCell = 1 +PipeLeftCell = 2 +PipeRightCell = 3 +OilCell = 4 +ControlCell = 5 +DuctCell = 6 +CenterSiloCell = 7 +SigRoomCell = 8 +WestSiloCell = 9 +EastSiloCell = 10 +CenterSiloOutsideCell = 11 +PaintMixerStorageCell = 12 +EastCatwalkCell = 13 +WestCatwalkCell = 14 +BattleCells = {LobbyCell: {'parentEntId': LobbyParent, + 'pos': Point3(0, 0, 0)}, + BoilerCell: {'parentEntId': BoilerParent, + 'pos': Point3(0, 0, 0)}, + OilCell: {'parentEntId': OilParent, + 'pos': Point3(0, 0, 0)}, + ControlCell: {'parentEntId': ControlParent, + 'pos': Point3(0, 0, 0)}, + CenterSiloCell: {'parentEntId': CenterSiloBattleCellParent, + 'pos': Point3(0, 0, 0)}, + PipeLeftCell: {'parentEntId': PipeLeftParent, + 'pos': Point3(0, 0, 0)}, + PipeRightCell: {'parentEntId': PipeRightParent, + 'pos': Point3(0, 0, 0)}, + DuctCell: {'parentEntId': DuctParent, + 'pos': Point3(0, 0, 0)}, + SigRoomCell: {'parentEntId': SigRoomParent, + 'pos': Point3(0, 0, 0)}, + WestSiloCell: {'parentEntId': WestSiloBattleCellParent, + 'pos': Point3(0, 0, 0)}, + EastSiloCell: {'parentEntId': EastSiloBattleCellParent, + 'pos': Point3(-20, -10, 0)}, + CenterSiloOutsideCell: {'parentEntId': CenterSiloOutsideBattleCellParent, + 'pos': Point3(0, 0, 0)}, + PaintMixerStorageCell: {'parentEntId': PaintMixerStorageParent, + 'pos': Point3(0, 0, 0)}, + EastCatwalkCell: {'parentEntId': EastCatwalkParent, + 'pos': Point3(0, 0, 0)}, + WestCatwalkCell: {'parentEntId': WestCatwalkParent, + 'pos': Point3(0, 0, 0)}} +CogData = [{'type': 'm', + 'parentEntId': LobbyParent, + 'boss': 0, + 'level': 8, + 'battleCell': LobbyCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20078, + 'skeleton': 0, + 'revives': 1}, + {'type': 'tf', + 'parentEntId': LobbyParent, + 'boss': 0, + 'level': 9, + 'battleCell': LobbyCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20009, + 'skeleton': 0, + 'revives': 1}, + {'type': 'tf', + 'parentEntId': LobbyParent, + 'boss': 0, + 'level': 7, + 'battleCell': LobbyCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20079, + 'skeleton': 0, + 'revives': 1}, + {'type': 'tf', + 'parentEntId': BoilerParent, + 'boss': 0, + 'level': 8, + 'battleCell': BoilerCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'walk', + 'path': 20076, + 'skeleton': 0, + 'revives': 1}, + {'type': 'm', + 'parentEntId': BoilerParent, + 'boss': 0, + 'level': 10, + 'battleCell': BoilerCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'walk', + 'path': 20077, + 'skeleton': 0, + 'revives': 1}, + {'type': 'tf', + 'parentEntId': BoilerParent, + 'boss': 0, + 'level': 7, + 'battleCell': BoilerCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'm', + 'parentEntId': OilParent, + 'boss': 0, + 'level': 10, + 'battleCell': OilCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 60133, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': OilParent, + 'boss': 0, + 'level': 9, + 'battleCell': OilCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 60134, + 'skeleton': 0, + 'revives': 1}, + {'type': 'gh', + 'parentEntId': OilParent, + 'boss': 0, + 'level': 8, + 'battleCell': OilCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 60135, + 'skeleton': 0, + 'revives': 1}, + {'type': 'gh', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 8, + 'battleCell': ControlCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20039, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 9, + 'battleCell': ControlCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20049, + 'skeleton': 0, + 'revives': 1}, + {'type': 'tf', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 9, + 'battleCell': ControlCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20075, + 'skeleton': 0, + 'revives': 1}, + {'type': 'm', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 11, + 'battleCell': CenterSiloCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20103, + 'skeleton': 0, + 'revives': 1}, + {'type': 'mh', + 'parentEntId': CenterSiloParent, + 'boss': 1, + 'level': 12, + 'battleCell': CenterSiloCell, + 'pos': Point3(0, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'tf', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 10, + 'battleCell': CenterSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20104, + 'skeleton': 0, + 'revives': 1}, + {'type': 'm', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 11, + 'battleCell': CenterSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20105, + 'skeleton': 0, + 'revives': 1}, + {'type': 'mh', + 'parentEntId': WestSiloParent, + 'boss': 0, + 'level': 9, + 'battleCell': WestSiloCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20097, + 'skeleton': 0, + 'revives': 1}, + {'type': 'tf', + 'parentEntId': WestSiloParent, + 'boss': 0, + 'level': 10, + 'battleCell': WestSiloCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20098, + 'skeleton': 0, + 'revives': 1}, + {'type': 'm', + 'parentEntId': WestSiloParent, + 'boss': 0, + 'level': 11, + 'battleCell': WestSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20099, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': EastSiloParent, + 'boss': 0, + 'level': 9, + 'battleCell': EastSiloCell, + 'pos': Point3(0, -5, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20100, + 'skeleton': 0, + 'revives': 1}, + {'type': 'mh', + 'parentEntId': EastSiloParent, + 'boss': 0, + 'level': 10, + 'battleCell': EastSiloCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20101, + 'skeleton': 0, + 'revives': 1}, + {'type': 'm', + 'parentEntId': EastSiloParent, + 'boss': 0, + 'level': 11, + 'battleCell': EastSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20102, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': PipeLeftParent, + 'boss': 0, + 'level': 9, + 'battleCell': PipeLeftCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20109, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': PipeLeftParent, + 'boss': 0, + 'level': 8, + 'battleCell': PipeLeftCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20110, + 'skeleton': 0, + 'revives': 1}, + {'type': 'gh', + 'parentEntId': PipeLeftParent, + 'boss': 0, + 'level': 8, + 'battleCell': PipeLeftCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20111, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': PipeRightParent, + 'boss': 0, + 'level': 9, + 'battleCell': PipeRightCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20106, + 'skeleton': 0, + 'revives': 1}, + {'type': 'gh', + 'parentEntId': PipeRightParent, + 'boss': 0, + 'level': 8, + 'battleCell': PipeRightCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20107, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': PipeRightParent, + 'boss': 0, + 'level': 7, + 'battleCell': PipeRightCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20108, + 'skeleton': 0, + 'revives': 1}, + {'type': 'mh', + 'parentEntId': DuctParent, + 'boss': 0, + 'level': 10, + 'battleCell': DuctCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20038, + 'skeleton': 0, + 'revives': 1}, + {'type': 'mh', + 'parentEntId': DuctParent, + 'boss': 0, + 'level': 10, + 'battleCell': DuctCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20067, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': DuctParent, + 'boss': 0, + 'level': 8, + 'battleCell': DuctCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20068, + 'skeleton': 0, + 'revives': 1}, + {'type': 'm', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 11, + 'battleCell': SigRoomCell, + 'pos': Point3(5, -10.75, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'tf', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 8, + 'battleCell': SigRoomCell, + 'pos': Point3(5, -3.25, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'm', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 11, + 'battleCell': SigRoomCell, + 'pos': Point3(5, 3.25, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'gh', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 8, + 'battleCell': SigRoomCell, + 'pos': Point3(5, 10.75, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': CenterSiloOutsideBattleCellParent, + 'boss': 0, + 'level': 9, + 'battleCell': CenterSiloOutsideCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId':CenterSiloOutsideBattleCellParent, + 'boss': 0, + 'level': 9, + 'battleCell': CenterSiloOutsideCell, + 'pos': Point3(32.86, -21.97, 0), + 'h': -119, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'm', + 'parentEntId': CenterSiloOutsideBattleCellParent, + 'boss': 0, + 'level': 9, + 'battleCell': CenterSiloOutsideCell, + 'pos': Point3(-28.6, -28.2, 0), + 'h': 111.9, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'm', + 'parentEntId': CenterSiloOutsideBattleCellParent, + 'boss': 0, + 'level': 11, + 'battleCell': CenterSiloOutsideCell, + 'pos': Point3(0, 27, 0), + 'h': 0, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': PaintMixerStorageParent, + 'boss': 0, + 'level': 9, + 'battleCell': PaintMixerStorageCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': EastCatwalkParent, + 'boss': 0, + 'level': 9, + 'battleCell': EastCatwalkCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': EastCatwalkParent, + 'boss': 0, + 'level': 9, + 'battleCell': EastCatwalkCell, + 'pos': Point3(0, -5, 0), + 'h': 0, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': WestCatwalkParent, + 'boss': 0, + 'level': 9, + 'battleCell': WestCatwalkCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}, + {'type': 'ms', + 'parentEntId': WestCatwalkParent, + 'boss': 0, + 'level': 9, + 'battleCell': WestCatwalkCell, + 'pos': Point3(0, -5, 0), + 'h': 0, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0, + 'revives': 1}] +ReserveCogData = [{}] diff --git a/toontown/coghq/SellbotMegaCorpLegSpec.py b/toontown/coghq/SellbotMegaCorpLegSpec.py new file mode 100755 index 00000000..232feadb --- /dev/null +++ b/toontown/coghq/SellbotMegaCorpLegSpec.py @@ -0,0 +1,4815 @@ +from toontown.toonbase import TTLocalizer +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500.0, + 'modelFilename': 'phase_9/models/cogHQ/SelbotLegFactory', + 'wantDoors': 1}, + 0: {'type': 'zone', + 'name': 'UberZone', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 3: {'type': 'zone', + 'name': 'Main Entrance', + 'comment': '', + 'parentEntId': 0, + 'scale': Vec3(1, 1, 1), + 'description': TTLocalizer.SellbotLegFactorySpecMainEntrance, + 'visibility': [114]}, + 4: {'type': 'zone', + 'name': 'Lobby', + 'comment': '', + 'parentEntId': 0, + 'scale': Vec3(1, 1, 1), + 'description': TTLocalizer.SellbotLegFactorySpecLobby, + 'visibility': [113, 114]}, + 5: {'type': 'zone', + 'name': 'hallwayFromLobby', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [113, 116]}, + 6: {'type': 'zone', + 'name': 'hallwayToBoiler/Control/Lookout', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLobbyHallway, + 'visibility': [109, + 116, + 117, + 118]}, + 7: {'type': 'zone', + 'name': 'GearRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecGearRoom, + 'visibility': [109, 110]}, + 8: {'type': 'zone', + 'name': 'BoilerRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecBoilerRoom, + 'visibility': [108, 117]}, + 9: {'type': 'zone', + 'name': 'EastCatwalk', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastCatwalk, + 'visibility': [23, + 25, + 26, + 33, + 34, + 35, + 38, + 41, + 53, + 110, + 112, + 115, + 124, + 200, + 222]}, + 10: {'type': 'zone', + 'name': 'PaintMixer', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecPaintMixer, + 'visibility': [11, 111, 112]}, + 11: {'type': 'zone', + 'name': 'PaintMixerRewardRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecPaintMixerStorageRoom, + 'visibility': [10, 111, 112]}, + 12: {'type': 'zone', + 'name': 'WestSiloCatwalk', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestSiloCatwalk, + 'visibility': [21, + 26, + 33, + 34, + 35, + 36, + 37, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 119, + 120, + 125, + 127, + 128, + 129, + 130, + 200]}, + 13: {'type': 'zone', + 'name': 'PipeRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecPipeRoom, + 'visibility': [119, 121]}, + 14: {'type': 'zone', + 'name': 'StairsToPipeRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [17, + 18, + 121, + 126, + 131]}, + 15: {'type': 'zone', + 'name': 'DuctRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecDuctRoom, + 'visibility': [106, 126]}, + 16: {'type': 'zone', + 'name': 'Side Entrance', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecSideEntrance, + 'visibility': [106]}, + 17: {'type': 'zone', + 'name': 'StomperAlley', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecStomperAlley, + 'visibility': [14, + 121, + 126, + 131]}, + 18: {'type': 'zone', + 'name': 'LavaRoomFoyer', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLavaRoomFoyer, + 'visibility': [19, + 20, + 102, + 103, + 105, + 131]}, + 19: {'type': 'zone', + 'name': 'LavaRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLavaRoom, + 'visibility': [17, + 18, + 20, + 105, + 131]}, + 20: {'type': 'zone', + 'name': 'LavaRewardRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLavaStorageRoom, + 'visibility': [18, 19, 105]}, + 21: {'type': 'zone', + 'name': 'WestCatwalk', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestCatwalk, + 'visibility': [12, + 23, + 26, + 33, + 34, + 35, + 40, + 41, + 53, + 60, + 108, + 119, + 120, + 125, + 127, + 200]}, + 22: {'type': 'zone', + 'name': 'OilRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecOilRoom, + 'visibility': [107]}, + 23: {'type': 'zone', + 'name': 'Lookout', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLookout, + 'visibility': [24, + 39, + 115, + 118, + 120, + 123, + 124, + 125]}, + 24: {'type': 'zone', + 'name': 'Warehouse', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWarehouse, + 'visibility': [23, + 39, + 115, + 120, + 123, + 124, + 125]}, + 25: {'type': 'zone', + 'name': 'PaintMixerExterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 26: {'type': 'zone', + 'name': 'WarehouseExterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 27: {'type': 'zone', + 'name': 'OilRoomHallway', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecOilRoomHallway, + 'visibility': [105, 107, 127]}, + 30: {'type': 'zone', + 'name': 'EastSiloControlRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastSiloControlRoom, + 'visibility': [130]}, + 31: {'type': 'zone', + 'name': 'WestSiloControlRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestSiloControlRoom, + 'visibility': [128]}, + 32: {'type': 'zone', + 'name': 'CenterSiloControlRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecCenterSiloControlRoom, + 'visibility': [129]}, + 33: {'type': 'zone', + 'name': 'EastSilo', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastSilo, + 'visibility': [9, + 12, + 21, + 25, + 26, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 124, + 128, + 129, + 130, + 200, + 222]}, + 34: {'type': 'zone', + 'name': 'WestSilo', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestSilo, + 'visibility': [9, + 12, + 21, + 25, + 26, + 33, + 35, + 36, + 37, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 120, + 125, + 127, + 128, + 129, + 130, + 200]}, + 35: {'type': 'zone', + 'name': 'CenterSilo', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecCenterSilo, + 'visibility': [9, + 21, + 25, + 26, + 33, + 34, + 36, + 37, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 128, + 129, + 130, + 200]}, + 36: {'type': 'zone', + 'name': 'WestSiloBridge', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [9, + 12, + 21, + 25, + 26, + 33, + 34, + 35, + 36, + 37, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 127, + 128, + 129, + 130, + 200]}, + 37: {'type': 'zone', + 'name': 'EastSiloBridge', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [9, + 12, + 21, + 25, + 26, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 128, + 129, + 130, + 200, + 222]}, + 38: {'type': 'zone', + 'name': 'EastSiloCatwalk', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastSiloCatwalk, + 'visibility': [9, + 25, + 26, + 33, + 34, + 35, + 36, + 37, + 41, + 53, + 60, + 110, + 112, + 115, + 124, + 200, + 222]}, + 39: {'type': 'zone', + 'name': 'WarehouseCeiling', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 40: {'type': 'zone', + 'name': 'WestExterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 41: {'type': 'zone', + 'name': 'EastExterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 53: {'type': 'zone', + 'name': 'ExteriorFloor', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 60: {'type': 'zone', + 'name': 'WestElevatorShaft', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestElevatorShaft, + 'visibility': [12, 34]}, + 61: {'type': 'zone', + 'name': 'EastElevatorShaft', + 'comment': 'no geom or DCS', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastElevatorShaft, + 'visibility': [33, 38]}, + 101: {'type': 'zone', + 'name': 'dwToLavaRewardRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 102: {'type': 'zone', + 'name': 'dwToLavaRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 103: {'type': 'zone', + 'name': 'dwToLavaRoomHallway', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 105: {'type': 'zone', + 'name': 'dwToOilRoomCatwalks', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 106: {'type': 'zone', + 'name': 'dwToDuctRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 107: {'type': 'zone', + 'name': 'dwToOilRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 108: {'type': 'zone', + 'name': 'dwFromBoilerRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 109: {'type': 'zone', + 'name': 'dwToGearRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110: {'type': 'zone', + 'name': 'dwFromGearRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 111: {'type': 'zone', + 'name': 'dwToPaintMixerRewardRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 112: {'type': 'zone', + 'name': 'dwToPaintMixer', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 113: {'type': 'zone', + 'name': 'dwFromLobby', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 114: {'type': 'zone', + 'name': 'dwToLobby', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 115: {'type': 'zone', + 'name': 'dwToWarehouseFromRight', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 116: {'type': 'zone', + 'name': 'dwFromLobbyFar', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 117: {'type': 'zone', + 'name': 'dwToBoilerRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 118: {'type': 'zone', + 'name': 'dwToLookout', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 119: {'type': 'zone', + 'name': 'dwFromPipeRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 120: {'type': 'zone', + 'name': 'dwToWarehouseFromLeft', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 121: {'type': 'zone', + 'name': 'dwToPipeRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 122: {'type': 'zone', + 'name': 'dwToWarehouseControlRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 123: {'type': 'zone', + 'name': 'dwFromWarehouseFloor', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 124: {'type': 'zone', + 'name': 'dwFromWarehouseRight', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 125: {'type': 'zone', + 'name': 'dwFromWarehouseLeft', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 126: {'type': 'zone', + 'name': 'dwFromDuctRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 127: {'type': 'zone', + 'name': 'dwFromOilRoomHallway', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 128: {'type': 'zone', + 'name': 'dwToWestSiloRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 129: {'type': 'zone', + 'name': 'dwToCenterSiloRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 130: {'type': 'zone', + 'name': 'dwToEastSiloRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 131: {'type': 'zone', + 'name': 'dwFromStomperAlley', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 200: {'type': 'zone', + 'name': 'sky', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 201: {'type': 'zone', + 'name': 'extraZone201', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 202: {'type': 'zone', + 'name': 'extraZone202', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 203: {'type': 'zone', + 'name': 'extraZone203', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 204: {'type': 'zone', + 'name': 'extraZone204', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 205: {'type': 'zone', + 'name': 'extraZone205', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 206: {'type': 'zone', + 'name': 'extraZone206', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 207: {'type': 'zone', + 'name': 'extraZone207', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 208: {'type': 'zone', + 'name': 'extraZone208', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 209: {'type': 'zone', + 'name': 'extraZone209', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 210: {'type': 'zone', + 'name': 'extraZone210', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 211: {'type': 'zone', + 'name': 'extraZone211', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 212: {'type': 'zone', + 'name': 'extraZone212', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 213: {'type': 'zone', + 'name': 'extraZone213', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 214: {'type': 'zone', + 'name': 'extraZone214', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 215: {'type': 'zone', + 'name': 'extraZone215', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 216: {'type': 'zone', + 'name': 'extraZone216', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 217: {'type': 'zone', + 'name': 'extraZone217', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 218: {'type': 'zone', + 'name': 'extraZone218', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 219: {'type': 'zone', + 'name': 'extraZone219', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 220: {'type': 'zone', + 'name': 'extraZone220', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 221: {'type': 'zone', + 'name': 'extraZone221', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 222: {'type': 'zone', + 'name': 'dwToEastSiloInterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10010: {'type': 'ambientSound', + 'name': 'westWind', + 'comment': '', + 'parentEntId': 35, + 'pos': Point3(-52.7549, -38.8374, 53.3758), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'enabled': 1, + 'soundPath': 'phase_9/audio/sfx/CHQ_FACT_whistling_wind.ogg', + 'volume': 1}, + 10016: {'type': 'ambientSound', + 'name': 'sndConveyorBelt', + 'comment': '', + 'parentEntId': 10056, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'enabled': 1, + 'soundPath': 'phase_9/audio/sfx/CHQ_FACT_conveyor_belt.ogg', + 'volume': 0.5}, + 10053: {'type': 'ambientSound', + 'name': 'eastWind', + 'comment': '', + 'parentEntId': 35, + 'pos': Point3(52.75, -38.84, 53.38), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'enabled': 1, + 'soundPath': 'phase_9/audio/sfx/CHQ_FACT_whistling_wind.ogg', + 'volume': 1}, + 10055: {'type': 'ambientSound', + 'name': 'sndGears', + 'comment': '', + 'parentEntId': 10056, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'enabled': 1, + 'soundPath': 'phase_9/audio/sfx/CHQ_FACT_gears_turning.ogg', + 'volume': 1}, + 10031: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 8, + 'pos': Point3(-1, 79, 10), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.75, 1, 1), + 'cellId': 1, + 'radius': 10.0}, + 10035: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 4, + 'radius': 10.0}, + 10038: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(0, -28.04, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 5, + 'radius': 10.0}, + 20048: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0.973602, 71.7, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 0.2, 1), + 'cellId': 0, + 'radius': 15.0}, + 20063: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 20033, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 8, + 'radius': 1}, + 20064: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 20034, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 8, + 'radius': 1}, + 20065: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 20035, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 8, + 'radius': 1}, + 20066: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 20036, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 8, + 'radius': 1}, + 20086: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 33, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1, 1), + 'cellId': 6, + 'radius': 12.0}, + 20112: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(-10.0936, -9.55975, 4), + 'hpr': Point3(45, 0, 0), + 'scale': Point3(10, 1, 5), + 'cellId': 10, + 'radius': 5.0}, + 20113: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(9.08399, 4.42157, 0), + 'hpr': Point3(-50, 0, 0), + 'scale': Point3(10, 2, 6), + 'cellId': 9, + 'radius': 5.0}, + 20114: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 60103, + 'pos': Point3(0, 0, 1), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 0.5), + 'cellId': 8, + 'radius': 3.0}, + 10003: {'type': 'beanBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(1.25458, 19.2471, 0.0249529), + 'hpr': Vec3(-8.28434, 0, 0), + 'scale': 1, + 'rewardPerGrab': 100, + 'rewardPerGrabMax': 250}, + 10011: {'type': 'beanBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(16.344, -9.73, 0.025), + 'hpr': Vec3(-79.8888, 0, 0), + 'scale': 1, + 'rewardPerGrab': 100, + 'rewardPerGrabMax': 250}, + 20017: {'type': 'beanBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(20.0035, 2.94232, 0), + 'hpr': Vec3(-31.6033, 0, 0), + 'scale': 1, + 'rewardPerGrab': 70, + 'rewardPerGrabMax': 120}, + 10039: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(-7, 29, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(4, 4, 4), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 20033: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(0.862745, 0.517647, 0.0941177, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1}, + 20034: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(7.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(0.862745, 0.517647, 0.0941177, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1}, + 20035: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(15, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(0.862745, 0.517647, 0.0941177, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1}, + 20036: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(22.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(0.862745, 0.517647, 0.0941177, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1}, + 30040: {'type': 'button', + 'name': 'door button', + 'comment': 'Entrance door unlock', + 'parentEntId': 3, + 'pos': Point3(0, 6.75, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 30076: {'type': 'button', + 'name': 'open door 113', + 'comment': 'Lobby door unlock', + 'parentEntId': 4, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1}, + 60102: {'type': 'button', + 'name': 'door button', + 'comment': 'Entrance Door Unlock', + 'parentEntId': 16, + 'pos': Point3(4, 8, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 60103: {'type': 'button', + 'name': 'door button', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(25, -7, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(4, 4, 4), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 60104: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 31, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(5, 5, 4), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 60105: {'type': 'button', + 'name': 'door button', + 'comment': '', + 'parentEntId': 30, + 'pos': Point3(-4, 7, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(5, 5, 4), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 60118: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 10005: {'type': 'conveyorBelt', + 'name': 'belt', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 45.2024, 7.24937), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'floorName': 'platformcollision', + 'length': 78.81881352704218, + 'speed': 2.0, + 'treadLength': 10.0, + 'treadModelPath': 'phase_9/models/cogHQ/platform1', + 'widthScale': 0.85}, + 20081: {'type': 'crate', + 'name': '', + 'comment': '', + 'parentEntId': 20080, + 'pos': Point3(0, 0, 0), + 'scale': 0.920000016689, + 'crushCellId': None, + 'gridId': 20080, + 'modelType': 0, + 'pushable': 1}, + 20091: {'type': 'crate', + 'name': '', + 'comment': '', + 'parentEntId': 20090, + 'pos': Point3(0, 23, 0), + 'scale': 0.920000016689, + 'crushCellId': None, + 'gridId': 20090, + 'modelType': 0, + 'pushable': 1}, + 20024: {'type': 'crusherCell', + 'name': '', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'col': 1, + 'gridId': 20025, + 'row': 14}, + 20026: {'type': 'crusherCell', + 'name': '', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'col': 10, + 'gridId': 20025, + 'row': 14}, + 20027: {'type': 'crusherCell', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(1, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'col': 21, + 'gridId': 20025, + 'row': 14}, + 20028: {'type': 'crusherCell', + 'name': 'copy of copy of ', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(2, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'col': 28, + 'gridId': 20025, + 'row': 14}, + 30078: {'type': 'cutScene', + 'name': 'button door', + 'comment': '', + 'parentEntId': 114, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'duration': 4.0, + 'effect': 'irisInOut', + 'motion': 'doorUnlock', + 'startStopEvent': 30077}, + 10002: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 128, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 10052: {'type': 'door', + 'name': 'door 127', + 'comment': '', + 'parentEntId': 127, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 10039, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30000: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 114, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60132, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30001: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 105, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1.0, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30002: {'type': 'door', + 'name': 'door 106', + 'comment': '', + 'parentEntId': 106, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60132, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30003: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 107, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30004: {'type': 'door', + 'name': 'doorFromBoilerRoom', + 'comment': '', + 'parentEntId': 108, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30005: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 109, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30006: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 110, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30008: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 112, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30009: {'type': 'door', + 'name': 'door 113', + 'comment': '', + 'parentEntId': 113, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60119, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30010: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 115, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30011: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 116, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30012: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 117, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30013: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 118, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30014: {'type': 'door', + 'name': 'doorFromPipeRoom 119', + 'comment': '', + 'parentEntId': 119, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30015: {'type': 'door', + 'name': 'door 120', + 'comment': '', + 'parentEntId': 120, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30016: {'type': 'door', + 'name': 'door 121', + 'comment': '', + 'parentEntId': 121, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30017: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 122, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30018: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 123, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 0, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60103, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30019: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 124, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30020: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 125, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30021: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 126, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60119, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 60088: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 131, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1.0, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 60094: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 129, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 0, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1.0, + 'unlock0Event': 0, + 'unlock1Event': 60104, + 'unlock2Event': 60105, + 'unlock3Event': 0}, + 60095: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 130, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 60101: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 222, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 10049: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 3}, + 10051: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 4}, + 60000: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 5}, + 60001: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 6}, + 60002: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 7}, + 60003: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 9}, + 60004: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 10}, + 60005: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 8}, + 60006: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 21}, + 60007: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 24}, + 60009: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 38}, + 60011: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 12}, + 60013: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 13}, + 60014: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 14}, + 60015: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 17}, + 60016: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 15}, + 60017: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 16}, + 60018: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 19}, + 60019: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 18}, + 60024: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 22}, + 60031: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 23}, + 60044: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 33}, + 60066: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 11}, + 60067: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 27}, + 60096: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 30}, + 60108: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 34}, + 60111: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 36}, + 60114: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 37}, + 60121: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 31}, + 60126: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 35}, + 60130: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 32}, + 10028: {'type': 'entrancePoint', + 'name': 'entrance1', + 'comment': '', + 'parentEntId': 3, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'entranceId': 0, + 'radius': 15, + 'theta': 20}, + 10029: {'type': 'entrancePoint', + 'name': 'entrance2', + 'comment': '', + 'parentEntId': 16, + 'pos': Point3(0, 10, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'entranceId': 1, + 'radius': 15, + 'theta': 20}, + 10330: {'type': 'entrancePoint', + 'name': 'entrance3', + 'comment': '', + 'parentEntId': 3, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'entranceId': 2, + 'radius': 15, + 'theta': 20}, + 10021: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-2.02081, 0, 0), + 'hpr': Vec3(337.477, 0, 0), + 'scale': 1, + 'gagLevel': 4, + 'gagLevelMax': 0, + 'gagTrack': 0, + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 10024: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(20.3012, -26.3219, 0), + 'hpr': Vec3(233.187, 0, 0), + 'scale': 1, + 'gagLevel': 4, + 'gagLevelMax': 0, + 'gagTrack': 4, + 'rewardPerGrab': 4, + 'rewardPerGrabMax': 0}, + 10025: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-47.312, 7.22571, 0), + 'hpr': Vec3(19.1524, 0, 0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 2, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10026: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-11.2037, 5.43514, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 5, + 'rewardPerGrab': 4, + 'rewardPerGrabMax': 0}, + 10227: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(23.59, 4.83, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 1, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 20020: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(-23.0209, 0, 0), + 'hpr': Vec3(126.676, 0, 0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 3, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 20021: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(-31.3225, 14.1021, 0), + 'hpr': Vec3(-136.57, 0, 0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 1, + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 20085: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 5, + 'pos': Point3(3.14, 12.6703, 10.12), + 'hpr': Vec3(-24.8105, 0, 0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 4, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 20093: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20087, + 'pos': Point3(2.4, -1, 7), + 'hpr': Vec3(-151.532, 0, 0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 6, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10006: {'type': 'gear', + 'name': 'first', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 0, 26.0634), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'degreesPerSec': 37.0, + 'gearScale': 25.0, + 'modelType': 'factory', + 'orientation': 'vertical', + 'phaseShift': 0}, + 10007: {'type': 'gear', + 'name': 'second', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 15, 26.06), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'degreesPerSec': 40.0, + 'gearScale': 25.0, + 'modelType': 'factory', + 'orientation': 'vertical', + 'phaseShift': 0}, + 10008: {'type': 'gear', + 'name': 'third', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 30, 26.06), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'degreesPerSec': 43.0, + 'gearScale': 25.0, + 'modelType': 'factory', + 'orientation': 'vertical', + 'phaseShift': 0}, + 10009: {'type': 'gear', + 'name': 'fourth', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 45, 26.06), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'degreesPerSec': 47.0, + 'gearScale': 25.0, + 'modelType': 'factory', + 'orientation': 'vertical', + 'phaseShift': 0}, + 20013: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20012, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 2.25, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 20014: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20010, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 2.25, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 20016: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20015, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 2.25, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 20041: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20040, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 2.5, + 'attackRadius': 15, + 'crushCellId': 20026, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 80.0, + 'strength': 20, + 'velocity': 6.0}, + 20043: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20042, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 2.25, + 'attackRadius': 15, + 'crushCellId': 20024, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 80.0, + 'strength': 15, + 'velocity': 5.0}, + 20046: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20044, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 2.4, + 'attackRadius': 15, + 'crushCellId': 20024, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 70, + 'strength': 17, + 'velocity': 6.0}, + 20047: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20045, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.0, + 'attackRadius': 15, + 'crushCellId': 20026, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 80.0, + 'strength': 6, + 'velocity': 8.0}, + 20052: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20051, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 12.0, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4.0}, + 20054: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20053, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20027, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 80.0, + 'strength': 10, + 'velocity': 5.5}, + 20056: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20055, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20028, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 70, + 'strength': 10, + 'velocity': 6.0}, + 20060: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20059, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20028, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 90.0, + 'strength': 10, + 'velocity': 6.5}, + 20062: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20061, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20027, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 70, + 'strength': 20, + 'velocity': 7.5}, + 20071: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20070, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 30, + 'velocity': 3.0}, + 20072: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20069, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 2.0, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 80.0, + 'strength': 12, + 'velocity': 8.0}, + 20074: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20073, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.25, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 21, + 'velocity': 4}, + 20089: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20084, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 3, + 'velocity': 8}, + 20115: {'type': 'goonClipPlane', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, -7.4, 0), + 'hpr': Point3(-90, 0, 0), + 'scale': Point3(5, 5, 5), + 'goonId': 20052}, + 20116: {'type': 'goonClipPlane', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, -58, 0), + 'hpr': Point3(90, 0, 0), + 'scale': 1, + 'goonId': None}, + 20117: {'type': 'goonClipPlane', + 'name': '', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(0, -29, 0), + 'hpr': Point3(90, 0, 0), + 'scale': 1, + 'goonId': None}, + 20118: {'type': 'goonClipPlane', + 'name': '', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(-52, 0, 5), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'goonId': None}, + 20025: {'type': 'grid', + 'name': '', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(-48.4442, -24.9385, 0), + 'scale': 1, + 'cellSize': 3, + 'numCol': 30, + 'numRow': 16}, + 20080: {'type': 'grid', + 'name': '', + 'comment': '', + 'parentEntId': 5, + 'pos': Point3(1.5, -10.7, 0), + 'scale': 1, + 'cellSize': 3, + 'numCol': 2, + 'numRow': 5}, + 20090: {'type': 'grid', + 'name': '', + 'comment': '', + 'parentEntId': 17, + 'pos': Point3(-6.5, -111, 0), + 'scale': 1, + 'cellSize': 3, + 'numCol': 2, + 'numRow': 9}, + 20011: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(-2.06235, 20.2198, 0.025), + 'hpr': Vec3(-19.2153, 0, 0), + 'scale': 1, + 'rewardPerGrab': 10, + 'rewardPerGrabMax': 0}, + 20092: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20087, + 'pos': Point3(-1, -1.5, 7), + 'hpr': Vec3(-191.79, 0, 0), + 'scale': 1, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10041: {'type': 'lift', + 'name': 'westLift', + 'comment': '', + 'parentEntId': 60, + 'pos': Point3(0, 0, 0.0641994), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'autoMoveDelay': 5, + 'duration': 7.0, + 'endBoardSides': ['back'], + 'endGuardName': 'topGuard', + 'endPos': Point3(0, 0, 165), + 'floorName': 'elevator_floor', + 'modelPath': 'phase_9/models/cogHQ/Elevator.bam', + 'modelScale': Vec3(1, 1, 1), + 'moveDelay': 1, + 'startBoardSides': ['front'], + 'startGuardName': 'bottomGuard', + 'startPos': Point3(0, 0, 0)}, + 10048: {'type': 'lift', + 'name': 'eastLift', + 'comment': '', + 'parentEntId': 61, + 'pos': Point3(0, -0.684064, 0.589322), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'autoMoveDelay': 5.0, + 'duration': 7.0, + 'endBoardSides': ['front', + 'back', + 'left', + 'right'], + 'endGuardName': 'topGuard', + 'endPos': Point3(0, 0, 165), + 'floorName': 'elevator_floor', + 'modelPath': 'phase_9/models/cogHQ/Elevator.bam', + 'modelScale': Vec3(1, 1, 1), + 'moveDelay': 1, + 'startBoardSides': ['front'], + 'startGuardName': 'bottomGuard', + 'startPos': Point3(0, 0, 0)}, + 10057: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 10043, + 'input1Event': 30009, + 'input2Event': 30000, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 10059: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 10058, + 'input1Event': 10057, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 10061: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 10060, + 'input1Event': 10059, + 'input2Event': 30013, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 10063: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 10062, + 'input1Event': 60033, + 'input2Event': 30009, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 30068: {'type': 'logicGate', + 'name': 'door 116 and door 118', + 'comment': '', + 'parentEntId': 30069, + 'input1Event': 30013, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60023: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60021, + 'input1Event': 30011, + 'input2Event': 30009, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60025: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60022, + 'input1Event': 60023, + 'input2Event': 30013, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60028: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60026, + 'input1Event': 30011, + 'input2Event': 30005, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60029: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60027, + 'input1Event': 30011, + 'input2Event': 30012, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60030: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 30071, + 'input1Event': 30011, + 'input2Event': 30009, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60033: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 30073, + 'input1Event': 30013, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60034: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 30075, + 'input1Event': 30013, + 'input2Event': 30005, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60035: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60032, + 'input1Event': 30013, + 'input2Event': 30012, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60037: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60036, + 'input1Event': 30005, + 'input2Event': 30012, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60039: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60038, + 'input1Event': 30012, + 'input2Event': 30005, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60041: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60040, + 'input1Event': 30020, + 'input2Event': 30019, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60043: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60042, + 'input1Event': 30019, + 'input2Event': 30020, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60047: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60045, + 'input1Event': 10002, + 'input2Event': 30019, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60049: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60048, + 'input1Event': 30003, + 'input2Event': 10052, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60051: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60050, + 'input1Event': 30001, + 'input2Event': 10052, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60053: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60052, + 'input1Event': 30021, + 'input2Event': 30016, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60055: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60054, + 'input1Event': 30002, + 'input2Event': 30021, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60057: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60056, + 'input1Event': 30016, + 'input2Event': 30021, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60059: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60058, + 'input1Event': 30012, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60061: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60060, + 'input1Event': 30012, + 'input2Event': 30013, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60064: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60062, + 'input1Event': 30005, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60065: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60063, + 'input1Event': 30005, + 'input2Event': 30013, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60074: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60072, + 'input1Event': 10052, + 'input2Event': 30003, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60075: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60073, + 'input1Event': 10052, + 'input2Event': 30001, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60076: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60020, + 'input1Event': 30021, + 'input2Event': 30002, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60078: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60077, + 'input1Event': 30021, + 'input2Event': 30002, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60080: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60079, + 'input1Event': 60057, + 'input2Event': 30002, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60082: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60081, + 'input1Event': 60055, + 'input2Event': 30016, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60084: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60083, + 'input1Event': 30004, + 'input2Event': 30014, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60086: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60085, + 'input1Event': 30006, + 'input2Event': 30008, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60091: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60087, + 'input1Event': 60088, + 'input2Event': 30001, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60093: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60092, + 'input1Event': 30001, + 'input2Event': 60088, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60100: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60099, + 'input1Event': 60095, + 'input2Event': 10002, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60119: {'type': 'logicGate', + 'name': 'open sesame Duct & Lobby', + 'comment': 'links together the Duct Room and Lobby buttons', + 'parentEntId': 0, + 'input1Event': 30076, + 'input2Event': 60118, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'or'}, + 60132: {'type': 'logicGate', + 'name': 'open sesame Entrances', + 'comment': 'links together the buttons in the two entrances', + 'parentEntId': 0, + 'input1Event': 30040, + 'input2Event': 60102, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'or'}, + 60138: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60137, + 'input1Event': 60095, + 'input2Event': 60094, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60141: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60139, + 'input1Event': 10002, + 'input2Event': 60094, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60142: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60140, + 'input1Event': 10002, + 'input2Event': 60095, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 10001: {'type': 'model', + 'name': 'dropshadow', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0, 0, -25), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1.5, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_3/models/props/drop_shadow.bam'}, + 10012: {'type': 'model', + 'name': 'backCrate', + 'comment': '', + 'parentEntId': 10067, + 'pos': Point3(0, -5.81496, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 10033: {'type': 'model', + 'name': 'dropshadow', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0, 0, -25), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1.5, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_3/models/props/drop_shadow.bam'}, + 10045: {'type': 'model', + 'name': 'dropshadow', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0, 0, -25), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1.5, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_3/models/props/drop_shadow.bam'}, + 10046: {'type': 'model', + 'name': 'dropshadow', + 'comment': '', + 'parentEntId': 10009, + 'pos': Point3(0, 0, -25), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1.5, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_3/models/props/drop_shadow.bam'}, + 10050: {'type': 'model', + 'name': 'sky', + 'comment': '', + 'parentEntId': 200, + 'pos': Point3(-142.02, 437.227, 0.922491), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(2.5, 2.5, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/cog_sky.bam'}, + 10066: {'type': 'model', + 'name': 'frontCrate', + 'comment': '', + 'parentEntId': 10067, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 10069: {'type': 'model', + 'name': 'backCrate', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(0, -5.81496, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 10070: {'type': 'model', + 'name': 'frontCrate', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 20082: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 5, + 'pos': Point3(4.50815, 11.6508, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.92, 0.92, 0.92), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 20083: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 20082, + 'pos': Point3(0, 0, 5.5), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 20088: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 20087, + 'pos': Point3(1, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.3, 1, 1.3), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 10000: {'type': 'nodepath', + 'name': 'gearGauntletObstacle', + 'comment': '', + 'parentEntId': 10027, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': 'gearGauntlet', + 'comment': 'gears are staggered 15 ft in Y', + 'parentEntId': 10000, + 'pos': Point3(0, -23.25, 6.85), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10014: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, 34.07, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10015: {'type': 'nodepath', + 'name': 'paint mixer platforms', + 'comment': '', + 'parentEntId': 10, + 'pos': Point3(0, 5.15136, -2), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10022: {'type': 'nodepath', + 'name': 'gagBarrels', + 'comment': '', + 'parentEntId': 11, + 'pos': Point3(11.2328, 14.7959, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10023: {'type': 'nodepath', + 'name': 'leftCogs', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(-42.0363, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10027: {'type': 'nodepath', + 'name': 'zoneNodeCompensate', + 'comment': 'I think the ZoneNode was moved.', + 'parentEntId': 19, + 'pos': Point3(-0.426482, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10030: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 8, + 'pos': Point3(2.5, 62.5, 10), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10032: {'type': 'nodepath', + 'name': 'rightCogs', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(46.88, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10034: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': 1}, + 10036: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(5.5, 0, 0), + 'hpr': Point3(161, 0, 0), + 'scale': 1}, + 10037: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(3.1, -48.27, 0.05), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10040: {'type': 'nodepath', + 'name': 'FactoryBoss', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(0, 68.4457, 9.5669), + 'hpr': Point3(180, 0, 0), + 'scale': 1}, + 10047: {'type': 'nodepath', + 'name': 'battleCell', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10056: {'type': 'nodepath', + 'name': 'sounds', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 15), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10660: {'type': 'nodepath', + 'name': 'battleCell', + 'comment': 'Paint Mixer Storage Room', + 'parentEntId': 11, + 'pos': Point3(0, 7.2, 0), + 'hpr': Vec3(-180, 0, 0), + 'scale': 1}, + 10661: {'type': 'nodepath', + 'name': 'battleCell', + 'comment': 'East Catwalk', + 'parentEntId': 9, + 'pos': Point3(-64.82, 69.84, 0), + 'hpr': Vec3(-90, 0, 0), + 'scale': 1}, + 10662: {'type': 'nodepath', + 'name': 'battleCell', + 'comment': 'West Catwalk', + 'parentEntId': 21, + 'pos': Point3(109, 250.38, 0), + 'hpr': Vec3(-180, 0, 0), + 'scale': 1}, + 10663: {'type': 'nodepath', + 'name': 'battleCell', + 'comment': 'Outside Center Silo', + 'parentEntId': 35, + 'pos': Point3(-4, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10064: {'type': 'nodepath', + 'name': 'battleCell', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, -5.20447, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10065: {'type': 'nodepath', + 'name': 'backSteps', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 56.2652, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1.5, 1.3, 0.73)}, + 10067: {'type': 'nodepath', + 'name': 'frontSteps', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, -44.7196, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(1.5, 1.3, 0.729057)}, + 10068: {'type': 'nodepath', + 'name': 'battleCell', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20000: {'type': 'nodepath', + 'name': 'stompers', + 'comment': '', + 'parentEntId': 17, + 'pos': Point3(0.75, 0, 0.5), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20018: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0, -24, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20019: {'type': 'nodepath', + 'name': 'cogsJoin', + 'comment': '', + 'parentEntId': 10030, + 'pos': Point3(16, 2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20022: {'type': 'nodepath', + 'name': 'StomperButtonsNodepath', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(-11.75, -35.8, 14.9), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20023: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20037: {'type': 'nodepath', + 'name': 'SignatureGoonNP', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(-48.4442, -24.9385, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20058: {'type': 'nodepath', + 'name': 'SigRoomCogs', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(-1.0928, -45, 14.99), + 'hpr': Point3(90, 0, 0), + 'scale': 1}, + 20087: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 17, + 'pos': Point3(-4, -117, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20094: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(-0.720506, 27.5461, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20095: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 20096: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(4.84921, 8.74482, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10017: {'type': 'paintMixer', + 'name': 'fifth', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(5.24, 23.52, 8), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(-12, -6, 0), + 'period': 8.0, + 'phaseShift': 0.5, + 'shaftScale': 1, + 'waitPercent': 0.1}, + 10018: {'type': 'paintMixer', + 'name': 'fourth', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(-12.1, 3, 8), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(0, -6, 15), + 'period': 8.0, + 'phaseShift': 0.0, + 'shaftScale': 2.5, + 'waitPercent': 0.1}, + 10019: {'type': 'paintMixer', + 'name': 'third', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(-3.85419, -7.75751, 22.5836), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(7, 0, 0), + 'period': 8.0, + 'phaseShift': 0.0, + 'shaftScale': 2.5, + 'waitPercent': 0.1}, + 10020: {'type': 'paintMixer', + 'name': 'second', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(16.01, -6.47, 23), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(-4, -8, -15), + 'period': 8.0, + 'phaseShift': 0.0, + 'shaftScale': 2.5, + 'waitPercent': 0.1}, + 10054: {'type': 'paintMixer', + 'name': 'first', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(-10, -26.1, 8), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(15, 0, 0), + 'period': 8.0, + 'phaseShift': 0.0, + 'shaftScale': 1, + 'waitPercent': 0.1}, + 20008: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 20009: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 17, + 'pathScale': 1.0}, + 20010: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 21, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 36, + 'pathScale': 1.0}, + 20012: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 21, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 34, + 'pathScale': 1.0}, + 20015: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 21, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 37, + 'pathScale': 1.0}, + 20038: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 38, + 'pathScale': 1.0}, + 20039: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 12, + 'pathScale': 1.0}, + 20040: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(41.5, 33.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 20042: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(15, 34, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 20044: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(1.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'pathIndex': 6, + 'pathScale': 1.0}, + 20045: {'type': 'path', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'pathIndex': 7, + 'pathScale': 1.0}, + 20049: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 13, + 'pathScale': 1.0}, + 20051: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(1, -24, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 42, + 'pathScale': 1.0}, + 20053: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 8, + 'pathScale': 1.0}, + 20055: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 9, + 'pathScale': 1.0}, + 20059: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 10, + 'pathScale': 1.0}, + 20061: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 11, + 'pathScale': 1.0}, + 20067: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 39, + 'pathScale': 1.0}, + 20068: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 40, + 'pathScale': 1.0}, + 20069: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 9, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 5, + 'pathScale': 1.0}, + 20070: {'type': 'path', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 9, + 'pos': Point3(1, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 5, + 'pathScale': 1.0}, + 20073: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 21, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 35, + 'pathScale': 1.0}, + 20075: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(4, 4, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 14, + 'pathScale': 1.0}, + 20076: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 8, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 15, + 'pathScale': 1.0}, + 20077: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 8, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 16, + 'pathScale': 1.0}, + 20078: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 18, + 'pathScale': 1.0}, + 20079: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 20084: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 9, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 41, + 'pathScale': 1.0}, + 20097: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 19, + 'pathScale': 1.0}, + 20098: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 20, + 'pathScale': 1.0}, + 20099: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 21, + 'pathScale': 1.0}, + 20100: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 22, + 'pathScale': 1.0}, + 20101: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 23, + 'pathScale': 1.0}, + 20102: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 24, + 'pathScale': 1.0}, + 20103: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 25, + 'pathScale': 1.0}, + 20104: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 26, + 'pathScale': 1.0}, + 20105: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 27, + 'pathScale': 1.0}, + 20106: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 28, + 'pathScale': 1.0}, + 20107: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 29, + 'pathScale': 1.0}, + 20108: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 30, + 'pathScale': 1.0}, + 20109: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 31, + 'pathScale': 1.0}, + 20110: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 32, + 'pathScale': 1.0}, + 20111: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 33, + 'pathScale': 1.0}, + 60133: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(-10, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 60134: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 60135: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(10, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 10042: {'type': 'propSpinner', + 'name': '', + 'comment': '', + 'parentEntId': 7}, + 20001: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 12, + 'headScale': Point3(7, 5, 7), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.0, + 'range': 30.0, + 'shaftScale': Point3(0.5, 12, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20002: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(0, -14.3294, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 12, + 'headScale': Point3(7, 5, 7), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 10.0, + 'shaftScale': Point3(0.5, 12, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20003: {'type': 'stomper', + 'name': 'copy of copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(0, -28.3252, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 12, + 'headScale': Point3(7, 5, 7), + 'modelPath': 0, + 'motion': 4, + 'period': 2.0, + 'phaseShift': 0.5, + 'range': 10.0, + 'shaftScale': Point3(0.5, 12, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20004: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(-3.5, 16.2588, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 12, + 'headScale': Point3(3.5, 5, 3.5), + 'modelPath': 0, + 'motion': 1, + 'period': 3.0001373423482587, + 'phaseShift': 0.0, + 'range': 15.0, + 'shaftScale': Point3(0.71, 12, 0.71), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20005: {'type': 'stomper', + 'name': 'copy of copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(3.5, 16.2588, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 12, + 'headScale': Point3(3.5, 5, 3.5), + 'modelPath': 0, + 'motion': 0, + 'period': 1.5, + 'phaseShift': 0.0, + 'range': 15.0, + 'shaftScale': Point3(0.71, 12, 0.71), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 1, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20006: {'type': 'stomper', + 'name': 'copy of copy of copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(-3.5, 23.4392, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 12, + 'headScale': Point3(3.5, 5, 3.5), + 'modelPath': 0, + 'motion': 2, + 'period': 1.5, + 'phaseShift': 0.5, + 'range': 15.0, + 'shaftScale': Point3(0.71, 12, 0.71), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20007: {'type': 'stomper', + 'name': 'copy of copy of copy of copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(3.5, 23.4392, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 12, + 'headScale': Point3(3.5, 5, 3.5), + 'modelPath': 0, + 'motion': 3, + 'period': 5.0, + 'phaseShift': 0.5, + 'range': 15.0, + 'shaftScale': Point3(0.71, 12, 0.71), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20029: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 20025, + 'pos': Point3(4.5, 43.5, 0.25), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 0, + 'crushCellId': 20024, + 'damage': 12, + 'headScale': Point3(3, 2, 3), + 'modelPath': 0, + 'motion': 5, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 12.0, + 'shaftScale': Point3(0.66, 37.5, 0.66), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 20033, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20030: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20025, + 'pos': Point3(31.5, 43.5, 0.25), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 0, + 'crushCellId': 20026, + 'damage': 3, + 'headScale': Point3(3, 2, 3), + 'modelPath': 0, + 'motion': 5, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 12.0, + 'shaftScale': Point3(0.66, 37.5, 0.66), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 20034, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20031: {'type': 'stomper', + 'name': 'copy of copy of ', + 'comment': '', + 'parentEntId': 20025, + 'pos': Point3(64.5, 43.5, 0.25), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 0, + 'crushCellId': 20027, + 'damage': 3, + 'headScale': Point3(3, 2, 3), + 'modelPath': 0, + 'motion': 5, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 12.0, + 'shaftScale': Point3(0.66, 37.5, 0.66), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 20035, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20032: {'type': 'stomper', + 'name': 'copy of copy of copy of ', + 'comment': '', + 'parentEntId': 20025, + 'pos': Point3(85.5, 43.5, 0.25), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 0, + 'crushCellId': 20028, + 'damage': 3, + 'headScale': Point3(3, 2, 3), + 'modelPath': 0, + 'motion': 5, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 12.0, + 'shaftScale': Point3(0.66, 37.5, 0.66), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 20036, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20050: {'type': 'trigger', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(10, 0, 10), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(20, 20, 20), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1, + 'triggerName': 'signatureRoomView'}, + 20057: {'type': 'trigger', + 'name': '', + 'comment': '', + 'parentEntId': 23, + 'pos': Point3(3, -8.8, 15.5091), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(25, 25, 25), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1, + 'triggerName': 'lookoutTrigger'}, + 30077: {'type': 'trigger', + 'name': 'button cutscene', + 'comment': '', + 'parentEntId': 3, + 'pos': Point3(-4, 8, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1, + 'triggerName': ''}, + 10013: {'type': 'visibilityExtender', + 'name': 'intoEastSilo', + 'comment': '', + 'parentEntId': 60009, + 'event': 60101, + 'newZones': [61]}, + 10043: {'type': 'visibilityExtender', + 'name': 'beyondLobby', + 'comment': '', + 'parentEntId': 10049, + 'event': 10057, + 'newZones': [5, 116]}, + 10044: {'type': 'visibilityExtender', + 'name': 'intoEntrance1', + 'comment': '', + 'parentEntId': 10051, + 'event': 30000, + 'newZones': [3]}, + 10058: {'type': 'visibilityExtender', + 'name': 'intoFarHallway', + 'comment': '', + 'parentEntId': 10049, + 'event': 10059, + 'newZones': [6, 118]}, + 10060: {'type': 'visibilityExtender', + 'name': 'intoLookout', + 'comment': '', + 'parentEntId': 10049, + 'event': 10061, + 'newZones': [23]}, + 10062: {'type': 'visibilityExtender', + 'name': 'intoLobby', + 'comment': '', + 'parentEntId': 60031, + 'event': 10063, + 'newZones': [4, 114]}, + 30022: {'type': 'visibilityExtender', + 'name': 'intoLobby', + 'comment': '', + 'parentEntId': 10049, + 'event': 30000, + 'newZones': [4, 113]}, + 30023: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60017, + 'event': 30002, + 'newZones': [15, 126]}, + 30024: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60016, + 'event': 30002, + 'newZones': [16]}, + 30025: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60016, + 'event': 30021, + 'newZones': [14, 17, 121]}, + 30026: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60015, + 'event': 30016, + 'newZones': [13, 119]}, + 30027: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60015, + 'event': 30021, + 'newZones': [15, 106]}, + 30029: {'type': 'visibilityExtender', + 'name': 'beyondLobby', + 'comment': '', + 'parentEntId': 10051, + 'event': 30009, + 'newZones': [5, 116]}, + 30030: {'type': 'visibilityExtender', + 'name': 'beyond door 113', + 'comment': '', + 'parentEntId': 60000, + 'event': 30009, + 'newZones': [4, 114]}, + 30031: {'type': 'visibilityExtender', + 'name': 'beyond door 116', + 'comment': '', + 'parentEntId': 60000, + 'event': 30011, + 'newZones': [6, + 109, + 117, + 118]}, + 30032: {'type': 'visibilityExtender', + 'name': 'intoHallwayFromLobby', + 'comment': '', + 'parentEntId': 60001, + 'event': 30011, + 'newZones': [5, 113]}, + 30033: {'type': 'visibilityExtender', + 'name': 'intoBoilerRoom', + 'comment': '', + 'parentEntId': 60001, + 'event': 30012, + 'newZones': [8]}, + 30034: {'type': 'visibilityExtender', + 'name': 'intoLookout', + 'comment': '', + 'parentEntId': 60001, + 'event': 30013, + 'newZones': [23, 39]}, + 30035: {'type': 'visibilityExtender', + 'name': 'intoGearRoom', + 'comment': '', + 'parentEntId': 60001, + 'event': 30005, + 'newZones': [7]}, + 30036: {'type': 'visibilityExtender', + 'name': 'beyond door 109', + 'comment': '', + 'parentEntId': 60002, + 'event': 30005, + 'newZones': [6, + 116, + 117, + 118]}, + 30037: {'type': 'visibilityExtender', + 'name': 'beyond door 110', + 'comment': '', + 'parentEntId': 60002, + 'event': 30006, + 'newZones': [9, + 25, + 26, + 33, + 34, + 35, + 38, + 41, + 53, + 112, + 115, + 200]}, + 30038: {'type': 'visibilityExtender', + 'name': 'beyond door 117', + 'comment': '', + 'parentEntId': 60005, + 'event': 30012, + 'newZones': [6, + 109, + 116, + 118]}, + 30039: {'type': 'visibilityExtender', + 'name': 'beyond door 108', + 'comment': '', + 'parentEntId': 60005, + 'event': 30004, + 'newZones': [12, + 21, + 26, + 34, + 35, + 40, + 41, + 53, + 60, + 119, + 120, + 200]}, + 30041: {'type': 'visibilityExtender', + 'name': 'beyond door 110', + 'comment': '', + 'parentEntId': 60003, + 'event': 30006, + 'newZones': [7]}, + 30042: {'type': 'visibilityExtender', + 'name': 'beyond door 112', + 'comment': '', + 'parentEntId': 60003, + 'event': 30008, + 'newZones': [10, 11]}, + 30043: {'type': 'visibilityExtender', + 'name': 'intoWarehouse', + 'comment': '', + 'parentEntId': 60003, + 'event': 30010, + 'newZones': [24, 39]}, + 30044: {'type': 'visibilityExtender', + 'name': 'beyond door 112', + 'comment': '', + 'parentEntId': 60004, + 'event': 30008, + 'newZones': [9, + 25, + 26, + 33, + 34, + 35, + 38, + 41, + 53, + 110, + 115, + 200]}, + 30046: {'type': 'visibilityExtender', + 'name': 'beyond door 112', + 'comment': '', + 'parentEntId': 60066, + 'event': 30008, + 'newZones': [9, + 25, + 26, + 41, + 200]}, + 30049: {'type': 'visibilityExtender', + 'name': 'beyond door 119', + 'comment': '', + 'parentEntId': 60013, + 'event': 30014, + 'newZones': [12, + 21, + 23, + 26, + 33, + 34, + 35, + 41, + 53, + 60, + 108, + 112, + 120, + 200]}, + 30050: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60013, + 'event': 30016, + 'newZones': [14, 17, 126]}, + 30051: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60014, + 'event': 30016, + 'newZones': [13, 119]}, + 30052: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60014, + 'event': 30021, + 'newZones': [15, 106]}, + 30055: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60019, + 'event': 30001, + 'newZones': [27, 127]}, + 30056: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60018, + 'event': 30001, + 'newZones': [27, 127]}, + 30057: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60018, + 'event': 60088, + 'newZones': [17]}, + 30059: {'type': 'visibilityExtender', + 'name': 'beyond door 108', + 'comment': '', + 'parentEntId': 60006, + 'event': 30004, + 'newZones': [8, 117]}, + 30060: {'type': 'visibilityExtender', + 'name': 'beyond door 119', + 'comment': '', + 'parentEntId': 60006, + 'event': 30014, + 'newZones': [13, 121]}, + 30061: {'type': 'visibilityExtender', + 'name': 'intoWarehouse', + 'comment': '', + 'parentEntId': 60006, + 'event': 30015, + 'newZones': [24, 39]}, + 30062: {'type': 'visibilityExtender', + 'name': 'beyond door 107', + 'comment': '', + 'parentEntId': 60024, + 'event': 30003, + 'newZones': [27, 127]}, + 30063: {'type': 'visibilityExtender', + 'name': 'intoHallway', + 'comment': '', + 'parentEntId': 60031, + 'event': 30013, + 'newZones': [6, + 109, + 116, + 117]}, + 30064: {'type': 'visibilityExtender', + 'name': 'beyondLowerWestDoor', + 'comment': '', + 'parentEntId': 60007, + 'event': 30015, + 'newZones': [12, + 21, + 26, + 34, + 40, + 41, + 53, + 200]}, + 30066: {'type': 'visibilityExtender', + 'name': 'beyondLowerEastDoor', + 'comment': '', + 'parentEntId': 60007, + 'event': 30010, + 'newZones': [9, + 25, + 26, + 33, + 38, + 41, + 200]}, + 30067: {'type': 'visibilityExtender', + 'name': 'beyondUpperEastDoor', + 'comment': '', + 'parentEntId': 60007, + 'event': 30019, + 'newZones': [9, + 33, + 38, + 41, + 200, + 222]}, + 30069: {'type': 'visibilityExtender', + 'name': 'beyond door 118', + 'comment': '', + 'parentEntId': 60000, + 'event': 30068, + 'newZones': [23]}, + 30071: {'type': 'visibilityExtender', + 'name': 'intoLobby', + 'comment': '', + 'parentEntId': 60001, + 'event': 60030, + 'newZones': [4, 114]}, + 30073: {'type': 'visibilityExtender', + 'name': 'intoLobbyHallway', + 'comment': '', + 'parentEntId': 60031, + 'event': 60033, + 'newZones': [5, 113]}, + 30075: {'type': 'visibilityExtender', + 'name': 'intoGearRoom', + 'comment': '', + 'parentEntId': 60031, + 'event': 60034, + 'newZones': [7]}, + 60008: {'type': 'visibilityExtender', + 'name': 'beyondUpperWestDoor', + 'comment': '', + 'parentEntId': 60007, + 'event': 30020, + 'newZones': [12, + 21, + 34, + 40, + 41, + 60, + 127, + 200]}, + 60010: {'type': 'visibilityExtender', + 'name': 'intoWarehouse', + 'comment': '', + 'parentEntId': 60009, + 'event': 30019, + 'newZones': [24, 39, 125]}, + 60012: {'type': 'visibilityExtender', + 'name': 'beyond door 125', + 'comment': '', + 'parentEntId': 60011, + 'event': 30020, + 'newZones': [24, 39, 124]}, + 60020: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60015, + 'event': 60076, + 'newZones': [16]}, + 60021: {'type': 'visibilityExtender', + 'name': 'beyond door 116', + 'comment': '', + 'parentEntId': 10051, + 'event': 60023, + 'newZones': [6, 118]}, + 60022: {'type': 'visibilityExtender', + 'name': 'beyond door 118', + 'comment': '', + 'parentEntId': 10051, + 'event': 60025, + 'newZones': [23]}, + 60026: {'type': 'visibilityExtender', + 'name': 'beyond door 109', + 'comment': '', + 'parentEntId': 60000, + 'event': 60028, + 'newZones': [7]}, + 60027: {'type': 'visibilityExtender', + 'name': 'beyond door 117', + 'comment': '', + 'parentEntId': 60000, + 'event': 60029, + 'newZones': [8]}, + 60032: {'type': 'visibilityExtender', + 'name': 'intoBoilerRoom', + 'comment': '', + 'parentEntId': 60031, + 'event': 60035, + 'newZones': [8]}, + 60036: {'type': 'visibilityExtender', + 'name': 'beyond door 117', + 'comment': '', + 'parentEntId': 60002, + 'event': 60037, + 'newZones': [8]}, + 60038: {'type': 'visibilityExtender', + 'name': 'beyond door 109', + 'comment': '', + 'parentEntId': 60005, + 'event': 60039, + 'newZones': [7]}, + 60040: {'type': 'visibilityExtender', + 'name': 'beyond door 124', + 'comment': '', + 'parentEntId': 60011, + 'event': 60041, + 'newZones': [38]}, + 60042: {'type': 'visibilityExtender', + 'name': 'beyondWarehouse', + 'comment': '', + 'parentEntId': 60009, + 'event': 60043, + 'newZones': [12, 200]}, + 60045: {'type': 'visibilityExtender', + 'name': 'beyond door 124', + 'comment': '', + 'parentEntId': 60044, + 'event': 60047, + 'newZones': [24]}, + 60046: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60044, + 'event': 10002, + 'newZones': [31]}, + 60048: {'type': 'visibilityExtender', + 'name': 'beyond door 127', + 'comment': '', + 'parentEntId': 60024, + 'event': 60049, + 'newZones': [21, 200]}, + 60050: {'type': 'visibilityExtender', + 'name': 'beyond door 127', + 'comment': '', + 'parentEntId': 60019, + 'event': 60051, + 'newZones': [21, 34, 200]}, + 60052: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60016, + 'event': 60053, + 'newZones': [13, 119]}, + 60054: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60017, + 'event': 60055, + 'newZones': [14, 17, 121]}, + 60056: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60013, + 'event': 60057, + 'newZones': [15, 106]}, + 60058: {'type': 'visibilityExtender', + 'name': 'beyond door 116', + 'comment': '', + 'parentEntId': 60005, + 'event': 60059, + 'newZones': [5]}, + 60060: {'type': 'visibilityExtender', + 'name': 'beyond door 118', + 'comment': '', + 'parentEntId': 60005, + 'event': 60061, + 'newZones': [23]}, + 60062: {'type': 'visibilityExtender', + 'name': 'beyond door 116', + 'comment': '', + 'parentEntId': 60002, + 'event': 60064, + 'newZones': [5]}, + 60063: {'type': 'visibilityExtender', + 'name': 'beyond door 118', + 'comment': '', + 'parentEntId': 60002, + 'event': 60065, + 'newZones': [23]}, + 60068: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60067, + 'event': 30001, + 'newZones': [18, + 19, + 20, + 131]}, + 60069: {'type': 'visibilityExtender', + 'name': 'beyond door 107', + 'comment': '', + 'parentEntId': 60067, + 'event': 30003, + 'newZones': [22]}, + 60070: {'type': 'visibilityExtender', + 'name': 'beyond door 127', + 'comment': '', + 'parentEntId': 60067, + 'event': 10052, + 'newZones': [12, + 21, + 26, + 34, + 35, + 40, + 41, + 53, + 60, + 200]}, + 60071: {'type': 'visibilityExtender', + 'name': 'beyond door 127', + 'comment': '', + 'parentEntId': 60006, + 'event': 10052, + 'newZones': [27, 105, 107]}, + 60072: {'type': 'visibilityExtender', + 'name': 'beyond door 107', + 'comment': '', + 'parentEntId': 60006, + 'event': 60074, + 'newZones': [22]}, + 60073: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60006, + 'event': 60075, + 'newZones': [18]}, + 60077: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60014, + 'event': 60078, + 'newZones': [16]}, + 60079: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60013, + 'event': 60080, + 'newZones': [16]}, + 60081: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60017, + 'event': 60082, + 'newZones': [13]}, + 60083: {'type': 'visibilityExtender', + 'name': 'beyond door 119', + 'comment': '', + 'parentEntId': 60005, + 'event': 60084, + 'newZones': [13]}, + 60085: {'type': 'visibilityExtender', + 'name': 'beyond door 112', + 'comment': '', + 'parentEntId': 60002, + 'event': 60086, + 'newZones': [10]}, + 60087: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60015, + 'event': 60091, + 'newZones': [27]}, + 60089: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60019, + 'event': 60088, + 'newZones': [17]}, + 60090: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60015, + 'event': 60088, + 'newZones': [18, 19, 105]}, + 60092: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60067, + 'event': 60093, + 'newZones': [17]}, + 60097: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60096, + 'event': 60095, + 'newZones': [33, + 34, + 35, + 36, + 37, + 60, + 61, + 128, + 129, + 200]}, + 60098: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60044, + 'event': 60095, + 'newZones': [30]}, + 60099: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60096, + 'event': 60100, + 'newZones': [31]}, + 60106: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60011, + 'event': 60094, + 'newZones': [32]}, + 60107: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60011, + 'event': 60095, + 'newZones': [30]}, + 60109: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60108, + 'event': 60094, + 'newZones': [32]}, + 60110: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60108, + 'event': 60095, + 'newZones': [30]}, + 60112: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60111, + 'event': 60094, + 'newZones': [32]}, + 60113: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60111, + 'event': 60095, + 'newZones': [30]}, + 60115: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60114, + 'event': 60094, + 'newZones': [32]}, + 60116: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60114, + 'event': 60095, + 'newZones': [30]}, + 60117: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60014, + 'event': 60088, + 'newZones': [18]}, + 60120: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60108, + 'event': 10002, + 'newZones': [31]}, + 60122: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60121, + 'event': 10002, + 'newZones': [33, + 34, + 35, + 36, + 37, + 60, + 61, + 128, + 129, + 130, + 200]}, + 60123: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60111, + 'event': 10002, + 'newZones': []}, + 60124: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60114, + 'event': 10002, + 'newZones': [31]}, + 60125: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60011, + 'event': 10002, + 'newZones': [31]}, + 60127: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60126, + 'event': 10002, + 'newZones': [31]}, + 60128: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60126, + 'event': 60094, + 'newZones': [32]}, + 60129: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60126, + 'event': 60095, + 'newZones': [30]}, + 60131: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60130, + 'event': 60094, + 'newZones': [33, + 34, + 35, + 36, + 37, + 60, + 61, + 128, + 130, + 200]}, + 60136: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60044, + 'event': 60094, + 'newZones': [32]}, + 60137: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60096, + 'event': 60138, + 'newZones': [32]}, + 60139: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60121, + 'event': 60141, + 'newZones': [32]}, + 60140: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60121, + 'event': 60142, + 'newZones': [30]}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/SinkingPlatformGlobals.py b/toontown/coghq/SinkingPlatformGlobals.py new file mode 100755 index 00000000..ce9ca8cb --- /dev/null +++ b/toontown/coghq/SinkingPlatformGlobals.py @@ -0,0 +1,3 @@ +OFF = 0 +SINKING = 1 +RISING = 2 diff --git a/toontown/coghq/SpecImports.py b/toontown/coghq/SpecImports.py new file mode 100755 index 00000000..6ce27080 --- /dev/null +++ b/toontown/coghq/SpecImports.py @@ -0,0 +1 @@ +from panda3d.core import * diff --git a/toontown/coghq/StageInterior.py b/toontown/coghq/StageInterior.py new file mode 100755 index 00000000..47233adf --- /dev/null +++ b/toontown/coghq/StageInterior.py @@ -0,0 +1,276 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattlePlace +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.showbase import BulletinBoardWatcher +from panda3d.core import * +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toon import Toon +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownBattleGlobals +from toontown.coghq import DistributedStage +from toontown.building import Elevator +from otp.nametag import NametagGlobals + +class StageInterior(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('StageInterior') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.zoneId = loader.stageId + self.elevatorDoneEvent = 'elevatorDone' + self.fsm = ClassicFSM.ClassicFSM('StageInterior', [State.State('start', self.enterStart, self.exitStart, ['walk', 'teleportIn', 'fallDown']), + State.State('walk', self.enterWalk, self.exitWalk, ['push', + 'sit', + 'stickerBook', + 'WaitForBattle', + 'battle', + 'died', + 'teleportOut', + 'squished', + 'fallDown', + 'elevator']), + State.State('sit', self.enterSit, self.exitSit, ['walk', 'died', 'teleportOut']), + State.State('push', self.enterPush, self.exitPush, ['walk', 'died', 'teleportOut']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'battle', + 'WaitForBattle', + 'died', + 'teleportOut']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', + 'walk', + 'died', + 'teleportOut']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('fallDown', self.enterFallDown, self.exitFallDown, ['walk', 'died', 'teleportOut']), + State.State('squished', self.enterSquished, self.exitSquished, ['walk', 'died', 'teleportOut']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', + 'teleportOut', + 'quietZone', + 'died']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', + 'FLA', + 'quietZone', + 'WaitForBattle']), + State.State('died', self.enterDied, self.exitDied, ['teleportOut']), + State.State('FLA', self.enterFLA, self.exitFLA, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['teleportIn']), + State.State('elevator', self.enterElevator, self.exitElevator, ['walk']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + self.parentFSM.getStateNamed('stageInterior').addChild(self.fsm) + BattlePlace.BattlePlace.load(self) + self.music = base.loadMusic('phase_9/audio/bgm/CHQ_FACT_bg.ogg') + + def unload(self): + self.parentFSM.getStateNamed('stageInterior').removeChild(self.fsm) + del self.music + del self.fsm + del self.parentFSM + BattlePlace.BattlePlace.unload(self) + + def enter(self, requestStatus): + self.fsm.enterInitialState() + base.transitions.fadeOut(t=0) + self._telemLimiter = TLGatherAllAvs('StageInterior', RotationLimitToH) + base.localAvatar.inventory.setRespectInvasions(0) + base.cr.forbidCheesyEffects(1) + + def commence(self = self): + NametagGlobals.setMasterArrowsOn(1) + self.fsm.request(requestStatus['how'], [requestStatus]) + base.playMusic(self.music, looping=1, volume=0.8) + base.transitions.irisIn() + stage = bboard.get(DistributedStage.DistributedStage.ReadyPost) + self.loader.hood.spawnTitleText(stage.stageId) + + self.stageReadyWatcher = BulletinBoardWatcher.BulletinBoardWatcher('StageReady', DistributedStage.DistributedStage.ReadyPost, commence) + self.stageDefeated = 0 + self.acceptOnce(DistributedStage.DistributedStage.WinEvent, self.handleStageWinEvent) + if __debug__ and 0: + self.accept('f10', lambda : messenger.send(DistributedStage.DistributedStage.WinEvent)) + self.confrontedBoss = 0 + + def handleConfrontedBoss(self = self): + self.confrontedBoss = 1 + + self.acceptOnce('localToonConfrontedStageBoss', handleConfrontedBoss) + + def exit(self): + NametagGlobals.setMasterArrowsOn(0) + self._telemLimiter.destroy() + del self._telemLimiter + bboard.remove(DistributedStage.DistributedStage.ReadyPost) + base.cr.forbidCheesyEffects(0) + base.localAvatar.inventory.setRespectInvasions(1) + self.fsm.requestFinalState() + self.loader.music.stop() + self.music.stop() + self.ignoreAll() + del self.stageReadyWatcher + + def enterWalk(self, teleportIn = 0): + BattlePlace.BattlePlace.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterPush(self): + BattlePlace.BattlePlace.enterPush(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterWaitForBattle(self): + StageInterior.notify.debug('enterWaitForBattle') + BattlePlace.BattlePlace.enterWaitForBattle(self) + if base.localAvatar.getParent() != render: + base.localAvatar.wrtReparentTo(render) + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + + def exitWaitForBattle(self): + StageInterior.notify.debug('exitWaitForBattle') + BattlePlace.BattlePlace.exitWaitForBattle(self) + + def enterBattle(self, event): + StageInterior.notify.debug('enterBattle') + self.music.stop() + BattlePlace.BattlePlace.enterBattle(self, event) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterTownBattle(self, event): + mult = ToontownBattleGlobals.getStageCreditMultiplier(bboard.get(DistributedStage.DistributedStage.FloorNum)) + base.localAvatar.inventory.setBattleCreditMultiplier(mult) + self.loader.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult) + + def exitBattle(self): + StageInterior.notify.debug('exitBattle') + BattlePlace.BattlePlace.exitBattle(self) + self.loader.music.stop() + base.playMusic(self.music, looping=1, volume=0.8) + + def enterStickerBook(self, page = None): + BattlePlace.BattlePlace.enterStickerBook(self, page) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterSit(self): + BattlePlace.BattlePlace.enterSit(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterZone(self, zoneId): + pass + + def enterTeleportOut(self, requestStatus): + StageInterior.notify.debug('enterTeleportOut()') + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __processLeaveRequest(self, requestStatus): + hoodId = requestStatus['hoodId'] + if hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def __teleportOutDone(self, requestStatus): + StageInterior.notify.debug('__teleportOutDone()') + messenger.send('leavingStage') + messenger.send('localToonLeft') + if self.stageDefeated and not self.confrontedBoss: + self.fsm.request('FLA', [requestStatus]) + else: + self.__processLeaveRequest(requestStatus) + + def exitTeleportOut(self): + StageInterior.notify.debug('exitTeleportOut()') + BattlePlace.BattlePlace.exitTeleportOut(self) + + def handleStageWinEvent(self): + StageInterior.notify.debug('handleStageWinEvent') + + if base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'died': + return + + self.stageDefeated = 1 + + if 1: + zoneId = ZoneUtil.getHoodId(self.zoneId) + else: + zoneId = ZoneUtil.getSafeZoneId(base.localAvatar.defaultZone) + + self.fsm.request('teleportOut', [{ + 'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1, + }]) + + def enterDied(self, requestStatus, callback = None): + StageInterior.notify.debug('enterDied') + + def diedDone(requestStatus, self = self, callback = callback): + if callback is not None: + callback() + messenger.send('leavingStage') + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + BattlePlace.BattlePlace.enterDied(self, requestStatus, diedDone) + + def enterFLA(self, requestStatus): + StageInterior.notify.debug('enterFLA') + self.flaDialog = TTDialog.TTGlobalDialog(message=TTLocalizer.ForcedLeaveStageAckMsg, doneEvent='FLADone', style=TTDialog.Acknowledge, fadeScreen=1) + + def continueExit(self = self, requestStatus = requestStatus): + self.__processLeaveRequest(requestStatus) + + self.accept('FLADone', continueExit) + self.flaDialog.show() + + def exitFLA(self): + StageInterior.notify.debug('exitFLA') + if hasattr(self, 'flaDialog'): + self.flaDialog.cleanup() + del self.flaDialog + + def detectedElevatorCollision(self, distElevator): + self.fsm.request('elevator', [distElevator]) + + def enterElevator(self, distElevator): + self.accept(self.elevatorDoneEvent, self.handleElevatorDone) + self.elevator = Elevator.Elevator(self.fsm.getStateNamed('elevator'), self.elevatorDoneEvent, distElevator) + distElevator.elevatorFSM = self.elevator + self.elevator.load() + self.elevator.enter() + + def exitElevator(self): + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + + def handleElevatorDone(self, doneStatus): + self.notify.debug('handling elevator done event') + where = doneStatus['where'] + if where == 'reject': + if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen(): + pass + else: + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'factoryInterior' or where == 'suitInterior': + self.doneStatus = doneStatus + self.doneEvent = 'lawOfficeFloorDone' + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') diff --git a/toontown/coghq/StageLayout.py b/toontown/coghq/StageLayout.py new file mode 100755 index 00000000..23c0e797 --- /dev/null +++ b/toontown/coghq/StageLayout.py @@ -0,0 +1,702 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.PythonUtil import invertDictLossless +from toontown.coghq import StageRoomSpecs +from toontown.toonbase import ToontownGlobals +from direct.showbase.PythonUtil import normalDistrib, lerp +import random + +def printAllCashbotInfo(): + print 'roomId: roomName' + for roomId, roomName in StageRoomSpecs.CashbotStageRoomId2RoomName.items(): + print '%s: %s' % (roomId, roomName) + + print '\nroomId: numBattles' + for roomId, numBattles in StageRoomSpecs.roomId2numBattles.items(): + print '%s: %s' % (roomId, numBattles) + + print '\nstageId floor roomIds' + printStageRoomIds() + print '\nstageId floor numRooms' + printNumRooms() + print '\nstageId floor numForcedBattles' + printNumBattles() + + +def iterateLawbotStages(func): + from toontown.toonbase import ToontownGlobals + for layoutId in xrange(len(stageLayouts)): + for floorNum in xrange(getNumFloors(layoutId)): + func(StageLayout(0, floorNum, layoutId)) + + +def printStageInfo(): + + def func(sl): + print sl + + iterateLawbotStages(func) + + +def printRoomUsage(): + usage = {} + + def func(sl): + for roomId in sl.getRoomIds(): + usage.setdefault(roomId, 0) + usage[roomId] += 1 + + iterateLawbotStages(func) + roomIds = usage.keys() + roomIds.sort() + for roomId in roomIds: + print '%s: %s' % (roomId, usage[roomId]) + + +def printRoomInfo(): + roomIds = StageRoomSpecs.roomId2numCogs.keys() + roomIds.sort() + for roomId in roomIds: + print 'room %s: %s cogs, %s cogLevels, %s merit cogLevels' % (roomId, + StageRoomSpecs.roomId2numCogs[roomId], + StageRoomSpecs.roomId2numCogLevels[roomId], + StageRoomSpecs.roomId2numMeritCogLevels[roomId]) + + +def printStageRoomIds(): + + def func(ml): + print ml.getStageId(), ml.getFloorNum(), ml.getRoomIds() + + iterateCashbotStages(func) + + +def printStageRoomNames(): + + def func(ml): + print ml.getStageId(), ml.getFloorNum(), ml.getRoomNames() + + iterateCashbotStages(func) + + +def printNumRooms(): + + def func(ml): + print ml.getStageId(), ml.getFloorNum(), ml.getNumRooms() + + iterateCashbotStages(func) + + +def printNumBattles(): + + def func(ml): + print ml.getStageId(), ml.getFloorNum(), ml.getNumBattles() + + iterateCashbotStages(func) + + +DefaultLayout1 = ({0: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 1: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 2: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 3: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 4: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 5: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 6: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 7: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 8: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 9: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 10: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 11: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 12: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 13: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 14: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 15: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 16: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 17: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 18: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 19: (0, + 1, + 2, + 3, + 1, + 2, + 4)},) +DefaultLayout = [(0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1)] +testLayout = [(0, + 3, + 8, + 105, + 1), (0, + 7, + 8, + 105, + 2)] +LawOfficeLayout2_0 = [(0, + 7, + 8, + 105, + 1), (0, + 10, + 104, + 103, + 1), (0, + 105, + 101, + 12, + 2)] +LawOfficeLayout2_1 = [(0, + 10, + 11, + 104, + 1), (0, + 100, + 105, + 8, + 1), (0, + 103, + 3, + 104, + 2)] +LawOfficeLayout2_2 = [(0, + 8, + 105, + 102, + 1), (0, + 100, + 104, + 10, + 1), (0, + 101, + 105, + 3, + 2)] +LawOfficeLayout3_0 = [(0, + 8, + 101, + 104, + 1), + (0, + 7, + 105, + 103, + 1), + (0, + 100, + 8, + 104, + 1), + (0, + 105, + 10, + 12, + 2)] +LawOfficeLayout3_1 = [(0, + 100, + 8, + 105, + 1), + (0, + 103, + 10, + 104, + 1), + (0, + 8, + 7, + 105, + 1), + (0, + 104, + 12, + 101, + 2)] +LawOfficeLayout3_2 = [(0, + 103, + 104, + 100, + 1), + (0, + 102, + 8, + 105, + 1), + (0, + 10, + 104, + 3, + 1), + (0, + 105, + 10, + 11, + 2)] +LawOfficeLayout4_0 = [(0, + 3, + 7, + 105, + 1), + (0, + 103, + 104, + 8, + 1), + (0, + 102, + 105, + 11, + 1), + (0, + 8, + 104, + 100, + 1), + (0, + 10, + 105, + 12, + 2)] +LawOfficeLayout4_1 = [(0, + 7, + 105, + 102, + 1), + (0, + 103, + 12, + 104, + 1), + (0, + 101, + 104, + 8, + 1), + (0, + 10, + 3, + 105, + 1), + (0, + 8, + 104, + 102, + 2)] +LawOfficeLayout4_2 = [(0, + 11, + 105, + 102, + 1), + (0, + 3, + 104, + 8, + 1), + (0, + 100, + 10, + 104, + 1), + (0, + 8, + 12, + 105, + 1), + (0, + 104, + 102, + 11, + 2)] +LawOfficeLayout5_0 = [(0, + 104, + 10, + 7, + 1), + (0, + 105, + 103, + 3, + 1), + (0, + 104, + 11, + 12, + 1), + (0, + 101, + 8, + 105, + 1), + (0, + 10, + 104, + 12, + 1), + (0, + 105, + 100, + 7, + 2)] +LawOfficeLayout5_1 = [(0, + 11, + 8, + 104, + 1), + (0, + 102, + 10, + 105, + 1), + (0, + 104, + 7, + 101, + 1), + (0, + 105, + 10, + 12, + 1), + (0, + 8, + 11, + 105, + 1), + (0, + 104, + 12, + 3, + 2)] +LawOfficeLayout5_2 = [(0, + 105, + 103, + 8, + 1), + (0, + 10, + 3, + 104, + 1), + (0, + 105, + 103, + 101, + 1), + (0, + 12, + 8, + 104, + 1), + (0, + 7, + 11, + 104, + 1), + (0, + 105, + 12, + 10, + 2)] +stageLayouts = [LawOfficeLayout2_0, + LawOfficeLayout2_1, + LawOfficeLayout2_2, + LawOfficeLayout3_0, + LawOfficeLayout3_1, + LawOfficeLayout3_2, + LawOfficeLayout4_0, + LawOfficeLayout4_1, + LawOfficeLayout4_2, + LawOfficeLayout5_0, + LawOfficeLayout5_1, + LawOfficeLayout5_2] +stageLayouts1 = [testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout] + +def getNumFloors(layoutIndex): + return len(stageLayouts[layoutIndex]) + + +class StageLayout: + notify = DirectNotifyGlobal.directNotify.newCategory('StageLayout') + + def __init__(self, stageId, floorNum, stageLayout = 0): + self.stageId = stageId + self.floorNum = floorNum + self.roomIds = [] + self.hallways = [] + self.layoutId = stageLayout + self.roomIds = stageLayouts[stageLayout][floorNum] + self.numRooms = 1 + len(self.roomIds) + self.numHallways = self.numRooms - 1 + hallwayRng = self.getRng() + connectorRoomNames = StageRoomSpecs.CashbotStageConnectorRooms + for i in xrange(self.numHallways): + self.hallways.append(hallwayRng.choice(connectorRoomNames)) + + def getNumRooms(self): + return len(self.roomIds) + + def getRoomId(self, n): + return self.roomIds[n] + + def getRoomIds(self): + return self.roomIds[:] + + def getRoomNames(self): + names = [] + for roomId in self.roomIds: + names.append(StageRoomSpecs.CashbotStageRoomId2RoomName[roomId]) + + return names + + def getNumHallways(self): + return len(self.hallways) + + def getHallwayModel(self, n): + return self.hallways[n] + + def getNumBattles(self): + numBattles = 0 + for roomId in self.getRoomIds(): + numBattles += StageRoomSpecs.roomId2numBattles[roomId] + + return numBattles + + def getNumCogs(self): + numCogs = 0 + for roomId in self.getRoomIds(): + numCogs += StageRoomSpecs.roomId2numCogs[roomId] + + return numCogs + + def getNumCogLevels(self): + numLevels = 0 + for roomId in self.getRoomIds(): + numLevels += StageRoomSpecs.roomId2numCogLevels[roomId] + + return numLevels + + def getNumMeritCogLevels(self): + numLevels = 0 + for roomId in self.getRoomIds(): + numLevels += StageRoomSpecs.roomId2numMeritCogLevels[roomId] + + return numLevels + + def getStageId(self): + return self.stageId + + def getFloorNum(self): + return self.floorNum + + def getRng(self): + return random.Random(self.stageId * self.floorNum) + + def __str__(self): + return 'StageLayout: id=%s, layout=%s, floor=%s, meritCogLevels=%s, numRooms=%s, numBattles=%s, numCogs=%s' % (self.stageId, + self.layoutId, + self.floorNum, + self.getNumMeritCogLevels(), + self.getNumRooms(), + self.getNumBattles(), + self.getNumCogs()) + + def __repr__(self): + return str(self) diff --git a/toontown/coghq/StageRoom.py b/toontown/coghq/StageRoom.py new file mode 100755 index 00000000..7db85e62 --- /dev/null +++ b/toontown/coghq/StageRoom.py @@ -0,0 +1,104 @@ +from panda3d.core import * +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals +from toontown.coghq import StageRoomSpecs +import random + +class StageRoom(DirectObject.DirectObject): + FloorCollPrefix = 'stageFloorColl' + CashbotStageDoorFrame = 'phase_10/models/cashbotHQ/DoorFrame' + + def __init__(self, path = None): + if path is not None: + if path in StageRoomSpecs.CashbotStageConnectorRooms: + loadFunc = loader.loadModelCopy + else: + loadFunc = loader.loadModel + self.setGeom(loadFunc(path)) + self.localToonFSM = ClassicFSM.ClassicFSM('StageRoomLocalToonPresent', [State.State('off', self.enterLtOff, self.exitLtOff, ['notPresent']), State.State('notPresent', self.enterLtNotPresent, self.exitLtNotPresent, ['present']), State.State('present', self.enterLtPresent, self.exitLtPresent, ['notPresent'])], 'notPresent', 'notPresent') + self.localToonFSM.enterInitialState() + return + + def delete(self): + del self.localToonFSM + + def enter(self): + self.localToonFSM.request('notPresent') + + def exit(self): + self.localToonFSM.requestFinalState() + + def setRoomNum(self, num): + self.roomNum = num + + def getRoomNum(self): + return self.roomNum + + def setGeom(self, geom): + self.__geom = geom + ug = self.__geom.find('**/underground') + if not ug.isEmpty(): + ug.setBin('ground', -10) + + def getGeom(self): + return self.__geom + + def _getEntrances(self): + return self.__geom.findAllMatches('**/ENTRANCE*') + + def _getExits(self): + return self.__geom.findAllMatches('**/EXIT*') + + def attachTo(self, other, rng): + otherExits = other._getExits() + entrances = self._getEntrances() + otherDoor = otherExits[0] + thisDoor = rng.choice(entrances) + geom = self.getGeom() + otherGeom = other.getGeom() + tempNode = otherDoor.attachNewNode('tempRotNode') + geom.reparentTo(tempNode) + geom.clearMat() + geom.setPos(Vec3(0) - thisDoor.getPos(geom)) + tempNode.setH(-thisDoor.getH(otherDoor)) + geom.wrtReparentTo(otherGeom.getParent()) + tempNode.removeNode() + + def getFloorCollName(self): + return '%s%s' % (StageRoom.FloorCollPrefix, self.roomNum) + + def initFloorCollisions(self): + allColls = self.getGeom().findAllMatches('**/+CollisionNode') + floorColls = [] + for coll in allColls: + bitmask = coll.node().getIntoCollideMask() + if not (bitmask & ToontownGlobals.FloorBitmask).isZero(): + floorColls.append(coll) + + if len(floorColls) > 0: + floorCollName = self.getFloorCollName() + others = self.getGeom().findAllMatches('**/%s' % floorCollName) + for other in others: + other.setName('%s_renamed' % floorCollName) + + for floorColl in floorColls: + floorColl.setName(floorCollName) + + def enterLtOff(self): + pass + + def exitLtOff(self): + pass + + def enterLtNotPresent(self): + pass + + def exitLtNotPresent(self): + pass + + def enterLtPresent(self): + pass + + def exitLtPresent(self): + pass diff --git a/toontown/coghq/StageRoomBase.py b/toontown/coghq/StageRoomBase.py new file mode 100755 index 00000000..bf28c4e5 --- /dev/null +++ b/toontown/coghq/StageRoomBase.py @@ -0,0 +1,16 @@ +from toontown.toonbase import ToontownGlobals + +class StageRoomBase: + + def __init__(self): + pass + + def setStageId(self, stageId): + self.stageId = stageId + self.cogTrack = ToontownGlobals.cogHQZoneId2dept(stageId) + + def setRoomId(self, roomId): + self.roomId = roomId + + def getCogTrack(self): + return self.cogTrack diff --git a/toontown/coghq/StageRoomSpecs.py b/toontown/coghq/StageRoomSpecs.py new file mode 100755 index 00000000..f0f1f91b --- /dev/null +++ b/toontown/coghq/StageRoomSpecs.py @@ -0,0 +1,125 @@ +from direct.showbase.PythonUtil import invertDict +from toontown.toonbase import ToontownGlobals +from toontown.coghq import NullCogs +from toontown.coghq import LawbotOfficeOilRoom_Battle00_Cogs +from toontown.coghq import LawbotOfficeOilRoom_Battle01_Cogs +from toontown.coghq import LawbotOfficeBoilerRoom_Battle00_Cogs +from toontown.coghq import LawbotOfficeBoilerRoom_Trap00_Cogs +from toontown.coghq import LawbotOfficeLobby_Trap00_Cogs +from toontown.coghq import LawbotOfficeDiamondRoom_Trap00_Cogs +from toontown.coghq import LawbotOfficeDiamondRoom_Battle00_Cogs +from toontown.coghq import LawbotOfficeGearRoom_Battle00_Cogs + +# Explicit room imports... +from toontown.coghq import LawbotOfficeEntrance_Action00 +from toontown.coghq import LawbotOfficeOilRoom_Battle00 +from toontown.coghq import LawbotOfficeOilRoom_Battle01 +from toontown.coghq import LawbotOfficeBoilerRoom_Security00 +from toontown.coghq import LawbotOfficeBoilerRoom_Battle00 +from toontown.coghq import LawbotOfficeGearRoom_Action00 +from toontown.coghq import LawbotOfficeLobby_Action00 +from toontown.coghq import LawbotOfficeGearRoom_Security00 +from toontown.coghq import LawbotOfficeLobby_Trap00 +from toontown.coghq import LawbotOfficeDiamondRoom_Security00 +from toontown.coghq import LawbotOfficeDiamondRoom_Trap00 +from toontown.coghq import LawbotOfficeGearRoom_Platform00 +from toontown.coghq import LawbotOfficeLobby_Lights00 +from toontown.coghq import LawbotOfficeBoilerRoom_Action01 +from toontown.coghq import LawbotOfficeDiamondRoom_Action00 +from toontown.coghq import LawbotOfficeDiamondRoom_Action01 +from toontown.coghq import LawbotOfficeLobby_Action01 +from toontown.coghq import LawbotOfficeDiamondRoom_Battle00 +from toontown.coghq import LawbotOfficeGearRoom_Battle00 + +def getStageRoomSpecModule(roomId): + return CashbotStageSpecModules[roomId] + + +def getCogSpecModule(roomId): + roomName = CashbotStageRoomId2RoomName[roomId] + return CogSpecModules.get(roomName, NullCogs) + + +def getNumBattles(roomId): + return roomId2numBattles[roomId] + + +CashbotStageRoomId2RoomName = {0: 'LawbotOfficeEntrance_Action00', + 1: 'LawbotOfficeOilRoom_Battle00', + 2: 'LawbotOfficeOilRoom_Battle01', + 3: 'LawbotOfficeBoilerRoom_Security00', + 4: 'LawbotOfficeBoilerRoom_Battle00', + 5: 'LawbotOfficeGearRoom_Action00', + 6: 'LawbotOfficeLobby_Action00', + 7: 'LawbotOfficeGearRoom_Security00', + 8: 'LawbotOfficeLobby_Trap00', + 9: 'LawbotOfficeDiamondRoom_Security00', + 10: 'LawbotOfficeDiamondRoom_Trap00', + 11: 'LawbotOfficeGearRoom_Platform00', + 12: 'LawbotOfficeLobby_Lights00', + 100: 'LawbotOfficeBoilerRoom_Action01', + 101: 'LawbotOfficeDiamondRoom_Action00', + 102: 'LawbotOfficeDiamondRoom_Action01', + 103: 'LawbotOfficeLobby_Action01', + 104: 'LawbotOfficeDiamondRoom_Battle00', + 105: 'LawbotOfficeGearRoom_Battle00'} +CashbotStageRoomName2RoomId = invertDict(CashbotStageRoomId2RoomName) +CashbotStageEntranceIDs = (0,) +CashbotStageMiddleRoomIDs = (1,) +CashbotStageFinalRoomIDs = (2,) +CashbotStageConnectorRooms = ('phase_11/models/lawbotHQ/LB_connector_7cubeL2', 'phase_11/models/lawbotHQ/LB_connector_7cubeLR') +CashbotStageSpecModules = {} +for roomName, roomId in CashbotStageRoomName2RoomId.items(): + CashbotStageSpecModules[roomId] = locals()[roomName] + +CogSpecModules = {'LawbotOfficeOilRoom_Battle00': LawbotOfficeOilRoom_Battle00_Cogs, + 'LawbotOfficeOilRoom_Battle01': LawbotOfficeOilRoom_Battle01_Cogs, + 'LawbotOfficeBoilerRoom_Battle00': LawbotOfficeBoilerRoom_Battle00_Cogs, + 'LawbotOfficeBoilerRoom_Trap00': LawbotOfficeBoilerRoom_Trap00_Cogs, + 'LawbotOfficeLobby_Trap00': LawbotOfficeLobby_Trap00_Cogs, + 'LawbotOfficeDiamondRoom_Trap00': LawbotOfficeDiamondRoom_Trap00_Cogs, + 'LawbotOfficeDiamondRoom_Battle00': LawbotOfficeDiamondRoom_Battle00_Cogs, + 'LawbotOfficeGearRoom_Battle00': LawbotOfficeGearRoom_Battle00_Cogs} +roomId2numBattles = {} +for roomName, roomId in CashbotStageRoomName2RoomId.items(): + if roomName not in CogSpecModules: + roomId2numBattles[roomId] = 0 + else: + cogSpecModule = CogSpecModules[roomName] + roomId2numBattles[roomId] = len(cogSpecModule.BattleCells) + +roomId2numCogs = {} +for roomName, roomId in CashbotStageRoomName2RoomId.items(): + if roomName not in CogSpecModules: + roomId2numCogs[roomId] = 0 + else: + cogSpecModule = CogSpecModules[roomName] + roomId2numCogs[roomId] = len(cogSpecModule.CogData) + +roomId2numCogLevels = {} +for roomName, roomId in CashbotStageRoomName2RoomId.items(): + if roomName not in CogSpecModules: + roomId2numCogLevels[roomId] = 0 + else: + cogSpecModule = CogSpecModules[roomName] + levels = 0 + for cogData in cogSpecModule.CogData: + levels += cogData['level'] + + roomId2numCogLevels[roomId] = levels + +roomId2numMeritCogLevels = {} +for roomName, roomId in CashbotStageRoomName2RoomId.items(): + if roomName not in CogSpecModules or roomId in (8, 10): + roomId2numMeritCogLevels[roomId] = 0 + else: + cogSpecModule = CogSpecModules[roomName] + levels = 0 + for cogData in cogSpecModule.CogData: + levels += cogData['level'] + + roomId2numMeritCogLevels[roomId] = levels + +middleRoomId2numBattles = {} +for roomId in CashbotStageMiddleRoomIDs: + middleRoomId2numBattles[roomId] = roomId2numBattles[roomId] diff --git a/toontown/coghq/StomperGlobals.py b/toontown/coghq/StomperGlobals.py new file mode 100755 index 00000000..9359f903 --- /dev/null +++ b/toontown/coghq/StomperGlobals.py @@ -0,0 +1,12 @@ +STOMPER_START = 0 +STOMPER_PAUSE = 1 +STOMPER_RESUME = 2 +STOMPER_RESET = 3 +STOMPER_STOMP = 4 +STOMPER_RISE = 5 +MotionLinear = 0 +MotionSinus = 1 +MotionHalfSinus = 2 +MotionSlowFast = 3 +MotionCrush = 4 +MotionSwitched = 5 diff --git a/toontown/coghq/__init__.py b/toontown/coghq/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/distributed/DelayDeletable.py b/toontown/distributed/DelayDeletable.py new file mode 100755 index 00000000..5b3782f6 --- /dev/null +++ b/toontown/distributed/DelayDeletable.py @@ -0,0 +1,31 @@ +from direct.distributed.DistributedObject import ESGenerating, ESGenerated, ESNum2Str + +class DelayDeletable: + DelayDeleteSerialGen = SerialNumGen() + + def delayDelete(self): + pass + + def acquireDelayDelete(self, name): + global ESGenerating + global ESGenerated + if not self._delayDeleteForceAllow and self.activeState not in (ESGenerating, ESGenerated): + self.notify.error('cannot acquire DelayDelete "%s" on %s because it is in state %s' % (name, self.__class__.__name__, ESNum2Str[self.activeState])) + if self.getDelayDeleteCount() == 0: + self.cr._addDelayDeletedDO(self) + token = DelayDeletable.DelayDeleteSerialGen.next() + self._token2delayDeleteName[token] = name + return token + + def releaseDelayDelete(self, token): + name = self._token2delayDeleteName.pop(token) + if len(self._token2delayDeleteName) == 0: + self.cr._removeDelayDeletedDO(self) + if self._delayDeleted: + self.disableAnnounceAndDelete() + + def getDelayDeleteNames(self): + return self._token2delayDeleteName.values() + + def forceAllowDelayDelete(self): + self._delayDeleteForceAllow = True diff --git a/toontown/distributed/DelayDelete.py b/toontown/distributed/DelayDelete.py new file mode 100755 index 00000000..59f4924c --- /dev/null +++ b/toontown/distributed/DelayDelete.py @@ -0,0 +1,42 @@ + + +class DelayDelete: + + def __init__(self, distObj, name): + self._distObj = distObj + self._name = name + self._token = self._distObj.acquireDelayDelete(name) + + def getObject(self): + return self._distObj + + def getName(self): + return self._name + + def destroy(self): + token = self._token + del self._token + self._distObj.releaseDelayDelete(token) + del self._distObj + del self._name + + +def cleanupDelayDeletes(interval): + if hasattr(interval, 'delayDelete'): + delayDelete = interval.delayDelete + del interval.delayDelete + if type(delayDelete) == type([]): + for i in delayDelete: + i.destroy() + + else: + delayDelete.destroy() + if hasattr(interval, 'delayDeletes'): + delayDeletes = interval.delayDeletes + del interval.delayDeletes + if type(delayDeletes) == type([]): + for i in delayDeletes: + i.destroy() + + else: + delayDeletes.destroy() diff --git a/toontown/distributed/DistributedTimer.py b/toontown/distributed/DistributedTimer.py new file mode 100755 index 00000000..98214e5f --- /dev/null +++ b/toontown/distributed/DistributedTimer.py @@ -0,0 +1,32 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from panda3d.core import * +from direct.distributed.ClockDelta import * +import time + +class DistributedTimer(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTimer') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + base.cr.DTimer = self + + def delete(self): + DistributedObject.DistributedObject.delete(self) + base.cr.DTimer = None + return + + def setStartTime(self, time): + self.startTime = time + print 'TIMER startTime %s' % time + + def getStartTime(self): + return self.startTime + + def getTime(self): + elapsedTime = globalClockDelta.localElapsedTime(self.startTime, bits=32) + return elapsedTime diff --git a/toontown/distributed/DistributedTimerAI.py b/toontown/distributed/DistributedTimerAI.py new file mode 100755 index 00000000..50f92a59 --- /dev/null +++ b/toontown/distributed/DistributedTimerAI.py @@ -0,0 +1,17 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.distributed.ClockDelta import * +import time + +class DistributedTimerAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedTimerAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.setStartTime(globalClockDelta.getRealNetworkTime(bits = 32)) + + def setStartTime(self, time): + self.startTime = time + + def getStartTime(self): + return self.startTime diff --git a/toontown/distributed/HoodMgr.py b/toontown/distributed/HoodMgr.py new file mode 100755 index 00000000..1ae151da --- /dev/null +++ b/toontown/distributed/HoodMgr.py @@ -0,0 +1,300 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import DirectObject +from panda3d.core import * +import random + +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals + + +class HoodMgr(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('HoodMgr') + + ToontownCentralInitialDropPoints = ( + [-90.7, -60, 0.025, 102.575, 0, 0], + [-91.4, -40.5, -3.948, 125.763, 0, 0], + [-107.8, -17.8, -1.937, 149.456, 0, 0], + [-108.7, 12.8, -1.767, 158.756, 0, 0], + [-42.1, -22.8, -1.328, -248.1, 0, 0], + [-35.2, -60.2, 0.025, -265.639, 0, 0] + ) + ToontownCentralHQDropPoints = ( + [-43.5, 42.6, -0.55, -100.454, 0, 0], + [-53.0, 12.5, -2.948, 281.502, 0, 0], + [-40.3, -18.5, -0.913, -56.674, 0, 0], + [-1.9, -37.0, 0.025, -23.43, 0, 0], + [1.9, -5.9, 4, -37.941, 0, 0] + ) + ToontownCentralTunnelDropPoints = ( + [-28.3, 40.1, 0.25, 17.25, 0, 0], + [-63.75, 58.96, -0.5, -23.75, 0, 0], + [-106.93, 17.66, -2.2, 99, 0, 0], + [-116.0, -21.5, -0.038, 50, 0, 0], + [74.88, -115, 2.53, -224.41, 0, 0], + [30.488, -101.5, 2.53, -179.23, 0, 0] + ) + dropPoints = { + ToontownGlobals.DonaldsDock: ( + [-28, -2.5, 5.8, 120, 0, 0], + [-22, 13, 5.8, 155.6, 0, 0], + [67, 47, 5.7, 134.7, 0, 0], + [62, 19, 5.7, 97, 0, 0], + [66, -27, 5.7, 80.5, 0, 0], + [-114, -7, 5.7, -97, 0, 0], + [-108, 36, 5.7, -153.8, 0, 0], + [-116, -46, 5.7, -70.1, 0, 0], + [-63, -79, 5.7, -41.2, 0, 0], + [-2, -79, 5.7, 57.4, 0, 0], + [-38, -78, 5.7, 9.1, 0, 0] + ), + ToontownGlobals.ToontownCentral: ( + [-60, -8, 1.3, -90, 0, 0], + [-66, -9, 1.3, -274, 0, 0], + [17, -28, 4.1, -44, 0, 0], + [87.7, -22, 4, 66, 0, 0], + [-9.6, 61.1, 0, 132, 0, 0], + [-109.0, -2.5, -1.656, -90, 0, 0], + [-35.4, -81.3, 0.5, -4, 0, 0], + [-103, 72, 0, -141, 0, 0], + [93.5, -148.4, 2.5, 43, 0, 0], + [25, 123.4, 2.55, 272, 0, 0], + [48, 39, 4, 201, 0, 0], + [-80, -61, 0.1, -265, 0, 0], + [-46.875, 43.68, -1.05, 124, 0, 0], + [34, -105, 2.55, 45, 0, 0], + [16, -75, 2.55, 56, 0, 0], + [-27, -56, 0.1, 45, 0, 0], + [100, 27, 4.1, 150, 0, 0], + [-70, 4.6, -1.9, 90, 0, 0], + [-130.7, 50, 0.55, -111, 0, 0] + ), + ToontownGlobals.TheBrrrgh: ( + [35, -32, 6.2, 138, 0, 0], + [26, -105, 6.2, -339, 0, 0], + [-29, -139, 6.2, -385, 0, 0], + [-79, -123, 6.2, -369, 0, 0], + [-114, -86, 3, -54, 0, 0], + [-136, 9, 6.2, -125, 0, 0], + [-75, 92, 6.2, -187, 0, 0], + [-7, 75, 6.2, -187, 0, 0], + [-106, -42, 8.6, -111, 0, 0], + [-116, -44, 8.3, -20, 0, 0] + ), + ToontownGlobals.MinniesMelodyland: ( + [86, 44, -13.5, 121.1, 0, 0], + [88, -8, -13.5, 91, 0, 0], + [92, -76, -13.5, 62.5, 0, 0], + [53, -112, 6.5, 65.8, 0, 0], + [-69, -71, 6.5, -67.2, 0, 0], + [-75, 21, 6.5, -100.9, 0, 0], + [-21, 72, 6.5, -129.5, 0, 0], + [56, 72, 6.5, 138.2, 0, 0], + [-41, 47, 6.5, -98.9, 0, 0] + ), + ToontownGlobals.DaisyGardens: ( + [0, 0, 0, -10.5, 0, 0], + [76, 35, 1.1, -30.2, 0, 0], + [97, 106, 0, 51.4, 0, 0], + [51, 180, 10, 22.6, 0, 0], + [-14, 203, 10, 85.6, 0, 0], + [-58, 158, 10, -146.9, 0, 0], + [-86, 128, 0, -178.9, 0, 0], + [-64, 65, 0, 17.7, 0, 0], + [-13, 39, 0, -15.7, 0, 0], + [-12, 193, 0, -112.4, 0, 0], + [87, 128, 0, 45.4, 0, 0] + ), + ToontownGlobals.DonaldsDreamland: ( + [77, 91, 0, 124.4, 0, 0], + [29, 92, 0, -154.5, 0, 0], + [-28, 49, -16.4, -142, 0, 0], + [21, 40, -16, -65.1, 0, 0], + [48, 27, -15.4, -161, 0, 0], + [-2, -22, -15.2, -132.1, 0, 0], + [-92, -88, 0, -116.3, 0, 0], + [-56, -93, 0, -21.5, 0, 0], + [20, -88, 0, -123.4, 0, 0], + [76, -90, 0, 11, 0, 0] + ), + ToontownGlobals.GoofySpeedway: ( + [-0.7, 62, 0.08, 182, 0, 0], + [-1, -30, 0.06, 183, 0, 0], + [-13, -120, 0, 307, 0, 0], + [16.4, -120, 0, 65, 0, 0], + [-0.5, -90, 0, 182, 0, 0], + [-30, -25, -0.373, 326, 0, 0], + [29, -17, -0.373, 32, 0, 0] + ), + ToontownGlobals.GolfZone: ( + [-49.6, 102, 0, 162, 0, 0], + [-22.8, 36.6, 0, 157.5, 0, 0], + [40, 51, 0, 185, 0, 0], + [48.3, 122.2, 0, 192, 0, 0], + [106.3, 69.2, 0, 133, 0, 0], + [-81.5, 47.2, 0, 183, 0, 0], + [-80.5, -84.2, 0, 284, 0, 0], + [73, -111, 0, 354, 0, 0] + ), + ToontownGlobals.OutdoorZone: ( + [-165.8, 108, 0.025, 252, 0, 0], + [21, 130, 0.16, 170, 0, 0], + [93, 78.5, 0.23, 112, 0, 0], + [79, -1.6, 0.75, 163, 0, 0], + [10, 33, 5.32, 130.379, 0, 0], + [-200, -42, 0.025, 317.543, 0, 0], + [-21, -65, 0.335, -18, 0, 0], + [23, 68.5, 4.51, -22.808, 0, 0] + ), + ToontownGlobals.Tutorial: ( + [130.9, -8.6, -1.3, 105.5, 0, 0], + ), + ToontownGlobals.SellbotHQ: ( + [56.910, -173.576, -7.037, 15.061, 0, 0], + [-53.105, -197.259, -4.812, 25.870, 0, 0], + [-103, -118, 0.367, 622.422, 0, 0], + [-5.361, -228.596, -10.817, -118.934, 0, 0], + [-8.2536, -175.53, -19.5944, -313.592, 0, 0], + [66.7811, -96.8434, 0.286679, -567.363, 0, 0] + ), + ToontownGlobals.CashbotHQ: ( + [102, -437, -23.439, 0, 0, 0], + [124, -437, -23.439, 0, 0, 0], + [110, -446, -23.439, 0, 0, 0], + [132, -446, -23.439, 0, 0, 0] + ), + ToontownGlobals.LawbotHQ: ( + [77.5, 129.13, -68.4, -166.6, 0, 0], + [-57.7, 80.75, -68.4, -139.2, 0, 0], + [203.3, 46.36, -68.4, -213.37, 0, 0], + [88.2, -336.52, -68.4, -720.4, 0, 0], + [232.77, -305.33, -68.4, -651, 0, 0], + [-20.16, -345.76, -68.4, -777.98, 0, 0] + ) + } + DefaultDropPoint = [0, 0, 0, 0, 0, 0] + hoodName2Id = { + 'dd': ToontownGlobals.DonaldsDock, + 'tt': ToontownGlobals.ToontownCentral, + 'br': ToontownGlobals.TheBrrrgh, + 'mm': ToontownGlobals.MinniesMelodyland, + 'dg': ToontownGlobals.DaisyGardens, + 'oz': ToontownGlobals.OutdoorZone, + 'ff': ToontownGlobals.FunnyFarm, + 'gs': ToontownGlobals.GoofySpeedway, + 'dl': ToontownGlobals.DonaldsDreamland, + 'bosshq': ToontownGlobals.BossbotHQ, + 'sellhq': ToontownGlobals.SellbotHQ, + 'cashhq': ToontownGlobals.CashbotHQ, + 'lawhq': ToontownGlobals.LawbotHQ, + 'gz': ToontownGlobals.GolfZone + } + hoodId2Name = { + ToontownGlobals.DonaldsDock: 'dd', + ToontownGlobals.ToontownCentral: 'tt', + ToontownGlobals.Tutorial: 'tt', + ToontownGlobals.TheBrrrgh: 'br', + ToontownGlobals.MinniesMelodyland: 'mm', + ToontownGlobals.DaisyGardens: 'dg', + ToontownGlobals.OutdoorZone: 'oz', + ToontownGlobals.FunnyFarm: 'ff', + ToontownGlobals.GoofySpeedway: 'gs', + ToontownGlobals.DonaldsDreamland: 'dl', + ToontownGlobals.BossbotHQ: 'bosshq', + ToontownGlobals.SellbotHQ: 'sellhq', + ToontownGlobals.CashbotHQ: 'cashhq', + ToontownGlobals.LawbotHQ: 'lawhq', + ToontownGlobals.GolfZone: 'gz' + } + dbgDropMode = 0 + currentDropPoint = 0 + + def __init__(self, cr): + self.cr = cr + + def getDropPoint(self, dropPointList): + if self.dbgDropMode == 0: + return random.choice(dropPointList) + else: + droppnt = self.currentDropPoint % len(dropPointList) + self.currentDropPoint = (self.currentDropPoint + 1) % len(dropPointList) + return dropPointList[droppnt] + + def getPlaygroundCenterFromId(self, hoodId): + dropPointList = self.dropPoints.get(hoodId, None) + if dropPointList: + return self.getDropPoint(dropPointList) + else: + self.notify.warning('getPlaygroundCenterFromId: No such hood name as: ' + str(hoodId)) + return self.DefaultDropPoint + + def getIdFromName(self, hoodName): + id = self.hoodName2Id.get(hoodName) + if id: + return id + else: + self.notify.error('No such hood name as: %s' % hoodName) + + def getNameFromId(self, hoodId): + name = self.hoodId2Name.get(hoodId) + if name: + return name + else: + self.notify.error('No such hood id as: %s' % hoodId) + + def getFullnameFromId(self, hoodId): + hoodId = ZoneUtil.getCanonicalZoneId(hoodId) + return ToontownGlobals.hoodNameMap[hoodId][-1] + + def addLinkTunnelHooks(self, hoodPart, nodeList): + tunnelOriginList = [] + for i in nodeList: + linkTunnelNPC = i.findAllMatches('**/linktunnel*') + for p in xrange(linkTunnelNPC.getNumPaths()): + linkTunnel = linkTunnelNPC.getPath(p) + name = linkTunnel.getName() + nameParts = name.split('_') + hoodStr = nameParts[1] + zoneStr = nameParts[2] + hoodId = self.getIdFromName(hoodStr) + zoneId = int(zoneStr) + linkSphere = linkTunnel.find('**/tunnel_trigger') + if linkSphere.isEmpty(): + linkSphere = linkTunnel.find('**/tunnel_sphere') + if not linkSphere.isEmpty(): + cnode = linkSphere.node() + cnode.setName('tunnel_trigger_' + hoodStr + '_' + zoneStr) + cnode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.GhostBitmask) + else: + linkSphere = linkTunnel.find('**/tunnel_trigger_' + hoodStr + '_' + zoneStr) + if linkSphere.isEmpty(): + self.notify.error('tunnel_trigger not found') + tunnelOrigin = linkTunnel.find('**/tunnel_origin') + if tunnelOrigin.isEmpty(): + self.notify.error('tunnel_origin not found') + tunnelOriginPlaceHolder = render.attachNewNode('toph_' + hoodStr + '_' + zoneStr) + tunnelOriginList.append(tunnelOriginPlaceHolder) + tunnelOriginPlaceHolder.setPos(tunnelOrigin.getPos(render)) + tunnelOriginPlaceHolder.setHpr(tunnelOrigin.getHpr(render)) + hood = base.localAvatar.cr.playGame.hood + if ZoneUtil.tutorialDict: + how = 'teleportIn' + tutorialFlag = 1 + else: + how = 'tunnelIn' + tutorialFlag = 0 + hoodPart.accept('enter' + linkSphere.getName(), hoodPart.handleEnterTunnel, [{'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': how, + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None, + 'tunnelOrigin': tunnelOriginPlaceHolder, + 'tutorial': tutorialFlag}]) + + return tunnelOriginList + + def extractGroupName(self, groupFullName): + return groupFullName.split(':', 1)[0] + + def makeLinkTunnelName(self, hoodId, currentZone): + return '**/toph_' + self.getNameFromId(hoodId) + '_' + str(currentZone) diff --git a/toontown/distributed/PlayGame.py b/toontown/distributed/PlayGame.py new file mode 100755 index 00000000..03e74023 --- /dev/null +++ b/toontown/distributed/PlayGame.py @@ -0,0 +1,557 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task.Task import Task +from ToontownMsgTypes import * +from toontown.toonbase import ToontownGlobals +from toontown.hood import TTHood +from toontown.hood import DDHood +from toontown.hood import MMHood +from toontown.hood import BRHood +from toontown.hood import DGHood +from toontown.hood import DLHood +from toontown.hood import GSHood +from toontown.hood import OZHood +from toontown.hood import GZHood +from toontown.hood import SellbotHQ, CashbotHQ, LawbotHQ, BossbotHQ +from toontown.hood import TutorialHood +from direct.task import TaskManagerGlobal +from toontown.hood import QuietZoneState +from toontown.hood import ZoneUtil +from toontown.hood import EstateHood +from toontown.hood import PartyHood +from toontown.toonbase import TTLocalizer +from toontown.parties.PartyGlobals import GoToPartyStatus +from toontown.dna.DNAParser import * + +class PlayGame(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('PlayGame') + Hood2ClassDict = {ToontownGlobals.ToontownCentral: TTHood.TTHood, + ToontownGlobals.DonaldsDock: DDHood.DDHood, + ToontownGlobals.TheBrrrgh: BRHood.BRHood, + ToontownGlobals.MinniesMelodyland: MMHood.MMHood, + ToontownGlobals.DaisyGardens: DGHood.DGHood, + ToontownGlobals.DonaldsDreamland: DLHood.DLHood, + ToontownGlobals.GoofySpeedway: GSHood.GSHood, + ToontownGlobals.OutdoorZone: OZHood.OZHood, + ToontownGlobals.Tutorial: TutorialHood.TutorialHood, + ToontownGlobals.MyEstate: EstateHood.EstateHood, + ToontownGlobals.BossbotHQ: BossbotHQ.BossbotHQ, + ToontownGlobals.SellbotHQ: SellbotHQ.SellbotHQ, + ToontownGlobals.CashbotHQ: CashbotHQ.CashbotHQ, + ToontownGlobals.LawbotHQ: LawbotHQ.LawbotHQ, + ToontownGlobals.GolfZone: GZHood.GZHood, + ToontownGlobals.PartyHood: PartyHood.PartyHood} + Hood2StateDict = {ToontownGlobals.ToontownCentral: 'TTHood', + ToontownGlobals.DonaldsDock: 'DDHood', + ToontownGlobals.TheBrrrgh: 'BRHood', + ToontownGlobals.MinniesMelodyland: 'MMHood', + ToontownGlobals.DaisyGardens: 'DGHood', + ToontownGlobals.DonaldsDreamland: 'DLHood', + ToontownGlobals.GoofySpeedway: 'GSHood', + ToontownGlobals.OutdoorZone: 'OZHood', + ToontownGlobals.Tutorial: 'TutorialHood', + ToontownGlobals.MyEstate: 'EstateHood', + ToontownGlobals.BossbotHQ: 'BossbotHQ', + ToontownGlobals.SellbotHQ: 'SellbotHQ', + ToontownGlobals.CashbotHQ: 'CashbotHQ', + ToontownGlobals.LawbotHQ: 'LawbotHQ', + ToontownGlobals.GolfZone: 'GZHood', + ToontownGlobals.PartyHood: 'PartyHood'} + + def __init__(self, parentFSM, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.place = None + self.fsm = ClassicFSM.ClassicFSM('PlayGame', [State.State('start', self.enterStart, self.exitStart, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['TTHood', + 'DDHood', + 'BRHood', + 'MMHood', + 'DGHood', + 'DLHood', + 'GSHood', + 'OZHood', + 'GZHood', + 'SellbotHQ', + 'CashbotHQ', + 'LawbotHQ', + 'BossbotHQ', + 'TutorialHood', + 'EstateHood', + 'PartyHood']), + State.State('TTHood', self.enterTTHood, self.exitTTHood, ['quietZone']), + State.State('DDHood', self.enterDDHood, self.exitDDHood, ['quietZone']), + State.State('BRHood', self.enterBRHood, self.exitBRHood, ['quietZone']), + State.State('MMHood', self.enterMMHood, self.exitMMHood, ['quietZone']), + State.State('DGHood', self.enterDGHood, self.exitDGHood, ['quietZone']), + State.State('DLHood', self.enterDLHood, self.exitDLHood, ['quietZone']), + State.State('GSHood', self.enterGSHood, self.exitGSHood, ['quietZone']), + State.State('OZHood', self.enterOZHood, self.exitOZHood, ['quietZone']), + State.State('GZHood', self.enterGZHood, self.exitGZHood, ['quietZone']), + State.State('BossbotHQ', self.enterBossbotHQ, self.exitBossbotHQ, ['quietZone']), + State.State('SellbotHQ', self.enterSellbotHQ, self.exitSellbotHQ, ['quietZone']), + State.State('CashbotHQ', self.enterCashbotHQ, self.exitCashbotHQ, ['quietZone']), + State.State('LawbotHQ', self.enterLawbotHQ, self.exitLawbotHQ, ['quietZone']), + State.State('TutorialHood', self.enterTutorialHood, self.exitTutorialHood, ['quietZone']), + State.State('EstateHood', self.enterEstateHood, self.exitEstateHood, ['quietZone']), + State.State('PartyHood', self.enterPartyHood, self.exitPartyHood, ['quietZone'])], 'start', 'start') + self.fsm.enterInitialState() + self.parentFSM = parentFSM + self.parentFSM.getStateNamed('playGame').addChild(self.fsm) + self.hoodDoneEvent = 'hoodDone' + self.hood = None + self.quietZoneDoneEvent = uniqueName('quietZoneDone') + self.quietZoneStateData = None + return + + def enter(self, hoodId, zoneId, avId): + if hoodId == ToontownGlobals.Tutorial: + loaderName = 'townLoader' + whereName = 'toonInterior' + elif hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(avId, zoneId) + return + elif hoodId == ToontownGlobals.PartyHood: + self.getPartyZoneAndGoToParty(avId, zoneId) + return + else: + loaderName = ZoneUtil.getLoaderName(zoneId) + whereName = ZoneUtil.getToonWhereName(zoneId) + self.fsm.request('quietZone', [{'loader': loaderName, + 'where': whereName, + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': avId}]) + return + + def exit(self): + if base.placeBeforeObjects and self.quietZoneStateData: + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + self.ignore(self.quietZoneDoneEvent) + return + + def load(self): + pass + + def loadDnaStoreTutorial(self): + self.dnaStore = DNAStorage() + files = ('phase_3.5/dna/storage_tutorial.pdna', 'phase_3.5/dna/storage_interior.pdna') + dnaBulk = DNABulkLoader(self.dnaStore, files) + dnaBulk.loadDNAFiles() + + def loadDnaStore(self): + if not hasattr(self, 'dnaStore'): + self.dnaStore = DNAStorage() + files = ('phase_4/dna/storage.pdna', 'phase_3.5/dna/storage_interior.pdna') + dnaBulk = DNABulkLoader(self.dnaStore, files) + dnaBulk.loadDNAFiles() + self.dnaStore.storeFont('humanist', ToontownGlobals.getInterfaceFont()) + self.dnaStore.storeFont('mickey', ToontownGlobals.getSignFont()) + self.dnaStore.storeFont('suit', ToontownGlobals.getSuitFont()) + + def unloadDnaStore(self): + if hasattr(self, 'dnaStore'): + del self.dnaStore + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def unload(self): + self.unloadDnaStore() + if self.hood: + self.notify.info('Aggressively cleaning up hood: %s' % self.hood) + self.hood.exit() + self.hood.unload() + self.hood = None + base.cr.cache.flush() + + def enterStart(self): + pass + + def exitStart(self): + pass + + def handleHoodDone(self): + doneStatus = self.hood.getDoneStatus() + shardId = doneStatus['shardId'] + if shardId != None: + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + base.transitions.fadeOut(0) + return + if doneStatus['where'] == 'party': + self.getPartyZoneAndGoToParty(doneStatus['avId'], doneStatus['zoneId']) + return + how = doneStatus['how'] + if how in ['tunnelIn', + 'teleportIn', + 'doorIn', + 'elevatorIn']: + self.fsm.request('quietZone', [doneStatus]) + else: + self.notify.error('Exited hood with unexpected mode %s' % how) + return + + def _destroyHood(self): + self.unload() + + def enterQuietZone(self, requestStatus): + self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone) + self.quietZoneStateData = QuietZoneState.QuietZoneState(self.quietZoneDoneEvent) + self._quietZoneLeftEvent = self.quietZoneStateData.getQuietZoneLeftEvent() + if base.placeBeforeObjects: + self.acceptOnce(self._quietZoneLeftEvent, self.handleLeftQuietZone) + self._enterWaitForSetZoneResponseMsg = self.quietZoneStateData.getEnterWaitForSetZoneResponseMsg() + self.acceptOnce(self._enterWaitForSetZoneResponseMsg, self.handleWaitForSetZoneResponse) + self.quietZoneStateData.load() + self.quietZoneStateData.enter(requestStatus) + + def exitQuietZone(self): + self.ignore(self._quietZoneLeftEvent) + self.ignore(self._enterWaitForSetZoneResponseMsg) + if not base.placeBeforeObjects: + self.ignore(self.quietZoneDoneEvent) + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + return + + def handleWaitForSetZoneResponse(self, requestStatus): + hoodId = requestStatus['hoodId'] + canonicalHoodId = ZoneUtil.getCanonicalZoneId(hoodId) + toHoodPhrase = ToontownGlobals.hoodNameMap[canonicalHoodId][0] + hoodName = ToontownGlobals.hoodNameMap[canonicalHoodId][-1] + zoneId = requestStatus['zoneId'] + loaderName = requestStatus['loader'] + avId = requestStatus.get('avId', -1) + ownerId = requestStatus.get('ownerId', avId) + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: NEIGHBORHOODS: Visit %s' % hoodName) + count = ToontownGlobals.hoodCountMap[canonicalHoodId] + if loaderName == 'safeZoneLoader': + count += ToontownGlobals.safeZoneCountMap[canonicalHoodId] + elif loaderName == 'townLoader': + count += ToontownGlobals.townCountMap[canonicalHoodId] + if not loader.inBulkBlock: + if hoodId == ToontownGlobals.MyEstate: + if avId == -1: + loader.beginBulkLoad('hood', TTLocalizer.HeadingToYourEstate, count, 1, TTLocalizer.TIP_ESTATE, zoneId) + else: + owner = base.cr.identifyAvatar(ownerId) + if owner == None: + friend = base.cr.identifyAvatar(avId) + if friend != None: + avName = friend.getName() + loader.beginBulkLoad('hood', TTLocalizer.HeadingToFriend % avName, count, 1, TTLocalizer.TIP_ESTATE, zoneId) + else: + self.notify.warning("we can't perform this teleport") + return + else: + avName = owner.getName() + loader.beginBulkLoad('hood', TTLocalizer.HeadingToEstate % avName, count, 1, TTLocalizer.TIP_ESTATE, zoneId) + elif ZoneUtil.isCogHQZone(zoneId): + loader.beginBulkLoad('hood', TTLocalizer.HeadingToHood % {'to': toHoodPhrase, + 'hood': hoodName}, count, 1, TTLocalizer.TIP_COGHQ, zoneId) + elif ZoneUtil.isGoofySpeedwayZone(zoneId): + loader.beginBulkLoad('hood', TTLocalizer.HeadingToHood % {'to': toHoodPhrase, + 'hood': hoodName}, count, 1, TTLocalizer.TIP_KARTING, zoneId) + else: + loader.beginBulkLoad('hood', TTLocalizer.HeadingToHood % {'to': toHoodPhrase, + 'hood': hoodName}, count, 1, TTLocalizer.TIP_GENERAL, zoneId) + if hoodId == ToontownGlobals.Tutorial: + self.loadDnaStoreTutorial() + else: + if not hasattr(self, 'dnaStore'): + self.loadDnaStore() + hoodClass = self.getHoodClassByNumber(canonicalHoodId) + self.hood = hoodClass(self.fsm, self.hoodDoneEvent, self.dnaStore, hoodId) + self.hood.load() + self.hood.loadLoader(requestStatus) + if not base.placeBeforeObjects: + loader.endBulkLoad('hood') + return + + def handleLeftQuietZone(self): + status = self.quietZoneStateData.getRequestStatus() + hoodId = ZoneUtil.getCanonicalZoneId(status['hoodId']) + hoodState = self.getHoodStateByNumber(hoodId) + self.fsm.request(hoodState, [status]) + + def handleQuietZoneDone(self): + if base.placeBeforeObjects: + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + loader.endBulkLoad('hood') + else: + self.handleLeftQuietZone() + return + + def enterTTHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitTTHood(self): + self._destroyHood() + + def enterDDHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitDDHood(self): + self._destroyHood() + + def enterMMHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitMMHood(self): + self._destroyHood() + + def enterBRHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitBRHood(self): + self._destroyHood() + + def enterDGHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitDGHood(self): + self._destroyHood() + + def enterDLHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitDLHood(self): + self._destroyHood() + + def enterGSHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitGSHood(self): + self._destroyHood() + + def enterOZHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitOZHood(self): + self._destroyHood() + + def enterGZHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitGZHood(self): + self._destroyHood() + + def enterSellbotHQ(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitSellbotHQ(self): + self._destroyHood() + + def enterCashbotHQ(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitCashbotHQ(self): + self._destroyHood() + + def enterLawbotHQ(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitLawbotHQ(self): + self._destroyHood() + + def enterBossbotHQ(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitBossbotHQ(self): + self._destroyHood() + + def enterTutorialHood(self, requestStatus): + messenger.send('toonArrivedTutorial') + self.accept(self.hoodDoneEvent, self.handleHoodDone) + base.localAvatar.book.obscureButton(1) + base.localAvatar.book.setSafeMode(1) + base.localAvatar.laffMeter.obscure(1) + base.localAvatar.chatMgr.obscure(1, 1) + base.localAvatar.obscureFriendsListButton(1) + requestStatus['how'] = 'tutorial' + self.hood.enter(requestStatus) + + def exitTutorialHood(self): + self.unloadDnaStore() + self._destroyHood() + base.localAvatar.book.obscureButton(0) + base.localAvatar.book.setSafeMode(0) + base.localAvatar.laffMeter.obscure(0) + base.localAvatar.chatMgr.obscure(0, 0) + base.localAvatar.obscureFriendsListButton(-1) + + def enterEstateHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitEstateHood(self): + self._destroyHood() + + def getEstateZoneAndGoHome(self, avId, zoneId): + self.doneStatus = {'avId': avId, + 'zoneId': zoneId, + 'hoodId': ToontownGlobals.MyEstate, + 'loader': 'safeZoneLoader', + 'how': 'teleportIn', + 'shardId': None} + self.acceptOnce('setLocalEstateZone', self.goHome) + if avId > 0: + base.cr.estateMgr.getLocalEstateZone(avId) + else: + base.cr.estateMgr.getLocalEstateZone(base.localAvatar.getDoId()) + return + + def goHome(self, ownerId, zoneId): + self.notify.debug('goHome ownerId = %s' % ownerId) + if ownerId > 0 and ownerId != base.localAvatar.doId and not base.cr.isFriend(ownerId): + self.doneStatus['failed'] = 1 + taskMgr.remove('goHomeFailed') + taskMgr.add(self.goHomeFailed, 'goHomeFailed') + return + if ownerId == 0 and zoneId == 0: + self.doneStatus['failed'] = 1 + self.goHomeFailed(None) + return + if self.doneStatus['zoneId'] != zoneId: + self.doneStatus['where'] = 'house' + else: + self.doneStatus['where'] = 'estate' + self.doneStatus['ownerId'] = ownerId + self.fsm.request('quietZone', [self.doneStatus]) + return + + def goHomeFailed(self, task): + self.notify.debug('goHomeFailed') + failedToVisitAvId = self.doneStatus.get('avId') + if failedToVisitAvId > 0: + message = TTLocalizer.EstateTeleportFailedNotFriends % base.cr.identifyAvatar(failedToVisitAvId).getName() + else: + message = TTLocalizer.EstateTeleportFailed + self.notify.debug('goHomeFailed, why =: %s' % message) + self.ignore('setLocalEstateZone') + zoneId = base.localAvatar.lastHood + loaderName = ZoneUtil.getLoaderName(zoneId) + whereName = ZoneUtil.getToonWhereName(zoneId) + base.localAvatar.setSystemMessage(0, message) + self.fsm.request('quietZone', [{'loader': loaderName, + 'where': whereName, + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None}]) + return Task.done + + def enterPartyHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + requestStatus['where'] = 'party' + self.hood.enter(requestStatus) + + def exitPartyHood(self): + self._destroyHood() + + def getPartyZoneAndGoToParty(self, avId, zoneId): + self.doneStatus = {'avId': avId, + 'zoneId': zoneId, + 'hoodId': ToontownGlobals.PartyHood, + 'loader': 'safeZoneLoader', + 'how': 'teleportIn', + 'shardId': None} + if avId < 0: + avId = base.localAvatar.getDoId() + base.cr.partyManager.requestPartyZone(avId, zoneId, callback=self.goToParty) + return + + def goToParty(self, ownerId, partyId, zoneId): + if ownerId == 0 or partyId == 0 or zoneId == 0: + self.doneStatus['where'] = 'playground' + else: + self.doneStatus['where'] = 'party' + self.doneStatus['ownerId'] = ownerId + self.doneStatus['partyId'] = partyId + self.doneStatus['zoneId'] = zoneId + self.fsm.request('quietZone', [self.doneStatus]) + + def goToPartyFailed(self, reason): + self.notify.debug('goToPartyFailed') + failedToVisitAvId = self.doneStatus.get('avId') + message = base.cr.partyManager.getGoToPartyFailedMessage(reason) + self.notify.debug('goToPartyFailed, why =: %s' % message) + self.ignore('gotLocalPartyZone') + zoneId = base.localAvatar.lastHood + loaderName = ZoneUtil.getLoaderName(zoneId) + whereName = ZoneUtil.getToonWhereName(zoneId) + base.localAvatar.setSystemMessage(0, message) + self.fsm.request('quietZone', [{'loader': loaderName, + 'where': whereName, + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None}]) + return Task.done + + def getCatalogCodes(self, category): + numCodes = self.dnaStore.getNumCatalogCodes(category) + codes = [] + for i in xrange(numCodes): + codes.append(self.dnaStore.getCatalogCode(category, i)) + + return codes + + def getNodePathList(self, catalogGroup): + result = [] + codes = self.getCatalogCodes(catalogGroup) + for code in codes: + np = self.dnaStore.findNode(code) + result.append(np) + + return result + + def getNodePathDict(self, catalogGroup): + result = {} + codes = self.getCatalogCodes(catalogGroup) + for code in codes: + np = self.dnaStore.findNode(code) + result[code] = np + + return result + + def getHoodClassByNumber(self, hoodNumber): + return self.Hood2ClassDict[hoodNumber] + + def getHoodStateByNumber(self, hoodNumber): + return self.Hood2StateDict[hoodNumber] + + def setPlace(self, place): + self.place = place + if self.place: + messenger.send('playGameSetPlace') + + def getPlace(self): + return self.place + + def getPlaceId(self): + return self.hood.hoodId if self.hood else None diff --git a/toontown/distributed/ShardStatusReceiver.py b/toontown/distributed/ShardStatusReceiver.py new file mode 100755 index 00000000..e297cdbb --- /dev/null +++ b/toontown/distributed/ShardStatusReceiver.py @@ -0,0 +1,16 @@ +class ShardStatusReceiver: + def __init__(self, air): + self.air = air + + self.shards = {} + + # Accept the shardStatus event: + self.air.accept('shardStatus', self.handleShardStatus) + + self.air.sendNetEvent('queryShardStatus') + + def handleShardStatus(self, channel, status): + self.shards.setdefault(channel, {}).update(status) + + def getShards(self): + return self.shards diff --git a/toontown/distributed/ToontownClientRepository.py b/toontown/distributed/ToontownClientRepository.py new file mode 100755 index 00000000..dd35209b --- /dev/null +++ b/toontown/distributed/ToontownClientRepository.py @@ -0,0 +1,918 @@ +import types +import time +from direct.distributed.ClockDelta import * +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.interval.IntervalGlobal import ivalMgr +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedSmoothNode +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.PyDatagramIterator import PyDatagramIterator +from direct.task import Task +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.showbase.PythonUtil import Functor, ScratchPad +from direct.showbase.InputStateGlobal import inputState +from otp.avatar import Avatar +from otp.avatar import DistributedAvatar +from otp.friends import FriendManager +from otp.distributed import OTPClientRepository +from otp.distributed import PotentialAvatar +from otp.distributed import PotentialShard +from otp.distributed import DistributedDistrict +from otp.distributed.OtpDoGlobals import * +from otp.distributed import OtpDoGlobals +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPLocalizer +from otp.avatar.Avatar import teleportNotify +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.distributed import DelayDelete +from toontown.friends import FriendHandle +from toontown.friends import FriendsListPanel +from toontown.friends import ToontownFriendSecret +from toontown.login import AvatarChooser +from toontown.makeatoon import MakeAToon +from toontown.pets import DistributedPet, PetDetail, PetHandle +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toon import LocalToon +from toontown.toon import ToonDNA +from toontown.distributed import ToontownDistrictStats +from toontown.makeatoon import TTPickANamePattern +from toontown.parties import ToontownTimeManager +from toontown.toon import Toon, DistributedToon +from ToontownMsgTypes import * +import HoodMgr +import PlayGame +import random + + +class ToontownClientRepository(OTPClientRepository.OTPClientRepository): + SupportTutorial = 1 + GameGlobalsId = OTP_DO_ID_TOONTOWN + SetZoneDoneEvent = 'TCRSetZoneDone' + EmuSetZoneDoneEvent = 'TCREmuSetZoneDone' + SetInterest = 'Set' + ClearInterest = 'Clear' + ClearInterestDoneEvent = 'TCRClearInterestDone' + KeepSubShardObjects = False + + def __init__(self, serverVersion): + OTPClientRepository.OTPClientRepository.__init__(self, serverVersion, playGame=PlayGame.PlayGame) + self._playerAvDclass = self.dclassesByName['DistributedToon'] + setInterfaceFont(TTLocalizer.InterfaceFont) + setSignFont(TTLocalizer.SignFont) + setChalkFont(TTLocalizer.ChalkFont) + for i in xrange(len(TTLocalizer.NametagFonts)): + setNametagFont(i, TTLocalizer.NametagFonts[i]) + + self.toons = {} + self.__forbidCheesyEffects = 0 + self.friendManager = None + self.trophyManager = None + self.catalogManager = None + self.tutorialManager = None + self.newsManager = None + self.distributedDistrict = None + self.partyManager = None + self.lobbyManager = None + + self.toontownTimeManager = ToontownTimeManager.ToontownTimeManager() + + self.csm = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_CLIENT_SERVICES_MANAGER, 'ClientServicesManager') + self.ttsFriendsManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_TTS_FRIENDS_MANAGER, 'TTSFriendsManager') + + self.furnitureManager = None + self.objectManager = None + self.openAvatarPanels = set() + self.friendsMap = {} + self.friendsOnline = {} + self.friendsMapPending = 0 + self.friendsListError = 0 + self.elderFriendsMap = {} + self.__queryAvatarMap = {} + self.hoodMgr = HoodMgr.HoodMgr(self) + self.setZonesEmulated = 0 + self.old_setzone_interest_handle = None + self.setZoneQueue = Queue() + self.accept(ToontownClientRepository.SetZoneDoneEvent, self._handleEmuSetZoneDone) + self._deletedSubShardDoIds = set() + self.toonNameDict = {} + self.gameFSM.addState(State.State('skipTutorialRequest', self.enterSkipTutorialRequest, self.exitSkipTutorialRequest, ['playGame', 'gameOff', 'tutorialQuestion'])) + state = self.gameFSM.getStateNamed('waitOnEnterResponses') + state.addTransition('skipTutorialRequest') + state = self.gameFSM.getStateNamed('playGame') + state.addTransition('skipTutorialRequest') + self.wantCogdominiums = base.config.GetBool('want-cogdominiums', 1) + self.wantEmblems = base.config.GetBool('want-emblems', 0) + if base.config.GetBool('tt-node-check', 0): + for species in ToonDNA.toonSpeciesTypes: + for head in ToonDNA.getHeadList(species): + for torso in ToonDNA.toonTorsoTypes: + for legs in ToonDNA.toonLegTypes: + for gender in ('m', 'f'): + print 'species: %s, head: %s, torso: %s, legs: %s, gender: %s' % (species, + head, + torso, + legs, + gender) + dna = ToonDNA.ToonDNA() + dna.newToon((head, + torso, + legs, + gender)) + toon = Toon.Toon() + try: + toon.setDNA(dna) + except Exception, e: + print e + + return + + def congratulations(self, avatarChoice): + self.acceptedScreen = loader.loadModel('phase_3/models/gui/toon_council') + self.acceptedScreen.find('**/chars').removeNode() + self.acceptedScreen.setScale(0.667) + self.acceptedScreen.setPos(0, 0, 0.2) + self.acceptedScreen.reparentTo(aspect2d) + base.setBackgroundColor(Vec4(0.7647, 0.3529, 0.2352, 1)) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.acceptedBanner = DirectLabel(parent=self.acceptedScreen, relief=None, text=OTPLocalizer.CRNameCongratulations, text_scale=0.18, text_fg=Vec4(0.6, 0.1, 0.1, 1), text_pos=(0, 0.05), text_font=getMinnieFont()) + newName = avatarChoice.approvedName + self.acceptedText = DirectLabel(parent=self.acceptedScreen, relief=None, text=OTPLocalizer.CRNameAccepted % newName, text_scale=0.125, text_fg=Vec4(0, 0, 0, 1), text_pos=(0, -0.15)) + self.okButton = DirectButton(parent=self.acceptedScreen, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text='Ok', scale=1.5, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0, 0, -1), command=self.__handleCongrats, extraArgs=[avatarChoice]) + buttons.removeNode() + base.transitions.noFade() + return + + def __handleCongrats(self, avatarChoice): + self.acceptedBanner.destroy() + self.acceptedText.destroy() + self.okButton.destroy() + self.acceptedScreen.removeNode() + del self.acceptedScreen + del self.okButton + del self.acceptedText + del self.acceptedBanner + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + self.csm.sendAcknowledgeAvatarName( + avatarChoice.id, + lambda: self.loginFSM.request('waitForSetAvatarResponse', [avatarChoice])) + + def betterlucknexttime(self, avList, index): + self.rejectDoneEvent = 'rejectDone' + self.rejectDialog = TTDialog.TTGlobalDialog(doneEvent=self.rejectDoneEvent, message=TTLocalizer.NameShopNameRejected, style=TTDialog.Acknowledge) + self.rejectDialog.show() + self.acceptOnce(self.rejectDoneEvent, self.__handleReject, [avList, index]) + base.transitions.noFade() + + def __handleReject(self, avList, index): + self.rejectDialog.cleanup() + avid = 0 + for k in avList: + if k.position == index: + avid = k.id + + if avid == 0: + self.notify.error('Avatar rejected not found in avList. Index is: ' + str(index)) + self.csm.sendAcknowledgeAvatarName( + avid, + lambda: self.loginFSM.request('waitForAvatarList')) + + def enterChooseAvatar(self, avList): + ModelPool.garbageCollect() + TexturePool.garbageCollect() + self.sendSetAvatarIdMsg(0) + self.clearFriendState() + if self.music == None and base.musicManagerIsValid: + self.music = base.musicManager.getSound('phase_3/audio/bgm/tt_theme.ogg') + if self.music: + self.music.setLoop(1) + self.music.setVolume(0.9) + self.music.play() + base.playMusic(self.music, looping=1, volume=0.9, interrupt=None) + self.handler = self.handleMessageType + self.avChoiceDoneEvent = 'avatarChooserDone' + self.avChoice = AvatarChooser.AvatarChooser(avList, self.avChoiceDoneEvent) + self.avChoice.load() + self.avChoice.enter() + self.accept(self.avChoiceDoneEvent, self.__handleAvatarChooserDone, [avList]) + return + + def __handleAvatarChooserDone(self, avList, doneStatus): + done = doneStatus['mode'] + if done == 'exit': + self.loginFSM.request('shutdown') + index = self.avChoice.getChoice() + for av in avList: + if av.position == index: + avatarChoice = av + dna = ToonDNA.ToonDNA() + dna.makeFromNetString(av.dna) + print '__handleAvatarChooserDone: %r, %r, %r, %r' % (av.id, av.name, dna.asTuple(), av.position) + + if done == 'chose': + self.avChoice.exit() + if avatarChoice.approvedName != '': + self.congratulations(avatarChoice) + avatarChoice.approvedName = '' + elif avatarChoice.rejectedName != '': + avatarChoice.rejectedName = '' + self.betterlucknexttime(avList, index) + else: + base.localAvatarStyle = dna + base.localAvatarName = avatarChoice.name + self.loginFSM.request('waitForSetAvatarResponse', [avatarChoice]) + elif done == 'create': + self.loginFSM.request('createAvatar', [avList, index]) + elif done == 'delete': + self.loginFSM.request('waitForDeleteAvatarResponse', [avatarChoice]) + + def exitChooseAvatar(self): + self.handler = None + self.avChoice.exit() + self.avChoice.unload() + self.avChoice = None + self.ignore(self.avChoiceDoneEvent) + return + + def goToPickAName(self, avList, index): + self.avChoice.exit() + self.loginFSM.request('createAvatar', [avList, index]) + + def enterCreateAvatar(self, avList, index, newDNA = None): + if self.music: + self.music.stop() + self.music = None + if newDNA != None: + self.newPotAv = PotentialAvatar.PotentialAvatar('deleteMe', ['', + '', + '', + ''], newDNA.makeNetString(), index, 1) + avList.append(self.newPotAv) + base.transitions.noFade() + self.avCreate = MakeAToon.MakeAToon(self.loginFSM, avList, 'makeAToonComplete', index) + self.avCreate.load() + self.avCreate.enter() + self.accept('makeAToonComplete', self.__handleMakeAToon, [avList, index]) + self.accept('nameShopPost', self.relayMessage) + return + + def relayMessage(self, dg): + self.send(dg) + + def __handleMakeAToon(self, avList, avPosition): + done = self.avCreate.getDoneStatus() + if done == 'cancel': + if hasattr(self, 'newPotAv'): + if self.newPotAv in avList: + avList.remove(self.newPotAv) + self.avCreate.exit() + self.loginFSM.request('chooseAvatar', [avList]) + elif done == 'created': + self.avCreate.exit() + for i in avList: + if i.position == avPosition: + newPotAv = i + + self.loginFSM.request('waitForSetAvatarResponse', [newPotAv]) + else: + self.notify.error('Invalid doneStatus from MakeAToon: ' + str(done)) + + def exitCreateAvatar(self): + self.ignore('makeAToonComplete') + self.ignore('nameShopPost') + self.avCreate.unload() + self.avCreate = None + self.handler = None + if hasattr(self, 'newPotAv'): + del self.newPotAv + return + + def handleAvatarResponseMsg(self, avatarId, di): + self.cleanupWaitingForDatabase() + dclass = self.dclassesByName['DistributedToon'] + NametagGlobals.setMasterArrowsOn(0) + loader.beginBulkLoad('localAvatarPlayGame', OTPLocalizer.CREnteringToontown, 400, 1, TTLocalizer.TIP_GENERAL, 0) + localAvatar = LocalToon.LocalToon(self) + localAvatar.dclass = dclass + base.localAvatar = localAvatar + __builtins__['localAvatar'] = base.localAvatar + NametagGlobals.setToon(base.localAvatar) + localAvatar.doId = avatarId + self.localAvatarDoId = avatarId + parentId = None + zoneId = None + localAvatar.setLocation(parentId, zoneId) + localAvatar.generateInit() + localAvatar.generate() + dclass.receiveUpdateBroadcastRequiredOwner(localAvatar, di) + localAvatar.announceGenerate() + localAvatar.postGenerateMessage() + self.doId2do[avatarId] = localAvatar + localAvatar.initInterface() + self.sendGetFriendsListRequest() + self.loginFSM.request('playingGame') + + def getAvatarDetails(self, avatar, func, *args): + pad = ScratchPad() + pad.func = func + pad.args = args + pad.avatar = avatar + pad.delayDelete = DelayDelete.DelayDelete(avatar, 'getAvatarDetails') + self.__queryAvatarMap[avatar.doId] = pad + self.__sendGetAvatarDetails(avatar.doId, pet=(args[0].endswith("Pet"))) + + def cancelAvatarDetailsRequest(self, avatar): + avId = avatar.doId + if avId in self.__queryAvatarMap: + pad = self.__queryAvatarMap.pop(avId) + pad.delayDelete.destroy() + + def __sendGetAvatarDetails(self, avId, pet=0): + if pet: + self.ttsFriendsManager.d_getPetDetails(avId) + else: + self.ttsFriendsManager.d_getAvatarDetails(avId) + + def n_handleGetAvatarDetailsResp(self, avId, fields): + self.notify.info('Query reponse for avId %d' % avId) + try: + pad = self.__queryAvatarMap[avId] + except: + self.notify.warning('Received unexpected or outdated details for avatar %d.' % avId) + return + + del self.__queryAvatarMap[avId] + gotData = 0 + + dclassName = pad.args[0] + dclass = self.dclassesByName[dclassName] + #pad.avatar.updateAllRequiredFields(dclass, fields) + + # This is a much saner way to load avatar details, and is also + # dynamic. This means we aren't restricted in what we pass. + # Due to Python's random ordering of dictionaries, we have to pass + # a list containing a list of the field and value. For example: + # To set the hp and maxHp of an avatar, my fields list would be + # fields = [['setHp', 15], ['setMaxHp', 15]] + + for currentField in fields: + getattr(pad.avatar, currentField[0])(currentField[1]) + + gotData = 1 + + + if isinstance(pad.func, types.StringType): + messenger.send(pad.func, list((gotData, pad.avatar) + pad.args)) + else: + apply(pad.func, (gotData, pad.avatar) + pad.args) + + pad.delayDelete.destroy() + + def handleGetAvatarDetailsResp(self, di): + avId = di.getUint32() + returnCode = di.getUint8() + self.notify.info('Got query response for avatar %d, code = %d.' % (avId, returnCode)) + try: + pad = self.__queryAvatarMap[avId] + except: + self.notify.warning('Received unexpected or outdated details for avatar %d.' % avId) + return + + del self.__queryAvatarMap[avId] + gotData = 0 + if returnCode != 0: + self.notify.warning('No information available for avatar %d.' % avId) + else: + dclassName = pad.args[0] + dclass = self.dclassesByName[dclassName] + pad.avatar.updateAllRequiredFields(dclass, di) + gotData = 1 + if isinstance(pad.func, types.StringType): + messenger.send(pad.func, list((gotData, pad.avatar) + pad.args)) + else: + apply(pad.func, (gotData, pad.avatar) + pad.args) + pad.delayDelete.destroy() + + def enterPlayingGame(self, *args, **kArgs): + OTPClientRepository.OTPClientRepository.enterPlayingGame(self, *args, **kArgs) + self.gameFSM.request('waitOnEnterResponses', [None, + base.localAvatar.defaultZone, + base.localAvatar.defaultZone, + -1]) + self._userLoggingOut = False + + def exitPlayingGame(self): + ivalMgr.interrupt() + if self.objectManager != None: + self.objectManager.destroy() + self.objectManager = None + ToontownFriendSecret.unloadFriendSecret() + FriendsListPanel.unloadFriendsList() + messenger.send('cancelFriendInvitation') + base.removeGlitchMessage() + taskMgr.remove('avatarRequestQueueTask') + OTPClientRepository.OTPClientRepository.exitPlayingGame(self) + if hasattr(base, 'localAvatar'): + camera.reparentTo(render) + camera.setPos(0, 0, 0) + camera.setHpr(0, 0, 0) + del self.doId2do[base.localAvatar.getDoId()] + if base.localAvatar.getDelayDeleteCount() != 0: + self.notify.error('could not delete localAvatar, delayDeletes=%s' % (base.localAvatar.getDelayDeleteNames(),)) + base.localAvatar.deleteOrDelay() + base.localAvatar.detectLeaks() + NametagGlobals.setToon(base.cam) + del base.localAvatar + del __builtins__['localAvatar'] + base.localAvatarStyle = None + base.localAvatarName = None + loader.abortBulkLoad() + base.transitions.noTransitions() + return + + def enterGameOff(self): + OTPClientRepository.OTPClientRepository.enterGameOff(self) + + def enterWaitOnEnterResponses(self, shardId, hoodId, zoneId, avId): + self.resetDeletedSubShardDoIds() + OTPClientRepository.OTPClientRepository.enterWaitOnEnterResponses(self, shardId, hoodId, zoneId, avId) + + def enterSkipTutorialRequest(self, hoodId, zoneId, avId): + self.handlerArgs = {'hoodId': hoodId, + 'zoneId': zoneId, + 'avId': avId} + self.__requestSkipTutorial(hoodId, zoneId, avId) + + def __requestSkipTutorial(self, hoodId, zoneId, avId): + self.notify.debug('requesting skip tutorial') + self.acceptOnce('skipTutorialAnswered', self.__handleSkipTutorialAnswered, [hoodId, zoneId, avId]) + messenger.send('requestSkipTutorial') + self.waitForDatabaseTimeout(requestName='RequestSkipTutorial') + + def __handleSkipTutorialAnswered(self, hoodId, zoneId, avId, allOk): + if allOk: + hoodId = self.handlerArgs['hoodId'] + zoneId = self.handlerArgs['zoneId'] + avId = self.handlerArgs['avId'] + self.gameFSM.request('playGame', [hoodId, zoneId, avId]) + else: + self.notify.warning('allOk is false on skip tutorial, forcing the tutorial.') + self.gameFSM.request('tutorialQuestion', [hoodId, zoneId, avId]) + + def exitSkipTutorialRequest(self): + self.cleanupWaitingForDatabase() + self.handler = None + self.handlerArgs = None + self.ignore('skipTutorialAnswered') + return + + def enterTutorialQuestion(self, hoodId, zoneId, avId): + self.__requestTutorial(hoodId, zoneId, avId) + + def __requestTutorial(self, hoodId, zoneId, avId): + self.notify.debug('requesting tutorial') + self.acceptOnce('startTutorial', self.__handleStartTutorial, [avId]) + messenger.send('requestTutorial') + self.waitForDatabaseTimeout(requestName='RequestTutorial') + + def __handleStartTutorial(self, avId, zoneId): + self.gameFSM.request('playGame', [Tutorial, zoneId, avId]) + + def exitTutorialQuestion(self): + self.cleanupWaitingForDatabase() + self.handler = None + self.handlerArgs = None + self.ignore('startTutorial') + taskMgr.remove('waitingForTutorial') + return + + def enterSwitchShards(self, shardId, hoodId, zoneId, avId): + OTPClientRepository.OTPClientRepository.enterSwitchShards(self, shardId, hoodId, zoneId, avId) + self.handler = self.handleCloseShard + + def exitSwitchShards(self): + OTPClientRepository.OTPClientRepository.exitSwitchShards(self) + self.ignore(ToontownClientRepository.ClearInterestDoneEvent) + self.handler = None + return + + def enterCloseShard(self, loginState = None): + OTPClientRepository.OTPClientRepository.enterCloseShard(self, loginState) + self.handler = self.handleCloseShard + self._removeLocalAvFromStateServer() + + def handleCloseShard(self, msgType, di): + if msgType == CLIENT_ENTER_OBJECT_REQUIRED: + di2 = PyDatagramIterator(di) + parentId = di2.getUint32() + if self._doIdIsOnCurrentShard(parentId): + return + elif msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER: + di2 = PyDatagramIterator(di) + parentId = di2.getUint32() + if self._doIdIsOnCurrentShard(parentId): + return + elif msgType == CLIENT_OBJECT_SET_FIELD: + di2 = PyDatagramIterator(di) + doId = di2.getUint32() + if self._doIdIsOnCurrentShard(doId): + return + self.handleMessageType(msgType, di) + + def _logFailedDisable(self, doId, ownerView): + if doId not in self.doId2do and doId in self._deletedSubShardDoIds: + return + OTPClientRepository.OTPClientRepository._logFailedDisable(self, doId, ownerView) + + def exitCloseShard(self): + OTPClientRepository.OTPClientRepository.exitCloseShard(self) + self.ignore(ToontownClientRepository.ClearInterestDoneEvent) + self.handler = None + return + + def isShardInterestOpen(self): + return self.old_setzone_interest_handle is not None or self.uberZoneInterest is not None + + def resetDeletedSubShardDoIds(self): + self._deletedSubShardDoIds.clear() + + def dumpAllSubShardObjects(self): + if self.KeepSubShardObjects: + return + messenger.send('clientCleanup') + for avId, pad in self.__queryAvatarMap.items(): + pad.delayDelete.destroy() + + self.__queryAvatarMap = {} + delayDeleted = [] + doIds = self.doId2do.keys() + for doId in doIds: + obj = self.doId2do[doId] + if obj.parentId == localAvatar.defaultShard and obj is not localAvatar: + if not obj.neverDisable: + self.deleteObject(doId) + self._deletedSubShardDoIds.add(doId) + if obj.getDelayDeleteCount() != 0: + delayDeleted.append(obj) + + delayDeleteLeaks = [] + for obj in delayDeleted: + if obj.getDelayDeleteCount() != 0: + delayDeleteLeaks.append(obj) + + if len(delayDeleteLeaks): + s = 'dumpAllSubShardObjects:' + for obj in delayDeleteLeaks: + s += '\n could not delete %s (%s), delayDeletes=%s' % (safeRepr(obj), itype(obj), obj.getDelayDeleteNames()) + + self.notify.error(s) + + def _removeCurrentShardInterest(self, callback): + if self.old_setzone_interest_handle is None: + self.notify.warning('removeToontownShardInterest: no shard interest open') + callback() + return + self.acceptOnce(ToontownClientRepository.ClearInterestDoneEvent, Functor(self._tcrRemoveUberZoneInterest, callback)) + self._removeEmulatedSetZone(ToontownClientRepository.ClearInterestDoneEvent) + return + + def _tcrRemoveUberZoneInterest(self, callback): + self.acceptOnce(ToontownClientRepository.ClearInterestDoneEvent, Functor(self._tcrRemoveShardInterestDone, callback)) + self.removeInterest(self.uberZoneInterest, ToontownClientRepository.ClearInterestDoneEvent) + + def _tcrRemoveShardInterestDone(self, callback): + self.uberZoneInterest = None + callback() + return + + def _doIdIsOnCurrentShard(self, doId): + if doId == base.localAvatar.defaultShard: + return True + do = self.getDo(doId) + if do: + if do.parentId == base.localAvatar.defaultShard: + return True + return False + + def _wantShardListComplete(self): + print self.activeDistrictMap + if self._shardsAreReady(): + self.acceptOnce(ToontownDistrictStats.EventName(), self.shardDetailStatsComplete) + ToontownDistrictStats.refresh() + else: + self.loginFSM.request('noShards') + + def shardDetailStatsComplete(self): + self.loginFSM.request('waitForAvatarList') + + def exitWaitForShardList(self): + self.ignore(ToontownDistrictStats.EventName()) + OTPClientRepository.OTPClientRepository.exitWaitForShardList(self) + + def fillUpFriendsMap(self): + if self.isFriendsMapComplete(): + return 1 + if not self.friendsMapPending and not self.friendsListError: + self.notify.warning('Friends list stale; fetching new list.') + self.sendGetFriendsListRequest() + return 0 + + def isFriend(self, doId): + if doId in base.localAvatar.friendsList: + self.identifyFriend(doId) + return 1 + + return 0 + + def isFriendOnline(self, doId): + return doId in self.friendsOnline + + def addAvatarToFriendsList(self, avatar): + self.friendsMap[avatar.doId] = avatar + + def identifyFriend(self, doId, source = None): + if doId in self.friendsMap: + teleportNotify.debug('friend %s in friendsMap' % doId) + return self.friendsMap[doId] + if doId in self.doId2do: + teleportNotify.debug('found friend %s in doId2do' % doId) + return self.doId2do[doId] + elif self.cache.contains(doId): + teleportNotify.debug('found friend %s in cache' % doId) + return self.cache.dict[doId] + self.notify.warning("Don't know who friend %s is." % doId) + + def identifyAvatar(self, doId): + if doId in self.doId2do: + return self.doId2do[doId] + else: + return self.identifyFriend(doId) + + def isFriendsMapComplete(self): + for friendId in base.localAvatar.friendsList: + if self.identifyFriend(friendId) == None: + return 0 + + if base.wantPets and base.localAvatar.hasPet(): + if base.localAvatar.getPetId() not in self.friendsMap: + return 0 + + return 1 + + def removeFriend(self, avatarId): + self.ttsFriendsManager.d_removeFriend(avatarId) + + def clearFriendState(self): + self.friendsMap = {} + self.friendsOnline = {} + self.friendsMapPending = 0 + self.friendsListError = 0 + + def sendGetFriendsListRequest(self): + self.friendsMapPending = 1 + self.friendsListError = 0 + self.ttsFriendsManager.d_requestFriendsList() + + def cleanPetsFromFriendsMap(self): + for objId, obj in self.friendsMap.items(): + from toontown.pets import DistributedPet + if isinstance(obj, DistributedPet.DistributedPet): + print 'Removing %s reference from the friendsMap' % obj.getName() + del self.friendsMap[objId] + + def removePetFromFriendsMap(self): + doId = base.localAvatar.getPetId() + if doId and doId in self.friendsMap: + del self.friendsMap[doId] + + def addPetToFriendsMap(self, callback = None): + doId = base.localAvatar.getPetId() + if not doId or doId in self.friendsMap: + if callback: + callback() + return + + def petDetailsCallback(petAvatar): + petAvatar.announceGenerate() + handle = PetHandle.PetHandle(petAvatar) + self.friendsMap[doId] = handle + petAvatar.disable() + petAvatar.delete() + if callback: + callback() + petAvatar.detectLeaks() + + PetDetail.PetDetail(doId, petDetailsCallback) + + def handleGetFriendsList(self, resp): + for toon in resp: + doId = toon[0] + name = toon[1] + dnaString = toon[2] + dna = ToonDNA.ToonDNA() + dna.makeFromNetString(dnaString) + adminAccess = toon[3] + petId = toon[4] + handle = FriendHandle.FriendHandle(doId, name, dna, adminAccess, petId) + self.friendsMap[doId] = handle + if doId in self.friendsOnline: + self.friendsOnline[doId] = handle + + if base.wantPets and base.localAvatar.hasPet(): + + def handleAddedPet(): + self.friendsMapPending = 0 + messenger.send('friendsMapComplete') + + self.addPetToFriendsMap(handleAddedPet) + return + self.friendsMapPending = 0 + messenger.send('friendsMapComplete') + + def handleFriendOnline(self, doId): + self.notify.debug('Friend %d now online.' % doId) + if doId not in self.friendsOnline: + self.friendsOnline[doId] = self.identifyAvatar(doId) + messenger.send('friendOnline', [doId]) + + def handleFriendOffline(self, doId): + self.notify.debug('Friend %d now offline.' % doId) + if doId in self.friendsOnline: + del self.friendsOnline[doId] + messenger.send('friendOffline', [doId]) + + def handleGenerateWithRequiredOtherOwner(self, di): + # Toontown only makes use of OwnerViews for LocalToon. + if self.loginFSM.getCurrentState().getName() == 'waitForSetAvatarResponse': + doId = di.getUint32() + parentId = di.getUint32() + zoneId = di.getUint32() + dclassId = di.getUint16() + self.handleAvatarResponseMsg(doId, di) + + def getFirstBattle(self): + from toontown.battle import DistributedBattleBase + for dobj in self.doId2do.values(): + if isinstance(dobj, DistributedBattleBase.DistributedBattleBase): + return dobj + + def forbidCheesyEffects(self, forbid): + wasAllowed = self.__forbidCheesyEffects != 0 + if forbid: + self.__forbidCheesyEffects += 1 + else: + self.__forbidCheesyEffects -= 1 + isAllowed = self.__forbidCheesyEffects != 0 + if wasAllowed != isAllowed: + for av in Avatar.Avatar.ActiveAvatars: + if hasattr(av, 'reconsiderCheesyEffect'): + av.reconsiderCheesyEffect() + + base.localAvatar.reconsiderCheesyEffect() + + def areCheesyEffectsAllowed(self): + return self.__forbidCheesyEffects == 0 + + def getNextSetZoneDoneEvent(self): + return '%s-%s' % (ToontownClientRepository.EmuSetZoneDoneEvent, self.setZonesEmulated + 1) + + def getLastSetZoneDoneEvent(self): + return '%s-%s' % (ToontownClientRepository.EmuSetZoneDoneEvent, self.setZonesEmulated) + + def getQuietZoneLeftEvent(self): + return 'leftQuietZone-%s' % (id(self),) + + def sendSetZoneMsg(self, zoneId, visibleZoneList = None): + event = self.getNextSetZoneDoneEvent() + self.setZonesEmulated += 1 + parentId = base.localAvatar.defaultShard + self.sendSetLocation(base.localAvatar.doId, parentId, zoneId) + localAvatar.setLocation(parentId, zoneId) + interestZones = zoneId + if visibleZoneList is not None: + interestZones = visibleZoneList + self._addInterestOpToQueue(ToontownClientRepository.SetInterest, [parentId, interestZones, 'OldSetZoneEmulator'], event) + return + + def resetInterestStateForConnectionLoss(self): + OTPClientRepository.OTPClientRepository.resetInterestStateForConnectionLoss(self) + self.old_setzone_interest_handle = None + self.setZoneQueue.clear() + return + + def _removeEmulatedSetZone(self, doneEvent): + self._addInterestOpToQueue(ToontownClientRepository.ClearInterest, None, doneEvent) + return + + def _addInterestOpToQueue(self, op, args, event): + self.setZoneQueue.push([op, args, event]) + if len(self.setZoneQueue) == 1: + self._sendNextSetZone() + + def _sendNextSetZone(self): + op, args, event = self.setZoneQueue.top() + if op == ToontownClientRepository.SetInterest: + parentId, interestZones, name = args + if self.old_setzone_interest_handle == None: + self.old_setzone_interest_handle = self.addInterest(parentId, interestZones, name, ToontownClientRepository.SetZoneDoneEvent) + else: + self.alterInterest(self.old_setzone_interest_handle, parentId, interestZones, name, ToontownClientRepository.SetZoneDoneEvent) + elif op == ToontownClientRepository.ClearInterest: + self.removeInterest(self.old_setzone_interest_handle, ToontownClientRepository.SetZoneDoneEvent) + self.old_setzone_interest_handle = None + else: + self.notify.error('unknown setZone op: %s' % op) + return + + def _handleEmuSetZoneDone(self): + op, args, event = self.setZoneQueue.pop() + queueIsEmpty = self.setZoneQueue.isEmpty() + if event is not None: + if not base.killInterestResponse: + messenger.send(event) + elif not hasattr(self, '_dontSendSetZoneDone'): + import random + if random.random() < 0.05: + self._dontSendSetZoneDone = True + else: + messenger.send(event) + if not queueIsEmpty: + self._sendNextSetZone() + return + + def _isPlayerDclass(self, dclass): + return dclass == self._playerAvDclass + + def _isValidPlayerLocation(self, parentId, zoneId): + if not self.distributedDistrict: + return False + if parentId != self.distributedDistrict.doId: + return False + if parentId == self.distributedDistrict.doId and zoneId == OTPGlobals.UberZone: + return False + return True + + def sendQuietZoneRequest(self): + self.sendSetZoneMsg(OTPGlobals.QuietZone, []) + + def handleQuietZoneGenerateWithRequired(self, di): + doId = di.getUint32() + parentId = di.getUint32() + zoneId = di.getUint32() + classId = di.getUint16() + dclass = self.dclassesByNumber[classId] + if dclass.getClassDef().neverDisable: + dclass.startGenerate() + distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId) + dclass.stopGenerate() + + def handleQuietZoneGenerateWithRequiredOther(self, di): + doId = di.getUint32() + parentId = di.getUint32() + zoneId = di.getUint32() + classId = di.getUint16() + dclass = self.dclassesByNumber[classId] + if dclass.getClassDef().neverDisable: + dclass.startGenerate() + distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId) + dclass.stopGenerate() + + def handleQuietZoneUpdateField(self, di): + di2 = DatagramIterator(di) + doId = di2.getUint32() + if doId in self.deferredDoIds: + args, deferrable, dg0, updates = self.deferredDoIds[doId] + dclass = args[2] + if not dclass.getClassDef().neverDisable: + return + else: + do = self.getDo(doId) + if do: + if not do.neverDisable: + return + OTPClientRepository.OTPClientRepository.handleUpdateField(self, di) + + def handleDelete(self, di): + doId = di.getUint32() + self.deleteObject(doId) + + def deleteObject(self, doId, ownerView = False): + if doId in self.doId2do: + obj = self.doId2do[doId] + del self.doId2do[doId] + obj.deleteOrDelay() + if obj.getDelayDeleteCount() <= 0: + obj.detectLeaks() + elif self.cache.contains(doId): + self.cache.delete(doId) + else: + self.notify.warning('Asked to delete non-existent DistObj ' + str(doId)) + + def _abandonShard(self): + for doId, obj in self.doId2do.items(): + if obj.parentId == localAvatar.defaultShard and obj is not localAvatar: + + self.deleteObject(doId) diff --git a/toontown/distributed/ToontownDistrict.py b/toontown/distributed/ToontownDistrict.py new file mode 100755 index 00000000..53130eb0 --- /dev/null +++ b/toontown/distributed/ToontownDistrict.py @@ -0,0 +1,13 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from otp.distributed import DistributedDistrict + +class ToontownDistrict(DistributedDistrict.DistributedDistrict): + notify = DirectNotifyGlobal.directNotify.newCategory('ToontownDistrict') + + def __init__(self, cr): + DistributedDistrict.DistributedDistrict.__init__(self, cr) + self.avatarCount = 0 + self.invasionStatus = 0 + self.suitStatus = '' + self.groupAvCount = [] diff --git a/toontown/distributed/ToontownDistrictAI.py b/toontown/distributed/ToontownDistrictAI.py new file mode 100755 index 00000000..b8695e04 --- /dev/null +++ b/toontown/distributed/ToontownDistrictAI.py @@ -0,0 +1,48 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from otp.distributed.DistributedDistrictAI import DistributedDistrictAI +import time + +class ToontownDistrictAI(DistributedDistrictAI): + notify = directNotify.newCategory('ToontownDistrictAI') + created = 0 + + def announceGenerate(self): + DistributedDistrictAI.announceGenerate(self) + + # Remember the time of which this district was created: + self.created = int(time.time()) + + # We want to handle shard status queries so that a ShardStatusReceiver + # being created after we're generated will know where we're at: + self.air.accept('queryShardStatus', self.handleShardStatusQuery) + + # Send a shard status update with the information we have: + status = { + 'available': bool(self.available), + 'name': self.name, + 'created': int(time.time()) + } + self.air.sendNetEvent('shardStatus', [self.air.ourChannel, status]) + + def handleShardStatusQuery(self): + # Send a shard status update with the information we have: + status = { + 'available': bool(self.available), + 'name': self.name, + 'created': int(time.time()) + } + self.air.sendNetEvent('shardStatus', [self.air.ourChannel, status]) + + def setName(self, name): + DistributedDistrictAI.setName(self, name) + + # Send a shard status update containing our name: + status = {'name': name} + self.air.sendNetEvent('shardStatus', [self.air.ourChannel, status]) + + def setAvailable(self, available): + DistributedDistrictAI.setAvailable(self, available) + + # Send a shard status update containing our availability: + status = {'available': bool(available)} + self.air.sendNetEvent('shardStatus', [self.air.ourChannel, status]) diff --git a/toontown/distributed/ToontownDistrictStats.py b/toontown/distributed/ToontownDistrictStats.py new file mode 100755 index 00000000..344e123d --- /dev/null +++ b/toontown/distributed/ToontownDistrictStats.py @@ -0,0 +1,88 @@ +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.task import Task +from direct.distributed import DoInterestManager +from otp.distributed.OtpDoGlobals import * + +_ToonTownDistrictStatInterest = None +_ToonTownDistrictStatInterestComplete = 0 +_trashObject = DirectObject.DirectObject() + +def EventName(): + return 'ShardPopulationSet' + +def isOpen(): + global _ToonTownDistrictStatInterest + return _ToonTownDistrictStatInterest is not None + +def isComplete(): + global _ToonTownDistrictStatInterestComplete + return _ToonTownDistrictStatInterestComplete + +def open(event = None): + global _trashObject + global _ToonTownDistrictStatInterest + if not isOpen(): + def _CompleteProc(event): + global _ToonTownDistrictStatInterestComplete + _ToonTownDistrictStatInterestComplete = 1 + if event is not None: + messenger.send(event) + return + _trashObject.acceptOnce(EventName(), _CompleteProc) + _ToonTownDistrictStatInterest = base.cr.addInterest(OTP_DO_ID_TOONTOWN, OTP_ZONE_ID_DISTRICTS_STATS, EventName(), EventName()) + elif isComplete(): + messenger.send(EventName()) + +def refresh(event = None): + global _ToonTownDistrictStatInterest + if isOpen(): + if isComplete(): + messenger.send(EventName()) + if event is not none: + messenger.send(event) + else: + def _CompleteProc(event): + global _ToonTownDistrictStatInterestComplete + _ToonTownDistrictStatInterestComplete = 1 + if event is not None: + messenger.send(event) + close() + return + _trashObject.acceptOnce(EventName(), _CompleteProc, [event]) + _ToonTownDistrictStatInterest = base.cr.addInterest(OTP_DO_ID_TOONTOWN, OTP_ZONE_ID_DISTRICTS_STATS, EventName(), EventName()) + +def close(): + global _ToonTownDistrictStatInterest + global _ToonTownDistrictStatInterestComplete + if isOpen(): + _ToonTownDistrictStatInterestComplete = 0 + base.cr.removeInterest(_ToonTownDistrictStatInterest, None) + _ToonTownDistrictStatInterest = None + + +class ToontownDistrictStats(DistributedObject.DistributedObject): + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.districtId = 0 + + def setDistrictId(self, value): + self.districtId = value + + def setAvatarCount(self, avatarCount): + if self.districtId in self.cr.activeDistrictMap: + self.cr.activeDistrictMap[self.districtId].avatarCount = avatarCount + messenger.send('shardInfoUpdated') + + def setInvasionStatus(self, invasionStatus): + if self.districtId in self.cr.activeDistrictMap: + self.cr.activeDistrictMap[self.districtId].invasionStatus = invasionStatus + messenger.send('shardInfoUpdated') + + def setGroupAvCount(self, groupAvCount): + if self.districtId in self.cr.activeDistrictMap: + self.cr.activeDistrictMap[self.districtId].groupAvCount = groupAvCount + messenger.send('shardInfoUpdated') diff --git a/toontown/distributed/ToontownDistrictStatsAI.py b/toontown/distributed/ToontownDistrictStatsAI.py new file mode 100755 index 00000000..f578ea2a --- /dev/null +++ b/toontown/distributed/ToontownDistrictStatsAI.py @@ -0,0 +1,96 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.toonbase import ToontownGlobals +from toontown.toon import DistributedToonAI + +class ToontownDistrictStatsAI(DistributedObjectAI): + notify = directNotify.newCategory('ToontownDistrictStatsAI') + + districtId = 0 + avatarCount = 0 + invasionStatus = 0 + groupAvCount = [0] * len(ToontownGlobals.GROUP_ZONES) + + def announceGenerate(self): + DistributedObjectAI.announceGenerate(self) + + # We want to handle shard status queries so that a ShardStatusReceiver + # being created after we're generated will know where we're at: + self.air.accept('queryShardStatus', self.handleShardStatusQuery) + taskMgr.doMethodLater(15, self.__countGroups, self.uniqueName('countGroups')) + + def delete(self): + taskMgr.remove(self.uniqueName('countGroups')) + DistributedObjectAI.delete(self) + + def handleShardStatusQuery(self): + # Send a shard status update containing our population: + status = {'population': self.avatarCount} + self.air.sendNetEvent('shardStatus', [self.air.ourChannel, status]) + + def setDistrictId(self, districtId): + self.districtId = districtId + + def d_setDistrictId(self, districtId): + self.sendUpdate('setDistrictId', [districtId]) + + def b_setDistrictId(self, districtId): + self.setDistrictId(districtId) + self.d_setDistrictId(districtId) + + def getDistrictId(self): + return self.districtId + + def setAvatarCount(self, avatarCount): + self.avatarCount = avatarCount + + # Send a shard status update containing our population: + status = {'population': self.avatarCount} + self.air.sendNetEvent('shardStatus', [self.air.ourChannel, status]) + + def d_setAvatarCount(self, avatarCount): + self.sendUpdate('setAvatarCount', [avatarCount]) + + def b_setAvatarCount(self, avatarCount): + self.d_setAvatarCount(avatarCount) + self.setAvatarCount(avatarCount) + + def getAvatarCount(self): + return self.avatarCount + + def setInvasionStatus(self, invasionStatus): + self.invasionStatus = invasionStatus + + def d_setInvasionStatus(self, invasionStatus): + self.sendUpdate('setInvasionStatus', [invasionStatus]) + + def b_setInvasionStatus(self, invasionStatus): + self.setInvasionStatus(invasionStatus) + self.d_setInvasionStatus(invasionStatus) + + def getInvasionStatus(self): + return self.invasionStatus + + def setGroupAvCount(self, groupAvCount): + self.groupAvCount = groupAvCount + + def d_setGroupAvCount(self, groupAvCount): + self.sendUpdate('setGroupAvCount', [groupAvCount]) + + def b_setGroupAvCount(self, groupAvCount): + self.setGroupAvCount(groupAvCount) + self.d_setGroupAvCount(groupAvCount) + + def getGroupAvCount(self): + return self.groupAvCount + + def __countGroups(self, task): + zones = ToontownGlobals.GROUP_ZONES + self.groupAvCount = [0] * len(zones) + + for av in self.air.doId2do.values(): + if isinstance(av, DistributedToonAI.DistributedToonAI) and av.isPlayerControlled() and av.zoneId in zones: + self.groupAvCount[zones.index(av.zoneId)] += 1 + + taskMgr.doMethodLater(15, self.__countGroups, self.uniqueName('countGroups')) + self.b_setGroupAvCount(self.groupAvCount) \ No newline at end of file diff --git a/toontown/distributed/ToontownInternalRepository.py b/toontown/distributed/ToontownInternalRepository.py new file mode 100755 index 00000000..78566901 --- /dev/null +++ b/toontown/distributed/ToontownInternalRepository.py @@ -0,0 +1,87 @@ +from direct.distributed.AstronInternalRepository import AstronInternalRepository +from otp.distributed.OtpDoGlobals import * +from toontown.distributed.ToontownNetMessengerAI import ToontownNetMessengerAI +from direct.distributed.PyDatagram import PyDatagram +import traceback +import sys +import urlparse + +class ToontownInternalRepository(AstronInternalRepository): + GameGlobalsId = OTP_DO_ID_TOONTOWN + dbId = 4003 + + def __init__(self, baseChannel, serverId=None, dcFileNames=None, + dcSuffix='AI', connectMethod=None, threadedNet=None): + AstronInternalRepository.__init__( + self, baseChannel, serverId=serverId, dcFileNames=dcFileNames, + dcSuffix=dcSuffix, connectMethod=connectMethod, threadedNet=threadedNet) + + self.wantMongo = config.GetBool('want-mongo', False) + + def handleConnected(self): + self.__messenger = ToontownNetMessengerAI(self) + if self.wantMongo: + import pymongo + mongourl = config.GetString('mongodb-url', 'mongodb://localhost') + replicaset = config.GetString('mongodb-replicaset', '') + db = (urlparse.urlparse(mongourl).path or '/Astron_Dev')[1:] + if replicaset: + self.dbConn = pymongo.MongoClient(mongourl, replicaset=replicaset) + else: + self.dbConn = pymongo.MongoClient(mongourl) + self.database = self.dbConn[db] + self.dbGlobalCursor = self.database.toontownstride + else: + self.dbConn = None + self.database = None + self.dbGlobalCursor = None + + def sendNetEvent(self, message, sentArgs=[]): + self.__messenger.send(message, sentArgs) + + def addExitEvent(self, message): + dg = self.__messenger.prepare(message) + self.addPostRemove(dg) + + def handleDatagram(self, di): + msgType = self.getMsgType() + + if msgType == self.__messenger.msgType: + self.__messenger.handle(msgType, di) + return + + AstronInternalRepository.handleDatagram(self, di) + + def getAvatarIdFromSender(self): + return int(self.getMsgSender() & 0xFFFFFFFF) + + def getAccountIdFromSender(self): + return int((self.getMsgSender()>>32) & 0xFFFFFFFF) + + def _isValidPlayerLocation(self, parentId, zoneId): + if zoneId < 1000 and zoneId != 1: + return False + + return True + + def readerPollOnce(self): + try: + return AstronInternalRepository.readerPollOnce(self) + + except SystemExit, KeyboardInterrupt: + raise + + except Exception as e: + if self.getAvatarIdFromSender() > 100000000: + dg = PyDatagram() + dg.addServerHeader(self.getMsgSender(), self.ourChannel, CLIENTAGENT_EJECT) + dg.addUint16(166) + dg.addString('You were disconnected to prevent a district reset.') + self.send(dg) + + self.writeServerEvent('INTERNAL-EXCEPTION', self.getAvatarIdFromSender(), self.getAccountIdFromSender(), repr(e), traceback.format_exc()) + self.notify.warning('INTERNAL-EXCEPTION: %s (%s)' % (repr(e), self.getAvatarIdFromSender())) + print traceback.format_exc() + sys.exc_clear() + + return 1 diff --git a/toontown/distributed/ToontownMsgTypes.py b/toontown/distributed/ToontownMsgTypes.py new file mode 100755 index 00000000..8cdcb2bc --- /dev/null +++ b/toontown/distributed/ToontownMsgTypes.py @@ -0,0 +1 @@ +from direct.distributed.MsgTypes import * diff --git a/toontown/distributed/ToontownNetMessengerAI.py b/toontown/distributed/ToontownNetMessengerAI.py new file mode 100644 index 00000000..81ef7ff8 --- /dev/null +++ b/toontown/distributed/ToontownNetMessengerAI.py @@ -0,0 +1,35 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.PyDatagram import PyDatagram +import cPickle, zlib + +class ToontownNetMessengerAI: + """ + This works very much like the NetMessenger class except that + this is much simpler and makes much more sense. + """ + notify = DirectNotifyGlobal.directNotify.newCategory('ToontownNetMessengerAI') + + def __init__(self, air, msgChannel=40000, msgType=54321): + self.air = air + self.air.registerForChannel(msgChannel) + self.msgChannel = msgChannel + self.msgType = msgType + + def prepare(self, message, sentArgs=[]): + dg = PyDatagram() + dg.addServerHeader(self.msgChannel, self.air.ourChannel, self.msgType) + dg.addString(message) + dg.addString(zlib.compress(cPickle.dumps(sentArgs))) + return dg + + def send(self, message, sentArgs=[]): + self.notify.debug('sendNetEvent: %s %r' % (message, sentArgs)) + dg = self.prepare(message, sentArgs) + self.air.send(dg) + + def handle(self, msgType, di): + message = di.getString() + data = zlib.decompress(di.getString()) + sentArgs = cPickle.loads(data) + messenger.send(message, sentArgs) + \ No newline at end of file diff --git a/toontown/distributed/__init__.py b/toontown/distributed/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/dna/DNAParser.py b/toontown/dna/DNAParser.py new file mode 100755 index 00000000..44368b40 --- /dev/null +++ b/toontown/dna/DNAParser.py @@ -0,0 +1,43 @@ +from direct.stdpy import threading + +from libpandadna import * + +class DNABulkLoader: + def __init__(self, storage, files): + self.dnaStorage = storage + self.dnaFiles = files + + def loadDNAFiles(self): + for file in self.dnaFiles: + print 'Reading DNA file...', file + loadDNABulk(self.dnaStorage, file) + del self.dnaStorage + del self.dnaFiles + +def loadDNABulk(dnaStorage, file): + dnaLoader = DNALoader() + file = '/' + file + dnaLoader.loadDNAFile(dnaStorage, file) + +def loadDNAFile(dnaStorage, file): + print 'Reading DNA file...', file + dnaLoader = DNALoader() + file = '/' + file + node = dnaLoader.loadDNAFile(dnaStorage, file) + if node.node().getNumChildren() > 0: + return node.node() + +def loadDNAFileAI(dnaStorage, file): + dnaLoader = DNALoader() + file = '/' + file + data = dnaLoader.loadDNAFileAI(dnaStorage, file) + return data + +def setupDoor(a, b, c, d, e, f): + try: + e = int(str(e).split('_')[0]) + except: + print 'setupDoor: error parsing', e + e = 9999 + + DNADoor.setupDoor(a, b, c, d, e, f) diff --git a/toontown/dna/__init__.py b/toontown/dna/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/effects/BlastEffect.py b/toontown/effects/BlastEffect.py new file mode 100755 index 00000000..04a3d05f --- /dev/null +++ b/toontown/effects/BlastEffect.py @@ -0,0 +1,29 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from EffectController import EffectController + +class BlastEffect(NodePath, EffectController): + + def __init__(self): + NodePath.__init__(self, 'BlastEffect') + EffectController.__init__(self) + self.fadeTime = 0.15 + self.effectColor = Vec4(1, 1, 1, 1) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_particleCards') + self.effectModel = model.find('**/tt_t_efx_ext_particleBlast') + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + self.effectModel.setColorScale(0, 0, 0, 0) + fadeBlast = self.effectModel.colorScaleInterval(self.fadeTime, Vec4(0, 0, 0, 0), startColorScale=Vec4(self.effectColor), blendType='easeOut') + scaleBlast = self.effectModel.scaleInterval(self.fadeTime, 4, startScale=1.0, blendType='easeIn') + self.track = Sequence(Parallel(fadeBlast, scaleBlast), Func(self.cleanUpEffect)) + + def setEffectColor(self, color): + self.effectColor = color diff --git a/toontown/effects/Bubbles.py b/toontown/effects/Bubbles.py new file mode 100755 index 00000000..891b4eda --- /dev/null +++ b/toontown/effects/Bubbles.py @@ -0,0 +1,62 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect +from direct.particles import Particles +from direct.particles import ForceGroup +import random + +class Bubbles(NodePath): + + def __init__(self, parent, renderParent): + NodePath.__init__(self) + self.renderParent = renderParent.attachNewNode('bubbleRenderParent') + self.renderParent.setBin('fixed', 0) + self.assign(parent.attachNewNode('bubbles')) + self.effect = ParticleEffect.ParticleEffect() + p0 = Particles.Particles('particles-1') + p0.setFactory('PointParticleFactory') + p0.setRenderer('SpriteParticleRenderer') + p0.setEmitter('DiscEmitter') + p0.setPoolSize(8) + p0.setBirthRate(0.75) + p0.setLitterSize(2) + p0.setLitterSpread(1) + p0.factory.setLifespanBase(2.0) + p0.factory.setLifespanSpread(0.5) + p0.factory.setTerminalVelocityBase(400.0) + p0.factory.setTerminalVelocitySpread(40.0) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.0) + p0.renderer.setTextureFromNode('phase_4/models/char/bubble', '**/*') + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setInitialXScale(0.07) + p0.renderer.setFinalXScale(0.2) + p0.renderer.setInitialYScale(0.07) + p0.renderer.setFinalYScale(0.2) + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitudeSpread(0.025) + p0.emitter.setAmplitude(0.1) + p0.emitter.setRadius(0.5) + gravityForceGroup = ForceGroup.ForceGroup('air') + force0 = LinearVectorForce(Vec3(0.0, 0.0, 1.0), 1.0, 0) + force0.setActive(1) + force1 = LinearJitterForce(2.5, 0) + force1.setActive(1) + gravityForceGroup.addForce(force0) + gravityForceGroup.addForce(force1) + self.effect.addForceGroup(gravityForceGroup) + self.effect.addParticles(p0) + self.effect.setPos(0, 0, 0) + + def start(self): + self.effect.start(self, self.renderParent) + + def stop(self): + self.effect.disable() + + def destroy(self): + self.effect.cleanup() + self.renderParent.removeNode() + del self.effect + del self.renderParent diff --git a/toontown/effects/ChrysanthemumEffect.py b/toontown/effects/ChrysanthemumEffect.py new file mode 100755 index 00000000..bf6e9615 --- /dev/null +++ b/toontown/effects/ChrysanthemumEffect.py @@ -0,0 +1,48 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from EffectController import EffectController + +class ChrysanthemumEffect(NodePath, EffectController): + + def __init__(self): + NodePath.__init__(self, 'ChrysanthemumEffect') + EffectController.__init__(self) + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.effectModel = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkBurst_tflip') + self.effectModel.setColorScale(Point4(0, 0, 0, 0)) + self.effectModel.reparentTo(self) + self.seqNode = self.effectModel.getChild(0).getChild(0).node() + self.effectModel2 = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkBurst_tflip') + self.effectModel2.setColorScale(Point4(0, 0, 0, 0)) + self.effectModel2.reparentTo(self) + self.seqNode2 = self.effectModel2.getChild(0).getChild(0).node() + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.stars = model.find('**/tt_t_efx_ext_fireworkStars_02') + self.stars.setColorScale(Point4(0, 0, 0, 0)) + self.stars.reparentTo(self) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + self.effectModel.setColorScale(0, 0, 0, 0) + self.effectModel2.setColorScale(0, 0, 0, 0) + self.stars.setColorScale(0, 0, 0, 0) + fadeColor = self.effectColor - Vec4(0, 0, 0, 1) + fadeBlast = self.effectModel.colorScaleInterval(1.25, fadeColor, startColorScale=Vec4(1, 1, 0.8, 1), blendType='easeIn') + scaleBlast = self.effectModel.scaleInterval(0.5, 700 * self.effectScale, startScale=200 * self.effectScale, blendType='easeOut') + fadeBlast2 = self.effectModel2.colorScaleInterval(1.0, fadeColor, startColorScale=Vec4(1, 1, 0.8, 1), blendType='easeIn') + scaleBlast2 = self.effectModel2.scaleInterval(1.0, 720 * self.effectScale, startScale=250 * self.effectScale, blendType='easeOut') + starsFadeIn = self.stars.colorScaleInterval(0.25, self.effectColor, startColorScale=Vec4(1, 1, 1, 0)) + starsFadeOut = self.stars.colorScaleInterval(1.0, Vec4(0, 0, 0, 0), startColorScale=self.effectColor, blendType='easeIn') + starsScaleUp = self.stars.scaleInterval(1.5, 720 * self.effectScale, startScale=660 * self.effectScale, blendType='easeOut') + self.track = Parallel(Func(self.effectModel.setColorScale, self.effectColor), Func(self.effectModel2.setColorScale, self.effectColor), scaleBlast, fadeBlast, scaleBlast2, fadeBlast2, starsScaleUp, Sequence(Wait(0.4), starsFadeIn, starsFadeOut, Wait(0.5), Func(self.cleanUpEffect))) + + def setEffectColor(self, color): + self.effectColor = color + + def setEffectScale(self, scale): + self.effectScale = scale diff --git a/toontown/effects/DistributedFireworkShow.py b/toontown/effects/DistributedFireworkShow.py new file mode 100755 index 00000000..334fb733 --- /dev/null +++ b/toontown/effects/DistributedFireworkShow.py @@ -0,0 +1,27 @@ +from direct.distributed import DistributedObject +from toontown.effects.FireworkShowMixin import FireworkShowMixin + +class DistributedFireworkShow(DistributedObject.DistributedObject, FireworkShowMixin): + notify = directNotify.newCategory('DistributedFireworkShow') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FireworkShowMixin.__init__(self) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def disable(self): + DistributedObject.DistributedObject.disable(self) + FireworkShowMixin.disable(self) + + def delete(self): + DistributedObject.DistributedObject.delete(self) + + def d_requestFirework(self, x, y, z, style, color1, color2): + self.sendUpdate('requestFirework', (x, + y, + z, + style, + color1, + color2)) diff --git a/toontown/effects/DistributedFireworkShowAI.py b/toontown/effects/DistributedFireworkShowAI.py new file mode 100755 index 00000000..e04b2d7b --- /dev/null +++ b/toontown/effects/DistributedFireworkShowAI.py @@ -0,0 +1,62 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI + +from direct.distributed.ClockDelta import * +from direct.task import Task + +from otp.ai.MagicWordGlobal import * + +from toontown.toonbase import ToontownGlobals +from toontown.parties import PartyGlobals + +import FireworkShows +import random + +class DistributedFireworkShowAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedFireworkShowAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.air = air + + def startShow(self, eventId, style, timeStamp): + taskMgr.doMethodLater(FireworkShows.getShowDuration(eventId, style), self.requestDelete, 'delete%i' % self.doId, []) + + def d_startShow(self, eventId, style, timeStamp): + self.sendUpdate('startShow', [eventId, style, random.randint(0,1), timeStamp]) + + def b_startShow(self, eventId, style, timeStamp): + self.startShow(eventId, style, timeStamp) + self.d_startShow(eventId, style, timeStamp) + + def requestFirework(self, todo0, todo1, todo2, todo3, todo4, todo5): + pass + + def shootFirework(self, todo0, todo1, todo2, todo3, todo4, todo5): + pass + + +@magicWord(category=CATEGORY_SYSTEM_ADMINISTRATOR, types=[str]) +def fireworks(showName='july4'): + """ + Starts a fireworks show on the AI server. + """ + showName = showName.lower() + if showName == 'july4': + showType = ToontownGlobals.SUMMER_FIREWORKS + elif showName == 'newyears': + showType = ToontownGlobals.NEW_YEAR_FIREWORKS + elif showName == 'summer': + showType = PartyGlobals.FireworkShows.Summer + else: + return 'Invalid fireworks show name!' + numShows = len(FireworkShows.shows.get(showType, [])) + showIndex = random.randint(0, numShows - 1) + for hood in simbase.air.hoods: + if hood.zoneId == ToontownGlobals.GolfZone: + continue + fireworkShow = DistributedFireworkShowAI(simbase.air) + fireworkShow.generateWithRequired(hood.zoneId) + fireworkShow.b_startShow(showType, showIndex, + globalClockDelta.getRealNetworkTime()) + return 'A %s fireworks show was started!' % showName diff --git a/toontown/effects/Drift.py b/toontown/effects/Drift.py new file mode 100755 index 00000000..4aff94ce --- /dev/null +++ b/toontown/effects/Drift.py @@ -0,0 +1,30 @@ +from panda3d.core import * +from direct.particles import ParticleEffect +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattleParticles + +class Drift(NodePath): + + def __init__(self, parent, renderParent): + NodePath.__init__(self) + notify = DirectNotifyGlobal.directNotify.newCategory('DriftParticles') + self.renderParent = renderParent.attachNewNode('driftRenderParent') + self.renderParent.setBin('fixed', 0) + self.renderParent.setDepthWrite(0) + self.assign(parent.attachNewNode('drift')) + self.effect = BattleParticles.loadParticleFile('drift.ptf') + ren = self.effect.getParticlesNamed('particles-1').getRenderer() + ren.setTextureFromNode('phase_6/models/karting/driftSmoke', '**/*') + + def start(self): + self.effect.start(self, self.renderParent) + + def stop(self): + self.effect.disable() + + def destroy(self): + self.stop() + self.effect.cleanup() + self.renderParent.removeNode() + del self.effect + del self.renderParent diff --git a/toontown/effects/DustCloud.py b/toontown/effects/DustCloud.py new file mode 100755 index 00000000..8a37c1ba --- /dev/null +++ b/toontown/effects/DustCloud.py @@ -0,0 +1,87 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.showbase import PythonUtil +from toontown.battle.BattleProps import globalPropPool +from direct.directnotify import DirectNotifyGlobal +SFX = PythonUtil.Enum('poof, magic') +SFXPATHS = {SFX.poof: 'phase_4/audio/sfx/firework_distance_02.ogg', + SFX.magic: 'phase_4/audio/sfx/SZ_DD_treasure.ogg'} + +class DustCloud(NodePath): + dustCloudCount = 0 + sounds = {} + notify = DirectNotifyGlobal.directNotify.newCategory('DustCloud') + + def __init__(self, parent = hidden, fBillboard = 1, wantSound = 0): + NodePath.__init__(self) + self.assign(globalPropPool.getProp('suit_explosion_dust')) + if fBillboard: + self.setBillboardAxis() + self.reparentTo(parent) + self.seqNode = self.find('**/+SequenceNode').node() + self.seqNode.setFrameRate(0) + self.wantSound = wantSound + if self.wantSound and not DustCloud.sounds: + DustCloud.sounds[SFX.poof] = loader.loadSfx(SFXPATHS[SFX.poof]) + self.track = None + self.trackId = DustCloud.dustCloudCount + DustCloud.dustCloudCount += 1 + self.setBin('fixed', 100, 1) + self.hide() + return + + def createTrack(self, rate = 24): + + def getSoundFuncIfAble(soundId): + sound = DustCloud.sounds.get(soundId) + if self.wantSound and sound: + return sound.play + else: + + def dummy(): + pass + + return dummy + + tflipDuration = self.seqNode.getNumChildren() / float(rate) + self.track = Sequence(Func(self.show), Func(self.messaging), Func(self.seqNode.play, 0, self.seqNode.getNumFrames() - 1), Func(self.seqNode.setFrameRate, rate), Func(getSoundFuncIfAble(SFX.poof)), Wait(tflipDuration), Func(self._resetTrack), name='dustCloud-track-%d' % self.trackId) + + def _resetTrack(self): + self.seqNode.setFrameRate(0) + self.hide() + + def messaging(self): + self.notify.debug('CREATING TRACK ID: %s' % self.trackId) + + def isPlaying(self): + if self.track == None: + return False + if self.track.isPlaying(): + return True + else: + return False + return + + def play(self, rate = 24): + self.stop() + self.createTrack(rate) + self.track.start() + + def loop(self, rate = 24): + self.stop() + self.createTrack(rate) + self.track.loop() + + def stop(self): + if self.track: + self.track.finish() + self.track.clearToInitial() + + def destroy(self): + self.notify.debug('DESTROYING TRACK ID: %s' % self.trackId) + if self.track: + self._resetTrack() + self.track.clearToInitial() + del self.track + del self.seqNode + self.removeNode() diff --git a/toontown/effects/EffectController.py b/toontown/effects/EffectController.py new file mode 100755 index 00000000..ee4eeea6 --- /dev/null +++ b/toontown/effects/EffectController.py @@ -0,0 +1,107 @@ +from panda3d.core import * + +class EffectController: + particleDummy = None + + def __init__(self): + self.track = None + self.startEffect = None + self.endEffect = None + self.f = None + self.p0 = None + return + + def createTrack(self): + pass + + def destroy(self): + self.finish() + if self.f: + self.f.cleanup() + self.f = None + self.p0 = None + self.removeNode() + return + + def cleanUpEffect(self): + if self.f: + self.setPosHpr(0, 0, 0, 0, 0, 0) + self.f.disable() + self.detachNode() + + def reallyCleanUpEffect(self): + self.cleanUpEffect() + self.finish() + + def play(self, lod = None): + if lod != None: + try: + self.createTrack(lod) + except TypeError, e: + raise TypeError('Error loading %s effect.' % self.__class__.__name__) + + else: + self.createTrack() + self.track.start() + return + + def stop(self): + if self.track: + self.track.pause() + self.track = None + if self.startEffect: + self.startEffect.pause() + self.startEffect = None + if self.endEffect: + self.endEffect.pause() + self.endEffect = None + self.cleanUpEffect() + return + + def finish(self): + if self.track: + self.track.pause() + self.track = None + if self.startEffect: + self.startEffect.pause() + self.startEffect = None + if self.endEffect: + self.endEffect.pause() + self.endEffect = None + return + + def startLoop(self, lod = None): + if lod != None: + try: + self.createTrack(lod) + except TypeError, e: + raise TypeError('Error loading %s effect.' % self.__class__.__name__) + + else: + self.createTrack() + if self.startEffect: + self.startEffect.start() + return + + def stopLoop(self): + if self.startEffect: + self.startEffect.pause() + self.startEffect = None + if self.endEffect and not self.endEffect.isPlaying(): + self.endEffect.start() + return + + def getTrack(self): + if not self.track: + self.createTrack() + return self.track + + def enableEffect(self): + if self.f and self.particleDummy: + self.f.start(self, self.particleDummy) + elif self.f: + self.f.start(self, self) + + def disableEffect(self): + if self.f: + self.f.disable() diff --git a/toontown/effects/Firework.py b/toontown/effects/Firework.py new file mode 100755 index 00000000..8a034395 --- /dev/null +++ b/toontown/effects/Firework.py @@ -0,0 +1,149 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.effects.FireworkGlobals import * +from toontown.effects.FireworkEffect import FireworkEffect +import random + +class Firework(NodePath): + + def __init__(self, typeId, velocity = Vec3(0, 0, 500), scale = 1.0, color1 = Vec4(1, 1, 1, 1), color2 = None, burstDelay = 1.25): + NodePath.__init__(self, 'Firework') + self.typeId = typeId + self.velocity = velocity + self.scale = scale + self.primaryColor = color1 + self.secondaryColor = color2 + if not self.secondaryColor: + self.secondaryColor = self.primaryColor + self.burstDelay = burstDelay + self.fireworkIval = None + self.fireworkEffects = [] + return + + def play(self): + if not self.fireworkIval: + self.generateFireworkIval() + self.fireworkIval.start() + + def generateFireworkIval(self): + if not self.fireworkIval: + self.fireworkIval = Sequence() + if self.typeId == FireworkType.BasicPeony: + firework = FireworkEffect(FireworkBurstType.PeonyShell, FireworkTrailType.Default, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.AdvancedPeony: + firework = FireworkEffect(FireworkBurstType.PeonyParticleShell, FireworkTrailType.Default, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.DiademPeony: + firework = FireworkEffect(FireworkBurstType.PeonyDiademShell, FireworkTrailType.Default, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.Chrysanthemum: + firework = FireworkEffect(FireworkBurstType.ChrysanthemumShell, FireworkTrailType.Glow, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.DiademChrysanthemum: + firework = FireworkEffect(FireworkBurstType.ChrysanthemumDiademShell, FireworkTrailType.Glow, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.Ring: + firework = FireworkEffect(FireworkBurstType.RingShell, FireworkTrailType.Default, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.Saturn: + firework = FireworkEffect(FireworkBurstType.SaturnShell, FireworkTrailType.GlowSparkle, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.Bees: + firework = FireworkEffect(FireworkBurstType.BeeShell, FireworkTrailType.Polygonal, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.TrailBurst: + firework = FireworkEffect(FireworkBurstType.TrailExplosion, FireworkTrailType.Glow, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.GlowFlare: + firework = FireworkEffect(None, FireworkTrailType.LongGlowSparkle, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + firework.gravityMult = 1.0 + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.PalmTree: + firework = FireworkEffect(FireworkBurstType.TrailExplosion, FireworkTrailType.LongGlowSparkle, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + firework.gravityMult = 1.0 + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.Mickey: + head = FireworkEffect(FireworkBurstType.PeonyShell, FireworkTrailType.Glow, velocity=Vec3(0, 0, 80 * self.scale), scale=self.scale / 1.2, primaryColor=self.primaryColor, secondaryColor=self.secondaryColor, burstDelay=1.5) + leftEar = FireworkEffect(FireworkBurstType.PeonyShell, FireworkTrailType.Glow, velocity=Vec3(-25 * self.scale, 0, 115 * self.scale), scale=self.scale / 1.6, primaryColor=self.primaryColor, secondaryColor=self.secondaryColor, burstDelay=1.7) + rightEar = FireworkEffect(FireworkBurstType.PeonyShell, FireworkTrailType.Glow, velocity=Vec3(25 * self.scale, 0, 115 * self.scale), scale=self.scale / 1.6, primaryColor=self.primaryColor, secondaryColor=self.secondaryColor, burstDelay=1.7) + head.reparentTo(self) + leftEar.reparentTo(self) + rightEar.reparentTo(self) + self.fireworkEffects = self.fireworkEffects + [head, leftEar, rightEar] + fireworkParallel = Parallel() + fireworkParallel.append(head.getFireworkMainIval()) + fireworkParallel.append(leftEar.getFireworkMainIval()) + fireworkParallel.append(rightEar.getFireworkMainIval()) + self.fireworkIval.append(fireworkParallel) + elif self.typeId == FireworkType.PirateSkull: + skull = FireworkEffect(FireworkBurstType.SkullBlast, FireworkTrailType.GlowSparkle, velocity=Vec3(0, 0, 400 * self.scale), scale=self.scale, primaryColor=self.primaryColor, secondaryColor=self.secondaryColor, burstDelay=1.75) + leftBone = FireworkEffect(None, FireworkTrailType.LongGlowSparkle, velocity=Vec3(220 * self.scale, 0, 250 * self.scale), scale=self.scale * 1.25, primaryColor=self.primaryColor, secondaryColor=self.secondaryColor, burstDelay=2.25) + rightBone = FireworkEffect(None, FireworkTrailType.LongGlowSparkle, velocity=Vec3(-220 * self.scale, 0, 250 * self.scale), scale=self.scale * 1.25, primaryColor=self.primaryColor, secondaryColor=self.secondaryColor, burstDelay=2.25) + skull.reparentTo(self) + leftBone.reparentTo(self) + leftBone.setPos(-225 * self.scale, 0, 0) + leftBone.gravityMult = 3.5 + rightBone.reparentTo(self) + rightBone.setPos(225 * self.scale, 0, 0) + rightBone.gravityMult = 3.5 + self.fireworkEffects = self.fireworkEffects + [skull, leftBone, rightBone] + fireworkParallel = Parallel() + fireworkParallel.append(skull.getFireworkMainIval()) + fireworkParallel.append(leftBone.getFireworkMainIval()) + fireworkParallel.append(rightBone.getFireworkMainIval()) + self.fireworkIval.append(fireworkParallel) + elif self.typeId == FireworkType.AmericanFlag: + fireworkParallel = Parallel() + colors = [Vec4(1, 0, 0, 1), Vec4(1, 1, 1, 1)] + for i in xrange(4): + firework = FireworkEffect(None, FireworkTrailType.LongGlowSparkle, velocity=Vec3(-30 * self.scale, 0, 150 * self.scale - 20 * i), scale=self.scale * 3.0, primaryColor=colors[i % 2], burstDelay=2.5) + firework.reparentTo(self) + firework.setX(-20.0 * self.scale + 10.0 * i * self.scale) + self.fireworkEffects.append(firework) + fireworkParallel.append(Sequence(Wait(0.25 * i), firework.getFireworkMainIval())) + + firework = FireworkEffect(FireworkBurstType.Sparkles, FireworkTrailType.Default, velocity=Vec3(20, 0, 90), scale=self.scale * 1.5) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + fireworkParallel.append(Sequence(Wait(1.5), firework.getFireworkMainIval())) + self.fireworkIval.append(fireworkParallel) + elif self.typeId == FireworkType.IceCream: + firework = FireworkEffect(FireworkBurstType.IceCream, FireworkTrailType.Default, self.velocity, self.scale, Vec4(1, 1, 1, 1), Vec4(1, 1, 1, 1), self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + self.fireworkIval.append(Func(self.cleanup)) + return self.fireworkIval + + def cleanup(self): + if self.fireworkIval: + self.fireworkIval.pause() + self.fireworkIval = None + for effect in self.fireworkEffects: + effect.cleanupEffect() + + self.fireworkEffects = [] + return diff --git a/toontown/effects/FireworkEffect.py b/toontown/effects/FireworkEffect.py new file mode 100755 index 00000000..eb055fb9 --- /dev/null +++ b/toontown/effects/FireworkEffect.py @@ -0,0 +1,447 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +import random +from toontown.effects.FireworkGlobals import * +from toontown.effects.Glow import Glow +from toontown.effects.GlowTrail import GlowTrail +from toontown.effects.SparksTrail import SparksTrail +from toontown.effects.SparksTrailLong import SparksTrailLong +from toontown.effects.PolyTrail import PolyTrail +from toontown.effects.FlashEffect import FlashEffect +from toontown.effects.BlastEffect import BlastEffect +from toontown.effects.FireworkSparkles import FireworkSparkles +from toontown.effects.SimpleSparkles import SimpleSparkles +from toontown.effects.PeonyEffect import PeonyEffect +from toontown.effects.RayBurst import RayBurst +from toontown.effects.StarBurst import StarBurst +from toontown.effects.ChrysanthemumEffect import ChrysanthemumEffect +from toontown.effects.RingEffect import RingEffect +from toontown.effects.NoiseSparkles import NoiseSparkles +from toontown.effects.SkullBurst import SkullBurst +from toontown.effects.SkullFlash import SkullFlash +from toontown.effects.TrailExplosion import TrailExplosion +from toontown.effects.IceCream import IceCream +trailSfxNames = ['phase_4/audio/sfx/firework_whistle_01.ogg', 'phase_4/audio/sfx/firework_whistle_02.ogg'] +burstSfxNames = ['phase_4/audio/sfx/firework_explosion_01.ogg', + 'phase_4/audio/sfx/firework_explosion_02.ogg', + 'phase_4/audio/sfx/firework_explosion_03.ogg', + 'phase_4/audio/sfx/firework_distance_01.ogg', + 'phase_4/audio/sfx/firework_distance_02.ogg', + 'phase_4/audio/sfx/firework_distance_03.ogg'] + +class FireworkEffect(NodePath): + + def __init__(self, burstEffectId, trailEffectId = FireworkTrailType.Default, velocity = Vec3(0, 0, 500), scale = 1.0, primaryColor = Vec4(1, 1, 1, 1), secondaryColor = None, burstDelay = 1.25): + NodePath.__init__(self, 'FireworkEffect') + self.burstTypeId = burstEffectId + self.trailTypeId = trailEffectId + self.velocity = velocity + self.scale = scale / 7 + self.primaryColor = primaryColor + self.secondaryColor = secondaryColor + if not self.secondaryColor: + self.secondaryColor = self.primaryColor + self.burstDelay = burstDelay + self.gravityMult = 1.0 + self.fireworkMainIval = None + self.trailEffectsIval = None + self.burstEffectsIval = None + self.effectsNode = self.attachNewNode('fireworkEffectsNode') + self.trailEffects = [] + self.burstEffects = [] + self.trailSfx = [] + for audio in trailSfxNames: + audio = loader.loadSfx(audio) + audio.setVolume(0.075) + self.trailSfx.append(audio) + + self.burstSfx = [] + for audio in burstSfxNames: + audio = loader.loadSfx(audio) + audio.setVolume(0.8) + self.burstSfx.append(audio) + + return + + def play(self): + self.getFireworkMainIval().start() + + def getFireworkMainIval(self): + self.effectsNode.setPos(0, 0, 0) + if not self.fireworkMainIval: + self.fireworkMainIval = Parallel() + self.fireworkMainIval.append(self.getTrailEffectsIval()) + self.fireworkMainIval.append(Sequence(Wait(self.burstDelay), Func(self.cleanupTrailEffects), self.getBurstEffectsIval(), Func(self.cleanupBurstEffects), Func(self.cleanupEffect))) + return self.fireworkMainIval + + def getTrailEffectsIval(self): + if not self.trailEffectsIval: + if self.trailTypeId is None: + self.effectNode.setPos(self.velocity) + self.trailEffectsIval = Wait(self.burstDelay) + return self.trailEffectsIval + self.trailEffectsIval = Parallel() + self.trailEffectsIval.append(ProjectileInterval(self.effectsNode, startVel=self.velocity, duration=self.burstDelay, gravityMult=self.gravityMult)) + if self.trailTypeId is None: + return self.trailEffectsIval + self.trailEffectsIval.append(Func(random.choice(self.trailSfx).play)) + if base.config.GetInt('toontown-sfx-setting', 1) == 0: + if self.trailTypeId != FireworkTrailType.LongGlowSparkle: + self.trailTypeId = FireworkTrailType.Default + if self.trailTypeId == FireworkTrailType.Default: + glowEffect = Glow.getEffect() + if glowEffect: + glowEffect.reparentTo(self.effectsNode) + glowEffect.setColorScale(Vec4(1, 1, 1, 1)) + glowEffect.setScale(10.0) + self.trailEffects.append(glowEffect) + self.trailEffectsIval.append(Func(glowEffect.startLoop)) + elif self.trailTypeId == FireworkTrailType.Polygonal: + r = 0.75 + mColor = Vec4(1, 1, 1, 1) + vertex_list = [Vec4(r, 0.0, r, 1.0), + Vec4(r, 0.0, -r, 1.0), + Vec4(-r, 0.0, -r, 1.0), + Vec4(-r, 0.0, r, 1.0), + Vec4(r, 0.0, r, 1.0)] + motion_color = [mColor, + mColor, + mColor, + mColor, + mColor] + trailEffect = PolyTrail(None, vertex_list, motion_color, 0.5) + trailEffect.setUnmodifiedVertexColors(motion_color) + trailEffect.reparentTo(self.effectsNode) + trailEffect.motion_trail.geom_node_path.setTwoSided(False) + trailEffect.setBlendModeOn() + trailEffect.setLightOff() + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.beginTrail)) + elif self.trailTypeId == FireworkTrailType.Glow: + trailEffect = GlowTrail.getEffect() + if trailEffect: + trailEffect.reparentTo(self.effectsNode) + trailEffect.setEffectScale(self.scale * 0.75) + trailEffect.setEffectColor(Vec4(1, 1, 1, 1)) + trailEffect.setLifespan(0.25) + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.startLoop)) + elif self.trailTypeId == FireworkTrailType.Sparkle: + trailEffect = SparksTrail.getEffect() + if trailEffect: + trailEffect.reparentTo(self.effectsNode) + trailEffect.setEffectScale(self.scale) + trailEffect.setEffectColor(Vec4(1, 1, 1, 1)) + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.startLoop)) + elif self.trailTypeId == FireworkTrailType.GlowSparkle: + glowEffect = Glow.getEffect() + if glowEffect: + glowEffect.reparentTo(self.effectsNode) + glowEffect.setColorScale(Vec4(1, 1, 1, 1)) + glowEffect.setScale(15.0) + self.trailEffects.append(glowEffect) + self.trailEffectsIval.append(Func(glowEffect.startLoop)) + trailEffect = SparksTrail.getEffect() + if trailEffect: + trailEffect.reparentTo(self.effectsNode) + trailEffect.setEffectScale(self.scale) + trailEffect.setEffectColor(Vec4(1, 1, 1, 1)) + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.startLoop)) + elif self.trailTypeId == FireworkTrailType.LongSparkle: + trailEffect = SparksTrailLong.getEffect() + if trailEffect: + trailEffect.reparentTo(self.effectsNode) + trailEffect.setEffectScale(self.scale) + trailEffect.setEffectColor(Vec4(1, 1, 1, 1)) + trailEffect.setLifespan(4.0) + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.startLoop)) + elif self.trailTypeId == FireworkTrailType.LongGlowSparkle: + trailEffect = SparksTrailLong.getEffect() + if trailEffect: + trailEffect.reparentTo(self.effectsNode) + trailEffect.setEffectScale(self.scale) + trailEffect.setEffectColor(self.secondaryColor) + trailEffect.setLifespan(3.5) + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.startLoop)) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + trailEffect = GlowTrail.getEffect() + if trailEffect: + trailEffect.reparentTo(self.effectsNode) + trailEffect.setEffectScale(self.scale) + trailEffect.setEffectColor(self.primaryColor) + trailEffect.setLifespan(1.0) + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.startLoop)) + return self.trailEffectsIval + + def getBurstEffectsIval(self): + if not self.burstEffectsIval: + self.burstEffectsIval = Parallel() + if self.burstTypeId is None: + return self.burstEffectsIval + self.burstEffectsIval.append(Wait(0.5)) + self.burstEffectsIval.append(Func(random.choice(self.burstSfx).play)) + flash = FlashEffect() + flash.reparentTo(self.effectsNode) + flash.setEffectColor(self.primaryColor) + flash.setScale(1200 * self.scale) + flash.fadeTime = 0.5 + self.burstEffectsIval.append(flash.getTrack()) + self.burstEffects.append(flash) + primaryBlast = BlastEffect() + primaryBlast.reparentTo(self.effectsNode) + primaryBlast.setScale(100 * self.scale) + primaryBlast.setEffectColor(Vec4(1, 1, 1, 1)) + primaryBlast.fadeTime = 0.75 + self.burstEffectsIval.append(primaryBlast.getTrack()) + self.burstEffects.append(primaryBlast) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + secondaryBlast = BlastEffect() + secondaryBlast.reparentTo(self.effectsNode) + secondaryBlast.setScale(250 * self.scale) + secondaryBlast.setEffectColor(self.primaryColor) + secondaryBlast.fadeTime = 0.3 + self.burstEffectsIval.append(secondaryBlast.getTrack()) + self.burstEffects.append(secondaryBlast) + if self.burstTypeId == FireworkBurstType.Sparkles: + sparkles = FireworkSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale) + sparkles.setRadius(100 * self.scale) + sparkles.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + elif self.burstTypeId == FireworkBurstType.PeonyShell: + explosion = PeonyEffect.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + explosion.startDelay = 0.0 + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + rays = RayBurst() + rays.reparentTo(self.effectsNode) + rays.setEffectScale(self.scale) + rays.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(rays.getTrack()) + self.burstEffects.append(rays) + if base.config.GetInt('toontown-sfx-setting', 1) >= 2: + sparkles = FireworkSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale) + sparkles.setEffectColor(self.primaryColor) + sparkles.startDelay = 0.0 + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + explosion = PeonyEffect.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale * 0.8) + explosion.setEffectColor(self.primaryColor) + explosion.startDelay = 0.15 + explosion.setR(220) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + elif self.burstTypeId == FireworkBurstType.PeonyParticleShell: + explosion = StarBurst.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + rays = RayBurst() + rays.reparentTo(self.effectsNode) + rays.setEffectScale(self.scale * 0.75) + rays.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(rays.getTrack()) + self.burstEffects.append(rays) + elif self.burstTypeId == FireworkBurstType.PeonyDiademShell: + explosion = StarBurst.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + rays = RayBurst() + rays.reparentTo(self.effectsNode) + rays.setEffectScale(self.scale) + rays.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(rays.getTrack()) + self.burstEffects.append(rays) + sparkles = SimpleSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale) + sparkles.setRadius(100 * self.scale) + sparkles.setEffectColor(self.secondaryColor) + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + elif self.burstTypeId == FireworkBurstType.ChrysanthemumShell: + explosion = ChrysanthemumEffect() + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + if base.config.GetInt('toontown-sfx-setting', 1) >= 2: + sparkles = FireworkSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale * 0.8) + sparkles.setEffectColor(self.primaryColor) + sparkles.startDelay = 0.2 + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + elif self.burstTypeId == FireworkBurstType.ChrysanthemumDiademShell: + explosion = ChrysanthemumEffect() + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + sparkles = SimpleSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale) + sparkles.setRadius(100 * self.scale) + sparkles.setEffectColor(self.secondaryColor) + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + elif self.burstTypeId == FireworkBurstType.RingShell: + explosion = RingEffect.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + elif self.burstTypeId == FireworkBurstType.SaturnShell: + explosion = RingEffect.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + sparkles = SimpleSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale) + sparkles.setRadius(75 * self.scale) + sparkles.setEffectColor(self.secondaryColor) + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + elif self.burstTypeId == FireworkBurstType.BeeShell: + explosion = NoiseSparkles.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(Sequence(Wait(0.1), explosion.getTrack())) + self.burstEffects.append(explosion) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + rays = RayBurst() + rays.reparentTo(self.effectsNode) + rays.setEffectScale(self.scale) + rays.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(rays.getTrack()) + self.burstEffects.append(rays) + elif self.burstTypeId == FireworkBurstType.SkullBlast: + explosion = SkullBurst.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + explosion.startDelay = 0.1 + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + skullFlash = SkullFlash.getEffect() + if skullFlash: + skullFlash.reparentTo(self.effectsNode) + skullFlash.setScale(650 * self.scale) + skullFlash.fadeTime = 0.75 + skullFlash.startDelay = 0.08 + self.burstEffectsIval.append(skullFlash.getTrack()) + self.burstEffects.append(skullFlash) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + rays = RayBurst() + rays.reparentTo(self.effectsNode) + rays.setEffectScale(self.scale) + rays.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(rays.getTrack()) + self.burstEffects.append(rays) + if base.config.GetInt('toontown-sfx-setting', 1) >= 2: + sparkles = FireworkSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale) + sparkles.setRadius(400 * self.scale) + sparkles.startDelay = 0.1 + sparkles.setEffectColor(self.secondaryColor) + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + elif self.burstTypeId == FireworkBurstType.TrailExplosion: + explosion = TrailExplosion.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + explosion.numTrails = 3 + base.config.GetInt('toontown-sfx-setting', 1) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + elif self.burstTypeId == FireworkBurstType.IceCream: + explosion = IceCream.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + return self.burstEffectsIval + + def cleanupTrailEffects(self): + if self.trailEffectsIval: + self.trailEffectsIval.pause() + self.trailEffectsIval = None + for effect in self.trailEffects: + if isinstance(effect, PolyTrail): + effect.destroy() + effect = None + else: + effect.stopLoop() + effect = None + + self.trailEffects = [] + return + + def cleanupBurstEffects(self): + if self.burstEffectsIval: + self.burstEffectsIval.pause() + self.burstEffectsIval = None + for effect in self.burstEffects: + effect.stop() + effect = None + + self.burstEffects = [] + return + + def cleanupEffect(self): + if self.fireworkMainIval: + self.fireworkMainIval.pause() + self.fireworkMainIval = None + self.cleanupTrailEffects() + self.cleanupBurstEffects() + return diff --git a/toontown/effects/FireworkGlobals.py b/toontown/effects/FireworkGlobals.py new file mode 100755 index 00000000..c1c3b8e3 --- /dev/null +++ b/toontown/effects/FireworkGlobals.py @@ -0,0 +1,106 @@ +FW_T = 0 +FW_STYLE = 1 +FW_COLOR1 = 2 +FW_COLOR2 = 3 +FW_AMP = 4 +FW_POS_X = 5 +FW_POS_Y = 6 +FW_POS_Z = 7 +CIRCLE = 0 +ROCKET = 1 +RING = 2 +CIRCLELARGE = 3 +CIRCLESMALL = 4 +POP = 5 +CIRCLESPRITE = 6 +styleNames = ['CIRCLE', + 'ROCKET', + 'RING', + 'CIRCLELARGE', + 'CIRCLESMALL', + 'POP', + 'CIRCLESPRITE'] +styleNamesShort = ['CIR', + 'RKT', + 'RNG', + 'CLG', + 'CSM', + 'POP', + 'SPR'] +Names = ['Pow', + 'Rocket', + 'Ring', + 'Large\nPow', + 'Small\nPow', + 'Pop', + 'Widow\nMaker'] +WHITE = 0 +RED = 1 +BLUE = 2 +YELLOW = 3 +GREEN = 4 +PINK = 5 +PURPLE = 6 +CYAN = 7 +PEACH = 8 +ColorNames = ['White', + 'Red', + 'Blue', + 'Yellow', + 'Green', + 'Pink', + 'Purple', + 'Cyan', + 'Peach'] +SNOWFLAKE = 0 +MUSICNOTE = 1 +FLOWER = 2 +ICECREAM = 3 +STARFISH = 4 +ZZZ = 5 +skyTransitionDuration = 2.0 +preShowPauseDuration = 2.0 +postShowPauseDuration = 4.0 +preNormalMusicPauseDuration = 0.5 + +class FireworkTrailType: + Default = 0 + Polygonal = 1 + Glow = 2 + Sparkle = 3 + GlowSparkle = 4 + LongSparkle = 5 + LongGlowSparkle = 6 + + +class FireworkBurstType: + Sparkles = 0 + PeonyShell = 1 + PeonyParticleShell = 2 + PeonyDiademShell = 3 + ChrysanthemumShell = 4 + ChrysanthemumDiademShell = 5 + RingShell = 6 + SaturnShell = 7 + BeeShell = 8 + SkullBlast = 9 + TrailExplosion = 10 + IceCream = 11 + + +class FireworkType: + BasicPeony = 0 + AdvancedPeony = 1 + DiademPeony = 2 + Chrysanthemum = 3 + DiademChrysanthemum = 4 + Ring = 5 + Saturn = 6 + Bees = 7 + TrailBurst = 8 + GlowFlare = 9 + PalmTree = 10 + Mickey = 11 + PirateSkull = 12 + AmericanFlag = 13 + IceCream = 14 diff --git a/toontown/effects/FireworkShow.py b/toontown/effects/FireworkShow.py new file mode 100755 index 00000000..269565ea --- /dev/null +++ b/toontown/effects/FireworkShow.py @@ -0,0 +1,1204 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.effects.FireworkGlobals import * +from toontown.effects.Firework import Firework +from toontown.toonbase import ToontownGlobals +from toontown.parties import PartyGlobals +import random +colors = [Vec4(1, 1, 1, 1), + Vec4(1, 0.1, 0.1, 1), + Vec4(0.1, 1, 0.1, 1), + Vec4(0.3, 1, 0.3, 1), + Vec4(0.2, 0.2, 1, 1), + Vec4(1, 1, 0.1, 1), + Vec4(1, 0.5, 0.1, 1), + Vec4(1, 0.1, 1, 1), + Vec4(0.1, 1, 1, 1), + Vec4(0.1, 0.5, 1, 1)] +fireworkShowTypes = [ToontownGlobals.SUMMER_FIREWORKS, + PartyGlobals.FireworkShows.Summer, + ToontownGlobals.NEW_YEAR_FIREWORKS, + ToontownGlobals.COMBO_FIREWORKS] + +class FireworkShow(NodePath): + + def r(): + return random.randint(8, 12) / 10.0 + + def rV(): + return Vec3(random.randint(-60, 60), random.randint(10, 30), random.randint(125, 150)) + + def rP(): + return Point3(0, 0, 0) + + def rS(): + return 1.0 + random.random() / 2.0 + + def rC(): + return random.choice(colors) + + def rT(): + return random.randint(12, 20) / 10.0 + + def rD(): + return random.randint(1, 20) / 10.0 + + showData = {ToontownGlobals.SUMMER_FIREWORKS: [[FireworkType.GlowFlare, + Vec3(-90, 0, 80), + Vec3(120, 0, 0), + rS(), + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 0.0], + [FireworkType.GlowFlare, + Vec3(90, 0, 80), + Vec3(-120, 0, 0), + rS(), + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.BasicPeony, + Vec3(50, 0, 140), + rP(), + rS(), + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + rT(), + 0.0], + [FireworkType.BasicPeony, + Vec3(-50, 0, 140), + rP(), + rS(), + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + rT(), + 3.0], + [FireworkType.AdvancedPeony, + Vec3(-90, 0, 110), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.AdvancedPeony, + Vec3(0, 0, 90), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.AdvancedPeony, + Vec3(90, 0, 110), + rP(), + rS(), + rC(), + rC(), + rT(), + 4.0], + [FireworkType.GlowFlare, + Vec3(-90, 0, 80), + Vec3(120, 0, 0), + 1.5, + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 3.0, + 3.0], + [FireworkType.Ring, + Vec3(-90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.2], + [FireworkType.Ring, + Vec3(-30, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.2], + [FireworkType.Ring, + Vec3(30, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.2], + [FireworkType.Ring, + Vec3(90, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.Bees, + Vec3(0, 50, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 2.0], + [FireworkType.TrailBurst, + Vec3(-70, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.TrailBurst, + Vec3(70, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.DiademPeony, + Vec3(90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(-30, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(30, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(-90, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.PalmTree, + Vec3(0, 40, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 4.0], + [FireworkType.Chrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.0], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.Saturn, + Vec3(90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.0], + [FireworkType.Saturn, + Vec3(-90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 2.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 90), + Vec3(-120, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(-60, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 110), + Vec3(0, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 120), + Vec3(60, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 130), + Vec3(120, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 2.0], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 2.0], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 2.0], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 1.0], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.AmericanFlag, + Vec3(0, 0, 230), + Vec3(-50, 0, 0), + rS(), + rC(), + rC(), + rT(), + 6], + [FireworkType.DiademPeony, + Vec3(90, 0, 120), + rP(), + rS(), + rC(), + rC(), + 2.5, + 0.15], + [FireworkType.DiademPeony, + Vec3(30, 0, 140), + rP(), + rS(), + rC(), + rC(), + 2.5, + 0.15], + [FireworkType.DiademPeony, + Vec3(-30, 0, 120), + rP(), + rS(), + rC(), + rC(), + 2.5, + 0.15], + [FireworkType.DiademPeony, + Vec3(-90, 0, 140), + rP(), + rS(), + rC(), + rC(), + 2.5, + 3.0], + [FireworkType.Mickey, + Vec3(0, 0, 100), + rP(), + 1.4, + rC(), + rC(), + 2.0, + 10.0]], + PartyGlobals.FireworkShows.Summer: [[FireworkType.DiademPeony, + Vec3(90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.0], + [FireworkType.DiademPeony, + Vec3(0, 0, 70), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.0], + [FireworkType.DiademPeony, + Vec3(-90, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 130), + Vec3(0, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 3.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 90), + Vec3(-50, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 2.5, + 0.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 90), + Vec3(50, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 2.5, + 2.0], + [FireworkType.DiademChrysanthemum, + Vec3(40, 50, 140), + rP(), + rS(), + rC(), + rC(), + rT(), + 1.5], + [FireworkType.DiademChrysanthemum, + Vec3(-40, -50, 140), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.GlowFlare, + Vec3(-90, 0, 80), + Vec3(120, 0, 0), + 1.5, + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 3.0, + 5.5], + [FireworkType.DiademChrysanthemum, + Vec3(0, 0, 100), + Vec3(-120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(-120, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 1.0], + [FireworkType.DiademChrysanthemum, + Vec3(0, 0, 100), + Vec3(0, 20, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(0, 20, 0), + rS(), + rC(), + rC(), + 1.5, + 1.0], + [FireworkType.DiademChrysanthemum, + Vec3(0, 0, 100), + Vec3(120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(120, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 5.0], + [FireworkType.AdvancedPeony, + Vec3(-90, 0, 110), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.AdvancedPeony, + Vec3(0, 0, 90), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.AdvancedPeony, + Vec3(90, 0, 110), + rP(), + rS(), + rC(), + rC(), + rT(), + 4.0], + [FireworkType.Mickey, + Vec3(70, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.DiademPeony, + Vec3(90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(-30, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(30, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(-90, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.Bees, + Vec3(0, 0, 100), + rP(), + 1.4, + rC(), + rC(), + 2.0, + 4.0], + [FireworkType.Chrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.0], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.GlowFlare, + Vec3(200, 0, 180), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 2.0], + [FireworkType.GlowFlare, + Vec3(150, 10, 180), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(100, 20, 180), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(50, 30, 180), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 40, 180), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 2.0], + [FireworkType.Saturn, + Vec3(0, 0, 100), + Vec3(-120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(-120, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 1.0], + [FireworkType.Saturn, + Vec3(0, 0, 100), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 1.0], + [FireworkType.Saturn, + Vec3(0, 0, 100), + Vec3(120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(120, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 5.0], + [FireworkType.GlowFlare, + Vec3(-15, 0, 60), + Vec3(0, 0, 0), + rS(), + Vec4(1, 1, 0.4, 1), + Vec4(1, 1, 1, 1), + 2.5, + 0.0], + [FireworkType.GlowFlare, + Vec3(15, 0, 60), + Vec3(0, 0, 0), + rS(), + Vec4(1, 1, 0.4, 1), + Vec4(1, 1, 1, 1), + 2.5, + 0.0], + [FireworkType.IceCream, + Vec3(0, 0, 80), + Vec3(0, 0, 0), + 1.0, + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 0.0], + [FireworkType.IceCream, + Vec3(0, 0, 110), + Vec3(0, 0, 0), + 0.6, + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 0.0], + [FireworkType.IceCream, + Vec3(0, 0, 130), + Vec3(0, 0, 0), + 0.3, + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 10.0]], + ToontownGlobals.NEW_YEAR_FIREWORKS: [[FireworkType.GlowFlare, + Vec3(0, 0, 180), + Vec3(-120, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 120), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 80), + Vec3(-10, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 80), + Vec3(10, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 120), + Vec3(60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 180), + Vec3(120, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 2.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 80), + Vec3(120, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 120), + Vec3(60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 180), + Vec3(10, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 180), + Vec3(-10, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 120), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 80), + Vec3(-120, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 2.0], + [FireworkType.GlowFlare, + Vec3(-180, 0, 180), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 2.5, + 0.15], + [FireworkType.GlowFlare, + Vec3(180, 0, 180), + Vec3(60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 2.5, + 0.15], + [FireworkType.DiademChrysanthemum, + Vec3(40, 50, 140), + rP(), + rS(), + rC(), + rC(), + rT(), + 1.5], + [FireworkType.DiademChrysanthemum, + Vec3(-40, -50, 140), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.DiademChrysanthemum, + Vec3(-140, 50, 120), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.DiademChrysanthemum, + Vec3(70, -40, 90), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 0.25], + [FireworkType.DiademChrysanthemum, + Vec3(-100, 30, 60), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.DiademChrysanthemum, + Vec3(0, 20, 100), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 0.25], + [FireworkType.DiademChrysanthemum, + Vec3(-70, 0, 130), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + Vec3(120, 50, 100), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 3.5], + [FireworkType.Mickey, + Vec3(70, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.5], + [FireworkType.DiademPeony, + Vec3(90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(-30, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(30, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(-90, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.Chrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 1.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.Saturn, + Vec3(0, 0, 100), + Vec3(-120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.Saturn, + Vec3(20, 0, 70), + Vec3(-120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademPeony, + Vec3(-30, 0, 120), + Vec3(120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademPeony, + Vec3(0, 0, 90), + Vec3(120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 4.0], + [FireworkType.DiademPeony, + Vec3(-140, 50, 120), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.DiademChrysanthemum, + Vec3(70, -40, 90), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 0.25], + [FireworkType.DiademPeony, + Vec3(-100, 30, 60), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 2.25, + 0.25], + [FireworkType.DiademChrysanthemum, + Vec3(0, 20, 100), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 2.0], + [FireworkType.DiademPeony, + Vec3(-70, 0, 130), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + rT(), + 1.5], + [FireworkType.DiademChrysanthemum, + Vec3(120, 50, 100), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 5.0], + [FireworkType.Bees, + Vec3(0, 0, 100), + rP(), + 1.4, + rC(), + rC(), + 2.0, + 10.0]]} + showData[ToontownGlobals.COMBO_FIREWORKS] = showData[ToontownGlobals.NEW_YEAR_FIREWORKS] + sectionData = {ToontownGlobals.SUMMER_FIREWORKS: [(0, 24), (24, len(showData[ToontownGlobals.SUMMER_FIREWORKS]))], + PartyGlobals.FireworkShows.Summer: [(0, 24), (24, len(showData[PartyGlobals.FireworkShows.Summer]))], + ToontownGlobals.NEW_YEAR_FIREWORKS: [(0, len(showData[PartyGlobals.FireworkShows.Summer]))], + ToontownGlobals.COMBO_FIREWORKS: [(0, len(showData[PartyGlobals.FireworkShows.Summer]))]} + showMusic = {} + + @classmethod + def isValidShowType(cls, showType = -1): + if showType in cls.showData.keys(): + return True + else: + return False + + def __init__(self, showType = ToontownGlobals.NEW_YEAR_FIREWORKS): + NodePath.__init__(self, 'FireworkShow') + self.showType = showType + self.sectionIvals = [] + self.fireworks = [] + self.delaySectionStart = None + self.curSection = None + self.curOffset = 0.0 + return + + def beginSection(self, startIndex, endIndex, offset): + taskMgr.remove('beginSection' + str(startIndex) + str(endIndex)) + sectionIval = Parallel() + time = 2.0 + showMusic = self.showMusic.get(self.showType) + if showMusic: + base.musicMgr.load(showMusic, looping=False) + musicOffset = self.getDuration(0, startIndex) - self.getDuration(startIndex, startIndex) + offset + sectionIval.append(Func(base.musicMgr.request, showMusic, priority=2, looping=False)) + sectionIval.append(Func(base.musicMgr.offsetMusic, musicOffset)) + sectionData = self.showData.get(self.showType)[startIndex:endIndex] + for fireworkInfo in sectionData: + typeId = fireworkInfo[0] + velocity = fireworkInfo[1] + pos = fireworkInfo[2] + scale = fireworkInfo[3] + color1 = fireworkInfo[4] + color2 = fireworkInfo[5] + if color2 == -1: + color2 = color1 + trailDur = fireworkInfo[6] + delay = fireworkInfo[7] + firework = Firework(typeId, velocity, scale, color1, color2, trailDur) + firework.reparentTo(self) + firework.setPos(pos) + self.fireworks.append(firework) + sectionIval.append(Sequence(Wait(time), firework.generateFireworkIval())) + time += delay + + self.sectionIvals.append(sectionIval) + self.curSection = sectionIval + self.curOffset = offset + self.delaySectionStart = FrameDelayedCall('delaySectionStart', self.startCurSection, frames=24) + + def startCurSection(self): + self.curSection.start(self.curOffset) + + def begin(self, timestamp): + time = 0.0 + for section in self.sectionData.get(self.showType): + startIndex = section[0] + endIndex = section[1] + sectionDur = self.getDuration(startIndex, endIndex) + if timestamp < sectionDur: + timestamp = max(0.0, timestamp) + taskMgr.doMethodLater(time, self.beginSection, 'beginSection' + str(startIndex) + str(endIndex), extraArgs=[startIndex, endIndex, timestamp]) + time = time + sectionDur - timestamp + timestamp -= sectionDur + + def getDuration(self, startIndex = 0, endIndex = None): + duration = 0.0 + if endIndex == None: + endIndex = len(self.showData.get(self.showType)) + for firework in self.showData.get(self.showType)[startIndex:endIndex]: + duration += firework[7] + + return duration + + def getShowDuration(self, eventId = None): + duration = 0.0 + if eventId: + for firework in self.showData[eventId]: + duration += firework[7] + + else: + for firework in self.showData[self.showType]: + duration += firework[7] + + return duration + + def isPlaying(self): + for ival in self.sectionIvals: + if ival.isPlaying(): + return True + + return False + + def cleanupShow(self): + if self.delaySectionStart: + self.delaySectionStart.destroy() + del self.delaySectionStart + self.delaySectionStart = None + showMusic = self.showMusic.get(self.showType) + if showMusic: + base.musicMgr.requestFadeOut(showMusic) + for section in self.sectionData.get(self.showType): + startIndex = section[0] + endIndex = section[1] + taskMgr.remove('beginSection' + str(startIndex) + str(endIndex)) + + for ival in self.sectionIvals: + ival.pause() + del ival + ival = None + + self.sectionIvals = [] + for firework in self.fireworks: + firework.cleanup() + del firework + firework = None + + self.fireworks = [] + return diff --git a/toontown/effects/FireworkShowMixin.py b/toontown/effects/FireworkShowMixin.py new file mode 100755 index 00000000..f497b2a7 --- /dev/null +++ b/toontown/effects/FireworkShowMixin.py @@ -0,0 +1,285 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.hood import * +import Fireworks +import FireworkShows +from FireworkGlobals import skyTransitionDuration, preShowPauseDuration, postShowPauseDuration, preNormalMusicPauseDuration +from toontown.effects.FireworkShow import FireworkShow + +class FireworkShowMixin: + notify = DirectNotifyGlobal.directNotify.newCategory('FireworkShowMixin') + + def __init__(self, restorePlaygroundMusic = True, startDelay = 0.0): + self.currentShow = None + self.restorePlaygroundMusic = restorePlaygroundMusic + self.startDelay = startDelay + self.timestamp = None + self.fireworkShow = None + self.eventId = SUMMER_FIREWORKS + self.accept('MusicEnabled', self.startMusic) + return + + def disable(self): + if self.currentShow: + self.currentShow.pause() + self.currentShow = None + if base.cr.config.GetBool('want-old-fireworks', 0): + ivalMgr.finishIntervalsMatching('shootFirework*') + else: + self.destroyFireworkShow() + from toontown.hood import DDHood + if isinstance(self.getHood(), DDHood.DDHood): + self.getHood().whiteFogColor = Vec4(0.8, 0.8, 0.8, 1) + self.restoreCameraLens() + if hasattr(self.getHood(), 'loader'): + self.getGeom().clearColorScale() + if hasattr(self.getHood(), 'sky'): + self.getSky().show() + self.getSky().clearColorScale() + if hasattr(base, 'localAvatar') and base.localAvatar: + base.localAvatar.clearColorScale() + base.setBackgroundColor(DefaultBackgroundColor) + self.ignoreAll() + return + + def startMusic(self): + if self.timestamp: + self.getLoader().music.stop() + t = globalClockDelta.localElapsedTime(self.timestamp) - self.startDelay + base.playMusic(self.showMusic, 0, 1, 1, max(0, t)) + + def shootFirework(self, x, y, z, style, color1, color2): + amp = 5 + Fireworks.shootFirework(style, x, y, z, color1, color2, amp) + + def startShow(self, eventId, style, songId, timestamp, root = render): + t = globalClockDelta.localElapsedTime(timestamp) - self.startDelay + self.timestamp = timestamp + self.showMusic = None + self.eventId = eventId + if base.config.GetBool('want-old-fireworks', 0): + self.currentShow = self.getFireworkShowIval(eventId, style, songId, t) + if self.currentShow: + self.currentShow.start(t) + else: + self.createFireworkShow() + if t > self.fireworkShow.getShowDuration(): + return + preShow = self.preShow(eventId, songId, t) + postShow = self.postShow(eventId) + beginFireworkShow = Func(self.beginFireworkShow, max(0, t), root) + self.currentShow = Sequence(preShow, beginFireworkShow, Wait(max(0, self.fireworkShow.getShowDuration() - max(0, t))), postShow) + self.currentShow.start() + return + + def preShow(self, eventId, songId, startT): + if eventId == SUMMER_FIREWORKS: + instructionMessage = TTLocalizer.FireworksInstructions + startMessage = TTLocalizer.FireworksJuly4Beginning + endMessage = TTLocalizer.FireworksJuly4Ending + songs = ['tt_summer', 'firework_music'] + musicFile = 'phase_4/audio/bgm/%s.ogg' % songs[songId] + elif eventId == NEW_YEAR_FIREWORKS: + instructionMessage = TTLocalizer.FireworksInstructions + startMessage = TTLocalizer.FireworksNewYearsEveBeginning + endMessage = TTLocalizer.FireworksNewYearsEveEnding + songs = ['new_years_fireworks_music', 'tt_s_ara_gen_fireworks_auldLangSyne'] + musicFile = 'phase_4/audio/bgm/%s.ogg' % songs[songId] + elif eventId == PartyGlobals.FireworkShows.Summer: + instructionMessage = TTLocalizer.FireworksActivityInstructions + startMessage = TTLocalizer.FireworksActivityBeginning + endMessage = TTLocalizer.FireworksActivityEnding + songs = ['tt_party1', 'tt_party2'] + musicFile = 'phase_4/audio/bgm/%s.ogg' % songs[songId] + elif eventId == COMBO_FIREWORKS: + instructionMessage = TTLocalizer.FireworksInstructions + startMessage = TTLocalizer.FireworksComboBeginning + endMessage = TTLocalizer.FireworksComboEnding + songs = ['new_years_fireworks_music', 'tt_s_ara_gen_fireworks_auldLangSyne'] + musicFile = 'phase_4/audio/bgm/%s.ogg' % songs[songId] + else: + FireworkShowMixin.notify.warning('Invalid fireworks event ID: %d' % eventId) + return None + + self.showMusic = loader.loadMusic(musicFile) + self.showMusic.setVolume(1) + + def __lightDecorationOn__(): + place = base.cr.playGame.getPlace() + if place is None: + return + if hasattr(place, 'halloweenLights'): + if not self.__checkStreetValidity(): + return + else: + place.halloweenLights = base.cr.playGame.getPlace().loader.geom.findAllMatches('**/*light*') + place.halloweenLights.extend(base.cr.playGame.getPlace().loader.geom.findAllMatches('**/*lamp*')) + for light in place.halloweenLights: + light.setColorScaleOff(0) + + elif not self.__checkHoodValidity(): + return + else: + place.loader.hood.halloweenLights = base.cr.playGame.hood.loader.geom.findAllMatches('**/*light*') + place.loader.hood.halloweenLights.extend(base.cr.playGame.hood.loader.geom.findAllMatches('**/*lamp*')) + for light in base.cr.playGame.hood.halloweenLights: + light.setColorScaleOff(0) + + if self.fireworkShow and not self.fireworkShow.isEmpty(): + self.fireworkShow.setColorScaleOff(0) + return + if self.__checkHoodValidity() and hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'sky') and base.cr.playGame.hood.sky: + preShow = Sequence(Func(base.localAvatar.setSystemMessage, 0, startMessage), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 2.5, Vec4(0.0, 0.0, 0.0, 1.0)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(0.25, 0.25, 0.35, 1)), LerpColorScaleInterval(base.localAvatar, 2.5, Vec4(0.85, 0.85, 0.85, 1)), Func(__lightDecorationOn__)), Func(base.setBackgroundColor, Vec4(0, 0, 0, 1)), Func(self.__checkDDFog), Func(base.camLens.setFar, 1000.0), Func(base.cr.playGame.hood.sky.hide), Func(base.localAvatar.setSystemMessage, 0, instructionMessage), Func(self.getLoader().music.stop), Wait(2.0), Func(base.playMusic, self.showMusic, 0, 1, 0.8, max(0, startT))) + return preShow + return None + + def restoreCameraLens(self): + hood = self.getHood() + if hood != None: + if hood.id == GoofySpeedway or hood.id == OutdoorZone: + base.camLens.setFar(SpeedwayCameraFar) + else: + base.camLens.setFar(DefaultCameraFar) + + def postShow(self, eventId): + if eventId == SUMMER_FIREWORKS: + endMessage = TTLocalizer.FireworksJuly4Ending + elif eventId == NEW_YEAR_FIREWORKS: + endMessage = TTLocalizer.FireworksNewYearsEveEnding + elif eventId == PartyGlobals.FireworkShows.Summer: + endMessage = TTLocalizer.FireworksActivityEnding + elif eventId == COMBO_FIREWORKS: + endMessage = TTLocalizer.FireworksComboEnding + else: + FireworkShowMixin.notify.warning('Invalid fireworks event ID: %d' % eventId) + return None + + if self.__checkHoodValidity() and hasattr(base.cr.playGame.hood, 'sky') and base.cr.playGame.hood.sky: + postShow = Sequence(Func(base.cr.playGame.hood.sky.show), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 2.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.localAvatar, 2.5, Vec4(1, 1, 1, 1))), Func(self.__restoreDDFog), Func(self.restoreCameraLens), Func(base.setBackgroundColor, DefaultBackgroundColor), Func(self.showMusic.stop), Func(base.localAvatar.setSystemMessage, 0, endMessage)) + if self.restorePlaygroundMusic: + postShow.append(Wait(2.0)) + postShow.append(Func(base.playMusic, self.getLoader().music, 1, 1, 0.8)) + return postShow + + def createFireworkShow(self): + if not self.fireworkShow: + self.fireworkShow = FireworkShow(self.eventId) + + def destroyFireworkShow(self): + if self.fireworkShow: + self.fireworkShow.cleanupShow() + self.fireworkShow = None + return + + def beginFireworkShow(self, timeStamp, root): + if self.fireworkShow and not self.fireworkShow.isPlaying(): + self.fireworkShow.begin(timeStamp) + self.fireworkShow.reparentTo(root) + hood = self.getHood() + if isinstance(hood, TTHood.TTHood): + self.fireworkShow.setPos(150, 0, 80) + self.fireworkShow.setHpr(90, 0, 0) + elif isinstance(hood, BRHood.BRHood): + self.fireworkShow.setPos(-200, -60, 50) + self.fireworkShow.setHpr(270, 0, 0) + elif isinstance(hood, MMHood.MMHood): + self.fireworkShow.setPos(150, -25, 40) + self.fireworkShow.setHpr(90, 0, 0) + elif isinstance(hood, DGHood.DGHood): + self.fireworkShow.setPos(-80, -50, 60) + self.fireworkShow.setHpr(0, 0, 0) + elif isinstance(hood, DLHood.DLHood): + self.fireworkShow.setPos(-160, 0, 80) + self.fireworkShow.setHpr(270, 0, 0) + elif isinstance(hood, GSHood.GSHood): + self.fireworkShow.setPos(60, -350, 80) + self.fireworkShow.setHpr(20, 0, 0) + elif isinstance(hood, DDHood.DDHood): + self.fireworkShow.setPos(150, 0, 50) + self.fireworkShow.setHpr(90, 0, 0) + elif isinstance(hood, OZHood.OZHood): + self.fireworkShow.setPos(-450, -80, 140) + self.fireworkShow.setHpr(300, 0, 0) + elif isinstance(hood, PartyHood.PartyHood): + self.fireworkShow.setPos(0, -400, 120) + self.fireworkShow.lookAt(0, 0, 0) + self.fireworkShow.setScale(1.8) + + def getFireworkShowIval(self, eventId, index, songId, startT): + show = FireworkShows.getShow(eventId, index) + if show is None: + FireworkShowMixin.notify.warning('could not find firework show: index: %s' % index) + return + preShow = self.preShow(eventId, songId, startT) + mainShow = Sequence() + currentT = skyTransitionDuration + preShowPauseDuration + for effect in show: + waitTime, style, colorIndex1, colorIndex2, amp, x, y, z = effect + if waitTime > 0: + currentT += waitTime + mainShow.append(Wait(waitTime)) + if currentT >= startT: + mainShow.append(Func(Fireworks.shootFirework, style, x, y, z, colorIndex1, colorIndex2, amp)) + + postShow = self.postShow(eventId) + return Sequence(preShow, mainShow, postShow) + + def clearMyColorScales(self): + if self.getGeom() and not self.getGeom().isEmpty(): + self.getGeom().clearColorScale() + if self.getSky() and not self.getSky().isEmpty(): + self.getSky().clearColorScale() + + def getLoader(self): + if base.cr.playGame.hood != None: + return base.cr.playGame.hood.loader + return + + def getHood(self): + if base.cr.playGame.hood != None: + return base.cr.playGame.hood + return + + def getGeom(self): + loader = self.getLoader() + if loader: + return loader.geom + return None + + def getSky(self): + hood = self.getHood() + if hood: + return hood.sky + return None + + def __checkDDFog(self): + from toontown.hood import DDHood + if isinstance(self.getHood(), DDHood.DDHood): + self.getHood().whiteFogColor = Vec4(0.2, 0.2, 0.2, 1) + if hasattr(base.cr.playGame.getPlace(), 'cameraSubmerged'): + if not base.cr.playGame.getPlace().cameraSubmerged: + self.getHood().setWhiteFog() + + def __restoreDDFog(self): + from toontown.hood import DDHood + if isinstance(self.getHood(), DDHood.DDHood): + self.getHood().whiteFogColor = Vec4(0.8, 0.8, 0.8, 1) + if hasattr(base.cr.playGame.getPlace(), 'cameraSubmerged'): + if not base.cr.playGame.getPlace().cameraSubmerged: + self.getHood().setWhiteFog() + + def __checkStreetValidity(self): + if hasattr(base.cr.playGame, 'getPlace') and base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'loader') and base.cr.playGame.getPlace().loader and hasattr(base.cr.playGame.getPlace().loader, 'geom') and base.cr.playGame.getPlace().loader.geom: + return True + else: + return False + + def __checkHoodValidity(self): + if hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'loader') and base.cr.playGame.hood.loader and hasattr(base.cr.playGame.hood.loader, 'geom') and base.cr.playGame.hood.loader.geom: + return True + else: + return False diff --git a/toontown/effects/FireworkShows.py b/toontown/effects/FireworkShows.py new file mode 100755 index 00000000..c1f196ec --- /dev/null +++ b/toontown/effects/FireworkShows.py @@ -0,0 +1,13881 @@ +from FireworkGlobals import * +from toontown.toonbase import ToontownGlobals +from toontown.parties import PartyGlobals +shows = {ToontownGlobals.SUMMER_FIREWORKS: [((2, + ROCKET, + RED, + RED, + 5, + 80, + 88, + 30), + (0.1, + ROCKET, + BLUE, + BLUE, + 5, + 80, + -88, + 30), + (0.1, + ROCKET, + WHITE, + WHITE, + 5, + 80, + -66, + 30), + (0.1, + ROCKET, + WHITE, + WHITE, + 5, + 80, + 66, + 30), + (0.1, + ROCKET, + BLUE, + BLUE, + 5, + 80, + 44, + 30), + (0.1, + ROCKET, + RED, + RED, + 5, + 80, + -44, + 30), + (0.1, + ROCKET, + RED, + RED, + 5, + 80, + 22, + 30), + (0.1, + ROCKET, + BLUE, + BLUE, + 5, + 80, + -22, + 30), + (0.1, + ROCKET, + WHITE, + WHITE, + 5, + 80, + 0, + 30), + (2, + CIRCLELARGE, + RED, + RED, + 20, + 120, + -2, + 120), + (0.1, + CIRCLE, + RED, + RED, + 12, + 120, + -30, + 150), + (0.1, + CIRCLE, + RED, + RED, + 12, + 120, + 26, + 150), + (1.5, + ROCKET, + WHITE, + YELLOW, + 3, + 80, + -40, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 80, + -20, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 80, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 80, + 20, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 80, + 40, + 0), + (2, + CIRCLE, + BLUE, + BLUE, + 6, + 80, + -40, + 70), + (0.2, + CIRCLE, + RED, + RED, + 7, + 80, + -20, + 70), + (0.2, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 80, + 0, + 70), + (0.2, + CIRCLE, + RED, + RED, + 7, + 80, + 20, + 70), + (0.2, + CIRCLE, + BLUE, + BLUE, + 6, + 80, + 40, + 70), + (1, + CIRCLE, + WHITE, + WHITE, + 14, + 80, + 0, + 80), + (0.1, + CIRCLE, + WHITE, + WHITE, + 7, + 80, + -10, + 90), + (0.1, + CIRCLE, + WHITE, + WHITE, + 7, + 80, + 10, + 90), + (1, + ROCKET, + WHITE, + YELLOW, + 3, + -40, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 0, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 40, + 0, + 0), + (2, + CIRCLE, + BLUE, + BLUE, + 6, + -40, + 0, + 60), + (0.2, + CIRCLE, + RED, + RED, + 7, + -20, + 0, + 60), + (0.2, + CIRCLE, + WHITE, + WHITE, + 8, + 0, + 0, + 60), + (0.2, + CIRCLE, + RED, + RED, + 7, + 20, + 0, + 60), + (0.2, + CIRCLE, + BLUE, + BLUE, + 6, + 40, + 0, + 60), + (1.5, + CIRCLE, + WHITE, + WHITE, + 10, + 0, + 0, + 80), + (0.1, + CIRCLE, + WHITE, + WHITE, + 5, + -10, + 0, + 90), + (0.1, + CIRCLE, + WHITE, + WHITE, + 5, + 10, + 0, + 90), + (2, + ROCKET, + WHITE, + YELLOW, + 3, + -40, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + 0, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + 40, + 0, + 0), + (1.5, + CIRCLE, + BLUE, + RED, + 10, + -30, + -30, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + -30, + -30, + 80), + (0.5, + CIRCLE, + BLUE, + RED, + 10, + 30, + -30, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 30, + -30, + 80), + (0.5, + CIRCLE, + BLUE, + RED, + 10, + -30, + 30, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + -30, + 30, + 80), + (0.5, + CIRCLE, + BLUE, + RED, + 10, + 30, + 30, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 30, + 30, + 80), + (2, + CIRCLELARGE, + RED, + WHITE, + 16, + 0, + 0, + 100), + (2, + CIRCLELARGE, + BLUE, + WHITE, + 16, + 0, + 0, + 100), + (2, + CIRCLE, + RED, + RED, + 8, + -10, + 0, + 90), + (0.3, + CIRCLE, + BLUE, + BLUE, + 10, + 0, + 20, + 60), + (0.3, + CIRCLE, + RED, + RED, + 5, + 10, + 10, + 80), + (0.5, + CIRCLE, + RED, + BLUE, + 8, + -10, + 0, + 90), + (0.4, + CIRCLE, + RED, + BLUE, + 10, + 0, + 10, + 60), + (0.4, + CIRCLE, + RED, + WHITE, + 8, + -10, + 0, + 90), + (0.5, + CIRCLE, + BLUE, + WHITE, + 10, + 0, + -20, + 80), + (0.4, + CIRCLE, + WHITE, + WHITE, + 5, + 10, + 0, + 90), + (1, + CIRCLE, + WHITE, + WHITE, + 15, + 0, + 0, + 100), + (1.5, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 0, + 0), + (2, + RING, + RED, + RED, + 8, + 0, + 0, + 100), + (0.1, + RING, + BLUE, + BLUE, + 8, + 0, + 0, + 100), + (1.5, + ROCKET, + RED, + RED, + 5, + 82, + -47, + 20), + (0.1, + ROCKET, + RED, + RED, + 5, + 93, + 45, + 26), + (0.1, + ROCKET, + RED, + RED, + 5, + 37, + -47, + 20), + (0.1, + ROCKET, + RED, + RED, + 5, + 60, + 45, + 26), + (1.5, + ROCKET, + BLUE, + BLUE, + 5, + 82, + -47, + 20), + (0.1, + ROCKET, + BLUE, + BLUE, + 5, + 93, + 45, + 26), + (0.1, + ROCKET, + BLUE, + BLUE, + 5, + 37, + -47, + 20), + (0.1, + ROCKET, + BLUE, + BLUE, + 5, + 60, + 45, + 26), + (2, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 0, + 0), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 0, + 0, + 0), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 0, + 0), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 40, + 0, + 0), + (2, + CIRCLE, + BLUE, + BLUE, + 6, + -40, + 0, + 60), + (0.1, + CIRCLE, + BLUE, + BLUE, + 6, + 40, + 0, + 60), + (0.1, + CIRCLE, + RED, + RED, + 7, + -20, + 0, + 60), + (0.1, + CIRCLE, + RED, + RED, + 7, + 20, + 0, + 60), + (0.1, + CIRCLE, + WHITE, + WHITE, + 8, + 0, + 0, + 60), + (1, + CIRCLELARGE, + BLUE, + WHITE, + 16, + 0, + 0, + 100), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + -21, + 30), + (1, + CIRCLELARGE, + BLUE, + WHITE, + 16, + 0, + 0, + 100), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + -21, + 30), + (1, + CIRCLE, + WHITE, + WHITE, + 12, + 0, + 0, + 80), + (0.1, + CIRCLE, + WHITE, + WHITE, + 6, + -10, + 0, + 90), + (0.1, + CIRCLE, + WHITE, + WHITE, + 6, + 10, + 0, + 90), + (1, + RING, + RED, + WHITE, + 10, + 0, + 0, + 80), + (0.1, + RING, + BLUE, + WHITE, + 5, + -10, + 0, + 90), + (0.1, + RING, + BLUE, + WHITE, + 5, + 10, + 0, + 90), + (1, + CIRCLE, + BLUE, + RED, + 10, + -20, + -20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + -20, + -20, + 80), + (0.5, + CIRCLE, + BLUE, + RED, + 10, + 20, + -20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 20, + -20, + 80), + (0.5, + CIRCLE, + BLUE, + RED, + 10, + -20, + 20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + -20, + 20, + 80), + (0.5, + CIRCLE, + BLUE, + RED, + 10, + 20, + 20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 20, + 20, + 80), + (2, + CIRCLE, + WHITE, + RED, + 8, + -10, + 0, + 90), + (0.1, + ROCKET, + WHITE, + YELLOW, + 6, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 6, + 120, + -21, + 30), + (0.3, + CIRCLE, + WHITE, + BLUE, + 10, + 0, + 20, + 60), + (0.2, + CIRCLE, + WHITE, + RED, + 5, + 10, + 10, + 80), + (0.2, + CIRCLE, + WHITE, + RED, + 10, + 0, + 10, + 60), + (0.3, + CIRCLE, + WHITE, + WHITE, + 5, + 10, + 0, + 90), + (0.2, + CIRCLE, + WHITE, + RED, + 8, + -10, + 0, + 90), + (0.3, + CIRCLE, + WHITE, + BLUE, + 10, + 0, + -20, + 80), + (0.2, + CIRCLE, + WHITE, + RED, + 5, + 10, + 0, + 90), + (0.1, + ROCKET, + WHITE, + YELLOW, + 6, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 6, + 120, + -21, + 30), + (1, + CIRCLELARGE, + RED, + RED, + 12, + 0, + 0, + 80), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 14, + 0, + 0, + 90), + (0.2, + CIRCLELARGE, + BLUE, + BLUE, + 16, + 0, + 0, + 100)), + ((1, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.8, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.6, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.4, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (2, + CIRCLE, + WHITE, + YELLOW, + 5, + 0, + 80, + 80), + (1, + CIRCLE, + PURPLE, + PURPLE, + 4, + -15, + 70, + 90), + (0.5, + CIRCLE, + GREEN, + GREEN, + 6, + 15, + 100, + 85), + (1, + CIRCLE, + CYAN, + CYAN, + 8, + 20, + 90, + 90), + (0.6, + CIRCLE, + YELLOW, + WHITE, + 4, + 30, + 110, + 90), + (1, + CIRCLE, + PINK, + PINK, + 5, + 10, + 80, + 80), + (0.3, + CIRCLE, + YELLOW, + YELLOW, + 5, + 0, + 80, + 90), + (1, + CIRCLE, + WHITE, + WHITE, + 8, + -20, + 120, + 85), + (0.4, + CIRCLE, + PINK, + YELLOW, + 8, + 30, + 85, + 90), + (0.7, + CIRCLE, + YELLOW, + PINK, + 8, + 10, + 90, + 100), + (2, + ROCKET, + WHITE, + YELLOW, + 3, + 10, + 90, + 0), + (2, + CIRCLE, + YELLOW, + YELLOW, + 8, + 10, + 90, + 70), + (0.2, + RING, + YELLOW, + YELLOW, + 8, + 10, + 90, + 70), + (0.8, + CIRCLE, + YELLOW, + YELLOW, + 8, + 10, + 90, + 80), + (0.2, + RING, + YELLOW, + YELLOW, + 8, + 10, + 90, + 80), + (1, + ROCKET, + WHITE, + YELLOW, + 3, + 10, + 90, + 0), + (2, + CIRCLE, + GREEN, + GREEN, + 8, + 10, + 90, + 70), + (0.2, + RING, + GREEN, + GREEN, + 8, + 10, + 90, + 70), + (0.8, + CIRCLE, + GREEN, + GREEN, + 8, + 10, + 90, + 80), + (0.2, + RING, + GREEN, + GREEN, + 8, + 10, + 90, + 80), + (1, + ROCKET, + WHITE, + YELLOW, + 3, + 10, + 90, + 0), + (2, + CIRCLE, + PINK, + PINK, + 8, + 10, + 90, + 75), + (0.2, + RING, + PINK, + PINK, + 8, + 10, + 90, + 75), + (0.8, + CIRCLE, + PINK, + PINK, + 8, + 10, + 90, + 85), + (0.2, + RING, + PINK, + PINK, + 8, + 10, + 90, + 85), + (1, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 70, + 0), + (2, + CIRCLE, + WHITE, + WHITE, + 10, + 20, + 70, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 110, + 0), + (2, + CIRCLE, + WHITE, + WHITE, + 10, + 20, + 110, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 70, + 0), + (2, + CIRCLE, + WHITE, + WHITE, + 10, + -20, + 70, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 110, + 0), + (2, + CIRCLE, + WHITE, + WHITE, + 10, + -20, + 110, + 60), + (1.5, + CIRCLE, + PURPLE, + PINK, + 8, + -30, + 110, + 60), + (0.5, + CIRCLE, + PURPLE, + PINK, + 9, + -15, + 110, + 60), + (0.5, + CIRCLE, + PURPLE, + PINK, + 10, + 0, + 110, + 60), + (0.5, + CIRCLE, + PURPLE, + PINK, + 9, + 15, + 110, + 60), + (0.5, + CIRCLE, + PURPLE, + PINK, + 8, + 30, + 110, + 60), + (1.5, + CIRCLE, + GREEN, + CYAN, + 8, + 30, + 110, + 70), + (0.5, + CIRCLE, + GREEN, + CYAN, + 9, + 15, + 110, + 70), + (0.5, + CIRCLE, + GREEN, + CYAN, + 10, + 0, + 110, + 70), + (0.5, + CIRCLE, + GREEN, + CYAN, + 9, + -15, + 110, + 70), + (0.5, + CIRCLE, + GREEN, + CYAN, + 8, + -30, + 110, + 70), + (2, + CIRCLE, + WHITE, + YELLOW, + 6, + 0, + 80, + 80), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 8, + -15, + 70, + 90), + (0.5, + CIRCLE, + GREEN, + GREEN, + 6, + 15, + 100, + 85), + (1, + CIRCLE, + CYAN, + CYAN, + 8, + 20, + 90, + 90), + (0.5, + CIRCLE, + YELLOW, + WHITE, + 7, + 30, + 110, + 90), + (1, + CIRCLE, + PINK, + PINK, + 12, + 10, + 80, + 80), + (0.6, + CIRCLELARGE, + YELLOW, + YELLOW, + 5, + 0, + 80, + 90), + (1, + CIRCLE, + PINK, + YELLOW, + 8, + 30, + 85, + 90), + (0.5, + CIRCLE, + YELLOW, + PINK, + 12, + 10, + 90, + 100), + (0.4, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.5, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 90, + 60), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.8, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 90, + 70), + (0.8, + CIRCLELARGE, + WHITE, + WHITE, + 10, + 0, + 90, + 80), + (0.2, + RING, + YELLOW, + YELLOW, + 12, + 0, + 90, + 80), + (0.8, + CIRCLELARGE, + WHITE, + WHITE, + 12, + 0, + 90, + 80), + (0.2, + RING, + YELLOW, + YELLOW, + 12, + 0, + 90, + 80), + (1.5, + RING, + YELLOW, + YELLOW, + 6, + 20, + 110, + 70), + (0.4, + RING, + YELLOW, + YELLOW, + 6, + 0, + 90, + 65), + (0.4, + RING, + YELLOW, + YELLOW, + 8, + -10, + 100, + 80), + (0.4, + RING, + YELLOW, + YELLOW, + 8, + 10, + 110, + 60), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.4, + RING, + YELLOW, + YELLOW, + 12, + -20, + 90, + 70), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.4, + CIRCLELARGE, + WHITE, + WHITE, + 12, + 0, + 90, + 80), + (0.7, + RING, + YELLOW, + YELLOW, + 6, + 0, + 90, + 65), + (0.2, + RING, + YELLOW, + YELLOW, + 8, + -10, + 100, + 80), + (0.2, + RING, + YELLOW, + YELLOW, + 8, + 10, + 110, + 60), + (0.2, + RING, + YELLOW, + YELLOW, + 12, + -20, + 90, + 70), + (1, + CIRCLESPRITE, + WHITE, + FLOWER, + 24, + 1, + 92, + 80)), + ((1, + ROCKET, + WHITE, + YELLOW, + 3, + 63, + 88, + 10), + (0, + ROCKET, + WHITE, + YELLOW, + 3, + 63, + -88, + 10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 88, + 10), + (0, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + -88, + 10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -22, + 88, + 10), + (0, + ROCKET, + WHITE, + YELLOW, + 3, + -22, + -88, + 10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -83, + 88, + 10), + (0, + ROCKET, + WHITE, + YELLOW, + 3, + -83, + -88, + 10), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 30, + 0, + -20), + (2, + CIRCLE, + PURPLE, + PINK, + 8, + 30, + 0, + 80), + (0.2, + CIRCLE, + PURPLE, + PINK, + 8, + 10, + 0, + 80), + (0.4, + CIRCLE, + PURPLE, + PINK, + 8, + 0.5, + 0, + 80), + (0.5, + CIRCLE, + PURPLE, + PINK, + 8, + 30, + 20, + 80), + (0.2, + CIRCLE, + PURPLE, + PINK, + 8, + 30, + -20, + 80), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 10, + 30, + 0, + 100), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 0, + -20), + (2, + CIRCLE, + YELLOW, + PINK, + 8, + 0, + 0, + 80), + (0.2, + CIRCLE, + YELLOW, + PINK, + 8, + -20, + 0, + 80), + (0.4, + CIRCLE, + YELLOW, + PINK, + 8, + 20, + 0, + 80), + (0.5, + CIRCLE, + YELLOW, + PINK, + 8, + 0, + 20, + 80), + (0.2, + CIRCLE, + YELLOW, + PINK, + 8, + 0, + -20, + 80), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 10, + 0, + 0, + 100), + (1.5, + ROCKET, + WHITE, + YELLOW, + 5, + -30, + 0, + -20), + (2, + CIRCLE, + PEACH, + PINK, + 8, + -30, + 0, + 80), + (0.2, + CIRCLE, + PEACH, + PINK, + 8, + 0.5, + 0, + 80), + (0.4, + CIRCLE, + PEACH, + PINK, + 8, + 10, + 0, + 80), + (0.5, + CIRCLE, + PEACH, + PINK, + 8, + -30, + 20, + 80), + (0.2, + CIRCLE, + PEACH, + PINK, + 8, + -30, + -20, + 80), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 10, + -30, + 0, + 100), + (2, + CIRCLE, + PEACH, + PEACH, + 3, + 0, + 0, + 80), + (0.5, + RING, + YELLOW, + WHITE, + 3, + 0, + 0, + 80), + (0.1, + RING, + YELLOW, + WHITE, + 3, + 0, + 0, + 80), + (1, + CIRCLE, + PEACH, + PEACH, + 9, + 0, + 0, + 100), + (0.5, + RING, + YELLOW, + WHITE, + 9, + 0, + 0, + 100), + (0.1, + RING, + YELLOW, + WHITE, + 9, + 0, + 0, + 100), + (1, + ROCKET, + YELLOW, + YELLOW, + 5, + 80, + 88, + 30), + (0.1, + ROCKET, + YELLOW, + YELLOW, + 5, + 80, + -88, + 30), + (0.1, + ROCKET, + PURPLE, + PURPLE, + 5, + 80, + -66, + 30), + (0.1, + ROCKET, + PURPLE, + PURPLE, + 5, + 80, + 66, + 30), + (0.1, + ROCKET, + PINK, + PINK, + 5, + 80, + 44, + 30), + (0.1, + ROCKET, + PINK, + PINK, + 5, + 80, + -44, + 30), + (0.1, + ROCKET, + PEACH, + PEACH, + 5, + 80, + 22, + 30), + (0.1, + ROCKET, + PEACH, + PEACH, + 5, + 80, + -22, + 30), + (0.1, + ROCKET, + WHITE, + WHITE, + 5, + 80, + 0, + 30), + (2, + ROCKET, + YELLOW, + YELLOW, + 5, + -80, + 88, + 30), + (0.1, + ROCKET, + YELLOW, + YELLOW, + 5, + -80, + -88, + 30), + (0.1, + ROCKET, + PURPLE, + PURPLE, + 5, + -80, + -66, + 30), + (0.1, + ROCKET, + PURPLE, + PURPLE, + 5, + -80, + 66, + 30), + (0.1, + ROCKET, + PINK, + PINK, + 5, + -80, + 44, + 30), + (0.1, + ROCKET, + PINK, + PINK, + 5, + -80, + -44, + 30), + (0.1, + ROCKET, + PEACH, + PEACH, + 5, + -80, + 22, + 30), + (0.1, + ROCKET, + PEACH, + PEACH, + 5, + -80, + -22, + 30), + (0.1, + ROCKET, + WHITE, + WHITE, + 5, + -80, + 0, + 30), + (2, + CIRCLE, + PURPLE, + PURPLE, + 6, + -40, + 0, + 100), + (0.5, + RING, + YELLOW, + YELLOW, + 8, + -40, + 0, + 100), + (1, + CIRCLE, + BLUE, + BLUE, + 6, + 0, + 0, + 100), + (0.5, + RING, + YELLOW, + YELLOW, + 8, + 0, + 0, + 100), + (0.2, + RING, + YELLOW, + YELLOW, + 10, + 0, + 0, + 100), + (1, + CIRCLE, + PURPLE, + PURPLE, + 8, + 40, + 0, + 100), + (0.5, + RING, + YELLOW, + YELLOW, + 10, + 40, + 0, + 100), + (0.2, + RING, + YELLOW, + YELLOW, + 12, + 40, + 0, + 100), + (0.2, + RING, + YELLOW, + YELLOW, + 14, + 40, + 0, + 100), + (1, + CIRCLELARGE, + PURPLE, + PURPLE, + 8, + 0, + 0, + 100), + (0.5, + RING, + YELLOW, + YELLOW, + 8, + 0, + 0, + 100), + (0.2, + RING, + YELLOW, + YELLOW, + 8, + 0, + 0, + 100), + (0.2, + RING, + YELLOW, + YELLOW, + 10, + 0, + 0, + 100), + (0.2, + RING, + YELLOW, + YELLOW, + 12, + 0, + 0, + 100), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 30, + 30, + -20), + (0.2, + ROCKET, + WHITE, + YELLOW, + 4, + 30, + -30, + -20), + (0.2, + ROCKET, + WHITE, + YELLOW, + 6, + -30, + 30, + -20), + (0.2, + ROCKET, + WHITE, + YELLOW, + 8, + -30, + -30, + -20), + (2, + CIRCLELARGE, + WHITE, + WHITE, + 16, + 20, + -2, + 120), + (0.2, + CIRCLE, + WHITE, + WHITE, + 12, + 20, + -30, + 150), + (0.2, + CIRCLE, + WHITE, + WHITE, + 12, + 20, + 26, + 150), + (1, + CIRCLE, + WHITE, + WHITE, + 6, + 0, + 0, + 100), + (0.5, + RING, + YELLOW, + YELLOW, + 6, + 0, + 0, + 100), + (0.2, + RING, + PURPLE, + PURPLE, + 8, + 0, + 0, + 100), + (0.2, + RING, + PEACH, + PEACH, + 10, + 0, + 0, + 100), + (0.2, + RING, + PINK, + PINK, + 12, + 0, + 0, + 100), + (0.5, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 0, + 100), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 0, + 100), + (0.2, + CIRCLESMALL, + WHITE, + WHITE, + 4, + 10, + 30, + 80), + (0.3, + CIRCLESMALL, + WHITE, + WHITE, + 4, + 20, + -20, + 90), + (0.5, + CIRCLESMALL, + WHITE, + WHITE, + 4, + -30, + 0, + 100), + (0.4, + CIRCLESMALL, + WHITE, + WHITE, + 4, + 15, + -10, + 95), + (0.3, + CIRCLESMALL, + WHITE, + WHITE, + 4, + 0, + 0, + 100), + (1, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 0, + -20), + (2, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 0, + 100), + (0.5, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 0, + 105), + (0.4, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 0, + -20), + (0.4, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 0, + -20), + (2, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 0, + 100), + (0.1, + RING, + RED, + WHITE, + 8, + 0, + 0, + 100), + (0.2, + RING, + BLUE, + WHITE, + 4, + 10, + 30, + 80), + (0.3, + RING, + RED, + WHITE, + 6, + 20, + -20, + 90), + (0.1, + RING, + BLUE, + WHITE, + 8, + -30, + 0, + 100), + (0.4, + RING, + RED, + WHITE, + 6, + 15, + -10, + 95), + (0.1, + RING, + BLUE, + WHITE, + 9, + 0, + 0, + 100), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 20, + 0, + 0, + 100), + (0.5, + CIRCLE, + RED, + WHITE, + 20, + 20, + 20, + 105), + (0.2, + CIRCLE, + BLUE, + WHITE, + 20, + -30, + 40, + 110), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 30, + 0, + -20), + (2, + CIRCLE, + PURPLE, + PINK, + 8, + 30, + 0, + 80), + (0.2, + CIRCLE, + YELLOW, + PINK, + 8, + 10, + 0, + 80), + (0.4, + CIRCLE, + PINK, + WHITE, + 8, + 0.5, + 0, + 80), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 8, + 30, + 20, + 80), + (0.2, + CIRCLE, + BLUE, + PURPLE, + 8, + 30, + -20, + 80), + (0.5, + CIRCLELARGE, + WHITE, + WHITE, + 10, + 30, + 0, + 100), + (0.1, + POP, + WHITE, + WHITE, + 20, + 0, + 0, + 90), + (0.2, + POP, + WHITE, + WHITE, + 20, + 0, + 0, + 90), + (0.1, + POP, + WHITE, + WHITE, + 20, + 0, + 0, + 90), + (0.2, + POP, + WHITE, + WHITE, + 20, + 0, + 0, + 90)), + ((3, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -70, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -39, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -20, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 0, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + 29, + -10), + (2, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -35, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -30, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -20, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -10, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -5, + 60), + (1.5, + CIRCLE, + PINK, + PINK, + 14, + 145, + -100, + 100), + (0.5, + CIRCLE, + YELLOW, + YELLOW, + 14, + 145, + -60, + 120), + (0.5, + CIRCLE, + PINK, + PINK, + 14, + 145, + -20, + 140), + (0.5, + CIRCLE, + YELLOW, + YELLOW, + 14, + 145, + 20, + 120), + (0.5, + CIRCLE, + PINK, + PINK, + 14, + 145, + 60, + 100), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -70, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -39, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -20, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 0, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + 29, + -10), + (2, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -5, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -10, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -20, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -30, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -35, + 60), + (2, + CIRCLELARGE, + PINK, + PINK, + 14, + 145, + -20, + 120), + (0.1, + CIRCLE, + PINK, + PINK, + 14, + 145, + -38, + 140), + (0.1, + CIRCLE, + PINK, + PINK, + 14, + 145, + -2, + 140), + (1, + CIRCLE, + PURPLE, + PINK, + 10, + 120, + -60, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 120, + -60, + 80), + (0.5, + CIRCLE, + PINK, + PURPLE, + 10, + 0.5, + -60, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 0.5, + -60, + 80), + (0.5, + CIRCLE, + PURPLE, + YELLOW, + 10, + 120, + 20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 120, + 20, + 80), + (0.5, + CIRCLE, + YELLOW, + PURPLE, + 10, + 0.5, + 20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 0.5, + 20, + 80), + (0.5, + CIRCLE, + WHITE, + WHITE, + 14, + 145, + -20, + 140), + (0.1, + POP, + WHITE, + WHITE, + 20, + 0, + 0, + 90), + (0.2, + POP, + WHITE, + WHITE, + 20, + 0, + 0, + 90), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 6, + 140, + -80, + 80), + (0.5, + CIRCLE, + YELLOW, + YELLOW, + 6, + 140, + 40, + 80), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 10, + 140, + -60, + 100), + (0.5, + CIRCLE, + YELLOW, + YELLOW, + 10, + 140, + 20, + 100), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 14, + 140, + -40, + 120), + (0.5, + CIRCLE, + YELLOW, + YELLOW, + 14, + 140, + 0, + 120), + (0.5, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -20, + 60), + (2, + CIRCLELARGE, + WHITE, + WHITE, + 20, + 140, + -20, + 140), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 29, + -10), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -70, + -10), + (2, + CIRCLELARGE, + WHITE, + WHITE, + 20, + 140, + -20, + 140), + (1.5, + RING, + PURPLE, + PURPLE, + 8, + 140, + -20, + 120), + (0.8, + RING, + PINK, + PINK, + 10, + 140, + -20, + 120), + (0.1, + RING, + PINK, + PINK, + 10, + 140, + -20, + 120), + (0.8, + RING, + YELLOW, + YELLOW, + 12, + 140, + -20, + 120), + (0.1, + RING, + YELLOW, + YELLOW, + 12, + 140, + -20, + 120), + (2, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -5, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -10, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -20, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -30, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -35, + 60), + (2, + CIRCLELARGE, + PINK, + PINK, + 14, + 145, + -20, + 120), + (0.1, + CIRCLE, + PINK, + PINK, + 14, + 145, + -38, + 140), + (0.1, + CIRCLE, + PINK, + PINK, + 14, + 145, + -2, + 140), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -70, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -39, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -20, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 0, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + 29, + -10), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 6, + 140, + -80, + 80), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -70, + -10), + (0.1, + CIRCLE, + YELLOW, + YELLOW, + 6, + 140, + 40, + 80), + (0.4, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -39, + -10), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 10, + 140, + -60, + 100), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -20, + -10), + (0.1, + CIRCLE, + YELLOW, + YELLOW, + 10, + 140, + 20, + 100), + (0.4, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 0, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + 29, + -10), + (0, + CIRCLE, + PURPLE, + PURPLE, + 14, + 140, + -40, + 120), + (0.1, + CIRCLE, + YELLOW, + YELLOW, + 14, + 140, + 0, + 120), + (0.5, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -20, + 60), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -70, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -39, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -20, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 0, + -10), + (0, + CIRCLELARGE, + WHITE, + WHITE, + 20, + 140, + -20, + 140), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + 29, + -10), + (0.4, + RING, + PURPLE, + PURPLE, + 8, + 140, + -20, + 120), + (0.1, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -70, + -10), + (0.4, + RING, + PINK, + PINK, + 10, + 140, + -20, + 120), + (0.1, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -39, + -10), + (0.1, + RING, + PINK, + PINK, + 10, + 140, + -20, + 120), + (0.4, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -20, + -10), + (0.5, + RING, + YELLOW, + YELLOW, + 12, + 140, + -20, + 120), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 0, + -10), + (0.1, + RING, + YELLOW, + YELLOW, + 12, + 140, + -20, + 120), + (0.4, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + 29, + -10), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 25, + 140, + -20, + 140), + (0.3, + CIRCLE, + WHITE, + WHITE, + 20, + 140, + -40, + 120), + (0.2, + CIRCLE, + PINK, + WHITE, + 22, + 130, + 40, + 140), + (0.4, + CIRCLE, + PURPLE, + WHITE, + 12, + 0.5, + -30, + 110), + (0.8, + CIRCLE, + WHITE, + PINK, + 20, + 140, + -60, + 100), + (0.5, + CIRCLE, + PURPLE, + YELLOW, + 20, + 0.5, + 0.5, + 120), + (0.2, + CIRCLE, + PURPLE, + PURPLE, + 16, + 140, + -20, + 90), + (0.4, + CIRCLE, + PURPLE, + PEACH, + 20, + 0.5, + -30, + 110), + (0.8, + CIRCLE, + WHITE, + PINK, + 16, + 140, + -20, + 140), + (0.5, + CIRCLE, + PINK, + YELLOW, + 22, + 0.5, + 10, + 120), + (0.2, + CIRCLE, + PEACH, + PURPLE, + 20, + 140, + -20, + 90), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 20, + 140, + -20, + 140), + (0.6, + CIRCLESPRITE, + WHITE, + MUSICNOTE, + 30, + 140, + -20, + 140)), + ((1, + ROCKET, + WHITE, + BLUE, + 3, + -117, + -39, + 0), + (0.5, + ROCKET, + WHITE, + BLUE, + 4, + -117, + -39, + 0), + (0.5, + ROCKET, + WHITE, + BLUE, + 4, + -117, + -39, + 0), + (0.5, + ROCKET, + WHITE, + BLUE, + 5, + -117, + -39, + 0), + (2, + CIRCLE, + WHITE, + WHITE, + 16, + -117, + -39, + 100), + (0.3, + CIRCLE, + WHITE, + BLUE, + 20, + -140, + -40, + 70), + (0.2, + CIRCLE, + BLUE, + WHITE, + 22, + -130, + 40, + 70), + (0.8, + CIRCLE, + WHITE, + CYAN, + 20, + -140, + -60, + 80), + (0.5, + CIRCLE, + CYAN, + BLUE, + 20, + -.5, + 0.5, + 90), + (0.2, + CIRCLE, + BLUE, + CYAN, + 16, + -140, + -20, + 80), + (0.8, + CIRCLE, + WHITE, + WHITE, + 16, + -140, + -20, + 80), + (0.5, + CIRCLE, + BLUE, + WHITE, + 22, + -.5, + 10, + 90), + (0.2, + CIRCLE, + CYAN, + BLUE, + 20, + -140, + -20, + 80), + (0, + ROCKET, + WHITE, + BLUE, + 4, + -117, + -39, + 0), + (0.5, + ROCKET, + WHITE, + BLUE, + 4, + -117, + -39, + 0), + (0.5, + ROCKET, + WHITE, + BLUE, + 4, + -117, + -39, + 0), + (0.5, + ROCKET, + WHITE, + BLUE, + 4, + -117, + -39, + 0), + (1.7, + CIRCLE, + WHITE, + WHITE, + 16, + -117, + -39, + 100), + (1, + CIRCLE, + WHITE, + WHITE, + 12, + -147, + -39, + 100), + (0.8, + CIRCLE, + WHITE, + WHITE, + 12, + -87, + -39, + 100), + (0.2, + CIRCLE, + WHITE, + WHITE, + 12, + -107, + -59, + 100), + (0.4, + CIRCLE, + WHITE, + WHITE, + 12, + -127, + -59, + 100), + (0.3, + CIRCLE, + WHITE, + WHITE, + 12, + -107, + -19, + 100), + (0.5, + CIRCLE, + WHITE, + WHITE, + 12, + -127, + -19, + 100), + (1.8, + ROCKET, + BLUE, + WHITE, + 4, + -117, + -39, + 0), + (2, + CIRCLE, + BLUE, + WHITE, + 10, + -117, + -39, + 80), + (0.8, + CIRCLE, + BLUE, + WHITE, + 12, + -117, + -39, + 100), + (0.5, + CIRCLE, + BLUE, + WHITE, + 10, + -117, + -39, + 90), + (0.1, + RING, + WHITE, + WHITE, + 14, + -117, + -39, + 90), + (0.6, + CIRCLE, + BLUE, + WHITE, + 10, + -97, + -59, + 80), + (0.1, + RING, + WHITE, + WHITE, + 14, + -97, + -59, + 80), + (0.5, + CIRCLE, + BLUE, + WHITE, + 10, + -147, + -9, + 100), + (0.1, + RING, + WHITE, + WHITE, + 14, + -147, + -9, + 100), + (0.7, + CIRCLE, + BLUE, + WHITE, + 10, + -87, + -39, + 80), + (0.1, + RING, + WHITE, + WHITE, + 14, + -87, + -39, + 80), + (2.5, + ROCKET, + BLUE, + WHITE, + 3, + -137, + -39, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 4, + -127, + -39, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 5, + -117, + -39, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 4, + -107, + -39, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 3, + -97, + -39, + 0), + (1.5, + RING, + WHITE, + BLUE, + 14, + -117, + -39, + 90), + (0.1, + RING, + WHITE, + WHITE, + 14, + -117, + -39, + 90), + (0.1, + RING, + BLUE, + WHITE, + 14, + -117, + -39, + 90), + (1.5, + ROCKET, + BLUE, + WHITE, + 3, + -117, + -19, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 4, + -117, + -29, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 5, + -117, + -39, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 4, + -117, + -49, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 3, + -117, + -59, + 0), + (1.5, + RING, + WHITE, + WHITE, + 14, + -117, + -39, + 90), + (0.1, + RING, + BLUE, + WHITE, + 14, + -117, + -39, + 90), + (0.1, + RING, + WHITE, + BLUE, + 14, + -117, + -39, + 90), + (1.5, + CIRCLE, + WHITE, + WHITE, + 20, + -100, + -40, + 80), + (0.1, + CIRCLE, + WHITE, + WHITE, + 20, + -140, + -40, + 80), + (0.1, + CIRCLE, + WHITE, + BLUE, + 10, + -110, + -40, + 100), + (0.1, + CIRCLE, + WHITE, + BLUE, + 10, + -130, + -40, + 100), + (0.1, + CIRCLE, + WHITE, + WHITE, + 8, + -120, + -40, + 120), + (1.5, + CIRCLE, + WHITE, + WHITE, + 20, + -120, + -40, + 120), + (0.2, + CIRCLE, + BLUE, + WHITE, + 22, + -130, + 0, + 140), + (0.4, + CIRCLE, + BLUE, + WHITE, + 12, + -110, + -30, + 110), + (0.8, + CIRCLE, + WHITE, + BLUE, + 20, + -120, + -60, + 100), + (0.5, + CIRCLE, + BLUE, + BLUE, + 20, + -100, + 10, + 120), + (0.2, + CIRCLE, + BLUE, + CYAN, + 16, + -140, + -20, + 90), + (0.5, + CIRCLE, + CYAN, + BLUE, + 22, + -130, + 10, + 120), + (0.2, + CIRCLE, + BLUE, + WHITE, + 20, + -110, + -20, + 90), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 20, + -120, + -40, + 140), + (1, + ROCKET, + BLUE, + WHITE, + 5, + -117, + -59, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 5, + -117, + -19, + 0), + (2, + RING, + WHITE, + BLUE, + 14, + -117, + -59, + 80), + (0.1, + RING, + WHITE, + BLUE, + 14, + -117, + -19, + 80), + (0.1, + RING, + WHITE, + WHITE, + 14, + -117, + -59, + 80), + (0.1, + RING, + BLUE, + WHITE, + 14, + -117, + -19, + 80), + (0.1, + RING, + WHITE, + WHITE, + 14, + -117, + -59, + 80), + (0.1, + RING, + BLUE, + WHITE, + 14, + -117, + -19, + 80), + (2, + ROCKET, + BLUE, + WHITE, + 4, + -105, + -20, + 0), + (0.2, + CIRCLE, + BLUE, + WHITE, + 22, + -130, + 0, + 140), + (0.8, + ROCKET, + BLUE, + WHITE, + 4, + -135, + -20, + 0), + (0.5, + CIRCLE, + WHITE, + BLUE, + 20, + -120, + -60, + 100), + (0.4, + CIRCLE, + BLUE, + BLUE, + 20, + -110, + -30, + 110), + (0.1, + ROCKET, + BLUE, + WHITE, + 4, + -105, + -60, + 0), + (0.5, + CIRCLE, + BLUE, + BLUE, + 20, + -100, + 10, + 120), + (0.5, + ROCKET, + BLUE, + WHITE, + 4, + -135, + -60, + 0), + (1, + ROCKET, + BLUE, + WHITE, + 4, + -105, + -20, + 0), + (0.8, + CIRCLE, + WHITE, + BLUE, + 16, + -140, + -20, + 140), + (0.2, + ROCKET, + BLUE, + WHITE, + 4, + -135, + -20, + 0), + (0.5, + CIRCLE, + BLUE, + BLUE, + 20, + -100, + 10, + 120), + (0.4, + CIRCLE, + BLUE, + BLUE, + 20, + -100, + 10, + 120), + (0.1, + ROCKET, + BLUE, + WHITE, + 4, + -105, + -60, + 0), + (1, + ROCKET, + BLUE, + WHITE, + 4, + -135, + -60, + 0), + (0.8, + CIRCLE, + WHITE, + BLUE, + 16, + -140, + -20, + 140), + (0.2, + ROCKET, + BLUE, + WHITE, + 4, + -105, + -20, + 0), + (0.7, + CIRCLE, + WHITE, + BLUE, + 16, + -140, + -20, + 140), + (0.3, + ROCKET, + BLUE, + WHITE, + 4, + -135, + -20, + 0), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 20, + -120, + -40, + 140), + (0, + ROCKET, + BLUE, + WHITE, + 4, + -105, + -60, + 0), + (0.5, + CIRCLE, + BLUE, + BLUE, + 20, + -100, + 10, + 120), + (1, + ROCKET, + BLUE, + WHITE, + 4, + -115, + -35, + 0), + (0.2, + ROCKET, + BLUE, + WHITE, + 4, + -120, + -35, + 0), + (0.2, + ROCKET, + BLUE, + WHITE, + 4, + -115, + -45, + 0), + (0.2, + ROCKET, + BLUE, + WHITE, + 4, + -120, + -45, + 0), + (1.2, + CIRCLELARGE, + WHITE, + WHITE, + 20, + -120, + -40, + 120), + (1, + CIRCLE, + WHITE, + WHITE, + 20, + -120, + -40, + 100), + (0.8, + CIRCLE, + WHITE, + WHITE, + 20, + -120, + -40, + 110), + (0.8, + CIRCLE, + WHITE, + WHITE, + 20, + -120, + -40, + 100), + (1, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 30, + -117, + -39, + 110)), + ((1, + ROCKET, + WHITE, + YELLOW, + 3, + -23, + 3, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -23, + 3, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -23, + 3, + 0), + (1, + ROCKET, + WHITE, + YELLOW, + 3, + -60, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + -40, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 0, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 0, + 0), + (2, + CIRCLE, + BLUE, + GREEN, + 6, + -60, + 0, + 60), + (0.2, + CIRCLE, + GREEN, + BLUE, + 7, + -40, + 0, + 60), + (0.2, + CIRCLE, + WHITE, + WHITE, + 8, + -20, + 0, + 60), + (0.2, + CIRCLE, + GREEN, + GREEN, + 7, + 0, + 0, + 60), + (0.2, + CIRCLE, + BLUE, + BLUE, + 6, + 20, + 0, + 60), + (1.5, + CIRCLE, + WHITE, + WHITE, + 10, + 0, + 0, + 80), + (0.1, + CIRCLE, + WHITE, + WHITE, + 5, + -10, + 0, + 90), + (0.1, + CIRCLE, + WHITE, + WHITE, + 5, + 10, + 0, + 90), + (1, + CIRCLE, + GREEN, + BLUE, + 10, + -20, + -20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + -20, + -20, + 80), + (0.5, + CIRCLE, + GREEN, + BLUE, + 10, + 20, + -20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 20, + -20, + 80), + (0.5, + CIRCLE, + GREEN, + BLUE, + 10, + -20, + 20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + -20, + 20, + 80), + (0.5, + CIRCLE, + GREEN, + BLUE, + 10, + 20, + 20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 20, + 20, + 80), + (2, + ROCKET, + WHITE, + YELLOW, + 3, + -60, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -40, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + 0, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 0, + 0), + (1, + CIRCLELARGE, + BLUE, + WHITE, + 16, + 0, + 0, + 100), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + -21, + 30), + (1, + CIRCLELARGE, + BLUE, + WHITE, + 16, + 0, + 0, + 100), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + -21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + -23, + 3, + 0), + (2, + CIRCLE, + BLUE, + BLUE, + 10, + -23, + 3, + 80), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + -23, + 3, + 0), + (2, + CIRCLE, + BLUE, + BLUE, + 14, + -23, + 3, + 90), + (0.1, + ROCKET, + WHITE, + YELLOW, + 5, + -23, + 3, + 0), + (2, + CIRCLE, + BLUE, + BLUE, + 18, + -23, + 3, + 100), + (1.5, + ROCKET, + WHITE, + RED, + 6, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + RED, + 6, + 120, + -21, + 30), + (0.3, + CIRCLE, + WHITE, + RED, + 10, + 0, + 20, + 60), + (0.4, + CIRCLE, + WHITE, + RED, + 5, + 10, + 10, + 80), + (0.2, + CIRCLE, + WHITE, + RED, + 10, + 0, + 10, + 60), + (0.3, + CIRCLE, + WHITE, + RED, + 5, + 10, + 0, + 90), + (0.2, + CIRCLE, + WHITE, + RED, + 8, + -10, + 0, + 90), + (0.3, + CIRCLE, + WHITE, + RED, + 10, + 0, + -20, + 80), + (0.2, + CIRCLE, + WHITE, + RED, + 5, + 10, + 0, + 90), + (2, + ROCKET, + WHITE, + YELLOW, + 6, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 6, + 120, + -21, + 30), + (0.2, + CIRCLE, + WHITE, + YELLOW, + 5, + 10, + 10, + 80), + (0.4, + CIRCLE, + WHITE, + YELLOW, + 8, + -10, + 0, + 90), + (0.2, + CIRCLE, + WHITE, + YELLOW, + 10, + 0, + 10, + 60), + (0.3, + CIRCLE, + WHITE, + YELLOW, + 5, + 10, + 0, + 90), + (0.4, + CIRCLE, + WHITE, + YELLOW, + 10, + 0, + -20, + 80), + (0.3, + CIRCLE, + WHITE, + YELLOW, + 10, + 0, + 20, + 60), + (0.2, + CIRCLE, + WHITE, + YELLOW, + 5, + 10, + 0, + 110), + (2, + ROCKET, + WHITE, + BLUE, + 6, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + BLUE, + 6, + 120, + -21, + 30), + (0.3, + CIRCLE, + WHITE, + BLUE, + 10, + 0, + 20, + 60), + (0.2, + CIRCLE, + WHITE, + BLUE, + 5, + 10, + 10, + 80), + (0.4, + CIRCLE, + WHITE, + BLUE, + 10, + 0, + 10, + 60), + (0.3, + CIRCLE, + WHITE, + BLUE, + 5, + 10, + 0, + 90), + (0.2, + CIRCLE, + WHITE, + BLUE, + 8, + -10, + 0, + 90), + (0.4, + CIRCLE, + WHITE, + BLUE, + 10, + 0, + -20, + 80), + (0.2, + CIRCLE, + WHITE, + BLUE, + 5, + 10, + 0, + 90), + (2.5, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + 8, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + 8, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + -2, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + -2, + 0), + (2, + CIRCLE, + BLUE, + GREEN, + 8, + -60, + 20, + 60), + (0.2, + CIRCLE, + BLUE, + BLUE, + 8, + 20, + -20, + 60), + (0.6, + CIRCLE, + GREEN, + BLUE, + 9, + -40, + 20, + 60), + (0.2, + CIRCLE, + GREEN, + GREEN, + 9, + 0, + -20, + 60), + (0.6, + CIRCLE, + WHITE, + WHITE, + 11, + -20, + 20, + 60), + (0.2, + CIRCLE, + WHITE, + WHITE, + 11, + -20, + -20, + 60), + (0.6, + CIRCLE, + GREEN, + GREEN, + 9, + 0, + 20, + 60), + (0.2, + CIRCLE, + GREEN, + BLUE, + 9, + -40, + -20, + 60), + (0.6, + CIRCLE, + BLUE, + BLUE, + 8, + 20, + 20, + 60), + (0.2, + CIRCLE, + BLUE, + GREEN, + 8, + -60, + -20, + 60), + (2, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + 8, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + 8, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + -2, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + -2, + 0), + (2, + RING, + BLUE, + GREEN, + 8, + -60, + 20, + 60), + (0.1, + RING, + BLUE, + BLUE, + 8, + 20, + -20, + 60), + (0.5, + RING, + GREEN, + BLUE, + 9, + -40, + 20, + 60), + (0.1, + RING, + GREEN, + GREEN, + 9, + 0, + -20, + 60), + (0.5, + RING, + WHITE, + WHITE, + 11, + -20, + 20, + 60), + (0.1, + RING, + WHITE, + WHITE, + 11, + -20, + -20, + 60), + (0.5, + RING, + GREEN, + GREEN, + 9, + 0, + 20, + 60), + (0.1, + RING, + GREEN, + BLUE, + 9, + -40, + -20, + 60), + (0.5, + RING, + BLUE, + BLUE, + 8, + 20, + 20, + 60), + (0.1, + RING, + BLUE, + GREEN, + 8, + -60, + -20, + 60), + (2, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + 8, + 0), + (1, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + 8, + 0), + (0.4, + CIRCLE, + RED, + RED, + 15, + -30, + -10, + 80), + (0.6, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + -2, + 0), + (0.2, + CIRCLE, + RED, + RED, + 12, + -10, + -30, + 85), + (0.2, + CIRCLE, + BLUE, + BLUE, + 10, + 20, + -30, + 90), + (0.6, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + -2, + 0), + (0.2, + CIRCLE, + BLUE, + BLUE, + 8, + -20, + 10, + 100), + (0.3, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + 8, + 0), + (0.5, + RING, + WHITE, + WHITE, + 11, + -20, + 20, + 60), + (0, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + 8, + 0), + (0.1, + RING, + WHITE, + WHITE, + 11, + -20, + -20, + 60), + (0.2, + CIRCLE, + BLUE, + BLUE, + 12, + -20, + 10, + 90), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + -2, + 0), + (0.2, + CIRCLE, + BLUE, + BLUE, + 14, + -23, + 3, + 80), + (0.3, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + -2, + 0), + (0.3, + CIRCLE, + RED, + RED, + 14, + -23, + 3, + 90), + (0.3, + CIRCLELARGE, + WHITE, + WHITE, + 16, + -23, + 3, + 100))], + ToontownGlobals.NEW_YEAR_FIREWORKS: [((0.5, + ROCKET, + WHITE, + WHITE, + 5.0, + 113.22, + 1.05, + 73.24), + (0.25, + ROCKET, + RED, + RED, + 5.0, + 115.54, + 13.8, + 59.11), + (0.03, + ROCKET, + RED, + RED, + 5.0, + 118.36, + -13.83, + 57.71), + (0.22, + ROCKET, + BLUE, + BLUE, + 5.0, + 110.05, + 25.37, + 57.01), + (0.03, + ROCKET, + BLUE, + BLUE, + 5.0, + 110.05, + -25.37, + 57.01), + (3.22, + CIRCLELARGE, + WHITE, + WHITE, + 10.0, + 121.75, + 1.07, + 106.69), + (1.94, + CIRCLELARGE, + RED, + RED, + 10.0, + 121.75, + 1.07, + 106.69), + (1.31, + ROCKET, + BLUE, + BLUE, + 4.0, + 121.75, + 28.63, + 38.55), + (0.06, + ROCKET, + RED, + RED, + 4.0, + 121.75, + 1.3, + 38.76), + (0.06, + ROCKET, + WHITE, + WHITE, + 4.0, + 121.81, + -25.6, + 37.54), + (0.88, + ROCKET, + BLUE, + BLUE, + 4.0, + 121.81, + -25.6, + 37.54), + (0.06, + ROCKET, + RED, + RED, + 4.0, + 121.74, + 1.3, + 37.75), + (0.06, + ROCKET, + WHITE, + WHITE, + 4.0, + 121.81, + 27.34, + 37.12), + (0.82, + ROCKET, + BLUE, + BLUE, + 4.0, + 55.0, + 40.0, + 40.0), + (0.09, + ROCKET, + RED, + RED, + 4.0, + 70.0, + 40.0, + 40.0), + (0.09, + ROCKET, + WHITE, + WHITE, + 4.0, + 85.0, + 40.0, + 40.0), + (0.72, + ROCKET, + BLUE, + BLUE, + 4.0, + 55.0, + -40.0, + 40.0), + (0.09, + ROCKET, + RED, + RED, + 4.0, + 70.0, + -40.0, + 40.0), + (0.09, + ROCKET, + WHITE, + WHITE, + 4.0, + 85.0, + -40.0, + 40.0), + (0.84, + CIRCLE, + BLUE, + BLUE, + 10.0, + 121.81, + 29.29, + 78.62), + (0.06, + RING, + WHITE, + WHITE, + 5.0, + 121.81, + 29.29, + 78.62), + (0.75, + CIRCLE, + RED, + RED, + 10.0, + 120.0, + -30.0, + 100.0), + (0.06, + RING, + WHITE, + WHITE, + 5.0, + 120.0, + -30.0, + 100.0), + (0.7, + CIRCLE, + WHITE, + WHITE, + 10.0, + 121.81, + 29.29, + 92.0), + (0.19, + RING, + BLUE, + BLUE, + 5.0, + 121.81, + 29.29, + 92.0), + (0.8, + CIRCLE, + WHITE, + WHITE, + 10.0, + 121.81, + 0.0, + 100.0), + (0.08, + RING, + RED, + RED, + 5.0, + 121.81, + 0.0, + 100.0), + (1.0, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.43, + 22.69, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.24, + 17.57, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 10.82, + 12.13, + 4.0), + (0.22, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.17, + 6.76, + 4.0), + (0.31, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.1, + 1.91, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.71, + -3.56, + 4.0), + (0.19, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.26, + -8.51, + 4.0), + (0.28, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.25, + -14.18, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.2, + -19.42, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.53, + -24.46, + 4.0), + (1.24, + CIRCLESMALL, + RED, + WHITE, + 15.0, + 68.68, + -29.58, + 57.72), + (1.63, + CIRCLE, + BLUE, + WHITE, + 15.0, + 70.33, + 29.76, + 56.3), + (0.87, + CIRCLELARGE, + WHITE, + WHITE, + 15.0, + 121.64, + 1.2, + 92.64), + (1.26, + RING, + WHITE, + WHITE, + 15.0, + 83.12, + 12.21, + 92.04), + (0.44, + RING, + RED, + RED, + 15.0, + 84.41, + -23.72, + 78.01), + (0.44, + RING, + WHITE, + WHITE, + 15.0, + 83.5, + 1.77, + 68.65), + (0.38, + RING, + BLUE, + BLUE, + 15.0, + 82.6, + 26.64, + 68.65), + (0.37, + CIRCLESMALL, + WHITE, + WHITE, + 11.0, + 74.5, + 15.0, + 71.8), + (0.12, + CIRCLELARGE, + RED, + RED, + 11.0, + 74.5, + 15.0, + 71.8), + (0.75, + CIRCLESMALL, + WHITE, + WHITE, + 11.0, + 94.03, + -15.23, + 62.12), + (0.13, + CIRCLELARGE, + BLUE, + BLUE, + 11.0, + 94.03, + -15.23, + 62.12), + (0.56, + CIRCLESMALL, + RED, + RED, + 11.0, + 54.56, + 7.87, + 53.74), + (0.19, + CIRCLELARGE, + WHITE, + WHITE, + 11.0, + 54.56, + 7.87, + 53.74), + (0.63, + CIRCLESMALL, + BLUE, + BLUE, + 11.0, + 82.51, + 0.23, + 82.99), + (0.12, + CIRCLELARGE, + WHITE, + WHITE, + 11.0, + 82.51, + 0.23, + 82.99), + (0.37, + RING, + RED, + RED, + 5.0, + 82.51, + 0.23, + 82.99), + (0.47, + RING, + WHITE, + WHITE, + 6.0, + 82.51, + 0.23, + 82.99), + (0.47, + RING, + BLUE, + BLUE, + 7.0, + 82.51, + 0.23, + 82.99), + (0.47, + RING, + WHITE, + WHITE, + 8.0, + 82.51, + 0.23, + 82.99), + (0.47, + RING, + RED, + RED, + 10.0, + 82.51, + 0.23, + 82.99), + (0.37, + ROCKET, + WHITE, + WHITE, + 8.0, + 53.85, + 14.92, + 4.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 8.0, + 54.66, + -16.71, + 4.0), + (0.44, + ROCKET, + WHITE, + WHITE, + 8.0, + 73.27, + 15.16, + 4.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 8.0, + 73.38, + -15.98, + 4.0), + (0.44, + ROCKET, + WHITE, + WHITE, + 8.0, + 93.33, + 15.17, + 4.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 8.0, + 93.71, + -15.73, + 4.0), + (0.89, + CIRCLELARGE, + WHITE, + WHITE, + 8.0, + 151.88, + 1.92, + 144.56), + (0.93, + CIRCLESMALL, + WHITE, + WHITE, + 8.0, + 82.25, + 16.72, + 82.5), + (0.12, + CIRCLELARGE, + WHITE, + WHITE, + 8.0, + 79.31, + 1.0, + 63.69), + (0.13, + CIRCLESMALL, + WHITE, + WHITE, + 8.0, + 82.02, + -13.25, + 79.16), + (0.88, + ROCKET, + WHITE, + RED, + 4.0, + 6.47, + -15.0, + 8.6), + (0.06, + ROCKET, + WHITE, + RED, + 4.0, + 18.41, + 15.0, + 4.0), + (0.31, + ROCKET, + WHITE, + BLUE, + 4.0, + 53.79, + 15.0, + 4.0), + (0.06, + ROCKET, + WHITE, + BLUE, + 4.0, + 54.85, + -15.0, + 4.0), + (0.5, + ROCKET, + RED, + RED, + 4.0, + 90.45, + -15.0, + 4.0), + (0.19, + ROCKET, + RED, + RED, + 4.0, + 90.21, + 15.0, + 4.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 4.0, + 90.21, + 0.0, + 4.0), + (0.44, + CIRCLE, + WHITE, + WHITE, + 4.0, + 18.41, + 15.0, + 90.0), + (0.06, + RING, + RED, + RED, + 1.0, + 18.41, + 15.0, + 90.0), + (0.75, + CIRCLE, + WHITE, + WHITE, + 4.0, + 93.56, + -16.02, + 72.89), + (0.06, + RING, + RED, + RED, + 1.0, + 93.56, + -16.02, + 72.89), + (0.69, + CIRCLE, + WHITE, + WHITE, + 4.0, + 53.19, + -10.03, + 98.97), + (0.06, + RING, + BLUE, + BLUE, + 1.0, + 53.19, + -10.03, + 98.97), + (0.69, + CIRCLE, + WHITE, + WHITE, + 4.0, + 71.46, + 13.03, + 56.82), + (0.06, + RING, + BLUE, + BLUE, + 1.0, + 71.46, + 13.03, + 56.82), + (0.25, + CIRCLE, + RED, + RED, + 4.0, + 89.38, + -3.18, + 95.18), + (0.06, + RING, + WHITE, + WHITE, + 1.0, + 89.38, + -3.18, + 95.18), + (0.31, + CIRCLELARGE, + WHITE, + WHITE, + 19.0, + 94.74, + 0.89, + 65.55), + (0.44, + ROCKET, + WHITE, + PEACH, + 3.0, + 96.5, + 2.5, + 4.0), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 95.93, + -2.61, + 4.0), + (0.38, + ROCKET, + WHITE, + PEACH, + 3.0, + 76.08, + 7.72, + 4.0), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 76.54, + -8.28, + 4.0), + (0.37, + ROCKET, + WHITE, + PEACH, + 3.0, + 56.41, + 12.05, + 4.0), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 56.76, + -13.51, + 4.0), + (0.31, + ROCKET, + WHITE, + PEACH, + 3.0, + 36.29, + 16.92, + 4.0), + (0.1, + ROCKET, + WHITE, + PEACH, + 3.0, + 37.03, + -19.08, + 4.0), + (0.96, + CIRCLESMALL, + WHITE, + WHITE, + 3.0, + 100.31, + 2.5, + 66.59), + (0.06, + CIRCLESMALL, + WHITE, + WHITE, + 3.0, + 100.31, + -2.5, + 66.59), + (0.44, + CIRCLESMALL, + WHITE, + RED, + 6.0, + 76.25, + 8.0, + 66.59), + (0.06, + CIRCLESMALL, + WHITE, + RED, + 6.0, + 76.25, + -8.0, + 66.59), + (0.44, + CIRCLE, + WHITE, + BLUE, + 3.0, + 34.77, + 12.0, + 66.59), + (0.06, + CIRCLE, + WHITE, + BLUE, + 3.0, + 34.77, + -12.0, + 66.59), + (0.44, + CIRCLELARGE, + WHITE, + WHITE, + 3.0, + 10.0, + 16.0, + 66.59), + (0.06, + CIRCLELARGE, + WHITE, + WHITE, + 3.0, + 10.0, + -16.0, + 66.59), + (0.94, + CIRCLESPRITE, + WHITE, + ICECREAM, + 6.0, + 10.0, + 0.0, + 66.59), + (2.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + 23.91, + 19.44), + (0.13, + ROCKET, + RED, + RED, + 5.0, + 90.0, + 23.91, + 19.44), + (0.44, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + -23.91, + 19.44), + (0.06, + ROCKET, + RED, + RED, + 5.0, + 90.0, + -23.91, + 19.44), + (0.38, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + 23.91, + 19.44), + (0.13, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0, + 23.91, + 19.44), + (0.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + -23.91, + 19.44), + (0.25, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0, + -23.91, + 19.44), + (0.63, + ROCKET, + RED, + RED, + 5.0, + 50.0, + 23.91, + 19.44), + (0.13, + ROCKET, + BLUE, + BLUE, + 5.0, + 50.0, + -23.91, + 19.44), + (0.25, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0, + 23.91, + 19.44), + (0.19, + ROCKET, + RED, + RED, + 5.0, + 90.0, + -23.91, + 19.44), + (0.94, + ROCKET, + WHITE, + WHITE, + 5.0, + 74.23, + -0.06, + 4.0), + (0.28, + ROCKET, + BLUE, + BLUE, + 5.0, + 53.66, + 0.14, + 13.48), + (0.28, + ROCKET, + RED, + RED, + 5.0, + 53.96, + -0.38, + 4.0), + (0.28, + ROCKET, + WHITE, + WHITE, + 5.0, + 63.67, + 10.54, + 4.0), + (0.34, + ROCKET, + WHITE, + WHITE, + 4.0, + 63.88, + -10.98, + 4.0), + (0.16, + ROCKET, + RED, + RED, + 5.0, + 64.24, + -4.44, + 4.0), + (0.16, + ROCKET, + RED, + RED, + 5.0, + 64.16, + 2.92, + 4.0), + (0.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + 23.91, + 19.44), + (0.12, + ROCKET, + RED, + RED, + 5.0, + 90.0, + 23.91, + 19.44), + (0.34, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + -23.91, + 19.44), + (0.03, + ROCKET, + RED, + RED, + 5.0, + 90.0, + -23.91, + 19.44), + (0.5, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + 23.91, + 19.44), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0, + 23.91, + 19.44), + (0.38, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + -23.91, + 19.44), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0, + -23.91, + 19.44), + (0.34, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79, + 19.8, + 24.32), + (0.25, + ROCKET, + RED, + RED, + 5.0, + 107.58, + 11.29, + 45.0), + (0.22, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.22, + 3.33, + 60.0), + (0.37, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.34, + -0.98, + 60.0), + (0.22, + ROCKET, + RED, + RED, + 5.0, + 108.45, + -9.33, + 45.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69, + -17.77, + 24.4), + (0.53, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79, + 19.8, + 24.32), + (0.25, + ROCKET, + RED, + RED, + 5.0, + 107.58, + 11.29, + 45.0), + (0.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.22, + 3.33, + 60.0), + (0.5, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.34, + -0.98, + 60.0), + (0.19, + ROCKET, + RED, + RED, + 5.0, + 108.45, + -9.33, + 45.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69, + -17.77, + 24.4), + (0.5, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79, + 19.8, + 24.32), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69, + -17.77, + 24.4), + (0.19, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.22, + 3.33, + 60.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.34, + -0.98, + 60.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79, + 19.8, + 24.32), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69, + -17.77, + 24.4), + (0.94, + CIRCLELARGE, + BLUE, + BLUE, + 5.0, + 43.82, + -2.09, + 49.31), + (0.25, + POP, + BLUE, + BLUE, + 5.0, + 43.82, + -2.09, + 49.31), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 5.0, + 43.82, + -2.09, + 49.31), + (0.13, + CIRCLELARGE, + RED, + RED, + 5.0, + 40.83, + 10.33, + 65.41), + (0.25, + CIRCLE, + WHITE, + WHITE, + 5.0, + 103.3, + 12.66, + 83.93), + (0.25, + RING, + RED, + RED, + 5.0, + 95.27, + -9.92, + 72.55), + (0.37, + POP, + BLUE, + BLUE, + 5.0, + 43.82, + -2.09, + 49.31), + (0.13, + CIRCLELARGE, + BLUE, + RED, + 5.0, + 107.72, + -11.81, + 50.51), + (0.5, + CIRCLESMALL, + BLUE, + WHITE, + 5.0, + 102.29, + -8.59, + 80.37), + (0.31, + RING, + BLUE, + BLUE, + 5.0, + 49.85, + 12.65, + 59.45), + (0.44, + RING, + WHITE, + WHITE, + 5.0, + 22.91, + -0.7, + 68.75), + (0.13, + CIRCLELARGE, + WHITE, + RED, + 5.0, + 91.87, + 0.99, + 72.32), + (0.25, + POP, + BLUE, + BLUE, + 5.0, + 43.82, + -2.09, + 49.31), + (0.25, + CIRCLE, + RED, + RED, + 5.0, + 2.49, + 6.37, + 43.93), + (0.37, + RING, + BLUE, + BLUE, + 5.0, + 84.93, + 8.5, + 77.25), + (0.44, + RING, + WHITE, + WHITE, + 5.0, + 85.37, + -5.29, + 75.3), + (0.44, + CIRCLELARGE, + RED, + RED, + 5.0, + 86.11, + -11.82, + 71.43), + (0.31, + CIRCLELARGE, + WHITE, + WHITE, + 5.0, + 79.35, + 8.71, + 77.0), + (0.19, + CIRCLELARGE, + BLUE, + BLUE, + 5.0, + 66.08, + 0.98, + 68.8)), + ((3, + ROCKET, + RED, + WHITE, + 1, + 1.44, + 92.5, + -5), + (1, + ROCKET, + WHITE, + WHITE, + 2, + 1.44, + 92.5, + -5), + (1, + ROCKET, + BLUE, + WHITE, + 3, + 1.44, + 92.5, + -5), + (2.5, + CIRCLE, + YELLOW, + WHITE, + 17, + 1.44, + 92.5, + 70), + (3.5, + ROCKET, + BLUE, + WHITE, + 2, + 1.44, + 92.5, + -5), + (1, + ROCKET, + WHITE, + WHITE, + 3, + 1.44, + 92.5, + -5), + (1, + ROCKET, + RED, + WHITE, + 4, + 1.44, + 92.5, + -5), + (2.25, + CIRCLE, + YELLOW, + WHITE, + 20, + 1.44, + 92.5, + 75), + (2.75, + ROCKET, + WHITE, + WHITE, + 5, + 1.44, + 92.5, + -5), + (2.97, + RING, + RED, + RED, + 5, + 1.44, + 92.5, + 80), + (0.03, + RING, + RED, + RED, + 5, + 1.44, + 92.5, + 80), + (0.03, + RING, + RED, + RED, + 5, + 1.44, + 92.5, + 80), + (0.44, + RING, + BLUE, + BLUE, + 7.5, + 1.44, + 92.5, + 85), + (0.03, + RING, + BLUE, + BLUE, + 7.5, + 1.44, + 92.5, + 85), + (0.05, + RING, + BLUE, + BLUE, + 7.5, + 1.44, + 92.5, + 85), + (0.39, + RING, + WHITE, + WHITE, + 5, + 1.44, + 92.5, + 90), + (0.03, + RING, + WHITE, + WHITE, + 5, + 1.44, + 92.5, + 90), + (0.03, + RING, + WHITE, + WHITE, + 5, + 1.44, + 92.5, + 90), + (4, + POP, + BLUE, + BLUE, + 15, + 1.44, + 92.5, + 90), + (1.5, + CIRCLESPRITE, + WHITE, + FLOWER, + 5, + 1.44, + 92.5, + 30), + (0.5, + CIRCLESPRITE, + RED, + FLOWER, + 7.5, + 1.44, + 92.5, + 0.5), + (0.63, + CIRCLESPRITE, + BLUE, + FLOWER, + 10, + 1.44, + 92.5, + 70), + (3.38, + POP, + BLUE, + BLUE, + 15, + 1.44, + 92.5, + 90), + (1, + CIRCLESPRITE, + YELLOW, + FLOWER, + 20, + 1.44, + 92.5, + 40), + (0.5, + CIRCLESPRITE, + GREEN, + FLOWER, + 25, + 1.44, + 92.5, + 60), + (0.5, + CIRCLESPRITE, + PINK, + FLOWER, + 30, + 1.44, + 92.5, + 80), + (3.5, + CIRCLELARGE, + BLUE, + WHITE, + 20, + -4.44, + 88.5, + 80), + (0.5, + CIRCLELARGE, + WHITE, + WHITE, + 20, + 6.44, + 92.5, + 80), + (0.5, + CIRCLELARGE, + RED, + WHITE, + 20, + 6.44, + 88.5, + 80), + (1.5, + CIRCLESMALL, + WHITE, + WHITE, + 30, + 1.44, + 92.5, + 80), + (0.5, + CIRCLE, + RED, + RED, + 40, + 1.44, + 92.5, + 90), + (0.5, + CIRCLELARGE, + BLUE, + BLUE, + 0.5, + 1.44, + 92.5, + 100), + (1, + POP, + BLUE, + BLUE, + 15, + 1.44, + 92.5, + 90), + (0.22, + POP, + BLUE, + WHITE, + 25, + 1.44, + 92.5, + 70), + (0.91, + POP, + BLUE, + WHITE, + 25, + 1.44, + 92.5, + 70), + (0.12, + POP, + BLUE, + BLUE, + 15, + 1.44, + 92.5, + 90), + (0.13, + POP, + BLUE, + WHITE, + 25, + 1.44, + 92.5, + 70), + (1.62, + CIRCLESMALL, + BLUE, + BLUE, + 20, + -4.44, + 88.5, + 90), + (0.5, + CIRCLESMALL, + RED, + RED, + 20, + 6.44, + 92.5, + 90), + (0.5, + CIRCLESMALL, + WHITE, + WHITE, + 20, + 6.44, + 88.5, + 90), + (2, + CIRCLE, + WHITE, + WHITE, + 40, + -4.44, + 88.5, + 100), + (0.5, + CIRCLE, + BLUE, + BLUE, + 40, + 6.44, + 92.5, + 100), + (0.38, + CIRCLE, + RED, + RED, + 40, + 6.44, + 88.5, + 100), + (3.88, + POP, + WHITE, + WHITE, + 20, + 1.44, + 92.5, + 40), + (0.25, + POP, + WHITE, + WHITE, + 30, + 1.44, + 92.5, + 40), + (0.25, + POP, + WHITE, + WHITE, + 40, + 1.44, + 92.5, + 40), + (1.69, + ROCKET, + BLUE, + BLUE, + 2, + 6.44, + 92.5, + -1), + (0.07, + ROCKET, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.05, + ROCKET, + WHITE, + WHITE, + 2, + -4.44, + 88.5, + -1), + (0.87, + ROCKET, + RED, + RED, + 4, + 6.44, + 88.5, + -1), + (0.07, + ROCKET, + WHITE, + WHITE, + 4, + -4.44, + 88.5, + -1), + (0.05, + ROCKET, + BLUE, + BLUE, + 4, + 6.44, + 92.5, + -1), + (0.87, + ROCKET, + BLUE, + BLUE, + 8, + 6.44, + 92.5, + -1), + (0.07, + ROCKET, + WHITE, + WHITE, + 8, + -4.44, + 88.5, + -1), + (0.05, + ROCKET, + RED, + RED, + 8, + 6.44, + 88.5, + -1), + (0.87, + POP, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.07, + POP, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.05, + POP, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.87, + POP, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.07, + POP, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.05, + POP, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.87, + CIRCLELARGE, + BLUE, + BLUE, + 2, + 6.44, + 88.5, + 80), + (0.06, + CIRCLELARGE, + RED, + RED, + 2, + -4.44, + 88.5, + 80), + (0.06, + CIRCLELARGE, + WHITE, + WHITE, + 2, + 6.44, + 92.5, + 80), + (0.44, + POP, + RED, + WHITE, + 30, + 6.44, + 92.5, + 100), + (0.06, + POP, + RED, + WHITE, + 30, + 6.44, + 92.5, + 100), + (0.06, + POP, + RED, + WHITE, + 30, + 6.44, + 92.5, + 100), + (0.49, + CIRCLE, + BLUE, + WHITE, + 40, + 6.44, + 88.5, + 100), + (0.06, + CIRCLE, + WHITE, + WHITE, + 40, + -4.44, + 88.5, + 100), + (0.06, + CIRCLE, + RED, + WHITE, + 40, + 6.01, + 91.87, + 96.32), + (0.55, + POP, + RED, + WHITE, + 30, + 6.44, + 92.5, + 100), + (0.04, + POP, + RED, + WHITE, + 30, + 6.44, + 92.5, + 100), + (0.05, + POP, + RED, + WHITE, + 30, + 6.44, + 92.5, + 100), + (0.41, + CIRCLELARGE, + BLUE, + WHITE, + 30, + -4.44, + 88.5, + 120), + (0.06, + CIRCLELARGE, + RED, + WHITE, + 30, + 6.44, + 92.5, + 120), + (0.06, + CIRCLELARGE, + WHITE, + WHITE, + 30, + 6.44, + 88.5, + 120)), + ((1.0, + ROCKET, + PURPLE, + PURPLE, + 4.0, + -120.0, + -71.66, + 52.06), + (0.06, + ROCKET, + PURPLE, + PURPLE, + 4.0, + -120.0, + 71.17, + 52.45), + (0.81, + RING, + RED, + RED, + 4.0, + -76.73, + -12.67, + 38.84), + (0.75, + RING, + BLUE, + BLUE, + 4.0, + -76.18, + 13.97, + 53.23), + (0.5, + RING, + PURPLE, + PURPLE, + 4.0, + -66.77, + -1.55, + 46.73), + (0.13, + RING, + PINK, + PINK, + 4.0, + -66.77, + -1.55, + 46.73), + (0.12, + RING, + BLUE, + BLUE, + 4.0, + -66.77, + -1.55, + 46.73), + (1.13, + ROCKET, + RED, + RED, + 4.0, + -62.87, + -56.53, + -5.17), + (0.06, + ROCKET, + RED, + RED, + 4.0, + -62.67, + 56.28, + -5.13), + (0.06, + ROCKET, + RED, + RED, + 4.0, + -116.1, + -53.82, + -1.46), + (0.06, + ROCKET, + RED, + RED, + 4.0, + -116.34, + 52.84, + -1.33), + (1.44, + CIRCLELARGE, + PURPLE, + PURPLE, + 12.0, + -92.38, + -0.33, + 55.76), + (0.62, + RING, + PINK, + PINK, + 12.0, + -92.38, + -0.33, + 55.76), + (0.13, + RING, + RED, + RED, + 12.0, + -92.38, + -0.33, + 55.76), + (0.13, + RING, + BLUE, + BLUE, + 12.0, + -92.38, + -0.33, + 55.76), + (0.87, + ROCKET, + RED, + RED, + 12.0, + -63.5, + -56.18, + -4.62), + (0.03, + ROCKET, + RED, + RED, + 12.0, + -63.5, + 55.42, + -3.42), + (0.09, + ROCKET, + PURPLE, + PURPLE, + 12.0, + -91.51, + -56.5, + -3.09), + (0.03, + ROCKET, + PURPLE, + PURPLE, + 12.0, + -91.51, + 54.87, + -1.89), + (0.09, + ROCKET, + BLUE, + BLUE, + 12.0, + -118.26, + -55.07, + -1.63), + (0.03, + ROCKET, + BLUE, + BLUE, + 12.0, + -118.26, + 54.95, + -0.44), + (0.59, + ROCKET, + RED, + RED, + 12.0, + -117.97, + -56.18, + -4.62), + (0.03, + ROCKET, + RED, + RED, + 12.0, + -115.83, + 56.32, + -3.41), + (0.09, + ROCKET, + PURPLE, + PURPLE, + 12.0, + -91.51, + -55.17, + -3.08), + (0.03, + ROCKET, + PURPLE, + PURPLE, + 12.0, + -91.51, + 55.55, + -1.88), + (0.09, + ROCKET, + BLUE, + BLUE, + 12.0, + -64.8, + -55.07, + -1.63), + (0.03, + ROCKET, + BLUE, + BLUE, + 12.0, + -64.8, + 54.24, + -0.45), + (0.84, + CIRCLELARGE, + PURPLE, + PURPLE, + 12.0, + -92.38, + -0.33, + 55.76), + (0.13, + RING, + RED, + RED, + 5.0, + -92.38, + -0.33, + 55.76), + (1.5, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -50.54, + 75.3), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.34, + 75.34), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -50.38, + 70.28), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.49, + 70.17), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -50.85, + 65.71), + (0.25, + CIRCLESMALL, + RED, + RED, + 2.0, + -134.53, + -45.37, + 65.13), + (0.25, + CIRCLESMALL, + RED, + RED, + 2.0, + -134.53, + -40.51, + 65.14), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.65, + 65.14), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -50.38, + 60.39), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.49, + 60.42), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -50.07, + 55.81), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.34, + 54.95), + (0.5, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.11, + 75.23), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -22.78, + 70.35), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -17.6, + 70.36), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -24.2, + 65.02), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -16.5, + 65.04), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -26.71, + 60.43), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 2.0, + -134.53, + -22.94, + 60.14), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 2.0, + -134.53, + -18.54, + 60.15), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -13.04, + 60.6), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -30.32, + 55.1), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.37, + 55.13), + (0.5, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + -5.65, + 74.82), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + -0.46, + 75.43), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 4.26, + 75.74), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + -5.18, + 70.39), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 7.41, + 70.12), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + -5.49, + 65.35), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + -0.46, + 65.36), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 4.42, + 65.23), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.5, + -5.42, + 58.9), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.5, + -5.13, + 53.96), + (0.5, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 14.5, + 74.87), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 19.71, + 75.33), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 24.28, + 75.34), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 14.66, + 70.58), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 27.12, + 70.46), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 14.66, + 65.25), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 19.39, + 65.25), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 24.28, + 64.82), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 14.66, + 60.21), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 14.66, + 55.47), + (0.5, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 29.33, + 75.21), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 44.65, + 74.8), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 33.28, + 70.03), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 40.54, + 70.19), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 37.54, + 65.14), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 37.54, + 59.95), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 37.22, + 55.35), + (0.5, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.27, + 74.94), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -15.34, + 74.83), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.27, + 70.35), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -25.71, + 70.07), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -15.49, + 70.09), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.27, + 65.03), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -23.2, + 65.04), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -15.65, + 65.2), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.43, + 60.44), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -17.85, + 60.02), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -15.49, + 60.03), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.27, + 55.27), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -15.65, + 55.58), + (0.53, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.3, + 75.13), + (0.22, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.58, + 75.0), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -0.38, + 75.01), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 4.66, + 74.88), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.3, + 70.4), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.14, + 65.22), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.89, + 65.22), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -0.38, + 65.09), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.5, + -10.26, + 59.42), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.5, + -10.26, + 54.56), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.89, + 55.01), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -0.54, + 55.02), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 4.35, + 55.17), + (0.5, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 9.71, + 75.04), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 29.59, + 75.38), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 11.44, + 70.15), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 28.01, + 70.19), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 12.23, + 65.26), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 2.0, + -134.53, + 19.96, + 65.13), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 27.07, + 65.29), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 12.86, + 60.22), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 2.0, + -134.53, + 16.78, + 60.16), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 2.0, + -134.53, + 21.54, + 60.39), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 26.12, + 60.25), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 14.75, + 55.63), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 24.38, + 55.35), + (0.53, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -40.16, + 75.06), + (0.22, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -25.4, + 75.39), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.5, + -35.69, + 68.32), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -29.33, + 70.21), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -32.63, + 65.17), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -32.78, + 60.44), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -32.78, + 55.41), + (0.5, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.37, + 74.96), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -15.49, + 74.83), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.3, + 74.99), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.74, + 74.85), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.53, + 70.08), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.21, + 65.2), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -15.65, + 64.91), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.14, + 65.07), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.05, + 60.02), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.21, + 55.43), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -15.02, + 55.14), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.62, + 55.15), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.74, + 55.3), + (0.5, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.5, + 8.72, + 73.77), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 6.24, + 70.14), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 13.02, + 70.3), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 4.35, + 65.39), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 14.28, + 65.12), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.5, + 1.92, + 60.18), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 2.0, + -134.53, + 7.9, + 60.22), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 2.0, + -134.53, + 11.91, + 60.22), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 16.17, + 60.23), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.5, + -0.85, + 54.23), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 19.49, + 55.34), + (0.5, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 24.7, + 75.22), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 29.28, + 75.23), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 34.97, + 74.65), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 24.86, + 69.88), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 37.34, + 70.06), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 24.7, + 65.29), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 29.75, + 64.85), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 34.49, + 65.16), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 24.54, + 59.8), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 37.66, + 60.12), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 24.54, + 55.2), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 40.82, + 54.63), + (0.5, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -40.47, + 75.06), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.45, + 75.07), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -45.49, + 70.47), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.58, + 70.5), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -34.67, + 65.17), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -40.47, + 62.05), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -45.65, + 59.53), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -45.65, + 55.39), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -40.47, + 55.55), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.92, + 55.26), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.58, + 55.27), + (0.5, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -15.49, + 75.12), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.62, + 74.99), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.68, + 70.37), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -4.95, + 70.26), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.37, + 65.2), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.74, + 65.08), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.21, + 60.02), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.42, + 60.19), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -15.49, + 55.14), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.46, + 55.15), + (0.5, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 9.39, + 75.18), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 14.6, + 75.2), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 4.66, + 69.99), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 18.7, + 70.31), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 4.35, + 65.24), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 19.64, + 65.28), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 5.45, + 60.21), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 18.54, + 60.08), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 9.39, + 55.03), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 14.6, + 55.04), + (0.53, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 29.75, + 75.23), + (0.22, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 42.56, + 74.97), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 29.44, + 70.04), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 42.4, + 70.22), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 29.59, + 65.15), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 33.07, + 65.15), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 38.29, + 65.02), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 42.08, + 65.17), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 42.24, + 60.57), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 42.08, + 55.23), + (0.28, + ROCKET, + RED, + RED, + 1.0, + -134.53, + 51.93, + 55.34), + (0.75, + CIRCLELARGE, + RED, + RED, + 5.0, + -90.69, + -19.74, + 38.96), + (0.22, + CIRCLESMALL, + PURPLE, + PURPLE, + 5.0, + -89.41, + 12.93, + 57.47), + (0.25, + RING, + BLUE, + BLUE, + 5.0, + -89.41, + -14.28, + 51.82), + (0.25, + CIRCLESMALL, + PINK, + PINK, + 5.0, + -89.41, + 17.67, + 27.95), + (0.25, + CIRCLESMALL, + WHITE, + WHITE, + 5.0, + -89.41, + -15.05, + 31.01), + (0.25, + CIRCLELARGE, + BLUE, + RED, + 5.0, + -89.41, + -16.86, + 59.2), + (0.25, + RING, + RED, + PURPLE, + 5.0, + -89.41, + 9.76, + 46.8), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 5.0, + -89.41, + 12.64, + 30.14), + (0.25, + CIRCLELARGE, + BLUE, + BLUE, + 5.0, + -89.41, + -16.2, + 36.45), + (0.38, + CIRCLESMALL, + PURPLE, + PURPLE, + 5.0, + -89.41, + -3.85, + 61.98), + (0.37, + RING, + RED, + RED, + 5.0, + -89.41, + 12.96, + 34.61), + (0.37, + CIRCLELARGE, + PURPLE, + PURPLE, + 5.0, + -89.41, + 13.46, + 66.24), + (0.25, + CIRCLESMALL, + BLUE, + RED, + 5.0, + -89.41, + -5.28, + 37.68), + (0.25, + RING, + RED, + RED, + 5.0, + -89.41, + -0.94, + 44.71), + (0.06, + RING, + PURPLE, + PURPLE, + 5.0, + -89.41, + -0.94, + 44.71), + (0.06, + RING, + BLUE, + BLUE, + 5.0, + -89.41, + -0.94, + 44.71)), + ((0.56, + CIRCLELARGE, + WHITE, + PEACH, + 10.0, + 137.92, + -20.14, + 107.4), + (0.94, + CIRCLE, + PEACH, + PEACH, + 10.0, + 137.92, + 63.27, + 50.69), + (0.06, + CIRCLE, + PEACH, + PEACH, + 10.0, + 137.92, + -101.42, + 50.69), + (0.69, + CIRCLE, + YELLOW, + YELLOW, + 10.0, + 137.92, + 30.99, + 73.3), + (0.06, + CIRCLE, + YELLOW, + YELLOW, + 10.0, + 137.92, + -72.63, + 73.3), + (0.69, + ROCKET, + PINK, + PINK, + 3.0, + 137.92, + 5.88, + 75.31), + (0.12, + ROCKET, + PINK, + PINK, + 3.0, + 137.92, + -45.83, + 75.31), + (0.13, + ROCKET, + PURPLE, + PURPLE, + 5.0, + 137.92, + -19.45, + 75.31), + (1.75, + CIRCLELARGE, + WHITE, + PURPLE, + 15.0, + 137.92, + -19.45, + 170.91), + (1.0, + CIRCLE, + PINK, + PINK, + 15.0, + 137.92, + 27.97, + 118.05), + (0.12, + RING, + WHITE, + WHITE, + 15.0, + 137.92, + 27.97, + 118.05), + (0.62, + CIRCLE, + PURPLE, + PURPLE, + 15.0, + 137.92, + -72.79, + 118.05), + (0.13, + RING, + WHITE, + WHITE, + 15.0, + 137.92, + -72.79, + 118.05), + (0.75, + ROCKET, + YELLOW, + YELLOW, + 5.0, + 132.97, + 36.51, + -10.35), + (0.09, + ROCKET, + YELLOW, + YELLOW, + 5.0, + 132.97, + 20.55, + -3.8), + (0.09, + ROCKET, + YELLOW, + YELLOW, + 5.0, + 132.97, + 5.97, + 1.14), + (0.09, + ROCKET, + YELLOW, + YELLOW, + 5.0, + 132.97, + -9.7, + 5.16), + (0.59, + ROCKET, + PEACH, + PEACH, + 5.0, + 132.97, + -77.37, + -10.5), + (0.13, + ROCKET, + PEACH, + PEACH, + 5.0, + 132.97, + -60.43, + -3.31), + (0.13, + ROCKET, + PEACH, + PEACH, + 5.0, + 132.97, + -47.12, + 1.14), + (0.12, + ROCKET, + PEACH, + PEACH, + 5.0, + 132.97, + -30.65, + 5.16), + (0.62, + ROCKET, + PINK, + PINK, + 5.0, + 132.97, + 36.51, + -10.35), + (0.13, + ROCKET, + PINK, + PINK, + 5.0, + 132.97, + 20.55, + -3.8), + (0.13, + ROCKET, + PINK, + PINK, + 5.0, + 132.97, + 5.97, + 1.14), + (0.12, + ROCKET, + PINK, + PINK, + 5.0, + 132.97, + -9.7, + 5.16), + (0.5, + ROCKET, + PURPLE, + PURPLE, + 5.0, + 132.97, + -77.37, + -10.5), + (0.13, + ROCKET, + PURPLE, + PURPLE, + 5.0, + 132.97, + -60.43, + -3.31), + (0.12, + ROCKET, + PURPLE, + PURPLE, + 5.0, + 132.97, + -47.12, + 1.14), + (0.13, + ROCKET, + PURPLE, + PURPLE, + 5.0, + 132.97, + -30.65, + 5.16), + (0.75, + CIRCLELARGE, + YELLOW, + YELLOW, + 15.0, + 110.03, + 29.4, + 21.16), + (0.75, + CIRCLELARGE, + PEACH, + PEACH, + 15.0, + 110.03, + -68.21, + 21.16), + (0.75, + CIRCLELARGE, + PINK, + PINK, + 15.0, + 110.03, + 0.88, + 47.21), + (0.75, + CIRCLELARGE, + PURPLE, + PURPLE, + 15.0, + 110.03, + -58.67, + 68.78), + (0.75, + CIRCLESMALL, + WHITE, + YELLOW, + 15.0, + 110.03, + -50.66, + 48.46), + (0.31, + CIRCLESMALL, + WHITE, + PINK, + 15.0, + 110.03, + 13.95, + 48.97), + (0.25, + CIRCLELARGE, + PURPLE, + PURPLE, + 15.0, + 110.03, + -17.82, + 72.34), + (0.06, + RING, + WHITE, + WHITE, + 15.0, + 110.03, + -17.82, + 72.34), + (0.31, + CIRCLELARGE, + PEACH, + PEACH, + 15.0, + 110.03, + -17.82, + 72.34), + (0.06, + RING, + WHITE, + WHITE, + 15.0, + 110.03, + -17.82, + 72.34), + (0.38, + POP, + WHITE, + WHITE, + 15.0, + 110.03, + -17.82, + 72.34), + (2.62, + ROCKET, + PEACH, + WHITE, + 3.0, + 112.26, + 15.89, + -4.52), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 112.26, + -54.6, + -4.52), + (0.28, + ROCKET, + PEACH, + WHITE, + 3.0, + 101.59, + 20.65, + -4.71), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 103.6, + -57.91, + -4.52), + (0.31, + ROCKET, + PEACH, + WHITE, + 3.0, + 99.07, + 31.56, + -4.52), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 98.42, + -68.05, + -4.52), + (0.31, + ROCKET, + PEACH, + WHITE, + 3.0, + 102.69, + -78.04, + -4.52), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 103.51, + 42.25, + -4.52), + (0.34, + ROCKET, + PEACH, + WHITE, + 3.0, + 114.48, + 45.43, + -4.52), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 112.58, + -82.93, + -4.52), + (0.28, + ROCKET, + PEACH, + WHITE, + 3.0, + 123.92, + 40.62, + -4.52), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 122.92, + -79.72, + -4.52), + (0.28, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + 30.05, + -4.52), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.24, + -69.29, + -4.52), + (0.34, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + 14.58, + -0.18), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + -55.6, + -0.18), + (0.28, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + 1.12, + 4.79), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + -41.73, + 4.79), + (0.28, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + -10.37, + 7.53), + (0.06, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + -29.31, + 7.53), + (0.19, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + -20.23, + 7.53), + (2.12, + RING, + PINK, + PINK, + 5.0, + 128.0, + -19.26, + 65.26), + (0.13, + CIRCLELARGE, + PURPLE, + PURPLE, + 5.0, + 128.0, + -19.26, + 65.26), + (2.0, + ROCKET, + WHITE, + WHITE, + 3.0, + 128.74, + -76.99, + -10.35), + (0.25, + ROCKET, + YELLOW, + YELLOW, + 3.0, + 128.74, + -48.58, + -0.3), + (0.25, + ROCKET, + PEACH, + PEACH, + 3.0, + 128.74, + -19.91, + 5.62), + (0.25, + ROCKET, + PINK, + PINK, + 3.0, + 128.74, + 7.82, + 0.08), + (0.25, + ROCKET, + PURPLE, + PURPLE, + 3.0, + 128.74, + 36.06, + -9.94), + (0.25, + ROCKET, + PINK, + PINK, + 3.0, + 128.74, + 7.97, + 0.13), + (0.25, + ROCKET, + PEACH, + PEACH, + 3.0, + 128.74, + -20.22, + 5.66), + (0.25, + ROCKET, + YELLOW, + YELLOW, + 3.0, + 128.74, + -49.59, + -0.26), + (0.25, + ROCKET, + WHITE, + WHITE, + 3.0, + 128.74, + -77.29, + -10.44), + (0.75, + CIRCLE, + WHITE, + PINK, + 8.0, + 128.74, + 49.33, + 27.06), + (0.25, + CIRCLE, + WHITE, + YELLOW, + 10.0, + 128.74, + 24.3, + 59.18), + (0.25, + CIRCLE, + WHITE, + PEACH, + 12.0, + 128.74, + -20.59, + 86.54), + (0.25, + CIRCLE, + WHITE, + PURPLE, + 10.0, + 128.74, + -63.3, + 62.82), + (0.25, + CIRCLE, + WHITE, + RED, + 8.0, + 128.74, + -85.9, + 28.25), + (1.19, + RING, + PINK, + PINK, + 4.0, + 78.66, + -50.27, + 65.42), + (0.5, + CIRCLESMALL, + PURPLE, + PURPLE, + 12.0, + 119.63, + -34.17, + 43.49), + (0.25, + CIRCLESMALL, + YELLOW, + YELLOW, + 12.0, + 78.8, + 0.32, + 44.5), + (0.31, + CIRCLELARGE, + WHITE, + WHITE, + 12.0, + 81.0, + -13.05, + 72.06), + (0.06, + RING, + PEACH, + PEACH, + 12.0, + 81.0, + -13.05, + 72.06), + (2.44, + ROCKET, + WHITE, + WHITE, + 4.0, + 139.06, + 4.93, + 66.62), + (0.06, + ROCKET, + WHITE, + WHITE, + 4.0, + 139.06, + -45.46, + 66.08), + (0.44, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 139.06, + -78.11, + 35.09), + (0.06, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 139.06, + -97.43, + 22.07), + (0.44, + ROCKET, + PEACH, + PEACH, + 4.0, + 139.06, + 57.01, + 22.06), + (0.06, + ROCKET, + PEACH, + PEACH, + 4.0, + 139.06, + 37.16, + 34.56), + (0.44, + ROCKET, + PINK, + PINK, + 4.0, + 139.06, + -83.11, + 43.43), + (0.06, + ROCKET, + PINK, + PINK, + 4.0, + 139.06, + -46.63, + 55.47), + (0.44, + ROCKET, + PURPLE, + PURPLE, + 4.0, + 139.06, + 41.59, + 44.45), + (0.06, + ROCKET, + PURPLE, + PURPLE, + 4.0, + 139.06, + 5.84, + 55.9), + (1.44, + RING, + PINK, + PINK, + 2.0, + 139.06, + 58.76, + 48.85), + (0.06, + RING, + PINK, + PINK, + 2.0, + 139.06, + -100.96, + 47.13), + (0.38, + RING, + PURPLE, + PURPLE, + 2.0, + 139.06, + 30.48, + 72.08), + (0.06, + RING, + PURPLE, + PURPLE, + 2.0, + 139.06, + -68.17, + 72.65), + (0.44, + CIRCLELARGE, + RED, + WHITE, + 2.0, + 136.43, + -9.32, + 91.88), + (0.37, + CIRCLELARGE, + WHITE, + WHITE, + 2.0, + 139.06, + -24.09, + 110.54), + (0.06, + RING, + PURPLE, + PURPLE, + 2.0, + 139.06, + -24.09, + 110.54), + (0.5, + ROCKET, + WHITE, + PINK, + 3.0, + 140.86, + -3.39, + 66.1), + (0.06, + ROCKET, + WHITE, + PINK, + 3.0, + 140.86, + -34.91, + 65.76), + (0.31, + ROCKET, + WHITE, + PEACH, + 3.0, + 140.86, + 8.9, + 54.37), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 140.86, + -49.88, + 53.74), + (0.31, + ROCKET, + WHITE, + PURPLE, + 3.0, + 140.86, + 31.63, + 47.78), + (0.06, + ROCKET, + WHITE, + PURPLE, + 3.0, + 140.86, + -74.65, + 46.63), + (0.41, + ROCKET, + WHITE, + RED, + 3.0, + 140.86, + 57.27, + 21.24), + (0.09, + ROCKET, + WHITE, + RED, + 3.0, + 140.86, + -98.14, + 19.57), + (0.91, + ROCKET, + PINK, + PINK, + 3.0, + 140.86, + 41.17, + 44.63), + (0.03, + ROCKET, + PINK, + PINK, + 3.0, + 140.86, + -80.26, + 43.33), + (0.22, + ROCKET, + PURPLE, + PURPLE, + 3.0, + 140.86, + 12.12, + 62.48), + (0.03, + ROCKET, + PURPLE, + PURPLE, + 3.0, + 140.86, + -54.27, + 61.76), + (0.16, + ROCKET, + RED, + RED, + 3.0, + 140.86, + -13.79, + 75.93), + (0.03, + ROCKET, + RED, + RED, + 3.0, + 140.86, + -23.17, + 75.83), + (0.56, + RING, + YELLOW, + YELLOW, + 3.0, + 120.63, + -19.19, + 77.13), + (0.5, + RING, + PEACH, + PEACH, + 3.0, + 120.63, + -19.19, + 77.13), + (0.38, + CIRCLESMALL, + PURPLE, + PURPLE, + 8.0, + 85.06, + -0.67, + 38.82), + (0.12, + CIRCLESMALL, + PINK, + PINK, + 9.0, + 85.06, + -23.72, + 49.29), + (0.13, + CIRCLESMALL, + PEACH, + PEACH, + 10.0, + 85.06, + 8.51, + 62.3), + (0.25, + CIRCLELARGE, + WHITE, + PINK, + 15.0, + 86.04, + -16.85, + 58.76), + (0.06, + RING, + PURPLE, + WHITE, + 5.0, + 86.04, + -16.85, + 58.76), + (0.69, + CIRCLELARGE, + WHITE, + WHITE, + 5.0, + 82.52, + -32.63, + 42.45), + (0.25, + CIRCLELARGE, + PURPLE, + PURPLE, + 10.0, + 98.02, + -4.11, + 58.76), + (0.5, + CIRCLELARGE, + PINK, + PINK, + 8.0, + 107.66, + -45.36, + 67.65), + (0.5, + CIRCLELARGE, + PEACH, + PEACH, + 12.0, + 101.02, + -11.32, + 35.33), + (0.38, + CIRCLELARGE, + YELLOW, + YELLOW, + 14.0, + 84.21, + -9.1, + 68.81), + (0.31, + CIRCLELARGE, + RED, + RED, + 8.0, + 86.04, + -37.69, + 42.21), + (0.5, + RING, + PURPLE, + PINK, + 4.0, + 97.11, + -17.22, + 58.76), + (0.02, + RING, + PINK, + PEACH, + 4.0, + 97.11, + -17.22, + 58.76), + (0.02, + RING, + PEACH, + YELLOW, + 4.0, + 97.11, + -17.22, + 58.76), + (1.03, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 139.7, + 53.94, + 24.67), + (0.06, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 139.7, + -94.94, + 23.07), + (0.19, + ROCKET, + PEACH, + PEACH, + 4.0, + 139.7, + 27.59, + 49.73), + (0.06, + ROCKET, + PEACH, + PEACH, + 4.0, + 139.7, + -70.33, + 48.68), + (0.19, + ROCKET, + PINK, + PINK, + 4.0, + 139.7, + 4.74, + 66.45), + (0.09, + ROCKET, + PINK, + PINK, + 4.0, + 139.7, + -45.2, + 65.92), + (0.59, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 139.7, + 53.94, + 24.67), + (0.03, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 139.7, + -94.94, + 23.07), + (0.09, + ROCKET, + PEACH, + PEACH, + 4.0, + 139.7, + 27.59, + 49.73), + (0.03, + ROCKET, + PEACH, + PEACH, + 4.0, + 139.7, + -70.33, + 48.68), + (0.09, + ROCKET, + PINK, + PINK, + 4.0, + 139.7, + 4.74, + 66.45), + (0.03, + ROCKET, + PINK, + PINK, + 4.0, + 139.7, + -45.2, + 65.92), + (0.47, + CIRCLELARGE, + PURPLE, + PINK, + 5.0, + 82.52, + -32.63, + 42.45), + (0.31, + CIRCLELARGE, + PINK, + PEACH, + 10.0, + 98.02, + -4.11, + 58.76), + (0.31, + CIRCLELARGE, + PEACH, + YELLOW, + 8.0, + 107.66, + -45.36, + 67.65), + (0.44, + CIRCLELARGE, + YELLOW, + WHITE, + 12.0, + 101.02, + -11.32, + 35.33), + (0.69, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 106.9, + 41.71, + -4.52), + (0.25, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 127.41, + 29.95, + -4.52), + (0.25, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 112.57, + 16.35, + -4.52), + (0.31, + ROCKET, + PEACH, + PEACH, + 4.0, + 112.01, + -82.53, + -4.52), + (0.31, + ROCKET, + PEACH, + PEACH, + 4.0, + 127.64, + -69.67, + -4.52), + (0.25, + ROCKET, + PEACH, + PEACH, + 4.0, + 113.58, + -54.24, + -4.52), + (0.37, + ROCKET, + PINK, + PINK, + 4.0, + 113.77, + 44.25, + -4.52), + (0.25, + ROCKET, + PINK, + PINK, + 4.0, + 127.64, + 30.1, + -4.52), + (0.25, + ROCKET, + PINK, + PINK, + 4.0, + 112.21, + 16.69, + -4.52), + (0.31, + ROCKET, + WHITE, + WHITE, + 4.0, + 112.99, + -81.98, + -4.52), + (0.25, + ROCKET, + WHITE, + WHITE, + 4.0, + 127.44, + -69.67, + -4.52), + (0.25, + ROCKET, + WHITE, + WHITE, + 4.0, + 113.97, + -54.42, + -4.52), + (0.25, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 113.97, + 44.25, + -4.52), + (0.06, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 112.4, + 16.14, + -4.52), + (0.19, + ROCKET, + PEACH, + PEACH, + 4.0, + 113.77, + -54.24, + -4.52), + (0.06, + ROCKET, + PEACH, + PEACH, + 4.0, + 112.4, + -81.98, + -4.52), + (0.19, + ROCKET, + PINK, + PINK, + 4.0, + 115.09, + -10.1, + 0.79), + (0.06, + ROCKET, + PINK, + PINK, + 4.0, + 115.66, + -29.69, + 0.79), + (0.69, + CIRCLELARGE, + PURPLE, + PURPLE, + 10.0, + 112.99, + -21.41, + 73.4), + (0.25, + CIRCLESMALL, + PINK, + PINK, + 10.0, + 82.59, + -7.83, + 49.23), + (0.12, + CIRCLESMALL, + PINK, + PINK, + 10.0, + 106.28, + -39.22, + 60.32), + (0.13, + CIRCLESMALL, + WHITE, + WHITE, + 10.0, + 106.28, + -18.8, + 69.23), + (0.25, + CIRCLELARGE, + RED, + RED, + 10.0, + 106.28, + -5.25, + 43.69), + (0.25, + CIRCLESMALL, + PINK, + PINK, + 10.0, + 107.33, + -21.69, + 78.98), + (0.12, + CIRCLESMALL, + PINK, + PINK, + 10.0, + 111.92, + -52.95, + 64.92), + (0.13, + CIRCLESMALL, + WHITE, + WHITE, + 10.0, + 109.82, + 14.35, + 79.55), + (0.25, + CIRCLELARGE, + YELLOW, + YELLOW, + 10.0, + 75.62, + -42.59, + 39.25), + (0.25, + CIRCLESMALL, + PEACH, + PEACH, + 10.0, + 92.41, + -1.7, + 30.9), + (0.12, + CIRCLESMALL, + PINK, + PINK, + 10.0, + 71.9, + -0.06, + 50.32), + (0.13, + CIRCLESMALL, + RED, + RED, + 10.0, + 100.33, + -38.07, + 49.92), + (0.25, + CIRCLELARGE, + RED, + WHITE, + 10.0, + 125.29, + -21.7, + 85.61), + (0.25, + RING, + YELLOW, + YELLOW, + 4.0, + 125.29, + -21.7, + 85.61), + (0.13, + RING, + PEACH, + PEACH, + 4.0, + 125.29, + -21.7, + 85.61), + (0.12, + RING, + PINK, + PINK, + 4.0, + 125.29, + -21.7, + 85.61), + (0.25, + CIRCLELARGE, + PEACH, + PEACH, + 4.0, + 86.78, + -32.61, + 78.74), + (0.25, + CIRCLESMALL, + PINK, + PINK, + 4.0, + 85.81, + 6.87, + 65.98), + (0.13, + RING, + YELLOW, + YELLOW, + 4.0, + 97.06, + -16.05, + 94.59), + (0.12, + CIRCLESMALL, + RED, + RED, + 4.0, + 76.01, + -46.69, + 61.22), + (0.31, + CIRCLELARGE, + WHITE, + PINK, + 4.0, + 56.79, + 4.47, + 27.11), + (0.31, + RING, + RED, + RED, + 4.0, + 113.67, + -19.24, + 78.88), + (0.06, + CIRCLESMALL, + PINK, + PINK, + 4.0, + 86.78, + -11.48, + 84.63), + (0.06, + RING, + PURPLE, + PURPLE, + 4.0, + 76.66, + -54.89, + 50.2), + (0.5, + CIRCLELARGE, + PURPLE, + WHITE, + 4.0, + 94.84, + -38.08, + 63.24), + (0.25, + CIRCLESMALL, + PEACH, + WHITE, + 4.0, + 109.52, + -1.16, + 63.64), + (0.13, + CIRCLESMALL, + PINK, + WHITE, + 4.0, + 76.28, + -37.94, + 50.25), + (0.12, + CIRCLESMALL, + YELLOW, + WHITE, + 4.0, + 71.61, + 1.57, + 41.52), + (0.69, + RING, + YELLOW, + YELLOW, + 4.0, + 141.96, + -20.09, + 92.36), + (0.03, + RING, + PEACH, + PEACH, + 4.0, + 141.96, + -20.09, + 92.36), + (0.03, + RING, + RED, + RED, + 4.0, + 141.96, + -20.09, + 92.36)), + ((4, + POP, + PURPLE, + WHITE, + 20, + -107.67, + -39.76, + 9), + (1.5, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 20, + -107.67, + -39.76, + 30), + (2.25, + POP, + PURPLE, + WHITE, + 20, + -107.67, + -39.76, + 9), + (1.5, + CIRCLESPRITE, + CYAN, + SNOWFLAKE, + 20, + -107.67, + -39.76, + 60), + (2.75, + POP, + CYAN, + WHITE, + 20, + -107.67, + -39.76, + 60), + (1.5, + CIRCLESPRITE, + BLUE, + SNOWFLAKE, + 20, + -107.67, + -39.76, + 60), + (2.5, + POP, + BLUE, + WHITE, + 20, + -107.67, + -39.76, + 60), + (1.5, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 20, + -107.67, + -39.76, + 80), + (4.75, + POP, + WHITE, + WHITE, + 20, + -107.67, + -39.76, + 80), + (0.5, + POP, + WHITE, + WHITE, + 20, + -107.67, + -39.76, + 80), + (0.5, + POP, + WHITE, + WHITE, + 20, + -107.67, + -39.76, + 80), + (1.25, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 30, + -107.67, + -39.76, + 90), + (0.5, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 35, + -107.67, + -39.76, + 90), + (0.5, + CIRCLESPRITE, + CYAN, + SNOWFLAKE, + 40, + -107.67, + -39.76, + 90), + (3.75, + POP, + CYAN, + WHITE, + 60, + -107.67, + -39.76, + 90), + (0.25, + POP, + CYAN, + WHITE, + 60, + -107.67, + -39.76, + 90), + (0.25, + POP, + CYAN, + WHITE, + 60, + -107.67, + -39.76, + 90), + (0.25, + POP, + CYAN, + WHITE, + 60, + -107.67, + -39.76, + 90), + (1.25, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 0.5, + -107.67, + -59.76, + 90), + (0.25, + CIRCLESPRITE, + BLUE, + SNOWFLAKE, + 0.5, + -87.67, + -39.76, + 90), + (0.25, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 0.5, + -107.67, + -19.76, + 90), + (0.25, + CIRCLESPRITE, + CYAN, + SNOWFLAKE, + 0.5, + -127.67, + -39.76, + 90), + (6, + POP, + CYAN, + WHITE, + 40, + -107.67, + -39.76, + 90), + (1, + ROCKET, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.5, + CIRCLE, + PURPLE, + WHITE, + 20, + -107.67, + -39.76, + 10), + (0.5, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 49, + -107.67, + -39.76, + 110), + (3, + POP, + WHITE, + WHITE, + 49, + -107.67, + -39.76, + 110), + (0.25, + POP, + WHITE, + WHITE, + 49, + -107.67, + -39.76, + 110), + (1, + ROCKET, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + CIRCLE, + PURPLE, + WHITE, + 20, + -107.67, + -39.76, + 10), + (0.25, + CIRCLE, + CYAN, + WHITE, + 20, + -107.67, + -39.76, + 10), + (0.25, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 49, + -107.67, + -39.76, + 110), + (0.25, + CIRCLESPRITE, + BLUE, + SNOWFLAKE, + 49, + -107.67, + -39.76, + 110), + (2.5, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.5, + ROCKET, + BLUE, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + ROCKET, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.5, + CIRCLESPRITE, + BLUE, + SNOWFLAKE, + 49, + -107.67, + -39.76, + 110), + (0.25, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 49, + -107.67, + -39.76, + 110), + (0.25, + CIRCLESPRITE, + CYAN, + SNOWFLAKE, + 49, + -107.67, + -39.76, + 110), + (6.5, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.5, + POP, + CYAN, + WHITE, + 40, + -107.67, + -59.76, + 90), + (0.05, + POP, + CYAN, + WHITE, + 40, + -107.67, + -59.76, + 90), + (0.05, + POP, + CYAN, + WHITE, + 40, + -87.67, + -39.76, + 90), + (0.05, + POP, + CYAN, + WHITE, + 40, + -87.67, + -39.76, + 90), + (0.05, + POP, + CYAN, + WHITE, + 40, + -107.67, + -19.76, + 90), + (0.05, + POP, + CYAN, + WHITE, + 40, + -107.67, + -19.76, + 90), + (0.05, + POP, + CYAN, + WHITE, + 40, + -127.67, + -39.76, + 90), + (0, + ROCKET, + CYAN, + WHITE, + 5, + -107.67, + -59.76, + 10), + (0.05, + POP, + CYAN, + WHITE, + 40, + -127.67, + -39.76, + 90), + (0, + ROCKET, + BLUE, + WHITE, + 5, + -107.67, + -59.76, + 10), + (0.05, + ROCKET, + CYAN, + WHITE, + 5, + -87.67, + -39.76, + 10), + (0.05, + ROCKET, + BLUE, + WHITE, + 5, + -87.67, + -39.76, + 10), + (0.05, + ROCKET, + CYAN, + WHITE, + 5, + -107.67, + -19.76, + 10), + (0.05, + ROCKET, + BLUE, + WHITE, + 5, + -107.67, + -19.76, + 10), + (0.05, + ROCKET, + CYAN, + WHITE, + 5, + -127.67, + -39.76, + 10), + (0, + CIRCLE, + PURPLE, + WHITE, + 20, + -107.67, + -59.76, + 10), + (0.05, + ROCKET, + BLUE, + WHITE, + 5, + -127.67, + -39.76, + 10), + (0, + CIRCLE, + CYAN, + WHITE, + 20, + -107.67, + -59.76, + 10), + (0.05, + CIRCLE, + PURPLE, + WHITE, + 20, + -87.67, + -39.76, + 10), + (0.05, + CIRCLE, + CYAN, + WHITE, + 20, + -87.67, + -39.76, + 10), + (0.05, + CIRCLE, + PURPLE, + WHITE, + 20, + -107.67, + -19.76, + 10), + (0.05, + CIRCLE, + CYAN, + WHITE, + 20, + -107.67, + -19.76, + 10), + (0.05, + CIRCLE, + PURPLE, + WHITE, + 20, + -127.67, + -39.76, + 10), + (0.05, + CIRCLE, + CYAN, + WHITE, + 20, + -127.67, + -39.76, + 10), + (0.35, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 49, + -107.67, + -59.76, + 110), + (0.06, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 49, + -107.67, + -59.76, + 110), + (0.05, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 49, + -87.67, + -39.76, + 110), + (0.05, + CIRCLESPRITE, + PURPLE, + WHITE, + 49, + -87.67, + -39.76, + 110), + (0.05, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 49, + -107.67, + -19.76, + 110), + (0.05, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 49, + -107.67, + -19.76, + 110), + (0.05, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 49, + -127.67, + -39.76, + 110), + (0.06, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 49, + -127.67, + -39.76, + 110), + (0.09, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.09, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.09, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.06, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.25, + CIRCLESPRITE, + PINK, + SNOWFLAKE, + 65, + -107.67, + -59.76, + 110), + (0.16, + CIRCLESPRITE, + PINK, + SNOWFLAKE, + 65, + -87.67, + -39.76, + 110), + (0.22, + CIRCLESPRITE, + PINK, + SNOWFLAKE, + 65, + -107.67, + -19.76, + 110), + (0.12, + CIRCLESPRITE, + PINK, + SNOWFLAKE, + 65, + -127.67, + -39.76, + 110), + (0.22, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.09, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.09, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.12, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.28, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 55, + -107.67, + -59.76, + 110), + (0.12, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 55, + -87.67, + -39.76, + 110), + (0.12, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 55, + -107.67, + -19.76, + 110), + (0.09, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 55, + -127.67, + -39.76, + 110)), + ((4.5, + ROCKET, + YELLOW, + WHITE, + 5, + 0, + 190, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 20, + 170, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 25.65, + 158.38, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 35, + 0.5, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 40, + 130, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 54.79, + 127.03, + 0), + (3.5, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 40, + 130, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 37.27, + 139.59, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 35, + 0.5, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 20, + 170, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 14.69, + 179.71, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 190, + 0), + (4, + ROCKET, + YELLOW, + WHITE, + 5, + 35, + 0.5, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 0, + 190, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 40, + 130, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 20, + 170, + 0), + (1, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 14.69, + 179.71, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 37.27, + 139.59, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 190, + 0), + (0.31, + ROCKET, + WHITE, + YELLOW, + 5, + 40, + 130, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 14.69, + 179.71, + 0), + (0.31, + ROCKET, + WHITE, + YELLOW, + 5, + 20, + 170, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 37.27, + 139.59, + 0), + (0.38, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 35, + 0.5, + 0), + (4, + ROCKET, + WHITE, + YELLOW, + 5, + 35, + 0.5, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0.38, + ROCKET, + WHITE, + YELLOW, + 5, + 20, + 170, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 37.27, + 139.59, + 0), + (0.37, + ROCKET, + WHITE, + YELLOW, + 5, + 40, + 130, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 14.69, + 179.71, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 190, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (4, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 190, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 35, + 0.5, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 40, + 130, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 37.27, + 139.59, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 14.69, + 179.71, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 20, + 170, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 35, + 0.5, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 190, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 190, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 14.69, + 179.71, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 20, + 170, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 40, + 130, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 37.27, + 139.59, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 35, + 0.5, + 0), + (3.13, + ROCKET, + YELLOW, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 0, + 190, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 0, + 190, + 0), + (0, + ROCKET, + PINK, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0, + ROCKET, + PURPLE, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0.25, + ROCKET, + YELLOW, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 40, + 130, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0, + ROCKET, + PINK, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0, + ROCKET, + PURPLE, + WHITE, + 5, + 40, + 130, + 0), + (0.25, + ROCKET, + YELLOW, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 20, + 170, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0, + ROCKET, + PINK, + WHITE, + 5, + 20, + 170, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0, + ROCKET, + PURPLE, + WHITE, + 5, + 20, + 170, + 0), + (2.88, + ROCKET, + YELLOW, + WHITE, + 5, + 0, + 190, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 0, + 190, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 0, + 190, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0.25, + ROCKET, + YELLOW, + WHITE, + 5, + 20, + 170, + 0), + (0, + ROCKET, + PURPLE, + WHITE, + 5, + 20, + 170, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 20, + 170, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 25.65, + 158.38, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 25.65, + 158.38, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 25.65, + 158.38, + 0), + (0.25, + ROCKET, + YELLOW, + WHITE, + 5, + 35, + 0.5, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 35, + 0.5, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 35, + 0.5, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 40, + 130, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 40, + 130, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 54.79, + 127.03, + 0), + (3.99, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.12, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.14, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.75, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.12, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.13, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.75, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.13, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.12, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.71, + CIRCLE, + YELLOW, + WHITE, + 10, + 0, + 190, + 5), + (0.03, + ROCKET, + YELLOW, + WHITE, + 5, + 0, + 190, + 0), + (0.09, + CIRCLE, + YELLOW, + WHITE, + 10, + 14.69, + 179.71, + 0), + (0.03, + ROCKET, + YELLOW, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0.09, + CIRCLE, + YELLOW, + WHITE, + 10, + 20, + 170, + 0), + (0.04, + ROCKET, + YELLOW, + WHITE, + 5, + 20, + 170, + 0), + (0.08, + CIRCLE, + YELLOW, + WHITE, + 10, + 35, + 0.5, + 0), + (0.04, + ROCKET, + YELLOW, + WHITE, + 5, + 35, + 0.5, + 0), + (0.08, + CIRCLE, + YELLOW, + WHITE, + 10, + 35, + 0.5, + 0), + (0.05, + ROCKET, + YELLOW, + WHITE, + 5, + 35, + 0.5, + 0), + (0.07, + CIRCLE, + YELLOW, + WHITE, + 10, + 37.27, + 139.59, + 0), + (0.05, + ROCKET, + YELLOW, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0.07, + CIRCLE, + YELLOW, + WHITE, + 10, + 40, + 130, + 0), + (0.06, + ROCKET, + YELLOW, + WHITE, + 5, + 40, + 130, + 0), + (0.06, + CIRCLE, + YELLOW, + WHITE, + 10, + 54.79, + 127.03, + 0), + (0.06, + ROCKET, + YELLOW, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0.56, + CIRCLE, + PINK, + WHITE, + 12, + 0, + 190, + 5), + (0.04, + ROCKET, + PINK, + WHITE, + 7, + 0, + 190, + 0), + (0.08, + CIRCLE, + PINK, + WHITE, + 12, + 14.69, + 179.71, + 5), + (0.04, + ROCKET, + PINK, + WHITE, + 7, + 14.69, + 179.71, + 0), + (0.08, + CIRCLE, + PINK, + WHITE, + 7, + 20, + 170, + 5), + (0.05, + ROCKET, + PINK, + WHITE, + 7, + 20, + 170, + 0), + (0.06, + CIRCLE, + PINK, + WHITE, + 12, + 35, + 0.5, + 5), + (0.03, + ROCKET, + PINK, + WHITE, + 7, + 35, + 0.5, + 0), + (0.1, + CIRCLE, + PINK, + WHITE, + 12, + 35, + 0.5, + 5), + (0.03, + ROCKET, + PINK, + WHITE, + 7, + 35, + 0.5, + 0), + (0.09, + CIRCLE, + PINK, + WHITE, + 12, + 37.27, + 139.59, + 5), + (0.03, + ROCKET, + PINK, + WHITE, + 7, + 37.27, + 139.59, + 0), + (0.09, + CIRCLE, + PINK, + WHITE, + 12, + 40, + 130, + 5), + (0.04, + ROCKET, + PINK, + WHITE, + 7, + 40, + 130, + 0), + (0.08, + CIRCLE, + PINK, + WHITE, + 12, + 54.79, + 127.03, + 5), + (0.04, + ROCKET, + PINK, + WHITE, + 7, + 54.79, + 127.03, + 0), + (0.58, + CIRCLE, + PURPLE, + WHITE, + 14, + 0, + 190, + 5), + (0.05, + ROCKET, + PURPLE, + WHITE, + 9, + 0, + 190, + 0), + (0.12, + CIRCLE, + PURPLE, + WHITE, + 14, + 14.69, + 179.71, + 5), + (0.03, + ROCKET, + PURPLE, + WHITE, + 9, + 14.69, + 179.71, + 0), + (0.09, + CIRCLE, + PURPLE, + WHITE, + 14, + 20, + 170, + 5), + (0.06, + ROCKET, + PURPLE, + WHITE, + 9, + 20, + 170, + 0), + (0.09, + CIRCLE, + PURPLE, + WHITE, + 14, + 35, + 0.5, + 5), + (0.06, + ROCKET, + PURPLE, + WHITE, + 9, + 35, + 0.5, + 0), + (0.09, + CIRCLE, + PURPLE, + WHITE, + 14, + 35, + 0.5, + 5), + (0.03, + ROCKET, + PURPLE, + WHITE, + 9, + 35, + 0.5, + 0), + (0.12, + CIRCLE, + PURPLE, + WHITE, + 14, + 37.27, + 139.59, + 5), + (0.03, + ROCKET, + PURPLE, + WHITE, + 9, + 37.27, + 139.59, + 0), + (0.12, + CIRCLE, + PURPLE, + WHITE, + 14, + 40, + 130, + 0), + (0.03, + ROCKET, + PURPLE, + WHITE, + 9, + 40, + 130, + 0), + (0.09, + CIRCLE, + PURPLE, + WHITE, + 14, + 54.79, + 127.03, + 5), + (0.06, + ROCKET, + PURPLE, + WHITE, + 9, + 54.79, + 127.03, + 0), + (0.5, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 0, + 190, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 14.69, + 179.71, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 20, + 170, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 35, + 0.5, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 35, + 0.5, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 37.27, + 139.59, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 40, + 130, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 54.79, + 127.03, + 80), + (0.56, + CIRCLELARGE, + PINK, + WHITE, + 15, + 0, + 190, + 90), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 15, + 14.69, + 179.71, + 90), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 15, + 20, + 170, + 90), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 15, + 35, + 0.5, + 90), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 15, + 35, + 0.5, + 90), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 15, + 37.27, + 139.59, + 0), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 15, + 40, + 130, + 90), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 7, + 54.79, + 127.03, + 90), + (0.56, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 0, + 190, + 100), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 14.69, + 179.71, + 100), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 20, + 170, + 100), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 35, + 0.5, + 100), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 35, + 0.5, + 100), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 37.27, + 139.59, + 100), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 9, + 40, + 130, + 0), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 54.79, + 127.03, + 100))], + PartyGlobals.FireworkShows.Summer: [((0.5, + ROCKET, + WHITE, + WHITE, + 5.0, + 113.22 + PartyGlobals.FireworksGlobalXOffset, + 1.05 + PartyGlobals.FireworksGlobalYOffset, + 73.24), + (0.25, + ROCKET, + RED, + RED, + 5.0, + 115.54 + PartyGlobals.FireworksGlobalXOffset, + 13.8 + PartyGlobals.FireworksGlobalYOffset, + 59.11), + (0.03, + ROCKET, + RED, + RED, + 5.0, + 118.36 + PartyGlobals.FireworksGlobalXOffset, + -13.83 + PartyGlobals.FireworksGlobalYOffset, + 57.71), + (0.22, + ROCKET, + BLUE, + BLUE, + 5.0, + 110.05 + PartyGlobals.FireworksGlobalXOffset, + 25.37 + PartyGlobals.FireworksGlobalYOffset, + 57.01), + (0.03, + ROCKET, + BLUE, + BLUE, + 5.0, + 110.05 + PartyGlobals.FireworksGlobalXOffset, + -25.37 + PartyGlobals.FireworksGlobalYOffset, + 57.01), + (3.22, + CIRCLELARGE, + WHITE, + WHITE, + 10.0, + 121.75 + PartyGlobals.FireworksGlobalXOffset, + 1.07 + PartyGlobals.FireworksGlobalYOffset, + 106.69), + (1.94, + CIRCLELARGE, + RED, + RED, + 10.0, + 121.75 + PartyGlobals.FireworksGlobalXOffset, + 1.07 + PartyGlobals.FireworksGlobalYOffset, + 106.69), + (1.31, + ROCKET, + BLUE, + BLUE, + 4.0, + 121.75 + PartyGlobals.FireworksGlobalXOffset, + 28.63 + PartyGlobals.FireworksGlobalYOffset, + 38.55), + (0.06, + ROCKET, + RED, + RED, + 4.0, + 121.75 + PartyGlobals.FireworksGlobalXOffset, + 1.3 + PartyGlobals.FireworksGlobalYOffset, + 38.76), + (0.06, + ROCKET, + WHITE, + WHITE, + 4.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + -25.6 + PartyGlobals.FireworksGlobalYOffset, + 37.54), + (0.88, + ROCKET, + BLUE, + BLUE, + 4.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + -25.6 + PartyGlobals.FireworksGlobalYOffset, + 37.54), + (0.06, + ROCKET, + RED, + RED, + 4.0, + 121.74 + PartyGlobals.FireworksGlobalXOffset, + 1.3 + PartyGlobals.FireworksGlobalYOffset, + 37.75), + (0.06, + ROCKET, + WHITE, + WHITE, + 4.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 27.34 + PartyGlobals.FireworksGlobalYOffset, + 37.12), + (0.82, + ROCKET, + BLUE, + BLUE, + 4.0, + 55.0 + PartyGlobals.FireworksGlobalXOffset, + 40.0 + PartyGlobals.FireworksGlobalYOffset, + 40.0), + (0.09, + ROCKET, + RED, + RED, + 4.0, + 70.0 + PartyGlobals.FireworksGlobalXOffset, + 40.0 + PartyGlobals.FireworksGlobalYOffset, + 40.0), + (0.09, + ROCKET, + WHITE, + WHITE, + 4.0, + 85.0 + PartyGlobals.FireworksGlobalXOffset, + 40.0 + PartyGlobals.FireworksGlobalYOffset, + 40.0), + (0.72, + ROCKET, + BLUE, + BLUE, + 4.0, + 55.0 + PartyGlobals.FireworksGlobalXOffset, + -40.0 + PartyGlobals.FireworksGlobalYOffset, + 40.0), + (0.09, + ROCKET, + RED, + RED, + 4.0, + 70.0 + PartyGlobals.FireworksGlobalXOffset, + -40.0 + PartyGlobals.FireworksGlobalYOffset, + 40.0), + (0.09, + ROCKET, + WHITE, + WHITE, + 4.0, + 85.0 + PartyGlobals.FireworksGlobalXOffset, + -40.0 + PartyGlobals.FireworksGlobalYOffset, + 40.0), + (0.84, + CIRCLE, + BLUE, + BLUE, + 10.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 29.29 + PartyGlobals.FireworksGlobalYOffset, + 78.62), + (0.06, + RING, + WHITE, + WHITE, + 5.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 29.29 + PartyGlobals.FireworksGlobalYOffset, + 78.62), + (0.75, + CIRCLE, + RED, + RED, + 10.0, + 120.0 + PartyGlobals.FireworksGlobalXOffset, + -30.0 + PartyGlobals.FireworksGlobalYOffset, + 100.0), + (0.06, + RING, + WHITE, + WHITE, + 5.0, + 120.0 + PartyGlobals.FireworksGlobalXOffset, + -30.0 + PartyGlobals.FireworksGlobalYOffset, + 100.0), + (0.7, + CIRCLE, + WHITE, + WHITE, + 10.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 29.29 + PartyGlobals.FireworksGlobalYOffset, + 92.0), + (0.19, + RING, + BLUE, + BLUE, + 5.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 29.29 + PartyGlobals.FireworksGlobalYOffset, + 92.0), + (0.8, + CIRCLE, + WHITE, + WHITE, + 10.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 0.0 + PartyGlobals.FireworksGlobalYOffset, + 100.0), + (0.08, + RING, + RED, + RED, + 5.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 0.0 + PartyGlobals.FireworksGlobalYOffset, + 100.0), + (1.0, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.43 + PartyGlobals.FireworksGlobalXOffset, + 22.69 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.24 + PartyGlobals.FireworksGlobalXOffset, + 17.57 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 10.82 + PartyGlobals.FireworksGlobalXOffset, + 12.13 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.22, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.17 + PartyGlobals.FireworksGlobalXOffset, + 6.76 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.31, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.1 + PartyGlobals.FireworksGlobalXOffset, + 1.91 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.71 + PartyGlobals.FireworksGlobalXOffset, + -3.56 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.19, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.26 + PartyGlobals.FireworksGlobalXOffset, + -8.51 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.28, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.25 + PartyGlobals.FireworksGlobalXOffset, + -14.18 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.2 + PartyGlobals.FireworksGlobalXOffset, + -19.42 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.53 + PartyGlobals.FireworksGlobalXOffset, + -24.46 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (1.24, + CIRCLESMALL, + RED, + WHITE, + 15.0, + 68.68 + PartyGlobals.FireworksGlobalXOffset, + -29.58 + PartyGlobals.FireworksGlobalYOffset, + 57.72), + (1.63, + CIRCLE, + BLUE, + WHITE, + 15.0, + 70.33 + PartyGlobals.FireworksGlobalXOffset, + 29.76 + PartyGlobals.FireworksGlobalYOffset, + 56.3), + (0.87, + CIRCLELARGE, + WHITE, + WHITE, + 15.0, + 121.64 + PartyGlobals.FireworksGlobalXOffset, + 1.2 + PartyGlobals.FireworksGlobalYOffset, + 92.64), + (1.26, + RING, + WHITE, + WHITE, + 15.0, + 83.12 + PartyGlobals.FireworksGlobalXOffset, + 12.21 + PartyGlobals.FireworksGlobalYOffset, + 92.04), + (0.44, + RING, + RED, + RED, + 15.0, + 84.41 + PartyGlobals.FireworksGlobalXOffset, + -23.72 + PartyGlobals.FireworksGlobalYOffset, + 78.01), + (0.44, + RING, + WHITE, + WHITE, + 15.0, + 83.5 + PartyGlobals.FireworksGlobalXOffset, + 1.77 + PartyGlobals.FireworksGlobalYOffset, + 68.65), + (0.38, + RING, + BLUE, + BLUE, + 15.0, + 82.6 + PartyGlobals.FireworksGlobalXOffset, + 26.64 + PartyGlobals.FireworksGlobalYOffset, + 68.65), + (0.37, + CIRCLESMALL, + WHITE, + WHITE, + 11.0, + 74.5 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 71.8), + (0.12, + CIRCLELARGE, + RED, + RED, + 11.0, + 74.5 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 71.8), + (0.75, + CIRCLESMALL, + WHITE, + WHITE, + 11.0, + 94.03 + PartyGlobals.FireworksGlobalXOffset, + -15.23 + PartyGlobals.FireworksGlobalYOffset, + 62.12), + (0.13, + CIRCLELARGE, + BLUE, + BLUE, + 11.0, + 94.03 + PartyGlobals.FireworksGlobalXOffset, + -15.23 + PartyGlobals.FireworksGlobalYOffset, + 62.12), + (0.56, + CIRCLESMALL, + RED, + RED, + 11.0, + 54.56 + PartyGlobals.FireworksGlobalXOffset, + 7.87 + PartyGlobals.FireworksGlobalYOffset, + 53.74), + (0.19, + CIRCLELARGE, + WHITE, + WHITE, + 11.0, + 54.56 + PartyGlobals.FireworksGlobalXOffset, + 7.87 + PartyGlobals.FireworksGlobalYOffset, + 53.74), + (0.63, + CIRCLESMALL, + BLUE, + BLUE, + 11.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.12, + CIRCLELARGE, + WHITE, + WHITE, + 11.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.37, + RING, + RED, + RED, + 5.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.47, + RING, + WHITE, + WHITE, + 6.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.47, + RING, + BLUE, + BLUE, + 7.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.47, + RING, + WHITE, + WHITE, + 8.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.47, + RING, + RED, + RED, + 10.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.37, + ROCKET, + WHITE, + WHITE, + 8.0, + 53.85 + PartyGlobals.FireworksGlobalXOffset, + 14.92 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 8.0, + 54.66 + PartyGlobals.FireworksGlobalXOffset, + -16.71 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.44, + ROCKET, + WHITE, + WHITE, + 8.0, + 73.27 + PartyGlobals.FireworksGlobalXOffset, + 15.16 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 8.0, + 73.38 + PartyGlobals.FireworksGlobalXOffset, + -15.98 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.44, + ROCKET, + WHITE, + WHITE, + 8.0, + 93.33 + PartyGlobals.FireworksGlobalXOffset, + 15.17 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 8.0, + 93.71 + PartyGlobals.FireworksGlobalXOffset, + -15.73 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.89, + CIRCLELARGE, + WHITE, + WHITE, + 8.0, + 151.88 + PartyGlobals.FireworksGlobalXOffset, + 1.92 + PartyGlobals.FireworksGlobalYOffset, + 144.56), + (0.93, + CIRCLESMALL, + WHITE, + WHITE, + 8.0, + 82.25 + PartyGlobals.FireworksGlobalXOffset, + 16.72 + PartyGlobals.FireworksGlobalYOffset, + 82.5), + (0.12, + CIRCLELARGE, + WHITE, + WHITE, + 8.0, + 79.31 + PartyGlobals.FireworksGlobalXOffset, + 1.0 + PartyGlobals.FireworksGlobalYOffset, + 63.69), + (0.13, + CIRCLESMALL, + WHITE, + WHITE, + 8.0, + 82.02 + PartyGlobals.FireworksGlobalXOffset, + -13.25 + PartyGlobals.FireworksGlobalYOffset, + 79.16), + (0.88, + ROCKET, + WHITE, + RED, + 4.0, + 6.47 + PartyGlobals.FireworksGlobalXOffset, + -15.0 + PartyGlobals.FireworksGlobalYOffset, + 8.6), + (0.06, + ROCKET, + WHITE, + RED, + 4.0, + 18.41 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.31, + ROCKET, + WHITE, + BLUE, + 4.0, + 53.79 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + BLUE, + 4.0, + 54.85 + PartyGlobals.FireworksGlobalXOffset, + -15.0 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.5, + ROCKET, + RED, + RED, + 4.0, + 90.45 + PartyGlobals.FireworksGlobalXOffset, + -15.0 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.19, + ROCKET, + RED, + RED, + 4.0, + 90.21 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 4.0, + 90.21 + PartyGlobals.FireworksGlobalXOffset, + 0.0 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.44, + CIRCLE, + WHITE, + WHITE, + 4.0, + 18.41 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 90.0), + (0.06, + RING, + RED, + RED, + 1.0, + 18.41 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 90.0), + (0.75, + CIRCLE, + WHITE, + WHITE, + 4.0, + 93.56 + PartyGlobals.FireworksGlobalXOffset, + -16.02 + PartyGlobals.FireworksGlobalYOffset, + 72.89), + (0.06, + RING, + RED, + RED, + 1.0, + 93.56 + PartyGlobals.FireworksGlobalXOffset, + -16.02 + PartyGlobals.FireworksGlobalYOffset, + 72.89), + (0.69, + CIRCLE, + WHITE, + WHITE, + 4.0 + PartyGlobals.FireworksGlobalXOffset, + 53.19 + PartyGlobals.FireworksGlobalYOffset, + -10.03, + 98.97), + (0.06, + RING, + BLUE, + BLUE, + 1.0, + 53.19 + PartyGlobals.FireworksGlobalXOffset, + -10.03 + PartyGlobals.FireworksGlobalYOffset, + 98.97), + (0.69, + CIRCLE, + WHITE, + WHITE, + 4.0, + 71.46 + PartyGlobals.FireworksGlobalXOffset, + 13.03 + PartyGlobals.FireworksGlobalYOffset, + 56.82), + (0.06, + RING, + BLUE, + BLUE, + 1.0, + 71.46 + PartyGlobals.FireworksGlobalXOffset, + 13.03 + PartyGlobals.FireworksGlobalYOffset, + 56.82), + (0.25, + CIRCLE, + RED, + RED, + 4.0, + 89.38 + PartyGlobals.FireworksGlobalXOffset, + -3.18 + PartyGlobals.FireworksGlobalYOffset, + 95.18), + (0.06, + RING, + WHITE, + WHITE, + 1.0, + 89.38 + PartyGlobals.FireworksGlobalXOffset, + -3.18 + PartyGlobals.FireworksGlobalYOffset, + 95.18), + (0.31, + CIRCLELARGE, + WHITE, + WHITE, + 19.0, + 94.74 + PartyGlobals.FireworksGlobalXOffset, + 0.89 + PartyGlobals.FireworksGlobalYOffset, + 65.55), + (0.44, + ROCKET, + WHITE, + PEACH, + 3.0, + 96.5 + PartyGlobals.FireworksGlobalXOffset, + 2.5 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 95.93 + PartyGlobals.FireworksGlobalXOffset, + -2.61 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.38, + ROCKET, + WHITE, + PEACH, + 3.0, + 76.08 + PartyGlobals.FireworksGlobalXOffset, + 7.72 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 76.54 + PartyGlobals.FireworksGlobalXOffset, + -8.28 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.37, + ROCKET, + WHITE, + PEACH, + 3.0, + 56.41 + PartyGlobals.FireworksGlobalXOffset, + 12.05 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 56.76 + PartyGlobals.FireworksGlobalXOffset, + -13.51 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.31, + ROCKET, + WHITE, + PEACH, + 3.0, + 36.29 + PartyGlobals.FireworksGlobalXOffset, + 16.92 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.1, + ROCKET, + WHITE, + PEACH, + 3.0, + 37.03 + PartyGlobals.FireworksGlobalXOffset, + -19.08 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.96, + CIRCLESMALL, + WHITE, + WHITE, + 3.0, + 100.31 + PartyGlobals.FireworksGlobalXOffset, + 2.5 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.06, + CIRCLESMALL, + WHITE, + WHITE, + 3.0, + 100.31 + PartyGlobals.FireworksGlobalXOffset, + -2.5 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.44, + CIRCLESMALL, + WHITE, + RED, + 6.0, + 76.25 + PartyGlobals.FireworksGlobalXOffset, + 8.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.06, + CIRCLESMALL, + WHITE, + RED, + 6.0, + 76.25 + PartyGlobals.FireworksGlobalXOffset, + -8.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.44, + CIRCLE, + WHITE, + BLUE, + 3.0, + 34.77 + PartyGlobals.FireworksGlobalXOffset, + 12.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.06, + CIRCLE, + WHITE, + BLUE, + 3.0, + 34.77 + PartyGlobals.FireworksGlobalXOffset, + -12.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.44, + CIRCLELARGE, + WHITE, + WHITE, + 3.0, + 10.0 + PartyGlobals.FireworksGlobalXOffset, + 16.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.06, + CIRCLELARGE, + WHITE, + WHITE, + 3.0, + 10.0 + PartyGlobals.FireworksGlobalXOffset, + -16.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.94, + CIRCLESPRITE, + WHITE, + ICECREAM, + 6.0, + 10.0 + PartyGlobals.FireworksGlobalXOffset, + 0.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (2.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.13, + ROCKET, + RED, + RED, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.44, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.06, + ROCKET, + RED, + RED, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.38, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.13, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.25, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.63, + ROCKET, + RED, + RED, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.13, + ROCKET, + BLUE, + BLUE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.25, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.19, + ROCKET, + RED, + RED, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.94, + ROCKET, + WHITE, + WHITE, + 5.0, + 74.23 + PartyGlobals.FireworksGlobalXOffset, + -0.06 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.28, + ROCKET, + BLUE, + BLUE, + 5.0, + 53.66 + PartyGlobals.FireworksGlobalXOffset, + 0.14 + PartyGlobals.FireworksGlobalYOffset, + 13.48), + (0.28, + ROCKET, + RED, + RED, + 5.0, + 53.96 + PartyGlobals.FireworksGlobalXOffset, + -0.38 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.28, + ROCKET, + WHITE, + WHITE, + 5.0, + 63.67 + PartyGlobals.FireworksGlobalXOffset, + 10.54 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.34, + ROCKET, + WHITE, + WHITE, + 4.0, + 63.88 + PartyGlobals.FireworksGlobalXOffset, + -10.98 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.16, + ROCKET, + RED, + RED, + 5.0, + 64.24 + PartyGlobals.FireworksGlobalXOffset, + -4.44 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.16, + ROCKET, + RED, + RED, + 5.0, + 64.16 + PartyGlobals.FireworksGlobalXOffset, + 2.92 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.12, + ROCKET, + RED, + RED, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.34, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.03, + ROCKET, + RED, + RED, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.5, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.38, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.34, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79 + PartyGlobals.FireworksGlobalXOffset, + 19.8 + PartyGlobals.FireworksGlobalYOffset, + 24.32), + (0.25, + ROCKET, + RED, + RED, + 5.0, + 107.58 + PartyGlobals.FireworksGlobalXOffset, + 11.29 + PartyGlobals.FireworksGlobalYOffset, + 45.0), + (0.22, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.22 + PartyGlobals.FireworksGlobalXOffset, + 3.33 + PartyGlobals.FireworksGlobalYOffset, + 60.0), + (0.37, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.34 + PartyGlobals.FireworksGlobalXOffset, + -0.98 + PartyGlobals.FireworksGlobalYOffset, + 60.0), + (0.22, + ROCKET, + RED, + RED, + 5.0, + 108.45 + PartyGlobals.FireworksGlobalXOffset, + -9.33 + PartyGlobals.FireworksGlobalYOffset, + 45.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69 + PartyGlobals.FireworksGlobalXOffset, + -17.77 + PartyGlobals.FireworksGlobalYOffset, + 24.4), + (0.53, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79 + PartyGlobals.FireworksGlobalXOffset, + 19.8 + PartyGlobals.FireworksGlobalYOffset, + 24.32), + (0.25, + ROCKET, + RED, + RED, + 5.0, + 107.58 + PartyGlobals.FireworksGlobalXOffset, + 11.29 + PartyGlobals.FireworksGlobalYOffset, + 45.0), + (0.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.22 + PartyGlobals.FireworksGlobalXOffset, + 3.33 + PartyGlobals.FireworksGlobalYOffset, + 60.0), + (0.5, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.34 + PartyGlobals.FireworksGlobalXOffset, + -0.98 + PartyGlobals.FireworksGlobalYOffset, + 60.0), + (0.19, + ROCKET, + RED, + RED, + 5.0, + 108.45 + PartyGlobals.FireworksGlobalXOffset, + -9.33 + PartyGlobals.FireworksGlobalYOffset, + 45.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69 + PartyGlobals.FireworksGlobalXOffset, + -17.77 + PartyGlobals.FireworksGlobalYOffset, + 24.4), + (0.5, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79 + PartyGlobals.FireworksGlobalXOffset, + 19.8 + PartyGlobals.FireworksGlobalYOffset, + 24.32), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69 + PartyGlobals.FireworksGlobalXOffset, + -17.77 + PartyGlobals.FireworksGlobalYOffset, + 24.4), + (0.19, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.22 + PartyGlobals.FireworksGlobalXOffset, + 3.33 + PartyGlobals.FireworksGlobalYOffset, + 60.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.34 + PartyGlobals.FireworksGlobalXOffset, + -0.98 + PartyGlobals.FireworksGlobalYOffset, + 60.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79 + PartyGlobals.FireworksGlobalXOffset, + 19.8 + PartyGlobals.FireworksGlobalYOffset, + 24.32), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69 + PartyGlobals.FireworksGlobalXOffset, + -17.77 + PartyGlobals.FireworksGlobalYOffset, + 24.4), + (0.94, + CIRCLELARGE, + BLUE, + BLUE, + 5.0, + 43.82 + PartyGlobals.FireworksGlobalXOffset, + -2.09 + PartyGlobals.FireworksGlobalYOffset, + 49.31), + (0.25, + POP, + BLUE, + BLUE, + 5.0, + 43.82 + PartyGlobals.FireworksGlobalXOffset, + -2.09 + PartyGlobals.FireworksGlobalYOffset, + 49.31), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 5.0, + 43.82 + PartyGlobals.FireworksGlobalXOffset, + -2.09 + PartyGlobals.FireworksGlobalYOffset, + 49.31), + (0.13, + CIRCLELARGE, + RED, + RED, + 5.0, + 40.83 + PartyGlobals.FireworksGlobalXOffset, + 10.33 + PartyGlobals.FireworksGlobalYOffset, + 65.41), + (0.25, + CIRCLE, + WHITE, + WHITE, + 5.0, + 103.3 + PartyGlobals.FireworksGlobalXOffset, + 12.66 + PartyGlobals.FireworksGlobalYOffset, + 83.93), + (0.25, + RING, + RED, + RED, + 5.0, + 95.27 + PartyGlobals.FireworksGlobalXOffset, + -9.92 + PartyGlobals.FireworksGlobalYOffset, + 72.55), + (0.37, + POP, + BLUE, + BLUE, + 5.0, + 43.82 + PartyGlobals.FireworksGlobalXOffset, + -2.09 + PartyGlobals.FireworksGlobalYOffset, + 49.31), + (0.13, + CIRCLELARGE, + BLUE, + RED, + 5.0, + 107.72 + PartyGlobals.FireworksGlobalXOffset, + -11.81 + PartyGlobals.FireworksGlobalYOffset, + 50.51), + (0.5, + CIRCLESMALL, + BLUE, + WHITE, + 5.0, + 102.29 + PartyGlobals.FireworksGlobalXOffset, + -8.59 + PartyGlobals.FireworksGlobalYOffset, + 80.37), + (0.31, + RING, + BLUE, + BLUE, + 5.0, + 49.85 + PartyGlobals.FireworksGlobalXOffset, + 12.65 + PartyGlobals.FireworksGlobalYOffset, + 59.45), + (0.44, + RING, + WHITE, + WHITE, + 5.0, + 22.91 + PartyGlobals.FireworksGlobalXOffset, + -0.7 + PartyGlobals.FireworksGlobalYOffset, + 68.75), + (0.13, + CIRCLELARGE, + WHITE, + RED, + 5.0, + 91.87 + PartyGlobals.FireworksGlobalXOffset, + 0.99 + PartyGlobals.FireworksGlobalYOffset, + 72.32), + (0.25, + POP, + BLUE, + BLUE, + 5.0, + 43.82 + PartyGlobals.FireworksGlobalXOffset, + -2.09 + PartyGlobals.FireworksGlobalYOffset, + 49.31), + (0.25, + CIRCLE, + RED, + RED, + 5.0, + 2.49 + PartyGlobals.FireworksGlobalXOffset, + 6.37 + PartyGlobals.FireworksGlobalYOffset, + 43.93), + (0.37, + RING, + BLUE, + BLUE, + 5.0, + 84.93 + PartyGlobals.FireworksGlobalXOffset, + 8.5 + PartyGlobals.FireworksGlobalYOffset, + 77.25), + (0.44, + RING, + WHITE, + WHITE, + 5.0, + 85.37 + PartyGlobals.FireworksGlobalXOffset, + -5.29 + PartyGlobals.FireworksGlobalYOffset, + 75.3), + (0.44, + CIRCLELARGE, + RED, + RED, + 5.0, + 86.11 + PartyGlobals.FireworksGlobalXOffset, + -11.82 + PartyGlobals.FireworksGlobalYOffset, + 71.43), + (0.31, + CIRCLELARGE, + WHITE, + WHITE, + 5.0, + 79.35 + PartyGlobals.FireworksGlobalXOffset, + 8.71 + PartyGlobals.FireworksGlobalYOffset, + 77.0), + (0.19, + CIRCLELARGE, + BLUE, + BLUE, + 5.0, + 66.08 + PartyGlobals.FireworksGlobalXOffset, + 0.98 + PartyGlobals.FireworksGlobalYOffset, + 68.8))]} + +def getShow(holidayId, index): + showList = shows.get(holidayId, []) + if index < len(showList): + return showList[index] + else: + return None + return None + + +def getShowDuration(eventId, index): + show = getShow(eventId, index) + duration = 0.0 + duration += skyTransitionDuration + duration += preShowPauseDuration + for effect in show: + waitTime, style, colorIndex1, colorIndex2, amp, x, y, z = effect + duration += waitTime + + duration += postShowPauseDuration + duration += preNormalMusicPauseDuration + return duration diff --git a/toontown/effects/FireworkSparkles.py b/toontown/effects/FireworkSparkles.py new file mode 100755 index 00000000..4180ff5b --- /dev/null +++ b/toontown/effects/FireworkSparkles.py @@ -0,0 +1,94 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from EffectController import EffectController +from PooledEffect import PooledEffect +import random + +class FireworkSparkles(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.card = model.find('**/tt_t_efx_ext_particleSpark_sharp') + self.cardScale = 16.0 + self.setDepthWrite(0) + self.setColorScaleOff() + self.setLightOff() + self.startDelay = 0.0 + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.f = ParticleEffect.ParticleEffect('Sparkles') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-2') + self.p0.setFactory('PointParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('SphereVolumeEmitter') + self.f.addParticles(self.p0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -15.0), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + self.p0.setPoolSize(64) + self.p0.setBirthRate(0.02) + self.p0.setLitterSize(10) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(1) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(1.5) + self.p0.factory.setLifespanSpread(1.0) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.renderer.getColorInterpolationManager().addLinear(0.0, 0.1, Vec4(0, 0, 0, 0), self.effectColor, 1) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, 0.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + self.setEffectColor(self.effectColor) + + def createTrack(self): + self.track = Sequence(Wait(self.startDelay), Func(self.p0.setBirthRate, 0.03), Func(self.p0.clearToInitial), Func(self.f.start, self, self), Wait(0.3), Func(self.p0.setBirthRate, 100.0), Wait(2.5), Func(self.cleanUpEffect)) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(1.2 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(1.5 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(1.5 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(1.2 * self.cardScale * scale) + self.p0.emitter.setAmplitude(25.0 * scale) + self.p0.emitter.setRadius(400.0 * scale) + + def setRadius(self, radius): + self.p0.emitter.setRadius(radius) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/Fireworks.py b/toontown/effects/Fireworks.py new file mode 100755 index 00000000..1d007e69 --- /dev/null +++ b/toontown/effects/Fireworks.py @@ -0,0 +1,239 @@ +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect +from direct.particles import Particles +from direct.particles import ForceGroup +from panda3d.core import * +import random +from FireworkGlobals import * +colors = {WHITE: Vec4(1, 1, 1, 1), + RED: Vec4(1, 0.2, 0.2, 1), + BLUE: Vec4(0.2, 0.2, 1, 1), + YELLOW: Vec4(1, 1, 0.2, 1), + GREEN: Vec4(0.2, 1, 0.2, 1), + PINK: Vec4(1, 0.5, 0.5, 1), + PEACH: Vec4(0.9, 0.6, 0.4, 1), + PURPLE: Vec4(1, 0.1, 1, 1), + CYAN: Vec4(0.2, 1, 1, 1)} +textures = {SNOWFLAKE: 'phase_8/models/props/snowflake_treasure', + MUSICNOTE: 'phase_6/models/props/music_treasure', + FLOWER: 'phase_8/models/props/flower_treasure', + ICECREAM: 'phase_4/models/props/icecream', + STARFISH: 'phase_6/models/props/starfish_treasure', + ZZZ: 'phase_8/models/props/zzz_treasure'} +fireworkId = 0 + +def getNextSequenceName(name): + global fireworkId + fireworkId += 1 + return '%s-%s' % (name, fireworkId) + + +def getColor(colorIndex): + return colors.get(colorIndex) + + +def getTexture(textureIndex): + return loader.loadModel(textures.get(textureIndex)) + + +def shootFirework(style, x = 0, y = 0, z = 0, colorIndex1 = 0, colorIndex2 = 0, amp = 10): + func = style2shootFunc.get(style) + color1 = getColor(colorIndex1) + if style is CIRCLESPRITE: + color2 = getTexture(colorIndex2) + else: + color2 = getColor(colorIndex2) + if func and color1 and color2: + return func(x, y, z, color1, color2, amp) + + +def shootFireworkRing(x, y, z, color1, color2, amp): + f = ParticleEffect.ParticleEffect() + p0 = Particles.Particles('particles-1') + p0.setFactory('PointParticleFactory') + p0.setRenderer('SparkleParticleRenderer') + p0.setEmitter('RingEmitter') + p0.setPoolSize(100) + p0.setBirthRate(0.01) + p0.setLitterSize(100) + p0.setLitterSpread(0) + p0.factory.setLifespanBase(1.5) + p0.factory.setLifespanSpread(0.5) + p0.factory.setMassBase(1.0) + p0.factory.setMassSpread(0.0) + p0.factory.setTerminalVelocityBase(20.0) + p0.factory.setTerminalVelocitySpread(2.0) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.0) + p0.renderer.setCenterColor(color1) + p0.renderer.setEdgeColor(color2) + p0.renderer.setBirthRadius(0.3) + p0.renderer.setDeathRadius(0.3) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(0) + p0.emitter.setAmplitudeSpread(0) + f0 = ForceGroup.ForceGroup('gravity') + force0 = LinearSourceForce(Point3(x, y, z), LinearDistanceForce.FTONEOVERR, 0.1, 1.1 * amp, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearSinkForce(Point3(x, y, z), LinearDistanceForce.FTONEOVERR, 0.5, 2.0 * amp, 1) + force1.setActive(1) + f0.addForce(force1) + f.addForceGroup(f0) + p0.emitter.setRadius(4.0) + f.addParticles(p0) + f.setPos(x, y, z) + f.setHpr(0, random.random() * 180, random.random() * 180) + sfx = loader.loadSfx('phase_4/audio/sfx/firework_distance_03.ogg') + sfx.setVolume(0.7) + t = Sequence(Func(f.start, render, render), Func(sfx.play), Wait(0.5), Func(p0.setBirthRate, 3), Wait(1.5), Func(f.cleanup), name=getNextSequenceName('shootFireworkRing')) + t.start() + + +def shootFireworkRocket(x, y, z, color1, color2, amp): + f = ParticleEffect.ParticleEffect() + p0 = Particles.Particles('particles-1') + p0.setFactory('PointParticleFactory') + p0.setRenderer('SparkleParticleRenderer') + p0.setEmitter('SphereVolumeEmitter') + p0.setPoolSize(110) + p0.setBirthRate(0.01) + p0.setLitterSize(2) + p0.setLitterSpread(0) + p0.factory.setLifespanBase(0.4) + p0.factory.setLifespanSpread(0.1) + p0.factory.setMassBase(1.0) + p0.factory.setMassSpread(0.0) + p0.factory.setTerminalVelocityBase(400.0) + p0.factory.setTerminalVelocitySpread(0.0) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.0) + p0.renderer.setCenterColor(color1) + p0.renderer.setEdgeColor(color2) + p0.renderer.setBirthRadius(0.6) + p0.renderer.setDeathRadius(0.6) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(amp) + p0.emitter.setAmplitudeSpread(0.0) + p0.emitter.setRadius(0.3) + f.addParticles(p0) + gravityForceGroup = ForceGroup.ForceGroup('gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -10.0), 1.0, 0) + force0.setActive(1) + gravityForceGroup.addForce(force0) + f.addForceGroup(gravityForceGroup) + f.setPos(x, y, z) + sfxName = random.choice(('phase_4/audio/sfx/firework_whistle_01.ogg', 'phase_4/audio/sfx/firework_whistle_02.ogg')) + sfx = loader.loadSfx(sfxName) + sfx.setVolume(0.4) + t = Sequence(Func(f.start, render, render), Func(sfx.play), LerpPosInterval(f, 2.0, Vec3(x, y, z + 20 * amp), blendType='easeInOut'), Func(p0.setBirthRate, 3), Wait(0.5), Func(f.cleanup), name=getNextSequenceName('shootFirework')) + t.start() + + +def shootPop(x, y, z, color1, color2, amp): + sfxName = random.choice(('phase_4/audio/sfx/firework_distance_01.ogg', 'phase_4/audio/sfx/firework_distance_02.ogg', 'phase_4/audio/sfx/firework_distance_03.ogg')) + sfx = loader.loadSfx(sfxName) + t = Sequence(Func(sfx.play), Wait(3), name=getNextSequenceName('shootFireworkRocket')) + t.start() + + +def shootFireworkCircle(x, y, z, color1, color2, amp): + return shootFireworkCircleGeneric(x, y, z, color1, color2, amp, 100) + + +def shootFireworkCircleLarge(x, y, z, color1, color2, amp): + return shootFireworkCircleGeneric(x, y, z, color1, color2, amp * 1.5, 200) + + +def shootFireworkCircleSmall(x, y, z, color1, color2, amp): + return shootFireworkCircleGeneric(x, y, z, color1, color2, amp * 0.5, 50) + + +def shootFireworkCircleGeneric(x, y, z, color1, color2, amp, poolSize): + f = ParticleEffect.ParticleEffect() + p0 = Particles.Particles('particles-1') + p0.setFactory('PointParticleFactory') + p0.setRenderer('SparkleParticleRenderer') + p0.setEmitter('SphereVolumeEmitter') + p0.setPoolSize(poolSize) + p0.setBirthRate(0.01) + p0.setLitterSize(poolSize) + p0.factory.setLifespanBase(2.0) + p0.factory.setLifespanSpread(0.5) + p0.factory.setTerminalVelocityBase(400.0) + p0.factory.setTerminalVelocitySpread(40.0) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.0) + p0.renderer.setCenterColor(color1) + p0.renderer.setEdgeColor(color1) + p0.renderer.setBirthRadius(0.4) + p0.renderer.setDeathRadius(0.6) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPSCALE) + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitudeSpread(0.1) + p0.emitter.setAmplitude(amp) + p0.emitter.setRadius(0.1) + f.addParticles(p0) + circleForceGroup = ForceGroup.ForceGroup('gravity') + force1 = LinearSinkForce(Point3(x, y, z - 100), LinearDistanceForce.FTONEOVERRSQUARED, 2.0, 0.3 * amp * 0.1, 1) + force1.setActive(1) + circleForceGroup.addForce(force1) + f.addForceGroup(circleForceGroup) + f.setPos(x, y, z) + sfxName = random.choice(('phase_4/audio/sfx/firework_explosion_01.ogg', 'phase_4/audio/sfx/firework_explosion_02.ogg', 'phase_4/audio/sfx/firework_explosion_03.ogg')) + sfx = loader.loadSfx(sfxName) + sfx.setVolume(0.7) + t = Sequence(Func(f.start, render, render), Func(sfx.play), Wait(0.5), Func(p0.setBirthRate, 3), Wait(0.5), Func(p0.renderer.setCenterColor, color2), Func(p0.renderer.setEdgeColor, color2), Wait(1.5), Func(f.cleanup), name=getNextSequenceName('shootFireworkCircle')) + t.start() + + +def shootFireworkCircleSprite(x, y, z, color, texture, amp): + f = ParticleEffect.ParticleEffect() + p0 = Particles.Particles('particles-1') + p0.setFactory('PointParticleFactory') + p0.setRenderer('SpriteParticleRenderer') + p0.setEmitter('SphereVolumeEmitter') + p0.setPoolSize(100) + p0.setBirthRate(0.01) + p0.setLitterSize(100) + p0.factory.setLifespanBase(2.0) + p0.factory.setLifespanSpread(0.5) + p0.factory.setTerminalVelocityBase(400.0) + p0.factory.setTerminalVelocitySpread(40.0) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.0) + p0.renderer.setFromNode(texture) + p0.renderer.setColor(color) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setInitialXScale(0.12) + p0.renderer.setFinalXScale(0.48) + p0.renderer.setInitialYScale(0.12) + p0.renderer.setFinalYScale(0.48) + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitudeSpread(0.1) + p0.emitter.setAmplitude(amp) + p0.emitter.setRadius(0.1) + f.addParticles(p0) + circleForceGroup = ForceGroup.ForceGroup('gravity') + force1 = LinearSinkForce(Point3(x, y, z - 100), LinearDistanceForce.FTONEOVERRSQUARED, 2.0, 0.3 * amp * 0.1, 1) + force1.setActive(1) + circleForceGroup.addForce(force1) + f.addForceGroup(circleForceGroup) + f.setPos(x, y, z) + sfxName = random.choice(('phase_4/audio/sfx/firework_explosion_01.ogg', 'phase_4/audio/sfx/firework_explosion_02.ogg', 'phase_4/audio/sfx/firework_explosion_03.ogg')) + sfx = loader.loadSfx(sfxName) + sfx.setVolume(0.7) + t = Sequence(Func(f.start, render, render), Func(sfx.play), Wait(0.5), Func(p0.setBirthRate, 3), Wait(2.0), Func(f.cleanup), name=getNextSequenceName('shootFireworkSprite')) + t.start() + + +style2shootFunc = {CIRCLE: shootFireworkCircle, + CIRCLELARGE: shootFireworkCircleLarge, + CIRCLESMALL: shootFireworkCircleSmall, + CIRCLESPRITE: shootFireworkCircleSprite, + ROCKET: shootFireworkRocket, + RING: shootFireworkRing, + POP: shootPop} diff --git a/toontown/effects/FlashEffect.py b/toontown/effects/FlashEffect.py new file mode 100755 index 00000000..6cdaf6b5 --- /dev/null +++ b/toontown/effects/FlashEffect.py @@ -0,0 +1,29 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from EffectController import EffectController + +class FlashEffect(NodePath, EffectController): + + def __init__(self): + NodePath.__init__(self, 'FlashEffect') + EffectController.__init__(self) + self.fadeTime = 0.15 + self.effectColor = Vec4(1, 1, 1, 1) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_particleCards') + self.effectModel = model.find('**/tt_t_efx_ext_particleWhiteGlow') + self.effectModel.setBillboardAxis(0) + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + fadeBlast = self.effectModel.colorScaleInterval(self.fadeTime, Vec4(0, 0, 0, 0), startColorScale=Vec4(self.effectColor), blendType='easeOut') + scaleBlast = self.effectModel.scaleInterval(self.fadeTime, 5, startScale=1.0, blendType='easeIn') + self.track = Sequence(Parallel(fadeBlast, scaleBlast), Func(self.cleanUpEffect)) + + def setEffectColor(self, color): + self.effectColor = color diff --git a/toontown/effects/Glow.py b/toontown/effects/Glow.py new file mode 100755 index 00000000..de540042 --- /dev/null +++ b/toontown/effects/Glow.py @@ -0,0 +1,39 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from EffectController import EffectController +from PooledEffect import PooledEffect + +class Glow(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + + def createTrack(self): + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_particleCards') + self.spark = model.find('**/tt_t_efx_ext_particleSparkle') + self.effectModel = self.attachNewNode('effectModelNode') + self.spark.reparentTo(self.effectModel) + self.effectColor = Vec4(1, 1, 1, 1) + self.effectModel.hide() + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + self.effectModel.hide() + self.effectModel.setColorScale(self.effectColor) + pulseIval = Sequence(Wait(0.1), Func(self.effectModel.setColorScale, self.effectColor), Wait(0.1), Func(self.effectModel.setColorScale, Vec4(1, 1, 1, 0.7))) + self.startEffect = Sequence(Func(self.effectModel.show), Func(pulseIval.loop)) + self.endEffect = Sequence(Func(pulseIval.finish), Func(self.cleanUpEffect)) + self.track = Sequence(self.startEffect, Wait(1.0), self.endEffect) + + def cleanUpEffect(self): + self.effectModel.hide() + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/GlowTrail.py b/toontown/effects/GlowTrail.py new file mode 100755 index 00000000..37e6f31d --- /dev/null +++ b/toontown/effects/GlowTrail.py @@ -0,0 +1,90 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from PooledEffect import PooledEffect +from EffectController import EffectController + +class GlowTrail(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_particleCards') + self.card = model.find('**/tt_t_efx_ext_particleWhiteGlow') + self.cardScale = 64.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.effectScale = 1.0 + self.lifespan = 0.5 + if not GlowTrail.particleDummy: + GlowTrail.particleDummy = render.attachNewNode(ModelNode('GlowTrailParticleDummy')) + GlowTrail.particleDummy.setDepthWrite(0) + GlowTrail.particleDummy.setLightOff() + GlowTrail.particleDummy.setFogOff() + self.f = ParticleEffect.ParticleEffect('GlowTrail') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-1') + self.p0.setFactory('PointParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('PointEmitter') + self.f.addParticles(self.p0) + self.p0.setPoolSize(64) + self.p0.setBirthRate(0.02) + self.p0.setLitterSize(1) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(0) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(self.lifespan) + self.p0.factory.setLifespanSpread(0.1) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, -2.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + + def createTrack(self): + self.startEffect = Sequence(Func(self.p0.setBirthRate, 0.015), Func(self.p0.clearToInitial), Func(self.f.start, self, self.particleDummy)) + self.endEffect = Sequence(Func(self.p0.setBirthRate, 100.0), Wait(self.lifespan + 0.1), Func(self.cleanUpEffect)) + self.track = Sequence(self.startEffect, Wait(1.0), self.endEffect) + + def setLifespan(self, duration): + self.lifespan = duration + self.p0.setPoolSize(int(128 * self.lifespan)) + self.p0.factory.setLifespanBase(self.lifespan) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(0.15 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(0.1 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(0.15 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(0.4 * self.cardScale * scale) + self.p0.emitter.setAmplitude(10.0 * scale) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/IceCream.py b/toontown/effects/IceCream.py new file mode 100755 index 00000000..dc1320a6 --- /dev/null +++ b/toontown/effects/IceCream.py @@ -0,0 +1,91 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from EffectController import EffectController +from PooledEffect import PooledEffect + +class IceCream(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + self.card = loader.loadModel('phase_4/models/props/icecream') + self.cardScale = 18.0 + self.setDepthWrite(0) + self.setColorScaleOff() + self.setLightOff() + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.f = ParticleEffect.ParticleEffect('IceCream') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-0') + self.p0.setFactory('ZSpinParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('SphereSurfaceEmitter') + self.f.addParticles(self.p0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -0.25), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + self.p0.setPoolSize(128) + self.p0.setBirthRate(0.04) + self.p0.setLitterSize(64) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(1) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(3.25) + self.p0.factory.setLifespanSpread(1.0) + self.p0.factory.setMassBase(0.5) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.factory.setInitialAngle(0.0) + self.p0.factory.setInitialAngleSpread(360.0) + self.p0.factory.enableAngularVelocity(1) + self.p0.factory.setAngularVelocity(100.0) + self.p0.factory.setAngularVelocitySpread(0.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(2) + self.p0.renderer.setYScaleFlag(2) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, 0.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + self.setEffectColor(self.effectColor) + + def createTrack(self): + self.track = Sequence(Func(self.p0.setBirthRate, 0.04), Func(self.p0.clearToInitial), Func(self.f.start, self, self), Wait(0.2), Func(self.p0.setBirthRate, 100.0), Wait(4.0), Func(self.cleanUpEffect)) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(0.5 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(1.0 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(0.5 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(1.0 * self.cardScale * scale) + self.p0.emitter.setAmplitude(20.0 * scale) + self.p0.emitter.setAmplitudeSpread(2.0 * scale) + self.p0.emitter.setRadius(150.0 * scale) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/NoiseSparkles.py b/toontown/effects/NoiseSparkles.py new file mode 100755 index 00000000..b81ab8a1 --- /dev/null +++ b/toontown/effects/NoiseSparkles.py @@ -0,0 +1,98 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from EffectController import EffectController +from PooledEffect import PooledEffect +import random + +class NoiseSparkles(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.card = model.find('**/tt_t_efx_ext_particleSpark_soft') + self.cardScale = 16.0 + self.setDepthWrite(0) + self.setColorScaleOff() + self.setLightOff() + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.f = ParticleEffect.ParticleEffect('NoiseSparkles') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-2') + self.p0.setFactory('PointParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('SphereSurfaceEmitter') + self.f.addParticles(self.p0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, 10.0), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + f1 = ForceGroup.ForceGroup('Noise') + self.noiseForce = LinearNoiseForce(10.0, 0.0) + self.noiseForce.setVectorMasks(1, 1, 1) + self.noiseForce.setActive(0) + f1.addForce(self.noiseForce) + self.f.addForceGroup(f1) + self.p0.setPoolSize(150) + self.p0.setBirthRate(0.02) + self.p0.setLitterSize(50) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(1) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(2.0) + self.p0.factory.setLifespanSpread(0.5) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, 0.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + self.setEffectColor(self.effectColor) + + def createTrack(self): + self.track = Sequence(Func(self.p0.setBirthRate, 0.02), Func(self.p0.clearToInitial), Func(self.f.start, self, self), Func(self.noiseForce.setActive, 0), Wait(0.25), Func(self.noiseForce.setActive, 1), Wait(0.5), Func(self.p0.setBirthRate, 100.0), Wait(3.0), Func(self.cleanUpEffect)) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(1.0 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(0.5 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(1.0 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(0.5 * self.cardScale * scale) + self.p0.emitter.setAmplitude(25.0 * scale) + self.p0.emitter.setRadius(250.0 * scale) + + def setRadius(self, radius): + self.p0.emitter.setRadius(radius) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/PeonyEffect.py b/toontown/effects/PeonyEffect.py new file mode 100755 index 00000000..84584027 --- /dev/null +++ b/toontown/effects/PeonyEffect.py @@ -0,0 +1,47 @@ +from panda3d.core import * +from direct.showbase.DirectObject import * +from direct.interval.IntervalGlobal import * +from PooledEffect import PooledEffect +from EffectController import EffectController + +class PeonyEffect(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + self.fadeTime = 1.25 + self.startDelay = 0.0 + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.effectModel = model.find('**/tt_t_efx_ext_fireworkStars_01') + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + self.effectModel.setColorScale(0, 0, 0, 0) + self.effectModel.setScale(700 * self.effectScale) + fadeIn = self.effectModel.colorScaleInterval(0.2, Vec4(self.effectColor), startColorScale=Vec4(0, 0, 0, 0), blendType='easeIn') + fadeBlast = self.effectModel.colorScaleInterval(self.fadeTime, Vec4(0, 0, 0, 0), startColorScale=Vec4(self.effectColor), blendType='easeIn') + scaleBlast = self.effectModel.scaleInterval(self.fadeTime, 750 * self.effectScale, startScale=700 * self.effectScale, blendType='easeOut') + self.track = Sequence(Wait(self.startDelay), fadeIn, Parallel(fadeBlast, scaleBlast), Func(self.cleanUpEffect)) + + def setEffectColor(self, color): + self.effectColor = color + + def setEffectScale(self, scale): + self.effectScale = scale + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/PolyTrail.py b/toontown/effects/PolyTrail.py new file mode 100755 index 00000000..2e158ed9 --- /dev/null +++ b/toontown/effects/PolyTrail.py @@ -0,0 +1,165 @@ +from panda3d.core import * +from direct.motiontrail.MotionTrail import * +import random + +class PolyTrail(NodePath): + + def __init__(self, root_node_path = None, vertex_list = None, color_list = None, time_window = 0.25): + NodePath.__init__(self, 'PolyTrail') + self.time_window = time_window + self.root_node_path = root_node_path + if not self.root_node_path: + self.root_node_path = render + self.vertex_list = vertex_list + if not self.vertex_list: + self.vertex_list = [Vec4(0.0, 0.4, 0.0, 1.0), Vec4(0.0, 2.0, 0.0, 1.0)] + self.color_list = color_list + if not self.color_list: + self.color_list = [] + for i in self.vertex_list: + self.color_list.append(Vec4(0.1, 0.2, 0.4, 1.0)) + + self.motion_trail = None + self.motion_trail_vertex = None + self.addMotionTrail() + self.setVertexColors(self.color_list) + self.setTimeWindow(self.time_window) + self.motion_trail.attach_motion_trail() + return + + def destroy(self): + self.removeMotionTrail() + self.removeNode() + self.root_node_path = None + self.motion_trail = None + self.vertex_list = None + self.motion_trail_vertex = None + return + + def beginTrail(self): + if self.motion_trail: + self.motion_trail.begin_motion_trail() + + def endTrail(self): + if self.motion_trail: + self.motion_trail.end_motion_trail() + self.motion_trail.time_window = self.time_window + + def removeMotionTrail(self): + self.endTrail() + if self.motion_trail: + self.motion_trail.unregister_motion_trail() + self.motion_trail.delete() + self.motion_trail = None + if self.motion_trail_vertex: + self.motion_trail_vertex = None + return + + def addMotionTrail(self): + if not self.motion_trail: + self.motion_trail = MotionTrail('motion_trail', self) + self.motion_trail.root_node_path = self.root_node_path + if False: + axis = loader.loadModel('models/misc/xyzAxis') + axis.reparentTo(self) + + def test_vertex_function(motion_trail_vertex, vertex_id, context): + return self.vertex_list[vertex_id] + + index = 0 + total_test_vertices = len(self.vertex_list) + while index < total_test_vertices: + self.motion_trail_vertex = self.motion_trail.add_vertex(index, test_vertex_function, None) + if True: + if index == 0: + self.motion_trail_vertex.start_color = Vec4(0.0, 0.25, 0.0, 1.0) + self.motion_trail_vertex.end_color = Vec4(0.0, 0.0, 0.0, 1.0) + if index == 1: + self.motion_trail_vertex.start_color = Vec4(0.25, 0.0, 0.0, 1.0) + self.motion_trail_vertex.end_color = Vec4(0.0, 0.0, 0.0, 1.0) + if index == 2: + self.motion_trail_vertex.start_color = Vec4(0.0, 0.0, 1.0, 1.0) + self.motion_trail_vertex.end_color = Vec4(0.0, 0.0, 0.0, 1.0) + if index == 3: + self.motion_trail_vertex.start_color = Vec4(0.0, 1.0, 1.0, 1.0) + self.motion_trail_vertex.end_color = Vec4(0.0, 0.0, 0.0, 1.0) + if index == 4: + self.motion_trail_vertex.start_color = Vec4(1.0, 1.0, 0.0, 1.0) + self.motion_trail_vertex.end_color = Vec4(0.0, 0.0, 0.0, 1.0) + index += 1 + + self.motion_trail.update_vertices() + self.motion_trail.calculate_relative_matrix = True + self.motion_trail.time_window = self.time_window + self.motion_trail.continuous_motion_trail = False + self.motion_trail.end_motion_trail() + self.motion_trail.register_motion_trail() + if False: + axis = Vec3(0.0, 0.0, 1.0) + time = 0.0 + angle = (1.0 - time) * 90.0 + matrix = Mat4.rotateMat(angle, axis) + self.motion_trail.update_motion_trail(time, matrix) + time = 0.2 + angle = (1.0 - time) * 90.0 + matrix = Mat4.rotateMat(angle, axis) + self.motion_trail.update_motion_trail(time, matrix) + time = 0.4 + angle = (1.0 - time) * 90.0 + matrix = Mat4.rotateMat(angle, axis) + self.motion_trail.update_motion_trail(time, matrix) + time = 0.6 + angle = (1.0 - time) * 90.0 + matrix = Mat4.rotateMat(angle, axis) + self.motion_trail.update_motion_trail(time, matrix) + time = 0.8 + angle = (1.0 - time) * 90.0 + matrix = Mat4.rotateMat(angle, axis) + self.motion_trail.update_motion_trail(time, matrix) + time = 1.0 + angle = (1.0 - time) * 90.0 + matrix = Mat4.rotateMat(angle, axis) + self.motion_trail.update_motion_trail(time, matrix) + return + + def setVertexColors(self, color_list): + if self.motion_trail: + black = Vec4(0.0, 0.0, 0.0, 1.0) + scale_array = [0.25, + 0.4, + 0.7, + 1.0] + total_scales = len(scale_array) + for index in xrange(len(color_list)): + color = color_list[index] + if index < total_scales: + scale = scale_array[index] * 0.75 + else: + scale = 1.0 + scaled_color = Vec4(color[0] * scale, color[1] * scale, color[2] * scale, 1.0) + self.motion_trail.set_vertex_color(index, scaled_color, black) + + def setUnmodifiedVertexColors(self, color_list): + if self.motion_trail: + for index in xrange(len(color_list)): + color = color_list[index] + self.motion_trail.set_vertex_color(index, color, color) + + def setTimeWindow(self, time_window): + if self.motion_trail: + self.motion_trail.time_window = time_window + + def setUseNurbs(self, val): + self.motion_trail.use_nurbs = val + + def setTexture(self, texture): + if self.motion_trail: + self.motion_trail.set_texture(texture) + + def setBlendModeOn(self): + if self.motion_trail: + self.motion_trail.geom_node_path.node().setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + + def setBlendModeOff(self): + if self.motion_trail: + self.motion_trail.geom_node_path.node().setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MNone)) diff --git a/toontown/effects/PooledEffect.py b/toontown/effects/PooledEffect.py new file mode 100755 index 00000000..cd4d83e2 --- /dev/null +++ b/toontown/effects/PooledEffect.py @@ -0,0 +1,40 @@ +from panda3d.core import * +from direct.showbase import Pool +from direct.showbase.DirectObject import DirectObject +import re + +class PooledEffect(DirectObject, NodePath): + pool = None + poolLimit = 124 + + @classmethod + def getEffect(cls, context = ''): + if cls.pool is None: + cls.pool = Pool.Pool() + if cls.pool.hasFree(): + return cls.pool.checkout() + else: + free, used = cls.pool.getNumItems() + if free + used < cls.poolLimit: + cls.pool.add(cls()) + return cls.pool.checkout() + else: + return + return + + @classmethod + def cleanup(cls): + if cls.pool: + cls.pool.cleanup(cls.destroy) + cls.pool = None + return + + def __init__(self): + NodePath.__init__(self, self.__class__.__name__) + self.accept('clientLogout', self.__class__.cleanup) + + def destroy(self, item = None): + if item: + self.pool.remove(item) + self.ignore('clientLogout') + self.removeNode() diff --git a/toontown/effects/RayBurst.py b/toontown/effects/RayBurst.py new file mode 100755 index 00000000..509c5744 --- /dev/null +++ b/toontown/effects/RayBurst.py @@ -0,0 +1,34 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from EffectController import EffectController + +class RayBurst(NodePath, EffectController): + + def __init__(self): + NodePath.__init__(self, 'RayBurst') + EffectController.__init__(self) + self.fadeTime = 0.25 + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.effectModel = model.find('**/tt_t_efx_ext_fireworkRays') + self.effectModel.setBillboardPointWorld() + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + self.effectModel.setColorScale(1, 1, 1, 0) + fadeBlast = self.effectModel.colorScaleInterval(self.fadeTime, Vec4(1, 1, 1, 0), startColorScale=Vec4(self.effectColor), blendType='easeIn') + scaleBlast = self.effectModel.scaleInterval(self.fadeTime, 700 * self.effectScale, startScale=100 * self.effectScale, blendType='easeOut') + self.track = Sequence(Parallel(fadeBlast, scaleBlast), Func(self.cleanUpEffect)) + + def setEffectColor(self, color): + self.effectColor = color + + def setEffectScale(self, scale): + self.effectScale = scale diff --git a/toontown/effects/RingEffect.py b/toontown/effects/RingEffect.py new file mode 100755 index 00000000..4259d3ba --- /dev/null +++ b/toontown/effects/RingEffect.py @@ -0,0 +1,109 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from EffectController import EffectController +from PooledEffect import PooledEffect +import random + +class RingEffect(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.card = model.find('**/tt_t_efx_ext_particleSpark_soft') + self.cardScale = 16.0 + self.effectModel = model.find('**/tt_t_efx_ext_particleStars') + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.f = ParticleEffect.ParticleEffect('RingEffect') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-2') + self.p0.setFactory('PointParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('RingEmitter') + self.f.addParticles(self.p0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -15.0), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + f1 = ForceGroup.ForceGroup('Noise') + force1 = LinearNoiseForce(2.5, 0.0) + force1.setVectorMasks(1, 1, 1) + force1.setActive(1) + f1.addForce(force1) + self.f.addForceGroup(f1) + self.p0.setPoolSize(16) + self.p0.setBirthRate(0.1) + self.p0.setLitterSize(16) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(1) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(1.0) + self.p0.factory.setLifespanSpread(0.2) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAINOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(0) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, 0.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.p0.emitter.setUniformEmission(16) + self.setEffectScale(self.effectScale) + self.setEffectColor(self.effectColor) + + def createTrack(self): + self.f.setP(random.randint(50, 100)) + self.effectModel.setR(random.randint(0, 90)) + self.effectModel.setPos(random.randint(-20, 20), random.randint(-20, 20), random.randint(-20, 20)) + fadeBlast = self.effectModel.colorScaleInterval(1.0, Vec4(0, 0, 0, 0), startColorScale=Vec4(self.effectColor), blendType='easeIn') + scaleBlast = self.effectModel.scaleInterval(1.0, 75 * self.effectScale, startScale=50 * self.effectScale, blendType='easeOut') + self.track = Sequence(Func(self.p0.setBirthRate, 0.15), Func(self.p0.clearToInitial), Func(self.f.start, self, self), Parallel(fadeBlast, scaleBlast), Func(self.p0.setBirthRate, 100.0), Wait(3.0), Func(self.cleanUpEffect)) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(1.4 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(1.2 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(1.4 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(1.2 * self.cardScale * scale) + self.p0.emitter.setAmplitude(75.0 * scale) + self.p0.emitter.setRadius(200.0 * scale) + + def setRadius(self, radius): + self.p0.emitter.setRadius(radius) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/Ripples.py b/toontown/effects/Ripples.py new file mode 100755 index 00000000..7bb9e56f --- /dev/null +++ b/toontown/effects/Ripples.py @@ -0,0 +1,45 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import globalPropPool + +class Ripples(NodePath): + rippleCount = 0 + + def __init__(self, parent = hidden): + NodePath.__init__(self) + self.assign(globalPropPool.getProp('ripples')) + self.reparentTo(parent) + self.getChild(0).setZ(0.1) + self.seqNode = self.find('**/+SequenceNode').node() + self.seqNode.setPlayRate(0) + self.track = None + self.trackId = Ripples.rippleCount + Ripples.rippleCount += 1 + self.setBin('fixed', 100, 1) + self.hide() + return + + def createTrack(self, rate = 1): + tflipDuration = self.seqNode.getNumChildren() / (float(rate) * 24) + self.track = Sequence(Func(self.show), Func(self.seqNode.play, 0, self.seqNode.getNumFrames() - 1), Func(self.seqNode.setPlayRate, rate), Wait(tflipDuration), Func(self.seqNode.setPlayRate, 0), Func(self.hide), name='ripples-track-%d' % self.trackId) + + def play(self, rate = 1): + self.stop() + self.createTrack(rate) + self.track.start() + + def loop(self, rate = 1): + self.stop() + self.createTrack(rate) + self.track.loop() + + def stop(self): + if self.track: + self.track.finish() + + def destroy(self): + self.stop() + self.track = None + del self.seqNode + self.removeNode() + return diff --git a/toontown/effects/RocketExplosion.py b/toontown/effects/RocketExplosion.py new file mode 100755 index 00000000..15602e8b --- /dev/null +++ b/toontown/effects/RocketExplosion.py @@ -0,0 +1,58 @@ +from panda3d.core import * +from direct.particles import ParticleEffect +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from toontown.battle import BattleParticles + +class RocketExplosion(NodePath): + + def __init__(self, parent, smokeParent): + NodePath.__init__(self) + notify = DirectNotifyGlobal.directNotify.newCategory('RocketExplosionParticles') + self.effectNode = parent.attachNewNode('RocketExplosion') + self.effectNode.setBin('fixed', 1) + self.effectNode.setDepthWrite(1) + self.smokeEffectNode = smokeParent.attachNewNode('RocketSmoke') + self.smokeEffectNode.setBin('fixed', 1) + self.smokeEffectNode.setDepthWrite(0) + self.effect = BattleParticles.loadParticleFile('tt_p_efx_rocketLaunchFire.ptf') + self.smokeEffect = BattleParticles.loadParticleFile('tt_p_efx_rocketLaunchSmoke.ptf') + ren = self.effect.getParticlesNamed('particles-1').getRenderer() + ren.setTextureFromNode('phase_4/models/props/tt_m_efx_fireball', '**/*') + ren = self.smokeEffect.getParticlesNamed('particles-1').getRenderer() + ren.setTextureFromNode('phase_4/models/props/tt_m_efx_smoke', '**/*') + self.endSeq = None + self.cleanupCompleted = 0 + return + + def start(self): + self.effect.start(parent=self.effectNode) + self.smokeEffect.start(parent=self.smokeEffectNode) + + def stop(self): + try: + self.effect.disable() + self.smokeEffect.disable() + except AttributeError: + pass + + def end(self): + self.endSeq = Sequence(LerpColorScaleInterval(self.smokeEffectNode, 2.0, Vec4(1, 1, 1, 0)), Func(self.destroy)) + self.endSeq.start() + + def destroy(self): + if self.endSeq: + self.endSeq.pause() + self.endSeq = None + self.stop() + if not self.cleanupCompleted: + self.effect.cleanup() + self.smokeEffect.cleanup() + self.effectNode.removeNode() + self.smokeEffectNode.removeNode() + del self.effect + del self.smokeEffect + del self.effectNode + del self.smokeEffectNode + self.cleanupCompleted = 1 + return diff --git a/toontown/effects/ScavengerHuntEffects.py b/toontown/effects/ScavengerHuntEffects.py new file mode 100755 index 00000000..79be620e --- /dev/null +++ b/toontown/effects/ScavengerHuntEffects.py @@ -0,0 +1,94 @@ +from direct.interval.IntervalGlobal import * +from direct.gui.DirectFrame import DirectFrame +from direct.gui.DirectLabel import DirectLabel +from toontown.toonbase import ToontownGlobals, TTLocalizer + +class ScavengerHuntEffect: + images = None + + def __init__(self, beanAmount): + if not ScavengerHuntEffect.images: + ScavengerHuntEffect.images = loader.loadModel('phase_4/models/props/tot_jar') + + self.npRoot = DirectFrame(parent=aspect2d, relief=None, scale=0.75, pos=(0, 0, 0.6)) + + if beanAmount > 0: + self.npRoot.setColorScale(VBase4(1, 1, 1, 0)) + self.jar = DirectFrame(parent=self.npRoot, relief=None, image=ScavengerHuntEffect.images.find('**/tot_jar')) + self.jar.hide() + self.eventImage = NodePath('EventImage') + self.eventImage.reparentTo(self.npRoot) + self.countLabel = DirectLabel(parent=self.jar, relief=None, text='+0', text_pos=(0.02, -0.2), text_scale=0.25, text_fg=(0.95, 0.0, 0, 1), text_font=ToontownGlobals.getSignFont()) + + def countUp(t, startVal, endVal): + beanCountStr = startVal + t * (endVal - startVal) + self.countLabel['text'] = '+' + `(int(beanCountStr))` + + def setCountColor(color): + self.countLabel['text_fg'] = color + + self.track = Sequence(LerpColorScaleInterval(self.npRoot, 1, colorScale=VBase4(1, 1, 1, 1), startColorScale=VBase4(1, 1, 1, 0)), Wait(1), Func(self.jar.show), LerpColorScaleInterval(self.eventImage, 1, colorScale=VBase4(1, 1, 1, 0), startColorScale=VBase4(1, 1, 1, 1)), Parallel(LerpScaleInterval(self.npRoot, 1, scale=0.5, startScale=0.75), LerpPosInterval(self.npRoot, 1, pos=VBase3(-0.9, 0, -0.83))), LerpFunc(countUp, duration=2, extraArgs=[0, beanAmount]), Func(setCountColor, VBase4(0.95, 0.95, 0, 1)), Wait(3), Func(self.destroy)) + else: + self.npRoot.setColorScale(VBase4(1, 1, 1, 0)) + self.attemptFailedMsg() + self.track = Sequence(LerpColorScaleInterval(self.npRoot, 1, colorScale=VBase4(1, 1, 1, 1), startColorScale=VBase4(1, 1, 1, 0)), Wait(5), LerpColorScaleInterval(self.npRoot, 1, colorScale=VBase4(1, 1, 1, 0), startColorScale=VBase4(1, 1, 1, 1)), Func(self.destroy)) + + def play(self): + if self.npRoot: + self.track.start() + + def stop(self): + if self.track != None and self.track.isPlaying(): + self.track.finish() + + def destroy(self): + self.stop() + self.track = None + + if hasattr(self, 'eventImage') and self.eventImage: + self.eventImage.detachNode() + del self.eventImage + if hasattr(self, 'countLabel') and self.countLabel: + self.countLabel.destroy() + del self.countLabel + if hasattr(self, 'jar') and self.jar: + self.jar.destroy() + del self.jar + if hasattr(self, 'npRoot') and self.npRoot: + self.npRoot.destroy() + del self.npRoot + +class TrickOrTreatTargetEffect(ScavengerHuntEffect): + + def __init__(self, beanAmount): + ScavengerHuntEffect.__init__(self, beanAmount) + + if beanAmount > 0: + self.pumpkin = DirectFrame(parent=self.eventImage, relief=None, image=ScavengerHuntEffect.images.find('**/tot_pumpkin_tall')) + + def attemptFailedMsg(self): + pLabel = DirectLabel(parent=self.npRoot, relief=None, pos=(0.0, 0.0, -0.15), text=TTLocalizer.TrickOrTreatMsg, text_fg=(0.95, 0.5, 0.0, 1.0), text_scale=0.12, text_font=ToontownGlobals.getSignFont()) + + def destroy(self): + if hasattr(self, 'pumpkin') and self.pumpkin: + self.pumpkin.destroy() + + ScavengerHuntEffect.destroy(self) + +class WinterCarolingEffect(ScavengerHuntEffect): + + def __init__(self, beanAmount): + ScavengerHuntEffect.__init__(self, beanAmount) + + if beanAmount > 0: + sm = loader.loadModel('phase_5.5/models/estate/tt_m_prp_ext_snowman_icon') + self.snowman = DirectFrame(parent=self.eventImage, relief=None, image=sm, scale=20.0) + + def attemptFailedMsg(self): + pLabel = DirectLabel(parent=self.npRoot, relief=None, pos=(0.0, 0.0, -0.15), text=TTLocalizer.WinterCarolingMsg, text_fg=(0.9, 0.9, 1.0, 1.0), text_scale=0.12, text_font=ToontownGlobals.getSignFont()) + + def destroy(self): + if hasattr(self, 'snowman') and self.snowman: + self.snowman.destroy() + + ScavengerHuntEffect.destroy(self) diff --git a/toontown/effects/SimpleSparkles.py b/toontown/effects/SimpleSparkles.py new file mode 100755 index 00000000..3945e686 --- /dev/null +++ b/toontown/effects/SimpleSparkles.py @@ -0,0 +1,98 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from EffectController import EffectController +from PooledEffect import PooledEffect +import random + +class SimpleSparkles(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.card = model.find('**/tt_t_efx_ext_particleStars') + self.cardScale = 64.0 + self.setDepthWrite(0) + self.setColorScaleOff() + self.setLightOff() + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.f = ParticleEffect.ParticleEffect('SimpleSparkles') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-2') + self.p0.setFactory('ZSpinParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('SphereSurfaceEmitter') + self.f.addParticles(self.p0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -10.0), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + self.p0.setPoolSize(32) + self.p0.setBirthRate(0.02) + self.p0.setLitterSize(4) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(1) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(2.0) + self.p0.factory.setLifespanSpread(0.5) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.factory.setInitialAngle(0.0) + self.p0.factory.setInitialAngleSpread(180.0) + self.p0.factory.enableAngularVelocity(1) + self.p0.factory.setAngularVelocity(0.0) + self.p0.factory.setAngularVelocitySpread(10.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitude(3.0) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, 0.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + self.setEffectColor(self.effectColor) + + def createTrack(self): + self.track = Sequence(Func(self.p0.setBirthRate, 0.03), Func(self.p0.clearToInitial), Func(self.f.start, self, self), Wait(1.0), Func(self.p0.setBirthRate, 100.0), Wait(2.5), Func(self.cleanUpEffect)) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(0.8 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(0.4 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(0.8 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(0.4 * self.cardScale * scale) + self.p0.emitter.setAmplitude(30.0 * scale) + self.p0.emitter.setRadius(75.0 * scale) + + def setRadius(self, radius): + self.p0.emitter.setRadius(radius) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/SkullBurst.py b/toontown/effects/SkullBurst.py new file mode 100755 index 00000000..0a143f0d --- /dev/null +++ b/toontown/effects/SkullBurst.py @@ -0,0 +1,47 @@ +from panda3d.core import * +from direct.showbase.DirectObject import * +from direct.interval.IntervalGlobal import * +from PooledEffect import PooledEffect +from EffectController import EffectController + +class SkullBurst(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + self.fadeTime = 1.5 + self.startDelay = 0.0 + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.effectModel = model.find('**/tt_t_efx_ext_skull') + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + self.effectModel.setColorScale(0, 0, 0, 0) + self.effectModel.setScale(700 * self.effectScale) + fadeIn = self.effectModel.colorScaleInterval(0.2, Vec4(self.effectColor), startColorScale=Vec4(0, 0, 0, 0), blendType='easeIn') + fadeBlast = self.effectModel.colorScaleInterval(self.fadeTime, Vec4(0, 0, 0, 0), startColorScale=Vec4(self.effectColor), blendType='easeIn') + scaleBlast = self.effectModel.scaleInterval(self.fadeTime, 850 * self.effectScale, startScale=750 * self.effectScale, blendType='easeOut') + self.track = Sequence(Wait(self.startDelay), fadeIn, Parallel(fadeBlast, scaleBlast), Func(self.cleanUpEffect)) + + def setEffectColor(self, color): + self.effectColor = color + + def setEffectScale(self, scale): + self.effectScale = scale + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/SkullFlash.py b/toontown/effects/SkullFlash.py new file mode 100755 index 00000000..26637191 --- /dev/null +++ b/toontown/effects/SkullFlash.py @@ -0,0 +1,38 @@ +from panda3d.core import * +from direct.showbase.DirectObject import * +from direct.interval.IntervalGlobal import * +from PooledEffect import PooledEffect +from EffectController import EffectController + +class SkullFlash(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + self.fadeTime = 0.15 + self.startDelay = 0.0 + self.effectColor = Vec4(1, 1, 1, 1) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.effectModel = model.find('**/tt_t_efx_ext_skullGlow') + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + self.effectModel.setColorScale(0, 0, 0, 0) + fadeBlast = self.effectModel.colorScaleInterval(self.fadeTime, Vec4(0, 0, 0, 0), startColorScale=Vec4(self.effectColor), blendType='easeOut') + scaleBlast = self.effectModel.scaleInterval(self.fadeTime, 2.0, startScale=1.0, blendType='easeOut') + self.track = Sequence(Wait(self.startDelay), Parallel(fadeBlast, scaleBlast), Func(self.cleanUpEffect)) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/Sparks.py b/toontown/effects/Sparks.py new file mode 100755 index 00000000..e3d607d4 --- /dev/null +++ b/toontown/effects/Sparks.py @@ -0,0 +1,33 @@ +from panda3d.core import * +from direct.particles import ParticleEffect +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattleParticles + +class Sparks(NodePath): + + def __init__(self, parent, renderParent): + NodePath.__init__(self) + notify = DirectNotifyGlobal.directNotify.newCategory('SparkParticles') + self.renderParent = renderParent.attachNewNode('sparkRenderParent') + self.renderParent.setBin('fixed', 0) + self.renderParent.setDepthWrite(0) + self.assign(parent.attachNewNode('sparks')) + self.effect = BattleParticles.loadParticleFile('sparks.ptf') + ren = self.effect.getParticlesNamed('particles-1').getRenderer() + ren.setTextureFromNode('phase_6/models/karting/particleSpark', '**/*') + + def start(self): + self.effect.start(self, self.renderParent) + + def stop(self): + try: + self.effect.disable() + except AttributeError: + pass + + def destroy(self): + self.stop() + self.effect.cleanup() + self.renderParent.removeNode() + del self.effect + del self.renderParent diff --git a/toontown/effects/SparksTrail.py b/toontown/effects/SparksTrail.py new file mode 100755 index 00000000..6e48c125 --- /dev/null +++ b/toontown/effects/SparksTrail.py @@ -0,0 +1,90 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from PooledEffect import PooledEffect +from EffectController import EffectController + +class SparksTrail(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_particleCards') + self.card = model.find('**/tt_t_efx_ext_particleStars') + self.cardScale = 64.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.effectScale = 1.0 + self.lifespan = 1.0 + if not SparksTrail.particleDummy: + SparksTrail.particleDummy = render.attachNewNode(ModelNode('SparksTrailParticleDummy')) + SparksTrail.particleDummy.setDepthWrite(0) + SparksTrail.particleDummy.setLightOff() + SparksTrail.particleDummy.setFogOff() + self.f = ParticleEffect.ParticleEffect('SparksTrail') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-1') + self.p0.setFactory('ZSpinParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('PointEmitter') + self.f.addParticles(self.p0) + self.p0.setPoolSize(64) + self.p0.setBirthRate(0.02) + self.p0.setLitterSize(1) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(0) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(0.5) + self.p0.factory.setLifespanSpread(0.1) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.factory.setInitialAngle(0.0) + self.p0.factory.setInitialAngleSpread(90.0) + self.p0.factory.enableAngularVelocity(1) + self.p0.factory.setAngularVelocity(0.0) + self.p0.factory.setAngularVelocitySpread(25.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, -2.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + + def createTrack(self): + self.startEffect = Sequence(Func(self.p0.setBirthRate, 0.01), Func(self.p0.clearToInitial), Func(self.f.start, self, self.particleDummy)) + self.endEffect = Sequence(Func(self.p0.setBirthRate, 100.0), Wait(1.0), Func(self.cleanUpEffect)) + self.track = Sequence(self.startEffect, Wait(1.0), self.endEffect) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(0.1 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(0.2 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(0.1 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(0.2 * self.cardScale * scale) + self.p0.emitter.setAmplitude(20.0 * scale) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/SparksTrailLong.py b/toontown/effects/SparksTrailLong.py new file mode 100755 index 00000000..63c01067 --- /dev/null +++ b/toontown/effects/SparksTrailLong.py @@ -0,0 +1,98 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from PooledEffect import PooledEffect +from EffectController import EffectController + +class SparksTrailLong(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_particleCards') + self.card = model.find('**/tt_t_efx_ext_particleStars') + self.cardScale = 64.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.effectScale = 1.0 + self.lifespan = 2.0 + if not SparksTrailLong.particleDummy: + SparksTrailLong.particleDummy = render.attachNewNode(ModelNode('SparksTrailLongParticleDummy')) + SparksTrailLong.particleDummy.setDepthWrite(0) + self.f = ParticleEffect.ParticleEffect('SparksTrailLong') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-1') + self.p0.setFactory('ZSpinParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('PointEmitter') + self.f.addParticles(self.p0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -2.0), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + self.p0.setPoolSize(128) + self.p0.setBirthRate(0.02) + self.p0.setLitterSize(1) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(0) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(self.lifespan) + self.p0.factory.setLifespanSpread(0.1) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.factory.setInitialAngle(0.0) + self.p0.factory.setInitialAngleSpread(90.0) + self.p0.factory.enableAngularVelocity(1) + self.p0.factory.setAngularVelocity(0.0) + self.p0.factory.setAngularVelocitySpread(25.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, -2.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + + def createTrack(self): + self.startEffect = Sequence(Func(self.p0.setBirthRate, 0.015), Func(self.p0.clearToInitial), Func(self.f.start, self, self.particleDummy)) + self.endEffect = Sequence(Func(self.p0.setBirthRate, 100.0), Wait(self.lifespan + 0.1), Func(self.cleanUpEffect)) + self.track = Sequence(self.startEffect, Wait(1.0), self.endEffect) + + def setLifespan(self, duration): + self.lifespan = duration + self.p0.factory.setLifespanBase(duration) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(0.05 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(0.5 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(0.05 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(0.5 * self.cardScale * scale) + self.p0.emitter.setAmplitude(30.0 * scale) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/Splash.py b/toontown/effects/Splash.py new file mode 100755 index 00000000..d69b6eb2 --- /dev/null +++ b/toontown/effects/Splash.py @@ -0,0 +1,69 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from Ripples import * +from toontown.battle.BattleProps import globalPropPool +from toontown.battle import BattleParticles + +class Splash(NodePath): + splashCount = 0 + + def __init__(self, parent = hidden, wantParticles = 1): + NodePath.__init__(self, parent) + self.assign(parent.attachNewNode('splash')) + self.splashdown = globalPropPool.getProp('splashdown') + self.splashdown.reparentTo(self) + self.splashdown.setZ(-0.01) + self.splashdown.setScale(0.4) + ta = TransparencyAttrib.make(TransparencyAttrib.MBinary) + self.splashdown.node().setAttrib(ta, 1) + self.splashdown.setBin('fixed', 130, 1) + self.ripples = Ripples(self) + self.ripples.setBin('fixed', 120, 1) + self.wantParticles = 1 + if self.wantParticles: + self.pSystem = BattleParticles.createParticleEffect('SplashLines') + self.pSystem.setScale(0.4) + self.pSystem.setBin('fixed', 150, 1) + self.particles = self.pSystem.particlesDict.get('particles-1') + self.track = None + self.trackId = Splash.splashCount + Splash.splashCount += 1 + self.setBin('fixed', 100, 1) + self.hide() + return + + def createTrack(self, rate = 1): + self.ripples.createTrack(rate) + self.splashdown.setPlayRate(rate, 'splashdown') + animDuration = self.splashdown.getDuration('splashdown') * 0.65 + rippleSequence = Sequence(Func(self.splashdown.show), Func(self.splashdown.play, 'splashdown'), Wait(animDuration), Func(self.splashdown.hide)) + if self.wantParticles: + particleSequence = Sequence(Func(self.pSystem.show), Func(self.particles.induceLabor), Func(self.pSystem.start, self), Wait(2.2), Func(self.pSystem.hide), Func(self.pSystem.disable)) + else: + particleSequence = Sequence() + self.track = Sequence(Func(self.show), Parallel(self.ripples.track, rippleSequence, particleSequence), Func(self.hide), name='splashdown-%d-track' % self.trackId) + + def play(self, rate = 1): + self.stop() + self.createTrack(rate) + self.track.start() + + def loop(self, rate = 1): + self.stop() + self.createTrack(rate) + self.track.loop() + + def stop(self): + if self.track: + self.track.finish() + + def destroy(self): + self.stop() + del self.track + self.ripples.destroy() + del self.ripples + if self.wantParticles: + self.pSystem.cleanup() + del self.pSystem + del self.particles + self.removeNode() diff --git a/toontown/effects/StarBurst.py b/toontown/effects/StarBurst.py new file mode 100755 index 00000000..91850f33 --- /dev/null +++ b/toontown/effects/StarBurst.py @@ -0,0 +1,94 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from EffectController import EffectController +from PooledEffect import PooledEffect + +class StarBurst(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.card = model.find('**/tt_t_efx_ext_particleStars') + self.cardScale = 64.0 + self.setDepthWrite(0) + self.setColorScaleOff() + self.setLightOff() + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.f = ParticleEffect.ParticleEffect('StarBurst') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-0') + self.p0.setFactory('ZSpinParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('SphereSurfaceEmitter') + self.f.addParticles(self.p0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -25.0), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + self.p0.setPoolSize(128) + self.p0.setBirthRate(0.04) + self.p0.setLitterSize(64) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(1) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(1.25) + self.p0.factory.setLifespanSpread(0.5) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.factory.setInitialAngle(0.0) + self.p0.factory.setInitialAngleSpread(360.0) + self.p0.factory.enableAngularVelocity(1) + self.p0.factory.setAngularVelocity(0.0) + self.p0.factory.setAngularVelocitySpread(0.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.renderer.getColorInterpolationManager().addLinear(0.0, 0.1, Vec4(0, 0, 0, 0), self.effectColor, 1) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, 0.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + self.setEffectColor(self.effectColor) + + def createTrack(self): + self.track = Sequence(Func(self.p0.setBirthRate, 0.04), Func(self.p0.clearToInitial), Func(self.f.start, self, self), Wait(0.2), Func(self.p0.setBirthRate, 100.0), Wait(2.0), Func(self.cleanUpEffect)) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(0.5 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(0.8 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(0.5 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(1.0 * self.cardScale * scale) + self.p0.emitter.setAmplitude(100.0 * scale) + self.p0.emitter.setAmplitudeSpread(50.0 * scale) + self.p0.emitter.setRadius(220.0 * scale) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/TrailExplosion.py b/toontown/effects/TrailExplosion.py new file mode 100755 index 00000000..5087b309 --- /dev/null +++ b/toontown/effects/TrailExplosion.py @@ -0,0 +1,75 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from EffectController import EffectController +from PooledEffect import PooledEffect +from toontown.effects.SparksTrailLong import SparksTrailLong +import random + +class TrailExplosion(PooledEffect, EffectController): + trailsVel = [[Vec3(150, -50, 100), Vec3(-150, -50, 100), Vec3(0, 150, 100)], [Vec3(120, 120, 100), + Vec3(120, -120, 100), + Vec3(-120, 120, 100), + Vec3(-120, -120, 100)], [Vec3(0, 150, 100), + Vec3(140, 30, 100), + Vec3(-140, 30, 100), + Vec3(30, -60, 100), + Vec3(-30, -60, 100)]] + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.numTrails = 5 + self.trails = [] + self.trailEffects = [] + self.trailIval = Parallel() + + def createTrack(self): + self.trails = [] + self.trailEffects = [] + self.trailIval = Parallel() + vels = None + if self.numTrails >= 3 and self.numTrails <= 5: + vels = self.trailsVel[self.numTrails - 3] + for i in xrange(self.numTrails): + self.trails.append(self.attachNewNode('trail')) + vel = Vec3(0, 0, 0) + if vels: + vel = Vec3(vels[i][0] + random.randint(-20, 20), vels[i][1] + random.randint(-20, 20), vels[i][2] + random.randint(0, 50)) + else: + vel = Vec3(random.randint(-200, 200), random.randint(-200, 200), random.randint(80, 150)) + vel *= self.effectScale + dur = 2.0 + random.random() / 4.0 + self.trailIval.append(ProjectileInterval(self.trails[i], startVel=vel, duration=dur, gravityMult=0.6)) + self.trailEffects.append(SparksTrailLong.getEffect()) + if self.trailEffects[i]: + self.trailEffects[i].reparentTo(self.trails[i]) + self.trailEffects[i].setLifespan(2.0) + self.trailEffects[i].setEffectColor(self.effectColor) + self.trailEffects[i].setEffectScale(self.effectScale) + self.trailIval.append(Sequence(Func(self.trailEffects[i].startLoop), Wait(dur), Func(self.trailEffects[i].stopLoop))) + + self.track = Sequence(self.trailIval, Func(self.cleanUpEffect)) + return + + def setEffectScale(self, scale): + self.effectScale = scale + + def setEffectColor(self, color): + self.effectColor = color + + def cleanUpEffect(self): + for effect in self.trailEffects: + if effect: + effect.stopLoop() + effect = None + + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + return + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/Wake.py b/toontown/effects/Wake.py new file mode 100755 index 00000000..55cfc5a3 --- /dev/null +++ b/toontown/effects/Wake.py @@ -0,0 +1,137 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import globalPropPool + +class Wake(NodePath): + wakeCount = 0 + + def __init__(self, parent = hidden, target = hidden): + NodePath.__init__(self) + self.assign(parent.attachNewNode('wake')) + self.target = target + self.ripples = globalPropPool.getProp('ripples') + tformNode = self.ripples.getChild(0) + tformNode.setZ(0.01) + self.seqNodePath = self.ripples.find('**/+SequenceNode') + self.seqNode = self.seqNodePath.node() + self.sortBase = 10 + self.rippleCount = 0 + self.doLaters = [None] * 20 + self.trackId = Wake.wakeCount + Wake.wakeCount += 1 + return + + def createRipple(self, zPos, rate = 1.0, startFrame = 0): + ripple = self.ripples.copyTo(self) + ripple.iPos(self.target) + ripple.setZ(render, zPos + self.rippleCount * 0.001) + ripple.setBin('fixed', self.sortBase + self.rippleCount, 1) + seqNode = ripple.find('**/+SequenceNode').node() + seqNode.setPlayRate(rate) + seqNode.play(startFrame, seqNode.getNumFrames() - 1) + duration = (24 - startFrame) / 24.0 + + def clearDoLaterList(rippleCount): + self.doLaters[rippleCount] = None + return + + def destroyRipple(task): + ripple.removeNode() + + t = taskMgr.doMethodLater(duration, clearDoLaterList, 'wake-%d-destroy-%d' % (self.trackId, self.rippleCount), extraArgs=(self.rippleCount,), uponDeath=destroyRipple) + self.doLaters[self.rippleCount] = t + self.rippleCount = (self.rippleCount + 1) % 20 + + def stop(self): + for i in xrange(len(self.doLaters)): + if self.doLaters[i]: + taskMgr.remove(self.doLaters[i]) + self.doLaters[i] = None + + return + + def destroy(self): + self.stop() + self.removeNode() + self.ripples.removeNode() + del self.target + + +class WakeSequence(NodePath): + wakeCount = 0 + + def __init__(self, parent = hidden): + NodePath.__init__(self) + self.assign(globalPropPool.getProp('wake')) + self.reparentTo(parent) + tformNode = self.getChild(0) + tformNode.setZ(0.1) + self.startNodePath = self.find('**/+SequenceNode') + self.startSeqNode = self.startNodePath.node() + self.startSeqNode.setName('start') + self.startSeqNode.setPlayRate(0) + self.cycleNodePath = NodePath(SequenceNode(0, 'cycle')) + self.cycleNodePath.reparentTo(tformNode) + self.cycleSeqNode = self.cycleNodePath.node() + self.endNodePath = NodePath(SequenceNode(0, 'end')) + self.endNodePath.reparentTo(tformNode) + self.endSeqNode = self.endNodePath.node() + children = self.startNodePath.getChildren() + for child in children[12:16]: + child.reparentTo(self.cycleNodePath) + + for child in children[16:]: + child.reparentTo(self.endNodePath) + + self.tracks = [] + self.rate = None + self.trackId = Wake.wakeCount + Wake.wakeCount += 1 + self.setBin('fixed', 10, 1) + self.hide() + return + + def createTracks(self, rate = 1): + self.stop() + self.tracks = [] + tflipDuration = self.startSeqNode.getNumChildren() / (float(rate) * 24) + startTrack = Sequence(Func(self.show), Func(self.showTrack, 0), Func(self.startSeqNode.play, 0, self.startSeqNode.getNumFrames() - 1), Func(self.startSeqNode.setPlayRate, rate), Wait(tflipDuration), Func(self.showTrack, 1), Func(self.startSeqNode.play, 0, self.startSeqNode.getNumFrames() - 1), Func(self.cycleSeqNode.setPlayRate, rate), name='start-wake-track-%d' % self.trackId) + self.tracks.append(startTrack) + tflipDuration = self.endSeqNode.getNumChildren() / (float(rate) * 24) + endTrack = Sequence(Func(self.showTrack, 2), Func(self.endSeqNode.play, 0, self.endSeqNode.getNumFrames() - 1), Func(self.endSeqNode.setPlayRate, rate), Wait(tflipDuration), Func(self.endSeqNode.setPlayRate, 0), Func(self.hide), name='end-wake-track-%d' % self.trackId) + self.tracks.append(endTrack) + self.rate = rate + + def showTrack(self, trackId): + if trackId == 0: + self.startNodePath.show() + else: + self.startNodePath.hide() + if trackId == 1: + self.cycleNodePath.show() + else: + self.cycleNodePath.hide() + if trackId == 2: + self.endNodePath.show() + else: + self.endNodePath.hide() + + def play(self, trackId, rate = 1): + if self.rate != rate: + self.createTracks(rate) + self.tracks[trackId].start() + + def loop(self, trackId, rate = 1): + if self.rate != rate: + self.createTracks(rate) + self.tracks[trackId].loop() + + def stop(self): + for track in self.tracks: + track.finish() + + def destroy(self): + self.stop() + self.tracks = None + self.removeNode() + return diff --git a/toontown/effects/__init__.py b/toontown/effects/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/estate/BankGUI.py b/toontown/estate/BankGUI.py new file mode 100755 index 00000000..8c96bf1b --- /dev/null +++ b/toontown/estate/BankGUI.py @@ -0,0 +1,141 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.task.Task import Task + +class BankGui(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('BankGui') + + def __init__(self, doneEvent, allowWithdraw = 1): + DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.33, 1, 1.1), pos=(0, 0, 0)) + self.initialiseoptions(BankGui) + self.doneEvent = doneEvent + self.__transactionAmount = 0 + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + jarGui = loader.loadModel('phase_3.5/models/gui/jar_gui') + arrowGui = loader.loadModel('phase_3/models/gui/create_a_toon_gui') + bankModel = loader.loadModel('phase_5.5/models/estate/jellybeanBank') + bankModel.setDepthWrite(1) + bankModel.setDepthTest(1) + bankModel.find('**/jellybeans').setDepthWrite(0) + bankModel.find('**/jellybeans').setDepthTest(0) + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + arrowImageList = (arrowGui.find('**/CrtATn_R_Arrow_UP'), + arrowGui.find('**/CrtATn_R_Arrow_DN'), + arrowGui.find('**/CrtATn_R_Arrow_RLVR'), + arrowGui.find('**/CrtATn_R_Arrow_UP')) + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(-0.2, 0, -0.4), text=TTLocalizer.BankGuiCancel, text_scale=0.06, text_pos=(0, -0.1), command=self.__cancel) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, pos=(0.2, 0, -0.4), text=TTLocalizer.BankGuiOk, text_scale=0.06, text_pos=(0, -0.1), command=self.__requestTransaction) + self.jarDisplay = DirectLabel(parent=self, relief=None, pos=(-0.4, 0, 0), scale=0.7, text=str(base.localAvatar.getMoney()), text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0, -0.1, 0), image=jarGui.find('**/Jar'), text_font=ToontownGlobals.getSignFont()) + self.bankDisplay = DirectLabel(parent=self, relief=None, pos=(0.4, 0, 0), scale=0.9, text=str(base.localAvatar.getBankMoney()), text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0, -0.1, 0), geom=bankModel, geom_scale=0.08, geom_pos=(0, 10, -0.26), geom_hpr=(0, 0, 0), text_font=ToontownGlobals.getSignFont()) + self.depositArrow = DirectButton(parent=self, relief=None, image=arrowImageList, image_scale=(1, 1, 1), image3_color=Vec4(0.6, 0.6, 0.6, 0.25), pos=(0.01, 0, 0.15)) + self.withdrawArrow = DirectButton(parent=self, relief=None, image=arrowImageList, image_scale=(-1, 1, 1), image3_color=Vec4(0.6, 0.6, 0.6, 0.25), pos=(-0.01, 0, -0.15)) + self.depositArrow.bind(DGG.B1PRESS, self.__depositButtonDown) + self.depositArrow.bind(DGG.B1RELEASE, self.__depositButtonUp) + self.withdrawArrow.bind(DGG.B1PRESS, self.__withdrawButtonDown) + self.withdrawArrow.bind(DGG.B1RELEASE, self.__withdrawButtonUp) + self.accept('bankAsleep', self.__cancel) + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.accept(localAvatar.uniqueName('bankMoneyChange'), self.__bankMoneyChange) + if allowWithdraw: + self.depositArrow.setPos(0.01, 0, 0.15) + self.withdrawArrow.setPos(-0.01, 0, -0.15) + else: + self.depositArrow.setPos(0, 0, 0) + self.withdrawArrow.hide() + buttons.removeNode() + jarGui.removeNode() + arrowGui.removeNode() + self.__updateTransaction(0) + return + + def destroy(self): + taskMgr.remove(self.taskName('runCounter')) + self.ignore(localAvatar.uniqueName('moneyChange')) + self.ignore(localAvatar.uniqueName('bankMoneyChange')) + DirectFrame.destroy(self) + + def __cancel(self): + messenger.send(self.doneEvent, [0]) + + def __requestTransaction(self): + messenger.send(self.doneEvent, [self.__transactionAmount]) + + def __updateTransaction(self, amount): + hitLimit = 0 + self.__transactionAmount += amount + jarMoney = base.localAvatar.getMoney() + maxJarMoney = base.localAvatar.getMaxMoney() + bankMoney = base.localAvatar.getBankMoney() + maxBankMoney = base.localAvatar.getMaxBankMoney() + self.__transactionAmount = min(self.__transactionAmount, jarMoney) + self.__transactionAmount = min(self.__transactionAmount, maxBankMoney - bankMoney) + self.__transactionAmount = -min(-self.__transactionAmount, maxJarMoney - jarMoney) + self.__transactionAmount = -min(-self.__transactionAmount, bankMoney) + newJarMoney = jarMoney - self.__transactionAmount + newBankMoney = bankMoney + self.__transactionAmount + if newJarMoney <= 0 or newBankMoney >= maxBankMoney: + self.depositArrow['state'] = DGG.DISABLED + hitLimit = 1 + else: + self.depositArrow['state'] = DGG.NORMAL + if newBankMoney <= 0 or newJarMoney >= maxJarMoney: + self.withdrawArrow['state'] = DGG.DISABLED + hitLimit = 1 + else: + self.withdrawArrow['state'] = DGG.NORMAL + self.jarDisplay['text'] = str(newJarMoney) + self.bankDisplay['text'] = str(newBankMoney) + return (hitLimit, + newJarMoney, + newBankMoney, + self.__transactionAmount) + + def __runCounter(self, task): + if task.time - task.prevTime < task.delayTime: + return Task.cont + else: + task.delayTime = max(0.05, task.delayTime * 0.75) + task.prevTime = task.time + hitLimit, jar, bank, trans = self.__updateTransaction(task.delta) + if hitLimit: + return Task.done + else: + return Task.cont + + def __depositButtonUp(self, event): + messenger.send('wakeup') + taskMgr.remove(self.taskName('runCounter')) + + def __depositButtonDown(self, event): + messenger.send('wakeup') + task = Task(self.__runCounter) + task.delayTime = 0.4 + task.prevTime = 0.0 + task.delta = 1 + hitLimit, jar, bank, trans = self.__updateTransaction(task.delta) + if not hitLimit: + taskMgr.add(task, self.taskName('runCounter')) + + def __withdrawButtonUp(self, event): + messenger.send('wakeup') + taskMgr.remove(self.taskName('runCounter')) + + def __withdrawButtonDown(self, event): + messenger.send('wakeup') + task = Task(self.__runCounter) + task.delayTime = 0.4 + task.prevTime = 0.0 + task.delta = -1 + hitLimit, jar, bank, trans = self.__updateTransaction(task.delta) + if not hitLimit: + taskMgr.add(task, self.taskName('runCounter')) + + def __moneyChange(self, money): + self.__updateTransaction(0) + + def __bankMoneyChange(self, bankMoney): + self.__updateTransaction(0) diff --git a/toontown/estate/BankGlobals.py b/toontown/estate/BankGlobals.py new file mode 100755 index 00000000..7aef4838 --- /dev/null +++ b/toontown/estate/BankGlobals.py @@ -0,0 +1,7 @@ +BANK_MOVIE_CLEAR = 1 +BANK_MOVIE_GUI = 2 +BANK_MOVIE_DEPOSIT = 3 +BANK_MOVIE_WITHDRAW = 4 +BANK_MOVIE_NO_OP = 5 +BANK_MOVIE_NOT_OWNER = 6 +BANK_MOVIE_NO_OWNER = 7 diff --git a/toontown/estate/BeanRecipeGui.py b/toontown/estate/BeanRecipeGui.py new file mode 100755 index 00000000..5eac0a0d --- /dev/null +++ b/toontown/estate/BeanRecipeGui.py @@ -0,0 +1,60 @@ +from direct.gui.DirectGui import * +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * +from toontown.estate import GardenGlobals +from toontown.estate import PlantingGUI +from toontown.toonbase import TTLocalizer + +class BeanRecipeGui(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PlantingGUI') + + def __init__(self, parent, recipe, **kw): + left = 0 + right = 0.445 + bottom = 0 + top = 0.08 + borderWidth = 0.01 + optiondefs = [('relief', DGG.RIDGE, None), + ('state', 'normal', None), + ('pos', (0, 0, 0), None), + ('frameSize', (left, + right, + bottom, + top), None), + ('borderWidth', (borderWidth, borderWidth), self.setBorderWidth)] + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.initialiseoptions(BeanRecipeGui) + self.jellyBeanBoxList = [] + xIncrement = 0.052 + for i in xrange(len(recipe)): + beanIndex = GardenGlobals.BeanColorLetters.index(recipe[i]) + self.createJellyBeanBox(beanIndex, borderWidth + xIncrement * i, borderWidth) + + for j in xrange(len(recipe), GardenGlobals.getNumberOfShovelBoxes()): + self.createEmptyBeanBox(borderWidth + xIncrement * j, borderWidth) + + return + + def createJellyBeanBox(self, beanIndex, xPos, zPos): + geomColor = (1, 1, 1, 1) + state = DGG.NORMAL + command = None + xAdj = 0.03 + zAdj = 0.03 + newBox = DirectButton(parent=self, pos=(xPos + xAdj, 0, zPos + zAdj), geom=DGG.getDefaultDialogGeom(), geom_scale=(0.1, 1.0, 0.1), geom_color=geomColor, scale=(0.5, 1, 0.5), relief=None, state=state, command=command, extraArgs=[beanIndex], text=TTLocalizer.BeanColorWords[beanIndex], text_pos=(0.0, 0.1), text_scale=0.07, text_fg=Vec4(0, 0, 0, 0), text1_fg=Vec4(0, 0, 0, 1), text2_fg=Vec4(0, 0, 0, 1), text3_fg=Vec4(0, 0, 0, 0), clickSound=None, pressEffect=0) + beanParent = newBox.attachNewNode('bean_%d' % beanIndex) + PlantingGUI.loadJellyBean(beanParent, beanIndex) + self.jellyBeanBoxList.append(newBox) + return + + def createEmptyBeanBox(self, xPos, zPos): + geomColor = (1, 1, 1, 1) + state = DGG.NORMAL + command = None + xAdj = 0.03 + zAdj = 0.03 + newBox = DirectButton(parent=self, pos=(xPos + xAdj, 0, zPos + zAdj), geom=DGG.getDefaultDialogGeom(), geom_scale=(0.1, 1.0, 0.1), geom_color=geomColor, scale=(0.5, 1, 0.5), relief=None, state=state, command=command, text='', text_pos=(0.0, 0.1), text_scale=0.07, text_fg=Vec4(0, 0, 0, 0), text1_fg=Vec4(0, 0, 0, 1), text2_fg=Vec4(0, 0, 0, 1), text3_fg=Vec4(0, 0, 0, 0), clickSound=None, pressEffect=0) + newBox.setColorScale(0.5, 0.5, 0.5, 1) + self.jellyBeanBoxList.append(newBox) + return diff --git a/toontown/estate/CannonGlobals.py b/toontown/estate/CannonGlobals.py new file mode 100755 index 00000000..0650f4b4 --- /dev/null +++ b/toontown/estate/CannonGlobals.py @@ -0,0 +1,11 @@ +CANNON_TIMEOUT = 30 +CANNON_MOVIE_LOAD = 1 +CANNON_MOVIE_CLEAR = 2 +CANNON_MOVIE_FORCE_EXIT = 3 +CANNON_MOVIE_LANDED = 4 +cannonDrops = [(-110, -66, 0.025, -64, 0, 0), + (65.68, -71.1, 0.025, 24, 0, 0), + (-53.7788, -104.5, 0.72, -24, 0, 0), + (39.58, 1.53, 8.2, 101.2, 0, 0), + (61.3, 62.78, 0.031, 171.2, 0, 0), + (-43.8, 75.2, 0.025, -158.15, 0, 0)] diff --git a/toontown/estate/ClosetGUI.py b/toontown/estate/ClosetGUI.py new file mode 100755 index 00000000..40c52089 --- /dev/null +++ b/toontown/estate/ClosetGUI.py @@ -0,0 +1,232 @@ +from direct.showbase.PythonUtil import Functor +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.makeatoon import ClothesGUI +import ClosetGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer + +class ClosetGUI(ClothesGUI.ClothesGUI): + notify = directNotify.newCategory('ClosetGUI') + + def __init__(self, isOwner, doneEvent, cancelEvent, swapEvent, deleteEvent, topList = None, botList = None, maxClothes = 0): + ClothesGUI.ClothesGUI.__init__(self, ClothesGUI.CLOTHES_CLOSET, doneEvent, swapEvent) + self.toon = None + self.topsList = topList + self.bottomsList = botList + self.isOwner = isOwner + self.deleteEvent = deleteEvent + self.cancelEvent = cancelEvent + self.genderChange = 0 + self.maxClothes = maxClothes + self.verify = None + + def load(self): + ClothesGUI.ClothesGUI.load(self) + self.gui = loader.loadModel('phase_3/models/gui/create_a_toon_gui') + self.cancelButton = DirectButton(relief=None, image=(self.gui.find('**/CrtAtoon_Btn2_UP'), self.gui.find('**/CrtAtoon_Btn2_DOWN'), self.gui.find('**/CrtAtoon_Btn2_RLLVR')), pos=(0.15, 0, -0.85), command=self.__handleCancel, text=('', TTLocalizer.MakeAToonCancel, TTLocalizer.MakeAToonCancel), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, -0.03), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.cancelButton.hide() + self.countFrame = DirectFrame(parent=self.parentFrame, image=self.shuffleFrame, image_scale=(-0.6, 0.6, 0.6), relief=None, pos=(0, 0, -0.125), scale=1.2, frameColor=(1, 1, 1, 1), text=TTLocalizer.ClothesGUICount % (0, 0), text_scale=0.0575, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + if self.isOwner: + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') + trashImage = (trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_RLVR')) + self.trashPanel = DirectFrame(parent=aspect2d, image=DGG.getDefaultDialogGeom(), image_color=(1, 1, 0.75, 0.8), image_scale=(0.36, 0, 0.75), pos=(-.86, 0, -.05), relief=None) + self.topTrashButton = DirectButton(parent=self.trashPanel, image=trashImage, relief=None, pos=(-0.09, 0, 0.2), command=self.__handleDelete, extraArgs=[ClosetGlobals.SHIRT], scale=(0.5, 0.5, 0.5), text=TTLocalizer.ClosetDeleteShirt, text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.12, text_pos=(0.3, 0), text_fg=(0.8, 0.2, 0.2, 1), text_shadow=(0, 0, 0, 1), textMayChange=0) + self.bottomTrashButton = DirectButton(parent=self.trashPanel, image=trashImage, relief=None, textMayChange=1, pos=(-0.09, 0, -0.2), command=self.__handleDelete, extraArgs=[ClosetGlobals.SHORTS], scale=(0.5, 0.5, 0.5), text=TTLocalizer.ClosetDeleteShorts, text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.12, text_pos=(0.3, 0), text_fg=(0.8, 0.2, 0.2, 1), text_shadow=(0, 0, 0, 1)) + self.button = DirectButton(relief=None, image=(self.gui.find('**/CrtAtoon_Btn1_UP'), self.gui.find('**/CrtAtoon_Btn1_DOWN'), self.gui.find('**/CrtAtoon_Btn1_RLLVR')), pos=(-0.15, 0, -0.85), command=self.__handleButton, text=('', TTLocalizer.MakeAToonDone, TTLocalizer.MakeAToonDone), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, -0.03), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + trashcanGui.removeNode() + + def unload(self): + self.ignore('verifyDone') + ClothesGUI.ClothesGUI.unload(self) + self.cancelButton.destroy() + del self.cancelButton + self.countFrame.destroy() + del self.countFrame + if self.isOwner: + self.topTrashButton.destroy() + self.bottomTrashButton.destroy() + self.button.destroy() + del self.topTrashButton + del self.bottomTrashButton + del self.button + self.trashPanel.destroy() + del self.trashPanel + if self.verify: + self.verify.cleanup() + del self.verify + + def showButtons(self): + ClothesGUI.ClothesGUI.showButtons(self) + self.cancelButton.show() + if self.isOwner: + self.topTrashButton.show() + self.bottomTrashButton.show() + self.button.show() + + def hideButtons(self): + ClothesGUI.ClothesGUI.hideButtons(self) + self.cancelButton.hide() + if self.isOwner: + self.topTrashButton.hide() + self.bottomTrashButton.hide() + self.button.hide() + + def setupScrollInterface(self): + self.notify.debug('setupScrollInterface') + self.dna = self.toon.getStyle() + self.gender = self.dna.getGender() + self.swappedTorso = 0 + if self.topsList == None: + self.topsList = self.toon.getClothesTopsList() + if self.bottomsList == None: + self.bottomsList = self.toon.getClothesBottomsList() + self.tops = [] + self.bottoms = [] + self.tops.append((self.dna.topTex, + self.dna.topTexColor, + self.dna.sleeveTex, + self.dna.sleeveTexColor)) + self.bottoms.append((self.dna.botTex, self.dna.botTexColor)) + i = 0 + while i < len(self.topsList): + self.tops.append((self.topsList[i], + self.topsList[i + 1], + self.topsList[i + 2], + self.topsList[i + 3])) + i = i + 4 + + i = 0 + while i < len(self.bottomsList): + self.bottoms.append((self.bottomsList[i], self.bottomsList[i + 1])) + i = i + 2 + + self.topChoice = 0 + self.bottomChoice = 0 + self.swapTop(0) + self.swapBottom(0) + if self.isOwner: + self.updateTrashButtons() + self.setupButtons() + self.updateCount() + + def updateCount(self, clothes, maxClothes): + self.countFrame['text'] = TTLocalizer.ClothesGUICount % (clothes, maxClothes) + + def updateTrashButtons(self): + if len(self.tops) < 2: + self.topTrashButton['state'] = DGG.DISABLED + else: + self.topTrashButton['state'] = DGG.NORMAL + if len(self.bottoms) < 2: + self.bottomTrashButton['state'] = DGG.DISABLED + else: + self.bottomTrashButton['state'] = DGG.NORMAL + if self.toon: + if self.toon.style.torso[1] == 'd': + self.bottomTrashButton['text'] = TTLocalizer.ClosetDeleteSkirt + else: + self.bottomTrashButton['text'] = TTLocalizer.ClosetDeleteShorts + self.updateCount() + + def updateCount(self): + clothes = (len(self.tops) + len(self.bottoms)) - 2 + self.countFrame['text'] = TTLocalizer.ClothesGUICount % (clothes, self.maxClothes) + + def setGender(self, gender): + self.ownerGender = gender + self.genderChange = 1 + + def swapBottom(self, offset): + length = len(self.bottoms) + self.bottomChoice += offset + if self.bottomChoice <= 0: + self.bottomChoice = 0 + self.updateScrollButtons(self.bottomChoice, length, 0, self.bottomLButton, self.bottomRButton) + if self.bottomChoice < 0 or self.bottomChoice >= len(self.bottoms) or len(self.bottoms[self.bottomChoice]) != 2: + self.notify.warning('bottomChoice index is out of range!') + return None + self.toon.style.botTex = self.bottoms[self.bottomChoice][0] + self.toon.style.botTexColor = self.bottoms[self.bottomChoice][1] + if self.genderChange == 1: + if self.bottomChoice > 0: + self.__handleGenderBender(1) + else: + self.__handleGenderBender(0) + if self.toon.generateToonClothes() == 1: + self.toon.loop('neutral', 0) + self.swappedTorso = 1 + if self.isOwner: + self.updateTrashButtons() + if self.swapEvent != None: + messenger.send(self.swapEvent) + + def __handleGenderBender(self, type): + if type == 1: + if self.toon.style.gender != self.ownerGender and self.toon.style.gender == 'f': + self.toon.swapToonTorso(self.toon.style.torso[0] + 's', genClothes=0) + self.toon.loop('neutral', 0) + self.swappedTorso = 1 + self.toon.style.gender = self.ownerGender + else: + self.toon.style.gender = self.gender + if self.toon.style.gender != self.ownerGender and self.toon.style.gender == 'm': + self.toon.swapToonTorso(self.toon.style.torso[0] + 's', genClothes=0) + self.toon.loop('neutral', 0) + self.swappedTorso = 1 + + def removeTop(self, index): + listLen = len(self.tops) + if index < listLen: + del self.tops[index] + if self.topChoice > index: + self.topChoice -= 1 + elif self.topChoice == index: + self.topChoice = 0 + return 1 + return 0 + + def removeBottom(self, index): + listLen = len(self.bottoms) + if index < listLen: + del self.bottoms[index] + if self.bottomChoice > index: + self.bottomChoice -= 1 + elif self.bottomChoice == index: + self.bottomChoice = 0 + return 1 + return 0 + + def __handleButton(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) + messenger.send('wakeup') + + def __handleCancel(self): + messenger.send(self.cancelEvent) + messenger.send('wakeup') + + def __handleDelete(self, t_or_b): + if t_or_b == ClosetGlobals.SHIRT: + item = TTLocalizer.ClosetShirt + elif self.toon.style.torso[1] == 'd': + item = TTLocalizer.ClosetSkirt + else: + item = TTLocalizer.ClosetShorts + self.verify = TTDialog.TTGlobalDialog(doneEvent='verifyDone', message=TTLocalizer.ClosetVerifyDelete % item, style=TTDialog.TwoChoice) + self.verify.show() + self.accept('verifyDone', Functor(self.__handleVerifyDelete, t_or_b)) + messenger.send('wakeup') + + def __handleVerifyDelete(self, t_or_b): + status = self.verify.doneStatus + self.ignore('verifyDone') + self.verify.cleanup() + del self.verify + self.verify = None + if status == 'ok': + messenger.send(self.deleteEvent, [t_or_b]) + messenger.send('wakeup') + return diff --git a/toontown/estate/ClosetGlobals.py b/toontown/estate/ClosetGlobals.py new file mode 100755 index 00000000..afad39e0 --- /dev/null +++ b/toontown/estate/ClosetGlobals.py @@ -0,0 +1,8 @@ +SHIRT = 1 +SHORTS = 2 +CLOSET_MOVIE_COMPLETE = 1 +CLOSET_MOVIE_CLEAR = 2 +CLOSET_MOVIE_TIMEOUT = 3 +CLOSED = 0 +OPEN = 1 +TIMEOUT_TIME = 200 diff --git a/toontown/estate/DNAFurnitureReaderAI.py b/toontown/estate/DNAFurnitureReaderAI.py new file mode 100755 index 00000000..53248c27 --- /dev/null +++ b/toontown/estate/DNAFurnitureReaderAI.py @@ -0,0 +1,88 @@ +from toontown.catalog.CatalogItemList import CatalogItemList +from toontown.catalog.CatalogFurnitureItem import CatalogFurnitureItem +from toontown.catalog import CatalogItem + +# Mapping of DNA prop codes to furniture ID values. Use None to ignore a code. +DNA2Furniture = { + 'house_interiorA': None, + 'GardenA': None, + + 'chairA': 100, + 'chair': 110, + 'regular_bed': (200, 210), + 'FireplaceSq': 400, + 'closetBoy': (500, 510), + 'lamp_short': 600, + 'lamp_tall': 610, + 'couch_1person': 700, + 'couch_2person': 710, + 'desk_only_wo_phone': 800, + 'desk_only': 800, + 'coatrack': 910, + 'paper_trashcan': 920, + 'rug': 1000, + 'rugA': 1010, + 'rugB': 1020, + 'cabinetYwood': 1110, + 'bookcase': 1120, + 'bookcase_low': 1130, + 'ending_table': 1200, + 'jellybeanBank': 1300, + 'trunkBoy': (4000, 4010) +} + + +class DNAFurnitureReaderAI: + # This object processes the house_interior*.dna files and produces a + # CatalogItemList representing the furniture in the DNA file. The resulting + # list is passed to the FurnitureManager in order to initialize a blank + # house to the default furniture arrangement. + notify = directNotify.newCategory("DNAFurnitureReaderAI") + + def __init__(self, dnaData, gender, phonePos): + self.dnaData = dnaData + self.gender = gender + self.phonePos = phonePos + self.itemList = None + + def buildList(self): + self.itemList = CatalogItemList(store=(CatalogItem.Customization | + CatalogItem.Location)) + + # Find the interior node: + for i in xrange(self.dnaData.getNumChildren()): + child = self.dnaData.at(i) + if child.getName() == 'interior': + interior = child + break + else: + self.notify.error('Could not find "interior" in DNA!') + + self.itemList.append(CatalogFurnitureItem(1399, posHpr=self.phonePos)) + # Every child in the interior node is a prop, thus: + for i in xrange(interior.getNumChildren()): + child = interior.at(i) + code = child.getCode() + + if code not in DNA2Furniture: + self.notify.warning('Unrecognized furniture code %r!' % code) + continue + + itemId = DNA2Furniture[code] + if itemId is None: + continue + if hasattr(itemId, '__getitem__'): + itemId = itemId[self.gender] + + x, y, z = child.getPos() + h, p, r = child.getHpr() + self.itemList.append(CatalogFurnitureItem(itemId, + posHpr=(x, y, z, h, p, r))) + + def getList(self): + if not self.itemList: + self.buildList() + return self.itemList + + def getBlob(self): + return self.getList().getBlob() diff --git a/toontown/estate/DistributedAnimatedStatuary.py b/toontown/estate/DistributedAnimatedStatuary.py new file mode 100755 index 00000000..8e4d7498 --- /dev/null +++ b/toontown/estate/DistributedAnimatedStatuary.py @@ -0,0 +1,45 @@ +from pandac.PandaModules import NodePath +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.estate import DistributedStatuary +from toontown.estate import GardenGlobals +from direct.actor import Actor + +class DistributedAnimatedStatuary(DistributedStatuary.DistributedStatuary): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedAnimatedStatuary') + + def __init__(self, cr): + self.notify.debug('constructing DistributedAnimatedStatuary') + DistributedStatuary.DistributedStatuary.__init__(self, cr) + + def loadModel(self): + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = Actor.Actor() + animPath = self.modelPath + self.anims[1] + self.model.loadModel(self.modelPath + self.anims[0]) + self.model.loadAnims(dict([[self.anims[1], animPath]])) + colNode = self.model.find('**/+CollisionNode') + if self.typeIndex == 234: + colNode.setScale(0.5) + if not colNode.isEmpty(): + score, multiplier = ToontownGlobals.PinballScoring[ToontownGlobals.PinballStatuary] + if self.pinballScore: + score = self.pinballScore[0] + multiplier = self.pinballScore[1] + scoreNodePath = NodePath('statuary-%d-%d' % (score, multiplier)) + colNode.setName('statuaryCol') + scoreNodePath.reparentTo(colNode.getParent()) + colNode.reparentTo(scoreNodePath) + self.model.setScale(self.worldScale) + self.model.reparentTo(self.rotateNode) + self.model.loop(self.anims[1]) + + def setTypeIndex(self, typeIndex): + DistributedStatuary.DistributedStatuary.setTypeIndex(self, typeIndex) + self.anims = GardenGlobals.PlantAttributes[typeIndex]['anims'] + + def setupShadow(self): + if self.typeIndex == 234: + pass + else: + DistributedStatuary.DistributedStatuary.setupShadow() diff --git a/toontown/estate/DistributedAnimatedStatuaryAI.py b/toontown/estate/DistributedAnimatedStatuaryAI.py new file mode 100755 index 00000000..e7ce70d7 --- /dev/null +++ b/toontown/estate/DistributedAnimatedStatuaryAI.py @@ -0,0 +1,6 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.estate.DistributedStatuaryAI import DistributedStatuaryAI + +class DistributedAnimatedStatuaryAI(DistributedStatuaryAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedAnimatedStatuaryAI") + diff --git a/toontown/estate/DistributedBank.py b/toontown/estate/DistributedBank.py new file mode 100755 index 00000000..64c8c660 --- /dev/null +++ b/toontown/estate/DistributedBank.py @@ -0,0 +1,220 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownGlobals +import DistributedFurnitureItem +from toontown.toonbase import TTLocalizer +import BankGUI +from BankGlobals import * +from toontown.toontowngui import TTDialog +from toontown.catalog.CatalogFurnitureItem import FurnitureTypes +from toontown.catalog.CatalogFurnitureItem import FTScale + +class DistributedBank(DistributedFurnitureItem.DistributedFurnitureItem): + notify = directNotify.newCategory('DistributedBank') + + def __init__(self, cr): + DistributedFurnitureItem.DistributedFurnitureItem.__init__(self, cr) + self.bankGui = None + self.bankTrack = None + self.bankDialog = None + self.hasLocalAvatar = 0 + self.hasJarOut = 0 + self.jarLods = [] + return + + def generate(self): + DistributedFurnitureItem.DistributedFurnitureItem.generate(self) + self.bankSphereEvent = 'bankSphere' + self.bankSphereEnterEvent = 'enter' + self.bankSphereEvent + self.bankSphereExitEvent = 'exit' + self.bankSphereEvent + self.bankGuiDoneEvent = 'bankGuiDone' + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedFurnitureItem.DistributedFurnitureItem.announceGenerate(self) + self.accept(self.bankSphereEnterEvent, self.__handleEnterSphere) + + def loadModel(self): + model = DistributedFurnitureItem.DistributedFurnitureItem.loadModel(self) + bowl = model.find('**/bowl') + if bowl: + bowl.setBin('fixed', 40) + return model + + def disable(self): + self.notify.debug('disable') + self.ignore(self.bankSphereEnterEvent) + self.ignore(self.bankSphereExitEvent) + self.ignore(self.bankGuiDoneEvent) + if self.bankTrack: + self.bankTrack.pause() + self.bankTrack = None + if self.bankGui: + self.bankGui.destroy() + self.bankGui = None + if self.bankDialog: + self.bankDialog.cleanup() + self.bankDialog = None + if self.hasLocalAvatar: + self.freeAvatar() + self.__removeToonJar() + self.ignoreAll() + DistributedFurnitureItem.DistributedFurnitureItem.disable(self) + return + + def delete(self): + self.notify.debug('delete') + DistributedFurnitureItem.DistributedFurnitureItem.delete(self) + + def __handleEnterSphere(self, collEntry): + if self.smoothStarted: + return + if self.hasLocalAvatar: + self.freeAvatar() + self.notify.debug('Entering Bank Sphere....') + self.acceptOnce(self.bankSphereExitEvent, self.__handleExitSphere) + self.ignore(self.bankSphereEnterEvent) + self.cr.playGame.getPlace().fsm.request('banking') + self.hasLocalAvatar = 1 + self.sendUpdate('avatarEnter', []) + + def __handleExitSphere(self, collEntry): + self.notify.debug('Exiting Bank Sphere....') + if self.bankTrack is not None: + self.bankTrack.pause() + self.bankTrack = None + if self.bankDialog is not None: + self.bankDialog.cleanup() + self.bankDialog = None + self.__handleBankDone(0) + return + + def __handleBankDone(self, transactionAmount): + self.notify.debug('__handleBankDone(transactionAmount=%s' % (transactionAmount,)) + self.sendUpdate('transferMoney', [transactionAmount]) + self.ignore(self.bankGuiDoneEvent) + self.ignore(self.bankSphereExitEvent) + if self.bankGui is not None: + self.bankGui.destroy() + self.bankGui = None + return + + def freeAvatar(self): + self.notify.debug('freeAvatar()') + if self.hasLocalAvatar: + base.localAvatar.posCamera(0, 0) + if base.cr.playGame.place != None: + base.cr.playGame.getPlace().setState('walk') + self.hasLocalAvatar = 0 + self.accept(self.bankSphereEnterEvent, self.__handleEnterSphere) + return + + def showBankGui(self): + if self.bankGui: + self.bankGui.destroy() + self.bankGui = BankGUI.BankGui(self.bankGuiDoneEvent) + self.accept(self.bankGuiDoneEvent, self.__handleBankDone) + + def setMovie(self, mode, avId, timestamp): + self.notify.debug('setMovie(mode=%s, avId=%s, timestamp=%s)' % (mode, avId, timestamp)) + timeStamp = globalClockDelta.localElapsedTime(timestamp) + isLocalToon = avId == base.localAvatar.doId + self.notify.info('setMovie: mode=%s, avId=%s, timeStamp=%s, isLocalToon=%s' % (mode, + avId, + timeStamp, + isLocalToon)) + if mode == BANK_MOVIE_CLEAR: + self.notify.debug('setMovie: clear') + elif mode == BANK_MOVIE_GUI: + self.notify.debug('setMovie: gui') + track = Sequence() + track.append(Func(self.__takeOutToonJar, avId)) + if isLocalToon: + track.append(Wait(3.0)) + track.append(Func(self.showBankGui)) + track.start() + self.bankTrack = track + elif mode == BANK_MOVIE_DEPOSIT: + self.notify.debug('setMovie: deposit') + self.__putAwayToonJar(avId) + elif mode == BANK_MOVIE_WITHDRAW: + self.notify.debug('setMovie: withdraw') + self.__putAwayToonJar(avId) + elif mode == BANK_MOVIE_NO_OP: + self.notify.debug('setMovie: no op') + self.__putAwayToonJar(avId) + elif mode == BANK_MOVIE_NOT_OWNER: + self.notify.debug('setMovie: not owner') + if isLocalToon: + self.bankDialog = TTDialog.TTDialog(dialogName='BankNotOwner', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedBankNotOwner, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog) + elif mode == BANK_MOVIE_NO_OWNER: + self.notify.debug('setMovie: no owner') + if isLocalToon: + self.bankDialog = TTDialog.TTDialog(dialogName='BankNoOwner', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedBankNoOwner, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog) + else: + self.notify.warning('unknown mode in setMovie: %s' % mode) + + def __clearDialog(self, event): + self.notify.debug('__clearDialog(event=%s)' % (event,)) + if self.bankDialog is not None: + self.bankDialog.cleanup() + self.bankDialog = None + self.freeAvatar() + return + + def __attachToonJar(self, toon): + self.__removeToonJar() + for hand in toon.getRightHands(): + self.jarLods.append(toon.jar.instanceTo(hand)) + + def __removeToonJar(self): + for jar in self.jarLods: + jar.removeNode() + + self.jarLods = [] + + def __takeOutToonJar(self, avId): + self.notify.debug('__takeOutToonJar(avId=%s)' % (avId,)) + toon = base.cr.doId2do.get(avId) + if toon == None: + return + track = Sequence() + index = self.item.furnitureType + scale = FurnitureTypes[index][FTScale] + walkToBank = Sequence(Func(toon.stopSmooth), Func(toon.loop, 'walk'), toon.posHprInterval(0.5, Point3(0, -3.125 * (scale + 0.2), 0), Point3(0, 0, 0), other=self, blendType='easeInOut'), Func(toon.loop, 'neutral'), Func(toon.startSmooth)) + track.append(walkToBank) + if not toon.jar: + toon.getJar() + self.__attachToonJar(toon) + jarAndBank = Parallel(LerpScaleInterval(toon.jar, 1.5, 1.0, blendType='easeOut'), ActorInterval(base.cr.doId2do[avId], 'bank', endTime=3.8)) + track.append(jarAndBank) + track.append(Func(base.cr.doId2do[avId].pingpong, 'bank', fromFrame=48, toFrame=92)) + track.start() + self.hasJarOut = 1 + return + + def __putAwayToonJar(self, avId): + self.notify.debug('__putAwayToonJar(avId=%s)' % (avId,)) + toon = base.cr.doId2do.get(avId) + if toon is None: + return + if not self.hasJarOut: + return + self.hasJarOut = 0 + if not toon.jar: + toon.getJar() + track = Sequence() + jarAndBank = Parallel(ActorInterval(base.cr.doId2do[avId], 'bank', startTime=2.0, endTime=0.0), LerpScaleInterval(toon.jar, 2.0, 0.0, blendType='easeIn')) + track.append(jarAndBank) + track.append(Func(self.__removeToonJar)) + track.append(Func(toon.removeJar)) + track.append(Func(toon.loop, 'neutral')) + if avId == base.localAvatar.doId: + track.append(Func(self.freeAvatar)) + track.start() + self.bankTrack = track + return diff --git a/toontown/estate/DistributedBankAI.py b/toontown/estate/DistributedBankAI.py new file mode 100755 index 00000000..aa7e5f63 --- /dev/null +++ b/toontown/estate/DistributedBankAI.py @@ -0,0 +1,76 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.estate.DistributedFurnitureItemAI import DistributedFurnitureItemAI +from direct.distributed.ClockDelta import * +import BankGlobals + +class DistributedBankAI(DistributedFurnitureItemAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedBankAI") + + def __init__(self, air, furnitureMgr, item): + DistributedFurnitureItemAI.__init__(self, air, furnitureMgr, item) + self.avId = None + self.movie = BankGlobals.BANK_MOVIE_CLEAR + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if not self.avId: + if not self.furnitureMgr.ownerId: + self.b_setMovie(BankGlobals.BANK_MOVIE_NO_OWNER, avId, globalClockDelta.getRealNetworkTime()) + return + elif self.furnitureMgr.ownerId != avId: + self.b_setMovie(BankGlobals.BANK_MOVIE_NOT_OWNER, avId, globalClockDelta.getRealNetworkTime()) + return + else: + self.avId = avId + self.b_setMovie(BankGlobals.BANK_MOVIE_GUI, avId, globalClockDelta.getRealNetworkTime()) + return + else: + if avId == self.avId: + self.air.writeServerEvent('suspicious', avId=avId, issue='Tried to use bank while already using it!') + self.sendUpdateToAvatarId(avId, 'freeAvatar', []) + + def freeAvatar(self): + pass + + def setMovie(self, mode, avId, time): + self.movie = mode + if self.movie != BankGlobals.BANK_MOVIE_CLEAR: + taskMgr.doMethodLater(2.0, self.clearMovie, 'clear-movie-%d' % self.getDoId()) + + def clearMovie(self, task): + self.b_setMovie(BankGlobals.BANK_MOVIE_CLEAR, 0, globalClockDelta.getRealNetworkTime()) + + def b_setMovie(self, mode, avId, time): + self.setMovie(mode, avId, time) + self.d_setMovie(mode, avId, time) + + def d_setMovie(self, mode, avId, time): + self.sendUpdate('setMovie', [mode, avId, time]) + + def transferMoney(self, amount): + avId = self.air.getAvatarIdFromSender() + if avId != self.avId: + self.air.writeServerEvent('suspicious', avId=avId, issue='Tried to transfer money while not using a bank!') + return + av = self.air.doId2do.get(avId) + if not av: + self.air.writeServerEvent('suspicious', avId=avId, issue='Tried to transfer money while not on the AI!') + return + if amount == 0: # No transfer needed. + self.b_setMovie(BankGlobals.BANK_MOVIE_NO_OP, avId, globalClockDelta.getRealNetworkTime()) + elif amount > 0: + self.b_setMovie(BankGlobals.BANK_MOVIE_DEPOSIT, avId, globalClockDelta.getRealNetworkTime()) + if av.money < amount: + self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to deposit more money than they have!') + else: + av.b_setMoney(av.money - amount) + av.b_setBankMoney(av.bankMoney + amount) + else: + self.b_setMovie(BankGlobals.BANK_MOVIE_WITHDRAW, avId, globalClockDelta.getRealNetworkTime()) + if av.bankMoney + amount < 0: + self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to withdraw more money than they have!') + else: + av.b_setMoney(av.money - amount) + av.b_setBankMoney(av.bankMoney + amount) + + self.avId = None diff --git a/toontown/estate/DistributedCannon.py b/toontown/estate/DistributedCannon.py new file mode 100755 index 00000000..2678a704 --- /dev/null +++ b/toontown/estate/DistributedCannon.py @@ -0,0 +1,1581 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownTimer +from direct.task.Task import Task +from toontown.minigame import Trajectory +import math +from toontown.toon import ToonHead +from toontown.effects import Splash +from toontown.effects import DustCloud +from toontown.minigame import CannonGameGlobals +import CannonGlobals +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from direct.distributed import DistributedObject +from toontown.effects import Wake +from direct.controls.ControlManager import CollisionHandlerRayStart + +from otp.nametag.NametagFloat3d import NametagFloat3d +from otp.nametag.Nametag import Nametag + +LAND_TIME = 2 +WORLD_SCALE = 2.0 +GROUND_SCALE = 1.4 * WORLD_SCALE +CANNON_SCALE = 1.0 +FAR_PLANE_DIST = 600 * WORLD_SCALE +GROUND_PLANE_MIN = -15 +CANNON_Y = -int(CannonGameGlobals.TowerYRange / 2 * 1.3) +CANNON_X_SPACING = 12 +CANNON_Z = 20 +CANNON_ROTATION_MIN = -55 +CANNON_ROTATION_MAX = 50 +CANNON_ROTATION_VEL = 15.0 +CANNON_ANGLE_MIN = 15 +CANNON_ANGLE_MAX = 85 +CANNON_ANGLE_VEL = 15.0 +CANNON_MOVE_UPDATE_FREQ = 0.5 +CAMERA_PULLBACK_MIN = 20 +CAMERA_PULLBACK_MAX = 40 +MAX_LOOKAT_OFFSET = 80 +TOON_TOWER_THRESHOLD = 150 +SHADOW_Z_OFFSET = 0.5 +TOWER_HEIGHT = 43.85 +TOWER_RADIUS = 10.5 +BUCKET_HEIGHT = 36 +TOWER_Y_RANGE = CannonGameGlobals.TowerYRange +TOWER_X_RANGE = int(TOWER_Y_RANGE / 2.0) +INITIAL_VELOCITY = 80.0 +WHISTLE_SPEED = INITIAL_VELOCITY * 0.35 + +class DistributedCannon(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCannon') + font = ToontownGlobals.getToonFont() + LOCAL_CANNON_MOVE_TASK = 'localCannonMoveTask' + REWARD_COUNTDOWN_TASK = 'cannonGameRewardCountdown' + HIT_GROUND = 0 + HIT_TOWER = 1 + HIT_WATER = 2 + FIRE_KEY = 'control' + UP_KEY = 'arrow_up' + DOWN_KEY = 'arrow_down' + LEFT_KEY = 'arrow_left' + RIGHT_KEY = 'arrow_right' + BUMPER_KEY = 'delete' + BUMPER_KEY2 = 'insert' + INTRO_TASK_NAME = 'CannonGameIntro' + INTRO_TASK_NAME_CAMERA_LERP = 'CannonGameIntroCamera' + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.avId = 0 + self.av = None + self.localToonShooting = 0 + self.nodePath = None + self.collSphere = None + self.collNode = None + self.collNodePath = None + self.madeGui = 0 + self.gui = None + self.cannonLocation = None + self.cannonPosition = None + self.cannon = None + self.toonModel = None + self.shadowNode = None + self.toonHead = None + self.toonScale = None + self.estateId = None + self.targetId = None + self.splash = None + self.dustCloud = None + self.model_Created = 0 + self.lastWakeTime = 0 + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.hitBumper = 0 + self.hitTarget = 0 + self.lastPos = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.vel = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + self.hitTrack = None + self.cTrav = None + self.cRay = None + self.cRayNode = None + self.cRayNodePath = None + self.lifter = None + self.flyColNode = None + self.flyColNodePath = None + self.bumperCol = None + self.cannonMoving = 0 + self.inWater = 0 + self.localAvId = base.localAvatar.doId + self.nextState = None + self.nextKey = None + self.cannonsActive = 0 + self.codeFSM = ClassicFSM.ClassicFSM('CannonCode', [State.State('init', self.enterInit, self.exitInit, ['u1', 'init']), + State.State('u1', self.enteru1, self.exitu1, ['u2', 'init']), + State.State('u2', self.enteru2, self.exitu2, ['d3', 'init']), + State.State('d3', self.enterd3, self.exitd3, ['d4', 'init']), + State.State('d4', self.enterd4, self.exitd4, ['l5', 'init']), + State.State('l5', self.enterl5, self.exitl5, ['r6', 'init']), + State.State('r6', self.enterr6, self.exitr6, ['l7', 'init']), + State.State('l7', self.enterl7, self.exitl7, ['r8', 'init']), + State.State('r8', self.enterr8, self.exitr8, ['acceptCode', 'init']), + State.State('acceptCode', self.enterAcceptCode, self.exitAcceptCode, ['init', 'final']), + State.State('final', self.enterFinal, self.exitFinal, [])], 'init', 'final') + self.codeFSM.enterInitialState() + self.curPinballScore = 0 + self.curPinballMultiplier = 1 + return + + def disable(self): + self.__unmakeGui() + taskMgr.remove(self.taskNameFireCannon) + taskMgr.remove(self.taskNameShoot) + taskMgr.remove(self.taskNameFly) + taskMgr.remove(self.taskNameSmoke) + self.ignoreAll() + self.setMovie(CannonGlobals.CANNON_MOVIE_CLEAR, 0) + self.nodePath.detachNode() + if self.hitTrack: + self.hitTrack.finish() + del self.hitTrack + self.hitTrack = None + DistributedObject.DistributedObject.disable(self) + return + + def __unmakeGui(self): + if not self.madeGui: + return + self.aimPad.destroy() + del self.aimPad + del self.fireButton + del self.upButton + del self.downButton + del self.leftButton + del self.rightButton + self.madeGui = 0 + + def generateInit(self): + DistributedObject.DistributedObject.generateInit(self) + self.taskNameFireCannon = self.taskName('fireCannon') + self.taskNameShoot = self.taskName('shootTask') + self.taskNameSmoke = self.taskName('smokeTask') + self.taskNameFly = self.taskName('flyTask') + self.nodePath = NodePath(self.uniqueName('Cannon')) + self.load() + self.activateCannons() + self.listenForCode() + + def listenForCode(self): + self.accept(self.UP_KEY + '-up', self.__upKeyCode) + self.accept(self.DOWN_KEY + '-up', self.__downKeyCode) + self.accept(self.LEFT_KEY + '-up', self.__leftKeyCode) + self.accept(self.RIGHT_KEY + '-up', self.__rightKeyCode) + + def ignoreCode(self): + self.ignore(self.UP_KEY + '-up') + self.ignore(self.DOWN_KEY + '-up') + self.ignore(self.LEFT_KEY + '-up') + self.ignore(self.RIGHT_KEY + '-up') + + def activateCannons(self): + if not self.cannonsActive: + self.cannonsActive = 1 + self.onstage() + self.nodePath.reparentTo(self.getParentNodePath()) + self.accept(self.uniqueName('enterCannonSphere'), self.__handleEnterSphere) + + def deActivateCannons(self): + if self.cannonsActive: + self.cannonsActive = 0 + self.offstage() + self.nodePath.reparentTo(hidden) + self.ignore(self.uniqueName('enterCannonSphere')) + + def delete(self): + self.offstage() + self.unload() + DistributedObject.DistributedObject.delete(self) + + def __handleEnterSphere(self, collEntry): + self.notify.debug('collEntry: %s' % collEntry) + base.cr.playGame.getPlace().setState('fishing') + self.d_requestEnter() + + def d_requestEnter(self): + self.sendUpdate('requestEnter', []) + + def requestExit(self): + self.notify.debug('requestExit') + base.localAvatar.reparentTo(render) + base.cr.playGame.getPlace().setState('walk') + + def getSphereRadius(self): + return 1.5 + + def getParentNodePath(self): + return base.cr.playGame.hood.loader.geom + + def setEstateId(self, estateId): + self.estateId = estateId + + def setTargetId(self, targetId): + self.notify.debug('setTargetId %d' % targetId) + self.targetId = targetId + + def setPosHpr(self, x, y, z, h, p, r): + self.nodePath.setPosHpr(x, y, z, h, p, r) + + def setMovie(self, mode, avId): + wasLocalToon = self.localToonShooting + self.avId = avId + if mode == CannonGlobals.CANNON_MOVIE_CLEAR: + self.listenForCode() + self.setLanded() + elif mode == CannonGlobals.CANNON_MOVIE_LANDED: + self.setLanded() + elif mode == CannonGlobals.CANNON_MOVIE_FORCE_EXIT: + self.exitCannon(self.avId) + self.setLanded() + elif mode == CannonGlobals.CANNON_MOVIE_LOAD: + self.ignoreCode() + if self.avId == base.localAvatar.doId: + base.localAvatar.pose('lose', 110) + base.localAvatar.pose('slip-forward', 25) + base.cr.playGame.getPlace().setState('fishing') + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.collisionsOff() + base.setCellsAvailable([base.bottomCells[3], base.bottomCells[4]], 0) + base.setCellsAvailable([base.rightCells[1]], 0) + self.localToonShooting = 1 + self.__makeGui() + camera.reparentTo(self.barrel) + camera.setPos(0.5, -2, 2.5) + self.curPinballScore = 0 + self.curPinballMultiplier = 1 + self.incrementPinballInfo(0, 0) + if self.avId in self.cr.doId2do: + self.av = self.cr.doId2do[self.avId] + self.acceptOnce(self.av.uniqueName('disable'), self.__avatarGone) + self.av.stopSmooth() + self.__createToonModels() + else: + self.notify.warning('Unknown avatar %d in cannon %d' % (self.avId, self.doId)) + if wasLocalToon and not self.localToonShooting: + base.setCellsAvailable([base.bottomCells[3], base.bottomCells[4]], 1) + base.setCellsAvailable([base.rightCells[1]], 1) + + def __avatarGone(self): + self.setMovie(CannonGlobals.CANNON_MOVIE_CLEAR, 0) + + def load(self): + self.cannon = loader.loadModel('phase_4/models/minigames/toon_cannon') + self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.shadowNode = hidden.attachNewNode('dropShadow') + self.shadow.copyTo(self.shadowNode) + self.smoke = loader.loadModel('phase_4/models/props/test_clouds') + self.smoke.setBillboardPointEye() + self.cannon.setScale(CANNON_SCALE) + self.shadowNode.setColor(0, 0, 0, 0.5) + self.shadowNode.setBin('fixed', 0, 1) + self.splash = Splash.Splash(render) + self.dustCloud = DustCloud.DustCloud(render) + self.dustCloud.setBillboardPointEye() + self.sndCannonMove = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.ogg') + self.sndCannonFire = base.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.ogg') + self.sndHitGround = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg') + self.sndHitTower = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_tower.ogg') + self.sndHitWater = base.loadSfx('phase_4/audio/sfx/MG_cannon_splash.ogg') + self.sndWhizz = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg') + self.sndWin = base.loadSfx('phase_4/audio/sfx/MG_win.ogg') + self.sndHitHouse = base.loadSfx('phase_5/audio/sfx/AA_drop_sandbag.ogg') + self.collSphere = CollisionSphere(0, 0, 0, self.getSphereRadius()) + self.collSphere.setTangible(1) + self.collNode = CollisionNode(self.uniqueName('CannonSphere')) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.nodePath.attachNewNode(self.collNode) + self.loadCannonBumper() + + def setupMovingShadow(self): + self.cTrav = base.cTrav + self.cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0) + self.cRayNode = CollisionNode('cRayNode') + self.cRayNode.addSolid(self.cRay) + self.cRayNodePath = self.shadowNode.attachNewNode(self.cRayNode) + self.cRayNodePath.hide() + self.cRayBitMask = ToontownGlobals.FloorBitmask + self.cRayNode.setFromCollideMask(self.cRayBitMask) + self.cRayNode.setIntoCollideMask(BitMask32.allOff()) + self.lifter = CollisionHandlerFloor() + self.lifter.setOffset(ToontownGlobals.FloorOffset) + self.lifter.setReach(20.0) + self.enableRaycast(1) + + def enableRaycast(self, enable = 1): + if not self.cTrav or not hasattr(self, 'cRayNode') or not self.cRayNode: + return + self.notify.debug('-------enabling raycast--------') + self.cTrav.removeCollider(self.cRayNodePath) + if enable: + self.cTrav.addCollider(self.cRayNodePath, self.lifter) + + def __makeGui(self): + if self.madeGui: + return + guiModel = 'phase_4/models/gui/cannon_game_gui' + cannonGui = loader.loadModel(guiModel) + self.aimPad = DirectFrame(image=cannonGui.find('**/CannonFire_PAD'), relief=None, pos=(0.7, 0, -0.553333), scale=0.8) + cannonGui.removeNode() + self.fireButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Fire_Btn_UP'), (guiModel, '**/Fire_Btn_DN'), (guiModel, '**/Fire_Btn_RLVR')), relief=None, pos=(0.0115741, 0, 0.00505051), scale=1.0, command=self.__firePressed) + self.upButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0115741, 0, 0.221717)) + self.downButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0136112, 0, -0.210101), image_hpr=(0, 0, 180)) + self.leftButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(-0.199352, 0, -0.000505269), image_hpr=(0, 0, -90)) + self.rightButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.219167, 0, -0.00101024), image_hpr=(0, 0, 90)) + self.aimPad.setColor(1, 1, 1, 0.9) + + def bindButton(button, upHandler, downHandler): + button.bind(DGG.B1PRESS, lambda x, handler = upHandler: handler()) + button.bind(DGG.B1RELEASE, lambda x, handler = downHandler: handler()) + + bindButton(self.upButton, self.__upPressed, self.__upReleased) + bindButton(self.downButton, self.__downPressed, self.__downReleased) + bindButton(self.leftButton, self.__leftPressed, self.__leftReleased) + bindButton(self.rightButton, self.__rightPressed, self.__rightReleased) + self.__enableAimInterface() + self.madeGui = 1 + return + + def __unmakeGui(self): + self.notify.debug('__unmakeGui') + if not self.madeGui: + return + self.__disableAimInterface() + self.upButton.unbind(DGG.B1PRESS) + self.upButton.unbind(DGG.B1RELEASE) + self.downButton.unbind(DGG.B1PRESS) + self.downButton.unbind(DGG.B1RELEASE) + self.leftButton.unbind(DGG.B1PRESS) + self.leftButton.unbind(DGG.B1RELEASE) + self.rightButton.unbind(DGG.B1PRESS) + self.rightButton.unbind(DGG.B1RELEASE) + self.aimPad.destroy() + del self.aimPad + del self.fireButton + del self.upButton + del self.downButton + del self.leftButton + del self.rightButton + self.madeGui = 0 + + def unload(self): + self.ignoreCode() + del self.codeFSM + if self.cannon: + self.cannon.removeNode() + self.cannon = None + if self.shadowNode != None: + self.shadowNode.removeNode() + del self.shadowNode + if self.splash != None: + self.splash.destroy() + del self.splash + if self.dustCloud != None: + self.dustCloud.destroy() + del self.dustCloud + del self.sndCannonMove + del self.sndCannonFire + del self.sndHitHouse + del self.sndHitGround + del self.sndHitTower + del self.sndHitWater + del self.sndWhizz + del self.sndWin + self.bumperCol = None + taskMgr.remove(self.uniqueName('BumperON')) + if self.av: + self.__resetToon(self.av) + self.av.loop('neutral') + self.av.setPlayRate(1.0, 'run') + if hasattr(self.av, 'nametag'): + self.av.nametag.removeNametag(self.toonHead.tag) + if self.toonHead != None: + self.toonHead.stopBlink() + self.toonHead.stopLookAroundNow() + self.toonHead.delete() + self.toonHead = None + if self.toonModel != None: + self.toonModel.removeNode() + self.toonModel = None + del self.toonScale + del self.cannonLocation + del self.cRay + del self.cRayNode + if self.cRayNodePath: + self.cRayNodePath.removeNode() + del self.cRayNodePath + del self.lifter + self.enableRaycast(0) + return + + def onstage(self): + self.__createCannon() + self.cannon.reparentTo(self.nodePath) + self.splash.reparentTo(render) + self.dustCloud.reparentTo(render) + + def offstage(self): + if self.cannon: + self.cannon.reparentTo(hidden) + if self.splash: + self.splash.reparentTo(hidden) + self.splash.stop() + if self.dustCloud: + self.dustCloud.reparentTo(hidden) + self.dustCloud.stop() + + def __createCannon(self): + self.barrel = self.cannon.find('**/cannon') + self.cannonLocation = Point3(0, 0, 0.025) + self.cannonPosition = [0, CANNON_ANGLE_MIN] + self.cannon.setPos(self.cannonLocation) + self.__updateCannonPosition(self.avId) + + def __createToonModels(self): + self.model_Created = 1 + toon = self.av + self.toonScale = toon.getScale() + toon.useLOD(1000) + toonParent = render.attachNewNode('toonOriginChange') + toon.wrtReparentTo(toonParent) + toon.setPosHpr(0, 0, -(toon.getHeight() / 2.0), 0, -90, 0) + self.toonModel = toonParent + self.toonHead = ToonHead.ToonHead() + self.toonHead.setupHead(self.av.style) + self.toonHead.reparentTo(hidden) + tag = NametagFloat3d() + tag.setContents(Nametag.CSpeech | Nametag.CThought) + tag.setBillboardOffset(0) + tag.setAvatar(self.toonHead) + toon.nametag.addNametag(tag) + tagPath = self.toonHead.attachNewNode(tag) + tagPath.setPos(0, 0, 1) + self.toonHead.tag = tag + self.__loadToonInCannon() + self.av.dropShadow.hide() + self.dropShadow = self.shadowNode.copyTo(hidden) + + def __destroyToonModels(self): + if self.av != None: + self.av.dropShadow.show() + if self.dropShadow != None: + self.dropShadow.removeNode() + self.dropShadow = None + self.hitBumper = 0 + self.hitTarget = 0 + self.angularVel = 0 + self.vel = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.lastPos = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + self.av = None + self.lastWakeTime = 0 + self.localToonShooting = 0 + if self.toonHead != None: + self.toonHead.reparentTo(hidden) + self.toonHead.stopBlink() + self.toonHead.stopLookAroundNow() + self.toonHead.delete() + self.toonHead = None + if self.toonModel != None: + self.toonModel.removeNode() + self.toonModel = None + self.model_Created = 0 + return + + def updateCannonPosition(self, avId, zRot, angle): + if avId != self.localAvId: + self.cannonPosition = [zRot, angle] + self.__updateCannonPosition(avId) + + def setCannonWillFire(self, avId, fireTime, zRot, angle, timestamp): + self.notify.debug('setCannonWillFire: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle) + ', time=' + str(fireTime)) + if not self.model_Created: + self.notify.warning("We walked into the zone mid-flight, so we won't see it") + return + self.cannonPosition[0] = zRot + self.cannonPosition[1] = angle + self.__updateCannonPosition(avId) + task = Task(self.__fireCannonTask) + task.avId = avId + ts = globalClockDelta.localElapsedTime(timestamp) + task.fireTime = fireTime - ts + if task.fireTime < 0.0: + task.fireTime = 0.0 + taskMgr.add(task, self.taskNameFireCannon) + + def exitCannon(self, avId): + self.__unmakeGui() + if self.avId == avId: + if self.av: + self.__resetToonToCannon(self.av) + + def __enableAimInterface(self): + self.aimPad.show() + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + self.accept(self.UP_KEY, self.__upKeyPressed) + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.accept(self.BUMPER_KEY, self.__bumperKeyPressed) + self.accept(self.BUMPER_KEY2, self.__bumperKeyPressed) + self.__spawnLocalCannonMoveTask() + + def __disableAimInterface(self): + self.aimPad.hide() + self.ignore(self.FIRE_KEY) + self.ignore(self.UP_KEY) + self.ignore(self.DOWN_KEY) + self.ignore(self.LEFT_KEY) + self.ignore(self.RIGHT_KEY) + self.ignore(self.FIRE_KEY + '-up') + self.ignore(self.UP_KEY + '-up') + self.ignore(self.DOWN_KEY + '-up') + self.ignore(self.LEFT_KEY + '-up') + self.ignore(self.RIGHT_KEY + '-up') + self.__killLocalCannonMoveTask() + + def __fireKeyPressed(self): + self.ignore(self.FIRE_KEY) + self.accept(self.FIRE_KEY + '-up', self.__fireKeyReleased) + self.__firePressed() + + def __upKeyPressed(self): + self.ignore(self.UP_KEY) + self.accept(self.UP_KEY + '-up', self.__upKeyReleased) + self.__upPressed() + + def __downKeyPressed(self): + self.ignore(self.DOWN_KEY) + self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased) + self.__downPressed() + + def __leftKeyPressed(self): + self.ignore(self.LEFT_KEY) + self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased) + self.__leftPressed() + + def __rightKeyPressed(self): + self.ignore(self.RIGHT_KEY) + self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased) + self.__rightPressed() + + def __fireKeyReleased(self): + self.ignore(self.FIRE_KEY + '-up') + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + self.__fireReleased() + + def __leftKeyReleased(self): + self.ignore(self.LEFT_KEY + '-up') + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.handleCodeKey('left') + self.__leftReleased() + + def __rightKeyReleased(self): + self.ignore(self.RIGHT_KEY + '-up') + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.handleCodeKey('right') + self.__rightReleased() + + def __upKeyReleased(self): + self.ignore(self.UP_KEY + '-up') + self.accept(self.UP_KEY, self.__upKeyPressed) + self.__upReleased() + + def __downKeyReleased(self): + self.ignore(self.DOWN_KEY + '-up') + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.handleCodeKey('down') + self.__downReleased() + + def __upKeyCode(self): + self.handleCodeKey('up') + + def __downKeyCode(self): + self.handleCodeKey('down') + + def __rightKeyCode(self): + self.handleCodeKey('right') + + def __leftKeyCode(self): + self.handleCodeKey('left') + + def __firePressed(self): + self.notify.debug('fire pressed') + self.__broadcastLocalCannonPosition() + self.__unmakeGui() + self.sendUpdate('setCannonLit', [self.cannonPosition[0], self.cannonPosition[1]]) + + def __upPressed(self): + self.notify.debug('up pressed') + self.upPressed = self.__enterControlActive(self.upPressed) + + def __downPressed(self): + self.notify.debug('down pressed') + self.downPressed = self.__enterControlActive(self.downPressed) + + def __leftPressed(self): + self.notify.debug('left pressed') + self.leftPressed = self.__enterControlActive(self.leftPressed) + + def __rightPressed(self): + self.notify.debug('right pressed') + self.rightPressed = self.__enterControlActive(self.rightPressed) + + def __upReleased(self): + self.notify.debug('up released') + self.upPressed = self.__exitControlActive(self.upPressed) + + def __downReleased(self): + self.notify.debug('down released') + self.downPressed = self.__exitControlActive(self.downPressed) + + def __leftReleased(self): + self.notify.debug('left released') + self.leftPressed = self.__exitControlActive(self.leftPressed) + + def __rightReleased(self): + self.notify.debug('right released') + self.rightPressed = self.__exitControlActive(self.rightPressed) + + def __enterControlActive(self, control): + return control + 1 + + def __exitControlActive(self, control): + return max(0, control - 1) + + def __spawnLocalCannonMoveTask(self): + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.cannonMoving = 0 + task = Task(self.__localCannonMoveTask) + task.lastPositionBroadcastTime = 0.0 + taskMgr.add(task, self.LOCAL_CANNON_MOVE_TASK) + + def __killLocalCannonMoveTask(self): + taskMgr.remove(self.LOCAL_CANNON_MOVE_TASK) + if self.cannonMoving: + self.sndCannonMove.stop() + + def __localCannonMoveTask(self, task): + pos = self.cannonPosition + oldRot = pos[0] + oldAng = pos[1] + rotVel = 0 + if self.leftPressed: + rotVel += CANNON_ROTATION_VEL + if self.rightPressed: + rotVel -= CANNON_ROTATION_VEL + pos[0] += rotVel * globalClock.getDt() + if pos[0] < CANNON_ROTATION_MIN: + pos[0] = CANNON_ROTATION_MIN + elif pos[0] > CANNON_ROTATION_MAX: + pos[0] = CANNON_ROTATION_MAX + angVel = 0 + if self.upPressed: + angVel += CANNON_ANGLE_VEL + if self.downPressed: + angVel -= CANNON_ANGLE_VEL + pos[1] += angVel * globalClock.getDt() + if pos[1] < CANNON_ANGLE_MIN: + pos[1] = CANNON_ANGLE_MIN + elif pos[1] > CANNON_ANGLE_MAX: + pos[1] = CANNON_ANGLE_MAX + if oldRot != pos[0] or oldAng != pos[1]: + if self.cannonMoving == 0: + self.cannonMoving = 1 + base.playSfx(self.sndCannonMove, looping=1) + self.__updateCannonPosition(self.localAvId) + if task.time - task.lastPositionBroadcastTime > CANNON_MOVE_UPDATE_FREQ: + task.lastPositionBroadcastTime = task.time + self.__broadcastLocalCannonPosition() + elif self.cannonMoving: + self.cannonMoving = 0 + self.sndCannonMove.stop() + self.__broadcastLocalCannonPosition() + print 'Cannon Rot:%s Angle:%s' % (pos[0], pos[1]) + return Task.cont + + def __broadcastLocalCannonPosition(self): + self.sendUpdate('setCannonPosition', [self.cannonPosition[0], self.cannonPosition[1]]) + + def __updateCannonPosition(self, avId): + self.cannon.setHpr(self.cannonPosition[0], 0.0, 0.0) + self.barrel.setHpr(0.0, self.cannonPosition[1], 0.0) + maxP = 90 + newP = self.barrel.getP() + yScale = 1 - 0.5 * float(newP) / maxP + shadow = self.cannon.find('**/square_drop_shadow') + shadow.setScale(1, yScale, 1) + + def __getCameraPositionBehindCannon(self): + return Point3(self.cannonLocationDict[self.localAvId][0], CANNON_Y - 5.0, CANNON_Z + 7) + + def __putCameraBehindCannon(self): + camera.setPos(self.__getCameraPositionBehindCannon()) + camera.setHpr(0, 0, 0) + + def __loadToonInCannon(self): + self.toonModel.reparentTo(hidden) + self.toonHead.startBlink() + self.toonHead.startLookAround() + self.toonHead.reparentTo(self.barrel) + self.toonHead.setPosHpr(0, 6, 0, 0, -45, 0) + sc = self.toonScale + self.toonHead.setScale(render, sc[0], sc[1], sc[2]) + self.toonModel.setPos(self.toonHead.getPos(render)) + + def __toRadians(self, angle): + return angle * 2.0 * math.pi / 360.0 + + def __toDegrees(self, angle): + return angle * 360.0 / (2.0 * math.pi) + + def __calcFlightResults(self, avId, launchTime): + head = self.toonHead + startPos = head.getPos(render) + startHpr = head.getHpr(render) + hpr = self.barrel.getHpr(render) + rotation = self.__toRadians(hpr[0]) + angle = self.__toRadians(hpr[1]) + horizVel = INITIAL_VELOCITY * math.cos(angle) + xVel = horizVel * -math.sin(rotation) + yVel = horizVel * math.cos(rotation) + zVel = INITIAL_VELOCITY * math.sin(angle) + startVel = Vec3(xVel, yVel, zVel) + trajectory = Trajectory.Trajectory(launchTime, startPos, startVel) + self.trajectory = trajectory + hitTreasures = self.__calcHitTreasures(trajectory) + timeOfImpact, hitWhat = self.__calcToonImpact(trajectory) + return startPos, startHpr, startVel, trajectory, 3 * timeOfImpact, hitWhat + + def __fireCannonTask(self, task): + launchTime = task.fireTime + avId = task.avId + self.inWater = 0 + if not self.toonHead: + return Task.done + startPos, startHpr, startVel, trajectory, timeOfImpact, hitWhat = self.__calcFlightResults(avId, launchTime) + + self.notify.debug('start position: ' + str(startPos)) + self.notify.debug('start velocity: ' + str(startVel)) + self.notify.debug('time of launch: ' + str(launchTime)) + self.notify.debug('time of impact: ' + str(timeOfImpact)) + self.notify.debug('location of impact: ' + str(trajectory.getPos(timeOfImpact))) + if hitWhat == self.HIT_WATER: + self.notify.debug('toon will land in the water') + elif hitWhat == self.HIT_TOWER: + self.notify.debug('toon will hit the tower') + else: + self.notify.debug('toon will hit the ground') + head = self.toonHead + head.stopBlink() + head.stopLookAroundNow() + head.reparentTo(hidden) + av = self.toonModel + av.reparentTo(render) + print 'start Pos%s Hpr%s' % (startPos, startHpr) + av.setPos(startPos) + barrelHpr = self.barrel.getHpr(render) + place = base.cr.playGame.getPlace() + if self.av == base.localAvatar: + place.fsm.request('stopped') + av.setHpr(startHpr) + avatar = self.av + avatar.loop('swim') + avatar.setPosHpr(0, 0, -(avatar.getHeight() / 2.0), 0, 0, 0) + info = {} + info['avId'] = avId + info['trajectory'] = trajectory + info['launchTime'] = launchTime + info['timeOfImpact'] = timeOfImpact + info['hitWhat'] = hitWhat + info['toon'] = self.toonModel + info['hRot'] = self.cannonPosition[0] + info['haveWhistled'] = 0 + info['maxCamPullback'] = CAMERA_PULLBACK_MIN + if self.localToonShooting: + camera.reparentTo(self.av) + camera.setP(45.0) + camera.setZ(-10.0) + self.flyColSphere = CollisionSphere(0, 0, self.av.getHeight() / 2.0, 1.0) + self.flyColNode = CollisionNode(self.uniqueName('flySphere')) + self.flyColNode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.FloorBitmask) + self.flyColNode.addSolid(self.flyColSphere) + self.flyColNodePath = self.av.attachNewNode(self.flyColNode) + self.flyColNodePath.setColor(1, 0, 0, 1) + self.handler = CollisionHandlerEvent() + self.handler.setInPattern(self.uniqueName('cannonHit')) + base.cTrav.addCollider(self.flyColNodePath, self.handler) + self.accept(self.uniqueName('cannonHit'), self.__handleCannonHit) + shootTask = Task(self.__shootTask, self.taskNameShoot) + smokeTask = Task(self.__smokeTask, self.taskNameSmoke) + flyTask = Task(self.__flyTask, self.taskNameFly) + shootTask.info = info + flyTask.info = info + seqTask = Task.sequence(shootTask, smokeTask, flyTask) + if self.av == base.localAvatar: + print 'disable controls' + base.localAvatar.disableAvatarControls() + taskMgr.add(seqTask, self.taskName('flyingToon') + '-' + str(avId)) + self.acceptOnce(self.uniqueName('stopFlyTask'), self.__stopFlyTask) + return Task.done + + def __stopFlyTask(self, avId): + taskMgr.remove(self.taskName('flyingToon') + '-' + str(avId)) + + def b_setLanded(self): + self.d_setLanded() + + def d_setLanded(self): + self.notify.debug('localTOonshooting = %s' % self.localToonShooting) + if self.localToonShooting: + self.sendUpdate('setLanded', []) + + def setLanded(self): + self.removeAvFromCannon() + + def removeAvFromCannon(self): + place = base.cr.playGame.getPlace() + self.notify.debug('self.inWater = %s' % self.inWater) + if place: + if not hasattr(place, 'fsm'): + return + placeState = place.fsm.getCurrentState().getName() + print placeState + if (self.inWater or place.toonSubmerged) and placeState != 'fishing': + if self.av != None: + self.av.startSmooth() + self.__destroyToonModels() + return + self.inWater = 0 + if self.av != None: + self.__stopCollisionHandler(self.av) + self.av.resetLOD() + if self.av == base.localAvatar: + if place and not self.inWater: + place.fsm.request('walk') + self.av.setPlayRate(1.0, 'run') + self.av.nametag.removeNametag(self.toonHead.tag) + if self.av.getParent().getName() == 'toonOriginChange': + self.av.wrtReparentTo(render) + self.__setToonUpright(self.av) + if self.av == base.localAvatar: + self.av.startPosHprBroadcast() + self.av.startSmooth() + self.av.setScale(1, 1, 1) + if self.av == base.localAvatar: + print 'enable controls' + base.localAvatar.enableAvatarControls() + self.ignore(self.av.uniqueName('disable')) + self.__destroyToonModels() + return + + def __stopCollisionHandler(self, avatar): + if avatar: + avatar.loop('neutral') + if self.flyColNode: + self.flyColNode = None + if avatar == base.localAvatar: + avatar.collisionsOn() + self.flyColSphere = None + if self.flyColNodePath: + base.cTrav.removeCollider(self.flyColNodePath) + self.flyColNodePath.removeNode() + self.flyColNodePath = None + self.handler = None + return + + def __handleCannonHit(self, collisionEntry): + if self.av == None or self.flyColNode == None: + return + + hitNode = collisionEntry.getIntoNode().getName() + self.notify.debug('hitNode = %s' % hitNode) + self.notify.debug('hitNodePath.getParent = %s' % collisionEntry.getIntoNodePath().getParent()) + + self.vel = self.trajectory.getVel(self.t) + vel = self.trajectory.getVel(self.t) + vel.normalize() + + if self.hitBumper: + vel = self.lastVel * 1 + vel.normalize() + self.notify.debug('normalized vel=%s' % vel) + + solid = collisionEntry.getInto() + intoNormal = collisionEntry.getSurfaceNormal(collisionEntry.getIntoNodePath()) + self.notify.debug('old intoNormal = %s' % intoNormal) + intoNormal = collisionEntry.getSurfaceNormal(render) + self.notify.debug('new intoNormal = %s' % intoNormal) + + hitPylonAboveWater = False + hitPylonBelowWater = False + if hitNode in ['pier_pylon_collisions_1', 'pier_pylon_collisions_3']: + if collisionEntry.getSurfacePoint(render)[2] > 0: + hitPylonAboveWater = True + self.notify.debug('hitPylonAboveWater = True') + else: + hitPylonBelowWater = True + self.notify.debug('hitPylonBelowWater = True') + + hitNormal = intoNormal + if ( hitNode.find('cSphere') == 0 or + hitNode.find('treasureSphere') == 0 or + hitNode.find('prop') == 0 or + hitNode.find('distAvatarCollNode') == 0 or + hitNode.find('CannonSphere') == 0 or + hitNode.find('plotSphere') == 0 or + hitNode.find('flySphere') == 0 or + hitNode.find('mailboxSphere') == 0 or + hitNode.find('FishingSpotSphere') == 0 or + hitNode == 'gagtree_collision' or + hitNode == 'sign_collision' or + hitNode == 'FlowerSellBox' or + hitPylonBelowWater): + self.notify.debug('--------------hit and ignoring %s' % hitNode) + return + + if vel.dot(hitNormal) > 0 and not hitNode == 'collision_roof' and not hitNode == 'collision_fence': + self.notify.debug('--------------hit and ignoring backfacing %s, dot=%s' % (hitNode, vel.dot(hitNormal))) + return + + intoNode = collisionEntry.getIntoNodePath() + bumperNodes = ['collision_house', + 'collision_fence', + 'targetSphere', + 'collision_roof', + 'collision_cannon_bumper', + 'statuaryCol'] + cloudBumpers = ['cloudSphere-0'] + bumperNodes += cloudBumpers + + if hitNode not in bumperNodes: + self.__stopCollisionHandler(self.av) + self.__stopFlyTask(self.avId) + self.notify.debug('stopping flying since we hit %s' % hitNode) + if self.hitTarget == 0: + messenger.send('missedTarget') + else: + if hitNode == 'collision_house': + self.__hitHouse(self.av, collisionEntry) + elif hitNode == 'collision_fence': + self.__hitFence(self.av, collisionEntry) + elif hitNode == 'collision_roof': + self.__hitRoof(self.av, collisionEntry) + elif hitNode == 'targetSphere': + self.__hitTarget(self.av, collisionEntry, [vel]) + elif hitNode in cloudBumpers: + self.__hitCloudPlatform(self.av, collisionEntry) + elif hitNode == 'collision_cannon_bumper': + self.__hitCannonBumper(self.av, collisionEntry) + elif hitNode == 'statuaryCol': + self.__hitStatuary(self.av, collisionEntry) + else: + self.notify.debug('*************** hit something else ************') + return + + if self.localToonShooting: + camera.wrtReparentTo(render) + + if self.dropShadow: + self.dropShadow.reparentTo(hidden) + + pos = collisionEntry.getSurfacePoint(render) + hpr = self.av.getHpr() + hitPos = collisionEntry.getSurfacePoint(render) + pos = hitPos + self.landingPos = pos + + self.notify.debug('hitNode,Normal = %s,%s' % (hitNode, intoNormal)) + + track = Sequence() + track.append(Func(self.av.wrtReparentTo, render)) + if self.localToonShooting: + track.append(Func(self.av.collisionsOff)) + if hitPylonAboveWater or hitNode in ['matCollisions', + 'collision1', + 'floor', + 'sand_collision', + 'dirt_collision', + 'soil1', + 'collision2', + 'floor_collision']: + track.append(Func(self.__hitGround, self.av, pos)) + track.append(Wait(1.0)) + track.append(Func(self.__setToonUpright, self.av, self.landingPos)) + elif hitNode == 'collision_house': + track.append(Func(self.__hitHouse, self.av, collisionEntry)) + elif hitNode == 'collision_fence' or hitNode == 'collision4': + track.append(Func(self.__hitFence, self.av, collisionEntry)) + elif hitNode == 'targetSphere': + track.append(Func(self.__hitHouse, self.av, collisionEntry)) + elif hitNode == 'collision3': + track.append(Func(self.__hitWater, self.av, pos, collisionEntry)) + track.append(Wait(2.0)) + track.append(Func(self.__setToonUpright, self.av, self.landingPos)) + elif hitNode == 'roofOutside' or hitNode == 'collision_roof' or hitNode == 'roofclision': + track.append(Func(self.__hitRoof, self.av, collisionEntry)) + track.append(Wait(2.0)) + track.append(Func(self.__setToonUpright, self.av, self.landingPos)) + elif hitNode.find('MovingPlatform') == 0 or hitNode.find('cloudSphere') == 0: + track.append(Func(self.__hitCloudPlatform, self.av, collisionEntry)) + else: + self.notify.warning('************* unhandled hitNode=%s parent =%s' % (hitNode, collisionEntry.getIntoNodePath().getParent())) + + track.append(Func(self.b_setLanded)) + + if self.localToonShooting: + track.append(Func(self.av.collisionsOn)) + + if 1: + if self.hitTrack: + self.hitTrack.finish() + self.hitTrack = track + self.hitTrack.start() + + def __hitGround(self, avatar, pos, extraArgs = []): + hitP = avatar.getPos(render) + self.notify.debug('hitGround pos = %s, hitP = %s' % (pos, hitP)) + self.notify.debug('avatar hpr = %s' % avatar.getHpr()) + h = self.barrel.getH(render) + avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + avatar.setHpr(h, -135, 0) + self.notify.debug('parent = %s' % avatar.getParent()) + self.notify.debug('pos = %s, hpr = %s' % (avatar.getPos(render), avatar.getHpr(render))) + self.dustCloud.setPos(render, pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + self.dustCloud.setScale(0.35) + self.dustCloud.play() + base.playSfx(self.sndHitGround) + avatar.setPlayRate(2.0, 'run') + avatar.loop('run') + + def __hitHouse(self, avatar, collisionEntry, extraArgs = []): + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.2, angVel=3) + pinballScore = ToontownGlobals.PinballScoring[ToontownGlobals.PinballHouse] + self.incrementPinballInfo(pinballScore[0], pinballScore[1]) + + def __hitFence(self, avatar, collisionEntry, extraArgs = []): + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.2, angVel=3) + pinballScore = ToontownGlobals.PinballScoring[ToontownGlobals.PinballFence] + self.incrementPinballInfo(pinballScore[0], pinballScore[1]) + + def __hitTarget(self, avatar, collisionEntry, extraArgs = []): + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.1, angVel=2) + pinballScore = ToontownGlobals.PinballScoring[ToontownGlobals.PinballTarget] + self.incrementPinballInfo(pinballScore[0], pinballScore[1]) + if self.localToonShooting: + self.hitTarget = 1 + messenger.send('hitTarget', [self.avId, self.lastVel]) + + def __hitBumper(self, avatar, collisionEntry, sound, kr = 0.6, angVel = 1): + self.hitBumper = 1 + base.playSfx(self.sndHitHouse) + hitP = avatar.getPos(render) + self.lastPos = hitP + house = collisionEntry.getIntoNodePath() + normal = collisionEntry.getSurfaceNormal(house) + normal.normalize() + normal = collisionEntry.getSurfaceNormal(render) + self.notify.debug('normal = %s' % normal) + vel = self.vel * 1 + speed = vel.length() + vel.normalize() + self.notify.debug('old vel = %s' % vel) + newVel = (normal * 2.0 + vel) * (kr * speed) + self.lastVel = newVel + self.notify.debug('new vel = %s' % newVel) + self.angularVel = angVel * 360 + t = Sequence(Func(avatar.pose, 'lose', 110)) + t.start() + + def __hitRoof(self, avatar, collisionEntry, extraArgs = []): + if True: + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.3, angVel=3) + pinballScore = ToontownGlobals.PinballScoring[ToontownGlobals.PinballRoof] + self.incrementPinballInfo(pinballScore[0], pinballScore[1]) + return + np = collisionEntry.getIntoNodePath() + roof = np.getParent() + normal = collisionEntry.getSurfaceNormal(np) + normal.normalize() + vel = self.trajectory.getVel(self.t) + vel.normalize() + dot = normal.dot(vel) + self.notify.debug('--------------dot product = %s---------------' % dot) + temp = render.attachNewNode('temp') + temp.iPosHpr() + temp.lookAt(Point3(normal)) + temp.reparentTo(roof) + self.notify.debug('avatar pos = %s, landingPos = %s' % (avatar.getPos(), self.landingPos)) + temp.setPos(render, self.landingPos) + avatar.reparentTo(temp) + avatar.setPosHpr(0, 0.25, 0.5, 0, 270, 180) + avatar.pose('slip-forward', 25) + base.playSfx(self.sndHitHouse) + avatar.setPlayRate(1.0, 'jump') + h = self.barrel.getH(render) + t = Sequence(LerpPosInterval(avatar, 0.5, Point3(0, 0, -.5), blendType='easeInOut'), Func(avatar.clearColorScale), Func(avatar.wrtReparentTo, render), Wait(0.3), Parallel(Func(avatar.setP, 0), Func(avatar.play, 'jump', None, 19, 39), LerpHprInterval(avatar, 0.3, Vec3(h, 0, 0), blendType='easeOut')), Func(avatar.play, 'neutral')) + t.start() + hitP = avatar.getPos(render) + return + + def __hitBridge(self, avatar, collisionEntry, extraArgs = []): + self.notify.debug('hit bridge') + hitP = avatar.getPos(render) + self.dustCloud.setPos(render, hitP[0], hitP[1], hitP[2] - 0.5) + self.dustCloud.setScale(0.35) + self.dustCloud.play() + base.playSfx(self.sndHitGround) + + def __hitWater(self, avatar, pos, collisionEntry, extraArgs = []): + hitP = avatar.getPos(render) + if hitP[2] > ToontownGlobals.EstateWakeWaterHeight: + self.notify.debug('we hit the ground before we hit water') + self.__hitGround(avatar, pos, extraArgs) + print 'but not really' + return + self.inWater = 1 + self.notify.debug('hit water') + hitP = avatar.getPos(render) + avatar.loop('neutral') + self.splash.setPos(hitP) + self.splash.setZ(ToontownGlobals.EstateWakeWaterHeight) + self.splash.setScale(2) + self.splash.play() + base.playSfx(self.sndHitWater) + place = base.cr.playGame.getPlace() + self.notify.debug('hitWater: submerged = %s' % place.toonSubmerged) + + def __hitCannonBumper(self, avatar, collisionEntry, extraArgs = []): + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.4, angVel=5) + score, multiplier = ToontownGlobals.PinballScoring[ToontownGlobals.PinballCannonBumper] + self.incrementPinballInfo(score, multiplier) + + def __hitStatuary(self, avatar, collisionEntry, extraArgs = []): + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.4, angVel=5) + score, multiplier = ToontownGlobals.PinballScoring[ToontownGlobals.PinballStatuary] + intoNodePath = collisionEntry.getIntoNodePath() + name = intoNodePath.getParent().getName() + splitParts = name.split('-') + if len(splitParts) >= 3: + score = int(splitParts[1]) + multiplier = int(splitParts[2]) + self.incrementPinballInfo(score, multiplier) + + def __hitCloudPlatform(self, avatar, collisionEntry, extraArgs = []): + if True: + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.4, angVel=5) + score, multiplier = ToontownGlobals.PinballScoring[ToontownGlobals.PinballCloudBumperLow] + intoNodePath = collisionEntry.getIntoNodePath() + name = intoNodePath.getParent().getName() + splitParts = name.split('-') + if len(splitParts) >= 3: + score = int(splitParts[1]) + multiplier = int(splitParts[2]) + self.incrementPinballInfo(score, multiplier) + return + avatar.reparentTo(collisionEntry.getIntoNodePath()) + h = self.barrel.getH(render) + avatar.setPosHpr(0, 0, 0, h, 0, 0) + messenger.send('hitCloud') + + def __setToonUpright(self, avatar, pos = None): + if avatar: + if self.inWater: + avatar.setP(0) + avatar.setR(0) + return + if not pos: + pos = avatar.getPos(render) + avatar.setPos(render, pos) + avatar.loop('neutral') + place = base.cr.playGame.getPlace() + h = self.barrel.getH(render) + p = Point3(self.vel[0], self.vel[1], self.vel[2]) + self.notify.debug('lookat = %s' % p) + if hasattr(self, 'cannon') and self.cannon: + avatar.lookAt(self.cannon) + avatar.setP(0) + avatar.setR(0) + avatar.setScale(1, 1, 1) + + def __calcToonImpact(self, trajectory): + t_groundImpact = trajectory.checkCollisionWithGround(GROUND_PLANE_MIN) + if t_groundImpact >= trajectory.getStartTime(): + return (t_groundImpact, self.HIT_GROUND) + else: + self.notify.error('__calcToonImpact: toon never impacts ground?') + return (0.0, self.HIT_GROUND) + + def __calcHitTreasures(self, trajectory): + estate = self.cr.doId2do.get(self.estateId) + self.hitTreasures = [] + '''if estate: + doIds = estate.flyingTreasureId + for id in doIds: + t = self.cr.doId2do.get(id) + if t: + pos = t.pos + rad = 10.5 + height = 10.0 + t_impact = trajectory.checkCollisionWithCylinderSides(pos, rad, height) + if t_impact > 0: + self.hitTreasures.append([t_impact, t]) + + del estate''' + return None + + def __shootTask(self, task): + base.playSfx(self.sndCannonFire) + self.dropShadow.reparentTo(render) + return Task.done + + def __smokeTask(self, task): + self.smoke.reparentTo(self.barrel) + self.smoke.setPos(0, 6, -3) + self.smoke.setScale(0.5) + self.smoke.wrtReparentTo(render) + track = Sequence(Parallel(LerpScaleInterval(self.smoke, 0.5, 3), LerpColorScaleInterval(self.smoke, 0.5, Vec4(2, 2, 2, 0))), Func(self.smoke.reparentTo, hidden), Func(self.smoke.clearColorScale)) + track.start() + return Task.done + + def __flyTask(self, task): + toon = task.info['toon'] + if toon.isEmpty(): + self.__resetToonToCannon(self.av) + return Task.done + curTime = task.time + task.info['launchTime'] + t = curTime + self.lastT = self.t + self.t = t + deltaT = self.t - self.lastT + self.deltaT = deltaT + if self.hitBumper: + pos = self.lastPos + self.lastVel * deltaT + vel = self.lastVel + self.lastVel += Vec3(0, 0, -32.0) * deltaT + self.lastPos = pos + toon.setFluidPos(pos) + lastH = toon.getH() + toon.setH(lastH + deltaT * self.angularVel) + view = 0 + else: + pos = task.info['trajectory'].getPos(t) + toon.setFluidPos(pos) + shadowPos = Point3(pos) + shadowPos.setZ(SHADOW_Z_OFFSET) + self.dropShadow.setPos(shadowPos) + vel = task.info['trajectory'].getVel(t) + run = math.sqrt(vel[0] * vel[0] + vel[1] * vel[1]) + rise = vel[2] + theta = self.__toDegrees(math.atan(rise / run)) + toon.setHpr(self.cannon.getH(render), -90 + theta, 0) + view = 2 + if pos.getZ() < -20 or pos.getZ() > 1000: + self.notify.debug('stopping fly task toon.getZ()=%.2f' % pos.getZ()) + self.__resetToonToCannon(self.av) + return Task.done + lookAt = task.info['toon'].getPos(render) + hpr = task.info['toon'].getHpr(render) + if self.localToonShooting: + if view == 0: + camera.wrtReparentTo(render) + camera.lookAt(lookAt) + elif view == 1: + camera.reparentTo(render) + camera.setPos(render, 100, 100, 35.25) + camera.lookAt(render, lookAt) + elif view == 2: + if hpr[1] > -90: + camera.setPos(0, 0, -30) + if camera.getZ() < lookAt[2]: + camera.setZ(render, lookAt[2] + 10) + camera.lookAt(Point3(0, 0, 0)) + self.__pickupTreasures(t) + return Task.cont + + def __pickupTreasures(self, t): + updatedList = [] + for tList in self.hitTreasures: + if t > tList[0]: + messenger.send(tList[1].uniqueName('entertreasureSphere')) + self.notify.debug('hit something!') + else: + updatedList.append(tList) + + self.hitTreasures = updatedList + + def __resetToonToCannon(self, avatar): + pos = None + if not avatar: + if self.avId: + avatar = base.cr.doId2do.get(self.avId, None) + if avatar: + if self.cannon: + avatar.reparentTo(self.cannon) + avatar.setPos(2, -4, 0) + avatar.wrtReparentTo(render) + self.__resetToon(avatar) + return + + def __resetToon(self, avatar, pos = None): + self.notify.debug('__resetToon') + if avatar: + self.__stopCollisionHandler(avatar) + self.__setToonUpright(avatar, pos) + if self.localToonShooting: + self.notify.debug('toon setting position to %s' % pos) + if pos: + base.localAvatar.setPos(pos) + camera.reparentTo(avatar) + self.b_setLanded() + + def setActiveState(self, active): + self.notify.debug('got setActiveState(%s)' % active) + if active and not self.cannonsActive: + self.activateCannons() + elif not active and self.cannonsActive: + self.deActivateCannons() + + def enterInit(self): + self.nextKey = 'up' + self.nextState = 'u1' + + def exitInit(self): + pass + + def enteru1(self): + self.nextKey = 'up' + self.nextState = 'u2' + + def exitu1(self): + pass + + def enteru2(self): + self.nextKey = 'down' + self.nextState = 'd3' + + def exitu2(self): + pass + + def enterd3(self): + self.nextKey = 'down' + self.nextState = 'd4' + + def exitd3(self): + pass + + def enterd4(self): + self.nextKey = 'left' + self.nextState = 'l5' + + def exitd4(self): + pass + + def enterl5(self): + self.nextKey = 'right' + self.nextState = 'r6' + + def exitl5(self): + pass + + def enterr6(self): + self.nextKey = 'left' + self.nextState = 'l7' + + def exitr6(self): + pass + + def enterl7(self): + self.nextKey = 'right' + self.nextState = 'r8' + + def exitl7(self): + pass + + def enterr8(self): + self.nextKey = None + self.nextState = '' + self.codeFSM.request('acceptCode') + return + + def exitr8(self): + pass + + def enterAcceptCode(self): + if not self.cannonsActive: + self.activateCannons() + self.sendUpdate('setActive', [1]) + else: + self.deActivateCannons() + self.sendUpdate('setActive', [0]) + self.codeFSM.request('init') + + def exitAcceptCode(self): + pass + + def enterFinal(self): + pass + + def exitFinal(self): + pass + + def handleCodeKey(self, key): + if self.nextKey and self.nextState: + if key == self.nextKey: + self.codeFSM.request(self.nextState) + else: + self.codeFSM.request('init') + + def incrementPinballInfo(self, score, multiplier): + if base.localAvatar.doId == self.avId: + self.curPinballScore += score + self.curPinballMultiplier += multiplier + self.notify.debug('score =%d multiplier=%d curscore=%d curMult=%d' % (score, + multiplier, + self.curPinballScore, + self.curPinballMultiplier)) + self.d_setPinballInfo() + + def d_setPinballInfo(self): + self.notify.debug('d_setPinballInfo %d %d' % (self.curPinballScore, self.curPinballMultiplier)) + target = base.cr.doId2do[self.targetId] + target.b_setCurPinballScore(self.avId, self.curPinballScore, self.curPinballMultiplier) + + def createBlock(self, collisionName = None): + gFormat = GeomVertexFormat.getV3c4() + myVertexData = GeomVertexData('Cannon bumper vertices', gFormat, Geom.UHDynamic) + vertexWriter = GeomVertexWriter(myVertexData, 'vertex') + colorWriter = GeomVertexWriter(myVertexData, 'color') + vertices = [(-1, 1, 1), + (1, 1, 1), + (1, -1, 1), + (-1, -1, 1), + (-1, 1, -1), + (1, 1, -1), + (1, -1, -1), + (-1, -1, -1)] + colors = [(0, 0, 0, 1), + (0, 0, 1, 1), + (0, 1, 0, 1), + (0, 1, 1, 1), + (1, 0, 0, 1), + (1, 0, 1, 1), + (1, 1, 0, 1), + (1, 1, 1, 1)] + faces = [(0, 2, 1), + (0, 3, 2), + (7, 4, 5), + (6, 7, 5), + (2, 3, 7), + (2, 7, 6), + (4, 0, 1), + (5, 4, 1), + (0, 4, 3), + (3, 4, 7), + (1, 2, 6), + (1, 6, 5)] + quads = [(3, 2, 1, 0), + (4, 5, 6, 7), + (3, 7, 6, 2), + (0, 1, 5, 4), + (0, 4, 7, 3), + (1, 2, 6, 5)] + for i in xrange(len(vertices)): + vertex = vertices[i] + vertexWriter.addData3f(vertex[0], vertex[1], vertex[2]) + colorWriter.addData4f(*colors[i]) + + cubeGeom = Geom(myVertexData) + tris = GeomTriangles(Geom.UHDynamic) + tris.makeIndexed() + for face in faces: + for vertex in face: + tris.addVertex(vertex) + + tris.closePrimitive() + cubeGeom.addPrimitive(tris) + cubeGN = GeomNode('cubeGeom') + cubeGN.addGeom(cubeGeom) + if collisionName: + colNode = CollisionNode(collisionName) + else: + colNode = CollisionNode('cubeCollision') + for quad in quads: + colQuad = CollisionPolygon(Point3(*vertices[quad[0]]), Point3(*vertices[quad[1]]), Point3(*vertices[quad[2]]), Point3(*vertices[quad[3]])) + colQuad.setTangible(0) + colNode.addSolid(colQuad) + + block = NodePath('cubeNodePath') + block.attachNewNode(cubeGN) + block.attachNewNode(colNode) + return block + + def loadCannonBumper(self): + self.cannonBumper = loader.loadModel('phase_5.5/models/estate/bumper_cloud') + self.cannonBumper.reparentTo(self.nodePath) + self.cannonBumper.setScale(4.0) + self.cannonBumper.setColor(0.52, 0.8, 0.98, 1) + colCube = self.cannonBumper.find('**/collision') + colCube.setName('cloudSphere-0') + self.bumperCol = colCube + self.notify.debug('------------self.cannonBumper.setPos %.2f %.2f %.2f' % (ToontownGlobals.PinballCannonBumperInitialPos[0], ToontownGlobals.PinballCannonBumperInitialPos[1], ToontownGlobals.PinballCannonBumperInitialPos[2])) + self.cannonBumper.setPos(*ToontownGlobals.PinballCannonBumperInitialPos) + + def __bumperKeyPressed(self): + self.notify.debug('__bumperKeyPressed') + self.__bumperPressed() + + def __bumperPressed(self): + renderPos = base.localAvatar.getPos(render) + if renderPos[2] > 15.0: + if not self.localToonShooting: + return + self.ignore(self.BUMPER_KEY) + self.ignore(self.BUMPER_KEY2) + self.notify.debug('renderPos %s' % renderPos) + cannonPos = base.localAvatar.getPos(self.nodePath) + self.notify.debug('cannonPos %s' % cannonPos) + self.setCannonBumperPos(cannonPos[0], cannonPos[1], cannonPos[2]) + self.requestBumperMove(cannonPos[0], cannonPos[1], cannonPos[2]) + + def requestBumperMove(self, x, y, z): + self.sendUpdate('requestBumperMove', [x, y, z]) + + def setCannonBumperPos(self, x, y, z): + self.notify.debug('------------setCannonBumperPos %f %f %f' % (x, y, z)) + self.cannonBumper.setPos(x, y, z) + self.bumperCol.setCollideMask(BitMask32.allOff()) + taskMgr.doMethodLater(0.25, self.turnOnBumperCollision, self.uniqueName('BumperON')) + + def turnOnBumperCollision(self, whatever = 0): + if self.bumperCol: + self.bumperCol.setCollideMask(ToontownGlobals.WallBitmask) diff --git a/toontown/estate/DistributedCannonAI.py b/toontown/estate/DistributedCannonAI.py new file mode 100755 index 00000000..d11cf3e9 --- /dev/null +++ b/toontown/estate/DistributedCannonAI.py @@ -0,0 +1,121 @@ +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task +from toontown.minigame import CannonGameGlobals +from direct.distributed import DistributedObjectAI +from toontown.minigame import Trajectory +import CannonGlobals + +class DistributedCannonAI(DistributedObjectAI.DistributedObjectAI): + notify = directNotify.newCategory('DistributedCannonAI') + + def __init__(self, air, estateId, targetId, x, y, z, h, p, r): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.posHpr = [x, + y, + z, + h, + p, + r] + self.avId = 0 + self.estateId = estateId + self.timeoutTask = None + self.targetId = targetId + self.cannonBumperPos = list(ToontownGlobals.PinballCannonBumperInitialPos) + return + + def delete(self): + self.ignoreAll() + self.__stopTimeout() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def requestEnter(self): + avId = self.air.getAvatarIdFromSender() + if self.avId == 0: + self.avId = avId + self.__stopTimeout() + self.setMovie(CannonGlobals.CANNON_MOVIE_LOAD, self.avId) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + self.acceptOnce('bootAvFromEstate-' + str(avId), self.__handleBootMessage, extraArgs=[avId]) + self.__startTimeout(CannonGlobals.CANNON_TIMEOUT) + else: + self.air.writeServerEvent('suspicious', avId, 'DistributedCannonAI.requestEnter cannon already occupied') + self.notify.warning('requestEnter() - cannon already occupied') + self.sendUpdateToAvatarId(avId, 'requestExit', []) + + def setMovie(self, mode, avId): + self.avId = avId + self.sendUpdate('setMovie', [mode, avId]) + + def getCannonBumperPos(self): + self.notify.debug('---------getCannonBumperPos %s' % self.cannonBumperPos) + return self.cannonBumperPos + + def requestBumperMove(self, x, y, z): + self.cannonBumperPos = [x, y, z] + self.sendUpdate('setCannonBumperPos', [x, y, z]) + + def getPosHpr(self): + return self.posHpr + + def getEstateId(self): + return self.estateId + + def getTargetId(self): + return self.targetId + + def setCannonPosition(self, zRot, angle): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setCannonPosition: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle)) + self.sendUpdate('updateCannonPosition', [avId, zRot, angle]) + + def setCannonLit(self, zRot, angle): + avId = self.air.getAvatarIdFromSender() + self.__stopTimeout() + self.notify.debug('setCannonLit: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle)) + fireTime = CannonGameGlobals.FUSE_TIME + self.sendUpdate('setCannonWillFire', [avId, + fireTime, + zRot, + angle, + globalClockDelta.getRealNetworkTime()]) + + def setLanded(self): + self.ignore(self.air.getAvatarExitEvent(self.avId)) + self.setMovie(CannonGlobals.CANNON_MOVIE_LANDED, 0) + self.avId = 0 + + def setActive(self, active): + if active < 0 or active > 1: + self.air.writeServerEvent('suspicious', active, 'DistributedCannon.setActive value should be 0-1 range') + return + self.active = active + self.sendUpdate('setActiveState', [active]) + + def __startTimeout(self, timeLimit): + self.__stopTimeout() + self.timeoutTask = taskMgr.doMethodLater(timeLimit, self.__handleTimeout, self.taskName('timeout')) + + def __stopTimeout(self): + if self.timeoutTask != None: + taskMgr.remove(self.timeoutTask) + self.timeoutTask = None + return + + def __handleTimeout(self, task): + self.notify.debug('Timeout expired!') + self.__doExit() + return Task.done + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.__doExit() + + def __handleBootMessage(self, avId): + self.notify.warning('avatar:' + str(avId) + ' got booted ') + self.__doExit() + + def __doExit(self): + self.setMovie(CannonGlobals.CANNON_MOVIE_FORCE_EXIT, self.avId) diff --git a/toontown/estate/DistributedChair.py b/toontown/estate/DistributedChair.py new file mode 100644 index 00000000..b68847bc --- /dev/null +++ b/toontown/estate/DistributedChair.py @@ -0,0 +1,136 @@ +from direct.interval.IntervalGlobal import * +from direct.gui.DirectGui import * +from toontown.catalog import CatalogFurnitureItem +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.toontowngui import TTDialog +from DistributedFurnitureItem import DistributedFurnitureItem + +class DistributedChair(DistributedFurnitureItem): + + def __init__(self, cr): + DistributedFurnitureItem.__init__(self, cr) + self.dialog = None + self.exitButton = None + self.avId = ToontownGlobals.CHAIR_NONE + self.accept('exitingStoppedState', self.destroyGui) + + def loadModel(self): + model = DistributedFurnitureItem.loadModel(self) + cSphere = CollisionSphere(0.0, self.getChair()[3], 1.0, 1.575) + cSphere.setTangible(0) + colNode = CollisionNode('Chair-%s' % self.doId) + colNode.addSolid(cSphere) + cSpherePath = model.attachNewNode(colNode) + cSpherePath.setCollideMask(ToontownGlobals.WallBitmask) + self.accept('enterChair-%s' % self.doId, self.__enterSphere) + return model + + def disable(self): + av = base.cr.doId2do.get(self.avId) + + if av: + self.resetAvatar(av) + + self.ignoreAll() + DistributedFurnitureItem.disable(self) + + def getChair(self): + return CatalogFurnitureItem.ChairToPosHpr[self.item.furnitureType] + + def destroyGui(self): + if self.exitButton: + self.exitButton.destroy() + self.exitButton = None + + if self.dialog: + self.dialog.destroy() + self.dialog = None + + def setupGui(self): + castGui = loader.loadModel('phase_4/models/gui/fishingGui') + self.exitButton = DirectButton(parent=base.a2dBottomRight, relief=None, text=('', TTLocalizer.FishingExit, TTLocalizer.FishingExit), text_align=TextNode.ACenter, text_scale=0.1, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0.0, -0.12), pos=(-0.158, 0, 0.14), image=(castGui.find('**/exit_buttonUp'), castGui.find('**/exit_buttonDown'), castGui.find('**/exit_buttonRollover')), command=self.sendUpdate, extraArgs=['requestSit', [ToontownGlobals.CHAIR_STOP]]) + castGui.removeNode() + + base.localAvatar.stopSleepWatch() + base.localAvatar.startSleepWatch(self.__handleFallingAsleep) + + def resetAvatar(self, av): + av.loop('neutral') + av.setPos(av.getPos(render)) + av.getGeomNode().setHpr(0, 0, 0) + av.setH(self.getH() + self.getChair()[1][0]) + av.reparentTo(render) + + if av == base.localAvatar: + base.localAvatar.setPreventCameraDisable(False) + base.cr.playGame.getPlace().setState('walk') + + def setAvId(self, avId): + if avId == ToontownGlobals.CHAIR_NONE: + self.avId = avId + return + + chair = self.getChair() + av = base.cr.doId2do.get(avId) + + if not av: + return + + sitStartDuration = av.getDuration('sit-start') + sequence = Sequence(Func(av.loop, 'walk'), av.getGeomNode().hprInterval(0.25, chair[1]), Parallel(Sequence(Wait(sitStartDuration * 0.25), av.posInterval(sitStartDuration * 0.25, chair[0])), ActorInterval(av, 'sit-start')), Func(av.setAnimState, 'Sit', 1.0)) + + av.setPosHpr(chair[2], (0, 0, 0)) + av.reparentTo(self) + + if av == base.localAvatar: + base.cr.playGame.getPlace().setState('walk') + base.localAvatar.setPreventCameraDisable(True) + base.cr.playGame.getPlace().setState('stopped') + sequence.append(Func(self.setupGui)) + + sequence.start() + self.avId = avId + + def setStatus(self, status): + av = base.cr.doId2do.get(self.avId) + + if not av: + return + + if status == ToontownGlobals.CHAIR_UNEXPECTED_EXIT: + self.resetAvatar(av) + else: + sitStartDuration = av.getDuration('sit-start') + self.destroyGui() + Sequence(Parallel(ActorInterval(av, 'sit-start', startTime=sitStartDuration, endTime=0.0), Sequence(Wait(sitStartDuration * 0.25), av.posInterval(sitStartDuration * 0.25, self.getChair()[2]))), Func(self.resetAvatar, av)).start() + + def resetAvatar(self, av): + av.loop('neutral') + av.setPos(av.getPos(render)) + av.getGeomNode().setHpr(0, 0, 0) + av.setH(self.getH() + self.getChair()[1][0]) + av.reparentTo(render) + + if av == base.localAvatar: + base.localAvatar.setPreventCameraDisable(False) + base.cr.playGame.getPlace().setState('walk') + self.destroyGui() + + def __enterSphere(self, collisionEntry): + if self.avId in base.cr.doId2do: + return + + base.cr.playGame.getPlace().setState('stopped') + self.dialog = TTDialog.TTDialog(style=TTDialog.TwoChoice, text=TTLocalizer.ChairAskToUse, fadeScreen=1, command=self.__handleDialogResponse) + + def __handleDialogResponse(self, response): + self.destroyGui() + + if response < 0: + base.cr.playGame.getPlace().setState('walk') + return + + self.sendUpdate('requestSit', [ToontownGlobals.CHAIR_START]) + + def __handleFallingAsleep(self, arg): + self.sendUpdate('requestSit', [ToontownGlobals.CHAIR_STOP]) \ No newline at end of file diff --git a/toontown/estate/DistributedChairAI.py b/toontown/estate/DistributedChairAI.py new file mode 100644 index 00000000..a07d58c7 --- /dev/null +++ b/toontown/estate/DistributedChairAI.py @@ -0,0 +1,57 @@ +from toontown.catalog import CatalogAccessoryItem, CatalogClothingItem, CatalogNametagItem, CatalogEmoteItem +from toontown.catalog.CatalogAccessoryItemGlobals import * +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.toon import ToonDNA +from toontown.quest import Quests +from DistributedFurnitureItemAI import DistributedFurnitureItemAI +import random, time + +class DistributedChairAI(DistributedFurnitureItemAI): + + def __init__(self, air, furnitureMgr, itemType): + DistributedFurnitureItemAI.__init__(self, air, furnitureMgr, itemType) + self.avId = ToontownGlobals.CHAIR_NONE + + def destroy(self): + self.ignoreAll() + DistributedFurnitureItemAI.destroy(self) + + def b_setAvId(self, avId): + self.avId = avId + self.sendUpdate('setAvId', [avId]) + + def b_resetAvId(self): + self.b_setAvId(ToontownGlobals.CHAIR_NONE) + + def b_resetAvWithAnim(self, reason): + self.sendUpdate('setStatus', [reason]) + self.b_resetAvId() + + def getAvId(self): + return self.avId + + def getSitResponse(self): + return ToontownGlobals.CHAIR_NONE + + def requestSit(self, requestCode): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + + if not av: + return + + if requestCode == ToontownGlobals.CHAIR_START: + if self.avId in self.air.doId2do: + return + + self.b_setAvId(avId) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit) + elif requestCode == ToontownGlobals.CHAIR_STOP: + if self.avId != avId: + return + + self.b_resetAvWithAnim(ToontownGlobals.CHAIR_EXIT) + self.ignoreAll() + + def __handleUnexpectedExit(self): + self.b_resetAvWithAnim(ToontownGlobals.CHAIR_UNEXPECTED_EXIT) \ No newline at end of file diff --git a/toontown/estate/DistributedChangingStatuary.py b/toontown/estate/DistributedChangingStatuary.py new file mode 100755 index 00000000..e963322b --- /dev/null +++ b/toontown/estate/DistributedChangingStatuary.py @@ -0,0 +1,60 @@ +from pandac.PandaModules import NodePath +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.estate import DistributedStatuary +from toontown.estate import GardenGlobals + +class DistributedChangingStatuary(DistributedStatuary.DistributedStatuary): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedChangingStatuary') + + def __init__(self, cr): + self.notify.debug('constructing DistributedChangingStatuary') + DistributedStatuary.DistributedStatuary.__init__(self, cr) + + def loadModel(self): + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = loader.loadModel(self.modelPath) + colNode = self.model.find('**/+CollisionNode') + if not colNode.isEmpty(): + score, multiplier = ToontownGlobals.PinballScoring[ToontownGlobals.PinballStatuary] + if self.pinballScore: + score = self.pinballScore[0] + multiplier = self.pinballScore[1] + scoreNodePath = NodePath('statuary-%d-%d' % (score, multiplier)) + colNode.setName('statuaryCol') + scoreNodePath.reparentTo(colNode.getParent()) + colNode.reparentTo(scoreNodePath) + self.model.setScale(self.worldScale) + self.model.reparentTo(self.rotateNode) + self.hideParts() + self.stick2Ground() + + def hideParts(self): + stage = -1 + attrib = GardenGlobals.PlantAttributes[self.typeIndex] + growthThresholds = attrib['growthThresholds'] + for index, threshold in enumerate(growthThresholds): + if self.growthLevel < threshold: + stage = index + break + + if stage == -1: + stage = len(growthThresholds) + self.notify.debug('growth Stage=%d' % stage) + for index in xrange(len(growthThresholds) + 1): + if index != stage: + partName = '**/growthStage_%d' % index + self.notify.debug('trying to remove %s' % partName) + hideThis = self.model.find(partName) + if not hideThis.isEmpty(): + hideThis.removeNode() + + def setupShadow(self): + DistributedStatuary.DistributedStatuary.setupShadow(self) + self.hideShadow() + + def setGrowthLevel(self, growthLevel): + self.growthLevel = growthLevel + if self.model: + self.model.removeNode() + self.loadModel() diff --git a/toontown/estate/DistributedChangingStatuaryAI.py b/toontown/estate/DistributedChangingStatuaryAI.py new file mode 100755 index 00000000..cbdc1877 --- /dev/null +++ b/toontown/estate/DistributedChangingStatuaryAI.py @@ -0,0 +1,9 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.estate.DistributedStatuaryAI import DistributedStatuaryAI + +class DistributedChangingStatuaryAI(DistributedStatuaryAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedChangingStatuaryAI") + + def setGrowthLevel(self, todo0): + pass + diff --git a/toontown/estate/DistributedCloset.py b/toontown/estate/DistributedCloset.py new file mode 100755 index 00000000..e16cd9ce --- /dev/null +++ b/toontown/estate/DistributedCloset.py @@ -0,0 +1,493 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownGlobals +from direct.showbase import DirectObject +from toontown.toon import ToonDNA +from direct.fsm import ClassicFSM, State, StateData +import ClosetGUI +from direct.task.Task import Task +import ClosetGlobals +import DistributedFurnitureItem +from toontown.toonbase import TTLocalizer +from toontown.catalog import CatalogFurnitureItem + +class DistributedCloset(DistributedFurnitureItem.DistributedFurnitureItem): + notify = directNotify.newCategory('DistributedCloset') + + def __init__(self, cr): + DistributedFurnitureItem.DistributedFurnitureItem.__init__(self, cr) + self.notify.debug('__init__') + self.lastAvId = 0 + self.hasLocalAvatar = 0 + self.lastTime = 0 + self.av = None + self.closetGUI = None + self.closetModel = None + self.closetSphere = None + self.closetSphereNode = None + self.closetSphereNodePath = None + self.topList = [] + self.botList = [] + self.oldTopList = [] + self.oldBotList = [] + self.oldStyle = None + self.button = None + self.topTrashButton = None + self.bottomTrashButton = None + self.isLocalToon = None + self.popupInfo = None + self.isOwner = 0 + self.ownerId = 0 + self.customerId = 0 + self.purchaseDoneEvent = '' + self.swapEvent = '' + self.locked = 0 + self.gender = None + self.topDeleted = 0 + self.bottomDeleted = 0 + self.closetTrack = None + self.avMoveTrack = None + self.scale = 1.0 + self.fsm = ClassicFSM.ClassicFSM('Closet', [State.State('off', self.enterOff, self.exitOff, ['ready', 'open', 'closed']), + State.State('ready', self.enterReady, self.exitReady, ['open', 'closed']), + State.State('closed', self.enterClosed, self.exitClosed, ['open', 'off']), + State.State('open', self.enterOpen, self.exitOpen, ['closed', 'off'])], 'off', 'off') + self.fsm.enterInitialState() + return + + def generate(self): + DistributedFurnitureItem.DistributedFurnitureItem.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedFurnitureItem.DistributedFurnitureItem.announceGenerate(self) + self.load() + self.setupCollisionSphere() + self.fsm.request('ready') + + def load(self): + self.setTwoSided(1) + lNode = self.find('**/door_rotate_L') + lDoor = self.find('**/closetdoor_L') + if lNode.isEmpty() or lDoor.isEmpty(): + self.leftDoor = None + else: + lDoor.wrtReparentTo(lNode) + self.leftDoor = lNode + rNode = self.find('**/door_rotate_R') + rDoor = self.find('**/closetdoor_R') + if rNode.isEmpty() or rDoor.isEmpty(): + self.rightDoor = None + else: + rDoor.wrtReparentTo(rNode) + self.rightDoor = rNode + if not lNode.isEmpty(): + self.scale = lNode.getScale()[0] + return + + def setupCollisionSphere(self): + if self.ownerId: + self.closetSphereEvent = self.uniqueName('closetSphere') + self.closetSphereEnterEvent = 'enter' + self.closetSphereEvent + self.closetSphere = CollisionSphere(0, 0, 0, self.scale * 2.125) + self.closetSphere.setTangible(0) + self.closetSphereNode = CollisionNode(self.closetSphereEvent) + self.closetSphereNode.setIntoCollideMask(WallBitmask) + self.closetSphereNode.addSolid(self.closetSphere) + self.closetSphereNodePath = self.attachNewNode(self.closetSphereNode) + + def disable(self): + self.notify.debug('disable') + self.ignore(self.closetSphereEnterEvent) + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupChangeClothesGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + taskMgr.remove(self.uniqueName('lerpToon')) + if self.closetTrack: + self.closetTrack.finish() + self.closetTrack = None + if self.closetGUI: + self.closetGUI.resetClothes(self.oldStyle) + self.resetCloset() + if self.hasLocalAvatar: + self.freeAvatar() + self.ignoreAll() + DistributedFurnitureItem.DistributedFurnitureItem.disable(self) + return + + def delete(self): + self.notify.debug('delete') + DistributedFurnitureItem.DistributedFurnitureItem.delete(self) + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + if self.av: + del self.av + del self.gender + del self.closetSphere + del self.closetSphereNode + del self.closetSphereNodePath + del self.closetGUI + del self.fsm + return + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterReady(self): + if self.ownerId: + self.accept(self.closetSphereEnterEvent, self.handleEnterSphere) + + def exitReady(self): + pass + + def enterOpen(self): + if self.ownerId: + self.ignore(self.closetSphereEnterEvent) + self._openDoors() + if self.customerId == base.localAvatar.doId: + camera.wrtReparentTo(self) + camera.posQuatInterval(1, (-7.58, -6.02, 6.9), (286.3, 336.8, 0), other=self, blendType='easeOut').start() + camera.setPosHpr(self, -7.58, -6.02, 6.9, 286.3, 336.8, 0) + if self.av: + if self.avMoveTrack: + self.avMoveTrack.finish() + self.av.stopSmooth() + self.avMoveTrack = Sequence(Parallel(Func(self.av.play, 'walk'), LerpPosHprInterval(nodePath=self.av, other=self, duration=1.0, pos=Vec3(1.67, -3.29, 0.025), hpr=Vec3(112, 0, 0), blendType='easeOut')), Func(self.av.loop, 'neutral'), Func(self.av.startSmooth)) + self.avMoveTrack.start() + + def exitOpen(self): + if self.ownerId: + self._closeDoors() + + def enterClosed(self): + if self.ownerId: + self.accept(self.closetSphereEnterEvent, self.handleEnterSphere) + + def exitClosed(self): + pass + + def handleEnterSphere(self, collEntry): + if self.smoothStarted: + return + if base.localAvatar.doId == self.lastAvId and globalClock.getFrameTime() <= self.lastTime + 0.5: + self.notify.info('Ignoring duplicate entry for avatar.') + return + if self.hasLocalAvatar: + self.freeAvatar() + self.notify.debug('Entering Closet Sphere....%s' % self.closetSphereEnterEvent) + if self.cr.playGame.getPlace() == None: + self.notify.info('Not opening closet before place is defined.') + return + self.ignore(self.closetSphereEnterEvent) + if not self.locked: + self.cr.playGame.getPlace().fsm.request('closet') + self.accept('closetAsleep', self._handleCancel) + self.sendUpdate('enterAvatar', []) + self.hasLocalAvatar = 1 + return + + def setState(self, mode, avId, ownerId, gender, topList, botList): + self.notify.debug('setState, mode=%s, avId=%s, ownerId=%d' % (mode, avId, ownerId)) + self.isOwner = avId == ownerId + self.ownerGender = gender + if mode == ClosetGlobals.CLOSED: + self.fsm.request('closed') + return + elif mode == ClosetGlobals.OPEN: + self.customerId = avId + self.av = self.cr.doId2do.get(self.customerId, None) + if self.av: + if base.localAvatar.getDoId() == self.customerId: + self.gender = self.av.style.gender + self.topList = topList + self.botList = botList + self.oldTopList = self.topList[0:] + self.oldBotList = self.botList[0:] + self.printInfo() + if not self.isOwner: + self.__popupNotOwnerPanel() + else: + taskMgr.doMethodLater(0.5, self.popupChangeClothesGUI, self.uniqueName('popupChangeClothesGUI')) + self.fsm.request('open') + return + + def _revertGender(self): + if self.gender: + self.av.style.gender = self.gender + self.av.loop('neutral') + + def popupChangeClothesGUI(self, task): + self.notify.debug('popupChangeClothesGUI') + self.purchaseDoneEvent = self.uniqueName('purchaseDone') + self.swapEvent = self.uniqueName('swap') + self.cancelEvent = self.uniqueName('cancel') + self.accept(self.purchaseDoneEvent, self.__proceedToCheckout) + self.accept(self.swapEvent, self.__handleSwap) + self.accept(self.cancelEvent, self._handleCancel) + self.deleteEvent = self.uniqueName('delete') + if self.isOwner: + self.accept(self.deleteEvent, self.__handleDelete) + if not self.closetGUI: + maxClothes = CatalogFurnitureItem.ClosetToClothes.get(self.item.furnitureType) + self.closetGUI = ClosetGUI.ClosetGUI(self.isOwner, self.purchaseDoneEvent, self.cancelEvent, self.swapEvent, self.deleteEvent, self.topList, self.botList, maxClothes) + self.closetGUI.load() + if self.gender != self.ownerGender: + self.closetGUI.setGender(self.ownerGender) + self.closetGUI.enter(base.localAvatar) + self.closetGUI.showButtons() + style = self.av.getStyle() + self.oldStyle = ToonDNA.ToonDNA() + self.oldStyle.makeFromNetString(style.makeNetString()) + return Task.done + + def resetCloset(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupChangeClothesGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + taskMgr.remove(self.uniqueName('lerpToon')) + if self.closetGUI: + self.closetGUI.hideButtons() + self.closetGUI.exit() + self.closetGUI.unload() + self.closetGUI = None + del self.av + self.av = base.localAvatar + style = self.av.getStyle() + self.oldStyle = ToonDNA.ToonDNA() + self.oldStyle.makeFromNetString(style.makeNetString()) + self.topDeleted = 0 + self.bottomDeleted = 0 + return Task.done + + def __handleButton(self): + messenger.send('next') + + def _handleCancel(self): + if self.oldStyle: + self.d_setDNA(self.oldStyle.makeNetString(), 1) + else: + self.notify.info('avoided crash in handleCancel') + self._handlePurchaseDone() + if self.closetGUI: + self.closetGUI.resetClothes(self.oldStyle) + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = None + return + + def __handleSwap(self): + self.d_setDNA(self.av.getStyle().makeNetString(), 0) + + def __handleDelete(self, t_or_b): + if t_or_b == ClosetGlobals.SHIRT: + itemList = self.closetGUI.tops + trashIndex = self.closetGUI.topChoice + swapFunc = self.closetGUI.swapTop + removeFunc = self.closetGUI.removeTop + self.topDeleted = self.topDeleted | 1 + + def setItemChoice(i): + self.closetGUI.topChoice = i + + else: + itemList = self.closetGUI.bottoms + trashIndex = self.closetGUI.bottomChoice + swapFunc = self.closetGUI.swapBottom + removeFunc = self.closetGUI.removeBottom + self.bottomDeleted = self.bottomDeleted | 1 + + def setItemChoice(i): + self.closetGUI.bottomChoice = i + + if len(itemList) > 1: + trashDNA = ToonDNA.ToonDNA() + trashItem = self.av.getStyle().makeNetString() + trashDNA.makeFromNetString(trashItem) + if trashIndex == 0: + swapFunc(1) + else: + swapFunc(-1) + removeFunc(trashIndex) + self.sendUpdate('removeItem', [trashItem, t_or_b]) + swapFunc(0) + self.closetGUI.updateTrashButtons() + else: + self.notify.warning("cant delete this item(type = %s), since we don't have a replacement" % t_or_b) + + def __proceedToCheckout(self): + if self.topDeleted or self.bottomDeleted: + self.__popupAreYouSurePanel() + else: + self._handlePurchaseDone() + + def _handlePurchaseDone(self, timeout = 0): + if timeout == 1: + self.d_setDNA(self.oldStyle.makeNetString(), 1) + else: + which = 0 + if hasattr(self.closetGUI, 'topChoice') and hasattr(self.closetGUI, 'bottomChoice'): + if self.closetGUI.topChoice != 0 or self.topDeleted: + which = which | 1 + if self.closetGUI.bottomChoice != 0 or self.bottomDeleted: + which = which | 2 + self.d_setDNA(self.av.getStyle().makeNetString(), 2, which) + + def d_setDNA(self, dnaString, finished, whichItems = 3): + self.sendUpdate('setDNA', [dnaString, finished, whichItems]) + + def setCustomerDNA(self, avId, dnaString): + if avId and avId != base.localAvatar.doId: + av = base.cr.doId2do.get(avId, None) + if av: + if self.av == base.cr.doId2do[avId]: + oldTorso = self.av.style.torso + self.av.style.makeFromNetString(dnaString) + if len(oldTorso) == 2 and len(self.av.style.torso) == 2 and self.av.style.torso[1] != oldTorso[1]: + self.av.swapToonTorso(self.av.style.torso, genClothes=0) + self.av.loop('neutral', 0) + self.av.generateToonClothes() + + def printInfo(self): + print 'avid: %s, gender: %s' % (self.av.doId, self.av.style.gender) + print 'current top = %s,%s,%s,%s and bot = %s,%s,' % (self.av.style.topTex, + self.av.style.topTexColor, + self.av.style.sleeveTex, + self.av.style.sleeveTexColor, + self.av.style.botTex, + self.av.style.botTexColor) + print 'topsList = %s' % self.av.getClothesTopsList() + print 'bottomsList = %s' % self.av.getClothesBottomsList() + + def setMovie(self, mode, avId, timestamp): + self.isLocalToon = avId == base.localAvatar.doId + if avId != 0: + self.lastAvId = avId + self.lastTime = globalClock.getFrameTime() + if mode == ClosetGlobals.CLOSET_MOVIE_CLEAR: + return + elif mode == ClosetGlobals.CLOSET_MOVIE_COMPLETE: + if self.isLocalToon: + self._revertGender() + self.printInfo() + self.resetCloset() + self.freeAvatar() + return + elif mode == ClosetGlobals.CLOSET_MOVIE_TIMEOUT: + taskMgr.remove(self.uniqueName('lerpCamera')) + taskMgr.remove(self.uniqueName('lerpToon')) + if self.isLocalToon: + self.ignore(self.purchaseDoneEvent) + self.ignore(self.swapEvent) + if self.closetGUI: + self.closetGUI.resetClothes(self.oldStyle) + self._handlePurchaseDone(timeout=1) + self.resetCloset() + self._popupTimeoutPanel() + self.freeAvatar() + + def freeAvatar(self): + self.notify.debug('freeAvatar()') + if self.hasLocalAvatar: + base.localAvatar.posCamera(0, 0) + place = base.cr.playGame.getPlace() + if place: + place.setState('walk') + self.ignore('closetAsleep') + base.localAvatar.startLookAround() + self.hasLocalAvatar = 0 + self.lastTime = globalClock.getFrameTime() + + def setOwnerId(self, avId): + self.ownerId = avId + + def _popupTimeoutPanel(self): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = None + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=TTLocalizer.ClosetTimeoutMessage, frameSize=(-1, 1, -1, 1), geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(0.88, 1, 0.45), geom_pos=(0, 0, -.08), text_scale=0.08) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.ClosetPopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.16), command=self.__handleTimeoutMessageOK) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + + def __handleTimeoutMessageOK(self): + self.popupInfo.reparentTo(hidden) + + def __popupNotOwnerPanel(self): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = None + self.purchaseDoneEvent = self.uniqueName('purchaseDone') + self.swapEvent = self.uniqueName('swap') + self.cancelEvent = self.uniqueName('cancel') + self.accept(self.purchaseDoneEvent, self.__proceedToCheckout) + self.accept(self.swapEvent, self.__handleSwap) + self.accept(self.cancelEvent, self._handleCancel) + self.deleteEvent = self.uniqueName('delete') + if self.isOwner: + self.accept(self.deleteEvent, self.__handleDelete) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=TTLocalizer.ClosetNotOwnerMessage, frameSize=(-1, 1, -1, 1), text_wordwrap=10, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(0.88, 1, 0.55), geom_pos=(0, 0, -.08), text_scale=0.08, text_pos=(0, 0.06)) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.ClosetPopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.21), command=self._handleNotOwnerMessageOK) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + return + + def _handleNotOwnerMessageOK(self): + self.popupInfo.reparentTo(hidden) + taskMgr.doMethodLater(0.1, self.popupChangeClothesGUI, self.uniqueName('popupChangeClothesGUI')) + + def __popupAreYouSurePanel(self): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = None + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelButtonImage = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=TTLocalizer.ClosetAreYouSureMessage, frameSize=(-1, 1, -1, 1), text_wordwrap=10, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(0.88, 1, 0.55), geom_pos=(0, 0, -.08), text_scale=0.08, text_pos=(0, 0.08)) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.ClosetPopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(-0.1, 0.0, -0.21), command=self._handleYesImSure) + DirectButton(self.popupInfo, image=cancelButtonImage, relief=None, text=TTLocalizer.ClosetPopupCancel, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.1, 0.0, -0.21), command=self._handleNotSure) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + return + + def _handleYesImSure(self): + self.popupInfo.reparentTo(hidden) + self._handlePurchaseDone() + + def _handleNotSure(self): + self.popupInfo.reparentTo(hidden) + + def _openDoors(self): + if self.closetTrack: + self.closetTrack.finish() + leftHpr = Vec3(-110, 0, 0) + rightHpr = Vec3(110, 0, 0) + self.closetTrack = Parallel() + if self.rightDoor: + self.closetTrack.append(self.rightDoor.hprInterval(0.5, rightHpr)) + if self.leftDoor: + self.closetTrack.append(self.leftDoor.hprInterval(0.5, leftHpr)) + self.closetTrack.start() + + def _closeDoors(self): + if self.closetTrack: + self.closetTrack.finish() + leftHpr = Vec3(0, 0, 0) + rightHpr = Vec3(0, 0, 0) + self.closetTrack = Parallel() + if self.rightDoor: + self.closetTrack.append(self.rightDoor.hprInterval(0.5, rightHpr)) + if self.leftDoor: + self.closetTrack.append(self.leftDoor.hprInterval(0.5, leftHpr)) + self.closetTrack.start() diff --git a/toontown/estate/DistributedClosetAI.py b/toontown/estate/DistributedClosetAI.py new file mode 100755 index 00000000..bb72ea6f --- /dev/null +++ b/toontown/estate/DistributedClosetAI.py @@ -0,0 +1,210 @@ +from toontown.estate.DistributedFurnitureItemAI import DistributedFurnitureItemAI +from toontown.toon.ToonDNA import ToonDNA +from direct.distributed.ClockDelta import globalClockDelta +import ClosetGlobals + + +class DistributedClosetAI(DistributedFurnitureItemAI): + notify = directNotify.newCategory('DistributedClosetAI') + + def __init__(self, air, furnitureMgr, itemType): + DistributedFurnitureItemAI.__init__(self, air, furnitureMgr, itemType) + + self.avId = None + self.customerDNA = None + self.topList = [] + self.botList = [] + self.gender = 'm' + self.removedBottoms = [] + self.removedTops = [] + + def generate(self): + if self.furnitureMgr.ownerId: + owner = self.air.doId2do.get(self.furnitureMgr.ownerId) + if owner: + self.topList = owner.clothesTopsList + self.botList = owner.clothesBottomsList + self.gender = owner.dna.gender + else: + self.air.dbInterface.queryObject(self.air.dbId, self.furnitureMgr.ownerId, self.__gotOwner) + + def __gotOwner(self, dclass, fields): + if dclass != self.air.dclassesByName['DistributedToonAI']: + return + self.botList = fields['setClothesBottomsList'][0] + self.topList = fields['setClothesTopsList'][0] + dna = ToonDNA(str=fields['setDNAString'][0]) + self.gender = dna.gender + + def getOwnerId(self): + return self.furnitureMgr.ownerId + + def __verifyAvatarInMyZone(self, av): + return av.getLocation() == self.getLocation() + + def enterAvatar(self): + avId = self.air.getAvatarIdFromSender() + if self.avId: + if self.avId == avId: + self.air.writeServerEvent('suspicious', avId, 'Tried to use closet twice!') + self.sendUpdateToAvatarId(avId, 'freeAvatar', []) + return + av = self.air.doId2do.get(avId) + if not av: + self.air.writeServerEvent('suspicious', avId, 'Not in same shard as closet!') + return + if not self.__verifyAvatarInMyZone(av): + self.air.writeServerEvent('suspicious', avId, 'Not in same zone as closet!') + return + self.customerDNA = av.dna + self.avId = avId + self.d_setState(ClosetGlobals.OPEN, avId, self.furnitureMgr.ownerId, self.gender, self.topList, self.botList) + + def removeItem(self, item, topOrBottom): + avId = self.air.getAvatarIdFromSender() + if avId != self.furnitureMgr.ownerId: + self.air.writeServerEvent('suspicious', avId, 'Tried to remove item from someone else\'s closet!') + return + if avId != self.avId: + self.air.writeServerEvent('suspicious', avId, 'Tried to remove item while not interacting with closet!') + return + av = self.air.doId2do.get(avId) + if not av: + self.air.writeServerEvent('suspicious', avId, 'Tried to interact with a closet from another shard!') + return + tempDna = ToonDNA() + if not tempDna.isValidNetString(item): + self.air.writeServerEvent('suspicious', avId, 'Sent an invalid DNA string!') + return + tempDna.makeFromNetString(item) + if topOrBottom == ClosetGlobals.SHIRT: + self.removedTops.append([tempDna.topTex, tempDna.topTexColor, tempDna.sleeveTex, tempDna.sleeveTexColor]) + elif topOrBottom == ClosetGlobals.SHORTS: + self.removedBottoms.append([tempDna.botTex, tempDna.botTexColor]) + else: + self.air.writeServerEvent('suspicious', avId, 'Set an invalid topOrBottom value!') + return + + def __checkValidDNAChange(self, av, testDNA): + if testDNA.head != av.dna.head: + return False + if testDNA.torso != av.dna.torso: + if av.dna.gender == 'm': + return False + elif testDNA.torso[0] != av.dna.torso[0]: + return False + if testDNA.legs != av.dna.legs: + return False + if testDNA.gender != av.dna.gender: + return False + if testDNA.armColor != av.dna.armColor: + return False + if testDNA.gloveColor != av.dna.gloveColor: + return False + if testDNA.legColor != av.dna.legColor: + return False + if testDNA.headColor != av.dna.headColor: + return False + return True + + def setDNA(self, dnaString, finished, whichItem): + avId = self.air.getAvatarIdFromSender() + if avId != self.avId: + self.air.writeServerEvent('suspicious', avId, 'Tried to set DNA from closet while not using it!') + return + av = self.air.doId2do.get(avId) + if not av: + self.air.writeServerEvent('suspicious', avId, 'Interacted with a closet from another shard!') + return + if not self.__verifyAvatarInMyZone(av): + self.air.writeServerEvent('suspicious', avId, 'Tried to setDNA while in another zone!') + return + testDna = ToonDNA() + if not testDna.isValidNetString(dnaString): + self.air.writeServerEvent('suspicious', avId, 'Tried to set invalid DNA at a closet!') + return + if not finished: + testDna.makeFromNetString(dnaString) + if not self.__checkValidDNAChange(av, testDna): + self.air.writeServerEvent('suspicious', avId, 'Tried to change their DNA temporarily!') + return + self.sendUpdate('setCustomerDNA', [avId, dnaString]) + return + elif finished == 1: + self.d_setMovie(ClosetGlobals.CLOSET_MOVIE_COMPLETE, avId, globalClockDelta.getRealNetworkTime()) + self.resetMovie() + self.d_setState(ClosetGlobals.CLOSED, 0, self.furnitureMgr.ownerId, self.gender, self.topList, self.botList) + av.b_setDNAString(self.customerDNA.makeNetString()) + self.removedBottoms = [] + self.removedTops = [] + self.customerDNA = None + self.avId = None + elif finished == 2: + if avId != self.furnitureMgr.ownerId: + self.air.writeServerEvent('suspicious', avId, 'Tried to set their clothes from somebody else\'s closet!') + return + testDna.makeFromNetString(dnaString) + if whichItem & ClosetGlobals.SHIRT: + success = av.replaceItemInClothesTopsList(testDna.topTex, testDna.topTexColor, testDna.sleeveTex, testDna.sleeveTexColor, self.customerDNA.topTex, self.customerDNA.topTexColor, self.customerDNA.sleeveTex, self.customerDNA.sleeveTexColor) + if success: + self.customerDNA.topTex = testDna.topTex + self.customerDNA.topTexColor = testDna.topTexColor + self.customerDNA.sleeveTex = testDna.sleeveTex + self.customerDNA.sleeveTexColor = testDna.sleeveTexColor + else: + self.air.writeServerEvent('suspicious', avId, 'Tried to set their shirt to a shirt they don\'t own!') + if whichItem & ClosetGlobals.SHORTS: + success = av.replaceItemInClothesBottomsList(testDna.botTex, testDna.botTexColor, self.customerDNA.botTex, self.customerDNA.botTexColor) + if success: + self.customerDNA.botTex = testDna.botTex + self.customerDNA.botTexColor = testDna.botTexColor + if self.customerDNA.torso != testDna.torso: + if self.customerDNA.gender == 'm': + self.air.writeServerEvent('suspicious', avId, 'Tried to change their torso size!') + return + elif self.customerDNA.torso[0] != testDna.torso[0]: + self.air.writeServerEvent('suspicious', avId, 'Tried to change their torso size!') + return + self.customerDNA.torso = testDna.torso + else: + self.air.writeServerEvent('suspicious', avId, 'Tried to set their shorts to a pair they don\'t own!') + for bottom in self.removedBottoms: + botTex, botTexColor = bottom + success = av.removeItemInClothesBottomsList(botTex, botTexColor) + if not success: + self.air.writeServerEvent('suspicious', avId, 'Tried to remove a bottom they didn\'t have!') + for top in self.removedTops: + topTex, topTexColor, sleeveTex, sleeveTexColor = top + success = av.removeItemInClothesTopsList(topTex, topTexColor, sleeveTex, sleeveTexColor) + if not success: + self.air.writeServerEvent('suspicious', avId, 'Tried to remove a top they didn\'t have!') + av.b_setDNAString(self.customerDNA.makeNetString()) + av.b_setClothesTopsList(av.getClothesTopsList()) + av.b_setClothesBottomsList(av.getClothesBottomsList()) + self.topList = av.getClothesTopsList() + self.botList = av.getClothesBottomsList() + self.removedBottoms = [] + self.removedTops = [] + self.d_setMovie(ClosetGlobals.CLOSET_MOVIE_COMPLETE, avId, globalClockDelta.getRealNetworkTime()) + self.resetMovie() + self.d_setState(ClosetGlobals.CLOSED, 0, self.furnitureMgr.ownerId, self.gender, self.topList, self.botList) + self.customerDNA = None + self.avId = None + + def setState(self, todo0, todo1, todo2, todo3, todo4, todo5): + pass + + def d_setState(self, mode, avId, ownerId, gender, topList, botList): + self.sendUpdate('setState', [mode, avId, ownerId, gender, topList, botList]) + + def d_setMovie(self, movie, avId, time): + self.sendUpdate('setMovie', [movie, avId, time]) + + def resetMovie(self): + taskMgr.doMethodLater(1, self.d_setMovie, 'resetMovie-%d' % self.getDoId(), extraArgs=[ClosetGlobals.CLOSET_MOVIE_CLEAR, 0, globalClockDelta.getRealNetworkTime()]) + + def setMovie(self, todo0, todo1, todo2): + pass + + def setCustomerDNA(self, todo0, todo1): + pass diff --git a/toontown/estate/DistributedEstate.py b/toontown/estate/DistributedEstate.py new file mode 100755 index 00000000..a14acdbf --- /dev/null +++ b/toontown/estate/DistributedEstate.py @@ -0,0 +1,441 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.gui.DirectGui import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import math +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.task.Task import Task +from toontown.toonbase import TTLocalizer +import random +import cPickle +import time +import HouseGlobals +from toontown.estate import GardenGlobals +from toontown.estate import FlowerSellGUI +from toontown.toontowngui import TTDialog +from toontown.fishing import FishSellGUI + +class DistributedEstate(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedEstate') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.closestHouse = 0 + self.ground = None + self.dayTrack = None + self.sunTrack = None + self.airplane = None + self.flowerSellBox = None + self.estateDoneEvent = 'estateDone' + self.load() + self.initCamera() + self.plotTable = [] + self.idList = [] + self.flowerGuiDoneEvent = 'flowerGuiDone' + self.fishGuiDoneEvent = 'fishGuiDone' + + def disable(self): + self.notify.debug('disable') + self.__stopBirds() + self.__stopCrickets() + DistributedObject.DistributedObject.disable(self) + self.ignoreAll() + + def delete(self): + self.notify.debug('delete') + self.unload() + DistributedObject.DistributedObject.delete(self) + + def load(self): + self.defaultSignModel = loader.loadModel('phase_13/models/parties/eventSign') + self.activityIconsModel = loader.loadModel('phase_4/models/parties/eventSignIcons') + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.HALLOWEEN): + self.loadWitch() + else: + self.loadAirplane() + self.loadFlowerSellBox() + self.loadFishSellBox() + self.oldClear = base.win.getClearColor() + base.win.setClearColor(Vec4(0.09, 0.55, 0.21, 1.0)) + + def unload(self): + self.ignoreAll() + base.win.setClearColor(self.oldClear) + self.__killAirplaneTask() + self.__killDaytimeTask() + self.__stopBirds() + self.__stopCrickets() + if self.dayTrack: + self.dayTrack.pause() + self.dayTrack = None + self.__killSunTask() + if self.sunTrack: + self.sunTrack.pause() + self.sunTrack = None + if self.ground: + self.ground.removeNode() + del self.ground + if self.airplane: + self.airplane.removeNode() + del self.airplane + self.airplane = None + if self.flowerSellBox: + self.flowerSellBox.removeNode() + del self.flowerSellBox + self.flowerSellBox = None + if self.fishSellBox: + self.fishSellBox.removeNode() + del self.fishSellBox + self.fishSellBox = None + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + + def loadAirplane(self): + self.airplane = loader.loadModel('phase_4/models/props/airplane.bam') + self.airplane.setScale(4) + self.airplane.setPos(0, 0, 1) + self.banner = self.airplane.find('**/*banner') + bannerText = TextNode('bannerText') + bannerText.setTextColor(1, 0, 0, 1) + bannerText.setAlign(bannerText.ACenter) + bannerText.setFont(ToontownGlobals.getSignFont()) + bannerText.setText(TTLocalizer.EstatePlaneReturn) + self.bn = self.banner.attachNewNode(bannerText.generate()) + self.bn.setHpr(180, 0, 0) + self.bn.setPos(-5.8, 0.1, -0.25) + self.bn.setScale(0.95) + self.bn.setDepthTest(1) + self.bn.setDepthWrite(1) + self.bn.setDepthOffset(500) + + def loadWitch(self): + if not self.airplane: + self.airplane = loader.loadModel('phase_4/models/props/tt_m_prp_ext_flyingWitch.bam') + + def __replaceAirplane__(): + self.airplane.reparentTo(hidden) + del self.airplane + self.airplane = loader.loadModel('phase_4/models/props/tt_m_prp_ext_flyingWitch.bam') + self.airplane.setScale(2) + self.airplane.setPos(0, 0, 1) + self.airplane.find('**/').setH(180) + bannerText = TextNode('bannerText') + bannerText.setTextColor(1, 0, 0, 1) + bannerText.setAlign(bannerText.ACenter) + bannerText.setFont(ToontownGlobals.getSignFont()) + bannerText.setText(TTLocalizer.EstatePlaneHoliday) + self.bn = self.airplane.attachNewNode(bannerText.generate()) + self.bn.setPos(-20.0, -.1, 0) + self.bn.setH(180) + self.bn.setScale(2.35) + self.bn.setDepthTest(1) + self.bn.setDepthWrite(1) + self.bn.setDepthOffset(500) + + replacement = Sequence(LerpColorScaleInterval(self.airplane, 0.1, Vec4(1, 1, 1, 0)), Func(__replaceAirplane__), LerpColorScaleInterval(self.airplane, 0.1, Vec4(1, 1, 1, 1))) + replacement.start() + + def unloadWitch(self): + + def __replaceWitch__(): + self.airplane.reparentTo(hidden) + del self.airplane + del self.bn + self.airplane = loader.loadModel('phase_4/models/props/airplane.bam') + self.airplane.setScale(4) + self.airplane.setPos(0, 0, 1) + self.banner = self.airplane.find('**/*banner') + bannerText = TextNode('bannerText') + bannerText.setTextColor(1, 0, 0, 1) + bannerText.setAlign(bannerText.ACenter) + bannerText.setFont(ToontownGlobals.getSignFont()) + bannerText.setText(TTLocalizer.EstatePlaneReturn) + self.bn = self.banner.attachNewNode(bannerText.generate()) + self.bn.setHpr(180, 0, 0) + self.bn.setPos(-5.8, 0.1, -0.25) + self.bn.setScale(0.95) + self.bn.setDepthTest(1) + self.bn.setDepthWrite(1) + self.bn.setDepthOffset(500) + + replacement = Sequence(LerpColorScaleInterval(self.airplane, 0.1, Vec4(1, 1, 1, 0)), Func(__replaceWitch__), LerpColorScaleInterval(self.airplane, 0.1, Vec4(1, 1, 1, 1))) + replacement.start() + + def initCamera(self): + initCamPos = VBase3(0, -10, 5) + initCamHpr = VBase3(0, -10, 0) + + def setEstateType(self, index): + self.estateType = index + + def setHouseInfo(self, houseInfo): + self.notify.debug('setHouseInfo') + houseType, housePos = cPickle.loads(houseInfo) + self.loadEstate(houseType, housePos) + + def loadEstate(self, indexList, posList): + self.notify.debug('loadEstate') + self.houseType = indexList + self.housePos = posList + self.numHouses = len(self.houseType) + self.house = [None] * self.numHouses + + def __startAirplaneTask(self): + self.theta = 0 + self.phi = 0 + taskMgr.remove(self.taskName('estate-airplane')) + taskMgr.add(self.airplaneFlyTask, self.taskName('estate-airplane')) + + def __pauseAirplaneTask(self): + pause = 45 + self.phi = 0 + self.airplane.reparentTo(hidden) + self.theta = (self.theta + 10) % 360 + taskMgr.remove(self.taskName('estate-airplane')) + taskMgr.doMethodLater(pause, self.airplaneFlyTask, self.taskName('estate-airplane')) + + def __killAirplaneTask(self): + taskMgr.remove(self.taskName('estate-airplane')) + + def airplaneFlyTask(self, task): + rad = 300.0 + amp = 80.0 + self.theta += 0.25 + self.phi += 0.005 + sinPhi = math.sin(self.phi) + if sinPhi <= 0: + self.__pauseAirplaneTask() + angle = math.pi * self.theta / 180.0 + x = rad * math.cos(angle) + y = rad * math.sin(angle) + z = amp * sinPhi + self.airplane.reparentTo(render) + self.airplane.setH(90 + self.theta) + self.airplane.setPos(x, y, z) + return Task.cont + + def sendHouseColor(self, index, r, g, b, a): + self.house[index].setColor(r, g, b, a) + + def setTreasureIds(self, doIds): + self.flyingTreasureId = [] + for id in doIds: + self.flyingTreasureId.append(id) + + def setDawnTime(self, ts): + self.notify.debug('setDawnTime') + self.dawnTime = ts + self.sendUpdate('requestServerTime', []) + + def setServerTime(self, ts): + self.notify.debug('setServerTime') + self.serverTime = ts + self.clientTime = time.time() % HouseGlobals.DAY_NIGHT_PERIOD + self.deltaTime = self.clientTime - self.serverTime + if base.dayNightEnabled: + self.__initDaytimeTask() + self.__initSunTask() + self.__startAirplaneTask() + + def getDeltaTime(self): + curTime = time.time() % HouseGlobals.DAY_NIGHT_PERIOD + dawnTime = self.dawnTime + dT = (curTime - dawnTime - self.deltaTime) % HouseGlobals.DAY_NIGHT_PERIOD + self.notify.debug( + 'getDeltaTime = %s. curTime=%s. dawnTime=%s. serverTime=%s. deltaTime=%s' + % (dT, curTime, dawnTime, self.serverTime, self.deltaTime)) + return dT + + def __initDaytimeTask(self): + self.__killDaytimeTask() + task = Task(self.__dayTimeTask) + dT = self.getDeltaTime() + task.ts = dT + taskMgr.add(task, self.taskName('daytime')) + + def __killDaytimeTask(self): + taskMgr.remove(self.taskName('daytime')) + + def __dayTimeTask(self, task): + taskName = self.taskName('daytime') + track = Sequence(Parallel(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, HouseGlobals.HALF_DAY_PERIOD, Vec4(1, 0.6, 0.6, 1)), LerpColorScaleInterval(base.cr.playGame.hood.sky, HouseGlobals.HALF_DAY_PERIOD, Vec4(1, 0.8, 0.8, 1))), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, HouseGlobals.HALF_NIGHT_PERIOD, Vec4(0.2, 0.2, 0.5, 1)), LerpColorScaleInterval(base.cr.playGame.hood.sky, HouseGlobals.HALF_NIGHT_PERIOD, Vec4(0.2, 0.2, 0.4, 1))), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, HouseGlobals.HALF_NIGHT_PERIOD, Vec4(0.6, 0.6, 0.8, 1)), LerpColorScaleInterval(base.cr.playGame.hood.sky, HouseGlobals.HALF_NIGHT_PERIOD, Vec4(0.5, 0.5, 0.6, 1))), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, HouseGlobals.HALF_DAY_PERIOD, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.cr.playGame.hood.sky, HouseGlobals.HALF_DAY_PERIOD, Vec4(1, 1, 1, 1))), Func(base.cr.playGame.hood.loader.geom.clearColorScale), Func(base.cr.playGame.hood.sky.clearColorScale)) + if self.dayTrack: + self.dayTrack.finish() + self.dayTrack = track + ts = 0 + if hasattr(task, 'ts'): + ts = task.ts + self.dayTrack.start(ts) + taskMgr.doMethodLater(HouseGlobals.DAY_NIGHT_PERIOD - ts, self.__dayTimeTask, self.taskName('daytime')) + return Task.done + + def __initSunTask(self): + self.__killSunTask() + task = Task(self.__sunTask) + dT = self.getDeltaTime() + task.ts = dT + taskMgr.add(task, self.taskName('sunTask')) + + def __killSunTask(self): + taskMgr.remove(self.taskName('sunTask')) + + def __sunTask(self, task): + sunMoonNode = base.cr.playGame.hood.loader.sunMoonNode + sun = base.cr.playGame.hood.loader.sun + h = 30 + halfPeriod = HouseGlobals.DAY_NIGHT_PERIOD / 2.0 + track = Sequence(Parallel(LerpHprInterval(sunMoonNode, HouseGlobals.HALF_DAY_PERIOD, Vec3(0, 0, 0)), LerpColorScaleInterval(sun, HouseGlobals.HALF_DAY_PERIOD, Vec4(1, 1, 0.5, 1))), Func(sun.clearColorScale), Func(self.__stopBirds), LerpHprInterval(sunMoonNode, 0.2, Vec3(0, -h - 3, 0)), LerpHprInterval(sunMoonNode, 0.1, Vec3(0, -h + 2, 0)), LerpHprInterval(sunMoonNode, 0.1, Vec3(0, -h - 1.5, 0)), LerpHprInterval(sunMoonNode, 0.1, Vec3(0, -h, 0)), Func(self.notify.debug, 'night'), Wait(HouseGlobals.HALF_NIGHT_PERIOD - 0.5), LerpHprInterval(sunMoonNode, HouseGlobals.HALF_NIGHT_PERIOD, Vec3(0, 0, 0)), Func(self.__startBirds), LerpHprInterval(sunMoonNode, 0.2, Vec3(0, h + 3, 0)), LerpHprInterval(sunMoonNode, 0.1, Vec3(0, h - 2, 0)), LerpHprInterval(sunMoonNode, 0.1, Vec3(0, h + 1.5, 0)), LerpHprInterval(sunMoonNode, 0.1, Vec3(0, h, 0)), Func(self.notify.debug, 'day'), Func(sunMoonNode.setHpr, 0, h, 0), Wait(HouseGlobals.HALF_DAY_PERIOD - 0.5)) + if self.sunTrack: + self.sunTrack.finish() + self.sunTrack = track + ts = 0 + if hasattr(task, 'ts'): + ts = task.ts + if ts > HouseGlobals.HALF_DAY_PERIOD and ts < HouseGlobals.DAY_NIGHT_PERIOD - HouseGlobals.HALF_DAY_PERIOD: + self.__stopBirds() + self.__startCrickets() + else: + self.__stopCrickets() + self.__startBirds() + self.sunTrack.start(ts) + taskMgr.doMethodLater(HouseGlobals.DAY_NIGHT_PERIOD - ts, self.__sunTask, self.taskName('sunTask')) + return Task.done + + def __stopBirds(self): + taskMgr.remove('estate-birds') + + def __startBirds(self): + self.__stopBirds() + taskMgr.doMethodLater(1, self.__birds, 'estate-birds') + + def __birds(self, task): + base.playSfx(random.choice(base.cr.playGame.hood.loader.birdSound)) + t = random.random() * 20.0 + 1 + taskMgr.doMethodLater(t, self.__birds, 'estate-birds') + return Task.done + + def __stopCrickets(self): + taskMgr.remove('estate-crickets') + + def __startCrickets(self): + self.__stopCrickets() + taskMgr.doMethodLater(1, self.__crickets, 'estate-crickets') + + def __crickets(self, task): + sfx = base.cr.playGame.hood.loader.cricketSound + track = Sequence(Func(base.playSfx, random.choice(sfx)), Wait(1)) + track.start() + t = random.random() * 20.0 + 1 + taskMgr.doMethodLater(t, self.__crickets, 'estate-crickets') + return Task.done + + def getLastEpochTimeStamp(self): + return self.lastEpochTimeStamp + + def setLastEpochTimeStamp(self, ts): + self.lastEpochTimeStamp = ts + + def getIdList(self): + return self.idList + + def setIdList(self, idList): + self.idList = idList + + def loadFlowerSellBox(self): + self.flowerSellBox = loader.loadModel('phase_5.5/models/estate/wheelbarrel.bam') + self.flowerSellBox.setPos(-142.586, 4.353, 0.025) + self.flowerSellBox.reparentTo(render) + colNode = self.flowerSellBox.find('**/collision') + colNode.setName('FlowerSellBox') + self.accept('enterFlowerSellBox', self.__touchedFlowerSellBox) + + def __touchedFlowerSellBox(self, entry): + if base.localAvatar.doId in self.idList: + if len(base.localAvatar.flowerBasket.flowerList): + self.popupFlowerGUI() + + def __handleSaleDone(self, sell = 0): + self.ignore(self.flowerGuiDoneEvent) + self.sendUpdate('completeFlowerSale', [sell]) + self.ignore('stoppedAsleep') + self.flowerGui.destroy() + self.flowerGui = None + + def popupFlowerGUI(self): + self.acceptOnce(self.flowerGuiDoneEvent, self.__handleSaleDone) + self.flowerGui = FlowerSellGUI.FlowerSellGUI(self.flowerGuiDoneEvent) + self.accept('stoppedAsleep', self.__handleSaleDone) + + def loadFishSellBox(self): + self.fishSellBox = loader.loadModel('phase_4/models/minigames/treasure_chest.bam') + self.fishSellBox.setPos(45, -165.75, 0.025) + self.fishSellBox.setH(210) + self.fishSellBox.reparentTo(render) + cSphere = CollisionSphere(0.0, 0.0, 0.0, 2.25) + cSphere.setTangible(0) + colNode = CollisionNode('FishSellBox') + colNode.addSolid(cSphere) + cSpherePath = self.fishSellBox.attachNewNode(colNode) + cSpherePath.hide() + cSpherePath.setCollideMask(ToontownGlobals.WallBitmask) + self.accept('enterFishSellBox', self.__touchedFishSellBox) + + def __touchedFishSellBox(self, entry): + if base.localAvatar.doId in self.idList: + if base.localAvatar.fishTank.getFish(): + self.popupFishGUI() + + def __handleFishSaleDone(self, sell=0): + if sell: + self.sendUpdate('completeFishSale') + else: + base.localAvatar.setSystemMessage(0, TTLocalizer.STOREOWNER_NOFISH) + + base.setCellsAvailable(base.bottomCells, 1) + base.cr.playGame.getPlace().setState('walk') + self.ignore(self.fishGuiDoneEvent) + self.ignore('stoppedAsleep') + self.fishGui.destroy() + self.fishGui = None + + def popupFishGUI(self): + base.setCellsAvailable(base.bottomCells, 0) + base.cr.playGame.getPlace().setState('stopped') + self.acceptOnce(self.fishGuiDoneEvent, self.__handleFishSaleDone) + self.fishGui = FishSellGUI.FishSellGUI(self.fishGuiDoneEvent) + self.accept('stoppedAsleep', self.__handleFishSaleDone) + + def thankSeller(self, mode, fish, maxFish): + if mode == ToontownGlobals.FISHSALE_TROPHY: + base.localAvatar.setSystemMessage(0, TTLocalizer.STOREOWNER_TROPHY % (fish, maxFish)) + elif mode == ToontownGlobals.FISHSALE_COMPLETE: + base.localAvatar.setSystemMessage(0, TTLocalizer.STOREOWNER_THANKSFISH) + + def closedAwardDialog(self, value): + self.awardDialog.destroy() + base.cr.playGame.getPlace().detectedGardenPlotDone() + + def awardedTrophy(self, avId): + if base.localAvatar.doId == avId: + base.cr.playGame.getPlace().detectedGardenPlotUse() + msg = TTLocalizer.GardenTrophyAwarded % (len(base.localAvatar.getFlowerCollection()), GardenGlobals.getNumberOfFlowerVarieties()) + self.awardDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=msg, command=self.closedAwardDialog) + + def setClouds(self, clouds): + self.clouds = clouds + base.cr.playGame.hood.loader.setCloudSwitch(clouds) + + def getClouds(self): + if hasattr(self, 'clouds'): + return self.clouds + else: + return 0 + + def cannonsOver(self, arg = None): + base.localAvatar.setSystemMessage(0, TTLocalizer.EstateCannonGameEnd) + + def gameTableOver(self, arg = None): + base.localAvatar.setSystemMessage(0, TTLocalizer.GameTableRentalEnd) diff --git a/toontown/estate/DistributedEstateAI.py b/toontown/estate/DistributedEstateAI.py new file mode 100755 index 00000000..7582d146 --- /dev/null +++ b/toontown/estate/DistributedEstateAI.py @@ -0,0 +1,966 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.toonbase import ToontownGlobals +import HouseGlobals +import time, random + +from toontown.fishing.DistributedFishingPondAI import DistributedFishingPondAI +from toontown.fishing import FishingTargetGlobals, FishGlobals +from toontown.safezone import TreasureGlobals +from toontown.safezone.SZTreasurePlannerAI import SZTreasurePlannerAI +from toontown.safezone import DistributedEFlyingTreasureAI +from toontown.safezone import ButterflyGlobals +from toontown.safezone import DistributedButterflyAI +from toontown.safezone.DistributedFishingSpotAI import DistributedFishingSpotAI +from toontown.parties.DistributedPartyJukeboxActivityAI import DistributedPartyJukeboxActivityAI + +from DistributedGardenBoxAI import * +from DistributedGardenPlotAI import * +from DistributedGagTreeAI import * +from DistributedFlowerAI import * +from DistributedStatuaryAI import * +from DistributedToonStatuaryAI import * +from DistributedAnimatedStatuaryAI import * +from DistributedChangingStatuaryAI import * +import GardenGlobals + +from DistributedCannonAI import * +from DistributedTargetAI import * +import CannonGlobals + +# planted, waterLevel, lastCheck, growthLevel, optional +NULL_PLANT = [-1, -1, 0, 0, 0] +NULL_TREES = [NULL_PLANT] * 8 +NULL_FLOWERS = [NULL_PLANT] * 10 +NULL_STATUARY = 0 + +NULL_DATA = {'trees': NULL_TREES, 'statuary': NULL_STATUARY, 'flowers': NULL_FLOWERS} + +from direct.distributed.PyDatagramIterator import * +from direct.distributed.PyDatagram import * + +class Garden: + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedEstateAI') + + WANT_FLOWERS = True # puke + WANT_TREES = True + WANT_STATUARY = True + + def __init__(self, air, avId): + self.air = air + self.avId = avId + + self.trees = set() + self.flowers = set() + self.objects = set() + + if not self.air.dbConn: + self.notify.warning('Not using mongodb, garden data will be non-persistent') + self.data = NULL_DATA.copy() + + else: + d = self.air.dbGlobalCursor.gardens.find_one({'avId': avId}) + if d is None: + self.data = NULL_DATA.copy() + self.air.dbGlobalCursor.gardens.update({'avId': avId}, {'$set': NULL_DATA}, upsert=True) + + else: + self.data = d + + self.data.pop('_id', None) + + def destroy(self): + messenger.send('garden-%d-666-going-down' % self.avId) + + for tree in self.trees: + tree.requestDelete() + + for flower in self.flowers: + flower.requestDelete() + + for object in self.objects: + object.requestDelete() + + self.air = None + self.estateMgr = None + + def create(self, estateMgr): + self.estateMgr = estateMgr + + if self.avId not in estateMgr.toons: + estateMgr.notify.warning('Garden associated to unknown avatar %d, deleting...' % self.avId) + return False + + houseIndex = estateMgr.toons.index(self.avId) + + if self.WANT_FLOWERS: + boxIndex = 0 + boxes = [] + boxDefs = GardenGlobals.estateBoxes[houseIndex] + for x, y, h, boxType in boxDefs: + box = DistributedGardenBoxAI(self) + + box.setTypeIndex(boxType) + box.setPos(x, y, 0) + box.setH(h) + box.setOwnerIndex(houseIndex) + box.generateWithRequired(estateMgr.zoneId) + + self.objects.add(box) + boxes.append(box) + boxIndex += 1 + + self._boxes = boxes + + plots = GardenGlobals.estatePlots[houseIndex] + treeIndex = 0 + flowerIndex = 0 + for plot, (x, y, h, type) in enumerate(plots): + if type == GardenGlobals.GAG_TREE_TYPE and self.WANT_TREES: + data = self.data['trees'][treeIndex] + + planted, waterLevel, lastCheck, growthLevel, lastHarvested = data + + if planted != -1: + obj = self.plantTree(treeIndex, planted, waterLevel=waterLevel, + lastCheck=lastCheck, growthLevel=growthLevel, + lastHarvested=lastHarvested, generate=False) + + self.trees.add(obj) + + else: + obj = self.placePlot(treeIndex) + + obj.setPos(x, y, 0) + obj.setH(h) + obj.setPlot(plot) + obj.setOwnerIndex(houseIndex) + obj.generateWithRequired(estateMgr.zoneId) + treeIndex += 1 + + elif type == GardenGlobals.FLOWER_TYPE and self.WANT_FLOWERS: + data = self.data['flowers'][flowerIndex] + + planted, waterLevel, lastCheck, growthLevel, variety = data + + if planted != -1: + obj = self.plantFlower(flowerIndex, planted, variety, waterLevel=waterLevel, + lastCheck=lastCheck, growthLevel=growthLevel, + generate=False) + + else: + obj = self.placePlot(flowerIndex) + obj.flowerIndex = flowerIndex + + obj.setPlot(plot) + obj.setOwnerIndex(houseIndex) + obj.generateWithRequired(estateMgr.zoneId) + + index = (0, 1, 2, 2, 2, 3, 3, 3, 4, 4)[flowerIndex] + idx = (0, 0, 0, 1, 2, 0, 1, 2, 0, 1)[flowerIndex] + obj.sendUpdate('setBoxDoId', [boxes[index].doId, idx]) + flowerIndex += 1 + + elif type == GardenGlobals.STATUARY_TYPE and self.WANT_STATUARY: + data = self.data['statuary'] + if data == 0: + obj = self.placePlot(-1) + + else: + obj = self.placeStatuary(data, generate=False) + + obj.setPos(x, y, 0) + obj.setH(h) + obj.setPlot(plot) + obj.setOwnerIndex(houseIndex) + obj.generateWithRequired(estateMgr.zoneId) + + for tree in self.trees: + tree.calcDependencies() + + self.reconsiderAvatarOrganicBonus() + + return True + + def hasTree(self, track, index): + x = track * 7 + index + for tree in self.data['trees']: + if tree[0] == x: + return True + + return False + + def getTree(self, track, index): + for tree in self.trees: + if tree.typeIndex == track * 7 + index: + return tree + + def plantTree(self, treeIndex, value, plot=None, waterLevel=-1, + lastCheck=0, growthLevel=0, lastHarvested=0, + ownerIndex=-1, plotId=-1, pos=None, generate=True): + if not self.air: + return + + if plot: + if plot not in self.objects: + return + + plot.requestDelete() + self.objects.remove(plot) + + tree = DistributedGagTreeAI(self) + + tree.setTypeIndex(value) + tree.setWaterLevel(waterLevel) + tree.setGrowthLevel(growthLevel) + if ownerIndex != -1: + tree.setOwnerIndex(ownerIndex) + + if plotId != -1: + tree.setPlot(plotId) + + if pos is not None: + pos, h = pos + tree.setPos(pos) + tree.setH(h) + + tree.treeIndex = treeIndex + tree.calculate(lastHarvested, lastCheck) + self.trees.add(tree) + + if generate: + tree.generateWithRequired(self.estateMgr.zoneId) + + return tree + + def placePlot(self, treeIndex): + obj = DistributedGardenPlotAI(self) + obj.treeIndex = treeIndex + self.objects.add(obj) + + return obj + + def plantFlower(self, flowerIndex, species, variety, plot=None, waterLevel=-1, + lastCheck=0, growthLevel=0, ownerIndex=-1, plotId=-1, generate=True): + if not self.air: + return + + if plot: + if plot not in self.objects: + return + + plot.requestDelete() + self.objects.remove(plot) + + flower = DistributedFlowerAI(self) + + flower.setTypeIndex(species) + flower.setVariety(variety) + flower.setWaterLevel(waterLevel) + flower.setGrowthLevel(growthLevel) + if ownerIndex != -1: + flower.setOwnerIndex(ownerIndex) + + if plotId != -1: + flower.setPlot(plotId) + + flower.flowerIndex = flowerIndex + flower.calculate(lastCheck) + self.flowers.add(flower) + + if generate: + flower.generateWithRequired(self.estateMgr.zoneId) + + return flower + + def placeStatuary(self, data, plot=None, plotId=-1, ownerIndex=-1, + pos=None, generate=True): + if not self.air: + return + + if plot: + if plot not in self.objects: + return + + plot.requestDelete() + self.objects.remove(plot) + + data, lastCheck, index, growthLevel = self.S_unpack(data) + + dclass = DistributedStatuaryAI + if index in GardenGlobals.ToonStatuaryTypeIndices: + dclass = DistributedToonStatuaryAI + + elif index in GardenGlobals.ChangingStatuaryTypeIndices: + dclass = DistributedChangingStatuaryAI + + elif index in GardenGlobals.AnimatedStatuaryTypeIndices: + dclass = DistributedAnimatedStatuaryAI + + obj = dclass(self) + obj.growthLevel = growthLevel + obj.index = index + obj.data = data + + if ownerIndex != -1: + obj.setOwnerIndex(ownerIndex) + + if plotId != -1: + obj.setPlot(plotId) + + if pos is not None: + pos, h = pos + obj.setPos(pos) + obj.setH(h) + + obj.calculate(lastCheck) + + self.objects.add(obj) + + if generate: + obj.announceGenerate() + + return obj + + # Data structure + # VERY HIGH (vh) (64-bit) + # high high (H) = data (32-bit) + # high low (L) = lastCheck (32-bit) + # VERY LOW (vl) (16-bit) + # low high (h) = index (8-bit) + # low low (l) = growthLevel (8-bit) + + @staticmethod + def S_pack(data, lastCheck, index, growthLevel): + vh = data << 32 | lastCheck + vl = index << 8 | growthLevel + + return vh << 16 | vl + + @staticmethod + def S_unpack(x): + vh = x >> 16 + vl = x & 0xFFFF + + data = vh >> 32 + lastCheck = vh & 0xFFFFFFFF + + index = vl >> 8 + growthLevel = vl & 0xFF + + return data, lastCheck, index, growthLevel + + def getNullPlant(self): + return NULL_PLANT + + def reconsiderAvatarOrganicBonus(self): + av = self.air.doId2do.get(self.avId) + if not av: + return + + bonus = [-1] * 7 + for track in xrange(7): + for level in xrange(8):#7 + if not self.hasTree(track, level): + break + + tree = self.getTree(track, level) + if tree.getGrowthLevel() < tree.growthThresholds[1] or tree.getWilted(): + break + + bonus[track] = level - 1 + + av.b_setTrackBonusLevel(bonus) + + def update(self): + if self.air.dbConn: + self.air.dbGlobalCursor.gardens.update({'avId': self.avId}, {'$set': self.data}, upsert=True) + +class GardenManager: + def __init__(self, mgr): + self.mgr = mgr + self.gardens = {} + + def handleSingleGarden(self, avId): + g = Garden(self.mgr.air, avId) + g.gardenMgr = self + res = g.create(self.mgr) + if res: + self.gardens[avId] = g + + def destroy(self): + for garden in self.gardens.values(): + garden.destroy() + + del self.gardens + +class Rental: + def __init__(self, estate): + self.estate = estate + self.objects = set() + + def destroy(self): + del self.estate + for object in self.objects: + if not object.isDeleted(): + object.requestDelete() + taskMgr.remove(object.uniqueName('delete')) + self.objects = set() + +class CannonRental(Rental): + def generateObjects(self): + target = DistributedTargetAI(self.estate.air) + target.generateWithRequired(self.estate.zoneId) + + for drop in CannonGlobals.cannonDrops: + cannon = DistributedCannonAI(self.estate.air, self.estate.zoneId, target.doId, *drop) + cannon.generateWithRequired(self.estate.zoneId) + self.objects.add(cannon) + + self.generateTreasures() + self.estate.b_setClouds(1) + + def destroy(self): + self.estate.b_setClouds(0) + Rental.destroy(self) + + def generateTreasures(self): + doIds = [] + z = 35 + + for i in xrange(20): + x = random.randint(100, 300) - 200 + y = random.randint(100, 300) - 200 + treasure = DistributedEFlyingTreasureAI.DistributedEFlyingTreasureAI(self.estate.air, self, 7, x, y, z) + treasure.generateWithRequired(self.estate.zoneId) + self.objects.add(treasure) + doIds.append(treasure.doId) + + self.estate.sendUpdate('setTreasureIds', [doIds]) + + def grabAttempt(self, avId, treasureId): + av = self.estate.air.doId2do.get(avId) + if av == None: + self.estate.air.writeServerEvent('suspicious', avId, 'TreasurePlannerAI.grabAttempt unknown avatar') + self.estate.notify.warning('avid: %s does not exist' % avId) + return + + treasure = self.estate.air.doId2do.get(treasureId) + if self.validAvatar(av): + treasure.d_setGrab(avId) + self.deleteTreasureSoon(treasure) + + else: + treasure.d_setReject() + + def deleteTreasureSoon(self, treasure): + taskName = treasure.uniqueName('delete') + taskMgr.doMethodLater(5, self.__deleteTreasureNow, taskName, extraArgs=(treasure, taskName)) + + def __deleteTreasureNow(self, treasure, taskName): + treasure.requestDelete() + + def validAvatar(self, av): + if av.getMaxHp() == av.getHp(): + return 0 + + av.toonUp(3) + return 1 + +class DistributedEstateAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedEstateAI') + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.toons = [0, 0, 0, 0, 0, 0] + self.items = [[], [], [], [], [], []] + self.estateType = 0 + self.cloudType = 0 + self.dawnTime = 0 + self.lastEpochTimestamp = 0 + self.rentalTimestamp = 0 + self.houses = [None] * 6 + self.rentalType = 0 + self.rentalHandle = None + + self.pond = None + self.jukebox = None + self.spots = [] + self.butterflies = [] + + self.owner = None + + self.gardenManager = GardenManager(self) + self.__pendingGardens = {} + + @property + def hostId(self): + return 1000000001 + + def generate(self): + DistributedObjectAI.generate(self) + + self.pond = DistributedFishingPondAI(simbase.air) + self.pond.setArea(ToontownGlobals.MyEstate) + self.pond.generateWithRequired(self.zoneId) + self.pond.start() + + treasureType, healAmount, spawnPoints, spawnRate, maxTreasures = TreasureGlobals.SafeZoneTreasureSpawns[ToontownGlobals.MyEstate] + self.treasurePlanner = SZTreasurePlannerAI(self.zoneId, treasureType, healAmount, spawnPoints, spawnRate, maxTreasures) + self.treasurePlanner.start() + + spot = DistributedFishingSpotAI(self.air) + spot.setPondDoId(self.pond.getDoId()) + spot.setPosHpr(49.1029, -124.805, 0.344704, 90, 0, 0) + spot.generateWithRequired(self.zoneId) + self.spots.append(spot) + + spot = DistributedFishingSpotAI(self.air) + spot.setPondDoId(self.pond.getDoId()) + spot.setPosHpr(46.5222, -134.739, 0.390713, 75, 0, 0) + spot.generateWithRequired(self.zoneId) + self.spots.append(spot) + + spot = DistributedFishingSpotAI(self.air) + spot.setPondDoId(self.pond.getDoId()) + spot.setPosHpr(41.31, -144.559, 0.375978, 45, 0, 0) + spot.generateWithRequired(self.zoneId) + self.spots.append(spot) + + spot = DistributedFishingSpotAI(self.air) + spot.setPondDoId(self.pond.getDoId()) + spot.setPosHpr(46.8254, -113.682, 0.46015, 135, 0, 0) + spot.generateWithRequired(self.zoneId) + self.spots.append(spot) + + self.jukebox = DistributedPartyJukeboxActivityAI(self.air, self.doId, (0, 0, 0, 0)) + self.jukebox.generateWithRequired(self.zoneId) + self.jukebox.sendUpdate('setX', [-21.8630]) + self.jukebox.sendUpdate('setY', [-154.669]) + self.jukebox.sendUpdate('setH', [148.7050]) + self.jukebox.sendUpdate('unloadSign') + + ButterflyGlobals.generateIndexes(self.zoneId, ButterflyGlobals.ESTATE) + for i in xrange(0, ButterflyGlobals.NUM_BUTTERFLY_AREAS[ButterflyGlobals.ESTATE]): + for j in xrange(0, ButterflyGlobals.NUM_BUTTERFLIES[ButterflyGlobals.ESTATE]): + butterfly = DistributedButterflyAI.DistributedButterflyAI(self.air, ButterflyGlobals.ESTATE, i, self.zoneId) + butterfly.generateWithRequired(self.zoneId) + butterfly.start() + self.butterflies.append(butterfly) + + def destroy(self): + for house in self.houses: + if house: + house.requestDelete() + for butterfly in self.butterflies: + if butterfly: + butterfly.requestDelete() + del self.houses[:] + if self.pond: + for spot in self.spots: + spot.requestDelete() + self.spots = [] + self.pond.requestDelete() + self.pond = None + if self.jukebox: + self.jukebox.requestDelete() + if self.treasurePlanner: + self.treasurePlanner.stop() + + self.gardenManager.destroy() + if self.rentalHandle: + self.rentalHandle.destroy() + self.rentalHandle = None + + self.requestDelete() + + def addDistObj(self, distObj): + self.doId2do[distObj.doId] = distObj + + def setClientReady(self): + self.sendUpdate('setEstateReady', []) + + def setEstateType(self, type): + self.estateType = type + + def d_setEstateType(self, type): + self.sendUpdate('setEstateType', [type]) + + def b_setEstateType(self, type): + self.setEstateType(type) + self.d_setEstateType(type) + + def getEstateType(self): + return self.estateType + + def requestServerTime(self): + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'setServerTime', [time.time() % HouseGlobals.DAY_NIGHT_PERIOD]) + + def setDawnTime(self, dawnTime): + self.dawnTime = dawnTime + + def d_setDawnTime(self, dawnTime): + self.sendUpdate('setDawnTime', [dawnTime]) + + def b_setDawnTime(self, dawnTime): + self.setDawnTime(dawnTime) + self.d_setDawnTime(dawnTime) + + def getDawnTime(self): + return self.dawnTime + + def setLastEpochTimeStamp(self, last): + self.lastEpochTimestamp = last + + def d_setLastEpochTimeStamp(self, last): + self.sendUpdate('setLastEpochTimeStamp', [last]) + + def b_setLastEpochTimeStamp(self, last): + self.setLastEpochTimeStamp(last) + self.d_setLastEpochTimeStamp(last) + + def getLastEpochTimeStamp(self): + return self.lastEpochTimestamp + + def setRentalTimeStamp(self, rental): + self.rentalTimestamp = rental + + def d_setRentalTimeStamp(self, rental): + self.sendUpdate('setRentalTimeStamp', [rental]) + + def b_setRentalTimeStamp(self, rental): + self.setRentalTimeStamp(rental) + self.d_setRentalTimeStamp(rental) + + def getRentalTimeStamp(self): + return self.rentalTimestamp + + def b_setRentalType(self, type): + self.d_setRentalType(type) + self.setRentalType(type) + + def d_setRentalType(self, type): + self.sendUpdate('setRentalType', [type]) + + def setRentalType(self, type): + expirestamp = self.getRentalTimeStamp() + if expirestamp == 0: + expire = 0 + + else: + expire = int(expirestamp - time.time()) + + if expire < 0: + self.rentalType = 0 + self.d_setRentalType(0) + self.b_setRentalTimeStamp(0) + + else: + if self.rentalType == type: + return + + self.rentalType = type + if self.rentalHandle: + self.rentalHandle.destroy() + self.rentalHandle = None + + if self.rentalType == ToontownGlobals.RentalCannon: + self.rentalHandle = CannonRental(self) + + else: + self.notify.warning('Unknown rental %s' % self.rentalType) + return + + self.rentalHandle.generateObjects() + + def getRentalType(self): + return self.rentalType + + def rentItem(self, rentType, duration): + self.b_setRentalTimeStamp(time.time() + duration * 60) + self.b_setRentalType(rentType) + + def setSlot0ToonId(self, id): + self.toons[0] = id + + def d_setSlot0ToonId(self, id): + self.sendUpdate('setSlot0ToonId', [id]) + + def b_setSlot0ToonId(self, id): + self.setSlot0ToonId(id) + self.d_setSlot0ToonId(id) + + def getSlot0ToonId(self): + return self.toons[0] + + def setSlot0Items(self, items): + self.items[0] = items + + def d_setSlot0Items(self, items): + self.sendUpdate('setSlot5Items', [items]) + + def b_setSlot0Items(self, items): + self.setSlot0Items(items) + self.d_setSlot0Items(items) + + def getSlot0Items(self): + return self.items[0] + + def setSlot1ToonId(self, id): + self.toons[1] = id + + def d_setSlot1ToonId(self, id): + self.sendUpdate('setSlot1ToonId', [id]) + + def b_setSlot1ToonId(self, id): + self.setSlot1ToonId(id) + self.d_setSlot1ToonId(id) + + def getSlot1ToonId(self): + return self.toons[1] + + def setSlot1Items(self, items): + self.items[1] = items + + def d_setSlot1Items(self, items): + self.sendUpdate('setSlot2Items', [items]) + + def b_setSlot1Items(self, items): + self.setSlot2Items(items) + self.d_setSlot2Items(items) + + def getSlot1Items(self): + return self.items[1] + + def setSlot2ToonId(self, id): + self.toons[2] = id + + def d_setSlot2ToonId(self, id): + self.sendUpdate('setSlot2ToonId', [id]) + + def b_setSlot2ToonId(self, id): + self.setSlot2ToonId(id) + self.d_setSlot2ToonId(id) + + def getSlot2ToonId(self): + return self.toons[2] + + def setSlot2Items(self, items): + self.items[2] = items + + def d_setSlot2Items(self, items): + self.sendUpdate('setSlot2Items', [items]) + + def b_setSlot2Items(self, items): + self.setSlot2Items(items) + self.d_setSlot2Items(items) + + def getSlot2Items(self): + return self.items[2] + + def setSlot3ToonId(self, id): + self.toons[3] = id + + def d_setSlot3ToonId(self, id): + self.sendUpdate('setSlot3ToonId', [id]) + + def b_setSlot3ToonId(self, id): + self.setSlot3ToonId(id) + self.d_setSlot3ToonId(id) + + def getSlot3ToonId(self): + return self.toons[3] + + def setSlot3Items(self, items): + self.items[3] = items + + def d_setSlot3Items(self, items): + self.sendUpdate('setSlot3Items', [items]) + + def b_setSlot3Items(self, items): + self.setSlot3Items(items) + self.d_setSlot3Items(items) + + def getSlot3Items(self): + return self.items[3] + + def setSlot4ToonId(self, id): + self.toons[4] = id + + def d_setSlot4ToonId(self, id): + self.sendUpdate('setSlot4ToonId', [id]) + + def b_setSlot5ToonId(self, id): + self.setSlot4ToonId(id) + self.d_setSlot4ToonId(id) + + def getSlot4ToonId(self): + return self.toons[4] + + def setSlot4Items(self, items): + self.items[4] = items + + def d_setSlot4Items(self, items): + self.sendUpdate('setSlot4Items', [items]) + + def b_setSlot4Items(self, items): + self.setSlot4Items(items) + self.d_setSlot4Items(items) + + def getSlot4Items(self): + return self.items[4] + + def setSlot5ToonId(self, id): + self.toons[5] = id + + def d_setSlot5ToonId(self, id): + self.sendUpdate('setSlot5ToonId', [id]) + + def b_setSlot5ToonId(self, id): + self.setSlot5ToonId(id) + self.d_setSlot5ToonId(id) + + def getSlot5ToonId(self): + return self.toons[5] + + def setSlot5Items(self, items): + self.items[5] = items + + def d_setSlot5Items(self, items): + self.sendUpdate('setSlot5Items', [items]) + + def b_setSlot5Items(self, items): + self.setSlot5Items(items) + self.d_setSlot5Items(items) + + def getSlot5Items(self): + return self.items[5] + + def setIdList(self, idList): + for i in xrange(len(idList)): + if i >= 6: + return + self.toons[i] = idList[i] + + def d_setIdList(self, idList): + self.sendUpdate('setIdList', [idList]) + + def b_setIdList(self, idList): + self.setIdList(idList) + self.d_setIdLst(idList) + + def completeFlowerSale(self, flag): + if not flag: + return + + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if not av: + return + + collection = av.flowerCollection + + earning = 0 + newSpecies = 0 + for flower in av.flowerBasket.getFlower(): + if collection.collectFlower(flower) == GardenGlobals.COLLECT_NEW_ENTRY: + newSpecies += 1 + + earning += flower.getValue() + + av.b_setFlowerBasket([], []) + av.d_setFlowerCollection(*av.flowerCollection.getNetLists()) + av.addMoney(earning) + + oldSpecies = len(collection) - newSpecies + dt = abs(len(collection) // 10 - oldSpecies // 10) + if dt: + self.notify.info('%d is getting a gardening trophy!' % avId) + + maxHp = av.getMaxHp() + maxHp = min(ToontownGlobals.MaxHpLimit, maxHp + dt) + av.b_setMaxHp(maxHp) + av.toonUp(maxHp) + + self.sendUpdate('awardedTrophy', [avId]) + + av.b_setGardenTrophies(range(len(collection) // 10)) + + def completeFishSale(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + + if not av: + return + + if self.air.fishManager.creditFishTank(av): + self.sendUpdateToAvatarId(avId, 'thankSeller', [ToontownGlobals.FISHSALE_TROPHY, len(av.fishCollection), FishGlobals.getTotalNumFish()]) + else: + self.sendUpdateToAvatarId(avId, 'thankSeller', [ToontownGlobals.FISHSALE_COMPLETE, 0, 0]) + + def setClouds(self, clouds): + self.cloudType = clouds + + def d_setClouds(self, clouds): + self.sendUpdate('setClouds', [clouds]) + + def b_setClouds(self, clouds): + self.setClouds(clouds) + self.d_setClouds(clouds) + + def getClouds(self): + return self.cloudType + + # Garden methods + def getToonSlot(self, avId): + if avId not in self.toons: + return + + return self.toons.index(avId) + + def setSlot0Garden(self, flag): + self.__pendingGardens[0] = flag + + def setSlot1Garden(self, flag): + self.__pendingGardens[1] = flag + + def setSlot2Garden(self, flag): + self.__pendingGardens[2] = flag + + def setSlot3Garden(self, flag): + self.__pendingGardens[3] = flag + + def setSlot4Garden(self, flag): + self.__pendingGardens[4] = flag + + def setSlot5Garden(self, flag): + self.__pendingGardens[5] = flag + + def placeStarterGarden(self, avId, record=1): + av = self.air.doId2do.get(avId) + if not av: + return + + slot = self.getToonSlot(avId) + if slot is None: + return + + if record: + av.b_setGardenStarted(1) + self.sendUpdate('setSlot%dGarden' % slot, ['started']) + + self.notify.info('placeStarterGarden %d %d' % (avId, slot)) + self.gardenManager.handleSingleGarden(avId) + + def announceGenerate(self): + DistributedObjectAI.announceGenerate(self) + self.sendUpdate('setIdList', [self.toons]) + + for index, started in self.__pendingGardens.items(): + if started: + self.gardenManager.handleSingleGarden(self.toons[index]) + + self.__pendingGardens = {} + if config.GetBool('fake-garden-started-ai', False): + self.placeStarterGarden(100000002, 0) \ No newline at end of file diff --git a/toontown/estate/DistributedFireworksCannon.py b/toontown/estate/DistributedFireworksCannon.py new file mode 100755 index 00000000..55727940 --- /dev/null +++ b/toontown/estate/DistributedFireworksCannon.py @@ -0,0 +1,109 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from HouseGlobals import * +from toontown.effects import DistributedFireworkShow +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from pandac.PandaModules import CollisionSphere +from pandac.PandaModules import CollisionNode +import FireworksGui + +class DistributedFireworksCannon(DistributedFireworkShow.DistributedFireworkShow): + notify = directNotify.newCategory('DistributedFireworksCannon') + + def __init__(self, cr): + DistributedFireworkShow.DistributedFireworkShow.__init__(self, cr) + self.fireworksGui = None + self.load() + return + + def generateInit(self): + DistributedFireworkShow.DistributedFireworkShow.generateInit(self) + self.fireworksSphereEvent = self.uniqueName('fireworksSphere') + self.fireworksSphereEnterEvent = 'enter' + self.fireworksSphereEvent + self.fireworksGuiDoneEvent = 'fireworksGuiDone' + self.shootEvent = 'fireworkShootEvent' + self.collSphere = CollisionSphere(0, 0, 0, 2.5) + self.collSphere.setTangible(1) + self.collNode = CollisionNode(self.fireworksSphereEvent) + self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.geom.attachNewNode(self.collNode) + + def generate(self): + DistributedFireworkShow.DistributedFireworkShow.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + self.accept(self.fireworksSphereEnterEvent, self.__handleEnterSphere) + + def disable(self): + self.notify.debug('disable') + self.ignore(self.fireworksSphereEnterEvent) + self.ignore(self.shootEvent) + self.ignore(self.fireworksGuiDoneEvent) + if self.fireworksGui: + self.fireworksGui.destroy() + self.fireworksGui = None + DistributedFireworkShow.DistributedFireworkShow.disable(self) + return + + def delete(self): + self.notify.debug('delete') + self.geom.removeNode() + DistributedFireworkShow.DistributedFireworkShow.delete(self) + + def load(self): + self.geom = loader.loadModel('phase_5/models/props/trashcan_TT.bam') + self.geom.reparentTo(base.cr.playGame.hood.loader.geom) + self.geom.setScale(0.5) + + def __handleEnterSphere(self, collEntry): + self.notify.debug('handleEnterSphere()') + self.ignore(self.fireworksSphereEnterEvent) + self.sendUpdate('avatarEnter', []) + + def __handleFireworksDone(self): + self.ignore(self.fireworksGuiDoneEvent) + self.ignore(self.shootEvent) + self.sendUpdate('avatarExit') + self.fireworksGui.destroy() + self.fireworksGui = None + return + + def freeAvatar(self): + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + self.accept(self.fireworksSphereEnterEvent, self.__handleEnterSphere) + + def setMovie(self, mode, avId, timestamp): + timeStamp = globalClockDelta.localElapsedTime(timestamp) + isLocalToon = avId == base.localAvatar.doId + if mode == FIREWORKS_MOVIE_CLEAR: + self.notify.debug('setMovie: clear') + return + elif mode == FIREWORKS_MOVIE_GUI: + self.notify.debug('setMovie: gui') + if isLocalToon: + self.fireworksGui = FireworksGui.FireworksGui(self.fireworksGuiDoneEvent, self.shootEvent) + self.accept(self.fireworksGuiDoneEvent, self.__handleFireworksDone) + self.accept(self.shootEvent, self.localShootFirework) + return + else: + self.notify.warning('unknown mode in setMovie: %s' % mode) + + def setPosition(self, x, y, z): + self.pos = [x, y, z] + self.geom.setPos(x, y, z) + + def localShootFirework(self, index): + style = index + col1, col2 = self.fireworksGui.getCurColor() + amp = 30 + dummy = base.localAvatar.attachNewNode('dummy') + dummy.setPos(0, 100, 60) + pos = dummy.getPos(render) + dummy.removeNode() + print 'lauFirework: %s, col=%s' % (index, col1) + self.d_requestFirework(pos[0], pos[1], pos[2], style, col1, col2) diff --git a/toontown/estate/DistributedFireworksCannonAI.py b/toontown/estate/DistributedFireworksCannonAI.py new file mode 100755 index 00000000..bcdafa7a --- /dev/null +++ b/toontown/estate/DistributedFireworksCannonAI.py @@ -0,0 +1,20 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.effects.DistributedFireworkShowAI import DistributedFireworkShowAI + +class DistributedFireworksCannonAI(DistributedFireworkShowAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedFireworksCannonAI") + + def avatarEnter(self): + pass + + def avatarExit(self): + pass + + def freeAvatar(self): + pass + + def setMovie(self, todo0, todo1, todo2): + pass + + def setPosition(self, todo0, todo1, todo2): + pass diff --git a/toontown/estate/DistributedFlower.py b/toontown/estate/DistributedFlower.py new file mode 100755 index 00000000..d6afcf94 --- /dev/null +++ b/toontown/estate/DistributedFlower.py @@ -0,0 +1,213 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.estate import DistributedPlantBase +from toontown.estate.DistributedGardenBox import DistributedGardenBox +from toontown.estate import FlowerBase +from toontown.estate import GardenGlobals +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer + +DIRT_AS_WATER_INDICATOR = True +DIRT_MOUND_HEIGHT = 0.3 + +class DistributedFlower(DistributedPlantBase.DistributedPlantBase, FlowerBase.FlowerBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFlower') + deferFor = 2 + + def __init__(self, cr): + DistributedPlantBase.DistributedPlantBase.__init__(self, cr) + FlowerBase.FlowerBase.__init__(self, 49, 0) + self.confirmDialog = None + self.stickUp = 1.07 + if DIRT_AS_WATER_INDICATOR: + self.stickUp += DIRT_MOUND_HEIGHT + self.collSphereRadius = 2.2 + self.shadowScale = 0.5 + self.collSphereOffset = 0.0 + self.dirtMound = None + self.sandMound = None + self.resultDialog = None + + def delete(self): + DistributedPlantBase.DistributedPlantBase.delete(self) + + del self.dirtMound + del self.sandMound + + def setTypeIndex(self, typeIndex): + DistributedPlantBase.DistributedPlantBase.setTypeIndex(self, typeIndex) + self.setSpecies(typeIndex) + + def showWiltOrBloom(self): + if not self.model: + return + nodePath = self.model + desat = None + flowerColorIndex = GardenGlobals.PlantAttributes[self.getSpecies()]['varieties'][self.getVariety()][1] + colorTuple = GardenGlobals.FlowerColors[flowerColorIndex] + useWilted = self.waterLevel < 0 + wilt = nodePath.find('**/*wilt*') + bloom = nodePath.find('**/*bloom*') + if useWilted: + wilt.show() + desat = wilt.find('**/*desat*') + bloom.hide() + leaves = wilt.findAllMatches('**/*leaf*') + for leafIndex in xrange(leaves.getNumPaths()): + leaf = leaves.getPath(leafIndex) + leaf.setColorScale(1.0, 0.3, 0.1, 1.0) + + else: + bloom.show() + desat = bloom.find('**/*desat*') + wilt.hide() + if desat and not desat.isEmpty(): + desat.setColorScale(colorTuple[0], colorTuple[1], colorTuple[2], 1.0) + elif not self.isSeedling(): + nodePath.setColorScale(colorTuple[0], colorTuple[1], colorTuple[2], 1.0) + return + + def loadModel(self): + DistributedPlantBase.DistributedPlantBase.loadModel(self) + self.showWiltOrBloom() + self.model.setH(180) + flowerScale = 2.0 + invFlowerScale = 1.0 / flowerScale + self.model.setScale(flowerScale) + if DIRT_AS_WATER_INDICATOR: + dirtMoundScale = invFlowerScale * 0.63 + self.dirtMound = loader.loadModel('phase_5.5/models/estate/dirt_mound') + self.dirtMound.reparentTo(self.model) + self.dirtMound.setScale(dirtMoundScale) + self.dirtMound.setZ(self.dirtMound.getZ() - DIRT_MOUND_HEIGHT / 2.0) + self.sandMound = loader.loadModel('phase_5.5/models/estate/sand_mound') + self.sandMound.reparentTo(self.model) + self.sandMound.setScale(dirtMoundScale) + self.sandMound.setZ(self.sandMound.getZ() - DIRT_MOUND_HEIGHT / 2.0) + self.adjustWaterIndicator() + + def handlePicking(self): + messenger.send('wakeup') + fullName = GardenGlobals.getFlowerVarietyName(self.species, self.variety) + if self.isWilted(): + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.ConfirmWiltedFlower % {'plant': fullName}, command=self.confirmCallback) + elif not self.isFruiting(): + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.ConfirmUnbloomingFlower % {'plant': fullName}, command=self.confirmCallback) + elif base.localAvatar.isFlowerBasketFull(): + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.CancelOnly, text=TTLocalizer.ConfirmBasketFull, command=self.confirmCallback) + else: + shovel = base.localAvatar.shovel + skill = base.localAvatar.shovelSkill + shovelPower = GardenGlobals.getShovelPower(shovel, skill) + giveSkillUp = True + beansRequired = GardenGlobals.getNumBeansRequired(self.species, self.variety) + if not shovelPower == beansRequired: + giveSkillUp = False + if giveSkillUp: + if skill == GardenGlobals.getMaxShovelSkill(): + text = (TTLocalizer.ConfirmMaxedSkillFlower % {'plant': fullName},) + else: + text = TTLocalizer.ConfirmSkillupFlower % {'plant': fullName} + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=text, command=self.confirmCallback) + else: + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.ConfirmNoSkillupFlower % {'plant': fullName}, command=self.confirmCallback) + self.confirmDialog.show() + base.localAvatar.setInGardenAction(self) + base.cr.playGame.getPlace().detectedGardenPlotUse() + + def confirmCallback(self, value): + self.notify.debug('value=%d' % value) + self.confirmDialog.destroy() + self.confirmDialog = None + if value > 0: + self.doPicking() + else: + self.finishInteraction() + return + + def doPicking(self): + if not self.canBePicked(): + self.notify.debug("I don't own this flower, just returning") + return + base.localAvatar.showGardeningGui() + base.localAvatar.removeShovelRelatedDoId(self.doId) + base.localAvatar.setInGardenAction(self) + base.cr.playGame.getPlace().detectedGardenPlotUse() + self.sendUpdate('removeItem', []) + + def setWaterLevel(self, waterLevel): + DistributedPlantBase.DistributedPlantBase.setWaterLevel(self, waterLevel) + self.showWiltOrBloom() + if self.model: + self.adjustWaterIndicator() + + def setGrowthLevel(self, growthLevel): + origGrowthLevel = self.growthLevel + self.growthLevel = growthLevel + if origGrowthLevel > -1: + self.loadModel() + self.makeMovieNode() + + if hasattr(self, '_boxDoId'): + self.setBoxDoId(*self._boxDoId) + + def makeMovieNode(self): + self.movieNode = self.rotateNode.attachNewNode('moviePos') + self.movieNode.setPos(0, 3, 0) + self.movieNode.setH(180) + self.stick2Ground() + + def setupShadow(self): + if DIRT_AS_WATER_INDICATOR: + pass + else: + DistributedPlantBase.DistributedPlantBase.setupShadow(self) + self.setShadowHeight(-(self.stickUp + 1)) + + def adjustWaterIndicator(self): + if DIRT_AS_WATER_INDICATOR: + if self.dirtMound: + curWaterLevel = self.waterLevel + if curWaterLevel > self.maxWaterLevel: + curWaterLevel = self.maxWaterLevel + if curWaterLevel > 0: + darkestColorScale = 0.4 + lightestColorScale = 1.0 + scaleRange = lightestColorScale - darkestColorScale + scaleIncrement = scaleRange / self.maxWaterLevel + darker = lightestColorScale - scaleIncrement * curWaterLevel + self.dirtMound.setColorScale(darker, darker, darker, 1.0) + self.sandMound.hide() + self.dirtMound.show() + else: + self.sandMound.show() + self.dirtMound.hide() + elif self.model: + color = float(self.waterLevel) / self.maxWaterLevel + self.dropShadow.setColor(0.0, 0.0, 0.0, color) + + def doResultDialog(self): + self.startInteraction() + flowerName = GardenGlobals.getFlowerVarietyName(self.species, self.variety) + stringToShow = TTLocalizer.getResultPlantedSomethingSentence(flowerName) + self.resultDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=stringToShow, command=self.resultsCallback) + + def resultsCallback(self, value): + self.notify.debug('value=%d' % value) + if self.resultDialog: + self.resultDialog.destroy() + self.resultDialog = None + self.finishInteraction() + + def setBoxDoId(self, boxId, index): + self._boxDoId = (boxId, index) + box = base.cr.doId2do[boxId] + x = GardenGlobals.FLOWER_POS[box.typeIndex][index] + + self.setPos(0, 0, 0) + self.reparentTo(box) + self.setZ(1.5) + self.setX(x) + + def stick2Ground(self): + pass + \ No newline at end of file diff --git a/toontown/estate/DistributedFlowerAI.py b/toontown/estate/DistributedFlowerAI.py new file mode 100755 index 00000000..9f79ace6 --- /dev/null +++ b/toontown/estate/DistributedFlowerAI.py @@ -0,0 +1,145 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.ai.MagicWordGlobal import * +from DistributedPlantBaseAI import DistributedPlantBaseAI +from FlowerBase import FlowerBase + +import GardenGlobals +import time + +ONE_DAY = 86400 + +class DistributedFlowerAI(DistributedPlantBaseAI, FlowerBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFlowerAI') + + def setTypeIndex(self, value): + DistributedPlantBaseAI.setTypeIndex(self, value) + FlowerBase.setSpecies(self, value) + + def calculate(self, lastCheck): + now = int(time.time()) + if lastCheck == 0: + lastCheck = now + + grown = 0 + + # Water level + elapsed = now - lastCheck + while elapsed > ONE_DAY: + if self.waterLevel >= 0: + grown += 1 + + elapsed -= ONE_DAY + self.waterLevel -= 1 + + self.waterLevel = max(self.waterLevel, -2) + + # Growth level + maxGrowth = self.growthThresholds[2] + newGL = min(self.growthLevel + grown, maxGrowth) + self.setGrowthLevel(newGL) + + self.lastCheck = now - elapsed + self.update() + + def update(self): + mdata = map(list, self.mgr.data['flowers']) + mdata[self.flowerIndex] = [self.getSpecies(), self.waterLevel, self.lastCheck, self.getGrowthLevel(), self.getVariety()] + self.mgr.data['flowers'] = mdata + self.mgr.update() + + def removeItem(self, usingSatanPickAll=0): + avId = self.air.getAvatarIdFromSender() + if not usingSatanPickAll: + if avId != self.ownerDoId: + self.air.writeServerEvent('suspicious', avId, 'tried to remove someone else\'s flower!') + return + + self.d_setMovie(GardenGlobals.MOVIE_REMOVE) + + action = 'remove' + if self.getGrowthLevel() >= self.growthThresholds[2]: + action = 'pick' + + def _remove(task): + if not self.air: + return + + av = self.air.doId2do.get(self.ownerDoId) + if not av: + return + + plot = self.mgr.placePlot(self.flowerIndex) + plot.flowerIndex = self.flowerIndex + plot.setPlot(self.plot) + plot.setOwnerIndex(self.ownerIndex) + plot.generateWithRequired(self.zoneId) + + index = (0, 1, 2, 2, 2, 3, 3, 3, 4, 4)[self.flowerIndex] + idx = (0, 0, 0, 1, 2, 0, 1, 2, 0, 1)[self.flowerIndex] + plot.sendUpdate('setBoxDoId', [self.mgr._boxes[index].doId, idx]) + + self.air.writeServerEvent('%s-flower' % action, avId, plot=self.plot) + self.requestDelete() + + self.mgr.flowers.remove(self) + + mdata = map(list, self.mgr.data['flowers']) + mdata[self.flowerIndex] = self.mgr.getNullPlant() + self.mgr.data['flowers'] = mdata + self.mgr.update() + + if action == 'pick': + av.b_setShovelSkill(av.getShovelSkill() + self.getValue()) + av.addFlowerToBasket(self.getSpecies(), self.getVariety()) + + if task: + return task.done + + if usingSatanPickAll: + _remove(None) + + else: + taskMgr.doMethodLater(7, _remove, self.uniqueName('do-remove')) + +@magicWord(category=CATEGORY_PROGRAMMER) +def satanGrowFlowers(): + av = spellbook.getTarget() + estate = av.air.estateManager._lookupEstate(av) + + if not estate: + return 'Estate not found!' + + garden = estate.gardenManager.gardens.get(av.doId) + if not garden: + return 'Garden not found!' + + now = int(time.time()) + i = 0 + for flower in garden.flowers.union(garden.trees): + flower.b_setWaterLevel(5) + flower.b_setGrowthLevel(2) + flower.update() + i += 1 + + return '%d disgusting flowers and trees grown' % i + +@magicWord(category=CATEGORY_PROGRAMMER) +def satanPickAll(): + av = spellbook.getTarget() + estate = av.air.estateManager._lookupEstate(av) + + if not estate: + return 'Estate not found!' + + garden = estate.gardenManager.gardens.get(av.doId) + if not garden: + return 'Garden not found!' + + i = 0 + for flower in garden.flowers.copy(): + if flower.getGrowthLevel() >= flower.growthThresholds[2]: + flower.removeItem(1) + i += 1 + + return '%d disgusting flowers picked' % i + \ No newline at end of file diff --git a/toontown/estate/DistributedFurnitureItem.py b/toontown/estate/DistributedFurnitureItem.py new file mode 100755 index 00000000..5b6e88a5 --- /dev/null +++ b/toontown/estate/DistributedFurnitureItem.py @@ -0,0 +1,126 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.catalog import CatalogItem +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from toontown.toonbase import TTLocalizer +import DistributedHouseItem +from direct.distributed import DistributedSmoothNode +from direct.task import Task +import HouseGlobals + +class DistributedFurnitureItem(DistributedHouseItem.DistributedHouseItem, DistributedSmoothNode.DistributedSmoothNode): + notify = directNotify.newCategory('DistributedFurnitureItem') + def __init__(self, cr): + DistributedHouseItem.DistributedHouseItem.__init__(self, cr) + DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr) + NodePath.__init__(self) + self.localControl = True + self.__broadcastFrequency = 0.25 + self.__adjustStarted = 0 + self.furnitureMgr = None + self.transmitRelativeTo = None + return + + def generate(self): + DistributedHouseItem.DistributedHouseItem.generate(self) + DistributedSmoothNode.DistributedSmoothNode.generate(self) + self.__taskName = self.taskName('sendRequestPosHpr') + + def announceGenerate(self): + DistributedHouseItem.DistributedHouseItem.announceGenerate(self) + DistributedSmoothNode.DistributedSmoothNode.announceGenerate(self) + self.load() + + def load(self): + pass + + def disable(self): + taskMgr.remove(self.__taskName) + self.stopSmooth() + self.furnitureMgr.dfitems.remove(self) + self.furnitureMgr = None + DistributedHouseItem.DistributedHouseItem.disable(self) + DistributedSmoothNode.DistributedSmoothNode.disable(self) + return + + def delete(self): + self.removeNode() + del self.item + DistributedHouseItem.DistributedHouseItem.delete(self) + DistributedSmoothNode.DistributedSmoothNode.delete(self) + + def setItem(self, furnitureMgrId, blob): + self.furnitureMgr = self.cr.doId2do.get(furnitureMgrId) + if self.furnitureMgr: + self.furnitureMgr.dfitems.append(self) + self.item = CatalogItem.getItem(blob, store=CatalogItem.Customization) + self.assign(self.loadModel()) + interior = self.furnitureMgr.getInteriorObject() + self.reparentTo(interior.interior) + + def loadModel(self): + return self.item.loadModel() + + def startAdjustPosHpr(self): + if self.__adjustStarted: + return + self.__adjustStarted = 1 + self.clearSmoothing() + taskMgr.remove(self.__taskName) + posHpr = self.__getPosHpr() + self.__oldPosHpr = posHpr + self.sendRequestPosHpr(0, *posHpr) + taskMgr.doMethodLater(self.__broadcastFrequency, self.__posHprBroadcast, self.__taskName) + + def __posHprBroadcast(self, task): + posHpr = self.__getPosHpr() + if not self.__comparePosHpr(posHpr, self.__oldPosHpr, 0.1): + pass + else: + self.__oldPosHpr = posHpr + self.sendRequestPosHpr(0, *posHpr) + taskMgr.doMethodLater(self.__broadcastFrequency, self.__posHprBroadcast, self.__taskName) + return Task.done + + def stopAdjustPosHpr(self): + if not self.__adjustStarted: + return + self.__adjustStarted = 0 + taskMgr.remove(self.__taskName) + posHpr = self.__getPosHpr() + self.sendRequestPosHpr(1, *posHpr) + del self.__oldPosHpr + + def sendRequestPosHpr(self, final, x, y, z, h, p, r): + t = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('requestPosHpr', (final, x, y, z, h, 0, 0, t)) + + def setMode(self, mode, avId): + if mode == HouseGlobals.FURNITURE_MODE_START: + if avId != base.localAvatar.getDoId(): + self.startSmooth() + elif mode == HouseGlobals.FURNITURE_MODE_STOP: + if avId != base.localAvatar.getDoId(): + self.stopSmooth() + elif mode == HouseGlobals.FURNITURE_MODE_OFF: + pass + else: + self.notify.warning('setMode: unknown mode: %s avId: %s' % (mode, avId)) + + def __getPosHpr(self): + if self.transmitRelativeTo == None: + pos = self.getPos() + hpr = self.getHpr() + else: + pos = self.getPos(self.transmitRelativeTo) + hpr = self.getHpr(self.transmitRelativeTo) + return (pos[0], pos[1], pos[2], hpr[0], 0, 0) + + def __comparePosHpr(self, a, b, threshold): + for i in xrange(len(a)): + if abs(a[i] - b[i]) >= threshold: + return 1 + + return 0 diff --git a/toontown/estate/DistributedFurnitureItemAI.py b/toontown/estate/DistributedFurnitureItemAI.py new file mode 100755 index 00000000..37d52e9c --- /dev/null +++ b/toontown/estate/DistributedFurnitureItemAI.py @@ -0,0 +1,68 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedSmoothNodeAI import DistributedSmoothNodeAI +from toontown.catalog import CatalogItem +import HouseGlobals + +class DistributedFurnitureItemAI(DistributedSmoothNodeAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedFurnitureItemAI") + + def __init__(self, air, furnitureMgr, catalogItem): + DistributedSmoothNodeAI.__init__(self, air) + + self.furnitureMgr = furnitureMgr + self.catalogItem = catalogItem + + self.mode = HouseGlobals.FURNITURE_MODE_OFF + self.modeAvId = 0 + + def announceGenerate(self): + x, y, z, h, p, r = self.catalogItem.posHpr + self.b_setPosHpr(x, y, z, h, 0, 0) + + def getItem(self): + return (self.furnitureMgr.doId, self.catalogItem.getBlob(CatalogItem.Customization)) + + def requestPosHpr(self, final, x, y, z, h, p, r, t): + senderId = self.air.getAvatarIdFromSender() + if (not self.furnitureMgr.director or senderId != self.furnitureMgr.director.doId): + self.air.writeServerEvent('suspicious', avId=senderId, issue='DistributedFurnitureItemAI.requestPosHpr Tried to move furniture without being the director!') + return + + self.catalogItem.posHpr = x, y, z, h, 0, 0 + + if not final and self.mode != HouseGlobals.FURNITURE_MODE_START: + self.b_setMode(HouseGlobals.FURNITURE_MODE_START, senderId) + elif final and self.mode == HouseGlobals.FURNITURE_MODE_START: + self.b_setMode(HouseGlobals.FURNITURE_MODE_STOP, 0) + return + + self.sendUpdate('setSmPosHpr', [x, y, z, h, 0, 0, t]) + + def setMode(self, mode, avId): + self.mode = mode + self.modeAvId = avId + + if mode == HouseGlobals.FURNITURE_MODE_STOP: + x, y, z, h, p, r = self.catalogItem.posHpr + self.b_setPosHpr(x, y, z, h, 0, 0) + + def d_setMode(self, mode, avId): + self.sendUpdate('setMode', [mode, avId]) + + def b_setMode(self, mode, avId): + self.setMode(mode, avId) + self.d_setMode(mode, avId) + + def getMode(self): + return self.mode, self.modeAvId + + def destroy(self): + # Presently, we just delete ourselves... No real shutdown needed. + self.requestDelete() + + def delete(self): + if hasattr(self, 'do_deleted'): + return + + self.do_deleted = True + DistributedSmoothNodeAI.delete(self) diff --git a/toontown/estate/DistributedFurnitureManager.py b/toontown/estate/DistributedFurnitureManager.py new file mode 100755 index 00000000..1540bd49 --- /dev/null +++ b/toontown/estate/DistributedFurnitureManager.py @@ -0,0 +1,166 @@ +from direct.distributed import DistributedObject +from toontown.catalog import CatalogItem +from toontown.catalog import CatalogItemList +from direct.directnotify.DirectNotifyGlobal import * + +class DistributedFurnitureManager(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedFurnitureManager') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.director = 0 + self.dfitems = [] + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.accept('releaseDirector', self.releaseDirector) + + def disable(self): + self.ignoreAll() + if self.cr.furnitureManager == self: + self.cr.furnitureManager = None + base.localAvatar.setFurnitureDirector(0, self) + self.director = 0 + self.notify.debug('disable') + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + self.notify.debug('delete') + DistributedObject.DistributedObject.delete(self) + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + if self.ownerId == base.localAvatar.doId: + self.cr.furnitureManager = self + if self.cr.objectManager == None: + import houseDesign + self.cr.objectManager = houseDesign.ObjectManager() + return + + def setOwnerName(self, name): + self.ownerName = name + + def setInteriorId(self, interiorId): + self.interiorId = interiorId + + def getInteriorObject(self): + return self.cr.doId2do.get(self.interiorId) + + def setAtticItems(self, items): + self.atticItems = CatalogItemList.CatalogItemList(items, store=CatalogItem.Customization) + + def setAtticWallpaper(self, items): + self.atticWallpaper = CatalogItemList.CatalogItemList(items, store=CatalogItem.Customization) + + def setAtticWindows(self, items): + self.atticWindows = CatalogItemList.CatalogItemList(items, store=CatalogItem.Customization) + + def setDeletedItems(self, items): + self.deletedItems = CatalogItemList.CatalogItemList(items, store=CatalogItem.Customization) + + def releaseDirector(self): + if self.director == base.localAvatar.doId: + self.d_suggestDirector(0) + self.setDirector(0) + + def d_suggestDirector(self, avId): + self.sendUpdate('suggestDirector', [avId]) + + def setDirector(self, avId): + self.notify.info('Furniture director is now %s' % avId) + base.localAvatar.setFurnitureDirector(avId, self) + self.director = avId + + def d_avatarEnter(self): + self.sendUpdate('avatarEnter', []) + + def d_avatarExit(self): + self.sendUpdate('avatarExit', []) + + def moveItemToAttic(self, dfitem, callback): + context = self.getCallbackContext(callback, [dfitem.item]) + self.sendUpdate('moveItemToAtticMessage', [dfitem.doId, context]) + + def moveItemFromAttic(self, index, posHpr, callback): + context = self.getCallbackContext(callback, [index]) + self.sendUpdate('moveItemFromAtticMessage', [index, posHpr[0], posHpr[1], posHpr[2], posHpr[3], posHpr[4], posHpr[5], context]) + + def deleteItemFromAttic(self, item, index, callback): + context = self.getCallbackContext(callback, [item, index]) + blob = item.getBlob(store=CatalogItem.Customization) + self.sendUpdate('deleteItemFromAtticMessage', [blob, index, context]) + + def deleteItemFromRoom(self, dfitem, callback): + context = self.getCallbackContext(callback, [dfitem.item]) + blob = dfitem.item.getBlob(store=CatalogItem.Customization) + self.sendUpdate('deleteItemFromRoomMessage', [blob, dfitem.doId, context]) + + def moveWallpaperFromAttic(self, index, room, callback): + context = self.getCallbackContext(callback, [index, room]) + self.sendUpdate('moveWallpaperFromAtticMessage', [index, room, context]) + + def deleteWallpaperFromAttic(self, item, index, callback): + context = self.getCallbackContext(callback, [item, index]) + blob = item.getBlob(store=CatalogItem.Customization) + self.sendUpdate('deleteWallpaperFromAtticMessage', [blob, index, context]) + + def moveWindowToAttic(self, slot, callback): + context = self.getCallbackContext(callback, [slot]) + self.sendUpdate('moveWindowToAtticMessage', [slot, context]) + + def moveWindowFromAttic(self, index, slot, callback): + context = self.getCallbackContext(callback, [index, slot]) + self.sendUpdate('moveWindowFromAtticMessage', [index, slot, context]) + + def moveWindow(self, fromSlot, toSlot, callback): + context = self.getCallbackContext(callback, [fromSlot, toSlot]) + self.sendUpdate('moveWindowMessage', [fromSlot, toSlot, context]) + + def deleteWindowFromAttic(self, item, index, callback): + context = self.getCallbackContext(callback, [item, index]) + blob = item.getBlob(store=CatalogItem.Customization) + self.sendUpdate('deleteWindowFromAtticMessage', [blob, index, context]) + + def recoverDeletedItem(self, item, index, callback): + context = self.getCallbackContext(callback, [item, index]) + blob = item.getBlob(store=CatalogItem.Customization) + self.sendUpdate('recoverDeletedItemMessage', [blob, index, context]) + + def moveItemToAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def moveItemFromAtticResponse(self, retcode, objectId, context): + if retcode >= 0: + dfitem = base.cr.doId2do[objectId] + else: + dfitem = None + self.doCallbackContext(context, [retcode, dfitem]) + return + + def deleteItemFromAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def deleteItemFromRoomResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def moveWallpaperFromAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def deleteWallpaperFromAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def moveWindowToAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def moveWindowFromAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def moveWindowResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def deleteWindowFromAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def recoverDeletedItemResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) diff --git a/toontown/estate/DistributedFurnitureManagerAI.py b/toontown/estate/DistributedFurnitureManagerAI.py new file mode 100755 index 00000000..149fa16a --- /dev/null +++ b/toontown/estate/DistributedFurnitureManagerAI.py @@ -0,0 +1,528 @@ +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.catalog.CatalogItemList import CatalogItemList +from toontown.catalog import CatalogItem +from toontown.catalog.CatalogFurnitureItem import CatalogFurnitureItem, FLTrunk, FLCloset, FLBank, FLPhone, FLCrate, FLChair, FLTV +from toontown.catalog.CatalogWallpaperItem import CatalogWallpaperItem +from toontown.catalog.CatalogMouldingItem import CatalogMouldingItem +from toontown.catalog.CatalogFlooringItem import CatalogFlooringItem +from toontown.catalog.CatalogWainscotingItem import CatalogWainscotingItem +from toontown.toonbase import ToontownGlobals +from DistributedFurnitureItemAI import DistributedFurnitureItemAI +from DistributedPhoneAI import DistributedPhoneAI +from DistributedClosetAI import DistributedClosetAI +from DistributedTrunkAI import DistributedTrunkAI +from DistributedBankAI import DistributedBankAI +from DistributedRewardCrateAI import DistributedRewardCrateAI +from DistributedChairAI import DistributedChairAI +from DistributedTVAI import DistributedTVAI +from otp.ai.MagicWordGlobal import * + +class FurnitureError(Exception): + def __init__(self, code): + Exception.__init__(self) + self.code = code + + +class DistributedFurnitureManagerAI(DistributedObjectAI): + notify = directNotify.newCategory("DistributedFurnitureManagerAI") + + def __init__(self, air, house, interior): + DistributedObjectAI.__init__(self, air) + + self.house = house + self.interior = interior + + self.director = None + + self.ownerId = house.avatarId + self.ownerName = house.name + + self.atticItems = None + self.atticWallpaper = None + self.wallpaper = None + self.atticWindows = None + self.windows = None + self.deletedItems = None + + self.items = [] + + # Initialize the above variables: + self.loadFromHouse() + + def announceGenerate(self): + DistributedObjectAI.announceGenerate(self) + + for item in self.items: + item.generateWithRequired(self.zoneId) + + def delete(self): + for item in self.items: + item.destroy() + + DistributedObjectAI.delete(self) + + def loadFromHouse(self): + self.b_setAtticItems(self.house.getAtticItems()) + self.b_setAtticWallpaper(self.house.getAtticWallpaper()) + self.b_setAtticWindows(self.house.getAtticWindows()) + self.b_setDeletedItems(self.house.getDeletedItems()) + + self.wallpaper = CatalogItemList(self.house.getInteriorWallpaper(), + store=CatalogItem.Customization) + self.applyWallpaper() + self.windows = CatalogItemList(self.house.getInteriorWindows(), + store=CatalogItem.Customization | + CatalogItem.WindowPlacement) + self.applyWindows() + + self.setItems(self.house.getInteriorItems()) + + def saveToHouse(self): + self.house.b_setAtticItems(self.getAtticItems()) + self.house.b_setAtticWallpaper(self.getAtticWallpaper()) + self.house.b_setAtticWindows(self.getAtticWindows()) + self.house.b_setDeletedItems(self.getDeletedItems()) + + self.house.b_setInteriorWallpaper(self.wallpaper.getBlob()) + self.house.b_setInteriorWindows(self.windows.getBlob()) + + self.house.b_setInteriorItems(self.getItems()) + + def applyWallpaper(self): + self.interior.b_setWallpaper(self.wallpaper.getBlob()) + + def applyWindows(self): + self.interior.b_setWindows(self.windows.getBlob()) + + def setItems(self, items): + items = CatalogItemList(items, store=CatalogItem.Customization|CatalogItem.Location) + + for item in self.items: + item.destroy() + + self.items = [] + + for item in items: + self.generateItem(item) + + def getItems(self): + items = CatalogItemList(store=CatalogItem.Customization|CatalogItem.Location) + + for item in self.items: + items.append(item.catalogItem) + + return items.getBlob() + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + + def d_setOwnerId(self, ownerId): + self.sendUpdate('setOwnerId', [ownerId]) + + def b_setOwnerId(self, ownerId): + self.setOwnerId(ownerId) + self.d_setOwnerId(ownerId) + + def getOwnerId(self): + return self.ownerId + + def setOwnerName(self, ownerName): + self.ownerName = ownerName + + def d_setOwnerName(self, ownerName): + self.sendUpdate('setOwnerName', [ownerName]) + + def b_setOwnerName(self, ownerName): + self.setOwnerName(ownerName) + self.d_setOwnerName(ownerName) + + def getOwnerName(self): + return self.ownerName + + def getInteriorId(self): + return self.interior.doId + + def setAtticItems(self, items): + self.atticItems = CatalogItemList(items, store=CatalogItem.Customization) + + def d_setAtticItems(self, items): + self.sendUpdate('setAtticItems', [items]) + + def b_setAtticItems(self, items): + self.setAtticItems(items) + if self.isGenerated(): + self.d_setAtticItems(items) + + def getAtticItems(self): + return self.atticItems.getBlob() + + def setAtticWallpaper(self, items): + self.atticWallpaper = CatalogItemList(items, store=CatalogItem.Customization) + + def d_setAtticWallpaper(self, items): + self.sendUpdate('setAtticWallpaper', [items]) + + def b_setAtticWallpaper(self, items): + self.setAtticWallpaper(items) + if self.isGenerated(): + self.d_setAtticWallpaper(items) + + def getAtticWallpaper(self): + return self.atticWallpaper.getBlob() + + def setAtticWindows(self, items): + self.atticWindows = CatalogItemList(items, store=CatalogItem.Customization) + + def d_setAtticWindows(self, items): + self.sendUpdate('setAtticWindows', [items]) + + def b_setAtticWindows(self, items): + self.setAtticWindows(items) + if self.isGenerated(): + self.d_setAtticWindows(items) + + def getAtticWindows(self): + return self.atticWindows.getBlob() + + def setDeletedItems(self, items): + self.deletedItems = CatalogItemList(items, store=CatalogItem.Customization) + + def d_setDeletedItems(self, items): + self.sendUpdate('setDeletedItems', [items]) + + def b_setDeletedItems(self, items): + self.setDeletedItems(items) + if self.isGenerated(): + self.d_setDeletedItems(items) + + def getDeletedItems(self): + return self.deletedItems.getBlob() + + def suggestDirector(self, directorId): + senderId = self.air.getAvatarIdFromSender() + + if self.ownerId != senderId: + self.air.writeServerEvent('suspicious', avId=senderId, issue='Tried to move furniture, but not the house owner!') + return + + if senderId != directorId and directorId != 0: + self.air.writeServerEvent('suspicious', avId=senderId, issue='Tried to make someone else (%d) move their furniture!' % directorId) + return + + director = self.air.doId2do.get(directorId) + if directorId and not director: + self.air.writeServerEvent('suspicious', avId=directorId, issue='Tried to move furniture without being on the shard!') + return + + if self.director: + self.director.b_setGhostMode(0) + if director: + if director.zoneId != self.zoneId: + self.air.writeServerEvent('suspicious', avId=directorId, issue='Tried to become director from another zone!') + return + director.b_setGhostMode(1) + + self.director = director + self.sendUpdate('setDirector', [directorId]) + + # Let's also save the furniture to the house (and thus to the DB) while + # we're at it... + self.saveToHouse() + + def avatarEnter(self): + pass + + def avatarExit(self): + pass + + # Furniture-manipulation: + + def generateItem(self, item): + if item.getFlags() & FLTrunk: + do = DistributedTrunkAI(self.air, self, item) + elif item.getFlags() & FLCloset: + do = DistributedClosetAI(self.air, self, item) + elif item.getFlags() & FLBank: + do = DistributedBankAI(self.air, self, item) + elif item.getFlags() & FLPhone: + do = DistributedPhoneAI(self.air, self, item) + elif item.getFlags() & FLCrate: + do = DistributedRewardCrateAI(self.air, self, item) + elif item.getFlags() & FLChair: + do = DistributedChairAI(self.air, self, item) + elif item.getFlags() & FLTV: + do = DistributedTVAI(self.air, self, item) + else: + do = DistributedFurnitureItemAI(self.air, self, item) + + if self.isGenerated(): + do.generateWithRequired(self.zoneId) + + self.items.append(do) + return do + + def moveItemToAttic(self, doId): + item = self.getItemObject(doId) + + self.atticItems.append(item.catalogItem) + self.d_setAtticItems(self.getAtticItems()) + + item.destroy() + self.items.remove(item) + + return ToontownGlobals.FM_MovedItem + + def moveItemFromAttic(self, index, x, y, z, h, p, r): + item = self.getAtticFurniture(self.atticItems, index) + + self.atticItems.remove(item) + self.d_setAtticItems(self.getAtticItems()) + + item.posHpr = (x, y, z, h, p, r) + object = self.generateItem(item) + + return (ToontownGlobals.FM_MovedItem, object.doId) + + def deleteItemFromAttic(self, blob, index): + item = self.getAtticFurniture(self.atticItems, index) + if item is None: + self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to delete an invalid item at index %s' % index) + return ToontownGlobals.FM_InvalidIndex + + self.atticItems.remove(item) + self.d_setAtticItems(self.getAtticItems()) + + return ToontownGlobals.FM_DeletedItem + + def deleteItemFromRoom(self, doId, addToTrash=True): + item = self.getItemObject(doId) + + if not item: + self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to delete an invalid item with doId %s' % doId) + return ToontownGlobals.FM_InvalidIndex + + if addToTrash: + self.deletedItems.append(item.catalogItem) + self.d_setDeletedItems(self.getDeletedItems()) + + item.destroy() + self.items.remove(item) + + return ToontownGlobals.FM_DeletedItem + + def moveWallpaperFromAttic(self, index, room): + retcode = ToontownGlobals.FM_SwappedItem + wallpaper = self.getAtticFurniture(self.atticWallpaper, index) + if wallpaper is None: + self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Invalid wallpaper at index %s' % index) + return ToontownGlobals.FM_InvalidIndex + + if room > 3: + self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to apply a wallpaper in an invalid room %d!' % room) + return ToontownGlobals.FM_InvalidItem + interiorIndex = room*4 + if isinstance(wallpaper, CatalogMouldingItem): + interiorIndex += 1 + elif isinstance(wallpaper, CatalogFlooringItem): + interiorIndex += 2 + elif isinstance(wallpaper, CatalogWainscotingItem): + interiorIndex += 3 + atticIndex = self.atticWallpaper.index(wallpaper) + self.atticWallpaper[atticIndex] = self.wallpaper[interiorIndex] + self.d_setAtticWallpaper(self.getAtticWallpaper()) + self.wallpaper[interiorIndex] = wallpaper + self.applyWallpaper() + + return retcode + + def deleteWallpaperFromAttic(self, blob, index): + wallpaper = self.getAtticFurniture(blob, index) + if wallpaper in self.atticWallpaper: + self.atticWallpaper.remove(wallpaper) + self.b_setAtticWallpaper(self.getAtticWallpaper()) + + def moveWindowToAttic(self, slot): + window = self.getWindow(slot) + if window is None: + return ToontownGlobals.FM_InvalidIndex + self.windows.remove(window) + self.applyWindows() + self.atticWindows.append(window) + self.d_setAtticWindows(self.getAtticWindows()) + return ToontownGlobals.FM_MovedItem + + def moveWindowFromAttic(self, index, slot): + retcode = ToontownGlobals.FM_MovedItem + window = self.getAtticFurniture(self.atticWindows, index) + if slot > 7: + self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), + issue='Tried to move window to invalid slot %d!' % slot) + return ToontownGlobals.FM_HouseFull + if self.getWindow(slot): + self.moveWindowToAttic(slot) + retcode = ToontownGlobals.FM_SwappedItem + self.atticWindows.remove(window) + self.d_setAtticWindows(self.getAtticWindows()) + window.placement = slot + self.windows.append(window) + self.applyWindows() + return retcode + + def moveWindow(self, fromSlot, toSlot): + retcode = ToontownGlobals.FM_MovedItem + window = self.getWindow(fromSlot) + if window is None: + return ToontownGlobals.FM_InvalidIndex + if toSlot > 7: + self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), + issue='DistributedfTried to move window to invalid slot %d!' % toSlot) + return ToontownGlobals.FM_HouseFull + if self.getWindow(toSlot): + self.moveWindowToAttic(toSlot) + retcode = ToontownGlobals.FM_SwappedItem + window.placement = toSlot + self.applyWindows() + return retcode + + def deleteWindowFromAttic(self, blob, index): + window = self.getAtticFurniture(self.atticWindows, index) + if window is None: + self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to delete an invalid window at index %s' % index) + return ToontownGlobals.FM_InvalidIndex + self.atticWindows.remove(window) + self.d_setAtticWindows(self.getAtticWindows()) + return ToontownGlobals.FM_DeletedItem + + def recoverDeletedItem(self, blob, index): + if len(self.deletedItems) <= index: + return + + item = self.deletedItems[index] + self.deletedItems.remove(item) + self.atticItems.append(item) + self.d_setDeletedItems(self.deletedItems) + self.d_setAtticItems(self.getAtticItems()) + + return ToontownGlobals.FM_MovedItem + + def handleMessage(self, func, response, *args): + context = args[-1] + args = args[:-1] + senderId = self.air.getAvatarIdFromSender() + if not self.director or senderId != self.director.doId: + self.air.writeServerEvent('suspicious', avId=senderId, + issue='Sent furniture management request without' + ' being the director.') + retval = ToontownGlobals.FM_NotDirector + else: + try: + retval = func(*args) or 0 + except FurnitureError as e: + retval = e.code + if response == 'moveItemFromAtticResponse': + if type(retval) == tuple: + retval, doId = retval + else: + doId = 0 + taskMgr.doMethodLater(1, self.sendUpdateToAvatarId, + self.uniqueName('send-attic-response'), + extraArgs=[senderId, response, [retval, doId, context]]) + else: + self.sendUpdateToAvatarId(senderId, response, [retval, context]) + + def moveItemToAtticMessage(self, doId, context): + self.handleMessage(self.moveItemToAttic, 'moveItemToAtticResponse', doId, context) + + def moveItemFromAtticMessage(self, index, x, y, z, h, p, r, context): + self.handleMessage(self.moveItemFromAttic, 'moveItemFromAtticResponse', index, x, y, z, h, p, r, context) + + def deleteItemFromAtticMessage(self, blob, index, context): + self.handleMessage(self.deleteItemFromAttic, 'deleteItemFromAtticResponse', blob, index, context) + + def deleteItemFromRoomMessage(self, blob, doId, context): + self.handleMessage(self.deleteItemFromRoom, 'deleteItemFromRoomResponse', doId, context) + + def moveWallpaperFromAtticMessage(self, index, room, context): + self.handleMessage(self.moveWallpaperFromAttic, 'moveWallpaperFromAtticResponse', index, room, context) + + def deleteWallpaperFromAtticMessage(self, blob, index, context): + self.handleMessage(self.deleteWallpaperFromAttic, 'deleteWallpaperFromAtticResponse', blob, index, context) + + def moveWindowToAtticMessage(self, slot, context): + self.handleMessage(self.moveWindowToAttic, 'moveWindowToAtticResponse', slot, context) + + def moveWindowFromAtticMessage(self, index, slot, context): + self.handleMessage(self.moveWindowFromAttic, 'moveWindowFromAtticResponse', index, slot, context) + + def moveWindowMessage(self, fromSlot, toSlot, context): + self.handleMessage(self.moveWindow, 'moveWindowResponse', fromSlot, toSlot, context) + + def deleteWindowFromAtticMessage(self, blob, index, context): + self.handleMessage(self.deleteWindowFromAttic, 'deleteWindowFromAtticResponse', blob, index, context) + + def recoverDeletedItemMessage(self, blob, index, context): + self.handleMessage(self.recoverDeletedItem, 'recoverDeletedItemResponse', blob, index, context) + + def getItemObject(self, doId): + item = self.air.doId2do.get(doId) + if item is None: + raise FurnitureError(ToontownGlobals.FM_InvalidItem) + if item not in self.items: + raise FurnitureError(ToontownGlobals.FM_InvalidItem) + return item + + def getAtticFurniture(self, attic, index): + if index >= len(attic): + raise FurnitureError(ToontownGlobals.FM_InvalidIndex) + return attic[index] + + def getWindow(self, slot): + for window in self.windows: + if window.placement == slot: + return window + return None + +@magicWord(category=CATEGORY_PROGRAMMER) +def fillAttic(): + """ + Move everything to the attic. + """ + target = spellbook.getTarget() + + if not hasattr(target, "estate") or not hasattr(target.estate, "houses"): + return "The target is not in an estate!" + + for house in target.estate.houses: + if house.doId == target.houseId: + manager = house.interior.furnitureManager + + for item in reversed(manager.items): + manager.moveItemToAttic(item.doId) + + manager.saveToHouse() + return "Everything has been moved to the attic!" + + return "The target is not in his estate!" + +@magicWord(category=CATEGORY_PROGRAMMER) +def emptyHouse(): + """ + Delete everything in the house. + """ + target = spellbook.getTarget() + + if not hasattr(target, "estate") or not hasattr(target.estate, "houses"): + return "The target is not in an estate!" + + for house in target.estate.houses: + if house.doId == target.houseId: + manager = house.interior.furnitureManager + + for item in reversed(manager.items): + item.destroy() + + manager.items = [] + manager.saveToHouse() + return "Everything has been deleted!" + + return "The target is not in his estate!" \ No newline at end of file diff --git a/toontown/estate/DistributedGagTree.py b/toontown/estate/DistributedGagTree.py new file mode 100755 index 00000000..0a09e61e --- /dev/null +++ b/toontown/estate/DistributedGagTree.py @@ -0,0 +1,407 @@ +from toontown.estate import DistributedPlantBase +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import PythonUtil +from toontown.toonbase import ToontownBattleGlobals +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +import GardenGlobals +import HouseGlobals +from direct.task import Task +from panda3d.core import * +from otp.otpbase import OTPGlobals +from toontown.estate import DistributedLawnDecor +DIRT_AS_WATER_INDICATOR = True + +class DistributedGagTree(DistributedPlantBase.DistributedPlantBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGagTree') + + def __init__(self, cr): + DistributedPlantBase.DistributedPlantBase.__init__(self, cr) + base.tree = self + self.collSphereRadius = 4.2 + self.confirmDialog = None + self.resultDialog = None + self.dirtMound = None + self.sandMound = None + self.needToPlant = 0 + self.needToLoad = 0 + self.backupFruits = [] + self.signHasBeenStuck2Ground = False + self.setName('DistributedGagTree') + self.fruiting = 0 + + def delete(self): + DistributedPlantBase.DistributedPlantBase.delete(self) + del self.prop + del self.prop2 + del self.dirtMound + del self.sandMound + self.signModel.removeNode() + self.signModel = None + return + + def setTypeIndex(self, typeIndex): + DistributedPlantBase.DistributedPlantBase.setTypeIndex(self, typeIndex) + track, level = GardenGlobals.getTreeTrackAndLevel(typeIndex) + self.gagTrack = track + self.gagLevel = level + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + propName = ToontownBattleGlobals.AvPropsNew[track][level] + self.prop = invModel.find('**/' + propName) + self.prop.setScale(7) + invModel.removeNode() + invModel2 = loader.loadModel('phase_3.5/models/gui/inventory_icons') + propName = ToontownBattleGlobals.AvPropsNew[track][level] + self.prop2 = invModel2.find('**/' + propName) + self.prop2.setScale(7) + self.filename = self.attributes['filename'] + self.maxFruit = self.attributes['maxFruit'] + if hasattr(self, 'needToLoad'): + if self.needToLoad: + self.loadModel() + + def loadModel(self): + if not hasattr(self, 'filename'): + self.needToLoad = 1 + return + if not self.rotateNode: + self.rotateNode = self.plantPath.attachNewNode('rotate') + all = loader.loadModel(self.filename) + self.modelName = self.getModelName() + if self.isWilted(): + self.modelName += '_wilt' + self.model = all.find('**/' + self.modelName) + all.detachNode() + shadow = self.model.find('**/shadow1') + if shadow: + shadow.hide() + self.model.reparentTo(self.rotateNode) + if self.isFruiting() and not self.isWilted(): + self.fruits = [] + for i in xrange(1, self.maxFruit + 1): + pos = self.model.find('**/locator' + str(i)) + if pos and not pos.isEmpty(): + fruit = self.prop.copyTo(self.model) + fruit.setPos(pos, 0, 0, 0) + fruit.setScale(13) + self.fruits.append(fruit) + + self.createBackupFruits() + if DIRT_AS_WATER_INDICATOR: + self.dirtMound = loader.loadModel('phase_5.5/models/estate/dirt_mound') + self.dirtMound.reparentTo(self.model) + self.sandMound = loader.loadModel('phase_5.5/models/estate/sand_mound') + self.sandMound.reparentTo(self.model) + self.adjustGrowth() + self.signModel = loader.loadModel('phase_5.5/models/estate/garden_sign.bam') + self.signModel.setPos(3.5, 0, 0.025) + self.signModel.reparentTo(self.rotateNode) + owner = self.getOwnerIndex() + color = HouseGlobals.houseColors[owner] + for geomName in ('sign', 'sign1'): + sign = self.signModel.find('**/' + geomName) + if sign: + sign.setColor(*color) + + self.prop.setPos(0.1, -0.17, 1.63) + self.prop.reparentTo(self.signModel) + self.prop2.setPos(0.15, 0.17, 1.63) + self.prop2.setH(self.prop.getH() + 180) + self.prop2.reparentTo(self.signModel) + self.needToLoad = 0 + if self.needToPlant: + self.stickParts() + + def setupShadow(self): + DistributedPlantBase.DistributedPlantBase.setupShadow(self) + self.adjustGrowth() + + def makeMovieNode(self): + self.movieNode = self.rotateNode.attachNewNode('moviePos') + self.movieNode.setPos(0, -5, 0) + self.createBackupFruits() + + def handlePicking(self): + messenger.send('wakeup') + if self.isFruiting() and self.canBeHarvested(): + self.startInteraction() + self.doHarvesting() + return + fullName = self.name + text = TTLocalizer.ConfirmRemoveTree % {'tree': fullName} + if self.hasDependentTrees(): + text += TTLocalizer.ConfirmWontBeAbleToHarvest + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=text, command=self.confirmCallback) + self.confirmDialog.show() + self.startInteraction() + return + + def confirmCallback(self, value): + self.confirmDialog.destroy() + self.confirmDialog = None + if value > 0: + self.doPicking() + else: + self.finishInteraction() + return + + def doPicking(self): + if not self.canBePicked(): + return + self.sendUpdate('removeItem', []) + + def createBackupFruits(self): + if not hasattr(self, 'fruits'): + return + if not self.fruits: + return + if not hasattr(self, 'movieNode'): + return + if not self.movieNode: + return + if self.movieNode.isEmpty(): + return + if not self.signHasBeenStuck2Ground: + return + if not self.backupFruits: + for fruit in self.fruits: + newFruit = fruit.copyTo(render) + newFruit.setPos(fruit.getPos(render)) + newFruit.setH(self.movieNode.getH(render)) + newFruit.hide() + self.backupFruits.append(newFruit) + + def clearBackupFruits(self): + if self.fruits: + for fruit in self.fruits: + fruit.removeNode() + + self.fruits = None + self.backupFruits = [] + + def doHarvesting(self): + if not self.canBePicked(): + return + if hasattr(self, 'backupFruits'): + for fruit in self.backupFruits: + fruit.show() + + self.sendUpdate('requestHarvest', []) + + def getTrack(self): + return self.gagTrack + + def getGagLevel(self): + return self.gagLevel + + def setWaterLevel(self, waterLevel): + self.waterLevel = waterLevel + self.adjustWaterIndicator() + + def setGrowthLevel(self, growthLevel): + self.growthLevel = growthLevel + if self.model: + newModelName = self.getModelName() + if True: + self.model.removeNode() + self.loadModel() + self.adjustWaterIndicator() + self.stick2Ground() + else: + self.adjustGrowth() + + def adjustGrowth(self): + newScale = self.growthLevel + 1 + if newScale > 1: + newScale = 1 + shadowScale = 2.5 + collScale = 1.5 + if self.isSeedling(): + shadowScale = 1 + collScale = 1 + if self.shadowJoint: + self.shadowJoint.setScale(shadowScale) + if DIRT_AS_WATER_INDICATOR: + dirtMoundScale = shadowScale * 1.5 + dirtMoundDepth = 2.0 + if self.isEstablished(): + dirtMoundScale = shadowScale * 1.2 + self.dirtMound.setScale(dirtMoundScale, dirtMoundScale, dirtMoundDepth) + self.sandMound.setScale(dirtMoundScale, dirtMoundScale, dirtMoundDepth) + self.adjustWaterIndicator() + + def setWilted(self, wilted): + self.wilted = wilted + + def isWilted(self): + return self.wilted + + def setMovie(self, mode, avId): + if mode == GardenGlobals.MOVIE_HARVEST: + self.doHarvestTrack(avId) + elif mode == GardenGlobals.MOVIE_WATER: + self.doWaterTrack(avId) + elif mode == GardenGlobals.MOVIE_FINISHPLANTING: + self.doFinishPlantingTrack(avId) + elif mode == GardenGlobals.MOVIE_REMOVE: + self.doDigupTrack(avId) + + def doFinishPlantingTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + self.movie = Sequence() + if self.model: + self.model.setTransparency(1) + self.model.setAlphaScale(0) + self.movie.append(LerpFunc(self.model.setAlphaScale, fromData=0, toData=1, duration=3)) + if self.signModel: + self.signModel.hide() + self.movie.append(Func(self.signModel.show)) + self.movie.append(LerpScaleInterval(self.signModel, 1, 1, 0)) + self.movie.append(Func(toon.loop, 'neutral')) + if avId == localAvatar.doId: + self.movie.append(Func(self.finishInteraction)) + self.movie.append(Func(self.movieDone)) + self.movie.append(Func(self.doResultDialog)) + self.movie.start() + + def doHarvestTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + moveTrack = self.generateToonMoveTrack(toon) + harvestTrack = self.generateHarvestTrack(toon) + self.movie = Sequence(self.startCamIval(avId), moveTrack, harvestTrack, self.stopCamIval(avId)) + if avId == localAvatar.doId: + self.movie.append(Func(self.finishInteraction)) + self.movie.append(Func(self.movieDone)) + self.movie.start() + + def setupShadow(self): + if DIRT_AS_WATER_INDICATOR: + pass + else: + DistributedPlantBase.DistributedPlantBase.setupShadow(self) + + def generateHarvestTrack(self, toon): + pos = toon.getPos(render) + pos.setZ(pos.getZ() + 2) + fruitTrack = Parallel() + for fruit in self.backupFruits: + fruitTrack.append(Sequence(Func(fruit.show), LerpPosInterval(fruit, 1.5, pos, startPos=Point3(fruit.getX(), fruit.getY(), fruit.getZ() + self.model.getZ())), Func(fruit.removeNode))) + + harvestTrack = Sequence(fruitTrack, Func(self.clearBackupFruits)) + return harvestTrack + + def adjustWaterIndicator(self): + DistributedPlantBase.DistributedPlantBase.adjustWaterIndicator(self) + if self.dirtMound: + curWaterLevel = self.waterLevel + if curWaterLevel > self.maxWaterLevel: + curWaterLevel = self.maxWaterLevel + if curWaterLevel > 0: + darkestColorScale = 0.4 + lightestColorScale = 1.0 + scaleRange = lightestColorScale - darkestColorScale + scaleIncrement = scaleRange / self.maxWaterLevel + darker = lightestColorScale - scaleIncrement * curWaterLevel + self.dirtMound.setColorScale(darker, darker, darker, 1.0) + self.sandMound.hide() + self.dirtMound.show() + else: + self.sandMound.show() + self.dirtMound.hide() + + def stickParts(self): + if not hasattr(self, 'signModel'): + self.needToPlant = 1 + return Task.done + if self.signModel.isEmpty(): + return Task.done + testPath = NodePath('testPath') + testPath.reparentTo(render) + cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0) + cRayNode = CollisionNode(self.uniqueName('estate-FloorRay')) + cRayNode.addSolid(cRay) + cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + cRayNode.setIntoCollideMask(BitMask32.allOff()) + cRayNodePath = testPath.attachNewNode(cRayNode) + queue = CollisionHandlerQueue() + picker = CollisionTraverser() + picker.addCollider(cRayNodePath, queue) + testPath.setPos(self.signModel.getX(render), self.signModel.getY(render), 0) + picker.traverse(render) + if queue.getNumEntries() > 0: + queue.sortEntries() + for index in xrange(queue.getNumEntries()): + entry = queue.getEntry(index) + if DistributedLawnDecor.recurseParent(entry.getIntoNode(), 'terrain_DNARoot'): + self.signModel.wrtReparentTo(render) + self.signModel.setZ(entry.getSurfacePoint(render)[2] + self.stickUp + 0.1) + self.signModel.wrtReparentTo(self.rotateNode) + self.signHasBeenStuck2Ground = True + self.createBackupFruits() + return Task.done + + return Task.done + + def canBeHarvested(self): + return self.isFruiting() + + def hasDependentTrees(self): + myTrack, myLevel = GardenGlobals.getTreeTrackAndLevel(self.typeIndex) + allGagTrees = base.cr.doFindAll('DistributedGagTree') + for gagTree in allGagTrees: + if gagTree.getOwnerId() == localAvatar.doId: + curTrack, curLevel = GardenGlobals.getTreeTrackAndLevel(gagTree.typeIndex) + if curTrack == myTrack: + if myLevel < curLevel: + return True + + return False + + def doResultDialog(self): + self.startInteraction() + curTrack, curLevel = GardenGlobals.getTreeTrackAndLevel(self.typeIndex) + species = GardenGlobals.getTreeTypeIndex(curTrack, curLevel) + treeName = GardenGlobals.PlantAttributes[species]['name'] + stringToShow = TTLocalizer.getResultPlantedSomethingSentence(treeName) + self.resultDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=stringToShow, command=self.resultsCallback) + + def resultsCallback(self, value): + if self.resultDialog: + self.resultDialog.destroy() + self.resultDialog = None + self.finishInteraction() + return + + def allowedToPick(self): + return True + + def unlockPick(self): + retval = True + toon = base.localAvatar + inventory = toon.inventory + load = inventory.totalProps + maxCarry = toon.getMaxCarry() + if load >= maxCarry and not self.gagLevel > ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL: + retval = False + if inventory.numItem(self.gagTrack, self.gagLevel) >= inventory.getMax(self.gagTrack, self.gagLevel): + retval = False + return retval + + def setFruiting(self, fruiting): + self.fruiting = fruiting + if self.model: + self.model.removeNode() + self.loadModel() + self.adjustWaterIndicator() + self.stick2Ground() + + def isFruiting(self): + return self.fruiting + \ No newline at end of file diff --git a/toontown/estate/DistributedGagTreeAI.py b/toontown/estate/DistributedGagTreeAI.py new file mode 100755 index 00000000..285eb3cc --- /dev/null +++ b/toontown/estate/DistributedGagTreeAI.py @@ -0,0 +1,223 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.ai.MagicWordGlobal import * +from toontown.estate.DistributedPlantBaseAI import DistributedPlantBaseAI +import GardenGlobals, time + +ONE_DAY = 86400 + +PROBLEM_WILTED = 1 +PROBLEM_NOT_GROWN = 2 +PROBLEM_HARVESTED_LATELY = 4 + +class DistributedGagTreeAI(DistributedPlantBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGagTreeAI') + GrowRate = config.GetBool('trees-grow-rate', 2) + + def __init__(self, mgr): + DistributedPlantBaseAI.__init__(self, mgr) + self.wilted = 0 + + def announceGenerate(self): + DistributedPlantBaseAI.announceGenerate(self) + messenger.send(self.getEventName('generate')) + + def setWilted(self, wilted): + self.wilted = wilted + + def d_setWilted(self, wilted): + self.sendUpdate('setWilted', [wilted]) + + def b_setWilted(self, wilted): + self.setWilted(wilted) + self.d_setWilted(wilted) + + def getWilted(self): + return self.wilted + + def calculate(self, lastHarvested, lastCheck): + now = int(time.time()) + if lastCheck == 0: + lastCheck = now + + grown = 0 + + # Water level + elapsed = now - lastCheck + while elapsed > ONE_DAY: + if self.waterLevel >= 0: + grown += self.GrowRate + + elapsed -= ONE_DAY + self.waterLevel -= 1 + + self.waterLevel = max(self.waterLevel, -2) + + # Growth level + maxGrowth = self.growthThresholds[2] + newGL = min(self.growthLevel + grown, maxGrowth) + self.setGrowthLevel(newGL) + + self.setWilted(self.waterLevel == -2) + + self.lastCheck = now - elapsed + self.lastHarvested = lastHarvested + self.update() + + def calcDependencies(self): + if self.getWilted(): + return + + missingPrevIndex = 0 + track, value = GardenGlobals.getTreeTrackAndLevel(self.typeIndex) + while value: + value -= 1 + if not self.mgr.hasTree(track, value): + self.b_setWilted(1) + continue + + tree = self.mgr.getTree(track, value) + if not tree: + self.b_setWilted(1) + continue + + self.accept(self.getEventName('going-down', 666), self.ignoreAll) + self.accept(self.getEventName('remove', track * 7 + value), self.calcDependencies) + + def getEventName(self, string, typeIndex=None): + typeIndex = typeIndex if typeIndex is not None else self.typeIndex + return 'garden-%d-%d-%s' % (self.ownerDoId, typeIndex, string) + + def delete(self): + messenger.send(self.getEventName('remove')) + self.ignoreAll() + DistributedPlantBaseAI.delete(self) + + def update(self): + mdata = map(list, self.mgr.data['trees']) + mdata[self.treeIndex] = [self.typeIndex, self.waterLevel, self.lastCheck, self.getGrowthLevel(), self.lastHarvested] + self.mgr.data['trees'] = mdata + self.mgr.update() + + def isFruiting(self): + problem = 0 + if self.getWilted(): + problem |= PROBLEM_WILTED + + if self.getGrowthLevel() < self.growthThresholds[2]: + problem |= PROBLEM_NOT_GROWN + + if (self.lastCheck - self.lastHarvested) < ONE_DAY: + problem |= PROBLEM_HARVESTED_LATELY + + return problem + + def getFruiting(self): + return self.isFruiting() == 0 + + def requestHarvest(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if not av: + return + + if avId != self.ownerDoId: + self.air.writeServerEvent('suspicious', avId, 'tried to harvest someone else\'s tree!') + return + + problem = self.isFruiting() + if problem: + self.air.writeServerEvent('suspicious', avId, 'tried to harvest a tree that\'s not fruiting!', problem=problem) + return + + harvested = 0 + track, level = GardenGlobals.getTreeTrackAndLevel(self.typeIndex) + while av.inventory.addItem(track, level) > 0 and harvested < 10: + harvested += 1 + + av.d_setInventory(av.getInventory()) + + self.lastHarvested = int(time.time()) + self.sendUpdate('setFruiting', [self.getFruiting()]) + self.d_setMovie(GardenGlobals.MOVIE_HARVEST) + self.update() + + def removeItem(self): + avId = self.air.getAvatarIdFromSender() + self.d_setMovie(GardenGlobals.MOVIE_REMOVE) + + def _remove(task): + if not self.air: + return + + plot = self.mgr.placePlot(self.treeIndex) + plot.setPlot(self.plot) + plot.setPos(self.getPos()) + plot.setH(self.getH()) + plot.setOwnerIndex(self.ownerIndex) + plot.generateWithRequired(self.zoneId) + + self.air.writeServerEvent('remove-tree', avId, plot=self.plot) + self.requestDelete() + + self.mgr.trees.remove(self) + + mdata = map(list, self.mgr.data['trees']) + mdata[self.treeIndex] = self.mgr.getNullPlant() + self.mgr.data['trees'] = mdata + self.mgr.update() + + self.mgr.reconsiderAvatarOrganicBonus() + + return task.done + + taskMgr.doMethodLater(7, _remove, self.uniqueName('do-remove')) + + def doGrow(self, grown): + maxGrowth = self.growthThresholds[2] + newGL = max(0, min(self.growthLevel + grown, maxGrowth)) + oldGrowthLevel = self.growthLevel + + self.b_setGrowthLevel(newGL) + self.update() + + return newGL - oldGrowthLevel + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int, int, int]) +def satanGrow(track, index, grown=21): + av = spellbook.getTarget() + estate = av.air.estateManager._lookupEstate(av) + + if not estate: + return 'Estate not found!' + + garden = estate.gardenManager.gardens.get(av.doId) + if not garden: + return 'Garden not found!' + + tree = garden.getTree(track, index) + if not tree: + return 'Tree not found!' + + result = tree.doGrow(grown) + return 'Satan has grown %d units!' % result + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int, int]) +def satanFruit(track, index): + av = spellbook.getTarget() + estate = av.air.estateManager._lookupEstate(av) + + if not estate: + return 'Estate not found!' + + garden = estate.gardenManager.gardens.get(av.doId) + if not garden: + return 'Garden not found!' + + tree = garden.getTree(track, index) + if not tree: + return 'Tree not found!' + + tree.calculate(0, tree.lastCheck) + tree.sendUpdate('setFruiting', [tree.getFruiting()]) + return 'Satan is now fruiting!' + \ No newline at end of file diff --git a/toontown/estate/DistributedGarden.py b/toontown/estate/DistributedGarden.py new file mode 100755 index 00000000..8a559e34 --- /dev/null +++ b/toontown/estate/DistributedGarden.py @@ -0,0 +1,115 @@ +from panda3d.core import * +from direct.distributed import DistributedObject +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from toontown.toonbase.ToonBaseGlobal import * +import HouseGlobals + +class DistributedGarden(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedGarden') + + def __init__(self, cr): + self.notify.debug('init') + DistributedObject.DistributedObject.__init__(self, cr) + self.props = [] + self.pos = None + self.radius = 0 + self.gridCells = 20 + self.propTable = [None] * self.gridCells + for i in xrange(len(self.propTable)): + self.propTable[i] = [None] * self.gridCells + + self.dx = 1.0 / self.gridCells + self.occupied = [] + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + + def disable(self): + DistributedObject.DistributedObject.disable(self) + + def unload(self): + pass + + def delete(self): + for prop in self.props: + prop[0].removeNode() + del prop[0] + del prop + + del self.props + self.props = None + self.unload() + return + + def sendNewProp(self, prop, x, y, z): + self.notify.debug('sendNewProp') + print 'new prop (%d) = %s,%s,%s' % (prop, + x, + y, + z) + if prop == HouseGlobals.PROP_ICECUBE: + model = loader.loadModel('phase_8/models/props/icecube.bam') + elif prop == HouseGlobals.PROP_FLOWER: + model = loader.loadModel('phase_8/models/props/flower_treasure.bam') + elif prop == HouseGlobals.PROP_SNOWFLAKE: + model = loader.loadModel('phase_8/models/props/snowflake_treasure.bam') + model.reparentTo(hidden) + model.setPos(x, y, z) + model.setScale(0.2) + model.setBillboardPointEye() + model.reparentTo(render) + self.props.append([model, + x, + y, + z]) + + def getPropPos(self, i, j): + pos = [self.pos[0] - self.radius + 2 * self.radius * i, self.pos[1] - self.radius + 2 * self.radius * j, self.pos[2]] + return pos + + def loadProp(self, prop, i, j): + pos = self.getPropPos(i, j) + if prop == HouseGlobals.PROP_ICECUBE: + model = loader.loadModel('phase_8/models/props/icecube.bam') + elif prop == HouseGlobals.PROP_FLOWER: + model = loader.loadModel('phase_8/models/props/flower_treasure.bam') + elif prop == HouseGlobals.PROP_SNOWFLAKE: + model = loader.loadModel('phase_8/models/props/snowflake_treasure.bam') + else: + self.notify.error('cant find prop: %s' % prop) + model.reparentTo(hidden) + model.setPos(pos[0], pos[1], pos[2]) + model.setScale(0.2) + model.setBillboardPointEye() + model.reparentTo(render) + + def setAddProp(self, prop, i, j): + self.notify.debug('addProp') + self.props.append([prop, i, j]) + self.loadProp(prop, i, j) + self.b_setProps(self, props) + + def b_setProps(self, props): + self.notify.debug('b_setProps') + self.setProps(props) + self.d_setProps(props) + + def d_setProps(self, props): + self.notify.debug('d_setProps') + aProps = [] + for prop in props: + aProps = aProps + prop + + self.sendUpdate('setProps', [aProps]) + + def setProps(self, props): + self.notify.debug('setProps') + self.props = props + for prop in self.props: + pInd, i, j = prop + self.propTable[i, j] = pInd diff --git a/toontown/estate/DistributedGardenAI.py b/toontown/estate/DistributedGardenAI.py new file mode 100755 index 00000000..34378ce2 --- /dev/null +++ b/toontown/estate/DistributedGardenAI.py @@ -0,0 +1,9 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI + +class DistributedGardenAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedGardenAI") + + def sendNewProp(self, todo0, todo1, todo2, todo3): + pass + diff --git a/toontown/estate/DistributedGardenBox.py b/toontown/estate/DistributedGardenBox.py new file mode 100755 index 00000000..48edda74 --- /dev/null +++ b/toontown/estate/DistributedGardenBox.py @@ -0,0 +1,53 @@ +import DistributedLawnDecor +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.ShowBase import * +import GardenGlobals +from toontown.toonbase import TTLocalizer +from toontown.estate import PlantingGUI +from toontown.estate import PlantTreeGUI +from direct.distributed import DistributedNode +from pandac.PandaModules import NodePath +from pandac.PandaModules import Vec3 + +class DistributedGardenBox(DistributedLawnDecor.DistributedLawnDecor): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGardenPlot') + + def __init__(self, cr): + DistributedLawnDecor.DistributedLawnDecor.__init__(self, cr) + self.plantPath = NodePath('plantPath') + self.plantPath.reparentTo(self) + self.plotScale = 1.0 + self.plantingGuiDoneEvent = 'plantingGuiDone' + self.defaultModel = 'phase_5.5/models/estate/planterC' + + def doModelSetup(self): + if self.typeIndex == GardenGlobals.BOX_THREE: + self.defaultModel = 'phase_5.5/models/estate/planterA' + elif self.typeIndex == GardenGlobals.BOX_TWO: + self.defaultModel = 'phase_5.5/models/estate/planterC' + else: + self.defaultModel = 'phase_5.5/models/estate/planterD' + self.collSphereOffset = 0.0 + self.collSphereRadius = self.collSphereRadius * 1.41 + self.plotScale = Vec3(1.0, 1.0, 1.0) + + def setupShadow(self): + pass + + def loadModel(self): + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = None + self.model = loader.loadModel(self.defaultModel) + self.model.setScale(self.plotScale) + self.model.reparentTo(self.rotateNode) + self.stick2Ground() + + def handleEnterPlot(self, entry = None): + pass + + def handleExitPlot(self, entry = None): + DistributedLawnDecor.DistributedLawnDecor.handleExitPlot(self, entry) + + def setTypeIndex(self, typeIndex): + self.typeIndex = typeIndex + \ No newline at end of file diff --git a/toontown/estate/DistributedGardenBoxAI.py b/toontown/estate/DistributedGardenBoxAI.py new file mode 100755 index 00000000..eadd31a9 --- /dev/null +++ b/toontown/estate/DistributedGardenBoxAI.py @@ -0,0 +1,12 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.estate.DistributedLawnDecorAI import DistributedLawnDecorAI + +class DistributedGardenBoxAI(DistributedLawnDecorAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedGardenBoxAI") + + def setTypeIndex(self, index): + self.index = index + + def getTypeIndex(self): + return self.index + \ No newline at end of file diff --git a/toontown/estate/DistributedGardenPlot.py b/toontown/estate/DistributedGardenPlot.py new file mode 100755 index 00000000..acfab9e6 --- /dev/null +++ b/toontown/estate/DistributedGardenPlot.py @@ -0,0 +1,377 @@ +import DistributedLawnDecor +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.ShowBase import * +from direct.interval.IntervalGlobal import * +from DistributedGardenBox import DistributedGardenBox +import GardenGlobals +from toontown.toonbase import TTLocalizer +from toontown.estate import PlantingGUI +from toontown.estate import PlantTreeGUI +from toontown.estate import ToonStatueSelectionGUI +from toontown.toontowngui import TTDialog +from pandac.PandaModules import Vec4 +from pandac.PandaModules import NodePath +import types + +class DistributedGardenPlot(DistributedLawnDecor.DistributedLawnDecor): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGardenPlot') + deferFor = 2 + + def __init__(self, cr): + DistributedLawnDecor.DistributedLawnDecor.__init__(self, cr) + self.plantPath = NodePath('plantPath') + self.plantPath.reparentTo(self) + self.plotScale = 1.0 + self.plantingGuiDoneEvent = 'plantingGuiDone' + self.toonStatueSelectionDoneEvent = 'toonStatueSelectionDone' + self.defaultModel = 'phase_5.5/models/estate/dirt_mound' + self.colorScaler = Vec4(1, 1, 1, 1) + self.plantingGui = None + return + + def delete(self): + if self.plantingGui: + self.plantingGui.destroy() + self.plantingGui = None + DistributedLawnDecor.DistributedLawnDecor.delete(self) + return + + def announceGenerate(self): + self.plotType = GardenGlobals.whatCanBePlanted(self.ownerIndex, self.plot) + self.stickUp = 0.0 + if self.getOwnerId() != localAvatar.doId: + self.defaultModel = None + elif self.plotType == GardenGlobals.FLOWER_TYPE: + self.collSphereRadius = 2.0 + self.collSphereOffset = 0.0 + self.plotScale = 0.6 + self.stickUp = 1.1 + elif self.plotType == GardenGlobals.GAG_TREE_TYPE: + self.collSphereRadius = 3.0 + self.plotScale = 1.5 + self.colorScaler = Vec4(1.0, 1.0, 1.0, 1) + elif self.plotType == GardenGlobals.STATUARY_TYPE: + self.collSphereRadius = 3.0 + self.plotScale = 0.075 + self.stickUp = -0.0 + self.defaultModel = 'phase_5.5/models/estate/garden_slab' + else: + self.collSphereOffset = 0.0 + self.notify.debug('announceGenerate') + DistributedLawnDecor.DistributedLawnDecor.announceGenerate(self) + return + + def loadModel(self): + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = None + if self.defaultModel: + self.model = loader.loadModel(self.defaultModel) + if type(self.plotScale) == types.TupleType: + self.model.setScale(*self.plotScale) + else: + self.model.setScale(self.plotScale) + self.model.reparentTo(self.rotateNode) + self.model.setColorScale(self.colorScaler) + self.stick2Ground() + return + + def setupShadow(self): + pass + + def getShovelCommand(self): + return self.plantSomething + + def getShovelAction(self): + return self.getPlantingText() + + def handleEnterPlot(self, entry = None): + dist = self.getDistance(localAvatar) + if self.canBePlanted(): + base.localAvatar.addShovelRelatedDoId(self.doId) + + def handleExitPlot(self, entry = None): + DistributedLawnDecor.DistributedLawnDecor.handleExitPlot(self, entry) + base.localAvatar.removeShovelRelatedDoId(self.doId) + + def getPlantingText(self): + plantText = 'hardcoding' + if self.canBePlanted(): + whatCanBePlanted = GardenGlobals.whatCanBePlanted(self.ownerIndex, self.plot) + plantText = TTLocalizer.GardeningPlant + if whatCanBePlanted == GardenGlobals.INVALID_TYPE: + self.notify.warning('whatCanBePlanted returned INVALID_TYPE for %d %d' % (self.ownerIndex, self.plot)) + elif whatCanBePlanted == GardenGlobals.FLOWER_TYPE: + plantText = TTLocalizer.GardeningPlantFlower + elif whatCanBePlanted == GardenGlobals.GAG_TREE_TYPE: + plantText = TTLocalizer.GardeningPlantTree + elif whatCanBePlanted == GardenGlobals.STATUARY_TYPE: + plantText = TTLocalizer.GardeningPlantItem + return plantText + + def canBePlanted(self): + return base.localAvatar.doId == self.getOwnerId() + + def plantSomething(self): + whatCanBePlanted = GardenGlobals.whatCanBePlanted(self.ownerIndex, self.plot) + if whatCanBePlanted == GardenGlobals.INVALID_TYPE: + self.notify.warning('whatCanBePlanted returned INVALID_TYPE for %d %d' % (self.ownerIndex, self.plot)) + elif whatCanBePlanted == GardenGlobals.FLOWER_TYPE: + self.popupFlowerPlantingGui() + self.startInteraction() + elif whatCanBePlanted == GardenGlobals.GAG_TREE_TYPE: + self.popupTreePlantingGui() + self.startInteraction() + elif whatCanBePlanted == GardenGlobals.STATUARY_TYPE: + self.popupItemPlantingGui() + self.startInteraction() + + def __handleFlowerPlantingDone(self, willPlant = 0, recipeStr = '', special = -1): + self.ignore(self.plantingGuiDoneEvent) + self.ignore('stoppedAsleep') + self.plantingGui.destroy() + self.plantingGui = None + base.localAvatar.showGardeningGui() + base.localAvatar.removeShovelRelatedDoId(self.doId) + successPlanting = False + if willPlant: + recipeKey = GardenGlobals.getRecipeKey(recipeStr, special) + if recipeKey >= 0: + species, variety = GardenGlobals.getSpeciesVarietyGivenRecipe(recipeKey) + if species >= 0 and variety >= 0: + self.sendUpdate('plantFlower', [species, variety]) + successPlanting = True + else: + self.notify.debug('%s %d is not a valid recipe' % (recipeStr, special)) + burntBeans = len(recipeStr) + self.sendUpdate('plantNothing', [burntBeans]) + if successPlanting: + flowerName = GardenGlobals.getFlowerVarietyName(species, variety) + stringToShow = TTLocalizer.getResultPlantedSomethingSentence(flowerName) + elif willPlant: + self.resultDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.ResultPlantedNothing, command=self.popupFlowerPlantingGuiAgain) + else: + self.finishInteraction() + return + + def popupFlowerPlantingGui(self): + base.localAvatar.hideGardeningGui() + self.acceptOnce(self.plantingGuiDoneEvent, self.__handleFlowerPlantingDone) + self.plantingGui = PlantingGUI.PlantingGUI(self.plantingGuiDoneEvent) + self.accept('stoppedAsleep', self.__handleFlowerPlantingDone) + + def resultsCallback(self, value): + self.notify.debug('value=%d' % value) + self.resultDialog.destroy() + self.resultDialog = None + self.finishInteraction() + return + + def popupFlowerPlantingGuiAgain(self, value): + self.notify.debug('value=%d' % value) + self.resultDialog.destroy() + self.resultDialog = None + self.popupFlowerPlantingGui() + return + + def popupItemPlantingGuiAgain(self, value): + self.notify.debug('value=%d' % value) + self.resultDialog.destroy() + self.resultDialog = None + self.popupItemPlantingGui() + return + + def __handleItemPlantingDone(self, willPlant = 0, recipeStr = '', selectedSpecial = -1): + self.ignore(self.plantingGuiDoneEvent) + self.ignore('stoppedAsleep') + self.plantingGui.destroy() + self.plantingGui = None + base.localAvatar.showGardeningGui() + base.localAvatar.removeShovelRelatedDoId(self.doId) + gardenSpecials = base.localAvatar.getGardenSpecials() + special = -1 + if selectedSpecial >= 0: + special = gardenSpecials[selectedSpecial][0] + successPlanting = False + successToonStatue = False + if willPlant: + recipeKey = GardenGlobals.getRecipeKey(recipeStr, special) + if recipeKey >= 0: + species, variety = GardenGlobals.getSpeciesVarietyGivenRecipe(recipeKey) + if species >= 0 and variety >= 0: + if GardenGlobals.PlantAttributes[species]['plantType'] == GardenGlobals.STATUARY_TYPE: + successPlanting = True + if species >= 205 and species <= 208: + successToonStatue = True + else: + self.sendUpdate('plantStatuary', [species]) + else: + self.notify.debug('%s %d is not a valid recipe' % (recipeStr, special)) + burntBeans = len(recipeStr) + self.sendUpdate('plantNothing', [burntBeans]) + if successPlanting: + itemName = GardenGlobals.PlantAttributes[species]['name'] + stringToShow = TTLocalizer.getResultPlantedSomethingSentence(itemName) + elif willPlant: + self.resultDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.ResultPlantedNothing, command=self.popupItemPlantingGuiAgain) + else: + self.finishInteraction() + if successToonStatue: + self.popupToonStatueSelectionGui(species) + return + + def popupItemPlantingGui(self): + base.localAvatar.hideGardeningGui() + self.acceptOnce(self.plantingGuiDoneEvent, self.__handleItemPlantingDone) + self.plantingGui = PlantingGUI.PlantingGUI(self.plantingGuiDoneEvent, True) + self.plantingGui.showFirstSpecial() + self.accept('stoppedAsleep', self.__handleItemPlantingDone) + + def popupToonStatueSelectionGui(self, species): + base.localAvatar.hideGardeningGui() + self.acceptOnce(self.toonStatueSelectionDoneEvent, self.__handleToonStatueSelectionDone, extraArgs=[species]) + self.toonStatueSelectionGui = ToonStatueSelectionGUI.ToonStatueSelectionGUI(self.toonStatueSelectionDoneEvent, True) + self.accept('stoppedAsleep', self.__handleToonStatueSelectionDone) + + def popupToonStatueSelectionGuiAgain(self, species): + self.resultDialog.destroy() + self.resultDialog = None + self.popupToonStatueSelectionGui(species) + return + + def __handleToonStatueSelectionDone(self, species, willPlant = 0, recipeStr = '', dnaCode = -1): + self.ignore(self.toonStatueSelectionDoneEvent) + self.ignore('stoppedAsleep') + self.toonStatueSelectionGui.destroy() + self.toonStatueSelectionGui = None + base.localAvatar.showGardeningGui() + base.localAvatar.removeShovelRelatedDoId(self.doId) + if willPlant: + self.sendUpdate('plantToonStatuary', [species, dnaCode]) + else: + self.popupItemPlantingGui() + return + + def popupTreePlantingGui(self): + base.localAvatar.hideGardeningGui() + self.acceptOnce(self.plantingGuiDoneEvent, self.__handleTreePlantingDone) + self.plantingGui = PlantTreeGUI.PlantTreeGUI(self.plantingGuiDoneEvent) + self.accept('stoppedAsleep', self.__handleTreePlantingDone) + + def __handleTreePlantingDone(self, willPlant = False, gagTrack = None, gagLevel = None): + self.ignore(self.plantingGuiDoneEvent) + self.ignore('stoppedAsleep') + self.plantingGui.destroy() + self.plantingGui = None + base.localAvatar.showGardeningGui() + base.localAvatar.removeShovelRelatedDoId(self.doId) + if willPlant: + self.sendUpdate('plantGagTree', [gagTrack, gagLevel]) + else: + self.finishInteraction() + return + + def setMovie(self, mode, avId): + if mode == GardenGlobals.MOVIE_PLANT: + self.doPlaceItemTrack(avId) + elif mode == GardenGlobals.MOVIE_FINISHREMOVING: + self.doFinishRemovingTrack(avId) + elif mode == GardenGlobals.MOVIE_PLANT_REJECTED: + self.doPlantRejectedTrack(avId) + + def doPlantRejectedTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + self.movie = Sequence() + self.movie.append(Func(toon.detachShovel)) + self.movie.append(Func(toon.loop, 'neutral')) + if avId == localAvatar.doId: + self.movie.append(Func(self.finishInteraction)) + self.movie.append(Func(self.movieDone)) + self.movie.start() + + def doFinishRemovingTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + self.movie = Sequence() + self.movie.append(Func(toon.detachShovel)) + if self.model: + pos = self.model.getPos() + pos.setZ(pos[2] - 1) + animProp = LerpPosInterval(self.model, 3, self.model.getPos(), pos) + shrinkProp = LerpScaleInterval(self.model, 3, scale=self.plotScale, startScale=0.01) + objAnimShrink = ParallelEndTogether(animProp, shrinkProp) + self.movie.append(objAnimShrink) + self.movie.append(self.stopCamIval(avId)) + self.movie.append(Func(toon.loop, 'neutral')) + if avId == localAvatar.doId: + self.movie.append(Func(self.finishInteraction)) + self.movie.append(Func(self.movieDone)) + self.movie.start() + + def doPlaceItemTrack(self, avId, item = None): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + if avId == localAvatar.doId: + self.startInteraction() + shovel = toon.attachShovel() + shovel.hide() + moveTrack = self.generateToonMoveTrack(toon) + placeItemTrack = self.generatePlaceItemTrack(toon, item) + self.movie = Sequence(self.startCamIval(avId), moveTrack, Func(shovel.show), placeItemTrack) + if avId == localAvatar.doId: + self.expectingReplacement = 1 + self.movie.append(Func(self.movieDone)) + self.movie.start() + + def generatePlaceItemTrack(self, toon, item): + sound = loader.loadSfx('phase_5.5/audio/sfx/burrow.ogg') + sound.setPlayRate(0.5) + placeItemTrack = Parallel() + placeItemTrack.append(Sequence(ActorInterval(toon, 'start-dig'), Parallel(ActorInterval(toon, 'loop-dig', loop=1, duration=5.13), Sequence(Wait(0.25), SoundInterval(sound, node=toon, duration=0.55), Wait(0.8), SoundInterval(sound, node=toon, duration=0.55), Wait(1.35), SoundInterval(sound, node=toon, duration=0.55))), ActorInterval(toon, 'start-dig', playRate=-1), Func(toon.loop, 'neutral'), Func(toon.detachShovel))) + if self.model: + pos = self.model.getPos() + pos.setZ(pos[2] - 1) + animProp = LerpPosInterval(self.model, 3, pos) + shrinkProp = LerpScaleInterval(self.model, 3, scale=0.01, startScale=self.model.getScale()) + objAnimShrink = ParallelEndTogether(animProp, shrinkProp) + placeItemTrack.append(objAnimShrink) + if item: + placeItemTrack.append(Sequence(Func(item.reparentTo, toon.rightHand), Wait(0.55), Func(item.wrtReparentTo, render), Parallel(LerpHprInterval(item, hpr=self.getHpr(render), duration=1.2), ProjectileInterval(item, endPos=self.getPos(render), duration=1.2, gravityMult=0.45)), Func(item.removeNode))) + return placeItemTrack + + def makeMovieNode(self): + if self.plotType == GardenGlobals.FLOWER_TYPE: + self.movieNode = self.rotateNode.attachNewNode('moviePos') + self.movieNode.setPos(0, 3, 0) + self.movieNode.setH(180) + self.stick2Ground() + else: + DistributedLawnDecor.DistributedLawnDecor.makeMovieNode(self) + + def setBoxDoId(self, boxId, index): + self.index = index + if boxId in base.cr.doId2do: + self.setBox(base.cr.doId2do[boxId]) + else: + self.acceptOnce('generate-%d' % boxId, self.setBox) + + def setBox(self, box): + x = GardenGlobals.FLOWER_POS[box.typeIndex][self.index] + + self.setPos(0, 0, 0) + self.reparentTo(box) + self.setZ(1.2) + self.setX(x) + + def stick2Ground(self, *args, **kwargs): + plotType = GardenGlobals.whatCanBePlanted(self.ownerIndex, self.plot) + if plotType == GardenGlobals.FLOWER_TYPE: + return + + return DistributedLawnDecor.DistributedLawnDecor.stick2Ground(self, *args, **kwargs) diff --git a/toontown/estate/DistributedGardenPlotAI.py b/toontown/estate/DistributedGardenPlotAI.py new file mode 100755 index 00000000..648f9b38 --- /dev/null +++ b/toontown/estate/DistributedGardenPlotAI.py @@ -0,0 +1,243 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.ai.MagicWordGlobal import * +from DistributedLawnDecorAI import DistributedLawnDecorAI +import GardenGlobals + +class DistributedGardenPlotAI(DistributedLawnDecorAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGardenPlotAI') + + def announceGenerate(self): + DistributedLawnDecorAI.announceGenerate(self) + self.plotType = GardenGlobals.whatCanBePlanted(self.ownerIndex, self.plot) + self.__plantingAvId = 0 + + def __initialSanityCheck(self, wantedType=None, forceOwner=False): + if self.__plantingAvId: + # Busy, silently ignore + return + + avId = self.air.getAvatarIdFromSender() + + av = self.air.doId2do.get(avId) + if not av: + self.air.writeServerEvent('suspicious', avId, 'called DistributedGardenPlotAI method outside shard!') + return + + if wantedType is not None and self.plotType != wantedType: + self.air.writeServerEvent('suspicious', avId, 'called incorrect DistributedGardenPlotAI method!', plotType=self.plotType, + wantedType=wantedType) + return self.d_interactionDenied() + + + if avId != self.ownerDoId and not forceOwner: + self.air.writeServerEvent('suspicious', avId, 'called someone else\'s DistributedGardenPlotAI plant method!', + ownerDoId=self.ownerDoId) + return self.d_interactionDenied() + + return av + + def plantFlower(self, species, variety, usingSatanFlowerAll=0): + av = self.__initialSanityCheck(GardenGlobals.FLOWER_TYPE if not usingSatanFlowerAll else None, usingSatanFlowerAll) + if not av: + return + + def invalid(problem): + msg = 'tried to plant flower but something went wrong: %s' % problem + self.notify.warning('%d %s' % (av.doId, msg)) + self.air.writeServerEvent('suspicious', av.doId, msg) + if not usingSatanFlowerAll: + return self.d_setMovie(GardenGlobals.MOVIE_PLANT_REJECTED) + + attr = GardenGlobals.PlantAttributes.get(species, {}) + if attr.get('plantType') != GardenGlobals.FLOWER_TYPE: + return invalid('invalid species: %d' % species) + + if variety >= len(attr['varieties']): + return invalid('invalid variety: %d' % variety) + + if not usingSatanFlowerAll: + cost = len(GardenGlobals.Recipes[attr['varieties'][variety][0]]['beans']) + av.takeMoney(cost) + + self.d_setMovie(GardenGlobals.MOVIE_PLANT) + + def _plant(task): + flower = self.mgr.plantFlower(self.flowerIndex, species, variety, plot=self, + ownerIndex=self.ownerIndex, plotId=self.plot, + waterLevel=0) + index = (0, 1, 2, 2, 2, 3, 3, 3, 4, 4)[self.flowerIndex] + idx = (0, 0, 0, 1, 2, 0, 1, 2, 0, 1)[self.flowerIndex] + flower.sendUpdate('setBoxDoId', [self.mgr._boxes[index].doId, idx]) + + if not usingSatanFlowerAll: + flower.d_setMovie(GardenGlobals.MOVIE_FINISHPLANTING, self.__plantingAvId) + flower.d_setMovie(GardenGlobals.MOVIE_CLEAR, self.__plantingAvId) + + self.air.writeServerEvent('plant-flower', self.__plantingAvId, species=species, variety=variety, + plot=self.plot, name=attr.get('name', 'unknown satan flower')) + if task: + return task.done + + if usingSatanFlowerAll: + _plant(None) + + else: + taskMgr.doMethodLater(7, _plant, self.uniqueName('do-plant')) + + self.__plantingAvId = av.doId + return 1 + + def plantGagTree(self, track, index): + av = self.__initialSanityCheck(GardenGlobals.GAG_TREE_TYPE) + if not av: + return + + for i in xrange(index): + if not self.mgr.hasTree(track, i): + msg = 'tried to plant tree but an index is missing: %d' % index + self.notify.warning('%d %s' % (av.doId, msg)) + self.air.writeServerEvent('suspicious', av.doId, msg) + return self.d_setMovie(GardenGlobals.MOVIE_PLANT_REJECTED) + + if self.mgr.hasTree(track, index): + msg = 'tried to plant tree but gag already planted' + self.notify.warning('%d %s' % (av.doId, msg)) + self.air.writeServerEvent('suspicious', av.doId, msg) + return self.d_setMovie(GardenGlobals.MOVIE_PLANT_REJECTED) + + if av.inventory.useItem(track, index) == -1: + msg = 'tried to plant tree but not carrying selected gag' + self.notify.warning('%d %s' % (av.doId, msg)) + self.air.writeServerEvent('suspicious', av.doId, msg) + return self.d_setMovie(GardenGlobals.MOVIE_PLANT_REJECTED) + + av.d_setInventory(av.getInventory()) + self.d_setMovie(GardenGlobals.MOVIE_PLANT) + + def _plant(task): + if not self.air: + return + + tree = self.mgr.plantTree(self.treeIndex, track * 7 + index, plot=self, ownerIndex=self.ownerIndex, + plotId=self.plot, pos=(self.getPos(), self.getH())) + tree.d_setMovie(GardenGlobals.MOVIE_FINISHPLANTING, self.__plantingAvId) + tree.d_setMovie(GardenGlobals.MOVIE_CLEAR, self.__plantingAvId) + self.air.writeServerEvent('plant-tree', self.__plantingAvId, track=track, index=index, plot=self.plot) + return task.done + + taskMgr.doMethodLater(7, _plant, self.uniqueName('do-plant')) + self.__plantingAvId = av.doId + + def plantStatuary(self, species): + av = self.__initialSanityCheck(GardenGlobals.STATUARY_TYPE) + if not av: + return + + def invalid(problem): + msg = 'tried to plant statuary but something went wrong: %s' % problem + self.notify.warning('%d %s' % (av.doId, msg)) + self.air.writeServerEvent('suspicious', av.doId, msg) + return self.d_setMovie(GardenGlobals.MOVIE_PLANT_REJECTED) + + attr = GardenGlobals.PlantAttributes.get(species, {}) + if attr.get('plantType') != GardenGlobals.STATUARY_TYPE: + return invalid('invalid species: %d' % species) + + it = species - 100 + if it == 134: + it = 135 + + if not av.removeGardenItem(it, 1): + return invalid('av doesn\'t own item: %d' % species) + + self.d_setMovie(GardenGlobals.MOVIE_PLANT) + + def _plant(task): + if not self.air: + return + + statuary = self.mgr.placeStatuary(self.mgr.S_pack(0, 0, species, 0), plot=self, + ownerIndex=self.ownerIndex, plotId=self.plot, + pos=(self.getPos(), self.getH()), generate=False) + statuary.generateWithRequired(self.zoneId) + statuary.d_setMovie(GardenGlobals.MOVIE_FINISHPLANTING, self.__plantingAvId) + statuary.d_setMovie(GardenGlobals.MOVIE_CLEAR, self.__plantingAvId) + self.air.writeServerEvent('plant-statuary', self.__plantingAvId, species=species, plot=self.plot) + return task.done + + taskMgr.doMethodLater(7, _plant, self.uniqueName('do-plant')) + self.__plantingAvId = av.doId + + def plantToonStatuary(self, species, dnaCode): + av = self.__initialSanityCheck(GardenGlobals.STATUARY_TYPE) + if not av: + return + + def invalid(problem): + msg = 'tried to plant statuary but something went wrong: %s' % problem + self.notify.warning('%d %s' % (av.doId, msg)) + self.air.writeServerEvent('suspicious', av.doId, msg) + return self.d_setMovie(GardenGlobals.MOVIE_PLANT_REJECTED) + + attr = GardenGlobals.PlantAttributes.get(species, {}) + if attr.get('plantType') != GardenGlobals.STATUARY_TYPE: + return invalid('invalid species: %d' % species) + + if not av.removeGardenItem(species - 100, 1): + return invalid('av doesn\'t own item: %d' % species) + + self.d_setMovie(GardenGlobals.MOVIE_PLANT) + + def _plant(task): + if not self.air: + return + + statuary = self.mgr.placeStatuary(self.mgr.S_pack(dnaCode, 0, species, 0), plot=self, + ownerIndex=self.ownerIndex, plotId=self.plot, + pos=(self.getPos(), self.getH()), generate=False) + statuary.generateWithRequired(self.zoneId) + statuary.d_setMovie(GardenGlobals.MOVIE_FINISHPLANTING, self.__plantingAvId) + self.air.writeServerEvent('plant-statuary', self.__plantingAvId, species=species, plot=self.plot) + return task.done + + taskMgr.doMethodLater(7, _plant, self.uniqueName('do-plant')) + self.__plantingAvId = av.doId + + def plantNothing(self, burntBeans): + av = self.__initialSanityCheck() + if av: + av.takeMoney(burntBeans) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int, int]) +def satanFlowerAll(species=49, variety=0): + invoker = spellbook.getInvoker() + av = spellbook.getTarget() + estate = av.air.estateManager._lookupEstate(av) + + if not estate: + return 'Estate not found!' + + garden = estate.gardenManager.gardens.get(av.doId) + if not garden: + return 'Garden not found!' + + i = 0 + for obj in garden.objects.copy(): + if isinstance(obj, DistributedGardenPlotAI): + if obj.plotType != GardenGlobals.FLOWER_TYPE: + continue + + if not obj.plantFlower(species, variety, 1): + return 'Error on plot %d' % i + + i += 1 + + return '%d disgusting flowers planted' % i + +@magicWord(category=CATEGORY_PROGRAMMER) +def gibSpecials(): + av = spellbook.getTarget() + av.gardenSpecials = [] + for x in (100, 101, 103, 105, 106, 107, 108, 130, 131, 135): + av.addGardenItem(x, 99) + \ No newline at end of file diff --git a/toontown/estate/DistributedHouse.py b/toontown/estate/DistributedHouse.py new file mode 100755 index 00000000..37bb1218 --- /dev/null +++ b/toontown/estate/DistributedHouse.py @@ -0,0 +1,374 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.gui.DirectGui import * +from direct.distributed.ClockDelta import * +from toontown.minigame.OrthoWalk import * +from string import * +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.toon import Toon +from direct.showbase import RandomNumGen +from toontown.toonbase import TTLocalizer +import random +from direct.showbase import PythonUtil +from toontown.hood import Place +import HouseGlobals +from toontown.building import ToonInteriorColors +from direct.showbase.MessengerGlobal import messenger +from toontown.dna.DNAParser import * +from otp.nametag.NametagGroup import NametagGroup +from otp.nametag.Nametag import Nametag + +class DistributedHouse(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedHouse') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.houseType = 0 + self.avId = -1 + self.ownerId = 0 + self.colorIndex = 0 + self.house = None + self.name = '' + self.namePlate = None + self.nameText = None + self.nametag = None + self.floorMat = None + self.matText = None + self.randomGenerator = None + self.housePosInd = 0 + self.house_loaded = 0 + return + + def disable(self): + DistributedObject.DistributedObject.disable(self) + + def delete(self): + self.notify.debug('delete') + self.unload() + self.clearNametag() + if self.namePlate: + self.namePlate.removeNode() + del self.namePlate + self.namePlate = None + if self.floorMat: + self.floorMat.removeNode() + del self.floorMat + self.floorMat = None + if self.house: + self.house.removeNode() + del self.house + self.house_loaded = 0 + del self.randomGenerator + DistributedObject.DistributedObject.delete(self) + return + + def clearNametag(self): + if self.nametag != None: + self.nametag.unmanage(base.marginManager) + self.nametag.setAvatar(NodePath()) + self.nametag.destroy() + self.nametag = None + return + + def load(self): + self.notify.debug('load') + if not self.house_loaded: + if self.houseType >= len(self.cr.playGame.hood.loader.houseModels): + self.houseType = HouseGlobals.HOUSE_DEFAULT + houseModel = self.cr.playGame.hood.loader.houseModels[self.houseType] + self.house = houseModel.copyTo(self.cr.playGame.hood.loader.houseNode[self.housePosInd]) + self.house_loaded = 1 + self.cr.playGame.hood.loader.houseId2house[self.doId] = self.house + if self.houseType == HouseGlobals.HOUSE_DEFAULT: + self.__setHouseColor() + if self.houseType == HouseGlobals.HOUSE_DEFAULT or self.houseType == HouseGlobals.HOUSE_TEST: + self.__setupDoor() + else: + self.__setupDoorCustom() + messenger.send('houseLoaded-%d' % self.doId) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + messenger.send('setBuilding-' + str(self.doId)) + + def __setupDoor(self): + self.notify.debug('setupDoor') + self.dnaStore = self.cr.playGame.dnaStore + doorModelName = 'door_double_round_ul' + if doorModelName[-1:] == 'r': + doorModelName = doorModelName[:-1] + 'l' + else: + doorModelName = doorModelName[:-1] + 'r' + door = self.dnaStore.findNode(doorModelName) + door_origin = self.house.find('**/door_origin') + door_origin.setHpr(90, 0, 0) + door_origin.setScale(0.6, 0.6, 0.8) + door_origin.setPos(door_origin, 0.5, 0, 0.0) + doorNP = door.copyTo(door_origin) + self.door_origin = door_origin + self.randomGenerator = random.Random() + self.randomGenerator.seed(self.doId) + houseColor = HouseGlobals.stairWood + color = Vec4(houseColor[0], houseColor[1], houseColor[2], 1) + setupDoor(doorNP, door_origin, door_origin, self.dnaStore, str(self.colorIndex), color) + self.__setupNamePlate() + self.__setupFloorMat() + self.__setupNametag() + + def __setupDoorCustom(self): + self.randomGenerator = random.Random() + self.randomGenerator.seed(self.doId) + self.notify.debug('setupDoorCustom') + self.dnaStore = self.cr.playGame.dnaStore + door = self.house.find('**/door_0') + door_origin = self.house.find('**/door_origin') + door_origin.setHpr(90, 0, 0) + door_origin.setScale(0.6, 0.6, 0.8) + doorNP = door + self.door_origin = door_origin + color = Vec4(1, 1, 1, 1) + parent = door_origin + rightDoor = door.find('**/rightDoor') + rightDoor.setHpr(door_origin, Vec3(0, 0, 0)) + leftDoor = door.find('**/leftDoor') + leftDoor.setHpr(door_origin, Vec3(0, 0, 0)) + doorTrigger = doorNP.find('**/door_*_trigger') + doorTrigger.wrtReparentTo(door_origin) + doorTrigger.node().setName('door_trigger_' + str(self.doId)) + self.__setupFloorMat(changeColor=False) + self.__setupNametag() + self.__setupNamePlateCustom() + + def __setupNamePlate(self): + self.notify.debug('__setupNamePlate') + if self.namePlate: + self.namePlate.removeNode() + del self.namePlate + self.namePlate = None + nameText = TextNode('nameText') + r = self.randomGenerator.random() + g = self.randomGenerator.random() + b = self.randomGenerator.random() + nameText.setTextColor(r, g, b, 1) + nameText.setAlign(nameText.ACenter) + nameText.setFont(ToontownGlobals.getBuildingNametagFont()) + nameText.setShadowColor(0, 0, 0, 1) + nameText.setBin('fixed') + if TTLocalizer.BuildingNametagShadow: + nameText.setShadow(*TTLocalizer.BuildingNametagShadow) + nameText.setWordwrap(16.0) + xScale = 1.0 + numLines = 0 + if self.name == '': + return + else: + houseName = TTLocalizer.AvatarsHouse % TTLocalizer.GetPossesive(self.name) + nameText.setText(houseName) + self.nameText = nameText + textHeight = nameText.getHeight() - 2 + textWidth = nameText.getWidth() + xScale = 1.0 + if textWidth > 16: + xScale = 16.0 / textWidth + sign_origin = self.house.find('**/sign_origin') + pos = sign_origin.getPos() + sign_origin.setPosHpr(pos[0], pos[1], pos[2] + 0.15 * textHeight, 90, 0, 0) + self.namePlate = sign_origin.attachNewNode(self.nameText) + self.namePlate.setDepthWrite(0) + self.namePlate.setPos(0, -0.05, 0) + self.namePlate.setScale(xScale) + return nameText + + def __setupFloorMat(self, changeColor = True): + if self.floorMat: + self.floorMat.removeNode() + del self.floorMat + self.floorMat = None + mat = self.house.find('**/mat') + if changeColor: + mat.setColor(0.4, 0.357, 0.259, 1.0) + color = HouseGlobals.houseColors[self.housePosInd] + matText = TextNode('matText') + matText.setTextColor(color[0], color[1], color[2], 1) + matText.setAlign(matText.ACenter) + matText.setFont(ToontownGlobals.getBuildingNametagFont()) + matText.setShadowColor(0, 0, 0, 1) + matText.setBin('fixed') + if TTLocalizer.BuildingNametagShadow: + matText.setShadow(*TTLocalizer.BuildingNametagShadow) + matText.setWordwrap(10.0) + xScale = 1.0 + numLines = 0 + if self.name == '': + return + else: + houseName = TTLocalizer.AvatarsHouse % TTLocalizer.GetPossesive(self.name) + matText.setText(houseName) + self.matText = matText + textHeight = matText.getHeight() - 2 + textWidth = matText.getWidth() + xScale = 1.0 + if textWidth > 8: + xScale = 8.0 / textWidth + mat_origin = self.house.find('**/mat_origin') + pos = mat_origin.getPos() + mat_origin.setPosHpr(pos[0] - 0.15 * textHeight, pos[1], pos[2], 90, -90, 0) + self.floorMat = mat_origin.attachNewNode(self.matText) + self.floorMat.setDepthWrite(0) + self.floorMat.setPos(0, -.025, 0) + self.floorMat.setScale(0.45 * xScale) + return + + def __setupNametag(self): + if self.nametag: + self.clearNametag() + if self.name == '': + houseName = '' + else: + houseName = TTLocalizer.AvatarsHouse % TTLocalizer.GetPossesive(self.name) + self.nametag = NametagGroup() + self.nametag.setFont(ToontownGlobals.getBuildingNametagFont()) + if TTLocalizer.BuildingNametagShadow: + self.nametag.setShadow(*TTLocalizer.BuildingNametagShadow) + self.nametag.setContents(Nametag.CName) + self.nametag.setColorCode(NametagGroup.CCHouseBuilding) + self.nametag.setActive(0) + self.nametag.setAvatar(self.house) + self.nametag.setObjectCode(self.doId) + self.nametag.setName(houseName) + self.nametag.manage(base.marginManager) + + def unload(self): + self.notify.debug('unload') + self.ignoreAll() + + def setHouseReady(self): + self.notify.debug('setHouseReady') + try: + self.House_initialized + except: + self.House_initialized = 1 + self.load() + + def setHousePos(self, index): + self.notify.debug('setHousePos') + self.housePosInd = index + self.__setHouseColor() + + def setHouseType(self, index): + self.notify.debug('setHouseType') + self.houseType = index + + def setFavoriteNum(self, index): + self.notify.debug('setFavoriteNum') + self.favoriteNum = index + + def __setHouseColor(self): + if self.house: + bwall = self.house.find('**/*back') + rwall = self.house.find('**/*right') + fwall = self.house.find('**/*front') + lwall = self.house.find('**/*left') + kd = 0.8 + color = HouseGlobals.houseColors[self.colorIndex] + dark = (kd * color[0], kd * color[1], kd * color[2]) + if not bwall.isEmpty(): + bwall.setColor(color[0], color[1], color[2], 1) + if not fwall.isEmpty(): + fwall.setColor(color[0], color[1], color[2], 1) + if not rwall.isEmpty(): + rwall.setColor(dark[0], dark[1], dark[2], 1) + if not lwall.isEmpty(): + lwall.setColor(dark[0], dark[1], dark[2], 1) + aColor = HouseGlobals.atticWood + attic = self.house.find('**/attic') + if not attic.isEmpty(): + attic.setColor(aColor[0], aColor[1], aColor[2], 1) + color = HouseGlobals.houseColors2[self.colorIndex] + chimneyList = self.house.findAllMatches('**/chim*') + for chimney in chimneyList: + chimney.setColor(color[0], color[1], color[2], 1) + + def setAvId(self, id): + self.avId = id + + def setAvatarId(self, avId): + self.notify.debug('setAvatarId = %s' % avId) + self.ownerId = avId + + def getAvatarId(self): + self.notify.debug('getAvatarId') + return self.ownerId + + def setName(self, name): + self.name = name + if self.nameText and self.nameText.getText() != self.name: + if self.name == '': + self.nameText.setText('') + else: + self.nameText.setText(self.name + "'s\n House") + + def getName(self): + return self.name + + def b_setColor(self, colorInd): + self.setColor(colorInd) + self.d_setColor(colorInd) + + def d_setColor(self, colorInd): + self.sendUpdate('setColor', [colorInd]) + + def setColor(self, colorInd): + self.colorIndex = colorInd + if self.house: + self.__setHouseColor() + + def getColor(self): + return self.colorIndex + + def __setupNamePlateCustom(self): + self.notify.debug('__setupNamePlateCustom') + if self.namePlate: + self.namePlate.removeNode() + del self.namePlate + self.namePlate = None + nameText = TextNode('nameText') + nameText.setCardAsMargin(0.1, 0.1, 0.1, 0.1) + nameText.setCardDecal(True) + nameText.setCardColor(1.0, 1.0, 1.0, 0.0) + r = self.randomGenerator.random() + g = self.randomGenerator.random() + b = self.randomGenerator.random() + nameText.setTextColor(r, g, b, 1) + nameText.setAlign(nameText.ACenter) + nameText.setFont(ToontownGlobals.getBuildingNametagFont()) + nameText.setShadowColor(0, 0, 0, 1) + nameText.setBin('fixed') + if TTLocalizer.BuildingNametagShadow: + nameText.setShadow(*TTLocalizer.BuildingNametagShadow) + nameText.setWordwrap(16.0) + xScale = 1.0 + numLines = 0 + if self.name == '': + return + else: + houseName = TTLocalizer.AvatarsHouse % TTLocalizer.GetPossesive(self.name) + nameText.setText(houseName) + self.nameText = nameText + textHeight = nameText.getHeight() - 2 + textWidth = nameText.getWidth() + xScale = 1.0 + if textWidth > 16: + xScale = 16.0 / textWidth + sign_origin = self.house.find('**/sign_origin') + pos = sign_origin.getPos() + sign_origin.setPosHpr(pos[0], pos[1], pos[2] + 0.15 * textHeight, 90, 0, 0) + self.namePlate = sign_origin.attachNewNode(self.nameText) + self.namePlate.setDepthWrite(0) + self.namePlate.setPos(0, -0.05, 0) + self.namePlate.setScale(xScale) + return nameText diff --git a/toontown/estate/DistributedHouseAI.py b/toontown/estate/DistributedHouseAI.py new file mode 100755 index 00000000..407b5ac5 --- /dev/null +++ b/toontown/estate/DistributedHouseAI.py @@ -0,0 +1,308 @@ +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.estate.DistributedHouseInteriorAI import DistributedHouseInteriorAI +from toontown.estate.DistributedHouseDoorAI import DistributedHouseDoorAI +from toontown.estate.DistributedMailboxAI import DistributedMailboxAI +from toontown.building import DoorTypes +from toontown.catalog.CatalogItemList import CatalogItemList +from otp.ai.MagicWordGlobal import * +from toontown.catalog.CatalogFurnitureItem import * +from toontown.catalog.CatalogItem import Customization, WindowPlacement, Location + + +class DistributedHouseAI(DistributedObjectAI): + notify = directNotify.newCategory("DistributedHouseAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + + self.houseType = 0 + self.gardenPos = 0 + self.avatarId = 0 + self.name = '' + self.color = 0 + self.housePos = 0 + self.gender = 0 + self.isInteriorInitialized = 1 + + self.atticItems = CatalogItemList(store=Customization) + self.interiorItems = CatalogItemList(store=Customization) + self.interiorWallpaper = CatalogItemList(store=Customization) + self.atticWallpaper = CatalogItemList(store=Customization) + self.interiorWindows = CatalogItemList(store=Customization) + self.atticWindows = CatalogItemList(store=Customization) + self.deletedItems = CatalogItemList(store=Customization) + + def announceGenerate(self): + DistributedObjectAI.announceGenerate(self) + self.interiorZone = self.air.allocateZone() + + self.door = DistributedHouseDoorAI(self.air, self.getDoId(), DoorTypes.EXT_STANDARD) + self.door.setSwing(3) + self.door.generateWithRequired(self.zoneId) + + self.interiorDoor = DistributedHouseDoorAI(self.air, self.getDoId(), DoorTypes.INT_STANDARD) + self.interiorDoor.setSwing(3) + self.interiorDoor.setOtherDoor(self.door) + self.interiorDoor.generateWithRequired(self.interiorZone) + + self.door.setOtherDoor(self.interiorDoor) + + self.interior = DistributedHouseInteriorAI(self.air, self) + self.interior.setHouseIndex(self.housePos) + self.interior.setHouseId(self.getDoId()) + self.interior.generateWithRequired(self.interiorZone) + + if self.avatarId: + self.mailbox = DistributedMailboxAI(self.air, self) + self.mailbox.generateWithRequired(self.zoneId) + self.sendUpdate('setHouseReady', []) + + def delete(self): + self.door.requestDelete() + self.interiorDoor.requestDelete() + self.interior.requestDelete() + if self.avatarId: + self.mailbox.requestDelete() + self.air.deallocateZone(self.interiorZone) + DistributedObjectAI.delete(self) + + def setHousePos(self, pos): + self.housePos = pos + + def d_setHousePos(self, pos): + self.sendUpdate('setHousePos', [pos]) + + def b_setHousePos(self, pos): + self.setHousePos(pos) + self.d_setHousePos(pos) + + def getHousePos(self): + return self.housePos + + def setHouseType(self, type): + self.houseType = type + + def d_setHouseType(self, type): + self.sendUpdate('setHouseType', [type]) + + def b_setHouseType(self, type): + self.setHouseType(type) + self.d_setHouseType(type) + + def getHouseType(self): + return self.houseType + + def setGardenPos(self, pos): + self.gardenPos = pos + + def d_setGardenPos(self, pos): + self.sendUpdate('setGardenPos', [pos]) + + def b_setGardenPos(self, pos): + self.setGardenPow(pos) + self.d_setGardenPos(pos) + + def getGardenPos(self): + return self.gardenPos + + def setAvatarId(self, avId): + self.avatarId = avId + + def d_setAvatarId(self, avId): + self.sendUpdate('setAvatarId', [avId]) + + def b_setAvatarId(self, avId): + self.setAvatarId(avId) + self.d_setAvatarId(avId) + + def getAvatarId(self): + return self.avatarId + + def setName(self, name): + self.name = name + + def d_setName(self, name): + self.sendUpdate('setName', [name]) + + def b_setName(self, name): + self.setName(name) + self.d_setName(name) + + def getName(self): + return self.name + + def setColor(self, color): + self.color = color + + def d_setColor(self, color): + self.sendUpdate('setColor', [color]) + + def b_setColor(self, color): + self.setColor(color) + self.d_setColor(color) + + def getColor(self): + return self.color + + def setGender(self, genderIndex): + self.gender = genderIndex + + def getGender(self): + return self.gender + + def setAtticItems(self, atticItems): + self.atticItems = CatalogItemList(atticItems, store=Customization) + + def d_setAtticItems(self, atticItems): + self.sendUpdate('setAtticItems', [atticItems]) + + def b_setAtticItems(self, atticItems): + self.setAtticItems(atticItems) + self.d_setAtticItems(atticItems) + + def getAtticItems(self): + return self.atticItems.getBlob() + + def setInteriorItems(self, interiorItems): + self.interiorItems = CatalogItemList(interiorItems, store=Customization | Location) + + def d_setInteriorItems(self, interiorItems): + self.sendUpdate('setInteriorItems', [interiorItems]) + + def b_setInteriorItems(self, interiorItems): + self.setInteriorItems(interiorItems) + self.d_setInteriorItems(interiorItems) + + def getInteriorItems(self): + return self.interiorItems.getBlob() + + def setAtticWallpaper(self, atticWallpaper): + self.atticWallpaper = CatalogItemList(atticWallpaper, store=Customization) + + def d_setAtticWallpaper(self, atticWallpaper): + self.sendUpdate('setAtticWallpaper', [atticWallpaper]) + + def b_setAtticWallpaper(self, atticWallpaper): + self.setAtticWallpaper(atticWallpaper) + self.d_setAtticWallpaper(atticWallpaper) + + def getAtticWallpaper(self): + return self.atticWallpaper.getBlob() + + def setInteriorWallpaper(self, interiorWallpaper): + self.interiorWallpaper = CatalogItemList(interiorWallpaper, store=Customization) + + def d_setInteriorWallpaper(self, interiorWallpaper): + self.sendUpdate('setInteriorWallpaper', [interiorWallpaper]) + + def b_setInteriorWallpaper(self, interiorWallpaper): + self.setInteriorWallpaper(interiorWallpaper) + self.d_setInteriorWallpaper(interiorWallpaper) + + def getInteriorWallpaper(self): + return self.interiorWallpaper.getBlob() + + def setAtticWindows(self, atticWindows): + self.atticWindows = CatalogItemList(atticWindows, store=Customization) + + def d_setAtticWindows(self, atticWindows): + self.sendUpdate('setAtticWindows', [atticWindows]) + + def b_setAtticWindows(self, atticWindows): + self.setAtticWindows(atticWindows) + self.d_setAtticWindows(atticWindows) + + def getAtticWindows(self): + return self.atticWindows.getBlob() + + def setInteriorWindows(self, interiorWindows): + self.interiorWindows = CatalogItemList(interiorWindows, store=Customization | WindowPlacement) + + def d_setInteriorWindows(self, interiorWindows): + self.sendUpdate('setInteriorWindows', [interiorWindows]) + + def b_setInteriorWindows(self, interiorWindows): + self.setInteriorWindows(interiorWindows) + self.d_setInteriorWindows(interiorWindows) + + def getInteriorWindows(self): + return self.interiorWindows.getBlob() + + def setDeletedItems(self, deletedItems): + self.deletedItems = CatalogItemList(deletedItems, store=Customization) + + def d_setDeletedItems(self, deletedItems): + self.sendUpdate('setDeletedItems', [deletedItems]) + + def b_setDeletedItems(self, deletedItems): + self.setDeletedItems(deletedItems) + self.d_setDeletedItems(deletedItems) + + def getDeletedItems(self): + return self.deletedItems.getBlob() + + def setInteriorInitialized(self, initialized): + self.isInteriorInitialized = initialized + + def d_setInteriorInitialized(self, initialized): + self.sendUpdate('setInteriorInitialized', [initialized]) + + def b_setInteriorInitialized(self, initialized): + self.setInteriorInitialized(initialized) + self.d_setInteriorInitialized(initialized) + + def getInteriorInitialized(self): + return self.isInteriorInitialized + + def setCannonEnabled(self, todo0): + pass + + def getCannonEnabled(self): + return 0 + + def setHouseReady(self): + pass + + def addAtticItem(self, item): + self.interior.furnitureManager.saveToHouse() + if item.getFlags() & FLTrunk: + self.atticItems.append(item) + elif item.replacesExisting() and item.hasExisting(): + if item.getFlags() & FLCloset: + items = ClosetToClothes.keys() if item.getFlags() & FLCloset else BankToMoney.keys() + + for itItem in self.interiorItems: + if itItem.furnitureType in items: + posHpr = itItem.posHpr + self.interiorItems.remove(itItem) + item.posHpr = posHpr + self.interiorItems.append(item) + break + for itItem in self.atticItems: + if itItem.furnitureType in items: + self.atticItems.remove(itItem) + self.atticItems.append(item) + break + else: + self.atticItems.append(item) + self.d_setAtticItems(self.atticItems.getBlob()) + self.d_setInteriorItems(self.interiorItems.getBlob()) + self.interior.furnitureManager.loadFromHouse() + + def addWindow(self, item): + self.interior.furnitureManager.saveToHouse() + self.atticWindows.append(item) + self.d_setAtticWindows(self.atticWindows.getBlob()) + self.interior.furnitureManager.loadFromHouse() + + def addWallpaper(self, item): + self.interior.furnitureManager.saveToHouse() + self.atticWallpaper.append(item) + self.d_setAtticWallpaper(self.atticWallpaper.getBlob()) + self.interior.furnitureManager.loadFromHouse() + + def initializeInterior(self): + if (not self.isInteriorInitialized): + self.notify.info('Initializing interior...') + self.interior.initialize() + self.b_setInteriorInitialized(1) \ No newline at end of file diff --git a/toontown/estate/DistributedHouseDoor.py b/toontown/estate/DistributedHouseDoor.py new file mode 100755 index 00000000..b9acae3f --- /dev/null +++ b/toontown/estate/DistributedHouseDoor.py @@ -0,0 +1,134 @@ +from toontown.toonbase.ToonBaseGlobal import * +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.MessengerGlobal import messenger +from direct.fsm import ClassicFSM +from toontown.building import DistributedDoor +from toontown.hood import ZoneUtil +from toontown.suit import Suit +from toontown.building import FADoorCodes +from toontown.building import DoorTypes +from toontown.estate.DistributedHouse import DistributedHouse + +class DistributedHouseDoor(DistributedDoor.DistributedDoor): + + def __init__(self, cr): + DistributedDoor.DistributedDoor.__init__(self, cr) + + def disable(self): + DistributedDoor.DistributedDoor.disable(self) + self.ignoreAll() + + def setZoneIdAndBlock(self, zoneId, block): + self.houseId = block + DistributedDoor.DistributedDoor.setZoneIdAndBlock(self, zoneId, block) + + def getTriggerName(self): + return 'door_trigger_' + str(self.houseId) + + def hideDoorParts(self): + try: + self.findDoorNode('doorFrameHoleRight').hide() + self.findDoorNode('doorFrameHoleLeft').hide() + except: + pass + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + if self.doorType == DoorTypes.EXT_STANDARD: + house = base.cr.doId2do.get(self.houseId) + if not isinstance(house, DistributedHouse): + self.notify.error('tried to use {0} as house'.format(house.__class__.__name__)) + if house and house.house_loaded: + self.__gotRelatedHouse() + else: + self.acceptOnce('houseLoaded-%d' % self.houseId, self.__gotRelatedHouse) + elif self.doorType == DoorTypes.INT_STANDARD: + door = render.find('**/leftDoor;+s') + if door.isEmpty(): + self.acceptOnce('houseInteriorLoaded-%d' % self.zoneId, self.__gotRelatedHouse) + else: + self.__gotRelatedHouse() + + def __gotRelatedHouse(self): + self.doPostAnnounceGenerate() + self.bHasFlat = not self.findDoorNode('door*flat', True).isEmpty() + self.hideDoorParts() + + building = self.getBuilding() + doorTrigger = building.find('**/door_trigger*') + doorTrigger.setName(self.getTriggerName()) + + self.accept(self.getEnterTriggerEvent(), self.doorTrigger) + self.acceptOnce('clearOutToonInterior', self.doorTrigger) + self.zoneDoneLoading = 0 + + def getBuilding(self, allowEmpty = False): + if 'building' not in self.__dict__: + if self.doorType == DoorTypes.INT_STANDARD: + door = render.find('**/leftDoor;+s') + self.building = door.getParent() + elif self.doorType == DoorTypes.EXT_STANDARD: + if self.houseId: + self.building = self.cr.playGame.hood.loader.houseId2house.get(self.houseId, None) + if allowEmpty: + return self.building + return self.building + + def isInterior(self): + if self.doorType == DoorTypes.INT_STANDARD: + return 1 + return 0 + + def getDoorNodePath(self): + if self.doorType == DoorTypes.INT_STANDARD: + otherNP = render.find('**/door_origin') + elif self.doorType == DoorTypes.EXT_STANDARD: + building = self.getBuilding() + otherNP = building.find('**/door') + if otherNP.isEmpty(): + otherNP = building.find('**/door_origin') + else: + self.notify.error('No such door type as ' + str(self.doorType)) + return otherNP + + def enterClosing(self, ts): + doorFrameHoleRight = self.findDoorNode('doorFrameHoleRight') + if doorFrameHoleRight.isEmpty(): + self.notify.warning('enterClosing(): did not find doorFrameHoleRight') + return + rightDoor = self.findDoorNode('rightDoor') + if rightDoor.isEmpty(): + self.notify.warning('enterClosing(): did not find rightDoor') + return + otherNP = self.getDoorNodePath() + trackName = 'doorClose-%d' % self.doId + if self.rightSwing: + h = 100 + else: + h = -100 + self.finishDoorTrack() + self.doorTrack = Sequence(LerpHprInterval(nodePath=rightDoor, duration=1.0, hpr=VBase3(0, 0, 0), startHpr=VBase3(h, 0, 0), other=otherNP, blendType='easeInOut'), Func(doorFrameHoleRight.hide), Func(self.hideIfHasFlat, rightDoor), SoundInterval(self.closeSfx, node=rightDoor), name=trackName) + self.doorTrack.start(ts) + if hasattr(self, 'done'): + base.cr.playGame.hood.loader.setHouse(self.houseId) + zoneId = self.otherZoneId + if self.doorType == DoorTypes.EXT_STANDARD: + whereTo = 'house' + else: + whereTo = 'estate' + request = {'loader': 'safeZoneLoader', + 'where': whereTo, + 'how': 'doorIn', + 'hoodId': ToontownGlobals.MyEstate, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1, + 'allowRedirect': 0, + 'doorDoId': self.otherDoId} + messenger.send('doorDoneEvent', [request]) + return diff --git a/toontown/estate/DistributedHouseDoorAI.py b/toontown/estate/DistributedHouseDoorAI.py new file mode 100755 index 00000000..d7a816f7 --- /dev/null +++ b/toontown/estate/DistributedHouseDoorAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.building.DistributedDoorAI import DistributedDoorAI + +class DistributedHouseDoorAI(DistributedDoorAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedHouseDoorAI") diff --git a/toontown/estate/DistributedHouseInterior.py b/toontown/estate/DistributedHouseInterior.py new file mode 100755 index 00000000..f62a67d6 --- /dev/null +++ b/toontown/estate/DistributedHouseInterior.py @@ -0,0 +1,187 @@ +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase.ToonBaseGlobal import * +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +import HouseGlobals +from toontown.catalog import CatalogItemList +from toontown.catalog import CatalogItem +from toontown.catalog import CatalogSurfaceItem +from toontown.catalog import CatalogWallpaperItem +from toontown.catalog import CatalogFlooringItem +from toontown.catalog import CatalogMouldingItem +from toontown.catalog import CatalogWainscotingItem +from toontown.dna.DNAParser import * +WindowPlugNames = ['**/windowcut_%s*' % x for x in ('b', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i')] +RoomNames = ['**/group%s' % x for x in ( 4, 3, 2, 1)] +WallNames = ('ceiling*', 'wall_side_middle*', 'wall_front_middle*', 'windowcut_*') +MouldingNames = ('wall_side_top*', 'wall_front_top*') +FloorNames = ('floor*',) +WainscotingNames = ('wall_side_bottom*', 'wall_front_bottom*') +BorderNames = ('wall_side_middle*_border', 'wall_front_middle*_border', 'windowcut_*_border') +WallpaperPieceNames = (WallNames, + MouldingNames, + FloorNames, + WainscotingNames, + BorderNames) + +class DistributedHouseInterior(DistributedObject.DistributedObject): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.houseId = 0 + self.houseIndex = 0 + self.interior = None + self.exteriorWindowsHidden = 0 + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.setup() + + def disable(self): + self.interior.removeNode() + del self.interior + DistributedObject.DistributedObject.disable(self) + + def delete(self): + self.ignore(self.uniqueName('enterclosetSphere')) + DistributedObject.DistributedObject.delete(self) + + def setup(self): + dnaStore = base.cr.playGame.dnaStore + self.interior = loader.loadModel('phase_5.5/models/estate/tt_m_ara_int_estateHouseA') + self.interior.reparentTo(render) + doorModelName = 'door_double_round_ur' + door = dnaStore.findNode(doorModelName) + door_origin = self.interior.find('**/door_origin') + door_origin.setHpr(180, 0, 0) + door_origin.setScale(0.8, 0.8, 0.8) + door_origin.setPos(door_origin, 0, -0.025, 0) + doorNP = door.copyTo(door_origin) + houseColor = HouseGlobals.atticWood + color = Vec4(houseColor[0], houseColor[1], houseColor[2], 1) + setupDoor(doorNP, door_origin, door_origin, dnaStore, str(self.houseId), color) + doorFrame = doorNP.find('door_*_flat') + doorFrame.setColor(color) + self.interior.flattenMedium() + self.windowSlots = [] + for name in WindowPlugNames: + plugNodes = self.interior.findAllMatches(name) + if plugNodes.isEmpty(): + self.windowSlots.append((None, None)) + else: + viewBase = plugNodes[0].getParent().attachNewNode('view') + viewBase.setTransform(plugNodes[0].getTransform()) + plug = plugNodes[0].getParent().attachNewNode('plug') + plugNodes.reparentTo(plug) + plug.flattenLight() + self.windowSlots.append((plug, viewBase)) + + self.windowSlots[2][1].setPosHpr(-21.28, -37.15, 16.25, -90.4, 0, 0) + self.windowSlots[6][1].setPosHpr(-12.0, 26.0, 5.51, 0, 0, 0) + self.windowSlots[4][1].setPosHpr(16.0, -12.0, 5.51, -90, 0, 0) + self.__colorWalls() + self.__setupWindows() + messenger.send('houseInteriorLoaded-%d' % self.zoneId) + return None + + def __colorWalls(self): + if not self.wallpaper: + self.notify.info('No wallpaper in interior; clearing.') + for str in WallNames + WainscotingNames: + nodes = self.interior.findAllMatches('**/%s' % str) + for node in nodes: + node.setTextureOff(1) + + return + numSurfaceTypes = CatalogSurfaceItem.NUM_ST_TYPES + numRooms = min(len(self.wallpaper) / numSurfaceTypes, len(RoomNames)) + for room in xrange(numRooms): + roomName = RoomNames[room] + roomNode = self.interior.find(roomName) + if not roomNode.isEmpty(): + for surface in xrange(numSurfaceTypes): + slot = room * numSurfaceTypes + surface + wallpaper = self.wallpaper[slot] + color = wallpaper.getColor() + texture = wallpaper.loadTexture() + for str in WallpaperPieceNames[surface]: + nodes = roomNode.findAllMatches('**/%s' % str) + for node in nodes: + if str == 'ceiling*': + r, g, b, a = color + scale = 0.66 + r *= scale + g *= scale + b *= scale + node.setColorScale(r, g, b, a) + else: + node.setColorScale(*color) + node.setTexture(texture, 1) + + if wallpaper.getSurfaceType() == CatalogSurfaceItem.STWallpaper: + color2 = wallpaper.getBorderColor() + texture2 = wallpaper.loadBorderTexture() + nodes = roomNode.findAllMatches('**/%s_border' % str) + for node in nodes: + node.setColorScale(*color2) + node.setTexture(texture2, 1) + + nodes = self.interior.findAllMatches('**/arch*') + for node in nodes: + node.setColorScale(*(HouseGlobals.archWood + (1,))) + + def __setupWindows(self): + for plug, viewBase in self.windowSlots: + if plug: + plug.show() + if viewBase: + viewBase.getChildren().detach() + + if not self.windows: + self.notify.info('No windows in interior; returning.') + return + for item in self.windows: + plug, viewBase = self.windowSlots[item.placement] + if plug: + plug.hide() + if viewBase: + model = item.loadModel() + model.reparentTo(viewBase) + if self.exteriorWindowsHidden: + model.findAllMatches('**/outside').stash() + + def hideExteriorWindows(self): + self.exteriorWindowsHidden = 1 + for item in self.windows: + plug, viewBase = self.windowSlots[item.placement] + if viewBase: + viewBase.findAllMatches('**/outside').stash() + + def showExteriorWindows(self): + self.exteriorWindowsHidden = 0 + for item in self.windows: + plug, viewBase = self.windowSlots[item.placement] + if viewBase: + viewBase.findAllMatches('**/outside;+s').unstash() + + def setHouseId(self, index): + self.houseId = index + + def setHouseIndex(self, index): + self.houseIndex = index + + def setWallpaper(self, items): + self.wallpaper = CatalogItemList.CatalogItemList(items, store=CatalogItem.Customization) + if self.interior: + self.__colorWalls() + + def setWindows(self, items): + self.windows = CatalogItemList.CatalogItemList(items, store=CatalogItem.Customization | CatalogItem.WindowPlacement) + if self.interior: + self.__setupWindows() diff --git a/toontown/estate/DistributedHouseInteriorAI.py b/toontown/estate/DistributedHouseInteriorAI.py new file mode 100755 index 00000000..124bd0fa --- /dev/null +++ b/toontown/estate/DistributedHouseInteriorAI.py @@ -0,0 +1,149 @@ +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from DistributedFurnitureManagerAI import * +from toontown.catalog import CatalogItem +from toontown.catalog.CatalogWindowItem import CatalogWindowItem +from toontown.catalog.CatalogWallpaperItem import CatalogWallpaperItem +from toontown.catalog.CatalogMouldingItem import CatalogMouldingItem +from toontown.catalog.CatalogFlooringItem import CatalogFlooringItem +from toontown.catalog.CatalogWainscotingItem import CatalogWainscotingItem +from DNAFurnitureReaderAI import DNAFurnitureReaderAI +from toontown.dna.DNAParser import * +import HouseGlobals +import random + +# The house interior DNA files for each +houseInteriors = [ + 'phase_5.5/dna/house_interior3.pdna', + 'phase_5.5/dna/house_interior4.pdna', + 'phase_5.5/dna/house_interior5.pdna', + 'phase_5.5/dna/house_interior7.pdna', + 'phase_5.5/dna/house_interior8.pdna', + 'phase_5.5/dna/house_interior10.pdna', +] + +defaultWindows = [ + CatalogWindowItem(20, placement=2), CatalogWindowItem(20, placement=4), CatalogWindowItem(20, placement=6) +] + +defaultWallpaper = [ + CatalogWallpaperItem(1110, 0, 1010, 0), + CatalogMouldingItem(1000, 2), + CatalogFlooringItem(1000, 4), + CatalogWainscotingItem(1010, 4), + CatalogWallpaperItem(1110, 0, 1010, 0), + CatalogMouldingItem(1000, 2), + CatalogFlooringItem(1000, 4), + CatalogWainscotingItem(1010, 4), + CatalogWallpaperItem(1110, 0, 1010, 0), + CatalogMouldingItem(1000, 2), + CatalogFlooringItem(1000, 4), + CatalogWainscotingItem(1010, 4), + CatalogWallpaperItem(1110, 0, 1010, 0), + CatalogMouldingItem(1000, 2), + CatalogFlooringItem(1000, 4), + CatalogWainscotingItem(1010, 4), +] + + +class DistributedHouseInteriorAI(DistributedObjectAI): + notify = directNotify.newCategory("DistributedHouseInteriorAI") + + def __init__(self, air, house): + DistributedObjectAI.__init__(self, air) + + self.house = house + self.houseId = 0 + self.houseIndex = 0 + self.wallpaper = '' + self.windows = '' + + self.furnitureManager = DistributedFurnitureManagerAI(self.air, self.house, self) + + def announceGenerate(self): + DistributedObjectAI.announceGenerate(self) + + self.furnitureManager.generateWithRequired(self.zoneId) + + def delete(self): + DistributedObjectAI.delete(self) + + self.furnitureManager.delete() + + def initialize(self): + # Get DNA file appropriate to this house... + dnaFile = houseInteriors[self.houseIndex] + + # Load DNA... + dnaStorage = DNAStorage() + dnaData = loadDNAFileAI(dnaStorage, dnaFile) + + # Read it into furniture... + furnitureReader = DNAFurnitureReaderAI(dnaData, self.house.gender, [-11, 2, 0, 0, 0, 0]) + + # Set furniture: + self.furnitureManager.setItems(furnitureReader.getBlob()) + + # Set default windows and wallpaper: + del self.furnitureManager.windows[:] + self.furnitureManager.windows.extend(defaultWindows) + self.furnitureManager.applyWindows() + del self.furnitureManager.wallpaper[:] + self.furnitureManager.wallpaper.extend(defaultWallpaper) + self.furnitureManager.applyWallpaper() + + # Save: + self.furnitureManager.saveToHouse() + + def setHouseId(self, houseId): + self.houseId = houseId + + def d_setHouseId(self, houseId): + self.sendUpdate('setHouseId', [houseId]) + + def b_setHouseId(self, houseId): + self.setHouseId(houseId) + self.d_setHouseId(houseId) + + def getHouseId(self): + return self.houseId + + def setHouseIndex(self, index): + self.houseIndex = index + + def d_setHouseIndex(self, index): + self.sendUpdate('setHouseIndex', [index]) + + def b_setHouseIndex(self, index): + self.setHouseIndex(index) + self.d_setHouseIndex(index) + + def getHouseIndex(self): + return self.houseIndex + + def setWallpaper(self, wallpaper): + self.wallpaper = wallpaper + + def d_setWallpaper(self, wallpaper): + self.sendUpdate('setWallpaper', [wallpaper]) + + def b_setWallpaper(self, wallpaper): + self.setWallpaper(wallpaper) + if self.isGenerated(): + self.d_setWallpaper(wallpaper) + + def getWallpaper(self): + return self.wallpaper + + def setWindows(self, windows): + self.windows = windows + + def d_setWindows(self, windows): + self.sendUpdate('setWindows', [windows]) + + def b_setWindows(self, windows): + self.setWindows(windows) + if self.isGenerated(): + self.d_setWindows(windows) + + def getWindows(self): + return self.windows diff --git a/toontown/estate/DistributedHouseItem.py b/toontown/estate/DistributedHouseItem.py new file mode 100755 index 00000000..f1f1267e --- /dev/null +++ b/toontown/estate/DistributedHouseItem.py @@ -0,0 +1,28 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from toontown.toonbase import TTLocalizer + +class DistributedHouseItem(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedHouseItem') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.load() + + def load(self): + pass + + def disable(self): + DistributedObject.DistributedObject.disable(self) + + def delete(self): + DistributedObject.DistributedObject.delete(self) diff --git a/toontown/estate/DistributedLawnDecor.py b/toontown/estate/DistributedLawnDecor.py new file mode 100755 index 00000000..1a2a7c1b --- /dev/null +++ b/toontown/estate/DistributedLawnDecor.py @@ -0,0 +1,376 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.distributed import ClockDelta +from direct.showbase.PythonUtil import lerp +import math +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.distributed import DistributedNode +from direct.showbase import PythonUtil +from otp.avatar import ShadowCaster +import random +from otp.otpbase import OTPGlobals +from toontown.estate import GardenGlobals + +def recurseParent(intoNode, ParentName): + # funny fact: cogtown had a func like this + parent = intoNode.getParent(0) + if not parent or parent.getName() == 'render': + return 0 + elif parent.getName() == ParentName: + return 1 + else: + return recurseParent(parent, ParentName) + + +class DistributedLawnDecor(DistributedNode.DistributedNode, NodePath, ShadowCaster.ShadowCaster): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawnDecor') + + def __init__(self, cr): + DistributedNode.DistributedNode.__init__(self, cr) + NodePath.__init__(self, 'decor') + ShadowCaster.ShadowCaster.__init__(self, False) + self.plantPath = NodePath('plantPath') + self.plantPath.reparentTo(self) + self.defaultModel = 'phase_9/models/cogHQ/woodCrateB' + self.messageName = None + self.model = None + self.colSphereNode = None + self.rotateNode = None + self.collSphereOffset = 0.0 + self.collSphereRadius = 1.0 + self.stickUp = 0.0 + self.movieNode = None + self.shadowJoint = None + self.shadowScale = 1 + self.expectingReplacement = 0 + self.movie = None + return + + def setHeading(self, h): + self.notify.debug('setting h') + DistributedNode.DistributedNode.setH(self, h) + + def generateInit(self): + self.notify.debug('generateInit') + DistributedNode.DistributedNode.generateInit(self) + + def generate(self): + self.notify.debug('generate') + self.reparentTo(render) + DistributedNode.DistributedNode.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedNode.DistributedNode.announceGenerate(self) + self.doModelSetup() + self.loadModel() + self.setupShadow() + self.makeMovieNode() + self.stick2Ground() + self.setupCollision() + + def doModelSetup(self): + pass + + def disable(self): + self.notify.debug('disable') + self.finishMovies() + self.handleExitPlot() + self.ignoreAll() + DistributedNode.DistributedNode.disable(self) + if hasattr(self, 'nodePath'): + self.nodePath.detachNode() + + def delete(self): + self.notify.debug('delete') + ShadowCaster.ShadowCaster.delete(self) + self.unloadModel() + DistributedNode.DistributedNode.delete(self) + + def loadModel(self): + if not self.rotateNode: + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = None + if __dev__: + self.model = loader.loadModel(self.defaultModel) + self.model.setScale(0.4, 0.4, 0.1) + self.model.reparentTo(self.rotateNode) + return + + def setupShadow(self): + self.shadowJoint = self.rotateNode.attachNewNode('shadow') + self.initializeDropShadow(False) + self.shadowJoint.setScale(self.shadowScale) + self.setActiveShadow() + + def makeMovieNode(self): + self.movieNode = self.rotateNode.attachNewNode('moviePos') + self.movieNode.setPos(0, -3, 0) + + def setupCollision(self): + self.messageName = self.uniqueName('enterplotSphere') + self.messageStartName = self.uniqueName('plotSphere') + self.exitMessageName = self.uniqueName('exitplotSphere') + if self.collSphereOffset <= 0.1: + colSphere = CollisionSphere(0, 0, 0, self.collSphereRadius) + else: + colSphere = CollisionTube(0, -self.collSphereOffset, 0, 0, self.collSphereOffset, 0, self.collSphereRadius) + colSphere.setTangible(0) + colNode = CollisionNode(self.messageStartName) + colNode.addSolid(colSphere) + colSphereNode = self.attachNewNode(colNode) + self.colSphereNode = colSphereNode + self.accept(self.messageName, self.handleEnterPlot) + self.accept(self.exitMessageName, self.handleExitPlot) + + def handleEnterPlot(self, optional = None): + self.notify.debug('handleEnterPlot %d' % self.doId) + self.sendUpdate('plotEntered', []) + + def handleExitPlot(self, optional = None): + if base.localAvatar.inGardenAction == self: + base.localAvatar.handleEndPlantInteraction(self, replacement=self.expectingReplacement) + + def handleWatering(self): + self.handleExitPlot() + base.localAvatar.removeShovelRelatedDoId(self.doId) + + def unloadModel(self): + if self.model: + self.model.removeNode() + del self.model + self.model = None + if hasattr(self, 'nodePath') and self.nodePath: + self.nodePath.removeNode() + self.nodePath = None + taskMgr.remove(self.uniqueName('adjust tree')) + return + + def setPos(self, x, y, z): + DistributedNode.DistributedNode.setPos(self, x, y, z) + self.stick2Ground() + + def setPosition(self, x, y, z): + DistributedNode.DistributedNode.setPos(self, x, y, z) + self.stick2Ground() + + def stick2Ground(self, taskfooler = 0): + if self.isEmpty(): + return Task.done + testPath = NodePath('testPath') + testPath.reparentTo(render) + cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0) + cRayNode = CollisionNode(self.uniqueName('estate-FloorRay')) + cRayNode.addSolid(cRay) + cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + cRayNode.setIntoCollideMask(BitMask32.allOff()) + cRayNodePath = testPath.attachNewNode(cRayNode) + queue = CollisionHandlerQueue() + picker = CollisionTraverser() + picker.addCollider(cRayNodePath, queue) + if self.movieNode: + testPath.setPos(self.movieNode.getX(render), self.movieNode.getY(render), 0) + picker.traverse(render) + if queue.getNumEntries() > 0: + queue.sortEntries() + for index in xrange(queue.getNumEntries()): + entry = queue.getEntry(index) + if recurseParent(entry.getIntoNode(), 'terrain_DNARoot'): + self.movieNode.setZ(entry.getSurfacePoint(self)[2]) + + testPath.setPos(self.getX(), self.getY(), 0) + picker.traverse(render) + if queue.getNumEntries() > 0: + queue.sortEntries() + for index in xrange(queue.getNumEntries()): + entry = queue.getEntry(index) + if recurseParent(entry.getIntoNode(), 'terrain_DNARoot'): + self.setZ(entry.getSurfacePoint(render)[2] + self.stickUp + 0.1) + self.stickParts() + return Task.done + + taskMgr.doMethodLater(1.0, self.stick2Ground, uniqueName('groundsticker')) + return Task.done + + def stickParts(self): + pass + + def setPlot(self, plot): + self.plot = plot + + def setH(self, h): + DistributedNode.DistributedNode.setH(self, h) + + def getPlot(self): + return self.plot + + def setOwnerIndex(self, index): + self.ownerIndex = index + + def getOwnerIndex(self): + return self.ownerIndex + + def getOwnerId(self): + retval = 0 + estate = base.cr.doFind('DistributedEstate') + if estate and hasattr(estate, 'idList') and estate.idList: + if self.ownerIndex < len(estate.idList): + retval = estate.idList[self.ownerIndex] + return retval + + def canBePicked(self): + retval = True + self.notify.debug('base.localAvatar.doId : %s' % base.localAvatar.doId) + self.notify.debug('self.getOwnerId : %s ' % self.getOwnerId()) + self.notify.debug("statue's DoId : %s " % self.doId) + if not hasattr(base, 'localAvatar') or not base.localAvatar.doId == self.getOwnerId(): + retval = False + return retval + + def allowedToPick(self): + return True + + def unlockPick(self): + return True + + def handleRemove(self): + if not self.canBePicked(): + self.notify.debug("I don't own this item, just returning") + return + base.localAvatar.hideShovelButton() + base.localAvatar.hideWateringCanButton() + self.startInteraction() + self.sendUpdate('removeItem', []) + + def generateToonMoveTrack(self, toon): + node = NodePath('tempNode') + displacement = Vec3(toon.getPos(render) - self.getPos(render)) + displacement.setZ(0) + displacement.normalize() + movieDistance = self.movieNode.getDistance(self.rotateNode) + displacement *= movieDistance + node.reparentTo(render) + node.setPos(displacement + self.getPos(render)) + node.lookAt(self) + heading = PythonUtil.fitDestAngle2Src(toon.getH(render), node.getH(render)) + hpr = toon.getHpr(render) + hpr.setX(heading) + finalX = node.getX(render) + finalY = node.getY(render) + finalZ = node.getZ(render) + node.removeNode() + toonTrack = Sequence(Parallel(ActorInterval(toon, 'walk', loop=True, duration=1), Parallel(LerpPosInterval(toon, 1.0, Point3(finalX, finalY, toon.getZ(render)), fluid=True, bakeInStart=False)), LerpHprInterval(toon, 1.0, hpr=hpr)), Func(toon.loop, 'neutral')) + return toonTrack + + def unprint(self, string): + print string + + def startInteraction(self): + place = base.cr.playGame.getPlace() + if place: + place.detectedGardenPlotUse() + base.localAvatar.setInGardenAction(self) + + def finishInteraction(self): + if hasattr(base.cr.playGame.getPlace(), 'detectedGardenPlotDone'): + base.cr.playGame.getPlace().detectedGardenPlotDone() + self.notify.debug('done interaction') + else: + self.notify.warning('base.cr.playGame.getPlace() does not have detectedGardenPlotDone') + if hasattr(base, 'localAvatar'): + base.localAvatar.handleEndPlantInteraction(self) + + def startCamIval(self, avId): + track = Sequence() + if avId == localAvatar.doId: + track = Sequence(Func(base.localAvatar.disableSmartCameraViews), Func(base.localAvatar.setCameraPosForPetInteraction)) + return track + + def stopCamIval(self, avId): + track = Sequence() + if avId == localAvatar.doId: + track = Sequence(Func(base.localAvatar.unsetCameraPosForPetInteraction), Wait(0.8), Func(base.localAvatar.enableSmartCameraViews)) + return track + + def canBeWatered(self): + return 0 + + def getShovelAction(self): + return None + + def getShovelCommand(self): + return None + + def canBePlanted(self): + return 0 + + def movieDone(self): + self.sendUpdate('movieDone', []) + + def setMovie(self, mode, avId): + if mode == GardenGlobals.MOVIE_FINISHPLANTING: + self.doFinishPlantingTrack(avId) + elif mode == GardenGlobals.MOVIE_REMOVE: + self.doDigupTrack(avId) + + def finishMovies(self): + if self.movie: + self.movie.finish() + self.movie = None + return + + def doDigupTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + self.model.setTransparency(1) + self.model.setAlphaScale(1) + shovel = toon.attachShovel() + shovel.hide() + moveTrack = self.generateToonMoveTrack(toon) + digupTrack = self.generateDigupTrack(toon) + self.movie = Sequence(self.startCamIval(avId), moveTrack, Func(shovel.show), digupTrack) + if avId == localAvatar.doId: + # self.expectingReplacement = 1 + self.movie.append(Func(self.movieDone)) + self.movie.start() + + def generateDigupTrack(self, toon): + sound = loader.loadSfx('phase_5.5/audio/sfx/burrow.ogg') + sound.setPlayRate(0.5) + pos = self.model.getPos() + pos.setZ(pos[2] - 1) + track = Parallel() + track.append(Sequence(ActorInterval(toon, 'start-dig'), Parallel(ActorInterval(toon, 'loop-dig', loop=1, duration=5.13), Sequence(Wait(0.25), SoundInterval(sound, node=toon, duration=0.55), Wait(0.8), SoundInterval(sound, node=toon, duration=0.55), Wait(1.35), SoundInterval(sound, node=toon, duration=0.55))), ActorInterval(toon, 'start-dig', playRate=-1), LerpFunc(self.model.setAlphaScale, fromData=1, toData=0, duration=1), Func(toon.loop, 'neutral'), Func(toon.detachShovel))) + return track + + def doFinishPlantingTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + self.movie = Sequence() + if avId == localAvatar.doId: + self.startInteraction() + if self.model: + self.model.setTransparency(1) + self.model.setAlphaScale(0) + self.movie.append(LerpFunc(self.model.setAlphaScale, fromData=0, toData=1, duration=3)) + self.movie.append(self.stopCamIval(avId)) + self.movie.append(Func(toon.detachShovel)) + self.movie.append(Func(toon.loop, 'neutral')) + if avId == localAvatar.doId: + self.movie.append(Func(self.finishInteraction)) + self.movie.append(Func(self.movieDone)) + if hasattr(self, 'doResultDialog'): + self.movie.append(Func(self.doResultDialog)) + self.movie.start() + + def interactionDenied(self, avId): + if avId == localAvatar.doId: + self.finishInteraction() diff --git a/toontown/estate/DistributedLawnDecorAI.py b/toontown/estate/DistributedLawnDecorAI.py new file mode 100755 index 00000000..6bc3c61d --- /dev/null +++ b/toontown/estate/DistributedLawnDecorAI.py @@ -0,0 +1,40 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedNodeAI import DistributedNodeAI + +class DistributedLawnDecorAI(DistributedNodeAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedLawnDecorAI") + + def __init__(self, mgr): + self.mgr = mgr + DistributedNodeAI.__init__(self, self.mgr.air) + self.plot = 0 + self.ownerIndex = 0 + + def setPlot(self, plot): + self.plot = plot + + def getPlot(self): + return self.plot + + def getHeading(self): + return self.getH() + + def getPosition(self): + return self.getPos() + + def setOwnerIndex(self, ownerIndex): + self.ownerIndex = ownerIndex + self.ownerDoId = self.mgr.gardenMgr.mgr.toons[ownerIndex] + self.owner = self.air.doId2do.get(self.ownerDoId) + + def getOwnerIndex(self): + return self.ownerIndex + + def d_setMovie(self, mode, avId=None): + if avId is None: + avId = self.air.getAvatarIdFromSender() + + self.sendUpdate('setMovie', [mode, avId]) + + def d_interactionDenied(self): + self.sendUpdate('interactionDenied', [self.air.getAvatarIdFromSender()]) diff --git a/toontown/estate/DistributedMailbox.py b/toontown/estate/DistributedMailbox.py new file mode 100755 index 00000000..989c1b5d --- /dev/null +++ b/toontown/estate/DistributedMailbox.py @@ -0,0 +1,275 @@ +from direct.distributed import DistributedObject +from toontown.toonbase import ToontownGlobals +import MailboxGlobals +from toontown.catalog import CatalogItem +from toontown.catalog import CatalogItemList +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from toontown.catalog import MailboxScreen +from direct.directnotify.DirectNotifyGlobal import * +from direct.distributed.ClockDelta import * +from panda3d.core import * +import random +from direct.interval.IntervalGlobal import SoundInterval +FlagPitchEmpty = -70 +FlagPitchFull = 0 + +class DistributedMailbox(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedMailbox') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.model = None + self.flag = None + self.flagIval = None + self.nameText = None + self.fullIndicator = 0 + self.mailboxGui = None + self.mailboxDialog = None + self.mailboxSphereEvent = None + self.mailboxSphereEnterEvent = None + self.mailboxGuiDoneEvent = 'mailboxGuiDone' + return + + def announceGenerate(self): + DistributedMailbox.notify.debug('announceGenerate') + DistributedObject.DistributedObject.announceGenerate(self) + self.mailboxSphereEvent = self.taskName('mailboxSphere') + self.mailboxSphereEnterEvent = 'enter' + self.mailboxSphereEvent + if self.houseId == base.localAvatar.houseId: + self.accept(self.mailboxSphereEnterEvent, self.__handleEnterSphere) + self.load() + + def load(self): + DistributedMailbox.notify.debug('load') + randomGenerator = random.Random() + randomGenerator.seed(self.houseId) + r = randomGenerator.random() + g = randomGenerator.random() + b = randomGenerator.random() + self.nameColor = (r, + g, + b, + 1) + houseNode = self.cr.playGame.hood.loader.houseNode[self.housePosInd] + estateNode = houseNode.getParent() + zOffset = 0 + if self.housePosInd == 3: + zOffset = -1 + elif self.housePosInd == 2: + zOffset = 0.5 + self.model = loader.loadModel('phase_5.5/models/estate/mailboxHouse') + self.model.reparentTo(estateNode) + self.model.setPos(houseNode, 19, -4, 0 + zOffset) + self.model.setH(houseNode, 90) + self.flag = self.model.find('**/mailbox_flag') + if self.fullIndicator: + self.flag.setP(FlagPitchFull) + else: + self.flag.setP(FlagPitchEmpty) + self.__setupName() + collision = self.model.find('**/mailbox_collision') + collision.setName(self.mailboxSphereEvent) + + def disable(self): + DistributedMailbox.notify.debug('disable') + self.notify.debug('disable') + self.ignoreAll() + if self.flagIval: + self.flagIval.finish() + self.flagIval = None + if self.model: + self.model.removeNode() + self.model = None + if self.nameText: + self.nameText.removeNode() + self.nameText = None + if self.mailboxGui: + self.mailboxGui.hide() + self.mailboxGui.unload() + self.mailboxGui = None + if self.mailboxDialog: + self.mailboxDialog.cleanup() + self.mailboxDialog = None + self.mailboxSphereEvent = None + self.mailboxSphereEnterEvent = None + DistributedObject.DistributedObject.disable(self) + return + + def setHouseId(self, houseId): + DistributedMailbox.notify.debug('setHouseId( houseId=%d )' % houseId) + self.houseId = houseId + + def setHousePos(self, housePosInd): + DistributedMailbox.notify.debug('setHousePos') + self.housePosInd = housePosInd + + def setName(self, name): + DistributedMailbox.notify.debug('setName( name=%s )' % name) + self.name = name + + def setFullIndicator(self, full): + DistributedMailbox.notify.debug('setFullIndicator( full=%s )' % full) + if self.fullIndicator != full: + self.fullIndicator = full + if self.flag: + if self.flagIval: + self.flagIval.pause() + self.flagIval = None + p = FlagPitchEmpty + if self.fullIndicator: + p = FlagPitchFull + self.flagIval = self.flag.hprInterval(0.5, VBase3(0, p, 0), blendType='easeInOut') + self.flagIval.start() + return + + def __handleEnterSphere(self, collEntry): + DistributedMailbox.notify.debug('Entering Mailbox Sphere....') + self.ignore(self.mailboxSphereEnterEvent) + self.cr.playGame.getPlace().detectedMailboxCollision() + self.accept('mailboxAsleep', self.__handleMailboxSleep) + self.sendUpdate('avatarEnter', []) + + def __handleMailboxSleep(self): + DistributedMailbox.notify.debug('Mailbox Sleep') + if self.mailboxGui: + self.mailboxGui.hide() + self.mailboxGui.unload() + self.mailboxGui = None + if self.mailboxDialog: + self.mailboxDialog.cleanup() + self.mailboxDialog = None + self.__handleMailboxDone() + return + + def __handleMailboxDone(self): + DistributedMailbox.notify.debug('Mailbox Done') + self.sendUpdate('avatarExit', []) + self.ignore(self.mailboxGuiDoneEvent) + self.mailboxGui = None + return + + def freeAvatar(self): + DistributedMailbox.notify.debug('freeAvatar') + self.notify.debug('freeAvatar') + curState = base.cr.playGame.getPlace().getState() + self.notify.debug('Estate.getState() == %s' % curState) + if not curState == 'stopped': + base.cr.playGame.getPlace().setState('walk') + self.ignore('mailboxAsleep') + self.accept(self.mailboxSphereEnterEvent, self.__handleEnterSphere) + + def setMovie(self, mode, avId): + isLocalToon = avId == base.localAvatar.doId + if isLocalToon: + DistributedMailbox.notify.debug('setMovie( mode=%d, avId=%d ) called on a local toon' % (mode, avId)) + else: + DistributedMailbox.notify.debug('setMovie( mode=%d, avId=%d ) called on a non-local toon' % (mode, avId)) + if mode == MailboxGlobals.MAILBOX_MOVIE_CLEAR: + DistributedMailbox.notify.debug('setMovie: clear') + return + elif mode == MailboxGlobals.MAILBOX_MOVIE_EXIT: + if random.random() < 0.5: + sfx = base.loadSfx('phase_5.5/audio/sfx/mailbox_close_1.ogg') + else: + sfx = base.loadSfx('phase_5.5/audio/sfx/mailbox_close_2.ogg') + sfxTrack = SoundInterval(sfx, node=self.model) + sfxTrack.start() + DistributedMailbox.notify.debug('setMovie: exit') + return + elif mode == MailboxGlobals.MAILBOX_MOVIE_EMPTY: + DistributedMailbox.notify.debug('setMovie: empty') + if isLocalToon: + self.mailboxDialog = TTDialog.TTDialog(dialogName='MailboxEmpty', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedMailboxEmpty, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog) + return + elif mode == MailboxGlobals.MAILBOX_MOVIE_WAITING: + DistributedMailbox.notify.debug('setMovie: waiting') + if isLocalToon: + self.mailboxDialog = TTDialog.TTDialog(dialogName='MailboxWaiting', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedMailboxWaiting, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog) + return + elif mode == MailboxGlobals.MAILBOX_MOVIE_READY: + DistributedMailbox.notify.debug('setMovie: ready') + if random.random() < 0.5: + sfx = base.loadSfx('phase_5.5/audio/sfx/mailbox_open_1.ogg') + else: + sfx = base.loadSfx('phase_5.5/audio/sfx/mailbox_open_2.ogg') + sfxTrack = SoundInterval(sfx, node=self.model) + sfxTrack.start() + if isLocalToon: + self.mailboxGui = MailboxScreen.MailboxScreen(self, base.localAvatar, self.mailboxGuiDoneEvent) + self.mailboxGui.show() + self.accept(self.mailboxGuiDoneEvent, self.__handleMailboxDone) + return + elif mode == MailboxGlobals.MAILBOX_MOVIE_NOT_OWNER: + DistributedMailbox.notify.debug('setMovie: not owner') + if isLocalToon: + self.mailboxDialog = TTDialog.TTDialog(dialogName='MailboxNotOwner', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedMailboxNotOwner, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog) + return + else: + DistributedMailbox.notify.warning('unknown mode in setMovie: %s' % mode) + + def acceptItem(self, item, index, callback, optional = -1): + DistributedMailbox.notify.debug('acceptItem') + blob = item.getBlob(store=CatalogItem.Customization) + context = self.getCallbackContext(callback, [item, index]) + self.sendUpdate('acceptItemMessage', [context, + blob, + index, + optional]) + + def acceptInvite(self, item, acceptingIndex, callback, optional = -1): + DistributedMailbox.notify.debug('acceptInvite') + context = self.getCallbackContext(callback, [item, acceptingIndex]) + self.sendUpdate('acceptInviteMessage', [context, item.inviteKey]) + + def acceptItemResponse(self, context, retcode): + DistributedMailbox.notify.debug('acceptItemResponse') + if retcode == ToontownGlobals.P_UserCancelled: + print 'DistributedMailbox User Canceled' + self.doCallbackContext(context, [retcode]) + + def discardItem(self, item, index, callback, optional = -1): + DistributedMailbox.notify.debug('discardItem') + blob = item.getBlob(store=CatalogItem.Customization) + context = self.getCallbackContext(callback, [item, index]) + self.sendUpdate('discardItemMessage', [context, + blob, + index, + optional]) + + def rejectInvite(self, item, acceptingIndex, callback, optional = -1): + DistributedMailbox.notify.debug('rejectInvite') + context = self.getCallbackContext(callback, [item, acceptingIndex]) + self.sendUpdate('rejectInviteMessage', [context, item.inviteKey]) + + def discardItemResponse(self, context, retcode): + DistributedMailbox.notify.debug('discardItemResponse') + self.doCallbackContext(context, [retcode]) + + def __setupName(self): + DistributedMailbox.notify.debug('__setupName') + if self.nameText: + self.nameText.removeNode() + self.nameText = None + nameOrigin = self.model.find('**/nameLocator') + if not nameOrigin.isEmpty(): + text = TextNode('nameText') + text.setTextColor(*self.nameColor) + text.setAlign(TextNode.ACenter) + text.setFont(ToontownGlobals.getToonFont()) + text.setWordwrap(7.5) + text.setText(self.name) + self.nameText = nameOrigin.attachNewNode(text) + self.nameText.setH(90) + self.nameText.setScale(0.2) + return + + def __clearDialog(self, event): + DistributedMailbox.notify.debug('__clearDialog') + self.mailboxDialog.cleanup() + self.mailboxDialog = None + self.freeAvatar() + return + + def sendInviteReadButNotReplied(self, inviteKey): + self.sendUpdate('markInviteReadButNotReplied', [inviteKey]) diff --git a/toontown/estate/DistributedMailboxAI.py b/toontown/estate/DistributedMailboxAI.py new file mode 100755 index 00000000..46410a15 --- /dev/null +++ b/toontown/estate/DistributedMailboxAI.py @@ -0,0 +1,127 @@ +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.toonbase import ToontownGlobals +from toontown.estate import MailboxGlobals + + +class DistributedMailboxAI(DistributedObjectAI): + notify = directNotify.newCategory('DistributedMailboxAI') + + def __init__(self, air, house): + DistributedObjectAI.__init__(self, air) + + self.busy = False + self.user = None + self.house = house + self.houseId = self.house.doId + self.housePos = self.house.housePos + self.name = self.house.name + + def generate(self): + DistributedObjectAI.generate(self) + + self.updateIndicatorFlag() + + def getHouseId(self): + return self.houseId + + def getHousePos(self): + return self.housePos + + def getName(self): + return self.name + + def avatarEnter(self): + if self.busy: + return + avId = self.air.getAvatarIdFromSender() + if avId != self.house.avatarId: + self.setMovie(MailboxGlobals.MAILBOX_MOVIE_NOT_OWNER, avId) + self.resetMovie() + return + + av = self.air.doId2do.get(avId) + if not av: + return + + if len(av.mailboxContents): + self.setMovie(MailboxGlobals.MAILBOX_MOVIE_READY, avId) + self.user = avId + self.busy = True + elif len(av.onOrder): + self.setMovie(MailboxGlobals.MAILBOX_MOVIE_WAITING, avId) + else: + self.setMovie(MailboxGlobals.MAILBOX_MOVIE_EMPTY, avId) + + self.resetMovie() + + def avatarExit(self): + avId = self.air.getAvatarIdFromSender() + if avId != self.user: + return + self.user = None + self.busy = False + self.updateIndicatorFlag() + self.setMovie(MailboxGlobals.MAILBOX_MOVIE_EXIT, avId) + self.sendUpdateToAvatarId(avId, 'freeAvatar', []) + self.resetMovie() + + def setMovie(self, movie, avId): + self.sendUpdate('setMovie', [movie, avId]) + + def resetMovie(self): + taskMgr.doMethodLater(2, self.setMovie, 'resetMovie-%d' % self.doId, extraArgs=[MailboxGlobals.MAILBOX_MOVIE_CLEAR, 0]) + + def updateIndicatorFlag(self): + av = self.air.doId2do.get(self.house.avatarId) + if av: + self.sendUpdate('setFullIndicator', [len(av.mailboxContents)]) + else: + self.sendUpdate('setFullIndicator', [0]) + + def acceptItemMessage(self, context, item, index, optional): + avId = self.air.getAvatarIdFromSender() + if avId != self.user: + return + + av = self.air.doId2do.get(avId) + if not av: + return + + if index >= len(av.mailboxContents): + self.sendUpdateToAvatarId(avId, 'acceptItemResponse', [context, ToontownGlobals.P_InvalidIndex]) + return + + item = av.mailboxContents[index] + returnCode = item.recordPurchase(av, optional) + + if returnCode == ToontownGlobals.P_ItemAvailable: + del av.mailboxContents[index] + av.b_setMailboxContents(av.mailboxContents) + + self.sendUpdateToAvatarId(avId, 'acceptItemResponse', [context, returnCode]) + + def discardItemMessage(self, context, item, index, optional): + avId = self.air.getAvatarIdFromSender() + if avId != self.user: + return + + av = self.air.doId2do.get(avId) + if not av: + return + + if index >= len(av.mailboxContents): + self.sendUpdateToAvatarId(avId, 'discardItemResponse', [context, ToontownGlobals.P_InvalidIndex]) + return + + del av.mailboxContents[index] + av.b_setMailboxContents(av.mailboxContents) + self.sendUpdateToAvatarId(avId, 'discardItemResponse', [context, ToontownGlobals.P_ItemAvailable]) + + def acceptInviteMessage(self, todo0, todo1): + pass # TODO + + def rejectInviteMessage(self, todo0, todo1): + pass # TODO + + def markInviteReadButNotReplied(self, todo0): + pass # TODO diff --git a/toontown/estate/DistributedPhone.py b/toontown/estate/DistributedPhone.py new file mode 100755 index 00000000..02813ca7 --- /dev/null +++ b/toontown/estate/DistributedPhone.py @@ -0,0 +1,378 @@ +from toontown.toonbase import ToontownGlobals +import PhoneGlobals +from toontown.catalog import CatalogScreen +from toontown.catalog import CatalogItem +from toontown.catalog import GiftAvatar +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +import DistributedHouseInterior +from direct.actor import Actor +import DistributedFurnitureItem +from direct.distributed import ClockDelta +from direct.showbase import PythonUtil +from direct.showutil import Rope +from direct.directnotify.DirectNotifyGlobal import * +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.quest import Quests +from direct.task import Task + +class DistributedPhone(DistributedFurnitureItem.DistributedFurnitureItem): + notify = directNotify.newCategory('DistributedPhone') + movieDelay = 0.5 + + def __init__(self, cr): + DistributedFurnitureItem.DistributedFurnitureItem.__init__(self, cr) + self.lastAvId = 0 + self.hasLocalAvatar = 0 + self.lastTime = 0 + self.initialScale = None + self.usedInitialScale = 0 + self.toonScale = None + self.phoneGui = None + self.phoneDialog = None + self.model = None + self.cord = None + self.receiverGeom = None + self.receiverJoint = None + self.phoneSphereEvent = 'phoneSphere' + self.phoneSphereEnterEvent = 'enter' + self.phoneSphereEvent + self.phoneGuiDoneEvent = 'phoneGuiDone' + self.pickupMovieDoneEvent = 'phonePickupDone' + self.numHouseItems = None + self.interval = None + self.intervalAvatar = None + self.phoneInUse = 0 + self.origToonHpr = None + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedFurnitureItem.DistributedFurnitureItem.announceGenerate(self) + self.accept(self.phoneSphereEnterEvent, self.__handleEnterSphere) + self.load() + taskMgr.doMethodLater(6, self.ringIfHasPhoneQuest, self.uniqueName('ringDoLater')) + + def loadModel(self): + self.model = Actor.Actor('phase_5.5/models/estate/prop_phone-mod', {'SS_phoneOut': 'phase_5.5/models/estate/prop_phone-SS_phoneOut', + 'SS_takePhone': 'phase_5.5/models/estate/prop_phone-SS_takePhone', + 'SS_phoneNeutral': 'phase_5.5/models/estate/prop_phone-SS_phoneNeutral', + 'SS_phoneBack': 'phase_5.5/models/estate/prop_phone-SS_phoneBack', + 'SM_phoneOut': 'phase_5.5/models/estate/prop_phone-SM_phoneOut', + 'SM_takePhone': 'phase_5.5/models/estate/prop_phone-SM_takePhone', + 'SM_phoneNeutral': 'phase_5.5/models/estate/prop_phone-SM_phoneNeutral', + 'SM_phoneBack': 'phase_5.5/models/estate/prop_phone-SM_phoneBack', + 'SL_phoneOut': 'phase_5.5/models/estate/prop_phone-SL_phoneOut', + 'SL_takePhone': 'phase_5.5/models/estate/prop_phone-SL_takePhone', + 'SL_phoneNeutral': 'phase_5.5/models/estate/prop_phone-SL_phoneNeutral', + 'SL_phoneBack': 'phase_5.5/models/estate/prop_phone-SL_phoneBack', + 'MS_phoneOut': 'phase_5.5/models/estate/prop_phone-MS_phoneOut', + 'MS_takePhone': 'phase_5.5/models/estate/prop_phone-MS_takePhone', + 'MS_phoneNeutral': 'phase_5.5/models/estate/prop_phone-MS_phoneNeutral', + 'MS_phoneBack': 'phase_5.5/models/estate/prop_phone-MS_phoneBack', + 'MM_phoneOut': 'phase_5.5/models/estate/prop_phone-MM_phoneOut', + 'MM_takePhone': 'phase_5.5/models/estate/prop_phone-MM_takePhone', + 'MM_phoneNeutral': 'phase_5.5/models/estate/prop_phone-MM_phoneNeutral', + 'MM_phoneBack': 'phase_5.5/models/estate/prop_phone-MM_phoneBack', + 'ML_phoneOut': 'phase_5.5/models/estate/prop_phone-ML_phoneOut', + 'ML_takePhone': 'phase_5.5/models/estate/prop_phone-ML_takePhone', + 'ML_phoneNeutral': 'phase_5.5/models/estate/prop_phone-ML_phoneNeutral', + 'ML_phoneBack': 'phase_5.5/models/estate/prop_phone-ML_phoneBack', + 'LS_phoneOut': 'phase_5.5/models/estate/prop_phone-LS_phoneOut', + 'LS_takePhone': 'phase_5.5/models/estate/prop_phone-LS_takePhone', + 'LS_phoneNeutral': 'phase_5.5/models/estate/prop_phone-LS_phoneNeutral', + 'LS_phoneBack': 'phase_5.5/models/estate/prop_phone-LS_phoneBack', + 'LM_phoneOut': 'phase_5.5/models/estate/prop_phone-LM_phoneOut', + 'LM_takePhone': 'phase_5.5/models/estate/prop_phone-LM_takePhone', + 'LM_phoneNeutral': 'phase_5.5/models/estate/prop_phone-LM_phoneNeutral', + 'LM_phoneBack': 'phase_5.5/models/estate/prop_phone-LM_phoneBack', + 'LL_phoneOut': 'phase_5.5/models/estate/prop_phone-LL_phoneOut', + 'LL_takePhone': 'phase_5.5/models/estate/prop_phone-LL_takePhone', + 'LL_phoneNeutral': 'phase_5.5/models/estate/prop_phone-LL_phoneNeutral', + 'LL_phoneBack': 'phase_5.5/models/estate/prop_phone-LL_phoneBack'}) + self.model.pose('SS_phoneOut', 0) + self.receiverJoint = self.model.find('**/joint_receiver') + self.receiverGeom = self.receiverJoint.getChild(0) + mount = loader.loadModel('phase_5.5/models/estate/phoneMount-mod') + mount.setTransparency(0, 1) + self.model.reparentTo(mount) + self.ringSfx = loader.loadSfx('phase_3.5/audio/sfx/telephone_ring.ogg') + self.handleSfx = loader.loadSfx('phase_5.5/audio/sfx/telephone_handle2.ogg') + self.hangUpSfx = loader.loadSfx('phase_5.5/audio/sfx/telephone_hang_up.ogg') + self.pickUpSfx = loader.loadSfx('phase_5.5/audio/sfx/telephone_pickup1.ogg') + if self.initialScale: + mount.setScale(*self.initialScale) + self.usedInitialScale = 1 + phoneSphere = CollisionSphere(0, -0.88, 0, 0.2) + phoneSphere.setTangible(0) + phoneSphereNode = CollisionNode(self.phoneSphereEvent) + phoneSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + phoneSphereNode.addSolid(phoneSphere) + mount.attachNewNode(phoneSphereNode) + if not self.model.find('**/CurveNode7').isEmpty(): + self.setupCord() + return mount + + def setupCamera(self, mode): + camera.wrtReparentTo(render) + if mode == PhoneGlobals.PHONE_MOVIE_PICKUP: + quat = Quat() + quat.setHpr((35, -8, 0)) + LerpPosQuatInterval(camera, 1, (4, -4, base.localAvatar.getHeight() - 0.5), quat, blendType='easeOut', other=base.localAvatar).start() + + def setupCord(self): + if self.cord: + self.cord.detachNode() + self.cord = None + self.cord = Rope.Rope(self.uniqueName('phoneCord')) + self.cord.setColor(0, 0, 0, 1) + self.cord.setup(4, ((self.receiverGeom, (0, 0, 0)), + (self.model.find('**/joint_curveNode1'), (0, 0, 0)), + (self.model.find('**/joint_curveNode2'), (0, 0, 0)), + (self.model.find('**/joint_curveNode3'), (0, 0, 0)), + (self.model.find('**/joint_curveNode4'), (0, 0, 0)), + (self.model.find('**/joint_curveNode5'), (0, 0, 0)), + (self.model.find('**/joint_curveNode6'), (0, 0, 0)), + (self.model.find('**/CurveNode7'), (0, 0, 0)))) + self.cord.reparentTo(self.model) + self.cord.node().setBounds(BoundingSphere(Point3(-1.0, -3.2, 2.6), 2.0)) + + def disable(self): + self.notify.debug('disable') + taskMgr.remove(self.uniqueName('ringDoLater')) + self.clearInterval() + if self.phoneGui: + self.phoneGui.hide() + self.phoneGui.unload() + self.phoneGui = None + if self.phoneDialog: + self.phoneDialog.cleanup() + self.phoneDialog = None + self.__receiverToPhone() + if self.hasLocalAvatar: + self.freeAvatar() + self.ignoreAll() + DistributedFurnitureItem.DistributedFurnitureItem.disable(self) + + def delete(self): + self.notify.debug('delete') + self.model.cleanup() + DistributedFurnitureItem.DistributedFurnitureItem.delete(self) + + def setInitialScale(self, sx, sy, sz): + self.initialScale = (sx, sy, sz) + if not self.usedInitialScale and self.model: + self.setScale(*self.initialScale) + self.usedInitialScale = 1 + + def __handleEnterSphere(self, collEntry): + if self.phoneGui or self.smoothStarted: + return + + if base.localAvatar.doId == self.lastAvId and globalClock.getFrameTime() <= self.lastTime + 0.5: + self.notify.debug('Ignoring duplicate entry for avatar.') + return + + if self.hasLocalAvatar: + self.freeAvatar() + + self.notify.debug('Entering Phone Sphere....') + taskMgr.remove(self.uniqueName('ringDoLater')) + base.localAvatar.obscureMoveFurnitureButton(1) + self.cr.playGame.getPlace().detectedPhoneCollision() + self.hasLocalAvatar = 1 + self.sendUpdate('avatarEnter', []) + + def __handlePhoneDone(self): + self.sendUpdate('avatarExit', []) + self.ignore(self.phoneGuiDoneEvent) + base.localAvatar.obscureMoveFurnitureButton(0) + self.setPos(self.getPos()) + self.phoneGui = None + + def freeAvatar(self): + if self.hasLocalAvatar: + base.localAvatar.speed = 0 + taskMgr.remove(self.uniqueName('lerpCamera')) + base.localAvatar.posCamera(0, 0) + if base.cr.playGame.place != None: + base.cr.playGame.getPlace().setState('walk') + self.hasLocalAvatar = 0 + self.ignore(self.pickupMovieDoneEvent) + self.lastTime = globalClock.getFrameTime() + + def setLimits(self, numHouseItems): + self.numHouseItems = numHouseItems + + def setMovie(self, mode, avId, timestamp): + elapsed = ClockDelta.globalClockDelta.localElapsedTime(timestamp, bits=32) + elapsed = max(elapsed - self.movieDelay, 0) + self.ignore(self.pickupMovieDoneEvent) + if avId != 0: + self.lastAvId = avId + + self.lastTime = globalClock.getFrameTime() + isLocalToon = avId == base.localAvatar.doId + avatar = self.cr.doId2do.get(avId) + self.notify.debug('setMovie: %s %s %s' % (mode, avId, isLocalToon)) + + if mode == PhoneGlobals.PHONE_MOVIE_CLEAR: + self.notify.debug('setMovie: clear') + self.numHouseItems = None + if self.phoneInUse: + self.clearInterval() + self.phoneInUse = 0 + elif mode == PhoneGlobals.PHONE_MOVIE_PICKUP: + self.notify.debug('setMovie: gui') + if avatar: + interval = self.takePhoneInterval(avatar) + if isLocalToon: + self.setupCamera(mode) + interval.setDoneEvent(self.pickupMovieDoneEvent) + self.acceptOnce(self.pickupMovieDoneEvent, self.__showPhoneGui) + self.playInterval(interval, elapsed, avatar) + self.phoneInUse = 1 + elif mode == PhoneGlobals.PHONE_MOVIE_HANGUP: + self.notify.debug('setMovie: gui') + if avatar: + interval = self.replacePhoneInterval(avatar) + self.playInterval(interval, elapsed, avatar) + self.numHouseItems = None + self.phoneInUse = 0 + else: + self.notify.warning('unknown mode in setMovie: %s' % mode) + + def __showPhoneGui(self): + if self.toonScale: + self.sendUpdate('setNewScale', [self.toonScale[0], self.toonScale[1], self.toonScale[2]]) + self.phoneGui = CatalogScreen.CatalogScreen(phone=self, doneEvent=self.phoneGuiDoneEvent) + self.phoneGui.show() + self.accept(self.phoneGuiDoneEvent, self.__handlePhoneDone) + self.accept('phoneAsleep', self.__handlePhoneAsleep) + + def __handlePhoneAsleep(self): + self.ignore('phoneAsleep') + if self.phoneGui: + self.phoneGui.unload() + self.__handlePhoneDone() + + def requestPurchase(self, item, callback, optional = -1): + blob = item.getBlob(store=CatalogItem.Customization) + context = self.getCallbackContext(callback, [item]) + self.sendUpdate('requestPurchaseMessage', [context, blob, optional]) + + def requestGiftPurchase(self, item, targetDoID, callback, optional = -1): + blob = item.getBlob(store=CatalogItem.Customization) + context = self.getCallbackContext(callback, [item]) + self.sendUpdate('requestGiftPurchaseMessage', [context, targetDoID, blob, optional]) + + def requestPurchaseResponse(self, context, retcode): + self.doCallbackContext(context, [retcode]) + + def requestGiftPurchaseResponse(self, context, retcode): + self.doCallbackContext(context, [retcode]) + + def __clearDialog(self, event): + self.phoneDialog.cleanup() + self.phoneDialog = None + self.freeAvatar() + + def takePhoneInterval(self, toon): + torso = TextEncoder.upper(toon.style.torso[0]) + legs = TextEncoder.upper(toon.style.legs[0]) + phoneOutAnim = '%s%s_phoneOut' % (torso, legs) + takePhoneAnim = '%s%s_takePhone' % (torso, legs) + phoneNeutralAnim = '%s%s_phoneNeutral' % (torso, legs) + self.toonScale = toon.getGeomNode().getChild(0).getScale(self.getParent()) + walkTime = 1.0 + scaleTime = 1.0 + origScale = self.getScale() + origToonPos = toon.getPos() + origToonHpr = toon.getHpr() + self.origToonHpr = origToonHpr + self.setScale(self.toonScale) + toon.setPosHpr(self, 0, -4.5, 0, 0, 0, 0) + destToonPos = toon.getPos() + destToonHpr = toon.getHpr() + destToonHpr = VBase3(PythonUtil.fitSrcAngle2Dest(destToonHpr[0], origToonHpr[0]), destToonHpr[1], destToonHpr[2]) + self.setScale(origScale) + toon.setPos(origToonPos) + toon.setHpr(origToonHpr) + walkToPhone = Sequence(Func(toon.stopSmooth), Func(toon.loop, 'walk'), Func(base.playSfx, base.localAvatar.soundWalk), toon.posHprInterval(walkTime, destToonPos, destToonHpr, blendType='easeInOut'), Func(toon.loop, 'neutral'), Func(toon.startSmooth)) + interval = Sequence(Parallel(walkToPhone, ActorInterval(self.model, phoneOutAnim), self.scaleInterval(scaleTime, self.toonScale, blendType='easeInOut')), Parallel(ActorInterval(self.model, takePhoneAnim), ActorInterval(toon, 'takePhone'), Sequence(Wait(0.625), Func(base.playSfx, self.pickUpSfx), Func(self.__receiverToHand, toon), Wait(1), Func(base.playSfx, self.handleSfx))), Func(self.model.loop, phoneNeutralAnim), Func(toon.loop, 'phoneNeutral'), Func(base.playSfx, self.ringSfx)) + return interval + + def replacePhoneInterval(self, toon): + torso = TextEncoder.upper(toon.style.torso[0]) + legs = TextEncoder.upper(toon.style.legs[0]) + phoneBackAnim = '%s%s_phoneBack' % (torso, legs) + scaleTime = 1.0 + interval = Sequence(Parallel(ActorInterval(self.model, phoneBackAnim), ActorInterval(toon, 'phoneBack'), Sequence(Wait(1.0), Func(self.__receiverToPhone), Func(base.playSfx, self.hangUpSfx))), self.scaleInterval(scaleTime, localAvatar.getGeomNode().getScale()[2], blendType='easeInOut'), Func(toon.loop, 'neutral')) + if self.origToonHpr: + interval.append(Func(toon.setHpr, self.origToonHpr)) + self.origToonHpr = None + if toon == base.localAvatar: + interval.append(Func(self.freeAvatar)) + return interval + + def __receiverToHand(self, toon): + self.receiverGeom.reparentTo(toon.leftHand) + self.receiverGeom.setPosHpr(0.0906813, 0.380375, 0.1, 32.41, 70.68, 137.04) + + def __receiverToPhone(self): + self.receiverGeom.reparentTo(self.receiverJoint) + self.receiverGeom.setPosHpr(0, 0, 0, 0, 0, 0) + + def playInterval(self, interval, elapsed, avatar): + if self.interval != None: + self.interval.finish() + self.interval = None + self.interval = interval + self.interval.start(elapsed) + if self.intervalAvatar != avatar: + if self.intervalAvatar: + self.ignore(self.intervalAvatar.uniqueName('disable')) + if avatar: + self.accept(avatar.uniqueName('disable'), self.clearInterval) + self.intervalAvatar = avatar + + def clearInterval(self): + if self.interval != None: + self.interval.finish() + self.interval = None + if self.intervalAvatar: + self.ignore(self.intervalAvatar.uniqueName('disable')) + self.intervalAvatar = None + self.__receiverToPhone() + self.model.pose('SS_phoneOut', 0) + self.phoneInUse = 0 + + def ringIfHasPhoneQuest(self, task): + if Quests.avatarHasPhoneQuest(base.localAvatar) and not Quests.avatarHasCompletedPhoneQuest(base.localAvatar): + self.ring() + return Task.done + + def ring(self): + if self.phoneInUse: + return 0 + phone = self.find('**/prop_phone') + r = 2.0 + w = 0.05 + shakeOnce = Sequence(Func(phone.setR, r), Wait(w), Func(phone.setR, -r), Wait(w)) + shakeSeq = Sequence() + for i in xrange(16): + shakeSeq.append(shakeOnce) + + ringIval = Parallel(Func(base.playSfx, self.ringSfx), shakeSeq, Func(phone.setR, 0)) + self.playInterval(ringIval, 0.0, None) + + def requestGiftAvatar(self, doId): + if not self.phoneGui: + return + + self.sendUpdate('requestGiftAvatar', [doId]) + + def setGiftAvatar(self, fields): + if not self.phoneGui: + return + + self.phoneGui.setFriendReady(GiftAvatar.createFromJson(fields)) \ No newline at end of file diff --git a/toontown/estate/DistributedPhoneAI.py b/toontown/estate/DistributedPhoneAI.py new file mode 100755 index 00000000..041c62ce --- /dev/null +++ b/toontown/estate/DistributedPhoneAI.py @@ -0,0 +1,258 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from toontown.estate.DistributedFurnitureItemAI import DistributedFurnitureItemAI +from PhoneGlobals import * + +from toontown.toonbase import ToontownGlobals +from toontown.catalog import CatalogItem, CatalogInvalidItem, GiftAvatar +from toontown.catalog.CatalogItemList import CatalogItemList +from toontown.uberdog import TopToonsGlobals + +import json + +class LoadGiftAvatar: + + def __init__(self, phone, avId, targetId, optional, callback): + self.air = phone.air + self.phone = phone + self.avId = avId + self.targetId = targetId + self.optional = optional + self.callback = callback + + def start(self): + self.air.dbInterface.queryObject(self.air.dbId, self.targetId, self.__gotAvatar) + + def copyDict(self, dict, *keys): + return {key: dict[key] for key in keys} + + def __gotAvatar(self, dclass, fields): + if dclass != self.air.dclassesByName['DistributedToonAI']: + return + + for key in ('setDNAString', 'setMailboxContents', 'setGiftSchedule', 'setDeliverySchedule'): + fields[key] = fields[key][0].encode('base64') + + newDict = self.copyDict(fields, 'setDNAString', 'setMailboxContents', 'setGiftSchedule', 'setDeliverySchedule', 'setHat', 'setGlasses', 'setBackpack', + 'setShoes', 'setHatList', 'setGlassesList', 'setBackpackList', 'setShoes', 'setShoesList', 'setCustomMessages', 'setEmoteAccess', + 'setClothesTopsList', 'setClothesBottomsList', 'setPetTrickPhrases', 'setNametagStyles') + + self.callback(self.avId, self.targetId, newDict, self.optional) + del self.phone.fsms[self.avId] + +class DistributedPhoneAI(DistributedFurnitureItemAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPhoneAI") + + def __init__(self, air, furnitureMgr, catalogItem): + DistributedFurnitureItemAI.__init__(self, air, furnitureMgr, catalogItem) + self.fsms = {} + self.initialScale = (0.8, 0.8, 0.8) + self.inUse = False + self.currAvId = 0 + + def calcHouseItems(self, avatar): + houseId = avatar.houseId + + if not houseId: + self.notify.warning('Avatar %s has no houseId associated.' % avatar.doId) + return 0 + + house = simbase.air.doId2do.get(houseId) + if not house: + self.notify.warning('House %s (for avatar %s) not instantiated.' % (houseId, avatar.doId)) + return 0 + + mgr = house.interior.furnitureManager + attic = (mgr.atticItems, mgr.atticWallpaper, mgr.atticWindows) + numHouseItems = len(CatalogItemList(house.getInteriorItems(), store=CatalogItem.Customization | CatalogItem.Location)) + numAtticItems = sum(len(x) for x in attic) + + return numHouseItems + numAtticItems + + def setInitialScale(self, scale): + self.initialScale = scale + + def getInitialScale(self): + return self.initialScale + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if self.inUse: + self.ejectAvatar(avId) + return + + av = self.air.doId2do.get(avId) + if av: + self.setInUse(avId) + self.sendUpdateToAvatarId(avId, 'setLimits', [self.calcHouseItems(av)]) + self.d_setMovie(PHONE_MOVIE_PICKUP, avId) + av.b_setCatalogNotify(0, av.mailboxNotify) + + self.air.questManager.toonCalledClarabelle(av) + + def avatarExit(self): + if not self.inUse: + self.notify.warning('Requested avatarExit but phone isn\'t in use!') + return + avId = self.air.getAvatarIdFromSender() + if avId != self.currAvId: + self.notify.warning('Requested avatarExit from unknown avatar %s' %avId) + return + self.d_setMovie(PHONE_MOVIE_HANGUP, avId) + taskMgr.doMethodLater(1, self.resetMovie, self.taskName('resetMovie')) + self.setFree() + + def setFree(self): + self.inUse = False + self.currAvId = 0 + + def setInUse(self, avId): + self.inUse = True + self.currAvId = avId + + def d_setMovie(self, movie, avId): + self.sendUpdate('setMovie', args=[movie, avId, globalClockDelta.getRealNetworkTime(bits=32)]) + + def ejectAvatar(self, avId): + self.sendUpdateToAvatarId(avId, 'freeAvatar', []) + + def __getCaller(self): + avId = self.air.getAvatarIdFromSender() + if avId != self.currAvId: + self.air.writeServerEvent('suspicious', avId, 'tried purchasing item, but not using phone') + self.notify.warning('%d tried purchasing item, but not using phone' % avId) + return + + av = self.air.doId2do.get(avId) + if not av: + self.air.writeServerEvent('suspicious', avId, 'tried purchasing item, but not on shard') + self.notify.warning('%d tried purchasing item, but not on shard' % avId) + return + + return av + + def checkPurchaseLimit(self, recipient, item): + if len(recipient.mailboxContents) + len(recipient.onOrder) + len(recipient.onGiftOrder) + 1 >= ToontownGlobals.MaxMailboxContents: + return ToontownGlobals.P_MailboxFull + elif item.reachedPurchaseLimit(recipient): + return ToontownGlobals.P_ReachedPurchaseLimit + + return ToontownGlobals.P_ItemOnOrder + + def chargeAvatar(self, av, money, emblems): + av.takeMoney(money) + av.subtractEmblems(emblems) + + def attemptPurchase(self, context, av, blob, optional, gifting=False): + avId = av.doId + item = CatalogItem.getItem(blob, CatalogItem.Customization) + + if isinstance(item, CatalogInvalidItem.CatalogInvalidItem): + self.air.writeServerEvent('suspicious', avId, 'tried purchasing invalid item') + self.notify.warning('%d tried purchasing invalid item' % avId) + return ToontownGlobals.P_NotInCatalog + elif (not item.hasEmblemPrices()) and item not in av.backCatalog and item not in av.weeklyCatalog and item not in av.monthlyCatalog: + self.air.writeServerEvent('suspicious', avId, 'tried purchasing non-existing item') + self.notify.warning('%d tried purchasing non-existing item' % avId) + return ToontownGlobals.P_NotInCatalog + + if gifting and not item.isGift(): + return ToontownGlobals.P_NotAGift + + price = item.getPrice(CatalogItem.CatalogTypeBackorder if item in av.backCatalog else 0) + + if price > av.getTotalMoney() or (item.hasEmblemPrices() and not av.isEnoughEmblemsToBuy(item.getEmblemPrices())): + return ToontownGlobals.P_NotEnoughMoney + + if gifting: + return self.requestGiftAvatarOperation(avId, gifting, [context, item, price], self.attemptGiftPurchase) + else: + returnCode = self.checkPurchaseLimit(av, item) + + if returnCode != ToontownGlobals.P_ItemOnOrder: + return returnCode + + if item.getDeliveryTime(): + self.chargeAvatar(av, price, item.getEmblemPrices()) + av.addToDeliverySchedule(item, item.getDeliveryTime()) + av.addStat(ToontownGlobals.STAT_ITEMS) + else: + returnCode = item.recordPurchase(av, optional) + + if returnCode == ToontownGlobals.P_ItemAvailable: + self.chargeAvatar(av, price, item.getEmblemPrices()) + av.addStat(ToontownGlobals.STAT_ITEMS) + + return returnCode + + return None + + def attemptGiftPurchase(self, avId, targetId, avatar, optional): + av = self.air.doId2do.get(avId) + + if not av: + return + + recipient = GiftAvatar.createFromFields(avatar) + context = optional[0] + item = optional[1] + returnCode = self.checkPurchaseLimit(recipient, item) + + if returnCode != ToontownGlobals.P_ItemOnOrder: + self.sendGiftPurchaseResponse(context, avId, returnCode) + return + + self.chargeAvatar(av, optional[2], item.getEmblemPrices()) + recipient.addToGiftSchedule(avId, targetId, item, item.getDeliveryTime()) + av.addStat(ToontownGlobals.STAT_ITEMS) + + self.sendGiftPurchaseResponse(context, avId, ToontownGlobals.P_ItemOnOrder) + + def sendGiftPurchaseResponse(self, context, avId, returnCode): + if returnCode in (ToontownGlobals.P_ItemOnOrder, ToontownGlobals.P_ItemAvailable): + messenger.send('topToonsManager-event', [avId, TopToonsGlobals.CAT_CATALOG | TopToonsGlobals.CAT_GIFTS, 1]) + + self.sendUpdateToAvatarId(avId, 'requestGiftPurchaseResponse', [context, returnCode]) + + def requestPurchaseMessage(self, context, blob, optional): + av = self.__getCaller() + + if not av: + return + + returnCode = self.attemptPurchase(context, av, blob, optional) + + if returnCode in (ToontownGlobals.P_ItemOnOrder, ToontownGlobals.P_ItemAvailable): + messenger.send('topToonsManager-event', [av.doId, TopToonsGlobals.CAT_CATALOG, 1]) + + self.sendUpdateToAvatarId(av.doId, 'requestPurchaseResponse', [context, returnCode]) + + def requestGiftPurchaseMessage(self, context, targetId, blob, optional): + av = self.__getCaller() + + if not av: + return + + returnCode = self.attemptPurchase(context, av, blob, optional, gifting=targetId) + + if returnCode: + self.sendGiftPurchaseResponse(context, av.doId, returnCode) + + def requestGiftAvatar(self, doId): + self.requestGiftAvatarOperation(self.air.getAvatarIdFromSender(), doId, None, self.sendGiftAvatarResponse) + + def requestGiftAvatarOperation(self, avId, doId, optional, callback): + if avId in self.fsms: + return ToontownGlobals.P_TooFast + + loadOperation = LoadGiftAvatar(self, avId, doId, optional, callback) + loadOperation.start() + self.fsms[avId] = loadOperation + return None + + def sendGiftAvatarResponse(self, avId, targetId, avatar, optional): + self.sendUpdateToAvatarId(avId, 'setGiftAvatar', [json.dumps(avatar)]) + + def resetMovie(self, task): + self.d_setMovie(PHONE_MOVIE_CLEAR, 0) + return task.done \ No newline at end of file diff --git a/toontown/estate/DistributedPlantBase.py b/toontown/estate/DistributedPlantBase.py new file mode 100755 index 00000000..4b1803c9 --- /dev/null +++ b/toontown/estate/DistributedPlantBase.py @@ -0,0 +1,223 @@ +import DistributedLawnDecor +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.ShowBase import * +import GardenGlobals +from toontown.toonbase import TTLocalizer + +class DistributedPlantBase(DistributedLawnDecor.DistributedLawnDecor): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPlantBase') + + def __init__(self, cr): + DistributedLawnDecor.DistributedLawnDecor.__init__(self, cr) + self.model = None + self.growthLevel = -1 + self.waterTrackDict = {} + return + + def delete(self): + self.notify.debug('delete') + for waterTrack in self.waterTrackDict.values(): + if waterTrack: + waterTrack.finish() + + self.waterTrackDict = None + DistributedLawnDecor.DistributedLawnDecor.delete(self) + return + + def disable(self): + self.notify.debug('disable') + DistributedLawnDecor.DistributedLawnDecor.disable(self) + + def loadModel(self): + if hasattr(self, 'rotateNode') and self.rotateNode: + self.rotateNode.removeNode() + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = None + modelName = self.getModelName() + self.model = loader.loadModel(modelName) + self.model.reparentTo(self.rotateNode) + self.stick2Ground() + return + + def setupShadow(self): + DistributedLawnDecor.DistributedLawnDecor.setupShadow(self) + self.adjustWaterIndicator() + + def setTypeIndex(self, typeIndex): + self.typeIndex = typeIndex + self.attributes = GardenGlobals.PlantAttributes[typeIndex] + self.name = self.attributes['name'] + self.plantType = self.attributes['plantType'] + self.growthThresholds = self.attributes['growthThresholds'] + self.maxWaterLevel = self.attributes['maxWaterLevel'] + self.minWaterLevel = self.attributes['minWaterLevel'] + self.seedlingModel = self.attributes['seedlingModel'] + self.establishedModel = self.attributes['establishedModel'] + self.fullGrownModel = self.attributes['fullGrownModel'] + + def getTypeIndex(self): + return self.typeIndex + + def setWaterLevel(self, waterLevel): + self.waterLevel = waterLevel + + def getWaterLevel(self): + return self.waterLevel + + def setGrowthLevel(self, growthLevel): + self.growthLevel = growthLevel + if self.model: + self.model.setScale(growthLevel) + + def getGrowthLevel(self): + return self.growthLevel + + def getShovelAction(self): + if self.isFruiting() and not self.isWilted() and self.canBeHarvested(): + return TTLocalizer.GardeningPick + else: + return TTLocalizer.GardeningRemove + + def getShovelCommand(self): + return self.handlePicking + + def canBeHarvested(self): + return True + + def handleEnterPlot(self, colEntry = None): + dist = self.getDistance(localAvatar) + self.accept('water-plant', self.__handleWatering) + base.localAvatar.addShovelRelatedDoId(self.doId) + + def handleExitPlot(self, entry = None): + DistributedLawnDecor.DistributedLawnDecor.handleExitPlot(self, entry) + base.localAvatar.removeShovelRelatedDoId(self.doId) + self.ignore('water-plant') + + def handleWatering(self): + self.startInteraction() + self.sendUpdate('waterPlant') + + def __handleWatering(self, plantToWaterId): + if plantToWaterId == self.doId: + self.sendUpdate('waterPlant') + else: + self.notify.debug('not sending water plant') + + def isFruiting(self): + retval = self.growthLevel >= self.growthThresholds[2] + return retval + + def isGTEFruiting(self): + retval = self.growthLevel >= self.growthThresholds[2] + return retval + + def isFullGrown(self): + if self.growthLevel >= self.growthThresholds[2]: + return False + elif self.growthLevel >= self.growthThresholds[1]: + return True + return False + + def isGTEFullGrown(self): + retval = self.growthLevel >= self.growthThresholds[1] + return retval + + def isEstablished(self): + if self.growthLevel >= self.growthThresholds[2]: + return False + elif self.growthLevel >= self.growthThresholds[1]: + return False + elif self.growthLevel >= self.growthThresholds[0]: + return True + return False + + def isGTEEstablished(self): + if self.growthLevel >= self.growthThresholds[0]: + return True + return False + + def isSeedling(self): + if self.growthLevel >= self.growthThresholds[2]: + return False + elif self.growthLevel >= self.growthThresholds[1]: + return False + elif self.growthLevel >= self.growthThresholds[0]: + return False + elif self.growthLevel < self.growthThresholds[0]: + return True + return False + + def isGTESeedling(self): + return True + + def isWilted(self): + return self.waterLevel < 0 + + def getModelName(self): + if self.growthLevel >= self.growthThresholds[1]: + modelName = self.fullGrownModel + elif self.growthLevel >= self.growthThresholds[1]: + modelName = self.fullGrownModel + elif self.growthLevel >= self.growthThresholds[0]: + modelName = self.establishedModel + else: + modelName = self.seedlingModel + return modelName + + def setMovie(self, mode, avId): + if mode == GardenGlobals.MOVIE_WATER: + self.doWaterTrack(avId) + elif mode == GardenGlobals.MOVIE_FINISHPLANTING: + self.doFinishPlantingTrack(avId) + elif mode == GardenGlobals.MOVIE_REMOVE: + self.doDigupTrack(avId) + + def doWaterTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + can = toon.getWateringCanModel() + can.hide() + can.reparentTo(toon.rightHand) + track = Sequence() + track.append(self.startCamIval(avId)) + track.append(self.generateToonMoveTrack(toon)) + track.append(Func(can.show)) + track.append(self.generateWaterTrack(toon)) + track.append(Func(can.removeNode)) + track.append(self.stopCamIval(avId)) + if avId == localAvatar.doId: + track.append(Func(self.sendUpdate, 'waterPlantDone')) + track.append(Func(self.finishInteraction)) + track.start() + self.waterTrackDict[avId] = track + + def generateWaterTrack(self, toon): + sound = loader.loadSfx('phase_5/audio/sfx/firehose_spray.ogg') + sound.setPlayRate(0.75) + waterTrack = Parallel() + waterTrack.append(Sequence(Parallel(ActorInterval(toon, 'water'), SoundInterval(sound, node=toon, volume=0.5)), Func(toon.loop, 'neutral'))) + if hasattr(self, 'dropShadow') and self.dropShadow: + newColor = self.dropShadow.getColor() + alpha = min(1.0, newColor.getW() + 1 / 5.0) + newColor.setW(alpha) + waterTrack.append(LerpColorInterval(self.dropShadow, 2.1, newColor)) + return waterTrack + + def adjustWaterIndicator(self): + if self.model: + color = self.waterLevel / 5.0 + 1 / 5.0 + self.notify.debug('%s %s' % (self.waterLevel, color)) + if color < 0.2: + color = 0.2 + if hasattr(self, 'dropShadow') and self.dropShadow: + self.dropShadow.setColor(0.0, 0.0, 0.0, color) + + def canBeWatered(self): + return 1 + + def finishInteraction(self): + DistributedLawnDecor.DistributedLawnDecor.finishInteraction(self) + base.localAvatar.handleEndPlantInteraction(self) diff --git a/toontown/estate/DistributedPlantBaseAI.py b/toontown/estate/DistributedPlantBaseAI.py new file mode 100755 index 00000000..b2dcd3bf --- /dev/null +++ b/toontown/estate/DistributedPlantBaseAI.py @@ -0,0 +1,81 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.estate.DistributedLawnDecorAI import DistributedLawnDecorAI +import GardenGlobals + +class DistributedPlantBaseAI(DistributedLawnDecorAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPlantBaseAI') + + def __init__(self, mgr): + DistributedLawnDecorAI.__init__(self, mgr) + self.typeIndex = 0 + self.waterLevel = 0 + self.growthLevel = 0 + + def setTypeIndex(self, typeIndex): + self.typeIndex = typeIndex + self.attributes = GardenGlobals.PlantAttributes[typeIndex] + self.growthThresholds = self.attributes['growthThresholds'] + + def d_setTypeIndex(self, typeIndex): + self.sendUpdate('setTypeIndex', [typeIndex]) + + def b_setTypeIndex(self, typeIndex): + self.setTypeIndex(typeIndex) + self.d_setTypeIndex(typeIndex) + + def getTypeIndex(self): + return self.typeIndex + + def setWaterLevel(self, waterLevel): + self.waterLevel = waterLevel + + def d_setWaterLevel(self, waterLevel): + self.sendUpdate('setWaterLevel', [waterLevel]) + + def b_setWaterLevel(self, waterLevel): + self.setWaterLevel(waterLevel) + self.d_setWaterLevel(waterLevel) + + def getWaterLevel(self): + return self.waterLevel + + def setGrowthLevel(self, growthLevel): + self.growthLevel = growthLevel + + def d_setGrowthLevel(self, growthLevel): + self.sendUpdate('setGrowthLevel', [growthLevel]) + + def b_setGrowthLevel(self, growthLevel): + self.setGrowthLevel(growthLevel) + self.d_setGrowthLevel(growthLevel) + + def getGrowthLevel(self): + return self.growthLevel + + def waterPlant(self): + av = self.air.doId2do.get(self.air.getAvatarIdFromSender()) + if not av: + return + + level = max(1, self.getWaterLevel() + av.getWateringCan() + 1) + level = min(20, level) + self.b_setWaterLevel(level) + + self.d_setMovie(GardenGlobals.MOVIE_WATER) + self.update() + + def waterPlantDone(self): + av = self.air.doId2do.get(self.air.getAvatarIdFromSender()) + if not av: + return + + if self.waterLevel < 6: + av.b_setWateringCanSkill(av.getWateringCanSkill() + 1) + else: + av.b_setWateringCanSkill(av.getWateringCanSkill()) + + self.d_setMovie(GardenGlobals.MOVIE_CLEAR) + + def update(self): + pass + \ No newline at end of file diff --git a/toontown/estate/DistributedRewardCrate.py b/toontown/estate/DistributedRewardCrate.py new file mode 100644 index 00000000..cd291bc5 --- /dev/null +++ b/toontown/estate/DistributedRewardCrate.py @@ -0,0 +1,88 @@ +from direct.interval.IntervalGlobal import * +from toontown.effects import DustCloud +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.quest import Quests +from DistributedFurnitureItem import DistributedFurnitureItem + +class DistributedRewardCrate(DistributedFurnitureItem): + + def __init__(self, cr): + DistributedFurnitureItem.__init__(self, cr) + self.dialog = None + self.accept('exitingStoppedState', self.destroyDialog) + + def loadModel(self): + model = DistributedFurnitureItem.loadModel(self) + cSphere = CollisionSphere(0.0, 0.0, 1.0, 2.25) + cSphere.setTangible(0) + colNode = CollisionNode('Crate-%s' % self.doId) + colNode.addSolid(cSphere) + cSpherePath = model.attachNewNode(colNode) + cSpherePath.setCollideMask(ToontownGlobals.WallBitmask) + self.accept('enterCrate-%s' % self.doId, self.__enterSphere) + return model + + def disable(self): + self.ignoreAll() + dustCloud = DustCloud.DustCloud(fBillboard=0, wantSound=1) + dustCloud.setBillboardAxis(2.0) + dustCloud.setScale(0.6) + dustCloud.createTrack() + Sequence(Func(dustCloud.reparentTo, render), Func(dustCloud.setPos, self.getPos()), dustCloud.track, Func(dustCloud.detachNode), Func(dustCloud.destroy)).start() + DistributedFurnitureItem.disable(self) + + def destroyDialog(self): + if self.dialog: + self.dialog.destroy() + self.dialog = None + + def showDialog(self, text): + base.cr.playGame.getPlace().setState('stopped') + self.dialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=text, text_wordwrap=15, fadeScreen=1, command=self.__destroyDialog) + + def __destroyDialog(self, arg): + self.destroyDialog() + base.cr.playGame.getPlace().setState('walk') + + def __enterSphere(self, collisionEntry): + if base.localAvatar.doId != self.furnitureMgr.ownerId: + self.useKeyResponse(ToontownGlobals.CRATE_NOT_OWNER, 0) + return + elif not base.localAvatar.getCrateKeys(): + self.useKeyResponse(ToontownGlobals.CRATE_NO_KEYS, 0) + return + + base.cr.playGame.getPlace().setState('stopped') + self.dialog = TTDialog.TTDialog(style=TTDialog.TwoChoice, text=TTLocalizer.CrateAskToUse, fadeScreen=1, command=self.__handleDialogResponse) + + def __handleDialogResponse(self, response): + self.destroyDialog() + + if response < 0: + base.cr.playGame.getPlace().setState('walk') + return + + self.sendUpdate('requestKeyUsage') + + def useKeyResponse(self, responseCode, amount): + if responseCode == ToontownGlobals.CRATE_NOT_OWNER: + self.showDialog(TTLocalizer.CrateNotOwner) + return + elif responseCode == ToontownGlobals.CRATE_NO_KEYS: + self.showDialog(TTLocalizer.CrateNoKeys) + return + elif responseCode == ToontownGlobals.CRATE_BEANS: + self.showDialog(TTLocalizer.CrateBeanPrize % amount) + elif responseCode == ToontownGlobals.CRATE_BUFFS: + buff = Quests.RewardDict[amount] + + self.showDialog(TTLocalizer.CrateBuffPrize % buff[0](amount, buff[1:]).getString()) + elif responseCode == ToontownGlobals.CRATE_NAMETAGS: + self.showDialog(TTLocalizer.CrateNametagPrize) + elif responseCode == ToontownGlobals.CRATE_EMOTES: + self.showDialog(TTLocalizer.CrateEmotePrize) + elif responseCode == ToontownGlobals.CRATE_CLOTHING: + self.showDialog(TTLocalizer.CrateClothingPrize) + elif responseCode == ToontownGlobals.CRATE_ACCESSORIES: + self.showDialog(TTLocalizer.CrateAccessoryPrize) \ No newline at end of file diff --git a/toontown/estate/DistributedRewardCrateAI.py b/toontown/estate/DistributedRewardCrateAI.py new file mode 100644 index 00000000..8a173612 --- /dev/null +++ b/toontown/estate/DistributedRewardCrateAI.py @@ -0,0 +1,109 @@ +from toontown.catalog import CatalogAccessoryItem, CatalogClothingItem, CatalogNametagItem, CatalogEmoteItem +from toontown.catalog.CatalogAccessoryItemGlobals import * +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.toon import ToonDNA +from toontown.quest import Quests +from DistributedFurnitureItemAI import DistributedFurnitureItemAI +import random, time + +RANDOM_PRIZES = [ToontownGlobals.CRATE_BEANS] * 10 + [ToontownGlobals.CRATE_BUFFS] * 5 + [ToontownGlobals.CRATE_NAMETAGS] * 10 + [ToontownGlobals.CRATE_EMOTES] * 10 + [ToontownGlobals.CRATE_CLOTHING] * 30 + [ToontownGlobals.CRATE_ACCESSORIES] * 35 + +class DistributedRewardCrateAI(DistributedFurnitureItemAI): + + def requestKeyUsage(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + + if not av: + return + + if avId != self.furnitureMgr.ownerId: + self.sendUpdateToAvatarId(avId, 'useKeyResponse', [ToontownGlobals.CRATE_NOT_OWNER, 0]) + return + elif not av.getCrateKeys(): + self.sendUpdateToAvatarId(avId, 'useKeyResponse', [ToontownGlobals.CRATE_NO_KEYS, 0]) + return + + av.removeCrateKeys(1) + self.choosePrize(av) + + if not config.GetBool('dont-destroy-crate', False): + self.furnitureMgr.deleteItemFromRoom(self.doId, False) + + def choosePrize(self, av, tryNumber = 0): + if tryNumber == 10: + self.giveBeans(av) + return + + prizeType = random.choice(RANDOM_PRIZES) + + if prizeType == ToontownGlobals.CRATE_BEANS: + self.giveBeans(av) + elif prizeType == ToontownGlobals.CRATE_BUFFS: + buffId = random.choice(Quests.BuffRewardIds) + buff = Quests.RewardDict[buffId] + + buff[0](buffId, buff[1:]).sendRewardAI(av) + self.sendUpdateToAvatarId(av.doId, 'useKeyResponse', [ToontownGlobals.CRATE_BUFFS, buffId]) + elif prizeType == ToontownGlobals.CRATE_NAMETAGS: + allNametags = xrange(len(TTLocalizer.NametagFonts)) + playerNametags = av.nametagStyles + remainingNametags = [nametag for nametag in allNametags if nametag not in playerNametags] + + if not remainingNametags: + self.choosePrize(av, tryNumber + 1) + return + + nametag = random.choice(remainingNametags) + item = CatalogNametagItem.CatalogNametagItem(nametag, 0) + + if item.reachedPurchaseLimit(av): + return + + av.addToDeliverySchedule(item) + self.sendUpdateToAvatarId(av.doId, 'useKeyResponse', [ToontownGlobals.CRATE_NAMETAGS, 0]) + elif prizeType == ToontownGlobals.CRATE_EMOTES: + playerEmotes = av.emoteAccess + remainingEmotes = [i for i, access in enumerate(playerEmotes) if (not access) and access not in (17, 18, 19)] + + if not remainingEmotes: + self.choosePrize(av, tryNumber + 1) + return + + emote = random.choice(remainingEmotes) + item = CatalogEmoteItem.CatalogEmoteItem(emote, 0) + + if item.reachedPurchaseLimit(av): + self.choosePrize(av, tryNumber + 1) + return + + av.addToDeliverySchedule(item) + self.sendUpdateToAvatarId(av.doId, 'useKeyResponse', [ToontownGlobals.CRATE_EMOTES, 0]) + elif prizeType == ToontownGlobals.CRATE_CLOTHING: + clothing = CatalogClothingItem.ClothingTypes.keys() + random.shuffle(clothing) + + for id in clothing: + item = CatalogClothingItem.CatalogClothingItem(id, 0) + + if not item.notOfferedTo(av) and not item.reachedPurchaseLimit(av): + av.addToDeliverySchedule(item) + self.sendUpdateToAvatarId(av.doId, 'useKeyResponse', [ToontownGlobals.CRATE_CLOTHING, 0]) + return + elif prizeType == ToontownGlobals.CRATE_ACCESSORIES: + accessories = AccessoryTypes.keys() + random.shuffle(accessories) + + for id in accessories: + item = CatalogAccessoryItem.CatalogAccessoryItem(id, 0) + + if not item.reachedPurchaseLimit(av): + av.addToDeliverySchedule(item) + self.sendUpdateToAvatarId(av.doId, 'useKeyResponse', [ToontownGlobals.CRATE_ACCESSORIES, 0]) + return + + def giveBeans(self, av): + beans = random.randint(1, 15) * 100 + + av.addMoney(beans) + self.sendUpdateToAvatarId(av.doId, 'useKeyResponse', [ToontownGlobals.CRATE_BEANS, beans]) \ No newline at end of file diff --git a/toontown/estate/DistributedStatuary.py b/toontown/estate/DistributedStatuary.py new file mode 100755 index 00000000..4d9680b1 --- /dev/null +++ b/toontown/estate/DistributedStatuary.py @@ -0,0 +1,135 @@ +import DistributedLawnDecor +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.ShowBase import * +import GardenGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from pandac.PandaModules import NodePath +from pandac.PandaModules import Point3 + +class DistributedStatuary(DistributedLawnDecor.DistributedLawnDecor): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStatuary') + + def __init__(self, cr): + self.notify.debug('constructing DistributedStatuary') + DistributedLawnDecor.DistributedLawnDecor.__init__(self, cr) + self.confirmDialog = None + self.resultDialog = None + return + + def loadModel(self): + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = loader.loadModel(self.modelPath) + colNode = self.model.find('**/+CollisionNode') + if not colNode.isEmpty(): + score, multiplier = ToontownGlobals.PinballScoring[ToontownGlobals.PinballStatuary] + if self.pinballScore: + score = self.pinballScore[0] + multiplier = self.pinballScore[1] + scoreNodePath = NodePath('statuary-%d-%d' % (score, multiplier)) + colNode.setName('statuaryCol') + scoreNodePath.reparentTo(colNode.getParent()) + colNode.reparentTo(scoreNodePath) + self.model.setScale(self.worldScale) + self.model.reparentTo(self.rotateNode) + attrib = GardenGlobals.PlantAttributes[self.typeIndex] + self.stick2Ground() + + def setTypeIndex(self, typeIndex): + self.typeIndex = typeIndex + self.name = GardenGlobals.PlantAttributes[typeIndex]['name'] + self.plantType = GardenGlobals.PlantAttributes[typeIndex]['plantType'] + self.modelPath = GardenGlobals.PlantAttributes[typeIndex]['model'] + self.pinballScore = None + if 'pinballScore' in GardenGlobals.PlantAttributes[typeIndex]: + self.pinballScore = GardenGlobals.PlantAttributes[typeIndex]['pinballScore'] + self.worldScale = 1.0 + if 'worldScale' in GardenGlobals.PlantAttributes[typeIndex]: + self.worldScale = GardenGlobals.PlantAttributes[typeIndex]['worldScale'] + return + + def getTypeIndex(self): + return self.typeIndex + + def setWaterLevel(self, waterLevel): + self.waterLevel = waterLevel + + def getWaterLevel(self): + return self.waterLevel + + def setGrowthLevel(self, growthLevel): + self.growthLevel = growthLevel + + def getGrowthLevel(self): + return self.growthLevel + + def setupCollision(self): + DistributedLawnDecor.DistributedLawnDecor.setupCollision(self) + minPt = Point3(0, 0, 0) + maxPt = Point3(0, 0, 0) + self.model.calcTightBounds(minPt, maxPt) + self.notify.debug('max=%s min=%s' % (maxPt, minPt)) + xDiff = maxPt[0] - minPt[0] + yDiff = maxPt[1] - minPt[1] + radius = (xDiff * xDiff + yDiff * yDiff) ** 0.5 + radius /= 3 + self.notify.debug('xDiff=%s yDiff=%s radius = %s' % (xDiff, yDiff, radius)) + self.colSphereNode.setScale(radius) + + def getShovelCommand(self): + return self.handlePicking + + def getShovelAction(self): + return TTLocalizer.GardeningRemove + + def handleEnterPlot(self, colEntry = None): + if self.canBePicked(): + self.notify.debug('entering if') + base.localAvatar.addShovelRelatedDoId(self.doId) + base.localAvatar.setShovelAbility(TTLocalizer.GardeningRemove) + else: + self.notify.debug('entering else') + + def handlePicking(self): + fullName = self.name + messenger.send('wakeup') + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.ConfirmRemoveStatuary % {'item': fullName}, command=self.confirmCallback) + self.confirmDialog.show() + base.cr.playGame.getPlace().detectedGardenPlotUse() + + def confirmCallback(self, value): + self.notify.debug('value=%d' % value) + if self.confirmDialog: + self.confirmDialog.destroy() + self.confirmDialog = None + if value > 0: + self.doPicking() + else: + base.cr.playGame.getPlace().detectedGardenPlotDone() + return + + def doPicking(self): + if not self.canBePicked(): + self.notify.debug("I don't own this flower, just returning") + return + self.handleRemove() + + def handleExitPlot(self, entry = None): + DistributedLawnDecor.DistributedLawnDecor.handleExitPlot(self, entry) + base.localAvatar.removeShovelRelatedDoId(self.doId) + + def doResultDialog(self): + self.startInteraction() + itemName = GardenGlobals.PlantAttributes[self.typeIndex]['name'] + stringToShow = TTLocalizer.getResultPlantedSomethingSentence(itemName) + self.resultDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=stringToShow, command=self.resultsCallback) + + def resultsCallback(self, value): + self.notify.debug('value=%d' % value) + if self.resultDialog: + self.resultDialog.destroy() + self.resultDialog = None + self.finishInteraction() + return diff --git a/toontown/estate/DistributedStatuaryAI.py b/toontown/estate/DistributedStatuaryAI.py new file mode 100755 index 00000000..20f24acd --- /dev/null +++ b/toontown/estate/DistributedStatuaryAI.py @@ -0,0 +1,66 @@ +from direct.directnotify import DirectNotifyGlobal +from DistributedLawnDecorAI import DistributedLawnDecorAI + +import GardenGlobals +import time + +FOUR_DAYS = 86400 * 4 + +class DistributedStatuaryAI(DistributedLawnDecorAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedStatuaryAI") + + def calculate(self, lastCheck): + self.attributes = GardenGlobals.PlantAttributes[self.index] + self.growthThresholds = self.attributes.get('growthThresholds', (0, 0)) + + now = int(time.time()) + self.lastCheck = lastCheck + if self.lastCheck == 0: + self.lastCheck = now + + self.growthLevel = min((now - self.lastCheck) // FOUR_DAYS, self.growthThresholds[-1] + 1) + self.update() + + def getTypeIndex(self): + return self.index + + def getWaterLevel(self): + return 1 + + def getGrowthLevel(self): + return self.growthLevel + + def getOptional(self): + return self.data + + def update(self): + self.mgr.data['statuary'] = self.mgr.S_pack(self.data, self.lastCheck, self.index, self.growthLevel) + self.mgr.update() + + def removeItem(self): + avId = self.air.getAvatarIdFromSender() + self.d_setMovie(GardenGlobals.MOVIE_REMOVE) + + def _remove(task): + if not self.air: + return + + plot = self.mgr.placePlot(-1) + plot.setPlot(self.plot) + plot.setPos(self.getPos()) + plot.setH(self.getH()) + plot.setOwnerIndex(self.ownerIndex) + plot.generateWithRequired(self.zoneId) + + self.air.writeServerEvent('remove-statuary', avId, plot=self.plot) + self.requestDelete() + + self.mgr.objects.remove(self) + + self.mgr.data['statuary'] = 0 + self.mgr.update() + + return task.done + + taskMgr.doMethodLater(7, _remove, self.uniqueName('do-remove')) + \ No newline at end of file diff --git a/toontown/estate/DistributedTV.py b/toontown/estate/DistributedTV.py new file mode 100644 index 00000000..c15be8d1 --- /dev/null +++ b/toontown/estate/DistributedTV.py @@ -0,0 +1,144 @@ +from direct.gui.DirectGui import * +from otp.otpbase import OTPLocalizer +from toontown.catalog import CatalogFurnitureItem +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.toontowngui import TTDialog +from DistributedFurnitureItem import DistributedFurnitureItem +import glob, ntpath, os, time + +class DistributedTV(DistributedFurnitureItem): + + def __init__(self, cr): + DistributedFurnitureItem.__init__(self, cr) + self.dialog = None + self.screen = None + self.sound = None + self.accept('exitingStoppedState', self.destroyGui) + + def loadModel(self, animate=1): + model = DistributedFurnitureItem.loadModel(self) + + if animate: + pos = CatalogFurnitureItem.TvToPosScale[self.item.furnitureType] + self.screen = NodePath(CardMaker('tv-screen').generate()) + + model.find('**/toonTownBugTV_screen').hide() + self.screen.reparentTo(model) + self.screen.setScale(*pos[1]) + self.screen.setPos(*pos[0]) + self.resetScreen() + + cSphere = CollisionSphere(0.0, -1.5, 1.0, 1.575) + cSphere.setTangible(0) + colNode = CollisionNode('TV-%s' % self.doId) + colNode.addSolid(cSphere) + cSpherePath = model.attachNewNode(colNode) + cSpherePath.setCollideMask(ToontownGlobals.WallBitmask) + self.accept('enterTV-%s' % self.doId, self.__enterSphere) + return model + + def disable(self): + self.ignoreAll() + self.destroyGui() + self.destroySound() + DistributedFurnitureItem.disable(self) + + def setVideo(self, video, time): + if (not video) or (not time): + return + + self.destroySound() + self.startVideo(os.path.join('user', os.path.join('videos', video)), time) + + def getPack(self, name): + for pack in TTLocalizer.TVPacks: + if pack.lower() in name: + return pack + + return None + + def destroySound(self): + if self.sound: + self.sound.stop() + self.sound = None + + def destroyGui(self, arg=None): + if self.dialog: + self.dialog.destroy() + self.dialog = None + + def destroyGuiAndWalk(self, arg=None): + self.destroyGui() + base.cr.playGame.getPlace().setState('walk') + + def cutOff(self, string): + return string if len(string) < 24 else '%s...' % string[:24] + + def resetScreen(self): + self.screen.setTextureOff(TextureStage.getDefault()) + self.screen.setColor(0.3, 0.3, 0.3, 1.0) + + def startVideo(self, video, startTime): + if not os.path.exists(video): + pack = self.getPack(video) + base.localAvatar.setSystemMessage(0, TTLocalizer.TVUnknownVideoPack % pack if pack else TTLocalizer.TVUnknownVideo) + self.resetScreen() + return + + start = time.time() - startTime + movie = loader.loadTexture(video) + self.sound = loader.loadSfx(video) + length = self.sound.length() + + if start >= length: + start -= int(start / length) * length + + movie.synchronizeTo(self.sound) + self.screen.setColor(1, 1, 1, 1) + self.screen.setTexture(movie) + self.screen.setTexScale(TextureStage.getDefault(), movie.getTexScale()) + self.sound.setTime(start) + self.sound.setLoop(True) + self.sound.play() + + def __enterSphere(self, collisionEntry): + if base.localAvatar.doId != self.furnitureMgr.ownerId: + return + + videos = [] + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + buttonImage = (gui.find('**/FndsLst_ScrollUp'), gui.find('**/FndsLst_ScrollDN'), gui.find('**/FndsLst_ScrollUp_Rllvr'), gui.find('**/FndsLst_ScrollUp')) + base.cr.playGame.getPlace().setState('stopped') + self.dialog = DirectFrame(relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.33, 1, 1.4), + pos=(0, 0, 0), text=TTLocalizer.TVChooseVideo, text_scale=0.07, text_pos=(0, 0.575)) + + for file in sorted(glob.glob('user/videos/*.mp4')): + filename = ntpath.basename(file) + videos.append(DirectButton(relief=None, text=self.cutOff(filename[:-4]), text_pos=(0.0, -0.0225), text_scale=0.048, text_align=TextNode.ALeft, text_fg=(0, 0, 0, 1), text3_fg=(0.4, 0.8, 0.4, 1), text1_bg=(0.5, 0.9, 1, 1), text2_bg=(1, 1, 0, 1), command=self.chooseVideo, extraArgs=[filename])) + + scrollList = DirectScrolledList(parent=self.dialog, relief=None, pos=(-0.05, 0, 0), incButton_image=buttonImage, incButton_relief=None, incButton_scale=(1.3, 1.3, -1.3), + incButton_pos=(0.045, 0, -0.4), incButton_image3_color=(1, 1, 1, 0.2), decButton_image=buttonImage, decButton_relief=None, + decButton_scale=1.3, decButton_pos=(0.045, 0, 0.5), decButton_image3_color=(1, 1, 1, 0.2), itemFrame_pos=(-0.247, 0, 0.365), + itemFrame_scale=1.0, itemFrame_relief=DGG.SUNKEN, itemFrame_frameSize=(-0.02, 0.6, -0.7, 0.08), itemFrame_frameColor=(0.85, 0.95, 1, 1), + itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=10, forceHeight=0.065, items=videos) + cancelButton = DirectButton(parent=self.dialog, relief=None, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), pos=(0, 0, -0.55), text=OTPLocalizer.lCancel, text_scale=0.06, text_pos=(0, -0.1), command=self.destroyGuiAndWalk) + + gui.removeNode() + buttons.removeNode() + + def chooseVideo(self, video): + self.destroyGuiAndWalk() + self.sendUpdate('requestVideo', [video]) + + def showDialog(self, text): + base.cr.playGame.getPlace().setState('stopped') + self.dialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=text, text_wordwrap=15, fadeScreen=1, command=self.destroyGuiAndWalk) + + def requestVideoResponse(self, response): + if response == ToontownGlobals.TV_NOT_OWNER: + self.showDialog(TTLocalizer.TVNotOwner) + elif response == ToontownGlobals.TV_INVALID_VIDEO: + self.showDialog(TTLocalizer.TVInvalidVideo) + elif response == ToontownGlobals.TV_OK: + self.showDialog(TTLocalizer.TVOK) \ No newline at end of file diff --git a/toontown/estate/DistributedTVAI.py b/toontown/estate/DistributedTVAI.py new file mode 100644 index 00000000..0f287b64 --- /dev/null +++ b/toontown/estate/DistributedTVAI.py @@ -0,0 +1,32 @@ +from toontown.toonbase import ToontownGlobals +from DistributedFurnitureItemAI import DistributedFurnitureItemAI +import time + +class DistributedTVAI(DistributedFurnitureItemAI): + + def __init__(self, air, furnitureMgr, item): + DistributedFurnitureItemAI.__init__(self, air, furnitureMgr, item) + self.video = ['', 0] + + def d_setVideo(self, video): + self.sendUpdate('setVideo', video) + + def getVideo(self): + return self.video + + def requestVideo(self, video): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + + if not av: + return + elif self.furnitureMgr.ownerId != avId: + self.sendUpdateToAvatarId(avId, 'requestVideoResponse', [ToontownGlobals.TV_NOT_OWNER]) + return + elif not video.endswith('.mp4'): + self.sendUpdateToAvatarId(avId, 'requestVideoResponse', [ToontownGlobals.TV_INVALID_VIDEO]) + return + + self.video = [video, int(time.time())] + self.d_setVideo(self.video) + self.sendUpdateToAvatarId(avId, 'requestVideoResponse', [ToontownGlobals.TV_OK]) \ No newline at end of file diff --git a/toontown/estate/DistributedTarget.py b/toontown/estate/DistributedTarget.py new file mode 100755 index 00000000..d6b36573 --- /dev/null +++ b/toontown/estate/DistributedTarget.py @@ -0,0 +1,245 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase import ToontownTimer +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer + +class DistributedTarget(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTarget') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.geom = None + self.numConsecutiveHits = 0 + self.enabled = 0 + self.score = 0 + self.hitTime = 0 + self.targetBounceTrack = None + self.pinballInfo = {} + self.pinballHiScore = 0 + self.pinballHiScorer = '' + self.onscreenMessage = None + self.fadeTrack = None + return + + def disable(self): + self.ignoreAll() + DistributedObject.DistributedObject.disable(self) + if self.targetBounceTrack: + self.targetBounceTrack.finish() + self.targetBounceTrack = None + if self.fadeTrack: + self.fadeTrack.pause() + self.fadeTrack = None + self.__clearOnscreenMessage() + return + + def generateInit(self): + DistributedObject.DistributedObject.generateInit(self) + self.load() + + def load(self): + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(base.a2dBottomRight) + self.timer.setPos(-0.233, 0, 0.85) + self.timer.hide() + self.geom = loader.loadModel('phase_5.5/models/estate/target') + self.geom.reparentTo(base.cr.playGame.hood.loader.geom) + self.geom.setPos(0, 0, 40) + self.geom.setScale(3) + self.geom.stash() + self.hitSound = base.loadSfx('phase_4/audio/sfx/MG_Tag_A.ogg') + self.rewardSound = base.loadSfx('phase_4/audio/sfx/MG_pos_buzzer.ogg') + self.scoreText = TextNode('scoreText') + self.scoreText.setTextColor(1, 0, 0, 1) + self.scoreText.setAlign(self.scoreText.ACenter) + self.scoreText.setFont(getSignFont()) + self.scoreText.setText('0') + self.scoreNode = self.timer.attachNewNode(self.scoreText) + self.scoreNode.setPos(0, 0, 0.35) + self.scoreNode.setScale(0.25) + self.curPinballScoreText = TextNode('pinballScoreText') + self.curPinballScoreText.setTextColor(1, 0, 0, 1) + self.curPinballScoreText.setAlign(self.scoreText.ACenter) + self.curPinballScoreText.setFont(getSignFont()) + self.curPinballScoreText.setText('') + self.curPinballScoreNode = render.attachNewNode(self.curPinballScoreText) + self.curPinballScoreNode.setPos(0.5, 0.5, 0.3) + self.curPinballScoreNode.setScale(0.25) + colSphere = CollisionSphere(0, 0, 0, 3.5) + colSphere.setTangible(0) + colNode = CollisionNode('targetSphere') + colNode.addSolid(colSphere) + colSphereNode = self.geom.attachNewNode(colNode) + self.accept('hitTarget', self.handleHitTarget) + self.accept('missedTarget', self.handleMissedTarget) + self.accept('entertargetSphere', self.handleEnterTarget) + + def delete(self): + self.ignoreAll() + self.scoreNode.removeNode() + del self.scoreNode + self.curPinballScoreNode.removeNode() + del self.curPinballScoreNode + self.geom.removeNode() + del self.geom + self.timer.destroy() + del self.timer + del self.rewardSound + del self.hitSound + DistributedObject.DistributedObject.delete(self) + + def setState(self, enabled, score, time): + if self.enabled != enabled: + if self.fadeTrack: + self.fadeTrack.pause() + if enabled: + self.fadeTrack = Sequence(Func(base.localAvatar.setSystemMessage, 0, TTLocalizer.EstateTargetGameStart), Func(self.geom.unstash), self.geom.colorScaleInterval(1.0, Vec4(1.0, 1.0, 1.0, 1.0)), Wait(1), Func(base.localAvatar.setSystemMessage, 0, TTLocalizer.EstateTargetGameInst)) + else: + self.fadeTrack = Sequence(self.geom.colorScaleInterval(1.0, Vec4(1.0, 1.0, 1.0, 0.0)), Func(self.geom.stash), Func(self.hideTimer), Func(base.localAvatar.setSystemMessage, 0, TTLocalizer.EstateTargetGameEnd)) + self.fadeTrack.start() + self.enabled = enabled + if score != self.score: + self.setLevel(score) + if time != self.hitTime: + self.setTimer(time) + + def setReward(self, reward): + base.playSfx(self.rewardSound) + + def handleEnterTarget(self, collEntry): + self.handleHitTarget() + + def handleHitTarget(self, avId = None, vel = None): + if not avId: + avId = base.localAvatar.doId + if self.enabled: + self.sendUpdate('setResult', [avId]) + if vel: + if self.targetBounceTrack: + self.targetBounceTrack.finish() + pos = self.geom.getPos() + dist = Vec3(vel) + dist.normalize() + newPos = pos - dist * 1.5 + springPos = pos + dist + self.notify.debug('reaction distance = %s,%s,%s' % (vel[0], vel[1], vel[2])) + self.targetBounceTrack = Sequence(LerpPosInterval(self.geom, duration=0.1, pos=newPos, blendType='easeOut'), LerpPosInterval(self.geom, duration=0.25, pos=springPos, blendType='easeOut'), LerpPosInterval(self.geom, duration=0.2, pos=pos, blendType='easeOut')) + self.targetBounceTrack.start() + + def handleMissedTarget(self): + if self.enabled: + self.sendUpdate('setResult', [0]) + + def handleHitCloud(self): + if self.enabled: + self.sendUpdate('setBonus', [0.5]) + + def setLevel(self, level): + self.notify.debug('setLevel(%s)' % level) + self.score = level + base.playSfx(self.hitSound) + self.scoreText.setText('+' + str(int(self.score))) + + def setTimer(self, time): + self.hitTime = time + self.notify.debug('updateTimer(%s)' % self.enabled) + if self.enabled: + self.showTimer() + self.notify.debug('hitTime = %s' % self.hitTime) + self.timer.setTime(self.hitTime) + self.timer.countdown(self.hitTime) + + def showTimer(self): + if base.localAvatar.animFSM.getCurrentState().getName() != 'ReadBook': + base.setCellsAvailable([base.rightCells[0]], 0) + self.timer.show() + + def hideTimer(self): + self.timer.hide() + base.setCellsAvailable([base.rightCells[0]], 1) + + def setPosition(self, x, y, z): + self.geom.setPos(x, y, z) + + def showScore(self): + scoreName = self.pinballHiScorer[0:12] + if scoreName: + if len(self.pinballHiScorer) > 12: + scoreName += TTLocalizer.PinballHiScoreAbbrev + titleText = TTLocalizer.PinballHiScore % scoreName + scoreText = TTLocalizer.PinballScoreHolder % self.pinballHiScore + pinballEntry = self.pinballInfo.get(base.localAvatar.doId) + if pinballEntry: + titleText += TTLocalizer.PinballYourBestScore + scoreText += TTLocalizer.PinballScoreHolder % pinballEntry[0] + titleText += TTLocalizer.PinballScore % (pinballEntry[1], pinballEntry[2]) + scoreText += TTLocalizer.PinballScoreHolder % (pinballEntry[1] * pinballEntry[2]) + self.__showOnscreenMessage(titleText, scoreText) + + def setCurPinballScore(self, avId, score, multiplier): + self.notify.debug('setCurPinballScore %d %d %d' % (avId, score, multiplier)) + if self.pinballInfo.get(avId) == None: + self.pinballInfo[avId] = [0, 0, 0] + pinballEntry = self.pinballInfo[avId] + pinballEntry[1] = score + pinballEntry[2] = multiplier + curScore = score * multiplier + if curScore > pinballEntry[0]: + pinballEntry[0] = curScore + if curScore > self.pinballHiScore: + self.pinballHiScore = pinballEntry[0] + toon = base.cr.doId2do.get(avId) + if toon: + self.pinballHiScorer = toon.getName() + self.showScore() + return + + def b_setCurPinballScore(self, avId, score, multiplier): + self.setCurPinballScore(avId, score, multiplier) + self.sendUpdate('setCurPinballScore', [avId, score, multiplier]) + + def __showOnscreenMessage(self, titleText, scoreText): + self.notify.debug('----- __showOnscreenmessage') + if not self.onscreenMessage: + self.onscreenMessage = DirectFrame(relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(12, 1, 3), pos=(0, 0, 0.8), scale=0.1) + titles = DirectLabel(parent=self.onscreenMessage, relief=None, text=titleText, text_fg=VBase4(0, 0, 0, 1), text_align=TextNode.ALeft, text_scale=0.7, pos=(-5.75, 0, 0.5)) + scores = DirectLabel(parent=self.onscreenMessage, relief=None, text=scoreText, text_fg=VBase4(1, 0, 0, 1), text_align=TextNode.ARight, text_scale=0.7, pos=(5.75, 0, 0.5)) + self.onscreenMessage.titles = titles + self.onscreenMessage.scores = scores + else: + self.onscreenMessage.titles['text'] = titleText + self.onscreenMessage.scores['text'] = scoreText + base.foobar = self.onscreenMessage + return + + def __clearOnscreenMessage(self): + self.notify.debug('----- __clearOnscreenMessage') + if self.onscreenMessage: + self.onscreenMessage.destroy() + self.onscreenMessage = None + return + + def setPinballHiScore(self, score): + self.pinballHiScore = score + self.showScore() + + def setPinballHiScorer(self, name): + self.pinballHiScorer = name + self.showScore() + + def hideGui(self): + if self.timer: + self.hideTimer() + if self.onscreenMessage: + self.onscreenMessage.hide() + + def showGui(self): + if self.timer: + if self.enabled: + self.showTimer() + if self.onscreenMessage: + self.onscreenMessage.show() diff --git a/toontown/estate/DistributedTargetAI.py b/toontown/estate/DistributedTargetAI.py new file mode 100755 index 00000000..f2d7dba8 --- /dev/null +++ b/toontown/estate/DistributedTargetAI.py @@ -0,0 +1,85 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +import CannonGlobals + +class DistributedTargetAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedTargetAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.enabled = 0 + self.highscore = 0 + self.scoreDict = {} + + self.__newGame() + + def announceGenerate(self): + DistributedObjectAI.announceGenerate(self) + taskMgr.doMethodLater(10, self.__startNewGame, self.taskName('newGame')) + + def __newGame(self): + self.power = 1 + self.time = CannonGlobals.CANNON_TIMEOUT + + def getPosition(self): + return (0, 0, 40) + + def getState(self): + return self.enabled, 2**self.power, self.time + + def d_updateState(self): + self.sendUpdate("setState", self.getState()) + + def d_setReward(self, reward): + self.sendUpdate("setReward", [reward]) + + def setResult(self, avId): + if avId and self.enabled: + self.power += 1 + self.time = int(CannonGlobals.CANNON_TIMEOUT / self.power) + taskMgr.remove(self.taskName('gameover')) + taskMgr.doMethodLater(self.time, self.__gameOver, self.taskName('gameover')) + self.d_updateState() + + def __gameOver(self, task): + self.enabled = 0 + self.time = 0 + self.d_updateState() + taskMgr.doMethodLater(10, self.__startNewGame, self.taskName('newGame')) + + for avId in self.scoreDict: + av = self.air.doId2do.get(avId) + if av: + if av.zoneId == self.zoneId: + av.toonUp(2 ** self.power) + + return task.done + + def __startNewGame(self, task): + self.enabled = 1 + self.__newGame() + self.d_updateState() + taskMgr.doMethodLater(self.time, self.__gameOver, self.taskName('gameover')) + return task.done + + def setBonus(self, bonus): + pass + + def setCurPinballScore(self, avId, score, bonus): + av = self.air.doId2do.get(avId) + if not av: + return + + S = score * bonus + self.scoreDict[avId] = S + if S > self.highscore: + self.highscore = S + self.d_updateHighscore(av, S) + + def d_updateHighscore(self, av, score): + self.sendUpdate("setPinballHiScorer", [av.getName()]) + self.sendUpdate("setPinballHiScore", [score]) + + def delete(self): + taskMgr.remove(self.taskName('newGame')) + taskMgr.remove(self.taskName('gameover')) diff --git a/toontown/estate/DistributedToonStatuary.py b/toontown/estate/DistributedToonStatuary.py new file mode 100755 index 00000000..3926a79e --- /dev/null +++ b/toontown/estate/DistributedToonStatuary.py @@ -0,0 +1,224 @@ +from toontown.estate import DistributedStatuary +from toontown.estate import DistributedLawnDecor +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.ShowBase import * +from pandac.PandaModules import * +from toontown.toon import Toon +from toontown.toon import ToonDNA +import GardenGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import NodePath +from pandac.PandaModules import Point3 + +def dnaCodeFromToonDNA(dna): + + def findItemNumInList(wantItem, wantList): + i = 0 + for item in wantList: + if item == wantItem: + break + i += 1 + + return i + + if dna.gender == 'f': + genderTypeNum = 0 + else: + genderTypeNum = 1 + legTypeNum = findItemNumInList(dna.legs, ToonDNA.toonLegTypes) << 1 + torsoTypeNum = findItemNumInList(dna.torso, ToonDNA.toonTorsoTypes) << 3 + headTypeNum = findItemNumInList(dna.head, ToonDNA.toonHeadTypes) << 7 + return headTypeNum | torsoTypeNum | legTypeNum | genderTypeNum + + +class DistributedToonStatuary(DistributedStatuary.DistributedStatuary): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedToonStatuary') + + def __init__(self, cr): + self.notify.debug('constructing DistributedToonStatuary') + DistributedStatuary.DistributedStatuary.__init__(self, cr) + self.toon = None + return + + def loadModel(self): + DistributedStatuary.DistributedStatuary.loadModel(self) + self.model.setScale(self.worldScale * 1.5, self.worldScale * 1.5, self.worldScale) + self.getToonPropertiesFromOptional() + dna = ToonDNA.ToonDNA() + dna.newToonFromProperties(self.headType, self.torsoType, self.legType, self.gender, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + self.setupStoneToon(dna) + self.poseToonFromTypeIndex(self.typeIndex) + self.toon.reparentTo(self.model) + + def delete(self): + self.deleteToon() + DistributedStatuary.DistributedStatuary.delete(self) + + def setupStoneToon(self, dna): + self.toon = Toon.Toon() + self.toon.setPos(0, 0, 0) + self.toon.setDNA(dna) + self.toon.initializeBodyCollisions('toonStatue') + self.toon.stopBlink() + self.toon.stopLookAround() + self.gender = self.toon.style.gender + self.speciesType = self.toon.style.getAnimal() + self.headType = self.toon.style.head + self.removeTextures() + self.setStoneTexture() + self.toon.dropShadow.hide() + self.toon.setZ(70) + self.toon.setScale(20 / 1.5, 20 / 1.5, 20) + + def deleteToon(self): + self.notify.debug('entering deleteToon') + self.toon.delete() + self.toon = None + return + + def copyLocalAvatarToon(self): + self.toon = Toon.Toon() + self.toon.reparentTo(render) + self.toon.setDNA(base.localAvatar.style) + self.toon.setPos(base.localAvatar, 0, 0, 0) + self.toon.pose('victory', 30) + self.toon.setH(180) + self.speciesType = self.toon.style.getAnimal() + self.gender = self.toon.style.gender + + def setupCollision(self): + DistributedStatuary.DistributedStatuary.setupCollision(self) + self.colSphereNode.setScale(self.colSphereNode.getScale() * 1.5) + + def setupShadow(self): + pass + + def removeTextures(self): + for node in self.toon.findAllMatches('**/*'): + node.setState(RenderState.makeEmpty()) + + desatShirtTex = loader.loadTexture('phase_3/maps/desat_shirt_1.jpg') + desatSleeveTex = loader.loadTexture('phase_3/maps/desat_sleeve_1.jpg') + desatShortsTex = loader.loadTexture('phase_3/maps/desat_shorts_1.jpg') + desatSkirtTex = loader.loadTexture('phase_3/maps/desat_skirt_1.jpg') + if self.toon.hasLOD(): + for lodName in self.toon.getLODNames(): + torso = self.toon.getPart('torso', lodName) + torsoTop = torso.find('**/torso-top') + if torsoTop: + torsoTop.setTexture(desatShirtTex, 1) + sleeves = torso.find('**/sleeves') + if sleeves: + sleeves.setTexture(desatSleeveTex, 1) + bottoms = torso.findAllMatches('**/torso-bot*') + for bottomNum in xrange(0, bottoms.getNumPaths()): + bottom = bottoms.getPath(bottomNum) + if bottom: + if self.toon.style.torso[1] == 's': + bottom.setTexture(desatShortsTex, 1) + else: + bottom.setTexture(desatSkirtTex, 1) + + def setStoneTexture(self): + gray = VBase4(1.6, 1.6, 1.6, 1) + self.toon.setColor(gray, 10) + stoneTex = loader.loadTexture('phase_5.5/maps/smoothwall_1.jpg') + ts = TextureStage('ts') + ts.setPriority(1) + self.toon.setTexture(ts, stoneTex) + tsDetail = TextureStage('tsDetail') + tsDetail.setPriority(2) + tsDetail.setSort(10) + tsDetail.setCombineRgb(tsDetail.CMInterpolate, tsDetail.CSTexture, tsDetail.COSrcColor, tsDetail.CSPrevious, tsDetail.COSrcColor, tsDetail.CSConstant, tsDetail.COSrcColor) + tsDetail.setColor(VBase4(0.5, 0.5, 0.5, 1)) + if self.toon.hasLOD(): + for lodName in self.toon.getLODNames(): + head = self.toon.getPart('head', lodName) + eyes = head.find('**/eye*') + if not eyes.isEmpty(): + eyes.setColor(Vec4(1.4, 1.4, 1.4, 0.3), 10) + ears = head.find('**/ears*') + animal = self.toon.style.getAnimal() + if animal != 'dog': + muzzle = head.find('**/muzzle*neutral') + else: + muzzle = head.find('**/muzzle*') + if ears != ears.notFound(): + if self.speciesType == 'cat': + ears.setTexture(tsDetail, stoneTex) + elif self.speciesType == 'horse': + pass + elif self.speciesType == 'rabbit': + ears.setTexture(tsDetail, stoneTex) + elif self.speciesType == 'monkey': + ears.setTexture(tsDetail, stoneTex) + ears.setColor(VBase4(0.6, 0.9, 1, 1), 10) + if muzzle != muzzle.notFound(): + muzzle.setTexture(tsDetail, stoneTex) + if self.speciesType == 'dog': + nose = head.find('**/nose') + if nose != nose.notFound(): + nose.setTexture(tsDetail, stoneTex) + + tsLashes = TextureStage('tsLashes') + tsLashes.setPriority(2) + tsLashes.setMode(tsLashes.MDecal) + if self.gender == 'f': + if self.toon.hasLOD(): + head = self.toon.getPart('head', '1000') + else: + head = self.toon.getPart('head', 'lodRoot') + if self.headType[1] == 'l': + openString = 'open-long' + closedString = 'closed-long' + else: + openString = 'open-short' + closedString = 'closed-short' + lashesOpen = head.find('**/' + openString) + lashesClosed = head.find('**/' + closedString) + if lashesOpen != lashesOpen.notFound(): + lashesOpen.setTexture(tsLashes, stoneTex) + lashesOpen.setColor(VBase4(1, 1, 1, 0.4), 10) + if lashesClosed != lashesClosed.notFound(): + lashesClosed.setTexture(tsLashes, stoneTex) + lashesClosed.setColor(VBase4(1, 1, 1, 0.4), 10) + + def setOptional(self, optional): + self.optional = optional + + def getToonPropertiesFromOptional(self): + genderTypeNum = self.optional & 1 + legTypeNum = (self.optional & 6) >> 1 + torsoTypeNum = (self.optional & 120) >> 3 + headTypeNum = (self.optional & 65408) >> 7 + if genderTypeNum == 0: + self.gender = 'f' + else: + self.gender = 'm' + if legTypeNum <= len(ToonDNA.toonLegTypes): + self.legType = ToonDNA.toonLegTypes[legTypeNum] + if torsoTypeNum <= len(ToonDNA.toonTorsoTypes): + self.torsoType = ToonDNA.toonTorsoTypes[torsoTypeNum] + if headTypeNum <= len(ToonDNA.toonHeadTypes): + self.headType = ToonDNA.toonHeadTypes[headTypeNum] + + def poseToonFromTypeIndex(self, typeIndex): + if typeIndex == 205: + self.toon.pose('wave', 18) + elif typeIndex == 206: + self.toon.pose('victory', 116) + elif typeIndex == 207: + self.toon.pose('bored', 96) + elif typeIndex == 208: + self.toon.pose('think', 59) + + def poseToonFromSpecialsIndex(self, specialsIndex): + if specialsIndex == 105: + self.toon.pose('wave', 18) + elif specialsIndex == 106: + self.toon.pose('victory', 116) + elif specialsIndex == 107: + self.toon.pose('bored', 96) + elif specialsIndex == 108: + self.toon.pose('think', 59) diff --git a/toontown/estate/DistributedToonStatuaryAI.py b/toontown/estate/DistributedToonStatuaryAI.py new file mode 100755 index 00000000..880aa8b3 --- /dev/null +++ b/toontown/estate/DistributedToonStatuaryAI.py @@ -0,0 +1,9 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.estate.DistributedStatuaryAI import DistributedStatuaryAI + +class DistributedToonStatuaryAI(DistributedStatuaryAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedToonStatuaryAI") + + def setOptional(self, todo0): + pass + diff --git a/toontown/estate/DistributedTrunk.py b/toontown/estate/DistributedTrunk.py new file mode 100755 index 00000000..66c94ba4 --- /dev/null +++ b/toontown/estate/DistributedTrunk.py @@ -0,0 +1,357 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +import DistributedCloset +import ClosetGlobals +import TrunkGUI +from toontown.toon import ToonDNA +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +N_A = 0 + +class DistributedTrunk(DistributedCloset.DistributedCloset): + notify = directNotify.newCategory('DistributedTrunk') + + def __init__(self, cr): + DistributedCloset.DistributedCloset.__init__(self, cr) + self.hatList = [] + self.glassesList = [] + self.backpackList = [] + self.shoesList = [] + self.oldHatList = [] + self.oldGlassesList = [] + self.oldBackpackList = [] + self.oldShoesList = [] + self.swapHatEvent = '' + self.swapGlassesEvent = '' + self.swapBackpackEvent = '' + self.swapShoesEvent = '' + self.hatDeleted = 0 + self.glassesDeleted = 0 + self.backpackDeleted = 0 + self.shoesDeleted = 0 + + def printInfo(self): + print 'avid: %s, gender: %s' % (self.av.doId, self.av.style.gender) + print 'current hat = %s, glasses = %s, backpack = %s, shoes = %s' % (self.av.getHat(), + self.av.getGlasses(), + self.av.getBackpack(), + self.av.getShoes()) + print 'hatList = %s' % self.av.getHatList() + print 'glassesList = %s' % self.av.getGlassesList() + print 'backpackList = %s' % self.av.getBackpackList() + print 'shoesList = %s' % self.av.getShoesList() + + def setState(self, mode, avId, ownerId, gender, hatList, glassesList, backpackList, shoesList): + self.notify.debug('setState, mode=%s, avId=%s, ownerId=%d' % (mode, avId, ownerId)) + self.isOwner = avId == ownerId + self.ownerGender = gender + if mode == ClosetGlobals.CLOSED: + self.fsm.request('closed') + return + elif mode == ClosetGlobals.OPEN: + self.customerId = avId + self.av = self.cr.doId2do.get(self.customerId, None) + if self.av: + if base.localAvatar.getDoId() == self.customerId: + self.gender = self.av.style.gender + self.hatList = hatList + self.glassesList = glassesList + self.backpackList = backpackList + self.shoesList = shoesList + self.oldHatList = self.hatList[0:] + self.oldGlassesList = self.glassesList[0:] + self.oldBackpackList = self.backpackList[0:] + self.oldShoesList = self.shoesList[0:] + if not self.isOwner: + self.__popupNotOwnerPanel() + else: + taskMgr.doMethodLater(0.5, self.popupChangeClothesGUI, self.uniqueName('popupChangeClothesGUI')) + self.fsm.request('open') + return + + def load(self): + lNode = self.find('**/lid_origin') + lLid = self.find('**/lid') + if lNode.isEmpty() or lLid.isEmpty(): + self.lid = None + else: + lLid.wrtReparentTo(lNode) + self.lid = lNode + if not lNode.isEmpty(): + self.scale = lLid.getScale()[0] * 0.6 + return + + def popupChangeClothesGUI(self, task): + self.notify.debug('popupChangeClothesGUI') + self.purchaseDoneEvent = self.uniqueName('purchaseDone') + self.swapHatEvent = self.uniqueName('swapHat') + self.swapGlassesEvent = self.uniqueName('swapGlasses') + self.swapBackpackEvent = self.uniqueName('swapBackpack') + self.swapShoesEvent = self.uniqueName('swapShoes') + self.cancelEvent = self.uniqueName('cancel') + self.accept(self.purchaseDoneEvent, self.__proceedToCheckout) + self.accept(self.swapHatEvent, self.__handleSwapHat) + self.accept(self.swapGlassesEvent, self.__handleSwapGlasses) + self.accept(self.swapBackpackEvent, self.__handleSwapBackpack) + self.accept(self.swapShoesEvent, self.__handleSwapShoes) + self.accept(self.cancelEvent, self._handleCancel) + self.deleteEvent = self.uniqueName('delete') + if self.isOwner: + self.accept(self.deleteEvent, self.__handleDelete) + if not self.closetGUI: + self.closetGUI = TrunkGUI.TrunkGUI(self.isOwner, self.purchaseDoneEvent, self.cancelEvent, self.swapHatEvent, self.swapGlassesEvent, self.swapBackpackEvent, self.swapShoesEvent, self.deleteEvent, self.hatList, self.glassesList, self.backpackList, self.shoesList) + self.closetGUI.load() + if self.gender != self.ownerGender: + self.closetGUI.setGender(self.ownerGender) + self.closetGUI.enter(base.localAvatar) + self.closetGUI.showButtons() + oldHat = self.av.getHat() + oldGlasses = self.av.getGlasses() + oldBackpack = self.av.getBackpack() + oldShoes = self.av.getShoes() + self.oldStyle = {ToonDNA.HAT: oldHat, + ToonDNA.GLASSES: oldGlasses, + ToonDNA.BACKPACK: oldBackpack, + ToonDNA.SHOES: oldShoes} + return Task.done + + def resetCloset(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupChangeClothesGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + taskMgr.remove(self.uniqueName('lerpToon')) + if self.closetGUI: + self.closetGUI.hideButtons() + self.closetGUI.exit() + self.closetGUI.unload() + self.closetGUI = None + del self.av + self.av = base.localAvatar + oldHat = self.av.getHat() + oldGlasses = self.av.getGlasses() + oldBackpack = self.av.getBackpack() + oldShoes = self.av.getShoes() + self.oldStyle = {ToonDNA.HAT: oldHat, + ToonDNA.GLASSES: oldGlasses, + ToonDNA.BACKPACK: oldBackpack, + ToonDNA.SHOES: oldShoes} + self.hatDeleted = 0 + self.glassesDeleted = 0 + self.backpackDeleted = 0 + self.shoesDeleted = 0 + return Task.done + + def _handleCancel(self): + if self.oldStyle: + oldHat = self.oldStyle[ToonDNA.HAT] + oldGlasses = self.oldStyle[ToonDNA.GLASSES] + oldBackpack = self.oldStyle[ToonDNA.BACKPACK] + oldShoes = self.oldStyle[ToonDNA.SHOES] + self.d_setDNA(oldHat[0], oldHat[1], oldHat[2], oldGlasses[0], oldGlasses[1], oldGlasses[2], oldBackpack[0], oldBackpack[1], oldBackpack[2], oldShoes[0], oldShoes[1], oldShoes[2], 1) + else: + self.notify.info('avoided crash in handleCancel') + self._handlePurchaseDone() + if self.closetGUI: + self.closetGUI.resetClothes(self.oldStyle) + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = None + return + + def __handleSwapHat(self): + item = self.av.getHat() + self.d_setDNA(item[0], item[1], item[2], N_A, N_A, N_A, N_A, N_A, N_A, N_A, N_A, N_A, 0, ToonDNA.HAT) + if self.closetGUI: + self.closetGUI.updateTrashButtons() + + def __handleSwapGlasses(self): + item = self.av.getGlasses() + self.d_setDNA(N_A, N_A, N_A, item[0], item[1], item[2], N_A, N_A, N_A, N_A, N_A, N_A, 0, ToonDNA.GLASSES) + if self.closetGUI: + self.closetGUI.updateTrashButtons() + + def __handleSwapBackpack(self): + item = self.av.getBackpack() + self.d_setDNA(N_A, N_A, N_A, N_A, N_A, N_A, item[0], item[1], item[2], N_A, N_A, N_A, 0, ToonDNA.BACKPACK) + if self.closetGUI: + self.closetGUI.updateTrashButtons() + + def __handleSwapShoes(self): + item = self.av.getShoes() + self.d_setDNA(N_A, N_A, N_A, N_A, N_A, N_A, N_A, N_A, N_A, item[0], item[1], item[2], 0, ToonDNA.SHOES) + if self.closetGUI: + self.closetGUI.updateTrashButtons() + + def __handleDelete(self, which): + if which == ToonDNA.HAT: + itemList = self.closetGUI.hats + trashIndex = self.closetGUI.hatChoice + swapFunc = self.closetGUI.swapHat + removeFunc = self.closetGUI.removeHat + trashItem = self.av.getHat() + self.hatDeleted = self.hatDeleted | 1 + elif which == ToonDNA.GLASSES: + itemList = self.closetGUI.glasses + trashIndex = self.closetGUI.glassesChoice + swapFunc = self.closetGUI.swapGlasses + removeFunc = self.closetGUI.removeGlasses + trashItem = self.av.getGlasses() + self.glassesDeleted = self.glassesDeleted | 1 + elif which == ToonDNA.BACKPACK: + itemList = self.closetGUI.backpacks + trashIndex = self.closetGUI.backpackChoice + swapFunc = self.closetGUI.swapBackpack + removeFunc = self.closetGUI.removeBackpack + trashItem = self.av.getBackpack() + self.backpackDeleted = self.backpackDeleted | 1 + elif which == ToonDNA.SHOES: + itemList = self.closetGUI.shoes + trashIndex = self.closetGUI.shoesChoice + swapFunc = self.closetGUI.swapShoes + removeFunc = self.closetGUI.removeShoes + trashItem = self.av.getShoes() + self.shoesDeleted = self.shoesDeleted | 1 + else: + self.notify.warning("we don't know about this item(type = %s)" % which) + return + if len(itemList) > 1: + if trashIndex == 0: + swapFunc(1) + else: + swapFunc(-1) + removeFunc(trashIndex) + self.sendUpdate('removeItem', [trashItem[0], + trashItem[1], + trashItem[2], + which]) + swapFunc(0) + self.closetGUI.updateTrashButtons() + else: + self.notify.warning("cant delete this item(type = %s), since we don't have a replacement" % which) + + def __proceedToCheckout(self): + if self.hatDeleted or self.glassesDeleted or self.backpackDeleted or self.shoesDeleted: + self.__popupAreYouSurePanel() + else: + self._handlePurchaseDone() + + def _handlePurchaseDone(self, timeout = 0): + if timeout == 1: + oldHat = self.oldStyle[ToonDNA.HAT] + oldGlasses = self.oldStyle[ToonDNA.GLASSES] + oldBackpack = self.oldStyle[ToonDNA.BACKPACK] + oldShoes = self.oldStyle[ToonDNA.SHOES] + self.d_setDNA(oldHat[0], oldHat[1], oldHat[2], oldGlasses[0], oldGlasses[1], oldGlasses[2], oldBackpack[0], oldBackpack[1], oldBackpack[2], oldShoes[0], oldShoes[1], oldShoes[2], 1) + else: + which = 0 + if hasattr(self.closetGUI, 'hatChoice') and hasattr(self.closetGUI, 'glassesChoice') and hasattr(self.closetGUI, 'backpackChoice') and hasattr(self.closetGUI, 'shoesChoice'): + if self.closetGUI.hatChoice != 0 or self.hatDeleted: + which = which | ToonDNA.HAT + if self.closetGUI.glassesChoice != 0 or self.glassesDeleted: + which = which | ToonDNA.GLASSES + if self.closetGUI.backpackChoice != 0 or self.backpackDeleted: + which = which | ToonDNA.BACKPACK + if self.closetGUI.shoesChoice != 0 or self.shoesDeleted: + which = which | ToonDNA.SHOES + hat = self.av.getHat() + glasses = self.av.getGlasses() + backpack = self.av.getBackpack() + shoes = self.av.getShoes() + self.d_setDNA(hat[0], hat[1], hat[2], glasses[0], glasses[1], glasses[2], backpack[0], backpack[1], backpack[2], shoes[0], shoes[1], shoes[2], 2, which) + + def d_setDNA(self, hatIdx, hatTexture, hatColor, glassesIdx, glassesTexture, glassesColor, backpackIdx, backpackTexture, backpackColor, shoesIdx, shoesTexture, shoesColor, finished, which = ToonDNA.HAT | ToonDNA.GLASSES | ToonDNA.BACKPACK | ToonDNA.SHOES): + self.sendUpdate('setDNA', [hatIdx, + hatTexture, + hatColor, + glassesIdx, + glassesTexture, + glassesColor, + backpackIdx, + backpackTexture, + backpackColor, + shoesIdx, + shoesTexture, + shoesColor, + finished, + which]) + + def setCustomerDNA(self, avId, hatIdx, hatTexture, hatColor, glassesIdx, glassesTexture, glassesColor, backpackIdx, backpackTexture, backpackColor, shoesIdx, shoesTexture, shoesColor, which): + if avId and avId != base.localAvatar.doId: + av = base.cr.doId2do.get(avId, None) + if av: + if self.av == base.cr.doId2do[avId]: + if which & ToonDNA.HAT: + self.av.setHat(hatIdx, hatTexture, hatColor) + if which & ToonDNA.GLASSES: + self.av.setGlasses(glassesIdx, glassesTexture, glassesColor) + if which & ToonDNA.BACKPACK: + self.av.setBackpack(backpackIdx, backpackTexture, backpackColor) + if which & ToonDNA.SHOES: + self.av.setShoes(shoesIdx, shoesTexture, shoesColor) + self.av.generateToonAccessories() + return + + def __popupNotOwnerPanel(self): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = None + self.purchaseDoneEvent = self.uniqueName('purchaseDone') + self.swapHatEvent = self.uniqueName('swapHat') + self.swapGlassesEvent = self.uniqueName('swapGlasses') + self.swapBackpackEvent = self.uniqueName('swapBackpack') + self.swapShoesEvent = self.uniqueName('swapShoes') + self.cancelEvent = self.uniqueName('cancel') + self.accept(self.purchaseDoneEvent, self.__proceedToCheckout) + self.accept(self.swapHatEvent, self.__handleSwapHat) + self.accept(self.swapGlassesEvent, self.__handleSwapGlasses) + self.accept(self.swapBackpackEvent, self.__handleSwapBackpack) + self.accept(self.swapShoesEvent, self.__handleSwapShoes) + self.accept(self.cancelEvent, self._handleCancel) + self.deleteEvent = self.uniqueName('delete') + if self.isOwner: + self.accept(self.deleteEvent, self.__handleDelete) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=TTLocalizer.TrunkNotOwnerMessage, frameSize=(-1, 1, -1, 1), text_wordwrap=10, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(0.88, 1, 0.55), geom_pos=(0, 0, -.08), text_scale=0.08, text_pos=(0, 0.06)) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.ClosetPopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.21), command=self._handleNotOwnerMessageOK) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + return + + def __popupAreYouSurePanel(self): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = None + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelButtonImage = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=TTLocalizer.TrunkAreYouSureMessage, frameSize=(-1, 1, -1, 1), text_wordwrap=10, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(0.88, 1, 0.55), geom_pos=(0, 0, -.08), text_scale=0.08, text_pos=(0, 0.08)) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.ClosetPopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(-0.1, 0.0, -0.21), command=self._handleYesImSure) + DirectButton(self.popupInfo, image=cancelButtonImage, relief=None, text=TTLocalizer.ClosetPopupCancel, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.1, 0.0, -0.21), command=self._handleNotSure) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + return + + def _openDoors(self): + if self.closetTrack: + self.closetTrack.finish() + openHpr = Vec3(0, -80, 0) + if self.av: + self.av.applyCheesyEffect(ToontownGlobals.CENormal) + self.closetTrack = Parallel() + if self.lid: + self.closetTrack.append(self.lid.hprInterval(0.5, openHpr)) + self.closetTrack.start() + + def _closeDoors(self): + if self.closetTrack: + self.closetTrack.finish() + closeHpr = Vec3(0, 0, 0) + if self.av: + self.av.reconsiderCheesyEffect() + self.closetTrack = Parallel() + if self.lid: + self.closetTrack.append(self.lid.hprInterval(0.5, closeHpr)) + self.closetTrack.start() diff --git a/toontown/estate/DistributedTrunkAI.py b/toontown/estate/DistributedTrunkAI.py new file mode 100755 index 00000000..b24840a1 --- /dev/null +++ b/toontown/estate/DistributedTrunkAI.py @@ -0,0 +1,173 @@ +from toontown.estate.DistributedClosetAI import DistributedClosetAI +from toontown.toon.ToonDNA import ToonDNA, HAT, GLASSES, BACKPACK, SHOES +from direct.distributed.ClockDelta import globalClockDelta +import ClosetGlobals + + +class DistributedTrunkAI(DistributedClosetAI): + notify = directNotify.newCategory('DistributedTrunkAI') + + def __init__(self, air, furnitureMgr, itemType): + DistributedClosetAI.__init__(self, air, furnitureMgr, itemType) + + self.hatList = [] + self.glassesList = [] + self.backpackList = [] + self.shoesList = [] + + self.removedHats = [] + self.removedGlasses = [] + self.removedBackpacks = [] + self.removedShoes = [] + + def generate(self): + if self.furnitureMgr.ownerId: + owner = self.air.doId2do.get(self.furnitureMgr.ownerId) + if owner: + self.hatList = owner.hatList + self.glassesList = owner.glassesList + self.backpackList = owner.backpackList + self.shoesList = owner.shoesList + self.gender = owner.dna.gender + else: + self.air.dbInterface.queryObject(self.air.dbId, self.furnitureMgr.ownerId, self.__gotOwner) + + def __gotOwner(self, dclass, fields): + if dclass != self.air.dclassesByName['DistributedToonAI']: + self.notify.warning('Got object of wrong type!') + return + self.hatList = fields['setHatList'][0] + self.glassesList = fields['setGlassesList'][0] + self.backpackList = fields['setBackpackList'][0] + self.shoesList = fields['setShoesList'][0] + dna = ToonDNA(str=fields['setDNAString'][0]) + self.gender = dna.gender + + def __verifyAvatarInMyZone(self, av): + return av.getLocation() == self.getLocation() + + def setState(self, mode, avId, ownerId, gender, hatList, glassesList, backpackList, shoesList): + self.sendUpdate('setState', [mode, avId, ownerId, gender, hatList, glassesList, backpackList, shoesList]) + + def removeItem(self, itemIdx, textureIdx, colorIdx, which): + avId = self.air.getAvatarIdFromSender() + if avId != self.furnitureMgr.ownerId: + self.air.writeServerEvent('suspicious', avId=avId, issue='Tried to remove item from someone else\'s closet!') + return + if avId != self.avId: + self.air.writeServerEvent('suspicious', avId=avId, issue='Tried to remove item while not interacting with closet!') + return + av = self.air.doId2do.get(avId) + if not av: + self.air.writeServerEvent('suspicious', avId=avId, issue='Tried to interact with a closet from another shard!') + return + + if which == HAT: + self.removedHats.append((itemIdx, textureIdx, colorIdx)) + elif which == GLASSES: + self.removedGlasses.append((itemIdx, textureIdx, colorIdx)) + elif which == BACKPACK: + self.removedBackpacks.append((itemIdx, textureIdx, colorIdx)) + elif which == SHOES: + self.removedShoes.append((itemIdx, textureIdx, colorIdx)) + + def setDNA(self, hatIdx, hatTexture, hatColor, glassesIdx, glassesTexture, glassesColor, backpackIdx, backpackTexture, backpackColor, shoesIdx, shoesTexture, shoesColor, finished, which): + avId = self.air.getAvatarIdFromSender() + if avId != self.avId: + self.air.writeServerEvent('suspicious', avId, 'Tried to set DNA from closet while not using it!') + return + av = self.air.doId2do.get(avId) + if not av: + self.air.writeServerEvent('suspicious', avId, 'Interacted with a closet from another shard!') + return + if not self.__verifyAvatarInMyZone(av): + self.air.writeServerEvent('suspicious', avId, 'Tried to setDNA while in another zone!') + return + if not finished: + # They changed one of their accessories. + if which == HAT: + av.b_setHat(hatIdx, hatTexture, hatColor) + if which == GLASSES: + av.b_setGlasses(glassesIdx, glassesTexture, glassesColor) + if which == BACKPACK: + av.b_setBackpack(backpackIdx, backpackTexture, backpackColor) + if which == SHOES: + av.b_setShoes(shoesIdx, shoesTexture, shoesColor) + elif finished == 1: + # The user pressed the cancel button. All we need to do is free him. + # Reset the removed items and our user. + av.b_setHat(hatIdx, hatTexture, hatColor) + av.b_setGlasses(glassesIdx, glassesTexture, glassesColor) + av.b_setBackpack(backpackIdx, backpackTexture, backpackColor) + av.b_setShoes(shoesIdx, shoesTexture, shoesColor) + + self.removedHats = [] + self.removedGlasses = [] + self.removedBackpacks = [] + self.removedShoes = [] + self.avId = None + # Free the user. + self.d_setMovie(ClosetGlobals.CLOSET_MOVIE_COMPLETE, avId, globalClockDelta.getRealNetworkTime()) + self.resetMovie() + self.setState(ClosetGlobals.CLOSED, 0, self.furnitureMgr.ownerId, self.gender, self.hatList, self.glassesList, self.backpackList, self.shoesList) + elif finished == 2: + # They are done using the trunk. Update their removed items. + # Is the user actually the owner? + if avId != self.furnitureMgr.ownerId: + self.air.writeServerEvent('suspicious', avId, 'Tried to set their clothes from somebody else\'s closet!') + return + + # Put on the accessories they want... + if which & HAT: + av.b_setHat(hatIdx, hatTexture, hatColor) + if which & GLASSES: + av.b_setGlasses(glassesIdx, glassesTexture, glassesColor) + if which & BACKPACK: + av.b_setBackpack(backpackIdx, backpackTexture, backpackColor) + if which & SHOES: + av.b_setShoes(shoesIdx, shoesTexture, shoesColor) + + # Delete all their items they want to be deleted... + for hat in self.removedHats: + id, texture, color = hat + av.removeItemInAccessoriesList(HAT, id, texture, color) + for glasses in self.removedGlasses: + id, texture, color = glasses + av.removeItemInAccessoriesList(GLASSES, id, texture, color) + for backpack in self.removedBackpacks: + id, texture, color = backpack + av.removeItemInAccessoriesList(BACKPACK, id, texture, color) + for shoe in self.removedShoes: + id, texture, color = shoe + av.removeItemInAccessoriesList(SHOES, id, texture, color) + + # Regenerate the available accessories... + self.removedHats = [] + self.removedGlasses = [] + self.removedBackpacks = [] + self.removedShoes = [] + self.generate() + + self.avId = None + + # We are done, free the user! + self.d_setMovie(ClosetGlobals.CLOSET_MOVIE_COMPLETE, avId, globalClockDelta.getRealNetworkTime()) + self.resetMovie() + self.setState(ClosetGlobals.CLOSED, 0, self.furnitureMgr.ownerId, self.gender, self.hatList, self.glassesList, self.backpackList, self.shoesList) + + def enterAvatar(self): + avId = self.air.getAvatarIdFromSender() + if self.avId: + if self.avId == avId: + self.air.writeServerEvent('suspicious', avId=avId, issue='Tried to use closet twice!') + self.sendUpdateToAvatarId(avId, 'freeAvatar', []) + return + av = self.air.doId2do.get(avId) + if not av: + self.air.writeServerEvent('suspicious', avId=avId, issue='Not in same shard as closet!') + return + if not self.__verifyAvatarInMyZone(av): + self.air.writeServerEvent('suspicious', avId=avId, issue='Not in same zone as closet!') + return + self.avId = avId + self.setState(ClosetGlobals.OPEN, avId, self.furnitureMgr.ownerId, self.gender, self.hatList, self.glassesList, self.backpackList, self.shoesList) diff --git a/toontown/estate/Estate.py b/toontown/estate/Estate.py new file mode 100755 index 00000000..160ab258 --- /dev/null +++ b/toontown/estate/Estate.py @@ -0,0 +1,377 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from direct.distributed.ClockDelta import * +from toontown.hood import Place +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.task.Task import Task +from toontown.toonbase import TTLocalizer +import random +from direct.showbase import PythonUtil +from toontown.hood import Place +from toontown.pets import PetTutorial +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs, TLNull +from toontown.safezone import SZUtil +import HouseGlobals + +class Estate(Place.Place): + notify = DirectNotifyGlobal.directNotify.newCategory('Estate') + + def __init__(self, loader, avId, zoneId, parentFSMState, doneEvent): + Place.Place.__init__(self, None, doneEvent) + self.id = MyEstate + self.avId = avId + self.zoneId = zoneId + self.loader = loader + self.cameraSubmerged = -1 + self.toonSubmerged = -1 + self.fsm = ClassicFSM.ClassicFSM('Estate', [State.State('init', self.enterInit, self.exitInit, ['final', + 'teleportIn', + 'doorIn', + 'walk']), + State.State('petTutorial', self.enterPetTutorial, self.exitPetTutorial, ['walk']), + State.State('walk', self.enterWalk, self.exitWalk, ['final', + 'sit', + 'stickerBook', + 'options', + 'quest', + 'fishing', + 'mailbox', + 'stopped', + 'teleportOut', + 'doorOut', + 'push', + 'pet', + 'activity']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut']), + State.State('sit', self.enterSit, self.exitSit, ['walk']), + State.State('push', self.enterPush, self.exitPush, ['walk']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'sit', + 'quest', + 'fishing', + 'mailbox', + 'stopped', + 'doorOut', + 'push', + 'pet', + 'teleportOut', + 'activity']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', 'petTutorial']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', 'walk', 'final']), + State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk', 'stopped']), + State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['final', 'walk', 'stopped']), + State.State('final', self.enterFinal, self.exitFinal, ['teleportIn']), + State.State('quest', self.enterQuest, self.exitQuest, ['walk']), + State.State('activity', self.enterActivity, self.exitActivity, ['walk', 'stopped']), + State.State('fishing', self.enterFishing, self.exitFishing, ['walk', 'stopped']), + State.State('mailbox', self.enterMailbox, self.exitMailbox, ['walk', 'stopped']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk']), + State.State('pet', self.enterPet, self.exitPet, ['walk', 'teleportOut'])], 'init', 'final') + self.fsm.enterInitialState() + self.doneEvent = doneEvent + self.parentFSMState = parentFSMState + return + + def delete(self): + self.unload() + + def load(self): + Place.Place.load(self) + self.fog = Fog('EstateFog') + taskMgr.add(self.__checkCameraUnderwater, 'estate-check-cam-underwater') + path = self.loader.geom.find('**/Path') + path.setBin('ground', 10, 1) + self.parentFSMState.addChild(self.fsm) + + def unload(self): + self.ignoreAll() + self.notify.info('remove estate-check-toon-underwater to TaskMgr in unload()') + taskMgr.remove('estate-check-toon-underwater') + taskMgr.remove('estate-check-cam-underwater') + self.parentFSMState.removeChild(self.fsm) + del self.fsm + self.fog = None + Place.Place.unload(self) + return + + def enter(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + if config.GetBool('want-estate-telemetry-limiter', 1): + limiter = TLGatherAllAvs('Estate', RotationLimitToH) + else: + limiter = TLNull() + self._telemLimiter = limiter + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.HALLOWEEN) and self.loader.hood.spookySkyFile: + lightsOff = Sequence(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(0.55, 0.55, 0.65, 1)), Func(self.loader.hood.startSpookySky)) + lightsOff.start() + else: + self.loader.hood.startSky() + lightsOn = LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(1, 1, 1, 1)) + lightsOn.start() + self.loader.hood.sky.setFogOff() + self.__setFaintFog() + for i in self.loader.nodeList: + self.loader.enterAnimatedProps(i) + + self.loader.geom.reparentTo(render) + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.APRIL_TOONS_WEEK): + base.localAvatar.startAprilToonsControls() + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + self.fsm.request(requestStatus['how'], [requestStatus]) + + def exit(self): + base.localAvatar.stopChat() + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.APRIL_TOONS_WEEK): + base.localAvatar.stopAprilToonsControls() + self._telemLimiter.destroy() + del self._telemLimiter + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + self.loader.geom.reparentTo(hidden) + for i in self.loader.nodeList: + self.loader.exitAnimatedProps(i) + + self.loader.hood.stopSky() + render.setFogOff() + base.cr.cache.flush() + + def __setZoneId(self, zoneId): + self.zoneId = zoneId + + def detectedMailboxCollision(self): + self.fsm.request('mailbox') + + def detectedGardenPlotUse(self): + if hasattr(self, 'fsm'): + self.fsm.request('stopped') + + def detectedGardenPlotDone(self): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + + def detectedFlowerSellUse(self): + if hasattr(self, 'fsm'): + self.fsm.request('stopped') + + def detectedFlowerSellDone(self): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + + def enterInit(self): + pass + + def exitInit(self): + pass + + def enterActivity(self, setAnimState = True): + if setAnimState: + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(False) + base.localAvatar.laffMeter.start() + + def exitActivity(self): + base.localAvatar.setTeleportAvailable(True) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + + def enterPetTutorial(self, bDummy = True): + self.notify.info('remove estate-check-toon-underwater to TaskMgr in enterPetTutorial()') + taskMgr.remove('estate-check-toon-underwater') + self.petTutorialDoneEvent = 'PetTutorialDone' + self.acceptOnce(self.petTutorialDoneEvent, self.petTutorialDone) + self.petTutorial = PetTutorial.PetTutorial(self.petTutorialDoneEvent) + + def exitPetTutorial(self): + self.notify.info('add estate-check-toon-underwater to TaskMgr in exitPetTutorial()') + if hasattr(self, 'fsm'): + taskMgr.add(self.__checkToonUnderwater, 'estate-check-toon-underwater') + if hasattr(self, 'petTutorial') and self.petTutorial is not None: + self.petTutorial.destroy() + return + + def petTutorialDone(self): + self.ignore(self.petTutorialDoneEvent) + self.petTutorial.destroy() + self.petTutorial = None + self.fsm.request('walk', [1]) + return + + def enterMailbox(self): + Place.Place.enterPurchase(self) + base.localAvatar.startSleepWatch(self.__handleFallingAsleepMailbox) + + def __handleFallingAsleepMailbox(self, arg): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + messenger.send('mailboxAsleep') + base.localAvatar.forceGotoSleep() + + def exitMailbox(self): + Place.Place.exitPurchase(self) + base.localAvatar.stopSleepWatch() + + def enterTeleportIn(self, requestStatus): + self._etiToken = self.addSetZoneCompleteCallback(Functor(self._teleportToHouse, requestStatus)) + Place.Place.enterTeleportIn(self, requestStatus) + + def _teleportToHouse(self, requestStatus): + try: + houseDo = base.cr.doId2do.get(base.localAvatar.houseId) + house = houseDo.house + pos = house.getPos(render) + base.localAvatar.detachNode() + base.localAvatar.setPosHpr(house, 17, 3, 0, 125, 0, 0) + except: + x, y, z, h, p, r = HouseGlobals.defaultEntryPoint + base.localAvatar.detachNode() + base.localAvatar.setPosHpr(render, x, y, z, h, p, r) + + base.localAvatar.setScale(1, 1, 1) + self.toonSubmerged = -1 + self.notify.info('remove estate-check-toon-underwater to TaskMgr in enterTeleportIn()') + taskMgr.remove('estate-check-toon-underwater') + if base.wantPets: + if base.localAvatar.hasPet() and not base.localAvatar.petTutorialDone: + self.nextState = 'petTutorial' + + def teleportInDone(self): + self.notify.debug('teleportInDone') + self.toonSubmerged = -1 + if self.nextState is not 'petTutorial': + self.notify.info('add estate-check-toon-underwater to TaskMgr in teleportInDone()') + if hasattr(self, 'fsm'): + taskMgr.add(self.__checkToonUnderwater, 'estate-check-toon-underwater') + Place.Place.teleportInDone(self) + + def exitTeleportIn(self): + self.removeSetZoneCompleteCallback(self._etiToken) + Place.Place.exitTeleportIn(self) + + def enterTeleportOut(self, requestStatus): + Place.Place.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + avId = requestStatus['avId'] + shardId = requestStatus['shardId'] + if hoodId == ToontownGlobals.MyEstate and zoneId == self.getZoneId() and shardId == None: + self.fsm.request('teleportIn', [requestStatus]) + elif hoodId == ToontownGlobals.MyEstate and shardId == None: + self.doneStatus = requestStatus + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent, [self.doneStatus]) + return + + def goHomeFailed(self, task): + self.notifyUserGoHomeFailed() + self.ignore('setLocalEstateZone') + self.doneStatus['avId'] = -1 + self.doneStatus['zoneId'] = self.getZoneId() + self.fsm.request('teleportIn', [self.doneStatus]) + return Task.done + + def exitTeleportOut(self): + Place.Place.exitTeleportOut(self) + + def exitDoorIn(self): + self.toonSubmerged = -1 + self.notify.info('add estate-check-toon-underwater to TaskMgr in exitDoorIn()') + if hasattr(self, 'fsm'): + taskMgr.add(self.__checkToonUnderwater, 'estate-check-toon-underwater') + Place.Place.exitDoorIn(self) + + def getZoneId(self): + if self.zoneId: + return self.zoneId + else: + self.notify.warning('no zone id available') + + def __checkCameraUnderwater(self, task): + if camera.getZ(render) < -1.2: + self.__submergeCamera() + else: + self.__emergeCamera() + return Task.cont + + def __checkToonUnderwater(self, task): + if base.localAvatar.getZ() < -4.0: + self.__submergeToon() + else: + self.__emergeToon() + return Task.cont + + def __submergeCamera(self): + if self.cameraSubmerged == 1: + return + self.__setUnderwaterFog() + base.playSfx(self.loader.underwaterSound, looping=1, volume=0.8) + self.cameraSubmerged = 1 + + def __emergeCamera(self): + if self.cameraSubmerged == 0: + return + self.loader.underwaterSound.stop() + self.loader.hood.sky.setFogOff() + self.__setFaintFog() + self.cameraSubmerged = 0 + + def forceUnderWater(self): + self.toonSubmerged = 0 + self.__submergeToon() + + def __submergeToon(self): + if self.toonSubmerged == 1: + return + self.notify.debug('continuing in __submergeToon') + if hasattr(self, 'loader') and self.loader: + base.playSfx(self.loader.submergeSound) + if base.config.GetBool('disable-flying-glitch') == 0: + self.fsm.request('walk') + self.walkStateData.fsm.request('swimming', [self.loader.swimSound]) + pos = base.localAvatar.getPos(render) + base.localAvatar.d_playSplashEffect(pos[0], pos[1], -2.3) + self.toonSubmerged = 1 + + def __emergeToon(self): + if self.toonSubmerged == 0: + return + self.notify.debug('continuing in __emergeToon') + if hasattr(self, 'walkStateData'): + self.walkStateData.fsm.request('walking') + self.toonSubmerged = 0 + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.APRIL_TOONS_WEEK): + base.localAvatar.startAprilToonsControls() + + def __setUnderwaterFog(self): + if base.wantFog: + self.fog.setLinearRange(0.1, 100.0) + render.setFog(self.fog) + self.loader.hood.sky.setFog(self.fog) + SZUtil.startUnderwaterFog() + + def __setWhiteFog(self): + if base.wantFog: + self.fog.setColor(Vec4(0.8, 0.8, 0.8, 1.0)) + self.fog.setLinearRange(0.0, 400.0) + render.setFog(self.fog) + self.loader.hood.sky.setFog(self.fog) + SZUtil.stopUnderwaterFog() + + def __setFaintFog(self): + if base.wantFog: + self.fog.setColor(Vec4(0.8, 0.8, 0.8, 1.0)) + self.fog.setLinearRange(0.0, 700.0) + render.setFog(self.fog) + SZUtil.stopUnderwaterFog() diff --git a/toontown/estate/EstateLoader.py b/toontown/estate/EstateLoader.py new file mode 100755 index 00000000..6735b72b --- /dev/null +++ b/toontown/estate/EstateLoader.py @@ -0,0 +1,325 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from toontown.safezone import SafeZoneLoader +import random, math, House, Estate, HouseGlobals +from toontown.coghq import MovingPlatform +from direct.directnotify import DirectNotifyGlobal + +class EstateLoader(SafeZoneLoader.SafeZoneLoader): + notify = DirectNotifyGlobal.directNotify.newCategory('EstateLoader') + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + del self.fsm + self.fsm = ClassicFSM.ClassicFSM('EstateLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'estate', 'house']), + State.State('estate', self.enterEstate, self.exitEstate, ['quietZone']), + State.State('house', self.enterHouse, self.exitHouse, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['house', 'estate']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.musicFile = 'phase_4/audio/bgm/TC_nbrhood.ogg' + self.activityMusicFile = 'phase_3.5/audio/bgm/TC_SZ_activity.ogg' + self.dnaFile = 'phase_5.5/dna/estate_1.pdna' + self.safeZoneStorageDNAFile = None + self.cloudSwitch = 0 + self.id = MyEstate + self.estateOwnerId = None + self.branchZone = None + self.houseDoneEvent = 'houseDone' + self.estateDoneEvent = 'estateDone' + self.enteredHouse = None + self.houseNode = [None] * 6 + self.houseModels = [None] * HouseGlobals.NUM_HOUSE_TYPES + self.houseId2house = {} + self.barrel = None + self.clouds = [] + self.cloudTrack = None + self.sunMoonNode = None + self.fsm.enterInitialState() + return + + def load(self): + SafeZoneLoader.SafeZoneLoader.load(self) + self.music = base.loadMusic('phase_4/audio/bgm/TC_nbrhood.ogg') + self.underwaterSound = base.loadSfx('phase_4/audio/sfx/AV_ambient_water.ogg') + self.swimSound = base.loadSfx('phase_4/audio/sfx/AV_swim_single_stroke.ogg') + self.submergeSound = base.loadSfx('phase_5.5/audio/sfx/AV_jump_in_water.ogg') + self.birdSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.ogg', 'phase_4/audio/sfx/SZ_TC_bird2.ogg', 'phase_4/audio/sfx/SZ_TC_bird3.ogg']) + self.cricketSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.ogg', 'phase_4/audio/sfx/SZ_TC_bird2.ogg', 'phase_4/audio/sfx/SZ_TC_bird3.ogg']) + + def unload(self): + self.ignoreAll() + base.cr.estateMgr.leaveEstate() + self.estateOwnerId = None + self.estateZoneId = None + if self.place: + self.place.exit() + self.place.unload() + del self.place + del self.underwaterSound + del self.swimSound + del self.submergeSound + del self.birdSound + del self.cricketSound + for node in self.houseNode: + node.removeNode() + + del self.houseNode + for model in self.houseModels: + model.removeNode() + + del self.houseModels + del self.houseId2house + if self.sunMoonNode: + self.sunMoonNode.removeNode() + del self.sunMoonNode + self.sunMoonNode = None + if self.clouds: + for cloud in self.clouds: + cloud[0].removeNode() + del cloud[1] + + del self.clouds + if self.barrel: + self.barrel.removeNode() + SafeZoneLoader.SafeZoneLoader.unload(self) + return + + def enter(self, requestStatus): + self.estateOwnerId = requestStatus.get('ownerId', base.localAvatar.doId) + base.localAvatar.inEstate = 1 + self.loadCloudPlatforms() + if base.cloudPlatformsEnabled and 0: + self.setCloudSwitch(1) + if self.cloudSwitch: + self.setCloudSwitch(self.cloudSwitch) + SafeZoneLoader.SafeZoneLoader.enter(self, requestStatus) + + def exit(self): + self.ignoreAll() + base.cr.cache.flush() + base.localAvatar.stopChat() + base.localAvatar.inEstate = 0 + SafeZoneLoader.SafeZoneLoader.exit(self) + + def createSafeZone(self, dnaFile): + SafeZoneLoader.SafeZoneLoader.createSafeZone(self, dnaFile) + self.loadHouses() + self.loadSunMoon() + + def loadHouses(self): + for i in xrange(HouseGlobals.NUM_HOUSE_TYPES): + self.houseModels[i] = loader.loadModel(HouseGlobals.houseModels[i]) + + for i in xrange(6): + posHpr = HouseGlobals.houseDrops[i] + self.houseNode[i] = self.geom.attachNewNode('esHouse_' + str(i)) + self.houseNode[i].setPosHpr(*posHpr) + + def loadSunMoon(self): + self.sun = loader.loadModel('phase_4/models/props/sun.bam') + self.moon = loader.loadModel('phase_5.5/models/props/moon.bam') + self.sunMoonNode = self.geom.attachNewNode('sunMoon') + self.sunMoonNode.setPosHpr(0, 0, 0, 0, 0, 0) + if self.sun: + self.sun.reparentTo(self.sunMoonNode) + self.sun.setY(270) + self.sun.setScale(2) + self.sun.setBillboardPointEye() + if self.moon: + self.moon.reparentTo(self.sunMoonNode) + self.moon.setY(-270) + self.moon.setScale(15) + self.moon.setBillboardPointEye() + self.sunMoonNode.setP(30) + + def enterEstate(self, requestStatus): + self.notify.debug('enterEstate: requestStatus = %s' % requestStatus) + ownerId = requestStatus.get('ownerId') + if ownerId: + self.estateOwnerId = ownerId + zoneId = requestStatus['zoneId'] + self.notify.debug('enterEstate, ownerId = %s, zoneId = %s' % (self.estateOwnerId, zoneId)) + self.accept(self.estateDoneEvent, self.handleEstateDone) + self.place = Estate.Estate(self, self.estateOwnerId, zoneId, self.fsm.getStateNamed('estate'), self.estateDoneEvent) + base.cr.playGame.setPlace(self.place) + self.place.load() + self.place.enter(requestStatus) + self.estateZoneId = zoneId + + def exitEstate(self): + self.notify.debug('exitEstate') + self.ignore(self.estateDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + base.cr.cache.flush() + return + + def handleEstateDone(self, doneStatus = None): + if not doneStatus: + doneStatus = self.place.getDoneStatus() + how = doneStatus['how'] + shardId = doneStatus['shardId'] + hoodId = doneStatus['hoodId'] + zoneId = doneStatus['zoneId'] + avId = doneStatus.get('avId', -1) + ownerId = doneStatus.get('ownerId', -1) + if shardId != None or hoodId != MyEstate: + self.notify.debug('estate done, and we are backing out to a different hood/shard') + self.notify.debug('hoodId = %s, avId = %s' % (hoodId, avId)) + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + return + if how in ['tunnelIn', + 'teleportIn', + 'doorIn', + 'elevatorIn']: + self.notify.debug('staying in estateloader') + self.fsm.request('quietZone', [doneStatus]) + else: + self.notify.error('Exited hood with unexpected mode %s' % how) + return + + def enterHouse(self, requestStatus): + ownerId = requestStatus.get('ownerId') + if ownerId: + self.estateOwnerId = ownerId + self.acceptOnce(self.houseDoneEvent, self.handleHouseDone) + self.place = House.House(self, self.estateOwnerId, self.fsm.getStateNamed('house'), self.houseDoneEvent) + base.cr.playGame.setPlace(self.place) + self.place.load() + self.place.enter(requestStatus) + + def exitHouse(self): + self.ignore(self.houseDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def handleHouseDone(self, doneStatus = None): + if not doneStatus: + doneStatus = self.place.getDoneStatus() + shardId = doneStatus['shardId'] + hoodId = doneStatus['hoodId'] + if shardId != None or hoodId != MyEstate: + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + return + how = doneStatus['how'] + if how in ['tunnelIn', + 'teleportIn', + 'doorIn', + 'elevatorIn']: + self.fsm.request('quietZone', [doneStatus]) + else: + self.notify.error('Exited hood with unexpected mode %s' % how) + return + + def handleQuietZoneDone(self): + status = self.quietZoneStateData.getRequestStatus() + self.fsm.request(status['where'], [status]) + + def atMyEstate(self): + if self.estateOwnerId != None: + if self.estateOwnerId == base.localAvatar.getDoId(): + return 1 + else: + return 0 + else: + self.notify.warning("We aren't in an estate") + return + + def setHouse(self, houseId): + try: + houseDo = base.cr.doId2do[houseId] + self.enteredHouse = houseDo.house + except KeyError: + self.notify.debug("can't find house: %d" % houseId) + + def startCloudPlatforms(self): + return + if len(self.clouds): + self.cloudTrack = self.__cloudTrack() + self.cloudTrack.loop() + + def stopCloudPlatforms(self): + if self.cloudTrack: + self.cloudTrack.pause() + del self.cloudTrack + self.cloudTrack = None + return + + def __cloudTrack(self): + track = Parallel() + for cloud in self.clouds: + axis = cloud[1] + pos = cloud[0].getPos(render) + newPos = pos + axis * 30 + reversePos = pos - axis * 30 + track.append(Sequence(LerpPosInterval(cloud[0], 10, newPos), LerpPosInterval(cloud[0], 20, reversePos), LerpPosInterval(cloud[0], 10, pos))) + + return track + + def debugGeom(self, decomposed): + print 'numPrimitives = %d' % decomposed.getNumPrimitives() + for primIndex in xrange(decomposed.getNumPrimitives()): + prim = decomposed.getPrimitive(primIndex) + print 'prim = %s' % prim + print 'isIndexed = %d' % prim.isIndexed() + print 'prim.getNumPrimitives = %d' % prim.getNumPrimitives() + for basicPrim in xrange(prim.getNumPrimitives()): + print '%d start=%d' % (basicPrim, prim.getPrimitiveStart(basicPrim)) + print '%d end=%d' % (basicPrim, prim.getPrimitiveEnd(basicPrim)) + + def loadOnePlatform(self, version, radius, zOffset, score, multiplier): + self.notify.debug('loadOnePlatform version=%d' % version) + cloud = NodePath('cloud-%d-%d' % (score, multiplier)) + cloudModel = loader.loadModel('phase_5.5/models/estate/bumper_cloud') + cc = cloudModel.copyTo(cloud) + colCube = cc.find('**/collision') + colCube.setName('cloudSphere-0') + dTheta = 2.0 * math.pi / self.numClouds + cloud.reparentTo(self.cloudOrigin) + axes = [Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)] + cloud.setPos(radius * math.cos(version * dTheta), radius * math.sin(version * dTheta), 4 * random.random() + zOffset) + cloud.setScale(4.0) + self.clouds.append([cloud, random.choice(axes)]) + + def loadSkyCollision(self): + plane = CollisionPlane(Plane(Vec3(0, 0, -1), Point3(0, 0, 300))) + plane.setTangible(0) + planeNode = CollisionNode('cloudSphere-0') + planeNode.addSolid(plane) + self.cloudOrigin.attachNewNode(planeNode) + + def loadCloudPlatforms(self): + self.cloudOrigin = self.geom.attachNewNode('cloudOrigin') + self.cloudOrigin.setZ(30) + self.loadSkyCollision() + self.numClouds = 12 + pinballScore = PinballScoring[PinballCloudBumperLow] + for i in xrange(12): + self.loadOnePlatform(i, 40, 0, pinballScore[0], pinballScore[1]) + + pinballScore = PinballScoring[PinballCloudBumperMed] + for i in xrange(12): + self.loadOnePlatform(i, 60, 40, pinballScore[0], pinballScore[1]) + + pinballScore = PinballScoring[PinballCloudBumperHigh] + for i in xrange(12): + self.loadOnePlatform(i, 20, 80, pinballScore[0], pinballScore[1]) + + self.cloudOrigin.stash() + + def setCloudSwitch(self, on): + self.cloudSwitch = on + if hasattr(self, 'cloudOrigin'): + if on: + self.cloudOrigin.unstash() + else: + self.cloudOrigin.stash() diff --git a/toontown/estate/EstateManager.py b/toontown/estate/EstateManager.py new file mode 100755 index 00000000..bc43024f --- /dev/null +++ b/toontown/estate/EstateManager.py @@ -0,0 +1,70 @@ +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +import random +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +import HouseGlobals +import Estate + +class EstateManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('EstateManager') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.availableZones = 0 + self.popupInfo = None + return + + def disable(self): + self.notify.debug("i'm disabling EstateManager rightnow.") + self.ignore('getLocalEstateZone') + self.ignoreAll() + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + DistributedObject.DistributedObject.disable(self) + return + + def allocateMyEstateZone(self): + self.getLocalEstateZone(base.localAvatar.getDoId()) + + def getLocalEstateZone(self, avId): + self.sendUpdate('getEstateZone', [avId]) + + def setEstateZone(self, ownerId, zoneId): + self.notify.debug('setEstateZone(%s, %s)' % (ownerId, zoneId)) + messenger.send('setLocalEstateZone', [ownerId, zoneId]) + + def generate(self): + self.notify.debug('BASE: generate') + DistributedObject.DistributedObject.generate(self) + base.cr.estateMgr = self + self.accept('getLocalEstateZone', self.getLocalEstateZone) + self.announceGenerateName = self.uniqueName('generate') + + def setAvHouseId(self, avId, houseIds): + self.notify.debug('setAvHouseId %d' % base.localAvatar.doId) + for av in base.cr.avList: + if av.id == avId: + houseId = houseIds[av.position] + ownerAv = base.cr.doId2do.get(avId) + if ownerAv: + ownerAv.b_setHouseId(houseId) + return + + def sendAvToPlayground(self, avId, retCode): + self.notify.debug('sendAvToPlayground: %d' % avId) + messenger.send('kickToPlayground', [retCode]) + + def leaveEstate(self): + if self.isDisabled(): + self.notify.warning('EstateManager disabled; unable to leave estate.') + return + self.sendUpdate('exitEstate') + + def removeFriend(self, ownerId, avId): + self.notify.debug('removeFriend ownerId = %s, avId = %s' % (ownerId, avId)) + self.sendUpdate('removeFriend', [ownerId, avId]) diff --git a/toontown/estate/EstateManagerAI.py b/toontown/estate/EstateManagerAI.py new file mode 100755 index 00000000..573ba265 --- /dev/null +++ b/toontown/estate/EstateManagerAI.py @@ -0,0 +1,498 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.fsm.FSM import FSM +from toontown.estate.DistributedEstateAI import DistributedEstateAI +from toontown.estate.DistributedHouseAI import DistributedHouseAI +from toontown.toon import ToonDNA +import HouseGlobals +import functools + +class LoadHouseFSM(FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('LoadHouseFSM') + + def __init__(self, mgr, estate, houseIndex, toon, callback): + FSM.__init__(self, 'LoadHouseFSM') + self.mgr = mgr + self.estate = estate + self.houseIndex = houseIndex + self.toon = toon + self.callback = callback + + self.done = False + + def start(self): + # We have a few different cases here: + if self.toon is None: + # Case #1: There isn't a Toon in that estate slot. Make a blank house. + + # Because this state completes so fast, we'll use taskMgr to delay + # it until the next iteration. This solves re-entrancy problems. + taskMgr.doMethodLater(0.0, self.demand, + 'makeBlankHouse-%s' % id(self), + extraArgs=['MakeBlankHouse']) + return + + self.houseId = self.toon.get('setHouseId', [0])[0] + if self.houseId == 0: + # Case #2: There is a Toon, but no setHouseId. Gotta make one. + self.demand('CreateHouse') + else: + # Case #3: Toon with a setHouseId. Load it. + self.demand('LoadHouse') + + def enterMakeBlankHouse(self): + self.house = DistributedHouseAI(self.mgr.air) + self.house.setHousePos(self.houseIndex) + self.house.setColor(self.houseIndex) + self.house.generateWithRequired(self.estate.zoneId) + self.estate.houses[self.houseIndex] = self.house + self.demand('Off') + + def enterCreateHouse(self): + style = ToonDNA.ToonDNA() + style.makeFromNetString(self.toon['setDNAString'][0]) + + self.mgr.air.dbInterface.createObject( + self.mgr.air.dbId, + self.mgr.air.dclassesByName['DistributedHouseAI'], + { + 'setName' : [self.toon['setName'][0]], + 'setAvatarId' : [self.toon['ID']], + 'setGender': [0 if style.getGender() == 'm' else 1] + }, + self.__handleCreate) + + def __handleCreate(self, doId): + if self.state != 'CreateHouse': + return + + # Update the avatar's houseId: + av = self.mgr.air.doId2do.get(self.toon['ID']) + if av: + av.b_setHouseId(doId) + else: + self.mgr.air.dbInterface.updateObject( + self.mgr.air.dbId, + self.toon['ID'], + self.mgr.air.dclassesByName['DistributedToonAI'], + {'setHouseId': [doId]}) + + self.houseId = doId + self.demand('LoadHouse') + + def enterLoadHouse(self): + # Activate the house: + self.mgr.air.sendActivate(self.houseId, self.mgr.air.districtId, self.estate.zoneId, + self.mgr.air.dclassesByName['DistributedHouseAI'], + {'setHousePos': [self.houseIndex], + 'setColor': [self.houseIndex], + 'setName': [self.toon['setName'][0]], + 'setAvatarId': [self.toon['ID']]}) + + # Now we wait for the house to show up... We do this by hanging a messenger + # hook which the DistributedHouseAI throws once it spawns. + self.acceptOnce('generate-%d' % self.houseId, self.__gotHouse) + + def __gotHouse(self, house): + self.house = house + house.initializeInterior() + + self.estate.houses[self.houseIndex] = self.house + + self.demand('Off') + + def exitLoadHouse(self): + self.ignore('generate-%d' % self.houseId) + + def enterOff(self): + self.done = True + self.callback(self.house) + +class LoadPetFSM(FSM): + def __init__(self, mgr, estate, toon, callback): + FSM.__init__(self, 'LoadPetFSM') + self.mgr = mgr + self.estate = estate + self.toon = toon + self.callback = callback + + self.done = False + + def start(self): + self.petId = self.toon['setPetId'][0] + if not self.petId in self.mgr.air.doId2do: + self.mgr.air.sendActivate(self.petId, self.mgr.air.districtId, self.estate.zoneId) + self.acceptOnce('generate-%d' % self.petId, self.__generated) + else: + self.__generated(self.mgr.air.doId2do[self.petId]) + + def __generated(self, pet): + self.pet = pet + self.estate.pets.append(pet) + self.demand('Off') + + def enterOff(self): + self.done = True + self.callback(self.pet) + +class LoadEstateFSM(FSM): + def __init__(self, mgr, callback): + FSM.__init__(self, 'LoadEstateFSM') + self.mgr = mgr + self.callback = callback + + self.estate = None + + def start(self, accountId, zoneId): + self.accountId = accountId + self.zoneId = zoneId + self.demand('QueryAccount') + + def enterQueryAccount(self): + self.mgr.air.dbInterface.queryObject(self.mgr.air.dbId, self.accountId, + self.__gotAccount) + + def __gotAccount(self, dclass, fields): + if self.state != 'QueryAccount': + return # We must have aborted or something... + + if dclass != self.mgr.air.dclassesByName['AccountAI']: + self.mgr.notify.warning('Account %d has non-account dclass %d!' % + (self.accountId, dclass)) + self.demand('Failure') + return + + self.accountFields = fields + + self.estateId = fields.get('ESTATE_ID', 0) + self.demand('QueryToons') + + def enterQueryToons(self): + self.toonIds = self.accountFields.get('ACCOUNT_AV_SET', [0]*6) + self.toons = {} + + for index, toonId in enumerate(self.toonIds): + if toonId == 0: + self.toons[index] = None + continue + self.mgr.air.dbInterface.queryObject( + self.mgr.air.dbId, toonId, + functools.partial(self.__gotToon, index=index)) + + def __gotToon(self, dclass, fields, index): + if self.state != 'QueryToons': + return # We must have aborted or something... + + if dclass != self.mgr.air.dclassesByName['DistributedToonAI']: + self.mgr.notify.warning('Account %d has avatar %d with non-Toon dclass %d!' % + (self.accountId, self.toonIds[index], dclass)) + self.demand('Failure') + return + + fields['ID'] = self.toonIds[index] + self.toons[index] = fields + if len(self.toons) == 6: + self.__gotAllToons() + + def __gotAllToons(self): + # Okay, we have all of our Toons, now we can proceed with estate! + if self.estateId: + # We already have an estate, load it! + self.demand('LoadEstate') + else: + # We don't have one yet, make one! + self.demand('CreateEstate') + + def enterCreateEstate(self): + # We have to ask the DB server to construct a blank estate object... + self.mgr.air.dbInterface.createObject( + self.mgr.air.dbId, + self.mgr.air.dclassesByName['DistributedEstateAI'], + {}, + self.__handleEstateCreate) + + def __handleEstateCreate(self, estateId): + if self.state != 'CreateEstate': + return # We must have aborted or something... + self.estateId = estateId + self.demand('StoreEstate') + + def enterStoreEstate(self): + # store the estate in account + # congrats however wrote this for forgetting it! + + self.mgr.air.dbInterface.updateObject( + self.mgr.air.dbId, + self.accountId, + self.mgr.air.dclassesByName['AccountAI'], + {'ESTATE_ID': self.estateId}, + {'ESTATE_ID': 0}, + self.__handleStoreEstate) + + def __handleStoreEstate(self, fields): + if fields: + self.notify.warning("Failed to associate Estate %d with account %d, loading anyway." % (self.estateId, self.accountId)) + + self.demand('LoadEstate') + + def enterLoadEstate(self): + # Activate the estate: + fields = {} + for i, toon in enumerate(self.toonIds): + fields['setSlot%dToonId' % i] = (toon,) + + self.mgr.air.sendActivate(self.estateId, self.mgr.air.districtId, self.zoneId, + self.mgr.air.dclassesByName['DistributedEstateAI'], fields) + + # Now we wait for the estate to show up... We do this by hanging a messenger + # hook which the DistributedEstateAI throws once it spawns. + self.acceptOnce('generate-%d' % self.estateId, self.__gotEstate) + + def __gotEstate(self, estate): + self.estate = estate + estate.pets = [] + + # Gotcha! Now we need to load houses: + self.demand('LoadHouses') + + def exitLoadEstate(self): + self.ignore('generate-%d' % self.estateId) + + def enterLoadHouses(self): + self.houseFSMs = [] + + for houseIndex in xrange(6): + fsm = LoadHouseFSM(self.mgr, self.estate, houseIndex, + self.toons[houseIndex], self.__houseDone) + self.houseFSMs.append(fsm) + fsm.start() + + def __houseDone(self, house): + if self.state != 'LoadHouses': + # We aren't loading houses, so we probably got cancelled. Therefore, + # the only sensible thing to do is simply destroy the house. + house.requestDelete() + return + + # A houseFSM just finished! Let's see if all of them are done: + if all(houseFSM.done for houseFSM in self.houseFSMs): + self.demand('LoadPets') + + def enterLoadPets(self): + self.petFSMs = [] + for houseIndex in xrange(6): + toon = self.toons[houseIndex] + if toon and toon['setPetId'][0] != 0: + fsm = LoadPetFSM(self.mgr, self.estate, toon, self.__petDone) + self.petFSMs.append(fsm) + fsm.start() + + if not self.petFSMs: + taskMgr.doMethodLater(0, lambda: self.demand('Finished'), 'nopets', extraArgs=[]) + + def __petDone(self, pet): + if self.state != 'LoadPets': + pet.requestDelete() + return + + # A petFSM just finished! Let's see if all of them are done: + if all(petFSM.done for petFSM in self.petFSMs): + self.demand('Finished') + + def enterFinished(self): + self.callback(True) + + def enterFailure(self): + self.cancel() + + self.callback(False) + + def cancel(self): + if self.estate: + self.estate.destroy() + self.estate = None + + self.demand('Off') + +class EstateManagerAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("EstateManagerAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + + self.estate2toons = {} + self.toon2estate = {} + self.estate2timeout = {} + self.zoneId2owner = {} + + def getEstateZone(self, avId): + senderId = self.air.getAvatarIdFromSender() + accId = self.air.getAccountIdFromSender() + + toon = self.air.doId2do.get(senderId) + if not toon: + self.air.writeServerEvent('suspicious', senderId, 'Sent getEstateZone() but not on district!') + return + + # If there's an avId included, then the Toon is interested in visiting a + # friend. We do NOT load the estate, we simply see if it's already up... + if avId and avId != senderId: + av = self.air.doId2do.get(avId) + if av and av.dclass == self.air.dclassesByName['DistributedToonAI']: + estate = self._lookupEstate(av) + if estate: + # Yep, there it is! + avId = estate.owner.doId + zoneId = estate.zoneId + self._mapToEstate(toon, estate) + self._unloadEstate(toon) # In case they're doing estate->estate TP. + self.sendUpdateToAvatarId(senderId, 'setEstateZone', [avId, zoneId]) + + # Bummer, couldn't find avId at an estate... + self.sendUpdateToAvatarId(senderId, 'setEstateZone', [0, 0]) + return + + # The Toon definitely wants to go to his own estate... + + estate = getattr(toon, 'estate', None) + if estate: + # They already have an estate loaded, so let's just return it: + self._mapToEstate(toon, toon.estate) + self.sendUpdateToAvatarId(senderId, 'setEstateZone', [senderId, estate.zoneId]) + + # If a timeout is active, cancel it: + if estate in self.estate2timeout: + self.estate2timeout[estate].remove() + del self.estate2timeout[estate] + + return + + if getattr(toon, 'loadEstateFSM', None): + # We already have a loading operation underway; ignore this second + # request since the first operation will setEstateZone() when it + # finishes anyway. + return + + zoneId = self.air.allocateZone() + + def estateLoaded(success): + if success: + toon.estate = toon.loadEstateFSM.estate + toon.estate.owner = toon + self._mapToEstate(toon, toon.estate) + self.sendUpdateToAvatarId(senderId, 'setEstateZone', [senderId, zoneId]) + else: + # Estate loading failed??! + self.sendUpdateToAvatarId(senderId, 'setEstateZone', [0, 0]) + + # And I guess we won't need our zoneId anymore... + self.air.deallocateZone(zoneId) + del self.zoneId2owner[zoneId] + + toon.loadEstateFSM = None + + self.acceptOnce(self.air.getAvatarExitEvent(toon.doId), self._unloadEstate, extraArgs=[toon]) + self.zoneId2owner[zoneId] = avId + toon.loadEstateFSM = LoadEstateFSM(self, estateLoaded) + toon.loadEstateFSM.start(accId, zoneId) + + def exitEstate(self): + senderId = self.air.getAvatarIdFromSender() + toon = self.air.doId2do.get(senderId) + + if not toon: + self.air.writeServerEvent('suspicious', senderId, 'Sent exitEstate() but not on district!') + return + + self._unmapFromEstate(toon) + self._unloadEstate(toon) + + def _unloadEstate(self, toon): + if getattr(toon, 'estate', None): + estate = toon.estate + if estate not in self.estate2timeout: + self.estate2timeout[estate] = \ + taskMgr.doMethodLater(HouseGlobals.BOOT_GRACE_PERIOD, + self._cleanupEstate, + estate.uniqueName('emai-cleanup-task'), + extraArgs=[estate]) + self._sendToonsToPlayground(toon.estate, 0) # This is a warning only... + + if getattr(toon, 'loadEstateFSM', None): + self.air.deallocateZone(toon.loadEstateFSM.zoneId) + toon.loadEstateFSM.cancel() + toon.loadEstateFSM = None + + self.ignore(self.air.getAvatarExitEvent(toon.doId)) + + def _cleanupEstate(self, estate): + # Boot all Toons from estate: + self._sendToonsToPlayground(estate, 1) + + # Clean up toon<->estate mappings... + for toon in self.estate2toons.get(estate, []): + try: + del self.toon2estate[toon] + except KeyError: + pass + try: + del self.estate2toons[estate] + except KeyError: + pass + + # Clean up timeout, if it exists: + if estate in self.estate2timeout: + del self.estate2timeout[estate] + + # Destroy estate and unmap from owner: + estate.destroy() + estate.owner.estate = None + + # Destroy pets: + for pet in estate.pets: + pet.requestDelete() + + estate.pets = [] + + # Free estate's zone: + self.air.deallocateZone(estate.zoneId) + del self.zoneId2owner[estate.zoneId] + + def _sendToonsToPlayground(self, estate, reason): + for toon in self.estate2toons.get(estate, []): + self.sendUpdateToAvatarId(toon.doId, 'sendAvToPlayground', + [toon.doId, reason]) + + def _mapToEstate(self, toon, estate): + self._unmapFromEstate(toon) + self.estate2toons.setdefault(estate, []).append(toon) + self.toon2estate[toon] = estate + + if hasattr(toon, 'enterEstate'): + toon.enterEstate(estate.owner.doId, estate.zoneId) + + def _unmapFromEstate(self, toon): + estate = self.toon2estate.get(toon) + if not estate: return + del self.toon2estate[toon] + + try: + self.estate2toons[estate].remove(toon) + except (KeyError, ValueError): + pass + + if hasattr(toon, 'exitEstate'): + toon.exitEstate() + + def _lookupEstate(self, toon): + return self.toon2estate.get(toon) + + def getOwnerFromZone(self, zoneId): + return self.zoneId2owner.get(zoneId, 0) + + def getEstateZones(self, ownerId): + estate = self._lookupEstate(self.air.doId2do.get(ownerId)) + if estate: + return [estate.zoneId] + + return [] diff --git a/toontown/estate/FireworkItemPanel.py b/toontown/estate/FireworkItemPanel.py new file mode 100755 index 00000000..06e60b0c --- /dev/null +++ b/toontown/estate/FireworkItemPanel.py @@ -0,0 +1,48 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.effects import FireworkGlobals +from toontown.effects import Fireworks +import FireworksGui + +class FireworkItemPanel(DirectFrame): + + def __init__(self, itemName, itemNum, *extraArgs): + self.gui = extraArgs[0][0] + self.type = extraArgs[0][1][itemNum] + self.shootEvent = extraArgs[0][2] + self.name = FireworkGlobals.Names[self.type] + DirectFrame.__init__(self, image=DGG.getDefaultDialogGeom(), image_color=(0.75, 0.75, 0.75, 1), image_scale=(0.25, 0, 0.25), relief=None) + self.initialiseoptions(FireworkItemPanel) + self.load() + return + + def load(self): + self.picture = DirectButton(parent=self, image=(DGG.getDefaultDialogGeom(), DGG.getDefaultDialogGeom(), DGG.getDefaultDialogGeom()), relief=None, command=self.__launchFirework, extraArgs=[self.type], image_color=(0.8, 0.9, 1, 1)) + self.picture.setScale(0.2) + self.picture.setPos(0, 0, 0) + self.picture.initialiseoptions(self.picture) + panelWidth = 7 + nameFont = ToontownGlobals.getInterfaceFont() + self.quantityLabel = DirectLabel(parent=self.picture, relief=None, pos=(0, 0, 0.0), scale=0.45, text=self.name, text_scale=0.6, text_fg=(0, 0, 0, 1), text_pos=(0, -.14, 0), text_font=nameFont, text_wordwrap=panelWidth) + return + + def unload(self): + del self.picture + self.quantityLabel.destroy() + del self.quantityLabel + DirectFrame.destroy(self) + + def destroy(self): + self.unload() + + def __launchFirework(self, index): + messenger.send(self.shootEvent, [index]) + + def __dimSky(self): + self.oldSkyScale = base.cr.playGame.hood.loader.sky.getColorScale() + base.cr.playGame.hood.loader.sky.setColorScale(0.3, 0.3, 0.3, 1) + + def __resetSky(self): + base.cr.playGame.hood.loader.sky.setColorScale(self.oldSkyScale) diff --git a/toontown/estate/FireworksGui.py b/toontown/estate/FireworksGui.py new file mode 100755 index 00000000..9199e4bb --- /dev/null +++ b/toontown/estate/FireworksGui.py @@ -0,0 +1,100 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.gui.DirectScrolledList import * +from toontown.toonbase import ToontownGlobals +import FireworkItemPanel +from direct.directnotify import DirectNotifyGlobal +from toontown.effects import FireworkGlobals +from toontown.effects import Fireworks +NUM_ITEMS_SHOWN = 4 + +class FireworksGui(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FireworksGui') + + def __init__(self, doneEvent, shootEvent): + DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=(0, 0.5, 1, 1), geom_scale=(0.43, 1, 1.4), pos=(1.1, 0, 0)) + self.initialiseoptions(FireworksGui) + self.doneEvent = doneEvent + self.shootEvent = shootEvent + self.itemList = [] + self.type = None + self.load() + return + + def load(self): + itemTypes = [0, + 1, + 2, + 3, + 4, + 5] + itemStrings = [] + for i in itemTypes: + itemStrings.append(FireworkGlobals.Names[i]) + + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.panelPicker = DirectScrolledList(parent=self, items=itemStrings, command=self.scrollItem, itemMakeFunction=FireworkItemPanel.FireworkItemPanel, itemMakeExtraArgs=[self, itemTypes, self.shootEvent], numItemsVisible=NUM_ITEMS_SHOWN, incButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_scale=(0.5, 1, -1), incButton_pos=(0, 0, -1.08), incButton_image3_color=Vec4(1, 1, 1, 0.3), decButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_scale=(0.5, 1, 1), decButton_pos=(0, 0, 0.2), decButton_image3_color=Vec4(1, 1, 1, 0.3)) + self.panelPicker.setPos(-.06, 0, 0.42) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(0.15, 0, -0.62), text_scale=0.06, text_pos=(0, -0.1), command=self.__cancel) + buttons.removeNode() + self.hilightColor = VBase4(1, 1, 1, 1) + self.bgColor = VBase4(0.8, 0.8, 0.8, 1) + self.colorButtons = [] + for i in Fireworks.colors.keys(): + color = Fireworks.colors[i] + height = 0.07 + paddedHeight = 0.1 + buttonBg = DirectFrame(self, geom=DGG.getDefaultDialogGeom(), geom_scale=paddedHeight, geom_color=self.bgColor, pos=(0.15, 0, 0.5 - (paddedHeight + 0.025) * i), relief=None) + self.initialiseoptions(buttonBg) + button = DirectButton(buttonBg, image=(DGG.getDefaultDialogGeom(), DGG.getDefaultDialogGeom(), DGG.getDefaultDialogGeom()), relief=None, command=self.__handleColor, extraArgs=[i]) + button.setScale(height) + button.setColor(color) + self.colorButtons.append([button, buttonBg]) + + self.__initColor(0) + return + + def unload(self): + del self.parent + del self.itemList + del self.panelPicker + + def update(self): + pass + + def __cancel(self): + messenger.send(self.doneEvent) + + def __initColor(self, index): + self.colorButtons[index][1]['geom_color'] = self.hilightColor + self.colorButtons[index][1].setScale(1.2) + self.curColor = index + self.fadeColor = 0 + + def __handleColor(self, index): + color = Fireworks.colors[index] + for i in xrange(len(self.colorButtons)): + self.colorButtons[i][1]['geom_color'] = self.bgColor + self.colorButtons[i][1].setScale(1) + + self.colorButtons[index][1].setScale(1.2) + if index == self.curColor: + self.fadeColor = (self.fadeColor + 1) % len(Fireworks.colors) + else: + self.fadeColor = 0 + self.colorButtons[index][1]['geom_color'] = Fireworks.colors[self.fadeColor] + self.curColor = index + + def scrollItem(self): + pass + + def getCurColor(self): + return (self.curColor, self.fadeColor) diff --git a/toontown/estate/FlowerBase.py b/toontown/estate/FlowerBase.py new file mode 100755 index 00000000..bb12d1a2 --- /dev/null +++ b/toontown/estate/FlowerBase.py @@ -0,0 +1,50 @@ +import GardenGlobals +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal + +class FlowerBase: + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerBase') + + def __init__(self, species, variety): + self.species = species + self.variety = variety + if self.species not in GardenGlobals.PlantAttributes.keys(): + print 'remove me when everyone is updated' + self.species = 56 + species = 56 + + def getSpecies(self): + return self.species + + def setSpecies(self, species): + self.species = species + + def getVariety(self): + return self.variety + + def setVariety(self, variety): + self.variety = variety + + def getVitals(self): + return (self.species, self.variety) + + def getValue(self): + return GardenGlobals.PlantAttributes[self.species]['varieties'][self.variety][2] + + def getSpeciesName(self): + return TTLocalizer.FlowerSpeciesNames[self.species] + + def getVarietyName(self): + return self.getFullName() + + def getFullName(self): + return GardenGlobals.getFlowerVarietyName(self.species, self.variety) + + def getFullNameWithRecipe(self): + name = GardenGlobals.getFlowerVarietyName(self.species, self.variety) + recipeKey = GardenGlobals.PlantAttributes[self.species]['varieties'][self.variety][0] + name += ' (%s)' % GardenGlobals.Recipes[recipeKey]['beans'] + return name + + def __str__(self): + return '%s, value: %s' % (self.getFullName(), self.getValue()) diff --git a/toontown/estate/FlowerBasket.py b/toontown/estate/FlowerBasket.py new file mode 100755 index 00000000..6f08102f --- /dev/null +++ b/toontown/estate/FlowerBasket.py @@ -0,0 +1,73 @@ +import GardenGlobals +from direct.directnotify import DirectNotifyGlobal +import FlowerBase + +class FlowerBasket: + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerBasket') + + def __init__(self): + self.flowerList = [] + + def __len__(self): + return len(self.flowerList) + + def getFlower(self): + return self.flowerList + + def makeFromNetLists(self, speciesList, varietyList): + self.flowerList = [] + for species, variety in zip(speciesList, varietyList): + self.flowerList.append(FlowerBase.FlowerBase(species, variety)) + + def getNetLists(self): + speciesList = [] + varietyList = [] + for flower in self.flowerList: + speciesList.append(flower.getSpecies()) + varietyList.append(flower.getVariety()) + + return [speciesList, varietyList] + + def hasFlower(self, species, variety): + for flower in self.flowerList: + if flower.getSpecies() == species and flower.getVariety() == variety: + return 1 + + return 0 + + def addFlower(self, species, variety): + self.flowerList.append(FlowerBase.FlowerBase(species, variety)) + return 1 + + def removeFishAtIndex(self, index): + if index >= len(self.flowerList): + return 0 + else: + del self.flowerList[i] + return 1 + + def generateRandomBasket(self): + import random + numFish = random.randint(1, 20) + self.flowerList = [] + for i in xrange(numFish): + species, variety = GardenGlobals.getRandomFlower() + self.addFlower(species, variety) + + def getTotalValue(self): + value = 0 + for flower in self.flowerList: + value += flower.getValue() + + return value + + def __str__(self): + numFlower = len(self.flowerList) + value = 0 + txt = 'Flower Basket (%s flower):' % numFlower + for flower in self.flowerList: + txt += '\n' + str(flower) + + value = self.getTotalValue() + txt += '\nTotal value: %s' % value + return txt diff --git a/toontown/estate/FlowerBrowser.py b/toontown/estate/FlowerBrowser.py new file mode 100755 index 00000000..9589e389 --- /dev/null +++ b/toontown/estate/FlowerBrowser.py @@ -0,0 +1,58 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer +import FlowerSpeciesPanel +import GardenGlobals + +class FlowerBrowser(DirectScrolledList): + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerBrowser') + + def __init__(self, parent = aspect2d, **kw): + self.parent = parent + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + optiondefs = (('parent', self.parent, None), + ('relief', None, None), + ('incButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('incButton_relief', None, None), + ('incButton_scale', (1.3, 1.3, -1.3), None), + ('incButton_pos', (0, 0, -0.525), None), + ('incButton_image3_color', Vec4(0.8, 0.8, 0.8, 0.5), None), + ('decButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('decButton_relief', None, None), + ('decButton_scale', (1.3, 1.3, 1.3), None), + ('decButton_pos', (0, 0, 0.525), None), + ('decButton_image3_color', Vec4(0.8, 0.8, 0.8, 0.5), None), + ('numItemsVisible', 1, None), + ('items', map(str, GardenGlobals.getFlowerSpecies()), None), + ('scrollSpeed', 4, None), + ('itemMakeFunction', FlowerSpeciesPanel.FlowerSpeciesPanel, None), + ('itemMakeExtraArgs', base.localAvatar.flowerCollection, None)) + gui.removeNode() + self.defineoptions(kw, optiondefs) + DirectScrolledList.__init__(self, parent) + self.initialiseoptions(FlowerBrowser) + return None + + def destroy(self): + DirectScrolledList.destroy(self) + self.parent = None + return + + def update(self): + pass + + def show(self): + self['items'][self.index].show() + DirectScrolledList.show(self) + + def hide(self): + self['items'][self.index].hide() + DirectScrolledList.hide(self) diff --git a/toontown/estate/FlowerCollection.py b/toontown/estate/FlowerCollection.py new file mode 100755 index 00000000..ce8ad26e --- /dev/null +++ b/toontown/estate/FlowerCollection.py @@ -0,0 +1,74 @@ +import GardenGlobals +from direct.directnotify import DirectNotifyGlobal +import FlowerBase + +class FlowerCollection: + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerCollection') + + def __init__(self): + self.flowerlist = [] + + def __len__(self): + return len(self.flowerlist) + + def getFlower(self): + return self.flowerlist + + def makeFromNetLists(self, speciesList, varietyList): + self.flowerlist = [] + for species, variety in zip(speciesList, varietyList): + self.flowerlist.append(FlowerBase.FlowerBase(species, variety)) + + def getNetLists(self): + speciesList = [] + varietyList = [] + for flower in self.flowerlist: + speciesList.append(flower.getSpecies()) + varietyList.append(flower.getVariety()) + + return [speciesList, varietyList] + + def hasFlower(self, species, variety): + for flower in self.flowerlist: + if flower.getSpecies() == species and flower.getVariety() == variety: + return 1 + + return 0 + + def hasSpecies(self, species): + for flower in self.flowerlist: + if flower.getSpecies() == species: + return 1 + + return 0 + + def getInitialVariety(self, species): + retVal = 100000 + for flower in self.flowerlist: + if flower.getSpecies() == species: + if flower.getVariety() < retVal: + retVal = flower.getVariety() + + if retVal == 100000: + retVal = 0 + return retVal + + def __collect(self, newFlower, updateCollection): + for flower in self.flowerlist: + if flower.getVariety() == newFlower.getVariety() and flower.getSpecies() == newFlower.getSpecies(): + return GardenGlobals.COLLECT_NO_UPDATE + + if updateCollection: + self.flowerlist.append(newFlower) + return GardenGlobals.COLLECT_NEW_ENTRY + + def collectFlower(self, newFlower): + return self.__collect(newFlower, updateCollection=1) + + def __str__(self): + numFlower = len(self.flowerlist) + txt = 'Flower Collection (%s flowers):' % numFlower + for flower in self.flowerlist: + txt += '\n' + str(flower) + + return txt diff --git a/toontown/estate/FlowerPanel.py b/toontown/estate/FlowerPanel.py new file mode 100755 index 00000000..09788dde --- /dev/null +++ b/toontown/estate/FlowerPanel.py @@ -0,0 +1,95 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer +from direct.interval.IntervalGlobal import * +import GardenGlobals +import FlowerPhoto + +class FlowerPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerPanel') + + def __init__(self, flower = None, parent = aspect2d, doneEvent = None, **kw): + optiondefs = (('relief', None, None), + ('state', DGG.DISABLED, None), + ('image', DGG.getDefaultDialogGeom(), None), + ('image_color', ToontownGlobals.GlobalDialogColor, None), + ('image_scale', (0.65, 1, 0.85), None), + ('text', '', None), + ('text_scale', 0.06, None), + ('text_fg', (0, 0, 0, 1), None), + ('text_pos', (0, 0.35, 0), None), + ('text_font', ToontownGlobals.getInterfaceFont(), None), + ('text_wordwrap', 13.5, None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.initialiseoptions(FlowerPanel) + self.doneEvent = doneEvent + self.flower = flower + self.parent = parent + self.photo = None + return + + def destroy(self): + if self.photo: + self.photo.destroy() + self.photo = None + self.flower = None + DirectFrame.destroy(self) + self.parent = None + return + + def load(self): + self.weight = DirectLabel(parent=self, pos=(0, 0, -0.28), relief=None, state=DGG.NORMAL, text='', text_scale=0.05, text_fg=(0, 0, 0, 1), text_pos=(0, 0.0, 0), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=10.5) + self.value = DirectLabel(parent=self, pos=TTLocalizer.FPvaluePos, relief=None, state=DGG.NORMAL, text='', text_scale=TTLocalizer.FPvalue, text_fg=(0, 0, 0, 1), text_pos=(0, 0, 0), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=10.5) + self.mystery = DirectLabel(parent=self, pos=(-0.025, 0, -0.055), relief=None, state=DGG.NORMAL, text='?', text_scale=0.25, text_fg=(0, 0, 0, 1), text_pos=(0, 0, 0), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=10.5) + self.extraLabel = DirectLabel(parent=self, relief=None, state=DGG.NORMAL, text='', text_fg=(0.2, 0.8, 0.4, 1), text_font=ToontownGlobals.getSignFont(), text_scale=0.08, pos=(0, 0, 0.26)) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.cancel = DirectButton(parent=self, pos=(0.275, 0, -0.375), relief=None, state=DGG.NORMAL, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(0.6, 1, 0.6), command=self.handleCancel) + buttons.removeNode() + self.photo = FlowerPhoto.FlowerPhoto(parent=self) + self.update(self.flower) + return + + def update(self, flower): + self.flower = flower + if self.flower == None: + return + self['text'] = self.flower.getFullName() + value = self.flower.getValue() + if value == 1: + self.value['text'] = TTLocalizer.GardenPageValueS % value + else: + self.value['text'] = TTLocalizer.GardenPageValueP % value + self.photo.update(flower.getSpecies(), flower.getVariety()) + return + + def setSwimBounds(self, *bounds): + self.swimBounds = bounds + + def setSwimColor(self, *colors): + self.swimColor = colors + + def handleCancel(self): + self.hide() + if self.doneEvent: + messenger.send(self.doneEvent) + + def show(self, code = GardenGlobals.FlowerItem): + messenger.send('wakeup') + apply(self.photo.setSwimBounds, self.swimBounds) + apply(self.photo.setSwimColor, self.swimColor) + if code == GardenGlobals.FlowerItem: + self.extraLabel.hide() + elif code == GardenGlobals.FlowerItemNewEntry: + self.extraLabel.show() + self.extraLabel['text'] = TTLocalizer.FloweringNewEntry + self.extraLabel['text_scale'] = 0.08 + self.extraLabel.setPos(0, 0, 0.26) + self.photo.show() + DirectFrame.show(self) + + def hide(self): + self.photo.hide() + DirectFrame.hide(self) diff --git a/toontown/estate/FlowerPhoto.py b/toontown/estate/FlowerPhoto.py new file mode 100755 index 00000000..5d20ee97 --- /dev/null +++ b/toontown/estate/FlowerPhoto.py @@ -0,0 +1,202 @@ +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.fishing import FishGlobals +import GardenGlobals +from direct.actor import Actor + +class DirectRegion(NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('DirectRegion') + + def __init__(self, parent = aspect2d): + NodePath.__init__(self) + self.assign(parent.attachNewNode('DirectRegion')) + + def destroy(self): + self.unload() + self.parent = None + return + + def setBounds(self, *bounds): + self.bounds = bounds + + def setColor(self, *colors): + self.color = colors + + def show(self): + pass + + def hide(self): + pass + + def load(self): + if not hasattr(self, 'cRender'): + self.cRender = NodePath('fishSwimRender') + self.fishSwimCamera = self.cRender.attachNewNode('fishSwimCamera') + self.cCamNode = Camera('fishSwimCam') + self.cLens = PerspectiveLens() + self.cLens.setFov(40, 40) + self.cLens.setNear(0.1) + self.cLens.setFar(100.0) + self.cCamNode.setLens(self.cLens) + self.cCamNode.setScene(self.cRender) + self.fishSwimCam = self.fishSwimCamera.attachNewNode(self.cCamNode) + cm = CardMaker('displayRegionCard') + apply(cm.setFrame, self.bounds) + self.card = card = self.attachNewNode(cm.generate()) + apply(card.setColor, self.color) + newBounds = card.getTightBounds() + ll = render2d.getRelativePoint(card, newBounds[0]) + ur = render2d.getRelativePoint(card, newBounds[1]) + newBounds = [ll.getX(), + ur.getX(), + ll.getZ(), + ur.getZ()] + newBounds = map(lambda x: max(0.0, min(1.0, (x + 1.0) / 2.0)), newBounds) + self.cDr = base.win.makeDisplayRegion(*newBounds) + self.cDr.setSort(10) + self.cDr.setClearColor(card.getColor()) + self.cDr.setClearDepthActive(1) + self.cDr.setClearColorActive(1) + self.cDr.setCamera(self.fishSwimCam) + return self.cRender + + def unload(self): + if hasattr(self, 'cRender'): + base.win.removeDisplayRegion(self.cDr) + del self.cRender + del self.fishSwimCamera + del self.cCamNode + del self.cLens + del self.fishSwimCam + del self.cDr + + +class FlowerPhoto(NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerPhoto') + + def __init__(self, species = None, variety = None, parent = aspect2d): + NodePath.__init__(self) + self.assign(parent.attachNewNode('FlowerPhoto')) + self.species = species + self.variety = variety + self.actor = None + self.sound = None + self.soundTrack = None + self.track = None + self.flowerFrame = None + return + + def destroy(self): + self.hide() + if hasattr(self, 'background'): + del self.background + self.fish = None + del self.soundTrack + del self.track + self.parent = None + return + + def update(self, species, variety): + self.species = species + self.variety = variety + + def setSwimBounds(self, *bounds): + self.swimBounds = bounds + + def setSwimColor(self, *colors): + self.swimColor = colors + + def load(self): + pass + + def makeFlowerFrame(self, actor): + actor.setDepthTest(1) + actor.setDepthWrite(1) + if not hasattr(self, 'flowerDisplayRegion'): + self.flowerDisplayRegion = DirectRegion(parent=self) + apply(self.flowerDisplayRegion.setBounds, self.swimBounds) + apply(self.flowerDisplayRegion.setColor, self.swimColor) + frame = self.flowerDisplayRegion.load() + pitch = frame.attachNewNode('pitch') + rotate = pitch.attachNewNode('rotate') + scale = rotate.attachNewNode('scale') + actor.reparentTo(scale) + bMin, bMax = actor.getTightBounds() + center = (bMin + bMax) / 2.0 + actor.setPos(-center[0], -center[1], -center[2]) + attrib = GardenGlobals.PlantAttributes[self.species] + if 'photoPos' in attrib: + self.notify.debug('oldPos = %s' % actor.getPos()) + photoPos = attrib['photoPos'] + self.notify.debug('newPos = %s' % str(photoPos)) + actor.setPos(photoPos[0], photoPos[1], photoPos[2]) + scale.setScale(attrib['photoScale']) + rotate.setH(attrib['photoHeading']) + pitch.setP(attrib['photoPitch']) + pitch.setY(1.75) + return frame + + def loadModel(self, species, variety): + modelName = GardenGlobals.PlantAttributes[species]['fullGrownModel'] + nodePath = loader.loadModel(modelName) + desat = None + flowerColorIndex = GardenGlobals.PlantAttributes[species]['varieties'][variety][1] + colorTuple = GardenGlobals.FlowerColors[flowerColorIndex] + useWilted = 0 + wilt = nodePath.find('**/*wilt*') + bloom = nodePath.find('**/*bloom*') + if useWilted: + wilt.show() + desat = wilt.find('**/*desat*') + bloom.hide() + else: + bloom.show() + desat = bloom.find('**/*desat*') + wilt.hide() + if desat and not desat.isEmpty(): + desat.setColorScale(colorTuple[0], colorTuple[1], colorTuple[2], 1.0) + else: + nodePath.setColorScale(colorTuple[0], colorTuple[1], colorTuple[2], 1.0) + return nodePath + + def show(self, showBackground = 0): + self.notify.debug('show') + messenger.send('wakeup') + if self.flowerFrame: + if hasattr(self.actor, 'cleanup'): + self.actor.cleanup() + if hasattr(self, 'flowerDisplayRegion'): + self.flowerDisplayRegion.unload() + self.hide() + self.actor = self.loadModel(self.species, self.variety) + self.flowerFrame = self.makeFlowerFrame(self.actor) + if showBackground: + if not hasattr(self, 'background'): + background = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + background = background.find('**/Fish_BG') + self.background = background + self.background.setPos(0, 15, 0) + self.background.setScale(11) + self.background.reparentTo(self.flowerFrame) + + def hide(self): + if hasattr(self, 'flowerDisplayRegion'): + self.flowerDisplayRegion.unload() + if self.actor: + if hasattr(self.actor, 'stop'): + self.actor.stop() + self.actor.hide() + if self.sound: + self.sound.stop() + self.sound = None + if self.soundTrack: + self.soundTrack.pause() + self.soundTrack = None + if self.track: + self.track.pause() + self.track = None + return + + def changeVariety(self, variety): + self.variety = variety diff --git a/toontown/estate/FlowerPicker.py b/toontown/estate/FlowerPicker.py new file mode 100755 index 00000000..d72370eb --- /dev/null +++ b/toontown/estate/FlowerPicker.py @@ -0,0 +1,126 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer +import FlowerPanel + +class FlowerPicker(DirectScrolledList): + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerPicker') + + def __init__(self, parent = aspect2d, **kw): + self.flowerList = [] + self.parent = parent + self.shown = 0 + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + optiondefs = (('parent', self.parent, None), + ('relief', None, None), + ('incButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('incButton_relief', None, None), + ('incButton_scale', (1.6, 1.6, -1.6), None), + ('incButton_pos', (0.16, 0, -0.47), None), + ('incButton_image3_color', Vec4(0.7, 0.7, 0.7, 0.75), None), + ('decButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('decButton_relief', None, None), + ('decButton_scale', (1.6, 1.6, 1.6), None), + ('decButton_pos', (0.16, 0, 0.09), None), + ('decButton_image3_color', Vec4(0.7, 0.7, 0.7, 0.75), None), + ('itemFrame_pos', (-0.025, 0, 0), None), + ('itemFrame_scale', 0.54, None), + ('itemFrame_relief', None, None), + ('itemFrame_frameSize', (-0.05, + 0.75, + -0.75, + 0.05), None), + ('numItemsVisible', 10, None), + ('items', [], None)) + self.defineoptions(kw, optiondefs) + DirectScrolledList.__init__(self, parent) + self.initialiseoptions(FlowerPicker) + self.flowerGui = loader.loadModel('phase_3.5/models/gui/fishingBook').find('**/bucket') + self.flowerGui.find('**/fram1').removeNode() + self.flowerGui.find('**/bubble').removeNode() + self.flowerGui.find('**/bucket').removeNode() + self.flowerGui.reparentTo(self, -1) + self.flowerGui.setPos(0.63, 0.1, -0.1) + self.flowerGui.setScale(0.035) + self.basketGui = loader.loadModel('phase_5.5/models/estate/flowerBasket') + self.basketGui.reparentTo(self, -2) + self.basketGui.setPos(0.17, 0.1, 0.0) + self.basketGui.setScale(1.25) + self.info = DirectLabel(parent=self, relief=None, text='', text_scale=TTLocalizer.FPinfo, pos=(0.18, 0, -0.67)) + self.flowerPanel = FlowerPanel.FlowerPanel(parent=self) + self.flowerPanel.setSwimBounds(-0.3, 0.3, -0.235, 0.25) + self.flowerPanel.setSwimColor(1.0, 1.0, 0.74901, 1.0) + gui.removeNode() + return None + + def destroy(self): + DirectScrolledList.destroy(self) + self.parent = None + self.flowerList = [] + self.flowerPanel = None + return + + def hideFlowerPanel(self): + self.flowerPanel.hide() + + def hide(self): + if not hasattr(self, 'loaded'): + return + self.hideFlowerPanel() + DirectScrolledList.hide(self) + self.shown = 0 + + def show(self): + if not hasattr(self, 'loaded'): + self.load() + self.updatePanel() + DirectScrolledList.show(self) + self.shown = 1 + + def load(self): + self.loaded = 1 + self.flowerPanel.load() + self.flowerPanel.setPos(1.05, 0, 0.1) + self.flowerPanel.setScale(0.9) + + def update(self, newFloweres): + for flower, flowerButton in self.flowerList[:]: + self.removeItem(flowerButton) + flowerButton.destroy() + self.flowerList.remove([flower, flowerButton]) + + for flower in newFloweres: + flowerButton = self.makeFlowerButton(flower) + self.addItem(flowerButton) + self.flowerList.append([flower, flowerButton]) + + value = 0 + for flower in newFloweres: + value += flower.getValue() + + maxFlower = base.localAvatar.getMaxFlowerBasket() + self.info['text'] = TTLocalizer.FlowerPickerTotalValue % (len(newFloweres), maxFlower, value) + if self.shown: + self.updatePanel() + + def updatePanel(self): + if len(self.flowerList) >= 1: + self.showFlowerPanel(self.flowerList[0][0]) + else: + self.hideFlowerPanel() + + def makeFlowerButton(self, flower): + return DirectScrolledListItem(parent=self, relief=None, text=flower.getFullName(), text_scale=0.07, text_align=TextNode.ALeft, text1_fg=Vec4(1, 1, 0, 1), text2_fg=Vec4(0.5, 0.9, 1, 1), text3_fg=Vec4(0.4, 0.8, 0.4, 1), command=self.showFlowerPanel, extraArgs=[flower]) + + def showFlowerPanel(self, flower): + self.flowerPanel.update(flower) + self.flowerPanel.show() + messenger.send('wakeup') diff --git a/toontown/estate/FlowerSellGUI.py b/toontown/estate/FlowerSellGUI.py new file mode 100755 index 00000000..5f0f07ee --- /dev/null +++ b/toontown/estate/FlowerSellGUI.py @@ -0,0 +1,51 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.task import Task +import FlowerBase +import FlowerPicker + +class FlowerSellGUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerGui') + + def __init__(self, doneEvent): + DirectFrame.__init__(self, relief=None, state='normal', geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(2.0, 1, 1.5), frameSize=(-1, 1, -1, 1), pos=(0, 0, 0), text='', text_wordwrap=26, text_scale=TTLocalizer.FSGUIdirectFrame, text_pos=(0, 0.65)) + self.initialiseoptions(FlowerSellGUI) + self.doneEvent = doneEvent + self.picker = FlowerPicker.FlowerPicker(self) + self.picker.load() + self.picker.setPos(-0.59, 0, 0.03) + self.picker.setScale(0.93) + newBasketFlower = base.localAvatar.flowerBasket.getFlower() + self.picker.update(newBasketFlower) + self.picker.show() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(0.3, 0, -0.58), text=TTLocalizer.FlowerGuiCancel, text_scale=TTLocalizer.FSGUIcancelButton, text_pos=(0, -0.1), command=self.__cancel) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, pos=(0.6, 0, -0.58), text=TTLocalizer.FlowerGuiOk, text_scale=TTLocalizer.FSGUIokButton, text_pos=(0, -0.1), command=self.__sellFlower) + buttons.removeNode() + self.__updateFlowerValue() + base.cr.playGame.getPlace().detectedFlowerSellUse() + return + + def destroy(self): + DirectFrame.destroy(self) + base.cr.playGame.getPlace().detectedFlowerSellDone() + + def __cancel(self): + messenger.send(self.doneEvent, [0]) + + def __sellFlower(self): + messenger.send(self.doneEvent, [1]) + + def __updateFlowerValue(self): + flowerBasket = base.localAvatar.getFlowerBasket() + num = len(flowerBasket) + value = flowerBasket.getTotalValue() + self['text'] = TTLocalizer.FlowerBasketValue % {'name': base.localAvatar.getName(), + 'num': num, + 'value': value} + self.setText() diff --git a/toontown/estate/FlowerSpeciesPanel.py b/toontown/estate/FlowerSpeciesPanel.py new file mode 100755 index 00000000..8e14f921 --- /dev/null +++ b/toontown/estate/FlowerSpeciesPanel.py @@ -0,0 +1,153 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer +import GardenGlobals +import FlowerPhoto +from toontown.estate import BeanRecipeGui + +class FlowerSpeciesPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerSpeciesPanel') + + def __init__(self, species = None, itemIndex = 0, *extraArgs): + flowerGui = loader.loadModel('phase_3.5/models/gui/fishingBook') + albumGui = flowerGui.find('**/photo_frame1') + pictureGroup = albumGui.attachNewNode('PictureGroup') + hideList = ['corner_backs', + 'shadow', + 'bg', + 'corners', + 'picture'] + for name in hideList: + temp = flowerGui.find('**/%s' % name) + if not temp.isEmpty(): + temp.wrtReparentTo(pictureGroup) + + pictureGroup.setPos(0, 0, 1.0) + albumGui.find('**/arrows').removeNode() + optiondefs = (('relief', None, None), + ('state', DGG.NORMAL, None), + ('image', albumGui, None), + ('image_scale', (0.025, 0.025, 0.025), None), + ('image_pos', (0, 1, 0), None), + ('text', TTLocalizer.FlowerUnknown, None), + ('text_scale', 0.065, None), + ('text_fg', (0.2, 0.1, 0.0, 1), None), + ('text_pos', (-0.5, -0.34), None), + ('text_font', ToontownGlobals.getInterfaceFont(), None), + ('text_wordwrap', 13.5, None), + ('text_align', TextNode.ALeft, None)) + self.defineoptions({}, optiondefs) + DirectFrame.__init__(self) + self.initialiseoptions(FlowerSpeciesPanel) + self.flowerPanel = None + self.species = None + self.variety = 0 + self.flowerCollection = extraArgs[0] + self.setSpecies(int(species)) + self.setScale(1.2) + albumGui.removeNode() + self.beanRecipeGui = None + return + + def destroy(self): + if self.flowerPanel: + self.flowerPanel.destroy() + del self.flowerPanel + self.flowerCollection = None + self.cleanupBeanRecipeGui() + DirectFrame.destroy(self) + return + + def load(self): + pass + + def setSpecies(self, species): + if self.species == species: + return + self.species = species + if self.species != None: + if self.flowerPanel: + self.flowerPanel.destroy() + varietyToUse = self.flowerCollection.getInitialVariety(self.species) + self.variety = varietyToUse + self.flowerPanel = FlowerPhoto.FlowerPhoto(species=self.species, variety=varietyToUse, parent=self) + zAdj = 0.0131 + xAdj = -0.002 + self.flowerPanel.setPos(-0.229 + xAdj, 1, -0.01 + zAdj) + self.flowerPanel.setSwimBounds(-0.2461, 0.2367, -0.207 + zAdj, 0.2664 + zAdj) + self.flowerPanel.setSwimColor(0.75, 0.75, 0.75, 1.0) + varietyList = GardenGlobals.getFlowerVarieties(self.species) + self.speciesLabels = [] + offset = 0.075 + startPos = len(varietyList) / 2 * offset + if not len(varietyList) % 2: + startPos -= offset / 2 + for variety in xrange(len(varietyList)): + label = DirectButton(parent=self, frameSize=(0, + 0.445, + -0.02, + 0.04), relief=None, state=DGG.DISABLED, pos=(0.06, 0, startPos - variety * offset), text=TTLocalizer.FlowerUnknown, text_fg=(0.2, 0.1, 0.0, 1), text_scale=(0.045, 0.045, 0.45), text_align=TextNode.ALeft, text_font=ToontownGlobals.getInterfaceFont(), command=self.changeVariety, extraArgs=[variety], text1_bg=Vec4(1, 1, 0, 1), text2_bg=Vec4(0.5, 0.9, 1, 1), text3_fg=Vec4(0.4, 0.8, 0.4, 1)) + self.speciesLabels.append(label) + + return + + def show(self): + self.update() + DirectFrame.show(self) + + def hide(self): + if self.flowerPanel is not None: + self.flowerPanel.hide() + if self.beanRecipeGui is not None: + self.beanRecipeGui.hide() + DirectFrame.hide(self) + return + + def showRecipe(self): + if base.localAvatar.flowerCollection.hasSpecies(self.species): + self['text'] = TTLocalizer.FlowerSpeciesNames[self.species] + if base.localAvatar.flowerCollection.hasFlower(self.species, self.variety): + name = GardenGlobals.getFlowerVarietyName(self.species, self.variety) + recipeKey = GardenGlobals.PlantAttributes[self.species]['varieties'][self.variety][0] + self['text'] = name + self.createBeanRecipeGui(GardenGlobals.Recipes[recipeKey]['beans']) + else: + self.cleanupBeanRecipeGui() + else: + self['text'] = TTLocalizer.FlowerUnknown + self.cleanupBeanRecipeGui() + + def update(self): + if base.localAvatar.flowerCollection.hasSpecies(self.species): + self.flowerPanel.show(showBackground=0) + self['text'] = TTLocalizer.FlowerSpeciesNames[self.species] + for variety in xrange(len(GardenGlobals.getFlowerVarieties(self.species))): + if base.localAvatar.flowerCollection.hasFlower(self.species, variety): + name = GardenGlobals.getFlowerVarietyName(self.species, variety) + self.speciesLabels[variety]['text'] = name + self.speciesLabels[variety]['state'] = DGG.NORMAL + + self.showRecipe() + + def changeVariety(self, variety): + self.variety = variety + self.flowerPanel.changeVariety(variety) + self.flowerPanel.show() + self.showRecipe() + + def createBeanRecipeGui(self, recipe): + if self.beanRecipeGui: + self.beanRecipeGui.destroy() + pos1 = (-0.2, 0, -0.365) + pos2 = (-0.46, 0, 0.3) + pos3 = (-0.46, 0, -0.3) + pos4 = (-0.6, 0, -0.27) + self.beanRecipeGui = BeanRecipeGui.BeanRecipeGui(aspect2dp, recipe, pos=pos4, scale=1.3, frameColor=(0.8, 0.8, 0.8, 1.0)) + + def cleanupBeanRecipeGui(self): + if self.beanRecipeGui: + self.beanRecipeGui.destroy() + self.beanRecipeGui = None + return diff --git a/toontown/estate/GameSprite.py b/toontown/estate/GameSprite.py new file mode 100755 index 00000000..5d68032b --- /dev/null +++ b/toontown/estate/GameSprite.py @@ -0,0 +1,100 @@ +import math + +class GameSprite: + + colorRed = (1, 0, 0, 1) + colorBlue = (0, 0, 1, 1) + colorGreen = (0, 1, 0, 1) + colorYellow = (1, 1, 0, 1) + colorPurple = (1, 0, 1, 1) + colorGhostRed = (1, 0, 0, 0.5) + colorGhostBlue = (0, 0, 1, 0.5) + colorGhostGreen = (0, 1, 0, 0.5) + colorGhostYellow = (1, 1, 0, 0.5) + colorGhostPurple = (1, 0, 1, 0.5) + colorWhite = (1, 1, 1, 1) + colorBlack = (0.5, 0.5, 0.5, 1.0) + colorShadow = (0, 0, 0, 0.5) + + def __init__(self, nodeObj, colorType = 0, foundation = 0): + self.nodeObj = nodeObj + self.foundation = foundation + self.velX = 0 + self.velZ = 0 + self.prevX = 0 + self.prevZ = 0 + self.isActive = 1 + self.size = 0.04 + self.isQue = 0 + self.colorType = colorType + if not foundation: + if colorType == 0: + self.setColor(GameSprite.colorGhostRed) + elif colorType == 1: + self.setColor(GameSprite.colorGhostBlue) + elif colorType == 2: + self.setColor(GameSprite.colorGhostGreen) + elif colorType == 3: + self.setColor(GameSprite.colorGhostYellow) + elif colorType == 4: + self.setColor(GameSprite.colorGhostPurple) + elif colorType == 0: + self.setColor(GameSprite.colorRed) + elif colorType == 1: + self.setColor(GameSprite.colorBlue) + elif colorType == 2: + self.setColor(GameSprite.colorGreen) + elif colorType == 3: + self.setColor(GameSprite.colorYellow) + elif colorType == 4: + self.setColor(GameSprite.colorPurple) + self.markedForDeath = 0 + + def delete(self): + self.nodeObj.destroy() + + def run(self, timeDelta): + if self.isActive and not self.isQue: + self.prevX = self.nodeObj.getX() + self.prevZ = self.nodeObj.getZ() + self.setX(self.getX() + self.velX * timeDelta) + self.setZ(self.getZ() + self.velZ * timeDelta) + self.velX = self.velX * (1 - timeDelta * 4) + self.velZ = self.velZ * (1 - timeDelta * 4) + + def setActive(self, active): + if active: + self.isActive = 1 + else: + self.isActive = 0 + self.velX = 0 + self.velZ = 0 + + def getX(self): + return self.nodeObj.getX() + + def getZ(self): + return self.nodeObj.getZ() + + def setX(self, x): + self.prevX = self.nodeObj.getX() + self.nodeObj.setX(x) + + def setZ(self, z): + self.prevZ = self.nodeObj.getZ() + self.nodeObj.setZ(z) + + def addForce(self, force, direction): + if self.isActive: + forceX = math.cos(direction) * force + forceZ = math.sin(direction) * force + self.velX += forceX + self.velZ += forceZ + + def setColor(self, trip): + self.nodeObj.setColorScale(trip[0], trip[1], trip[2], trip[3]) + + def collide(self): + if self.isActive: + self.setX(self.prevX) + self.setZ(self.prevZ) diff --git a/toontown/estate/GardenDropGame.py b/toontown/estate/GardenDropGame.py new file mode 100755 index 00000000..de023795 --- /dev/null +++ b/toontown/estate/GardenDropGame.py @@ -0,0 +1,666 @@ +import math, random, GameSprite, GardenGameGlobals +from math import pi +from direct.gui.DirectGui import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import TTLocalizer + +LevelNumber = 1 + +class GardenDropGame: + + def __init__(self): + self.inHelp = False + self.sprites = [] + self.lastTime = [] + self.grid = [] + print ('Grid Dimensions X%s Z%s' % (GardenGameGlobals.gX, + GardenGameGlobals.gZ)) + base.gardenGame = self + self.matchList = [] + self.massCount = 0 + self.foundCount = 0 + + return None + + def reinitialize(self): + self.inHelp = False + self.sprites = [] + self.lastTime = [] + self.grid = [] + self.matchList = [] + self.massCount = 0 + self.foundCount = 0 + + return None + + def load(self): + model = loader.loadModel('phase_5.5/models/gui/package_delivery_panel.bam') + model1 = loader.loadModel('phase_3.5/models/gui/matching_game_gui.bam') + + self.model = model + self.model1 = model1 + + background = model.find('**/bg') + itemBoard = model.find('**/item_board') + + self.frame = DirectFrame(scale=1.1000000000000001, relief=DGG.FLAT, frameSize=(-0.5, + 0.5, + -0.45000000000000001, + -0.050000000000000003), frameColor=(0.73699999999999999, 0.57299999999999995, 0.34499999999999997, 1.0)) + + self.background = DirectFrame(self.frame, image=background, image_scale=0.050000000000000003, relief=None, pos=(0, 1, 0)) + self.itemBoard = DirectFrame(parent=self.frame, image=itemBoard, image_scale=0.050000000000000003, image_color=(0.92200000000000004, 0.92200000000000004, 0.753, 1), relief=None, pos=(0, 1, 0)) + gui2 = loader.loadModel('phase_3/models/gui/quit_button.bam') + + self.font = loader.loadFont("phase_3/models/fonts/MickeyFont.bam") + self.gardenDropText = OnscreenText(parent=self.frame, text=TTLocalizer.GardenDropTitle,scale=(0.17,0.17,0.17), font=self.font, pos=(0,0.685,0), fg=(1,1,1,1)) + + self.quitButton = DirectButton(parent=self.frame, relief=None, image=(gui2.find('**/QuitBtn_UP'), + gui2.find('**/QuitBtn_DN'), + gui2.find('**/QuitBtn_RLVR')), pos=(0.5, + 1.0, + -0.41999999999999998), scale=0.90000000000000002, text=TTLocalizer.GardenDropExitGame, text_font=self.font, text0_fg=(1, 1, 1, 1), text1_fg=(1, 1, 1, 1), text2_fg=(1, 1, 1, 1), text_scale=0.044999999999999998, text_pos=(0, + -0.01), command=self._GardenDropGame__handleExit) + + if LevelNumber == 1: + self.helpButton = DirectButton(parent=self.frame, relief=None, image=(gui2.find('**/QuitBtn_UP'), + gui2.find('**/QuitBtn_DN'), + gui2.find('**/QuitBtn_RLVR')), pos=(-0.5, + 1.0, + -0.41999999999999998), scale=0.90000000000000002, text=TTLocalizer.PicnicTableTutorial, text_font=self.font, text0_fg=(1, 1, 1, 1), text1_fg=(1, 1, 1, 1), text2_fg=(1, 1, 1, 1), text_scale=0.044999999999999998, text_pos=(0, + -0.01), command=self._GardenDropGame__openHelp) + + def help(self): + self.inHelp = True + + frameGui = loader.loadModel('phase_3/models/gui/dialog_box_gui.bam') + self.helpFrame = DirectFrame(scale=1.1, relief=None, image=frameGui, image_scale=(1.75, 1, 0.75), image_color=(1,1,1,1), frameSize=(-0.5, + 0.5, + -0.45, + -0.05)) + + self.font = loader.loadFont("phase_3/models/fonts/MickeyFont.bam") + self.helpText = DirectLabel(scale=1.1, relief=None, text_pos=(0, 0.2), text_wordwrap=16, text=TTLocalizer.GardenDropHelpTitle, text_font=self.font, pos=(0.0, 0.0, 0.0), text_scale=0.1, text0_fg=(1, 1, 1, 1), parent=self.helpFrame) + + self.font2 = loader.loadFont("phase_3/models/fonts/Comedy.bam") + self.helpText2 = DirectLabel(scale=1.1, relief=None, text_pos=(-0.6, 0.1), text_wordwrap=15, text=TTLocalizer.GardenDropInstructions, text_font=self.font2, pos=(0.0, 0.0, 0.0), text_scale=0.085, text0_fg=(0, 0, 0, 1), parent=self.helpFrame, text_align=TextNode.ALeft) + + gui2 = loader.loadModel('phase_3/models/gui/quit_button.bam') + self.backButton = DirectButton(parent=self.helpFrame, relief=None, image=(gui2.find('**/QuitBtn_UP'), gui2.find('**/QuitBtn_DN'), gui2.find('**/QuitBtn_RLVR')), pos=(0.5, 1.0, -0.32), scale=0.9, text=TTLocalizer.GardenDropBackToGame, text_font=self.font, text0_fg=(1, 1, 1, 1), text1_fg=(1, 1, 1, 1), text2_fg=(1, 1, 1, 1), text_scale=0.045, text_pos=(0, -0.01), command=self.unloadHelp) + + return True + + def addSprite(self, image, size = 0.5, posX = 0, posZ = 0, found = 0): + nodeObj = DirectLabel(parent=self.frame, relief=None, image=image, pos=(posX, 0.0, posZ), scale=size, image_color=(1.0, 1.0, 1.0, 1)) + if LevelNumber == 1 or LevelNumber == 2: + colorChoice = random.choice(range(0, 3)) + if LevelNumber == 3 or LevelNumber == 4: + colorChoice = random.choice(range(0, 4)) + if LevelNumber == 5: + colorChoice = random.choice(range(0, 5)) + + newSprite = GameSprite.GameSprite(nodeObj, colorChoice, found) + self.sprites.append(newSprite) + + if found: + self.foundCount += 1 + + return newSprite + + def addUnSprite(self, image, size = 0.5, posX = 0, posZ = 0): + nodeObj = DirectLabel(parent=self.frame, relief=None, image=image, pos=(posX, 0.0, posZ), scale=size, image_color=(1.0, 1.0, 1.0, 1)) + + newSprite = GameSprite.GameSprite(nodeObj) + newSprite = GameSprite.GameSprite(nodeObj) + + return newSprite + + def testPointDistanceSquare(self, x1, z1, x2, z2): + distX = x1 - x2 + distZ = z1 - z2 + distC = distX * distX + distZ * distZ + + if distC == 0: + distC = 1e-10 + + return distC + + def testDistance(self, nodeA, nodeB): + distX = nodeA.getX() - nodeB.getX() + distZ = nodeA.getZ() - nodeB.getZ() + distC = distX * distX + distZ * distZ + dist = math.sqrt(distC) + + return dist + + def testGridfull(self, cell): + if not cell: + return 0 + elif cell[0] != None: + return 1 + else: + return 0 + + returnTrue + + def getValidGrid(self, x, z): + if x < 0 or x >= GardenGameGlobals.gridDimX: + return None + elif z < 0 or z >= GardenGameGlobals.gridDimZ: + return None + else: + return self.grid[x][z] + + return None + + def getColorType(self, x, z): + if x < 0 or x >= GardenGameGlobals.gridDimX: + return -1 + elif z < 0 or z >= GardenGameGlobals.gridDimZ: + return -1 + elif self.grid[x][z][0] == None: + return -1 + else: + return self.grid[x][z][0].colorType + + return True + + def getSprite(self, spriteIndex): + if spriteIndex >= len(self.sprites) or self.sprites[spriteIndex].markedForDeath: + return None + else: + return self.sprites[spriteIndex] + + return None + + def findGrid(self, x, z, force = 0): + currentClosest = None + currentDist = 10000000 + + for countX in xrange(GardenGameGlobals.gridDimX): + for countZ in xrange(GardenGameGlobals.gridDimZ): + testDist = self.testPointDistanceSquare(x, z, self.grid[countX][countZ][1], self.grid[countX][countZ][2]) + if self.grid[countX][countZ][0] == None and testDist < currentDist and (force or self.hasNeighbor(countX, countZ)): + currentClosest = self.grid[countX][countZ] + self.closestX = countX + self.closestZ = countZ + currentDist = testDist + + return currentClosest + + def findGridCog(self): + GardenGameGlobals.cogX = 0 + GardenGameGlobals.cogZ = 0 + self.massCount = 0 + + for row in self.grid: + for cell in row: + if cell[0] != None: + GardenGameGlobals.cogX += cell[1] + GardenGameGlobals.cogZ += cell[2] + self.massCount += 1 + + if self.massCount > 0: + self.cogX = (GardenGameGlobals.cogX / self.massCount) + self.cogZ = (GardenGameGlobals.cogZ / self.massCount) + self.cogSprite.setX(self.cogX) + self.cogSprite.setZ(self.cogZ) + else: + self.doOnClearGrid() + + return True + + def stickInGrid(self, sprite, force = 0): + if sprite.isActive and not sprite.isQue: + gridCell = self.findGrid(sprite.getX(), sprite.getZ(), force) + + if gridCell: + gridCell[0] = sprite + sprite.setActive(0) + sprite.setX(gridCell[1]) + sprite.setZ(gridCell[2]) + self.createMatchList(self.closestX, self.closestZ) + + if len(self.matchList) >= 3: + self.clearMatchList() + self.findGridCog() + + def fillMatchList(self, cellX, cellZ): + if (cellX, cellZ) in self.matchList: + return True + + self.matchList.append((cellX, cellZ)) + colorType = self.grid[cellX][cellZ][0].colorType + + if cellZ % 2 == 0: + if self.getColorType(cellX - 1, cellZ) == colorType: + self.fillMatchList(cellX - 1, cellZ) + + if self.getColorType(cellX + 1, cellZ) == colorType: + self.fillMatchList(cellX + 1, cellZ) + + if self.getColorType(cellX, cellZ + 1) == colorType: + self.fillMatchList(cellX, cellZ + 1) + + if self.getColorType(cellX + 1, cellZ + 1) == colorType: + self.fillMatchList(cellX + 1, cellZ + 1) + + if self.getColorType(cellX, cellZ - 1) == colorType: + self.fillMatchList(cellX, cellZ - 1) + + if self.getColorType(cellX + 1, cellZ - 1) == colorType: + self.fillMatchList(cellX + 1, cellZ - 1) + else: + if self.getColorType(cellX - 1, cellZ) == colorType: + self.fillMatchList(cellX - 1, cellZ) + + if self.getColorType(cellX + 1, cellZ) == colorType: + self.fillMatchList(cellX + 1, cellZ) + + if self.getColorType(cellX, cellZ + 1) == colorType: + self.fillMatchList(cellX, cellZ + 1) + + if self.getColorType(cellX - 1, cellZ + 1) == colorType: + self.fillMatchList(cellX - 1, cellZ + 1) + + if self.getColorType(cellX, cellZ - 1) == colorType: + self.fillMatchList(cellX, cellZ - 1) + + if self.getColorType(cellX - 1, cellZ - 1) == colorType: + self.fillMatchList(cellX - 1, cellZ - 1) + + def createMatchList(self, x, z): + self.matchList = [] + self.fillMatchList(x, z) + + def clearMatchList(self): + for entry in self.matchList: + gridEntry = self.grid[entry[0]][entry[1]] + sprite = gridEntry[0] + gridEntry[0] = None + sprite.markedForDeath = 1 + + return True + + def hasNeighbor(self, cellX, cellZ): + gotNeighbor = 0 + + if cellZ % 2 == 0: + if self.testGridfull(self.getValidGrid(cellX - 1, cellZ)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX, cellZ + 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ + 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX, cellZ - 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ - 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX - 1, cellZ)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX, cellZ + 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX - 1, cellZ + 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX, cellZ - 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX - 1, cellZ - 1)): + gotNeighbor = 1 + + return gotNeighbor + + def __colTest(self): + if not hasattr(self, 'tick'): + self.tick = 0 + + self.tick += 1 + + if self.tick > 5: + self.tick = 0 + sizeSprites = len(self.sprites) + + for movingSpriteIndex in xrange(len(self.sprites)): + for testSpriteIndex in xrange(movingSpriteIndex, len(self.sprites)): + movingSprite = self.getSprite(movingSpriteIndex) + testSprite = self.getSprite(testSpriteIndex) + + if testSprite and movingSprite: + if movingSpriteIndex != testSpriteIndex and (movingSprite.isActive or testSprite.isActive): + if movingSprite.isQue or testSprite.isQue: + if self.testDistance(movingSprite.nodeObj, testSprite.nodeObj) < GardenGameGlobals.queExtent * (movingSprite.size + testSprite.size): + self.push(movingSprite, testSprite) + elif self.testDistance(movingSprite.nodeObj, testSprite.nodeObj) < movingSprite.size + testSprite.size: + if movingSprite.isActive: + testSprite.isActive or self.__collide(movingSprite, testSprite) + + if self.testDistance(self.cogSprite.nodeObj, testSprite.nodeObj) < (self.cogSprite.size + testSprite.size): + if movingSprite.isActive: + self.stickInGrid(testSprite, 1) + + if self.tick == 5: + pass + + def __collide(self, move, test): + queHit = 0 + + if move.isQue: + que = move + hit = test + queHit = 1 + elif test.isQue: + que = test + hit = move + queHit = 1 + else: + test.velX = 0 + test.velZ = 0 + move.velX = 0 + move.velZ = 0 + test.collide() + move.collide() + self.stickInGrid(move,1) + self.stickInGrid(test,1) + + if queHit: + forceM = 0.1 + distX = que.getX() - hit.getX() + distZ = que.getZ() - hit.getZ() + self.stickInGrid(move,1) + self.stickInGrid(test,1) + + def push(self, move, test): + queHit = 0 + + if move.isQue: + que = move + hit = test + queHit = 1 + elif test.isQue: + que = test + hit = move + queHit = 1 + + if queHit: + forceM = 0.1 + dist = self.testDistance(move.nodeObj, test.nodeObj) + + if abs(dist) < GardenGameGlobals.queExtent * que.size and abs(dist) > 0: + scaleSize = GardenGameGlobals.queExtent * que.size * 0.5 + distFromPara = abs(abs(dist) - scaleSize) + force = (scaleSize - distFromPara) / scaleSize * (dist / abs(dist)) + angle = self.angleTwoSprites(que, hit) + + if angle < 0: + angle = angle + 2 * pi + + if angle > pi * 2.0: + angle = angle - 2 * pi + + newAngle = pi * 1.0 + + if angle > pi * 1.5 or angle < pi * 0.5: + newAngle = pi * 0.0 + + hit.addForce(forceM * force, newAngle) + + def angleTwoSprites(self, sprite1, sprite2): + x1 = sprite1.getX() + z1 = sprite1.getZ() + x2 = sprite2.getX() + z2 = sprite2.getZ() + x = x2 - x1 + z = z2 - z1 + angle = math.atan2(-x, z) + + return angle + pi * 0.5 + + def doOnClearGrid(self): + secondSprite = self.addSprite(self.block, posX=GardenGameGlobals.newBallX, posZ=0.0, found=1) + secondSprite.addForce(0, 1.55 * pi) + self.stickInGrid(secondSprite, 1) + + def __run(self, Task): + if self.lastTime == None: + self.lastTime = globalClock.getRealTime() + + timeDelta = 0.0265 + self.lastTime = globalClock.getRealTime() + GardenGameGlobals.newBallCountUp += timeDelta + + if base.mouseWatcherNode.hasMouse(): + x = base.mouseWatcherNode.getMouseX() + y = base.mouseWatcherNode.getMouseY() + self.queBall.setX(x) + self.queBall.setZ(y) + + for sprite in self.sprites: + sprite.run(timeDelta) + if sprite.getX() > GardenGameGlobals.maxX: + sprite.setX(GardenGameGlobals.maxX) + sprite.velX = -sprite.velX + + if sprite.getX() < GardenGameGlobals.minX: + sprite.setX(GardenGameGlobals.minX) + sprite.velX = -sprite.velX + + if sprite.getZ() > GardenGameGlobals.maxZ: + sprite.setZ(GardenGameGlobals.maxZ) + sprite.velZ = -sprite.velZ + + if sprite.getZ() < GardenGameGlobals.minZ: + self.stickInGrid(sprite, 1) + + if sprite.isActive: + sprite.addForce(timeDelta * 0.9, pi * 1.5) + + self.queBall.velX = (self.queBall.getX() - self.queBall.prevX) / timeDelta + self.queBall.velZ = (self.queBall.getZ() - self.queBall.prevZ) / timeDelta + self.__colTest() + + for sprite in self.sprites: + if sprite.markedForDeath: + if sprite.foundation: + self.foundCount -= 1 + + self.sprites.remove(sprite) + sprite.delete() + + if GardenGameGlobals.controlSprite == None: + self.addControlSprite(GardenGameGlobals.newBallX, GardenGameGlobals.newBallZ) + GardenGameGlobals.newBallCountUp = 0.0 + + if GardenGameGlobals.newBallCountUp >= GardenGameGlobals.newBallTime: + self.addControlSprite(GardenGameGlobals.newBallX, GardenGameGlobals.newBallZ) + GardenGameGlobals.newBallCountUp = 0.0 + + if not GardenGameGlobals.controlSprite.isActive: + GardenGameGlobals.controlSprite = None + + if self.foundCount <= 0: + self.__handleWin() + + return Task.cont + + def loadStartingSprites(self, levelNum): + self.queBall = self.addSprite(self.block, posX=0.25, posZ=0.5, found=0) + self.queBall.setColor(GardenGameGlobals.colorWhite) + self.queBall.isQue = 1 + + GardenGameGlobals.controlSprite = None + self.cogSprite = self.addUnSprite(self.block, posX=0.25, posZ=0.5) + self.cogSprite.setColor(GardenGameGlobals.colorBlack) + + for ball in xrange(0, levelNum): + place = random.random() * GardenGameGlobals.rangeX + self.newSprite = self.addSprite(self.block, size=0.5, posX=GardenGameGlobals.minX + place, posZ=0.0, found=1) + self.stickInGrid(self.newSprite, 1) + + def __handlePlay(self): + if hasattr(base, 'localAvatar'): + base.cr.playGame.getPlace().fsm.forceTransition('stopped') + + self.reinitialize() + self.load() + + self.itemboard = self.model.find('**/item_board') + self.block = self.model1.find('**/minnieCircle') + + size = 0.085 + sizeZ = size * 0.8 + + for countX in xrange(GardenGameGlobals.gridDimX): + newRow = [] + for countZ in xrange(GardenGameGlobals.gridDimZ): + offset = 0 + if countZ % 2 == 0: + offset = size / 2 + newRow.append([None, countX * size + GardenGameGlobals.minX + offset, countZ * sizeZ + GardenGameGlobals.minZ]) + + self.grid.append(newRow) + + if LevelNumber == 1: + self.loadStartingSprites(3) + elif LevelNumber == 2: + self.loadStartingSprites(5) + elif LevelNumber == 3: + self.loadStartingSprites(7) + elif LevelNumber == 4: + self.loadStartingSprites(10) + elif LevelNumber == 5: + self.loadStartingSprites(15) + base.taskMgr.add(self._GardenDropGame__run,"MouseCheck") + + if hasattr(self, 'victoryFrame'): + self.victoryFrame.removeNode() + del self.victoryFrame + + def addControlSprite(self, x = 0.0, z = 0.0): + newSprite = self.addSprite(self.block, posX=x, posZ=z) + GardenGameGlobals.controlSprite = newSprite + + def playGardenDrop(self): + self.GDButtonImage = loader.loadModel("phase_3/models/gui/quit_button.bam") + self.font = loader.loadFont("phase_3/models/fonts/MickeyFont.bam") + self.yellowButton = (self.GDButtonImage.find('**/QuitBtn_UP'), self.GDButtonImage.find('**/QuitBtn_DN'), self.GDButtonImage.find('**/QuitBtn_RLVR')) + + self.GardenGameButton = DirectButton(frameSize=(0), text=TTLocalizer.GardenDropButtonTitle, image=self.yellowButton, text_pos=(0,0.01), relief=None, text_fg=(1, 1, 1, 1), \ + geom=None, pad=(0.01, 0.01), suppressKeys=0, command=self.deleteGDButton, pos=(0.49, 0, 0.115), text_font=self.font, text_scale=(0.083,0.040,0.049), borderWidth=(0.13, 0.01), scale=(0.8,1,1.8)) + self.GardenGameButton.reparentTo(base.a2dBottomLeft) + base.localAvatar.gardenGameButton = self.GardenGameButton + + def __openHelp(self): + self.unload() + self.help() + base.taskMgr.remove('MouseCheck') + + def unload(self): + self.frame.destroy() + del self.frame + + if (GardenGameGlobals.acceptErrorDialog and GardenGameGlobals.acceptErrorDialog.cleanup()): + GardenGameGlobals.acceptErrorDialog = 1 + + def unloadHelp(self): + self.helpFrame.removeNode() + self._GardenDropGame__handlePlay() + + def deleteGDButton(self): + self.GardenGameButton.removeNode() + self.__handlePlay() + + def __handleExit(self): + self._GardenDropGame__acceptExit() + + def __acceptExit(self, buttonValue = None): + global LevelNumber + if hasattr(base, 'localAvatar'): + base.cr.playGame.getPlace().fsm.forceTransition('walk') + + self.playGardenDrop() + if (hasattr(self, 'frame') and self.frame.hide()): + self.unload() + messenger.send(GardenGameGlobals.doneEvent) + + LevelNumber = 1 + base.taskMgr.remove('MouseCheck') + + def __handleWin(self): + global LevelNumber + self.unload() + self.loadWin() + LevelNumber += 1 + base.taskMgr.remove('MouseCheck') + + def loadWin(self): + model = loader.loadModel('phase_5.5/models/gui/package_delivery_panel.bam') + model1 = loader.loadModel('phase_3.5/models/gui/matching_game_gui.bam') + + self.model = model + self.model1 = model1 + + background = model.find('**/bg') + itemBoard = model.find('**/item_board') + + frameGui = loader.loadModel('phase_3/models/gui/dialog_box_gui.bam') + self.victoryFrame = DirectFrame(scale=1.1, relief=None, image=frameGui, image_scale=(1.75, 1, 0.75), image_color=(1,1,1,1), frameSize=(-0.5, + 0.5, + -0.45, + -0.05)) + + frameGui2 = loader.loadModel('phase_3.5/models/gui/jar_gui.bam') + self.jar = DirectFrame(parent=self.victoryFrame, scale=(0.65,1.4,1.3), relief=None, image=frameGui2, image_scale=(1.75, 1, 0.75), image_color=(1,1,1,1), pos=(-0.5,-0.15,-0.075), frameSize=(-0.5, + 0.45, + -0.45, + -0.05)) + + gui2 = loader.loadModel('phase_3/models/gui/quit_button.bam') + self.font = loader.loadFont("phase_3/models/fonts/MickeyFont.bam") + + if LevelNumber == 5: + congratsMessage = TTLocalizer.GardenDropWinGame + else: + congratsMessage = TTLocalizer.GardenDropProgressLevels + self.nextButton = DirectButton(parent=self.victoryFrame, relief=None, image=(gui2.find('**/QuitBtn_UP'), gui2.find('**/QuitBtn_DN'), gui2.find('**/QuitBtn_RLVR')), pos=(0, 1.0, -0.32), scale=0.9, text=TTLocalizer.lNext, text_font=self.font, text0_fg=(1, 1, 1, 1), text1_fg=(1, 1, 1, 1), text2_fg=(1, 1, 1, 1), text_scale=0.045, text_pos=(0, -0.01), command=self._GardenDropGame__handlePlay) + + self.congratsText = DirectLabel(scale=1.1, relief=None, text_pos=(0, 0.2), text_wordwrap=16, text=TTLocalizer.GardenDropCongradulations, text_font=self.font, pos=(0.0, 0.0, 0.0), text_scale=0.1, text0_fg=(1, 1, 1, 1), parent=self.victoryFrame) + + self.font2 = loader.loadFont("phase_3/models/fonts/Comedy.bam") + self.congratsText2 = DirectLabel(scale=1.1, relief=None, text_pos=(0.2, 0.025), text_wordwrap=10, text=congratsMessage, text_font=self.font2, pos=(0.0, 0.0, 0.0), text_scale=0.085, text0_fg=(0, 0, 0, 1), parent=self.victoryFrame) + + self.quitButton = DirectButton(parent=self.victoryFrame, relief=None, image=(gui2.find('**/QuitBtn_UP'), gui2.find('**/QuitBtn_DN'), gui2.find('**/QuitBtn_RLVR')), pos=(0.5, 1.0, -0.32), scale=0.9, text=TTLocalizer.GardenDropExit, text_font=self.font, text0_fg=(1, 1, 1, 1), text1_fg=(1, 1, 1, 1), text2_fg=(1, 1, 1, 1), text_scale=0.045, text_pos=(0, -0.01), command=self.__handleExitWin) + + return True + + def unloadWin(self): + self.victoryFrame.removeNode() + del self.victoryFrame + + if GardenGameGlobals.acceptErrorDialog: + GardenGameGlobals.acceptErrorDialog.cleanup() + GardenGameGlobals.acceptErrorDialog = None + + self.playGardenDrop() + base.taskMgr.remove('gameTask') + + return True + + def __handleExitWin(self): + global LevelNumber + self._GardenDropGame__acceptExitWin() + LevelNumber = 1 + + def __acceptExitWin(self, buttonValue = None): + if hasattr(base, 'localAvatar'): + base.cr.playGame.getPlace().fsm.forceTransition('walk') + + if hasattr(self, 'victoryFrame'): + self.unloadWin() + messenger.send(GardenGameGlobals.doneEvent) + + def endGame(self): + if hasattr(base, 'localAvatar'): + base.localAvatar.gardenGameButton.removeNode() diff --git a/toontown/estate/GardenGameGlobals.py b/toontown/estate/GardenGameGlobals.py new file mode 100755 index 00000000..04fe4114 --- /dev/null +++ b/toontown/estate/GardenGameGlobals.py @@ -0,0 +1,36 @@ +acceptErrorDialog = 0 +doneEvent = 'game Done' +colorRed = (1, 0, 0, 1) +colorBlue = (0, 0, 1, 1) +colorGreen = (0, 1, 0, 1) +colorGhostRed = (1, 0, 0, 0.5) +colorGhostGreen = (0, 1, 0, 0.5) +colorWhite = (1, 1, 1, 1) +colorBlack = (0.5, 0.5, 0.5, 1.0) +colorShadow = (0, 0, 0, 0.5) +running = 0 +maxX = 0.46999999999999997 +minX = -0.46999999999999997 +maxZ = 0.75000000000000002 +minZ = -0.00000000000000001 +newBallX = 0.0 +newBallZ = 0.69999999999999998 +rangeX = (maxX - minX) +rangeZ = (maxZ - minZ) +size = 0.085000000000000006 +sizeZ = (size * 0.80000000000000004) +gX = int((rangeX / size)) +gZ = int((rangeZ / sizeZ)) +maxX = (minX + (gX * size)) +maxZ = (minZ + (gZ * sizeZ)) +controlOffsetX = 0.0 +controlOffsetZ = 0.0 +queExtent = 3 +gridDimX = gX +gridDimZ = gZ +gridBrick = False +newBallTime = 1.0 +newBallCountUp = 0.0 +cogX = 0 +cogZ = 0 +controlSprite = None diff --git a/toontown/estate/GardenGlobals.py b/toontown/estate/GardenGlobals.py new file mode 100755 index 00000000..770318ce --- /dev/null +++ b/toontown/estate/GardenGlobals.py @@ -0,0 +1,1499 @@ +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal +import random +FLOWERS_PER_BONUS = 10 +ACCELERATOR_USED_FROM_SHTIKER_BOOK = True +COLLECT_NO_UPDATE = 0 +COLLECT_NEW_ENTRY = 1 +gardenNotify = DirectNotifyGlobal.directNotify.newCategory('GardenGlobals') +FlowerItem = 2 +FlowerItemNewEntry = 9 +INVALID_TYPE = -1 +GAG_TREE_TYPE = 0 +FLOWER_TYPE = 1 +STATUARY_TYPE = 2 +WATERING_CAN_SMALL = 0 +WATERING_CAN_MEDIUM = 1 +WATERING_CAN_LARGE = 2 +WATERING_CAN_HUGE = 3 +MAX_WATERING_CANS = 4 +WateringCanAttributes = {0: {'numBoxes': 2, + 'skillPts': 100, + 'name': TTLocalizer.WateringCanSmall}, + 1: {'numBoxes': 2, + 'skillPts': 200, + 'name': TTLocalizer.WateringCanMedium}, + 2: {'numBoxes': 2, + 'skillPts': 400, + 'name': TTLocalizer.WateringCanLarge}, + 3: {'numBoxes': 2, + 'skillPts': 1000, + 'name': TTLocalizer.WateringCanHuge}} +WateringMult = 2 + +def getWateringCanPower(wateringCan, wateringCanSkill): + numBoxes = 0 + for curWateringCan in xrange(wateringCan + 1): + wateringCanAttrib = WateringCanAttributes[curWateringCan] + curBoxes = wateringCanAttrib['numBoxes'] + skill = wateringCanAttrib['skillPts'] + if wateringCanSkill >= skill: + if curWateringCan == wateringCan: + gardenNotify.warning("this shouldn't happen wateringCanSkill %d >= skill %d" % (wateringCanSkill, skill)) + wateringCanSkill = skill - 1 + if curWateringCan == wateringCan: + skillPtPerBox = skill / curBoxes + numBoxes += 1 + int(wateringCanSkill) / int(skillPtPerBox) + else: + numBoxes += curBoxes + + return numBoxes * WateringMult + + +def getMaxWateringCanPower(): + retval = 0 + for wateringCanAttrib in WateringCanAttributes.values(): + retval += wateringCanAttrib['numBoxes'] + + return retval * WateringMult + + +FlowerColors = [(0.804, 0.2, 0.2), + (0.922, 0.463, 0.0), + (0.5, 0.2, 1.0), + (0.4, 0.4, 1.0), + (0.953, 0.545, 0.757), + (0.992, 0.843, 0.392), + (1.0, 1.0, 1.0), + (0.5, 0.8, 0.5)] +FLOWER_RED = 0 +FLOWER_ORANGE = 1 +FLOWER_VIOLET = 2 +FLOWER_BLUE = 3 +FLOWER_PINK = 4 +FLOWER_YELLOW = 5 +FLOWER_WHITE = 6 +FLOWER_GREEN = 7 +ToonStatuaryTypeIndices = xrange(205, 209) +ChangingStatuaryTypeIndices = xrange(230, 232) +AnimatedStatuaryTypeIndices = xrange(234, 238) +PlantAttributes = {49: {'name': TTLocalizer.FlowerSpeciesNames[49], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/daisy.bam', + 'fullGrownModel': 'phase_5.5/models/estate/daisy.bam', + 'photoPos': (0.0, -0.35, -0.361882), + 'photoScale': 1, + 'photoHeading': 0, + 'photoPitch': 35, + 'varieties': ((10, FLOWER_YELLOW, 1), + (11, FLOWER_PINK, 2), + (12, FLOWER_WHITE, 3), + (13, FLOWER_RED, 4), + (14, FLOWER_ORANGE, 5), + (15, FLOWER_BLUE, 6), + (16, FLOWER_GREEN, 7), + (17, FLOWER_VIOLET, 8))}, + 50: {'name': TTLocalizer.FlowerSpeciesNames[50], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/tulip.bam', + 'fullGrownModel': 'phase_5.5/models/estate/tulip.bam', + 'photoPos': (0.0, -0.35, -0.35), + 'photoScale': 1, + 'photoHeading': 0, + 'photoPitch': 35, + 'varieties': ((20, FLOWER_VIOLET, 5), (21, FLOWER_RED, 6), (22, FLOWER_YELLOW, 8))}, + 51: {'name': TTLocalizer.FlowerSpeciesNames[51], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/carnation.bam', + 'fullGrownModel': 'phase_5.5/models/estate/carnation.bam', + 'photoPos': (0.0, -0.35, -0.4), + 'photoScale': 1, + 'photoHeading': 0, + 'photoPitch': 35, + 'varieties': ((30, FLOWER_PINK, 1), + (31, FLOWER_YELLOW, 2), + (32, FLOWER_RED, 3), + (33, FLOWER_WHITE, 5), + (34, FLOWER_GREEN, 7))}, + 52: {'name': TTLocalizer.FlowerSpeciesNames[52], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/lily.bam', + 'fullGrownModel': 'phase_5.5/models/estate/lily.bam', + 'photoPos': (0.0174745, -0.05, -0.670513), + 'photoScale': 1, + 'photoHeading': 0, + 'photoPitch': 35, + 'varieties': ((40, FLOWER_WHITE, 1), + (41, FLOWER_GREEN, 2), + (42, FLOWER_ORANGE, 3), + (43, FLOWER_PINK, 4), + (44, FLOWER_RED, 5), + (45, FLOWER_VIOLET, 6), + (46, FLOWER_BLUE, 7), + (47, FLOWER_YELLOW, 8))}, + 53: {'name': TTLocalizer.FlowerSpeciesNames[53], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/narcissi.bam', + 'fullGrownModel': 'phase_5.5/models/estate/narcissi.bam', + 'photoPos': (-0.0403175, 0.060933, -0.548368), + 'photoScale': 1, + 'photoHeading': 20, + 'photoPitch': 0, + 'varieties': ((50, FLOWER_GREEN, 1), + (51, FLOWER_WHITE, 2), + (52, FLOWER_YELLOW, 4), + (53, FLOWER_PINK, 5))}, + 54: {'name': TTLocalizer.FlowerSpeciesNames[54], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/pansy.bam', + 'fullGrownModel': 'phase_5.5/models/estate/pansy.bam', + 'photoScale': 2.5, + 'photoHeading': 0, + 'photoPitch': 0, + 'varieties': ((60, FLOWER_ORANGE, 1), + (61, FLOWER_WHITE, 2), + (62, FLOWER_RED, 3), + (63, FLOWER_YELLOW, 4), + (64, FLOWER_PINK, 6))}, + 55: {'name': TTLocalizer.FlowerSpeciesNames[55], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/petunia.bam', + 'fullGrownModel': 'phase_5.5/models/estate/petunia.bam', + 'photoPos': (0.02, -0.0324585, -0.167735), + 'photoScale': 1.5, + 'photoHeading': -20, + 'photoPitch': 35, + 'varieties': ((70, FLOWER_BLUE, 7), (71, FLOWER_PINK, 8))}, + 56: {'name': TTLocalizer.FlowerSpeciesNames[56], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -1, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/rose.bam', + 'fullGrownModel': 'phase_5.5/models/estate/rose.bam', + 'photoPos': (0.04396, 0.124797, -0.877291), + 'photoScale': 1, + 'photoHeading': 0, + 'photoPitch': 35, + 'varieties': ((0, FLOWER_RED, 3), + (1, FLOWER_YELLOW, 4), + (2, FLOWER_PINK, 6), + (3, FLOWER_WHITE, 7), + (4, FLOWER_BLUE, 8))}, + 200: {'name': TTLocalizer.StatuaryDonald, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_donald.bam', + 'worldScale': 0.05, + 'varieties': ((1000, 1, 0),), + 'pinballScore': (10, 1)}, + 201: {'name': TTLocalizer.StatuaryMickey1, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_mickey_flute', + 'worldScale': 0.05, + 'varieties': ((1001, 1, 0),), + 'pinballScore': (50, 1)}, + 202: {'name': TTLocalizer.StatuaryGardenAccelerator, + 'plantType': STATUARY_TYPE, + 'model': 'phase_4/models/props/goofy_statue', + 'varieties': ((1002, 1, 0),)}, + 203: {'name': TTLocalizer.StatuaryMinnie, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_minnie', + 'worldScale': 0.05, + 'varieties': ((1003, 1, 0),), + 'pinballScore': (150, 1)}, + 204: {'name': TTLocalizer.StatuaryMickey2, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_mickey_shovel', + 'worldScale': 0.05, + 'varieties': ((1004, 1, 0),), + 'pinballScore': (250, 1)}, + 205: {'name': TTLocalizer.StatuaryToonWave, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_pedestal', + 'worldScale': 0.05, + 'varieties': ((1005, 1, 0),), + 'pinballScore': (500, 1)}, + 206: {'name': TTLocalizer.StatuaryToonVictory, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_pedestal', + 'worldScale': 0.05, + 'varieties': ((1006, 1, 0),), + 'pinballScore': (500, 1)}, + 207: {'name': TTLocalizer.StatuaryToonCrossedArms, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_pedestal', + 'worldScale': 0.05, + 'varieties': ((1007, 1, 0),), + 'pinballScore': (500, 1)}, + 208: {'name': TTLocalizer.StatuaryToonThinking, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_pedestal', + 'worldScale': 0.05, + 'varieties': ((1008, 1, 0),), + 'pinballScore': (500, 1)}, + 230: {'name': TTLocalizer.StatuaryMeltingSnowman, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/tt_m_prp_ext_snowman', + 'worldScale': 1.0, + 'varieties': ((1030, 1, 0),), + 'pinballScore': (500, 1), + 'growthThresholds': (1, 2)}, + 231: {'name': TTLocalizer.StatuaryMeltingSnowDoodle, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/tt_m_prp_ext_snowDoodle', + 'worldScale': 1.0, + 'varieties': ((1031, 1, 0),), + 'pinballScore': (500, 1), + 'growthThresholds': (1, 2)}, + 234: {'name': TTLocalizer.AnimatedStatuaryFlappyCog, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/tt_a_ara_pty_tubeCogVictory_', + 'anims': ['default', 'wave'], + 'worldScale': 0.5, + 'varieties': ((1035, 1, 0),), + 'pinballScore': (500, 1)}, + 254: {'name': 'reserved tag', + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_minnie', + 'worldScale': 0.05, + 'varieties': ((2001, 1, 0),), + 'pinballScore': (15, 1)}, + 255: {'name': 'reserved tag', + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_minnie', + 'worldScale': 0.05, + 'varieties': ((2002, 1, 0),), + 'pinballScore': (15, 1)}} +if ACCELERATOR_USED_FROM_SHTIKER_BOOK: + del PlantAttributes[202] + +def getTreeTrackAndLevel(typeIndex): + track = typeIndex / 7 + level = typeIndex % 7 + return (track, level) + + +def getTreeTypeIndex(track, level): + return track * 7 + level + + +NUM_GAGS = 7 * 7 +for i in xrange(NUM_GAGS): + track, level = getTreeTrackAndLevel(i) + if level <= 6: + name = TTLocalizer.BattleGlobalAvPropStrings[track][level] + TTLocalizer.GardenGagTree + else: + name = TTLocalizer.GardenUberGag + attr = {'name': name, + 'plantType': GAG_TREE_TYPE, + 'growthThresholds': (level + 1, (level + 1) * 2, (level + 1) * 3), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -1, + 'maxFruit': 9, + 'filename': 'phase_5.5/models/estate/gag_tree_stages.bam', + 'seedlingModel': 'gag_tree_small', + 'establishedModel': 'gag_tree_med', + 'fullGrownModel': 'gag_tree_large', + 'varieties': ((),)} + PlantAttributes[i] = attr + +BeanColors = [(255, 0, 0), + (0, 255, 0), + (255, 165, 0), + (148, 0, 211), + (0, 0, 255), + (255, 192, 203), + (255, 255, 0), + (0, 255, 255), + (192, 192, 192)] +BeanColorLetters = ['R', + 'G', + 'O', + 'V', + 'B', + 'P', + 'Y', + 'C', + 'S'] +Recipes = {0: {'beans': 'RRR', + 'special': -1}, + 1: {'beans': 'RYOY', + 'special': -1}, + 2: {'beans': 'RPOROP', + 'special': -1}, + 3: {'beans': 'RCOPVCC', + 'special': -1}, + 4: {'beans': 'RBVVBBPB', + 'special': -1}, + 10: {'beans': 'Y', + 'special': -1}, + 11: {'beans': 'YR', + 'special': -1}, + 12: {'beans': 'YRG', + 'special': -1}, + 13: {'beans': 'YRCO', + 'special': -1}, + 14: {'beans': 'YROOO', + 'special': -1}, + 15: {'beans': 'YBCVBB', + 'special': -1}, + 16: {'beans': 'YGROGGG', + 'special': -1}, + 17: {'beans': 'YBVCVROV', + 'special': -1}, + 20: {'beans': 'VRBVV', + 'special': -1}, + 21: {'beans': 'VRRRVV', + 'special': -1}, + 22: {'beans': 'VYYVYOVY', + 'special': -1}, + 30: {'beans': 'P', + 'special': -1}, + 31: {'beans': 'PY', + 'special': -1}, + 32: {'beans': 'PRR', + 'special': -1}, + 33: {'beans': 'PRGBR', + 'special': -1}, + 34: {'beans': 'PGGGGYG', + 'special': -1}, + 40: {'beans': 'C', + 'special': -1}, + 41: {'beans': 'CG', + 'special': -1}, + 42: {'beans': 'COO', + 'special': -1}, + 43: {'beans': 'COOP', + 'special': -1}, + 44: {'beans': 'CRRRR', + 'special': -1}, + 45: {'beans': 'CRVVVV', + 'special': -1}, + 46: {'beans': 'CVCBCBB', + 'special': -1}, + 47: {'beans': 'CBYYCBYY', + 'special': -1}, + 50: {'beans': 'G', + 'special': -1}, + 51: {'beans': 'GC', + 'special': -1}, + 52: {'beans': 'GPYY', + 'special': -1}, + 53: {'beans': 'GPBPP', + 'special': -1}, + 60: {'beans': 'O', + 'special': -1}, + 61: {'beans': 'OC', + 'special': -1}, + 62: {'beans': 'ORR', + 'special': -1}, + 63: {'beans': 'OYYR', + 'special': -1}, + 64: {'beans': 'OPPOBP', + 'special': -1}, + 70: {'beans': 'BVBVCBB', + 'special': -1}, + 71: {'beans': 'BPPBROYY', + 'special': -1}, + 1000: {'beans': 'GG', + 'special': 100}, + 1001: {'beans': 'SSSS', + 'special': 101}, + 1002: {'beans': 'S', + 'special': 102}, + 1003: {'beans': 'VVVVVV', + 'special': 103}, + 1004: {'beans': 'OOOOOOOO', + 'special': 104}, + 1005: {'beans': 'RRRRRRRR', + 'special': 105}, + 1006: {'beans': 'GGGGGGGG', + 'special': 106}, + 1007: {'beans': 'BBBBBBBB', + 'special': 107}, + 1008: {'beans': 'SSSSSSSS', + 'special': 108}, + 1030: {'beans': 'S', + 'special': 130}, + 1031: {'beans': 'S', + 'special': 131}, + 1035: {'beans': 'S', + 'special': 135}, + 2001: {'beans': 'ZVOVOVO', + 'special': -1}, + 2002: {'beans': 'ZOVOVOV', + 'special': -1}} + +def getRecipeKey(beans, special): + testDict = {'beans': beans, + 'special': special} + for key in Recipes.keys(): + recipe = Recipes[key] + if testDict == recipe: + return key + + return -1 + + +def getRecipeKeyUsingSpecial(special): + for key in Recipes.keys(): + recipe = Recipes[key] + if recipe['special'] == special: + return key + + return -1 + + +SHOVEL_TIN = 0 +SHOVEL_STEEL = 1 +SHOVEL_SILVER = 2 +SHOVEL_GOLD = 3 +MAX_SHOVELS = 4 +ShovelAttributes = {0: {'numBoxes': 2, + 'skillPts': 80, + 'name': TTLocalizer.ShovelTin}, + 1: {'numBoxes': 2, + 'skillPts': 160, + 'name': TTLocalizer.ShovelSteel}, + 2: {'numBoxes': 2, + 'skillPts': 320, + 'name': TTLocalizer.ShovelSilver}, + 3: {'numBoxes': 2, + 'skillPts': 640, + 'name': TTLocalizer.ShovelGold}} + +def getShovelPower(shovel, shovelSkill): + numBoxes = 0 + for curShovel in xrange(shovel + 1): + shovelAttrib = ShovelAttributes[curShovel] + curBoxes = shovelAttrib['numBoxes'] + skill = shovelAttrib['skillPts'] + if curShovel == shovel: + if shovelSkill >= skill: + gardenNotify.warning("this shouldn't happen shovelSkill %d >= skill %d" % (shovelSkill, skill)) + shovelSkill = skill - 1 + skillPtPerBox = skill / curBoxes + numBoxes += 1 + int(shovelSkill) / int(skillPtPerBox) + else: + numBoxes += curBoxes + + return numBoxes + + +def getMaxShovelSkill(): + retVal = 0 + retVal += ShovelAttributes[MAX_SHOVELS - 1]['skillPts'] - 1 + return retVal + + +def getNumberOfShovelBoxes(): + retVal = 0 + for attrib in ShovelAttributes.values(): + retVal += attrib['numBoxes'] + + return retVal + + +def getNumberOfWateringCanBoxes(): + retVal = 0 + for attrib in WateringCanAttributes.values(): + retVal += attrib['numBoxes'] + + return retVal + + +def getNumberOfFlowerVarieties(): + retVal = 0 + for attrib in PlantAttributes.values(): + if attrib['plantType'] == FLOWER_TYPE: + retVal += len(attrib['varieties']) + + return retVal + + +def getNumberOfFlowerSpecies(): + retVal = 0 + for attrib in PlantAttributes.values(): + if attrib['plantType'] == FLOWER_TYPE: + retVal += 1 + + return retVal + + +def getFlowerVarieties(species): + retval = () + if species in PlantAttributes.keys(): + attrib = PlantAttributes[species] + if attrib['plantType'] == FLOWER_TYPE: + retval = attrib['varieties'] + return retval + + +def getFlowerSpecies(): + retVal = [] + for key in PlantAttributes.keys(): + attrib = PlantAttributes[key] + if attrib['plantType'] == FLOWER_TYPE: + retVal.append(key) + + return retVal + + +def getRandomFlower(): + species = random.choice(getFlowerSpecies()) + variety = random.randint(0, len(PlantAttributes[species]['varieties']) - 1) + return (species, variety) + + +def getFlowerVarietyName(species, variety): + retVal = TTLocalizer.FlowerUnknown + if species in PlantAttributes.keys(): + attrib = PlantAttributes[species] + if variety < len(attrib['varieties']): + funnySpeciesNameList = TTLocalizer.FlowerFunnyNames.get(species) + if funnySpeciesNameList: + if variety < len(funnySpeciesNameList): + retVal = TTLocalizer.FlowerFunnyNames[species][variety] + else: + gardenNotify.warning('warning unknown species=%d variety= %d' % (species, variety)) + else: + gardenNotify.warning('warning unknown species %d' % species) + return retVal + + +def getSpeciesVarietyGivenRecipe(recipeKey): + for species in PlantAttributes.keys(): + attrib = PlantAttributes[species] + if attrib['plantType'] == GAG_TREE_TYPE: + continue + if 'varieties' in attrib: + for variety in xrange(len(attrib['varieties'])): + if attrib['varieties'][variety][0] == recipeKey: + return (species, variety) + + return (-1, -1) + + +def getNumBeansRequired(species, variety): + retval = -1 + if not PlantAttributes.get(species): + return retval + if 'varieties' not in PlantAttributes[species]: + return retval + if variety >= len(PlantAttributes[species]['varieties']): + return -1 + recipeKey = PlantAttributes[species]['varieties'][variety][0] + recipe = Recipes.get(recipeKey) + if recipe: + if 'beans' in recipe: + retval = len(recipe['beans']) + return retval + + +def validateRecipes(notify): + uniqueRecipes = [] + uniqueBeans = [] + numBoxes = getNumberOfShovelBoxes() + uniqueSpecials = [] + for key in Recipes.keys(): + recipe = Recipes[key] + beans = recipe['beans'] + if len(beans) > numBoxes: + notify.warning('numBoxes=%d beans=%s, truncating to %s' % (numBoxes, beans, beans[:numBoxes])) + beans = beans[:numBoxes] + for letter in beans: + if key not in (2001, 2002): + pass + + testTuple = (beans, recipe['special']) + uniqueRecipes.append(testTuple) + if beans: + if beans in uniqueBeans: + notify.warning('duplicate beans=%s in key=%d' % (beans, key)) + else: + uniqueBeans.append(beans) + special = recipe['special'] + if special != -1: + uniqueSpecials.append(special) + + notify.debug('recipes are ok') + + +def validatePlantAttributes(notify): + uniqueRecipes = [] + flowerRecipeDistribution = [] + for i in xrange(getNumberOfShovelBoxes() + 1): + flowerRecipeDistribution.append([]) + + for key in PlantAttributes.keys(): + plant = PlantAttributes[key] + notify.debug('now validating %s' % plant['name']) + if plant['plantType'] in (GAG_TREE_TYPE, FLOWER_TYPE): + growthThresholds = plant['growthThresholds'] + lastValue = 0 + for testValue in growthThresholds: + lastValue = testValue + + if plant['plantType'] in (STATUARY_TYPE, FLOWER_TYPE): + varieties = plant['varieties'] + for variety in varieties: + recipeNum = variety[0] + uniqueRecipes.append(recipeNum) + if plant['plantType'] == FLOWER_TYPE: + recipeLength = len(Recipes[recipeNum]['beans']) + newInfo = (getFlowerVarietyName(key, list(varieties).index(variety)), Recipes[recipeNum]['beans'], TTLocalizer.FlowerColorStrings[variety[1]]) + flowerRecipeDistribution[recipeLength].append(newInfo) + + for numBeans in xrange(len(flowerRecipeDistribution)): + notify.debug('%d flowers with %d beans' % (len(flowerRecipeDistribution[numBeans]), numBeans)) + for flower in flowerRecipeDistribution[numBeans]: + notify.debug(' %s, beans = %s, color=%s' % (flower[0], flower[1], flower[2])) + + notify.debug('plant attributes are ok') + + +plots0 = ((0, + 0, + 0.0, + FLOWER_TYPE), + (1, + 0, + 0.0, + FLOWER_TYPE), + (2, + 0, + 0.0, + FLOWER_TYPE), + (2, + 1, + 0.0, + FLOWER_TYPE), + (2, + 2, + 0.0, + FLOWER_TYPE), + (3, + 0, + 0.0, + FLOWER_TYPE), + (3, + 1, + 0.0, + FLOWER_TYPE), + (3, + 2, + 0.0, + FLOWER_TYPE), + (4, + 0, + 0.0, + FLOWER_TYPE), + (4, + 1, + 0.0, + FLOWER_TYPE), + (-54, + -13.5, + 276.0, + GAG_TREE_TYPE), + (-7, + -48, + 343.0, + GAG_TREE_TYPE), + (-40, + -75, + 27.0, + GAG_TREE_TYPE), + (-78, + -44, + 309.0, + GAG_TREE_TYPE), + (-72, + -15, + 260.0, + GAG_TREE_TYPE), + (-24, + -19, + 294.0, + GAG_TREE_TYPE), + (11, + -26, + 0.0, + GAG_TREE_TYPE), + (-92, + -4, + 0.0, + GAG_TREE_TYPE), + (-100, + -43, + -90.0, + STATUARY_TYPE)) +plots1 = ((0, + 0, + 0.0, + FLOWER_TYPE), + (1, + 0, + 0.0, + FLOWER_TYPE), + (2, + 0, + 0.0, + FLOWER_TYPE), + (2, + 1, + 0.0, + FLOWER_TYPE), + (2, + 2, + 0.0, + FLOWER_TYPE), + (3, + 0, + 0.0, + FLOWER_TYPE), + (3, + 1, + 0.0, + FLOWER_TYPE), + (3, + 2, + 0.0, + FLOWER_TYPE), + (4, + 0, + 0.0, + FLOWER_TYPE), + (4, + 1, + 0.0, + FLOWER_TYPE), + (62, + -81, + 194.0, + GAG_TREE_TYPE), + (101, + -52, + 250.0, + GAG_TREE_TYPE), + (93, + -104, + 214.0, + GAG_TREE_TYPE), + (69, + -122, + 188.0, + GAG_TREE_TYPE), + (92, + -120, + 184.0, + GAG_TREE_TYPE), + (113, + -29, + 250.0, + GAG_TREE_TYPE), + (125, + -57, + 0.0, + GAG_TREE_TYPE), + (114, + -40, + 0.0, + GAG_TREE_TYPE), + (47, + -82, + -30.0, + STATUARY_TYPE)) +plots2 = ((0, + 0, + 0.0, + FLOWER_TYPE), + (1, + 0, + 0.0, + FLOWER_TYPE), + (2, + 0, + 0.0, + FLOWER_TYPE), + (2, + 1, + 0.0, + FLOWER_TYPE), + (2, + 2, + 0.0, + FLOWER_TYPE), + (3, + 0, + 0.0, + FLOWER_TYPE), + (3, + 1, + 0.0, + FLOWER_TYPE), + (3, + 2, + 0.0, + FLOWER_TYPE), + (4, + 0, + 0.0, + FLOWER_TYPE), + (4, + 1, + 0.0, + FLOWER_TYPE), + (-40, + -114, + 176.0, + GAG_TREE_TYPE), + (-44, + -148, + 162.0, + GAG_TREE_TYPE), + (-97, + -99, + 138.0, + GAG_TREE_TYPE), + (-82, + -94, + 134.0, + GAG_TREE_TYPE), + (-27, + -106, + 195.0, + GAG_TREE_TYPE), + (-76, + -147, + 110.0, + GAG_TREE_TYPE), + (-29, + -164, + 0.0, + GAG_TREE_TYPE), + (-107, + -94, + 0.0, + GAG_TREE_TYPE), + (-97, + -114, + -60.0, + STATUARY_TYPE)) +plots3 = ((0, + 0, + 0.0, + FLOWER_TYPE), + (1, + 0, + 0.0, + FLOWER_TYPE), + (2, + 0, + 0.0, + FLOWER_TYPE), + (2, + 1, + 0.0, + FLOWER_TYPE), + (2, + 2, + 0.0, + FLOWER_TYPE), + (3, + 0, + 0.0, + FLOWER_TYPE), + (3, + 1, + 0.0, + FLOWER_TYPE), + (3, + 2, + 0.0, + FLOWER_TYPE), + (4, + 0, + 0.0, + FLOWER_TYPE), + (4, + 1, + 0.0, + FLOWER_TYPE), + (59, + 35, + 187.0, + GAG_TREE_TYPE), + (87, + 28, + 114.0, + GAG_TREE_TYPE), + (67, + -16, + 78.0, + GAG_TREE_TYPE), + (24, + 19, + 155.0, + GAG_TREE_TYPE), + (18, + 31, + 172.0, + GAG_TREE_TYPE), + (74, + 36, + 133.0, + GAG_TREE_TYPE), + (35, + -34, + 0.0, + GAG_TREE_TYPE), + (116, + 17, + 0.0, + GAG_TREE_TYPE), + (117, + 27, + 102.0, + STATUARY_TYPE)) +plots4 = ((0, + 0, + 0.0, + FLOWER_TYPE), + (1, + 0, + 0.0, + FLOWER_TYPE), + (2, + 0, + 0.0, + FLOWER_TYPE), + (2, + 1, + 0.0, + FLOWER_TYPE), + (2, + 2, + 0.0, + FLOWER_TYPE), + (3, + 0, + 0.0, + FLOWER_TYPE), + (3, + 1, + 0.0, + FLOWER_TYPE), + (3, + 2, + 0.0, + FLOWER_TYPE), + (4, + 0, + 0.0, + FLOWER_TYPE), + (4, + 1, + 0.0, + FLOWER_TYPE), + (37, + 101, + 350.0, + GAG_TREE_TYPE), + (15, + 100, + 342.0, + GAG_TREE_TYPE), + (73, + 92, + 0.0, + GAG_TREE_TYPE), + (74, + 69, + 347.0, + GAG_TREE_TYPE), + (102, + 62, + 334.0, + GAG_TREE_TYPE), + (86, + 76, + 350.0, + GAG_TREE_TYPE), + (100, + 78, + 327.0, + GAG_TREE_TYPE), + (15, + 73, + 50.0, + GAG_TREE_TYPE), + (16, + 87, + -140.0, + STATUARY_TYPE)) +plots5 = ((0, + 0, + 0.0, + FLOWER_TYPE), + (1, + 0, + 0.0, + FLOWER_TYPE), + (2, + 0, + 0.0, + FLOWER_TYPE), + (2, + 1, + 0.0, + FLOWER_TYPE), + (2, + 2, + 0.0, + FLOWER_TYPE), + (3, + 0, + 0.0, + FLOWER_TYPE), + (3, + 1, + 0.0, + FLOWER_TYPE), + (3, + 2, + 0.0, + FLOWER_TYPE), + (4, + 0, + 0.0, + FLOWER_TYPE), + (4, + 1, + 0.0, + FLOWER_TYPE), + (-26, + 92, + 41.0, + GAG_TREE_TYPE), + (-71, + 58, + 37.0, + GAG_TREE_TYPE), + (-67, + 21, + 243.0, + GAG_TREE_TYPE), + (-10, + -2.6, + 178.0, + GAG_TREE_TYPE), + (-60, + 13.7, + 250.0, + GAG_TREE_TYPE), + (-13, + 84, + 2.0, + GAG_TREE_TYPE), + (-62, + 65, + 0.0, + GAG_TREE_TYPE), + (-16.6, + 52.7, + 0.0, + GAG_TREE_TYPE), + (-55, + 70, + 213.0, + STATUARY_TYPE)) +estatePlots = (plots0, + plots1, + plots2, + plots3, + plots4, + plots5) +BOX_ONE = 1 +BOX_TWO = 2 +BOX_THREE = 3 +FLOWER_POS = (None, (0,), (-1.5, 1.5), (-3.5, 0, 3.5)) +flowerBoxes0 = ((-62.5, + -52.5, + 182.0, + BOX_ONE), + (-52, + -52, + 182, + BOX_ONE), + (-64.5, + -42, + 92.0, + BOX_THREE), + (-49, + -43, + 266.0, + BOX_THREE), + (-57, + -33, + 0.0, + BOX_TWO)) +flowerBoxes1 = ((85.0, + -67.0, + 26.0, + BOX_ONE), + (75, + -72, + 26.0, + BOX_ONE), + (91.0, + -74.0, + -63.0, + BOX_THREE), + (77, + -81, + 117.0, + BOX_THREE), + (88, + -86, + 206.0, + BOX_TWO)) +flowerBoxes2 = ((-62, + -112, + 350.0, + BOX_ONE), + (-72, + -110, + 350.0, + BOX_ONE), + (-62, + -122, + 257.0, + BOX_THREE), + (-76, + -118, + 79.0, + BOX_THREE), + (-71, + -129, + 169.0, + BOX_TWO)) +flowerBoxes3 = ((72, + 5, + 265.0, + BOX_ONE), + (72.5, + 16, + 265.0, + BOX_ONE), + (63, + 3, + 178.0, + BOX_THREE), + (64, + 19, + 355.0, + BOX_THREE), + (54, + 12, + 86.0, + BOX_TWO)) +flowerBoxes4 = ((35.5, + 70, + 152.0, + BOX_ONE), + (46, + 66, + 152.0, + BOX_ONE), + (36.5, + 79.5, + 71.0, + BOX_THREE), + (51.5, + 74, + 247.0, + BOX_THREE), + (47, + 86, + -19.0, + BOX_TWO)) +flowerBoxes5 = ((-26.5, + 37.5, + 318.0, + BOX_ONE), + (-33, + 46, + 318.0, + BOX_ONE), + (-32, + 30, + 217.0, + BOX_THREE), + (-42, + 42, + 37.0, + BOX_THREE), + (-45, + 31, + 124.0, + BOX_TWO)) +estateBoxes = (flowerBoxes0, + flowerBoxes1, + flowerBoxes2, + flowerBoxes3, + flowerBoxes4, + flowerBoxes5) + +def whatCanBePlanted(plotIndex, hardPointIndex): + retval = INVALID_TYPE + if plotIndex < len(estatePlots) and plotIndex >= 0: + if hardPointIndex < len(estatePlots[plotIndex]) and hardPointIndex >= 0: + if len(estatePlots[plotIndex][hardPointIndex]) >= 4: + retval = estatePlots[plotIndex][hardPointIndex][3] + return retval + + +MAGIC_BEAN_SUBTYPE = 0 +GARDEN_ITEM_SUBTYPE = 1 +Specials = {0: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 1, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeans, + 'description': TTLocalizer.GardenSpecialDiscription, + 'beanCost': 125}, + 1: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 2, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeansB, + 'description': TTLocalizer.GardenSpecialDiscriptionB, + 'beanCost': 125}, + 2: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 1, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeans, + 'description': TTLocalizer.GardenSpecialDiscription, + 'beanCost': 125}, + 3: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 2, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeansB, + 'description': TTLocalizer.GardenSpecialDiscriptionB, + 'beanCost': 125}, + 4: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 1, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeans, + 'description': TTLocalizer.GardenSpecialDiscription, + 'beanCost': 125}, + 5: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 2, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeansB, + 'description': TTLocalizer.GardenSpecialDiscriptionB, + 'beanCost': 125}, + 6: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 2, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeansB, + 'description': TTLocalizer.GardenSpecialDiscription, + 'beanCost': 125}, + 7: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 2, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeansB, + 'description': TTLocalizer.GardenSpecialDiscriptionB, + 'beanCost': 125}, + 100: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_donald', + 'photoScale': 0.04, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.StatuaryDonald, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 125}, + 101: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_mickey_flute', + 'photoScale': 0.025, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryMickey1, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 250}, + 102: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/sack', + 'photoScale': 1.0, + 'photoPos': (0, 0, -1.0), + 'photoName': TTLocalizer.StatuaryGardenAccelerator, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 7500, + 'useFromShtiker': False}, + 103: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_minnie', + 'photoScale': 0.02, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryMinnie, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 500}, + 104: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_mickey_shovel', + 'photoScale': 0.02, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryMickey2, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 1000}, + 105: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_pedestal', + 'photoScale': 0.02, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryToonWave, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 5000, + 'minSkill': 639}, + 106: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_pedestal', + 'photoScale': 0.02, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryToonVictory, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 5000, + 'minSkill': 639}, + 107: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_pedestal', + 'photoScale': 0.02, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryToonCrossedArms, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 5000, + 'minSkill': 639}, + 108: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_pedestal', + 'photoScale': 0.02, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryToonThinking, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 5000, + 'minSkill': 639}, + 130: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/tt_m_prp_ext_snowman_icon', + 'photoScale': 90.0, + 'photoPos': (0, 0, 0.0), + 'photoName': TTLocalizer.StatuaryMeltingSnowman, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 25, + 'minSkill': 0}, + 131: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/tt_m_prp_ext_snowDoodle_icon', + 'photoScale': 90.0, + 'photoPos': (0, 0, 0.0), + 'photoName': TTLocalizer.StatuaryMeltingSnowDoodle, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 50, + 'minSkill': 0}, + 135: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/tt_a_ara_pty_tubeCogVictory_', + 'photoAnimation': ['default', 'wave'], + 'photoScale': 1.25, + 'photoPos': (0, 0, -0.04), + 'photoName': TTLocalizer.AnimatedStatuaryFlappyCog, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 50, + 'minSkill': 1}} +GardenAcceleratorSpecial = 102 +GardenAcceleratorSpecies = 202 +if ACCELERATOR_USED_FROM_SHTIKER_BOOK: + Specials[GardenAcceleratorSpecial]['useFromShtiker'] = True + +def getPlantItWithString(special): + retval = '' + recipeKey = getRecipeKeyUsingSpecial(special) + if not recipeKey == -1: + beanTuple = [] + beanStr = Recipes[recipeKey]['beans'] + for letter in beanStr: + index = BeanColorLetters.index(letter) + beanTuple.append(index) + + beanText = TTLocalizer.getRecipeBeanText(beanTuple) + retval += TTLocalizer.PlantItWith % beanText + return retval + + +for specialKey in Specials.keys(): + recipeKey = getRecipeKeyUsingSpecial(specialKey) + if not recipeKey == -1: + Specials[specialKey]['description'] = getPlantItWithString(specialKey) + if specialKey == GardenAcceleratorSpecial: + if ACCELERATOR_USED_FROM_SHTIKER_BOOK: + Specials[specialKey]['description'] = TTLocalizer.UseFromSpecialsTab + Specials[specialKey]['description'] += TTLocalizer.MakeSureWatered + +TIME_OF_DAY_FOR_EPOCH = 3 +MOVIE_HARVEST = 0 +MOVIE_PLANT = 1 +MOVIE_REMOVE = 2 +MOVIE_WATER = 3 +MOVIE_FINISHPLANTING = 4 +MOVIE_FINISHREMOVING = 5 +MOVIE_CLEAR = 6 +MOVIE_PLANT_REJECTED = 7 +TrophyDict = {0: (TTLocalizer.GardenTrophyNameDict[0],), + 1: (TTLocalizer.GardenTrophyNameDict[1],), + 2: (TTLocalizer.GardenTrophyNameDict[2],), + 3: (TTLocalizer.GardenTrophyNameDict[3],)} diff --git a/toontown/estate/GardenProgressMeter.py b/toontown/estate/GardenProgressMeter.py new file mode 100755 index 00000000..3810b4a3 --- /dev/null +++ b/toontown/estate/GardenProgressMeter.py @@ -0,0 +1,81 @@ +from panda3d.core import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.gui.DirectScrolledList import * +from direct.gui.DirectGui import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.estate import GardenGlobals + +SHOVEL = 0 +WATERINGCAN = 1 +GAMEWIN = 2 + +class GardenProgressMeter(DirectObject.DirectObject): + + def __init__(self, typePromotion = 'game', level = 0): + if typePromotion == 'shovel': + self.typePromotion = SHOVEL + elif typePromotion == 'wateringCan': + self.typePromotion = WATERINGCAN + elif typePromotion == 'game': + self.typePromotion == GAMEWIN + else: + print 'No type of %s' % typePromotion + self.level = level + self.acceptErrorDialog = None + self.doneEvent = 'game Done' + self.sprites = [] + self.load() + thing = self.model.find('**/item_board') + self.block = self.model1.find('**/minnieCircle') + return + + def load(self): + model = loader.loadModel('phase_5.5/models/gui/package_delivery_panel') + model1 = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + self.model = model + self.model1 = model1 + background = model.find('**/bg') + itemBoard = model.find('**/item_board') + congratsMessage = 'Super Congratulations!!' + if self.typePromotion == SHOVEL: + congratsMessage = TTLocalizer.GardenShovelLevelUp + ' \n' + GardenGlobals.ShovelAttributes[self.level]['name'] + elif self.typePromotion == WATERINGCAN: + congratsMessage = TTLocalizer.GardenWateringCanLevelUp + ' \n' + GardenGlobals.WateringCanAttributes[self.level]['name'] + elif self.typePromotion == GAMEWIN: + congratsMessage = TTLocalizer.GardenMiniGameWon + self.frame = DirectFrame(scale=1.1, relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(1.75, 1, 0.75), image_color=ToontownGlobals.GlobalDialogColor, frameSize=(-0.5, + 0.5, + -0.45, + -0.05)) + self.congratsText = DirectLabel(scale=1.1, relief=None, text_pos=(0, 0.2), text_wordwrap=16, text=congratsMessage, text_font=ToontownGlobals.getSignFont(), pos=(0.0, 0.0, 0.0), text_scale=0.1, text0_fg=(1, 1, 1, 1), parent=self.frame) + gui2 = loader.loadModel('phase_3/models/gui/quit_button') + self.quitButton = DirectButton(parent=self.frame, relief=None, image=(gui2.find('**/QuitBtn_UP'), gui2.find('**/QuitBtn_DN'), gui2.find('**/QuitBtn_RLVR')), pos=(0.5, 1.0, -0.32), scale=0.9, text='Exit', text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text1_fg=(1, 1, 1, 1), text2_fg=(1, 1, 1, 1), text_scale=0.045, text_pos=(0, -0.01), command=self.__handleExit) + return + + def unload(self): + self.frame.destroy() + del self.frame + if self.acceptErrorDialog: + self.acceptErrorDialog.cleanup() + self.acceptErrorDialog = None + taskMgr.remove('gameTask') + self.ignoreAll() + return + + def show(self): + self.frame.show() + + def hide(self): + self.frame.hide() + + def __handleExit(self): + self.__acceptExit() + + def __acceptExit(self, buttonValue = None): + if hasattr(self, 'frame'): + self.hide() + self.unload() + messenger.send(self.doneEvent) diff --git a/toontown/estate/GardenTutorial.py b/toontown/estate/GardenTutorial.py new file mode 100755 index 00000000..1659b163 --- /dev/null +++ b/toontown/estate/GardenTutorial.py @@ -0,0 +1,117 @@ +from direct.gui.DirectGui import * +from direct.fsm import FSM +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from panda3d.core import * + +class GardenTutorial(DirectFrame, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('GardenTutorial') + + def __init__(self, doneEvent = None, callback = None): + FSM.FSM.__init__(self, 'GardenTutorial') + self.doneEvent = doneEvent + self.callback = callback + self.setStateArray(['Page1', + 'Page2', + 'Page3', + 'Page4', + 'Page5']) + DirectFrame.__init__(self, pos=(0.0, 0.0, 0.0), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.5, 1.5, 0.9), text='', text_scale=0.06) + self['image'] = DGG.getDefaultDialogGeom() + self.title = DirectLabel(self, relief=None, text='', text_pos=(0.0, 0.32), text_fg=(1, 0, 0, 1), text_scale=0.13, text_font=ToontownGlobals.getSignFont()) + images = loader.loadModel('phase_5.5/models/estate/gardenTutorialPages') + self.iPage1 = DirectFrame(self, image=images.find('**/GardenTutorialPage1'), scale=0.35, pos=(-0.51, -0.1, 0.05)) + self.iPage1.hide() + self.iPage2 = DirectFrame(self, image=images.find('**/GardenTutorialPage2'), scale=1.25, pos=(0.43, -0.1, 0.05)) + self.iPage2.hide() + self.iPage3 = DirectFrame(self, image=images.find('**/GardenTutorialPage3'), scale=0.5, pos=(-0.52, -0.1, 0.02)) + self.iPage3.hide() + self.iPage4 = DirectFrame(self, image=images.find('**/GardenTutorialPage4'), scale=0.75, pos=(0, -0.1, -0.05)) + self.iPage4.hide() + self.iPage5 = DirectFrame(self, image=images.find('**/GardenTutorialPage5'), scale=0.7, pos=(-0.51, -0.1, 0.05)) + self.iPage5.hide() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.bNext = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), relief=None, text=TTLocalizer.GardenTutorialNext, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.2, -0.3, -0.25), command=self.requestNext) + self.bPrev = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), image_scale=(-1.0, 1.0, 1.0), relief=None, text=TTLocalizer.GardenTutorialPrev, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.2, -0.3, -0.25), command=self.requestPrev) + self.bQuit = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.GardenTutorialDone, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.55, -0.3, -0.25), command=self.__handleQuit) + self.bQuit.hide() + buttons.removeNode() + gui.removeNode() + self.notify.debug('garden tutorial detectedGardenPlotUse') + base.cr.playGame.getPlace().detectedGardenPlotUse() + curState = base.cr.playGame.getPlace().getState() + self.notify.debug('Estate.getState() == %s' % curState) + self.request('Page1') + return + + def enterPage1(self, *args): + self.title['text'] = (TTLocalizer.GardenTutorialTitle1,) + self['text'] = TTLocalizer.GardenTutorialPage1 + self['text_pos'] = (0.15, 0.13) + self['text_wordwrap'] = 16.5 + self.bPrev['state'] = DGG.DISABLED + self.iPage1.show() + + def exitPage1(self, *args): + self.bPrev['state'] = DGG.NORMAL + self.iPage1.hide() + + def enterPage2(self, *args): + self.title['text'] = (TTLocalizer.GardenTutorialTitle2,) + self['text'] = TTLocalizer.GardenTutorialPage2 + self['text_pos'] = (-0.27, 0.16) + self['text_wordwrap'] = TTLocalizer.GTenterPage2Wordwrap + self.iPage2.show() + + def exitPage2(self, *args): + self.iPage2.hide() + + def enterPage3(self, *args): + self.title['text'] = (TTLocalizer.GardenTutorialTitle3,) + self['text'] = TTLocalizer.GardenTutorialPage3 + self['text_pos'] = (0.15, 0.13) + self['text_wordwrap'] = 16.5 + self.iPage3.show() + + def exitPage3(self, *args): + self.iPage3.hide() + + def enterPage4(self, *args): + self.title['text'] = (TTLocalizer.GardenTutorialTitle4,) + self['text'] = TTLocalizer.GardenTutorialPage4 + self['text_pos'] = (0.0, 0.19) + self['text_wordwrap'] = TTLocalizer.GTenterPage4Wordwrap + self.iPage4.show() + + def exitPage4(self, *args): + self.iPage4.hide() + + def enterPage5(self, *args): + self.title['text'] = (TTLocalizer.GardenTutorialTitle5,) + self['text'] = TTLocalizer.GardenTutorialPage5 + self['text_pos'] = (0.15, 0.13) + self['text_wordwrap'] = 16.5 + self.bQuit.show() + self.bNext['state'] = DGG.DISABLED + self.iPage5.show() + + def exitPage5(self, *args): + self.bNext['state'] = DGG.NORMAL + self.iPage5.hide() + self.bQuit.hide() + + def __handleQuit(self): + self.notify.debug('garden tutorial detectedGardenPlotDone') + base.cr.playGame.getPlace().detectedGardenPlotDone() + if self.callback: + self.callback() + else: + messenger.send(self.doneEvent) diff --git a/toontown/estate/House.py b/toontown/estate/House.py new file mode 100755 index 00000000..25f04538 --- /dev/null +++ b/toontown/estate/House.py @@ -0,0 +1,174 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import Place +from direct.showbase import DirectObject +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class House(Place.Place): + notify = DirectNotifyGlobal.directNotify.newCategory('House') + + def __init__(self, loader, avId, parentFSMState, doneEvent): + Place.Place.__init__(self, loader, doneEvent) + self.id = ToontownGlobals.MyEstate + self.ownersAvId = avId + self.dnaFile = 'phase_7/models/modules/toon_interior' + self.isInterior = 1 + self.oldStyle = None + self.fsm = ClassicFSM.ClassicFSM('House', [State.State('start', self.enterStart, self.exitStart, ['doorIn', 'teleportIn', 'tutorial']), + State.State('walk', self.enterWalk, self.exitWalk, ['sit', + 'stickerBook', + 'doorOut', + 'teleportOut', + 'quest', + 'purchase', + 'closet', + 'banking', + 'phone', + 'stopped']), + State.State('sit', self.enterSit, self.exitSit, ['walk']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'sit', + 'doorOut', + 'teleportOut', + 'quest', + 'purchase', + 'closet', + 'banking', + 'phone', + 'stopped']), + State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk', 'stopped']), + State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk', 'stopped']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn']), + State.State('quest', self.enterQuest, self.exitQuest, ['walk', 'doorOut']), + State.State('tutorial', self.enterTutorial, self.exitTutorial, ['walk', 'quest']), + State.State('purchase', self.enterPurchase, self.exitPurchase, ['walk', 'doorOut']), + State.State('closet', self.enterCloset, self.exitCloset, ['walk']), + State.State('banking', self.enterBanking, self.exitBanking, ['walk']), + State.State('phone', self.enterPhone, self.exitPhone, ['walk']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk']), + State.State('final', self.enterFinal, self.exitFinal, ['start', 'teleportIn'])], 'start', 'final') + self.parentFSMState = parentFSMState + return + + def load(self): + Place.Place.load(self) + self.parentFSMState.addChild(self.fsm) + + def unload(self): + Place.Place.unload(self) + self.parentFSMState.removeChild(self.fsm) + del self.parentFSMState + del self.fsm + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def enter(self, requestStatus): + self.zoneId = requestStatus['zoneId'] + self.fsm.enterInitialState() + messenger.send('enterHouse') + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + self._telemLimiter = TLGatherAllAvs('House', RotationLimitToH) + NametagGlobals.setMasterArrowsOn(1) + self.fsm.request(requestStatus['how'], [requestStatus]) + + def exit(self): + self.ignoreAll() + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + self._telemLimiter.destroy() + del self._telemLimiter + messenger.send('exitHouse') + NametagGlobals.setMasterArrowsOn(0) + + def setState(self, state): + if hasattr(self, 'fsm'): + self.fsm.request(state) + + def getZoneId(self): + return self.zoneId + + def enterTutorial(self, requestStatus): + self.fsm.request('walk') + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + globalClock.tick() + base.transitions.irisIn() + messenger.send('enterTutorialInterior') + + def exitTutorial(self): + pass + + def enterTeleportIn(self, requestStatus): + base.localAvatar.setPosHpr(2.5, 11.5, ToontownGlobals.FloorOffset, 45.0, 0.0, 0.0) + Place.Place.enterTeleportIn(self, requestStatus) + + def enterTeleportOut(self, requestStatus): + Place.Place.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + self.notify.debug('House: teleportOutDone: requestStatus = %s' % requestStatus) + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + avId = requestStatus['avId'] + shardId = requestStatus['shardId'] + if hoodId == ToontownGlobals.MyEstate and zoneId == self.getZoneId(): + self.fsm.request('teleportIn', [requestStatus]) + elif hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent, [self.doneStatus]) + + def goHomeFailed(self, task): + self.notifyUserGoHomeFailed() + self.ignore('setLocalEstateZone') + self.doneStatus['avId'] = -1 + self.doneStatus['zoneId'] = self.getZoneId() + self.fsm.request('teleportIn', [self.doneStatus]) + return Task.done + + def exitTeleportOut(self): + Place.Place.exitTeleportOut(self) + + def enterPurchase(self): + Place.Place.enterPurchase(self) + + def exitPurchase(self): + Place.Place.exitPurchase(self) + + def enterCloset(self): + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + base.localAvatar.obscureMoveFurnitureButton(1) + base.localAvatar.startSleepWatch(self.__handleFallingAsleepCloset) + + def __handleFallingAsleepCloset(self, arg): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + messenger.send('closetAsleep') + base.localAvatar.forceGotoSleep() + + def exitCloset(self): + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(0) + base.localAvatar.stopSleepWatch() + + def enterBanking(self): + Place.Place.enterBanking(self) + + def exitBanking(self): + Place.Place.exitBanking(self) diff --git a/toontown/estate/HouseGlobals.py b/toontown/estate/HouseGlobals.py new file mode 100755 index 00000000..ef5f4d6e --- /dev/null +++ b/toontown/estate/HouseGlobals.py @@ -0,0 +1,120 @@ +NUM_HOUSE_TYPES = 6 +HOUSE_DEFAULT = 0 +HOUSE_CRAFTSMAN = 1 +HOUSE_TEST = 5 +CLEANUP_DELAY = 8 +BOOT_GRACE_PERIOD = 15 +CLEANUP_DELAY_AFTER_BOOT = 2 +WANT_TELEPORT_TIMEOUT = 0 +TELEPORT_TIMEOUT = 15 +defaultEntryPoint = (23.875, -13.052, 10.092, 7.52, 0, 0) +houseModels = ['phase_5.5/models/estate/houseA.bam', + 'phase_5.5/models/estate/tt_m_ara_est_house_tiki.bam', + 'phase_5.5/models/estate/tt_m_ara_est_house_tepee.bam', + 'phase_5.5/models/estate/tt_m_ara_est_house_castle.bam', + 'phase_5.5/models/estate/tt_m_ara_est_house_cupcake.bam', + 'phase_5.5/models/estate/houseB.bam'] # do we need test_houseA? +houseDrops = [(-56.7788, -42.8756, 4.06471, -90, 0, 0), + (83.3909, -77.5085, 0.0708361, 116.565, 0, 0), + (-69.077, -119.496, 0.025, 77.1957, 0, 0), + (63.4545, 11.0656, 8.05158, 356.6, 0, 0), + (43.9315, 76.72, 0.0377455, 248.962, 0, 0), + (-36.9122, 36.3429, 2.49382, 36.8699, 0, 0)] +gardenDrops = [(25, 68, 0), + (68, -6, 0), + (27, -59, 0), + (-54, -72, 1), + (-95, -29, 0), + (-30, 58, 0)] +houseColors = [(0.892, 0.453, 0.39), + (0.276, 0.692, 0.539), + (0.639, 0.624, 0.882), + (0.525, 0.78, 0.935), + (0.953, 0.545, 0.757), + (0.992, 0.843, 0.392)] +houseColors2 = [(0.792, 0.353, 0.29), + (0.176, 0.592, 0.439), + (0.439, 0.424, 0.682), + (0.325, 0.58, 0.835), + (0.753, 0.345, 0.557), + (0.992, 0.843, 0.392)] +interiorColors = [(0.789, 1, 0.7), + (1, 1, 0.7), + (1, 0.82, 0.7), + (0.839, 0.651, 0.549), + (0.5, 0.586, 0.4), + (0.808, 0.678, 0.51), + (0.875, 0.937, 1.0)] +interiorWood = [(1.0, 1.0, 1.0), + (0.69, 0.741, 0.71), + (1.0, 1.0, 1.0), + (1.0, 1.0, 1.0), + (1.0, 1.0, 1.0), + (0.69, 0.741, 0.71)] +archWood = (0.7, 0.6, 0.5) +atticWood = (0.49, 0.314, 0.224) +stairWood = (0.651, 0.376, 0.31) +doorWood = (0.647, 0.392, 0.353) +windowWood = (0.557, 0.388, 0.2) +interiors = [['phase_5.5/dna/house_interior3.pdna', + [-19.45, + 24.7018, + 0, + 0, + 0, + 0], + [-21.4932, + 5.76027, + 0, + 120, + 0, + 0], + []], ['phase_5.5/dna/house_interior7.pdna', + [-19.45, + 24.7018, + 0, + 0, + 0, + 0], + [-21.4932, + 5.76027, + 0, + 120, + 0, + 0], + []], ['phase_5.5/dna/house_interior10.pdna', + [-22.5835, + 21.8784, + 0, + 90, + 0, + 0], + [-20.96, + 6.49, + 0, + 120, + 0, + 0], + ['c', 'e']]] +NUM_PROPS = 3 +PROP_ICECUBE = 0 +PROP_FLOWER = 1 +PROP_SNOWFLAKE = 2 +FURNITURE_MODE_OFF = 0 +FURNITURE_MODE_STOP = 1 +FURNITURE_MODE_START = 2 +DAY_NIGHT_PERIOD = 270 +DAY_PERIOD = 210 +NIGHT_PERIOD = 60 +HALF_DAY_PERIOD = 105 +HALF_NIGHT_PERIOD = 30 +FIREWORKS_MOVIE_CLEAR = 0 +FIREWORKS_MOVIE_GUI = 1 +HouseEmblemPrices = ( + (50, 20), # bungalo + (200, 75), # tiki + (200, 75), # tepee + (500, 250), # castle + (350, 150), # cupcake + (400, 200) # cabin +) diff --git a/toontown/estate/MailboxGlobals.py b/toontown/estate/MailboxGlobals.py new file mode 100755 index 00000000..edc967d4 --- /dev/null +++ b/toontown/estate/MailboxGlobals.py @@ -0,0 +1,6 @@ +MAILBOX_MOVIE_CLEAR = 2 +MAILBOX_MOVIE_EMPTY = 3 +MAILBOX_MOVIE_WAITING = 4 +MAILBOX_MOVIE_READY = 5 +MAILBOX_MOVIE_NOT_OWNER = 6 +MAILBOX_MOVIE_EXIT = 7 diff --git a/toontown/estate/PhoneGlobals.py b/toontown/estate/PhoneGlobals.py new file mode 100755 index 00000000..d4a1e6a4 --- /dev/null +++ b/toontown/estate/PhoneGlobals.py @@ -0,0 +1,3 @@ +PHONE_MOVIE_CLEAR = 2 +PHONE_MOVIE_PICKUP = 3 +PHONE_MOVIE_HANGUP = 4 \ No newline at end of file diff --git a/toontown/estate/PlantTreeGUI.py b/toontown/estate/PlantTreeGUI.py new file mode 100755 index 00000000..12cedee7 --- /dev/null +++ b/toontown/estate/PlantTreeGUI.py @@ -0,0 +1,34 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.ShowBase import * +from toontown.toonbase import TTLocalizer +import string +from direct.fsm import StateData + +class PlantTreeGUI(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('PlantTreeGUI') + + def __init__(self, doneEvent): + self.doneEvent = doneEvent + self.oldActivateMode = base.localAvatar.inventory.activateMode + base.localAvatar.inventory.setActivateMode('plantTree') + base.localAvatar.inventory.show() + self.accept('inventory-selection', self.__handleInventory) + self.accept('inventory-pass', self.__handleCancel) + return + + def destroy(self): + self.ignore('inventory-selection') + self.ignore('inventory-pass') + base.localAvatar.inventory.setActivateMode(self.oldActivateMode) + base.localAvatar.inventory.hide() + return + + def __handleInventory(self, track, level): + if base.localAvatar.inventory.numItem(track, level) > 0: + messenger.send(self.doneEvent, [True, track, level]) + else: + self.notify.error("An item we don't have: track %s level %s was selected." % (track, level)) + + def __handleCancel(self): + messenger.send(self.doneEvent, [False, None, None]) + return diff --git a/toontown/estate/PlantingGUI.py b/toontown/estate/PlantingGUI.py new file mode 100755 index 00000000..7a37079a --- /dev/null +++ b/toontown/estate/PlantingGUI.py @@ -0,0 +1,410 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.task import Task +from toontown.estate import GardenGlobals +from direct.interval.IntervalGlobal import * +from toontown.estate import SpecialsPhoto +USE_SCROLLING_BEAN_BOX = False +JELLY_BEAN_PICKER_HAS_EMPTY_BOX = False +CAN_CHANGE_BEAN_COLOR = True +FORCE_LEFT_TO_RIGHT = True +ONLY_ONE_SPIFFY_BOX_CAN_BE_CLICKED = True +DO_PICKER_INTERVAL = False +PICKER_ALWAYS_UP = True + +def loadJellyBean(parent, beanIndex): + gui = loader.loadModel('phase_5.5/models/estate/jellyBean') + newBean = gui.instanceTo(parent) + parent.setScale(0.09) + colors = GardenGlobals.BeanColors[beanIndex] + parent.setColorScale(colors[0] / 255.0 * 1.0, colors[1] / 255.0 * 1.0, colors[2] / 255.0 * 1.0, 1) + + +class GenericBoxScrollList(DirectScrolledList): + + def __init__(self, parent, items, **kw): + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.forceHeight = 1 + optiondefs = (('parent', parent, None), + ('relief', None, None), + ('incButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('incButton_relief', None, None), + ('incButton_scale', (1.3, 1.3, -1.3), None), + ('incButton_pos', (0, 0, -0.525), None), + ('incButton_image3_color', Vec4(0.8, 0.8, 0.8, 0.5), None), + ('decButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('decButton_relief', None, None), + ('decButton_scale', (1.3, 1.3, 1.3), None), + ('decButton_pos', (0, 0, 0.525), None), + ('decButton_image3_color', Vec4(0.8, 0.8, 0.8, 0.5), None), + ('numItemsVisible', 1, None), + ('items', items, None), + ('scrollSpeed', 1.0, None), + ('forceHeight', 1, None)) + gui.removeNode() + self.defineoptions(kw, optiondefs) + DirectScrolledList.__init__(self, parent, forceHeight=self.forceHeight) + self.initialiseoptions(GenericBoxScrollList) + return None + + +class BoxItem(NodePath): + + def getHeight(self): + return 0.05 + + +JellyBeanPickerEndPos = (0, 0, 0.1) +JellyBeanPickerScale = (1.1, 1.0, 0.13) +if JELLY_BEAN_PICKER_HAS_EMPTY_BOX: + JellyBeanPickerGeomScale = (1.0, 1.0, 1.0) +else: + JellyBeanPickerGeomScale = (0.9, 1.0, 1.0) +JellyBeanPickerScaleInverse = (1.0 / JellyBeanPickerScale[0], 1.0 / JellyBeanPickerScale[1], 1.0 / JellyBeanPickerScale[2]) + +class JellyBeanPicker(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PlantingGUI') + + def __init__(self, parent, callBack, boxPosition, **kw): + self.index = 0 + self.selectedIndex = 0 + self.callBack = callBack + self.boxPosition = boxPosition + DirectFrame.__init__(self, relief=None, state='normal', geom=DGG.getDefaultDialogGeom(), geom_color=(0.8, 0.8, 0.8, 1.0), geom_scale=JellyBeanPickerGeomScale, scale=JellyBeanPickerScale, frameSize=(-1, 1, -1, 1), pos=(0, 0, 0)) + self.initialiseoptions(JellyBeanPicker) + self.jellyBeanBoxList = [] + self.createJellyBeanBoxes() + return + + def jellyBeanBoxClicked(self, beanIndex): + if JELLY_BEAN_PICKER_HAS_EMPTY_BOX: + self.callBack(self.boxPosition, beanIndex) + else: + self.callBack(self.boxPosition, beanIndex + 1) + + def createJellyBeanBox(self, beanIndex, xPos, zPos): + geomColor = (1, 1, 1, 1) + state = DGG.NORMAL + command = self.jellyBeanBoxClicked + newBox = DirectButton(parent=self, pos=(xPos, 0, zPos), geom=DGG.getDefaultDialogGeom(), geom_scale=(0.1, 1.0, 0.1), geom_color=geomColor, scale=JellyBeanPickerScaleInverse, relief=None, state=state, command=command, extraArgs=[beanIndex], text='', text_pos=(0.0, 0.1), text_scale=0.07, text_fg=Vec4(0, 0, 0, 0), text1_fg=Vec4(0, 0, 0, 0), text2_fg=Vec4(0, 0, 0, 1), text3_fg=Vec4(0, 0, 0, 0)) + if JELLY_BEAN_PICKER_HAS_EMPTY_BOX: + if beanIndex: + beanParent = newBox.attachNewNode('bean_%d' % (beanIndex - 1)) + loadJellyBean(beanParent, beanIndex - 1) + else: + beanParent = newBox.attachNewNode('bean_%d' % beanIndex) + loadJellyBean(beanParent, beanIndex) + self.jellyBeanBoxList.append(newBox) + return + + def setColorText(self): + for beanIndex in xrange(len(self.jellyBeanBoxList)): + if JELLY_BEAN_PICKER_HAS_EMPTY_BOX: + if beanIndex: + box = self.jellyBeanBoxList[beanIndex] + box['text'] = TTLocalizer.BeanColorWords[beanIndex - 1] + else: + box = self.jellyBeanBoxList[beanIndex] + box['text'] = TTLocalizer.BeanColorWords[beanIndex] + + def createJellyBeanBoxes(self): + zCoord = 0 + xIncrement = 0.095 + xPos = 0 + maxBoxes = len(GardenGlobals.BeanColors) + if JELLY_BEAN_PICKER_HAS_EMPTY_BOX: + maxBoxes += 1 + startingXCoord = -0.1 * float(maxBoxes) / 2.0 + 0.075 + for activeBox in xrange(maxBoxes): + xPos = xIncrement * activeBox + startingXCoord + self.createJellyBeanBox(activeBox, xPos, zCoord) + + +class SpiffyBeanBox(DirectButton): + + def __init__(self, parent, index, **kw): + self.boxIndex = index + self.selectedIndex = 0 + optiondefs = () + self.defineoptions(kw, optiondefs) + DirectButton.__init__(self, parent=parent) + self.initialiseoptions(SpiffyBeanBox) + self.selectedBean = self.attachNewNode('selectedBean') + + def getSelectedIndex(self): + if hasattr(self, 'selectedIndex'): + return self.selectedIndex + return 0 + + def setSelectedIndex(self, newIndex): + self.selectedIndex = newIndex + self.selectedBean.removeNode() + self.selectedBean = self.attachNewNode('selectedBean') + if newIndex: + newIndex -= 1 + loadJellyBean(self.selectedBean, newIndex) + + +class PlantingGUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PlantingGUI') + + def __init__(self, doneEvent, specialBoxActive = False): + if specialBoxActive: + instructions = TTLocalizer.GardeningChooseBeansItem + instructionsPos = (0, 0.4) + else: + instructions = TTLocalizer.GardeningChooseBeans + instructionsPos = (0, 0.35) + DirectFrame.__init__(self, relief=None, state='normal', geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.5, 1.0, 1.0), frameSize=(-1, 1, -1, 1), pos=(0, 0, 0), text=instructions, text_wordwrap=20, text_scale=0.08, text_pos=instructionsPos) + self.initialiseoptions(PlantingGUI) + self.doneEvent = doneEvent + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + resetImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(-0.3, 0, -0.35), text=TTLocalizer.PlantingGuiCancel, text_scale=0.06, text_pos=(0, -0.1), command=self.__cancel) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, pos=(0.3, 0, -0.35), text=TTLocalizer.PlantingGuiOk, text_scale=0.06, text_pos=(0, -0.1), command=self.__doPlant) + self.resetButton = DirectButton(parent=self, relief=None, image=resetImageList, pos=(0.0, 0, -0.35), text=TTLocalizer.PlantingGuiReset, text_scale=0.06, text_pos=(0, -0.1), command=self.__reset) + buttons.removeNode() + self.availableBoxes = base.localAvatar.getBoxCapability() + self.maxBoxes = GardenGlobals.getNumberOfShovelBoxes() + self.activeBoxesList = [] + self.specialBox = None + self.specialBoxActive = specialBoxActive + self.boxList = [] + self.jellyBeanPicker = None + self.jellyBeanPickerInterval = None + self.createBoxes() + guiItems = loader.loadModel('phase_5.5/models/gui/catalog_gui') + self.beanBank = DirectLabel(self, relief=None, image=guiItems.find('**/bean_bank'), text=str(base.localAvatar.getMoney() + base.localAvatar.getBankMoney()), text_align=TextNode.ARight, text_scale=0.11, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0.75, -0.81), text_font=ToontownGlobals.getSignFont(), pos=(-0.85, 0, 0.2), scale=0.5) + self.matchBoxesToAvailableMoney() + if PICKER_ALWAYS_UP: + self.spiffyBeanBoxClicked(0) + return + + def destroy(self): + if self.boxList: + for box in self.boxList: + box.destroy() + + self.boxList = [] + DirectFrame.destroy(self) + self.doneEvent = None + if self.jellyBeanPickerInterval: + self.jellyBeanPickerInterval.finish() + self.jellyBeanPickerInterval = None + if self.jellyBeanPicker: + self.jellyBeanPicker.destroy() + self.jellyBeanPicker = None + if hasattr(self, 'specialPhotoList') and self.specialPhotoList: + for photo in self.specialPhotoList: + photo.destroy() + + self.specialPhotoList = [] + return + + def __cancel(self): + messenger.send(self.doneEvent, [0, '', -1]) + messenger.send('wakeup') + + def __reset(self): + if self.jellyBeanPicker: + self.jellyBeanPicker.destroy() + for box in self.boxList: + box.setSelectedIndex(0) + + self.beanBank['text'] = str(base.localAvatar.getMoney() + base.localAvatar.getBankMoney()) + self.matchBoxesToAvailableMoney() + if PICKER_ALWAYS_UP: + self.spiffyBeanBoxClicked(0) + messenger.send('wakeup') + + def getRecipeStr(self): + retval = '' + for box in self.boxList: + beanIndex = box.getSelectedIndex() + if beanIndex: + beanIndex -= 1 + beanLetter = GardenGlobals.BeanColorLetters[beanIndex] + retval += beanLetter + + return retval + + def __doPlant(self): + recipeStr = self.getRecipeStr() + selectedSpecial = self.specialButton.getSelectedIndex() + selectedSpecial -= 1 + messenger.send(self.doneEvent, [1, recipeStr, selectedSpecial]) + messenger.send('wakeup') + + def createBoxes(self): + zCoord = -0.15 + xIncrement = 0.1 + xPos = 0 + startingXCoord = -0.1 * float(self.maxBoxes) / 2.0 + 0.05 + for activeBox in xrange(self.availableBoxes): + xPos = xIncrement * activeBox + startingXCoord + if USE_SCROLLING_BEAN_BOX: + self.createScrollingBeanBox(activeBox, xPos, zCoord, True) + else: + self.createSpiffyBeanBox(activeBox, xPos, zCoord, True) + + for disabledBox in xrange(self.availableBoxes, self.maxBoxes): + xPos = xIncrement * disabledBox + startingXCoord + if USE_SCROLLING_BEAN_BOX: + self.createScrollingBeanBox(disabledBox, xPos, zCoord, False) + else: + self.createSpiffyBeanBox(disabledBox, xPos, zCoord, False) + + xPos += xIncrement * 2 + self.createSpecialBox(xPos, zCoord) + + def createSpecialBox(self, xPos, zPos): + geomColor = (1, 1, 1, 1) + if not self.specialBoxActive: + geomColor = (0.5, 0.5, 0.5, 1) + geomScaleX = 0.2 + geomScaleZ = 0.2 + self.specialButtonFrame = DirectFrame(parent=self, pos=(xPos, 0, zPos), geom=DGG.getDefaultDialogGeom(), geom_scale=(geomScaleX, 1.0, geomScaleZ), geom_color=geomColor, relief=None) + items = [] + if self.specialBoxActive: + gardenSpecials = base.localAvatar.getGardenSpecials() + tempItem = BoxItem(self.attachNewNode('blankSpecial')) + items.append(tempItem) + self.specialPhotoList = [] + for item in gardenSpecials: + tempItem = BoxItem(self.specialButtonFrame.attachNewNode('temp1')) + specialsPhoto = SpecialsPhoto.SpecialsPhoto(item[0], parent=tempItem) + specialsPhoto.setBackBounds(-geomScaleX / 2.0, geomScaleX / 2.0, -geomScaleZ / 2.0, geomScaleZ / 2.0) + specialsPhoto.setBackColor(1.0, 1.0, 1.0, 1.0) + items.append(tempItem) + self.specialPhotoList.append(specialsPhoto) + + self.specialButton = GenericBoxScrollList(self.specialButtonFrame, items, incButton_pos=(0, 0, -0.135), incButton_scale=(0.75, 1.0, -1.0), decButton_pos=(0, 0, 0.135), decButton_scale=(0.75, 1.0, 1.0), command=self.photoSpecialChanged) + return + + def photoSpecialChanged(self): + if not hasattr(self, 'specialButton'): + return + messenger.send('wakeup') + selectedSpecial = self.specialButton.getSelectedIndex() + if selectedSpecial == 0: + for photo in self.specialPhotoList: + photo.hide() + + else: + selectedSpecial -= 1 + self.specialPhotoList[selectedSpecial].show() + + def showFirstSpecial(self): + if len(self.specialButton['items']) > 1: + self.specialButton.scrollTo(self.specialButton.getSelectedIndex() + 1) + + def createScrollingBeanBox(self, index, xPos, zPos, active): + geomColor = (1, 1, 1, 1) + if not active: + geomColor = (0.5, 0.5, 0.5, 1) + boxFrame = DirectFrame(parent=self, pos=(xPos, 0, zPos), geom=DGG.getDefaultDialogGeom(), geom_scale=(0.1, 1.0, 0.1), geom_color=geomColor, relief=None) + items = [] + if active: + tempItem = BoxItem(self.attachNewNode('emptyBean')) + items.append(tempItem) + for curBean in xrange(len(GardenGlobals.BeanColors)): + tempItem = BoxItem(self.attachNewNode('bean-%d-%d' % (index, curBean))) + loadJellyBean(tempItem, curBean) + items.append(tempItem) + + box = GenericBoxScrollList(boxFrame, items, incButton_pos=(0, 0, -0.07), incButton_scale=(0.4, 1.0, -1.0), decButton_pos=(0, 0, 0.065), decButton_scale=(0.4, 1.0, 1.0)) + self.boxList.append(box) + return + + def spiffyBeanBoxClicked(self, index): + if self.jellyBeanPicker: + self.jellyBeanPicker.destroy() + outOfMoney = int(self.beanBank['text']) <= 0 + if self.boxList[index].getSelectedIndex() == 0 and outOfMoney: + return + if not CAN_CHANGE_BEAN_COLOR and self.boxList[index].getSelectedIndex(): + return + self.jellyBeanPicker = JellyBeanPicker(self, self.selectedNewBeanColor, index) + self.jellyBeanPicker.setPos(*JellyBeanPickerEndPos) + if DO_PICKER_INTERVAL: + boxPos = self.boxList[index].getPos() + self.jellyBeanPicker.setPos(boxPos) + self.jellyBeanPickerInterval = Sequence(Parallel(self.jellyBeanPicker.posInterval(duration=0.3, pos=VBase3(*JellyBeanPickerEndPos)), self.jellyBeanPicker.scaleInterval(duration=0.3, startScale=VBase3(JellyBeanPickerScale[0] / 10.0, JellyBeanPickerScale[1], JellyBeanPickerScale[2]), scale=VBase3(*JellyBeanPickerScale))), Func(self.jellyBeanPicker.setColorText)) + self.jellyBeanPickerInterval.start() + else: + self.jellyBeanPicker.setColorText() + + def matchBoxesToAvailableMoney(self): + outOfMoney = int(self.beanBank['text']) <= 0 + if outOfMoney: + for box in self.boxList: + if box.getSelectedIndex() == 0: + box['state'] = DGG.DISABLED + box.setState() + box.setColorScale(0.5, 0.5, 0.5, 1) + + else: + for box in self.boxList: + box['state'] = DGG.NORMAL + box.setState() + box.setColorScale(1, 1, 1, 1) + + if FORCE_LEFT_TO_RIGHT: + boxIndexToEnable = len(self.getRecipeStr()) + for i in xrange(0, boxIndexToEnable): + box = self.boxList[i] + if ONLY_ONE_SPIFFY_BOX_CAN_BE_CLICKED: + box['state'] = DGG.DISABLED + box.setState() + box.setColorScale(0.875, 0.875, 0.875, 1) + + if boxIndexToEnable < self.maxBoxes: + box = self.boxList[boxIndexToEnable] + if boxIndexToEnable >= self.availableBoxes: + box['state'] = DGG.DISABLED + box.setState() + box.setColorScale(0.5, 0.5, 0.5, 1) + for i in xrange(boxIndexToEnable + 1, len(self.boxList)): + box = self.boxList[i] + box['state'] = DGG.DISABLED + box.setState() + box.setColorScale(0.5, 0.5, 0.5, 1) + + def selectedNewBeanColor(self, boxPosition, beanIndex): + self.boxList[boxPosition].setSelectedIndex(beanIndex) + if self.jellyBeanPicker: + self.jellyBeanPicker.destroy() + self.jellyBeanPicker = None + cost = len(self.getRecipeStr()) + newMoney = base.localAvatar.getMoney() + base.localAvatar.getBankMoney() - cost + self.beanBank['text'] = str(newMoney) + self.matchBoxesToAvailableMoney() + messenger.send('wakeup') + if PICKER_ALWAYS_UP: + numBoxesFilled = len(self.getRecipeStr()) + if numBoxesFilled < self.availableBoxes: + self.spiffyBeanBoxClicked(numBoxesFilled) + return + + def createSpiffyBeanBox(self, index, xPos, zPos, active): + geomColor = (1, 1, 1, 1) + state = DGG.NORMAL + command = self.spiffyBeanBoxClicked + if not active: + geomColor = (0.5, 0.5, 0.5, 1) + command = None + state = DGG.DISABLED + newBox = SpiffyBeanBox(index=index, parent=self, pos=(xPos, 0, zPos), geom=DGG.getDefaultDialogGeom(), geom_scale=(0.1, 1.0, 0.1), geom_color=geomColor, relief=None, state=state, command=command, extraArgs=[index]) + self.boxList.append(newBox) + return diff --git a/toontown/estate/SpecialsPhoto.py b/toontown/estate/SpecialsPhoto.py new file mode 100755 index 00000000..18afd4d7 --- /dev/null +++ b/toontown/estate/SpecialsPhoto.py @@ -0,0 +1,223 @@ +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.fishing import FishGlobals +import GardenGlobals +from direct.actor import Actor +import random + +class DirectRegion(NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('DirectRegion') + + def __init__(self, parent = aspect2d): + NodePath.__init__(self) + self.assign(parent.attachNewNode('DirectRegion')) + + def destroy(self): + self.unload() + self.parent = None + return + + def setBounds(self, *bounds): + self.bounds = bounds + + def setColor(self, *colors): + self.color = colors + + def show(self): + pass + + def hide(self): + NodePath.hide(self) + + def load(self): + if not hasattr(self, 'cRender'): + self.cRender = NodePath('fishSwimRender') + self.fishSwimCamera = self.cRender.attachNewNode('fishSwimCamera') + self.cCamNode = Camera('fishSwimCam') + self.cLens = PerspectiveLens() + self.cLens.setFov(40, 40) + self.cLens.setNear(0.1) + self.cLens.setFar(100.0) + self.cCamNode.setLens(self.cLens) + self.cCamNode.setScene(self.cRender) + self.fishSwimCam = self.fishSwimCamera.attachNewNode(self.cCamNode) + cm = CardMaker('displayRegionCard') + apply(cm.setFrame, self.bounds) + self.card = card = self.attachNewNode(cm.generate()) + apply(card.setColor, self.color) + newBounds = card.getTightBounds() + ll = render2d.getRelativePoint(card, newBounds[0]) + ur = render2d.getRelativePoint(card, newBounds[1]) + newBounds = [ll.getX(), + ur.getX(), + ll.getZ(), + ur.getZ()] + newBounds = map(lambda x: max(0.0, min(1.0, (x + 1.0) / 2.0)), newBounds) + self.cDr = base.win.makeDisplayRegion(*newBounds) + self.cDr.setSort(10) + self.cDr.setClearColor(card.getColor()) + self.cDr.setClearDepthActive(1) + self.cDr.setClearColorActive(1) + self.cDr.setCamera(self.fishSwimCam) + return self.cRender + + def unload(self): + if hasattr(self, 'cRender'): + base.win.removeDisplayRegion(self.cDr) + del self.cRender + del self.fishSwimCamera + del self.cCamNode + del self.cLens + del self.fishSwimCam + del self.cDr + + +class SpecialsPhoto(NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('SpecialsPhoto') + + def __init__(self, type = None, parent = aspect2d): + NodePath.__init__(self) + self.assign(parent.attachNewNode('SpecialsPhoto')) + self.type = type + self.actor = None + self.sound = None + self.soundTrack = None + self.track = None + self.specialsFrame = None + return + + def destroy(self): + self.hide() + if hasattr(self, 'background'): + self.background.destroy() + del self.background + if hasattr(self, 'specialsFrame') and hasattr(self.specialsFrame, 'destroy'): + self.specialsFrame.destroy() + if hasattr(self, 'toonStatuary'): + if self.toonStatuary.toon: + self.toonStatuary.deleteToon() + self.type = None + del self.soundTrack + del self.track + self.parent = None + return + + def update(self, type): + self.type = type + + def setBackBounds(self, *bounds): + self.backBounds = bounds + + def setBackColor(self, *colors): + self.backColor = colors + + def load(self): + pass + + def makeSpecialsFrame(self, actor): + actor.setDepthTest(1) + actor.setDepthWrite(1) + if not hasattr(self, 'specialsDisplayRegion'): + self.specialsDisplayRegion = DirectRegion(parent=self) + apply(self.specialsDisplayRegion.setBounds, self.backBounds) + apply(self.specialsDisplayRegion.setColor, self.backColor) + frame = self.specialsDisplayRegion.load() + pitch = frame.attachNewNode('pitch') + rotate = pitch.attachNewNode('rotate') + scale = rotate.attachNewNode('scale') + actor.reparentTo(scale) + if actor.getTightBounds(): + bMin, bMax = actor.getTightBounds() + center = (bMin + bMax) / 2.0 + actor.setPos(-center[0], -center[1], -center[2]) + else: + actor.setPos(0, 0, 0) + pitch.setY(2.5) + return frame + + def loadModel(self, specialsIndex): + if specialsIndex == -1: + nodePath = self.attachNewNode('blank') + return nodePath + if specialsIndex >= 105 and specialsIndex <= 108: + from toontown.estate import DistributedToonStatuary + self.toonStatuary = DistributedToonStatuary.DistributedToonStatuary(None) + self.toonStatuary.setupStoneToon(base.localAvatar.style) + self.toonStatuary.poseToonFromSpecialsIndex(specialsIndex) + self.toonStatuary.toon.setH(180) + pedestalModelPath = GardenGlobals.Specials[specialsIndex]['photoModel'] + pedestal = loader.loadModel(pedestalModelPath) + self.toonStatuary.toon.reparentTo(pedestal) + pedestal.setScale(GardenGlobals.Specials[specialsIndex]['photoScale'] * 0.5) + return pedestal + elif specialsIndex == 135: + model = Actor.Actor() + modelPath = GardenGlobals.Specials[specialsIndex]['photoModel'] + anims = GardenGlobals.Specials[specialsIndex]['photoAnimation'] + animPath = modelPath + anims[1] + model.loadModel(modelPath + anims[0]) + model.loadAnims(dict([[anims[1], animPath]])) + frameNo = random.randint(1, 2) + model.pose(anims[1], 1) + model.setScale(GardenGlobals.Specials[specialsIndex]['photoScale'] * 0.1) + return model + else: + modelName = GardenGlobals.Specials[specialsIndex]['photoModel'] + nodePath = loader.loadModel(modelName) + desat = None + colorTuple = (1, 1, 1) + if desat and not desat.isEmpty(): + desat.setColorScale(colorTuple[0], colorTuple[1], colorTuple[2], 1.0) + else: + nodePath.setColorScale(colorTuple[0], colorTuple[1], colorTuple[2], 1.0) + nodePath.setScale(GardenGlobals.Specials[specialsIndex]['photoScale'] * 0.5) + return nodePath + return + + def show(self, showBackground = 0): + self.notify.debug('show') + messenger.send('wakeup') + if self.specialsFrame: + if hasattr(self.actor, 'cleanup'): + self.actor.cleanup() + if hasattr(self, 'specialsDisplayRegion'): + self.specialsDisplayRegion.unload() + self.hide() + self.actor = self.loadModel(self.type) + self.specialsFrame = self.makeSpecialsFrame(self.actor) + if showBackground: + if not hasattr(self, 'background'): + background = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + background = background.find('**/Fish_BG') + self.background = background + self.background.setPos(0, 15, 0) + self.background.setScale(11) + self.background.reparentTo(self.specialsFrame) + + def hide(self): + NodePath.hide(self) + if hasattr(self, 'specialsDisplayRegion'): + self.specialsDisplayRegion.unload() + if hasattr(self, 'background'): + self.background.hide() + if self.actor: + if hasattr(self.actor, 'stop'): + self.actor.stop() + self.actor.hide() + if self.sound: + self.sound.stop() + self.sound = None + if self.soundTrack: + self.soundTrack.pause() + self.soundTrack = None + if self.track: + self.track.pause() + self.track = None + if hasattr(self, 'toonStatuary'): + if self.toonStatuary.toon: + self.toonStatuary.deleteToon() + return + + def changeVariety(self, variety): + self.variety = variety diff --git a/toontown/estate/TableGlobals.py b/toontown/estate/TableGlobals.py new file mode 100755 index 00000000..21822a07 --- /dev/null +++ b/toontown/estate/TableGlobals.py @@ -0,0 +1 @@ +tableDrops = [(0, 0, 0, 0, 0, 0)] #TODO diff --git a/toontown/estate/ToonStatueSelectionGUI.py b/toontown/estate/ToonStatueSelectionGUI.py new file mode 100755 index 00000000..0bfe95cf --- /dev/null +++ b/toontown/estate/ToonStatueSelectionGUI.py @@ -0,0 +1,165 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.gui.DirectGui import * +from direct.gui.DirectScrolledList import * +from direct.interval.IntervalGlobal import * +from direct.task import Task +from panda3d.core import * + +from toontown.estate import DistributedToonStatuary +from toontown.estate import GardenGlobals +from toontown.estate import PlantingGUI +from otp.nametag import NametagGlobals, NametagConstants +from otp.nametag.NametagGroup import * +from toontown.toon import DistributedToon +from toontown.toon import Toon +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals + + +class ToonStatueSelectionGUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonStatueSelectionGUI') + + def __init__(self, doneEvent, specialBoxActive = False): + base.tssGUI = self + instructions = TTLocalizer.GardeningChooseToonStatue + instructionsPos = (0, 0.4) + DirectFrame.__init__(self, relief=None, state='normal', geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.5, 1.0, 1.0), frameSize=(-1, 1, -1, 1), pos=(0, 0, 0), text=instructions, text_wordwrap=18, text_scale=0.08, text_pos=instructionsPos) + self.initialiseoptions(ToonStatueSelectionGUI) + self.doneEvent = doneEvent + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(-0.3, 0, -0.35), text=TTLocalizer.PlantingGuiCancel, text_scale=0.06, text_pos=(0, -0.1), command=self.__cancel) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, pos=(0.3, 0, -0.35), text=TTLocalizer.PlantingGuiOk, text_scale=0.06, text_pos=(0, -0.1), command=self.__accept) + buttons.removeNode() + self.ffList = [] + self.friends = {} + self.doId2Dna = {} + self.textRolloverColor = Vec4(1, 1, 0, 1) + self.textDownColor = Vec4(0.5, 0.9, 1, 1) + self.textDisabledColor = Vec4(0.4, 0.8, 0.4, 1) + self.createFriendsList() + return + + def destroy(self): + self.doneEvent = None + self.previewToon.delete() + self.previewToon = None + for ff in self.ffList: + self.friends[ff].destroy() + + self.ffList = [] + self.friends = {} + self.doId2Dna = {} + self.scrollList.destroy() + DirectFrame.destroy(self) + return + + def __cancel(self): + messenger.send(self.doneEvent, [0, '', -1]) + messenger.send('wakeup') + + def __accept(self): + messenger.send(self.doneEvent, [1, '', DistributedToonStatuary.dnaCodeFromToonDNA(self.dnaSelected)]) + messenger.send('wakeup') + + def createFriendsList(self): + self.__makeFFlist() + if len(self.ffList) > 0: + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.scrollList = DirectScrolledList(parent=self, relief=None, incButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_pos=(0.0, 0.0, -0.316), incButton_image1_color=Vec4(1.0, 0.9, 0.4, 1.0), incButton_image3_color=Vec4(1.0, 1.0, 0.6, 0.5), incButton_scale=(1.0, 1.0, -1.0), decButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_pos=(0.0, 0.0, 0.117), decButton_image1_color=Vec4(1.0, 1.0, 0.6, 1.0), decButton_image3_color=Vec4(1.0, 1.0, 0.6, 0.6), itemFrame_pos=(-0.17, 0.0, 0.06), itemFrame_relief=DGG.SUNKEN, itemFrame_frameSize=(-0.01, + 0.35, + -0.35, + 0.04), itemFrame_frameColor=(0.85, 0.95, 1, 1), itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=8, itemFrame_scale=1.0, items=[]) + gui.removeNode() + self.scrollList.setPos(0.35, 0, 0.125) + self.scrollList.setScale(1.25) + clipper = PlaneNode('clipper') + clipper.setPlane(Plane(Vec3(-1, 0, 0), Point3(0.17, 0, 0))) + clipNP = self.scrollList.attachNewNode(clipper) + self.scrollList.setClipPlane(clipNP) + self.__makeScrollList() + return + + def checkFamily(self, doId): + test = 0 + for familyMember in base.cr.avList: + if familyMember.id == doId: + test = 1 + + return test + + def __makeFFlist(self): + playerAvatar = (base.localAvatar.doId, base.localAvatar.name, CCNonPlayer) + self.ffList.append(playerAvatar) + self.dnaSelected = base.localAvatar.style + self.createPreviewToon(self.dnaSelected) + for familyMember in base.cr.avList: + if familyMember.id != base.localAvatar.doId: + newFF = (familyMember.id, familyMember.name, CCNonPlayer) + self.ffList.append(newFF) + + for friendId in base.localAvatar.friendsList: + handle = base.cr.identifyFriend(friendId) + if handle and not self.checkFamily(friendId): + if hasattr(handle, 'getName'): + self.ffList.append((friendId, handle.getName(), NametagConstants.getFriendColor(handle))) + else: + self.notify.warning('Bad Handle for getName in makeFFlist') + + def __makeScrollList(self): + for ff in self.ffList: + ffbutton = self.makeFamilyButton(ff[0], ff[1], ff[2]) + if ffbutton: + self.scrollList.addItem(ffbutton, refresh=0) + self.friends[ff] = ffbutton + + self.scrollList.refresh() + + def makeFamilyButton(self, familyId, familyName, colorCode): + return DirectButton(relief=None, text=familyName, text_scale=0.04, text_align=TextNode.ALeft, text_fg=NametagConstants.NAMETAG_COLORS[colorCode][0][0], text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, text3_fg=self.textDisabledColor, textMayChange=0, command=self.__chooseFriend, extraArgs=[familyId, familyName]) + + def __chooseFriend(self, friendId, friendName): + messenger.send('wakeup') + if self.checkFamily(friendId): + if friendId == base.localAvatar.doId: + self.createPreviewToon(base.localAvatar.style) + elif friendId in self.doId2Dna: + self.createPreviewToon(self.doId2Dna[friendId]) + else: + familyAvatar = DistributedToon.DistributedToon(base.cr) + familyAvatar.doId = friendId + familyAvatar.forceAllowDelayDelete() + base.cr.getAvatarDetails(familyAvatar, self.__handleFamilyAvatar, 'DistributedToon') + else: + friend = base.cr.identifyFriend(friendId) + if friend: + self.createPreviewToon(friend.style) + + def __handleFamilyAvatar(self, gotData, avatar, dclass): + self.doId2Dna[avatar.doId] = avatar.style + self.createPreviewToon(avatar.style) + avatar.delete() + + def createPreviewToon(self, dna): + if hasattr(self, 'previewToon'): + self.previewToon.delete() + self.dnaSelected = dna + self.previewToon = Toon.Toon() + self.previewToon.setDNA(dna) + self.previewToon.loop('neutral') + self.previewToon.setH(180) + self.previewToon.setPos(-0.3, 0, -0.3) + self.previewToon.setScale(0.13) + self.previewToon.reparentTo(self) + self.previewToon.startBlink() + self.previewToon.startLookAround() + self.previewToon.getGeomNode().setDepthWrite(1) + self.previewToon.getGeomNode().setDepthTest(1) diff --git a/toontown/estate/TrunkGUI.py b/toontown/estate/TrunkGUI.py new file mode 100755 index 00000000..2a3017bb --- /dev/null +++ b/toontown/estate/TrunkGUI.py @@ -0,0 +1,555 @@ +from direct.showbase.PythonUtil import Functor +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.distributed import ClockDelta +from direct.fsm import StateData +from direct.task.Task import Task +import ClosetGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog +from toontown.toon import ToonDNA +from toontown.makeatoon.MakeAToonGlobals import * +from toontown.makeatoon import ShuffleButton + +class TrunkGUI(StateData.StateData): + notify = directNotify.newCategory('TrunkGUI') + + def __init__(self, isOwner, doneEvent, cancelEvent, swapHatEvent, swapGlassesEvent, swapBackpackEvent, swapShoesEvent, deleteEvent, hatList = None, glassesList = None, backpackList = None, shoesList = None): + StateData.StateData.__init__(self, doneEvent) + self.toon = None + self.hatList = hatList + self.glassesList = glassesList + self.backpackList = backpackList + self.shoesList = shoesList + self.isOwner = isOwner + self.swapHatEvent = swapHatEvent + self.swapGlassesEvent = swapGlassesEvent + self.swapBackpackEvent = swapBackpackEvent + self.swapShoesEvent = swapShoesEvent + self.deleteEvent = deleteEvent + self.cancelEvent = cancelEvent + self.genderChange = 0 + self.verify = None + + def load(self): + self.matGui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + guiRArrowUp = self.matGui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowRollover = self.matGui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowDown = self.matGui.find('**/tt_t_gui_mat_arrowDown') + guiRArrowDisabled = self.matGui.find('**/tt_t_gui_mat_arrowDisabled') + guiArrowRotateUp = self.matGui.find('**/tt_t_gui_mat_arrowRotateUp') + guiArrowRotateDown = self.matGui.find('**/tt_t_gui_mat_arrowRotateDown') + self.shuffleFrame = self.matGui.find('**/tt_t_gui_mat_shuffleFrame') + shuffleArrowUp = self.matGui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDown = self.matGui.find('**/tt_t_gui_mat_shuffleArrowDown') + shuffleArrowRollover = self.matGui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDisabled = self.matGui.find('**/tt_t_gui_mat_shuffleArrowDisabled') + self.parentFrame = DirectFrame(relief=DGG.RAISED, pos=(0.98, 0, 0.216), frameColor=(1, 0, 0, 0)) + + def addFrame(posZ, text): + return DirectFrame(parent=self.parentFrame, image=self.shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, posZ), hpr=(0, 0, 3), scale=1.2, frameColor=(1, 1, 1, 1), text=text, text_scale=0.0575, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + + def addButton(parent, scale, hoverScale, posX, command, extraArg): + return DirectButton(parent=parent, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=scale, image1_scale=hoverScale, image2_scale=hoverScale, pos=(posX, 0, 0), command=command, extraArgs=[extraArg]) + + self.countFrame = addFrame(0.37, TTLocalizer.ClothesGUICount % (0, 0)) + self.hatFrame = addFrame(0.1, TTLocalizer.TrunkHatGUI) + self.hatLButton = addButton(self.hatFrame, halfButtonScale, halfButtonHoverScale, -0.2, self.swapHat, -1) + self.hatRButton = addButton(self.hatFrame, halfButtonInvertScale, halfButtonInvertHoverScale, 0.2, self.swapHat, 1) + self.glassesFrame = addFrame(-0.15, TTLocalizer.TrunkGlassesGUI) + self.glassesLButton = addButton(self.glassesFrame, halfButtonScale, halfButtonHoverScale, -0.2, self.swapGlasses, -1) + self.glassesRButton = addButton(self.glassesFrame, halfButtonInvertScale, halfButtonInvertHoverScale, 0.2, self.swapGlasses, 1) + self.backpackFrame = addFrame(-0.4, TTLocalizer.TrunkBackpackGUI) + self.backpackLButton = addButton(self.backpackFrame, halfButtonScale, halfButtonHoverScale, -0.2, self.swapBackpack, -1) + self.backpackRButton = addButton(self.backpackFrame, halfButtonInvertScale, halfButtonInvertHoverScale, 0.2, self.swapBackpack, 1) + self.shoesFrame = addFrame(-0.65, TTLocalizer.TrunkShoesGUI) + self.shoesLButton = addButton(self.shoesFrame, halfButtonScale, halfButtonHoverScale, -0.2, self.swapShoes, -1) + self.shoesRButton = addButton(self.shoesFrame, halfButtonInvertScale, halfButtonInvertHoverScale, 0.2, self.swapShoes, 1) + self.parentFrame.hide() + self.shuffleFetchMsg = 'TrunkShuffle' + self.shuffleButton = ShuffleButton.ShuffleButton(self, self.shuffleFetchMsg) + self.gui = loader.loadModel('phase_3/models/gui/create_a_toon_gui') + self.cancelButton = DirectButton(relief=None, image=(self.gui.find('**/CrtAtoon_Btn2_UP'), self.gui.find('**/CrtAtoon_Btn2_DOWN'), self.gui.find('**/CrtAtoon_Btn2_RLLVR')), pos=(0.15, 0, -0.85), command=self.__handleCancel, text=('', TTLocalizer.MakeAToonCancel, TTLocalizer.MakeAToonCancel), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, -0.03), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.cancelButton.hide() + self.rotateL = DirectButton(relief=None, pos=(-0.15, 0, 0.85), image=(guiArrowRotateUp, + guiArrowRotateDown, + guiArrowRotateUp, + guiArrowRotateDown), image_scale=(-0.7, 0.7, 0.7), image1_scale=(-0.8, 0.8, 0.8), image2_scale=(-0.8, 0.8, 0.8)) + self.rotateL.hide() + self.rotateL.bind(DGG.B1PRESS, self.__rotateLDown) + self.rotateL.bind(DGG.B1RELEASE, self.__rotateLUp) + self.rotateR = DirectButton(relief=None, pos=(0.15, 0, 0.85), image=(guiArrowRotateUp, + guiArrowRotateDown, + guiArrowRotateUp, + guiArrowRotateDown), image_scale=(0.7, 0.7, 0.7), image1_scale=(0.8, 0.8, 0.8), image2_scale=(0.8, 0.8, 0.8)) + self.rotateR.hide() + self.rotateR.bind(DGG.B1PRESS, self.__rotateRDown) + self.rotateR.bind(DGG.B1RELEASE, self.__rotateRUp) + if self.isOwner: + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui.bam') + trashImage = (trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_RLVR')) + self.trashPanel = DirectFrame(parent=aspect2d, image=DGG.getDefaultDialogGeom(), image_color=(1, 1, 0.75, 0.8), image_scale=(0.36, 0, 1.2), pos=(-.86, 0, 0.1), relief=None) + + def addTrashButton(posZ, text, extraArg): + return DirectButton(parent=self.trashPanel, image=trashImage, relief=None, pos=(-0.09, 0, posZ), command=self.__handleDelete, text=text, extraArgs=[extraArg], scale=(0.5, 0.5, 0.5), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.12, text_pos=(0.3, 0), text_fg=(0.8, 0.2, 0.2, 1), text_shadow=(0, 0, 0, 1), textMayChange=0) + + self.hatTrashButton = addTrashButton(0.5, TTLocalizer.TrunkDeleteHat, ToonDNA.HAT) + self.glassesTrashButton = addTrashButton(0.2, TTLocalizer.TrunkDeleteGlasses, ToonDNA.GLASSES) + self.backpackTrashButton = addTrashButton(-0.1, TTLocalizer.TrunkDeleteBackpack, ToonDNA.BACKPACK) + self.shoesTrashButton = addTrashButton(-0.4, TTLocalizer.TrunkDeleteShoes, ToonDNA.SHOES) + self.button = DirectButton(relief=None, image=(self.gui.find('**/CrtAtoon_Btn1_UP'), self.gui.find('**/CrtAtoon_Btn1_DOWN'), self.gui.find('**/CrtAtoon_Btn1_RLLVR')), pos=(-0.15, 0, -0.85), command=self.__handleButton, text=('', TTLocalizer.MakeAToonDone, TTLocalizer.MakeAToonDone), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, -0.03), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + trashcanGui.removeNode() + + def unload(self): + taskMgr.remove(self.taskName('rotateL')) + taskMgr.remove(self.taskName('rotateR')) + self.ignore('verifyDone') + self.gui.removeNode() + del self.gui + self.matGui.removeNode() + del self.matGui + del self.shuffleFrame + self.parentFrame.destroy() + self.countFrame.destroy() + self.hatFrame.destroy() + self.glassesFrame.destroy() + self.backpackFrame.destroy() + self.shoesFrame.destroy() + self.hatLButton.destroy() + self.hatRButton.destroy() + self.glassesLButton.destroy() + self.glassesRButton.destroy() + self.backpackLButton.destroy() + self.backpackRButton.destroy() + self.shoesLButton.destroy() + self.shoesRButton.destroy() + del self.countFrame + del self.parentFrame + del self.hatFrame + del self.glassesFrame + del self.backpackFrame + del self.shoesFrame + del self.hatLButton + del self.hatRButton + del self.glassesLButton + del self.glassesRButton + del self.backpackLButton + del self.backpackRButton + del self.shoesLButton + del self.shoesRButton + self.shuffleButton.unload() + self.ignore('MAT-newToonCreated') + self.cancelButton.destroy() + del self.cancelButton + self.rotateL.destroy() + del self.rotateL + self.rotateR.destroy() + del self.rotateR + if self.isOwner: + self.hatTrashButton.destroy() + self.glassesTrashButton.destroy() + self.backpackTrashButton.destroy() + self.shoesTrashButton.destroy() + self.button.destroy() + del self.hatTrashButton + del self.glassesTrashButton + del self.backpackTrashButton + del self.shoesTrashButton + del self.button + self.trashPanel.destroy() + del self.trashPanel + if self.verify: + self.verify.cleanup() + del self.verify + + def showButtons(self): + self.parentFrame.show() + self.cancelButton.show() + self.rotateL.show() + self.rotateR.show() + if self.isOwner: + self.hatTrashButton.show() + self.glassesTrashButton.show() + self.backpackTrashButton.show() + self.shoesTrashButton.show() + self.button.show() + + def hideButtons(self): + self.parentFrame.hide() + self.cancelButton.hide() + self.rotateL.hide() + self.rotateR.hide() + if self.isOwner: + self.hatTrashButton.hide() + self.glassesTrashButton.hide() + self.backpackTrashButton.hide() + self.shoesTrashButton.hide() + self.button.hide() + + def enter(self, toon): + self.notify.debug('enter') + base.disableMouse() + self.toon = toon + self.setupScrollInterface() + currHat = self.toon.hat + currHatIdx = self.hats.index(currHat) + self.swapHat(currHatIdx - self.hatChoice) + currGlasses = self.toon.glasses + currGlassesIdx = self.glasses.index(currGlasses) + self.swapGlasses(currGlassesIdx - self.glassesChoice) + currBackpack = self.toon.backpack + currBackpackIdx = self.backpacks.index(currBackpack) + self.swapBackpack(currBackpackIdx - self.backpackChoice) + currShoes = self.toon.shoes + currShoesIdx = self.shoes.index(currShoes) + self.swapShoes(currShoesIdx - self.shoesChoice) + choicePool = [self.hats, + self.glasses, + self.backpacks, + self.shoes] + self.shuffleButton.setChoicePool(choicePool) + self.accept(self.shuffleFetchMsg, self.changeAccessories) + self.acceptOnce('MAT-newToonCreated', self.shuffleButton.cleanHistory) + + def exit(self): + try: + del self.toon + except: + self.notify.warning('TrunkGUI: toon not found') + + self.hideButtons() + self.ignore('enter') + self.ignore('next') + self.ignore('last') + self.ignore(self.shuffleFetchMsg) + + def setupButtons(self): + self.acceptOnce('last', self.__handleBackward) + self.acceptOnce('next', self.__handleForward) + return None + + def setupScrollInterface(self): + self.notify.debug('setupScrollInterface') + if self.hatList == None: + self.hatList = self.toon.getHatList() + if self.glassesList == None: + self.glassesList = self.toon.getGlassesList() + if self.backpackList == None: + self.backpackList = self.toon.getBackpackList() + if self.shoesList == None: + self.shoesList = self.toon.getShoesList() + self.hats = [] + self.glasses = [] + self.backpacks = [] + self.shoes = [] + choices = [0, 0, 0, 0] + currentAccessories = (self.toon.getHat(), self.toon.getGlasses(), self.toon.getBackpack(), self.toon.getShoes()) + + self.hats.append((0, 0, 0)) + self.glasses.append((0, 0, 0)) + self.backpacks.append((0, 0, 0)) + self.shoes.append((0, 0, 0)) + + i = 0 + while i < len(self.hatList): + self.hats.append((self.hatList[i], self.hatList[i + 1], self.hatList[i + 2])) + i = i + 3 + + i = 0 + while i < len(self.glassesList): + self.glasses.append((self.glassesList[i], self.glassesList[i + 1], self.glassesList[i + 2])) + i = i + 3 + + i = 0 + while i < len(self.backpackList): + self.backpacks.append((self.backpackList[i], self.backpackList[i + 1], self.backpackList[i + 2])) + i = i + 3 + + i = 0 + while i < len(self.shoesList): + self.shoes.append((self.shoesList[i], self.shoesList[i + 1], self.shoesList[i + 2])) + i = i + 3 + + for i, list in enumerate((self.hats, self.glasses, self.backpacks, self.shoes)): + if len(list) >= 3: + index = list.index(currentAccessories[i]) + list[index], list[1] = list[1], list[index] + choices[i] = 1 + + self.hatChoice = choices[0] + self.glassesChoice = choices[1] + self.backpackChoice = choices[2] + self.shoesChoice = choices[3] + self.swapHat(0) + self.swapGlasses(0) + self.swapBackpack(0) + self.swapShoes(0) + self.updateTrashButtons() + self.setupButtons() + self.updateCountFrame() + + def updateTrashButtons(self): + if not self.isOwner: + return + if len(self.hats) < 2 or self.toon.hat[0] == 0: + self.hatTrashButton['state'] = DGG.DISABLED + else: + self.hatTrashButton['state'] = DGG.NORMAL + if len(self.glasses) < 2 or self.toon.glasses[0] == 0: + self.glassesTrashButton['state'] = DGG.DISABLED + else: + self.glassesTrashButton['state'] = DGG.NORMAL + if len(self.backpacks) < 2 or self.toon.backpack[0] == 0: + self.backpackTrashButton['state'] = DGG.DISABLED + else: + self.backpackTrashButton['state'] = DGG.NORMAL + if len(self.shoes) < 2 or self.toon.shoes[0] == 0: + self.shoesTrashButton['state'] = DGG.DISABLED + else: + self.shoesTrashButton['state'] = DGG.NORMAL + self.updateCountFrame() + + def updateCountFrame(self): + accessories = (len(self.hats) + len(self.glasses) + len(self.backpacks) + len(self.shoes)) - 4 + self.countFrame['text'] = TTLocalizer.ClothesGUICount % (accessories, ToontownGlobals.MaxAccessories) + + def rotateToonL(self, task): + self.toon.setH(self.toon.getH() - 4) + return Task.cont + + def rotateToonR(self, task): + self.toon.setH(self.toon.getH() + 4) + return Task.cont + + def __rotateLUp(self, event): + messenger.send('wakeup') + taskMgr.remove(self.taskName('rotateL')) + + def __rotateLDown(self, event): + messenger.send('wakeup') + task = Task(self.rotateToonL) + taskMgr.add(task, self.taskName('rotateL')) + + def __rotateRUp(self, event): + messenger.send('wakeup') + taskMgr.remove(self.taskName('rotateR')) + + def __rotateRDown(self, event): + messenger.send('wakeup') + task = Task(self.rotateToonR) + taskMgr.add(task, self.taskName('rotateR')) + + def setGender(self, gender): + self.ownerGender = gender + self.genderChange = 1 + + def swapHat(self, offset): + length = len(self.hats) + self.hatChoice += offset + if self.hatChoice <= 0: + self.hatChoice = 0 + self.updateScrollButtons(self.hatChoice, length, 0, self.hatLButton, self.hatRButton) + if self.hatChoice < 0 or self.hatChoice >= len(self.hats) or len(self.hats[self.hatChoice]) != 3: + self.notify.warning('hatChoice index is out of range!') + return None + hat = self.hats[self.hatChoice] + self.toon.setHat(hat[0], hat[1], hat[2]) + if self.swapHatEvent != None: + messenger.send(self.swapHatEvent) + messenger.send('wakeup') + + def swapGlasses(self, offset): + length = len(self.glasses) + self.glassesChoice += offset + if self.glassesChoice <= 0: + self.glassesChoice = 0 + self.updateScrollButtons(self.glassesChoice, length, 0, self.glassesLButton, self.glassesRButton) + if self.glassesChoice < 0 or self.glassesChoice >= len(self.glasses) or len(self.glasses[self.glassesChoice]) != 3: + self.notify.warning('glassesChoice index is out of range!') + return None + glasses = self.glasses[self.glassesChoice] + self.toon.setGlasses(glasses[0], glasses[1], glasses[2]) + if self.swapGlassesEvent != None: + messenger.send(self.swapGlassesEvent) + messenger.send('wakeup') + + def swapBackpack(self, offset): + length = len(self.backpacks) + self.backpackChoice += offset + if self.backpackChoice <= 0: + self.backpackChoice = 0 + self.updateScrollButtons(self.backpackChoice, length, 0, self.backpackLButton, self.backpackRButton) + if self.backpackChoice < 0 or self.backpackChoice >= len(self.backpacks) or len(self.backpacks[self.backpackChoice]) != 3: + self.notify.warning('backpackChoice index is out of range!') + return None + backpack = self.backpacks[self.backpackChoice] + self.toon.setBackpack(backpack[0], backpack[1], backpack[2]) + if self.swapBackpackEvent != None: + messenger.send(self.swapBackpackEvent) + messenger.send('wakeup') + + def swapShoes(self, offset): + length = len(self.shoes) + self.shoesChoice += offset + if self.shoesChoice <= 0: + self.shoesChoice = 0 + self.updateScrollButtons(self.shoesChoice, length, 0, self.shoesLButton, self.shoesRButton) + if self.shoesChoice < 0 or self.shoesChoice >= len(self.shoes) or len(self.shoes[self.shoesChoice]) != 3: + self.notify.warning('shoesChoice index is out of range!') + return None + shoes = self.shoes[self.shoesChoice] + self.toon.setShoes(shoes[0], shoes[1], shoes[2]) + if self.swapShoesEvent != None: + messenger.send(self.swapShoesEvent) + messenger.send('wakeup') + + def updateScrollButtons(self, choice, length, startTex, lButton, rButton): + if choice >= length - 1: + rButton['state'] = DGG.DISABLED + else: + rButton['state'] = DGG.NORMAL + if choice <= 0: + lButton['state'] = DGG.DISABLED + else: + lButton['state'] = DGG.NORMAL + + def __handleForward(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) + + def __handleBackward(self): + self.doneStatus = 'last' + messenger.send(self.doneEvent) + + def resetClothes(self, style): + if self.toon: + oldHat = style[ToonDNA.HAT] + oldGlasses = style[ToonDNA.GLASSES] + oldBackpack = style[ToonDNA.BACKPACK] + oldShoes = style[ToonDNA.SHOES] + self.toon.setHat(oldHat[0], oldHat[1], oldHat[2]) + self.toon.setGlasses(oldGlasses[0], oldGlasses[1], oldGlasses[2]) + self.toon.setBackpack(oldBackpack[0], oldBackpack[1], oldBackpack[2]) + self.toon.setShoes(oldShoes[0], oldShoes[1], oldShoes[2]) + self.toon.loop('neutral', 0) + + def changeAccessories(self): + self.notify.debug('Entering changeAccessories') + NoItem = (0, 0, 0) + newChoice = self.shuffleButton.getCurrChoice() + if newChoice[0] in self.hats: + newHatIndex = self.hats.index(newChoice[0]) + else: + newHatIndex = self.hats.index(NoItem) + if newChoice[1] in self.glasses: + newGlassesIndex = self.glasses.index(newChoice[1]) + else: + newGlassesIndex = self.glasses.index(NoItem) + if newChoice[2] in self.backpacks: + newBackpackIndex = self.backpacks.index(newChoice[2]) + else: + newBackpackIndex = self.backpacks.index(NoItem) + if newChoice[3] in self.shoes: + newShoesIndex = self.shoes.index(newChoice[3]) + else: + newShoesIndex = self.shoes.index(NoItem) + oldHatIndex = self.hatChoice + oldGlassesIndex = self.glassesChoice + oldBackpackIndex = self.backpackChoice + oldShoesIndex = self.shoesChoice + self.swapHat(newHatIndex - oldHatIndex) + self.swapGlasses(newGlassesIndex - oldGlassesIndex) + self.swapBackpack(newBackpackIndex - oldBackpackIndex) + self.swapShoes(newShoesIndex - oldShoesIndex) + + def getCurrToonSetting(self): + return [self.hats[self.hatChoice], + self.glasses[self.glassesChoice], + self.backpacks[self.backpackChoice], + self.shoes[self.shoesChoice]] + + def removeHat(self, index): + listLen = len(self.hats) + if index < listLen: + del self.hats[index] + if self.hatChoice > index: + self.hatChoice -= 1 + elif self.hatChoice == index: + self.hatChoice = 0 + return 1 + return 0 + + def removeGlasses(self, index): + listLen = len(self.glasses) + if index < listLen: + del self.glasses[index] + if self.glassesChoice > index: + self.glassesChoice -= 1 + elif self.glassesChoice == index: + self.glassesChoice = 0 + return 1 + return 0 + + def removeBackpack(self, index): + listLen = len(self.backpacks) + if index < listLen: + del self.backpacks[index] + if self.backpackChoice > index: + self.backpackChoice -= 1 + elif self.backpackChoice == index: + self.backpackChoice = 0 + return 1 + return 0 + + def removeShoes(self, index): + listLen = len(self.shoes) + if index < listLen: + del self.shoes[index] + if self.shoesChoice > index: + self.shoesChoice -= 1 + elif self.shoesChoice == index: + self.shoesChoice = 0 + return 1 + return 0 + + def __handleButton(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) + messenger.send('wakeup') + + def __handleCancel(self): + messenger.send(self.cancelEvent) + messenger.send('wakeup') + + def __handleDelete(self, which): + abortDeletion = False + if which == ToonDNA.HAT: + item = TTLocalizer.TrunkHat + elif which == ToonDNA.GLASSES: + item = TTLocalizer.TrunkGlasses + elif which == ToonDNA.BACKPACK: + item = TTLocalizer.TrunkBackpack + else: + item = TTLocalizer.TrunkShoes + self.verify = TTDialog.TTGlobalDialog(doneEvent='verifyDone', message=TTLocalizer.ClosetVerifyDelete % item, style=TTDialog.TwoChoice) + self.verify.show() + self.accept('verifyDone', Functor(self.__handleVerifyDelete, which)) + messenger.send('wakeup') + + def __handleVerifyDelete(self, which): + status = self.verify.doneStatus + self.ignore('verifyDone') + self.verify.cleanup() + del self.verify + self.verify = None + if status == 'ok': + messenger.send(self.deleteEvent, [which]) + messenger.send('wakeup') + return + + def taskName(self, idString): + return idString + '-TrunkGUI' diff --git a/toontown/estate/__init__.py b/toontown/estate/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/estate/houseDesign.py b/toontown/estate/houseDesign.py new file mode 100755 index 00000000..f87110ab --- /dev/null +++ b/toontown/estate/houseDesign.py @@ -0,0 +1,1663 @@ +from direct.directtools.DirectSelection import * +from direct.directtools.DirectUtil import ROUND_TO +from direct.directtools.DirectGeometry import LineNodePath +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from toontown.catalog import CatalogFurnitureItem +from toontown.catalog import CatalogItemTypes +from direct.showbase import PythonUtil +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +camPos50 = (Point3(0.0, -10.0, 50.0), + Point3(0.0, -9.66, 49.06), + Point3(0.0, 1.5, 12.38), + Point3(0.0, 1.5, -3.1), + 1) +camPos40 = (Point3(0.0, -15.0, 40.0), + Point3(0.0, -14.5, 39.13), + Point3(0.0, 1.5, 12.38), + Point3(0.0, 1.5, -3.1), + 1) +camPos30 = (Point3(0.0, -20.0, 30.0), + Point3(0.0, -19.29, 29.29), + Point3(0.0, 1.5, 12.38), + Point3(0.0, 1.5, -3.1), + 1) +camPos20 = (Point3(0.0, -20.0, 20.0), + Point3(0.0, -19.13, 19.5), + Point3(0.0, 1.5, 12.38), + Point3(0.0, 1.5, -3.1), + 1) +camPosList = [camPos20, + camPos30, + camPos40, + camPos50] +DEFAULT_CAM_INDEX = 2 +NormalPickerPanelColor = (1, 0.9, 0.745, 1) +DisabledPickerPanelColor = (0.7, 0.65, 0.58, 1) +DeletePickerPanelColor = (1, 0.4, 0.4, 1) +DisabledDeletePickerPanelColor = (0.7, 0.3, 0.3, 1) + +class FurnitureItemPanel(DirectButton): + + def __init__(self, item, itemId, command = None, deleteMode = 0, withinFunc = None, helpCategory = None): + self.item = item + self.itemId = itemId + self.command = command + self.origHelpCategory = helpCategory + self.deleteMode = deleteMode + if self.deleteMode: + framePanelColor = DeletePickerPanelColor + else: + framePanelColor = NormalPickerPanelColor + DirectButton.__init__(self, relief=DGG.RAISED, frameSize=(-0.25, + 0.25, + -0.2, + 0.2), frameColor=framePanelColor, borderWidth=(0.02, 0.02), command=self.clicked) + if self.deleteMode: + helpCategory = 'FurnitureItemPanelDelete' + self.bindHelpText(helpCategory) + if withinFunc: + self.bind(DGG.WITHIN, lambda event: withinFunc(self.itemId)) + self.initialiseoptions(FurnitureItemPanel) + self.load() + + def show(self): + DirectFrame.show(self) + if self.ival: + self.ival.resume() + + def hide(self): + DirectFrame.hide(self) + if self.ival: + self.ival.pause() + + def load(self): + panelWidth = 7 + panelCenter = 0 + self.picture, self.ival = self.item.getPicture(base.localAvatar) + if self.picture: + self.picture.reparentTo(self) + self.picture.setScale(0.14) + self.picture.setPos(0, 0, -0.02) + text = self.item.getName() + text_pos = (0, -0.1, 0) + else: + text = self.item.getTypeName() + ': ' + self.item.getName() + text_pos = (0, -0.3, 0) + if self.ival: + self.ival.loop() + self.ival.pause() + self.nameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.17), scale=0.45, text=text, text_scale=0.15, text_fg=(0, 0, 0, 1), text_pos=text_pos, text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=panelWidth) + return + + def clicked(self): + self.command(self.item, self.itemId) + + def unload(self): + if self.item.hasPicture: + self.item.cleanupPicture() + del self.item + self.nameLabel.destroy() + del self.nameLabel + if self.ival: + self.ival.finish() + del self.ival + del self.picture + self.command = None + return + + def destroy(self): + self.unload() + DirectButton.destroy(self) + + def bindHelpText(self, category): + self.unbind(DGG.ENTER) + self.unbind(DGG.EXIT) + if category is None: + category = self.origHelpCategory + self.bind(DGG.ENTER, base.cr.objectManager.showHelpText, extraArgs=[category, self.item.getName()]) + self.bind(DGG.EXIT, base.cr.objectManager.hideHelpText) + return + + def setDeleteMode(self, deleteMode): + self.deleteMode = deleteMode + self.__updateAppearance() + + def enable(self, enabled): + if enabled: + self['state'] = DGG.NORMAL + else: + self['state'] = DGG.DISABLED + self.__updateAppearance() + + def __updateAppearance(self): + color = NormalPickerPanelColor + relief = DGG.RAISED + if self.deleteMode: + if self['state'] == DGG.DISABLED: + color = DisabledDeletePickerPanelColor + relief = DGG.SUNKEN + else: + color = DeletePickerPanelColor + relief = DGG.RAISED + elif self['state'] == DGG.DISABLED: + color = DisabledPickerPanelColor + relief = DGG.SUNKEN + else: + color = NormalPickerPanelColor + relief = DGG.RAISED + self['frameColor'] = color + + +class MovableObject(NodePath, DirectObject): + + def __init__(self, dfitem, parent = render): + NodePath.__init__(self) + self.assign(dfitem) + self.dfitem = dfitem + dfitem.transmitRelativeTo = dfitem.getParent() + self.reparentTo(parent) + self.setTag('movableObject', '1') + self.builtInCNodes = self.findAllMatches('**/+CollisionNode') + self.numBuiltInNodes = self.builtInCNodes.getNumPaths() + self.stashBuiltInCollisionNodes() + shadows = self.findAllMatches('**/*shadow*') + shadows.addPathsFrom(self.findAllMatches('**/*Shadow*')) + shadows.stash() + flags = self.dfitem.item.getFlags() + if flags & CatalogFurnitureItem.FLPainting: + self.setOnFloor(0) + self.setOnWall(1) + else: + self.setOnFloor(1) + self.setOnWall(0) + if flags & CatalogFurnitureItem.FLOnTable: + self.setOnTable(1) + else: + self.setOnTable(0) + if flags & CatalogFurnitureItem.FLRug: + self.setIsRug(1) + else: + self.setIsRug(0) + if flags & CatalogFurnitureItem.FLIsTable: + self.setIsTable(1) + else: + self.setIsTable(0) + m = self.getTransform() + self.iPosHpr() + bMin, bMax = self.bounds = self.getTightBounds() + bMin -= Vec3(0.1, 0.1, 0) + bMax += Vec3(0.1, 0.1, 0) + self.c0 = Point3(bMin[0], bMin[1], 0.2) + self.c1 = Point3(bMax[0], bMin[1], 0.2) + self.c2 = Point3(bMax[0], bMax[1], 0.2) + self.c3 = Point3(bMin[0], bMax[1], 0.2) + self.center = (bMin + bMax) / 2.0 + if flags & CatalogFurnitureItem.FLPainting: + self.dragPoint = Vec3(self.center[0], bMax[1], self.center[2]) + else: + self.dragPoint = Vec3(self.center[0], self.center[1], bMin[2]) + delta = self.dragPoint - self.c0 + self.radius = min(delta[0], delta[1]) + if self.getOnWall(): + self.setWallOffset(0.1) + else: + self.setWallOffset(self.radius + 0.1) + self.makeCollisionBox() + self.setTransform(m) + self.unstashBuiltInCollisionNodes() + shadows.unstash() + + def resetMovableObject(self): + self.unstashBuiltInCollisionNodes() + self.collisionNodePath.removeNode() + self.clearTag('movableObject') + + def setOnFloor(self, fOnFloor): + self.fOnFloor = fOnFloor + + def getOnFloor(self): + return self.fOnFloor + + def setOnWall(self, fOnWall): + self.fOnWall = fOnWall + + def getOnWall(self): + return self.fOnWall + + def setOnTable(self, fOnTable): + self.fOnTable = fOnTable + + def getOnTable(self): + return self.fOnTable + + def setIsRug(self, fIsRug): + self.fIsRug = fIsRug + + def getIsRug(self): + return self.fIsRug + + def setIsTable(self, fIsTable): + self.fIsTable = fIsTable + + def getIsTable(self): + return self.fIsTable + + def setWallOffset(self, offset): + self.wallOffset = offset + + def getWallOffset(self): + return self.wallOffset + + def destroy(self): + self.removeNode() + + def stashBuiltInCollisionNodes(self): + self.builtInCNodes.stash() + + def unstashBuiltInCollisionNodes(self): + self.builtInCNodes.unstash() + + def getFloorBitmask(self): + if self.getOnTable(): + return ToontownGlobals.FloorBitmask | ToontownGlobals.FurnitureTopBitmask + else: + return ToontownGlobals.FloorBitmask + + def getWallBitmask(self): + if self.getIsRug() or self.getOnWall(): + return ToontownGlobals.WallBitmask + else: + return ToontownGlobals.WallBitmask | ToontownGlobals.FurnitureSideBitmask + + def makeCollisionBox(self): + self.collisionNodePath = self.attachNewNode('furnitureCollisionNode') + if self.getIsRug() or self.getOnWall(): + return + mx = self.bounds[0][0] - 0.01 + Mx = self.bounds[1][0] + 0.01 + my = self.bounds[0][1] - 0.01 + My = self.bounds[1][1] + 0.01 + mz = self.bounds[0][2] + Mz = self.bounds[1][2] + cn = CollisionNode('sideCollisionNode') + cn.setIntoCollideMask(ToontownGlobals.FurnitureSideBitmask) + self.collisionNodePath.attachNewNode(cn) + cp = CollisionPolygon(Point3(mx, My, mz), Point3(mx, my, mz), Point3(mx, my, Mz), Point3(mx, My, Mz)) + cn.addSolid(cp) + cp = CollisionPolygon(Point3(Mx, my, mz), Point3(Mx, My, mz), Point3(Mx, My, Mz), Point3(Mx, my, Mz)) + cn.addSolid(cp) + cp = CollisionPolygon(Point3(mx, my, mz), Point3(Mx, my, mz), Point3(Mx, my, Mz), Point3(mx, my, Mz)) + cn.addSolid(cp) + cp = CollisionPolygon(Point3(Mx, My, mz), Point3(mx, My, mz), Point3(mx, My, Mz), Point3(Mx, My, Mz)) + cn.addSolid(cp) + if self.getIsTable(): + cn = CollisionNode('topCollisionNode') + cn.setIntoCollideMask(ToontownGlobals.FurnitureTopBitmask) + self.collisionNodePath.attachNewNode(cn) + cp = CollisionPolygon(Point3(mx, my, Mz), Point3(Mx, my, Mz), Point3(Mx, My, Mz), Point3(mx, My, Mz)) + cn.addSolid(cp) + + +class ObjectManager(NodePath, DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('ObjectManager') + + def __init__(self): + NodePath.__init__(self) + self.assign(render.attachNewNode('objectManager')) + self.objectDict = {} + self.selectedObject = None + self.movingObject = 0 + self.deselectEvent = None + self.startPose = render.attachNewNode('startPose') + self.dragPointNP = self.attachNewNode('dragPoint') + self.gridSnapNP = self.dragPointNP.attachNewNode('gridSnap') + self.collisionOffsetNP = self.gridSnapNP.attachNewNode('collisionResponse') + self.iRay = SelectionRay() + self.iSegment = SelectionSegment(numSegments=6) + self.iSegment4 = SelectionSegment(numSegments=4) + self.iSphere = SelectionSphere() + self.houseExtents = None + self.doorBlocker = None + cp = CollisionPolygon(Point3(-100, -100, 0), Point3(100, -100, 0), Point3(100, 100, 0), Point3(-100, 100, 0)) + cn = CollisionNode('dragCollisionNode') + cn.addSolid(cp) + cn.setIntoCollideMask(ToontownGlobals.FurnitureDragBitmask) + self.collisionNP = NodePath(cn) + self.lnp = LineNodePath() + self.fRecenter = 0 + self.gridSpacing = None + self.firstTime = 0 + guiModels = loader.loadModel('phase_5.5/models/gui/house_design_gui') + self.createSelectedObjectPanel(guiModels) + self.createMainControls(guiModels) + self.furnitureManager = None + self.atticPicker = None + self.inRoomPicker = None + self.inTrashPicker = None + self.dialog = None + self.deleteMode = 0 + self.nonDeletableItem = None + self.verifyFrame = None + self.deleteItemText = None + self.okButton = None + self.cancelButton = None + self.itemIval = None + self.itemPanel = None + self.guiInterval = None + self.accept('enterFurnitureMode', self.enterFurnitureMode) + self.accept('exitFurnitureMode', self.exitFurnitureMode) + return + + def enterFurnitureMode(self, furnitureManager, fDirector): + if not fDirector: + if self.furnitureManager: + self.exitFurnitureMode(self.furnitureManager) + return + if furnitureManager == self.furnitureManager: + return + if self.furnitureManager != None: + self.exitFurnitureMode(self.furnitureManager) + self.notify.info('enterFurnitureMode, fDirector = %s' % fDirector) + self.furnitureManager = furnitureManager + self.furnitureManager.d_avatarEnter() + house = furnitureManager.getInteriorObject() + house.hideExteriorWindows() + self.setTargetNodePath(house.interior) + self.createAtticPicker() + self.initializeDistributedFurnitureItems(furnitureManager.dfitems) + self.setCamPosIndex(DEFAULT_CAM_INDEX) + base.localAvatar.setGhostMode(1) + taskMgr.remove('editModeTransition') + self.orientCamH(base.localAvatar.getH(self.targetNodePath)) + self.accept('mouse1', self.moveObjectStart) + self.accept('mouse1-up', self.moveObjectStop) + self.furnitureGui.show() + self.deleteMode = 0 + self.__updateDeleteButtons() + self.showAtticPicker() + base.localAvatar.laffMeter.stop() + base.setCellsAvailable(base.leftCells + [base.bottomCells[0]], 0) + if self.guiInterval: + self.guiInterval.finish() + self.guiInterval = self.furnitureGui.posHprScaleInterval(1.0, Point3(0.155, -0.6, -1.045), Vec3(0), Vec3(0.06), startPos=Point3(0.115, 0.0, -0.66), startHpr=Vec3(0), startScale=Vec3(0.04), blendType='easeInOut', name='lerpFurnitureButton') + self.guiInterval.start() + taskMgr.add(self.recenterButtonFrameTask, 'recenterButtonFrameTask', 10) + messenger.send('wakeup') + return + + def exitFurnitureMode(self, furnitureManager): + if furnitureManager != self.furnitureManager: + return + self.notify.info('exitFurnitureMode') + house = furnitureManager.getInteriorObject() + if house: + house.showExteriorWindows() + self.furnitureManager.d_avatarExit() + self.furnitureManager = None + base.localAvatar.setCameraPositionByIndex(0) + self.exitDeleteMode() + self.houseExtents.detachNode() + self.doorBlocker.detachNode() + self.deselectObject() + self.ignore('mouse1') + self.ignore('mouse1-up') + if self.atticPicker: + self.atticPicker.destroy() + self.atticPicker = None + if self.inRoomPicker: + self.inRoomPicker.destroy() + self.inRoomPicker = None + if self.inTrashPicker: + self.inTrashPicker.destroy() + self.inTrashPicker = None + self.__cleanupVerifyDelete() + self.furnitureGui.hide() + base.setCellsAvailable(base.leftCells + [base.bottomCells[0]], 1) + base.localAvatar.laffMeter.start() + taskMgr.remove('recenterButtonFrameTask') + self.cleanupDialog() + taskMgr.remove('showHelpTextDoLater') + messenger.send('wakeup') + return + + def initializeDistributedFurnitureItems(self, dfitems): + self.objectDict = {} + for item in dfitems: + mo = MovableObject(item, parent=self.targetNodePath) + self.objectDict[mo.get_key()] = mo + + def setCamPosIndex(self, index): + self.camPosIndex = index + base.localAvatar.setCameraSettings(camPosList[index]) + + def zoomCamIn(self): + self.setCamPosIndex(max(0, self.camPosIndex - 1)) + messenger.send('wakeup') + + def zoomCamOut(self): + self.setCamPosIndex(min(len(camPosList) - 1, self.camPosIndex + 1)) + messenger.send('wakeup') + + def rotateCamCW(self): + self.orientCamH(base.localAvatar.getH(self.targetNodePath) - 90) + messenger.send('wakeup') + + def rotateCamCCW(self): + self.orientCamH(base.localAvatar.getH(self.targetNodePath) + 90) + messenger.send('wakeup') + + def orientCamH(self, toonH): + targetH = ROUND_TO(toonH, 90) + base.localAvatar.hprInterval(duration=1, hpr=Vec3(targetH, 0, 0), other=self.targetNodePath, blendType='easeInOut', name='editModeTransition').start() + + def setTargetNodePath(self, nodePath): + self.targetNodePath = nodePath + if self.houseExtents: + self.houseExtents.removeNode() + if self.doorBlocker: + self.doorBlocker.removeNode() + self.makeHouseExtentsBox() + self.makeDoorBlocker() + self.collisionNP.reparentTo(self.targetNodePath) + + def loadObject(self, filename): + mo = MovableObject(filename, parent=self.targetNodePath) + self.objectDict[mo.get_key()] = mo + self.selectObject(mo) + return mo + + def pickObject(self): + self.iRay.setParentNP(base.cam) + entry = self.iRay.pickGeom(targetNodePath=self.targetNodePath, skipFlags=SKIP_ALL) + if entry: + nodePath = entry.getIntoNodePath() + if self.isMovableObject(nodePath): + self.selectObject(self.findObject(nodePath)) + return + self.deselectObject() + + def pickInRoom(self, objectId): + self.selectObject(self.objectDict.get(objectId)) + + def selectObject(self, selectedObject): + messenger.send('wakeup') + if self.selectedObject: + self.deselectObject() + if selectedObject: + self.selectedObject = selectedObject + self.deselectEvent = self.selectedObject.dfitem.uniqueName('disable') + self.acceptOnce(self.deselectEvent, self.deselectObject) + self.lnp.reset() + self.lnp.reparentTo(selectedObject) + self.lnp.moveTo(selectedObject.c0) + self.lnp.drawTo(selectedObject.c1) + self.lnp.drawTo(selectedObject.c2) + self.lnp.drawTo(selectedObject.c3) + self.lnp.drawTo(selectedObject.c0) + self.lnp.create() + self.buttonFrame.show() + self.enableButtonFrameTask() + self.sendToAtticButton.show() + self.atticRoof.hide() + + def deselectObject(self): + self.moveObjectStop() + if self.deselectEvent: + self.ignore(self.deselectEvent) + self.deselectEvent = None + self.selectedObject = None + self.lnp.detachNode() + self.buttonFrame.hide() + self.disableButtonFrameTask() + self.sendToAtticButton.hide() + self.atticRoof.show() + return + + def isMovableObject(self, nodePath): + return nodePath.hasNetTag('movableObject') + + def findObject(self, nodePath): + np = nodePath.findNetTag('movableObject') + if np.isEmpty(): + return None + else: + return self.objectDict.get(np.get_key(), None) + return None + + def moveObjectStop(self, *args): + if self.movingObject: + self.movingObject = 0 + taskMgr.remove('moveObjectTask') + if self.selectedObject: + self.selectedObject.wrtReparentTo(self.targetNodePath) + self.selectedObject.collisionNodePath.unstash() + self.selectedObject.dfitem.stopAdjustPosHpr() + for object in self.objectDict.values(): + object.unstashBuiltInCollisionNodes() + + self.centerMarker['image'] = [self.grabUp, self.grabDown, self.grabRollover] + self.centerMarker.configure(text=['', TTLocalizer.HDMoveLabel], text_pos=(0, 1), text_scale=0.7, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image_scale=0.3) + + def moveObjectStart(self): + self.moveObjectStop() + self.pickObject() + self.moveObjectContinue() + + def moveObjectContinue(self, *args): + messenger.send('wakeup') + if self.selectedObject: + for object in self.objectDict.values(): + object.stashBuiltInCollisionNodes() + + self.selectedObject.collisionNodePath.stash() + self.selectedObject.dfitem.startAdjustPosHpr() + self.firstTime = 1 + self.iPosHpr() + self.startPoseValid = 0 + self.centerMarker['image'] = self.grabDown + self.centerMarker.configure(text=TTLocalizer.HDMoveLabel, text_pos=(0, 1), text_scale=0.7, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image_scale=0.3) + taskMgr.add(self.moveObjectTask, 'moveObjectTask') + self.movingObject = 1 + + def setLnpColor(self, r, g, b): + for i in xrange(5): + self.lnp.lineSegs.setVertexColor(i, r, g, b) + + def markNewPosition(self, isValid): + if not isValid: + if self.startPoseValid: + self.collisionOffsetNP.setPosHpr(self.startPose, self.selectedObject.dragPoint, Vec3(0)) + else: + self.startPoseValid = 1 + + def moveObjectTask(self, state): + so = self.selectedObject + target = self.targetNodePath + self.startPose.iPosHpr(so) + self.iRay.setParentNP(base.cam) + entry = self.iRay.pickBitMask(bitMask=ToontownGlobals.FurnitureDragBitmask, targetNodePath=target, skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) + if not entry: + return Task.cont + self.setPos(base.cam, entry.getSurfacePoint(base.cam)) + if self.firstTime: + self.moveObjectInit() + self.firstTime = 0 + else: + self.gridSnapNP.iPos() + self.collisionOffsetNP.iPosHpr() + if self.gridSpacing: + pos = self.dragPointNP.getPos(target) + self.gridSnapNP.setPos(target, ROUND_TO(pos[0], self.gridSpacing), ROUND_TO(pos[1], self.gridSpacing), pos[2]) + self.iRay.setParentNP(base.cam) + entry = self.iRay.pickBitMask3D(bitMask=so.getWallBitmask(), targetNodePath=target, dir=Vec3(self.getNearProjectionPoint(self.gridSnapNP)), skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) + fWall = 0 + if not so.getOnTable(): + while entry: + intoMask = entry.getIntoNodePath().node().getIntoCollideMask() + fClosest = (intoMask & ToontownGlobals.WallBitmask).isZero() + if self.alignObject(entry, target, fClosest=fClosest): + fWall = 1 + break + entry = self.iRay.findNextCollisionEntry(skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) + + if so.getOnWall(): + self.markNewPosition(fWall) + return Task.cont + self.iRay.setParentNP(target) + entry = self.iRay.pickBitMask3D(bitMask=so.getFloorBitmask(), targetNodePath=target, origin=Point3(self.gridSnapNP.getPos(target) + Vec3(0, 0, 10)), dir=Vec3(0, 0, -1), skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) + if not entry: + self.markNewPosition(0) + return Task.cont + nodePath = entry.getIntoNodePath() + if self.isMovableObject(nodePath): + self.gridSnapNP.setPos(target, Point3(entry.getSurfacePoint(target))) + else: + self.gridSnapNP.setPos(target, Point3(entry.getSurfacePoint(target) + Vec3(0, 0, ToontownGlobals.FloorOffset))) + if not fWall: + self.iSphere.setParentNP(self.gridSnapNP) + self.iSphere.setCenterRadius(0, Point3(0), so.radius * 1.25) + entry = self.iSphere.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=target, skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + if entry: + self.alignObject(entry, target, fClosest=1) + isValid = self.collisionTest() + self.markNewPosition(isValid) + return Task.cont + + def collisionTest(self): + so = self.selectedObject + target = self.targetNodePath + entry = self.segmentCollision() + if not entry: + return 1 + offsetDict = {} + while entry: + offset = self.computeSegmentOffset(entry) + if offset: + eid = entry.getInto() + maxOffsetVec = offsetDict.get(eid, Vec3(0)) + if offset.length() > maxOffsetVec.length(): + maxOffsetVec.assign(offset) + offsetDict[eid] = maxOffsetVec + entry = self.iSegment.findNextCollisionEntry(skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + + if offsetDict: + keys = offsetDict.keys() + ortho1 = offsetDict[keys[0]] + ortho2 = Vec3(0) + v1 = Vec3(ortho1) + v1.normalize() + for key in keys[1:]: + offset = offsetDict[key] + v2 = Vec3(offset) + v2.normalize() + dp = v1.dot(v2) + if abs(dp) > 0.95: + if offset.length() > ortho1.length(): + ortho1.assign(offset) + elif abs(dp) < 0.05: + if offset.length() > ortho2.length(): + ortho2.assign(offset) + else: + o1Len = ortho1.length() + parallelVec = Vec3(ortho1 * offset.dot(ortho1) / (o1Len * o1Len)) + perpVec = Vec3(offset - parallelVec) + if parallelVec.length() > o1Len: + ortho1.assign(parallelVec) + if perpVec.length() > ortho2.length(): + ortho2.assign(perpVec) + + totalOffset = ortho1 + ortho2 + self.collisionOffsetNP.setPos(self.collisionOffsetNP, totalOffset) + if not self.segmentCollision(): + return 1 + m = self.startPose.getMat(so) + deltaMove = Vec3(m.getRow3(3)) + if deltaMove.length() == 0: + return 1 + self.iSegment4.setParentNP(so) + entry = self.iSegment4.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=target, endPointList=[(so.c0, Point3(m.xformPoint(so.c0))), + (so.c1, Point3(m.xformPoint(so.c1))), + (so.c2, Point3(m.xformPoint(so.c2))), + (so.c3, Point3(m.xformPoint(so.c3)))], skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + maxLen = 0 + maxOffset = None + while entry: + offset = Vec3(entry.getSurfacePoint(entry.getFromNodePath()) - entry.getFrom().getPointA()) + offsetLen = Vec3(offset).length() + if offsetLen > maxLen: + maxLen = offsetLen + maxOffset = offset + entry = self.iSegment4.findNextCollisionEntry(skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + + if maxOffset: + self.collisionOffsetNP.setPos(self.collisionOffsetNP, maxOffset) + if not self.segmentCollision(): + return 1 + return 0 + + def segmentCollision(self): + so = self.selectedObject + self.iSegment.setParentNP(so) + entry = self.iSegment.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=self.targetNodePath, endPointList=[(so.c0, so.c1), + (so.c1, so.c2), + (so.c2, so.c3), + (so.c3, so.c0), + (so.c0, so.c2), + (so.c1, so.c3)], skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + return entry + + def computeSegmentOffset(self, entry): + fromNodePath = entry.getFromNodePath() + if entry.hasSurfaceNormal(): + normal = entry.getSurfaceNormal(fromNodePath) + else: + return None + hitPoint = entry.getSurfacePoint(fromNodePath) + m = self.selectedObject.getMat(self.startPose) + hp = Point3(m.xformPoint(hitPoint)) + hpn = Vec3(m.xformVec(normal)) + hitPointVec = Vec3(hp - self.selectedObject.dragPoint) + if hitPointVec.dot(hpn) > 0: + return None + nLen = normal.length() + offsetVecA = hitPoint - entry.getFrom().getPointA() + offsetA = normal * offsetVecA.dot(normal) / (nLen * nLen) + if offsetA.dot(normal) > 0: + return offsetA * 1.01 + else: + offsetVecB = hitPoint - entry.getFrom().getPointB() + offsetB = normal * offsetVecB.dot(normal) / (nLen * nLen) + return offsetB * 1.01 + return None + + def alignObject(self, entry, target, fClosest = 0, wallOffset = None): + if not entry.hasSurfaceNormal(): + return 0 + normal = entry.getSurfaceNormal(target) + if abs(normal.dot(Vec3(0, 0, 1))) < 0.1: + tempNP = target.attachNewNode('temp') + normal.setZ(0) + normal.normalize() + lookAtNormal = Point3(normal) + lookAtNormal *= -1 + tempNP.lookAt(lookAtNormal) + realAngle = ROUND_TO(self.gridSnapNP.getH(tempNP), 90.0) + if fClosest: + angle = realAngle + else: + angle = 0 + self.gridSnapNP.setHpr(tempNP, angle, 0, 0) + hitPoint = entry.getSurfacePoint(target) + tempNP.setPos(hitPoint) + if wallOffset == None: + wallOffset = self.selectedObject.getWallOffset() + self.gridSnapNP.setPos(tempNP, 0, -wallOffset, 0) + tempNP.removeNode() + if realAngle == 180.0: + self.gridSnapNP.setH(self.gridSnapNP.getH() + 180.0) + return 1 + return 0 + + def rotateLeft(self): + if not self.selectedObject: + return + so = self.selectedObject + so.dfitem.startAdjustPosHpr() + self.iPosHpr(so) + self.moveObjectInit() + if so.getOnWall(): + startR = self.gridSnapNP.getR() + newR = ROUND_TO(startR + 22.5, 22.5) + self.gridSnapNP.setR(newR) + else: + startH = self.gridSnapNP.getH(self.targetNodePath) + newH = ROUND_TO(startH - 22.5, 22.5) + self.iSphere.setParentNP(self.gridSnapNP) + self.iSphere.setCenterRadius(0, Point3(0), so.radius * 1.25) + entry = self.iSphere.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=self.targetNodePath, skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + if not entry: + self.gridSnapNP.setHpr(self.targetNodePath, newH, 0, 0) + self.collisionTest() + so.wrtReparentTo(self.targetNodePath) + self.disableButtonFrameTask() + so.dfitem.stopAdjustPosHpr() + + def rotateRight(self): + if not self.selectedObject: + return + so = self.selectedObject + so.dfitem.startAdjustPosHpr() + self.iPosHpr(so) + self.moveObjectInit() + if so.getOnWall(): + startR = self.gridSnapNP.getR() + newR = ROUND_TO(startR - 22.5, 22.5) + self.gridSnapNP.setR(newR) + else: + startH = self.gridSnapNP.getH(self.targetNodePath) + newH = ROUND_TO(startH + 22.5, 22.5) % 360.0 + self.iSphere.setParentNP(self.gridSnapNP) + self.iSphere.setCenterRadius(0, Point3(0), so.radius * 1.25) + entry = self.iSphere.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=self.targetNodePath, skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + if not entry: + self.gridSnapNP.setHpr(self.targetNodePath, newH, 0, 0) + self.collisionTest() + so.wrtReparentTo(self.targetNodePath) + self.disableButtonFrameTask() + so.dfitem.stopAdjustPosHpr() + + def moveObjectInit(self): + self.dragPointNP.setPosHpr(self.selectedObject, self.selectedObject.dragPoint, Vec3(0)) + self.gridSnapNP.iPosHpr() + self.collisionOffsetNP.iPosHpr() + self.selectedObject.wrtReparentTo(self.collisionOffsetNP) + + def resetFurniture(self): + for o in self.objectDict.values(): + o.resetMovableObject() + + self.objectDict = {} + self.deselectObject() + self.buttonFrame.hide() + + def destroy(self): + self.ignore('enterFurnitureMode') + self.ignore('exitFurnitureMode') + if self.guiInterval: + self.guiInterval.finish() + if self.furnitureManager: + self.exitFurnitureMode(self.furnitureManager) + self.cleanupDialog() + self.resetFurniture() + self.buttonFrame.destroy() + self.furnitureGui.destroy() + if self.houseExtents: + self.houseExtents.removeNode() + if self.doorBlocker: + self.doorBlocker.removeNode() + self.removeNode() + if self.verifyFrame: + self.verifyFrame.destroy() + self.verifyFrame = None + self.deleteItemText = None + self.okButton = None + self.cancelButton = None + return + + def createSelectedObjectPanel(self, guiModels): + self.buttonFrame = DirectFrame(scale=0.5) + self.grabUp = guiModels.find('**/handup') + self.grabDown = guiModels.find('**/handdown') + self.grabRollover = guiModels.find('**/handrollover') + self.centerMarker = DirectButton(parent=self.buttonFrame, text=['', TTLocalizer.HDMoveLabel], text_pos=(0, 1), text_scale=0.7, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image=[self.grabUp, self.grabDown, self.grabRollover], image_scale=0.3, relief=None, scale=0.12) + self.centerMarker.bind(DGG.B1PRESS, self.moveObjectContinue) + self.centerMarker.bind(DGG.B1RELEASE, self.moveObjectStop) + guiCCWArrowUp = guiModels.find('**/LarrowUp') + guiCCWArrowDown = guiModels.find('**/LarrowDown') + guiCCWArrowRollover = guiModels.find('**/LarrowRollover') + self.rotateLeftButton = DirectButton(parent=self.buttonFrame, relief=None, image=(guiCCWArrowUp, + guiCCWArrowDown, + guiCCWArrowRollover, + guiCCWArrowUp), image_pos=(0, 0, 0.1), image_scale=0.15, image3_color=Vec4(0.5, 0.5, 0.5, 0.75), text=('', + TTLocalizer.HDRotateCCWLabel, + TTLocalizer.HDRotateCCWLabel, + ''), text_pos=(0.135, -0.1), text_scale=0.1, text_align=TextNode.ARight, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), pos=(-.125, 0, -.2), scale=0.7, command=self.rotateLeft) + self.rotateLeftButton.bind(DGG.EXIT, self.enableButtonFrameTask) + guiCWArrowUp = guiModels.find('**/RarrowUp') + guiCWArrowDown = guiModels.find('**/RarrowDown') + guiCWArrowRollover = guiModels.find('**/RarrowRollover') + self.rotateRightButton = DirectButton(parent=self.buttonFrame, relief=None, image=(guiCWArrowUp, + guiCWArrowDown, + guiCWArrowRollover, + guiCWArrowUp), image_pos=(0, 0, 0.1), image_scale=0.15, image3_color=Vec4(0.5, 0.5, 0.5, 0.75), text=('', + TTLocalizer.HDRotateCWLabel, + TTLocalizer.HDRotateCWLabel, + ''), text_pos=(-0.135, -0.1), text_scale=0.1, text_align=TextNode.ALeft, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), pos=(0.125, 0, -0.2), scale=0.7, command=self.rotateRight) + self.rotateRightButton.bind(DGG.EXIT, self.enableButtonFrameTask) + self.buttonFrame.hide() + return + + def recenterButtonFrameTask(self, state): + if self.selectedObject and self.fRecenter: + self.buttonFrame.setPos(self.getSelectedObjectScreenXY()) + return Task.cont + + def disableButtonFrameTask(self, event = None): + self.fRecenter = 0 + + def enableButtonFrameTask(self, event = None): + self.fRecenter = 1 + + def getNearProjectionPoint(self, nodePath): + origin = nodePath.getPos(camera) + if origin[1] != 0.0: + return origin * (base.camLens.getNear() / origin[1]) + else: + return Point3(0, base.camLens.getNear(), 0) + + def getSelectedObjectScreenXY(self): + tNodePath = self.selectedObject.attachNewNode('temp') + tNodePath.setPos(self.selectedObject.center) + nearVec = self.getNearProjectionPoint(tNodePath) + nearVec *= base.camLens.getFocalLength() / base.camLens.getNear() + render2dX = CLAMP(nearVec[0] / (base.camLens.getFilmSize()[0] / 2.0), -.9, 0.9) + aspect2dX = render2dX * base.getAspectRatio() + aspect2dZ = CLAMP(nearVec[2] / (base.camLens.getFilmSize()[1] / 2.0), -.8, 0.9) + tNodePath.removeNode() + return Vec3(aspect2dX, 0, aspect2dZ) + + def createMainControls(self, guiModels): + attic = guiModels.find('**/attic') + self.furnitureGui = DirectFrame(relief=None, parent=base.a2dTopLeft, pos=(0.155, -0.6, -1.045), scale=0.04, image=attic) + bMoveStopUp = guiModels.find('**/bu_atticX/bu_attic_up') + bMoveStopDown = guiModels.find('**/bu_atticX/bu_attic_down') + bMoveStopRollover = guiModels.find('**/bu_atticX/bu_attic_rollover') + self.bStopMoveFurniture = DirectButton(parent=self.furnitureGui, relief=None, image=[bMoveStopUp, + bMoveStopDown, + bMoveStopRollover, + bMoveStopUp], text=['', TTLocalizer.HDStopMoveFurnitureButton, TTLocalizer.HDStopMoveFurnitureButton], text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), pos=(-0.3, 0, 9.4), command=base.localAvatar.stopMoveFurniture) + self.bindHelpText(self.bStopMoveFurniture, 'DoneMoving') + self.atticRoof = DirectLabel(parent=self.furnitureGui, relief=None, image=guiModels.find('**/rooftile')) + self.itemBackgroundFrame = DirectFrame(parent=self.furnitureGui, relief=None, image=guiModels.find('**/item_backgroun'), image_pos=(0, 0, -22), image_scale=(1, 1, 5)) + self.scrollUpFrame = DirectFrame(parent=self.furnitureGui, relief=None, image=guiModels.find('**/scrollup'), pos=(0, 0, -0.58)) + self.camButtonFrame = DirectFrame(parent=self.furnitureGui, relief=None, image=guiModels.find('**/low'), pos=(0, 0, -11.69)) + tagUp = guiModels.find('**/tag_up') + tagDown = guiModels.find('**/tag_down') + tagRollover = guiModels.find('**/tag_rollover') + self.inAtticButton = DirectButton(parent=self.itemBackgroundFrame, relief=None, text=TTLocalizer.HDInAtticLabel, text_pos=(-0.1, -0.25), image=[tagUp, tagDown, tagRollover], pos=(2.85, 0, 4), scale=0.8, command=self.showAtticPicker) + self.bindHelpText(self.inAtticButton, 'Attic') + self.inRoomButton = DirectButton(parent=self.itemBackgroundFrame, relief=None, text=TTLocalizer.HDInRoomLabel, text_pos=(-0.1, -0.25), image=[tagUp, tagDown, tagRollover], pos=(2.85, 0, 1.1), scale=0.8, command=self.showInRoomPicker) + self.bindHelpText(self.inRoomButton, 'Room') + self.inTrashButton = DirectButton(parent=self.itemBackgroundFrame, relief=None, text=TTLocalizer.HDInTrashLabel, text_pos=(-0.1, -0.25), image=[tagUp, tagDown, tagRollover], pos=(2.85, 0, -1.8), scale=0.8, command=self.showInTrashPicker) + self.bindHelpText(self.inTrashButton, 'Trash') + for i in xrange(4): + self.inAtticButton.component('text%d' % i).setR(-90) + self.inRoomButton.component('text%d' % i).setR(-90) + self.inTrashButton.component('text%d' % i).setR(-90) + + backInAtticUp = guiModels.find('**/bu_backinattic_up1') + backInAtticDown = guiModels.find('**/bu_backinattic_down1') + backInAtticRollover = guiModels.find('**/bu_backinattic_rollover2') + self.sendToAtticButton = DirectButton(parent=self.furnitureGui, relief=None, pos=(0.4, 0, 12.8), text=['', TTLocalizer.HDToAtticLabel], text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_pos=(1.2, -0.3), image=[backInAtticUp, backInAtticDown, backInAtticRollover], command=self.sendItemToAttic) + self.sendToAtticButton.hide() + self.bindHelpText(self.sendToAtticButton, 'SendToAttic') + zoomInUp = guiModels.find('**/bu_RzoomOut_up') + zoomInDown = guiModels.find('**/bu_RzoomOut_down') + zoomInRollover = guiModels.find('**/bu_RzoomOut_rollover') + self.zoomInButton = DirectButton(parent=self.camButtonFrame, image=[zoomInUp, zoomInDown, zoomInRollover], relief=None, pos=(0.9, 0, -0.75), command=self.zoomCamIn) + self.bindHelpText(self.zoomInButton, 'ZoomIn') + zoomOutUp = guiModels.find('**/bu_LzoomIn_up') + zoomOutDown = guiModels.find('**/bu_LzoomIn_down') + zoomOutRollover = guiModels.find('**/buLzoomIn_rollover') + self.zoomOutButton = DirectButton(parent=self.camButtonFrame, image=[zoomOutUp, zoomOutDown, zoomOutRollover], relief=None, pos=(-1.4, 0, -0.75), command=self.zoomCamOut) + self.bindHelpText(self.zoomOutButton, 'ZoomOut') + camCCWUp = guiModels.find('**/bu_Rarrow_up1') + camCCWDown = guiModels.find('**/bu_Rarrow_down1') + camCCWRollover = guiModels.find('**/bu_Rarrow_orllover') + self.rotateCamLeftButton = DirectButton(parent=self.camButtonFrame, image=[camCCWUp, camCCWDown, camCCWRollover], relief=None, pos=(0.9, 0, -3.0), command=self.rotateCamCCW) + self.bindHelpText(self.rotateCamLeftButton, 'RotateLeft') + camCWUp = guiModels.find('**/bu_Larrow_up1') + camCWDown = guiModels.find('**/bu_Larrow_down1') + camCWRollover = guiModels.find('**/bu_Larrow_rollover2') + self.rotateCamRightButton = DirectButton(parent=self.camButtonFrame, image=[camCWUp, camCWDown, camCWRollover], relief=None, pos=(-1.4, 0, -3.0), command=self.rotateCamCW) + self.bindHelpText(self.rotateCamRightButton, 'RotateRight') + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') + trashcanUp = trashcanGui.find('**/TrashCan_CLSD') + trashcanDown = trashcanGui.find('**/TrashCan_OPEN') + trashcanRollover = trashcanGui.find('**/TrashCan_RLVR') + self.deleteEnterButton = DirectButton(parent=self.furnitureGui, image=(trashcanUp, + trashcanDown, + trashcanRollover, + trashcanUp), text=['', + TTLocalizer.InventoryDelete, + TTLocalizer.InventoryDelete, + ''], text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_align=TextNode.ACenter, text_pos=(0, -0.12), text_font=ToontownGlobals.getInterfaceFont(), textMayChange=0, relief=None, pos=(3.7, 0.0, -13.8), scale=7.13, command=self.enterDeleteMode) + self.bindHelpText(self.deleteEnterButton, 'DeleteEnter') + self.deleteExitButton = DirectButton(parent=self.furnitureGui, image=(trashcanUp, + trashcanDown, + trashcanRollover, + trashcanUp), text=('', + TTLocalizer.InventoryDone, + TTLocalizer.InventoryDone, + ''), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_align=TextNode.ACenter, text_pos=(0, -0.12), text_font=ToontownGlobals.getInterfaceFont(), textMayChange=0, relief=None, pos=(3.7, 0.0, -13.8), scale=7.13, command=self.exitDeleteMode) + self.bindHelpText(self.deleteExitButton, 'DeleteExit') + self.deleteExitButton.hide() + self.trashcanBase = DirectLabel(parent=self.furnitureGui, image=guiModels.find('**/trashcan_base'), relief=None, pos=(0, 0, -11.64)) + self.furnitureGui.hide() + self.helpText = DirectLabel(parent=self.furnitureGui, relief=DGG.SUNKEN, frameSize=(-0.5, + 10, + -3, + 0.9), frameColor=(0.2, 0.2, 0.2, 0.5), borderWidth=(0.01, 0.01), text='', text_wordwrap=12, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.8, pos=(3, 0.0, -7), scale=1, text_align=TextNode.ALeft) + self.helpText.hide() + return + + def createAtticPicker(self): + self.atticItemPanels = [] + for itemIndex in xrange(len(self.furnitureManager.atticItems)): + panel = FurnitureItemPanel(self.furnitureManager.atticItems[itemIndex], itemIndex, command=self.bringItemFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + self.atticItemPanels.append(panel) + + self.atticWallpaperPanels = [] + for itemIndex in xrange(len(self.furnitureManager.atticWallpaper)): + panel = FurnitureItemPanel(self.furnitureManager.atticWallpaper[itemIndex], itemIndex, command=self.bringWallpaperFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + self.atticWallpaperPanels.append(panel) + + self.atticWindowPanels = [] + for itemIndex in xrange(len(self.furnitureManager.atticWindows)): + panel = FurnitureItemPanel(self.furnitureManager.atticWindows[itemIndex], itemIndex, command=self.bringWindowFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + self.atticWindowPanels.append(panel) + + self.regenerateAtticPicker() + + def regenerateAtticPicker(self): + selectedIndex = 0 + if self.atticPicker: + selectedIndex = self.atticPicker.getSelectedIndex() + for panel in self.atticItemPanels: + panel.detachNode() + + for panel in self.atticWallpaperPanels: + panel.detachNode() + + for panel in self.atticWindowPanels: + panel.detachNode() + + self.atticPicker.destroy() + self.atticPicker = None + itemList = self.atticItemPanels + self.atticWallpaperPanels + self.atticWindowPanels + if self.deleteMode: + text = TTLocalizer.HDDeletePickerLabel + else: + text = TTLocalizer.HDAtticPickerLabel + self.atticPicker = self.createScrolledList(itemList, text, 'atticPicker', selectedIndex) + if self.inRoomPicker or self.inTrashPicker: + self.atticPicker.hide() + else: + self.atticPicker.show() + return + + def createInRoomPicker(self): + self.inRoomPanels = [] + for objectId, object in self.objectDict.items(): + panel = FurnitureItemPanel(object.dfitem.item, objectId, command=self.requestReturnToAttic, deleteMode=self.deleteMode, withinFunc=self.pickInRoom, helpCategory='FurnitureItemPanelRoom') + self.inRoomPanels.append(panel) + + self.regenerateInRoomPicker() + + def regenerateInRoomPicker(self): + selectedIndex = 0 + if self.inRoomPicker: + selectedIndex = self.inRoomPicker.getSelectedIndex() + for panel in self.inRoomPanels: + panel.detachNode() + + self.inRoomPicker.destroy() + self.inRoomPicker = None + if self.deleteMode: + text = TTLocalizer.HDDeletePickerLabel + else: + text = TTLocalizer.HDInRoomPickerLabel + self.inRoomPicker = self.createScrolledList(self.inRoomPanels, text, 'inRoomPicker', selectedIndex) + return + + def createInTrashPicker(self): + self.inTrashPanels = [] + for itemIndex in xrange(len(self.furnitureManager.deletedItems)): + panel = FurnitureItemPanel(self.furnitureManager.deletedItems[itemIndex], itemIndex, command=self.requestReturnToAtticFromTrash, helpCategory='FurnitureItemPanelTrash') + self.inTrashPanels.append(panel) + + self.regenerateInTrashPicker() + + def regenerateInTrashPicker(self): + selectedIndex = 0 + if self.inTrashPicker: + selectedIndex = self.inTrashPicker.getSelectedIndex() + for panel in self.inTrashPanels: + panel.detachNode() + + self.inTrashPicker.destroy() + self.inTrashPicker = None + text = TTLocalizer.HDInTrashPickerLabel + self.inTrashPicker = self.createScrolledList(self.inTrashPanels, text, 'inTrashPicker', selectedIndex) + return + + def createScrolledList(self, itemList, text, name, selectedIndex): + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + picker = DirectScrolledList(parent=self.furnitureGui, pos=(-0.38, 0.0, 3), scale=7.125, relief=None, items=itemList, numItemsVisible=5, text=text, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_pos=(0, 0.4), decButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_scale=(1.5, 1.5, 1.5), decButton_pos=(0, 0, 0.3), decButton_image3_color=Vec4(1, 1, 1, 0.1), incButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_scale=(1.5, 1.5, -1.5), incButton_pos=(0, 0, -1.878), incButton_image3_color=Vec4(1, 1, 1, 0.1)) + picker.setName(name) + picker.scrollTo(selectedIndex) + return picker + + def reset(): + self.destroy() + furnitureMenu.destroy() + + def showAtticPicker(self): + if self.inRoomPicker: + self.inRoomPicker.destroy() + self.inRoomPicker = None + if self.inTrashPicker: + self.inTrashPicker.destroy() + self.inTrashPicker = None + self.atticPicker.show() + self.inAtticButton['image_color'] = Vec4(1, 1, 1, 1) + self.inRoomButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + self.inTrashButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + self.deleteExitButton['state'] = 'normal' + self.deleteEnterButton['state'] = 'normal' + return + + def showInRoomPicker(self): + messenger.send('wakeup') + if not self.inRoomPicker: + self.createInRoomPicker() + self.atticPicker.hide() + if self.inTrashPicker: + self.inTrashPicker.destroy() + self.inTrashPicker = None + self.inAtticButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + self.inRoomButton['image_color'] = Vec4(1, 1, 1, 1) + self.inTrashButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + self.deleteExitButton['state'] = 'normal' + self.deleteEnterButton['state'] = 'normal' + return + + def showInTrashPicker(self): + messenger.send('wakeup') + if not self.inTrashPicker: + self.createInTrashPicker() + self.atticPicker.hide() + if self.inRoomPicker: + self.inRoomPicker.destroy() + self.inRoomPicker = None + self.inAtticButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + self.inRoomButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + self.inTrashButton['image_color'] = Vec4(1, 1, 1, 1) + self.deleteExitButton['state'] = 'disabled' + self.deleteEnterButton['state'] = 'disabled' + return + + def sendItemToAttic(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ESTATE: Send Item to Attic') + messenger.send('wakeup') + if self.selectedObject: + callback = PythonUtil.Functor(self.__sendItemToAtticCallback, self.selectedObject.get_key()) + self.furnitureManager.moveItemToAttic(self.selectedObject.dfitem, callback) + self.deselectObject() + + def __sendItemToAtticCallback(self, objectId, retcode, item): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to send item %s to attic, reason %s.' % (item.getName(), retcode)) + return + del self.objectDict[objectId] + if self.selectedObject != None and self.selectedObject.get_key() == objectId: + self.selectedObject.detachNode() + self.deselectObject() + itemIndex = len(self.atticItemPanels) + panel = FurnitureItemPanel(item, itemIndex, command=self.bringItemFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + self.atticItemPanels.append(panel) + self.regenerateAtticPicker() + if self.inRoomPicker: + for i in xrange(len(self.inRoomPanels)): + if self.inRoomPanels[i].itemId == objectId: + del self.inRoomPanels[i] + self.regenerateInRoomPicker() + return + + return + + def cleanupDialog(self, buttonValue = None): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + self.__enableItemButtons(1) + return + + def enterDeleteMode(self): + self.deleteMode = 1 + self.__updateDeleteMode() + + def exitDeleteMode(self): + self.deleteMode = 0 + self.__updateDeleteMode() + + def __updateDeleteMode(self): + if not self.atticPicker: + return + self.notify.debug('__updateDeleteMode deleteMode=%s' % self.deleteMode) + if self.deleteMode: + framePanelColor = DeletePickerPanelColor + atticText = TTLocalizer.HDDeletePickerLabel + inRoomText = TTLocalizer.HDDeletePickerLabel + helpCategory = 'FurnitureItemPanelDelete' + else: + framePanelColor = NormalPickerPanelColor + atticText = TTLocalizer.HDAtticPickerLabel + inRoomText = TTLocalizer.HDInRoomPickerLabel + helpCategory = None + if self.inRoomPicker: + self.inRoomPicker['text'] = inRoomText + for panel in self.inRoomPicker['items']: + panel.setDeleteMode(self.deleteMode) + panel.bindHelpText(helpCategory) + + if self.atticPicker: + self.atticPicker['text'] = atticText + for panel in self.atticPicker['items']: + panel.setDeleteMode(self.deleteMode) + panel.bindHelpText(helpCategory) + + self.__updateDeleteButtons() + return + + def __updateDeleteButtons(self): + if self.deleteMode: + self.deleteExitButton.show() + self.deleteEnterButton.hide() + else: + self.deleteEnterButton.show() + self.deleteExitButton.hide() + + def deleteItemFromRoom(self, dfitem, objectId, itemIndex): + messenger.send('wakeup') + callback = PythonUtil.Functor(self.__deleteItemFromRoomCallback, objectId, itemIndex) + self.furnitureManager.deleteItemFromRoom(dfitem, callback) + + def __deleteItemFromRoomCallback(self, objectId, itemIndex, retcode, item): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to delete item %s from room, reason %s.' % (item.getName(), retcode)) + return + del self.objectDict[objectId] + if self.selectedObject != None and self.selectedObject.get_key() == objectId: + self.selectedObject.detachNode() + self.deselectObject() + if self.inRoomPicker and itemIndex is not None: + del self.inRoomPanels[itemIndex] + self.regenerateInRoomPicker() + return + + def bringItemFromAttic(self, item, itemIndex): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ESTATE: Place Item in Room') + messenger.send('wakeup') + self.__enableItemButtons(0) + if self.deleteMode: + self.requestDelete(item, itemIndex, self.deleteItemFromAttic) + return + pos = self.targetNodePath.getRelativePoint(base.localAvatar, Point3(0, 2, 0)) + hpr = Point3(0, 0, 0) + if abs(pos[0]) > 3000 or abs(pos[1]) > 3000 or abs(pos[2]) > 300: + self.notify.warning('bringItemFromAttic extreme pos targetNodePath=%s avatar=%s %s' % (repr(self.targetNodePath.getPos(render)), repr(base.localAvatar.getPos(render)), repr(pos))) + if item.getFlags() & CatalogFurnitureItem.FLPainting: + for object in self.objectDict.values(): + object.stashBuiltInCollisionNodes() + + self.gridSnapNP.iPosHpr() + target = self.targetNodePath + self.iRay.setParentNP(base.localAvatar) + entry = self.iRay.pickBitMask3D(bitMask=ToontownGlobals.WallBitmask, targetNodePath=target, origin=Point3(0, 0, 6), dir=Vec3(0, 1, 0), skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) + for object in self.objectDict.values(): + object.unstashBuiltInCollisionNodes() + + if entry: + self.alignObject(entry, target, fClosest=0, wallOffset=0.1) + pos = self.gridSnapNP.getPos(target) + hpr = self.gridSnapNP.getHpr(target) + else: + self.notify.warning('wall not found for painting') + self.furnitureManager.moveItemFromAttic(itemIndex, (pos[0], + pos[1], + pos[2], + hpr[0], + hpr[1], + hpr[2]), self.__bringItemFromAtticCallback) + + def __bringItemFromAtticCallback(self, retcode, dfitem, itemIndex): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to bring furniture item %s into room, reason %s.' % (itemIndex, retcode)) + return + mo = self.loadObject(dfitem) + objectId = mo.get_key() + self.atticItemPanels[itemIndex].destroy() + del self.atticItemPanels[itemIndex] + for i in xrange(itemIndex, len(self.atticItemPanels)): + self.atticItemPanels[i].itemId -= 1 + + self.regenerateAtticPicker() + if self.inRoomPicker: + panel = FurnitureItemPanel(dfitem.item, objectId, command=self.requestReturnToAttic, helpCategory='FurnitureItemPanelRoom') + self.inRoomPanels.append(panel) + self.regenerateInRoomPicker() + + def deleteItemFromAttic(self, item, itemIndex): + messenger.send('wakeup') + self.furnitureManager.deleteItemFromAttic(item, itemIndex, self.__deleteItemFromAtticCallback) + + def __deleteItemFromAtticCallback(self, retcode, item, itemIndex): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to delete furniture item %s, reason %s.' % (itemIndex, retcode)) + return + self.atticItemPanels[itemIndex].destroy() + del self.atticItemPanels[itemIndex] + for i in xrange(itemIndex, len(self.atticItemPanels)): + self.atticItemPanels[i].itemId -= 1 + + self.regenerateAtticPicker() + + def bringWallpaperFromAttic(self, item, itemIndex): + messenger.send('wakeup') + self.__enableItemButtons(0) + if self.deleteMode: + self.requestDelete(item, itemIndex, self.deleteWallpaperFromAttic) + return + room = self.getRoom() + self.furnitureManager.moveWallpaperFromAttic(itemIndex, room, self.__bringWallpaperFromAtticCallback) + + def getRoom(self): + x, y, z = base.localAvatar.getPos() + + if (x <= -13.5 and y <= -7.6 and y >= 0.0) or (z >= 4.5 and z <= 10): + return 0 + elif base.localAvatar.getZ() > 5.0: + return 1 + elif base.localAvatar.getY() < 2.3: + return 2 + return 3 + + def __bringWallpaperFromAtticCallback(self, retcode, itemIndex, room): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to bring wallpaper %s into room %s, reason %s.' % (itemIndex, room, retcode)) + return + self.atticWallpaperPanels[itemIndex].destroy() + item = self.furnitureManager.atticWallpaper[itemIndex] + panel = FurnitureItemPanel(item, itemIndex, command=self.bringWallpaperFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + self.atticWallpaperPanels[itemIndex] = panel + self.regenerateAtticPicker() + + def deleteWallpaperFromAttic(self, item, itemIndex): + messenger.send('wakeup') + self.furnitureManager.deleteWallpaperFromAttic(item, itemIndex, self.__deleteWallpaperFromAtticCallback) + + def __deleteWallpaperFromAtticCallback(self, retcode, item, itemIndex): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to delete wallpaper %s, reason %s.' % (itemIndex, retcode)) + return + self.atticWallpaperPanels[itemIndex].destroy() + del self.atticWallpaperPanels[itemIndex] + for i in xrange(itemIndex, len(self.atticWallpaperPanels)): + self.atticWallpaperPanels[i].itemId -= 1 + + self.regenerateAtticPicker() + + def bringWindowFromAttic(self, item, itemIndex): + messenger.send('wakeup') + self.__enableItemButtons(0) + if self.deleteMode: + self.requestDelete(item, itemIndex, self.deleteWindowFromAttic) + return + room = self.getRoom() + if room == 0: + room = 1 + slot = room * 2 + self.furnitureManager.moveWindowFromAttic(itemIndex, slot, self.__bringWindowFromAtticCallback) + + def __bringWindowFromAtticCallback(self, retcode, itemIndex, slot): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to bring window %s into slot %s, reason %s.' % (itemIndex, slot, retcode)) + return + if retcode == ToontownGlobals.FM_SwappedItem: + self.atticWindowPanels[itemIndex].destroy() + item = self.furnitureManager.atticWindows[itemIndex] + panel = FurnitureItemPanel(item, itemIndex, command=self.bringWindowFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + self.atticWindowPanels[itemIndex] = panel + else: + self.atticWindowPanels[itemIndex].destroy() + del self.atticWindowPanels[itemIndex] + for i in xrange(itemIndex, len(self.atticWindowPanels)): + self.atticWindowPanels[i].itemId -= 1 + + self.regenerateAtticPicker() + + def deleteWindowFromAttic(self, item, itemIndex): + messenger.send('wakeup') + self.furnitureManager.deleteWindowFromAttic(item, itemIndex, self.__deleteWindowFromAtticCallback) + + def __deleteWindowFromAtticCallback(self, retcode, item, itemIndex): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to delete window %s, reason %s.' % (itemIndex, retcode)) + return + self.atticWindowPanels[itemIndex].destroy() + del self.atticWindowPanels[itemIndex] + for i in xrange(itemIndex, len(self.atticWindowPanels)): + self.atticWindowPanels[i].itemId -= 1 + + self.regenerateAtticPicker() + + def setGridSpacingString(self, spacingStr): + spacing = eval(spacingStr) + self.setGridSpacing(spacing) + + def setGridSpacing(self, gridSpacing): + self.gridSpacing = gridSpacing + + def makeHouseExtentsBox(self): + houseGeom = self.targetNodePath.findAllMatches('**/group*') + targetBounds = houseGeom.getTightBounds() + self.houseExtents = self.targetNodePath.attachNewNode('furnitureCollisionNode') + mx = targetBounds[0][0] + Mx = targetBounds[1][0] + my = targetBounds[0][1] + My = targetBounds[1][1] + mz = targetBounds[0][2] + Mz = targetBounds[1][2] + cn = CollisionNode('extentsCollisionNode') + cn.setIntoCollideMask(ToontownGlobals.GhostBitmask) + self.houseExtents.attachNewNode(cn) + cp = CollisionPolygon(Point3(mx, my, mz), Point3(mx, My, mz), Point3(mx, My, Mz), Point3(mx, my, Mz)) + cn.addSolid(cp) + cp = CollisionPolygon(Point3(Mx, My, mz), Point3(Mx, my, mz), Point3(Mx, my, Mz), Point3(Mx, My, Mz)) + cn.addSolid(cp) + cp = CollisionPolygon(Point3(Mx, my, mz), Point3(mx, my, mz), Point3(mx, my, Mz), Point3(Mx, my, Mz)) + cn.addSolid(cp) + cp = CollisionPolygon(Point3(mx, My, mz), Point3(Mx, My, mz), Point3(Mx, My, Mz), Point3(mx, My, Mz)) + cn.addSolid(cp) + + def makeDoorBlocker(self): + self.doorBlocker = self.targetNodePath.attachNewNode('doorBlocker') + cn = CollisionNode('doorBlockerCollisionNode') + cn.setIntoCollideMask(ToontownGlobals.FurnitureSideBitmask) + self.doorBlocker.attachNewNode(cn) + cs = CollisionSphere(Point3(-12, -33, 0), 7.5) + cn.addSolid(cs) + + def createVerifyDialog(self, item, verifyText, okFunc, cancelFunc): + if self.verifyFrame == None: + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelButtonImage = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.verifyFrame = DirectFrame(pos=(-0.4, 0.1, 0.3), scale=0.75, relief=None, image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.2, 1, 1.3), text='', text_wordwrap=19, text_scale=0.06, text_pos=(0, 0.5), textMayChange=1, sortOrder=NO_FADE_SORT_INDEX) + self.okButton = DirectButton(parent=self.verifyFrame, image=okButtonImage, relief=None, text=OTPLocalizer.DialogOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(-0.22, 0.0, -0.5)) + self.cancelButton = DirectButton(parent=self.verifyFrame, image=cancelButtonImage, relief=None, text=OTPLocalizer.DialogCancel, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.22, 0.0, -0.5)) + self.deleteItemText = DirectLabel(parent=self.verifyFrame, relief=None, text='', text_wordwrap=16, pos=(0.0, 0.0, -0.4), scale=0.09) + self.verifyFrame['text'] = verifyText + self.deleteItemText['text'] = item.getName() + self.okButton['command'] = okFunc + self.cancelButton['command'] = cancelFunc + self.verifyFrame.show() + self.itemPanel, self.itemIval = item.getPicture(base.localAvatar) + if self.itemPanel: + self.itemPanel.reparentTo(self.verifyFrame, -1) + self.itemPanel.setPos(0, 0, 0.05) + self.itemPanel.setScale(0.35) + self.deleteItemText.setPos(0.0, 0.0, -0.4) + else: + self.deleteItemText.setPos(0, 0, 0.07) + if self.itemIval: + self.itemIval.loop() + return + + def __handleVerifyDeleteOK(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ESTATE: Send Item to Trash') + deleteFunction = self.verifyItems[0] + deleteFunctionArgs = self.verifyItems[1:] + self.__cleanupVerifyDelete() + deleteFunction(*deleteFunctionArgs) + + def __cleanupVerifyDelete(self, *args): + if self.nonDeletableItem: + self.nonDeletableItem.cleanup() + self.nonDeletableItem = None + if self.verifyFrame: + self.verifyFrame.hide() + if self.itemIval: + self.itemIval.finish() + self.itemIval = None + if self.itemPanel: + self.itemPanel.destroy() + self.itemPanel = None + self.verifyItems = None + return + + def __enableItemButtons(self, enabled): + self.notify.debug('__enableItemButtons %d' % enabled) + if enabled: + buttonState = DGG.NORMAL + else: + buttonState = DGG.DISABLED + if hasattr(self, 'inAtticButton'): + self.inAtticButton['state'] = buttonState + if hasattr(self, 'inRoomButton'): + self.inRoomButton['state'] = buttonState + if hasattr(self, 'inTrashButton'): + self.inTrashButton['state'] = buttonState + pickers = [self.atticPicker, self.inRoomPicker, self.inTrashPicker] + for picker in pickers: + if picker: + for panel in picker['items']: + if not panel.isEmpty(): + panel.enable(enabled) + + def __resetAndCleanup(self, *args): + self.__enableItemButtons(1) + self.__cleanupVerifyDelete() + + def requestDelete(self, item, itemIndex, deleteFunction): + self.__cleanupVerifyDelete() + if self.furnitureManager.ownerId != base.localAvatar.doId or not item.isDeletable(): + self.warnNonDeletableItem(item) + return + self.createVerifyDialog(item, TTLocalizer.HDDeleteItem, self.__handleVerifyDeleteOK, self.__resetAndCleanup) + self.verifyItems = (deleteFunction, item, itemIndex) + + def requestRoomDelete(self, dfitem, objectId, itemIndex): + self.__cleanupVerifyDelete() + item = dfitem.item + if self.furnitureManager.ownerId != base.localAvatar.doId or not item.isDeletable(): + self.warnNonDeletableItem(item) + return + self.createVerifyDialog(item, TTLocalizer.HDDeleteItem, self.__handleVerifyDeleteOK, self.__resetAndCleanup) + self.verifyItems = (self.deleteItemFromRoom, + dfitem, + objectId, + itemIndex) + + def warnNonDeletableItem(self, item): + message = TTLocalizer.HDNonDeletableItem + if not item.isDeletable(): + if item.getFlags() & CatalogFurnitureItem.FLBank: + message = TTLocalizer.HDNonDeletableBank + elif item.getFlags() & CatalogFurnitureItem.FLCloset: + message = TTLocalizer.HDNonDeletableCloset + elif item.getFlags() & CatalogFurnitureItem.FLPhone: + message = TTLocalizer.HDNonDeletablePhone + elif item.getFlags() & CatalogFurnitureItem.FLTrunk: + message = TTLocalizer.HDNonDeletableTrunk + if self.furnitureManager.ownerId != base.localAvatar.doId: + message = TTLocalizer.HDNonDeletableNotOwner % self.furnitureManager.ownerName + self.nonDeletableItem = TTDialog.TTDialog(text=message, style=TTDialog.Acknowledge, fadeScreen=0, command=self.__resetAndCleanup) + self.nonDeletableItem.show() + + def requestReturnToAttic(self, item, objectId): + self.__cleanupVerifyDelete() + itemIndex = None + for i in xrange(len(self.inRoomPanels)): + if self.inRoomPanels[i].itemId == objectId: + itemIndex = i + self.__enableItemButtons(0) + break + + if self.deleteMode: + dfitem = self.objectDict[objectId].dfitem + self.requestRoomDelete(dfitem, objectId, itemIndex) + return + self.createVerifyDialog(item, TTLocalizer.HDReturnVerify, self.__handleVerifyReturnOK, self.__resetAndCleanup) + self.verifyItems = (item, objectId) + return + + def __handleVerifyReturnOK(self): + item, objectId = self.verifyItems + self.__cleanupVerifyDelete() + self.pickInRoom(objectId) + self.sendItemToAttic() + + def requestReturnToAtticFromTrash(self, item, itemIndex): + self.__cleanupVerifyDelete() + self.__enableItemButtons(0) + self.createVerifyDialog(item, TTLocalizer.HDReturnFromTrashVerify, self.__handleVerifyReturnFromTrashOK, self.__resetAndCleanup) + self.verifyItems = (item, itemIndex) + + def __handleVerifyReturnFromTrashOK(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ESTATE: Send Item to Attic') + item, itemIndex = self.verifyItems + self.__cleanupVerifyDelete() + self.recoverDeletedItem(item, itemIndex) + + def recoverDeletedItem(self, item, itemIndex): + messenger.send('wakeup') + self.furnitureManager.recoverDeletedItem(item, itemIndex, self.__recoverDeletedItemCallback) + + def __recoverDeletedItemCallback(self, retcode, item, itemIndex): + self.__cleanupVerifyDelete() + if retcode < 0: + if retcode == ToontownGlobals.FM_HouseFull: + self.showHouseFullDialog() + self.notify.info('Unable to recover deleted item %s, reason %s.' % (itemIndex, retcode)) + return + self.__enableItemButtons(1) + self.inTrashPanels[itemIndex].destroy() + del self.inTrashPanels[itemIndex] + for i in xrange(itemIndex, len(self.inTrashPanels)): + self.inTrashPanels[i].itemId -= 1 + + self.regenerateInTrashPicker() + itemType = item.getTypeCode() + if itemType == CatalogItemTypes.WALLPAPER_ITEM or itemType == CatalogItemTypes.FLOORING_ITEM or itemType == CatalogItemTypes.MOULDING_ITEM or itemType == CatalogItemTypes.WAINSCOTING_ITEM: + itemIndex = len(self.atticWallpaperPanels) + bringCommand = self.bringWallpaperFromAttic + elif itemType == CatalogItemTypes.WINDOW_ITEM: + itemIndex = len(self.atticWindowPanels) + bringCommand = self.bringWindowFromAttic + else: + itemIndex = len(self.atticItemPanels) + bringCommand = self.bringItemFromAttic + panel = FurnitureItemPanel(item, itemIndex, command=bringCommand, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + if itemType == CatalogItemTypes.WALLPAPER_ITEM or itemType == CatalogItemTypes.FLOORING_ITEM or itemType == CatalogItemTypes.MOULDING_ITEM or itemType == CatalogItemTypes.WAINSCOTING_ITEM: + self.atticWallpaperPanels.append(panel) + elif itemType == CatalogItemTypes.WINDOW_ITEM: + self.atticWindowPanels.append(panel) + else: + self.atticItemPanels.append(panel) + self.regenerateAtticPicker() + + def showHouseFullDialog(self): + self.cleanupDialog() + self.dialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.HDHouseFull, text_wordwrap=15, command=self.cleanupDialog) + self.dialog.show() + + def bindHelpText(self, button, category): + button.bind(DGG.ENTER, self.showHelpText, extraArgs=[category, None]) + button.bind(DGG.EXIT, self.hideHelpText) + return + + def showHelpText(self, category, itemName, xy): + + def showIt(task): + helpText = TTLocalizer.HDHelpDict.get(category) + if helpText: + if itemName: + helpText = helpText % itemName + self.helpText['text'] = helpText + self.helpText.show() + else: + print 'category: %s not found' + + taskMgr.doMethodLater(0.75, showIt, 'showHelpTextDoLater') + + def hideHelpText(self, xy): + taskMgr.remove('showHelpTextDoLater') + self.helpText['text'] = '' + self.helpText.hide() diff --git a/toontown/fishing/BingoCardBase.py b/toontown/fishing/BingoCardBase.py new file mode 100755 index 00000000..4fe07633 --- /dev/null +++ b/toontown/fishing/BingoCardBase.py @@ -0,0 +1,146 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.fishing import FishGlobals +from toontown.fishing import BingoGlobals +from direct.showbase import RandomNumGen +from math import ceil, pow + +class BingoCardBase: + notify = DirectNotifyGlobal.directNotify.newCategory('BingoCardBase') + + def __init__(self, cardSize = BingoGlobals.CARD_SIZE, rowSize = BingoGlobals.CARD_ROWS, colSize = BingoGlobals.CARD_COLS): + self.rowSize = rowSize + self.colSize = colSize + self.cardSize = cardSize + self.cellList = [] + self.gameType = None + self.gameState = 1 << self.cardSize / 2 + return + + def destroy(self): + del self.cellList + + def generateCard(self, tileSeed, zoneId): + rng = RandomNumGen.RandomNumGen(tileSeed) + fishList = FishGlobals.getPondGeneraList(zoneId) + emptyCells = self.cardSize - 1 - len(fishList) + rodId = 0 + for i in xrange(emptyCells): + fish = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) + while not fish[0]: + fish = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) + + fishList.append((fish[1], fish[2])) + rodId += 1 + if rodId > 4: + rodId = 0 + + for index in xrange(self.cardSize): + if index != self.cardSize / 2: + choice = rng.randrange(0, len(fishList)) + self.cellList.append(fishList.pop(choice)) + else: + self.cellList.append((None, None)) + + return None + + def getGameType(self): + return self.gameType + + def getGameState(self): + return self.gameState + + def getCardSize(self): + return self.cardSize + + def getRowSize(self): + return self.rowSize + + def getColSize(self): + return self.colSize + + def setGameState(self, state): + self.gameState = state + + def clearCellList(self): + del self.cellList + self.cellList = [] + + def cellUpdateCheck(self, id, genus, species): + if id >= self.cardSize: + self.notify.warning('cellUpdateCheck: Invalid Cell Id %s. Id greater than Card Size.') + return + elif id < 0: + self.notify.warning('cellUpdateCheck: Invalid Cell Id %s. Id less than zero.') + return + fishTuple = (genus, species) + if self.cellList[id][0] == genus or fishTuple == FishGlobals.BingoBoot: + self.gameState = self.gameState | 1 << id + if self.checkForWin(id): + return BingoGlobals.WIN + return BingoGlobals.UPDATE + return BingoGlobals.NO_UPDATE + + def checkForWin(self, id): + pass + + def rowCheck(self, rowId): + for colId in xrange(self.colSize): + if not self.gameState & 1 << self.rowSize * rowId + colId: + return 0 + + return 1 + + def colCheck(self, colId): + for rowId in xrange(self.rowSize): + if not self.gameState & 1 << self.rowSize * rowId + colId: + return 0 + + return 1 + + def fDiagCheck(self, id): + checkNum = self.rowSize + 1 + if not id % checkNum: + for i in xrange(self.rowSize): + if not self.gameState & 1 << i * checkNum: + return 0 + + return 1 + else: + return 0 + + def bDiagCheck(self, id): + checkNum = self.rowSize - 1 + if not id % checkNum and not id == self.cardSize - 1: + for i in xrange(self.rowSize): + if not self.gameState & 1 << i * checkNum + checkNum: + return 0 + + return 1 + return 0 + + def cellCheck(self, id): + if self.gameState & 1 << id: + return 1 + return 0 + + def onRow(self, row, id): + if int(id / self.rowSize) == row: + return 1 + return 0 + + def onCol(self, col, id): + if id % BingoGlobals.CARD_COLS == col: + return 1 + return 0 + + def onFDiag(self, id): + checkNum = self.rowSize + 1 + if not id % checkNum: + return 1 + return 0 + + def onBDiag(self, id): + checkNum = self.rowSize - 1 + if not id % checkNum: + return 1 + return 0 diff --git a/toontown/fishing/BingoCardCell.py b/toontown/fishing/BingoCardCell.py new file mode 100755 index 00000000..813cbf62 --- /dev/null +++ b/toontown/fishing/BingoCardCell.py @@ -0,0 +1,104 @@ +from direct.fsm import FSM +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.fishing import FishPhoto +from toontown.fishing import BingoGlobals + +class BingoCardCell(DirectButton, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('BingoCardCell') + + def __init__(self, cellId, fish, model, color, parent, **kw): + self.model = model + self.color = color + buttonToUse = self.model.find('**/mickeyButton') + optiondefs = (('relief', None, None), + ('state', DGG.DISABLED, None), + ('image', buttonToUse, None), + ('image_color', self.color, None), + ('image_hpr', (0, 90, 0), None), + ('image_pos', (0, 0, 0), None), + ('pressEffect', False, None)) + self.defineoptions(kw, optiondefs) + DirectButton.__init__(self, parent) + FSM.FSM.__init__(self, 'BingoCardCell') + self.initialiseoptions(BingoCardCell) + self.parent = parent + self.fish = fish + self.cellId = cellId + self.request('Off') + return + + def destroy(self): + DirectButton.destroy(self) + + def setImageTo(self, button): + button.setHpr(0, 90, 0) + button.setPos(0, 0, 0) + button.setScale(BingoGlobals.CellImageScale) + button.setColor(self.color[0], self.color[1], self.color[2], self.color[3]) + self['image'] = button + self.setImage() + + def getButtonName(self): + genus = self.getFishGenus() + return BingoGlobals.FishButtonDict[genus][0] + + def generateLogo(self): + buttonName = self.getButtonName() + buttonToUse = self.model.find('**/' + buttonName) + self.setImageTo(buttonToUse) + + def generateMarkedLogo(self): + self.setImageTo(self.model.find('**/mickeyButton')) + + def setFish(self, fish): + if self.fish: + del self.fish + self.fish = fish + + def getFish(self): + return self.fish + + def getFishGenus(self): + if self.fish == 'Free': + return -1 + return self.fish.getGenus() + + def getFishSpecies(self): + return self.fish.getSpecies() + + def enable(self, callback = None): + self.request('On', callback) + + def disable(self): + self.request('Off') + if not self.fish == 'Free': + self.generateMarkedLogo() + + def enterOff(self): + self['state'] = DGG.DISABLED + self['command'] = None + return + + def filterOff(self, request, args): + if request == 'On': + return (request, args) + elif request == 'Off': + return request + else: + self.notify.debug('filterOff: Invalid State Transition from Off to %s' % request) + + def enterOn(self, args): + self['state'] = DGG.NORMAL + if args[0]: + self['command'] = Func(args[0], self.cellId).start + + def filterOn(self, request, args): + if request == 'Off': + return request + else: + self.notify.debug('filterOn: Invalid State Transition from Off to %s' % request) diff --git a/toontown/fishing/BingoCardGui.py b/toontown/fishing/BingoCardGui.py new file mode 100755 index 00000000..b0cba51a --- /dev/null +++ b/toontown/fishing/BingoCardGui.py @@ -0,0 +1,437 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.task import Task +import random +from toontown.fishing import BingoCardCell +from toontown.fishing import BingoGlobals +from toontown.fishing import FishBase +from toontown.fishing import FishGlobals +from direct.showbase import RandomNumGen +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownTimer +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +BG = BingoGlobals + +class BingoCardGui(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('BingoCardGui') + + def __init__(self, parent = aspect2d, **kw): + self.notify.debug('Bingo card initialized') + self.model = loader.loadModel('phase_4/models/gui/FishBingo') + optiondefs = (('relief', None, None), + ('state', DGG.NORMAL, None), + ('image', self.model.find('**/g'), None), + ('image_color', BG.getColor(0), None), + ('image_scale', BG.CardImageScale, None), + ('image_hpr', (0.0, 90.0, 0.0), None), + ('pos', BG.CardPosition, None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.initialiseoptions(BingoCardGui) + self.game = None + self.cellGuiList = [] + self.parent = parent + self.load() + self.hide() + self.taskNameFlashFish = 'flashMatchingFishTask' + return + + def show(self): + DirectFrame.show(self) + if self.game and self.game.checkForBingo(): + self.__indicateBingo(True) + if self.game.getGameType() == BG.BLOCKOUT_CARD: + self.showJackpot() + + def hide(self): + self.hideTutorial() + self.hideJackpot() + DirectFrame.hide(self) + + def loadGameTimer(self): + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(self) + self.timer.setScale(0.17) + self.timer.setPos(0.24, 0, -0.18) + + def resetGameTimer(self): + self.timer.reset() + + def startGameCountdown(self, time): + self.notify.debug('startGameCountdown: %s' % time) + self.timer.countdown(time) + + def startNextGameCountdown(self, time): + self.nextGameTimer.countdown(time) + + def hideNextGameTimer(self): + self.nextGame['text'] = '' + self.nextGame.hide() + + def showNextGameTimer(self, text): + self.nextGame['text'] = text + self.nextGame.show() + + def resetNextGameTimer(self): + self.nextGameTimer.reset() + + def stopNextGameTimer(self): + self.nextGameTimer.stop() + + def setNextGameText(self, text): + self.nextGame['text'] = text + + def setJackpotText(self, text): + if text: + str = TTLocalizer.FishBingoJackpotWin % text + else: + str = '' + self.jpText['text'] = str + + def resetGameTypeText(self): + self.gameType['text'] = '' + self.gameType.setFrameSize() + self.gameType.hide() + + def loadNextGameTimer(self): + self.nextGame = DirectLabel(parent=self, relief=None, text='', text_font=ToontownGlobals.getSignFont(), text_scale=TTLocalizer.BCGnextGame * BG.CardImageScale[2], text_fg=(1.0, 1.0, 1.0, 1), pos=(BG.GridXOffset, 0, 4 * BG.CardImageScale[2])) + self.nextGameTimer = ToontownTimer.ToontownTimer() + self.nextGameTimer.reparentTo(self.nextGame) + self.nextGameTimer.setPos(0, 0, -5 * BG.CardImageScale[2]) + self.nextGameTimer.setProp('image', None) + self.nextGameTimer.setProp('text_font', ToontownGlobals.getSignFont()) + self.nextGameTimer.setProp('text_scale', 0.2 * BG.CardImageScale[2]) + self.nextGameTimer.setFontColor(Vec4(1.0, 1.0, 1.0, 1)) + return + + def setGameOver(self, text): + self.gameOver['text'] = text + + def load(self): + self.notify.debug('Bingo card loading') + self.loadGameTimer() + self.loadNextGameTimer() + textScale = 0.06 + textHeight = 0.38 * BG.CardImageScale[2] + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.bingo = DirectButton(parent=self, pos=(BG.GridXOffset, 0, 0.305), scale=(0.0343, 0.035, 0.035), relief=None, state=DGG.DISABLED, geom=self.model.find('**/BINGObutton'), geom_pos=(0, 0, 0), geom_hpr=(0, 90, 0), image=(self.model.find('**/Gold_TTButtUP'), self.model.find('**/goldTTButtDown'), self.model.find('**/RolloverBingoButton1')), image_pos=(0, 0, 0), image_hpr=(0, 90, 0), image_color=BG.getButtonColor(0), pressEffect=False) + guiButton.removeNode() + arrowModel = loader.loadModel('phase_3.5/models/gui/speedChatGui') + self.gameType = DirectButton(parent=self, pos=(BG.GridXOffset, 0, -8 * BG.CardImageScale[2] - 0.01), relief=None, image=arrowModel.find('**/chatArrow'), image_scale=-0.05, image_pos=(-0.2, 0, 0.025), text='', text_scale=0.045, text_fg=(1, 1, 1, 1), text_font=ToontownGlobals.getSignFont(), text_wordwrap=10.5, text_pos=(0.01, 0.008), pressEffect=False) + arrowModel.removeNode() + self.gameType.bind(DGG.ENTER, self.onMouseEnter) + self.gameType.bind(DGG.EXIT, self.onMouseLeave) + self.gameType.hide() + self.jpText = DirectLabel(parent=self, pos=(BG.GridXOffset, 0, 0.22), relief=None, state=DGG.NORMAL, text='', text_scale=TTLocalizer.BCGjpText, text_pos=(0, 0, 0), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=TTLocalizer.BCGjpTextWordwrap) + self.gameOver = DirectLabel(parent=self, pos=(BG.GridXOffset, 0, 0), relief=None, state=DGG.NORMAL, text='', text_scale=textScale, text_fg=(1, 1, 1, 1), text_font=ToontownGlobals.getSignFont()) + self.jpSign = DirectFrame(parent=self.parent, relief=None, state=DGG.NORMAL, pos=BG.CardPosition, scale=(0.035, 0.035, 0.035), text=TTLocalizer.FishBingoJackpot, text_scale=2, text_pos=(-1.5, 18.6), text_fg=(1, 1, 1, 1), image=self.model.find('**/jackpot'), image_pos=(0, 0, 0), image_hpr=(0, 90, 0), sortOrder=DGG.BACKGROUND_SORT_INDEX) + self.makeJackpotLights(self.jpSign) + self.hideJackpot() + self.makeTutorial() + return + + def destroy(self): + self.cleanTutorial() + self.removeGame() + del self.cellGuiList + self.gameOver.destroy() + self.destroyJackpotLights() + self.jpSign.destroy() + self.nextGameTimer.destroy() + self.nextGame.destroy() + self.timer.destroy() + self.bingo.destroy() + self.gameType.destroy() + DirectFrame.destroy(self) + self.notify.debug('Bingo card destroyed') + + def loadCard(self): + cardSize = self.game.getCardSize() + for index in xrange(cardSize): + self.cellGuiList[index].generateLogo() + if index == cardSize / 2: + self.cellGuiList[index].generateMarkedLogo() + elif self.game.getGameState() & 1 << index: + self.cellGuiList[index].disable() + + def enableCard(self, callback = None): + self.notify.info('enable Bingo card') + self.stopCellBlinking() + for index in xrange(len(self.cellGuiList)): + if index != self.game.getCardSize() / 2: + self.cellGuiList[index].enable(callback) + + def generateCard(self, tileSeed, zoneId): + rng = RandomNumGen.RandomNumGen(tileSeed) + rowSize = self.game.getRowSize() + fishList = FishGlobals.getPondGeneraList(zoneId) + for i in xrange(len(fishList)): + fishTuple = fishList.pop(0) + weight = FishGlobals.getRandomWeight(fishTuple[0], fishTuple[1]) + fish = FishBase.FishBase(fishTuple[0], fishTuple[1], weight) + fishList.append(fish) + + emptyCells = self.game.getCardSize() - 1 - len(fishList) + rodId = 0 + for i in xrange(emptyCells): + fishVitals = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) + while not fishVitals[0]: + fishVitals = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) + + fish = FishBase.FishBase(fishVitals[1], fishVitals[2], fishVitals[3]) + fishList.append(fish) + rodId += 1 + if rodId > 4: + rodId = 0 + + for i in xrange(rowSize): + for j in xrange(self.game.getColSize()): + color = self.getCellColor(i * rowSize + j) + if i * rowSize + j == self.game.getCardSize() / 2: + tmpFish = 'Free' + else: + choice = rng.randrange(0, len(fishList)) + tmpFish = fishList.pop(choice) + xPos = BG.CellImageScale * (j - 2) + BG.GridXOffset + yPos = BG.CellImageScale * (i - 2) - 0.015 + cellGui = BingoCardCell.BingoCardCell(i * rowSize + j, tmpFish, self.model, color, self, image_scale=BG.CellImageScale, pos=(xPos, 0, yPos)) + self.cellGuiList.append(cellGui) + + def cellUpdateCheck(self, id, genus, species): + fishTuple = (genus, species) + if self.cellGuiList[id].getFishGenus() == genus or fishTuple == FishGlobals.BingoBoot: + self.notify.debug('Square found! Cell disabled: %s' % id) + self.stopCellBlinking() + self.cellGuiList[id].disable() + self.game.setGameState(self.game.getGameState() | 1 << id) + if self.game.checkForBingo(): + return BG.WIN + return BG.UPDATE + return BG.NO_UPDATE + + def cellUpdate(self, cellId): + self.cellGuiList[cellId].disable() + + def addGame(self, game): + self.game = game + type = game.getGameType() + self.gameType.setProp('text', BG.getGameName(type)) + self.gameType.setFrameSize() + self.gameType.show() + + def getGame(self): + return self.game + + def removeGame(self): + self.stopCellBlinking() + if self.game: + self.game.destroy() + self.game = None + self.setJackpotText(None) + self.resetGameTypeText() + for cell in self.cellGuiList: + cell.destroy() + + self.cellGuiList = [] + return + + def getUnmarkedMatches(self, fish): + if self.game is None: + return [] + matches = [] + for cell in self.cellGuiList: + if cell['state'] == DGG.NORMAL: + if cell.getFishGenus() == fish[0] or fish == FishGlobals.BingoBoot: + matches.append(cell) + + return matches + + def getCellColor(self, id): + if self.game.checkForColor(id): + return BG.CellColorActive + return BG.CellColorInactive + + def resetCellColors(self): + for cell in self.cellGuiList: + c = self.getCellColor(cell.cellId) + cell['image'].setColor(c[0], c[1], c[2], c[3]) + cell.setImage() + + def stopCellBlinking(self): + self.hideTutorial() + self.resetCellColors() + taskMgr.remove(self.taskNameFlashFish) + + def __indicateMatches(self, bFlipFlop, fish): + unmarkedMatches = self.getUnmarkedMatches(fish) + if len(unmarkedMatches) is 0: + return Task.done + if bFlipFlop: + for cell in unmarkedMatches: + cell['image'].setColor(1, 0, 0, 1) + cell.setImage() + + else: + self.resetCellColors() + taskMgr.doMethodLater(0.5, self.__indicateMatches, self.taskNameFlashFish, extraArgs=(not bFlipFlop, fish)) + return Task.done + + def fishCaught(self, fish): + self.stopCellBlinking() + self.__indicateMatches(True, fish) + + def setBingo(self, state = DGG.DISABLED, callback = None): + self.notify.debug('setBingo: %s %s' % (state, callback)) + if self.bingo['state'] == state: + return + if not self.game: + return + if state == DGG.NORMAL: + self.__indicateBingo(True) + if self.game.getGameType() == BG.BLOCKOUT_CARD: + self.showJackpot() + else: + taskMgr.remove('bingoFlash') + c = BG.getButtonColor(self.game.getGameType()) + self.bingo['image_color'] = Vec4(c[0], c[1], c[2], c[3]) + self.hideJackpot() + self.bingo['state'] = state + self.bingo['command'] = callback + + def checkForBingo(self): + return self.game.checkForBingo() + + def __indicateBingo(self, bFlipFlop): + if not self.game: + return Task.done + if bFlipFlop: + gameType = self.game.getGameType() + if gameType == BG.DIAGONAL_CARD: + color = Vec4(1, 1, 1, 1) + else: + color = Vec4(1, 0, 0, 1) + self.bingo['image_color'] = color + else: + c = BG.getButtonColor(self.game.getGameType()) + self.bingo['image_color'] = Vec4(c[0], c[1], c[2], c[3]) + taskMgr.doMethodLater(0.5, self.__indicateBingo, 'bingoFlash', extraArgs=(not bFlipFlop,)) + return Task.done + + NumLights = 32 + Off = False + On = True + + def showJackpot(self): + self.jpSign.show() + for light in self.jpLights: + light.show() + + self.flashJackpotLights(random.randrange(3)) + + def hideJackpot(self): + self.jpSign.hide() + for light in self.jpLights: + light.hide() + + taskMgr.remove('jackpotLightFlash') + + def getLightName(self, lightIndex, bOn): + lightIndex += 1 + if bOn == self.On: + return '**/LightOn_0%02d' % lightIndex + else: + return '**/LightOff_0%02d' % lightIndex + + def makeJackpotLights(self, parent): + self.jpLights = [] + for nLight in xrange(self.NumLights): + lightName = self.getLightName(nLight, self.Off) + light = DirectFrame(parent=parent, relief=None, image=self.model.find(lightName), image_hpr=(0, 90, 0)) + self.jpLights.append(light) + + return + + def destroyJackpotLights(self): + taskMgr.remove('jackpotLightFlash') + for light in self.jpLights: + light.destroy() + + def lightSwitch(self, bOn, lightIndex = -1): + if lightIndex == -1: + for nLight in xrange(self.NumLights): + self.lightSwitch(bOn, nLight) + + else: + lightIndex %= self.NumLights + light = self.jpLights[lightIndex - 1] + light['image'] = self.model.find(self.getLightName(lightIndex, bOn)) + light['image_hpr'] = (0, 90, 0) + + def flashJackpotLights(self, flashMode, nTimeIndex = 0): + if flashMode == 2: + self.lightSwitch(self.Off) + self.lightSwitch(self.On, nTimeIndex) + self.lightSwitch(self.On, self.NumLights - nTimeIndex) + self.lightSwitch(self.On, self.NumLights / 2 + nTimeIndex) + self.lightSwitch(self.On, self.NumLights / 2 - nTimeIndex) + nTimeIndex = (nTimeIndex + 1) % (self.NumLights / 2) + delay = 0.05 + elif flashMode == 1: + if nTimeIndex: + self.lightSwitch(self.On) + else: + self.lightSwitch(self.Off) + nTimeIndex = not nTimeIndex + delay = 0.5 + elif flashMode == 0: + for nLight in xrange(self.NumLights): + if nLight % 2 == nTimeIndex: + self.lightSwitch(self.On, nLight) + else: + self.lightSwitch(self.Off, nLight) + + nTimeIndex = (nTimeIndex + 1) % 2 + delay = 0.2 + taskMgr.doMethodLater(delay, self.flashJackpotLights, 'jackpotLightFlash', extraArgs=(flashMode, nTimeIndex)) + return Task.done + + def makeTutorial(self): + self.tutorial = TTDialog.TTDialog(fadeScreen=0, pad=(0.05, 0.05), midPad=0, topPad=0, sidePad=0, text=TTLocalizer.FishBingoHelpBlockout, style=TTDialog.NoButtons, pos=BG.TutorialPosition, scale=BG.TutorialScale) + self.tutorial.hide() + + def cleanTutorial(self): + self.tutorial.cleanup() + self.tutorial = None + return + + def showTutorial(self, messageType): + if messageType == BG.TutorialIntro: + self.tutorial['text'] = TTLocalizer.FishBingoHelpMain + elif messageType == BG.TutorialMark: + self.tutorial['text'] = TTLocalizer.FishBingoHelpFlash + elif messageType == BG.TutorialCard: + if self.game: + gameType = self.game.getGameType() + self.tutorial['text'] = BG.getHelpString(gameType) + elif messageType == BG.TutorialBingo: + self.tutorial['text'] = TTLocalizer.FishBingoHelpBingo + self.tutorial.show() + + def hideTutorial(self, event = None): + if self.tutorial: + self.tutorial.hide() + + def onMouseEnter(self, event): + if self.gameType['text'] is not '': + self.showTutorial(BG.TutorialCard) + + def onMouseLeave(self, event): + self.hideTutorial() + + def castingStarted(self): + if taskMgr.hasTaskNamed(self.taskNameFlashFish): + if not base.localAvatar.fishBingoMarkTutorialDone: + self.showTutorial(BG.TutorialMark) + base.localAvatar.b_setFishBingoMarkTutorialDone(True) diff --git a/toontown/fishing/BingoGlobals.py b/toontown/fishing/BingoGlobals.py new file mode 100755 index 00000000..e05932ea --- /dev/null +++ b/toontown/fishing/BingoGlobals.py @@ -0,0 +1,118 @@ +from toontown.toonbase import TTLocalizer +NORMAL_CARD = 0 +FOURCORNER_CARD = 1 +DIAGONAL_CARD = 2 +THREEWAY_CARD = 3 +BLOCKOUT_CARD = 4 +Style1 = ((249, 193, 41, 255), (106, 241, 233, 255), (64, 215, 206, 255)) +Style2 = ((138, 241, 106, 255), (246, 129, 220, 255), (221, 113, 197, 255)) +Style3 = ((128, 108, 250, 255), (248, 129, 56, 255), (250, 95, 26, 255)) +Style4 = ((10, 118, 251, 255), (252, 225, 97, 255), (245, 207, 29, 255)) +Style5 = ((243, 84, 253, 255), (97, 163, 253, 255), (48, 129, 240, 255)) +CardTypeDict = {NORMAL_CARD: (Style1, + 10, + 140, + TTLocalizer.FishBingoTypeNormal, + TTLocalizer.FishBingoHelpNormal), + FOURCORNER_CARD: (Style2, + 20, + 120, + TTLocalizer.FishBingoTypeCorners, + TTLocalizer.FishBingoHelpCorners), + DIAGONAL_CARD: (Style3, + 40, + 180, + TTLocalizer.FishBingoTypeDiagonal, + TTLocalizer.FishBingoHelpDiagonals), + THREEWAY_CARD: (Style4, + 80, + 180, + TTLocalizer.FishBingoTypeThreeway, + TTLocalizer.FishBingoHelpThreeway), + BLOCKOUT_CARD: (Style5, + 1000, + 90, + TTLocalizer.FishBingoTypeBlockout, + TTLocalizer.FishBingoHelpBlockout)} + +def getGameTime(typeId): + return CardTypeDict[typeId][2] + + +def getGameName(typeId): + return CardTypeDict[typeId][3] + + +def getJackpot(typeId): + return CardTypeDict[typeId][1] + + +def getColor(typeId): + float_color = map(lambda x: x / 255.0, CardTypeDict[typeId][0][0]) + return float_color + + +def getButtonColor(typeId): + float_color = map(lambda x: x / 255.0, CardTypeDict[typeId][0][1]) + return float_color + + +def getButtonRolloverColor(typeId): + float_color = map(lambda x: x / 255.0, CardTypeDict[typeId][0][2]) + return float_color + + +def getHelpString(typeId): + return CardTypeDict[typeId][4] + + +CellColorActive = (1.0, 1.0, 1.0, 1.0) +CellColorInactive = (0.8, 0.8, 0.8, 1.0) +ROLLOVER_AMOUNT = 100 +MIN_SUPER_JACKPOT = 1000 +MAX_SUPER_JACKPOT = 10000 +NO_UPDATE = 0 +UPDATE = 1 +WIN = 2 +CARD_ROWS = 5 +CARD_COLS = 5 +CARD_SIZE = 25 +INTRO_SESSION = 5.0 +TIMEOUT_SESSION = 15.0 +REWARD_TIMEOUT = 5.0 +CLOSE_EVENT_TIMEOUT = 5.0 +HOUR_BREAK_SESSION = 300 +HOUR_BREAK_MIN = 55 +NORMAL_GAME = 0 +INTERMISSION = 1 +CLOSE_EVENT = 2 +CardImageScale = (0.035, 0.035, 0.035) +CardPosition = (0.75, 1.0, -.65) +TutorialPosition = (0.2, 1.0, -0.76) +TutorialScale = 0.6 +TutorialTextScale = (0.07, 0.233) +CellImageScale = 0.088 +GridXOffset = -0.052 +FishButtonDict = {-1: ('mickeyButton',), + 0: ('BaloonFishButton',), + 2: ('CatfishButton',), + 4: ('ClownfishButton',), + 6: ('FrozenfishButton',), + 8: ('starfishButton',), + 10: ('holyMackrelButton',), + 12: ('DogfishButton',), + 14: ('amoreEelButton',), + 16: ('nursesharkButton',), + 18: ('kingcrabButton',), + 20: ('moonfishButton',), + 22: ('pPlane21',), + 24: ('poolsharkButton',), + 26: ('BearacudaButton',), + 28: ('troutButton',), + 30: ('pianotunaButton',), + 32: ('PBJfishButton',), + 34: ('DevilrayButton',)} +TutorialIntro = 1 +TutorialMark = 2 +TutorialCard = 3 +TutorialBingo = 4 diff --git a/toontown/fishing/BlockoutBingo.py b/toontown/fishing/BlockoutBingo.py new file mode 100755 index 00000000..0d7b1097 --- /dev/null +++ b/toontown/fishing/BlockoutBingo.py @@ -0,0 +1,25 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.fishing import BingoGlobals +from toontown.fishing import BingoCardBase + +class BlockoutBingo(BingoCardBase.BingoCardBase): + notify = DirectNotifyGlobal.directNotify.newCategory('BlockoutBingo') + + def __init__(self, cardSize = BingoGlobals.CARD_SIZE, rowSize = BingoGlobals.CARD_ROWS, colSize = BingoGlobals.CARD_COLS): + BingoCardBase.BingoCardBase.__init__(self, cardSize, rowSize, colSize) + self.gameType = BingoGlobals.BLOCKOUT_CARD + + def checkForWin(self, id = 0): + for i in xrange(self.rowSize): + if not self.rowCheck(i): + return BingoGlobals.NO_UPDATE + + return BingoGlobals.WIN + + def checkForColor(self, id): + return 1 + + def checkForBingo(self): + if self.checkForWin(): + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE diff --git a/toontown/fishing/DiagonalBingo.py b/toontown/fishing/DiagonalBingo.py new file mode 100755 index 00000000..f5b80630 --- /dev/null +++ b/toontown/fishing/DiagonalBingo.py @@ -0,0 +1,30 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.fishing import BingoGlobals +from toontown.fishing import BingoCardBase + +class DiagonalBingo(BingoCardBase.BingoCardBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DiagonalBingo') + + def __init__(self, cardSize = BingoGlobals.CARD_SIZE, rowSize = BingoGlobals.CARD_ROWS, colSize = BingoGlobals.CARD_COLS): + BingoCardBase.BingoCardBase.__init__(self, cardSize, rowSize, colSize) + self.gameType = BingoGlobals.DIAGONAL_CARD + self.fDiagResult = 0 + self.bDiagResult = 0 + + def checkForWin(self, id): + if self.fDiagCheck(id): + self.fDiagResult = 1 + if self.bDiagCheck(id): + self.bDiagResult = 1 + if self.fDiagResult and self.bDiagResult: + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE + + def checkForColor(self, id): + return self.onFDiag(id) | self.onBDiag(id) + + def checkForBingo(self): + id = self.cardSize / 2 + if self.checkForWin(id): + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE diff --git a/toontown/fishing/DistributedFishingPond.py b/toontown/fishing/DistributedFishingPond.py new file mode 100755 index 00000000..5bb8bef4 --- /dev/null +++ b/toontown/fishing/DistributedFishingPond.py @@ -0,0 +1,126 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import FishGlobals +from toontown.fishing import DistributedPondBingoManager +from pandac.PandaModules import Vec3 +from direct.task import Task + +class DistributedFishingPond(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFishingPond') + pollInterval = 0.5 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.notify.debug('init') + self.targets = {} + self.area = None + self.localToonBobPos = None + self.localToonSpot = None + self.pondBingoMgr = None + self.visitedSpots = {} + return + + def disable(self): + self.visitedSpots.clear() + self.stopCheckingTargets() + DistributedObject.DistributedObject.disable(self) + + def setArea(self, area): + self.area = area + + def getArea(self): + return self.area + + def addTarget(self, target): + self.notify.debug('addTarget: %s' % target) + self.targets[target.getDoId()] = target + + def removeTarget(self, target): + self.notify.debug('removeTarget: %s' % target) + del self.targets[target.getDoId()] + + def startCheckingTargets(self, spot, bobPos): + self.notify.debug('startCheckingTargets') + if base.wantBingo: + pass + self.localToonSpot = spot + self.localToonBobPos = bobPos + taskMgr.doMethodLater(self.pollInterval * 2, self.checkTargets, self.taskName('checkTargets')) + + def stopCheckingTargets(self): + self.notify.debug('stopCheckingTargets') + taskMgr.remove(self.taskName('checkTargets')) + if not base.wantBingo: + self.localToonSpot = None + self.localToonBobPos = None + return + + def checkTargets(self, task = None): + self.notify.debug('checkTargets') + if self.localToonSpot != None: + for target in self.targets.values(): + targetPos = target.getPos(render) + distVec = Vec3(targetPos - self.localToonBobPos) + dist = distVec.length() + if dist < target.getRadius(): + self.notify.debug('checkTargets: hit target: %s' % target.getDoId()) + self.d_hitTarget(target) + return Task.done + + taskMgr.doMethodLater(self.pollInterval, self.checkTargets, self.taskName('checkTargets')) + else: + self.notify.warning('localToonSpot became None while checking targets') + return Task.done + + def d_hitTarget(self, target): + self.localToonSpot.hitTarget() + self.sendUpdate('hitTarget', [target.getDoId()]) + + def setPondBingoManager(self, pondBingoMgr): + self.pondBingoMgr = pondBingoMgr + + def removePondBingoManager(self): + del self.pondBingoMgr + self.pondBingoMgr = None + return + + def getPondBingoManager(self): + return self.pondBingoMgr + + def hasPondBingoManager(self): + return self.pondBingoMgr is not None + + def handleBingoCatch(self, catch): + if self.pondBingoMgr: + self.pondBingoMgr.setLastCatch(catch) + + def handleBingoBoot(self): + if self.pondBingoMgr: + self.pondBingoMgr.handleBoot() + + def cleanupBingoMgr(self): + if self.pondBingoMgr: + self.pondBingoMgr.cleanup() + + def setLocalToonSpot(self, spot = None): + self.localToonSpot = spot + if spot is not None and spot.getDoId() not in self.visitedSpots: + self.visitedSpots[spot.getDoId()] = spot + return + + def showBingoGui(self): + if self.pondBingoMgr: + self.pondBingoMgr.showCard() + + def getLocalToonSpot(self): + return self.localToonSpot + + def resetSpotGui(self): + for spot in self.visitedSpots.values(): + spot.resetCastGui() + + def setSpotGui(self): + for spot in self.visitedSpots.values(): + spot.setCastGui() diff --git a/toontown/fishing/DistributedFishingPondAI.py b/toontown/fishing/DistributedFishingPondAI.py new file mode 100755 index 00000000..ee98b379 --- /dev/null +++ b/toontown/fishing/DistributedFishingPondAI.py @@ -0,0 +1,81 @@ +from direct.directnotify.DirectNotifyGlobal import * +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.toonbase import ToontownGlobals +from DistributedFishingTargetAI import DistributedFishingTargetAI +from DistributedPondBingoManagerAI import DistributedPondBingoManagerAI +import FishingTargetGlobals + +class DistributedFishingPondAI(DistributedObjectAI): + notify = directNotify.newCategory("DistributedFishingPondAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + + self.area = None + self.targets = {} + self.spots = {} + self.bingoMgr = None + + def announceGenerate(self): + if self.air.newsManager.isHolidayRunning(ToontownGlobals.FISH_BINGO, ToontownGlobals.SILLY_SATURDAY): + self.startBingo() + + self.accept('startBingo', self.startBingo) + self.accept('stopBingo', self.stopBingo) + DistributedObjectAI.announceGenerate(self) + + def delete(self): + self.ignoreAll() + DistributedObjectAI.delete(self) + + def start(self): + for _ in xrange(FishingTargetGlobals.getNumTargets(self.area)): + fishingTarget = DistributedFishingTargetAI(simbase.air) + fishingTarget.setPondDoId(self.doId) + fishingTarget.generateWithRequired(self.zoneId) + + def startBingo(self): + if self.bingoMgr: + self.notify.warning('Tried to start bingo while already started!') + return + + self.bingoMgr = DistributedPondBingoManagerAI(self.air) + self.bingoMgr.setPondDoId(self.getDoId()) + self.bingoMgr.generateWithRequired(self.zoneId) + self.bingoMgr.createGame() + + def stopBingo(self): + if not self.bingoMgr: + self.notify.warning('Tried to stop bingo but not started!') + return + + self.bingoMgr.requestDelete() + self.bingoMgr = None + + def hitTarget(self, target): + avId = self.air.getAvatarIdFromSender() + if self.targets.get(target) is None: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to hit nonexistent fishing target!') + return + spot = self.hasToon(avId) + if spot: + spot.rewardIfValid(target) + return + self.air.writeServerEvent('suspicious', avId, 'Toon tried to catch fish while not fishing!') + + def addTarget(self, target): + self.targets[target.doId] = target + + def addSpot(self, spot): + self.spots[spot.doId] = spot + + def setArea(self, area): + self.area = area + + def getArea(self): + return self.area + + def hasToon(self, avId): + for spot in self.spots: + if self.spots[spot].avId == avId: + return self.spots[spot] diff --git a/toontown/fishing/DistributedFishingTarget.py b/toontown/fishing/DistributedFishingTarget.py new file mode 100755 index 00000000..c617be5d --- /dev/null +++ b/toontown/fishing/DistributedFishingTarget.py @@ -0,0 +1,93 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedNode +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.directutil import Mopath +from toontown.toonbase import ToontownGlobals +from toontown.hood import FishAnimatedProp +from direct.actor import Actor +import FishingTargetGlobals +import random +import math +from toontown.effects import Bubbles + +class DistributedFishingTarget(DistributedNode.DistributedNode): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFishingTarget') + radius = 2.5 + + def __init__(self, cr): + DistributedNode.DistributedNode.__init__(self, cr) + NodePath.__init__(self) + self.pond = None + self.centerPoint = (0, 0, 0) + self.maxRadius = 1.0 + self.track = None + self.pondDoId = None + return + + def generate(self): + self.assign(render.attachNewNode('DistributedFishingTarget')) + shadow = loader.loadModel('phase_3/models/props/drop_shadow') + shadow.setPos(0, 0, -0.1) + shadow.setScale(0.33) + shadow.setColorScale(1, 1, 1, 0.75) + shadow.reparentTo(self) + self.bubbles = Bubbles.Bubbles(self, render) + self.bubbles.renderParent.setDepthWrite(0) + self.bubbles.start() + self.fish = FishAnimatedProp.FishAnimatedProp(self) + self.fish.enter() + DistributedNode.DistributedNode.generate(self) + + def disable(self): + if self.track: + self.track.finish() + self.track = None + self.bubbles.destroy() + del self.bubbles + self.fish.exit() + self.fish.delete() + del self.fish + if self.pond: + self.pond.removeTarget(self) + self.pond = None + self.ignore('generate-%d' % self.pondDoId) + DistributedNode.DistributedNode.disable(self) + return + + def delete(self): + del self.pond + DistributedNode.DistributedNode.delete(self) + + def setPondDoId(self, pondDoId): + self.pondDoId = pondDoId + if pondDoId in self.cr.doId2do: + self.setPond(self.cr.doId2do[pondDoId]) + else: + self.acceptOnce('generate-%d' % pondDoId, self.setPond) + + def setPond(self, pond): + self.pond = pond + self.pond.addTarget(self) + self.centerPoint = FishingTargetGlobals.getTargetCenter(self.pond.getArea()) + self.maxRadius = FishingTargetGlobals.getTargetRadius(self.pond.getArea()) + + def getDestPos(self, angle, radius): + x = radius * math.cos(angle) + self.centerPoint[0] + y = radius * math.sin(angle) + self.centerPoint[1] + z = self.centerPoint[2] + return (x, y, z) + + def setState(self, stateIndex, angle, radius, time, timeStamp): + ts = globalClockDelta.localElapsedTime(timeStamp) + pos = self.getDestPos(angle, radius) + if self.track and self.track.isPlaying(): + self.track.finish() + self.track = Sequence(LerpPosInterval(self, time - ts, Point3(*pos), blendType='easeInOut')) + self.track.start() + + def getRadius(self): + return self.radius diff --git a/toontown/fishing/DistributedFishingTargetAI.py b/toontown/fishing/DistributedFishingTargetAI.py new file mode 100755 index 00000000..307ede59 --- /dev/null +++ b/toontown/fishing/DistributedFishingTargetAI.py @@ -0,0 +1,57 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedNodeAI import DistributedNodeAI +from direct.distributed.ClockDelta import * +from toontown.fishing import FishingTargetGlobals +from direct.task import Task +import random +import math + +class DistributedFishingTargetAI(DistributedNodeAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedFishingTargetAI") + + def __init__(self, air): + DistributedNodeAI.__init__(self, air) + self.pondId = 0 + self.angle = 0 + self.targetRadius = 0 + self.time = 0 + self.centerPoint = [0, 0, 0] + + def generate(self): + DistributedNodeAI.generate(self) + self.updateState() + if not self.pondId: + #We dont have a pond ID for some reason... + return + pond = self.air.doId2do[self.pondId] + pond.addTarget(self) + self.centerPoint = FishingTargetGlobals.getTargetCenter(pond.getArea()) + + def delete(self): + taskMgr.remove('updateFishingTarget%d' % self.doId) + + DistributedNodeAI.delete(self) + + def setPondDoId(self, pondId): + self.pondId = pondId + + def getPondDoId(self): + return self.pondId + + def setState(self, stateIndex, angle, radius, time, timeStamp): + self.angle = angle + self.targetRadius = radius + self.time = time + + def getState(self): + return [0, self.angle, self.targetRadius, self.time, globalClockDelta.getRealNetworkTime()] + + def updateState(self): + if not self.pondId in self.air.doId2do: + return + self.b_setPosHpr(self.targetRadius * math.cos(self.angle) + self.centerPoint[0], self.targetRadius * math.sin(self.angle) + self.centerPoint[1], self.centerPoint[2], 0, 0, 0) + self.angle = random.randrange(359) + self.targetRadius = random.uniform(FishingTargetGlobals.getTargetRadius(self.air.doId2do[self.pondId].getArea()), 0) + self.time = random.uniform(10.0, 5.0) + self.sendUpdate('setState', [0, self.angle, self.targetRadius, self.time, globalClockDelta.getRealNetworkTime()]) + taskMgr.doMethodLater(self.time + random.uniform(5, 2.5), DistributedFishingTargetAI.updateState, 'updateFishingTarget%d' % self.doId, [self]) diff --git a/toontown/fishing/DistributedPondBingoManager.py b/toontown/fishing/DistributedPondBingoManager.py new file mode 100755 index 00000000..5eaa61d0 --- /dev/null +++ b/toontown/fishing/DistributedPondBingoManager.py @@ -0,0 +1,336 @@ +from direct.distributed import DistributedObject +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import FSM +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.task import Task +from toontown.fishing import BingoGlobals +from toontown.fishing import BingoCardGui +from toontown.fishing import FishGlobals +from toontown.fishing import NormalBingo +from toontown.fishing import FourCornerBingo +from toontown.fishing import DiagonalBingo +from toontown.fishing import ThreewayBingo +from toontown.fishing import BlockoutBingo +from direct.showbase import RandomNumGen +from toontown.toonbase import ToontownTimer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import time + +class DistributedPondBingoManager(DistributedObject.DistributedObject, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPondBingoManager') + cardTypeDict = {BingoGlobals.NORMAL_CARD: NormalBingo.NormalBingo, + BingoGlobals.FOURCORNER_CARD: FourCornerBingo.FourCornerBingo, + BingoGlobals.DIAGONAL_CARD: DiagonalBingo.DiagonalBingo, + BingoGlobals.THREEWAY_CARD: ThreewayBingo.ThreewayBingo, + BingoGlobals.BLOCKOUT_CARD: BlockoutBingo.BlockoutBingo} + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedPondBingoManager') + self.cardId = 0 + self.jackpot = 0 + self.pond = None + self.spot = None + self.card = None + self.hasEntered = 0 + self.initGameState = None + self.lastCatch = None + self.typeId = BingoGlobals.NORMAL_CARD + return + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.card = BingoCardGui.BingoCardGui() + self.card.reparentTo(aspect2d, 1) + self.card.hideNextGameTimer() + self.notify.debug('generate: DistributedPondBingoManager') + + def delete(self): + self.pond.resetSpotGui() + del self.pond.pondBingoMgr + self.pond.pondBingoMgr = None + del self.pond + self.pond = None + FSM.FSM.cleanup(self) + self.card.destroy() + del self.card + self.notify.debug('delete: Deleting Local PondManager %s' % self.doId) + DistributedObject.DistributedObject.delete(self) + + def d_cardUpdate(self, cellId, genus, species): + self.sendUpdate('cardUpdate', [self.cardId, + cellId, + genus, + species]) + + def d_bingoCall(self): + self.sendUpdate('handleBingoCall', [self.cardId]) + + def setCardState(self, cardId, typeId, tileSeed, gameState): + self.cardId = cardId + self.typeId = typeId + self.tileSeed = tileSeed + self.jackpot = BingoGlobals.getJackpot(typeId) + self.initGameState = gameState + + def checkForUpdate(self, cellId): + if self.lastCatch is not None: + genus = self.lastCatch[0] + species = self.lastCatch[1] + self.d_cardUpdate(cellId, genus, species) + success = self.card.cellUpdateCheck(cellId, genus, species) + if success == BingoGlobals.WIN: + self.lastCatch = None + self.enableBingo() + self.pond.getLocalToonSpot().cleanupFishPanel() + self.pond.getLocalToonSpot().hideBootPanel() + elif success == BingoGlobals.UPDATE: + self.lastCatch = None + self.pond.getLocalToonSpot().cleanupFishPanel() + self.pond.getLocalToonSpot().hideBootPanel() + else: + self.notify.warning('CheckForWin: Attempt to Play Cell without a valid catch.') + return + + def updateGameState(self, gameState, cellId): + game = self.card.getGame() + if game is not None: + game.setGameState(gameState) + self.card.cellUpdate(cellId) + return + + def __generateCard(self): + self.notify.debug('__generateCard: %s' % self.typeId) + if self.card.getGame(): + self.card.removeGame() + game = self.__cardChoice() + game.setGameState(self.initGameState) + self.card.addGame(game) + self.card.generateCard(self.tileSeed, self.pond.getArea()) + color = BingoGlobals.getColor(self.typeId) + self.card.setProp('image_color', VBase4(color[0], color[1], color[2], color[3])) + color = BingoGlobals.getButtonColor(self.typeId) + self.card.bingo.setProp('image_color', VBase4(color[0], color[1], color[2], color[3])) + if self.hasEntered: + self.card.loadCard() + self.card.show() + else: + self.card.hide() + + def showCard(self): + if self.state != 'Off' and self.card.getGame() != None: + self.card.loadCard() + self.card.show() + elif self.state == 'GameOver': + self.card.show() + elif self.state == 'Reward': + self.card.show() + elif self.state == 'WaitCountdown': + self.card.show() + self.card.showNextGameTimer(TTLocalizer.FishBingoNextGame) + elif self.state == 'Intermission': + self.card.showNextGameTimer(TTLocalizer.FishBingoIntermission) + self.card.show() + self.hasEntered = 1 + + def __cardChoice(self): + return self.cardTypeDict.get(self.typeId)() + + def checkForBingo(self): + success = self.card.checkForBingo() + if success: + self.d_bingoCall() + self.request('Reward') + + def enableBingo(self): + self.card.setBingo(DGG.NORMAL, self.checkForBingo) + + def setPondDoId(self, pondId): + self.pondDoId = pondId + if pondId in self.cr.doId2do: + self.setPond(self.cr.doId2do[pondId]) + else: + self.acceptOnce('generate-%d' % pondId, self.setPond) + + def setPond(self, pond): + self.pond = pond + self.pond.setPondBingoManager(self) + + def setState(self, state, timeStamp): + self.notify.debug('State change: %s -> %s' % (self.state, state)) + self.request(state, timeStamp) + + def setLastCatch(self, catch): + self.lastCatch = catch + self.card.fishCaught(catch) + + def castingStarted(self): + if self.card: + self.card.castingStarted() + + def setSpot(self, spot): + self.spot = spot + + def setJackpot(self, jackpot): + self.jackpot = jackpot + + #todo: fix crash + def enterOff(self, args = None): + self.notify.debug('enterOff: Enter Off State') + del self.spot + self.spot = None + if self.card.getGame: + self.card.removeGame() + self.card.hide() + self.card.stopNextGameTimer() + self.hasEntered = 0 + self.lastCatch = None + return + + def filterOff(self, request, args): + if request == 'Intro': + return 'Intro' + elif request == 'WaitCountdown': + return (request, args) + elif request == 'Playing': + self.__generateCard() + self.card.setJackpotText(str(self.jackpot)) + return (request, args) + elif request == 'Intermission': + return (request, args) + elif request == 'GameOver': + return (request, args) + elif request == 'Reward': + return ('GameOver', args) + else: + self.notify.debug('filterOff: Invalid State Transition from, Off to %s' % request) + + def exitOff(self): + self.notify.debug('exitOff: Exit Off State') + + def enterIntro(self, args = None): + self.notify.debug('enterIntro: Enter Intro State') + self.pond.setSpotGui() + self.hasEntered = 1 + + def filterIntro(self, request, args): + if request == 'WaitCountdown': + return (request, args) + else: + self.notify.debug('filterIntro: Invalid State Transition from Intro to %s' % request) + + def exitIntro(self): + self.notify.debug('exitIntro: Exit Intro State') + + def enterWaitCountdown(self, timeStamp): + self.notify.debug('enterWaitCountdown: Enter WaitCountdown State') + time = BingoGlobals.TIMEOUT_SESSION - globalClockDelta.localElapsedTime(timeStamp[0]) + self.card.startNextGameCountdown(time) + if self.hasEntered: + self.card.showNextGameTimer(TTLocalizer.FishBingoNextGame) + + def filterWaitCountdown(self, request, args): + if request == 'Playing': + return (request, args) + else: + self.notify.debug('filterOff: Invalid State Transition from WaitCountdown to %s' % request) + + def exitWaitCountdown(self): + self.notify.debug('exitWaitCountdown: Exit WaitCountdown State') + if self.pond: + self.__generateCard() + self.card.setJackpotText(str(self.jackpot)) + self.card.resetGameTimer() + self.card.hideNextGameTimer() + + def enterPlaying(self, timeStamp): + self.notify.debug('enterPlaying: Enter Playing State') + self.lastCatch = None + session = BingoGlobals.getGameTime(self.typeId) + time = session - globalClockDelta.localElapsedTime(timeStamp[0]) + self.card.startGameCountdown(time) + self.card.enableCard(self.checkForUpdate) + return + + def filterPlaying(self, request, args): + if request == 'Reward': + return (request, args) + elif request == 'GameOver': + return (request, args) + else: + self.notify.debug('filterOff: Invalid State Transition from Playing to %s' % request) + + def exitPlaying(self): + self.notify.debug('exitPlaying: Exit Playing State') + self.card.resetGameTimer() + + def enterReward(self, timeStamp): + self.notify.debug('enterReward: Enter Reward State') + if self.card: + self.card.setBingo() + self.card.removeGame() + self.card.setGameOver(TTLocalizer.FishBingoVictory) + localToonSpot = self.pond.getLocalToonSpot() + if localToonSpot: + localToonSpot.setJarAmount(self.jackpot) + self.jackpot = 0 + + def filterReward(self, request, args): + if request == 'WaitCountdown': + return (request, args) + elif request == 'Intermission': + return (request, args) + elif request == 'Off': + return 'Off' + else: + self.notify.debug('filterOff: Invalid State Transition from Reward to %s' % request) + + def exitReward(self): + self.notify.debug('exitReward: Exit Reward State') + self.card.setGameOver('') + + def enterGameOver(self, timeStamp): + self.notify.debug('enterGameOver: Enter GameOver State') + self.card.setBingo() + self.card.removeGame() + self.card.setGameOver(TTLocalizer.FishBingoGameOver) + + def filterGameOver(self, request, args): + if request == 'WaitCountdown': + return (request, args) + elif request == 'Intermission': + return (request, args) + elif request == 'Off': + return 'Off' + else: + self.notify.debug('filterOff: Invalid State Transition from GameOver to %s' % request) + + def exitGameOver(self): + self.notify.debug('exitGameOver: Exit GameOver State') + self.card.setGameOver('') + self.card.resetGameTypeText() + + def enterIntermission(self, timeStamp): + self.notify.debug('enterIntermission: Enter Intermission State') + if self.hasEntered: + self.card.showNextGameTimer(TTLocalizer.FishBingoIntermission) + self.notify.debug('enterIntermission: timestamp %s' % timeStamp[0]) + elapsedTime = globalClockDelta.localElapsedTime(timeStamp[0]) + self.notify.debug('enterIntermission: elapsedTime %s' % elapsedTime) + waitTime = BingoGlobals.HOUR_BREAK_SESSION - elapsedTime + self.notify.debug('enterIntermission: waitTime %s' % waitTime) + self.card.startNextGameCountdown(waitTime) + + def filterIntermission(self, request, args): + if request == 'WaitCountdown': + return (request, args) + elif request == 'Off': + return 'Off' + else: + self.notify.warning('filterOff: Invalid State Transition from GameOver to %s' % request) + + def exitIntermission(self): + self.notify.debug('enterIntermission: Exit Intermission State') diff --git a/toontown/fishing/DistributedPondBingoManagerAI.py b/toontown/fishing/DistributedPondBingoManagerAI.py new file mode 100755 index 00000000..bcdab127 --- /dev/null +++ b/toontown/fishing/DistributedPondBingoManagerAI.py @@ -0,0 +1,192 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.distributed.ClockDelta import * +from toontown.fishing import BingoGlobals +from toontown.fishing import FishGlobals +from toontown.fishing.NormalBingo import NormalBingo +from toontown.fishing.ThreewayBingo import ThreewayBingo +from toontown.fishing.DiagonalBingo import DiagonalBingo +from toontown.fishing.BlockoutBingo import BlockoutBingo +from toontown.fishing.FourCornerBingo import FourCornerBingo +import random + +RequestCard = {} + +class DistributedPondBingoManagerAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPondBingoManagerAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.air = air + self.bingoCard = None + self.tileSeed = None + self.typeId = None + self.state = 'Off' + self.pond = None + self.canCall = False + self.lastUpdate = globalClockDelta.getRealNetworkTime() + self.cardId = 0 + + def delete(self): + taskMgr.remove(self.uniqueName('startWait')) + taskMgr.remove(self.uniqueName('createGame')) + taskMgr.remove(self.uniqueName('finishGame')) + DistributedObjectAI.delete(self) + + def setPondDoId(self, pondId): + self.pond = self.air.doId2do[pondId] + + def getPondDoId(self): + return self.pond.getDoId() + + def cardUpdate(self, cardId, cellId, genus, species): + avId = self.air.getAvatarIdFromSender() + spot = self.pond.hasToon(avId) + if not spot: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to call bingo while not fishing!') + return + fishTuple = (genus, species) + if (genus != spot.lastFish[1] or species != spot.lastFish[2]) and (spot.lastFish[0] != FishGlobals.BootItem): + self.air.writeServerEvent('suspicious', avId, 'Toon tried to update bingo card with a fish they didn\'t catch!') + return + if cardId != self.cardId: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to update expired bingo card!') + return + if self.state != 'Playing': + self.air.writeServerEvent('suspicious', avId, 'Toon tried to update while the game is not running!') + return + spot.lastFish = [None, None, None, None] + result = self.bingoCard.cellUpdateCheck(cellId, genus, species) + if result == BingoGlobals.WIN: + self.canCall = True + self.sendCanBingo() + self.sendGameStateUpdate(cellId) + elif result == BingoGlobals.UPDATE: + self.sendGameStateUpdate(cellId) + + def handleBingoCall(self, cardId): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + + if not av: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to call bingo while not on district!') + return + + spot = self.pond.hasToon(avId) + + if not spot: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to call bingo while not fishing!') + return + if not self.canCall: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to call bingo whle the game is not running!') + return + if cardId != self.cardId: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to call bingo with an expired cardId!') + return + + av.d_announceBingo() + self.rewardAll() + + def setJackpot(self, jackpot): + self.jackpot = jackpot + + def d_setJackpot(self, jackpot): + self.sendUpdate('setJackpot', [jackpot]) + + def b_setJackpot(self, jackpot): + self.setJackpot(jackpot) + self.d_setJackpot(jackpot) + + def activateBingoForPlayer(self, avId): + self.sendUpdateToAvatarId(avId, 'setCardState', [self.cardId, self.typeId, self.tileSeed, self.bingoCard.getGameState()]) + self.sendUpdateToAvatarId(avId, 'setState', [self.state, self.lastUpdate]) + self.canCall = True + + def sendStateUpdate(self): + self.lastUpdate = globalClockDelta.getRealNetworkTime() + for spot in self.pond.spots: + if self.pond.spots[spot].avId == None or self.pond.spots[spot].avId == 0: + continue + avId = self.pond.spots[spot].avId + self.sendUpdateToAvatarId(avId, 'setState', [self.state, self.lastUpdate]) + + def sendCardStateUpdate(self): + for spot in self.pond.spots: + if self.pond.spots[spot].avId == None or self.pond.spots[spot].avId == 0: + continue + avId = self.pond.spots[spot].avId + self.sendUpdateToAvatarId(avId, 'setCardState', [self.cardId, self.typeId, self.tileSeed, self.bingoCard.getGameState()]) + + def sendGameStateUpdate(self, cellId): + for spot in self.pond.spots: + if self.pond.spots[spot].avId == None or self.pond.spots[spot].avId == 0: + continue + avId = self.pond.spots[spot].avId + self.sendUpdateToAvatarId(avId, 'updateGameState', [self.bingoCard.getGameState(), cellId]) + + def sendCanBingo(self): + for spot in self.pond.spots: + if self.pond.spots[spot].avId == None or self.pond.spots[spot].avId == 0: + continue + avId = self.pond.spots[spot].avId + self.sendUpdateToAvatarId(avId, 'enableBingo', []) + + def rewardAll(self): + self.state = 'Reward' + self.sendStateUpdate() + for spot in self.pond.spots: + if self.pond.spots[spot].avId == None or self.pond.spots[spot].avId == 0: + continue + av = self.air.doId2do[self.pond.spots[spot].avId] + av.addMoney(self.jackpot) + taskMgr.doMethodLater(5, self.startWait, self.uniqueName('startWait')) + taskMgr.remove(self.uniqueName('finishGame')) + + def finishGame(self, task=None): + self.state = 'GameOver' + self.sendStateUpdate() + taskMgr.doMethodLater(5, self.startWait, self.uniqueName('startWait')) + + def startIntermission(self): + self.state = 'Intermission' + self.sendStateUpdate() + taskMgr.doMethodLater(300, self.startWait, self.uniqueName('startWait')) + + def startWait(self, task=None): + self.state = 'WaitCountdown' + self.sendStateUpdate() + taskMgr.doMethodLater(15, self.createGame, self.uniqueName('createGame')) + + def createGame(self, task=None): + self.canCall = False + self.tileSeed = None + self.typeId = None + self.cardId += 1 + for spot in self.pond.spots: + avId = self.pond.spots[spot].avId + request = RequestCard.get(avId) + if request: + self.typeId, self.tileSeed = request + del RequestCard[avId] + if self.cardId > 65535: + self.cardId = 0 + if not self.tileSeed: + self.tileSeed = random.randrange(0, 65535) + if self.typeId == None: + self.typeId = random.randrange(0, 4) + if self.typeId == BingoGlobals.NORMAL_CARD: + self.bingoCard = NormalBingo() + elif self.typeId == BingoGlobals.DIAGONAL_CARD: + self.bingoCard = DiagonalBingo() + elif self.typeId == BingoGlobals.THREEWAY_CARD: + self.bingoCard = ThreewayBingo() + elif self.typeId == BingoGlobals.FOURCORNER_CARD: + self.bingoCard = FourCornerBingo() + else: + self.bingoCard = BlockoutBingo() + self.bingoCard.generateCard(self.tileSeed, self.pond.getArea()) + self.sendCardStateUpdate() + self.b_setJackpot(BingoGlobals.getJackpot(self.typeId)) + self.state = 'Playing' + self.sendStateUpdate() + taskMgr.doMethodLater(BingoGlobals.getGameTime(self.typeId), self.finishGame, self.uniqueName('finishGame')) diff --git a/toontown/fishing/FishBase.py b/toontown/fishing/FishBase.py new file mode 100755 index 00000000..58dd8003 --- /dev/null +++ b/toontown/fishing/FishBase.py @@ -0,0 +1,74 @@ +import FishGlobals +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal + +class FishBase: + notify = DirectNotifyGlobal.directNotify.newCategory('FishBase') + + def __init__(self, genus, species, weight): + self.genus = genus + self.species = species + self.weight = weight + + def getGenus(self): + return self.genus + + def getSpecies(self): + return self.species + + def getWeight(self): + return self.weight + + def setWeight(self, weight): + self.weight = weight + + def getVitals(self): + return (self.genus, self.species, self.weight) + + def getValue(self): + return FishGlobals.getValue(self.genus, self.species, self.weight) + + def getGenusName(self): + return TTLocalizer.FishGenusNames[self.genus] + + def getSpeciesName(self): + return TTLocalizer.FishSpeciesNames[self.genus][self.species] + + def getRarity(self): + return FishGlobals.getRarity(self.genus, self.species) + + def getPhase(self): + dict = FishGlobals.FishFileDict + fileInfo = dict.get(self.genus, dict[-1]) + return fileInfo[0] + + def getActor(self): + prefix = 'phase_%s/models/char/' % self.getPhase() + dict = FishGlobals.FishFileDict + fileInfo = dict.get(self.genus, dict[-1]) + from direct.actor import Actor + actor = Actor.Actor(prefix + fileInfo[1], {'intro': prefix + fileInfo[2], + 'swim': prefix + fileInfo[3]}) + return actor + + def getSound(self): + sound = None + loop = None + delay = None + playRate = None + if base.config.GetBool('want-fish-audio', 1): + soundDict = FishGlobals.FishAudioFileDict + fileInfo = soundDict.get(self.genus, None) + if fileInfo: + prefix = 'phase_%s/audio/sfx/' % self.getPhase() + sound = loader.loadSfx(prefix + soundDict[self.genus][0]) + loop = soundDict[self.genus][1] + delay = soundDict[self.genus][2] + playRate = soundDict[self.genus][3] + return (sound, + loop, + delay, + playRate) + + def __str__(self): + return '%s, weight: %s value: %s' % (self.getSpeciesName(), self.weight, self.getValue()) diff --git a/toontown/fishing/FishBrowser.py b/toontown/fishing/FishBrowser.py new file mode 100755 index 00000000..453a85d6 --- /dev/null +++ b/toontown/fishing/FishBrowser.py @@ -0,0 +1,59 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer +import GenusPanel +import FishGlobals + +class FishBrowser(DirectScrolledList): + notify = DirectNotifyGlobal.directNotify.newCategory('FishBrowser') + + def __init__(self, parent = aspect2d, **kw): + self.parent = parent + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + optiondefs = (('parent', self.parent, None), + ('relief', None, None), + ('incButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('incButton_relief', None, None), + ('incButton_scale', (1.3, 1.3, -1.3), None), + ('incButton_pos', (0, 0, -0.525), None), + ('incButton_image3_color', Vec4(0.8, 0.8, 0.8, 0.5), None), + ('decButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('decButton_relief', None, None), + ('decButton_scale', (1.3, 1.3, 1.3), None), + ('decButton_pos', (0, 0, 0.525), None), + ('decButton_image3_color', Vec4(0.8, 0.8, 0.8, 0.5), None), + ('numItemsVisible', 1, None), + ('items', map(str, FishGlobals.getGenera()), None), + ('scrollSpeed', 4, None), + ('itemMakeFunction', GenusPanel.GenusPanel, None), + ('itemMakeExtraArgs', None, None)) + gui.removeNode() + self.defineoptions(kw, optiondefs) + DirectScrolledList.__init__(self, parent) + self.initialiseoptions(FishBrowser) + return None + + def destroy(self): + DirectScrolledList.destroy(self) + self.parent = None + return + + def update(self): + pass + + def show(self): + if not self.parent.isHidden(): + self['items'][self.index].show() + DirectScrolledList.show(self) + + def hide(self): + self['items'][self.index].hide() + DirectScrolledList.hide(self) diff --git a/toontown/fishing/FishCollection.py b/toontown/fishing/FishCollection.py new file mode 100755 index 00000000..31566f3b --- /dev/null +++ b/toontown/fishing/FishCollection.py @@ -0,0 +1,71 @@ +import FishBase +import FishGlobals + +class FishCollection: + + def __init__(self): + self.fishList = [] + + def __len__(self): + return len(self.fishList) + + def getFish(self): + return self.fishList + + def makeFromNetLists(self, genusList, speciesList, weightList): + self.fishList = [] + for genus, species, weight in zip(genusList, speciesList, weightList): + self.fishList.append(FishBase.FishBase(genus, species, weight)) + + def getNetLists(self): + genusList = [] + speciesList = [] + weightList = [] + for fish in self.fishList: + genusList.append(fish.getGenus()) + speciesList.append(fish.getSpecies()) + weightList.append(fish.getWeight()) + + return [genusList, speciesList, weightList] + + def hasFish(self, genus, species): + for fish in self.fishList: + if fish.getGenus() == genus and fish.getSpecies() == species: + return 1 + + return 0 + + def hasGenus(self, genus): + for fish in self.fishList: + if fish.getGenus() == genus: + return 1 + + return 0 + + def __collect(self, newFish, updateCollection): + for fish in self.fishList: + if fish.getGenus() == newFish.getGenus() and fish.getSpecies() == newFish.getSpecies(): + if fish.getWeight() < newFish.getWeight(): + if updateCollection: + fish.setWeight(newFish.getWeight()) + return FishGlobals.COLLECT_NEW_RECORD + else: + return FishGlobals.COLLECT_NO_UPDATE + + if updateCollection: + self.fishList.append(newFish) + return FishGlobals.COLLECT_NEW_ENTRY + + def collectFish(self, newFish): + return self.__collect(newFish, updateCollection=1) + + def getCollectResult(self, newFish): + return self.__collect(newFish, updateCollection=0) + + def __str__(self): + numFish = len(self.fishList) + txt = 'Fish Collection (%s fish):' % numFish + for fish in self.fishList: + txt += '\n' + str(fish) + + return txt diff --git a/toontown/fishing/FishGlobals.py b/toontown/fishing/FishGlobals.py new file mode 100755 index 00000000..bafe4492 --- /dev/null +++ b/toontown/fishing/FishGlobals.py @@ -0,0 +1,897 @@ +from toontown.toonbase import TTLocalizer +from math import ceil, pow +import random +from toontown.toonbase import ToontownGlobals +import copy +NoMovie = 0 +EnterMovie = 1 +ExitMovie = 2 +CastMovie = 3 +PullInMovie = 4 +CastTimeout = 45.0 +Nothing = 0 +QuestItem = 1 +FishItem = 2 +JellybeanItem = 3 +BootItem = 4 +GagItem = 5 +OverTankLimit = 8 +FishItemNewEntry = 9 +FishItemNewRecord = 10 +BingoBoot = (BootItem, 99) +ProbabilityDict = {94: FishItem, + 92: QuestItem, + 95: JellybeanItem, + 100: BootItem} +SortedProbabilityCutoffs = ProbabilityDict.keys() +SortedProbabilityCutoffs.sort() +Rod2JellybeanDict = {0: 10, + 1: 20, + 2: 30, + 3: 75, + 4: 150} +HealAmount = 1 +JellybeanFishingHolidayScoreMultiplier = 2 +MAX_RARITY = 10 +GlobalRarityDialBase = 3.8 +FishingAngleMax = 50.0 +OVERALL_VALUE_SCALE = 15 +RARITY_VALUE_SCALE = 0.2 +WEIGHT_VALUE_SCALE = 0.05 / 16.0 +COLLECT_NO_UPDATE = 0 +COLLECT_NEW_ENTRY = 1 +COLLECT_NEW_RECORD = 2 +RodFileDict = {0: 'phase_4/models/props/pole_treebranch-mod', + 1: 'phase_4/models/props/pole_bamboo-mod', + 2: 'phase_4/models/props/pole_wood-mod', + 3: 'phase_4/models/props/pole_steel-mod', + 4: 'phase_4/models/props/pole_gold-mod'} +RodPriceDict = {0: 0, + 1: 400, + 2: 800, + 3: 1200, + 4: 2000} +TankPriceDict = {0: 0, + 40: 400, + 60: 800, + 80: 1200, + 100: 2000} +NextTank = {20: 40, + 40: 60, + 60: 80, + 80: 100} +RodRarityFactor = {0: 1.0 / (GlobalRarityDialBase * 1), + 1: 1.0 / (GlobalRarityDialBase * 0.975), + 2: 1.0 / (GlobalRarityDialBase * 0.95), + 3: 1.0 / (GlobalRarityDialBase * 0.9), + 4: 1.0 / (GlobalRarityDialBase * 0.85)} +MaxRodId = 4 +MaxTank = 100 +FishAudioFileDict = {-1: ('Clownfish.ogg', + 1, + 1.5, + 1.0), + 0: ('BalloonFish.ogg', + 1, + 0, + 1.23), + 2: ('CatFish.ogg', + 1, + 0, + 1.26), + 4: ('Clownfish.ogg', + 1, + 1.5, + 1.0), + 6: ('Frozen_Fish.ogg', + 1, + 0, + 1.0), + 8: ('Starfish.ogg', + 0, + 0, + 1.25), + 10: ('Holy_Mackerel.ogg', + 1, + 0.9, + 1.0), + 12: ('Dog_Fish.ogg', + 1, + 0, + 1.25), + 14: ('AmoreEel.ogg', + 1, + 0, + 1.0), + 16: ('Nurse_Shark.ogg', + 0, + 0, + 1.0), + 18: ('King_Crab.ogg', + 0, + 0, + 1.0), + 20: ('Moon_Fish.ogg', + 0, + 1.0, + 1.0), + 22: ('Seahorse.ogg', + 1, + 0, + 1.26), + 24: ('Pool_Shark.ogg', + 1, + 2.0, + 1.0), + 26: ('Bear_Acuda.ogg', + 1, + 0, + 1.0), + 28: ('CutThroatTrout.ogg', + 1, + 0, + 1.0), + 30: ('Piano_Tuna.ogg', + 0, + 0, + 1.0), + 32: ('PBJ_Fish.ogg', + 1, + 0, + 1.25), + 34: ('DevilRay.ogg', + 0, + 0, + 1.0)} +FishFileDict = {-1: (4, + 'clownFish-zero', + 'clownFish-swim', + 'clownFish-swim', + None, + (0.12, 0, -0.15), + 0.38, + -35, + 20), + 0: (4, + 'balloonFish-zero', + 'balloonFish-swim', + 'balloonFish-swim', + None, + (0.0, 0, 0.0), + 1.0, + 0, + 0), + 2: (4, + 'catFish-zero', + 'catFish-swim', + 'catFish-swim', + None, + (1.2, -2.0, 0.5), + 0.22, + -35, + 10), + 4: (4, + 'clownFish-zero', + 'clownFish-swim', + 'clownFish-swim', + None, + (0.12, 0, -0.15), + 0.38, + -35, + 20), + 6: (4, + 'frozenFish-zero', + 'frozenFish-swim', + 'frozenFish-swim', + None, + (0, 0, 0), + 0.5, + -35, + 20), + 8: (4, + 'starFish-zero', + 'starFish-swim', + 'starFish-swimLOOP', + None, + (0, 0, -0.38), + 0.36, + -35, + 20), + 10: (4, + 'holeyMackerel-zero', + 'holeyMackerel-swim', + 'holeyMackerel-swim', + None, + None, + 0.4, + 0, + 0), + 12: (4, + 'dogFish-zero', + 'dogFish-swim', + 'dogFish-swim', + None, + (0.8, -1.0, 0.275), + 0.33, + -38, + 10), + 14: (4, + 'amoreEel-zero', + 'amoreEel-swim', + 'amoreEel-swim', + None, + (0.425, 0, 1.15), + 0.5, + 0, + 60), + 16: (4, + 'nurseShark-zero', + 'nurseShark-swim', + 'nurseShark-swim', + None, + (0, 0, -0.15), + 0.3, + -40, + 10), + 18: (4, + 'kingCrab-zero', + 'kingCrab-swim', + 'kingCrab-swimLOOP', + None, + None, + 0.4, + 0, + 0), + 20: (4, + 'moonFish-zero', + 'moonFish-swim', + 'moonFish-swimLOOP', + None, + (-1.2, 14, -2.0), + 0.33, + 0, + -10), + 22: (4, + 'seaHorse-zero', + 'seaHorse-swim', + 'seaHorse-swim', + None, + (-0.57, 0.0, -2.1), + 0.23, + 33, + -10), + 24: (4, + 'poolShark-zero', + 'poolShark-swim', + 'poolShark-swim', + None, + (-0.45, 0, -1.8), + 0.33, + 45, + 0), + 26: (4, + 'BearAcuda-zero', + 'BearAcuda-swim', + 'BearAcuda-swim', + None, + (0.65, 0, -3.3), + 0.2, + -35, + 20), + 28: (4, + 'cutThroatTrout-zero', + 'cutThroatTrout-swim', + 'cutThroatTrout-swim', + None, + (-0.2, 0, -0.1), + 0.5, + 35, + 20), + 30: (4, + 'pianoTuna-zero', + 'pianoTuna-swim', + 'pianoTuna-swim', + None, + (0.3, 0, 0.0), + 0.6, + 40, + 30), + 32: (4, + 'PBJfish-zero', + 'PBJfish-swim', + 'PBJfish-swim', + None, + (0, 0, 0.72), + 0.31, + -35, + 10), + 34: (4, + 'devilRay-zero', + 'devilRay-swim', + 'devilRay-swim', + None, + (0, 0, 0), + 0.4, + -35, + 20)} +FISH_PER_BONUS = 10 +TrophyDict = {0: (TTLocalizer.FishTrophyNameDict[0],), + 1: (TTLocalizer.FishTrophyNameDict[1],), + 2: (TTLocalizer.FishTrophyNameDict[2],), + 3: (TTLocalizer.FishTrophyNameDict[3],), + 4: (TTLocalizer.FishTrophyNameDict[4],), + 5: (TTLocalizer.FishTrophyNameDict[5],), + 6: (TTLocalizer.FishTrophyNameDict[6],)} +WEIGHT_MIN_INDEX = 0 +WEIGHT_MAX_INDEX = 1 +RARITY_INDEX = 2 +ZONE_LIST_INDEX = 3 +Anywhere = 1 +TTG = ToontownGlobals +__fishDict = {0: ((1, + 3, + 1, + (Anywhere,)), + (1, + 1, + 4, + (TTG.ToontownCentral, Anywhere)), + (3, + 5, + 5, + (TTG.PunchlinePlace, TTG.TheBrrrgh)), + (3, + 5, + 3, + (TTG.SillyStreet, TTG.DaisyGardens)), + (1, + 5, + 2, + (TTG.LoopyLane, TTG.ToontownCentral))), + 2: ((2, + 6, + 1, + (TTG.DaisyGardens, Anywhere)), + (2, + 6, + 9, + (TTG.ElmStreet, TTG.DaisyGardens)), + (5, + 11, + 4, + (TTG.LullabyLane, TTG.BedtimeBoulevard)), + (2, + 6, + 3, + (TTG.DaisyGardens, TTG.MyEstate, TTG.OutdoorZone)), + (5, + 11, + 2, + (TTG.DonaldsDreamland, TTG.MyEstate, TTG.OutdoorZone))), + 4: ((2, + 8, + 1, + (TTG.ToontownCentral, Anywhere)), + (2, + 8, + 4, + (TTG.ToontownCentral, Anywhere)), + (2, + 8, + 2, + (TTG.ToontownCentral, Anywhere)), + (2, + 8, + 6, + (TTG.ToontownCentral, TTG.MinniesMelodyland))), + 6: ((8, + 12, + 1, + (TTG.TheBrrrgh,)),), + 8: ((1, + 5, + 1, + (Anywhere,)), + (2, + 6, + 2, + (TTG.MinniesMelodyland, Anywhere)), + (5, + 10, + 5, + (TTG.MinniesMelodyland, Anywhere)), + (1, + 5, + 7, + (TTG.MyEstate, TTG.OutdoorZone, Anywhere)), + (1, + 5, + 10, + (TTG.MyEstate, TTG.OutdoorZone, Anywhere))), + 10: ((6, + 10, + 9, + (TTG.MyEstate, TTG.OutdoorZone, Anywhere)),), + 12: ((7, + 15, + 1, + (TTG.DonaldsDock, Anywhere)), + (18, + 20, + 6, + (TTG.DonaldsDock, TTG.MyEstate, TTG.OutdoorZone)), + (1, + 5, + 5, + (TTG.DonaldsDock, TTG.MyEstate, TTG.OutdoorZone)), + (3, + 7, + 4, + (TTG.DonaldsDock, TTG.MyEstate, TTG.OutdoorZone)), + (1, + 2, + 2, + (TTG.DonaldsDock, Anywhere))), + 14: ((2, + 6, + 1, + (TTG.DaisyGardens, TTG.MyEstate, TTG.OutdoorZone, Anywhere)), (2, + 6, + 3, + (TTG.DaisyGardens, TTG.MyEstate, TTG.OutdoorZone))), + 16: ((4, + 12, + 5, + (TTG.MinniesMelodyland, Anywhere)), (4, + 12, + 7, + (TTG.BaritoneBoulevard, TTG.MinniesMelodyland)), (4, + 12, + 8, + (TTG.TenorTerrace, TTG.MinniesMelodyland))), + 18: ((2, + 4, + 3, + (TTG.DonaldsDock, Anywhere)), (5, + 8, + 7, + (TTG.TheBrrrgh,)), (4, + 6, + 8, + (TTG.LighthouseLane,))), + 20: ((4, + 6, + 1, + (TTG.DonaldsDreamland,)), + (14, + 18, + 10, + (TTG.DonaldsDreamland,)), + (6, + 10, + 8, + (TTG.LullabyLane, TTG.BedtimeBoulevard)), + (1, + 1, + 3, + (TTG.DonaldsDreamland,)), + (2, + 6, + 6, + (TTG.LullabyLane, TTG.BedtimeBoulevard)), + (10, + 14, + 4, + (TTG.DonaldsDreamland, TTG.DaisyGardens))), + 22: ((12, + 16, + 2, + (TTG.MyEstate, TTG.OutdoorZone, TTG.DaisyGardens, Anywhere)), + (14, + 18, + 3, + (TTG.MyEstate, TTG.OutdoorZone, TTG.DaisyGardens, Anywhere)), + (14, + 20, + 5, + (TTG.MyEstate, TTG.OutdoorZone, TTG.DaisyGardens)), + (14, + 20, + 7, + (TTG.MyEstate, TTG.OutdoorZone, TTG.DaisyGardens))), + 24: ((9, + 11, + 3, + (Anywhere,)), + (8, + 12, + 5, + (TTG.DaisyGardens, TTG.DonaldsDock)), + (8, + 12, + 6, + (TTG.DaisyGardens, TTG.DonaldsDock)), + (8, + 16, + 7, + (TTG.DaisyGardens, TTG.DonaldsDock))), + 26: ((10, + 18, + 2, + (TTG.TheBrrrgh,)), + (10, + 18, + 3, + (TTG.TheBrrrgh,)), + (10, + 18, + 4, + (TTG.TheBrrrgh,)), + (10, + 18, + 5, + (TTG.TheBrrrgh,)), + (12, + 20, + 6, + (TTG.TheBrrrgh,)), + (14, + 20, + 7, + (TTG.TheBrrrgh,)), + (14, + 20, + 8, + (TTG.SleetStreet, TTG.TheBrrrgh)), + (16, + 20, + 10, + (TTG.WalrusWay, TTG.TheBrrrgh))), + 28: ((2, + 10, + 2, + (TTG.DonaldsDock, Anywhere)), (4, + 10, + 6, + (TTG.BarnacleBoulevard, TTG.DonaldsDock)), (4, + 10, + 7, + (TTG.SeaweedStreet, TTG.DonaldsDock))), + 30: ((13, + 17, + 5, + (TTG.MinniesMelodyland, Anywhere)), + (16, + 20, + 10, + (TTG.AltoAvenue, TTG.MinniesMelodyland)), + (12, + 18, + 9, + (TTG.TenorTerrace, TTG.MinniesMelodyland)), + (12, + 18, + 6, + (TTG.MinniesMelodyland,)), + (12, + 18, + 7, + (TTG.MinniesMelodyland,))), + 32: ((1, + 5, + 2, + (TTG.ToontownCentral, TTG.MyEstate, TTG.OutdoorZone, Anywhere)), + (1, + 5, + 3, + (TTG.TheBrrrgh, TTG.MyEstate, TTG.OutdoorZone, Anywhere)), + (1, + 5, + 4, + (TTG.DaisyGardens, TTG.MyEstate, TTG.OutdoorZone)), + (1, + 5, + 5, + (TTG.DonaldsDreamland, TTG.MyEstate, TTG.OutdoorZone)), + (1, + 5, + 10, + (TTG.TheBrrrgh, TTG.DonaldsDreamland))), + 34: ((1, + 20, + 10, + (TTG.DonaldsDreamland, Anywhere)),)} + +def getSpecies(genus): + return __fishDict[genus] + + +def getGenera(): + return __fishDict.keys() + + +ROD_WEIGHT_MIN_INDEX = 0 +ROD_WEIGHT_MAX_INDEX = 1 +ROD_CAST_COST_INDEX = 2 +__rodDict = {0: (0, 4, 1), + 1: (0, 8, 2), + 2: (0, 12, 3), + 3: (0, 16, 4), + 4: (0, 20, 5)} + +def getNumRods(): + return len(__rodDict) + + +def getCastCost(rodId): + return __rodDict[rodId][ROD_CAST_COST_INDEX] + + +def getEffectiveRarity(rarity, offset): + return min(MAX_RARITY, rarity + offset) + + +def canBeCaughtByRod(genus, species, rodIndex): + minFishWeight, maxFishWeight = getWeightRange(genus, species) + minRodWeight, maxRodWeight = getRodWeightRange(rodIndex) + if minRodWeight <= maxFishWeight and maxRodWeight >= minFishWeight: + return 1 + else: + return 0 + + +def getRodWeightRange(rodIndex): + rodProps = __rodDict[rodIndex] + return (rodProps[ROD_WEIGHT_MIN_INDEX], rodProps[ROD_WEIGHT_MAX_INDEX]) + + +def __rollRarityDice(rodId, rNumGen): + if rNumGen is None: + diceRoll = random.random() + else: + diceRoll = rNumGen.random() + exp = RodRarityFactor[rodId] + rarity = int(ceil(10 * (1 - pow(diceRoll, exp)))) + if rarity <= 0: + rarity = 1 + return rarity + + +def getRandomWeight(genus, species, rodIndex = None, rNumGen = None): + minFishWeight, maxFishWeight = getWeightRange(genus, species) + if rodIndex is None: + minWeight = minFishWeight + maxWeight = maxFishWeight + else: + minRodWeight, maxRodWeight = getRodWeightRange(rodIndex) + minWeight = max(minFishWeight, minRodWeight) + maxWeight = min(maxFishWeight, maxRodWeight) + if rNumGen is None: + randNumA = random.random() + randNumB = random.random() + else: + randNumA = rNumGen.random() + randNumB = rNumGen.random() + randNum = (randNumA + randNumB) / 2.0 + randWeight = minWeight + (maxWeight - minWeight) * randNum + return int(round(randWeight * 16)) + + +def getRandomFishVitals(zoneId, rodId, rNumGen = None): + rarity = __rollRarityDice(rodId, rNumGen) + rodDict = __pondInfoDict.get(zoneId) + rarityDict = rodDict.get(rodId) + fishList = rarityDict.get(rarity) + if fishList: + if rNumGen is None: + genus, species = random.choice(fishList) + else: + genus, species = rNumGen.choice(fishList) + weight = getRandomWeight(genus, species, rodId, rNumGen) + return (1, + genus, + species, + weight) + else: + return (0, 0, 0, 0) + return + + +def getWeightRange(genus, species): + fishInfo = __fishDict[genus][species] + return (fishInfo[WEIGHT_MIN_INDEX], fishInfo[WEIGHT_MAX_INDEX]) + + +def getRarity(genus, species): + return __fishDict[genus][species][RARITY_INDEX] + + +def getValue(genus, species, weight): + rarity = getRarity(genus, species) + rarityValue = pow(RARITY_VALUE_SCALE * rarity, 1.5) + weightValue = pow(WEIGHT_VALUE_SCALE * weight, 1.1) + value = OVERALL_VALUE_SCALE * (rarityValue + weightValue) + finalValue = int(ceil(value)) + base = getBase() + newsManager = base.cr.newsManager if hasattr(base, 'cr') else simbase.air.newsManager + + if newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY) or newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY_MONTH): + finalValue *= JellybeanFishingHolidayScoreMultiplier + + return finalValue + +__totalNumFish = 0 +__emptyRodDict = {} +for rodIndex in __rodDict: + __emptyRodDict[rodIndex] = {} + +__anywhereDict = copy.deepcopy(__emptyRodDict) +__pondInfoDict = {} +for genus, speciesList in __fishDict.items(): + for species in xrange(len(speciesList)): + __totalNumFish += 1 + speciesDesc = speciesList[species] + rarity = speciesDesc[RARITY_INDEX] + zoneList = speciesDesc[ZONE_LIST_INDEX] + for zoneIndex in xrange(len(zoneList)): + zone = zoneList[zoneIndex] + effectiveRarity = getEffectiveRarity(rarity, zoneIndex) + if zone == Anywhere: + for rodIndex, rarityDict in __anywhereDict.items(): + if canBeCaughtByRod(genus, species, rodIndex): + fishList = rarityDict.setdefault(effectiveRarity, []) + fishList.append((genus, species)) + + else: + pondZones = [zone] + subZones = ToontownGlobals.HoodHierarchy.get(zone) + if subZones: + pondZones.extend(subZones) + for pondZone in pondZones: + if pondZone in __pondInfoDict: + rodDict = __pondInfoDict[pondZone] + else: + rodDict = copy.deepcopy(__emptyRodDict) + __pondInfoDict[pondZone] = rodDict + for rodIndex, rarityDict in rodDict.items(): + if canBeCaughtByRod(genus, species, rodIndex): + fishList = rarityDict.setdefault(effectiveRarity, []) + fishList.append((genus, species)) + +for zone, rodDict in __pondInfoDict.items(): + for rodIndex, anywhereRarityDict in __anywhereDict.items(): + for rarity, anywhereFishList in anywhereRarityDict.items(): + rarityDict = rodDict[rodIndex] + fishList = rarityDict.setdefault(rarity, []) + fishList.extend(anywhereFishList) + +def getPondDict(zoneId): + print __pondInfoDict[zoneId] + + +def getTotalNumFish(): + return __totalNumFish + + +def testRarity(rodId = 0, numIter = 100000): + d = {1: 0, + 2: 0, + 3: 0, + 4: 0, + 5: 0, + 6: 0, + 7: 0, + 8: 0, + 9: 0, + 10: 0} + for i in xrange(numIter): + v = __rollRarityDice(rodId) + d[v] += 1 + + for rarity, count in d.items(): + percentage = count / float(numIter) * 100 + d[rarity] = percentage + + print d + + +def getRandomFish(): + genus = random.choice(__fishDict.keys()) + species = random.randint(0, len(__fishDict[genus]) - 1) + return (genus, species) + + +def getPondInfo(): + return __pondInfoDict + + +def getSimplePondInfo(): + info = {} + for pondId, pondInfo in __pondInfoDict.items(): + pondFishList = [] + for rodId, rodInfo in pondInfo.items(): + for rarity, fishList in rodInfo.items(): + for fish in fishList: + if fish not in pondFishList: + pondFishList.append(fish) + + pondFishList.sort() + info[pondId] = pondFishList + + return info + + +def getPondGeneraList(pondId): + tmpList = [] + generaList = [] + pondInfo = getSimplePondInfo() + for fish in pondInfo[pondId]: + if fish[0] not in tmpList: + tmpList.append(fish[0]) + generaList.append(fish) + + return generaList + + +def printNumGeneraPerPond(): + pondInfo = getSimplePondInfo() + for pondId, fishList in pondInfo.items(): + generaList = [] + for fish in fishList: + if fish[0] not in generaList: + generaList.append(fish[0]) + + print 'Pond %s has %s Genera' % (pondId, len(generaList)) + + +def generateFishingReport(numCasts = 10000, hitRate = 0.8): + totalPondMoney = {} + totalRodMoney = {} + totalPondBaitCost = {} + for pond in __pondInfoDict: + totalPondMoney[pond] = 0 + totalPondBaitCost[pond] = 0 + for rod in xrange(MaxRodId + 1): + totalRodMoney.setdefault(rod, 0) + baitCost = getCastCost(rod) + for cast in xrange(numCasts): + totalPondBaitCost[pond] += baitCost + if random.random() > hitRate: + continue + rand = random.random() * 100.0 + for cutoff in SortedProbabilityCutoffs: + if rand <= cutoff: + itemType = ProbabilityDict[cutoff] + break + + if itemType == FishItem: + success, genus, species, weight = getRandomFishVitals(pond, rod) + if success: + value = getValue(genus, species, weight) + totalPondMoney[pond] += value + totalRodMoney[rod] += value + elif itemType == JellybeanItem: + value = Rod2JellybeanDict[rod] + totalPondMoney[pond] += value + totalRodMoney[rod] += value + + numPonds = len(totalPondMoney) + for pond, money in totalPondMoney.items(): + baitCost = 0 + for rod in xrange(MaxRodId + 1): + baitCost += getCastCost(rod) + + totalCastCost = baitCost * numCasts + print ('pond: %s totalMoney: %s profit: %s perCast: %s' % (pond, + money, + money - totalCastCost, + (money - totalCastCost) / float(numCasts * (MaxRodId + 1))),) + + for rod, money in totalRodMoney.items(): + baitCost = getCastCost(rod) + totalCastCost = baitCost * (numCasts * numPonds) + print ('rod: %s totalMoney: %s castCost: %s profit: %s perCast: %s' % (rod, + money, + totalCastCost, + money - totalCastCost, + (money - totalCastCost) / float(numCasts * numPonds)),) diff --git a/toontown/fishing/FishPanel.py b/toontown/fishing/FishPanel.py new file mode 100755 index 00000000..b5c32a1c --- /dev/null +++ b/toontown/fishing/FishPanel.py @@ -0,0 +1,114 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer +from direct.interval.IntervalGlobal import * +import FishGlobals +import FishPhoto + +class FishPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FishPanel') + + def __init__(self, fish = None, parent = aspect2d, doneEvent = None, **kw): + optiondefs = (('relief', None, None), + ('state', DGG.DISABLED, None), + ('image', DGG.getDefaultDialogGeom(), None), + ('image_color', ToontownGlobals.GlobalDialogColor, None), + ('image_scale', (0.65, 1, 0.85), None), + ('text', '', None), + ('text_scale', 0.06, None), + ('text_fg', (0, 0, 0, 1), None), + ('text_pos', (0, 0.35, 0), None), + ('text_font', ToontownGlobals.getInterfaceFont(), None), + ('text_wordwrap', 13.5, None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.initialiseoptions(FishPanel) + self.doneEvent = doneEvent + self.fish = fish + self.parent = parent + self.photo = None + return + + def destroy(self): + if self.photo: + self.photo.destroy() + self.photo = None + self.fish = None + DirectFrame.destroy(self) + self.parent = None + return + + def load(self): + self.weight = DirectLabel(parent=self, pos=(0, 0, -0.28), relief=None, state=DGG.NORMAL, text='', text_scale=0.05, text_fg=(0, 0, 0, 1), text_pos=(0, 0.0, 0), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=10.5) + self.value = DirectLabel(parent=self, pos=(0, 0, -0.35), relief=None, state=DGG.NORMAL, text='', text_scale=0.05, text_fg=(0, 0, 0, 1), text_pos=(0, 0, 0), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=10.5) + self.mystery = DirectLabel(parent=self, pos=(-0.025, 0, -0.055), relief=None, state=DGG.NORMAL, text='?', text_scale=0.25, text_fg=(0, 0, 0, 1), text_pos=(0, 0, 0), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=10.5) + self.extraLabel = DirectLabel(parent=self, relief=None, state=DGG.NORMAL, text='', text_fg=(0.2, 0.8, 0.4, 1), text_font=ToontownGlobals.getSignFont(), text_scale=0.08, pos=(0, 0, 0.26)) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.cancel = DirectButton(parent=self, pos=(0.275, 0, -0.375), relief=None, state=DGG.NORMAL, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(0.6, 1, 0.6), command=self.handleCancel) + buttons.removeNode() + self.photo = FishPhoto.FishPhoto(parent=self) + self.update(self.fish) + return + + def update(self, fish): + self.fish = fish + if self.fish == None: + return + self['text'] = self.fish.getSpeciesName() + weight = self.fish.getWeight() + conv = TTLocalizer.FishPageWeightConversion + large = weight / conv + if large == 1: + largeStr = TTLocalizer.FishPageWeightLargeS % large + else: + largeStr = TTLocalizer.FishPageWeightLargeP % large + small = weight % conv + if small == 1: + smallStr = TTLocalizer.FishPageWeightSmallS % small + else: + smallStr = TTLocalizer.FishPageWeightSmallP % small + self.weight['text'] = TTLocalizer.FishPageWeightStr + largeStr + smallStr + value = self.fish.getValue() + if value == 1: + self.value['text'] = TTLocalizer.FishPageValueS % value + else: + self.value['text'] = TTLocalizer.FishPageValueP % value + self.photo.update(fish) + return + + def setSwimBounds(self, *bounds): + self.swimBounds = bounds + + def setSwimColor(self, *colors): + self.swimColor = colors + + def handleCancel(self): + self.hide() + if self.doneEvent: + messenger.send(self.doneEvent) + + def show(self, code = FishGlobals.FishItem): + messenger.send('wakeup') + apply(self.photo.setSwimBounds, self.swimBounds) + apply(self.photo.setSwimColor, self.swimColor) + if code == FishGlobals.FishItem: + self.extraLabel.hide() + elif code == FishGlobals.FishItemNewEntry: + self.extraLabel.show() + self.extraLabel['text_fg'] = (0.2, 0.8, 0.4, 1) + self.extraLabel['text'] = TTLocalizer.FishingNewEntry + self.extraLabel['text_scale'] = TTLocalizer.FPnewEntry + self.extraLabel.setPos(0, 0, 0.26) + elif code == FishGlobals.FishItemNewRecord: + self.extraLabel.show() + self.extraLabel['text_fg'] = (1, .5, 0, 1) + self.extraLabel['text'] = TTLocalizer.FishingNewRecord + self.extraLabel['text_scale'] = TTLocalizer.FPnewRecord + self.photo.show() + DirectFrame.show(self) + + def hide(self): + self.photo.hide() + DirectFrame.hide(self) diff --git a/toontown/fishing/FishPhoto.py b/toontown/fishing/FishPhoto.py new file mode 100755 index 00000000..f72e8393 --- /dev/null +++ b/toontown/fishing/FishPhoto.py @@ -0,0 +1,183 @@ +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * +from direct.interval.IntervalGlobal import * +import FishGlobals + +class DirectRegion(NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('DirectRegion') + + def __init__(self, parent = aspect2d): + NodePath.__init__(self) + self.assign(parent.attachNewNode('DirectRegion')) + + def destroy(self): + self.unload() + + def setBounds(self, *bounds): + self.bounds = bounds + + def setColor(self, *colors): + self.color = colors + + def show(self): + pass + + def hide(self): + pass + + def load(self): + if not hasattr(self, 'cRender'): + self.cRender = NodePath('fishSwimRender') + self.fishSwimCamera = self.cRender.attachNewNode('fishSwimCamera') + self.cCamNode = Camera('fishSwimCam') + self.cLens = PerspectiveLens() + self.cLens.setFov(40, 40) + self.cLens.setNear(0.1) + self.cLens.setFar(100.0) + self.cCamNode.setLens(self.cLens) + self.cCamNode.setScene(self.cRender) + self.fishSwimCam = self.fishSwimCamera.attachNewNode(self.cCamNode) + cm = CardMaker('displayRegionCard') + apply(cm.setFrame, self.bounds) + self.card = card = self.attachNewNode(cm.generate()) + apply(card.setColor, self.color) + newBounds = card.getTightBounds() + ll = render2d.getRelativePoint(card, newBounds[0]) + ur = render2d.getRelativePoint(card, newBounds[1]) + newBounds = [ll.getX(), + ur.getX(), + ll.getZ(), + ur.getZ()] + newBounds = map(lambda x: max(0.0, min(1.0, (x + 1.0) / 2.0)), newBounds) + self.cDr = base.win.makeDisplayRegion(*newBounds) + self.cDr.setSort(10) + self.cDr.setClearColor(card.getColor()) + self.cDr.setClearDepthActive(1) + self.cDr.setClearColorActive(1) + self.cDr.setCamera(self.fishSwimCam) + return self.cRender + + def unload(self): + if hasattr(self, 'cRender'): + base.win.removeDisplayRegion(self.cDr) + del self.cRender + del self.fishSwimCamera + del self.cCamNode + del self.cLens + del self.fishSwimCam + del self.cDr + + +class FishPhoto(NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('FishPhoto') + + def __init__(self, fish = None, parent = aspect2d): + NodePath.__init__(self) + self.assign(parent.attachNewNode('FishPhoto')) + self.fish = fish + self.actor = None + self.sound = None + self.soundTrack = None + self.track = None + self.fishFrame = None + return + + def destroy(self): + self.hide() + if hasattr(self, 'background'): + del self.background + self.fish = None + del self.soundTrack + del self.track + return + + def update(self, fish): + self.fish = fish + + def setSwimBounds(self, *bounds): + self.swimBounds = bounds + + def setSwimColor(self, *colors): + self.swimColor = colors + + def load(self): + pass + + def makeFishFrame(self, actor): + actor.setDepthTest(1) + actor.setDepthWrite(1) + if not hasattr(self, 'fishDisplayRegion'): + self.fishDisplayRegion = DirectRegion(parent=self) + apply(self.fishDisplayRegion.setBounds, self.swimBounds) + apply(self.fishDisplayRegion.setColor, self.swimColor) + frame = self.fishDisplayRegion.load() + pitch = frame.attachNewNode('pitch') + rotate = pitch.attachNewNode('rotate') + scale = rotate.attachNewNode('scale') + actor.reparentTo(scale) + bMin, bMax = actor.getTightBounds() + center = (bMin + bMax) / 2.0 + actor.setPos(-center[0], -center[1], -center[2]) + genus = self.fish.getGenus() + fishInfo = FishGlobals.FishFileDict.get(genus, FishGlobals.FishFileDict[-1]) + fishPos = fishInfo[5] + if fishPos: + actor.setPos(fishPos[0], fishPos[1], fishPos[2]) + scale.setScale(fishInfo[6]) + rotate.setH(fishInfo[7]) + pitch.setP(fishInfo[8]) + pitch.setY(2) + return frame + + def show(self, showBackground = 0): + messenger.send('wakeup') + if self.fishFrame: + self.actor.cleanup() + if hasattr(self, 'fishDisplayRegion'): + self.fishDisplayRegion.unload() + self.hide() + self.actor = self.fish.getActor() + self.actor.setTwoSided(1) + self.fishFrame = self.makeFishFrame(self.actor) + if showBackground: + if not hasattr(self, 'background'): + background = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + background = background.find('**/Fish_BG') + self.background = background + self.background.setPos(0, 15, 0) + self.background.setScale(11) + self.background.reparentTo(self.fishFrame) + self.sound, loop, delay, playRate = self.fish.getSound() + if playRate is not None: + self.actor.setPlayRate(playRate, 'intro') + self.actor.setPlayRate(playRate, 'swim') + introDuration = self.actor.getDuration('intro') + track = Parallel(Sequence(Func(self.actor.play, 'intro'), Wait(introDuration), Func(self.actor.loop, 'swim'))) + if self.sound: + soundTrack = Sequence(Wait(delay), Func(self.sound.play)) + if loop: + duration = max(introDuration, self.sound.length()) + soundTrack.append(Wait(duration - delay)) + track.append(Func(soundTrack.loop)) + self.soundTrack = soundTrack + else: + track.append(soundTrack) + self.track = track + self.track.start() + return + + def hide(self): + if hasattr(self, 'fishDisplayRegion'): + self.fishDisplayRegion.unload() + if self.actor: + self.actor.stop() + if self.sound: + self.sound.stop() + self.sound = None + if self.soundTrack: + self.soundTrack.pause() + self.soundTrack = None + if self.track: + self.track.pause() + self.track = None + return diff --git a/toontown/fishing/FishPicker.py b/toontown/fishing/FishPicker.py new file mode 100755 index 00000000..87381067 --- /dev/null +++ b/toontown/fishing/FishPicker.py @@ -0,0 +1,120 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer +import FishPanel + +class FishPicker(DirectScrolledList): + notify = DirectNotifyGlobal.directNotify.newCategory('FishPicker') + + def __init__(self, parent = aspect2d, **kw): + self.fishList = [] + self.parent = parent + self.shown = 0 + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + optiondefs = (('parent', self.parent, None), + ('relief', None, None), + ('incButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('incButton_relief', None, None), + ('incButton_scale', (1.6, 1.6, -1.6), None), + ('incButton_pos', (0.16, 0, -0.47), None), + ('incButton_image3_color', Vec4(0.7, 0.7, 0.7, 0.75), None), + ('decButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('decButton_relief', None, None), + ('decButton_scale', (1.6, 1.6, 1.6), None), + ('decButton_pos', (0.16, 0, 0.09), None), + ('decButton_image3_color', Vec4(0.7, 0.7, 0.7, 0.75), None), + ('itemFrame_pos', (-0.025, 0, 0), None), + ('itemFrame_scale', 0.54, None), + ('itemFrame_relief', None, None), + ('itemFrame_frameSize', (-0.05, + 0.75, + -0.75, + 0.05), None), + ('numItemsVisible', 10, None), + ('items', [], None)) + self.defineoptions(kw, optiondefs) + DirectScrolledList.__init__(self, parent) + self.initialiseoptions(FishPicker) + self.fishGui = loader.loadModel('phase_3.5/models/gui/fishingBook').find('**/bucket') + self.fishGui.find('**/fram1').removeNode() + self.fishGui.find('**/bubble').removeNode() + self.fishGui.reparentTo(self, -1) + self.fishGui.setPos(0.63, 0.1, -0.1) + self.fishGui.setScale(0.035) + self.info = DirectLabel(parent=self, relief=None, text='', text_scale=0.055, pos=(0.18, 0, -0.67)) + self.fishPanel = FishPanel.FishPanel(parent=self) + self.fishPanel.setSwimBounds(-0.3, 0.3, -0.235, 0.25) + self.fishPanel.setSwimColor(1.0, 1.0, 0.74901, 1.0) + gui.removeNode() + return None + + def destroy(self): + DirectScrolledList.destroy(self) + self.parent = None + self.fishList = [] + self.fishPanel = None + return + + def hideFishPanel(self): + self.fishPanel.hide() + + def hide(self): + if not hasattr(self, 'loaded'): + return + self.hideFishPanel() + DirectScrolledList.hide(self) + self.shown = 0 + + def show(self): + if not hasattr(self, 'loaded'): + self.load() + self.updatePanel() + DirectScrolledList.show(self) + self.shown = 1 + + def load(self): + self.loaded = 1 + self.fishPanel.load() + self.fishPanel.setPos(1.05, 0, 0.1) + self.fishPanel.setScale(0.9) + + def update(self, newFishes): + for fish, fishButton in self.fishList[:]: + self.removeItem(fishButton) + fishButton.destroy() + self.fishList.remove([fish, fishButton]) + + for fish in newFishes: + fishButton = self.makeFishButton(fish) + self.addItem(fishButton) + self.fishList.append([fish, fishButton]) + + value = 0 + for fish in newFishes: + value += fish.getValue() + + maxFish = base.localAvatar.getMaxFishTank() + self.info['text'] = TTLocalizer.FishPickerTotalValue % (len(newFishes), maxFish, value) + if self.shown: + self.updatePanel() + + def updatePanel(self): + if len(self.fishList) >= 1: + self.showFishPanel(self.fishList[0][0]) + else: + self.hideFishPanel() + + def makeFishButton(self, fish): + return DirectScrolledListItem(parent=self, relief=None, text=fish.getSpeciesName(), text_scale=0.07, text_align=TextNode.ALeft, text1_fg=Vec4(1, 1, 0, 1), text2_fg=Vec4(0.5, 0.9, 1, 1), text3_fg=Vec4(0.4, 0.8, 0.4, 1), command=self.showFishPanel, extraArgs=[fish]) + + def showFishPanel(self, fish): + self.fishPanel.update(fish) + self.fishPanel.show() diff --git a/toontown/fishing/FishSellGUI.py b/toontown/fishing/FishSellGUI.py new file mode 100755 index 00000000..91bea82e --- /dev/null +++ b/toontown/fishing/FishSellGUI.py @@ -0,0 +1,57 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.task import Task +import FishBase +import FishPicker + +class FishSellGUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FishGui') + + def __init__(self, doneEvent): + DirectFrame.__init__(self, relief=None, state='normal', geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(2.0, 1, 1.5), frameSize=(-1, 1, -1, 1), pos=(0, 0, 0), text='', text_wordwrap=26, text_scale=0.06, text_pos=(0, 0.65)) + self.initialiseoptions(FishSellGUI) + self.doneEvent = doneEvent + self.picker = FishPicker.FishPicker(self) + self.picker.load() + self.picker.setPos(-0.59, 0, 0.03) + self.picker.setScale(0.93) + newTankFish = base.localAvatar.fishTank.getFish() + self.picker.update(newTankFish) + self.picker.show() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(0.3, 0, -0.58), text=TTLocalizer.FishGuiCancel, text_scale=TTLocalizer.FSGUIcancelButton, text_pos=(0, -0.1), command=self.__cancel) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, pos=(0.6, 0, -0.58), text=TTLocalizer.FishGuiOk, text_scale=TTLocalizer.FSGUIokButton, text_pos=(0, -0.1), command=self.__sellFish) + self.rewardDoubledJellybeanLabel = DirectLabel(text='', text_fg=(1.0, 0.125, 0.125, 1.0), relief=None, pos=(0.45, 0, -0.48), scale=0.07) + buttons.removeNode() + self.__updateFishValue() + return + + def destroy(self): + DirectFrame.destroy(self) + self.rewardDoubledJellybeanLabel.removeNode() + del self.rewardDoubledJellybeanLabel + + def __cancel(self): + messenger.send(self.doneEvent, [0]) + + def __sellFish(self): + messenger.send(self.doneEvent, [1]) + + def __updateFishValue(self): + doubledJellybean = '' + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY_MONTH): + doubledJellybean = TTLocalizer.PartyRewardDoubledJellybean + self.rewardDoubledJellybeanLabel['text'] = doubledJellybean + self.rewardDoubledJellybeanLabel.setText() + fishTank = base.localAvatar.getFishTank() + num = len(fishTank) + value = fishTank.getTotalValue() + self['text'] = TTLocalizer.FishTankValue % {'name': base.localAvatar.getName(), + 'num': num, + 'value': value} + self.setText() diff --git a/toontown/fishing/FishTank.py b/toontown/fishing/FishTank.py new file mode 100755 index 00000000..454f5861 --- /dev/null +++ b/toontown/fishing/FishTank.py @@ -0,0 +1,82 @@ +import FishBase +import FishGlobals + +class FishTank: + + def __init__(self): + self.fishList = [] + + def __len__(self): + return len(self.fishList) + + def getFish(self): + return self.fishList + + def makeFromNetLists(self, genusList, speciesList, weightList): + self.fishList = [] + for genus, species, weight in zip(genusList, speciesList, weightList): + self.fishList.append(FishBase.FishBase(genus, species, weight)) + + def getNetLists(self): + genusList = [] + speciesList = [] + weightList = [] + for fish in self.fishList: + genusList.append(fish.getGenus()) + speciesList.append(fish.getSpecies()) + weightList.append(fish.getWeight()) + + return [genusList, speciesList, weightList] + + def hasFish(self, genus, species): + for fish in self.fishList: + if fish.getGenus() == genus and fish.getSpecies() == species: + return 1 + + return 0 + + def hasBiggerFish(self, genus, species, weight): + for fish in self.fishList: + if fish.getGenus() == genus and fish.getSpecies() == species and fish.getWeight() >= weight: + return 1 + + return 0 + + def addFish(self, fish): + self.fishList.append(fish) + return 1 + + def removeFishAtIndex(self, index): + if index >= len(self.fishList): + return 0 + else: + del self.fishList[i] + return 1 + + def generateRandomTank(self): + import random + numFish = random.randint(1, 20) + self.fishList = [] + for i in xrange(numFish): + genus, species = FishGlobals.getRandomFish() + weight = FishGlobals.getRandomWeight(genus, species) + fish = FishBase.FishBase(genus, species, weight) + self.addFish(fish) + + def getTotalValue(self): + value = 0 + for fish in self.fishList: + value += fish.getValue() + + return value + + def __str__(self): + numFish = len(self.fishList) + value = 0 + txt = 'Fish Tank (%s fish):' % numFish + for fish in self.fishList: + txt += '\n' + str(fish) + value += fish.getValue() + + txt += '\nTotal value: %s' % value + return txt diff --git a/toontown/fishing/FishingTargetGlobals.py b/toontown/fishing/FishingTargetGlobals.py new file mode 100755 index 00000000..044d4dd2 --- /dev/null +++ b/toontown/fishing/FishingTargetGlobals.py @@ -0,0 +1,197 @@ +from toontown.toonbase import ToontownGlobals +OFF = 0 +MOVING = 1 +StepTime = 5.0 +MinimumHunger = 1.0 +NUM_TARGETS_INDEX = 0 +POS_START_INDEX = 1 +POS_END_INDEX = 4 +RADIUS_INDEX = 4 +WATER_LEVEL_INDEX = 5 +__targetInfoDict = {ToontownGlobals.ToontownCentral: (2, + -81, + 31, + -4.8, + 14, + -1.4), + ToontownGlobals.SillyStreet: (2, + 20, + -664, + -1.4, + 14, + -1.4 - 0.438), + ToontownGlobals.LoopyLane: (2, + -234, + 175, + -1.4, + 14, + -1.4 - 0.462), + ToontownGlobals.PunchlinePlace: (2, + 529, + -70, + -1.4, + 13, + -1.4 - 0.486), + ToontownGlobals.DonaldsDock: (2, + -17, + 130, + 1.73, + 15, + 1.73 - 3.615), + ToontownGlobals.BarnacleBoulevard: (2, + 381, + -350, + -2, + 14, + -2 - 0.482), + ToontownGlobals.SeaweedStreet: (2, + -395, + -226, + -2, + 14, + -2 - 0.482), + ToontownGlobals.LighthouseLane: (2, + 350, + 100, + -2, + 14, + -2 - 0.482), + ToontownGlobals.DaisyGardens: (2, + 50, + 47, + -1.48, + 13, + -1.48 - 0.345), + ToontownGlobals.ElmStreet: (2, + 149, + 44, + -1.43, + 13, + -1.43 - 0.618), + ToontownGlobals.MapleStreet: (2, + 176, + 100, + -1.43, + 13, + -1.43 - 0.618), + ToontownGlobals.OakStreet: (2, + 134, + -70.5, + -1.5, + 13, + -1.5 - 0.377), + ToontownGlobals.MinniesMelodyland: (2, + -0.2, + -20.2, + -14.65, + 14, + -14.65 - -12), + ToontownGlobals.AltoAvenue: (2, + -580, + -90, + -0.87, + 14, + -0.87 - 1.844), + ToontownGlobals.BaritoneBoulevard: (2, + -214, + 250, + -0.87, + 14, + -0.87 - 1.844), + ToontownGlobals.TenorTerrace: (2, + 715, + -15, + -0.87, + 14, + -0.87 - 1.844), + ToontownGlobals.TheBrrrgh: (2, + -58, + -26, + 1.7, + 10, + -0.8), + ToontownGlobals.WalrusWay: (2, + 460, + 29, + -2, + 13, + -2 - 0.4), + ToontownGlobals.SleetStreet: (2, + 340, + 480, + -2, + 13, + -2 - 0.4), + ToontownGlobals.PolarPlace: (2, + 45.5, + 90.86, + -2, + 13, + -2 - 0.4), + ToontownGlobals.DonaldsDreamland: (2, + 159, + 0.2, + -17.1, + 14, + -17.1 - -14.6), + ToontownGlobals.LullabyLane: (2, + 118, + -185, + -2.1, + 14, + -2.1 - 0.378), + ToontownGlobals.PajamaPlace: (2, + 241, + -348, + -2.1, + 14, + -2.1 - 0.378), + ToontownGlobals.BedtimeBoulevard: (2, + 378, + -355, + -2.04, + 14, + -2 - 0.483), + ToontownGlobals.MyEstate: (5, + 30, + -126, + -0.3, + 16, + -0.83), + ToontownGlobals.OutdoorZone: (3, + -130.8, + -15.5, + -0.025, + 15, + -0.5)} + +def getNumTargets(zone): + info = __targetInfoDict.get(zone) + if info: + return info[NUM_TARGETS_INDEX] + else: + return 2 + + +def getTargetCenter(zone): + info = __targetInfoDict.get(zone) + if info: + return info[POS_START_INDEX:POS_END_INDEX] + else: + return (0, 0, 0) + + +def getTargetRadius(zone): + info = __targetInfoDict.get(zone) + if info: + return info[RADIUS_INDEX] + else: + return 10 + + +def getWaterLevel(zone): + info = __targetInfoDict.get(zone) + if info: + return info[WATER_LEVEL_INDEX] + else: + return 0 diff --git a/toontown/fishing/FourCornerBingo.py b/toontown/fishing/FourCornerBingo.py new file mode 100755 index 00000000..1c162e97 --- /dev/null +++ b/toontown/fishing/FourCornerBingo.py @@ -0,0 +1,35 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.fishing import BingoGlobals +from toontown.fishing import BingoCardBase + +class FourCornerBingo(BingoCardBase.BingoCardBase): + notify = DirectNotifyGlobal.directNotify.newCategory('FourCornerBingo') + corners = [0, + BingoGlobals.CARD_ROWS - 1, + BingoGlobals.CARD_COLS * (BingoGlobals.CARD_ROWS - 1), + BingoGlobals.CARD_COLS * BingoGlobals.CARD_ROWS - 1] + + def __init__(self, cardSize = BingoGlobals.CARD_SIZE, rowSize = BingoGlobals.CARD_ROWS, colSize = BingoGlobals.CARD_COLS): + BingoCardBase.BingoCardBase.__init__(self, cardSize, rowSize, colSize) + self.gameType = BingoGlobals.FOURCORNER_CARD + + def checkForWin(self, id): + corners = self.corners + if self.cellCheck(corners[0]) and self.cellCheck(corners[1]) and self.cellCheck(corners[2]) and self.cellCheck(corners[3]): + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE + + def checkForColor(self, id): + topLeft, topRight, bottomLeft, bottomRight = (0, 0, 0, 0) + if id == self.corners[0]: + topLeft = 1 + elif id == self.corners[1]: + topRight = 1 + elif id == self.corners[2]: + bottomLeft = 1 + elif id == self.corners[3]: + bottomRight = 1 + return topLeft or topRight or bottomLeft or bottomRight + + def checkForBingo(self): + return self.checkForWin(0) diff --git a/toontown/fishing/GenusPanel.py b/toontown/fishing/GenusPanel.py new file mode 100755 index 00000000..db5cf8b9 --- /dev/null +++ b/toontown/fishing/GenusPanel.py @@ -0,0 +1,91 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer +import FishBase +import FishGlobals +import FishPhoto + +class GenusPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('GenusPanel') + + def __init__(self, genus = None, itemIndex = 0, *extraArgs): + fishingGui = loader.loadModel('phase_3.5/models/gui/fishingBook') + albumGui = fishingGui.find('**/photo_frame1').copyTo(hidden) + albumGui.find('**/picture_frame').reparentTo(albumGui, -1) + albumGui.find('**/arrows').removeNode() + optiondefs = (('relief', None, None), + ('state', DGG.NORMAL, None), + ('image', albumGui, None), + ('image_scale', (0.025, 0.025, 0.025), None), + ('image_pos', (0, 1, 0), None), + ('text', TTLocalizer.UnknownFish, None), + ('text_scale', 0.065, None), + ('text_fg', (0.2, 0.1, 0.0, 1), None), + ('text_pos', (-0.5, -0.34), None), + ('text_font', ToontownGlobals.getInterfaceFont(), None), + ('text_wordwrap', 13.5, None), + ('text_align', TextNode.ALeft, None)) + self.defineoptions({}, optiondefs) + DirectFrame.__init__(self) + self.initialiseoptions(GenusPanel) + self.fishPanel = None + self.genus = None + self.setGenus(int(genus)) + self.setScale(1.2) + albumGui.removeNode() + return + + def destroy(self): + if self.fishPanel: + self.fishPanel.destroy() + del self.fishPanel + DirectFrame.destroy(self) + + def load(self): + pass + + def setGenus(self, genus): + if self.genus == genus: + return + self.genus = genus + if self.genus != None: + if self.fishPanel: + self.fishPanel.destroy() + f = FishBase.FishBase(self.genus, 0, 0) + self.fishPanel = FishPhoto.FishPhoto(fish=f, parent=self) + self.fishPanel.setPos(-0.23, 1, -0.01) + self.fishPanel.setSwimBounds(-0.2461, 0.2367, -0.207, 0.2664) + self.fishPanel.setSwimColor(0.47, 1.0, 0.99, 1.0) + speciesList = FishGlobals.getSpecies(self.genus) + self.speciesLabels = [] + offset = 0.075 + startPos = len(speciesList) / 2 * offset + if not len(speciesList) % 2: + startPos -= offset / 2 + for species in xrange(len(speciesList)): + label = DirectLabel(parent=self, relief=None, state=DGG.NORMAL, pos=(0.06, 0, startPos - species * offset), text=TTLocalizer.UnknownFish, text_fg=(0.2, 0.1, 0.0, 1), text_scale=TTLocalizer.GPgenus, text_align=TextNode.ALeft, text_font=ToontownGlobals.getInterfaceFont()) + self.speciesLabels.append(label) + + return + + def show(self): + self.update() + DirectFrame.show(self) + + def hide(self): + if self.fishPanel is not None: + self.fishPanel.hide() + DirectFrame.hide(self) + return + + def update(self): + if base.localAvatar.fishCollection.hasGenus(self.genus) and self.fishPanel is not None: + self.fishPanel.show(showBackground=1) + self['text'] = TTLocalizer.FishGenusNames[self.genus] + for species in xrange(len(FishGlobals.getSpecies(self.genus))): + if base.localAvatar.fishCollection.hasFish(self.genus, species): + self.speciesLabels[species]['text'] = TTLocalizer.FishSpeciesNames[self.genus][species] + + return diff --git a/toontown/fishing/NormalBingo.py b/toontown/fishing/NormalBingo.py new file mode 100755 index 00000000..f2b7cf74 --- /dev/null +++ b/toontown/fishing/NormalBingo.py @@ -0,0 +1,37 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.fishing import BingoGlobals +from toontown.fishing import BingoCardBase + +class NormalBingo(BingoCardBase.BingoCardBase): + notify = DirectNotifyGlobal.directNotify.newCategory('NormalBingo') + + def __init__(self, cardSize = BingoGlobals.CARD_SIZE, rowSize = BingoGlobals.CARD_ROWS, colSize = BingoGlobals.CARD_COLS): + BingoCardBase.BingoCardBase.__init__(self, cardSize, rowSize, colSize) + self.gameType = BingoGlobals.NORMAL_CARD + + def checkForWin(self, id): + rowId = int(id / BingoGlobals.CARD_ROWS) + colId = id % BingoGlobals.CARD_COLS + rowResult = self.rowCheck(rowId) + colResult = self.colCheck(colId) + fDiagResult = self.fDiagCheck(id) + bDiagResult = self.bDiagCheck(id) + if rowResult or colResult or fDiagResult or bDiagResult: + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE + + def checkForColor(self, id): + return 1 + + def checkForBingo(self): + id = self.cardSize / 2 + if self.checkForWin(id): + return BingoGlobals.WIN + for i in xrange(BingoGlobals.CARD_ROWS): + if i != BingoGlobals.CARD_ROWS / 2: + rowResult = self.rowCheck(i) + colResult = self.colCheck(i) + if rowResult | colResult: + return BingoGlobals.WIN + + return BingoGlobals.NO_UPDATE diff --git a/toontown/fishing/ThreewayBingo.py b/toontown/fishing/ThreewayBingo.py new file mode 100755 index 00000000..515f98fe --- /dev/null +++ b/toontown/fishing/ThreewayBingo.py @@ -0,0 +1,35 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.fishing import BingoGlobals +from toontown.fishing import BingoCardBase + +class ThreewayBingo(BingoCardBase.BingoCardBase): + notify = DirectNotifyGlobal.directNotify.newCategory('ThreewayBingo') + + def __init__(self, cardSize = BingoGlobals.CARD_SIZE, rowSize = BingoGlobals.CARD_ROWS, colSize = BingoGlobals.CARD_COLS): + BingoCardBase.BingoCardBase.__init__(self, cardSize, rowSize, colSize) + self.gameType = BingoGlobals.THREEWAY_CARD + self.rowResult = 0 + self.fDiagResult = 0 + self.bDiagResult = 0 + + def checkForWin(self, id): + rowId = int(id / BingoGlobals.CARD_ROWS) + colId = id % BingoGlobals.CARD_COLS + if rowId == 2: + self.rowResult = self.rowCheck(rowId) + if self.fDiagCheck(id): + self.fDiagResult = 1 + if self.bDiagCheck(id): + self.bDiagResult = 1 + if self.rowResult and self.fDiagResult and self.bDiagResult: + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE + + def checkForColor(self, id): + return self.onRow(2, id) | self.onFDiag(id) | self.onBDiag(id) + + def checkForBingo(self): + id = self.cardSize / 2 + if self.checkForWin(id): + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE diff --git a/toontown/fishing/__init__.py b/toontown/fishing/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/friends/FriendHandle.py b/toontown/friends/FriendHandle.py new file mode 100755 index 00000000..69c2c760 --- /dev/null +++ b/toontown/friends/FriendHandle.py @@ -0,0 +1,73 @@ +from otp.ai.MagicWordGlobal import * +from otp.avatar.Avatar import teleportNotify +from toontown.chat import ToonChatGarbler +from toontown.toonbase import ToontownGlobals + +class FriendHandle: + def __init__(self, doId, name, style, adminAccess, petId, isAPet = False): + self.doId = doId + self.style = style + self.petId = petId + self.adminAccess = adminAccess + self.isAPet = isAPet + self.chatGarbler = ToonChatGarbler.ToonChatGarbler() + self.name = name + + def getDoId(self): + return self.doId + + def getAdminAccess(self): + return self.adminAccess + + def isAdmin(self): + return self.adminAccess >= MINIMUM_MAGICWORD_ACCESS + + def getPetId(self): + return self.petId + + def hasPet(self): + return self.getPetId() != 0 + + def isPet(self): + return self.isAPet + + def getName(self): + return self.name + + def getFont(self): + return ToontownGlobals.getToonFont() + + def getStyle(self): + return self.style + + def uniqueName(self, idString): + return idString + '-' + str(self.getDoId()) + + def d_battleSOS(self, sendToId): + base.cr.ttsFriendsManager.d_battleSOS(self.doId) + + def d_teleportQuery(self, requesterId): + teleportNotify.debug('sending d_teleportQuery(%s)' % (requesterId,)) + + base.cr.ttsFriendsManager.d_teleportQuery(self.doId) + + def d_teleportResponse(self, avId, available, shardId, hoodId, zoneId): + teleportNotify.debug('sending teleportResponse%s' % ((avId, available, + shardId, hoodId, zoneId),) + ) + + base.cr.ttsFriendsManager.d_teleportResponse(self.doId, available, + shardId, hoodId, zoneId + ) + + def d_teleportGiveup(self, requesterId): + teleportNotify.debug('sending d_teleportGiveup(%s)' % (requesterId,)) + + base.cr.ttsFriendsManager.d_teleportGiveup(self.doId) + + def isUnderstandable(self): + if base.cr.wantTypedChat(): + return 1 + elif base.localAvatar.isTrueFriends(self.doId): + return 1 + return 0 diff --git a/toontown/friends/FriendInvitee.py b/toontown/friends/FriendInvitee.py new file mode 100755 index 00000000..c7b0dc5a --- /dev/null +++ b/toontown/friends/FriendInvitee.py @@ -0,0 +1,71 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toontowngui import TTDialog +from otp.otpbase import OTPLocalizer +from toontown.toontowngui import ToonHeadDialog +from direct.gui.DirectGui import DGG +from otp.otpbase import OTPGlobals + +class FriendInvitee(ToonHeadDialog.ToonHeadDialog): + notify = DirectNotifyGlobal.directNotify.newCategory('FriendInvitee') + + def __init__(self, avId, avName, avDNA, context, **kw): + self.avId = avId + self.avDNA = avDNA + self.context = context + self.avName = avName + if len(base.localAvatar.friendsList) >= MaxFriends: + base.cr.friendManager.up_inviteeFriendResponse(3, self.context) + self.context = None + text = OTPLocalizer.FriendInviteeTooManyFriends % self.avName + style = TTDialog.Acknowledge + buttonTextList = [OTPLocalizer.FriendInviteeOK] + command = self.__handleOhWell + else: + text = OTPLocalizer.FriendInviteeInvitation % self.avName + style = TTDialog.TwoChoice + buttonTextList = [OTPLocalizer.FriendInviteeOK, OTPLocalizer.FriendInviteeNo] + command = self.__handleButton + optiondefs = (('dialogName', 'FriendInvitee', None), + ('text', text, None), + ('style', style, None), + ('buttonTextList', buttonTextList, None), + ('command', command, None), + ('image_color', (1.0, 0.89, 0.77, 1.0), None), + ('geom_scale', 0.2, None), + ('geom_pos', (-0.1, 0, -0.025), None), + ('pad', (0.075, 0.075), None), + ('topPad', 0, None), + ('midPad', 0, None), + ('pos', (0.45, 0, 0.75), None), + ('scale', 0.75, None)) + self.defineoptions(kw, optiondefs) + ToonHeadDialog.ToonHeadDialog.__init__(self, self.avDNA) + self.accept('cancelFriendInvitation', self.__handleCancelFromAbove) + self.initialiseoptions(FriendInvitee) + self.show() + return + + def cleanup(self): + ToonHeadDialog.ToonHeadDialog.cleanup(self) + self.ignore('cancelFriendInvitation') + if self.context != None: + base.cr.friendManager.up_inviteeFriendResponse(2, self.context) + self.context = None + + def __handleButton(self, value): + base.cr.friendManager.up_inviteeFriendResponse(value == DGG.DIALOG_OK, self.context) + self.context = None + self.cleanup() + return + + def __handleOhWell(self, value): + self.cleanup() + + def __handleCancelFromAbove(self, context = None): + if context == None or context == self.context: + self.context = None + self.cleanup() + return diff --git a/toontown/friends/FriendInviter.py b/toontown/friends/FriendInviter.py new file mode 100755 index 00000000..7051776f --- /dev/null +++ b/toontown/friends/FriendInviter.py @@ -0,0 +1,424 @@ +from panda3d.core import * +from direct.task.Task import Task +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer +from toontown.toon import ToonTeleportPanel +from toontown.suit import Suit +from toontown.pets import Pet +from otp.otpbase import OTPLocalizer +from otp.otpbase import OTPGlobals +globalFriendInviter = None + +def showFriendInviter(avId, avName, avDisableName): + global globalFriendInviter + if globalFriendInviter != None: + globalFriendInviter.cleanup() + globalFriendInviter = None + globalFriendInviter = FriendInviter(avId, avName, avDisableName) + return + + +def hideFriendInviter(): + global globalFriendInviter + if globalFriendInviter != None: + globalFriendInviter.cleanup() + globalFriendInviter = None + return + + +def unloadFriendInviter(): + global globalFriendInviter + if globalFriendInviter != None: + globalFriendInviter.cleanup() + globalFriendInviter = None + return + + +class FriendInviter(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FriendInviter') + + def __init__(self, avId, avName, avDisableName): + DirectFrame.__init__(self, pos=(-1.033, 0.1, -0.35), parent=base.a2dTopRight, image_color=GlobalDialogColor, image_scale=(1.0, 1.0, 0.6), text='', text_wordwrap=TTLocalizer.FIdirectFrameWordwrap, text_scale=TTLocalizer.FIdirectFrame, text_pos=TTLocalizer.FIdirectFramePos) + self['image'] = DGG.getDefaultDialogGeom() + self.avId = avId + self.toonName = avName + avatar = base.cr.doId2do.get(self.avId) + self.avDisableName = avDisableName + self.fsm = ClassicFSM.ClassicFSM('FriendInviter', [State.State('off', self.enterOff, self.exitOff), + State.State('getNewFriend', self.enterGetNewFriend, self.exitGetNewFriend), + State.State('check', self.enterCheck, self.exitCheck), + State.State('tooMany', self.enterTooMany, self.exitTooMany), + State.State('checkAvailability', self.enterCheckAvailability, self.exitCheckAvailability), + State.State('notAvailable', self.enterNotAvailable, self.exitNotAvailable), + State.State('notAcceptingFriends', self.enterNotAcceptingFriends, self.exitNotAcceptingFriends), + State.State('wentAway', self.enterWentAway, self.exitWentAway), + State.State('already', self.enterAlready, self.exitAlready), + State.State('askingCog', self.enterAskingCog, self.exitAskingCog), + State.State('askingPet', self.enterAskingPet, self.exitAskingPet), + State.State('endFriendship', self.enterEndFriendship, self.exitEndFriendship), + State.State('friendsNoMore', self.enterFriendsNoMore, self.exitFriendsNoMore), + State.State('self', self.enterSelf, self.exitSelf), + State.State('ignored', self.enterIgnored, self.exitIgnored), + State.State('asking', self.enterAsking, self.exitAsking), + State.State('yes', self.enterYes, self.exitYes), + State.State('no', self.enterNo, self.exitNo), + State.State('otherTooMany', self.enterOtherTooMany, self.exitOtherTooMany), + State.State('maybe', self.enterMaybe, self.exitMaybe), + State.State('down', self.enterDown, self.exitDown), + State.State('cancel', self.enterCancel, self.exitCancel)], 'off', 'off') + self.context = None + from toontown.toon import ToonAvatarDetailPanel + ToonTeleportPanel.hideTeleportPanel() + ToonAvatarDetailPanel.hideAvatarDetail() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.bOk = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=OTPLocalizer.FriendInviterOK, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, 0.0, -0.1), command=self.__handleOk) + self.bOk.hide() + self.bCancel = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=OTPLocalizer.FriendInviterCancel, text_scale=0.05, text_pos=(0.0, -0.1), pos=TTLocalizer.FIbCancelPos, command=self.__handleCancel) + self.bCancel.hide() + self.bStop = DirectButton(self, image=(gui.find('**/Ignore_Btn_UP'), gui.find('**/Ignore_Btn_DN'), gui.find('**/Ignore_Btn_RLVR')), relief=None, text=OTPLocalizer.FriendInviterStopBeingFriends, text_align=TextNode.ALeft, text_scale=TTLocalizer.FIbStop, text_pos=TTLocalizer.FIbStopTextPos, pos=TTLocalizer.FIbStopPos, command=self.__handleStop) + self.bStop.hide() + self.bYes = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=OTPLocalizer.FriendInviterYes, text_scale=0.05, text_pos=(0.0, -0.1), pos=TTLocalizer.FIbYesPos, command=self.__handleYes) + self.bYes.hide() + self.bNo = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=OTPLocalizer.FriendInviterNo, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.15, 0.0, -0.1), command=self.__handleNo) + self.bNo.hide() + buttons.removeNode() + gui.removeNode() + self.fsm.enterInitialState() + if self.avId == None: + self.fsm.request('getNewFriend') + else: + self.fsm.request('check') + return + + def cleanup(self): + self.fsm.request('cancel') + del self.fsm + self.destroy() + + def getName(self): + name = self.toonName + + if name == None: + name = TTLocalizer.FriendInviterThatToon + + return name + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterGetNewFriend(self): + self['text'] = TTLocalizer.FriendInviterClickToon % len(base.localAvatar.friendsList) + self.bCancel.show() + self.accept('clickedNametag', self.__handleClickedNametag) + + def exitGetNewFriend(self): + self.bCancel.hide() + self.ignore('clickedNametag') + + def __handleClickedNametag(self, avatar): + self.avId = avatar.doId + self.toonName = avatar.getName() + self.avDisableName = avatar.uniqueName('disable') + self.fsm.request('check') + + def enterCheck(self): + myId = base.localAvatar.doId + self.accept(self.avDisableName, self.__handleDisableAvatar) + if self.avId == myId: + self.fsm.request('self') + elif base.cr.isFriend(self.avId): + self.fsm.request('already') + else: + if len(base.localAvatar.friendsList) >= MaxFriends: + self.fsm.request('tooMany') + else: + self.fsm.request('checkAvailability') + + def exitCheck(self): + self.ignore(self.avDisableName) + + def enterTooMany(self): + text = OTPLocalizer.FriendInviterToonTooMany + name = self.toonName + self['text'] = text % name + self.bCancel.show() + self.bCancel.setPos(0.0, 0.0, -0.16) + + def exitTooMany(self): + self.bCancel.hide() + + def enterCheckAvailability(self): + self.accept(self.avDisableName, self.__handleDisableAvatar) + if base.localAvatar.hasPet() and base.localAvatar.getPetId() == self.avId: + self.fsm.request('askingPet') + return + if self.avId not in base.cr.doId2do: + self.fsm.request('wentAway') + return + else: + avatar = base.cr.doId2do.get(self.avId) + if isinstance(avatar, Suit.Suit): + self.fsm.request('askingCog') + return + if isinstance(avatar, Pet.Pet): + self.fsm.request('askingPet') + return + if not base.cr.friendManager: + self.notify.warning('No FriendManager available.') + self.fsm.request('down') + return + base.cr.friendManager.up_friendQuery(self.avId) + self['text'] = OTPLocalizer.FriendInviterCheckAvailability % self.toonName + self.accept('friendResponse', self.__friendResponse) + self.bCancel.show() + self.accept('friendConsidering', self.__friendConsidering) + + def exitCheckAvailability(self): + self.ignore(self.avDisableName) + self.ignore('friendConsidering') + self.ignore('friendResponse') + self.bCancel.hide() + + def enterNotAvailable(self): + self['text'] = OTPLocalizer.FriendInviterNotAvailable % self.getName() + self.context = None + self.bOk.show() + return + + def exitNotAvailable(self): + self.bOk.hide() + + def enterNotAcceptingFriends(self): + self['text'] = OTPLocalizer.FriendInviterFriendSaidNoNewFriends % self.getName() + self.context = None + self.bOk.show() + return + + def exitNotAcceptingFriends(self): + self.bOk.hide() + + def enterWentAway(self): + self['text'] = OTPLocalizer.FriendInviterWentAway % self.getName() + if self.context != None: + base.cr.friendManager.up_cancelFriendQuery(self.context) + self.context = None + self.bOk.show() + return + + def exitWentAway(self): + self.bOk.hide() + + def enterAlready(self): + self['text'] = TTLocalizer.FriendInviterToonAlready % self.getName() + self.bStop['text'] = TTLocalizer.FriendInviterStopBeingToonFriends + self.context = None + self.bStop.show() + self.bCancel.show() + return + + def exitAlready(self): + self['text'] = '' + self.bStop.hide() + self.bCancel.hide() + + def enterAskingCog(self): + self['text'] = OTPLocalizer.FriendInviterAskingCog % self.getName() + taskMgr.doMethodLater(2.0, self.cogReplies, 'cogFriendship') + self.bCancel.show() + + def exitAskingCog(self): + taskMgr.remove('cogFriendship') + self.bCancel.hide() + + def cogReplies(self, task): + self.fsm.request('no') + return Task.done + + def enterAskingPet(self): + if base.localAvatar.hasPet() and base.localAvatar.getPetId() == self.avId: + self['text'] = OTPLocalizer.FriendInviterAskingMyPet % self.getName() + else: + self['text'] = OTPLocalizer.FriendInviterAskingPet % self.getName() + self.context = None + self.bOk.show() + + def exitAskingPet(self): + self.bOk.hide() + + def enterEndFriendship(self): + self['text'] = TTLocalizer.FriendInviterEndFriendshipToon % self.getName() + self.context = None + self.bYes.show() + self.bNo.show() + return + + def exitEndFriendship(self): + self.bYes.hide() + self.bNo.hide() + + def enterFriendsNoMore(self): + base.cr.removeFriend(self.avId) + self['text'] = OTPLocalizer.FriendInviterFriendsNoMore % self.getName() + self.bOk.show() + if self.avId not in base.cr.doId2do: + messenger.send(self.avDisableName) + + def exitFriendsNoMore(self): + self.bOk.hide() + + def enterSelf(self): + self['text'] = OTPLocalizer.FriendInviterSelf + self.context = None + self.bOk.show() + return + + def exitSelf(self): + self.bOk.hide() + + def enterIgnored(self): + self['text'] = OTPLocalizer.FriendInviterIgnored % self.toonName + self.context = None + self.bOk.show() + return + + def exitIgnored(self): + self.bOk.hide() + + def enterAsking(self): + self.accept(self.avDisableName, self.__handleDisableAvatar) + self['text'] = OTPLocalizer.FriendInviterAsking % self.toonName + self.accept('friendResponse', self.__friendResponse) + self.bCancel.show() + + def exitAsking(self): + self.ignore(self.avDisableName) + self.ignore('friendResponse') + self.bCancel.hide() + + def enterYes(self): + self['text'] = OTPLocalizer.FriendInviterFriendSaidYes % self.toonName + self.context = None + self.bOk.show() + return + + def exitYes(self): + self.bOk.hide() + + def enterNo(self): + self['text'] = OTPLocalizer.FriendInviterFriendSaidNo % self.toonName + self.context = None + self.bOk.show() + return + + def exitNo(self): + self.bOk.hide() + + def enterOtherTooMany(self): + self['text'] = OTPLocalizer.FriendInviterOtherTooMany % self.toonName + self.context = None + self.bOk.show() + return + + def exitOtherTooMany(self): + self.bOk.hide() + + def enterMaybe(self): + self['text'] = OTPLocalizer.FriendInviterMaybe % self.toonName + self.context = None + self.bOk.show() + return + + def exitMaybe(self): + self.bOk.hide() + + def enterDown(self): + self['text'] = OTPLocalizer.FriendInviterDown + self.context = None + self.bOk.show() + return + + def exitDown(self): + self.bOk.hide() + + def enterCancel(self): + if self.context != None: + base.cr.friendManager.up_cancelFriendQuery(self.context) + self.context = None + self.fsm.request('off') + return + + def exitCancel(self): + pass + + def __handleOk(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: MAKEAFRIENDSHIP: Make a friendship') + unloadFriendInviter() + + def __handleCancel(self): + unloadFriendInviter() + + def __handleStop(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: BREAKAFRIENDSHIP: Break a friendship') + self.fsm.request('endFriendship') + + def __handleYes(self): + if self.fsm.getCurrentState().getName() == 'endFriendship': + self.fsm.request('friendsNoMore') + else: + unloadFriendInviter() + + def __handleNo(self): + unloadFriendInviter() + + def __handleList(self): + messenger.send('openFriendsList') + + def __friendConsidering(self, yesNoAlready, context): + if yesNoAlready == 1: + self.context = context + self.fsm.request('asking') + elif yesNoAlready == 0: + self.fsm.request('notAvailable') + elif yesNoAlready == 2: + self.fsm.request('already') + elif yesNoAlready == 3: + self.fsm.request('self') + elif yesNoAlready == 4: + self.fsm.request('ignored') + elif yesNoAlready == 6: + self.fsm.request('notAcceptingFriends') + elif yesNoAlready == 10: + self.fsm.request('no') + elif yesNoAlready == 13: + self.fsm.request('otherTooMany') + else: + self.notify.warning('Got unexpected response to friendConsidering: %s' % yesNoAlready) + self.fsm.request('maybe') + + def __friendResponse(self, yesNoMaybe, context): + if self.context != context: + self.notify.warning('Unexpected change of context from %s to %s.' % (self.context, context)) + self.context = context + if yesNoMaybe == 1: + self.fsm.request('yes') + elif yesNoMaybe == 0: + self.fsm.request('no') + elif yesNoMaybe == 3: + self.fsm.request('otherTooMany') + else: + self.notify.warning('Got unexpected response to friendResponse: %s' % yesNoMaybe) + self.fsm.request('maybe') + + def __handleDisableAvatar(self): + self.fsm.request('wentAway') diff --git a/toontown/friends/FriendsListManager.py b/toontown/friends/FriendsListManager.py new file mode 100755 index 00000000..a4b4cab3 --- /dev/null +++ b/toontown/friends/FriendsListManager.py @@ -0,0 +1,135 @@ +from panda3d.core import * +import FriendsListPanel +import FriendInviter +import FriendInvitee +from direct.directnotify import DirectNotifyGlobal +from toontown.toon import ToonTeleportPanel +from toontown.friends import ToontownFriendSecret +from toontown.pets import PetAvatarPanel +from toontown.toon import ToonAvatarPanel +from toontown.suit import SuitAvatarPanel +from toontown.toon import ToonDNA +from toontown.toon import ToonAvatarDetailPanel +from toontown.toonbase import ToontownGlobals +from toontown.toon import Toon +import FriendHandle +from otp.otpbase import OTPGlobals +from otp.nametag import NametagGlobals + +class FriendsListManager: + notify = DirectNotifyGlobal.directNotify.newCategory('FriendsListManager') + + def __init__(self): + self.avatarPanel = None + self._preserveFriendsList = False + self._entered = False + self.friendsRequestQueue = [] + + def load(self): + pass + + def unload(self): + self.exitFLM() + if self.avatarPanel: + del self.avatarPanel + FriendInviter.unloadFriendInviter() + ToonAvatarDetailPanel.unloadAvatarDetail() + ToonTeleportPanel.unloadTeleportPanel() + return + + def enterFLM(self): + self.notify.debug('FriendsListManager: enterFLM()') + if self._preserveFriendsList: + self._preserveFriendsList = 0 + return + self._entered = True + self.accept('openFriendsList', self.__openFriendsList) + self.accept('clickedNametag', self.__handleClickedNametag) + base.localAvatar.setFriendsListButtonActive(1) + NametagGlobals.setMasterNametagsActive(1) + self.accept('gotoAvatar', self.__handleGotoAvatar) + self.accept('friendAvatar', self.__handleFriendAvatar) + self.accept('avatarDetails', self.__handleAvatarDetails) + self.accept('friendInvitation', self.__handleFriendInvitation) + if base.cr.friendManager: + base.cr.friendManager.setAvailable(1) + + def exitFLM(self): + self.notify.debug('FriendsListManager: exitFLM()') + if self._preserveFriendsList: + return + if not self._entered: + return + self._entered = False + self.ignore('openFriendsList') + self.ignore('clickedNametag') + base.localAvatar.setFriendsListButtonActive(0) + NametagGlobals.setMasterNametagsActive(0) + if self.avatarPanel: + self.avatarPanel.cleanup() + self.avatarPanel = None + self.ignore('gotoAvatar') + self.ignore('friendAvatar') + self.ignore('avatarDetails') + FriendsListPanel.hideFriendsList() + ToontownFriendSecret.hideFriendSecret() + if base.cr.friendManager: + base.cr.friendManager.setAvailable(0) + self.ignore('friendInvitation') + FriendInviter.hideFriendInviter() + ToonAvatarDetailPanel.hideAvatarDetail() + ToonTeleportPanel.hideTeleportPanel() + return + + def __openFriendsList(self): + FriendsListPanel.showFriendsList() + + def __handleClickedNametag(self, avatar): + self.notify.debug('__handleClickedNametag. doId = %s' % avatar.doId) + if avatar.isPet(): + self.avatarPanel = PetAvatarPanel.PetAvatarPanel(avatar) + elif isinstance(avatar, Toon.Toon) or isinstance(avatar, FriendHandle.FriendHandle): + if hasattr(self, 'avatarPanel'): + if self.avatarPanel: + if not hasattr(self.avatarPanel, 'getAvId') or self.avatarPanel.getAvId() == avatar.doId: + if not self.avatarPanel.isHidden(): + if self.avatarPanel.getType() == 'toon': + return + self.avatarPanel = ToonAvatarPanel.ToonAvatarPanel(avatar) + else: + self.avatarPanel = SuitAvatarPanel.SuitAvatarPanel(avatar) + + def __handleGotoAvatar(self, avId, avName, avDisableName): + ToonTeleportPanel.showTeleportPanel(avId, avName, avDisableName) + + def __handleFriendAvatar(self, avId, avName, avDisableName): + FriendInviter.showFriendInviter(avId, avName, avDisableName) + + def __handleFriendInvitation(self, avId, avName, inviterDna, context): + dna = ToonDNA.ToonDNA() + dna.makeFromNetString(inviterDna) + if not base.localAvatar.isIgnored(avId): + FriendInvitee.FriendInvitee(avId, avName, dna, context) + + def processQueuedRequests(self): + if len(self.friendsRequestQueue): + request = self.friendsRequestQueue.pop(0) + self.__processFriendRequest(request[0], request[1], request[2], request[3]) + + def __processFriendRequest(self, avId, avName, inviterDna = None, context = None): + self.notify.debug('__handleAvatarFriendInvitation') + askerToon = base.cr.doId2do.get(avId) + if askerToon: + self.notify.debug('got toon') + dna = askerToon.getStyle() + if not base.localAvatar.isIgnored(avId): + FriendInvitee.FriendInvitee(avId, avName, dna, context) + else: + self.notify.debug('no toon') + + def __handleAvatarDetails(self, avId, avName): + ToonAvatarDetailPanel.showAvatarDetail(avId, avName) + + def preserveFriendsList(self): + self.notify.debug('Preserving Friends List') + self._preserveFriendsList = True diff --git a/toontown/friends/FriendsListPanel.py b/toontown/friends/FriendsListPanel.py new file mode 100755 index 00000000..de38fe38 --- /dev/null +++ b/toontown/friends/FriendsListPanel.py @@ -0,0 +1,311 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from direct.fsm import StateData +from toontown.toon import ToonAvatarPanel +from toontown.friends import ToontownFriendSecret +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.nametag.NametagGroup import * +from otp.nametag.NametagConstants import * +from otp.otpbase import OTPGlobals +FLPPets = 1 +FLPOnline = 2 +FLPAll = 3 +globalFriendsList = None + +def determineFriendName(friendId): + handle = base.cr.identifyFriend(friendId) + + return handle.getName() if handle else '' + +def compareFriends(f1, f2): + name1 = determineFriendName(f1) + name2 = determineFriendName(f2) + if name1 > name2: + return 1 + elif name1 == name2: + return 0 + else: + return -1 + +def showFriendsList(): + global globalFriendsList + if globalFriendsList == None: + globalFriendsList = FriendsListPanel() + globalFriendsList.enter() + return + +def hideFriendsList(): + if globalFriendsList != None: + globalFriendsList.exit() + return + +def showFriendsListTutorial(): + global globalFriendsList + if globalFriendsList == None: + globalFriendsList = FriendsListPanel() + globalFriendsList.enter() + globalFriendsList.closeCommand = globalFriendsList.close['command'] + globalFriendsList.close['command'] = None + return + +def hideFriendsListTutorial(): + if globalFriendsList != None: + if hasattr(globalFriendsList, 'closeCommand'): + globalFriendsList.close['command'] = globalFriendsList.closeCommand + globalFriendsList.exit() + return + +def isFriendsListShown(): + if globalFriendsList != None: + return globalFriendsList.isEntered + return 0 + +def unloadFriendsList(): + global globalFriendsList + if globalFriendsList != None: + globalFriendsList.unload() + globalFriendsList = None + return + +class FriendsListPanel(DirectFrame, StateData.StateData): + + def __init__(self): + self.leftmostPanel = FLPPets + self.rightmostPanel = FLPAll + DirectFrame.__init__(self, relief=None) + self.listScrollIndex = [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0] + self.initialiseoptions(FriendsListPanel) + StateData.StateData.__init__(self, 'friends-list-done') + self.friends = {} + self.textRolloverColor = Vec4(1, 1, 0, 1) + self.textDownColor = Vec4(0.5, 0.9, 1, 1) + self.textDisabledColor = Vec4(0.4, 0.8, 0.4, 1) + self.panelType = FLPOnline + + def load(self): + if self.isLoaded == 1: + return None + self.isLoaded = 1 + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + auxGui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.title = DirectLabel(parent=self, relief=None, text='', text_scale=TTLocalizer.FLPtitle, text_fg=(0, 0.1, 0.4, 1), pos=(0.007, 0.0, 0.2)) + background_image = gui.find('**/FriendsBox_Open') + self['image'] = background_image + self.reparentTo(base.a2dTopRight) + self.setPos(-0.233, 0, -0.46) + self.scrollList = DirectScrolledList(parent=self, relief=None, incButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_pos=(0.0, 0.0, -0.316), incButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), incButton_scale=(1.0, 1.0, -1.0), decButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_pos=(0.0, 0.0, 0.117), decButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), itemFrame_pos=(-0.17, 0.0, 0.06), itemFrame_relief=None, numItemsVisible=8, items=[]) + clipper = PlaneNode('clipper') + clipper.setPlane(Plane(Vec3(-1, 0, 0), Point3(0.2, 0, 0))) + clipNP = self.scrollList.attachNewNode(clipper) + self.scrollList.setClipPlane(clipNP) + self.close = DirectButton(parent=self, relief=None, image=(auxGui.find('**/CloseBtn_UP'), auxGui.find('**/CloseBtn_DN'), auxGui.find('**/CloseBtn_Rllvr')), pos=(0.01, 0, -0.38), command=self.__close) + self.left = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(0.6, 0.6, 0.6, 0.6), pos=(-0.15, 0.0, -0.38), scale=(-1.0, 1.0, 1.0), command=self.__left) + self.right = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(0.6, 0.6, 0.6, 0.6), pos=(0.17, 0, -0.38), command=self.__right) + self.newFriend = DirectButton(parent=self, relief=None, pos=(-0.14, 0.0, 0.14), image=(auxGui.find('**/Frnds_Btn_UP'), auxGui.find('**/Frnds_Btn_DN'), auxGui.find('**/Frnds_Btn_RLVR')), text=('', TTLocalizer.FriendsListPanelNewFriend, TTLocalizer.FriendsListPanelNewFriend), text_scale=TTLocalizer.FLPnewFriend, text_fg=(0, 0, 0, 1), text_bg=(1, 1, 1, 1), text_pos=(0.1, -0.085), textMayChange=0, command=self.__newFriend) + self.trueFriends = DirectButton(parent=self, relief=None, pos=TTLocalizer.FLPtruefriendsPos, image=(auxGui.find('**/ChtBx_ChtBtn_UP'), auxGui.find('**/ChtBx_ChtBtn_DN'), auxGui.find('**/ChtBx_ChtBtn_RLVR')), text=('', + TTLocalizer.FriendsListPanelTrueFriends, + TTLocalizer.FriendsListPanelTrueFriends, + ''), text_scale=TTLocalizer.FLPtruefriends, text_fg=(0, 0, 0, 1), text_bg=(1, 1, 1, 1), text_pos=(-0.04, -0.085), textMayChange=0, command=self.__trueFriends) + gui.removeNode() + auxGui.removeNode() + + def unload(self): + if self.isLoaded == 0: + return None + self.isLoaded = 0 + self.exit() + del self.title + del self.scrollList + del self.close + del self.left + del self.right + del self.friends + DirectFrame.destroy(self) + + def makeFriendButton(self, avId, color): + handle = base.cr.identifyFriend(avId) + + if not handle: + base.cr.fillUpFriendsMap() + return + + return DirectButton(relief=None, text=handle.getName(), text_scale=0.04, text_align=TextNode.ALeft, text_fg=color, text_shadow=None, text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, text3_fg=self.textDisabledColor, text_font=ToontownGlobals.getToonFont(), textMayChange=0, command=self.__choseFriend, extraArgs=[avId]) + + def enter(self): + if self.isEntered == 1: + return None + self.isEntered = 1 + if self.isLoaded == 0: + self.load() + base.localAvatar.obscureFriendsListButton(1) + if ToonAvatarPanel.ToonAvatarPanel.currentAvatarPanel: + ToonAvatarPanel.ToonAvatarPanel.currentAvatarPanel.cleanup() + ToonAvatarPanel.ToonAvatarPanel.currentAvatarPanel = None + self.__updateScrollList() + self.__updateTitle() + self.__updateArrows() + self.show() + self.accept('friendOnline', self.__friendOnline) + self.accept('friendOffline', self.__friendOffline) + self.accept('friendsListChanged', self.__friendsListChanged) + self.accept('friendsMapComplete', self.__friendsListChanged) + return + + def exit(self): + if self.isEntered == 0: + return None + self.isEntered = 0 + self.listScrollIndex[self.panelType] = self.scrollList.index + self.hide() + base.cr.cleanPetsFromFriendsMap() + self.ignore('friendOnline') + self.ignore('friendOffline') + self.ignore('friendsListChanged') + self.ignore('friendsMapComplete') + base.localAvatar.obscureFriendsListButton(-1) + messenger.send(self.doneEvent) + return None + + def __close(self): + messenger.send('wakeup') + self.exit() + + def __left(self): + messenger.send('wakeup') + self.listScrollIndex[self.panelType] = self.scrollList.index + if self.panelType > self.leftmostPanel: + self.panelType -= 1 + self.__updateScrollList() + self.__updateTitle() + self.__updateArrows() + + def __right(self): + messenger.send('wakeup') + self.listScrollIndex[self.panelType] = self.scrollList.index + if self.panelType < self.rightmostPanel: + self.panelType += 1 + self.__updateScrollList() + self.__updateTitle() + self.__updateArrows() + + def __trueFriends(self): + messenger.send('wakeup') + ToontownFriendSecret.showFriendSecret() + + def __newFriend(self): + messenger.send('wakeup') + messenger.send('friendAvatar', [None, None, None]) + + def __choseFriend(self, friendId): + messenger.send('wakeup') + handle = base.cr.identifyFriend(friendId) + if handle != None: + messenger.send('clickedNametag', [handle]) + + def createButtons(self, avIds, nametag): + avIds.sort(compareFriends) + + for avId in avIds: + if avId not in self.friends: + button = self.makeFriendButton(avId, nametag) + + if button: + self.scrollList.addItem(button, refresh=0) + self.friends[avId] = button + + def __updateScrollList(self): + petFriends = [] + trueFriends = [] + friends = [] + + if self.panelType == FLPAll or self.panelType == FLPOnline: + if base.wantPets and base.localAvatar.hasPet(): + petFriends.insert(0, base.localAvatar.getPetId()) + + for friendId in base.localAvatar.friendsList: + if self.panelType != FLPOnline or base.cr.isFriendOnline(friendId): + handle = base.cr.identifyFriend(friendId) + + if not handle: + base.cr.fillUpFriendsMap() + return + + if base.localAvatar.isTrueFriends(friendId): + trueFriends.insert(0, friendId) + else: + friends.insert(0, friendId) + elif self.panelType == FLPPets and base.wantPets: + for avId, av in base.cr.doId2do.items(): + from toontown.pets import DistributedPet + if isinstance(av, DistributedPet.DistributedPet): + petFriends.append(avId) + + for friendId in self.friends.keys(): + friendButton = self.friends[friendId] + self.scrollList.removeItem(friendButton, refresh=0) + friendButton.destroy() + del self.friends[friendId] + + self.createButtons(petFriends, NAMETAG_COLORS[CCNonPlayer][0][0]) + self.createButtons(trueFriends, NAMETAG_COLORS[CCNormal][0][0]) + self.createButtons(friends, NAMETAG_COLORS[CCSpeedChat][0][0]) + + self.scrollList.index = self.listScrollIndex[self.panelType] + self.scrollList.refresh() + + def __updateTitle(self): + if self.panelType == FLPOnline: + self.title['text'] = TTLocalizer.FriendsListPanelOnlineFriends + elif self.panelType == FLPAll: + self.title['text'] = TTLocalizer.FriendsListPanelAllFriends + else: + self.title['text'] = TTLocalizer.FriendsListPanelPets + self.title.resetFrameSize() + + def __updateArrows(self): + if self.panelType == self.leftmostPanel: + self.left['state'] = 'inactive' + else: + self.left['state'] = 'normal' + if self.panelType == self.rightmostPanel: + self.right['state'] = 'inactive' + else: + self.right['state'] = 'normal' + + def __friendOnline(self, doId): + if self.panelType == FLPOnline: + self.__updateScrollList() + + def __friendOffline(self, doId): + if self.panelType == FLPOnline: + self.__updateScrollList() + + def __friendsListChanged(self, arg1 = None, arg2 = None): + self.__updateScrollList() diff --git a/toontown/friends/TTSFriendsManager.py b/toontown/friends/TTSFriendsManager.py new file mode 100755 index 00000000..5349865f --- /dev/null +++ b/toontown/friends/TTSFriendsManager.py @@ -0,0 +1,169 @@ +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal +from otp.otpbase import OTPLocalizer, OTPGlobals +from toontown.hood import ZoneUtil +import time + +class TTSFriendsManager(DistributedObjectGlobal): + + def __init__(self, cr): + DistributedObjectGlobal.__init__(self, cr) + self.nextTeleportFail = 0 + + def d_removeFriend(self, friendId): + self.sendUpdate('removeFriend', [friendId]) + + def d_requestFriendsList(self): + self.sendUpdate('requestFriendsList', []) + + def friendList(self, resp): + base.cr.handleGetFriendsList(resp) + + def friendOnline(self, id): + base.cr.handleFriendOnline(id) + + def friendOffline(self, id): + base.cr.handleFriendOffline(id) + + def d_getAvatarDetails(self, avId): + self.sendUpdate('getAvatarDetails', [avId]) + + def friendDetails(self, avId, inventory, trackAccess, hp, maxHp, defaultShard, lastHood, dnaString, experience, trackBonusLevel, npcFriends): + fields = [ + ['setExperience' , experience], + ['setTrackAccess' , trackAccess], + ['setTrackBonusLevel' , trackBonusLevel], + ['setInventory' , inventory], + ['setHp' , hp], + ['setMaxHp' , maxHp], + ['setDefaultShard' , defaultShard], + ['setLastHood' , lastHood], + ['setDNAString' , dnaString], + ['setNPCFriendsDict', npcFriends] + ] + base.cr.n_handleGetAvatarDetailsResp(avId, fields=fields) + + def d_getPetDetails(self, avId): + self.sendUpdate('getPetDetails', [avId]) + + def petDetails(self, avId, ownerId, petName, traitSeed, sz, traits, moods, dna, lastSeen): + fields = list(zip(("setHead", "setEars", "setNose", "setTail", "setBodyTexture", "setColor", "setColorScale", "setEyeColor", "setGender"), dna)) + fields.extend(zip(("setBoredom", "setRestlessness", "setPlayfulness", "setLoneliness", + "setSadness", "setAffection", "setHunger", "setConfusion", "setExcitement", + "setFatigue", "setAnger", "setSurprise"), moods)) + fields.extend(zip(("setForgetfulness", "setBoredomThreshold", "setRestlessnessThreshold", + "setPlayfulnessThreshold", "setLonelinessThreshold", "setSadnessThreshold", + "setFatigueThreshold", "setHungerThreshold", "setConfusionThreshold", + "setExcitementThreshold", "setAngerThreshold", "setSurpriseThreshold", + "setAffectionThreshold"), traits)) + fields.append(("setOwnerId", ownerId)) + fields.append(("setPetName", petName)) + fields.append(("setTraitSeed", traitSeed)) + fields.append(("setSafeZone", sz)) + fields.append(("setLastSeenTimestamp", lastSeen)) + base.cr.n_handleGetAvatarDetailsResp(avId, fields=fields) + + def d_teleportQuery(self, toId): + self.sendUpdate('routeTeleportQuery', [toId]) + + def teleportQuery(self, fromId): + if not hasattr(base, 'localAvatar'): + self.sendUpdate('teleportResponse', [ fromId, 0, 0, 0, 0 ]) + return + if not hasattr(base.localAvatar, 'getTeleportAvailable') or not hasattr(base.localAvatar, 'ghostMode'): + self.sendUpdate('teleportResponse', [ fromId, 0, 0, 0, 0 ]) + return + if not base.localAvatar.acceptingTeleport: + self.sendUpdate('teleportResponse', [ fromId, 3, 0, 0, 0 ]) + return + if base.localAvatar.isIgnored(fromId): + self.sendUpdate('teleportResponse', [ fromId, 2, 0, 0, 0 ]) + return + + friend = base.cr.identifyFriend(fromId) + + if not base.localAvatar.getTeleportAvailable() or base.localAvatar.ghostMode: + if hasattr(friend, 'getName') and self.nextTeleportFail < time.time(): + self.nextTeleportFail = time.time() + OTPGlobals.TeleportFailCooldown + base.localAvatar.setSystemMessage(fromId, OTPLocalizer.WhisperFailedVisit % friend.getName()) + self.sendUpdate('teleportResponse', [ fromId, 0, 0, 0, 0 ]) + return + + hoodId = base.cr.playGame.getPlaceId() + if hasattr(friend, 'getName'): + base.localAvatar.setSystemMessage(fromId, OTPLocalizer.WhisperComingToVisit % friend.getName()) + self.sendUpdate('teleportResponse', [ + fromId, + base.localAvatar.getTeleportAvailable(), + base.localAvatar.defaultShard, + hoodId, + base.localAvatar.getZoneId() + ]) + + def d_teleportResponse(self, toId, available, shardId, hoodId, zoneId): + self.sendUpdate('teleportResponse', [toId, available, shardId, + hoodId, zoneId] + ) + + def setTeleportResponse(self, fromId, available, shardId, hoodId, zoneId): + base.localAvatar.teleportResponse(fromId, available, shardId, hoodId, zoneId) + + def d_whisperSCTo(self, toId, msgIndex): + self.sendUpdate('whisperSCTo', [toId, msgIndex]) + + def setWhisperSCFrom(self, fromId, msgIndex): + if not hasattr(base, 'localAvatar'): + return + if not hasattr(base.localAvatar, 'setWhisperSCFrom'): + return + base.localAvatar.setWhisperSCFrom(fromId, msgIndex) + + def d_whisperSCCustomTo(self, toId, msgIndex): + self.sendUpdate('whisperSCCustomTo', [toId, msgIndex]) + + def setWhisperSCCustomFrom(self, fromId, msgIndex): + if not hasattr(base, 'localAvatar'): + return + if not hasattr(base.localAvatar, 'setWhisperSCCustomFrom'): + return + base.localAvatar.setWhisperSCCustomFrom(fromId, msgIndex) + + def d_whisperSCEmoteTo(self, toId, emoteId): + self.sendUpdate('whisperSCEmoteTo', [toId, emoteId]) + + def setWhisperSCEmoteFrom(self, fromId, emoteId): + if not hasattr(base, 'localAvatar'): + return + if not hasattr(base.localAvatar, 'setWhisperSCEmoteFrom'): + return + base.localAvatar.setWhisperSCEmoteFrom(fromId, emoteId) + + def receiveTalkWhisper(self, fromId, message): + base.localAvatar.setTalkWhisper(fromId, message) + + def d_battleSOS(self, toId): + self.sendUpdate('battleSOS', [toId]) + + def setBattleSOS(self, fromId): + base.localAvatar.battleSOS(fromId) + + def d_teleportGiveup(self, toId): + self.sendUpdate('teleportGiveup', [toId]) + + def setTeleportGiveup(self, fromId): + base.localAvatar.teleportGiveup(fromId) + + def d_whisperSCToontaskTo(self, toId, taskId, toNpcId, toonProgress, msgIndex): + self.sendUpdate('whisperSCToontaskTo', [toId, taskId, toNpcId, + toonProgress, msgIndex] + ) + + def setWhisperSCToontaskFrom(self, fromId, taskId, toNpcId, toonProgress, msgIndex): + base.localAvatar.setWhisperSCToontaskFrom(fromId, taskId, toNpcId, + toonProgress, msgIndex + ) + + def d_sleepAutoReply(self, toId): + self.sendUpdate('sleepAutoReply', [toId]) + + def setSleepAutoReply(self, fromId): + base.localAvatar.setSleepAutoReply(fromId) diff --git a/toontown/friends/TTSFriendsManagerUD.py b/toontown/friends/TTSFriendsManagerUD.py new file mode 100755 index 00000000..8237cf43 --- /dev/null +++ b/toontown/friends/TTSFriendsManagerUD.py @@ -0,0 +1,380 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.distributed.DistributedObjectGlobalUD import DistributedObjectGlobalUD +from direct.distributed.PyDatagram import * +from direct.fsm.FSM import FSM +import time + +# -- FSMS -- +class OperationFSM(FSM): + + def __init__(self, mgr, air, senderAvId, targetAvId=None, callback=None): + FSM.__init__(self, 'OperationFSM-%s' % senderAvId) + self.mgr = mgr + self.air = air + self.sender = senderAvId + self.result = None + self.target = targetAvId + self.callback = callback + + def enterOff(self): + if self.callback: + if self.result is not None: + self.callback(self.sender, self.result) + else: + self.callback() + + if self in self.mgr.operations: + self.mgr.operations.remove(self) + + def enterError(self, message=None): + self.mgr.notify.warning("An error has occurred in a '%s'. Message: %s" % + (type(self).__name__, message) ) + if self.sender in self.mgr.operations: + del self.mgr.operations[self.sender] + + +# -- Friends list -- +class FriendsListOperation(OperationFSM): + + def enterStart(self): + self.air.dbInterface.queryObject(self.air.dbId, self.sender, + self.handleRetrieveSender) + + def handleRetrieveSender(self, dclass, fields): + if dclass != self.air.dclassesByName['DistributedToonUD']: + self.demand('Error', 'Distributed Class was not a Toon.') + return + + self.demand('Retrieved', fields['setFriendsList'][0]) + + def enterRetrieved(self, friendsList): + self.friendsList = friendsList + if len(self.friendsList) <= 0: + self.result = [] + self.demand('Off') + return + + self.friendIndex = 0 + self.realFriendsList = [] + + self.air.dbInterface.queryObject(self.air.dbId, self.friendsList[0], + self.addFriend) + + def addFriend(self, dclass, fields): + if dclass != self.air.dclassesByName['DistributedToonUD']: + self.demand('Error', 'Friend was not a Toon') + return + friendId = self.friendsList[self.friendIndex] + self.realFriendsList.append([friendId, fields['setName'][0], + fields['setDNAString'][0], fields['setAdminAccess'][0], fields['setPetId'][0]]) + + if len(self.realFriendsList) >= len(self.friendsList): + self.result = self.realFriendsList + self.demand('Off') + return + + self.friendIndex += 1 + self.air.dbInterface.queryObject(self.air.dbId, + self.friendsList[self.friendIndex], self.addFriend) + + +# -- Remove Friends -- +class RemoveFriendOperation(OperationFSM): + + def __init__(self, mgr, air, senderAvId, targetAvId=None, callback=None, alert=False): + OperationFSM.__init__(self, mgr, air, senderAvId, targetAvId, callback) + self.alert = alert + + def enterStart(self): + self.air.dbInterface.queryObject(self.air.dbId, self.sender, + self.handleRetrieve) + + def handleRetrieve(self, dclass, fields): + if dclass != self.air.dclassesByName['DistributedToonUD']: + self.demand('Error', 'Distributed Class was not a Toon.') + return + + self.demand('Retrieved', fields['setFriendsList'][0], fields['setTrueFriends'][0]) + + def enterRetrieved(self, friendsList, trueFriendsList): + if self.target in friendsList: + friendsList.remove(self.target) + if self.target in trueFriendsList: + trueFriendsList.remove(self.target) + if self.sender in self.mgr.onlineToons: + dg = self.air.dclassesByName['DistributedToonUD'].aiFormatUpdate( + 'setFriendsList', self.sender, self.sender, + self.air.ourChannel, [friendsList]) + self.air.send(dg) + if self.alert: + dg = self.air.dclassesByName['DistributedToonUD'].aiFormatUpdate( + 'friendsNotify', self.sender, self.sender, + self.air.ourChannel, [self.target, 1]) + self.air.send(dg) + self.demand('Off') + return + + self.air.dbInterface.updateObject(self.air.dbId, self.sender, + self.air.dclassesByName['DistributedToonUD'], + {'setFriendsList' : [friendsList], 'setTrueFriends': [trueFriendsList]}) + self.demand('Off') + +# -- Clear List -- +class ClearListOperation(OperationFSM): + + def enterStart(self): + self.air.dbInterface.queryObject(self.air.dbId, self.sender, + self.handleRetrieved) + + def handleRetrieved(self, dclass, fields): + if dclass != self.air.dclassesByName['DistributedToonUD']: + self.demand('Error', 'Distributed Class was not a Toon.') + return + self.demand('Retrieved', fields['setFriendsList'][0]) + + def enterRetrieved(self, friendsList): + for friend in friendsList: + newOperation = RemoveFriendOperation(self.mgr, self.air, friend, + targetAvId=self.sender, alert=True) + self.mgr.operations.append(newOperation) + newOperation.demand('Start') + self.demand('Off') + +# -- FriendsManager -- + +class TTSFriendsManagerUD(DistributedObjectGlobalUD): + notify = directNotify.newCategory('TTSFriendsManagerUD') + + def announceGenerate(self): + DistributedObjectGlobalUD.announceGenerate(self) + + self.onlineToons = [] + self.tpRequests = {} + self.whisperRequests = {} + self.toon2data = {} + self.operations = [] + self.delayTime = 1.0 + + # -- Friends list -- + def requestFriendsList(self): + avId = self.air.getAvatarIdFromSender() + newOperation = FriendsListOperation(self, self.air, avId, + callback = self.sendFriendsList) + self.operations.append(newOperation) + newOperation.demand('Start') + + def sendFriendsList(self, sender, friendsList): + self.sendUpdateToAvatarId(sender, 'friendList', [friendsList]) + if sender not in self.onlineToons: + self.toonOnline(sender, friendsList) + + # -- Remove Friend -- + def removeFriend(self, friendId): + avId = self.air.getAvatarIdFromSender() + + # Sender remove Friend + newOperation = RemoveFriendOperation(self, self.air, avId, friendId) + self.operations.append(newOperation) + newOperation.demand('Start') + + # Friend remove Sender + newOperation = RemoveFriendOperation(self, self.air, friendId, avId, + alert=True) + self.operations.append(newOperation) + newOperation.demand('Start') + + # -- Avatar Info -- + def getAvatarDetails(self, avId): + senderId = self.air.getAvatarIdFromSender() + def handleToon(dclass, fields): + if dclass != self.air.dclassesByName['DistributedToonUD']: + return + inventory = fields['setInventory'][0] + trackAccess = fields['setTrackAccess'][0] + hp = fields['setHp'][0] + maxHp = fields['setMaxHp'][0] + defaultShard = fields['setDefaultShard'][0] + lastHood = fields['setLastHood'][0] + dnaString = fields['setDNAString'][0] + experience = fields['setExperience'][0] + trackBonusLevel = fields['setTrackBonusLevel'][0] + npcFriends = fields['setNPCFriendsDict'][0] + + self.sendUpdateToAvatarId(senderId, 'friendDetails', [avId, inventory, trackAccess, hp, maxHp, defaultShard, lastHood, dnaString, experience, trackBonusLevel, npcFriends]) + self.air.dbInterface.queryObject(self.air.dbId, avId, handleToon) + + def getPetDetails(self, avId): + senderId = self.air.getAvatarIdFromSender() + def handlePet(dclass, fields): + if dclass != self.air.dclassesByName['DistributedPetAI']: + return + dna = [fields.get(x, [0])[0] for x in ("setHead", "setEars", "setNose", "setTail", "setBodyTexture", "setColor", + "setColorScale", "setEyeColor", "setGender")] + moods = [fields.get(x, [0])[0] for x in ("setBoredom", "setRestlessness", "setPlayfulness", "setLoneliness", + "setSadness", "setAffection", "setHunger", "setConfusion", "setExcitement", + "setFatigue", "setAnger", "setSurprise")] + traits = [fields.get(x, [0])[0] for x in ("setForgetfulness", "setBoredomThreshold", "setRestlessnessThreshold", + "setPlayfulnessThreshold", "setLonelinessThreshold", "setSadnessThreshold", + "setFatigueThreshold", "setHungerThreshold", "setConfusionThreshold", + "setExcitementThreshold", "setAngerThreshold", "setSurpriseThreshold", + "setAffectionThreshold")] + self.sendUpdateToAvatarId(senderId, 'petDetails', [avId, fields.get("setOwnerId", [0])[0], fields.get("setPetName", ["???"])[0], + fields.get("setTraitSeed", [0])[0], fields.get("setSafeZone", [0])[0], + traits, moods, dna, fields.get("setLastSeenTimestamp", [0])[0]]) + self.air.dbInterface.queryObject(self.air.dbId, avId, handlePet) + + # -- Toon Online/Offline -- + def toonOnline(self, doId, friendsList): + if doId not in self.onlineToons: + self.onlineToons.append(doId) + + channel = self.GetPuppetConnectionChannel(doId) + dgcleanup = self.dclass.aiFormatUpdate('goingOffline', self.doId, self.doId, self.air.ourChannel, [doId]) + dg = PyDatagram() + dg.addServerHeader(channel, self.air.ourChannel, CLIENTAGENT_ADD_POST_REMOVE) + dg.addString(dgcleanup.getMessage()) + self.air.send(dg) + + for friend in friendsList: + friendId = friend[0] + if friendId in self.onlineToons: + self.sendUpdateToAvatarId(doId, 'friendOnline', [friendId]) + self.sendUpdateToAvatarId(friendId, 'friendOnline', [doId]) + + def goingOffline(self, avId): + self.toonOffline(avId) + + def toonOffline(self, doId): + if doId not in self.onlineToons: + return + def handleToon(dclass, fields): + if dclass != self.air.dclassesByName['DistributedToonUD']: + return + friendsList = fields['setFriendsList'][0] + for friend in friendsList: + if friend in self.onlineToons: + self.sendUpdateToAvatarId(friend, 'friendOffline', [doId]) + if doId in self.onlineToons: + self.onlineToons.remove(doId) + if doId in self.toon2data: + del self.toon2data[doId] + self.air.dbInterface.queryObject(self.air.dbId, doId, handleToon) + + # -- Clear List -- + def clearList(self, doId): + newOperation = ClearListOperation(self, self.air, doId) + self.operations.append(newOperation) + newOperation.demand('Start') + + # -- Teleport and Whispers -- + def routeTeleportQuery(self, toId): + fromId = self.air.getAvatarIdFromSender() + if fromId in self.tpRequests.values(): + return + self.tpRequests[fromId] = toId + self.sendUpdateToAvatarId(toId, 'teleportQuery', [fromId]) + taskMgr.doMethodLater(5, self.giveUpTeleportQuery, 'tp-query-timeout-%d' % fromId, extraArgs=[fromId, toId]) + + def giveUpTeleportQuery(self, fromId, toId): + # The client didn't respond to the query within the set time, + # So we will tell the query sender that the toon is unavailable. + if fromId in self.tpRequests: + del self.tpRequests[fromId] + self.sendUpdateToAvatarId(fromId, 'setTeleportResponse', [toId, 0, 0, 0, 0]) + self.notify.warning('Teleport request that was sent by %d to %d timed out.' % (fromId, toId)) + + def teleportResponse(self, toId, available, shardId, hoodId, zoneId): + # Here is where the toId and fromId swap (because we are now sending it back) + fromId = self.air.getAvatarIdFromSender() + + # We got the query response, so no need to give up! + if taskMgr.hasTaskNamed('tp-query-timeout-%d' % toId): + taskMgr.remove('tp-query-timeout-%d' % toId) + + if toId not in self.tpRequests: + return + if self.tpRequests.get(toId) != fromId: + self.air.writeServerEvent('suspicious', fromId, 'toon tried to send teleportResponse for a query that isn\'t theirs!') + return + self.sendUpdateToAvatarId(toId, 'setTeleportResponse', [fromId, available, shardId, hoodId, zoneId]) + del self.tpRequests[toId] + + def whisperSCTo(self, toId, msgIndex): + fromId = self.air.getAvatarIdFromSender() + currStamp = time.time() + if fromId in self.whisperRequests: + elapsed = currStamp - self.whisperRequests[fromId] + if elapsed < self.delayTime: + self.whisperRequests[fromId] = currStamp + return + self.whisperRequests[fromId] = currStamp + self.sendUpdateToAvatarId(toId, 'setWhisperSCFrom', [fromId, msgIndex]) + + def whisperSCCustomTo(self, toId, msgIndex): + fromId = self.air.getAvatarIdFromSender() + currStamp = time.time() + if fromId in self.whisperRequests: + elapsed = currStamp - self.whisperRequests[fromId] + if elapsed < self.delayTime: + self.whisperRequests[fromId] = currStamp + return + self.whisperRequests[fromId] = currStamp + self.sendUpdateToAvatarId(toId, 'setWhisperSCCustomFrom', [fromId, msgIndex]) + + def whisperSCEmoteTo(self, toId, msgIndex): + fromId = self.air.getAvatarIdFromSender() + currStamp = time.time() + if fromId in self.whisperRequests: + elapsed = currStamp - self.whisperRequests[fromId] + if elapsed < self.delayTime: + self.whisperRequests[fromId] = currStamp + return + self.whisperRequests[fromId] = currStamp + self.sendUpdateToAvatarId(toId, 'setWhisperSCEmoteFrom', [fromId, msgIndex]) + + def sendTalkWhisper(self, toId, message): + fromId = self.air.getAvatarIdFromSender() + currStamp = time.time() + if fromId in self.whisperRequests: + elapsed = currStamp - self.whisperRequests[fromId] + if elapsed < self.delayTime: + self.whisperRequests[fromId] = currStamp + return + self.whisperRequests[fromId] = currStamp + self.sendUpdateToAvatarId(toId, 'receiveTalkWhisper', [fromId, message]) + self.air.writeServerEvent('whisper-said', fromId, toId, message) + + # -- Routes -- + def battleSOS(self, toId): + requester = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(toId, 'setBattleSOS', [requester]) + + def teleportGiveup(self, toId): + requester = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(toId, 'setTeleportGiveup', [requester]) + + def whisperSCToontaskTo(self, toId, taskId, toNpcId, toonProgress, msgIndex): + requester = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(toId, 'setWhisperSCToontaskFrom', [requester, + taskId, toNpcId, toonProgress, msgIndex] + ) + + def sleepAutoReply(self, toId): + requester = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(toId, 'setSleepAutoReply', [requester]) + + def getToonAccess(self, doId): + return self.toon2data.get(doId, {}).get('access', 0) + + def getToonName(self, doId): + return self.toon2data.get(doId, {}).get('name', '???') + + def getToonAccId(self, doId): + return self.toon2data.get(doId, {}).get('accId', 0) + + def addToonData(self, doId, fields): + data = {} + data['access'] = fields.get('setAdminAccess', [0])[0] + data['name'] = fields['setName'][0] + data['accId'] = fields.get('setDISLid', [0])[0] + self.toon2data[doId] = data diff --git a/toontown/friends/ToontownFriendSecret.py b/toontown/friends/ToontownFriendSecret.py new file mode 100644 index 00000000..eb7abff1 --- /dev/null +++ b/toontown/friends/ToontownFriendSecret.py @@ -0,0 +1,198 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer, ToontownGlobals +from otp.otpbase import OTPGlobals + +globalFriendSecret = None + +def showFriendSecret(): + global globalFriendSecret + if not base.cr.wantTrueFriends(): + chatMgr = base.localAvatar.chatMgr + chatMgr.fsm.request('noTrueFriends') + else: + if globalFriendSecret != None: + globalFriendSecret.unload() + globalFriendSecret = ToontownFriendSecret() + globalFriendSecret.enter() + +def hideFriendSecret(): + if globalFriendSecret != None: + globalFriendSecret.exit() + +def unloadFriendSecret(): + global globalFriendSecret + if globalFriendSecret != None: + globalFriendSecret.unload() + globalFriendSecret = None + return + +class ToontownFriendSecret(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('ToontownFriendSecret') + + def __init__(self): + DirectFrame.__init__(self, parent=aspect2dp, pos=(0, 0, 0.3), relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(1.6, 1, 1.4), image_pos=(0, 0, -0.05), image_color=OTPGlobals.GlobalDialogColor, borderWidth=(0.01, 0.01)) + self.initialiseoptions(ToontownFriendSecret) + self.isLoaded = 0 + self.isEntered = 0 + + def unload(self): + if self.isLoaded == 0: + return None + self.isLoaded = 0 + self.exit() + del self.introText + del self.getSecret + del self.enterSecretText + del self.enterSecret + del self.ok1 + del self.ok2 + del self.cancel + del self.secretText + DirectFrame.destroy(self) + + def load(self): + if self.isLoaded == 1: + return None + self.isLoaded = 1 + self.introText = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.4), scale=0.05, text=TTLocalizer.FriendSecretIntro, text_fg=(0, 0, 0, 1), text_wordwrap=30) + self.introText.hide() + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.getSecret = DirectButton(parent=self, relief=None, pos=(0, 0, -0.11), image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=TTLocalizer.FSgetSecret, text=TTLocalizer.FriendSecretGetSecret, text_scale=TTLocalizer.FSgetSecretButton, text_pos=(0, -0.02), command=self.__getSecret) + self.getSecret.hide() + self.enterSecretText = DirectLabel(parent=self, relief=None, pos=TTLocalizer.FSenterSecretTextPos, scale=0.05, text=TTLocalizer.FriendSecretEnterSecret, text_fg=(0, 0, 0, 1), text_wordwrap=30) + self.enterSecretText.hide() + self.enterSecret = DirectEntry(parent=self, relief=DGG.SUNKEN, scale=0.06, pos=(-0.6, 0, -0.38), frameColor=(0.8, 0.8, 0.5, 1), borderWidth=(0.1, 0.1), numLines=1, width=20, frameSize=(-0.4, + 20.4, + -0.4, + 1.1), command=self.__enterSecret) + self.enterSecret.resetFrameSize() + self.enterSecret.hide() + self.ok1 = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=TTLocalizer.FSok1, text=TTLocalizer.FriendSecretEnter, text_scale=0.06, text_pos=(0, -0.02), pos=(0, 0, -0.5), command=self.__ok1) + self.ok1.hide() + self.ok2 = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=TTLocalizer.FSok2, text=TTLocalizer.FriendSecretOK, text_scale=0.06, text_pos=(0, -0.02), pos=(0, 0, -0.57), command=self.__ok2) + self.ok2.hide() + self.cancel = DirectButton(parent=self, relief=None, text=TTLocalizer.FriendSecretCancel, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=TTLocalizer.FScancel, text_scale=0.06, text_pos=(0, -0.02), pos=(0, 0, -0.57), command=self.__cancel) + self.cancel.hide() + self.nextText = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.3), scale=0.06, text='', text_scale=TTLocalizer.FSnextText, text_fg=(0, 0, 0, 1), text_wordwrap=25.5) + self.nextText.hide() + self.secretText = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.42), scale=0.08, text='', text_fg=(0, 0, 0, 1), text_wordwrap=30) + self.secretText.hide() + guiButton.removeNode() + + def enter(self): + if self.isEntered == 1: + return + self.isEntered = 1 + if self.isLoaded == 0: + self.load() + self.show() + self.introText.show() + self.getSecret.show() + self.enterSecretText.show() + self.enterSecret.show() + self.ok1.show() + self.ok2.hide() + self.cancel.hide() + self.nextText.hide() + self.secretText.hide() + base.localAvatar.chatMgr.fsm.request('otherDialog') + self.enterSecret['focus'] = 1 + + def exit(self): + if self.isEntered == 0: + return + self.isEntered = 0 + self.__cleanupFirstPage() + self.hide() + + def __getSecret(self): + self.__cleanupFirstPage() + self.nextText['text'] = TTLocalizer.FriendSecretGettingSecret + self.nextText.setPos(0, 0, 0.3) + self.nextText.show() + self.ok1.hide() + self.cancel.show() + base.cr.friendManager.requestTFCode(self.gotSecret) + + def gotSecret(self, response, code): + if response == ToontownGlobals.TF_COOLDOWN: + self.rejectGetSecret(TTLocalizer.FriendSecretTooMany) + elif response == ToontownGlobals.TF_SUCCESS: + self.successGetSecret(code) + + def rejectGetSecret(self, reason): + self.nextText['text'] = reason + self.nextText.show() + self.secretText.show() + self.cancel.hide() + self.ok1.hide() + self.ok2.show() + + def successGetSecret(self, code): + self.nextText['text'] = TTLocalizer.FriendSecretGotSecret + self.nextText.setPos(*TTLocalizer.FSgotSecretPos) + self.secretText['text'] = code + self.secretText.setScale(0.1 if code.startswith('TT') else 0.08) + self.nextText.show() + self.secretText.show() + self.cancel.hide() + self.ok1.hide() + self.ok2.show() + + def __enterSecret(self, secret): + self.enterSecret.set('') + secret = secret.strip() + + if not secret: + self.exit() + return + + self.__cleanupFirstPage() + self.nextText['text'] = TTLocalizer.FriendSecretTryingSecret + base.cr.friendManager.redeemTFCode(secret, self.gotResponse) + self.nextText.setPos(0, 0, 0.3) + self.nextText.show() + self.cancel.hide() + self.ok1.hide() + self.ok2.show() + + def gotResponse(self, response, name): + if response == ToontownGlobals.TF_UNKNOWN_SECRET: + self.nextText['text'] = TTLocalizer.FriendSecretEnteredSecretUnknown + elif response == ToontownGlobals.TF_SELF_SECRET: + self.nextText['text'] = TTLocalizer.FriendSecretEnteredSecretSelf + elif response == ToontownGlobals.TF_TOO_FAST: + self.nextText['text'] = TTLocalizer.FriendSecretTooFast + elif response == ToontownGlobals.TF_FRIENDS_LIST_FULL_YOU: + self.nextText['text'] = TTLocalizer.FriendSecretEnteredSecretFullYou + elif response == ToontownGlobals.TF_FRIENDS_LIST_FULL_HIM: + self.nextText['text'] = TTLocalizer.FriendSecretEnteredSecretFullHim % name + elif response == ToontownGlobals.TF_ALREADY_FRIENDS: + self.nextText['text'] = TTLocalizer.FriendSecretAlreadyFriends + elif response == ToontownGlobals.TF_ALREADY_FRIENDS_NAME: + self.nextText['text'] = TTLocalizer.FriendSecretAlreadyFriendsName % name + elif response == ToontownGlobals.TF_SUCCESS: + self.nextText['text'] = TTLocalizer.FriendSecretNowFriends % name + self.nextText.show() + self.cancel.hide() + self.ok1.hide() + self.ok2.show() + + def __ok1(self): + secret = self.enterSecret.get() + self.__enterSecret(secret) + + def __ok2(self): + self.exit() + + def __cancel(self): + self.exit() + + def __cleanupFirstPage(self): + self.introText.hide() + self.getSecret.hide() + self.enterSecretText.hide() + self.enterSecret.hide() + base.localAvatar.chatMgr.fsm.request('mainMenu') diff --git a/toontown/friends/__init__.py b/toontown/friends/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/golf/BuildGeometry.py b/toontown/golf/BuildGeometry.py new file mode 100755 index 00000000..010f1114 --- /dev/null +++ b/toontown/golf/BuildGeometry.py @@ -0,0 +1,309 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from panda3d.core import * +from math import * +import math +GEO_ID = 0 + +def circleX(angle, radius, centerX, centerY): + x = radius * cos(angle) + centerX + return x + + +def circleY(angle, radius, centerX, centerY): + y = radius * sin(angle) + centerY + return y + + +def getCirclePoints(segCount, centerX, centerY, radius, wideX = 1.0, wideY = 1.0): + returnShape = [] + for seg in xrange(0, segCount): + coordX = wideX * circleX(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + + coordX = wideX * circleX(pi * 2.0 * float(0 / segCount), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(0 / segCount), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + return returnShape + + +def addCircle(attachNode, vertexCount, radius, color = Vec4(1.0, 1.0, 1.0, 1.0), layer = 0): + targetGN = GeomNode('Circle Geom') + zFloat = 0.025 + targetCircleShape = getCirclePoints(5 + vertexCount, 0.0, 0.0, radius) + gFormat = GeomVertexFormat.getV3cp() + targetCircleVertexData = GeomVertexData('holds my vertices', gFormat, Geom.UHDynamic) + targetCircleVertexWriter = GeomVertexWriter(targetCircleVertexData, 'vertex') + targetCircleColorWriter = GeomVertexWriter(targetCircleVertexData, 'color') + targetCircleVertexWriter.addData3f(0.0, 0.0, zFloat) + targetCircleColorWriter.addData4f(color[0], color[1], color[2], color[3]) + for vertex in targetCircleShape: + targetCircleVertexWriter.addData3f(0.0 + vertex[0], 0.0 + vertex[1], zFloat) + targetCircleColorWriter.addData4f(color[0], color[1], color[2], color[3]) + + targetTris = GeomTrifans(Geom.UHStatic) + sizeTarget = len(targetCircleShape) + targetTris.addVertex(0) + for countVertex in xrange(1, sizeTarget + 1): + targetTris.addVertex(countVertex) + + targetTris.addVertex(1) + targetTris.closePrimitive() + targetGeom = Geom(targetCircleVertexData) + targetGeom.addPrimitive(targetTris) + attachNode.addGeom(targetGeom) + return targetGeom + + +def addCircleGeom(rootNode, vertexCount, radius, color = Vec4(1.0, 1.0, 1.0, 1.0), layer = 0): + global GEO_ID + GN = GeomNode('Circle %s' % GEO_ID) + GEO_ID += 1 + NodePathGeom = rootNode.attachNewNode(GN) + geo = addCircle(GN, vertexCount, radius, color, layer) + return (NodePathGeom, GN, geo) + + +def addSquare(attachNode, sizeX, sizeY, color = Vec4(1.0, 1.0, 1.0, 1.0), layer = 0): + targetGN = GeomNode('Square Geom') + sX = sizeX / 2.0 + sY = sizeY / 2.0 + color1 = color + color2 = color + color3 = color + gFormat = GeomVertexFormat.getV3n3cpt2() + boxVertexData = GeomVertexData('vertices', gFormat, Geom.UHDynamic) + boxVertexWriter = GeomVertexWriter(boxVertexData, 'vertex') + boxNormalWriter = GeomVertexWriter(boxVertexData, 'normal') + boxColorWriter = GeomVertexWriter(boxVertexData, 'color') + boxTextureWriter = GeomVertexWriter(boxVertexData, 'texcoord') + boxVertexWriter.addData3f(-sX, sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxTextureWriter.addData2f(0.0, 1.0) + boxVertexWriter.addData3f(-sX, -sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxTextureWriter.addData2f(0.0, 0.0) + boxVertexWriter.addData3f(sX, -sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxTextureWriter.addData2f(1.0, 0.0) + boxVertexWriter.addData3f(sX, sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxTextureWriter.addData2f(1.0, 1.0) + boxTris = GeomTristrips(Geom.UHStatic) + boxTris.addVertex(1) + boxTris.addVertex(2) + boxTris.addVertex(0) + boxTris.addVertex(3) + boxTris.closePrimitive() + boxGeom = Geom(boxVertexData) + boxGeom.addPrimitive(boxTris) + attachNode.addGeom(boxGeom) + return boxGeom + + +def addSquareGeom(rootNode, sizeX, sizeY, color = Vec4(1.0, 1.0, 1.0, 1.0), layer = 0): + global GEO_ID + GN = GeomNode('Square %s' % GEO_ID) + GEO_ID += 1 + NodePathGeom = rootNode.attachNewNode(GN) + geo = addSquare(GN, sizeX, sizeY, color, layer) + return (NodePathGeom, GN, geo) + + +def addBox(attachNode, sizeX, sizeY, sizeZ, color = Vec4(1.0, 1.0, 1.0, 1.0), darken = 0): + targetGN = GeomNode('Box Geom') + sX = sizeX / 2.0 + sY = sizeY / 2.0 + sZ = sizeZ / 2.0 + color1 = color + color2 = color + color3 = color + if darken: + color1 = color * 0.75 + color2 = color * 0.5 + color3 = color * 0.25 + gFormat = GeomVertexFormat.getV3n3cp() + boxVertexData = GeomVertexData('vertices', gFormat, Geom.UHDynamic) + boxVertexWriter = GeomVertexWriter(boxVertexData, 'vertex') + boxNormalWriter = GeomVertexWriter(boxVertexData, 'normal') + boxColorWriter = GeomVertexWriter(boxVertexData, 'color') + boxVertexWriter.addData3f(sX, sY, sZ) + boxNormalWriter.addData3f(0, 1, 0) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, sY, -sZ) + boxNormalWriter.addData3f(0, 1, 0) + boxColorWriter.addData4f(color1[0], color1[1], color1[2], color1[3]) + boxVertexWriter.addData3f(-sX, sY, -sZ) + boxNormalWriter.addData3f(0, 1, 0) + boxColorWriter.addData4f(color1[0], color1[1], color1[2], color1[3]) + boxVertexWriter.addData3f(-sX, sY, sZ) + boxNormalWriter.addData3f(0, 1, 0) + boxColorWriter.addData4f(color1[0], color1[1], color1[2], color1[3]) + boxVertexWriter.addData3f(-sX, -sY, sZ) + boxNormalWriter.addData3f(0, -1, 0) + boxColorWriter.addData4f(color2[0], color2[1], color2[2], color2[3]) + boxVertexWriter.addData3f(-sX, -sY, -sZ) + boxNormalWriter.addData3f(0, -1, 0) + boxColorWriter.addData4f(color3[0], color3[1], color3[2], color3[3]) + boxVertexWriter.addData3f(sX, -sY, -sZ) + boxNormalWriter.addData3f(0, -1, 0) + boxColorWriter.addData4f(color2[0], color2[1], color2[2], color2[3]) + boxVertexWriter.addData3f(sX, -sY, sZ) + boxNormalWriter.addData3f(0, -1, 0) + boxColorWriter.addData4f(color2[0], color2[1], color2[2], color2[3]) + boxVertexWriter.addData3f(-sX, sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(-sX, -sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, -sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, sY, -sZ) + boxNormalWriter.addData3f(0, 0, -1) + boxColorWriter.addData4f(color3[0], color3[1], color3[2], color3[3]) + boxVertexWriter.addData3f(sX, -sY, -sZ) + boxNormalWriter.addData3f(0, 0, -1) + boxColorWriter.addData4f(color3[0], color3[1], color3[2], color3[3]) + boxVertexWriter.addData3f(-sX, -sY, -sZ) + boxNormalWriter.addData3f(0, 0, -1) + boxColorWriter.addData4f(color3[0], color3[1], color3[2], color3[3]) + boxVertexWriter.addData3f(-sX, sY, -sZ) + boxNormalWriter.addData3f(0, 0, -1) + boxColorWriter.addData4f(color3[0], color3[1], color3[2], color3[3]) + boxVertexWriter.addData3f(sX, sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, -sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color1[0], color1[1], color1[2], color1[3]) + boxVertexWriter.addData3f(sX, -sY, -sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color1[0], color1[1], color1[2], color1[3]) + boxVertexWriter.addData3f(sX, sY, -sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color1[0], color1[1], color1[2], color1[3]) + boxVertexWriter.addData3f(-sX, sY, -sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color2[0], color2[1], color2[2], color2[3]) + boxVertexWriter.addData3f(-sX, -sY, -sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color3[0], color3[1], color3[2], color3[3]) + boxVertexWriter.addData3f(-sX, -sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color2[0], color2[1], color2[2], color2[3]) + boxVertexWriter.addData3f(-sX, sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color2[0], color2[1], color2[2], color2[3]) + boxTris = GeomTristrips(Geom.UHStatic) + boxTris.addVertex(0) + boxTris.addVertex(1) + boxTris.addVertex(3) + boxTris.addVertex(2) + boxTris.closePrimitive() + boxTris.addVertex(5) + boxTris.addVertex(6) + boxTris.addVertex(4) + boxTris.addVertex(7) + boxTris.closePrimitive() + boxTris.addVertex(9) + boxTris.addVertex(10) + boxTris.addVertex(8) + boxTris.addVertex(11) + boxTris.closePrimitive() + boxTris.addVertex(13) + boxTris.addVertex(14) + boxTris.addVertex(12) + boxTris.addVertex(15) + boxTris.closePrimitive() + boxTris.addVertex(16) + boxTris.addVertex(17) + boxTris.addVertex(19) + boxTris.addVertex(18) + boxTris.closePrimitive() + boxTris.addVertex(21) + boxTris.addVertex(22) + boxTris.addVertex(20) + boxTris.addVertex(23) + boxTris.closePrimitive() + boxGeom = Geom(boxVertexData) + boxGeom.addPrimitive(boxTris) + attachNode.addGeom(boxGeom) + return boxGeom + + +def addBoxGeom(rootNode, sizeX, sizeY, sizeZ, color = Vec4(1.0, 1.0, 1.0, 1.0), darken = 0): + global GEO_ID + GN = GeomNode('Box %s' % GEO_ID) + GEO_ID += 1 + nodePathGeom = rootNode.attachNewNode(GN) + geo = addBox(GN, sizeX, sizeY, sizeZ, color, darken) + return (nodePathGeom, GN, geo) + + +def addArrow(attachNode, sizeX, sizeY, color = Vec4(1.0, 1.0, 1.0, 1.0), layer = 0): + targetGN = GeomNode('Arrow Geom') + sX = sizeX / 2.0 + sY = sizeY / 2.0 + color1 = color + color2 = color + color3 = color + gFormat = GeomVertexFormat.getV3n3cp() + boxVertexData = GeomVertexData('vertices', gFormat, Geom.UHDynamic) + boxVertexWriter = GeomVertexWriter(boxVertexData, 'vertex') + boxNormalWriter = GeomVertexWriter(boxVertexData, 'normal') + boxColorWriter = GeomVertexWriter(boxVertexData, 'color') + boxVertexWriter.addData3f(-sX, sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(-sX, -sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, -sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxTris = GeomTristrips(Geom.UHStatic) + boxTris.addVertex(1) + boxTris.addVertex(2) + boxTris.addVertex(0) + boxTris.addVertex(3) + boxTris.closePrimitive() + boxVertexWriter.addData3f(-sX * 2.0, sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX * 2.0, sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(0.0, sY * 2.0, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxTris.addVertex(4) + boxTris.addVertex(5) + boxTris.addVertex(6) + boxTris.closePrimitive() + boxGeom = Geom(boxVertexData) + boxGeom.addPrimitive(boxTris) + attachNode.addGeom(boxGeom) + return boxGeom + + +def addArrowGeom(rootNode, sizeX, sizeY, color = Vec4(1.0, 1.0, 1.0, 1.0), layer = 0): + global GEO_ID + GN = GeomNode('Arrow %s' % GEO_ID) + GEO_ID += 1 + NodePathGeom = rootNode.attachNewNode(GN) + geo = addArrow(GN, sizeX, sizeY, color, layer) + return (NodePathGeom, GN, geo) diff --git a/toontown/golf/DistributedGolfCourse.py b/toontown/golf/DistributedGolfCourse.py new file mode 100755 index 00000000..b4ae6386 --- /dev/null +++ b/toontown/golf/DistributedGolfCourse.py @@ -0,0 +1,412 @@ +from direct.interval.IntervalGlobal import Sequence, Func, Wait, LerpColorScaleInterval, Parallel +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.task.Task import Task +from direct.showbase import PythonUtil +from toontown.distributed import DelayDelete +from toontown.distributed.DelayDeletable import DelayDeletable +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from panda3d.core import * +from direct.gui.DirectGui import * +from direct.distributed.ClockDelta import * +from direct.fsm.FSM import FSM +from toontown.golf import GolfGlobals +from toontown.golf import GolfScoreBoard +from toontown.golf import GolfRewardDialog +from toontown.toon import ToonHeadFrame + +class DistributedGolfCourse(DistributedObject.DistributedObject, FSM, DelayDeletable): + notify = directNotify.newCategory('DistributedGolfCourse') + defaultTransitions = {'Off': ['Join'], + 'Join': ['WaitStartHole', 'Cleanup'], + 'WaitStartHole': ['PlayHole', 'Cleanup', 'WaitReward'], + 'PlayHole': ['WaitFinishCourse', + 'WaitStartHole', + 'WaitReward', + 'Cleanup'], + 'WaitReward': ['WaitFinishCourse', 'Cleanup'], + 'WaitFinishCourse': ['Cleanup'], + 'Cleanup': ['Off']} + id = 0 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, base.cr) + FSM.__init__(self, 'Golf_%s_FSM' % self.id) + self.waitingStartLabel = DirectLabel(text=TTLocalizer.MinigameWaitingForOtherToons, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075) + self.waitingStartLabel.hide() + self.avIdList = [] + self.remoteAvIdList = [] + self.exitedAvIdList = [] + self.toonPanels = [] + self.exitedPanels = [] + self.exitedToonsWithPanels = [] + self.localAvId = base.localAvatar.doId + self.hasLocalToon = 0 + self.modelCount = 500 + self.cleanupActions = [] + self.courseId = None + self.scores = {} + self.curHoleIndex = 0 + self.golfRewardDialog = None + self.rewardIval = None + self.scoreBoard = None + self.exit = False + self.drivingToons = [] + return + + def generate(self): + self.notify.debug('GOLF COURSE: generate, %s' % self.getTitle()) + DistributedObject.DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + if not self.hasLocalToon: + return + self.notify.debug('BASE: handleAnnounceGenerate: send setAvatarJoined') + self.__delayDelete = DelayDelete.DelayDelete(self, 'GolfCourse.self') + self.request('Join') + self.normalExit = 1 + count = self.modelCount + loader.beginBulkLoad('minigame', TTLocalizer.HeadingToMinigameTitle % self.getTitle(), count, 1, TTLocalizer.TIP_GOLF, self.zoneId) + self.load() + globalClock.syncFrameTime() + self.onstage() + self.accept('clientCleanup', self._handleClientCleanup) + + def _handleClientCleanup(self): + self._destroyDelayDelete() + + def _destroyDelayDelete(self): + if self.__delayDelete: + self.__delayDelete.destroy() + self.__delayDelete = None + return + + def delete(self): + self.ignore('clientCleanup') + if self.scoreBoard: + self.scoreBoard.delete() + DistributedObject.DistributedObject.delete(self) + if self.golfRewardDialog: + self.golfRewardDialog.delete() + self.cleanUpReward() + if self.toonPanels: + for x in xrange(len(self.toonPanels)): + self.toonPanels[x].destroy() + + self.toonPanels = None + self.scores = None + self.music.stop() + self.music = None + for avId in self.avIdList: + av = base.cr.doId2do.get(avId) + if av: + av.show() + + return + + def load(self): + self.music = base.loadMusic('phase_6/audio/bgm/GZ_PlayGolf.ogg') + + def setCourseReady(self, numHoles, holeIds, coursePar): + self.notify.debug('GOLF COURSE: received setCourseReady') + if self.state == 'Cleanup': + return + self.numHoles = numHoles + self.holeIds = holeIds + self.coursePar = coursePar + for avId in self.avIdList: + blankScoreList = [0] * self.numHoles + self.scores[avId] = blankScoreList + + self.request('WaitStartHole') + for avId in self.avIdList: + av = base.cr.doId2do.get(avId) + if av: + av.show() + av.reparentTo(render) + av.setPos(0, 0, -100) + else: + self.notify.warning('avId =%d does not exist') + + self.scoreBoard = GolfScoreBoard.GolfScoreBoard(self) + toonPanelsStart = 0.3 + whichToon = 0 + color = 0 + tpDiff = -0.45 + headPanel = loader.loadModel('phase_6/models/golf/headPanel') + if self.numPlayers > 0: + for avId in self.avIdList: + if not self.localAvId == avId: + av = base.cr.doId2do.get(avId) + if av: + tPanels = ToonHeadFrame.ToonHeadFrame(av, GolfGlobals.PlayerColors[color], headPanel) + tPanels.reparentTo(aspect2d) + tPanels.setPos(base.a2dTopLeft.getPos()[0] + 0.1875, 0, toonPanelsStart + whichToon * tpDiff) + tPanels.setScale(0.3, 1, 0.7) + tPanels.head.setPos(0, 10, 0.18) + tPanels.head.setScale(0.47, 0.2, 0.2) + tPanels.tag1.setPos(0.3, 10, 0.18) + tPanels.tag1.setScale(0.1283, 0.055, 0.055) + tPanels.tag2.setPos(0, 10, 0.43) + tPanels.tag2.setScale(0.117, 0.05, 0.05) + self.toonPanels.append(tPanels) + whichToon = whichToon + 1 + color += 1 + else: + color += 1 + + base.setCellsAvailable(base.leftCells, 0) + + else: + self.toonPanels = None + for avId in self.exitedAvIdList: + if avId not in self.exitedToonsWithPanels: + self.exitMessageForToon(avId) + + + def setPlayHole(self): + self.notify.debug('GOLF COURSE: received setPlayHole') + if self.state not in ['PlayHole', 'Cleanup']: + self.request('PlayHole') + + def getTitle(self): + return GolfGlobals.getCourseName(self.courseId) + + def getInstructions(self): + return 'You should not be seeing this' + + def setGolferIds(self, avIds): + self.avIdList = avIds + self.numPlayers = len(self.avIdList) + self.hasLocalToon = self.localAvId in self.avIdList + if not self.hasLocalToon: + self.notify.warning('localToon (%s) not in list of golfers: %s' % (self.localAvId, self.avIdList)) + return + self.notify.info('GOLF COURSE: setParticipants: %s' % self.avIdList) + self.remoteAvIdList = [] + for avId in self.avIdList: + if avId != self.localAvId: + self.remoteAvIdList.append(avId) + + def setCourseAbort(self, avId): + if avId == self.localAvId or avId == 0: + if not self.hasLocalToon: + return + self.notify.warning('GOLF COURSE: setGameAbort: Aborting game') + self.normalExit = 0 + if not self.state == 'Cleanup': + self.request('Cleanup') + else: + self.notify.warning('GOLF COURSE: Attempting to clean up twice') + + base.setCellsAvailable(base.leftCells, 1) + + def onstage(self): + self.notify.debug('GOLF COURSE: onstage') + base.playMusic(self.music, looping=1, volume=0.9) + + def avExited(self, avId): + self.exitedAvIdList.append(avId) + hole = base.cr.doId2do.get(self.curHoleDoId) + if hole: + hole.avExited(avId) + if self.localAvId == avId: + self.notify.debug('forcing setCourseAbort') + if self.state == 'Join': + loader.endBulkLoad('minigame') + self.setCourseAbort(0) + self.exitMessageForToon(avId) + + def exitMessageForToon(self, avId): + if self.toonPanels and self.localAvId != avId: + y = 0 + for x in xrange(len(self.avIdList)): + if avId == self.avIdList[x] and y < len(self.toonPanels): + toonPanel = self.toonPanels[y] + toonPanel.headModel.hide() + toonPanel.tag1.hide() + toonPanel.tag2.hide() + exitedToon = DirectLabel(parent=self.toonPanels[y], relief=None, pos=(0, 0, 0.4), color=(1, 1, 1, 1), text_align=TextNode.ACenter, text=TTLocalizer.GolferExited % toonPanel.av.getName(), text_scale=0.07, text_wordwrap=6) + exitedToon.setScale(2, 1, 1) + self.exitedPanels.append(exitedToon) + self.exitedToonsWithPanels.append(avId) + toonPanel.removeAvKeep() + elif not self.avIdList[x] == self.localAvId: + y += 1 + + return + + def enterJoin(self): + self.sendUpdate('setAvatarJoined', []) + + def handleFallingAsleepGolf(self, task): + base.localAvatar.stopSleepWatch() + base.localAvatar.forceGotoSleep() + self.sendUpdate('setAvatarExited', []) + + def exitJoin(self): + pass + + def enterWaitStartHole(self): + self.sendUpdate('setAvatarReadyCourse', []) + + def exitWaitStartHole(self): + pass + + def enterPlayHole(self): + loader.endBulkLoad('minigame') + + def exitPlayHole(self): + pass + + def enterCleanup(self): + base.localAvatar.stopSleepWatch() + for action in self.cleanupActions: + action() + + self.cleanupActions = [] + if not self.scoreBoard == None: + self.scoreBoard.delete() + if self.toonPanels: + for x in xrange(len(self.toonPanels)): + self.toonPanels[x].destroy() + + self.toonPanels = None + for avId in self.avIdList: + av = base.cr.doId2do.get(avId) + if av: + av.show() + av.resetLOD() + + self.ignoreAll() + if self.hasLocalToon: + messenger.send('leavingGolf') + self._destroyDelayDelete() + return + + def exitCleanup(self): + pass + + def setCourseId(self, courseId): + self.courseId = courseId + + def calcHolesToUse(self): + retval = [] + while len(retval) < self.numHoles: + for holeId in self.courseInfo['holeIds']: + retval.append(holeId) + if len(retval) >= self.numHoles: + break + + return retval + + def calcCoursePar(self): + retval = 0 + for holeId in self.holeIds: + holeInfo = GolfGlobals.HoleInfo[holeId] + retval += holeInfo['par'] + + return retval + + def setScores(self, scoreList): + scoreList.reverse() + for avId in self.avIdList: + avScores = [] + for holeIndex in xrange(self.numHoles): + avScores.append(scoreList.pop()) + + self.scores[avId] = avScores + + self.notify.debug('self.scores=%s' % self.scores) + + def setCurHoleIndex(self, holeIndex): + self.curHoleIndex = holeIndex + + def setCurHoleDoId(self, holeDoId): + self.curHoleDoId = holeDoId + + def getCurGolfer(self): + if self.curHoleDoId != 0: + av = base.cr.doId2do.get(self.curHoleDoId) + if av: + return av.currentGolfer + else: + return None + return None + + def getStrokesForCurHole(self, avId): + retval = 0 + if avId in self.scores: + retval = self.scores[avId][self.curHoleIndex] + return retval + + def isGameDone(self): + retval = False + self.notify.debug('Self state is: %s' % self.state) + if self.getCurrentOrNextState() == 'WaitReward' or self.getCurrentOrNextState() == 'WaitFinishCourse': + retval = True + return retval + + def setReward(self, trophiesList, rankingsList, holeBestList, courseBestList, cupList, tieBreakWinner, aim0, aim1, aim2, aim3): + self.trophiesList = trophiesList + self.rankingsList = rankingsList + self.holeBestList = holeBestList + self.courseBestList = courseBestList + self.cupList = cupList + self.tieBreakWinner = tieBreakWinner + self.aimTimesList = [aim0, + aim1, + aim2, + aim3] + if self.state not in ['Cleanup']: + self.demand('WaitReward') + + def enterWaitReward(self): + self.scoreBoard.showBoardFinal() + if self.curHoleDoId != 0: + av = base.cr.doId2do.get(self.curHoleDoId) + av.cleanupPowerBar() + + def doneWithRewardMovie(): + if self.exit == False: + self.notify.debug('doneWithRewardMovie') + self.sendUpdate('setDoneReward', []) + self._destroyDelayDelete() + self.exit = True + + self.golfRewardDialog = GolfRewardDialog.GolfRewardDialog(self.avIdList, self.trophiesList, self.rankingsList, self.holeBestList, self.courseBestList, self.cupList, self.localAvId, self.tieBreakWinner, self.aimTimesList) + self.rewardIval = Sequence(Parallel(Wait(5), self.golfRewardDialog.getMovie()), Func(doneWithRewardMovie)) + self.rewardIval.start() + + def exitEarly(self): + if self.exit == False: + self.notify.debug('doneWithRewardMovie') + self.sendUpdate('setDoneReward', []) + self._destroyDelayDelete() + self.exit = True + + def exitReward(self): + self.cleanUpReward() + + def cleanUpReward(self): + if self.rewardIval: + self.rewardIval.pause() + self.rewardIval = None + return + + def updateScoreBoard(self): + if self.scoreBoard: + self.scoreBoard.update() + + def changeDrivePermission(self, avId, canDrive): + if canDrive: + if avId not in self.drivingToons: + self.drivingToons.append(avId) + elif avId in self.drivingToons: + self.drivingToons.remove(avId) + + def canDrive(self, avId): + retval = avId in self.drivingToons + return retval diff --git a/toontown/golf/DistributedGolfCourseAI.py b/toontown/golf/DistributedGolfCourseAI.py new file mode 100755 index 00000000..96e21b2f --- /dev/null +++ b/toontown/golf/DistributedGolfCourseAI.py @@ -0,0 +1,998 @@ +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.golf import DistributedGolfHoleAI +from panda3d.core import * +from direct.fsm.FSM import FSM +from toontown.ai.ToonBarrier import * +from toontown.golf import GolfGlobals +from toontown.uberdog import TopToonsGlobals +INITIAL = 0 +EXITED = 1 +EXPECTED = 2 +JOINED = 3 +READY = 4 +ONHOLE = 5 +BALLIN = 6 +JOIN_TIMEOUT = 30 +READY_TIMEOUT = 30 +EXIT_TIMEOUT = 30 +REWARD_TIMEOUT = 30 + +class DistributedGolfCourseAI(DistributedObjectAI.DistributedObjectAI, FSM): + notify = directNotify.newCategory('DistributedGolfCourseAI') + defaultTransitions = {'Off': ['WaitJoin'], + 'WaitJoin': ['WaitReadyCourse', 'Cleanup'], + 'WaitReadyCourse': ['WaitReadyHole', 'Cleanup'], + 'WaitReadyHole': ['PlayHole', + 'Cleanup', + 'WaitLeaveHole', + 'WaitReward'], + 'PlayHole': ['PlayHole', + 'WaitLeaveHole', + 'Cleanup', + 'WaitReward'], + 'WaitLeaveHole': ['WaitReadyHole', + 'WaitLeaveCourse', + 'Cleanup', + 'WaitReward'], + 'WaitReward': ['WaitLeaveCourse', 'Cleanup', 'WaitLeaveHole'], + 'WaitLeaveCourse': ['Cleanup'], + 'Cleanup': ['Off']} + + def __init__(self, zoneId, avIds, courseId, preferredHoleId = None): + FSM.__init__(self, 'GolfCourse_%s_FSM' % zoneId) + DistributedObjectAI.DistributedObjectAI.__init__(self, simbase.air) + self.notify.debug('GOLF COURSE: init') + self.zoneId = zoneId + self.currentHole = None + self.avIdList = [] + self.avStateDict = {} + self.addExpectedGolfers(avIds) + self.courseId = courseId + self.preferredHoleId = preferredHoleId + self.courseInfo = GolfGlobals.CourseInfo[self.courseId] + self.numHoles = self.courseInfo['numHoles'] + self.holeIds = self.calcHolesToUse() + self.notify.debug('self.holeIds = %s' % self.holeIds) + self.numHolesPlayed = 0 + self.curHoleIndex = 0 + self.trophyListLen = 0 + self.courseBestListLen = 0 + self.holeBestListLen = 0 + self.cupListLen = 0 + self.scores = {} + self.aimTimes = {} + self.startingHistory = {} + self.endingHistory = {} + self.startingHoleBest = {} + self.endingHoleBest = {} + self.startingCourseBest = {} + self.endingCourseBest = {} + self.startingCups = {} + self.endingCups = {} + self.initHistory() + self.newTrophies = {} + self.newHoleBest = {} + self.newCourseBest = {} + self.newCups = {} + self.drivingToons = [] + self.__barrier = None + self.winnerByTieBreak = 0 + return + + def initHistory(self): + for avId in self.avIdList: + av = simbase.air.doId2do.get(avId) + if av: + history = av.getGolfHistory() + self.startingHistory[avId] = history[:] + self.endingHistory[avId] = history[:] + holeBest = av.getGolfHoleBest() + self.startingHoleBest[avId] = holeBest[:] + self.endingHoleBest[avId] = holeBest[:] + courseBest = av.getGolfCourseBest() + self.startingCourseBest[avId] = courseBest[:] + self.endingCourseBest[avId] = courseBest[:] + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.grabGolfers() + + def delete(self): + self.notify.debug('GOLF COURSE: delete: deleting AI GolfCourse object') + if hasattr(self, 'rewardBarrier'): + self.rewardBarrier.cleanup() + del self.rewardBarrier + if self.currentHole: + self.notify.debug('calling requestDelete on hole %d' % self.currentHole.doId) + self.currentHole.requestDelete() + self.currentHole = None + self.ignoreAll() + from toontown.golf import GolfManagerAI + GolfManagerAI.GolfManagerAI().removeCourse(self) + if self.__barrier: + self.__barrier.cleanup() + self.__barrier = None + DistributedObjectAI.DistributedObjectAI.delete(self) + return + + def load(self): + self.b_setCourseReady() + self.request('WaitReadyCourse') + + def getZoneId(self): + return self.zoneId + + def addExpectedGolfers(self, avIdList): + self.notify.debug('Sending %s to course %s' % (avIdList, self.zoneId)) + for avId in avIdList: + golfer = simbase.air.doId2do.get(avId) + if golfer: + if avId not in self.avIdList: + self.avIdList.append(avId) + self.avStateDict[avId] = INITIAL + elif self.avStateDict[avId] == EXITED: + if self.isGenerated(): + pass + else: + self.notify.warning('GOLF COURSE: trying to grab golfer %s that is already on the course' % avId) + + def grabGolfers(self): + for avId in self.avIdList: + golfer = simbase.air.doId2do.get(avId) + if golfer: + if self.avStateDict[avId] == INITIAL: + self.avStateDict[avId] = EXPECTED + + self.request('WaitJoin') + + def getGolferIds(self): + return self.avIdList + + def checkGolferPlaying(self, avId): + if self.avStateDict[avId] == ONHOLE: + return 1 + else: + return 0 + + def b_setCourseReady(self): + self.setCourseReady() + self.d_setCourseReady() + + def d_setCourseReady(self): + self.notify.debug('GOLF COURSE: Sending setCourseReady') + self.sendUpdate('setCourseReady', [self.numHoles, self.holeIds, self.calcCoursePar()]) + + def setCourseReady(self): + self.notify.debug('GOLF COURSE: setCourseReady: golf course ready with avatars: %s' % self.avIdList) + self.trophyListLen = 0 + self.courseBestListLen = 0 + self.holeBestListLen = 0 + self.cupListLen = 0 + self.normalExit = 1 + + def d_setPlayHole(self): + self.notify.debug('GOLF COURSE: setPlayHole: play on golf hole about to start') + self.sendUpdate('setPlayHole', []) + + def b_setCourseExit(self): + self.d_setCourseExit() + self.setCourseExit() + + def d_setCourseExit(self): + self.notify.debug('GOLF COURSE: Sending setGameExit') + self.sendUpdate('setCourseExit', []) + + def setCourseExit(self): + self.notify.debug('GOLF COURSE: setGameExit') + + def handleExitedAvatar(self, avId): + self.notify.warning('GOLF COURSE: handleExitedAvatar: avatar id exited: ' + str(avId)) + self.avStateDict[avId] = EXITED + self.sendUpdate('avExited', [avId]) + if self.currentHole and not self.haveAllGolfersExited(): + self.currentHole.avatarDropped(avId) + if self.haveAllGolfersExited(): + self.setCourseAbort() + elif self.isCurHoleDone(): + if self.isPlayingLastHole(): + if self.state not in ['WaitReward', 'WaitReadyHole']: + self.safeDemand('WaitReward') + else: + self.notify.debug('allBalls are in holes, calling holeOver') + self.holeOver() + if hasattr(self, 'rewardBarrier'): + if self.rewardBarrier: + self.rewardBarrier.clear(avId) + if hasattr(self, '__barrier'): + if self.__barrier: + self.__.clear(avId) + + def startNextHole(self): + self.notify.debugStateCall(self) + holeId = self.holeIds[self.numHolesPlayed] + self.currentHole = DistributedGolfHoleAI.DistributedGolfHoleAI(self.zoneId, golfCourse=self, holeId=holeId) + self.currentHole.generateWithRequired(self.zoneId) + self.d_setCurHoleDoId(self.currentHole.doId) + self.safeDemand('WaitReadyHole') + + def holeOver(self): + self.notify.debug('GOLF COURSE: holeOver') + self.numHolesPlayed += 1 + if self.numHolesPlayed < self.numHoles: + self.b_setCurHoleIndex(self.numHolesPlayed) + self.safeDemand('WaitLeaveHole') + + def setCourseAbort(self): + self.notify.debug('GOLF COURSE: setGameAbort') + self.normalExit = 0 + self.sendUpdate('setCourseAbort', [0]) + self.safeDemand('Cleanup') + + def enterOff(self): + self.notify.debug('GOLF COURSE: enterOff') + + def exitOff(self): + self.notify.debug('GOLF COURSE: exitOff') + + def enterWaitJoin(self): + self.notify.debug('GOLF COURSE: enterWaitJoin') + for avId in self.avIdList: + self.avStateDict[avId] = EXPECTED + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleExitedAvatar, extraArgs=[avId]) + + def allAvatarsJoined(self = self): + self.notify.debug('GOLF COURSE: all avatars joined') + self.load() + + def handleTimeout(avIds, self = self): + self.notify.debug('GOLF COURSE: timed out waiting for clients %s to join' % avIds) + for avId in self.avStateDict: + if not self.avStateDict[avId] == JOINED: + self.handleExitedAvatar(avId) + + if self.haveAllGolfersExited(): + self.setCourseAbort() + else: + self.load() + + self.__barrier = ToonBarrier('waitClientsJoin', self.uniqueName('waitClientsJoin'), self.avIdList, JOIN_TIMEOUT, allAvatarsJoined, handleTimeout) + + def exitWaitJoin(self): + self.notify.debugStateCall(self) + self.__barrier.cleanup() + self.__barrier = None + return + + def setAvatarJoined(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('GOLF COURSE: setAvatarJoined: avatar id joined: ' + str(avId)) + self.avStateDict[avId] = JOINED + self.notify.debug('GOLF COURSE: setAvatarJoined: new states: ' + str(self.avStateDict)) + if hasattr(self, '_DistributedGolfCourseAI__barrier') and self.__barrier: + self.__barrier.clear(avId) + else: + self.notify.warning('setAvatarJoined avId=%d but barrier is invalid' % avId) + + def exitFrameworkWaitClientsJoin(self): + self.__barrier.cleanup() + del self.__barrier + + def enterWaitReadyCourse(self): + self.notify.debug('GOLF COURSE: enterWaitReadyCourse') + + def allAvatarsInCourse(self = self): + self.notify.debug('GOLF COURSE: all avatars ready course') + for avId in self.avIdList: + blankScoreList = [0] * self.numHoles + self.scores[avId] = blankScoreList + self.aimTimes[avId] = 0 + + self.notify.debug('self.scores = %s' % self.scores) + self.startNextHole() + + def handleTimeout(avIds, self = self): + self.notify.debug("GOLF COURSE: Course timed out waiting for clients %s to report 'ready'" % avIds) + if self.haveAllGolfersExited(): + self.setCourseAbort() + else: + allAvatarsInCourse() + + self.__barrier = ToonBarrier('WaitReadyCourse', self.uniqueName('WaitReadyCourse'), self.avIdList, READY_TIMEOUT, allAvatarsInCourse, handleTimeout) + for avId in self.avStateDict.keys(): + if self.avStateDict[avId] == READY: + self.__barrier.clear(avId) + + def setAvatarReadyCourse(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('GOLF COURSE: setAvatarReadyCourse: avatar id ready: ' + str(avId)) + self.avStateDict[avId] = READY + self.notify.debug('GOLF COURSE: setAvatarReadyCourse: new avId states: ' + str(self.avStateDict)) + if self.state == 'WaitReadyCourse': + self.__barrier.clear(avId) + + def exitWaitReadyCourse(self): + self.notify.debugStateCall(self) + self.__barrier.cleanup() + self.__barrier = None + return + + def enterWaitReadyHole(self): + self.notify.debug('GOLF COURSE: enterWaitReadyHole') + + def allAvatarsInHole(self = self): + self.notify.debug('GOLF COURSE: all avatars ready hole') + if self.safeDemand('PlayHole'): + self.d_setPlayHole() + + def handleTimeout(avIds, self = self): + self.notify.debug("GOLF COURSE: Hole timed out waiting for clients %s to report 'ready'" % avIds) + if self.haveAllGolfersExited(): + self.setCourseAbort() + elif self.safeDemand('PlayHole'): + self.d_setPlayHole() + + stillPlaying = self.getStillPlayingAvIds() + self.__barrier = ToonBarrier('WaitReadyHole', self.uniqueName('WaitReadyHole'), stillPlaying, READY_TIMEOUT, allAvatarsInHole, handleTimeout) + for avId in self.avStateDict.keys(): + if self.avStateDict[avId] == ONHOLE: + self.__barrier.clear(avId) + + def exitWaitReadyHole(self): + self.notify.debugStateCall(self) + if hasattr(self, '__barrier'): + self.__barrier.cleanup() + self.__barrier = None + return + + def getStillPlayingAvIds(self): + retval = [] + for avId in self.avIdList: + av = simbase.air.doId2do.get(avId) + if av: + if avId in self.avStateDict and not self.avStateDict[avId] == EXITED: + retval.append(avId) + + return retval + + def avatarReadyHole(self, avId): + if self.state not in ['WaitJoin', 'WaitReadyCourse', 'WaitReadyHole']: + self.notify.debug('GOLF COURSE: Ignoring setAvatarReadyHole message') + return + self.notify.debug('GOLF COURSE: setAvatarReadyHole: avatar id ready: ' + str(avId)) + self.avStateDict[avId] = ONHOLE + self.notify.debug('GOLF COURSE: setAvatarReadyHole: new avId states: ' + str(self.avStateDict)) + if self.state == 'WaitReadyHole': + self.__barrier.clear(avId) + + def enterPlayHole(self): + self.notify.debug('GOLF COURSE: enterPlayHole') + if self.currentHole and not self.currentHole.playStarted: + self.currentHole.startPlay() + + def exitPlayHole(self): + self.notify.debug('GOLF COURSE: exitPlayHole') + + def enterWaitLeaveHole(self): + self.notify.debugStateCall(self) + self.notify.debug('calling requestDelete on hole %d' % self.currentHole.doId) + self.currentHole.requestDelete() + self.currentHole = None + if self.numHolesPlayed >= self.numHoles: + pass + else: + self.startNextHole() + return + + def exitWaitLeaveHole(self): + pass + + def enterWaitReward(self): + self.updateHistoryForCourseComplete() + self.awardTrophies() + self.awardCups() + self.awardHoleBest() + self.awardCourseBest() + self.recordHoleInOne() + self.recordCourseUnderPar() + trophiesList = [] + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + if avId in self.newTrophies: + oneTrophyList = self.newTrophies[avId] + trophiesList.append(oneTrophyList) + else: + trophiesList.append([]) + + while len(trophiesList) < GolfGlobals.MAX_PLAYERS_PER_HOLE: + trophiesList.append([]) + + holeBestList = [] + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + if avId in self.newHoleBest: + oneTrophyList = self.newHoleBest[avId] + holeBestList.append(oneTrophyList) + else: + holeBestList.append([]) + + while len(holeBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE: + holeBestList.append([]) + + courseBestList = [] + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + if avId in self.newCourseBest: + oneTrophyList = self.newCourseBest[avId] + courseBestList.append(oneTrophyList) + else: + courseBestList.append([]) + + while len(courseBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE: + courseBestList.append([]) + + cupList = [] + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + if avId in self.newCups: + oneCupList = self.newCups[avId] + cupList.append(oneCupList) + self.cupListLen = self.cupListLen + 1 + else: + cupList.append([]) + + while len(cupList) < GolfGlobals.MAX_PLAYERS_PER_HOLE: + cupList.append([]) + + REWARD_TIMEOUT = (self.trophyListLen + self.holeBestListLen + self.courseBestListLen + self.cupListLen) * 5 + 19 + aimTimesList = [0] * 4 + aimIndex = 0 + stillPlaying = self.getStillPlayingAvIds() + for avId in self.avIdList: + if avId in stillPlaying: + aimTime = 0 + if avId in self.aimTimes: + aimTime = self.aimTimes[avId] + aimTimesList[aimIndex] = aimTime + aimIndex += 1 + + self.sendUpdate('setReward', [trophiesList, + self.rankings, + holeBestList, + courseBestList, + cupList, + self.winnerByTieBreak, + aimTimesList[0], + aimTimesList[1], + aimTimesList[2], + aimTimesList[3]]) + + def allAvatarsRewarded(self = self): + self.notify.debug('GOLF COURSE: all avatars rewarded') + self.rewardDone() + + def handleRewardTimeout(avIds, self = self): + self.notify.debug('GOLF COURSE: timed out waiting for clients %s to finish reward' % avIds) + self.rewardDone() + + stillPlaying = self.getStillPlayingAvIds() + self.rewardBarrier = ToonBarrier('waitReward', self.uniqueName('waitReward'), stillPlaying, REWARD_TIMEOUT, allAvatarsRewarded, handleRewardTimeout) + + def exitWaitReward(self): + pass + + def enterWaitLeaveCourse(self): + self.notify.debugStateCall(self) + self.setCourseAbort() + + def exitWaitLeaveCourse(self): + pass + + def enterCleanup(self): + self.notify.debug('GOLF COURSE: enterCleanup') + self.requestDelete() + + def exitCleanup(self): + self.notify.debug('GOLF COURSE: exitCleanup') + + def isCurHoleDone(self): + retval = False + if self.areAllBallsInHole(): + retval = True + else: + retval = True + for state in self.avStateDict.values(): + if not (state == BALLIN or state == EXITED): + retval = False + break + + return retval + + def areAllBallsInHole(self): + self.notify.debug('areAllBallsInHole, self.avStateDict=%s' % self.avStateDict) + allBallsInHole = True + for state in self.avStateDict.values(): + if state != BALLIN: + allBallsInHole = False + + return allBallsInHole + + def isPlayingLastHole(self): + retval = self.numHoles - self.numHolesPlayed == 1 + return retval + + def setBallIn(self, avId): + self.notify.debug('setBallIn %d' % avId) + if self.avStateDict[avId] == BALLIN: + self.notify.debug('setBallIn already in BALLIN state, just returning') + return + self.avStateDict[avId] = BALLIN + self.updateHistoryForBallIn(avId) + if self.isCurHoleDone(): + if self.isPlayingLastHole(): + if self.state != 'WaitReward': + self.safeDemand('WaitReward') + else: + self.notify.debug('allBalls are in holes, calling holeOver') + self.holeOver() + + def updateHistoryForBallIn(self, avId): + if self.currentHole == None: + return + holeId = self.currentHole.holeId + holeInfo = GolfGlobals.HoleInfo[holeId] + par = holeInfo['par'] + holeIndex = self.numHolesPlayed + if holeIndex >= self.numHoles: + self.notify.warning('updateHistoryForBallIn invalid holeIndex %d' % holeIndex) + holeIndex = self.numHoles - 1 + elif holeIndex < 0: + self.notify.warning('updateHistoryForBallIn invalid holeIndex %d' % holeIndex) + holeIndex = 0 + strokes = self.scores[avId][holeIndex] + self.notify.debug('self.scores = %s' % self.scores) + diff = strokes - par + if strokes == 1: + self.incrementEndingHistory(avId, GolfGlobals.HoleInOneShots) + if diff <= -2: + self.incrementEndingHistory(avId, GolfGlobals.EagleOrBetterShots) + if diff <= -1: + self.incrementEndingHistory(avId, GolfGlobals.BirdieOrBetterShots) + if diff <= 0: + self.endingHistory[avId][GolfGlobals.ParOrBetterShots] += 1 + if strokes < self.endingHoleBest[avId][holeId] or self.endingHoleBest[avId][holeId] == 0: + self.endingHoleBest[avId][holeId] = strokes + return + + def incrementEndingHistory(self, avId, historyIndex): + if avId in self.endingHistory and historyIndex in GolfGlobals.TrophyRequirements: + maximumAmount = GolfGlobals.TrophyRequirements[historyIndex][-1] + if self.endingHistory[avId][historyIndex] < maximumAmount: + self.endingHistory[avId][historyIndex] += 1 + + def getCourseId(self): + return self.courseId + + def abortCurrentHole(self): + holeId = self.currentHole.holeId + holeDoId = self.currentHole.doId + self.currentHole.finishHole() + return (holeId, holeDoId) + + def calcHolesToUse(self): + retval = [] + if simbase.air.config.GetBool('golf-course-randomized', 1): + retval = self.calcHolesToUseRandomized(self.courseId) + self.notify.debug('randomized courses!') + for x in xrange(len(retval)): + self.notify.debug('Hole is: %s' % retval[x]) + + else: + validHoles = self.calcUniqueHoles(self.courseId) + if self.preferredHoleId in validHoles: + retval.append(self.preferredHoleId) + while len(retval) < self.numHoles: + for holeId in GolfGlobals.CourseInfo[self.courseId]['holeIds']: + if type(holeId) == type(0): + retval.append(holeId) + elif type(holeId) == type(()): + retval.append(holeId[0]) + else: + self.notify.warning('cant handle %s' % self.holeId) + if len(retval) >= self.numHoles: + break + + return retval + + def incrementScore(self, avId): + self.notify.debug('incrementScore self.scores=%s avId=%s' % (self.scores, avId)) + self.scores[avId][self.numHolesPlayed] += 1 + self.notify.debug('after increment self.score=%s' % self.scores) + self.sendScores() + + def sendScores(self): + self.notify.debug('sendScores self.scores = %s' % self.scores) + scorelist = [] + for avId in self.avIdList: + for score in self.scores[avId]: + scorelist.append(score) + + self.sendUpdate('setScores', [scorelist]) + self.notify.debug('sendScores end self.scores = %s' % self.scores) + + def getCurHoleIndex(self): + return self.curHoleIndex + + def b_setCurHoleIndex(self, holeIndex): + self.setCurHoleIndex(holeIndex) + self.d_setCurHoleIndex(holeIndex) + + def d_setCurHoleIndex(self, holeIndex): + self.sendUpdate('setCurHoleIndex', [holeIndex]) + + def setCurHoleIndex(self, holeIndex): + self.curHoleIndex = holeIndex + + def setDoneReward(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('got rewardDone from %d' % avId) + if hasattr(self, 'rewardBarrier'): + self.rewardBarrier.clear(avId) + self.sendUpdate('setCourseAbort', [avId]) + + def rewardDone(self): + self.notify.debug('rewardDone') + self.holeOver() + self.safeDemand('WaitLeaveCourse') + + def updateHistoryForCourseComplete(self): + self.calcRankings() + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + self.incrementEndingHistory(avId, GolfGlobals.CoursesCompleted) + coursePar = self.calcCoursePar() + totalScore = self.getTotalScore(avId) + if totalScore < coursePar: + self.incrementEndingHistory(avId, GolfGlobals.CoursesUnderPar) + if len(stillPlaying) > 1: + self.incrementEndingHistory(avId, GolfGlobals.MultiPlayerCoursesCompleted) + if self.rankingsById[avId] == 1: + if self.courseId == 0: + self.incrementEndingHistory(avId, GolfGlobals.CourseZeroWins) + elif self.courseId == 1: + self.incrementEndingHistory(avId, GolfGlobals.CourseOneWins) + elif self.courseId == 2: + self.incrementEndingHistory(avId, GolfGlobals.CourseTwoWins) + else: + self.notify.warning('unhandled case, self.courseId=%s' % self.courseId) + if totalScore < self.endingCourseBest[avId][self.courseId] or self.endingCourseBest[avId][self.courseId] == 0: + self.endingCourseBest[avId][self.courseId] = totalScore + + def calcRankings(self): + stillPlaying = self.getStillPlayingAvIds() + self.rankings = [] + totalScores = [] + for avId in self.avIdList: + aimTime = 0 + if avId in self.aimTimes: + aimTime = self.aimTimes[avId] + if avId in stillPlaying: + totalScores.append((avId, self.getTotalScore(avId), aimTime)) + else: + totalScores.append((avId, 255, aimTime)) + + def scoreCompareNoTime(tupleA, tupleB): + if tupleA[1] > tupleB[1]: + return 1 + elif tupleA[1] == tupleB[1]: + return 0 + else: + return -1 + + def scoreCompareWithTime(tupleA, tupleB): + if tupleA[1] > tupleB[1]: + return 1 + elif tupleA[1] == tupleB[1]: + if tupleA[2] > tupleB[2]: + return 1 + elif tupleA[2] == tupleB[2]: + return 0 + else: + return -1 + else: + return -1 + + if GolfGlobals.TIME_TIE_BREAKER: + totalScores.sort(scoreCompareWithTime) + else: + totalScores.sort(scoreCompareNoTime) + curRank = 0 + oldScore = 0 + oldTime = 0 + self.rankingsById = {} + for scoreTuple in totalScores: + time = scoreTuple[2] + score = scoreTuple[1] + avId = scoreTuple[0] + if score > oldScore or GolfGlobals.TIME_TIE_BREAKER and score == oldScore and time > oldTime: + curRank += 1 + oldScore = score + oldTime = time + self.rankingsById[avId] = curRank + + tiedForFirst = [] + tempRank = 0 + oldScore = 0 + oldTime = 0 + for scoreTuple in totalScores: + time = scoreTuple[2] + score = scoreTuple[1] + avId = scoreTuple[0] + if score > oldScore: + tempRank += 1 + oldScore = score + oldTime = time + if tempRank == 1: + tiedForFirst.append(avId) + + for avId in self.avIdList: + if avId in stillPlaying: + self.rankings.append(self.rankingsById[avId]) + else: + self.rankings.append(-1) + + if len(tiedForFirst) >= 2 and not GolfGlobals.TIME_TIE_BREAKER: + winnerAvId = random.choice(tiedForFirst) + winnerIndex = self.avIdList.index(winnerAvId) + self.winnerByTieBreak = winnerAvId + for index in xrange(len(self.rankings)): + if self.rankings[index] > 0 and index != winnerIndex: + self.rankings[index] += 1 + + for avId in self.rankingsById: + if self.rankingsById[avId] > 0 and avId != winnerAvId: + self.rankingsById[avId] += 1 + + elif len(tiedForFirst) >= 2: + winnerAvId = totalScores[0][0] + self.winnerByTieBreak = winnerAvId + + def awardTrophies(self): + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + av = simbase.air.doId2do.get(avId) + if av: + oldHistory = self.startingHistory[avId] + endingHistory = self.endingHistory[avId] + oldTrophies = GolfGlobals.calcTrophyListFromHistory(oldHistory) + endingTrophies = GolfGlobals.calcTrophyListFromHistory(endingHistory) + av.b_setGolfHistory(endingHistory) + av.addStat(ToontownGlobals.STAT_GOLF) + newTrophies = [] + for index in xrange(len(oldTrophies)): + if not oldTrophies[index] and endingTrophies[index]: + self.notify.debug('New Trophy %d' % index) + self.air.writeServerEvent('golf_trophy', avId, '%s' % index) + newTrophies.append(True) + self.trophyListLen = self.trophyListLen + 1 + else: + newTrophies.append(False) + + self.newTrophies[avId] = newTrophies + + def awardCups(self): + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + av = simbase.air.doId2do.get(avId) + if av: + oldHistory = self.startingHistory[avId] + endingHistory = self.endingHistory[avId] + oldCups = GolfGlobals.calcCupListFromHistory(oldHistory) + endingCups = GolfGlobals.calcCupListFromHistory(endingHistory) + newCups = [] + for index in xrange(len(oldCups)): + if not oldCups[index] and endingCups[index]: + self.notify.debug('New Trophy %d' % index) + newCups.append(True) + self.air.writeServerEvent('golf_cup', avId, '%s' % index) + newMaxHp = av.getMaxHp() + 1 + av.b_setMaxHp(newMaxHp) + av.toonUp(newMaxHp) + else: + newCups.append(False) + + self.newCups[avId] = newCups + + def awardHoleBest(self): + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + av = simbase.air.doId2do.get(avId) + if av: + oldHoleBest = self.startingHoleBest[avId] + endingHoleBest = self.endingHoleBest[avId] + av.b_setGolfHoleBest(endingHoleBest) + newHoleBest = [] + longestHoleBestList = 0 + for index in xrange(len(oldHoleBest)): + if endingHoleBest[index] < oldHoleBest[index]: + self.notify.debug('New HoleBest %d' % index) + newHoleBest.append(True) + longestHoleBestList = longestHoleBestList + 1 + else: + newHoleBest.append(False) + + if longestHoleBestList > self.holeBestListLen: + self.holeBestListLen = longestHoleBestList + self.newHoleBest[avId] = newHoleBest + + def awardCourseBest(self): + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + av = simbase.air.doId2do.get(avId) + if av: + oldCourseBest = self.startingCourseBest[avId] + endingCourseBest = self.endingCourseBest[avId] + av.b_setGolfCourseBest(endingCourseBest) + newCourseBest = [] + longestCourseBestList = 0 + for index in xrange(len(oldCourseBest)): + if endingCourseBest[index] < oldCourseBest[index]: + self.notify.debug('New CourseBest %d' % index) + newCourseBest.append(True) + longestCourseBestList = longestCourseBestList + 1 + else: + newCourseBest.append(False) + + if longestCourseBestList > self.courseBestListLen: + self.courseBestListLen = longestCourseBestList + self.newCourseBest[avId] = newCourseBest + + def haveAllGolfersExited(self): + retval = True + for avId in self.avStateDict: + if not self.avStateDict[avId] == EXITED: + retval = False + break + + return retval + + def getCurHoleDoId(self): + retval = 0 + if self.currentHole: + retval = self.currentHole.doId + return retval + + def d_setCurHoleDoId(self, curHoleDoId): + self.sendUpdate('setCurHoleDoId', [curHoleDoId]) + + def calcCoursePar(self): + retval = 0 + for holeId in self.holeIds: + holeInfo = GolfGlobals.HoleInfo[holeId] + retval += holeInfo['par'] + + return retval + + def getTotalScore(self, avId): + retval = 0 + if avId in self.scores: + for holeScore in self.scores[avId]: + retval += holeScore + + return retval + + def getCurHoleScore(self, avId): + retval = 0 + if avId in self.scores and self.numHolesPlayed < len(self.scores[avId]): + retval = self.scores[avId][self.numHolesPlayed] + return retval + + def toggleDrivePermission(self, avId): + if avId in self.drivingToons: + self.drivingToons.remove(avId) + self.sendUpdate('changeDrivePermission', [avId, 0]) + retval = False + else: + self.drivingToons.append(avId) + self.sendUpdate('changeDrivePermission', [avId, 1]) + retval = True + return retval + + def safeDemand(self, newState): + doingDemand = False + if self.state == 'Cleanup': + pass + else: + if self.state in self.defaultTransitions: + if newState in self.defaultTransitions[self.state]: + self.demand(newState) + doingDemand = True + elif self.state == None: + self.demand(newState) + doingDemand = True + if not doingDemand: + self.notify.warning('doId=%d ignoring demand from %s to %s' % (self.doId, self.state, newState)) + return doingDemand + + def setAvatarExited(self): + avId = self.air.getAvatarIdFromSender() + self.handleExitedAvatar(avId) + + def createChoicesList(self, courseId, possibleHoles): + retval = [] + holeIds = GolfGlobals.CourseInfo[courseId]['holeIds'] + for holeOrTuple in holeIds: + if type(holeOrTuple) == type(()): + holeId = holeOrTuple[0] + weight = holeOrTuple[1] + elif type(holeOrTuple) == type(0): + holeId = holeOrTuple + weight = 1 + else: + self.notify.warning('cant handle %s' % holeOrTuple) + continue + if holeId in possibleHoles: + retval += [holeId] * weight + + return retval + + def calcUniqueHoles(self, courseId): + uniqueHoles = set() + for holeOrTuple in GolfGlobals.CourseInfo[courseId]['holeIds']: + if type(holeOrTuple) == type(()): + uniqueHoles.add(holeOrTuple[0]) + elif type(holeOrTuple) == type(0): + uniqueHoles.add(holeOrTuple) + else: + self.notify.warning('cant handle %s' % holeOrTuple) + + return uniqueHoles + + def calcHolesToUseRandomized(self, courseId): + retval = [] + numHoles = GolfGlobals.CourseInfo[courseId]['numHoles'] + uniqueHoles = self.calcUniqueHoles(courseId) + curHolesChosen = set() + while len(retval) < numHoles: + if uniqueHoles == curHolesChosen: + curHolesChosen = set() + possibleHoles = uniqueHoles - curHolesChosen + choicesList = self.createChoicesList(courseId, possibleHoles) + if not self.preferredHoleId == None and self.preferredHoleId in choicesList and self.preferredHoleId not in curHolesChosen: + holeChosen = self.preferredHoleId + else: + holeChosen = random.choice(choicesList) + retval.append(holeChosen) + curHolesChosen.add(holeChosen) + + return retval + + def recordHoleInOne(self): + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + scoreList = self.scores[avId] + ns = 0 + for holeIndex in xrange(len(scoreList)): + strokes = scoreList[holeIndex] + if strokes == 1: + ns +=1 + holeId = self.holeIds[holeIndex] + self.air.writeServerEvent('golf_ace', avId, '%d|%d|%s' % (self.courseId, holeId, stillPlaying)) + + if ns: + messenger.send('topToonsManager-event', [avId, TopToonsGlobals.CAT_HOLE_IN_ONE, ns]) + + def recordCourseUnderPar(self): + coursePar = self.calcCoursePar() + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + totalScore = self.getTotalScore(avId) + netScore = totalScore - coursePar + if netScore < 0: + self.air.writeServerEvent('golf_underPar', avId, '%d|%d|%s' % (self.courseId, netScore, stillPlaying)) + messenger.send('topToonsManager-event', [avId, TopToonsGlobals.CAT_COURSE_UNDER_PAR, 1]) + + def addAimTime(self, avId, aimTime): + if avId in self.aimTimes: + self.aimTimes[avId] += aimTime diff --git a/toontown/golf/DistributedGolfHole.py b/toontown/golf/DistributedGolfHole.py new file mode 100755 index 00000000..64070363 --- /dev/null +++ b/toontown/golf/DistributedGolfHole.py @@ -0,0 +1,1669 @@ +import math +import random +import time +from pandac.PandaModules import TextNode, BitMask32, Point3, Vec3, Vec4, deg2Rad, Mat3, NodePath, VBase4, OdeTriMeshData, OdeTriMeshGeom, OdeRayGeom, CollisionTraverser, CollisionSegment, CollisionNode, CollisionHandlerQueue +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from otp.otpbase import OTPGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownTimer +from direct.gui.DirectGui import DirectWaitBar, DGG, DirectLabel +from direct.task import Task +from direct.fsm.FSM import FSM +from toontown.minigame import ArrowKeys +from direct.showbase import PythonUtil +from toontown.golf import BuildGeometry +from toontown.golf import DistributedPhysicsWorld +from toontown.golf import GolfGlobals +from direct.interval.IntervalGlobal import Sequence, Parallel, LerpScaleInterval, LerpFunctionInterval, Func, Wait, SoundInterval, ParallelEndTogether, LerpPosInterval, ActorInterval, LerpPosHprInterval, LerpColorScaleInterval, WaitInterval +from direct.actor import Actor +from toontown.golf import GolfHoleBase +from toontown.distributed import DelayDelete + +class DistributedGolfHole(DistributedPhysicsWorld.DistributedPhysicsWorld, FSM, GolfHoleBase.GolfHoleBase): + defaultTransitions = {'Off': ['Cleanup', 'ChooseTee', 'WatchTee'], + 'ChooseTee': ['Aim', 'Cleanup'], + 'WatchTee': ['WatchAim', + 'Cleanup', + 'WatchTee', + 'ChooseTee', + 'Aim'], + 'Wait': ['Aim', + 'WatchAim', + 'Playback', + 'Cleanup', + 'ChooseTee', + 'WatchTee'], + 'Aim': ['Shoot', + 'Playback', + 'Cleanup', + 'Aim', + 'WatchAim'], + 'WatchAim': ['WatchAim', + 'WatchShoot', + 'Playback', + 'Cleanup', + 'Aim', + 'ChooseTee', + 'WatchTee'], + 'Playback': ['Wait', + 'Aim', + 'WatchAim', + 'Cleanup', + 'ChooseTee', + 'WatchTee'], + 'Cleanup': ['Off']} + id = 0 + notify = directNotify.newCategory('DistributedGolfHole') + unlimitedAimTime = base.config.GetBool('unlimited-aim-time', 0) + unlimitedTeeTime = base.config.GetBool('unlimited-tee-time', 0) + golfPowerSpeed = base.config.GetDouble('golf-power-speed', 3) + golfPowerExponent = base.config.GetDouble('golf-power-exponent', 0.75) + DefaultCamP = -16 + MaxCamP = -90 + + def __init__(self, cr): + self.notify.debug('Hole Init') + DistributedPhysicsWorld.DistributedPhysicsWorld.__init__(self, base.cr) + GolfHoleBase.GolfHoleBase.__init__(self, 1) + FSM.__init__(self, 'Golf_%s_FSM' % self.id) + self.currentGolfer = 0 + self.ballDict = {} + self.ballShadowDict = {} + self.holeNodes = [] + self.golfCourse = None + self.golfCourseRequest = None + self.holePositions = [] + self.timer = None + self.teeTimer = None + self.aimStart = None + self.titleLabel = None + self.teeInstructions = None + self.aimInstructions = None + self.powerReminder = None + self.lastTimeHeadingSent = 0 + self.lastTempHeadingSent = 0 + self.holdCycleTime = 0.0 + self.inPlayBack = 0 + self.swingInterval = None + self.sfxInterval = None + self.isLookingAtPutt = False + self.clubs = {} + self.camInterval = None + self.flyOverInterval = None + self.needToDoFlyOver = True + self.translucentLastFrame = [] + self.translucentCurFrame = [] + self.localMissedSwings = 0 + self.localToonHitControl = False + self.warningInterval = None + self.playBackDelayDelete = None + self.aimMomentum = 0.0 + self.lastBumpSfxPos = Point3(0, 0, 0) + self.__textGen = TextNode('golfHoleText') + self.__textGen.setFont(ToontownGlobals.getSignFont()) + self.__textGen.setAlign(TextNode.ACenter) + if TTLocalizer.getLanguage() in ['Castillian', + 'Japanese', + 'German', + 'Portuguese', + 'French']: + self.__textGen.setGlyphScale(0.7) + self.avIdList = [] + self.enterAimStart = 0 + return + + def generate(self): + self.notify.debug('Hole Generate') + DistributedPhysicsWorld.DistributedPhysicsWorld.generate(self) + self.golfPowerTaskName = self.uniqueName('updateGolfPower') + + def announceGenerate(self): + DistributedPhysicsWorld.DistributedPhysicsWorld.announceGenerate(self) + self.setup() + self.sendReady() + self.request('Off') + index = 1 + for avId in self.avIdList: + self.createBall(avId, index) + self.createClub(avId) + index += 1 + + if self.avIdList: + avId = self.avIdList[0] + self.currentGolfer = avId + self.currentGolferActive = False + + def delete(self): + self.removePlayBackDelayDelete() + self.request('Cleanup') + taskMgr.remove(self.golfPowerTaskName) + DistributedPhysicsWorld.DistributedPhysicsWorld.delete(self) + GolfHoleBase.GolfHoleBase.delete(self) + if hasattr(self, 'perfectIval'): + self.perfectIval.pause() + del self.perfectIval + self.golfCourse = None + if self.teeInstructions: + self.teeInstructions.destroy() + self.teeInstructions = None + if self.aimInstructions: + self.aimInstructions.destory() + self.aimInstructions = None + if self.powerReminder: + self.powerReminder.destroy() + self.powerReminder = None + if self.swingInterval: + self.swingInterval.pause() + self.swingInterval = None + if self.sfxInterval: + self.sfxInterval.pause() + self.sfxInterval = None + if self.camInterval: + self.camInterval.pause() + self.camInterval = None + for club in self.clubs: + self.clubs[club].removeNode() + + del self.clubs + if hasattr(self, 'scoreBoard'): + if hasattr(self.scoreBoard, 'maximizeB'): + if self.scoreBoard.maximizeB: + self.scoreBoard.maximizeB.hide() + if not self.titleLabel == None: + self.titleLabel.destroy() + self.notify.debug('Deleted title label') + self.notify.debug('Delete function') + if self.flyOverInterval: + self.flyOverInterval.pause() + self.flyOverInterval = None + for key in self.ballShadowDict: + self.ballShadowDict[key].removeNode() + + self.dropShadowModel.removeNode() + return + + def sendReady(self): + self.sendUpdate('setAvatarReadyHole', []) + + def createClub(self, avId): + club = NodePath('club-%s' % avId) + clubModel = loader.loadModel('phase_6/models/golf/putter') + clubModel.reparentTo(club) + clubModel.setR(clubModel, 45) + self.clubs[avId] = club + + def attachClub(self, avId, pointToBall = False): + club = self.clubs[avId] + if club: + av = base.cr.doId2do.get(avId) + if av: + av.useLOD(1000) + lHand = av.getLeftHands()[0] + club.setPos(0, 0, 0) + club.reparentTo(lHand) + netScale = club.getNetTransform().getScale()[1] + counterActToonScale = lHand.find('**/counteractToonScale') + if counterActToonScale.isEmpty(): + counterActToonScale = lHand.attachNewNode('counteractToonScale') + counterActToonScale.setScale(1 / netScale) + self.notify.debug('creating counterActToonScale for %s' % av.getName()) + club.reparentTo(counterActToonScale) + club.setX(-0.25 * netScale) + if pointToBall: + club.lookAt(self.clubLookatSpot) + + def createToonRay(self): + self.toonRay = OdeRayGeom(self.space, 10.0) + self.toonRay.setCollideBits(BitMask32(16777215)) + self.toonRay.setCategoryBits(BitMask32(0)) + self.toonRay.setRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1)) + self.space.setCollideId(self.toonRay, GolfGlobals.TOON_RAY_COLLIDE_ID) + self.rayList.append(self.toonRay) + + def createSkyRay(self): + self.skyRay = OdeRayGeom(self.space, 100.0) + self.skyRay.setCollideBits(BitMask32(240)) + self.skyRay.setCategoryBits(BitMask32(0)) + self.skyRay.setRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1)) + self.space.setCollideId(self.skyRay, 78) + self.rayList.append(self.skyRay) + + def createCameraRay(self): + self.cameraRay = OdeRayGeom(self.space, 30.0) + self.cameraRay.setCollideBits(BitMask32(8388608)) + self.cameraRay.setCategoryBits(BitMask32(0)) + self.space.setCollideId(self.cameraRay, GolfGlobals.CAMERA_RAY_COLLIDE_ID) + self.cameraRayNodePath = self.terrainModel.attachNewNode('cameraRayNodePath') + self.rayList.append(self.cameraRay) + + def loadLevel(self): + GolfHoleBase.GolfHoleBase.loadLevel(self) + self.teeNodePath = self.terrainModel.find('**/tee0') + if self.teeNodePath.isEmpty(): + teePos = Vec3(0, 0, 10) + else: + teePos = self.teeNodePath.getPos() + teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS) + self.notify.debug('teeNodePath heading = %s' % self.teeNodePath.getH()) + self.teePositions = [teePos] + teeIndex = 1 + teeNode = self.terrainModel.find('**/tee%d' % teeIndex) + while not teeNode.isEmpty(): + teePos = teeNode.getPos() + teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS) + self.teePositions.append(teePos) + self.notify.debug('teeNodeP heading = %s' % teeNode.getH()) + teeIndex += 1 + teeNode = self.terrainModel.find('**/tee%d' % teeIndex) + + self.holeBottomNodePath = self.terrainModel.find('**/holebottom0') + if self.holeBottomNodePath.isEmpty(): + self.holeBottomPos = Vec3(*self.holeInfo['holePos'][0]) + else: + self.holeBottomPos = self.holeBottomNodePath.getPos() + self.holePositions.append(self.holeBottomPos) + minHard = Point3(0, 0, 0) + maxHard = Point3(0, 0, 0) + self.hardSurfaceNodePath.calcTightBounds(minHard, maxHard) + centerX = (minHard[0] + maxHard[0]) / 2.0 + centerY = (minHard[1] + maxHard[1]) / 2.0 + heightX = (centerX - minHard[0]) / math.tan(deg2Rad(23)) + heightY = (centerY - minHard[1]) / math.tan(deg2Rad(18)) + height = max(heightX, heightY) + self.camTopViewPos = Point3(centerX, centerY, height) + self.camTopViewHpr = Point3(0, -90, 0) + self.createRays() + self.createToonRay() + self.createCameraRay() + + def createLocatorDict(self): + self.locDict = {} + locatorNum = 1 + curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum) + while not curNodePath.isEmpty(): + self.locDict[locatorNum] = curNodePath + locatorNum += 1 + curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum) + + def loadBlockers(self): + loadAll = base.config.GetBool('golf-all-blockers', 0) + self.createLocatorDict() + self.blockerNums = self.holeInfo['blockers'] + for locatorNum in self.locDict: + if locatorNum in self.blockerNums or loadAll: + locator = self.locDict[locatorNum] + locatorParent = locator.getParent() + locator.getChildren().wrtReparentTo(locatorParent) + else: + self.locDict[locatorNum].removeNode() + + self.hardSurfaceNodePath.flattenStrong() + + def loadSounds(self): + self.hitBallSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hit_Ball.ogg') + self.holeInOneSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hole_In_One.ogg') + self.holeInTwoPlusSfx = loader.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_fall.ogg') + self.ballGoesInStartSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Ball_Goes_In_Start.ogg') + self.ballGoesInLoopSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Ball_Goes_In_Loop.ogg') + self.ballGoesToRestSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Ball_Rest_In_Cup.ogg') + self.kickedOutSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Sad_Noise_Kicked_Off_Hole.ogg') + self.crowdBuildupSfx = [] + self.crowdApplauseSfx = [] + self.crowdMissSfx = [] + for i in xrange(4): + self.crowdBuildupSfx.append(loader.loadSfx('phase_6/audio/sfx/Golf_Crowd_Buildup.ogg')) + self.crowdApplauseSfx.append(loader.loadSfx('phase_6/audio/sfx/Golf_Crowd_Applause.ogg')) + self.crowdMissSfx.append(loader.loadSfx('phase_6/audio/sfx/Golf_Crowd_Miss.ogg')) + + self.bumpHardSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hit_Barrier_3.ogg') + self.bumpMoverSfx = loader.loadSfx('phase_4/audio/sfx/Golf_Hit_Barrier_2.ogg') + self.bumpWindmillSfx = loader.loadSfx('phase_4/audio/sfx/Golf_Hit_Barrier_1.ogg') + + def setup(self): + self.notify.debug('setup golf hole') + self.loadLevel() + self.loadSounds() + self.camMove = 0 + self.arrowKeys = ArrowKeys.ArrowKeys() + self.arrowKeys.setPressHandlers([None, + None, + self.__leftArrowPressed, + self.__rightArrowPressed, + self.__beginTossGolf]) + self.arrowKeys.setReleaseHandlers([None, + None, + None, + None, + self.__endTossGolf]) + self.targets = render.attachNewNode('targetGameTargets') + self.ballFollow = render.attachNewNode('nodeAtBall') + self.startingTeeHeading = self.teeNodePath.getH() + self.ballFollow.setH(self.startingTeeHeading) + self.ballFollowToonSpot = self.ballFollow.attachNewNode('toonAimSpot') + self.ballFollowToonSpot.setX(-2.0) + self.ballFollowToonSpot.setY(0) + self.ballFollowToonSpot.setH(-90) + self.clubLookatSpot = self.ballFollow.attachNewNode('clubLookat') + self.clubLookatSpot.setY(-(GolfGlobals.GOLF_BALL_RADIUS + 0.1)) + camera.reparentTo(self.ballFollow) + self.camPosBallFollow = Point3(0.0, -23.0, 12.0) + self.camHprBallFollow = Point3(0, -16.0, 0) + camera.setPos(self.camPosBallFollow) + camera.setHpr(self.camHprBallFollow) + if self.holeBottomNodePath.isEmpty(): + holePositions = self.holePositions + for index in xrange(len(holePositions)): + holePos = holePositions[index] + targetNodePathGeom, t1, t2 = BuildGeometry.addCircleGeom(self.targets, 16, 1) + targetNodePathGeom.setPos(holePos) + targetNodePathGeom.setBin('ground', 0) + targetNodePathGeom.setDepthWrite(False) + targetNodePathGeom.setDepthTest(False) + targetNodePathGeom.setTransparency(TransparencyAttrib.MAlpha) + targetNodePathGeom.setColorScale(0.0, 0.0, 0.0, 1.0) + self.holeNodes.append(targetNodePathGeom) + holeSphere = CollisionSphere(0, 0, 0, 1) + holeSphere.setTangible(1) + holeCNode = CollisionNode('Hole') + holeCNode.addSolid(holeSphere) + holeC = targetNodePathGeom.attachNewNode(holeCNode) + holeC.show() + + holeC.setCollideMask(ToontownGlobals.PieBitmask) + toon = base.localAvatar + toon.setPos(0.0, 0.0, -100.0) + toon.b_setAnimState('neutral', 1.0) + self.pollingCtrl = 0 + self.timeLastCtrl = 0.0 + self.powerBar = DirectWaitBar(guiId='launch power bar', pos=(0.0, 0, -0.65), relief=DGG.SUNKEN, frameSize=(-2.0, + 2.0, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=0.25, range=100, sortOrder=50, frameColor=(0.5, 0.5, 0.5, 0.5), barColor=(1.0, 0.0, 0.0, 1.0), text='', text_scale=0.26, text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05)) + self.power = 0 + self.powerBar['value'] = self.power + self.powerBar.hide() + self.accept('tab', self.tabKeyPressed) + self.putAwayAllToons() + base.transitions.irisOut(t=0) + self.dropShadowModel = loader.loadModel('phase_3/models/props/drop_shadow') + self.dropShadowModel.setColor(0, 0, 0, 0.5) + self.dropShadowModel.flattenMedium() + self.dropShadowModel.hide() + return + + def switchToAnimState(self, animStateName, forced = False): + curAnimState = base.localAvatar.animFSM.getCurrentState() + curAnimStateName = '' + if curAnimState: + curAnimStateName = curAnimState.getName() + if curAnimStateName != animStateName or forced: + base.localAvatar.b_setAnimState(animStateName) + + def __aimTask(self, task): + self.attachClub(self.currentGolfer, True) + x = -math.sin(self.ballFollow.getH() * 0.0174532925) + y = math.cos(self.ballFollow.getH() * 0.0174532925) + dt = globalClock.getDt() + b = self.curGolfBall() + forceMove = 500 + forceMoveDt = forceMove * dt + posUpdate = False + momentumChange = dt * 60.0 + if (self.arrowKeys.upPressed() or self.arrowKeys.downPressed()) and not self.golfCourse.canDrive(self.currentGolfer): + posUpdate = True + self.aimMomentum = 0.0 + self.ballFollow.headsUp(self.holeBottomNodePath) + elif self.arrowKeys.rightPressed() and not self.arrowKeys.leftPressed(): + self.aimMomentum -= momentumChange + if self.aimMomentum > 0: + self.aimMomentum = 0.0 + elif self.aimMomentum < -30.0: + self.aimMomentum = -30.0 + posUpdate = True + self.switchToAnimState('GolfRotateLeft') + self.scoreBoard.hide() + elif self.arrowKeys.leftPressed() and not self.arrowKeys.rightPressed(): + self.aimMomentum += momentumChange + if self.aimMomentum < 0.0: + self.aimMomentum = 0.0 + elif self.aimMomentum > 30.0: + self.aimMomentum = 30.0 + posUpdate = True + self.switchToAnimState('GolfRotateRight') + self.scoreBoard.hide() + else: + self.aimMomentum = 0.0 + self.switchToAnimState('GolfPuttLoop') + self.ballFollow.setH(self.ballFollow.getH() + self.aimMomentum * dt) + if self.arrowKeys.upPressed() and self.golfCourse.canDrive(self.currentGolfer): + b.enable() + b.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0)) + if self.arrowKeys.downPressed() and self.golfCourse.canDrive(self.currentGolfer): + b.enable() + b.addForce(Vec3(-x * forceMoveDt, -y * forceMoveDt, 0)) + if self.arrowKeys.leftPressed() and self.arrowKeys.rightPressed() and self.golfCourse.canDrive(self.currentGolfer): + b.enable() + b.addForce(Vec3(0, 0, 3000 * dt)) + if posUpdate: + if globalClock.getFrameTime() - self.lastTimeHeadingSent > 0.2: + self.sendUpdate('setTempAimHeading', [localAvatar.doId, self.ballFollow.getH()]) + self.lastTimeHeadingSent = globalClock.getFrameTime() + self.lastTempHeadingSent = self.ballFollow.getH() + elif self.lastTempHeadingSent != self.ballFollow.getH(): + self.sendUpdate('setTempAimHeading', [localAvatar.doId, self.ballFollow.getH()]) + self.lastTimeHeadingSent = globalClock.getFrameTime() + self.lastTempHeadingSent = self.ballFollow.getH() + self.setCamera2Ball() + self.fixCurrentGolferFeet() + self.adjustClub() + self.orientCameraRay() + return task.cont + + def fixCurrentGolferFeet(self): + golfer = base.cr.doId2do.get(self.currentGolfer) + if not golfer: + return + golferPos = golfer.getPos(render) + newPos = Vec3(golferPos[0], golferPos[1], golferPos[2] + 5) + self.toonRay.setPosition(newPos) + + def adjustClub(self): + club = self.clubs[self.currentGolfer] + if club: + distance = club.getDistance(self.clubLookatSpot) + scaleFactor = distance / 2.058 + club.setScale(1, scaleFactor, 1) + + def resetPowerBar(self): + self.power = 0 + self.powerBar['value'] = self.power + self.powerBar['text'] = '' + + def sendSwingInfo(self): + kickHimOut = self.updateWarning() + if kickHimOut: + return + curAimTime = globalClock.getRealTime() - self.enterAimStart + if curAimTime < 0: + curAimTime = 0 + if curAimTime > GolfGlobals.AIM_DURATION: + curAimTime = GolfGlobals.AIM_DURATION + self.notify.debug('curAimTime = %f' % curAimTime) + x = -math.sin(self.ballFollow.getH() * 0.0174532925) + y = math.cos(self.ballFollow.getH() * 0.0174532925) + b = self.curGolfBall() + if hasattr(base, 'golfPower') and base.golfPower != None: + self.power = float(base.golfPower) + if not self.swingInfoSent: + self.sendUpdate('postSwingState', [self.getCycleTime(), + self.power, + b.getPosition()[0], + b.getPosition()[1], + b.getPosition()[2], + x, + y, + curAimTime, + self.getCommonObjectData()]) + self.swingInfoSent = True + if self.power < 15 and self.golfCourse.scores[localAvatar.doId][self.golfCourse.curHoleIndex] == 0: + self.powerReminder = DirectLabel(text=TTLocalizer.GolfPowerReminder, text_shadow=(0, 0, 0, 1), text_fg=VBase4(1, 1, 0.0, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.8), scale=0.12) + return + + def updateWarning(self): + retval = False + if not self.localToonHitControl: + self.localMissedSwings += 1 + else: + self.localMissedSwings = 0 + if self.localMissedSwings == GolfGlobals.KICKOUT_SWINGS - 1: + self.warningLabel = DirectLabel(parent=aspect2d, relief=None, pos=(0, 0, 0), text_align=TextNode.ACenter, text=TTLocalizer.GolfWarningMustSwing, text_scale=0.12, text_font=ToontownGlobals.getSignFont(), text_fg=(1, 0.1, 0.1, 1), text_wordwrap=20) + self.warningInterval = Sequence(LerpColorScaleInterval(self.warningLabel, 10, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='easeIn'), Func(self.warningLabel.destroy)) + self.warningInterval.start() + elif self.localMissedSwings >= GolfGlobals.KICKOUT_SWINGS: + self.golfCourse.handleFallingAsleepGolf(None) + retval = True + return retval + + def assignRecordSwing(self, avId, cycleTime, power, x, y, z, dirX, dirY, commonObjectData): + ball = self.ballDict[avId]['golfBall'] + holdBallPos = ball.getPosition() + self.useCommonObjectData(commonObjectData) + self.trackRecordBodyFlight(ball, cycleTime, power, Vec3(x, y, z), dirX, dirY) + ball.setPosition(holdBallPos) + self.sendUpdate('ballMovie2AI', [cycleTime, + avId, + self.recording, + self.aVRecording, + self.ballInHoleFrame, + self.ballTouchedHoleFrame, + self.ballFirstTouchedHoleFrame, + commonObjectData]) + self.ballMovie2Client(cycleTime, avId, self.recording, self.aVRecording, self.ballInHoleFrame, self.ballTouchedHoleFrame, self.ballFirstTouchedHoleFrame, commonObjectData) + + def __watchAimTask(self, task): + self.setCamera2Ball() + self.attachClub(self.currentGolfer, True) + self.adjustClub() + self.fixCurrentGolferFeet() + self.orientCameraRay() + return task.cont + + def __watchTeeTask(self, task): + self.setCamera2Ball() + return task.cont + + def curGolfBall(self): + return self.ballDict[self.currentGolfer]['golfBall'] + + def curGolfBallGeom(self): + return self.ballDict[self.currentGolfer]['golfBallGeom'] + + def curBallShadow(self): + return self.ballShadowDict[self.currentGolfer] + + def cleanupGeom(self): + self.targets.remove() + self.terrainModel.remove() + self.powerBar.destroy() + + def cleanupPowerBar(self): + self.powerBar.hide() + + def cleanupPhysics(self): + pass + + def curBall(self): + return self.ballDict[self.currentGolfer]['ball'] + + def curBallANP(self): + return self.ballDict[self.currentGolfer]['ballActorNodePath'] + + def curBallActor(self): + return self.ballDict[self.currentGolfer]['ballActor'] + + def enterAim(self): + self.notify.debug('Aim') + self.notify.debug('currentGolfer = %s' % self.currentGolfer) + self.switchToAnimState('GolfPuttLoop', forced=True) + self.swingInfoSent = False + self.lastState = self.state + self.aimMomentum = 0.0 + self.enterAimStart = globalClock.getRealTime() + taskMgr.add(self.__aimTask, 'Aim Task') + self.showOnlyCurGolfer() + strokes = self.golfCourse.getStrokesForCurHole(self.currentGolfer) + self.camPivot = self.ballFollow.attachNewNode('golf-camPivot') + self.targetCamPivot = self.ballFollow.attachNewNode('golf-targetCamPivot') + self.targetCamPivot.setP(self.DefaultCamP) + self.curCamPivot = self.ballFollow.attachNewNode('golf-curCamPivot') + self.curCamPivot.setP(self.DefaultCamP) + self.ccTrav = CollisionTraverser('golf.ccTrav') + self.ccLine = CollisionSegment(0.0, 0.0, 0.0, 1.0, 0.0, 0.0) + self.ccLineNode = CollisionNode('golf.ccLineNode') + self.ccLineNode.addSolid(self.ccLine) + self.ccLineNodePath = self.camPivot.attachNewNode(self.ccLineNode) + self.ccLineBitMask = BitMask32(1048576) + self.ccLineNode.setFromCollideMask(self.ccLineBitMask) + self.ccLineNode.setIntoCollideMask(BitMask32.allOff()) + self.camCollisionQueue = CollisionHandlerQueue() + self.ccTrav.addCollider(self.ccLineNodePath, self.camCollisionQueue) + if strokes: + self.ballFollow.headsUp(self.holeBottomNodePath) + self.camPivot.setP(self.DefaultCamP) + self._golfBarrierCollection = self.terrainModel.findAllMatches('**/collision?') + self._camAdjust = ScratchPad() + self._camAdjust.iters = 0 + self._camAdjust.lower = self.DefaultCamP + self._camAdjust.upper = self.MaxCamP + base.camera.setPos(self.camPosBallFollow) + base.camera.setHpr(self.camHprBallFollow) + self.camPivot.setP(self.DefaultCamP) + base.camera.wrtReparentTo(self.camPivot) + A = Point3(0, 0, 0) + B = base.camera.getPos() + AtoB = B - A + AtoBnorm = Point3(AtoB) + AtoBnorm.normalize() + A += AtoBnorm * 0.4 + self.ccLine.setPointA(A) + self.ccLine.setPointB(B) + self.camPivot.setP(self.DefaultCamP) + self._camAdjust.task = taskMgr.add(self._adjustCamera, 'adjustCamera') + self.resetPowerBar() + self.powerBar.show() + self.aimDuration = GolfGlobals.AIM_DURATION + if not self.unlimitedAimTime: + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(self.aimDuration) + self.timer.countdown(self.aimDuration, self.timerExpired) + self.aimInstructions = DirectLabel(text=TTLocalizer.GolfAimInstructions, text_shadow=(0, 0, 0, 1), text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, -0.8), scale=TTLocalizer.DGHaimInstructions) + self.skyContact = 1 + self.localToonHitControl = False + self._adjustCamera() + return + + def exitAim(self): + localAvatar.wrtReparentTo(render) + taskMgr.remove(self._camAdjust.task) + taskMgr.remove('Aim Task') + taskMgr.remove(self.golfPowerTaskName) + if self.timer: + self.timer.stop() + self.timer.destroy() + self.timer = None + self.powerBar.hide() + self.ccLineNodePath.detachNode() + self.targetCamPivot.detachNode() + self.curCamPivot.detachNode() + self.camPivot.detachNode() + if self.aimInstructions: + self.aimInstructions.destroy() + self.aimInstructions = None + return + + def timerExpired(self): + taskMgr.remove(self.golfPowerTaskName) + self.aimStart = None + self.sendSwingInfo() + self.resetPowerBar() + return + + def _adjustCamera(self, task=None, first=True): + if task is None and first: + while 1: + self._adjustCamera(first=False) + if self._camAdjust.iters == 0: + return Task.cont + + MaxIters = 5 + finalP = self._camAdjust.lower + + localAvatar.stash() + for barrier in self._golfBarrierCollection: + barrier.stash() + + self.ccTrav.traverse(render) + + for barrier in self._golfBarrierCollection: + barrier.unstash() + localAvatar.unstash() + + midP = (self._camAdjust.lower + self._camAdjust.upper)/2 + if self.camCollisionQueue.getNumEntries() > 0: + self.camCollisionQueue.sortEntries() + entry = self.camCollisionQueue.getEntry(0) + sPoint = entry.getSurfacePoint(self.camPivot) + self._camAdjust.lower = self.camPivot.getP() + finalP = midP + self.camPivot.setP(finalP) + else: + self._camAdjust.upper = self.camPivot.getP() + finalP = self._camAdjust.upper + self.camPivot.setP(midP) + if abs(self._camAdjust.lower - self._camAdjust.upper) < 1.0: + self._camAdjust.iters = MaxIters + + self._camAdjust.iters += 1 + if self._camAdjust.iters >= MaxIters: + self.targetCamPivot.setP(self._camAdjust.upper) + if task is None: + self.curCamPivot.setP(finalP) + self._camAdjust.iters = 0 + self._camAdjust.lower = self.DefaultCamP + self._camAdjust.upper = self.MaxCamP + self.camPivot.setP(self.DefaultCamP) + + if task is not None: + self.curCamPivot.setP(self.curCamPivot, + self.targetCamPivot.getP(self.curCamPivot)*min(1.0, 1.0*globalClock.getDt())) + + curP = self.curCamPivot.getP() + self.curCamPivot.setP(self.DefaultCamP) + base.camera.reparentTo(self.ballFollow) + base.camera.setPos(self.camPosBallFollow) + base.camera.setHpr(self.camHprBallFollow) + base.camera.wrtReparentTo(self.curCamPivot) + self.curCamPivot.setP(curP) + base.camera.wrtReparentTo(self.ballFollow) + + return Task.cont + + def enterChooseTee(self): + self.notify.debug('ChooseTee') + self.curGolfBallGeom().show() + self.curBallShadow().show() + self.lastState = self.state + taskMgr.add(self.__chooseTeeTask, 'ChooseTee Task') + self.ballFollow.setH(self.startingTeeHeading) + self.localAvatarChosenTee = False + self.localTempTee = 0 + if len(self.teePositions) > 1: + self.localTempTee = 1 + self.chooseTeeDuration = GolfGlobals.TEE_DURATION + if not self.unlimitedTeeTime: + self.teeTimer = ToontownTimer.ToontownTimer() + self.teeTimer.posInTopRightCorner() + self.teeTimer.setTime(self.chooseTeeDuration) + self.teeTimer.countdown(self.chooseTeeDuration, self.teeTimerExpired) + self.teeInstructions = DirectLabel(text=TTLocalizer.GolfChooseTeeInstructions, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, text_shadow=(0, 0, 0, 1), relief=None, pos=(0, 0, -0.75), scale=TTLocalizer.DGHteeInstructions) + self.powerBar.hide() + return + + def exitChooseTee(self): + localAvatar.wrtReparentTo(render) + if hasattr(self, 'teeInstructions') and self.teeInstructions: + self.teeInstructions.destroy() + self.teeInstructions = None + taskMgr.remove('ChooseTee Task') + taskMgr.remove(self.golfPowerTaskName) + if self.teeTimer: + self.teeTimer.stop() + self.teeTimer.destroy() + self.teeTimer = None + self.powerBar.show() + return + + def sendTeeInfo(self): + self.sendUpdate('setAvatarTee', [self.localTempTee]) + self.localAvatarChosenTee = True + + def __chooseTeeTask(self, task): + if self.localAvatarChosenTee: + return task.done + if self.arrowKeys.jumpPressed(): + if self.flyOverInterval and self.flyOverInterval.isPlaying(): + pass + else: + self.sendTeeInfo() + return task.cont + + def changeTee(self, newTee): + ball = self.curGolfBall() + ball.setPosition(self.teePositions[newTee]) + self.setCamera2Ball() + self.fixCurrentGolferFeet() + self.adjustClub() + + def changeLocalTee(self, newTee): + self.changeTee(newTee) + self.sendUpdate('setAvatarTempTee', [localAvatar.doId, newTee]) + self.fixCurrentGolferFeet() + self.adjustClub() + + def __leftArrowPressed(self): + if self.state != 'ChooseTee': + return + self.localTempTee -= 1 + if self.localTempTee < 0: + self.localTempTee = len(self.teePositions) - 1 + self.changeLocalTee(self.localTempTee) + + def __rightArrowPressed(self): + if self.state != 'ChooseTee': + return + self.localTempTee += 1 + self.localTempTee %= len(self.teePositions) + self.changeLocalTee(self.localTempTee) + + def teeTimerExpired(self): + self.sendTeeInfo() + + def enterWatchAim(self): + self.notify.debug('Watch Aim') + self.notify.debugStateCall(self) + self.notify.debug('currentGolfer = %s' % self.currentGolfer) + strokes = self.golfCourse.getStrokesForCurHole(self.currentGolfer) + if strokes: + self.ballFollow.lookAt(self.holeBottomNodePath) + self.ballFollow.setP(0) + self.showOnlyCurGolfer() + taskMgr.add(self.__watchAimTask, 'Watch Aim Task') + + def exitWatchAim(self): + self.notify.debugStateCall(self) + av = base.cr.doId2do.get(self.currentGolfer) + if av: + heading = av.getH(render) + toonPos = av.getPos(render) + av.reparentTo(render) + av.setH(heading) + av.setPos(toonPos) + self.notify.debug('av %s now at position %s' % (av.getName(), av.getPos())) + else: + self.notify.debug('could not get avId %d' % self.currentGolfer) + taskMgr.remove('Watch Aim Task') + + def enterWatchTee(self): + self.notify.debug('Watch Tee') + self.notify.debugStateCall(self) + self.curGolfBallGeom().show() + self.ballFollow.setH(self.startingTeeHeading) + self.ballShadowDict[self.currentGolfer].show() + + def exitWatchTee(self): + self.notify.debugStateCall(self) + av = base.cr.doId2do.get(self.currentGolfer) + taskMgr.remove('Watch Tee Task') + + def enterWait(self): + self.notify.debug('Wait') + self.notify.debugStateCall(self) + + def exitWait(self): + self.notify.debugStateCall(self) + + def removePlayBackDelayDelete(self): + if self.playBackDelayDelete: + self.playBackDelayDelete.destroy() + self.playBackDelayDelete = None + return + + def enterPlayback(self): + + def shiftClubToRightHand(): + club = self.clubs[self.currentGolfer] + av = base.cr.doId2do.get(self.currentGolfer) + if av and club: + club.wrtReparentTo(av.getRightHands()[0]) + + av = base.cr.doId2do.get(self.currentGolfer) + if not av: + return + else: + self.removePlayBackDelayDelete() + self.playBackDelayDelete = DelayDelete.DelayDelete(av, 'GolfHole.enterPlayback') + self.accept('clientCleanup', self._handleClientCleanup) + self.inPlayBack = 1 + self.setLookingAtPutt(False) + self.swingInterval = Sequence(ActorInterval(av, 'swing-putt', startFrame=0, endFrame=GolfGlobals.BALL_CONTACT_FRAME), Func(self.startBallPlayback), ActorInterval(av, 'swing-putt', startFrame=GolfGlobals.BALL_CONTACT_FRAME, endFrame=23), Func(shiftClubToRightHand), Func(self.setLookingAtPutt, True), Func(self.removePlayBackDelayDelete)) + adjustedBallTouchedHoleTime = self.ballTouchedHoleTime + GolfGlobals.BALL_CONTACT_TIME + adjustedBallFirstTouchedHoleTime = self.ballFirstTouchedHoleTime + GolfGlobals.BALL_CONTACT_TIME + adjustedBallDropTime = self.ballDropTime + GolfGlobals.BALL_CONTACT_TIME + adjustedPlaybackEndTime = self.playbackMovieDuration + GolfGlobals.BALL_CONTACT_TIME + self.notify.debug('adjustedTimes ballTouched=%.2f ballFirstTouched=%.2f ballDrop=%.2f playbaybackEnd=%.2f' % (adjustedBallTouchedHoleTime, + adjustedBallFirstTouchedHoleTime, + adjustedBallDropTime, + adjustedPlaybackEndTime)) + if self.ballWillGoInHole: + curDuration = self.swingInterval.getDuration() + lookPuttInterval = ActorInterval(av, 'look-putt') + if curDuration < adjustedBallDropTime: + self.swingInterval.append(lookPuttInterval) + curDuration = self.swingInterval.getDuration() + diffTime = adjustedBallDropTime - curDuration + if diffTime > 0: + self.swingInterval.append(ActorInterval(av, 'lookloop-putt', endTime=diffTime)) + self.swingInterval.append(ActorInterval(av, 'good-putt', endTime=self.playbackMovieDuration, loop=1)) + elif self.ballTouchedHoleTime: + self.notify.debug('doing self.ballTouchedHoleTime') + curDuration = self.swingInterval.getDuration() + lookPuttInterval = ActorInterval(av, 'look-putt') + if curDuration < adjustedBallTouchedHoleTime: + self.swingInterval.append(lookPuttInterval) + curDuration = self.swingInterval.getDuration() + diffTime = adjustedBallTouchedHoleTime - curDuration + if diffTime > 0: + self.swingInterval.append(ActorInterval(av, 'lookloop-putt', endTime=diffTime)) + self.swingInterval.append(ActorInterval(av, 'bad-putt', endFrame=32)) + self.swingInterval.append(ActorInterval(av, 'badloop-putt', endTime=self.playbackMovieDuration, loop=1)) + else: + self.swingInterval.append(ActorInterval(av, 'look-putt')) + self.swingInterval.append(ActorInterval(av, 'lookloop-putt', endTime=self.playbackMovieDuration, loop=1)) + sfxInterval = Parallel() + ballHitInterval = Sequence(Wait(GolfGlobals.BALL_CONTACT_TIME), SoundInterval(self.hitBallSfx)) + sfxInterval.append(ballHitInterval) + if self.ballWillGoInHole: + ballRattle = Sequence() + timeToPlayBallRest = adjustedPlaybackEndTime - self.ballGoesToRestSfx.length() + if adjustedBallFirstTouchedHoleTime < timeToPlayBallRest: + diffTime = timeToPlayBallRest - adjustedBallFirstTouchedHoleTime + if self.ballGoesInStartSfx.length() < diffTime: + ballRattle.append(Wait(adjustedBallFirstTouchedHoleTime)) + ballRattle.append(SoundInterval(self.ballGoesInStartSfx)) + timeToPlayLoop = adjustedBallFirstTouchedHoleTime + self.ballGoesInStartSfx.length() + loopTime = timeToPlayBallRest - timeToPlayLoop + if self.ballGoesInLoopSfx.length() == 0.0: + numLoops = 0 + else: + numLoops = int(loopTime / self.ballGoesInLoopSfx.length()) + self.notify.debug('numLoops=%d loopTime=%f' % (numLoops, loopTime)) + if loopTime > 0: + ballRattle.append(SoundInterval(self.ballGoesInLoopSfx, loop=1, duration=loopTime, seamlessLoop=True)) + ballRattle.append(SoundInterval(self.ballGoesToRestSfx)) + self.notify.debug('playing full rattling') + else: + self.notify.debug('playing abbreviated rattling') + timeToPlayBallGoesIn = adjustedBallFirstTouchedHoleTime + ballRattle.append(Wait(timeToPlayBallGoesIn)) + startTime = self.ballGoesInStartSfx.length() - diffTime + self.notify.debug('adjustedBallDropTime=%s diffTime=%s starTime=%s' % (adjustedBallDropTime, diffTime, startTime)) + ballRattle.append(SoundInterval(self.ballGoesInStartSfx, startTime=startTime)) + ballRattle.append(SoundInterval(self.ballGoesToRestSfx)) + else: + self.notify.debug('playing abbreviated ball goes to rest') + ballRattle.append(Wait(adjustedBallFirstTouchedHoleTime)) + diffTime = adjustedPlaybackEndTime - adjustedBallFirstTouchedHoleTime + startTime = self.ballGoesToRestSfx.length() - diffTime + self.notify.debug('adjustedBallDropTime=%s diffTime=%s starTime=%s' % (adjustedBallDropTime, diffTime, startTime)) + ballRattle.append(SoundInterval(self.ballGoesToRestSfx, startTime=startTime)) + sfxInterval.append(ballRattle) + crowdBuildupSfx = self.crowdBuildupSfx[self.avIdList.index(self.currentGolfer)] + crowdApplauseSfx = self.crowdApplauseSfx[self.avIdList.index(self.currentGolfer)] + crowdMissSfx = self.crowdMissSfx[self.avIdList.index(self.currentGolfer)] + if self.ballWillGoInHole: + crowdIval = Sequence() + buildupLength = crowdBuildupSfx.length() + self.notify.debug('buildupLength=%s' % buildupLength) + diffTime = adjustedBallFirstTouchedHoleTime - buildupLength + if diffTime > 0: + crowdIval.append(Wait(diffTime)) + crowdIval.append(SoundInterval(crowdBuildupSfx)) + crowdIval.append(SoundInterval(crowdApplauseSfx)) + else: + startTime = buildupLength - adjustedBallFirstTouchedHoleTime + self.notify.debug('playing abbreviated crowd build and applause diffTime=%s startTime=%s' % (diffTime, startTime)) + crowdIval.append(SoundInterval(crowdBuildupSfx, startTime=startTime)) + crowdIval.append(SoundInterval(crowdApplauseSfx)) + sfxInterval.append(crowdIval) + elif self.ballFirstTouchedHoleTime: + crowdIval = Sequence() + buildupLength = crowdBuildupSfx.length() + self.notify.debug('touched but not going in buildupLength=%s' % buildupLength) + diffTime = adjustedBallFirstTouchedHoleTime - buildupLength + if diffTime > 0: + self.notify.debug('waiting %.2f to play crowd buildup' % diffTime) + crowdIval.append(Wait(diffTime)) + crowdIval.append(SoundInterval(crowdBuildupSfx)) + crowdIval.append(SoundInterval(crowdMissSfx)) + else: + startTime = buildupLength - adjustedBallFirstTouchedHoleTime + self.notify.debug('playing abbreviated crowd build and miss diffTime=%s startTime=%s' % (diffTime, startTime)) + crowdIval.append(SoundInterval(crowdBuildupSfx, startTime=startTime)) + crowdIval.append(SoundInterval(crowdMissSfx)) + sfxInterval.append(crowdIval) + if self.sfxInterval: + sfxInterval.finish() + self.sfxInterval = sfxInterval + self.sfxInterval.start() + self.swingInterval.start() + + def exitPlayback(self): + self.notify.debug('Exiting Playback') + if self.swingInterval: + self.swingInterval.pause() + av = base.cr.doId2do.get(self.currentGolfer) + if av: + if self.ballWillGoInHole: + av.loop('good-putt', restart=0) + elif self.ballTouchedHoleTime: + pass + else: + av.loop('neutral') + self.setLookingAtPutt(False) + if av == base.localAvatar: + if self.ballWillGoInHole: + av.b_setAnimState('GolfGoodPutt') + elif self.ballTouchedHoleTime: + av.b_setAnimState('GolfBadPutt') + else: + av.b_setAnimState('neutral') + taskMgr.remove('playback task') + self.curGolfBall().disable() + self.readyCurrentGolfer(None) + self.inPlayBack = 0 + if self.powerReminder: + self.powerReminder.destroy() + self.powerReminder = None + return + + def setLookingAtPutt(self, newVal): + self.isLookingAtPutt = newVal + + def getLookingAtPutt(self): + return self.isLookingAtPutt + + def startBallPlayback(self): + self.playbackFrameNum = 0 + self.sourceFrame = self.recording[0] + self.destFrameNum = 1 + self.destFrame = self.recording[self.destFrameNum] + self.aVSourceFrame = self.aVRecording[0] + self.aVDestFrameNum = 1 + self.aVDestFrame = self.aVRecording[self.aVDestFrameNum] + self.inPlayBack = 2 + + def isCurBallInHole(self): + retval = False + ball = self.curGolfBall() + ballPos = ball.getPosition() + for holePos in self.holePositions: + displacement = ballPos - holePos + length = displacement.length() + self.notify.debug('hole %s length=%s' % (holePos, length)) + if length <= GolfGlobals.DistanceToBeInHole: + retval = True + break + + return retval + + def handleBallGoingInHole(self): + par = GolfGlobals.HoleInfo[self.holeId]['par'] + unlimitedSwing = False + av = base.cr.doId2do.get(self.currentGolfer) + if av: + unlimitedSwing = av.getUnlimitedSwing() + if not unlimitedSwing: + self.curGolfBall().setPosition(0, 0, -100) + self.ballShadowDict[self.currentGolfer].setPos(0, 0, -100) + self.ballShadowDict[self.currentGolfer].hide() + strokes = 3 + if self.golfCourse: + strokes = self.golfCourse.getStrokesForCurHole(self.currentGolfer) + else: + self.notify.warning('self.golfCourse is None') + diff = strokes - par + if diff > 0: + textStr = '+' + str(diff) + else: + textStr = diff + if strokes == 1: + textStr = TTLocalizer.GolfHoleInOne + elif diff in TTLocalizer.GolfShotDesc: + if self.ballWillGoInHole: + textStr = TTLocalizer.GolfShotDesc[diff] + perfectTextSubnode = hidden.attachNewNode(self.__genText(textStr)) + perfectText = hidden.attachNewNode('perfectText') + perfectTextSubnode.reparentTo(perfectText) + frame = self.__textGen.getCardActual() + offsetY = -abs(frame[2] + frame[3]) / 2.0 - 1.35 + perfectTextSubnode.setPos(0, 0, offsetY) + perfectText.setColor(1, 0.1, 0.1, 1) + + def fadeFunc(t, text = perfectText): + text.setColorScale(1, 1, 1, t) + + def destroyText(text = perfectText): + text.removeNode() + + animTrack = Sequence() + av = base.cr.doId2do.get(self.currentGolfer) + animTrack.append(Func(self.golfCourse.updateScoreBoard)) + textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5), Func(self.sendUpdate, 'turnDone', [])) + soundTrack = Sequence() + if strokes == 1: + soundTrack.append(SoundInterval(self.holeInOneSfx)) + elif self.hasCurGolferReachedMaxSwing and not self.ballWillGoInHole: + soundTrack.append(SoundInterval(self.kickedOutSfx)) + self.perfectIval = Parallel(textTrack, soundTrack, animTrack) + self.perfectIval.start() + + def __playbackTask(self, task): + return self.playBackFrame(task) + + def toonRayCollisionCallback(self, x, y, z): + if self.state not in ('Aim', 'WatchAim', 'ChooseTee', 'WatchTee'): + return + tempPath = render.attachNewNode('temp') + tempPath.setPos(x, y, z) + relPos = tempPath.getPos(self.ballFollowToonSpot) + av = base.cr.doId2do.get(self.currentGolfer) + if av: + zToUse = relPos[2] + if zToUse < 0 - GolfGlobals.GOLF_BALL_RADIUS: + zToUse = 0 - GolfGlobals.GOLF_BALL_RADIUS + av.setPos(0, 0, zToUse) + tempPath.removeNode() + + def preStep(self): + if self.currentGolferActive: + GolfHoleBase.GolfHoleBase.preStep(self) + + def postStep(self): + if self.currentGolferActive: + GolfHoleBase.GolfHoleBase.postStep(self) + DistributedPhysicsWorld.DistributedPhysicsWorld.postStep(self) + if self.inPlayBack == 2: + self.playBackFrame() + self.makeCurGolferLookAtBall() + elif self.state == 'Playback' and self.inPlayBack == 0: + self.request('Wait') + self.updateTranslucentObjects() + + def updateTranslucentObjects(self): + for translucentNodePathLastFrame in self.translucentLastFrame: + if translucentNodePathLastFrame not in self.translucentCurFrame: + translucentNodePathLastFrame.setColorScale(1, 1, 1, 1) + + for transNpCurFrame in self.translucentCurFrame: + if transNpCurFrame not in self.translucentLastFrame: + self.notify.debug('making translucent %s' % transNpCurFrame) + transNpCurFrame.setColorScale(1, 1, 1, 0.25) + transNpCurFrame.setTransparency(1) + + def makeCurGolferLookAtBall(self): + if self.getLookingAtPutt(): + av = base.cr.doId2do.get(self.currentGolfer) + if av: + ballPos = self.curGolfBall().getPosition() + av.headsUp(ballPos[0], ballPos[1], ballPos[2]) + av.setH(av.getH() - 90) + + def playBackFrame(self): + doPrint = 0 + doAVPrint = 0 + lastFrame = self.recording[len(self.recording) - 1][0] + if self.playbackFrameNum >= self.destFrame[0]: + self.sourceFrame = self.destFrame + self.destFrameNum += 1 + doPrint = 1 + if self.destFrameNum < len(self.recording): + self.destFrame = self.recording[self.destFrameNum] + else: + self.notify.debug('recording length %s' % len(self.recording)) + if self.isCurBallInHole() or self.hasCurGolferReachedMaxSwing(): + self.handleBallGoingInHole() + self.request('Wait') + else: + self.golfCourse.updateScoreBoard() + self.request('Wait') + self.sendUpdate('turnDone', []) + return + self.projLength = self.destFrame[0] - self.sourceFrame[0] + self.projPen = self.destFrame[0] - self.playbackFrameNum + propSource = float(self.projPen) / float(self.projLength) + propDest = 1.0 - propSource + projX = self.sourceFrame[1] * propSource + self.destFrame[1] * propDest + projY = self.sourceFrame[2] * propSource + self.destFrame[2] * propDest + projZ = self.sourceFrame[3] * propSource + self.destFrame[3] * propDest + newPos = Vec3(projX, projY, projZ) + ball = self.curGolfBall() + ball.setPosition(newPos) + if self.playbackFrameNum >= self.aVDestFrame[0]: + self.aVSourceFrame = self.aVDestFrame + self.aVDestFrameNum += 1 + doAVPrint = 1 + if self.aVDestFrameNum < len(self.aVRecording): + self.aVDestFrame = self.aVRecording[self.aVDestFrameNum] + newAV = Vec3(self.aVSourceFrame[1], self.aVSourceFrame[2], self.aVSourceFrame[3]) + self.projLength = self.aVDestFrame[0] - self.aVSourceFrame[0] + self.projPen = self.aVDestFrame[0] - self.playbackFrameNum + propSource = float(self.projPen) / float(self.projLength) + propDest = 1.0 - propSource + projX = self.aVSourceFrame[1] * propSource + self.aVDestFrame[1] * propDest + projY = self.aVSourceFrame[2] * propSource + self.aVDestFrame[2] * propDest + projZ = self.aVSourceFrame[3] * propSource + self.aVDestFrame[3] * propDest + newAV = Vec3(projX, projY, projZ) + ball = self.curGolfBall() + ball.setAngularVel(newAV) + if self.playbackFrameNum < lastFrame - 1: + ball.enable() + else: + ball.disable() + self.setCamera2Ball() + self.placeBodies() + if doAVPrint: + pass + if doPrint: + self.notify.debug('. %s %s %s %s %s' % (self.playbackFrameNum, + self.sourceFrame[0], + self.destFrame[0], + self.destFrameNum, + newPos)) + self.playbackFrameNum += 1 + + def enterCleanup(self): + taskMgr.remove('update task') + if hasattr(self, 'arrowKeys'): + self.arrowKeys.destroy() + self.arrowKeys = None + self.ignoreAll() + if self.swingInterval: + self.swingInterval.pause() + self.swingInterval = None + if self.sfxInterval: + self.sfxInterval.pause() + self.sfxInterval = None + self.cleanupGeom() + return + + def exitCleanup(self): + pass + + def setCamera2Ball(self): + b = self.curGolfBall() + ballPos = Point3(b.getPosition()[0], b.getPosition()[1], b.getPosition()[2]) + self.ballFollow.setPos(ballPos) + + def hitBall(self, ball, power, x, y): + self.performSwing(self, ball, power, x, y) + + def ballMovie2Client(self, cycleTime, avId, movie, spinMovie, ballInFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame, commonObjectData): + self.notify.debug('received Movie, number of frames %s %s ballInFrame=%d ballTouchedHoleFrame=%d ballFirstTouchedHoleFrame=%d' % (len(movie), + len(spinMovie), + ballInFrame, + ballTouchedHoleFrame, + ballFirstTouchedHoleFrame)) + if self.state == 'Playback': + self.notify.debug('SMASHED PLAYBACK') + return + self.ballShadowDict[avId].show() + self.holdCycleTime = cycleTime + self.holdCommonObjectData = commonObjectData + self.useCommonObjectData(self.holdCommonObjectData) + self.recording = movie + self.aVRecording = spinMovie + endingBallPos = Vec3(movie[-1][1], movie[-1][2], movie[-1][3]) + endingFrame = movie[-1][0] + self.playbackMovieDuration = endingFrame * self.DTAStep + self.notify.debug('playback movie duration=%s' % self.playbackMovieDuration) + displacement = self.holePositions[0] - endingBallPos + self.ballWillGoInHole = False + if displacement.length() <= GolfGlobals.DistanceToBeInHole: + self.ballWillGoInHole = True + self.notify.debug('endingBallPos=%s, distanceToHole=%s, ballWillGoInHole=%s' % (endingBallPos, displacement.length(), self.ballWillGoInHole)) + self.ballDropTime = ballInFrame * self.DTAStep + self.ballTouchedHoleTime = ballTouchedHoleFrame * self.DTAStep + self.ballFirstTouchedHoleTime = ballFirstTouchedHoleFrame * self.DTAStep + if self.state == 'WatchTee': + self.request('WatchAim') + self.request('Playback') + + def golfersTurn(self, avId): + self.readyCurrentGolfer(avId) + if avId == localAvatar.doId: + self.setCamera2Ball() + self.request('Aim') + else: + self.setCamera2Ball() + self.request('WatchAim') + + def readyCurrentGolfer(self, avId): + for index in self.ballDict: + self.ballDict[index]['golfBallOdeGeom'].setCollideBits(BitMask32(0)) + self.ballDict[index]['golfBallOdeGeom'].setCategoryBits(BitMask32(0)) + self.ballDict[index]['golfBall'].disable() + + if avId: + self.currentGolfer = avId + self.currentGolferActive = True + if avId in self.ballDict: + self.ballDict[avId]['golfBallOdeGeom'].setCollideBits(BitMask32(16777215)) + self.ballDict[avId]['golfBallOdeGeom'].setCategoryBits(BitMask32(4278190080L)) + else: + self.currentGolferActive = False + + def setGolferIds(self, avIds): + self.avIdList = avIds + self.numPlayers = len(self.avIdList) + self.teeChosen = {} + for avId in self.avIdList: + self.teeChosen[avId] = -1 + + def setHoleId(self, holeId): + self.holeId = holeId + self.holeInfo = GolfGlobals.HoleInfo[holeId] + + def createBall(self, avId, index = None): + golfBallGeom, golfBall, odeGeom = self.createSphere(self.world, self.space, GolfGlobals.GOLF_BALL_DENSITY, GolfGlobals.GOLF_BALL_RADIUS, index) + startPos = self.teePositions[0] + if len(self.teePositions) > 1: + startPos = self.teePositions[1] + golfBall.setPosition(startPos) + golfBallGeom.hide() + if self.notify.getDebug(): + self.notify.debug('golf ball body id') + golfBall.write() + self.notify.debug(' -') + golfBallGeom.setName('golfBallGeom%s' % avId) + self.ballDict[avId] = {'golfBall': golfBall, + 'golfBallGeom': golfBallGeom, + 'golfBallOdeGeom': odeGeom} + golfBall.disable() + shadow = self.dropShadowModel.copyTo(render) + shadow.setBin('shadow', 100) + shadow.setScale(0.09) + shadow.setDepthWrite(False) + shadow.setDepthTest(True) + self.ballShadowDict[avId] = shadow + shadow.hide() + + def setGolfCourseDoId(self, golfCourseDoId): + self.golfCourseDoId = golfCourseDoId + self.golfCourse = base.cr.doId2do.get(self.golfCourseDoId) + if not self.golfCourse: + self.cr.relatedObjectMgr.abortRequest(self.golfCourseRequest) + self.golfCourseRequest = self.cr.relatedObjectMgr.requestObjects([self.golfCourseDoId], eachCallback=self.__gotGolfCourse) + else: + self.scoreBoard = self.golfCourse.scoreBoard + self.scoreBoard.hide() + + def __gotGolfCourse(self, golfCourse): + self.golfCourseRequest = None + self.golfCourse = golfCourse + return + + def __genText(self, text): + self.__textGen.setText(text) + return self.__textGen.generate() + + def sendBox(self, pos0, pos1, pos2, quat0, quat1, quat2, quat3, anV0, anV1, anV2, lnV0, lnV1, lnV2): + self.swingBox.setPosition(pos0, pos1, pos2) + self.swingBox.setQuaternion(Quat(quat0, quat1, quat2, quat3)) + self.swingBox.setAngularVel(anV0, anV1, anV2) + self.swingBox.setLinearVel(lnV0, lnV1, lnV2) + + def hasCurGolferReachedMaxSwing(self): + strokes = self.golfCourse.getStrokesForCurHole(self.currentGolfer) + maxSwing = self.holeInfo['maxSwing'] + retval = strokes >= maxSwing + if retval: + pass + return retval + + def __getGolfPower(self, time): + elapsed = max(time - self.aimStart, 0.0) + t = elapsed / self.golfPowerSpeed + t = math.pow(t, self.golfPowerExponent) + power = int(t * 100) % 200 + if power > 100: + power = 200 - power + return power + + def __beginTossGolf(self): + if self.aimStart != None: + return + if not self.state == 'Aim': + return + if self.swingInfoSent: + return + self.localToonHitControl = True + time = globalClock.getFrameTime() + self.aimStart = time + messenger.send('wakeup') + self.scoreBoard.hide() + taskMgr.add(self.__updateGolfPower, self.golfPowerTaskName) + return + + def __endTossGolf(self): + if self.aimStart == None: + return + if not self.state == 'Aim': + return + messenger.send('wakeup') + taskMgr.remove(self.golfPowerTaskName) + self.aimStart = None + self.sendSwingInfo() + self.resetPowerBar() + return + + def __updateGolfPower(self, task): + if not self.powerBar: + print '### no power bar!!!' + return Task.done + newPower = self.__getGolfPower(globalClock.getFrameTime()) + self.power = newPower + self.powerBar['value'] = newPower + self.powerBar['text'] = TTLocalizer.GolfPowerBarText % {'power': newPower} + return Task.cont + + def golferChooseTee(self, avId): + self.readyCurrentGolfer(avId) + self.putAwayAllToons() + if self.needToDoFlyOver and self.doFlyOverMovie(avId): + pass + else: + if avId == localAvatar.doId: + self.setCamera2Ball() + if not self.state == 'ChooseTee': + self.request('ChooseTee') + else: + self.setCamera2Ball() + self.request('WatchTee') + self.takeOutToon(self.currentGolfer) + + def setAvatarTempTee(self, avId, tempTee): + if self.state != 'WatchTee': + return + if avId != self.currentGolfer: + self.notify.warning('setAvatarTempTee avId=%s not equal to self.currentGolfer=%s' % (avId, self.currentGolfer)) + return + self.changeTee(tempTee) + + def setAvatarFinalTee(self, avId, finalTee): + if avId != self.currentGolfer: + self.notify.warning('setAvatarTempTee avId=%s not equal to self.currentGolfer=%s' % (avId, self.currentGolfer)) + return + self.changeTee(finalTee) + + def setTempAimHeading(self, avId, heading): + if avId != self.currentGolfer: + self.notify.warning('setAvatarTempTee avId=%s not equal to self.currentGolfer=%s' % (avId, self.currentGolfer)) + return + if self.state != 'WatchAim': + return + if avId != localAvatar.doId: + self.ballFollow.setH(heading) + + def stickToonToBall(self, avId): + av = base.cr.doId2do.get(avId) + if av: + av.reparentTo(self.ballFollowToonSpot) + av.setPos(0, 0, 0) + av.setH(0) + + def putAwayToon(self, avId): + av = base.cr.doId2do.get(avId) + if av: + av.reparentTo(render) + av.setPos(0, 0, -1000) + av.setH(0) + + def putAwayAllToons(self): + for avId in self.avIdList: + self.putAwayToon(avId) + + def takeOutToon(self, avId): + self.stickToonToBall(avId) + self.fixCurrentGolferFeet() + self.attachClub(avId) + + def showOnlyCurGolfer(self): + self.notify.debug('curGolfer = %s' % self.currentGolfer) + self.stickToonToBall(self.currentGolfer) + self.fixCurrentGolferFeet() + self.attachClub(self.currentGolfer) + for avId in self.avIdList: + if avId != self.currentGolfer: + self.putAwayToon(avId) + + def tabKeyPressed(self): + doInterval = True + self.notify.debug('tab key pressed') + if not hasattr(self, 'ballFollow'): + return + if self.flyOverInterval and self.flyOverInterval.isPlaying(): + return + if self.camInterval and self.camInterval.isPlaying(): + self.camInterval.pause() + if base.camera.getParent() == self.ballFollow: + if doInterval: + curHpr = camera.getHpr(render) + angle = PythonUtil.closestDestAngle2(curHpr[0], 0) + self.camInterval = Sequence(Func(base.camera.wrtReparentTo, render), LerpPosHprInterval(base.camera, 2, self.camTopViewPos, self.camTopViewHpr)) + self.camInterval.start() + else: + base.camera.reparentTo(render) + base.camera.setPos(self.camTopViewPos) + base.camera.setHpr(self.camTopViewHpr) + elif doInterval: + curHpr = camera.getHpr(self.ballFollow) + angle = PythonUtil.closestDestAngle2(curHpr[0], 0) + self.camInterval = Sequence(Func(base.camera.wrtReparentTo, self.ballFollow), LerpPosHprInterval(base.camera, 2, self.camPosBallFollow, self.camHprBallFollow)) + self.camInterval.start() + else: + base.camera.reparentTo(self.ballFollow) + base.camera.setPos(self.camPosBallFollow) + base.camera.setHpr(self.camHprBallFollow) + + def doFlyOverMovie(self, avId): + title = GolfGlobals.getCourseName(self.golfCourse.courseId) + ' :\n ' + GolfGlobals.getHoleName(self.holeId) + '\n' + TTLocalizer.GolfPar + ' : ' + '%s' % self.holeInfo['par'] + self.titleLabel = DirectLabel(parent=aspect2d, relief=None, pos=(0, 0, 0.8), text_align=TextNode.ACenter, text=title, text_scale=0.12, text_font=ToontownGlobals.getSignFont(), text_fg=(1, 0.8, 0.4, 1)) + self.titleLabel.setBin('opaque', 19) + self.titleLabel.hide() + self.needToDoFlyOver = False + bamFile = self.holeInfo['terrainModel'] + fileName = bamFile.split('/')[-1] + dotIndex = fileName.find('.') + baseName = fileName[0:dotIndex] + camModelName = baseName + '_cammodel.bam' + cameraName = baseName + '_camera.bam' + path = bamFile[0:bamFile.find(fileName)] + camModelFullPath = path + camModelName + cameraAnimFullPath = path + cameraName + try: + self.flyOverActor = Actor.Actor(camModelFullPath, {'camera': cameraAnimFullPath}) + except StandardError: + self.notify.debug("Couldn't find flyover %s" % camModelFullPath) + return False + + base.transitions.noIris() + self.flyOverActor.reparentTo(render) + self.flyOverActor.setBlend(frameBlend=True) + flyOverJoint = self.flyOverActor.find('**/camera1') + children = flyOverJoint.getChildren() + numChild = children.getNumPaths() + for i in xrange(numChild): + childNodePath = children.getPath(i) + childNodePath.removeNode() + + self.flyOverJoint = flyOverJoint + self.flyOverInterval = Sequence(Func(base.camera.reparentTo, flyOverJoint), Func(base.camera.clearTransform), Func(self.titleLabel.show), ActorInterval(self.flyOverActor, 'camera'), Func(base.camera.reparentTo, self.ballFollow), Func(base.camera.setPos, self.camPosBallFollow), Func(base.camera.setHpr, self.camHprBallFollow)) + if avId == localAvatar.doId: + self.flyOverInterval.append(Func(self.setCamera2Ball)) + self.flyOverInterval.append(Func(self.safeRequestToState, 'ChooseTee')) + else: + self.flyOverInterval.append(Func(self.setCamera2Ball)) + self.flyOverInterval.append(Func(self.safeRequestToState, 'WatchTee')) + self.flyOverInterval.append(Func(self.titleLabel.hide)) + self.flyOverInterval.append(Func(self.takeOutToon, avId)) + self.flyOverInterval.start() + return True + + def avExited(self, avId): + if self.state == 'Playback' and self.currentGolfer == avId: + pass + else: + self.ballDict[avId]['golfBallGeom'].hide() + + def orientCameraRay(self): + pos = base.camera.getPos(self.terrainModel) + self.cameraRayNodePath.setPos(pos) + self.cameraRayNodePath.lookAt(self.ballFollow) + renderPos = self.cameraRayNodePath.getPos(render) + if renderPos != pos: + self.notify.debug('orientCamerRay this should not happen') + ballPos = self.ballFollow.getPos(self.terrainModel) + dirCam = Vec3(ballPos - pos) + dirCam.normalize() + self.cameraRay.set(pos, dirCam) + + def performSwing(self, ball, power, dirX, dirY): + startTime = globalClock.getRealTime() + avId = base.localAvatar.doId + position = ball.getPosition() + x = position[0] + y = position[1] + z = position[2] + if avId not in self.golfCourse.drivingToons: + x = position[0] + y = position[1] + z = position[2] + self.swingTime = cycleTime + lift = 0 + ball = self.ball + forceMove = 2500 + if power > 50: + lift = 0 + ball.enable() + ball.setPosition(x, y, z) + ball.setLinearVel(0.0, 0.0, 0.0) + ball.setAngularVel(0.0, 0.0, 0.0) + ball.addForce(Vec3(dirX * forceMove * power / 100.0, dirY * forceMove * power / 100.0, lift)) + self.initRecord() + safety = 0 + self.llv = None + self.record(ball) + while ball.isEnabled() and len(self.recording) < 2000: + self.preStep() + self.simulate() + self.postStep() + self.record(ball) + safety += 1 + + self.record(ball) + midTime = globalClock.getRealTime() + self.processRecording() + self.processAVRecording() + self.notify.debug('Recording End time %s cycle %s len %s avLen %s' % (self.timingSimTime, + self.getSimCycleTime(), + len(self.recording), + len(self.aVRecording))) + self.request('WaitPlayback') + length = len(self.recording) - 1 + x = self.recording[length][1] + y = self.recording[length][2] + z = self.recording[length][3] + self.ballPos[avId] = Vec3(x, y, z) + endTime = globalClock.getRealTime() + diffTime = endTime - startTime + fpsTime = self.frame / diffTime + self.notify.debug('Time Start %s Mid %s End %s Diff %s Fps %s frames %s' % (startTime, + midTime, + endTime, + diffTime, + fpsTime, + self.frame)) + self.ballMovie2Client(cycleTime, avId, self.recording, self.aVRecording, self.ballInHoleFrame, self.ballTouchedHoleFrame, self.ballFirstTouchedHoleFrame) + return + + def handleBallHitNonGrass(self, c0, c1): + if not self.inPlayBack: + return + golfBallPos = self.curGolfBall().getPosition() + if self.lastBumpSfxPos == golfBallPos: + return + if GolfGlobals.HARD_COLLIDE_ID in [c0, c1]: + if not self.bumpHardSfx.status() == self.bumpHardSfx.PLAYING: + distance = (golfBallPos - self.lastBumpSfxPos).length() + if distance > 2.0: + base.playSfx(self.bumpHardSfx) + self.lastBumpSfxPos = golfBallPos + elif GolfGlobals.MOVER_COLLIDE_ID in [c0, c1]: + if not self.bumpMoverSfx.status() == self.bumpMoverSfx.PLAYING: + base.playSfx(self.bumpMoverSfx) + self.lastBumpSfxPos = golfBallPos + elif GolfGlobals.WINDMILL_BASE_COLLIDE_ID in [c0, c1]: + if not self.bumpWindmillSfx.status() == self.bumpWindmillSfx.PLAYING: + base.playSfx(self.bumpWindmillSfx) + self.lastBumpSfxPos = golfBallPos + + def safeRequestToState(self, newState): + doingRequest = False + if self.state in self.defaultTransitions: + if newState in self.defaultTransitions[self.state]: + self.request(newState) + doingRequest = True + if not doingRequest: + self.notify.warning('ignoring transition from %s to %s' % (self.state, newState)) + + def doMagicWordHeading(self, heading): + if self.state == 'Aim': + self.aimMomentum = 0.0 + self.ballFollow.setH(float(heading)) + + def _handleClientCleanup(self): + self.removePlayBackDelayDelete() + self.ignore('clientCleanup') diff --git a/toontown/golf/DistributedGolfHoleAI.py b/toontown/golf/DistributedGolfHoleAI.py new file mode 100755 index 00000000..be2532a1 --- /dev/null +++ b/toontown/golf/DistributedGolfHoleAI.py @@ -0,0 +1,493 @@ +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from panda3d.core import * +import DistributedPhysicsWorldAI +from direct.fsm.FSM import FSM +from toontown.ai.ToonBarrier import * +from toontown.golf import GolfGlobals +import random +from toontown.golf import GolfHoleBase + +class DistributedGolfHoleAI(DistributedPhysicsWorldAI.DistributedPhysicsWorldAI, FSM, GolfHoleBase.GolfHoleBase): + defaultTransitions = {'Off': ['Cleanup', 'WaitTee'], + 'WaitTee': ['WaitSwing', + 'Cleanup', + 'WaitTee', + 'WaitPlayback'], + 'WaitSwing': ['WaitPlayback', + 'Cleanup', + 'WaitSwing', + 'WaitTee'], + 'WaitPlayback': ['WaitSwing', + 'Cleanup', + 'WaitTee', + 'WaitPlayback'], + 'Cleanup': ['Off']} + id = 0 + notify = directNotify.newCategory('DistributedGolfHoleAI') + + def __init__(self, zoneId, golfCourse, holeId): + FSM.__init__(self, 'Golf_%s_FSM' % self.id) + DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.__init__(self, simbase.air) + GolfHoleBase.GolfHoleBase.__init__(self) + self.zoneId = zoneId + self.golfCourse = golfCourse + self.holeId = holeId + self.avIdList = golfCourse.avIdList[:] + self.watched = [0, + 0, + 0, + 0] + self.barrierPlayback = None + self.trustedAvId = None + self.activeGolferIndex = None + self.activeGolferId = None + self.holeInfo = GolfGlobals.HoleInfo[self.holeId] + self.teeChosen = {} + for avId in self.avIdList: + self.teeChosen[avId] = -1 + + self.ballPos = {} + for avId in self.avIdList: + self.ballPos[avId] = Vec3(0, 0, 0) + + self.playStarted = False + return + + def curGolfBall(self): + return self.ball + + def generate(self): + DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.generate(self) + self.ball = self.createBall() + self.createRays() + if len(self.teePositions) > 1: + startPos = self.teePositions[1] + else: + startPos = self.teePositions[0] + startPos += Vec3(0, 0, GolfGlobals.GOLF_BALL_RADIUS) + self.ball.setPosition(startPos) + + def delete(self): + self.notify.debug('__delete__') + DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.delete(self) + self.notify.debug('calling self.terrainModel.removeNode') + self.terrainModel.removeNode() + self.notify.debug('self.barrierPlayback is %s' % self.barrierPlayback) + if self.barrierPlayback: + self.notify.debug('calling self.barrierPlayback.cleanup') + self.barrierPlayback.cleanup() + self.notify.debug('calling self.barrierPlayback = None') + self.barrierPlayback = None + self.activeGolferId = None + return + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def setAvatarReadyHole(self): + self.notify.debugStateCall(self) + avId = self.air.getAvatarIdFromSender() + self.golfCourse.avatarReadyHole(avId) + + def startPlay(self): + self.notify.debug('startPlay') + self.playStarted = True + self.numGolfers = len(self.golfCourse.getGolferIds()) + self.selectNextGolfer() + + def selectNextGolfer(self): + self.notify.debug('selectNextGolfer, old golferIndex=%s old golferId=%s' % (self.activeGolferIndex, self.activeGolferId)) + if self.golfCourse.isCurHoleDone(): + return + if self.activeGolferIndex == None: + self.activeGolferIndex = 0 + self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex] + else: + self.activeGolferIndex += 1 + if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()): + self.activeGolferIndex = 0 + self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex] + safety = 0 + while safety < 50 and not self.golfCourse.checkGolferPlaying(self.golfCourse.getGolferIds()[self.activeGolferIndex]): + self.activeGolferIndex += 1 + self.notify.debug('Index %s' % self.activeGolferIndex) + if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()): + self.activeGolferIndex = 0 + self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex] + safety += 1 + + if safety != 50: + golferId = self.golfCourse.getGolferIds()[self.activeGolferIndex] + if self.teeChosen[golferId] == -1: + self.sendUpdate('golferChooseTee', [golferId]) + self.request('WaitTee') + else: + self.sendUpdate('golfersTurn', [golferId]) + self.request('WaitSwing') + else: + self.notify.debug('safety') + self.notify.debug('selectNextGolfer, new golferIndex=%s new golferId=%s' % (self.activeGolferIndex, self.activeGolferId)) + return + + def clearWatched(self): + self.watched = [1, + 1, + 1, + 1] + for index in xrange(len(self.golfCourse.getGolferIds())): + self.watched[index] = 0 + + def setWatched(self, avId): + for index in xrange(len(self.golfCourse.getGolferIds())): + if self.golfCourse.getGolferIds()[index] == avId: + self.watched[index] = 1 + + def checkWatched(self): + if 0 not in self.watched: + return True + else: + return False + + def turnDone(self): + self.notify.debug('Turn Done') + avId = self.air.getAvatarIdFromSender() + if self.barrierPlayback: + self.barrierPlayback.clear(avId) + + def ballInHole(self, golferId = None): + self.notify.debug('ballInHole') + if golferId: + avId = golferId + else: + avId = self.air.getAvatarIdFromSender() + self.golfCourse.setBallIn(avId) + if self.golfCourse.isCurHoleDone(): + self.notify.debug('ballInHole doing nothing') + else: + self.notify.debug('ballInHole calling self.selectNextGolfer') + self.selectNextGolfer() + + def getHoleId(self): + return self.holeId + + def finishHole(self): + self.notify.debug('finishHole') + self.golfCourse.holeOver() + + def getGolferIds(self): + return self.avIdList + + def loadLevel(self): + GolfHoleBase.GolfHoleBase.loadLevel(self) + optionalObjects = self.terrainModel.findAllMatches('**/optional*') + requiredObjects = self.terrainModel.findAllMatches('**/required*') + self.parseLocators(optionalObjects, 1) + self.parseLocators(requiredObjects, 0) + self.teeNodePath = self.terrainModel.find('**/tee0') + if self.teeNodePath.isEmpty(): + teePos = Vec3(0, 0, 10) + else: + teePos = self.teeNodePath.getPos() + teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS) + self.notify.debug('teeNodePath heading = %s' % self.teeNodePath.getH()) + self.teePositions = [teePos] + teeIndex = 1 + teeNode = self.terrainModel.find('**/tee%d' % teeIndex) + while not teeNode.isEmpty(): + teePos = teeNode.getPos() + teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS) + self.teePositions.append(teePos) + self.notify.debug('teeNodeP heading = %s' % teeNode.getH()) + teeIndex += 1 + teeNode = self.terrainModel.find('**/tee%d' % teeIndex) + + def createLocatorDict(self): + self.locDict = {} + locatorNum = 1 + curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum) + while not curNodePath.isEmpty(): + self.locDict[locatorNum] = curNodePath + locatorNum += 1 + curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum) + + def loadBlockers(self): + loadAll = simbase.config.GetBool('golf-all-blockers', 0) + self.createLocatorDict() + self.blockerNums = self.holeInfo['blockers'] + for locatorNum in self.locDict: + if locatorNum in self.blockerNums or loadAll: + locator = self.locDict[locatorNum] + locatorParent = locator.getParent() + locator.getChildren().wrtReparentTo(locatorParent) + else: + self.locDict[locatorNum].removeNode() + + self.hardSurfaceNodePath.flattenStrong() + + def createBall(self): + golfBallGeom = self.createSphere(self.world, self.space, GolfGlobals.GOLF_BALL_DENSITY, GolfGlobals.GOLF_BALL_RADIUS, 1)[1] + return golfBallGeom + + def preStep(self): + GolfHoleBase.GolfHoleBase.preStep(self) + + def postStep(self): + GolfHoleBase.GolfHoleBase.postStep(self) + + def postSwing(self, cycleTime, power, x, y, z, dirX, dirY): + avId = self.air.getAvatarIdFromSender() + self.storeAction = [avId, + cycleTime, + power, + x, + y, + z, + dirX, + dirY] + if self.commonHoldData: + self.doAction() + + def postSwingState(self, cycleTime, power, x, y, z, dirX, dirY, curAimTime, commonObjectData): + self.notify.debug('postSwingState') + if not self.golfCourse.getStillPlayingAvIds(): + return + avId = self.air.getAvatarIdFromSender() + self.storeAction = [avId, + cycleTime, + power, + x, + y, + z, + dirX, + dirY] + self.commonHoldData = commonObjectData + self.trustedAvId = self.chooseAvatarToSimulate() + self.sendUpdateToAvatarId(self.trustedAvId, 'assignRecordSwing', [avId, + cycleTime, + power, + x, + y, + z, + dirX, + dirY, + commonObjectData]) + self.golfCourse.addAimTime(avId, curAimTime) + + def chooseAvatarToSimulate(self): + stillPlaying = self.golfCourse.getStillPlayingAvIds() + + return stillPlaying[0] if stillPlaying else 0 + + def ballMovie2AI(self, cycleTime, avId, movie, spinMovie, ballInFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame, commonObjectData): + sentFromId = self.air.getAvatarIdFromSender() + if sentFromId == self.trustedAvId: + lastFrameNum = len(movie) - 2 + if lastFrameNum < 0: + lastFrameNum = 0 + lastFrame = movie[lastFrameNum] + lastPos = Vec3(lastFrame[1], lastFrame[2], lastFrame[3]) + self.ballPos[avId] = lastPos + self.golfCourse.incrementScore(avId) + for id in self.golfCourse.getStillPlayingAvIds(): + if not id == sentFromId: + self.sendUpdateToAvatarId(id, 'ballMovie2Client', [cycleTime, + avId, + movie, + spinMovie, + ballInFrame, + ballTouchedHoleFrame, + ballFirstTouchedHoleFrame, + commonObjectData]) + + if self.state == 'WaitPlayback' or self.state == 'WaitTee': + self.notify.warning('ballMovie2AI requesting from %s to WaitPlayback' % self.state) + self.request('WaitPlayback') + elif self.trustedAvId == None: + return + else: + self.doAction() + self.trustedAvId = None + return + + def performReadyAction(self): + avId = self.storeAction[0] + if self.state == 'WaitPlayback': + self.notify.debugStateCall(self) + self.notify.debug('ignoring the postSwing for avId=%d since we are in WaitPlayback' % avId) + return + if avId == self.activeGolferId: + self.golfCourse.incrementScore(self.activeGolferId) + else: + self.notify.warning('activGolferId %d not equal to sender avId %d' % (self.activeGolferId, avId)) + if avId not in self.golfCourse.drivingToons: + position = self.ballPos[avId] + else: + position = Vec3(self.storeAction[3], self.storeAction[4], self.storeAction[5]) + self.useCommonObjectData(self.commonHoldData) + newPos = self.trackRecordBodyFlight(self.ball, self.storeAction[1], self.storeAction[2], position, self.storeAction[6], self.storeAction[7]) + if self.state == 'WaitPlayback' or self.state == 'WaitTee': + self.notify.warning('performReadyAction requesting from %s to WaitPlayback' % self.state) + self.request('WaitPlayback') + self.sendUpdate('ballMovie2Client', [self.storeAction[1], + avId, + self.recording, + self.aVRecording, + self.ballInHoleFrame, + self.ballTouchedHoleFrame, + self.ballFirstTouchedHoleFrame, + self.commonHoldData]) + self.ballPos[avId] = newPos + self.trustedAvId = None + return + + def postResult(self, cycleTime, avId, recording, aVRecording, ballInHoleFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame): + pass + + def enterWaitSwing(self): + pass + + def exitWaitSwing(self): + pass + + def enterWaitTee(self): + pass + + def exitWaitTee(self): + pass + + def enterWaitPlayback(self): + self.notify.debug('enterWaitPlayback') + stillPlayingList = self.golfCourse.getStillPlayingAvIds() + self.barrierPlayback = ToonBarrier('waitClientsPlayback', self.uniqueName('waitClientsPlayback'), stillPlayingList, 120, self.handleWaitPlaybackDone, self.handlePlaybackTimeout) + + def hasCurGolferReachedMaxSwing(self): + strokes = self.golfCourse.getCurHoleScore(self.activeGolferId) + maxSwing = self.holeInfo['maxSwing'] + retval = strokes >= maxSwing + if retval: + av = simbase.air.doId2do.get(self.activeGolferId) + if av: + if av.getUnlimitedSwing(): + retval = False + return retval + + def handleWaitPlaybackDone(self): + if self.isCurBallInHole(self.activeGolferId) or self.hasCurGolferReachedMaxSwing(): + if self.activeGolferId: + self.ballInHole(self.activeGolferId) + else: + self.selectNextGolfer() + + def isCurBallInHole(self, golferId): + retval = False + for holePos in self.holePositions: + displacement = self.ballPos[golferId] - holePos + length = displacement.length() + self.notify.debug('hole %s length=%s' % (holePos, length)) + if length <= GolfGlobals.DistanceToBeInHole: + retval = True + break + + return retval + + def exitWaitPlayback(self): + self.notify.debug('exitWaitPlayback') + if hasattr(self, 'barrierPlayback') and self.barrierPlayback: + self.barrierPlayback.cleanup() + self.barrierPlayback = None + return + + def enterCleanup(self): + pass + + def exitCleanup(self): + pass + + def handlePlaybackTimeout(self, task = None): + self.notify.debug('handlePlaybackTimeout') + self.handleWaitPlaybackDone() + + def getGolfCourseDoId(self): + return self.golfCourse.doId + + def avatarDropped(self, avId): + self.notify.warning('avId %d dropped, self.state=%s' % (avId, self.state)) + if self.barrierPlayback: + self.barrierPlayback.clear(avId) + else: + if avId == self.trustedAvId: + self.doAction() + if avId == self.activeGolferId and not self.golfCourse.haveAllGolfersExited(): + self.selectNextGolfer() + + def setAvatarTee(self, chosenTee): + golferId = self.air.getAvatarIdFromSender() + self.teeChosen[golferId] = chosenTee + self.ballPos[golferId] = self.teePositions[chosenTee] + self.sendUpdate('setAvatarFinalTee', [golferId, chosenTee]) + self.sendUpdate('golfersTurn', [golferId]) + self.request('WaitSwing') + + def setBox(self, pos0, pos1, pos2, quat0, quat1, quat2, quat3, anV0, anV1, anV2, lnV0, lnV1, lnV2): + self.sendUpdate('sendBox', [pos0, + pos1, + pos2, + quat0, + quat1, + quat2, + quat3, + anV0, + anV1, + anV2, + lnV0, + lnV1, + lnV2]) + + def parseLocators(self, objectCollection, optional = 0): + if optional and objectCollection.getNumPaths(): + if 'optionalMovers' in self.holeInfo: + for optionalMoverId in self.holeInfo['optionalMovers']: + searchStr = 'optional_mover_' + str(optionalMoverId) + for objIndex in xrange(objectCollection.getNumPaths()): + object = objectCollection.getPath(objIndex) + if searchStr in object.getName(): + self.fillLocator(objectCollection, objIndex) + break + + else: + for index in xrange(objectCollection.getNumPaths()): + self.fillLocator(objectCollection, index) + + def fillLocator(self, objectCollection, index): + path = objectCollection[index] + pathName = path.getName() + pathArray = pathName.split('_') + sizeX = None + sizeY = None + move = None + type = None + for subString in pathArray: + if subString[:1] == 'X': + dataString = subString[1:] + dataString = dataString.replace('p', '.') + sizeX = float(dataString) + elif subString[:1] == 'Y': + dataString = subString[1:] + dataString = dataString.replace('p', '.') + sizeY = float(dataString) + elif subString[:1] == 'd': + dataString = subString[1:] + dataString = dataString.replace('p', '.') + move = float(dataString) + elif subString == 'mover': + type = 4 + elif subString == 'windmillLocator': + type = 3 + + if type == 4 and move and sizeX and sizeY: + self.createCommonObject(4, path.getPos(), path.getHpr(), sizeX, sizeY, move) + elif type == 3: + self.createCommonObject(3, path.getPos(), path.getHpr()) + return diff --git a/toontown/golf/DistributedPhysicsWorld.py b/toontown/golf/DistributedPhysicsWorld.py new file mode 100755 index 00000000..7e19b390 --- /dev/null +++ b/toontown/golf/DistributedPhysicsWorld.py @@ -0,0 +1,107 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from panda3d.core import * +from math import * +import math +from direct.fsm.FSM import FSM +from toontown.minigame import ArrowKeys +from direct.showbase import PythonUtil +from direct.showutil import Rope +from direct.task import Task +from direct.distributed.ClockDelta import * +import BuildGeometry +from toontown.golf import GolfGlobals +from toontown.golf import PhysicsWorldBase +import random, time +from direct.interval.SoundInterval import SoundInterval + +def scalp(vec, scal): + vec0 = vec[0] * scal + vec1 = vec[1] * scal + vec2 = vec[2] * scal + vec = Vec3(vec0, vec1, vec2) + + +def length(vec): + return sqrt(vec[0] ** 2 + vec[1] ** 2 + vec[2] ** 2) + + +class DistributedPhysicsWorld(DistributedObject.DistributedObject, PhysicsWorldBase.PhysicsWorldBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPhysicsWorld') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + PhysicsWorldBase.PhysicsWorldBase.__init__(self, 1) + self.accept('ode toggle contacts', self.__handleToggleContacts) + self.physicsSfxDict = {} + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.setupSimulation() + self.startSim() + + def delete(self): + DistributedObject.DistributedObject.delete(self) + PhysicsWorldBase.PhysicsWorldBase.delete(self) + taskMgr.remove('simulation task') + self.ignoreAll() + for index in self.physicsSfxDict: + sfxPair = self.physicsSfxDict[index] + sfxPair[0].stop() + sfxPair[1].finish() + + self.physicsSfxDict = None + return + + def clientCommonObject(self, type, commonId, pos, hpr, sizeX, sizeY, moveDistance): + data = self.createCommonObject(type, commonId, pos, hpr, sizeX, sizeY, moveDistance) + index = data[1] + if type == 3: + cross = self.commonObjectDict[commonId][2] + for pair in self.odePandaRelationList: + pandaNodePathGeom = pair[0] + odeBody = pair[1] + if odeBody == cross: + base.sfxPlayer.setCutoffDistance(240) + self.notify.debug('nodePath = %s' % pandaNodePathGeom) + windmillSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Windmill_Loop.ogg') + windMillSoundInterval = SoundInterval(windmillSfx, node=pandaNodePathGeom, listenerNode=base.camera, seamlessLoop=True, volume=0.5) + windMillSoundInterval.loop() + self.physicsSfxDict[index] = (windmillSfx, windMillSoundInterval) + break + + elif type == 4: + box = self.commonObjectDict[commonId][2] + for pair in self.odePandaRelationList: + pandaNodePathGeom = pair[0] + odeBody = pair[1] + if odeBody == box: + self.notify.debug('nodePath = %s' % pandaNodePathGeom) + moverSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Moving_Barrier.ogg') + moverSoundInterval = SoundInterval(moverSfx, node=pandaNodePathGeom, listenerNode=base.camera, seamlessLoop=True, volume=0.5) + moverSoundInterval.start() + self.physicsSfxDict[index] = (moverSfx, moverSoundInterval, index) + break + + def commonObjectEvent(self, key, model, type, force, event): + self.notify.debug('commonObjectForceEvent key %s model %s type %s force %s event %s' % (key, + model, + type, + force, + event)) + if type == 4: + if event > 0: + self.physicsSfxDict[key][1].start() + + def setCommonObjects(self, objectData): + self.useCommonObjectData(objectData) + + def upSendCommonObjects(self): + self.sendUpdate('upSetCommonObjects', [self.getCommonObjectData()]) + + def __handleToggleContacts(self, message = None): + if self.showContacts: + self.showContacts = 0 + else: + self.showContacts = 1 diff --git a/toontown/golf/DistributedPhysicsWorldAI.py b/toontown/golf/DistributedPhysicsWorldAI.py new file mode 100755 index 00000000..2d11223b --- /dev/null +++ b/toontown/golf/DistributedPhysicsWorldAI.py @@ -0,0 +1,68 @@ +from math import * +import math +import random, time + +import BuildGeometry +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObjectAI +from panda3d.core import * +from toontown.golf import PhysicsWorldBase +from toontown.toonbase import ToontownGlobals + + +class DistributedPhysicsWorldAI(DistributedObjectAI.DistributedObjectAI, PhysicsWorldBase.PhysicsWorldBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPhysicsWorldAI') + + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + PhysicsWorldBase.PhysicsWorldBase.__init__(self, 0) + self.commonHoldData = None + self.storeAction = None + self.holdingUpObjectData = 0 + return + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.loadLevel() + self.setupSimulation() + + def delete(self): + self.notify.debug('Calling DistributedObjectAI.delete') + DistributedObjectAI.DistributedObjectAI.delete(self) + self.notify.debug('Calling PhysicsWorldBase.delete') + PhysicsWorldBase.PhysicsWorldBase.delete(self) + + def loadLevel(self): + pass + + def createCommonObject(self, type, pos, hpr, sizeX = 0, sizeY = 0, moveDistance = 0): + commonObjectDatam = PhysicsWorldBase.PhysicsWorldBase.createCommonObject(self, type, None, pos, hpr, sizeX, sizeY, moveDistance) + self.sendUpdate('clientCommonObject', commonObjectDatam) + return + + def updateCommonObjects(self): + self.sendUpdate('setCommonObjects', [self.getCommonObjectData()]) + + def doAction(self): + self.performReadyAction() + self.storeAction = None + self.commonHoldData = None + return + + def upSetCommonObjects(self, objectData): + self.holdingUpObjectData = 1 + self.commonHoldData = objectData + if self.storeAction: + self.doAction() + + def setupCommonObjects(self): + print self.commonHoldData + if not self.commonHoldData: + return + elif self.commonHoldData[0][1] == 99: + print 'no common objects' + else: + self.useCommonObjectData(self.commonHoldData, 0) + + def performReadyAction(self): + pass diff --git a/toontown/golf/GolfGlobals.py b/toontown/golf/GolfGlobals.py new file mode 100755 index 00000000..11bee4e5 --- /dev/null +++ b/toontown/golf/GolfGlobals.py @@ -0,0 +1,51 @@ +from direct.directnotify import DirectNotifyGlobal;import random;MAX_PLAYERS_PER_HOLE=4;GOLF_BALL_RADIUS=0.25;GOLF_BALL_VOLUME=4.0/3.0*3.14159*GOLF_BALL_RADIUS**3;GOLF_BALL_MASS=0.5;GOLF_BALL_DENSITY=GOLF_BALL_MASS/GOLF_BALL_VOLUME;GRASS_SURFACE=0;BALL_SURFACE=1;HARD_SURFACE=2;HOLE_SURFACE=3;SLICK_SURFACE=4;OOB_RAY_COLLIDE_ID=-1;GRASS_COLLIDE_ID=2;HARD_COLLIDE_ID=3;TOON_RAY_COLLIDE_ID=4;MOVER_COLLIDE_ID=7;WINDMILL_BASE_COLLIDE_ID=8;CAMERA_RAY_COLLIDE_ID=10;BALL_COLLIDE_ID=42;HOLE_CUP_COLLIDE_ID=64;SKY_RAY_COLLIDE_ID=78;SLICK_COLLIDE_ID=13;BALL_CONTACT_FRAME=9;BALL_CONTACT_TIME=(BALL_CONTACT_FRAME+1)/24.0;AIM_DURATION=60;TEE_DURATION=15;RANDOM_HOLES=True;KICKOUT_SWINGS=2;TIME_TIE_BREAKER=True;CourseInfo={0:{'holeIds':(2,3,4,5,6,7,8,12,13,15,16),'numHoles':3,'name':''},1:{'holeIds':((0,5),(1,5),2,3,4,5,6,7,8,9,10,(11,5),12,13,(14,5),15,16,(17,5),(20,5),(21,5),(22,5),(23,5),(24,5),(25,5),(26,5),(28,5),(30,5),(31,5),(33,5),(34,5)),'numHoles':6,'name':''},2:{'holeIds':((1,5),4,5,6,7,8,9,10,11,12,13,(14,5),15,(17,5),(18,20),(19,20),(20,20),(21,5),(22,5),(23,20),(24,20),(25,20),(26,20),(27,20),(28,20),(29,20),(30,5),(31,20),(32,20),(33,5),(34,20),(35,20)),'numHoles':9,'name':''}};HoleInfo={0:{'optionalMovers':(),'maxSwing':6,'terrainModel':'phase_6/models/golf/hole18.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen18'},1:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole1.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen1'},2:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole2.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen2'},3:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole3.bam','name':'','blockers':(),'par':2,'physicsData':'golfGreen3'},4:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole4.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen4'},5:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole5.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen2'},6:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole6.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen6'},7:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole7.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen7'},8:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole8.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen8'},9:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole9.bam','name':'','blockers':2,'par':3,'physicsData':'golfGreen9'},10:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole10.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen10'},11:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole11.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen11'},12:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole12.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen12'},13:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole13.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen13'},14:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole14.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen14'},15:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole15.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen15'},16:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole16.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen16'},17:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole17.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen17'},18:{'optionalMovers':1,'maxSwing':6,'terrainModel':'phase_6/models/golf/hole18.bam','name':'','blockers':(1,2),'par':3,'physicsData':'golfGreen18'},19:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole1.bam','name':'','blockers':(2,5),'par':3,'physicsData':'golfGreen1'},20:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole2.bam','name':'','blockers':(1,3),'par':3,'physicsData':'golfGreen2'},21:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole3.bam','name':'','blockers':(1,2,3),'par':3,'physicsData':'golfGreen3'},22:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole4.bam','name':'','blockers':2,'par':3,'physicsData':'golfGreen4'},23:{'optionalMovers':1,'maxSwing':6,'terrainModel':'phase_6/models/golf/hole5.bam','name':'','blockers':(3,4),'par':3,'physicsData':'golfGreen5'},24:{'optionalMovers':1,'maxSwing':6,'terrainModel':'phase_6/models/golf/hole6.bam','name':'','blockers':1,'par':3,'physicsData':'golfGreen6'},25:{'optionalMovers':1,'maxSwing':6,'terrainModel':'phase_6/models/golf/hole7.bam','name':'','blockers':3,'par':3,'physicsData':'golfGreen7'},26:{'optionalMovers':1,'maxSwing':6,'terrainModel':'phase_6/models/golf/hole8.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen8'},27:{'optionalMovers':(1,2),'maxSwing':6,'terrainModel':'phase_6/models/golf/hole9.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen9'},28:{'optionalMovers':(1,2),'maxSwing':6,'terrainModel':'phase_6/models/golf/hole10.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen10'},29:{'optionalMovers':1,'maxSwing':6,'terrainModel':'phase_6/models/golf/hole11.bam','name':'','blockers':(),'par':3,'physicsData':'golfGreen11'},30:{'maxSwing':6,'terrainModel':'phase_6/models/golf/hole12.bam','name':'','blockers':(1,2,3),'par':3,'physicsData':'golfGreen12'},31:{'optionalMovers':1,'maxSwing':7,'terrainModel':'phase_6/models/golf/hole13.bam','name':'','blockers':(3,4),'par':4,'physicsData':'golfGreen13'},32:{'optionalMovers':1,'maxSwing':6,'terrainModel':'phase_6/models/golf/hole14.bam','name':'','blockers':1,'par':3,'physicsData':'golfGreen14'},33:{'optionalMovers':(1,2),'maxSwing':6,'terrainModel':'phase_6/models/golf/hole15.bam','name':'','blockers':(1,2,3),'par':3,'physicsData':'golfGreen15'},34:{'optionalMovers':1,'maxSwing':6,'terrainModel':'phase_6/models/golf/hole16.bam','name':'','blockers':(1,2,5,6),'par':3,'physicsData':'golfGreen16'},35:{'maxSwing':7,'terrainModel':'phase_6/models/golf/hole17.bam','name':'','blockers':(3,4,5),'par':4,'physicsData':'golfGreen17'}} +for hi in HoleInfo: + if type(HoleInfo[hi]['blockers'])==type(0):blockerNum=HoleInfo[hi]['blockers'];HoleInfo[hi]['blockers']=(blockerNum,) + if 'optionalMovers' in HoleInfo[hi] and type(HoleInfo[hi]['optionalMovers'])==type(0):blockerNum=HoleInfo[hi]['optionalMovers'];HoleInfo[hi]['optionalMovers']=(blockerNum,) +DistanceToBeInHole=0.75;CoursesCompleted=0;CoursesUnderPar=1;HoleInOneShots=2;EagleOrBetterShots=3;BirdieOrBetterShots=4;ParOrBetterShots=5;MultiPlayerCoursesCompleted=6;CourseZeroWins=7;CourseOneWins=8;CourseTwoWins=9;TwoPlayerWins=10;ThreePlayerWins=11;FourPlayerWins=12;MaxHistoryIndex=9;NumHistory=MaxHistoryIndex+1;CalcOtherHoleBest = False;CalcOtherCourseBest = False;TrophyRequirements={CoursesCompleted:(4,40,400),CoursesUnderPar:(1,10,100),HoleInOneShots:(1,10,100),EagleOrBetterShots:(2,20,200),BirdieOrBetterShots:(3,30,300),ParOrBetterShots:(4,40,400),MultiPlayerCoursesCompleted:(6,60,600),CourseZeroWins:(1,10,100),CourseOneWins:(1,10,100),CourseTwoWins:(1,10,100)};PlayerColors=[(0.925,0.168,0.168,1),(0.13,0.59,0.973,1),(0.973,0.809,0.129,1),(0.598,0.402,0.875,1)];KartColors=[[[0,50],[90,255],[0,85]],[[160,255],[-15,15],[0,120]],[[160,255],[0,110],[0,110]]];NumTrophies=0 +for key in TrophyRequirements:NumTrophies += len(TrophyRequirements[key]) +NumCups=3;TrophiesPerCup=NumTrophies/NumCups +def calcTrophyListFromHistory(h): + rv,hi=[],0 + for ti in xrange(NumHistory): + r=TrophyRequirements[ti] + for an in r: + if h[hi]>=an:rv.append(True) + else:rv.append(False) + hi+=1 + return rv +def calcCupListFromHistory(h): + rv,tl,nt=[False]*NumCups,calcTrophyListFromHistory(h),0 + for gt in tl: + if gt:nt+=1 + for ci in xrange(len(rv)): + tr=(ci+1)*TrophiesPerCup + if tr<=nt:rv[ci]=True + return rv +def getCourseName(ci): + from toontown.toonbase import TTLocalizer + if ci in CourseInfo: + if not CourseInfo[ci]['name']:CourseInfo[ci]['name']=TTLocalizer.GolfCourseNames[ci] + return CourseInfo[ci]['name'] + else:return '' +def getHoleName(hi): + from toontown.toonbase import TTLocalizer + if hi in HoleInfo: + if not HoleInfo[hi]['name']:HoleInfo[hi]['name']=TTLocalizer.GolfHoleNames[hi] + return HoleInfo[hi]['name'] + else:return '' +def getHistoryIndexForTrophy(ti): + rv,db=-1,int(ti/3) + if db>4;rv.append(hb) + return rv diff --git a/toontown/golf/GolfHoleBase.py b/toontown/golf/GolfHoleBase.py new file mode 100755 index 00000000..fad15545 --- /dev/null +++ b/toontown/golf/GolfHoleBase.py @@ -0,0 +1,556 @@ +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from panda3d.core import * +from direct.fsm.FSM import FSM +from toontown.ai.ToonBarrier import * +from toontown.golf import GolfGlobals +import random +import math + +class GolfHoleBase: + + def __init__(self, canRender = 0): + self.canRender = canRender + self.recording = [] + self.aVRecording = [] + self.holePositions = [] + self.grayCount = 0 + self.skyContact = None + self.lastSkyContactPoint = None + self.doingRecording = 0 + self.backAmount = 270 + self.ballRocket = 0 + self.inCount = 0 + self.frame = 0 + self.onSlick = 0 + self.didHoleBreak = 0 + return + + def loadLevel(self): + tm = self.holeInfo['terrainModel'] + self.terrainModel = loader.loadModel(tm) + td = self.holeInfo['physicsData'] + if self.canRender: + self.terrainModel.reparentTo(render) + if self.canRender: + self.terrainModel.find('**/softSurface').setBin('ground', 0) + terrainData = self.terrainModel.find('**/softSurface') + grassData = terrainData.findAllMatches('**/grass*') + self.terrainData = [] + for index in xrange(grassData.getNumPaths()): + someTerrainData = grassData[index] + terrainDataOde = OdeTriMeshData(someTerrainData) + self.meshDataList.append(terrainDataOde) + terrainGeomOde = OdeTriMeshGeom(self.space, terrainDataOde) + self.geomDataList.append(terrainGeomOde) + terrainGeomOde.setCollideBits(BitMask32(4026531840L)) + terrainGeomOde.setCategoryBits(BitMask32(240)) + self.space.setSurfaceType(terrainGeomOde, GolfGlobals.GRASS_SURFACE) + self.space.setCollideId(terrainGeomOde, 2) + + slickData = terrainData.findAllMatches('**/slick*') + self.terrainData = [] + for index in xrange(slickData.getNumPaths()): + someTerrainData = slickData[index] + terrainDataOde = OdeTriMeshData(someTerrainData) + self.meshDataList.append(terrainDataOde) + terrainGeomOde = OdeTriMeshGeom(self.space, terrainDataOde) + self.geomDataList.append(terrainGeomOde) + terrainGeomOde.setCollideBits(BitMask32(4026531840L)) + terrainGeomOde.setCategoryBits(BitMask32(240)) + self.space.setSurfaceType(terrainGeomOde, GolfGlobals.SLICK_SURFACE) + self.space.setCollideId(terrainGeomOde, GolfGlobals.SLICK_COLLIDE_ID) + + cupData = terrainData.find('**/hole*') + cupData = OdeTriMeshData(cupData) + self.meshDataList.append(cupData) + cupGeom = OdeTriMeshGeom(self.space, cupData) + self.geomDataList.append(cupGeom) + cupGeom.setCollideBits(BitMask32(4026531840L)) + cupGeom.setCategoryBits(BitMask32(240)) + self.space.setSurfaceType(cupGeom, GolfGlobals.HOLE_SURFACE) + self.space.setCollideId(cupGeom, GolfGlobals.HOLE_CUP_COLLIDE_ID) + if self.canRender: + self.golfBarrier = self.terrainModel.find('**/collision1') + if not self.golfBarrier.isEmpty(): + golfBarrierCollection = self.terrainModel.findAllMatches('**/collision?') + for i in xrange(golfBarrierCollection.getNumPaths()): + oneBarrier = golfBarrierCollection.getPath(i) + if oneBarrier != self.golfBarrier: + oneBarrier.wrtReparentTo(self.golfBarrier) + + self.golfBarrier.hide() + else: + self.notify.warning('Could not find collision1 node ---------') + self.hardSurfaceNodePath = self.terrainModel.find('**/hardSurface') + if self.canRender: + self.terrainModel.find('**/hardSurface').setBin('ground', 0) + self.loadBlockers() + hardData = OdeTriMeshData(self.hardSurfaceNodePath) + self.meshDataList.append(hardData) + hardGeom = OdeTriMeshGeom(self.space, hardData) + self.geomDataList.append(hardGeom) + hardGeom.setCollideBits(BitMask32(4026531840L)) + hardGeom.setCategoryBits(BitMask32(240)) + self.space.setCollideId(hardGeom, 3) + hardSurface = self.space.getSurfaceType(hardGeom) + self.notify.debug('hardSurface = %s' % hardSurface) + if self.notify.getDebug(): + self.notify.debug('self.hardGeom') + hardGeom.write() + self.notify.debug(' -') + self.holeBottomNodePath = self.terrainModel.find('**/holebottom0') + if self.holeBottomNodePath.isEmpty(): + self.holeBottomPos = Vec3(*self.holeInfo['holePos'][0]) + else: + self.holeBottomPos = self.holeBottomNodePath.getPos() + self.holePositions.append(self.holeBottomPos) + + def isBallInHole(self, ball): + retval = False + for holePos in self.holePositions: + displacement = ball.getPosition() - holePos + length = displacement.length() + self.notify.debug('hole %s length=%s' % (holePos, length)) + if length <= GolfGlobals.DistanceToBeInHole * 0.5: + retval = True + break + + return retval + + def createRays(self): + self.notify.debug('createRays') + body = OdeBody(self.world) + self.ballRay = OdeRayGeom(self.space, 50.0) + self.ballRay.setBody(body) + self.ballRay.setOffsetRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1)) + self.ballRay.setOffsetPosition(0, 0, 0.0) + self.ballRay.setCollideBits(BitMask32(16773375)) + self.ballRay.setCategoryBits(BitMask32(4278190080L)) + self.ballRayBody = body + self.space.setCollideId(self.ballRay, GolfGlobals.OOB_RAY_COLLIDE_ID) + self.rayList.append(self.ballRay) + self.rayList.append(self.ballRayBody) + self.skyRay = OdeRayGeom(self.space, 100.0) + self.skyRay.setCollideBits(BitMask32(240)) + self.skyRay.setCategoryBits(BitMask32(0)) + self.skyRay.setRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1)) + self.space.setCollideId(self.skyRay, GolfGlobals.SKY_RAY_COLLIDE_ID) + self.rayList.append(self.skyRay) + + def delete(self): + self.ballRay = None + self.skyRay = None + self.recording = None + self.avRecording = None + self.llv = None + return + + def initRecord(self): + del self.recording + self.recording = [] + del self.aVRecording + self.aVRecording = [] + self.skipFrame = 0.0 + self.frame = 0 + self.tXYMax = 1.0 + self.tZMax = 1.0 + self.tXYMin = 0.1 + self.tZMin = 0.1 + self.skyContact = 1 + self.doingRecording = 1 + self.ballRocket = 0 + self.inCount = 0 + self.ballInHoleFrame = 0 + self.ballTouchedHoleFrame = 0 + self.ballFirstTouchedHoleFrame = 0 + self.ballLastTouchedGrass = 0 + self.hasReset = 0 + self.resetAt = 100000 + self.greenIn = 0 + for key in self.commonObjectDict: + self.commonObjectDict[key][2].enable() + + def checkCommonObjectsNeedPass(self): + for index in self.commonObjectDict: + if self.commonObjectDict[index][1] in [4]: + return 1 + + return 0 + + def checkInRadius(self, ball): + smallestDist = None + for index in self.commonObjectDict: + if self.commonObjectDict[index][1] in [4]: + radius = self.commonObjectDict[index][8] + mover = self.commonObjectDict[index][2] + diffX = ball.getPosition()[0] - mover.getPosition()[0] + diffY = ball.getPosition()[1] - mover.getPosition()[1] + diffZ = ball.getPosition()[2] - mover.getPosition()[2] + dist = math.sqrt(diffX * diffX + diffY * diffY + diffZ * diffZ) + if dist < radius: + if not smallestDist or smallestDist[1] > dist: + smallestDist = [radius, dist] + self.notify.debug('Ball Pos %s\nMover Pos %s' % (ball.getPosition(), mover.getPosition())) + + return smallestDist + + def trackRecordBodyFlight(self, ball, cycleTime, power, startPos, dirX, dirY): + self.notify.debug('trackRecordBodyFlight') + self.ballInHoleFrame = 0 + self.ballTouchedHoleFrame = 0 + self.ballFirstTouchedHoleFrame = 0 + self.ballLastTouchedGrass = 0 + startTime = globalClock.getRealTime() + self.notify.debug('start position %s' % startPos) + self.swingTime = cycleTime + frameCount = 0 + lift = 0 + startTime = GolfGlobals.BALL_CONTACT_FRAME / 24 + startFrame = int(startTime * self.FPS) + for frame in xrange(int(startFrame)): + self.simulate() + self.setTimeIntoCycle(self.swingTime + float(frameCount) * self.DTAStep) + frameCount += 1 + + forceMove = 1500 + if power > 50: + lift = 0 + self.didHoleBreak = 0 + ball.setPosition(startPos) + ball.setLinearVel(0.0, 0.0, 0.0) + ball.setAngularVel(0.0, 0.0, 0.0) + ball.enable() + self.preStep() + self.simulate() + self.postStep() + ball.enable() + ball.addForce(Vec3(dirX * forceMove * power / 100.0, dirY * forceMove * power / 100.0, lift)) + self.initRecord() + self.llv = None + self.lastSkyContactPoint = None + ran = 0 + self.record(ball) + self.comObjNeedPass = self.checkCommonObjectsNeedPass() + self.notify.debug('self.comObjNeedPass %s' % self.comObjNeedPass) + firstDisabled = -1 + reEnabled = 0 + lastFrameEnabled = 0 + checkFrames = self.FPS * (self.timingCycleLength + 1.0) + hasPrinted = 0 + while ball.isEnabled() and len(self.recording) < 2100 or self.comObjNeedPass or len(self.recording) < 10: + ran = 1 + if len(self.recording) > 2100 and not hasPrinted: + self.notify.debug('recording too long %s' % len(self.recording)) + hasPrinted = 1 + ball.disable() + self.preStep() + self.simulate() + self.setTimeIntoCycle(self.swingTime + float(frameCount) * self.DTAStep) + frameCount += 1 + self.postStep() + self.record(ball) + if self.comObjNeedPass: + if firstDisabled == -1 and not ball.isEnabled(): + firstDisabled = self.frame + self.notify.debug('firstDisabled %s' % firstDisabled) + check = self.checkInRadius(ball) + if check == None: + self.comObjNeedPass = 0 + self.notify.debug('out radius') + else: + self.notify.debug('in radius %s dist %s' % (check[0], check[1])) + elif ball.isEnabled() and firstDisabled != -1 and not reEnabled: + reEnabled = self.frame + self.notify.debug('reEnabled %s' % reEnabled) + if reEnabled: + if self.frame > reEnabled + checkFrames: + self.comObjNeedPass = 0 + self.notify.debug('renable limit passed') + elif self.frame > 2100 + checkFrames: + self.comObjNeedPass = 0 + print 'recording limit passed comObj' + if ball.isEnabled(): + lastFrameEnabled = self.frame + + self.notify.debug('lastFrameEnabled %s' % lastFrameEnabled) + if lastFrameEnabled < 3: + lastFrameEnabled = 3 + self.record(ball) + self.notify.debug('Frames %s' % self.frame) + midTime = globalClock.getRealTime() + self.recording = self.recording[:lastFrameEnabled] + self.aVRecording = self.aVRecording[:lastFrameEnabled] + self.frame = lastFrameEnabled + self.processRecording() + self.processAVRecording() + self.notify.debug('Recording End time %s cycle %s len %s avLen %s' % (self.timingSimTime, + self.getCycleTime(), + len(self.recording), + len(self.aVRecording))) + length = len(self.recording) - 1 + x = self.recording[length][1] + y = self.recording[length][2] + z = self.recording[length][3] + endTime = globalClock.getRealTime() + diffTime = endTime - startTime + self.doingRecording = 0 + fpsTime = self.frame / diffTime + self.notify.debug('Time Start %s Mid %s End %s Diff %s Fps %s frames %s' % (startTime, + midTime, + endTime, + diffTime, + fpsTime, + self.frame)) + return Vec3(x, y, z) + + def record(self, ball): + self.recording.append((self.frame, + ball.getPosition()[0], + ball.getPosition()[1], + ball.getPosition()[2])) + self.aVRecording.append((self.frame, + ball.getAngularVel()[0], + ball.getAngularVel()[1], + ball.getAngularVel()[2])) + if self.frame > 50 and not self.frame % 13: + curFrame = self.recording[self.frame] + pastFrame5 = self.recording[self.frame - 11] + pastFrame10 = self.recording[self.frame - 34] + currPosA = Vec3(curFrame[1], curFrame[2], curFrame[3]) + past5PosA = Vec3(pastFrame5[1], pastFrame5[2], pastFrame5[3]) + past10PosA = Vec3(pastFrame10[1], pastFrame10[2], pastFrame10[3]) + displacement1 = currPosA - past5PosA + displacement2 = currPosA - past10PosA + if displacement1.lengthSquared() < 0.002 and displacement2.lengthSquared() < 0.002 and not self.grayCount and not self.onSlick: + ball.disable() + self.frame += 1 + + def preStep(self): + if hasattr(self, 'ballRay'): + bp = self.curGolfBall().getPosition() + self.ballRayBody.setPosition(bp[0], bp[1], bp[2]) + self.skyRay.setPosition(bp[0], bp[1], 50.0) + + def getOrderedContacts(self, entry): + c0 = self.space.getCollideId(entry.getGeom1()) + c1 = self.space.getCollideId(entry.getGeom2()) + if c0 > c1: + return (c1, c0) + else: + return (c0, c1) + + def postStep(self): + if self.canRender: + self.translucentLastFrame = self.translucentCurFrame[:] + self.translucentCurFrame = [] + self.onSlick = 0 + rayCount = 0 + skyRayHitPos = None + ballRayHitPos = None + bp = self.curGolfBall().getPosition() + for entry in self.colEntries: + c0, c1 = self.getOrderedContacts(entry) + x, y, z = entry.getContactPoint(0) + if c0 == GolfGlobals.OOB_RAY_COLLIDE_ID or c1 == GolfGlobals.OOB_RAY_COLLIDE_ID: + rayCount += 1 + if self.canRender: + if self.currentGolfer: + self.ballShadowDict[self.currentGolfer].setPos(x, y, z + 0.1) + if c1 == GolfGlobals.GRASS_COLLIDE_ID or c1 == GolfGlobals.HARD_COLLIDE_ID: + if self.curGolfBall().getPosition()[2] < z + 0.2: + ballRayHitPos = Vec3(x, y, z) + if c0 == GolfGlobals.OOB_RAY_COLLIDE_ID and c1 == GolfGlobals.SLICK_COLLIDE_ID: + self.onSlick = 1 + elif c0 == GolfGlobals.OOB_RAY_COLLIDE_ID and c1 == GolfGlobals.HARD_COLLIDE_ID: + self.onSlick = 1 + if c0 == GolfGlobals.GRASS_COLLIDE_ID and c1 == GolfGlobals.SKY_RAY_COLLIDE_ID: + self.lastSkyContactPoint = (x, y, z) + if self.curGolfBall().getPosition()[2] < z + 0.2 and rayCount == 0: + if self.skyContact in [1, 2]: + skyRayHitPos = Vec3(x, y, z) + self.skyContact += 1 + if self.doingRecording: + if c0 == GolfGlobals.OOB_RAY_COLLIDE_ID or c1 == GolfGlobals.OOB_RAY_COLLIDE_ID: + rayCount += 1 + if c1 == GolfGlobals.GRASS_COLLIDE_ID: + self.greenIn = self.frame + self.llv = self.curGolfBall().getLinearVel() + elif GolfGlobals.BALL_COLLIDE_ID in [c0, c1] and GolfGlobals.HOLE_CUP_COLLIDE_ID in [c0, c1]: + self.ballTouchedHoleFrame = self.frame + ballUndersideZ = self.curGolfBall().getPosition()[2] - 0.05 + if z < ballUndersideZ: + if not self.ballInHoleFrame: + self.ballInHoleFrame = self.frame + if self.ballFirstTouchedHoleFrame < self.ballLastTouchedGrass: + self.ballFirstTouchedHoleFrame = self.frame + if self.isBallInHole(self.curGolfBall()) and self.didHoleBreak == 0: + self.comObjNeedPass = 0 + ballLV = self.curGolfBall().getLinearVel() + ballAV = self.curGolfBall().getAngularVel() + self.curGolfBall().setLinearVel(0.5 * ballLV[0], 0.5 * ballLV[1], 0.5 * ballLV[2]) + self.curGolfBall().setAngularVel(0.5 * ballAV[0], 0.5 * ballAV[1], 0.5 * ballAV[2]) + self.notify.debug('BALL IN THE HOLE!!! FOO!') + self.didHoleBreak = 1 + return + elif GolfGlobals.BALL_COLLIDE_ID in [c0, c1] and GolfGlobals.GRASS_COLLIDE_ID in [c0, c1]: + if self.ballInHoleFrame: + self.ballInHoleFrame = 0 + self.notify.debug('setting ballInHoleFrame=0') + self.ballLastTouchedGrass = self.frame + elif self.canRender: + if c0 == GolfGlobals.TOON_RAY_COLLIDE_ID or c1 == GolfGlobals.TOON_RAY_COLLIDE_ID: + self.toonRayCollisionCallback(x, y, z) + if GolfGlobals.CAMERA_RAY_COLLIDE_ID in [c0, c1] and GolfGlobals.WINDMILL_BASE_COLLIDE_ID in [c0, c1]: + self.translucentCurFrame.append(self.windmillFanNodePath) + self.translucentCurFrame.append(self.windmillBaseNodePath) + if GolfGlobals.BALL_COLLIDE_ID in [c0, c1] and GolfGlobals.GRASS_COLLIDE_ID not in [c0, c1]: + self.handleBallHitNonGrass(c0, c1) + + if not self.curGolfBall().isEnabled(): + return + if rayCount == 0: + self.notify.debug('out of bounds detected!') + self.grayCount += 1 + self.outCommon = self.getCommonObjectData() + self.inCount = 0 + if skyRayHitPos: + self.curGolfBall().setPosition(skyRayHitPos[0], skyRayHitPos[1], skyRayHitPos[2] + 0.27) + self.notify.debug('SKY RAY ADJUST?') + else: + if self.grayCount > 1: + self.notify.debug('Back in bounds') + self.grayCount = 0 + self.inCount += 1 + if ballRayHitPos: + self.curGolfBall().setPosition(ballRayHitPos[0], ballRayHitPos[1], ballRayHitPos[2] + 0.245) + ballRayHitPos = None + if self.doingRecording: + self.notify.debug('BALL RAY ADJUST!') + self.notify.debug('%s' % self.curGolfBall().getLinearVel()) + if self.ballRocket > 0 and self.inCount > 1: + self.ballRocket -= 1 + rocketVel = self.curGolfBall().getLinearVel() + self.curGolfBall().setLinearVel(2.0 * rocketVel[0], 2.0 * rocketVel[1], 2.0 * rocketVel[2]) + self.notify.debug('ROCKET!!!!') + if self.grayCount > self.backAmount and self.doingRecording: + if self.greenIn > 2: + self.greenIn -= 2 + if self.greenIn > self.resetAt: + self.greenIn = self.resetAt - 10 + if self.greenIn < 0 or self.hasReset > 3: + self.greenIn = 0 + self.hasReset += 1 + self.notify.debug('BALL RESET frame %s greenIn %s resetAt %s' % (self.frame, self.greenIn, self.resetAt)) + self.useCommonObjectData(self.outCommon) + self.curGolfBall().setPosition(self.recording[self.greenIn][1], self.recording[self.greenIn][2], self.recording[self.greenIn][3] + 0.27) + self.curGolfBall().setAngularVel(0, 0, 0) + if self.hasReset < 3 and self.llv: + self.ballRocket += 1 + self.notify.debug(' BRAKE!!!!') + self.curGolfBall().setLinearVel(0.5 * self.llv[0], 0.5 * self.llv[1], 0.5 * self.llv[2]) + else: + self.notify.debug('back disable %s' % self.frame) + if self.lastSkyContactPoint: + self.curGolfBall().setPosition(self.lastSkyContactPoint[0], self.lastSkyContactPoint[1], self.lastSkyContactPoint[2] + 0.27) + self.curGolfBall().setLinearVel(0, 0, 0) + self.curGolfBall().disable() + self.recording = self.recording[:self.greenIn] + self.aVRecording = self.aVRecording[:self.greenIn] + self.frame = self.greenIn + self.resetAt = self.greenIn + self.grayCount = 0 + if self.ballFirstTouchedHoleFrame > self.frame: + self.notify.debug('reseting first touched hole, self.frame=%d self.ballFirstTouchedHoleFrame=%d' % (self.frame, self.ballFirstTouchedHoleFrame)) + self.ballFirstTouchedHoleFrame = 0 + if self.ballLastTouchedGrass > self.frame: + self.ballLastTouchedGrass = 0 + return + + def processRecording(self, errorMult = 1.0): + self.notify.debug('processRecording') + lastFrame = self.recording[len(self.recording) - 1][0] + countRemovals = 0 + for frame in self.recording: + if frame[0] == 0 or frame[0] == lastFrame: + pass + else: + index = self.recording.index(frame) + prevFrame = self.recording[index - 1] + nextFrame = self.recording[index + 1] + if self.predict(frame, prevFrame, nextFrame, errorMult): + self.recording.remove(frame) + countRemovals += 1 + + if countRemovals > 5: + self.processRecording() + elif len(self.recording) > 120: + self.processRecording(errorMult * 1.25) + else: + for frame in self.recording: + pass + + def processAVRecording(self, errorMult = 1.0, trials = 0): + self.notify.debug('processAVRecording') + lastFrame = self.recording[len(self.recording) - 1][0] + countRemovals = 0 + countTrials = trials + for frame in self.aVRecording: + if frame[0] == 0 or frame[0] == lastFrame: + pass + else: + index = self.aVRecording.index(frame) + prevFrame = self.aVRecording[index - 1] + nextFrame = self.aVRecording[index + 1] + if self.predictAV(frame, prevFrame, nextFrame, errorMult): + self.aVRecording.remove(frame) + countRemovals += 1 + else: + countTrials += 1 + + if countRemovals > 5: + self.processAVRecording(errorMult, countTrials) + elif len(self.aVRecording) > 80: + self.processAVRecording(errorMult * 1.25, countTrials) + else: + for frame in self.aVRecording: + pass + + def predict(self, frame, sourceFrame, destFrame, errorMult = 1.0): + tXY = 0.05 * errorMult + tZ = 0.05 * errorMult + projLength = destFrame[0] - sourceFrame[0] + projPen = destFrame[0] - frame[0] + propSource = float(projPen) / float(projLength) + propDest = 1.0 - propSource + projX = sourceFrame[1] * propSource + destFrame[1] * propDest + projY = sourceFrame[2] * propSource + destFrame[2] * propDest + projZ = sourceFrame[3] * propSource + destFrame[3] * propDest + varX = abs(projX - frame[1]) + varY = abs(projY - frame[2]) + varZ = abs(projZ - frame[3]) + if varX > tXY or varY > tXY or varZ > tZ: + return 0 + else: + return 1 + + def predictAV(self, frame, sourceFrame, destFrame, errorMult = 1.0): + tXYZ = 1.5 * errorMult + projLength = destFrame[0] - sourceFrame[0] + projPen = destFrame[0] - frame[0] + propSource = float(projPen) / float(projLength) + propDest = 1.0 - propSource + projX = sourceFrame[1] * propSource + destFrame[1] * propDest + projY = sourceFrame[2] * propSource + destFrame[2] * propDest + projZ = sourceFrame[3] * propSource + destFrame[3] * propDest + varX = abs(projX - frame[1]) + varY = abs(projY - frame[2]) + varZ = abs(projZ - frame[3]) + if varX > tXYZ or varY > tXYZ or varZ > tXYZ: + return 0 + else: + return 1 + + def handleBallHitNonGrass(self, c0, c1): + pass diff --git a/toontown/golf/GolfManagerAI.py b/toontown/golf/GolfManagerAI.py new file mode 100755 index 00000000..b7adb2f6 --- /dev/null +++ b/toontown/golf/GolfManagerAI.py @@ -0,0 +1,57 @@ +from direct.directnotify.DirectNotifyGlobal import * +from direct.showbase import DirectObject +from panda3d.core import * +from toontown.golf import DistributedGolfCourseAI + + +RequestHole = {} + + +def GolfManagerAI(): + if not hasattr(simbase, 'golf'): + simbase.golf = __GolfManagerAI() + return simbase.golf + + +class __GolfManagerAI(DirectObject.DirectObject): + notify = directNotify.newCategory('GolfManagerAI') + + def __init__(self): + DirectObject.DirectObject.__init__(self) + self.courseList = [] + + def delete(self): + DirectObject.DirectObject.delete(self) + + def readyGolfCourse(self, avIds, courseId = 0): + self.notify.debug('readyGolfCourse avIds=%s courseId=%d' % (avIds, courseId)) + golfZone = simbase.air.allocateZone() + preferredHoleId = None + for avId in avIds: + if avId in RequestHole: + preferredHoleId = RequestHole[avId][0] + newCourse = DistributedGolfCourseAI.DistributedGolfCourseAI( + golfZone, avIds, courseId, preferredHoleId) + newCourse.generateWithRequired(golfZone) + self.courseList.append(newCourse) + newCourse.addExpectedGolfers(avIds) + golfZone = newCourse.getZoneId() + self.notify.debug('%s' % self) + self.notify.debug('returning %d' % golfZone) + return golfZone + + def findGolfCourse(self, avId): + retval = None + for course in self.courseList: + if avId in course.avIdList: + retval = course + break + return retval + + def removeCourse(self, course): + if course in self.courseList: + for avId in course.avIdList: + if avId in RequestHole: + if not RequestHole[avId][1]: + del RequestHole[avId] + self.courseList.remove(course) diff --git a/toontown/golf/GolfRewardDialog.py b/toontown/golf/GolfRewardDialog.py new file mode 100755 index 00000000..204c4af4 --- /dev/null +++ b/toontown/golf/GolfRewardDialog.py @@ -0,0 +1,270 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from direct.task import Task +from direct.directnotify import DirectNotifyGlobal +from math import * +from direct.distributed.ClockDelta import * +from toontown.golf import GolfGlobals +from toontown.shtiker.GolfPage import GolfTrophy + +class GolfRewardDialog: + notify = directNotify.newCategory('GolfRewardDialog') + + def __init__(self, avIdList, trophyList, rankingsList, holeBestList, courseBestList, cupList, localAvId, tieBreakWinner, aimTimesList, endMovieCallback = None): + self.avIdList = avIdList + self.trophyList = trophyList + self.rankingsList = rankingsList + self.holeBestList = holeBestList + self.courseBestList = courseBestList + self.cupList = cupList + self.tieBreakWinner = tieBreakWinner + self.movie = None + self.myPlace = 0 + self.victory = None + self.endMovieCallback = endMovieCallback + self.aimTimesList = aimTimesList + self.setup(localAvId) + return + + def calcTrophyTextListForOnePlayer(self, avId): + retval = [] + av = base.cr.doId2do.get(avId) + if av and avId in self.avIdList: + playerIndex = self.avIdList.index(avId) + name = av.getName() + for trophyIndex in xrange(len(self.trophyList[playerIndex])): + wonTrophy = self.trophyList[playerIndex][trophyIndex] + if wonTrophy: + trophyName = TTLocalizer.GolfTrophyDescriptions[trophyIndex] + text = TTLocalizer.GolfAvReceivesTrophy % {'name': name, + 'award': trophyName} + retval.append(text) + + return retval + + def calcCupTextListForAllPlayers(self, localAvId): + retval = [] + for cupPlayerIndex in xrange(len(self.avIdList)): + if self.avIdList[cupPlayerIndex] != localAvId: + av = base.cr.doId2do.get(self.avIdList[cupPlayerIndex]) + name = '' + if av: + name = av.getName() + cupIndex = 0 + for cupIndex in xrange(len(self.cupList[cupPlayerIndex])): + if self.cupList[cupPlayerIndex][cupIndex]: + cupName = TTLocalizer.GolfCupDescriptions[cupIndex] + text = TTLocalizer.GolfAvReceivesCup % {'name': name, + 'cup': cupName} + retval.append(text) + + for cupPlayerIndex in xrange(len(self.avIdList)): + if self.avIdList[cupPlayerIndex] == localAvId: + av = base.cr.doId2do.get(self.avIdList[cupPlayerIndex]) + name = av.getName() + cupIndex = 0 + for cupIndex in xrange(len(self.cupList[cupPlayerIndex])): + if self.cupList[cupPlayerIndex][cupIndex]: + cupName = TTLocalizer.GolfCupDescriptions[cupIndex] + text = TTLocalizer.GolfAvReceivesCup % {'name': name, + 'cup': cupName} + retval.append(text) + + return retval + + def calcRankings(self, localAvId): + retval = [] + self.notify.debug('aimTimesList=%s' % self.aimTimesList) + for rank in xrange(len(self.rankingsList) + 1): + for avIndex in xrange(len(self.avIdList)): + if self.rankingsList[avIndex] == rank: + name = ' ' + av = base.cr.doId2do.get(self.avIdList[avIndex]) + if av: + name = av.getName() + text = '%d. ' % rank + ' ' + name + if GolfGlobals.TIME_TIE_BREAKER: + time = self.aimTimesList[avIndex] + minutes = int(time / 60) + time -= minutes * 60 + seconds = int(time) + padding = (seconds < 10 and ['0'] or [''])[0] + time -= seconds + fraction = str(time)[2:4] + fraction = fraction + '0' * (2 - len(fraction)) + timeStr = "%d'%s%d''%s" % (minutes, + padding, + seconds, + fraction) + text += ' - ' + timeStr + retval.append(text) + if self.avIdList[avIndex] == localAvId: + self.myPlace = rank + + return retval + + def calcHoleBestTextListForAllPlayers(self, localAvId): + retval = [] + if GolfGlobals.CalcOtherHoleBest: + for hbPlayerIndex in xrange(len(self.avIdList)): + if self.avIdList[hbPlayerIndex] != localAvId: + av = base.cr.doId2do.get(self.avIdList[hbPlayerIndex]) + name = av.getName() + for hbIndex in xrange(len(self.holeBestList[hbPlayerIndex])): + if self.holeBestList[hbPlayerIndex][hbIndex]: + hbName = TTLocalizer.GolfHoleNames[hbIndex] + text = TTLocalizer.GolfAvReceivesHoleBest % {'name': name, + 'hole': hbName} + retval.append(text) + + for hbPlayerIndex in xrange(len(self.avIdList)): + if self.avIdList[hbPlayerIndex] == localAvId: + av = base.cr.doId2do.get(self.avIdList[hbPlayerIndex]) + name = av.getName() + for hbIndex in xrange(len(self.holeBestList[hbPlayerIndex])): + if self.holeBestList[hbPlayerIndex][hbIndex]: + hbName = TTLocalizer.GolfHoleNames[hbIndex] + text = TTLocalizer.GolfAvReceivesHoleBest % {'name': name, + 'hole': hbName} + retval.append(text) + + return retval + + def calcCourseBestTextListForAllPlayers(self, localAvId): + retval = [] + if GolfGlobals.CalcOtherCourseBest: + for cbPlayerIndex in xrange(len(self.avIdList)): + if self.avIdList[cbPlayerIndex] != localAvId: + av = base.cr.doId2do.get(self.avIdList[cbPlayerIndex]) + name = av.getName() + for cbIndex in xrange(len(self.holeBestList[cbPlayerIndex])): + if self.holeBestList[cbPlayerIndex][cbIndex]: + cbName = TTLocalizer.GolfCourseNames[cbIndex] + text = TTLocalizer.GolfAvReceivesCourseBest % {'name': name, + 'course': cbName} + retval.append(text) + + for cbPlayerIndex in xrange(len(self.avIdList)): + if self.avIdList[cbPlayerIndex] == localAvId: + av = base.cr.doId2do.get(self.avIdList[cbPlayerIndex]) + name = av.getName() + for cbIndex in xrange(len(self.courseBestList[cbPlayerIndex])): + if self.courseBestList[cbPlayerIndex][cbIndex]: + cbName = TTLocalizer.GolfCourseNames[cbIndex] + text = TTLocalizer.GolfAvReceivesCourseBest % {'name': name, + 'course': cbName} + retval.append(text) + + return retval + + def createRewardMovie(self, localAvId): + retval = Sequence(name='Reward sequence', autoPause=1) + self.trophy = None + + def setTrophyLabelText(text, playerIndex, trophyIndex): + self.rankLabel.hide() + self.rewardLabel.hide() + self.trophy = GolfTrophy(level=self.trophyList[playerIndex][trophyIndex], parent=self.trophyLabel, pos=(1.3, 0, -0.25)) + self.trophy.setScale(0.65, 1, 0.65) + self.trophy.show() + self.trophyLabel['text'] = text + + def setRewardLabelText(text): + self.rewardLabel.show() + self.rankLabel.hide() + self.trophyLabel.hide() + if self.trophy: + self.trophy.hide() + self.rewardLabel['text'] = text + + def setRankLabelText(text): + self.rankLabel.show() + self.rewardLabel.hide() + self.trophyLabel.hide() + if self.trophy: + self.trophy.hide() + self.rankLabel['text'] = text + if len(self.avIdList) > 1: + self.victory = base.loadSfx('phase_6/audio/sfx/KART_Applause_%d.ogg' % self.myPlace) + self.victory.play() + + for avId in self.avIdList: + if avId != localAvId: + rewardTextList = self.calcTrophyTextListForOnePlayer(avId) + trophyIndex = 0 + for rewardText in rewardTextList: + playerIndex = self.avIdList.index(avId) + var = (rewardText, playerIndex, trophyIndex) + oneTrophyIval = Parallel(Func(setTrophyLabelText, rewardText, playerIndex, trophyIndex), LerpColorScaleInterval(self.trophyLabel, 4, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='easeIn')) + trophyIndex = trophyIndex + 1 + retval.append(oneTrophyIval) + + rewardTextList = self.calcTrophyTextListForOnePlayer(localAvId) + trophyIndex = 0 + playerIndex = self.avIdList.index(localAvId) + for rewardText in rewardTextList: + if len(rewardTextList) > 0: + var = (rewardText, playerIndex, trophyIndex) + oneRewardIval = Parallel(Func(setTrophyLabelText, rewardText, playerIndex, trophyIndex), LerpColorScaleInterval(self.trophyLabel, 4, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='easeIn')) + retval.append(oneRewardIval) + + rewardCupList = self.calcCupTextListForAllPlayers(localAvId) + if len(rewardCupList) > 0: + for rewardText in rewardCupList: + oneCupIval = Parallel(Func(setRewardLabelText, rewardText), LerpColorScaleInterval(self.rewardLabel, 4, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='noBlend')) + retval.append(oneCupIval) + + if self.tieBreakWinner: + name = '' + av = base.cr.doId2do.get(self.tieBreakWinner) + if av: + name = av.getName() + if GolfGlobals.TIME_TIE_BREAKER: + rewardText = TTLocalizer.GolfTimeTieBreakWinner % {'name': name} + else: + rewardText = TTLocalizer.GolfTieBreakWinner % {'name': name} + randomWinnerIval = Parallel(Func(setRewardLabelText, rewardText), LerpColorScaleInterval(self.rewardLabel, 7, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='noBlend')) + retval.append(randomWinnerIval) + rankings = self.calcRankings(localAvId) + rankText = TTLocalizer.GolfRanking + '\n' + for rank in xrange(len(rankings)): + rankText = rankText + rankings[rank] + '\n' + + oneRankIval = Parallel(Func(setRankLabelText, rankText), LerpColorScaleInterval(self.rankLabel, 8, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 1), blendType='easeIn')) + retval.append(oneRankIval) + rewardHoleList = self.calcHoleBestTextListForAllPlayers(localAvId) + if len(rewardHoleList) > 0: + for rewardText in rewardHoleList: + oneHoleIval = Parallel(Func(setRewardLabelText, rewardText), LerpColorScaleInterval(self.rewardLabel, 8, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='easeIn')) + retval.append(oneHoleIval) + + rewardCourseList = self.calcCourseBestTextListForAllPlayers(localAvId) + if len(rewardCourseList) > 0: + for rewardText in rewardCourseList: + oneCourseIval = Parallel(Func(setRewardLabelText, rewardText), LerpColorScaleInterval(self.rewardLabel, 4, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='easeIn')) + retval.append(oneCourseIval) + + if self.endMovieCallback: + retval.append(Func(self.endMovieCallback)) + return retval + + def setup(self, localAvId): + self.rewardBoard = DirectFrame(parent=aspect2d, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.75, 1, 0.6), pos=(0, 0, -0.6)) + self.rewardLabel = DirectLabel(parent=self.rewardBoard, relief=None, pos=(-0, 0, 0), text_align=TextNode.ACenter, text='', text_scale=0.05, text_wordwrap=30) + self.rankLabel = DirectLabel(parent=self.rewardBoard, relief=None, pos=(-0, 0, 0.17), text_align=TextNode.ACenter, text='', text_scale=0.06) + self.trophyLabel = DirectLabel(parent=self.rewardBoard, relief=None, pos=(-0.7, 0, 0.05), text_align=TextNode.ALeft, text='', text_scale=0.06, text_wordwrap=20) + self.movie = self.createRewardMovie(localAvId) + return + + def delete(self): + self.movie.pause() + self.notify.debug('Movie is paused') + self.rewardBoard.destroy() + self.notify.debug('Reward board is destroyed') + self.movie = None + self.notify.debug('Movie is deleted') + return + + def getMovie(self): + return self.movie diff --git a/toontown/golf/GolfScoreBoard.py b/toontown/golf/GolfScoreBoard.py new file mode 100755 index 00000000..b80f2406 --- /dev/null +++ b/toontown/golf/GolfScoreBoard.py @@ -0,0 +1,229 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.task import Task +from math import * +from direct.distributed.ClockDelta import * +from toontown.golf import GolfGlobals +from pandac.PandaModules import LineSegs +AUTO_HIDE_TIMEOUT = 3 + +class GolfScoreBoard: + notify = directNotify.newCategory('GolfScoreBoard') + + def __init__(self, golfCourse): + self.golfCourse = golfCourse + self.numPlayas = len(golfCourse.avIdList) + self.avIdList = golfCourse.avIdList + self.playaTags = [] + self.scoreTags = [] + self.totalTags = [] + self.scoreLabels = [] + self.holeLabels = [] + self.parLabels = [] + self.numExited = 0 + self.setup() + + def setup(self): + self.scoreboard = DirectFrame(parent=aspect2d, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.9, 1, 1.05), pos=(0, 0, 0.375)) + self.lines = LineSegs() + self.lines.setColor(0, 0, 0, 1) + self.lines.setThickness(2) + guiModel = loader.loadModel('phase_6/models/golf/golf_gui') + highlight = loader.loadModel('phase_6/models/golf/headPanel') + self.maximizeB = DirectButton(parent=base.a2dBottomRight, pos=(-0.15, 0, 0.15), relief=None, state=DGG.NORMAL, image=(guiModel.find('**/score_card_icon'), guiModel.find('**/score_card_icon_rollover'), guiModel.find('**/score_card_icon_rollover')), image_scale=(0.2, 1, 0.2), command=self.showBoard) + self.vertOffset = 0.13 + self.playaTop = 0.12 + horzOffset = 0.12 + holeTop = 0.3 + self.vCenter = 0.025 + totScore = 0 + totPar = 0 + self.lineVStart = -0.465 + self.lineHStart = 0.17 + self.lineHorOffset = 0.13 + self.lineVertOffset = 0.125 + self.lineVCenter = 0.025 + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.minimizeB = DirectButton(parent=self.scoreboard, pos=(0, 0, self.lineHStart - 0.59), relief=None, state=DGG.NORMAL, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(1, 1, 1), command=self.hideBoard, extraArgs=[None]) + self.exitCourseB = DirectButton(parent=self.scoreboard, pos=(0, 0, self.lineHStart - 0.59), relief=None, state=DGG.NORMAL, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(1, 1, 1), text=TTLocalizer.GolfExitCourse, text_scale=0.04, text_pos=TTLocalizer.GSBexitCourseBPos, command=self.exitCourse) + self.exitCourseB.hide() + self.highlightCur = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.003, 0, 0.038), image=highlight, image_scale=(1.82, 1, 0.135)) + self.titleBar = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.003, 0, 0.166), color=(0.7, 0.7, 0.7, 0.3), image=highlight, image_scale=(1.82, 1, 0.195)) + self.titleBar.show() + self.highlightCur.show() + buttons.removeNode() + guiModel.removeNode() + title = GolfGlobals.getCourseName(self.golfCourse.courseId) + ' - ' + GolfGlobals.getHoleName(self.golfCourse.holeIds[self.golfCourse.curHoleIndex]) + self.titleLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(0, 0, holeTop + 0.1), text_align=TextNode.ACenter, text=title, text_scale=TTLocalizer.GSBtitleLabel, text_font=ToontownGlobals.getSignFont(), text_fg=(0, 0.5, 0.125, 1)) + self.playaLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart - 0.23, 0, holeTop), text_align=TextNode.ACenter, text=TTLocalizer.GolfHole, text_font=ToontownGlobals.getMinnieFont(), text_scale=0.05) + for holeLIndex in xrange(self.golfCourse.numHoles): + holeLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.055 + horzOffset * holeLIndex, 0, holeTop), text_align=TextNode.ACenter, text='%s' % (holeLIndex + 1), text_scale=0.05) + self.holeLabels.append(holeLabel) + + self.totalLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.1 + horzOffset * 9.5, 0, holeTop), text_align=TextNode.ACenter, text=TTLocalizer.GolfTotal, text_font=ToontownGlobals.getMinnieFont(), text_scale=0.05) + self.parTitleLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart - 0.23, 0, holeTop - 0.1), text_align=TextNode.ACenter, text=TTLocalizer.GolfPar, text_font=ToontownGlobals.getMinnieFont(), text_scale=0.05) + for parHoleIndex in xrange(self.golfCourse.numHoles): + parLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.055 + horzOffset * parHoleIndex, 0, holeTop - 0.1), text_align=TextNode.ACenter, text='%s' % GolfGlobals.HoleInfo[self.golfCourse.holeIds[parHoleIndex]]['par'], text_scale=0.05, text_wordwrap=10) + totPar = totPar + GolfGlobals.HoleInfo[self.golfCourse.holeIds[parHoleIndex]]['par'] + self.parLabels.append(parLabel) + + parLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.1 + horzOffset * 9.5, 0, holeTop - 0.1), text_align=TextNode.ACenter, text='%s' % totPar, text_scale=0.05, text_wordwrap=10) + self.parLabels.append(parLabel) + vert = 0.0 + self.numPlayas = len(self.golfCourse.avIdList) + for playaIndex in xrange(self.numPlayas): + name = TTLocalizer.GolfUnknownPlayer + av = base.cr.doId2do.get(self.golfCourse.avIdList[playaIndex]) + if av: + name = av.getName() + playaLabel = DirectLabel(parent=self.scoreboard, relief=None, text_align=TextNode.ACenter, text=name, text_scale=0.05, text_wordwrap=9) + self.playaTags.append(playaLabel) + textN = playaLabel.component(playaLabel.components()[0]) + if type(textN) == OnscreenText: + try: + if textN.textNode.getWordwrappedWtext() != name: + vert = self.playaTop - self.vertOffset * playaIndex + else: + vert = self.playaTop - self.vertOffset * playaIndex - self.vCenter + except: + vert = self.playaTop - self.vertOffset * playaIndex + + self.playaTags[playaIndex].setPos(self.lineVStart - 0.23, 0, vert) + self.notify.debug('self.text height = %f' % self.playaTags[playaIndex].getHeight()) + holeIndex = 0 + for holeIndex in xrange(self.golfCourse.numHoles): + holeLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.055 + horzOffset * holeIndex, 0, self.playaTop - self.vertOffset * playaIndex - self.vCenter), text_align=TextNode.ACenter, text='-', text_scale=0.05, text_wordwrap=10) + self.scoreTags.append(holeLabel) + + holeLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.1 + horzOffset * 9.5, 0, self.playaTop - self.vertOffset * playaIndex - self.vCenter), text_align=TextNode.ACenter, text='-', text_scale=0.05, text_wordwrap=10) + self.totalTags.append(holeLabel) + + self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart + 0.19) + self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart + 0.19) + self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart + 0.09) + self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart + 0.09) + self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart) + self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart) + self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart + 0.19) + self.lines.drawTo(self.lineVStart - 0.45, 0, self.lineHStart - 4 * 0.13) + self.lines.moveTo(self.lineVStart, 0, self.lineHStart + 0.19) + self.lines.drawTo(self.lineVStart, 0, self.lineHStart - 4 * 0.13) + for x in xrange(4): + self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart - (x + 1) * self.lineHorOffset) + self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset + 0.005, 0, self.lineHStart - (x + 1) * self.lineHorOffset) + + for y in xrange(10): + self.lines.moveTo(self.lineVStart + y * self.lineVertOffset, 0, self.lineHStart + 0.19) + self.lines.drawTo(self.lineVStart + y * self.lineVertOffset, 0, self.lineHStart - 4 * 0.13) + + self.lines.moveTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart + 0.19) + self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart - 4 * 0.13) + self.scoreboard.attachNewNode(self.lines.create()) + self.hide() + return + + def getScoreLabel(self, avIdorIndex, holeNum): + index = None + if avIdorIndex < 100: + index = avIdorIndex + else: + for playaIndex in xrange(self.numPlayas): + if self.golfCourse.avIdList[playaIndex] == avIdorIndex: + index = playaIndex + + return self.scoreTags[index * self.golfCourse.numHoles + holeNum] + + def update(self): + self.showBoard() + taskMgr.doMethodLater(AUTO_HIDE_TIMEOUT, self.hideBoard, 'hide score board') + + def hideBoard(self, task): + self.hide() + + def hide(self): + self.scoreboard.hide() + self.maximizeB.show() + + def showBoardFinal(self, task = None): + self.exitCourseB.show() + self.minimizeB.hide() + self.showBoard() + + def showBoard(self, task = None): + scoreDict = self.golfCourse.scores + x = 0 + currentGolfer = self.golfCourse.getCurGolfer() + for playaIndex in xrange(self.numPlayas): + if self.golfCourse.isGameDone(): + self.playaTags[playaIndex].setColor(0, 0, 0, 1) + elif currentGolfer == self.golfCourse.avIdList[playaIndex]: + self.highlightCur.setColor(*GolfGlobals.PlayerColors[playaIndex]) + self.highlightCur.setAlphaScale(0.4) + self.highlightCur.setPos(-0.003, 0, 0.038 - playaIndex * (self.lineVertOffset + 0.005)) + self.highlightCur.show() + else: + self.playaTags[playaIndex].setColor(0, 0, 0, 1) + + for avId in self.avIdList: + holeIndex = 0 + totScore = 0 + playerExited = False + for y in xrange(len(self.golfCourse.exitedAvIdList)): + if self.golfCourse.exitedAvIdList[y] == avId: + self.playaTags[x].setColor(0.7, 0.7, 0.7, 1) + holeIndex = 0 + for holeIndex in xrange(self.golfCourse.numHoles): + self.getScoreLabel(self.avIdList[x], holeIndex).setColor(0.7, 0.7, 0.7, 1) + + self.totalTags[x].setColor(0.7, 0.7, 0.7, 1) + playerExited = True + + if playerExited == False: + for holeIndex in xrange(self.golfCourse.numHoles): + if holeIndex <= self.golfCourse.curHoleIndex: + self.getScoreLabel(avId, holeIndex)['text'] = '%s' % scoreDict[avId][holeIndex] + totScore = totScore + scoreDict[avId][holeIndex] + if self.golfCourse.isGameDone() == False: + if holeIndex == self.golfCourse.curHoleIndex: + self.getScoreLabel(avId, holeIndex).setColor(1, 0, 0, 1) + self.holeLabels[holeIndex].setColor(1, 0, 0, 1) + self.parLabels[holeIndex].setColor(1, 0, 0, 1) + title = GolfGlobals.getCourseName(self.golfCourse.courseId) + ' - ' + GolfGlobals.getHoleName(self.golfCourse.holeIds[self.golfCourse.curHoleIndex]) + self.titleLabel['text'] = title + else: + self.getScoreLabel(avId, holeIndex).setColor(0, 0, 0, 1) + self.holeLabels[holeIndex].setColor(0, 0, 0, 1) + self.parLabels[holeIndex].setColor(0, 0, 0, 1) + + self.totalTags[x]['text'] = '%s' % totScore + if self.golfCourse.isGameDone(): + self.getScoreLabel(avId, self.golfCourse.numHoles - 1).setColor(0, 0, 0, 1) + self.totalTags[x].setColor(1, 0, 0, 1) + x = x + 1 + + y = 0 + if self.golfCourse.isGameDone(): + self.parLabels[self.golfCourse.numHoles - 1].setColor(0, 0, 0, 1) + self.holeLabels[self.golfCourse.numHoles - 1].setColor(0, 0, 0, 1) + self.parLabels[self.golfCourse.numHoles].setColor(1, 0, 0, 1) + self.totalLabel.setColor(1, 0, 0, 1) + self.scoreboard.show() + self.maximizeB.hide() + + def exitCourse(self): + course = self.golfCourse + self.delete() + course.exitEarly() + + def delete(self): + if self.maximizeB: + self.maximizeB.destroy() + self.maximizeB = None + if self.scoreboard: + self.scoreboard.destroy() + self.scoreboard = None + self.golfCourse = None + taskMgr.remove('hide score board') + return diff --git a/toontown/golf/PhysicsWorldBase.py b/toontown/golf/PhysicsWorldBase.py new file mode 100755 index 00000000..19a2b36e --- /dev/null +++ b/toontown/golf/PhysicsWorldBase.py @@ -0,0 +1,722 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from panda3d.core import * +from math import * +import math +from direct.fsm.FSM import FSM +from toontown.minigame import ArrowKeys +from direct.showbase import PythonUtil +from direct.task import Task +from direct.distributed.ClockDelta import * +import BuildGeometry +from toontown.golf import GolfGlobals +import random, time + +def scalp(vec, scal): + vec0 = vec[0] * scal + vec1 = vec[1] * scal + vec2 = vec[2] * scal + vec = Vec3(vec0, vec1, vec2) + + +def length(vec): + return sqrt(vec[0] ** 2 + vec[1] ** 2 + vec[2] ** 2) + + +class PhysicsWorldBase: + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPhysicsWorld') + + def __init__(self, canRender = 0): + self.canRender = canRender + self.world = OdeWorld() + self.space = OdeSimpleSpace() + self.contactgroup = OdeJointGroup() + self.bodyList = [] + self.geomList = [] + self.massList = [] + self.rayList = [] + self.showContacts = 0 + self.jointMarkers = [] + self.jointMarkerCount = 64 + self.meshDataList = [] + self.geomDataList = [] + self.commonObjectInfoDict = {} + self.maxColCount = 0 + if self.canRender: + self.odePandaRelationList = self.bodyList + self.root = render.attachNewNode('physics root node') + else: + self.root = NodePath('physics root node') + self.placerNode = self.root.attachNewNode('Placer') + self.subPlacerNode = self.placerNode.attachNewNode('Placer Sub Node') + self.commonObjectDict = {} + self.commonId = 0 + self.worldAttach = self.root.attachNewNode('physics geom attach point') + self.timingCycleLength = 10.0 + self.timingCycleOffset = 0.0 + self.timingSimTime = 0.0 + self.FPS = 90.0 + self.refFPS = 60.0 + self.DTAStep = 1.0 / self.FPS + self.refCon = 1.2 + + self.collisionEventName = 'ode-collision-%d' % id(self) + self.space.setCollisionEvent(self.collisionEventName) + self.accept(self.collisionEventName, self.__collisionHandler) + + def delete(self): + self.notify.debug('Max Collision Count was %s' % self.maxColCount) + self.stopSim() + self.commonObjectDict = None + if self.canRender: + for pair in self.odePandaRelationList: + pair[0].removeNode() + pair[1].destroy() + + self.odePandaRelationList = None + else: + for body in self.bodyList: + body[1].destroy() + + self.bodyList = None + for mass in self.massList: + mass = None + + for geom in self.geomList: + geom.destroy() + geom = None + + for ray in self.rayList: + ray.destroy() + ray = None + + self.placerNode.removeNode() + self.root.removeNode() + for marker in self.jointMarkers: + marker.removeNode() + + self.jointMarkers = None + for data in self.geomDataList: + data.destroy() + + for data in self.meshDataList: + data.destroy() + + self.floor.destroy() + self.floor = None + self.contactgroup.empty() + self.world.destroy() + self.space.destroy() + self.world = None + self.space = None + + self.ignore(self.collisionEventName) + + def setupSimulation(self): + self.world.setAutoDisableFlag(0) + self.world.setAutoDisableLinearThreshold(0.15) + self.world.setAutoDisableAngularThreshold(0.15) + self.world.setAutoDisableSteps(2) + self.world.setGravity(0, 0, -25) + self.world.setErp(0.8) + self.world.setCfm(1e-05) + self.world.initSurfaceTable(5) + self.world.setSurfaceEntry(0, 0, 150, 0.05, 0.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(1, 1, 1500, 0.05, 0.1, 0.9, 1e-05, 0.0, 0.001 / self.refCon) + self.world.setSurfaceEntry(2, 2, 150, 0.05, 0.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(0, 2, 150, 0.05, 0.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(0, 3, 150, 0.0, 0.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(1, 3, 150, 0.0, 99.1, 0.9, 1e-05, 0.0, 1.0 / self.refCon) + self.world.setSurfaceEntry(2, 3, 150, 0.0, 9.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(3, 3, 150, 0.0, 9.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(4, 4, 150, 0.0, 9.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(1, 4, 150, 0.0, 99.1, 0.9, 1e-05, 0.0, 0.001 / self.refCon) + self.world.setSurfaceEntry(pos1=0, pos2=1, mu=80, bounce=0.15, bounce_vel=0.1, soft_erp=0.9, soft_cfm=1e-05, slip=0.0, dampen=0.35 / self.refCon) + self.world.setSurfaceEntry(pos1=2, pos2=1, mu=1500, bounce=0.9, bounce_vel=0.01, soft_erp=0.9, soft_cfm=1e-05, slip=0.0, dampen=0.001 / self.refCon) + self.floor = OdePlaneGeom(self.space, Vec4(0.0, 0.0, 1.0, -20.0)) + self.floor.setCollideBits(BitMask32(0)) + self.floor.setCategoryBits(BitMask32(3840)) + self.space.setAutoCollideWorld(self.world) + self.space.setAutoCollideJointGroup(self.contactgroup) + self.world.setQuickStepNumIterations(8) + self.DTA = 0.0 + self.frameCounter = 0 + if self.canRender: + for count in xrange(self.jointMarkerCount): + testMarker = render.attachNewNode('Joint Marker') + ballmodel = loader.loadModel('phase_3/models/misc/sphere') + ballmodel.reparentTo(testMarker) + ballmodel.setScale(0.1) + testMarker.setPos(0.0, 0.0, -100.0) + self.jointMarkers.append(testMarker) + + def setTimingCycleLength(self, time): + self.timingCycleLength = time + + def getTimingCycleLength(self): + return self.timingCycleLength + + def getCycleTime(self, doprint = 0): + cycleTime = (globalClock.getRealTime() + self.timingCycleOffset) % self.timingCycleLength + if doprint: + print 'Get Cycle Time %s' % cycleTime + return cycleTime + + def setTimeIntoCycle(self, time, doprint = 0): + trueCycleTime = globalClock.getRealTime() % self.timingCycleLength + self.timingCycleOffset = time - trueCycleTime + if doprint: + self.notify.debug('Set Cycle Time %s' % self.timingCycleOffset) + self.notify.debug('SET cycle time %s' % ((globalClock.getRealTime() + self.timingCycleOffset) % self.timingCycleLength)) + + def getSimCycleTime(self): + return + return self.timingSimTime % self.timingCycleLength + + def startSim(self): + taskMgr.add(self.__simulationTask, 'simulation task') + + def stopSim(self): + taskMgr.remove('simulation task') + + def __simulationTask(self, task): + self.DTA += globalClock.getDt() + self.frameCounter += 1 + if self.frameCounter >= 10: + self.frameCounter = 0 + startTime = globalClock.getRealTime() + colCount = 0 + while self.DTA >= self.DTAStep: + self.DTA -= self.DTAStep + self.preStep() + self.simulate() + self.postStep() + + if self.canRender: + self.placeBodies() + if self.frameCounter == 0: + endTime = globalClock.getRealTime() - startTime + return task.cont + + def __collisionHandler(self, entry): + self.colEntries.append(entry) + + def simulate(self): + self.colEntries = [] + self.space.autoCollide() + # We need the callbacks processed now, before we try to look at colEntries, so: + eventMgr.doEvents() + self.colCount = len(self.colEntries) + if self.maxColCount < self.colCount: + self.maxColCount = self.colCount + self.notify.debug('New Max Collision Count %s' % self.maxColCount) + self.world.quickStep(self.DTAStep) + for bodyPair in self.bodyList: + self.world.applyDampening(self.DTAStep, bodyPair[1]) + + self.contactgroup.empty() + self.commonObjectControl() + self.timingSimTime = self.timingSimTime + self.DTAStep + + def placeBodies(self): + for pair in self.odePandaRelationList: + pandaNodePathGeom = pair[0] + odeBody = pair[1] + if pandaNodePathGeom: + pandaNodePathGeom.setPos(odeBody.getPosition()) + rotation = odeBody.getRotation() * (180.0 / math.pi) + pandaNodePathGeom.setQuat(Quat(odeBody.getQuaternion()[0], odeBody.getQuaternion()[1], odeBody.getQuaternion()[2], odeBody.getQuaternion()[3])) + + def preStep(self): + pass + + def postStep(self): + if self.showContacts and self.canRender: + for count in xrange(self.jointMarkerCount): + pandaNodePathGeom = self.jointMarkers[count] + if count < self.colCount: + pandaNodePathGeom.setPos(self.space.getContactData(count * 3 + 0), self.space.getContactData(count * 3 + 1), self.space.getContactData(count * 3 + 2)) + else: + pandaNodePathGeom.setPos(0.0, 0.0, -100.0) + + def commonObjectControl(self): + time = self.getCycleTime() + for key in self.commonObjectDict: + if key not in self.commonObjectInfoDict: + self.commonObjectInfoDict[key] = None + entry = self.commonObjectDict[key] + if entry[1] in [2, 4]: + type = entry[1] + body = entry[2] + motor = entry[3] + timeData = entry[4] + forceData = entry[5] + eventData = entry[6] + model = entry[7] + force = 0.0 + for index in xrange(len(timeData)): + if index == len(timeData) - 1 and timeData[index] < time or timeData[index] < time and timeData[index + 1] > time: + force = forceData[index] + event = eventData[index] + if event != self.commonObjectInfoDict[key]: + self.commonObjectEvent(key, model, type, force, event) + self.commonObjectInfoDict[key] = event + + motor.setParamVel(force) + + return + + def commonObjectEvent(self, key, model, type, force, event): + self.notify.debug('commonObjectForceEvent %s %s %s %s %s' % (key, + model, + type, + force, + event)) + + def getCommonObjectData(self): + objectStream = [(0, + 0, + self.getCycleTime(), + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0)] + for key in self.commonObjectDict: + objectPair = self.commonObjectDict[key] + object = objectPair[2] + pos3 = object.getPosition() + quat4 = object.getQuaternion() + anV3 = object.getAngularVel() + lnV3 = object.getLinearVel() + data = (objectPair[0], + objectPair[1], + pos3[0], + pos3[1], + pos3[2], + quat4[0], + quat4[1], + quat4[2], + quat4[3], + anV3[0], + anV3[1], + anV3[2], + lnV3[0], + lnV3[1], + lnV3[2]) + objectStream.append(data) + + if len(objectStream) <= 1: + data = (0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + objectStream.append(data) + return objectStream + + def useCommonObjectData(self, objectData, enable = 1): + if not objectData: + return + if objectData[1][1] == 99: + return + time = objectData[0] + self.setTimeIntoCycle(time[2]) + if time[2] > self.timingCycleLength: + pass + for dataIndex in xrange(1, len(objectData)): + data = objectData[dataIndex] + commonObject = self.commonObjectDict[data[0]] + commonObject[2].setPosition(data[2], data[3], data[4]) + commonObject[2].setQuaternion(Quat(data[5], data[6], data[7], data[8])) + commonObject[2].setAngularVel(data[9], data[10], data[11]) + commonObject[2].setLinearVel(data[12], data[13], data[14]) + if enable: + commonObject[2].enable() + else: + commonObject[2].disable() + + def createCommonObject(self, type, commonId, pos, hpr, sizeX = 0, sizeY = 0, moveDistance = 0): + if commonId == None: + commonId = self.commonId + self.commonId += 1 + vPos = Point3(float(pos[0]), float(pos[1]), float(pos[2])) + vHpr = Vec3(float(hpr[0]), float(hpr[1]), float(hpr[2])) + rHpr = Vec3(float(hpr[0]), float(hpr[1]), float(hpr[2])) + self.placerNode.setHpr(vHpr) + self.placerNode.setPos(vPos) + if type == 0: + model, box = self.createBox(self.world, self.space, 10.0, 5.0, 5.0, 5.0) + box.setPosition(vPos) + self.placerNode.setHpr(vHpr) + box.setQuaternion(self.placerNode.getQuat()) + self.commonObjectDict[commonId] = (commonId, type, box) + elif type == 1: + model, cross = self.createCross(self.world, self.space, 1.0, 3.0, 12.0, 2.0, 2) + motor = OdeHingeJoint(self.world) + cross.setPosition(vPos) + cross.setQuaternion(self.placerNode.getQuat()) + ourAxis = render.getRelativeVector(self.placerNode, Vec3(0, 0, 1)) + motor.setParamVel(1.5) + motor.setParamFMax(500000000.0) + boxsize = Vec3(1.0, 1.0, 1.0) + motor.attachBody(cross, 0) + motor.setAnchor(vPos) + motor.setAxis(ourAxis) + self.cross = cross + cross.enable() + self.commonObjectDict[commonId] = (commonId, type, cross) + elif type == 2: + ourAxis = render.getRelativeVector(self.placerNode, Vec3(0, 0, 1)) + model, box = self.createBox(self.world, self.space, 10.0, 5.0, 5.0, 5.0, 2) + box.setPosition(vPos) + box.setQuaternion(self.placerNode.getQuat()) + motor = OdeSliderJoint(self.world) + motor.attachBody(box, 0) + motor.setAxis(ourAxis) + motor.setParamVel(3.0) + motor.setParamFMax(5000000.0) + motor.setParamHiStop(10.0) + motor.setParamLoStop(-10.0) + timeData = (0.0, 5.0) + forceData = (3.0, -3.0) + eventData = (1, 2) + self.commonObjectDict[commonId] = (commonId, + type, + box, + motor, + timeData, + forceData, + eventData, + model) + elif type == 3: + vPos = Point3(float(pos[0]), float(pos[1]), float(pos[2])) + vHpr = Vec3(float(hpr[0]), float(hpr[1]), float(hpr[2])) + self.placerNode.setHpr(vHpr) + self.placerNode.setPos(vPos) + self.subPlacerNode.setPos(0, 0, 0) + if self.canRender: + myModel = loader.loadModel('phase_6/models/golf/golf_windmill_b') + else: + myModel = loader.loadModel('phase_6/models/golf/golf_windmill_b.bam') + myModel.reparentTo(self.root) + myModel.setPos(vPos) + myModel.setHpr(vHpr) + millFan = myModel.find('**/windmillFan0') + millBase = myModel.find('**/arm') + rod = myModel.find('**/rod') + rod.wrtReparentTo(millBase) + self.windmillFanNodePath = millFan + self.windmillBaseNodePath = millBase + millData = OdeTriMeshData(millBase) + millGeom = OdeTriMeshGeom(self.space, millData) + self.meshDataList.append(millData) + millGeom.setPosition(self.subPlacerNode.getPos(self.root)) + millGeom.setQuaternion(self.subPlacerNode.getQuat()) + millGeom.setCollideBits(BitMask32(251658240)) + millGeom.setCategoryBits(BitMask32(8388608)) + self.space.setCollideId(millGeom, 8) + vPos = Point3(float(pos[0]), float(pos[1]), float(pos[2]) + 5) + vHpr = Vec3(float(hpr[0]), float(hpr[1] + 90), float(hpr[2]) - 90) + self.placerNode.setHpr(vHpr) + self.placerNode.setPos(vPos) + self.subPlacerNode.setPos(-1, 0, 0.0) + model, cross = self.createPinWheel(self.world, self.space, 10.0, 1.6, 4.0, 0.6, 5, 3.7, 1.2, 1, millFan, (0, 0, 90), (-4.6, -0.5, -0.25), 20) + self.placerNode.setHpr(vHpr) + self.placerNode.setPos(vPos) + self.subPlacerNode.setPos(-1, 0, 0.0) + motor = OdeHingeJoint(self.world) + cross.setPosition(self.subPlacerNode.getPos(self.root)) + cross.setQuaternion(self.placerNode.getQuat()) + ourAxis = self.root.getRelativeVector(self.subPlacerNode, Vec3(0, 0, 1)) + motor.setParamVel(1.0) + motor.setParamFMax(50000.0) + boxsize = Vec3(1.0, 1.0, 1.0) + motor.attachBody(cross, 0) + motor.setAnchor(self.subPlacerNode.getPos(self.root)) + motor.setAxis(ourAxis) + self.cross = cross + cross.enable() + self.commonObjectDict[commonId] = (commonId, type, cross) + elif type == 4: + ourAxis = self.root.getRelativeVector(self.placerNode, Vec3(0, 1, 0)) + model, box = self.createBox(self.world, self.space, 50.0, sizeX, sizeY, 1.0, 2) + box.setPosition(vPos) + box.setQuaternion(self.placerNode.getQuat()) + motor = OdeSliderJoint(self.world) + motor.attachBody(box, 0) + motor.setAxis(ourAxis) + motor.setParamVel(moveDistance / 4.0) + motor.setParamFMax(25000.0) + motor.setParamHiStop(moveDistance) + motor.setParamLoStop(0) + timeData = (0.0, 1.0, 5.0, 6.0) + forceData = (-moveDistance / 4.0, + moveDistance / 4.0, + moveDistance / 4.0, + -moveDistance / 4.0) + eventData = (-1, 1, -2, 2) + radius = moveDistance + sizeY * 0.5 + self.commonObjectDict[commonId] = (commonId, + type, + box, + motor, + timeData, + forceData, + eventData, + model, + radius) + return [type, + commonId, + (pos[0], pos[1], pos[2]), + (hpr[0], hpr[1], hpr[2]), + sizeX, + sizeY, + moveDistance] + + def createSphere(self, world, space, density, radius, ballIndex = None): + self.notify.debug('create sphere index %s' % ballIndex) + body = OdeBody(world) + M = OdeMass() + M.setSphere(density, radius) + body.setMass(M) + body.setPosition(0, 0, -100) + geom = OdeSphereGeom(space, radius) + self.space.setSurfaceType(geom, 1) + self.notify.debug('collide ID is %s' % self.space.setCollideId(geom, 42)) + self.massList.append(M) + self.geomList.append(geom) + if ballIndex == 1: + self.notify.debug('1') + geom.setCollideBits(BitMask32(16777215)) + geom.setCategoryBits(BitMask32(4278190080L)) + elif ballIndex == 2: + self.notify.debug('2') + geom.setCollideBits(BitMask32(16777215)) + geom.setCategoryBits(BitMask32(4278190080L)) + elif ballIndex == 3: + self.notify.debug('3') + geom.setCollideBits(BitMask32(16777215)) + geom.setCategoryBits(BitMask32(4278190080L)) + elif ballIndex == 4: + self.notify.debug('4') + geom.setCollideBits(BitMask32(16777215)) + geom.setCategoryBits(BitMask32(4278190080L)) + else: + geom.setCollideBits(BitMask32(4294967295L)) + geom.setCategoryBits(BitMask32(4294967295L)) + geom.setBody(body) + if self.notify.getDebug(): + self.notify.debug('golf ball geom id') + geom.write() + self.notify.debug(' -') + self.notify.debug('Collide Bits %s' % geom.getCollideBits()) + if self.canRender: + testball = render.attachNewNode('Ball Holder') + ballmodel = loader.loadModel('phase_6/models/golf/golf_ball') + ballmodel.reparentTo(testball) + ballmodel.setColor(*GolfGlobals.PlayerColors[ballIndex - 1]) + testball.setPos(0, 0, -100) + self.odePandaRelationList.append((testball, body)) + else: + testball = None + self.bodyList.append((None, body)) + return (testball, body, geom) + + def createBox(self, world, space, density, lx, ly, lz, colOnlyBall = 0): + body = OdeBody(self.world) + M = OdeMass() + M.setSphere(density, 0.3 * (lx + ly + lz)) + body.setMass(M) + boxsize = Vec3(lx, ly, lz) + geom = OdeBoxGeom(space, boxsize) + geom.setBody(body) + self.space.setSurfaceType(geom, 0) + self.space.setCollideId(geom, 7) + self.massList.append(M) + self.geomList.append(geom) + if colOnlyBall: + geom.setCollideBits(BitMask32(251658240)) + geom.setCategoryBits(BitMask32(0)) + elif colOnlyBall == 2: + geom.setCollideBits(BitMask32(0)) + geom.setCategoryBits(BitMask32(0)) + if self.canRender: + color = random.choice([Vec4(1.0, 0.0, 0.5, 1.0), Vec4(0.5, 0.5, 1.0, 1.0), Vec4(0.5, 1.0, 0.5, 1.0)]) + boxsize = Vec3(lx, ly, lz) + boxNodePathGeom, t1, t2 = BuildGeometry.addBoxGeom(self.worldAttach, lx, ly, lz, color, 1) + boxNodePathGeom.setPos(0, 0, -100) + self.odePandaRelationList.append((boxNodePathGeom, body)) + else: + boxNodePathGeom = None + self.bodyList.append((None, body)) + return (boxNodePathGeom, body) + + def createCross(self, world, space, density, lx, ly, lz, colOnlyBall = 0, attachedGeo = None, aHPR = None, aPos = None): + body = OdeBody(self.world) + M = OdeMass() + M.setBox(density, lx, ly, lz) + body.setMass(M) + body.setFiniteRotationMode(1) + boxsize = Vec3(lx, ly, lz) + boxsize2 = Vec3(ly, lx, lz) + geom = OdeBoxGeom(space, boxsize) + geom.setBody(body) + self.space.setSurfaceType(geom, 0) + self.space.setCollideId(geom, 13) + geom2 = OdeBoxGeom(space, boxsize2) + geom2.setBody(body) + self.space.setSurfaceType(geom2, 0) + self.space.setCollideId(geom2, 26) + self.massList.append(M) + self.geomList.append(geom) + self.geomList.append(geom2) + self.odePandaRelationList.append((boxNodePathGeom, body)) + if colOnlyBall == 1: + geom.setCollideBits(BitMask32(251658240)) + geom.setCategoryBits(BitMask32(0)) + geom2.setCollideBits(BitMask32(251658240)) + geom2.setCategoryBits(BitMask32(0)) + elif colOnlyBall == 2: + geom.setCollideBits(BitMask32(0)) + geom.setCategoryBits(BitMask32(0)) + geom2.setCollideBits(BitMask32(0)) + geom2.setCategoryBits(BitMask32(0)) + if self.canRender: + boxNodePathGeom, t1, t2 = BuildGeometry.addBoxGeom(self.worldAttach, lx, ly, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom.setPos(0, 0, -100) + boxNodePathGeom2, t1, t2 = BuildGeometry.addBoxGeom(boxNodePathGeom, ly, lx, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom2.setPos(0, 0, 0) + if attachedGeo: + attachedGeo.reparentTo(boxNodePathGeom) + attachedGeo.setHpr(0, 0, 90) + attachedGeo.setPos(-4.8, 0, -2.0) + self.odePandaRelationList.append((boxNodePathGeom, body)) + else: + boxNodePathGeom = None + self.bodyList.append((None, body)) + return (boxNodePathGeom, body) + + def createCross2(self, world, space, density, lx, ly, lz, latSlide, colOnlyBall = 0, attachedGeo = None, aHPR = None, aPos = None): + body = OdeBody(self.world) + M = OdeMass() + M.setBox(density, lx, ly, lz) + body.setMass(M) + body.setFiniteRotationMode(1) + boxsize = Vec3(lx, ly * 0.5, lz) + boxsize2 = Vec3(ly * 0.5, lx, lz) + geom = OdeBoxGeom(space, boxsize) + geom.setBody(body) + geom.setOffsetPosition(-latSlide, ly * 0.25, 0) + self.space.setSurfaceType(geom, 0) + self.space.setCollideId(geom, 13) + geom2 = OdeBoxGeom(space, boxsize2) + geom2.setBody(body) + geom2.setOffsetPosition(ly * 0.25, latSlide, 0) + self.space.setSurfaceType(geom2, 0) + self.space.setCollideId(geom2, 13) + geom3 = OdeBoxGeom(space, boxsize) + geom3.setBody(body) + geom3.setOffsetPosition(latSlide, -ly * 0.25, 0) + self.space.setSurfaceType(geom3, 0) + self.space.setCollideId(geom3, 13) + geom4 = OdeBoxGeom(space, boxsize2) + geom4.setBody(body) + geom4.setOffsetPosition(-ly * 0.25, -latSlide, 0) + self.space.setSurfaceType(geom4, 0) + self.space.setCollideId(geom4, 13) + self.massList.append(M) + self.geomList.append(geom) + self.geomList.append(geom2) + self.geomList.append(geom3) + self.geomList.append(geom4) + if colOnlyBall == 1: + geom.setCollideBits(BitMask32(251658240)) + geom.setCategoryBits(BitMask32(0)) + geom2.setCollideBits(BitMask32(251658240)) + geom2.setCategoryBits(BitMask32(0)) + geom3.setCollideBits(BitMask32(251658240)) + geom3.setCategoryBits(BitMask32(0)) + geom4.setCollideBits(BitMask32(251658240)) + geom4.setCategoryBits(BitMask32(0)) + elif colOnlyBall == 2: + geom.setCollideBits(BitMask32(0)) + geom.setCategoryBits(BitMask32(0)) + geom2.setCollideBits(BitMask32(0)) + geom2.setCategoryBits(BitMask32(0)) + geom3.setCollideBits(BitMask32(0)) + geom3.setCategoryBits(BitMask32(0)) + geom4.setCollideBits(BitMask32(0)) + geom4.setCategoryBits(BitMask32(0)) + if self.canRender: + someNodePathGeom = render.attachNewNode('pinwheel') + if attachedGeo: + attachedGeo.reparentTo(someNodePathGeom) + attachedGeo.setHpr(aHPR[0], aHPR[1], aHPR[2]) + attachedGeo.setPos(aPos[0], aPos[1], aPos[2]) + boxNodePathGeom, t1, t2 = BuildGeometry.addBoxGeom(someNodePathGeom, lx, ly * 0.5, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom.setPos(-latSlide, ly * 0.25, 0) + boxNodePathGeom2, t1, t2 = BuildGeometry.addBoxGeom(someNodePathGeom, ly * 0.5, lx, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom2.setPos(ly * 0.25, latSlide, 0) + boxNodePathGeom3, t1, t2 = BuildGeometry.addBoxGeom(someNodePathGeom, lx, ly * 0.5, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom3.setPos(latSlide, -ly * 0.25, 0) + boxNodePathGeom4, t1, t2 = BuildGeometry.addBoxGeom(someNodePathGeom, ly * 0.5, lx, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom4.setPos(-ly * 0.25, -latSlide, 0) + self.odePandaRelationList.append((someNodePathGeom, body)) + else: + someNodePathGeom = None + self.bodyList.append((None, body)) + return (someNodePathGeom, body) + + def createPinWheel(self, world, space, density, lx, ly, lz, numBoxes, disV, disH, colOnlyBall = 0, attachedGeo = None, aHPR = None, aPos = None, offRot = 0): + body = OdeBody(self.world) + M = OdeMass() + M.setBox(density, lx, ly, lz) + body.setMass(M) + body.setFiniteRotationMode(1) + boxsize = Vec3(lx, ly * 0.5, lz) + boxsize2 = Vec3(ly * 0.5, lx, lz) + self.massList.append(M) + self.placerNode.setPos(0, 0, 0) + self.placerNode.setHpr(0, 0, 0) + self.subPlacerNode.setHpr(0, 0, 0) + self.subPlacerNode.setPos(disH, disV, 0) + if self.canRender: + someNodePathGeom = render.attachNewNode('pinwheel') + else: + someNodePathGeom = self.root.attachNewNode('pinwheel') + for num in xrange(numBoxes): + spin = 360.0 * float(num) / float(numBoxes) + float(offRot) + self.placerNode.setH(spin) + geom = OdeBoxGeom(space, boxsize) + geom.setBody(body) + geom.setOffsetPosition(self.subPlacerNode.getPos(self.root)) + geom.setOffsetQuaternion(self.subPlacerNode.getQuat(self.root)) + self.geomList.append(geom) + self.space.setSurfaceType(geom, 0) + self.space.setCollideId(geom, 13) + if colOnlyBall == 1: + geom.setCollideBits(BitMask32(251658240)) + geom.setCategoryBits(BitMask32(0)) + elif colOnlyBall == 2: + geom.setCollideBits(BitMask32(0)) + geom.setCategoryBits(BitMask32(0)) + if not attachedGeo: + boxNodePathGeom, t1, t2 = BuildGeometry.addBoxGeom(someNodePathGeom, lx, ly * 0.5, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom.setPos(self.subPlacerNode.getPos(self.root)) + boxNodePathGeom.setHpr(self.subPlacerNode.getHpr(self.root)) + + if attachedGeo and self.canRender: + attachedGeo.reparentTo(someNodePathGeom) + attachedGeo.setHpr(aHPR[0], aHPR[1], aHPR[2]) + attachedGeo.setPos(aPos[0], aPos[1], aPos[2]) + if self.canRender: + self.odePandaRelationList.append((someNodePathGeom, body)) + else: + someNodePathGeom = None + self.bodyList.append((None, body)) + return (someNodePathGeom, body) diff --git a/toontown/golf/__init__.py b/toontown/golf/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/hood/AnimatedProp.py b/toontown/hood/AnimatedProp.py new file mode 100755 index 00000000..34ee330e --- /dev/null +++ b/toontown/hood/AnimatedProp.py @@ -0,0 +1,20 @@ +from direct.showbase.DirectObject import DirectObject + + +class AnimatedProp(DirectObject): + notify = directNotify.newCategory('AnimatedProp') + + def __init__(self, node): + self.node = node + + def delete(self): + pass + + def uniqueName(self, name): + return name + '-' + str(self.node.this) + + def enter(self): + self.notify.debug('enter') + + def exit(self): + self.notify.debug('exit') diff --git a/toontown/hood/BRHood.py b/toontown/hood/BRHood.py new file mode 100755 index 00000000..f8792bdb --- /dev/null +++ b/toontown/hood/BRHood.py @@ -0,0 +1,19 @@ +from toontown.safezone.BRSafeZoneLoader import BRSafeZoneLoader +from toontown.town.BRTownLoader import BRTownLoader +from toontown.toonbase import ToontownGlobals +from toontown.hood.ToonHood import ToonHood + +class BRHood(ToonHood): + notify = directNotify.newCategory('BRHood') + + ID = ToontownGlobals.TheBrrrgh + TOWNLOADER_CLASS = BRTownLoader + SAFEZONELOADER_CLASS = BRSafeZoneLoader + STORAGE_DNA = 'phase_8/dna/storage_BR.pdna' + SKY_FILE = 'phase_3.5/models/props/BR_sky' + SPOOKY_SKY_FILE = 'phase_3.5/models/props/BR_sky' + TITLE_COLOR = (0.3, 0.6, 1.0, 1.0) + + HOLIDAY_DNA = { + ToontownGlobals.CHRISTMAS: ['phase_8/dna/winter_storage_BR.pdna'], + ToontownGlobals.HALLOWEEN: ['phase_8/dna/halloween_props_storage_BR.pdna']} diff --git a/toontown/hood/BRHoodAI.py b/toontown/hood/BRHoodAI.py new file mode 100755 index 00000000..34267506 --- /dev/null +++ b/toontown/hood/BRHoodAI.py @@ -0,0 +1,35 @@ +from toontown.hood import HoodAI +from toontown.safezone import DistributedTrolleyAI +from toontown.toonbase import ToontownGlobals +from toontown.ai import DistributedPolarPlaceEffectMgrAI +from toontown.ai import DistributedEffectMgrAI + +class BRHoodAI(HoodAI.HoodAI): + def __init__(self, air): + HoodAI.HoodAI.__init__(self, air, + ToontownGlobals.TheBrrrgh, + ToontownGlobals.TheBrrrgh) + + self.trolley = None + + self.startup() + + def startup(self): + HoodAI.HoodAI.startup(self) + + if simbase.config.GetBool('want-minigames', True): + self.createTrolley() + + self.PolarPlaceEffectManager = DistributedPolarPlaceEffectMgrAI.DistributedPolarPlaceEffectMgrAI(self.air) + self.PolarPlaceEffectManager.generateWithRequired(3821) + + self.trickOrTreatMgr = DistributedEffectMgrAI.DistributedEffectMgrAI(self.air, ToontownGlobals.HALLOWEEN, 12) + self.trickOrTreatMgr.generateWithRequired(3707) # Snowplace Like Home, Sleet Street + + self.winterCarolingMgr = DistributedEffectMgrAI.DistributedEffectMgrAI(self.air, ToontownGlobals.CHRISTMAS, 14) + self.winterCarolingMgr.generateWithRequired(3828) # Snowman's Land, Polar Place + + def createTrolley(self): + self.trolley = DistributedTrolleyAI.DistributedTrolleyAI(self.air) + self.trolley.generateWithRequired(self.zoneId) + self.trolley.start() diff --git a/toontown/hood/BossbotHQ.py b/toontown/hood/BossbotHQ.py new file mode 100755 index 00000000..21cff57d --- /dev/null +++ b/toontown/hood/BossbotHQ.py @@ -0,0 +1,21 @@ +from toontown.coghq.BossbotCogHQLoader import BossbotCogHQLoader +from toontown.toonbase import ToontownGlobals +from toontown.hood.CogHood import CogHood + + +class BossbotHQ(CogHood): + notify = directNotify.newCategory('BossbotHQ') + + ID = ToontownGlobals.BossbotHQ + LOADER_CLASS = BossbotCogHQLoader + + def load(self): + CogHood.load(self) + + self.sky.hide() + + def enter(self, requestStatus): + CogHood.enter(self, requestStatus) + + base.localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + base.camLens.setNearFar(ToontownGlobals.BossbotHQCameraNear, ToontownGlobals.BossbotHQCameraFar) diff --git a/toontown/hood/BossbotHQAI.py b/toontown/hood/BossbotHQAI.py new file mode 100755 index 00000000..d734ec9d --- /dev/null +++ b/toontown/hood/BossbotHQAI.py @@ -0,0 +1,63 @@ +from toontown.building import DistributedBBElevatorAI +from toontown.building import FADoorCodes +from toontown.building.DistributedBoardingPartyAI import DistributedBoardingPartyAI +from toontown.coghq import DistributedCogKartAI +from toontown.hood import CogHQAI +from toontown.suit import DistributedBossbotBossAI +from toontown.suit import DistributedSuitPlannerAI +from toontown.toonbase import ToontownGlobals + + +class BossbotHQAI(CogHQAI.CogHQAI): + def __init__(self, air): + CogHQAI.CogHQAI.__init__( + self, air, ToontownGlobals.BossbotHQ, ToontownGlobals.BossbotLobby, + FADoorCodes.BB_DISGUISE_INCOMPLETE, + DistributedBBElevatorAI.DistributedBBElevatorAI, + DistributedBossbotBossAI.DistributedBossbotBossAI) + + self.cogKarts = [] + self.courseBoardingParty = None + self.suitPlanners = [] + + self.startup() + + def startup(self): + CogHQAI.CogHQAI.startup(self) + + self.createCogKarts() + if simbase.config.GetBool('want-boarding-groups', True): + self.createCourseBoardingParty() + if simbase.config.GetBool('want-suit-planners', True): + self.createSuitPlanners() + + def createCogKarts(self): + posList = ( + (154.762, 37.169, 0), (141.403, -81.887, 0), + (-48.44, 15.308, 0) + ) + hprList = ((110.815, 0, 0), (61.231, 0, 0), (-105.481, 0, 0)) + for cogCourse in xrange(len(posList)): + pos = posList[cogCourse] + hpr = hprList[cogCourse] + cogKart = DistributedCogKartAI.DistributedCogKartAI( + self.air, cogCourse, + pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2], + self.air.countryClubMgr) + cogKart.generateWithRequired(self.zoneId) + self.cogKarts.append(cogKart) + + def createCourseBoardingParty(self): + cogKartIdList = [] + for cogKart in self.cogKarts: + cogKartIdList.append(cogKart.doId) + self.courseBoardingParty = DistributedBoardingPartyAI(self.air, cogKartIdList, 4) + self.courseBoardingParty.generateWithRequired(self.zoneId) + + def createSuitPlanners(self): + suitPlanner = DistributedSuitPlannerAI.DistributedSuitPlannerAI(self.air, self.zoneId) + suitPlanner.generateWithRequired(self.zoneId) + suitPlanner.d_setZoneId(self.zoneId) + suitPlanner.initTasks() + self.suitPlanners.append(suitPlanner) + self.air.suitPlanners[self.zoneId] = suitPlanner diff --git a/toontown/hood/CashbotHQ.py b/toontown/hood/CashbotHQ.py new file mode 100755 index 00000000..e51476f6 --- /dev/null +++ b/toontown/hood/CashbotHQ.py @@ -0,0 +1,26 @@ +from toontown.coghq.CashbotCogHQLoader import CashbotCogHQLoader +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.hood.CogHood import CogHood +from toontown.hood import ZoneUtil + + +class CashbotHQ(CogHood): + notify = directNotify.newCategory('CashbotHQ') + + ID = ToontownGlobals.CashbotHQ + LOADER_CLASS = CashbotCogHQLoader + SKY_FILE = 'phase_3.5/models/props/TT_sky' + + def enter(self, requestStatus): + CogHood.enter(self, requestStatus) + + base.localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + base.camLens.setNearFar(ToontownGlobals.CashbotHQCameraNear, ToontownGlobals.CashbotHQCameraFar) + + def spawnTitleText(self, zoneId, floorNum=None): + if ZoneUtil.isMintInteriorZone(zoneId): + text = '%s\n%s' % (ToontownGlobals.StreetNames[zoneId][-1], TTLocalizer.MintFloorTitle % (floorNum + 1)) + self.doSpawnTitleText(text) + return + + CogHood.spawnTitleText(self, zoneId) diff --git a/toontown/hood/CashbotHQAI.py b/toontown/hood/CashbotHQAI.py new file mode 100755 index 00000000..24343eff --- /dev/null +++ b/toontown/hood/CashbotHQAI.py @@ -0,0 +1,59 @@ +from toontown.building import DistributedCFOElevatorAI +from toontown.building import FADoorCodes +from toontown.building.DistributedBoardingPartyAI import DistributedBoardingPartyAI +from toontown.coghq.DistributedMintElevatorExtAI import DistributedMintElevatorExtAI +from toontown.hood import CogHQAI +from toontown.suit import DistributedCashbotBossAI +from toontown.suit import DistributedSuitPlannerAI +from toontown.toonbase import ToontownGlobals + + +class CashbotHQAI(CogHQAI.CogHQAI): + def __init__(self, air): + CogHQAI.CogHQAI.__init__( + self, air, ToontownGlobals.CashbotHQ, ToontownGlobals.CashbotLobby, + FADoorCodes.CB_DISGUISE_INCOMPLETE, + DistributedCFOElevatorAI.DistributedCFOElevatorAI, + DistributedCashbotBossAI.DistributedCashbotBossAI) + + self.mintElevators = [] + self.mintBoardingParty = None + self.suitPlanners = [] + + self.startup() + + def startup(self): + CogHQAI.CogHQAI.startup(self) + + self.createMintElevators() + if simbase.config.GetBool('want-boarding-groups', True): + self.createMintBoardingParty() + if simbase.config.GetBool('want-suit-planners', True): + self.createSuitPlanners() + + def createMintElevators(self): + destZones = ( + ToontownGlobals.CashbotMintIntA, + ToontownGlobals.CashbotMintIntB, + ToontownGlobals.CashbotMintIntC + ) + for i in xrange(len(destZones)): + mintElevator = DistributedMintElevatorExtAI( + self.air, self.air.mintMgr, destZones[i]) + mintElevator.generateWithRequired(self.zoneId) + self.mintElevators.append(mintElevator) + + def createMintBoardingParty(self): + mintIdList = [] + for mintElevator in self.mintElevators: + mintIdList.append(mintElevator.doId) + self.mintBoardingParty = DistributedBoardingPartyAI(self.air, mintIdList, 4) + self.mintBoardingParty.generateWithRequired(self.zoneId) + + def createSuitPlanners(self): + suitPlanner = DistributedSuitPlannerAI.DistributedSuitPlannerAI(self.air, self.zoneId) + suitPlanner.generateWithRequired(self.zoneId) + suitPlanner.d_setZoneId(self.zoneId) + suitPlanner.initTasks() + self.suitPlanners.append(suitPlanner) + self.air.suitPlanners[self.zoneId] = suitPlanner diff --git a/toontown/hood/CogHQAI.py b/toontown/hood/CogHQAI.py new file mode 100755 index 00000000..e75efcfa --- /dev/null +++ b/toontown/hood/CogHQAI.py @@ -0,0 +1,81 @@ +from toontown.building import DoorTypes +from toontown.building.DistributedBoardingPartyAI import DistributedBoardingPartyAI +from toontown.coghq import DistributedCogHQDoorAI, DistributedCogHQExteriorDoorAI, LobbyManagerAI +from toontown.toonbase import TTLocalizer, ToontownGlobals +from toontown.toon import NPCToons + +class CogHQAI: + notify = directNotify.newCategory('CogHQAI') + notify.setInfo(True) + + def __init__( + self, air, zoneId, lobbyZoneId, lobbyFADoorCode, + lobbyElevatorCtor, bossCtor): + self.air = air + self.zoneId = zoneId + self.lobbyZoneId = lobbyZoneId + self.lobbyFADoorCode = lobbyFADoorCode + self.lobbyElevatorCtor = lobbyElevatorCtor + self.bossCtor = bossCtor + + self.lobbyMgr = None + self.lobbyElevator = None + self.boardingParty = None + + self.notify.info('Creating objects... ' + self.getLocationName(zoneId)) + + def getLocationName(self, zoneId): + lookupTable = ToontownGlobals.hoodNameMap + if (zoneId % 1000) != 0: + lookupTable = TTLocalizer.GlobalStreetNames + name = lookupTable.get(zoneId, '') + if isinstance(name, str): + return name + return name[2] + + def startup(self): + self.createLobbyManager() + self.createLobbyElevator() + self.extDoor = self.makeCogHQDoor(self.lobbyZoneId, 0, 0, self.lobbyFADoorCode) + if simbase.config.GetBool('want-boarding-groups', True): + self.createBoardingParty() + self.npcs = NPCToons.createNpcsInZone(self.air, self.zoneId) + + def shutdown(self): + for npc in self.npcs: + npc.requestDelete() + del self.npcs + + def createLobbyManager(self): + self.lobbyMgr = LobbyManagerAI.LobbyManagerAI(self.air, self.bossCtor) + self.lobbyMgr.generateWithRequired(self.lobbyZoneId) + + def createLobbyElevator(self): + self.lobbyElevator = self.lobbyElevatorCtor( + self.air, self.lobbyMgr, self.lobbyZoneId) + self.lobbyElevator.generateWithRequired(self.lobbyZoneId) + + def makeCogHQDoor(self, destinationZone, intDoorIndex, extDoorIndex, lock=0): + intDoor = DistributedCogHQDoorAI.DistributedCogHQDoorAI( + self.air, 0, DoorTypes.INT_COGHQ, self.zoneId, + doorIndex=intDoorIndex, lockValue=lock) + intDoor.zoneId = destinationZone + + extDoor = DistributedCogHQDoorAI.DistributedCogHQDoorAI( + self.air, 0, DoorTypes.EXT_COGHQ, destinationZone, + doorIndex=extDoorIndex, lockValue=lock) + + extDoor.setOtherDoor(intDoor) + intDoor.setOtherDoor(extDoor) + + intDoor.generateWithRequired(destinationZone) + intDoor.sendUpdate('setDoorIndex', [intDoor.getDoorIndex()]) + + extDoor.generateWithRequired(self.zoneId) + extDoor.sendUpdate('setDoorIndex', [extDoor.getDoorIndex()]) + + return extDoor + + def createBoardingParty(self): + self.boardingParty = DistributedBoardingPartyAI(self.air, [self.lobbyElevator.doId], 8) + self.boardingParty.generateWithRequired(self.lobbyZoneId) diff --git a/toontown/hood/CogHood.py b/toontown/hood/CogHood.py new file mode 100755 index 00000000..0cd1bbdc --- /dev/null +++ b/toontown/hood/CogHood.py @@ -0,0 +1,98 @@ +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals +from toontown.hood.Hood import Hood + + +class CogHood(Hood): + notify = directNotify.newCategory('CogHood') + + ID = None + LOADER_CLASS = None + SKY_FILE = 'phase_9/models/cogHQ/cog_sky' + TITLE_COLOR = (0.5, 0.5, 0.5, 1.0) + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + Hood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + + self.fsm = ClassicFSM.ClassicFSM( + 'Hood', + [State.State('start', + self.enterStart, + self.exitStart, + ['cogHQLoader']), + State.State('cogHQLoader', + self.enterCogHQLoader, + self.exitCogHQLoader, + ['quietZone']), + State.State('quietZone', + self.enterQuietZone, + self.exitQuietZone, + ['cogHQLoader']), + State.State('final', + self.enterFinal, + self.exitFinal, + []) + ], + 'start', + 'final') + self.fsm.enterInitialState() + + # Until Hood is cleaned up, we will need to define some variables: + + self.id = self.ID + self.storageDNAFile = None + self.skyFile = self.SKY_FILE + self.titleColor = self.TITLE_COLOR + + def load(self): + Hood.load(self) + + skyInner = self.sky.find('**/InnerGroup') + skyMiddle = self.sky.find('**/MiddleGroup') + skyOuter = self.sky.find('**/OutterSky') + + if not skyOuter.isEmpty(): + skyOuter.setBin('background', 0) + if not skyMiddle.isEmpty(): + skyMiddle.setDepthWrite(0) + skyMiddle.setBin('background', 10) + if not skyInner.isEmpty(): + skyInner.setDepthWrite(0) + skyInner.setBin('background', 20) + + self.parentFSM.getStateNamed(self.__class__.__name__).addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed(self.__class__.__name__).removeChild(self.fsm) + + Hood.unload(self) + + def loadLoader(self, requestStatus): + loaderName = requestStatus['loader'] + if loaderName == 'cogHQLoader': + self.loader = self.LOADER_CLASS(self, self.fsm.getStateNamed('cogHQLoader'), self.loaderDoneEvent) + self.loader.load(requestStatus['zoneId']) + + def enterCogHQLoader(self, requestStatus): + self.accept(self.loaderDoneEvent, self.handleCogHQLoaderDone) + self.loader.enter(requestStatus) + + def exitCogHQLoader(self): + self.ignore(self.loaderDoneEvent) + self.loader.exit() + self.loader.unload() + del self.loader + + def handleCogHQLoaderDone(self): + doneStatus = self.loader.getDoneStatus() + if self.isSameHood(doneStatus): + self.fsm.request('quietZone', [doneStatus]) + else: + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + + def exit(self): + base.localAvatar.setCameraFov(settings['fov']) + base.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar) + + Hood.exit(self) diff --git a/toontown/hood/DDHood.py b/toontown/hood/DDHood.py new file mode 100755 index 00000000..70fa3561 --- /dev/null +++ b/toontown/hood/DDHood.py @@ -0,0 +1,25 @@ +from pandac.PandaModules import Vec4 +from toontown.safezone.DDSafeZoneLoader import DDSafeZoneLoader +from toontown.town.DDTownLoader import DDTownLoader +from toontown.toonbase import ToontownGlobals +from toontown.hood.ToonHood import ToonHood + +class DDHood(ToonHood): + notify = directNotify.newCategory('DDHood') + + ID = ToontownGlobals.DonaldsDock + TOWNLOADER_CLASS = DDTownLoader + SAFEZONELOADER_CLASS = DDSafeZoneLoader + STORAGE_DNA = 'phase_6/dna/storage_DD.pdna' + SKY_FILE = 'phase_3.5/models/props/BR_sky' + SPOOKY_SKY_FILE = 'phase_3.5/models/props/BR_sky' + TITLE_COLOR = (0.8, 0.6, 0.5, 1.0) + + HOLIDAY_DNA = { + ToontownGlobals.CHRISTMAS: ['phase_6/dna/winter_storage_DD.pdna'], + ToontownGlobals.HALLOWEEN: ['phase_6/dna/halloween_props_storage_DD.pdna']} + + def load(self): + ToonHood.load(self) + + self.fog = Fog('DDFog') diff --git a/toontown/hood/DDHoodAI.py b/toontown/hood/DDHoodAI.py new file mode 100755 index 00000000..e7843779 --- /dev/null +++ b/toontown/hood/DDHoodAI.py @@ -0,0 +1,39 @@ +from toontown.hood import HoodAI +from toontown.safezone import DistributedBoatAI +from toontown.safezone import DistributedTrolleyAI +from toontown.toonbase import ToontownGlobals +from toontown.ai import DistributedEffectMgrAI + +class DDHoodAI(HoodAI.HoodAI): + def __init__(self, air): + HoodAI.HoodAI.__init__(self, air, + ToontownGlobals.DonaldsDock, + ToontownGlobals.DonaldsDock) + + self.trolley = None + self.boat = None + + self.startup() + + def startup(self): + HoodAI.HoodAI.startup(self) + + if simbase.config.GetBool('want-minigames', True): + self.createTrolley() + self.createBoat() + + self.trickOrTreatMgr = DistributedEffectMgrAI.DistributedEffectMgrAI(self.air, ToontownGlobals.HALLOWEEN, 12) + self.trickOrTreatMgr.generateWithRequired(1834) # Rudderly Ridiculous, Lighthouse Lane + + self.winterCarolingMgr = DistributedEffectMgrAI.DistributedEffectMgrAI(self.air, ToontownGlobals.CHRISTMAS, 14) + self.winterCarolingMgr.generateWithRequired(1707) # Gifts with a Porpoise, Seaweed Street + + def createTrolley(self): + self.trolley = DistributedTrolleyAI.DistributedTrolleyAI(self.air) + self.trolley.generateWithRequired(self.zoneId) + self.trolley.start() + + def createBoat(self): + self.boat = DistributedBoatAI.DistributedBoatAI(self.air) + self.boat.generateWithRequired(self.zoneId) + self.boat.start() diff --git a/toontown/hood/DGHood.py b/toontown/hood/DGHood.py new file mode 100755 index 00000000..ce2fc224 --- /dev/null +++ b/toontown/hood/DGHood.py @@ -0,0 +1,19 @@ +from toontown.safezone.DGSafeZoneLoader import DGSafeZoneLoader +from toontown.town.DGTownLoader import DGTownLoader +from toontown.toonbase import ToontownGlobals +from toontown.hood.ToonHood import ToonHood + +class DGHood(ToonHood): + notify = directNotify.newCategory('DGHood') + + ID = ToontownGlobals.DaisyGardens + TOWNLOADER_CLASS = DGTownLoader + SAFEZONELOADER_CLASS = DGSafeZoneLoader + STORAGE_DNA = 'phase_8/dna/storage_DG.pdna' + SKY_FILE = 'phase_3.5/models/props/TT_sky' + SPOOKY_SKY_FILE = 'phase_3.5/models/props/BR_sky' + TITLE_COLOR = (0.8, 0.6, 1.0, 1.0) + + HOLIDAY_DNA = { + ToontownGlobals.CHRISTMAS: ['phase_8/dna/winter_storage_DG.pdna'], + ToontownGlobals.HALLOWEEN: ['phase_8/dna/halloween_props_storage_DG.pdna']} diff --git a/toontown/hood/DGHoodAI.py b/toontown/hood/DGHoodAI.py new file mode 100755 index 00000000..53f4271d --- /dev/null +++ b/toontown/hood/DGHoodAI.py @@ -0,0 +1,62 @@ +from toontown.hood import HoodAI +from toontown.safezone import ButterflyGlobals +from toontown.safezone import DistributedButterflyAI +from toontown.safezone import DistributedDGFlowerAI +from toontown.safezone import DistributedTrolleyAI +from toontown.toonbase import ToontownGlobals +from toontown.ai import DistributedEffectMgrAI + +class DGHoodAI(HoodAI.HoodAI): + + def __init__(self, air): + HoodAI.HoodAI.__init__(self, air, + ToontownGlobals.DaisyGardens, + ToontownGlobals.DaisyGardens) + + self.trolley = None + self.flower = None + + self.startup() + + def startup(self): + HoodAI.HoodAI.startup(self) + + if simbase.config.GetBool('want-minigames', True): + self.createTrolley() + self.createFlower() + if simbase.config.GetBool('want-butterflies', True): + self.createButterflies() + + self.greenToonMgr = DistributedEffectMgrAI.DistributedEffectMgrAI(self.air, ToontownGlobals.IDES_OF_MARCH, 15) + self.greenToonMgr.generateWithRequired(5819) + + self.trickOrTreatMgr = DistributedEffectMgrAI.DistributedEffectMgrAI(self.air, ToontownGlobals.HALLOWEEN, 12) + self.trickOrTreatMgr.generateWithRequired(5620) # Rake It Inn, Elm Street + + self.winterCarolingMgr = DistributedEffectMgrAI.DistributedEffectMgrAI(self.air, ToontownGlobals.CHRISTMAS, 14) + self.winterCarolingMgr.generateWithRequired(5626) # Pine Needle Crafts, Elm Street + + def shutdown(self): + HoodAI.HoodAI.shutdown(self) + + ButterflyGlobals.clearIndexes(self.zoneId) + + def createTrolley(self): + self.trolley = DistributedTrolleyAI.DistributedTrolleyAI(self.air) + self.trolley.generateWithRequired(self.zoneId) + self.trolley.start() + + def createFlower(self): + self.flower = DistributedDGFlowerAI.DistributedDGFlowerAI(self.air) + self.flower.generateWithRequired(self.zoneId) + self.flower.start() + + def createButterflies(self): + playground = ButterflyGlobals.DG + ButterflyGlobals.generateIndexes(self.zoneId, ButterflyGlobals.DG) + + for i in xrange(0, ButterflyGlobals.NUM_BUTTERFLY_AREAS[ButterflyGlobals.DG]): + for _ in xrange(0, ButterflyGlobals.NUM_BUTTERFLIES[ButterflyGlobals.DG]): + butterfly = DistributedButterflyAI.DistributedButterflyAI(self.air, playground, i, self.zoneId) + butterfly.generateWithRequired(self.zoneId) + butterfly.start() diff --git a/toontown/hood/DLHood.py b/toontown/hood/DLHood.py new file mode 100755 index 00000000..87dc4867 --- /dev/null +++ b/toontown/hood/DLHood.py @@ -0,0 +1,18 @@ +from toontown.safezone.DLSafeZoneLoader import DLSafeZoneLoader +from toontown.town.DLTownLoader import DLTownLoader +from toontown.toonbase import ToontownGlobals +from toontown.hood.ToonHood import ToonHood + +class DLHood(ToonHood): + notify = directNotify.newCategory('DLHood') + + ID = ToontownGlobals.DonaldsDreamland + TOWNLOADER_CLASS = DLTownLoader + SAFEZONELOADER_CLASS = DLSafeZoneLoader + STORAGE_DNA = 'phase_8/dna/storage_DL.pdna' + SKY_FILE = 'phase_8/models/props/DL_sky' + TITLE_COLOR = (1.0, 0.9, 0.5, 1.0) + + HOLIDAY_DNA = { + ToontownGlobals.CHRISTMAS: ['phase_8/dna/winter_storage_DL.pdna'], + ToontownGlobals.HALLOWEEN: ['phase_8/dna/halloween_props_storage_DL.pdna']} diff --git a/toontown/hood/DLHoodAI.py b/toontown/hood/DLHoodAI.py new file mode 100755 index 00000000..2ee55fa2 --- /dev/null +++ b/toontown/hood/DLHoodAI.py @@ -0,0 +1,35 @@ +from toontown.hood import HoodAI +from toontown.safezone import DistributedTrolleyAI +from toontown.toonbase import ToontownGlobals +from toontown.ai import DistributedResistanceEmoteMgrAI +from toontown.ai import DistributedEffectMgrAI + +class DLHoodAI(HoodAI.HoodAI): + def __init__(self, air): + HoodAI.HoodAI.__init__(self, air, + ToontownGlobals.DonaldsDreamland, + ToontownGlobals.DonaldsDreamland) + + self.trolley = None + + self.startup() + + def startup(self): + HoodAI.HoodAI.startup(self) + + if simbase.config.GetBool('want-minigames', True): + self.createTrolley() + + self.resistanceEmoteManager = DistributedResistanceEmoteMgrAI.DistributedResistanceEmoteMgrAI(self.air) + self.resistanceEmoteManager.generateWithRequired(9720) + + self.trickOrTreatMgr = DistributedEffectMgrAI.DistributedEffectMgrAI(self.air, ToontownGlobals.HALLOWEEN, 12) + self.trickOrTreatMgr.generateWithRequired(9619) # Relax to the Max, Lullaby Lane + + self.winterCarolingMgr = DistributedEffectMgrAI.DistributedEffectMgrAI(self.air, ToontownGlobals.CHRISTMAS, 14) + self.winterCarolingMgr.generateWithRequired(9722) # Dream On Talent Agency, Pajama Place + + def createTrolley(self): + self.trolley = DistributedTrolleyAI.DistributedTrolleyAI(self.air) + self.trolley.generateWithRequired(self.zoneId) + self.trolley.start() \ No newline at end of file diff --git a/toontown/hood/EstateHood.py b/toontown/hood/EstateHood.py new file mode 100755 index 00000000..3fdde689 --- /dev/null +++ b/toontown/hood/EstateHood.py @@ -0,0 +1,147 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.distributed.ToontownMsgTypes import * +from direct.fsm import ClassicFSM, State +from toontown.minigame import Purchase +from otp.avatar import DistributedAvatar +from direct.task.Task import Task +from toontown.hood.Hood import Hood +from toontown.estate.EstateLoader import EstateLoader +from toontown.estate import HouseGlobals +from toontown.hood import ZoneUtil +from toontown.safezone import SZUtil + +class EstateHood(Hood): + notify = directNotify.newCategory('EstateHood') + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + Hood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + + self.fsm = ClassicFSM.ClassicFSM('Hood', [State.State('start', self.enterStart, self.exitStart, ['safeZoneLoader']), + State.State('safeZoneLoader', self.enterSafeZoneLoader, self.exitSafeZoneLoader, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['safeZoneLoader']), + State.State('final', self.enterFinal, self.exitFinal, [])], 'start', 'final') + self.fsm.enterInitialState() + + self.id = MyEstate + self.safeZoneLoaderClass = EstateLoader + self.storageDNAFile = 'phase_5.5/dna/storage_estate.pdna' + + self.holidayStorageDNADict = { + CHRISTMAS: ['phase_5.5/dna/winter_storage_estate.pdna'], + HALLOWEEN: ['phase_5.5/dna/halloween_props_storage_estate.pdna']} + + self.skyFile = 'phase_3.5/models/props/TT_sky' + self.spookySkyFile = 'phase_3.5/models/props/BR_sky' + self.popupInfo = None + + def unload(self): + del self.safeZoneLoaderClass + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + + Hood.unload(self) + + def enter(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + self.accept('kickToPlayground', self.kickToPlayground) + self.fsm.request(requestStatus['loader'], [requestStatus]) + + def exit(self): + if self.loader: + self.loader.exit() + self.loader.unload() + del self.loader + + Hood.exit(self) + + def loadLoader(self, requestStatus): + loaderName = requestStatus['loader'] + if loaderName == 'safeZoneLoader': + self.loader = self.safeZoneLoaderClass(self, self.fsm.getStateNamed('safeZoneLoader'), self.loaderDoneEvent) + self.loader.load() + + def spawnTitleText(self, zoneId): + pass + + def hideTitleTextTask(self, task): + return Task.done + + def kickToPlayground(self, retCode): + if retCode == 0: + msg = TTLocalizer.EstateOwnerLeftMessage % HouseGlobals.BOOT_GRACE_PERIOD + self.__popupKickoutMessage(msg) + elif retCode == 1: + zoneId = base.localAvatar.lastHood + self.doneStatus = {'loader': ZoneUtil.getBranchLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1} + messenger.send(self.doneEvent) + elif retCode == 2: + zoneId = base.localAvatar.lastHood + self.doneStatus = {'loader': ZoneUtil.getBranchLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1} + messenger.send(self.doneEvent) + else: + self.notify.error('unknown reason for exiting estate') + + def __popupKickoutMessage(self, msg): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = None + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=msg, frameSize=(-1, 1, -1, 1), text_wordwrap=10, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(0.88, 1, 0.75), geom_pos=(0, 0, -.08), text_scale=TTLocalizer.EHpopupInfo, text_pos=(0, 0.1)) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.EstatePopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.3), command=self.__handleKickoutOk) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + + def __handleKickoutOk(self): + self.popupInfo.reparentTo(hidden) + + def skyTrack(self, task): + return SZUtil.cloudSkyTrack(task) + + def startSky(self): + if not self.sky.getTag('sky') == 'Regular': + self.endSpookySky() + SZUtil.startCloudSky(self) + if base.cloudPlatformsEnabled: + self.loader.startCloudPlatforms() + + def stopSky(self): + Hood.stopSky(self) + + self.loader.stopCloudPlatforms() + + def startSpookySky(self): + if hasattr(self, 'loader') and self.loader and hasattr(self.loader, 'cloudTrack') and self.loader.cloudTrack: + self.stopSky() + self.sky = loader.loadModel(self.spookySkyFile) + self.sky.setTag('sky', 'Halloween') + self.sky.setScale(1.0) + self.sky.setDepthTest(0) + self.sky.setDepthWrite(0) + self.sky.setColor(0.5, 0.5, 0.5, 1) + self.sky.setBin('background', 100) + self.sky.setFogOff() + self.sky.reparentTo(camera) + self.sky.setTransparency(TransparencyAttrib.MDual, 1) + fadeIn = self.sky.colorScaleInterval(1.5, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0.25), blendType='easeInOut') + fadeIn.start() + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) diff --git a/toontown/hood/FishAnimatedProp.py b/toontown/hood/FishAnimatedProp.py new file mode 100755 index 00000000..5ee0799d --- /dev/null +++ b/toontown/hood/FishAnimatedProp.py @@ -0,0 +1,40 @@ +import AnimatedProp +from direct.actor import Actor +from direct.interval.IntervalGlobal import * +from toontown.effects.Splash import * +from toontown.effects.Ripples import * +import random + +class FishAnimatedProp: + + def __init__(self, node): + self.fish = Actor.Actor('phase_4/models/props/SZ_fish-mod', {'jump': 'phase_4/models/props/SZ_fish-jump', 'swim': 'phase_4/models/props/SZ_fish-swim'}, copy=0) + self.fish.hide() + self.fish.reparentTo(node) + self.splashSfxList = (loader.loadSfx('phase_4/audio/sfx/TT_splash1.ogg'), loader.loadSfx('phase_4/audio/sfx/TT_splash2.ogg')) + self.geom = self.fish.getGeomNode() + self.exitRipples = Ripples(self.geom) + self.exitRipples.setBin('fixed', 25, 1) + self.exitRipples.setPosHprScale(-0.3, 0.0, 1.24, 0.0, 0.0, 0.0, 0.7, 0.7, 0.7) + self.splash = Splash(self.geom, wantParticles=0) + self.splash.setPosHprScale(-1, 0.0, 1.23, 0.0, 0.0, 0.0, 0.7, 0.7, 0.7) + randomSplash = random.choice(self.splashSfxList) + self.track = Sequence(Wait(5 + 10 * random.random()), Parallel(Func(self.fish.show), self.fish.actorInterval('jump'), Sequence(Wait(0.25), Func(self.exitRipples.play, 0.75)), Sequence(Wait(1.13), Func(self.splash.play), SoundInterval(randomSplash, volume = 0.3, node = self.fish), Func(self.fish.hide)))) + + def delete(self): + self.exitRipples.destroy() + del self.exitRipples + self.splash.destroy() + del self.splash + del self.track + self.fish.removeNode() + del self.fish + del self.geom + + def enter(self): + self.track.loop() + + def exit(self): + self.track.finish() + self.splash.stop() + self.exitRipples.stop() diff --git a/toontown/hood/GSHood.py b/toontown/hood/GSHood.py new file mode 100755 index 00000000..83cfd0fe --- /dev/null +++ b/toontown/hood/GSHood.py @@ -0,0 +1,25 @@ +from toontown.safezone.GSSafeZoneLoader import GSSafeZoneLoader +from toontown.toonbase import ToontownGlobals +from toontown.hood.ToonHood import ToonHood + +class GSHood(ToonHood): + notify = directNotify.newCategory('GSHood') + + ID = ToontownGlobals.GoofySpeedway + SAFEZONELOADER_CLASS = GSSafeZoneLoader + STORAGE_DNA = 'phase_6/dna/storage_GS.pdna' + SKY_FILE = 'phase_3.5/models/props/TT_sky' + SPOOKY_SKY_FILE = 'phase_3.5/models/props/BR_sky' + TITLE_COLOR = (1.0, 0.5, 0.4, 1.0) + + def enter(self, requestStatus): + ToonHood.enter(self, requestStatus) + + base.localAvatar.chatMgr.chatInputSpeedChat.addKartRacingMenu() + base.camLens.setNearFar(ToontownGlobals.SpeedwayCameraNear, ToontownGlobals.SpeedwayCameraFar) + + def exit(self): + base.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar) + base.localAvatar.chatMgr.chatInputSpeedChat.removeKartRacingMenu() + + ToonHood.exit(self) diff --git a/toontown/hood/GSHoodAI.py b/toontown/hood/GSHoodAI.py new file mode 100755 index 00000000..719b1361 --- /dev/null +++ b/toontown/hood/GSHoodAI.py @@ -0,0 +1,154 @@ +from toontown.dna.DNAParser import DNAGroup, DNAVisGroup +from toontown.hood import HoodAI +from toontown.hood import ZoneUtil +from toontown.racing import RaceGlobals +from toontown.racing.DistributedRacePadAI import DistributedRacePadAI +from toontown.racing.DistributedStartingBlockAI import DistributedStartingBlockAI +from toontown.racing.DistributedViewPadAI import DistributedViewPadAI +from toontown.racing.DistributedStartingBlockAI import DistributedViewingBlockAI +from toontown.racing.DistributedLeaderBoardAI import DistributedLeaderBoardAI +from toontown.toonbase import ToontownGlobals + +class GSHoodAI(HoodAI.HoodAI): + def __init__(self, air): + HoodAI.HoodAI.__init__(self, air, + ToontownGlobals.GoofySpeedway, + ToontownGlobals.GoofySpeedway) + + self.racingPads = [] + self.viewingPads = [] + self.viewingBlocks = [] + self.startingBlocks = [] + self.leaderBoards = [] + + self.startup() + + def startup(self): + HoodAI.HoodAI.startup(self) + + self.createStartingBlocks() + self.createLeaderBoards() + + def shutdown(self): + HoodAI.HoodAI.shutdown(self) + + taskMgr.removeTasksMatching('leaderBoardSwitch') + for board in self.leaderBoards: + board.delete() + del self.leaderBoards + + def findRacingPads(self, dnaGroup, zoneId, area, padType='racing_pad'): + racingPads = [] + racingPadGroups = [] + if isinstance(dnaGroup, DNAGroup) and (padType in dnaGroup.getName()): + racingPadGroups.append(dnaGroup) + + if padType == 'racing_pad': + nameInfo = dnaGroup.getName().split('_') + racingPad = DistributedRacePadAI(simbase.air) + racingPad.setArea(zoneId) + racingPad.nameType = nameInfo[3] + racingPad.index = int(nameInfo[2]) + nextRaceInfo = RaceGlobals.getNextRaceInfo(-1, racingPad.nameType, racingPad.index) + racingPad.setTrackInfo([nextRaceInfo[0], nextRaceInfo[1]]) + racingPad.generateWithRequired(zoneId) + elif padType == 'viewing_pad': + racingPad = DistributedViewPadAI(simbase.air) + racingPad.setArea(zoneId) + racingPad.generateWithRequired(zoneId) + else: + self.notify.error('Invalid racing pad type: ' + padType) + + racingPads.append(racingPad) + elif isinstance(dnaGroup, DNAVisGroup): + zoneId = int(dnaGroup.getName().split(':')[0]) + for i in xrange(dnaGroup.getNumChildren()): + (foundRacingPads, foundRacingPadGroups) = self.findRacingPads(dnaGroup.at(i), zoneId, area, padType=padType) + racingPads.extend(foundRacingPads) + racingPadGroups.extend(foundRacingPadGroups) + return (racingPads, racingPadGroups) + + def findStartingBlocks(self, dnaGroup, racePad): + startingBlocks = [] + if isinstance(dnaGroup, DNAGroup) and ('starting_block' in dnaGroup.getName()): + x, y, z = dnaGroup.getPos() + h, p, r = dnaGroup.getHpr() + if isinstance(racePad, DistributedRacePadAI): + startingBlock = DistributedStartingBlockAI(simbase.air) + elif isinstance(racePad, DistributedViewPadAI): + startingBlock = DistributedViewingBlockAI(simbase.air) + else: + self.notify.error('Unknown starting block type.') + startingBlock.setPosHpr(x, y, z, h, p, r) + startingBlock.setPadDoId(racePad.doId) + startingBlock.setPadLocationId(getattr(racePad, 'index', 0)) + startingBlock.generateWithRequired(racePad.zoneId) + + startingBlocks.append(startingBlock) + for i in xrange(dnaGroup.getNumChildren()): + foundStartingBlocks = self.findStartingBlocks(dnaGroup.at(i), racePad) + startingBlocks.extend(foundStartingBlocks) + return startingBlocks + + def createStartingBlocks(self): + self.racingPads = [] + self.viewingPads = [] + racingPadGroups = [] + viewingPadGroups = [] + for zoneId in self.getZoneTable(): + dnaData = self.air.dnaDataMap.get(zoneId, None) + if dnaData.getName() == 'root': + area = ZoneUtil.getCanonicalZoneId(zoneId) + (foundRacingPads, foundRacingPadGroups) = self.findRacingPads(dnaData, zoneId, area, padType='racing_pad') + (foundViewingPads, foundViewingPadGroups) = self.findRacingPads(dnaData, zoneId, area, padType='viewing_pad') + self.racingPads.extend(foundRacingPads) + racingPadGroups.extend(foundRacingPadGroups) + self.viewingPads.extend(foundViewingPads) + viewingPadGroups.extend(foundViewingPadGroups) + self.startingBlocks = [] + for (dnaGroup, racePad) in zip(racingPadGroups, self.racingPads): + foundStartingBlocks = self.findStartingBlocks(dnaGroup, racePad) + self.startingBlocks.extend(foundStartingBlocks) + for startingBlock in foundStartingBlocks: + racePad.addStartingBlock(startingBlock) + self.viewingBlocks = [] + for (dnaGroup, viewPad) in zip(viewingPadGroups, self.viewingPads): + foundViewingBlocks = self.findStartingBlocks(dnaGroup, viewPad) + self.viewingBlocks.extend(foundViewingBlocks) + for viewingBlock in foundViewingBlocks: + viewPad.addStartingBlock(viewingBlock) + + def findLeaderBoards(self, dnaGroup, zoneId): + if not self.air.wantKarts: + return + + leaderBoards = [] + + if isinstance(dnaGroup, DNAGroup) and ('leader_board' in dnaGroup.getName()): + for i in xrange(dnaGroup.getNumChildren()): + childDnaGroup = dnaGroup.at(i) + + if 'leaderBoard' in childDnaGroup.getName(): + pos = childDnaGroup.getPos() + hpr = childDnaGroup.getHpr() + nameInfo = childDnaGroup.getName().split('_') + + if nameInfo[1] in RaceGlobals.LBSubscription: + leaderBoard = DistributedLeaderBoardAI(simbase.air, RaceGlobals.LBSubscription[nameInfo[1]]) + leaderBoard.setPosHpr(pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2]) + leaderBoard.generateWithRequired(zoneId) + leaderBoards.append(leaderBoard) + elif isinstance(dnaGroup, DNAVisGroup): + zoneId = int(dnaGroup.getName().split(':')[0]) + + for i in xrange(dnaGroup.getNumChildren()): + foundLeaderBoards = self.findLeaderBoards(dnaGroup.at(i), zoneId) + leaderBoards.extend(foundLeaderBoards) + + return leaderBoards + + def createLeaderBoards(self): + self.leaderBoards = [] + dnaData = self.air.dnaDataMap[self.zoneId] + if dnaData.getName() == 'root': + self.leaderBoards = self.findLeaderBoards(dnaData, self.zoneId) diff --git a/toontown/hood/GZHood.py b/toontown/hood/GZHood.py new file mode 100755 index 00000000..8ab2dcfe --- /dev/null +++ b/toontown/hood/GZHood.py @@ -0,0 +1,26 @@ +from toontown.safezone.GZSafeZoneLoader import GZSafeZoneLoader +from toontown.toonbase import ToontownGlobals +from toontown.hood.ToonHood import ToonHood + + +class GZHood(ToonHood): + notify = directNotify.newCategory('GZHood') + + ID = ToontownGlobals.GolfZone + SAFEZONELOADER_CLASS = GZSafeZoneLoader + STORAGE_DNA = 'phase_6/dna/storage_GZ.pdna' + SKY_FILE = 'phase_3.5/models/props/TT_sky' + SPOOKY_SKY_FILE = 'phase_3.5/models/props/BR_sky' + TITLE_COLOR = (1.0, 0.5, 0.4, 1.0) + + def enter(self, requestStatus): + ToonHood.enter(self, requestStatus) + + base.localAvatar.chatMgr.chatInputSpeedChat.addGolfMenu() + base.camLens.setNearFar(ToontownGlobals.SpeedwayCameraNear, ToontownGlobals.SpeedwayCameraFar) + + def exit(self): + base.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar) + base.localAvatar.chatMgr.chatInputSpeedChat.removeGolfMenu() + + ToonHood.exit(self) diff --git a/toontown/hood/GZHoodAI.py b/toontown/hood/GZHoodAI.py new file mode 100755 index 00000000..b3ad0a1a --- /dev/null +++ b/toontown/hood/GZHoodAI.py @@ -0,0 +1,55 @@ +from panda3d.core import * +from toontown.dna.DNAParser import DNAGroup, DNAVisGroup +from toontown.hood import HoodAI +from toontown.hood import ZoneUtil +from toontown.safezone.DistributedGolfKartAI import DistributedGolfKartAI +from toontown.toonbase import ToontownGlobals + + +class GZHoodAI(HoodAI.HoodAI): + def __init__(self, air): + HoodAI.HoodAI.__init__(self, air, + ToontownGlobals.GolfZone, + ToontownGlobals.GolfZone) + + self.golfKarts = [] + + self.startup() + + def startup(self): + HoodAI.HoodAI.startup(self) + + self.createGolfKarts() + + def findGolfKarts(self, dnaGroup, zoneId, area, overrideDNAZone=False): + golfKarts = [] + if isinstance(dnaGroup, DNAGroup) and ('golf_kart' in dnaGroup.getName()): + nameInfo = dnaGroup.getName().split('_') + golfCourse = int(nameInfo[2]) + for i in xrange(dnaGroup.getNumChildren()): + childDnaGroup = dnaGroup.at(i) + if 'starting_block' in childDnaGroup.getName(): + pos = childDnaGroup.getPos() + hpr = childDnaGroup.getHpr() + golfKart = DistributedGolfKartAI( + self.air, golfCourse, + pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2]) + golfKart.generateWithRequired(zoneId) + golfKarts.append(golfKart) + elif isinstance(dnaGroup, DNAVisGroup) and (not overrideDNAZone): + zoneId = int(dnaGroup.getName().split(':')[0]) + for i in xrange(dnaGroup.getNumChildren()): + foundGolfKarts = self.findGolfKarts(dnaGroup.at(i), zoneId, area, overrideDNAZone=overrideDNAZone) + golfKarts.extend(foundGolfKarts) + return golfKarts + + def createGolfKarts(self): + self.golfKarts = [] + for zoneId in self.getZoneTable(): + dnaData = self.air.dnaDataMap.get(zoneId, None) + if dnaData.getName() == 'root': + area = ZoneUtil.getCanonicalZoneId(zoneId) + foundGolfKarts = self.findGolfKarts(dnaData, zoneId, area, overrideDNAZone=True) + self.golfKarts.extend(foundGolfKarts) + for golfKart in self.golfKarts: + golfKart.start() diff --git a/toontown/hood/GenericAnimatedProp.py b/toontown/hood/GenericAnimatedProp.py new file mode 100755 index 00000000..0d6d3f78 --- /dev/null +++ b/toontown/hood/GenericAnimatedProp.py @@ -0,0 +1,109 @@ +import AnimatedProp +from direct.actor import Actor +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil +from toontown.hood import HoodUtil + +class GenericAnimatedProp(AnimatedProp.AnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('GenericAnimatedProp') + + def __init__(self, node): + AnimatedProp.AnimatedProp.__init__(self, node) + self.origAnimNameToSound = {} + code = node.getTag('DNACode') + + if code.startswith('interactive_prop_'): + pathStr = code[len('interactive_prop_'):].split('__')[0] + elif code.startswith('animated_prop_generic_'): + pathStr = code[len('animated_prop_generic_'):].split('__')[0] + elif code.startswith('animated_prop_'): + tempStr = code[len('animated_prop_'):] + nextUnderscore = tempStr.find('_') + finalStr = tempStr[nextUnderscore + 1:] + pathStr = finalStr.split('__')[0] + + phaseDelimeter = len('phase_') + pathStr[len('phase_'):].find('_') + phaseStr = pathStr[:phaseDelimeter] + pathTokens = pathStr[phaseDelimeter + 1:].split('_') + self.path = phaseStr + + for path in pathTokens: + self.path += '/' + self.path += path + + self.notify.debug('self.path=%s' % self.path) + self.calcHoodId(node) + self.propType = HoodUtil.calcPropType(node) + self.setupActor(node) + self.code = code + + def delete(self): + AnimatedProp.AnimatedProp.delete(self) + + if hasattr(self, 'soundNode'): + self.soundNode.removeNode() + del self.soundNode + + self.node.cleanup() + del self.node + del self.trashcan + + def enter(self): + self.node.postFlatten() + AnimatedProp.AnimatedProp.enter(self) + self.node.loop('anim') + + def exit(self): + AnimatedProp.AnimatedProp.exit(self) + self.node.stop() + + def getActor(self): + return self.node + + def setupActor(self, node): + anim = node.getTag('DNAAnim') + self.trashcan = Actor.Actor(node, copy=0) + self.trashcan.reparentTo(node) + self.trashcan.loadAnims({'anim': '%s/%s' % (self.path, anim)}) + self.trashcan.pose('anim', 0) + self.node = self.trashcan + + def calcHoodId(self, node): + self.hoodId = ToontownGlobals.ToontownCentral + fullString = str(node) + splits = fullString.split('/') + + if len(splits) >= 5: + visId = int(splits[4]) + self.visId = visId + self.hoodId = ZoneUtil.getCanonicalHoodId(visId) + self.notify.debug('calcHoodId %d from %s' % (self.hoodId, fullString)) + else: + self.notify.warning("calcHoodId couldn't parse %s using 0" % fullString) + self.hoodId = 0 + self.visId = 0 + + def createSoundInterval(self, origAnimNameWithPath, maximumDuration): + if not hasattr(base, 'localAvatar'): + return Sequence() + + if not hasattr(self, 'soundPath'): + self.soundPath = self.path.replace('/models/char', '/audio/sfx') + + origAnimName = origAnimNameWithPath.split('/')[-1] + sound = self.origAnimNameToSound.get(origAnimName) + + if not sound: + sound = loader.loadSfx('%s/%s.ogg' % (self.soundPath, origAnimName.replace('tt_a_ara', 'tt_s_ara'))) + self.origAnimNameToSound[origAnimName] = sound + + if sound: + if not hasattr(self, 'soundNode'): + self.soundNode = render.attachNewNode('Sound Node') + self.soundNode.setPos(self.trashcan.getBounds().getCenter()) + + return SoundInterval(sound, node=self.soundNode, listenerNode=base.localAvatar, volume=1.0, cutOff=45, startTime=0, duration=min(sound.length(), maximumDuration)) + + return Sequence() diff --git a/toontown/hood/HQPeriscopeAnimatedProp.py b/toontown/hood/HQPeriscopeAnimatedProp.py new file mode 100755 index 00000000..f8e75e8e --- /dev/null +++ b/toontown/hood/HQPeriscopeAnimatedProp.py @@ -0,0 +1,30 @@ +import AnimatedProp +from direct.actor import Actor +from direct.interval.IntervalGlobal import * + +class HQPeriscopeAnimatedProp(AnimatedProp.AnimatedProp): + + def __init__(self, node): + AnimatedProp.AnimatedProp.__init__(self, node) + parent = node.getParent() + self.periscope = Actor.Actor(node, copy=0) + self.periscope.reparentTo(parent) + self.periscope.loadAnims({'anim': 'phase_3.5/models/props/HQ_periscope-chan'}) + self.periscope.pose('anim', 0) + self.node = self.periscope + self.track = Sequence(Wait(2.0), self.periscope.actorInterval('anim', startFrame=0, endFrame=40), Wait(0.7), self.periscope.actorInterval('anim', startFrame=40, endFrame=90), Wait(0.7), self.periscope.actorInterval('anim', startFrame=91, endFrame=121), Wait(0.7), self.periscope.actorInterval('anim', startFrame=121, endFrame=91), Wait(0.7), self.periscope.actorInterval('anim', startFrame=90, endFrame=40), Wait(0.7), self.periscope.actorInterval('anim', startFrame=40, endFrame=90), Wait(0.7), self.periscope.actorInterval('anim', startFrame=91, endFrame=121), Wait(0.5), self.periscope.actorInterval('anim', startFrame=121, endFrame=148), Wait(3.0), name=self.uniqueName('HQPeriscope')) + + def delete(self): + AnimatedProp.AnimatedProp.delete(self) + self.node.cleanup() + del self.node + del self.periscope + del self.track + + def enter(self): + AnimatedProp.AnimatedProp.enter(self) + self.track.loop() + + def exit(self): + AnimatedProp.AnimatedProp.exit(self) + self.track.finish() diff --git a/toontown/hood/HQTelescopeAnimatedProp.py b/toontown/hood/HQTelescopeAnimatedProp.py new file mode 100755 index 00000000..b80587f0 --- /dev/null +++ b/toontown/hood/HQTelescopeAnimatedProp.py @@ -0,0 +1,30 @@ +import AnimatedProp +from direct.actor import Actor +from direct.interval.IntervalGlobal import * + +class HQTelescopeAnimatedProp(AnimatedProp.AnimatedProp): + + def __init__(self, node): + AnimatedProp.AnimatedProp.__init__(self, node) + parent = node.getParent() + self.telescope = Actor.Actor(node, copy=0) + self.telescope.reparentTo(parent) + self.telescope.loadAnims({'anim': 'phase_3.5/models/props/HQ_telescope-chan'}) + self.telescope.pose('anim', 0) + self.node = self.telescope + self.track = Sequence(Wait(5.0), self.telescope.actorInterval('anim', startFrame=0, endFrame=32), Wait(0.5), self.telescope.actorInterval('anim', startFrame=32, endFrame=78), Wait(0.5), self.telescope.actorInterval('anim', startFrame=79, endFrame=112), Wait(0.5), self.telescope.actorInterval('anim', startFrame=112, endFrame=79), Wait(0.5), self.telescope.actorInterval('anim', startFrame=78, endFrame=32), Wait(0.5), self.telescope.actorInterval('anim', startFrame=32, endFrame=78), Wait(0.5), self.telescope.actorInterval('anim', startFrame=79, endFrame=112), Wait(0.5), self.telescope.actorInterval('anim', startFrame=112, endFrame=148), Wait(4.0), name=self.uniqueName('HQTelescope')) + + def delete(self): + AnimatedProp.AnimatedProp.delete(self) + self.node.cleanup() + del self.node + del self.telescope + del self.track + + def enter(self): + AnimatedProp.AnimatedProp.enter(self) + self.track.loop() + + def exit(self): + AnimatedProp.AnimatedProp.exit(self) + self.track.finish() diff --git a/toontown/hood/Hood.py b/toontown/hood/Hood.py new file mode 100755 index 00000000..9f605e30 --- /dev/null +++ b/toontown/hood/Hood.py @@ -0,0 +1,277 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.distributed.ToontownMsgTypes import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from toontown.minigame import Purchase +from direct.gui import OnscreenText +from toontown.building import SuitInterior +import QuietZoneState +import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toon.Toon import teleportDebug +from toontown.dna.DNAParser import * + +class Hood(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('Hood') + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + StateData.StateData.__init__(self, doneEvent) + self.loader = 'not initialized' + self.parentFSM = parentFSM + self.dnaStore = dnaStore + self.loaderDoneEvent = 'loaderDone' + self.id = None + self.hoodId = hoodId + self.titleText = None + self.titleColor = (1, 1, 1, 1) + self.holidayStorageDNADict = {} + self.spookySkyFile = None + self.snowySkyFile = 'phase_3.5/models/props/BR_sky' + self.halloweenLights = [] + return + + def enter(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + hoodText = self.getHoodText(zoneId) + self.titleText = OnscreenText.OnscreenText(hoodText, fg=self.titleColor, font=getSignFont(), pos=(0, -0.5), scale=TTLocalizer.HtitleText, drawOrder=0, mayChange=1) + self.fsm.request(requestStatus['loader'], [requestStatus]) + + def getHoodText(self, zoneId): + hoodText = base.cr.hoodMgr.getFullnameFromId(self.id) + if self.id != Tutorial: + streetName = StreetNames.get(ZoneUtil.getCanonicalBranchZone(zoneId)) + if streetName: + hoodText = hoodText + '\n' + streetName[-1] + return hoodText + + def spawnTitleText(self, zoneId): + hoodText = self.getHoodText(zoneId) + self.doSpawnTitleText(hoodText) + + def doSpawnTitleText(self, text): + self.titleText.setText(text) + self.titleText.show() + self.titleText.setColor(Vec4(*self.titleColor)) + self.titleText.clearColorScale() + self.titleText.setFg(self.titleColor) + seq = Sequence(Wait(0.1), Wait(6.0), self.titleText.colorScaleInterval(0.5, Vec4(1.0, 1.0, 1.0, 0.0)), Func(self.titleText.hide)) + seq.start() + + def hideTitleText(self): + if self.titleText: + self.titleText.hide() + + def exit(self): + taskMgr.remove('titleText') + if self.titleText: + self.titleText.cleanup() + self.titleText = None + base.localAvatar.stopChat() + return + + def load(self): + files = [] + + if self.storageDNAFile: + files.append(self.storageDNAFile) + + for key, value in self.holidayStorageDNADict.iteritems(): + if base.cr.newsManager.isHolidayRunning(key): + for storageFile in value: + files.append(storageFile) + + if not base.cr.newsManager.isHolidayRunning(ToontownGlobals.HALLOWEEN) or not self.spookySkyFile: + self.sky = loader.loadModel(self.skyFile) + self.sky.setTag('sky', 'Regular') + self.sky.setScale(1.0) + self.sky.setFogOff() + else: + self.sky = loader.loadModel(self.spookySkyFile) + self.sky.setTag('sky', 'Halloween') + + dnaBulk = DNABulkLoader(self.dnaStore, tuple(files)) + dnaBulk.loadDNAFiles() + + def unload(self): + if hasattr(self, 'loader'): + self.notify.info('Aggressively cleaning up loader: %s' % self.loader) + self.loader.exit() + self.loader.unload() + del self.loader + del self.fsm + del self.parentFSM + self.dnaStore.resetHood() + del self.dnaStore + self.sky.removeNode() + del self.sky + self.ignoreAll() + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def enterStart(self): + pass + + def exitStart(self): + pass + + def isSameHood(self, status): + return status['hoodId'] == self.hoodId and status['shardId'] == None + + def enterFinal(self): + pass + + def exitFinal(self): + pass + + def enterQuietZone(self, requestStatus): + teleportDebug(requestStatus, 'Hood.enterQuietZone: status=%s' % requestStatus) + self._quietZoneDoneEvent = uniqueName('quietZoneDone') + self.acceptOnce(self._quietZoneDoneEvent, self.handleQuietZoneDone) + self.quietZoneStateData = QuietZoneState.QuietZoneState(self._quietZoneDoneEvent) + self._enterWaitForSetZoneResponseMsg = self.quietZoneStateData.getEnterWaitForSetZoneResponseMsg() + self.acceptOnce(self._enterWaitForSetZoneResponseMsg, self.handleWaitForSetZoneResponse) + self._quietZoneLeftEvent = self.quietZoneStateData.getQuietZoneLeftEvent() + if base.placeBeforeObjects: + self.acceptOnce(self._quietZoneLeftEvent, self.handleLeftQuietZone) + self.quietZoneStateData.load() + self.quietZoneStateData.enter(requestStatus) + + def exitQuietZone(self): + self.ignore(self._quietZoneDoneEvent) + self.ignore(self._quietZoneLeftEvent) + self.ignore(self._enterWaitForSetZoneResponseMsg) + del self._quietZoneDoneEvent + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + return + + def loadLoader(self, requestStatus): + pass + + def handleWaitForSetZoneResponse(self, requestStatus): + loaderName = requestStatus['loader'] + if loaderName == 'safeZoneLoader': + if not loader.inBulkBlock: + loader.beginBulkLoad('hood', TTLocalizer.HeadingToPlayground, safeZoneCountMap[self.id], 1, TTLocalizer.TIP_GENERAL, self.id) + self.loadLoader(requestStatus) + loader.endBulkLoad('hood') + elif loaderName == 'townLoader': + if not loader.inBulkBlock: + zoneId = requestStatus['zoneId'] + toPhrase = StreetNames[ZoneUtil.getCanonicalBranchZone(zoneId)][0] + streetName = StreetNames[ZoneUtil.getCanonicalBranchZone(zoneId)][-1] + loader.beginBulkLoad('hood', TTLocalizer.HeadingToStreet % {'to': toPhrase, + 'street': streetName}, townCountMap[self.id], 1, TTLocalizer.TIP_STREET, zoneId) + self.loadLoader(requestStatus) + loader.endBulkLoad('hood') + elif loaderName == 'minigame': + pass + elif loaderName == 'cogHQLoader': + print 'should be loading HQ' + + def handleLeftQuietZone(self): + status = self.quietZoneStateData.getRequestStatus() + teleportDebug(status, 'handleLeftQuietZone, status=%s' % status) + teleportDebug(status, 'requesting %s' % status['loader']) + self.fsm.request(status['loader'], [status]) + + def handleQuietZoneDone(self): + if not base.placeBeforeObjects: + status = self.quietZoneStateData.getRequestStatus() + self.fsm.request(status['loader'], [status]) + + def enterSafeZoneLoader(self, requestStatus): + self.accept(self.loaderDoneEvent, self.handleSafeZoneLoaderDone) + self.loader.enter(requestStatus) + self.spawnTitleText(requestStatus['zoneId']) + + def exitSafeZoneLoader(self): + taskMgr.remove('titleText') + self.hideTitleText() + self.ignore(self.loaderDoneEvent) + self.loader.exit() + self.loader.unload() + del self.loader + + def handleSafeZoneLoaderDone(self): + doneStatus = self.loader.getDoneStatus() + teleportDebug(doneStatus, 'handleSafeZoneLoaderDone, doneStatus=%s' % doneStatus) + if self.isSameHood(doneStatus) and doneStatus['where'] != 'party' or doneStatus['loader'] == 'minigame': + teleportDebug(doneStatus, 'same hood') + self.fsm.request('quietZone', [doneStatus]) + else: + teleportDebug(doneStatus, 'different hood') + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + + def startSky(self): + self.sky.reparentTo(camera) + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) + + def stopSky(self): + taskMgr.remove('skyTrack') + self.sky.reparentTo(hidden) + + def startSpookySky(self): + if not self.spookySkyFile: + return + if hasattr(self, 'sky') and self.sky: + self.stopSky() + self.sky = loader.loadModel(self.spookySkyFile) + self.sky.setTag('sky', 'Halloween') + self.sky.setColor(0.5, 0.5, 0.5, 1) + self.sky.reparentTo(camera) + self.sky.setTransparency(TransparencyAttrib.MDual, 1) + fadeIn = self.sky.colorScaleInterval(1.5, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0.25), blendType='easeInOut') + fadeIn.start() + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) + + def endSpookySky(self): + if hasattr(self, 'sky') and self.sky: + self.sky.reparentTo(hidden) + if hasattr(self, 'sky'): + self.sky = loader.loadModel(self.skyFile) + self.sky.setTag('sky', 'Regular') + self.sky.setScale(1.0) + self.startSky() + + def startSnowySky(self): + if hasattr(self, 'sky') and self.sky: + self.stopSky() + self.sky = loader.loadModel(self.snowySkyFile) + self.sky.setTag('sky', 'Winter') + self.sky.setScale(1.0) + self.sky.setDepthTest(0) + self.sky.setDepthWrite(0) + self.sky.setColor(1, 1, 1, 1) + self.sky.setBin('background', 100) + self.sky.setFogOff() + self.sky.reparentTo(camera) + self.sky.setTransparency(TransparencyAttrib.MDual, 1) + fadeIn = self.sky.colorScaleInterval(1.5, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0.25), blendType='easeInOut') + fadeIn.start() + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) + + def endSnowySky(self): + if hasattr(self, 'sky') and self.sky: + self.sky.reparentTo(hidden) + if hasattr(self, 'sky'): + self.sky = loader.loadModel(self.skyFile) + self.sky.setTag('sky', 'Regular') + self.sky.setScale(1.0) + self.startSky() diff --git a/toontown/hood/HoodAI.py b/toontown/hood/HoodAI.py new file mode 100755 index 00000000..8ba9b854 --- /dev/null +++ b/toontown/hood/HoodAI.py @@ -0,0 +1,181 @@ +from direct.directnotify.DirectNotifyGlobal import * +from toontown.building import DistributedBuildingMgrAI +from toontown.dna.DNAParser import DNAStorage, DNAGroup, DNAVisGroup +from toontown.fishing.DistributedFishingPondAI import DistributedFishingPondAI +from toontown.hood import ZoneUtil +from toontown.safezone import TreasureGlobals +from toontown.safezone.DistributedFishingSpotAI import DistributedFishingSpotAI +from toontown.safezone.DistributedPartyGateAI import DistributedPartyGateAI +from toontown.safezone.SZTreasurePlannerAI import SZTreasurePlannerAI +from toontown.suit import DistributedSuitPlannerAI +from toontown.toon import NPCToons +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals + + +class HoodAI: + notify = directNotify.newCategory('HoodAI') + notify.setInfo(True) + + def __init__(self, air, zoneId, canonicalHoodId): + self.air = air + self.zoneId = zoneId + self.canonicalHoodId = canonicalHoodId + + self.fishingPonds = [] + self.partyGates = [] + self.treasurePlanner = None + self.buildingManagers = [] + self.suitPlanners = [] + + for zoneId in self.getZoneTable(): + self.notify.info('Creating objects... ' + self.getLocationName(zoneId)) + dnaFileName = self.air.lookupDNAFileName(zoneId) + dnaStore = DNAStorage() + dnaData = simbase.air.loadDNAFileAI(dnaStore, dnaFileName) + self.air.dnaStoreMap[zoneId] = dnaStore + self.air.dnaDataMap[zoneId] = dnaData + + def getZoneTable(self): + zoneTable = [self.zoneId] + zoneTable.extend(ToontownGlobals.HoodHierarchy.get(self.canonicalHoodId, [])) + return zoneTable + + def getLocationName(self, zoneId): + lookupTable = ToontownGlobals.hoodNameMap + isStreet = (zoneId%1000) != 0 + if isStreet: + lookupTable = TTLocalizer.GlobalStreetNames + name = lookupTable.get(zoneId, '') + if isStreet: + return '%s, %s' % (self.getLocationName(self.zoneId), name[2]) + return name[2] + + def startup(self): + if self.air.wantFishing: + self.createFishingPonds() + if self.air.wantParties: + self.createPartyPeople() + if simbase.config.GetBool('want-treasure-planners', True): + self.createTreasurePlanner() + self.createBuildingManagers() + if simbase.config.GetBool('want-suit-planners', True): + self.createSuitPlanners() + + def shutdown(self): + if self.treasurePlanner: + self.treasurePlanner.stop() + self.treasurePlanner.deleteAllTreasuresNow() + self.treasurePlanner = None + for suitPlanner in self.suitPlanners: + suitPlanner.requestDelete() + del self.air.suitPlanners[suitPlanner.zoneId] + self.suitPlanners = [] + for buildingManager in self.buildingManagers: + buildingManager.cleanup() + del self.air.buildingManagers[buildingManager.branchId] + self.buildingManagers = [] + del self.fishingPonds + for distObj in self.doId2do.values(): + distObj.requestDelete() + + def findFishingPonds(self, dnaGroup, zoneId, area): + fishingPonds = [] + fishingPondGroups = [] + if isinstance(dnaGroup, DNAGroup) and ('fishing_pond' in dnaGroup.getName()): + fishingPondGroups.append(dnaGroup) + + fishingPond = DistributedFishingPondAI(simbase.air) + fishingPond.setArea(area) + fishingPond.generateWithRequired(zoneId) + fishingPond.start() + fishingPonds.append(fishingPond) + elif isinstance(dnaGroup, DNAVisGroup): + zoneId = int(dnaGroup.getName().split(':')[0]) + for i in xrange(dnaGroup.getNumChildren()): + (foundFishingPonds, foundFishingPondGroups) = self.findFishingPonds(dnaGroup.at(i), zoneId, area) + fishingPonds.extend(foundFishingPonds) + fishingPondGroups.extend(foundFishingPondGroups) + return (fishingPonds, fishingPondGroups) + + def findFishingSpots(self, dnaGroup, fishingPond): + fishingSpots = [] + if isinstance(dnaGroup, DNAGroup) and ('fishing_spot' in dnaGroup.getName()): + fishingSpot = DistributedFishingSpotAI(simbase.air) + fishingSpot.setPondDoId(fishingPond.doId) + x, y, z = dnaGroup.getPos() + h, p, r = dnaGroup.getHpr() + fishingSpot.setPosHpr(x, y, z, h, p, r) + fishingSpot.generateWithRequired(fishingPond.zoneId) + + fishingSpots.append(fishingSpot) + for i in xrange(dnaGroup.getNumChildren()): + foundFishingSpots = self.findFishingSpots(dnaGroup.at(i), fishingPond) + fishingSpots.extend(foundFishingSpots) + return fishingSpots + + def createFishingPonds(self): + self.fishingPonds = [] + fishingPondGroups = [] + for zoneId in self.getZoneTable(): + dnaData = self.air.dnaDataMap.get(zoneId, None) + if dnaData.getName() == 'root': + area = ZoneUtil.getCanonicalZoneId(zoneId) + (foundFishingPonds, foundFishingPondGroups) = self.findFishingPonds(dnaData, zoneId, area) + self.fishingPonds.extend(foundFishingPonds) + fishingPondGroups.extend(foundFishingPondGroups) + for fishingPond in self.fishingPonds: + NPCToons.createNpcsInZone(self.air, fishingPond.zoneId) + fishingSpots = [] + for (dnaGroup, fishingPond) in zip(fishingPondGroups, self.fishingPonds): + fishingSpots.extend(self.findFishingSpots(dnaGroup, fishingPond)) + + def findPartyGates(self, dnaGroup, zoneId): + partyGates = [] + if isinstance(dnaGroup, DNAGroup) and ('prop_party_gate' in dnaGroup.getName()): + partyGate = DistributedPartyGateAI(simbase.air) + partyGate.setArea(zoneId) + partyGate.generateWithRequired(zoneId) + + partyGates.append(partyGates) + for i in xrange(dnaGroup.getNumChildren()): + foundPartyGates = self.findPartyGates(dnaGroup.at(i), zoneId) + partyGates.extend(foundPartyGates) + return partyGates + + def createPartyPeople(self): + self.partyGates = [] + for zoneId in self.getZoneTable(): + dnaData = self.air.dnaDataMap.get(zoneId, None) + if dnaData.getName() == 'root': + foundPartyGates = self.findPartyGates(dnaData, zoneId) + self.partyGates.extend(foundPartyGates) + + def createTreasurePlanner(self): + spawnInfo = TreasureGlobals.SafeZoneTreasureSpawns.get(self.canonicalHoodId) + if not spawnInfo: + return + treasureType, healAmount, spawnPoints, spawnRate, maxTreasures = spawnInfo + self.treasurePlanner = SZTreasurePlannerAI( + self.canonicalHoodId, treasureType, healAmount, spawnPoints, + spawnRate, maxTreasures) + self.treasurePlanner.start() + + def createBuildingManagers(self): + for zoneId in self.getZoneTable(): + dnaStore = self.air.dnaStoreMap[zoneId] + buildingManager = DistributedBuildingMgrAI.DistributedBuildingMgrAI( + self.air, zoneId, dnaStore, self.air.trophyMgr) + self.buildingManagers.append(buildingManager) + self.air.buildingManagers[zoneId] = buildingManager + + def createSuitPlanners(self): + for zoneId in self.getZoneTable(): + if zoneId == self.zoneId: + continue + suitPlanner = DistributedSuitPlannerAI.DistributedSuitPlannerAI(self.air, zoneId) + suitPlanner.generateWithRequired(zoneId) + suitPlanner.d_setZoneId(zoneId) + suitPlanner.initTasks() + self.suitPlanners.append(suitPlanner) + self.air.suitPlanners[zoneId] = suitPlanner diff --git a/toontown/hood/HoodUtil.py b/toontown/hood/HoodUtil.py new file mode 100755 index 00000000..f66b06cb --- /dev/null +++ b/toontown/hood/HoodUtil.py @@ -0,0 +1,12 @@ +from toontown.toonbase import ToontownGlobals + +def calcPropType(node): + propType = ToontownGlobals.AnimPropTypes.Unknown + fullString = str(node) + if 'hydrant' in fullString: + propType = ToontownGlobals.AnimPropTypes.Hydrant + elif 'trashcan' in fullString: + propType = ToontownGlobals.AnimPropTypes.Trashcan + elif 'mailbox' in fullString: + propType = ToontownGlobals.AnimPropTypes.Mailbox + return propType diff --git a/toontown/hood/HydrantInteractiveProp.py b/toontown/hood/HydrantInteractiveProp.py new file mode 100755 index 00000000..59d2317e --- /dev/null +++ b/toontown/hood/HydrantInteractiveProp.py @@ -0,0 +1,118 @@ +from direct.actor import Actor +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import Sequence, Func +from toontown.hood import InteractiveAnimatedProp +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals, TTLocalizer + +class HydrantInteractiveProp(InteractiveAnimatedProp.InteractiveAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('HydrantInteractiveProp') + BattleTrack = ToontownBattleGlobals.SQUIRT_TRACK + BattleCheerText = TTLocalizer.InteractivePropTrackBonusTerms[BattleTrack] + + ZoneToIdles = { + ToontownGlobals.ToontownCentral: (('tt_a_ara_ttc_hydrant_idle0', 1, 1, None, 3, 10), + ('tt_a_ara_ttc_hydrant_idle2', 1, 1, None, 3, 10), + ('tt_a_ara_ttc_hydrant_idle1', 1, 1, None, 3, 10), + ('tt_a_ara_ttc_hydrant_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.DonaldsDock: (('tt_a_ara_ttc_hydrant_idle0', 1, 1, None, 3, 10), + ('tt_a_ara_ttc_hydrant_idle2', 1, 1, None, 3, 10), + ('tt_a_ara_ttc_hydrant_idle1', 1, 1, None, 3, 10), + ('tt_a_ara_ttc_hydrant_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.DaisyGardens: (('tt_a_ara_dga_hydrant_idle0', 3, 10, 'tt_a_ara_dga_hydrant_idle0settle', 3, 10), + ('tt_a_ara_dga_hydrant_idleLook1', 1, 1, None, 3, 10), + ('tt_a_ara_dga_hydrant_idleSneeze2', 1, 1, None, 3, 10), + ('tt_a_ara_dga_hydrant_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.MinniesMelodyland: (('tt_a_ara_mml_hydrant_idle0', 3, 10, 'tt_a_ara_mml_hydrant_idle0settle', 3, 10), + ('tt_a_ara_mml_hydrant_idle2', 3, 10, 'tt_a_ara_mml_hydrant_idle2settle', 3, 10), + ('tt_a_ara_mml_hydrant_idle1', 3, 10, 'tt_a_ara_mml_hydrant_idle1settle', 3, 10), + ('tt_a_ara_mml_hydrant_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.TheBrrrgh: (('tt_a_ara_tbr_hydrant_idleShiver1', 1, 1, None, 3, 10), + ('tt_a_ara_tbr_hydrant_idleRubNose0', 1, 1, None, 3, 10), + ('tt_a_ara_tbr_hydrant_idleSneeze2', 1, 1, None, 3, 10), + ('tt_a_ara_tbr_hydrant_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.DonaldsDreamland: (('tt_a_ara_ddl_hydrant_idle0', 3, 10, None, 0, 0), + ('tt_a_ara_ddl_hydrant_idle1', 1, 1, None, 0, 0), + ('tt_a_ara_ddl_hydrant_idle2', 1, 1, None, 0, 0), + ('tt_a_ara_ddl_hydrant_idleAwesome3', 1, 1, None, 0, 0))} + + ZoneToIdleIntoFightAnims = { + ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_hydrant_idleIntoFight', + ToontownGlobals.DonaldsDock: 'tt_a_ara_ttc_hydrant_idleIntoFight', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_hydrant_idleIntoFight', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_hydrant_idleIntoFight', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_hydrant_idleIntoFight', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_hydrant_idleIntoFight'} + + ZoneToVictoryAnims = { + ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_hydrant_victoryDance', + ToontownGlobals.DonaldsDock: 'tt_a_ara_ttc_hydrant_victoryDance', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_hydrant_victoryDance', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_hydrant_victoryDance', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_hydrant_victoryDance', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_hydrant_victoryDance'} + + ZoneToSadAnims = { + ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_hydrant_fightSad', + ToontownGlobals.DonaldsDock: 'tt_a_ara_ttc_hydrant_fightSad', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_hydrant_fightSad', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_hydrant_fightSad', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_hydrant_fightSad', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_hydrant_fightSad'} + + ZoneToFightAnims = { + ToontownGlobals.ToontownCentral: ('tt_a_ara_ttc_hydrant_fightBoost', 'tt_a_ara_ttc_hydrant_fightCheer', 'tt_a_ara_ttc_hydrant_fightIdle'), + ToontownGlobals.DonaldsDock: ('tt_a_ara_ttc_hydrant_fightBoost', 'tt_a_ara_ttc_hydrant_fightCheer', 'tt_a_ara_ttc_hydrant_fightIdle'), + ToontownGlobals.DaisyGardens: ('tt_a_ara_dga_hydrant_fightBoost', 'tt_a_ara_dga_hydrant_fightCheer', 'tt_a_ara_dga_hydrant_fightIdle'), + ToontownGlobals.MinniesMelodyland: ('tt_a_ara_mml_hydrant_fightBoost', 'tt_a_ara_mml_hydrant_fightCheer', 'tt_a_ara_mml_hydrant_fightIdle'), + ToontownGlobals.TheBrrrgh: ('tt_a_ara_tbr_hydrant_fightBoost', 'tt_a_ara_tbr_hydrant_fightCheer', 'tt_a_ara_tbr_hydrant_fightIdle'), + ToontownGlobals.DonaldsDreamland: ('tt_a_ara_ddl_hydrant_fightBoost', 'tt_a_ara_ddl_hydrant_fightCheer', 'tt_a_ara_ddl_hydrant_fightIdle')} + + IdlePauseTime = base.config.GetFloat('prop-idle-pause-time', 0.0) + + def __init__(self, node): + self.leftWater = None + self.rightWater = None + InteractiveAnimatedProp.InteractiveAnimatedProp.__init__(self, node) + + def setupActor(self, node): + InteractiveAnimatedProp.InteractiveAnimatedProp.setupActor(self, node) + + if not self.hoodId == ToontownGlobals.TheBrrrgh: + water = loader.loadModel('phase_5/models/char/tt_m_efx_hydrantSquirt') + self.leftWater = water.find('**/efx_hydrantSquirtLeft') + self.rightWater = water.find('**/efx_hydrantSquirtRight') + + if self.leftWater: + self.leftWater.reparentTo(self.node.find('**/dx_left_water')) + base.leftWater = self.leftWater + self.leftWater.hide() + + if self.rightWater: + self.rightWater.reparentTo(self.node.find('**/dx_right_water')) + self.rightWater.hide() + + def hideWater(self): + if self.leftWater: + self.leftWater.hide() + if self.rightWater: + self.rightWater.hide() + + def showWater(self): + if self.leftWater: + self.leftWater.show() + if self.rightWater: + self.rightWater.show() + + def hasOverrideIval(self, origAnimName): + return ('fightBoost' in origAnimName or 'fightCheer' in origAnimName) and not self.hoodId == ToontownGlobals.TheBrrrgh + + def getOverrideIval(self, origAnimName): + result = Sequence() + + if self.hasOverrideIval(origAnimName): + result.append(Func(self.showWater)) + animAndSound = self.createAnimAndSoundIval('fight0' if 'fightBoost' in origAnimName else 'fight1') + result.append(animAndSound) + result.append(Func(self.hideWater)) + + return result diff --git a/toontown/hood/InteractiveAnimatedProp.py b/toontown/hood/InteractiveAnimatedProp.py new file mode 100755 index 00000000..adf44189 --- /dev/null +++ b/toontown/hood/InteractiveAnimatedProp.py @@ -0,0 +1,455 @@ +from pandac.PandaModules import TextNode, Vec3 +from direct.actor import Actor +from direct.fsm import FSM +from direct.interval.IntervalGlobal import Sequence, ActorInterval, Wait, Func, SoundInterval, Parallel +from direct.showbase.PythonUtil import weightedChoice +from toontown.toonbase import ToontownGlobals +import GenericAnimatedProp +import math, random + +def clearPythonIvals(ival): + if hasattr(ival, 'function'): + ival.function = None + if hasattr(ival, 'pythonIvals'): + for oneIval in ival.pythonIvals: + clearPythonIvals(oneIval) + + ival.pythonIvals = [] + +class InteractiveAnimatedProp(GenericAnimatedProp.GenericAnimatedProp, FSM.FSM): + ZoneToIdles = {} + ZoneToIdleIntoFightAnims = {} + ZoneToFightAnims = {} + ZoneToVictoryAnims = {} + ZoneToSadAnims = {} + IdlePauseTime = base.config.GetFloat('prop-idle-pause-time', 0.0) + HpTextGenerator = TextNode('HpTextGenerator') + BattleCheerText = '+' + + def __init__(self, node): + FSM.FSM.__init__(self, 'InteractiveProp-%s' % str(node)) + self.numIdles = 0 + self.numFightAnims = 0 + self.idleInterval = None + self.battleCheerInterval = None + self.sadInterval = None + self.victoryInterval = None + self.lastIdleAnimName = '' + self.lastIdleTime = 0 + self.curIval = None + self.okToStartNextAnim = False + cellIndexStr = node.getTag('DNACellIndex') + self.cellIndex = ord(cellIndexStr) + self.origAnimNameToSound = {} + self.lastPlayingAnimPhase = 0 + self.buildingsMakingMeSad = set() + GenericAnimatedProp.GenericAnimatedProp.__init__(self, node) + + def delete(self): + self.exit() + GenericAnimatedProp.GenericAnimatedProp.delete(self) + self.idleInterval = None + self.battleCheerInterval = None + self.sadInterval = None + self.victoryInterval = None + + def getCellIndex(self): + return self.cellIndex + + def playBattleCheerAnim(self): + self.node.loop('battleCheer') + + def setupActor(self, node): + if self.hoodId in self.ZoneToIdles: + self.numIdles = len(self.ZoneToIdles[self.hoodId]) + if self.hoodId in self.ZoneToFightAnims: + self.numFightAnims = len(self.ZoneToFightAnims[self.hoodId]) + + self.idleInterval = None + anim = node.getTag('DNAAnim') + self.trashcan = Actor.Actor(node, copy=0) + self.trashcan.reparentTo(node) + animDict = {} + animDict['anim'] = '%s/%s' % (self.path, anim) + + for i in xrange(self.numIdles): + baseAnim = self.ZoneToIdles[self.hoodId][i] + + if isinstance(baseAnim, tuple): + baseAnim = baseAnim[0] + + animStr = self.path + '/' + baseAnim + animKey = 'idle%d' % i + animDict[animKey] = animStr + settleName = self.getSettleName(i) + + if settleName: + settleStr = self.path + '/' + settleName + settleKey = 'settle%d' % i + animDict[settleKey] = settleStr + + for i in xrange(self.numFightAnims): + animStr = self.path + '/' + self.ZoneToFightAnims[self.hoodId][i] + animKey = 'fight%d' % i + animDict[animKey] = animStr + + if self.hoodId in self.ZoneToIdleIntoFightAnims: + animStr = self.path + '/' + self.ZoneToIdleIntoFightAnims[self.hoodId] + animKey = 'idleIntoFight' + animDict[animKey] = animStr + + if self.hoodId in self.ZoneToIdleIntoFightAnims: + animStr = self.path + '/' + self.ZoneToVictoryAnims[self.hoodId] + animKey = 'victory' + animDict[animKey] = animStr + + if self.hoodId in self.ZoneToSadAnims: + animStr = self.path + '/' + self.ZoneToSadAnims[self.hoodId] + animKey = 'sad' + animDict[animKey] = animStr + + self.trashcan.loadAnims(animDict) + self.trashcan.pose('anim', 0) + self.node = self.trashcan + self.idleInterval = self.createIdleInterval() + self.battleCheerInterval = self.createBattleCheerInterval() + self.victoryInterval = self.createVictoryInterval() + self.sadInterval = self.createSadInterval() + + def createIdleInterval(self): + result = Sequence() + + if self.numIdles >= 3: + numberOfAnimsAbove2 = self.numIdles - 2 + for rareIdle in xrange(2, self.numIdles): + for i in xrange(2): + result.append(ActorInterval(self.node, 'idle0')) + result.append(Wait(self.IdlePauseTime)) + result.append(ActorInterval(self.node, 'idle1')) + result.append(Wait(self.IdlePauseTime)) + + result.append(ActorInterval(self.node, 'idle%d' % rareIdle)) + result.append(Wait(self.IdlePauseTime)) + + else: + for i in xrange(self.numIdles): + result.append(ActorInterval(self.node, 'idle%d' % i)) + + self.notify.debug('idle interval=%s' % result) + return result + + def createBattleCheerText(self): + self.HpTextGenerator.setFont(ToontownGlobals.getSignFont()) + self.HpTextGenerator.setText(self.BattleCheerText) + self.HpTextGenerator.clearShadow() + self.HpTextGenerator.setAlign(TextNode.ACenter) + r = 0 + g = 0 + b = 1 + a = 1 + self.HpTextGenerator.setTextColor(r, g, b, a) + self.hpTextNode = self.HpTextGenerator.generate() + self.hpText = self.node.attachNewNode(self.hpTextNode) + self.hpText.setScale(1) + self.hpText.setBillboardPointEye() + self.hpText.setBin('fixed', 100) + self.hpText.setPos(0, 0, 4) + self.hpText.hide() + + def createBattleCheerInterval(self): + result = Sequence() + + for i in xrange(self.numFightAnims): + animKey = 'fight%d' % i + animAndSoundIval = self.createAnimAndSoundIval(animKey) + origAnimName = self.node.getAnimFilename(animKey).split('/')[-1] + if self.hasOverrideIval(origAnimName): + result.append(self.getOverrideIval(origAnimName)) + elif self.hasSpecialIval(origAnimName): + result.append(Parallel(animAndSoundIval, self.getSpecialIval(origAnimName))) + else: + result.append(animAndSoundIval) + + self.createBattleCheerText() + battleCheerTextIval = Sequence(Func(self.hpText.show), self.hpText.posInterval(duration=4.0, pos=Vec3(0, 0, 7), startPos=(0, 0, 3)), Func(self.hpText.hide)) + ivalWithText = Parallel(battleCheerTextIval, result) + + return ivalWithText + + def createSadInterval(self): + result = Sequence() + if self.hoodId in self.ZoneToSadAnims: + result = self.createAnimAndSoundIval('sad') + return result + + def hasSpecialIval(self, origAnimName): + return False + + def getSpecialIval(self, origAnimName): + return Sequence() + + def hasOverrideIval(self, origAnimName): + return False + + def getOverrideIval(self, origAnimName): + return Sequence() + + def createVictoryInterval(self): + result = Sequence() + + if self.hoodId in self.ZoneToVictoryAnims: + animAndSoundIval = self.createAnimAndSoundIval('victory') + result.append(animAndSoundIval) + + return result + + def enter(self): + GenericAnimatedProp.GenericAnimatedProp.enter(self) + + if base.config.GetBool('props-buff-battles', True): + self.notify.debug('props buff battles is true') + self.node.stop() + self.node.pose('idle0', 0) + self.requestIdleOrSad() + else: + self.notify.debug('props do not buff battles') + self.node.stop() + self.node.pose('idle0', 0) + + def exit(self): + self.okToStartNextAnim = False + self.notify.debug('%s %d okToStartNextAnim=%s' % (self, self.visId, self.okToStartNextAnim)) + GenericAnimatedProp.GenericAnimatedProp.exit(self) + self.request('Off') + + def requestIdleOrSad(self): + if not hasattr(self, 'node') or not self.node: + self.notify.warning("requestIdleOrSad returning hasattr(self,'node')=%s" % hasattr(self, 'node')) + return + + if self.buildingsMakingMeSad: + self.request('Sad') + else: + self.request('DoIdleAnim') + + def enterDoIdleAnim(self): + self.notify.debug('enterDoIdleAnim numIdels=%d' % self.numIdles) + self.okToStartNextAnim = True + self.notify.debug('%s %d okToStartNextAnim=%s' % (self, self.visId, self.okToStartNextAnim)) + self.startNextIdleAnim() + + def exitDoIdleAnim(self): + self.notify.debug('exitDoIdlesAnim numIdles=%d' % self.numIdles) + self.okToStartNextAnim = False + self.notify.debug('%s %d okToStartNextAnim=%s' % (self, self.visId, self.okToStartNextAnim)) + self.calcLastIdleFrame() + self.clearCurIval() + + def calcLastIdleFrame(self): + if self.curIval and self.curIval.ivals: + firstIval = self.curIval.ivals[0] + if isinstance(firstIval, ActorInterval): + self.lastIdleFrame = firstIval.getCurrentFrame() + self.lastIdleAnimName = firstIval.animName + elif isinstance(firstIval, Parallel): + for testIval in firstIval.ivals: + if isinstance(firstIval, ActorInterval): + self.lastIdleTime = testIval.getT() + self.lastIdleAnimName = testIval.animName + break + + def chooseIdleAnimToRun(self): + result = self.numIdles - 1 + + if base.config.GetBool('randomize-interactive-idles', True): + pairs = [] + for i in xrange(self.numIdles): + reversedChance = self.numIdles - i - 1 + pairs.append((math.pow(2, reversedChance), i)) + + sum = math.pow(2, self.numIdles) - 1 + result = weightedChoice(pairs, sum=sum) + self.notify.debug('chooseAnimToRun numIdles=%s pairs=%s result=%s' % (self.numIdles, pairs, result)) + else: + result = self.lastPlayingAnimPhase + 1 + if result >= len(self.ZoneToIdles[self.hoodId]): + result = 0 + + return result + + def startNextIdleAnim(self): + if not hasattr(self, 'node') or not self.node: + self.notify.warning("startNextIdleAnim returning hasattr(self,'node')=%s" % hasattr(self, 'node')) + return + + self.curIval = None + + if self.okToStartNextAnim: + self.notify.debug('got pass okToStartNextAnim') + whichAnim = self.chooseIdleAnimToRun() + self.lastPlayingAnimPhase = whichAnim + self.curIval = self.createIdleAnimSequence(whichAnim) + self.notify.debug('starting curIval of length %s' % self.curIval.getDuration()) + self.curIval.start() + else: + self.curIval = Wait(3) + self.notify.debug('false self.okToStartNextAnim=%s' % self.okToStartNextAnim) + + def createIdleAnimAndSoundInterval(self, whichIdleAnim, startingTime = 0): + animIval = self.node.actorInterval('idle%d' % whichIdleAnim, startTime=startingTime) + animIvalDuration = animIval.getDuration() + origAnimName = self.ZoneToIdles[self.hoodId][whichIdleAnim] + + if isinstance(origAnimName, tuple): + origAnimName = origAnimName[0] + + soundIval = self.createSoundInterval(origAnimName, animIvalDuration) + soundIvalDuration = soundIval.getDuration() + + if self.hasSpecialIval(origAnimName): + specialIval = self.getSpecialIval(origAnimName) + return Parallel(animIval, soundIval, specialIval) + else: + return Parallel(animIval, soundIval) + + def createIdleAnimSequence(self, whichIdleAnim): + dummyResult = Sequence(Wait(self.IdlePauseTime)) + + if not hasattr(self, 'node') or not self.node: + self.notify.warning("createIdleAnimSequence returning dummyResult hasattr(self,'node')=%s" % hasattr(self, 'node')) + return dummyResult + + idleAnimAndSound = self.createIdleAnimAndSoundInterval(whichIdleAnim) + result = Sequence(idleAnimAndSound, Wait(self.IdlePauseTime), Func(self.startNextIdleAnim)) + + if isinstance(self.ZoneToIdles[self.hoodId][whichIdleAnim], tuple) and len(self.ZoneToIdles[self.hoodId][whichIdleAnim]) > 2: + info = self.ZoneToIdles[self.hoodId][whichIdleAnim] + origAnimName = info[0] + minLoop = info[1] + maxLoop = info[2] + settleAnim = info[3] + minPauseTime = info[4] + maxPauseTime = info[5] + numberOfLoops = random.randrange(minLoop, maxLoop + 1) + pauseTime = random.randrange(minPauseTime, maxPauseTime + 1) + result = Sequence() + for i in xrange(numberOfLoops): + result.append(idleAnimAndSound) + + if self.getSettleName(whichIdleAnim): + result.append(self.node.actorInterval('settle%d' % whichIdleAnim)) + result.append(Wait(pauseTime)) + result.append(Func(self.startNextIdleAnim)) + + return result + + def gotoBattleCheer(self): + self.notify.debugStateCall(self) + self.request('BattleCheer') + + def gotoIdle(self): + self.notify.debugStateCall(self) + self.request('DoIdleAnim') + + def gotoVictory(self): + self.notify.debugStateCall(self) + self.request('Victory') + + def gotoSad(self, buildingDoId): + self.notify.debugStateCall(self) + self.buildingsMakingMeSad.add(buildingDoId) + self.request('Sad') + + def buildingLiberated(self, buildingDoId): + self.buildingsMakingMeSad.discard(buildingDoId) + + if not self.buildingsMakingMeSad: + self.gotoIdle() + + def calcWhichIdleAnim(self, animName): + result = 0 + info = self.ZoneToIdles[self.hoodId] + + for index, curInfo in enumerate(info): + if isinstance(curInfo, tuple): + if curInfo[0] == animName: + result = index + break + elif isinstance(curInfo, str): + if curInfo == animName: + result = index + breal + + return result + + def enterBattleCheer(self): + self.notify.debugStateCall(self) + self.curIval = self.battleCheerInterval + + if self.curIval: + self.curIval.loop() + + def exitBattleCheer(self): + self.notify.debugStateCall(self) + + if self.curIval: + self.curIval.finish() + self.curIval = None + + def enterVictory(self): + self.notify.debugStateCall(self) + self.curIval = self.victoryInterval + + if self.curIval: + self.curIval.loop() + + def exitVictory(self): + self.notify.debugStateCall(self) + + if self.curIval: + self.curIval.finish() + self.curIval = None + + def enterSad(self): + self.notify.debugStateCall(self) + self.curIval = self.sadInterval + + if self.curIval: + self.curIval.loop() + + def exitSad(self): + self.notify.debugStateCall(self) + + if self.curIval: + self.curIval.finish() + self.curIval = None + + def getSettleName(self, whichIdleAnim): + if isinstance(self.ZoneToIdles[self.hoodId][whichIdleAnim], tuple) and len(self.ZoneToIdles[self.hoodId][whichIdleAnim]) > 3: + return self.ZoneToIdles[self.hoodId][whichIdleAnim][3] + return None + + def getOrigIdleAnimName(self, whichIdleAnim): + anim = self.ZoneToIdles[self.hoodId][whichIdleAnim] + + return anim[0] if isinstance(anim, tuple) else anim + + def createAnimAndSoundIval(self, animKey): + animIval = self.node.actorInterval(animKey) + animIvalDuration = animIval.getDuration() + origAnimName = self.node.getAnimFilename(animKey) + soundIval = self.createSoundInterval(origAnimName, animIvalDuration) + soundIvalDuration = soundIval.getDuration() + + if self.hasSpecialIval(origAnimName): + specialIval = self.getSpecialIval(origAnimName) + return Parallel(animIval, soundIval, specialIval) + else: + return Parallel(animIval, soundIval) + + def clearCurIval(self): + if self.curIval: + self.curIval.finish() + + clearPythonIvals(self.curIval) + self.curIval = None diff --git a/toontown/hood/LawbotHQ.py b/toontown/hood/LawbotHQ.py new file mode 100755 index 00000000..6544c231 --- /dev/null +++ b/toontown/hood/LawbotHQ.py @@ -0,0 +1,21 @@ +from toontown.coghq.LawbotCogHQLoader import LawbotCogHQLoader +from toontown.toonbase import ToontownGlobals +from toontown.hood.CogHood import CogHood + + +class LawbotHQ(CogHood): + notify = directNotify.newCategory('LawbotHQ') + + ID = ToontownGlobals.LawbotHQ + LOADER_CLASS = LawbotCogHQLoader + + def load(self): + CogHood.load(self) + + self.sky.hide() + + def enter(self, requestStatus): + CogHood.enter(self, requestStatus) + + base.localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + base.camLens.setNearFar(ToontownGlobals.LawbotHQCameraNear, ToontownGlobals.LawbotHQCameraFar) diff --git a/toontown/hood/LawbotHQAI.py b/toontown/hood/LawbotHQAI.py new file mode 100755 index 00000000..f91f7f07 --- /dev/null +++ b/toontown/hood/LawbotHQAI.py @@ -0,0 +1,74 @@ +from toontown.building import DistributedCJElevatorAI +from toontown.building import FADoorCodes +from toontown.building.DistributedBoardingPartyAI import DistributedBoardingPartyAI +from toontown.coghq.DistributedLawOfficeElevatorExtAI import DistributedLawOfficeElevatorExtAI +from toontown.hood import CogHQAI +from toontown.suit import DistributedLawbotBossAI +from toontown.suit import DistributedSuitPlannerAI +from toontown.toonbase import ToontownGlobals + + +class LawbotHQAI(CogHQAI.CogHQAI): + def __init__(self, air): + CogHQAI.CogHQAI.__init__( + self, air, ToontownGlobals.LawbotHQ, ToontownGlobals.LawbotLobby, + FADoorCodes.LB_DISGUISE_INCOMPLETE, + DistributedCJElevatorAI.DistributedCJElevatorAI, + DistributedLawbotBossAI.DistributedLawbotBossAI) + + self.lawOfficeElevators = [] + self.officeBoardingParty = None + self.suitPlanners = [] + + self.startup() + + def startup(self): + CogHQAI.CogHQAI.startup(self) + + self.createLawOfficeElevators() + self.makeCogHQDoor(ToontownGlobals.LawbotOfficeExt, 0, 0) + if simbase.config.GetBool('want-boarding-groups', True): + self.createOfficeBoardingParty() + if simbase.config.GetBool('want-suit-planners', True): + self.createSuitPlanners() + + def makeCogHQDoor(self, destinationZone, intDoorIndex, extDoorIndex, lock=0): + # For Lawbot HQ, the lobby door index is 1, even though that index + # should be for the Lawbot office exterior door. + if destinationZone == self.lobbyZoneId: + extDoorIndex = 1 + elif destinationZone == ToontownGlobals.LawbotOfficeExt: + extDoorIndex = 0 + + return CogHQAI.CogHQAI.makeCogHQDoor( + self, destinationZone, intDoorIndex, extDoorIndex, lock=lock) + + def createLawOfficeElevators(self): + destZones = ( + ToontownGlobals.LawbotStageIntA, + ToontownGlobals.LawbotStageIntB, + ToontownGlobals.LawbotStageIntC, + ToontownGlobals.LawbotStageIntD + ) + for i in xrange(len(destZones)): + lawOfficeElevator = DistributedLawOfficeElevatorExtAI( + self.air, self.air.lawOfficeMgr, destZones[i], i) + lawOfficeElevator.generateWithRequired( + ToontownGlobals.LawbotOfficeExt) + self.lawOfficeElevators.append(lawOfficeElevator) + + def createOfficeBoardingParty(self): + lawOfficeIdList = [] + for lawOfficeElevator in self.lawOfficeElevators: + lawOfficeIdList.append(lawOfficeElevator.doId) + self.officeBoardingParty = DistributedBoardingPartyAI( + self.air, lawOfficeIdList, 4) + self.officeBoardingParty.generateWithRequired(ToontownGlobals.LawbotOfficeExt) + + def createSuitPlanners(self): + suitPlanner = DistributedSuitPlannerAI.DistributedSuitPlannerAI(self.air, self.zoneId) + suitPlanner.generateWithRequired(self.zoneId) + suitPlanner.d_setZoneId(self.zoneId) + suitPlanner.initTasks() + self.suitPlanners.append(suitPlanner) + self.air.suitPlanners[self.zoneId] = suitPlanner diff --git a/toontown/hood/MMHood.py b/toontown/hood/MMHood.py new file mode 100755 index 00000000..6ecd61cb --- /dev/null +++ b/toontown/hood/MMHood.py @@ -0,0 +1,20 @@ +from toontown.safezone.MMSafeZoneLoader import MMSafeZoneLoader +from toontown.town.MMTownLoader import MMTownLoader +from toontown.toonbase import ToontownGlobals +from toontown.hood.ToonHood import ToonHood + + +class MMHood(ToonHood): + notify = directNotify.newCategory('MMHood') + + ID = ToontownGlobals.MinniesMelodyland + TOWNLOADER_CLASS = MMTownLoader + SAFEZONELOADER_CLASS = MMSafeZoneLoader + STORAGE_DNA = 'phase_6/dna/storage_MM.pdna' + SKY_FILE = 'phase_6/models/props/MM_sky' + SPOOKY_SKY_FILE = 'phase_6/models/props/MM_sky' + TITLE_COLOR = (1.0, 0.5, 0.5, 1.0) + + HOLIDAY_DNA = { + ToontownGlobals.CHRISTMAS: ['phase_6/dna/winter_storage_MM.pdna'], + ToontownGlobals.HALLOWEEN: ['phase_6/dna/halloween_props_storage_MM.pdna']} diff --git a/toontown/hood/MMHoodAI.py b/toontown/hood/MMHoodAI.py new file mode 100755 index 00000000..6510465c --- /dev/null +++ b/toontown/hood/MMHoodAI.py @@ -0,0 +1,36 @@ +from toontown.hood import HoodAI +from toontown.safezone import DistributedTrolleyAI +from toontown.safezone import DistributedMMPianoAI +from toontown.toonbase import ToontownGlobals +from toontown.ai import DistributedEffectMgrAI + +class MMHoodAI(HoodAI.HoodAI): + def __init__(self, air): + HoodAI.HoodAI.__init__(self, air, + ToontownGlobals.MinniesMelodyland, + ToontownGlobals.MinniesMelodyland) + + self.trolley = None + self.piano = None + + self.startup() + + def startup(self): + HoodAI.HoodAI.startup(self) + + if simbase.config.GetBool('want-minigames', True): + self.createTrolley() + + self.piano = DistributedMMPianoAI.DistributedMMPianoAI(self.air) + self.piano.generateWithRequired(self.zoneId) + + self.trickOrTreatMgr = DistributedEffectMgrAI.DistributedEffectMgrAI(self.air, ToontownGlobals.HALLOWEEN, 12) + self.trickOrTreatMgr.generateWithRequired(4835) # Ursatz for Really Kool Katz, Tenor Terrace + + self.winterCarolingMgr = DistributedEffectMgrAI.DistributedEffectMgrAI(self.air, ToontownGlobals.CHRISTMAS, 14) + self.winterCarolingMgr.generateWithRequired(4614) # Shave and a Haircut for a Song, Alto Avenue + + def createTrolley(self): + self.trolley = DistributedTrolleyAI.DistributedTrolleyAI(self.air) + self.trolley.generateWithRequired(self.zoneId) + self.trolley.start() diff --git a/toontown/hood/MailboxInteractiveProp.py b/toontown/hood/MailboxInteractiveProp.py new file mode 100755 index 00000000..9719461c --- /dev/null +++ b/toontown/hood/MailboxInteractiveProp.py @@ -0,0 +1,90 @@ +from direct.actor import Actor +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import Sequence, Func +from toontown.hood import InteractiveAnimatedProp +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals, TTLocalizer + +class MailboxInteractiveProp(InteractiveAnimatedProp.InteractiveAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('MailboxInteractiveProp') + BattleTrack = ToontownBattleGlobals.THROW_TRACK + BattleCheerText = TTLocalizer.InteractivePropTrackBonusTerms[BattleTrack] + + ZoneToIdles = { + ToontownGlobals.ToontownCentral: (('tt_a_ara_ttc_mailbox_idle0', 3, 10, 'tt_a_ara_ttc_mailbox_idle0settle', 3, 10), + ('tt_a_ara_ttc_mailbox_idleTake2', 1, 1, None, 3, 10), + ('tt_a_ara_ttc_mailbox_idleLook1', 1, 1, None, 3, 10), + ('tt_a_ara_ttc_mailbox_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.DonaldsDock: (('tt_a_ara_dod_mailbox_idle0', 3, 10, 'tt_a_ara_dod_mailbox_idle0settle', 3, 10), + ('tt_a_ara_dod_mailbox_idle2', 1, 1, None, 3, 10), + ('tt_a_ara_dod_mailbox_idle1', 1, 1, None, 3, 10), + ('tt_a_ara_dod_mailbox_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.DaisyGardens: (('tt_a_ara_dga_mailbox_idle0', 3, 10, 'tt_a_ara_dga_mailbox_idle0settle', 3, 10), + ('tt_a_ara_dga_mailbox_idleTake1', 1, 1, None, 3, 10), + ('tt_a_ara_dga_mailbox_idleLook2', 1, 1, None, 3, 10), + ('tt_a_ara_dga_mailbox_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.MinniesMelodyland: (('tt_a_ara_mml_mailbox_idle0', 3, 10, 'tt_a_ara_mml_mailbox_idle0settle', 3, 10), + ('tt_a_ara_mml_mailbox_idleTake1', 1, 1, None, 3, 10), + ('tt_a_ara_mml_mailbox_idleLook2', 1, 1, None, 3, 10), + ('tt_a_ara_mml_mailbox_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.TheBrrrgh: (('tt_a_ara_tbr_mailbox_idleShiver1', 1, 1, None, 3, 10), + ('tt_a_ara_tbr_mailbox_idleSneeze2', 1, 1, None, 3, 10), + ('tt_a_ara_tbr_mailbox_idleSpin0', 1, 1, None, 3, 10), + ('tt_a_ara_tbr_mailbox_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.DonaldsDreamland: (('tt_a_ara_ddl_mailbox_idleSleep0', 3, 10, None, 0, 0), + ('tt_a_ara_ddl_mailbox_idleShake2', 1, 1, None, 0, 0), + ('tt_a_ara_ddl_mailbox_idleSnore1', 1, 1, None, 0, 0), + ('tt_a_ara_ddl_mailbox_idleAwesome3', 1, 1, None, 0, 0))} + + ZoneToIdleIntoFightAnims = { + ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_mailbox_idleIntoFight', + ToontownGlobals.DonaldsDock: 'tt_a_ara_dod_mailbox_idleIntoFight', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_mailbox_idleIntoFight', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_mailbox_idleIntoFight', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_mailbox_idleIntoFight', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_mailbox_idleIntoFight'} + + ZoneToVictoryAnims = { + ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_mailbox_victoryDance', + ToontownGlobals.DonaldsDock: 'tt_a_ara_dod_mailbox_victoryDance', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_mailbox_victoryDance', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_mailbox_victoryDance', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_mailbox_victoryDance', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_mailbox_victoryDance'} + + ZoneToSadAnims = { + ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_mailbox_fightSad', + ToontownGlobals.DonaldsDock: 'tt_a_ara_dod_mailbox_fightSad', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_mailbox_fightSad', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_mailbox_fightSad', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_mailbox_fightSad', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_mailbox_fightSad'} + + ZoneToFightAnims = { + ToontownGlobals.ToontownCentral: ('tt_a_ara_ttc_mailbox_fightBoost', 'tt_a_ara_ttc_mailbox_fightCheer', 'tt_a_ara_ttc_mailbox_fightIdle'), + ToontownGlobals.DonaldsDock: ('tt_a_ara_dod_mailbox_fightBoost', 'tt_a_ara_dod_mailbox_fightCheer', 'tt_a_ara_dod_mailbox_fightIdle'), + ToontownGlobals.DaisyGardens: ('tt_a_ara_dga_mailbox_fightBoost', 'tt_a_ara_dga_mailbox_fightCheer', 'tt_a_ara_dga_mailbox_fightIdle'), + ToontownGlobals.MinniesMelodyland: ('tt_a_ara_mml_mailbox_fightBoost', 'tt_a_ara_mml_mailbox_fightCheer', 'tt_a_ara_mml_mailbox_fightIdle'), + ToontownGlobals.TheBrrrgh: ('tt_a_ara_tbr_mailbox_fightBoost', 'tt_a_ara_tbr_mailbox_fightCheer', 'tt_a_ara_tbr_mailbox_fightIdle'), + ToontownGlobals.DonaldsDreamland: ('tt_a_ara_ddl_mailbox_fightBoost', 'tt_a_ara_ddl_mailbox_fightCheer', 'tt_a_ara_ddl_mailbox_fightIdle')} + + IdlePauseTime = base.config.GetFloat('prop-idle-pause-time', 0.0) + + def setupActor(self, node): + self.pieActor = Actor.Actor('phase_5/models/char/tt_r_prp_ext_piePackage', {'fightBoost': 'phase_5/models/char/tt_a_prp_ext_piePackage_fightBoost'}) + self.pieActor.reparentTo(self.node) + self.pieActor.hide() + + InteractiveAnimatedProp.InteractiveAnimatedProp.setupActor(self, node) + + def hasSpecialIval(self, origAnimName): + return 'fightBoost' in origAnimName + + def getSpecialIval(self, origAnimName): + result = Sequence() + + if self.hasSpecialIval(origAnimName): + result.append(Func(self.pieActor.show)) + result.append(self.pieActor.actorInterval('fightBoost')) + result.append(Func(self.pieActor.hide)) + + return result diff --git a/toontown/hood/OZHood.py b/toontown/hood/OZHood.py new file mode 100755 index 00000000..49cedff2 --- /dev/null +++ b/toontown/hood/OZHood.py @@ -0,0 +1,32 @@ +from pandac.PandaModules import Vec4 +from toontown.safezone.OZSafeZoneLoader import OZSafeZoneLoader +from toontown.toonbase import ToontownGlobals +from toontown.hood.ToonHood import ToonHood + +class OZHood(ToonHood): + notify = directNotify.newCategory('OZHood') + + ID = ToontownGlobals.OutdoorZone + SAFEZONELOADER_CLASS = OZSafeZoneLoader + STORAGE_DNA = 'phase_6/dna/storage_OZ.pdna' + SKY_FILE = 'phase_3.5/models/props/TT_sky' + SPOOKY_SKY_FILE = 'phase_3.5/models/props/BR_sky' + TITLE_COLOR = (1.0, 0.5, 0.4, 1.0) + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + ToonHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + + def load(self): + ToonHood.load(self) + + self.fog = Fog('OZFog') + + def enter(self, requestStatus): + ToonHood.enter(self, requestStatus) + + base.camLens.setNearFar(ToontownGlobals.SpeedwayCameraNear, ToontownGlobals.SpeedwayCameraFar) + + def exit(self): + base.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar) + + ToonHood.exit(self) diff --git a/toontown/hood/OZHoodAI.py b/toontown/hood/OZHoodAI.py new file mode 100755 index 00000000..af543d7e --- /dev/null +++ b/toontown/hood/OZHoodAI.py @@ -0,0 +1,102 @@ +from toontown.hood import HoodAI +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from toontown.distributed.DistributedTimerAI import DistributedTimerAI +import string +from toontown.dna.DNAParser import DNAGroup, DNAVisGroup +from toontown.safezone.DistributedPicnicBasketAI import DistributedPicnicBasketAI +from toontown.safezone import DistributedPicnicTableAI +from toontown.safezone import DistributedChineseCheckersAI +from toontown.safezone import DistributedCheckersAI +from toontown.hood import ZoneUtil +import random + + +class OZHoodAI(HoodAI.HoodAI): + def __init__(self, air): + HoodAI.HoodAI.__init__(self, air, + ToontownGlobals.OutdoorZone, + ToontownGlobals.OutdoorZone) + + self.timer = None + self.picnicTables = [] + self.gameTables = [] + + self.startup() + + def startup(self): + HoodAI.HoodAI.startup(self) + + self.createTimer() + self.createPicnicTables() + if simbase.config.GetBool('want-game-tables', True): + self.createGameTables() + + def createTimer(self): + self.timer = DistributedTimerAI(self.air) + self.timer.generateWithRequired(self.zoneId) + + def findPicnicTables(self, dnaGroup, zoneId, area, overrideDNAZone=False): + picnicTables = [] + if isinstance(dnaGroup, DNAGroup) and ('picnic_table' in dnaGroup.getName()): + nameInfo = dnaGroup.getName().split('_') + for i in xrange(dnaGroup.getNumChildren()): + childDnaGroup = dnaGroup.at(i) + if 'picnic_table' in childDnaGroup.getName(): + pos = childDnaGroup.getPos() + hpr = childDnaGroup.getHpr() + picnicTable = DistributedPicnicBasketAI( + simbase.air, nameInfo[2], + pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2]) + picnicTable.generateWithRequired(zoneId) + picnicTables.append(picnicTable) + elif isinstance(dnaGroup, DNAVisGroup) and (not overrideDNAZone): + zoneId = int(dnaGroup.getName().split(':')[0]) + for i in xrange(dnaGroup.getNumChildren()): + foundPicnicTables = self.findPicnicTables( + dnaGroup.at(i), zoneId, area, overrideDNAZone=overrideDNAZone) + picnicTables.extend(foundPicnicTables) + return picnicTables + + def createPicnicTables(self): + self.picnicTables = [] + for zoneId in self.getZoneTable(): + dnaData = self.air.dnaDataMap.get(zoneId, None) + if dnaData.getName() == 'root': + area = ZoneUtil.getCanonicalZoneId(zoneId) + foundPicnicTables = self.findPicnicTables( + dnaData, zoneId, area, overrideDNAZone=True) + self.picnicTables.extend(foundPicnicTables) + for picnicTable in self.picnicTables: + picnicTable.start() + + def findGameTables(self, dnaGroup, zoneId, area, overrideDNAZone=False): + gameTables = [] + if isinstance(dnaGroup, DNAGroup) and ('game_table' in dnaGroup.getName()): + for i in xrange(dnaGroup.getNumChildren()): + childDnaGroup = dnaGroup.at(i) + if 'game_table' in childDnaGroup.getName(): + pos = childDnaGroup.getPos() + hpr = childDnaGroup.getHpr() + nameInfo = childDnaGroup.getName().split('_') + tableIndex = int(childDnaGroup.get_parent().getName().split('_')[-1]) + gameTable = DistributedPicnicTableAI.DistributedPicnicTableAI(simbase.air, zoneId, nameInfo[2], pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2]) + gameTable.setTableIndex(tableIndex) + gameTable.generateOtpObject(simbase.air.districtId, zoneId, ['setX', 'setY', 'setZ', 'setH', 'setP', 'setR']) + elif isinstance(dnaGroup, DNAVisGroup) and (not overrideDNAZone): + zoneId = int(dnaGroup.getName().split(':')[0]) + for i in xrange(dnaGroup.getNumChildren()): + foundGameTables = self.findGameTables( + dnaGroup.at(i), zoneId, area, overrideDNAZone=overrideDNAZone) + gameTables.extend(foundGameTables) + return gameTables + + def createGameTables(self): + self.gameTables = [] + for zoneId in self.getZoneTable(): + dnaData = self.air.dnaDataMap.get(zoneId, None) + if dnaData.getName() == 'root': + area = ZoneUtil.getCanonicalZoneId(zoneId) + foundGameTables = self.findGameTables( + dnaData, zoneId, area, overrideDNAZone=True) + self.gameTables.extend(foundGameTables) diff --git a/toontown/hood/PartyHood.py b/toontown/hood/PartyHood.py new file mode 100755 index 00000000..669644b1 --- /dev/null +++ b/toontown/hood/PartyHood.py @@ -0,0 +1,116 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.task.Task import Task +from panda3d.core import * +from otp.avatar import DistributedAvatar +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.distributed.ToontownMsgTypes import * +from toontown.minigame import Purchase +from toontown.parties import PartyLoader, PartyGlobals +from toontown.hood import Hood, ZoneUtil +from toontown.safezone import SZUtil + +class PartyHood(Hood.Hood): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyHood') + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + Hood.Hood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.fsm = ClassicFSM.ClassicFSM('Hood', [State.State('start', self.enterStart, self.exitStart, ['safeZoneLoader']), + State.State('safeZoneLoader', self.enterSafeZoneLoader, self.exitSafeZoneLoader, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['safeZoneLoader']), + State.State('final', self.enterFinal, self.exitFinal, [])], 'start', 'final') + self.fsm.enterInitialState() + self.id = PartyHood + self.safeZoneLoaderClass = PartyLoader.PartyLoader + self.partyActivityDoneEvent = 'partyActivityDone' + self.storageDNAFile = 'phase_13/dna/storage_party_sz.pdna' + self.holidayStorageDNADict = {CHRISTMAS: ['phase_5.5/dna/winter_storage_estate.pdna']} + self.skyFile = 'phase_3.5/models/props/TT_sky' + self.popupInfo = None + + def load(self): + Hood.Hood.load(self) + + def unload(self): + del self.safeZoneLoaderClass + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + Hood.Hood.unload(self) + return + + def enter(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + self.accept(PartyGlobals.KICK_TO_PLAYGROUND_EVENT, self.kickToPlayground) + self.fsm.request(requestStatus['loader'], [requestStatus]) + + def exit(self): + if self.loader: + self.loader.exit() + self.loader.unload() + del self.loader + Hood.Hood.exit(self) + + def kickToPlayground(self, retCode): + if retCode == 0: + msg = TTLocalizer.PartyOverWarningNoName + if hasattr(base, 'distributedParty') and base.distributedParty: + name = base.distributedParty.hostName + msg = TTLocalizer.PartyOverWarningWithName % TTLocalizer.GetPossesive(name) + self.__popupKickoutMessage(msg) + base.localAvatar.setTeleportAvailable(0) + if retCode == 1: + zoneId = base.localAvatar.lastHood + self.doneStatus = {'loader': ZoneUtil.getBranchLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1} + messenger.send(self.doneEvent) + return + + def __popupKickoutMessage(self, msg): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = None + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=msg, frameSize=(-1, 1, -1, 1), text_wordwrap=10, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(0.88, 1, 0.75), geom_pos=(0, 0, -.08), text_scale=0.08, text_pos=(0, 0.1)) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.EstatePopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.3), command=self.__handleKickoutOk) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + return + + def __handleKickoutOk(self): + self.popupInfo.reparentTo(hidden) + + def handlePartyActivityDone(self): + return None + + def loadLoader(self, requestStatus): + loaderName = requestStatus['loader'] + if loaderName == 'safeZoneLoader': + self.loader = self.safeZoneLoaderClass(self, self.fsm.getStateNamed('safeZoneLoader'), self.loaderDoneEvent) + self.loader.load() + + def spawnTitleText(self, zoneId): + pass + + def hideTitleTextTask(self, task): + pass + + def skyTrack(self, task): + return SZUtil.cloudSkyTrack(task) + + def startSky(self): + SZUtil.startCloudSky(self) + if base.cloudPlatformsEnabled: + self.loader.startCloudPlatforms() + + def stopSky(self): + Hood.Hood.stopSky(self) + self.loader.stopCloudPlatforms() diff --git a/toontown/hood/PetShopFishAnimatedProp.py b/toontown/hood/PetShopFishAnimatedProp.py new file mode 100755 index 00000000..c4cab527 --- /dev/null +++ b/toontown/hood/PetShopFishAnimatedProp.py @@ -0,0 +1,28 @@ +import AnimatedProp +from direct.actor import Actor +from direct.interval.IntervalGlobal import * + +class PetShopFishAnimatedProp(AnimatedProp.AnimatedProp): + + def __init__(self, node): + AnimatedProp.AnimatedProp.__init__(self, node) + parent = node.getParent() + self.fish = Actor.Actor(node, copy=0) + self.fish.reparentTo(parent) + self.fish.loadAnims({'swim': 'phase_4/models/props/exteriorfish-swim'}) + self.fish.pose('swim', 0) + self.node = self.fish + + def delete(self): + AnimatedProp.AnimatedProp.delete(self) + self.fish.cleanup() + del self.fish + del self.node + + def enter(self): + AnimatedProp.AnimatedProp.enter(self) + self.fish.loop('swim') + + def exit(self): + AnimatedProp.AnimatedProp.exit(self) + self.fish.stop() diff --git a/toontown/hood/Place.py b/toontown/hood/Place.py new file mode 100755 index 00000000..c501bb8c --- /dev/null +++ b/toontown/hood/Place.py @@ -0,0 +1,822 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from toontown.toonbase.PythonUtil import PriorityCallbacks +from toontown.safezone import PublicWalk +import ZoneUtil +from toontown.friends import FriendsListManager +from toontown.toonbase import ToontownGlobals +from toontown.toon.Toon import teleportDebug +from toontown.estate import HouseGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +from otp.avatar import Emote +from otp.avatar.Avatar import teleportNotify +from direct.task import Task +import QuietZoneState +from toontown.distributed import ToontownDistrictStats + +class Place(StateData.StateData, FriendsListManager.FriendsListManager): + notify = DirectNotifyGlobal.directNotify.newCategory('Place') + + def __init__(self, loader, doneEvent): + StateData.StateData.__init__(self, doneEvent) + FriendsListManager.FriendsListManager.__init__(self) + self.loader = loader + self.zoneId = None + self._tiToken = None + self._leftQuietZoneLocalCallbacks = PriorityCallbacks() + self._leftQuietZoneSubframeCall = None + self._setZoneCompleteLocalCallbacks = PriorityCallbacks() + self._setZoneCompleteSubframeCall = None + return + + def load(self): + StateData.StateData.load(self) + FriendsListManager.FriendsListManager.load(self) + self.walkDoneEvent = 'walkDone' + self.walkStateData = PublicWalk.PublicWalk(self.fsm, self.walkDoneEvent) + self.walkStateData.load() + self._tempFSM = self.fsm + + def unload(self): + StateData.StateData.unload(self) + FriendsListManager.FriendsListManager.unload(self) + self.notify.info('Unloading Place (%s). Fsm in %s' % (self.zoneId, self._tempFSM.getCurrentState().getName())) + if self._leftQuietZoneSubframeCall: + self._leftQuietZoneSubframeCall.cleanup() + self._leftQuietZoneSubframeCall = None + if self._setZoneCompleteSubframeCall: + self._setZoneCompleteSubframeCall.cleanup() + self._setZoneCompleteSubframeCall = None + self._leftQuietZoneLocalCallbacks = None + self._setZoneCompleteLocalCallbacks = None + del self._tempFSM + taskMgr.remove('goHomeFailed') + del self.walkDoneEvent + self.walkStateData.unload() + del self.walkStateData + del self.loader + return + + def _getQZState(self): + if hasattr(base, 'cr') and hasattr(base.cr, 'playGame'): + if hasattr(base.cr.playGame, 'quietZoneStateData') and base.cr.playGame.quietZoneStateData: + return base.cr.playGame.quietZoneStateData + return None + + def addLeftQuietZoneCallback(self, callback, priority = None): + qzsd = self._getQZState() + if qzsd: + return qzsd.addLeftQuietZoneCallback(callback, priority) + else: + token = self._leftQuietZoneLocalCallbacks.add(callback, priority=priority) + if not self._leftQuietZoneSubframeCall: + self._leftQuietZoneSubframeCall = SubframeCall(self._doLeftQuietZoneCallbacks, taskMgr.getCurrentTask().getPriority() - 1) + return token + + def removeLeftQuietZoneCallback(self, token): + if token is not None: + if token in self._leftQuietZoneLocalCallbacks: + self._leftQuietZoneLocalCallbacks.remove(token) + qzsd = self._getQZState() + if qzsd: + qzsd.removeLeftQuietZoneCallback(token) + return + + def _doLeftQuietZoneCallbacks(self): + self._leftQuietZoneLocalCallbacks() + self._leftQuietZoneLocalCallbacks.clear() + self._leftQuietZoneSubframeCall = None + return + + def addSetZoneCompleteCallback(self, callback, priority = None): + qzsd = self._getQZState() + if qzsd: + return qzsd.addSetZoneCompleteCallback(callback, priority) + else: + token = self._setZoneCompleteLocalCallbacks.add(callback, priority=priority) + if not self._setZoneCompleteSubframeCall: + self._setZoneCompleteSubframeCall = SubframeCall(self._doSetZoneCompleteLocalCallbacks, taskMgr.getCurrentTask().getPriority() - 1) + return token + + def removeSetZoneCompleteCallback(self, token): + if token is not None: + if any(token==x[1] for x in self._setZoneCompleteLocalCallbacks._callbacks): + self._setZoneCompleteLocalCallbacks.remove(token) + qzsd = self._getQZState() + if qzsd: + qzsd.removeSetZoneCompleteCallback(token) + return + + def _doSetZoneCompleteLocalCallbacks(self): + self._setZoneCompleteSubframeCall = None + localCallbacks = self._setZoneCompleteLocalCallbacks + self._setZoneCompleteLocalCallbacks() + localCallbacks.clear() + return + + def setState(self, state): + if hasattr(self, 'fsm'): + curState = self.fsm.getName() + if state == 'pet' or curState == 'pet': + self.preserveFriendsList() + self.fsm.request(state) + + def getState(self): + if hasattr(self, 'fsm'): + curState = self.fsm.getCurrentState().getName() + return curState + + def getZoneId(self): + return self.zoneId + + def getTaskZoneId(self): + return self.getZoneId() + + def handleTeleportQuery(self, fromAvatar, toAvatar): + if toAvatar == localAvatar: + toAvatar.doTeleportResponse(fromAvatar, toAvatar, toAvatar.doId, 1, toAvatar.defaultShard, base.cr.playGame.getPlaceId(), self.getZoneId(), fromAvatar.doId) + else: + self.notify.warning('handleTeleportQuery toAvatar.doId != localAvatar.doId' % (toAvatar.doId, localAvatar.doId)) + + def detectedPhoneCollision(self): + self.fsm.request('phone') + + def detectedFishingCollision(self): + self.fsm.request('fishing') + + def enterStart(self): + pass + + def exitStart(self): + pass + + def enterFinal(self): + pass + + def exitFinal(self): + pass + + def enterWalk(self, teleportIn = 0): + self.enterFLM() + self.walkStateData.enter() + if teleportIn == 0: + self.walkStateData.fsm.request('walking') + self.acceptOnce(self.walkDoneEvent, self.handleWalkDone) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.questPage.acceptOnscreenHooks() + base.localAvatar.invPage.acceptOnscreenHooks() + base.localAvatar.questMap.acceptOnscreenHooks() + self.walkStateData.fsm.request('walking') + + def exitWalk(self): + self.exitFLM() + messenger.send('wakeup') + self.walkStateData.exit() + self.ignore(self.walkDoneEvent) + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + if base.cr.playGame.hood != None: + base.cr.playGame.hood.hideTitleText() + base.localAvatar.questPage.hideQuestsOnscreen() + base.localAvatar.questPage.ignoreOnscreenHooks() + base.localAvatar.invPage.ignoreOnscreenHooks() + base.localAvatar.invPage.hideInventoryOnscreen() + base.localAvatar.questMap.hide() + base.localAvatar.questMap.ignoreOnscreenHooks() + + def handleWalkDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'StickerBook': + self.last = self.fsm.getCurrentState().getName() + self.fsm.request('stickerBook') + elif mode == 'Options': + self.last = self.fsm.getCurrentState().getName() + self.fsm.request('stickerBook', [base.localAvatar.optionsPage]) + elif mode == 'Sit': + self.last = self.fsm.getCurrentState().getName() + self.fsm.request('sit') + else: + Place.notify.error('Invalid mode: %s' % mode) + + def enterSit(self): + self.enterFLM() + base.localAvatar.laffMeter.start() + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.b_setAnimState('SitStart', 1) + self.accept('arrow_up', self.fsm.request, extraArgs=['walk']) + + def exitSit(self): + self.exitFLM() + base.localAvatar.laffMeter.stop() + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + self.ignore('arrow_up') + + def enterDrive(self): + self.enterFLM() + base.localAvatar.laffMeter.start() + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.b_setAnimState('SitStart', 1) + + def exitDrive(self): + self.exitFLM() + base.localAvatar.laffMeter.stop() + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + + def enterPush(self): + self.enterFLM() + base.localAvatar.laffMeter.start() + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.attachCamera() + base.localAvatar.startUpdateSmartCamera() + base.localAvatar.startPosHprBroadcast() + base.localAvatar.b_setAnimState('Push', 1) + + def exitPush(self): + self.exitFLM() + base.localAvatar.laffMeter.stop() + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.detachCamera() + base.localAvatar.stopPosHprBroadcast() + self.ignore('teleportQuery') + + def enterStickerBook(self, page = None): + self.enterFLM() + base.localAvatar.laffMeter.start() + target = base.cr.doFind('DistributedTarget') + if target: + target.hideGui() + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + if page: + base.localAvatar.book.setPage(page) + base.localAvatar.b_setAnimState('OpenBook', 1, self.enterStickerBookGUI) + base.localAvatar.obscureMoveFurnitureButton(1) + + def enterStickerBookGUI(self): + base.localAvatar.collisionsOn() + base.localAvatar.book.showButton() + base.localAvatar.book.enter() + base.localAvatar.setGuiConflict(1) + base.localAvatar.startSleepWatch(self.__handleFallingAsleep) + self.accept('bookDone', self.__handleBook) + base.localAvatar.b_setAnimState('ReadBook', 1) + + def __handleFallingAsleep(self, task): + base.localAvatar.book.exit() + base.localAvatar.b_setAnimState('CloseBook', 1, callback=self.__handleFallingAsleepBookClose) + return Task.done + + def __handleFallingAsleepBookClose(self): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + base.localAvatar.forceGotoSleep() + + def exitStickerBook(self): + base.localAvatar.stopSleepWatch() + self.exitFLM() + base.localAvatar.laffMeter.stop() + base.localAvatar.setGuiConflict(0) + base.localAvatar.book.exit() + base.localAvatar.book.hideButton() + base.localAvatar.collisionsOff() + self.ignore('bookDone') + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.obscureMoveFurnitureButton(0) + target = base.cr.doFind('DistributedTarget') + if target: + target.showGui() + + def __handleBook(self): + base.localAvatar.stopSleepWatch() + base.localAvatar.book.exit() + bookStatus = base.localAvatar.book.getDoneStatus() + if bookStatus['mode'] == 'close': + base.localAvatar.b_setAnimState('CloseBook', 1, callback=self.handleBookClose) + elif bookStatus['mode'] == 'teleport': + zoneId = bookStatus['hood'] + base.localAvatar.collisionsOff() + base.localAvatar.b_setAnimState('CloseBook', 1, callback=self.handleBookCloseTeleport, extraArgs=[zoneId, zoneId]) + elif bookStatus['mode'] == 'exit': + self.exitTo = bookStatus.get('exitTo') + base.localAvatar.collisionsOff() + base.localAvatar.b_setAnimState('CloseBook', 1, callback=self.__handleBookCloseExit) + elif bookStatus['mode'] == 'gohome': + zoneId = bookStatus['hood'] + base.localAvatar.collisionsOff() + base.localAvatar.b_setAnimState('CloseBook', 1, callback=self.goHomeNow, extraArgs=[zoneId]) + elif bookStatus['mode'] == 'startparty': + firstStart = bookStatus['firstStart'] + hostId = bookStatus['hostId'] + base.localAvatar.collisionsOff() + base.localAvatar.b_setAnimState('CloseBook', 1, callback=self.startPartyNow, extraArgs=[firstStart, hostId]) + + def handleBookCloseTeleport(self, hoodId, zoneId): + if localAvatar.hasActiveBoardingGroup(): + rejectText = TTLocalizer.BoardingCannotLeaveZone + localAvatar.elevatorNotifier.showMe(rejectText) + return + self.requestLeave({'loader': ZoneUtil.getBranchLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1}) + return + + def __handleBookCloseExit(self): + base.localAvatar.b_setAnimState('TeleportOut', 1, self.__handleBookExitTeleport, [0]) + + def __handleBookExitTeleport(self, requestStatus): + if base.cr.timeManager: + base.cr.timeManager.setDisconnectReason(ToontownGlobals.DisconnectBookExit) + base.transitions.fadeScreen(1.0) + base.cr.gameFSM.request(self.exitTo) + + def goHomeNow(self, curZoneId): + if localAvatar.hasActiveBoardingGroup(): + rejectText = TTLocalizer.BoardingCannotLeaveZone + localAvatar.elevatorNotifier.showMe(rejectText) + return + hoodId = ToontownGlobals.MyEstate + self.requestLeave({'loader': 'safeZoneLoader', + 'where': 'estate', + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': -1, + 'shardId': None, + 'avId': -1}) + return + + def startPartyNow(self, firstStart, hostId): + if localAvatar.hasActiveBoardingGroup(): + rejectText = TTLocalizer.BoardingCannotLeaveZone + localAvatar.elevatorNotifier.showMe(rejectText) + return + base.localAvatar.creatingNewPartyWithMagicWord = False + base.localAvatar.aboutToPlanParty = False + hoodId = ToontownGlobals.PartyHood + if firstStart: + zoneId = 0 + ToontownDistrictStats.refresh('shardInfoUpdated') + curShardTuples = base.cr.listActiveShards() + lowestPop = 100000000000000000L + shardId = None + for shardInfo in curShardTuples: + pop = shardInfo[2] + if pop < lowestPop: + lowestPop = pop + shardId = shardInfo[0] + + if shardId == base.localAvatar.defaultShard: + shardId = None + base.cr.playGame.getPlace().requestLeave({'loader': 'safeZoneLoader', + 'where': 'party', + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None, # ALPHA BANDAGE: should be shardId, but this causes the AI it teleports to to die right now. + 'avId': -1}) + else: + if hostId is None: + hostId = base.localAvatar.doId + base.cr.partyManager.sendAvatarToParty(hostId) + return + return + + def handleBookClose(self): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + if hasattr(self, 'toonSubmerged') and self.toonSubmerged == 1: + if hasattr(self, 'walkStateData'): + self.walkStateData.fsm.request('swimming', [self.loader.swimSound]) + + def requestLeave(self, requestStatus): + teleportDebug(requestStatus, 'requestLeave(%s)' % (requestStatus,)) + if hasattr(self, 'fsm'): + self.doRequestLeave(requestStatus) + + def handleEnterTunnel(self, requestStatus, collEntry): + if localAvatar.hasActiveBoardingGroup(): + rejectText = TTLocalizer.BoardingCannotLeaveZone + localAvatar.elevatorNotifier.showMe(rejectText) + dummyNP = NodePath('dummyNP') + dummyNP.reparentTo(render) + tunnelOrigin = requestStatus['tunnelOrigin'] + dummyNP.setPos(localAvatar.getPos()) + dummyNP.setH(tunnelOrigin.getH()) + dummyNP.setPos(dummyNP, 0, 4, 0) + localAvatar.setPos(dummyNP.getPos()) + dummyNP.removeNode() + del dummyNP + return + self.requestLeave(requestStatus) + + def doRequestLeave(self, requestStatus): + if requestStatus.get('tutorial', 0): + out = {'teleportIn': 'tunnelOut'} + requestStatus['zoneId'] = 2000 + requestStatus['hoodId'] = 2000 + else: + out = {'teleportIn': 'teleportOut', + 'tunnelIn': 'tunnelOut', + 'doorIn': 'doorOut'} + self.fsm.request(out[requestStatus['how']], [requestStatus]) + + def enterDoorIn(self, requestStatus): + NametagGlobals.setMasterArrowsOn(0) + door = base.cr.doId2do.get(requestStatus['doorDoId']) + if not door is None: + door.readyToExit() + base.localAvatar.obscureMoveFurnitureButton(1) + base.localAvatar.startQuestMap() + + def exitDoorIn(self): + NametagGlobals.setMasterArrowsOn(1) + base.localAvatar.obscureMoveFurnitureButton(0) + + def enterDoorOut(self): + base.localAvatar.obscureMoveFurnitureButton(1) + + def exitDoorOut(self): + base.localAvatar.obscureMoveFurnitureButton(0) + base.localAvatar.stopQuestMap() + + def handleDoorDoneEvent(self, requestStatus): + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def handleDoorTrigger(self): + self.fsm.request('doorOut') + + def enterTunnelIn(self, requestStatus): + self.notify.debug('enterTunnelIn(requestStatus=' + str(requestStatus) + ')') + tunnelOrigin = base.render.find(requestStatus['tunnelName']) + self.accept('tunnelInMovieDone', self.__tunnelInMovieDone) + base.localAvatar.reconsiderCheesyEffect() + base.localAvatar.tunnelIn(tunnelOrigin) + base.localAvatar.startQuestMap() + + def __tunnelInMovieDone(self): + self.ignore('tunnelInMovieDone') + self.fsm.request('walk') + + def exitTunnelIn(self): + pass + + def enterTunnelOut(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + how = requestStatus['how'] + tunnelOrigin = requestStatus['tunnelOrigin'] + fromZoneId = ZoneUtil.getCanonicalZoneId(self.getZoneId()) + tunnelName = requestStatus.get('tunnelName') + if tunnelName == None: + tunnelName = base.cr.hoodMgr.makeLinkTunnelName(self.loader.hood.id, fromZoneId) + self.doneStatus = {'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': how, + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None, + 'tunnelName': tunnelName} + self.accept('tunnelOutMovieDone', self.__tunnelOutMovieDone) + base.localAvatar.tunnelOut(tunnelOrigin) + base.localAvatar.stopQuestMap() + + def __tunnelOutMovieDone(self): + self.ignore('tunnelOutMovieDone') + messenger.send(self.doneEvent) + + def exitTunnelOut(self): + pass + + def enterTeleportOut(self, requestStatus, callback): + base.localAvatar.laffMeter.start() + base.localAvatar.b_setAnimState('TeleportOut', 1, callback, [requestStatus]) + base.localAvatar.obscureMoveFurnitureButton(1) + + def exitTeleportOut(self): + base.localAvatar.laffMeter.stop() + base.localAvatar.stopQuestMap() + base.localAvatar.obscureMoveFurnitureButton(0) + + def enterDied(self, requestStatus, callback = None): + if callback == None: + callback = self.__diedDone + base.localAvatar.laffMeter.start() + camera.wrtReparentTo(render) + base.localAvatar.b_setAnimState('Died', 1, callback, [requestStatus]) + base.localAvatar.obscureMoveFurnitureButton(1) + return + + def __diedDone(self, requestStatus): + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def exitDied(self): + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(0) + + def getEstateZoneAndGoHome(self, requestStatus): + self.doneStatus = requestStatus + avId = requestStatus['avId'] + self.acceptOnce('setLocalEstateZone', self.goHome) + if avId > 0: + base.cr.estateMgr.getLocalEstateZone(avId) + else: + base.cr.estateMgr.getLocalEstateZone(base.localAvatar.getDoId()) + if HouseGlobals.WANT_TELEPORT_TIMEOUT: + taskMgr.doMethodLater(HouseGlobals.TELEPORT_TIMEOUT, self.goHomeFailed, 'goHomeFailed') + + def goHome(self, ownerId, zoneId): + self.notify.debug('goHome ownerId = %s' % ownerId) + taskMgr.remove('goHomeFailed') + if ownerId > 0 and ownerId != base.localAvatar.doId and not base.cr.isFriend(ownerId): + self.doneStatus['failed'] = 1 + self.goHomeFailed(None) + return + if ownerId == 0 and zoneId == 0: + if self.doneStatus['shardId'] is None or self.doneStatus['shardId'] is base.localAvatar.defaultShard: + self.doneStatus['failed'] = 1 + self.goHomeFailed(None) + return + else: + self.doneStatus['hood'] = ToontownGlobals.MyEstate + self.doneStatus['zone'] = base.localAvatar.lastHood + self.doneStatus['loaderId'] = 'safeZoneLoader' + self.doneStatus['whereId'] = 'estate' + self.doneStatus['how'] = 'teleportIn' + messenger.send(self.doneEvent) + return + if self.doneStatus['zoneId'] == -1: + self.doneStatus['zoneId'] = zoneId + elif self.doneStatus['zoneId'] != zoneId: + self.doneStatus['where'] = 'house' + self.doneStatus['ownerId'] = ownerId + messenger.send(self.doneEvent) + messenger.send('localToonLeft') + return + + def goHomeFailed(self, task): + self.notify.debug('goHomeFailed') + self.notifyUserGoHomeFailed() + self.ignore('setLocalEstateZone') + self.doneStatus['hood'] = base.localAvatar.lastHood + self.doneStatus['zone'] = base.localAvatar.lastHood + self.fsm.request('teleportIn', [self.doneStatus]) + return Task.done + + def notifyUserGoHomeFailed(self): + self.notify.debug('notifyUserGoHomeFailed') + failedToVisitAvId = self.doneStatus.get('avId', -1) + avName = None + if failedToVisitAvId != -1: + avatar = base.cr.identifyAvatar(failedToVisitAvId) + if avatar: + avName = avatar.getName() + if avName: + message = TTLocalizer.EstateTeleportFailedNotFriends % avName + else: + message = TTLocalizer.EstateTeleportFailed + base.localAvatar.setSystemMessage(0, message) + return + + def enterTeleportIn(self, requestStatus): + self._tiToken = self.addSetZoneCompleteCallback(Functor(self._placeTeleportInPostZoneComplete, requestStatus), 100) + + def _placeTeleportInPostZoneComplete(self, requestStatus): + teleportDebug(requestStatus, '_placeTeleportInPostZoneComplete(%s)' % (requestStatus,)) + NametagGlobals.setMasterArrowsOn(0) + base.localAvatar.laffMeter.start() + base.localAvatar.startQuestMap() + base.localAvatar.reconsiderCheesyEffect() + base.localAvatar.obscureMoveFurnitureButton(1) + avId = requestStatus.get('avId', -1) + if avId != -1: + if avId in base.cr.doId2do: + teleportDebug(requestStatus, 'teleport to avatar') + avatar = base.cr.doId2do[avId] + avatar.forceToTruePosition() + base.localAvatar.gotoNode(avatar) + base.localAvatar.b_teleportGreeting(avId) + else: + friend = base.cr.identifyAvatar(avId) + if friend == None: + teleportDebug(requestStatus, 'friend not here, giving up') + base.localAvatar.setSystemMessage(avId, OTPLocalizer.WhisperTargetLeftVisit % (friend.getName(),)) + friend.d_teleportGiveup(base.localAvatar.doId) + else: + def doTeleport(task): + avatar = base.cr.doId2do[friend.getDoId()] + base.localAvatar.gotoNode(avatar) + base.localAvatar.b_teleportGreeting(friend.getDoId()) + return task.done + self.acceptOnce('generate-%d' % friend.getDoId(), lambda x: taskMgr.doMethodLater(1, doTeleport, uniqueName('doTeleport'))) + base.transitions.irisIn() + self.nextState = requestStatus.get('nextState', 'walk') + base.localAvatar.attachCamera() + base.localAvatar.startUpdateSmartCamera() + base.localAvatar.startPosHprBroadcast() + globalClock.tick() + base.localAvatar.b_setAnimState('TeleportIn', 1, callback=self.teleportInDone) + base.localAvatar.d_broadcastPositionNow() + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + return + + def teleportInDone(self): + if hasattr(self, 'fsm'): + teleportNotify.debug('teleportInDone: %s' % self.nextState) + self.fsm.request(self.nextState, [1]) + + def exitTeleportIn(self): + self.removeSetZoneCompleteCallback(self._tiToken) + self._tiToken = None + NametagGlobals.setMasterArrowsOn(1) + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(0) + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.detachCamera() + base.localAvatar.stopPosHprBroadcast() + return + + def requestTeleport(self, hoodId, zoneId, shardId, avId): + if avId > 0: + teleportNotify.debug('requestTeleport%s' % ((hoodId, + zoneId, + shardId, + avId),)) + if localAvatar.hasActiveBoardingGroup(): + if avId > 0: + teleportNotify.debug('requestTeleport: has active boarding group') + rejectText = TTLocalizer.BoardingCannotLeaveZone + localAvatar.elevatorNotifier.showMe(rejectText) + return + loaderId = ZoneUtil.getBranchLoaderName(zoneId) + whereId = ZoneUtil.getToonWhereName(zoneId) + if hoodId == ToontownGlobals.MyEstate: + loaderId = 'safeZoneLoader' + whereId = 'estate' + if hoodId == ToontownGlobals.PartyHood: + loaderId = 'safeZoneLoader' + whereId = 'party' + self.requestLeave({'loader': loaderId, + 'where': whereId, + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': shardId, + 'avId': avId}) + + def enterQuest(self, npcToon): + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + base.localAvatar.obscureMoveFurnitureButton(1) + + def exitQuest(self): + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(0) + + def enterPurchase(self): + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + base.localAvatar.obscureMoveFurnitureButton(1) + + def exitPurchase(self): + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(0) + + def enterFishing(self): + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + + def exitFishing(self): + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + + def enterBanking(self): + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + base.localAvatar.obscureMoveFurnitureButton(1) + base.localAvatar.startSleepWatch(self.__handleFallingAsleepBanking) + + def __handleFallingAsleepBanking(self, arg): + if hasattr(self, 'fsm'): + messenger.send('bankAsleep') + self.fsm.request('walk') + base.localAvatar.forceGotoSleep() + + def exitBanking(self): + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(0) + base.localAvatar.stopSleepWatch() + + def enterPhone(self): + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + base.localAvatar.obscureMoveFurnitureButton(1) + base.localAvatar.startSleepWatch(self.__handleFallingAsleepPhone) + + def __handleFallingAsleepPhone(self, arg): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + messenger.send('phoneAsleep') + base.localAvatar.forceGotoSleep() + + def exitPhone(self): + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(0) + base.localAvatar.stopSleepWatch() + + def enterStopped(self): + base.localAvatar.b_setAnimState('neutral', 1) + Emote.globalEmote.disableBody(base.localAvatar, 'enterStopped') + self.accept('teleportQuery', self.handleTeleportQuery) + if base.localAvatar.isDisguised: + base.localAvatar.setTeleportAvailable(0) + else: + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + base.localAvatar.obscureMoveFurnitureButton(1) + base.localAvatar.startSleepWatch(self.__handleFallingAsleepStopped) + + def __handleFallingAsleepStopped(self, arg): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + base.localAvatar.forceGotoSleep() + messenger.send('stoppedAsleep') + + def exitStopped(self): + Emote.globalEmote.releaseBody(base.localAvatar, 'exitStopped') + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(0) + base.localAvatar.stopSleepWatch() + messenger.send('exitingStoppedState') + + def enterPet(self): + base.localAvatar.b_setAnimState('neutral', 1) + Emote.globalEmote.disableBody(base.localAvatar, 'enterPet') + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.setTeleportAllowed(0) + base.localAvatar.laffMeter.start() + self.enterFLM() + + def exitPet(self): + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.setTeleportAllowed(1) + Emote.globalEmote.releaseBody(base.localAvatar, 'exitPet') + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + self.exitFLM() + + def enterQuietZone(self, requestStatus): + self.quietZoneDoneEvent = uniqueName('quietZoneDone') + self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone) + self.quietZoneStateData = QuietZoneState.QuietZoneState(self.quietZoneDoneEvent) + self.quietZoneStateData.load() + self.quietZoneStateData.enter(requestStatus) + + def exitQuietZone(self): + self.ignore(self.quietZoneDoneEvent) + del self.quietZoneDoneEvent + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + return + + def handleQuietZoneDone(self): + how = base.cr.handlerArgs['how'] + self.fsm.request(how, [base.cr.handlerArgs]) diff --git a/toontown/hood/QuietZoneState.py b/toontown/hood/QuietZoneState.py new file mode 100755 index 00000000..de3cc646 --- /dev/null +++ b/toontown/hood/QuietZoneState.py @@ -0,0 +1,333 @@ +from panda3d.core import * +from direct.showbase.PythonUtil import Functor +from toontown.toonbase.PythonUtil import PriorityCallbacks +from direct.task import Task +from toontown.distributed.ToontownMsgTypes import * +from otp.otpbase import OTPGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import ZoneUtil + +class QuietZoneState(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('QuietZoneState') + Disable = False + Queue = [] + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.fsm = ClassicFSM.ClassicFSM('QuietZoneState', [State.State('off', self.enterOff, self.exitOff, ['waitForQuietZoneResponse']), + State.State('waitForQuietZoneResponse', self.enterWaitForQuietZoneResponse, self.exitWaitForQuietZoneResponse, ['waitForZoneRedirect']), + State.State('waitForZoneRedirect', self.enterWaitForZoneRedirect, self.exitWaitForZoneRedirect, ['waitForSetZoneResponse']), + State.State('waitForSetZoneResponse', self.enterWaitForSetZoneResponse, self.exitWaitForSetZoneResponse, ['waitForSetZoneComplete']), + State.State('waitForSetZoneComplete', self.enterWaitForSetZoneComplete, self.exitWaitForSetZoneComplete, ['waitForLocalAvatarOnShard']), + State.State('waitForLocalAvatarOnShard', self.enterWaitForLocalAvatarOnShard, self.exitWaitForLocalAvatarOnShard, ['off'])], 'off', 'off') + self._enqueueCount = 0 + self.fsm.enterInitialState() + + def load(self): + self.notify.debug('load()') + + def unload(self): + self._dequeue() + self.notify.debug('unload()') + del self.fsm + + @classmethod + def enqueueState(cls, state, requestStatus): + cls.Queue = [(state, requestStatus)] + cls.Queue + state._enqueueCount += 1 + if len(cls.Queue) == 1: + cls.startNextQueuedState() + + @classmethod + def dequeueState(cls, state): + s, requestStatus = cls.Queue.pop() + s._enqueueCount -= 1 + if len(cls.Queue) > 0: + cls.startNextQueuedState() + + @classmethod + def startNextQueuedState(cls): + state, requestStatus = cls.Queue[-1] + state._start(requestStatus) + + def _dequeue(self): + newQ = [] + for item in self.__class__.Queue: + state, requestStatus = item + if state is not self: + newQ.append(item) + + self.__class__.Queue = newQ + + def getEnterWaitForSetZoneResponseMsg(self): + return 'enterWaitForSetZoneResponse-%s' % (id(self),) + + def getQuietZoneLeftEvent(self): + return '%s-%s' % (base.cr.getQuietZoneLeftEvent(), id(self)) + + def getSetZoneCompleteEvent(self): + return 'setZoneComplete-%s' % (id(self),) + + def enter(self, requestStatus): + self.notify.debug('enter(requestStatus=' + str(requestStatus) + ')') + self._requestStatus = requestStatus + self._leftQuietZoneCallbacks = None + self._setZoneCompleteCallbacks = None + self._leftQuietZoneLocalCallbacks = {} + self._setZoneCompleteLocalCallbacks = {} + self.enqueueState(self, requestStatus) + return + + def _start(self, requestStatus): + base.transitions.fadeScreen(1.0) + self.fsm.request('waitForQuietZoneResponse') + + def getRequestStatus(self): + return self._requestStatus + + def exit(self): + self.notify.debug('exit()') + del self._requestStatus + base.transitions.noTransitions() + self.fsm.request('off') + self._dequeue() + + def waitForDatabase(self, description): + if base.endlessQuietZone: + return + base.cr.waitForDatabaseTimeout(requestName='quietZoneState-%s' % description) + + def clearWaitForDatabase(self): + base.cr.cleanupWaitingForDatabase() + + def addLeftQuietZoneCallback(self, callback, priority = None): + if self._leftQuietZoneCallbacks: + return self._leftQuietZoneCallbacks.add(callback, priority) + else: + token = PriorityCallbacks.GetToken() + fdc = SubframeCall(callback, taskMgr.getCurrentTask().getPriority() - 1) + self._leftQuietZoneLocalCallbacks[token] = fdc + return token + + def removeLeftQuietZoneCallback(self, token): + if token is not None: + lc = self._leftQuietZoneLocalCallbacks.pop(token, None) + if lc: + lc.cleanup() + if self._leftQuietZoneCallbacks: + self._leftQuietZoneCallbacks.remove(token) + return + + def addSetZoneCompleteCallback(self, callback, priority = None): + if self._setZoneCompleteCallbacks: + return self._setZoneCompleteCallbacks.add(callback, priority) + else: + token = PriorityCallbacks.GetToken() + fdc = SubframeCall(callback, taskMgr.getCurrentTask().getPriority() - 1) + self._setZoneCompleteLocalCallbacks[token] = fdc + return token + + def removeSetZoneCompleteCallback(self, token): + if token is not None: + lc = self._setZoneCompleteLocalCallbacks.pop(token, None) + if lc: + lc.cleanup() + if self._setZoneCompleteCallbacks: + self._setZoneCompleteCallbacks.remove(token) + return + + def handleWaitForQuietZoneResponse(self, msgType, di): + self.notify.debug('handleWaitForQuietZoneResponse(' + 'msgType=' + str(msgType) + ', di=' + str(di) + ')') + if msgType == CLIENT_ENTER_OBJECT_REQUIRED: + base.cr.handleQuietZoneGenerateWithRequired(di) + elif msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER: + base.cr.handleQuietZoneGenerateWithRequiredOther(di) + elif msgType == CLIENT_OBJECT_SET_FIELD: + base.cr.handleQuietZoneUpdateField(di) + elif msgType in QUIET_ZONE_IGNORED_LIST: + self.notify.debug('ignoring unwanted message from previous zone') + else: + base.cr.handlePlayGame(msgType, di) + + def handleWaitForZoneRedirect(self, msgType, di): + self.notify.debug('handleWaitForZoneRedirect(' + 'msgType=' + str(msgType) + ', di=' + str(di) + ')') + if msgType == CLIENT_ENTER_OBJECT_REQUIRED: + base.cr.handleQuietZoneGenerateWithRequired(di) + elif msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER: + base.cr.handleQuietZoneGenerateWithRequiredOther(di) + elif msgType == CLIENT_OBJECT_SET_FIELD: + base.cr.handleQuietZoneUpdateField(di) + else: + base.cr.handlePlayGame(msgType, di) + + def enterOff(self): + self.notify.debug('enterOff()') + + def exitOff(self): + self.notify.debug('exitOff()') + self._leftQuietZoneCallbacks = PriorityCallbacks() + self._setZoneCompleteCallbacks = PriorityCallbacks() + self._leftQuietZoneLocalCallbacks = {} + self._setZoneCompleteLocalCallbacks = {} + + def enterWaitForQuietZoneResponse(self): + self.notify.debug('enterWaitForQuietZoneResponse(doneStatus=' + str(self._requestStatus) + ')') + if not self.Disable: + base.cr.handler = self.handleWaitForQuietZoneResponse + base.cr.handlerArgs = self._requestStatus + base.cr.setInQuietZone(True) + self.setZoneDoneEvent = base.cr.getNextSetZoneDoneEvent() + self.acceptOnce(self.setZoneDoneEvent, self._handleQuietZoneComplete) + self.waitForDatabase('WaitForQuietZoneResponse') + if base.slowQuietZone: + + def sQZR(task): + base.cr.sendQuietZoneRequest() + return Task.done + + taskMgr.doMethodLater(base.slowQuietZoneDelay, sQZR, 'slowQuietZone-sendQuietZoneRequest') + else: + base.cr.sendQuietZoneRequest() + + def _handleQuietZoneComplete(self): + self.fsm.request('waitForZoneRedirect') + + def exitWaitForQuietZoneResponse(self): + self.notify.debug('exitWaitForQuietZoneResponse()') + self.clearWaitForDatabase() + base.cr.handler = base.cr.handlePlayGame + base.cr.handlerArgs = None + base.cr.setInQuietZone(False) + self.ignore(self.setZoneDoneEvent) + del self.setZoneDoneEvent + return + + def enterWaitForZoneRedirect(self): + print 'entered wait for zone redirect' + self.notify.debug('enterWaitForZoneRedirect(requestStatus=' + str(self._requestStatus) + ')') + if not self.Disable: + base.cr.handler = self.handleWaitForZoneRedirect + base.cr.handlerArgs = self._requestStatus + base.cr.setInQuietZone(True) + self.waitForDatabase('WaitForZoneRedirect') + self.fsm.request('waitForSetZoneResponse') + + def gotZoneRedirect(self, zoneId): + self.notify.info('Redirecting to zone %s.' % zoneId) + base.cr.handlerArgs['zoneId'] = zoneId + base.cr.handlerArgs['hoodId'] = ZoneUtil.getHoodId(zoneId) + self.fsm.request('waitForSetZoneResponse') + + def exitWaitForZoneRedirect(self): + self.notify.debug('exitWaitForZoneRedirect()') + self.clearWaitForDatabase() + base.cr.handler = base.cr.handlePlayGame + base.cr.handlerArgs = None + base.cr.setInQuietZone(False) + return + + def enterWaitForSetZoneResponse(self): + print 'entered wait for set zone response - 2' + self.notify.debug('enterWaitForSetZoneResponse(requestStatus=' + str(self._requestStatus) + ')') + if not self.Disable: + messenger.send(self.getEnterWaitForSetZoneResponseMsg(), [self._requestStatus]) + base.cr.handlerArgs = self._requestStatus + zoneId = self._requestStatus['zoneId'] + base.cr.dumpAllSubShardObjects() + base.cr.resetDeletedSubShardDoIds() + base.cr.sendSetZoneMsg(zoneId) + self.waitForDatabase('WaitForSetZoneResponse') + self.fsm.request('waitForSetZoneComplete') + + def exitWaitForSetZoneResponse(self): + self.notify.debug('exitWaitForSetZoneResponse()') + self.clearWaitForDatabase() + base.cr.handler = base.cr.handlePlayGame + base.cr.handlerArgs = None + return + + def enterWaitForSetZoneComplete(self): + self.notify.debug('enterWaitForSetZoneComplete(requestStatus=' + str(self._requestStatus) + ')') + if not self.Disable: + base.cr.handlerArgs = self._requestStatus + if base.slowQuietZone: + + def delayFunc(self = self): + + def hSZC(task): + self._handleSetZoneComplete() + return Task.done + + taskMgr.doMethodLater(base.slowQuietZoneDelay, hSZC, 'slowQuietZone-sendSetZoneComplete') + + nextFunc = delayFunc + else: + nextFunc = self._handleSetZoneComplete + self.waitForDatabase('WaitForSetZoneComplete') + self.setZoneDoneEvent = base.cr.getLastSetZoneDoneEvent() + self.acceptOnce(self.setZoneDoneEvent, nextFunc) + if base.placeBeforeObjects: + self._leftQuietZoneCallbacks() + self._leftQuietZoneCallbacks = None + fdcs = self._leftQuietZoneLocalCallbacks.values() + self._leftQuietZoneLocalCallbacks = {} + for fdc in fdcs: + if not fdc.isFinished(): + fdc.finish() + + messenger.send(self.getQuietZoneLeftEvent()) + return + + def _handleSetZoneComplete(self): + self.fsm.request('waitForLocalAvatarOnShard') + + def exitWaitForSetZoneComplete(self): + self.notify.debug('exitWaitForSetZoneComplete()') + self.clearWaitForDatabase() + base.cr.handler = base.cr.handlePlayGame + base.cr.handlerArgs = None + self.ignore(self.setZoneDoneEvent) + del self.setZoneDoneEvent + return + + def enterWaitForLocalAvatarOnShard(self): + self.notify.debug('enterWaitForLocalAvatarOnShard()') + if not self.Disable: + base.cr.handlerArgs = self._requestStatus + self._onShardEvent = localAvatar.getArrivedOnDistrictEvent() + self.waitForDatabase('WaitForLocalAvatarOnShard') + if localAvatar.isGeneratedOnDistrict(localAvatar.defaultShard): + self._announceDone() + else: + self.acceptOnce(self._onShardEvent, self._announceDone) + + def _announceDone(self): + base.localAvatar.startChat() + if base.endlessQuietZone: + self._dequeue() + return + doneEvent = self.doneEvent + requestStatus = self._requestStatus + self._setZoneCompleteCallbacks() + self._setZoneCompleteCallbacks = None + fdcs = self._setZoneCompleteLocalCallbacks.values() + self._setZoneCompleteLocalCallbacks = {} + for fdc in fdcs: + if not fdc.isFinished(): + fdc.finish() + + messenger.send(self.getSetZoneCompleteEvent(), [requestStatus]) + messenger.send(doneEvent) + self._dequeue() + return + + def exitWaitForLocalAvatarOnShard(self): + self.notify.debug('exitWaitForLocalAvatarOnShard()') + self.clearWaitForDatabase() + self.ignore(self._onShardEvent) + base.cr.handlerArgs = None + del self._onShardEvent + return diff --git a/toontown/hood/SellbotHQ.py b/toontown/hood/SellbotHQ.py new file mode 100755 index 00000000..0cb89017 --- /dev/null +++ b/toontown/hood/SellbotHQ.py @@ -0,0 +1,21 @@ +from toontown.coghq.SellbotCogHQLoader import SellbotCogHQLoader +from toontown.toonbase import ToontownGlobals +from toontown.hood.CogHood import CogHood + + +class SellbotHQ(CogHood): + notify = directNotify.newCategory('SellbotHQ') + + ID = ToontownGlobals.SellbotHQ + LOADER_CLASS = SellbotCogHQLoader + + def load(self): + CogHood.load(self) + + self.sky.setScale(2.0) + + def enter(self, requestStatus): + CogHood.enter(self, requestStatus) + + base.localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + base.camLens.setNearFar(ToontownGlobals.CogHQCameraNear, ToontownGlobals.CogHQCameraFar) diff --git a/toontown/hood/SellbotHQAI.py b/toontown/hood/SellbotHQAI.py new file mode 100755 index 00000000..576c3451 --- /dev/null +++ b/toontown/hood/SellbotHQAI.py @@ -0,0 +1,76 @@ +from toontown.building import DistributedVPElevatorAI +from toontown.building import FADoorCodes +from toontown.building.DistributedBoardingPartyAI import DistributedBoardingPartyAI +from toontown.coghq.DistributedFactoryElevatorExtAI import DistributedFactoryElevatorExtAI +from toontown.hood import CogHQAI +from toontown.suit import DistributedSellbotBossAI +from toontown.suit import DistributedSuitPlannerAI +from toontown.toonbase import ToontownGlobals + + +class SellbotHQAI(CogHQAI.CogHQAI): + def __init__(self, air): + CogHQAI.CogHQAI.__init__( + self, air, ToontownGlobals.SellbotHQ, ToontownGlobals.SellbotLobby, + FADoorCodes.SB_DISGUISE_INCOMPLETE, + DistributedVPElevatorAI.DistributedVPElevatorAI, + DistributedSellbotBossAI.DistributedSellbotBossAI) + + self.factoryElevators = [] + self.factoryBoardingParty = None + self.suitPlanners = [] + + self.startup() + + def startup(self): + CogHQAI.CogHQAI.startup(self) + + # Sellbot HQ has not just one, but four lobby doors: + self.cogHQDoors = [self.extDoor] + for i in xrange(3): # CogHQAI already created one of the doors for us. + extDoor = self.makeCogHQDoor(self.lobbyZoneId, 0, i + 1, self.lobbyFADoorCode) + self.cogHQDoors.append(extDoor) + self.createFactoryElevators() + if simbase.config.GetBool('want-boarding-groups', True): + self.createFactoryBoardingParty() + if simbase.config.GetBool('want-suit-planners', True): + self.createSuitPlanners() + + # Our suit planner needs the Cog HQ doors as well: + for sp in self.suitPlanners: + if sp.zoneId == self.zoneId: + sp.cogHQDoors = self.cogHQDoors + + def createFactoryElevators(self): + # We only have two factory elevators: the front, and side elevators. + for i in xrange(2): + factoryElevator = DistributedFactoryElevatorExtAI( + self.air, self.air.factoryMgr, ToontownGlobals.SellbotFactoryInt, i) + factoryElevator.generateWithRequired(ToontownGlobals.SellbotFactoryExt) + self.factoryElevators.append(factoryElevator) + + if simbase.config.GetBool('want-megacorp', True): + factoryElevator = DistributedFactoryElevatorExtAI( + self.air, self.air.factoryMgr, ToontownGlobals.SellbotMegaCorpInt, 2) + factoryElevator.generateWithRequired(ToontownGlobals.SellbotFactoryExt) + self.factoryElevators.append(factoryElevator) + + def createFactoryBoardingParty(self): + factoryIdList = [elevator.doId for elevator in self.factoryElevators] + self.factoryBoardingParty = DistributedBoardingPartyAI(self.air, factoryIdList, 4) + self.factoryBoardingParty.generateWithRequired(ToontownGlobals.SellbotFactoryExt) + + def createSuitPlanners(self): + suitPlanner = DistributedSuitPlannerAI.DistributedSuitPlannerAI(self.air, self.zoneId) + suitPlanner.generateWithRequired(self.zoneId) + suitPlanner.d_setZoneId(self.zoneId) + suitPlanner.initTasks() + self.suitPlanners.append(suitPlanner) + self.air.suitPlanners[self.zoneId] = suitPlanner + + suitPlanner = DistributedSuitPlannerAI.DistributedSuitPlannerAI(self.air, ToontownGlobals.SellbotFactoryExt) + suitPlanner.generateWithRequired(ToontownGlobals.SellbotFactoryExt) + suitPlanner.d_setZoneId(ToontownGlobals.SellbotFactoryExt) + suitPlanner.initTasks() + self.suitPlanners.append(suitPlanner) + self.air.suitPlanners[ToontownGlobals.SellbotFactoryExt] = suitPlanner diff --git a/toontown/hood/TTHood.py b/toontown/hood/TTHood.py new file mode 100755 index 00000000..3f7d088c --- /dev/null +++ b/toontown/hood/TTHood.py @@ -0,0 +1,40 @@ +from otp.ai.MagicWordGlobal import * +from toontown.safezone.TTSafeZoneLoader import TTSafeZoneLoader +from toontown.town.TTTownLoader import TTTownLoader +from toontown.toonbase import ToontownGlobals +from toontown.hood.ToonHood import ToonHood + +class TTHood(ToonHood): + notify = directNotify.newCategory('TTHood') + + ID = ToontownGlobals.ToontownCentral + TOWNLOADER_CLASS = TTTownLoader + SAFEZONELOADER_CLASS = TTSafeZoneLoader + STORAGE_DNA = 'phase_4/dna/storage_TT.pdna' + SKY_FILE = 'phase_3.5/models/props/TT_sky' + SPOOKY_SKY_FILE = 'phase_3.5/models/props/BR_sky' + TITLE_COLOR = (1.0, 0.5, 0.4, 1.0) + + HOLIDAY_DNA = { + ToontownGlobals.CHRISTMAS: ['phase_4/dna/winter_storage_TT.pdna', 'phase_4/dna/winter_storage_TT_sz.pdna'], + ToontownGlobals.HALLOWEEN: ['phase_4/dna/halloween_props_storage_TT.pdna', 'phase_4/dna/halloween_props_storage_TT_sz.pdna']} + +@magicWord(category=CATEGORY_CREATIVE) +def spooky(): + """ + Activates the 'spooky' effect on the current area. + """ + hood = base.cr.playGame.hood + if not hasattr(hood, 'startSpookySky'): + return "Couldn't find spooky sky." + if hasattr(hood, 'magicWordSpookyEffect'): + return 'The spooky effect is already active!' + hood.magicWordSpookyEffect = True + hood.startSpookySky() + fadeOut = base.cr.playGame.getPlace().loader.geom.colorScaleInterval( + 1.5, Vec4(0.55, 0.55, 0.65, 1), startColorScale=Vec4(1, 1, 1, 1), + blendType='easeInOut') + fadeOut.start() + spookySfx = base.loadSfx('phase_4/audio/sfx/spooky.ogg') + spookySfx.play() + return 'Activating the spooky effect...' diff --git a/toontown/hood/TTHoodAI.py b/toontown/hood/TTHoodAI.py new file mode 100755 index 00000000..7fb90618 --- /dev/null +++ b/toontown/hood/TTHoodAI.py @@ -0,0 +1,49 @@ +from toontown.hood import HoodAI +from toontown.safezone import ButterflyGlobals +from toontown.safezone import DistributedButterflyAI +from toontown.safezone import DistributedTrolleyAI +from toontown.toonbase import ToontownGlobals +from toontown.ai import DistributedEffectMgrAI + +class TTHoodAI(HoodAI.HoodAI): + def __init__(self, air): + HoodAI.HoodAI.__init__(self, air, + ToontownGlobals.ToontownCentral, + ToontownGlobals.ToontownCentral) + + self.trolley = None + + self.startup() + + def startup(self): + HoodAI.HoodAI.startup(self) + + if simbase.config.GetBool('want-minigames', True): + self.createTrolley() + if simbase.config.GetBool('want-butterflies', True): + self.createButterflies() + + self.trickOrTreatMgr = DistributedEffectMgrAI.DistributedEffectMgrAI(self.air, ToontownGlobals.HALLOWEEN, 12) + self.trickOrTreatMgr.generateWithRequired(2649) # All Fun and Games Shop, Silly Street + + self.winterCarolingMgr = DistributedEffectMgrAI.DistributedEffectMgrAI(self.air, ToontownGlobals.CHRISTMAS, 14) + self.winterCarolingMgr.generateWithRequired(2659) # Joy Buzzers to the World, Silly Street + + def shutdown(self): + HoodAI.HoodAI.shutdown(self) + ButterflyGlobals.clearIndexes(self.zoneId) + + def createTrolley(self): + self.trolley = DistributedTrolleyAI.DistributedTrolleyAI(self.air) + self.trolley.generateWithRequired(self.zoneId) + self.trolley.start() + + def createButterflies(self): + playground = ButterflyGlobals.TTC + ButterflyGlobals.generateIndexes(self.zoneId, ButterflyGlobals.TTC) + + for i in xrange(0, ButterflyGlobals.NUM_BUTTERFLY_AREAS[ButterflyGlobals.TTC]): + for _ in xrange(0, ButterflyGlobals.NUM_BUTTERFLIES[ButterflyGlobals.TTC]): + butterfly = DistributedButterflyAI.DistributedButterflyAI(self.air, playground, i, self.zoneId) + butterfly.generateWithRequired(self.zoneId) + butterfly.start() diff --git a/toontown/hood/ToonHood.py b/toontown/hood/ToonHood.py new file mode 100755 index 00000000..c8c5c81a --- /dev/null +++ b/toontown/hood/ToonHood.py @@ -0,0 +1,259 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.distributed.ToontownMsgTypes import * +from direct.fsm import ClassicFSM, State +from toontown.minigame import Purchase +from otp.avatar import DistributedAvatar +from toontown.hood.Hood import Hood +from toontown.building import SuitInterior +from toontown.cogdominium import CogdoInterior +from toontown.toon.Toon import teleportDebug +from toontown.safezone import SZUtil + +class ToonHood(Hood): + notify = directNotify.newCategory('ToonHood') + + ID = None + TOWNLOADER_CLASS = None + SAFEZONELOADER_CLASS = None + STORAGE_DNA = None + SKY_FILE = None + SPOOKY_SKY_FILE = None + TITLE_COLOR = None + HOLIDAY_DNA = {} + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + Hood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + + self.suitInteriorDoneEvent = 'suitInteriorDone' + self.minigameDoneEvent = 'minigameDone' + self.whiteFogColor = Vec4(0.8, 0.8, 0.8, 1) + + self.fsm = ClassicFSM.ClassicFSM('Hood', [State.State('start', self.enterStart, self.exitStart, ['townLoader', 'safeZoneLoader']), + State.State('townLoader', self.enterTownLoader, self.exitTownLoader, ['quietZone', + 'safeZoneLoader', + 'suitInterior', + 'cogdoInterior']), + State.State('safeZoneLoader', self.enterSafeZoneLoader, self.exitSafeZoneLoader, ['quietZone', + 'suitInterior', + 'cogdoInterior', + 'townLoader', + 'minigame']), + State.State('purchase', self.enterPurchase, self.exitPurchase, ['quietZone', 'minigame', 'safeZoneLoader']), + State.State('suitInterior', self.enterSuitInterior, self.exitSuitInterior, ['quietZone', 'townLoader', 'safeZoneLoader']), + State.State('cogdoInterior', self.enterCogdoInterior, self.exitCogdoInterior, ['quietZone', 'townLoader', 'safeZoneLoader']), + State.State('minigame', self.enterMinigame, self.exitMinigame, ['purchase']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['safeZoneLoader', + 'townLoader', + 'suitInterior', + 'cogdoInterior', + 'minigame']), + State.State('final', self.enterFinal, self.exitFinal, [])], 'start', 'final') + self.fsm.enterInitialState() + + # Until we cleanup Hood, we will need to define some variables + self.id = self.ID + self.storageDNAFile = self.STORAGE_DNA + self.holidayStorageDNADict = self.HOLIDAY_DNA + self.skyFile = self.SKY_FILE + self.spookySkyFile = self.SPOOKY_SKY_FILE + self.titleColor = self.TITLE_COLOR + + def load(self): + Hood.load(self) + + self.parentFSM.getStateNamed(self.__class__.__name__).addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed(self.__class__.__name__).removeChild(self.fsm) + + Hood.unload(self) + + def loadLoader(self, requestStatus): + loaderName = requestStatus['loader'] + if loaderName == 'safeZoneLoader': + self.loader = self.SAFEZONELOADER_CLASS(self, self.fsm.getStateNamed('safeZoneLoader'), self.loaderDoneEvent) + self.loader.load() + elif loaderName == 'townLoader': + self.loader = self.TOWNLOADER_CLASS(self, self.fsm.getStateNamed('townLoader'), self.loaderDoneEvent) + self.loader.load(requestStatus['zoneId']) + + def enterTownLoader(self, requestStatus): + teleportDebug(requestStatus, 'ToonHood.enterTownLoader, status=%s' % (requestStatus,)) + self.accept(self.loaderDoneEvent, self.handleTownLoaderDone) + self.loader.enter(requestStatus) + self.spawnTitleText(requestStatus['zoneId']) + + def exitTownLoader(self): + taskMgr.remove('titleText') + self.hideTitleText() + self.ignore(self.loaderDoneEvent) + self.loader.exit() + self.loader.unload() + del self.loader + + def handleTownLoaderDone(self): + doneStatus = self.loader.getDoneStatus() + teleportDebug(doneStatus, 'handleTownLoaderDone, doneStatus=%s' % (doneStatus,)) + if self.isSameHood(doneStatus): + teleportDebug(doneStatus, 'same hood') + self.fsm.request('quietZone', [doneStatus]) + else: + teleportDebug(doneStatus, 'different hood') + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + + def enterPurchase(self, pointsAwarded, playerMoney, avIds, playerStates, remain): + messenger.send('enterSafeZone') + DistributedAvatar.DistributedAvatar.HpTextEnabled = 0 + base.localAvatar.laffMeter.start() + self.purchaseDoneEvent = 'purchaseDone' + self.accept(self.purchaseDoneEvent, self.handlePurchaseDone) + self.purchase = Purchase.Purchase(base.localAvatar, pointsAwarded, playerMoney, avIds, playerStates, remain, self.purchaseDoneEvent) + self.purchase.load() + self.purchase.enter() + + def exitPurchase(self): + messenger.send('exitSafeZone') + DistributedAvatar.DistributedAvatar.HpTextEnabled = 1 + base.localAvatar.laffMeter.stop() + self.ignore(self.purchaseDoneEvent) + self.purchase.exit() + self.purchase.unload() + del self.purchase + + def handlePurchaseDone(self): + doneStatus = self.purchase.getDoneStatus() + if doneStatus['where'] == 'playground': + self.fsm.request('quietZone', [{'loader': 'safeZoneLoader', + 'where': 'playground', + 'how': 'teleportIn', + 'hoodId': self.hoodId, + 'zoneId': self.hoodId, + 'shardId': None, + 'avId': -1}]) + elif doneStatus['loader'] == 'minigame': + self.fsm.request('minigame') + else: + self.notify.error('handlePurchaseDone: unknown mode') + return + + def enterSuitInterior(self, requestStatus = None): + self.placeDoneEvent = 'suit-interior-done' + self.acceptOnce(self.placeDoneEvent, self.handleSuitInteriorDone) + self.place = SuitInterior.SuitInterior(self, self.fsm, self.placeDoneEvent) + self.place.load() + self.place.enter(requestStatus) + base.cr.playGame.setPlace(self.place) + + def exitSuitInterior(self): + self.ignore(self.placeDoneEvent) + del self.placeDoneEvent + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + + def handleSuitInteriorDone(self): + doneStatus = self.place.getDoneStatus() + if self.isSameHood(doneStatus): + self.fsm.request('quietZone', [doneStatus]) + else: + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + + def enterCogdoInterior(self, requestStatus = None): + self.placeDoneEvent = 'cogdo-interior-done' + self.acceptOnce(self.placeDoneEvent, self.handleCogdoInteriorDone) + self.place = CogdoInterior.CogdoInterior(self, self.fsm, self.placeDoneEvent) + self.place.load() + self.place.enter(requestStatus) + base.cr.playGame.setPlace(self.place) + + def exitCogdoInterior(self): + self.ignore(self.placeDoneEvent) + del self.placeDoneEvent + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + + def handleCogdoInteriorDone(self): + doneStatus = self.place.getDoneStatus() + if self.isSameHood(doneStatus): + self.fsm.request('quietZone', [doneStatus]) + else: + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + + def enterMinigame(self, ignoredParameter = None): + messenger.send('enterSafeZone') + DistributedAvatar.DistributedAvatar.HpTextEnabled = 0 + base.localAvatar.laffMeter.start() + base.cr.forbidCheesyEffects(1) + self.acceptOnce(self.minigameDoneEvent, self.handleMinigameDone) + + def exitMinigame(self): + messenger.send('exitSafeZone') + DistributedAvatar.DistributedAvatar.HpTextEnabled = 1 + base.localAvatar.laffMeter.stop() + base.cr.forbidCheesyEffects(0) + self.ignore(self.minigameDoneEvent) + minigameState = self.fsm.getStateNamed('minigame') + for childFSM in minigameState.getChildren(): + minigameState.removeChild(childFSM) + + def handleMinigameDone(self): + pass + + def skyTrack(self, task): + return SZUtil.cloudSkyTrack(task) + + def startSky(self): + if not self.sky.getTag('sky') == 'Regular': + self.endSpookySky() + SZUtil.startCloudSky(self) + + def startSpookySky(self): + if hasattr(self, 'sky') and self.sky: + self.stopSky() + self.sky = loader.loadModel(self.spookySkyFile) + self.sky.setTag('sky', 'Halloween') + self.sky.setScale(1.0) + self.sky.setDepthTest(0) + self.sky.setDepthWrite(0) + self.sky.setColor(0.5, 0.5, 0.5, 1) + self.sky.setBin('background', 100) + self.sky.setFogOff() + self.sky.reparentTo(camera) + self.sky.setTransparency(TransparencyAttrib.MDual, 1) + fadeIn = self.sky.colorScaleInterval(1.5, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0.25), blendType='easeInOut') + fadeIn.start() + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) + + def setUnderwaterFog(self): + if base.wantFog: + self.fog.setLinearRange(0.1, 100.0) + render.setFog(self.fog) + self.sky.setFog(self.fog) + SZUtil.startUnderwaterFog() + + def setWhiteFog(self): + if base.wantFog: + self.fog.setColor(self.whiteFogColor) + self.fog.setLinearRange(0.0, 400.0) + render.clearFog() + render.setFog(self.fog) + self.sky.clearFog() + self.sky.setFog(self.fog) + SZUtil.stopUnderwaterFog() + + def setNoFog(self): + if base.wantFog: + render.clearFog() + self.sky.clearFog() + SZUtil.stopUnderwaterFog() diff --git a/toontown/hood/TrashcanInteractiveProp.py b/toontown/hood/TrashcanInteractiveProp.py new file mode 100755 index 00000000..587b4f70 --- /dev/null +++ b/toontown/hood/TrashcanInteractiveProp.py @@ -0,0 +1,69 @@ +from direct.actor import Actor +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import InteractiveAnimatedProp +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals, TTLocalizer + +class TrashcanInteractiveProp(InteractiveAnimatedProp.InteractiveAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('TrashcanInteractiveProp') + BattleTrack = ToontownBattleGlobals.HEAL_TRACK + BattleCheerText = TTLocalizer.InteractivePropTrackBonusTerms[BattleTrack] + + ZoneToIdles = { + ToontownGlobals.ToontownCentral: (('tt_a_ara_ttc_trashcan_idleTake2', 1, 1, None, 3, 10), + ('tt_a_ara_ttc_trashcan_idleHiccup0', 1, 1, None, 3, 10), + ('tt_a_ara_ttc_trashcan_idleLook1', 1, 1, None, 3, 10), + ('tt_a_ara_ttc_trashcan_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.DonaldsDock: (('tt_a_ara_dod_trashcan_idleBounce2', 3, 10, 'tt_a_ara_dod_trashcan_idle0settle', 3, 10), + ('tt_a_ara_dod_trashcan_idle0', 1, 1, None, 3, 10), + ('tt_a_ara_dod_trashcan_idle1', 1, 1, None, 3, 10), + ('tt_a_ara_dod_trashcan_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.DaisyGardens: (('tt_a_ara_dga_trashcan_idleTake2', 1, 1, None, 3, 10), + ('tt_a_ara_dga_trashcan_idleHiccup0', 1, 1, None, 3, 10), + ('tt_a_ara_dga_trashcan_idleLook1', 1, 1, None, 3, 10), + ('tt_a_ara_dga_trashcan_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.MinniesMelodyland: (('tt_a_ara_mml_trashcan_idleBounce0', 3, 10, 'tt_a_ara_mml_trashcan_idle0settle', 3, 10), + ('tt_a_ara_mml_trashcan_idleLook1', 1, 1, None, 3, 10), + ('tt_a_ara_mml_trashcan_idleHelicopter2', 1, 1, None, 3, 10), + ('tt_a_ara_mml_trashcan_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.TheBrrrgh: (('tt_a_ara_tbr_trashcan_idleShiver1', 1, 1, None, 3, 10), + ('tt_a_ara_tbr_trashcan_idleSneeze2', 1, 1, None, 3, 10), + ('tt_a_ara_tbr_trashcan_idle0', 1, 1, None, 3, 10), + ('tt_a_ara_tbr_trashcan_idleAwesome3', 1, 1, None, 3, 10)), + ToontownGlobals.DonaldsDreamland: (('tt_a_ara_ddl_trashcan_idleSleep0', 3, 10, None, 0, 0), + ('tt_a_ara_ddl_trashcan_idleShake2', 1, 1, None, 0, 0), + ('tt_a_ara_ddl_trashcan_idleSnore1', 1, 1, None, 0, 0), + ('tt_a_ara_ddl_trashcan_idleAwesome3', 1, 1, None, 0, 0))} + + ZoneToIdleIntoFightAnims = { + ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_trashcan_idleIntoFight', + ToontownGlobals.DonaldsDock: 'tt_a_ara_dod_trashcan_idleIntoFight', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_trashcan_idleIntoFight', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_trashcan_idleIntoFight', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_trashcan_idleIntoFight', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_trashcan_idleIntoFight'} + + ZoneToVictoryAnims = { + ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_trashcan_victoryDance', + ToontownGlobals.DonaldsDock: 'tt_a_ara_dod_trashcan_victoryDance', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_trashcan_victoryDance', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_trashcan_victoryDance', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_trashcan_victoryDance', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_trashcan_victoryDance'} + + ZoneToSadAnims = { + ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_trashcan_fightSad', + ToontownGlobals.DonaldsDock: 'tt_a_ara_dod_trashcan_fightSad', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_trashcan_fightSad', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_trashcan_fightSad', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_trashcan_fightSad', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_trashcan_fightSad'} + + ZoneToFightAnims = { + ToontownGlobals.ToontownCentral: ('tt_a_ara_ttc_trashcan_fightBoost', 'tt_a_ara_ttc_trashcan_fightCheer', 'tt_a_ara_ttc_trashcan_fightIdle'), + ToontownGlobals.DonaldsDock: ('tt_a_ara_dod_trashcan_fightBoost', 'tt_a_ara_dod_trashcan_fightCheer', 'tt_a_ara_dod_trashcan_fightIdle'), + ToontownGlobals.DaisyGardens: ('tt_a_ara_dga_trashcan_fightBoost', 'tt_a_ara_dga_trashcan_fightCheer', 'tt_a_ara_dga_trashcan_fightIdle'), + ToontownGlobals.MinniesMelodyland: ('tt_a_ara_mml_trashcan_fightBoost', 'tt_a_ara_mml_trashcan_fightCheer0', 'tt_a_ara_mml_trashcan_fightCheer1', 'tt_a_ara_mml_trashcan_fightIdle'), + ToontownGlobals.TheBrrrgh: ('tt_a_ara_tbr_trashcan_fightBoost', 'tt_a_ara_tbr_trashcan_fightCheer', 'tt_a_ara_tbr_trashcan_fightIdle'), + ToontownGlobals.DonaldsDreamland: ('tt_a_ara_ddl_trashcan_fightBoost', 'tt_a_ara_ddl_trashcan_fightCheer', 'tt_a_ara_ddl_trashcan_fightIdle')} + + IdlePauseTime = base.config.GetFloat('prop-idle-pause-time', 0.0) diff --git a/toontown/hood/TutorialHood.py b/toontown/hood/TutorialHood.py new file mode 100755 index 00000000..1ecdb784 --- /dev/null +++ b/toontown/hood/TutorialHood.py @@ -0,0 +1,12 @@ +from toontown.town.TutorialTownLoader import TutorialTownLoader +from toontown.toonbase import ToontownGlobals +from toontown.hood.ToonHood import ToonHood + + +class TutorialHood(ToonHood): + notify = directNotify.newCategory('TutorialHood') + + ID = ToontownGlobals.Tutorial + TOWNLOADER_CLASS = TutorialTownLoader + SKY_FILE = 'phase_3.5/models/props/TT_sky' + TITLE_COLOR = (1.0, 0.5, 0.4, 1.0) diff --git a/toontown/hood/ZoneUtil.py b/toontown/hood/ZoneUtil.py new file mode 100755 index 00000000..ebd1a1dc --- /dev/null +++ b/toontown/hood/ZoneUtil.py @@ -0,0 +1,224 @@ +from toontown.toonbase.ToontownGlobals import * + + +zoneUtilNotify = directNotify.newCategory('ZoneUtil') +tutorialDict = None + + +def isGoofySpeedwayZone(zoneId): + return zoneId == 8000 + + +def isCogHQZone(zoneId): + return zoneId >= 10000 and zoneId < 15000 + + +def isMintInteriorZone(zoneId): + return zoneId in (CashbotMintIntA, CashbotMintIntB, CashbotMintIntC) + + +def isDynamicZone(zoneId): + return zoneId >= DynamicZonesBegin and zoneId < DynamicZonesEnd + + +def getStreetName(branchId): + global tutorialDict + if tutorialDict: + return StreetNames[20000][-1] + else: + return StreetNames[branchId][-1] + + +def getLoaderName(zoneId): + if tutorialDict: + if zoneId == ToontownCentral: + loaderName = 'safeZoneLoader' + else: + loaderName = 'townLoader' + else: + suffix = zoneId % 1000 + if suffix >= 500: + suffix -= 500 + if isCogHQZone(zoneId): + loaderName = 'cogHQLoader' + elif suffix < 100: + loaderName = 'safeZoneLoader' + else: + loaderName = 'townLoader' + return loaderName + + +def getBranchLoaderName(zoneId): + return getLoaderName(getBranchZone(zoneId)) + + +def getSuitWhereName(zoneId): + where = getWhereName(zoneId, 0) + return where + + +def getToonWhereName(zoneId): + where = getWhereName(zoneId, 1) + return where + + +def isPlayground(zoneId): + whereName = getWhereName(zoneId, False) + if whereName == 'cogHQExterior': + return True + else: + return zoneId % 1000 == 0 and zoneId < DynamicZonesBegin + + +def isHQ(zoneId): + if zoneId == 2520 or zoneId == 1507 or zoneId == 3508 or zoneId == 4504 or zoneId == 5502 or zoneId == 7503 or zoneId == 9505: + return True + return False + +def isPetshop(zoneId): + if zoneId == 2522 or zoneId == 1510 or zoneId == 3511 or zoneId == 4508 or zoneId == 5505 or zoneId == 7504 or zoneId == 9508: + return True + return False + + +def getWhereName(zoneId, isToon): + if tutorialDict: + if zoneId in tutorialDict['interiors']: + where = 'toonInterior' + elif zoneId in tutorialDict['exteriors']: + where = 'street' + elif zoneId == ToontownCentral or zoneId == WelcomeValleyToken: + where = 'playground' + else: + zoneUtilNotify.error('No known zone: ' + str(zoneId)) + else: + suffix = zoneId % 1000 + suffix = suffix - suffix % 100 + if isCogHQZone(zoneId): + if suffix == 0: + where = 'cogHQExterior' + elif suffix == 100: + where = 'cogHQLobby' + elif suffix == 200: + where = 'factoryExterior' + elif getHoodId(zoneId) == LawbotHQ and suffix in (300, 400, 500, 600): + where = 'stageInterior' + elif getHoodId(zoneId) == BossbotHQ and suffix in (500, 600, 700): + where = 'countryClubInterior' + elif suffix >= 500: + if getHoodId(zoneId) == SellbotHQ: + if suffix == 600: + where = 'megaCorpInterior' + else: + where = 'factoryInterior' + elif getHoodId(zoneId) == CashbotHQ: + where = 'mintInterior' + else: + zoneUtilNotify.error('unknown cogHQ interior for hood: ' + str(getHoodId(zoneId))) + else: + zoneUtilNotify.error('unknown cogHQ where: ' + str(zoneId)) + elif suffix == 0: + where = 'playground' + elif suffix >= 500: + if isToon: + where = 'toonInterior' + else: + where = 'suitInterior' + else: + where = 'street' + return where + + +def getBranchZone(zoneId): + if tutorialDict: + branchId = tutorialDict['branch'] + else: + branchId = zoneId - zoneId % 100 + if not isCogHQZone(zoneId): + if zoneId % 1000 >= 500: + branchId -= 500 + return branchId + + +def getCanonicalBranchZone(zoneId): + return getBranchZone(getCanonicalZoneId(zoneId)) + + +def getCanonicalZoneId(zoneId): + return zoneId + + +def getHoodId(zoneId): + if tutorialDict: + hoodId = Tutorial + else: + hoodId = zoneId - zoneId % 1000 + return hoodId + + +def getSafeZoneId(zoneId): + hoodId = getHoodId(zoneId) + if hoodId in HQToSafezone: + hoodId = HQToSafezone[hoodId] + return hoodId + + +def getCanonicalHoodId(zoneId): + return getHoodId(getCanonicalZoneId(zoneId)) + +def getCanonicalSafeZoneId(zoneId): + return getSafeZoneId(getCanonicalZoneId(zoneId)) + +def isInterior(zoneId): + if tutorialDict: + if zoneId in tutorialDict['interiors']: + r = 1 + else: + r = 0 + else: + r = zoneId % 1000 >= 500 + return r + +def overrideOn(branch, exteriorList, interiorList): + global tutorialDict + if tutorialDict: + zoneUtilNotify.warning('setTutorialDict: tutorialDict is already set!') + tutorialDict = {'branch': branch, + 'exteriors': exteriorList, + 'interiors': interiorList} + +def overrideOff(): + global tutorialDict + tutorialDict = None + return + +def getWakeInfo(hoodId = None, zoneId = None): + wakeWaterHeight = 0 + showWake = 0 + try: + if hoodId is None: + hoodId = base.cr.playGame.getPlaceId() + if zoneId is None: + zoneId = base.cr.playGame.getPlace().getZoneId() + canonicalZoneId = getCanonicalZoneId(zoneId) + if canonicalZoneId == DonaldsDock: + wakeWaterHeight = DDWakeWaterHeight + showWake = 1 + elif canonicalZoneId == ToontownCentral: + wakeWaterHeight = TTWakeWaterHeight + showWake = 1 + elif canonicalZoneId == OutdoorZone: + wakeWaterHeight = OZWakeWaterHeight + showWake = 1 + elif hoodId == MyEstate: + wakeWaterHeight = EstateWakeWaterHeight + showWake = 1 + except AttributeError: + pass + + return (showWake, wakeWaterHeight) + +def canWearSuit(zoneId): + zoneId = getCanonicalHoodId(zoneId) + + return zoneId >= DynamicZonesBegin or zoneId in [LawbotHQ, CashbotHQ, SellbotHQ, BossbotHQ] diff --git a/toontown/hood/__init__.py b/toontown/hood/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/language/LanguageSelector.py b/toontown/language/LanguageSelector.py new file mode 100755 index 00000000..53a23296 --- /dev/null +++ b/toontown/language/LanguageSelector.py @@ -0,0 +1,82 @@ +from direct.gui.DirectGui import OnscreenImage, DirectLabel, DirectButton +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.toontowngui import TTDialog +import os + +class LanguageSelector: + + def __init__(self, leaveFunction): + self.title = None + self.current = None + self.available = None + self.english = None + self.french = None + self.portuguese = None + self.german = None + self.backButton = None + self.confirmDialog = None + self.leaveFunction = leaveFunction + + def create(self): + self.background = OnscreenImage(parent=render2d, image="phase_3.5/maps/blackboardEmpty.jpg") + self.gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + self.shuffleUp = self.gui.find('**/tt_t_gui_mat_shuffleUp') + self.shuffleDown = self.gui.find('**/tt_t_gui_mat_shuffleDown') + + self.title = DirectLabel(aspect2d, relief=None, text=TTLocalizer.LanguageSelectorTitle, + text_fg=(0, 1, 0, 1), text_scale=0.15, text_font=ToontownGlobals.getSuitFont(), + pos=(0, 0, 0.70), text_shadow=(0, 0.392, 0, 1)) + + self.current = DirectLabel(aspect2d, relief=None, text=TTLocalizer.LanguageSelectorCurrent % settings['language'], + text_fg=(0, 1, 0, 1), text_scale=0.09, text_font=ToontownGlobals.getSuitFont(), + pos=(0, 0, 0.55), text_shadow=(0, 0.392, 0, 1)) + + self.available = DirectLabel(aspect2d, relief=None, text=TTLocalizer.LanguageSelectorAvailable, + text_fg=(1, 0, 0, 1), text_scale=0.09, text_font=ToontownGlobals.getSuitFont(), + pos=(0, 0, 0), text_shadow=(0.545, 0, 0, 1)) + + self.english = DirectButton(aspect2d, relief=None, text='English', + text_fg=(1, 0.549, 0, 1), text_scale=0.09, text_font=ToontownGlobals.getSuitFont(), + pos=(0, 0, -0.15), text_shadow=(1, 0.27, 0, 1), command=self.switchLanguage, extraArgs=['English']) + + self.french = DirectButton(aspect2d, relief=None, text='French', + text_fg=(1, 0.549, 0, 1), text_scale=0.09, text_font=ToontownGlobals.getSuitFont(), + pos=(0, 0, -0.25), text_shadow=(1, 0.27, 0, 1), command=self.switchLanguage, extraArgs=['French']) + + self.portuguese = DirectButton(aspect2d, relief=None, text='Portuguese', + text_fg=(1, 0.549, 0, 1), text_scale=0.09, text_font=ToontownGlobals.getSuitFont(), + pos=(0, 0, -0.35), text_shadow=(1, 0.27, 0, 1), command=self.switchLanguage, extraArgs=['Portuguese']) + + self.german = DirectButton(aspect2d, relief=None, text='German', + text_fg=(1, 0.549, 0, 1), text_scale=0.09, text_font=ToontownGlobals.getSuitFont(), + pos=(0, 0, -0.45), text_shadow=(1, 0.27, 0, 1), command=self.switchLanguage, extraArgs=['German']) + + self.backButton = DirectButton(aspect2d, relief=None, image=(self.shuffleUp, self.shuffleDown), + text=TTLocalizer.LanguageSelectorBack, text_font=ToontownGlobals.getInterfaceFont(), + text_scale=0.11, text_pos=(0, -0.02), pos=(0, 0, -0.75), text_fg=(1, 1, 1, 1), + text_shadow=(0, 0, 0, 1), command=self.destroy) + + def destroy(self): + for element in [self.background, self.title, self.current, self.available, self.english, self.french, self.portuguese, self.german, self.backButton, self.confirmDialog]: + if element: + element.destroy() + element = None + + self.leaveFunction() + + def switchLanguage(self, language): + if language == settings['language']: + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.LanguageSelectorSameLanguage, command=self.cleanupDialog) + else: + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.LanguageSelectorConfirm % language, command=self.confirmSwitchLanguage, extraArgs=[language]) + self.confirmDialog.show() + + def confirmSwitchLanguage(self, value, language): + if value > 0: + settings['language'] = language + os._exit(1) + else: + self.cleanupDialog() + + def cleanupDialog(self, value=0): + self.confirmDialog.cleanup() diff --git a/toontown/language/__init__.py b/toontown/language/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/launcher/TTSLauncher.py b/toontown/launcher/TTSLauncher.py new file mode 100755 index 00000000..38886271 --- /dev/null +++ b/toontown/launcher/TTSLauncher.py @@ -0,0 +1,62 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +import os +import sys +import time + +class LogAndOutput: + def __init__(self, orig, log): + self.orig = orig + self.log = log + + def write(self, str): + self.log.write(str) + self.log.flush() + self.orig.write(str) + self.orig.flush() + + def flush(self): + self.log.flush() + self.orig.flush() + +class TTSLauncher: + notify = DirectNotifyGlobal.directNotify.newCategory('TTSLauncher') + + def __init__(self): + self.http = HTTPClient() + + self.logPrefix = 'stride-' + + ltime = 1 and time.localtime() + logSuffix = '%02d%02d%02d_%02d%02d%02d' % (ltime[0] - 2000, ltime[1], ltime[2], ltime[3], ltime[4], ltime[5]) + + if not os.path.exists('user/logs/'): + os.mkdir('user/logs/') + self.notify.info('Made new directory to save logs.') + + logfile = os.path.join('user/logs', self.logPrefix + logSuffix + '.log') + + log = open(logfile, 'a') + logOut = LogAndOutput(sys.stdout, log) + logErr = LogAndOutput(sys.stderr, log) + sys.stdout = logOut + sys.stderr = logErr + + def getPlayToken(self): + return self.getValue('TTS_PLAYCOOKIE') + + def getGameServer(self): + return self.getValue('TTS_GAMESERVER') + + def getValue(self, key, default = None): + return os.environ.get(key, default) + + def setPandaErrorCode(self): + pass + + def setDisconnectDetails(self, disconnectCode, disconnectMsg): + self.disconnectCode = disconnectCode + self.disconnectMsg = disconnectMsg + + def setDisconnectDetailsNormal(self): + self.setDisconnectDetails(0, 'normal') diff --git a/toontown/launcher/__init__.py b/toontown/launcher/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/login/AvatarChoice.py b/toontown/login/AvatarChoice.py new file mode 100755 index 00000000..faf3665a --- /dev/null +++ b/toontown/login/AvatarChoice.py @@ -0,0 +1,202 @@ +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from direct.showbase import DirectObject +from toontown.toon import ToonDNA +from toontown.toon import ToonHead +from toontown.toontowngui import TTDialog +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal +NAME_ROTATIONS = (7, -11, 1, -5, 3.5, -5) +NAME_POSITIONS = ((0, 0, 0.26), + (-0.03, 0, 0.25), + (0, 0, 0.27), + (-0.03, 0, 0.25), + (0.03, 0, 0.26), + (0, 0, 0.26)) +DELETE_POSITIONS = ((0.187, 0, -0.26), + (0.31, 0, -0.167), + (0.231, 0, -0.241), + (0.314, 0, -0.186), + (0.243, 0, -0.233), + (0.28, 0, -0.207)) + +class AvatarChoice(DirectButton): + notify = DirectNotifyGlobal.directNotify.newCategory('AvatarChoice') + MODE_CREATE = 0 + MODE_CHOOSE = 1 + + def __init__(self, av = None, position = 0): + DirectButton.__init__(self, relief=None, text='', text_font=ToontownGlobals.getSignFont()) + self.initialiseoptions(AvatarChoice) + self.mode = None + if not av: + self.mode = AvatarChoice.MODE_CREATE + self.name = '' + self.dna = None + else: + self.mode = AvatarChoice.MODE_CHOOSE + self.name = av.name + self.dna = ToonDNA.ToonDNA(av.dna) + self.wantName = av.wantName + self.approvedName = av.approvedName + self.rejectedName = av.rejectedName + self.allowedName = av.allowedName + self.position = position + self.doneEvent = 'avChoicePanel-' + str(self.position) + self.deleteWithPasswordFrame = None + self.pickAToonGui = loader.loadModel('phase_3/models/gui/tt_m_gui_pat_mainGui') + self.buttonBgs = [] + self.buttonBgs.append(self.pickAToonGui.find('**/tt_t_gui_pat_squareRed')) + self.buttonBgs.append(self.pickAToonGui.find('**/tt_t_gui_pat_squareGreen')) + self.buttonBgs.append(self.pickAToonGui.find('**/tt_t_gui_pat_squarePurple')) + self.buttonBgs.append(self.pickAToonGui.find('**/tt_t_gui_pat_squareBlue')) + self.buttonBgs.append(self.pickAToonGui.find('**/tt_t_gui_pat_squarePink')) + self.buttonBgs.append(self.pickAToonGui.find('**/tt_t_gui_pat_squareYellow')) + self['image'] = self.buttonBgs[position] + self.setScale(1.01) + if self.mode is AvatarChoice.MODE_CREATE: + self['command'] = self.__handleCreate + self['text'] = (TTLocalizer.AvatarChoiceMakeAToon,) + self['text_pos'] = (0, 0) + self['text0_scale'] = 0.1 + self['text1_scale'] = TTLocalizer.ACmakeAToon + self['text2_scale'] = TTLocalizer.ACmakeAToon + self['text0_fg'] = (0, 1, 0.8, 0.5) + self['text1_fg'] = (0, 1, 0.8, 1) + self['text2_fg'] = (0.3, 1, 0.9, 1) + else: + self['command'] = self.__handleChoice + self['text'] = ('', TTLocalizer.AvatarChoicePlayThisToon, TTLocalizer.AvatarChoicePlayThisToon) + self['text_scale'] = TTLocalizer.ACplayThisToon + self['text_fg'] = (1, 0.9, 0.1, 1) + self.nameText = DirectLabel(parent=self, relief=None, scale=0.08, pos=NAME_POSITIONS[position], text=self.name, hpr=(0, 0, NAME_ROTATIONS[position]), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_wordwrap=8, text_font=ToontownGlobals.getToonFont(), state=DGG.DISABLED) + if self.approvedName != '': + self.nameText['text'] = self.approvedName + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.nameYourToonButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), text=(TTLocalizer.AvatarChoiceNameYourToon, TTLocalizer.AvatarChoiceNameYourToon, TTLocalizer.AvatarChoiceNameYourToon), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.15, text_pos=(0, 0.03), text_font=ToontownGlobals.getInterfaceFont(), pos=(-0.2, 0, -0.3), scale=0.45, image_scale=(2, 1, 3), command=self.__handleCreate) + guiButton.removeNode() + self.statusText = DirectLabel(parent=self, relief=None, scale=0.09, pos=(0, 0, -0.24), text='', text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_wordwrap=7.5, text_scale=TTLocalizer.ACstatusText, text_font=ToontownGlobals.getToonFont(), state=DGG.DISABLED) + if self.wantName != '': + self.nameYourToonButton.hide() + self.statusText['text'] = TTLocalizer.AvatarChoiceNameReview + elif self.approvedName != '': + self.nameYourToonButton.hide() + self.statusText['text'] = TTLocalizer.AvatarChoiceNameApproved + elif self.rejectedName != '': + self.nameYourToonButton.hide() + self.statusText['text'] = TTLocalizer.AvatarChoiceNameRejected + elif self.allowedName == 1: + self.nameYourToonButton.show() + self.statusText['text'] = '' + else: + self.nameYourToonButton.hide() + self.statusText['text'] = '' + self.head = hidden.attachNewNode('head') + self.head.setPosHprScale(0, 5, -0.1, 180, 0, 0, 0.24, 0.24, 0.24) + self.head.reparentTo(self.stateNodePath[0], 20) + self.head.instanceTo(self.stateNodePath[1], 20) + self.head.instanceTo(self.stateNodePath[2], 20) + self.headModel = ToonHead.ToonHead() + self.headModel.setupHead(self.dna, forGui=1) + self.headModel.reparentTo(self.head) + animalStyle = self.dna.getAnimal() + bodyScale = ToontownGlobals.toonBodyScales[animalStyle] + self.headModel.setScale(bodyScale / 0.75) + self.headModel.startBlink() + self.headModel.startLookAround() + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') + self.deleteButton = DirectButton(parent=self, image=(trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_RLVR')), text=('', TTLocalizer.AvatarChoiceDelete, TTLocalizer.AvatarChoiceDelete), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.15, text_pos=(0, -0.1), text_font=ToontownGlobals.getInterfaceFont(), relief=None, pos=DELETE_POSITIONS[position], scale=0.45, command=self.__handleDelete) + trashcanGui.removeNode() + self.resetFrameSize() + self.avForLogging = None + if av: + self.avForLogging = str(av.id) + else: + self.avForLogging = None + return + + def destroy(self): + loader.unloadModel('phase_3/models/gui/pick_a_toon_gui') + self.pickAToonGui.removeNode() + del self.pickAToonGui + del self.dna + if self.mode == AvatarChoice.MODE_CREATE: + pass + else: + self.headModel.stopBlink() + self.headModel.stopLookAroundNow() + self.headModel.delete() + self.head.removeNode() + del self.head + del self.headModel + del self.nameText + del self.statusText + self.deleteButton.destroy() + del self.deleteButton + self.nameYourToonButton.destroy() + del self.nameYourToonButton + loader.unloadModel('phase_3/models/gui/trashcan_gui') + loader.unloadModel('phase_3/models/gui/quit_button') + DirectFrame.destroy(self) + if self.deleteWithPasswordFrame: + self.deleteWithPasswordFrame.destroy() + + def __handleChoice(self): + cleanupDialog('globalDialog') + messenger.send(self.doneEvent, ['chose', self.position]) + + def __handleCreate(self): + cleanupDialog('globalDialog') + messenger.send(self.doneEvent, ['create', self.position]) + + def __handleDelete(self): + cleanupDialog('globalDialog') + self.verify = TTDialog.TTGlobalDialog(doneEvent='verifyDone', message=TTLocalizer.AvatarChoiceDeleteConfirm % self.name, style=TTDialog.TwoChoice) + self.verify.show() + self.accept('verifyDone', self.__handleVerifyDelete) + + def __handleVerifyDelete(self): + status = self.verify.doneStatus + self.ignore('verifyDone') + self.verify.cleanup() + del self.verify + if status == 'ok': + self.verifyDeleteWithPassword() + + def verifyDeleteWithPassword(self): + deleteText = TTLocalizer.AvatarChoiceDeleteConfirmText % {'name': self.name} + if self.deleteWithPasswordFrame == None: + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + nameBalloon = loader.loadModel('phase_3/models/props/chatbox_input') + okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelButtonImage = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.deleteWithPasswordFrame = DirectFrame(pos=(0.0, 0.1, 0.2), parent=aspect2dp, relief=None, image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.4, 1.0, 1.0), text=deleteText, text_wordwrap=19, text_scale=TTLocalizer.ACdeleteWithPasswordFrame, text_pos=(0, 0.25), textMayChange=1, sortOrder=NO_FADE_SORT_INDEX) + self.deleteWithPasswordFrame.hide() + self.passwordEntry = DirectEntry(parent=self.deleteWithPasswordFrame, relief=None, image=nameBalloon, image1_color=(0.8, 0.8, 0.8, 1.0), scale=0.064, pos=(-0.3, 0.0, -0.2), width=10, numLines=1, focus=1, cursorKeys=1, command=self.__handleDeleteWithConfirmOK) + DirectButton(parent=self.deleteWithPasswordFrame, image=okButtonImage, relief=None, text=TTLocalizer.AvatarChoiceDeletePasswordOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(-0.22, 0.0, -0.35), command=self.__handleDeleteWithConfirmOK) + DirectLabel(parent=self.deleteWithPasswordFrame, relief=None, pos=(0, 0, 0.35), text=TTLocalizer.AvatarChoiceDeletePasswordTitle, textMayChange=0, text_scale=0.08) + DirectButton(parent=self.deleteWithPasswordFrame, image=cancelButtonImage, relief=None, text=TTLocalizer.AvatarChoiceDeletePasswordCancel, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=1, pos=(0.2, 0.0, -0.35), command=self.__handleDeleteWithPasswordCancel) + buttons.removeNode() + nameBalloon.removeNode() + else: + self.deleteWithPasswordFrame['text'] = deleteText + self.passwordEntry['focus'] = 1 + self.passwordEntry.enterText('') + base.transitions.fadeScreen(0.5) + self.deleteWithPasswordFrame.show() + return + + def __handleDeleteWithConfirmOK(self, *args): + if self.passwordEntry.get().lower() == self.name.lower(): + self.deleteWithPasswordFrame.hide() + base.transitions.noTransitions() + messenger.send(self.doneEvent, ['delete', self.position]) + else: + self.deleteWithPasswordFrame['text'] = TTLocalizer.AvatarChoiceDeleteWrongConfirm % {'name': self.name} + self.passwordEntry['focus'] = 1 + self.passwordEntry.enterText('') + + def __handleDeleteWithPasswordCancel(self): + self.deleteWithPasswordFrame.hide() + base.transitions.noTransitions() diff --git a/toontown/login/AvatarChooser.py b/toontown/login/AvatarChooser.py new file mode 100755 index 00000000..04b6d61a --- /dev/null +++ b/toontown/login/AvatarChooser.py @@ -0,0 +1,234 @@ +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from toontown.language import LanguageSelector +from direct.fsm import StateData +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +import random, AvatarChoice + +MAX_AVATARS = 6 +POSITIONS = (Vec3(-0.840167, 0, 0.359333), + Vec3(0.00933349, 0, 0.306533), + Vec3(0.862, 0, 0.3293), + Vec3(-0.863554, 0, -0.445659), + Vec3(0.00999999, 0, -0.5181), + Vec3(0.864907, 0, -0.445659)) +COLORS = (Vec4(0.917, 0.164, 0.164, 1), + Vec4(0.152, 0.75, 0.258, 1), + Vec4(0.598, 0.402, 0.875, 1), + Vec4(0.133, 0.59, 0.977, 1), + Vec4(0.895, 0.348, 0.602, 1), + Vec4(0.977, 0.816, 0.133, 1)) +chooser_notify = DirectNotifyGlobal.directNotify.newCategory('AvatarChooser') + +class AvatarChooser(StateData.StateData): + + def __init__(self, avatarList, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.choice = None + self.avatarList = avatarList + return + + def enter(self): + self.notify.info('AvatarChooser.enter') + if self.isLoaded == 0: + self.load() + base.disableMouse() + self.title.reparentTo(aspect2d) + self.quitButton.show() + if config.GetBool('want-language-selection', False): + self.languageButton.show() + self.pickAToonBG.setBin('background', 1) + self.pickAToonBG.reparentTo(aspect2d) + base.setBackgroundColor(Vec4(0.145, 0.368, 0.78, 1)) + choice = config.GetInt('auto-avatar-choice', -1) + for panel in self.panelList: + panel.show() + self.accept(panel.doneEvent, self.__handlePanelDone) + if panel.position == choice and panel.mode == AvatarChoice.AvatarChoice.MODE_CHOOSE: + self.__handlePanelDone('chose', panelChoice=choice) + + def exit(self): + if self.isLoaded == 0: + return None + for panel in self.panelList: + panel.hide() + + self.ignoreAll() + self.title.reparentTo(hidden) + self.quitButton.hide() + self.languageButton.hide() + self.pickAToonBG.reparentTo(hidden) + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + return None + + def load(self): + if self.isLoaded == 1: + return None + gui = loader.loadModel('phase_3/models/gui/pick_a_toon_gui') + gui2 = loader.loadModel('phase_3/models/gui/quit_button') + newGui = loader.loadModel('phase_3/models/gui/tt_m_gui_pat_mainGui') + self.pickAToonBG = newGui.find('**/tt_t_gui_pat_background') + self.pickAToonBG.reparentTo(hidden) + self.pickAToonBG.setPos(0.0, 2.73, 0.0) + self.pickAToonBG.setScale(1, 1, 1) + self.title = OnscreenText(TTLocalizer.AvatarChooserPickAToon, scale=TTLocalizer.ACtitle, parent=hidden, font=ToontownGlobals.getSignFont(), fg=(1, 0.9, 0.1, 1), pos=(0.0, 0.82)) + quitHover = gui.find('**/QuitBtn_RLVR') + self.quitButton = DirectButton(image=(quitHover, quitHover, quitHover), relief=None, text=TTLocalizer.AvatarChooserQuit, text_font=ToontownGlobals.getSignFont(), text_fg=(0.977, 0.816, 0.133, 1), text_pos=TTLocalizer.ACquitButtonPos, text_scale=TTLocalizer.ACquitButton, image_scale=1, image1_scale=1.05, image2_scale=1.05, scale=1.05, pos=(-0.25, 0, 0.075), command=self.__handleQuit) + self.quitButton.reparentTo(base.a2dBottomRight) + self.languageButton = DirectButton(relief=None, image=(quitHover, quitHover, quitHover), text=TTLocalizer.LanguageButtonText, text_font=ToontownGlobals.getSignFont(), text_fg=(0.977, 0.816, 0.133, 1), text_scale=TTLocalizer.AClanguageButton, text_pos=(0, -0.025), pos=(0.25, 0, 0.075), image_scale=1.05, image1_scale=1.05, image2_scale=1.05, scale=1.05, command=self.openLanguageGui) + self.languageButton.reparentTo(base.a2dBottomLeft) + self.languageButton.hide() + gui.removeNode() + gui2.removeNode() + newGui.removeNode() + self.panelList = [] + used_position_indexs = [] + for av in self.avatarList: + panel = AvatarChoice.AvatarChoice(av, position=av.position) + panel.setPos(POSITIONS[av.position]) + used_position_indexs.append(av.position) + self.panelList.append(panel) + + for panelNum in xrange(0, MAX_AVATARS): + if panelNum not in used_position_indexs: + panel = AvatarChoice.AvatarChoice(position=panelNum) + panel.setPos(POSITIONS[panelNum]) + self.panelList.append(panel) + + if len(self.avatarList) > 0: + self.initLookAtInfo() + self.isLoaded = 1 + + def getLookAtPosition(self, toonHead, toonidx): + lookAtChoice = random.random() + if len(self.used_panel_indexs) == 1: + lookFwdPercent = 0.33 + lookAtOthersPercent = 0 + else: + lookFwdPercent = 0.2 + if len(self.used_panel_indexs) == 2: + lookAtOthersPercent = 0.4 + else: + lookAtOthersPercent = 0.65 + lookRandomPercent = 1.0 - lookFwdPercent - lookAtOthersPercent + if lookAtChoice < lookFwdPercent: + self.IsLookingAt[toonidx] = 'f' + return Vec3(0, 1.5, 0) + elif lookAtChoice < lookRandomPercent + lookFwdPercent or len(self.used_panel_indexs) == 1: + self.IsLookingAt[toonidx] = 'r' + return toonHead.getRandomForwardLookAtPoint() + else: + other_toon_idxs = [] + for i in xrange(len(self.IsLookingAt)): + if self.IsLookingAt[i] == toonidx: + other_toon_idxs.append(i) + + if len(other_toon_idxs) == 1: + IgnoreStarersPercent = 0.4 + else: + IgnoreStarersPercent = 0.2 + NoticeStarersPercent = 0.5 + bStareTargetTurnsToMe = 0 + if len(other_toon_idxs) == 0 or random.random() < IgnoreStarersPercent: + other_toon_idxs = [] + for i in self.used_panel_indexs: + if i != toonidx: + other_toon_idxs.append(i) + + if random.random() < NoticeStarersPercent: + bStareTargetTurnsToMe = 1 + if len(other_toon_idxs) == 0: + return toonHead.getRandomForwardLookAtPoint() + else: + lookingAtIdx = random.choice(other_toon_idxs) + if bStareTargetTurnsToMe: + self.IsLookingAt[lookingAtIdx] = toonidx + otherToonHead = None + for panel in self.panelList: + if panel.position == lookingAtIdx: + otherToonHead = panel.headModel + + otherToonHead.doLookAroundToStareAt(otherToonHead, self.getLookAtToPosVec(lookingAtIdx, toonidx)) + self.IsLookingAt[toonidx] = lookingAtIdx + return self.getLookAtToPosVec(toonidx, lookingAtIdx) + return + + def getLookAtToPosVec(self, fromIdx, toIdx): + x = -(POSITIONS[toIdx][0] - POSITIONS[fromIdx][0]) + y = POSITIONS[toIdx][1] - POSITIONS[fromIdx][1] + z = POSITIONS[toIdx][2] - POSITIONS[fromIdx][2] + return Vec3(x, y, z) + + def initLookAtInfo(self): + self.used_panel_indexs = [] + for panel in self.panelList: + if panel.dna != None: + self.used_panel_indexs.append(panel.position) + + if len(self.used_panel_indexs) == 0: + return + self.IsLookingAt = [] + for i in xrange(MAX_AVATARS): + self.IsLookingAt.append('f') + + for panel in self.panelList: + if panel.dna != None: + panel.headModel.setLookAtPositionCallbackArgs((self, panel.headModel, panel.position)) + + return + + def unload(self): + if self.isLoaded == 0: + return None + cleanupDialog('globalDialog') + for panel in self.panelList: + panel.destroy() + + del self.panelList + self.title.removeNode() + del self.title + self.quitButton.destroy() + del self.quitButton + self.languageButton.destroy() + del self.languageButton + self.pickAToonBG.removeNode() + del self.pickAToonBG + del self.avatarList + self.ignoreAll() + self.isLoaded = 0 + ModelPool.garbageCollect() + TexturePool.garbageCollect() + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + return None + + def __handlePanelDone(self, panelDoneStatus, panelChoice = 0): + self.doneStatus = {} + self.doneStatus['mode'] = panelDoneStatus + self.choice = panelChoice + if panelDoneStatus == 'chose': + self.__handleChoice() + elif panelDoneStatus == 'delete': + self.__handleDelete() + elif panelDoneStatus == 'create': + self.__handleChoice() + + def __handleChoice(self): + base.transitions.fadeOut(finishIval=EventInterval(self.doneEvent, [self.doneStatus])) + + def __handleDelete(self): + messenger.send(self.doneEvent, [self.doneStatus]) + + def __handleQuit(self): + cleanupDialog('globalDialog') + self.doneStatus = {'mode': 'exit'} + messenger.send(self.doneEvent, [self.doneStatus]) + + def getChoice(self): + return self.choice + + def openLanguageGui(self): + self.exit() + LanguageSelector.LanguageSelector(self.enter).create() diff --git a/toontown/login/__init__.py b/toontown/login/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/makeatoon/BodyShop.py b/toontown/makeatoon/BodyShop.py new file mode 100755 index 00000000..9188e360 --- /dev/null +++ b/toontown/makeatoon/BodyShop.py @@ -0,0 +1,328 @@ +from panda3d.core import * +from toontown.toon import ToonDNA +from direct.fsm import StateData +from direct.gui.DirectGui import * +from MakeAToonGlobals import * +import random +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal +import ShuffleButton + +class BodyShop(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('BodyShop') + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.toon = None + self.torsoChoice = 0 + self.legChoice = 0 + self.headChoice = 0 + self.speciesChoice = 0 + + def enter(self, toon, shopsVisited = []): + base.disableMouse() + self.toon = toon + self.dna = self.toon.getStyle() + gender = self.toon.style.getGender() + self.speciesStart = self.getSpeciesStart() + self.speciesChoice = self.speciesStart + self.headStart = 0 + self.headChoice = ToonDNA.toonHeadTypes.index(self.dna.head) - ToonDNA.getHeadStartIndex(self.species) + self.torsoStart = 0 + self.torsoChoice = ToonDNA.toonTorsoTypes.index(self.dna.torso) % 3 + self.legStart = 0 + self.legChoice = ToonDNA.toonLegTypes.index(self.dna.legs) + if CLOTHESSHOP in shopsVisited: + self.clothesPicked = 1 + else: + self.clothesPicked = 0 + self.clothesPicked = 1 + if gender == 'm' or ToonDNA.GirlBottoms[self.dna.botTex][1] == ToonDNA.SHORTS: + torsoStyle = 's' + torsoPool = ToonDNA.toonTorsoTypes[:3] + else: + torsoStyle = 'd' + torsoPool = ToonDNA.toonTorsoTypes[3:6] + self.__swapSpecies(0) + self.__swapHead(0) + self.__swapTorso(0) + self.__swapLegs(0) + choicePool = [ToonDNA.toonHeadTypes, torsoPool, ToonDNA.toonLegTypes] + self.shuffleButton.setChoicePool(choicePool) + self.accept(self.shuffleFetchMsg, self.changeBody) + self.acceptOnce('last', self.__handleBackward) + self.accept('next', self.__handleForward) + self.acceptOnce('MAT-newToonCreated', self.shuffleButton.cleanHistory) + + def getSpeciesStart(self): + for species in ToonDNA.toonSpeciesTypes: + if species == self.dna.head[0]: + self.species = species + return ToonDNA.toonSpeciesTypes.index(species) + + def showButtons(self): + self.parentFrame.show() + + def hideButtons(self): + self.parentFrame.hide() + + def exit(self): + try: + del self.toon + except: + self.notify.warning('BodyShop: toon not found') + + self.hideButtons() + self.ignore('last') + self.ignore('next') + self.ignore('enter') + self.ignore(self.shuffleFetchMsg) + + def load(self): + self.gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + guiRArrowUp = self.gui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowDown = self.gui.find('**/tt_t_gui_mat_arrowDown') + guiRArrowRollover = self.gui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowDisabled = self.gui.find('**/tt_t_gui_mat_arrowDisabled') + shuffleFrame = self.gui.find('**/tt_t_gui_mat_shuffleFrame') + shuffleArrowUp = self.gui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDown = self.gui.find('**/tt_t_gui_mat_shuffleArrowDown') + shuffleArrowRollover = self.gui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDisabled = self.gui.find('**/tt_t_gui_mat_shuffleArrowDisabled') + self.parentFrame = DirectFrame(relief=DGG.RAISED, pos=(0.98, 0, 0.416), frameColor=(1, 0, 0, 0)) + self.parentFrame.setPos(-0.36, 0, -0.5) + self.parentFrame.reparentTo(base.a2dTopRight) + self.speciesFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.073), hpr=(0, 0, 0), scale=1.3, frameColor=(1, 1, 1, 1), text='Species', text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.speciesLButton = DirectButton(parent=self.speciesFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapSpecies, extraArgs=[-1]) + self.speciesRButton = DirectButton(parent=self.speciesFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapSpecies, extraArgs=[1]) + self.headFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.3), hpr=(0, 0, 2), scale=0.9, frameColor=(1, 1, 1, 1), text=TTLocalizer.BodyShopHead, text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.headLButton = DirectButton(parent=self.headFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapHead, extraArgs=[-1]) + self.headRButton = DirectButton(parent=self.headFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapHead, extraArgs=[1]) + self.bodyFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonScale, relief=None, pos=(0, 0, -0.5), hpr=(0, 0, -2), scale=0.9, frameColor=(1, 1, 1, 1), text=TTLocalizer.BodyShopBody, text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.torsoLButton = DirectButton(parent=self.bodyFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapTorso, extraArgs=[-1]) + self.torsoRButton = DirectButton(parent=self.bodyFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapTorso, extraArgs=[1]) + self.legsFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.7), hpr=(0, 0, 3), scale=0.9, frameColor=(1, 1, 1, 1), text=TTLocalizer.BodyShopLegs, text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.legLButton = DirectButton(parent=self.legsFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapLegs, extraArgs=[-1]) + self.legRButton = DirectButton(parent=self.legsFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapLegs, extraArgs=[1]) + self.parentFrame.hide() + self.shuffleFetchMsg = 'BodyShopShuffle' + self.shuffleButton = ShuffleButton.ShuffleButton(self, self.shuffleFetchMsg) + return + + def unload(self): + self.gui.removeNode() + del self.gui + self.parentFrame.destroy() + self.speciesFrame.destroy() + self.headFrame.destroy() + self.bodyFrame.destroy() + self.legsFrame.destroy() + self.speciesLButton.destroy() + self.speciesRButton.destroy() + self.headLButton.destroy() + self.headRButton.destroy() + self.torsoLButton.destroy() + self.torsoRButton.destroy() + self.legLButton.destroy() + self.legRButton.destroy() + del self.parentFrame + del self.speciesFrame + del self.headFrame + del self.bodyFrame + del self.legsFrame + del self.speciesLButton + del self.speciesRButton + del self.headLButton + del self.headRButton + del self.torsoLButton + del self.torsoRButton + del self.legLButton + del self.legRButton + self.shuffleButton.unload() + self.ignore('MAT-newToonCreated') + + def __swapTorso(self, offset): + gender = self.toon.style.getGender() + if not self.clothesPicked: + length = len(ToonDNA.toonTorsoTypes[6:]) + torsoOffset = 6 + elif gender == 'm': + length = len(ToonDNA.toonTorsoTypes[:3]) + torsoOffset = 0 + if self.toon.style.topTex not in ToonDNA.MakeAToonBoyShirts: + randomShirt = ToonDNA.getRandomTop(gender, ToonDNA.MAKE_A_TOON) + shirtTex, shirtColor, sleeveTex, sleeveColor = randomShirt + self.toon.style.topTex = shirtTex + self.toon.style.topTexColor = shirtColor + self.toon.style.sleeveTex = sleeveTex + self.toon.style.sleeveTexColor = sleeveColor + if self.toon.style.botTex not in ToonDNA.MakeAToonBoyBottoms: + botTex, botTexColor = ToonDNA.getRandomBottom(gender, ToonDNA.MAKE_A_TOON) + self.toon.style.botTex = botTex + self.toon.style.botTexColor = botTexColor + else: + length = len(ToonDNA.toonTorsoTypes[3:6]) + if self.toon.style.torso[1] == 'd': + torsoOffset = 3 + else: + torsoOffset = 0 + if self.toon.style.topTex not in ToonDNA.MakeAToonGirlShirts: + randomShirt = ToonDNA.getRandomTop(gender, ToonDNA.MAKE_A_TOON) + shirtTex, shirtColor, sleeveTex, sleeveColor = randomShirt + self.toon.style.topTex = shirtTex + self.toon.style.topTexColor = shirtColor + self.toon.style.sleeveTex = sleeveTex + self.toon.style.sleeveTexColor = sleeveColor + if self.toon.style.botTex not in ToonDNA.MakeAToonGirlBottoms: + if self.toon.style.torso[1] == 'd': + botTex, botTexColor = ToonDNA.getRandomBottom(gender, ToonDNA.MAKE_A_TOON, girlBottomType=ToonDNA.SKIRT) + self.toon.style.botTex = botTex + self.toon.style.botTexColor = botTexColor + torsoOffset = 3 + else: + botTex, botTexColor = ToonDNA.getRandomBottom(gender, ToonDNA.MAKE_A_TOON, girlBottomType=ToonDNA.SHORTS) + self.toon.style.botTex = botTex + self.toon.style.botTexColor = botTexColor + torsoOffset = 0 + self.torsoChoice = (self.torsoChoice + offset) % length + self.__updateScrollButtons(self.torsoChoice, length, self.torsoStart, self.torsoLButton, self.torsoRButton) + torso = ToonDNA.toonTorsoTypes[torsoOffset + self.torsoChoice] + self.dna.torso = torso + self.toon.swapToonTorso(torso) + self.toon.loop('neutral', 0) + self.toon.swapToonColor(self.dna) + + def __swapLegs(self, offset): + length = len(ToonDNA.toonLegTypes) + self.legChoice = (self.legChoice + offset) % length + self.notify.debug('self.legChoice=%d, length=%d, self.legStart=%d' % (self.legChoice, length, self.legStart)) + self.__updateScrollButtons(self.legChoice, length, self.legStart, self.legLButton, self.legRButton) + newLeg = ToonDNA.toonLegTypes[self.legChoice] + self.dna.legs = newLeg + self.toon.swapToonLegs(newLeg) + self.toon.loop('neutral', 0) + self.toon.swapToonColor(self.dna) + + def __swapHead(self, offset): + self.headList = ToonDNA.getHeadList(self.species) + length = len(self.headList) + self.headChoice = (self.headChoice + offset) % length + self.__updateHead() + + def __swapSpecies(self, offset): + length = len(ToonDNA.toonSpeciesTypes) + self.speciesChoice = (self.speciesChoice + offset) % length + self.__updateScrollButtons(self.speciesChoice, length, self.speciesStart, self.speciesLButton, self.speciesRButton) + self.species = ToonDNA.toonSpeciesTypes[self.speciesChoice] + self.headList = ToonDNA.getHeadList(self.species) + self.__changeSpeciesName(self.species) + maxHeadChoice = len(self.headList) - 1 + if self.headChoice > maxHeadChoice: + self.headChoice = maxHeadChoice + self.__updateHead() + + def __updateHead(self): + self.__updateScrollButtons(self.headChoice, len(self.headList), self.headStart, self.headLButton, self.headRButton) + headIndex = ToonDNA.getHeadStartIndex(self.species) + self.headChoice + newHead = ToonDNA.toonHeadTypes[headIndex] + self.dna.head = newHead + self.toon.swapToonHead(newHead) + self.toon.loop('neutral', 0) + self.toon.swapToonColor(self.dna) + + def __updateScrollButtons(self, choice, length, start, lButton, rButton): + if choice == (start - 1) % length: + rButton['state'] = DGG.DISABLED + elif choice != (start - 1) % length: + rButton['state'] = DGG.NORMAL + if choice == start % length: + lButton['state'] = DGG.DISABLED + elif choice != start % length: + lButton['state'] = DGG.NORMAL + if lButton['state'] == DGG.DISABLED and rButton['state'] == DGG.DISABLED: + self.notify.info('Both buttons got disabled! Doing fallback code. choice%d, length=%d, start=%d, lButton=%s, rButton=%s' % (choice, + length, + start, + lButton, + rButton)) + if choice == start % length: + lButton['state'] = DGG.DISABLED + rButton['state'] = DGG.NORMAL + elif choice == (start - 1) % length: + lButton['state'] = DGG.NORMAL + rButton['state'] = DGG.DISABLED + else: + lButton['state'] = DGG.NORMAL + rButton['state'] = DGG.NORMAL + + def __handleForward(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) + + def __handleBackward(self): + self.doneStatus = 'last' + messenger.send(self.doneEvent) + + def changeBody(self): + newChoice = self.shuffleButton.getCurrChoice() + newHead = newChoice[0] + newSpeciesIndex = ToonDNA.toonSpeciesTypes.index(ToonDNA.getSpecies(newHead)) + newHeadIndex = ToonDNA.toonHeadTypes.index(newHead) - ToonDNA.getHeadStartIndex(ToonDNA.getSpecies(newHead)) + newTorsoIndex = ToonDNA.toonTorsoTypes.index(newChoice[1]) + newLegsIndex = ToonDNA.toonLegTypes.index(newChoice[2]) + oldHead = self.toon.style.head + oldSpeciesIndex = ToonDNA.toonSpeciesTypes.index(ToonDNA.getSpecies(oldHead)) + oldHeadIndex = ToonDNA.toonHeadTypes.index(oldHead) - ToonDNA.getHeadStartIndex(ToonDNA.getSpecies(oldHead)) + oldTorsoIndex = ToonDNA.toonTorsoTypes.index(self.toon.style.torso) + oldLegsIndex = ToonDNA.toonLegTypes.index(self.toon.style.legs) + self.__swapSpecies(newSpeciesIndex - oldSpeciesIndex) + self.__swapHead(newHeadIndex - oldHeadIndex) + self.__swapTorso(newTorsoIndex - oldTorsoIndex) + self.__swapLegs(newLegsIndex - oldLegsIndex) + + def getCurrToonSetting(self): + return [self.toon.style.head, self.toon.style.torso, self.toon.style.legs] + + def __changeSpeciesName(self, species): + if species == 'd': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['dog'] + elif species == 'c': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['cat'] + elif species == 'm': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['mouse'] + elif species == 'h': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['horse'] + elif species == 'r': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['rabbit'] + elif species == 'f': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['duck'] + elif species == 'p': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['monkey'] + elif species == 'b': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['bear'] + elif species == 's': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['pig'] diff --git a/toontown/makeatoon/ClothesGUI.py b/toontown/makeatoon/ClothesGUI.py new file mode 100755 index 00000000..1d4ea4b5 --- /dev/null +++ b/toontown/makeatoon/ClothesGUI.py @@ -0,0 +1,219 @@ +from panda3d.core import * +from toontown.toon import ToonDNA +from direct.fsm import StateData +from direct.gui.DirectGui import * +from MakeAToonGlobals import * +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal +import ShuffleButton +import random +CLOTHES_MAKETOON = 0 +CLOTHES_TAILOR = 1 +CLOTHES_CLOSET = 2 + +class ClothesGUI(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('ClothesGUI') + + def __init__(self, type, doneEvent, swapEvent = None): + StateData.StateData.__init__(self, doneEvent) + self.type = type + self.toon = None + self.swapEvent = swapEvent + self.gender = '?' + self.girlInShorts = 0 + self.swappedTorso = 0 + return + + def load(self): + self.matGui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + guiRArrowUp = self.matGui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowRollover = self.matGui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowDown = self.matGui.find('**/tt_t_gui_mat_arrowDown') + guiRArrowDisabled = self.matGui.find('**/tt_t_gui_mat_arrowDisabled') + self.shuffleFrame = self.matGui.find('**/tt_t_gui_mat_shuffleFrame') + shuffleArrowUp = self.matGui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDown = self.matGui.find('**/tt_t_gui_mat_shuffleArrowDown') + shuffleArrowRollover = self.matGui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDisabled = self.matGui.find('**/tt_t_gui_mat_shuffleArrowDisabled') + self.parentFrame = DirectFrame(relief=DGG.RAISED, pos=(0.98, 0, 0.416), frameColor=(1, 0, 0, 0)) + self.parentFrame.setPos(-0.36, 0, -0.5) + self.parentFrame.reparentTo(base.a2dTopRight) + self.shirtFrame = DirectFrame(parent=self.parentFrame, image=self.shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.4), hpr=(0, 0, 3), scale=1.2, frameColor=(1, 1, 1, 1), text=TTLocalizer.ClothesShopShirt, text_scale=0.0575, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.topLButton = DirectButton(parent=self.shirtFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.swapTop, extraArgs=[-1]) + self.topRButton = DirectButton(parent=self.shirtFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.swapTop, extraArgs=[1]) + self.bottomFrame = DirectFrame(parent=self.parentFrame, image=self.shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.65), hpr=(0, 0, -2), scale=1.2, frameColor=(1, 1, 1, 1), text=TTLocalizer.ColorShopToon, text_scale=0.0575, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.bottomLButton = DirectButton(parent=self.bottomFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.swapBottom, extraArgs=[-1]) + self.bottomRButton = DirectButton(parent=self.bottomFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.swapBottom, extraArgs=[1]) + self.parentFrame.hide() + self.shuffleFetchMsg = 'ClothesShopShuffle' + self.shuffleButton = ShuffleButton.ShuffleButton(self, self.shuffleFetchMsg) + + def unload(self): + self.matGui.removeNode() + del self.matGui + self.parentFrame.destroy() + self.shirtFrame.destroy() + self.bottomFrame.destroy() + self.topLButton.destroy() + self.topRButton.destroy() + self.bottomLButton.destroy() + self.bottomRButton.destroy() + del self.shuffleFrame + del self.parentFrame + del self.shirtFrame + del self.bottomFrame + del self.topLButton + del self.topRButton + del self.bottomLButton + del self.bottomRButton + self.shuffleButton.unload() + self.ignore('MAT-newToonCreated') + + def showButtons(self): + self.parentFrame.show() + + def hideButtons(self): + self.parentFrame.hide() + + def enter(self, toon): + self.notify.debug('enter') + base.disableMouse() + self.toon = toon + self.setupScrollInterface() + if not self.type == CLOTHES_TAILOR: + currTop = (self.toon.style.topTex, + self.toon.style.topTexColor, + self.toon.style.sleeveTex, + self.toon.style.sleeveTexColor) + currTopIndex = self.tops.index(currTop) + self.swapTop(currTopIndex - self.topChoice) + currBottom = (self.toon.style.botTex, self.toon.style.botTexColor) + currBottomIndex = self.bottoms.index(currBottom) + self.swapBottom(currBottomIndex - self.bottomChoice) + choicePool = [self.tops, self.bottoms] + self.shuffleButton.setChoicePool(choicePool) + self.accept(self.shuffleFetchMsg, self.changeClothes) + self.acceptOnce('MAT-newToonCreated', self.shuffleButton.cleanHistory) + + def exit(self): + try: + del self.toon + except: + self.notify.warning('ClothesGUI: toon not found') + + self.hideButtons() + self.ignore('enter') + self.ignore('next') + self.ignore('last') + self.ignore(self.shuffleFetchMsg) + + def setupButtons(self): + self.girlInShorts = 0 + if self.gender == 'f': + if self.bottomChoice == -1: + botTex = self.bottoms[0][0] + else: + botTex = self.bottoms[self.bottomChoice][0] + if ToonDNA.GirlBottoms[botTex][1] == ToonDNA.SHORTS: + self.girlInShorts = 1 + if self.toon.style.getGender() == 'm': + self.bottomFrame['text'] = TTLocalizer.ClothesShopShorts + else: + self.bottomFrame['text'] = TTLocalizer.ClothesShopBottoms + self.acceptOnce('last', self.__handleBackward) + self.acceptOnce('next', self.__handleForward) + return None + + def swapTop(self, offset): + length = len(self.tops) + self.topChoice += offset + if self.topChoice <= 0: + self.topChoice = 0 + self.updateScrollButtons(self.topChoice, length, 0, self.topLButton, self.topRButton) + if self.topChoice < 0 or self.topChoice >= len(self.tops) or len(self.tops[self.topChoice]) != 4: + self.notify.warning('topChoice index is out of range!') + return None + self.toon.style.topTex = self.tops[self.topChoice][0] + self.toon.style.topTexColor = self.tops[self.topChoice][1] + self.toon.style.sleeveTex = self.tops[self.topChoice][2] + self.toon.style.sleeveTexColor = self.tops[self.topChoice][3] + self.toon.generateToonClothes() + if self.swapEvent != None: + messenger.send(self.swapEvent) + messenger.send('wakeup') + + def swapBottom(self, offset): + length = len(self.bottoms) + self.bottomChoice += offset + if self.bottomChoice <= 0: + self.bottomChoice = 0 + self.updateScrollButtons(self.bottomChoice, length, 0, self.bottomLButton, self.bottomRButton) + if self.bottomChoice < 0 or self.bottomChoice >= len(self.bottoms) or len(self.bottoms[self.bottomChoice]) != 2: + self.notify.warning('bottomChoice index is out of range!') + return None + self.toon.style.botTex = self.bottoms[self.bottomChoice][0] + self.toon.style.botTexColor = self.bottoms[self.bottomChoice][1] + if self.toon.generateToonClothes() == 1: + self.toon.loop('neutral', 0) + self.swappedTorso = 1 + if self.swapEvent != None: + messenger.send(self.swapEvent) + messenger.send('wakeup') + + def updateScrollButtons(self, choice, length, startTex, lButton, rButton): + if choice >= length - 1: + rButton['state'] = DGG.DISABLED + else: + rButton['state'] = DGG.NORMAL + if choice <= 0: + lButton['state'] = DGG.DISABLED + else: + lButton['state'] = DGG.NORMAL + + def __handleForward(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) + + def __handleBackward(self): + self.doneStatus = 'last' + messenger.send(self.doneEvent) + + def resetClothes(self, style): + if self.toon: + self.toon.style.makeFromNetString(style.makeNetString()) + if self.swapEvent != None and self.swappedTorso == 1: + self.toon.swapToonTorso(self.toon.style.torso, genClothes=0) + self.toon.generateToonClothes() + self.toon.loop('neutral', 0) + return + + def changeClothes(self): + self.notify.debug('Entering changeClothes') + newChoice = self.shuffleButton.getCurrChoice() + if newChoice[0] in self.tops: + newTopIndex = self.tops.index(newChoice[0]) + else: + newTopIndex = self.topChoice + if newChoice[1] in self.bottoms: + newBottomIndex = self.bottoms.index(newChoice[1]) + else: + newBottomIndex = self.bottomChoice + oldTopIndex = self.topChoice + oldBottomIndex = self.bottomChoice + self.swapTop(newTopIndex - oldTopIndex) + self.swapBottom(newBottomIndex - oldBottomIndex) + + def getCurrToonSetting(self): + return [self.tops[self.topChoice], self.bottoms[self.bottomChoice]] diff --git a/toontown/makeatoon/ColorShop.py b/toontown/makeatoon/ColorShop.py new file mode 100755 index 00000000..c9bf9adc --- /dev/null +++ b/toontown/makeatoon/ColorShop.py @@ -0,0 +1,309 @@ +from panda3d.core import * +from toontown.toon import ToonDNA +from direct.fsm import StateData +from direct.gui.DirectGui import * +from MakeAToonGlobals import * +from toontown.toonbase import TTLocalizer, ToontownGlobals +import ShuffleButton +import random, colorsys +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task + +class ColorShop(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('ColorShop') + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.toon = None + self.colorAll = 1 + + def getColorList(self): + return ToonDNA.matColorsList + + def enter(self, toon, shopsVisited = []): + base.disableMouse() + self.toon = toon + self.dna = toon.getStyle() + colorList = self.getColorList() + self.allParts = (TTLocalizer.ColorAll, TTLocalizer.ColorShopHead, TTLocalizer.ColorShopBody, TTLocalizer.ColorShopLegs) + if not hasattr(self, 'headChoice'): + self.headChoice = colorList.index(self.dna.headColor) + self.allChoice = self.headChoice + self.armChoice = colorList.index(self.dna.armColor) + self.legChoice = colorList.index(self.dna.legColor) + self.partChoice = 0 + + self.startColor = 0 + self.acceptOnce('last', self.__handleBackward) + self.acceptOnce('next', self.__handleForward) + choicePool = [self.getColorList(), self.getColorList(), self.getColorList(), self.getColorList()] + self.shuffleButton.setChoicePool(choicePool) + self.accept(self.shuffleFetchMsg, self.changeColor) + self.acceptOnce('MAT-newToonCreated', self.shuffleButton.cleanHistory) + + def showButtons(self): + self.parentFrame.show() + + def hideButtons(self): + self.parentFrame.hide() + self.advancedFrame.hide() + + def exit(self): + self.ignore('last') + self.ignore('next') + self.ignore('enter') + self.ignore(self.shuffleFetchMsg) + try: + del self.toon + except: + print 'ColorShop: toon not found' + + self.hideButtons() + taskMgr.remove('colorDragTask') + + def load(self): + self.gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + guiRArrowUp = self.gui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowRollover = self.gui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowDown = self.gui.find('**/tt_t_gui_mat_arrowDown') + guiRArrowDisabled = self.gui.find('**/tt_t_gui_mat_arrowDisabled') + shuffleFrame = self.gui.find('**/tt_t_gui_mat_shuffleFrame') + shuffleUp = self.gui.find('**/tt_t_gui_mat_shuffleUp') + shuffleDown = self.gui.find('**/tt_t_gui_mat_shuffleDown') + shuffleImage = (self.gui.find('**/tt_t_gui_mat_shuffleArrowUp'), self.gui.find('**/tt_t_gui_mat_shuffleArrowDown'), self.gui.find('**/tt_t_gui_mat_shuffleArrowUp'), self.gui.find('**/tt_t_gui_mat_shuffleArrowDisabled')) + self.parentFrame = self.getNewFrame() + self.advancedFrame = self.getNewFrame() + self.toonFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.073), hpr=(0, 0, 0), scale=1.3, frameColor=(1, 1, 1, 1), text=TTLocalizer.ColorShopToon, text_scale=TTLocalizer.CStoonFrame, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.allLButton = DirectButton(parent=self.toonFrame, relief=None, image=shuffleImage, image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapAllColor, extraArgs=[-1]) + self.allRButton = DirectButton(parent=self.toonFrame, relief=None, image=shuffleImage, image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapAllColor, extraArgs=[1]) + self.headFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.3), hpr=(0, 0, 2), scale=0.9, frameColor=(1, 1, 1, 1), text=TTLocalizer.ColorShopHead, text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.headLButton = DirectButton(parent=self.headFrame, relief=None, image=shuffleImage, image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapHeadColor, extraArgs=[-1]) + self.headRButton = DirectButton(parent=self.headFrame, relief=None, image=shuffleImage, image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapHeadColor, extraArgs=[1]) + self.bodyFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonScale, relief=None, pos=(0, 0, -0.5), hpr=(0, 0, -2), scale=0.9, frameColor=(1, 1, 1, 1), text=TTLocalizer.ColorShopBody, text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.armLButton = DirectButton(parent=self.bodyFrame, relief=None, image=shuffleImage, image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapArmColor, extraArgs=[-1]) + self.armRButton = DirectButton(parent=self.bodyFrame, relief=None, image=shuffleImage, image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapArmColor, extraArgs=[1]) + self.legsFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.7), hpr=(0, 0, 3), scale=0.9, frameColor=(1, 1, 1, 1), text=TTLocalizer.ColorShopLegs, text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.legLButton = DirectButton(parent=self.legsFrame, relief=None, image=shuffleImage, image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapLegColor, extraArgs=[-1]) + self.legRButton = DirectButton(parent=self.legsFrame, relief=None, image=shuffleImage, image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapLegColor, extraArgs=[1]) + self.advancedButton = DirectButton(parent=self.parentFrame, relief=None, image=(shuffleUp, shuffleDown, shuffleUp), image_scale=(-0.8, 0.6, 0.6), image1_scale=(-0.83, 0.6, 0.6), image2_scale=(-0.83, 0.6, 0.6), text=TTLocalizer.ColorAdvanced, text_font=ToontownGlobals.getInterfaceFont(), text_scale=TTLocalizer.SBshuffleBtn, text_pos=(0, -0.02), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), pos=(0, 0, -1.15), command=self.popupAdvancedMenu) + self.basicButton = DirectButton(parent=self.advancedFrame, relief=None, image=(shuffleUp, shuffleDown, shuffleUp), image_scale=(-0.8, 0.6, 0.6), image1_scale=(-0.83, 0.6, 0.6), image2_scale=(-0.83, 0.6, 0.6), text=TTLocalizer.ColorBasic, text_font=ToontownGlobals.getInterfaceFont(), text_scale=TTLocalizer.SBshuffleBtn, text_pos=(0, -0.02), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), pos=(0, 0, -1.15), command=self.popupBasicMenu) + self.pickContainer = DirectFrame(parent=self.advancedFrame, relief=None, pos=(-0.4, 0, -0.5), image='phase_3/maps/color_picker_container.png', scale=(0.7, 0.5, 0.55)) + self.pickContainer.setTransparency(True) + self.pickImage = PNMImage(int((ToontownGlobals.COLOR_SATURATION_MAX - ToontownGlobals.COLOR_SATURATION_MIN) * 100), int((ToontownGlobals.COLOR_VALUE_MAX - ToontownGlobals.COLOR_VALUE_MIN) * 100)) + self.hueSlider = DirectSlider(parent=self.advancedFrame, relief=None, image='phase_3/maps/color_picker_hue.jpg', scale=0.3, pos=(-0.05, 0, -0.43), image_scale=(0.1, 1.0, 1.0), pageSize=5, orientation=DGG.VERTICAL, command=self.__chooseHue) + self.pickButton = DirectButton(parent=self.advancedFrame, relief=None, image='phase_3/maps/invisible.png', scale=0.3, pos=(-0.45, 0, -0.43), frameColor=(1, 1, 1, 0.1), pressEffect=0) + self.pickButton.bind(DGG.B1PRESS, self.__startPickColor) + self.pickButton.bind(DGG.B1RELEASE, self.__stopPickColor) + self.partsFrame = DirectFrame(parent=self.advancedFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(-0.395, 0, -0.85), hpr=(0, 0, -2), scale=0.9, frameColor=(1, 1, 1, 1), text=TTLocalizer.ColorAll, text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.partLButton = DirectButton(parent=self.partsFrame, relief=None, image=shuffleImage, image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), state=DGG.DISABLED, command=self.__swapPart, extraArgs=[-1]) + self.partRButton = DirectButton(parent=self.partsFrame, relief=None, image=shuffleImage, image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapPart, extraArgs=[1]) + self.parentFrame.hide() + self.advancedFrame.hide() + self.shuffleFetchMsg = 'ColorShopShuffle' + self.shuffleButton = ShuffleButton.ShuffleButton(self, self.shuffleFetchMsg) + + def unload(self): + self.gui.removeNode() + del self.gui + self.parentFrame.destroy() + self.advancedFrame.destroy() + self.toonFrame.destroy() + self.headFrame.destroy() + self.bodyFrame.destroy() + self.legsFrame.destroy() + self.headLButton.destroy() + self.headRButton.destroy() + self.armLButton.destroy() + self.armRButton.destroy() + self.legLButton.destroy() + self.legRButton.destroy() + self.allLButton.destroy() + self.allRButton.destroy() + self.advancedButton.destroy() + self.basicButton.destroy() + self.pickContainer.destroy() + self.hueSlider.destroy() + self.pickButton.destroy() + self.partsFrame.destroy() + self.partLButton.destroy() + self.partRButton.destroy() + del self.parentFrame + del self.advancedFrame + del self.toonFrame + del self.headFrame + del self.bodyFrame + del self.legsFrame + del self.headLButton + del self.headRButton + del self.armLButton + del self.armRButton + del self.legLButton + del self.legRButton + del self.allLButton + del self.allRButton + del self.advancedButton + del self.basicButton + del self.pickContainer + del self.hueSlider + del self.pickButton + del self.partsFrame + del self.partLButton + del self.partRButton + self.shuffleButton.unload() + self.ignore('MAT-newToonCreated') + + def getNewFrame(self): + frame = DirectFrame(relief=DGG.RAISED, pos=(0.98, 0, 0.416), frameColor=(1, 0, 0, 0)) + frame.setPos(-0.36, 0, -0.5) + frame.reparentTo(base.a2dTopRight) + return frame + + def popupAdvancedMenu(self): + self.parentFrame.hide() + self.advancedFrame.show() + + def popupBasicMenu(self): + self.parentFrame.show() + self.advancedFrame.hide() + + def calcRelative(self, value, baseMin, baseMax, limitMin, limitMax): + return ((limitMax - limitMin) * (value - baseMin) / (baseMax - baseMin)) + limitMin + + def __chooseHue(self): + for x in xrange(self.pickImage.getXSize()): + for y in xrange(self.pickImage.getYSize()): + self.pickImage.setXel(x, y, colorsys.hsv_to_rgb(self.hueSlider['value'], (x / 100.0) + ToontownGlobals.COLOR_SATURATION_MIN, (y / 100.0) + ToontownGlobals.COLOR_VALUE_MIN)) + + texture = Texture() + texture.load(self.pickImage) + self.pickButton['image'] = texture + + def __pickColor(self, task=None): + x = base.mouseWatcherNode.getMouseX() + y = base.mouseWatcherNode.getMouseY() + win_w, win_h = base.win.getSize() + + if win_w < win_h: + y *= 1. * win_h / win_w + else: + x *= 1. * win_w / win_h + + x -= self.pickButton.getX(aspect2d) + y -= self.pickButton.getZ(aspect2d) + image_scale = self.pickButton['image_scale'] + x = (.5 + x / (2. * self.pickButton.getSx(aspect2d) * image_scale[0])) + y = (.5 + y / -(2. * self.pickButton.getSz(aspect2d) * image_scale[2])) + + if not (0.0 <= x <= 1.0 and 0.0 <= y <= 1.0): + return Task.cont + + x = self.calcRelative(x, 0.0, 1.0, ToontownGlobals.COLOR_SATURATION_MIN, ToontownGlobals.COLOR_SATURATION_MAX) + y = self.calcRelative(y, 0.0, 1.0, ToontownGlobals.COLOR_VALUE_MIN, ToontownGlobals.COLOR_VALUE_MAX) + rgb = colorsys.hsv_to_rgb(self.hueSlider['value'], x, y) + (1,) + rgb = tuple([float('%.2f' % x) for x in rgb]) + + if self.partChoice in (0, 1): + self.dna.headColor = rgb + if self.partChoice in (0, 2): + self.dna.armColor = rgb + if self.partChoice in (0, 3): + self.dna.legColor = rgb + + self.toon.swapToonColor(self.dna) + return Task.cont + + def __startPickColor(self, extra): + self.__stopPickColor(extra) + taskMgr.add(self.__pickColor, 'colorDragTask') + + def __stopPickColor(self, extra): + taskMgr.remove('colorDragTask') + + def __swapPart(self, offset): + self.partChoice += offset + self.partLButton['state'] = DGG.DISABLED if self.partChoice <= 0 else DGG.NORMAL + self.partRButton['state'] = DGG.DISABLED if self.partChoice >= len(self.allParts) - 1 else DGG.NORMAL + self.partsFrame['text'] = self.allParts[self.partChoice] + + def __swapAllColor(self, offset): + colorList = self.getColorList() + length = len(colorList) + self.allChoice = (self.allChoice + offset) % length + self.__updateScrollButtons(self.allChoice, length, self.allLButton, self.allRButton) + self.__updateScrollButtons(self.allChoice, length, self.headLButton, self.headRButton) + self.__updateScrollButtons(self.allChoice, length, self.armLButton, self.armRButton) + self.__updateScrollButtons(self.allChoice, length, self.legLButton, self.legRButton) + newColor = colorList[self.allChoice] + self.dna.headColor = newColor + self.dna.armColor = newColor + self.dna.legColor = newColor + self.toon.swapToonColor(self.dna) + + def __swapHeadColor(self, offset): + colorList = self.getColorList() + length = len(colorList) + self.headChoice = (self.headChoice + offset) % length + self.__updateScrollButtons(self.headChoice, length, self.headLButton, self.headRButton) + newColor = colorList[self.headChoice] + self.dna.headColor = newColor + self.toon.swapToonColor(self.dna) + + def __swapArmColor(self, offset): + colorList = self.getColorList() + length = len(colorList) + self.armChoice = (self.armChoice + offset) % length + self.__updateScrollButtons(self.armChoice, length, self.armLButton, self.armRButton) + newColor = colorList[self.armChoice] + self.dna.armColor = newColor + self.toon.swapToonColor(self.dna) + + def __swapLegColor(self, offset): + colorList = self.getColorList() + length = len(colorList) + self.legChoice = (self.legChoice + offset) % length + self.__updateScrollButtons(self.legChoice, length, self.legLButton, self.legRButton) + newColor = colorList[self.legChoice] + self.dna.legColor = newColor + self.toon.swapToonColor(self.dna) + + def __updateScrollButtons(self, choice, length, lButton, rButton): + if choice == (self.startColor - 1) % length: + rButton['state'] = DGG.DISABLED + else: + rButton['state'] = DGG.NORMAL + if choice == self.startColor % length: + lButton['state'] = DGG.DISABLED + else: + lButton['state'] = DGG.NORMAL + + def __handleForward(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) + + def __handleBackward(self): + self.doneStatus = 'last' + messenger.send(self.doneEvent) + + def changeColor(self): + self.notify.debug('Entering changeColor') + colorList = self.getColorList() + newChoice = self.shuffleButton.getCurrChoice() + newHeadColorIndex = self.indexOf(colorList, newChoice[0], 25) + newArmColorIndex = self.indexOf(colorList, newChoice[1], 25) + newLegColorIndex = self.indexOf(colorList, newChoice[2], 25) + self.__swapHeadColor(newHeadColorIndex - self.headChoice) + if self.colorAll: + self.__swapArmColor(newHeadColorIndex - self.armChoice) + self.__swapLegColor(newHeadColorIndex - self.legChoice) + else: + self.__swapArmColor(newArmColorIndex - self.armChoice) + self.__swapLegColor(newLegColorIndex - self.legChoice) + + def indexOf(self, list, item, default): + try: + return list.index(item) + except: + return default + + def getCurrToonSetting(self): + return [self.dna.headColor, self.dna.armColor, self.dna.legColor] diff --git a/toontown/makeatoon/GenderShop.py b/toontown/makeatoon/GenderShop.py new file mode 100755 index 00000000..d830ef89 --- /dev/null +++ b/toontown/makeatoon/GenderShop.py @@ -0,0 +1,122 @@ +from panda3d.core import * +from direct.fsm import StateData +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toon import ToonDNA +from toontown.toon import Toon +from MakeAToonGlobals import * +from direct.directnotify import DirectNotifyGlobal +import random + +class GenderShop(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('GenderShop') + + def __init__(self, makeAToon, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.shopsVisited = [] + self.toon = None + self.gender = 'm' + self.makeAToon = makeAToon + return + + def enter(self): + base.disableMouse() + self.accept('next', self.__handleForward) + return None + + def showButtons(self): + return None + + def exit(self): + self.ignore('next') + + def load(self): + gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + guiBoyUp = gui.find('**/tt_t_gui_mat_boyUp') + guiBoyDown = gui.find('**/tt_t_gui_mat_boyDown') + guiGirlUp = gui.find('**/tt_t_gui_mat_girlUp') + guiGirlDown = gui.find('**/tt_t_gui_mat_girlDown') + self.boyButton = DirectButton(relief=None, image=(guiBoyUp, + guiBoyDown, + guiBoyUp, + guiBoyDown), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.4, 0, -0.8), command=self.createRandomBoy, text=('', + TTLocalizer.GenderShopBoyButtonText, + TTLocalizer.GenderShopBoyButtonText, + ''), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, 0.19), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.boyButton.hide() + self.boyButton.setPos(-0.45, 0, 0.19) + self.boyButton.reparentTo(base.a2dBottomCenter) + self.girlButton = DirectButton(relief=None, image=(guiGirlUp, + guiGirlDown, + guiGirlUp, + guiGirlDown), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(0.4, 0, -0.8), command=self.createRandomGirl, text=('', + TTLocalizer.GenderShopGirlButtonText, + TTLocalizer.GenderShopGirlButtonText, + ''), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, 0.19), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.girlButton.hide() + self.girlButton.setPos(0.45, 0, 0.19) + self.girlButton.reparentTo(base.a2dBottomCenter) + gui.removeNode() + del gui + self.toon = None + return + + def unload(self): + self.boyButton.destroy() + self.girlButton.destroy() + del self.boyButton + del self.girlButton + if self.toon: + self.toon.delete() + self.makeAToon = None + return + + def setGender(self, choice): + self.__setGender(choice) + + def __setGender(self, choice): + self.gender = 'm' + if self.toon: + self.gender = self.toon.style.gender + messenger.send(self.doneEvent) + + def hideButtons(self): + self.boyButton.hide() + self.girlButton.hide() + + def showButtons(self): + self.boyButton.show() + self.girlButton.show() + + def createRandomBoy(self): + self._createRandomToon('m') + + def createRandomGirl(self): + self._createRandomToon('f') + + def _createRandomToon(self, gender): + if self.toon: + self.toon.stopBlink() + self.toon.stopLookAroundNow() + self.toon.delete() + self.dna = ToonDNA.ToonDNA() + self.dna.newToonRandom(gender=gender, stage=1) + self.toon = Toon.Toon() + self.toon.setDNA(self.dna) + self.toon.useLOD(1000) + self.toon.setNameVisible(0) + self.toon.startBlink() + self.toon.startLookAround() + self.toon.reparentTo(render) + self.toon.setPos(self.makeAToon.toonPosition) + self.toon.setHpr(self.makeAToon.toonHpr) + self.toon.setScale(self.makeAToon.toonScale) + self.toon.loop('neutral') + self.makeAToon.setNextButtonState(DGG.NORMAL) + self.makeAToon.setToon(self.toon) + messenger.send('MAT-newToonCreated') + + def __handleForward(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) diff --git a/toontown/makeatoon/MakeAToon.py b/toontown/makeatoon/MakeAToon.py new file mode 100755 index 00000000..9a14e4a4 --- /dev/null +++ b/toontown/makeatoon/MakeAToon.py @@ -0,0 +1,782 @@ +from direct.actor.Actor import Actor +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from direct.task import Task +from panda3d.core import * +import random + +import BodyShop +import ColorShop +import GenderShop +from MakeAToonGlobals import * +import MakeClothesGUI +import NameShop +from otp.avatar import Avatar +from otp.nametag.NametagConstants import * +from toontown.distributed.ToontownMsgTypes import * +from toontown.toon import LocalToon +from toontown.toon import Toon +from toontown.toon import ToonDNA +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog + + +class MakeAToon(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('MakeAToon') + + def __init__(self, parentFSM, avList, doneEvent, index): + StateData.StateData.__init__(self, doneEvent) + self.phase = 3 + self.names = ['', + '', + '', + ''] + self.dnastring = None + self.dna = None + self.progressing = 0 + self.toonPosition = Point3(-1.62, -3.49, 0) + self.toonScale = Point3(1, 1, 1) + self.toonHpr = Point3(180, 0, 0) + self.leftTime = 1.6 + self.rightTime = 1 + self.slide = 0 + self.nameList = [] + self.warp = 0 + for av in avList: + if av.position == index: + self.warp = 1 + self.namelessPotAv = av + self.nameList.append(av.name) + + self.fsm = ClassicFSM.ClassicFSM('MakeAToon', [State.State('Init', self.enterInit, self.exitInit, ['GenderShop', 'NameShop']), + State.State('GenderShop', self.enterGenderShop, self.exitGenderShop, ['BodyShop']), + State.State('BodyShop', self.enterBodyShop, self.exitBodyShop, ['GenderShop', 'ColorShop']), + State.State('ColorShop', self.enterColorShop, self.exitColorShop, ['BodyShop', 'ClothesShop']), + State.State('ClothesShop', self.enterClothesShop, self.exitClothesShop, ['ColorShop', 'NameShop']), + State.State('NameShop', self.enterNameShop, self.exitNameShop, ['ClothesShop']), + State.State('Done', self.enterDone, self.exitDone, [])], 'Init', 'Done') + self.parentFSM = parentFSM + self.parentFSM.getStateNamed('createAvatar').addChild(self.fsm) + self.gs = GenderShop.GenderShop(self, 'GenderShop-done') + self.bs = BodyShop.BodyShop('BodyShop-done') + self.cos = ColorShop.ColorShop('ColorShop-done') + self.cls = MakeClothesGUI.MakeClothesGUI('ClothesShop-done') + self.ns = NameShop.NameShop(self, 'NameShop-done', avList, index) + self.shop = GENDERSHOP + self.shopsVisited = [] + if self.warp: + self.shopsVisited = [ + GENDERSHOP, + BODYSHOP, + COLORSHOP, + CLOTHESSHOP, + ] + self.music = None + self.soundBack = None + self.fsm.enterInitialState() + self.hprDelta = -1 + self.dropIval = None + self.roomSquishIval = None + self.propSquishIval = None + self.focusOutIval = None + self.focusInIval = None + self.toon = None + + def getToon(self): + return self.toon + + def enter(self): + self.notify.debug('Starting Make A Toon.') + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: MAKEATOON: Starting Make A Toon') + base.camLens.setMinFov(ToontownGlobals.MakeAToonCameraFov/(4./3.)) + base.playMusic(self.music, looping=1, volume=self.musicVolume) + camera.setPosHpr(-5.7, -12.3501, 2.15, -24.8499, 2.73, 0) + if self.warp: + if self.toon.style.torso[1] == 's': + self.toon.gender = 's' + else: + self.toon.gender = 'd' + self.toon.reparentTo(render) + self.toon.loop('neutral') + self.toon.setScale(self.toonScale) + self.spotlight.setPos(2, -1.95, 0.41) + self.toon.setPos(Point3(1.5, -4, 0)) + self.toon.setH(120) + self.guiTopBar.show() + self.guiBottomBar.show() + self.guiCancelButton.show() + if self.warp: + self.progressing = 0 + self.guiLastButton.hide() + self.fsm.request('NameShop') + else: + self.fsm.request('GenderShop') + + def exit(self): + base.camLens.setMinFov(settings['fov']/(4./3.)) + self.guiTopBar.hide() + self.guiBottomBar.hide() + self.music.stop() + self.fsm.request('Done') + self.room.reparentTo(hidden) + + def load(self): + gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + gui.flattenMedium() + guiAcceptUp = gui.find('**/tt_t_gui_mat_okUp') + guiAcceptUp.flattenStrong() + guiAcceptDown = gui.find('**/tt_t_gui_mat_okDown') + guiAcceptDown.flattenStrong() + guiCancelUp = gui.find('**/tt_t_gui_mat_closeUp') + guiCancelUp.flattenStrong() + guiCancelDown = gui.find('**/tt_t_gui_mat_closeDown') + guiCancelDown.flattenStrong() + guiNextUp = gui.find('**/tt_t_gui_mat_nextUp') + guiNextUp.flattenStrong() + guiNextDown = gui.find('**/tt_t_gui_mat_nextDown') + guiNextDown.flattenStrong() + guiNextDisabled = gui.find('**/tt_t_gui_mat_nextDisabled') + guiNextDisabled.flattenStrong() + skipTutorialUp = gui.find('**/tt_t_gui_mat_skipUp') + skipTutorialUp.flattenStrong() + skipTutorialDown = gui.find('**/tt_t_gui_mat_skipDown') + skipTutorialDown.flattenStrong() + rotateUp = gui.find('**/tt_t_gui_mat_arrowRotateUp') + rotateUp.flattenStrong() + rotateDown = gui.find('**/tt_t_gui_mat_arrowRotateDown') + rotateDown.flattenStrong() + self.guiTopBar = DirectFrame(relief=None, text=TTLocalizer.CreateYourToon, text_font=ToontownGlobals.getSignFont(), text_fg=(0.0, 0.65, 0.35, 1), text_scale=0.18, text_pos=(0, -0.03), pos=(0, 0, 0.86)) + self.guiTopBar.hide() + self.guiBottomBar = DirectFrame(relief=None, image_scale=(1.25, 1, 1), pos=(0.01, 0, -0.86)) + self.guiBottomBar.hide() + self.guiCheckButton = DirectButton(parent=self.guiBottomBar, relief=None, image=(guiAcceptUp, + guiAcceptDown, + guiAcceptUp, + guiAcceptDown), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(1.165, 0, -0.018), command=self.__handleNext, text=('', TTLocalizer.MakeAToonDone, TTLocalizer.MakeAToonDone), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_align=TextNode.ARight, text_pos=(0.075, 0.13), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.guiCheckButton.setPos(-0.13, 0, 0.13) + self.guiCheckButton.reparentTo(base.a2dBottomRight) + self.guiCheckButton.hide() + self.guiCancelButton = DirectButton(parent=self.guiBottomBar, relief=None, image=(guiCancelUp, + guiCancelDown, + guiCancelUp, + guiCancelDown), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-1.179, 0, -0.011), command=self.__handleCancel, text=('', TTLocalizer.MakeAToonCancel, TTLocalizer.MakeAToonCancel), text_font=ToontownGlobals.getInterfaceFont(), text_scale=TTLocalizer.MATguiCancelButton, text_pos=(0, 0.115), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.guiCancelButton.setPos(0.13,0,0.13) + self.guiCancelButton.reparentTo(base.a2dBottomLeft) + self.guiCancelButton.hide() + self.guiNextButton = DirectButton(parent=self.guiBottomBar, relief=None, image=(guiNextUp, + guiNextDown, + guiNextUp, + guiNextDisabled), image_scale=(0.3, 0.3, 0.3), image1_scale=(0.35, 0.35, 0.35), image2_scale=(0.35, 0.35, 0.35), pos=(1.165, 0, -0.018), command=self.__handleNext, text=('', + TTLocalizer.MakeAToonNext, + TTLocalizer.MakeAToonNext, + ''), text_font=ToontownGlobals.getInterfaceFont(), text_scale=TTLocalizer.MATguiNextButton, text_pos=(0, 0.115), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.guiNextButton.setPos(-0.13, 0, 0.13) + self.guiNextButton.reparentTo(base.a2dBottomRight) + self.guiNextButton.hide() + self.guiLastButton = DirectButton(parent=self.guiBottomBar, relief=None, image=(guiNextUp, + guiNextDown, + guiNextUp, + guiNextDown), image3_color=Vec4(0.5, 0.5, 0.5, 0.75), image_scale=(-0.3, 0.3, 0.3), image1_scale=(-0.35, 0.35, 0.35), image2_scale=(-0.35, 0.35, 0.35), pos=(0.825, 0, -0.018), command=self.__handleLast, text=('', + TTLocalizer.MakeAToonLast, + TTLocalizer.MakeAToonLast, + ''), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, 0.115), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.guiLastButton.setPos(-0.37, 0, 0.13) + self.guiLastButton.reparentTo(base.a2dBottomRight) + self.guiLastButton.hide() + self.rotateLeftButton = DirectButton(parent=self.guiBottomBar, relief=None, image=(rotateUp, + rotateDown, + rotateUp, + rotateDown), image_scale=(-0.4, 0.4, 0.4), image1_scale=(-0.5, 0.5, 0.5), image2_scale=(-0.5, 0.5, 0.5), pos=(-0.355, 0, 0.36)) + self.rotateLeftButton.flattenMedium() + self.rotateLeftButton.reparentTo(base.a2dBottomCenter) + self.rotateLeftButton.hide() + self.rotateLeftButton.bind(DGG.B1PRESS, self.rotateToonLeft) + self.rotateLeftButton.bind(DGG.B1RELEASE, self.stopToonRotateLeftTask) + self.rotateRightButton = DirectButton(parent=self.guiBottomBar, relief=None, image=(rotateUp, + rotateDown, + rotateUp, + rotateDown), image_scale=(0.4, 0.4, 0.4), image1_scale=(0.5, 0.5, 0.5), image2_scale=(0.5, 0.5, 0.5), pos=(0.355, 0, 0.36)) + self.rotateRightButton.flattenStrong() + self.rotateRightButton.reparentTo(base.a2dBottomCenter) + self.rotateRightButton.hide() + self.rotateRightButton.bind(DGG.B1PRESS, self.rotateToonRight) + self.rotateRightButton.bind(DGG.B1RELEASE, self.stopToonRotateRightTask) + gui.removeNode() + self.roomDropActor = Actor() + self.roomDropActor.loadModel('phase_3/models/makeatoon/roomAnim_model') + self.roomDropActor.loadAnims({'drop': 'phase_3/models/makeatoon/roomAnim_roomDrop'}) + self.roomDropActor.reparentTo(render) + self.dropJoint = self.roomDropActor.find('**/droppingJoint') + self.roomSquishActor = Actor() + self.roomSquishActor.loadModel('phase_3/models/makeatoon/roomAnim_model') + self.roomSquishActor.loadAnims({'squish': 'phase_3/models/makeatoon/roomAnim_roomSquish'}) + self.roomSquishActor.reparentTo(render) + self.squishJoint = self.roomSquishActor.find('**/scalingJoint') + self.propSquishActor = Actor() + self.propSquishActor.loadModel('phase_3/models/makeatoon/roomAnim_model') + self.propSquishActor.loadAnims({'propSquish': 'phase_3/models/makeatoon/roomAnim_propSquish'}) + self.propSquishActor.reparentTo(render) + self.propSquishActor.pose('propSquish', 0) + self.propJoint = self.propSquishActor.find('**/propJoint') + self.spotlightActor = Actor() + self.spotlightActor.loadModel('phase_3/models/makeatoon/roomAnim_model') + self.spotlightActor.loadAnims({'spotlightShake': 'phase_3/models/makeatoon/roomAnim_spotlightShake'}) + self.spotlightActor.reparentTo(render) + self.spotlightJoint = self.spotlightActor.find('**/spotlightJoint') + ee = DirectFrame(pos=(-1, 1, 1), frameSize=(-.01, 0.01, -.01, 0.01), frameColor=(0, 0, 0, 0.05), state='normal') + ee.bind(DGG.B1PRESS, lambda x, ee = ee: self.toggleSlide()) + self.eee = ee + self.room = loader.loadModel('phase_3/models/makeatoon/tt_m_ara_mat_room') + self.room.flattenMedium() + self.genderWalls = self.room.find('**/genderWalls') + self.genderWalls.flattenStrong() + self.genderProps = self.room.find('**/genderProps') + self.genderProps.flattenStrong() + self.bodyWalls = self.room.find('**/bodyWalls') + self.bodyWalls.flattenStrong() + self.bodyProps = self.room.find('**/bodyProps') + self.bodyProps.flattenStrong() + self.colorWalls = self.room.find('**/colorWalls') + self.colorWalls.flattenStrong() + self.colorProps = self.room.find('**/colorProps') + self.colorProps.flattenStrong() + self.clothesWalls = self.room.find('**/clothWalls') + self.clothesWalls.flattenMedium() + self.clothesProps = self.room.find('**/clothProps') + self.clothesProps.flattenMedium() + self.nameWalls = self.room.find('**/nameWalls') + self.nameWalls.flattenStrong() + self.nameProps = self.room.find('**/nameProps') + self.nameProps.flattenStrong() + self.background = self.room.find('**/background') + self.background.flattenStrong() + self.background.reparentTo(render) + self.floor = self.room.find('**/floor') + self.floor.flattenStrong() + self.floor.reparentTo(render) + self.spotlight = self.room.find('**/spotlight') + self.spotlight.reparentTo(self.spotlightJoint) + self.spotlight.setColor(1, 1, 1, 0.3) + self.spotlight.setPos(1.18, -1.27, 0.41) + self.spotlight.setScale(2.6) + self.spotlight.setHpr(0, 0, 0) + smokeSeqNode = SequenceNode('smoke') + smokeModel = loader.loadModel('phase_3/models/makeatoon/tt_m_ara_mat_smoke') + smokeFrameList = list(smokeModel.findAllMatches('**/smoke_*')) + smokeFrameList.reverse() + for smokeFrame in smokeFrameList: + smokeSeqNode.addChild(smokeFrame.node()) + + smokeSeqNode.setFrameRate(12) + self.smoke = render.attachNewNode(smokeSeqNode) + self.smoke.setScale(1, 1, 0.75) + self.smoke.hide() + if self.warp: + self.dna = ToonDNA.ToonDNA() + self.dna.makeFromNetString(self.namelessPotAv.dna) + self.toon = Toon.Toon() + self.toon.setDNA(self.dna) + self.toon.useLOD(1000) + self.toon.setNameVisible(0) + self.toon.startBlink() + self.toon.startLookAround() + self.gs.load() + self.bs.load() + self.cos.load() + self.cls.load() + self.ns.load() + self.music = base.loadMusic('phase_3/audio/bgm/create_a_toon.ogg') + self.musicVolume = base.config.GetFloat('makeatoon-music-volume', 1) + self.sfxVolume = base.config.GetFloat('makeatoon-sfx-volume', 1) + self.soundBack = base.loadSfx('phase_3/audio/sfx/GUI_create_toon_back.ogg') + self.crashSounds = map(base.loadSfx, ['phase_3/audio/sfx/tt_s_ara_mat_crash_boing.ogg', + 'phase_3/audio/sfx/tt_s_ara_mat_crash_glassBoing.ogg', + 'phase_3/audio/sfx/tt_s_ara_mat_crash_wood.ogg', + 'phase_3/audio/sfx/tt_s_ara_mat_crash_woodBoing.ogg', + 'phase_3/audio/sfx/tt_s_ara_mat_crash_woodGlass.ogg']) + + def unload(self): + self.exit() + if self.toon: + self.toon.stopBlink() + self.toon.stopLookAroundNow() + self.gs.unload() + self.bs.unload() + self.cos.unload() + self.cls.unload() + self.ns.unload() + del self.gs + del self.bs + del self.cos + del self.cls + del self.ns + self.guiTopBar.destroy() + self.guiBottomBar.destroy() + self.guiCancelButton.destroy() + self.guiCheckButton.destroy() + self.eee.destroy() + self.guiNextButton.destroy() + self.guiLastButton.destroy() + self.rotateLeftButton.destroy() + self.rotateRightButton.destroy() + del self.guiTopBar + del self.guiBottomBar + del self.guiCancelButton + del self.guiCheckButton + del self.eee + del self.guiNextButton + del self.guiLastButton + del self.rotateLeftButton + del self.rotateRightButton + del self.names + del self.dnastring + del self.nameList + del self.music + del self.soundBack + del self.dna + if self.toon: + self.toon.delete() + del self.toon + self.cleanupDropIval() + self.cleanupRoomSquishIval() + self.cleanupPropSquishIval() + self.cleanupFocusInIval() + self.cleanupFocusOutIval() + self.room.removeNode() + del self.room + self.genderWalls.removeNode() + self.genderProps.removeNode() + del self.genderWalls + del self.genderProps + self.bodyWalls.removeNode() + self.bodyProps.removeNode() + del self.bodyWalls + del self.bodyProps + self.colorWalls.removeNode() + self.colorProps.removeNode() + del self.colorWalls + del self.colorProps + self.clothesWalls.removeNode() + self.clothesProps.removeNode() + del self.clothesWalls + del self.clothesProps + self.nameWalls.removeNode() + self.nameProps.removeNode() + del self.nameWalls + del self.nameProps + self.background.removeNode() + del self.background + self.floor.removeNode() + del self.floor + self.spotlight.removeNode() + del self.spotlight + self.smoke.removeNode() + del self.smoke + while len(self.crashSounds): + del self.crashSounds[0] + + self.parentFSM.getStateNamed('createAvatar').removeChild(self.fsm) + del self.parentFSM + del self.fsm + self.ignoreAll() + loader.unloadModel('phase_3/models/gui/create_a_toon_gui') + loader.unloadModel('phase_3/models/gui/create_a_toon') + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def getDNA(self): + return self.dnastring + + def __handleBodyShop(self): + self.fsm.request('BodyShop') + + def __handleClothesShop(self): + self.fsm.request('ClothesShop') + + def __handleColorShop(self): + self.fsm.request('ColorShop') + + def __handleNameShop(self): + self.fsm.request('NameShop') + + def __handleCancel(self): + self.doneStatus = 'cancel' + self.shopsVisited = [] + base.transitions.fadeOut(finishIval=EventInterval(self.doneEvent)) + + def toggleSlide(self): + self.slide = 1 - self.slide + + def goToNextShop(self): + self.progressing = 1 + if self.shop == GENDERSHOP: + self.fsm.request('BodyShop') + elif self.shop == BODYSHOP: + self.fsm.request('ColorShop') + elif self.shop == COLORSHOP: + self.fsm.request('ClothesShop') + else: + self.fsm.request('NameShop') + + def goToLastShop(self): + self.progressing = 0 + if self.shop == BODYSHOP: + self.fsm.request('GenderShop') + elif self.shop == COLORSHOP: + self.fsm.request('BodyShop') + elif self.shop == CLOTHESSHOP: + self.fsm.request('ColorShop') + else: + self.fsm.request('ClothesShop') + + def enterInit(self): + pass + + def exitInit(self): + pass + + def enterGenderShop(self): + self.shop = GENDERSHOP + if GENDERSHOP not in self.shopsVisited: + self.shopsVisited.append(GENDERSHOP) + self.genderWalls.reparentTo(self.squishJoint) + self.genderProps.reparentTo(self.propJoint) + self.roomSquishActor.pose('squish', 0) + self.guiNextButton['state'] = DGG.DISABLED + else: + self.dropRoom(self.genderWalls, self.genderProps) + self.guiTopBar['text'] = TTLocalizer.CreateYourToonTitle + self.guiTopBar['text_fg'] = (1, 0.92, 0.2, 1) + self.guiTopBar['text_scale'] = TTLocalizer.MATenterGenderShop + base.transitions.fadeIn() + self.accept('GenderShop-done', self.__handleGenderShopDone) + self.gs.enter() + self.guiNextButton.show() + self.gs.showButtons() + self.rotateLeftButton.hide() + self.rotateRightButton.hide() + + def exitGenderShop(self): + self.squishRoom(self.genderWalls) + self.squishProp(self.genderProps) + self.gs.exit() + self.ignore('GenderShop-done') + + def __handleGenderShopDone(self): + self.guiNextButton.hide() + self.gs.hideButtons() + self.goToNextShop() + + def bodyShopOpening(self): + self.bs.showButtons() + self.guiNextButton.show() + self.guiLastButton.show() + self.rotateLeftButton.show() + self.rotateRightButton.show() + + def enterBodyShop(self): + self.toon.show() + self.shop = BODYSHOP + self.guiTopBar['text'] = TTLocalizer.ShapeYourToonTitle + self.guiTopBar['text_fg'] = (0.0, 0.98, 0.5, 1) + self.guiTopBar['text_scale'] = TTLocalizer.MATenterBodyShop + self.accept('BodyShop-done', self.__handleBodyShopDone) + self.dropRoom(self.bodyWalls, self.bodyProps) + self.bs.enter(self.toon, self.shopsVisited) + if BODYSHOP not in self.shopsVisited: + self.shopsVisited.append(BODYSHOP) + self.bodyShopOpening() + + def exitBodyShop(self): + self.squishRoom(self.bodyWalls) + self.squishProp(self.bodyProps) + self.bs.exit() + self.ignore('BodyShop-done') + + def __handleBodyShopDone(self): + self.guiNextButton.hide() + self.guiLastButton.hide() + if self.bs.doneStatus == 'next': + self.bs.hideButtons() + self.goToNextShop() + else: + self.bs.hideButtons() + self.goToLastShop() + + def colorShopOpening(self): + self.cos.showButtons() + self.guiNextButton.show() + self.guiLastButton.show() + self.rotateLeftButton.show() + self.rotateRightButton.show() + + def enterColorShop(self): + self.shop = COLORSHOP + self.guiTopBar['text'] = TTLocalizer.PaintYourToonTitle + self.guiTopBar['text_fg'] = (0, 1, 1, 1) + self.guiTopBar['text_scale'] = TTLocalizer.MATenterColorShop + self.accept('ColorShop-done', self.__handleColorShopDone) + self.dropRoom(self.colorWalls, self.colorProps) + self.toon.setPos(self.toonPosition) + self.colorShopOpening() + self.cos.enter(self.toon, self.shopsVisited) + if COLORSHOP not in self.shopsVisited: + self.shopsVisited.append(COLORSHOP) + + def exitColorShop(self): + self.squishRoom(self.colorWalls) + self.squishProp(self.colorProps) + self.cos.exit() + self.ignore('ColorShop-done') + + def __handleColorShopDone(self): + self.guiNextButton.hide() + self.guiLastButton.hide() + if self.cos.doneStatus == 'next': + self.cos.hideButtons() + self.goToNextShop() + else: + self.cos.hideButtons() + self.goToLastShop() + + def clothesShopOpening(self): + self.guiNextButton.show() + self.guiLastButton.show() + self.cls.showButtons() + self.rotateLeftButton.show() + self.rotateRightButton.show() + + def enterClothesShop(self): + self.shop = CLOTHESSHOP + self.guiTopBar['text'] = TTLocalizer.PickClothesTitle + self.guiTopBar['text_fg'] = (1, 0.92, 0.2, 1) + self.guiTopBar['text_scale'] = TTLocalizer.MATenterClothesShop + self.accept('ClothesShop-done', self.__handleClothesShopDone) + self.dropRoom(self.clothesWalls, self.clothesProps) + self.toon.setScale(self.toonScale) + self.toon.setPos(self.toonPosition) + if not self.progressing: + self.toon.setHpr(self.toonHpr) + self.clothesShopOpening() + self.cls.enter(self.toon) + if CLOTHESSHOP not in self.shopsVisited: + self.shopsVisited.append(CLOTHESSHOP) + + def exitClothesShop(self): + self.squishRoom(self.clothesWalls) + self.squishProp(self.clothesProps) + self.cls.exit() + self.ignore('ClothesShop-done') + + def __handleClothesShopDone(self): + self.guiNextButton.hide() + self.guiLastButton.hide() + if self.cls.doneStatus == 'next': + self.cls.hideButtons() + self.goToNextShop() + else: + self.cls.hideButtons() + self.goToLastShop() + + def nameShopOpening(self, task): + self.guiCheckButton.show() + self.guiLastButton.show() + if self.warp: + self.guiLastButton.hide() + if NAMESHOP not in self.shopsVisited: + self.shopsVisited.append(NAMESHOP) + return Task.done + + def enterNameShop(self): + self.shop = NAMESHOP + self.guiTopBar['text'] = TTLocalizer.NameToonTitle + self.guiTopBar['text_fg'] = (0.0, 0.98, 0.5, 1) + self.guiTopBar['text_scale'] = TTLocalizer.MATenterNameShop + self.accept('NameShop-done', self.__handleNameShopDone) + self.dropRoom(self.nameWalls, self.nameProps) + self.spotlight.setPos(2, -1.95, 0.41) + self.toon.setPos(Point3(1.5, -4, 0)) + self.toon.setH(120) + self.rotateLeftButton.hide() + self.rotateRightButton.hide() + if self.progressing: + waittime = self.leftTime + else: + waittime = 0.2 + self.ns.enter(self.toon, self.nameList, self.warp) + taskMgr.doMethodLater(waittime, self.nameShopOpening, 'nameShopOpeningTask') + + def exitNameShop(self): + self.squishRoom(self.nameWalls) + self.squishProp(self.nameProps) + self.spotlight.setPos(1.18, -1.27, 0.41) + self.ns.exit() + self.ignore('NameShop-done') + taskMgr.remove('nameShopOpeningTask') + + def rejectName(self): + self.ns.rejectName(TTLocalizer.RejectNameText) + + def __handleNameShopDone(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: MAKEATOON: Creating A Toon') + self.guiLastButton.hide() + self.guiCheckButton.hide() + if self.ns.getDoneStatus() == 'last': + self.ns.hideAll() + self.goToLastShop() + else: + self.doneStatus = 'created' + base.transitions.fadeOut(finishIval=EventInterval(self.doneEvent)) + + def __handleNext(self): + messenger.send('next') + + def __handleLast(self): + messenger.send('last') + + def __handleSkipTutorial(self): + messenger.send('skipTutorial') + + def enterDone(self): + pass + + def exitDone(self): + pass + + def create3DGui(self): + self.proto = loader.loadModel('phase_3/models/makeatoon/tt_m_ara_mat_protoMachine') + self.proto.setScale(0.2) + self.proto.reparentTo(render) + + def setup3DPicker(self): + self.accept('mouse1', self.mouseDown) + self.accept('mouse1-up', self.mouseUp) + self.pickerQueue = CollisionHandlerQueue() + self.pickerTrav = CollisionTraverser('MousePickerTraverser') + self.pickerTrav.setRespectPrevTransform(True) + self.pickerNode = CollisionNode('mouseRay') + self.pickerNP = camera.attachNewNode(self.pickerNode) + self.pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) + self.pickerRay = CollisionRay() + self.pickerNode.addSolid(self.pickerRay) + self.pickerTrav.addCollider(self.pickerNP, self.pickerQueue) + + def mouseDown(self): + self.notify.debug('Mouse 1 Down') + mpos = base.mouseWatcherNode.getMouse() + self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) + self.pickerTrav.traverse(render) + if self.pickerQueue.getNumEntries() > 0: + self.pickerQueue.sortEntries() + self.pickedObj = self.pickerQueue.getEntry(0).getIntoNodePath() + + def mouseUp(self): + self.notify.debug('Mouse 1 Up') + + def squishRoom(self, room): + if self.roomSquishIval and self.roomSquishIval.isPlaying(): + self.roomSquishIval.finish() + squishDuration = self.roomSquishActor.getDuration('squish') + self.roomSquishIval = Sequence(Func(self.roomSquishActor.play, 'squish'), Wait(squishDuration), Func(room.hide)) + self.roomSquishIval.start() + + def squishProp(self, prop): + if not prop.isEmpty(): + if self.propSquishIval and self.propSquishIval.isPlaying(): + self.propSquishIval.finish() + squishDuration = self.propSquishActor.getDuration('propSquish') + self.propSquishIval = Sequence(Func(self.propSquishActor.play, 'propSquish'), Wait(squishDuration), Func(prop.hide)) + self.propSquishIval.start() + + def dropRoom(self, walls, props): + + def propReparentTo(props): + if not props.isEmpty(): + props.reparentTo(self.propJoint) + + if self.dropIval and self.dropIval.isPlaying(): + self.dropIval.finish() + walls.reparentTo(self.dropJoint) + walls.show() + if not props.isEmpty(): + props.reparentTo(self.dropJoint) + props.show() + dropDuration = self.roomDropActor.getDuration('drop') + self.dropIval = Parallel(Sequence(Func(self.roomDropActor.play, 'drop'), Wait(dropDuration), Func(walls.reparentTo, self.squishJoint), Func(propReparentTo, props), Func(self.propSquishActor.pose, 'propSquish', 0), Func(self.roomSquishActor.pose, 'squish', 0)), Sequence(Wait(0.25), Func(self.smoke.show), Func(self.smoke.node().play), LerpColorScaleInterval(self.smoke, 0.5, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1)), Func(self.smoke.hide)), Func(self.spotlightActor.play, 'spotlightShake'), Func(self.playRandomCrashSound)) + self.dropIval.start() + + def startFocusOutIval(self): + if self.focusInIval.isPlaying(): + self.focusInIval.pause() + if not self.focusOutIval.isPlaying(): + self.focusOutIval = LerpScaleInterval(self.spotlight, 0.25, self.spotlightFinalScale) + self.focusOutIval.start() + + def startFocusInIval(self): + if self.focusOutIval.isPlaying(): + self.focusOutIval.pause() + if not self.focusInIval.isPlaying(): + self.focusInIval = LerpScaleInterval(self.spotlight, 0.25, self.spotlightOriginalScale) + self.focusInIval.start() + + def cleanupFocusOutIval(self): + if self.focusOutIval: + self.focusOutIval.finish() + del self.focusOutIval + + def cleanupFocusInIval(self): + if self.focusInIval: + self.focusInIval.finish() + del self.focusInIval + + def cleanupDropIval(self): + if self.dropIval: + self.dropIval.finish() + del self.dropIval + + def cleanupRoomSquishIval(self): + if self.roomSquishIval: + self.roomSquishIval.finish() + del self.roomSquishIval + + def cleanupPropSquishIval(self): + if self.propSquishIval: + self.propSquishIval.finish() + del self.propSquishIval + + def setToon(self, toon): + self.toon = toon + + def setNextButtonState(self, state): + self.guiNextButton['state'] = state + + def playRandomCrashSound(self): + index = random.randint(0, len(self.crashSounds) - 1) + base.playSfx(self.crashSounds[index], volume=self.sfxVolume) + + def rotateToonLeft(self, event): + taskMgr.add(self.rotateToonLeftTask, 'rotateToonLeftTask') + + def rotateToonLeftTask(self, task): + self.toon.setH(self.toon.getH() + self.hprDelta) + return task.cont + + def stopToonRotateLeftTask(self, event): + taskMgr.remove('rotateToonLeftTask') + + def rotateToonRight(self, event): + taskMgr.add(self.rotateToonRightTask, 'rotateToonRightTask') + + def rotateToonRightTask(self, task): + self.toon.setH(self.toon.getH() - self.hprDelta) + return task.cont + + def stopToonRotateRightTask(self, event): + taskMgr.remove('rotateToonRightTask') diff --git a/toontown/makeatoon/MakeAToonGlobals.py b/toontown/makeatoon/MakeAToonGlobals.py new file mode 100755 index 00000000..65753527 --- /dev/null +++ b/toontown/makeatoon/MakeAToonGlobals.py @@ -0,0 +1,13 @@ +GENDERSHOP = 1 +BODYSHOP = 2 +COLORSHOP = 3 +CLOTHESSHOP = 4 +NAMESHOP = 5 +halfButtonScale = (0.6, 0.6, 0.6) +halfButtonHoverScale = (0.7, 0.7, 0.7) +halfButtonInvertScale = (-0.6, 0.6, 0.6) +halfButtonInvertHoverScale = (-0.7, 0.7, 0.7) +buttonScale = (1.2, 1.2, 1.2) +buttonHoverScale = (1.4, 1.4, 1.4) +buttonInvertScale = (-1.2, 1.2, 1.2) +buttonInvertHoverScale = (-1.4, 1.4, 1.4) diff --git a/toontown/makeatoon/MakeClothesGUI.py b/toontown/makeatoon/MakeClothesGUI.py new file mode 100755 index 00000000..4cfe39b9 --- /dev/null +++ b/toontown/makeatoon/MakeClothesGUI.py @@ -0,0 +1,35 @@ +import ClothesGUI +from toontown.toon import ToonDNA + +class MakeClothesGUI(ClothesGUI.ClothesGUI): + notify = directNotify.newCategory('MakeClothesGUI') + + def __init__(self, doneEvent): + ClothesGUI.ClothesGUI.__init__(self, ClothesGUI.CLOTHES_MAKETOON, doneEvent) + + def setupScrollInterface(self): + self.dna = self.toon.getStyle() + gender = self.dna.getGender() + if gender != self.gender: + self.tops = ToonDNA.getRandomizedTops(gender, tailorId=ToonDNA.MAKE_A_TOON) + self.bottoms = ToonDNA.getRandomizedBottoms(gender, tailorId=ToonDNA.MAKE_A_TOON) + self.gender = gender + self.topChoice = 0 + self.bottomChoice = 0 + self.setupButtons() + + def setupButtons(self): + ClothesGUI.ClothesGUI.setupButtons(self) + if len(self.dna.torso) == 1: + if self.gender == 'm': + torsoStyle = 's' + elif self.girlInShorts == 1: + torsoStyle = 's' + else: + torsoStyle = 'd' + self.toon.swapToonTorso(self.dna.torso[0] + torsoStyle) + self.toon.loop('neutral', 0) + self.toon.swapToonColor(self.dna) + self.swapTop(0) + self.swapBottom(0) + return None diff --git a/toontown/makeatoon/NameGenerator.py b/toontown/makeatoon/NameGenerator.py new file mode 100755 index 00000000..c3c1b762 --- /dev/null +++ b/toontown/makeatoon/NameGenerator.py @@ -0,0 +1,362 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer, ToontownGlobals +import random + +class NameGenerator: + text = TextNode('text') + if process == 'client': + text.setFont(ToontownGlobals.getInterfaceFont()) + notify = DirectNotifyGlobal.directNotify.newCategory('NameGenerator') + boyTitles = [] + girlTitles = [] + neutralTitles = [] + boyFirsts = [] + girlFirsts = [] + neutralFirsts = [] + capPrefixes = [] + lastPrefixes = [] + lastSuffixes = [] + + def __init__(self): + self.generateLists() + + def generateLists(self): + self.boyTitles = [] + self.girlTitles = [] + self.neutralTitles = [] + self.boyFirsts = [] + self.girlFirsts = [] + self.neutralFirsts = [] + self.capPrefixes = [] + self.lastPrefixes = [] + self.lastSuffixes = [] + self.nameDictionary = {} + searchPath = DSearchPath() + if __debug__: + searchPath.appendDirectory(Filename('../resources/phase_3/etc')) + searchPath.appendDirectory(Filename('resources/phase_3/etc')) + searchPath.appendDirectory(Filename('/phase_3/etc')) + filename = Filename(TTLocalizer.NameShopNameMaster) + found = vfs.resolveFilename(filename, searchPath) + if not found: + self.notify.error("NameGenerator: Error opening name list text file '%s.'" % TTLocalizer.NameShopNameMaster) + input = StreamReader(vfs.openReadFile(filename, 1), 1) + currentLine = input.readline().strip() + while currentLine: + if currentLine.lstrip()[0:1] != '#': + a1 = currentLine.find('*') + a2 = currentLine.find('*', a1 + 1) + self.nameDictionary[int(currentLine[0:a1])] = (int(currentLine[a1 + 1:a2]), currentLine[a2 + 1:len(currentLine)]) + currentLine = input.readline().strip() + + masterList = [self.boyTitles, + self.girlTitles, + self.neutralTitles, + self.boyFirsts, + self.girlFirsts, + self.neutralFirsts, + self.capPrefixes, + self.lastPrefixes, + self.lastSuffixes] + for tu in self.nameDictionary.values(): + masterList[tu[0]].append(tu[1]) + + return 1 + + def _getNameParts(self, cat2part): + nameParts = [{}, + {}, + {}, + {}] + for id, tpl in self.nameDictionary.iteritems(): + cat, str = tpl + if cat in cat2part: + nameParts[cat2part[cat]][str] = id + + return nameParts + + def getMaleNameParts(self): + return self._getNameParts({0: 0, + 2: 0, + 3: 1, + 5: 1, + 6: 2, + 7: 2, + 8: 3}) + + def getFemaleNameParts(self): + return self._getNameParts({1: 0, + 2: 0, + 4: 1, + 5: 1, + 6: 2, + 7: 2, + 8: 3}) + + def getLastNamePrefixesCapped(self): + return self.capPrefixes + + def returnUniqueID(self, name, listnumber): + newtu = [(), (), ()] + if listnumber == 0: + newtu[0] = (0, name) + newtu[1] = (1, name) + newtu[2] = (2, name) + elif listnumber == 1: + newtu[0] = (3, name) + newtu[1] = (4, name) + newtu[2] = (5, name) + elif listnumber == 2: + newtu[0] = (6, name) + newtu[1] = (7, name) + else: + newtu[0] = (8, name) + for tu in self.nameDictionary.items(): + for g in newtu: + if tu[1] == g: + return tu[0] + + return -1 + + def findWidestInList(self, text, nameList): + maxWidth = 0 + maxName = '' + for name in nameList: + width = text.calcWidth(name) + if width > maxWidth: + maxWidth = text.calcWidth(name) + maxName = name + + print maxName + ' ' + str(maxWidth) + return maxName + + def findWidestName(self): + longestBoyTitle = self.findWidestInList(self.text, self.boyTitles + self.neutralTitles) + longestGirlTitle = self.findWidestInList(self.text, self.girlTitles + self.neutralTitles) + longestBoyFirst = self.findWidestInList(self.text, self.boyFirsts + self.neutralFirsts) + longestGirlFirst = self.findWidestInList(self.text, self.girlFirsts + self.neutralFirsts) + longestLastPrefix = self.findWidestInList(self.text, self.lastPrefixes) + longestLastSuffix = self.findWidestInList(self.text, self.lastSuffixes) + longestBoyFront = self.findWidestInList(self.text, [longestBoyTitle, longestBoyFirst]) + longestGirlFront = self.findWidestInList(self.text, [longestGirlTitle, longestGirlFirst]) + longestBoyName = longestBoyTitle + ' ' + longestBoyFirst + ' ' + longestLastPrefix + longestLastSuffix + longestGirlName = longestGirlTitle + ' ' + longestGirlFirst + ' ' + longestLastPrefix + longestLastSuffix + longestName = self.findWidestInList(self.text, [longestBoyName, longestGirlName]) + return longestName + + def findWidestTitleFirst(self): + longestBoyTitle = self.findWidestInList(self.text, self.boyTitles + self.neutralTitles) + longestGirlTitle = self.findWidestInList(self.text, self.girlTitles + self.neutralTitles) + longestBoyFirst = self.findWidestInList(self.text, self.boyFirsts + self.neutralFirsts) + longestGirlFirst = self.findWidestInList(self.text, self.girlFirsts + self.neutralFirsts) + longestBoyName = longestBoyTitle + ' ' + longestBoyFirst + longestGirlName = longestGirlTitle + ' ' + longestGirlFirst + longestName = self.findWidestInList(self.text, [longestBoyName, longestGirlName]) + + def findWidestTitle(self): + widestTitle = self.findWidestInList(self.text, self.neutralTitles + self.boyTitles + self.girlTitles) + return widestTitle + + def findWidestFirstName(self): + widestFirst = self.findWidestInList(self.text, self.neutralFirsts + self.boyFirsts + self.girlFirsts) + return widestFirst + + def findWidestLastName(self): + longestLastPrefix = self.findWidestInList(self.text, self.lastPrefixes) + longestLastSuffix = self.findWidestInList(self.text, self.lastSuffixes) + longestLastName = longestLastPrefix + longestLastSuffix + return longestLastName + + def findWidestNameWord(self): + widestWord = self.findWidestInList(self.text, [self.findWidestTitle(), self.findWidestFirstName(), self.findWidestLastName()]) + return widestWord + + def findWidestNameWidth(self): + name = self.findWidestName() + return self.text.calcWidth(name) + + def printWidestName(self): + name = self.findWidestName() + width = self.text.calcWidth(name) + widthStr = str(width) + print 'The widest name is: ' + name + ' (' + widthStr + ' units)' + + def printWidestLastName(self): + name = self.findWidestLastName() + width = self.text.calcWidth(name) + widthStr = str(width) + print 'The widest last name is: ' + name + ' (' + widthStr + ' units)' + + def randomName(self, boy = 0, girl = 0): + if boy and girl: + self.error("A name can't be both boy and girl!") + if not boy and not girl: + boy = random.choice([0, 1]) + girl = not boy + uberFlag = random.choice(['title-first', + 'title-last', + 'first', + 'last', + 'first-last', + 'title-first-last']) + titleFlag = 0 + if uberFlag == 'title-first' or uberFlag == 'title-last' or uberFlag == 'title-first-last': + titleFlag = 1 + firstFlag = 0 + if uberFlag == 'title-first' or uberFlag == 'first' or uberFlag == 'first-last' or uberFlag == 'title-first-last': + firstFlag = 1 + lastFlag = 0 + if uberFlag == 'title-last' or uberFlag == 'last' or uberFlag == 'first-last' or uberFlag == 'title-first-last': + lastFlag = 1 + retString = '' + if titleFlag: + titleList = self.neutralTitles[:] + if boy: + titleList += self.boyTitles + elif girl: + titleList += self.girlTitles + else: + self.error('Must be boy or girl.') + retString += random.choice(titleList) + ' ' + if firstFlag: + firstList = self.neutralFirsts[:] + if boy: + firstList += self.boyFirsts + elif girl: + firstList += self.girlFirsts + else: + self.error('Must be boy or girl.') + retString += random.choice(firstList) + if lastFlag: + retString += ' ' + if lastFlag: + lastPrefix = random.choice(self.lastPrefixes) + lastSuffix = random.choice(self.lastSuffixes) + if lastPrefix in self.capPrefixes: + lastSuffix = lastSuffix.capitalize() + retString += lastPrefix + lastSuffix + return retString + + def randomNameMoreinfo(self, boy = 0, girl = 0): + if boy and girl: + self.error("A name can't be both boy and girl!") + if not boy and not girl: + boy = random.choice([0, 1]) + girl = not boy + uberFlag = random.choice(['title-first', + 'title-last', + 'first', + 'last', + 'first-last', + 'title-first-last']) + titleFlag = 0 + if uberFlag == 'title-first' or uberFlag == 'title-last' or uberFlag == 'title-first-last': + titleFlag = 1 + firstFlag = 0 + if uberFlag == 'title-first' or uberFlag == 'first' or uberFlag == 'first-last' or uberFlag == 'title-first-last': + firstFlag = 1 + lastFlag = 0 + if uberFlag == 'title-last' or uberFlag == 'last' or uberFlag == 'first-last' or uberFlag == 'title-first-last': + lastFlag = 1 + retString = '' + uberReturn = [0, + 0, + 0, + '', + '', + '', + ''] + uberReturn[0] = titleFlag + uberReturn[1] = firstFlag + uberReturn[2] = lastFlag + titleList = self.neutralTitles[:] + if boy: + titleList += self.boyTitles + elif girl: + titleList += self.girlTitles + else: + self.error('Must be boy or girl.') + uberReturn[3] = random.choice(titleList) + firstList = self.neutralFirsts[:] + if boy: + firstList += self.boyFirsts + elif girl: + firstList += self.girlFirsts + else: + self.error('Must be boy or girl.') + uberReturn[4] = random.choice(firstList) + lastPrefix = random.choice(self.lastPrefixes) + lastSuffix = random.choice(self.lastSuffixes) + if lastPrefix in self.capPrefixes: + lastSuffix = lastSuffix.capitalize() + uberReturn[5] = lastPrefix + uberReturn[6] = lastSuffix + if titleFlag: + retString += uberReturn[3] + ' ' + if firstFlag: + retString += uberReturn[4] + if lastFlag: + retString += ' ' + if lastFlag: + retString += uberReturn[5] + uberReturn[6] + uberReturn.append(retString) + return uberReturn + + def printRandomNames(self, boy = 0, girl = 0, total = 1): + i = 0 + origBoy = boy + origGirl = girl + while i < total: + if not origBoy and not origGirl: + boy = random.choice([0, 1]) + girl = not boy + name = self.randomName(boy, girl) + width = self.text.calcWidth(name) + widthStr = str(width) + if boy: + print 'Boy: ' + name + ' (' + widthStr + ' units)' + if girl: + print 'Girl: ' + name + ' (' + widthStr + ' units)' + i += 1 + + def percentOver(self, limit = 9.0, samples = 1000): + i = 0 + over = 0 + while i < samples: + name = self.randomName() + width = self.text.calcWidth(name) + if width > limit: + over += 1 + i += 1 + + percent = float(over) / float(samples) * 100 + print 'Samples: ' + str(samples) + ' Over: ' + str(over) + ' Percent: ' + str(percent) + + def totalNames(self): + firsts = len(self.boyFirsts) + len(self.girlFirsts) + len(self.neutralFirsts) + print 'Total firsts: ' + str(firsts) + lasts = len(self.lastPrefixes) * len(self.lastSuffixes) + print 'Total lasts: ' + str(lasts) + neutralTitleFirsts = len(self.neutralTitles) * len(self.neutralFirsts) + boyTitleFirsts = len(self.boyTitles) * (len(self.neutralFirsts) + len(self.boyFirsts)) + len(self.neutralTitles) * len(self.boyFirsts) + girlTitleFirsts = len(self.girlTitles) * (len(self.neutralFirsts) + len(self.girlFirsts)) + len(self.neutralTitles) * len(self.girlFirsts) + totalTitleFirsts = neutralTitleFirsts + boyTitleFirsts + girlTitleFirsts + print 'Total title firsts: ' + str(totalTitleFirsts) + neutralTitleLasts = len(self.neutralTitles) * lasts + boyTitleLasts = len(self.boyTitles) * lasts + girlTitleLasts = len(self.girlTitles) * lasts + totalTitleLasts = neutralTitleLasts + boyTitleFirsts + girlTitleLasts + print 'Total title lasts: ' + str(totalTitleLasts) + neutralFirstLasts = len(self.neutralFirsts) * lasts + boyFirstLasts = len(self.boyFirsts) * lasts + girlFirstLasts = len(self.girlFirsts) * lasts + totalFirstLasts = neutralFirstLasts + boyFirstLasts + girlFirstLasts + print 'Total first lasts: ' + str(totalFirstLasts) + neutralTitleFirstLasts = neutralTitleFirsts * lasts + boyTitleFirstLasts = boyTitleFirsts * lasts + girlTitleFirstLasts = girlTitleFirsts * lasts + totalTitleFirstLasts = neutralTitleFirstLasts + boyTitleFirstLasts + girlTitleFirstLasts + print 'Total title first lasts: ' + str(totalTitleFirstLasts) + totalNames = firsts + lasts + totalTitleFirsts + totalTitleLasts + totalFirstLasts + totalTitleFirstLasts + print 'Total Names: ' + str(totalNames) diff --git a/toontown/makeatoon/NameShop.py b/toontown/makeatoon/NameShop.py new file mode 100755 index 00000000..9ef6b73b --- /dev/null +++ b/toontown/makeatoon/NameShop.py @@ -0,0 +1,964 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.task.TaskManagerGlobal import * +from direct.gui.DirectGui import * +from toontown.distributed.ToontownMsgTypes import * +from direct.directnotify import DirectNotifyGlobal +from direct.gui import OnscreenText +from otp.avatar import Avatar +from otp.chat import ChatManager +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toontowngui import TTDialog +import re +from toontown.toonbase import TTLocalizer +import NameGenerator +import random +from otp.distributed import PotentialAvatar +from otp.namepanel import NameCheck +from direct.distributed.PyDatagram import PyDatagram +from direct.showbase import PythonUtil +from toontown.toon import NPCToons +from direct.task import Task +from toontown.makeatoon.TTPickANamePattern import TTPickANamePattern +from pandac.PandaModules import TextEncoder +MAX_NAME_WIDTH = TTLocalizer.NSmaxNameWidth +ServerDialogTimeout = 3.0 + +class NameShop(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('NameShop') + + def __init__(self, makeAToon, doneEvent, avList, index): + StateData.StateData.__init__(self, doneEvent) + self.makeAToon = makeAToon + self.avList = avList + self.index = index + self.shopsVisited = [] + self.avId = -1 + self.avExists = 0 + self.names = ['', + '', + '', + ''] + self.toon = None + self.boy = 0 + self.girl = 0 + self.allTitles = [] + self.allFirsts = [] + self.allPrefixes = [] + self.allSuffixes = [] + self.titleIndex = 0 + self.firstIndex = 0 + self.prefixIndex = 0 + self.suffixIndex = 0 + self.titleActive = 0 + self.firstActive = 0 + self.lastActive = 0 + self.quickFind = 0 + self.listsLoaded = 0 + self.addedGenderSpecific = 0 + self.chastise = 0 + self.nameIndices = [-1, + -1, + -1, + -1] + self.nameFlags = [1, + 1, + 1, + 0] + self.dummyReturn = 2 + self.nameAction = 0 + self.pickANameGUIElements = [] + self.typeANameGUIElements = [] + self.textRolloverColor = Vec4(1, 1, 0, 1) + self.textDownColor = Vec4(0.5, 0.9, 1, 1) + self.textDisabledColor = Vec4(0.4, 0.8, 0.4, 1) + self.fsm = ClassicFSM.ClassicFSM('NameShop', [State.State('Init', self.enterInit, self.exitInit, ['PickAName']), + State.State('PickAName', self.enterPickANameState, self.exitPickANameState, ['TypeAName', 'Done']), + State.State('TypeAName', self.enterTypeANameState, self.exitTypeANameState, ['PickAName', + 'Approval', + 'Rejected']), + State.State('Approval', self.enterApprovalState, self.exitApprovalState, ['PickAName', 'ApprovalAccepted']), + State.State('ApprovalAccepted', self.enterApprovalAcceptedState, self.exitApprovalAcceptedState, ['Done']), + State.State('Rejected', self.enterRejectedState, self.exitRejectedState, ['TypeAName']), + State.State('Done', self.enterDone, self.exitDone, ['Init'])], 'Init', 'Done') + self.parentFSM = makeAToon.fsm + self.parentFSM.getStateNamed('NameShop').addChild(self.fsm) + self.nameGen = NameGenerator.NameGenerator() + self.fsm.enterInitialState() + self.requestingSkipTutorial = False + return + + def makeLabel(self, te, index, others): + alig = others[0] + listName = others[1] + if alig == TextNode.ARight: + newpos = (0.44, 0, 0) + elif alig == TextNode.ALeft: + newpos = (0, 0, 0) + else: + newpos = (0.2, 0, 0) + df = DirectFrame(state='normal', relief=None, text=te, text_scale=TTLocalizer.NSmakeLabel, text_pos=newpos, text_align=alig, textMayChange=0) + df.bind(DGG.B1PRESS, lambda x, df = df: self.nameClickedOn(listName, index)) + return df + + def nameClickedOn(self, listType, index): + if listType == 'title': + self.titleIndex = index + elif listType == 'first': + self.firstIndex = index + elif listType == 'prefix': + self.prefixIndex = index + else: + self.suffixIndex = index + self.updateLists() + self.__listsChanged() + + def enter(self, toon, usedNames, warp): + self.notify.debug('enter') + self.newwarp = warp + self.avExists = warp + if self.avExists: + for g in self.avList: + if g.position == self.index: + self.avId = g.id + + if toon == None: + return + else: + self.toon = toon + if self.toon.style.gender == 'm': + self.boy = 1 + self.girl = 0 + else: + self.boy = 0 + self.girl = 1 + self.usedNames = usedNames + if not self.addedGenderSpecific or self.oldBoy != self.boy: + self.oldBoy = self.boy + self.listsLoaded = 0 + self.allTitles = [' '] + [' '] + self.nameGen.boyTitles * self.boy + self.nameGen.girlTitles * self.girl + self.nameGen.neutralTitles + self.allTitles.sort() + self.allTitles += [' '] + [' '] + self.allFirsts = [' '] + [' '] + self.nameGen.boyFirsts * self.boy + self.nameGen.girlFirsts * self.girl + self.nameGen.neutralFirsts + self.allFirsts.sort() + self.allFirsts += [' '] + [' '] + try: + k = self.allFirsts.index('Von') + self.allFirsts[k] = 'von' + except: + print "NameShop: Couldn't find von" + + if not self.addedGenderSpecific: + nameShopGui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_nameShop') + self.namePanel = DirectFrame(parent=aspect2d, image=None, relief='flat', state='disabled', pos=(-0.42, 0, -0.15), image_pos=(0, 0, 0.025), frameColor=(1, 1, 1, 0.3)) + panel = nameShopGui.find('**/tt_t_gui_mat_namePanel') + self.panelFrame = DirectFrame(image=panel, scale=(0.75, 0.7, 0.7), relief='flat', frameColor=(1, 1, 1, 0), pos=(-0.0163, 0, 0.1199)) + self.panelFrame.reparentTo(self.namePanel, sort=1) + self.pickANameGUIElements.append(self.namePanel) + self.pickANameGUIElements.append(self.panelFrame) + self.nameResult.reparentTo(self.namePanel, sort=2) + self.circle = nameShopGui.find('**/tt_t_gui_mat_namePanelCircle') + self.titleCheck = self.makeCheckBox((-0.615, 0, 0.371), TTLocalizer.TitleCheckBox, (0, 0.25, 0.5, 1), self.titleToggle) + self.firstCheck = self.makeCheckBox((-0.2193, 0, 0.371), TTLocalizer.FirstCheckBox, (0, 0.25, 0.5, 1), self.firstToggle) + self.lastCheck = self.makeCheckBox((0.3886, 0, 0.371), TTLocalizer.LastCheckBox, (0, 0.25, 0.5, 1), self.lastToggle) + del self.circle + self.pickANameGUIElements.append(self.titleCheck) + self.pickANameGUIElements.append(self.firstCheck) + self.pickANameGUIElements.append(self.lastCheck) + self.titleCheck.reparentTo(self.namePanel, sort=2) + self.firstCheck.reparentTo(self.namePanel, sort=2) + self.lastCheck.reparentTo(self.namePanel, sort=2) + nameShopGui.removeNode() + self.lastprefixScrollList.reparentTo(self.namePanel) + self.lastprefixScrollList.decButton.wrtReparentTo(self.namePanel, sort=2) + self.lastprefixScrollList.incButton.wrtReparentTo(self.namePanel, sort=2) + self.lastsuffixScrollList.reparentTo(self.namePanel) + self.lastsuffixScrollList.decButton.wrtReparentTo(self.namePanel, sort=2) + self.lastsuffixScrollList.incButton.wrtReparentTo(self.namePanel, sort=2) + self.titleHigh.reparentTo(self.namePanel) + self.prefixHigh.reparentTo(self.namePanel) + self.firstHigh.reparentTo(self.namePanel) + self.suffixHigh.reparentTo(self.namePanel) + self.randomButton.reparentTo(self.namePanel, sort=2) + self.typeANameButton.reparentTo(self.namePanel, sort=2) + self.pickANameGUIElements.remove(self.titleScrollList) + self.pickANameGUIElements.remove(self.firstnameScrollList) + self.titleScrollList.destroy() + self.firstnameScrollList.destroy() + self.titleScrollList = self.makeScrollList(None, (-0.6, 0, 0.202), (1, 0.8, 0.8, 1), self.allTitles, self.makeLabel, [TextNode.ACenter, 'title']) + self.firstnameScrollList = self.makeScrollList(None, (-0.2, 0, 0.202), (0.8, 1, 0.8, 1), self.allFirsts, self.makeLabel, [TextNode.ACenter, 'first']) + self.pickANameGUIElements.append(self.titleScrollList) + self.pickANameGUIElements.append(self.firstnameScrollList) + self.titleScrollList.reparentTo(self.namePanel, sort=-1) + self.titleScrollList.decButton.wrtReparentTo(self.namePanel, sort=2) + self.titleScrollList.incButton.wrtReparentTo(self.namePanel, sort=2) + self.firstnameScrollList.reparentTo(self.namePanel, sort=-1) + self.firstnameScrollList.decButton.wrtReparentTo(self.namePanel, sort=2) + self.firstnameScrollList.incButton.wrtReparentTo(self.namePanel, sort=2) + self.listsLoaded = 1 + self.addedGenderSpecific = 1 + self.__randomName() + self.typeANameButton['text'] = TTLocalizer.TypeANameButton + if self.isLoaded == 0: + self.load() + self.ubershow(self.pickANameGUIElements) + self.acceptOnce('next', self.__handleDone) + self.acceptOnce('last', self.__handleBackward) + self.acceptOnce('skipTutorial', self.__handleSkipTutorial) + self.__listsChanged() + self.fsm.request('PickAName') + return + + def __overflowNameInput(self): + self.rejectName(TTLocalizer.NameTooLong) + + def exit(self): + self.notify.debug('exit') + if self.isLoaded == 0: + return None + self.ignore('next') + self.ignore('last') + self.ignore('skipTutorial') + self.hideAll() + return None + + def __listsChanged(self): + newname = '' + if self.listsLoaded: + if self.titleActive: + self.showList(self.titleScrollList) + self.titleHigh.show() + newtitle = self.titleScrollList['items'][self.titleScrollList.index + 2]['text'] + self.nameIndices[0] = self.nameGen.returnUniqueID(newtitle, 0) + newname += newtitle + ' ' + else: + self.nameIndices[0] = -1 + self.stealth(self.titleScrollList) + self.titleHigh.hide() + if self.firstActive: + self.showList(self.firstnameScrollList) + self.firstHigh.show() + newfirst = self.firstnameScrollList['items'][self.firstnameScrollList.index + 2]['text'] + if newfirst == 'von': + nt = 'Von' + else: + nt = newfirst + self.nameIndices[1] = self.nameGen.returnUniqueID(nt, 1) + if not self.titleActive and newfirst == 'von': + newfirst = 'Von' + newname += newfirst + else: + newname += newfirst + if newfirst == 'von': + self.nameFlags[1] = 0 + else: + self.nameFlags[1] = 1 + if self.lastActive: + newname += ' ' + else: + self.firstHigh.hide() + self.stealth(self.firstnameScrollList) + self.nameIndices[1] = -1 + if self.lastActive: + self.showList(self.lastprefixScrollList) + self.showList(self.lastsuffixScrollList) + self.prefixHigh.show() + self.suffixHigh.show() + lp = self.lastprefixScrollList['items'][self.lastprefixScrollList.index + 2]['text'] + ls = self.lastsuffixScrollList['items'][self.lastsuffixScrollList.index + 2]['text'] + self.nameIndices[2] = self.nameGen.returnUniqueID(lp, 2) + self.nameIndices[3] = self.nameGen.returnUniqueID(ls, 3) + newname += lp + if lp in self.nameGen.capPrefixes: + ls = ls.capitalize() + self.nameFlags[3] = 1 + else: + self.nameFlags[3] = 0 + newname += ls + else: + self.stealth(self.lastprefixScrollList) + self.stealth(self.lastsuffixScrollList) + self.prefixHigh.hide() + self.suffixHigh.hide() + self.nameIndices[2] = -1 + self.nameIndices[3] = -1 + self.titleIndex = self.titleScrollList.index + 2 + self.firstIndex = self.firstnameScrollList.index + 2 + self.prefixIndex = self.lastprefixScrollList.index + 2 + self.suffixIndex = self.lastsuffixScrollList.index + 2 + self.nameResult['text'] = newname + self.names[0] = newname + + def makeScrollList(self, gui, ipos, mcolor, nitems, nitemMakeFunction, nitemMakeExtraArgs): + self.notify.debug('makeScrollList') + it = nitems[:] + ds = DirectScrolledList(items=it, itemMakeFunction=nitemMakeFunction, itemMakeExtraArgs=nitemMakeExtraArgs, parent=aspect2d, relief=None, command=self.__listsChanged, pos=ipos, scale=0.6, incButton_image=(self.arrowUp, + self.arrowDown, + self.arrowHover, + self.arrowUp), incButton_relief=None, incButton_scale=(1.2, 1.2, -1.2), incButton_pos=(0.0189, 0, -0.5335), incButton_image0_color=mcolor, incButton_image1_color=mcolor, incButton_image2_color=mcolor, incButton_image3_color=Vec4(1, 1, 1, 0), decButton_image=(self.arrowUp, + self.arrowDown, + self.arrowHover, + self.arrowUp), decButton_relief=None, decButton_scale=(1.2, 1.2, 1.2), decButton_pos=(0.0195, 0, 0.1779), decButton_image0_color=mcolor, decButton_image1_color=mcolor, decButton_image2_color=mcolor, decButton_image3_color=Vec4(1, 1, 1, 0), itemFrame_pos=(-.2, 0, 0.028), itemFrame_scale=1.0, itemFrame_relief=DGG.RAISED, itemFrame_frameSize=(-0.07, + 0.5, + -0.52, + 0.12), itemFrame_frameColor=mcolor, itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=5, forceHeight=TTLocalizer.NSdirectScrolleList) + return ds + + def makeCheckBox(self, npos, ntex, ntexcolor, comm): + return DirectCheckButton(parent=aspect2d, relief=None, scale=0.1, boxBorder=0.08, boxImage=self.circle, boxImageScale=4, boxImageColor=VBase4(0, 0.25, 0.5, 1), boxRelief=None, pos=npos, text=ntex, text_fg=ntexcolor, text_scale=TTLocalizer.NSmakeCheckBox, text_pos=(0.2, 0), indicator_pos=(-0.566667, 0, -0.045), indicator_image_pos=(-0.26, 0, 0.075), command=comm, text_align=TextNode.ALeft) + + def makeHighlight(self, npos): + return DirectFrame(parent=aspect2d, relief='flat', scale=(0.552, 0, 0.11), state='disabled', frameSize=(-0.07, + 0.52, + -0.5, + 0.1), borderWidth=(0.01, 0.01), pos=npos, frameColor=(1, 0, 1, 0.4)) + + def titleToggle(self, value): + self.titleActive = self.titleCheck['indicatorValue'] + if not (self.titleActive or self.firstActive or self.lastActive): + self.titleActive = 1 + self.__listsChanged() + if self.titleActive: + self.titleScrollList.refresh() + self.updateCheckBoxes() + + def firstToggle(self, value): + self.firstActive = self.firstCheck['indicatorValue'] + if self.chastise == 2: + messenger.send('NameShop-mickeyChange', [[TTLocalizer.ApprovalForName1, TTLocalizer.ApprovalForName2]]) + self.chastise = 0 + if not self.firstActive and not self.lastActive: + self.firstActive = 1 + messenger.send('NameShop-mickeyChange', [[TTLocalizer.MustHaveAFirstOrLast1, TTLocalizer.MustHaveAFirstOrLast2]]) + self.chastise = 1 + self.__listsChanged() + if self.firstActive: + self.firstnameScrollList.refresh() + self.updateCheckBoxes() + + def lastToggle(self, value): + self.lastActive = self.lastCheck['indicatorValue'] + if self.chastise == 1: + messenger.send('NameShop-mickeyChange', [[TTLocalizer.ApprovalForName1, TTLocalizer.ApprovalForName2]]) + self.chastise = 0 + if not self.firstActive and not self.lastActive: + self.lastActive = 1 + messenger.send('NameShop-mickeyChange', [[TTLocalizer.MustHaveAFirstOrLast1, TTLocalizer.MustHaveAFirstOrLast2]]) + self.chastise = 2 + self.__listsChanged() + if self.lastActive: + self.lastprefixScrollList.refresh() + self.lastsuffixScrollList.refresh() + self.updateCheckBoxes() + + def updateCheckBoxes(self): + self.titleCheck['indicatorValue'] = self.titleActive + self.titleCheck.setIndicatorValue() + self.firstCheck['indicatorValue'] = self.firstActive + self.firstCheck.setIndicatorValue() + self.lastCheck['indicatorValue'] = self.lastActive + self.lastCheck.setIndicatorValue() + + def load(self): + self.notify.debug('load') + if self.isLoaded == 1: + return None + nameBalloon = loader.loadModel('phase_3/models/props/chatbox_input') + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_nameShop') + self.arrowUp = gui.find('**/tt_t_gui_mat_namePanelArrowUp') + self.arrowDown = gui.find('**/tt_t_gui_mat_namePanelArrowDown') + self.arrowHover = gui.find('**/tt_t_gui_mat_namePanelArrowHover') + self.squareUp = gui.find('**/tt_t_gui_mat_namePanelSquareUp') + self.squareDown = gui.find('**/tt_t_gui_mat_namePanelSquareDown') + self.squareHover = gui.find('**/tt_t_gui_mat_namePanelSquareHover') + typePanel = gui.find('**/tt_t_gui_mat_typeNamePanel') + self.typeNamePanel = DirectFrame(parent=aspect2d, image=None, relief='flat', scale=(0.75, 0.7, 0.7), state='disabled', pos=(-0.0163333, 0, 0.075), image_pos=(0, 0, 0.025), frameColor=(1, 1, 1, 0)) + self.typePanelFrame = DirectFrame(image=typePanel, relief='flat', frameColor=(1, 1, 1, 0), pos=(-0.008, 0, 0.019)) + self.typePanelFrame.reparentTo(self.typeNamePanel, sort=1) + self.typeANameGUIElements.append(self.typeNamePanel) + self.typeANameGUIElements.append(self.typePanelFrame) + self.nameLabel = OnscreenText.OnscreenText(TTLocalizer.PleaseTypeName, parent=aspect2d, style=OnscreenText.ScreenPrompt, scale=TTLocalizer.NSnameLabel, pos=(-0.0163333, 0.53)) + self.nameLabel.wrtReparentTo(self.typeNamePanel, sort=2) + self.typeANameGUIElements.append(self.nameLabel) + self.typeNotification = OnscreenText.OnscreenText(TTLocalizer.AllNewNames, parent=aspect2d, style=OnscreenText.ScreenPrompt, scale=TTLocalizer.NStypeNotification, pos=(-0.0163333, 0.15)) + self.typeNotification.wrtReparentTo(self.typeNamePanel, sort=2) + self.typeANameGUIElements.append(self.typeNotification) + self.nameMessages = OnscreenText.OnscreenText(TTLocalizer.NameMessages, parent=aspect2d, style=OnscreenText.ScreenPrompt, scale=0.06, pos=(-0.0163333, -0.05)) + self.nameMessages.wrtReparentTo(self.typeNamePanel, sort=2) + self.typeANameGUIElements.append(self.nameMessages) + self.nameEntry = DirectEntry(parent=aspect2d, relief=None, scale=TTLocalizer.NSnameEntry, entryFont=getToonFont(), width=MAX_NAME_WIDTH, numLines=2, focus=0, cursorKeys=1, pos=(0.0, 0.0, 0.39), text_align=TextNode.ACenter, command=self.__typedAName, autoCapitalize=1) + self.nameEntry.wrtReparentTo(self.typeNamePanel, sort=2) + self.typeANameGUIElements.append(self.nameEntry) + self.submitButton = DirectButton(parent=aspect2d, relief=None, image=(self.squareUp, + self.squareDown, + self.squareHover, + self.squareUp), image_scale=(1.2, 0, 1.1), pos=(-0.01, 0, -0.25), text=TTLocalizer.NameShopSubmitButton, text_scale=0.06, text_pos=(0, -0.02), command=self.__typedAName) + self.submitButton.wrtReparentTo(self.typeNamePanel, sort=2) + self.typeNamePanel.setPos(-0.42, 0, -0.078) + self.typeANameGUIElements.append(self.submitButton) + self.randomButton = DirectButton(parent=aspect2d, relief=None, image=(self.squareUp, + self.squareDown, + self.squareHover, + self.squareUp), image_scale=(1.15, 1.1, 1.1), scale=(1.05, 1, 1), pos=(0, 0, -0.25), text=TTLocalizer.ShuffleButton, text_scale=0.06, text_pos=(0, -0.02), command=self.__randomName) + self.pickANameGUIElements.append(self.randomButton) + self.typeANameButton = DirectButton(parent=aspect2d, relief=None, image=(self.squareUp, + self.squareDown, + self.squareHover, + self.squareUp), image_scale=(1, 1.1, 0.9), pos=(0.0033, 0, -.38833), scale=(1.2, 1, 1.2), text=TTLocalizer.TypeANameButton, text_scale=TTLocalizer.NStypeANameButton, text_pos=TTLocalizer.NStypeANameButtonPos, command=self.__typeAName) + self.pickANameGUIElements.append(self.typeANameButton) + self.nameResult = DirectLabel(parent=aspect2d, relief=None, scale=TTLocalizer.NSnameResult, pos=(0.005, 0, 0.585), text=' \n ', text_scale=0.8, text_align=TextNode.ACenter, text_wordwrap=MAX_NAME_WIDTH) + self.pickANameGUIElements.append(self.nameResult) + self.allPrefixes = self.nameGen.lastPrefixes[:] + self.allSuffixes = self.nameGen.lastSuffixes[:] + self.allPrefixes.sort() + self.allSuffixes.sort() + self.allPrefixes = [' '] + [' '] + self.allPrefixes + [' '] + [' '] + self.allSuffixes = [' '] + [' '] + self.allSuffixes + [' '] + [' '] + self.titleScrollList = self.makeScrollList(gui, (-0.6, 0, 0.202), (1, 0.8, 0.8, 1), self.allTitles, self.makeLabel, [TextNode.ACenter, 'title']) + self.firstnameScrollList = self.makeScrollList(gui, (-0.2, 0, 0.202), (0.8, 1, 0.8, 1), self.allFirsts, self.makeLabel, [TextNode.ACenter, 'first']) + self.lastprefixScrollList = self.makeScrollList(gui, (0.2, 0, 0.202), (0.8, 0.8, 1, 1), self.allPrefixes, self.makeLabel, [TextNode.ARight, 'prefix']) + self.lastsuffixScrollList = self.makeScrollList(gui, (0.55, 0, 0.202), (0.8, 0.8, 1, 1), self.allSuffixes, self.makeLabel, [TextNode.ALeft, 'suffix']) + gui.removeNode() + self.pickANameGUIElements.append(self.lastprefixScrollList) + self.pickANameGUIElements.append(self.lastsuffixScrollList) + self.pickANameGUIElements.append(self.titleScrollList) + self.pickANameGUIElements.append(self.firstnameScrollList) + self.titleHigh = self.makeHighlight((-0.710367, 0.0, 0.122967)) + self.firstHigh = self.makeHighlight((-0.310367, 0.0, 0.122967)) + self.pickANameGUIElements.append(self.titleHigh) + self.pickANameGUIElements.append(self.firstHigh) + self.prefixHigh = self.makeHighlight((0.09, 0.0, 0.122967)) + self.suffixHigh = self.makeHighlight((0.44, 0.0, 0.122967)) + self.pickANameGUIElements.append(self.prefixHigh) + self.pickANameGUIElements.append(self.suffixHigh) + nameBalloon.removeNode() + imageList = (guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')) + buttonImage = [imageList, imageList] + buttonText = [TTLocalizer.NameShopContinueSubmission, TTLocalizer.NameShopChooseAnother] + self.approvalDialog = DirectDialog(relief=None, image=DGG.getDefaultDialogGeom(), dialogName='approvalstate', topPad=0, fadeScreen=0.2, pos=(0, 0.1, 0.1), button_relief=None, image_color=GlobalDialogColor, text_align=TextNode.ACenter, text=TTLocalizer.NameShopToonCouncil, buttonTextList=buttonText, buttonImageList=buttonImage, buttonValueList=[1, 0], command=self.approvalAction) + self.approvalDialog.buttonList[0].setPos(0, 0, -.3) + self.approvalDialog.buttonList[1].setPos(0, 0, -.43) + self.approvalDialog['image_scale'] = (0.8, 1, 0.77) + for x in xrange(0, 2): + self.approvalDialog.buttonList[x]['text_pos'] = (0, -.01) + self.approvalDialog.buttonList[x]['text_scale'] = (0.04, 0.05999) + self.approvalDialog.buttonList[x].setScale(1.2, 1, 1) + + self.approvalDialog.hide() + guiButton.removeNode() + self.uberhide(self.typeANameGUIElements) + self.uberhide(self.pickANameGUIElements) + self.isLoaded = 1 + + def ubershow(self, guiObjectsToShow): + self.notify.debug('ubershow %s' % str(guiObjectsToShow)) + for x in guiObjectsToShow: + try: + x.show() + except: + print 'NameShop: Tried to show already removed object' + + def hideAll(self): + self.uberhide(self.pickANameGUIElements) + self.uberhide(self.typeANameGUIElements) + + def uberhide(self, guiObjectsToHide): + self.notify.debug('uberhide %s' % str(guiObjectsToHide)) + for x in guiObjectsToHide: + try: + x.hide() + except: + print 'NameShop: Tried to hide already removed object' + + def uberdestroy(self, guiObjectsToDestroy): + self.notify.debug('uberdestroy %s' % str(guiObjectsToDestroy)) + for x in guiObjectsToDestroy: + try: + x.destroy() + del x + except: + print 'NameShop: Tried to destroy already removed object' + + def getNameIndices(self): + return self.nameIndices + + def getNameFlags(self): + return self.nameFlags + + def getNameAction(self): + return self.nameAction + + def unload(self): + self.notify.debug('unload') + if self.isLoaded == 0: + return None + self.exit() + cleanupDialog('globalDialog') + self.uberdestroy(self.pickANameGUIElements) + self.uberdestroy(self.typeANameGUIElements) + del self.toon + self.approvalDialog.cleanup() + del self.approvalDialog + self.parentFSM.getStateNamed('NameShop').removeChild(self.fsm) + del self.parentFSM + del self.fsm + self.ignoreAll() + self.isLoaded = 0 + self.makeAToon = None + + def _checkNpcNames(self, name): + + def match(npcName, name = name): + name = TextEncoder().encodeWtext(name) + name = name.strip() + return TextEncoder.upper(npcName) == TextEncoder.upper(name) + + for npcId in NPCToons.NPCToonDict.keys(): + npcName = NPCToons.NPCToonDict[npcId][1] + if match(npcName): + self.notify.info('name matches NPC name "%s"' % npcName) + return TTLocalizer.NCGeneric + + def nameIsValid(self, name): + self.notify.debug('nameIsValid') + if name in self.usedNames: + return TTLocalizer.ToonAlreadyExists % name + problem = NameCheck.checkName(name, [self._checkNpcNames], font=self.nameEntry.getFont()) + if problem: + return problem + return None + + def setShopsVisited(self, list): + self.shopsVisited = list + + def __handleDone(self): + if self.fsm.getCurrentState().getName() == 'TypeAName': + self.__typedAName() + else: + self.__isFirstTime() + + def __handleSkipTutorial(self): + self.__createAvatar(skipTutorial=True) + + def __handleBackward(self): + self.doneStatus = 'last' + messenger.send(self.doneEvent) + + def __handleChastised(self): + self.chastiseDialog.cleanup() + + def __createAvatar(self, skipTutorial = False, *args): + self.notify.debug('__createAvatar') + if self.fsm.getCurrentState().getName() == 'TypeAName': + self.__typedAName() + return + if not self.avExists or self.avExists and self.avId == 'deleteMe': + self.serverCreateAvatar(skipTutorial) + elif self.names[0] == '': + self.rejectName(TTLocalizer.EmptyNameError) + else: + rejectReason = self.nameIsValid(self.names[0]) + if rejectReason != None: + self.rejectName(rejectReason) + else: + self.checkNamePattern() + return + + def acceptName(self): + self.notify.debug('acceptName') + self.toon.setName(self.names[0]) + self.doneStatus = 'done' + self.storeSkipTutorialRequest() + messenger.send(self.doneEvent) + + def rejectName(self, str): + self.notify.debug('rejectName') + self.names[0] = '' + self.rejectDialog = TTDialog.TTGlobalDialog(doneEvent='rejectDone', message=str, style=TTDialog.Acknowledge) + self.rejectDialog.show() + self.acceptOnce('rejectDone', self.__handleReject) + + def __handleReject(self): + self.rejectDialog.cleanup() + self.nameEntry['focus'] = 1 + self.typeANameButton.show() + self.acceptOnce('next', self.__handleDone) + + def restoreIndexes(self, oi): + self.titleIndex = oi[0] + self.firstIndex = oi[1] + self.prefixIndex = oi[2] + self.suffixIndex = oi[3] + + def stealth(self, listToDo): + listToDo.decButton['state'] = 'disabled' + listToDo.incButton['state'] = 'disabled' + for item in listToDo['items']: + if item.__class__.__name__ != 'str': + item.hide() + + def showList(self, listToDo): + listToDo.show() + listToDo.decButton['state'] = 'normal' + listToDo.incButton['state'] = 'normal' + + def updateLists(self): + oldindex = [self.titleIndex, + self.firstIndex, + self.prefixIndex, + self.suffixIndex] + self.titleScrollList.scrollTo(self.titleIndex - 2) + self.restoreIndexes(oldindex) + self.firstnameScrollList.scrollTo(self.firstIndex - 2) + self.restoreIndexes(oldindex) + self.lastprefixScrollList.scrollTo(self.prefixIndex - 2) + self.restoreIndexes(oldindex) + self.lastsuffixScrollList.scrollTo(self.suffixIndex - 2) + self.restoreIndexes(oldindex) + + def __randomName(self): + self.notify.debug('Finding random name') + uberReturn = self.nameGen.randomNameMoreinfo(self.boy, self.girl) + flags = uberReturn[:3] + names = uberReturn[3:7] + fullName = uberReturn[-1] + self._updateGuiWithPickAName(flags, names, fullName) + + def _updateGuiWithPickAName(self, flags, names, fullName): + uberReturn = flags + names + [fullName] + self.names[0] = uberReturn[len(uberReturn) - 1] + self.titleActive = 0 + self.firstActive = 0 + self.lastActive = 0 + if uberReturn[0]: + self.titleActive = 1 + if uberReturn[1]: + self.firstActive = 1 + if uberReturn[2]: + self.lastActive = 1 + try: + self.titleIndex = self.allTitles.index(uberReturn[3]) + self.nameIndices[0] = self.nameGen.returnUniqueID(uberReturn[3], 0) + self.nameFlags[0] = 1 + except: + print 'NameShop : Should have found title, uh oh!' + print uberReturn + + try: + self.firstIndex = self.allFirsts.index(uberReturn[4]) + self.nameIndices[1] = self.nameGen.returnUniqueID(uberReturn[4], 1) + self.nameFlags[1] = 1 + except: + print 'NameShop : Should have found first name, uh oh!' + print uberReturn + + try: + self.prefixIndex = self.allPrefixes.index(uberReturn[5]) + self.suffixIndex = self.allSuffixes.index(uberReturn[6].lower()) + self.nameIndices[2] = self.nameGen.returnUniqueID(uberReturn[5], 2) + self.nameIndices[3] = self.nameGen.returnUniqueID(uberReturn[6].lower(), 3) + if uberReturn[5] in self.nameGen.capPrefixes: + self.nameFlags[3] = 1 + else: + self.nameFlags[3] = 0 + except: + print 'NameShop : Some part of last name not found, uh oh!' + print uberReturn + + self.updateCheckBoxes() + self.updateLists() + self.nameResult['text'] = self.names[0] + + def findTempName(self): + colorstring = TTLocalizer.ColorfulToon + animaltype = TTLocalizer.AnimalToSpecies[self.toon.style.getAnimal()] + tempname = colorstring + ' ' + animaltype + if not TTLocalizer.NScolorPrecede: + tempname = animaltype + ' ' + colorstring + self.names[0] = tempname + tempname = '"' + tempname + '"' + return tempname + + def enterInit(self): + self.notify.debug('enterInit') + + def exitInit(self): + pass + + def enterPickANameState(self): + self.notify.debug('enterPickANameState') + self.ubershow(self.pickANameGUIElements) + self.nameAction = 1 + self.__listsChanged() + + def exitPickANameState(self): + self.uberhide(self.pickANameGUIElements) + + def enterTypeANameState(self): + self.notify.debug('enterTypeANameState') + self.ubershow(self.typeANameGUIElements) + self.typeANameButton.show() + self.typeANameButton.wrtReparentTo(aspect2d, sort=2) + self.nameEntry.set('') + self.nameEntry['focus'] = 1 + + def __typeAName(self): + if self.fsm.getCurrentState().getName() == 'TypeAName': + self.typeANameButton['text'] = TTLocalizer.TypeANameButton + self.typeANameButton.wrtReparentTo(self.namePanel, sort=2) + self.fsm.request('PickAName') + else: + self.typeANameButton['text'] = TTLocalizer.PickANameButton + self.typeANameButton.wrtReparentTo(aspect2d, sort=2) + self.typeANameButton.show() + self.fsm.request('TypeAName') + + def __typedAName(self, *args): + self.notify.debug('__typedAName') + self.nameEntry['focus'] = 0 + name = self.nameEntry.get() + name = TextEncoder().decodeText(name) + name = name.strip() + name = TextEncoder().encodeWtext(name) + self.nameEntry.enterText(name) + problem = self.nameIsValid(self.nameEntry.get()) + if problem: + self.rejectName(problem) + return + self.checkNameTyped(justCheck=True) + + def exitTypeANameState(self): + self.typeANameButton.wrtReparentTo(self.namePanel, sort=2) + self.uberhide(self.typeANameGUIElements) + + def enterApprovalState(self): + self.notify.debug('enterApprovalState') + tempname = self.findTempName() + self.approvalDialog['text'] = TTLocalizer.NameShopToonCouncil + tempname + self.approvalDialog.show() + + def approvalAction(self, value): + self.notify.debug('approvalAction') + self.approvalDialog.hide() + if value: + self.nameAction = 2 + if not self.makeAToon.warp: + self.__isFirstTime() + else: + self.serverCreateAvatar() + else: + self.typeANameButton['text'] = TTLocalizer.TypeANameButton + self.fsm.request('PickAName') + + def exitApprovalState(self): + pass + + def enterApprovalAcceptedState(self): + self.notify.debug('enterApprovalAcceptedState') + self.doneStatus = 'done' + self.storeSkipTutorialRequest() + messenger.send(self.doneEvent) + + def exitApprovalAcceptedState(self): + pass + + def enterRejectedState(self): + self.notify.debug('enterRejectedState') + self.rejectedDialog = TTDialog.TTGlobalDialog(doneEvent='rejectedDone', message=TTLocalizer.NameShopNameRejected, style=TTDialog.Acknowledge) + self.rejectedDialog.show() + self.acceptOnce('rejectedDone', self.__handleRejected) + + def __handleRejected(self): + self.rejectedDialog.cleanup() + self.fsm.request('TypeAName') + + def exitRejectedState(self): + pass + + def enterDone(self): + self.notify.debug('enterDone') + return None + + def exitDone(self): + return None + + def nameShopHandler(self, msgType, di): + self.notify.debug('nameShopHandler') + if msgType == CLIENT_CREATE_AVATAR_RESP: + self.handleCreateAvatarResponseMsg(di) + return None + + def checkNamePattern(self): + self.notify.debug('checkNamePattern') + base.cr.csm.sendSetNamePattern(self.avId, + self.nameIndices[0], self.nameFlags[0], + self.nameIndices[1], self.nameFlags[1], + self.nameIndices[2], self.nameFlags[2], + self.nameIndices[3], self.nameFlags[3], + self.handleSetNamePatternResp) + self.waitForServer() + + def handleSetNamePatternResp(self, avId, status): + self.notify.debug('handleSetNamePatternResp') + self.cleanupWaitForServer() + if avId != self.avId: + self.notify.debug("doid's don't match up!") + self.rejectName(TTLocalizer.NameError) + if status == 1: + style = self.toon.getStyle() + avDNA = style.makeNetString() + self.notify.debug('pattern name accepted') + newPotAv = PotentialAvatar.PotentialAvatar(avId, self.names, avDNA, self.index, 0) + self.avList.append(newPotAv) + self.doneStatus = 'done' + self.storeSkipTutorialRequest() + messenger.send(self.doneEvent) + else: + self.notify.debug('name pattern rejected') + self.rejectName(TTLocalizer.NameError) + return None + + def _submitTypeANameAsPickAName(self): + pnp = TTPickANamePattern(self.nameEntry.get(), self.toon.style.gender) + if pnp.hasNamePattern(): + pattern = pnp.getNamePattern() + self.fsm.request('PickAName') + flags = [pattern[0] != -1, pattern[1] != -1, pattern[2] != -1] + names = [] + for i in xrange(len(pattern)): + if pattern[i] != -1: + names.append(pnp.getNamePartString(self.toon.style.gender, i, pattern[i])) + else: + names.append('') + + fullName = pnp.getNameString(pattern, self.toon.style.gender) + self._updateGuiWithPickAName(flags, names, fullName) + self.__handleDone() + return True + return False + + def checkNameTyped(self, justCheck = False): + self.notify.debug('checkNameTyped') + if self._submitTypeANameAsPickAName(): + return + if justCheck: + avId = 0 + else: + avId = self.avId + base.cr.csm.sendSetNameTyped(avId, self.nameEntry.get(), self.handleSetNameTypedResp) + self.waitForServer() + + def handleSetNameTypedResp(self, avId, status): + self.notify.debug('handleSetNameTypedResp') + self.cleanupWaitForServer() + if avId and avId != self.avId: + self.notify.debug("doid's don't match up!") + self.rejectName(TTLocalizer.NameError) + if avId == 0: + if status == 1: + self.notify.debug('name check pending') + self.fsm.request('Approval') + elif status == 0: + self.notify.debug('name check rejected') + self.fsm.request('TypeAName') + self.rejectName(TTLocalizer.NameError) + else: + self.notify.debug('typed name response did not contain any return fields') + self.rejectName(TTLocalizer.NameError) + else: + if status == 1: + style = self.toon.getStyle() + avDNA = style.makeNetString() + self.names[1] = self.nameEntry.get() + self.notify.debug('typed name needs approval') + newPotAv = PotentialAvatar.PotentialAvatar(avId, self.names, avDNA, self.index, 1) + if not self.newwarp: + self.avList.append(newPotAv) + self.fsm.request('ApprovalAccepted') + elif status == 0: + self.fsm.request('Rejected') + else: + self.notify.debug("name typed accepted but didn't fill any return fields") + self.rejectName(TTLocalizer.NameError) + + def serverCreateAvatar(self, skipTutorial = False): + self.notify.debug('serverCreateAvatar') + style = self.toon.getStyle() + self.newDNA = style.makeNetString() + self.requestingSkipTutorial = skipTutorial + if not self.avExists or self.avExists and self.avId == 'deleteMe': + base.cr.csm.sendCreateAvatar(style, '', self.index) + self.accept('nameShopCreateAvatarDone', self.handleCreateAvatarResponse) + else: + self.checkNameTyped() + self.notify.debug('Ending Make A Toon: %s' % self.toon.style) + + def handleCreateAvatarResponse(self, avId): + self.notify.debug('handleCreateAvatarResponse') + self.notify.debug('avatar with default name accepted') + self.avId = avId + self.avExists = 1 + if self.nameAction == 0: + self.toon.setName(self.names[0]) + newPotAv = PotentialAvatar.PotentialAvatar(self.avId, self.names, self.newDNA, self.index, 1) + self.avList.append(newPotAv) + self.doneStatus = 'done' + self.storeSkipTutorialRequest() + messenger.send(self.doneEvent) + elif self.nameAction == 1: + self.checkNamePattern() + elif self.nameAction == 2: + self.checkNameTyped() + else: + self.notify.debug('avatar invalid nameAction') + self.rejectName(TTLocalizer.NameError) + + def waitForServer(self): + self.waitForServerDialog = TTDialog.TTDialog(text=TTLocalizer.WaitingForNameSubmission, style=TTDialog.NoButtons) + self.waitForServerDialog.show() + + def cleanupWaitForServer(self): + if self.waitForServerDialog != None: + self.waitForServerDialog.cleanup() + self.waitForServerDialog = None + return + + def printTypeANameInfo(self, str): + sourceFilename, lineNumber, functionName = PythonUtil.stackEntryInfo(1) + self.notify.debug('========================================\n%s : %s : %s' % (sourceFilename, lineNumber, functionName)) + self.notify.debug(str) + curPos = self.typeANameButton.getPos() + self.notify.debug('Pos = %.2f %.2f %.2f' % (curPos[0], curPos[1], curPos[2])) + parent = self.typeANameButton.getParent() + parentPos = parent.getPos() + self.notify.debug('Parent = %s' % parent) + self.notify.debug('ParentPos = %.2f %.2f %.2f' % (parentPos[0], parentPos[1], parentPos[2])) + + def storeSkipTutorialRequest(self): + base.cr.skipTutorialRequest = self.requestingSkipTutorial + + def __isFirstTime(self): + if self.makeAToon.warp: + self.__createAvatar() + else: + self.promptTutorial() + + def promptTutorial(self): + self.promptTutorialDialog = TTDialog.TTDialog(parent=aspect2dp, text=TTLocalizer.PromptTutorial, text_scale=0.06, text_align=TextNode.ACenter, text_wordwrap=22, command=self.__openTutorialDialog, fadeScreen=0.5, style=TTDialog.TwoChoice, buttonTextList=[TTLocalizer.MakeAToonEnterTutorial, TTLocalizer.MakeAToonSkipTutorial], button_text_scale=0.06, buttonPadSF=5.5, sortOrder=NO_FADE_SORT_INDEX) + self.promptTutorialDialog.show() + + def __openTutorialDialog(self, choice = 0): + if choice == 1: + self.notify.debug('enterTutorial') + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ENTERTUTORIAL: Enter Tutorial') + self.__createAvatar() + else: + self.notify.debug('skipTutorial') + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: SKIPTUTORIAL: Skip Tutorial') + self.__handleSkipTutorial() + self.promptTutorialDialog.destroy() diff --git a/toontown/makeatoon/ShuffleButton.py b/toontown/makeatoon/ShuffleButton.py new file mode 100755 index 00000000..46869042 --- /dev/null +++ b/toontown/makeatoon/ShuffleButton.py @@ -0,0 +1,142 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from MakeAToonGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +import random + +class ShuffleButton: + notify = DirectNotifyGlobal.directNotify.newCategory('ShuffleButton') + + def __init__(self, parent, fetchEvent): + self.parent = parent + self.fetchEvent = fetchEvent + self.history = [0] + self.historyPtr = 0 + self.maxHistory = 10 + self.load() + + def load(self): + gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + shuffleFrame = gui.find('**/tt_t_gui_mat_shuffleFrame') + shuffleUp = gui.find('**/tt_t_gui_mat_shuffleUp') + shuffleDown = gui.find('**/tt_t_gui_mat_shuffleDown') + shuffleArrowUp = gui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDown = gui.find('**/tt_t_gui_mat_shuffleArrowDown') + shuffleArrowDisabled = gui.find('**/tt_t_gui_mat_shuffleArrowDisabled') + gui.removeNode() + del gui + self.parentFrame = DirectFrame(parent=self.parent.parentFrame, relief=DGG.RAISED, pos=(0, 0, -1), frameColor=(1, 0, 0, 0)) + self.shuffleFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, frameColor=(1, 1, 1, 1)) + self.shuffleFrame.hide() + self.shuffleBtn = DirectButton(parent=self.parentFrame, relief=None, image=(shuffleUp, shuffleDown, shuffleUp), image_scale=halfButtonInvertScale, image1_scale=(-0.63, 0.6, 0.6), image2_scale=(-0.63, 0.6, 0.6), text=(TTLocalizer.ShuffleButton, + TTLocalizer.ShuffleButton, + TTLocalizer.ShuffleButton, + ''), text_font=ToontownGlobals.getInterfaceFont(), text_scale=TTLocalizer.SBshuffleBtn, text_pos=(0, -0.02), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), command=self.chooseRandom) + self.incBtn = DirectButton(parent=self.parentFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowUp, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.202, 0, 0), command=self.__goFrontHistory) + self.incBtn.hide() + self.decBtn = DirectButton(parent=self.parentFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowUp, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.202, 0, 0), command=self.__goBackHistory) + self.decBtn.hide() + self.lerpDuration = 0.5 + self.showLerp = None + self.frameShowLerp = LerpColorInterval(self.shuffleFrame, self.lerpDuration, Vec4(1, 1, 1, 1), Vec4(1, 1, 1, 0)) + self.incBtnShowLerp = LerpColorInterval(self.incBtn, self.lerpDuration, Vec4(1, 1, 1, 1), Vec4(1, 1, 1, 0)) + self.decBtnShowLerp = LerpColorInterval(self.decBtn, self.lerpDuration, Vec4(1, 1, 1, 1), Vec4(1, 1, 1, 0)) + self.__updateArrows() + + def unload(self): + if self.showLerp: + self.showLerp.finish() + del self.showLerp + self.parent = None + self.parentFrame.destroy() + self.shuffleFrame.destroy() + self.shuffleBtn.destroy() + self.incBtn.destroy() + self.decBtn.destroy() + del self.parentFrame + del self.shuffleFrame + del self.shuffleBtn + del self.incBtn + del self.decBtn + + def showButtons(self): + self.shuffleFrame.show() + self.shuffleBtn.show() + self.incBtn.show() + self.decBtn.show() + + def hideButtons(self): + self.shuffleFrame.hide() + self.shuffleBtn.hide() + self.incBtn.hide() + self.decBtn.hide() + + def setChoicePool(self, pool): + self.pool = pool + + def chooseRandom(self): + self.saveCurrChoice() + self.currChoice = [] + for prop in self.pool: + self.currChoice.append(random.choice(prop)) + + self.notify.debug('current choice : %s' % self.currChoice) + if len(self.history) == self.maxHistory: + self.history.remove(self.history[0]) + self.history.append(0) + self.historyPtr = len(self.history) - 1 + if len(self.history) == 2: + self.startShowLerp() + self.__updateArrows() + messenger.send(self.fetchEvent) + + def getCurrChoice(self): + return self.currChoice + + def saveCurrChoice(self): + self.currChoice = self.parent.getCurrToonSetting() + self.history[self.historyPtr] = self.currChoice + + def __goBackHistory(self): + self.saveCurrChoice() + self.historyPtr -= 1 + self.currChoice = self.history[self.historyPtr] + self.__updateArrows() + messenger.send(self.fetchEvent) + + def __goFrontHistory(self): + self.saveCurrChoice() + self.historyPtr += 1 + self.currChoice = self.history[self.historyPtr] + self.__updateArrows() + messenger.send(self.fetchEvent) + + def __updateArrows(self): + if self.historyPtr == 0: + self.decBtn['state'] = DGG.DISABLED + else: + self.decBtn['state'] = DGG.NORMAL + if self.historyPtr >= len(self.history) - 1: + self.incBtn['state'] = DGG.DISABLED + else: + self.incBtn['state'] = DGG.NORMAL + + def startShowLerp(self): + self.showLerp = Sequence(Parallel(Func(self.shuffleFrame.show), Func(self.incBtn.show), Func(self.decBtn.show)), Parallel(self.frameShowLerp, self.incBtnShowLerp, self.decBtnShowLerp)) + self.showLerp.start() + + def cleanHistory(self): + self.history = [0] + self.historyPtr = 0 + self.shuffleFrame.hide() + self.incBtn.hide() + self.decBtn.hide() diff --git a/toontown/makeatoon/TTPickANamePattern.py b/toontown/makeatoon/TTPickANamePattern.py new file mode 100755 index 00000000..c835f8f1 --- /dev/null +++ b/toontown/makeatoon/TTPickANamePattern.py @@ -0,0 +1,22 @@ +from direct.showbase.PythonUtil import listToItem2index +from otp.namepanel.PickANamePattern import PickANamePatternTwoPartLastName +from toontown.makeatoon.NameGenerator import NameGenerator +import types + +class TTPickANamePattern(PickANamePatternTwoPartLastName): + NameParts = None + LastNamePrefixesCapped = None + + def _getNameParts(self, gender): + if TTPickANamePattern.NameParts is None: + TTPickANamePattern.NameParts = {} + ng = NameGenerator() + TTPickANamePattern.NameParts['m'] = ng.getMaleNameParts() + TTPickANamePattern.NameParts['f'] = ng.getFemaleNameParts() + return TTPickANamePattern.NameParts[gender] + + def _getLastNameCapPrefixes(self): + if TTPickANamePattern.LastNamePrefixesCapped is None: + ng = NameGenerator() + TTPickANamePattern.LastNamePrefixesCapped = ng.getLastNamePrefixesCapped()[:] + return TTPickANamePattern.LastNamePrefixesCapped diff --git a/toontown/makeatoon/__init__.py b/toontown/makeatoon/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/minigame/ArrowKeys.py b/toontown/minigame/ArrowKeys.py new file mode 100755 index 00000000..48743016 --- /dev/null +++ b/toontown/minigame/ArrowKeys.py @@ -0,0 +1,169 @@ +from pandac.PandaModules import ModifierButtons +from direct.showbase.DirectObject import DirectObject + +class ArrowKeys(DirectObject): + UP_KEY = 'arrow_up' + DOWN_KEY = 'arrow_down' + LEFT_KEY = 'arrow_left' + RIGHT_KEY = 'arrow_right' + JUMP_KEY = 'control' + UP_INDEX = 0 + DOWN_INDEX = 1 + LEFT_INDEX = 2 + RIGHT_INDEX = 3 + JUMP_INDEX = 4 + NULL_HANDLERS = (None, None, None, None, None) + + def __init__(self): + self.__jumpPost = 0 + self.setPressHandlers(self.NULL_HANDLERS) + self.setReleaseHandlers(self.NULL_HANDLERS) + self.origMb = base.buttonThrowers[0].node().getModifierButtons() + base.buttonThrowers[0].node().setModifierButtons(ModifierButtons()) + self.enable() + + def enable(self): + self.disable() + self.accept(self.UP_KEY, self.__upKeyPressed) + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.accept(self.JUMP_KEY, self.__jumpKeyPressed) + + def disable(self): + self.__upPressed = 0 + self.__downPressed = 0 + self.__leftPressed = 0 + self.__rightPressed = 0 + self.__jumpPressed = 0 + self.ignore(self.UP_KEY) + self.ignore(self.DOWN_KEY) + self.ignore(self.LEFT_KEY) + self.ignore(self.RIGHT_KEY) + self.ignore(self.JUMP_KEY) + self.ignore(self.UP_KEY + '-up') + self.ignore(self.DOWN_KEY + '-up') + self.ignore(self.LEFT_KEY + '-up') + self.ignore(self.RIGHT_KEY + '-up') + self.ignore(self.JUMP_KEY + '-up') + + def destroy(self): + base.buttonThrowers[0].node().setModifierButtons(self.origMb) + events = [self.UP_KEY, + self.DOWN_KEY, + self.LEFT_KEY, + self.RIGHT_KEY, + self.JUMP_KEY] + for event in events: + self.ignore(event) + self.ignore(event + '-up') + + def upPressed(self): + return self.__upPressed + + def downPressed(self): + return self.__downPressed + + def leftPressed(self): + return self.__leftPressed + + def rightPressed(self): + return self.__rightPressed + + def jumpPressed(self): + return self.__jumpPressed + + def jumpPost(self): + jumpCache = self.__jumpPost + self.__jumpPost = 0 + return jumpCache + + def setPressHandlers(self, handlers): + if len(handlers) == 4: + handlers.append(None) + self.__checkCallbacks(handlers) + self.__pressHandlers = handlers + return + + def setReleaseHandlers(self, handlers): + if len(handlers) == 4: + handlers.append(None) + self.__checkCallbacks(handlers) + self.__releaseHandlers = handlers + return + + def clearPressHandlers(self): + self.setPressHandlers(self.NULL_HANDLERS) + + def clearReleaseHandlers(self): + self.setReleaseHandlers(self.NULL_HANDLERS) + + def __checkCallbacks(self, callbacks): + for callback in callbacks: + pass + + def __doCallback(self, callback): + if callback: + callback() + + def __upKeyPressed(self): + self.ignore(self.UP_KEY) + self.accept(self.UP_KEY + '-up', self.__upKeyReleased) + self.__upPressed = 1 + self.__doCallback(self.__pressHandlers[self.UP_INDEX]) + + def __downKeyPressed(self): + self.ignore(self.DOWN_KEY) + self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased) + self.__downPressed = 1 + self.__doCallback(self.__pressHandlers[self.DOWN_INDEX]) + + def __leftKeyPressed(self): + self.ignore(self.LEFT_KEY) + self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased) + self.__leftPressed = 1 + self.__doCallback(self.__pressHandlers[self.LEFT_INDEX]) + + def __rightKeyPressed(self): + self.ignore(self.RIGHT_KEY) + self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased) + self.__rightPressed = 1 + self.__doCallback(self.__pressHandlers[self.RIGHT_INDEX]) + + def __jumpKeyPressed(self): + self.ignore(self.JUMP_KEY) + self.accept(self.JUMP_KEY + '-up', self.__jumpKeyReleased) + self.__jumpPressed = 1 + self.__jumpPost = 1 + self.__doCallback(self.__pressHandlers[self.JUMP_INDEX]) + + def __upKeyReleased(self): + self.ignore(self.UP_KEY + '-up') + self.accept(self.UP_KEY, self.__upKeyPressed) + self.__upPressed = 0 + self.__doCallback(self.__releaseHandlers[self.UP_INDEX]) + + def __downKeyReleased(self): + self.ignore(self.DOWN_KEY + '-up') + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.__downPressed = 0 + self.__doCallback(self.__releaseHandlers[self.DOWN_INDEX]) + + def __leftKeyReleased(self): + self.ignore(self.LEFT_KEY + '-up') + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.__leftPressed = 0 + self.__doCallback(self.__releaseHandlers[self.LEFT_INDEX]) + + def __rightKeyReleased(self): + self.ignore(self.RIGHT_KEY + '-up') + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__rightPressed = 0 + self.__doCallback(self.__releaseHandlers[self.RIGHT_INDEX]) + + def __jumpKeyReleased(self): + self.ignore(self.JUMP_KEY + '-up') + self.accept(self.JUMP_KEY, self.__jumpKeyPressed) + self.__jumpPressed = 0 + self.__jumpPost = 0 + self.__doCallback(self.__releaseHandlers[self.JUMP_INDEX]) diff --git a/toontown/minigame/CannonGameGlobals.py b/toontown/minigame/CannonGameGlobals.py new file mode 100755 index 00000000..84c0742b --- /dev/null +++ b/toontown/minigame/CannonGameGlobals.py @@ -0,0 +1,16 @@ +TowerYRange = 200 +GameTime = 90 +MAX_SCORE = 23 +MIN_SCORE = 5 +FUSE_TIME = 0.0 +CANNON_ROTATION_MIN = -20 +CANNON_ROTATION_MAX = 20 +CANNON_ROTATION_VEL = 15.0 +CANNON_ANGLE_MIN = 10 +CANNON_ANGLE_MAX = 85 +CANNON_ANGLE_VEL = 15.0 + +def calcScore(t): + range = MAX_SCORE - MIN_SCORE + score = MAX_SCORE - range * (float(t) / GameTime) + return int(score + 0.5) diff --git a/toontown/minigame/CatchGameGlobals.py b/toontown/minigame/CatchGameGlobals.py new file mode 100755 index 00000000..b968162e --- /dev/null +++ b/toontown/minigame/CatchGameGlobals.py @@ -0,0 +1,57 @@ +EndlessGame = config.GetBool('endless-catch-game', 0) +GameDuration = 55.0 + +class DropObject: + + def __init__(self, name, good, onscreenDurMult, modelPath): + self.name = name + self.good = good + self.onscreenDurMult = onscreenDurMult + self.modelPath = modelPath + + def isBaseline(self): + return self.onscreenDurMult == 1.0 + + +DropObjectTypes = [DropObject('apple', 1, 1.0, 'phase_4/models/minigames/apple'), + DropObject('orange', 1, 1.0, 'phase_4/models/minigames/orange'), + DropObject('pear', 1, 1.0, 'phase_4/models/minigames/pear'), + DropObject('coconut', 1, 1.0, 'phase_4/models/minigames/coconut'), + DropObject('watermelon', 1, 1.0, 'phase_4/models/minigames/watermelon'), + DropObject('pineapple', 1, 1.0, 'phase_4/models/minigames/pineapple'), + DropObject('anvil', 0, 0.4, 'phase_4/models/props/anvil-mod')] +Name2DropObjectType = {} +for type in DropObjectTypes: + Name2DropObjectType[type.name] = type + +Name2DOTypeId = {} +names = Name2DropObjectType.keys() +names.sort() +for i in xrange(len(names)): + Name2DOTypeId[names[i]] = i + +DOTypeId2Name = names +NumFruits = [{2000: 18, + 1000: 19, + 5000: 22, + 4000: 24, + 3000: 27, + 9000: 28}, + {2000: 30, + 1000: 33, + 5000: 38, + 4000: 42, + 3000: 46, + 9000: 50}, + {2000: 42, + 1000: 48, + 5000: 54, + 4000: 60, + 3000: 66, + 9000: 71}, + {2000: 56, + 1000: 63, + 5000: 70, + 4000: 78, + 3000: 85, + 9000: 92}] diff --git a/toontown/minigame/CatchGameToonSD.py b/toontown/minigame/CatchGameToonSD.py new file mode 100755 index 00000000..6c430601 --- /dev/null +++ b/toontown/minigame/CatchGameToonSD.py @@ -0,0 +1,189 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.distributed.DelayDelete import DelayDelete +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import CatchGameGlobals +from direct.task.Task import Task + +class CatchGameToonSD(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('CatchGameToonSD') + FallBackAnim = 'slip-backward' + FallFwdAnim = 'slip-forward' + CatchNeutralAnim = 'catch-neutral' + CatchRunAnim = 'catch-run' + EatNeutralAnim = 'catch-eatneutral' + EatNRunAnim = 'catch-eatnrun' + animList = [FallBackAnim, + FallFwdAnim, + CatchNeutralAnim, + CatchRunAnim, + EatNeutralAnim, + EatNRunAnim] + + def __init__(self, avId, game): + self.avId = avId + self.game = game + self.isLocal = avId == base.localAvatar.doId + self.toon = self.game.getAvatar(self.avId) + self._delayDelete = DelayDelete(self.toon, 'CatchGameToonSD') + self.unexpectedExit = False + self.fsm = ClassicFSM.ClassicFSM('CatchGameAnimFSM-%s' % self.avId, [State.State('init', self.enterInit, self.exitInit, ['normal']), + State.State('normal', self.enterNormal, self.exitNormal, ['eatFruit', 'fallBack', 'fallForward']), + State.State('eatFruit', self.enterEatFruit, self.exitEatFruit, ['normal', + 'fallBack', + 'fallForward', + 'eatFruit']), + State.State('fallBack', self.enterFallBack, self.exitFallBack, ['normal']), + State.State('fallForward', self.enterFallForward, self.exitFallForward, ['normal']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'init', 'cleanup') + + def load(self): + self.setAnimState('off', 1.0) + for anim in self.animList: + self.toon.pose(anim, 0) + + def unload(self): + self._delayDelete.destroy() + del self.fsm + + def enter(self): + self.fsm.enterInitialState() + self._exiting = False + + def exit(self, unexpectedExit = False): + if self._exiting: + return + self._exiting = True + self.unexpectedExit = unexpectedExit + self.fsm.requestFinalState() + del self._exiting + + def enterInit(self): + self.notify.debug('enterInit') + self.toon.startBlink() + self.toon.stopLookAround() + if self.isLocal: + self.game.initOrthoWalk() + self.toon.useLOD(1000) + self.dropShadow = self.toon.dropShadow + self.origDropShadowColor = self.dropShadow.getColor() + c = self.origDropShadowColor + alpha = 0.35 + self.dropShadow.setColor(c[0], c[1], c[2], alpha) + + def exitInit(self): + pass + + def enterNormal(self): + self.notify.debug('enterNormal') + self.setAnimState('Catching', 1.0) + if self.isLocal: + self.game.orthoWalk.start() + self.toon.lerpLookAt(Vec3.forward() + Vec3.up(), time=0.2, blink=0) + + def exitNormal(self): + self.setAnimState('off', 1.0) + if self.isLocal: + self.game.orthoWalk.stop() + self.toon.lerpLookAt(Vec3.forward(), time=0.2, blink=0) + + def eatFruit(self, fruitModel, handNode): + if self.fsm.getCurrentState().getName() == 'eatFruit': + self.fsm.request('normal') + self.fsm.request('eatFruit', [fruitModel, handNode]) + + def enterEatFruit(self, fruitModel, handNode): + self.notify.debug('enterEatFruit') + self.setAnimState('CatchEating', 1.0) + if self.isLocal: + self.game.orthoWalk.start() + self.fruitModel = fruitModel + renderScale = fruitModel.getScale(render) + fruitModel.reparentTo(handNode) + fruitModel.setScale(render, renderScale) + + def finishedEating(self = self, fruitModel = fruitModel): + self.fsm.request('normal') + return Task.done + + duration = self.toon.getDuration('catch-eatneutral') + self.eatIval = Sequence(Parallel(WaitInterval(duration), Sequence(LerpScaleInterval(fruitModel, duration / 2.0, fruitModel.getScale() * 0.5, blendType='easeInOut'), Func(fruitModel.hide))), Func(finishedEating), name=self.toon.uniqueName('eatingIval')) + self.eatIval.start() + + def exitEatFruit(self): + self.eatIval.pause() + del self.eatIval + self.fruitModel.reparentTo(hidden) + self.fruitModel.removeNode() + del self.fruitModel + self.setAnimState('off', 1.0) + if self.isLocal: + self.game.orthoWalk.stop() + + def enterFallBack(self): + self.notify.debug('enterFallBack') + if self.isLocal: + base.playSfx(self.game.sndOof) + duration = 1.0 + animName = self.FallBackAnim + startFrame = 12 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.toon.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + + def resume(self = self): + self.fsm.request('normal') + + self.fallBackIval = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate), FunctionInterval(resume)) + self.fallBackIval.start() + + def exitFallBack(self): + self.fallBackIval.pause() + del self.fallBackIval + + def enterFallForward(self): + self.notify.debug('enterFallForward') + if self.isLocal: + base.playSfx(self.game.sndOof) + duration = 1.0 + animName = self.FallFwdAnim + startFrame = 12 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.toon.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + + def resume(self = self): + self.fsm.request('normal') + + self.fallFwdIval = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate), FunctionInterval(resume)) + self.fallFwdIval.start() + + def exitFallForward(self): + self.fallFwdIval.pause() + del self.fallFwdIval + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.toon.stopBlink() + self.toon.startLookAround() + if self.isLocal: + self.game.orthoWalk.stop() + self.game.destroyOrthoWalk() + self.toon.resetLOD() + self.dropShadow.setColor(self.origDropShadowColor) + + def exitCleanup(self): + pass + + def setAnimState(self, newState, playRate): + if not self.unexpectedExit: + self.toon.setAnimState(newState, playRate) diff --git a/toontown/minigame/ClerkPurchase.py b/toontown/minigame/ClerkPurchase.py new file mode 100755 index 00000000..6e9e06a5 --- /dev/null +++ b/toontown/minigame/ClerkPurchase.py @@ -0,0 +1,52 @@ +from PurchaseBase import * +from toontown.toonbase import ToontownTimer +COUNT_UP_RATE = 0.15 +DELAY_BEFORE_COUNT_UP = 1.25 +DELAY_AFTER_COUNT_UP = 1.75 +COUNT_DOWN_RATE = 0.075 +DELAY_AFTER_COUNT_DOWN = 0.0 +DELAY_AFTER_CELEBRATE = 3.0 + +class ClerkPurchase(PurchaseBase): + activateMode = 'storePurchase' + + def __init__(self, toon, remain, doneEvent): + PurchaseBase.__init__(self, toon, doneEvent) + self.remain = remain + + def load(self): + purchaseModels = loader.loadModel('phase_4/models/gui/gag_shop_purchase_gui') + PurchaseBase.load(self, purchaseModels) + self.backToPlayground = DirectButton(parent=self.frame, relief=None, scale=1.04, pos=(0.71, 0, -0.045), image=(purchaseModels.find('**/PurchScrn_BTN_UP'), purchaseModels.find('**/PurchScrn_BTN_DN'), purchaseModels.find('**/PurchScrn_BTN_RLVR')), text=TTLocalizer.GagShopDoneShopping, text_fg=(0, 0.1, 0.7, 1), text_scale=0.05, text_pos=(0, 0.015, 0), command=self.__handleBackToPlayground) + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(self.frame) + self.timer.posInTopRightCorner() + purchaseModels.removeNode() + + def unload(self): + PurchaseBase.unload(self) + del self.backToPlayground + self.timer.removeNode() + del self.timer + + def __handleBackToPlayground(self): + self.toon.inventory.reparentTo(hidden) + self.toon.inventory.hide() + self.handleDone(0) + + def __timerExpired(self): + self.handleDone(2) + + def enterPurchase(self): + PurchaseBase.enterPurchase(self) + self.backToPlayground.reparentTo(self.toon.inventory.storePurchaseFrame) + self.pointDisplay.reparentTo(self.toon.inventory.storePurchaseFrame) + self.statusLabel.reparentTo(self.toon.inventory.storePurchaseFrame) + self.timer.countdown(self.remain, self.__timerExpired) + + def exitPurchase(self): + PurchaseBase.exitPurchase(self) + self.backToPlayground.reparentTo(self.frame) + self.pointDisplay.reparentTo(self.frame) + self.statusLabel.reparentTo(self.frame) + self.ignore('purchaseStateChange') diff --git a/toontown/minigame/CogThief.py b/toontown/minigame/CogThief.py new file mode 100755 index 00000000..315788be --- /dev/null +++ b/toontown/minigame/CogThief.py @@ -0,0 +1,474 @@ +import math +from pandac.PandaModules import CollisionSphere, CollisionNode, Point3, CollisionTube, Vec3, rad2Deg +from direct.showbase.DirectObject import DirectObject +from direct.distributed.ClockDelta import globalClockDelta +from direct.interval.IntervalGlobal import Parallel, SoundInterval, Sequence, Func, LerpScaleInterval +from toontown.suit import Suit +from toontown.suit import SuitDNA +from toontown.toonbase import ToontownGlobals +from toontown.minigame import CogThiefGameGlobals +from toontown.battle.BattleProps import globalPropPool +from toontown.battle.BattleSounds import globalBattleSoundCache +CTGG = CogThiefGameGlobals + +class CogThief(DirectObject): + notify = directNotify.newCategory('CogThief') + DefaultSpeedWalkAnim = 4.0 + CollisionRadius = 1.25 + MaxFriendsVisible = 4 + Infinity = 100000.0 + SeparationDistance = 6.0 + MinUrgency = 0.5 + MaxUrgency = 0.75 + + def __init__(self, cogIndex, suitType, game, cogSpeed): + self.cogIndex = cogIndex + self.suitType = suitType + self.game = game + self.cogSpeed = cogSpeed + suit = Suit.Suit() + d = SuitDNA.SuitDNA() + d.newSuit(suitType) + suit.setDNA(d) + suit.pose('walk', 0) + self.suit = suit + self.goal = CTGG.NoGoal + self.goalId = CTGG.InvalidGoalId + self.lastLocalTimeStampFromAI = 0 + self.lastPosFromAI = Point3(0, 0, 0) + self.lastThinkTime = 0 + self.doneAdjust = False + self.barrel = CTGG.NoBarrelCarried + self.signalledAtReturnPos = False + self.defaultPlayRate = 1.0 + self.netTimeSentToStartByHit = 0 + self.velocity = Vec3(0, 0, 0) + self.oldVelocity = Vec3(0, 0, 0) + self.acceleration = Vec3(0, 0, 0) + self.bodyLength = self.CollisionRadius * 2 + self.cruiseDistance = 2 * self.bodyLength + self.maxVelocity = self.cogSpeed + self.maxAcceleration = 5.0 + self.perceptionRange = 6 + self.notify.debug('cogSpeed=%s' % self.cogSpeed) + self.kaboomSound = loader.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.ogg') + self.kaboom = loader.loadModel('phase_4/models/minigames/ice_game_kaboom') + self.kaboom.setScale(2.0) + self.kaboom.setBillboardPointEye() + self.kaboom.hide() + self.kaboomTrack = None + splatName = 'splat-creampie' + self.splat = globalPropPool.getProp(splatName) + self.splat.setBillboardPointEye() + self.splatType = globalPropPool.getPropType(splatName) + self.pieHitSound = globalBattleSoundCache.getSound('AA_wholepie_only.ogg') + return + + def destroy(self): + self.ignoreAll() + self.suit.delete() + self.game = None + return + + def uniqueName(self, baseStr): + return baseStr + '-' + str(self.game.doId) + + def handleEnterSphere(self, collEntry): + intoNp = collEntry.getIntoNodePath() + self.notify.debug('handleEnterSphere suit %d hit %s' % (self.cogIndex, intoNp)) + if self.game: + self.game.handleEnterSphere(collEntry) + + def gameStart(self, gameStartTime): + self.gameStartTime = gameStartTime + self.initCollisions() + self.startWalkAnim() + + def gameEnd(self): + self.moveIval.pause() + del self.moveIval + self.shutdownCollisions() + self.suit.loop('neutral') + + def initCollisions(self): + self.collSphere = CollisionSphere(0, 0, 0, 1.25) + self.collSphere.setTangible(1) + name = 'CogThiefSphere-%d' % self.cogIndex + self.collSphereName = self.uniqueName(name) + self.collNode = CollisionNode(self.collSphereName) + self.collNode.setIntoCollideMask(CTGG.BarrelBitmask | ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.suit.attachNewNode(self.collNode) + self.accept('enter' + self.collSphereName, self.handleEnterSphere) + self.pieCollSphere = CollisionTube(0, 0, 0, 0, 0, 4, self.CollisionRadius) + self.pieCollSphere.setTangible(1) + name = 'CogThiefPieSphere-%d' % self.cogIndex + self.pieCollSphereName = self.uniqueName(name) + self.pieCollNode = CollisionNode(self.pieCollSphereName) + self.pieCollNode.setIntoCollideMask(ToontownGlobals.PieBitmask) + self.pieCollNode.addSolid(self.pieCollSphere) + self.pieCollNodePath = self.suit.attachNewNode(self.pieCollNode) + + def shutdownCollisions(self): + self.ignore(self.uniqueName('enter' + self.collSphereName)) + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + + def updateGoal(self, timestamp, inResponseClientStamp, goalType, goalId, pos): + self.notify.debug('self.netTimeSentToStartByHit =%s' % self.netTimeSentToStartByHit) + if not self.game: + self.notify.debug('updateGoal self.game is None, just returning') + return + if not self.suit: + self.notify.debug('updateGoal self.suit is None, just returning') + return + if self.goal == CTGG.NoGoal: + self.startWalkAnim() + if goalType == CTGG.NoGoal: + self.notify.debug('updateGoal setting position to %s' % pos) + self.suit.setPos(pos) + self.lastThinkTime = 0 + self.velocity = Vec3(0, 0, 0) + self.oldVelocity = Vec3(0, 0, 0) + self.acceleration = Vec3(0, 0, 0) + if goalType == CTGG.RunAwayGoal: + pass + if inResponseClientStamp < self.netTimeSentToStartByHit and self.goal == CTGG.NoGoal and goalType == CTGG.RunAwayGoal: + self.notify.warning('ignoring newGoal %s as cog %d was recently hit responsetime=%s hitTime=%s' % (CTGG.GoalStr[goalType], + self.cogIndex, + inResponseClientStamp, + self.netTimeSentToStartByHit)) + else: + self.lastLocalTimeStampFromAI = globalClockDelta.networkToLocalTime(timestamp, bits=32) + self.goal = goalType + self.goalId = goalId + self.lastPosFromAI = pos + self.doneAdjust = False + self.signalledAtReturnPos = False + + def startWalkAnim(self): + if self.suit: + self.suit.loop('walk') + speed = self.cogSpeed + self.defaultPlayRate = float(self.cogSpeed / self.DefaultSpeedWalkAnim) + self.suit.setPlayRate(self.defaultPlayRate, 'walk') + + def think(self): + if self.goal == CTGG.ToonGoal: + self.thinkAboutCatchingToon() + elif self.goal == CTGG.BarrelGoal: + self.thinkAboutGettingBarrel() + elif self.goal == CTGG.RunAwayGoal: + self.thinkAboutRunAway() + + def thinkAboutCatchingToon(self): + if not self.game: + return + av = self.game.getAvatar(self.goalId) + if av: + if not self.lastThinkTime: + self.lastThinkTime = globalClock.getFrameTime() + diffTime = globalClock.getFrameTime() - self.lastThinkTime + avPos = av.getPos() + myPos = self.suit.getPos() + if not self.doneAdjust: + myPos = self.lastPosFromAI + self.notify.debug('thinkAboutCatchingToon not doneAdjust setting pos %s' % myPos) + self.doneAdjust = True + self.suit.setPos(myPos) + if self.game.isToonPlayingHitTrack(self.goalId): + self.suit.headsUp(av) + self.velocity = Vec3(0, 0, 0) + self.oldVelocity = Vec3(0, 0, 0) + self.acceleration = Vec3(0, 0, 0) + else: + self.commonMove() + newPos = self.suit.getPos() + self.adjustPlayRate(newPos, myPos, diffTime) + self.lastThinkTime = globalClock.getFrameTime() + + def convertNetworkStampToGameTime(self, timestamp): + localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + gameTime = self.game.local2GameTime(localStamp) + return gameTime + + def respondToToonHit(self, timestamp): + localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + if self.netTimeSentToStartByHit < timestamp: + self.clearGoal() + self.showKaboom() + startPos = CTGG.CogStartingPositions[self.cogIndex] + oldPos = self.suit.getPos() + self.suit.setPos(startPos) + if self.netTimeSentToStartByHit < timestamp: + self.netTimeSentToStartByHit = timestamp + else: + self.notify.debug('localStamp = %s, lastLocalTimeStampFromAI=%s, ignoring respondToToonHit' % (localStamp, self.lastLocalTimeStampFromAI)) + self.notify.debug('respondToToonHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit) + + def clearGoal(self): + self.goal = CTGG.NoGoal + self.goalId = CTGG.InvalidGoalId + + def thinkAboutGettingBarrel(self): + if not self.game: + return + if not hasattr(self.game, 'barrels'): + return + if self.goalId not in xrange(len(self.game.barrels)): + return + if not self.lastThinkTime: + self.lastThinkTime = globalClock.getFrameTime() + diffTime = globalClock.getFrameTime() - self.lastThinkTime + barrel = self.game.barrels[self.goalId] + barrelPos = barrel.getPos() + myPos = self.suit.getPos() + if not self.doneAdjust: + myPos = self.lastPosFromAI + self.notify.debug('thinkAboutGettingBarrel not doneAdjust setting position to %s' % myPos) + self.suit.setPos(myPos) + self.doneAdjust = True + displacement = barrelPos - myPos + distanceToToon = displacement.length() + self.suit.headsUp(barrel) + lengthTravelled = diffTime * self.cogSpeed + if lengthTravelled > distanceToToon: + lengthTravelled = distanceToToon + displacement.normalize() + dirVector = displacement + dirVector *= lengthTravelled + newPos = myPos + dirVector + newPos.setZ(0) + self.suit.setPos(newPos) + self.adjustPlayRate(newPos, myPos, diffTime) + self.lastThinkTime = globalClock.getFrameTime() + + def stopWalking(self, timestamp): + localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + if localStamp > self.lastLocalTimeStampFromAI: + self.suit.loop('neutral') + self.clearGoal() + + def thinkAboutRunAway(self): + if not self.game: + return + if not self.lastThinkTime: + self.lastThinkTime = globalClock.getFrameTime() + diffTime = globalClock.getFrameTime() - self.lastThinkTime + returnPos = CTGG.CogReturnPositions[self.goalId] + myPos = self.suit.getPos() + if not self.doneAdjust: + myPos = self.lastPosFromAI + self.suit.setPos(myPos) + self.doneAdjust = True + displacement = returnPos - myPos + distanceToToon = displacement.length() + tempNp = render.attachNewNode('tempRet') + tempNp.setPos(returnPos) + self.suit.headsUp(tempNp) + tempNp.removeNode() + lengthTravelled = diffTime * self.cogSpeed + if lengthTravelled > distanceToToon: + lengthTravelled = distanceToToon + displacement.normalize() + dirVector = displacement + dirVector *= lengthTravelled + newPos = myPos + dirVector + newPos.setZ(0) + self.suit.setPos(newPos) + self.adjustPlayRate(newPos, myPos, diffTime) + if (self.suit.getPos() - returnPos).length() < 0.0001: + if not self.signalledAtReturnPos and self.barrel >= 0: + self.game.sendCogAtReturnPos(self.cogIndex, self.barrel) + self.signalledAtReturnPos = True + self.lastThinkTime = globalClock.getFrameTime() + + def makeCogCarryBarrel(self, timestamp, inResponseClientStamp, barrelModel, barrelIndex, cogPos): + if not self.game: + return + localTimeStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + self.lastLocalTimeStampFromAI = localTimeStamp + inResponseGameTime = self.convertNetworkStampToGameTime(inResponseClientStamp) + self.notify.debug('inResponseGameTime =%s timeSentToStart=%s' % (inResponseGameTime, self.netTimeSentToStartByHit)) + if inResponseClientStamp < self.netTimeSentToStartByHit and self.goal == CTGG.NoGoal: + self.notify.warning('ignoring makeCogCarrybarrel') + else: + barrelModel.setPos(0, -1.0, 1.5) + barrelModel.reparentTo(self.suit) + self.suit.setPos(cogPos) + self.barrel = barrelIndex + + def makeCogDropBarrel(self, timestamp, inResponseClientStamp, barrelModel, barrelIndex, barrelPos): + localTimeStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + self.lastLocalTimeStampFromAI = localTimeStamp + barrelModel.reparentTo(render) + barrelModel.setPos(barrelPos) + self.barrel = CTGG.NoBarrelCarried + + def respondToPieHit(self, timestamp): + localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + if self.netTimeSentToStartByHit < timestamp: + self.clearGoal() + self.showSplat() + startPos = CTGG.CogStartingPositions[self.cogIndex] + oldPos = self.suit.getPos() + self.suit.setPos(startPos) + if self.netTimeSentToStartByHit < timestamp: + self.netTimeSentToStartByHit = timestamp + else: + self.notify.debug('localStamp = %s, lastLocalTimeStampFromAI=%s, ignoring respondToPieHit' % (localStamp, self.lastLocalTimeStampFromAI)) + self.notify.debug('respondToPieHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit) + + def cleanup(self): + self.clearGoal() + self.ignoreAll() + self.suit.delete() + if self.kaboomTrack and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + self.suit = None + self.game = None + return + + def adjustPlayRate(self, newPos, oldPos, diffTime): + lengthTravelled = (newPos - oldPos).length() + if diffTime: + speed = lengthTravelled / diffTime + else: + speed = self.cogSpeed + rateMult = speed / self.cogSpeed + newRate = rateMult * self.defaultPlayRate + self.suit.setPlayRate(newRate, 'walk') + + def commonMove(self): + if not self.lastThinkTime: + self.lastThinkTime = globalClock.getFrameTime() + dt = globalClock.getFrameTime() - self.lastThinkTime + self.oldpos = self.suit.getPos() + pos = self.suit.getPos() + pos += self.velocity * dt + self.suit.setPos(pos) + self.seeFriends() + acc = Vec3(0, 0, 0) + self.accumulate(acc, self.getTargetVector()) + if self.numFlockmatesSeen > 0: + keepDistanceVector = self.keepDistance() + oldAcc = Vec3(acc) + self.accumulate(acc, keepDistanceVector) + if self.cogIndex == 0: + pass + if acc.length() > self.maxAcceleration: + acc.normalize() + acc *= self.maxAcceleration + self.oldVelocity = self.velocity + self.velocity += acc + if self.velocity.length() > self.maxVelocity: + self.velocity.normalize() + self.velocity *= self.maxVelocity + forwardVec = Vec3(1, 0, 0) + heading = rad2Deg(math.atan2(self.velocity[1], self.velocity[0])) + heading -= 90 + self.suit.setH(heading) + + def getTargetVector(self): + targetPos = Point3(0, 0, 0) + if self.goal == CTGG.ToonGoal: + av = self.game.getAvatar(self.goalId) + if av: + targetPos = av.getPos() + elif self.goal == CTGG.BarrelGoal: + barrel = self.game.barrels[self.goalId] + targetPos = barrel.getPos() + elif self.goal == CTGG.RunAwayGoal: + targetPos = CTGG.CogReturnPositions[self.goalId] + targetPos.setZ(0) + myPos = self.suit.getPos() + diff = targetPos - myPos + if diff.length() > 1.0: + diff.normalize() + diff *= 1.0 + return diff + + def accumulate(self, accumulator, valueToAdd): + accumulator += valueToAdd + return accumulator.length() + + def seeFriends(self): + self.clearVisibleList() + for cogIndex in self.game.cogInfo.keys(): + if cogIndex == self.cogIndex: + continue + if self.sameGoal(cogIndex): + dist = self.canISee(cogIndex) + if dist != self.Infinity: + self.addToVisibleList(cogIndex) + if dist < self.distToNearestFlockmate: + self.nearestFlockmate = cogIndex + self.distToNearestFlockmate = dist + + return self.numFlockmatesSeen + + def clearVisibleList(self): + self.visibleFriendsList = [] + self.numFlockmatesSeen = 0 + self.nearestFlockmate = None + self.distToNearestFlockmate = self.Infinity + return + + def addToVisibleList(self, cogIndex): + if self.numFlockmatesSeen < self.MaxFriendsVisible: + self.visibleFriendsList.append(cogIndex) + self.numFlockmatesSeen += 1 + if self.cogIndex == 0: + pass + + def canISee(self, cogIndex): + if self.cogIndex == cogIndex: + return self.Infinity + cogThief = self.game.getCogThief(cogIndex) + distance = self.suit.getDistance(cogThief.suit) + if distance < self.perceptionRange: + return distance + return self.Infinity + + def sameGoal(self, cogIndex): + cogThief = self.game.getCogThief(cogIndex) + result = cogThief.goalId == self.goalId and cogThief.goal == self.goal + return result + + def keepDistance(self): + ratio = self.distToNearestFlockmate / self.SeparationDistance + nearestThief = self.game.getCogThief(self.nearestFlockmate) + change = nearestThief.suit.getPos() - self.suit.getPos() + if ratio < self.MinUrgency: + ratio = self.MinUrgency + if ratio > self.MaxUrgency: + ratio = self.MaxUrgency + if self.distToNearestFlockmate < self.SeparationDistance: + change.normalize() + change *= -(1 - ratio) + elif self.distToNearestFlockmate > self.SeparationDistance: + change.normalize() + change *= ratio + else: + change = Vec3(0, 0, 0) + return change + + def showKaboom(self): + if self.kaboomTrack and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + self.kaboom.reparentTo(render) + self.kaboom.setPos(self.suit.getPos()) + self.kaboom.setZ(3) + self.kaboomTrack = Parallel(SoundInterval(self.kaboomSound, volume=0.5), Sequence(Func(self.kaboom.showThrough), LerpScaleInterval(self.kaboom, duration=0.5, scale=Point3(10, 10, 10), startScale=Point3(1, 1, 1), blendType='easeOut'), Func(self.kaboom.hide))) + self.kaboomTrack.start() + + def showSplat(self): + if self.kaboomTrack and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + self.splat.reparentTo(render) + self.splat.setPos(self.suit.getPos()) + self.splat.setZ(3) + self.kaboomTrack = Parallel(SoundInterval(self.pieHitSound, volume=1.0), Sequence(Func(self.splat.showThrough), LerpScaleInterval(self.splat, duration=0.5, scale=1.75, startScale=Point3(0.1, 0.1, 0.1), blendType='easeOut'), Func(self.splat.hide))) + self.kaboomTrack.start() diff --git a/toontown/minigame/CogThiefGameGlobals.py b/toontown/minigame/CogThiefGameGlobals.py new file mode 100755 index 00000000..1d1ef755 --- /dev/null +++ b/toontown/minigame/CogThiefGameGlobals.py @@ -0,0 +1,143 @@ +from pandac.PandaModules import VBase3, BitMask32 + +GameTime = 60 +NumBarrels = 4 +BarrelStartingPositions = ( + VBase3(4.3, 4, 0), + VBase3(4.3, -4, 0), + VBase3(-4.3, 4, 0), + VBase3(-4.3, -4, 0) +) +ToonStartingPositions = ( + VBase3(0, 16, 0), + VBase3(0, -16, 0), + VBase3(-16, 0, 0), + VBase3(16, 0, 0) +) +CogStartingPositions = ( + VBase3(35, 18, 0), + VBase3(35, 0, 0), + VBase3(35, -18, 0), + VBase3(-35, 18, 0), + VBase3(-35, 0, 0), + VBase3(-35, -18, 0), + VBase3(0, 27, 0), + VBase3(0, -27, 0), + VBase3(35, 9, 0), + VBase3(-35, 9, 0), + VBase3(35, -9, 0), + VBase3(-35, -9, 0) +) +CogReturnPositions = ( + VBase3(-35, 28, 0), + VBase3(-14, 28, 0), + VBase3(14, 28, 0), + VBase3(35, 28, 0), + VBase3(35, 0, 0), + VBase3(35, -28, 0), + VBase3(-14, -28, 0), + VBase3(14, -28, 0), + VBase3(-35, -28, 0), + VBase3(-35, 0, 0) +) +StageHalfWidth = 25 +StageHalfHeight = 18 +NoGoal = 0 +BarrelGoal = 1 +ToonGoal = 2 +RunAwayGoal = 3 +InvalidGoalId = -1 +GoalStr = { + NoGoal: 'NoGoal', + BarrelGoal: 'BarrelGoal', + ToonGoal: 'ToonGoal', + RunAwayGoal: 'RunAwayGoal', + InvalidGoalId: 'InvalidGoa' +} +BarrelBitmask = BitMask32(512) +BarrelOnGround = -1 +NoBarrelCarried = -1 +LyingDownDuration = 2.0 +MAX_SCORE = 20 +MIN_SCORE = 3 + +def calcScore(t): + range = MAX_SCORE - MIN_SCORE + score = range * (float(t) / GameTime) + MIN_SCORE + return int(score + 0.5) + + +def getMaxScore(): + result = calcScore(GameTime) + return result + + +NumCogsTable = [ + {2000: 5, + 1000: 5, + 5000: 5, + 4000: 5, + 3000: 5, + 9000: 5}, + {2000: 7, + 1000: 7, + 5000: 7, + 4000: 7, + 3000: 7, + 9000: 7}, + {2000: 9, + 1000: 9, + 5000: 9, + 4000: 9, + 3000: 9, + 9000: 9}, + {2000: 11, + 1000: 11, + 5000: 11, + 4000: 11, + 3000: 11, + 9000: 11} +] +CogSpeedTable = [ + {2000: 6.0, + 1000: 6.4, + 5000: 6.8, + 4000: 7.2, + 3000: 7.6, + 9000: 8.0}, + {2000: 6.0, + 1000: 6.4, + 5000: 6.8, + 4000: 7.2, + 3000: 7.6, + 9000: 8.0}, + {2000: 6.0, + 1000: 6.4, + 5000: 6.8, + 4000: 7.2, + 3000: 7.6, + 9000: 8.0}, + {2000: 6.0, + 1000: 6.4, + 5000: 6.8, + 4000: 7.2, + 3000: 7.6, + 9000: 8.0} +] +ToonSpeed = 9.0 +PerfectBonus = [8, 6, 4, 2] + +def calculateCogs(numPlayers, safezone): + result = 5 + if numPlayers <= len(NumCogsTable): + if safezone in NumCogsTable[numPlayers - 1]: + result = NumCogsTable[numPlayers - 1][safezone] + return result + + +def calculateCogSpeed(numPlayers, safezone): + result = 6.0 + if numPlayers <= len(NumCogsTable): + if safezone in CogSpeedTable[numPlayers - 1]: + result = CogSpeedTable[numPlayers - 1][safezone] + return result diff --git a/toontown/minigame/CogThiefGameToonSD.py b/toontown/minigame/CogThiefGameToonSD.py new file mode 100755 index 00000000..2270fbc3 --- /dev/null +++ b/toontown/minigame/CogThiefGameToonSD.py @@ -0,0 +1,187 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task.Task import Task + +class CogThiefGameToonSD(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('CogThiefGameToonSD') + FallBackAnim = 'slip-backward' + FallFwdAnim = 'slip-forward' + NeutralAnim = 'neutral' + RunAnim = 'run' + ThrowNeutralAnim = 'throw' + ThrowRunAnim = 'throw' + animList = [FallBackAnim, + FallFwdAnim, + NeutralAnim, + RunAnim, + ThrowNeutralAnim, + ThrowRunAnim] + + def __init__(self, avId, game): + self.avId = avId + self.game = game + self.isLocal = avId == base.localAvatar.doId + self.toon = self.game.getAvatar(self.avId) + self.unexpectedExit = False + self.fsm = ClassicFSM.ClassicFSM('CogThiefGameAnimFSM-%s' % self.avId, [State.State('init', self.enterInit, self.exitInit, ['normal']), + State.State('normal', self.enterNormal, self.exitNormal, ['throwPie', 'fallBack', 'fallForward']), + State.State('throwPie', self.enterThrowPie, self.exitThrowPie, ['normal', + 'fallBack', + 'fallForward', + 'throwPie']), + State.State('fallBack', self.enterFallBack, self.exitFallBack, ['normal']), + State.State('fallForward', self.enterFallForward, self.exitFallForward, ['normal']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'init', 'cleanup') + self.exitAlreadyCalled = False + + def load(self): + self.setAnimState('off', 1.0) + for anim in self.animList: + self.toon.pose(anim, 0) + + def unload(self): + del self.fsm + + def enter(self): + self.fsm.enterInitialState() + + def exit(self, unexpectedExit = False): + if self.exitAlreadyCalled: + return + self.exitAlreadyCalled = True + self.notify.debug('in exit self.toon.doId=%s' % self.toon.doId) + self.unexpectedExit = unexpectedExit + self.fsm.requestFinalState() + + def enterInit(self): + self.notify.debug('enterInit') + self.toon.startBlink() + self.toon.stopLookAround() + if self.isLocal: + self.game.initGameWalk() + self.toon.useLOD(1000) + self.dropShadow = self.toon.dropShadow + self.origDropShadowColor = self.dropShadow.getColor() + c = self.origDropShadowColor + alpha = 0.35 + self.dropShadow.setColor(c[0], c[1], c[2], alpha) + + def exitInit(self): + pass + + def setAnimState(self, newState, playRate): + if not self.unexpectedExit: + self.toon.setAnimState(newState, playRate) + + def enterNormal(self): + self.notify.debug('enterNormal') + self.setAnimState('CogThiefRunning', 1.0) + if self.isLocal: + self.game.startGameWalk() + self.toon.lerpLookAt(Vec3.forward() + Vec3.up(), time=0.2, blink=0) + + def exitNormal(self): + self.notify.debug('exitNormal') + self.setAnimState('off', 1.0) + if self.isLocal: + self.game.stopGameWalk() + self.toon.lerpLookAt(Vec3.forward(), time=0.2, blink=0) + + def throwPie(self, pieModel, handNode): + if self.fsm.getCurrentState().getName() == 'throwPie': + self.fsm.request('normal') + self.fsm.request('throwPie', [pieModel, handNode]) + + def enterThrowPie(self, pieModel, handNode): + self.notify.debug('enterThrowPie') + self.setAnimState('CatchEating', 1.0) + if self.isLocal: + self.game.startGameWalk() + self.pieModel = pieModel + renderScale = pieModel.getScale(render) + pieModel.reparentTo(handNode) + pieModel.setScale(render, renderScale) + + def finishedEating(self = self, pieModel = pieModel): + self.fsm.request('normal') + return Task.done + + duration = self.toon.getDuration('catch-eatneutral') + self.eatIval = Sequence(Parallel(WaitInterval(duration), Sequence(LerpScaleInterval(pieModel, duration / 2.0, pieModel.getScale() * 0.5, blendType='easeInOut'), Func(pieModel.hide))), Func(finishedEating), name=self.toon.uniqueName('eatingIval')) + self.eatIval.start() + + def exitThrowPie(self): + self.eatIval.pause() + del self.eatIval + self.pieModel.reparentTo(hidden) + self.pieModel.removeNode() + del self.pieModel + self.setAnimState('off', 1.0) + if self.isLocal: + self.game.stopGameWalk() + + def enterFallBack(self): + self.notify.debug('enterFallBack') + if self.isLocal: + base.playSfx(self.game.sndOof) + duration = 1.0 + animName = self.FallBackAnim + startFrame = 12 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.toon.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + + def resume(self = self): + self.fsm.request('normal') + + self.fallBackIval = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate), FunctionInterval(resume)) + self.fallBackIval.start() + + def exitFallBack(self): + self.fallBackIval.pause() + del self.fallBackIval + + def enterFallForward(self): + self.notify.debug('enterFallForward') + if self.isLocal: + base.playSfx(self.game.sndOof) + duration = 1.0 + animName = self.FallFwdAnim + startFrame = 12 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.toon.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + + def resume(self = self): + self.fsm.request('normal') + + self.fallFwdIval = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate), FunctionInterval(resume)) + self.fallFwdIval.start() + + def exitFallForward(self): + self.fallFwdIval.pause() + del self.fallFwdIval + + def enterCleanup(self): + self.notify.debug('enterCleanup %s' % self.toon.doId) + if self.toon and not self.toon.isEmpty(): + self.toon.stopBlink() + self.toon.startLookAround() + if self.isLocal: + self.game.stopGameWalk() + self.game.destroyGameWalk() + self.toon.resetLOD() + self.dropShadow.setColor(self.origDropShadowColor) + + def exitCleanup(self): + pass diff --git a/toontown/minigame/CogThiefWalk.py b/toontown/minigame/CogThiefWalk.py new file mode 100755 index 00000000..a1de3669 --- /dev/null +++ b/toontown/minigame/CogThiefWalk.py @@ -0,0 +1,25 @@ +from toontown.safezone import Walk + +class CogThiefWalk(Walk.Walk): + notify = directNotify.newCategory('CogThiefWalk') + + def __init__(self, doneEvent): + Walk.Walk.__init__(self, doneEvent) + + def enter(self, slowWalk = 0): + base.localAvatar.startPosHprBroadcast() + base.localAvatar.startBlink() + base.localAvatar.showName() + base.localAvatar.collisionsOn() + base.localAvatar.startGlitchKiller() + base.localAvatar.enableAvatarControls() + + def exit(self): + self.fsm.request('off') + self.ignore('control') + base.localAvatar.disableAvatarControls() + base.localAvatar.stopPosHprBroadcast() + base.localAvatar.stopBlink() + base.localAvatar.stopGlitchKiller() + base.localAvatar.collisionsOff() + base.localAvatar.controlManager.placeOnFloor() diff --git a/toontown/minigame/DistributedCannonGame.py b/toontown/minigame/DistributedCannonGame.py new file mode 100755 index 00000000..873e7972 --- /dev/null +++ b/toontown/minigame/DistributedCannonGame.py @@ -0,0 +1,994 @@ +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * +from otp.nametag.NametagFloat3d import NametagFloat3d +from otp.nametag.Nametag import Nametag +from toontown.toonbase.ToonBaseGlobal import * +from DistributedMinigame import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownTimer +from direct.task.Task import Task +import Trajectory +import math +from toontown.toon import ToonHead +from toontown.effects import Splash +from toontown.effects import DustCloud +import CannonGameGlobals +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +LAND_TIME = 2 +WORLD_SCALE = 2.0 +GROUND_SCALE = 1.4 * WORLD_SCALE +CANNON_SCALE = 1.0 +FAR_PLANE_DIST = 600 * WORLD_SCALE +CANNON_Y = -int(CannonGameGlobals.TowerYRange / 2 * 1.3) +CANNON_X_SPACING = 12 +CANNON_Z = 20 +CANNON_MOVE_UPDATE_FREQ = 0.5 +CAMERA_PULLBACK_MIN = 20 +CAMERA_PULLBACK_MAX = 40 +MAX_LOOKAT_OFFSET = 80 +TOON_TOWER_THRESHOLD = 150 +SHADOW_Z_OFFSET = 0.5 +TOWER_HEIGHT = 43.85 +TOWER_RADIUS = 10.5 +BUCKET_HEIGHT = 36 +TOWER_Y_RANGE = CannonGameGlobals.TowerYRange +TOWER_X_RANGE = int(TOWER_Y_RANGE / 2.0) +INITIAL_VELOCITY = 94.0 +WHISTLE_SPEED = INITIAL_VELOCITY * 0.55 + +class DistributedCannonGame(DistributedMinigame): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMinigame') + font = ToontownGlobals.getToonFont() + LOCAL_CANNON_MOVE_TASK = 'localCannonMoveTask' + REWARD_COUNTDOWN_TASK = 'cannonGameRewardCountdown' + HIT_GROUND = 0 + HIT_TOWER = 1 + HIT_WATER = 2 + FIRE_KEY = 'control' + UP_KEY = 'arrow_up' + DOWN_KEY = 'arrow_down' + LEFT_KEY = 'arrow_left' + RIGHT_KEY = 'arrow_right' + INTRO_TASK_NAME = 'CannonGameIntro' + INTRO_TASK_NAME_CAMERA_LERP = 'CannonGameIntroCamera' + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedCannonGame', [State.State('off', self.enterOff, self.exitOff, ['aim']), + State.State('aim', self.enterAim, self.exitAim, ['shoot', 'waitForToonsToLand', 'cleanup']), + State.State('shoot', self.enterShoot, self.exitShoot, ['aim', 'waitForToonsToLand', 'cleanup']), + State.State('waitForToonsToLand', self.enterWaitForToonsToLand, self.exitWaitForToonsToLand, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.cannonLocationDict = {} + self.cannonPositionDict = {} + self.cannonDict = {} + self.toonModelDict = {} + self.dropShadowDict = {} + self.toonHeadDict = {} + self.toonScaleDict = {} + self.toonIntervalDict = {} + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.cannonMoving = 0 + self.modelCount = 14 + + def getTitle(self): + return TTLocalizer.CannonGameTitle + + def getInstructions(self): + return TTLocalizer.CannonGameInstructions + + def getMaxDuration(self): + return CannonGameGlobals.GameTime + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.sky = loader.loadModel('phase_3.5/models/props/TT_sky') + self.ground = loader.loadModel('phase_4/models/minigames/toon_cannon_gameground') + self.tower = loader.loadModel('phase_4/models/minigames/toon_cannon_water_tower') + self.cannon = loader.loadModel('phase_4/models/minigames/toon_cannon') + self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.hill = loader.loadModel('phase_4/models/minigames/cannon_hill') + self.sky.setScale(WORLD_SCALE) + self.ground.setScale(GROUND_SCALE) + self.cannon.setScale(CANNON_SCALE) + self.dropShadow.setColor(0, 0, 0, 0.5) + self.ground.setColor(0.85, 0.85, 0.85, 1.0) + self.hill.setScale(1, 1, CANNON_Z / 20.0) + self.dropShadow.setBin('fixed', 0, 1) + self.splash = Splash.Splash(render) + self.dustCloud = DustCloud.DustCloud(render) + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + self.jarImage = purchaseModels.find('**/Jar') + self.jarImage.reparentTo(hidden) + self.rewardPanel = DirectLabel(parent=hidden, relief=None, pos=(-0.173, 0.0, -0.55), scale=0.65, text='', text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_pos=(0, -.13), text_font=ToontownGlobals.getSignFont(), image=self.jarImage) + self.rewardPanelTitle = DirectLabel(parent=self.rewardPanel, relief=None, pos=(0, 0, 0.06), scale=0.08, text=TTLocalizer.CannonGameReward, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1)) + self.music = base.loadMusic('phase_4/audio/bgm/MG_cannon_game_tug.ogg') + self.sndCannonMove = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.ogg') + self.sndCannonFire = base.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.ogg') + self.sndHitGround = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg') + self.sndHitTower = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_tower.ogg') + self.sndHitWater = base.loadSfx('phase_4/audio/sfx/MG_cannon_splash.ogg') + self.sndWhizz = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg') + self.sndWin = base.loadSfx('phase_4/audio/sfx/MG_win.ogg') + self.sndRewardTick = base.loadSfx('phase_3.5/audio/sfx/tick_counter.ogg') + guiModel = 'phase_4/models/gui/cannon_game_gui' + cannonGui = loader.loadModel(guiModel) + self.aimPad = DirectFrame(image=cannonGui.find('**/CannonFire_PAD'), relief=None, pos=(0.7, 0, -0.553333), scale=0.8) + cannonGui.removeNode() + self.aimPad.hide() + self.fireButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Fire_Btn_UP'), (guiModel, '**/Fire_Btn_DN'), (guiModel, '**/Fire_Btn_RLVR')), relief=None, pos=(0.0115741, 0, 0.00505051), scale=1.0, command=self.__firePressed) + self.upButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0115741, 0, 0.221717)) + self.downButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0136112, 0, -0.210101), image_hpr=(0, 0, 180)) + self.leftButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(-0.199352, 0, -0.000505269), image_hpr=(0, 0, -90)) + self.rightButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.219167, 0, -0.00101024), image_hpr=(0, 0, 90)) + self.aimPad.setColor(1, 1, 1, 0.9) + + def bindButton(button, upHandler, downHandler): + button.bind(DGG.B1PRESS, lambda x, handler = upHandler: handler()) + button.bind(DGG.B1RELEASE, lambda x, handler = downHandler: handler()) + + bindButton(self.upButton, self.__upPressed, self.__upReleased) + bindButton(self.downButton, self.__downPressed, self.__downReleased) + bindButton(self.leftButton, self.__leftPressed, self.__leftReleased) + bindButton(self.rightButton, self.__rightPressed, self.__rightReleased) + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.hide() + self.DEBUG_TOWER_RANGE = 0 + self.DEBUG_CANNON_FAR_LEFT = 0 + self.DEBUG_TOWER_NEAR = 1 + self.DEBUG_TOWER_FAR_LEFT = 1 + return + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.sky.removeNode() + del self.sky + self.ground.removeNode() + del self.ground + self.tower.removeNode() + del self.tower + self.cannon.removeNode() + del self.cannon + del self.dropShadowDict + self.dropShadow.removeNode() + del self.dropShadow + self.splash.destroy() + del self.splash + self.dustCloud.destroy() + del self.dustCloud + self.hill.removeNode() + del self.hill + self.rewardPanel.destroy() + del self.rewardPanel + self.jarImage.removeNode() + del self.jarImage + del self.music + del self.sndCannonMove + del self.sndCannonFire + del self.sndHitGround + del self.sndHitTower + del self.sndHitWater + del self.sndWhizz + del self.sndWin + del self.sndRewardTick + self.aimPad.destroy() + del self.aimPad + del self.fireButton + del self.upButton + del self.downButton + del self.leftButton + del self.rightButton + for avId in self.toonHeadDict.keys(): + head = self.toonHeadDict[avId] + head.stopBlink() + head.stopLookAroundNow() + av = self.getAvatar(avId) + if av: + av.loop('neutral') + av.setPlayRate(1.0, 'run') + av.nametag.removeNametag(head.tag) + head.delete() + + del self.toonHeadDict + for model in self.toonModelDict.values(): + model.removeNode() + + del self.toonModelDict + del self.toonScaleDict + for interval in self.toonIntervalDict.values(): + interval.finish() + + del self.toonIntervalDict + for avId in self.avIdList: + self.cannonDict[avId][0].removeNode() + del self.cannonDict[avId][0] + + del self.cannonDict + self.timer.destroy() + del self.timer + del self.cannonLocationDict + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.__createCannons() + for avId in self.avIdList: + self.cannonDict[avId][0].reparentTo(render) + + self.towerPos = self.getTowerPosition() + self.tower.setPos(self.towerPos) + self.tower.reparentTo(render) + self.sky.reparentTo(render) + self.ground.reparentTo(render) + self.hill.setPosHpr(0, CANNON_Y + 2.33, 0, 0, 0, 0) + self.hill.reparentTo(render) + self.splash.reparentTo(render) + self.dustCloud.reparentTo(render) + self.__createToonModels(self.localAvId) + camera.reparentTo(render) + self.__oldCamFar = base.camLens.getFar() + base.camLens.setFar(FAR_PLANE_DIST) + self.__startIntro() + base.transitions.irisIn(0.4) + base.playMusic(self.music, looping=1, volume=0.8) + + def offstage(self): + self.notify.debug('offstage') + self.sky.reparentTo(hidden) + self.ground.reparentTo(hidden) + self.hill.reparentTo(hidden) + self.tower.reparentTo(hidden) + for avId in self.avIdList: + self.cannonDict[avId][0].reparentTo(hidden) + if avId in self.dropShadowDict: + self.dropShadowDict[avId].reparentTo(hidden) + av = self.getAvatar(avId) + if av: + av.dropShadow.show() + av.resetLOD() + + self.splash.reparentTo(hidden) + self.splash.stop() + self.dustCloud.reparentTo(hidden) + self.dustCloud.stop() + self.__stopIntro() + base.camLens.setFar(self.__oldCamFar) + self.timer.reparentTo(hidden) + self.rewardPanel.reparentTo(hidden) + DistributedMinigame.offstage(self) + + def getTowerPosition(self): + yRange = TOWER_Y_RANGE + yMin = yRange * 0.3 + yMax = yRange + if self.DEBUG_TOWER_RANGE: + if self.DEBUG_TOWER_NEAR: + y = yMin + else: + y = yMax + else: + y = self.randomNumGen.randint(yMin, yMax) + xRange = TOWER_X_RANGE + if self.DEBUG_TOWER_RANGE: + if self.DEBUG_TOWER_FAR_LEFT: + x = 0 + else: + x = xRange + else: + x = self.randomNumGen.randint(0, xRange) + x = x - int(xRange / 2.0) + if base.wantMinigameDifficulty: + diff = self.getDifficulty() + scale = 0.5 + 0.5 * diff + x *= scale + yCenter = (yMin + yMax) / 2.0 + y = (y - yCenter) * scale + yCenter + x = float(x) * (float(y) / float(yRange)) + y = y - int(yRange / 2.0) + self.notify.debug('getTowerPosition: ' + str(x) + ', ' + str(y)) + return Point3(x, y, 0.0) + + def __createCannons(self): + for avId in self.avIdList: + cannon = self.cannon.copyTo(hidden) + barrel = cannon.find('**/cannon') + self.cannonDict[avId] = [cannon, barrel] + + numAvs = self.numPlayers + for i in xrange(numAvs): + avId = self.avIdList[i] + self.cannonLocationDict[avId] = Point3(i * CANNON_X_SPACING - (numAvs - 1) * CANNON_X_SPACING / 2, CANNON_Y, CANNON_Z) + if self.DEBUG_TOWER_RANGE: + if self.DEBUG_CANNON_FAR_LEFT: + self.cannonLocationDict[avId] = Point3(0 * CANNON_X_SPACING - (4 - 1) * CANNON_X_SPACING / 2, CANNON_Y, CANNON_Z) + else: + self.cannonLocationDict[avId] = Point3(3 * CANNON_X_SPACING - (4 - 1) * CANNON_X_SPACING / 2, CANNON_Y, CANNON_Z) + self.cannonPositionDict[avId] = [0, CannonGameGlobals.CANNON_ANGLE_MIN] + self.cannonDict[avId][0].setPos(self.cannonLocationDict[avId]) + self.__updateCannonPosition(avId) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + for avId in self.avIdList: + if avId != self.localAvId: + self.__createToonModels(avId) + + def __createToonModels(self, avId): + toon = self.getAvatar(avId) + self.toonScaleDict[avId] = toon.getScale() + toon.useLOD(1000) + toonParent = render.attachNewNode('toonOriginChange') + toon.reparentTo(toonParent) + toon.setPosHpr(0, 0, -(toon.getHeight() / 2.0), 0, 0, 0) + self.toonModelDict[avId] = toonParent + head = ToonHead.ToonHead() + head.setupHead(self.getAvatar(avId).style) + head.reparentTo(hidden) + self.toonHeadDict[avId] = head + toon = self.getAvatar(avId) + tag = NametagFloat3d() + tag.setContents(Nametag.CSpeech | Nametag.CThought) + tag.setBillboardOffset(0) + tag.setAvatar(head) + toon.nametag.addNametag(tag) + tagPath = head.attachNewNode(tag) + tagPath.setPos(0, 0, 1) + head.tag = tag + self.__loadToonInCannon(avId) + self.getAvatar(avId).dropShadow.hide() + self.dropShadowDict[avId] = self.dropShadow.copyTo(hidden) + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.__stopIntro() + self.__putCameraBehindCannon() + if not base.config.GetBool('endless-cannon-game', 0): + self.timer.show() + self.timer.countdown(CannonGameGlobals.GameTime, self.__gameTimerExpired) + self.rewardPanel.reparentTo(base.a2dTopRight) + self.scoreMult = MinigameGlobals.getScoreMult(self.cr.playGame.hood.id) + self.__startRewardCountdown() + self.airborneToons = 0 + self.clockStopTime = None + self.gameFSM.request('aim') + return + + def __gameTimerExpired(self): + self.notify.debug('game timer expired') + self.gameOver() + + def __playing(self): + return self.gameFSM.getCurrentState() != self.gameFSM.getFinalState() + + def updateCannonPosition(self, avId, zRot, angle): + if not self.hasLocalToon: + return + if not self.__playing(): + return + if avId != self.localAvId: + self.cannonPositionDict[avId] = [zRot, angle] + self.__updateCannonPosition(avId) + + def setCannonWillFire(self, avId, fireTime, zRot, angle): + if not self.hasLocalToon: + return + if not self.__playing(): + return + self.notify.debug('setCannonWillFire: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle) + ', time=' + str(fireTime)) + self.cannonPositionDict[avId][0] = zRot + self.cannonPositionDict[avId][1] = angle + self.__updateCannonPosition(avId) + task = Task(self.__fireCannonTask) + task.avId = avId + task.fireTime = fireTime + timeToWait = task.fireTime - self.getCurrentGameTime() + if timeToWait > 0.0: + fireTask = Task.sequence(Task.pause(timeToWait), task) + else: + fireTask = task + fireTask = task + taskMgr.add(fireTask, 'fireCannon' + str(avId)) + self.airborneToons += 1 + + def announceToonWillLandInWater(self, avId, landTime): + if not self.hasLocalToon: + return + self.notify.debug('announceToonWillLandInWater: ' + str(avId) + ': time=' + str(landTime)) + if self.clockStopTime == None: + self.clockStopTime = landTime + return + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterAim(self): + self.notify.debug('enterAim') + self.__enableAimInterface() + self.__putCameraBehindCannon() + + def exitAim(self): + self.__disableAimInterface() + + def enterShoot(self): + self.notify.debug('enterShoot') + self.__broadcastLocalCannonPosition() + self.sendUpdate('setCannonLit', [self.cannonPositionDict[self.localAvId][0], self.cannonPositionDict[self.localAvId][1]]) + + def exitShoot(self): + pass + + def __somebodyWon(self, avId): + if avId == self.localAvId: + base.playSfx(self.sndWin) + self.__killRewardCountdown() + self.timer.stop() + self.gameFSM.request('waitForToonsToLand') + + def enterWaitForToonsToLand(self): + self.notify.debug('enterWaitForToonsToLand') + if not self.airborneToons: + self.gameOver() + + def exitWaitForToonsToLand(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.music.stop() + self.__killRewardCountdown() + if hasattr(self, 'jarIval'): + self.jarIval.finish() + del self.jarIval + for avId in self.avIdList: + taskMgr.remove('fireCannon' + str(avId)) + taskMgr.remove('flyingToon' + str(avId)) + + def exitCleanup(self): + pass + + def __enableAimInterface(self): + self.aimPad.show() + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + self.accept(self.UP_KEY, self.__upKeyPressed) + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__spawnLocalCannonMoveTask() + + def __disableAimInterface(self): + self.aimPad.hide() + self.ignore(self.FIRE_KEY) + self.ignore(self.UP_KEY) + self.ignore(self.DOWN_KEY) + self.ignore(self.LEFT_KEY) + self.ignore(self.RIGHT_KEY) + self.ignore(self.FIRE_KEY + '-up') + self.ignore(self.UP_KEY + '-up') + self.ignore(self.DOWN_KEY + '-up') + self.ignore(self.LEFT_KEY + '-up') + self.ignore(self.RIGHT_KEY + '-up') + self.__killLocalCannonMoveTask() + + def __fireKeyPressed(self): + self.ignore(self.FIRE_KEY) + self.accept(self.FIRE_KEY + '-up', self.__fireKeyReleased) + self.__firePressed() + + def __upKeyPressed(self): + self.ignore(self.UP_KEY) + self.accept(self.UP_KEY + '-up', self.__upKeyReleased) + self.__upPressed() + + def __downKeyPressed(self): + self.ignore(self.DOWN_KEY) + self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased) + self.__downPressed() + + def __leftKeyPressed(self): + self.ignore(self.LEFT_KEY) + self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased) + self.__leftPressed() + + def __rightKeyPressed(self): + self.ignore(self.RIGHT_KEY) + self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased) + self.__rightPressed() + + def __fireKeyReleased(self): + self.ignore(self.FIRE_KEY + '-up') + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + self.__fireReleased() + + def __leftKeyReleased(self): + self.ignore(self.LEFT_KEY + '-up') + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.__leftReleased() + + def __rightKeyReleased(self): + self.ignore(self.RIGHT_KEY + '-up') + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__rightReleased() + + def __upKeyReleased(self): + self.ignore(self.UP_KEY + '-up') + self.accept(self.UP_KEY, self.__upKeyPressed) + self.__upReleased() + + def __downKeyReleased(self): + self.ignore(self.DOWN_KEY + '-up') + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.__downReleased() + + def __firePressed(self): + self.notify.debug('fire pressed') + self.gameFSM.request('shoot') + + def __upPressed(self): + self.notify.debug('up pressed') + self.upPressed = self.__enterControlActive(self.upPressed) + + def __downPressed(self): + self.notify.debug('down pressed') + self.downPressed = self.__enterControlActive(self.downPressed) + + def __leftPressed(self): + self.notify.debug('left pressed') + self.leftPressed = self.__enterControlActive(self.leftPressed) + + def __rightPressed(self): + self.notify.debug('right pressed') + self.rightPressed = self.__enterControlActive(self.rightPressed) + + def __upReleased(self): + self.notify.debug('up released') + self.upPressed = self.__exitControlActive(self.upPressed) + + def __downReleased(self): + self.notify.debug('down released') + self.downPressed = self.__exitControlActive(self.downPressed) + + def __leftReleased(self): + self.notify.debug('left released') + self.leftPressed = self.__exitControlActive(self.leftPressed) + + def __rightReleased(self): + self.notify.debug('right released') + self.rightPressed = self.__exitControlActive(self.rightPressed) + + def __enterControlActive(self, control): + return control + 1 + + def __exitControlActive(self, control): + return max(0, control - 1) + + def __spawnLocalCannonMoveTask(self): + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.cannonMoving = 0 + task = Task(self.__localCannonMoveTask) + task.lastPositionBroadcastTime = 0.0 + taskMgr.add(task, self.LOCAL_CANNON_MOVE_TASK) + + def __killLocalCannonMoveTask(self): + taskMgr.remove(self.LOCAL_CANNON_MOVE_TASK) + if self.cannonMoving: + self.sndCannonMove.stop() + + def __localCannonMoveTask(self, task): + pos = self.cannonPositionDict[self.localAvId] + oldRot = pos[0] + oldAng = pos[1] + rotVel = 0 + if self.leftPressed: + rotVel += CannonGameGlobals.CANNON_ROTATION_VEL + if self.rightPressed: + rotVel -= CannonGameGlobals.CANNON_ROTATION_VEL + pos[0] += rotVel * globalClock.getDt() + if pos[0] < CannonGameGlobals.CANNON_ROTATION_MIN: + pos[0] = CannonGameGlobals.CANNON_ROTATION_MIN + elif pos[0] > CannonGameGlobals.CANNON_ROTATION_MAX: + pos[0] = CannonGameGlobals.CANNON_ROTATION_MAX + angVel = 0 + if self.upPressed: + angVel += CannonGameGlobals.CANNON_ANGLE_VEL + if self.downPressed: + angVel -= CannonGameGlobals.CANNON_ANGLE_VEL + pos[1] += angVel * globalClock.getDt() + if pos[1] < CannonGameGlobals.CANNON_ANGLE_MIN: + pos[1] = CannonGameGlobals.CANNON_ANGLE_MIN + elif pos[1] > CannonGameGlobals.CANNON_ANGLE_MAX: + pos[1] = CannonGameGlobals.CANNON_ANGLE_MAX + if oldRot != pos[0] or oldAng != pos[1]: + if self.cannonMoving == 0: + self.cannonMoving = 1 + base.playSfx(self.sndCannonMove, looping=1) + self.__updateCannonPosition(self.localAvId) + if task.time - task.lastPositionBroadcastTime > CANNON_MOVE_UPDATE_FREQ: + task.lastPositionBroadcastTime = task.time + self.__broadcastLocalCannonPosition() + elif self.cannonMoving: + self.cannonMoving = 0 + self.sndCannonMove.stop() + self.__broadcastLocalCannonPosition() + return Task.cont + + def __broadcastLocalCannonPosition(self): + self.sendUpdate('setCannonPosition', [self.cannonPositionDict[self.localAvId][0], self.cannonPositionDict[self.localAvId][1]]) + + def __updateCannonPosition(self, avId): + self.cannonDict[avId][0].setHpr(self.cannonPositionDict[avId][0], 0.0, 0.0) + self.cannonDict[avId][1].setHpr(0.0, self.cannonPositionDict[avId][1], 0.0) + + def __getCameraPositionBehindCannon(self): + return Point3(self.cannonLocationDict[self.localAvId][0], CANNON_Y - 25.0, CANNON_Z + 7) + + def __putCameraBehindCannon(self): + camera.setPos(self.__getCameraPositionBehindCannon()) + camera.setHpr(0, 0, 0) + + def __loadToonInCannon(self, avId): + self.toonModelDict[avId].detachNode() + head = self.toonHeadDict[avId] + head.startBlink() + head.startLookAround() + head.reparentTo(self.cannonDict[avId][1]) + head.setPosHpr(0, 6, 0, 0, -45, 0) + sc = self.toonScaleDict[avId] + head.setScale(render, sc[0], sc[1], sc[2]) + + def __toRadians(self, angle): + return angle * 2.0 * math.pi / 360.0 + + def __toDegrees(self, angle): + return angle * 360.0 / (2.0 * math.pi) + + def __calcFlightResults(self, avId, launchTime): + head = self.toonHeadDict[avId] + startPos = head.getPos(render) + startHpr = head.getHpr(render) + hpr = self.cannonDict[avId][1].getHpr(render) + towerPos = self.tower.getPos(render) + rotation = self.__toRadians(hpr[0]) + angle = self.__toRadians(hpr[1]) + horizVel = INITIAL_VELOCITY * math.cos(angle) + xVel = horizVel * -math.sin(rotation) + yVel = horizVel * math.cos(rotation) + zVel = INITIAL_VELOCITY * math.sin(angle) + startVel = Vec3(xVel, yVel, zVel) + trajectory = Trajectory.Trajectory(launchTime, startPos, startVel) + towerList = [towerPos + Point3(0, 0, BUCKET_HEIGHT), TOWER_RADIUS, TOWER_HEIGHT - BUCKET_HEIGHT] + self.notify.debug('calcFlightResults(%s): rotation(%s), angle(%s), horizVel(%s), xVel(%s), yVel(%s), zVel(%s), startVel(%s), trajectory(%s), towerList(%s)' % (avId, + rotation, + angle, + horizVel, + xVel, + yVel, + zVel, + startVel, + trajectory, + towerList)) + timeOfImpact, hitWhat = self.__calcToonImpact(trajectory, towerList) + return startPos, startHpr, startVel, trajectory, timeOfImpact, hitWhat + + def __fireCannonTask(self, task): + launchTime = task.fireTime + avId = task.avId + self.notify.debug('FIRING CANNON FOR AVATAR ' + str(avId)) + startPos, startHpr, startVel, trajectory, timeOfImpact, hitWhat = self.__calcFlightResults(avId, launchTime) + + self.notify.debug('start position: ' + str(startPos)) + self.notify.debug('start velocity: ' + str(startVel)) + self.notify.debug('time of launch: ' + str(launchTime)) + self.notify.debug('time of impact: ' + str(timeOfImpact)) + self.notify.debug('location of impact: ' + str(trajectory.getPos(timeOfImpact))) + if hitWhat == self.HIT_WATER: + self.notify.debug('toon will land in the water') + elif hitWhat == self.HIT_TOWER: + self.notify.debug('toon will hit the tower') + else: + self.notify.debug('toon will hit the ground') + head = self.toonHeadDict[avId] + head.stopBlink() + head.stopLookAroundNow() + head.reparentTo(hidden) + av = self.toonModelDict[avId] + av.reparentTo(render) + av.setPos(startPos) + av.setHpr(startHpr) + avatar = self.getAvatar(avId) + avatar.loop('swim') + avatar.setPosHpr(0, 0, -(avatar.getHeight() / 2.0), 0, 0, 0) + shootTask = Task(self.__shootTask) + flyTask = Task(self.__flyTask) + seqDoneTask = Task(self.__flySequenceDoneTask) + info = {} + info['avId'] = avId + info['trajectory'] = trajectory + info['launchTime'] = launchTime + info['timeOfImpact'] = timeOfImpact + info['hitWhat'] = hitWhat + info['toon'] = self.toonModelDict[avId] + info['hRot'] = self.cannonPositionDict[avId][0] + info['haveWhistled'] = 0 + info['maxCamPullback'] = CAMERA_PULLBACK_MIN + info['timeEnterTowerXY'], info['timeExitTowerXY'] = trajectory.calcEnterAndLeaveCylinderXY(self.tower.getPos(render), TOWER_RADIUS) + shootTask.info = info + flyTask.info = info + seqDoneTask.info = info + seqTask = Task.sequence(shootTask, flyTask, Task.pause(LAND_TIME), seqDoneTask) + taskMgr.add(seqTask, 'flyingToon' + str(avId)) + if avId == self.localAvId: + if info['hitWhat'] == self.HIT_WATER: + self.sendUpdate('setToonWillLandInWater', [info['timeOfImpact']]) + return Task.done + + def __calcToonImpact(self, trajectory, waterTower): + self.notify.debug('trajectory: %s' % trajectory) + self.notify.debug('waterTower: %s' % waterTower) + waterDiscCenter = Point3(waterTower[0]) + waterDiscCenter.setZ(waterDiscCenter[2] + waterTower[2]) + t_waterImpact = trajectory.checkCollisionWithDisc(waterDiscCenter, waterTower[1]) + self.notify.debug('t_waterImpact: %s' % t_waterImpact) + if t_waterImpact > 0: + return (t_waterImpact, self.HIT_WATER) + t_towerImpact = trajectory.checkCollisionWithCylinderSides(waterTower[0], waterTower[1], waterTower[2]) + self.notify.debug('t_towerImpact: %s' % t_towerImpact) + if t_towerImpact > 0: + return (t_towerImpact, self.HIT_TOWER) + t_groundImpact = trajectory.checkCollisionWithGround() + self.notify.debug('t_groundImpact: %s' % t_groundImpact) + if t_groundImpact >= trajectory.getStartTime(): + return (t_groundImpact, self.HIT_GROUND) + else: + self.notify.error('__calcToonImpact: toon never impacts ground?') + return (self.startTime, self.HIT_GROUND) + + def __shootTask(self, task): + base.playSfx(self.sndCannonFire) + self.dropShadowDict[task.info['avId']].reparentTo(render) + return Task.done + + def __flyTask(self, task): + curTime = task.time + task.info['launchTime'] + t = min(curTime, task.info['timeOfImpact']) + pos = task.info['trajectory'].getPos(t) + task.info['toon'].setPos(pos) + shadowPos = Point3(pos) + if t >= task.info['timeEnterTowerXY'] and t <= task.info['timeExitTowerXY'] and pos[2] >= self.tower.getPos(render)[2] + TOWER_HEIGHT: + shadowPos.setZ(self.tower.getPos(render)[2] + TOWER_HEIGHT + SHADOW_Z_OFFSET) + else: + shadowPos.setZ(SHADOW_Z_OFFSET) + self.dropShadowDict[task.info['avId']].setPos(shadowPos) + vel = task.info['trajectory'].getVel(t) + run = math.sqrt(vel[0] * vel[0] + vel[1] * vel[1]) + rise = vel[2] + theta = self.__toDegrees(math.atan(rise / run)) + task.info['toon'].setHpr(task.info['hRot'], -90 + theta, 0) + if task.info['avId'] == self.localAvId: + lookAt = self.tower.getPos(render) + lookAt.setZ(lookAt.getZ() - TOWER_HEIGHT / 2.0) + towerPos = Point3(self.towerPos) + towerPos.setZ(TOWER_HEIGHT) + ttVec = Vec3(pos - towerPos) + toonTowerDist = ttVec.length() + multiplier = 0.0 + if toonTowerDist < TOON_TOWER_THRESHOLD: + up = Vec3(0.0, 0.0, 1.0) + perp = up.cross(vel) + perp.normalize() + if ttVec.dot(perp) > 0.0: + perp = Vec3(-perp[0], -perp[1], -perp[2]) + a = 1.0 - toonTowerDist / TOON_TOWER_THRESHOLD + a_2 = a * a + multiplier = -2.0 * a_2 * a + 3 * a_2 + lookAt = lookAt + perp * (multiplier * MAX_LOOKAT_OFFSET) + foo = Vec3(pos - lookAt) + foo.normalize() + task.info['maxCamPullback'] = max(task.info['maxCamPullback'], CAMERA_PULLBACK_MIN + multiplier * (CAMERA_PULLBACK_MAX - CAMERA_PULLBACK_MIN)) + foo = foo * task.info['maxCamPullback'] + camPos = pos + Point3(foo) + camera.setPos(camPos) + camera.lookAt(pos) + if task.info['haveWhistled'] == 0: + if -vel[2] > WHISTLE_SPEED: + if t < task.info['timeOfImpact'] - 0.5: + task.info['haveWhistled'] = 1 + base.playSfx(self.sndWhizz) + if t == task.info['timeOfImpact']: + if task.info['haveWhistled']: + self.sndWhizz.stop() + self.dropShadowDict[task.info['avId']].reparentTo(hidden) + avatar = self.getAvatar(task.info['avId']) + if task.info['hitWhat'] == self.HIT_WATER: + avatar.loop('neutral') + self.splash.setPos(task.info['toon'].getPos()) + self.splash.setScale(2) + self.splash.play() + base.playSfx(self.sndHitWater) + task.info['toon'].setHpr(task.info['hRot'], 0, 0) + self.__somebodyWon(task.info['avId']) + elif task.info['hitWhat'] == self.HIT_TOWER: + toon = task.info['toon'] + pos = toon.getPos() + ttVec = Vec3(pos - self.towerPos) + ttVec.setZ(0) + ttVec.normalize() + h = rad2Deg(math.asin(ttVec[0])) + toon.setHpr(h, 94, 0) + deltaZ = TOWER_HEIGHT - BUCKET_HEIGHT + sf = min(max(pos[2] - BUCKET_HEIGHT, 0), deltaZ) / deltaZ + hitPos = pos + Point3(ttVec * (0.75 * sf)) + toon.setPos(hitPos) + hitPos.setZ(hitPos[2] - 1.0) + s = Sequence(Wait(0.5), toon.posInterval(duration=LAND_TIME - 0.5, pos=hitPos, blendType='easeIn')) + self.toonIntervalDict[task.info['avId']] = s + s.start() + avatar.iPos() + avatar.pose('slip-forward', 25) + base.playSfx(self.sndHitTower) + elif task.info['hitWhat'] == self.HIT_GROUND: + task.info['toon'].setP(render, -150.0) + self.dustCloud.setPos(task.info['toon'], 0, 0, -2.5) + self.dustCloud.setScale(0.35) + self.dustCloud.play() + base.playSfx(self.sndHitGround) + avatar.setPlayRate(2.0, 'run') + avatar.loop('run') + return Task.done + return Task.cont + + def __flySequenceDoneTask(self, task): + self.airborneToons -= 1 + if self.gameFSM.getCurrentState().getName() == 'waitForToonsToLand': + if 0 == self.airborneToons: + self.gameOver() + else: + self.__loadToonInCannon(task.info['avId']) + if task.info['avId'] == self.localAvId: + self.gameFSM.request('aim') + return Task.done + + def __startRewardCountdown(self): + taskMgr.remove(self.REWARD_COUNTDOWN_TASK) + taskMgr.add(self.__updateRewardCountdown, self.REWARD_COUNTDOWN_TASK) + + def __killRewardCountdown(self): + taskMgr.remove(self.REWARD_COUNTDOWN_TASK) + + def __updateRewardCountdown(self, task): + if not hasattr(self, 'rewardPanel'): + return Task.cont + curTime = self.getCurrentGameTime() + if self.clockStopTime is not None: + if self.clockStopTime < curTime: + self.__killRewardCountdown() + curTime = self.clockStopTime + score = int(self.scoreMult * CannonGameGlobals.calcScore(curTime) + 0.5) + if not hasattr(task, 'curScore'): + task.curScore = score + self.rewardPanel['text'] = str(score) + if task.curScore != score: + if hasattr(self, 'jarIval'): + self.jarIval.finish() + s = self.rewardPanel.getScale() + self.jarIval = Parallel(Sequence(self.rewardPanel.scaleInterval(0.15, s * 3.0 / 4.0, blendType='easeOut'), self.rewardPanel.scaleInterval(0.15, s, blendType='easeIn')), SoundInterval(self.sndRewardTick), name='cannonGameRewardJarThrob') + self.jarIval.start() + task.curScore = score + return Task.cont + + def __startIntro(self): + self.T_WATER = 1 + self.T_WATER2LONGVIEW = 1 + self.T_LONGVIEW = 1 + self.T_LONGVIEW2TOONHEAD = 2 + self.T_TOONHEAD = 2 + self.T_TOONHEAD2CANNONBACK = 2 + self.__introCameraInterval = None + taskLookInWater = Task(self.__taskLookInWater) + taskPullBackFromWater = Task(self.__taskPullBackFromWater) + taskFlyUpToToon = Task(self.__flyUpToToon) + taskFlyToBackOfCannon = Task(self.__flyToBackOfCannon) + commonData = {} + taskLookInWater.data = commonData + taskPullBackFromWater.data = commonData + taskFlyUpToToon.data = commonData + taskFlyToBackOfCannon.data = commonData + introTask = Task.sequence(taskLookInWater, Task.pause(self.T_WATER), taskPullBackFromWater, Task.pause(self.T_WATER2LONGVIEW + self.T_LONGVIEW), taskFlyUpToToon, Task.pause(self.T_LONGVIEW2TOONHEAD + self.T_TOONHEAD), taskFlyToBackOfCannon) + taskMgr.add(introTask, self.INTRO_TASK_NAME) + + def __stopIntro(self): + taskMgr.remove(self.INTRO_TASK_NAME) + if self.__introCameraInterval: + self.__introCameraInterval.finish() + camera.wrtReparentTo(render) + + def __spawnCameraLookAtLerp(self, targetPos, targetLookAt, duration): + oldPos = camera.getPos() + oldHpr = camera.getHpr() + camera.setPos(targetPos) + camera.lookAt(targetLookAt) + targetQuat = Quat() + targetQuat.setHpr(camera.getHpr()) + camera.setPos(oldPos) + camera.setHpr(oldHpr) + if self.__introCameraInterval: + self.__introCameraInterval.finish() + self.__introCameraInterval = camera.posQuatInterval(duration, Point3(targetPos), targetQuat, blendType='easeInOut') + self.__introCameraInterval.start() + + def __taskLookInWater(self, task): + task.data['cannonCenter'] = Point3(0, CANNON_Y, CANNON_Z) + task.data['towerWaterCenter'] = Point3(self.towerPos + Point3(0, 0, TOWER_HEIGHT)) + task.data['vecTowerToCannon'] = Point3(task.data['cannonCenter'] - task.data['towerWaterCenter']) + vecAwayFromCannons = Vec3(Point3(0, 0, 0) - task.data['vecTowerToCannon']) + vecAwayFromCannons.setZ(0.0) + vecAwayFromCannons.normalize() + camLoc = Point3(vecAwayFromCannons * 20) + Point3(0, 0, 20) + camLoc = camLoc + task.data['towerWaterCenter'] + camera.setPos(camLoc) + camera.lookAt(task.data['towerWaterCenter']) + task.data['vecAwayFromCannons'] = vecAwayFromCannons + return Task.done + + def __taskPullBackFromWater(self, task): + camLoc = Point3(task.data['vecAwayFromCannons'] * 40) + Point3(0, 0, 20) + camLoc = camLoc + task.data['towerWaterCenter'] + lookAt = task.data['cannonCenter'] + self.__spawnCameraLookAtLerp(camLoc, lookAt, self.T_WATER2LONGVIEW) + return Task.done + + def __flyUpToToon(self, task): + headPos = self.toonHeadDict[self.localAvId].getPos(render) + camLoc = headPos + Point3(0, 5, 0) + lookAt = Point3(headPos) + self.__spawnCameraLookAtLerp(camLoc, lookAt, self.T_LONGVIEW2TOONHEAD) + return Task.done + + def __flyToBackOfCannon(self, task): + lerpNode = hidden.attachNewNode('CannonGameCameraLerpNode') + lerpNode.reparentTo(render) + lerpNode.setPos(self.cannonLocationDict[self.localAvId] + Point3(0, 1, 0)) + relCamPos = camera.getPos(lerpNode) + relCamHpr = camera.getHpr(lerpNode) + startRotation = lerpNode.getHpr() + endRotation = Point3(-180, 0, 0) + lerpNode.setHpr(endRotation) + camera.setPos(self.__getCameraPositionBehindCannon()) + endPos = camera.getPos(lerpNode) + lerpNode.setHpr(startRotation) + camera.reparentTo(lerpNode) + camera.setPos(relCamPos) + camera.setHpr(relCamHpr) + if self.__introCameraInterval: + self.__introCameraInterval.finish() + self.__introCameraInterval = Parallel( + lerpNode.hprInterval(self.T_TOONHEAD2CANNONBACK, endRotation, blendType='easeInOut'), + camera.posInterval(self.T_TOONHEAD2CANNONBACK, endPos, blendType='easeInOut')) + self.__introCameraInterval.start() + return Task.done diff --git a/toontown/minigame/DistributedCannonGameAI.py b/toontown/minigame/DistributedCannonGameAI.py new file mode 100755 index 00000000..5350f14f --- /dev/null +++ b/toontown/minigame/DistributedCannonGameAI.py @@ -0,0 +1,129 @@ +from DistributedMinigameAI import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +import CannonGameGlobals + +class DistributedCannonGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedCannonGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + if not config.GetBool('endless-cannon-game', 0): + taskMgr.doMethodLater(CannonGameGlobals.GameTime, self.timerExpired, self.taskName('gameTimer')) + + def timerExpired(self, task): + self.notify.debug('timer expired') + self.gameOver() + return Task.done + + def __playing(self): + if not hasattr(self, 'gameFSM'): + return False + if self.gameFSM.getCurrentState() == None: + return False + return self.gameFSM.getCurrentState().getName() == 'play' + + def _checkCannonRange(self, zRot, angle, avId): + outOfRange = 0 + if zRot < CannonGameGlobals.CANNON_ROTATION_MIN or zRot > CannonGameGlobals.CANNON_ROTATION_MAX: + self.air.writeServerEvent('suspicious', avId, 'Cannon game z-rotation out of range: %s' % zRot) + self.notify.warning('av %s cannon z-rotation out of range: %s' % (avId, zRot)) + outOfRange = 1 + if angle < CannonGameGlobals.CANNON_ANGLE_MIN or angle > CannonGameGlobals.CANNON_ANGLE_MAX: + self.air.writeServerEvent('suspicious', avId, 'Cannon game vertical angle out of range: %s' % angle) + self.notify.warning('av %s cannon vertical angle out of range: %s' % (avId, angle)) + outOfRange = 1 + return outOfRange + + def setCannonPosition(self, zRot, angle): + if not self.__playing(): + self.notify.debug('ignoring setCannonPosition message') + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setCannonPosition: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle)) + if self._checkCannonRange(zRot, angle, avId): + return + self.sendUpdate('updateCannonPosition', [avId, zRot, angle]) + + def setCannonLit(self, zRot, angle): + if not self.__playing(): + self.notify.debug('ignoring setCannonLit message') + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setCannonLit: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle)) + if self._checkCannonRange(zRot, angle, avId): + return + fireTime = self.getCurrentGameTime() + CannonGameGlobals.FUSE_TIME + self.sendUpdate('setCannonWillFire', [avId, + fireTime, + zRot, + angle]) + + def setToonWillLandInWater(self, landTime): + if not self.__playing(): + self.notify.debug('ignoring setToonWillLandInWater message') + return + senderAvId = self.air.getAvatarIdFromSender() + score = CannonGameGlobals.calcScore(landTime) + for avId in self.avIdList: + self.scoreDict[avId] = score + + self.notify.debug('setToonWillLandInWater: time=%s, score=%s' % (landTime, score)) + taskMgr.remove(self.taskName('gameTimer')) + delay = max(0, landTime - self.getCurrentGameTime()) + taskMgr.doMethodLater(delay, self.toonLandedInWater, self.taskName('game-over')) + self.sendUpdate('announceToonWillLandInWater', [senderAvId, landTime]) + + def toonLandedInWater(self, task): + self.notify.debug('toonLandedInWater') + if self.__playing(): + self.gameOver() + return Task.done + + def exitPlay(self): + taskMgr.remove(self.taskName('gameTimer')) + taskMgr.remove(self.taskName('game-over')) + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedCatchGame.py b/toontown/minigame/DistributedCatchGame.py new file mode 100755 index 00000000..f9147367 --- /dev/null +++ b/toontown/minigame/DistributedCatchGame.py @@ -0,0 +1,960 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from DistributedMinigame import * +from direct.interval.IntervalGlobal import * +from OrthoWalk import * +from direct.showbase.PythonUtil import Functor, bound, lineupPos, lerp +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import TTLocalizer +import CatchGameGlobals +from direct.task.Task import Task +from toontown.toon import Toon +from toontown.suit import Suit +import MinigameAvatarScorePanel +from toontown.toonbase import ToontownTimer +from toontown.toonbase import ToontownGlobals +import CatchGameToonSD +import Trajectory +import math +from direct.distributed import DistributedSmoothNode +from direct.showbase.RandomNumGen import RandomNumGen +import MinigameGlobals +from toontown.toon import ToonDNA +from toontown.suit import SuitDNA +from CatchGameGlobals import DropObjectTypes +from CatchGameGlobals import Name2DropObjectType +from DropPlacer import * +from DropScheduler import * + +class DistributedCatchGame(DistributedMinigame): + DropTaskName = 'dropSomething' + EndGameTaskName = 'endCatchGame' + SuitWalkTaskName = 'catchGameSuitWalk' + DropObjectPlurals = {'apple': TTLocalizer.CatchGameApples, + 'orange': TTLocalizer.CatchGameOranges, + 'pear': TTLocalizer.CatchGamePears, + 'coconut': TTLocalizer.CatchGameCoconuts, + 'watermelon': TTLocalizer.CatchGameWatermelons, + 'pineapple': TTLocalizer.CatchGamePineapples, + 'anvil': TTLocalizer.CatchGameAnvils} + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedCatchGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.setUsesSmoothing() + self.setUsesLookAround() + + def getTitle(self): + return TTLocalizer.CatchGameTitle + + def getInstructions(self): + return TTLocalizer.CatchGameInstructions % {'fruit': self.DropObjectPlurals[self.fruitName], + 'badThing': self.DropObjectPlurals['anvil']} + + def getMaxDuration(self): + return CatchGameGlobals.GameDuration + 5 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.defineConstants() + groundModels = ['phase_4/models/minigames/treehouse_2players', + 'phase_4/models/minigames/treehouse_2players', + 'phase_4/models/minigames/treehouse_3players', + 'phase_4/models/minigames/treehouse_4players'] + index = self.getNumPlayers() - 1 + self.ground = loader.loadModel(groundModels[index]) + self.ground.setHpr(180, -90, 0) + self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.dropObjModels = {} + for objType in DropObjectTypes: + if objType.name not in ['anvil', self.fruitName]: + continue + model = loader.loadModel(objType.modelPath) + self.dropObjModels[objType.name] = model + modelScales = {'apple': 0.7, + 'orange': 0.7, + 'pear': 0.5, + 'coconut': 0.7, + 'watermelon': 0.6, + 'pineapple': 0.45} + if objType.name in modelScales: + model.setScale(modelScales[objType.name]) + if objType == Name2DropObjectType['pear']: + model.setZ(-.6) + if objType == Name2DropObjectType['coconut']: + model.setP(180) + if objType == Name2DropObjectType['watermelon']: + model.setH(135) + model.setZ(-.5) + if objType == Name2DropObjectType['pineapple']: + model.setZ(-1.7) + if objType == Name2DropObjectType['anvil']: + model.setZ(-self.ObjRadius) + model.flattenMedium() + + self.music = base.loadMusic('phase_4/audio/bgm/MG_toontag.ogg') + self.sndGoodCatch = base.loadSfx('phase_4/audio/sfx/SZ_DD_treasure.ogg') + self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg') + self.sndAnvilLand = base.loadSfx('phase_4/audio/sfx/AA_drop_anvil_miss.ogg') + self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.ogg') + self.toonSDs = {} + avId = self.localAvId + toonSD = CatchGameToonSD.CatchGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.load() + if self.WantSuits: + suitTypes = ['f', + 'tm', + 'pp', + 'dt'] + self.suits = [] + for type in suitTypes: + suit = Suit.Suit() + d = SuitDNA.SuitDNA() + d.newSuit(type) + suit.setDNA(d) + suit.nametag3d.stash() + suit.nametag.destroy() + suit.pose('walk', 0) + self.suits.append(suit) + + self.__textGen = TextNode('ringGame') + self.__textGen.setFont(ToontownGlobals.getSignFont()) + self.__textGen.setAlign(TextNode.ACenter) + self.introMovie = self.getIntroMovie() + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + self.introMovie.finish() + del self.introMovie + del self.__textGen + for avId in self.toonSDs.keys(): + toonSD = self.toonSDs[avId] + toonSD.unload() + + del self.toonSDs + for suit in self.suits: + suit.reparentTo(hidden) + suit.delete() + + del self.suits + self.ground.removeNode() + del self.ground + self.dropShadow.removeNode() + del self.dropShadow + for model in self.dropObjModels.values(): + model.removeNode() + + del self.dropObjModels + del self.music + del self.sndGoodCatch + del self.sndOof + del self.sndAnvilLand + del self.sndPerfect + + def getObjModel(self, objName): + return self.dropObjModels[objName].copyTo(hidden) + + def __genText(self, text): + self.__textGen.setText(text) + return self.__textGen.generate() + + def calcDifficultyConstants(self, difficulty, numPlayers): + ToonSpeedRange = [16.0, 25.0] + self.ToonSpeed = lerp(ToonSpeedRange[0], ToonSpeedRange[1], difficulty) + self.SuitSpeed = self.ToonSpeed / 2.0 + self.SuitPeriodRange = [lerp(5.0, 3.0, self.getDifficulty()), lerp(15.0, 8.0, self.getDifficulty())] + + def scaledDimensions(widthHeight, scale): + w, h = widthHeight + return [math.sqrt(scale * w * w), math.sqrt(scale * h * h)] + + BaseStageDimensions = [20, 15] + areaScales = [1.0, + 1.0, + 3.0 / 2, + 4.0 / 2] + self.StageAreaScale = areaScales[numPlayers - 1] + self.StageLinearScale = math.sqrt(self.StageAreaScale) + self.notify.debug('StageLinearScale: %s' % self.StageLinearScale) + self.StageDimensions = scaledDimensions(BaseStageDimensions, self.StageAreaScale) + self.notify.debug('StageDimensions: %s' % self.StageDimensions) + self.StageHalfWidth = self.StageDimensions[0] / 2.0 + self.StageHalfHeight = self.StageDimensions[1] / 2.0 + MOHs = [24] * 2 + [26, 28] + self.MinOffscreenHeight = MOHs[self.getNumPlayers() - 1] + distance = math.sqrt(self.StageDimensions[0] * self.StageDimensions[0] + self.StageDimensions[1] * self.StageDimensions[1]) + distance /= self.StageLinearScale + if self.DropPlacerType == PathDropPlacer: + distance /= 1.5 + ToonRunDuration = distance / self.ToonSpeed + offScreenOnScreenRatio = 1.0 + fraction = 1.0 / 3 * 0.85 + self.BaselineOnscreenDropDuration = ToonRunDuration / (fraction * (1.0 + offScreenOnScreenRatio)) + self.notify.debug('BaselineOnscreenDropDuration=%s' % self.BaselineOnscreenDropDuration) + self.OffscreenTime = offScreenOnScreenRatio * self.BaselineOnscreenDropDuration + self.notify.debug('OffscreenTime=%s' % self.OffscreenTime) + self.BaselineDropDuration = self.BaselineOnscreenDropDuration + self.OffscreenTime + self.MaxDropDuration = self.BaselineDropDuration + self.DropPeriod = self.BaselineDropDuration / 2.0 + scaledNumPlayers = (numPlayers - 1.0) * 0.75 + 1.0 + self.DropPeriod /= scaledNumPlayers + typeProbs = {'fruit': 3, + 'anvil': 1} + probSum = reduce(lambda x, y: x + y, typeProbs.values()) + for key in typeProbs.keys(): + typeProbs[key] = float(typeProbs[key]) / probSum + + scheduler = DropScheduler(CatchGameGlobals.GameDuration, self.FirstDropDelay, self.DropPeriod, self.MaxDropDuration, self.FasterDropDelay, self.FasterDropPeriodMult) + self.totalDrops = 0 + while not scheduler.doneDropping(): + scheduler.stepT() + self.totalDrops += 1 + + self.numFruits = int(self.totalDrops * typeProbs['fruit']) + self.numAnvils = int(self.totalDrops - self.numFruits) + + def getNumPlayers(self): + return self.numPlayers + + def defineConstants(self): + self.notify.debug('defineConstants') + self.DropPlacerType = RegionDropPlacer + fruits = {ToontownGlobals.ToontownCentral: 'apple', + ToontownGlobals.DonaldsDock: 'orange', + ToontownGlobals.DaisyGardens: 'pear', + ToontownGlobals.MinniesMelodyland: 'coconut', + ToontownGlobals.TheBrrrgh: 'watermelon', + ToontownGlobals.DonaldsDreamland: 'pineapple'} + self.fruitName = fruits[self.getSafezoneId()] + self.ShowObjSpheres = 0 + self.ShowToonSpheres = 0 + self.ShowSuitSpheres = 0 + self.PredictiveSmoothing = 1 + self.UseGravity = 1 + self.TrickShadows = 1 + self.WantSuits = 1 + self.FirstDropDelay = 0.5 + self.FasterDropDelay = int(2.0 / 3 * CatchGameGlobals.GameDuration) + self.notify.debug('will start dropping fast after %s seconds' % self.FasterDropDelay) + self.FasterDropPeriodMult = 0.5 + self.calcDifficultyConstants(self.getDifficulty(), self.getNumPlayers()) + self.notify.debug('ToonSpeed: %s' % self.ToonSpeed) + self.notify.debug('total drops: %s' % self.totalDrops) + self.notify.debug('numFruits: %s' % self.numFruits) + self.notify.debug('numAnvils: %s' % self.numAnvils) + self.ObjRadius = 1.0 + dropGridDimensions = [[5, 5], + [5, 5], + [6, 6], + [7, 7]] + self.DropRows, self.DropColumns = dropGridDimensions[self.getNumPlayers() - 1] + self.cameraPosTable = [[0, -29.36, 28.17]] * 2 + [[0, -32.87, 30.43], [0, -35.59, 32.1]] + self.cameraHpr = [0, -35, 0] + self.CameraPosHpr = self.cameraPosTable[self.getNumPlayers() - 1] + self.cameraHpr + for objType in DropObjectTypes: + self.notify.debug('*** Object Type: %s' % objType.name) + objType.onscreenDuration = objType.onscreenDurMult * self.BaselineOnscreenDropDuration + self.notify.debug('onscreenDuration=%s' % objType.onscreenDuration) + v_0 = 0.0 + t = objType.onscreenDuration + x_0 = self.MinOffscreenHeight + x = 0.0 + g = 2.0 * (x - x_0 - v_0 * t) / (t * t) + self.notify.debug('gravity=%s' % g) + objType.trajectory = Trajectory.Trajectory(0, Vec3(0, 0, x_0), Vec3(0, 0, v_0), gravMult=abs(g / Trajectory.Trajectory.gravity)) + objType.fallDuration = objType.onscreenDuration + self.OffscreenTime + + def grid2world(self, column, row): + x = column / float(self.DropColumns - 1) + y = row / float(self.DropRows - 1) + x = x * 2.0 - 1.0 + y = y * 2.0 - 1.0 + x *= self.StageHalfWidth + y *= self.StageHalfHeight + return (x, y) + + def showPosts(self): + self.hidePosts() + self.posts = [Toon.Toon(), + Toon.Toon(), + Toon.Toon(), + Toon.Toon()] + for i in xrange(len(self.posts)): + toon = self.posts[i] + toon.setDNA(base.localAvatar.getStyle()) + toon.reparentTo(render) + x = self.StageHalfWidth + y = self.StageHalfHeight + if i > 1: + x = -x + if i % 2: + y = -y + toon.setPos(x, y, 0) + + def hidePosts(self): + if hasattr(self, 'posts'): + for toon in self.posts: + toon.removeNode() + + del self.posts + + def showDropGrid(self): + self.hideDropGrid() + self.dropMarkers = [] + print 'dropRows: %s' % self.DropRows + print 'dropCols: %s' % self.DropColumns + for row in xrange(self.DropRows): + self.dropMarkers.append([]) + rowList = self.dropMarkers[row] + for column in xrange(self.DropColumns): + toon = Toon.Toon() + toon.setDNA(base.localAvatar.getStyle()) + toon.reparentTo(render) + toon.setScale(1.0 / 3) + x, y = self.grid2world(column, row) + toon.setPos(x, y, 0) + rowList.append(toon) + + def hideDropGrid(self): + if hasattr(self, 'dropMarkers'): + for row in self.dropMarkers: + for marker in row: + marker.removeNode() + + del self.dropMarkers + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.ground.reparentTo(render) + self.scorePanels = [] + camera.reparentTo(render) + camera.setPosHpr(*self.CameraPosHpr) + lt = base.localAvatar + lt.reparentTo(render) + self.__placeToon(self.localAvId) + lt.setSpeed(0, 0) + toonSD = self.toonSDs[self.localAvId] + toonSD.enter() + toonSD.fsm.request('normal') + self.orthoWalk.stop() + radius = 0.7 + handler = CollisionHandlerEvent() + handler.setInPattern('ltCatch%in') + self.ltLegsCollNode = CollisionNode('catchLegsCollNode') + self.ltLegsCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask) + self.ltHeadCollNode = CollisionNode('catchHeadCollNode') + self.ltHeadCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask) + self.ltLHandCollNode = CollisionNode('catchLHandCollNode') + self.ltLHandCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask) + self.ltRHandCollNode = CollisionNode('catchRHandCollNode') + self.ltRHandCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask) + legsCollNodepath = lt.attachNewNode(self.ltLegsCollNode) + legsCollNodepath.hide() + head = base.localAvatar.getHeadParts().getPath(2) + headCollNodepath = head.attachNewNode(self.ltHeadCollNode) + headCollNodepath.hide() + lHand = base.localAvatar.getLeftHands()[0] + lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode) + lHandCollNodepath.hide() + rHand = base.localAvatar.getRightHands()[0] + rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode) + rHandCollNodepath.hide() + lt.cTrav.addCollider(legsCollNodepath, handler) + lt.cTrav.addCollider(headCollNodepath, handler) + lt.cTrav.addCollider(lHandCollNodepath, handler) + lt.cTrav.addCollider(lHandCollNodepath, handler) + if self.ShowToonSpheres: + legsCollNodepath.show() + headCollNodepath.show() + lHandCollNodepath.show() + rHandCollNodepath.show() + self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius)) + self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius)) + self.ltLHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) + self.ltRHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) + self.toonCollNodes = [legsCollNodepath, + headCollNodepath, + lHandCollNodepath, + rHandCollNodepath] + if self.PredictiveSmoothing: + DistributedSmoothNode.activateSmoothing(1, 1) + self.introMovie.start() + + def offstage(self): + self.notify.debug('offstage') + DistributedSmoothNode.activateSmoothing(1, 0) + self.introMovie.finish() + for avId in self.toonSDs.keys(): + self.toonSDs[avId].exit() + + self.hidePosts() + self.hideDropGrid() + for collNode in self.toonCollNodes: + while collNode.node().getNumSolids(): + collNode.node().removeSolid(0) + + base.localAvatar.cTrav.removeCollider(collNode) + + del self.toonCollNodes + for panel in self.scorePanels: + panel.cleanup() + + del self.scorePanels + self.ground.reparentTo(hidden) + DistributedMinigame.offstage(self) + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + self.toonSDs[avId].exit(unexpectedExit=True) + del self.toonSDs[avId] + DistributedMinigame.handleDisabledAvatar(self, avId) + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + idx = self.avIdList.index(avId) + x = lineupPos(idx, self.numPlayers, 4.0) + toon.setPos(x, 0, 0) + toon.setHpr(180, 0, 0) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + headCollNP = base.localAvatar.find('**/catchHeadCollNode') + if headCollNP and not headCollNP.isEmpty(): + headCollNP.hide() + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toonSD = CatchGameToonSD.CatchGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.load() + toonSD.enter() + toonSD.fsm.request('normal') + toon.startSmooth() + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.introMovie.finish() + camera.reparentTo(render) + camera.setPosHpr(*self.CameraPosHpr) + self.gameFSM.request('play') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.orthoWalk.start() + for suit in self.suits: + suitCollSphere = CollisionSphere(0, 0, 0, 1.0) + suit.collSphereName = 'suitCollSphere%s' % self.suits.index(suit) + suitCollSphere.setTangible(0) + suitCollNode = CollisionNode(self.uniqueName(suit.collSphereName)) + suitCollNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + suitCollNode.addSolid(suitCollSphere) + suit.collNodePath = suit.attachNewNode(suitCollNode) + suit.collNodePath.hide() + if self.ShowSuitSpheres: + suit.collNodePath.show() + self.accept(self.uniqueName('enter' + suit.collSphereName), self.handleSuitCollision) + + self.scores = [0] * self.numPlayers + spacing = 0.4 + for i in xrange(self.numPlayers): + avId = self.avIdList[i] + avName = self.getAvatarName(avId) + scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) + scorePanel.setScale(0.9) + scorePanel.setPos(-0.583 - spacing * (self.numPlayers - 1 - i), 0.0, -0.15) + scorePanel.reparentTo(base.a2dTopRight) + scorePanel.makeTransparent(0.75) + self.scorePanels.append(scorePanel) + + self.fruitsCaught = 0 + self.droppedObjCaught = {} + self.dropIntervals = {} + self.droppedObjNames = [] + self.dropSchedule = [] + self.numItemsDropped = 0 + self.scheduleDrops() + self.startDropTask() + if self.WantSuits: + self.startSuitWalkTask() + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(CatchGameGlobals.GameDuration) + self.timer.countdown(CatchGameGlobals.GameDuration, self.timerExpired) + self.timer.setTransparency(1) + self.timer.setColorScale(1, 1, 1, 0.75) + base.playMusic(self.music, looping=0, volume=0.9) + + def exitPlay(self): + self.stopDropTask() + self.stopSuitWalkTask() + if hasattr(self, 'perfectIval'): + self.perfectIval.pause() + del self.perfectIval + self.timer.stop() + self.timer.destroy() + del self.timer + self.music.stop() + for suit in self.suits: + self.ignore(self.uniqueName('enter' + suit.collSphereName)) + suit.collNodePath.removeNode() + + for ival in self.dropIntervals.values(): + ival.finish() + + del self.dropIntervals + del self.droppedObjNames + del self.droppedObjCaught + del self.dropSchedule + taskMgr.remove(self.EndGameTaskName) + + def timerExpired(self): + pass + + def __handleCatch(self, objNum): + self.notify.debug('catch: %s' % objNum) + self.showCatch(self.localAvId, objNum) + objName = self.droppedObjNames[objNum] + objTypeId = CatchGameGlobals.Name2DOTypeId[objName] + self.sendUpdate('claimCatch', [objNum, objTypeId]) + self.finishDropInterval(objNum) + + def showCatch(self, avId, objNum): + isLocal = avId == self.localAvId + objName = self.droppedObjNames[objNum] + objType = Name2DropObjectType[objName] + if objType.good: + if objNum not in self.droppedObjCaught: + if isLocal: + base.playSfx(self.sndGoodCatch) + fruit = self.getObjModel(objName) + toon = self.getAvatar(avId) + rHand = toon.getRightHands()[0] + self.toonSDs[avId].eatFruit(fruit, rHand) + else: + self.toonSDs[avId].fsm.request('fallForward') + self.droppedObjCaught[objNum] = 1 + + def setObjectCaught(self, avId, objNum): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'play': + self.notify.warning('ignoring msg: object %s caught by %s' % (objNum, avId)) + return + isLocal = avId == self.localAvId + if not isLocal: + self.notify.debug('AI: avatar %s caught %s' % (avId, objNum)) + self.finishDropInterval(objNum) + self.showCatch(avId, objNum) + objName = self.droppedObjNames[objNum] + if Name2DropObjectType[objName].good: + i = self.avIdList.index(avId) + self.scores[i] += 1 + self.scorePanels[i].setScore(self.scores[i]) + self.fruitsCaught += 1 + + def finishDropInterval(self, objNum): + if objNum in self.dropIntervals: + self.dropIntervals[objNum].finish() + + def scheduleDrops(self): + self.droppedObjNames = [self.fruitName] * self.numFruits + ['anvil'] * self.numAnvils + self.randomNumGen.shuffle(self.droppedObjNames) + dropPlacer = self.DropPlacerType(self, self.getNumPlayers(), self.droppedObjNames) + while not dropPlacer.doneDropping(): + self.dropSchedule.append(dropPlacer.getNextDrop()) + + def startDropTask(self): + taskMgr.add(self.dropTask, self.DropTaskName) + + def stopDropTask(self): + taskMgr.remove(self.DropTaskName) + + def dropTask(self, task): + curT = self.getCurrentGameTime() + while self.dropSchedule[0][0] <= curT: + drop = self.dropSchedule[0] + self.dropSchedule = self.dropSchedule[1:] + dropTime, objName, dropCoords = drop + objNum = self.numItemsDropped + lastDrop = len(self.dropSchedule) == 0 + x, y = self.grid2world(*dropCoords) + dropIval = self.getDropIval(x, y, objName, objNum) + + def cleanup(self = self, objNum = objNum, lastDrop = lastDrop): + del self.dropIntervals[objNum] + if lastDrop: + self.sendUpdate('reportDone') + + dropIval.append(Func(cleanup)) + self.dropIntervals[objNum] = dropIval + self.numItemsDropped += 1 + dropIval.start(curT - dropTime) + if lastDrop: + return Task.done + + return Task.cont + + def setEveryoneDone(self): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'play': + self.notify.warning('ignoring setEveryoneDone msg') + return + self.notify.debug('setEveryoneDone') + + def endGame(task, self = self): + if not CatchGameGlobals.EndlessGame: + self.gameOver() + return Task.done + + self.notify.debug('num fruits: %s' % self.numFruits) + self.notify.debug('num catches: %s' % self.fruitsCaught) + self.timer.hide() + + if self.fruitsCaught >= self.numFruits: + self.notify.debug('perfect game!') + perfectTextSubnode = hidden.attachNewNode(self.__genText(TTLocalizer.CatchGamePerfect)) + perfectText = hidden.attachNewNode('perfectText') + perfectTextSubnode.reparentTo(perfectText) + frame = self.__textGen.getCardActual() + offsetY = -abs(frame[2] + frame[3]) / 2.0 + perfectTextSubnode.setPos(0, 0, offsetY) + perfectText.setColor(1, 0.1, 0.1, 1) + + def fadeFunc(t, text = perfectText): + text.setColorScale(1, 1, 1, t) + + def destroyText(text = perfectText): + text.removeNode() + + textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5), Func(endGame, None)) + soundTrack = SoundInterval(self.sndPerfect) + self.perfectIval = Parallel(textTrack, soundTrack) + self.perfectIval.start() + else: + taskMgr.doMethodLater(1, endGame, self.EndGameTaskName) + return + + def getDropIval(self, x, y, dropObjName, num): + objType = Name2DropObjectType[dropObjName] + dropNode = hidden.attachNewNode('catchDropNode%s' % num) + dropNode.setPos(x, y, 0) + shadow = self.dropShadow.copyTo(dropNode) + shadow.setZ(0.2) + shadow.setColor(1, 1, 1, 1) + object = self.getObjModel(dropObjName) + object.reparentTo(dropNode) + if dropObjName in ['watermelon', 'anvil']: + objH = object.getH() + absDelta = {'watermelon': 12, + 'anvil': 15}[dropObjName] + delta = (self.randomNumGen.random() * 2.0 - 1.0) * absDelta + newH = objH + delta + else: + newH = self.randomNumGen.random() * 360.0 + object.setH(newH) + sphereName = 'FallObj%s' % num + radius = self.ObjRadius + if objType.good: + radius *= lerp(1.0, 1.3, self.getDifficulty()) + collSphere = CollisionSphere(0, 0, 0, radius) + collSphere.setTangible(0) + collNode = CollisionNode(sphereName) + collNode.setCollideMask(ToontownGlobals.CatchGameBitmask) + collNode.addSolid(collSphere) + collNodePath = object.attachNewNode(collNode) + collNodePath.hide() + if self.ShowObjSpheres: + collNodePath.show() + catchEventName = 'ltCatch' + sphereName + + def eatCollEntry(forward, collEntry): + forward() + + self.accept(catchEventName, Functor(eatCollEntry, Functor(self.__handleCatch, num))) + + def cleanup(self = self, dropNode = dropNode, num = num, event = catchEventName): + self.ignore(event) + dropNode.removeNode() + + duration = objType.fallDuration + onscreenDuration = objType.onscreenDuration + dropHeight = self.MinOffscreenHeight + targetShadowScale = 0.3 + if self.TrickShadows: + intermedScale = targetShadowScale * (self.OffscreenTime / self.BaselineDropDuration) + shadowScaleIval = Sequence(LerpScaleInterval(shadow, self.OffscreenTime, intermedScale, startScale=0)) + shadowScaleIval.append(LerpScaleInterval(shadow, duration - self.OffscreenTime, targetShadowScale, startScale=intermedScale)) + else: + shadowScaleIval = LerpScaleInterval(shadow, duration, targetShadowScale, startScale=0) + targetShadowAlpha = 0.4 + shadowAlphaIval = LerpColorScaleInterval(shadow, self.OffscreenTime, Point4(1, 1, 1, targetShadowAlpha), startColorScale=Point4(1, 1, 1, 0)) + shadowIval = Parallel(shadowScaleIval, shadowAlphaIval) + if self.UseGravity: + + def setObjPos(t, objType = objType, object = object): + z = objType.trajectory.calcZ(t) + object.setZ(z) + + setObjPos(0) + dropIval = LerpFunctionInterval(setObjPos, fromData=0, toData=onscreenDuration, duration=onscreenDuration) + else: + startPos = Point3(0, 0, self.MinOffscreenHeight) + object.setPos(startPos) + dropIval = LerpPosInterval(object, onscreenDuration, Point3(0, 0, 0), startPos=startPos, blendType='easeIn') + ival = Sequence(Func(Functor(dropNode.reparentTo, render)), Parallel(Sequence(WaitInterval(self.OffscreenTime), dropIval), shadowIval), Func(cleanup), name='drop%s' % num) + landSound = None + if objType == Name2DropObjectType['anvil']: + landSound = self.sndAnvilLand + if landSound: + ival.append(SoundInterval(landSound)) + return ival + + def startSuitWalkTask(self): + ival = Parallel(name='catchGameMetaSuitWalk') + rng = RandomNumGen(self.randomNumGen) + delay = 0.0 + while delay < CatchGameGlobals.GameDuration: + delay += lerp(self.SuitPeriodRange[0], self.SuitPeriodRange[0], rng.random()) + walkIval = Sequence(name='catchGameSuitWalk') + walkIval.append(Wait(delay)) + + def pickY(self = self, rng = rng): + return lerp(-self.StageHalfHeight, self.StageHalfHeight, rng.random()) + + m = [2.5, + 2.5, + 2.3, + 2.1][self.getNumPlayers() - 1] + startPos = Point3(-(self.StageHalfWidth * m), pickY(), 0) + stopPos = Point3(self.StageHalfWidth * m, pickY(), 0) + if rng.choice([0, 1]): + startPos, stopPos = stopPos, startPos + walkIval.append(self.getSuitWalkIval(startPos, stopPos, rng)) + ival.append(walkIval) + + ival.start() + self.suitWalkIval = ival + + def stopSuitWalkTask(self): + self.suitWalkIval.finish() + del self.suitWalkIval + + def getSuitWalkIval(self, startPos, stopPos, rng): + data = {} + lerpNP = render.attachNewNode('catchGameSuitParent') + + def setup(self = self, startPos = startPos, stopPos = stopPos, data = data, lerpNP = lerpNP, rng = rng): + if len(self.suits) == 0: + return + suit = rng.choice(self.suits) + data['suit'] = suit + self.suits.remove(suit) + suit.reparentTo(lerpNP) + suit.loop('walk') + suit.setPlayRate(self.SuitSpeed / ToontownGlobals.SuitWalkSpeed, 'walk') + suit.setPos(0, 0, 0) + lerpNP.setPos(startPos) + suit.lookAt(stopPos) + + def cleanup(self = self, data = data, lerpNP = lerpNP): + if 'suit' in data: + suit = data['suit'] + suit.reparentTo(hidden) + self.suits.append(suit) + lerpNP.removeNode() + + distance = Vec3(stopPos - startPos).length() + duration = distance / self.SuitSpeed + ival = Sequence(FunctionInterval(setup), LerpPosInterval(lerpNP, duration, stopPos), FunctionInterval(cleanup)) + return ival + + def handleSuitCollision(self, collEntry): + self.toonSDs[self.localAvId].fsm.request('fallBack') + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.sendUpdate('hitBySuit', [self.localAvId, timestamp]) + + def hitBySuit(self, avId, timestamp): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'play': + self.notify.warning('ignoring msg: av %s hit by suit' % avId) + return + toon = self.getAvatar(avId) + if toon == None: + return + self.notify.debug('avatar %s hit by a suit' % avId) + if avId != self.localAvId: + self.toonSDs[avId].fsm.request('fallBack') + return + + def enterCleanup(self): + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass + + def initOrthoWalk(self): + self.notify.debug('startOrthoWalk') + + def doCollisions(oldPos, newPos, self = self): + x = bound(newPos[0], self.StageHalfWidth, -self.StageHalfWidth) + y = bound(newPos[1], self.StageHalfHeight, -self.StageHalfHeight) + newPos.setX(x) + newPos.setY(y) + return newPos + + orthoDrive = OrthoDrive(self.ToonSpeed, customCollisionCallback=doCollisions) + self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) + + def destroyOrthoWalk(self): + self.notify.debug('destroyOrthoWalk') + self.orthoWalk.destroy() + del self.orthoWalk + + def getIntroMovie(self): + locNode = self.ground.find('**/locator_tree') + treeNode = locNode.attachNewNode('treeNode') + treeNode.setHpr(render, 0, 0, 0) + + def cleanupTree(treeNode = treeNode): + treeNode.removeNode() + + initialCamPosHpr = (-0.21, + -19.56, + 13.94, + 0.0, + 26.57, + 0.0) + suitViewCamPosHpr = (0, -11.5, 13, 0, -35, 0) + finalCamPosHpr = self.CameraPosHpr + cameraIval = Sequence(Func(camera.reparentTo, render), Func(camera.setPosHpr, treeNode, *initialCamPosHpr), WaitInterval(4.0), LerpPosHprInterval(camera, 2.0, Point3(*suitViewCamPosHpr[:3]), Point3(*suitViewCamPosHpr[3:]), blendType='easeInOut', name='lerpToSuitView'), WaitInterval(4.0), LerpPosHprInterval(camera, 3.0, Point3(*finalCamPosHpr[:3]), Point3(*finalCamPosHpr[3:]), blendType='easeInOut', name='lerpToPlayView')) + + def getIntroToon(toonProperties, parent, pos): + toon = Toon.Toon() + dna = ToonDNA.ToonDNA() + dna.newToonFromProperties(*toonProperties) + toon.setDNA(dna) + toon.reparentTo(parent) + toon.setPos(*pos) + toon.setH(180) + toon.startBlink() + return toon + + def cleanupIntroToon(toon): + toon.detachNode() + toon.stopBlink() + toon.delete() + + def getThrowIval(toon, hand, object, leftToon, isAnvil = 0): + anim = 'catch-intro-throw' + grabFrame = 12 + fullSizeFrame = 30 + framePeriod = 1.0 / toon.getFrameRate(anim) + objScaleDur = (fullSizeFrame - grabFrame) * framePeriod + releaseFrame = 35 + trajDuration = 1.6 + trajDistance = 4 + if leftToon: + releaseFrame = 34 + trajDuration = 1.0 + trajDistance = 1 + animIval = ActorInterval(toon, anim, loop=0) + + def getThrowDest(object = object, offset = trajDistance): + dest = object.getPos(render) + dest += Point3(0, -offset, 0) + dest.setZ(0) + return dest + + if leftToon: + trajIval = ProjectileInterval(object, startVel=Point3(0, 0, 0), duration=trajDuration) + else: + trajIval = ProjectileInterval(object, endPos=getThrowDest, duration=trajDuration) + trajIval = Sequence(Func(object.wrtReparentTo, render), trajIval, Func(object.wrtReparentTo, hidden)) + if isAnvil: + trajIval.append(SoundInterval(self.sndAnvilLand)) + objIval = Track((grabFrame * framePeriod, Sequence(Func(object.reparentTo, hand), Func(object.setPosHpr, 0.05, -.13, 0.62, 0, 0, 336.8), LerpScaleInterval(object, objScaleDur, 1.0, startScale=0.1, blendType='easeInOut'))), (releaseFrame * framePeriod, trajIval)) + + def cleanup(object = object): + object.reparentTo(hidden) + object.removeNode() + + throwIval = Sequence(Parallel(animIval, objIval), Func(cleanup)) + return throwIval + + tY = -4.0 + tZ = 19.5 + props = ['css', + 'md', + 'm', + 'f', + 9, + 0, + 9, + 9, + 13, + 5, + 11, + 5, + 8, + 7] + leftToon = getIntroToon(props, treeNode, [-2.3, tY, tZ]) + props = ['mss', + 'ls', + 'l', + 'm', + 6, + 0, + 6, + 6, + 3, + 5, + 3, + 5, + 5, + 0] + rightToon = getIntroToon(props, treeNode, [1.8, tY, tZ]) + fruit = self.getObjModel(self.fruitName) + if self.fruitName == 'pineapple': + fruit.setZ(0.42) + fruit.flattenMedium() + anvil = self.getObjModel('anvil') + anvil.setH(100) + anvil.setZ(0.42) + anvil.flattenMedium() + leftToonIval = getThrowIval(leftToon, leftToon.getRightHands()[0], fruit, leftToon=1) + rightToonIval = getThrowIval(rightToon, rightToon.getLeftHands()[0], anvil, leftToon=0, isAnvil=1) + animDur = leftToon.getNumFrames('catch-intro-throw') / leftToon.getFrameRate('catch-intro-throw') + toonIval = Sequence(Parallel(Sequence(leftToonIval, Func(leftToon.loop, 'neutral')), Sequence(Func(rightToon.loop, 'neutral'), WaitInterval(animDur / 2.0), rightToonIval, Func(rightToon.loop, 'neutral')), WaitInterval(cameraIval.getDuration())), Func(cleanupIntroToon, leftToon), Func(cleanupIntroToon, rightToon)) + self.treeNode = treeNode + self.fruit = fruit + self.anvil = anvil + self.leftToon = leftToon + self.rightToon = rightToon + introMovie = Sequence(Parallel(cameraIval, toonIval), Func(cleanupTree)) + return introMovie diff --git a/toontown/minigame/DistributedCatchGameAI.py b/toontown/minigame/DistributedCatchGameAI.py new file mode 100755 index 00000000..704a8a7c --- /dev/null +++ b/toontown/minigame/DistributedCatchGameAI.py @@ -0,0 +1,121 @@ +from DistributedMinigameAI import * +from toontown.ai.ToonBarrier import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import CatchGameGlobals +import MinigameGlobals + +class DistributedCatchGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedCatchGameAI_initialized + except: + self.DistributedCatchGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedCatchGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.notify.debug('fruits: %s, fruits caught: %s' % (self.numFruits, self.fruitsCaught)) + perfect = self.fruitsCaught >= self.numFruits + for avId in self.avIdList: + self.scoreDict[avId] = max(1, int(self.scoreDict[avId] / 2)) + if perfect: + self.notify.debug('PERFECT GAME!') + self.scoreDict[avId] += round(self.numFruits / 4.0) + self.logAllPerfect() + + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.caughtList = [0] * 100 + table = CatchGameGlobals.NumFruits[self.numPlayers - 1] + self.numFruits = table[self.getSafezoneId()] + self.notify.debug('numFruits: %s' % self.numFruits) + self.fruitsCaught = 0 + + def allToonsDone(self = self): + self.notify.debug('allToonsDone') + self.sendUpdate('setEveryoneDone') + if not CatchGameGlobals.EndlessGame: + self.gameOver() + + def handleTimeout(avIds, self = self): + self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds) + self.setGameAbort() + + self.doneBarrier = ToonBarrier('waitClientsDone', self.uniqueName('waitClientsDone'), self.avIdList, CatchGameGlobals.GameDuration + MinigameGlobals.latencyTolerance, allToonsDone, handleTimeout) + + def exitPlay(self): + del self.caughtList + self.doneBarrier.cleanup() + del self.doneBarrier + + def claimCatch(self, objNum, DropObjTypeId): + if self.gameFSM.getCurrentState().getName() != 'play': + return + if DropObjTypeId < 0 or DropObjTypeId >= len(CatchGameGlobals.DOTypeId2Name): + self.air.writeServerEvent('warning', DropObjTypeId, 'CatchGameAI.claimCatch DropObjTypeId out of range') + return + if objNum < 0 or objNum > 5000 or objNum >= 2 * len(self.caughtList): + self.air.writeServerEvent('warning', objNum, 'CatchGameAI.claimCatch objNum is too high or negative') + return + if objNum >= len(self.caughtList): + self.caughtList += [0] * len(self.caughtList) + if not self.caughtList[objNum]: + self.caughtList[objNum] = 1 + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('setObjectCaught', [avId, objNum]) + objName = CatchGameGlobals.DOTypeId2Name[DropObjTypeId] + self.notify.debug('avatar %s caught object %s: %s' % (avId, objNum, objName)) + if CatchGameGlobals.Name2DropObjectType[objName].good: + self.scoreDict[avId] += 1 + self.fruitsCaught += 1 + + def reportDone(self): + if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'play': + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('reportDone: avatar %s is done' % avId) + self.doneBarrier.clear(avId) + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedCogThiefGame.py b/toontown/minigame/DistributedCogThiefGame.py new file mode 100755 index 00000000..720276b8 --- /dev/null +++ b/toontown/minigame/DistributedCogThiefGame.py @@ -0,0 +1,907 @@ +from pandac.PandaModules import Point3, CollisionSphere, CollisionNode, CollisionHandlerEvent, NodePath, TextNode +from direct.distributed.ClockDelta import globalClockDelta +from direct.interval.IntervalGlobal import Wait, LerpFunctionInterval, LerpHprInterval, Sequence, Parallel, Func, SoundInterval, ActorInterval, ProjectileInterval, Track, LerpScaleInterval, WaitInterval, LerpPosHprInterval +from direct.gui.DirectGui import DirectLabel +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.showbase import RandomNumGen +from direct.task import Task +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownTimer +from toontown.minigame import CogThiefGameToonSD +from toontown.minigame.OrthoDrive import OrthoDrive +from toontown.minigame.OrthoWalk import OrthoWalk +from toontown.minigame import CogThiefGameGlobals +from toontown.minigame import CogThief +from toontown.minigame.DistributedMinigame import DistributedMinigame +from toontown.minigame import Trajectory +from toontown.minigame import MinigameGlobals +from toontown.minigame import CogThiefWalk +CTGG = CogThiefGameGlobals + +class DistributedCogThiefGame(DistributedMinigame): + notify = directNotify.newCategory('DistributedCogThiefGame') + ToonSpeed = CTGG.ToonSpeed + StageHalfWidth = 200.0 + StageHalfHeight = 100.0 + BarrelScale = 0.25 + TOON_Z = 0 + UPDATE_SUITS_TASK = 'CogThiefGameUpdateSuitsTask' + REWARD_COUNTDOWN_TASK = 'cogThiefGameRewardCountdown' + ControlKeyLimitTime = 1.0 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedCogThiefGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.cameraTopView = (0, 0, 55, 0, -90.0, 0) + self.barrels = [] + self.cogInfo = {} + self.lastTimeControlPressed = 0 + self.stolenBarrels = [] + self.useOrthoWalk = base.config.GetBool('cog-thief-ortho', 1) + self.resultIval = None + self.gameIsEnding = False + self.__textGen = TextNode('cogThiefGame') + self.__textGen.setFont(ToontownGlobals.getSignFont()) + self.__textGen.setAlign(TextNode.ACenter) + return + + def getTitle(self): + return TTLocalizer.CogThiefGameTitle + + def getInstructions(self): + return TTLocalizer.CogThiefGameInstructions + + def getMaxDuration(self): + return 0 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.music = base.loadMusic('phase_4/audio/bgm/MG_CogThief.ogg') + self.initCogInfo() + for barrelIndex in xrange(CTGG.NumBarrels): + barrel = loader.loadModel('phase_4/models/minigames/cogthief_game_gagTank') + barrel.setPos(CTGG.BarrelStartingPositions[barrelIndex]) + barrel.setScale(self.BarrelScale) + barrel.reparentTo(render) + barrel.setTag('barrelIndex', str(barrelIndex)) + collSphere = CollisionSphere(0, 0, 0, 4) + collSphere.setTangible(0) + name = 'BarrelSphere-%d' % barrelIndex + collSphereName = self.uniqueName(name) + collNode = CollisionNode(collSphereName) + collNode.setFromCollideMask(CTGG.BarrelBitmask) + collNode.addSolid(collSphere) + colNp = barrel.attachNewNode(collNode) + handler = CollisionHandlerEvent() + handler.setInPattern('barrelHit-%fn') + base.cTrav.addCollider(colNp, handler) + self.accept('barrelHit-' + collSphereName, self.handleEnterBarrel) + nodeToHide = '**/gagMoneyTen' + if barrelIndex % 2: + nodeToHide = '**/gagMoneyFive' + iconToHide = barrel.find(nodeToHide) + if not iconToHide.isEmpty(): + iconToHide.hide() + self.barrels.append(barrel) + + self.gameBoard = loader.loadModel('phase_4/models/minigames/cogthief_game') + self.gameBoard.find('**/floor_TT').hide() + self.gameBoard.find('**/floor_DD').hide() + self.gameBoard.find('**/floor_DG').hide() + self.gameBoard.find('**/floor_MM').hide() + self.gameBoard.find('**/floor_BR').hide() + self.gameBoard.find('**/floor_DL').hide() + zone = self.getSafezoneId() + if zone == ToontownGlobals.ToontownCentral: + self.gameBoard.find('**/floor_TT').show() + elif zone == ToontownGlobals.DonaldsDock: + self.gameBoard.find('**/floor_DD').show() + elif zone == ToontownGlobals.DaisyGardens: + self.gameBoard.find('**/floor_DG').show() + elif zone == ToontownGlobals.MinniesMelodyland: + self.gameBoard.find('**/floor_MM').show() + elif zone == ToontownGlobals.TheBrrrgh: + self.gameBoard.find('**/floor_BR').show() + elif zone == ToontownGlobals.DonaldsDreamland: + self.gameBoard.find('**/floor_DL').show() + else: + self.gameBoard.find('**/floor_TT').show() + self.gameBoard.setPosHpr(0, 0, 0, 0, 0, 0) + self.gameBoard.setScale(1.0) + self.toonSDs = {} + avId = self.localAvId + toonSD = CogThiefGameToonSD.CogThiefGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.load() + self.loadCogs() + self.toonHitTracks = {} + self.toonPieTracks = {} + self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg') + self.sndRewardTick = base.loadSfx('phase_3.5/audio/sfx/tick_counter.ogg') + self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.ogg') + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.hide() + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + self.jarImage = purchaseModels.find('**/Jar') + self.jarImage.reparentTo(hidden) + self.rewardPanel = DirectLabel(parent=hidden, relief=None, pos=(-0.173, 0.0, -0.55), scale=0.65, text='', text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_pos=(0, -.13), text_font=ToontownGlobals.getSignFont(), image=self.jarImage) + self.rewardPanelTitle = DirectLabel(parent=self.rewardPanel, relief=None, pos=(0, 0, 0.06), scale=0.08, text=TTLocalizer.CannonGameReward, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1)) + return + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + del self.music + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + self.gameBoard.removeNode() + del self.gameBoard + for barrel in self.barrels: + barrel.removeNode() + + del self.barrels + for avId in self.toonSDs.keys(): + toonSD = self.toonSDs[avId] + toonSD.unload() + + del self.toonSDs + self.timer.destroy() + del self.timer + self.rewardPanel.destroy() + del self.rewardPanel + self.jarImage.removeNode() + del self.jarImage + del self.sndRewardTick + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.gameBoard.reparentTo(render) + lt = base.localAvatar + lt.reparentTo(render) + self.__placeToon(self.localAvId) + lt.setSpeed(0, 0) + self.moveCameraToTop() + toonSD = self.toonSDs[self.localAvId] + toonSD.enter() + toonSD.fsm.request('normal') + self.stopGameWalk() + for cogIndex in xrange(self.getNumCogs()): + suit = self.cogInfo[cogIndex]['suit'].suit + pos = self.cogInfo[cogIndex]['pos'] + suit.reparentTo(self.gameBoard) + suit.setPos(pos) + suit.nametag3d.stash() + suit.nametag.destroy() + + for avId in self.avIdList: + self.toonHitTracks[avId] = Wait(0.1) + + self.toonRNGs = [] + for i in xrange(self.numPlayers): + self.toonRNGs.append(RandomNumGen.RandomNumGen(self.randomNumGen)) + + self.sndTable = {'hitBySuit': [None] * self.numPlayers, + 'falling': [None] * self.numPlayers} + for i in xrange(self.numPlayers): + self.sndTable['hitBySuit'][i] = base.loadSfx('phase_4/audio/sfx/MG_Tag_C.ogg') + self.sndTable['falling'][i] = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg') + + base.playMusic(self.music, looping=1, volume=0.8) + self.introTrack = self.getIntroTrack() + self.introTrack.start() + return + + def offstage(self): + self.notify.debug('offstage') + self.gameBoard.hide() + self.music.stop() + for barrel in self.barrels: + barrel.hide() + + for avId in self.toonSDs.keys(): + self.toonSDs[avId].exit() + + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.resetLOD() + + self.timer.reparentTo(hidden) + self.rewardPanel.reparentTo(hidden) + if self.introTrack.isPlaying(): + self.introTrack.finish() + del self.introTrack + DistributedMinigame.offstage(self) + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + self.toonSDs[avId].exit(unexpectedExit=True) + del self.toonSDs[avId] + DistributedMinigame.handleDisabledAvatar(self, avId) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toon.useLOD(1000) + toonSD = CogThiefGameToonSD.CogThiefGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.load() + toonSD.enter() + toonSD.fsm.request('normal') + toon.startSmooth() + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + if not base.config.GetBool('cog-thief-endless', 0): + self.timer.show() + self.timer.countdown(CTGG.GameTime, self.__gameTimerExpired) + self.clockStopTime = None + self.rewardPanel.reparentTo(base.a2dTopRight) + self.scoreMult = MinigameGlobals.getScoreMult(self.cr.playGame.hood.id) + self.__startRewardCountdown() + if self.introTrack.isPlaying(): + self.introTrack.finish() + self.gameFSM.request('play') + return + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.startGameWalk() + self.spawnUpdateSuitsTask() + self.accept('control', self.controlKeyPressed) + self.pieHandler = CollisionHandlerEvent() + self.pieHandler.setInPattern('pieHit-%fn') + + def exitPlay(self): + self.ignore('control') + if self.resultIval and self.resultIval.isPlaying(): + self.resultIval.finish() + self.resultIval = None + return + + def enterCleanup(self): + self.__killRewardCountdown() + if hasattr(self, 'jarIval'): + self.jarIval.finish() + del self.jarIval + for key in self.toonHitTracks: + ival = self.toonHitTracks[key] + if ival.isPlaying(): + ival.finish() + + self.toonHitTracks = {} + for key in self.toonPieTracks: + ival = self.toonPieTracks[key] + if ival.isPlaying(): + ival.finish() + + self.toonPieTracks = {} + for key in self.cogInfo: + cogThief = self.cogInfo[key]['suit'] + cogThief.cleanup() + + self.removeUpdateSuitsTask() + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + if toon: + index = self.avIdList.index(avId) + toon.setPos(CTGG.ToonStartingPositions[index]) + toon.setHpr(0, 0, 0) + + def moveCameraToTop(self): + camera.reparentTo(render) + p = self.cameraTopView + camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5]) + base.camLens.setMinFov(46/(4./3.)) + camera.setZ(camera.getZ() + base.config.GetFloat('cog-thief-z-camera-adjust', 0.0)) + + def destroyGameWalk(self): + self.notify.debug('destroyOrthoWalk') + if self.useOrthoWalk: + self.gameWalk.destroy() + del self.gameWalk + else: + self.notify.debug('TODO destroyGameWalk') + + def initGameWalk(self): + self.notify.debug('startOrthoWalk') + if self.useOrthoWalk: + + def doCollisions(oldPos, newPos, self = self): + x = bound(newPos[0], CTGG.StageHalfWidth, -CTGG.StageHalfWidth) + y = bound(newPos[1], CTGG.StageHalfHeight, -CTGG.StageHalfHeight) + newPos.setX(x) + newPos.setY(y) + return newPos + + orthoDrive = OrthoDrive(self.ToonSpeed, customCollisionCallback=doCollisions, instantTurn=True) + self.gameWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) + else: + self.gameWalk = CogThiefWalk.CogThiefWalk('walkDone') + forwardSpeed = self.ToonSpeed / 2.0 + base.mouseInterfaceNode.setForwardSpeed(forwardSpeed) + multiplier = forwardSpeed / ToontownGlobals.ToonForwardSpeed + base.mouseInterfaceNode.setRotateSpeed(ToontownGlobals.ToonRotateSpeed * 4) + + def initCogInfo(self): + for cogIndex in xrange(self.getNumCogs()): + self.cogInfo[cogIndex] = {'pos': Point3(CTGG.CogStartingPositions[cogIndex]), + 'goal': CTGG.NoGoal, + 'goalId': CTGG.InvalidGoalId, + 'suit': None} + + return + + def loadCogs(self): + suitTypes = ['ds', + 'ac', + 'bc', + 'ms'] + for suitIndex in xrange(self.getNumCogs()): + st = self.randomNumGen.choice(suitTypes) + suit = CogThief.CogThief(suitIndex, st, self, self.getCogSpeed()) + self.cogInfo[suitIndex]['suit'] = suit + + def handleEnterSphere(self, colEntry): + if self.gameIsEnding: + return + intoName = colEntry.getIntoNodePath().getName() + fromName = colEntry.getFromNodePath().getName() + debugInto = intoName.split('/') + debugFrom = fromName.split('/') + self.notify.debug('handleEnterSphere gametime=%s %s into %s' % (self.getCurrentGameTime(), debugFrom[-1], debugInto[-1])) + intoName = colEntry.getIntoNodePath().getName() + if 'CogThiefSphere' in intoName: + parts = intoName.split('-') + suitNum = int(parts[1]) + self.localToonHitBySuit(suitNum) + + def localToonHitBySuit(self, suitNum): + self.notify.debug('localToonHitBySuit %d' % suitNum) + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + pos = self.cogInfo[suitNum]['suit'].suit.getPos() + self.sendUpdate('hitBySuit', [self.localAvId, + timestamp, + suitNum, + pos[0], + pos[1], + pos[2]]) + self.showToonHitBySuit(self.localAvId, timestamp) + self.makeSuitRespondToToonHit(timestamp, suitNum) + + def hitBySuit(self, avId, timestamp, suitNum, x, y, z): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() not in ['play']: + self.notify.warning('ignoring msg: av %s hit by suit' % avId) + return + if self.gameIsEnding: + return + self.notify.debug('avatar ' + `avId` + ' hit by a suit') + if avId != self.localAvId: + self.showToonHitBySuit(avId, timestamp) + self.makeSuitRespondToToonHit(timestamp, suitNum) + + def showToonHitBySuit(self, avId, timestamp): + toon = self.getAvatar(avId) + if toon == None: + return + rng = self.toonRNGs[self.avIdList.index(avId)] + curPos = toon.getPos(render) + oldTrack = self.toonHitTracks[avId] + if oldTrack.isPlaying(): + oldTrack.finish() + toon.setPos(curPos) + toon.setZ(self.TOON_Z) + parentNode = render.attachNewNode('mazeFlyToonParent-' + `avId`) + parentNode.setPos(toon.getPos()) + toon.reparentTo(parentNode) + toon.setPos(0, 0, 0) + startPos = parentNode.getPos() + dropShadow = toon.dropShadow.copyTo(parentNode) + dropShadow.setScale(toon.dropShadow.getScale(render)) + trajectory = Trajectory.Trajectory(0, Point3(0, 0, 0), Point3(0, 0, 50), gravMult=1.0) + oldFlyDur = trajectory.calcTimeOfImpactOnPlane(0.0) + trajectory = Trajectory.Trajectory(0, Point3(0, 0, 0), Point3(0, 0, 50), gravMult=0.55) + flyDur = trajectory.calcTimeOfImpactOnPlane(0.0) + avIndex = self.avIdList.index(avId) + endPos = CTGG.ToonStartingPositions[avIndex] + + def flyFunc(t, trajectory, startPos = startPos, endPos = endPos, dur = flyDur, moveNode = parentNode, flyNode = toon): + u = t / dur + moveNode.setX(startPos[0] + u * (endPos[0] - startPos[0])) + moveNode.setY(startPos[1] + u * (endPos[1] - startPos[1])) + flyNode.setPos(trajectory.getPos(t)) + + flyTrack = Sequence(LerpFunctionInterval(flyFunc, fromData=0.0, toData=flyDur, duration=flyDur, extraArgs=[trajectory]), name=toon.uniqueName('hitBySuit-fly')) + geomNode = toon.getGeomNode() + startHpr = geomNode.getHpr() + destHpr = Point3(startHpr) + hRot = rng.randrange(1, 8) + if rng.choice([0, 1]): + hRot = -hRot + destHpr.setX(destHpr[0] + hRot * 360) + spinHTrack = Sequence(LerpHprInterval(geomNode, flyDur, destHpr, startHpr=startHpr), Func(geomNode.setHpr, startHpr), name=toon.uniqueName('hitBySuit-spinH')) + parent = geomNode.getParent() + rotNode = parent.attachNewNode('rotNode') + geomNode.reparentTo(rotNode) + rotNode.setZ(toon.getHeight() / 2.0) + oldGeomNodeZ = geomNode.getZ() + geomNode.setZ(-toon.getHeight() / 2.0) + startHpr = rotNode.getHpr() + destHpr = Point3(startHpr) + pRot = rng.randrange(1, 3) + if rng.choice([0, 1]): + pRot = -pRot + destHpr.setY(destHpr[1] + pRot * 360) + spinPTrack = Sequence(LerpHprInterval(rotNode, flyDur, destHpr, startHpr=startHpr), Func(rotNode.setHpr, startHpr), name=toon.uniqueName('hitBySuit-spinP')) + i = self.avIdList.index(avId) + soundTrack = Sequence(Func(base.playSfx, self.sndTable['hitBySuit'][i]), Wait(flyDur * (2.0 / 3.0)), SoundInterval(self.sndTable['falling'][i], duration=flyDur * (1.0 / 3.0)), name=toon.uniqueName('hitBySuit-soundTrack')) + + def preFunc(self = self, avId = avId, toon = toon, dropShadow = dropShadow): + forwardSpeed = toon.forwardSpeed + rotateSpeed = toon.rotateSpeed + if avId == self.localAvId: + self.stopGameWalk() + else: + toon.stopSmooth() + if forwardSpeed or rotateSpeed: + toon.setSpeed(forwardSpeed, rotateSpeed) + toon.dropShadow.hide() + + def postFunc(self = self, avId = avId, oldGeomNodeZ = oldGeomNodeZ, dropShadow = dropShadow, parentNode = parentNode): + if avId == self.localAvId: + base.localAvatar.setPos(endPos) + if hasattr(self, 'gameWalk'): + toon = base.localAvatar + toon.setSpeed(0, 0) + self.startGameWalk() + dropShadow.removeNode() + del dropShadow + toon = self.getAvatar(avId) + if toon: + toon.dropShadow.show() + geomNode = toon.getGeomNode() + rotNode = geomNode.getParent() + baseNode = rotNode.getParent() + geomNode.reparentTo(baseNode) + rotNode.removeNode() + del rotNode + geomNode.setZ(oldGeomNodeZ) + if toon: + toon.reparentTo(render) + toon.setPos(endPos) + parentNode.removeNode() + del parentNode + if avId != self.localAvId: + if toon: + toon.startSmooth() + + preFunc() + slipBack = Parallel(Sequence(ActorInterval(toon, 'slip-backward', endFrame=24), Wait(CTGG.LyingDownDuration - (flyDur - oldFlyDur)), ActorInterval(toon, 'slip-backward', startFrame=24))) + if toon.doId == self.localAvId: + slipBack.append(SoundInterval(self.sndOof)) + hitTrack = Sequence(Parallel(flyTrack, spinHTrack, spinPTrack, soundTrack), slipBack, Func(postFunc), name=toon.uniqueName('hitBySuit')) + self.notify.debug('hitTrack duration = %s' % hitTrack.getDuration()) + self.toonHitTracks[avId] = hitTrack + hitTrack.start(globalClockDelta.localElapsedTime(timestamp)) + return + + def updateSuitGoal(self, timestamp, inResponseToClientStamp, suitNum, goalType, goalId, x, y, z): + if not self.hasLocalToon: + return + self.notify.debug('updateSuitGoal gameTime=%s timeStamp=%s cog=%s goal=%s goalId=%s (%.1f, %.1f,%.1f)' % (self.getCurrentGameTime(), + timestamp, + suitNum, + CTGG.GoalStr[goalType], + goalId, + x, + y, + z)) + cog = self.cogInfo[suitNum] + cog['goal'] = goalType + cog['goalId'] = goalId + newPos = Point3(x, y, z) + cog['pos'] = newPos + suit = cog['suit'] + suit.updateGoal(timestamp, inResponseToClientStamp, goalType, goalId, newPos) + + def spawnUpdateSuitsTask(self): + self.notify.debug('spawnUpdateSuitsTask') + for cogIndex in self.cogInfo: + suit = self.cogInfo[cogIndex]['suit'] + suit.gameStart(self.gameStartTime) + + taskMgr.remove(self.UPDATE_SUITS_TASK) + taskMgr.add(self.updateSuitsTask, self.UPDATE_SUITS_TASK) + + def removeUpdateSuitsTask(self): + taskMgr.remove(self.UPDATE_SUITS_TASK) + + def updateSuitsTask(self, task): + if self.gameIsEnding: + return task.done + for cogIndex in self.cogInfo: + suit = self.cogInfo[cogIndex]['suit'] + suit.think() + + return task.cont + + def makeSuitRespondToToonHit(self, timestamp, suitNum): + cog = self.cogInfo[suitNum]['suit'] + cog.respondToToonHit(timestamp) + + def handleEnterBarrel(self, colEntry): + if self.gameIsEnding: + return + intoName = colEntry.getIntoNodePath().getName() + fromName = colEntry.getFromNodePath().getName() + debugInto = intoName.split('/') + debugFrom = fromName.split('/') + self.notify.debug('handleEnterBarrel gameTime=%s %s into %s' % (self.getCurrentGameTime(), debugFrom[-1], debugInto[-1])) + if 'CogThiefSphere' in intoName: + parts = intoName.split('-') + cogIndex = int(parts[1]) + barrelName = colEntry.getFromNodePath().getName() + barrelParts = barrelName.split('-') + barrelIndex = int(barrelParts[1]) + cog = self.cogInfo[cogIndex]['suit'] + if cog.barrel == CTGG.NoBarrelCarried and barrelIndex not in self.stolenBarrels: + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + if cog.suit: + cogPos = cog.suit.getPos() + collisionPos = colEntry.getContactPos(render) + self.sendUpdate('cogHitBarrel', [timestamp, + cogIndex, + barrelIndex, + cogPos[0], + cogPos[1], + cogPos[2]]) + + def makeCogCarryBarrel(self, timestamp, inResponseToClientStamp, cogIndex, barrelIndex, x, y, z): + if not self.hasLocalToon: + return + if self.gameIsEnding: + return + self.notify.debug('makeCogCarryBarrel gameTime=%s timeStamp=%s cog=%s barrel=%s (%.1f, %.1f,%.1f)' % (self.getCurrentGameTime(), + timestamp, + cogIndex, + barrelIndex, + x, + y, + z)) + barrel = self.barrels[barrelIndex] + self.notify.debug('barrelPos= %s' % barrel.getPos()) + cog = self.cogInfo[cogIndex]['suit'] + cogPos = Point3(x, y, z) + cog.makeCogCarryBarrel(timestamp, inResponseToClientStamp, barrel, barrelIndex, cogPos) + + def makeCogDropBarrel(self, timestamp, inResponseToClientStamp, cogIndex, barrelIndex, x, y, z): + if not self.hasLocalToon: + return + self.notify.debug('makeCogDropBarrel gameTime=%s timeStamp=%s cog=%s barrel=%s (%.1f, %.1f,%.1f)' % (self.getCurrentGameTime(), + timestamp, + cogIndex, + barrelIndex, + x, + y, + z)) + barrel = self.barrels[barrelIndex] + self.notify.debug('barrelPos= %s' % barrel.getPos()) + cog = self.cogInfo[cogIndex]['suit'] + cogPos = Point3(x, y, z) + cog.makeCogDropBarrel(timestamp, inResponseToClientStamp, barrel, barrelIndex, cogPos) + + def controlKeyPressed(self): + if self.isToonPlayingHitTrack(self.localAvId): + return + if self.gameIsEnding: + return + if self.getCurrentGameTime() - self.lastTimeControlPressed > self.ControlKeyLimitTime: + self.lastTimeControlPressed = self.getCurrentGameTime() + self.notify.debug('controlKeyPressed') + toonSD = self.toonSDs[self.localAvId] + curState = toonSD.fsm.getCurrentState().getName() + toon = self.getAvatar(self.localAvId) + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + pos = toon.getPos() + heading = toon.getH() + self.sendUpdate('throwingPie', [self.localAvId, + timestamp, + heading, + pos[0], + pos[1], + pos[2]]) + self.showToonThrowingPie(self.localAvId, timestamp, heading, pos) + + def throwingPie(self, avId, timestamp, heading, x, y, z): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() not in ['play']: + self.notify.warning('ignoring msg: av %s hit by suit' % avId) + return + self.notify.debug('avatar ' + `avId` + ' throwing pie') + if avId != self.localAvId: + pos = Point3(x, y, z) + self.showToonThrowingPie(avId, timestamp, heading, pos) + + def showToonThrowingPie(self, avId, timestamp, heading, pos): + toon = self.getAvatar(avId) + if toon: + tossTrack, pieTrack, flyPie = self.getTossPieInterval(toon, pos[0], pos[1], pos[2], heading, 0, 0, 0) + + def removePieFromTraverser(flyPie = flyPie): + if base.cTrav: + if flyPie: + base.cTrav.removeCollider(flyPie) + + if avId == self.localAvId: + flyPie.setTag('throwerId', str(avId)) + collSphere = CollisionSphere(0, 0, 0, 0.5) + collSphere.setTangible(0) + name = 'PieSphere-%d' % avId + collSphereName = self.uniqueName(name) + collNode = CollisionNode(collSphereName) + collNode.setFromCollideMask(ToontownGlobals.PieBitmask) + collNode.addSolid(collSphere) + colNp = flyPie.attachNewNode(collNode) + colNp.show() + base.cTrav.addCollider(colNp, self.pieHandler) + self.accept('pieHit-' + collSphereName, self.handlePieHitting) + + def matchRunningAnim(toon = toon): + toon.playingAnim = None + toon.setSpeed(toon.forwardSpeed, toon.rotateSpeed) + return + + newTossTrack = Sequence(tossTrack, Func(matchRunningAnim)) + pieTrack = Parallel(newTossTrack, pieTrack) + elapsedTime = globalClockDelta.localElapsedTime(timestamp) + if elapsedTime < 16.0 / 24.0: + elapsedTime = 16.0 / 24.0 + pieTrack.start(elapsedTime) + self.toonPieTracks[avId] = pieTrack + + def getTossPieInterval(self, toon, x, y, z, h, p, r, power, beginFlyIval = Sequence()): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + pie = toon.getPieModel() + pie.setScale(0.9) + flyPie = pie.copyTo(NodePath('a')) + pieName = ToontownBattleGlobals.pieNames[toon.pieType] + pieType = BattleProps.globalPropPool.getPropType(pieName) + animPie = Sequence() + if pieType == 'actor': + animPie = ActorInterval(pie, pieName, startFrame=48) + sound = loader.loadSfx('phase_3.5/audio/sfx/AA_pie_throw_only.ogg') + t = power / 100.0 + dist = 100 - 70 * t + time = 1 + 0.5 * t + proj = ProjectileInterval(None, startPos=Point3(0, 0, 0), endPos=Point3(0, dist, 0), duration=time) + relVel = proj.startVel + + def getVelocity(toon = toon, relVel = relVel): + return render.getRelativeVector(toon, relVel) * 0.6 + + toss = Track((0, Sequence(Func(toon.setPosHpr, x, y, z, h, p, r), Func(pie.reparentTo, toon.rightHand), Func(pie.setPosHpr, 0, 0, 0, 0, 0, 0), Parallel(ActorInterval(toon, 'throw', startFrame=48, partName='torso'), animPie), Func(toon.loop, 'neutral'))), (16.0 / 24.0, Func(pie.detachNode))) + fly = Track((14.0 / 24.0, SoundInterval(sound, node=toon)), (16.0 / 24.0, Sequence(Func(flyPie.reparentTo, render), Func(flyPie.setPosHpr, toon, 0.52, 0.97, 2.24, 0, -45, 0), beginFlyIval, ProjectileInterval(flyPie, startVel=getVelocity, duration=6), Func(flyPie.detachNode)))) + return (toss, fly, flyPie) + + def handlePieHitting(self, colEntry): + if self.gameIsEnding: + return + into = colEntry.getIntoNodePath() + intoName = into.getName() + if 'CogThiefPieSphere' in intoName: + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + parts = intoName.split('-') + suitNum = int(parts[1]) + pos = self.cogInfo[suitNum]['suit'].suit.getPos() + if pos in CTGG.CogStartingPositions: + self.notify.debug('Cog %d hit at starting pos %s, ignoring' % (suitNum, pos)) + else: + self.sendUpdate('pieHitSuit', [self.localAvId, + timestamp, + suitNum, + pos[0], + pos[1], + pos[2]]) + self.makeSuitRespondToPieHit(timestamp, suitNum) + + def pieHitSuit(self, avId, timestamp, suitNum, x, y, z): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() not in ['play']: + self.notify.warning('ignoring msg: av %s hit by suit' % avId) + return + if self.gameIsEnding: + return + self.notify.debug('avatar ' + `avId` + ' hit by a suit') + if avId != self.localAvId: + self.makeSuitRespondToPieHit(timestamp, suitNum) + + def makeSuitRespondToPieHit(self, timestamp, suitNum): + cog = self.cogInfo[suitNum]['suit'] + cog.respondToPieHit(timestamp) + + def sendCogAtReturnPos(self, cogIndex, barrelIndex): + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.sendUpdate('cogAtReturnPos', [timestamp, cogIndex, barrelIndex]) + + def markBarrelStolen(self, timestamp, inResponseToClientStamp, barrelIndex): + if not self.hasLocalToon: + return + if barrelIndex not in self.stolenBarrels: + self.stolenBarrels.append(barrelIndex) + barrel = self.barrels[barrelIndex] + barrel.hide() + if base.config.GetBool('cog-thief-check-barrels', 1): + if not base.config.GetBool('cog-thief-endless', 0): + if len(self.stolenBarrels) == len(self.barrels): + localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + gameTime = self.local2GameTime(localStamp) + self.clockStopTime = gameTime + self.notify.debug('clockStopTime = %s' % gameTime) + score = int(self.scoreMult * CTGG.calcScore(gameTime) + 0.5) + self.rewardPanel['text'] = str(score) + self.showResults() + + def __gameTimerExpired(self): + self.notify.debug('game timer expired') + self.showResults() + + def __startRewardCountdown(self): + taskMgr.remove(self.REWARD_COUNTDOWN_TASK) + taskMgr.add(self.__updateRewardCountdown, self.REWARD_COUNTDOWN_TASK) + + def __killRewardCountdown(self): + taskMgr.remove(self.REWARD_COUNTDOWN_TASK) + + def __updateRewardCountdown(self, task): + curTime = self.getCurrentGameTime() + if self.clockStopTime is not None: + if self.clockStopTime < curTime: + self.notify.debug('self.clockStopTime < curTime %s %s' % (self.clockStopTime, curTime)) + self.__killRewardCountdown() + curTime = self.clockStopTime + if curTime > CTGG.GameTime: + curTime = CTGG.GameTime + score = int(self.scoreMult * CTGG.calcScore(curTime) + 0.5) + if not hasattr(task, 'curScore'): + task.curScore = score + result = Task.cont + if hasattr(self, 'rewardPanel'): + self.rewardPanel['text'] = str(score) + if task.curScore != score: + if hasattr(self, 'jarIval'): + self.jarIval.finish() + s = self.rewardPanel.getScale() + self.jarIval = Parallel(Sequence(self.rewardPanel.scaleInterval(0.15, s * 3.0 / 4.0, blendType='easeOut'), self.rewardPanel.scaleInterval(0.15, s, blendType='easeIn')), SoundInterval(self.sndRewardTick), name='cogThiefGameRewardJarThrob') + self.jarIval.start() + task.curScore = score + else: + result = Task.done + return result + + def startGameWalk(self): + if self.useOrthoWalk: + self.gameWalk.start() + else: + self.gameWalk.enter() + self.gameWalk.fsm.request('walking') + + def stopGameWalk(self): + if self.useOrthoWalk: + self.gameWalk.stop() + else: + self.gameWalk.exit() + + def getCogThief(self, cogIndex): + return self.cogInfo[cogIndex]['suit'] + + def isToonPlayingHitTrack(self, avId): + if avId in self.toonHitTracks: + track = self.toonHitTracks[avId] + if track.isPlaying(): + return True + return False + + def getNumCogs(self): + result = base.config.GetInt('cog-thief-num-cogs', 0) + if not result: + safezone = self.getSafezoneId() + result = CTGG.calculateCogs(self.numPlayers, safezone) + return result + + def getCogSpeed(self): + result = 6.0 + safezone = self.getSafezoneId() + result = CTGG.calculateCogSpeed(self.numPlayers, safezone) + return result + + def showResults(self): + if not self.gameIsEnding: + self.gameIsEnding = True + for barrel in self.barrels: + barrel.wrtReparentTo(render) + + for key in self.cogInfo: + thief = self.cogInfo[key]['suit'] + thief.suit.setPos(100, 0, 0) + thief.suit.hide() + + self.__killRewardCountdown() + self.stopGameWalk() + numBarrelsSaved = len(self.barrels) - len(self.stolenBarrels) + resultStr = '' + if numBarrelsSaved == len(self.barrels): + resultStr = TTLocalizer.CogThiefPerfect + elif numBarrelsSaved > 1: + resultStr = TTLocalizer.CogThiefBarrelsSaved % {'num': numBarrelsSaved} + elif numBarrelsSaved == 1: + resultStr = TTLocalizer.CogThiefBarrelSaved % {'num': numBarrelsSaved} + else: + resultStr = TTLocalizer.CogThiefNoBarrelsSaved + perfectTextSubnode = hidden.attachNewNode(self.__genText(resultStr)) + perfectText = hidden.attachNewNode('perfectText') + perfectTextSubnode.reparentTo(perfectText) + frame = self.__textGen.getCardActual() + offsetY = -abs(frame[2] + frame[3]) / 2.0 + perfectTextSubnode.setPos(0, 0, offsetY) + perfectText.setColor(1, 0.1, 0.1, 1) + + def fadeFunc(t, text = perfectText): + text.setColorScale(1, 1, 1, t) + + def destroyText(text = perfectText): + text.removeNode() + + def safeGameOver(self = self): + if not self.frameworkFSM.isInternalStateInFlux(): + self.gameOver() + + textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5), Func(safeGameOver)) + if numBarrelsSaved == len(self.barrels): + soundTrack = SoundInterval(self.sndPerfect) + else: + soundTrack = Sequence() + self.resultIval = Parallel(textTrack, soundTrack) + self.resultIval.start() + + def __genText(self, text): + self.__textGen.setText(text) + return self.__textGen.generate() + + def getIntroTrack(self): + base.camera.setPosHpr(0, -13.66, 13.59, 0, -51.6, 0) + result = Sequence(Wait(2), LerpPosHprInterval(base.camera, 13, Point3(self.cameraTopView[0], self.cameraTopView[1], self.cameraTopView[2]), Point3(self.cameraTopView[3], self.cameraTopView[4], self.cameraTopView[5]), blendType='easeIn')) + return result diff --git a/toontown/minigame/DistributedCogThiefGameAI.py b/toontown/minigame/DistributedCogThiefGameAI.py new file mode 100755 index 00000000..d3dd8539 --- /dev/null +++ b/toontown/minigame/DistributedCogThiefGameAI.py @@ -0,0 +1,409 @@ +import random +from pandac.PandaModules import Point3 +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.distributed.ClockDelta import globalClockDelta +from direct.task import Task +from toontown.minigame import DistributedMinigameAI +from toontown.minigame import MinigameGlobals +from toontown.minigame import CogThiefGameGlobals +CTGG = CogThiefGameGlobals + +class DistributedCogThiefGameAI(DistributedMinigameAI.DistributedMinigameAI): + notify = directNotify.newCategory('DistributedCogThiefGameAI') + ExplodeWaitTime = 6.0 + CTGG.LyingDownDuration + + def __init__(self, air, minigameId): + try: + self.DistributedCogThiefGameAI_initialized + except: + self.DistributedCogThiefGameAI_initialized = 1 + DistributedMinigameAI.DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedCogThiefGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.cogInfo = {} + self.barrelInfo = {} + self.initBarrelInfo() + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + self.removeAllTasks() + DistributedMinigameAI.DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + self.initCogInfo() + DistributedMinigameAI.DistributedMinigameAI.setGameReady(self) + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + score = int(CTGG.calcScore(self.getCurrentGameTime())) + for avId in self.avIdList: + self.scoreDict[avId] = score + + if self.getNumBarrelsStolen() == 0: + for avId in self.avIdList: + self.scoreDict[avId] += CTGG.PerfectBonus[len(self.avIdList) - 1] + self.logPerfectGame(avId) + + self.gameFSM.request('cleanup') + DistributedMinigameAI.DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.startSuitGoals() + if not config.GetBool('cog-thief-endless', 0): + taskMgr.doMethodLater(CTGG.GameTime, self.timerExpired, self.taskName('gameTimer')) + + def exitPlay(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + taskMgr.remove(self.taskName('gameTimer')) + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def initCogInfo(self): + for cogIndex in xrange(self.getNumCogs()): + self.cogInfo[cogIndex] = {'pos': Point3(CogThiefGameGlobals.CogStartingPositions[cogIndex]), + 'goal': CTGG.NoGoal, + 'goalId': CTGG.InvalidGoalId, + 'barrel': CTGG.NoBarrelCarried} + + def initBarrelInfo(self): + for barrelIndex in xrange(CogThiefGameGlobals.NumBarrels): + self.barrelInfo[barrelIndex] = {'pos': Point3(CogThiefGameGlobals.BarrelStartingPositions[barrelIndex]), + 'carriedBy': CTGG.BarrelOnGround, + 'stolen': False} + + def makeCogCarryBarrel(self, timestamp, clientStamp, cogIndex, barrelIndex): + if cogIndex in self.cogInfo and barrelIndex in self.barrelInfo: + self.barrelInfo[barrelIndex]['carriedBy'] = cogIndex + self.cogInfo[cogIndex]['barrel'] = barrelIndex + else: + self.notify.warning('makeCogCarryBarrel invalid cogIndex=%s barrelIndex=%s' % (cogIndex, barrelIndex)) + + def b_makeCogCarryBarrel(self, clientStamp, cogIndex, barrelIndex): + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.notify.debug('b_makeCogCarryBarrel timeStamp=%s clientStamp=%s cog=%s barrel=%s' % (timestamp, + clientStamp, + cogIndex, + barrelIndex)) + self.makeCogCarryBarrel(timestamp, clientStamp, cogIndex, barrelIndex) + self.d_makeCogCarryBarrel(timestamp, clientStamp, cogIndex, barrelIndex) + + def d_makeCogCarryBarrel(self, timestamp, clientStamp, cogIndex, barrelIndex): + pos = self.cogInfo[cogIndex]['pos'] + gameTime = self.getCurrentGameTime() + self.sendUpdate('makeCogCarryBarrel', [timestamp, + clientStamp, + cogIndex, + barrelIndex, + pos[0], + pos[1], + pos[2]]) + + def makeCogDropBarrel(self, timestamp, clientStamp, cogIndex, barrelIndex, barrelPos): + if cogIndex in self.cogInfo and barrelIndex in self.barrelInfo: + self.barrelInfo[barrelIndex]['carriedBy'] = CTGG.BarrelOnGround + self.cogInfo[cogIndex]['barrel'] = CTGG.NoBarrelCarried + else: + self.notify.warning('makeCogDropBarrel invalid cogIndex=%s barrelIndex=%s' % (cogIndex, barrelIndex)) + + def b_makeCogDropBarrel(self, clientStamp, cogIndex, barrelIndex, barrelPos): + if self.barrelInfo[barrelIndex]['carriedBy'] != cogIndex: + self.notify.error("self.barrelInfo[%s]['carriedBy'] != %s" % (barrelIndex, cogIndex)) + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.makeCogDropBarrel(timestamp, clientStamp, cogIndex, barrelIndex, barrelPos) + self.d_makeCogDropBarrel(timestamp, clientStamp, cogIndex, barrelIndex, barrelPos) + + def d_makeCogDropBarrel(self, timestamp, clientStamp, cogIndex, barrelIndex, barrelPos): + pos = barrelPos + gameTime = self.getCurrentGameTime() + self.sendUpdate('makeCogDropBarrel', [timestamp, + clientStamp, + cogIndex, + barrelIndex, + pos[0], + pos[1], + pos[2]]) + + def isCogCarryingABarrel(self, cogIndex): + result = self.cogInfo[cogIndex]['barrel'] > CTGG.NoBarrelCarried + return result + + def isCogCarryingThisBarrel(self, cogIndex, barrelIndex): + result = self.cogInfo[cogIndex]['barrel'] == barrelIndex + return result + + def startSuitGoals(self): + delayTimes = [] + for cogIndex in xrange(self.getNumCogs()): + delayTimes.append(cogIndex * 1.0) + + random.shuffle(delayTimes) + for cogIndex in xrange(self.getNumCogs()): + self.doMethodLater(delayTimes[cogIndex], self.chooseSuitGoal, self.uniqueName('choseSuitGoal-%d-' % cogIndex), extraArgs=[cogIndex]) + + def chaseToon(self, suitNum, avId): + goalType = CTGG.ToonGoal + goalId = avId + self.cogInfo[suitNum]['goal'] = goalType + self.cogInfo[suitNum]['goalId'] = goalId + pos = self.cogInfo[suitNum]['pos'] + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.notify.debug('chaseToon time=%s suitNum=%s, avId=%s' % (timestamp, suitNum, avId)) + gameTime = self.getCurrentGameTime() + self.sendUpdate('updateSuitGoal', [timestamp, + timestamp, + suitNum, + goalType, + goalId, + pos[0], + pos[1], + pos[2]]) + + def hitBySuit(self, avId, timestamp, suitNum, x, y, z): + if suitNum >= self.getNumCogs(): + self.notify.warning('hitBySuit, possible hacker avId=%s' % avId) + return + barrelIndex = self.cogInfo[suitNum]['barrel'] + if barrelIndex >= 0: + barrelPos = Point3(x, y, z) + self.b_makeCogDropBarrel(timestamp, suitNum, barrelIndex, barrelPos) + startPos = CTGG.CogStartingPositions[suitNum] + self.cogInfo[suitNum]['pos'] = startPos + self.cogInfo[suitNum]['goal'] = CTGG.NoGoal + self.cogInfo[suitNum]['goalId'] = CTGG.InvalidGoalId + self.sendSuitSync(timestamp, suitNum) + self.doMethodLater(self.ExplodeWaitTime, self.chooseSuitGoal, self.uniqueName('choseSuitGoal-%d-' % suitNum), extraArgs=[suitNum]) + + def sendSuitSync(self, clientstamp, suitNum): + pos = self.cogInfo[suitNum]['pos'] + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + goalType = self.cogInfo[suitNum]['goal'] + goalId = self.cogInfo[suitNum]['goalId'] + gameTime = self.getCurrentGameTime() + self.sendUpdate('updateSuitGoal', [timestamp, + clientstamp, + suitNum, + goalType, + goalId, + pos[0], + pos[1], + pos[2]]) + + def chooseSuitGoal(self, suitNum): + barrelIndex = self.findClosestUnassignedBarrel(suitNum) + if barrelIndex >= 0: + self.chaseBarrel(suitNum, barrelIndex) + else: + noOneChasing = self.avIdList[:] + for key in self.cogInfo: + if self.cogInfo[key]['goal'] == CTGG.ToonGoal: + toonId = self.cogInfo[key]['goalId'] + if toonId in noOneChasing: + noOneChasing.remove(toonId) + + chaseToonId = self.avIdList[0] + if noOneChasing: + chaseToonId = random.choice(noOneChasing) + else: + chaseToonId = random.choice(self.avIdList) + self.chaseToon(suitNum, chaseToonId) + + def chaseBarrel(self, suitNum, barrelIndex): + goalType = CTGG.BarrelGoal + goalId = barrelIndex + self.cogInfo[suitNum]['goal'] = goalType + self.cogInfo[suitNum]['goalId'] = goalId + pos = self.cogInfo[suitNum]['pos'] + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.notify.debug('chaseBarrel time=%s suitNum=%s, barrelIndex=%s' % (timestamp, suitNum, barrelIndex)) + gameTime = self.getCurrentGameTime() + self.sendUpdate('updateSuitGoal', [timestamp, + timestamp, + suitNum, + goalType, + goalId, + pos[0], + pos[1], + pos[2]]) + + def getCogCarryingBarrel(self, barrelIndex): + return self.barrelInfo[barrelIndex]['carriedBy'] + + def cogHitBarrel(self, clientStamp, cogIndex, barrelIndex, x, y, z): + if cogIndex >= self.getNumCogs(): + self.notify.warning('cogHitBarrel, possible hacker cogIndex=%s' % cogIndex) + return + if barrelIndex >= CTGG.NumBarrels: + self.notify.warning('cogHitBarrel, possible hacker barrelIndex=%s' % barrelIndex) + return + if self.isCogCarryingABarrel(cogIndex): + self.notify.debug('cog is already carrying a barrel ignore') + return + if self.cogInfo[cogIndex]['goal'] == CTGG.NoGoal: + self.notify.debug('ignoring barrel hit as cog %d has no goal' % cogIndex) + return + if self.getCogCarryingBarrel(barrelIndex) == CTGG.BarrelOnGround: + pos = Point3(x, y, z) + returnPosIndex = self.chooseReturnPos(cogIndex, pos) + self.runAway(clientStamp, cogIndex, pos, barrelIndex, returnPosIndex) + + def chooseReturnPos(self, cogIndex, cogPos): + shortestDistance = 10000 + shortestReturnIndex = -1 + for retIndex in xrange(len(CTGG.CogReturnPositions)): + retPos = CTGG.CogReturnPositions[retIndex] + distance = (cogPos - retPos).length() + if distance < shortestDistance: + shortestDistance = distance + shortestReturnIndex = retIndex + self.notify.debug('shortest distance=%s index=%s' % (shortestDistance, shortestReturnIndex)) + + self.notify.debug('chooseReturnpos returning %s' % shortestReturnIndex) + return shortestReturnIndex + + def runAway(self, clientStamp, cogIndex, cogPos, barrelIndex, returnPosIndex): + self.cogInfo[cogIndex]['pos'] = cogPos + self.b_makeCogCarryBarrel(clientStamp, cogIndex, barrelIndex) + goalType = CTGG.RunAwayGoal + goalId = returnPosIndex + self.cogInfo[cogIndex]['goal'] = goalType + self.cogInfo[cogIndex]['goalId'] = 0 + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + gameTime = self.getCurrentGameTime() + self.sendUpdate('updateSuitGoal', [timestamp, + clientStamp, + cogIndex, + goalType, + goalId, + cogPos[0], + cogPos[1], + cogPos[2]]) + + def pieHitSuit(self, avId, timestamp, suitNum, x, y, z): + if suitNum >= self.getNumCogs(): + self.notify.warning('hitBySuit, possible hacker avId=%s' % avId) + return + barrelIndex = self.cogInfo[suitNum]['barrel'] + if barrelIndex >= 0: + barrelPos = Point3(x, y, z) + self.b_makeCogDropBarrel(timestamp, suitNum, barrelIndex, barrelPos) + startPos = CTGG.CogStartingPositions[suitNum] + self.cogInfo[suitNum]['pos'] = startPos + self.cogInfo[suitNum]['goal'] = CTGG.NoGoal + self.cogInfo[suitNum]['goalId'] = CTGG.InvalidGoalId + self.sendSuitSync(timestamp, suitNum) + self.doMethodLater(self.ExplodeWaitTime, self.chooseSuitGoal, self.uniqueName('choseSuitGoal-%d-' % suitNum), extraArgs=[suitNum]) + + def findClosestUnassignedBarrel(self, suitNum): + possibleBarrels = [] + for key in self.barrelInfo: + info = self.barrelInfo[key] + if info['carriedBy'] == CTGG.BarrelOnGround and not info['stolen']: + if not self.isCogGoingForBarrel(key): + possibleBarrels.append(key) + + shortestDistance = 10000 + shortestBarrelIndex = -1 + cogPos = self.cogInfo[suitNum]['pos'] + for possibleIndex in possibleBarrels: + barrelPos = self.barrelInfo[possibleIndex]['pos'] + distance = (cogPos - barrelPos).length() + if distance < shortestDistance: + shortestDistance = distance + shortestBarrelIndex = possibleIndex + + return shortestBarrelIndex + + def isCogGoingForBarrel(self, barrelIndex): + result = False + for suitNum in self.cogInfo: + cogInfo = self.cogInfo[suitNum] + if cogInfo['goal'] == CTGG.BarrelGoal and cogInfo['goalId'] == barrelIndex: + result = True + break + + return result + + def markBarrelStolen(self, clientStamp, barrelIndex): + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.sendUpdate('markBarrelStolen', [timestamp, clientStamp, barrelIndex]) + self.barrelInfo[barrelIndex]['stolen'] = True + + def getNumBarrelsStolen(self): + numStolen = 0 + for barrel in self.barrelInfo.values(): + if barrel['stolen']: + numStolen += 1 + + return numStolen + + def cogAtReturnPos(self, clientstamp, cogIndex, barrelIndex): + if cogIndex not in self.cogInfo or barrelIndex not in self.barrelInfo: + avId = self.air.getAvatarIdFromSender() + self.air.writeServerEvent('suspicious cogAtReturnPos avId=%s, cogIndex=%s, barrelIndex=%s' % (avId, cogIndex, barrelIndex)) + return + if self.cogInfo[cogIndex]['goal'] == CTGG.RunAwayGoal: + if self.isCogCarryingThisBarrel(cogIndex, barrelIndex): + self.markBarrelStolen(clientstamp, barrelIndex) + returnPosIndex = self.cogInfo[cogIndex]['goalId'] + retPos = CTGG.CogReturnPositions[returnPosIndex] + self.b_makeCogDropBarrel(clientstamp, cogIndex, barrelIndex, retPos) + startPos = CTGG.CogStartingPositions[cogIndex] + self.cogInfo[cogIndex]['pos'] = startPos + self.cogInfo[cogIndex]['goal'] = CTGG.NoGoal + self.cogInfo[cogIndex]['goalId'] = CTGG.InvalidGoalId + self.doMethodLater(0.5, self.chooseSuitGoal, self.uniqueName('choseSuitGoal-%d-' % cogIndex), extraArgs=[cogIndex]) + self.checkForGameOver() + + def checkForGameOver(self): + numStolen = 0 + for key in self.barrelInfo: + if self.barrelInfo[key]['stolen']: + numStolen += 1 + + self.notify.debug('numStolen = %s' % numStolen) + if simbase.config.GetBool('cog-thief-check-barrels', 1): + if not simbase.config.GetBool('cog-thief-endless', 0): + if numStolen == len(self.barrelInfo): + self.gameOver() + + def timerExpired(self, task): + self.notify.debug('timer expired') + self.gameOver() + return Task.done + + def getNumCogs(self): + result = simbase.config.GetInt('cog-thief-num-cogs', 0) + if not result: + safezone = self.getSafezoneId() + result = CTGG.calculateCogs(self.numPlayers, safezone) + return result diff --git a/toontown/minigame/DistributedDivingGame.py b/toontown/minigame/DistributedDivingGame.py new file mode 100755 index 00000000..5b2f407a --- /dev/null +++ b/toontown/minigame/DistributedDivingGame.py @@ -0,0 +1,978 @@ +from direct.showbase.ShowBaseGlobal import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase import ToontownTimer +from DistributedMinigame import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task +from direct.actor import Actor +from toontown.toon import LaffMeter +from direct.distributed import DistributedSmoothNode +import ArrowKeys +import Ring +import RingTrack +import DivingGameGlobals +import RingGroup +import RingTrackGroups +import random +import DivingGameToonSD +import DivingFishSpawn +import DivingTreasure +import math +import TreasureScorePanel +from otp.distributed.TelemetryLimiter import TelemetryLimiter, TLGatherAllAvs +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class DivingGameRotationLimiter(TelemetryLimiter): + + def __init__(self, h, p): + self._h = h + self._p = p + + def __call__(self, obj): + obj.setHpr(self._h, self._p, obj.getR()) + + +class DistributedDivingGame(DistributedMinigame): + COLLISION_WATCH_TASK = 'DivingGameCollisionWatchTask' + TREASURE_BOUNDS_TASK = 'DivingGameTreasureBoundsTask' + CRAB_TASK = 'DivingGameCrabTask' + UPDATE_LOCALTOON_TASK = 'DivingGameUpdateLocalToonTask' + COLLISION_DETECTION_PRIORITY = 5 + MAP_DIV = 2.8 + MAP_OFF = 14.0 + LAG_COMP = 1.25 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedDivingGame', [State.State('off', self.enterOff, self.exitOff, ['swim']), State.State('swim', self.enterSwim, self.exitSwim, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.iCount = 0 + self.reachedFlag = 0 + self.grabbingTreasure = -1 + self.dead = 0 + + def getTitle(self): + return TTLocalizer.DivingGameTitle + + def getInstructions(self): + p = self.avIdList.index(self.localAvId) + if self.isSinglePlayer(): + text = TTLocalizer.DivingInstructionsSinglePlayer + else: + text = TTLocalizer.DivingInstructionsMultiPlayer + return text + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + loadBase = 'phase_4/models/minigames/' + loadBaseShip = 'phase_5/models/props/' + self.sndAmbience = base.loadSfx('phase_4/audio/sfx/AV_ambient_water.ogg') + self.environModel = loader.loadModel(loadBase + 'diving_game.bam') + self.boatModel = self.environModel.find('**/boat') + self.skyModel = self.environModel.find('**/sky') + self.waterModel = self.environModel.find('**/seawater') + self.frontMap = self.environModel.find('**/sea_front') + self.frontMap.setY(3) + self.frontMap.setBin('fixed', 0) + self.frontMap.setDepthTest(0) + self.waterModel.setY(1.0) + bubbleModel = self.environModel.find('**/bubbles1') + bubbleModel.setY(1.0) + bubbleModel = self.environModel.find('**/bubbles2') + bubbleModel.setY(1.0) + bubbleModel = self.environModel.find('**/bubbles3') + bubbleModel.setY(1.0) + bubbleModel = self.environModel.find('**/bubbles4') + bubbleModel.setY(1.0) + bubbleModel = self.environModel.find('**/bubbles5') + bubbleModel.setY(1.0) + self.mapModel = loader.loadModel(loadBase + 'diving_game.bam') + boatMap = self.mapModel.find('**/boat') + skyMap = self.mapModel.find('**/sky') + frontMap = self.mapModel.find('**/sea_front') + skyMap.hide() + frontMap.hide() + boatMap.setZ(28.5) + self.crabs = [] + self.spawners = [] + self.toonSDs = {} + avId = self.localAvId + toonSD = DivingGameToonSD.DivingGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.load() + crabSoundName = 'King_Crab.ogg' + crabSoundPath = 'phase_4/audio/sfx/%s' % crabSoundName + self.crabSound = loader.loadSfx(crabSoundPath) + treasureSoundName = 'SZ_DD_treasure.ogg' + treasureSoundPath = 'phase_4/audio/sfx/%s' % treasureSoundName + self.treasureSound = loader.loadSfx(treasureSoundPath) + hitSoundName = 'diving_game_hit.ogg' + hitSoundPath = 'phase_4/audio/sfx/%s' % hitSoundName + self.hitSound = loader.loadSfx(hitSoundPath) + self.music = base.loadMusic('phase_4/audio/bgm/MG_Target.ogg') + self.addSound('dropGold', 'diving_treasure_drop_off.ogg', 'phase_4/audio/sfx/') + self.addSound('getGold', 'diving_treasure_pick_up.ogg', 'phase_4/audio/sfx/') + self.swimSound = loader.loadSfx('phase_4/audio/sfx/diving_swim_loop.ogg') + self.swimSound.setVolume(0.0) + self.swimSound.setPlayRate(1.0) + self.swimSound.setLoop(True) + self.swimSound.play() + + def addSound(self, name, soundName, path = None): + if not hasattr(self, 'soundTable'): + self.soundTable = {} + if path: + self.soundPath = path + soundSource = '%s%s' % (self.soundPath, soundName) + self.soundTable[name] = loader.loadSfx(soundSource) + + def playSound(self, name, volume = 1.0): + self.soundTable[name].setVolume(1.0) + self.soundTable[name].play() + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.mapModel.removeNode() + del self.mapModel + if hasattr(self, 'soundTable'): + del self.soundTable + del self.sndAmbience + del self.hitSound + del self.crabSound + del self.treasureSound + self.swimSound.stop() + del self.swimSound + self.environModel.removeNode() + del self.environModel + self.removeChildGameFSM(self.gameFSM) + for avId in self.toonSDs.keys(): + toonSD = self.toonSDs[avId] + toonSD.unload() + + del self.toonSDs + del self.gameFSM + del self.music + + def fishCollision(self, collEntry): + avId = int(collEntry.getFromNodePath().getName()) + toonSD = self.toonSDs[avId] + name = collEntry.getIntoNodePath().getName() + if len(name) >= 7: + if name[0:6] == 'crabby': + self.sendUpdate('handleCrabCollision', [avId, toonSD.status]) + else: + spawnerId = int(name[2]) + spawnId = int(name[3:len(name)]) + if spawnId in self.spawners[spawnerId].fishArray: + self.sendUpdate('handleFishCollision', [avId, + spawnId, + spawnerId, + toonSD.status]) + + def fishSpawn(self, timestamp, fishcode, spawnerId, offset): + if self.dead is 1: + return + ts = globalClockDelta.localElapsedTime(timestamp) + if not hasattr(self, 'spawners'): + return + if abs(self.spawners[spawnerId].lastSpawn - timestamp) < 150: + return + fish = self.spawners[spawnerId].createFish(fishcode) + fish.offset = offset + fish.setPos(self.spawners[spawnerId].position) + func = Func(self.fishRemove, fish.code) + self.spawners[spawnerId].lastSpawn = timestamp + iName = '%s %s' % (fish.name, self.iCount) + self.iCount += 1 + if fish.name == 'clown': + fish.moveLerp = Sequence(LerpPosInterval(fish, duration=8 * self.SPEEDMULT * self.LAG_COMP, startPos=self.spawners[spawnerId].position, pos=self.spawners[spawnerId].position + Point3(50 * self.spawners[spawnerId].direction, 0, (offset - 4) / 2.0), name=iName), func) + fish.specialLerp = Sequence() + elif fish.name == 'piano': + fish.moveLerp = Sequence(LerpPosInterval(fish, duration=5 * self.SPEEDMULT * self.LAG_COMP, startPos=self.spawners[spawnerId].position, pos=self.spawners[spawnerId].position + Point3(50 * self.spawners[spawnerId].direction, 0, (offset - 4) / 2.0), name=iName), func) + fish.specialLerp = Sequence() + elif fish.name == 'pbj': + fish.moveLerp = Sequence(LerpFunc(fish.setX, duration=12 * self.SPEEDMULT * self.LAG_COMP, fromData=self.spawners[spawnerId].position.getX(), toData=self.spawners[spawnerId].position.getX() + 50 * self.spawners[spawnerId].direction, name=iName), func) + fish.specialLerp = LerpFunc(self.pbjMove, duration=5 * self.SPEEDMULT * self.LAG_COMP, fromData=0, toData=2.0 * 3.14159, extraArgs=[fish, self.spawners[spawnerId].position.getZ()], blendType='easeInOut') + elif fish.name == 'balloon': + fish.moveLerp = Sequence(LerpPosInterval(fish, duration=10 * self.SPEEDMULT * self.LAG_COMP, startPos=self.spawners[spawnerId].position, pos=self.spawners[spawnerId].position + Point3(50 * self.spawners[spawnerId].direction, 0, (offset - 4) / 2.0), name=iName), func) + fish.specialLerp = Sequence(Wait(offset / 10.0 * 2 + 1.5), Parallel(LerpScaleInterval(fish, duration=0.3, startScale=Vec3(2, 2, 2), scale=Vec3(5, 3, 5), blendType='easeInOut')), Wait(1.0), Parallel(LerpScaleInterval(fish, duration=0.4, startScale=Vec3(5, 3, 5), scale=Vec3(2, 2, 2), blendType='easeInOut'))) + elif fish.name == 'bear' or fish.name == 'nurse': + fish.moveLerp = Sequence(LerpPosInterval(fish, duration=20 * self.LAG_COMP, startPos=self.spawners[spawnerId].position, pos=self.spawners[spawnerId].position + Point3(50 * self.spawners[spawnerId].direction, 0, 0), name=iName), func) + fish.specialLerp = Sequence() + fish.moveLerp.start(ts) + fish.specialLerp.loop(ts) + + def pbjMove(self, x, fish, Z): + z = math.sin(x + fish.offset * 3) * 3 + fish.setZ(z + Z) + + def getIntroMovie(self): + seq = Sequence() + seq.append(Wait(2.0)) + seq.append(LerpFunc(camera.setZ, duration=5, fromData=36, toData=-23, blendType='easeInOut', name='intro')) + seq.append(Wait(2.0)) + seq.append(LerpFunc(camera.setZ, duration=5, fromData=-23, toData=36 + 3, blendType='easeInOut', name='intro')) + return seq + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + base.localAvatar.collisionsOff() + DistributedSmoothNode.activateSmoothing(1, 1) + numToons = self.numPlayers + self.NUMTREASURES = numToons + camera.reparentTo(render) + camera.setZ(36) + camera.setHpr(0,0,0) + camera.setX(0) + base.camLens.setMinFov(31/(4./3.)) + camera.setY(-54) + base.camLens.setFar(1500) + self.introMovie = self.getIntroMovie() + self.introMovie.start() + self.accept('FishHit', self.fishCollision) + toonSD = self.toonSDs[self.localAvId] + toonSD.enter() + toonSD.fsm.request('normal') + toon = base.localAvatar + toon.reparentTo(render) + toon.setPos(-9, -1, 36) + self.__placeToon(self.localAvId) + self.arrowKeys = ArrowKeys.ArrowKeys() + self.xVel = 0 + self.zVel = 0 + self.orientNode = toon.attachNewNode('orientNode') + self.orientNode.setPos(0, 0, 1) + self.orientNode2 = toon.attachNewNode('orientNode') + self.orientNode2.setPos(0, 0, -1) + self.environNode = render.attachNewNode('environNode') + self.environModel.reparentTo(self.environNode) + self.environModel.setScale(2.8, 2.8, 2.73) + self.environModel.setPos(0, 0.5, -41) + self.skyModel.setScale(1.3, 1.0, 1.0) + boatoff = 6.75 + self.boatModel.reparentTo(self.environNode) + self.boatModel.setPos(0, 3.0, 40 - boatoff) + self.boatModel.setScale(2.8) + cSphere = CollisionSphere(0.0, 0.0, 0.0 + 2.0, 3.0) + cSphere.setTangible(0) + name = 'boat' + cSphereNode = CollisionNode(name) + cSphereNode.setIntoCollideMask(DivingGameGlobals.CollideMask) + cSphereNode.addSolid(cSphere) + self.boatNode = cSphereNode + self.boatCNP = self.boatModel.attachNewNode(cSphereNode) + self.accept('reach-boat', self.__boatReached) + self.boatTilt = Sequence(LerpFunc(self.boatModel.setR, duration=5, fromData=5, toData=-5, blendType='easeInOut', name='tilt'), LerpFunc(self.boatModel.setR, duration=5, fromData=-5, toData=5, blendType='easeInOut', name='tilt')) + self.boatTilt.loop() + self.mapScaleRatio = 40 + self.mapModel.reparentTo(base.a2dTopRight) + self.mapModel.setScale(1.0 / self.mapScaleRatio) + self.mapModel.setTransparency(1) + self.mapModel.setPos(-0.22, 0.0, -1.30) + self.mapModel.setColorScale(1, 1, 1, 0.7) + self.mapModel.hide() + if self.sndAmbience: + self.sndAmbience.setLoop(True) + self.sndAmbience.play() + self.sndAmbience.setVolume(0.01) + return + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + self.introMovie.finish() + self.boatTilt.finish() + self.mapModel.hide() + DistributedSmoothNode.activateSmoothing(1, 0) + for avId in self.toonSDs.keys(): + self.toonSDs[avId].exit() + + base.camLens.setFar(ToontownGlobals.DefaultCameraFar) + base.camLens.setMinFov(settings['fov']/(4./3.)) + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + self.arrowKeys.destroy() + del self.arrowKeys + self.environNode.removeNode() + del self.environNode + if None != self.sndAmbience: + self.sndAmbience.stop() + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.dropShadow.show() + av.resetLOD() + av.setAnimState('neutral', 1.0) + + self.dead = 1 + self.__killCrabTask() + for spawner in self.spawners: + spawner.destroy() + del spawner + + del self.spawners + for crab in self.crabs: + crab.moveLerp.finish() + crab.moveLerp = None + crab.removeNode() + del crab + + if hasattr(self, 'treasures') and self.treasures: + for i in xrange(self.NUMTREASURES): + self.treasures[i].destroy() + + del self.treasures + if hasattr(self, 'cSphereNodePath1'): + self.cSphereNodePath1.removeNode() + del self.cSphereNodePath1 + if hasattr(self, 'cSphereNodePath1'): + self.cSphereNodePath2.removeNode() + del self.cSphereNodePath2 + if hasattr(self, 'remoteToonCollNPs'): + for np in self.remoteToonCollNPs.values(): + np.removeNode() + + del self.remoteToonCollNPs + self.pusher = None + self.cTrav = None + self.cTrav2 = None + base.localAvatar.collisionsOn() + return + + def handleDisabledAvatar(self, avId): + self.dead = 1 + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + self.toonSDs[avId].exit(unexpectedExit=True) + del self.toonSDs[avId] + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + i = self.avIdList.index(avId) + numToons = float(self.numPlayers) + x = -10 + i * 5 + toon.setPos(x, -1, 36) + toon.setHpr(180, 180, 0) + + def getTelemetryLimiter(self): + return TLGatherAllAvs('DivingGame', Functor(DivingGameRotationLimiter, 180, 180)) + + def setGameReady(self): + self.notify.debug('setGameReady') + if not self.hasLocalToon: + return + if DistributedMinigame.setGameReady(self): + return + self.dead = 0 + self.difficultyPatterns = {ToontownGlobals.ToontownCentral: [1, + 1.5, + 65, + 3], + ToontownGlobals.DonaldsDock: [1, + 1.3, + 65, + 1], + ToontownGlobals.DaisyGardens: [2, + 1.2, + 65, + 1], + ToontownGlobals.MinniesMelodyland: [2, + 1.0, + 65, + 1], + ToontownGlobals.TheBrrrgh: [3, + 1.0, + 65, + 1], + ToontownGlobals.DonaldsDreamland: [3, + 1.0, + 65, + 1]} + pattern = self.difficultyPatterns[self.getSafezoneId()] + self.NUMCRABS = pattern[0] + self.SPEEDMULT = pattern[1] + self.TIME = pattern[2] + loadBase = 'phase_4/models/char/' + for i in xrange(self.NUMCRABS): + self.crabs.append(Actor.Actor(loadBase + 'kingCrab-zero.bam', {'anim': loadBase + 'kingCrab-swimLOOP.bam'})) + + for i in xrange(len(self.crabs)): + crab = self.crabs[i] + crab.reparentTo(render) + crab.name = 'king' + crab.crabId = i + cSphere = CollisionSphere(0.0, 0.0, 1, 1.3) + cSphereNode = CollisionNode('crabby' + str(i)) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(BitMask32.allOff()) + cSphereNode.setIntoCollideMask(DivingGameGlobals.CollideMask) + cSphereNodePath = crab.attachNewNode(cSphereNode) + cSphereNodePath.setScale(1, 3, 1) + self.accept('hitby-' + 'crabby' + str(i), self.fishCollision) + if i % 2 is 0: + crab.setPos(20, 0, -40) + crab.direction = -1 + else: + crab.setPos(-20, 0, -40) + crab.direction = 1 + crab.loop('anim') + crab.setScale(1, 0.3, 1) + crab.moveLerp = Sequence() + + self.collHandEvent = CollisionHandlerEvent() + self.cTrav = CollisionTraverser('DistributedDiverGame') + self.cTrav2 = CollisionTraverser('DistributedDiverGame') + self.collHandEvent.addInPattern('reach-%in') + self.collHandEvent.addAgainPattern('reach-%in') + self.collHandEvent.addInPattern('into-%in') + self.collHandEvent.addInPattern('hitby-%in') + loadBase = 'phase_4/models/minigames/' + self.treasures = [] + self.chestIcons = {} + for i in xrange(self.NUMTREASURES): + self.chestIcons[i] = loader.loadModel(loadBase + 'treasure_chest.bam') + self.chestIcons[i].reparentTo(self.mapModel) + self.chestIcons[i].setScale(1.5) + treasure = DivingTreasure.DivingTreasure(i) + self.accept('grab-' + str(i), self.__treasureGrabbed) + self.collHandEvent.addInPattern('grab-%in') + self.collHandEvent.addAgainPattern('grab-%in') + self.treasures.append(treasure) + + self.cTrav.traverse(render) + spawnX = 24 * self.LAG_COMP + spawnY = 0.6 + self.spawners.append(DivingFishSpawn.DivingFishSpawn(0, 1, Point3(-spawnX, spawnY, 25), self.collHandEvent)) + self.spawners.append(DivingFishSpawn.DivingFishSpawn(1, -1, Point3(spawnX, spawnY, 16), self.collHandEvent)) + self.spawners.append(DivingFishSpawn.DivingFishSpawn(2, 1, Point3(-spawnX, spawnY, 6), self.collHandEvent)) + self.spawners.append(DivingFishSpawn.DivingFishSpawn(3, -1, Point3(spawnX, spawnY, -4), self.collHandEvent)) + self.spawners.append(DivingFishSpawn.DivingFishSpawn(4, 1, Point3(-spawnX, spawnY, -15), self.collHandEvent)) + self.spawners.append(DivingFishSpawn.DivingFishSpawn(5, -1, Point3(spawnX, spawnY, -23), self.collHandEvent)) + for spawner in self.spawners: + spawner.lastSpawn = 0 + + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.4) + cSphereNode = CollisionNode('%s' % self.localAvId) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(DivingGameGlobals.CollideMask) + cSphereNode.setIntoCollideMask(BitMask32.allOff()) + headparts = base.localAvatar.getHeadParts() + pos = headparts[2].getPos() + self.cSphereNodePath1 = base.localAvatar.attachNewNode(cSphereNode) + self.cSphereNodePath1.setPos(pos + Point3(0, 1.5, 1)) + self.cTrav.addCollider(self.cSphereNodePath1, self.collHandEvent) + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.4) + cSphereNode = CollisionNode('%s' % self.localAvId) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(DivingGameGlobals.CollideMask) + cSphereNode.setFromCollideMask(BitMask32.allOff()) + cSphereNode.setIntoCollideMask(BitMask32.allOff()) + headparts = base.localAvatar.getHeadParts() + pos = headparts[2].getPos() + self.cSphereNodePath2 = base.localAvatar.attachNewNode(cSphereNode) + self.cSphereNodePath2.setPos(pos + Point3(0, 1.5, -1)) + self.cTrav.addCollider(self.cSphereNodePath2, self.collHandEvent) + self.pusher = CollisionHandlerPusher() + self.pusher.addCollider(self.cSphereNodePath1, base.localAvatar) + self.pusher.addCollider(self.cSphereNodePath2, base.localAvatar) + self.pusher.setHorizontal(0) + self.cTrav2.addCollider(self.cSphereNodePath1, self.pusher) + self.cTrav2.addCollider(self.cSphereNodePath2, self.pusher) + self.remoteToonCollNPs = {} + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + headparts = toon.getHeadParts() + pos = headparts[2].getPos() + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.4) + cSphereNode = CollisionNode('%s' % avId) + cSphereNode.addSolid(cSphere) + cSphereNode.setCollideMask(DivingGameGlobals.CollideMask) + cSphereNP = toon.attachNewNode(cSphereNode) + cSphereNP.setPos(pos + Point3(0, 1.5, 1)) + self.remoteToonCollNPs[int(str(avId) + str(1))] = cSphereNP + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.4) + cSphereNode = CollisionNode('%s' % avId) + cSphereNode.addSolid(cSphere) + cSphereNode.setCollideMask(DivingGameGlobals.CollideMask) + cSphereNP = toon.attachNewNode(cSphereNode) + cSphereNP.setPos(pos + Point3(0, 1.5, -1)) + self.remoteToonCollNPs[int(str(avId) + str(1))] = cSphereNP + toonSD = DivingGameToonSD.DivingGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.load() + toonSD.enter() + toonSD.fsm.request('normal') + + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toon.startSmooth() + + self.remoteToons = {} + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + self.remoteToons[avId] = toon + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + DistributedMinigame.setGameStart(self, timestamp) + self.notify.debug('setGameStart') + self.treasurePanel = TreasureScorePanel.TreasureScorePanel() + self.treasurePanel.setPos(0.145, 0, -0.27) + self.treasurePanel.reparentTo(base.a2dTopLeft) + self.treasurePanel.makeTransparent(0.7) + self.introMovie.finish() + self.gameFSM.request('swim') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterSwim(self): + self.notify.debug('enterSwim') + base.playMusic(self.music, looping=1, volume=0.9) + self.localLerp = Sequence() + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(self.TIME) + self.timer.countdown(self.TIME, self.timerExpired) + self.mapModel.show() + self.mapAvatars = {} + avatarScale = 0.025 * self.mapScaleRatio + for avId in self.remoteAvIdList: + avatar = base.cr.doId2do.get(avId, False) + if avatar != False: + self.mapAvatars[avId] = LaffMeter.LaffMeter(avatar.style, avatar.hp, avatar.maxHp) + self.mapAvatars[avId].reparentTo(self.mapModel) + self.mapAvatars[avId].setScale(avatarScale) + self.mapAvatars[avId].start() + + avatar = base.cr.doId2do[self.localAvId] + self.mapAvatars[self.localAvId] = LaffMeter.LaffMeter(avatar.style, avatar.hp, avatar.maxHp) + self.mapAvatars[self.localAvId].reparentTo(self.mapModel) + self.mapAvatars[self.localAvId].setScale(avatarScale) + self.mapAvatars[self.localAvId].start() + self.accept('resetClock', self.__resetClock) + self.__spawnUpdateLocalToonTask() + self.__spawnCrabTask() + self.__spawnTreasureBoundsTask() + + def __resetClock(self, tOffset): + self.notify.debug('resetClock') + self.gameStartTime += tOffset + self.timer.countdown(self.timer.currentTime + tOffset, self.timerExpired) + + def timerExpired(self): + self.notify.debug('local timer expired') + self.dead = 1 + self.gameOver() + + def __initPosBroadcast(self): + self.__posBroadcastPeriod = 0.2 + self.__timeSinceLastPosBroadcast = 0.0 + self.__lastPosBroadcast = self.getAvatar(self.localAvId).getPos() + self.__storeStop = 0 + lt = self.getAvatar(self.localAvId) + lt.d_clearSmoothing() + lt.sendCurrentPosition() + + def __posBroadcast(self, dt): + self.__timeSinceLastPosBroadcast += dt + if self.__timeSinceLastPosBroadcast > self.__posBroadcastPeriod: + self.__timeSinceLastPosBroadcast -= self.__posBroadcastPeriod + self.getAvatar(self.localAvId).cnode.broadcastPosHprFull() + + def __spawnTreasureBoundsTask(self): + taskMgr.remove(self.TREASURE_BOUNDS_TASK) + taskMgr.add(self.__treasureBoundsTask, self.TREASURE_BOUNDS_TASK) + + def __killTreasureBoundsTask(self): + taskMgr.remove(self.TREASURE_BOUNDS_TASK) + + def __treasureBoundsTask(self, task): + for i in xrange(self.NUMTREASURES): + self.chestIcons[i].setPos(self.treasures[i].chest.getPos(render) / self.MAP_DIV) + self.chestIcons[i].setZ(self.chestIcons[i].getZ() + self.MAP_OFF) + if self.treasures[i].treasureNode.getZ() < -36: + self.treasures[i].treasureNode.setZ(-36) + if self.treasures[i].treasureNode.getX() < -20: + self.treasures[i].treasureNode.setX(-20) + if self.treasures[i].treasureNode.getX() > 20: + self.treasures[i].treasureNode.setX(20) + + return Task.cont + + def incrementScore(self, avId, newSpot, timestamp): + if not self.hasLocalToon: + return + newSpot += -15 + ts = globalClockDelta.localElapsedTime(timestamp) + toonSD = self.toonSDs[avId] + if avId == self.localAvId: + self.reachedFlag = 0 + if toonSD.status == 'treasure' and self.treasures and self.chestIcons: + for i in xrange(self.NUMTREASURES): + if self.treasures[i].grabbedId == avId: + self.treasures[i].treasureNode.wrtReparentTo(render) + self.treasures[i].grabbedId = 0 + seq = Sequence() + shrink = LerpScaleInterval(self.treasures[i].treasureNode, duration=1.0, startScale=self.treasures[i].treasureNode.getScale(), scale=Vec3(0.001, 0.001, 0.001), blendType='easeIn') + shrinkIcon = LerpScaleInterval(self.chestIcons[i], duration=1.0, startScale=self.chestIcons[i].getScale(), scale=Vec3(0.001, 0.001, 0.001), blendType='easeIn') + jump = ProjectileInterval(self.treasures[i].treasureNode, duration=1.0, startPos=self.treasures[i].treasureNode.getPos(), endPos=Point3(0, 0, 40), gravityMult=0.7) + shrinkJump = Parallel(shrink, shrinkIcon, jump) + toonSD.fsm.request('normal') + grow = LerpScaleInterval(self.treasures[i].treasureNode, duration=1.0, scale=self.treasures[i].treasureNode.getScale(), startScale=Vec3(0.001, 0.001, 0.001), blendType='easeIn') + growIcon = LerpScaleInterval(self.chestIcons[i], duration=1.0, scale=self.chestIcons[i].getScale(), startScale=Vec3(0.001, 0.001, 0.001), blendType='easeIn') + place = Parallel(Func(self.treasures[i].treasureNode.setPos, Vec3(newSpot, 0.25, -36)), Func(self.treasures[i].treasureNode.setHpr, Vec3(0, 0, 0))) + growItems = Parallel(grow, growIcon) + resetChest = Func(self.treasures[i].chestNode.setIntoCollideMask, DivingGameGlobals.CollideMask) + seq = Sequence(shrinkJump, Wait(1.5), place, growItems, resetChest) + self.treasures[i].moveLerp.pause() + self.treasures[i].moveLerp = seq + self.treasures[i].moveLerp.start(ts) + self.playSound('dropGold') + self.treasurePanel.incrScore() + + def __boatReached(self, collEntry): + toonSD = self.toonSDs[self.localAvId] + if toonSD.status == 'treasure' and not self.reachedFlag: + self.sendUpdate('treasureRecovered') + self.reachedFlag = 1 + + def __treasureGrabbed(self, collEntry): + avId = int(collEntry.getFromNodePath().getName()) + chestId = int(collEntry.getIntoNodePath().getName()) + toonSD = self.toonSDs[avId] + if toonSD.status == 'normal' and self.grabbingTreasure == -1: + self.grabbingTreasure = chestId + self.sendUpdate('pickupTreasure', [chestId]) + + def setTreasureDropped(self, avId, timestamp): + if not hasattr(self, 'treasures'): + return + ts = globalClockDelta.localElapsedTime(timestamp) + for i in xrange(self.NUMTREASURES): + if self.treasures[i].grabbedId == avId: + self.treasures[i].grabbedId = 0 + toonSD = self.toonSDs[avId] + dist = abs(36.0 + self.treasures[i].treasureNode.getZ(render)) + delta = dist / 72.0 + dur = 10 * delta + self.treasures[i].treasureNode.wrtReparentTo(render) + self.treasures[i].chestNode.setIntoCollideMask(BitMask32.allOff()) + resetChest = Func(self.treasures[i].chestNode.setIntoCollideMask, DivingGameGlobals.CollideMask) + self.treasures[i].moveLerp.pause() + self.treasures[i].moveLerp = Parallel(Sequence(Wait(1.0), resetChest), LerpFunc(self.treasures[i].treasureNode.setZ, duration=dur, fromData=self.treasures[i].treasureNode.getZ(render), toData=-36, blendType='easeIn')) + self.treasures[i].moveLerp.start(ts) + + def performCrabCollision(self, avId, timestamp): + if not self.hasLocalToon: + return + ts = globalClockDelta.localElapsedTime(timestamp) + toonSD = self.toonSDs[avId] + toon = self.getAvatar(avId) + distance = base.localAvatar.getDistance(toon) + volume = 0 + soundRange = 15.0 + if distance < soundRange: + volume = (soundRange - distance) / soundRange + if toonSD.status == 'normal' or toonSD.status == 'treasure': + self.localLerp.finish() + self.localLerp = Sequence(Func(toonSD.fsm.request, 'freeze'), Wait(3.0), Func(toonSD.fsm.request, 'normal')) + self.localLerp.start(ts) + self.hitSound.play() + self.hitSound.setVolume(volume) + + def performFishCollision(self, avId, spawnId, spawnerId, timestamp): + if not hasattr(self, 'spawners'): + return + toonSD = self.toonSDs[avId] + ts = globalClockDelta.localElapsedTime(timestamp) + toon = self.getAvatar(avId) + distance = base.localAvatar.getDistance(toon) + volume = 0 + soundRange = 15.0 + if distance < soundRange: + volume = (soundRange - distance) / soundRange + if toonSD.status == 'normal' or toonSD.status == 'treasure': + self.localLerp.finish() + self.localLerp = Sequence(Func(toonSD.fsm.request, 'freeze'), Wait(3.0), Func(toonSD.fsm.request, 'normal')) + self.localLerp.start(ts) + if spawnId in self.spawners[spawnerId].fishArray: + fish = self.spawners[spawnerId].fishArray[spawnId] + endX = self.spawners[spawnerId].position.getX() + if fish.name == 'clown': + fishSoundName = 'Clownfish.ogg' + elif fish.name == 'pbj': + fishSoundName = 'PBJ_Fish.ogg' + elif fish.name == 'balloon': + fishSoundName = 'BalloonFish.ogg' + elif fish.name == 'bear': + fishSoundName = 'Bear_Acuda.ogg' + elif fish.name == 'nurse': + fishSoundName = 'Nurse_Shark.ogg' + elif fish.name == 'piano': + fishSoundName = 'Piano_Tuna.ogg' + else: + fishSoundName = ' ' + fishSoundPath = 'phase_4/audio/sfx/%s' % fishSoundName + fish.sound = loader.loadSfx(fishSoundPath) + if fish.sound: + fish.sound.play() + fish.sound.setVolume(volume) + self.hitSound.play() + self.hitSound.setVolume(volume) + if fish.name is 'bear' or fish.name is 'nurse': + return + colList = fish.findAllMatches('**/fc*') + for col in colList: + col.removeNode() + + fish.moveLerp.pause() + if fish.name == 'clown' or fish.name == 'piano': + if fish.name != 'piano': + endHpr = Vec3(fish.getH() * -1, 0, 0) + elif fish.direction == -1: + endHpr = Vec3(180, 0, 0) + else: + endHpr = Vec3(0, 0, 0) + fish.moveLerp = Sequence(LerpHprInterval(fish, duration=0.4, startHpr=fish.getHpr(), hpr=endHpr), LerpFunc(fish.setX, duration=1.5, fromData=fish.getX(), toData=endX), Func(self.fishRemove, str(spawnerId) + str(spawnId))) + elif fish.name == 'pbj': + fish.moveLerp = Sequence(LerpFunc(fish.setX, duration=2, fromData=fish.getX(), toData=endX), Func(self.fishRemove, str(spawnerId) + str(spawnId))) + elif fish.name == 'balloon': + fish.specialLerp.pause() + anim = Func(fish.play, 'anim', fromFrame=110, toFrame=200) + fish.setH(180) + speed = Func(fish.setPlayRate, 3.0, 'anim') + fish.moveLerp = Sequence(Func(fish.stop, 'anim'), speed, anim, Wait(1.0), LerpScaleInterval(fish, duration=0.8, startScale=fish.getScale, scale=0.001, blendType='easeIn'), Func(self.fishRemove, str(spawnerId) + str(spawnId))) + fish.sound.setTime(11.5) + fish.moveLerp.start(ts) + + def fishRemove(self, code): + spawnId = int(code[1:len(code)]) + spawnerId = int(code[0]) + if spawnId in self.spawners[spawnerId].fishArray: + fish = self.spawners[spawnerId].fishArray[spawnId] + fish.specialLerp.finish() + fish.moveLerp.finish() + fish.specialLerp = None + fish.moveLerp = None + fish.removeNode() + del fish + del self.spawners[spawnerId].fishArray[spawnId] + return + + def setTreasureGrabbed(self, avId, chestId): + if not self.hasLocalToon: + return + if self.grabbingTreasure == chestId: + self.grabbingTreasure = -1 + toonSD = self.toonSDs.get(avId) + if toonSD and toonSD.status == 'normal': + toonSD.fsm.request('treasure') + self.treasures[chestId].moveLerp.pause() + self.treasures[chestId].moveLerp = Sequence() + self.treasures[chestId].chestNode.setIntoCollideMask(BitMask32.allOff()) + self.treasures[chestId].treasureNode.reparentTo(self.getAvatar(avId)) + headparts = self.getAvatar(avId).getHeadParts() + pos = headparts[2].getPos() + self.treasures[chestId].treasureNode.setPos(pos + Point3(0, 0.2, 3)) + self.treasures[chestId].grabbedId = avId + timestamp = globalClockDelta.getFrameNetworkTime() + self.playSound('getGold') + + def __spawnCrabTask(self): + taskMgr.remove(self.CRAB_TASK) + taskMgr.add(self.__crabTask, self.CRAB_TASK) + + def __killCrabTask(self): + taskMgr.remove(self.CRAB_TASK) + + def __crabTask(self, task): + dt = globalClock.getDt() + for crab in self.crabs: + if not crab.moveLerp.isPlaying(): + crab.moveLerp = Wait(1.0) + crab.moveLerp.loop() + self.sendUpdate('getCrabMoving', [crab.crabId, crab.getX(), crab.direction]) + return Task.cont + + return Task.cont + + def setCrabMoving(self, crabId, timestamp, rand1, rand2, crabX, dir): + if self.dead == 1: + self.__killCrabTask() + return + if not hasattr(self, 'crabs'): + return + crab = self.crabs[crabId] + ts = globalClockDelta.localElapsedTime(timestamp) + x = 0 + for i in xrange(self.NUMTREASURES): + x += self.treasures[i].treasureNode.getX(render) + + x /= self.NUMTREASURES + goalX = int(x + dir * (rand1 / 10.0) * 12 + 4.0) + goalZ = -40 + 5 + 5 * (rand2 / 10.0) + xTime = 1 + rand1 / 10.0 * 2 + zTime = 0.5 + rand2 / 10.0 + wait = rand1 / 10.0 + rand2 / 10.0 + 1 + crab.direction *= -1 + if goalX > 20: + goalX = 20 + elif goalX < -20: + goalX = 20 + loc = crab.getPos(render) + distance = base.localAvatar.getDistance(crab) + crabVolume = 0 + soundRange = 25.0 + if distance < soundRange: + crabVolume = (soundRange - distance) / soundRange + crabSoundInterval = SoundInterval(self.crabSound, loop=0, duration=1.6, startTime=0.0) + seq = Sequence(Wait(wait), LerpPosInterval(crab, duration=xTime, startPos=Point3(crabX, 0, -40), pos=Point3(goalX, 0, -40), blendType='easeIn'), Parallel(Func(self.grabCrapVolume, crab), LerpPosInterval(crab, duration=zTime, startPos=Point3(goalX, 0, -40), pos=Point3(goalX, 0, goalZ), blendType='easeOut')), LerpPosInterval(crab, duration=zTime, startPos=Point3(goalX, 0, goalZ), pos=Point3(goalX, 0, -40), blendType='easeInOut')) + crab.moveLerp.pause() + crab.moveLerp = seq + crab.moveLerp.start(ts) + + def grabCrapVolume(self, crab): + if crab: + distance = base.localAvatar.getDistance(crab) + self.crabVolume = 0 + soundRange = 25.0 + if distance < soundRange: + crabVolume = (soundRange - distance) / soundRange + crabSoundInterval = SoundInterval(self.crabSound, loop=0, duration=1.6, startTime=0.0, volume=crabVolume) + crabSoundInterval.start() + + def __spawnUpdateLocalToonTask(self): + self.__initPosBroadcast() + taskMgr.remove(self.UPDATE_LOCALTOON_TASK) + taskMgr.add(self.__updateLocalToonTask, self.UPDATE_LOCALTOON_TASK) + + def __killUpdateLocalToonTask(self): + taskMgr.remove(self.UPDATE_LOCALTOON_TASK) + + def __updateLocalToonTask(self, task): + dt = globalClock.getDt() + toonPos = base.localAvatar.getPos() + toonHpr = base.localAvatar.getHpr() + self.xVel *= 0.99 + self.zVel *= 0.99 + pos = [toonPos[0], toonPos[1], toonPos[2]] + hpr = [toonHpr[0], toonHpr[1], toonHpr[2]] + r = 0 + toonSD = self.toonSDs[self.localAvId] + if toonSD.status == 'normal' or toonSD.status == 'treasure': + if self.arrowKeys.leftPressed(): + r -= 80 + if self.arrowKeys.rightPressed(): + r += 80 + hpr[2] += r * dt + pos1 = self.orientNode.getPos(render) + pos2 = self.orientNode2.getPos(render) + upVec = Vec2(pos1[0], pos1[2]) + bkVec = Vec2(pos2[0], pos2[2]) + forVec = upVec - Vec2(pos[0], pos[2]) + bckVec = bkVec - Vec2(pos[0], pos[2]) + r = 0 + if self.arrowKeys.upPressed(): + r += 20 + self.xVel = forVec[0] * 8 + self.zVel = forVec[1] * 8 + elif self.arrowKeys.downPressed(): + r -= 20 + self.xVel = bckVec[0] * 4 + self.zVel = bckVec[1] * 4 + if self.xVel > 20: + self.xVel = 20 + elif self.xVel < -20: + self.xVel = -20 + if self.zVel > 10: + self.zVel = 10 + elif self.zVel < -10: + self.zVel = -10 + swimVolume = (abs(self.zVel) + abs(self.xVel)) / 15.0 + self.swimSound.setVolume(swimVolume) + pos[0] += self.xVel * dt + pos[1] = -2 + pos[2] += self.zVel * dt + found = 0 + for i in xrange(self.NUMTREASURES): + if self.treasures[i].grabbedId == self.localAvId: + found = 1 + i = self.NUMTREASURES + 1 + pos[2] -= 0.8 * dt + + if found == 0: + pos[2] += 0.8 * dt + if pos[2] < -38: + pos[2] = -38 + elif pos[2] > 36: + pos[2] = 36 + if pos[0] < -20: + pos[0] = -20 + elif pos[0] > 20: + pos[0] = 20 + base.localAvatar.setPos(pos[0], pos[1], pos[2]) + base.localAvatar.setHpr(hpr[0], hpr[1], hpr[2]) + posDiv = self.MAP_DIV + self.mapAvatars[self.localAvId].setPos(pos[0] / posDiv, pos[1] / posDiv, pos[2] / posDiv + self.MAP_OFF) + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + pos = toon.getPos() + self.mapAvatars[avId].setPos(pos / posDiv) + self.mapAvatars[avId].setZ(self.mapAvatars[avId].getZ() + self.MAP_OFF) + + self.cTrav.traverse(render) + self.cTrav2.traverse(render) + self.__posBroadcast(dt) + z = self.getAvatar(self.localAvId).getZ() + 3 + camBottom = math.tan(base.camLens.getVfov()/2.0*math.pi/180)*54 + z = max(z, -42+camBottom) + camera.setZ(z) + ambVolume = abs(z - 25.0) / 50.0 + 0.1 + if ambVolume < 0.0: + ambVolume = 0.0 + if ambVolume > 1.0: + ambVolume = 1.0 + ambVolume = pow(ambVolume, 0.75) + self.sndAmbience.setVolume(ambVolume) + return Task.cont + + def exitSwim(self): + self.music.stop() + self.ignore('resetClock') + self.__killUpdateLocalToonTask() + self.__killCrabTask() + self.__killTreasureBoundsTask() + self.timer.stop() + self.timer.destroy() + self.localLerp.finish() + self.introMovie.finish() + self.boatTilt.finish() + self.treasurePanel.cleanup() + self.mapAvatars[self.localAvId].destroy() + del self.mapAvatars + for i in xrange(self.NUMTREASURES): + del self.chestIcons[i] + + del self.timer + + def enterCleanup(self): + pass + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedDivingGameAI.py b/toontown/minigame/DistributedDivingGameAI.py new file mode 100755 index 00000000..06cd53e9 --- /dev/null +++ b/toontown/minigame/DistributedDivingGameAI.py @@ -0,0 +1,464 @@ +from DistributedMinigameAI import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.actor import Actor +import DivingGameGlobals +import random +import random +import types + +class DistributedDivingGameAI(DistributedMinigameAI): + fishProportions = [] + for i in xrange(6): + fishProportions.append([]) + + n = 100 + fishProportions[0] + fishProportions[0].append(([0, 0.8], + [0.8, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[0].append(([0, 0.8], + [0.8, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[0].append(([0, 0.7], + [0.7, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[0].append(([0, 0.7], + [0.7, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[0].append(([0, 0.5], + [0.5, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[0].append(([n, 0.5], + [0.5, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[1] + fishProportions[1].append(([0, 0.8], + [0.8, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[1].append(([0, 0.8], + [0.8, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[1].append(([0, 0.7], + [0.7, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[1].append(([0, 0.7], + [0.7, 0.9], + [n, n], + [n, n], + [n, n], + [0.9, 1])) + fishProportions[1].append(([0, 0.4], + [0.4, 0.8], + [n, n], + [n, n], + [n, n], + [0.8, 1])) + fishProportions[1].append(([n, 0.3], + [0.3, 0.6], + [n, n], + [n, n], + [n, n], + [0.6, 1])) + fishProportions[2] + fishProportions[2].append(([0, 0.7], + [0.7, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[2].append(([0, 0.6], + [0.6, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[2].append(([0, 0.6], + [0.6, 0.8], + [n, n], + [0.8, 1], + [n, n], + [n, n])) + fishProportions[2].append(([0, 0.5], + [0.5, 0.7], + [n, n], + [0.7, 0.9], + [n, n], + [0.9, 1])) + fishProportions[2].append(([0, 0.2], + [0.2, 0.4], + [n, n], + [0.4, 0.75], + [n, n], + [0.75, 1])) + fishProportions[2].append(([n, 0.2], + [0.2, 0.6], + [n, n], + [0.6, 0.8], + [n, n], + [0.8, 1])) + fishProportions[3] + fishProportions[3].append(([0, 0.7], + [0.7, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[3].append(([0, 0.6], + [0.6, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[3].append(([0, 0.6], + [0.6, 0.8], + [n, n], + [0.95, 1], + [n, n], + [n, n])) + fishProportions[3].append(([0, 0.5], + [0.5, 0.7], + [n, n], + [0.7, 0.85], + [0.9, 0.95], + [0.95, 1])) + fishProportions[3].append(([0, 0.2], + [0.2, 0.4], + [n, n], + [0.4, 0.75], + [0.75, 0.85], + [0.85, 1])) + fishProportions[3].append(([n, 0.2], + [0.2, 0.6], + [n, n], + [0.6, 0.8], + [n, n], + [0.8, 1])) + fishProportions[4] + fishProportions[4].append(([0, 0.7], + [0.7, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[4].append(([0, 0.45], + [0.45, 0.9], + [n, n], + [0.9, 1], + [n, n], + [n, n])) + fishProportions[4].append(([0, 0.2], + [0.2, 0.5], + [n, n], + [0.5, 0.95], + [0.95, 1], + [n, n])) + fishProportions[4].append(([0, 0.1], + [0.1, 0.3], + [n, n], + [0.3, 0.75], + [0.75, 0.8], + [0.8, 1])) + fishProportions[4].append(([n, n], + [0, 0.15], + [n, n], + [0.15, 0.4], + [n, n], + [0.4, 1])) + fishProportions[4].append(([n, n], + [n, n], + [n, n], + [0, 0.4], + [n, n], + [0.6, 1])) + fishProportions[5] + fishProportions[5].append(([0, 0.7], + [0.7, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[5].append(([0, 0.45], + [0.45, 0.9], + [n, n], + [0.9, 1], + [n, n], + [n, n])) + fishProportions[5].append(([0, 0.2], + [0.2, 0.5], + [n, n], + [0.5, 0.95], + [0.95, 1], + [n, n])) + fishProportions[5].append(([0, 0.1], + [0.1, 0.3], + [n, n], + [0.3, 0.75], + [0.75, 0.8], + [0.8, 1])) + fishProportions[5].append(([n, n], + [0, 0.15], + [n, n], + [0.15, 0.4], + [n, n], + [0.4, 1])) + fishProportions[5].append(([n, n], + [n, n], + [n, n], + [0, 0.4], + [n, n], + [0.6, 1])) + difficultyPatternsAI = {ToontownGlobals.ToontownCentral: [3.5, fishProportions[0], 1.5], + ToontownGlobals.DonaldsDock: [3.0, fishProportions[1], 1.8], + ToontownGlobals.DaisyGardens: [2.5, fishProportions[2], 2.1], + ToontownGlobals.MinniesMelodyland: [2.0, fishProportions[3], 2.4], + ToontownGlobals.TheBrrrgh: [2.0, fishProportions[4], 2.7], + ToontownGlobals.DonaldsDreamland: [1.5, fishProportions[5], 3.0]} + + def __init__(self, air, minigameId): + try: + self.DistributedDivingGameAI_initialized + except: + self.DistributedDivingGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedDivingGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['swimming']), State.State('swimming', self.enterSwimming, self.exitSwimming, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.__timeBase = globalClockDelta.localToNetworkTime(globalClock.getRealTime()) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + self.sendUpdate('setTrolleyZone', [self.trolleyZone]) + for avId in self.scoreDict.keys(): + self.scoreDict[avId] = 0 + + self.treasureHolders = [0] * self.numPlayers + self.SPAWNTIME = self.difficultyPatternsAI[self.getSafezoneId()][0] + self.proportion = self.difficultyPatternsAI[self.getSafezoneId()][1] + self.REWARDMOD = self.difficultyPatternsAI[self.getSafezoneId()][2] + DistributedMinigameAI.setGameReady(self) + self.spawnings = [] + for i in xrange(DivingGameGlobals.NUM_SPAWNERS): + self.spawnings.append(Sequence(Func(self.spawnFish, i), Wait(self.SPAWNTIME + random.random()), Func(self.spawnFish, i), Wait(self.SPAWNTIME - 0.5 + random.random()))) + self.spawnings[i].loop() + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('swimming') + self.scoreTracking = {} + for avId in self.scoreDict.keys(): + self.scoreTracking[avId] = [0, + 0, + 0, + 0, + 0] + + def getCrabMoving(self, crabId, crabX, dir): + timestamp = globalClockDelta.getFrameNetworkTime() + rand1 = int(random.random() * 10) + rand2 = int(random.random() * 10) + self.sendUpdate('setCrabMoving', [crabId, + timestamp, + rand1, + rand2, + crabX, + dir]) + + def treasureRecovered(self): + if not hasattr(self, 'scoreTracking'): + return + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'DivingGameAI.treasureRecovered: invalid avId') + return + + if avId not in self.treasureHolders: + self.air.writeServerEvent('suspicious', avId, 'DivingGameAI.treasureRecovered: tried to recover without holding treasure') + return + + self.treasureHolders[self.treasureHolders.index(avId)] = 0 + + timestamp = globalClockDelta.getFrameNetworkTime() + newSpot = int(random.random() * 30) + self.scoreTracking[avId][4] += 1 + for someAvId in self.scoreDict.keys(): + if someAvId == avId: + self.scoreDict[avId] += 10 * (self.REWARDMOD * 0.25) + self.scoreDict[someAvId] += 10 * (self.REWARDMOD * 0.75 / float(len(self.scoreDict.keys()))) + + self.sendUpdate('incrementScore', [avId, newSpot, timestamp]) + + def hasScoreMult(self): + return 0 + + def setGameAbort(self): + self.notify.debug('setGameAbort') + taskMgr.remove(self.taskName('gameTimer')) + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + trackingString = 'MiniGame Stats : Diving Game' + trackingString += '\nDistrict:%s' % self.getSafezoneId() + for avId in self.scoreTracking.keys(): + trackingString = trackingString + '\navId:%s fishHits:%s crabHits:%s treasureCatches:%s treasureDrops:%s treasureRecoveries:%s Score: %s' % (avId, + self.scoreTracking[avId][0], + self.scoreTracking[avId][1], + self.scoreTracking[avId][2], + self.scoreTracking[avId][3], + self.scoreTracking[avId][4], + self.scoreDict[avId]) + + self.air.writeServerEvent('MiniGame Stats', None, trackingString) + return + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def getTimeBase(self): + return self.__timeBase + + def enterSwimming(self): + self.notify.debug('enterSwimming') + duration = 65.0 + taskMgr.doMethodLater(duration, self.timerExpired, self.taskName('gameTimer')) + + def timerExpired(self, task): + self.notify.debug('timer expired') + for avId in self.scoreDict.keys(): + if self.scoreDict[avId] < 5: + self.scoreDict[avId] = 5 + + self.gameOver() + return Task.done + + def exitSwimming(self): + for i in xrange(DivingGameGlobals.NUM_SPAWNERS): + self.spawnings[i].pause() + + def enterCleanup(self): + self.notify.debug('enterCleanup') + for i in xrange(DivingGameGlobals.NUM_SPAWNERS): + self.spawnings[i].finish() + + del self.spawnings + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def pickupTreasure(self, chestId): + if not hasattr(self, 'scoreTracking'): + return + timestamp = globalClockDelta.getFrameNetworkTime() + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'DivingGameAI.pickupTreasure: invalid avId') + return + if avId in self.treasureHolders: + self.air.writeServerEvent('suspicious', avId, 'DivingGameAI.pickupTreasure: already holding treasure') + return + if not (0 <= chestId < len(self.treasureHolders)): + self.air.writeServerEvent('suspicious', avId, 'DivingGameAI.pickupTreasure: invalid chest requested (#%d)' % chestId) + return + if self.treasureHolders[chestId]: + # This chest is already held by someone else. Because this can happen + # during normal play (race conditions if two Toons swim into the treasure + # simultaneously) we do not log a suspicious event and silently ignore it. + return + self.scoreTracking[avId][2] += 1 + self.treasureHolders[chestId] = avId + self.sendUpdate('setTreasureGrabbed', [avId, chestId]) + + def spawnFish(self, spawnerId): + timestamp = globalClockDelta.getFrameNetworkTime() + props = self.proportion[spawnerId] + num = random.random() + for i in xrange(len(props)): + prop = props[i] + low = prop[0] + high = prop[1] + if num > low and num <= high: + offset = int(10 * random.random()) + self.sendUpdate('fishSpawn', [timestamp, + i, + spawnerId, + offset]) + return + + def handleCrabCollision(self, avId, status): + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'DivingGameAI.handleCrabCollision: invalid avId') + return + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setTreasureDropped', [avId, timestamp]) + self.scoreTracking[avId][1] += 1 + if status == 'normal' or status == 'treasure': + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('performCrabCollision', [avId, timestamp]) + if status == 'treasure': + if avId in self.treasureHolders: + self.treasureHolders[self.treasureHolders.index(avId)] = 0 + self.scoreTracking[avId][3] += 1 + else: + self.air.writeServerEvent('suspicious', avId, 'DivingGameAI.handleCrabCollision: reported "treasure drop" without holding treasure') + + def handleFishCollision(self, avId, spawnId, spawnerId, status): + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'DivingGameAI.handleFishCollision: invalid avId') + return + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setTreasureDropped', [avId, timestamp]) + timestamp = globalClockDelta.getFrameNetworkTime() + self.scoreTracking[avId][0] += 1 + if status == 'treasure': + if avId in self.treasureHolders: + self.treasureHolders[self.treasureHolders.index(avId)] = 0 + self.scoreTracking[avId][3] += 1 + else: + self.air.writeServerEvent('suspicious', avId, 'DivingGameAI.handleFishCollision: reported "treasure drop" without holding treasure') + self.sendUpdate('performFishCollision', [avId, + spawnId, + spawnerId, + timestamp]) diff --git a/toontown/minigame/DistributedIceGame.py b/toontown/minigame/DistributedIceGame.py new file mode 100755 index 00000000..7bef3d7c --- /dev/null +++ b/toontown/minigame/DistributedIceGame.py @@ -0,0 +1,1061 @@ +import math +from pandac.PandaModules import Vec3, deg2Rad, Point3, NodePath, VBase4, CollisionHandlerEvent, CollisionNode, CollisionSphere +from direct.fsm import ClassicFSM, State +from direct.distributed.ClockDelta import globalClockDelta +from direct.gui.DirectGui import DirectLabel +from direct.interval.IntervalGlobal import Sequence, LerpScaleInterval, LerpFunctionInterval, Func, Parallel, LerpPosInterval, Wait, SoundInterval, LerpColorScaleInterval +from toontown.toonbase import ToontownGlobals, TTLocalizer, ToontownTimer +from toontown.minigame import ArrowKeys +from toontown.minigame import DistributedMinigame +from toontown.minigame import DistributedIceWorld +from toontown.minigame import IceGameGlobals +from toontown.minigame import MinigameAvatarScorePanel +from toontown.minigame import IceTreasure + +class DistributedIceGame(DistributedMinigame.DistributedMinigame, DistributedIceWorld.DistributedIceWorld): + notify = directNotify.newCategory('DistributedIceGame') + MaxLocalForce = 100 + MaxPhysicsForce = 25000 + + def __init__(self, cr): + DistributedMinigame.DistributedMinigame.__init__(self, cr) + DistributedIceWorld.DistributedIceWorld.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedIceGame', [State.State('off', self.enterOff, self.exitOff, ['inputChoice']), + State.State('inputChoice', self.enterInputChoice, self.exitInputChoice, ['waitServerChoices', + 'moveTires', + 'displayVotes', + 'cleanup']), + State.State('waitServerChoices', self.enterWaitServerChoices, self.exitWaitServerChoices, ['moveTires', 'cleanup']), + State.State('moveTires', self.enterMoveTires, self.exitMoveTires, ['synch', 'cleanup']), + State.State('synch', self.enterSynch, self.exitSynch, ['inputChoice', 'scoring', 'cleanup']), + State.State('scoring', self.enterScoring, self.exitScoring, ['cleanup', 'finalResults', 'inputChoice']), + State.State('finalResults', self.enterFinalResults, self.exitFinalResults, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.cameraThreeQuarterView = (0, -22, 45, 0, -62.89, 0) + self.tireDict = {} + self.forceArrowDict = {} + self.canDrive = False + self.timer = None + self.timerStartTime = None + self.curForce = 0 + self.curHeading = 0 + self.headingMomentum = 0.0 + self.forceMomentum = 0.0 + self.allTireInputs = None + self.curRound = 0 + self.curMatch = 0 + self.controlKeyWarningLabel = DirectLabel(text=TTLocalizer.IceGameControlKeyWarning, text_fg=VBase4(1, 0, 0, 1), relief=None, pos=(0.0, 0, 0), scale=0.15) + self.controlKeyWarningLabel.hide() + self.waitingMoveLabel = DirectLabel(text=TTLocalizer.WaitingForOtherToons, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075) + self.waitingMoveLabel.hide() + self.waitingSyncLabel = DirectLabel(text=TTLocalizer.WaitingForOtherToons, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075) + self.waitingSyncLabel.hide() + self.infoLabel = DirectLabel(text='', text_fg=VBase4(0, 0, 0, 1), relief=None, pos=(0.0, 0, 0.7), scale=0.075) + self.updateInfoLabel() + self.lastForceArrowUpdateTime = 0 + self.sendForceArrowUpdateAsap = False + self.treasures = [] + self.penalties = [] + self.obstacles = [] + self.controlKeyPressed = False + self.controlKeyWarningIval = None + return + + def delete(self): + DistributedIceWorld.DistributedIceWorld.delete(self) + DistributedMinigame.DistributedMinigame.delete(self) + if self.controlKeyWarningIval: + self.controlKeyWarningIval.finish() + self.controlKeyWarningIval = None + self.controlKeyWarningLabel.destroy() + del self.controlKeyWarningLabel + self.waitingMoveLabel.destroy() + del self.waitingMoveLabel + self.waitingSyncLabel.destroy() + del self.waitingSyncLabel + self.infoLabel.destroy() + del self.infoLabel + for treasure in self.treasures: + treasure.destroy() + + del self.treasures + for penalty in self.penalties: + penalty.destroy() + + del self.penalties + for obstacle in self.obstacles: + obstacle.removeNode() + + del self.obstacles + del self.gameFSM + return + + def announceGenerate(self): + DistributedMinigame.DistributedMinigame.announceGenerate(self) + DistributedIceWorld.DistributedIceWorld.announceGenerate(self) + self.debugTaskName = self.uniqueName('debugTask') + + def getTitle(self): + return TTLocalizer.IceGameTitle + + def getInstructions(self): + szId = self.getSafezoneId() + numPenalties = IceGameGlobals.NumPenalties[szId] + result = TTLocalizer.IceGameInstructions + if numPenalties == 0: + result = TTLocalizer.IceGameInstructionsNoTnt + return result + + def getMaxDuration(self): + return 0 + + def load(self): + self.notify.debug('load') + DistributedMinigame.DistributedMinigame.load(self) + self.music = base.loadMusic('phase_4/audio/bgm/MG_IceGame.ogg') + self.gameBoard = loader.loadModel('phase_4/models/minigames/ice_game_icerink') + background = loader.loadModel('phase_4/models/minigames/ice_game_2d') + backgroundWide = loader.loadModel('phase_4/models/minigames/iceslide_ground') + background.reparentTo(self.gameBoard) + backgroundWide.reparentTo(self.gameBoard) + backgroundWide.setPos(0, -0.3, -0.5) + self.gameBoard.setPosHpr(0, 0, 0, 0, 0, 0) + self.gameBoard.setScale(1.0) + self.setupSimulation() + index = 0 + for avId in self.avIdList: + self.setupTire(avId, index) + self.setupForceArrow(avId) + index += 1 + + for index in xrange(len(self.avIdList), 4): + self.setupTire(-index, index) + self.setupForceArrow(-index) + + self.showForceArrows(realPlayersOnly=True) + self.westWallModel = NodePath() + if not self.westWallModel.isEmpty(): + self.westWallModel.reparentTo(self.gameBoard) + self.westWallModel.setPos(IceGameGlobals.MinWall[0], IceGameGlobals.MinWall[1], 0) + self.westWallModel.setScale(4) + self.eastWallModel = NodePath() + if not self.eastWallModel.isEmpty(): + self.eastWallModel.reparentTo(self.gameBoard) + self.eastWallModel.setPos(IceGameGlobals.MaxWall[0], IceGameGlobals.MaxWall[1], 0) + self.eastWallModel.setScale(4) + self.eastWallModel.setH(180) + self.arrowKeys = ArrowKeys.ArrowKeys() + self.target = loader.loadModel('phase_3/models/misc/sphere') + self.target.setScale(0.01) + self.target.reparentTo(self.gameBoard) + self.target.setPos(0, 0, 0) + self.scoreCircle = loader.loadModel('phase_4/models/minigames/ice_game_score_circle') + self.scoreCircle.setScale(0.01) + self.scoreCircle.reparentTo(self.gameBoard) + self.scoreCircle.setZ(IceGameGlobals.TireRadius / 2.0) + self.scoreCircle.setAlphaScale(0.5) + self.scoreCircle.setTransparency(1) + self.scoreCircle.hide() + self.treasureModel = loader.loadModel('phase_4/models/minigames/ice_game_barrel') + self.penaltyModel = loader.loadModel('phase_4/models/minigames/ice_game_tnt2') + self.penaltyModel.setScale(0.75, 0.75, 0.7) + szId = self.getSafezoneId() + obstacles = IceGameGlobals.Obstacles[szId] + index = 0 + cubicObstacle = IceGameGlobals.ObstacleShapes[szId] + for pos in obstacles: + newPos = Point3(pos[0], pos[1], IceGameGlobals.TireRadius) + newObstacle = self.createObstacle(newPos, index, cubicObstacle) + self.obstacles.append(newObstacle) + index += 1 + + self.countSound = loader.loadSfx('phase_3.5/audio/sfx/tick_counter.ogg') + self.treasureGrabSound = loader.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_bananas.ogg') + self.penaltyGrabSound = loader.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.ogg') + self.tireSounds = [] + for tireIndex in xrange(4): + tireHit = loader.loadSfx('phase_4/audio/sfx/Golf_Hit_Barrier_1.ogg') + wallHit = loader.loadSfx('phase_4/audio/sfx/MG_maze_pickup.ogg') + obstacleHit = loader.loadSfx('phase_4/audio/sfx/Golf_Hit_Barrier_2.ogg') + self.tireSounds.append({'tireHit': tireHit, + 'wallHit': wallHit, + 'obstacleHit': obstacleHit}) + + self.arrowRotateSound = loader.loadSfx('phase_4/audio/sfx/MG_sfx_ice_force_rotate.ogg') + self.arrowUpSound = loader.loadSfx('phase_4/audio/sfx/MG_sfx_ice_force_increase_3sec.ogg') + self.arrowDownSound = loader.loadSfx('phase_4/audio/sfx/MG_sfx_ice_force_decrease_3sec.ogg') + self.scoreCircleSound = loader.loadSfx('phase_4/audio/sfx/MG_sfx_ice_scoring_1.ogg') + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.DistributedMinigame.unload(self) + del self.music + self.gameBoard.removeNode() + del self.gameBoard + for forceArrow in self.forceArrowDict.values(): + forceArrow.removeNode() + + del self.forceArrowDict + self.scoreCircle.removeNode() + del self.scoreCircle + del self.countSound + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.DistributedMinigame.onstage(self) + self.gameBoard.reparentTo(render) + self.__placeToon(self.localAvId) + self.moveCameraToTop() + self.scorePanels = [] + base.playMusic(self.music, looping=1, volume=0.8) + + def offstage(self): + self.notify.debug('offstage') + self.music.stop() + self.gameBoard.hide() + self.infoLabel.hide() + for avId in self.tireDict: + self.tireDict[avId]['tireNodePath'].hide() + + for panel in self.scorePanels: + panel.cleanup() + + del self.scorePanels + for obstacle in self.obstacles: + obstacle.hide() + + for treasure in self.treasures: + treasure.nodePath.hide() + + for penalty in self.penalties: + penalty.nodePath.hide() + + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.dropShadow.show() + av.resetLOD() + + taskMgr.remove(self.uniqueName('aimtask')) + self.arrowKeys.destroy() + del self.arrowKeys + DistributedMinigame.DistributedMinigame.offstage(self) + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + DistributedMinigame.DistributedMinigame.handleDisabledAvatar(self, avId) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.DistributedMinigame.setGameReady(self): + return + for index in xrange(self.numPlayers): + avId = self.avIdList[index] + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toon.forwardSpeed = 0 + toon.rotateSpeed = False + toon.dropShadow.hide() + toon.setAnimState('Sit') + if avId in self.tireDict: + tireNp = self.tireDict[avId]['tireNodePath'] + toon.reparentTo(tireNp) + toon.setY(1.0) + toon.setZ(-3) + toon.startLookAround() + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.DistributedMinigame.setGameStart(self, timestamp) + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.stopLookAround() + + self.scores = [0] * self.numPlayers + spacing = 0.4 + for i in xrange(self.numPlayers): + avId = self.avIdList[i] + avName = self.getAvatarName(avId) + scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) + scorePanel.setScale(0.9) + scorePanel.setPos(-0.583 - spacing * (self.numPlayers - 1 - i), 0.0, -0.15) + scorePanel.reparentTo(base.a2dTopRight) + scorePanel.makeTransparent(0.75) + self.scorePanels.append(scorePanel) + + self.arrowKeys.setPressHandlers([self.__upArrowPressed, + self.__downArrowPressed, + self.__leftArrowPressed, + self.__rightArrowPressed, + self.__controlPressed]) + + def isInPlayState(self): + if not self.gameFSM.getCurrentState(): + return False + if not self.gameFSM.getCurrentState().getName() == 'play': + return False + return True + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterInputChoice(self): + self.notify.debug('enterInputChoice') + self.forceLocalToonToTire() + self.controlKeyPressed = False + if self.curRound == 0: + self.setupStartOfMatch() + else: + self.notify.debug('self.curRound = %s' % self.curRound) + self.timer = ToontownTimer.ToontownTimer() + self.timer.hide() + if self.timerStartTime != None: + self.startTimer() + self.showForceArrows(realPlayersOnly=True) + self.localForceArrow().setPosHpr(0, 0, -1.0, 0, 0, 0) + self.localForceArrow().reparentTo(self.localTireNp()) + self.localForceArrow().setY(IceGameGlobals.TireRadius) + self.localTireNp().headsUp(self.target) + self.notify.debug('self.localForceArrow() heading = %s' % self.localForceArrow().getH()) + self.curHeading = self.localTireNp().getH() + self.curForce = 25 + self.updateLocalForceArrow() + for avId in self.forceArrowDict: + forceArrow = self.forceArrowDict[avId] + forceArrow.setPosHpr(0, 0, -1.0, 0, 0, 0) + tireNp = self.tireDict[avId]['tireNodePath'] + forceArrow.reparentTo(tireNp) + forceArrow.setY(IceGameGlobals.TireRadius) + tireNp.headsUp(self.target) + self.updateForceArrow(avId, tireNp.getH(), 25) + + taskMgr.add(self.__aimTask, self.uniqueName('aimtask')) + if base.localAvatar.laffMeter: + base.localAvatar.laffMeter.stop() + self.sendForceArrowUpdateAsap = False + return + + def exitInputChoice(self): + if not self.controlKeyPressed: + if self.controlKeyWarningIval: + self.controlKeyWarningIval.finish() + self.controlKeyWarningIval = None + self.controlKeyWarningIval = Sequence(Func(self.controlKeyWarningLabel.show), self.controlKeyWarningLabel.colorScaleInterval(10, VBase4(1, 1, 1, 0), startColorScale=VBase4(1, 1, 1, 1)), Func(self.controlKeyWarningLabel.hide)) + self.controlKeyWarningIval.start() + if self.timer != None: + self.timer.destroy() + self.timer = None + self.timerStartTime = None + self.hideForceArrows() + self.arrowRotateSound.stop() + self.arrowUpSound.stop() + self.arrowDownSound.stop() + taskMgr.remove(self.uniqueName('aimtask')) + return + + def enterWaitServerChoices(self): + self.waitingMoveLabel.show() + self.showForceArrows(True) + + def exitWaitServerChoices(self): + self.waitingMoveLabel.hide() + self.hideForceArrows() + + def enterMoveTires(self): + for key in self.tireDict: + body = self.tireDict[key]['tireBody'] + body.setAngularVel(0, 0, 0) + body.setLinearVel(0, 0, 0) + + for index in xrange(len(self.allTireInputs)): + input = self.allTireInputs[index] + avId = self.avIdList[index] + body = self.getTireBody(avId) + degs = input[1] + 90 + tireNp = self.getTireNp(avId) + tireH = tireNp.getH() + self.notify.debug('tireH = %s' % tireH) + radAngle = deg2Rad(degs) + foo = NodePath('foo') + dirVector = Vec3(math.cos(radAngle), math.sin(radAngle), 0) + self.notify.debug('dirVector is now=%s' % dirVector) + inputForce = input[0] + inputForce /= self.MaxLocalForce + inputForce *= self.MaxPhysicsForce + force = dirVector * inputForce + self.notify.debug('adding force %s to %d' % (force, avId)) + body.addForce(force) + + self.enableAllTireBodies() + self.totalPhysicsSteps = 0 + self.startSim() + taskMgr.add(self.__moveTiresTask, self.uniqueName('moveTiresTtask')) + + def exitMoveTires(self): + self.forceLocalToonToTire() + self.disableAllTireBodies() + self.stopSim() + self.notify.debug('total Physics steps = %d' % self.totalPhysicsSteps) + taskMgr.remove(self.uniqueName('moveTiresTtask')) + + def enterSynch(self): + self.waitingSyncLabel.show() + + def exitSynch(self): + self.waitingSyncLabel.hide() + + def enterScoring(self): + sortedByDistance = [] + for avId in self.avIdList: + np = self.getTireNp(avId) + pos = np.getPos() + pos.setZ(0) + sortedByDistance.append((avId, pos.length())) + + def compareDistance(x, y): + if x[1] - y[1] > 0: + return 1 + elif x[1] - y[1] < 0: + return -1 + else: + return 0 + + sortedByDistance.sort(cmp=compareDistance) + self.scoreMovie = Sequence() + curScale = 0.01 + curTime = 0 + self.scoreCircle.setScale(0.01) + self.scoreCircle.show() + self.notify.debug('newScores = %s' % self.newScores) + circleStartTime = 0 + for index in xrange(len(sortedByDistance)): + distance = sortedByDistance[index][1] + avId = sortedByDistance[index][0] + scorePanelIndex = self.avIdList.index(avId) + time = (distance - curScale) / IceGameGlobals.ExpandFeetPerSec + if time < 0: + time = 0.01 + scaleXY = distance + IceGameGlobals.TireRadius + self.notify.debug('circleStartTime = %s' % circleStartTime) + self.scoreMovie.append(Parallel(LerpScaleInterval(self.scoreCircle, time, Point3(scaleXY, scaleXY, 1.0)), SoundInterval(self.scoreCircleSound, duration=time, startTime=circleStartTime))) + circleStartTime += time + startScore = self.scorePanels[scorePanelIndex].getScore() + destScore = self.newScores[scorePanelIndex] + self.notify.debug('for avId %d, startScore=%d, newScores=%d' % (avId, startScore, destScore)) + + def increaseScores(t, scorePanelIndex = scorePanelIndex, startScore = startScore, destScore = destScore): + oldScore = self.scorePanels[scorePanelIndex].getScore() + diff = destScore - startScore + newScore = int(startScore + diff * t) + if newScore > oldScore: + base.playSfx(self.countSound) + self.scorePanels[scorePanelIndex].setScore(newScore) + self.scores[scorePanelIndex] = newScore + + duration = (destScore - startScore) * IceGameGlobals.ScoreCountUpRate + tireNp = self.tireDict[avId]['tireNodePath'] + self.scoreMovie.append(Parallel(LerpFunctionInterval(increaseScores, duration), Sequence(LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 0, 0, 1)), LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 1, 1, 1)), LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 0, 0, 1)), LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 1, 1, 1)), LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 0, 0, 1)), LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 1, 1, 1))))) + curScale += distance + + self.scoreMovie.append(Func(self.sendUpdate, 'reportScoringMovieDone', [])) + self.scoreMovie.start() + + def exitScoring(self): + self.scoreMovie.finish() + self.scoreMovie = None + self.scoreCircle.hide() + return + + def enterFinalResults(self): + lerpTrack = Parallel() + lerpDur = 0.5 + tY = 0.6 + bY = -.05 + lX = -.5 + cX = 0 + rX = 0.5 + scorePanelLocs = (((cX, bY),), + ((lX, bY), (rX, bY)), + ((cX, tY), (lX, bY), (rX, bY)), + ((lX, tY), + (rX, tY), + (lX, bY), + (rX, bY))) + scorePanelLocs = scorePanelLocs[self.numPlayers - 1] + for i in xrange(self.numPlayers): + panel = self.scorePanels[i] + pos = scorePanelLocs[i] + panel.wrtReparentTo(aspect2d) + lerpTrack.append(Parallel(LerpPosInterval(panel, lerpDur, Point3(pos[0], 0, pos[1]), blendType='easeInOut'), LerpScaleInterval(panel, lerpDur, Vec3(panel.getScale()) * 2.0, blendType='easeInOut'))) + + self.showScoreTrack = Parallel(lerpTrack, Sequence(Wait(IceGameGlobals.ShowScoresDuration), Func(self.gameOver))) + self.showScoreTrack.start() + + def exitFinalResults(self): + self.showScoreTrack.pause() + del self.showScoreTrack + + def enterCleanup(self): + self.notify.debug('enterCleanup') + if base.localAvatar.laffMeter: + base.localAvatar.laffMeter.start() + + def exitCleanup(self): + pass + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + if toon: + toon.setPos(0, 0, 0) + toon.setHpr(0, 0, 0) + + def moveCameraToTop(self): + camera.reparentTo(render) + p = self.cameraThreeQuarterView + camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5]) + + def setupTire(self, avId, index): + tireNp, tireBody, tireOdeGeom = self.createTire(index) + self.tireDict[avId] = {'tireNodePath': tireNp, + 'tireBody': tireBody, + 'tireOdeGeom': tireOdeGeom} + if avId <= 0: + tireBlocker = tireNp.find('**/tireblockermesh') + if not tireBlocker.isEmpty(): + tireBlocker.hide() + if avId == self.localAvId: + tireNp = self.tireDict[avId]['tireNodePath'] + self.treasureSphereName = 'treasureCollider' + self.treasureCollSphere = CollisionSphere(0, 0, 0, IceGameGlobals.TireRadius) + self.treasureCollSphere.setTangible(0) + self.treasureCollNode = CollisionNode(self.treasureSphereName) + self.treasureCollNode.setFromCollideMask(ToontownGlobals.PieBitmask) + self.treasureCollNode.addSolid(self.treasureCollSphere) + self.treasureCollNodePath = tireNp.attachNewNode(self.treasureCollNode) + self.treasureHandler = CollisionHandlerEvent() + self.treasureHandler.addInPattern('%fn-intoTreasure') + base.cTrav.addCollider(self.treasureCollNodePath, self.treasureHandler) + eventName = '%s-intoTreasure' % self.treasureCollNodePath.getName() + self.notify.debug('eventName = %s' % eventName) + self.accept(eventName, self.toonHitSomething) + + def setupForceArrow(self, avId): + arrow = loader.loadModel('phase_4/models/minigames/ice_game_arrow') + priority = 0 + if avId < 0: + priority = -avId + else: + priority = self.avIdList.index(avId) + if avId == self.localAvId: + priority = 10 + self.forceArrowDict[avId] = arrow + + def hideForceArrows(self): + for forceArrow in self.forceArrowDict.values(): + forceArrow.hide() + + def showForceArrows(self, realPlayersOnly = True): + for avId in self.forceArrowDict: + if realPlayersOnly: + if avId > 0: + self.forceArrowDict[avId].show() + else: + self.forceArrowDict[avId].hide() + else: + self.forceArrowDict[avId].show() + + def localForceArrow(self): + if self.localAvId in self.forceArrowDict: + return self.forceArrowDict[self.localAvId] + else: + return None + return None + + def setChoices(self, input0, input1, input2, input3): + pass + + def startDebugTask(self): + taskMgr.add(self.debugTask, self.debugTaskName) + + def stopDebugTask(self): + taskMgr.remove(self.debugTaskName) + + def debugTask(self, task): + if self.canDrive and localAvatar.doId in self.tireDict: + dt = globalClock.getDt() + forceMove = 25000 + forceMoveDt = forceMove + tireBody = self.tireDict[localAvatar.doId]['tireBody'] + if self.arrowKeys.upPressed() and not tireBody.isEnabled(): + x = 0 + y = 1 + tireBody.enable() + tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0)) + if self.arrowKeys.downPressed() and not tireBody.isEnabled(): + x = 0 + y = -1 + tireBody.enable() + tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0)) + if self.arrowKeys.leftPressed() and not tireBody.isEnabled(): + x = -1 + y = 0 + tireBody.enable() + tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0)) + if self.arrowKeys.rightPressed() and not tireBody.isEnabled(): + x = 1 + y = 0 + tireBody.enable() + tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0)) + return task.cont + + def __upArrowPressed(self): + pass + + def __downArrowPressed(self): + pass + + def __leftArrowPressed(self): + pass + + def __rightArrowPressed(self): + pass + + def __controlPressed(self): + if self.gameFSM.getCurrentState().getName() == 'inputChoice': + self.sendForceArrowUpdateAsap = True + self.updateLocalForceArrow() + self.controlKeyPressed = True + self.sendUpdate('setAvatarChoice', [self.curForce, self.curHeading]) + self.gameFSM.request('waitServerChoices') + + def startTimer(self): + now = globalClock.getFrameTime() + elapsed = now - self.timerStartTime + self.timer.posInTopRightCorner() + self.timer.setTime(IceGameGlobals.InputTimeout) + self.timer.countdown(IceGameGlobals.InputTimeout - elapsed, self.handleChoiceTimeout) + self.timer.show() + + def setTimerStartTime(self, timestamp): + if not self.hasLocalToon: + return + self.timerStartTime = globalClockDelta.networkToLocalTime(timestamp) + if self.timer != None: + self.startTimer() + return + + def handleChoiceTimeout(self): + self.sendUpdate('setAvatarChoice', [0, 0]) + self.gameFSM.request('waitServerChoices') + + def localTireNp(self): + ret = None + if self.localAvId in self.tireDict: + ret = self.tireDict[self.localAvId]['tireNodePath'] + return ret + + def localTireBody(self): + ret = None + if self.localAvId in self.tireDict: + ret = self.tireDict[self.localAvId]['tireBody'] + return ret + + def getTireBody(self, avId): + ret = None + if avId in self.tireDict: + ret = self.tireDict[avId]['tireBody'] + return ret + + def getTireNp(self, avId): + ret = None + if avId in self.tireDict: + ret = self.tireDict[avId]['tireNodePath'] + return ret + + def updateForceArrow(self, avId, curHeading, curForce): + forceArrow = self.forceArrowDict[avId] + tireNp = self.tireDict[avId]['tireNodePath'] + tireNp.setH(curHeading) + tireBody = self.tireDict[avId]['tireBody'] + tireBody.setQuaternion(tireNp.getQuat()) + self.notify.debug('curHeading = %s' % curHeading) + yScale = curForce / 100.0 + yScale *= 1 + headY = yScale * 15 + xScale = (yScale - 1) / 2.0 + 1.0 + shaft = forceArrow.find('**/arrow_shaft') + head = forceArrow.find('**/arrow_head') + shaft.setScale(xScale, yScale, 1) + head.setPos(0, headY, 0) + head.setScale(xScale, xScale, 1) + + def updateLocalForceArrow(self): + avId = self.localAvId + self.b_setForceArrowInfo(avId, self.curHeading, self.curForce) + + def __aimTask(self, task): + if not hasattr(self, 'arrowKeys'): + return task.done + dt = globalClock.getDt() + headingMomentumChange = dt * 60.0 + forceMomentumChange = dt * 160.0 + arrowUpdate = False + arrowRotating = False + arrowUp = False + arrowDown = False + if self.arrowKeys.upPressed() and not self.arrowKeys.downPressed(): + self.forceMomentum += forceMomentumChange + if self.forceMomentum < 0: + self.forceMomentum = 0 + if self.forceMomentum > 50: + self.forceMomentum = 50 + oldForce = self.curForce + self.curForce += self.forceMomentum * dt + arrowUpdate = True + if oldForce < self.MaxLocalForce: + arrowUp = True + elif self.arrowKeys.downPressed() and not self.arrowKeys.upPressed(): + self.forceMomentum += forceMomentumChange + if self.forceMomentum < 0: + self.forceMomentum = 0 + if self.forceMomentum > 50: + self.forceMomentum = 50 + oldForce = self.curForce + self.curForce -= self.forceMomentum * dt + arrowUpdate = True + if oldForce > 0.01: + arrowDown = True + else: + self.forceMomentum = 0 + if self.arrowKeys.leftPressed() and not self.arrowKeys.rightPressed(): + self.headingMomentum += headingMomentumChange + if self.headingMomentum < 0: + self.headingMomentum = 0 + if self.headingMomentum > 50: + self.headingMomentum = 50 + self.curHeading += self.headingMomentum * dt + arrowUpdate = True + arrowRotating = True + elif self.arrowKeys.rightPressed() and not self.arrowKeys.leftPressed(): + self.headingMomentum += headingMomentumChange + if self.headingMomentum < 0: + self.headingMomentum = 0 + if self.headingMomentum > 50: + self.headingMomentum = 50 + self.curHeading -= self.headingMomentum * dt + arrowUpdate = True + arrowRotating = True + else: + self.headingMomentum = 0 + if arrowUpdate: + self.normalizeHeadingAndForce() + self.updateLocalForceArrow() + if arrowRotating: + if not self.arrowRotateSound.status() == self.arrowRotateSound.PLAYING: + base.playSfx(self.arrowRotateSound, looping=True) + else: + self.arrowRotateSound.stop() + if arrowUp: + if not self.arrowUpSound.status() == self.arrowUpSound.PLAYING: + base.playSfx(self.arrowUpSound, looping=False) + else: + self.arrowUpSound.stop() + if arrowDown: + if not self.arrowDownSound.status() == self.arrowDownSound.PLAYING: + base.playSfx(self.arrowDownSound, looping=False) + else: + self.arrowDownSound.stop() + return task.cont + + def normalizeHeadingAndForce(self): + if self.curForce > self.MaxLocalForce: + self.curForce = self.MaxLocalForce + if self.curForce < 0.01: + self.curForce = 0.01 + + def setTireInputs(self, tireInputs): + if not self.hasLocalToon: + return + self.allTireInputs = tireInputs + self.gameFSM.request('moveTires') + + def enableAllTireBodies(self): + for avId in self.tireDict.keys(): + self.tireDict[avId]['tireBody'].enable() + + def disableAllTireBodies(self): + for avId in self.tireDict.keys(): + self.tireDict[avId]['tireBody'].disable() + + def areAllTiresDisabled(self): + for avId in self.tireDict.keys(): + if self.tireDict[avId]['tireBody'].isEnabled(): + return False + + return True + + def __moveTiresTask(self, task): + if self.areAllTiresDisabled(): + self.sendTirePositions() + self.gameFSM.request('synch') + return task.done + return task.cont + + def sendTirePositions(self): + tirePositions = [] + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + tire = self.getTireBody(avId) + pos = Point3(tire.getPosition()) + tirePositions.append([pos[0], pos[1], pos[2]]) + + for index in xrange(len(self.avIdList), 4): + avId = -index + tire = self.getTireBody(avId) + pos = Point3(tire.getPosition()) + tirePositions.append([pos[0], pos[1], pos[2]]) + + self.sendUpdate('endingPositions', [tirePositions]) + + def setFinalPositions(self, finalPos): + if not self.hasLocalToon: + return + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + tire = self.getTireBody(avId) + np = self.getTireNp(avId) + pos = finalPos[index] + tire.setPosition(pos[0], pos[1], pos[2]) + np.setPos(pos[0], pos[1], pos[2]) + + for index in xrange(len(self.avIdList), 4): + avId = -index + tire = self.getTireBody(avId) + np = self.getTireNp(avId) + pos = finalPos[index] + tire.setPosition(pos[0], pos[1], pos[2]) + np.setPos(pos[0], pos[1], pos[2]) + + def updateInfoLabel(self): + self.infoLabel['text'] = TTLocalizer.IceGameInfo % {'curMatch': self.curMatch + 1, + 'numMatch': IceGameGlobals.NumMatches, + 'curRound': self.curRound + 1, + 'numRound': IceGameGlobals.NumRounds} + + def setMatchAndRound(self, match, round): + if not self.hasLocalToon: + return + self.curMatch = match + self.curRound = round + self.updateInfoLabel() + + def setScores(self, match, round, scores): + if not self.hasLocalToon: + return + self.newMatch = match + self.newRound = round + self.newScores = scores + + def setNewState(self, state): + if not self.hasLocalToon: + return + self.notify.debug('setNewState gameFSM=%s newState=%s' % (self.gameFSM, state)) + self.gameFSM.request(state) + + def putAllTiresInStartingPositions(self): + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + np = self.tireDict[avId]['tireNodePath'] + np.setPos(IceGameGlobals.StartingPositions[index]) + self.notify.debug('avId=%s newPos=%s' % (avId, np.getPos)) + np.setHpr(0, 0, 0) + quat = np.getQuat() + body = self.tireDict[avId]['tireBody'] + body.setPosition(IceGameGlobals.StartingPositions[index]) + body.setQuaternion(quat) + + for index in xrange(len(self.avIdList), 4): + avId = -index + np = self.tireDict[avId]['tireNodePath'] + np.setPos(IceGameGlobals.StartingPositions[index]) + self.notify.debug('avId=%s newPos=%s' % (avId, np.getPos)) + np.setHpr(0, 0, 0) + quat = np.getQuat() + body = self.tireDict[avId]['tireBody'] + body.setPosition(IceGameGlobals.StartingPositions[index]) + body.setQuaternion(quat) + + def b_setForceArrowInfo(self, avId, force, heading): + self.setForceArrowInfo(avId, force, heading) + self.d_setForceArrowInfo(avId, force, heading) + + def d_setForceArrowInfo(self, avId, force, heading): + sendIt = False + curTime = self.getCurrentGameTime() + if self.sendForceArrowUpdateAsap: + sendIt = True + elif curTime - self.lastForceArrowUpdateTime > 0.2: + sendIt = True + if sendIt: + self.sendUpdate('setForceArrowInfo', [avId, force, heading]) + self.sendForceArrowUpdateAsap = False + self.lastForceArrowUpdateTime = self.getCurrentGameTime() + + def setForceArrowInfo(self, avId, force, heading): + if not self.hasLocalToon: + return + self.updateForceArrow(avId, force, heading) + + def setupStartOfMatch(self): + self.putAllTiresInStartingPositions() + szId = self.getSafezoneId() + self.numTreasures = IceGameGlobals.NumTreasures[szId] + if self.treasures: + for treasure in self.treasures: + treasure.destroy() + + self.treasures = [] + index = 0 + treasureMargin = IceGameGlobals.TireRadius + 1.0 + while len(self.treasures) < self.numTreasures: + xPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[0] + 5, IceGameGlobals.MaxWall[0] - 5) + yPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[1] + 5, IceGameGlobals.MaxWall[1] - 5) + self.notify.debug('yPos=%s' % yPos) + pos = Point3(xPos, yPos, IceGameGlobals.TireRadius) + newTreasure = IceTreasure.IceTreasure(self.treasureModel, pos, index, self.doId, penalty=False) + goodSpot = True + for obstacle in self.obstacles: + if newTreasure.nodePath.getDistance(obstacle) < treasureMargin: + goodSpot = False + break + + if goodSpot: + for treasure in self.treasures: + if newTreasure.nodePath.getDistance(treasure.nodePath) < treasureMargin: + goodSpot = False + break + + if goodSpot: + self.treasures.append(newTreasure) + index += 1 + else: + newTreasure.destroy() + + self.numPenalties = IceGameGlobals.NumPenalties[szId] + if self.penalties: + for penalty in self.penalties: + penalty.destroy() + + self.penalties = [] + index = 0 + while len(self.penalties) < self.numPenalties: + xPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[0] + 5, IceGameGlobals.MaxWall[0] - 5) + yPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[1] + 5, IceGameGlobals.MaxWall[1] - 5) + self.notify.debug('yPos=%s' % yPos) + pos = Point3(xPos, yPos, IceGameGlobals.TireRadius) + newPenalty = IceTreasure.IceTreasure(self.penaltyModel, pos, index, self.doId, penalty=True) + goodSpot = True + for obstacle in self.obstacles: + if newPenalty.nodePath.getDistance(obstacle) < treasureMargin: + goodSpot = False + break + + if goodSpot: + for treasure in self.treasures: + if newPenalty.nodePath.getDistance(treasure.nodePath) < treasureMargin: + goodSpot = False + break + + if goodSpot: + for penalty in self.penalties: + if newPenalty.nodePath.getDistance(penalty.nodePath) < treasureMargin: + goodSpot = False + break + + if goodSpot: + self.penalties.append(newPenalty) + index += 1 + else: + newPenalty.destroy() + + def toonHitSomething(self, entry): + self.notify.debug('---- treasure Enter ---- ') + self.notify.debug('%s' % entry) + name = entry.getIntoNodePath().getName() + parts = name.split('-') + if len(parts) < 3: + self.notify.debug('collided with %s, but returning' % name) + return + if not int(parts[1]) == self.doId: + self.notify.debug("collided with %s, but doId doesn't match" % name) + return + treasureNum = int(parts[2]) + if 'penalty' in parts[0]: + self.__penaltyGrabbed(treasureNum) + else: + self.__treasureGrabbed(treasureNum) + + def __treasureGrabbed(self, treasureNum): + self.treasures[treasureNum].showGrab() + self.treasureGrabSound.play() + self.sendUpdate('claimTreasure', [treasureNum]) + + def setTreasureGrabbed(self, avId, treasureNum): + if not self.hasLocalToon: + return + self.notify.debug('treasure %s grabbed by %s' % (treasureNum, avId)) + if avId != self.localAvId: + self.treasures[treasureNum].showGrab() + i = self.avIdList.index(avId) + self.scores[i] += 1 + self.scorePanels[i].setScore(self.scores[i]) + + def __penaltyGrabbed(self, penaltyNum): + self.penalties[penaltyNum].showGrab() + self.sendUpdate('claimPenalty', [penaltyNum]) + + def setPenaltyGrabbed(self, avId, penaltyNum): + if not self.hasLocalToon: + return + self.notify.debug('penalty %s grabbed by %s' % (penaltyNum, avId)) + if avId != self.localAvId: + self.penalties[penaltyNum].showGrab() + i = self.avIdList.index(avId) + self.scores[i] -= 1 + self.scorePanels[i].setScore(self.scores[i]) + + def postStep(self): + DistributedIceWorld.DistributedIceWorld.postStep(self) + if not self.colCount: + return + for count in xrange(self.colCount): + c0, c1 = self.getOrderedContacts(count) + if c1 in self.tireCollideIds: + tireIndex = self.tireCollideIds.index(c1) + if c0 in self.tireCollideIds: + self.tireSounds[tireIndex]['tireHit'].play() + elif c0 == self.wallCollideId: + self.tireSounds[tireIndex]['wallHit'].play() + elif c0 == self.obstacleCollideId: + self.tireSounds[tireIndex]['obstacleHit'].play() + + def forceLocalToonToTire(self): + toon = localAvatar + if toon and self.localAvId in self.tireDict: + tireNp = self.tireDict[self.localAvId]['tireNodePath'] + toon.reparentTo(tireNp) + toon.setPosHpr(0, 0, 0, 0, 0, 0) + toon.setY(1.0) + toon.setZ(-3) diff --git a/toontown/minigame/DistributedIceGameAI.py b/toontown/minigame/DistributedIceGameAI.py new file mode 100755 index 00000000..7d147cbc --- /dev/null +++ b/toontown/minigame/DistributedIceGameAI.py @@ -0,0 +1,354 @@ +from pandac.PandaModules import Point3 +from direct.distributed.ClockDelta import globalClockDelta +from direct.fsm import ClassicFSM, State +from direct.task import Task +from toontown.minigame import DistributedMinigameAI +from toontown.minigame import MinigameGlobals +from toontown.minigame import IceGameGlobals +from toontown.ai.ToonBarrier import ToonBarrier + +class DistributedIceGameAI(DistributedMinigameAI.DistributedMinigameAI): + notify = directNotify.newCategory('DistributedIceGameAI') + + def __init__(self, air, minigameId): + try: + self.DistributedIceGameAI_initialized + except: + self.DistributedIceGameAI_initialized = 1 + DistributedMinigameAI.DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedIceGameAI', [State.State('off', self.enterOff, self.exitOff, ['waitClientsChoices']), + State.State('waitClientsChoices', self.enterWaitClientsChoices, self.exitWaitClientsChoices, ['cleanup', 'processChoices']), + State.State('processChoices', self.enterProcessChoices, self.exitProcessChoices, ['waitEndingPositions', 'cleanup']), + State.State('waitEndingPositions', self.enterWaitEndingPositions, self.exitWaitEndingPositions, ['processEndingPositions', 'cleanup']), + State.State('processEndingPositions', self.enterProcessEndingPositions, self.exitProcessEndingPositions, ['waitClientsChoices', 'scoreMatch', 'cleanup']), + State.State('scoreMatch', self.enterScoreMatch, self.exitScoreMatch, ['waitClientsChoices', 'finalResults', 'cleanup']), + State.State('finalResults', self.enterFinalResults, self.exitFinalResults, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, ['off'])], 'off', 'off') + self.addChildGameFSM(self.gameFSM) + self.avatarChoices = {} + self.avatarEndingPositions = {} + self.curRound = 0 + self.curMatch = 0 + self.finalEndingPositions = [Point3(IceGameGlobals.StartingPositions[0]), + Point3(IceGameGlobals.StartingPositions[1]), + Point3(IceGameGlobals.StartingPositions[2]), + Point3(IceGameGlobals.StartingPositions[3])] + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + taskMgr.remove(self.taskName('wait-choices-timeout')) + taskMgr.remove(self.taskName('endingPositionsTimeout')) + del self.gameFSM + DistributedMinigameAI.DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.DistributedMinigameAI.setGameReady(self) + self.numTreasures = IceGameGlobals.NumTreasures[self.getSafezoneId()] + self.numTreasuresTaken = 0 + self.takenTreasuresTable = [0] * self.numTreasures + self.numPenalties = IceGameGlobals.NumPenalties[self.getSafezoneId()] + self.numPenaltiesTaken = 0 + self.takenPenaltiesTable = [0] * self.numPenalties + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('waitClientsChoices') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.DistributedMinigameAI.gameOver(self) + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('off') + + def exitCleanup(self): + pass + + def enterWaitClientsChoices(self): + self.notify.debug('enterWaitClientsChoices') + self.resetChoices() + self.sendUpdate('setMatchAndRound', [self.curMatch, self.curRound]) + self.sendUpdate('setNewState', ['inputChoice']) + taskMgr.doMethodLater(IceGameGlobals.InputTimeout, self.waitClientsChoicesTimeout, self.taskName('wait-choices-timeout')) + self.sendUpdate('setTimerStartTime', [globalClockDelta.getFrameNetworkTime()]) + + def exitWaitClientsChoices(self): + self.notify.debug('exitWaitClientsChoices') + taskMgr.remove(self.taskName('wait-choices-timeout')) + + def enterProcessChoices(self): + forceAndHeading = [] + for avId in self.avIdList: + force = self.avatarChoices[avId][0] + heading = self.avatarChoices[avId][1] + forceAndHeading.append([force, heading]) + + self.notify.debug('tireInputs = %s' % forceAndHeading) + self.sendUpdate('setTireInputs', [forceAndHeading]) + self.gameFSM.request('waitEndingPositions') + + def exitProcessChoices(self): + pass + + def enterWaitEndingPositions(self): + if self.curRound == 0: + self.takenTreasuresTable = [0] * self.numTreasures + self.takenPenaltiesTable = [0] * self.numPenalties + taskMgr.doMethodLater(IceGameGlobals.InputTimeout, self.waitClientsChoicesTimeout, self.taskName('endingPositionsTimeout')) + self.avatarEndingPositions = {} + + def exitWaitEndingPositions(self): + taskMgr.remove(self.taskName('endingPositionsTimeout')) + + def enterProcessEndingPositions(self): + averagePos = [Point3(0, 0, 0), + Point3(0, 0, 0), + Point3(0, 0, 0), + Point3(0, 0, 0)] + divisor = 0 + for avId in self.avatarEndingPositions.keys(): + divisor += 1 + oneClientEndingPositions = self.avatarEndingPositions[avId] + avIndex = self.avIdList.index(avId) + for index in xrange(len(oneClientEndingPositions)): + pos = oneClientEndingPositions[index] + averagePos[index] += Point3(pos[0], pos[1], pos[2]) + self.notify.debug('index = %d averagePos = %s' % (index, averagePos)) + + sentPos = [] + if divisor: + for newPos in averagePos: + newPos /= divisor + newPos.setZ(IceGameGlobals.TireRadius) + sentPos.append([newPos[0], newPos[1], newPos[2]]) + + else: + sentPos = self.finalEndingPositions + self.sendUpdate('setFinalPositions', [sentPos]) + self.finalEndingPositions = sentPos + if self.curMatch == IceGameGlobals.NumMatches - 1 and self.curRound == IceGameGlobals.NumRounds - 1: + self.gameFSM.request('scoreMatch') + elif self.curRound == IceGameGlobals.NumRounds - 1: + self.gameFSM.request('scoreMatch') + else: + self.curRound += 1 + self.sendUpdate('setMatchAndRound', [self.curMatch, self.curRound]) + self.gameFSM.request('waitClientsChoices') + + def exitProcessEndingPositions(self): + pass + + def enterScoreMatch(self): + sortedByDistance = [] + for avId in self.avIdList: + index = self.avIdList.index(avId) + pos = Point3(*self.finalEndingPositions[index]) + pos.setZ(0) + sortedByDistance.append((avId, pos.length())) + + def compareDistance(x, y): + if x[1] - y[1] > 0: + return 1 + elif x[1] - y[1] < 0: + return -1 + else: + return 0 + + sortedByDistance.sort(cmp=compareDistance) + self.scoresAsList = [] + totalPointsAdded = 0 + for index in xrange(len(self.avIdList)): + pos = Point3(*self.finalEndingPositions[index]) + pos.setZ(0) + length = pos.length() + points = length / IceGameGlobals.FarthestLength * (IceGameGlobals.PointsInCorner - IceGameGlobals.PointsDeadCenter[self.numPlayers]) + points += IceGameGlobals.PointsDeadCenter[self.numPlayers] + self.notify.debug('length = %s points=%s avId=%d' % (length, points, avId)) + avId = self.avIdList[index] + bonusIndex = 0 + for sortIndex in xrange(len(sortedByDistance)): + if sortedByDistance[sortIndex][0] == avId: + bonusIndex = sortIndex + + bonusIndex += 4 - len(self.avIdList) + pointsToAdd = int(points + 0.5) + IceGameGlobals.BonusPointsForPlace[bonusIndex] + totalPointsAdded += pointsToAdd + self.scoreDict[avId] += pointsToAdd + self.scoresAsList.append(self.scoreDict[avId]) + + self.curMatch += 1 + self.curRound = 0 + self.sendUpdate('setScores', [self.curMatch, self.curRound, self.scoresAsList]) + self.sendUpdate('setNewState', ['scoring']) + + def allToonsScoringMovieDone(self = self): + self.notify.debug('allToonsScoringMovieDone') + if self.curMatch == IceGameGlobals.NumMatches: + self.gameFSM.request('finalResults') + else: + self.gameFSM.request('waitClientsChoices') + + def handleTimeout(avIds, self = self): + self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds) + if self.curMatch == IceGameGlobals.NumMatches: + self.gameFSM.request('finalResults') + else: + self.gameFSM.request('waitClientsChoices') + + scoreMovieDuration = IceGameGlobals.FarthestLength * IceGameGlobals.ExpandFeetPerSec + scoreMovieDuration += totalPointsAdded * IceGameGlobals.ScoreCountUpRate + self.scoringMovieDoneBarrier = ToonBarrier('waitScoringMovieDone', self.uniqueName('waitScoringMovieDone'), self.avIdList, scoreMovieDuration + MinigameGlobals.latencyTolerance, allToonsScoringMovieDone, handleTimeout) + + def exitScoreMatch(self): + self.scoringMovieDoneBarrier.cleanup() + self.scoringMovieDoneBarrier = None + return + + def enterFinalResults(self): + self.checkScores() + self.sendUpdate('setNewState', ['finalResults']) + taskMgr.doMethodLater(IceGameGlobals.ShowScoresDuration, self.__doneShowingScores, self.taskName('waitShowScores')) + + def exitFinalResults(self): + taskMgr.remove(self.taskName('waitShowScores')) + + def __doneShowingScores(self, task): + self.notify.debug('doneShowingScores') + self.gameOver() + return Task.done + + def waitClientsChoicesTimeout(self, task): + self.notify.debug('waitClientsChoicesTimeout: did not hear from all clients') + for avId in self.avatarChoices.keys(): + if self.avatarChoices[avId] == (-1, 0): + self.avatarChoices[avId] = (0, 0) + + self.gameFSM.request('processChoices') + return Task.done + + def resetChoices(self): + for avId in self.avIdList: + self.avatarChoices[avId] = (-1, 0) + + def setAvatarChoice(self, force, direction): + avatarId = self.air.getAvatarIdFromSender() + self.notify.debug('setAvatarChoice: avatar: ' + str(avatarId) + ' votes: ' + str(force) + ' direction: ' + str(direction)) + self.avatarChoices[avatarId] = self.checkChoice(avatarId, force, direction) + if self.allAvatarsChosen(): + self.notify.debug('setAvatarChoice: all avatars have chosen') + self.gameFSM.request('processChoices') + else: + self.notify.debug('setAvatarChoice: still waiting for more choices') + + def checkChoice(self, avId, force, direction): + retForce = force + retDir = direction + if retForce < 0: + retForce = 0 + if retForce > 100: + retForce = 100 + return (retForce, retDir) + + def allAvatarsChosen(self): + for avId in self.avatarChoices.keys(): + choice = self.avatarChoices[avId] + if choice[0] == -1 and not self.stateDict[avId] == DistributedMinigameAI.EXITED: + return False + + return True + + def endingPositions(self, positions): + if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'waitEndingPositions': + return + self.notify.debug('got endingPositions from client %s' % positions) + avId = self.air.getAvatarIdFromSender() + self.avatarEndingPositions[avId] = positions + if self.allAvatarsSentEndingPositions(): + self.gameFSM.request('processEndingPositions') + + def allAvatarsSentEndingPositions(self): + if len(self.avatarEndingPositions) == len(self.avIdList): + return True + return False + + def endingPositionsTimeout(self, task): + self.notify.debug('endingPositionsTimeout : did not hear from all clients') + self.gameFSM.request('processEndingPositions') + return Task.done + + def reportScoringMovieDone(self): + if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'scoreMatch': + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('reportScoringMovieDone: avatar %s is done' % avId) + self.scoringMovieDoneBarrier.clear(avId) + + def claimTreasure(self, treasureNum): + if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'waitEndingPositions': + return + avId = self.air.getAvatarIdFromSender() + if avId not in self.scoreDict: + self.notify.warning('PROBLEM: avatar %s called claimTreasure(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId, + treasureNum, + self.scoreDict, + self.avIdList)) + return + if treasureNum < 0 or treasureNum >= self.numTreasures: + self.air.writeServerEvent('warning', treasureNum, 'MazeGameAI.claimTreasure treasureNum out of range') + return + if self.takenTreasuresTable[treasureNum]: + return + self.takenTreasuresTable[treasureNum] = 1 + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('setTreasureGrabbed', [avId, treasureNum]) + self.scoreDict[avId] += 1 + self.numTreasuresTaken += 1 + + def claimPenalty(self, penaltyNum): + if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'waitEndingPositions': + return + avId = self.air.getAvatarIdFromSender() + if avId not in self.scoreDict: + self.notify.warning('PROBLEM: avatar %s called claimPenalty(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId, + penaltyNum, + self.scoreDict, + self.avIdList)) + return + if penaltyNum < 0 or penaltyNum >= self.numPenalties: + self.air.writeServerEvent('warning', penaltyNum, 'IceGameAI.claimPenalty penaltyNum out of range') + return + if self.takenPenaltiesTable[penaltyNum]: + return + self.takenPenaltiesTable[penaltyNum] = 1 + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('setPenaltyGrabbed', [avId, penaltyNum]) + self.scoreDict[avId] -= 1 + self.numPenaltiesTaken += 1 + + def checkScores(self): + self.scoresAsList = [] + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + if self.scoreDict[avId] < 0: + self.scoreDict[avId] = 1 + self.scoresAsList.append(self.scoreDict[avId]) diff --git a/toontown/minigame/DistributedIceWorld.py b/toontown/minigame/DistributedIceWorld.py new file mode 100755 index 00000000..d4860036 --- /dev/null +++ b/toontown/minigame/DistributedIceWorld.py @@ -0,0 +1,180 @@ +from pandac.PandaModules import Vec4, BitMask32, Quat, Point3, NodePath +from pandac.PandaModules import OdePlaneGeom, OdeBody, OdeSphereGeom, OdeMass, OdeUtil, OdeBoxGeom +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame import DistributedMinigamePhysicsWorld +from toontown.minigame import IceGameGlobals +from toontown.golf import BuildGeometry +MetersToFeet = 3.2808399 +FeetToMeters = 1.0 / MetersToFeet + +class DistributedIceWorld(DistributedMinigamePhysicsWorld.DistributedMinigamePhysicsWorld): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMinigamePhysicsWorld') + floorCollideId = 1 + floorMask = BitMask32(floorCollideId) + wallCollideId = 1 << 1 + wallMask = BitMask32(wallCollideId) + obstacleCollideId = 1 << 2 + obstacleMask = BitMask32(obstacleCollideId) + tireCollideIds = [1 << 8, + 1 << 9, + 1 << 10, + 1 << 11] + tire0Mask = BitMask32(tireCollideIds[0]) + tire1Mask = BitMask32(tireCollideIds[1]) + tire2Mask = BitMask32(tireCollideIds[2]) + tire3Mask = BitMask32(tireCollideIds[3]) + allTiresMask = tire0Mask | tire1Mask | tire2Mask | tire3Mask + tireMasks = (tire0Mask, + tire1Mask, + tire2Mask, + tire3Mask) + tireDensity = 1 + tireSurfaceType = 0 + iceSurfaceType = 1 + fenceSurfaceType = 2 + + def __init__(self, cr): + DistributedMinigamePhysicsWorld.DistributedMinigamePhysicsWorld.__init__(self, cr) + + def delete(self): + DistributedMinigamePhysicsWorld.DistributedMinigamePhysicsWorld.delete(self) + if hasattr(self, 'floor'): + self.floor = None + return + + def setupSimulation(self): + DistributedMinigamePhysicsWorld.DistributedMinigamePhysicsWorld.setupSimulation(self) + self.world.setGravity(0, 0, -32.174) + self.world.setAutoDisableFlag(1) + self.world.setAutoDisableLinearThreshold(0.5 * MetersToFeet) + self.world.setAutoDisableAngularThreshold(OdeUtil.getInfinity()) + self.world.setAutoDisableSteps(10) + self.world.setCfm(1e-05 * MetersToFeet) + self.world.initSurfaceTable(3) + self.world.setSurfaceEntry(0, 1, 0.2, 0, 0, 0, 0, 0, 0.1) + self.world.setSurfaceEntry(0, 0, 0.1, 0.9, 0.1, 0, 0, 0, 0) + self.world.setSurfaceEntry(0, 2, 0.9, 0.9, 0.1, 0, 0, 0, 0) + self.floor = OdePlaneGeom(self.space, Vec4(0.0, 0.0, 1.0, -20.0)) + self.floor.setCollideBits(self.allTiresMask) + self.floor.setCategoryBits(self.floorMask) + self.westWall = OdePlaneGeom(self.space, Vec4(1.0, 0.0, 0.0, IceGameGlobals.MinWall[0])) + self.westWall.setCollideBits(self.allTiresMask) + self.westWall.setCategoryBits(self.wallMask) + self.space.setSurfaceType(self.westWall, self.fenceSurfaceType) + self.space.setCollideId(self.westWall, self.wallCollideId) + self.eastWall = OdePlaneGeom(self.space, Vec4(-1.0, 0.0, 0.0, -IceGameGlobals.MaxWall[0])) + self.eastWall.setCollideBits(self.allTiresMask) + self.eastWall.setCategoryBits(self.wallMask) + self.space.setSurfaceType(self.eastWall, self.fenceSurfaceType) + self.space.setCollideId(self.eastWall, self.wallCollideId) + self.southWall = OdePlaneGeom(self.space, Vec4(0.0, 1.0, 0.0, IceGameGlobals.MinWall[1])) + self.southWall.setCollideBits(self.allTiresMask) + self.southWall.setCategoryBits(self.wallMask) + self.space.setSurfaceType(self.southWall, self.fenceSurfaceType) + self.space.setCollideId(self.southWall, self.wallCollideId) + self.northWall = OdePlaneGeom(self.space, Vec4(0.0, -1.0, 0.0, -IceGameGlobals.MaxWall[1])) + self.northWall.setCollideBits(self.allTiresMask) + self.northWall.setCategoryBits(self.wallMask) + self.space.setSurfaceType(self.northWall, self.fenceSurfaceType) + self.space.setCollideId(self.northWall, self.wallCollideId) + self.floorTemp = OdePlaneGeom(self.space, Vec4(0.0, 0.0, 1.0, 0.0)) + self.floorTemp.setCollideBits(self.allTiresMask) + self.floorTemp.setCategoryBits(self.floorMask) + self.space.setSurfaceType(self.floorTemp, self.iceSurfaceType) + self.space.setCollideId(self.floorTemp, self.floorCollideId) + self.space.setAutoCollideWorld(self.world) + self.space.setAutoCollideJointGroup(self.contactgroup) + self.totalPhysicsSteps = 0 + + def createTire(self, tireIndex): + if tireIndex < 0 or tireIndex >= len(self.tireMasks): + self.notify.error('invalid tireIndex %s' % tireIndex) + self.notify.debug('create tireindex %s' % tireIndex) + zOffset = 0 + body = OdeBody(self.world) + mass = OdeMass() + mass.setSphere(self.tireDensity, IceGameGlobals.TireRadius) + body.setMass(mass) + body.setPosition(IceGameGlobals.StartingPositions[tireIndex][0], IceGameGlobals.StartingPositions[tireIndex][1], IceGameGlobals.StartingPositions[tireIndex][2]) + body.setAutoDisableDefaults() + geom = OdeSphereGeom(self.space, IceGameGlobals.TireRadius) + self.space.setSurfaceType(geom, self.tireSurfaceType) + self.space.setCollideId(geom, self.tireCollideIds[tireIndex]) + self.massList.append(mass) + self.geomList.append(geom) + geom.setCollideBits(self.allTiresMask | self.wallMask | self.floorMask | self.obstacleMask) + geom.setCategoryBits(self.tireMasks[tireIndex]) + geom.setBody(body) + if self.notify.getDebug(): + self.notify.debug('tire geom id') + geom.write() + self.notify.debug(' -') + if self.canRender: + testTire = render.attachNewNode('tire holder %d' % tireIndex) + smileyModel = NodePath() + if not smileyModel.isEmpty(): + smileyModel.setScale(IceGameGlobals.TireRadius) + smileyModel.reparentTo(testTire) + smileyModel.setAlphaScale(0.5) + smileyModel.setTransparency(1) + testTire.setPos(IceGameGlobals.StartingPositions[tireIndex]) + tireModel = loader.loadModel('phase_4/models/minigames/ice_game_tire') + tireHeight = 1 + tireModel.setZ(-IceGameGlobals.TireRadius + 0.01) + tireModel.reparentTo(testTire) + self.odePandaRelationList.append((testTire, body)) + else: + testTire = None + self.bodyList.append((None, body)) + return (testTire, body, geom) + + def placeBodies(self): + for pair in self.odePandaRelationList: + pandaNodePathGeom = pair[0] + odeBody = pair[1] + if pandaNodePathGeom: + pandaNodePathGeom.setPos(odeBody.getPosition()) + pandaNodePathGeom.setQuat(Quat(odeBody.getQuaternion()[0], odeBody.getQuaternion()[1], odeBody.getQuaternion()[2], odeBody.getQuaternion()[3])) + pandaNodePathGeom.setP(0) + pandaNodePathGeom.setR(0) + newQuat = pandaNodePathGeom.getQuat() + odeBody.setQuaternion(newQuat) + + def postStep(self): + DistributedMinigamePhysicsWorld.DistributedMinigamePhysicsWorld.postStep(self) + self.placeBodies() + self.totalPhysicsSteps += 1 + + def createObstacle(self, pos, obstacleIndex, cubicObstacle): + if cubicObstacle: + return self.createCubicObstacle(pos, obstacleIndex) + else: + return self.createCircularObstacle(pos, obstacleIndex) + + def createCircularObstacle(self, pos, obstacleIndex): + self.notify.debug('create obstacleindex %s' % obstacleIndex) + geom = OdeSphereGeom(self.space, IceGameGlobals.TireRadius) + geom.setCollideBits(self.allTiresMask) + geom.setCategoryBits(self.obstacleMask) + self.space.setCollideId(geom, self.obstacleCollideId) + tireModel = loader.loadModel('phase_4/models/minigames/ice_game_tirestack') + tireHeight = 1 + tireModel.setPos(pos) + tireModel.reparentTo(render) + geom.setPosition(tireModel.getPos()) + tireModel.setZ(0) + return tireModel + + def createCubicObstacle(self, pos, obstacleIndex): + self.notify.debug('create obstacleindex %s' % obstacleIndex) + sideLength = IceGameGlobals.TireRadius * 2 + geom = OdeBoxGeom(self.space, sideLength, sideLength, sideLength) + geom.setCollideBits(self.allTiresMask) + geom.setCategoryBits(self.obstacleMask) + self.space.setCollideId(geom, self.obstacleCollideId) + tireModel = loader.loadModel('phase_4/models/minigames/ice_game_crate') + tireModel.setPos(pos) + tireModel.reparentTo(render) + geom.setPosition(tireModel.getPos()) + tireModel.setZ(0) + return tireModel diff --git a/toontown/minigame/DistributedMazeGame.py b/toontown/minigame/DistributedMazeGame.py new file mode 100755 index 00000000..e4178cde --- /dev/null +++ b/toontown/minigame/DistributedMazeGame.py @@ -0,0 +1,1138 @@ +from direct.interval.IntervalGlobal import LerpPosInterval, LerpHprInterval, LerpPosHprInterval +from direct.interval.IntervalGlobal import SoundInterval, LerpScaleInterval, LerpFunctionInterval +from direct.interval.IntervalGlobal import Wait, Func +from direct.interval.MetaInterval import Sequence, Parallel +from direct.gui.DirectGui import DirectWaitBar, DGG +from direct.showbase import PythonUtil +from direct.fsm import ClassicFSM, State +from direct.showbase import RandomNumGen +from direct.task.Task import Task +from direct.distributed.ClockDelta import globalClockDelta +from pandac.PandaModules import Point3, Vec3 +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownTimer +from DistributedMinigame import DistributedMinigame +from MazeSuit import MazeSuit +from OrthoWalk import OrthoWalk +from OrthoDrive import OrthoDrive +import MazeGameGlobals +import MazeData +import MazeTreasure +import Trajectory +import Maze +import MinigameAvatarScorePanel +import MinigameGlobals + +class DistributedMazeGame(DistributedMinigame): + notify = directNotify.newCategory('DistributedMazeGame') + CAMERA_TASK = 'MazeGameCameraTask' + UPDATE_SUITS_TASK = 'MazeGameUpdateSuitsTask' + TREASURE_GRAB_EVENT_NAME = 'MazeTreasureGrabbed' + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedMazeGame', [State.State('off', self.enterOff, self.exitOff, ['play']), + State.State('play', self.enterPlay, self.exitPlay, ['cleanup', 'showScores']), + State.State('showScores', self.enterShowScores, self.exitShowScores, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.usesLookAround = 1 + + def getTitle(self): + return TTLocalizer.MazeGameTitle + + def getInstructions(self): + return TTLocalizer.MazeGameInstructions + + def getMaxDuration(self): + return MazeGameGlobals.GAME_DURATION + + def __defineConstants(self): + self.TOON_SPEED = 8.0 + self.TOON_Z = 0 + self.MinSuitSpeedRange = [0.8 * self.TOON_SPEED, 0.6 * self.TOON_SPEED] + self.MaxSuitSpeedRange = [1.1 * self.TOON_SPEED, 2.0 * self.TOON_SPEED] + self.FASTER_SUIT_CURVE = 1 + self.SLOWER_SUIT_CURVE = self.getDifficulty() < 0.5 + self.slowerSuitPeriods = {2000: {4: [128, 76], + 8: [128, + 99, + 81, + 68], + 12: [128, + 108, + 93, + 82, + 74, + 67], + 16: [128, + 112, + 101, + 91, + 83, + 76, + 71, + 66]}, + 1000: {4: [110, 69], + 8: [110, + 88, + 73, + 62], + 12: [110, + 95, + 83, + 74, + 67, + 61], + 16: [110, + 98, + 89, + 81, + 75, + 69, + 64, + 60]}, + 5000: {4: [96, 63], + 8: [96, + 79, + 66, + 57], + 12: [96, + 84, + 75, + 67, + 61, + 56], + 16: [96, + 87, + 80, + 73, + 68, + 63, + 59, + 55]}, + 4000: {4: [86, 58], + 8: [86, + 71, + 61, + 53], + 12: [86, + 76, + 68, + 62, + 56, + 52], + 16: [86, + 78, + 72, + 67, + 62, + 58, + 54, + 51]}, + 3000: {4: [78, 54], + 8: [78, + 65, + 56, + 49], + 12: [78, + 69, + 62, + 57, + 52, + 48], + 16: [78, + 71, + 66, + 61, + 57, + 54, + 51, + 48]}, + 9000: {4: [71, 50], + 8: [71, + 60, + 52, + 46], + 12: [71, + 64, + 58, + 53, + 49, + 45], + 16: [71, + 65, + 61, + 57, + 53, + 50, + 47, + 45]}} + self.slowerSuitPeriodsCurve = {2000: {4: [128, 65], + 8: [128, + 78, + 66, + 64], + 12: [128, + 88, + 73, + 67, + 64, + 64], + 16: [128, + 94, + 79, + 71, + 67, + 65, + 64, + 64]}, + 1000: {4: [110, 59], + 8: [110, + 70, + 60, + 58], + 12: [110, + 78, + 66, + 61, + 59, + 58], + 16: [110, + 84, + 72, + 65, + 61, + 59, + 58, + 58]}, + 5000: {4: [96, 55], + 8: [96, + 64, + 56, + 54], + 12: [96, + 71, + 61, + 56, + 54, + 54], + 16: [96, + 76, + 65, + 59, + 56, + 55, + 54, + 54]}, + 4000: {4: [86, 51], + 8: [86, + 59, + 52, + 50], + 12: [86, + 65, + 56, + 52, + 50, + 50], + 16: [86, + 69, + 60, + 55, + 52, + 51, + 50, + 50]}, + 3000: {4: [78, 47], + 8: [78, + 55, + 48, + 47], + 12: [78, + 60, + 52, + 48, + 47, + 47], + 16: [78, + 63, + 55, + 51, + 49, + 47, + 47, + 47]}, + 9000: {4: [71, 44], + 8: [71, + 51, + 45, + 44], + 12: [71, + 55, + 48, + 45, + 44, + 44], + 16: [71, + 58, + 51, + 48, + 45, + 44, + 44, + 44]}} + self.fasterSuitPeriods = {2000: {4: [54, 42], + 8: [59, + 52, + 47, + 42], + 12: [61, + 56, + 52, + 48, + 45, + 42], + 16: [61, + 58, + 54, + 51, + 49, + 46, + 44, + 42]}, + 1000: {4: [50, 40], + 8: [55, + 48, + 44, + 40], + 12: [56, + 52, + 48, + 45, + 42, + 40], + 16: [56, + 53, + 50, + 48, + 45, + 43, + 41, + 40]}, + 5000: {4: [47, 37], + 8: [51, + 45, + 41, + 37], + 12: [52, + 48, + 45, + 42, + 39, + 37], + 16: [52, + 49, + 47, + 44, + 42, + 40, + 39, + 37]}, + 4000: {4: [44, 35], + 8: [47, + 42, + 38, + 35], + 12: [48, + 45, + 42, + 39, + 37, + 35], + 16: [49, + 46, + 44, + 42, + 40, + 38, + 37, + 35]}, + 3000: {4: [41, 33], + 8: [44, + 40, + 36, + 33], + 12: [45, + 42, + 39, + 37, + 35, + 33], + 16: [45, + 43, + 41, + 39, + 38, + 36, + 35, + 33]}, + 9000: {4: [39, 32], + 8: [41, + 37, + 34, + 32], + 12: [42, + 40, + 37, + 35, + 33, + 32], + 16: [43, + 41, + 39, + 37, + 35, + 34, + 33, + 32]}} + self.fasterSuitPeriodsCurve = {2000: {4: [62, 42], + 8: [63, + 61, + 54, + 42], + 12: [63, + 63, + 61, + 56, + 50, + 42], + 16: [63, + 63, + 62, + 60, + 57, + 53, + 48, + 42]}, + 1000: {4: [57, 40], + 8: [58, + 56, + 50, + 40], + 12: [58, + 58, + 56, + 52, + 46, + 40], + 16: [58, + 58, + 57, + 56, + 53, + 49, + 45, + 40]}, + 5000: {4: [53, 37], + 8: [54, + 52, + 46, + 37], + 12: [54, + 53, + 52, + 48, + 43, + 37], + 16: [54, + 54, + 53, + 51, + 49, + 46, + 42, + 37]}, + 4000: {4: [49, 35], + 8: [50, + 48, + 43, + 35], + 12: [50, + 49, + 48, + 45, + 41, + 35], + 16: [50, + 50, + 49, + 48, + 46, + 43, + 39, + 35]}, + 3000: {4: [46, 33], + 8: [47, + 45, + 41, + 33], + 12: [47, + 46, + 45, + 42, + 38, + 33], + 16: [47, + 46, + 46, + 45, + 43, + 40, + 37, + 33]}, + 9000: {4: [43, 32], + 8: [44, + 42, + 38, + 32], + 12: [44, + 43, + 42, + 40, + 36, + 32], + 16: [44, + 44, + 43, + 42, + 40, + 38, + 35, + 32]}} + self.CELL_WIDTH = MazeData.CELL_WIDTH + self.MAX_FRAME_MOVE = self.CELL_WIDTH / 2 + startOffset = 3 + self.startPosHTable = [[Point3(0, startOffset, self.TOON_Z), 0], + [Point3(0, -startOffset, self.TOON_Z), 180], + [Point3(startOffset, 0, self.TOON_Z), 270], + [Point3(-startOffset, 0, self.TOON_Z), 90]] + self.camOffset = Vec3(0, -19, 45) + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.__defineConstants() + mazeName = MazeGameGlobals.getMazeName(self.doId, self.numPlayers, MazeData.mazeNames) + self.maze = Maze.Maze(mazeName) + model = loader.loadModel('phase_3.5/models/props/mickeySZ') + self.treasureModel = model.find('**/mickeySZ') + model.removeNode() + self.treasureModel.setScale(1.6) + self.treasureModel.setP(-90) + self.music = base.loadMusic('phase_4/audio/bgm/MG_toontag.ogg') + self.toonHitTracks = {} + self.scorePanels = [] + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + del self.toonHitTracks + self.maze.destroy() + del self.maze + self.treasureModel.removeNode() + del self.treasureModel + del self.music + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.maze.onstage() + self.randomNumGen.shuffle(self.startPosHTable) + lt = base.localAvatar + lt.reparentTo(render) + lt.hideName() + self.__placeToon(self.localAvId) + lt.setAnimState('Happy', 1.0) + lt.setSpeed(0, 0) + self.camParent = render.attachNewNode('mazeGameCamParent') + self.camParent.reparentTo(base.localAvatar) + self.camParent.setPos(0, 0, 0) + self.camParent.setHpr(render, 0, 0, 0) + camera.reparentTo(self.camParent) + camera.setPos(self.camOffset) + self.__spawnCameraTask() + self.toonRNGs = [] + for i in xrange(self.numPlayers): + self.toonRNGs.append(RandomNumGen.RandomNumGen(self.randomNumGen)) + + self.treasures = [] + for i in xrange(self.maze.numTreasures): + self.treasures.append(MazeTreasure.MazeTreasure(self.treasureModel, self.maze.treasurePosList[i], i, self.doId)) + + self.__loadSuits() + for suit in self.suits: + suit.onstage() + + self.sndTable = {'hitBySuit': [None] * self.numPlayers, + 'falling': [None] * self.numPlayers} + for i in xrange(self.numPlayers): + self.sndTable['hitBySuit'][i] = base.loadSfx('phase_4/audio/sfx/MG_Tag_C.ogg') + self.sndTable['falling'][i] = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg') + + self.grabSounds = [] + for i in xrange(5): + self.grabSounds.append(base.loadSfx('phase_4/audio/sfx/MG_maze_pickup.ogg')) + + self.grabSoundIndex = 0 + for avId in self.avIdList: + self.toonHitTracks[avId] = Wait(0.1) + + self.scores = [0] * self.numPlayers + self.goalBar = DirectWaitBar(parent=render2d, relief=DGG.SUNKEN, frameSize=(-0.35, + 0.35, + -0.15, + 0.15), borderWidth=(0.02, 0.02), scale=0.42, pos=(0.84, 0, 0.5 - 0.28 * self.numPlayers + 0.05), barColor=(0, 0.7, 0, 1)) + self.goalBar.setBin('unsorted', 0) + self.goalBar.hide() + self.introTrack = self.getIntroTrack() + self.introTrack.start() + return + + def offstage(self): + self.notify.debug('offstage') + if self.introTrack.isPlaying(): + self.introTrack.finish() + del self.introTrack + for avId in self.toonHitTracks.keys(): + track = self.toonHitTracks[avId] + if track.isPlaying(): + track.finish() + + self.__killCameraTask() + camera.wrtReparentTo(render) + self.camParent.removeNode() + del self.camParent + for panel in self.scorePanels: + panel.cleanup() + + self.scorePanels = [] + self.goalBar.destroy() + del self.goalBar + base.setCellsAvailable(base.rightCells, 1) + for suit in self.suits: + suit.offstage() + + self.__unloadSuits() + for treasure in self.treasures: + treasure.destroy() + + del self.treasures + del self.sndTable + del self.grabSounds + del self.toonRNGs + self.maze.offstage() + base.localAvatar.showName() + DistributedMinigame.offstage(self) + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + if self.numPlayers == 1: + toon.setPos(0, 0, self.TOON_Z) + toon.setHpr(180, 0, 0) + else: + posIndex = self.avIdList.index(avId) + toon.setPos(self.startPosHTable[posIndex][0]) + toon.setHpr(self.startPosHTable[posIndex][1], 0, 0) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toon.setAnimState('Happy', 1.0) + toon.startSmooth() + toon.startLookAround() + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + if self.introTrack.isPlaying(): + self.introTrack.finish() + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.stopLookAround() + + self.gameFSM.request('play') + + def handleDisabledAvatar(self, avId): + hitTrack = self.toonHitTracks[avId] + if hitTrack.isPlaying(): + hitTrack.finish() + DistributedMinigame.handleDisabledAvatar(self, avId) + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + for i in xrange(self.numPlayers): + avId = self.avIdList[i] + avName = self.getAvatarName(avId) + scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) + scorePanel.reparentTo(base.a2dTopRight) + scorePanel.setPos(-0.213, 0.0, -0.5 - 0.28 * i) + self.scorePanels.append(scorePanel) + + self.goalBar.show() + self.goalBar['value'] = 0.0 + base.setCellsAvailable(base.rightCells, 0) + self.__spawnUpdateSuitsTask() + orthoDrive = OrthoDrive(self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self.__doMazeCollisions, priority=1) + self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) + self.orthoWalk.start() + self.accept(MazeSuit.COLLISION_EVENT_NAME, self.__hitBySuit) + self.accept(self.TREASURE_GRAB_EVENT_NAME, self.__treasureGrabbed) + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(MazeGameGlobals.GAME_DURATION) + self.timer.countdown(MazeGameGlobals.GAME_DURATION, self.timerExpired) + self.accept('resetClock', self.__resetClock) + base.playMusic(self.music, looping=0, volume=0.8) + + def exitPlay(self): + self.notify.debug('exitPlay') + self.ignore('resetClock') + self.ignore(MazeSuit.COLLISION_EVENT_NAME) + self.ignore(self.TREASURE_GRAB_EVENT_NAME) + self.orthoWalk.stop() + self.orthoWalk.destroy() + del self.orthoWalk + self.__killUpdateSuitsTask() + self.timer.stop() + self.timer.destroy() + del self.timer + for avId in self.avIdList: + toon = self.getAvatar(avId) + if toon: + toon.loop('neutral') + + def __resetClock(self, tOffset): + self.notify.debug('resetClock') + self.gameStartTime += tOffset + self.timer.countdown(self.timer.currentTime + tOffset, self.timerExpired) + + def __treasureGrabbed(self, treasureNum): + self.treasures[treasureNum].showGrab() + self.grabSounds[self.grabSoundIndex].play() + self.grabSoundIndex = (self.grabSoundIndex + 1) % len(self.grabSounds) + self.sendUpdate('claimTreasure', [treasureNum]) + + def setTreasureGrabbed(self, avId, treasureNum): + if not self.hasLocalToon: + return + if avId != self.localAvId: + self.treasures[treasureNum].showGrab() + i = self.avIdList.index(avId) + self.scores[i] += 1 + self.scorePanels[i].setScore(self.scores[i]) + total = 0 + for score in self.scores: + total += score + + self.goalBar['value'] = 100.0 * (float(total) / float(self.maze.numTreasures)) + + def __hitBySuit(self, suitNum): + self.notify.debug('hitBySuit') + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.sendUpdate('hitBySuit', [self.localAvId, timestamp]) + self.__showToonHitBySuit(self.localAvId, timestamp) + + def hitBySuit(self, avId, timestamp): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() not in ['play', 'showScores']: + self.notify.warning('ignoring msg: av %s hit by suit' % avId) + return + self.notify.debug('avatar ' + `avId` + ' hit by a suit') + if avId != self.localAvId: + self.__showToonHitBySuit(avId, timestamp) + + def __showToonHitBySuit(self, avId, timestamp): + toon = self.getAvatar(avId) + if toon == None: + return + rng = self.toonRNGs[self.avIdList.index(avId)] + curPos = toon.getPos(render) + oldTrack = self.toonHitTracks[avId] + if oldTrack.isPlaying(): + oldTrack.finish() + toon.setPos(curPos) + toon.setZ(self.TOON_Z) + parentNode = render.attachNewNode('mazeFlyToonParent-' + `avId`) + parentNode.setPos(toon.getPos()) + toon.reparentTo(parentNode) + toon.setPos(0,0,0) + startPos = parentNode.getPos() + dropShadow = toon.dropShadow.copyTo(parentNode) + dropShadow.setScale(toon.dropShadow.getScale(render)) + trajectory = Trajectory.Trajectory( + 0, + Point3(0,0,0), + Point3(0,0,50), + gravMult=1.0) + flyDur = trajectory.calcTimeOfImpactOnPlane(0.0) + while 1: + endTile = [rng.randint(2, self.maze.width-1), rng.randint(2, self.maze.height-1)] + if self.maze.isWalkable(endTile[0], endTile[1]): + break + endWorldCoords = self.maze.tile2world(endTile[0], endTile[1]) + endPos = Point3(endWorldCoords[0], endWorldCoords[1], startPos[2]) + def flyFunc(t, trajectory, startPos = startPos, endPos = endPos, dur = flyDur, moveNode = parentNode, flyNode = toon): + u = t/dur + moveNode.setX(startPos[0] + u * (endPos[0]-startPos[0])) + moveNode.setY(startPos[1] + u * (endPos[1]-startPos[1])) + flyNode.setPos(trajectory.getPos(t)) + flyTrack = Sequence( + LerpFunctionInterval(flyFunc, fromData=0.0, toData=flyDur, duration=flyDur, extraArgs=[trajectory]), + name=toon.uniqueName('hitBySuit-fly')) + if avId != self.localAvId: + cameraTrack = Sequence() + else: + self.camParent.reparentTo(parentNode) + startCamPos = camera.getPos() + destCamPos = camera.getPos() + zenith = trajectory.getPos(flyDur/2.0)[2] + destCamPos.setZ(zenith*1.3) + destCamPos.setY(destCamPos[1]*0.3) + def camTask(task, zenith = zenith, flyNode = toon, startCamPos = startCamPos, camOffset = destCamPos - startCamPos): + u = flyNode.getZ()/zenith + camera.setPos(startCamPos + camOffset*u) + camera.lookAt(toon) + return Task.cont + camTaskName = 'mazeToonFlyCam-' + `avId` + taskMgr.add(camTask, camTaskName, priority=20) + def cleanupCamTask(self = self, toon = toon, camTaskName = camTaskName, startCamPos = startCamPos): + taskMgr.remove(camTaskName) + self.camParent.reparentTo(toon) + camera.setPos(startCamPos) + camera.lookAt(toon) + + cameraTrack = Sequence( + Wait(flyDur), + Func(cleanupCamTask), + name='hitBySuit-cameraLerp') + + geomNode = toon.getGeomNode() + startHpr = geomNode.getHpr() + destHpr = Point3(startHpr) + hRot = rng.randrange(1, 8) + if rng.choice([0, 1]): + hRot = -hRot + destHpr.setX(destHpr[0] + hRot*360) + spinHTrack = Sequence( + LerpHprInterval(geomNode, flyDur, destHpr, startHpr=startHpr), + Func(geomNode.setHpr, startHpr), + name=toon.uniqueName('hitBySuit-spinH')) + parent = geomNode.getParent() + rotNode = parent.attachNewNode('rotNode') + geomNode.reparentTo(rotNode) + rotNode.setZ(toon.getHeight()/2.0) + oldGeomNodeZ = geomNode.getZ() + geomNode.setZ(-toon.getHeight()/2.0) + startHpr = rotNode.getHpr() + destHpr = Point3(startHpr) + pRot = rng.randrange(1,3) + if rng.choice([0, 1]): + pRot = -pRot + destHpr.setY(destHpr[1] + pRot*360) + spinPTrack = Sequence( + LerpHprInterval(rotNode, flyDur, destHpr, startHpr=startHpr), + Func(rotNode.setHpr, startHpr), + name=toon.uniqueName('hitBySuit-spinP')) + i = self.avIdList.index(avId) + soundTrack = Sequence( + Func(base.playSfx, self.sndTable['hitBySuit'][i]), + Wait(flyDur * (2.0/3.0)), + SoundInterval(self.sndTable['falling'][i], + duration=flyDur * (1.0/3.0)), + name=toon.uniqueName('hitBySuit-soundTrack')) + + def preFunc(self = self, avId = avId, toon = toon, dropShadow = dropShadow): + forwardSpeed = toon.forwardSpeed + rotateSpeed = toon.rotateSpeed + if avId == self.localAvId: + self.orthoWalk.stop() + else: + toon.stopSmooth() + if forwardSpeed or rotateSpeed: + toon.setSpeed(forwardSpeed, rotateSpeed) + toon.dropShadow.hide() + + def postFunc(self = self, avId = avId, oldGeomNodeZ = oldGeomNodeZ, dropShadow = dropShadow, parentNode = parentNode): + if avId == self.localAvId: + base.localAvatar.setPos(endPos) + if hasattr(self, 'orthoWalk'): + if self.gameFSM.getCurrentState().getName() == 'play': + self.orthoWalk.start() + dropShadow.removeNode() + del dropShadow + toon.dropShadow.show() + geomNode = toon.getGeomNode() + rotNode = geomNode.getParent() + baseNode = rotNode.getParent() + geomNode.reparentTo(baseNode) + rotNode.removeNode() + del rotNode + geomNode.setZ(oldGeomNodeZ) + toon.reparentTo(render) + toon.setPos(endPos) + parentNode.removeNode() + del parentNode + if avId != self.localAvId: + toon.startSmooth() + + preFunc() + + hitTrack = Sequence(Parallel(flyTrack, cameraTrack, + spinHTrack, spinPTrack, soundTrack), + Func(postFunc), + name=toon.uniqueName('hitBySuit')) + + self.toonHitTracks[avId] = hitTrack + + hitTrack.start(globalClockDelta.localElapsedTime(timestamp)) + + def allTreasuresTaken(self): + if not self.hasLocalToon: + return + self.notify.debug('all treasures taken') + if not MazeGameGlobals.ENDLESS_GAME: + self.gameFSM.request('showScores') + + def timerExpired(self): + self.notify.debug('local timer expired') + if not MazeGameGlobals.ENDLESS_GAME: + self.gameFSM.request('showScores') + + def __doMazeCollisions(self, oldPos, newPos): + offset = newPos - oldPos + WALL_OFFSET = 1.0 + curX = oldPos[0] + curY = oldPos[1] + curTX, curTY = self.maze.world2tile(curX, curY) + + def calcFlushCoord(curTile, newTile, centerTile): + EPSILON = 0.01 + if newTile > curTile: + return (newTile - centerTile) * self.CELL_WIDTH - EPSILON - WALL_OFFSET + else: + return (curTile - centerTile) * self.CELL_WIDTH + WALL_OFFSET + + offsetX = offset[0] + offsetY = offset[1] + WALL_OFFSET_X = WALL_OFFSET + if offsetX < 0: + WALL_OFFSET_X = -WALL_OFFSET_X + WALL_OFFSET_Y = WALL_OFFSET + if offsetY < 0: + WALL_OFFSET_Y = -WALL_OFFSET_Y + newX = curX + offsetX + WALL_OFFSET_X + newY = curY + newTX, newTY = self.maze.world2tile(newX, newY) + if newTX != curTX: + if self.maze.collisionTable[newTY][newTX]: + offset.setX(calcFlushCoord(curTX, newTX, self.maze.originTX) - curX) + newX = curX + newY = curY + offsetY + WALL_OFFSET_Y + newTX, newTY = self.maze.world2tile(newX, newY) + if newTY != curTY: + if self.maze.collisionTable[newTY][newTX]: + offset.setY(calcFlushCoord(curTY, newTY, self.maze.originTY) - curY) + offsetX = offset[0] + offsetY = offset[1] + newX = curX + offsetX + WALL_OFFSET_X + newY = curY + offsetY + WALL_OFFSET_Y + newTX, newTY = self.maze.world2tile(newX, newY) + if self.maze.collisionTable[newTY][newTX]: + cX = calcFlushCoord(curTX, newTX, self.maze.originTX) + cY = calcFlushCoord(curTY, newTY, self.maze.originTY) + if abs(cX - curX) < abs(cY - curY): + offset.setX(cX - curX) + else: + offset.setY(cY - curY) + return oldPos + offset + + def __spawnCameraTask(self): + self.notify.debug('spawnCameraTask') + camera.lookAt(base.localAvatar) + taskMgr.remove(self.CAMERA_TASK) + taskMgr.add(self.__cameraTask, self.CAMERA_TASK, priority=45) + + def __killCameraTask(self): + self.notify.debug('killCameraTask') + taskMgr.remove(self.CAMERA_TASK) + + def __cameraTask(self, task): + self.camParent.setHpr(render, 0, 0, 0) + return Task.cont + + def __loadSuits(self): + self.notify.debug('loadSuits') + self.suits = [] + self.numSuits = 4 * self.numPlayers + safeZone = self.getSafezoneId() + slowerTable = self.slowerSuitPeriods + if self.SLOWER_SUIT_CURVE: + slowerTable = self.slowerSuitPeriodsCurve + slowerPeriods = slowerTable[safeZone][self.numSuits] + fasterTable = self.fasterSuitPeriods + if self.FASTER_SUIT_CURVE: + fasterTable = self.fasterSuitPeriodsCurve + fasterPeriods = fasterTable[safeZone][self.numSuits] + suitPeriods = slowerPeriods + fasterPeriods + self.notify.debug('suit periods: ' + `suitPeriods`) + self.randomNumGen.shuffle(suitPeriods) + for i in xrange(self.numSuits): + self.suits.append(MazeSuit(i, self.maze, self.randomNumGen, suitPeriods[i], self.getDifficulty())) + + def __unloadSuits(self): + self.notify.debug('unloadSuits') + for suit in self.suits: + suit.destroy() + + self.suits = [] + + def __spawnUpdateSuitsTask(self): + self.notify.debug('spawnUpdateSuitsTask') + for suit in self.suits: + suit.gameStart(self.gameStartTime) + + taskMgr.remove(self.UPDATE_SUITS_TASK) + taskMgr.add(self.__updateSuitsTask, self.UPDATE_SUITS_TASK) + + def __killUpdateSuitsTask(self): + self.notify.debug('killUpdateSuitsTask') + taskMgr.remove(self.UPDATE_SUITS_TASK) + for suit in self.suits: + suit.gameEnd() + + def __updateSuitsTask(self, task): + curT = globalClock.getFrameTime() - self.gameStartTime + curTic = int(curT * float(MazeGameGlobals.SUIT_TIC_FREQ)) + suitUpdates = [] + for i in xrange(len(self.suits)): + updateTics = self.suits[i].getThinkTimestampTics(curTic) + suitUpdates.extend(zip(updateTics, [i] * len(updateTics))) + + suitUpdates.sort(lambda a, b: a[0] - b[0]) + if len(suitUpdates) > 0: + curTic = 0 + for i in xrange(len(suitUpdates)): + update = suitUpdates[i] + tic = update[0] + suitIndex = update[1] + suit = self.suits[suitIndex] + if tic > curTic: + curTic = tic + j = i + 1 + while j < len(suitUpdates): + if suitUpdates[j][0] > tic: + break + self.suits[suitUpdates[j][1]].prepareToThink() + j += 1 + + unwalkables = [] + for si in xrange(suitIndex): + unwalkables.extend(self.suits[si].occupiedTiles) + + for si in xrange(suitIndex + 1, len(self.suits)): + unwalkables.extend(self.suits[si].occupiedTiles) + + suit.think(curTic, curT, unwalkables) + + return Task.cont + + def enterShowScores(self): + self.notify.debug('enterShowScores') + lerpTrack = Parallel() + lerpDur = 0.5 + lerpTrack.append(Parallel(LerpPosInterval(self.goalBar, lerpDur, Point3(0, 0, -.6), blendType='easeInOut'), LerpScaleInterval(self.goalBar, lerpDur, Vec3(self.goalBar.getScale()) * 2.0, blendType='easeInOut'))) + tY = 0.6 + bY = -.05 + lX = -.5 + cX = 0 + rX = 0.5 + scorePanelLocs = (((cX, bY),), + ((lX, bY), (rX, bY)), + ((cX, tY), (lX, bY), (rX, bY)), + ((lX, tY), + (rX, tY), + (lX, bY), + (rX, bY))) + scorePanelLocs = scorePanelLocs[self.numPlayers - 1] + for i in xrange(self.numPlayers): + panel = self.scorePanels[i] + pos = scorePanelLocs[i] + panel.wrtReparentTo(aspect2d) + lerpTrack.append(Parallel(LerpPosInterval(panel, lerpDur, Point3(pos[0], 0, pos[1]), blendType='easeInOut'), LerpScaleInterval(panel, lerpDur, Vec3(panel.getScale()) * 2.0, blendType='easeInOut'))) + + self.showScoreTrack = Parallel(lerpTrack, Sequence(Wait(MazeGameGlobals.SHOWSCORES_DURATION), Func(self.gameOver))) + self.showScoreTrack.start() + + def exitShowScores(self): + self.showScoreTrack.pause() + del self.showScoreTrack + + def enterCleanup(self): + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass + + def getIntroTrack(self): + self.__cameraTask(None) + origCamParent = camera.getParent() + origCamPos = camera.getPos() + origCamHpr = camera.getHpr() + iCamParent = base.localAvatar.attachNewNode('iCamParent') + iCamParent.setH(180) + camera.reparentTo(iCamParent) + toonHeight = base.localAvatar.getHeight() + camera.setPos(0, -15, toonHeight * 3) + camera.lookAt(0, 0, toonHeight / 2.0) + iCamParent.wrtReparentTo(origCamParent) + waitDur = 5.0 + lerpDur = 4.5 + lerpTrack = Parallel() + startHpr = iCamParent.getHpr() + startHpr.setX(PythonUtil.reduceAngle(startHpr[0])) + lerpTrack.append(LerpPosHprInterval(iCamParent, lerpDur, pos=Point3(0, 0, 0), hpr=Point3(0, 0, 0), startHpr=startHpr, name=self.uniqueName('introLerpParent'))) + lerpTrack.append(LerpPosHprInterval(camera, lerpDur, pos=origCamPos, hpr=origCamHpr, blendType='easeInOut', name=self.uniqueName('introLerpCameraPos'))) + base.localAvatar.startLookAround() + + def cleanup(origCamParent = origCamParent, origCamPos = origCamPos, origCamHpr = origCamHpr, iCamParent = iCamParent): + camera.reparentTo(origCamParent) + camera.setPos(origCamPos) + camera.setHpr(origCamHpr) + iCamParent.removeNode() + del iCamParent + base.localAvatar.stopLookAround() + + return Sequence(Wait(waitDur), + lerpTrack, + Func(cleanup)) diff --git a/toontown/minigame/DistributedMazeGameAI.py b/toontown/minigame/DistributedMazeGameAI.py new file mode 100755 index 00000000..6e905b8e --- /dev/null +++ b/toontown/minigame/DistributedMazeGameAI.py @@ -0,0 +1,129 @@ +from DistributedMinigameAI import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import PatternGameGlobals +from direct.task.Task import Task +import MazeGameGlobals +import MazeData + +class DistributedMazeGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedMinigameTemplateAI_initialized + except: + self.DistributedMinigameTemplateAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedMazeGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), + State.State('play', self.enterPlay, self.exitPlay, ['waitShowScores', 'cleanup']), + State.State('waitShowScores', self.enterWaitShowScores, self.exitWaitShowScores, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + mazeName = MazeGameGlobals.getMazeName(self.doId, self.numPlayers, MazeData.mazeNames) + mData = MazeData.mazeData[mazeName] + self.numTreasures = len(mData['treasurePosList']) + self.numTreasuresTaken = 0 + self.takenTable = [0] * self.numTreasures + for avId in self.scoreDict.keys(): + self.scoreDict[avId] = 0 + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def getTimeBase(self): + return self.__timeBase + + def enterPlay(self): + self.notify.debug('enterPlay') + taskMgr.doMethodLater(MazeGameGlobals.GAME_DURATION, self.timerExpired, self.taskName('gameTimer')) + + def exitPlay(self): + taskMgr.remove(self.taskName('gameTimer')) + + def claimTreasure(self, treasureNum): + if self.gameFSM.getCurrentState() is None or self.gameFSM.getCurrentState().getName() != 'play': + return + avId = self.air.getAvatarIdFromSender() + if avId not in self.scoreDict: + self.notify.warning('PROBLEM: avatar %s called claimTreasure(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId, + treasureNum, + self.scoreDict, + self.avIdList)) + return + if treasureNum < 0 or treasureNum >= self.numTreasures: + self.air.writeServerEvent('warning', treasureNum, 'MazeGameAI.claimTreasure treasureNum out of range') + return + if self.takenTable[treasureNum]: + return + self.takenTable[treasureNum] = 1 + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('setTreasureGrabbed', [avId, treasureNum]) + self.scoreDict[avId] += 1 + self.numTreasuresTaken += 1 + if self.numTreasuresTaken >= self.numTreasures: + self.logAllPerfect() + self.sendUpdate('allTreasuresTaken', []) + if not MazeGameGlobals.ENDLESS_GAME: + self.gameFSM.request('waitShowScores') + return + + def timerExpired(self, task): + self.notify.debug('timer expired') + if not MazeGameGlobals.ENDLESS_GAME: + self.gameFSM.request('waitShowScores') + return Task.done + + def enterWaitShowScores(self): + self.notify.debug('enterWaitShowScores') + taskMgr.doMethodLater(MazeGameGlobals.SHOWSCORES_DURATION, self.__doneShowingScores, self.taskName('waitShowScores')) + + def __doneShowingScores(self, task): + self.notify.debug('doneShowingScores') + for key in self.scoreDict.keys(): + self.scoreDict[key] = max(1, self.scoreDict[key] / 12) + + if self.numTreasuresTaken >= self.numTreasures: + for key in self.scoreDict.keys(): + self.scoreDict[key] += 8 + + self.gameOver() + return Task.done + + def exitWaitShowScores(self): + taskMgr.remove(self.taskName('waitShowScores')) + + def enterCleanup(self): + self.notify.debug('enterCleanup') + del self.takenTable + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedMinigame.py b/toontown/minigame/DistributedMinigame.py new file mode 100755 index 00000000..bcc6aa5a --- /dev/null +++ b/toontown/minigame/DistributedMinigame.py @@ -0,0 +1,439 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.gui.DirectGui import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import MinigameRulesPanel +from direct.task.Task import Task +from toontown.toon import Toon +from direct.showbase import RandomNumGen +from toontown.toonbase import TTLocalizer +import random +import MinigameGlobals +from direct.showbase import PythonUtil +from toontown.toon import TTEmote +from otp.avatar import Emote +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from otp.ai.MagicWordGlobal import * + +class DistributedMinigame(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMinigame') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.waitingStartLabel = DirectLabel(text=TTLocalizer.MinigameWaitingForOtherToons, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075) + self.waitingStartLabel.hide() + self.avIdList = [] + self.remoteAvIdList = [] + self.localAvId = base.localAvatar.doId + self.frameworkFSM = ClassicFSM.ClassicFSM('DistributedMinigame', [State.State('frameworkInit', self.enterFrameworkInit, self.exitFrameworkInit, ['frameworkRules', 'frameworkCleanup', 'frameworkAvatarExited']), + State.State('frameworkRules', self.enterFrameworkRules, self.exitFrameworkRules, ['frameworkWaitServerStart', 'frameworkCleanup', 'frameworkAvatarExited']), + State.State('frameworkWaitServerStart', self.enterFrameworkWaitServerStart, self.exitFrameworkWaitServerStart, ['frameworkGame', 'frameworkCleanup', 'frameworkAvatarExited']), + State.State('frameworkGame', self.enterFrameworkGame, self.exitFrameworkGame, ['frameworkWaitServerFinish', 'frameworkCleanup', 'frameworkAvatarExited']), + State.State('frameworkWaitServerFinish', self.enterFrameworkWaitServerFinish, self.exitFrameworkWaitServerFinish, ['frameworkCleanup']), + State.State('frameworkAvatarExited', self.enterFrameworkAvatarExited, self.exitFrameworkAvatarExited, ['frameworkCleanup']), + State.State('frameworkCleanup', self.enterFrameworkCleanup, self.exitFrameworkCleanup, [])], 'frameworkInit', 'frameworkCleanup') + hoodMinigameState = self.cr.playGame.hood.fsm.getStateNamed('minigame') + hoodMinigameState.addChild(self.frameworkFSM) + self.rulesDoneEvent = 'rulesDone' + self.acceptOnce('minigameAbort', self.d_requestExit) + self.acceptOnce('minigameSkip', self.requestSkip) + base.curMinigame = self + self.modelCount = 500 + self.cleanupActions = [] + self.usesSmoothing = 0 + self.usesLookAround = 0 + self.difficultyOverride = None + self.trolleyZoneOverride = None + self.hasLocalToon = 0 + self.frameworkFSM.enterInitialState() + self._telemLimiter = None + return + + def addChildGameFSM(self, gameFSM): + self.frameworkFSM.getStateNamed('frameworkGame').addChild(gameFSM) + + def removeChildGameFSM(self, gameFSM): + self.frameworkFSM.getStateNamed('frameworkGame').removeChild(gameFSM) + + def setUsesSmoothing(self): + self.usesSmoothing = 1 + + def setUsesLookAround(self): + self.usesLookAround = 1 + + def getTitle(self): + return TTLocalizer.DefaultMinigameTitle + + def getInstructions(self): + return TTLocalizer.DefaultMinigameInstructions + + def getMaxDuration(self): + raise Exception('Minigame implementer: you must override getMaxDuration()') + + def __createRandomNumGen(self): + self.notify.debug('BASE: self.doId=0x%08X' % self.doId) + self.randomNumGen = RandomNumGen.RandomNumGen(self.doId) + + def destroy(self = self): + self.notify.debug('BASE: destroying random num gen') + del self.randomNumGen + + self.cleanupActions.append(destroy) + + def generate(self): + self.notify.debug('BASE: generate, %s' % self.getTitle()) + DistributedObject.DistributedObject.generate(self) + self.__createRandomNumGen() + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + if not self.hasLocalToon: + return + self.notify.debug('BASE: handleAnnounceGenerate: send setAvatarJoined') + if base.randomMinigameNetworkPlugPull and random.random() < 1.0 / 25: + print '*** DOING RANDOM MINIGAME NETWORK-PLUG-PULL BEFORE SENDING setAvatarJoined ***' + base.cr.pullNetworkPlug() + self.sendUpdate('setAvatarJoined', []) + self.normalExit = 1 + count = self.modelCount + zoneId = 0 #TODO: Make a system for picking minigame backgrounds + loader.beginBulkLoad('minigame', TTLocalizer.HeadingToMinigameTitle % self.getTitle(), count, 1, TTLocalizer.TIP_MINIGAME, zoneId) + self.load() + loader.endBulkLoad('minigame') + globalClock.syncFrameTime() + self.onstage() + + def cleanup(self = self): + self.notify.debug('BASE: cleanup: normalExit=%s' % self.normalExit) + self.offstage() + base.cr.renderFrame() + if self.normalExit: + self.sendUpdate('setAvatarExited', []) + + self.cleanupActions.append(cleanup) + self._telemLimiter = self.getTelemetryLimiter() + self.frameworkFSM.request('frameworkRules') + + def disable(self): + self.notify.debug('BASE: disable') + if self._telemLimiter: + self._telemLimiter.destroy() + self._telemLimiter = None + self.frameworkFSM.request('frameworkCleanup') + taskMgr.remove(self.uniqueName('random-abort')) + taskMgr.remove(self.uniqueName('random-disconnect')) + taskMgr.remove(self.uniqueName('random-netplugpull')) + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + self.notify.debug('BASE: delete') + if self.hasLocalToon: + self.unload() + self.ignoreAll() + if self.cr.playGame.hood: + hoodMinigameState = self.cr.playGame.hood.fsm.getStateNamed('minigame') + hoodMinigameState.removeChild(self.frameworkFSM) + self.waitingStartLabel.destroy() + del self.waitingStartLabel + del self.frameworkFSM + DistributedObject.DistributedObject.delete(self) + + def getTelemetryLimiter(self): + return TLGatherAllAvs('Minigame', RotationLimitToH) + + def load(self): + self.notify.debug('BASE: load') + Toon.loadMinigameAnims() + + def onstage(self): + base.localAvatar.laffMeter.hide() + + def calcMaxDuration(self = self): + return (self.getMaxDuration() + MinigameGlobals.rulesDuration) * 1.1 + + if not base.cr.networkPlugPulled(): + if base.randomMinigameAbort: + maxDuration = calcMaxDuration() + self.randomAbortDelay = random.random() * maxDuration + taskMgr.doMethodLater(self.randomAbortDelay, self.doRandomAbort, self.uniqueName('random-abort')) + if base.randomMinigameDisconnect: + maxDuration = calcMaxDuration() + self.randomDisconnectDelay = random.random() * maxDuration + taskMgr.doMethodLater(self.randomDisconnectDelay, self.doRandomDisconnect, self.uniqueName('random-disconnect')) + if base.randomMinigameNetworkPlugPull: + maxDuration = calcMaxDuration() + self.randomNetPlugPullDelay = random.random() * maxDuration + taskMgr.doMethodLater(self.randomNetPlugPullDelay, self.doRandomNetworkPlugPull, self.uniqueName('random-netplugpull')) + + def doRandomAbort(self, task): + print '*** DOING RANDOM MINIGAME ABORT AFTER %.2f SECONDS ***' % self.randomAbortDelay + self.d_requestExit() + return Task.done + + def doRandomDisconnect(self, task): + print '*** DOING RANDOM MINIGAME DISCONNECT AFTER %.2f SECONDS ***' % self.randomDisconnectDelay + self.sendUpdate('setGameReady') + return Task.done + + def doRandomNetworkPlugPull(self, task): + print '*** DOING RANDOM MINIGAME NETWORK-PLUG-PULL AFTER %.2f SECONDS ***' % self.randomNetPlugPullDelay + base.cr.pullNetworkPlug() + return Task.done + + def offstage(self): + self.notify.debug('BASE: offstage') + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.detachNode() + + messenger.send('minigameOffstage') + + def unload(self): + self.notify.debug('BASE: unload') + if hasattr(base, 'curMinigame'): + del base.curMinigame + Toon.unloadMinigameAnims() + + def setParticipants(self, avIds): + self.avIdList = avIds + self.numPlayers = len(self.avIdList) + self.hasLocalToon = self.localAvId in self.avIdList + if not self.hasLocalToon: + self.notify.warning('localToon (%s) not in list of minigame players: %s' % (self.localAvId, self.avIdList)) + return + self.notify.info('BASE: setParticipants: %s' % self.avIdList) + self.remoteAvIdList = [] + for avId in self.avIdList: + if avId != self.localAvId: + self.remoteAvIdList.append(avId) + self.setSkipCount(0) + + def setTrolleyZone(self, trolleyZone): + if not self.hasLocalToon: + return + self.notify.debug('BASE: setTrolleyZone: %s' % trolleyZone) + self.trolleyZone = trolleyZone + + def setDifficultyOverrides(self, difficultyOverride, trolleyZoneOverride): + if not self.hasLocalToon: + return + if difficultyOverride != MinigameGlobals.NoDifficultyOverride: + self.difficultyOverride = difficultyOverride / float(MinigameGlobals.DifficultyOverrideMult) + if trolleyZoneOverride != MinigameGlobals.NoTrolleyZoneOverride: + self.trolleyZoneOverride = trolleyZoneOverride + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('BASE: setGameReady: Ready for game with avatars: %s' % self.avIdList) + self.notify.debug(' safezone: %s' % self.getSafezoneId()) + self.notify.debug('difficulty: %s' % self.getDifficulty()) + self.__serverFinished = 0 + for avId in self.remoteAvIdList: + if avId not in self.cr.doId2do: + self.notify.warning('BASE: toon %s already left or has not yet arrived; waiting for server to abort the game' % avId) + return 1 + + for avId in self.remoteAvIdList: + avatar = self.cr.doId2do[avId] + event = avatar.uniqueName('disable') + self.acceptOnce(event, self.handleDisabledAvatar, [avId]) + + def ignoreToonDisable(self = self, event = event): + self.ignore(event) + + self.cleanupActions.append(ignoreToonDisable) + + for avId in self.avIdList: + avatar = self.getAvatar(avId) + if avatar: + if not self.usesSmoothing: + avatar.stopSmooth() + if not self.usesLookAround: + avatar.stopLookAround() + + def cleanupAvatars(self = self): + for avId in self.avIdList: + avatar = self.getAvatar(avId) + if avatar: + avatar.stopSmooth() + avatar.startLookAround() + + self.cleanupActions.append(cleanupAvatars) + return 0 + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('BASE: setGameStart: Starting game') + self.gameStartTime = globalClockDelta.networkToLocalTime(timestamp) + self.frameworkFSM.request('frameworkGame') + + def setGameAbort(self): + if not self.hasLocalToon: + return + self.notify.warning('BASE: setGameAbort: Aborting game') + self.normalExit = 0 + self.frameworkFSM.request('frameworkCleanup') + + def gameOver(self): + if not self.hasLocalToon: + return + self.notify.debug('BASE: gameOver') + self.frameworkFSM.request('frameworkWaitServerFinish') + + def getAvatar(self, avId): + if avId in self.cr.doId2do: + return self.cr.doId2do[avId] + else: + self.notify.warning('BASE: getAvatar: No avatar in doId2do with id: ' + str(avId)) + return None + return None + + def getAvatarName(self, avId): + avatar = self.getAvatar(avId) + if avatar: + return avatar.getName() + else: + return 'Unknown' + + def isSinglePlayer(self): + if self.numPlayers == 1: + return 1 + else: + return 0 + + def handleDisabledAvatar(self, avId): + self.notify.warning('BASE: handleDisabledAvatar: disabled avId: ' + str(avId)) + self.frameworkFSM.request('frameworkAvatarExited') + + def d_requestExit(self): + self.notify.debug('BASE: Sending requestExit') + self.sendUpdate('requestExit', []) + + def enterFrameworkInit(self): + self.notify.debug('BASE: enterFrameworkInit') + self.setEmotes() + self.cleanupActions.append(self.unsetEmotes) + + def exitFrameworkInit(self): + pass + + def enterFrameworkRules(self): + self.notify.debug('BASE: enterFrameworkRules') + self.accept(self.rulesDoneEvent, self.handleRulesDone) + self.rulesPanel = MinigameRulesPanel.MinigameRulesPanel('MinigameRulesPanel', self.getTitle(), self.getInstructions(), self.rulesDoneEvent, playerCount=len(self.avIdList)) + self.rulesPanel.load() + self.rulesPanel.enter() + + def exitFrameworkRules(self): + self.ignore(self.rulesDoneEvent) + self.rulesPanel.exit() + self.rulesPanel.unload() + del self.rulesPanel + + def handleRulesDone(self): + self.notify.debug('BASE: handleRulesDone') + self.sendUpdate('setAvatarReady', []) + self.frameworkFSM.request('frameworkWaitServerStart') + + def setAvatarReady(self): + messenger.send('disableMinigameSkip') + + def enterFrameworkWaitServerStart(self): + self.notify.debug('BASE: enterFrameworkWaitServerStart') + if self.numPlayers > 1: + msg = TTLocalizer.MinigameWaitingForOtherToons + else: + msg = TTLocalizer.MinigamePleaseWait + self.waitingStartLabel['text'] = msg + self.waitingStartLabel.show() + + def exitFrameworkWaitServerStart(self): + self.waitingStartLabel.hide() + + def enterFrameworkGame(self): + self.notify.debug('BASE: enterFrameworkGame') + + def exitFrameworkGame(self): + pass + + def enterFrameworkWaitServerFinish(self): + self.notify.debug('BASE: enterFrameworkWaitServerFinish') + if self.__serverFinished: + self.frameworkFSM.request('frameworkCleanup') + + def setGameExit(self): + if not self.hasLocalToon: + return + + self.notify.debug('BASE: setGameExit -- it is now safe to exit the game.') + if self.frameworkFSM.getCurrentState().getName() != 'frameworkWaitServerFinish': + self.__serverFinished = 1 + else: + self.notify.debug("Must wait for server to exit game: ask the framework to cleanup.") + self.frameworkFSM.request('frameworkCleanup') + + def exitFrameworkWaitServerFinish(self): + pass + + def enterFrameworkAvatarExited(self): + self.notify.debug('BASE: enterFrameworkAvatarExited') + + def exitFrameworkAvatarExited(self): + pass + + def enterFrameworkCleanup(self): + self.notify.debug('BASE: enterFrameworkCleanup') + for action in self.cleanupActions: + action() + + self.cleanupActions = [] + self.ignoreAll() + if self.hasLocalToon: + messenger.send(self.cr.playGame.hood.minigameDoneEvent) + + def exitFrameworkCleanup(self): + pass + + def local2GameTime(self, timestamp): + return timestamp - self.gameStartTime + + def game2LocalTime(self, timestamp): + return timestamp + self.gameStartTime + + def getCurrentGameTime(self): + return self.local2GameTime(globalClock.getFrameTime()) + + def getDifficulty(self): + if self.difficultyOverride is not None: + return self.difficultyOverride + if hasattr(base, 'minigameDifficulty'): + return float(base.minigameDifficulty) + return MinigameGlobals.getDifficulty(self.getSafezoneId()) + + def getSafezoneId(self): + if self.trolleyZoneOverride is not None: + return self.trolleyZoneOverride + if hasattr(base, 'minigameSafezoneId'): + return MinigameGlobals.getSafezoneId(base.minigameSafezoneId) + return MinigameGlobals.getSafezoneId(self.trolleyZone) + + def setEmotes(self): + Emote.globalEmote.disableAll(base.localAvatar) + + def unsetEmotes(self): + Emote.globalEmote.releaseAll(base.localAvatar) + + def requestSkip(self): + self.sendUpdate('requestSkip') + + def setSkipCount(self, count): + messenger.send('gameSkipCountChange', [count, len(self.avIdList)]) diff --git a/toontown/minigame/DistributedMinigameAI.py b/toontown/minigame/DistributedMinigameAI.py new file mode 100755 index 00000000..0f76ac95 --- /dev/null +++ b/toontown/minigame/DistributedMinigameAI.py @@ -0,0 +1,380 @@ +from otp.ai.AIBase import * +from direct.distributed.ClockDelta import * +from toontown.ai.ToonBarrier import * +from direct.distributed import DistributedObjectAI +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.shtiker import PurchaseManagerAI +from toontown.shtiker import NewbiePurchaseManagerAI +import MinigameCreatorAI +from direct.task import Task +import MinigameGlobals +from direct.showbase import PythonUtil +from toontown.toonbase import ToontownGlobals +EXITED = 0 +EXPECTED = 1 +JOINED = 2 +READY = 3 +DEFAULT_POINTS = 1 +MAX_POINTS = 7 +JOIN_TIMEOUT = 40.0 + MinigameGlobals.latencyTolerance +READY_TIMEOUT = MinigameGlobals.MaxLoadTime + MinigameGlobals.rulesDuration + MinigameGlobals.latencyTolerance +EXIT_TIMEOUT = 20.0 + MinigameGlobals.latencyTolerance + +class DistributedMinigameAI(DistributedObjectAI.DistributedObjectAI): + notify = directNotify.newCategory('DistributedMinigameAI') + + def __init__(self, air, minigameId): + try: + self.DistributedMinigameAI_initialized + except: + self.DistributedMinigameAI_initialized = 1 + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.minigameId = minigameId + self.frameworkFSM = ClassicFSM.ClassicFSM('DistributedMinigameAI', [State.State('frameworkOff', self.enterFrameworkOff, self.exitFrameworkOff, ['frameworkWaitClientsJoin']), + State.State('frameworkWaitClientsJoin', self.enterFrameworkWaitClientsJoin, self.exitFrameworkWaitClientsJoin, ['frameworkWaitClientsReady', 'frameworkWaitClientsExit', 'frameworkCleanup']), + State.State('frameworkWaitClientsReady', self.enterFrameworkWaitClientsReady, self.exitFrameworkWaitClientsReady, ['frameworkGame', 'frameworkWaitClientsExit', 'frameworkCleanup']), + State.State('frameworkGame', self.enterFrameworkGame, self.exitFrameworkGame, ['frameworkWaitClientsExit', 'frameworkCleanup']), + State.State('frameworkWaitClientsExit', self.enterFrameworkWaitClientsExit, self.exitFrameworkWaitClientsExit, ['frameworkCleanup']), + State.State('frameworkCleanup', self.enterFrameworkCleanup, self.exitFrameworkCleanup, ['frameworkOff'])], 'frameworkOff', 'frameworkOff') + self.frameworkFSM.enterInitialState() + self.avIdList = [] + self.stateDict = {} + self.scoreDict = {} + self.difficultyOverride = None + self.trolleyZoneOverride = None + self.skippable = True + self.skipAvIds = [] + + def addChildGameFSM(self, gameFSM): + self.frameworkFSM.getStateNamed('frameworkGame').addChild(gameFSM) + + def removeChildGameFSM(self, gameFSM): + self.frameworkFSM.getStateNamed('frameworkGame').removeChild(gameFSM) + + def setExpectedAvatars(self, avIds): + self.avIdList = avIds + self.numPlayers = len(self.avIdList) + self.notify.debug('BASE: setExpectedAvatars: expecting avatars: ' + str(self.avIdList)) + + def setNewbieIds(self, newbieIds): + self.newbieIdList = newbieIds + if len(self.newbieIdList) > 0: + self.notify.debug('BASE: setNewbieIds: %s' % self.newbieIdList) + + def setTrolleyZone(self, trolleyZone): + self.trolleyZone = trolleyZone + + def setDifficultyOverrides(self, difficultyOverride, trolleyZoneOverride): + self.difficultyOverride = difficultyOverride + if self.difficultyOverride is not None: + self.difficultyOverride = MinigameGlobals.QuantizeDifficultyOverride(difficultyOverride) + self.trolleyZoneOverride = trolleyZoneOverride + return + + def _playing(self): + if not hasattr(self, 'gameFSM'): + return False + if self.gameFSM.getCurrentState() == None: + return False + return self.gameFSM.getCurrentState().getName() == 'play' + + def _inState(self, states): + if not hasattr(self, 'gameFSM'): + return False + if self.gameFSM.getCurrentState() == None: + return False + return self.gameFSM.getCurrentState().getName() in makeList(states) + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.frameworkFSM.request('frameworkWaitClientsJoin') + + def delete(self): + self.notify.debug('BASE: delete: deleting AI minigame object') + del self.frameworkFSM + self.ignoreAll() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def isSinglePlayer(self): + if self.numPlayers == 1: + return 1 + else: + return 0 + + def getParticipants(self): + return self.avIdList + + def getTrolleyZone(self): + return self.trolleyZone + + def getDifficultyOverrides(self): + response = [self.difficultyOverride, self.trolleyZoneOverride] + if response[0] is None: + response[0] = MinigameGlobals.NoDifficultyOverride + else: + response[0] *= MinigameGlobals.DifficultyOverrideMult + response[0] = int(response[0]) + if response[1] is None: + response[1] = MinigameGlobals.NoTrolleyZoneOverride + return response + + def b_setGameReady(self): + self.setGameReady() + self.d_setGameReady() + + def d_setGameReady(self): + self.notify.debug('BASE: Sending setGameReady') + self.sendUpdate('setGameReady', []) + + def setGameReady(self): + self.notify.debug('BASE: setGameReady: game ready with avatars: %s' % self.avIdList) + self.normalExit = 1 + + def b_setGameStart(self, timestamp): + self.d_setGameStart(timestamp) + self.setGameStart(timestamp) + + def d_setGameStart(self, timestamp): + self.notify.debug('BASE: Sending setGameStart') + self.sendUpdate('setGameStart', [timestamp]) + + def setGameStart(self, timestamp): + self.notify.debug('BASE: setGameStart') + + def b_setGameExit(self): + self.d_setGameExit() + self.setGameExit() + + def d_setGameExit(self): + self.notify.debug('BASE: Sending setGameExit') + self.sendUpdate('setGameExit', []) + + def setGameExit(self): + self.notify.debug('BASE: setGameExit') + + def setGameAbort(self): + self.notify.debug('BASE: setGameAbort') + self.normalExit = 0 + self.sendUpdate('setGameAbort', []) + self.frameworkFSM.request('frameworkCleanup') + + def handleExitedAvatar(self, avId): + self.notify.warning('BASE: handleExitedAvatar: avatar id exited: ' + str(avId)) + self.stateDict[avId] = EXITED + self.setGameAbort() + + def gameOver(self): + self.frameworkFSM.request('frameworkWaitClientsExit') + + def enterFrameworkOff(self): + self.notify.debug('BASE: enterFrameworkOff') + + def exitFrameworkOff(self): + pass + + def enterFrameworkWaitClientsJoin(self): + self.notify.debug('BASE: enterFrameworkWaitClientsJoin') + for avId in self.avIdList: + self.stateDict[avId] = EXPECTED + self.scoreDict[avId] = DEFAULT_POINTS + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleExitedAvatar, extraArgs=[avId]) + + def allAvatarsJoined(self = self): + self.notify.debug('BASE: all avatars joined') + self.b_setGameReady() + self.frameworkFSM.request('frameworkWaitClientsReady') + + def handleTimeout(avIds, self = self): + self.notify.debug('BASE: timed out waiting for clients %s to join' % avIds) + self.setGameAbort() + + self.__barrier = ToonBarrier('waitClientsJoin', self.uniqueName('waitClientsJoin'), self.avIdList, JOIN_TIMEOUT, allAvatarsJoined, handleTimeout) + + def setAvatarJoined(self): + if self.frameworkFSM.getCurrentState().getName() != 'frameworkWaitClientsJoin': + self.notify.debug('BASE: Ignoring setAvatarJoined message') + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('BASE: setAvatarJoined: avatar id joined: ' + str(avId)) + self.air.writeServerEvent('minigame_joined', avId, '%s|%s' % (self.minigameId, self.trolleyZone)) + self.stateDict[avId] = JOINED + self.notify.debug('BASE: setAvatarJoined: new states: ' + str(self.stateDict)) + self.__barrier.clear(avId) + + def exitFrameworkWaitClientsJoin(self): + self.__barrier.cleanup() + del self.__barrier + + def enterFrameworkWaitClientsReady(self): + self.notify.debug('BASE: enterFrameworkWaitClientsReady') + + def allAvatarsReady(self = self): + self.notify.debug('BASE: all avatars ready') + self.frameworkFSM.request('frameworkGame') + + def handleTimeout(avIds, self = self): + self.notify.debug("BASE: timed out waiting for clients %s to report 'ready'" % avIds) + self.setGameAbort() + + self.__barrier = ToonBarrier('waitClientsReady', self.uniqueName('waitClientsReady'), self.avIdList, READY_TIMEOUT, allAvatarsReady, handleTimeout) + for avId in self.stateDict.keys(): + if self.stateDict[avId] == READY: + self.__barrier.clear(avId) + + self.notify.debug(' safezone: %s' % self.getSafezoneId()) + self.notify.debug('difficulty: %s' % self.getDifficulty()) + + def setAvatarReady(self): + if self.frameworkFSM.getCurrentState().getName() not in ['frameworkWaitClientsReady', 'frameworkWaitClientsJoin']: + self.notify.debug('BASE: Ignoring setAvatarReady message') + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('BASE: setAvatarReady: avatar id ready: ' + str(avId)) + self.stateDict[avId] = READY + self.notify.debug('BASE: setAvatarReady: new avId states: ' + str(self.stateDict)) + if self.frameworkFSM.getCurrentState().getName() == 'frameworkWaitClientsReady': + self.__barrier.clear(avId) + self.skippable = False + + def exitFrameworkWaitClientsReady(self): + self.__barrier.cleanup() + del self.__barrier + + def enterFrameworkGame(self): + self.notify.debug('BASE: enterFrameworkGame') + self.gameStartTime = globalClock.getRealTime() + self.b_setGameStart(globalClockDelta.localToNetworkTime(self.gameStartTime)) + + def exitFrameworkGame(self): + pass + + def enterFrameworkWaitClientsExit(self): + self.notify.debug('BASE: enterFrameworkWaitClientsExit') + self.b_setGameExit() + + def allAvatarsExited(self = self): + self.notify.debug('BASE: all avatars exited') + self.frameworkFSM.request('frameworkCleanup') + + def handleTimeout(avIds, self = self): + self.notify.debug('BASE: timed out waiting for clients %s to exit' % avIds) + self.frameworkFSM.request('frameworkCleanup') + + self.__barrier = ToonBarrier('waitClientsExit', self.uniqueName('waitClientsExit'), self.avIdList, EXIT_TIMEOUT, allAvatarsExited, handleTimeout) + for avId in self.stateDict.keys(): + if self.stateDict[avId] == EXITED: + self.__barrier.clear(avId) + + def setAvatarExited(self): + if self.frameworkFSM.getCurrentState().getName() != 'frameworkWaitClientsExit': + self.notify.debug('BASE: Ignoring setAvatarExit message') + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('BASE: setAvatarExited: avatar id exited: ' + str(avId)) + self.stateDict[avId] = EXITED + self.notify.debug('BASE: setAvatarExited: new avId states: ' + str(self.stateDict)) + self.__barrier.clear(avId) + self.checkForSkip() + + def exitFrameworkWaitClientsExit(self): + self.__barrier.cleanup() + del self.__barrier + + def hasScoreMult(self): + return 1 + + def enterFrameworkCleanup(self): + self.notify.debug('BASE: enterFrameworkCleanup: normalExit=%s' % self.normalExit) + scoreMult = MinigameGlobals.getScoreMult(self.getSafezoneId()) + if not self.hasScoreMult(): + scoreMult = 1.0 + self.notify.debug('score multiplier: %s' % scoreMult) + for avId in self.avIdList: + self.scoreDict[avId] *= scoreMult + + scoreList = [] + for avId in self.avIdList: + if self.normalExit: + score = int(self.scoreDict[avId] + 0.5) + else: + score = 0 + if simbase.air.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY) or simbase.air.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH): + score *= MinigameGlobals.JellybeanTrolleyHolidayScoreMultiplier + logEvent = False + if score > 255: + score = 255 + logEvent = True + elif score < 0: + score = 0 + logEvent = True + if logEvent: + self.air.writeServerEvent('suspicious', avId, 'got %s jellybeans playing minigame %s in zone %s' % (score, self.minigameId, self.getSafezoneId())) + scoreList.append(score) + + self.requestDelete() + self.handleRegularPurchaseManager(scoreList) + self.frameworkFSM.request('frameworkOff') + + def handleRegularPurchaseManager(self, scoreList): + for id in self.newbieIdList: + pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(self.air, id, self.avIdList, scoreList, self.minigameId, self.trolleyZone) + MinigameCreatorAI.acquireMinigameZone(self.zoneId) + pm.generateWithRequired(self.zoneId) + + if len(self.avIdList) > len(self.newbieIdList): + pm = PurchaseManagerAI.PurchaseManagerAI(self.air, self.avIdList, scoreList, self.minigameId, self.trolleyZone, self.newbieIdList) + pm.generateWithRequired(self.zoneId) + + def exitFrameworkCleanup(self): + pass + + def requestExit(self): + self.notify.debug('BASE: requestExit: client has requested the game to end') + self.setGameAbort() + + def local2GameTime(self, timestamp): + return timestamp - self.gameStartTime + + def game2LocalTime(self, timestamp): + return timestamp + self.gameStartTime + + def getCurrentGameTime(self): + return self.local2GameTime(globalClock.getFrameTime()) + + def getDifficulty(self): + if self.difficultyOverride is not None: + return self.difficultyOverride + if hasattr(self.air, 'minigameDifficulty'): + return float(self.air.minigameDifficulty) + return MinigameGlobals.getDifficulty(self.getSafezoneId()) + + def getSafezoneId(self): + if self.trolleyZoneOverride is not None: + return self.trolleyZoneOverride + if hasattr(self.air, 'minigameSafezoneId'): + return MinigameGlobals.getSafezoneId(self.air.minigameSafezoneId) + return MinigameGlobals.getSafezoneId(self.trolleyZone) + + def logPerfectGame(self, avId): + self.air.writeServerEvent('perfectMinigame', avId, '%s|%s|%s' % (self.minigameId, self.trolleyZone, self.avIdList)) + + def logAllPerfect(self): + for avId in self.avIdList: + self.logPerfectGame(avId) + + def requestSkip(self): + avId = self.air.getAvatarIdFromSender() + + if (not self.skippable) or (avId not in self.avIdList) or (avId in self.skipAvIds): + return + + self.skipAvIds.append(avId) + self.checkForSkip() + + def checkForSkip(self): + if len(self.skipAvIds) >= len(self.avIdList): + self.skippable = False + self.setGameAbort() + else: + self.sendUpdate('setSkipCount', [len(self.skipAvIds)]) diff --git a/toontown/minigame/DistributedMinigamePhysicsWorld.py b/toontown/minigame/DistributedMinigamePhysicsWorld.py new file mode 100755 index 00000000..fe1f4fc5 --- /dev/null +++ b/toontown/minigame/DistributedMinigamePhysicsWorld.py @@ -0,0 +1,14 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame import MinigamePhysicsWorldBase + +class DistributedMinigamePhysicsWorld(DistributedObject.DistributedObject, MinigamePhysicsWorldBase.MinigamePhysicsWorldBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMinigamePhysicsWorld') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + MinigamePhysicsWorldBase.MinigamePhysicsWorldBase.__init__(self, canRender=1) + + def delete(self): + MinigamePhysicsWorldBase.MinigamePhysicsWorldBase.delete(self) + DistributedObject.DistributedObject.delete(self) diff --git a/toontown/minigame/DistributedMinigameTemplate.py b/toontown/minigame/DistributedMinigameTemplate.py new file mode 100755 index 00000000..bd8d75ee --- /dev/null +++ b/toontown/minigame/DistributedMinigameTemplate.py @@ -0,0 +1,78 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from DistributedMinigame import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import TTLocalizer + +class DistributedMinigameTemplate(DistributedMinigame): + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedMinigameTemplate', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + + def getTitle(self): + return TTLocalizer.MinigameTemplateTitle + + def getInstructions(self): + return TTLocalizer.MinigameTemplateInstructions + + def getMaxDuration(self): + return 0 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + DistributedMinigame.handleDisabledAvatar(self, avId) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.gameOver() + + def exitPlay(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedMinigameTemplateAI.py b/toontown/minigame/DistributedMinigameTemplateAI.py new file mode 100755 index 00000000..9d38daaa --- /dev/null +++ b/toontown/minigame/DistributedMinigameTemplateAI.py @@ -0,0 +1,63 @@ +from DistributedMinigameAI import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State + +class DistributedMinigameTemplateAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedMinigameTemplateAI_initialized + except: + self.DistributedMinigameTemplateAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedMinigameTemplateAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.gameOver() + + def exitPlay(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedPatternGame.py b/toontown/minigame/DistributedPatternGame.py new file mode 100755 index 00000000..30ce5942 --- /dev/null +++ b/toontown/minigame/DistributedPatternGame.py @@ -0,0 +1,798 @@ +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from panda3d.core import * +import random +import string + +import ArrowKeys +from DistributedMinigame import * +import PatternGameGlobals +from otp.nametag.NametagConstants import * +from toontown.toon import NPCToons +from toontown.toon import ToonHead +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownTimer +from toontown.toonbase.ToonBaseGlobal import * + + +class DistributedPatternGame(DistributedMinigame): + phase4snd = 'phase_4/audio/sfx/' + ButtonSoundNames = (phase4snd + 'm_match_trumpet.ogg', + phase4snd + 'm_match_guitar.ogg', + phase4snd + 'm_match_drums.ogg', + phase4snd + 'm_match_piano.ogg') + bgm = 'phase_4/audio/bgm/m_match_bg1.ogg' + strWatch = TTLocalizer.PatternGameWatch + strGo = TTLocalizer.PatternGameGo + strRight = TTLocalizer.PatternGameRight + strWrong = TTLocalizer.PatternGameWrong + strPerfect = TTLocalizer.PatternGamePerfect + strBye = TTLocalizer.PatternGameBye + strWaitingOtherPlayers = TTLocalizer.WaitingForOtherToons + strPleaseWait = TTLocalizer.PatternGamePleaseWait + strRound = TTLocalizer.PatternGameRound + toonAnimNames = ['up', + 'left', + 'down', + 'right', + 'slip-forward', + 'slip-backward', + 'victory'] + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedPatternGame', [State.State('off', self.enterOff, self.exitOff, ['waitForServerPattern']), + State.State('waitForServerPattern', self.enterWaitForServerPattern, self.exitWaitForServerPattern, ['showServerPattern', 'cleanup']), + State.State('showServerPattern', self.enterShowServerPattern, self.exitShowServerPattern, ['getUserInput', 'playBackPatterns', 'cleanup']), + State.State('getUserInput', self.enterGetUserInput, self.exitGetUserInput, ['waitForPlayerPatterns', 'playBackPatterns', 'cleanup']), + State.State('waitForPlayerPatterns', self.enterWaitForPlayerPatterns, self.exitWaitForPlayerPatterns, ['playBackPatterns', 'cleanup', 'checkGameOver']), + State.State('playBackPatterns', self.enterPlayBackPatterns, self.exitPlayBackPatterns, ['checkGameOver', 'cleanup']), + State.State('checkGameOver', self.enterCheckGameOver, self.exitCheckGameOver, ['waitForServerPattern', 'cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.arrowColor = VBase4(1, 0, 0, 1) + self.xColor = VBase4(1, 0, 0, 1) + self.celebrate = 0 + self.oldBgColor = None + self.trans = VBase4(1, 0, 0, 0) + self.opaq = VBase4(1, 0, 0, 1) + self.normalTextColor = VBase4(0.537, 0.84, 0.33, 1.0) + self.__otherToonIndex = {} + self.totalColorBalls = 1 #Start at 1, index starts at 0. + return + + def getTitle(self): + return TTLocalizer.PatternGameTitle + + def getInstructions(self): + return TTLocalizer.PatternGameInstructions + + def getMaxDuration(self): + inputDur = PatternGameGlobals.NUM_ROUNDS * PatternGameGlobals.InputTime + return inputDur * 1.3 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.hide() + self.room = loader.loadModel('phase_4/models/minigames/matching_room') + self.buttonSounds = [] + for soundName in self.ButtonSoundNames: + self.buttonSounds.append(base.loadSfx(soundName)) + + self.correctSound = base.loadSfx('phase_4/audio/sfx/MG_pos_buzzer.ogg') + self.incorrectSound = base.loadSfx('phase_4/audio/sfx/MG_neg_buzzer.ogg') + self.perfectSound = base.loadSfx('phase_4/audio/sfx/MG_win.ogg') + self.fallSound = base.loadSfx('phase_4/audio/sfx/MG_Tag_A.ogg') + self.music = base.loadMusic(self.bgm) + self.waitingText = DirectLabel(text=self.strPleaseWait, text_fg=(0.9, 0.9, 0.9, 1.0), frameColor=(1, 1, 1, 0), text_font=ToontownGlobals.getSignFont(), pos=(0, 0, -.78), scale=0.12) + self.roundText = DirectLabel(text=self.strRound % 1, text_fg=self.normalTextColor, frameColor=(1, 1, 1, 0), text_font=ToontownGlobals.getSignFont(), pos=(0.014, 0, -.84), scale=0.12) + self.roundText.hide() + self.waitingText.hide() + matchingGameGui = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + minnieArrow = matchingGameGui.find('**/minnieArrow') + minnieX = matchingGameGui.find('**/minnieX') + minnieCircle = matchingGameGui.find('**/minnieCircle') + self.arrows = [None] * 5 + for x in xrange(0, 5): + self.arrows[x] = minnieArrow.copyTo(hidden) + self.arrows[x].hide() + + self.xs = [None] * 5 + for x in xrange(0, 5): + self.xs[x] = minnieX.copyTo(hidden) + self.xs[x].hide() + + self.statusBalls = [] + self.totalMoves = PatternGameGlobals.INITIAL_ROUND_LENGTH + PatternGameGlobals.ROUND_LENGTH_INCREMENT * (PatternGameGlobals.NUM_ROUNDS - 1) + for x in xrange(0, 4): + self.statusBalls.append([None] * self.totalMoves) + + for x in xrange(0, 4): + for y in xrange(0, self.totalMoves): + self.statusBalls[x][y] = minnieCircle.copyTo(hidden) + self.statusBalls[x][y].hide() + + minnieArrow.removeNode() + minnieX.removeNode() + minnieCircle.removeNode() + matchingGameGui.removeNode() + self.toon = NPCToons.createLocalNPC(7010) + self.toon.reparentTo(hidden) + self.backRowHome = Point3(3, 11, 0) + self.backRowXSpacing = 1.8 + self.frontRowHome = Point3(0, 18, 0) + self.frontRowXSpacing = 3.0 + self.stdNumDanceStepPingFrames = self.toon.getNumFrames(self.toonAnimNames[0]) + self.stdNumDanceStepPingPongFrames = self.__numPingPongFrames(self.stdNumDanceStepPingFrames) + self.buttonPressDelayPercent = (self.stdNumDanceStepPingFrames - 1.0) / self.stdNumDanceStepPingPongFrames + self.animPlayRates = [] + animPlayRate = 1.4 + animPlayRateMult = 1.06 + for i in xrange(PatternGameGlobals.NUM_ROUNDS): + self.animPlayRates.append(animPlayRate) + animPlayRate *= animPlayRateMult + + return + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.timer.destroy() + del self.timer + del self.lt + del self.buttonSounds + del self.music + del self.__otherToonIndex + del self.correctSound + del self.incorrectSound + del self.perfectSound + del self.fallSound + self.waitingText.destroy() + del self.waitingText + self.roundText.destroy() + del self.roundText + for x in self.arrowDict.values(): + x[0].removeNode() + x[1].removeNode() + if len(x) == 3: + for y in x[2]: + y.removeNode() + + del self.arrowDict + for x in self.arrows: + if x: + x.removeNode() + + del self.arrows + for x in self.xs: + if x: + x.removeNode() + + del self.xs + for x in self.statusBalls: + if x: + for y in x: + if y: + y.removeNode() + del y + + del self.statusBalls + self.room.removeNode() + del self.room + self.toon.delete() + del self.toon + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.arrowDict = {} + self.lt = base.localAvatar + camera.reparentTo(render) + camera.setPosHpr(0.0, -14.59, 10.56, 0.0, -16.39, 0.0) + base.camLens.setMinFov(24.66/(4./3.)) + base.setBackgroundColor(Vec4(0.984, 0.984, 0.584, 1)) + NametagGlobals.setGlobalNametagScale(0.6) + self.arrowKeys = ArrowKeys.ArrowKeys() + self.room.reparentTo(render) + self.room.setPosHpr(0.0, 18.39, -ToontownGlobals.FloorOffset, 0.0, 0.0, 0.0) + self.room.setScale(1) + + for anim in self.toonAnimNames: + self.lt.pose(anim, 0) + + self.toonAnimSpeedMult = {} + + for anim in self.toonAnimNames: + numFrames = self.lt.getNumFrames(anim) + self.toonAnimSpeedMult[anim] = float(self.__numPingPongFrames(numFrames)) / float(self.stdNumDanceStepPingPongFrames) + + lt = self.lt + lt.reparentTo(render) + lt.useLOD(1000) + lt.setPos(-3.5, 11, 0.0) + lt.setScale(1) + self.makeToonLookatCamera(lt) + lt.loop('neutral') + lt.startBlink() + lt.startLookAround() + self.arrowDict['lt'] = [self.arrows.pop(), self.xs.pop(), self.statusBalls.pop()] + jj = self.lt.nametag3d + for k in xrange(0, 2): + self.arrowDict['lt'][k].setBillboardAxis() + self.arrowDict['lt'][k].setBin('fixed', 100) + self.arrowDict['lt'][k].reparentTo(self.lt.nametag3d) + if k == 0: + self.arrowDict['lt'][k].setScale(2.5) + self.arrowDict['lt'][k].setColor(self.arrowColor) + else: + self.arrowDict['lt'][k].setScale(4, 4, 4) + self.arrowDict['lt'][k].setColor(self.xColor) + self.arrowDict['lt'][k].setPos(0, 0, 1) + + self.formatStatusBalls(self.arrowDict['lt'][2], self.lt.nametag3d) + self.toon.reparentTo(render) + self.toon.setPos(-1.6, 20, 0) + self.toon.setScale(1) + self.makeToonLookatCamera(self.toon) + self.toon.loop('neutral') + self.toon.nametag.manage(base.marginManager) + self.toon.nametag.getNametag3d().setChatWordwrap(8) + self.arrowDict['m'] = [self.arrows.pop(), self.xs.pop()] + for k in xrange(0, 2): + self.arrowDict['m'][k].setBillboardAxis() + self.arrowDict['m'][k].setBin('fixed', 100) + self.arrowDict['m'][k].setColor(self.arrowColor) + self.arrowDict['m'][k].reparentTo(self.toon.nametag3d) + self.arrowDict['m'][k].setScale(4) + self.arrowDict['m'][k].setPos(0, 0, 1.7) + + base.playMusic(self.music, looping=1, volume=1) + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + self.music.stop() + base.camLens.setMinFov(settings['fov']/(4./3.)) + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + NametagGlobals.setGlobalNametagScale(1.0) + self.arrowKeys.destroy() + del self.arrowKeys + self.room.reparentTo(hidden) + self.roundText.hide() + self.toon.nametag.unmanage(base.marginManager) + self.toon.stop() + self.toon.reparentTo(hidden) + self.lt.setScale(1) + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.setScale(1) + + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.resetLOD() + for anim in self.toonAnimNames: + av.setPlayRate(1.0, anim) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + self.arrowDict[avId] = [self.arrows.pop(), self.xs.pop(), self.statusBalls.pop()] + jj = toon.nametag3d + for k in xrange(0, 2): + self.arrowDict[avId][k].setBillboardAxis() + self.arrowDict[avId][k].setBin('fixed', 100) + self.arrowDict[avId][k].reparentTo(jj) + if k == 0: + self.arrowDict[avId][k].setScale(2.5) + self.arrowDict[avId][k].setColor(self.arrowColor) + else: + self.arrowDict[avId][k].setScale(4, 4, 4) + self.arrowDict[avId][k].setColor(self.xColor) + self.arrowDict[avId][k].setPos(0, 0, 1) + + self.formatStatusBalls(self.arrowDict[avId][2], jj) + toon.reparentTo(render) + toon.useLOD(1000) + toon.setPos(self.getBackRowPos(avId)) + toon.setScale(0.9) + self.makeToonLookatCamera(toon) + for anim in self.toonAnimNames: + toon.pose(anim, 0) + + toon.loop('neutral') + + if self.isSinglePlayer(): + self.waitingText['text'] = self.strPleaseWait + else: + self.waitingText['text'] = self.strWaitingOtherPlayers + self.animTracks = {} + for avId in self.avIdList: + self.animTracks[avId] = None + + self.__initGameVars() + return + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('waitForServerPattern') + + def __initGameVars(self): + self.round = 0 + self.perfectGame = 1 + + def __numPingPongFrames(self, numFrames): + return numFrames * 2 - 1 + + def makeToonLookatCamera(self, toon): + toon.headsUp(camera) + + def setText(self, t, newtext): + t['text'] = newtext + + def setTextFG(self, t, fg): + t['text_fg'] = fg + + def getWalkTrack(self, toon, posList, startPos = None, lookAtCam = 1, endHeading = 180): + walkSpeed = 7 + origPos = toon.getPos() + origHpr = toon.getHpr() + track = Sequence(Func(toon.loop, 'run')) + if startPos: + toon.setPos(startPos) + track.append(Func(toon.setPos, startPos)) + for endPos in posList: + toon.headsUp(Point3(endPos)) + track.append(Func(toon.setHpr, Point3(toon.getH(), 0, 0))) + lastPos = toon.getPos() + distance = Vec3(endPos - lastPos).length() + duration = distance / walkSpeed + toon.setPos(endPos) + track.append(LerpPosInterval(toon, duration=duration, pos=Point3(endPos), startPos=Point3(lastPos))) + + if lookAtCam: + saveHpr = toon.getHpr() + toon.headsUp(camera) + endHeading = toon.getHpr()[0] + toon.setHpr(saveHpr) + curHeading = toon.getH() + if endHeading - curHeading > 180.0: + endHeading -= 360 + elif endHeading - curHeading < -180.0: + endHeading += 360 + endHpr = Point3(endHeading, 0, 0) + duration = abs(endHeading - curHeading) / 180.0 * 0.3 + track.extend([Func(toon.loop, 'walk'), LerpHprInterval(toon, duration, endHpr), Func(toon.loop, 'neutral')]) + toon.setPos(origPos) + toon.setHpr(origHpr) + return track + + def getDanceStepDuration(self): + numFrames = self.stdNumDanceStepPingPongFrames + return numFrames / abs(self.animPlayRate * self.toonAnimSpeedMult[self.toonAnimNames[0]] * self.toon.getFrameRate(self.toonAnimNames[0])) + + def __getDanceStepAnimTrack(self, toon, anim, speedScale): + numFrames = toon.getNumFrames(anim) + return Sequence(Func(toon.pingpong, anim, fromFrame=0, toFrame=numFrames - 1), Wait(self.getDanceStepDuration())) + + def __getToonDanceStepAnimTrack(self, toon, direction): + animName = self.toonAnimNames[direction] + return self.__getDanceStepAnimTrack(toon, animName, self.toonAnimSpeedMult[animName]) + + def getDanceStepButtonSoundTrack(self, index): + duration = self.getDanceStepDuration() + wait = duration * self.buttonPressDelayPercent + return Sequence(Wait(wait), Func(base.playSfx, self.__getButtonSound(index)), Wait(duration - wait)) + + def getDanceArrowAnimTrack(self, toonID, pattern, speedy): + track = Sequence() + track.append(Func(self.showArrow, toonID)) + for buttonIndex in pattern: + track.append(self.getDanceArrowSingleTrack(toonID, buttonIndex, speedy)) + + track.append(Func(self.hideArrow, toonID)) + return track + + def changeArrow(self, toonID, index): + self.arrowDict[toonID][0].setR(-(90 - 90 * index)) + + def showArrow(self, toonID): + self.arrowDict[toonID][0].show() + + def hideArrow(self, toonID): + self.arrowDict[toonID][0].hide() + + def showX(self, toonID): + self.arrowDict[toonID][1].show() + + def hideX(self, toonID): + self.arrowDict[toonID][1].hide() + + def celebrated(self): + self.celebrate = 1 + + def returnCelebrationIntervals(self, turnOn): + ri = [] + if turnOn: + ri.append(ActorInterval(actor=self.lt, animName='victory', duration=5.5)) + else: + ri.append(Func(self.lt.loop, 'neutral')) + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + if turnOn: + ri.append(ActorInterval(actor=toon, animName='victory', duration=5.5)) + else: + ri.append(Func(toon.loop, 'neutral')) + + if len(self.remoteAvIdList) == 0: + return ri + else: + return Parallel(ri) + + def formatStatusBalls(self, sb, jj): + for x in xrange(0, self.totalMoves): + sb[x].setBillboardAxis() + sb[x].setBin('fixed', 100) + sb[x].reparentTo(jj) + sb[x].setScale(1) + xpos = +(int(self.totalMoves / 2) * 0.25) - 0.25 * x + sb[x].setPos(xpos, 0, 0.3) + + def showStatusBalls(self, toonID): + sb = self.arrowDict[toonID][2] + for x in xrange(0, len(self.__serverPattern)): + sb[x].setColor(1, 1, 1, 1) + sb[x].show() + + def hideStatusBalls(self, toonID): + sb = self.arrowDict[toonID][2] + for x in xrange(0, len(sb)): + sb[x].hide() + + def colorStatusBall(self, toonID, which, good): + if not which>self.totalColorBalls: + if good: + self.arrowDict[toonID][2][which].setColor(0, 1, 0, 1) + else: + self.arrowDict[toonID][2][which].setColor(1, 0, 0, 1) + else: + self.notify.warning('toonID %s sent more colorStatusBall updates than he should have.' % toonID) + + def getDanceArrowSingleTrack(self, toonID, index, speedy): + duration = self.getDanceStepDuration() + wait = duration * self.buttonPressDelayPercent + d = duration - wait + if speedy: + track = Sequence(Func(self.changeArrow, toonID, index), Wait(wait)) + else: + track = Sequence(Func(self.changeArrow, toonID, index), Wait(wait), LerpColorInterval(self.arrowDict[toonID][0], d, self.trans, self.opaq)) + return track + + def getDanceSequenceAnimTrack(self, toon, pattern): + getDanceStepTrack = self.__getToonDanceStepAnimTrack + tracks = Sequence() + for direction in pattern: + tracks.append(getDanceStepTrack(toon, direction)) + + if len(pattern): + tracks.append(Func(toon.loop, 'neutral')) + return tracks + + def getDanceSequenceButtonSoundTrack(self, pattern): + track = Sequence() + for buttonIndex in pattern: + track.append(self.getDanceStepButtonSoundTrack(buttonIndex)) + + return track + + def __getRowPos(self, rowHome, xSpacing, index, numSpots): + xOffset = xSpacing * index - xSpacing * (numSpots - 1) / 2.0 + return rowHome + Point3(xOffset, 0, 0) + + def getBackRowPos(self, avId): + index = self.remoteAvIdList.index(avId) + return self.__getRowPos(self.backRowHome, self.backRowXSpacing, index, len(self.remoteAvIdList)) + + def getFrontRowPos(self, avId): + index = self.avIdList.index(avId) + return self.__getRowPos(self.frontRowHome, self.frontRowXSpacing, index, len(self.avIdList)) + + def __setToonChat(self, str, giggle): + str = str.replace('%s', self.getAvatar(self.localAvId).getName()) + self.toon.setChatAbsolute(str, CFSpeech) + + def __clearToonChat(self): + self.toon.clearChat() + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterWaitForServerPattern(self): + self.notify.debug('enterWaitForServerPattern') + self.sendUpdate('reportPlayerReady', []) + + def setPattern(self, pattern): + if not self.hasLocalToon: + return + self.notify.debug('setPattern: ' + str(pattern)) + self.__serverPattern = pattern + self.gameFSM.request('showServerPattern') + + def exitWaitForServerPattern(self): + pass + + def enterShowServerPattern(self): + self.notify.debug('enterShowServerPattern') + self.round += 1 + self.roundText.show() + self.roundText.setScale(0.12) + self.roundText['text'] = self.strRound % self.round + self.animPlayRate = self.animPlayRates[self.round - 1] + for avId in self.avIdList: + toon = self.getAvatar(avId) + if toon: + for anim in self.toonAnimNames: + toon.setPlayRate(self.animPlayRate * self.toonAnimSpeedMult[anim], anim) + + for anim in self.toonAnimNames: + self.toon.setPlayRate(self.animPlayRate * self.toonAnimSpeedMult[anim], anim) + + text = self.strWatch + danceTrack = self.getDanceSequenceAnimTrack(self.toon, self.__serverPattern) + arrowTrack = self.getDanceArrowAnimTrack('m', self.__serverPattern, 0) + soundTrack = self.getDanceSequenceButtonSoundTrack(self.__serverPattern) + self.showTrack = Sequence(Func(self.__setToonChat, text, 1), Wait(0.5), Parallel(danceTrack, soundTrack, arrowTrack), Wait(0.2), Func(self.__clearToonChat), Func(self.gameFSM.request, 'getUserInput')) + self.showTrack.start() + + def exitShowServerPattern(self): + if self.showTrack.isPlaying(): + self.showTrack.pause() + del self.showTrack + + def enterGetUserInput(self): + self.notify.debug('enterGetUserInput') + self.setupTrack = None + self.proceedTrack = None + + def startTimer(self = self): + self.currentStartTime = globalClock.getFrameTime() + self.timer.show() + self.timer.countdown(PatternGameGlobals.InputTime, self.__handleInputTimeout) + + def enableKeys(self = self): + + def keyPress(self, index): + self.__pressHandler(index) + + def keyRelease(self, index): + self.__releaseHandler(index) + + self.arrowKeys.setPressHandlers([lambda self = self, keyPress = keyPress: keyPress(self, 0), + lambda self = self, keyPress = keyPress: keyPress(self, 2), + lambda self = self, keyPress = keyPress: keyPress(self, 3), + lambda self = self, keyPress = keyPress: keyPress(self, 1)]) + self.arrowKeys.setReleaseHandlers([lambda self = self, keyRelease = keyRelease: keyRelease(self, 0), + lambda self = self, keyRelease = keyRelease: keyRelease(self, 2), + lambda self = self, keyRelease = keyRelease: keyRelease(self, 3), + lambda self = self, keyRelease = keyRelease: keyRelease(self, 1)]) + + self.__localPattern = [] + self.__otherToonIndex.clear() + self.showStatusBalls('lt') + for avId in self.remoteAvIdList: + self.showStatusBalls(avId) + self.__otherToonIndex[avId] = 0 + + self.setupTrack = Sequence(Func(self.__setToonChat, self.strGo, 0), Func(self.setText, self.roundText, TTLocalizer.PatternGameGo), Func(self.roundText.setScale, 0.3), Func(enableKeys), Func(startTimer), Wait(0.8), Func(self.__clearToonChat), Func(self.setText, self.roundText, ' '), Func(self.roundText.setScale, 0.12), Func(self.setTextFG, self.roundText, self.normalTextColor)) + self.setupTrack.start() + return + + def __handleInputTimeout(self): + self.__doneGettingInput(self.__localPattern) + + def __pressHandler(self, index): + self.__buttonPressed(index) + + def __releaseHandler(self, index): + pass + + def remoteButtonPressed(self, avId, index, wrong): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() not in ['getUserInput', 'waitForPlayerPatterns']: + return + if avId != self.localAvId: + if self.animTracks[avId]: + self.animTracks[avId].finish() + av = self.getAvatar(avId) + if wrong: + acts = ['slip-forward', 'slip-backward'] + ag = random.choice(acts) + self.arrowDict[avId][0].hide() + self.animTracks[avId] = Sequence(Func(self.showX, avId), Func(self.colorStatusBall, avId, self.__otherToonIndex[avId], 0), ActorInterval(actor=av, animName=ag, duration=2.35), Func(av.loop, 'neutral'), Func(self.hideX, avId)) + else: + self.colorStatusBall(avId, self.__otherToonIndex[avId], 1) + arrowTrack = self.getDanceArrowAnimTrack(avId, [index], 1) + potTrack = self.getDanceSequenceAnimTrack(av, [index]) + self.animTracks[avId] = Parallel(potTrack, arrowTrack) + self.__otherToonIndex[avId] += 1 + self.animTracks[avId].start() + + def __getButtonSound(self, index): + return self.buttonSounds[index] + + def __buttonPressed(self, index): + if len(self.__localPattern) >= len(self.__serverPattern): + return + if self.animTracks[self.localAvId]: + self.animTracks[self.localAvId].finish() + badd = 0 + if index != self.__serverPattern[len(self.__localPattern)]: + badd = 1 + acts = ['slip-forward', 'slip-backward'] + ag = random.choice(acts) + self.animTracks[self.localAvId] = Sequence(Func(self.showX, 'lt'), Func(self.colorStatusBall, 'lt', len(self.__localPattern), 0), ActorInterval(actor=self.lt, animName=ag, duration=2.35), Func(self.lt.loop, 'neutral'), Func(self.hideX, 'lt')) + self.arrowDict['lt'][0].hide() + base.playSfx(self.fallSound) + else: + self.colorStatusBall('lt', len(self.__localPattern), 1) + base.playSfx(self.__getButtonSound(index)) + arrowTrack = self.getDanceArrowAnimTrack('lt', [index], 1) + potTrack = self.getDanceSequenceAnimTrack(self.lt, [index]) + self.animTracks[self.localAvId] = Parallel(potTrack, arrowTrack) + self.sendUpdate('reportButtonPress', [index, badd]) + self.animTracks[self.localAvId].start() + self.__localPattern.append(index) + if len(self.__localPattern) == len(self.__serverPattern) or badd: + self.__doneGettingInput(self.__localPattern) + + def __doneGettingInput(self, pattern): + self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS) + self.currentTotalTime = globalClock.getFrameTime() - self.currentStartTime + self.proceedTrack = Sequence(Wait(self.getDanceStepDuration()), Func(self.sendUpdate, 'reportPlayerPattern', [pattern, self.currentTotalTime]), Func(self.gameFSM.request, 'waitForPlayerPatterns')) + self.proceedTrack.start() + + def exitGetUserInput(self): + self.timer.stop() + self.timer.hide() + self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS) + self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS) + if self.setupTrack and self.setupTrack.isPlaying(): + self.setupTrack.pause() + if self.proceedTrack and self.proceedTrack.isPlaying(): + self.proceedTrack.pause() + del self.setupTrack + del self.proceedTrack + self.__clearToonChat() + + def enterWaitForPlayerPatterns(self): + self.notify.debug('enterWaitForPlayerPatterns') + + def setPlayerPatterns(self, pattern1, pattern2, pattern3, pattern4, fastestAvId): + if not self.hasLocalToon: + return + self.fastestAvId = fastestAvId + self.notify.debug('setPlayerPatterns:' + ' pattern1:' + str(pattern1) + ' pattern2:' + str(pattern2) + ' pattern3:' + str(pattern3) + ' pattern4:' + str(pattern4)) + self.playerPatterns = {} + patterns = [pattern1, + pattern2, + pattern3, + pattern4] + for i in xrange(len(self.avIdList)): + self.playerPatterns[self.avIdList[i]] = patterns[i] + + self.gameFSM.request('playBackPatterns') + + def exitWaitForPlayerPatterns(self): + self.waitingText.hide() + + def enterPlayBackPatterns(self): + self.notify.debug('enterPlayBackPatterns') + self.totalColorBalls = (self.round * 2) + 1 + if self.fastestAvId == self.localAvId: + self.roundText.setScale(0.1) + if self.numPlayers != 2: + self.roundText['text'] = TTLocalizer.PatternGameFastest + else: + self.roundText['text'] = TTLocalizer.PatternGameFaster + jumpTrack = Sequence(ActorInterval(actor=self.lt, animName='jump', duration=1.7), Func(self.lt.loop, 'neutral')) + elif self.fastestAvId == 0: + if self.round == PatternGameGlobals.NUM_ROUNDS: + self.roundText['text'] = ' ' + else: + self.roundText.setScale(0.1) + self.roundText['text'] = TTLocalizer.PatternGameYouCanDoIt + jumpTrack = Sequence(Wait(0.5), Wait(0.5)) + elif self.fastestAvId == 1: + self.roundText.setScale(0.1) + self.roundText['text'] = TTLocalizer.PatternGameGreatJob + jumpTrack = Sequence(Wait(0.5), Wait(0.5)) + else: + self.roundText.setScale(0.08) + av = self.getAvatar(self.fastestAvId) + jumpTrack = Sequence(ActorInterval(actor=av, animName='jump', duration=1.7), Func(av.loop, 'neutral')) + if self.numPlayers != 2: + rewardStr = TTLocalizer.PatternGameOtherFastest + else: + rewardStr = TTLocalizer.PatternGameOtherFaster + self.roundText['text'] = av.getName() + rewardStr + success = self.playerPatterns[self.localAvId] == self.__serverPattern + self.hideStatusBalls('lt') + for avId in self.remoteAvIdList: + self.hideStatusBalls(avId) + + if success: + sound = self.correctSound + text = self.strRight + else: + self.perfectGame = 0 + sound = self.incorrectSound + text = self.strWrong + soundTrack = Sequence(Func(base.playSfx, sound), Wait(1.6)) + textTrack = Sequence(Wait(0.2), Func(self.__setToonChat, text, 0), Wait(1.3), Func(self.__clearToonChat)) + self.playBackPatternsTrack = Sequence(Parallel(soundTrack, textTrack, jumpTrack), Func(self.gameFSM.request, 'checkGameOver')) + self.playBackPatternsTrack.start() + + def exitPlayBackPatterns(self): + if self.playBackPatternsTrack.isPlaying(): + self.playBackPatternsTrack.pause() + del self.playBackPatternsTrack + + def enterCheckGameOver(self): + self.notify.debug('enterCheckGameOver') + self.__winTrack = None + if self.round < PatternGameGlobals.NUM_ROUNDS: + self.gameFSM.request('waitForServerPattern') + else: + text = self.strBye + sound = None + delay = 2.0 + if self.perfectGame: + text = self.strPerfect + sound = self.perfectSound + delay = 2.2 + if self.celebrate: + text = TTLocalizer.PatternGameImprov + self.__winTrack = Sequence(Func(self.__setToonChat, text, 1), Func(base.playSfx, self.perfectSound), Sequence(self.returnCelebrationIntervals(1)), Sequence(self.returnCelebrationIntervals(0)), Func(self.__clearToonChat), Func(self.gameOver)) + else: + self.__winTrack = Sequence(Func(self.__setToonChat, text, 1), Func(base.playSfx, sound), Wait(delay), Func(self.__clearToonChat), Func(self.gameOver)) + self.__winTrack.start() + return + + def exitCheckGameOver(self): + if self.__winTrack and self.__winTrack.isPlaying(): + self.__winTrack.pause() + del self.__winTrack + + def enterCleanup(self): + self.notify.debug('enterCleanup') + for track in self.animTracks.values(): + if track and track.isPlaying(): + track.pause() + + del self.animTracks + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedPatternGameAI.py b/toontown/minigame/DistributedPatternGameAI.py new file mode 100755 index 00000000..e0973101 --- /dev/null +++ b/toontown/minigame/DistributedPatternGameAI.py @@ -0,0 +1,195 @@ +from DistributedMinigameAI import * +from toontown.ai.ToonBarrier import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import random +import PatternGameGlobals +import copy + +class DistributedPatternGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedPatternGameAI_initialized + except: + self.DistributedPatternGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedPatternGameAI', [State.State('off', self.enterInactive, self.exitInactive, ['waitClientsReady', 'cleanup']), + State.State('waitClientsReady', self.enterWaitClientsReady, self.exitWaitClientsReady, ['generatePattern', 'cleanup']), + State.State('generatePattern', self.enterGeneratePattern, self.exitGeneratePattern, ['waitForResults', 'cleanup']), + State.State('waitForResults', self.enterWaitForResults, self.exitWaitForResults, ['waitClientsReady', 'cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + self.__initGameVars() + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('waitClientsReady') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def __initGameVars(self): + self.pattern = [] + self.round = 0 + self.perfectResults = {} + for avId in self.avIdList: + self.perfectResults[avId] = 1 + + self.readyClients = [] + self.timeoutTaskName = self.uniqueName('PatternGameResultsTimeout') + + def enterWaitClientsReady(self): + self.notify.debug('enterWaitClientsReady') + self.nextRoundBarrier = ToonBarrier('nextRoundReady', self.uniqueName('nextRoundReady'), self.avIdList, PatternGameGlobals.ClientsReadyTimeout, self.__allPlayersReady, self.__clientsReadyTimeout) + for avId in self.readyClients: + self.nextRoundBarrier.clear(avId) + + def reportPlayerReady(self): + if not self._inState('waitClientsReady'): + return + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.notify.warning('Got reportPlayerReady from an avId: %s not in our list: %s' % (avId, self.avIdList)) + else: + self.readyClients.append(avId) + self.nextRoundBarrier.clear(avId) + + def __allPlayersReady(self): + self.readyClients = [] + self.gameFSM.request('generatePattern') + + def __clientsReadyTimeout(self, avIds): + self.notify.debug('__clientsReadyTimeout: clients %s have not responded' % avIds) + self.setGameAbort() + + def exitWaitClientsReady(self): + self.nextRoundBarrier.cleanup() + del self.nextRoundBarrier + + def enterGeneratePattern(self): + self.notify.debug('enterGeneratePattern') + self.round += 1 + targetLen = PatternGameGlobals.INITIAL_ROUND_LENGTH + PatternGameGlobals.ROUND_LENGTH_INCREMENT * (self.round - 1) + count = targetLen - len(self.pattern) + for i in xrange(0, count): + self.pattern.append(random.randint(0, 3)) + + self.gameFSM.request('waitForResults') + self.sendUpdate('setPattern', [self.pattern]) + + def exitGeneratePattern(self): + pass + + def enterWaitForResults(self): + self.notify.debug('enterWaitForResults') + self.results = [None] * self.numPlayers + self.fastestTime = PatternGameGlobals.InputTime * 2 + self.fastestAvId = 0 + self.resultsBarrier = ToonBarrier('results', self.uniqueName('results'), self.avIdList, PatternGameGlobals.InputTimeout + 1.0 * self.round, self.__gotAllPatterns, self.__resultsTimeout) + return + + def reportButtonPress(self, index, wrong): + if not self._inState('waitForResults'): + return + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'PatternGameAI.reportButtonPress avId not on list') + return + if index < 0 or index > 3: + self.air.writeServerEvent('warning', index, 'PatternGameAI.reportButtonPress got bad index') + return + if wrong not in [0, 1]: + self.air.writeServerEvent('warning', wrong, "PatternGameAI.reportButtonPress got bad 'wrong'") + return + self.sendUpdate('remoteButtonPressed', [avId, index, wrong]) + + def __resultsTimeout(self, avIds): + self.notify.debug('__resultsTimeout: %s' % avIds) + for avId in avIds: + index = self.avIdList.index(avId) + self.__acceptPlayerPattern(avId, [], PatternGameGlobals.InputTime * 2) + + self.__gotAllPatterns() + + def reportPlayerPattern(self, pattern, totalTime): + if not self._inState('waitForResults'): + return + avId = self.air.getAvatarIdFromSender() + self.__acceptPlayerPattern(avId, pattern, totalTime) + self.resultsBarrier.clear(avId) + + def __acceptPlayerPattern(self, avId, pattern, totalTime): + index = self.avIdList.index(avId) + if self.results[index] != None: + return + self.results[index] = pattern + if totalTime < self.fastestTime and pattern == self.pattern: + self.fastestTime = totalTime + self.fastestAvId = avId + if self.numPlayers == 1: + self.fastestAvId = 1 + else: + self.scoreDict[self.fastestAvId] += 2 + return + + def __gotAllPatterns(self): + patterns = [[]] * 4 + for i in xrange(0, len(self.results)): + patterns[i] = self.results[i] + if patterns[i] is None: + patterns[i] = [] + + self.sendUpdate('setPlayerPatterns', patterns + [self.fastestAvId]) + for i in xrange(0, self.numPlayers): + avId = self.avIdList[i] + if not self.results[i] == self.pattern: + self.perfectResults[avId] = 0 + else: + self.scoreDict[avId] += self.round + + if self.round < PatternGameGlobals.NUM_ROUNDS: + self.gameFSM.request('waitClientsReady') + else: + for avId in self.avIdList: + if self.perfectResults[avId]: + self.scoreDict[avId] += 4 + self.logPerfectGame(avId) + + self.gameOver() + self.gameFSM.request('cleanup') + return + + def exitWaitForResults(self): + self.resultsBarrier.cleanup() + del self.resultsBarrier + + def enterCleanup(self): + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedRaceGame.py b/toontown/minigame/DistributedRaceGame.py new file mode 100755 index 00000000..b038d13e --- /dev/null +++ b/toontown/minigame/DistributedRaceGame.py @@ -0,0 +1,873 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.distributed.ClockDelta import * +from DistributedMinigame import * +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task.Task import Task +from toontown.toonbase import ToontownTimer +import RaceGameGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class DistributedRaceGame(DistributedMinigame): + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedRaceGame', [State.State('off', self.enterOff, self.exitOff, ['inputChoice']), + State.State('inputChoice', self.enterInputChoice, self.exitInputChoice, ['waitServerChoices', 'moveAvatars', 'cleanup']), + State.State('waitServerChoices', self.enterWaitServerChoices, self.exitWaitServerChoices, ['moveAvatars', 'cleanup']), + State.State('moveAvatars', self.enterMoveAvatars, self.exitMoveAvatars, ['inputChoice', 'winMovie', 'cleanup']), + State.State('winMovie', self.enterWinMovie, self.exitWinMovie, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.posHprArray = (((-9.03, + 0.06, + 0.025, + -152.9), + (-7.43, + -2.76, + 0.025, + -152.68), + (-6.02, + -5.48, + 0.025, + -157.54), + (-5.01, + -8.32, + 0.025, + -160.66), + (-4.05, + -11.36, + 0.025, + -170.22), + (-3.49, + -14.18, + 0.025, + -175.76), + (-3.12, + -17.15, + 0.025, + -177.73), + (-3.0, + -20.32, + 0.025, + 178.49), + (-3.09, + -23.44, + 0.025, + 176.59), + (-3.43, + -26.54, + 0.025, + 171.44), + (-4.07, + -29.44, + 0.025, + 163.75), + (-5.09, + -32.27, + 0.025, + 158.2), + (-6.11, + -35.16, + 0.025, + 154.98), + (-7.57, + -37.78, + 0.025, + 154.98), + (-9.28, + -40.65, + 0.025, + 150.41)), + ((-6.12, + 1.62, + 0.025, + -152.9), + (-4.38, + -1.35, + 0.025, + -150.92), + (-3.08, + -4.3, + 0.025, + -157.9), + (-1.85, + -7.26, + 0.025, + -162.54), + (-0.93, + -10.49, + 0.025, + -167.71), + (-0.21, + -13.71, + 0.025, + -171.79), + (0.21, + -17.08, + 0.025, + -174.92), + (0.31, + -20.2, + 0.025, + 177.1), + (0.17, + -23.66, + 0.025, + 174.82), + (-0.23, + -26.91, + 0.025, + 170.51), + (-0.99, + -30.2, + 0.025, + 162.54), + (-2.02, + -33.28, + 0.025, + 160.48), + (-3.28, + -36.38, + 0.025, + 157.96), + (-4.67, + -39.17, + 0.025, + 154.13), + (-6.31, + -42.15, + 0.025, + 154.13)), + ((-2.99, + 3.09, + 0.025, + -154.37), + (-1.38, + -0.05, + 0.025, + -154.75), + (-0.19, + -3.29, + 0.025, + -159.22), + (1.17, + -6.51, + 0.025, + -162.74), + (2.28, + -9.8, + 0.025, + -168.73), + (3.09, + -13.28, + 0.025, + -173.49), + (3.46, + -16.63, + 0.025, + -176.81), + (3.69, + -20.38, + 0.025, + 179.14), + (3.61, + -24.12, + 0.025, + 175.78), + (3.0, + -27.55, + 0.025, + 170.87), + (2.15, + -30.72, + 0.025, + 167.41), + (1.04, + -34.26, + 0.025, + 162.11), + (-0.15, + -37.44, + 0.025, + 158.59), + (-1.64, + -40.52, + 0.025, + 153.89), + (-3.42, + -43.63, + 0.025, + 153.89)), + ((0.0, + 4.35, + 0.025, + -154.37), + (1.52, + 1.3, + 0.025, + -155.67), + (3.17, + -2.07, + 0.025, + -155.67), + (4.47, + -5.41, + 0.025, + -163.0), + (5.56, + -9.19, + 0.025, + -168.89), + (6.22, + -12.66, + 0.025, + -171.67), + (6.67, + -16.56, + 0.025, + -176.53), + (6.93, + -20.33, + 0.025, + 179.87), + (6.81, + -24.32, + 0.025, + 175.19), + (6.22, + -27.97, + 0.025, + 170.81), + (5.59, + -31.73, + 0.025, + 167.54), + (4.48, + -35.42, + 0.025, + 161.92), + (3.06, + -38.82, + 0.025, + 158.56), + (1.4, + -42.0, + 0.025, + 154.32), + (-0.71, + -45.17, + 0.025, + 153.27))) + self.avatarPositions = {} + self.modelCount = 8 + self.cameraTopView = (-22.78, + -41.65, + 31.53, + -51.55, + -42.68, + -2.96) + self.timer = None + self.timerStartTime = None + return None + + def getTitle(self): + return TTLocalizer.RaceGameTitle + + def getInstructions(self): + return TTLocalizer.RaceGameInstructions + + def getMaxDuration(self): + return 60 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.raceBoard = loader.loadModel('phase_4/models/minigames/race') + self.raceBoard.setPosHpr(0, 0, 0, 0, 0, 0) + self.raceBoard.setScale(0.8) + self.dice = loader.loadModel('phase_4/models/minigames/dice') + self.dice1 = self.dice.find('**/dice_button1') + self.dice2 = self.dice.find('**/dice_button2') + self.dice3 = self.dice.find('**/dice_button3') + self.dice4 = self.dice.find('**/dice_button4') + self.diceList = [self.dice1, + self.dice2, + self.dice3, + self.dice4] + self.music = base.loadMusic('phase_4/audio/bgm/minigame_race.ogg') + self.posBuzzer = base.loadSfx('phase_4/audio/sfx/MG_pos_buzzer.ogg') + self.negBuzzer = base.loadSfx('phase_4/audio/sfx/MG_neg_buzzer.ogg') + self.winSting = base.loadSfx('phase_4/audio/sfx/MG_win.ogg') + self.loseSting = base.loadSfx('phase_4/audio/sfx/MG_lose.ogg') + self.diceButtonList = [] + for i in xrange(1, 5): + button = self.dice.find('**/dice_button' + str(i)) + button_down = self.dice.find('**/dice_button' + str(i) + '_down') + button_ro = self.dice.find('**/dice_button' + str(i) + '_ro') + diceButton = DirectButton(image=(button, + button_down, + button_ro, + None), relief=None, pos=(0.433 + (i - 1) * 0.2, 0.0, 0.15), parent=base.a2dBottomLeft, scale=0.25, command=self.handleInputChoice, extraArgs=[i]) + diceButton.hide() + self.diceButtonList.append(diceButton) + + self.waitingChoicesLabel = DirectLabel(text=TTLocalizer.RaceGameWaitingChoices, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075) + self.waitingChoicesLabel.hide() + self.chanceMarker = loader.loadModel('phase_4/models/minigames/question_mark') + self.chanceCard = loader.loadModel('phase_4/models/minigames/chance_card') + self.chanceCardText = OnscreenText('', fg=(1.0, 0, 0, 1), scale=0.14, font=ToontownGlobals.getSignFont(), wordwrap=14, pos=(0.0, 0.2), mayChange=1) + self.chanceCardText.hide() + self.cardSound = base.loadSfx('phase_3.5/audio/sfx/GUI_stickerbook_turn.ogg') + self.chanceMarkers = [] + return + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.raceBoard.removeNode() + del self.raceBoard + self.dice.removeNode() + del self.dice + self.chanceMarker.removeNode() + del self.chanceMarker + self.chanceCardText.removeNode() + del self.chanceCardText + self.chanceCard.removeNode() + del self.chanceCard + self.waitingChoicesLabel.destroy() + del self.waitingChoicesLabel + del self.music + del self.posBuzzer + del self.negBuzzer + del self.winSting + del self.loseSting + del self.cardSound + for button in self.diceButtonList: + button.destroy() + + del self.diceButtonList + for marker in self.chanceMarkers: + marker.removeNode() + del marker + + del self.chanceMarkers + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + base.playMusic(self.music, looping=1, volume=0.8) + self.raceBoard.reparentTo(render) + camera.reparentTo(render) + p = self.cameraTopView + camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5]) + base.transitions.irisIn(0.4) + base.setBackgroundColor(0.1875, 0.7929, 0) + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + self.music.stop() + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + self.raceBoard.reparentTo(hidden) + self.chanceCard.reparentTo(hidden) + self.chanceCardText.hide() + if hasattr(self, 'chanceMarkers'): + for marker in self.chanceMarkers: + marker.reparentTo(hidden) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + self.resetPositions() + for i in xrange(self.numPlayers): + avId = self.avIdList[i] + if self.localAvId == avId: + self.localAvLane = i + avatar = self.getAvatar(avId) + if avatar: + avatar.reparentTo(render) + avatar.setAnimState('neutral', 1) + self.positionInPlace(avatar, i, 0) + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('inputChoice') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterInputChoice(self): + self.notify.debug('enterInputChoice') + for button in self.diceButtonList: + button.show() + + self.timer = ToontownTimer.ToontownTimer() + self.timer.hide() + if self.timerStartTime != None: + self.startTimer() + return + + def startTimer(self): + now = globalClock.getFrameTime() + elapsed = now - self.timerStartTime + self.timer.posInTopRightCorner() + self.timer.setTime(RaceGameGlobals.InputTimeout) + self.timer.countdown(RaceGameGlobals.InputTimeout - elapsed, self.handleChoiceTimeout) + self.timer.show() + + def setTimerStartTime(self, timestamp): + if not self.hasLocalToon: + return + self.timerStartTime = globalClockDelta.networkToLocalTime(timestamp) + if self.timer != None: + self.startTimer() + return + + def exitInputChoice(self): + for button in self.diceButtonList: + button.hide() + + if self.timer != None: + self.timer.destroy() + self.timer = None + self.timerStartTime = None + self.ignore('diceButton') + return + + def handleChoiceTimeout(self): + self.sendUpdate('setAvatarChoice', [0]) + self.gameFSM.request('waitServerChoices') + + def handleInputChoice(self, choice): + self.sendUpdate('setAvatarChoice', [choice]) + self.gameFSM.request('waitServerChoices') + + def enterWaitServerChoices(self): + self.notify.debug('enterWaitServerChoices') + self.waitingChoicesLabel.show() + + def exitWaitServerChoices(self): + self.waitingChoicesLabel.hide() + + def localToonWon(self): + localToonPosition = self.avatarPositions[self.localAvId] + if localToonPosition >= RaceGameGlobals.NumberToWin: + self.notify.debug('localToon won') + return 1 + else: + return 0 + + def anyAvatarWon(self): + for position in self.avatarPositions.values(): + if position >= RaceGameGlobals.NumberToWin: + self.notify.debug('anyAvatarWon: Somebody won') + return 1 + + self.notify.debug('anyAvatarWon: Nobody won') + return 0 + + def showNumbers(self, task): + self.notify.debug('showing numbers...') + self.diceInstanceList = [] + for i in xrange(len(task.choiceList)): + avId = self.avIdList[i] + choice = task.choiceList[i] + if choice == 0: + self.diceInstanceList.append(None) + else: + diceInstance = self.diceList[choice - 1].copyTo(self.raceBoard) + self.diceInstanceList.append(diceInstance) + dicePosition = self.avatarPositions[avId] + 1 + diceInstance.setScale(4.0) + self.positionInPlace(diceInstance, i, dicePosition) + diceInstance.setP(-90) + diceInstance.setZ(0.05) + + return Task.done + + def showMatches(self, task): + self.notify.debug('showing matches...') + for i in xrange(len(task.choiceList)): + avId = self.avIdList[i] + choice = task.choiceList[i] + if choice != 0: + diceInstance = self.diceInstanceList[i] + freq = task.choiceList.count(choice) + if freq == 1: + diceInstance.setColor(0.2, 1, 0.2, 1) + if avId == self.localAvId: + base.playSfx(self.posBuzzer) + else: + diceInstance.setColor(1, 0.2, 0.2, 1) + if avId == self.localAvId: + base.playSfx(self.negBuzzer) + + return Task.done + + def hideNumbers(self, task): + self.notify.debug('hiding numbers...') + for dice in self.diceInstanceList: + if dice: + dice.removeNode() + + self.diceInstanceList = [] + return Task.done + + def enterMoveAvatars(self, choiceList, positionList, rewardList): + self.notify.debug('in enterMoveAvatars:') + tasks = [] + self.avatarPositionsCopy = self.avatarPositions.copy() + for i in xrange(0, len(choiceList) / self.numPlayers): + startIndex = i * self.numPlayers + endIndex = startIndex + self.numPlayers + self.choiceList = choiceList[startIndex:endIndex] + self.positionList = positionList[startIndex:endIndex] + self.rewardList = rewardList[startIndex:endIndex] + self.notify.debug(' turn: ' + str(i + 1)) + self.notify.debug(' choiceList: ' + str(self.choiceList)) + self.notify.debug(' positionList: ' + str(self.positionList)) + self.notify.debug(' rewardList: ' + str(self.rewardList)) + longestLerpTime = self.getLongestLerpTime(i > 0) + self.notify.debug('longestLerpTime: ' + str(longestLerpTime)) + if i == 0: + snt = Task(self.showNumbers) + snt.choiceList = self.choiceList + smt = Task(self.showMatches) + smt.choiceList = self.choiceList + tasks += [snt, Task.pause(0.5), smt] + if longestLerpTime > 0.0: + self.notify.debug('someone moved...') + mat = Task(self.moveAvatars) + mat.choiceList = self.choiceList + mat.positionList = self.positionList + mat.rewardList = self.rewardList + mat.name = 'moveAvatars' + if i == 0: + tasks += [Task.pause(0.75), + mat, + Task.pause(0.75), + Task(self.hideNumbers), + Task.pause(longestLerpTime - 0.5)] + else: + mat.chance = 1 + tasks += [mat, Task.pause(longestLerpTime)] + tasks += self.showChanceRewards() + else: + self.notify.debug('no one moved...') + tasks += [Task.pause(1.0), Task(self.hideNumbers)] + + self.notify.debug('task list : ' + str(tasks)) + wdt = Task(self.walkDone) + wdt.name = 'walk done' + tasks.append(wdt) + moveTask = Task.sequence(*tasks) + taskMgr.add(moveTask, 'moveAvatars') + + def walkDone(self, task): + self.choiceList = [] + self.positionList = [] + if self.anyAvatarWon(): + self.gameFSM.request('winMovie') + else: + self.gameFSM.request('inputChoice') + return Task.done + + def getLongestLerpTime(self, afterFirst): + self.notify.debug('afterFirst: ' + str(afterFirst)) + longestTime = 0.0 + for i in xrange(len(self.choiceList)): + freq = self.choiceList.count(self.choiceList[i]) + if afterFirst or freq == 1: + oldPosition = self.avatarPositionsCopy[self.avIdList[i]] + newPosition = self.positionList[i] + self.avatarPositionsCopy[self.avIdList[i]] = newPosition + squares_walked = abs(newPosition - oldPosition) + longestTime = max(longestTime, self.getWalkDuration(squares_walked)) + + return longestTime + + def showChanceRewards(self): + tasks = [] + for reward in self.rewardList: + self.notify.debug('showChanceRewards: reward = ' + str(reward)) + index = self.rewardList.index(reward) + if reward != -1: + self.notify.debug('adding tasks!') + hcc = Task(self.hideChanceMarker) + hcc.chanceMarkers = self.chanceMarkers + hcc.index = index + sct = Task(self.showChanceCard) + sct.chanceCard = self.chanceCard + sct.cardSound = self.cardSound + stt = Task(self.showChanceCardText) + rewardEntry = RaceGameGlobals.ChanceRewards[reward] + stt.rewardIdx = reward + if rewardEntry[0][0] < 0 or rewardEntry[0][1] > 0: + stt.sound = self.negBuzzer + else: + stt.sound = self.posBuzzer + stt.picker = self.avIdList[index] + rct = Task(self.resetChanceCard) + task = Task.sequence(hcc, sct, Task.pause(1.0), stt, Task.pause(3.0), rct, Task.pause(0.25)) + tasks.append(task) + + return tasks + + def showChanceCard(self, task): + base.playSfx(task.cardSound) + self.chanceCard.reparentTo(render) + quat = Quat() + quat.setHpr((270, 0, -85.24)) + self.chanceCard.posQuatInterval(1.0, (19.62, 13.41, 13.14), quat, other=camera).start() + return Task.done + + def hideChanceMarker(self, task): + task.chanceMarkers[task.index].reparentTo(hidden) + return Task.done + + def showChanceCardText(self, task): + self.notify.debug('showing chance reward: ' + str(task.rewardIdx)) + name = self.getAvatar(task.picker).getName() + rewardEntry = RaceGameGlobals.ChanceRewards[task.rewardIdx] + cardText = '' + if rewardEntry[1] != -1: + rewardstr_fmt = TTLocalizer.RaceGameCardText + if rewardEntry[2] > 0: + rewardstr_fmt = TTLocalizer.RaceGameCardTextBeans + cardText = rewardstr_fmt % {'name': name, + 'reward': rewardEntry[1]} + else: + rewardstr_fmt = TTLocalizer.RaceGameCardTextHi1 + cardText = rewardstr_fmt % {'name': name} + base.playSfx(task.sound) + self.chanceCardText.setText(cardText) + self.chanceCardText.show() + return Task.done + + def resetChanceCard(self, task): + self.chanceCardText.hide() + self.chanceCard.reparentTo(hidden) + self.chanceCard.setPosHpr(0, 0, 0, 0, 0, 0) + return Task.done + + def moveCamera(self): + bestPosIdx = self.avatarPositions.values()[0] + best_lane = 0 + cur_lane = 0 + for pos in self.avatarPositions.values(): + if pos > bestPosIdx: + bestPosIdx = pos + best_lane = cur_lane + cur_lane = cur_lane + 1 + + bestPosIdx = min(RaceGameGlobals.NumberToWin, bestPosIdx) + localToonPosition = self.avatarPositions[self.localAvId] + savedCamPos = camera.getPos() + savedCamHpr = camera.getHpr() + pos1_idx = min(RaceGameGlobals.NumberToWin - 4, localToonPosition) + pos1 = self.posHprArray[self.localAvLane][pos1_idx] + bestPosLookAtIdx = bestPosIdx + 1 + localToonLookAtIdx = localToonPosition + 4 + if localToonLookAtIdx >= bestPosLookAtIdx: + pos2_idx = localToonLookAtIdx + pos2_idx = min(RaceGameGlobals.NumberToWin, pos2_idx) + pos2 = self.posHprArray[self.localAvLane][pos2_idx] + else: + pos2_idx = bestPosLookAtIdx + pos2_idx = min(RaceGameGlobals.NumberToWin, pos2_idx) + pos2 = self.posHprArray[best_lane][pos2_idx] + posDeltaVecX = pos2[0] - pos1[0] + posDeltaVecY = pos2[1] - pos1[1] + DistanceMultiplier = 0.8 + camposX = pos2[0] + DistanceMultiplier * posDeltaVecX + camposY = pos2[1] + DistanceMultiplier * posDeltaVecY + race_fraction = bestPosIdx / float(RaceGameGlobals.NumberToWin) + CamHeight = 10.0 * race_fraction + (1.0 - race_fraction) * 22.0 + CamPos = Vec3(camposX, camposY, pos2[2] + CamHeight) + camera.setPos(CamPos) + camera_lookat_idx = min(RaceGameGlobals.NumberToWin - 6, localToonPosition) + posLookAt = self.posHprArray[self.localAvLane][camera_lookat_idx] + camera.lookAt(posLookAt[0], posLookAt[1], posLookAt[2]) + CamQuat = Quat() + CamQuat.setHpr(camera.getHpr()) + camera.setPos(savedCamPos) + camera.setHpr(savedCamHpr) + camera.posQuatInterval(0.75, CamPos, CamQuat).start() + + def getWalkDuration(self, squares_walked): + walkDuration = abs(squares_walked / 1.2) + if squares_walked > 4: + walkDuration = walkDuration * 0.3 + return walkDuration + + def moveAvatars(self, task): + self.notify.debug('In moveAvatars: ') + self.notify.debug(' choiceList: ' + str(task.choiceList)) + self.notify.debug(' positionList: ' + str(task.positionList)) + self.notify.debug(' rewardList: ' + str(task.rewardList)) + for i in xrange(len(self.choiceList)): + avId = self.avIdList[i] + choice = task.choiceList[i] + position = task.positionList[i] + chance = max(0, hasattr(task, 'chance')) + if choice != 0: + oldPosition = self.avatarPositions[avId] + self.avatarPositions[avId] = position + self.moveCamera() + if not chance and task.choiceList.count(choice) != 1: + self.notify.debug('duplicate choice!') + else: + avatar = self.getAvatar(avId) + if avatar: + squares_walked = abs(position - oldPosition) + if squares_walked > 4: + self.notify.debug('running') + avatar.setPlayRate(1.0, 'run') + self.runInPlace(avatar, i, oldPosition, position, self.getWalkDuration(squares_walked)) + else: + if choice > 0: + self.notify.debug('walking forwards') + avatar.setPlayRate(1.0, 'walk') + else: + self.notify.debug('walking backwards') + avatar.setPlayRate(-1.0, 'walk') + self.walkInPlace(avatar, i, position, self.getWalkDuration(squares_walked)) + + return Task.done + + def exitMoveAvatars(self): + self.notify.debug('In exitMoveAvatars: removing hooks') + taskMgr.remove('moveAvatars') + return None + + def gameOverCallback(self, task): + self.gameOver() + return Task.done + + def enterWinMovie(self): + self.notify.debug('enterWinMovie') + if self.localToonWon(): + base.playSfx(self.winSting) + else: + base.playSfx(self.loseSting) + for avId in self.avIdList: + avPosition = self.avatarPositions[avId] + if avPosition >= RaceGameGlobals.NumberToWin: + avatar = self.getAvatar(avId) + if avatar: + lane = str(self.avIdList.index(avId)) + taskMgr.remove('runAvatar-' + lane) + taskMgr.remove('walkAvatar-' + lane) + avatar.setAnimState('jump', 1.0) + + taskMgr.doMethodLater(4.0, self.gameOverCallback, 'playMovie') + + def exitWinMovie(self): + taskMgr.remove('playMovie') + self.winSting.stop() + self.loseSting.stop() + + def enterCleanup(self): + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass + + def positionInPlace(self, avatar, lane, place): + place = min(place, len(self.posHprArray[lane]) - 1) + posH = self.posHprArray[lane][place] + avatar.setPosHpr(self.raceBoard, posH[0], posH[1], posH[2], posH[3], 0, 0) + + def walkInPlace(self, avatar, lane, place, time): + place = min(place, len(self.posHprArray[lane]) - 1) + posH = self.posHprArray[lane][place] + + def stopWalk(raceBoard = self.raceBoard, posH = posH): + avatar.setAnimState('neutral', 1) + if raceBoard.isEmpty(): + avatar.setPosHpr(0, 0, 0, 0, 0, 0) + else: + avatar.setPosHpr(raceBoard, posH[0], posH[1], posH[2], posH[3], 0, 0) + + posQuat = Quat() + posQuat.setHpr((posH[3], 0, 0)) + walkSeq = Sequence(Func(avatar.setAnimState, 'walk', 1), + avatar.posQuatInterval(time, posH[:3], posQuat, other=self.raceBoard), + Func(stopWalk)) + walkSeq.start() + + def runInPlace(self, avatar, lane, currentPlace, newPlace, time): + place = min(newPlace, len(self.posHprArray[lane]) - 1) + step = (place - currentPlace) / 3 + pos1 = self.posHprArray[lane][currentPlace + step] + pos2 = self.posHprArray[lane][currentPlace + 2 * step] + pos3 = self.posHprArray[lane][place] + + def stopRun(raceBoard = self.raceBoard, pos3 = pos3): + avatar.setAnimState('neutral', 1) + avatar.setPosHpr(raceBoard, pos3[0], pos3[1], pos3[2], pos3[3], 0, 0) + + pos1Quat = Quat() + pos1Quat.setHpr((pos1[3], 0, 0)) + pos2Quat = Quat() + pos2Quat.setHpr((pos1[3], 0, 0)) + pos3Quat = Quat() + pos3Quat.setHpr((pos1[3], 0, 0)) + runSeq = Sequence(Func(avatar.setAnimState, 'run', 1), + avatar.posQuatInterval(time/3., pos1[:3], pos1Quat, other=self.raceBoard), + avatar.posQuatInterval(time/3., pos2[:3], pos2Quat, other=self.raceBoard), + avatar.posQuatInterval(time/3., pos3[:3], pos3Quat, other=self.raceBoard), + Func(stopRun)) + runSeq.start() + + def setAvatarChoice(self, choice): + self.notify.error('setAvatarChoice should not be called on the client') + + def setAvatarChose(self, avId): + if not self.hasLocalToon: + return + self.notify.debug('setAvatarChose: avatar: ' + str(avId) + ' choose a number') + + def setChancePositions(self, positions): + if not self.hasLocalToon: + return + row = 0 + for pos in positions: + marker = self.chanceMarker.copyTo(render) + posHpr = self.posHprArray[row][pos] + marker.setPosHpr(self.raceBoard, posHpr[0], posHpr[1], posHpr[2], posHpr[3] + 180, 0, 0.025) + marker.setScale(0.7) + self.chanceMarkers.append(marker) + row += 1 + + def setServerChoices(self, choices, positions, rewards): + if not self.hasLocalToon: + return + for i in xrange(len(positions)): + if positions[i] > RaceGameGlobals.NumberToWin: + positions[i] = RaceGameGlobals.NumberToWin + if positions[i] < 0: + positions[i] = 0 + + self.notify.debug('setServerChoices: %s positions: %s rewards: %s ' % (choices, positions, rewards)) + self.gameFSM.request('moveAvatars', [choices, positions, rewards]) + + def resetPositions(self): + for avId in self.avIdList: + self.avatarPositions[avId] = 0 diff --git a/toontown/minigame/DistributedRaceGameAI.py b/toontown/minigame/DistributedRaceGameAI.py new file mode 100755 index 00000000..23b62346 --- /dev/null +++ b/toontown/minigame/DistributedRaceGameAI.py @@ -0,0 +1,305 @@ +from math import * +from DistributedMinigameAI import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import random +from direct.task.Task import Task +import RaceGameGlobals + +class DistributedRaceGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedRaceGameAI_initialized + except: + self.DistributedRaceGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedRaceGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['waitClientsChoices']), + State.State('waitClientsChoices', self.enterWaitClientsChoices, self.exitWaitClientsChoices, ['processChoices', 'cleanup']), + State.State('processChoices', self.enterProcessChoices, self.exitProcessChoices, ['waitClientsChoices', 'cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.avatarChoices = {} + self.avatarPositions = {} + self.chancePositions = {} + self.rewardDict = {} + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + self.resetChancePositions() + self.resetPositions() + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('waitClientsChoices') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def anyAvatarWon(self, positionDict): + for avId, position in positionDict.items(): + if position >= RaceGameGlobals.NumberToWin: + self.notify.debug('anyAvatarWon: Somebody won') + return 1 + + self.notify.debug('anyAvatarWon: Nobody won') + return 0 + + def allAvatarsChosen(self): + for choice in self.avatarChoices.values(): + if choice == -1: + return 0 + + return 1 + + def checkChoice(self, choice): + if choice not in RaceGameGlobals.ValidChoices: + self.notify.warning('checkChoice: invalid choice: ' + str(choice)) + return RaceGameGlobals.ValidChoices[0] + else: + return choice + + def resetChoices(self): + for avId in self.avIdList: + self.avatarChoices[avId] = -1 + + def resetPositions(self): + for avId in self.avIdList: + self.avatarPositions[avId] = 0 + + def resetChancePositions(self): + chancePositions = [] + for avId in self.avIdList: + pos = random.randint(5, RaceGameGlobals.NumberToWin - 1) + self.chancePositions[avId] = pos + self.rewardDict[avId] = random.randint(0, len(RaceGameGlobals.ChanceRewards) - 1) + chancePositions.append(pos) + + self.sendUpdate('setChancePositions', [chancePositions]) + + def setAvatarChoice(self, choice): + avatarId = self.air.getAvatarIdFromSender() + self.notify.debug('setAvatarChoice: avatar: ' + str(avatarId) + ' chose: ' + str(choice)) + self.avatarChoices[avatarId] = self.checkChoice(choice) + self.sendUpdate('setAvatarChose', [avatarId]) + if self.allAvatarsChosen(): + self.notify.debug('setAvatarChoice: all avatars have chosen') + self.gameFSM.request('processChoices') + else: + self.notify.debug('setAvatarChoice: still waiting for more choices') + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterWaitClientsChoices(self): + self.notify.debug('enterWaitClientsChoices') + self.resetChoices() + taskMgr.doMethodLater(RaceGameGlobals.InputTimeout, self.waitClientsChoicesTimeout, self.taskName('input-timeout')) + self.sendUpdate('setTimerStartTime', [globalClockDelta.getFrameNetworkTime()]) + + def exitWaitClientsChoices(self): + taskMgr.remove(self.taskName('input-timeout')) + + def waitClientsChoicesTimeout(self, task): + self.notify.debug('waitClientsChoicesTimeout: did not hear from all clients') + for avId in self.avatarChoices.keys(): + if self.avatarChoices[avId] == -1: + self.avatarChoices[avId] = 0 + + self.gameFSM.request('processChoices') + return Task.done + + def enterProcessChoices(self): + self.notify.debug('enterProcessChoices: ') + self.choiceArray = [] + self.positionArray = [] + self.rewardArray = [] + for avId in self.avIdList: + choice = self.avatarChoices[avId] + freq = self.avatarChoices.values().count(choice) + self.processChoice(avId, choice, freq) + + masterList = [] + for avId in self.avIdList: + masterList.append(-1) + + done = 0 + rewarded = 0 + while not done: + self.notify.debug('enterProcessChoice: notDone') + rewardList = masterList[:] + for avId in self.avIdList: + reward = self.processReward(avId) + if reward != -1: + rewarded = 1 + rewardList[self.avIdList.index(avId)] = reward + self.rewardArray += rewardList + for av in self.avIdList: + if av == avId: + self.processChoice(av, RaceGameGlobals.ChanceRewards[reward][0][0]) + else: + self.processChoice(av, RaceGameGlobals.ChanceRewards[reward][0][1]) + + break + + if not rewarded: + self.rewardArray += rewardList + self.notify.debug(' rewardList: ' + str(rewardList)) + self.notify.debug(' rewardArray: ' + str(self.rewardArray)) + done = rewardList.count(-1) == len(rewardList) + + self.checkForWinners() + + def processChoice(self, avId, choice, freq = 1): + self.notify.debug('processChoice: av = ' + str(avId) + ' choice = ' + str(choice)) + if freq == 1: + if choice != 0: + if self.avatarPositions[avId] < RaceGameGlobals.NumberToWin: + self.avatarPositions[avId] += choice + if self.avatarPositions[avId] < 0: + self.avatarPositions[avId] = 0 + self.choiceArray.append(choice) + self.positionArray.append(self.avatarPositions[avId]) + self.notify.debug('Process choice (' + str(choice) + ') for av: ' + str(avId)) + self.notify.debug(' choiceArray: ' + str(self.choiceArray)) + self.notify.debug(' positionArray: ' + str(self.positionArray)) + + def processReward(self, rewardee): + self.notify.debug('processReward: ' + str(rewardee)) + reward = -1 + if self.avatarPositions[rewardee] == self.chancePositions[rewardee]: + reward = self.rewardDict[rewardee] + bonus = RaceGameGlobals.ChanceRewards[reward][2] + self.scoreDict[rewardee] = self.scoreDict[rewardee] + bonus + self.chancePositions[rewardee] = -1 + return reward + + def checkForWinners(self): + self.notify.debug('checkForWinners: ') + self.sendUpdate('setServerChoices', [self.choiceArray, self.positionArray, self.rewardArray]) + delay = 0.0 + for reward in self.rewardArray: + if reward != -1: + delay += 7.0 + + if self.anyAvatarWon(self.avatarPositions): + numWinners = 0 + for avId in self.avIdList: + if self.avatarPositions[avId] >= RaceGameGlobals.NumberToWin: + numWinners = numWinners + 1 + + for avId in self.avIdList: + newJellybeans = ceil(self.avatarPositions[avId] * 0.5) + if self.avatarPositions[avId] >= RaceGameGlobals.NumberToWin: + newJellybeans = RaceGameGlobals.NumberToWin + if numWinners > 1: + newJellybeans = newJellybeans - 3 + self.scoreDict[avId] = self.scoreDict[avId] + newJellybeans + + taskMgr.doMethodLater(delay, self.rewardTimeoutTaskGameOver, self.taskName('reward-timeout')) + else: + taskMgr.doMethodLater(delay, self.rewardTimeoutTask, self.taskName('reward-timeout')) + + def oldEnterProcessChoices(self, recurse = 0): + self.notify.debug('enterProcessChoices') + if not recurse: + self.choiceArray = [] + self.positionArray = [] + self.rewardArray = [] + for avId in self.avIdList: + choice = self.avatarChoices[avId] + reward = -1 + if choice != 0: + freq = self.avatarChoices.values().count(choice) + if recurse or freq == 1: + self.avatarPositions[avId] += choice + if self.avatarPositions[avId] < 0: + self.avatarPositions[avId] = 0 + if self.avatarPositions[avId] == self.chancePositions[avId]: + reward = self.rewardDict[avId] + self.scoreDict[avId] = self.scoreDict[avId] + RaceGameGlobals.ChanceRewards[reward][2] + self.chancePositions[avId] = -1 + self.choiceArray.append(choice) + self.positionArray.append(self.avatarPositions[avId]) + self.rewardArray.append(reward) + + self.notify.debug(' choiceArray: ' + str(self.choiceArray)) + self.notify.debug(' positionArray: ' + str(self.positionArray)) + self.notify.debug(' rewardArray: ' + str(self.rewardArray)) + thisTurnRewards = self.rewardArray[-len(self.avatarPositions):] + rewardIndex = 0 + for reward in thisTurnRewards: + if reward != -1: + for avId in self.avIdList: + if self.avIdList.index(avId) == rewardIndex: + self.avatarChoices[avId] = RaceGameGlobals.ChanceRewards[reward][0][0] + else: + self.avatarChoices[avId] = RaceGameGlobals.ChanceRewards[reward][0][1] + + self.enterProcessChoices(1) + rewardIndex += 1 + + if not recurse: + self.sendUpdate('setServerChoices', [self.choiceArray, self.positionArray, self.rewardArray]) + delay = 0.0 + for reward in self.rewardArray: + if reward != -1: + delay += 7.0 + + if self.anyAvatarWon(self.avatarPositions): + numWinners = 0 + for avId in self.avIdList: + if self.avatarPositions[avId] >= RaceGameGlobals.NumberToWin: + numWinners = numWinners + 1 + + for avId in self.avIdList: + newJellybeans = ceil(self.avatarPositions[avId] * 0.5) + if self.avatarPositions[avId] >= RaceGameGlobals.NumberToWin: + newJellybeans = RaceGameGlobals.NumberToWin + if numWinners > 1: + newJellybeans = newJellybeans - 3 + self.scoreDict[avId] = self.scoreDict[avId] + newJellybeans + + taskMgr.doMethodLater(delay, self.rewardTimeoutTaskGameOver, self.taskName('reward-timeout')) + else: + taskMgr.doMethodLater(delay, self.rewardTimeoutTask, self.taskName('reward-timeout')) + return None + + def rewardTimeoutTaskGameOver(self, task): + self.notify.debug('Done waiting for rewards, game over') + self.gameOver() + return Task.done + + def rewardTimeoutTask(self, task): + self.notify.debug('Done waiting for rewards') + self.gameFSM.request('waitClientsChoices') + return Task.done + + def exitProcessChoices(self): + taskMgr.remove(self.taskName('reward-timeout')) + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedRingGame.py b/toontown/minigame/DistributedRingGame.py new file mode 100755 index 00000000..dd22668b --- /dev/null +++ b/toontown/minigame/DistributedRingGame.py @@ -0,0 +1,912 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from DistributedMinigame import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +import ArrowKeys +import Ring +import RingTrack +import RingGameGlobals +import RingGroup +import RingTrackGroups +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class DistributedRingGame(DistributedMinigame): + UPDATE_ENVIRON_TASK = 'RingGameUpdateEnvironTask' + UPDATE_LOCALTOON_TASK = 'RingGameUpdateLocalToonTask' + UPDATE_RINGS_TASK = 'RingGameUpdateRingsTask' + UPDATE_SHADOWS_TASK = 'RingGameUpdateShadowsTask' + COLLISION_DETECTION_TASK = 'RingGameCollisionDetectionTask' + END_GAME_WAIT_TASK = 'RingGameCollisionDetectionTask' + COLLISION_DETECTION_PRIORITY = 5 + UPDATE_SHADOWS_PRIORITY = 47 + RT_UNKNOWN = 0 + RT_SUCCESS = 1 + RT_GROUPSUCCESS = 2 + RT_FAILURE = 3 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedRingGame', [State.State('off', self.enterOff, self.exitOff, ['swim']), State.State('swim', self.enterSwim, self.exitSwim, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + + def getTitle(self): + return TTLocalizer.RingGameTitle + + def getInstructions(self): + p = self.avIdList.index(self.localAvId) + if self.isSinglePlayer(): + text = TTLocalizer.RingGameInstructionsSinglePlayer + else: + text = TTLocalizer.RingGameInstructionsMultiPlayer + return text % RingGameGlobals.ringColors[self.colorIndices[p]][0] + + def getMaxDuration(self): + return RingGameGlobals.NUM_RING_GROUPS * self.ringGroupArrivalPeriod + self.T_FIRST_RING_GROUP_ARRIVES + self.GAME_END_DELAY + + def defineConstants(self): + self.CAMERA_Y = -15 + self.TOON_Y = 0 + self.FAR_PLANE_DIST = 150 + tScreenCenterToEdge = 1.0 + self.TOONXZ_SPEED = RingGameGlobals.MAX_TOONXZ / tScreenCenterToEdge + self.WATER_DEPTH = 35.0 + self.ENVIRON_LENGTH = 150.0 + self.ENVIRON_START_OFFSET = 20.0 + self.TOON_INITIAL_SPACING = 4.0 + waterZOffset = 3.0 + self.SEA_FLOOR_Z = -self.WATER_DEPTH / 2.0 + waterZOffset + farPlaneDist = self.CAMERA_Y + self.FAR_PLANE_DIST - self.TOON_Y + self.ringGroupArrivalPeriod = 3.0 + self.RING_GROUP_SPACING = farPlaneDist / 2.0 + self.TOON_SWIM_VEL = self.RING_GROUP_SPACING / self.ringGroupArrivalPeriod + self.T_FIRST_RING_GROUP_ARRIVES = farPlaneDist / self.TOON_SWIM_VEL + self.WATER_COLOR = Vec4(0, 0, 0.6, 1) + self.SHADOW_Z_OFFSET = 0.1 + self.Y_VIS_MAX = self.FAR_PLANE_DIST + self.Y_VIS_MIN = self.CAMERA_Y + ringRadius = RingGameGlobals.RING_RADIUS * 1.025 + self.RING_RADIUS_SQRD = ringRadius * ringRadius + self.GAME_END_DELAY = 1.0 + self.RING_RESPONSE_DELAY = 0.1 + self.TOON_LOD = 1000 + self.NumRingGroups = 16 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.defineConstants() + self.music = base.loadMusic('phase_4/audio/bgm/MG_toontag.ogg') + self.sndAmbience = base.loadSfx('phase_4/audio/sfx/AV_ambient_water.ogg') + self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.ogg') + loadBase = 'phase_4/models/minigames/' + self.environModel = loader.loadModel(loadBase + 'swimming_game.bam') + self.environModel.setPos(0, self.ENVIRON_LENGTH / 2.0, self.SEA_FLOOR_Z) + self.environModel.flattenMedium() + self.ringModel = loader.loadModel(loadBase + 'swimming_game_ring.bam') + self.ringModel.setTransparency(1) + modelRadius = 4.0 + self.ringModel.setScale(RingGameGlobals.RING_RADIUS / modelRadius) + self.ringModel.flattenMedium() + self.dropShadowModel = loader.loadModel('phase_3/models/props/drop_shadow') + self.dropShadowModel.setColor(0, 0, 0, 0.5) + self.dropShadowModel.flattenMedium() + self.toonDropShadows = [] + self.ringDropShadows = [] + self.__textGen = TextNode('ringGame') + self.__textGen.setFont(ToontownGlobals.getSignFont()) + self.__textGen.setAlign(TextNode.ACenter) + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + del self.__textGen + del self.toonDropShadows + del self.ringDropShadows + self.dropShadowModel.removeNode() + del self.dropShadowModel + self.environModel.removeNode() + del self.environModel + self.ringModel.removeNode() + del self.ringModel + del self.music + del self.sndAmbience + del self.sndPerfect + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.arrowKeys = ArrowKeys.ArrowKeys() + toon = base.localAvatar + toon.reparentTo(render) + toon.setAnimState('swim', 1.0) + toon.stopBobSwimTask() + toon.useLOD(self.TOON_LOD) + self.__placeToon(self.localAvId) + toon.dropShadow.hide() + camera.reparentTo(render) + camera.reparentTo(base.localAvatar) + camera.setPosHpr(0, self.CAMERA_Y + self.TOON_Y, 0, 0, 0, 0) + base.camLens.setMinFov(80/(4./3.)) + base.camLens.setFar(self.FAR_PLANE_DIST) + base.setBackgroundColor(self.WATER_COLOR) + self.__fog = Fog('ringGameFog') + if base.wantFog: + self.__fog.setColor(self.WATER_COLOR) + self.__fog.setLinearRange(0.1, self.FAR_PLANE_DIST - 1.0) + render.setFog(self.__fog) + self.environNode = render.attachNewNode('environNode') + self.environBlocks = [] + for i in xrange(0, 2): + instance = self.environModel.instanceUnderNode(self.environNode, 'model') + y = self.ENVIRON_LENGTH * i + instance.setY(y) + self.environBlocks.append(instance) + for j in xrange(0, 2): + instance = self.environModel.instanceUnderNode(self.environNode, 'blocks') + x = self.ENVIRON_LENGTH * (j + 1) + instance.setY(y) + instance.setX(-x) + self.environBlocks.append(instance) + + for j in xrange(0, 2): + instance = self.environModel.instanceUnderNode(self.environNode, 'blocks') + x = self.ENVIRON_LENGTH * (j + 1) + instance.setY(y) + instance.setX(x) + self.environBlocks.append(instance) + + self.ringNode = render.attachNewNode('ringNode') + self.sndTable = {'gotRing': [None] * self.numPlayers, + 'missedRing': [None] * self.numPlayers} + for i in xrange(0, self.numPlayers): + self.sndTable['gotRing'][i] = base.loadSfx('phase_4/audio/sfx/ring_get.ogg') + self.sndTable['missedRing'][i] = base.loadSfx('phase_4/audio/sfx/ring_miss.ogg') + + self.__addToonDropShadow(self.getAvatar(self.localAvId)) + self.__spawnUpdateEnvironTask() + self.__spawnUpdateShadowsTask() + self.__spawnUpdateLocalToonTask() + if None != self.sndAmbience: + base.playSfx(self.sndAmbience, looping=1, volume=0.8) + return + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + self.music.stop() + if None != self.sndAmbience: + self.sndAmbience.stop() + self.__killUpdateLocalToonTask() + self.__killUpdateShadowsTask() + self.__killUpdateEnvironTask() + del self.sndTable + self.__removeAllToonDropShadows() + render.clearFog() + base.camLens.setFar(ToontownGlobals.DefaultCameraFar) + base.camLens.setMinFov(settings['fov']/(4./3.)) + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + self.arrowKeys.destroy() + del self.arrowKeys + for block in self.environBlocks: + del block + + self.environNode.removeNode() + del self.environNode + self.ringNode.removeNode() + del self.ringNode + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.dropShadow.show() + av.resetLOD() + av.setAnimState('neutral', 1.0) + + return + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + self.__removeToonDropShadow(self.remoteToons[avId]) + DistributedMinigame.handleDisabledAvatar(self, avId) + + def __genText(self, text): + self.__textGen.setText(text) + return self.__textGen.generate() + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + i = self.avIdList.index(avId) + numToons = float(self.numPlayers) + x = i * self.TOON_INITIAL_SPACING + x -= self.TOON_INITIAL_SPACING * (numToons - 1) / 2.0 + toon.setPosHpr(x, self.TOON_Y, 0, 0, 0, 0) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + if not self.isSinglePlayer(): + base.localAvatar.collisionsOff() + cSphere = CollisionSphere(0.0, 0.0, 0.0, RingGameGlobals.CollisionRadius) + cSphereNode = CollisionNode('RingGameSphere-%s' % self.localAvId) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(RingGameGlobals.CollideMask) + cSphereNode.setIntoCollideMask(BitMask32.allOff()) + self.cSphereNodePath = base.localAvatar.attachNewNode(cSphereNode) + self.pusher = CollisionHandlerPusher() + self.pusher.addCollider(self.cSphereNodePath, base.localAvatar) + self.pusher.setHorizontal(0) + self.cTrav = CollisionTraverser('DistributedRingGame') + self.cTrav.addCollider(self.cSphereNodePath, self.pusher) + self.remoteToonCollNPs = {} + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + cSphere = CollisionSphere(0.0, 0.0, 0.0, RingGameGlobals.CollisionRadius) + cSphereNode = CollisionNode('RingGameSphere-%s' % avId) + cSphereNode.addSolid(cSphere) + cSphereNode.setCollideMask(RingGameGlobals.CollideMask) + cSphereNP = toon.attachNewNode(cSphereNode) + self.remoteToonCollNPs[avId] = cSphereNP + + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toon.setAnimState('swim', 1.0) + toon.stopBobSwimTask() + toon.useLOD(self.TOON_LOD) + toon.dropShadow.hide() + self.__addToonDropShadow(toon) + toon.startSmooth() + + self.remoteToons = {} + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + self.remoteToons[avId] = toon + + self.__nextRingGroupResultIndex = 0 + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('swim') + base.playMusic(self.music, looping=0, volume=0.8) + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterSwim(self): + self.notify.debug('enterSwim') + self.__ringTimeBase = self.gameStartTime + self.__numRingGroups = RingGameGlobals.NUM_RING_GROUPS + self.__ringGroupsPassed = 0 + self.__generateRings() + self.__spawnUpdateRingsTask() + self.__spawnCollisionDetectionTask() + self.__ringTracks = [] + self.colorRing = self.ringModel.copyTo(hidden) + self.colorRing.reparentTo(base.a2dBottomRight) + self.colorRing.setTwoSided(0) + self.colorRing.setPos(-0.143, 10, 0.14) + self.colorRing.setScale(0.04) + p = self.avIdList.index(self.localAvId) + self.colorRing.setColor(RingGameGlobals.ringColors[self.colorIndices[p]][1]) + self.resultTable = [self.RT_UNKNOWN] * self.__numRingGroups + self.__initTallyDisplay() + + def __initTallyDisplay(self): + self.__tallyTextNode = TextNode('tally') + self.__tallyTextNode.setFont(ToontownGlobals.getSignFont()) + self.__tallyTextNode.setAlign(TextNode.ACenter) + self.tallyMarkers = [None] * self.__numRingGroups + for i in xrange(0, self.__numRingGroups): + self.__createTallyMarker(i, self.RT_UNKNOWN) + + return + + def __destroyTallyDisplay(self): + for i in xrange(0, self.__numRingGroups): + self.__deleteTallyMarker(i) + + del self.tallyMarkers + del self.__tallyTextNode + + def __createTallyMarker(self, index, result): + chars = '-OOX' + colors = (Point4(0.8, 0.8, 0.8, 1), + Point4(0, 1, 0, 1), + Point4(1, 1, 0, 1), + Point4(1, 0, 0, 1)) + self.__deleteTallyMarker(index) + self.__tallyTextNode.setText(chars[result]) + node = self.__tallyTextNode.generate() + tallyText = base.a2dBottomLeft.attachNewNode(node) + tallyText.setColor(colors[result]) + tallyText.setScale(0.1) + zOffset = 0 + if result == self.RT_UNKNOWN: + zOffset = 0.015 + xSpacing = 0.085 + tallyText.setPos(0.333 + xSpacing * index, 0, 0.07 + zOffset) + self.tallyMarkers[index] = tallyText + + def __deleteTallyMarker(self, index): + marker = self.tallyMarkers[index] + if None != marker: + marker.removeNode() + self.tallyMarkers[index] = None + return + + def __updateTallyDisplay(self, index): + self.__createTallyMarker(index, self.resultTable[index]) + + def __generateRings(self): + self.ringGroups = [] + difficultyDistributions = {ToontownGlobals.ToontownCentral: [14, 2, 0], + ToontownGlobals.DonaldsDock: [10, 6, 0], + ToontownGlobals.DaisyGardens: [4, 12, 0], + ToontownGlobals.MinniesMelodyland: [4, 8, 4], + ToontownGlobals.TheBrrrgh: [4, 6, 6], + ToontownGlobals.DonaldsDreamland: [2, 6, 8]} + for distr in difficultyDistributions.values(): + sum = reduce(lambda x, y: x + y, distr) + + difficultyPatterns = {ToontownGlobals.ToontownCentral: [[0] * 14 + [1] * 2 + [2] * 0, [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1] * 2, [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1]], + ToontownGlobals.DonaldsDock: [[0] * 10 + [1] * 6 + [2] * 0, [0, + 0, + 0, + 0, + 0, + 1, + 1, + 1] * 2, [0, + 0, + 0, + 1, + 0, + 0, + 1, + 1] * 2], + ToontownGlobals.DaisyGardens: [[0] * 4 + [1] * 12 + [2] * 0, [0, + 0, + 1, + 1, + 1, + 1, + 1, + 1] * 2, [0, + 1, + 1, + 1, + 0, + 1, + 1, + 1] * 2], + ToontownGlobals.MinniesMelodyland: [[0] * 4 + [1] * 8 + [2] * 4, + [0, + 0, + 1, + 1, + 1, + 1, + 2, + 2] * 2, + [0, + 1, + 1, + 1, + 1, + 0, + 2, + 2] * 2, + [0, + 1, + 1, + 2, + 0, + 1, + 1, + 2] * 2, + [0, + 1, + 2, + 1, + 0, + 1, + 2, + 1] * 2], + ToontownGlobals.TheBrrrgh: [[0] * 4 + [1] * 6 + [2] * 6, + [0, + 0, + 1, + 1, + 1, + 2, + 2, + 2] * 2, + [0, + 1, + 1, + 1, + 0, + 2, + 2, + 2] * 2, + [0, + 1, + 1, + 2, + 0, + 1, + 2, + 2] * 2, + [0, + 1, + 2, + 1, + 0, + 1, + 2, + 2] * 2], + ToontownGlobals.DonaldsDreamland: [[0] * 2 + [1] * 6 + [2] * 8, + [0, + 1, + 1, + 1, + 2, + 2, + 2, + 2] * 2, + [0, + 1, + 1, + 2, + 2, + 1, + 2, + 2] * 2, + [0, + 1, + 2, + 1, + 2, + 1, + 2, + 2] * 2]} + safezone = self.getSafezoneId() + numGroupsPerDifficulty = difficultyDistributions[safezone] + + def patternsAreValid(difficultyPatterns = difficultyPatterns, difficultyDistributions = difficultyDistributions): + for sz in difficultyPatterns.keys(): + for pattern in difficultyPatterns[sz]: + for difficulty in [0, 1, 2]: + numGroupsPerDifficulty = difficultyDistributions[sz] + if numGroupsPerDifficulty[difficulty] != pattern.count(difficulty): + print 'safezone:', sz + print 'pattern:', pattern + print 'difficulty:', difficulty + print 'expected %s %ss, found %s' % (numGroupsPerDifficulty[difficulty], difficulty, pattern.count(difficulty)) + return 0 + + return 1 + + pattern = self.randomNumGen.choice(difficultyPatterns[self.getSafezoneId()]) + for i in xrange(0, self.__numRingGroups): + numRings = self.numPlayers + trackGroup = RingTrackGroups.getRandomRingTrackGroup(pattern[i], numRings, self.randomNumGen) + ringGroup = RingGroup.RingGroup(trackGroup, self.ringModel, RingGameGlobals.MAX_TOONXZ, self.colorIndices) + for r in xrange(numRings): + self.__addRingDropShadow(ringGroup.getRing(r)) + + self.ringGroups.append(ringGroup) + ringGroup.reparentTo(self.ringNode) + firstGroupOffset = self.TOON_Y + self.T_FIRST_RING_GROUP_ARRIVES * self.TOON_SWIM_VEL + ringGroup.setY(i * self.RING_GROUP_SPACING + firstGroupOffset) + + def __destroyRings(self): + for group in self.ringGroups: + group.delete() + group.removeNode() + + self.__removeAllRingDropShadows() + del self.ringGroups + + def __spawnUpdateLocalToonTask(self): + self.__initPosBroadcast() + taskMgr.remove(self.UPDATE_LOCALTOON_TASK) + taskMgr.add(self.__updateLocalToonTask, self.UPDATE_LOCALTOON_TASK) + + def __killUpdateLocalToonTask(self): + taskMgr.remove(self.UPDATE_LOCALTOON_TASK) + + def __initPosBroadcast(self): + self.__posBroadcastPeriod = 0.2 + self.__timeSinceLastPosBroadcast = 0.0 + self.__lastPosBroadcast = self.getAvatar(self.localAvId).getPos() + self.__storeStop = 0 + lt = self.getAvatar(self.localAvId) + lt.d_clearSmoothing() + lt.sendCurrentPosition() + + def __posBroadcast(self, dt): + self.__timeSinceLastPosBroadcast += dt + if self.__timeSinceLastPosBroadcast > self.__posBroadcastPeriod: + self.__timeSinceLastPosBroadcast -= self.__posBroadcastPeriod + self.getAvatar(self.localAvId).cnode.broadcastPosHprFull() + + def __updateLocalToonTask(self, task): + dt = globalClock.getDt() + toonPos = self.getAvatar(self.localAvId).getPos() + pos = [toonPos[0], 0, toonPos[2]] + xVel = 0.0 + if self.arrowKeys.leftPressed(): + xVel -= self.TOONXZ_SPEED + if self.arrowKeys.rightPressed(): + xVel += self.TOONXZ_SPEED + pos[0] += xVel * dt + if pos[0] < -RingGameGlobals.MAX_TOONXZ: + pos[0] = -RingGameGlobals.MAX_TOONXZ + if pos[0] > RingGameGlobals.MAX_TOONXZ: + pos[0] = RingGameGlobals.MAX_TOONXZ + zVel = 0.0 + if self.arrowKeys.upPressed(): + zVel += self.TOONXZ_SPEED + if self.arrowKeys.downPressed(): + zVel -= self.TOONXZ_SPEED + pos[2] += zVel * dt + if pos[2] < -RingGameGlobals.MAX_TOONXZ: + pos[2] = -RingGameGlobals.MAX_TOONXZ + if pos[2] > RingGameGlobals.MAX_TOONXZ: + pos[2] = RingGameGlobals.MAX_TOONXZ + self.getAvatar(self.localAvId).setPos(pos[0], self.TOON_Y, pos[2]) + if hasattr(self, 'cTrav'): + self.cTrav.traverse(render) + self.__posBroadcast(dt) + return Task.cont + + def exitSwim(self): + for track in self.__ringTracks: + track.finish() + + del self.__ringTracks + self.colorRing.removeNode() + del self.colorRing + self.__destroyTallyDisplay() + del self.resultTable + taskMgr.remove(self.END_GAME_WAIT_TASK) + self.__killUpdateRingsTask() + self.__killCollisionDetectionTask() + self.__destroyRings() + + def enterCleanup(self): + self.notify.debug('enterCleanup') + if not self.isSinglePlayer(): + for np in self.remoteToonCollNPs.values(): + np.removeNode() + + del self.remoteToonCollNPs + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + del self.pusher + del self.cTrav + base.localAvatar.collisionsOn() + + def exitCleanup(self): + pass + + def __addDropShadow_INTERNAL(self, object, scale_x, scale_y, scale_z, list): + shadow = self.dropShadowModel.copyTo(render) + shadow.setPos(0, self.CAMERA_Y, -100) + shadow.setScale(scale_x, scale_y, scale_z) + list.append([shadow, object]) + + def __removeDropShadow_INTERNAL(self, object, list): + for i in xrange(len(list)): + entry = list[i] + if entry[1] == object: + entry[0].removeNode() + list.pop(i) + return + + self.notify.warning('parent object ' + str(object) + ' not found in drop shadow list!') + + def __addToonDropShadow(self, object): + self.__addDropShadow_INTERNAL(object, 0.5, 0.5, 0.5, self.toonDropShadows) + + def __removeToonDropShadow(self, object): + self.__removeDropShadow_INTERNAL(object, self.toonDropShadows) + + def __addRingDropShadow(self, object): + self.__addDropShadow_INTERNAL(object, 1.2, 0.31, 1.0, self.ringDropShadows) + + def __removeRingDropShadow(self, object): + self.__removeDropShadow_INTERNAL(object, self.ringDropShadows) + + def __removeAllToonDropShadows(self): + for entry in self.toonDropShadows: + entry[0].removeNode() + + self.toonDropShadows = [] + + def __removeAllRingDropShadows(self): + for entry in self.ringDropShadows: + entry[0].removeNode() + + self.ringDropShadows = [] + + def __spawnUpdateShadowsTask(self): + taskMgr.remove(self.UPDATE_SHADOWS_TASK) + taskMgr.add(self.__updateShadowsTask, self.UPDATE_SHADOWS_TASK, priority=self.UPDATE_SHADOWS_PRIORITY) + + def __killUpdateShadowsTask(self): + taskMgr.remove(self.UPDATE_SHADOWS_TASK) + + def __updateShadowsTask(self, task): + list = self.toonDropShadows + self.ringDropShadows + for entry in list: + object = entry[1] + y = object.getY(render) + if y > self.Y_VIS_MAX: + continue + x = object.getX(render) + z = self.SEA_FLOOR_Z + self.SHADOW_Z_OFFSET + shadow = entry[0] + shadow.setPos(x, y, z) + + return Task.cont + + def __spawnUpdateEnvironTask(self): + taskMgr.remove(self.UPDATE_ENVIRON_TASK) + taskMgr.add(self.__updateEnvironTask, self.UPDATE_ENVIRON_TASK) + + def __killUpdateEnvironTask(self): + taskMgr.remove(self.UPDATE_ENVIRON_TASK) + + def __updateEnvironTask(self, task): + t = globalClock.getFrameTime() - self.__timeBase + distance = t * self.TOON_SWIM_VEL + distance %= self.ENVIRON_LENGTH + distance += self.ENVIRON_START_OFFSET + self.environNode.setY(-distance) + return Task.cont + + def __spawnUpdateRingsTask(self): + taskMgr.remove(self.UPDATE_RINGS_TASK) + taskMgr.add(self.__updateRingsTask, self.UPDATE_RINGS_TASK) + + def __killUpdateRingsTask(self): + taskMgr.remove(self.UPDATE_RINGS_TASK) + + def __updateRingsTask(self, task): + t = globalClock.getFrameTime() - self.__ringTimeBase + distance = t * self.TOON_SWIM_VEL + self.ringNode.setY(-distance) + for ringGroup in self.ringGroups: + groupY = ringGroup.getY(render) + if groupY <= self.Y_VIS_MAX and groupY >= self.Y_VIS_MIN: + ringGroup.setT(t) + + return Task.cont + + def __spawnCollisionDetectionTask(self): + self.__ringGroupsPassed = 0 + taskMgr.remove(self.COLLISION_DETECTION_TASK) + taskMgr.add(self.__collisionDetectionTask, self.COLLISION_DETECTION_TASK, priority=self.COLLISION_DETECTION_PRIORITY) + + def __killCollisionDetectionTask(self): + taskMgr.remove(self.COLLISION_DETECTION_TASK) + + def __makeRingSuccessFadeTrack(self, ring, duration, endScale, ringIndex): + targetScale = Point3(endScale, endScale, endScale) + dFade = 0.5 * duration + dColorChange = duration - dFade + origColor = ring.getColor() + targetColor = Point4(1.0 - origColor[0], 1.0 - origColor[1], 1.0 - origColor[2], 1) + + def colorChangeFunc(t, ring = ring, targetColor = targetColor, origColor = origColor): + newColor = targetColor * t + origColor * (1.0 - t) + ring.setColor(newColor) + + def fadeFunc(t, ring = ring): + ring.setColorScale(1, 1, 1, 1.0 - t) + + fadeAwayTrack = Parallel(Sequence(LerpFunctionInterval(colorChangeFunc, fromData=0.0, toData=1.0, duration=dColorChange), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=dFade)), LerpScaleInterval(ring, duration, targetScale)) + successTrack = Sequence(Wait(self.RING_RESPONSE_DELAY), Parallel(SoundInterval(self.sndTable['gotRing'][ringIndex]), Sequence(Func(ring.wrtReparentTo, render), fadeAwayTrack, Func(self.__removeRingDropShadow, ring), Func(ring.reparentTo, hidden)))) + return successTrack + + def __makeRingFailureFadeTrack(self, ring, duration, ringIndex): + ts = 0.01 + targetScale = Point3(ts, ts, ts) + missedTextNode = self.__genText(TTLocalizer.RingGameMissed) + missedText = hidden.attachNewNode(missedTextNode) + ringColor = RingGameGlobals.ringColors[self.colorIndices[ringIndex]][1] + + def addMissedText(ring = ring, ringColor = ringColor, missedText = missedText): + missedText.reparentTo(render) + missedText.setPos(ring.getPos(render) + Point3(0, -1, 0)) + missedText.setColor(ringColor) + + def removeMissedText(missedText = missedText): + missedText.removeNode() + missedText = None + return + + failureTrack = Sequence(Wait(self.RING_RESPONSE_DELAY), Parallel(SoundInterval(self.sndTable['missedRing'][ringIndex]), Sequence(Func(ring.wrtReparentTo, render), Func(addMissedText), LerpScaleInterval(ring, duration, targetScale, blendType='easeIn'), Func(removeMissedText), Func(self.__removeRingDropShadow, ring), Func(ring.reparentTo, hidden)))) + return failureTrack + + def __makeRingFadeAway(self, ring, success, ringIndex): + if success: + track = self.__makeRingSuccessFadeTrack(ring, 0.5, 2.0, ringIndex) + else: + track = self.__makeRingFailureFadeTrack(ring, 0.5, ringIndex) + self.__ringTracks.append(track) + track.start() + + def __collisionDetectionTask(self, task): + nextRingGroupIndex = self.__ringGroupsPassed + nextRingGroup = self.ringGroups[nextRingGroupIndex] + if nextRingGroup.getY(render) < 0: + groupIndex = nextRingGroupIndex + gotRing = 0 + ourRing = nextRingGroup.getRing(self.avIdList.index(self.localAvId)) + ringPos = ourRing.getPos(render) + localToonPos = base.localAvatar.getPos(render) + distX = localToonPos[0] - ringPos[0] + distZ = localToonPos[2] - ringPos[2] + distSqrd = distX * distX + distZ * distZ + if distSqrd <= self.RING_RADIUS_SQRD: + gotRing = 1 + self.__makeRingFadeAway(ourRing, gotRing, self.avIdList.index(self.localAvId)) + if gotRing: + self.resultTable[groupIndex] = self.RT_SUCCESS + else: + self.resultTable[groupIndex] = self.RT_FAILURE + self.__updateTallyDisplay(groupIndex) + self.sendUpdate('setToonGotRing', [gotRing]) + if self.isSinglePlayer(): + self.__processRingGroupResults([gotRing]) + self.__ringGroupsPassed += 1 + if self.__ringGroupsPassed >= self.__numRingGroups: + self.__killCollisionDetectionTask() + return Task.cont + + def __endGameDolater(self, task): + self.gameOver() + return Task.done + + def setTimeBase(self, timestamp): + if not self.hasLocalToon: + return + self.__timeBase = globalClockDelta.networkToLocalTime(timestamp) + + def setColorIndices(self, a, b, c, d): + if not self.hasLocalToon: + return + self.colorIndices = [a, + b, + c, + d] + + def __getSuccessTrack(self, groupIndex): + + def makeSuccessTrack(text, holdDuration, fadeDuration = 0.5, perfect = 0, self = self): + successText = hidden.attachNewNode(self.__genText(text)) + successText.setScale(0.25) + successText.setColor(1, 1, 0.5, 1) + + def fadeFunc(t, text): + text.setColorScale(1, 1, 1, 1.0 - t) + + def destroyText(text): + text.removeNode() + text = None + return + + track = Sequence(Func(successText.reparentTo, aspect2d), Wait(holdDuration), LerpFunctionInterval(fadeFunc, extraArgs=[successText], fromData=0.0, toData=1.0, duration=fadeDuration, blendType='easeIn'), Func(destroyText, successText)) + if perfect: + track = Parallel(track, SoundInterval(self.sndPerfect)) + return track + + def isPerfect(list, goodValues): + for value in list: + if value not in goodValues: + return 0 + + return 1 + + if groupIndex >= self.__numRingGroups - 1: + if not self.isSinglePlayer(): + if isPerfect(self.resultTable, [self.RT_GROUPSUCCESS]): + return makeSuccessTrack(TTLocalizer.RingGameGroupPerfect, 1.5, perfect=1) + if isPerfect(self.resultTable, [self.RT_SUCCESS, self.RT_GROUPSUCCESS]): + return makeSuccessTrack(TTLocalizer.RingGamePerfect, 1.5, perfect=1) + return Wait(1.0) + if not self.isSinglePlayer(): + if self.resultTable[groupIndex] == self.RT_GROUPSUCCESS: + return makeSuccessTrack(TTLocalizer.RingGameGroupBonus, 0.0, fadeDuration=0.4) + return None + + def __processRingGroupResults(self, results): + groupIndex = self.__nextRingGroupResultIndex + ringGroup = self.ringGroups[self.__nextRingGroupResultIndex] + self.__nextRingGroupResultIndex += 1 + for i in xrange(0, self.numPlayers): + if self.avIdList[i] != self.localAvId: + ring = ringGroup.getRing(i) + self.__makeRingFadeAway(ring, results[i], i) + + if not self.isSinglePlayer(): + if 0 not in results: + self.notify.debug('Everyone got their rings!!') + self.resultTable[groupIndex] = self.RT_GROUPSUCCESS + self.__updateTallyDisplay(groupIndex) + successTrack = self.__getSuccessTrack(groupIndex) + endGameTrack = None + if groupIndex >= self.__numRingGroups - 1: + + def endTheGame(self = self): + if not RingGameGlobals.ENDLESS_GAME: + taskMgr.doMethodLater(self.GAME_END_DELAY, self.__endGameDolater, self.END_GAME_WAIT_TASK) + + endGameTrack = Func(endTheGame) + if None != successTrack or None != endGameTrack: + track = Sequence() + if None != successTrack: + track.append(Wait(self.RING_RESPONSE_DELAY)) + track.append(successTrack) + if None != endGameTrack: + track.append(endGameTrack) + self.__ringTracks.append(track) + track.start() + return + + def setRingGroupResults(self, bitfield): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'swim': + return + results = [] + mask = 1 + for avId in self.avIdList: + results.append(not bitfield & mask) + mask <<= 1 + + self.__processRingGroupResults(results) diff --git a/toontown/minigame/DistributedRingGameAI.py b/toontown/minigame/DistributedRingGameAI.py new file mode 100755 index 00000000..2be9739f --- /dev/null +++ b/toontown/minigame/DistributedRingGameAI.py @@ -0,0 +1,146 @@ +from DistributedMinigameAI import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import RingGameGlobals +import random +import types + +class DistributedRingGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedRingGameAI_initialized + except: + self.DistributedRingGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedRingGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['swimming']), State.State('swimming', self.enterSwimming, self.exitSwimming, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.__timeBase = globalClockDelta.localToNetworkTime(globalClock.getRealTime()) + self.selectColorIndices() + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('swimming') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def getTimeBase(self): + return self.__timeBase + + def getColorIndices(self): + return self.colorIndices + + def selectColorIndices(self): + self.colorIndices = [None, + None, + None, + None] + chooseFrom = RingGameGlobals.ringColorSelection[:] + for i in xrange(0, 4): + c = random.choice(chooseFrom) + chooseFrom.remove(c) + if isinstance(c, types.TupleType): + c = random.choice(c) + self.colorIndices[i] = c + + return + + def enterSwimming(self): + self.notify.debug('enterSwimming') + self.__nextRingGroup = {} + for avId in self.avIdList: + self.__nextRingGroup[avId] = 0 + + self.__numRingsPassed = [0] * RingGameGlobals.NUM_RING_GROUPS + self.__ringResultBitfield = [0] * RingGameGlobals.NUM_RING_GROUPS + self.perfectGames = {} + for avId in self.avIdList: + self.perfectGames[avId] = 1 + + for avId in self.avIdList: + self.scoreDict[avId] = 0 + + def exitSwimming(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def setToonGotRing(self, success): + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'RingGameAI.setToonGotRing: invalid avId') + return + if self.gameFSM.getCurrentState() is None or self.gameFSM.getCurrentState().getName() != 'swimming': + self.air.writeServerEvent('suspicious', avId, 'RingGameAI.setToonGotRing: game not in swimming state') + return + ringGroupIndex = self.__nextRingGroup[avId] + if ringGroupIndex >= RingGameGlobals.NUM_RING_GROUPS: + self.notify.warning('warning: got extra ToonGotRing msg from av %s' % avId) + return + self.__nextRingGroup[avId] += 1 + if not success: + self.__ringResultBitfield[ringGroupIndex] |= 1 << self.avIdList.index(avId) + self.perfectGames[avId] = 0 + else: + self.scoreDict[avId] += 1 + self.__numRingsPassed[ringGroupIndex] += 1 + if self.__numRingsPassed[ringGroupIndex] >= self.numPlayers: + if not self.isSinglePlayer(): + bitfield = self.__ringResultBitfield[ringGroupIndex] + if bitfield == 0: + for id in self.avIdList: + self.scoreDict[id] += 0.5 + + self.sendUpdate('setRingGroupResults', [bitfield]) + if ringGroupIndex >= RingGameGlobals.NUM_RING_GROUPS - 1: + perfectBonuses = {1: 5, + 2: 5, + 3: 10, + 4: 18} + numPerfectToons = 0 + for avId in self.avIdList: + if self.perfectGames[avId]: + numPerfectToons += 1 + + for avId in self.avIdList: + if self.perfectGames[avId]: + self.scoreDict[avId] += perfectBonuses[numPerfectToons] + self.logPerfectGame(avId) + + for avId in self.avIdList: + self.scoreDict[avId] = max(1, self.scoreDict[avId]) + + if not RingGameGlobals.ENDLESS_GAME: + self.gameOver() + return diff --git a/toontown/minigame/DistributedTagGame.py b/toontown/minigame/DistributedTagGame.py new file mode 100755 index 00000000..83495aa1 --- /dev/null +++ b/toontown/minigame/DistributedTagGame.py @@ -0,0 +1,299 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from DistributedMinigame import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.safezone import Walk, SZUtil +from toontown.toonbase import ToontownTimer +from direct.gui import OnscreenText +import MinigameAvatarScorePanel +from direct.distributed import DistributedSmoothNode +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPGlobals +import TagGameGlobals +import Trajectory + +class DistributedTagGame(DistributedMinigame): + DURATION = TagGameGlobals.DURATION + IT_SPEED_INCREASE = 1.3 + IT_ROT_INCREASE = 1.3 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTagGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['off'])], 'off', 'off') + self.addChildGameFSM(self.gameFSM) + self.walkStateData = Walk.Walk('walkDone') + self.scorePanels = [] + base.localAvatar.isIt = 0 + self.modelCount = 4 + + def getTitle(self): + return TTLocalizer.TagGameTitle + + def getInstructions(self): + return TTLocalizer.TagGameInstructions + + def getMaxDuration(self): + return self.DURATION + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.itText = OnscreenText.OnscreenText('itText', fg=(0.95, 0.95, 0.65, 1), scale=0.14, font=ToontownGlobals.getSignFont(), pos=(0.0, -0.8), wordwrap=15, mayChange=1) + self.itText.hide() + safezoneId = self.getSafezoneId() + self.sky = loader.loadModel(TagGameGlobals.getSky(safezoneId)) + self.ground = loader.loadModel(TagGameGlobals.getGround(safezoneId)) + self.music = base.loadMusic('phase_4/audio/bgm/MG_toontag.ogg') + self.tagSfx = base.loadSfx('phase_4/audio/sfx/MG_Tag_C.ogg') + self.itPointer = loader.loadModel('phase_4/models/minigames/bboard-pointer') + self.tracks = [] + self.initialPositions = TagGameGlobals.getDropPoints(safezoneId) + self.IT = None + + if TagGameGlobals.isSnowHood(safezoneId): + self.snow, self.snowRender = SZUtil.createSnow(self.ground) + + return + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.ignoreAll() + del self.tracks + del self.IT + self.sky.removeNode() + del self.sky + self.itPointer.removeNode() + del self.itPointer + self.ground.removeNode() + del self.ground + del self.music + del self.tagSfx + self.itText.cleanup() + del self.itText + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + self.destroySnow() + + def destroySnow(self): + if hasattr(self, 'snow'): + self.snow.cleanup() + self.snowRender.removeNode() + del self.snow + del self.snowRender + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.ground.reparentTo(render) + self.sky.reparentTo(render) + myPos = self.avIdList.index(self.localAvId) + base.localAvatar.setPosHpr(*self.initialPositions[myPos]) + base.localAvatar.reparentTo(render) + base.localAvatar.loop('neutral') + camera.reparentTo(render) + camera.setPosHpr(0, -24, 16, 0, -30, 0) + base.camLens.setFar(450.0) + base.transitions.irisIn(0.4) + NametagGlobals.setMasterArrowsOn(1) + DistributedSmoothNode.activateSmoothing(1, 1) + self.IT = None + + if hasattr(self, 'snow'): + self.snow.start(camera, self.snowRender) + + return + + def offstage(self): + self.notify.debug('offstage') + DistributedSmoothNode.activateSmoothing(1, 0) + NametagGlobals.setMasterArrowsOn(0) + DistributedMinigame.offstage(self) + self.sky.reparentTo(hidden) + self.ground.reparentTo(hidden) + base.camLens.setFar(ToontownGlobals.DefaultCameraFar) + self.itText.hide() + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + for avId in self.avIdList: + self.acceptTagEvent(avId) + + myPos = self.avIdList.index(self.localAvId) + for i in xrange(self.numPlayers): + avId = self.avIdList[i] + avatar = self.getAvatar(avId) + if avatar: + avatar.startSmooth() + + base.localAvatar.setPosHpr(*self.initialPositions[myPos]) + base.localAvatar.d_clearSmoothing() + base.localAvatar.sendCurrentPosition() + base.localAvatar.b_setAnimState('neutral', 1) + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + for i in xrange(self.numPlayers): + avId = self.avIdList[i] + avName = self.getAvatarName(avId) + scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) + scorePanel.setPos(-0.213, 0.0, 0.28 * i + 0.66) + scorePanel.reparentTo(base.a2dBottomRight) + self.scorePanels.append(scorePanel) + + base.setCellsAvailable(base.rightCells, 0) + self.walkStateData.enter() + self.walkStateData.fsm.request('walking') + if base.localAvatar.isIt: + base.mouseInterfaceNode.setForwardSpeed(ToontownGlobals.ToonForwardSpeed * self.IT_SPEED_INCREASE) + base.mouseInterfaceNode.setRotateSpeed(ToontownGlobals.ToonRotateSpeed * self.IT_ROT_INCREASE) + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(self.DURATION) + self.timer.countdown(self.DURATION, self.timerExpired) + base.playMusic(self.music, looping=1, volume=0.9) + base.localAvatar.setIdealCameraPos(Point3(0, -24, 8)) + + def exitPlay(self): + for task in self.tracks: + task.finish() + + self.tracks = [] + for avId in self.avIdList: + toon = self.getAvatar(avId) + if toon: + toon.getGeomNode().clearMat() + toon.scale = 1.0 + toon.rescaleToon() + + self.walkStateData.exit() + self.music.stop() + self.timer.destroy() + del self.timer + for panel in self.scorePanels: + panel.cleanup() + + self.scorePanels = [] + base.setCellsAvailable(base.rightCells, 1) + base.mouseInterfaceNode.setForwardSpeed(ToontownGlobals.ToonForwardSpeed) + base.mouseInterfaceNode.setRotateSpeed(ToontownGlobals.ToonRotateSpeed) + self.itPointer.reparentTo(hidden) + base.localAvatar.cameraIndex = 0 + base.localAvatar.setCameraPositionByIndex(0) + + def timerExpired(self): + self.notify.debug('local timer expired') + self.gameOver() + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('off') + + def exitCleanup(self): + pass + + def setIt(self, avId): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'play': + self.notify.debug('Ignoring setIt after done playing') + return + self.itText.show() + self.notify.debug(str(avId) + ' is now it') + if avId == self.localAvId: + self.itText.setText(TTLocalizer.TagGameYouAreIt) + base.localAvatar.isIt = 1 + base.localAvatar.controlManager.setSpeeds(OTPGlobals.ToonForwardSpeed * self.IT_SPEED_INCREASE, OTPGlobals.ToonJumpForce, OTPGlobals.ToonReverseSpeed * self.IT_SPEED_INCREASE, OTPGlobals.ToonRotateSpeed * self.IT_ROT_INCREASE) + else: + self.itText.setText(TTLocalizer.TagGameSomeoneElseIsIt % self.getAvatarName(avId)) + base.localAvatar.isIt = 0 + base.localAvatar.setWalkSpeedNormal() + avatar = self.getAvatar(avId) + if avatar: + self.itPointer.reparentTo(avatar) + self.itPointer.setZ(avatar.getHeight()) + base.playSfx(self.tagSfx) + toon = self.getAvatar(avId) + duration = 0.6 + if not toon: + return + spinTrack = LerpHprInterval(toon.getGeomNode(), duration, Point3(0, 0, 0), startHpr=Point3(-5.0 * 360.0, 0, 0), blendType='easeOut') + growTrack = Parallel() + gs = 2.5 + for hi in xrange(toon.headParts.getNumPaths()): + head = toon.headParts[hi] + growTrack.append(LerpScaleInterval(head, duration, Point3(gs, gs, gs))) + + def bounceFunc(t, trajectory, node = toon.getGeomNode()): + node.setZ(trajectory.calcZ(t)) + + def bounceCleanupFunc(node = toon.getGeomNode(), z = toon.getGeomNode().getZ()): + node.setZ(z) + + bounceTrack = Sequence() + startZ = toon.getGeomNode().getZ() + tLen = 0 + zVel = 30 + decay = 0.6 + while tLen < duration: + trajectory = Trajectory.Trajectory(0, Point3(0, 0, startZ), Point3(0, 0, zVel), gravMult=5.0) + dur = trajectory.calcTimeOfImpactOnPlane(startZ) + if dur <= 0: + break + bounceTrack.append(LerpFunctionInterval(bounceFunc, fromData=0.0, toData=dur, duration=dur, extraArgs=[trajectory])) + tLen += dur + zVel *= decay + + bounceTrack.append(Func(bounceCleanupFunc)) + tagTrack = Sequence(Func(toon.animFSM.request, 'off'), Parallel(spinTrack, growTrack, bounceTrack), Func(toon.animFSM.request, 'Happy')) + self.tracks.append(tagTrack) + tagTrack.start() + if self.IT: + it = self.getAvatar(self.IT) + shrinkTrack = Parallel() + for hi in xrange(it.headParts.getNumPaths()): + head = it.headParts[hi] + scale = ToontownGlobals.toonHeadScales[it.style.getAnimal()] + shrinkTrack.append(LerpScaleInterval(head, duration, scale)) + + self.tracks.append(shrinkTrack) + shrinkTrack.start() + self.IT = avId + + def acceptTagEvent(self, avId): + self.accept('enterdistAvatarCollNode-' + str(avId), self.sendTagIfIt, [avId]) + + def sendTagIfIt(self, avId, collisionEntry): + if base.localAvatar.isIt: + self.notify.debug('Tagging ' + str(avId)) + self.sendUpdate('tag', [avId]) + else: + self.notify.debug('Bumped ' + str(avId)) + + def setTreasureScore(self, scores): + if not self.hasLocalToon: + return + self.notify.debug('setTreasureScore: %s' % scores) + for i in xrange(len(self.scorePanels)): + self.scorePanels[i].setScore(scores[i]) diff --git a/toontown/minigame/DistributedTagGameAI.py b/toontown/minigame/DistributedTagGameAI.py new file mode 100755 index 00000000..47c36487 --- /dev/null +++ b/toontown/minigame/DistributedTagGameAI.py @@ -0,0 +1,138 @@ +from DistributedMinigameAI import * +from TagTreasurePlannerAI import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +from toontown.safezone import TreasureGlobals +import random +import TagGameGlobals + +class DistributedTagGameAI(DistributedMinigameAI): + DURATION = TagGameGlobals.DURATION + + def __init__(self, air, minigameId): + try: + self.DistributedTagGameAI_initialized + except: + self.DistributedTagGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTagGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.treasureScores = {} + self.itAvId = None + self.healAmount = 3 + self.tagBack = 1 + + return + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + for avId in self.avIdList: + self.treasureScores[avId] = 0 + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.b_setIt(random.choice(self.avIdList)) + taskMgr.doMethodLater(self.DURATION, self.timerExpired, self.taskName('gameTimer')) + + safezoneId = self.getSafezoneId() + + if safezoneId in TreasureGlobals.SafeZoneTreasureSpawns: + treasureType, self.healAmount, spawnPoints, spawnRate, maxTreasures = TreasureGlobals.SafeZoneTreasureSpawns[safezoneId] + else: + treasureType, self.healAmount = TreasureGlobals.TreasureTT, 3 + + self.tagTreasurePlanner = TagTreasurePlannerAI(self.zoneId, self, self.treasureGrabCallback, treasureType, TagGameGlobals.getTreasurePoints(safezoneId)) + self.tagTreasurePlanner.start() + + def timerExpired(self, task): + self.notify.debug('timer expired') + self.gameOver() + return Task.done + + def exitPlay(self): + taskMgr.remove(self.taskName('gameTimer')) + taskMgr.remove(self.taskName('tagBack')) + taskMgr.remove(self.taskName('clearTagBack')) + self.tagTreasurePlanner.stop() + self.tagTreasurePlanner.deleteAllTreasuresNow() + del self.tagTreasurePlanner + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def treasureGrabCallback(self, avId): + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'TagGameAI.treasureGrabCallback non-player avId') + return + self.treasureScores[avId] += self.healAmount + self.notify.debug('treasureGrabCallback: ' + str(avId) + ' grabbed a treasure, new score: ' + str(self.treasureScores[avId])) + self.scoreDict[avId] = self.treasureScores[avId] + treasureScoreParams = [] + for avId in self.avIdList: + treasureScoreParams.append(self.treasureScores[avId]) + + self.sendUpdate('setTreasureScore', [treasureScoreParams]) + + def clearTagBack(self, task): + self.tagBack = 1 + return Task.done + + def tag(self, taggedAvId): + taggedAvatar = simbase.air.doId2do.get(taggedAvId) + if taggedAvatar == None: + self.air.writeServerEvent('suspicious', taggedAvId, 'TagGameAI.tag invalid taggedAvId') + return + itAvId = self.air.getAvatarIdFromSender() + if self.tagBack: + self.notify.debug('tag: ' + str(itAvId) + ' tagged: ' + str(taggedAvId)) + if self.itAvId == itAvId: + self.b_setIt(taggedAvId) + else: + self.notify.warning('Got tag message from avatar that is not IT') + return + self.tagBack = 0 + taskMgr.doMethodLater(2.0, self.clearTagBack, self.taskName('clearTagBack')) + return + + def b_setIt(self, avId): + self.d_setIt(avId) + self.setIt(avId) + + def d_setIt(self, avId): + self.sendUpdate('setIt', [avId]) + + def setIt(self, avId): + self.itAvId = avId diff --git a/toontown/minigame/DistributedTargetGame.py b/toontown/minigame/DistributedTargetGame.py new file mode 100755 index 00000000..dc6d522b --- /dev/null +++ b/toontown/minigame/DistributedTargetGame.py @@ -0,0 +1,1554 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from DistributedMinigame import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +import ArrowKeys +import TargetGameGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import math +from math import * +import random +import random +import RubberBand +import FogOverlay + +def circleX(angle, radius, centerX, centerY): + x = radius * cos(angle) + centerX + return x + + +def circleY(angle, radius, centerX, centerY): + y = radius * sin(angle) + centerY + return y + + +def getCirclePoints(segCount, centerX, centerY, radius, wideX = 1.0, wideY = 1.0): + returnShape = [] + for seg in xrange(0, int(segCount)): + coordX = wideX * circleX(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + + coordX = wideX * circleX(pi * 2.0 * float(0 / segCount), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(0 / segCount), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + return returnShape + + +def getRingPoints(segCount, centerX, centerY, radius, thickness = 2.0, wideX = 1.0, wideY = 1.0): + returnShape = [] + for seg in xrange(0, segCount): + coordX = wideX * circleX(pi * 2.0 * float(float(seg) / float(segCount)), radius - thickness, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(float(seg) / float(segCount)), radius - thickness, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + coordX = wideX * circleX(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + + return returnShape + + +def addRing(attachNode, color, vertexCount, radius, layer = 0, thickness = 1.0): + targetGN = GeomNode('target Circle') + zFloat = 0.025 + targetCircleShape = getRingPoints(5 + vertexCount, 0.0, 0.0, radius, thickness) + gFormat = GeomVertexFormat.getV3cp() + targetCircleVertexData = GeomVertexData('holds my vertices', gFormat, Geom.UHDynamic) + targetCircleVertexWriter = GeomVertexWriter(targetCircleVertexData, 'vertex') + targetCircleColorWriter = GeomVertexWriter(targetCircleVertexData, 'color') + for vertex in targetCircleShape: + targetCircleVertexWriter.addData3f(0.0 + vertex[0], 0.0 + vertex[1], zFloat) + targetCircleColorWriter.addData4f(1.0, 1.0, 1.0, 1.0) + + targetTris = GeomTristrips(Geom.UHStatic) + sizeTarget = len(targetCircleShape) + for countVertex in xrange(0, sizeTarget): + targetTris.addVertex(countVertex) + + targetTris.addVertex(0) + targetTris.addVertex(1) + targetTris.closePrimitive() + targetGeom = Geom(targetCircleVertexData) + targetGeom.addPrimitive(targetTris) + attachNode.addGeom(targetGeom) + return targetGeom + + +def addCircle(attachNode, color, vertexCount, radius, layer = 0): + targetGN = GeomNode('target Circle') + zFloat = 0.025 + targetCircleShape = getCirclePoints(5 + vertexCount, 0.0, 0.0, radius) + gFormat = GeomVertexFormat.getV3cp() + targetCircleVertexData = GeomVertexData('holds my vertices', gFormat, Geom.UHDynamic) + targetCircleVertexWriter = GeomVertexWriter(targetCircleVertexData, 'vertex') + targetCircleColorWriter = GeomVertexWriter(targetCircleVertexData, 'color') + targetCircleVertexWriter.addData3f(0.0, 0.0, zFloat) + targetCircleColorWriter.addData4f(1.0, 1.0, 1.0, 1.0) + for vertex in targetCircleShape: + targetCircleVertexWriter.addData3f(0.0 + vertex[0], 0.0 + vertex[1], zFloat) + targetCircleColorWriter.addData4f(1.0, 1.0, 1.0, 1.0) + + targetTris = GeomTrifans(Geom.UHStatic) + sizeTarget = len(targetCircleShape) + targetTris.addVertex(0) + for countVertex in xrange(1, sizeTarget + 1): + targetTris.addVertex(countVertex) + + targetTris.addVertex(1) + targetTris.closePrimitive() + targetGeom = Geom(targetCircleVertexData) + targetGeom.addPrimitive(targetTris) + attachNode.addGeom(targetGeom) + return targetGeom + + +def checkPlace(placeX, placeY, fillSize, placeList): + goodPlacement = 1 + for place in placeList: + distance = math.sqrt((place[0] - placeX) * (place[0] - placeX) + (place[1] - placeY) * (place[1] - placeY)) + distance = distance - (fillSize + place[2]) + if distance <= 0.0: + goodPlacement = 0 + break + + return goodPlacement + + +class DistributedTargetGame(DistributedMinigame): + UPDATE_ENVIRON_TASK = 'TargetGameUpdateEnvironTask' + UPDATE_LOCALTOON_TASK = 'TargetGameUpdateLocalToonTask' + UPDATE_SHADOWS_TASK = 'TargetGameUpdateShadowsTask' + COLLISION_DETECTION_TASK = 'TargetGameCollisionDetectionTask' + END_GAME_WAIT_TASK = 'TargetGameCollisionDetectionTask' + UPDATE_POWERBAR_TASK = 'TargetGameUpdatePowerBarTask' + GAME_DONE_TASK = 'gameDoneTask' + UPDATE_COUNTDOWN_TASK = 'countdown task' + FLY2FALL_CAM_TASK = 'fly2FallCameraTask' + PRELAUNCH_CAM_TASK = 'prelaunchCameraTask' + SCORE_CAM_TASK = 'scoreCameraTask' + TOONSTRETCHTASK = 'tooncamtask' + UPDATE_LOCAL_TOON_PRIORITY = 0 + UPDATE_POWER_BAR_PRIORITY = 1 + UPDATE_RUBBER_BAND_PRIORITY = 2 + COLLISION_DETECTION_PRIORITY = 5 + UPDATE_SHADOWS_PRIORITY = 47 + UMBRELLA_TEXTURE_LIST = ['phase_4/maps/mg_slingshot_umbrella_blue.jpg', + 'phase_4/maps/mg_slingshot_umbrella_purple.jpg', + 'phase_4/maps/mg_slingshot_umbrella_red.jpg', + 'phase_4/maps/mg_slingshot_umbrella_yellow.jpg'] + RT_UNKNOWN = 0 + RT_SUCCESS = 1 + RT_GROUPSUCCESS = 2 + RT_FAILURE = 3 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTargetGame', [State.State('off', self.enterOff, self.exitOff, ['prelaunch']), + State.State('prelaunch', self.enterPrelaunch, self.exitPrelaunch, ['launch']), + State.State('launch', self.enterLaunch, self.exitLaunch, ['fly']), + State.State('fly', self.enterFly, self.exitFly, ['fall', 'bouncing', 'score']), + State.State('fall', self.enterFall, self.exitFall, ['bouncing', 'score']), + State.State('bouncing', self.enterBouncing, self.exitBouncing, ['score']), + State.State('score', self.enterScore, self.exitScore, ['cleanup', 'prelaunch']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.targets = None + self.round = 1 + self.signalLaunch = 0 + self.stretchX = 0 + self.stretchY = 0 + self.targetsPlaced = [] + self.pattern = None + self.maxDist = 50 + self.targetMarker = None + self.umbrellaColorSelect = [1, + 1, + 1, + 1] + self.localTrack = None + self.hitInfo = None + return + + def getTitle(self): + return TTLocalizer.TargetGameTitle + + def getInstructions(self): + p = self.avIdList.index(self.localAvId) + if self.isSinglePlayer(): + text = TTLocalizer.TargetGameInstructionsSinglePlayer + else: + text = TTLocalizer.TargetGameInstructionsMultiPlayer + return text + + def getMaxDuration(self): + return 60 + + def defineConstants(self): + self.CAMERA_Y = -25 + self.TOON_Y = 0 + self.FAR_PLANE_DIST = 370 + tScreenCenterToEdge = 1.0 + self.TOONXZ_SPEED = TargetGameGlobals.MAX_TOONXZ / tScreenCenterToEdge + self.WATER_DEPTH = 75.0 + self.ENVIRON_LENGTH = TargetGameGlobals.ENVIRON_LENGTH + self.ENVIRON_WIDTH = TargetGameGlobals.ENVIRON_WIDTH + self.ENVIRON_START_OFFSET = 20.0 + self.TOON_INITIAL_SPACING = 14.0 + waterZOffset = 28.0 + self.SEA_FLOOR_Z = -self.WATER_DEPTH / 2.0 + waterZOffset + self.FOGDISTGROUND = 600 + self.FOGDISTCLOUD = 200 + farPlaneDist = self.CAMERA_Y + self.FAR_PLANE_DIST - self.TOON_Y + self.ringGroupArrivalPeriod = 3.0 + self.AMB_COLOR = Vec4(0.75, 0.8, 1.0, 1.0) + self.CLOUD_COLOR = Vec4(1.0, 1.0, 1.0, 1.0) + self.SHADOW_Z_OFFSET = 0.1 + self.Y_VIS_MAX = self.FAR_PLANE_DIST + self.Y_VIS_MIN = self.CAMERA_Y + targetRadius = TargetGameGlobals.TARGET_RADIUS * 1.025 + self.GAME_END_DELAY = 1.0 + self.TOON_LOD = 1000 + self.speedLaunch = 60 + self.speedStall = 30 + self.speedForward = 0 + self.airResistance = 0.1 + self.umbrellaResistance = 0.11 + self.distance = 0 + self.zVel = 0 + self.lastPressed = None + self.lastPressTime = None + self.driftX = 0 + self.driftY = 0 + self.launchLaterial = 0 + self.laterial = 0 + self.gravity = 12.5 + self.inList = [] + self.placeShift = 10 + self.startPower = 10 + self.canPressRight = 1 + self.canPressLeft = 1 + self.jumpCount = 0 + self.trampZ = 3.6 + self.trampBounceCount = 0 + self.cameraState = 'Low' + self.updateTick = 0 + self.hitInfo = None + return + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.defineConstants() + self.help = DirectLabel(parent=aspect2d, relief=None, pos=(-0.0, 0, -0.8), text=TTLocalizer.TargetGameCountdown, text_scale=0.08, text_fg=(1, 1, 1, 1)) + self.help.hide() + self.fogOver = FogOverlay.FogOverlay(Point3(self.CLOUD_COLOR[0], self.CLOUD_COLOR[1], self.CLOUD_COLOR[2])) + self.scoreboard = DirectFrame(parent=aspect2d, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.75, 1, 0.75), pos=(0, 0, 0.587)) + self.roundLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(0, 0, 0.28), text=TTLocalizer.TargetGameBoard % self.round, text_scale=0.08) + av1Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.8, 0, 0.14), text_align=TextNode.ALeft, text=' ', text_scale=0.08) + av2Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.8, 0, 0.02), text_align=TextNode.ALeft, text=' ', text_scale=0.08) + av3Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.8, 0, -0.1), text_align=TextNode.ALeft, text=' ', text_scale=0.08) + av4Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.8, 0, -0.22), text_align=TextNode.ALeft, text=' ', text_scale=0.08) + score1Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(0.8, 0, 0.14), text_align=TextNode.ARight, text=' ', text_scale=0.08) + score2Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(0.8, 0, 0.02), text_align=TextNode.ARight, text=' ', text_scale=0.08) + score3Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(0.8, 0, -0.1), text_align=TextNode.ARight, text=' ', text_scale=0.08) + score4Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(0.8, 0, -0.22), text_align=TextNode.ARight, text=' ', text_scale=0.08) + self.avLabels = [av1Label, + av2Label, + av3Label, + av4Label] + self.scoreLabels = [score1Label, + score2Label, + score3Label, + score4Label] + self.scoreboard.hide() + self.music = base.loadMusic('phase_4/audio/bgm/MG_Diving.ogg') + self.sndAmbience = None + self.skyListLow = [] + self.skyListMid = [] + loadBase = 'phase_4/models/minigames/' + self.modelName = 'slingshot_game_desert4.bam' + self.trampoline = loader.loadModel('phase_4/models/minigames/slingshot_game_tramp.bam') + self.trampoline.setBin('ground', 100) + self.trampoline.find('**/tramp_shadow').hide() + self.cacti = loader.loadModel('phase_4/models/minigames/slingshot_game_cacti.bam') + self.cactusA = self.cacti.find('**/cactus_a') + self.cactusB = self.cacti.find('**/cactus_b') + self.findGround = '**/sand_layer1' + self.findSky = '**/blue_sky_layer1' + self.findCloudExtra = '**/cloud_layer1' + self.findCloud = '**/cloud_layer_alpha' + self.findLeftMount = '**/mountain_set_b' + self.findRightMount = '**/mountain_c' + self.findBack = '**/backdrop_layer1' + self.environModel = loader.loadModel(loadBase + self.modelName) + self.skyGN = GeomNode('sky Geometry') + self.skyNode1 = self.environModel.attachNewNode(self.skyGN) + leftMountModel = self.environModel.find(self.findLeftMount) + rightMountModel = self.environModel.find(self.findRightMount) + wrongCloud = self.environModel.find(self.findCloudExtra) + wrongCloud.hide() + backModel = self.environModel.find(self.findBack) + backModel.hide() + leftMountModel.hide() + rightMountModel.hide() + groundModel = self.environModel.find(self.findGround) + groundModel.setBin('ground', 0) + groundModel.setZ(-0.05) + skyModel = self.environModel.find(self.findSky) + skyModel2 = self.environModel.find(self.findCloud) + skyModel3 = self.environModel.attachNewNode('skyLow') + skyModel2.copyTo(skyModel3) + skyModel.setZ(76.0) + skyModel.setColorScale(1.0, 1.0, 1.0, 1.0) + skyModel.setBin('ground', 2) + skyModel2.setZ(63.0) + skyModel2.setDepthWrite(0) + skyModel2.setTransparency(1) + skyModel2.setColorScale(1.0, 1.0, 1.0, 1.0) + skyModel2.setTwoSided(True) + skyModel2.setBin('fixed', 3) + skyModel2.setR(180) + skyModel3.setZ(50.0) + skyModel3.setDepthWrite(0) + skyModel3.setTransparency(1) + skyModel3.setColorScale(1.0, 1.0, 1.0, 1.0) + skyModel3.setTwoSided(True) + skyModel3.setBin('fixed', 4) + self.skyListLow.append(skyModel3) + self.lowSky = skyModel3 + self.environModel.setPos(0, -5.0, 0) + self.environModel.flattenMedium() + self.environModel.setScale(15.0, 15.0, 2.0) + self.dropShadowModel = loader.loadModel('phase_3/models/props/drop_shadow') + self.dropShadowModel.setColor(0, 0, 0, 0.5) + self.dropShadowModel.flattenMedium() + self.toonDropShadows = [] + self.__textGen = TextNode('targetGame') + self.__textGen.setFont(ToontownGlobals.getSignFont()) + self.__textGen.setAlign(TextNode.ACenter) + self.powerBar = DirectWaitBar(guiId='launch power bar', pos=(0.0, 0, -0.65), relief=DGG.SUNKEN, frameSize=(-2.0, + 2.0, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=0.25, range=100, sortOrder=50, frameColor=(0.5, 0.5, 0.5, 0.5), barColor=(1.0, 0.0, 0.0, 1.0), text='0%', text_scale=0.26, text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05)) + self.power = self.startPower + self.powerBar['value'] = self.power + self.powerBar.hide() + self.rubberBands = [] + self.umbrella = loader.loadModel('phase_4/models/minigames/slingshot_game_umbrellas.bam') + pick = random.randint(0, 3) + self.umbrellaColorSelect[pick] = 0 + tex = loader.loadTexture(DistributedTargetGame.UMBRELLA_TEXTURE_LIST[pick]) + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + self.umbrella.setTexture(tex, 1) + open = self.umbrella.find('**/open_umbrella') + open.hide() + open.setPos(0.1, 0.2, -0.1) + open.setHpr(-90.0, 0.0, 15.0) + closed = self.umbrella.find('**/closed_umbrella') + closed.show() + hands = localAvatar.getRightHands() + ce = CompassEffect.make(NodePath(), CompassEffect.PRot) + closed.node().setEffect(ce) + closed.setHpr(0.0, 100.0, 35.0) + for hand in hands: + self.umbrella.instanceTo(hand) + + self.remoteUmbrellas = {} + self.addSound('wind1', 'target_cloud.ogg', 'phase_4/audio/sfx/') + self.addSound('trampoline', 'target_trampoline_2.ogg', 'phase_4/audio/sfx/') + self.addSound('launch', 'target_launch.ogg', 'phase_4/audio/sfx/') + self.addSound('miss', 'target_Lose.ogg', 'phase_4/audio/sfx/') + self.addSound('score', 'target_happydance.ogg', 'phase_4/audio/sfx/') + self.addSound('impact', 'target_impact_grunt1.ogg', 'phase_4/audio/sfx/') + self.addSound('umbrella', 'target_chute.ogg', 'phase_4/audio/sfx/') + self.addSound('bounce', 'target_impact_only.ogg', 'phase_4/audio/sfx/') + self.flySound = loader.loadSfx('phase_4/audio/sfx/target_wind_fly_loop.ogg') + self.flySound.setVolume(0.0) + self.flySound.setPlayRate(1.0) + self.flySound.setLoop(True) + self.flySound.play() + self.rubberSound = loader.loadSfx('phase_4/audio/sfx/target_stretching_aim_loop.ogg') + self.rubberSound.setVolume(0.0) + self.rubberSound.setPlayRate(1.0) + self.rubberSound.setLoop(True) + self.rubberSound.play() + self.flutterSound = loader.loadSfx('phase_4/audio/sfx/target_wind_float_clothloop.ogg') + self.flutterSound.setVolume(1.0) + self.flutterSound.setPlayRate(1.0) + self.flutterSound.setLoop(True) + return + + def addSound(self, name, soundName, path = None): + if not hasattr(self, 'soundTable'): + self.soundTable = {} + if path: + self.soundPath = path + soundSource = '%s%s' % (self.soundPath, soundName) + self.soundTable[name] = loader.loadSfx(soundSource) + + def playSound(self, name, volume = 1.0): + if hasattr(self, 'soundTable'): + self.soundTable[name].setVolume(volume) + self.soundTable[name].play() + + def addSkys(self, instance): + low = instance.find('**/skyLow') + mid = instance.find(self.findCloud) + self.skyListLow.append(low) + self.skyListMid.append(mid) + + def setTargetSeed(self, targetSeed, extra = None): + if not self.hasLocalToon: + return + random.seed(targetSeed) + self.pattern = TargetGameGlobals.difficultyPatterns[self.getSafezoneId()] + print 'seed %s' % targetSeed + self.setupTargets() + + def setupTargets(self): + fieldWidth = self.ENVIRON_WIDTH * 3 + fieldLength = self.ENVIRON_LENGTH * 3.7 + self.targetSubParts = [2, + 2, + 2, + 1] + self.targetList = self.pattern[0] + self.targetValue = self.pattern[1] + self.targetSize = self.pattern[2] + self.targetColors = self.pattern[3] + self.targetSubParts = self.pattern[4] + highestValue = 0 + for value in self.targetValue: + if value > highestValue: + highestValue = value + + placeList = [] + self.jumpColor = TargetGameGlobals.colorBlack + self.placeValue = highestValue * 0.5 + self.jumpSize = self.pattern[5] + self.jumpNum = self.pattern[6] + self.accept('enterJump', self.jumpIn) + self.accept('exitJump', self.jumpOut) + self.targets = render.attachNewNode('targetGameTargets') + for typeIndex in xrange(len(self.targetList)): + for targetIndex in xrange(self.targetList[typeIndex]): + goodPlacement = 0 + while not goodPlacement: + placeX = random.random() * (fieldWidth * 0.6) - fieldWidth * 0.6 * 0.5 + placeY = (random.random() * 0.6 + (0.0 + 0.4 * (self.placeValue * 1.0 / (highestValue * 1.0)))) * fieldLength + fillSize = self.targetSize[typeIndex] + goodPlacement = checkPlace(placeX, placeY, fillSize, placeList) + + placeList.append((placeX, placeY, fillSize)) + subIndex = self.targetSubParts[typeIndex] + first = 1 + while subIndex: + combinedIndex = typeIndex + subIndex - 1 + targetGN = GeomNode('target Circle') + targetNodePathGeom = self.targets.attachNewNode(targetGN) + targetNodePathGeom.setPos(placeX, placeY, 0) + points = int(self.targetSize[combinedIndex] / 2) + 20 + order = len(self.targetList) - combinedIndex + 10 + if first: + first = 0 + geo = addCircle(targetGN, self.targetColors[combinedIndex], points, self.targetSize[combinedIndex], order) + else: + thickness = abs(self.targetSize[combinedIndex] - self.targetSize[combinedIndex + 1]) - 0.0 + geo = addRing(targetGN, self.targetColors[combinedIndex], points, self.targetSize[combinedIndex], order, thickness) + targetNodePathGeom.setBin('ground', combinedIndex * 2 + 1) + targetNodePathGeom.setColorScale(self.targetColors[combinedIndex]['Red'], self.targetColors[combinedIndex]['Green'], self.targetColors[combinedIndex]['Blue'], self.targetColors[combinedIndex]['Alpha']) + targetNodePathGeom.setDepthWrite(False) + targetNodePathGeom.setDepthTest(False) + targetNodePathGeom.setTransparency(TransparencyAttrib.MAlpha) + self.targetsPlaced.append((placeX, + placeY, + combinedIndex, + geo)) + subIndex -= 1 + + for jump in xrange(self.jumpNum): + normJumpSize = 6.8 + goodPlacement = 0 + while not goodPlacement: + placeX = random.random() * (fieldWidth * 0.6) - fieldWidth * 0.6 * 0.5 + placeY = (random.random() * 0.6 + (0.0 + 0.4 * (self.placeValue * 1.0 / (highestValue * 1.0)))) * fieldLength + fillSize = self.jumpSize + goodPlacement = checkPlace(placeX, placeY, fillSize, placeList) + + placeList.append((placeX, placeY, fillSize)) + target = CollisionSphere(0, 0, 0, self.jumpSize) + target.setTangible(0) + targetNode = CollisionNode('Jump') + targetNode.addSolid(target) + targetGN = GeomNode('target jump Circle') + targetNodePathGeom = self.targets.attachNewNode(targetGN) + trampI = self.trampoline.copyTo(self.targets) + trampI.setPos(placeX, placeY, 0.05) + latScale = self.jumpSize / normJumpSize + trampI.setScale(latScale, latScale, 1.0) + targetNodePath = self.targets.attachNewNode(targetNode) + targetNodePath.setPos(placeX, placeY, 0.0) + targetNodePathGeom.setPos(placeX, placeY, 0.05) + points = self.jumpSize / 2 + 20 + order = 0 + addCircle(targetGN, self.jumpColor, points, self.jumpSize + 0.5, order) + targetNodePathGeom.setColorScale(self.jumpColor['Red'], self.jumpColor['Green'], self.jumpColor['Blue'], 0.25) + targetNodePathGeom.setBin('ground', 20) + targetNodePathGeom.setDepthWrite(True) + targetNodePathGeom.setTransparency(TransparencyAttrib.MAlpha) + + cactusCount = 30 + for cactus in xrange(cactusCount): + placeX = random.random() * (fieldWidth * 0.75) - fieldWidth * 0.75 * 0.5 + placeY = 5.0 + random.random() * (fieldLength - 5.0) + targetGN = GeomNode('cactus') + cactus = random.choice([self.cactusA, self.cactusB]) + targetNodePathGeom = self.targets.attachNewNode(targetGN) + targetNodePathGeom = self.targets.attachNewNode(targetGN) + cactusI = cactus.copyTo(self.targets) + cactusI.setPos(placeX, placeY, 0) + cactusI.setHpr(random.random() * 360, 0, 0) + cactusI.setScale(0.5 + random.random()) + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + del self.__textGen + del self.toonDropShadows + self.dropShadowModel.removeNode() + del self.dropShadowModel + self.environModel.removeNode() + del self.environModel + self.fogOver.delete() + open = self.umbrella.find('**/open_umbrella') + open.hide() + closed = self.umbrella.find('**/closed_umbrella') + closed.hide() + self.umbrella.removeNode() + del self.umbrella + for umbrella in self.remoteUmbrellas: + self.remoteUmbrellas[umbrella].removeNode() + + self.remoteUmbrellas.clear() + del self.remoteUmbrellas + self.flySound.stop() + del self.flySound + self.rubberSound.stop() + del self.rubberSound + self.flutterSound.stop() + del self.flutterSound + del self.music + del self.sndAmbience + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + if self.targets: + self.targets.removeNode() + del self.targets + self.scoreboard.destroy() + del self.scoreboard + self.powerBar.destroy() + del self.powerBar + self.help.destroy() + del self.help + if self.localTrack: + self.localTrack.finish() + del self.localTrack + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.arrowKeys = ArrowKeys.ArrowKeys() + toon = base.localAvatar + toon.reparentTo(render) + toon.setAnimState('SitStart', 1.0) + toon.useLOD(self.TOON_LOD) + self.__placeToon(self.localAvId) + self.localStartPos = toon.getPos() + localBand = RubberBand.RubberBand(toon, Point3(0, -1.75, 0), taskPriority=self.UPDATE_RUBBER_BAND_PRIORITY) + tP = self.getToonPlace(self.localAvId) + pos = Point3(tP[0], tP[1] + 10, tP[2]) + localBand.setPos(pos) + self.rubberBands.append(localBand) + toon.dropShadow.hide() + toonPos = base.localAvatar.getPos() + camera.reparentTo(render) + toonPos = self.getAvatar(self.localAvId).getPos() + camera.setPosHpr(toonPos[0], toonPos[1] - 18, toonPos[2] + 10, 0, -15, 0) + base.camLens.setMinFov(80/(4./3.)) + base.camLens.setFar(self.FOGDISTGROUND) + base.setBackgroundColor(self.AMB_COLOR) + self.__fog = Fog('ringGameFog') + if base.wantFog: + self.__fog.setColor(self.AMB_COLOR) + self.__fog.setLinearRange(0.1, self.FOGDISTGROUND) + render.setFog(self.__fog) + self.environNode = render.attachNewNode('environNode') + self.environBlocks = [] + maxI = 4 + self.maxDist = (maxI - 1) * self.ENVIRON_LENGTH + for i in xrange(-1, maxI + 1): + instance = self.environModel.copyTo(self.environNode) + y = self.ENVIRON_LENGTH * i + instance.setY(y) + self.addSkys(instance) + self.environBlocks.append(instance) + jRange = 3 + for j in xrange(0, jRange): + instance = self.environModel.copyTo(self.environNode) + x = self.ENVIRON_WIDTH * (j + 1) + instance.setY(y) + instance.setX(-x) + height = random.random() * 5.0 + 5.0 + heading = random.random() * 15.0 - 7.5 + self.addSkys(instance) + self.environBlocks.append(instance) + if j == jRange - 2: + mount = instance.find(self.findLeftMount) + mount.setScale(1.0, 1.1, height) + mount.setBin('ground', 1) + mount.show() + sand = mount.find('**/sand_b') + sand.hide() + mount.setPos(mount.getX() + 8.5, 0, 0.0) + if i >= maxI - 1: + mount.setHpr(310, 0, 0) + mount.setScale(1.0, 1.5, height * 1.2) + mount.setPos(mount.getX() - 2.0, 0, 0.0) + if i == maxI: + mount.hide() + if i == -1: + mount.setHpr(50, 0, 0) + mount.setScale(1.0, 1.5, height * 1.2) + mount.setPos(mount.getX() + 5.0, 3.0, 0.0) + if j == jRange - 1: + sand = instance.find(self.findGround) + sand.hide() + + for j in xrange(0, jRange): + instance = self.environModel.copyTo(self.environNode) + x = self.ENVIRON_WIDTH * (j + 1) + instance.setY(y) + instance.setX(x) + height = random.random() * 5.0 + 5.0 + heading = random.random() * 15.0 - 7.5 + self.addSkys(instance) + self.environBlocks.append(instance) + if j == jRange - 2: + mount = instance.find(self.findRightMount) + mount.setScale(1.0, 1.1, height) + mount.setBin('ground', 1) + mount.show() + sand = mount.find('**/sand_c') + sand.hide() + mount.setPos(mount.getX() - 8.5, 0, 0.0) + if i >= maxI - 1: + mount.setHpr(50, 0, 0) + mount.setScale(1.0, 1.5, height * 1.2) + mount.setPos(mount.getX() + 2.0, 0, 0.0) + if i == maxI: + mount.hide() + if i == -1: + mount.setHpr(310, 0, 0) + mount.setScale(1.0, 1.5, height * 1.2) + mount.setPos(mount.getX() - 5.0, 3.0, 0.0) + if j == jRange - 1: + sand = instance.find(self.findGround) + sand.hide() + + self.__addToonDropShadow(self.getAvatar(self.localAvId)) + self.__spawnUpdateEnvironTask() + self.__spawnUpdateShadowsTask() + self.__spawnUpdateLocalToonTask() + if self.music: + base.playMusic(self.music, looping=1, volume=0.8) + if None != self.sndAmbience: + base.playSfx(self.sndAmbience, looping=1, volume=0.8) + return + + def reset(self): + toon = base.localAvatar + toon.setAnimState('SitStart', 1.0) + toon.useLOD(self.TOON_LOD) + self.__placeToon(self.localAvId) + toon.dropShadow.hide() + self.__spawnUpdateLocalToonTask() + toonPos = base.localAvatar.getPos() + camera.setPosHpr(toonPos[0], toonPos[1] - 26, toonPos[2] + 10, 0, -15, 0) + base.camLens.setMinFov(80/(4./3.)) + self.resetNums() + + def resetNums(self): + self.stretchX = 0 + self.stretchY = 0 + self.driftX = 0 + self.driftY = 0 + self.driftY = 0 + self.jumpCount = 0 + self.trampBounceCount = 0 + self.cameraState = 'Low' + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + if self.music: + self.music.stop() + if None != self.sndAmbience: + self.sndAmbience.stop() + self.__killUpdateLocalToonTask() + self.__killUpdateShadowsTask() + self.__killUpdateEnvironTask() + self.__killCountDownTask() + del self.soundTable + self.__removeAllToonDropShadows() + render.clearFog() + base.camLens.setFar(ToontownGlobals.DefaultCameraFar) + base.camLens.setMinFov(settings['fov']/(4./3.)) + camera.setHpr(0, 90, 0) + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + self.arrowKeys.destroy() + del self.arrowKeys + for block in self.environBlocks: + del block + + self.environNode.removeNode() + del self.environNode + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.dropShadow.show() + av.resetLOD() + + for band in self.rubberBands: + band.delete() + + rubberBands = [] + self.targetsPlaced = [] + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + self.__removeToonDropShadow(self.remoteToons[avId]) + DistributedMinigame.handleDisabledAvatar(self, avId) + + def __genText(self, text): + self.__textGen.setText(text) + return self.__textGen.generate() + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + i = self.avIdList.index(avId) + numToons = float(self.numPlayers) + x = i * self.TOON_INITIAL_SPACING + x -= self.TOON_INITIAL_SPACING * (numToons - 1) / 2.0 + pos = self.getToonPlace(avId) + toon.setPosHpr(pos[0], pos[1], pos[2], 0, 0, 0) + + def getToonPlace(self, avId): + toon = self.getAvatar(avId) + i = self.avIdList.index(avId) + numToons = float(self.numPlayers) + x = i * self.TOON_INITIAL_SPACING + x -= self.TOON_INITIAL_SPACING * (numToons - 1) / 2.0 + pos = Point3(x + self.placeShift, self.TOON_Y, 1.0) + return pos + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + if not self.isSinglePlayer(): + base.localAvatar.collisionsOff() + cSphere = CollisionSphere(0.0, 0.0, 0.0, TargetGameGlobals.CollisionRadius) + cSphereNode = CollisionNode('RingGameSphere-%s' % self.localAvId) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(TargetGameGlobals.CollideMask) + cSphereNode.setIntoCollideMask(BitMask32.allOff()) + self.cSphereNodePath = base.localAvatar.attachNewNode(cSphereNode) + self.pusher = CollisionHandlerPusher() + self.pusher.addCollider(self.cSphereNodePath, base.localAvatar) + self.pusher.setHorizontal(0) + self.cTrav = CollisionTraverser('DistributedRingGame') + self.cTrav.addCollider(self.cSphereNodePath, self.pusher) + self.remoteToonCollNPs = {} + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + cSphere = CollisionSphere(0.0, 0.0, 0.0, TargetGameGlobals.CollisionRadius) + cSphereNode = CollisionNode('RingGameSphere-%s' % avId) + cSphereNode.addSolid(cSphere) + cSphereNode.setCollideMask(TargetGameGlobals.CollideMask) + cSphereNP = toon.attachNewNode(cSphereNode) + self.remoteToonCollNPs[avId] = cSphereNP + + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toon.setAnimState('SitStart', 1.0) + toon.useLOD(self.TOON_LOD) + toon.dropShadow.hide() + self.__addToonDropShadow(toon) + band = RubberBand.RubberBand(toon, Point3(0, -1.75, 0), taskPriority=self.UPDATE_RUBBER_BAND_PRIORITY) + tP = self.getToonPlace(avId) + pos = Point3(tP[0], tP[1] + 10, tP[2]) + band.setPos(pos) + self.rubberBands.append(band) + umbrella = self.umbrella.copyTo(render) + self.remoteUmbrellas[avId] = umbrella + test = 0 + kill = 16 + while test == 0 and kill > 0: + kill -= 1 + pick = random.randint(0, 3) + if self.umbrellaColorSelect[pick]: + self.umbrellaColorSelect[pick] = 0 + test = 1 + + tex = loader.loadTexture(DistributedTargetGame.UMBRELLA_TEXTURE_LIST[pick]) + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + umbrella.setTexture(tex, 1) + open = umbrella.find('**/open_umbrella') + open.hide() + open.setPos(0.1, 0.2, -0.1) + open.setHpr(-90.0, 180.0, 0.0) + open.setScale(1.5) + closed = umbrella.find('**/closed_umbrella') + closed.show() + hands = toon.getRightHands() + hand = hands[0] + ce = CompassEffect.make(NodePath(), CompassEffect.PRot) + closed.node().setEffect(ce) + closed.setHpr(0.0, 90.0, 35.0) + umbrella.reparentTo(hand) + toon.startSmooth() + + self.remoteToons = {} + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + self.remoteToons[avId] = toon + + labelIndex = 0 + for avId in self.avIdList: + av = self.getAvatar(avId) + self.avLabels[labelIndex]['text'] = av.getName() + labelIndex += 1 + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('prelaunch') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPrelaunch(self): + self.defineConstants() + self.resetNums() + self.gameFSM.request('launch') + self.powerBar.show() + self.powerBar['value'] = self.startPower + camera.reparentTo(render) + self.__placeToon(self.localAvId) + toonPos = self.getAvatar(self.localAvId).getPos() + newPos = Point3(toonPos[0], toonPos[1] - 25, toonPos[2] + 7) + newHpr = Point3(0, -15, 0) + self.cameraWork = camera.posHprInterval(2.5, newPos, newHpr, blendType='easeInOut') + self.cameraWork.start() + base.camLens.setMinFov(80/(4./3.)) + self.stretchY = self.startPower + self.power = self.startPower + self.scoreboard.hide() + open = self.umbrella.find('**/open_umbrella') + open.hide() + closed = self.umbrella.find('**/closed_umbrella') + closed.show() + self.umbrella.setScale(1.0) + if self.targetMarker: + self.targetMarker.removeNode() + + def exitPrelaunch(self): + pass + + def enterLaunch(self): + self.__spawnUpdatePowerBarTask() + self.__spawnCountDownTask() + self.help.show() + self.help['text'] = TTLocalizer.TargetGameCountHelp + + def exitLaunch(self): + self.cameraWork.finish() + self.__killUpdatePowerBarTask() + self.speedLaunch = self.power + self.powerBar.hide() + self.playSound('launch') + + def enterFly(self): + self.notify.debug('enterFly') + self.__spawnCollisionDetectionTask() + self.__placeToon(self.localAvId) + self.speedForward = self.speedLaunch + self.laterial = -self.launchLaterial * 7 + self.zVel = self.speedForward * 0.5 + toon = base.localAvatar + toon.b_setAnimState('swim', 1.0) + toon.stopBobSwimTask() + toon.dropShadow.hide() + camera.reparentTo(base.localAvatar) + camera.setPosHpr(0, self.CAMERA_Y + self.TOON_Y + 12, 5, 0, -15, 0) + base.camLens.setMinFov(80/(4./3.)) + self.help.show() + self.help['text'] = TTLocalizer.TargetGameFlyHelp + self.rubberSound.setVolume(0.0) + self.rubberSound.setPlayRate(1.0) + self.rubberSound.stop() + + def exitFly(self): + taskMgr.remove(self.END_GAME_WAIT_TASK) + self.__killCollisionDetectionTask() + self.help.hide() + + def enterFall(self): + self.notify.debug('enterLaunch') + toon = base.localAvatar + toon.b_setAnimState('jumpAirborne', 1.0) + toon.dropShadow.hide() + self.speedForward = self.speedForward * 0.5 + self.gravity = 4 + newHpr = Point3(0, -68, 0) + newPos = Point3(0, self.CAMERA_Y + self.TOON_Y + 15, 15) + camera.posHprInterval(2.5, newPos, newHpr, blendType='easeInOut').start() + open = self.umbrella.find('**/open_umbrella') + open.show() + closed = self.umbrella.find('**/closed_umbrella') + closed.hide() + self.umbrella.setHpr(0, 180.0, -25.0) + self.umbrella.setScale(1.5) + self.help.show() + self.help['text'] = TTLocalizer.TargetGameFallHelp + self.playSound('umbrella') + self.flutterSound.play() + + def exitFall(self): + self.flutterSound.stop() + + def enterBouncing(self): + self.notify.debug('enterLaunch') + self.gravity = 12.5 + open = self.umbrella.find('**/open_umbrella') + open.hide() + closed = self.umbrella.find('**/closed_umbrella') + closed.hide() + self.umbrella.setHpr(0, 0, 0) + self.umbrella.setScale(1.0) + toon = base.localAvatar + toon.b_setAnimState('neutral', 1.0) + toon.dropShadow.hide() + self.help.show() + self.help['text'] = TTLocalizer.TargetGameBounceHelp + + def exitBouncing(self): + pass + + def enterScore(self): + + def count(value, list): + count = 0 + for item in list: + if value == item: + count += 1 + + return count + + self.notify.debug('enterScore') + self.__killUpdateLocalToonTask() + self.__spawnGameDoneTask() + self.scoreboard.show() + score = 1 + self.sendUpdate('setScore', [base.localAvatar.getX(), base.localAvatar.getY()]) + topValue = 0 + hitTarget = None + for target in self.targetsPlaced: + radius = self.targetSize[target[2]] + value = self.targetValue[target[2]] + posX = target[0] + posY = target[1] + dx = posX - base.localAvatar.getX() + dy = posY - base.localAvatar.getY() + distance = math.sqrt(dx * dx + dy * dy) + if distance < radius and topValue < value: + topValue = value + hitTarget = target + hitX = posX + hitY = posY + + if hitTarget: + targetGN = GeomNode('target Circle') + targetNodePathGeom = self.targets.attachNewNode(targetGN) + targetNodePathGeom.setPos(hitX, hitY, 0) + addRing(targetGN, TargetGameGlobals.colorBlack, 100, self.targetSize[hitTarget[2]] + 0.5, 0) + targetNodePathGeom.setColorScale(TargetGameGlobals.colorWhite['Red'], TargetGameGlobals.colorWhite['Green'], TargetGameGlobals.colorWhite['Blue'], TargetGameGlobals.colorWhite['Alpha']) + targetNodePathGeom.setBin('ground', hitTarget[2] * 2 + 100) + targetNodePathGeom.setDepthWrite(False) + targetNodePathGeom.setDepthTest(False) + targetNodePathGeom.setTransparency(TransparencyAttrib.MAlpha) + self.targetMarker = targetNodePathGeom + score = topValue + self.localTrack = Sequence() + if self.hitInfo == 'OnButt': + self.localTrack.append(ActorInterval(base.localAvatar, 'slip-forward', startFrame=25, endFrame=55)) + self.hitInfo = 'GettingUp' + else: + self.localTrack.append(ActorInterval(base.localAvatar, 'slip-forward', startFrame=50, endFrame=55)) + self.localTrack.append(Wait(0.5)) + if score <= 1: + self.localTrack.append(Parallel(Func(base.localAvatar.b_setAnimState, 'Sad', 1.0), Func(self.playSound, 'miss'))) + else: + self.localTrack.append(Parallel(Func(base.localAvatar.b_setAnimState, 'victory', 1.0), Func(self.playSound, 'score'))) + newHpr = Point3(180, 10, 0) + newPos = Point3(0, -(self.CAMERA_Y + self.TOON_Y + 12), 1) + camera.posHprInterval(5.0, newPos, newHpr, blendType='easeInOut').start() + self.help.hide() + self.localTrack.start() + return + + def exitScore(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + if not self.isSinglePlayer(): + for np in self.remoteToonCollNPs.values(): + np.removeNode() + + del self.remoteToonCollNPs + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + del self.pusher + del self.cTrav + base.localAvatar.collisionsOn() + + def exitCleanup(self): + pass + + def signalDone(self): + self.sendUpdate('setPlayerDone', []) + + def __spawnUpdatePowerBarTask(self): + taskMgr.remove(self.UPDATE_POWERBAR_TASK) + taskMgr.add(self.__updatePowerBarTask, self.UPDATE_POWERBAR_TASK, priority=self.UPDATE_POWER_BAR_PRIORITY) + self.rubberSound.play() + self.bandVolume = 0.0 + + def __killUpdatePowerBarTask(self): + taskMgr.remove(self.UPDATE_POWERBAR_TASK) + taskMgr.remove(self.TOONSTRETCHTASK) + + def __spawnCountDownTask(self): + self.countDown = 10 + self.signalLaunch = 0 + taskMgr.remove(self.UPDATE_COUNTDOWN_TASK) + self.doCountDown() + + def __killCountDownTask(self): + taskMgr.remove(self.UPDATE_COUNTDOWN_TASK) + + def __spawnGameDoneTask(self): + taskMgr.remove(self.GAME_DONE_TASK) + taskMgr.doMethodLater(5.0, self.__gameDone, self.GAME_DONE_TASK) + + def __killUpdateGameDoneTask(self): + taskMgr.remove(self.GAME_DONE_TASK) + + def __spawnUpdateLocalToonTask(self): + self.__initPosBroadcast() + taskMgr.remove(self.UPDATE_LOCALTOON_TASK) + taskMgr.add(self.__updateLocalToonTask, self.UPDATE_LOCALTOON_TASK, priority=self.UPDATE_LOCAL_TOON_PRIORITY) + + def __killUpdateLocalToonTask(self): + taskMgr.remove(self.UPDATE_LOCALTOON_TASK) + + def doCountDown(self, task = None): + if self.countDown < 0: + self.signalLaunch = 1 + else: + self.powerBar['text'] = TTLocalizer.TargetGameCountdown % self.countDown + self.countDown -= 1 + taskMgr.doMethodLater(1.0, self.doCountDown, self.UPDATE_COUNTDOWN_TASK) + + def __initPosBroadcast(self): + self.__posBroadcastPeriod = 0.2 + self.__timeSinceLastPosBroadcast = 0.0 + self.__lastPosBroadcast = self.getAvatar(self.localAvId).getPos() + self.__storeStop = 0 + lt = self.getAvatar(self.localAvId) + lt.d_clearSmoothing() + lt.sendCurrentPosition() + + def __posBroadcast(self, dt): + self.__timeSinceLastPosBroadcast += dt + if self.__timeSinceLastPosBroadcast > self.__posBroadcastPeriod: + self.__timeSinceLastPosBroadcast -= self.__posBroadcastPeriod + self.getAvatar(self.localAvId).cnode.broadcastPosHprFull() + + def __spawnUpdateEnvironTask(self): + taskMgr.remove(self.UPDATE_ENVIRON_TASK) + taskMgr.add(self.__updateEnvironTask, self.UPDATE_ENVIRON_TASK) + + def __killUpdateEnvironTask(self): + taskMgr.remove(self.UPDATE_ENVIRON_TASK) + + def __updateEnvironTask(self, task): + return + dt = globalClock.getDt() + self.speedForward = self.speedForward - self.speedForward * self.airResistance * dt + t = globalClock.getFrameTime() - self.__timeBase + self.distance = self.distance + dt * self.speedForward + distance = self.distance + distance %= self.ENVIRON_LENGTH + self.environNode.setY(-distance) + return Task.cont + + def __updatePowerBarTask(self, task): + powerUp = 0 + timeDiff = None + if not self.arrowKeys.rightPressed(): + self.canPressRight = 1 + elif self.arrowKeys.rightPressed() and self.canPressRight: + powerUp = 1 + self.canPressRight = 0 + if not self.arrowKeys.leftPressed(): + self.canPressLeft = 1 + elif self.arrowKeys.leftPressed() and self.canPressLeft: + powerUp = 1 + self.canPressLeft = 0 + if self.lastPressTime: + timeDiff = globalClock.getFrameTime() - self.lastPressTime + if powerUp and not self.lastPressTime: + self.lastPressTime = globalClock.getFrameTime() + self.ticker = 0.0 + elif powerUp: + splitTime = abs(timeDiff) + if splitTime < 0.15: + splitTime = 0.15 + self.power += 0.6 / abs(timeDiff) + 5.0 + self.lastPressTime = globalClock.getFrameTime() + self.powerBar['value'] = self.power + if self.signalLaunch: + self.lastPressTime = 1 + self.ticker = 1 + timeDiff = 100000 + if self.lastPressTime: + self.ticker += globalClock.getDt() + if self.ticker >= 0.1: + self.ticker = 0.0 + powerDiv = 0.05 + self.power -= 1.0 + 0.2 * (self.power * powerDiv * (self.power * powerDiv)) + if timeDiff > 0.5: + self.power = self.powerBar['value'] + self.signalLaunch = 0 + if self.power > 120: + self.power = 120 + if self.power < 40: + self.power = 40 + self.gameFSM.request('fly') + self.stretchY = 0.9 * self.stretchY + 0.1 * self.power + self.stretchX = 0.9 * self.stretchX + 0.1 * self.launchLaterial + base.localAvatar.setY(self.localStartPos[1] - self.stretchY * 0.12) + base.localAvatar.setX(self.localStartPos[0] + self.stretchX) + dt = globalClock.getDt() + self.__posBroadcast(dt) + stretchDiff = abs(self.stretchY - self.power) * 0.2 + self.bandVolume += stretchDiff + self.bandVolume -= globalClock.getDt() + self.rubberSound.setVolume(self.bandVolume) + self.rubberSound.setPlayRate(1.0 + self.stretchY / 120.0) + return task.cont + + def __updateLocalToonTask(self, task): + stateName = None + if self.gameFSM.getCurrentState(): + stateName = self.gameFSM.getCurrentState().getName() + toonPos = self.getAvatar(self.localAvId).getPos() + dt = globalClock.getDt() + if stateName == 'fall': + self.speedForward = self.speedForward - self.speedForward * self.umbrellaResistance * dt + self.laterial = self.laterial - self.laterial * self.umbrellaResistance * dt + if toonPos[2] > 350.0: + diff = toonPos[2] - 350.0 + self.speedForward += -diff * dt * 0.05 + else: + self.speedForward = self.speedForward - self.speedForward * self.airResistance * dt + self.laterial = self.laterial - self.laterial * self.airResistance * dt + t = globalClock.getFrameTime() - self.__timeBase + self.distance = self.distance + dt * self.speedForward + if self.distance > self.maxDist: + self.distance = self.maxDist + if stateName in ['fall', 'bouncing']: + if self.distance < 15.0: + self.distance = 15.0 + pos = [toonPos[0], self.distance, toonPos[2]] + if stateName in ['fly', 'fall']: + pos[0] += dt * self.laterial + liftMult = 0 + if stateName == 'fly': + if self.arrowKeys.upPressed(): + pass + if self.arrowKeys.downPressed(): + self.gameFSM.request('fall') + elif stateName == 'fall': + if self.driftX > self.TOONXZ_SPEED: + self.driftX = self.TOONXZ_SPEED + if self.driftX < -self.TOONXZ_SPEED: + self.driftX = -self.TOONXZ_SPEED + if self.driftY > self.TOONXZ_SPEED: + self.driftY = self.TOONXZ_SPEED + if self.driftY < -self.TOONXZ_SPEED: + self.driftY = -self.TOONXZ_SPEED + if self.arrowKeys.rightPressed(): + self.driftX += self.TOONXZ_SPEED * dt + if self.arrowKeys.leftPressed(): + self.driftX -= self.TOONXZ_SPEED * dt + if self.arrowKeys.upPressed(): + self.driftY += self.TOONXZ_SPEED * dt + if self.arrowKeys.downPressed(): + self.driftY -= self.TOONXZ_SPEED * dt + pos[0] += self.driftX * dt + pos[1] += self.driftY * dt + self.distance = pos[1] + liftMult = 1 + elif stateName == 'bouncing': + pos[0] += self.driftX * dt + pos[1] += self.driftY * dt + self.distance = pos[1] + else: + xVel = 0.0 + if self.arrowKeys.leftPressed(): + xVel -= self.TOONXZ_SPEED + if self.arrowKeys.rightPressed(): + xVel += self.TOONXZ_SPEED + self.launchLaterial += xVel * dt + if self.launchLaterial < -TargetGameGlobals.MAX_LAT: + self.launchLaterial = -TargetGameGlobals.MAX_LAT + if self.launchLaterial > TargetGameGlobals.MAX_LAT: + self.launchLaterial = TargetGameGlobals.MAX_LAT + pos[0] = self.getToonPlace(self.localAvId)[0] - self.launchLaterial + lift = (self.speedForward - self.speedStall) / self.speedStall * liftMult + onScreenDebug.removeAllWithPrefix(self.getDisplayPrefix()) + onScreenDebug.add('%s Vel' % self.getDisplayPrefix(), self.zVel) + onScreenDebug.add('%s Lift' % self.getDisplayPrefix(), lift) + onScreenDebug.add('%s Gravity' % self.getDisplayPrefix(), self.gravity) + onScreenDebug.add('%s Pos' % self.getDisplayPrefix(), pos[2]) + onScreenDebug.add('%s Drifty' % self.getDisplayPrefix(), self.driftY) + if lift < 0: + lift = 0 + upforce = lift * 10.0 - self.gravity + self.zVel += (lift - self.gravity) * dt + pos[2] += self.zVel * dt + if pos[2] < self.trampZ: + if self.jumpCount: + self.playSound('trampoline') + rebound = 0.75 + if self.trampBounceCount >= 1: + rebound = 0.5 + self.gameFSM.request('bouncing') + pos[2] = self.trampZ + if self.zVel != 0: + signZ = self.zVel / abs(self.zVel) + else: + signZ = 1 + self.zVel = -(self.zVel * rebound - signZ * 1.0) + self.trampBounceCount += 1 + if abs(self.zVel) < 1.0: + self.zVel = 0 + toon = base.localAvatar + if stateName in ['fly', 'fall', 'bouncing']: + toon.b_setAnimState('neutral', 1.0) + toon.dropShadow.hide() + self.gameFSM.request('score') + elif pos[2] < 0.1: + toon = base.localAvatar + if stateName == 'fall' or stateName == 'fly': + self.gameFSM.request('bouncing') + if stateName == 'fly': + ivolume = math.sqrt(abs(self.zVel) / 40.0) + self.hitInfo = 'OnButt' + self.localTrack = ActorInterval(toon, 'slip-forward', startFrame=12, endFrame=24) + self.localTrack.start() + self.playSound('impact', ivolume) + self.playSound('bounce', ivolume) + elif stateName == 'fall' or stateName == 'bouncing': + ivolume = math.sqrt(abs(self.zVel) / 40.0) + if ivolume > 0.4: + if self.hitInfo == 'OnButt': + self.localTrack = ActorInterval(toon, 'slip-forward', startFrame=13, endFrame=24) + self.localTrack.start() + else: + self.localTrack = ActorInterval(toon, 'jump-land', startFrame=6, endFrame=21) + self.localTrack.start() + self.playSound('bounce', ivolume) + pos[2] = 0.1 + rebound = 0.5 + self.speedForward = self.speedForward * rebound + self.driftX = self.driftX * rebound + self.driftY = self.driftY * rebound + toon = base.localAvatar + if self.zVel != 0: + signZ = self.zVel / abs(self.zVel) + else: + signZ = 1 + self.zVel = -(self.zVel * rebound - signZ * 1.0) + if abs(self.zVel) < 1.0: + self.zVel = 0 + if stateName == 'fly' or stateName == 'bouncing': + self.gameFSM.request('score') + if pos[0] > TargetGameGlobals.MAX_FIELD_SPAN: + pos[0] = TargetGameGlobals.MAX_FIELD_SPAN + self.laterial = 0 + if pos[0] < -TargetGameGlobals.MAX_FIELD_SPAN: + pos[0] = -TargetGameGlobals.MAX_FIELD_SPAN + self.laterial = 0 + self.getAvatar(self.localAvId).setPos(pos[0], pos[1], pos[2]) + if hasattr(self, 'cTrav'): + self.cTrav.traverse(render) + if stateName in ['fly', 'fall', 'bouncing']: + self.__posBroadcast(dt) + visZ = camera.getZ(render) + lowHeight = 100 + lowRange = 20 + opacityLow = 1 + baseOpLow = 0.6 + baseTransLow = 1.0 - baseOpLow + if visZ < lowHeight - lowRange or visZ > lowHeight + lowRange: + transLow = 1.0 + else: + dif = abs(visZ - lowHeight) + transLow = dif / lowRange + for item in self.skyListLow: + item.setColorScale(1.0, 1.0, 1.0, transLow * baseOpLow) + + midHeight = 126 + midRange = 20 + opacityMid = 1 + baseOpMid = 1.0 + baseTransMid = 1.0 - baseOpMid + if visZ < midHeight - midRange or visZ > midHeight + midRange: + transMid = 1.0 + else: + dif = abs(visZ - midHeight) + transMid = dif / midRange + for item in self.skyListMid: + item.setColorScale(1.0, 1.0, 1.0, transMid * baseOpMid) + + minTrans = min(1.0, transMid, transLow) + fogVis = 1.0 - minTrans + fogColor = self.AMB_COLOR * minTrans + self.CLOUD_COLOR * fogVis + fogRange = minTrans * self.FOGDISTGROUND + fogVis * self.FOGDISTCLOUD + newCamState = 'Low' + if visZ > midHeight: + newCamState = 'High' + if self.cameraState != newCamState: + self.cameraState = newCamState + self.cloudRendering('High') + elif visZ > lowHeight: + newCamState = 'Mid' + if self.cameraState != newCamState: + if self.cameraState == 'Low': + self.playSound('wind1') + self.cameraState = newCamState + self.cloudRendering('Mid') + else: + newCamState = 'Low' + if self.cameraState != newCamState: + self.cameraState = newCamState + self.cloudRendering('Low') + overlayOp = 0.5 + self.fogOver.setOpacity(fogVis * overlayOp) + vol = (self.speedForward + abs(self.zVel)) / 120.0 + self.flySound.setVolume(vol) + self.flySound.setPlayRate(1.0 + abs(self.zVel) * 0.005) + if self.updateTick % 5 == 0: + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + state = toon.animFSM.getCurrentState().getName() + umbrella = self.remoteUmbrellas.get(avId) + if umbrella: + if state == 'Sit': + open = umbrella.find('**/open_umbrella') + open.hide() + closed = umbrella.find('**/closed_umbrella') + closed.show() + elif state == 'swim': + open = umbrella.find('**/open_umbrella') + open.hide() + closed = umbrella.find('**/closed_umbrella') + closed.show() + elif state == 'jumpAirborne': + open = umbrella.find('**/open_umbrella') + open.show() + closed = umbrella.find('**/closed_umbrella') + closed.hide() + elif state == 'neutral': + open = umbrella.find('**/open_umbrella') + open.hide() + closed = umbrella.find('**/closed_umbrella') + closed.hide() + else: + open = umbrella.find('**/open_umbrella') + open.hide() + closed = umbrella.find('**/closed_umbrella') + closed.hide() + + self.updateTick += 1 + return Task.cont + + def cloudRendering(self, layer): + if layer == 'Low': + for item in self.skyListMid: + item.setBin('fixed', 3) + + for item in self.skyListLow: + item.setBin('fixed', 4) + + elif layer == 'Mid': + for item in self.skyListMid: + item.setBin('fixed', 3) + + for item in self.skyListLow: + item.setBin('fixed', 3) + + elif layer == 'High': + for item in self.skyListMid: + item.setBin('fixed', 4) + + for item in self.skyListLow: + item.setBin('fixed', 3) + + def __addDropShadow_INTERNAL(self, object, scale_x, scale_y, scale_z, list): + shadow = self.dropShadowModel.copyTo(render) + shadow.setPos(0, self.CAMERA_Y, -100) + shadow.setBin('shadow', 100) + shadow.setDepthWrite(False) + shadow.setDepthTest(True) + shadow.setScale(scale_x, scale_y, scale_z) + list.append([shadow, object]) + + def __removeDropShadow_INTERNAL(self, object, list): + for i in xrange(len(list)): + entry = list[i] + if entry[1] == object: + entry[0].removeNode() + list.pop(i) + return + + self.notify.warning('parent object ' + str(object) + ' not found in drop shadow list!') + + def __addToonDropShadow(self, object): + self.__addDropShadow_INTERNAL(object, 0.5, 0.5, 0.5, self.toonDropShadows) + + def __removeToonDropShadow(self, object): + self.__removeDropShadow_INTERNAL(object, self.toonDropShadows) + + def __removeAllToonDropShadows(self): + for entry in self.toonDropShadows: + entry[0].removeNode() + + self.toonDropShadows = [] + + def __spawnUpdateShadowsTask(self): + taskMgr.remove(self.UPDATE_SHADOWS_TASK) + taskMgr.add(self.__updateShadowsTask, self.UPDATE_SHADOWS_TASK, priority=self.UPDATE_SHADOWS_PRIORITY) + + def __killUpdateShadowsTask(self): + taskMgr.remove(self.UPDATE_SHADOWS_TASK) + + def __updateShadowsTask(self, task): + list = self.toonDropShadows + for entry in list: + object = entry[1] + y = object.getY(render) + x = object.getX(render) + if self.jumpCount and object == base.localAvatar: + z = self.trampZ + self.SHADOW_Z_OFFSET + else: + z = 0.0 + self.SHADOW_Z_OFFSET + shadow = entry[0] + shadow.setPos(x, y, z) + + return Task.cont + + def __spawnCollisionDetectionTask(self): + self.__ringGroupsPassed = 0 + taskMgr.remove(self.COLLISION_DETECTION_TASK) + taskMgr.add(self.__collisionDetectionTask, self.COLLISION_DETECTION_TASK, priority=self.COLLISION_DETECTION_PRIORITY) + + def __killCollisionDetectionTask(self): + taskMgr.remove(self.COLLISION_DETECTION_TASK) + + def __collisionDetectionTask(self, task): + return Task.cont + + def __endGameDolater(self, task): + self.gameOver() + return Task.done + + def setTimeBase(self, timestamp): + if not self.hasLocalToon: + return + self.__timeBase = globalClockDelta.networkToLocalTime(timestamp) + + def setColorIndices(self, a, b, c, d): + if not self.hasLocalToon: + return + self.colorIndices = [a, + b, + c, + d] + + def getDisplayPrefix(self): + return 'TargetGame' + + def __gameDone(self, task = None): + self.signalDone() + return task.done + + def setGameDone(self, extra = None): + self.gameOver() + + def setRoundDone(self): + if not self.hasLocalToon: + return + self.round += 1 + self.roundLabel['text'] = TTLocalizer.TargetGameBoard % self.round + self.reset() + self.gameFSM.request('prelaunch') + + def setSingleScore(self, score, avId): + if not self.hasLocalToon: + return + for existIndex in xrange(len(self.avIdList)): + if self.avIdList[existIndex] == avId: + self.scoreLabels[existIndex]['text'] = '%s' % score + + def jumpIn(self, colEntry): + self.jumpCount += 1 + + def jumpOut(self, colEntry): + self.jumpCount -= 1 + if self.jumpCount < 0: + self.jumpCount = 0 diff --git a/toontown/minigame/DistributedTargetGameAI.py b/toontown/minigame/DistributedTargetGameAI.py new file mode 100755 index 00000000..1266b7d9 --- /dev/null +++ b/toontown/minigame/DistributedTargetGameAI.py @@ -0,0 +1,231 @@ +from DistributedMinigameAI import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import TargetGameGlobals +import random +import types + +def checkPlace(placeX, placeY, fillSize, placeList): + goodPlacement = 1 + for place in placeList: + distance = math.sqrt((place[0] - placeX) * (place[0] - placeX) + (place[1] - placeY) * (place[1] - placeY)) + distance = distance - (fillSize + place[2]) + if distance <= 0.0: + goodPlacement = 0 + break + + return goodPlacement + + +class DistributedTargetGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedTargetGameAI_initialized + except: + self.DistributedTargetGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTargetGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['fly']), + State.State('fly', self.enterFly, self.exitFly, ['cleanup', 'resetRound']), + State.State('resetRound', self.enterResetRound, self.exitResetRound, ['cleanup', 'fly']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.__timeBase = globalClockDelta.localToNetworkTime(globalClock.getRealTime()) + self.round = 2 + self.barrierScore = None + self.scoreTrack = [] + + return + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + del self.scoreTrack + if hasattr(self, 'barrierScore'): + if self.barrierScore: + self.barrierScore.cleanup() + del self.barrierScore + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + self.sendUpdate('setTrolleyZone', [self.trolleyZone]) + DistributedMinigameAI.setGameReady(self) + import time + random.seed(time.time()) + seed = int(random.random() * 4000.0) + self.sendUpdate('setTargetSeed', [seed]) + random.seed(seed) + self.setupTargets() + + def setupTargets(self): + fieldWidth = TargetGameGlobals.ENVIRON_WIDTH * 3 + fieldLength = TargetGameGlobals.ENVIRON_LENGTH * 3.7 + self.pattern = TargetGameGlobals.difficultyPatterns[self.getSafezoneId()] + self.targetList = self.pattern[0] + self.targetValue = self.pattern[1] + self.targetSize = self.pattern[2] + self.targetColors = self.pattern[3] + self.targetSubParts = self.pattern[4] + highestValue = 0 + for value in self.targetValue: + if value > highestValue: + highestValue = value + + self.placeValue = highestValue * 0.5 + self.targetsPlaced = [] + placeList = [] + for typeIndex in xrange(len(self.targetList)): + for targetIndex in xrange(self.targetList[typeIndex]): + goodPlacement = 0 + while not goodPlacement: + placeX = random.random() * (fieldWidth * 0.6) - fieldWidth * 0.6 * 0.5 + placeY = (random.random() * 0.6 + (0.0 + 0.4 * (self.placeValue * 1.0 / (highestValue * 1.0)))) * fieldLength + fillSize = self.targetSize[typeIndex] + goodPlacement = checkPlace(placeX, placeY, fillSize, placeList) + + placeList.append((placeX, placeY, fillSize)) + subIndex = self.targetSubParts[typeIndex] + while subIndex: + combinedIndex = typeIndex + subIndex - 1 + self.targetsPlaced.append((placeX, placeY, combinedIndex)) + subIndex -= 1 + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + for avId in self.scoreDict.keys(): + self.scoreDict[avId] = 0 + + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('fly') + + def setScore(self, scoreX, scoreY, other = None): + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'RingGameAI.setScore: invalid avId') + return + topValue = 0 + hitTarget = None + for target in self.targetsPlaced: + radius = self.targetSize[target[2]] + value = self.targetValue[target[2]] + posX = target[0] + posY = target[1] + dx = posX - scoreX + dy = posY - scoreY + distance = math.sqrt(dx * dx + dy * dy) + if distance < radius and topValue < value: + topValue = value + hitTarget = target + hitX = posX + hitY = posY + + if self.scoreDict[avId] < topValue: + self.scoreDict[avId] = topValue + self.sendUpdate('setSingleScore', [topValue, avId]) + return + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def getTimeBase(self): + return self.__timeBase + + def enterFly(self): + self.notify.debug('enterFly') + self.barrierScore = ToonBarrier('waitClientsScore', self.uniqueName('waitClientsScore'), self.avIdList, 120, self.allAvatarsScore, self.handleTimeout) + + def exitFly(self): + pass + + def handleTimeout(self, other = None): + pass + + def allAvatarsScore(self, other = None): + if self.round == 0: + self.gameOver() + else: + self.round -= 1 + self.gameFSM.request('resetRound') + + def getScoreList(self): + scoreList = [0, + 0, + 0, + 0] + avList = [0, + 0, + 0, + 0] + scoreIndex = 0 + for avId in self.scoreDict.keys(): + scoreList[scoreIndex] = self.scoreDict[avId] + avList[scoreIndex] = avId + scoreIndex += 1 + + return scoreList + + def enterResetRound(self): + scoreList = self.getScoreList() + self.scoreTrack.append(scoreList) + self.sendUpdate('setRoundDone', []) + self.barrierScore.cleanup() + del self.barrierScore + taskMgr.doMethodLater(0.1, self.gotoFly, self.taskName('roundReset')) + + def exitResetRound(self): + pass + + def gotoFly(self, extra = None): + if hasattr(self, 'gameFSM'): + self.gameFSM.request('fly') + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def setPlayerDone(self, other = None): + if not hasattr(self, 'barrierScore') or self.barrierScore == None: + return + avId = self.air.getAvatarIdFromSender() + self.barrierScore.clear(avId) + for avId in self.stateDict.keys(): + if self.stateDict[avId] == EXITED: + self.barrierScore.clear(avId) + + return + + def gameOver(self): + self.notify.debug('gameOver') + for entry in self.scoreDict: + if self.scoreDict[entry] == 0: + self.scoreDict[entry] = 1 + + self.scoreTrack.append(self.getScoreList()) + statMessage = 'MiniGame Stats : Target Game' + '\nScores' + '%s' % self.scoreTrack + '\nAvIds' + '%s' % self.scoreDict.keys() + '\nSafeZone' + '%s' % self.getSafezoneId() + self.air.writeServerEvent('MiniGame Stats', None, statMessage) + self.sendUpdate('setGameDone', []) + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + return + + def hasScoreMult(self): + return 0 diff --git a/toontown/minigame/DistributedTugOfWarGame.py b/toontown/minigame/DistributedTugOfWarGame.py new file mode 100755 index 00000000..650eb7ea --- /dev/null +++ b/toontown/minigame/DistributedTugOfWarGame.py @@ -0,0 +1,1244 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from DistributedMinigame import * +from direct.gui.DirectGui import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import ToontownTimer +from toontown.toon import ToonHead +from toontown.suit import SuitDNA +from toontown.suit import Suit +import ArrowKeys +import random +from toontown.toonbase import ToontownGlobals +import string +from toontown.toonbase import TTLocalizer +import TugOfWarGameGlobals +from direct.showutil import Rope +from toontown.effects import Splash +from toontown.effects import Ripples +from toontown.toonbase import TTLocalizer +import MinigamePowerMeter +from direct.task.Task import Task +from otp.nametag import NametagGlobals + +class DistributedTugOfWarGame(DistributedMinigame): + bgm = 'phase_4/audio/bgm/MG_cannon_game_tug.ogg' + toonAnimNames = ['neutral', + 'tug-o-war', + 'slip-forward', + 'slip-backward', + 'victory', + 'sad-neutral'] + suitAnimNames = ['neutral', + 'tug-o-war', + 'slip-forward', + 'slip-backward', + 'flail', + 'victory'] + UPDATE_TIMER_TASK = 'TugOfWarGameUpdateTimerTask' + UPDATE_KEY_PRESS_RATE_TASK = 'TugOfWarGameUpdateKeyPressRateTask' + UPDATE_ROPE_TASK = 'TugOfWarGameUpdateRopeTask' + H_TO_L = 0 + L_TO_H = 1 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTugOfWarGame', [State.State('off', self.enterOff, self.exitOff, ['waitForGoSignal']), + State.State('waitForGoSignal', self.enterWaitForGoSignal, self.exitWaitForGoSignal, ['tug', 'cleanup']), + State.State('tug', self.enterTug, self.exitTug, ['gameDone', 'cleanup']), + State.State('gameDone', self.enterGameDone, self.exitGameDone, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.gameType = TugOfWarGameGlobals.TOON_VS_TOON + self.suit = None + self.suitId = 666 + self.suitType = 'f' + self.suitLevel = 1 + self.sides = {} + self.avList = [[], []] + self.buttons = [0, 1] + self.mouseMode = 0 + self.mouseSide = 0 + self.fallenList = [] + self.handycap = 2.0 + self.advantage = 1.0 + self.tugRopes = [] + self.ropePts = [] + self.ropeTex = [] + self.rightHandDict = {} + self.posDict = {} + self.hprDict = {} + self.offsetDict = {} + self.pullingDict = {} + self.dropShadowDict = {} + self.arrowKeys = None + self.keyTTL = [] + self.idealRate = 2.0 + self.idealForce = 0.0 + self.keyRate = 0 + self.allOutMode = 0 + self.rateMatchAward = 0 + self.targetRateList = [[8, 6], + [5, 7], + [6, 8], + [6, 10], + [7, 11], + [8, 12]] + self.nextRateIndex = 0 + self.drinkPositions = [] + for k in xrange(4): + self.drinkPositions.append(VBase3(-.2 + 0.2 * k, 16 + 2 * k, 0.0)) + + self.rng = RandomNumGen.RandomNumGen(1000) + self.introTrack = None + self.showTrack = None + self.setupTrack = None + self.animTracks = {} + self.randomNumGen = None + return + + def getTitle(self): + return TTLocalizer.TugOfWarGameTitle + + def getInstructions(self): + return TTLocalizer.TugOfWarInstructions + + def getMaxDuration(self): + return TugOfWarGameGlobals.GAME_DURATION + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.hide() + self.room = loader.loadModel('phase_4/models/minigames/tug_of_war_dock') + self.room.reparentTo(hidden) + ropeModel = loader.loadModel('phase_4/models/minigames/tug_of_war_rope') + self.ropeTexture = ropeModel.findTexture('*') + ropeModel.removeNode() + self.sky = loader.loadModel('phase_3.5/models/props/TT_sky') + self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.correctSound = base.loadSfx('phase_4/audio/sfx/MG_pos_buzzer.ogg') + self.sndHitWater = base.loadSfx('phase_4/audio/sfx/MG_cannon_splash.ogg') + self.whistleSound = base.loadSfx('phase_4/audio/sfx/AA_sound_whistle.ogg') + self.music = base.loadMusic(self.bgm) + self.roundText = DirectLabel(text=' ', text_fg=(0, 1, 0, 1), frameColor=(1, 1, 1, 0), text_font=ToontownGlobals.getSignFont(), pos=(0.014, 0, -.84), scale=0.2) + self.powerMeter = MinigamePowerMeter.MinigamePowerMeter(17) + self.powerMeter.reparentTo(aspect2d) + self.powerMeter.setPos(0, 0, 0.4) + self.powerMeter.setPower(8) + self.powerMeter.setTarget(8) + self.arrows = [None] * 2 + for x in xrange(len(self.arrows)): + self.arrows[x] = loader.loadModel('phase_3/models/props/arrow') + self.arrows[x].reparentTo(self.powerMeter) + self.arrows[x].hide() + self.arrows[x].setScale(0.2 - 0.4 * x, 0.2, 0.2) + self.arrows[x].setPos(0.12 - 0.24 * x, 0, -.26) + self.disableArrow(self.arrows[x]) + + self.splash = Splash.Splash(render) + self.suitSplash = Splash.Splash(render) + self.ripples = Ripples.Ripples(render) + self.suitRipples = Ripples.Ripples(render) + return + + def toggleMouseMode(self, param): + self.mouseMode = not self.mouseMode + if self.mouseMode: + mpos = param.getMouse() + if mpos[0] < 0: + self.hilightArrow(self.arrows[1]) + else: + self.hilightArrow(self.arrows[0]) + self.__spawnMouseSpeedTask() + else: + self.__releaseHandler(0) + self.__releaseHandler(1) + self.__killMouseSpeedTask() + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.timer.destroy() + del self.timer + self.room.removeNode() + del self.room + self.sky.removeNode() + del self.sky + del self.dropShadowDict + self.dropShadow.removeNode() + del self.dropShadow + del self.correctSound + del self.sndHitWater + del self.whistleSound + del self.music + self.roundText.destroy() + del self.roundText + if self.powerMeter: + self.powerMeter.destroy() + del self.powerMeter + for x in self.arrows: + if x: + x.removeNode() + del x + + del self.arrows + self.splash.destroy() + del self.splash + self.suitSplash.destroy() + del self.suitSplash + if self.ripples != None: + self.ripples.stop() + self.ripples.detachNode() + del self.ripples + if self.suitRipples != None: + self.suitRipples.stop() + self.suitRipples.detachNode() + del self.suitRipples + for x in self.avList: + del x + + del self.avList + for x in self.tugRopes: + if x != None: + x.detachNode() + del x + + del self.tugRopes + for x in self.ropePts: + if x: + for t in x: + del t + + del x + + del self.ropePts + for x in self.ropeTex: + if x: + for t in x: + t.destroy() + del t + + del x + + del self.ropeTex + del self.posDict + del self.hprDict + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + if self.suit: + self.suit.delete() + del self.suit + del self.sides + del self.buttons + del self.pullingDict + del self.rightHandDict + for x in self.drinkPositions: + del x + + del self.drinkPositions + del self.offsetDict + del self.keyTTL + del self.rng + return + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + NametagGlobals.setGlobalNametagScale(1) + self.arrowKeys = ArrowKeys.ArrowKeys() + self.room.reparentTo(render) + self.room.setPosHpr(0.0, 18.39, -ToontownGlobals.FloorOffset, 0.0, 0.0, 0.0) + self.room.setScale(0.4) + self.sky.setZ(-5) + self.sky.reparentTo(render) + self.dropShadow.setColor(0, 0, 0, 0.5) + camera.reparentTo(render) + camera.setPosHpr(-11.4427, 9.03559, 2.80094, -49.104, -0.89, 0) + self.dropShadow.setBin('fixed', 0, 1) + self.splash.reparentTo(render) + self.suitSplash.reparentTo(render) + base.playMusic(self.music, looping=1, volume=1) + for x in xrange(len(self.arrows)): + self.arrows[x].show() + + for avId in self.avIdList: + self.pullingDict[avId] = 0 + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + self.music.stop() + if self.introTrack: + self.introTrack.finish() + del self.introTrack + self.introTrack = None + for track in self.animTracks.values(): + if track: + track.finish() + del track + self.animTracks = {} + + if self.showTrack: + self.showTrack.finish() + del self.showTrack + self.showTrack = None + if self.setupTrack: + self.setupTrack.finish() + del self.setupTrack + self.setupTrack = None + base.camLens.setMinFov(settings['fov']/(4./3.)) + base.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar) + NametagGlobals.setGlobalNametagScale(1.0) + if self.arrowKeys: + self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS) + self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS) + self.arrowKeys.destroy() + del self.arrowKeys + self.arrowKeys = None + self.room.reparentTo(hidden) + self.sky.reparentTo(hidden) + self.splash.reparentTo(hidden) + self.splash.stop() + self.suitSplash.reparentTo(hidden) + self.suitSplash.stop() + self.ripples.reparentTo(hidden) + self.ripples.stop() + self.hideControls() + self.roundText.hide() + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.loop('neutral') + av.resetLOD() + av.dropShadow.show() + + for x in self.tugRopes: + if x != None: + x.reparentTo(hidden) + + if self.suit: + self.suit.reparentTo(hidden) + for avId in self.avIdList: + if avId in self.dropShadowDict: + self.dropShadowDict[avId].reparentTo(hidden) + + if self.suitId in self.dropShadowDict: + self.dropShadowDict[self.suitId].reparentTo(hidden) + return + + def initCamera(self): + birdseyePosHpr = [1.95461, + 18.4891, + 38.4646, + 1.18185, + -87.5308, + 0] + introPosHpr = [None] * 2 + introPosHpr[0] = [VBase3(-11.4427, 9.03559, 2.80094), VBase3(-49.104, -0.732374, 0)] + introPosHpr[1] = [VBase3(16.9291, 13.9302, 2.64282), VBase3(66.9685, -6.195, 0)] + gameCamHpr = VBase3(-1.13, 1.042, 0) + gameCamPos = VBase3(0, 1.0838, 2.745) + camera.reparentTo(render) + camera.setPosHpr(introPosHpr[self.sides[self.localAvId]][0], introPosHpr[self.sides[self.localAvId]][1]) + lerpDur = 8 + self.introTrack = LerpPosHprInterval(camera, lerpDur, pos=gameCamPos, hpr=gameCamHpr, blendType='easeInOut', name=self.uniqueName('introLerpCameraPos')) + self.introTrack.start() + base.camLens.setMinFov((60 + 2 * self.numPlayers)/(4./3.)) + base.camLens.setFar(450.0) + return + + def sendGameType(self, index, suit): + if not self.hasLocalToon: + return + self.gameType = index + self.suitLevel = suit + if suit == 1: + self.suitType = 'pp' + elif suit == 2: + self.suitType = 'dt' + elif suit == 3: + self.suitType = 'gh' + elif suit == 4: + self.suitType = 'cr' + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + self.initToons() + self.createSuits() + self.calculatePositions() + self.initHandycaps() + self.initRopes() + self.initCamera() + self.animTracks = {} + for avId in self.avIdList: + self.animTracks[avId] = None + + self.animTracks[self.suitId] = None + self.showTrack = None + self.setupTrack = None + self.__initGameVars() + return + + def hideControls(self): + for x in xrange(len(self.arrows)): + self.arrows[x].hide() + + for rope in self.tugRopes: + if rope != None: + rope.reparentTo(hidden) + + for tex in self.ropeTex: + if tex != None: + for texi in tex: + if texi: + texi.reparentTo(hidden) + + if self.powerMeter != None: + self.powerMeter.unbind(DGG.B1PRESS) + self.powerMeter.unbind(DGG.B1RELEASE) + self.powerMeter.hide() + return + + def setUpRopes(self, notTaut): + if self.numPlayers == 1: + suitRightHand = self.suit.getRightHand() + toonRightHand = self.rightHandDict[self.avIdList[0]] + if notTaut: + self.tugRopes[0].setup(3, ((toonRightHand, (0, 0, 0)), (render, (0, 18, -1)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + midPt = (suitRightHand.getPos() - toonRightHand.getPos()) / 2.0 + self.tugRopes[0].setup(3, ((toonRightHand, (0, 0, 0)), (toonRightHand, (0, 0, 0)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + elif self.numPlayers == 2: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + suitRightHand = self.suit.getRightHand() + toonRightHand = self.rightHandDict[self.avIdList[1]] + if notTaut: + self.tugRopes[1].setup(3, ((toonRightHand, (0, 0, 0)), (render, (0, 18, -1)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + midPt = (suitRightHand.getPos() - toonRightHand.getPos()) / 2.0 + self.tugRopes[1].setup(3, ((toonRightHand, (0, 0, 0)), (toonRightHand, (0, 0, 0)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + self.tugRopes[1].reparentTo(render) + else: + if notTaut: + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (render, (0, 18, -1)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + elif self.numPlayers == 3: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + suitRightHand = self.suit.getRightHand() + toonRightHand = self.rightHandDict[self.avIdList[2]] + if notTaut: + self.tugRopes[2].setup(3, ((toonRightHand, (0, 0, 0)), (render, (0, 18, -1)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + midPt = (suitRightHand.getPos() - toonRightHand.getPos()) / 2.0 + self.tugRopes[2].setup(3, ((toonRightHand, (0, 0, 0)), (toonRightHand, (0, 0, 0)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + self.tugRopes[1].reparentTo(render) + self.tugRopes[2].reparentTo(render) + else: + if notTaut: + self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (render, (0, 18, -1)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + self.tugRopes[1].reparentTo(render) + elif self.numPlayers == 4: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.tugRopes[2].setup(3, ((self.rightHandDict[self.avIdList[2]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0)), (self.rightHandDict[self.avIdList[3]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + suitRightHand = self.suit.getRightHand() + toonRightHand = self.rightHandDict[self.avIdList[3]] + if notTaut: + self.tugRopes[3].setup(3, ((toonRightHand, (0, 0, 0)), (render, (0, 18, -1)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + midPt = (suitRightHand.getPos() - toonRightHand.getPos()) / 2.0 + self.tugRopes[3].setup(3, ((toonRightHand, (0, 0, 0)), (toonRightHand, (0, 0, 0)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + self.tugRopes[1].reparentTo(render) + self.tugRopes[2].reparentTo(render) + self.tugRopes[3].reparentTo(render) + else: + self.tugRopes[2].setup(3, ((self.rightHandDict[self.avIdList[2]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0)), (self.rightHandDict[self.avIdList[3]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + if notTaut: + self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (render, (0, 18, -1)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + self.tugRopes[1].reparentTo(render) + self.tugRopes[2].reparentTo(render) + + def initToons(self): + for avId in self.avIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + toon.useLOD(1000) + toon.startBlink() + toon.startLookAround() + for anim in self.toonAnimNames: + toon.pose(anim, 0) + + toon.pose('tug-o-war', 3) + self.rightHandDict[avId] = toon.getRightHands()[0] + toon.loop('neutral') + toon.dropShadow.hide() + self.dropShadowDict[avId] = self.dropShadow.copyTo(hidden) + self.dropShadowDict[avId].reparentTo(toon) + self.dropShadowDict[avId].setScale(0.35) + + def calculatePositions(self): + hprPositions = [VBase3(240, 0, 0), VBase3(120, 0, 0)] + dockPositions = [] + for k in xrange(5): + dockPositions.append(VBase3(-9.0 + 1.5 * k, 18, 0.1)) + + for k in xrange(5): + dockPositions.append(VBase3(3 + 1.5 * k, 18, 0.1)) + + self.sendUpdate('sendNewAvIdList', [self.avIdList]) + if self.numPlayers == 1: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.posDict[self.suitId] = dockPositions[7] + self.posDict[self.avIdList[0]] = dockPositions[2] + self.hprDict[self.avIdList[0]] = hprPositions[0] + else: + self.notify.warning("can't play toon vs. toon with one player") + elif self.numPlayers == 2: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 1) + self.posDict[self.suitId] = dockPositions[7] + self.posDict[self.avIdList[0]] = dockPositions[1] + self.posDict[self.avIdList[1]] = dockPositions[2] + self.hprDict[self.avIdList[0]] = hprPositions[0] + self.hprDict[self.avIdList[1]] = hprPositions[0] + else: + self.randomNumGen.shuffle(self.avIdList) + self.posDict[self.avIdList[0]] = dockPositions[2] + self.posDict[self.avIdList[1]] = dockPositions[7] + self.hprDict[self.avIdList[0]] = hprPositions[0] + self.hprDict[self.avIdList[1]] = hprPositions[1] + elif self.numPlayers == 3: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 2) + self.posDict[self.suitId] = dockPositions[7] + self.posDict[self.avIdList[0]] = dockPositions[0] + self.posDict[self.avIdList[1]] = dockPositions[1] + self.posDict[self.avIdList[2]] = dockPositions[2] + self.hprDict[self.avIdList[0]] = hprPositions[0] + self.hprDict[self.avIdList[1]] = hprPositions[0] + self.hprDict[self.avIdList[2]] = hprPositions[0] + else: + self.randomNumGen.shuffle(self.avIdList) + self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 1) + self.posDict[self.avIdList[0]] = dockPositions[1] + self.posDict[self.avIdList[1]] = dockPositions[2] + self.posDict[self.avIdList[2]] = dockPositions[7] + self.hprDict[self.avIdList[0]] = hprPositions[0] + self.hprDict[self.avIdList[1]] = hprPositions[0] + self.hprDict[self.avIdList[2]] = hprPositions[1] + elif self.numPlayers == 4: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 3) + self.posDict[self.suitId] = dockPositions[6] + self.posDict[self.avIdList[0]] = dockPositions[0] + self.posDict[self.avIdList[1]] = dockPositions[1] + self.posDict[self.avIdList[2]] = dockPositions[2] + self.posDict[self.avIdList[3]] = dockPositions[3] + self.hprDict[self.avIdList[0]] = hprPositions[0] + self.hprDict[self.avIdList[1]] = hprPositions[0] + self.hprDict[self.avIdList[2]] = hprPositions[0] + self.hprDict[self.avIdList[3]] = hprPositions[0] + else: + self.randomNumGen.shuffle(self.avIdList) + self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 1) + self.arrangeByHeight(self.avIdList, self.L_TO_H, 2, 3) + self.posDict[self.avIdList[0]] = dockPositions[1] + self.posDict[self.avIdList[1]] = dockPositions[2] + self.posDict[self.avIdList[2]] = dockPositions[7] + self.posDict[self.avIdList[3]] = dockPositions[8] + self.hprDict[self.avIdList[0]] = hprPositions[0] + self.hprDict[self.avIdList[1]] = hprPositions[0] + self.hprDict[self.avIdList[2]] = hprPositions[1] + self.hprDict[self.avIdList[3]] = hprPositions[1] + for x in self.avIdList: + self.offsetDict[x] = 0 + if self.posDict[x][0] < 0: + self.sides[x] = 0 + self.avList[0].append(x) + else: + self.sides[x] = 1 + self.avList[1].append(x) + + for avId in self.avIdList: + toon = self.getAvatar(avId) + toon.setPos(self.posDict[avId]) + toon.setHpr(self.hprDict[avId]) + + def arrangeByHeight(self, avIdList, order, iStart, iFin): + for i in xrange(iStart, iFin + 1): + for j in xrange(i + 1, iFin + 1): + if order == self.H_TO_L and self.rightHandDict[avIdList[i]].getZ() < self.rightHandDict[avIdList[j]].getZ() or order == self.L_TO_H and self.rightHandDict[avIdList[i]].getZ() > self.rightHandDict[avIdList[j]].getZ(): + temp = avIdList[i] + avIdList[i] = avIdList[j] + avIdList[j] = temp + + def disableArrow(self, a): + a.setColor(1, 0, 0, 0.3) + + def enableArrow(self, a): + a.setColor(1, 0, 0, 1) + + def hilightArrow(self, a): + a.setColor(1, 0.7, 0, 1) + + def unhilightArrow(self, a): + self.enableArrow(a) + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + DistributedMinigame.handleDisabledAvatar(self, avId) + + def __playing(self): + return self.gameFSM.getCurrentState() != self.gameFSM.getFinalState() + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + if not self.__playing(): + return + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('waitForGoSignal') + + def __initGameVars(self): + pass + + def makeToonLookatCamera(self, toon): + toon.headsUp(camera) + + def setText(self, t, newtext): + t['text'] = newtext + + def setTextFG(self, t, fg): + t['text_fg'] = fg + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterWaitForGoSignal(self): + self.notify.debug('enterWaitForGoSignal') + self.powerMeter.show() + self.sendUpdate('reportPlayerReady', [self.sides[self.localAvId]]) + self.roundText.show() + taskMgr.doMethodLater(TugOfWarGameGlobals.WAIT_FOR_GO_TIMEOUT, self.waitForGoTimeoutTask, self.taskName('wait-for-go-timeout')) + + def exitWaitForGoSignal(self): + taskMgr.remove(self.taskName('wait-for-go-timeout')) + + def enterTug(self): + self.notify.debug('enterTug') + self.__spawnUpdateIdealRateTask() + self.__spawnUpdateTimerTask() + self.__spawnUpdateKeyPressRateTask() + taskMgr.doMethodLater(TugOfWarGameGlobals.TUG_TIMEOUT, self.tugTimeoutTask, self.taskName('tug-timeout')) + if self.suit: + self.suit.loop('tug-o-war') + + def exitTug(self): + self.notify.debug('exitTug') + if self.suit: + self.suit.loop('neutral') + self.timer.stop() + self.timer.hide() + taskMgr.remove(self.taskName('tug-timeout')) + + def enterGameDone(self): + pass + + def exitGameDone(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.__killUpdateIdealRateTask() + self.__killUpdateTimerTask() + self.__killUpdateKeyPressRateTask() + self.__killUpdateRopeTask() + + def exitCleanup(self): + pass + + def __gameTimerExpired(self): + self.notify.debug('game timer expired') + if self.arrowKeys: + self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS) + self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS) + + def __pressHandler(self, index): + self.notify.debug('pressHandler') + if index == self.buttons[0]: + self.hilightArrow(self.arrows[index]) + self.keyTTL.insert(0, 1.0) + self.buttons.reverse() + + def __releaseHandler(self, index): + self.notify.debug('releaseHandler') + if index in self.buttons: + self.unhilightArrow(self.arrows[index]) + + def __updateKeyPressRateTask(self, task): + if self.gameFSM.getCurrentState().getName() != 'tug': + return Task.done + for i in xrange(len(self.keyTTL)): + self.keyTTL[i] -= 0.1 + + for i in xrange(len(self.keyTTL)): + if self.keyTTL[i] <= 0: + a = self.keyTTL[0:i] + del self.keyTTL + self.keyTTL = a + break + + self.keyRate = len(self.keyTTL) + if self.keyRate == self.idealRate or self.keyRate == self.idealRate + 1: + self.rateMatchAward += 0.3 + else: + self.rateMatchAward = 0 + self.__spawnUpdateKeyPressRateTask() + return Task.done + + def __updateTimerTask(self, task): + if self.gameFSM.getCurrentState().getName() != 'tug': + return Task.done + self.currentForce = self.computeForce(self.keyRate) + self.sendUpdate('reportCurrentKeyRate', [self.keyRate, self.currentForce]) + self.setSpeedGauge() + self.setAnimState(self.localAvId, self.keyRate) + self.__spawnUpdateTimerTask() + return Task.done + + def __spawnUpdateTimerTask(self): + taskMgr.remove(self.taskName(self.UPDATE_TIMER_TASK)) + taskMgr.doMethodLater(TugOfWarGameGlobals.SEND_UPDATE, self.__updateTimerTask, self.taskName(self.UPDATE_TIMER_TASK)) + + def __killUpdateTimerTask(self): + taskMgr.remove(self.taskName(self.UPDATE_TIMER_TASK)) + + def __spawnUpdateKeyPressRateTask(self): + taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK)) + taskMgr.doMethodLater(0.1, self.__updateKeyPressRateTask, self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK)) + + def __killUpdateKeyPressRateTask(self): + taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK)) + + def __spawnUpdateIdealRateTask(self): + self.idealRate = self.targetRateList[self.nextRateIndex][1] + self.idealForce = self.advantage * (4 + 0.4 * self.idealRate) + taskMgr.doMethodLater(self.targetRateList[self.nextRateIndex][0], self.__updateIdealRateTask, self.taskName('targetRateTimer')) + + def __updateIdealRateTask(self, task): + self.nextRateIndex = self.nextRateIndex + 1 + if self.nextRateIndex < len(self.targetRateList): + if self.nextRateIndex == len(self.targetRateList) - 1: + self.allOutMode = 1 + self.idealRate = self.targetRateList[self.nextRateIndex][1] + self.idealForce = self.advantage * (4 + 0.4 * self.idealRate) + taskMgr.doMethodLater(self.targetRateList[self.nextRateIndex][0], self.__updateIdealRateTask, self.taskName('targetRateTimer')) + return Task.done + + def __killUpdateIdealRateTask(self): + taskMgr.remove(self.taskName('targetRateTimer')) + + def sendGoSignal(self, index): + if not self.hasLocalToon: + return + self.notify.debug('sendGoSignal') + self.buttons = index + self.setupTrack = None + self.showTrack = None + + def startTimer(self = self): + self.currentStartTime = int(globalClock.getFrameTime() * 1000) + time = 10 + self.timer.show() + self.timer.setTime(TugOfWarGameGlobals.GAME_DURATION) + self.timer.countdown(TugOfWarGameGlobals.GAME_DURATION, self.__gameTimerExpired) + + def enableKeys(self = self): + + def keyPress(self, index): + self.__pressHandler(index) + + def keyRelease(self, index): + self.__releaseHandler(index) + + self.arrowKeys.setPressHandlers([lambda self = self, keyPress = keyPress: keyPress(self, 2), + lambda self = self, keyPress = keyPress: keyPress(self, 3), + lambda self = self, keyPress = keyPress: keyPress(self, 1), + lambda self = self, keyPress = keyPress: keyPress(self, 0)]) + self.arrowKeys.setReleaseHandlers([lambda self = self, keyRelease = keyRelease: keyRelease(self, 2), + lambda self = self, keyRelease = keyRelease: keyRelease(self, 3), + lambda self = self, keyRelease = keyRelease: keyRelease(self, 1), + lambda self = self, keyRelease = keyRelease: keyRelease(self, 0)]) + for x in index: + self.enableArrow(self.arrows[x]) + + if self.introTrack != None: + self.introTrack.finish() + self.introTrack = None + self.setupTrack = Sequence(Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameReady), Wait(1.5), Func(base.playSfx, self.whistleSound), Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameGo), Func(self.roundText.setScale, 0.3), Wait(1.5), Func(startTimer), Func(enableKeys), Func(self.gameFSM.request, 'tug'), Func(self.setText, self.roundText, ' '), Func(self.roundText.setScale, 0.2)) + self.setupTrack.start() + return + + def sendStopSignal(self, winners, losers, tieers): + if not self.hasLocalToon: + return + self.notify.debug('sendStopSignal') + self.gameFSM.request('gameDone') + self.hideControls() + reactSeq = Sequence() + exitSeq = Sequence() + suitSlipTime = 0 + if self.gameFSM.getCurrentState().getName() == 'cleanup' or not self.randomNumGen: + return + if self.suit: + if self.suitId in winners: + newPos = VBase3(2.65, 18, 0.1) + randInt = self.randomNumGen.randrange(0, 10) + oopsTrack = Wait(0) + if randInt < 3: + suitSlipTime = 2.2 + waterPos = VBase3(0, 16, -5) + newPos -= VBase3(0.4, 0, 0) + self.suitSplash.stop() + self.suitSplash.setPos(waterPos[0], waterPos[1], -1.8) + self.suitSplash.setScale(3.5, 3.5, 1) + self.suitRipples.setPos(waterPos[0], waterPos[1], -1.7) + self.suitRipples.setScale(1, 1, 1) + startHpr = self.suit.getHpr() + destHpr = startHpr + VBase3(0, 0, -30) + oopsTrack = Sequence(Parallel(Func(self.suit.play, 'flail', None, 26, 38), LerpHprInterval(self.suit, 0.5, destHpr, startHpr=startHpr)), Parallel(Func(self.suit.play, 'slip-forward'), LerpPosInterval(self.suit, duration=1, pos=waterPos), Sequence(Wait(0.55), Func(base.playSfx, self.sndHitWater), Func(self.suitSplash.play), Func(self.ripples.play)))) + reactSeq.append(Sequence(Func(self.suit.loop, 'victory'), Wait(2.6), LerpPosInterval(self.suit, duration=2, pos=newPos), oopsTrack, Func(self.suit.loop, 'neutral'))) + for avId in self.avIdList: + toon = self.getAvatar(avId) + toon.loop('neutral') + if avId in winners: + reactSeq.append(Func(toon.loop, 'victory')) + elif avId in losers: + reactSeq.append(Func(toon.loop, 'neutral')) + else: + reactSeq.append(Func(toon.loop, 'neutral')) + + if self.localAvId in winners: + exitSeq.append(Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameEnd)) + exitSeq.append(Wait(5.0)) + elif self.localAvId in losers: + exitSeq.append(Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameEnd)) + exitSeq.append(Wait(4.8 + suitSlipTime)) + else: + exitSeq.append(Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameTie)) + exitSeq.append(Wait(2.5)) + exitSeq.append(Func(self.gameOver)) + self.showTrack = Parallel(reactSeq, exitSeq) + for x in self.animTracks.values(): + if x != None: + x.finish() + + self.showTrack.start() + if self.arrowKeys: + self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS) + self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS) + return + + def remoteKeyRateUpdate(self, avId, keyRate): + if not self.hasLocalToon: + return + if avId != self.localAvId: + self.setAnimState(avId, keyRate) + + def sendSuitPosition(self, suitOffset): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'tug': + return + self.suitOffset = suitOffset + self.moveSuits() + + def sendCurrentPosition(self, avIdList, offsetList): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'tug': + return + for i in xrange(len(avIdList)): + self.offsetDict[avIdList[i]] = offsetList[i] + + self.moveToons() + self.setUpRopes(0) + + def createSuits(self): + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.suit = Suit.Suit() + self.origSuitPosHpr = [VBase3(6.0, 18, 0.1), VBase3(120, 0, 0)] + self.suitOffset = 0 + d = SuitDNA.SuitDNA() + d.newSuit(self.suitType) + self.suit.setDNA(d) + self.suit.nametag3d.stash() + self.suit.nametag.destroy() + self.suit.reparentTo(render) + self.suit.setPos(self.origSuitPosHpr[0]) + self.suit.setHpr(self.origSuitPosHpr[1]) + for anim in self.suitAnimNames: + self.suit.pose(anim, 0) + + self.suit.pose('tug-o-war', 0) + self.dropShadowDict[self.suitId] = self.dropShadow.copyTo(hidden) + self.dropShadowDict[self.suitId].reparentTo(self.suit) + self.dropShadowDict[self.suitId].setScale(0.45) + + def initHandycaps(self): + if self.numPlayers == 3 and self.gameType == TugOfWarGameGlobals.TOON_VS_TOON: + if len(self.avList[0]) == 1: + toon = self.getAvatar(self.avList[0][0]) + if self.avList[0][0] == self.localAvId: + self.advantage = 2.0 + toon.applyCheesyEffect(ToontownGlobals.CEBigHead) + elif len(self.avList[1]) == 1: + toon = self.getAvatar(self.avList[1][0]) + if self.avList[1][0] == self.localAvId: + self.advantage = 2.0 + toon.applyCheesyEffect(ToontownGlobals.CEBigHead) + + def setSpeedGauge(self): + self.powerMeter.setPower(self.keyRate) + self.powerMeter.setTarget(self.idealRate) + if not self.allOutMode: + self.powerMeter.updateTooSlowTooFast() + if not self.allOutMode: + index = float(self.currentForce) / self.idealForce + bonus = 0.0 + if index > 1: + bonus = max(1, index - 1) + index = 1 + color = (0, + 0.75 * index + 0.25 * bonus, + 0.75 * (1 - index), + 0.5) + self.powerMeter.setBarColor(color) + else: + self.powerMeter.setBarColor((0, 1, 0, 0.5)) + + def setAnimState(self, avId, keyRate): + if self.gameFSM.getCurrentState().getName() != 'tug': + return + toon = self.getAvatar(avId) + if keyRate > 0 and self.pullingDict[avId] == 0: + toon.loop('tug-o-war') + self.pullingDict[avId] = 1 + if keyRate <= 0 and self.pullingDict[avId] == 1: + toon.pose('tug-o-war', 3) + toon.startLookAround() + self.pullingDict[avId] = 0 + + def moveSuits(self): + if self.gameType != TugOfWarGameGlobals.TOON_VS_COG: + return + origPos = self.origSuitPosHpr[0] + curPos = self.suit.getPos() + newPos = VBase3(origPos[0] + self.suitOffset, curPos[1], curPos[2]) + if self.animTracks[self.suitId] != None: + if self.animTracks[self.suitId].isPlaying(): + self.animTracks[self.suitId].finish() + self.checkIfFallen() + if self.suitId not in self.fallenList: + self.animTracks[self.suitId] = Sequence(LerpPosInterval(self.suit, duration=TugOfWarGameGlobals.SEND_UPDATE, pos=newPos), Func(self.checkIfFallen)) + self.animTracks[self.suitId].start() + return + + def moveToons(self): + for avId in self.avIdList: + if avId not in self.fallenList: + toon = self.getAvatar(avId) + if toon: + origPos = self.posDict[avId] + curPos = toon.getPos() + newPos = VBase3(origPos[0] + self.offsetDict[avId] / self.handycap, curPos[1], curPos[2]) + if self.animTracks[avId] != None: + if self.animTracks[avId].isPlaying(): + self.animTracks[avId].finish() + self.checkIfFallen(avId) + if avId not in self.fallenList: + self.animTracks[avId] = Sequence(LerpPosInterval(toon, duration=TugOfWarGameGlobals.SEND_UPDATE, pos=newPos), Func(self.checkIfFallen, avId)) + self.animTracks[avId].start() + + return + + def checkIfFallen(self, avId = None): + if avId == None: + if self.suitId not in self.fallenList: + curPos = self.suit.getPos() + if curPos[0] < 0 and curPos[0] > -2 or curPos[0] > 0 and curPos[0] < 2: + self.hideControls() + self.throwInWater() + losingSide = 1 + self.sendUpdate('reportEndOfContest', [losingSide]) + elif avId not in self.fallenList: + toon = self.getAvatar(avId) + if toon: + curPos = toon.getPos() + if curPos[0] < 0 and curPos[0] > -2 or curPos[0] > 0 and curPos[0] < 2: + self.hideControls() + losingSide = self.sides[avId] + for avId in self.avIdList: + if self.sides[avId] == losingSide: + self.throwInWater(avId) + + self.sendUpdate('reportEndOfContest', [losingSide]) + return + + def throwInWater(self, avId = None): + if avId == None: + self.fallenList.append(self.suitId) + waterPos = self.drinkPositions.pop() + newPos = VBase3(waterPos[0], waterPos[1], waterPos[2] - self.suit.getHeight() / 1.5) + self.suit.loop('neutral') + self.dropShadowDict[self.suitId].reparentTo(hidden) + loser = self.suit + animId = self.suitId + else: + self.fallenList.append(avId) + toon = self.getAvatar(avId) + waterPos = self.drinkPositions.pop() + newPos = VBase3(waterPos[0], waterPos[1], waterPos[2] - toon.getHeight()) + toon.loop('neutral') + self.dropShadowDict[avId].reparentTo(hidden) + loser = toon + animId = avId + if self.animTracks[animId] != None: + if self.animTracks[animId].isPlaying(): + self.animTracks[animId].finish() + self.splash.setPos(newPos[0], newPos[1], -1.8) + self.splash.setScale(2.5, 2.5, 1) + self.ripples.setPos(newPos[0], newPos[1], -1.7) + self.ripples.setScale(1, 1, 1) + self.animTracks[animId] = Sequence(Parallel(ActorInterval(actor=loser, animName='slip-forward', duration=2.0), LerpPosInterval(loser, duration=2.0, pos=newPos), Sequence(Wait(1.0), Parallel(Func(base.playSfx, self.sndHitWater), Func(self.splash.play), Func(self.ripples.play)))), Func(loser.loop, 'neutral')) + self.animTracks[animId].start() + return + + def computeForce(self, keyRate): + F = 0 + if self.allOutMode == 1: + F = 0.75 * keyRate + else: + stdDev = 0.25 * self.idealRate + F = self.advantage * (self.rateMatchAward + 4 + 0.4 * self.idealRate) * math.pow(math.e, -math.pow(keyRate - self.idealRate, 2) / (2.0 * math.pow(stdDev, 2))) + return F + + def initRopes(self): + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + numRopes = self.numPlayers + else: + numRopes = self.numPlayers - 1 + for x in xrange(0, numRopes): + rope = Rope.Rope(self.uniqueName('TugRope' + str(x))) + if rope.showRope: + rope.ropeNode.setRenderMode(RopeNode.RMBillboard) + rope.ropeNode.setThickness(0.2) + rope.setTexture(self.ropeTexture) + rope.ropeNode.setUvMode(RopeNode.UVDistance) + rope.ropeNode.setUvDirection(1) + rope.setTransparency(1) + rope.setColor(0.89, 0.89, 0.6, 1) + self.tugRopes.append(rope) + + self.setUpRopes(1) + + def __spawnUpdateRopeTask(self): + taskMgr.remove(self.taskName(self.UPDATE_ROPE_TASK)) + taskMgr.add(self.__updateRopeTask, self.taskName(self.UPDATE_ROPE_TASK)) + + def __updateRopeTask(self, task): + if self.tugRopes != None: + for i in xrange(len(self.tugRopes)): + if self.tugRopes[i] != None: + self.ropePts[i] = self.tugRopes[i].getPoints(len(self.ropeTex[i])) + for j in xrange(len(self.ropePts[i])): + self.ropeTex[i][j].setPos(self.ropePts[i][j]) + + return Task.cont + + def __killUpdateRopeTask(self): + taskMgr.remove(self.taskName(self.UPDATE_ROPE_TASK)) + + def tugTimeoutTask(self, task): + self.gameOver() + return Task.done + + def waitForGoTimeoutTask(self, task): + self.gameOver() + return Task.done + + def __spawnMouseSpeedTask(self): + taskMgr.remove(self.taskName('mouseSpeed')) + taskMgr.add(self.__mouseSpeedTask, self.taskName('mouseSpeed')) + + def __killMouseSpeedTask(self): + taskMgr.remove(self.taskName('mouseSpeed')) + + def __mouseSpeedTask(self, task): + dx = 0.1 + if self.mouseMode: + mx = base.mouseWatcherNode.getMouseX() + my = base.mouseWatcherNode.getMouseY() + if self.mouseSide == 0: + if mx > dx: + self.mouseSide = 1 + self.__releaseHandler(1) + self.__pressHandler(0) + elif mx > -dx: + self.__releaseHandler(1) + elif self.mouseSide == 1: + if mx < -dx: + self.mouseSide = 0 + self.__releaseHandler(0) + self.__pressHandler(1) + elif mx < dx: + self.__releaseHandler(0) + return Task.cont diff --git a/toontown/minigame/DistributedTugOfWarGameAI.py b/toontown/minigame/DistributedTugOfWarGameAI.py new file mode 100755 index 00000000..0e43bc48 --- /dev/null +++ b/toontown/minigame/DistributedTugOfWarGameAI.py @@ -0,0 +1,333 @@ +from DistributedMinigameAI import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import random +from direct.task.Task import Task +import copy +import TugOfWarGameGlobals +import math + +class DistributedTugOfWarGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedTugOfWarGameAI_initialized + except: + self.DistributedTugOfWarGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTugOfWarGameAI', [State.State('off', self.enterInactive, self.exitInactive, ['waitClientsReady', 'cleanup']), + State.State('waitClientsReady', self.enterWaitClientsReady, self.exitWaitClientsReady, ['sendGoSignal', 'cleanup']), + State.State('sendGoSignal', self.enterSendGoSignal, self.exitSendGoSignal, ['waitForResults', 'cleanup']), + State.State('waitForResults', self.enterWaitForResults, self.exitWaitForResults, ['waitClientsReady', 'contestOver', 'cleanup']), + State.State('contestOver', self.enterContestOver, self.exitContestOver, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, ['off'])], 'off', 'off') + self.addChildGameFSM(self.gameFSM) + + self.switched = 0 + self.side = {} + self.forceDict = [{}, {}] + self.keyRateDict = {} + self.howManyReported = 0 + self.offsetDict = {} + self.kMovement = 0.02 + self.suitId = 666 + self.suitForces = [(6, 4), + (2, 5), + (2, 6.5), + (3, 8.0), + (5, 6), + (8, 8), + (9, 8.5), + (4, 9)] + self.suitForceMultiplier = 0.75 + self.curSuitForce = 0 + self.suitOffset = 0 + self.contestEnded = 0 + self.losingSide = -1 + self.winners = [] + self.losers = [] + self.tieers = [] + self.timeBonus = float(TugOfWarGameGlobals.TIME_BONUS_RANGE) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + del self.side + del self.forceDict + del self.keyRateDict + del self.offsetDict + del self.suitForces + del self.winners + del self.tieers + del self.losers + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + self.suitType = random.randrange(1, 5) + self.suitJellybeanReward = math.pow(2, self.suitType - 1) + if self.isSinglePlayer(): + self.gameType = TugOfWarGameGlobals.TOON_VS_COG + self.suitForceMultiplier = 0.58 + float(self.suitType) / 10.0 + else: + randInt = random.randrange(0, 10) + if randInt < 8: + self.gameType = TugOfWarGameGlobals.TOON_VS_COG + self.suitForceMultiplier = 0.65 + float(self.suitType) / 16.0 + self.kMovement = 0.02 + else: + self.gameType = TugOfWarGameGlobals.TOON_VS_TOON + self.kMovement = 0.04 + self.sendUpdate('sendGameType', [self.gameType, self.suitType]) + DistributedMinigameAI.setGameReady(self) + self.__initGameVars() + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('waitClientsReady') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def __initGameVars(self): + self.currentButtons = [0, 0] + self.readyClients = [] + + def enterWaitClientsReady(self): + self.notify.debug('enterWaitClientsReady') + taskMgr.doMethodLater(TugOfWarGameGlobals.WAIT_FOR_CLIENTS_TIMEOUT, self.waitForClientsTimeout, self.taskName('clients-timeout')) + + def exitWaitClientsReady(self): + taskMgr.remove(self.taskName('clients-timeout')) + + def waitForClientsTimeout(self, task): + self.notify.debug('Done waiting for clients') + self.sendUpdate('sendStopSignal', [self.winners, self.losers, self.tieers]) + self.gameOver() + return Task.done + + def reportPlayerReady(self, side): + avId = self.air.getAvatarIdFromSender() + if avId in self.readyClients: + self.notify.warning('Got reportPlayerReady from an avId more than once: %s' % avId) + return + if avId not in self.avIdList or side not in [0, 1]: + self.notify.warning('Got reportPlayerReady from an avId: %s not in our list: %s' % (avId, self.avIdList)) + else: + self.readyClients.append(avId) + self.side[avId] = side + self.forceDict[side][avId] = 0 + self.offsetDict[avId] = 0 + if len(self.readyClients) == self.numPlayers: + self.readyClients = [] + self.gameFSM.request('sendGoSignal') + + def sendNewAvIdList(self, newAvIdList): + for avId in newAvIdList: + if avId not in self.scoreDict: + self.notify.debug('invalid avId in new list from %s' % self.air.getAvatarIdFromSender()) + return + + if not self.switched: + self.switched = 1 + self.avIdList = newAvIdList + elif self.avIdList != newAvIdList: + self.notify.debug('Big trouble in little TugOWar Town') + + def enterSendGoSignal(self): + self.notify.debug('enterSendGoSignal') + taskMgr.doMethodLater(TugOfWarGameGlobals.GAME_DURATION, self.timerExpired, self.taskName('gameTimer')) + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.curSuitForceInd = 0 + taskMgr.add(self.timeForNewSuitForce, self.taskName('suitForceTimer')) + taskMgr.doMethodLater(1, self.calcTimeBonus, self.taskName('timeBonusTimer')) + self.sendUpdate('sendGoSignal', [[0, 1]]) + self.gameFSM.request('waitForResults') + + def timerExpired(self, task): + self.notify.debug('timer expired') + self.gameFSM.request('contestOver') + return Task.done + + def timeForNewSuitForce(self, task): + self.notify.debug('timeForNewSuitForce') + if self.curSuitForceInd < len(self.suitForces): + randForce = random.random() - 0.5 + self.curSuitForce = self.suitForceMultiplier * self.numPlayers * (self.suitForces[self.curSuitForceInd][1] + randForce) + taskMgr.doMethodLater(self.suitForces[self.curSuitForceInd][0], self.timeForNewSuitForce, self.taskName('suitForceTimer')) + self.curSuitForceInd += 1 + return Task.done + + def calcTimeBonus(self, task): + delta = float(TugOfWarGameGlobals.TIME_BONUS_RANGE) / float(TugOfWarGameGlobals.GAME_DURATION) + self.timeBonus = self.timeBonus - delta + taskMgr.doMethodLater(1, self.calcTimeBonus, self.taskName('timeBonusTimer')) + return Task.done + + def exitSendGoSignal(self): + pass + + def enterWaitForResults(self): + self.notify.debug('enterWaitForResults') + + def calculateOffsets(self): + f = [0, 0] + for i in [0, 1]: + for x in self.forceDict[i].values(): + f[i] += x + + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + f[1] += self.curSuitForce + deltaF = f[1] - f[0] + deltaX = deltaF * self.kMovement + for avId in self.avIdList: + offset = deltaX + if self.side[avId] == 0: + if deltaX < 0: + offset = deltaX / 2.0 + elif deltaX > 0: + offset = deltaX / 2.0 + self.offsetDict[avId] += offset + + if deltaX < 0: + self.suitOffset += deltaX + else: + self.suitOffset += deltaX / 2.0 + + def reportCurrentKeyRate(self, keyRate, force): + avId = self.air.getAvatarIdFromSender() + if avId not in self.side: + self.notify.warning('Avatar %s sent reportCurrentKeyRate too early %s' % (avId, self.side)) + return + self.keyRateDict[avId] = keyRate + self.forceDict[self.side[avId]][avId] = force + self.sendUpdate('remoteKeyRateUpdate', [avId, self.keyRateDict[avId]]) + self.howManyReported += 1 + if self.howManyReported == self.numPlayers: + self.howManyReported = 0 + self.calculateOffsets() + self.sendUpdate('sendCurrentPosition', [self.offsetDict.keys(), self.offsetDict.values()]) + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.sendUpdate('sendSuitPosition', [self.suitOffset]) + + def reportEndOfContest(self, index): + if index not in [0, 1]: + self.notify.warning('Got a bad index %s ' % index) + return + self.losingSide = index + self.notify.debug('losing side = %d' % self.losingSide) + if self.contestEnded == 0: + self.contestEnded = 1 + self.gameFSM.request('contestOver') + + def __processResults(self): + if self.contestEnded: + self.timeBonus = TugOfWarGameGlobals.TIME_BONUS_MIN + int(self.timeBonus + 0.5) + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + if self.losingSide == 1: + self.losers.append(self.suitId) + else: + self.winners.append(self.suitId) + for i in xrange(0, self.numPlayers): + avId = self.avIdList[i] + if self.side[avId] != self.losingSide: + self.scoreDict[avId] = self.suitJellybeanReward + TugOfWarGameGlobals.WIN_JELLYBEANS + self.winners.append(avId) + else: + self.scoreDict[avId] = TugOfWarGameGlobals.LOSS_JELLYBEANS + self.losers.append(avId) + + else: + for i in xrange(0, self.numPlayers): + avId = self.avIdList[i] + if self.side[avId] != self.losingSide: + self.scoreDict[avId] = TugOfWarGameGlobals.WIN_JELLYBEANS + self.winners.append(avId) + else: + self.scoreDict[avId] = TugOfWarGameGlobals.LOSS_JELLYBEANS + self.losers.append(avId) + + elif self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + for i in xrange(0, self.numPlayers): + avId = self.avIdList[i] + if -self.offsetDict[avId] > self.suitOffset: + self.scoreDict[avId] = self.suitJellybeanReward / 2 + TugOfWarGameGlobals.TIE_WIN_JELLYBEANS + self.winners.append(avId) + else: + self.scoreDict[avId] = self.suitJellybeanReward / 2 + TugOfWarGameGlobals.TIE_LOSS_JELLYBEANS + self.losers.append(avId) + self.winners.append(self.suitId) + + else: + maxOffset = -100 + minOffset = 100 + for i in xrange(0, self.numPlayers): + avId = self.avIdList[i] + if self.side[avId] == 0: + if -self.offsetDict[avId] > maxOffset: + maxOffset = -self.offsetDict[avId] + elif -self.offsetDict[avId] < minOffset: + minOffset = -self.offsetDict[avId] + elif self.side[avId] == 1: + if self.offsetDict[avId] > maxOffset: + maxOffset = self.offsetDict[avId] + elif self.offsetDict[avId] < minOffset: + minOffset = self.offsetDict[avId] + + for i in xrange(0, self.numPlayers): + avId = self.avIdList[i] + if maxOffset != minOffset: + if self.side[avId] == 0: + if -self.offsetDict[avId] == maxOffset: + self.scoreDict[avId] = TugOfWarGameGlobals.TIE_WIN_JELLYBEANS + self.winners.append(avId) + else: + self.scoreDict[avId] = TugOfWarGameGlobals.TIE_LOSS_JELLYBEANS + self.losers.append(avId) + elif self.side[avId] == 1: + if self.offsetDict[avId] == maxOffset: + self.scoreDict[avId] = TugOfWarGameGlobals.TIE_WIN_JELLYBEANS + self.winners.append(avId) + else: + self.scoreDict[avId] = TugOfWarGameGlobals.TIE_LOSS_JELLYBEANS + self.losers.append(avId) + else: + self.scoreDict[avId] += TugOfWarGameGlobals.TIE_JELLYBEANS + self.tieers.append(avId) + + self.gameOver() + + def exitWaitForResults(self): + pass + + def enterContestOver(self): + self.__processResults() + self.sendUpdate('sendStopSignal', [self.winners, self.losers, self.tieers]) + + def exitContestOver(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + taskMgr.remove(self.taskName('gameTimer')) + taskMgr.remove(self.taskName('timeBonusTimer')) + taskMgr.remove(self.taskName('suitForceTimer')) + self.gameFSM.request('off') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedTwoDGame.py b/toontown/minigame/DistributedTwoDGame.py new file mode 100755 index 00000000..0ed16d91 --- /dev/null +++ b/toontown/minigame/DistributedTwoDGame.py @@ -0,0 +1,546 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase import TTLocalizer +from direct.gui.DirectFrame import DirectFrame +from direct.gui.DirectGui import DGG +from direct.task.Task import Task +from direct.fsm import ClassicFSM, State +from direct.directnotify import DirectNotifyGlobal +from DistributedMinigame import * +import MinigameAvatarScorePanel, ArrowKeys, ToonBlitzAssetMgr, TwoDCamera +import TwoDSectionMgr, ToonBlitzGlobals, TwoDGameToonSD +from toontown.toonbase import ToontownTimer +from TwoDWalk import * +from TwoDDrive import * +COLOR_RED = VBase4(1, 0, 0, 0.3) + +class DistributedTwoDGame(DistributedMinigame): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTwoDGame') + UpdateLocalToonTask = 'ToonBlitzUpdateLocalToonTask' + EndGameTaskName = 'endTwoDGame' + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTwoDGame', [State.State('off', self.enterOff, self.exitOff, ['play']), + State.State('play', self.enterPlay, self.exitPlay, ['cleanup', 'pause', 'showScores']), + State.State('pause', self.enterPause, self.exitPause, ['cleanup', 'play', 'showScores']), + State.State('showScores', self.enterShowScores, self.exitShowScores, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.reportedDone = False + self.showCollSpheres = False + + def getTitle(self): + return TTLocalizer.TwoDGameTitle + + def getInstructions(self): + return TTLocalizer.TwoDGameInstructions + + def getMaxDuration(self): + return 200 + + def __defineConstants(self): + self.TOON_SPEED = 12.0 + self.MAX_FRAME_MOVE = 1 + self.isHeadInFloor = False + self.timeToRunToElevator = 1.5 + + def setSectionsSelected(self, sectionsSelected): + self.sectionsSelected = sectionsSelected + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.__defineConstants() + self.assetMgr = ToonBlitzAssetMgr.ToonBlitzAssetMgr(self) + self.cameraMgr = TwoDCamera.TwoDCamera(camera) + self.sectionMgr = TwoDSectionMgr.TwoDSectionMgr(self, self.sectionsSelected) + self.gameStartX = -40.0 + endSection = self.sectionMgr.sections[-1] + self.gameEndX = endSection.spawnPointMgr.gameEndX + self.gameLength = self.gameEndX - self.gameStartX + self.toonSDs = {} + avId = self.localAvId + toonSD = TwoDGameToonSD.TwoDGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + self.toonSDs[avId].createHeadFrame(0) + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + taskMgr.remove(self.UpdateLocalToonTask) + for avId in self.toonSDs.keys(): + toonSD = self.toonSDs[avId] + toonSD.destroy() + + del self.toonSDs + self.cameraMgr.destroy() + del self.cameraMgr + self.sectionMgr.destroy() + del self.sectionMgr + for panel in self.scorePanels: + panel.cleanup() + + del self.scorePanels + self.assetMgr.destroy() + del self.assetMgr + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.scorePanels = [] + self.assetMgr.onstage() + lt = base.localAvatar + lt.reparentTo(render) + lt.hideName() + self.__placeToon(self.localAvId) + lt.setAnimState('Happy', 1.0) + lt.setSpeed(0, 0) + base.localAvatar.collisionsOn() + base.localAvatar.setTransparency(1) + self.setupHeadCollision() + self.cameraMgr.onstage() + toonSD = self.toonSDs[self.localAvId] + toonSD.enter() + toonSD.fsm.request('normal') + self.twoDDrive = TwoDDrive(self, self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE) + + def offstage(self): + self.notify.debug('offstage') + self.assetMgr.offstage() + for avId in self.toonSDs.keys(): + self.toonSDs[avId].exit() + + base.localAvatar.setTransparency(0) + self.ignore('enterheadCollSphere-into-floor1') + base.localAvatar.controlManager.currentControls.cTrav.removeCollider(self.headCollNP) + self.headCollNP.removeNode() + del self.headCollNP + base.localAvatar.laffMeter.start() + DistributedMinigame.offstage(self) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + drawNum = 0 + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + drawNum += 1 + toon.reparentTo(render) + toon.setAnimState('Happy', 1.0) + toon.hideName() + toon.startSmooth() + toon.startLookAround() + distCNP = toon.find('**/distAvatarCollNode*') + distCNP.node().setIntoCollideMask(BitMask32.allOff()) + toonSD = TwoDGameToonSD.TwoDGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.enter() + toonSD.fsm.request('normal') + self.toonSDs[avId].createHeadFrame(drawNum) + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.twoDWalk = TwoDWalk(self.twoDDrive, broadcast=not self.isSinglePlayer()) + self.scores = [0] * self.numPlayers + spacing = 0.4 + for i in xrange(self.numPlayers): + avId = self.avIdList[i] + avName = self.getAvatarName(avId) + scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) + scorePanel.setScale(0.9) + scorePanel.setPos(-0.583 - spacing * (self.numPlayers - 1 - i), 0.0, -0.15) + scorePanel.reparentTo(base.a2dTopRight) + scorePanel.makeTransparent(0.75) + self.scorePanels.append(scorePanel) + + self.gameFSM.request('play', [timestamp]) + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self, timestamp): + self.notify.debug('enterPlay') + elapsedTime = globalClockDelta.localElapsedTime(timestamp) + self.sectionMgr.enterPlay(elapsedTime) + handlers = [None, + None, + None, + None, + self.shootKeyHandler] + self.twoDDrive.arrowKeys.setPressHandlers(handlers) + self.twoDWalk.start() + self.accept('jumpStart', self.startJump) + self.accept('enemyHit', self.localToonHitByEnemy) + self.accept('twoDTreasureGrabbed', self.__treasureGrabbed) + self.accept('enemyShot', self.__enemyShot) + taskMgr.remove(self.UpdateLocalToonTask) + taskMgr.add(self.__updateLocalToonTask, self.UpdateLocalToonTask, priority=1) + base.localAvatar.laffMeter.stop() + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(ToonBlitzGlobals.GameDuration[self.getSafezoneId()]) + self.timer.countdown(ToonBlitzGlobals.GameDuration[self.getSafezoneId()], self.timerExpired) + return + + def exitPlay(self): + handlers = [None, + None, + None, + None, + None] + self.twoDDrive.arrowKeys.setPressHandlers(handlers) + if self.toonSDs[self.localAvId].fsm.getCurrentState().getName() != 'victory': + base.localAvatar.b_setAnimState('Happy', 1.0) + self.ignore('jumpStart') + self.ignore('enemyHit') + self.ignore('twoDTreasureGrabbed') + return + + def enterPause(self): + self.notify.debug('enterPause') + + def exitPause(self): + pass + + def enterShowScores(self): + self.notify.debug('enterShowScores') + lerpTrack = Parallel() + lerpDur = 0.5 + tY = 0.6 + bY = -.6 + lX = -.7 + cX = 0 + rX = 0.7 + scorePanelLocs = (((cX, bY),), + ((lX, bY), (rX, bY)), + ((cX, bY), (lX, bY), (rX, bY)), + ((lX, tY), + (rX, tY), + (lX, bY), + (rX, bY))) + scorePanelLocs = scorePanelLocs[self.numPlayers - 1] + for i in xrange(self.numPlayers): + panel = self.scorePanels[i] + pos = scorePanelLocs[i] + panel.wrtReparentTo(aspect2d) + lerpTrack.append(Parallel(LerpPosInterval(panel, lerpDur, Point3(pos[0], 0, pos[1]), blendType='easeInOut'), LerpScaleInterval(panel, lerpDur, Vec3(panel.getScale()) * 1.5, blendType='easeInOut'))) + + self.showScoreTrack = Parallel(lerpTrack, self.getElevatorCloseTrack(), Sequence(Wait(ToonBlitzGlobals.ShowScoresDuration), Func(self.gameOver))) + self.showScoreTrack.start() + + def exitShowScores(self): + self.showScoreTrack.pause() + del self.showScoreTrack + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.timer.stop() + self.timer.destroy() + del self.timer + taskMgr.remove(self.EndGameTaskName) + self.twoDWalk.stop() + self.twoDWalk.destroy() + del self.twoDWalk + self.twoDDrive = None + del self.twoDDrive + return + + def exitCleanup(self): + pass + + def acceptInputs(self): + if hasattr(self, 'twoDDrive'): + handlers = [None, + None, + None, + None, + self.shootKeyHandler] + self.twoDDrive.arrowKeys.setPressHandlers(handlers) + self.twoDDrive.start() + return + + def ignoreInputs(self): + if hasattr(self, 'twoDDrive'): + handlers = [None, + None, + None, + None, + None] + self.twoDDrive.arrowKeys.setPressHandlers(handlers) + self.twoDDrive.lastAction = None + self.twoDDrive.stop() + return + + def __updateLocalToonTask(self, task): + dt = globalClock.getDt() + self.cameraMgr.update() + if self.gameFSM.getCurrentState().getName() == 'play': + if not self.toonSDs[self.localAvId].fsm.getCurrentState().getName() == 'victory': + if not base.localAvatar.getY() == 0: + base.localAvatar.setY(0) + if base.localAvatar.getZ() < -2.0: + self.localToonFellDown() + for avId in self.toonSDs.keys(): + self.toonSDs[avId].update() + + return task.cont + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + self.toonSDs[avId].exit(unexpectedExit=True) + del self.toonSDs[avId] + DistributedMinigame.handleDisabledAvatar(self, avId) + + def setupHeadCollision(self): + collSphere = CollisionSphere(0, 0, 0, 1) + collSphere.setTangible(1) + collNode = CollisionNode('headCollSphere') + collNode.setFromCollideMask(ToontownGlobals.WallBitmask) + collNode.setIntoCollideMask(BitMask32.allOff()) + collNode.addSolid(collSphere) + head = base.localAvatar.getPart('head', '1000') + self.headCollNP = head.attachNewNode(collNode) + self.headCollNP.setPos(0, 0, 0.0) + animal = base.localAvatar.style.getAnimal() + if animal == 'dog' or animal == 'bear' or animal == 'horse': + torso = base.localAvatar.style.torso + legs = base.localAvatar.style.legs + if (torso == 'ls' or torso == 'ld') and legs == 'l': + self.headCollNP.setZ(-1.3) + else: + self.headCollNP.setZ(-0.7) + elif animal == 'mouse' or animal == 'duck': + self.headCollNP.setZ(0.5) + elif animal == 'cat': + self.headCollNP.setZ(-0.3) + elif animal == 'rabbit': + self.headCollNP.setZ(-0.5) + elif animal == 'monkey': + self.headCollNP.setZ(0.3) + elif animal == 'pig': + self.headCollNP.setZ(-0.7) + self.headCollNP.hide() + if self.showCollSpheres: + self.headCollNP.show() + headCollisionEvent = CollisionHandlerEvent() + headCollisionEvent.addInPattern('enter%fn-into-%in') + headCollisionEvent.addOutPattern('%fn-exit-%in') + cTrav = base.localAvatar.controlManager.currentControls.cTrav + cTrav.addCollider(self.headCollNP, headCollisionEvent) + self.accept('enterheadCollSphere-into-floor1', self.__handleHeadCollisionIntoFloor) + self.accept('headCollSphere-exit-floor1', self.__handleHeadCollisionExitFloor) + + def __handleHeadCollisionIntoFloor(self, cevent): + self.isHeadInFloor = True + if base.localAvatar.controlManager.currentControls.lifter.getVelocity() > 0: + base.localAvatar.controlManager.currentControls.lifter.setVelocity(0.0) + self.assetMgr.playHeadCollideSound() + + def __handleHeadCollisionExitFloor(self, cevent): + self.isHeadInFloor = False + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + i = self.avIdList.index(avId) + pos = Point3(ToonBlitzGlobals.ToonStartingPosition[0] + i, ToonBlitzGlobals.ToonStartingPosition[1], ToonBlitzGlobals.ToonStartingPosition[2]) + toon.setPos(pos) + toon.setHpr(-90, 0, 0) + + def startJump(self): + self.assetMgr.playJumpSound() + + def checkValidity(self, avId): + if not self.hasLocalToon: + return False + if self.gameFSM.getCurrentState() == None or self.gameFSM.getCurrentState().getName() != 'play': + self.notify.warning('ignoring msg: av %s performing some action.' % avId) + return False + toon = self.getAvatar(avId) + if toon == None: + return False + return True + + def shootKeyHandler(self): + self.toonSDs[self.localAvId].shootGun() + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.sendUpdate('showShootGun', [self.localAvId, timestamp]) + + def showShootGun(self, avId, timestamp): + if self.checkValidity(avId): + self.notify.debug('avatar %s is shooting water gun' % avId) + if avId != self.localAvId: + self.toonSDs[avId].shootGun() + + def localToonFellDown(self): + if self.toonSDs[self.localAvId].fsm.getCurrentState().getName() != 'fallDown': + self.toonSDs[self.localAvId].fsm.request('fallDown') + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.updateScore(self.localAvId, ToonBlitzGlobals.ScoreLossPerFallDown[self.getSafezoneId()]) + self.sendUpdate('toonFellDown', [self.localAvId, timestamp]) + + def toonFellDown(self, avId, timestamp): + if self.checkValidity(avId): + self.notify.debug('avatar %s fell down.' % avId) + if avId != self.localAvId: + self.updateScore(avId, ToonBlitzGlobals.ScoreLossPerFallDown[self.getSafezoneId()]) + self.toonSDs[avId].fsm.request('fallDown') + + def localToonHitByEnemy(self): + currToonState = self.toonSDs[self.localAvId].fsm.getCurrentState().getName() + if not (currToonState == 'fallBack' or currToonState == 'squish'): + self.toonSDs[self.localAvId].fsm.request('fallBack') + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.updateScore(self.localAvId, ToonBlitzGlobals.ScoreLossPerEnemyCollision[self.getSafezoneId()]) + self.sendUpdate('toonHitByEnemy', [self.localAvId, timestamp]) + + def toonHitByEnemy(self, avId, timestamp): + if self.checkValidity(avId): + self.notify.debug('avatar %s hit by a suit' % avId) + if avId != self.localAvId: + self.updateScore(avId, ToonBlitzGlobals.ScoreLossPerEnemyCollision[self.getSafezoneId()]) + self.toonSDs[avId].fsm.request('fallBack') + + def localToonSquished(self): + currToonState = self.toonSDs[self.localAvId].fsm.getCurrentState().getName() + if not (currToonState == 'fallBack' or currToonState == 'squish'): + self.toonSDs[self.localAvId].fsm.request('squish') + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.updateScore(self.localAvId, ToonBlitzGlobals.ScoreLossPerStomperSquish[self.getSafezoneId()]) + self.sendUpdate('toonSquished', [self.localAvId, timestamp]) + + def toonSquished(self, avId, timestamp): + if self.checkValidity(avId): + self.notify.debug('avatar %s is squished.' % avId) + if avId != self.localAvId: + self.updateScore(avId, ToonBlitzGlobals.ScoreLossPerStomperSquish[self.getSafezoneId()]) + self.toonSDs[avId].fsm.request('squish') + + def localToonVictory(self): + if not ToonBlitzGlobals.EndlessGame: + self.ignoreInputs() + if not self.toonSDs[self.localAvId].fsm.getCurrentState().getName() == 'victory': + self.toonSDs[self.localAvId].fsm.request('victory') + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.sendUpdate('toonVictory', [self.localAvId, timestamp]) + + def toonVictory(self, avId, timestamp): + if self.checkValidity(avId): + self.notify.debug('avatar %s achieves victory' % avId) + if avId != self.localAvId: + self.toonSDs[avId].fsm.request('victory') + + def addVictoryScore(self, avId, score): + if not self.hasLocalToon: + return + self.updateScore(avId, score) + if avId == self.localAvId: + self.assetMgr.threeSparkles.play() + + def __treasureGrabbed(self, sectionIndex, treasureIndex): + self.notify.debug('treasure %s-%s grabbed' % (sectionIndex, treasureIndex)) + section = self.sectionMgr.sections[sectionIndex] + section.treasureMgr.treasures[treasureIndex].hideTreasure() + self.assetMgr.treasureGrabSound.play() + self.sendUpdate('claimTreasure', [sectionIndex, treasureIndex]) + + def setTreasureGrabbed(self, avId, sectionIndex, treasureIndex): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() == 'play': + self.notify.debug('treasure %s-%s grabbed by %s' % (sectionIndex, treasureIndex, avId)) + numSections = len(self.sectionMgr.sections) + if sectionIndex < numSections: + section = self.sectionMgr.sections[sectionIndex] + numTreasures = len(section.treasureMgr.treasures) + if treasureIndex < numTreasures: + treasure = section.treasureMgr.treasures[treasureIndex] + if avId != self.localAvId: + treasure.hideTreasure() + self.updateScore(avId, ToonBlitzGlobals.ScoreGainPerTreasure * treasure.value) + else: + self.notify.error('WHOA!! treasureIndex %s is out of range; numTreasures = %s' % (treasureIndex, numTreasures)) + else: + self.notify.error('WHOA!! sectionIndex %s is out of range; numSections = %s' % (sectionIndex, numSections)) + + def __enemyShot(self, sectionIndex, enemyIndex): + self.sectionMgr.sections[sectionIndex].enemyMgr.enemies[enemyIndex].doShotTrack() + self.sendUpdate('claimEnemyShot', [sectionIndex, enemyIndex]) + + def setEnemyShot(self, avId, sectionIndex, enemyIndex, enemyHealth): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() == 'play': + self.notify.debug('enemy %s is shot by %s. Health left %s' % (enemyIndex, avId, enemyHealth)) + if enemyHealth > 0: + if not avId == self.localAvId: + self.sectionMgr.sections[sectionIndex].enemyMgr.enemies[enemyIndex].doShotTrack() + else: + enemy = self.sectionMgr.sections[sectionIndex].enemyMgr.enemies[enemyIndex] + treasureSpawnPoint = Point3(enemy.suit.getX(), enemy.suit.getY(), enemy.suit.getZ() + enemy.suit.height / 2.0) + self.spawnTreasure(sectionIndex, enemyIndex, treasureSpawnPoint) + enemy.doDeathTrack() + + def updateScore(self, avId, deltaScore): + i = self.avIdList.index(avId) + self.scores[i] += deltaScore + self.scorePanels[i].setScore(self.scores[i]) + self.toonSDs[avId].showScoreText(deltaScore) + + def spawnTreasure(self, sectionIndex, enemyIndex, pos): + if self.gameFSM.getCurrentState().getName() == 'play': + section = self.sectionMgr.sections[sectionIndex] + treasure = section.treasureMgr.enemyTreasures[enemyIndex] + treasure.setTreasurePos(pos) + treasure.popupEnemyTreasure() + + def timerExpired(self): + self.notify.debug('timer expired') + if not self.reportedDone: + if not ToonBlitzGlobals.EndlessGame: + self.ignoreInputs() + self.reportedDone = True + self.sendUpdate('reportDone') + + def setEveryoneDone(self): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'play': + self.notify.warning('ignoring setEveryoneDone msg') + return + self.notify.debug('setEveryoneDone') + + def endGame(task, self = self): + if not ToonBlitzGlobals.EndlessGame: + self.gameFSM.request('showScores') + return Task.done + + self.timer.hide() + taskMgr.doMethodLater(1, endGame, self.EndGameTaskName) + + def getElevatorCloseTrack(self): + leftDoor = self.sectionMgr.exitElevator.find('**/doorL') + rightDoor = self.sectionMgr.exitElevator.find('**/doorR') + leftDoorClose = LerpPosInterval(leftDoor, 2, Point3(3.02, 0, 0)) + rightDoorClose = LerpPosInterval(rightDoor, 2, Point3(-3.02, 0, 0)) + return Sequence(Wait(self.timeToRunToElevator), Parallel(leftDoorClose, rightDoorClose)) + + def debugStartPause(self): + self.sectionMgr.enterPause() + + def debugEndPause(self): + self.sectionMgr.exitPause() diff --git a/toontown/minigame/DistributedTwoDGameAI.py b/toontown/minigame/DistributedTwoDGameAI.py new file mode 100755 index 00000000..aab3597a --- /dev/null +++ b/toontown/minigame/DistributedTwoDGameAI.py @@ -0,0 +1,418 @@ +from DistributedMinigameAI import * +from toontown.ai.ToonBarrier import * +from direct.fsm import ClassicFSM, State +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame import ToonBlitzGlobals +from math import sqrt + +class DistributedTwoDGameAI(DistributedMinigameAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTwoDGameAI') + + def __init__(self, air, minigameId): + try: + self.DistributedTwoDGameAI_initialized + except: + self.DistributedTwoDGame_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTwoDGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.finishedBonusDict = {} + self.finishedTimeLeftDict = {} + self.numFallDownDict = {} + self.numHitByEnemyDict = {} + self.numSquishDict = {} + self.treasuresCollectedDict = {} + self.sectionsSelected = [] + self.enemyHealthTable = [] + self.treasureTakenTable = [] + self.sectionIndexList = [] + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setTrolleyZone(self, trolleyZone): + DistributedMinigameAI.setTrolleyZone(self, trolleyZone) + self.setupSections() + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + self.numTreasures = ToonBlitzGlobals.NumTreasures + self.numEnemies = ToonBlitzGlobals.NumEnemies + self.numTreasuresTaken = 0 + self.numEnemiesKilled = 0 + for avId in self.scoreDict.keys(): + self.scoreDict[avId] = 0 + self.finishedBonusDict[avId] = 0 + self.finishedTimeLeftDict[avId] = -1 + self.numFallDownDict[avId] = 0 + self.numHitByEnemyDict[avId] = 0 + self.numSquishDict[avId] = 0 + self.treasuresCollectedDict[avId] = [0, + 0, + 0, + 0] + + for i in xrange(len(self.sectionsSelected)): + sectionIndex = self.sectionsSelected[i][0] + attribs = ToonBlitzGlobals.SectionTypes[sectionIndex] + enemiesPool = attribs[3] + self.enemyHealthTable += [[]] + enemyIndicesSelected = self.sectionsSelected[i][1] + for j in xrange(len(enemyIndicesSelected)): + enemyIndex = enemyIndicesSelected[j] + enemyType = enemiesPool[enemyIndex][0] + self.enemyHealthTable[i] += [ToonBlitzGlobals.EnemyBaseHealth] + self.enemyHealthTable[i][j] *= self.numPlayers + if enemyType in ToonBlitzGlobals.EnemyHealthMultiplier: + self.enemyHealthTable[i][j] *= ToonBlitzGlobals.EnemyHealthMultiplier[enemyType] + + self.treasureTakenTable += [[]] + treasureIndicesSelected = self.sectionsSelected[i][2] + for j in xrange(len(treasureIndicesSelected)): + self.treasureTakenTable[i] += [0] + + enemyIndicesSelected = self.sectionsSelected[i][1] + for j in xrange(len(enemyIndicesSelected)): + self.treasureTakenTable[i] += [0] + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + scoreList = [] + finishedBonusList = [] + timeLeftList = [] + treasureCollectedList = [] + playerErrorList = [] + for avId in self.avIdList: + scoreList.append(self.scoreDict[avId]) + finishedBonusList.append(self.finishedBonusDict[avId]) + timeLeftList.append(self.finishedTimeLeftDict[avId]) + treasureCollectedList.append(self.treasuresCollectedDict[avId]) + playerError = [self.numFallDownDict[avId], self.numHitByEnemyDict[avId], self.numSquishDict[avId]] + playerErrorList.append(playerError) + self.scoreDict[avId] = max(0, self.scoreDict[avId]) + jellybeans = sqrt(self.scoreDict[avId] * ToonBlitzGlobals.ScoreToJellyBeansMultiplier) + self.scoreDict[avId] = max(1, int(jellybeans)) + + self.air.writeServerEvent('minigame_twoD', self.doId, '%s|%s|%s|%s|%s|%s|%s|%s|%s' % (ToontownGlobals.TwoDGameId, + self.getSafezoneId(), + self.avIdList, + scoreList, + finishedBonusList, + timeLeftList, + treasureCollectedList, + playerErrorList, + self.sectionIndexList)) + self.notify.debug('minigame_twoD%s: %s|%s|%s|%s|%s|%s|%s|%s|%s' % (self.doId, + ToontownGlobals.TwoDGameId, + self.getSafezoneId(), + self.avIdList, + scoreList, + finishedBonusList, + timeLeftList, + treasureCollectedList, + playerErrorList, + self.sectionIndexList)) + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + + def allToonsDone(self = self): + self.notify.debug('allToonsDone') + self.sendUpdate('setEveryoneDone') + if not ToonBlitzGlobals.EndlessGame: + self.gameOver() + + def handleTimeout(avIds, self = self): + self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds) + self.setGameAbort() + + self.doneBarrier = ToonBarrier('waitClientsDone', self.uniqueName('waitClientsDone'), self.avIdList, ToonBlitzGlobals.GameDuration[self.getSafezoneId()] + ToonBlitzGlobals.ShowScoresDuration + MinigameGlobals.latencyTolerance, allToonsDone, handleTimeout) + + def exitPlay(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.doneBarrier.cleanup() + del self.doneBarrier + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def claimTreasure(self, sectionIndex, treasureIndex): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('treasure %s-%s claimed by %s' % (sectionIndex, treasureIndex, avId)) + if sectionIndex < 0 or sectionIndex >= len(self.sectionsSelected): + self.air.writeServerEvent('warning', sectionIndex, 'TwoDGameAI.claimTreasure sectionIndex out of range.') + return + if treasureIndex < 0 or treasureIndex >= len(self.treasureTakenTable[sectionIndex]): + self.notify.warning('Treasure %s: TwoDGameAI.claimTreasure treasureIndex out of range.' % treasureIndex) + self.air.writeServerEvent('warning', treasureIndex, 'TwoDGameAI.claimTreasure treasureIndex out of range.') + return + if self.treasureTakenTable[sectionIndex][treasureIndex]: + return + initialTreasureList = self.sectionsSelected[sectionIndex][2] + if treasureIndex < len(initialTreasureList): + treasureValue = initialTreasureList[treasureIndex][1] + else: + treasureValue = self.numPlayers + self.treasureTakenTable[sectionIndex][treasureIndex] = treasureValue + self.treasuresCollectedDict[avId][treasureValue - 1] += 1 + self.scoreDict[avId] += ToonBlitzGlobals.ScoreGainPerTreasure * treasureValue + self.numTreasuresTaken += 1 + self.sendUpdate('setTreasureGrabbed', [avId, sectionIndex, treasureIndex]) + + def claimEnemyShot(self, sectionIndex, enemyIndex): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('enemy %s-%s shot claimed by %s' % (sectionIndex, enemyIndex, avId)) + if sectionIndex < 0 or sectionIndex >= len(self.sectionsSelected): + self.air.writeServerEvent('warning', sectionIndex, 'TwoDGameAI.claimEnemyShot sectionIndex out of range.') + return + if enemyIndex < 0 or enemyIndex >= len(self.sectionsSelected[sectionIndex][1]): + self.air.writeServerEvent('warning', enemyIndex, 'TwoDGameAI.claimEnemyShot enemyIndex out of range.') + return + if self.enemyHealthTable[sectionIndex][enemyIndex] > 0: + self.enemyHealthTable[sectionIndex][enemyIndex] -= ToonBlitzGlobals.DamagePerBullet + if self.enemyHealthTable[sectionIndex][enemyIndex] <= 0: + self.numEnemiesKilled += 1 + self.sendUpdate('setEnemyShot', [avId, + sectionIndex, + enemyIndex, + self.enemyHealthTable[sectionIndex][enemyIndex]]) + + def reportDone(self): + if self.gameFSM.getCurrentState() == None or self.gameFSM.getCurrentState().getName() != 'play': + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('reportDone: avatar %s is done' % avId) + self.doneBarrier.clear(avId) + return + + def toonVictory(self, avId, timestamp): + if self.gameFSM.getCurrentState() == None or self.gameFSM.getCurrentState().getName() != 'play': + msg = 'TwoDGameAI.toonVictory not in play state!' + self.notify.warning('suspicious: ' + str(avId) + ' ' + msg) + self.air.writeServerEvent('suspicious: ', avId, msg) + return + if avId not in self.scoreDict.keys(): + self.notify.warning('Avatar %s not in list.' % avId) + self.air.writeServerEvent('suspicious: ', avId, 'TwoDGameAI.toonVictory toon not in list.') + return + curTime = self.getCurrentGameTime() + timeLeft = ToonBlitzGlobals.GameDuration[self.getSafezoneId()] - curTime + self.notify.debug('curTime =%s timeLeft = %s' % (curTime, timeLeft)) + addBonus = int(ToonBlitzGlobals.BaseBonusOnCompletion[self.getSafezoneId()] + ToonBlitzGlobals.BonusPerSecondLeft * timeLeft) + self.notify.debug('addBOnus = %d' % addBonus) + if addBonus < 0: + addBonus = 0 + self.finishedBonusDict[avId] = addBonus + timeLeftStr = '%.1f' % timeLeft + self.finishedTimeLeftDict[avId] = timeLeftStr + self.scoreDict[avId] += addBonus + self.sendUpdate('addVictoryScore', [avId, addBonus]) + self.doneBarrier.clear(avId) + return + + def toonFellDown(self, avId, timestamp): + if avId not in self.scoreDict.keys(): + self.notify.warning('Avatar %s not in list.' % avId) + self.air.writeServerEvent('warning', avId, 'TwoDGameAI.toonFellDown toon not in list.') + return + self.numFallDownDict[avId] += 1 + self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerFallDown[self.getSafezoneId()] + + def toonHitByEnemy(self, avId, timestamp): + if avId not in self.scoreDict.keys(): + self.notify.warning('Avatar %s not in list.' % avId) + self.air.writeServerEvent('warning', avId, 'TwoDGameAI.toonHitByEnemy toon not in list.') + return + self.numHitByEnemyDict[avId] += 1 + self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerEnemyCollision[self.getSafezoneId()] + + def toonSquished(self, avId, timestamp): + if avId not in self.scoreDict.keys(): + self.notify.warning('Avatar %s not in list.' % avId) + self.air.writeServerEvent('warning', avId, 'TwoDGameAI.toonSquished toon not in list.') + return + self.numSquishDict[avId] += 1 + self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerStomperSquish[self.getSafezoneId()] + + def setupSections(self): + szId = self.getSafezoneId() + sectionWeights = ToonBlitzGlobals.SectionWeights[szId] + numSections = ToonBlitzGlobals.NumSections[szId] + difficultyPool = [] + difficultyList = [] + sectionsPool = ToonBlitzGlobals.SectionsPool + sectionTypes = ToonBlitzGlobals.SectionTypes + sectionsPoolByDifficulty = [[], + [], + [], + [], + [], + []] + sectionsSelectedByDifficulty = [[], + [], + [], + [], + [], + []] + sectionIndicesSelected = [] + for weight in sectionWeights: + difficulty, probability = weight + difficultyPool += [difficulty] * probability + + for i in xrange(numSections): + difficulty = random.choice(difficultyPool) + difficultyList.append(difficulty) + + difficultyList.sort() + for sectionIndex in sectionsPool: + difficulty = sectionTypes[sectionIndex][0] + sectionsPoolByDifficulty[difficulty] += [sectionIndex] + + for targetDifficulty in difficultyList: + whileCount = 0 + difficulty = targetDifficulty + while not len(sectionsPoolByDifficulty[difficulty]) > 0: + difficulty += 1 + if difficulty >= 5: + difficulty = 0 + whileCount += 1 + if whileCount > 1: + break + else: + sectionIndexChoice = random.choice(sectionsPoolByDifficulty[difficulty]) + sectionsSelectedByDifficulty[difficulty] += [sectionIndexChoice] + sectionsPoolByDifficulty[difficulty].remove(sectionIndexChoice) + + if whileCount > 1: + self.notify.debug('We need more sections than we have choices. We have to now repeat.') + + for i in xrange(len(sectionsSelectedByDifficulty)): + for j in xrange(len(sectionsSelectedByDifficulty[i])): + sectionIndicesSelected.append(sectionsSelectedByDifficulty[i][j]) + + for i in xrange(len(sectionIndicesSelected)): + sectionIndex = sectionIndicesSelected[i] + self.sectionIndexList.append(sectionIndex) + attribs = sectionTypes[sectionIndex] + difficulty = attribs[0] + length = attribs[1] + blocksPool = attribs[2] + enemiesPool = attribs[3] + treasuresPool = attribs[4] + spawnPointsPool = attribs[5] + stompersPool = attribs[6] + enemyIndicesPool = [] + enemyIndicesSelected = [] + if enemiesPool != None: + minEnemies, maxEnemies = attribs[7] + for i in xrange(len(enemiesPool)): + enemyIndicesPool += [i] + + numEnemies = maxEnemies * ToonBlitzGlobals.PercentMaxEnemies[szId] / 100 + numEnemies = max(numEnemies, minEnemies) + for j in xrange(int(numEnemies)): + if len(enemyIndicesPool) == 0: + break + enemyIndex = random.choice(enemyIndicesPool) + enemyIndicesSelected.append(enemyIndex) + enemyIndicesPool.remove(enemyIndex) + + enemyIndicesSelected.sort() + treasureIndicesPool = [] + treasureValuePool = [] + for value in xrange(1, 5): + treasureValuePool += [value] * ToonBlitzGlobals.TreasureValueProbability[value] + + treasureIndicesSelected = [] + if treasuresPool != None: + minTreasures, maxTreasures = attribs[8] + for i in xrange(len(treasuresPool)): + treasureIndicesPool += [i] + + numTreasures = maxTreasures * ToonBlitzGlobals.PercentMaxTreasures[szId] / 100 + numTreasures = max(numTreasures, minTreasures) + for i in xrange(int(numTreasures)): + if len(treasureIndicesPool) == 0: + break + treasureIndex = random.choice(treasureIndicesPool) + treasureValue = random.choice(treasureValuePool) + treasure = (treasureIndex, treasureValue) + treasureIndicesPool.remove(treasureIndex) + treasureIndicesSelected.append(treasure) + + treasureIndicesSelected.sort() + spawnPointIndicesPool = [] + spawnPointIndicesSelected = [] + if spawnPointsPool != None: + minSpawnPoints, maxSpawnPoints = attribs[9] + for i in xrange(len(spawnPointsPool)): + spawnPointIndicesPool += [i] + + numSpawnPoints = maxSpawnPoints * ToonBlitzGlobals.PercentMaxSpawnPoints[szId] / 100 + numSpawnPoints = max(numSpawnPoints, minSpawnPoints) + for i in xrange(int(numSpawnPoints)): + if len(spawnPointIndicesPool) == 0: + break + spawnPoint = random.choice(spawnPointIndicesPool) + spawnPointIndicesSelected.append(spawnPoint) + spawnPointIndicesPool.remove(spawnPoint) + + spawnPointIndicesSelected.sort() + stomperIndicesPool = [] + stomperIndicesSelected = [] + if stompersPool != None: + minStompers, maxStompers = attribs[10] + for i in xrange(len(stompersPool)): + stomperIndicesPool += [i] + + numStompers = maxStompers * ToonBlitzGlobals.PercentMaxStompers[szId] / 100 + numStompers = max(numStompers, minStompers) + for i in xrange(int(numStompers)): + if len(stomperIndicesPool) == 0: + break + stomper = random.choice(stomperIndicesPool) + stomperIndicesSelected.append(stomper) + stomperIndicesPool.remove(stomper) + + stomperIndicesSelected.sort() + sctionTuple = (sectionIndex, + enemyIndicesSelected, + treasureIndicesSelected, + spawnPointIndicesSelected, + stomperIndicesSelected) + self.sectionsSelected.append(sctionTuple) + + return + + def getSectionsSelected(self): + return self.sectionsSelected diff --git a/toontown/minigame/DistributedVineGame.py b/toontown/minigame/DistributedVineGame.py new file mode 100755 index 00000000..a685e318 --- /dev/null +++ b/toontown/minigame/DistributedVineGame.py @@ -0,0 +1,1549 @@ +from pandac.PandaModules import Point3, ForceNode, LinearVectorForce, CollisionHandlerEvent, CollisionNode, CollisionSphere, Camera, PerspectiveLens, Vec4, Point2, ActorNode, Vec3, BitMask32 +from direct.interval.IntervalGlobal import Sequence, Parallel, Func, Wait, LerpPosInterval, ActorInterval, LerpScaleInterval, ProjectileInterval, SoundInterval +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectFrame import DirectFrame +from direct.gui.DirectGui import DGG +from toontown.toonbase import ToontownGlobals +from direct.task.Task import Task +from direct.fsm import ClassicFSM, State +from toontown.toonbase import TTLocalizer +from toontown.minigame.DistributedMinigame import DistributedMinigame +from toontown.minigame import SwingVine +from toontown.minigame import ArrowKeys +from toontown.minigame import VineGameGlobals +from toontown.minigame import VineTreasure +from toontown.minigame import MinigameAvatarScorePanel +from toontown.toonbase import ToontownTimer +from toontown.minigame import VineHeadFrame +from toontown.minigame import VineBat + +class DistributedVineGame(DistributedMinigame): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedVineGame') + UpdateLocalToonTask = 'VineGameUpdateLocalToonTask' + LocalPhysicsRadius = 1.5 + Gravity = 32 + ToonVerticalRate = 0.25 + FallingNot = 0 + FallingNormal = 1 + FallingSpider = 2 + FallingBat = 3 + JumpingFrame = 128 + ToonMaxRightFrame = 35 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.JumpSpeed = 64 + self.TwoVineView = True + self.ArrowToChangeFacing = True + self.gameFSM = ClassicFSM.ClassicFSM('DistributedVineGame', [State.State('off', self.enterOff, self.exitOff, ['play']), + State.State('play', self.enterPlay, self.exitPlay, ['cleanup', 'showScores', 'waitShowScores']), + State.State('waitShowScores', self.enterWaitShowScores, self.exitWaitShowScores, ['cleanup', 'showScores']), + State.State('showScores', self.enterShowScores, self.exitShowScores, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.cameraTopView = (17.6, 6.18756, 43.9956, 0, -89, 0) + self.cameraThreeQuarterView = (0, -63.2, 16.3, 0, 0, 0) + self.cameraSidePos = Point3(0, -63.2, 16.3) + self.cameraTwoVineSidePos = Point3(0, -53, 17.3) + self.cameraTwoVineAdj = 5 + self.cameraSideView = (-15, -53, 17.3, 0, 0, 0) + self.localPhysicsNP = None + self.vines = [] + self.physicsHandler = None + self.lastJumpVine = -1 + self.lastJumpPos = None + self.lastJumpT = 0 + self.lastJumpFacingRight = True + self.lastJumpTime = 0 + self.toonInfo = {} + self.otherToonPhysics = {} + self.headFrames = {} + self.changeFacingInterval = None + self.attachingToVineCamIval = None + self.localToonJumpAnimIval = None + self.endingTracks = {} + self.endingTrackTaskNames = [] + self.sendNewVineTUpdateAsap = False + self.lastNewVineTUpdate = 0 + self.defaultMaxX = (VineGameGlobals.NumVines + 1) * VineGameGlobals.VineXIncrement + return + + def getClimbDir(self, avId): + retval = 0 + if avId in self.toonInfo: + retval = self.toonInfo[avId][5] + return retval + + def moveCameraToSide(self): + camera.reparentTo(render) + p = self.cameraSideView + camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5]) + + def getTitle(self): + return TTLocalizer.VineGameTitle + + def getInstructions(self): + return TTLocalizer.VineGameInstructions + + def getMaxDuration(self): + return 0 + + def defineConstants(self): + self.ShowToonSpheres = 0 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.defineConstants() + self.music = base.loadMusic('phase_4/audio/bgm/MG_Vine.ogg') + self.gameAssets = loader.loadModel('phase_4/models/minigames/vine_game') + self.gameBoard = self.gameAssets.find('**/background') + self.gameBoard.reparentTo(render) + self.gameBoard.show() + self.gameBoard.setPosHpr(0, 0, 0, 0, 0, 0) + self.gameBoard.setTransparency(1) + self.gameBoard.setColorScale(1, 1, 1, 0.5) + self.gameBoard.setScale(1.0) + self.gameBoard.hide(VineGameGlobals.RadarCameraBitmask) + self.gameBoardL = self.gameBoard.copyTo(render) + self.gameBoardL.setPos(-635, 0, 0) + self.gameBoardR = self.gameBoard.copyTo(render) + self.gameBoardR.setPos(635, 0, 0) + self.treasureModel = self.gameAssets.find('**/bananas') + self.setupVineCourse() + self.grabSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_bananas.ogg') + self.jumpSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_jump.ogg') + self.catchSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_catch.ogg') + self.spiderHitSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_spider_hit.ogg') + self.batHitVineSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_bat_hit.ogg') + self.batHitMidairSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_bat_hit_midair.ogg') + self.winSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_finish.ogg') + self.fallSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_fall.ogg') + self.loadBats() + self.createBatIvals() + bothPlatform = loader.loadModel('phase_4/models/minigames/vine_game_shelf') + self.startPlatform = bothPlatform.find('**/start1') + self.startPlatform.setPos(-16, 0, 15) + self.startPlatform.reparentTo(render) + self.startPlatform.setScale(1.0, 1.0, 0.75) + self.endPlatform = bothPlatform.find('**/end1') + endPos = self.vines[VineGameGlobals.NumVines - 1].getPos() + self.endPlatform.setPos(endPos[0] + 20, 0, 15) + self.endPlatform.reparentTo(render) + self.endPlatform.setScale(1.0, 1.0, 0.75) + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + del self.music + self.gameAssets.removeNode() + self.gameBoard.removeNode() + self.gameBoardL.removeNode() + self.gameBoardR.removeNode() + del self.gameBoard + del self.gameBoardL + del self.gameBoardR + self.treasureModel.removeNode() + del self.treasureModel + for vine in self.vines: + vine.unload() + del vine + + self.vines = [] + self.destroyBatIvals() + for bat in self.bats: + bat.destroy() + del bat + + self.bats = [] + del self.grabSound + del self.jumpSound + del self.catchSound + del self.spiderHitSound + del self.batHitVineSound + del self.batHitMidairSound + del self.winSound + del self.fallSound + if self.localToonJumpAnimIval: + self.localToonJumpAnimIval.finish() + del self.localToonJumpAnimIval + self.startPlatform.removeNode() + self.endPlatform.removeNode() + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + for avId in self.avIdList: + self.updateToonInfo(avId, vineIndex=0, vineT=VineGameGlobals.VineStartingT, posX=0, posZ=0, facingRight=0, climbDir=0, fallingInfo=self.FallingNot) + + self.scorePanels = [] + self.gameBoard.reparentTo(render) + self.moveCameraToSide() + self.arrowKeys = ArrowKeys.ArrowKeys() + handlers = [self.upArrowKeyHandler, + self.downArrowKeyHandler, + self.leftArrowKeyHandler, + self.rightArrowKeyHandler, + None] + self.arrowKeys.setPressHandlers(handlers) + self.numTreasures = len(self.vines) - 1 + self.treasures = [] + for i in xrange(self.numTreasures): + height = self.randomNumGen.randrange(10, 25) + xPos = self.randomNumGen.randrange(12, 18) + pos = Point3(self.vines[i].getX() + 15, 0, height) + self.treasures.append(VineTreasure.VineTreasure(self.treasureModel, pos, i, self.doId)) + + gravityFN = ForceNode('world-forces') + gravityFNP = render.attachNewNode(gravityFN) + gravityForce = LinearVectorForce(0, 0, -self.Gravity) + gravityFN.addForce(gravityForce) + base.physicsMgr.addLinearForce(gravityForce) + self.gravityForce = gravityForce + self.gravityForceFNP = gravityFNP + lt = base.localAvatar + radius = 0.7 + handler = CollisionHandlerEvent() + handler.setInPattern('ltCatch-%fn') + self.bodyColEventNames = [] + self.ltLegsCollNode = CollisionNode('catchLegsCollNode') + self.bodyColEventNames.append('ltCatch-%s' % 'catchLegsCollNode') + self.ltLegsCollNode.setCollideMask(VineGameGlobals.SpiderBitmask) + self.ltTorsoCollNode = CollisionNode('catchTorsoCollNode') + self.bodyColEventNames.append('ltCatch-%s' % 'catchTorsoCollNode') + self.ltTorsoCollNode.setCollideMask(VineGameGlobals.SpiderBitmask) + self.ltHeadCollNode = CollisionNode('catchHeadCollNode') + self.bodyColEventNames.append('ltCatch-%s' % 'catchHeadCollNode') + self.ltHeadCollNode.setCollideMask(VineGameGlobals.SpiderBitmask) + self.ltLHandCollNode = CollisionNode('catchLHandCollNode') + self.bodyColEventNames.append('ltCatch-%s' % 'catchLHandCollNode') + self.ltLHandCollNode.setCollideMask(VineGameGlobals.SpiderBitmask) + self.ltRHandCollNode = CollisionNode('catchRHandCollNode') + self.bodyColEventNames.append('ltCatch-%s' % 'catchRHandCollNode') + self.ltRHandCollNode.setCollideMask(VineGameGlobals.SpiderBitmask) + legsCollNodepath = lt.attachNewNode(self.ltLegsCollNode) + legsCollNodepath.hide() + head = base.localAvatar.getHeadParts().getPath(2) + headCollNodepath = head.attachNewNode(self.ltHeadCollNode) + headCollNodepath.hide() + torso = base.localAvatar.getTorsoParts().getPath(2) + torsoCollNodepath = torso.attachNewNode(self.ltTorsoCollNode) + torsoCollNodepath.hide() + self.torso = torsoCollNodepath + lHand = base.localAvatar.getLeftHands()[0] + lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode) + lHandCollNodepath.hide() + rHand = base.localAvatar.getRightHands()[0] + rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode) + rHandCollNodepath.hide() + lt.cTrav.addCollider(legsCollNodepath, handler) + lt.cTrav.addCollider(headCollNodepath, handler) + lt.cTrav.addCollider(lHandCollNodepath, handler) + lt.cTrav.addCollider(rHandCollNodepath, handler) + lt.cTrav.addCollider(torsoCollNodepath, handler) + if self.ShowToonSpheres: + legsCollNodepath.show() + headCollNodepath.show() + lHandCollNodepath.show() + rHandCollNodepath.show() + torsoCollNodepath.show() + self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius)) + self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius)) + self.ltLHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) + self.ltRHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) + self.ltTorsoCollNode.addSolid(CollisionSphere(0, 0, radius, radius * 2)) + self.toonCollNodes = [legsCollNodepath, + headCollNodepath, + lHandCollNodepath, + rHandCollNodepath, + torsoCollNodepath] + for eventName in self.bodyColEventNames: + self.accept(eventName, self.toonHitSomething) + + self.introTrack = self.getIntroTrack() + self.introTrack.start() + return + + def offstage(self): + self.notify.debug('offstage') + for panel in self.scorePanels: + panel.cleanup() + + del self.scorePanels + self.gameBoard.hide() + DistributedMinigame.offstage(self) + self.arrowKeys.destroy() + del self.arrowKeys + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.dropShadow.show() + av.resetLOD() + + for treasure in self.treasures: + treasure.destroy() + + del self.treasures + base.physicsMgr.removeLinearForce(self.gravityForce) + self.gravityForceFNP.removeNode() + for collNode in self.toonCollNodes: + while collNode.node().getNumSolids(): + collNode.node().removeSolid(0) + + base.localAvatar.cTrav.removeCollider(collNode) + + del self.toonCollNodes + for eventName in self.bodyColEventNames: + self.ignore(eventName) + + self.ignoreAll() + if self.introTrack.isPlaying(): + self.introTrack.finish() + del self.introTrack + for vine in self.vines: + vine.stopSwing() + vine.hide() + + self.startPlatform.hide() + self.endPlatform.hide() + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + DistributedMinigame.handleDisabledAvatar(self, avId) + + def updateToonInfo(self, avId, vineIndex = None, vineT = None, posX = None, posZ = None, facingRight = None, climbDir = None, velX = None, velZ = None, fallingInfo = None): + newVineIndex = vineIndex + newVineT = vineT + newPosX = posX + newPosZ = posZ + newFacingRight = facingRight + newClimbDir = climbDir + newVelX = velX + newVelZ = velZ + newFallingInfo = fallingInfo + oldInfo = None + if avId in self.toonInfo: + oldInfo = self.toonInfo[avId] + if vineIndex == None: + newVineIndex = oldInfo[0] + if vineT == None: + newVineT = oldInfo[1] + if posX == None: + newPosX = oldInfo[2] + if posZ == None: + newPosZ = oldInfo[3] + if facingRight == None: + newFacingRight = oldInfo[4] + if climbDir == None: + newClimbDir = oldInfo[5] + if velX == None: + newVelX = oldInfo[6] + if velZ == None: + newVelZ = oldInfo[7] + if fallingInfo == None: + newFallingInfo = oldInfo[8] + if newVineIndex < -1 or newVineIndex >= len(self.vines): + self.notify.warning('invalid vineIndex for %d, forcing 0' % avId) + newVineIndex = 0 + if newVineT < 0 or newVineT > 1: + self.notify.warning('invalid vineT for %d, setting to 0' % avId) + if not (newFacingRight == 0 or newFacingRight == 1): + self.notify.warning('invalid facingRight for %d, forcing to 1' % avId) + newFacingRight = 1 + if newPosX < -1000 or newPosX > 2000: + self.notify.warning('invalid posX for %d, forcing to 0' % avId) + newPosX = 0 + if newPosZ < -100 or newPosZ > 1000: + self.notify.warning('invalid posZ for %d, forcing to 0' % avId) + newPosZ = 0 + if newVelX < -1000 or newVelX > 1000: + self.notify.warning('invalid velX %s for %d, forcing to 0' % (newVelX, avId)) + newVelX = 0 + if newVelZ < -1000 or newVelZ > 1000: + self.notify.warning('invalid velZ %s for %d, forcing to 0' % (newVelZ, avId)) + newVelZ = 0 + if newFallingInfo < self.FallingNot or newFallingInfo > self.FallingBat: + self.notify.warning('invalid fallingInfo for %d, forcing to 0' % avId) + newFallingInfo = 0 + newInfo = [newVineIndex, + newVineT, + newPosX, + newPosZ, + newFacingRight, + newClimbDir, + newVelX, + newVelZ, + newFallingInfo] + self.toonInfo[avId] = newInfo + if oldInfo: + self.applyToonInfoChange(avId, newInfo, oldInfo) + self.sanityCheck() + return + + def applyToonInfoChange(self, avId, newInfo, oldInfo): + if not self.isInPlayState(): + return + oldVine = oldInfo[0] + newVine = newInfo[0] + if not oldVine == newVine: + self.notify.debug('we have a vine change') + if oldVine == -1: + self.notify.debug(' we were jumping and now attaching to a new vine') + newVineT = newInfo[1] + newFacingRight = newInfo[4] + self.vines[newVine].attachToon(avId, newVineT, newFacingRight) + if newVine == VineGameGlobals.NumVines - 1: + self.doEndingTrackTask(avId) + elif newVine == -1: + self.notify.debug('we were attached to a vine and we are now jumping') + curInfo = self.vines[oldVine].getAttachedToonInfo(avId) + self.vines[oldVine].detachToon(avId) + if not avId == self.localAvId: + posX = newInfo[2] + posZ = newInfo[3] + velX = newInfo[6] + velZ = newInfo[7] + self.makeOtherToonJump(avId, posX, posZ, velX, velZ) + else: + self.notify.warning('should not happen directly going from one vine to another') + self.vines[oldVine].detachToon(avId) + newVineT = newInfo[1] + newFacingRight = newInfo[4] + self.vines[newVine].attachToon(avId, newVineT, newFacingRight) + elif newVine == oldVine and newInfo[4] != oldInfo[4]: + self.notify.debug('# still on the same vine, but we changed facing') + self.vines[newVine].changeAttachedToonFacing(avId, newInfo[4]) + elif newVine >= 0: + self.vines[newVine].changeAttachedToonT(avId, newInfo[1]) + else: + self.notify.debug('we are still falling') + if oldInfo[8] != newInfo[8]: + if not avId == self.localAvId: + posX = newInfo[2] + posZ = newInfo[3] + velX = newInfo[6] + velZ = newInfo[7] + self.makeOtherToonFallFromMidair(avId, posX, posZ, velX, velZ) + + def sanityCheck(self): + if not self.isInPlayState(): + return + for avId in self.toonInfo.keys(): + myVineIndex = self.toonInfo[avId][0] + foundVines = [] + foundVineIndex = -1 + for curVine in xrange(len(self.vines)): + curInfo = self.vines[curVine].getAttachedToonInfo(avId) + if curInfo: + foundVines.append(curVine) + foundVineIndex = curVine + + if len(foundVines) > 1: + self.notify.warning('toon %d is attached to vines %s' % (avId, foundVines)) + if not foundVineIndex == myVineIndex and not myVineIndex == VineGameGlobals.NumVines - 1: + self.notify.warning('avId=%d foundVineIndex=%d != myVineIndex=%d' % (avId, foundVineIndex, myVineIndex)) + + def getVineAndVineInfo(self, avId): + retVine = -1 + retInfo = None + for curVine in xrange(len(self.vines)): + curInfo = self.vines[curVine].getAttachedToonInfo(avId) + if curInfo: + retVine = curVine + retInfo = curInfo + break + + if self.toonInfo[avId][0] != retVine: + self.notify.warning("getVineAndVineInfo don't agree, toonInfo[%d]=%s, retVine=%d" % (avId, self.toonInfo[avId][0], retVine)) + return (retVine, curInfo) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + self.toonOffsets = {} + self.toonOffsetsFalling = {} + for index in xrange(self.numPlayers): + avId = self.avIdList[index] + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + toon.setPos(0, 0, 0) + toon.setHpr(0, 0, 0) + self.toonOffsets[avId] = self.calcToonOffset(toon) + toon.dropShadow.hide() + newHeadFrame = VineHeadFrame.VineHeadFrame(toon) + newHeadFrame.hide() + self.headFrames[avId] = newHeadFrame + toon.useLOD(1000) + toon.setX(-100) + + def calcToonOffset(self, toon): + offset = Point3(0, 0, 0) + toon.pose('swing', 74) + leftHand = toon.find('**/leftHand') + if not leftHand.isEmpty(): + offset = leftHand.getPos(toon) + return offset + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + if self.introTrack.isPlaying(): + self.introTrack.finish() + self.gameFSM.request('play') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + if base.localAvatar.laffMeter: + base.localAvatar.laffMeter.stop() + self.createRadar() + self.scores = [0] * self.numPlayers + spacing = 0.4 + for i in xrange(self.numPlayers): + avId = self.avIdList[i] + avName = self.getAvatarName(avId) + scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) + scorePanel.setScale(0.9) + scorePanel.setPos(-0.583 - spacing * (self.numPlayers - 1 - i), 0.0, -0.15) + scorePanel.reparentTo(base.a2dTopRight) + scorePanel.makeTransparent(0.75) + self.scorePanels.append(scorePanel) + + for vine in self.vines: + vine.show() + vine.startSwing() + + self.startBatIvals() + for avId in self.avIdList: + toon = self.getAvatar(avId) + if toon: + if avId == self.localAvId: + self.attachLocalToonToVine(0, VineGameGlobals.VineStartingT) + else: + self.vines[0].attachToon(avId, VineGameGlobals.VineStartingT, self.lastJumpFacingRight) + + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(VineGameGlobals.GameDuration) + self.timer.countdown(VineGameGlobals.GameDuration, self.timerExpired) + base.playMusic(self.music, looping=1, volume=0.9) + self.__spawnUpdateLocalToonTask() + + def exitPlay(self): + self.notify.debug('exitPlay') + if base.localAvatar.laffMeter: + base.localAvatar.laffMeter.start() + self.timer.stop() + self.timer.destroy() + del self.timer + self.clearLocalPhysics() + for ival in self.batIvals: + ival.finish() + del ival + + self.batIvals = [] + self.music.stop() + + def enterWaitShowScores(self): + self.notify.debug('enterWaitShowScores') + + def exitWaitShowScores(self): + self.notify.debug('exitWaitShowScores') + + def enterShowScores(self): + self.notify.debug('enterShowScores') + lerpTrack = Parallel() + lerpDur = 0.5 + tY = 0.6 + bY = -.05 + lX = -.5 + cX = 0 + rX = 0.5 + scorePanelLocs = (((cX, bY),), + ((lX, bY), (rX, bY)), + ((cX, tY), (lX, bY), (rX, bY)), + ((lX, tY), + (rX, tY), + (lX, bY), + (rX, bY))) + scorePanelLocs = scorePanelLocs[self.numPlayers - 1] + for i in xrange(self.numPlayers): + panel = self.scorePanels[i] + pos = scorePanelLocs[i] + panel.wrtReparentTo(aspect2d) + lerpTrack.append(Parallel(LerpPosInterval(panel, lerpDur, Point3(pos[0], 0, pos[1]), blendType='easeInOut'), LerpScaleInterval(panel, lerpDur, Vec3(panel.getScale()) * 2.0, blendType='easeInOut'))) + + self.showScoreTrack = Parallel(lerpTrack, Sequence(Wait(VineGameGlobals.ShowScoresDuration), Func(self.gameOver))) + self.showScoreTrack.start() + + def exitShowScores(self): + self.showScoreTrack.pause() + del self.showScoreTrack + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.destroyRadar() + self.__killUpdateLocalToonTask() + self.cleanupEndingTracks() + for avId in self.headFrames: + self.headFrames[avId].destroy() + + def exitCleanup(self): + pass + + def __spawnUpdateLocalToonTask(self): + taskMgr.remove(self.UpdateLocalToonTask) + taskMgr.add(self.__updateLocalToonTask, self.UpdateLocalToonTask) + + def __killUpdateLocalToonTask(self): + taskMgr.remove(self.UpdateLocalToonTask) + + def clearLocalPhysics(self): + if self.localPhysicsNP: + an = self.localPhysicsNP.node() + base.physicsMgr.removePhysicalNode(an) + self.localPhysicsNP.removeNode() + del self.localPhysicsNP + self.localPhysicsNP = None + if hasattr(self, 'treasureCollNodePath') and self.treasureCollNodePath: + self.treasureCollNodePath.removeNode() + return + + def handleLocalToonFellDown(self): + self.notify.debug('attaching toon back to vine since he fell') + self.fallSound.play() + vineToAttach = self.lastJumpVine + if self.toonInfo[self.localAvId][8] == self.FallingSpider: + if self.vines[vineToAttach].hasSpider: + vineToAttach -= 1 + if vineToAttach < 0: + vineToAttach = 0 + self.attachLocalToonToVine(vineToAttach, VineGameGlobals.VineFellDownT) + + def makeCameraFollowJumpingToon(self): + camera.setHpr(0, 0, 0) + if self.TwoVineView: + camera.setPos(self.cameraTwoVineSidePos) + maxVine = self.lastJumpVine + 1 + if maxVine >= len(self.vines): + maxVine = len(self.vines) - 1 + maxX = self.defaultMaxX + if self.vines: + maxX = self.vines[maxVine].getX() + minVine = self.lastJumpVine - 1 + if minVine < 0: + minVine = 0 + minX = 0 + if self.vines: + minX = self.vines[minVine].getX() + camera.setX(base.localAvatar.getX(render)) + if camera.getX() > maxX: + camera.setX(maxX) + if camera.getX() < minX: + camera.setX(minX) + else: + camera.setPos(self.cameraSidePos) + maxVine = self.lastJumpVine + 1 + if maxVine >= len(self.vines): + maxVine = len(self.vines) - 1 + maxX = self.vines[maxVine].getX() + minVine = self.lastJumpVine - 1 + if minVine < 0: + minVine = 0 + minX = self.vines[minVine].getX() + camera.setX(base.localAvatar.getX(render)) + if camera.getX() > maxX: + camera.setX(maxX) + if camera.getX() < minX: + camera.setX(minX) + + def __updateOtherToonsClimbing(self): + for avId in self.toonInfo.keys(): + if avId == self.localAvId: + continue + toonInfo = self.toonInfo[avId] + vineIndex = toonInfo[0] + if vineIndex == -1: + continue + climbDir = toonInfo[5] + if climbDir == None or climbDir == 0: + continue + curT = toonInfo[1] + dt = globalClock.getDt() + diffT = 0 + baseVerticalRate = self.ToonVerticalRate + if climbDir == -1: + diffT -= baseVerticalRate * dt + if climbDir == 1: + diffT += baseVerticalRate * dt + newT = curT + diffT + if newT > 1: + newT = 1 + if newT < 0: + newT = 0 + if not newT == curT: + toonInfo[1] = newT + self.vines[vineIndex].changeAttachedToonT(avId, newT) + + return + + def __updateLocalToonTask(self, task): + dt = globalClock.getDt() + self.updateRadar() + self.__updateOtherToonsClimbing() + for bat in self.bats: + bat.checkScreech() + + if self.localPhysicsNP: + pos = self.localPhysicsNP.getPos(render) + if pos[2] < 0: + self.handleLocalToonFellDown() + avId = self.localAvId + curInfo = None + for vineIndex in xrange(len(self.vines)): + curInfo = self.vines[vineIndex].getAttachedToonInfo(avId) + if curInfo: + break + + if not curInfo: + if avId not in self.endingTracks: + self.makeCameraFollowJumpingToon() + if self.localPhysicsNP: + pos = self.localPhysicsNP.getPos(render) + else: + curT = curInfo[0] + diffT = 0 + baseVerticalRate = self.ToonVerticalRate + onEndVine = vineIndex == len(self.vines) - 1 + if self.arrowKeys.upPressed() and not onEndVine and self.isInPlayState(): + diffT -= baseVerticalRate * dt + if self.arrowKeys.downPressed() and not onEndVine and self.isInPlayState(): + diffT += baseVerticalRate * dt + newT = curT + diffT + if newT > 1: + newT = 1 + if newT < 0: + newT = 0 + oldClimbDir = self.getClimbDir(avId) + climbDir = 0 + if diffT < 0: + climbDir = -1 + elif diffT > 0: + climbDir = 1 + if not newT == curT: + self.vines[vineIndex].changeAttachedToonT(avId, newT) + if newT != curT or self.getClimbDir(avId) and not curT == 1.0 or oldClimbDir != climbDir: + if oldClimbDir != climbDir: + self.sendNewVineTUpdateAsap = True + self.b_setNewVineT(avId, newT, climbDir) + return task.cont + + def setupCollisions(self, anp): + fromObject = anp.attachNewNode(CollisionNode('colNode')) + fromObject.node().addSolid(CollisionSphere(0, 0, 0, self.LocalPhysicsRadius)) + fromCollideMask = ToontownGlobals.PieBitmask + self.notify.debug('fromCollideMask = %s' % fromCollideMask) + fromObject.node().setFromCollideMask(fromCollideMask) + self.handler = CollisionHandlerEvent() + self.handler.addInPattern('%fn-into') + base.cTrav.setRespectPrevTransform(True) + base.cTrav.addCollider(fromObject, self.handler) + eventName = '%s-into' % fromObject.getName() + self.accept(eventName, self.swingVineEnter) + height = base.localAvatar.getHeight() + self.treasureSphereName = 'treasureCollider' + center = Point3(0, 0, 0) + radius = VineTreasure.VineTreasure.RADIUS + self.treasureCollSphere = CollisionSphere(center[0], center[1], center[2], radius) + self.treasureCollSphere.setTangible(0) + self.treasureCollNode = CollisionNode(self.treasureSphereName) + self.treasureCollNode.setFromCollideMask(ToontownGlobals.WallBitmask) + self.treasureCollNode.addSolid(self.treasureCollSphere) + self.treasureCollNodePath = self.torso.attachNewNode(self.treasureCollNode) + self.treasureHandler = CollisionHandlerEvent() + self.treasureHandler.addInPattern('%fn-intoTreasure') + base.cTrav.addCollider(self.treasureCollNodePath, self.treasureHandler) + eventName = '%s-intoTreasure' % self.treasureCollNodePath.getName() + self.notify.debug('eventName = %s' % eventName) + self.accept(eventName, self.treasureEnter) + + def getFocusCameraPos(self, vineIndex, facingRight): + retPos = Point3(0, 0, 0) + if self.TwoVineView: + pos = self.vines[vineIndex].getPos() + retPos = Point3(self.cameraTwoVineSidePos) + if vineIndex == 0: + nextVinePos = self.vines[vineIndex + 1].getPos() + newX = (pos.getX() + nextVinePos.getX()) / 2.0 + newX -= self.cameraTwoVineAdj + retPos.setX(newX) + elif vineIndex == VineGameGlobals.NumVines - 1: + nextVinePos = self.vines[vineIndex].getPos() + nextVinePos.setX(nextVinePos.getX() + VineGameGlobals.VineXIncrement) + newX = (pos.getX() + nextVinePos.getX()) / 2.0 + newX += self.cameraTwoVineAdj + retPos.setX(newX) + else: + otherVineIndex = vineIndex - 1 + if self.lastJumpFacingRight: + otherVineIndex = vineIndex + 1 + nextVinePos = self.vines[otherVineIndex].getPos() + newX = (pos.getX() + nextVinePos.getX()) / 2.0 + if self.lastJumpFacingRight: + newX -= self.cameraTwoVineAdj + else: + newX += self.cameraTwoVineAdj + retPos.setX(newX) + else: + pos = self.vines[vineIndex].getPos() + retPos.setX(pos.getX()) + self.notify.debug('getFocusCameraPos returning %s' % retPos) + return retPos + + def focusCameraOnVine(self, vineIndex): + camera.setHpr(0, 0, 0) + newCameraPos = self.getFocusCameraPos(vineIndex, self.lastJumpFacingRight) + camera.setPos(newCameraPos) + + def attachLocalToonToVine(self, vineIndex, vineT, focusCameraImmediately = True, playSfx = False): + self.notify.debug('focusCameraImmediately = %s' % focusCameraImmediately) + if playSfx: + self.catchSound.play() + self.clearLocalPhysics() + vine = self.vines[vineIndex] + self.lastJumpVine = -1 + self.lastJumpPos = None + self.lastJumpT = 0 + if focusCameraImmediately: + self.focusCameraOnVine(vineIndex) + self.b_setNewVine(self.localAvId, vineIndex, vineT, self.lastJumpFacingRight) + return + + def setupJumpingTransitionIval(self, vineIndex): + self.doingJumpTransition = False + self.localToonJumpAnimIval = self.createJumpingTransitionIval(vineIndex) + if self.localToonJumpAnimIval: + self.doingJumpTransition = True + + def createJumpingTransitionIval(self, vineIndex): + retval = None + curInfo = self.vines[vineIndex].getAttachedToonInfo(base.localAvatar.doId) + if curInfo: + swingSeq = curInfo[6] + if swingSeq: + curFrame = -1 + for i in xrange(len(swingSeq)): + self.notify.debug('testing actor interval i=%d' % i) + actorIval = swingSeq[i] + if not actorIval.isStopped(): + testFrame = actorIval.getCurrentFrame() + self.notify.debug('actor ival is not stopped, testFrame=%f' % testFrame) + if testFrame: + curFrame = testFrame + break + + if curFrame > -1: + duration = 0.25 + if curFrame > self.ToonMaxRightFrame: + desiredFps = abs(self.JumpingFrame - curFrame) / duration + playRate = desiredFps / 24.0 + retval = ActorInterval(base.localAvatar, 'swing', startFrame=curFrame, endFrame=self.JumpingFrame, playRate=playRate) + else: + numFrames = curFrame + 1 + numFrames += SwingVine.SwingVine.MaxNumberOfFramesInSwingAnim - self.JumpingFrame + 1 + desiredFps = numFrames / duration + playRate = desiredFps / 24.0 + toonJump1 = ActorInterval(base.localAvatar, 'swing', startFrame=curFrame, endFrame=0, playRate=playRate) + toonJump2 = ActorInterval(base.localAvatar, 'swing', startFrame=SwingVine.SwingVine.MaxNumberOfFramesInSwingAnim - 1, endFrame=self.JumpingFrame, playRate=playRate) + retval = Sequence(toonJump1, toonJump2) + return retval + + def detachLocalToonFromVine(self, vineIndex, facingRight): + vine = self.vines[vineIndex] + self.lastJumpVine = vineIndex + self.lastJumpTime = self.getCurrentGameTime() + self.curFrame = base.localAvatar.getCurrentFrame() + curInfo = vine.getAttachedToonInfo(base.localAvatar.doId) + if curInfo: + self.lastJumpPos = curInfo[1] + self.lastJumpT = curInfo[0] + else: + self.lastJumpPos = None + self.lastJumpT = 0 + self.notify.warning('vine %d failed get tooninfo %d' % (vineIndex, base.localAvatar.doId)) + self.setupJumpingTransitionIval(vineIndex) + self.vines[vineIndex].detachToon(base.localAvatar.doId) + return + + def treasureEnter(self, entry): + self.notify.debug('---- treasure Enter ---- ') + self.notify.debug('%s' % entry) + name = entry.getIntoNodePath().getName() + parts = name.split('-') + if len(parts) < 3: + self.notify.debug('collided with %s, but returning' % name) + return + if not int(parts[1]) == self.doId: + self.notify.debug("collided with %s, but doId doesn't match" % name) + return + treasureNum = int(parts[2]) + self.__treasureGrabbed(treasureNum) + + def swingVineEnter(self, entry): + self.notify.debug('%s' % entry) + self.notify.debug('---- swingVine Enter ---- ') + name = entry.getIntoNodePath().getName() + parts = name.split('-') + if len(parts) < 3: + self.notify.debug('collided with %s, but returning' % name) + return + vineIndex = int(parts[1]) + if vineIndex < 0 or vineIndex >= len(self.vines): + self.notify.warning('invalid vine index %d' % vineIndex) + return + if vineIndex == self.lastJumpVine: + if self.lastJumpPos: + diff = self.lastJumpPos - entry.getSurfacePoint(render) + if diff.length() < self.LocalPhysicsRadius: + return + if self.getCurrentGameTime() - self.lastJumpTime < VineGameGlobals.JumpTimeBuffer: + return + fallingInfo = self.toonInfo[self.localAvId][8] + if fallingInfo == self.FallingSpider or fallingInfo == self.FallingBat: + return + if abs(self.lastJumpVine - vineIndex) > 1: + return + vine = self.vines[vineIndex] + vineT = vine.calcTFromTubeHit(entry) + self.attachLocalToonToVine(vineIndex, vineT, False, playSfx=True) + self.setupAttachingToVineCamIval(vineIndex, self.lastJumpFacingRight) + + def makeOtherToonJump(self, avId, posX, posZ, velX, velZ): + if avId not in self.otherToonPhysics: + an = ActorNode('other-physics%s' % avId) + anp = render.attachNewNode(an) + base.physicsMgr.attachPhysicalNode(an) + self.otherToonPhysics[avId] = (an, anp) + an, anp = self.otherToonPhysics[avId] + anp.setPos(posX, 0, posZ) + av = base.cr.doId2do.get(avId) + if av: + av.reparentTo(anp) + av.setPos(-self.toonOffsets[self.localAvId]) + av.pose('swing', self.JumpingFrame) + self.notify.debug('pose set to swing jumping frame.') + if velX >= 0: + anp.setH(-90) + else: + anp.setH(90) + physObject = an.getPhysicsObject() + physObject.setVelocity(velX, 0, velZ) + + def makeOtherToonFallFromMidair(self, avId, posX, posZ, velX, velZ): + if avId not in self.otherToonPhysics: + an = ActorNode('other-physics%s' % avId) + anp = render.attachNewNode(an) + base.physicsMgr.attachPhysicalNode(an) + self.otherToonPhysics[avId] = (an, anp) + an, anp = self.otherToonPhysics[avId] + anp.setPos(posX, 0, posZ) + av = base.cr.doId2do.get(avId) + if av: + av.reparentTo(anp) + av.setPos(-self.toonOffsets[self.localAvId]) + if velX >= 0: + anp.setH(-90) + else: + anp.setH(90) + physObject = an.getPhysicsObject() + physObject.setVelocity(velX, 0, velZ) + + def makeLocalToonJump(self, vineIndex, t, pos, normal): + self.jumpSound.play() + self.clearChangeFacingInterval() + self.clearAttachingToVineCamIval() + an = ActorNode('av-physics') + anp = render.attachNewNode(an) + anp.setPos(pos) + base.localAvatar.reparentTo(anp) + base.localAvatar.setPos(-self.toonOffsets[self.localAvId]) + if normal.getX() >= 0: + self.lastJumpFacingRight = True + anp.setH(-90) + else: + anp.setH(90) + self.lastJumpFacingRight = False + base.physicsMgr.attachPhysicalNode(an) + physObject = an.getPhysicsObject() + velocity = normal * self.JumpSpeed + velocity *= t + self.notify.debug('jumping from vine with velocity of %s' % velocity) + physObject.setVelocity(velocity) + self.localPhysicsNP = anp + self.setupCollisions(anp) + if self.doingJumpTransition: + self.localToonJumpAnimIval.start() + else: + base.localAvatar.pose('swing', self.JumpingFrame) + self.b_setJumpingFromVine(self.localAvId, vineIndex, self.lastJumpFacingRight, pos[0], pos[2], velocity[0], velocity[2]) + + def makeLocalToonFallFromVine(self, fallingInfo): + self.clearAttachingToVineCamIval() + self.clearChangeFacingInterval() + vineIndex, vineInfo = self.getVineAndVineInfo(self.localAvId) + if vineIndex == -1: + self.notify.warning('we are not attached to a vine') + return + pos = vineInfo[1] + self.detachLocalToonFromVine(vineIndex, self.lastJumpFacingRight) + self.clearChangeFacingInterval() + an = ActorNode('av-physics') + anp = render.attachNewNode(an) + anp.setPos(vineInfo[1]) + base.localAvatar.reparentTo(anp) + base.localAvatar.setPos(-self.toonOffsets[self.localAvId]) + if self.lastJumpFacingRight: + anp.setH(-90) + else: + anp.setH(90) + base.physicsMgr.attachPhysicalNode(an) + physObject = an.getPhysicsObject() + velocity = Vec3(0, 0, -0.1) + physObject.setVelocity(velocity) + self.localPhysicsNP = anp + self.b_setFallingFromVine(self.localAvId, vineIndex, self.lastJumpFacingRight, pos[0], pos[2], velocity[0], velocity[2], fallingInfo) + + def makeLocalToonFallFromMidair(self, fallingInfo): + vineIndex, vineInfo = self.getVineAndVineInfo(self.localAvId) + if not vineIndex == -1: + self.notify.warning(' makeLocalToonFallFromMidair we are still attached to a vine') + return + if not self.localPhysicsNP: + self.notify.warning('self.localPhysicsNP is invalid') + return + pos = self.localPhysicsNP.getPos() + an = self.localPhysicsNP.node() + physObject = an.getPhysicsObject() + velocity = Vec3(0, 0, -0.1) + physObject.setVelocity(velocity) + self.b_setFallingFromMidair(self.localAvId, self.lastJumpFacingRight, pos[0], pos[2], velocity[0], velocity[2], fallingInfo) + + def changeLocalToonFacing(self, vineIndex, swingVineInfo, newFacingRight): + self.lastJumpFacingRight = newFacingRight + self.attachLocalToonToVine(vineIndex, swingVineInfo[0], focusCameraImmediately=False) + self.setupChangeFacingInterval(vineIndex, newFacingRight) + + def upArrowKeyHandler(self): + self.sendNewVineTUpdateAsap = True + + def downArrowKeyHandler(self): + self.sendNewVineTUpdateAsap = True + + def rightArrowKeyHandler(self): + curInfo = None + for vineIndex in xrange(len(self.vines)): + curInfo = self.vines[vineIndex].getAttachedToonInfo(base.localAvatar.doId) + if curInfo: + break + + if not curInfo: + return + if vineIndex == len(self.vines) - 1: + return + if not self.isInPlayState(): + return + doJump = True + if self.ArrowToChangeFacing: + if not self.lastJumpFacingRight: + doJump = False + self.changeLocalToonFacing(vineIndex, curInfo, True) + if doJump: + self.detachLocalToonFromVine(vineIndex, 1) + normal = curInfo[2] + self.makeLocalToonJump(vineIndex, curInfo[0], curInfo[1], normal) + return + + def leftArrowKeyHandler(self): + curInfo = None + for vineIndex in xrange(len(self.vines)): + curInfo = self.vines[vineIndex].getAttachedToonInfo(base.localAvatar.doId) + if curInfo: + break + + curInfo = self.vines[vineIndex].getAttachedToonInfo(base.localAvatar.doId) + if not curInfo: + return + if vineIndex == 0: + return + if vineIndex == len(self.vines) - 1: + return + if not self.isInPlayState(): + return + doJump = True + if self.ArrowToChangeFacing: + if self.lastJumpFacingRight: + doJump = False + self.changeLocalToonFacing(vineIndex, curInfo, False) + if doJump: + self.detachLocalToonFromVine(vineIndex, 0) + normal = curInfo[2] + normal *= -1 + self.makeLocalToonJump(vineIndex, curInfo[0], curInfo[1], normal) + return + + def b_setNewVine(self, avId, vineIndex, vineT, facingRight): + self.setNewVine(avId, vineIndex, vineT, facingRight) + self.d_setNewVine(avId, vineIndex, vineT, facingRight) + + def d_setNewVine(self, avId, vineIndex, vineT, facingRight): + self.notify.debug('setNewVine avId=%d vineIndex=%s' % (avId, vineIndex)) + self.sendUpdate('setNewVine', [avId, + vineIndex, + vineT, + facingRight]) + + def setNewVine(self, avId, vineIndex, vineT, facingRight): + self.updateToonInfo(avId, vineIndex=vineIndex, vineT=vineT, facingRight=facingRight, climbDir=0, fallingInfo=self.FallingNot) + + def b_setNewVineT(self, avId, vineT, climbDir): + self.setNewVineT(avId, vineT, climbDir) + self.d_setNewVineT(avId, vineT, climbDir) + + def d_setNewVineT(self, avId, vineT, climbDir): + sendIt = False + curTime = self.getCurrentGameTime() + if self.sendNewVineTUpdateAsap: + sendIt = True + elif curTime - self.lastNewVineTUpdate > 0.2: + sendIt = True + if sendIt: + self.sendUpdate('setNewVineT', [avId, vineT, climbDir]) + self.sendNewVineTUpdateAsap = False + self.lastNewVineTUpdate = self.getCurrentGameTime() + + def setNewVineT(self, avId, vineT, climbDir): + self.updateToonInfo(avId, vineT=vineT, climbDir=climbDir) + + def b_setJumpingFromVine(self, avId, vineIndex, facingRight, posX, posZ, velX, velZ): + self.setJumpingFromVine(avId, vineIndex, facingRight, posX, posZ, velX, velZ) + self.d_setJumpingFromVine(avId, vineIndex, facingRight, posX, posZ, velX, velZ) + + def d_setJumpingFromVine(self, avId, vineIndex, facingRight, posX, posZ, velX, velZ): + self.sendUpdate('setJumpingFromVine', [avId, + vineIndex, + facingRight, + posX, + posZ, + velX, + velZ]) + + def setJumpingFromVine(self, avId, vineIndex, facingRight, posX, posZ, velX, velZ): + self.updateToonInfo(avId, vineIndex=-1, facingRight=facingRight, posX=posX, posZ=posZ, velX=velX, velZ=velZ) + + def b_setFallingFromVine(self, avId, vineIndex, facingRight, posX, posZ, velX, velZ, fallingInfo): + self.setFallingFromVine(avId, vineIndex, facingRight, posX, posZ, velX, velZ, fallingInfo) + self.d_setFallingFromVine(avId, vineIndex, facingRight, posX, posZ, velX, velZ, fallingInfo) + + def d_setFallingFromVine(self, avId, vineIndex, facingRight, posX, posZ, velX, velZ, fallingInfo): + self.sendUpdate('setFallingFromVine', [avId, + vineIndex, + facingRight, + posX, + posZ, + velX, + velZ, + fallingInfo]) + + def setFallingFromVine(self, avId, vineIndex, facingRight, posX, posZ, velX, velZ, fallingInfo): + self.updateToonInfo(avId, vineIndex=-1, facingRight=facingRight, posX=posX, posZ=posZ, velX=velX, velZ=velZ, fallingInfo=fallingInfo) + + def b_setFallingFromMidair(self, avId, facingRight, posX, posZ, velX, velZ, fallingInfo): + self.setFallingFromMidair(avId, facingRight, posX, posZ, velX, velZ, fallingInfo) + self.d_setFallingFromMidair(avId, facingRight, posX, posZ, velX, velZ, fallingInfo) + + def d_setFallingFromMidair(self, avId, facingRight, posX, posZ, velX, velZ, fallingInfo): + self.sendUpdate('setFallingFromMidair', [avId, + facingRight, + posX, + posZ, + velX, + velZ, + fallingInfo]) + + def setFallingFromMidair(self, avId, facingRight, posX, posZ, velX, velZ, fallingInfo): + self.updateToonInfo(avId=avId, facingRight=facingRight, posX=posX, posZ=posZ, velX=velX, velZ=velZ, fallingInfo=fallingInfo) + + def d_setFallingPos(self, avId, posX, posZ): + self.sendUpdate('setFallingPos', [avId, posX, posZ]) + + def setFallingPos(self, avId, posX, posZ): + self.updateToonInfo(avId, posX=posX, posZ=posZ) + + def __treasureGrabbed(self, treasureNum): + self.treasures[treasureNum].showGrab() + self.grabSound.play() + self.sendUpdate('claimTreasure', [treasureNum]) + + def setTreasureGrabbed(self, avId, treasureNum): + if not self.hasLocalToon: + return + self.notify.debug('treasure %s grabbed by %s' % (treasureNum, avId)) + if avId != self.localAvId: + self.treasures[treasureNum].showGrab() + i = self.avIdList.index(avId) + self.scores[i] += 1 + self.scorePanels[i].setScore(self.scores[i]) + + def setScore(self, avId, score): + if not self.hasLocalToon: + return + i = self.avIdList.index(avId) + if hasattr(self, 'scorePanels'): + self.scores[i] += score + self.scorePanels[i].setScore(score) + + def timerExpired(self): + self.notify.debug('game timer expired') + if not VineGameGlobals.EndlessGame: + if hasattr(self, 'gameFSM'): + self.gameFSM.request('showScores') + + def allAtEndVine(self): + if not self.hasLocalToon: + return + self.notify.debug('all at end vine') + if not VineGameGlobals.EndlessGame: + self.gameFSM.request('showScores') + + def clearChangeFacingInterval(self): + if self.changeFacingInterval: + self.changeFacingInterval.pause() + del self.changeFacingInterval + self.changeFacingInterval = None + return + + def setupChangeFacingInterval(self, vineIndex, newFacingRight): + self.clearChangeFacingInterval() + self.changeFacingInterval = Sequence() + if not (vineIndex == 0 or vineIndex == VineGameGlobals.NumVines - 1): + destPos = self.getFocusCameraPos(vineIndex, newFacingRight) + self.changeFacingInterval.append(LerpPosInterval(base.camera, 0.5, destPos)) + self.changeFacingInterval.append(Func(self.clearChangeFacingInterval)) + self.changeFacingInterval.start() + + def clearAttachingToVineCamIval(self): + if self.attachingToVineCamIval: + self.attachingToVineCamIval.pause() + del self.attachingToVineCamIval + self.attachingToVineCamIval = None + return + + def setupAttachingToVineCamIval(self, vineIndex, facingRight): + self.clearAttachingToVineCamIval() + self.attachingToVineCamIval = Sequence() + destPos = self.getFocusCameraPos(vineIndex, facingRight) + self.attachingToVineCamIval.append(LerpPosInterval(base.camera, 0.5, destPos)) + self.attachingToVineCamIval.append(Func(self.clearAttachingToVineCamIval)) + self.attachingToVineCamIval.start() + + def createRadar(self): + self.cCamera = render.attachNewNode('cCamera') + self.cCamNode = Camera('cCam') + self.cCamNode.setCameraMask(VineGameGlobals.RadarCameraBitmask) + self.cLens = PerspectiveLens() + xFov = 40 + yFov = 2.5 + self.cLens.setFov(xFov, yFov) + self.cLens.setNear(0.1) + self.cLens.setFar(1300.0) + self.cCamNode.setLens(self.cLens) + self.cCamNode.setScene(render) + self.cCam = self.cCamera.attachNewNode(self.cCamNode) + self.cCam.setPos(300, -850, 16.3) + endY = yFov / xFov + self.endZRadar = 0.09375 + self.cDr = base.win.makeDisplayRegion(0, 1, 0, self.endZRadar) + self.cDr.setSort(1) + self.cDr.setClearDepthActive(1) + self.cDr.setClearColorActive(1) + self.cDr.setClearColor(Vec4(0.85, 0.95, 0.95, 1)) + self.cDr.setCamera(self.cCam) + self.radarSeparator = DirectFrame(relief=None, image=DGG.getDefaultDialogGeom(), image_color=(0.2, 0.0, 0.8, 1), image_scale=(2.65, 1.0, 0.01), pos=(0, 0, -0.8125)) + self.oldBaseCameraMask = base.camNode.getCameraMask() + base.camNode.setCameraMask(BitMask32.bit(0)) + return + + def destroyRadar(self): + base.win.removeDisplayRegion(self.cDr) + self.cCamera.removeNode() + del self.cCamera + del self.cCamNode + del self.cLens + del self.cCam + del self.cDr + self.radarSeparator.destroy() + del self.radarSeparator + base.camNode.setCameraMask(self.oldBaseCameraMask) + + def updateRadar(self): + for avId in self.headFrames: + av = base.cr.doId2do.get(avId) + headFrameShown = False + if av: + avPos = av.getPos(render) + newPoint = self.mapRadarToAspect2d(render, avPos) + if newPoint: + headFrameShown = True + self.headFrames[avId].setPos(newPoint[0], newPoint[1], newPoint[2]) + if headFrameShown: + self.headFrames[avId].show() + else: + self.headFrames[avId].hide() + + def mapRadarToAspect2d(self, node, point): + if point[0] > 26: + pass + p3 = self.cCam.getRelativePoint(node, point) + p2 = Point2() + if not self.cLens.project(p3, p2): + return None + r2d = Point3(p2[0], 0, p2[1]) + a2d = aspect2d.getRelativePoint(render2d, r2d) + zAspect2DRadar = self.endZRadar * 2.0 - 1 + oldZ = a2d.getZ() + newZ = (oldZ + 1) / 2.0 * (zAspect2DRadar + 1) + newZ -= 1 + a2d.setZ(newZ) + return a2d + + def localToonHitSpider(self, colEntry): + self.notify.debug('toonHitSpider') + if self.toonInfo[self.localAvId][0] == -1: + fallingInfo = self.toonInfo[self.localAvId][8] + if not (fallingInfo == self.FallingBat or fallingInfo == self.FallingSpider): + self.spiderHitSound.play() + self.makeLocalToonFallFromMidair(self.FallingSpider) + else: + self.spiderHitSound.play() + self.makeLocalToonFallFromVine(self.FallingSpider) + + def localToonHitBat(self, colEntry): + self.notify.debug('toonHitBat') + if self.toonInfo[self.localAvId][0] == VineGameGlobals.NumVines - 1: + return + if self.toonInfo[self.localAvId][0] == -1: + self.batHitMidairSound.play() + fallingInfo = self.toonInfo[self.localAvId][8] + if not (fallingInfo == self.FallingBat or fallingInfo == self.FallingSpider): + self.makeLocalToonFallFromMidair(self.FallingBat) + else: + self.batHitVineSound.play() + self.makeLocalToonFallFromVine(self.FallingBat) + + def toonHitSomething(self, colEntry): + if not self.isInPlayState(): + return + intoName = colEntry.getIntoNodePath().getName() + if 'spider' in intoName: + self.localToonHitSpider(colEntry) + elif 'bat' in intoName: + self.localToonHitBat(colEntry) + + def setVineSections(self, vineSections): + self.vineSections = vineSections + + def setupVineCourse(self): + vineIndex = 0 + for section in self.vineSections: + for vineInfo in VineGameGlobals.CourseSections[section]: + length, baseAngle, vinePeriod, spiderPeriod = vineInfo + posX = vineIndex * VineGameGlobals.VineXIncrement + newVine = SwingVine.SwingVine(vineIndex, posX, 0, VineGameGlobals.VineHeight, length=length, baseAngle=baseAngle, period=vinePeriod, spiderPeriod=spiderPeriod) + self.vines.append(newVine) + vineIndex += 1 + + def loadBats(self): + self.bats = [] + szId = self.getSafezoneId() + self.batInfo = VineGameGlobals.BatInfo[szId] + batIndex = 0 + for batTuple in self.batInfo: + newBat = VineBat.VineBat(batIndex, self.batInfo[batIndex][0]) + xPos = VineGameGlobals.VineXIncrement * VineGameGlobals.NumVines + 100 + newBat.setX(xPos) + self.bats.append(newBat) + batIndex += 1 + + def createBatIvals(self): + self.batIvals = [] + for batIndex in xrange(len(self.bats)): + newBatIval = self.createBatIval(batIndex) + self.batIvals.append(newBatIval) + + def startBatIvals(self): + for batIval in self.batIvals: + batIval.start() + + def destroyBatIvals(self): + for batIval in self.batIvals: + batIval.finish() + + self.batIvals = [] + + def createBatIval(self, batIndex): + timeToTraverseField = self.batInfo[batIndex][0] + initialDelay = self.batInfo[batIndex][1] + startMultiplier = 1 + if len(self.batInfo[batIndex]) >= 3: + startMultiplier = 0.25 + batIval = Sequence() + batIval.append(Wait(initialDelay)) + batIval.append(Func(self.bats[batIndex].startFlying)) + startX = VineGameGlobals.VineXIncrement * VineGameGlobals.NumVines + endX = -VineGameGlobals.VineXIncrement + firstInterval = True + while batIval.getDuration() < VineGameGlobals.GameDuration: + batHeight = self.randomNumGen.randrange(VineGameGlobals.BatMinHeight, VineGameGlobals.BatMaxHeight) + batIval.append(Func(self.bats[batIndex].startLap)) + if firstInterval: + newIval = LerpPosInterval(self.bats[batIndex], duration=timeToTraverseField * startMultiplier, pos=Point3(endX, 0, batHeight), startPos=Point3(startX * startMultiplier, 0, batHeight)) + else: + newIval = LerpPosInterval(self.bats[batIndex], duration=timeToTraverseField, pos=Point3(endX, 0, batHeight), startPos=Point3(startX, 0, batHeight)) + batIval.append(newIval) + firstInterval = False + + batIval.append(Func(self.bats[batIndex].stopFlying)) + return batIval + + def isInPlayState(self): + if not self.gameFSM.getCurrentState(): + return False + if not self.gameFSM.getCurrentState().getName() == 'play': + return False + return True + + def getIntroTrack(self): + retval = Sequence() + toonTakeoffs = Parallel() + didCameraMove = False + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + if avId != self.localAvId: + continue + oneSeq = Sequence() + oneSeqAndHowl = Parallel() + av = base.cr.doId2do.get(avId) + if av: + toonOffset = self.calcToonOffset(av) + platformPos = self.startPlatform.getPos() + endPos = self.vines[0].getPos() + endPos.setZ(endPos.getZ() - toonOffset.getZ() - VineGameGlobals.VineStartingT * self.vines[0].cableLength) + xPos = platformPos[0] - 0.5 + takeOffPos = Point3(xPos, platformPos[1], platformPos[2]) + leftPos = Point3(xPos - 27, platformPos[1], platformPos[2]) + self.notify.debug('leftPos = %s platformPos=%s' % (leftPos, platformPos)) + startRunningPos = Point3(takeOffPos) + startRunningPos.setX(startRunningPos.getX() - 7) + oneSeq.append(Func(av.dropShadow.show)) + oneSeq.append(Func(av.setH, -90)) + oneSeq.append(Func(av.setPos, takeOffPos[0], takeOffPos[1], takeOffPos[2])) + exclamationSfx = av.getDialogueSfx('exclamation', 0) + oneSeq.append(Parallel(ActorInterval(av, 'confused', duration=3))) + questionSfx = av.getDialogueSfx('question', 0) + oneSeq.append(Parallel(ActorInterval(av, 'think', duration=3.0), SoundInterval(questionSfx, duration=3))) + oneSeq.append(Func(av.setH, 90)) + oneSeq.append(Func(av.setPlayRate, 1, 'walk')) + oneSeq.append(Func(av.loop, 'walk')) + oneSeq.append(Parallel(LerpPosInterval(av, pos=leftPos, duration=5.25), SoundInterval(av.soundWalk, loop=1, duration=5.25))) + oneSeq.append(Func(av.setH, -90)) + oneSeq.append(Func(av.loop, 'run')) + oneSeq.append(Parallel(LerpPosInterval(av, pos=takeOffPos, duration=2.5), SoundInterval(av.soundRun, loop=1, duration=2.5))) + oneSeq.append(Func(av.dropShadow.hide)) + howlTime = oneSeq.getDuration() + oneSeq.append(Parallel(LerpPosInterval(av, pos=endPos, duration=0.5), Func(av.pose, 'swing', self.JumpingFrame))) + attachingToVineSeq = Sequence(ActorInterval(av, 'swing', startFrame=self.JumpingFrame, endFrame=143, playRate=2.0), ActorInterval(av, 'swing', startFrame=0, endFrame=86, playRate=2.0)) + attachingToVine = Parallel(attachingToVineSeq, SoundInterval(self.catchSound)) + if didCameraMove: + oneSeq.append(attachingToVine) + else: + attachAndMoveCam = Parallel(attachingToVine, LerpPosInterval(base.camera, pos=self.getFocusCameraPos(0, True), duration=2)) + oneSeq.append(attachAndMoveCam) + oneSeq.append(Func(av.setPos, 0, 0, 0)) + oneSeq.append(Func(av.setH, 0)) + oneSeq.append(Func(self.vines[0].attachToon, avId, VineGameGlobals.VineStartingT, self.lastJumpFacingRight, setupAnim=False)) + oneSeq.append(Func(self.vines[0].updateAttachedToons)) + howlSfx = av.getDialogueSfx('special', 0) + howlSeq = Sequence(Wait(howlTime), SoundInterval(howlSfx)) + exclamationSeq = Sequence(Wait(0.5), SoundInterval(exclamationSfx)) + oneSeqAndHowl = Parallel(oneSeq, howlSeq, exclamationSeq) + toonTakeoffs.append(oneSeqAndHowl) + + retval.append(toonTakeoffs) + return retval + + def doEndingTrackTask(self, avId): + taskName = 'VineGameEnding-%s' % avId + if avId not in self.endingTracks: + taskMgr.doMethodLater(0.5, self.setupEndingTrack, taskName, extraArgs=(avId,)) + self.endingTrackTaskNames.append(taskName) + + def debugCameraPos(self): + self.notify.debug('cameraPos = %s' % base.camera.getPos()) + + def setupEndingTrack(self, avId): + if avId in self.endingTracks: + self.notify.warning('setupEndingTrack duplicate call avId=%d' % avId) + return + if len(self.vines) == 0: + return + endVine = VineGameGlobals.NumVines - 1 + platformPos = self.endPlatform.getPos() + avIndex = self.avIdList.index(avId) + landingPos = Point3(platformPos) + landingPos.setX(landingPos.getX() + avIndex * 2) + endingTrack = Sequence() + av = base.cr.doId2do.get(avId) + avPos = av.getPos(render) + cameraPos = base.camera.getPos() + self.notify.debug('avPos=%s cameraPos=%s' % (avPos, base.camera.getPos())) + if av: + midX = landingPos[0] + midZ = platformPos[2] + 6 + jumpingTransition = self.createJumpingTransitionIval(endVine) + endingTrack.append(Func(self.vines[endVine].detachToon, avId)) + endingTrack.append(Func(av.wrtReparentTo, render)) + endingTrack.append(Func(self.debugCameraPos)) + endingTrack.append(Func(av.loop, 'jump-idle')) + landingIval = Parallel(ProjectileInterval(av, startPos=av.getPos(render), endZ=landingPos[2], wayPoint=Point3(midX, 0, midZ), timeToWayPoint=1)) + endingTrack.append(landingIval) + endingTrack.append(Func(av.dropShadow.show)) + endingTrack.append(Func(av.play, 'jump-land')) + endingTrack.append(Func(self.winSound.play)) + endingTrack.append(Func(av.loop, 'victory')) + self.endingTracks[avId] = endingTrack + endingTrack.start() + return Task.done + + def cleanupEndingTracks(self): + for taskName in self.endingTrackTaskNames: + taskMgr.remove(taskName) + + for endingTrack in self.endingTracks.values(): + endingTrack.finish + del endingTrack + + self.endingTracks = {} diff --git a/toontown/minigame/DistributedVineGameAI.py b/toontown/minigame/DistributedVineGameAI.py new file mode 100755 index 00000000..17f41116 --- /dev/null +++ b/toontown/minigame/DistributedVineGameAI.py @@ -0,0 +1,320 @@ +from DistributedMinigameAI import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import VineGameGlobals + +class DistributedVineGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedVineGameAI_initialized + except: + self.DistributedVineGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedVineGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), + State.State('play', self.enterPlay, self.exitPlay, ['cleanup', 'waitShowScores']), + State.State('waitShowScores', self.enterWaitShowScores, self.exitWaitShowScores, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.toonInfo = {} + self.addChildGameFSM(self.gameFSM) + self.vineSections = [] + self.finishedBonus = {} + self.finishedTimeLeft = {} + self.totalSpiders = 0 + self.calculatedPartialBeans = False + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def _playing(self): + if not hasattr(self, 'gameFSM'): + return False + if self.gameFSM.getCurrentState() == None: + return False + return self.gameFSM.getCurrentState().getName() == 'play' + + def setGameReady(self): + self.notify.debug('setGameReady') + for avId in self.avIdList: + self.updateToonInfo(avId, vineIndex=0, vineT=VineGameGlobals.VineStartingT) + + DistributedMinigameAI.setGameReady(self) + self.numTreasures = VineGameGlobals.NumVines - 1 + self.numTreasuresTaken = 0 + self.takenTable = [0] * self.numTreasures + for avId in self.scoreDict.keys(): + self.scoreDict[avId] = 0 + self.finishedBonus[avId] = 0 + self.finishedTimeLeft[avId] = -1 + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + vineReached = [] + scoreList = [] + finishedList = [] + timeLeftList = [] + for avId in self.avIdList: + vineReached.append(self.toonInfo[avId][0]) + scoreList.append(self.scoreDict[avId]) + finishedList.append(self.finishedBonus[avId]) + timeLeftList.append(self.finishedTimeLeft[avId]) + + totalBats = len(VineGameGlobals.BatInfo[self.getSafezoneId()]) + self.air.writeServerEvent('minigame_vine', self.doId, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' % (ToontownGlobals.VineGameId, + self.getSafezoneId(), + self.avIdList, + scoreList, + self.vineSections, + finishedList, + timeLeftList, + vineReached, + self.totalSpiders, + totalBats)) + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.vines = [] + index = 0 + taskMgr.doMethodLater(VineGameGlobals.GameDuration, self.timerExpired, self.taskName('gameTimer')) + + def exitPlay(self): + taskMgr.remove(self.taskName('gameTimer')) + for vine in self.vines: + vine.requestDelete() + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def claimTreasure(self, treasureNum): + if not self._playing(): + return + avId = self.air.getAvatarIdFromSender() + if avId not in self.scoreDict: + self.notify.warning('PROBLEM: avatar %s called claimTreasure(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId, + treasureNum, + self.scoreDict, + self.avIdList)) + return + if treasureNum < 0 or treasureNum >= self.numTreasures: + self.air.writeServerEvent('warning', treasureNum, 'MazeGameAI.claimTreasure treasureNum out of range') + return + if self.takenTable[treasureNum]: + return + self.takenTable[treasureNum] = 1 + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('setTreasureGrabbed', [avId, treasureNum]) + self.scoreDict[avId] += 1 + self.numTreasuresTaken += 1 + + def timerExpired(self, task): + self.notify.debug('timer expired') + if not VineGameGlobals.EndlessGame: + self.gameFSM.request('waitShowScores') + return Task.done + + def enterWaitShowScores(self): + self.notify.debug('enterWaitShowScores') + self.awardPartialBeans() + taskMgr.doMethodLater(VineGameGlobals.ShowScoresDuration, self.__doneShowingScores, self.taskName('waitShowScores')) + + def __doneShowingScores(self, task): + self.notify.debug('doneShowingScores') + self.gameOver() + return Task.done + + def exitWaitShowScores(self): + taskMgr.remove(self.taskName('waitShowScores')) + + def reachedEndVine(self, vineIndex): + self.notify.debug('reachedEndVine') + return + avId = self.air.getAvatarIdFromSender() + oldVineIndex = self.toonInfo[avId][0] + self.updateToonInfo(avId, vineIndex=vineIndex) + if not oldVineIndex == vineIndex: + self.checkForEndVine(avId) + self.checkForEndGame() + + def setNewVine(self, avId, vineIndex, vineT, facingRight): + self.notify.debug('setNewVine') + if not self._playing(): + return + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'VineGameAI.setNewVine: invalid avId') + return + oldVineIndex = self.toonInfo[avId][0] + debugStr = 'setNewVine doId=%s avId=%d vineIndex=%s oldVineIndex=%s' % (self.doId, + avId, + vineIndex, + oldVineIndex) + self.updateToonInfo(avId, vineIndex=vineIndex, vineT=vineT, facingRight=facingRight) + if not oldVineIndex == vineIndex: + self.checkForEndVine(avId) + self.checkForEndGame() + + def checkForEndGame(self): + allDone = True + for avId in self.toonInfo: + if not self.toonInfo[avId][0] == VineGameGlobals.NumVines - 1: + allDone = False + break + + if allDone: + if not VineGameGlobals.EndlessGame: + self.awardPartialBeans() + self.sendUpdate('allAtEndVine', []) + self.gameOver() + + def checkForEndVine(self, avId): + if self.toonInfo[avId][0] == VineGameGlobals.NumVines - 1: + curTime = self.getCurrentGameTime() + timeLeft = VineGameGlobals.GameDuration - curTime + self.notify.debug('curTime =%s timeLeft = %s' % (curTime, timeLeft)) + if avId not in self.scoreDict: + self.notify.warning('PROBLEM: avatar %s called claimTreasure(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId, + treasureNum, + self.scoreDict, + self.avIdList)) + return + addBonus = int(VineGameGlobals.BaseBonusOnEndVine[self.getSafezoneId()] + VineGameGlobals.BonusPerSecondLeft * timeLeft) + self.notify.debug('addBOnus = %d' % addBonus) + if addBonus < 0: + addBonus = 0 + self.finishedBonus[avId] = addBonus + timeLeftStr = '%.1f' % timeLeft + self.finishedTimeLeft[avId] = timeLeftStr + self.scoreDict[avId] += addBonus + self.sendUpdate('setScore', [avId, self.scoreDict[avId]]) + + def updateToonInfo(self, avId, vineIndex = None, vineT = None, posX = None, posZ = None, facingRight = None, climbDir = None, velX = None, velZ = None): + newVineIndex = vineIndex + newVineT = vineT + newPosX = posX + newPosZ = posZ + newFacingRight = facingRight + newClimbDir = climbDir + newVelX = velX + newVelZ = velZ + oldInfo = None + if avId in self.toonInfo: + oldInfo = self.toonInfo[avId] + if vineIndex == None: + newVineIndex = oldInfo[0] + if vineT == None: + newVineT = oldInfo[1] + if posX == None: + newPosX = oldInfo[2] + if posZ == None: + newPosZ = oldInfo[3] + if facingRight == None: + newFacingRight = oldInfo[4] + if climbDir == None: + newClimbDir = oldInfo[5] + if velX == None: + newVelX = oldInfo[6] + if velZ == None: + newVelZ = oldInfo[7] + if newVineIndex < -1 or newVineIndex >= VineGameGlobals.NumVines: + newVineIndex = 0 + if newVineT < 0 or newVineT > 1: + pass + if not newFacingRight == 0 and not newFacingRight == 1: + newFacingRight = 1 + if newPosX < -1000 or newPosX > 2000: + newPosX = 0 + if newPosZ < -100 or newPosZ > 1000: + newPosZ = 0 + if newVelX < -1000 or newVelX > 1000: + newVelX = 0 + if newVelZ < -1000 or newVelZ > 1000: + newVelZ = 0 + newInfo = [newVineIndex, + newVineT, + newPosX, + newPosZ, + newFacingRight, + newClimbDir, + newVelX, + newVelZ] + self.toonInfo[avId] = newInfo + return + + def setupVineSections(self): + szId = self.getSafezoneId() + courseWeights = VineGameGlobals.CourseWeights[szId] + pool = [[], + [], + [], + [], + [], + []] + for weights in courseWeights: + section, chances = weights + numSpiders = VineGameGlobals.getNumSpidersInSection(section) + pool[numSpiders] += [section] * chances + + maxSpiders = VineGameGlobals.SpiderLimits[szId] + curSpiders = 0 + for i in xrange(4): + spidersLeft = maxSpiders - curSpiders + validChoices = [] + for numSpiders in xrange(spidersLeft + 1): + validChoices += pool[numSpiders] + + if not validChoices: + self.notify.warning('we ran out of valid choices szId=%s, vineSections=%s' % (szId, self.vineSections)) + validChoices += [0] + section = random.choice(validChoices) + curSpiders += VineGameGlobals.getNumSpidersInSection(section) + self.vineSections.append(section) + + self.totalSpiders = curSpiders + self.notify.debug('calc vineSections = %s' % self.vineSections) + + def getVineSections(self): + return self.vineSections + + def setTrolleyZone(self, trolleyZone): + DistributedMinigameAI.setTrolleyZone(self, trolleyZone) + self.setupVineSections() + + def awardPartialBeans(self): + if self.calculatedPartialBeans: + return + self.calculatedPartialBeans = True + for avId in self.avIdList: + vineIndex = self.toonInfo[avId][0] + if not vineIndex == VineGameGlobals.NumVines - 1: + partialBeans = int(vineIndex / 5.0) + if avId in self.scoreDict: + self.scoreDict[avId] += partialBeans + self.sendUpdate('setScore', [avId, self.scoreDict[avId]]) diff --git a/toontown/minigame/DivingFishSpawn.py b/toontown/minigame/DivingFishSpawn.py new file mode 100755 index 00000000..3be9fb5e --- /dev/null +++ b/toontown/minigame/DivingFishSpawn.py @@ -0,0 +1,120 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.actor import Actor +import random +import DivingGameGlobals + +class DivingFishSpawn(DirectObject): + RADIUS = 0.7 + + def __init__(self, spawnId, direction, position, cHandler): + loadBase = 'phase_4/models/char/' + self.entryNode = render.attachNewNode('entryNode') + self.direction = direction + self.fishArray = {} + self.inactiveArray = [] + self.fishActive = 0 + self.position = position + self.spawnId = spawnId + self.id = -1 + + def getUniqueNumber(self): + self.id += 1 + return self.id + + def createFish(self, fishcode): + loadBase = 'phase_4/models/char/' + if fishcode is 0: + fish = Actor.Actor('phase_4/models/char/clownFish-zero.bam', {'anim': loadBase + 'clownFish-swim.bam'}) + fish.name = 'clown' + elif fishcode is 1: + fish = Actor.Actor('phase_4/models/char/PBJfish-zero.bam', {'anim': 'phase_4/models/char/PBJfish-swim.bam'}) + fish.name = 'pbj' + elif fishcode is 2: + fish = Actor.Actor('phase_4/models/char/BearAcuda-zero.bam', {'anim': 'phase_4/models/char/BearAcuda-swim.bam'}) + fish.name = 'bear' + elif fishcode is 3: + fish = Actor.Actor(loadBase + 'balloonFish-zero.bam', {'anim': loadBase + 'balloonFish-swim.bam'}) + fish.name = 'balloon' + elif fishcode is 4: + fish = Actor.Actor(loadBase + 'nurseShark-zero.bam', {'anim': loadBase + 'nurseShark-swim.bam'}) + fish.name = 'nurse' + elif fishcode is 5: + fish = Actor.Actor(loadBase + 'pianoTuna-zero.bam', {'anim': loadBase + 'pianoTuna-swim.bam'}) + fish.name = 'piano' + else: + return + fish.active = 1 + fish.direction = self.direction + idCode = self.getUniqueNumber() + fish.code = str(self.spawnId) + str(idCode) + self.fishArray[idCode] = fish + fish.reparentTo(render) + fish.setScale(1) + fish.moveLerp = Sequence() + if fish.name == 'clown': + fish.setH(90 * self.direction) + fish.loop('anim') + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.2) + elif fish.name == 'pbj': + fish.setH(15 * self.direction) + fish.loop('anim') + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1) + elif fish.name == 'balloon': + fish.setH(0) + fish.loop('anim', fromFrame=0, toFrame=94) + fish.setScale(2) + cSphere = CollisionSphere(0.0, 0.0, 0.0, 0.2) + elif fish.name == 'bear': + fish.setH(90 * self.direction) + cSphere = CollisionSphere(0.0, -1.0, 3.5, 3.0) + fish.loop('anim') + fish.setScale(0.4, 1.7, 1.7) + elif fish.name == 'nurse': + fish.setH(90 * self.direction) + cSphere = CollisionSphere(0.0, -1.0, 0.0, 1) + fish.setScale(0.5, 1.7, 1.7) + fish.loop('anim') + elif fish.name == 'mackerel': + fish.setH(90 * self.direction) + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.5) + fish.loop('anim', fromFrame=36, toFrame=80) + elif fish.name == 'piano': + fish.loop('anim') + fish.setScale(1.4) + cSphere = CollisionSphere(0, 0, 0, 1) + fishSoundName = 'Piano_Tuna.ogg' + if self.direction is -1: + fish.setH(0) + else: + fish.setH(180) + cSphere.setTangible(0) + fish.offset = 0 + cSphereNode = CollisionNode('fc' + str(fish.code)) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(BitMask32.allOff()) + cSphereNode.setIntoCollideMask(DivingGameGlobals.CollideMask) + cSphereNodePath = fish.attachNewNode(cSphereNode) + self.accept('into-' + 'fc' + str(fish.code), self.__handleFishCollide) + fish.moveloop = Sequence(Wait(4), LerpScaleInterval(fish, startScale=1, scale=3, duration=1), Wait(1.5), LerpScaleInterval(fish, startScale=3, scale=1, duration=0.5)) + return fish + + def destroy(self): + self.ignoreAll() + for fish in self.fishArray.values(): + fish.moveLerp.pause() + fish.specialLerp.finish() + if hasattr(fish, 'sound'): + fish.sound.stop() + fish.sound = None + fish.moveLerp = None + fish.specialLerp = None + fish.removeNode() + del fish + + return + + def __handleFishCollide(self, collEntry): + messenger.send('FishHit', [collEntry]) diff --git a/toontown/minigame/DivingGameGlobals.py b/toontown/minigame/DivingGameGlobals.py new file mode 100755 index 00000000..c4b9186c --- /dev/null +++ b/toontown/minigame/DivingGameGlobals.py @@ -0,0 +1,5 @@ +from toontown.toonbase import ToontownGlobals +ENDLESS_GAME = config.GetBool('endless-maze-game', 0) +NUM_SPAWNERS = 6 +GAME_DURATION = 60.0 +CollideMask = ToontownGlobals.CatchGameBitmask diff --git a/toontown/minigame/DivingGameToonSD.py b/toontown/minigame/DivingGameToonSD.py new file mode 100755 index 00000000..9278f553 --- /dev/null +++ b/toontown/minigame/DivingGameToonSD.py @@ -0,0 +1,99 @@ +from direct.showbase.ShowBaseGlobal import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM +from direct.fsm import State +import CatchGameGlobals + +class DivingGameToonSD(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('DivingGameToonSD') + FallBackAnim = 'slip-backward' + FallFwdAnim = 'slip-forward' + CatchNeutralAnim = 'catch-neutral' + CatchRunAnim = 'catch-run' + EatNeutralAnim = 'catch-eatneutral' + EatNRunAnim = 'catch-eatnrun' + status = '' + animList = [FallBackAnim, + FallFwdAnim, + CatchNeutralAnim, + CatchRunAnim, + EatNeutralAnim, + EatNRunAnim] + + def __init__(self, avId, game): + self.avId = avId + self.game = game + self.isLocal = avId == base.localAvatar.doId + self.toon = self.game.getAvatar(self.avId) + self.unexpectedExit = False + self.fsm = ClassicFSM.ClassicFSM('CatchGameAnimFSM-%s' % self.avId, [State.State('init', self.enterInit, self.exitInit, ['normal']), + State.State('normal', self.enterNormal, self.exitNormal, ['freeze', 'treasure']), + State.State('treasure', self.enterTreasure, self.exitTreasure, ['freeze', 'normal']), + State.State('freeze', self.enterFreeze, self.exitFreeze, ['normal']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'init', 'cleanup') + + def load(self): + self.setAnimState('off', 1.0) + for anim in self.animList: + self.toon.pose(anim, 0) + + def unload(self): + del self.fsm + self.game = None + return + + def enter(self): + self.fsm.enterInitialState() + + def exit(self, unexpectedExit = False): + self.unexpectedExit = unexpectedExit + if not unexpectedExit: + self.fsm.requestFinalState() + + def enterInit(self): + self.notify.debug('enterInit') + self.status = 'init' + + def exitInit(self): + pass + + def enterNormal(self): + self.status = 'normal' + self.notify.debug('enterNormal') + self.setAnimState('dive', 1.0) + + def exitNormal(self): + self.setAnimState('off', 1.0) + + def enterTreasure(self): + self.status = 'treasure' + self.notify.debug('enterTreasure') + self.setAnimState('swimhold', 1.0) + + def exitTreasure(self): + self.notify.debug('exitTreasure') + + def enterFreeze(self): + self.status = 'freeze' + self.notify.debug('enterFreeze') + self.setAnimState('cringe', 1.0) + + def exitFreeze(self): + pass + + def enterCleanup(self): + self.status = 'cleanup' + self.notify.debug('enterCleanup') + if self.toon: + self.toon.resetLOD() + + def exitCleanup(self): + pass + + def setAnimState(self, newState, playRate): + if not self.unexpectedExit: + self.toon.setAnimState(newState, playRate) diff --git a/toontown/minigame/DivingTreasure.py b/toontown/minigame/DivingTreasure.py new file mode 100755 index 00000000..fbb219a4 --- /dev/null +++ b/toontown/minigame/DivingTreasure.py @@ -0,0 +1,36 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +import DivingGameGlobals + +class DivingTreasure(DirectObject): + + def __init__(self, i): + self.treasureNode = render.attachNewNode('treasure') + loadBase = 'phase_4/models/minigames/' + self.chest = loader.loadModel(loadBase + 'treasure.bam') + self.chest.reparentTo(self.treasureNode) + self.chest.setPos(0, 0, -25) + self.chest.setScale(1, 0.7, 1) + self.chestId = i + self.grabbedId = 0 + self.moveLerp = Sequence() + self.treasureNode.setScale(0.04) + self.treasureNode.setPos(-15 + 10.0 * i, 0.25, -36.0) + cSphere = CollisionSphere(0.0, 0.0, 0.0, 45) + cSphere.setTangible(0) + name = str(i) + cSphereNode = CollisionNode(name) + cSphereNode.setIntoCollideMask(DivingGameGlobals.CollideMask) + cSphereNode.addSolid(cSphere) + self.chestNode = cSphereNode + self.chestCNP = self.treasureNode.attachNewNode(cSphereNode) + + def destroy(self): + self.ignoreAll() + del self.chest + self.moveLerp.finish() + del self.moveLerp + self.treasureNode.removeNode() + del self.treasureNode diff --git a/toontown/minigame/DropPlacer.py b/toontown/minigame/DropPlacer.py new file mode 100755 index 00000000..c99d0b8c --- /dev/null +++ b/toontown/minigame/DropPlacer.py @@ -0,0 +1,1080 @@ +from direct.showbase.RandomNumGen import RandomNumGen +import CatchGameGlobals +import DropScheduler +from toontown.parties.PartyGlobals import CatchActivityDuration as PartyCatchDuration + +class DropPlacer: + + def __init__(self, game, numPlayers, dropTypes, startTime = None): + self.game = game + self.numPlayers = numPlayers + self.dropTypes = dropTypes + self.dtIndex = 0 + self._createScheduler(startTime) + self._createRng() + + def _createScheduler(self, startTime): + self.scheduler = DropScheduler.DropScheduler(CatchGameGlobals.GameDuration, self.game.FirstDropDelay, self.game.DropPeriod, self.game.MaxDropDuration, self.game.FasterDropDelay, self.game.FasterDropPeriodMult, startTime=startTime) + + def _createRng(self): + self.rng = self.game.randomNumGen + + def skipPercent(self, percent): + numSkips = self.scheduler.skipPercent(percent) + self.dtIndex += numSkips + return numSkips + + def doneDropping(self, continuous = None): + return self.scheduler.doneDropping(continuous) + + def getDuration(self): + return self.scheduler.getDuration() + + def getT(self): + return self.scheduler.getT() + + def stepT(self): + self.scheduler.stepT() + + def getNextDropTypeName(self): + if self.dtIndex >= len(self.dropTypes): + self.game.notify.debug('warning: defaulting to anvil') + typeName = 'anvil' + else: + typeName = self.dropTypes[self.dtIndex] + self.dtIndex += 1 + return typeName + + def getRandomColRow(self): + col = self.rng.randrange(0, self.game.DropColumns) + row = self.rng.randrange(0, self.game.DropRows) + return [col, row] + + def getNextDrop(self): + raise RuntimeError, 'DropPlacer.getNextDrop should never be called' + + +class RandomDropPlacer(DropPlacer): + + def __init__(self, game, numPlayers, dropTypes, startTime = None): + DropPlacer.__init__(self, game, numPlayers, dropTypes, startTime=startTime) + + def getNextDrop(self): + col, row = self.getRandomColRow() + drop = [self.getT(), self.getNextDropTypeName(), [col, row]] + self.stepT() + return drop + + +class RegionDropPlacer(DropPlacer): + DropRegionTables = [[[1, + 1, + 2, + 3, + 3], + [1, + 1, + 2, + 3, + 3], + [0, + 1, + 2, + 3, + 4], + [0, + 1, + 2, + 3, + 4], + [0, + 1, + 2, + 3, + 4]], + [[1, + 2, + 2, + 3, + 3, + 4], + [1, + 1, + 2, + 3, + 4, + 4], + [1, + 1, + 2, + 3, + 4, + 4], + [0, + 1, + 2, + 3, + 4, + 5], + [0, + 1, + 2, + 3, + 4, + 5], + [0, + 1, + 2, + 3, + 4, + 5]], + [[1, + 1, + 2, + 2, + 2, + 3, + 3], + [1, + 1, + 2, + 2, + 2, + 3, + 3], + [0, + 1, + 2, + 2, + 2, + 3, + 4], + [0, + 1, + 2, + 2, + 2, + 3, + 4], + [0, + 1, + 2, + 2, + 2, + 3, + 4], + [0, + 1, + 2, + 2, + 2, + 3, + 4], + [0, + 1, + 2, + 2, + 2, + 3, + 4]], + [[1, + 2, + 2, + 5, + 6, + 7, + 7, + 3], + [1, + 1, + 2, + 5, + 6, + 7, + 3, + 3], + [0, + 1, + 2, + 5, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 6, + 7, + 3, + 4], + [0, + 0, + 1, + 5, + 6, + 3, + 4, + 4]], + [[1, + 2, + 2, + 5, + 8, + 6, + 7, + 7, + 3], + [1, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 3], + [0, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 4], + [0, + 0, + 1, + 5, + 8, + 6, + 3, + 4, + 4]], + [[1, + 2, + 2, + 5, + 8, + 8, + 6, + 7, + 7, + 3], + [1, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 3], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 0, + 1, + 5, + 8, + 8, + 6, + 3, + 4, + 4]], + [[1, + 2, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 7, + 3], + [1, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 3], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 0, + 1, + 5, + 8, + 10, + 9, + 6, + 3, + 4, + 4]], + [[1, + 2, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 7, + 3], + [1, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 3], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 0, + 1, + 5, + 8, + 10, + 10, + 9, + 6, + 3, + 4, + 4]], + [[1, + 2, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 7, + 3], + [1, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 3], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 0, + 1, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 3, + 4, + 4]]] + Players2dropTable = [DropRegionTables[0], + DropRegionTables[0], + DropRegionTables[0], + DropRegionTables[1], + DropRegionTables[1], + DropRegionTables[2], + DropRegionTables[3], + DropRegionTables[3], + DropRegionTables[4], + DropRegionTables[4], + DropRegionTables[5], + DropRegionTables[5], + DropRegionTables[5], + DropRegionTables[6], + DropRegionTables[6], + DropRegionTables[7], + DropRegionTables[7], + DropRegionTables[7], + DropRegionTables[8], + DropRegionTables[8]] + + @classmethod + def getDropRegionTable(cls, numPlayers): + return cls.Players2dropTable[min(len(cls.Players2dropTable) - 1, numPlayers)] + + def __init__(self, game, numPlayers, dropTypes, startTime = None): + DropPlacer.__init__(self, game, numPlayers, dropTypes, startTime=startTime) + self.DropRegionTable = self.getDropRegionTable(self.numPlayers) + self.DropRegion2GridCoordList = {} + for row in xrange(len(self.DropRegionTable)): + rowList = self.DropRegionTable[row] + for column in xrange(len(rowList)): + region = rowList[column] + if region not in self.DropRegion2GridCoordList: + self.DropRegion2GridCoordList[region] = [] + self.DropRegion2GridCoordList[region].append([row, column]) + + self.DropRegions = self.DropRegion2GridCoordList.keys() + self.DropRegions.sort() + self.emptyDropRegions = self.DropRegions[:] + self.fallingObjs = [] + + def getNextDrop(self): + t = self.getT() + while len(self.fallingObjs): + landTime, dropRegion = self.fallingObjs[0] + if landTime > t: + break + self.fallingObjs = self.fallingObjs[1:] + if dropRegion not in self.emptyDropRegions: + self.emptyDropRegions.append(dropRegion) + + candidates = self.emptyDropRegions + if len(candidates) == 0: + candidates = self.DropRegions + dropRegion = self.rng.choice(candidates) + row, col = self.rng.choice(self.DropRegion2GridCoordList[dropRegion]) + dropTypeName = self.getNextDropTypeName() + drop = [t, dropTypeName, [row, col]] + duration = self.game.BaselineDropDuration + self.fallingObjs.append([t + duration, dropRegion]) + if dropRegion in self.emptyDropRegions: + self.emptyDropRegions.remove(dropRegion) + self.stepT() + return drop + + +class PartyRegionDropPlacer(RegionDropPlacer): + + def __init__(self, game, numPlayers, generationId, dropTypes, startTime = None): + self.generationId = generationId + RegionDropPlacer.__init__(self, game, numPlayers, dropTypes, startTime=startTime) + + def _createRng(self): + self.rng = RandomNumGen(self.generationId + self.game.doId) + + def _createScheduler(self, startTime): + self.scheduler = DropScheduler.ThreePhaseDropScheduler(PartyCatchDuration, self.game.FirstDropDelay, self.game.DropPeriod, self.game.MaxDropDuration, self.game.SlowerDropPeriodMult, self.game.NormalDropDelay, self.game.FasterDropDelay, self.game.FasterDropPeriodMult, startTime=startTime) + + +class PathDropPlacer(DropPlacer): + + def __init__(self, game, numPlayers, dropTypes, startTime = None): + DropPlacer.__init__(self, game, numPlayers, dropTypes, startTime=startTime) + self.moves = [[0, -1], + [1, -1], + [1, 0], + [1, 1], + [0, 1], + [-1, 1], + [-1, 0], + [-1, -1]] + self.paths = [] + for i in xrange(self.numPlayers): + dir = self.rng.randrange(0, len(self.moves)) + col, row = self.getRandomColRow() + path = {'direction': dir, + 'location': [col, row]} + self.paths.append(path) + + self.curPathIndex = 0 + + def getValidDirection(self, col, row, dir): + redirectTop = [(6, 2), + 2, + 2, + 3, + 4, + 5, + 6, + 6] + redirectRight = [0, + 0, + (0, 4), + 4, + 4, + 5, + 6, + 7] + redirectBottom = [0, + 1, + 2, + 2, + (2, 6), + 6, + 6, + 7] + redirectLeft = [0, + 1, + 2, + 3, + 4, + 4, + (4, 0), + 0] + redirectTopRight = [6, + (6, 4), + 4, + 4, + 4, + 5, + 6, + 6] + redirectBottomRight = [0, + 0, + 0, + (0, 6), + 6, + 6, + 6, + 7] + redirectBottomLeft = [0, + 1, + 2, + 2, + 2, + (2, 0), + 0, + 0] + redirectTopLeft = [2, + 2, + 2, + 3, + 4, + 4, + 4, + (4, 2)] + tables = [None, + redirectTop, + redirectBottom, + None, + redirectLeft, + redirectTopLeft, + redirectBottomLeft, + None, + redirectRight, + redirectTopRight, + redirectBottomRight] + if col == 0: + colIndex = 1 + elif col == self.game.DropColumns - 1: + colIndex = 2 + else: + colIndex = 0 + if row == 0: + rowIndex = 1 + elif row == self.game.DropRows - 1: + rowIndex = 2 + else: + rowIndex = 0 + index = (colIndex << 2) + rowIndex + redirectTable = tables[index] + if not redirectTable: + return dir + newDir = redirectTable[dir] + if type(newDir) != type(1): + newDir = self.rng.choice(newDir) + return newDir + + def getNextDrop(self): + path = self.paths[self.curPathIndex] + col, row = path['location'] + dir = path['direction'] + turns = [-1, + 0, + 0, + 1] + turn = self.rng.choice(turns) + if turn: + dir = (dir + turn) % len(self.moves) + dir = self.getValidDirection(col, row, dir) + dCol, dRow = self.moves[dir] + col += dCol + row += dRow + col = min(max(col, 0), self.game.DropColumns - 1) + row = min(max(row, 0), self.game.DropRows - 1) + path['location'] = [col, row] + path['direction'] = dir + self.curPathIndex = (self.curPathIndex + 1) % len(self.paths) + drop = [self.getT(), self.getNextDropTypeName(), [col, row]] + self.stepT() + return drop diff --git a/toontown/minigame/DropScheduler.py b/toontown/minigame/DropScheduler.py new file mode 100755 index 00000000..9aec11bb --- /dev/null +++ b/toontown/minigame/DropScheduler.py @@ -0,0 +1,72 @@ + + +class DropScheduler: + + def __init__(self, gameDuration, firstDropDelay, dropPeriod, maxDropDuration, fasterDropDelay, fasterDropPeriodMult, startTime = None): + self.gameDuration = gameDuration + self.firstDropDelay = firstDropDelay + self._dropPeriod = dropPeriod + self.maxDropDuration = maxDropDuration + self.fasterDropDelay = fasterDropDelay + self.fasterDropPeriodMult = fasterDropPeriodMult + if startTime is None: + startTime = 0 + self._startTime = startTime + self.curT = self._startTime + self.firstDropDelay + return + + def getT(self): + return self.curT + + def getDuration(self): + return self.gameDuration + + def getDropPeriod(self): + delay = self._dropPeriod + if self.curT - self._startTime >= self.fasterDropDelay: + delay *= self.fasterDropPeriodMult + return delay + + def doneDropping(self, continuous = None): + landTime = self.getT() - self._startTime + self.maxDropDuration + if continuous is None: + continuous = False + else: + continuous = True + if continuous: + maxTime = self.gameDuration + self.maxDropDuration + else: + maxTime = self.gameDuration + self.getDropPeriod() + return landTime >= maxTime + + def skipPercent(self, percent): + numSkips = 0 + while True: + prevT = self.curT + self.stepT() + if self.curT >= percent * self.gameDuration: + self.curT = prevT + break + else: + numSkips += 1 + + return numSkips + + def stepT(self): + self.curT += self.getDropPeriod() + + +class ThreePhaseDropScheduler(DropScheduler): + + def __init__(self, gameDuration, firstDropDelay, dropPeriod, maxDropDuration, slowerDropPeriodMult, normalDropDelay, fasterDropDelay, fasterDropPeriodMult, startTime = None): + self._slowerDropPeriodMult = slowerDropPeriodMult + self._normalDropDelay = normalDropDelay + DropScheduler.__init__(self, gameDuration, firstDropDelay, dropPeriod, maxDropDuration, fasterDropDelay, fasterDropPeriodMult, startTime) + + def getDropPeriod(self): + delay = self._dropPeriod + if self.curT - self._startTime < self._normalDropDelay: + delay *= self._slowerDropPeriodMult + elif self.curT - self._startTime >= self.fasterDropDelay: + delay *= self.fasterDropPeriodMult + return delay diff --git a/toontown/minigame/FogOverlay.py b/toontown/minigame/FogOverlay.py new file mode 100755 index 00000000..5b85a5ee --- /dev/null +++ b/toontown/minigame/FogOverlay.py @@ -0,0 +1,61 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +from toontown.toonbase import ToontownGlobals +import math +from math import * + +class FogOverlay: + SomeCounter = 0 + + def __init__(self, color = Point3(1.0, 1.0, 1.0)): + self.color = color + self.opacity = 1.0 + self.setup() + + def setup(self): + self.baseNode = aspect2d.attachNewNode('targetGameTargets') + self.overlayGN = GeomNode('Overlay Geometry') + self.overlayNodePathGeom = self.baseNode.attachNewNode(self.overlayGN) + self.overlayNodePathGeom.setDepthWrite(False) + self.overlayNodePathGeom.setTransparency(TransparencyAttrib.MAlpha) + shapeVertexs = [] + shapeVertexs.append((-2.0, 0.0, 1.0)) + shapeVertexs.append((-2.0, 0.0, -1.0)) + shapeVertexs.append((2.0, 0.0, 1.0)) + shapeVertexs.append((2.0, 0.0, -1.0)) + gFormat = GeomVertexFormat.getV3cp() + overlayVertexData = GeomVertexData('holds my vertices', gFormat, Geom.UHDynamic) + overlayVertexWriter = GeomVertexWriter(overlayVertexData, 'vertex') + overlayColorWriter = GeomVertexWriter(overlayVertexData, 'color') + for index in xrange(len(shapeVertexs)): + overlayVertexWriter.addData3f(shapeVertexs[index][0], shapeVertexs[index][1], shapeVertexs[index][2]) + overlayColorWriter.addData4f(1.0, 1.0, 1.0, 1.0) + + overlayTris = GeomTristrips(Geom.UHStatic) + for index in xrange(len(shapeVertexs)): + overlayTris.addVertex(index) + + overlayTris.closePrimitive() + overlayGeom = Geom(overlayVertexData) + overlayGeom.addPrimitive(overlayTris) + self.overlayGN.addGeom(overlayGeom) + + def setOpacity(self, opacity): + self.opacity = opacity + self.__applyColor() + + def setColor(self, color): + self.color = color + self.__applyColor() + + def __applyColor(self): + self.overlayNodePathGeom.setColorScale(self.color[0], self.color[1], self.color[2], self.opacity) + + def delete(self): + self.overlayGN.removeAllGeoms() + self.baseNode.removeNode() diff --git a/toontown/minigame/IceGameGlobals.py b/toontown/minigame/IceGameGlobals.py new file mode 100755 index 00000000..f87d5ec7 --- /dev/null +++ b/toontown/minigame/IceGameGlobals.py @@ -0,0 +1,68 @@ +import math +from pandac.PandaModules import Point3 +from toontown.toonbase import ToontownGlobals +InputTimeout = 15 +TireMovieTimeout = 120 +MinWall = (-20.0, -15.0) +MaxWall = (20.0, 15.0) +TireRadius = 1.5 +WallMargin = 1 + TireRadius +StartingPositions = ( + Point3(MinWall[0] + WallMargin, MinWall[1] + WallMargin, TireRadius), + Point3(MaxWall[0] - WallMargin, MaxWall[1] - WallMargin, TireRadius), + Point3(MinWall[0] + WallMargin, MaxWall[1] - WallMargin, TireRadius), + Point3(MaxWall[0] - WallMargin, MinWall[1] + WallMargin, TireRadius) +) +NumMatches = 3 +NumRounds = 2 +PointsDeadCenter = { + 0: 5, + 1: 5, + 2: 5, + 3: 4, + 4: 3 +} +PointsInCorner = 1 +FarthestLength = math.sqrt((MaxWall[0] - TireRadius) * (MaxWall[0] - TireRadius) + (MaxWall[1] - TireRadius) * (MaxWall[1] - TireRadius)) +BonusPointsForPlace = (3, 2, 1, 0) +ExpandFeetPerSec = 5 +ScoreCountUpRate = 0.15 +ShowScoresDuration = 4.0 +NumTreasures = { + ToontownGlobals.ToontownCentral: 2, + ToontownGlobals.DonaldsDock: 2, + ToontownGlobals.DaisyGardens: 2, + ToontownGlobals.MinniesMelodyland: 2, + ToontownGlobals.TheBrrrgh: 1, + ToontownGlobals.DonaldsDreamland: 1 +} +NumPenalties = { + ToontownGlobals.ToontownCentral: 0, + ToontownGlobals.DonaldsDock: 1, + ToontownGlobals.DaisyGardens: 1, + ToontownGlobals.MinniesMelodyland: 1, + ToontownGlobals.TheBrrrgh: 2, + ToontownGlobals.DonaldsDreamland: 2 +} +Obstacles = { + ToontownGlobals.ToontownCentral: (), + ToontownGlobals.DonaldsDock: ((0, 0),), + ToontownGlobals.DaisyGardens: ((MinWall[0] / 2, 0), (MaxWall[0] / 2, 0)), + ToontownGlobals.MinniesMelodyland: ((0, MinWall[1] / 2), (0, MaxWall[1] / 2)), + ToontownGlobals.TheBrrrgh: ((MinWall[0] / 2, 0), + (MaxWall[0] / 2, 0), + (0, MinWall[1] / 2), + (0, MaxWall[1] / 2)), + ToontownGlobals.DonaldsDreamland: ((MinWall[0] / 2, MinWall[1] / 2), + (MinWall[0] / 2, MaxWall[1] / 2), + (MaxWall[0] / 2, MinWall[1] / 2), + (MaxWall[0] / 2, MaxWall[1] / 2)) +} +ObstacleShapes = { + ToontownGlobals.ToontownCentral: True, + ToontownGlobals.DonaldsDock: True, + ToontownGlobals.DaisyGardens: True, + ToontownGlobals.MinniesMelodyland: True, + ToontownGlobals.TheBrrrgh: False, + ToontownGlobals.DonaldsDreamland: False +} diff --git a/toontown/minigame/IceTreasure.py b/toontown/minigame/IceTreasure.py new file mode 100755 index 00000000..dafae7b8 --- /dev/null +++ b/toontown/minigame/IceTreasure.py @@ -0,0 +1,68 @@ +from pandac.PandaModules import Point3, CollisionSphere, CollisionNode, BitMask32 +from direct.interval.IntervalGlobal import Sequence, LerpScaleInterval, Parallel, Func, SoundInterval +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase import ToontownGlobals +from toontown.battle import BattleParticles + +class IceTreasure(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('IceTreasure') + RADIUS = 1.0 + + def __init__(self, model, pos, serialNum, gameId, penalty = False): + self.serialNum = serialNum + self.penalty = penalty + center = model.getBounds().getCenter() + center = Point3(0, 0, 0) + self.nodePath = model.copyTo(render) + self.nodePath.setPos(pos[0] - center[0], pos[1] - center[1], pos[2] - center[2]) + self.nodePath.setZ(0) + self.notify.debug('newPos = %s' % self.nodePath.getPos()) + if self.penalty: + self.sphereName = 'penaltySphere-%s-%s' % (gameId, self.serialNum) + else: + self.sphereName = 'treasureSphere-%s-%s' % (gameId, self.serialNum) + self.collSphere = CollisionSphere(center[0], center[1], center[2], self.RADIUS) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.sphereName) + self.collNode.setIntoCollideMask(ToontownGlobals.PieBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = render.attachNewNode(self.collNode) + self.collNodePath.setPos(pos[0] - center[0], pos[1] - center[1], pos[2] - center[2]) + self.collNodePath.hide() + self.track = None + if self.penalty: + self.tip = self.nodePath.find('**/fusetip') + sparks = BattleParticles.createParticleEffect(file='icetnt') + self.sparksEffect = sparks + sparks.start(self.tip) + self.penaltyGrabSound = loader.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.ogg') + self.penaltyGrabSound.setVolume(0.75) + kaboomAttachPoint = self.nodePath.attachNewNode('kaboomAttach') + kaboomAttachPoint.setZ(3) + self.kaboom = loader.loadModel('phase_4/models/minigames/ice_game_kaboom') + self.kaboom.reparentTo(kaboomAttachPoint) + self.kaboom.setScale(2.0) + self.kaboom.setBillboardPointEye() + return + + def destroy(self): + self.ignoreAll() + if self.penalty: + self.sparksEffect.cleanup() + if self.track: + self.track.finish() + self.nodePath.removeNode() + del self.nodePath + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + + def showGrab(self): + self.nodePath.hide() + self.collNodePath.hide() + self.collNode.setIntoCollideMask(BitMask32(0)) + if self.penalty: + self.track = Parallel(SoundInterval(self.penaltyGrabSound), Sequence(Func(self.kaboom.showThrough), LerpScaleInterval(self.kaboom, duration=0.5, scale=Point3(10, 10, 10), blendType='easeOut'), Func(self.kaboom.hide))) + self.track.start() diff --git a/toontown/minigame/Maze.py b/toontown/minigame/Maze.py new file mode 100755 index 00000000..f431bc87 --- /dev/null +++ b/toontown/minigame/Maze.py @@ -0,0 +1,11 @@ +from MazeBase import MazeBase +import MazeData + +class Maze(MazeBase): + + def __init__(self, mapName, mazeData = MazeData.mazeData, cellWidth = MazeData.CELL_WIDTH): + model = loader.loadModel(mapName) + mData = mazeData[mapName] + self.treasurePosList = mData['treasurePosList'] + self.numTreasures = len(self.treasurePosList) + MazeBase.__init__(self, model, mData, cellWidth) diff --git a/toontown/minigame/MazeBase.py b/toontown/minigame/MazeBase.py new file mode 100755 index 00000000..f6e32a94 --- /dev/null +++ b/toontown/minigame/MazeBase.py @@ -0,0 +1,158 @@ +from pandac.PandaModules import VBase3 +from direct.showbase.RandomNumGen import RandomNumGen + +class MazeBase: + + def __init__(self, model, mazeData, cellWidth, parent = None): + if parent is None: + parent = render + self.width = mazeData['width'] + self.height = mazeData['height'] + self.originTX = mazeData['originX'] + self.originTY = mazeData['originY'] + self.collisionTable = mazeData['collisionTable'] + self._initialCellWidth = cellWidth + self.cellWidth = self._initialCellWidth + self.maze = model + self.maze.setPos(0, 0, 0) + self.maze.reparentTo(parent) + self.maze.stash() + return + + def destroy(self): + self.maze.removeNode() + del self.maze + + def onstage(self): + self.maze.unstash() + + def offstage(self): + self.maze.stash() + + def setScale(self, xy = 1, z = 1): + self.maze.setScale(VBase3(xy, xy, z)) + self.cellWidth = self._initialCellWidth * xy + + def isWalkable(self, tX, tY, rejectList = ()): + if tX <= 0 or tY <= 0 or tX >= self.width or tY >= self.height: + return 0 + return not self.collisionTable[tY][tX] and not self.collisionTable[tY - 1][tX] and not self.collisionTable[tY][tX - 1] and not self.collisionTable[tY - 1][tX - 1] and (tX, tY) not in rejectList + + def tile2world(self, TX, TY): + return [(TX - self.originTX) * self.cellWidth, (TY - self.originTY) * self.cellWidth] + + def world2tile(self, x, y): + return [int(x / self.cellWidth + self.originTX), int(y / self.cellWidth + self.originTY)] + + def world2tileClipped(self, x, y): + coords = [int(x / self.cellWidth + self.originTX), int(y / self.cellWidth + self.originTY)] + coords[0] = min(max(coords[0], 0), self.width - 1) + coords[1] = min(max(coords[1], 0), self.height - 1) + return coords + + def doOrthoCollisions(self, oldPos, newPos): + offset = newPos - oldPos + WALL_OFFSET = 1.0 + curX = oldPos[0] + curY = oldPos[1] + curTX, curTY = self.world2tile(curX, curY) + + def calcFlushCoord(curTile, newTile, centerTile): + EPSILON = 0.01 + if newTile > curTile: + return (newTile - centerTile) * self.cellWidth - EPSILON - WALL_OFFSET + else: + return (curTile - centerTile) * self.cellWidth + WALL_OFFSET + + offsetX = offset[0] + offsetY = offset[1] + WALL_OFFSET_X = WALL_OFFSET + if offsetX < 0: + WALL_OFFSET_X = -WALL_OFFSET_X + WALL_OFFSET_Y = WALL_OFFSET + if offsetY < 0: + WALL_OFFSET_Y = -WALL_OFFSET_Y + newX = curX + offsetX + WALL_OFFSET_X + newY = curY + newTX, newTY = self.world2tile(newX, newY) + if newTX != curTX: + if self.collisionTable[newTY][newTX] == 1: + offset.setX(calcFlushCoord(curTX, newTX, self.originTX) - curX) + newX = curX + newY = curY + offsetY + WALL_OFFSET_Y + newTX, newTY = self.world2tile(newX, newY) + if newTY != curTY: + if self.collisionTable[newTY][newTX] == 1: + offset.setY(calcFlushCoord(curTY, newTY, self.originTY) - curY) + offsetX = offset[0] + offsetY = offset[1] + newX = curX + offsetX + WALL_OFFSET_X + newY = curY + offsetY + WALL_OFFSET_Y + newTX, newTY = self.world2tile(newX, newY) + if self.collisionTable[newTY][newTX] == 1: + cX = calcFlushCoord(curTX, newTX, self.originTX) + cY = calcFlushCoord(curTY, newTY, self.originTY) + if abs(cX - curX) < abs(cY - curY): + offset.setX(cX - curX) + else: + offset.setY(cY - curY) + return oldPos + offset + + def createRandomSpotsList(self, numSpots, randomNumGen): + randomNumGen = RandomNumGen(randomNumGen) + width = self.width + height = self.height + halfWidth = int(width / 2) + halfHeight = int(height / 2) + quadrants = [(0, + 0, + halfWidth - 1, + halfHeight - 1), + (halfWidth, + 0, + width - 1, + halfHeight - 1), + (0, + halfHeight, + halfWidth - 1, + height - 1), + (halfWidth, + halfHeight, + width - 1, + height - 1)] + spotsTaken = [] + + def getEmptySpotInQuadrant(quadrant): + tX = -1 + tY = -1 + while tX < 0 or not self.isWalkable(tX, tY, spotsTaken): + tX = randomNumGen.randint(quadrant[0], quadrant[2]) + tY = randomNumGen.randint(quadrant[1], quadrant[3]) + + spot = (tX, tY) + spotsTaken.append(spot) + return spot + + def getSpotList(length): + randomNumGen.shuffle(quadrants) + l = [] + remaining = length + for quadrant in quadrants: + for u in xrange(int(length / 4)): + l.append(getEmptySpotInQuadrant(quadrant)) + + remaining -= int(length / 4) + + for u in xrange(remaining): + quadrant = quadrants[randomNumGen.randint(0, len(quadrants) - 1)] + l.append(getEmptySpotInQuadrant(quadrant)) + + return l + + if type(numSpots) == tuple or type(numSpots) == list: + spots = [] + for i in numSpots: + spots.append(getSpotList(i)) + + return spots + return getSpotList(numSpots) diff --git a/toontown/minigame/MazeData.py b/toontown/minigame/MazeData.py new file mode 100755 index 00000000..92bb52c9 --- /dev/null +++ b/toontown/minigame/MazeData.py @@ -0,0 +1,8167 @@ +CELL_WIDTH = 2 +mazeNames = [['phase_4/models/minigames/maze_1player'], + ['phase_4/models/minigames/maze_2player'], + ['phase_4/models/minigames/maze_3player'], + ['phase_4/models/minigames/maze_4player']] +mazeData = {} +mazeData['phase_4/models/minigames/maze_1player'] = {} +data = mazeData['phase_4/models/minigames/maze_1player'] +data['width'] = 28 +data['height'] = 22 +data['originX'] = 14 +data['originY'] = 11 +data['collisionTable'] = [[1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1], + [1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1], + [1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1]] +data['treasurePosList'] = [(-20, -18, 0.1), + (-18, -18, 0.1), + (-16, -18, 0.1), + (-14, -18, 0.1), + (-8, -18, 0.1), + (-6, -18, 0.1), + (-4, -18, 0.1), + (4, -18, 0.1), + (6, -18, 0.1), + (8, -18, 0.1), + (14, -18, 0.1), + (16, -18, 0.1), + (18, -18, 0.1), + (20, -18, 0.1), + (-20, -16, 0.1), + (-14, -16, 0.1), + (-4, -16, 0.1), + (4, -16, 0.1), + (14, -16, 0.1), + (20, -16, 0.1), + (-20, -14, 0.1), + (-14, -14, 0.1), + (-4, -14, 0.1), + (4, -14, 0.1), + (14, -14, 0.1), + (20, -14, 0.1), + (-24, -12, 0.1), + (-22, -12, 0.1), + (-20, -12, 0.1), + (-14, -12, 0.1), + (-12, -12, 0.1), + (-10, -12, 0.1), + (-8, -12, 0.1), + (-6, -12, 0.1), + (-4, -12, 0.1), + (-2, -12, 0.1), + (0, -12, 0.1), + (2, -12, 0.1), + (4, -12, 0.1), + (6, -12, 0.1), + (8, -12, 0.1), + (10, -12, 0.1), + (12, -12, 0.1), + (14, -12, 0.1), + (20, -12, 0.1), + (22, -12, 0.1), + (24, -12, 0.1), + (-24, -10, 0.1), + (-14, -10, 0.1), + (-8, -10, 0.1), + (0, -10, 0.1), + (8, -10, 0.1), + (14, -10, 0.1), + (24, -10, 0.1), + (-24, -8, 0.1), + (-14, -8, 0.1), + (-8, -8, 0.1), + (0, -8, 0.1), + (8, -8, 0.1), + (14, -8, 0.1), + (24, -8, 0.1), + (-24, -6, 0.1), + (-18, -6, 0.1), + (-16, -6, 0.1), + (-14, -6, 0.1), + (-8, -6, 0.1), + (0, -6, 0.1), + (8, -6, 0.1), + (14, -6, 0.1), + (16, -6, 0.1), + (18, -6, 0.1), + (24, -6, 0.1), + (-24, -4, 0.1), + (-18, -4, 0.1), + (-8, -4, 0.1), + (8, -4, 0.1), + (18, -4, 0.1), + (24, -4, 0.1), + (-24, -2, 0.1), + (-18, -2, 0.1), + (-8, -2, 0.1), + (8, -2, 0.1), + (18, -2, 0.1), + (24, -2, 0.1), + (-24, 0, 0.1), + (-18, 0, 0.1), + (-16, 0, 0.1), + (-14, 0, 0.1), + (-12, 0, 0.1), + (-10, 0, 0.1), + (-8, 0, 0.1), + (-6, 0, 0.1), + (6, 0, 0.1), + (8, 0, 0.1), + (10, 0, 0.1), + (12, 0, 0.1), + (14, 0, 0.1), + (16, 0, 0.1), + (18, 0, 0.1), + (24, 0, 0.1), + (-24, 2, 0.1), + (-12, 2, 0.1), + (-6, 2, 0.1), + (6, 2, 0.1), + (12, 2, 0.1), + (24, 2, 0.1), + (-24, 4, 0.1), + (-12, 4, 0.1), + (-6, 4, 0.1), + (6, 4, 0.1), + (12, 4, 0.1), + (24, 4, 0.1), + (-24, 6, 0.1), + (-22, 6, 0.1), + (-20, 6, 0.1), + (-18, 6, 0.1), + (-16, 6, 0.1), + (-14, 6, 0.1), + (-12, 6, 0.1), + (-6, 6, 0.1), + (-4, 6, 0.1), + (-2, 6, 0.1), + (0, 6, 0.1), + (2, 6, 0.1), + (4, 6, 0.1), + (6, 6, 0.1), + (12, 6, 0.1), + (14, 6, 0.1), + (16, 6, 0.1), + (18, 6, 0.1), + (20, 6, 0.1), + (22, 6, 0.1), + (24, 6, 0.1), + (-18, 8, 0.1), + (-12, 8, 0.1), + (0, 8, 0.1), + (12, 8, 0.1), + (18, 8, 0.1), + (-18, 10, 0.1), + (-12, 10, 0.1), + (0, 10, 0.1), + (12, 10, 0.1), + (18, 10, 0.1), + (-24, 12, 0.1), + (-22, 12, 0.1), + (-20, 12, 0.1), + (-18, 12, 0.1), + (-12, 12, 0.1), + (-10, 12, 0.1), + (-8, 12, 0.1), + (-6, 12, 0.1), + (-4, 12, 0.1), + (-2, 12, 0.1), + (0, 12, 0.1), + (2, 12, 0.1), + (4, 12, 0.1), + (6, 12, 0.1), + (8, 12, 0.1), + (10, 12, 0.1), + (12, 12, 0.1), + (18, 12, 0.1), + (20, 12, 0.1), + (22, 12, 0.1), + (24, 12, 0.1), + (-24, 14, 0.1), + (-8, 14, 0.1), + (8, 14, 0.1), + (24, 14, 0.1), + (-24, 16, 0.1), + (-8, 16, 0.1), + (8, 16, 0.1), + (24, 16, 0.1), + (-24, 18, 0.1), + (-22, 18, 0.1), + (-20, 18, 0.1), + (-18, 18, 0.1), + (-16, 18, 0.1), + (-14, 18, 0.1), + (-12, 18, 0.1), + (-10, 18, 0.1), + (-8, 18, 0.1), + (-6, 18, 0.1), + (-4, 18, 0.1), + (-2, 18, 0.1), + (0, 18, 0.1), + (2, 18, 0.1), + (4, 18, 0.1), + (6, 18, 0.1), + (8, 18, 0.1), + (10, 18, 0.1), + (12, 18, 0.1), + (14, 18, 0.1), + (16, 18, 0.1), + (18, 18, 0.1), + (20, 18, 0.1), + (22, 18, 0.1), + (24, 18, 0.1)] +mazeData['phase_4/models/minigames/maze_2player'] = {} +data = mazeData['phase_4/models/minigames/maze_2player'] +data['width'] = 32 +data['height'] = 50 +data['originX'] = 16 +data['originY'] = 25 +data['collisionTable'] = [[1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1]] +data['treasurePosList'] = [(-28, -46, 0.1), + (-26, -46, 0.1), + (-24, -46, 0.1), + (-22, -46, 0.1), + (-20, -46, 0.1), + (-18, -46, 0.1), + (-16, -46, 0.1), + (-14, -46, 0.1), + (-12, -46, 0.1), + (12, -46, 0.1), + (18, -46, 0.1), + (20, -46, 0.1), + (22, -46, 0.1), + (24, -46, 0.1), + (26, -46, 0.1), + (28, -46, 0.1), + (-28, -44, 0.1), + (-12, -44, 0.1), + (12, -44, 0.1), + (18, -44, 0.1), + (28, -44, 0.1), + (-28, -42, 0.1), + (-12, -42, 0.1), + (12, -42, 0.1), + (18, -42, 0.1), + (28, -42, 0.1), + (-28, -40, 0.1), + (-26, -40, 0.1), + (-24, -40, 0.1), + (-22, -40, 0.1), + (-12, -40, 0.1), + (12, -40, 0.1), + (18, -40, 0.1), + (28, -40, 0.1), + (-28, -38, 0.1), + (-22, -38, 0.1), + (-12, -38, 0.1), + (12, -38, 0.1), + (18, -38, 0.1), + (28, -38, 0.1), + (-28, -36, 0.1), + (-22, -36, 0.1), + (-20, -36, 0.1), + (-18, -36, 0.1), + (-16, -36, 0.1), + (-14, -36, 0.1), + (-12, -36, 0.1), + (-10, -36, 0.1), + (-8, -36, 0.1), + (-6, -36, 0.1), + (-4, -36, 0.1), + (-2, -36, 0.1), + (0, -36, 0.1), + (2, -36, 0.1), + (4, -36, 0.1), + (6, -36, 0.1), + (8, -36, 0.1), + (10, -36, 0.1), + (12, -36, 0.1), + (14, -36, 0.1), + (16, -36, 0.1), + (18, -36, 0.1), + (20, -36, 0.1), + (22, -36, 0.1), + (24, -36, 0.1), + (26, -36, 0.1), + (28, -36, 0.1), + (-28, -34, 0.1), + (-18, -34, 0.1), + (-8, -34, 0.1), + (8, -34, 0.1), + (18, -34, 0.1), + (-28, -32, 0.1), + (-18, -32, 0.1), + (-8, -32, 0.1), + (8, -32, 0.1), + (18, -32, 0.1), + (-28, -30, 0.1), + (-26, -30, 0.1), + (-24, -30, 0.1), + (-22, -30, 0.1), + (-20, -30, 0.1), + (-18, -30, 0.1), + (-12, -30, 0.1), + (-10, -30, 0.1), + (-8, -30, 0.1), + (-6, -30, 0.1), + (-4, -30, 0.1), + (-2, -30, 0.1), + (0, -30, 0.1), + (2, -30, 0.1), + (4, -30, 0.1), + (6, -30, 0.1), + (8, -30, 0.1), + (10, -30, 0.1), + (12, -30, 0.1), + (18, -30, 0.1), + (20, -30, 0.1), + (22, -30, 0.1), + (24, -30, 0.1), + (26, -30, 0.1), + (28, -30, 0.1), + (-18, -28, 0.1), + (-12, -28, 0.1), + (-2, -28, 0.1), + (0, -28, 0.1), + (2, -28, 0.1), + (12, -28, 0.1), + (18, -28, 0.1), + (-18, -26, 0.1), + (-12, -26, 0.1), + (-2, -26, 0.1), + (0, -26, 0.1), + (2, -26, 0.1), + (12, -26, 0.1), + (18, -26, 0.1), + (-18, -24, 0.1), + (-12, -24, 0.1), + (-6, -24, 0.1), + (-4, -24, 0.1), + (-2, -24, 0.1), + (0, -24, 0.1), + (2, -24, 0.1), + (4, -24, 0.1), + (6, -24, 0.1), + (12, -24, 0.1), + (18, -24, 0.1), + (-18, -22, 0.1), + (-16, -22, 0.1), + (-14, -22, 0.1), + (-12, -22, 0.1), + (-6, -22, 0.1), + (0, -22, 0.1), + (6, -22, 0.1), + (12, -22, 0.1), + (14, -22, 0.1), + (16, -22, 0.1), + (18, -22, 0.1), + (-18, -20, 0.1), + (-6, -20, 0.1), + (0, -20, 0.1), + (6, -20, 0.1), + (-18, -18, 0.1), + (-6, -18, 0.1), + (0, -18, 0.1), + (6, -18, 0.1), + (-18, -16, 0.1), + (-16, -16, 0.1), + (-14, -16, 0.1), + (-12, -16, 0.1), + (-10, -16, 0.1), + (-8, -16, 0.1), + (-6, -16, 0.1), + (0, -16, 0.1), + (6, -16, 0.1), + (8, -16, 0.1), + (10, -16, 0.1), + (12, -16, 0.1), + (14, -16, 0.1), + (16, -16, 0.1), + (18, -16, 0.1), + (-6, -14, 0.1), + (0, -14, 0.1), + (6, -14, 0.1), + (18, -14, 0.1), + (-6, -12, 0.1), + (0, -12, 0.1), + (6, -12, 0.1), + (18, -12, 0.1), + (-18, -10, 0.1), + (-16, -10, 0.1), + (-14, -10, 0.1), + (-12, -10, 0.1), + (-10, -10, 0.1), + (-8, -10, 0.1), + (-6, -10, 0.1), + (6, -10, 0.1), + (8, -10, 0.1), + (10, -10, 0.1), + (12, -10, 0.1), + (14, -10, 0.1), + (16, -10, 0.1), + (18, -10, 0.1), + (-18, -8, 0.1), + (-12, -8, 0.1), + (-6, -8, 0.1), + (6, -8, 0.1), + (12, -8, 0.1), + (18, -8, 0.1), + (-18, -6, 0.1), + (-12, -6, 0.1), + (-6, -6, 0.1), + (6, -6, 0.1), + (12, -6, 0.1), + (18, -6, 0.1), + (-18, -4, 0.1), + (-12, -4, 0.1), + (-6, -4, 0.1), + (6, -4, 0.1), + (12, -4, 0.1), + (18, -4, 0.1), + (-18, -2, 0.1), + (-12, -2, 0.1), + (-6, -2, 0.1), + (6, -2, 0.1), + (12, -2, 0.1), + (18, -2, 0.1), + (-18, 0, 0.1), + (-16, 0, 0.1), + (-14, 0, 0.1), + (-12, 0, 0.1), + (-10, 0, 0.1), + (-8, 0, 0.1), + (-6, 0, 0.1), + (6, 0, 0.1), + (8, 0, 0.1), + (10, 0, 0.1), + (12, 0, 0.1), + (14, 0, 0.1), + (16, 0, 0.1), + (18, 0, 0.1), + (-18, 2, 0.1), + (-12, 2, 0.1), + (-6, 2, 0.1), + (6, 2, 0.1), + (12, 2, 0.1), + (18, 2, 0.1), + (-18, 4, 0.1), + (-12, 4, 0.1), + (-6, 4, 0.1), + (6, 4, 0.1), + (12, 4, 0.1), + (18, 4, 0.1), + (-18, 6, 0.1), + (-12, 6, 0.1), + (-6, 6, 0.1), + (6, 6, 0.1), + (12, 6, 0.1), + (18, 6, 0.1), + (-18, 8, 0.1), + (-12, 8, 0.1), + (-6, 8, 0.1), + (6, 8, 0.1), + (12, 8, 0.1), + (18, 8, 0.1), + (-18, 10, 0.1), + (-16, 10, 0.1), + (-14, 10, 0.1), + (-12, 10, 0.1), + (-10, 10, 0.1), + (-8, 10, 0.1), + (-6, 10, 0.1), + (6, 10, 0.1), + (8, 10, 0.1), + (10, 10, 0.1), + (12, 10, 0.1), + (14, 10, 0.1), + (16, 10, 0.1), + (18, 10, 0.1), + (-6, 12, 0.1), + (0, 12, 0.1), + (6, 12, 0.1), + (18, 12, 0.1), + (-6, 14, 0.1), + (0, 14, 0.1), + (6, 14, 0.1), + (18, 14, 0.1), + (-18, 16, 0.1), + (-16, 16, 0.1), + (-14, 16, 0.1), + (-12, 16, 0.1), + (-10, 16, 0.1), + (-8, 16, 0.1), + (-6, 16, 0.1), + (0, 16, 0.1), + (6, 16, 0.1), + (8, 16, 0.1), + (10, 16, 0.1), + (12, 16, 0.1), + (14, 16, 0.1), + (16, 16, 0.1), + (18, 16, 0.1), + (-18, 18, 0.1), + (-6, 18, 0.1), + (0, 18, 0.1), + (6, 18, 0.1), + (-18, 20, 0.1), + (-6, 20, 0.1), + (0, 20, 0.1), + (6, 20, 0.1), + (-18, 22, 0.1), + (-16, 22, 0.1), + (-14, 22, 0.1), + (-12, 22, 0.1), + (-6, 22, 0.1), + (0, 22, 0.1), + (6, 22, 0.1), + (12, 22, 0.1), + (14, 22, 0.1), + (16, 22, 0.1), + (18, 22, 0.1), + (-18, 24, 0.1), + (-12, 24, 0.1), + (-6, 24, 0.1), + (-4, 24, 0.1), + (-2, 24, 0.1), + (0, 24, 0.1), + (2, 24, 0.1), + (4, 24, 0.1), + (6, 24, 0.1), + (12, 24, 0.1), + (18, 24, 0.1), + (-18, 26, 0.1), + (-12, 26, 0.1), + (-2, 26, 0.1), + (0, 26, 0.1), + (2, 26, 0.1), + (12, 26, 0.1), + (18, 26, 0.1), + (-18, 28, 0.1), + (-12, 28, 0.1), + (-2, 28, 0.1), + (0, 28, 0.1), + (2, 28, 0.1), + (12, 28, 0.1), + (18, 28, 0.1), + (-28, 30, 0.1), + (-26, 30, 0.1), + (-24, 30, 0.1), + (-22, 30, 0.1), + (-20, 30, 0.1), + (-18, 30, 0.1), + (-12, 30, 0.1), + (-10, 30, 0.1), + (-8, 30, 0.1), + (-6, 30, 0.1), + (-4, 30, 0.1), + (-2, 30, 0.1), + (0, 30, 0.1), + (2, 30, 0.1), + (4, 30, 0.1), + (6, 30, 0.1), + (8, 30, 0.1), + (10, 30, 0.1), + (12, 30, 0.1), + (18, 30, 0.1), + (20, 30, 0.1), + (22, 30, 0.1), + (24, 30, 0.1), + (26, 30, 0.1), + (28, 30, 0.1), + (-28, 32, 0.1), + (-18, 32, 0.1), + (-8, 32, 0.1), + (8, 32, 0.1), + (18, 32, 0.1), + (-28, 34, 0.1), + (-18, 34, 0.1), + (-8, 34, 0.1), + (8, 34, 0.1), + (18, 34, 0.1), + (-28, 36, 0.1), + (-22, 36, 0.1), + (-20, 36, 0.1), + (-18, 36, 0.1), + (-16, 36, 0.1), + (-14, 36, 0.1), + (-12, 36, 0.1), + (-10, 36, 0.1), + (-8, 36, 0.1), + (-6, 36, 0.1), + (-4, 36, 0.1), + (-2, 36, 0.1), + (0, 36, 0.1), + (2, 36, 0.1), + (4, 36, 0.1), + (6, 36, 0.1), + (8, 36, 0.1), + (10, 36, 0.1), + (12, 36, 0.1), + (14, 36, 0.1), + (16, 36, 0.1), + (18, 36, 0.1), + (20, 36, 0.1), + (22, 36, 0.1), + (24, 36, 0.1), + (26, 36, 0.1), + (28, 36, 0.1), + (-28, 38, 0.1), + (-22, 38, 0.1), + (-12, 38, 0.1), + (12, 38, 0.1), + (18, 38, 0.1), + (28, 38, 0.1), + (-28, 40, 0.1), + (-26, 40, 0.1), + (-24, 40, 0.1), + (-22, 40, 0.1), + (-12, 40, 0.1), + (12, 40, 0.1), + (18, 40, 0.1), + (28, 40, 0.1), + (-28, 42, 0.1), + (-12, 42, 0.1), + (12, 42, 0.1), + (18, 42, 0.1), + (28, 42, 0.1), + (-28, 44, 0.1), + (-12, 44, 0.1), + (12, 44, 0.1), + (18, 44, 0.1), + (28, 44, 0.1), + (-28, 46, 0.1), + (-26, 46, 0.1), + (-24, 46, 0.1), + (-22, 46, 0.1), + (-20, 46, 0.1), + (-18, 46, 0.1), + (-16, 46, 0.1), + (-14, 46, 0.1), + (-12, 46, 0.1), + (12, 46, 0.1), + (18, 46, 0.1), + (20, 46, 0.1), + (22, 46, 0.1), + (24, 46, 0.1), + (26, 46, 0.1), + (28, 46, 0.1)] +mazeData['phase_4/models/minigames/maze_3player'] = {} +data = mazeData['phase_4/models/minigames/maze_3player'] +data['width'] = 46 +data['height'] = 45 +data['originX'] = 23 +data['originY'] = 19 +data['collisionTable'] = [[1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1]] +data['treasurePosList'] = [(-22, -34, 0.1), + (-20, -34, 0.1), + (-18, -34, 0.1), + (-16, -34, 0.1), + (-14, -34, 0.1), + (-12, -34, 0.1), + (-10, -34, 0.1), + (-8, -34, 0.1), + (-6, -34, 0.1), + (-4, -34, 0.1), + (-2, -34, 0.1), + (0, -34, 0.1), + (2, -34, 0.1), + (4, -34, 0.1), + (6, -34, 0.1), + (8, -34, 0.1), + (10, -34, 0.1), + (12, -34, 0.1), + (14, -34, 0.1), + (16, -34, 0.1), + (18, -34, 0.1), + (20, -34, 0.1), + (22, -34, 0.1), + (-22, -32, 0.1), + (-6, -32, 0.1), + (6, -32, 0.1), + (22, -32, 0.1), + (-22, -30, 0.1), + (-6, -30, 0.1), + (6, -30, 0.1), + (22, -30, 0.1), + (-22, -28, 0.1), + (-20, -28, 0.1), + (-18, -28, 0.1), + (-16, -28, 0.1), + (-14, -28, 0.1), + (-12, -28, 0.1), + (-6, -28, 0.1), + (6, -28, 0.1), + (12, -28, 0.1), + (14, -28, 0.1), + (16, -28, 0.1), + (18, -28, 0.1), + (20, -28, 0.1), + (22, -28, 0.1), + (-22, -26, 0.1), + (-12, -26, 0.1), + (-6, -26, 0.1), + (6, -26, 0.1), + (12, -26, 0.1), + (22, -26, 0.1), + (-22, -24, 0.1), + (-12, -24, 0.1), + (-6, -24, 0.1), + (6, -24, 0.1), + (12, -24, 0.1), + (22, -24, 0.1), + (-22, -22, 0.1), + (-12, -22, 0.1), + (-6, -22, 0.1), + (6, -22, 0.1), + (12, -22, 0.1), + (22, -22, 0.1), + (-22, -20, 0.1), + (-16, -20, 0.1), + (-14, -20, 0.1), + (-12, -20, 0.1), + (-10, -20, 0.1), + (-8, -20, 0.1), + (-6, -20, 0.1), + (-4, -20, 0.1), + (-2, -20, 0.1), + (0, -20, 0.1), + (2, -20, 0.1), + (4, -20, 0.1), + (6, -20, 0.1), + (8, -20, 0.1), + (10, -20, 0.1), + (12, -20, 0.1), + (14, -20, 0.1), + (16, -20, 0.1), + (22, -20, 0.1), + (-22, -18, 0.1), + (-16, -18, 0.1), + (0, -18, 0.1), + (16, -18, 0.1), + (22, -18, 0.1), + (-22, -16, 0.1), + (-16, -16, 0.1), + (0, -16, 0.1), + (16, -16, 0.1), + (22, -16, 0.1), + (-42, -14, 0.1), + (-40, -14, 0.1), + (-38, -14, 0.1), + (-36, -14, 0.1), + (-34, -14, 0.1), + (-32, -14, 0.1), + (-30, -14, 0.1), + (-28, -14, 0.1), + (-26, -14, 0.1), + (-24, -14, 0.1), + (-22, -14, 0.1), + (-16, -14, 0.1), + (-14, -14, 0.1), + (-12, -14, 0.1), + (-10, -14, 0.1), + (-8, -14, 0.1), + (-6, -14, 0.1), + (-4, -14, 0.1), + (-2, -14, 0.1), + (0, -14, 0.1), + (2, -14, 0.1), + (4, -14, 0.1), + (6, -14, 0.1), + (8, -14, 0.1), + (10, -14, 0.1), + (12, -14, 0.1), + (14, -14, 0.1), + (16, -14, 0.1), + (22, -14, 0.1), + (24, -14, 0.1), + (26, -14, 0.1), + (28, -14, 0.1), + (30, -14, 0.1), + (32, -14, 0.1), + (34, -14, 0.1), + (36, -14, 0.1), + (38, -14, 0.1), + (40, -14, 0.1), + (42, -14, 0.1), + (-42, -12, 0.1), + (-16, -12, 0.1), + (0, -12, 0.1), + (16, -12, 0.1), + (42, -12, 0.1), + (-42, -10, 0.1), + (-16, -10, 0.1), + (0, -10, 0.1), + (16, -10, 0.1), + (42, -10, 0.1), + (-42, -8, 0.1), + (-40, -8, 0.1), + (-38, -8, 0.1), + (-36, -8, 0.1), + (-34, -8, 0.1), + (-32, -8, 0.1), + (-30, -8, 0.1), + (-24, -8, 0.1), + (-22, -8, 0.1), + (-20, -8, 0.1), + (-18, -8, 0.1), + (-16, -8, 0.1), + (-14, -8, 0.1), + (-12, -8, 0.1), + (-10, -8, 0.1), + (-8, -8, 0.1), + (8, -8, 0.1), + (10, -8, 0.1), + (12, -8, 0.1), + (14, -8, 0.1), + (16, -8, 0.1), + (18, -8, 0.1), + (20, -8, 0.1), + (22, -8, 0.1), + (24, -8, 0.1), + (30, -8, 0.1), + (32, -8, 0.1), + (34, -8, 0.1), + (36, -8, 0.1), + (38, -8, 0.1), + (40, -8, 0.1), + (42, -8, 0.1), + (-42, -6, 0.1), + (-36, -6, 0.1), + (-30, -6, 0.1), + (-24, -6, 0.1), + (-16, -6, 0.1), + (16, -6, 0.1), + (24, -6, 0.1), + (30, -6, 0.1), + (36, -6, 0.1), + (42, -6, 0.1), + (-42, -4, 0.1), + (-36, -4, 0.1), + (-30, -4, 0.1), + (-24, -4, 0.1), + (-16, -4, 0.1), + (16, -4, 0.1), + (24, -4, 0.1), + (30, -4, 0.1), + (36, -4, 0.1), + (42, -4, 0.1), + (-42, -2, 0.1), + (-36, -2, 0.1), + (-30, -2, 0.1), + (-24, -2, 0.1), + (-16, -2, 0.1), + (16, -2, 0.1), + (24, -2, 0.1), + (30, -2, 0.1), + (36, -2, 0.1), + (42, -2, 0.1), + (-42, 0, 0.1), + (-36, 0, 0.1), + (-30, 0, 0.1), + (-24, 0, 0.1), + (-16, 0, 0.1), + (16, 0, 0.1), + (24, 0, 0.1), + (30, 0, 0.1), + (36, 0, 0.1), + (42, 0, 0.1), + (-42, 2, 0.1), + (-36, 2, 0.1), + (-30, 2, 0.1), + (-24, 2, 0.1), + (-16, 2, 0.1), + (16, 2, 0.1), + (24, 2, 0.1), + (30, 2, 0.1), + (36, 2, 0.1), + (42, 2, 0.1), + (-42, 4, 0.1), + (-40, 4, 0.1), + (-38, 4, 0.1), + (-36, 4, 0.1), + (-34, 4, 0.1), + (-32, 4, 0.1), + (-30, 4, 0.1), + (-28, 4, 0.1), + (-26, 4, 0.1), + (-24, 4, 0.1), + (-22, 4, 0.1), + (-20, 4, 0.1), + (-18, 4, 0.1), + (-16, 4, 0.1), + (-14, 4, 0.1), + (-12, 4, 0.1), + (-10, 4, 0.1), + (-8, 4, 0.1), + (8, 4, 0.1), + (10, 4, 0.1), + (12, 4, 0.1), + (14, 4, 0.1), + (16, 4, 0.1), + (18, 4, 0.1), + (20, 4, 0.1), + (22, 4, 0.1), + (24, 4, 0.1), + (26, 4, 0.1), + (28, 4, 0.1), + (30, 4, 0.1), + (32, 4, 0.1), + (34, 4, 0.1), + (36, 4, 0.1), + (38, 4, 0.1), + (40, 4, 0.1), + (42, 4, 0.1), + (-42, 6, 0.1), + (-16, 6, 0.1), + (16, 6, 0.1), + (42, 6, 0.1), + (-42, 8, 0.1), + (-16, 8, 0.1), + (16, 8, 0.1), + (42, 8, 0.1), + (-42, 10, 0.1), + (-40, 10, 0.1), + (-38, 10, 0.1), + (-36, 10, 0.1), + (-34, 10, 0.1), + (-32, 10, 0.1), + (-30, 10, 0.1), + (-24, 10, 0.1), + (-22, 10, 0.1), + (-20, 10, 0.1), + (-18, 10, 0.1), + (-16, 10, 0.1), + (-14, 10, 0.1), + (-12, 10, 0.1), + (-10, 10, 0.1), + (-8, 10, 0.1), + (-6, 10, 0.1), + (0, 10, 0.1), + (6, 10, 0.1), + (8, 10, 0.1), + (10, 10, 0.1), + (12, 10, 0.1), + (14, 10, 0.1), + (16, 10, 0.1), + (18, 10, 0.1), + (20, 10, 0.1), + (22, 10, 0.1), + (24, 10, 0.1), + (30, 10, 0.1), + (32, 10, 0.1), + (34, 10, 0.1), + (36, 10, 0.1), + (38, 10, 0.1), + (40, 10, 0.1), + (42, 10, 0.1), + (-42, 12, 0.1), + (-36, 12, 0.1), + (-30, 12, 0.1), + (-24, 12, 0.1), + (-16, 12, 0.1), + (-6, 12, 0.1), + (0, 12, 0.1), + (6, 12, 0.1), + (16, 12, 0.1), + (24, 12, 0.1), + (30, 12, 0.1), + (36, 12, 0.1), + (42, 12, 0.1), + (-42, 14, 0.1), + (-36, 14, 0.1), + (-30, 14, 0.1), + (-24, 14, 0.1), + (-16, 14, 0.1), + (-6, 14, 0.1), + (0, 14, 0.1), + (6, 14, 0.1), + (16, 14, 0.1), + (24, 14, 0.1), + (30, 14, 0.1), + (36, 14, 0.1), + (42, 14, 0.1), + (-42, 16, 0.1), + (-36, 16, 0.1), + (-30, 16, 0.1), + (-24, 16, 0.1), + (-16, 16, 0.1), + (-6, 16, 0.1), + (-4, 16, 0.1), + (-2, 16, 0.1), + (0, 16, 0.1), + (2, 16, 0.1), + (4, 16, 0.1), + (6, 16, 0.1), + (16, 16, 0.1), + (24, 16, 0.1), + (30, 16, 0.1), + (36, 16, 0.1), + (42, 16, 0.1), + (-42, 18, 0.1), + (-36, 18, 0.1), + (-30, 18, 0.1), + (-24, 18, 0.1), + (-16, 18, 0.1), + (-6, 18, 0.1), + (0, 18, 0.1), + (6, 18, 0.1), + (16, 18, 0.1), + (24, 18, 0.1), + (30, 18, 0.1), + (36, 18, 0.1), + (42, 18, 0.1), + (-42, 20, 0.1), + (-36, 20, 0.1), + (-30, 20, 0.1), + (-24, 20, 0.1), + (-16, 20, 0.1), + (-6, 20, 0.1), + (0, 20, 0.1), + (6, 20, 0.1), + (16, 20, 0.1), + (24, 20, 0.1), + (30, 20, 0.1), + (36, 20, 0.1), + (42, 20, 0.1), + (-42, 22, 0.1), + (-40, 22, 0.1), + (-38, 22, 0.1), + (-36, 22, 0.1), + (-34, 22, 0.1), + (-32, 22, 0.1), + (-30, 22, 0.1), + (-28, 22, 0.1), + (-26, 22, 0.1), + (-24, 22, 0.1), + (-22, 22, 0.1), + (-20, 22, 0.1), + (-18, 22, 0.1), + (-16, 22, 0.1), + (-14, 22, 0.1), + (-12, 22, 0.1), + (-10, 22, 0.1), + (-8, 22, 0.1), + (-6, 22, 0.1), + (0, 22, 0.1), + (6, 22, 0.1), + (8, 22, 0.1), + (10, 22, 0.1), + (12, 22, 0.1), + (14, 22, 0.1), + (16, 22, 0.1), + (18, 22, 0.1), + (20, 22, 0.1), + (22, 22, 0.1), + (24, 22, 0.1), + (26, 22, 0.1), + (28, 22, 0.1), + (30, 22, 0.1), + (32, 22, 0.1), + (34, 22, 0.1), + (36, 22, 0.1), + (38, 22, 0.1), + (40, 22, 0.1), + (42, 22, 0.1), + (-42, 24, 0.1), + (-16, 24, 0.1), + (0, 24, 0.1), + (16, 24, 0.1), + (42, 24, 0.1), + (-42, 26, 0.1), + (-16, 26, 0.1), + (0, 26, 0.1), + (16, 26, 0.1), + (42, 26, 0.1), + (-42, 28, 0.1), + (-40, 28, 0.1), + (-38, 28, 0.1), + (-36, 28, 0.1), + (-34, 28, 0.1), + (-32, 28, 0.1), + (-30, 28, 0.1), + (-28, 28, 0.1), + (-26, 28, 0.1), + (-24, 28, 0.1), + (-22, 28, 0.1), + (-20, 28, 0.1), + (-18, 28, 0.1), + (-16, 28, 0.1), + (-14, 28, 0.1), + (-12, 28, 0.1), + (-10, 28, 0.1), + (-8, 28, 0.1), + (-6, 28, 0.1), + (-4, 28, 0.1), + (-2, 28, 0.1), + (0, 28, 0.1), + (2, 28, 0.1), + (4, 28, 0.1), + (6, 28, 0.1), + (8, 28, 0.1), + (10, 28, 0.1), + (12, 28, 0.1), + (14, 28, 0.1), + (16, 28, 0.1), + (18, 28, 0.1), + (20, 28, 0.1), + (22, 28, 0.1), + (24, 28, 0.1), + (26, 28, 0.1), + (28, 28, 0.1), + (30, 28, 0.1), + (32, 28, 0.1), + (34, 28, 0.1), + (36, 28, 0.1), + (38, 28, 0.1), + (40, 28, 0.1), + (42, 28, 0.1), + (-22, 30, 0.1), + (-16, 30, 0.1), + (0, 30, 0.1), + (16, 30, 0.1), + (22, 30, 0.1), + (-22, 32, 0.1), + (-16, 32, 0.1), + (0, 32, 0.1), + (16, 32, 0.1), + (22, 32, 0.1), + (-22, 34, 0.1), + (-16, 34, 0.1), + (-14, 34, 0.1), + (-12, 34, 0.1), + (-6, 34, 0.1), + (-4, 34, 0.1), + (-2, 34, 0.1), + (0, 34, 0.1), + (2, 34, 0.1), + (4, 34, 0.1), + (6, 34, 0.1), + (12, 34, 0.1), + (14, 34, 0.1), + (16, 34, 0.1), + (22, 34, 0.1), + (-22, 36, 0.1), + (-12, 36, 0.1), + (-6, 36, 0.1), + (6, 36, 0.1), + (12, 36, 0.1), + (22, 36, 0.1), + (-22, 38, 0.1), + (-12, 38, 0.1), + (-6, 38, 0.1), + (6, 38, 0.1), + (12, 38, 0.1), + (22, 38, 0.1), + (-22, 40, 0.1), + (-12, 40, 0.1), + (-6, 40, 0.1), + (6, 40, 0.1), + (12, 40, 0.1), + (22, 40, 0.1), + (-22, 42, 0.1), + (-20, 42, 0.1), + (-18, 42, 0.1), + (-16, 42, 0.1), + (-14, 42, 0.1), + (-12, 42, 0.1), + (-6, 42, 0.1), + (-4, 42, 0.1), + (-2, 42, 0.1), + (0, 42, 0.1), + (2, 42, 0.1), + (4, 42, 0.1), + (6, 42, 0.1), + (12, 42, 0.1), + (14, 42, 0.1), + (16, 42, 0.1), + (18, 42, 0.1), + (20, 42, 0.1), + (22, 42, 0.1), + (-22, 44, 0.1), + (-6, 44, 0.1), + (6, 44, 0.1), + (22, 44, 0.1), + (-22, 46, 0.1), + (-6, 46, 0.1), + (6, 46, 0.1), + (22, 46, 0.1), + (-22, 48, 0.1), + (-20, 48, 0.1), + (-18, 48, 0.1), + (-16, 48, 0.1), + (-14, 48, 0.1), + (-12, 48, 0.1), + (-10, 48, 0.1), + (-8, 48, 0.1), + (-6, 48, 0.1), + (-4, 48, 0.1), + (-2, 48, 0.1), + (0, 48, 0.1), + (2, 48, 0.1), + (4, 48, 0.1), + (6, 48, 0.1), + (8, 48, 0.1), + (10, 48, 0.1), + (12, 48, 0.1), + (14, 48, 0.1), + (16, 48, 0.1), + (18, 48, 0.1), + (20, 48, 0.1), + (22, 48, 0.1)] +mazeData['phase_4/models/minigames/maze_4player'] = {} +data = mazeData['phase_4/models/minigames/maze_4player'] +data['width'] = 50 +data['height'] = 40 +data['originX'] = 25 +data['originY'] = 20 +data['collisionTable'] = [[1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1]] +data['treasurePosList'] = [(-46, -36, 0.1), + (-44, -36, 0.1), + (-42, -36, 0.1), + (-40, -36, 0.1), + (-34, -36, 0.1), + (-32, -36, 0.1), + (-30, -36, 0.1), + (-28, -36, 0.1), + (-18, -36, 0.1), + (-10, -36, 0.1), + (-8, -36, 0.1), + (-6, -36, 0.1), + (-4, -36, 0.1), + (-2, -36, 0.1), + (0, -36, 0.1), + (2, -36, 0.1), + (4, -36, 0.1), + (6, -36, 0.1), + (8, -36, 0.1), + (10, -36, 0.1), + (18, -36, 0.1), + (28, -36, 0.1), + (30, -36, 0.1), + (32, -36, 0.1), + (34, -36, 0.1), + (40, -36, 0.1), + (42, -36, 0.1), + (44, -36, 0.1), + (46, -36, 0.1), + (-46, -34, 0.1), + (-40, -34, 0.1), + (-34, -34, 0.1), + (-28, -34, 0.1), + (-18, -34, 0.1), + (-10, -34, 0.1), + (0, -34, 0.1), + (10, -34, 0.1), + (18, -34, 0.1), + (28, -34, 0.1), + (34, -34, 0.1), + (40, -34, 0.1), + (46, -34, 0.1), + (-46, -32, 0.1), + (-40, -32, 0.1), + (-38, -32, 0.1), + (-36, -32, 0.1), + (-34, -32, 0.1), + (-28, -32, 0.1), + (-18, -32, 0.1), + (-10, -32, 0.1), + (0, -32, 0.1), + (10, -32, 0.1), + (18, -32, 0.1), + (28, -32, 0.1), + (34, -32, 0.1), + (36, -32, 0.1), + (38, -32, 0.1), + (40, -32, 0.1), + (46, -32, 0.1), + (-46, -30, 0.1), + (-40, -30, 0.1), + (-28, -30, 0.1), + (-18, -30, 0.1), + (-10, -30, 0.1), + (0, -30, 0.1), + (10, -30, 0.1), + (18, -30, 0.1), + (28, -30, 0.1), + (40, -30, 0.1), + (46, -30, 0.1), + (-46, -28, 0.1), + (-40, -28, 0.1), + (-28, -28, 0.1), + (-18, -28, 0.1), + (-10, -28, 0.1), + (0, -28, 0.1), + (10, -28, 0.1), + (18, -28, 0.1), + (28, -28, 0.1), + (40, -28, 0.1), + (46, -28, 0.1), + (-46, -26, 0.1), + (-40, -26, 0.1), + (-38, -26, 0.1), + (-36, -26, 0.1), + (-34, -26, 0.1), + (-32, -26, 0.1), + (-30, -26, 0.1), + (-28, -26, 0.1), + (-26, -26, 0.1), + (-24, -26, 0.1), + (-22, -26, 0.1), + (-20, -26, 0.1), + (-18, -26, 0.1), + (-16, -26, 0.1), + (-14, -26, 0.1), + (-12, -26, 0.1), + (-10, -26, 0.1), + (-8, -26, 0.1), + (-6, -26, 0.1), + (-4, -26, 0.1), + (-2, -26, 0.1), + (0, -26, 0.1), + (2, -26, 0.1), + (4, -26, 0.1), + (6, -26, 0.1), + (8, -26, 0.1), + (10, -26, 0.1), + (12, -26, 0.1), + (14, -26, 0.1), + (16, -26, 0.1), + (18, -26, 0.1), + (20, -26, 0.1), + (22, -26, 0.1), + (24, -26, 0.1), + (26, -26, 0.1), + (28, -26, 0.1), + (30, -26, 0.1), + (32, -26, 0.1), + (34, -26, 0.1), + (36, -26, 0.1), + (38, -26, 0.1), + (40, -26, 0.1), + (46, -26, 0.1), + (-46, -24, 0.1), + (-34, -24, 0.1), + (-28, -24, 0.1), + (-16, -24, 0.1), + (0, -24, 0.1), + (16, -24, 0.1), + (28, -24, 0.1), + (34, -24, 0.1), + (46, -24, 0.1), + (-46, -22, 0.1), + (-34, -22, 0.1), + (-28, -22, 0.1), + (-16, -22, 0.1), + (0, -22, 0.1), + (16, -22, 0.1), + (28, -22, 0.1), + (34, -22, 0.1), + (46, -22, 0.1), + (-46, -20, 0.1), + (-40, -20, 0.1), + (-38, -20, 0.1), + (-36, -20, 0.1), + (-34, -20, 0.1), + (-28, -20, 0.1), + (-26, -20, 0.1), + (-24, -20, 0.1), + (-22, -20, 0.1), + (-20, -20, 0.1), + (-18, -20, 0.1), + (-16, -20, 0.1), + (-14, -20, 0.1), + (-12, -20, 0.1), + (-6, -20, 0.1), + (-4, -20, 0.1), + (-2, -20, 0.1), + (0, -20, 0.1), + (2, -20, 0.1), + (4, -20, 0.1), + (6, -20, 0.1), + (12, -20, 0.1), + (14, -20, 0.1), + (16, -20, 0.1), + (18, -20, 0.1), + (20, -20, 0.1), + (22, -20, 0.1), + (24, -20, 0.1), + (26, -20, 0.1), + (28, -20, 0.1), + (34, -20, 0.1), + (36, -20, 0.1), + (38, -20, 0.1), + (40, -20, 0.1), + (46, -20, 0.1), + (-46, -18, 0.1), + (-40, -18, 0.1), + (-34, -18, 0.1), + (-28, -18, 0.1), + (-22, -18, 0.1), + (-12, -18, 0.1), + (-6, -18, 0.1), + (0, -18, 0.1), + (6, -18, 0.1), + (12, -18, 0.1), + (22, -18, 0.1), + (28, -18, 0.1), + (34, -18, 0.1), + (40, -18, 0.1), + (46, -18, 0.1), + (-46, -16, 0.1), + (-44, -16, 0.1), + (-42, -16, 0.1), + (-40, -16, 0.1), + (-34, -16, 0.1), + (-32, -16, 0.1), + (-30, -16, 0.1), + (-28, -16, 0.1), + (-22, -16, 0.1), + (-12, -16, 0.1), + (-10, -16, 0.1), + (-8, -16, 0.1), + (-6, -16, 0.1), + (0, -16, 0.1), + (6, -16, 0.1), + (8, -16, 0.1), + (10, -16, 0.1), + (12, -16, 0.1), + (22, -16, 0.1), + (28, -16, 0.1), + (30, -16, 0.1), + (32, -16, 0.1), + (34, -16, 0.1), + (40, -16, 0.1), + (42, -16, 0.1), + (44, -16, 0.1), + (46, -16, 0.1), + (-46, -14, 0.1), + (-34, -14, 0.1), + (-22, -14, 0.1), + (-12, -14, 0.1), + (0, -14, 0.1), + (12, -14, 0.1), + (22, -14, 0.1), + (34, -14, 0.1), + (46, -14, 0.1), + (-46, -12, 0.1), + (-34, -12, 0.1), + (-22, -12, 0.1), + (-12, -12, 0.1), + (0, -12, 0.1), + (12, -12, 0.1), + (22, -12, 0.1), + (34, -12, 0.1), + (46, -12, 0.1), + (-46, -10, 0.1), + (-44, -10, 0.1), + (-42, -10, 0.1), + (-40, -10, 0.1), + (-34, -10, 0.1), + (-32, -10, 0.1), + (-30, -10, 0.1), + (-28, -10, 0.1), + (-22, -10, 0.1), + (-12, -10, 0.1), + (-10, -10, 0.1), + (-8, -10, 0.1), + (-6, -10, 0.1), + (0, -10, 0.1), + (6, -10, 0.1), + (8, -10, 0.1), + (10, -10, 0.1), + (12, -10, 0.1), + (22, -10, 0.1), + (28, -10, 0.1), + (30, -10, 0.1), + (32, -10, 0.1), + (34, -10, 0.1), + (40, -10, 0.1), + (42, -10, 0.1), + (44, -10, 0.1), + (46, -10, 0.1), + (-46, -8, 0.1), + (-40, -8, 0.1), + (-34, -8, 0.1), + (-28, -8, 0.1), + (-22, -8, 0.1), + (-12, -8, 0.1), + (12, -8, 0.1), + (22, -8, 0.1), + (28, -8, 0.1), + (34, -8, 0.1), + (40, -8, 0.1), + (46, -8, 0.1), + (-46, -6, 0.1), + (-40, -6, 0.1), + (-38, -6, 0.1), + (-36, -6, 0.1), + (-34, -6, 0.1), + (-28, -6, 0.1), + (-26, -6, 0.1), + (-24, -6, 0.1), + (-22, -6, 0.1), + (-20, -6, 0.1), + (-18, -6, 0.1), + (-16, -6, 0.1), + (-14, -6, 0.1), + (-12, -6, 0.1), + (12, -6, 0.1), + (14, -6, 0.1), + (16, -6, 0.1), + (18, -6, 0.1), + (20, -6, 0.1), + (22, -6, 0.1), + (24, -6, 0.1), + (26, -6, 0.1), + (28, -6, 0.1), + (34, -6, 0.1), + (36, -6, 0.1), + (38, -6, 0.1), + (40, -6, 0.1), + (46, -6, 0.1), + (-46, -4, 0.1), + (-34, -4, 0.1), + (-28, -4, 0.1), + (-16, -4, 0.1), + (16, -4, 0.1), + (28, -4, 0.1), + (34, -4, 0.1), + (46, -4, 0.1), + (-46, -2, 0.1), + (-34, -2, 0.1), + (-28, -2, 0.1), + (-16, -2, 0.1), + (16, -2, 0.1), + (28, -2, 0.1), + (34, -2, 0.1), + (46, -2, 0.1), + (-46, 0, 0.1), + (-40, 0, 0.1), + (-38, 0, 0.1), + (-36, 0, 0.1), + (-34, 0, 0.1), + (-28, 0, 0.1), + (-26, 0, 0.1), + (-24, 0, 0.1), + (-22, 0, 0.1), + (-20, 0, 0.1), + (-18, 0, 0.1), + (-16, 0, 0.1), + (-14, 0, 0.1), + (-12, 0, 0.1), + (-10, 0, 0.1), + (10, 0, 0.1), + (12, 0, 0.1), + (14, 0, 0.1), + (16, 0, 0.1), + (18, 0, 0.1), + (20, 0, 0.1), + (22, 0, 0.1), + (24, 0, 0.1), + (26, 0, 0.1), + (28, 0, 0.1), + (34, 0, 0.1), + (36, 0, 0.1), + (38, 0, 0.1), + (40, 0, 0.1), + (46, 0, 0.1), + (-46, 2, 0.1), + (-34, 2, 0.1), + (-28, 2, 0.1), + (-16, 2, 0.1), + (16, 2, 0.1), + (28, 2, 0.1), + (34, 2, 0.1), + (46, 2, 0.1), + (-46, 4, 0.1), + (-34, 4, 0.1), + (-28, 4, 0.1), + (-16, 4, 0.1), + (16, 4, 0.1), + (28, 4, 0.1), + (34, 4, 0.1), + (46, 4, 0.1), + (-46, 6, 0.1), + (-40, 6, 0.1), + (-38, 6, 0.1), + (-36, 6, 0.1), + (-34, 6, 0.1), + (-28, 6, 0.1), + (-26, 6, 0.1), + (-24, 6, 0.1), + (-22, 6, 0.1), + (-20, 6, 0.1), + (-18, 6, 0.1), + (-16, 6, 0.1), + (-14, 6, 0.1), + (-12, 6, 0.1), + (12, 6, 0.1), + (14, 6, 0.1), + (16, 6, 0.1), + (18, 6, 0.1), + (20, 6, 0.1), + (22, 6, 0.1), + (24, 6, 0.1), + (26, 6, 0.1), + (28, 6, 0.1), + (34, 6, 0.1), + (36, 6, 0.1), + (38, 6, 0.1), + (40, 6, 0.1), + (46, 6, 0.1), + (-46, 8, 0.1), + (-40, 8, 0.1), + (-34, 8, 0.1), + (-28, 8, 0.1), + (-22, 8, 0.1), + (-12, 8, 0.1), + (12, 8, 0.1), + (22, 8, 0.1), + (28, 8, 0.1), + (34, 8, 0.1), + (40, 8, 0.1), + (46, 8, 0.1), + (-46, 10, 0.1), + (-44, 10, 0.1), + (-42, 10, 0.1), + (-40, 10, 0.1), + (-34, 10, 0.1), + (-32, 10, 0.1), + (-30, 10, 0.1), + (-28, 10, 0.1), + (-22, 10, 0.1), + (-12, 10, 0.1), + (-10, 10, 0.1), + (-8, 10, 0.1), + (-6, 10, 0.1), + (0, 10, 0.1), + (6, 10, 0.1), + (8, 10, 0.1), + (10, 10, 0.1), + (12, 10, 0.1), + (22, 10, 0.1), + (28, 10, 0.1), + (30, 10, 0.1), + (32, 10, 0.1), + (34, 10, 0.1), + (40, 10, 0.1), + (42, 10, 0.1), + (44, 10, 0.1), + (46, 10, 0.1), + (-46, 12, 0.1), + (-34, 12, 0.1), + (-22, 12, 0.1), + (-12, 12, 0.1), + (0, 12, 0.1), + (12, 12, 0.1), + (22, 12, 0.1), + (34, 12, 0.1), + (46, 12, 0.1), + (-46, 14, 0.1), + (-34, 14, 0.1), + (-22, 14, 0.1), + (-12, 14, 0.1), + (0, 14, 0.1), + (12, 14, 0.1), + (22, 14, 0.1), + (34, 14, 0.1), + (46, 14, 0.1), + (-46, 16, 0.1), + (-44, 16, 0.1), + (-42, 16, 0.1), + (-40, 16, 0.1), + (-34, 16, 0.1), + (-32, 16, 0.1), + (-30, 16, 0.1), + (-28, 16, 0.1), + (-22, 16, 0.1), + (-12, 16, 0.1), + (-10, 16, 0.1), + (-8, 16, 0.1), + (-6, 16, 0.1), + (0, 16, 0.1), + (6, 16, 0.1), + (8, 16, 0.1), + (10, 16, 0.1), + (12, 16, 0.1), + (22, 16, 0.1), + (28, 16, 0.1), + (30, 16, 0.1), + (32, 16, 0.1), + (34, 16, 0.1), + (40, 16, 0.1), + (42, 16, 0.1), + (44, 16, 0.1), + (46, 16, 0.1), + (-46, 18, 0.1), + (-40, 18, 0.1), + (-34, 18, 0.1), + (-28, 18, 0.1), + (-22, 18, 0.1), + (-12, 18, 0.1), + (-6, 18, 0.1), + (0, 18, 0.1), + (6, 18, 0.1), + (12, 18, 0.1), + (22, 18, 0.1), + (28, 18, 0.1), + (34, 18, 0.1), + (40, 18, 0.1), + (46, 18, 0.1), + (-46, 20, 0.1), + (-40, 20, 0.1), + (-38, 20, 0.1), + (-36, 20, 0.1), + (-34, 20, 0.1), + (-28, 20, 0.1), + (-26, 20, 0.1), + (-24, 20, 0.1), + (-22, 20, 0.1), + (-20, 20, 0.1), + (-18, 20, 0.1), + (-16, 20, 0.1), + (-14, 20, 0.1), + (-12, 20, 0.1), + (-6, 20, 0.1), + (-4, 20, 0.1), + (-2, 20, 0.1), + (0, 20, 0.1), + (2, 20, 0.1), + (4, 20, 0.1), + (6, 20, 0.1), + (12, 20, 0.1), + (14, 20, 0.1), + (16, 20, 0.1), + (18, 20, 0.1), + (20, 20, 0.1), + (22, 20, 0.1), + (24, 20, 0.1), + (26, 20, 0.1), + (28, 20, 0.1), + (34, 20, 0.1), + (36, 20, 0.1), + (38, 20, 0.1), + (40, 20, 0.1), + (46, 20, 0.1), + (-46, 22, 0.1), + (-34, 22, 0.1), + (-28, 22, 0.1), + (-16, 22, 0.1), + (0, 22, 0.1), + (16, 22, 0.1), + (28, 22, 0.1), + (34, 22, 0.1), + (46, 22, 0.1), + (-46, 24, 0.1), + (-34, 24, 0.1), + (-28, 24, 0.1), + (-16, 24, 0.1), + (0, 24, 0.1), + (16, 24, 0.1), + (28, 24, 0.1), + (34, 24, 0.1), + (46, 24, 0.1), + (-46, 26, 0.1), + (-40, 26, 0.1), + (-38, 26, 0.1), + (-36, 26, 0.1), + (-34, 26, 0.1), + (-32, 26, 0.1), + (-30, 26, 0.1), + (-28, 26, 0.1), + (-26, 26, 0.1), + (-24, 26, 0.1), + (-22, 26, 0.1), + (-20, 26, 0.1), + (-18, 26, 0.1), + (-16, 26, 0.1), + (-14, 26, 0.1), + (-12, 26, 0.1), + (-10, 26, 0.1), + (-8, 26, 0.1), + (-6, 26, 0.1), + (-4, 26, 0.1), + (-2, 26, 0.1), + (0, 26, 0.1), + (2, 26, 0.1), + (4, 26, 0.1), + (6, 26, 0.1), + (8, 26, 0.1), + (10, 26, 0.1), + (12, 26, 0.1), + (14, 26, 0.1), + (16, 26, 0.1), + (18, 26, 0.1), + (20, 26, 0.1), + (22, 26, 0.1), + (24, 26, 0.1), + (26, 26, 0.1), + (28, 26, 0.1), + (30, 26, 0.1), + (32, 26, 0.1), + (34, 26, 0.1), + (36, 26, 0.1), + (38, 26, 0.1), + (40, 26, 0.1), + (46, 26, 0.1), + (-46, 28, 0.1), + (-40, 28, 0.1), + (-28, 28, 0.1), + (-18, 28, 0.1), + (-10, 28, 0.1), + (0, 28, 0.1), + (10, 28, 0.1), + (18, 28, 0.1), + (28, 28, 0.1), + (40, 28, 0.1), + (46, 28, 0.1), + (-46, 30, 0.1), + (-40, 30, 0.1), + (-28, 30, 0.1), + (-18, 30, 0.1), + (-10, 30, 0.1), + (0, 30, 0.1), + (10, 30, 0.1), + (18, 30, 0.1), + (28, 30, 0.1), + (40, 30, 0.1), + (46, 30, 0.1), + (-46, 32, 0.1), + (-40, 32, 0.1), + (-38, 32, 0.1), + (-36, 32, 0.1), + (-34, 32, 0.1), + (-28, 32, 0.1), + (-18, 32, 0.1), + (-10, 32, 0.1), + (0, 32, 0.1), + (10, 32, 0.1), + (18, 32, 0.1), + (28, 32, 0.1), + (34, 32, 0.1), + (36, 32, 0.1), + (38, 32, 0.1), + (40, 32, 0.1), + (46, 32, 0.1), + (-46, 34, 0.1), + (-40, 34, 0.1), + (-34, 34, 0.1), + (-28, 34, 0.1), + (-18, 34, 0.1), + (-10, 34, 0.1), + (0, 34, 0.1), + (10, 34, 0.1), + (18, 34, 0.1), + (28, 34, 0.1), + (34, 34, 0.1), + (40, 34, 0.1), + (46, 34, 0.1), + (-46, 36, 0.1), + (-44, 36, 0.1), + (-42, 36, 0.1), + (-40, 36, 0.1), + (-34, 36, 0.1), + (-32, 36, 0.1), + (-30, 36, 0.1), + (-28, 36, 0.1), + (-18, 36, 0.1), + (-10, 36, 0.1), + (-8, 36, 0.1), + (-6, 36, 0.1), + (-4, 36, 0.1), + (-2, 36, 0.1), + (0, 36, 0.1), + (2, 36, 0.1), + (4, 36, 0.1), + (6, 36, 0.1), + (8, 36, 0.1), + (10, 36, 0.1), + (18, 36, 0.1), + (28, 36, 0.1), + (30, 36, 0.1), + (32, 36, 0.1), + (34, 36, 0.1), + (40, 36, 0.1), + (42, 36, 0.1), + (44, 36, 0.1), + (46, 36, 0.1)] diff --git a/toontown/minigame/MazeGameGlobals.py b/toontown/minigame/MazeGameGlobals.py new file mode 100755 index 00000000..4ae9caaa --- /dev/null +++ b/toontown/minigame/MazeGameGlobals.py @@ -0,0 +1,32 @@ +from direct.showbase import RandomNumGen + +def getMazeName(gameDoId, numPlayers, mazeNames): + try: + return forcedMaze + except: + names = mazeNames[numPlayers - 1] + return names[RandomNumGen.randHash(gameDoId) % len(names)] + + +ENDLESS_GAME = config.GetBool('endless-maze-game', 0) +GAME_DURATION = 60.0 +SHOWSCORES_DURATION = 2.0 +SUIT_TIC_FREQ = int(256) +WALK_SAME_DIRECTION_PROB = 4 +WALK_TURN_AROUND_PROB = 30 +SUIT_START_POSITIONS = ((0.25, 0.25), + (0.75, 0.75), + (0.25, 0.75), + (0.75, 0.25), + (0.2, 0.5), + (0.8, 0.5), + (0.5, 0.2), + (0.5, 0.8), + (0.33, 0.0), + (0.66, 0.0), + (0.33, 1.0), + (0.66, 1.0), + (0.0, 0.33), + (0.0, 0.66), + (1.0, 0.33), + (1.0, 0.66)) diff --git a/toontown/minigame/MazeMapGui.py b/toontown/minigame/MazeMapGui.py new file mode 100755 index 00000000..904473be --- /dev/null +++ b/toontown/minigame/MazeMapGui.py @@ -0,0 +1,245 @@ +from direct.showbase.PythonUtil import Enum +from direct.gui.DirectGui import DirectFrame, DGG +from pandac.PandaModules import Vec2, VBase4F +from pandac.PandaModules import CardMaker, NodePath +from pandac.PandaModules import Texture, PNMImage +DEFAULT_MASK_RESOLUTION = 32 +DEFAULT_RADIUS_RATIO = 0.05 +MAP_RESOLUTION = 320 +MazeRevealType = Enum(('SmoothCircle', 'HardCircle', 'Square')) +MAZE_REVEAL_TYPE = MazeRevealType.SmoothCircle + +class MazeMapGui(DirectFrame): + notify = directNotify.newCategory('MazeMapGui') + + def __init__(self, mazeCollTable, maskResolution = None, radiusRatio = None, bgColor = (0.8, 0.8, 0.8), fgColor = (0.5, 0.5, 0.5, 1.0)): + DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX) + self.hide() + self._bgColor = bgColor + self._fgColor = fgColor + self._mazeCollTable = mazeCollTable + self._mazeWidth = len(self._mazeCollTable[0]) + self._mazeHeight = len(self._mazeCollTable) + self._maskResolution = maskResolution or DEFAULT_MASK_RESOLUTION + if radiusRatio is None: + self._radius = self._maskResolution * DEFAULT_RADIUS_RATIO + else: + self._radius = self._maskResolution * radiusRatio + self._revealedCells = [] + for y in xrange(self._mazeHeight): + self._revealedCells.append([]) + for u in xrange(self._mazeWidth): + self._revealedCells[y].append(False) + + self._revealFunctions = {MazeRevealType.SmoothCircle: self._revealSmoothCircle, + MazeRevealType.HardCircle: self._revealHardCircle, + MazeRevealType.Square: self._revealSquare} + self._revealFunction = MAZE_REVEAL_TYPE + self.map = self._createMapTextureCard() + self.map.reparentTo(self) + self.maskedLayer = self.attachNewNode('maskedLayer') + self.mask = self._createMaskTextureCard() + self.mask.reparentTo(self) + self.visibleLayer = self.attachNewNode('visibleLayer') + self._laffMeterModel = loader.loadModel('phase_3/models/gui/laff_o_meter') + self._toon2marker = {} + return + + def _createMapTextureCard(self): + mapImage = PNMImage(MAP_RESOLUTION, MAP_RESOLUTION) + mapImage.fill(*self._bgColor) + fgColor = VBase4F(*self._fgColor) + for x in xrange(self._mazeHeight): + for y in xrange(self._mazeWidth): + if self._mazeCollTable[y][x] == 1: + ax = float(x) / self._mazeWidth * MAP_RESOLUTION + invertedY = self._mazeHeight - 1 - y + ay = float(invertedY) / self._mazeHeight * MAP_RESOLUTION + self._drawSquare(mapImage, int(ax), int(ay), 10, fgColor) + + mapTexture = Texture('mapTexture') + mapTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba) + mapTexture.setMinfilter(Texture.FTLinear) + mapTexture.load(mapImage) + mapTexture.setWrapU(Texture.WMClamp) + mapTexture.setWrapV(Texture.WMClamp) + mapImage.clear() + del mapImage + cm = CardMaker('map_cardMaker') + cm.setFrame(-1.0, 1.0, -1.0, 1.0) + map = self.attachNewNode(cm.generate()) + map.setTexture(mapTexture, 1) + return map + + def _createMaskTextureCard(self): + self._maskImage = PNMImage(self._maskResolution, self._maskResolution, 4) + for x in xrange(self._maskResolution): + for y in xrange(self._maskResolution): + self._maskImage.setXelA(x, y, 0, 0, 0, 1) + + self.maskTexture = Texture('maskTexture') + self.maskTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba) + self.maskTexture.setMinfilter(Texture.FTLinear) + self.maskTexture.setWrapU(Texture.WMClamp) + self.maskTexture.setWrapV(Texture.WMClamp) + self.maskTexture.load(self._maskImage) + base.graphicsEngine.renderFrame() + cm = CardMaker('mask_cardMaker') + cm.setFrame(-1.1, 1.1, -1.1, 1.1) + mask = self.attachNewNode(cm.generate()) + mask.setTexture(self.maskTexture, 1) + mask.setTransparency(1) + return mask + + def _drawSquare(self, image, ulx, uly, size, color): + x = int(ulx) + while x <= ulx + size: + y = int(uly) + while y <= uly + size: + if x > 0 and y > 0 and x < image.getXSize() and y < image.getYSize(): + image.setXelA(x, y, color) + y += 1 + + x += 1 + + def destroy(self): + del self._mazeCollTable + del self._maskResolution + del self._radius + del self._revealedCells + del self._revealFunctions + del self._revealFunction + self.map.removeNode() + del self.map + self.mask.removeNode() + del self.mask + self.maskedLayer.removeNode() + del self.maskedLayer + self.visibleLayer.removeNode() + del self.visibleLayer + self._maskImage.clear() + del self._maskImage + self.maskTexture.clear() + del self.maskTexture + self._laffMeterModel.removeNode() + del self._laffMeterModel + DirectFrame.destroy(self) + + def _revealSmoothCircle(self, x, y, center): + length = (Vec2(x, y) - center).length() + goalAlpha = max(0.0, length / float(self._radius) - 0.5) + self._maskImage.setXelA(x, y, VBase4F(0.0, 0.0, 0.0, min(self._maskImage.getAlpha(x, y), goalAlpha * 2.0))) + + def _revealHardCircle(self, x, y, center): + length = (Vec2(x, y) - center).length() + if length <= self._radius: + self._maskImage.setXelA(x, y, VBase4F(0, 0, 0, 0)) + + def _revealSquare(self, x, y, center): + self._maskImage.setXelA(x, y, VBase4F(0, 0, 0, 0)) + + def _drawHole(self, x, y): + center = Vec2(x, y) + ul = center - Vec2(self._radius, self._radius) + lr = center + Vec2(self._radius, self._radius) + x = int(ul[0]) + while x <= lr[0]: + y = int(ul[1]) + while y <= lr[1]: + if x > 0 and y > 0 and x < self._maskResolution and y < self._maskResolution: + self._revealFunctions[self._revealFunction](x, y, center) + y += 1 + + x += 1 + + self.maskTexture.load(self._maskImage) + self.mask.setTexture(self.maskTexture, 1) + + def _createSimpleMarker(self, size, color = (1, 1, 1)): + halfSize = size * 0.5 + cm = CardMaker('mazemap_simple_marker') + cm.setFrame(-halfSize, halfSize, -halfSize, halfSize) + markerNP = self.maskedLayer.attachNewNode(cm.generate()) + markerNP.setColor(*color) + return markerNP + + def tile2gui(self, x, y): + y = self._mazeHeight - y + cellWidth = self._maskResolution / self._mazeWidth + cellHeight = self._maskResolution / self._mazeHeight + ax = float(x) / self._mazeWidth * self._maskResolution + ax += cellWidth + ay = float(y) / self._mazeHeight * self._maskResolution + ay += cellHeight + return (ax, ay) + + def gui2pos(self, x, y): + return (x / self._maskResolution * 2.0 - 0.97, 0, y / self._maskResolution * -2.0 + 1.02) + + def _getToonMarker(self, toon): + hType = toon.style.getType() + if hType == 'rabbit': + hType = 'bunny' + return self._laffMeterModel.find('**/' + hType + 'head') + + def addToon(self, toon, tX, tY): + marker = NodePath('toon_marker-%i' % toon.doId) + marker.reparentTo(self) + self._getToonMarker(toon).copyTo(marker) + marker.setColor(toon.style.getHeadColor()) + if toon.isLocal(): + marker.setScale(0.07) + else: + marker.setScale(0.05) + marker.flattenStrong() + marker.setPos(*self.gui2pos(*self.tile2gui(tX, tY))) + self._toon2marker[toon] = marker + + def removeToon(self, toon): + if toon not in self._toon2marker: + return + self._toon2marker[toon].removeNode() + del self._toon2marker[toon] + + def updateToon(self, toon, tX, tY): + if toon not in self._toon2marker: + return + x, y = self.tile2gui(tX, tY) + self._toon2marker[toon].setPos(*self.gui2pos(x, y)) + if tY < 0 or tY >= len(self._revealedCells): + self.notify.warning('updateToon earlying out:') + self.notify.warning('(tX, tY): (%s, %s)' % (tX, tY)) + self.notify.warning('len(_revealedCells): %s' % (len(self._revealedCells),)) + if len(self._revealedCells) > 0: + self.notify.warning('len(_revealedCells[0]): %s' % (len(self._revealedCells[0]),)) + return + if tX < 0 or tX >= len(self._revealedCells[tY]): + self.notify.warning('updateToon earlying out:') + self.notify.warning('(tX, tY): (%s, %s)' % (tX, tY)) + self.notify.warning('len(_revealedCells): %s' % (len(self._revealedCells),)) + if tY < len(self._revealedCells): + self.notify.warning('len(_revealedCells[tY]): %s' % (len(self._revealedCells[tY]),)) + elif len(self._revealedCells) > 0: + self.notify.warning('len(_revealedCells[0]): %s' % (len(self._revealedCells[0]),)) + return + if not self._revealedCells[tY][tX]: + self._drawHole(x, y) + self._revealedCells[tY][tX] = True + + def revealCell(self, x, y): + ax, ay = self.tile2gui(x, y) + if not self._revealedCells[y][x]: + self._drawHole(ax, ay) + self._revealedCells[y][x] = True + + def revealAll(self): + for x in xrange(self._maskResolution): + for y in xrange(self._maskResolution): + self._maskImage.setXelA(x, y, 0, 0, 0, 0) + + self.revealCell(0, 0) + + def reset(self): + for x in xrange(self._maskResolution): + for y in xrange(self._maskResolution): + self._maskImage.setXelA(x, y, 0, 0, 0, 1) diff --git a/toontown/minigame/MazeSuit.py b/toontown/minigame/MazeSuit.py new file mode 100755 index 00000000..0e00e712 --- /dev/null +++ b/toontown/minigame/MazeSuit.py @@ -0,0 +1,262 @@ +from direct.showbase.DirectObject import DirectObject +from direct.interval.MetaInterval import Parallel +from direct.interval.LerpInterval import LerpPosInterval, LerpHprInterval +from direct.showbase.RandomNumGen import RandomNumGen +from pandac.PandaModules import Point3, WaitInterval +from pandac.PandaModules import CollisionSphere, CollisionNode +from toontown.suit import Suit +from toontown.suit import SuitDNA +from toontown.toonbase import ToontownGlobals +import MazeGameGlobals + +class MazeSuit(DirectObject): + COLL_SPHERE_NAME = 'MazeSuitSphere' + COLLISION_EVENT_NAME = 'MazeSuitCollision' + MOVE_IVAL_NAME = 'moveMazeSuit' + DIR_UP = 0 + DIR_DOWN = 1 + DIR_LEFT = 2 + DIR_RIGHT = 3 + oppositeDirections = [DIR_DOWN, + DIR_UP, + DIR_RIGHT, + DIR_LEFT] + directionHs = [0, + 180, + 90, + 270] + DEFAULT_SPEED = 4.0 + SUIT_Z = 0.1 + + def __init__(self, serialNum, maze, randomNumGen, cellWalkPeriod, difficulty, suitDnaName = 'f', startTile = None, ticFreq = MazeGameGlobals.SUIT_TIC_FREQ, walkSameDirectionProb = MazeGameGlobals.WALK_SAME_DIRECTION_PROB, walkTurnAroundProb = MazeGameGlobals.WALK_TURN_AROUND_PROB, uniqueRandomNumGen = True, walkAnimName = None): + self.serialNum = serialNum + self.maze = maze + if uniqueRandomNumGen: + self.rng = RandomNumGen(randomNumGen) + else: + self.rng = randomNumGen + self.difficulty = difficulty + self._walkSameDirectionProb = walkSameDirectionProb + self._walkTurnAroundProb = walkTurnAroundProb + self._walkAnimName = walkAnimName or 'walk' + self.suit = Suit.Suit() + d = SuitDNA.SuitDNA() + d.newSuit(suitDnaName) + self.suit.setDNA(d) + self.suit.nametag3d.stash() + self.suit.nametag.destroy() + if startTile is None: + defaultStartPos = MazeGameGlobals.SUIT_START_POSITIONS[self.serialNum] + self.startTile = (defaultStartPos[0] * self.maze.width, defaultStartPos[1] * self.maze.height) + else: + self.startTile = startTile + self.ticFreq = ticFreq + self.ticPeriod = int(cellWalkPeriod) + self.cellWalkDuration = float(self.ticPeriod) / float(self.ticFreq) + self.turnDuration = 0.6 * self.cellWalkDuration + return + + def destroy(self): + self.suit.delete() + + def uniqueName(self, str): + return str + `(self.serialNum)` + + def gameStart(self, gameStartTime): + self.gameStartTime = gameStartTime + self.initCollisions() + self.startWalkAnim() + self.occupiedTiles = [(self.nextTX, self.nextTY)] + n = 20 + self.nextThinkTic = self.serialNum * self.ticFreq / n + self.fromPos = Point3(0, 0, 0) + self.toPos = Point3(0, 0, 0) + self.fromHpr = Point3(0, 0, 0) + self.toHpr = Point3(0, 0, 0) + self.moveIval = WaitInterval(1.0) + + def gameEnd(self): + self.moveIval.pause() + del self.moveIval + self.shutdownCollisions() + self.suit.loop('neutral') + + def initCollisions(self): + self.collSphere = CollisionSphere(0, 0, 0, 2.0) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.uniqueName(self.COLL_SPHERE_NAME)) + self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.suit.attachNewNode(self.collNode) + self.collNodePath.hide() + self.accept(self.uniqueName('enter' + self.COLL_SPHERE_NAME), self.handleEnterSphere) + + def shutdownCollisions(self): + self.ignore(self.uniqueName('enter' + self.COLL_SPHERE_NAME)) + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + + def handleEnterSphere(self, collEntry): + messenger.send(self.COLLISION_EVENT_NAME, [self.serialNum]) + + def __getWorldPos(self, sTX, sTY): + wx, wy = self.maze.tile2world(sTX, sTY) + return Point3(wx, wy, self.SUIT_Z) + + def onstage(self): + sTX = int(self.startTile[0]) + sTY = int(self.startTile[1]) + c = 0 + lim = 0 + toggle = 0 + direction = 0 + while not self.maze.isWalkable(sTX, sTY): + if 0 == direction: + sTX -= 1 + elif 1 == direction: + sTY -= 1 + elif 2 == direction: + sTX += 1 + elif 3 == direction: + sTY += 1 + c += 1 + if c > lim: + c = 0 + direction = (direction + 1) % 4 + toggle += 1 + if not toggle & 1: + lim += 1 + + self.TX = sTX + self.TY = sTY + self.direction = self.DIR_DOWN + self.lastDirection = self.direction + self.nextTX = self.TX + self.nextTY = self.TY + self.suit.setPos(self.__getWorldPos(self.TX, self.TY)) + self.suit.setHpr(self.directionHs[self.direction], 0, 0) + self.suit.reparentTo(render) + self.suit.pose(self._walkAnimName, 0) + self.suit.loop('neutral') + + def offstage(self): + self.suit.reparentTo(hidden) + + def startWalkAnim(self): + self.suit.loop(self._walkAnimName) + speed = float(self.maze.cellWidth) / self.cellWalkDuration + self.suit.setPlayRate(speed / self.DEFAULT_SPEED, self._walkAnimName) + + def __applyDirection(self, dir, TX, TY): + if self.DIR_UP == dir: + TY += 1 + elif self.DIR_DOWN == dir: + TY -= 1 + elif self.DIR_LEFT == dir: + TX -= 1 + elif self.DIR_RIGHT == dir: + TX += 1 + return (TX, TY) + + def __chooseNewWalkDirection(self, unwalkables): + if not self.rng.randrange(self._walkSameDirectionProb): + newTX, newTY = self.__applyDirection(self.direction, self.TX, self.TY) + if self.maze.isWalkable(newTX, newTY, unwalkables): + return self.direction + if self.difficulty >= 0.5: + if not self.rng.randrange(self._walkTurnAroundProb): + oppositeDir = self.oppositeDirections[self.direction] + newTX, newTY = self.__applyDirection(oppositeDir, self.TX, self.TY) + if self.maze.isWalkable(newTX, newTY, unwalkables): + return oppositeDir + candidateDirs = [self.DIR_UP, + self.DIR_DOWN, + self.DIR_LEFT, + self.DIR_RIGHT] + candidateDirs.remove(self.oppositeDirections[self.direction]) + while len(candidateDirs): + dir = self.rng.choice(candidateDirs) + newTX, newTY = self.__applyDirection(dir, self.TX, self.TY) + if self.maze.isWalkable(newTX, newTY, unwalkables): + return dir + candidateDirs.remove(dir) + + return self.oppositeDirections[self.direction] + + def getThinkTimestampTics(self, curTic): + if curTic < self.nextThinkTic: + return [] + else: + r = range(self.nextThinkTic, curTic + 1, self.ticPeriod) + self.lastTicBeforeRender = r[-1] + return r + + def prepareToThink(self): + self.occupiedTiles = [(self.nextTX, self.nextTY)] + + def think(self, curTic, curT, unwalkables): + self.TX = self.nextTX + self.TY = self.nextTY + self.lastDirection = self.direction + self.direction = self.__chooseNewWalkDirection(unwalkables) + self.nextTX, self.nextTY = self.__applyDirection(self.direction, self.TX, self.TY) + self.occupiedTiles = [(self.TX, self.TY), (self.nextTX, self.nextTY)] + if curTic == self.lastTicBeforeRender: + fromCoords = self.maze.tile2world(self.TX, self.TY) + toCoords = self.maze.tile2world(self.nextTX, self.nextTY) + self.fromPos.set(fromCoords[0], fromCoords[1], self.SUIT_Z) + self.toPos.set(toCoords[0], toCoords[1], self.SUIT_Z) + self.moveIval = LerpPosInterval(self.suit, self.cellWalkDuration, self.toPos, startPos=self.fromPos, name=self.uniqueName(self.MOVE_IVAL_NAME)) + if self.direction != self.lastDirection: + self.fromH = self.directionHs[self.lastDirection] + toH = self.directionHs[self.direction] + if self.fromH == 270 and toH == 0: + self.fromH = -90 + elif self.fromH == 0 and toH == 270: + self.fromH = 360 + self.fromHpr.set(self.fromH, 0, 0) + self.toHpr.set(toH, 0, 0) + turnIval = LerpHprInterval(self.suit, self.turnDuration, self.toHpr, startHpr=self.fromHpr, name=self.uniqueName('turnMazeSuit')) + self.moveIval = Parallel(self.moveIval, turnIval, name=self.uniqueName(self.MOVE_IVAL_NAME)) + else: + self.suit.setH(self.directionHs[self.direction]) + moveStartT = float(self.nextThinkTic) / float(self.ticFreq) + self.moveIval.start(curT - (moveStartT + self.gameStartTime)) + self.nextThinkTic += self.ticPeriod + + @staticmethod + def thinkSuits(suitList, startTime, ticFreq = MazeGameGlobals.SUIT_TIC_FREQ): + curT = globalClock.getFrameTime() - startTime + curTic = int(curT * float(ticFreq)) + suitUpdates = [] + for i in xrange(len(suitList)): + updateTics = suitList[i].getThinkTimestampTics(curTic) + suitUpdates.extend(zip(updateTics, [i] * len(updateTics))) + + suitUpdates.sort(lambda a, b: a[0] - b[0]) + if len(suitUpdates) > 0: + curTic = 0 + for i in xrange(len(suitUpdates)): + update = suitUpdates[i] + tic = update[0] + suitIndex = update[1] + suit = suitList[suitIndex] + if tic > curTic: + curTic = tic + j = i + 1 + while j < len(suitUpdates): + if suitUpdates[j][0] > tic: + break + suitList[suitUpdates[j][1]].prepareToThink() + j += 1 + + unwalkables = [] + for si in xrange(suitIndex): + unwalkables.extend(suitList[si].occupiedTiles) + + for si in xrange(suitIndex + 1, len(suitList)): + unwalkables.extend(suitList[si].occupiedTiles) + + suit.think(curTic, curT, unwalkables) diff --git a/toontown/minigame/MazeTreasure.py b/toontown/minigame/MazeTreasure.py new file mode 100755 index 00000000..8d8aa990 --- /dev/null +++ b/toontown/minigame/MazeTreasure.py @@ -0,0 +1,38 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal + +class MazeTreasure(DirectObject): + RADIUS = 0.7 + + def __init__(self, model, pos, serialNum, gameId): + self.serialNum = serialNum + self.nodePath = model.copyTo(render) + self.nodePath.setPos(pos[0], pos[1], 1.0) + self.sphereName = 'treasureSphere%s-%s' % (gameId, self.serialNum) + self.collSphere = CollisionSphere(0, 0, 0, self.RADIUS) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.sphereName) + self.collNode.setIntoCollideMask(WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.nodePath.attachNewNode(self.collNode) + self.collNodePath.hide() + self.accept('enter' + self.sphereName, self.__handleEnterSphere) + self.nodePath.flattenLight() + + def destroy(self): + self.ignoreAll() + self.nodePath.removeNode() + del self.nodePath + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + + def __handleEnterSphere(self, collEntry): + self.ignoreAll() + messenger.send('MazeTreasureGrabbed', [self.serialNum]) + + def showGrab(self): + self.nodePath.reparentTo(hidden) + self.collNode.setIntoCollideMask(BitMask32(0)) diff --git a/toontown/minigame/MinigameAvatarScorePanel.py b/toontown/minigame/MinigameAvatarScorePanel.py new file mode 100755 index 00000000..3cbc0c70 --- /dev/null +++ b/toontown/minigame/MinigameAvatarScorePanel.py @@ -0,0 +1,45 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from toontown.toon import LaffMeter + +class MinigameAvatarScorePanel(DirectFrame): + + def __init__(self, avId, avName): + self.avId = avId + if self.avId in base.cr.doId2do: + self.avatar = base.cr.doId2do[self.avId] + else: + self.avatar = None + DirectFrame.__init__(self, relief=None, image_color=GlobalDialogColor, image_scale=(0.4, 1.0, 0.24), image_pos=(0.0, 0.1, 0.0)) + self['image'] = DGG.getDefaultDialogGeom() + self.scoreText = DirectLabel(self, relief=None, text='0', text_scale=TTLocalizer.MASPscoreText, pos=(0.1, 0.0, -0.09)) + if self.avatar: + self.laffMeter = LaffMeter.LaffMeter(self.avatar.style, self.avatar.hp, self.avatar.maxHp) + self.laffMeter.reparentTo(self) + self.laffMeter.setPos(-0.085, 0, -0.035) + self.laffMeter.setScale(0.05) + self.laffMeter.start() + else: + self.laffMeter = None + self.nameText = DirectLabel(self, relief=None, text=avName, text_scale=TTLocalizer.MASPnameText, text_pos=(0.0, 0.06), text_wordwrap=7.5, text_shadow=(1, 1, 1, 1)) + self.show() + return + + def cleanup(self): + if self.laffMeter: + self.laffMeter.destroy() + del self.laffMeter + del self.scoreText + del self.nameText + self.destroy() + + def setScore(self, score): + self.scoreText['text'] = str(score) + + def getScore(self): + return int(self.scoreText['text']) + + def makeTransparent(self, alpha): + self.setTransparency(1) + self.setColorScale(1, 1, 1, alpha) diff --git a/toontown/minigame/MinigameCreatorAI.py b/toontown/minigame/MinigameCreatorAI.py new file mode 100755 index 00000000..154f98a5 --- /dev/null +++ b/toontown/minigame/MinigameCreatorAI.py @@ -0,0 +1,171 @@ +import copy +import random +import time + +import DistributedMinigameAI +import DistributedCannonGameAI +import DistributedCatchGameAI +import DistributedCogThiefGameAI +import DistributedDivingGameAI +import DistributedIceGameAI +import DistributedMazeGameAI +import DistributedMinigameTemplateAI +import DistributedPatternGameAI +import DistributedRaceGameAI +import DistributedRingGameAI +import DistributedTagGameAI +import DistributedTargetGameAI +import DistributedTugOfWarGameAI +import DistributedTwoDGameAI +import DistributedVineGameAI +from otp.ai.MagicWordGlobal import * +from toontown.toonbase import ToontownGlobals +from toontown.uberdog import TopToonsGlobals + +simbase.forcedMinigameId = simbase.config.GetInt('force-minigame', 0) +RequestMinigame = {} +MinigameZoneRefs = {} + +def createMinigame(air, playerArray, trolleyZone, minigameZone=None, + previousGameId=ToontownGlobals.NoPreviousGameId, newbieIds=[]): + if minigameZone is None: + minigameZone = air.allocateZone() + acquireMinigameZone(minigameZone) + mgId = None + mgDiff = None + mgSzId = None + for avId in playerArray: + request = RequestMinigame.get(avId) + if request is not None: + mgId, mgKeep, mgDiff, mgSzId = request + if not mgKeep: + del RequestMinigame[avId] + break + if mgId is not None: + pass + elif simbase.forcedMinigameId: + mgId = simbase.forcedMinigameId + else: + randomList = list(copy.copy(ToontownGlobals.MinigamePlayerMatrix[len(playerArray)])) + if len(playerArray) > 1: + randomList = list(copy.copy(ToontownGlobals.MinigameIDs)) + if previousGameId != ToontownGlobals.NoPreviousGameId: + if randomList.count(previousGameId) != 0 and len(randomList) > 1: + randomList.remove(previousGameId) + mgId = random.choice(randomList) + mgCtors = { + ToontownGlobals.RaceGameId: DistributedRaceGameAI.DistributedRaceGameAI, + ToontownGlobals.CannonGameId: DistributedCannonGameAI.DistributedCannonGameAI, + ToontownGlobals.TagGameId: DistributedTagGameAI.DistributedTagGameAI, + ToontownGlobals.PatternGameId: DistributedPatternGameAI.DistributedPatternGameAI, + ToontownGlobals.RingGameId: DistributedRingGameAI.DistributedRingGameAI, + ToontownGlobals.MazeGameId: DistributedMazeGameAI.DistributedMazeGameAI, + ToontownGlobals.TugOfWarGameId: DistributedTugOfWarGameAI.DistributedTugOfWarGameAI, + ToontownGlobals.CatchGameId: DistributedCatchGameAI.DistributedCatchGameAI, + ToontownGlobals.DivingGameId: DistributedDivingGameAI.DistributedDivingGameAI, + ToontownGlobals.TargetGameId: DistributedTargetGameAI.DistributedTargetGameAI, + ToontownGlobals.MinigameTemplateId: DistributedMinigameTemplateAI.DistributedMinigameTemplateAI, + ToontownGlobals.VineGameId: DistributedVineGameAI.DistributedVineGameAI, + ToontownGlobals.IceGameId: DistributedIceGameAI.DistributedIceGameAI, + ToontownGlobals.CogThiefGameId: DistributedCogThiefGameAI.DistributedCogThiefGameAI, + ToontownGlobals.TwoDGameId: DistributedTwoDGameAI.DistributedTwoDGameAI + } + try: + mg = mgCtors[mgId](air, mgId) + except KeyError: + raise Exception, 'unknown minigame ID: %s' % mgId + mg.setExpectedAvatars(playerArray) + mg.setNewbieIds(newbieIds) + mg.setTrolleyZone(trolleyZone) + mg.setDifficultyOverrides(mgDiff, mgSzId) + mg.generateWithRequired(minigameZone) + toons = [] + for doId in playerArray: + toon = simbase.air.doId2do.get(doId) + if toon is not None: + toons.append(toon) + for toon in toons: + messenger.send('topToonsManager-event', [toon.doId, TopToonsGlobals.CAT_TROLLEY, 1]) + for toon in toons: + simbase.air.questManager.toonPlayedMinigame(toon, toons) + retVal = {} + retVal['minigameZone'] = minigameZone + retVal['minigameId'] = mgId + return retVal + + +def acquireMinigameZone(zoneId): + if zoneId not in MinigameZoneRefs: + MinigameZoneRefs[zoneId] = 0 + MinigameZoneRefs[zoneId] += 1 + + +def releaseMinigameZone(zoneId): + MinigameZoneRefs[zoneId] -= 1 + if MinigameZoneRefs[zoneId] <= 0: + del MinigameZoneRefs[zoneId] + simbase.air.deallocateZone(zoneId) + + +@magicWord(category=CATEGORY_PROGRAMMER, types=[str, str]) +def minigame(command, arg0=None): + """ + A command set for Trolley minigames. + """ + command = command.lower() + invoker = spellbook.getInvoker() + if (arg0 is None) and (command not in ('remove', 'abort')): + return '~minigame %s takes exactly 1 argument (0 given)' % command + elif arg0 and (command in ('remove', 'abort')): + return '~minigame %s takes no arguments (1 given)' % command + if command == 'request': + for name in ToontownGlobals.MinigameNames: + if arg0.lower() != name: + continue + name = ToontownGlobals.MinigameNames[name] + RequestMinigame[invoker.doId] = (name, False, None, None) + return 'Stored your request for minigame: ' + arg0 + return "Couldn't store your request for minigame: " + arg0 + if command == 'force': + for name in ToontownGlobals.MinigameNames: + if arg0.lower() != name: + break + name = ToontownGlobals.MinigameNames[name] + RequestMinigame[invoker.doId] = (name, True, None, None) + return 'Stored your force request for minigame: ' + arg0 + return "Couldn't store your force request for minigame: " + arg0 + if command == 'remove': + if invoker.doId in RequestMinigame: + del RequestMinigame[invoker.doId] + return 'Your minigame request has been removed.' + return 'You have no minigame requests!' + if command == 'difficulty': + if invoker.doId not in RequestMinigame: + return 'You have no minigame requests!' + try: + arg0 = int(arg0) + except: + return 'Argument 0 must be of type: ' + str(int) + request = RequestMinigame[invoker.doId] + RequestMinigame[invoker.doId] = request[:2] + (arg0,) + request[3:] + return 'Stored your request for the minigame difficulty: ' + str(arg0) + if command == 'safezone': + if invoker.doId not in RequestMinigame: + return 'You have no minigame requests!' + try: + arg0 = int(arg0) + except: + return 'Argument 0 must be of type: ' + str(int) + request = RequestMinigame[invoker.doId] + RequestMinigame[invoker.doId] = request[:3] + (arg0,) + request[4:] + return 'Stored your request for the minigame safezone: ' + str(arg0) + if command == 'abort': + for do in simbase.air.doId2do.values(): + if not isinstance(do, DistributedMinigameAI.DistributedMinigameAI): + continue + if invoker.doId not in do.avIdList: + continue + do.setGameAbort() + return 'Skipped minigame!' + return 'You are not currently in a minigame!' + return 'Invalid command.' diff --git a/toontown/minigame/MinigameGlobals.py b/toontown/minigame/MinigameGlobals.py new file mode 100755 index 00000000..8a0c14b6 --- /dev/null +++ b/toontown/minigame/MinigameGlobals.py @@ -0,0 +1,36 @@ +from direct.showbase import PythonUtil +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil +from random import choice +latencyTolerance = 10.0 +MaxLoadTime = 40.0 +rulesDuration = 21 +JellybeanTrolleyHolidayScoreMultiplier = 2 +DifficultyOverrideMult = int(1 << 16) + +def QuantizeDifficultyOverride(diffOverride): + return int(round(diffOverride * DifficultyOverrideMult)) / float(DifficultyOverrideMult) + + +NoDifficultyOverride = 2147483647 +NoTrolleyZoneOverride = -1 +SafeZones = [ToontownGlobals.ToontownCentral, + ToontownGlobals.DonaldsDock, + ToontownGlobals.DaisyGardens, + ToontownGlobals.MinniesMelodyland, + ToontownGlobals.TheBrrrgh, + ToontownGlobals.DonaldsDreamland] + +def getDifficulty(trolleyZone): + hoodZone = getSafezoneId(trolleyZone) + return float(SafeZones.index(hoodZone)) / (len(SafeZones) - 1) + + +def getSafezoneId(trolleyZone): + return ZoneUtil.getCanonicalHoodId(trolleyZone) + + +def getScoreMult(trolleyZone): + szId = getSafezoneId(trolleyZone) + multiplier = PythonUtil.lerp(1.0, 1.5, float(SafeZones.index(szId)) / (len(SafeZones) - 1)) + return multiplier diff --git a/toontown/minigame/MinigamePhysicsWorldBase.py b/toontown/minigame/MinigamePhysicsWorldBase.py new file mode 100755 index 00000000..1655c9ff --- /dev/null +++ b/toontown/minigame/MinigamePhysicsWorldBase.py @@ -0,0 +1,166 @@ +from pandac.PandaModules import Quat +from pandac.PandaModules import OdeWorld, OdeSimpleSpace, OdeJointGroup, OdeUtil +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import globalClockDelta + +class MinigamePhysicsWorldBase: + notify = DirectNotifyGlobal.directNotify.newCategory('MinigamePhysicsWorldBase') + + def __init__(self, canRender = 0): + self.canRender = canRender + self.world = OdeWorld() + self.space = OdeSimpleSpace() + self.contactgroup = OdeJointGroup() + self.bodyList = [] + self.geomList = [] + self.massList = [] + self.rayList = [] + self.showContacts = 0 + self.jointMarkers = [] + self.jointMarkerCount = 64 + self.meshDataList = [] + self.geomDataList = [] + self.commonObjectInfoDict = {} + self.maxColCount = 0 + if self.canRender: + self.odePandaRelationList = self.bodyList + self.root = render.attachNewNode('physics root node') + else: + self.root = NodePath('physics root node') + self.placerNode = self.root.attachNewNode('Placer') + self.subPlacerNode = self.placerNode.attachNewNode('Placer Sub Node') + self.commonObjectDict = {} + self.commonId = 0 + self.worldAttach = self.root.attachNewNode('physics geom attach point') + self.timingCycleLength = 10.0 + self.timingCycleOffset = 0.0 + self.timingSimTime = 0.0 + self.FPS = 60.0 + self.DTAStep = 1.0 / self.FPS + self.DTA = 0 + self.useQuickStep = False + self.deterministic = True + self.numStepsInSimulateTask = 0 + + def delete(self): + self.notify.debug('Max Collision Count was %s' % self.maxColCount) + self.stopSim() + self.commonObjectDict = None + if self.canRender: + for pair in self.odePandaRelationList: + pair[0].removeNode() + pair[1].destroy() + + self.odePandaRelationList = None + else: + for body in self.bodyList: + body[1].destroy() + + self.bodyList = None + for mass in self.massList: + mass = None + + for geom in self.geomList: + geom.destroy() + geom = None + + for ray in self.rayList: + ray.destroy() + ray = None + + self.placerNode.removeNode() + self.root.removeNode() + for marker in self.jointMarkers: + marker.removeNode() + + self.jointMarkers = None + for data in self.geomDataList: + data.destroy() + + for data in self.meshDataList: + data.destroy() + + self.contactgroup.empty() + self.world.destroy() + self.space.destroy() + self.world = None + self.space = None + return + + def setupSimulation(self): + if self.canRender: + for count in xrange(self.jointMarkerCount): + testMarker = render.attachNewNode('Joint Marker') + ballmodel = loader.loadModel('phase_3/models/misc/sphere') + ballmodel.reparentTo(testMarker) + ballmodel.setScale(0.1) + testMarker.setPos(0.0, 0.0, -100.0) + self.jointMarkers.append(testMarker) + + def startSim(self): + taskMgr.add(self.__simulationTask, 'simulation task') + + def stopSim(self): + taskMgr.remove('simulation task') + + def __simulationTask(self, task): + self.DTA += globalClock.getDt() + numSteps = int(self.DTA / self.DTAStep) + if numSteps > 10: + self.notify.warning('phyics steps = %d' % numSteps) + startTime = globalClock.getRealTime() + while self.DTA >= self.DTAStep: + if self.deterministic: + OdeUtil.randSetSeed(0) + self.DTA -= self.DTAStep + self.preStep() + self.simulate() + self.postStep() + + if self.canRender: + self.placeBodies() + return task.cont + + def preStep(self): + pass + + def postStep(self): + if self.showContacts and self.canRender: + for count in xrange(self.jointMarkerCount): + pandaNodePathGeom = self.jointMarkers[count] + if count < self.colCount: + pandaNodePathGeom.setPos(self.space.getContactData(count * 3 + 0), self.space.getContactData(count * 3 + 1), self.space.getContactData(count * 3 + 2)) + else: + pandaNodePathGeom.setPos(0.0, 0.0, -100.0) + + def simulate(self): + self.colCount = self.space.autoCollide() + if self.maxColCount < self.colCount: + self.maxColCount = self.colCount + self.notify.debug('New Max Collision Count %s' % self.maxColCount) + if self.useQuickStep: + self.world.quickStep(self.DTAStep) + else: + self.world.step(self.DTAStep) + for bodyPair in self.bodyList: + self.world.applyDampening(self.DTAStep, bodyPair[1]) + + self.contactgroup.empty() + self.timingSimTime = self.timingSimTime + self.DTAStep + + def placeBodies(self): + for pair in self.odePandaRelationList: + pandaNodePathGeom = pair[0] + odeBody = pair[1] + if pandaNodePathGeom: + pandaNodePathGeom.setPos(odeBody.getPosition()) + pandaNodePathGeom.setQuat(Quat(odeBody.getQuaternion()[0], odeBody.getQuaternion()[1], odeBody.getQuaternion()[2], odeBody.getQuaternion()[3])) + + def getOrderedContacts(self, count): + c0 = self.space.getContactId(count, 0) + c1 = self.space.getContactId(count, 1) + if c0 > c1: + chold = c1 + c1 = c0 + c0 = chold + return (c0, c1) diff --git a/toontown/minigame/MinigamePowerMeter.py b/toontown/minigame/MinigamePowerMeter.py new file mode 100755 index 00000000..03be9980 --- /dev/null +++ b/toontown/minigame/MinigamePowerMeter.py @@ -0,0 +1,81 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer + +class MinigamePowerMeter(DirectFrame): + + def __init__(self, size, label = None): + DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, image_color=GlobalDialogColor, image_scale=(0.48, 1.0, 0.7), image_pos=(0.0, 0.1, 0.0), sortOrder=DGG.BACKGROUND_SORT_INDEX) + self['image'] = DGG.getDefaultDialogGeom() + self.resetFrameSize() + if label == None: + label = TTLocalizer.MinigamePowerMeterLabel + self.powerText = DirectLabel(self, relief=None, text=label, text_scale=TTLocalizer.MPMpowerText, pos=(0.01, 0.0, 0.29)) + self.tooSlow = DirectLabel(parent=self, relief=None, text=TTLocalizer.MinigamePowerMeterTooSlow, scale=TTLocalizer.MPMtooSlow, pos=(-.15, 0, 0.05), color=(0.1, 0.3, 0.6, 1.0)) + self.tooFast = DirectLabel(parent=self, relief=None, text=TTLocalizer.MinigamePowerMeterTooFast, scale=TTLocalizer.MPMtooFast, pos=(0.15, 0, 0.05), color=(0.1, 0.3, 0.6, 1.0)) + self.tooSlow.hide() + self.tooFast.hide() + self.largeGauge = [] + self.gaugeSize = size + self.__createSpeedGauge() + self.show() + return + + def cleanup(self): + del self.powerText + for gauge in self.largeGauge: + if gauge: + del gauge + + del self.largeGauge + self.destroy() + + def __createSpeedGauge(self): + gaugeA = DirectWaitBar(parent=self, relief=DGG.RAISED, range=self.gaugeSize, frameSize=(-0.6, + 0.6, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=TTLocalizer.MPMgaugeA, pos=(0, 0, 0), frameColor=(0.0, 0.0, 0.0, 0.0), barColor=(0, 1, 0, 0.6), sortOrder=DGG.FOREGROUND_SORT_INDEX) + gaugeA.setR(-90) + gaugeA['value'] = 0 + self.largeGauge.append(gaugeA) + gaugeTargetTop = DirectWaitBar(parent=self, relief=DGG.RAISED, range=self.gaugeSize, frameSize=(-0.6, + 0.6, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=TTLocalizer.MPMgaugeTargetTop, pos=(0, 0, 0), frameColor=(1, 1, 1, 1), barColor=(1, 0, 0, 1), sortOrder=DGG.BACKGROUND_SORT_INDEX + 1) + gaugeTargetTop.setR(-90) + gaugeTargetTop['value'] = 1 + self.largeGauge.append(gaugeTargetTop) + gaugeTargetBot = DirectWaitBar(parent=self, relief=DGG.RAISED, range=self.gaugeSize, frameSize=(-0.6, + 0.6, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=TTLocalizer.MPMgaugeTargetBot, pos=(0, 0, 0), frameColor=(1, 1, 1, 0), barColor=(1, 1, 1, 1), sortOrder=DGG.BACKGROUND_SORT_INDEX + 2) + gaugeTargetBot['value'] = 0 + gaugeTargetBot.setR(-90) + self.largeGauge.append(gaugeTargetBot) + for gauge in self.largeGauge: + gauge.show() + + def setPower(self, power): + self.largeGauge[0]['value'] = power + + def setTarget(self, target): + self.largeGauge[2]['value'] = target + self.largeGauge[1]['value'] = target + 1 + + def clearTooSlowTooFast(self): + self.tooSlow.hide() + self.tooFast.hide() + + def updateTooSlowTooFast(self): + curSpeed = self.largeGauge[0]['value'] + target = self.largeGauge[2]['value'] + self.tooSlow.hide() + self.tooFast.hide() + if curSpeed < target - 2: + self.tooSlow.show() + elif curSpeed > target + 2: + self.tooFast.show() + + def setBarColor(self, color): + self.largeGauge[0]['barColor'] = color diff --git a/toontown/minigame/MinigameRulesPanel.py b/toontown/minigame/MinigameRulesPanel.py new file mode 100755 index 00000000..9dba1f24 --- /dev/null +++ b/toontown/minigame/MinigameRulesPanel.py @@ -0,0 +1,69 @@ +from direct.task import Task +from direct.fsm import StateData +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import ToontownTimer +from toontown.toonbase import TTLocalizer +import MinigameGlobals + +class MinigameRulesPanel(StateData.StateData): + + def __init__(self, panelName, gameTitle, instructions, doneEvent, timeout = MinigameGlobals.rulesDuration, playerCount = 0): + StateData.StateData.__init__(self, doneEvent) + self.gameTitle = gameTitle + self.instructions = instructions + self.TIMEOUT = timeout + self.playerCount = playerCount + + def load(self): + minigameGui = loader.loadModel('phase_4/models/gui/minigame_rules_gui') + buttonGui = loader.loadModel('phase_3.5/models/gui/inventory_gui') + buttonImage = (buttonGui.find('**/InventoryButtonUp'), buttonGui.find('**/InventoryButtonDown'), buttonGui.find('**/InventoryButtonRollover')) + self.frame = DirectFrame(image=minigameGui.find('**/minigame-rules-panel'), relief=None, pos=(0.1375, 0, -0.6667)) + self.gameTitleText = DirectLabel(parent=self.frame, text=self.gameTitle, scale=TTLocalizer.MRPgameTitleText, text_align=TextNode.ACenter, text_font=getSignFont(), text_fg=(1.0, 0.33, 0.33, 1.0), pos=TTLocalizer.MRgameTitleTextPos, relief=None) + self.instructionsText = DirectLabel(parent=self.frame, text=self.instructions, scale=TTLocalizer.MRPinstructionsText, text_align=TextNode.ACenter, text_wordwrap=TTLocalizer.MRPinstructionsTextWordwrap, pos=TTLocalizer.MRPinstructionsTextPos, relief=None) + self.playButton = DirectButton(parent=self.frame, relief=None, image=buttonImage, image_color=Vec4(0, 0.9, 0.1, 1), text=TTLocalizer.MinigameRulesPanelPlay, text_fg=(1, 1, 1, 1), text_pos=(0, -0.02, 0), text_scale=TTLocalizer.MRPplayButton, pos=(1.0025, 0, -0.203), scale=1.05, command=self.playCallback) + if self.playerCount: + self.skipButton = DirectButton(parent=self.frame, relief=None, image=buttonImage, image_color=Vec4(0, 0.9, 0.1, 1), text=TTLocalizer.MinigameRulesPanelSkip % (0, self.playerCount), text_fg=(1, 1, 1, 1), text_pos=(0, 0.01, 0), text_scale=0.045, pos=(0, 0, 0.25), scale=1.05, command=self.skipCallback) + self.acceptOnce('disableMinigameSkip', self.skipButton.hide) + self.accept('gameSkipCountChange', self.updateSkipButton) + minigameGui.removeNode() + buttonGui.removeNode() + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(self.frame) + self.timer.setScale(0.4) + self.timer.setPos(0.997, 0, 0.064) + self.frame.hide() + + def unload(self): + self.ignoreAll() + if hasattr(self, 'skipButton'): + self.skipButton.destroy() + del self.skipButton + self.frame.destroy() + del self.frame + del self.gameTitleText + del self.instructionsText + self.playButton.destroy() + del self.playButton + del self.timer + + def enter(self): + self.frame.show() + self.timer.countdown(self.TIMEOUT, self.playCallback) + self.accept('enter', self.playCallback) + + def exit(self): + self.frame.hide() + self.timer.stop() + self.ignore('enter') + + def playCallback(self): + messenger.send(self.doneEvent) + + def skipCallback(self): + messenger.send('minigameSkip') + + def updateSkipButton(self, min, max): + self.skipButton['text'] = TTLocalizer.MinigameRulesPanelSkip % (min, max) diff --git a/toontown/minigame/OrthoDrive.py b/toontown/minigame/OrthoDrive.py new file mode 100755 index 00000000..3a269132 --- /dev/null +++ b/toontown/minigame/OrthoDrive.py @@ -0,0 +1,136 @@ +from direct.interval.IntervalGlobal import * +from direct.task.Task import Task +from otp.otpbase import OTPGlobals +from toontown.toonbase.ToonBaseGlobal import * +import ArrowKeys + +class OrthoDrive: + notify = DirectNotifyGlobal.directNotify.newCategory('OrthoDrive') + TASK_NAME = 'OrthoDriveTask' + SET_ATREST_HEADING_TASK = 'setAtRestHeadingTask' + + def __init__(self, speed, maxFrameMove = None, customCollisionCallback = None, priority = 0, setHeading = 1, upHeading = 0, instantTurn = False, wantSound = False): + self.wantSound = wantSound + self.speed = speed + self.maxFrameMove = maxFrameMove + self.customCollisionCallback = customCollisionCallback + self.priority = priority + self.setHeading = setHeading + self.upHeading = upHeading + self.arrowKeys = ArrowKeys.ArrowKeys() + self.lt = base.localAvatar + self.instantTurn = instantTurn + + def destroy(self): + self.arrowKeys.destroy() + del self.arrowKeys + del self.customCollisionCallback + + def start(self): + self.notify.debug('start') + self.__placeToonHOG(self.lt.getPos()) + taskMgr.add(self.__update, OrthoDrive.TASK_NAME, priority=self.priority) + self.lastAction = None + return + + def __placeToonHOG(self, pos, h = None): + if h == None: + h = self.lt.getH() + self.lt.setPos(pos) + self.lt.setH(h) + self.lastPos = pos + self.atRestHeading = h + self.lastXVel = 0 + self.lastYVel = 0 + return + + def stop(self): + self.notify.debug('stop') + self.lt.stopSound() + taskMgr.remove(OrthoDrive.TASK_NAME) + taskMgr.remove(OrthoDrive.SET_ATREST_HEADING_TASK) + if hasattr(self, 'turnLocalToonIval'): + if self.turnLocalToonIval.isPlaying(): + self.turnLocalToonIval.pause() + del self.turnLocalToonIval + base.localAvatar.setSpeed(0, 0) + + def __update(self, task): + vel = Vec3(0, 0, 0) + xVel = 0 + yVel = 0 + if self.arrowKeys.upPressed(): + yVel += 1 + if self.arrowKeys.downPressed(): + yVel -= 1 + if self.arrowKeys.leftPressed(): + xVel -= 1 + if self.arrowKeys.rightPressed(): + xVel += 1 + vel.setX(xVel) + vel.setY(yVel) + vel.normalize() + vel *= self.speed + speed = vel.length() + action = self.lt.setSpeed(speed, 0) + if action != self.lastAction: + self.lastAction = action + if self.wantSound: + if action == OTPGlobals.WALK_INDEX or action == OTPGlobals.REVERSE_INDEX: + self.lt.walkSound() + elif action == OTPGlobals.RUN_INDEX: + self.lt.runSound() + else: + self.lt.stopSound() + if self.setHeading: + self.__handleHeading(xVel, yVel) + toonPos = self.lt.getPos() + dt = globalClock.getDt() + posOffset = vel * dt + posOffset += toonPos - self.lastPos + toonPos = self.lastPos + if self.maxFrameMove: + posOffsetLen = posOffset.length() + if posOffsetLen > self.maxFrameMove: + posOffset *= self.maxFrameMove + posOffset /= posOffsetLen + if self.customCollisionCallback: + toonPos = self.customCollisionCallback(toonPos, toonPos + posOffset) + else: + toonPos = toonPos + posOffset + self.lt.setPos(toonPos) + self.lastPos = toonPos + return Task.cont + + def __handleHeading(self, xVel, yVel): + def getHeading(xVel, yVel): + angTab = [[None, 0, 180], [-90, -45, -135], [90, 45, 135]] + return angTab[xVel][yVel] + self.upHeading + + def orientToon(angle, self = self): + startAngle = self.lt.getH() + startAngle = fitSrcAngle2Dest(startAngle, angle) + dur = 0.1 * abs(startAngle - angle) / 90 + self.turnLocalToonIval = LerpHprInterval(self.lt, dur, Point3(angle, 0, 0), startHpr=Point3(startAngle, 0, 0), name='OrthoDriveLerpHpr') + if self.instantTurn: + self.turnLocalToonIval.finish() + else: + self.turnLocalToonIval.start() + + if xVel != self.lastXVel or yVel != self.lastYVel: + taskMgr.remove(OrthoDrive.SET_ATREST_HEADING_TASK) + if not (xVel or yVel): + orientToon(self.atRestHeading) + else: + curHeading = getHeading(xVel, yVel) + if ((self.lastXVel and self.lastYVel) and not (xVel and yVel)): + def setAtRestHeading(task, self = self, angle = curHeading): + self.atRestHeading = angle + return Task.done + + taskMgr.doMethodLater(0.05, setAtRestHeading, OrthoDrive.SET_ATREST_HEADING_TASK) + else: + self.atRestHeading = curHeading + orientToon(curHeading) + self.lastXVel = xVel + self.lastYVel = yVel diff --git a/toontown/minigame/OrthoWalk.py b/toontown/minigame/OrthoWalk.py new file mode 100755 index 00000000..1ac10f06 --- /dev/null +++ b/toontown/minigame/OrthoWalk.py @@ -0,0 +1,76 @@ +from toontown.toonbase.ToonBaseGlobal import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from OrthoDrive import * +from direct.directnotify import DirectNotifyGlobal + +class OrthoWalk: + notify = DirectNotifyGlobal.directNotify.newCategory('OrthoWalk') + BROADCAST_POS_TASK = 'OrthoWalkBroadcastPos' + + def __init__(self, orthoDrive, collisions = 1, broadcast = 1, broadcastPeriod = 0.2): + self.orthoDrive = orthoDrive + self.collisions = collisions + self.broadcast = broadcast + self.broadcastPeriod = broadcastPeriod + self.priority = self.orthoDrive.priority + 1 + self.lt = base.localAvatar + + def destroy(self): + self.orthoDrive.destroy() + del self.orthoDrive + + def start(self): + self.notify.debug('OrthoWalk start') + if self.collisions: + self.initCollisions() + if self.broadcast: + self.initBroadcast() + self.orthoDrive.start() + + def stop(self): + self.notify.debug('OrthoWalk stop') + self.shutdownCollisions() + self.shutdownBroadcast() + self.orthoDrive.stop() + + def initCollisions(self): + self.notify.debug('OrthoWalk initCollisions') + lt = base.localAvatar + lt.collisionsOn() + self.__collisionsOn = 1 + + def shutdownCollisions(self): + if not hasattr(self, '_OrthoWalk__collisionsOn'): + return + del self.__collisionsOn + self.notify.debug('OrthoWalk shutdownCollisions') + lt = base.localAvatar + lt.collisionsOff() + + def initBroadcast(self): + self.notify.debug('OrthoWalk initBroadcast') + self.timeSinceLastPosBroadcast = 0.0 + self.lastPosBroadcast = self.lt.getPos() + self.lastHprBroadcast = self.lt.getHpr() + self.storeStop = 0 + lt = self.lt + lt.d_clearSmoothing() + lt.sendCurrentPosition() + taskMgr.remove(self.BROADCAST_POS_TASK) + taskMgr.add(self.doBroadcast, self.BROADCAST_POS_TASK, priority=self.priority) + + def shutdownBroadcast(self): + self.notify.debug('OrthoWalk shutdownBroadcast') + taskMgr.remove(self.BROADCAST_POS_TASK) + + def doBroadcast(self, task): + dt = globalClock.getDt() + self.timeSinceLastPosBroadcast += dt + if self.timeSinceLastPosBroadcast >= self.broadcastPeriod: + self.sendCurrentPosition() + return Task.cont + + def sendCurrentPosition(self): + self.timeSinceLastPosBroadcast -= self.broadcastPeriod + self.lt.cnode.broadcastPosHprXyh() diff --git a/toontown/minigame/PatternGameGlobals.py b/toontown/minigame/PatternGameGlobals.py new file mode 100755 index 00000000..a330d580 --- /dev/null +++ b/toontown/minigame/PatternGameGlobals.py @@ -0,0 +1,9 @@ +import MinigameGlobals + +INITIAL_ROUND_LENGTH = 2 +ROUND_LENGTH_INCREMENT = 2 +NUM_ROUNDS = 4 +InputTime = 10 +TOONTOWN_WORK = 1 +ClientsReadyTimeout = 5 + MinigameGlobals.latencyTolerance +InputTimeout = InputTime + MinigameGlobals.latencyTolerance diff --git a/toontown/minigame/Purchase.py b/toontown/minigame/Purchase.py new file mode 100755 index 00000000..7d3f0211 --- /dev/null +++ b/toontown/minigame/Purchase.py @@ -0,0 +1,690 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.gui import DirectGuiGlobals as DGG +from direct.showbase.PythonUtil import Functor +from direct.task.Task import Task + +import MinigameGlobals +from PurchaseBase import * +from toontown.distributed import DelayDelete +from otp.nametag.NametagFloat2d import * +from otp.nametag import NametagGlobals +from toontown.toon import ToonHead +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownTimer + + +COUNT_UP_RATE = 0.15 +COUNT_UP_DURATION = 0.5 +DELAY_BEFORE_COUNT_UP = 1.0 +DELAY_AFTER_COUNT_UP = 1.0 +COUNT_DOWN_RATE = 0.075 +COUNT_DOWN_DURATION = 0.5 +DELAY_AFTER_COUNT_DOWN = 0.0 +DELAY_AFTER_CELEBRATE = 2.6 +COUNT_SFX_MIN_DELAY = 0.034 +COUNT_SFX_START_T = 0.079 +OVERMAX_SFX_MIN_DELAY = 0.067 +OVERMAX_SFX_START_T = 0.021 + +class Purchase(PurchaseBase): + notify = DirectNotifyGlobal.directNotify.newCategory('Purchase') + + def __init__(self, toon, pointsArray, playerMoney, ids, states, remain, doneEvent): + PurchaseBase.__init__(self, toon, doneEvent) + self.ids = ids + self.pointsArray = pointsArray + self.playerMoney = playerMoney + self.states = states + self.remain = remain + self.tutorialMode = 0 + self.fsm.addState(State.State('reward', self.enterReward, self.exitReward, ['purchase'])) + doneState = self.fsm.getStateNamed('done') + doneState.addTransition('reward') + self.unexpectedEventNames = [] + self.unexpectedExits = [] + self.setupUnexpectedExitHooks() + + def load(self): + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + PurchaseBase.load(self, purchaseModels) + interiorPhase = 3.5 + self.bg = loader.loadModel('phase_%s/models/modules/toon_interior' % interiorPhase) + self.bg.setPos(0.0, 5.0, -1.0) + self.wt = self.bg.find('**/random_tc1_TI_wallpaper') + wallTex = loader.loadTexture('phase_%s/maps/wall_paper_a5.jpg' % interiorPhase) + self.wt.setTexture(wallTex, 100) + self.wt.setColorScale(0.8, 0.67, 0.549, 1.0) + self.bt = self.bg.find('**/random_tc1_TI_wallpaper_border') + wallTex = loader.loadTexture('phase_%s/maps/wall_paper_a5.jpg' % interiorPhase) + self.bt.setTexture(wallTex, 100) + self.bt.setColorScale(0.8, 0.67, 0.549, 1.0) + self.wb = self.bg.find('**/random_tc1_TI_wainscotting') + wainTex = loader.loadTexture('phase_%s/maps/wall_paper_b4.jpg' % interiorPhase) + self.wb.setTexture(wainTex, 100) + self.wb.setColorScale(0.473, 0.675, 0.488, 1.0) + self.playAgain = DirectButton(parent=self.frame, relief=None, scale=1.04, pos=(0.72, 0, -0.24), image=(purchaseModels.find('**/PurchScrn_BTN_UP'), + purchaseModels.find('**/PurchScrn_BTN_DN'), + purchaseModels.find('**/PurchScrn_BTN_RLVR'), + purchaseModels.find('**/PurchScrn_BTN_UP')), text=TTLocalizer.GagShopPlayAgain, text_fg=(0, 0.1, 0.7, 1), text_scale=0.05, text_pos=(0, 0.015, 0), image3_color=Vec4(0.6, 0.6, 0.6, 1), text3_fg=Vec4(0, 0, 0.4, 1), command=self.__handlePlayAgain) + self.backToPlayground = DirectButton(parent=self.frame, relief=None, scale=1.04, pos=(0.72, 0, -0.045), image=(purchaseModels.find('**/PurchScrn_BTN_UP'), + purchaseModels.find('**/PurchScrn_BTN_DN'), + purchaseModels.find('**/PurchScrn_BTN_RLVR'), + purchaseModels.find('**/PurchScrn_BTN_UP')), text=TTLocalizer.GagShopBackToPlayground, text_fg=(0, 0.1, 0.7, 1), text_scale=0.05, text_pos=(0, 0.015, 0), image3_color=Vec4(0.6, 0.6, 0.6, 1), text3_fg=Vec4(0, 0, 0.4, 1), command=self.__handleBackToPlayground) + self.timer = ToontownTimer.ToontownTimer() + self.timer.hide() + self.timer.posInTopRightCorner() + numAvs = 0 + count = 0 + localToonIndex = 0 + for index in xrange(len(self.ids)): + avId = self.ids[index] + if avId == base.localAvatar.doId: + localToonIndex = index + if self.states[index] != PURCHASE_NO_CLIENT_STATE and self.states[index] != PURCHASE_DISCONNECTED_STATE: + numAvs = numAvs + 1 + + layoutList = (None, + (0,), + (0, 2), + (0, 1, 3), + (0, 1, 2, 3)) + layout = layoutList[numAvs] + headFramePosList = (Vec3(0.105, 0, -0.384), + Vec3(0.105, 0, -0.776), + Vec3(0.85, 0, -0.555), + Vec3(-0.654, 0, -0.555)) + AVID_INDEX = 0 + LAYOUT_INDEX = 1 + TOON_INDEX = 2 + self.avInfoArray = [(base.localAvatar.doId, headFramePosList[0], localToonIndex)] + pos = 1 + for index in xrange(len(self.ids)): + avId = self.ids[index] + if self.states[index] != PURCHASE_NO_CLIENT_STATE and self.states[index] != PURCHASE_DISCONNECTED_STATE: + if avId != base.localAvatar.doId: + if avId in base.cr.doId2do: + self.avInfoArray.append((avId, headFramePosList[layout[pos]], index)) + pos = pos + 1 + + self.headFrames = [] + for avInfo in self.avInfoArray: + av = base.cr.doId2do.get(avInfo[AVID_INDEX]) + if av: + headFrame = PurchaseHeadFrame(av, purchaseModels) + headFrame.setAvatarState(self.states[avInfo[TOON_INDEX]]) + headFrame.setPos(avInfo[LAYOUT_INDEX]) + self.headFrames.append((avInfo[AVID_INDEX], headFrame)) + + purchaseModels.removeNode() + self.foreground = loader.loadModel('phase_3.5/models/modules/TT_A1') + self.foreground.setPos(12.5, -20, -5.5) + self.foreground.setHpr(180, 0, 0) + self.backgroundL = self.foreground.copyTo(hidden) + self.backgroundL.setPos(-14.5, -25, -5) + self.backgroundL.setHpr(180, 0, 0) + self.backgroundR = self.backgroundL.copyTo(hidden) + self.backgroundR.setPos(30, -25, -5) + self.backgroundR.setHpr(180, 0, 0) + streets = loader.loadModel('phase_3.5/models/modules/street_modules') + sidewalk = streets.find('**/street_sidewalk_40x40') + self.sidewalk = sidewalk.copyTo(hidden) + self.sidewalkR = sidewalk.copyTo(hidden) + self.sidewalkL = sidewalk.copyTo(hidden) + self.sidewalk.setPos(-20, -25, -5.5) + self.sidewalk.setColor(0.9, 0.6, 0.4) + self.sidewalkL.setPos(-40, -25, -5.5) + self.sidewalkL.setColor(0.9, 0.6, 0.4) + self.sidewalkR.setPos(0, -25, -5.5) + self.sidewalkR.setColor(0.9, 0.6, 0.4) + streets.removeNode() + doors = loader.loadModel('phase_4/models/modules/doors') + door = doors.find('**/door_single_square_ur_door') + self.door = door.copyTo(hidden) + self.door.setH(180) + self.door.setPos(0, -16.75, -5.5) + self.door.setScale(1.5, 1.5, 2.0) + self.door.setColor(1.0, 0.8, 0, 1) + doors.removeNode() + self.rewardDoubledJellybeanLabel = DirectLabel(text=TTLocalizer.PartyRewardDoubledJellybean, text_fg=(1.0, 0.125, 0.125, 1.0), text_shadow=(0, 0, 0, 1), relief=None, pos=(0.0, 0, -0.67), scale=0.08) + self.rewardDoubledJellybeanLabel.hide() + self.countSound = base.loadSfx('phase_3.5/audio/sfx/tick_counter.ogg') + self.overMaxSound = base.loadSfx('phase_3.5/audio/sfx/AV_collision.ogg') + self.celebrateSound = base.loadSfx('phase_4/audio/sfx/MG_win.ogg') + return + + def unload(self): + PurchaseBase.unload(self) + self.cleanupUnexpectedExitHooks() + self.bg.removeNode() + del self.bg + self.notify.debug('destroying head frames') + for headFrame in self.headFrames: + if not headFrame[1].isEmpty(): + headFrame[1].reparentTo(hidden) + headFrame[1].destroy() + + del self.headFrames + self.playAgain.destroy() + del self.playAgain + self.backToPlayground.destroy() + del self.backToPlayground + self.timer.stop() + self.timer.destroy() + del self.timer + for counter in self.counters: + counter.destroy() + del counter + + del self.counters + for total in self.totalCounters: + total.destroy() + del total + + del self.totalCounters + loader.unloadModel('phase_3.5/models/modules/TT_A1') + loader.unloadModel('phase_3.5/models/modules/street_modules') + loader.unloadModel('phase_4/models/modules/doors') + taskMgr.remove('countUpTask') + taskMgr.remove('countDownTask') + taskMgr.remove('celebrate') + taskMgr.remove('purchase-trans') + taskMgr.remove('delayAdd') + taskMgr.remove('delaySubtract') + self.foreground.removeNode() + del self.foreground + self.backgroundL.removeNode() + del self.backgroundL + self.backgroundR.removeNode() + del self.backgroundR + self.sidewalk.removeNode() + self.sidewalkL.removeNode() + self.sidewalkR.removeNode() + del self.sidewalk + del self.sidewalkL + del self.sidewalkR + self.door.removeNode() + del self.door + self.collisionFloor.removeNode() + del self.collisionFloor + del self.countSound + del self.celebrateSound + self.rewardDoubledJellybeanLabel.removeNode() + del self.rewardDoubledJellybeanLabel + + def showStatusText(self, text): + self.statusLabel['text'] = text + taskMgr.remove('resetStatusText') + taskMgr.doMethodLater(2.0, self.resetStatusText, 'resetStatusText') + + def resetStatusText(self, task): + self.statusLabel['text'] = '' + return Task.done + + def __handlePlayAgain(self): + for headFrame in self.headFrames: + headFrame[1].wrtReparentTo(aspect2d) + + self.toon.inventory.reparentTo(hidden) + self.toon.inventory.hide() + taskMgr.remove('resetStatusText') + taskMgr.remove('showBrokeMsgTask') + self.statusLabel['text'] = TTLocalizer.WaitingForOtherToons + messenger.send('purchasePlayAgain') + + def handleDone(self, playAgain): + base.localAvatar.b_setParent(ToontownGlobals.SPHidden) + if playAgain: + self.doneStatus = {'loader': 'minigame', + 'where': 'minigame'} + else: + self.doneStatus = {'loader': 'safeZoneLoader', + 'where': 'playground'} + messenger.send(self.doneEvent) + + def __handleBackToPlayground(self): + self.toon.inventory.reparentTo(hidden) + self.toon.inventory.hide() + messenger.send('purchaseBackToToontown') + + def __timerExpired(self): + messenger.send('purchaseTimeout') + + def findHeadFrame(self, id): + for headFrame in self.headFrames: + if headFrame[0] == id: + return headFrame[1] + + return None + + def __handleStateChange(self, playerStates): + self.states = playerStates + for avInfo in self.avInfoArray: + index = avInfo[2] + headFrame = self.findHeadFrame(avInfo[0]) + state = self.states[index] + headFrame.setAvatarState(state) + + def enter(self): + base.playMusic(self.music, looping=1, volume=0.8) + self.fsm.request('reward') + + def enterReward(self): + numToons = 0 + toonLayouts = ((2,), + (1, 3), + (0, 2, 4), + (0, 1, 3, 4)) + toonPositions = (5.0, + 1.75, + -0.25, + -1.75, + -5.0) + self.toons = [] + self.toonsKeep = [] + self.counters = [] + self.totalCounters = [] + camera.reparentTo(render) + camera.setPos(0, 16.0, 2.0) + camera.lookAt(0, 0, 0.75) + base.transitions.irisIn(0.4) + base.camLens.setMinFov(60/(4./3.)) + base.setBackgroundColor(Vec4(0, 0.6, 1, 1)) + self.title.reparentTo(aspect2d) + self.foreground.reparentTo(render) + self.backgroundL.reparentTo(render) + self.backgroundR.reparentTo(render) + self.sidewalk.reparentTo(render) + self.sidewalkL.reparentTo(render) + self.sidewalkR.reparentTo(render) + self.door.reparentTo(render) + size = 20 + z = -2.5 + floor = CollisionPolygon(Point3(-size, -size, z), Point3(size, -size, z), Point3(size, size, z), Point3(-size, size, z)) + floor.setTangible(1) + floorNode = CollisionNode('collision_floor') + floorNode.addSolid(floor) + self.collisionFloor = render.attachNewNode(floorNode) + NametagGlobals.setOnscreenChatForced(1) + for index in xrange(len(self.ids)): + avId = self.ids[index] + if self.states[index] != PURCHASE_NO_CLIENT_STATE and self.states[index] != PURCHASE_DISCONNECTED_STATE and avId in base.cr.doId2do: + numToons += 1 + toon = base.cr.doId2do[avId] + toon.stopSmooth() + self.toons.append(toon) + self.toonsKeep.append(DelayDelete.DelayDelete(toon, 'Purchase.enterReward')) + counter = DirectLabel(parent=hidden, relief=None, pos=(0.0, 0.0, 0.0), text=str(0), text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_pos=(0, -0.1, 0), text_font=ToontownGlobals.getSignFont()) + counter['image'] = DGG.getDefaultDialogGeom() + counter['image_scale'] = (0.33, 1, 0.33) + counter.setScale(0.5) + counter.count = 0 + counter.max = self.pointsArray[index] + self.counters.append(counter) + money = self.playerMoney[index] + totalCounter = DirectLabel(parent=hidden, relief=None, pos=(0.0, 0.0, 0.0), text=str(money), text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_pos=(0, -0.1, 0), text_font=ToontownGlobals.getSignFont(), image=self.jarImage) + totalCounter.setScale(0.5) + totalCounter.count = money + totalCounter.max = toon.getMaxMoney() + self.totalCounters.append(totalCounter) + + self.accept('clientCleanup', self._handleClientCleanup) + pos = 0 + toonLayout = toonLayouts[numToons - 1] + for toon in self.toons: + thisPos = toonPositions[toonLayout[pos]] + toon.setPos(Vec3(thisPos, 1.0, -2.5)) + toon.setHpr(Vec3(0, 0, 0)) + toon.setAnimState('neutral', 1) + toon.setShadowHeight(0) + if not toon.isDisabled(): + toon.reparentTo(render) + self.counters[pos].setPos(thisPos * -0.17, 0, toon.getHeight() / 10 + 0.25) + self.counters[pos].reparentTo(aspect2d) + self.totalCounters[pos].setPos(thisPos * -0.17, 0, -0.825) + self.totalCounters[pos].reparentTo(aspect2d) + pos += 1 + + self.maxPoints = max(self.pointsArray) + + def reqCountUp(state): + self.countUp() + return Task.done + + countUpDelay = DELAY_BEFORE_COUNT_UP + taskMgr.doMethodLater(countUpDelay, reqCountUp, 'countUpTask') + + def reqCountDown(state): + self.countDown() + return Task.done + + countDownDelay = countUpDelay + COUNT_UP_DURATION + DELAY_AFTER_COUNT_UP + taskMgr.doMethodLater(countDownDelay, reqCountDown, 'countDownTask') + + def celebrate(task): + for counter in task.counters: + counter.hide() + + winningPoints = max(task.pointsArray) + for i in xrange(len(task.ids)): + if task.pointsArray[i] == winningPoints: + avId = task.ids[i] + if avId in base.cr.doId2do: + toon = base.cr.doId2do[avId] + toon.setAnimState('jump', 1.0) + + base.playSfx(task.celebrateSound) + return Task.done + + celebrateDelay = countDownDelay + COUNT_DOWN_DURATION + DELAY_AFTER_COUNT_DOWN + celebrateTask = taskMgr.doMethodLater(celebrateDelay, celebrate, 'celebrate') + celebrateTask.counters = self.counters + celebrateTask.pointsArray = self.pointsArray + celebrateTask.ids = self.ids + celebrateTask.celebrateSound = self.celebrateSound + + def reqPurchase(state): + self.fsm.request('purchase') + return Task.done + + purchaseDelay = celebrateDelay + DELAY_AFTER_CELEBRATE + taskMgr.doMethodLater(purchaseDelay, reqPurchase, 'purchase-trans') + if base.skipMinigameReward: + self.fsm.request('purchase') + return + + def _changeCounterUp(self, task, counter, newCount, toonId): + counter.count = newCount + counter['text'] = str(counter.count) + if toonId == base.localAvatar.doId: + now = globalClock.getRealTime() + if task.lastSfxT + COUNT_SFX_MIN_DELAY < now: + base.playSfx(task.countSound, time=COUNT_SFX_START_T) + task.lastSfxT = now + + def _countUpTask(self, task): + now = globalClock.getRealTime() + startT = task.getStartTime() + if now >= startT + task.duration: + for counter, toonId in zip(self.counters, self.ids): + if counter.count != counter.max: + self._changeCounterUp(task, counter, counter.max, toonId) + + return Task.done + t = (now - startT) / task.duration + for counter, toonId in zip(self.counters, self.ids): + curCount = int(t * counter.max) + if curCount != counter.count: + self._changeCounterUp(task, counter, curCount, toonId) + + return Task.cont + + def countUp(self): + totalDelay = 0 + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH): + self.rewardDoubledJellybeanLabel.show() + countUpTask = taskMgr.add(self._countUpTask, 'countUp') + countUpTask.duration = COUNT_UP_DURATION + countUpTask.countSound = self.countSound + countUpTask.lastSfxT = 0 + + def _changeCounterDown(self, task, counter, newCount, total, toonId): + counter.count = newCount + counter['text'] = str(counter.count) + total.count = total.startAmount + (counter.max - newCount) + if total.count > total.max: + total.count = total.max + total['text'] = str(total.count) + if total.count == total.max: + total['text_fg'] = (1, 0, 0, 1) + if toonId == base.localAvatar.doId: + now = globalClock.getRealTime() + if total.count < total.max: + minDelay = COUNT_SFX_MIN_DELAY + snd = task.countSound + startT = COUNT_SFX_START_T + else: + minDelay = OVERMAX_SFX_MIN_DELAY + snd = task.overMaxSound + startT = OVERMAX_SFX_START_T + if task.lastSfxT + minDelay < now: + task.lastSfxT = now + base.playSfx(snd, time=startT) + + def _countDownTask(self, task): + now = globalClock.getRealTime() + startT = task.getStartTime() + if now >= startT + task.duration: + for counter, total, toonId in zip(self.counters, self.totalCounters, self.ids): + if counter.count != 0: + self._changeCounterDown(task, counter, 0, total, toonId) + + return Task.done + t = (now - startT) / task.duration + for counter, total, toonId in zip(self.counters, self.totalCounters, self.ids): + curCount = int(counter.max * (1 - t)) + if curCount != counter.count: + self._changeCounterDown(task, counter, curCount, total, toonId) + + return Task.cont + + def countDown(self): + totalDelay = 0 + for total in self.totalCounters: + total.startAmount = total.count + + countDownTask = taskMgr.add(self._countDownTask, 'countDown') + countDownTask.duration = COUNT_DOWN_DURATION + countDownTask.countSound = self.countSound + countDownTask.overMaxSound = self.overMaxSound + countDownTask.lastSfxT = 0 + + def exitReward(self): + self.ignore('clientCleanup') + taskMgr.remove('countUpTask') + taskMgr.remove('countDownTask') + taskMgr.remove('celebrate') + taskMgr.remove('purchase-trans') + taskMgr.remove('delayAdd') + taskMgr.remove('delaySubtract') + for toon in self.toons: + toon.detachNode() + + del self.toons + if hasattr(self, 'toonsKeep'): + for delayDelete in self.toonsKeep: + delayDelete.destroy() + + del self.toonsKeep + for counter in self.counters: + counter.reparentTo(hidden) + + for total in self.totalCounters: + total.reparentTo(hidden) + + self.foreground.reparentTo(hidden) + self.backgroundL.reparentTo(hidden) + self.backgroundR.reparentTo(hidden) + self.sidewalk.reparentTo(hidden) + self.sidewalkL.reparentTo(hidden) + self.sidewalkR.reparentTo(hidden) + self.door.reparentTo(hidden) + self.title.reparentTo(self.frame) + self.rewardDoubledJellybeanLabel.hide() + base.camLens.setMinFov(settings['fov']/(4./3.)) + NametagGlobals.setOnscreenChatForced(0) + + def _handleClientCleanup(self): + if hasattr(self, 'toonsKeep'): + for delayDelete in self.toonsKeep: + delayDelete.destroy() + + del self.toonsKeep + self.ignore('clientCleanup') + + def enterPurchase(self): + PurchaseBase.enterPurchase(self) + self.rewardDoubledJellybeanLabel.hide() + self.bg.reparentTo(render) + base.setBackgroundColor(0.78, 0.65, 0.53) + self.accept('purchaseStateChange', self.__handleStateChange) + self.playAgain.reparentTo(self.toon.inventory.purchaseFrame) + self.backToPlayground.reparentTo(self.toon.inventory.purchaseFrame) + self.pointDisplay.reparentTo(self.toon.inventory.purchaseFrame) + self.statusLabel.reparentTo(self.toon.inventory.purchaseFrame) + for headFrame in self.headFrames: + headFrame[1].show() + headFrame[1].reparentTo(self.toon.inventory.purchaseFrame) + + if not self.tutorialMode: + if not config.GetBool('disable-purchase-timer', 0): + self.timer.show() + self.timer.countdown(self.remain, self.__timerExpired) + else: + self.timer.hide() + self.disablePlayAgain() + self.accept('disableGagPanel', Functor(self.toon.inventory.setActivateMode, 'gagTutDisabled', gagTutMode=1)) + self.accept('disableBackToPlayground', self.disableBackToPlayground) + self.accept('enableGagPanel', self.handleEnableGagPanel) + self.accept('enableBackToPlayground', self.enableBackToPlayground) + for avId, headFrame in self.headFrames: + if avId != self.newbieId: + headFrame.hide() + + messenger.send('gagScreenIsUp') + if base.autoPlayAgain: + base.transitions.fadeOut(0) + self.__handlePlayAgain() + + def exitPurchase(self): + PurchaseBase.exitPurchase(self) + self.ignore('disableGagPanel') + self.ignore('disableBackToPlayground') + self.ignore('enableGagPanel') + self.ignore('enableBackToPlayground') + self.bg.reparentTo(hidden) + self.playAgain.reparentTo(self.frame) + self.backToPlayground.reparentTo(self.frame) + self.pointDisplay.reparentTo(self.frame) + self.statusLabel.reparentTo(self.frame) + self.ignore('purchaseStateChange') + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + if base.autoPlayAgain: + base.transitions.fadeIn() + + def disableBackToPlayground(self): + self.backToPlayground['state'] = DGG.DISABLED + + def enableBackToPlayground(self): + self.backToPlayground['state'] = DGG.NORMAL + + def disablePlayAgain(self): + self.playAgain['state'] = DGG.DISABLED + + def enablePlayAgain(self): + self.playAgain['state'] = DGG.NORMAL + + def enterTutorialMode(self, newbieId): + self.tutorialMode = 1 + self.newbieId = newbieId + + def handleEnableGagPanel(self): + self.toon.inventory.setActivateMode('purchase', gagTutMode=1) + self.checkForBroke() + + def handleGagTutorialDone(self): + self.enableBackToPlayground() + + def setupUnexpectedExitHooks(self): + for avId in self.ids: + if avId in base.cr.doId2do: + toon = base.cr.doId2do[avId] + eventName = toon.uniqueName('disable') + self.accept(eventName, self.__handleUnexpectedExit, extraArgs=[avId]) + self.unexpectedEventNames.append(eventName) + + def cleanupUnexpectedExitHooks(self): + for eventName in self.unexpectedEventNames: + self.ignore(eventName) + + def __handleUnexpectedExit(self, avId): + self.unexpectedExits.append(avId) + + +class PurchaseHeadFrame(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('Purchase') + + def __init__(self, av, purchaseModels): + DirectFrame.__init__(self, relief=None, image=purchaseModels.find('**/Char_Pnl')) + self.initialiseoptions(PurchaseHeadFrame) + self.statusLabel = DirectLabel(parent=self, relief=None, text='', text_scale=TTLocalizer.PstatusLabel, text_wordwrap=7.5, text_fg=(0.05, 0.14, 0.4, 1), text_pos=(0.1, 0, 0)) + self.av = av + self.avKeep = DelayDelete.DelayDelete(av, 'PurchaseHeadFrame.av') + self.accept('clientCleanup', self._handleClientCleanup) + self.head = self.stateNodePath[0].attachNewNode('head', 20) + self.head.setPosHprScale(-0.22, 10.0, -0.1, 180.0, 0.0, 0.0, 0.1, 0.1, 0.1) + self.headModel = ToonHead.ToonHead() + self.headModel.setupHead(self.av.style, forGui=1) + self.headModel.reparentTo(self.head) + self.tag2Node = NametagFloat2d() + self.tag2Node.setContents(Nametag.CName) + self.av.nametag.addNametag(self.tag2Node) + self.tag2 = self.attachNewNode(self.tag2Node) + self.tag2.setPosHprScale(-0.22, 10.0, 0.12, 0, 0, 0, 0.046, 0.046, 0.046) + self.tag1Node = NametagFloat2d() + self.tag1Node.setContents(Nametag.CSpeech | Nametag.CThought) + self.av.nametag.addNametag(self.tag1Node) + self.tag1 = self.attachNewNode(self.tag1Node) + self.tag1.setPosHprScale(-0.15, 0, -0.1, 0, 0, 0, 0.046, 0.046, 0.046) + self.hide() + + def destroy(self): + DirectFrame.destroy(self) + del self.statusLabel + self.headModel.delete() + del self.headModel + self.head.removeNode() + del self.head + self.av.nametag.removeNametag(self.tag1Node) + self.av.nametag.removeNametag(self.tag2Node) + self.tag1.removeNode() + self.tag2.removeNode() + del self.tag1 + del self.tag2 + del self.tag1Node + del self.tag2Node + del self.av + self.removeAvKeep() + + def setAvatarState(self, state): + if state == PURCHASE_DISCONNECTED_STATE: + self.statusLabel['text'] = TTLocalizer.GagShopPlayerDisconnected % self.av.getName() + self.statusLabel['text_pos'] = (0.015, 0.072, 0) + self.head.hide() + self.tag1.hide() + self.tag2.hide() + elif state == PURCHASE_EXIT_STATE: + self.statusLabel['text'] = TTLocalizer.GagShopPlayerExited % self.av.getName() + self.statusLabel['text_pos'] = (0.015, 0.072, 0) + self.head.hide() + self.tag1.hide() + self.tag2.hide() + elif state == PURCHASE_PLAYAGAIN_STATE: + self.statusLabel['text'] = TTLocalizer.GagShopPlayerPlayAgain + self.statusLabel['text_pos'] = (0.1, -0.12, 0) + elif state == PURCHASE_WAITING_STATE: + self.statusLabel['text'] = TTLocalizer.GagShopPlayerBuying + self.statusLabel['text_pos'] = (0.1, -0.12, 0) + elif state == PURCHASE_NO_CLIENT_STATE: + Purchase.notify.warning("setAvatarState('no client state'); OK for gag purchase tutorial") + else: + Purchase.notify.warning('unknown avatar state: %s' % state) + + def _handleClientCleanup(self): + self.destroy() + + def removeAvKeep(self): + if hasattr(self, 'avKeep'): + self.notify.debug('destroying avKeep %s' % self.avKeep) + self.avKeep.destroy() + del self.avKeep + self.ignore('clientCleanup') diff --git a/toontown/minigame/PurchaseBase.py b/toontown/minigame/PurchaseBase.py new file mode 100755 index 00000000..a5f76d8b --- /dev/null +++ b/toontown/minigame/PurchaseBase.py @@ -0,0 +1,141 @@ +from toontown.toonbase.ToontownBattleGlobals import * +from toontown.toonbase import ToontownGlobals +from direct.fsm import StateData +from toontown.shtiker.PurchaseManagerConstants import * +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.task import Task +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.toonbase import TTLocalizer + +class PurchaseBase(StateData.StateData): + activateMode = 'purchase' + + def __init__(self, toon, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.toon = toon + self.fsm = ClassicFSM.ClassicFSM('Purchase', [State.State('purchase', self.enterPurchase, self.exitPurchase, ['done']), State.State('done', self.enterDone, self.exitDone, ['purchase'])], 'done', 'done') + self.fsm.enterInitialState() + + def load(self, purchaseModels = None): + if purchaseModels == None: + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + self.music = base.loadMusic('phase_4/audio/bgm/FF_safezone.ogg') + self.jarImage = purchaseModels.find('**/Jar') + self.jarImage.reparentTo(hidden) + self.frame = DirectFrame(relief=None) + self.frame.hide() + self.title = DirectLabel(parent=self.frame, relief=None, pos=(0.0, 0.0, 0.83), scale=1.2, image=purchaseModels.find('**/Goofys_Sign'), text=TTLocalizer.GagShopName, text_fg=(0.6, 0.2, 0, 1), text_scale=0.09, text_wordwrap=10, text_pos=(0, 0.025, 0), text_font=ToontownGlobals.getSignFont()) + self.pointDisplay = DirectLabel(parent=self.frame, relief=None, pos=(-1.15, 0.0, 0.16), text=str(self.toon.getMoney()), text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0, -0.1, 0), image=self.jarImage, text_font=ToontownGlobals.getSignFont()) + self.statusLabel = DirectLabel(parent=self.frame, relief=None, pos=(-0.25, 0, 0.625), text=TTLocalizer.GagShopYouHave % self.toon.getMoney(), text_scale=TTLocalizer.PBstatusLabel, text_fg=(0.05, 0.14, 0.4, 1)) + if self.toon.getMoney() == 1: + self.statusLabel['text'] = TTLocalizer.GagShopYouHaveOne + self.isBroke = 0 + return + + def unload(self): + self.jarImage.removeNode() + del self.jarImage + self.frame.destroy() + del self.frame + del self.title + del self.pointDisplay + del self.statusLabel + del self.music + del self.fsm + return + + def __handleSelection(self, track, level): + self.handlePurchase(track, level) + + def handlePurchase(self, track, level): + if self.toon.getMoney() <= 0: + return + ret = self.toon.inventory.addItem(track, level) + if ret == -3: + text = TTLocalizer.GagShopNotEnoughJellybeans + elif ret == -2: + text = TTLocalizer.GagShopTooManyProps + elif ret == -1: + text = TTLocalizer.GagShopTooManyOfThatGag % TTLocalizer.BattleGlobalAvPropStringsPlural[track][level] + elif ret == 0: + text = TTLocalizer.GagShopInsufficientSkill + else: + text = TTLocalizer.GagShopYouPurchased % TTLocalizer.BattleGlobalAvPropStringsSingular[track][level] + self.toon.inventory.updateGUI(track, level) + self.toon.setMoney(self.toon.getMoney() - 1) + messenger.send('boughtGag') + self.showStatusText(text) + + def showStatusText(self, text): + self.statusLabel['text'] = text + taskMgr.remove('resetStatusText') + taskMgr.doMethodLater(2.0, self.resetStatusText, 'resetStatusText') + + def resetStatusText(self, task): + self.statusLabel['text'] = '' + return Task.done + + def checkForBroke(self): + money = self.toon.getMoney() + self.pointDisplay['text'] = str(money) + if money == 0: + if not self.isBroke: + self.toon.inventory.setActivateModeBroke() + taskMgr.doMethodLater(2.25, self.showBrokeMsg, 'showBrokeMsgTask') + self.isBroke = 1 + else: + if self.isBroke: + self.toon.inventory.setActivateMode(self.activateMode) + taskMgr.remove('showBrokeMsgTask') + self.isBroke = 0 + if money == 1: + self.statusLabel['text'] = TTLocalizer.GagShopYouHaveOne + else: + self.statusLabel['text'] = TTLocalizer.GagShopYouHave % money + + def showBrokeMsg(self, task): + self.statusLabel['text'] = TTLocalizer.GagShopOutOfJellybeans + return Task.done + + def handleDone(self, playAgain): + messenger.send(self.doneEvent, [playAgain]) + + def enter(self): + self.fsm.request('purchase') + + def exit(self): + self.music.stop() + self.fsm.request('done') + + def enterPurchase(self): + self.frame.show() + self.toon.inventory.enableUberGags(0) + self.toon.inventory.show() + self.toon.inventory.reparentTo(self.frame) + self.toon.inventory.setActivateMode(self.activateMode) + self.checkForBroke() + self.acceptOnce('purchaseOver', self.handleDone) + self.accept('inventory-selection', self.__handleSelection) + self.accept(self.toon.uniqueName('moneyChange'), self.__moneyChange) + + def exitPurchase(self): + self.frame.hide() + self.toon.inventory.enableUberGags(1) + self.toon.inventory.reparentTo(hidden) + self.toon.inventory.hide() + self.ignore('purchaseOver') + self.ignore('inventory-selection') + self.ignore(self.toon.uniqueName('moneyChange')) + taskMgr.remove('resetStatusText') + taskMgr.remove('showBrokeMsgTask') + + def __moneyChange(self, money): + self.checkForBroke() + + def enterDone(self): + pass + + def exitDone(self): + pass diff --git a/toontown/minigame/RaceGameGlobals.py b/toontown/minigame/RaceGameGlobals.py new file mode 100755 index 00000000..b4ba1f8d --- /dev/null +++ b/toontown/minigame/RaceGameGlobals.py @@ -0,0 +1,38 @@ +from toontown.toonbase import TTLocalizer +ValidChoices = [0, + 1, + 2, + 3, + 4] +NumberToWin = 14 +InputTimeout = 20 +ChanceRewards = (((1, 0), TTLocalizer.RaceGameForwardOneSpace, 0), + ((1, 0), TTLocalizer.RaceGameForwardOneSpace, 0), + ((1, 0), TTLocalizer.RaceGameForwardOneSpace, 0), + ((2, 0), TTLocalizer.RaceGameForwardTwoSpaces, 0), + ((2, 0), TTLocalizer.RaceGameForwardTwoSpaces, 0), + ((2, 0), TTLocalizer.RaceGameForwardTwoSpaces, 0), + ((3, 0), TTLocalizer.RaceGameForwardThreeSpaces, 0), + ((3, 0), TTLocalizer.RaceGameForwardThreeSpaces, 0), + ((3, 0), TTLocalizer.RaceGameForwardThreeSpaces, 0), + ((0, -3), TTLocalizer.RaceGameOthersBackThree, 0), + ((0, -3), TTLocalizer.RaceGameOthersBackThree, 0), + ((-1, 0), TTLocalizer.RaceGameBackOneSpace, 0), + ((-1, 0), TTLocalizer.RaceGameBackOneSpace, 0), + ((-2, 0), TTLocalizer.RaceGameBackTwoSpaces, 0), + ((-2, 0), TTLocalizer.RaceGameBackTwoSpaces, 0), + ((-3, 0), TTLocalizer.RaceGameBackThreeSpaces, 0), + ((-3, 0), TTLocalizer.RaceGameBackThreeSpaces, 0), + ((0, 3), TTLocalizer.RaceGameOthersForwardThree, 0), + ((0, 3), TTLocalizer.RaceGameOthersForwardThree, 0), + ((0, 0), TTLocalizer.RaceGameJellybeans2, 2), + ((0, 0), TTLocalizer.RaceGameJellybeans2, 2), + ((0, 0), TTLocalizer.RaceGameJellybeans2, 2), + ((0, 0), TTLocalizer.RaceGameJellybeans2, 2), + ((0, 0), TTLocalizer.RaceGameJellybeans4, 4), + ((0, 0), TTLocalizer.RaceGameJellybeans4, 4), + ((0, 0), TTLocalizer.RaceGameJellybeans4, 4), + ((0, 0), TTLocalizer.RaceGameJellybeans4, 4), + ((0, 0), TTLocalizer.RaceGameJellybeans10, 10), + ((0, 0), -1, 0), + ((NumberToWin, 0), TTLocalizer.RaceGameInstantWinner, 0)) diff --git a/toontown/minigame/Ring.py b/toontown/minigame/Ring.py new file mode 100755 index 00000000..9713caa2 --- /dev/null +++ b/toontown/minigame/Ring.py @@ -0,0 +1,27 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from pandac.PandaModules import NodePath +import RingTrack + +class Ring(NodePath): + + def __init__(self, moveTrack, tOffset, posScale = 1.0): + NodePath.__init__(self) + self.assign(hidden.attachNewNode(base.localAvatar.uniqueName('ring'))) + self.setMoveTrack(moveTrack) + self.setTOffset(tOffset) + self.setPosScale(posScale) + self.setT(0.0) + + def setMoveTrack(self, moveTrack): + self.__moveTrack = moveTrack + + def setTOffset(self, tOffset): + self.__tOffset = float(tOffset) + + def setPosScale(self, posScale): + self.__posScale = posScale + + def setT(self, t): + pos = self.__moveTrack.eval((t + self.__tOffset) % 1.0) + self.setPos(pos[0] * self.__posScale, 0, pos[1] * self.__posScale) diff --git a/toontown/minigame/RingAction.py b/toontown/minigame/RingAction.py new file mode 100755 index 00000000..7913cc74 --- /dev/null +++ b/toontown/minigame/RingAction.py @@ -0,0 +1,42 @@ +from direct.directnotify import DirectNotifyGlobal +import RingTrack + +class RingAction: + notify = DirectNotifyGlobal.directNotify.newCategory('RingAction') + + def __init__(self): + pass + + def eval(self, t): + return (0, 0) + + +class RingActionStaticPos(RingAction): + + def __init__(self, pos): + RingAction.__init__(self) + self.__pos = pos + + def eval(self, t): + return self.__pos + + +class RingActionFunction(RingAction): + + def __init__(self, func, args): + RingAction.__init__(self) + self.__func = func + self.__args = args + + def eval(self, t): + return self.__func(t, *self.__args) + + +class RingActionRingTrack(RingAction): + + def __init__(self, ringTrack): + RingAction.__init__(self) + self.__track = ringTrack + + def eval(self, t): + return self.__track.eval(t) diff --git a/toontown/minigame/RingGameGlobals.py b/toontown/minigame/RingGameGlobals.py new file mode 100755 index 00000000..518304f7 --- /dev/null +++ b/toontown/minigame/RingGameGlobals.py @@ -0,0 +1,21 @@ +from panda3d.core import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +ENDLESS_GAME = config.GetBool('endless-ring-game', 0) +NUM_RING_GROUPS = 16 +MAX_TOONXZ = 10.0 +CollisionRadius = 1.5 +CollideMask = ToontownGlobals.CatchGameBitmask +RING_RADIUS = MAX_TOONXZ / 3.0 * 0.9 +ringColors = ((TTLocalizer.ColorRed, VBase4(1.0, 0.4, 0.2, 1.0)), + (TTLocalizer.ColorGreen, VBase4(0.0, 0.9, 0.2, 1.0)), + (TTLocalizer.ColorOrange, VBase4(1.0, 0.5, 0.25, 1.0)), + (TTLocalizer.ColorPurple, VBase4(1.0, 0.0, 1.0, 1.0)), + (TTLocalizer.ColorWhite, VBase4(1.0, 1.0, 1.0, 1.0)), + (TTLocalizer.ColorBlack, VBase4(0.0, 0.0, 0.0, 1.0)), + (TTLocalizer.ColorYellow, VBase4(1.0, 1.0, 0.2, 1.0))) +ringColorSelection = [(0, 1, 2), + 3, + 4, + 5, + 6] diff --git a/toontown/minigame/RingGroup.py b/toontown/minigame/RingGroup.py new file mode 100755 index 00000000..409a5f8e --- /dev/null +++ b/toontown/minigame/RingGroup.py @@ -0,0 +1,48 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from pandac.PandaModules import NodePath +import Ring +import RingTrack +import RingTrackGroup +import RingGameGlobals + +class RingGroup(NodePath): + + def __init__(self, trackGroup, ringModel, posScale, colorIndices): + NodePath.__init__(self) + self.assign(hidden.attachNewNode(base.localAvatar.uniqueName('ring-group'))) + self.__period = trackGroup.period + self.__reverseFlag = trackGroup.reverseFlag + self.__tOffset = trackGroup.tOffset + self.__numRings = len(trackGroup.tracks) + self.__rings = [] + self.__ringModels = [] + for i in xrange(0, self.__numRings): + track = trackGroup.tracks[i] + tOffset = trackGroup.trackTOffsets[i] + ring = Ring.Ring(track, tOffset, posScale) + ring.reparentTo(self) + model = ringModel.copyTo(ring) + model.setColor(RingGameGlobals.ringColors[colorIndices[i]][1]) + self.__rings.append(ring) + self.__ringModels.append(model) + + def delete(self): + for model in self.__ringModels: + model.removeNode() + + for ring in self.__rings: + ring.removeNode() + + del self.__rings + del self.__ringModels + + def getRing(self, index): + return self.__ringModels[index] + + def setT(self, t): + normT = (t / self.__period + self.__tOffset) % 1.0 + if self.__reverseFlag: + normT = 1.0 - normT + for ring in self.__rings: + ring.setT(normT) diff --git a/toontown/minigame/RingTrack.py b/toontown/minigame/RingTrack.py new file mode 100755 index 00000000..6836b342 --- /dev/null +++ b/toontown/minigame/RingTrack.py @@ -0,0 +1,39 @@ +from direct.directnotify import DirectNotifyGlobal +import RingAction + +class RingTrack: + notify = DirectNotifyGlobal.directNotify.newCategory('RingTrack') + + def __init__(self, actions, actionDurations = None, reverseFlag = 0): + if actionDurations == None: + actionDurations = [1.0 / float(len(actions))] * len(actions) + sum = 0.0 + for duration in actionDurations: + sum += duration + + if sum != 1.0: + self.notify.warning('action lengths do not sum to 1.; sum=' + str(sum)) + self.actions = actions + self.actionDurations = actionDurations + self.reverseFlag = reverseFlag + return + + def eval(self, t): + t = float(t) + if self.reverseFlag: + t = 1.0 - t + actionStart = 0.0 + for action, duration in zip(self.actions, self.actionDurations): + actionEnd = actionStart + duration + if t < actionEnd: + actionT = (t - actionStart) / duration + return action.eval(actionT) + else: + actionStart = actionEnd + + if t == actionStart: + self.notify.debug('time value is at end of ring track: ' + str(t) + ' == ' + str(actionStart)) + else: + self.notify.debug('time value is beyond end of ring track: ' + str(t) + ' > ' + str(actionStart)) + lastAction = self.actions[len(self.actions) - 1] + return lastAction.eval(1.0) diff --git a/toontown/minigame/RingTrackGroup.py b/toontown/minigame/RingTrackGroup.py new file mode 100755 index 00000000..a6b1dbce --- /dev/null +++ b/toontown/minigame/RingTrackGroup.py @@ -0,0 +1,13 @@ + + +class RingTrackGroup: + + def __init__(self, tracks, period, trackTOffsets = None, reverseFlag = 0, tOffset = 0.0): + if trackTOffsets == None: + trackTOffsets = [0] * len(tracks) + self.tracks = tracks + self.period = period + self.trackTOffsets = trackTOffsets + self.reverseFlag = reverseFlag + self.tOffset = tOffset + return diff --git a/toontown/minigame/RingTrackGroups.py b/toontown/minigame/RingTrackGroups.py new file mode 100755 index 00000000..04398993 --- /dev/null +++ b/toontown/minigame/RingTrackGroups.py @@ -0,0 +1,294 @@ +import math +import RingGameGlobals +import RingAction +import RingTracks +import RingTrack +import RingTrackGroup +from direct.showbase import PythonUtil +STATIC = 0 +SIMPLE = 1 +COMPLEX = 2 + +def getRandomRingTrackGroup(type, numRings, rng): + global trackListGenFuncs + funcTable = trackListGenFuncs[type][numRings - 1] + func = rng.choice(funcTable) + tracks, tOffsets, period = func(numRings, rng) + tracks, tOffsets = __scramble(tracks, tOffsets, rng) + trackGroup = RingTrackGroup.RingTrackGroup(tracks, period, trackTOffsets=tOffsets, reverseFlag=rng.choice([0, 1]), tOffset=rng.random()) + return trackGroup + + +def __scramble(tracks, tOffsets, rng): + newTracks = [] + if tOffsets == None: + newTOffsets = None + else: + newTOffsets = [] + used = [0] * len(tracks) + count = 0 + while count < len(tracks): + i = rng.randint(0, len(tracks) - 1) + if not used[i]: + used[i] = 1 + count += 1 + newTracks.append(tracks[i]) + if newTOffsets != None: + newTOffsets.append(tOffsets[i]) + + return (newTracks, newTOffsets) + + +def angleToXY(angle, radius = 1.0): + return [radius * math.sin(angle), radius * math.cos(angle)] + + +def getTightCircleStaticPositions(numRings): + positions = [] + if numRings == 1: + positions.append([0, 0]) + else: + radius = RingGameGlobals.RING_RADIUS * 1.5 / RingGameGlobals.MAX_TOONXZ + step = 2.0 * math.pi / float(numRings) + for i in xrange(0, numRings): + angle = i * step + step / 2.0 + positions.append(angleToXY(angle, 1.0 / 3.0)) + + return positions + + +def get_keypad(numRings, rng): + positions = (RingTracks.center, + RingTracks.up, + RingTracks.down, + RingTracks.left, + RingTracks.right, + RingTracks.ul, + RingTracks.ur, + RingTracks.lr, + RingTracks.ll) + tracks = [] + usedPositions = [None] + posScale = 0.7 + rng.random() * 0.2 + for i in xrange(0, numRings): + pos = None + while pos in usedPositions: + pos = rng.choice(positions) + + usedPositions.append(pos) + scaledPos = [0, 0] + scaledPos[0] = pos[0] * posScale + scaledPos[1] = pos[1] * posScale + action = RingAction.RingActionStaticPos(scaledPos) + track = RingTrack.RingTrack([action], [1.0]) + tracks.append(track) + + return (tracks, None, 1.0) + + +fullCirclePeriod = 6.0 +plusPeriod = 4.0 + +def get_evenCircle(numRings, rng): + tracks = [] + tOffsets = [] + for i in xrange(0, numRings): + actions, durations = RingTracks.getCircleRingActions() + track = RingTrack.RingTrack(actions, durations) + tracks.append(track) + tOffsets.append(float(i) / numRings) + + return (tracks, tOffsets, fullCirclePeriod) + + +def get_followCircle(numRings, rng): + tracks = [] + tOffsets = [] + for i in xrange(0, numRings): + actions, durations = RingTracks.getCircleRingActions() + track = RingTrack.RingTrack(actions, durations) + delay = 0.12 + tracks.append(track) + tOffsets.append(float(i) * delay) + + return (tracks, tOffsets, fullCirclePeriod) + + +def get_evenCircle_withStationaryCenterRings(numRings, rng): + tracks = [] + tOffsets = [] + numCenterRings = rng.randint(1, numRings - 1) + positions = getTightCircleStaticPositions(numCenterRings) + for i in xrange(0, numCenterRings): + action = RingAction.RingActionStaticPos(positions[i]) + track = RingTrack.RingTrack([action]) + tracks.append(track) + tOffsets.append(0) + + numOuterRings = numRings - numCenterRings + for i in xrange(0, numOuterRings): + actions, durations = RingTracks.getCircleRingActions() + track = RingTrack.RingTrack(actions, durations) + tracks.append(track) + tOffsets.append(float(i) / numOuterRings) + + return (tracks, tOffsets, fullCirclePeriod) + + +def __get_Slots(numRings, rng, vertical = 1): + tracks = [] + tOffsets = [] + fpTab = [] + for i in xrange(numRings): + fpTab.append(PythonUtil.lineupPos(i, numRings, 2.0 / 3)) + + offset = 1 - fpTab[-1] + offset = rng.random() * (offset * 2) - offset + fpTab = map(lambda x: x + offset, fpTab) + for i in xrange(0, numRings): + if vertical: + getActionsFunc = RingTracks.getVerticalSlotActions + else: + getActionsFunc = RingTracks.getHorizontalSlotActions + actions, durations = getActionsFunc(fpTab[i]) + track = RingTrack.RingTrack(actions, durations) + tracks.append(track) + tOffsets.append(float(i) / numRings * 0.5) + + return (tracks, tOffsets, fullCirclePeriod) + + +def get_verticalSlots(numRings, rng): + return __get_Slots(numRings, rng, vertical=1) + + +def get_horizontalSlots(numRings, rng): + return __get_Slots(numRings, rng, vertical=0) + + +def get_plus(numRings, rng): + up = RingTracks.getPlusUpRingActions + down = RingTracks.getPlusDownRingActions + left = RingTracks.getPlusLeftRingActions + right = RingTracks.getPlusRightRingActions + actionSets = {2: [[up, down], [left, right]], + 3: [[up, left, right], + [left, up, down], + [down, left, right], + [right, up, down]], + 4: [[up, + down, + left, + right]]} + tracks = [] + actionSet = rng.choice(actionSets[numRings]) + for i in xrange(0, numRings): + actions, durations = actionSet[i]() + track = RingTrack.RingTrack(actions, durations) + tracks.append(track) + + return (tracks, [0] * numRings, plusPeriod) + + +infinityPeriod = 5.0 +fullCirclePeriodFaster = 5.0 +plusPeriodFaster = 2.5 +infinityTOffsets = [] + +def __initInfinityTOffsets(): + global infinityTOffsets + offsets = [[], + [], + [], + []] + offsets[0] = [0.0] + offsets[1] = [0.0, 3.0 / 4.0] + offsets[2] = [0.0, 1.0 / 3.0, 2.0 / 3.0] + inc = 14.0 / 23.0 + for numRings in xrange(4, 5): + o = [0] * numRings + accum = 0.0 + for i in xrange(0, numRings): + o[i] = accum % 1.0 + accum += inc + + offsets[numRings - 1] = o + + infinityTOffsets = offsets + + +__initInfinityTOffsets() + +def get_vertInfinity(numRings, rng): + tracks = [] + for i in xrange(0, numRings): + actions, durations = RingTracks.getVerticalInfinityRingActions() + track = RingTrack.RingTrack(actions, durations) + tracks.append(track) + + return (tracks, infinityTOffsets[numRings - 1], infinityPeriod) + + +def get_horizInfinity(numRings, rng): + tracks = [] + for i in xrange(0, numRings): + actions, durations = RingTracks.getHorizontalInfinityRingActions() + track = RingTrack.RingTrack(actions, durations) + tracks.append(track) + + return (tracks, infinityTOffsets[numRings - 1], infinityPeriod) + + +def get_evenCircle_withStationaryCenterRings_FASTER(numRings, rng): + tracks, tOffsets, period = get_evenCircle_withStationaryCenterRings(numRings, rng) + return (tracks, tOffsets, fullCirclePeriodFaster) + + +def get_plus_FASTER(numRings, rng): + tracks, tOffsets, period = get_plus(numRings, rng) + return (tracks, tOffsets, plusPeriodFaster) + + +allFuncs = [[get_keypad], [get_evenCircle, + get_followCircle, + get_evenCircle_withStationaryCenterRings, + get_verticalSlots, + get_horizontalSlots, + get_plus], [get_vertInfinity, + get_horizInfinity, + get_evenCircle_withStationaryCenterRings_FASTER, + get_plus_FASTER]] +dontUseFuncs = [[get_followCircle, + get_evenCircle_withStationaryCenterRings, + get_evenCircle_withStationaryCenterRings_FASTER, + get_plus, + get_plus_FASTER], + [], + [], + []] +trackListGenFuncs = [] + +def __listComplement(list1, list2): + result = [] + for item in list1: + if item not in list2: + result.append(item) + + return result + + +def __initFuncTables(): + global trackListGenFuncs + table = [[], [], []] + for diff in xrange(0, len(table)): + table[diff] = [[], + [], + [], + []] + for numRings in xrange(0, len(table[diff])): + table[diff][numRings] = __listComplement(allFuncs[diff], dontUseFuncs[numRings]) + + trackListGenFuncs = table + + +__initFuncTables() diff --git a/toontown/minigame/RingTracks.py b/toontown/minigame/RingTracks.py new file mode 100755 index 00000000..3836124a --- /dev/null +++ b/toontown/minigame/RingTracks.py @@ -0,0 +1,98 @@ +import math +import RingTrack +import RingAction +center = (0, 0) +up = (0, 1) +down = (0, -1) +left = (-1, 0) +right = (1, 0) +ul = (-1, 1) +ur = (1, 1) +lr = (1, -1) +ll = (-1, -1) + +def ringLerp(t, a, b): + omT = 1.0 - t + return (float(a[0]) * omT + float(b[0]) * t, float(a[1]) * omT + float(b[1]) * t) + + +def ringClerp(t, a, b): + return ringLerp(t * t * (3 - 2 * t), a, b) + + +def getSquareRingActions(): + return ([RingAction.RingActionFunction(ringClerp, [ul, ur]), + RingAction.RingActionFunction(ringClerp, [ur, lr]), + RingAction.RingActionFunction(ringClerp, [lr, ll]), + RingAction.RingActionFunction(ringClerp, [ll, ul])], [0.25, + 0.25, + 0.25, + 0.25]) + + +def getVerticalSlotActions(x): + return ([RingAction.RingActionFunction(ringClerp, [(x, 1), (x, -1)]), RingAction.RingActionFunction(ringClerp, [(x, -1), (x, 1)])], [0.5, 0.5]) + + +def getHorizontalSlotActions(y): + return ([RingAction.RingActionFunction(ringClerp, [(1, y), (-1, y)]), RingAction.RingActionFunction(ringClerp, [(-1, y), (1, y)])], [0.5, 0.5]) + + +def getCircleRingActions(): + + def circlePos(t): + return (math.sin(t * 2.0 * math.pi), math.cos(t * 2.0 * math.pi)) + + return ([RingAction.RingActionFunction(circlePos, [])], [1.0]) + + +def getVerticalInfinityRingActions(): + + def vertInfPos(t): + return (0.5 * math.sin(2.0 * t * 2.0 * math.pi), math.cos(t * 2.0 * math.pi)) + + return ([RingAction.RingActionFunction(vertInfPos, [])], [1.0]) + + +def getHorizontalInfinityRingActions(): + + def horizInfPos(t): + return (math.sin(t * 2.0 * math.pi), 0.5 * math.sin(2.0 * t * 2.0 * math.pi)) + + return ([RingAction.RingActionFunction(horizInfPos, [])], [1.0]) + + +RingOffset = 0.4 + +def getPlusUpRingActions(): + return ([RingAction.RingActionFunction(ringClerp, [(0, RingOffset), (0, 1)]), RingAction.RingActionFunction(ringClerp, [(0, 1), (0, RingOffset)])], [0.5, 0.5]) + + +def getPlusDownRingActions(): + return ([RingAction.RingActionFunction(ringClerp, [(0, -RingOffset), (0, -1)]), RingAction.RingActionFunction(ringClerp, [(0, -1), (0, -RingOffset)])], [0.5, 0.5]) + + +def getPlusRightRingActions(): + return ([RingAction.RingActionFunction(ringClerp, [(RingOffset, 0), (1, 0)]), RingAction.RingActionFunction(ringClerp, [(1, 0), (RingOffset, 0)])], [0.5, 0.5]) + + +def getPlusLeftRingActions(): + return ([RingAction.RingActionFunction(ringClerp, [(-RingOffset, 0), (-1, 0)]), RingAction.RingActionFunction(ringClerp, [(-1, 0), (-RingOffset, 0)])], [0.5, 0.5]) + + +def getHalfDomeRingActions(): + + def halfDome(t): + return (math.cos(t * math.pi), -math.sin(t * math.pi)) + + x1 = -1.0 + x2 = -1.0 / 3.0 + x3 = 1.0 / 3.0 + x4 = 1.0 + return ([RingAction.RingActionFunction(ringClerp, [(x1, 0), (x2, 0)]), + RingAction.RingActionFunction(ringClerp, [(x2, 0), (x3, 0)]), + RingAction.RingActionFunction(ringClerp, [(x3, 0), (x4, 0)]), + RingAction.RingActionFunction(halfDome, [])], [0.25, + 0.25, + 0.25, + 0.25]) diff --git a/toontown/minigame/RubberBand.py b/toontown/minigame/RubberBand.py new file mode 100755 index 00000000..b6731bb1 --- /dev/null +++ b/toontown/minigame/RubberBand.py @@ -0,0 +1,143 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +from toontown.toonbase import ToontownGlobals +import math +from math import * +import random + +class RubberBand: + SomeCounter = 0 + + def __init__(self, heldObject = None, heldOffset = None, taskPriority = 0): + self.heldObject = heldObject + self.heldOffset = heldOffset + self.bandNumber = self.SomeCounter + self._taskPriority = taskPriority + self.SomeCounter += 1 + if not heldOffset: + self.heldOffset = [0, 0, 0] + self.setup() + + def setup(self): + self.baseNode = render.attachNewNode('targetGameTargets') + target = CollisionSphere(0, 0, 0, 5.0) + target.setTangible(0) + targetNode = CollisionNode('thing') + targetNode.addSolid(target) + targetNodePath = self.baseNode.attachNewNode(targetNode) + self.slingModel = loader.loadModel('phase_4/models/minigames/slingshot_game_sling.bam') + self.slingModel.reparentTo(self.baseNode) + self.slingModel.setScale(1.0) + self.slingModel.setZ(-1.0) + self.bandGN = GeomNode('Band Geometry') + self.bandNodePathGeom = self.baseNode.attachNewNode(self.bandGN) + self.bandNodePathGeom.setTwoSided(True) + height = 13 + width = 5.5 + self.bandHeight = 1.0 + self.post1Pos = Point3(-width, 0, height) + self.post2Pos = Point3(width, 0, height) + taskMgr.add(self.redraw, 'recreateBand %s' % self.bandNumber, priority=self._taskPriority) + self.colorRelax = {} + self.colorRelax['Red'] = 1.0 + self.colorRelax['Green'] = 0.3 + self.colorRelax['Blue'] = 0.2 + self.colorRelax['Alpha'] = 1.0 + self.colorStrecth = {} + self.colorStrecth['Red'] = 1.0 + self.colorStrecth['Green'] = 0.6 + self.colorStrecth['Blue'] = 0.4 + self.colorStrecth['Alpha'] = 1.0 + + def setPos(self, pos): + self.baseNode.setPos(pos[0], pos[1], pos[2]) + + def delete(self): + taskMgr.remove('recreateBand %s' % self.bandNumber) + self.bandGN.removeAllGeoms() + self.baseNode.removeNode() + + def redraw(self, task): + color = {} + color['Red'] = 1.0 + color['Green'] = 0.3 + color['Blue'] = 0.2 + color['Alpha'] = 0.5 + self.bandGN.removeAllGeoms() + objPosX = self.heldObject.getX(self.baseNode) + self.heldOffset[0] + objPosY = self.heldObject.getY(self.baseNode) + self.heldOffset[1] + objPosZ = self.heldObject.getZ(self.baseNode) + self.heldOffset[2] + 1.5 + if objPosY > 0: + objPosY = 0 + objPosX = 0 + objPosZ = self.post1Pos[2] + objPosYSecondary = objPosY + 0.25 + if objPosYSecondary > 0: + objPosYSecondary = 0 + midPosY = objPosY + math.sqrt(abs(objPosY)) + midPosX = objPosX + 2 + maxStretch = 25.0 + bandThickness = self.bandHeight + objPosY * (1 / maxStretch) + if bandThickness < 0.2: + bandThickness = 0.2 + colorProp = bandThickness / self.bandHeight + color = {} + color['Red'] = colorProp * self.colorRelax['Red'] + (1 - colorProp) * self.colorStrecth['Red'] + color['Green'] = colorProp * self.colorRelax['Green'] + (1 - colorProp) * self.colorStrecth['Green'] + color['Blue'] = colorProp * self.colorRelax['Blue'] + (1 - colorProp) * self.colorStrecth['Blue'] + color['Alpha'] = colorProp * self.colorRelax['Alpha'] + (1 - colorProp) * self.colorStrecth['Alpha'] + bandBottomOrigin = self.post1Pos[2] - 0.5 * bandThickness + bandTopOrigin = self.post1Pos[2] + 0.5 * bandThickness + bandBottomHeld = objPosZ - 0.5 * bandThickness + bandTopHeld = objPosZ + 0.5 * bandThickness + midSegs = 12 + colorMultList = [] + colorHigh = 1.0 + colorLow = 0.5 + shapeVertexs = [] + shapeVertexs.append((self.post1Pos[0], self.post1Pos[1], bandBottomOrigin + 0.45)) + shapeVertexs.append((self.post1Pos[0], self.post1Pos[1], bandTopOrigin + 0.45)) + colorMultList.append(colorLow) + colorMultList.append(colorHigh) + s2 = pow(2, 0.5) + s2dif = s2 - 1 + objPosXSecondary = objPosX - 0.5 + shapeVertexs.append((objPosXSecondary, objPosYSecondary, bandBottomHeld)) + shapeVertexs.append((objPosXSecondary, objPosYSecondary, bandTopHeld)) + colorMultList.append(colorLow) + colorMultList.append(colorHigh) + shapeVertexs.append((objPosX, objPosY, bandBottomHeld)) + shapeVertexs.append((objPosX, objPosY, bandTopHeld)) + colorMultList.append(colorLow) + colorMultList.append(colorHigh) + objPosXSecondary = objPosX + 0.5 + shapeVertexs.append((objPosXSecondary, objPosYSecondary, bandBottomHeld)) + shapeVertexs.append((objPosXSecondary, objPosYSecondary, bandTopHeld)) + colorMultList.append(colorLow) + colorMultList.append(colorHigh) + shapeVertexs.append((self.post2Pos[0], self.post2Pos[1], bandBottomOrigin + 0.45)) + shapeVertexs.append((self.post2Pos[0], self.post2Pos[1], bandTopOrigin + 0.45)) + colorMultList.append(colorLow) + colorMultList.append(colorHigh) + gFormat = GeomVertexFormat.getV3cp() + bandVertexData = GeomVertexData('holds my vertices', gFormat, Geom.UHDynamic) + bandVertexWriter = GeomVertexWriter(bandVertexData, 'vertex') + bandColorWriter = GeomVertexWriter(bandVertexData, 'color') + for index in xrange(len(shapeVertexs)): + bandVertexWriter.addData3f(shapeVertexs[index][0], shapeVertexs[index][1], shapeVertexs[index][2]) + bandColorWriter.addData4f(color['Red'] * colorMultList[index], color['Green'] * colorMultList[index], color['Blue'] * colorMultList[index], color['Alpha'] * colorMultList[index]) + + bandTris = GeomTristrips(Geom.UHStatic) + for index in xrange(len(shapeVertexs)): + bandTris.addVertex(index) + + bandTris.closePrimitive() + bandGeom = Geom(bandVertexData) + bandGeom.addPrimitive(bandTris) + self.bandGN.addGeom(bandGeom) + return task.cont diff --git a/toontown/minigame/SwingVine.py b/toontown/minigame/SwingVine.py new file mode 100755 index 00000000..dae02dde --- /dev/null +++ b/toontown/minigame/SwingVine.py @@ -0,0 +1,657 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.distributed import DistributedObject +from direct.showutil import Rope +import math +from toontown.toonbase import ToontownGlobals +import VineGameGlobals +import VineSpider + +class SwingVine(NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('SwingVine') + defaultNormal = Vec3(1, 0, 0) + SwingAnimPeriod = 6.0 + SmallAnimAngle = 10 + NonMovingAnimAngle = 2 + SwingAnimFull = 0 + SwingAnimSmall = 1 + SwingAnimMinimal = 2 + MaxNumberOfFramesInSwingAnim = 144 + + def __init__(self, vineIndex, x, y, z, length = 20, baseAngle = 40, period = 4, spiderPeriod = 0): + NodePath.__init__(self, 'SwingVine') + self.cableLength = length + self.numLinks = 3 + self.links = [] + self.vineIndex = vineIndex + self.spider = None + self.spiderPeriod = spiderPeriod + self.hasSpider = not spiderPeriod == 0 + self.numTubes = self.numLinks * 3 + self.tubeLength = float(self.cableLength) / self.numTubes + self.tubeLength *= 1.25 + self.tIncrement = 1.0 / self.numTubes + self.tHalfIncrement = self.tIncrement / 2.0 + self.baseAngle = baseAngle + self.maxSwingAngle = deg2Rad(-90 + baseAngle) + self.minSwingAngle = deg2Rad(-90 - baseAngle) + self.period = period + self.swingingForward = True + self.swingT = 0 + self.swingAngle = 0 + self.setPos(x, y, z) + self.load() + self.attachedToons = {} + self.ival = None + self.spiderIval = None + self.unloading = False + self.spiderMovingDown = True + return + + def load(self): + self.root = self.attachNewNode('root') + self.topLink = self.root.attachNewNode('topLink') + self.setupCable() + self.reparentTo(render) + self.debugTangent = None + nearBubble = CollisionSphere(0, 0, -self.cableLength / 2.0, self.cableLength / 2.0) + nearBubble.setTangible(0) + nearBubbleNode = CollisionNode('SwingVine') + nearBubbleNode.setCollideMask(GeomNode.getDefaultCollideMask()) + nearBubbleNode.addSolid(nearBubble) + self.rope.setName('SwingVine-%d' % self.vineIndex) + self.setupTubes() + if self.hasSpider: + self.spider = VineSpider.VineSpider() + self.spiderT = 0.25 + return + + def unload(self): + self.unloading = True + if self.ival: + self.ival.finish() + self.ival = None + if self.spiderIval: + self.spiderIval.finish() + self.spiderIval = None + if self.debugTangent: + self.debugTangent.removeNode() + for tube in self.tubes: + tube.removeNode() + + self.tubes = [] + for tube in self.tubes2: + tube.removeNode() + + self.tubes2 = [] + if self.hasSpider: + self.spider.destroy() + del self.spider + for toonInfo in self.attachedToons.values(): + attachNode = toonInfo[4] + if attachNode: + attachNode.removeNode() + swingIval = toonInfo[6] + if swingIval: + swingIval.finish() + + self.removeNode() + return + + def setupCable(self): + self.links = [] + self.links.append((self.topLink, Point3(0, 0, 0))) + anchor = self.topLink + for linkNum in xrange(self.numLinks): + anchor = self.__makeLink(anchor, linkNum) + + self.bottomLink = self.links[-1][0] + self.link1 = self.links[-2][0] + self.rope = self.makeSpline() + self.rope.reparentTo(self.root) + myTexture = loader.loadTexture('phase_4/maps/swinging_vine.jpg') + gameAssets = loader.loadModel('phase_4/models/minigames/vine_game') + vine = gameAssets.find('**/vine1') + self.cableTex = vine.findTexture('*') + if self.cableTex: + self.cableTex.setWrapV(Texture.WMRepeat) + self.rope.setTexture(self.cableTex) + ts = TextureStage.getDefault() + self.rope.setTexScale(ts, 1.0, 0.15) + self.rope.setTransparency(1) + if self.vineIndex == VineGameGlobals.NumVines - 1: + pass + if self.cableTex: + self.setupStaticPart(self.cableTex) + + def setupStaticPart(self, vineTexture): + cm = CardMaker('card') + cm.setFrame(-0.5, 0.5, -0.1, 8) + self.staticVine = self.attachNewNode(cm.generate()) + self.staticVine.setTexture(vineTexture) + self.setTransparency(1) + radius = 0.5 + tubeIndex = 0 + colNode = CollisionNode('StaticVine-%d-%d' % (self.vineIndex, tubeIndex)) + bz = 0 + az = 58 + quad = CollisionPolygon(Point3(0.25, radius + 1, bz), Point3(0.25, radius + 1, az), Point3(0.25, -radius - 1, az), Point3(0.25, -radius - 1, bz)) + colNode.addSolid(quad) + colNode.setCollideMask(ToontownGlobals.PieBitmask) + colNode.setBounds(BoundingSphere(Point3(0, 0, 0), 10)) + self.staticVine.attachNewNode(colNode) + colNode2 = CollisionNode('StaticVine-%d-%d' % (self.vineIndex, tubeIndex)) + quad2 = CollisionPolygon(Point3(-0.25, -radius - 1, bz), Point3(-0.25, -radius - 1, az), Point3(-0.25, +radius + 1, az), Point3(-0.25, +radius + 1, bz)) + colNode2.addSolid(quad2) + colNode2.setCollideMask(ToontownGlobals.PieBitmask) + colNode2.setBounds(BoundingSphere(Point3(0, 0, 0), 10)) + self.staticVine.attachNewNode(colNode2) + + def setupTubes(self): + self.tubes = [] + self.tubes2 = [] + radius = 0.5 + for tubeIndex in xrange(self.numTubes): + az = self.tubeLength / 2.0 + bz = -self.tubeLength / 2.0 + ct = CollisionTube(0, 0, az, 0, 0, bz, radius) + ct.setTangible(0) + colNode = CollisionNode('SwingVine-%d-%d' % (self.vineIndex, tubeIndex)) + quad = CollisionPolygon(Point3(0.25, radius + 1, bz), Point3(0.25, radius + 1, az), Point3(0.25, -radius - 1, az), Point3(0.25, -radius - 1, bz)) + colNode.addSolid(quad) + colNode.setCollideMask(ToontownGlobals.PieBitmask) + colNode.setBounds(BoundingSphere(Point3(0, 0, 0), 10)) + colNode2 = CollisionNode('SwingVine-%d-%d' % (self.vineIndex, tubeIndex)) + quad2 = CollisionPolygon(Point3(-0.25, -radius - 1, bz), Point3(-0.25, -radius - 1, az), Point3(-0.25, +radius + 1, az), Point3(-0.25, +radius + 1, bz)) + colNode2.addSolid(quad2) + colNode2.setCollideMask(ToontownGlobals.PieBitmask) + colNode2.setBounds(BoundingSphere(Point3(0, 0, 0), 10)) + newTube = render.attachNewNode(colNode) + self.tubes.append(newTube) + newTube2 = render.attachNewNode(colNode2) + self.tubes2.append(newTube2) + + self.updateTubes() + + def __makeLink(self, anchor, linkNum): + an = ActorNode('link%s' % linkNum) + anp = NodePath(an) + anp.reparentTo(self.root) + z = float(linkNum + 1) / float(self.numLinks) * self.cableLength + anp.setPos(self.topLink.getPos()) + anp.setZ(anp.getZ() - z) + self.links.append((anp, Point3(0, 0, 0))) + return anp + + def makeSpline(self): + rope = Rope.Rope() + for i in xrange(len(self.links)): + pass + + rope.setup(min(len(self.links), 4), self.links) + for i in xrange(len(self.links)): + pass + + rope.curve.normalizeKnots() + self.notify.debug('after normalize Knots') + for i in xrange(len(self.links)): + pass + + rn = rope.ropeNode + rn.setRenderMode(RopeNode.RMBillboard) + rn.setNumSlices(3) + rn.setTubeUp(Vec3(0, -1, 0)) + rn.setUvMode(RopeNode.UVDistance) + rn.setUvDirection(False) + rn.setThickness(1.0) + return rope + + def positionLink1(self, t, angleInRadians): + degAngle = rad2Deg(angleInRadians) + diffFrom90 = degAngle - -90.0 + link1AngleDiff = diffFrom90 * 2 / 3.0 + link1AngleToUse = deg2Rad(-90 + link1AngleDiff) + lengthToUse = self.cableLength * 2.0 / 3.0 + link1X = math.cos(link1AngleToUse) * lengthToUse + link1Z = math.sin(link1AngleToUse) * lengthToUse + self.link1.setPos(link1X, 0, link1Z) + + def swingForward(self, t): + diffAngle = self.maxSwingAngle - self.minSwingAngle + multiplier = t + angleToUse = self.minSwingAngle + multiplier * diffAngle + newX = math.cos(angleToUse) * self.cableLength + newZ = math.sin(angleToUse) * self.cableLength + self.bottomLink.setPos(newX, 0, newZ) + self.positionLink1(t, angleToUse) + oldSwingingForward = self.swingingForward + self.swingingForward = True + self.swingT = t + self.swingAngle = angleToUse + self.updateAttachedStuff() + if not oldSwingingForward == self.swingingForward: + self.updateSwingAnims() + + def swingBack(self, t): + diffAngle = self.maxSwingAngle - self.minSwingAngle + multiplier = t + angleToUse = self.maxSwingAngle - multiplier * diffAngle + newX = math.cos(angleToUse) * self.cableLength + newZ = math.sin(angleToUse) * self.cableLength + self.bottomLink.setPos(newX, 0, newZ) + self.positionLink1(t, angleToUse) + oldSwingingForward = self.swingingForward + self.swingingForward = False + self.swingT = t + self.swingAngle = angleToUse + self.updateAttachedStuff() + if not oldSwingingForward == self.swingingForward: + self.updateSwingAnims() + + def moveSpiderDown(self, t): + self.spiderMovingDown = True + self.spiderT = t + + def moveSpiderUp(self, t): + self.spiderMovingDown = False + self.spiderT = 1 - t + + def startSwing(self): + forwardX = math.cos(self.maxSwingAngle) * self.cableLength + forwardZ = math.sin(self.maxSwingAngle) * self.cableLength + backX = math.cos(self.minSwingAngle) * self.cableLength + backZ = math.sin(self.minSwingAngle) * self.cableLength + self.bottomLink.setPos(backX, 0, backZ) + self.ival = Sequence(LerpFunctionInterval(self.swingForward, duration=self.period / 2.0, blendType='easeInOut')) + self.ival.append(LerpFunctionInterval(self.swingBack, duration=self.period / 2.0, blendType='easeInOut')) + self.ival.loop() + if self.hasSpider: + self.spiderIval = Sequence(LerpFunctionInterval(self.moveSpiderDown, duration=self.spiderPeriod / 2.0, blendType='easeInOut')) + self.spiderIval.append(LerpFunctionInterval(self.moveSpiderUp, duration=self.spiderPeriod / 2.0, blendType='easeInOut')) + self.spiderIval.loop() + + def stopSwing(self): + if self.ival: + self.ival.pause() + if self.hasSpider and self.spiderIval: + self.spiderIval.pause() + if self.hasSpider: + self.spider.hide() + + def getAttachNode(self, toonId): + retval = None + if toonId in self.attachedToons: + existingAttachNode = self.attachedToons[toonId][4] + if existingAttachNode: + retval = existingAttachNode + else: + retval = render.attachNewNode('vineAttachNode-%s-%s' % (self.vineIndex, toonId)) + return retval + + def calcOffset(self, toonId): + offset = Point3(0, 0, 0) + toon = base.cr.doId2do.get(toonId) + if toon: + toon.pose('swing', 86) + leftHand = toon.find('**/leftHand') + if not leftHand.isEmpty(): + offset = leftHand.getPos(toon) + self.notify.debug('offset = %s' % offset) + else: + self.notify.warning('left hand not found for toon %d' % toonId) + else: + self.notify.warning('toon %d not found' % toonId) + return offset + + def doubleCheckOffset(self, toonId): + if toonId in self.attachedToons: + curOffset = self.attachedToons[toonId][3] + if curOffset == Point3.zero(): + newOffset = self.calcOffset(toonId) + self.attachedToons[toonId][3] = newOffset + av = base.cr.doId2do.get(toonId) + self.notify.info('correcting wrong offset %s and changing to %s' % (curOffset, newOffset)) + if av: + av.setPos(-newOffset) + + def attachToon(self, toonId, t, facingRight, setupAnim = True): + self.notify.debug('attachToon toonId=%d vineIndex=%d' % (toonId, self.vineIndex)) + temp = Vec3(self.defaultNormal) + offset = self.calcOffset(toonId) + attachNode = self.getAttachNode(toonId) + if facingRight: + attachNode.setH(-90) + else: + attachNode.setH(90) + self.attachedToons[toonId] = [t, + temp, + Vec3(0, 0, 0), + offset, + attachNode, + facingRight, + None] + av = base.cr.doId2do.get(toonId) + if av: + av.reparentTo(attachNode) + if offset == Point3.zero(): + self.notify.warning('calculated offset for %d is zero' % toonId) + av.setPos(-offset) + if setupAnim: + self.setupSwingAnim(toonId) + else: + zDownTheVine = self.getPos().getZ() - t * self.cableLength + attachNode.setPos(self.getPos()) + attachNode.setZ(zDownTheVine) + else: + self.notify.warning('av %d not found' % toonId) + return + + def changeAttachedToonT(self, toonId, t): + if toonId in self.attachedToons: + oldT = self.attachedToons[toonId][0] + self.attachedToons[toonId][0] = t + oldSwingType = self.calcSwingAnimType(oldT) + newSwingType = self.calcSwingAnimType(t) + if oldSwingType != newSwingType: + self.setupSwingAnim(toonId) + else: + self.notify.warning('changeAttachedToonT avId %d was not in the dict' % toonId) + self.attachToon(toonId, t, 1) + + def changeAttachedToonFacing(self, toonId, facing): + if toonId in self.attachedToons: + curT = self.attachedToons[toonId][0] + self.detachToon(toonId) + self.attachToon(toonId, curT, facing) + else: + self.notify.warning('changeAttachedToonFacing avId %d was not in the dict' % toonId) + self.attachToon(toonId, VineGameGlobals.VineFellDownT, 1) + + def detachToon(self, toonId): + self.notify.debug('detachToon toonId=%d vineIndex=%d' % (toonId, self.vineIndex)) + if toonId in self.attachedToons: + self.attachedToons[toonId][4].removeNode() + swingIval = self.attachedToons[toonId][6] + if swingIval: + self.notify.debug('deleting swing ival %s' % swingIval) + swingIval.finish() + self.attachedToons[toonId][6] = None + del swingIval + del self.attachedToons[toonId] + return + + def getAttachedToonInfo(self, toonId): + if toonId in self.attachedToons: + return self.attachedToons[toonId] + else: + return None + return None + + def getCenterTForTube(self, tubeIndex): + retval = self.tIncrement * tubeIndex + self.tHalfIncrement + return retval + + def updateTubes(self): + newPoint = Vec3(0, 0, 0) + curve = self.rope.ropeNode.getCurve().evaluate() + for tubeIndex in xrange(self.numTubes): + tube = self.tubes[tubeIndex] + t = self.getCenterTForTube(tubeIndex) + curve.evalPoint(t, newPoint) + tube.setPos(newPoint) + tangent = Vec3(0, 0, 0) + curve.evalTangent(t, tangent) + tangent.normalize() + theta = math.atan2(tangent.getZ(), tangent.getX()) + degrees = rad2Deg(theta) + rAngle = -90 - degrees + tube.setR(rAngle) + + for tubeIndex in xrange(self.numTubes): + tube = self.tubes2[tubeIndex] + t = self.getCenterTForTube(tubeIndex) + curve.evalPoint(t, newPoint) + tube.setPos(newPoint) + tangent = Vec3(0, 0, 0) + curve.evalTangent(t, tangent) + tangent.normalize() + theta = math.atan2(tangent.getZ(), tangent.getX()) + degrees = rad2Deg(theta) + rAngle = -90 - degrees + tube.setR(rAngle) + + def updateSpiders(self): + curve = self.rope.ropeNode.getCurve().evaluate() + if self.hasSpider: + t = self.spiderT + newPoint = Vec3(0, 0, 0) + curve.evalPoint(t, newPoint) + newPoint.setY(-0.5) + self.spider.setPos(newPoint) + tangent = Vec3(0, 0, 0) + curve.evalTangent(t, tangent) + theta = math.atan2(tangent.getZ(), tangent.getX()) + degrees = rad2Deg(theta) + pAngle = degrees + 90 + pAngle = -pAngle + if self.spiderMovingDown: + self.spider.setR(pAngle) + else: + self.spider.setR(pAngle - 180) + + def updateAttachedStuff(self): + self.updateTubes() + self.updateSpiders() + self.updateAttachedToons() + + def updateAttachedToons(self): + curve = self.rope.ropeNode.getCurve().evaluate() + for avId in self.attachedToons.keys(): + self.doubleCheckOffset(avId) + t = self.attachedToons[avId][0] + newPoint = Vec3(0, 0, 0) + curve.evalPoint(t, newPoint) + attachNode = self.attachedToons[avId][4] + attachNode.setPos(newPoint) + tangent = Vec3(0, 0, 0) + curve.evalTangent(t, tangent) + tangent.normalize() + unitY = Vec3(0, 1, 0) + normal = tangent.cross(unitY) + theta = math.atan2(tangent.getZ(), tangent.getX()) + degrees = rad2Deg(theta) + pAngle = degrees + 90 + pAngle *= 0.5 + facingRight = self.attachedToons[avId][5] + if facingRight: + pass + if self.debugTangent: + self.debugTangent.setPos(newPoint + normal) + self.attachedToons[avId][1] = newPoint + self.attachedToons[avId][2] = normal + + def getAngularVelocity(self): + return deg2Rad(self.baseAngle) / self.period / 4.0 + + def getLinearSpeed(self, t): + retval = self.getAngularVelocity() * self.cableLength * t + return retval + + def calcTFromTubeHit(self, colEntry): + name = colEntry.getIntoNodePath().getName() + parts = name.split('-') + if len(parts) < 3: + return + tubeIndex = int(parts[2]) + if tubeIndex < 0 or tubeIndex >= len(self.tubes): + return + if parts[0] == 'StaticVine': + retval = 0 + else: + curve = self.rope.ropeNode.getCurve().evaluate() + tangent = Vec3(0, 0, 0) + centerT = self.getCenterTForTube(tubeIndex) + curve.evalTangent(centerT, tangent) + tangent.normalize() + endPos = colEntry.getSurfacePoint(render) + tubePos = self.tubes[tubeIndex].getPos() + vector = endPos - tubePos + projection = vector.dot(tangent) + self.notify.debug('projection = %s' % projection) + diffT = projection / self.tubeLength / 2.0 + retval = centerT + diffT + P1 = tubePos + P2 = tubePos + tangent + P3 = endPos + u = (P3.getX() - P1.getX()) * (P2.getX() - P1.getX()) + (P3.getZ() - P1.getZ()) * (P2.getZ() - P1.getZ()) + self.notify.debug('u=%s' % u) + x = P1.getX() + u * (P2.getX() - P1.getX()) + z = P1.getZ() + u * (P2.getZ() - P1.getZ()) + perpPoint = Vec3(x, 0, z) + distanceVector = perpPoint - tubePos + distance = distanceVector.length() + diffT = distance / self.cableLength + retval = centerT + diffT + if retval > 1: + retval = 1 + if retval < 0: + retval = 0 + self.notify.debug('retval = %s' % retval) + return retval + + def setupSwingAnimFull(self, av, avId): + toonT = self.attachedToons[avId][0] + playRate = self.SwingAnimPeriod / self.period + swingInterval = Sequence() + duration = (1 - self.swingT) * self.period / 2.0 + if duration < 0.001: + duration = 0.001 + facingRight = self.attachedToons[avId][5] + if self.swingingForward and facingRight or not self.swingingForward and not facingRight: + maxLeftFrame = 108 + downFrame1 = 143 + downFrame2 = 0 + maxRightFrame = 35 + numLeftFramesChoppedOff = (downFrame1 - maxLeftFrame) * (1 - toonT) + numRightFramesChoppedOff = maxRightFrame * (1 - toonT) + numLeftFramesChoppedOff = 0 + numRightFramesChoppedOff = 0 + maxLeftFrame += numLeftFramesChoppedOff + maxRightFrame -= numRightFramesChoppedOff + numFirstHalfFrames = downFrame1 - maxLeftFrame + 1 + numSecondHalfFrames = maxRightFrame - downFrame2 + 1 + numFrames = numFirstHalfFrames + numSecondHalfFrames + framesToChopOff = numFrames * self.swingT + if framesToChopOff < numFirstHalfFrames: + startingFrame = maxLeftFrame + framesToChopOff + halfDur = duration / 2.0 + swing1Dur = (1 - framesToChopOff / numFirstHalfFrames) * halfDur + toonSwing1 = ActorInterval(av, 'swing', startFrame=startingFrame, endFrame=downFrame1, playRate=playRate, name='swingForward1') + toonSwing2 = ActorInterval(av, 'swing', startFrame=downFrame2, endFrame=maxRightFrame, playRate=playRate, name='swingForward2') + swingInterval.append(toonSwing1) + swingInterval.append(toonSwing2) + else: + secondHalfFramesToChopOff = framesToChopOff - numFirstHalfFrames + startingFrame = downFrame2 + secondHalfFramesToChopOff + toonSwing2 = ActorInterval(av, 'swing', startFrame=startingFrame, endFrame=maxRightFrame, playRate=playRate, name='swingForward2') + swingInterval.append(toonSwing2) + else: + maxRightFrame = 35 + maxLeftFrame = 107 + midFrame = (maxLeftFrame + maxRightFrame) / 2.0 + numLeftFramesChoppedOff = 0 + numRightFramesChoppedOff = 0 + maxLeftFrame -= numLeftFramesChoppedOff + maxRightFrame += numRightFramesChoppedOff + numFrames = maxLeftFrame - maxRightFrame + 1 + framesToChopOff = numFrames * self.swingT + startingFrame = maxRightFrame + framesToChopOff + toonSwing = ActorInterval(av, 'swing', startFrame=startingFrame, endFrame=maxLeftFrame, playRate=playRate) + swingInterval.append(toonSwing) + self.attachedToons[avId][6] = swingInterval + swingInterval.start() + + def setupSwingAnimSmall(self, av, avId): + swingInterval = Sequence() + maxLeftFrame = 0 + maxRightFrame = 20 + startingLeftFrame = 5 + numFrames = maxRightFrame - maxLeftFrame + 1 + playRate = self.SwingAnimPeriod / self.period + duration = (1 - self.swingT) * self.period / 2.0 + if duration == 0: + return + toonT = self.attachedToons[avId][0] + framesPerSecondBase = self.MaxNumberOfFramesInSwingAnim / self.SwingAnimPeriod + desiredFramesPerSecond = numFrames / duration + slowedPlayRate = desiredFramesPerSecond / framesPerSecondBase + facingRight = self.attachedToons[avId][5] + if self.swingingForward and facingRight or not self.swingingForward and not facingRight: + toonSwing1 = ActorInterval(av, 'swing', startFrame=startingLeftFrame, endFrame=maxLeftFrame, playRate=slowedPlayRate) + toonSwing2 = ActorInterval(av, 'swing', startFrame=maxLeftFrame + 1, endFrame=maxRightFrame - startingLeftFrame, playRate=slowedPlayRate) + swingInterval.append(toonSwing1) + swingInterval.append(toonSwing2) + else: + toonSwing1 = ActorInterval(av, 'swing', startFrame=maxRightFrame - startingLeftFrame, endFrame=maxRightFrame, playRate=slowedPlayRate) + toonSwing2 = ActorInterval(av, 'swing', startFrame=maxRightFrame, endFrame=startingLeftFrame, playRate=slowedPlayRate) + swingInterval.append(toonSwing1) + swingInterval.append(toonSwing2) + self.attachedToons[avId][6] = swingInterval + swingInterval.start() + + def setupSwingAnimMinimal(self, av, avId): + swingInterval = Sequence() + maxLeftFrame = 88 + maxRightFrame = 84 + duration = (1 - self.swingT) * self.period / 2.0 + if duration < 0.001: + duration = 0.001 + numFrames = maxLeftFrame - maxRightFrame + 1 + numFrames *= 2 + framesPerSecondBase = self.MaxNumberOfFramesInSwingAnim / self.SwingAnimPeriod + desiredFramesPerSecond = numFrames / duration + slowedPlayRate = desiredFramesPerSecond / framesPerSecondBase + toonSwing1 = ActorInterval(av, 'swing', startFrame=maxLeftFrame, endFrame=maxRightFrame, playRate=slowedPlayRate) + toonSwing2 = ActorInterval(av, 'swing', startFrame=maxRightFrame, endFrame=maxLeftFrame, playRate=slowedPlayRate) + swingInterval.append(toonSwing1) + swingInterval.append(toonSwing2) + self.attachedToons[avId][6] = swingInterval + swingInterval.start() + + def setupSwingAnim(self, avId): + if avId not in self.attachedToons: + return + av = base.cr.doId2do.get(avId) + if not av: + return + prevIval = self.attachedToons[avId][6] + if prevIval: + prevIval.pause() + del prevIval + toonT = self.attachedToons[avId][0] + swingAnimType = self.calcSwingAnimType(toonT) + if swingAnimType == self.SwingAnimFull: + self.setupSwingAnimFull(av, avId) + elif swingAnimType == self.SwingAnimSmall: + self.setupSwingAnimSmall(av, avId) + else: + self.setupSwingAnimMinimal(av, avId) + + def calcSwingAnimType(self, toonT): + angleInDegrees = toonT * self.baseAngle + retval = self.SwingAnimFull + if angleInDegrees > 10: + retval = self.SwingAnimFull + elif angleInDegrees > 2: + retval = self.SwingAnimSmall + else: + retval = self.SwingAnimMinimal + return retval + + def updateSwingAnims(self): + if self.unloading: + return + for avId in self.attachedToons.keys(): + self.setupSwingAnim(avId) diff --git a/toontown/minigame/TagGameGlobals.py b/toontown/minigame/TagGameGlobals.py new file mode 100755 index 00000000..a440c26d --- /dev/null +++ b/toontown/minigame/TagGameGlobals.py @@ -0,0 +1,104 @@ +from toontown.toonbase import ToontownGlobals + +DURATION = 90 +DEFAULT_SKY = 'phase_3.5/models/props/TT_sky' +DEFAULT_GROUND = 'phase_4/models/minigames/tag_arena' + +DEFAULT_TREASURE_POINTS = [ + (0, 0, 0.1), + (5, 20, 0.1), + (0, 40, 0.1), + (-5, -20, 0.1), + (0, -40, 0.1), + (20, 0, 0.1), + (40, 5, 0.1), + (-20, -5, 0.1), + (-40, 0, 0.1), + (22, 20, 0.1), + (-20, 22, 0.1), + (20, -20, 0.1), + (-25, -20, 0.1), + (20, 40, 0.1), + (20, -44, 0.1), + (-24, 40, 0.1), + (-20, -40, 0.1)] + +DEFAULT_DROP_POINTS = ( + (0, 10, 0.25, 180, 0, 0), + (10, 0, 0.25, 90, 0, 0), + (0, -10, 0.25, 0, 0, 0), + (-10, 0, 0.25, -90, 0, 0) +) + +SKY = { + ToontownGlobals.TheBrrrgh: 'phase_3.5/models/props/BR_sky' +} + +GROUND = { + ToontownGlobals.TheBrrrgh: 'phase_8/models/minigames/tag_arena_BR', + ToontownGlobals.DaisyGardens: 'phase_8/models/minigames/tag_arena_DG' +} + +TREASURE_POINTS = { + ToontownGlobals.TheBrrrgh: [ + (-27, -30.7, 10.4), + (0.2, -33.6, 2.5), + (-60.9, -36.6, 2.7), + (-59.3, -9.04, 0.31), + (-78.4, 2.7, 4.2), + (-37.8, 2.1, 3.7), + (-28.6, 35.8, 11.6), + (7.7, 5.8, 2.4), + (41.5, -30.1, 14.2), + (37.8, 32.1, 11.7), + (2.8, 51.1, 3), + (-16.8, 0.6, 2.2), + (-72.4, 37.7, 5) + ], + ToontownGlobals.DaisyGardens: [ + (21.3, 27.6, 0.025), + (30.4, -8.4, 0.025), + (0.5, -36.3, 0.025), + (-34.9, -10.4, 0.025), + (-35.3, -34.3, 0.025), + (-33.3, -54, 0.025), + (41.6, -39.4, 0.025), + (19.0, -41.2, 0.025), + (0.5, 61.3, 0.025), + (-24.4, 52.1, 0.025), + (-49.9, 24.2, 0.025), + (-43.8, 8.4, 0.025) + ] +} + +DROP_POINTS = { + ToontownGlobals.TheBrrrgh: ( + (-30.4, 37.5, 11.1, 2006, 0, 0), + (34.7, -21.5, 12.5, 1875, 0, 0), + (-31.9, -29.4, 10, 1774), + (-74.1, -30.5, 5.3, 1720) + ), + ToontownGlobals.DaisyGardens: ( + (38.6, -55.1, 0.025, 396, 0, 0), + (3.1, 54, 0.025, 898, 0, 0), + (-37.8, -49.4, 0.025, 685, 0, 0), + (-55.9, 21, 0.025, 608, 0, 0) + ) +} + +SNOW_HOODS = [ToontownGlobals.TheBrrrgh] + +def getSky(safezoneId): + return SKY.get(safezoneId, DEFAULT_SKY) + +def getGround(safezoneId): + return GROUND.get(safezoneId, DEFAULT_GROUND) + +def getTreasurePoints(safezoneId): + return TREASURE_POINTS.get(safezoneId, DEFAULT_TREASURE_POINTS) + +def getDropPoints(safezoneId): + return DROP_POINTS.get(safezoneId, DEFAULT_DROP_POINTS) + +def isSnowHood(safezoneId): + return safezoneId in SNOW_HOODS diff --git a/toontown/minigame/TagTreasurePlannerAI.py b/toontown/minigame/TagTreasurePlannerAI.py new file mode 100755 index 00000000..1bb35626 --- /dev/null +++ b/toontown/minigame/TagTreasurePlannerAI.py @@ -0,0 +1,17 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.safezone import RegenTreasurePlannerAI + +class TagTreasurePlannerAI(RegenTreasurePlannerAI.RegenTreasurePlannerAI): + notify = DirectNotifyGlobal.directNotify.newCategory('TagTreasurePlannerAI') + + def __init__(self, zoneId, game, callback, treasureType, spawnPoints): + self.numPlayers = 0 + self.game = game + self.spawnPoints = spawnPoints + RegenTreasurePlannerAI.RegenTreasurePlannerAI.__init__(self, zoneId, treasureType, 'TagTreasurePlanner-' + str(zoneId), 3, 6, callback) + + def initSpawnPoints(self): + return self.spawnPoints + + def validAvatar(self, treasure, av): + return av.doId != self.game.itAvId diff --git a/toontown/minigame/TargetGameGlobals.py b/toontown/minigame/TargetGameGlobals.py new file mode 100755 index 00000000..f7d55e98 --- /dev/null +++ b/toontown/minigame/TargetGameGlobals.py @@ -0,0 +1,197 @@ +from panda3d.core import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +ENDLESS_GAME = config.GetBool('endless-ring-game', 0) +NUM_RING_GROUPS = 16 +MAX_TOONXZ = 15.0 +MAX_LAT = 5 +MAX_FIELD_SPAN = 135 +CollisionRadius = 1.5 +CollideMask = ToontownGlobals.CatchGameBitmask +TARGET_RADIUS = MAX_TOONXZ / 3.0 * 0.9 +targetColors = ((TTLocalizer.ColorRed, VBase4(1.0, 0.4, 0.2, 1.0)), + (TTLocalizer.ColorGreen, VBase4(0.0, 0.9, 0.2, 1.0)), + (TTLocalizer.ColorOrange, VBase4(1.0, 0.5, 0.25, 1.0)), + (TTLocalizer.ColorPurple, VBase4(1.0, 0.0, 1.0, 1.0)), + (TTLocalizer.ColorWhite, VBase4(1.0, 1.0, 1.0, 1.0)), + (TTLocalizer.ColorBlack, VBase4(0.0, 0.0, 0.0, 1.0)), + (TTLocalizer.ColorYellow, VBase4(1.0, 1.0, 0.2, 1.0))) +ENVIRON_LENGTH = 300 +ENVIRON_WIDTH = 150.0 +ringColorSelection = [(0, 1, 2), + 3, + 4, + 5, + 6] +colorRed = {} +colorRed['Red'] = 1.0 +colorRed['Green'] = 0.0 +colorRed['Blue'] = 0.0 +colorRed['Alpha'] = 0.5 +colorBlue = {} +colorBlue['Red'] = 0.0 +colorBlue['Green'] = 0.0 +colorBlue['Blue'] = 1.0 +colorBlue['Alpha'] = 0.5 +colorGreen = {} +colorGreen['Red'] = 0.0 +colorGreen['Green'] = 1.0 +colorGreen['Blue'] = 0.0 +colorGreen['Alpha'] = 0.5 +colorYellow = {} +colorYellow['Red'] = 1.0 +colorYellow['Green'] = 1.0 +colorYellow['Blue'] = 0.0 +colorYellow['Alpha'] = 0.5 +colorPurple = {} +colorPurple['Red'] = 0.75 +colorPurple['Green'] = 0.0 +colorPurple['Blue'] = 1.0 +colorPurple['Alpha'] = 0.5 +colorOrange = {} +colorOrange['Red'] = 1.0 +colorOrange['Green'] = 0.6 +colorOrange['Blue'] = 0.0 +colorOrange['Alpha'] = 0.5 +colorBlack = {} +colorBlack['Red'] = 0.0 +colorBlack['Green'] = 0.0 +colorBlack['Blue'] = 0.0 +colorBlack['Alpha'] = 1.0 +colorWhite = {} +colorWhite['Red'] = 1.0 +colorWhite['Green'] = 1.0 +colorWhite['Blue'] = 1.0 +colorWhite['Alpha'] = 1.0 +difficultyPatterns = {ToontownGlobals.ToontownCentral: [[8, + 4, + 2, + 0], + [10, + 16, + 21, + 28], + [31, + 15, + 7, + 3.5], + [colorRed, + colorGreen, + colorBlue, + colorYellow], + [2, + 2, + 2, + 1], + 10, + 2], + ToontownGlobals.DonaldsDock: [[7, + 4, + 2, + 0], + [11, + 17, + 23, + 32], + [29, + 13, + 6.5, + 3.2], + [colorRed, + colorGreen, + colorBlue, + colorYellow], + [2, + 2, + 2, + 1], + 9, + 2], + ToontownGlobals.DaisyGardens: [[6, + 4, + 2, + 0], + [11, + 18, + 25, + 34], + [29, + 13, + 6.5, + 3.1], + [colorRed, + colorGreen, + colorBlue, + colorYellow], + [2, + 2, + 2, + 1], + 8, + 2], + ToontownGlobals.MinniesMelodyland: [[6, + 4, + 2, + 0], + [12, + 19, + 27, + 37], + [28, + 12, + 6, + 3.0], + [colorGreen, + colorBlue, + colorYellow, + colorPurple], + [2, + 2, + 2, + 1], + 8, + 2], + ToontownGlobals.TheBrrrgh: [[5, + 4, + 2, + 0], + [12, + 20, + 29, + 40], + [25, + 12, + 5.5, + 2.5], + [colorGreen, + colorBlue, + colorYellow, + colorPurple], + [2, + 2, + 2, + 1], + 7, + 2], + ToontownGlobals.DonaldsDreamland: [[4, + 3, + 1, + 0], + [12, + 21, + 31, + 42], + [20, + 10, + 4.5, + 2.0], + [colorBlue, + colorYellow, + colorPurple, + colorOrange], + [2, + 2, + 2, + 1], + 7, + 2]} diff --git a/toontown/minigame/ToonBlitzAssetMgr.py b/toontown/minigame/ToonBlitzAssetMgr.py new file mode 100755 index 00000000..9f3b1e27 --- /dev/null +++ b/toontown/minigame/ToonBlitzAssetMgr.py @@ -0,0 +1,153 @@ +from panda3d.core import * +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToonBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame import ToonBlitzGlobals, TwoDBlock +from pandac.PandaModules import CardMaker + +class ToonBlitzAssetMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedToonBlitzAssets') + + def __init__(self, game): + self.__defineConstants() + self.game = game + self.load() + + def __defineConstants(self): + pass + + def load(self): + self.world = NodePath('ToonBlitzWorld') + self.background = loader.loadModel('phase_4/models/minigames/toonblitz_game') + self.background.reparentTo(self.world) + self.startingWall = loader.loadModel('phase_4/models/minigames/toonblitz_game_wall') + self.startingPipe = loader.loadModel('phase_4/models/minigames/toonblitz_game_start') + self.exitElevator = loader.loadModel('phase_4/models/minigames/toonblitz_game_elevator') + self.arrow = loader.loadModel('phase_4/models/minigames/toonblitz_game_arrow') + self.sprayProp = loader.loadModel('phase_4/models/minigames/prop_waterspray') + self.treasureModelList = [] + salesIcon = loader.loadModel('phase_4/models/minigames/salesIcon') + self.treasureModelList.append(salesIcon) + moneyIcon = loader.loadModel('phase_4/models/minigames/moneyIcon') + self.treasureModelList.append(moneyIcon) + legalIcon = loader.loadModel('phase_4/models/minigames/legalIcon') + self.treasureModelList.append(legalIcon) + corpIcon = loader.loadModel('phase_4/models/minigames/corpIcon') + self.treasureModelList.append(corpIcon) + self.particleGlow = loader.loadModel('phase_4/models/minigames/particleGlow') + self.blockTypes = [] + for i in xrange(4): + blockType = loader.loadModel('phase_4/models/minigames/toonblitz_game_block0' + str(i)) + self.blockTypes.append(blockType) + + self.stomper = loader.loadModel('phase_4/models/minigames/toonblitz_game_stomper') + plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, -50))) + dropPlane = CollisionNode('dropPlane') + dropPlane.addSolid(plane) + dropPlane.setCollideMask(ToontownGlobals.FloorBitmask) + self.world.attachNewNode(dropPlane) + self.gameMusic = base.loadMusic('phase_4/audio/bgm/MG_TwoDGame.ogg') + self.treasureGrabSound = loader.loadSfx('phase_4/audio/sfx/SZ_DD_treasure.ogg') + self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg') + self.soundJump = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_jump.ogg') + self.fallSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_fall.ogg') + self.watergunSound = base.loadSfx('phase_4/audio/sfx/AA_squirt_seltzer_miss.ogg') + self.splashSound = base.loadSfx('phase_4/audio/sfx/Seltzer_squirt_2dgame_hit.ogg') + self.threeSparkles = loader.loadSfx('phase_4/audio/sfx/threeSparkles.ogg') + self.sparkleSound = loader.loadSfx('phase_4/audio/sfx/sparkly.ogg') + self.headCollideSound = loader.loadSfx('phase_3.5/audio/sfx/AV_collision.ogg') + self.faceStartPos = Vec3(-0.8, 0, -0.87) + self.faceEndPos = Vec3(0.8, 0, -0.87) + self.aspect2dRoot = aspect2d.attachNewNode('TwoDGuiAspect2dRoot') + self.aspect2dRoot.setDepthWrite(1) + self.cardMaker = CardMaker('card') + self.cardMaker.reset() + self.cardMaker.setName('ProgressLine') + self.cardMaker.setFrame(-0.5, 0.5, -0.5, 0.5) + self.progressLine = self.aspect2dRoot.attachNewNode(self.cardMaker.generate()) + self.progressLine.setScale(self.faceEndPos[0] - self.faceStartPos[0], 1, 0.01) + self.progressLine.setPos(0, 0, self.faceStartPos[2]) + self.cardMaker.setName('RaceProgressLineHash') + for n in xrange(ToonBlitzGlobals.NumSections[self.game.getSafezoneId()] + 1): + hash = self.aspect2dRoot.attachNewNode(self.cardMaker.generate()) + hash.setScale(self.progressLine.getScale()[2], 1, self.progressLine.getScale()[2] * 5) + t = float(n) / ToonBlitzGlobals.NumSections[self.game.getSafezoneId()] + hash.setPos(self.faceStartPos[0] * (1 - t) + self.faceEndPos[0] * t, self.faceStartPos[1], self.faceStartPos[2]) + + def destroy(self): + while len(self.blockTypes): + blockType = self.blockTypes[0] + self.blockTypes.remove(blockType) + del blockType + + self.blockTypes = None + while len(self.treasureModelList): + treasureModel = self.treasureModelList[0] + self.treasureModelList.remove(treasureModel) + del treasureModel + + self.treasureModelList = None + self.startingWall.removeNode() + del self.startingWall + self.startingPipe.removeNode() + del self.startingPipe + self.exitElevator.removeNode() + del self.exitElevator + self.stomper.removeNode() + del self.stomper + self.arrow.removeNode() + del self.arrow + self.sprayProp.removeNode() + del self.sprayProp + self.aspect2dRoot.removeNode() + del self.aspect2dRoot + self.world.removeNode() + del self.world + del self.gameMusic + del self.treasureGrabSound + del self.sndOof + del self.soundJump + del self.fallSound + del self.watergunSound + del self.splashSound + del self.threeSparkles + del self.sparkleSound + del self.headCollideSound + self.game = None + return + + def onstage(self): + self.world.reparentTo(render) + base.playMusic(self.gameMusic, looping=1, volume=0.9) + + def offstage(self): + self.world.hide() + self.gameMusic.stop() + + def enterPlay(self): + pass + + def exitPlay(self): + pass + + def enterPause(self): + pass + + def exitPause(self): + pass + + def playJumpSound(self): + base.localAvatar.soundRun.stop() + base.playSfx(self.soundJump, looping=0) + + def playWatergunSound(self): + self.watergunSound.stop() + base.playSfx(self.watergunSound, looping=0) + + def playSplashSound(self): + self.splashSound.stop() + base.playSfx(self.splashSound, looping=0) + + def playHeadCollideSound(self): + self.headCollideSound.stop() + base.playSfx(self.headCollideSound, looping=0) diff --git a/toontown/minigame/ToonBlitzGlobals.py b/toontown/minigame/ToonBlitzGlobals.py new file mode 100755 index 00000000..765082fe --- /dev/null +++ b/toontown/minigame/ToonBlitzGlobals.py @@ -0,0 +1,778 @@ +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import BitMask32 +ShowScoresDuration = 4.0 +EndlessGame = config.GetBool('endless-2d-game', 0) +ScoreToJellyBeansMultiplier = 5 +ScoreGainPerTreasure = 1 +ToonStartingPosition = (-39, 0, 13.59) +CameraStartingPosition = (-28, + -53, + 17.3, + 0, + 0, + 0) +GameDuration = {ToontownGlobals.ToontownCentral: 150, + ToontownGlobals.DonaldsDock: 145, + ToontownGlobals.DaisyGardens: 140, + ToontownGlobals.MinniesMelodyland: 135, + ToontownGlobals.TheBrrrgh: 130, + ToontownGlobals.DonaldsDreamland: 125} +BaseBonusOnCompletion = {ToontownGlobals.ToontownCentral: 15, + ToontownGlobals.DonaldsDock: 17, + ToontownGlobals.DaisyGardens: 19, + ToontownGlobals.MinniesMelodyland: 21, + ToontownGlobals.TheBrrrgh: 23, + ToontownGlobals.DonaldsDreamland: 25} +BonusPerSecondLeft = 0.8 +ScoreLossPerEnemyCollision = {ToontownGlobals.ToontownCentral: -1, + ToontownGlobals.DonaldsDock: -1, + ToontownGlobals.DaisyGardens: -1, + ToontownGlobals.MinniesMelodyland: -1, + ToontownGlobals.TheBrrrgh: -1, + ToontownGlobals.DonaldsDreamland: -1} +ScoreLossPerFallDown = {ToontownGlobals.ToontownCentral: -0, + ToontownGlobals.DonaldsDock: -0, + ToontownGlobals.DaisyGardens: -0, + ToontownGlobals.MinniesMelodyland: -0, + ToontownGlobals.TheBrrrgh: -0, + ToontownGlobals.DonaldsDreamland: -0} +ScoreLossPerStomperSquish = {ToontownGlobals.ToontownCentral: -1, + ToontownGlobals.DonaldsDock: -1, + ToontownGlobals.DaisyGardens: -1, + ToontownGlobals.MinniesMelodyland: -1, + ToontownGlobals.TheBrrrgh: -1, + ToontownGlobals.DonaldsDreamland: -1} +SectionWeights = {ToontownGlobals.ToontownCentral: ((0, 25), + (1, 25), + (2, 25), + (3, 15), + (4, 10), + (5, 0)), + ToontownGlobals.DonaldsDock: ((0, 15), + (1, 25), + (2, 25), + (3, 15), + (4, 10), + (5, 10)), + ToontownGlobals.DaisyGardens: ((0, 15), + (1, 15), + (2, 25), + (3, 25), + (4, 10), + (5, 10)), + ToontownGlobals.MinniesMelodyland: ((0, 10), + (1, 10), + (2, 25), + (3, 25), + (4, 15), + (5, 15)), + ToontownGlobals.TheBrrrgh: ((0, 10), + (1, 10), + (2, 15), + (3, 25), + (4, 25), + (5, 15)), + ToontownGlobals.DonaldsDreamland: ((0, 10), + (1, 10), + (2, 15), + (3, 15), + (4, 25), + (5, 25))} +NumSections = {ToontownGlobals.ToontownCentral: 5, + ToontownGlobals.DonaldsDock: 5, + ToontownGlobals.DaisyGardens: 5, + ToontownGlobals.MinniesMelodyland: 5, + ToontownGlobals.TheBrrrgh: 5, + ToontownGlobals.DonaldsDreamland: 5} +PercentMaxEnemies = {ToontownGlobals.ToontownCentral: 50, + ToontownGlobals.DonaldsDock: 60, + ToontownGlobals.DaisyGardens: 70, + ToontownGlobals.MinniesMelodyland: 80, + ToontownGlobals.TheBrrrgh: 90, + ToontownGlobals.DonaldsDreamland: 100} +PercentMaxTreasures = {ToontownGlobals.ToontownCentral: 100, + ToontownGlobals.DonaldsDock: 100, + ToontownGlobals.DaisyGardens: 100, + ToontownGlobals.MinniesMelodyland: 100, + ToontownGlobals.TheBrrrgh: 100, + ToontownGlobals.DonaldsDreamland: 100} +PercentMaxSpawnPoints = {ToontownGlobals.ToontownCentral: 100, + ToontownGlobals.DonaldsDock: 90, + ToontownGlobals.DaisyGardens: 80, + ToontownGlobals.MinniesMelodyland: 70, + ToontownGlobals.TheBrrrgh: 60, + ToontownGlobals.DonaldsDreamland: 50} +PercentMaxStompers = {ToontownGlobals.ToontownCentral: 50, + ToontownGlobals.DonaldsDock: 60, + ToontownGlobals.DaisyGardens: 70, + ToontownGlobals.MinniesMelodyland: 80, + ToontownGlobals.TheBrrrgh: 90, + ToontownGlobals.DonaldsDreamland: 100} +TreasureValueProbability = {1: 4, + 2: 3, + 3: 2, + 4: 1} +BLOCK_H24 = 'BlockH24' +BLOCK_V24F = 'BlockV24F' +BLOCK_V24B = 'BlockV24B' +BLOCK_H12 = 'BlockH12' +BLOCK_V12F = 'BlockV12F' +BLOCK_V12B = 'BlockV12B' +BLOCK_H6 = 'BlockH6' +BLOCK_V6F = 'BlockV6F' +BLOCK_V6B = 'BlockV6B' +BLOCK_H3 = 'BlockH3' +BLOCK_V3F = 'BlockV3F' +BLOCK_V3B = 'BlockV3B' +BlockTypes = {BLOCK_H24: ('00', + (0, 0, 0), + (0, 0, 0), + (1, 1, 1)), + BLOCK_V24F: ('00', + (0, 0, 0), + (0, 0, 270), + (1, 1, 1)), + BLOCK_V24B: ('00', + (0, 0, 0), + (0, 0, 90), + (1, 1, 1)), + BLOCK_H12: ('01', + (0, 0, 0), + (0, 0, 0), + (1, 1, 1)), + BLOCK_V12F: ('01', + (0, 0, 0), + (0, 0, 270), + (1, 1, 1)), + BLOCK_V12B: ('01', + (0, 0, 0), + (0, 0, 90), + (1, 1, 1)), + BLOCK_H6: ('02', + (0, 0, 0), + (0, 0, 0), + (1, 1, 1)), + BLOCK_V6F: ('02', + (0, 0, 0), + (0, 0, 270), + (0.86, 1, 1)), + BLOCK_V6B: ('02', + (0, 0, 0), + (0, 0, 90), + (0.86, 1, 1)), + BLOCK_H3: ('03', + (0, 0, 0), + (0, 0, 0), + (1, 1, 1)), + BLOCK_V3F: ('03', + (0, 0, 0), + (0, 0, 270), + (1, 1, 1)), + BLOCK_V3B: ('03', + (0, 0, 0), + (0, 0, 90), + (1, 1, 1))} +BlocksList = [] +SpawnPointList = [] +DamagePerBullet = 25 +EnemyBaseHealth = 25 +EnemyHealthMultiplier = {'f': 1, + 'p': 1, + 'ym': 1, + 'mm': 1, + 'bf': 1, + 'b': 1, + 'dt': 1, + 'sc': 1, + 'pp': 1, + 'tw': 1, + 'cc': 1, + 'tm': 1} +NumEnemies = 2 +EnemyList = [] +NumTreasures = 5 +TreasureList = [] +BlockListStart = [[BLOCK_H24, [(0, 0, 12)]]] +BlockListEnd = [[BLOCK_H24, [(0, 0, 12)]], [BLOCK_H24, [(24, 0, 12)]], [BLOCK_H24, [(48, 0, 12)]]] +SpawnPointListEnd = [[(45, 0, 16)]] +BlockList0 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H24, [(22.4, 0, 12)]], + [BLOCK_H24, [(42.9, 0, 12)]], + [BLOCK_H24, [(30, 0, 18.75)]], + [BLOCK_H24, [(58, 0, 18.75)]], + [BLOCK_H24, [(0, 0, 18.75)]]] +EnemyList0 = [['tm', [(35, 0, 12.65), (30, 0, 12.65), 2]], ['tm', [(24, 0, 12.65), (24, 0, 30.0), 2]]] +TreasureList0 = [[(16, 0, 16)], + [(30, 0, 23)], + [(40, 0, 23)], + [(50, 0, 16)], + [(50, 0, 23)]] +SpawnPointList0 = [[(9, 0, 16), (4, 0, 16)], [(80, 0, 16)]] +StomperTypes = {1: (1, + 2, + [0, -4], + [1, 6.75], + 3), + 2: (1, + 5, + [0.6, -0.34], + [0.2, 1.5], + 2), + 3: (1, + 2, + [0, -9.85], + [1, 15], + 5)} +BlockList1 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H12, [(16, 0, 18.75)]], + [BLOCK_H12, [(36, 0, 12)]], + [BLOCK_H12, [(64, 0, 12)]], + [BLOCK_H24, [(19, 0, 5.25)]], + [BLOCK_H24, [(43, 0, 5.25)]], + [BLOCK_H24, [(67, 0, 5.25)]], + [BLOCK_H12, [(82, 0, 18.75)]]] +TreasureList1 = [[(8, 0, 27.25)], + [(36, 0, 27.25)], + [(20, 0, 7.25)], + [(32, 0, 7.25)], + [(56, 0, 22.5)], + [(56, 0, 7.25)], + [(80, 0, 7.25)], + [(90, 0, 7.25)], + [(74, 0, 27.25)], + [(102, 0, 27.25)]] +SpawnPointList1 = [[(2, 0, 16)], [(73, 0, 16)]] +EnemyList1 = [['f', [(20, 0, 6), (50, 0, 6), 3.5]], + ['bf', [(50, 0, 6), (20, 0, 6), 3.5]], + ['f', [(62, 0, 6), (90, 0, 6), 3.5]], + ['bf', [(90, 0, 6), (62, 0, 6), 3.5]], + ['dt', [(52, 0, 6), (52, 0, 28), 3.5]], + ['b', [(60, 0, 28), (60, 0, 6), 3.5]]] +BlockList2 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H12, [(16, 0, 18.75)]], + [BLOCK_H6, [(34, 0, 12)]], + [BLOCK_H12, [(25, 0, 5.25)]], + [BLOCK_H24, [(34, 0, 25.5)]], + [BLOCK_H12, [(64, 0, 18.75)]], + [BLOCK_H6, [(52, 0, 12)]], + [BLOCK_H12, [(55, 0, 5.25)]], + [BLOCK_H12, [(80, 0, 12), (100, 0, 12), 2.5]]] +TreasureList2 = [[(8, 0, 27.25)], + [(26, 0, 7.25)], + [(36, 0, 7.25)], + [(26, 0, 34)], + [(66, 0, 34)], + [(42, 0, 23)], + [(46, 0, 21)], + [(50, 0, 23)], + [(56, 0, 7.25)], + [(66, 0, 7.25)], + [(84, 0, 28)], + [(20, 0, 14)], + [(72, 0, 14)]] +SpawnPointList2 = [[(2, 0, 16)], [(72, 0, 22.75)]] +EnemyList2 = [['p', [(31, 0, 6), (31, 0, 31), 3.5]], + ['ym', [(31, 0, 31), (31, 0, 6), 3.5]], + ['bf', [(61, 0, 6), (61, 0, 31), 3.5]], + ['b', [(61, 0, 31), (61, 0, 6), 3.5]], + ['sc', [(35, 0, 26.25), (57, 0, 26.25), 3.5]], + ['tw', [(57, 0, 26.25), (35, 0, 26.25), 3.5]]] +BlockList3 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H12, [(12, 0, 5.25), (36, 0, 25.5), 3]], + [BLOCK_H12, [(48, 0, 25.5)]], + [BLOCK_H12, [(60, 0, 25.5), (84, 0, 5.25), 3]], + [BLOCK_H12, [(102, 0, 5.25)]], + [BLOCK_H12, [(120, 0, 5.25), (138, 0, 5.25), 2.5]], + [BLOCK_H12, [(156, 0, 5.25)]], + [BLOCK_H12, [(174, 0, 5.25), (174, 0, 25.5), 2.5]]] +TreasureList3 = [[(28, 0, 32)], + [(24, 0, 28)], + [(20, 0, 24)], + [(16, 0, 20)], + [(80, 0, 32)], + [(84, 0, 28)], + [(88, 0, 24)], + [(92, 0, 20)], + [(48, 0, 34)], + [(60, 0, 34)], + [(125, 0, 15)], + [(135, 0, 15)], + [(145, 0, 15)], + [(180, 0, 7.25)], + [(168, 0, 34)], + [(192, 0, 34)]] +SpawnPointList3 = [[(2, 0, 16)], + [(56, 0, 29.5)], + [(108, 0, 9.25)], + [(162, 0, 9.25)]] +EnemyList3 = [['p', [(31, 0, 6), (31, 0, 31), 3.5]], + ['ym', [(31, 0, 31), (31, 0, 6), 3.5]], + ['bf', [(61, 0, 6), (61, 0, 31), 3.5]], + ['b', [(61, 0, 31), (61, 0, 6), 3.5]], + ['sc', [(35, 0, 26.25), (57, 0, 26.25), 3.5]], + ['tw', [(57, 0, 26.25), (35, 0, 26.25), 3.5]]] +BlockList5 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H24, [(24, 0, 12)]], + [BLOCK_H24, [(48, 0, 12)]], + [BLOCK_H6, [(12, 0, 18.75)]], + [BLOCK_H6, [(18, 0, 25.5)]], + [BLOCK_H6, [(24, 0, 18.75)]], + [BLOCK_H6, [(44, 0, 18.75)]], + [BLOCK_H6, [(50, 0, 25.5)]], + [BLOCK_H6, [(56, 0, 18.75)]]] +TreasureList5 = [[(15, 0, 20.75)], + [(21, 0, 27.5)], + [(27, 0, 20.75)], + [(47, 0, 20.75)], + [(53, 0, 27.5)], + [(59, 0, 20.75)]] +SpawnPointList5 = [[(2, 0, 16)]] +BlockList6 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H24, [(0, 0, 5.25)]], + [BLOCK_H24, [(24, 0, 5.25)]], + [BLOCK_H24, [(48, 0, 5.25)]], + [BLOCK_H12, [(72, 0, 5.25)]], + [BLOCK_H12, [(30, 0, 12)]], + [BLOCK_H12, [(48, 0, 12)]], + [BLOCK_H24, [(66, 0, 12)]]] +TreasureList6 = [[(27, 0, 20.75)], + [(45, 0, 20.75)], + [(63, 0, 20.75)], + [(3, 0, 7.25)], + [(15, 0, 7.25)], + [(36, 0, 7.25)], + [(54, 0, 7.25)], + [(70, 0, 7.25)], + [(81, 0, 7.25)]] +SpawnPointList6 = [[(2, 0, 16)]] +BlockList7 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H12, [(10, 0, 18.75)]], + [BLOCK_H12, [(20, 0, 25.5)]], + [BLOCK_H12, [(30, 0, 18.75)]], + [BLOCK_H24, [(40, 0, 12)]], + [BLOCK_H24, [(14, 0, 5.25)]], + [BLOCK_H3, [(11, 0, 5.25)]], + [BLOCK_H3, [(38, 0, 5.25)]], + [BLOCK_H12, [(62, 0, 18.75)]], + [BLOCK_H12, [(72, 0, 12)]], + [BLOCK_H12, [(82, 0, 5.25)]], + [BLOCK_H12, [(92, 0, 12)]], + [BLOCK_H12, [(102, 0, 18.75)]], + [BLOCK_H12, [(112, 0, 12)]], + [BLOCK_H24, [(76, 0, 25.5)]], + [BLOCK_H3, [(73, 0, 25.5)]], + [BLOCK_H3, [(100, 0, 25.5)]]] +TreasureList7 = [[(12, 0, 34)], + [(26, 0, 34)], + [(40, 0, 34)], + [(26, 0, 23)], + [(26, 0, 12)], + [(13, 0, 7.25)], + [(40, 0, 7.25)], + [(88, 0, 34)], + [(112, 0, 34)], + [(64, 0, 34)], + [(88, 0, 7.25)], + [(88, 0, 23)]] +SpawnPointList7 = [[(2, 0, 16)]] +BlockList8 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H24, [(0, 0, 5.25)]], + [BLOCK_H12, [(16, 0, 18.75)]], + [BLOCK_H24, [(32, 0, 25.5)]], + [BLOCK_H12, [(64, 0, 25.5)]], + [BLOCK_H12, [(84, 0, 25.5)]], + [BLOCK_H6, [(104, 0, 25.5)]], + [BLOCK_H6, [(118, 0, 25.5)]], + [BLOCK_H3, [(132, 0, 25.5)]], + [BLOCK_H3, [(143, 0, 25.5)]], + [BLOCK_H3, [(154, 0, 25.5)]], + [BLOCK_H24, [(32, 0, 5.25)]], + [BLOCK_H12, [(64, 0, 5.25)]], + [BLOCK_H12, [(84, 0, 5.25)]], + [BLOCK_H6, [(104, 0, 5.25)]], + [BLOCK_H6, [(118, 0, 5.25)]], + [BLOCK_H3, [(132, 0, 5.25)]], + [BLOCK_H3, [(143, 0, 5.25)]], + [BLOCK_H3, [(154, 0, 5.25)]], + [BLOCK_H3, [(161, 0, 18.75)]], + [BLOCK_H6, [(160, 0, 12)]]] +TreasureList8 = [[(1, 0, 7.25)], + [(4, 0, 7.25)], + [(26, 0, 34)], + [(10, 0, 28)], + [(154, 0, 20.75)], + [(60, 0, 34)], + [(80, 0, 34)], + [(100, 0, 34)], + [(114, 0, 34)], + [(128, 0, 34)], + [(139, 0, 34)], + [(150, 0, 34)], + [(60, 0, 13.75)], + [(80, 0, 13.75)], + [(100, 0, 13.75)], + [(114, 0, 13.75)], + [(128, 0, 13.75)], + [(139, 0, 13.75)], + [(150, 0, 13.75)]] +SpawnPointList8 = [[(2, 0, 16)], [(128, 0, 29.5), (121, 0, 29.5)], [(128, 0, 9.25), (121, 0, 9.25)]] +BlockList9 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H12, [(28, 0, 12), (50, 0, 12), 3]], + [BLOCK_H12, [(66, 0, 12)]], + [BLOCK_H12, [(82, 0, 12), (104, 0, 12), 3]], + [BLOCK_H12, [(120, 0, 12)]], + [BLOCK_H12, [(136, 0, 25.5), (136, 0, 5.25), 3]], + [BLOCK_H12, [(152, 0, 25.5)]], + [BLOCK_H12, [(152, 0, 5.25)]], + [BLOCK_H12, [(168, 0, 25.5), (168, 0, 5.25), 3]], + [BLOCK_H12, [(184, 0, 12)]]] +TreasureList9 = [[(36, 0, 23)], + [(45, 0, 23)], + [(54, 0, 23)], + [(90, 0, 23)], + [(99, 0, 23)], + [(108, 0, 23)], + [(142, 0, 35)], + [(142, 0, 8)], + [(130, 0, 30)], + [(158, 0, 7.25)], + [(158, 0, 27.5)], + [(174, 0, 35)], + [(174, 0, 8)], + [(186, 0, 30)]] +SpawnPointList9 = [[(2, 0, 16)], [(126, 0, 16)]] +BlockList10 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H12, [(14, 0, 12), (40, 0, 12), 3.5]], + [BLOCK_H6, [(40, 0, 25.5)]], + [BLOCK_H6, [(40, 0, 5.25)]], + [BLOCK_H12, [(54, 0, 32), (54, 0, -1.5), 3.5]], + [BLOCK_H6, [(74, 0, 25.5)]], + [BLOCK_H6, [(74, 0, 5.25)]], + [BLOCK_H12, [(82, 0, 5.25), (114, 0, 5.25), 3.7]], + [BLOCK_H6, [(130, 0, 5.25)]], + [BLOCK_H6, [(130, 0, 25.5)]], + [BLOCK_H12, [(144, 0, 32), (144, 0, -1.5), 3.5]], + [BLOCK_H6, [(164, 0, 25.5)]], + [BLOCK_H6, [(164, 0, 5.25)]], + [BLOCK_H12, [(202, 0, 25.5), (172, 0, 25.5), 4]]] +TreasureList10 = [[(43, 0, 27.5)], + [(43, 0, 7.25)], + [(32, 0, 32)], + [(77, 0, 27.5)], + [(77, 0, 7.25)], + [(88, 0, 32)], + [(97, 0, 15)], + [(105, 0, 15)], + [(113, 0, 15)], + [(133, 0, 27.5)], + [(133, 0, 7.25)], + [(122, 0, 32)], + [(167, 0, 27.5)], + [(167, 0, 7.25)], + [(173, 0, 14)]] +SpawnPointList10 = [[(2, 0, 16)], [(133, 0, 9.25)], [(77, 0, 29.5)]] +BlockList11 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H24, [(24, 0, 12)]], + [BLOCK_H24, [(24, 0, 18.75)]], + [BLOCK_H24, [(48, 0, 12)]], + [BLOCK_H6, [(12, 0, 25.5)]], + [BLOCK_H6, [(54, 0, 25.5)]], + [BLOCK_H24, [(72, 0, 5.25)]], + [BLOCK_H12, [(78, 0, 18.75)]], + [BLOCK_H24, [(96, 0, 12)]], + [BLOCK_H24, [(120, 0, 18.75)]], + [BLOCK_H6, [(110, 0, 25.5)]], + [BLOCK_H6, [(148, 0, 25.5)]]] +TreasureList11 = [[(4, 0, 34)], + [(26, 0, 34)], + [(46, 0, 34)], + [(68, 0, 34)], + [(74, 0, 7.25)], + [(94, 0, 7.25)], + [(102, 0, 34)], + [(124, 0, 34)], + [(140, 0, 34)], + [(162, 0, 34)]] +EnemyList11 = [['cc', [(26, 0, 12.75), (46, 0, 12.75), 3.5]], ['tm', [(74, 0, 6), (94, 0, 6), 3.5]], ['sc', [(122, 0, 19.5), (142, 0, 19.5), 3.5]]] +SpawnPointList11 = [[(2, 0, 16)], [(70, 0, 16)]] +BlockList12 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H24, [(9, 0, 5.25)]], + [BLOCK_H24, [(33, 0, 5.25)]], + [BLOCK_H24, [(32, 0, 12)]], + [BLOCK_H24, [(56, 0, 12)]], + [BLOCK_H24, [(80, 0, 12)]], + [BLOCK_H12, [(60, 0, 18.75)]], + [BLOCK_H24, [(88, 0, 25.5)]], + [BLOCK_V12B, [(103.2, 0, 24.72)]]] +TreasureList12 = [[(12, 0, 7.25)], + [(55, 0, 7.25)], + [(52, 0, 7.25)], + [(36, 0, 34)], + [(64, 0, 34)], + [(68, 0, 34)], + [(100, 0, 14)], + [(95, 0, 22)], + [(90, 0, 14)]] +SpawnPointList12 = [[(2, 0, 16)]] +StomperList12 = [[1, (13, 0, 20.75), 2], [1, (50, 0, 20.75), 2], [1, (82, 0, 20.75), 1.5]] +BlockList13 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H24, [(24, 0, 12)]], + [BLOCK_H12, [(48, 0, 12)]], + [BLOCK_H24, [(22, 0, 25.5)]], + [BLOCK_H24, [(64, 0, 18.75)]], + [BLOCK_H24, [(96, 0, 18.75)]], + [BLOCK_H24, [(126, 0, 12)]], + [BLOCK_H24, [(150, 0, 12)]], + [BLOCK_H24, [(174, 0, 12)]], + [BLOCK_H24, [(148, 0, 25.5)]], + [BLOCK_H6, [(190, 0, 18.75)]]] +TreasureList13 = [[(2, 0, 34)], + [(66, 0, 34)], + [(91, 0, 29)], + [(12, 0, 23)], + [(20, 0, 23)], + [(48, 0, 23)], + [(56, 0, 23)], + [(72, 0, 29.75)], + [(80, 0, 29.75)], + [(104, 0, 29.75)], + [(112, 0, 29.75)], + [(138, 0, 23)], + [(146, 0, 23)], + [(174, 0, 23)], + [(182, 0, 23)], + [(128, 0, 34)], + [(192, 0, 34)]] +SpawnPointList13 = [[(2, 0, 16)], [(67, 0, 22.75)], [(130, 0, 16)]] +EnemyList13 = [['cc', [(20, 0, 12.75), (48, 0, 12.75), 3.5]], + ['cc', [(48, 0, 12.75), (20, 0, 12.75), 3.5]], + ['sc', [(92, 0, 31), (92, 0, -1), 3.5]], + ['sc', [(92, 0, -1), (92, 0, 31), 3.5]], + ['cc', [(146, 0, 12.75), (174, 0, 12.75), 3.5]], + ['cc', [(174, 0, 12.75), (146, 0, 12.75), 3.5]]] +StomperList13 = [[1, (16, 0, 20.75), 2], + [1, (52, 0, 20.75), 2], + [1, (76, 0, 27.5), 2], + [1, (108, 0, 27.5), 2], + [1, (142, 0, 20.75), 2], + [1, (178, 0, 20.75), 2]] +BlockList4 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H24, [(0, 0, 0)]], + [BLOCK_H24, [(0, 0, 0)]], + [BLOCK_H24, [(0, 0, 0)]], + [BLOCK_H24, [(0, 0, 0)]], + [BLOCK_H24, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H3, [(0, 0, 0)]], + [BLOCK_H3, [(0, 0, 0)]], + [BLOCK_H3, [(0, 0, 0)]], + [BLOCK_H3, [(0, 0, 0)]], + [BLOCK_H3, [(0, 0, 0)]], + [BLOCK_V12F, [(0, 0, 0)]], + [BLOCK_V12F, [(0, 0, 0)]], + [BLOCK_V12B, [(0, 0, 0)]], + [BLOCK_V12B, [(0, 0, 0)]], + [BLOCK_V6F, [(0, 0, 0)]], + [BLOCK_V6F, [(0, 0, 0)]], + [BLOCK_V6B, [(0, 0, 0)]], + [BLOCK_V6B, [(0, 0, 0)]]] +TreasureList4 = [[(-3, 0, 25)], + [(-3, 0, 25)], + [(-3, 0, 25)], + [(-3, 0, 25)], + [(-3, 0, 25)]] +SpawnPointList4 = [[(9, 0, 16)]] +EnemyList4 = [['cc', [(26, 0, 12.75), (46, 0, 12.75), 3.5]], ['tm', [(74, 0, 6), (94, 0, 6), 3.5]], ['sc', [(122, 0, 19.5), (142, 0, 19.5), 3.5]]] +StomperList4 = [[1, (12, 0, 20.75), 2], [1, (50, 0, 20.75), 2], [1, (82, 0, 20.75), 2]] +SectionTypes = {'end': (0, + 24, + BlockListEnd, + None, + None, + SpawnPointListEnd, + None, + [], + [], + [1, 1], + []), + 1: (4, + 100, + BlockList1, + EnemyList1, + TreasureList1, + SpawnPointList1, + None, + [3, 6], + [10, 10], + [1, 2], + []), + 2: (4, + 118, + BlockList2, + EnemyList2, + TreasureList2, + SpawnPointList2, + None, + [3, 6], + [13, 13], + [1, 2], + []), + 3: (4, + 192, + BlockList3, + None, + TreasureList3, + SpawnPointList3, + None, + [], + [16, 16], + [1, 4], + []), + 5: (0, + 72, + BlockList5, + None, + TreasureList5, + SpawnPointList5, + None, + [], + [6, 6], + [1, 1], + []), + 6: (0, + 90, + BlockList6, + None, + TreasureList6, + SpawnPointList6, + None, + [], + [9, 9], + [1, 1], + []), + 7: (1, + 124, + BlockList7, + None, + TreasureList7, + SpawnPointList7, + None, + [], + [12, 12], + [1, 1], + []), + 8: (5, + 174, + BlockList8, + None, + TreasureList8, + SpawnPointList8, + None, + [], + [19, 19], + [1, 3], + []), + 9: (3, + 204, + BlockList9, + None, + TreasureList9, + SpawnPointList9, + None, + [], + [14, 14], + [1, 2], + []), + 10: (4, + 214, + BlockList10, + None, + TreasureList10, + SpawnPointList10, + None, + [], + [15, 15], + [1, 3], + []), + 11: (2, + 164, + BlockList11, + EnemyList11, + TreasureList11, + SpawnPointList11, + None, + [3, 3], + [10, 10], + [1, 2], + []), + 12: (3, + 120, + BlockList12, + None, + TreasureList12, + SpawnPointList12, + StomperList12, + [], + [10, 10], + [1, 1], + [3, 3]), + 13: (3, + 198, + BlockList13, + EnemyList13, + TreasureList13, + SpawnPointList13, + StomperList13, + [3, 6], + [17, 17], + [1, 3], + [3, 6]), + 4: (0, + 1000, + BlockList4, + None, + TreasureList4, + SpawnPointList4, + None, + [25, 25], + [10, 10], + [10, 10], + [10, 10]), + 0: (0, + 75, + BlockList0, + None, + TreasureList0, + SpawnPointList0, + None, + [2, 5], + [5, 10], + [2, 5], + [])} +SectionsPool = [1, + 2, + 3, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13] diff --git a/toontown/minigame/Trajectory.py b/toontown/minigame/Trajectory.py new file mode 100755 index 00000000..d135f0c2 --- /dev/null +++ b/toontown/minigame/Trajectory.py @@ -0,0 +1,133 @@ +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * +from math import * + +class Trajectory: + notify = DirectNotifyGlobal.directNotify.newCategory('Trajectory') + gravity = 32.0 + __radius = 2.0 + + def __init__(self, startTime, startPos, startVel, gravMult = 1.0): + self.setStartTime(startTime) + self.setStartPos(startPos) + self.setStartVel(startVel) + self.setGravityMult(gravMult) + + def setStartTime(self, t): + self.__startTime = t + + def setStartPos(self, sp): + self.__startPos = sp + + def setStartVel(self, sv): + self.__startVel = sv + + def setGravityMult(self, mult): + self.__zAcc = mult * -Trajectory.gravity + + def getStartTime(self): + return self.__startTime + + def __str__(self): + return 'startTime: %s, startPos: %s, startVel: %s, zAcc: %s' % (self.__startTime, + repr(self.__startPos), + repr(self.__startVel), + self.__zAcc) + + def __calcTimeOfHighestPoint(self): + t = -self.__startVel[2] / self.__zAcc + if t < 0: + t = 0 + return t + self.__startTime + + def calcTimeOfImpactOnPlane(self, height = 0): + a = self.__zAcc * 0.5 + b = self.__startVel[2] + c = self.__startPos[2] - height + D = b * b - 4.0 * a * c + if D < 0: + return -1.0 + elif D == 0: + t = -b / (2.0 * a) + else: + t = (-b - sqrt(D)) / (2.0 * a) + if t < 0: + return -1.0 + return t + self.__startTime + + def calcZ(self, t): + tt = t - self.__startTime + return self.__startPos[2] + self.__startVel[2] * tt + 0.5 * self.__zAcc * tt * tt + + def __reachesHeight(self, height): + if self.calcZ(self.__calcTimeOfHighestPoint()) < height: + return 0 + return 1 + + def getPos(self, t): + tt = t - self.__startTime + return Point3(self.__startPos[0] + self.__startVel[0] * tt, self.__startPos[1] + self.__startVel[1] * tt, self.calcZ(t)) + + def getVel(self, t): + tt = t - self.__startTime + return Vec3(self.__startVel[0], self.__startVel[1], self.__startVel[2] + self.__zAcc * tt) + + def getStartTime(self): + return self.__startTime + + def checkCollisionWithGround(self, height = 0): + return self.calcTimeOfImpactOnPlane(height) + + def checkCollisionWithDisc(self, discCenter, discRadius): + if self.__reachesHeight(discCenter[2]) == 0: + return -1.0 + t_atDiscHeight = self.calcTimeOfImpactOnPlane(discCenter[2]) + if t_atDiscHeight < 0: + return -1.0 + p_atDiscHeight = self.getPos(t_atDiscHeight) + offset_x = p_atDiscHeight[0] - discCenter[0] + offset_y = p_atDiscHeight[1] - discCenter[1] + offset_from_center_SQUARED = offset_x * offset_x + offset_y * offset_y + max_offset = discRadius + max_offset_SQUARED = max_offset * max_offset + if offset_from_center_SQUARED < max_offset_SQUARED: + return t_atDiscHeight + else: + return -1.0 + + def calcEnterAndLeaveCylinderXY(self, cylBottomCenter, cylRadius): + v = Vec2(cylBottomCenter[0], cylBottomCenter[1]) + o = Vec2(self.__startPos[0], self.__startPos[1]) + d = Vec2(self.__startVel[0], self.__startVel[1]) + d.normalize() + b = d.dot(o - v) + c = (o - v).dot(o - v) - cylRadius * cylRadius + bsmc = b * b - c + if bsmc <= 0.0: + return (-1.0, -1.0) + sqrt_bsmc = sqrt(bsmc) + t1 = -b - sqrt_bsmc + t2 = -b + sqrt_bsmc + if t1 > t2: + self.notify.debug('calcEnterAndLeaveCylinderXY: t1 > t2??') + mag = Vec2(self.__startVel[0], self.__startVel[1]).length() + t1 = t1 / mag + t2 = t2 / mag + return (t1 + self.__startTime, t2 + self.__startTime) + + def checkCollisionWithCylinderSides(self, cylBottomCenter, cylRadius, cylHeight): + if self.__reachesHeight(cylBottomCenter[2]) == 0: + return -1.0 + t1, t2 = self.calcEnterAndLeaveCylinderXY(cylBottomCenter, cylRadius) + p1 = self.getPos(t1) + p2 = self.getPos(t2) + cylTopHeight = cylBottomCenter[2] + cylHeight + if p1[2] > cylTopHeight and p2[2] > cylTopHeight: + return -1.0 + if p1[2] < cylTopHeight and p1[2] > cylBottomCenter[2]: + if t1 > self.__startTime: + return t1 + return -1.0 + + def checkCollisionWithProjectile(self, projectile): + return -1.0 diff --git a/toontown/minigame/TreasureScorePanel.py b/toontown/minigame/TreasureScorePanel.py new file mode 100755 index 00000000..84fd42cb --- /dev/null +++ b/toontown/minigame/TreasureScorePanel.py @@ -0,0 +1,28 @@ +from direct.showbase.ShowBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from toontown.toon import LaffMeter +from toontown.toonbase import TTLocalizer + +class TreasureScorePanel(DirectFrame): + + def __init__(self): + DirectFrame.__init__(self, relief=None, image_color=GlobalDialogColor, image_scale=(0.24, 1.0, 0.24), image_pos=(0.0, 0.1, 0.0)) + self.score = 0 + self.scoreText = DirectLabel(self, relief=None, text=str(self.score), text_scale=0.08, pos=(0.0, 0.0, -0.09)) + self.nameText = DirectLabel(self, relief=None, text=TTLocalizer.DivingGameTreasuresRetrieved, text_scale=0.05, text_pos=(0.0, 0.06), text_wordwrap=7.5, text_shadow=(1, 1, 1, 1)) + self.show() + return + + def cleanup(self): + del self.scoreText + del self.nameText + self.destroy() + + def incrScore(self): + self.score += 1 + self.scoreText['text'] = str(self.score) + + def makeTransparent(self, alpha): + self.setTransparency(1) + self.setColorScale(1, 1, 1, alpha) diff --git a/toontown/minigame/TugOfWarGameGlobals.py b/toontown/minigame/TugOfWarGameGlobals.py new file mode 100755 index 00000000..2053f00b --- /dev/null +++ b/toontown/minigame/TugOfWarGameGlobals.py @@ -0,0 +1,18 @@ +GAME_DURATION = 40.0 +TIME_BONUS_MIN = 2 +TIME_BONUS_MAX = 5 +TIME_BONUS_RANGE = 3.0 +SEND_UPDATE = 0.2 +TOW_WIN = 0 +TOW_TIE = 1 +TOW_LOSS = 2 +TOON_VS_TOON = 0 +TOON_VS_COG = 1 +WAIT_FOR_CLIENTS_TIMEOUT = 20 +TUG_TIMEOUT = 45 +WAIT_FOR_GO_TIMEOUT = 15 +WIN_JELLYBEANS = 15 +LOSS_JELLYBEANS = 4 +TIE_WIN_JELLYBEANS = 12 +TIE_LOSS_JELLYBEANS = 8 +TIE_JELLYBEANS = 5 diff --git a/toontown/minigame/TwoDBattleMgr.py b/toontown/minigame/TwoDBattleMgr.py new file mode 100755 index 00000000..79372371 --- /dev/null +++ b/toontown/minigame/TwoDBattleMgr.py @@ -0,0 +1,188 @@ +from panda3d.core import * +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToonBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import * +from toontown.battle import MovieUtil +import math + +class TwoDBattleMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDBattleMgr') + + def __init__(self, game, toon): + self.game = game + self.toon = toon + self.waterBulletIval = None + self.shootTrack = None + self.showCollSpheres = False + self.WATER_SPRAY_COLOR = Point4(1, 1, 1, 1) + self.WATER_BULLET_SCALE = 0.2 + self.SHOOT_DISTANCE = 10 + self.WATER_BULLET_START_POINT = Point3(0, 1, 3) + self.WATER_BULLET_END_POINT = Point3(0, self.WATER_BULLET_START_POINT.getY() + self.SHOOT_DISTANCE, self.WATER_BULLET_START_POINT.getZ()) + self.WATER_BULLET_HIDE_POINT = Point3(0, 0, 1.5) + self.sprayProp = self.game.assetMgr.sprayProp.copyTo(self.game.assetMgr.world) + self.setupPistol() + if self.toon == base.localAvatar: + self.createShootCollision() + return + + def destroy(self): + if self.toon == base.localAvatar: + if self.waterBulletIval: + self.waterBulletIval.finish() + del self.waterBulletIval + self.waterBulletIval = None + self.ignore('enter' + self.collSphereName) + base.localAvatar.controlManager.currentControls.cTrav.removeCollider(self.waterBullet) + self.waterBullet.removeNode() + del self.waterBullet + self.hand_jointpath0.removeNode() + MovieUtil.removeProp(self.pistol) + if self.shootTrack != None: + self.shootTrack.finish() + self.shootTrack = None + self.game = None + self.toon = None + return + + def start(self): + pass + + def stop(self): + pass + + def setupPistol(self): + self.pistol = globalPropPool.getProp('water-gun') + hands = self.toon.getRightHands() + self.hand_jointpath0 = hands[0].attachNewNode('handJoint0-path') + pistolPos = Point3(0.28, 0.1, 0.08) + pistolHpr = VBase3(85.6, -4.44, 94.43) + MovieUtil.showProp(self.pistol, self.hand_jointpath0, pistolPos, pistolHpr) + + def shoot(self): + if not self.shootTrack: + self.shootTrack = Parallel(self.getToonShootTrack(), self.getSprayTrack()) + if self.toon == base.localAvatar: + self.shootTrack.append(Func(self.game.assetMgr.playWatergunSound)) + self.shootTrack.append(self.getWaterBulletIval()) + self.shootTrack.start() + return + elif self.shootTrack.isStopped(): + self.shootTrack = Parallel(self.getToonShootTrack(), self.getSprayTrack()) + if self.toon == base.localAvatar: + self.shootTrack.append(Func(self.game.assetMgr.playWatergunSound)) + self.shootTrack.append(self.getWaterBulletIval()) + self.shootTrack.start() + + def createShootCollision(self): + self.notify.debug('entering createShootCollision') + collSphere = CollisionSphere(0, 0, 0, 1) + collSphere.setTangible(0) + self.collSphereName = self.game.uniqueName('waterBullet') + collNode = CollisionNode(self.collSphereName) + collNode.setFromCollideMask(ToontownGlobals.WallBitmask) + collNode.addSolid(collSphere) + self.waterBullet = base.localAvatar.attachNewNode(collNode) + self.waterBullet.setPos(self.WATER_BULLET_HIDE_POINT) + self.waterBullet.setScale(self.WATER_BULLET_SCALE) + self.waterBullet.hide() + if self.showCollSpheres: + self.waterBullet.show() + bulletEvent = CollisionHandlerEvent() + bulletEvent.addInPattern('enter%fn') + bulletEvent.addOutPattern('exit%fn') + cTrav = base.localAvatar.controlManager.currentControls.cTrav + cTrav.addCollider(self.waterBullet, bulletEvent) + self.accept('enter' + self.collSphereName, self.handleBulletCollision) + self.waterBulletIval = Sequence(Wait(0.15)) + self.waterBulletIval.append(LerpPosInterval(self.waterBullet, 0.25, pos=Point3(self.WATER_BULLET_END_POINT), startPos=Point3(self.WATER_BULLET_START_POINT), name='waterBulletMoveFront')) + self.waterBulletIval.append(Func(self.waterBullet.setPos, self.WATER_BULLET_HIDE_POINT)) + + def getToonShootTrack(self): + + def returnToLastAnim(toon): + if hasattr(toon, 'playingAnim') and toon.playingAnim: + toon.loop(toon.playingAnim) + else: + toon.loop('neutral') + + torso = self.toon.getPart('torso', '1000') + toonTrack = Sequence(ActorInterval(self.toon, 'water-gun', startFrame=48, endFrame=58, partName='torso'), ActorInterval(self.toon, 'water-gun', startFrame=107, endFrame=126, playRate=2, partName='torso'), Func(returnToLastAnim, self.toon)) + return toonTrack + + def calcSprayStartPos(self): + if self.toon: + self.toon.update(0) + joint = self.pistol.find('**/joint_nozzle') + p = joint.getPos(render) + self.origin = p + + def calcSprayEndPos(self): + if self.toon: + xDirection = -math.sin(self.toon.getH()) + else: + xDirection = -math.sin(-90) + endPos = Point3(self.origin.getX() + self.SHOOT_DISTANCE * xDirection, self.origin.getY(), self.origin.getZ()) + self.target = endPos + + def getSprayTrack(self): + dSprayScale = 0.15 + dSprayHold = 0.035 + color = self.WATER_SPRAY_COLOR + parent = render + horizScale = 1.0 + vertScale = 1.0 + + def showSpray(sprayScale, sprayRot, sprayProp, parent): + sprayRot.reparentTo(parent) + sprayRot.clearMat() + sprayScale.reparentTo(sprayRot) + sprayScale.clearMat() + sprayProp.reparentTo(sprayScale) + sprayProp.clearMat() + sprayRot.setPos(self.origin) + sprayRot.lookAt(Point3(self.target)) + + def calcTargetScale(horizScale = horizScale, vertScale = vertScale): + distance = Vec3(self.target - self.origin).length() + yScale = distance / MovieUtil.SPRAY_LEN + targetScale = Point3(yScale * horizScale, yScale, yScale * vertScale) + return targetScale + + def prepareToShrinkSpray(spray, sprayProp): + sprayProp.setPos(Point3(0.0, -MovieUtil.SPRAY_LEN, 0.0)) + spray.setPos(self.target) + + def hideSpray(spray, sprayScale, sprayRot, sprayProp, propPool): + sprayProp.detachNode() + sprayRot.removeNode() + sprayScale.removeNode() + + sprayProp = self.sprayProp + sprayScale = hidden.attachNewNode('spray-parent') + sprayRot = hidden.attachNewNode('spray-rotate') + spray = sprayRot + spray.setColor(color) + if color[3] < 1.0: + spray.setTransparency(1) + track = Sequence(Wait(0.1), Func(self.calcSprayStartPos), Func(self.calcSprayEndPos), Func(showSpray, sprayScale, sprayRot, sprayProp, parent), LerpScaleInterval(sprayScale, dSprayScale, calcTargetScale, startScale=MovieUtil.PNT3_NEARZERO), Wait(dSprayHold), Func(prepareToShrinkSpray, spray, sprayProp), LerpScaleInterval(sprayScale, dSprayScale, MovieUtil.PNT3_NEARZERO), Func(hideSpray, spray, sprayScale, sprayRot, sprayProp, globalPropPool)) + return track + + def handleBulletCollision(self, cevent): + if cevent.getIntoNodePath().getName()[:5] == 'Enemy': + sectionIndex = int(cevent.getIntoNodePath().getName()[6:8]) + enemyIndex = int(cevent.getIntoNodePath().getName()[9:11]) + messenger.send('enemyShot', [sectionIndex, enemyIndex]) + + def clearWaterBulletIval(self): + if self.waterBulletIval: + self.waterBulletIval.finish() + del self.waterBulletIval + self.waterBulletIval = None + return + + def getWaterBulletIval(self): + if not self.waterBulletIval.isPlaying(): + return self.waterBulletIval diff --git a/toontown/minigame/TwoDBlock.py b/toontown/minigame/TwoDBlock.py new file mode 100755 index 00000000..ff04a595 --- /dev/null +++ b/toontown/minigame/TwoDBlock.py @@ -0,0 +1,86 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from otp.level import BasicEntities +from toontown.coghq import MovingPlatform +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame import ToonBlitzGlobals + +class TwoDBlock(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDBlock') + + def __init__(self, model, index, blockAttribs): + self.moveIval = None + self.isMovingBlock = False + self.index = index + self.createNewBlock(model, blockAttribs) + return + + def destroy(self): + if self.moveIval: + self.moveIval.pause() + del self.moveIval + if self.platform: + if self.isMovingBlock: + self.platform.destroy() + del self.platform + + def createNewBlock(self, model, blockAttribs): + initX, initY, initZ, initH, initP, initR = (0, 0, 0, 0, 0, 0) + finalX, finalY, finalZ, finalH, finalP, finalR = (0, 0, 0, 0, 0, 0) + blockType = blockAttribs[0] + typeAttribs = ToonBlitzGlobals.BlockTypes[blockType] + blockName = blockType + '-' + str(self.index) + self.model = NodePath(blockName) + typeX, typeY, typeZ = typeAttribs[1] + typeH, typeP, typeR = typeAttribs[2] + scaleX, scaleY, scaleZ = typeAttribs[3] + model.setScale(scaleX, scaleY, scaleZ) + blockPosAttribs = blockAttribs[1] + initX, initY, initZ = blockPosAttribs[0] + if len(blockPosAttribs) == 3: + self.isMovingBlock = True + finalX, finalY, finalZ = blockPosAttribs[1] + posIvalDuration = blockPosAttribs[2] + if len(blockAttribs) == 3: + blockHprAttribs = blockAttribs[2] + initH, initP, initR = blockHprAttribs[0] + if len(blockHprAttribs) == 3: + self.isMovingBlock = True + finalH, finalP, finalR = blockHprAttribs[1] + hprIvalDuration = blockHprAttribs[2] + if self.isMovingBlock: + self.platform = MovingPlatform.MovingPlatform() + self.platform.setupCopyModel(blockName, model) + self.platform.reparentTo(self.model) + self.clearMoveIval() + forwardIval = LerpPosInterval(self.model, posIvalDuration, pos=Point3(finalX, finalY, finalZ), startPos=Point3(initX, initY, initZ), name='%s-moveFront' % self.platform.name, fluid=1) + backwardIval = LerpPosInterval(self.model, posIvalDuration, pos=Point3(initX, initY, initZ), startPos=Point3(finalX, finalY, finalZ), name='%s-moveBack' % self.platform.name, fluid=1) + self.moveIval = Sequence(forwardIval, backwardIval) + else: + self.platform = model.copyTo(self.model) + self.model.flattenLight() + self.model.setPos(typeX + initX, typeY + initY, typeZ + initZ) + self.model.setHpr(typeH + initH, typeP + initP, typeR + initR) + + def clearMoveIval(self): + if self.moveIval: + self.moveIval.pause() + del self.moveIval + self.moveIval = None + return + + def start(self, elapsedTime): + if self.moveIval: + self.moveIval.loop() + self.moveIval.setT(elapsedTime) + + def enterPause(self): + if self.moveIval: + self.moveIval.pause() + + def exitPause(self): + if self.moveIval: + self.moveIval.resume() diff --git a/toontown/minigame/TwoDCamera.py b/toontown/minigame/TwoDCamera.py new file mode 100755 index 00000000..4674436c --- /dev/null +++ b/toontown/minigame/TwoDCamera.py @@ -0,0 +1,60 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.task.Task import Task +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from toontown.minigame import ToonBlitzGlobals +import math + +class TwoDCamera(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDCamera') + + def __init__(self, camera): + self.notify.debug('Constructing TwoDCamera with %s' % camera) + self.camera = camera + self.cameraSideView = ToonBlitzGlobals.CameraStartingPosition + self.threeQuarterOffset = 2 + self.changeFacingInterval = None + self.ivalControllingCamera = False + self.accept('avatarOrientationChanged', self.setupChangeFacingInterval) + return + + def onstage(self): + self.camera.reparentTo(render) + p = self.cameraSideView + self.camera.setPosHpr(render, p[0], p[1], p[2], p[3], p[4], p[5]) + self.camera.setX(render, base.localAvatar.getX(render) + self.threeQuarterOffset) + + def destroy(self): + self.ignore('avatarOrientationChanged') + p = self.cameraSideView + self.camera.setPosHpr(render, p[0], p[1], p[2], p[3], p[4], p[5]) + + def update(self): + if not self.ivalControllingCamera: + camX = base.localAvatar.getX(render) - math.sin(base.localAvatar.getH(render) * math.pi / 180) * self.threeQuarterOffset + self.camera.setX(render, camX) + + def clearChangeFacingInterval(self): + if self.changeFacingInterval: + self.changeFacingInterval.pause() + del self.changeFacingInterval + self.changeFacingInterval = None + return + + def setupChangeFacingInterval(self, newHeading): + self.clearChangeFacingInterval() + self.newHeading = newHeading + self.changeFacingInterval = LerpFunc(self.myLerpPos, duration=5.0) + self.changeFacingInterval.start() + + def myLerpPos(self, t): + self.ivalControllingCamera = True + finalCamX = base.localAvatar.getX(render) - math.sin(self.newHeading * math.pi / 180) * self.threeQuarterOffset + diffX = finalCamX - self.camera.getX(render) + self.camera.setX(render, self.camera.getX(render) + diffX * t) + if math.fabs(self.camera.getX(render) - finalCamX) < 0.01: + self.notify.debug('giving up camera control') + self.camera.setX(render, finalCamX) + self.ivalControllingCamera = False + self.clearChangeFacingInterval() diff --git a/toontown/minigame/TwoDDrive.py b/toontown/minigame/TwoDDrive.py new file mode 100755 index 00000000..7cedc8ba --- /dev/null +++ b/toontown/minigame/TwoDDrive.py @@ -0,0 +1,154 @@ +from toontown.toonbase.ToonBaseGlobal import * +from otp.otpbase import OTPGlobals +from direct.interval.IntervalGlobal import * +import ArrowKeys +from direct.task.Task import Task + +class TwoDDrive: + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDDrive') + TASK_NAME = 'TwoDDriveTask' + SET_ATREST_HEADING_TASK = 'setAtRestHeadingTask' + + def __init__(self, game, speed, maxFrameMove = None, customCollisionCallback = None, priority = 0, setHeading = 1, upHeading = 0): + self.game = game + self.speed = speed + self.maxFrameMove = maxFrameMove + self.customCollisionCallback = customCollisionCallback + self.priority = priority + self.setHeading = setHeading + self.upHeading = upHeading + self.arrowKeys = ArrowKeys.ArrowKeys() + self.wasUpReleased = True + self.lt = base.localAvatar + base.localAvatar.useTwoDControls() + base.localAvatar.controlManager.currentControls.avatarControlJumpForce = 30.0 + self.ONE_JUMP_PER_UP_PRESSED = True + self.lastAction = None + self.isMovingX = False + return + + def destroy(self): + self.game = None + base.localAvatar.controlManager.currentControls.avatarControlJumpForce = 24.0 + base.localAvatar.useWalkControls() + self.arrowKeys.destroy() + del self.arrowKeys + del self.customCollisionCallback + self.lastAction = None + return + + def start(self): + self.notify.debug('start') + self.__placeToonHOG(self.lt.getPos()) + base.localAvatar.enableAvatarControls() + taskMgr.remove(TwoDDrive.TASK_NAME) + taskMgr.add(self.__update, TwoDDrive.TASK_NAME, priority=self.priority) + + def __placeToonHOG(self, pos, h = None): + if h == None: + h = self.lt.getH() + self.lt.setPos(pos) + self.lt.setH(h) + self.lastPos = pos + self.atRestHeading = h + self.oldAtRestHeading = h + self.lastXVel = 0 + self.lastYVel = 0 + return + + def stop(self): + self.notify.debug('stop') + base.localAvatar.disableAvatarControls() + taskMgr.remove(TwoDDrive.TASK_NAME) + taskMgr.remove(TwoDDrive.SET_ATREST_HEADING_TASK) + if hasattr(self, 'turnLocalToonIval'): + if self.turnLocalToonIval.isPlaying(): + self.turnLocalToonIval.pause() + del self.turnLocalToonIval + base.localAvatar.setSpeed(0, 0) + base.localAvatar.stopSound() + + def __update(self, task): + vel = Vec3(0, 0, 0) + xVel = 0 + yVel = 0 + if self.ONE_JUMP_PER_UP_PRESSED: + if not self.arrowKeys.upPressed(): + self.wasUpReleased = True + elif self.arrowKeys.upPressed() and self.wasUpReleased: + self.wasUpReleased = False + if not self.game.isHeadInFloor: + if localAvatar.controlManager.currentControls == localAvatar.controlManager.get('twoD'): + base.localAvatar.controlManager.currentControls.jumpPressed() + elif self.arrowKeys.upPressed(): + if not self.game.isHeadInFloor: + if localAvatar.controlManager.currentControls == localAvatar.controlManager.get('twoD'): + base.localAvatar.controlManager.currentControls.jumpPressed() + if self.arrowKeys.leftPressed(): + xVel -= 1 + if self.arrowKeys.rightPressed(): + xVel += 1 + vel.setX(xVel) + vel.setY(yVel) + vel.normalize() + vel *= self.speed + if abs(xVel) > 0: + if not self.isMovingX: + self.isMovingX = True + messenger.send('avatarMovingX') + elif self.isMovingX: + self.isMovingX = False + messenger.send('avatarStoppedX') + speed = vel.length() + action = self.lt.setSpeed(speed, 0) + if action != self.lastAction: + self.lastAction = action + if action == OTPGlobals.RUN_INDEX: + base.localAvatar.runSound() + else: + base.localAvatar.stopSound() + if self.setHeading: + self.__handleHeading(xVel, yVel) + toonPos = self.lt.getPos() + dt = globalClock.getDt() + posOffset = vel * dt + if self.customCollisionCallback: + toonPos = self.customCollisionCallback(toonPos, toonPos + posOffset) + else: + toonPos += posOffset + self.lt.setPos(toonPos) + self.lastPos = toonPos + return Task.cont + + def __handleHeading(self, xVel, yVel): + def getHeading(xVel, yVel): + angTab = [[None, 0, 180], [-90, -45, -135], [90, 45, 135]] + return angTab[xVel][yVel] + self.upHeading + + def orientToon(angle, self = self): + startAngle = self.lt.getH() + startAngle = fitSrcAngle2Dest(startAngle, angle) + dur = 0.1 * abs(startAngle - angle) / 90 + self.turnLocalToonIval = LerpHprInterval(self.lt, dur, Point3(angle, 0, 0), startHpr=Point3(startAngle, 0, 0), name='TwoDDriveLerpHpr') + self.turnLocalToonIval.start() + if self.atRestHeading != self.oldAtRestHeading: + self.oldAtRestHeading = self.atRestHeading + messenger.send('avatarOrientationChanged', [self.atRestHeading]) + + if xVel != self.lastXVel or yVel != self.lastYVel: + taskMgr.remove(TwoDDrive.SET_ATREST_HEADING_TASK) + if not (xVel or yVel): + orientToon(self.atRestHeading) + else: + curHeading = getHeading(xVel, yVel) + if ((self.lastXVel and self.lastYVel) and not (xVel and yVel)): + def setAtRestHeading(task, self = self, angle = curHeading): + self.atRestHeading = angle + return Task.done + + taskMgr.doMethodLater(0.05, setAtRestHeading, TwoDDrive.SET_ATREST_HEADING_TASK) + else: + self.atRestHeading = curHeading + orientToon(curHeading) + self.lastXVel = xVel + self.lastYVel = yVel diff --git a/toontown/minigame/TwoDEnemy.py b/toontown/minigame/TwoDEnemy.py new file mode 100755 index 00000000..a311f917 --- /dev/null +++ b/toontown/minigame/TwoDEnemy.py @@ -0,0 +1,279 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from direct.showbase import PythonUtil +from direct.interval.IntervalGlobal import * +from toontown.minigame import ToonBlitzGlobals +from toontown.toonbase import ToontownGlobals +from toontown.suit import Suit +from toontown.suit import SuitDNA +from toontown.battle.BattleProps import * +from toontown.battle import MovieUtil +from toontown.battle import BattleParticles, BattleProps +from direct.particles import ParticleEffect +import math, random +COLOR_RED = VBase4(1, 0, 0, 0.3) + +class TwoDEnemy(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDEnemy') + + def __init__(self, enemyMgr, index, suitAttribs): + self.enemyMgr = enemyMgr + self.game = self.enemyMgr.section.sectionMgr.game + self.index = index + self.moveIval = None + self.propTrack = None + self.animTrack = None + self.shotTrack = None + self.deathTrack = None + self.deathSuit = None + self.suitSound = None + self.deleteMeCallback = None + self.isMovingUpDown = False + self.isMovingLeftRight = False + self.showCollSpheres = False + self.isDestroyed = False + self.isGoingUp = False + self.setupEnemy(suitAttribs) + BattleParticles.loadParticles() + return + + def destroy(self): + if self.isDestroyed: + return + self.isDestroyed = True + if hasattr(self.suit, 'prop') and self.suit.prop: + self.suit.prop.stash() + if self.propTrack: + self.propTrack.finish() + self.propTrack = None + if self.suitSound: + self.suitSound.stop() + del self.suitSound + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + if self.shotTrack != None: + self.shotTrack.finish() + self.shotTrack = None + if self.deathTrack != None: + self.deathTrack.finish() + self.deathTrack = None + if self.deathSuit: + self.deathSuit.detachNode() + self.suit.cleanupLoseActor() + self.deathSuit = None + if self.moveIval: + self.moveIval.pause() + del self.moveIval + if self.suit: + self.suit.delete() + self.suit = None + BattleParticles.unloadParticles() + self.ignore(self.game.uniqueName('enter' + self.suitName)) + self.game = None + self.enemyMgr = None + return + + def setupEnemy(self, suitAttribs): + suitType = suitAttribs[0] + self.suit = Suit.Suit() + suitDNA = SuitDNA.SuitDNA() + suitDNA.newSuit(suitType) + self.suit.setDNA(suitDNA) + self.suit.pose('walk', 0) + self.suitName = 'Enemy-%s' % self.index + self.suit.setName(self.suitName) + self.suit.nametag3d.stash() + self.suit.nametag.destroy() + suitPosAttribs = suitAttribs[1] + initX, initY, initZ = suitPosAttribs[0] + initPos = Point3(initX, initY, initZ) + if len(suitPosAttribs) == 3: + finalX, finalY, finalZ = suitPosAttribs[1] + finalPos = Point3(finalX, finalY, finalZ) + posIvalDuration = suitPosAttribs[2] + self.clearMoveIval() + + def getForwardIval(blendTypeStr, self = self): + forwardIval = LerpPosInterval(self.suit, posIvalDuration, pos=finalPos, startPos=initPos, name='%s-moveFront' % self.suitName, blendType=blendTypeStr, fluid=1) + return forwardIval + + def getBackwardIval(blendTypeStr, self = self): + backwardIval = LerpPosInterval(self.suit, posIvalDuration, pos=initPos, startPos=finalPos, name='%s-moveBack' % self.suitName, blendType=blendTypeStr, fluid=1) + return backwardIval + + if abs(finalZ - initZ) > 0.0: + + def setIsGoingUp(value): + self.isGoingUp = value + + self.isMovingUpDown = True + self.suit.setH(90) + self.suit.prop = None + if self.suit.prop == None: + self.suit.prop = BattleProps.globalPropPool.getProp('propeller') + self.suit.prop.setScale(1.1) + self.suit.prop.setColor(1, 1, 0.6, 1) + head = self.suit.find('**/joint_head') + self.suit.prop.reparentTo(head) + self.propTrack = Sequence(ActorInterval(self.suit.prop, 'propeller', startFrame=8, endFrame=25, playRate=2.0)) + self.animTrack = Sequence(ActorInterval(self.suit, 'landing', startFrame=8, endFrame=28, playRate=0.5), ActorInterval(self.suit, 'landing', startFrame=8, endFrame=28, playRate=-0.5)) + self.moveIval = Sequence(Func(setIsGoingUp, True), getForwardIval('easeInOut'), Func(setIsGoingUp, False), getBackwardIval('easeInOut')) + self.suitSound = base.loadSfx('phase_4/audio/sfx/TB_propeller.ogg') + else: + self.isMovingLeftRight = True + self.moveIval = Sequence(Func(self.setHeading, finalPos, initPos), getForwardIval('noBlend'), Func(self.setHeading, initPos, finalPos), getBackwardIval('noBlend')) + self.suit.setPos(initX, initY, initZ) + self.suit.dropShadow.hide() + self.setupCollision() + return + + def setupCollision(self): + collSphere = CollisionSphere(0, 0, 2, 2) + collSphere.setTangible(1) + collNode = CollisionNode(self.game.uniqueName(self.suitName)) + collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + collNode.addSolid(collSphere) + self.collNodePath = self.suit.attachNewNode(collNode) + self.collNodePath.hide() + if self.showCollSpheres: + self.collNodePath.show() + self.accept(self.game.uniqueName('enter' + self.suitName), self.handleEnemyCollision) + + def clearMoveIval(self): + if self.moveIval: + self.moveIval.pause() + del self.moveIval + self.moveIval = None + return + + def start(self, elapsedTime): + if self.moveIval: + self.moveIval.loop() + self.moveIval.setT(elapsedTime) + if self.isMovingLeftRight: + self.suit.loop('walk') + elif self.isMovingUpDown: + self.propTrack.loop() + self.animTrack.loop() + base.playSfx(self.suitSound, node=self.suit, looping=1) + + def enterPause(self): + if hasattr(self, 'moveIval') and self.moveIval: + self.moveIval.pause() + self.suit.loop('neutral') + if self.suitSound: + self.suitSound.stop() + + def exitPause(self): + if hasattr(self, 'moveIval') and self.moveIval: + self.moveIval.resume() + if self.isMovingLeftRight: + self.suit.loop('walk') + elif self.isMovingUpDown: + self.propTrack.loop() + self.animTrack.loop() + base.playSfx(self.suitSound, node=self.suit, looping=1, volume=0.1) + + def handleEnemyCollision(self, cevent): + messenger.send('enemyHit') + + def setHeading(self, finalPos, initPos): + diffX = finalPos.getX() - initPos.getX() + angle = -90 * diffX / math.fabs(diffX) + startAngle = self.suit.getH() + startAngle = PythonUtil.fitSrcAngle2Dest(startAngle, angle) + dur = 0.1 * abs(startAngle - angle) / 90 + self.suitTurnIval = LerpHprInterval(self.suit, dur, Point3(angle, 0, 0), startHpr=Point3(startAngle, 0, 0), name='SuitLerpHpr') + self.suitTurnIval.start() + + def blinkColor(self, color, duration): + blink = Sequence(LerpColorScaleInterval(self.suit, 0.5, color, startColorScale=VBase4(1, 1, 1, 1)), LerpColorScaleInterval(self.suit, 0.5, VBase4(1, 1, 1, 1), startColorScale=color)) + track = Sequence(Func(blink.loop), Wait(duration), Func(blink.finish)) + return track + + def doShotTrack(self): + blinkRed = self.blinkColor(COLOR_RED, 2) + point = Point3(self.suit.getX(render), self.suit.getY(render), self.suit.getZ(render) + self.suit.height / 2.0) + scale = 0.3 + splashHold = 0.1 + + def prepSplash(splash, point): + if callable(point): + point = point() + splash.reparentTo(render) + splash.setPos(point) + scale = splash.getScale() + splash.setBillboardPointWorld() + splash.setScale(scale) + + splash = globalPropPool.getProp('splash-from-splat') + splash.setScale(scale) + splashTrack = Sequence(Func(prepSplash, splash, point), ActorInterval(splash, 'splash-from-splat'), Wait(splashHold), Func(MovieUtil.removeProp, splash)) + self.shotTrack = Parallel(Func(self.game.assetMgr.playSplashSound), blinkRed, splashTrack) + self.shotTrack.start() + + def doDeathTrack(self): + + def removeDeathSuit(suit, deathSuit): + if not deathSuit.isEmpty(): + deathSuit.detachNode() + suit.cleanupLoseActor() + + if self.suitSound: + self.suitSound.stop() + self.deathSuit = self.suit.getLoseActor() + self.deathSuit.reparentTo(self.enemyMgr.enemiesNP) + self.deathSuit.setPos(render, self.suit.getPos(render)) + self.deathSuit.setHpr(render, self.suit.getHpr(render)) + self.suit.hide() + self.collNodePath.reparentTo(self.deathSuit) + treasureSpawnPoint = Point3(self.suit.getX(), self.suit.getY(), self.suit.getZ() + self.suit.height / 2.0) + gearPoint = Point3(0, 0, self.suit.height / 2.0 + 2.0) + spinningSound = base.loadSfx('phase_3.5/audio/sfx/Cog_Death.ogg') + deathSound = base.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart_%s.ogg' % random.randint(1, 6)) + smallGears = BattleParticles.createParticleEffect(file='gearExplosionSmall') + singleGear = BattleParticles.createParticleEffect('GearExplosion', numParticles=1) + smallGearExplosion = BattleParticles.createParticleEffect('GearExplosion', numParticles=10) + bigGearExplosion = BattleParticles.createParticleEffect('BigGearExplosion', numParticles=30) + smallGears.setPos(gearPoint) + singleGear.setPos(gearPoint) + smallGearExplosion.setPos(gearPoint) + bigGearExplosion.setPos(gearPoint) + smallGears.setDepthWrite(False) + singleGear.setDepthWrite(False) + smallGearExplosion.setDepthWrite(False) + bigGearExplosion.setDepthWrite(False) + if self.isMovingLeftRight: + self.enterPause() + suitTrack = Sequence(Func(self.collNodePath.stash), ActorInterval(self.deathSuit, 'lose', startFrame=80, endFrame=140), Func(removeDeathSuit, self.suit, self.deathSuit, name='remove-death-suit')) + explosionTrack = Sequence(Wait(1.5), MovieUtil.createKapowExplosionTrack(self.deathSuit, explosionPoint=gearPoint)) + soundTrack = Sequence(SoundInterval(spinningSound, duration=1.6, startTime=0.6, volume=0.8, node=self.deathSuit), SoundInterval(deathSound, volume=0.32, node=self.deathSuit)) + gears1Track = Sequence(ParticleInterval(smallGears, self.deathSuit, worldRelative=0, duration=4.3, cleanup=True), name='gears1Track') + gears2MTrack = Track((0.0, explosionTrack), (0.7, ParticleInterval(singleGear, self.deathSuit, worldRelative=0, duration=5.7, cleanup=True)), (5.2, ParticleInterval(smallGearExplosion, self.deathSuit, worldRelative=0, duration=1.2, cleanup=True)), (5.4, ParticleInterval(bigGearExplosion, self.deathSuit, worldRelative=0, duration=1.0, cleanup=True)), name='gears2MTrack') + elif self.isMovingUpDown: + + def getFinalPos(): + if self.isGoingUp: + direction = 1.0 + else: + direction = -1.0 + pos = Point3(self.deathSuit.getX(), self.deathSuit.getY(), self.deathSuit.getZ() + 2.0 * direction) + return pos + + deathMoveIval = LerpPosInterval(self.deathSuit, 1.5, pos=getFinalPos(), name='%s-deathSuitMove' % self.suitName, blendType='easeInOut', fluid=1) + suitTrack = Sequence(Func(self.collNodePath.stash), Parallel(ActorInterval(self.deathSuit, 'lose', startFrame=80, endFrame=140), deathMoveIval), Func(removeDeathSuit, self.suit, self.deathSuit, name='remove-death-suit')) + explosionTrack = Sequence(Wait(1.5), MovieUtil.createKapowExplosionTrack(self.deathSuit, explosionPoint=gearPoint)) + soundTrack = Sequence(SoundInterval(spinningSound, duration=1.6, startTime=0.6, volume=0.8, node=self.deathSuit), SoundInterval(deathSound, volume=0.32, node=self.deathSuit)) + gears1Track = Sequence(ParticleInterval(smallGears, self.deathSuit, worldRelative=0, duration=4.3, cleanup=True), name='gears1Track') + gears2MTrack = Track((0.0, explosionTrack), (0.0, ParticleInterval(singleGear, self.deathSuit, worldRelative=0, duration=5.7, cleanup=True)), (2.7, ParticleInterval(smallGearExplosion, self.deathSuit, worldRelative=0, duration=1.2, cleanup=True)), (2.9, ParticleInterval(bigGearExplosion, self.deathSuit, worldRelative=0, duration=1.0, cleanup=True)), name='gears2MTrack') + + def removeParticle(particle): + if particle and hasattr(particle, 'renderParent'): + particle.cleanup() + del particle + + removeParticles = Parallel(Func(removeParticle, smallGears), Func(removeParticle, singleGear), Func(removeParticle, smallGearExplosion), Func(removeParticle, bigGearExplosion)) + self.deathTrack = Sequence(Parallel(suitTrack, gears2MTrack, gears1Track, soundTrack), removeParticles, Func(self.destroy)) + self.deathTrack.start() diff --git a/toontown/minigame/TwoDEnemyMgr.py b/toontown/minigame/TwoDEnemyMgr.py new file mode 100755 index 00000000..6d7037e5 --- /dev/null +++ b/toontown/minigame/TwoDEnemyMgr.py @@ -0,0 +1,50 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.minigame import ToonBlitzGlobals +from toontown.minigame import TwoDEnemy + +class TwoDEnemyMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDEnemyMgr') + + def __init__(self, section, enemyList): + self.section = section + self.enemyList = enemyList + self.load() + + def destroy(self): + self.section = None + while len(self.enemies): + enemy = self.enemies[0] + enemy.destroy() + self.enemies.remove(enemy) + + self.enemies = None + return + + def load(self): + if len(self.enemyList): + self.enemiesNP = NodePath('Enemies') + self.enemiesNP.reparentTo(self.section.sectionNP) + self.enemies = [] + for index in xrange(len(self.enemyList)): + enemyId = self.section.getSectionizedId(index) + suitAttribs = self.enemyList[index] + newEnemy = TwoDEnemy.TwoDEnemy(self, enemyId, suitAttribs) + newEnemy.suit.reparentTo(self.enemiesNP) + self.enemies.append(newEnemy) + + def enterPlay(self, elapsedTime): + for enemy in self.enemies: + enemy.start(elapsedTime) + + def exitPlay(self): + pass + + def enterPause(self): + for enemy in self.enemies: + enemy.enterPause() + + def exitPause(self): + for enemy in self.enemies: + enemy.exitPause() diff --git a/toontown/minigame/TwoDGameToonSD.py b/toontown/minigame/TwoDGameToonSD.py new file mode 100755 index 00000000..b072ccfd --- /dev/null +++ b/toontown/minigame/TwoDGameToonSD.py @@ -0,0 +1,292 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import ToonBlitzGlobals +from otp.otpbase import OTPGlobals +from direct.task.Task import Task +from toontown.minigame import TwoDBattleMgr +from toontown.racing import RaceHeadFrame +COLOR_RED = VBase4(1, 0, 0, 0.3) + +class TwoDGameToonSD(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDGameToonSD') + FallBackAnim = 'slip-backward' + NeutralAnim = 'neutral' + RunAnim = 'run' + ShootGun = 'water-gun' + Victory = 'victory' + animList = [FallBackAnim, + NeutralAnim, + RunAnim, + ShootGun, + Victory] + ScoreTextGenerator = TextNode('ScoreTextGenerator') + + def __init__(self, avId, game): + self.avId = avId + self.game = game + self.isLocal = avId == base.localAvatar.doId + self.toon = self.game.getAvatar(self.avId) + self.unexpectedExit = False + self.fallBackIval = None + self.fallDownIval = None + self.victoryIval = None + self.squishIval = None + self.fsm = ClassicFSM.ClassicFSM('TwoDGameAnimFSM-%s' % self.avId, [State.State('init', self.enterInit, self.exitInit, ['normal']), + State.State('normal', self.enterNormal, self.exitNormal, ['shootGun', + 'fallBack', + 'fallDown', + 'victory', + 'squish']), + State.State('shootGun', self.enterShootGun, self.exitShootGun, ['normal', + 'fallBack', + 'fallDown', + 'victory', + 'squish']), + State.State('fallBack', self.enterFallBack, self.exitFallBack, ['normal', 'fallDown', 'squish']), + State.State('fallDown', self.enterFallDown, self.exitFallDown, ['normal']), + State.State('squish', self.enterSquish, self.exitSquish, ['normal']), + State.State('victory', self.enterVictory, self.exitVictory, ['normal', 'fallDown']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'init', 'cleanup') + self.scoreText = None + self.load() + self.progressLineLength = self.game.assetMgr.faceEndPos[0] - self.game.assetMgr.faceStartPos[0] + self.conversionRatio = self.progressLineLength / self.game.gameLength + return + + def load(self): + for anim in self.animList: + self.toon.pose(anim, 0) + + self.battleMgr = TwoDBattleMgr.TwoDBattleMgr(self.game, self.toon) + self.squishSound = base.loadSfx('phase_3.5/audio/dial/AV_' + self.toon.style.getAnimal() + '_exclaim.ogg') + + def destroy(self): + if self.fallBackIval != None: + self.fallBackIval.finish() + self.fallBackIval = None + if self.fallDownIval != None: + self.fallDownIval.finish() + self.fallDownIval = None + if self.squishIval != None: + self.squishIval.finish() + self.squishIval = None + if self.victoryIval != None: + self.victoryIval.finish() + self.victoryIval = None + self.hideScoreText() + self.headFrame.destroy() + self.battleMgr.destroy() + del self.battleMgr + del self.fsm + return + + def enter(self): + self.fsm.enterInitialState() + + def exit(self, unexpectedExit = False): + self.unexpectedExit = unexpectedExit + self.fsm.requestFinalState() + + def enterInit(self): + self.notify.debug('enterInit') + self.toon.startBlink() + self.toon.stopLookAround() + self.toon.useLOD(1000) + self.toon.dropShadow.hide() + + def exitInit(self): + pass + + def enterNormal(self): + self.notify.debug('enterNormal') + + def exitNormal(self): + pass + + def enterFallBack(self): + self.notify.debug('enterFallBack') + fallBackDist = 9 + self.blinkRed = self.blinkColor(COLOR_RED, 2) + duration = 4 + animName = self.FallBackAnim + startFrame = 1 + endFrame = 22 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.toon.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + fallBackAnim = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=endFrame / newRate, playRate=playRate), ActorInterval(self.toon, animName, startFrame=23, playRate=1), FunctionInterval(self.resume)) + self.fallBackIval = Parallel(self.blinkRed, fallBackAnim) + if self.isLocal: + self.game.ignoreInputs() + self.toon.controlManager.currentControls.lifter.setVelocity(12) + if self.toon.getH() == 90: + endPoint = Point3(self.toon.getX() + fallBackDist, self.toon.getY(), self.toon.getZ()) + else: + endPoint = Point3(self.toon.getX() - fallBackDist, self.toon.getY(), self.toon.getZ()) + enemyHitTrajectory = LerpFunc(self.toon.setX, fromData=self.toon.getX(), toData=endPoint.getX(), duration=0.75, name='enemyHitTrajectory') + self.fallBackIval.append(enemyHitTrajectory) + base.playSfx(self.game.assetMgr.sndOof) + self.fallBackIval.start() + + def exitFallBack(self): + self.fallBackIval.pause() + self.blinkRed.finish() + if self.isLocal: + if self.game.gameFSM.getCurrentState().getName() == 'play': + self.game.acceptInputs() + + def shootGun(self): + if self.fsm.getCurrentState().getName() == 'shootGun': + self.fsm.request('normal') + self.fsm.request('shootGun') + + def enterShootGun(self): + self.battleMgr.shoot() + self.fsm.request('normal') + + def exitShootGun(self): + pass + + def enterFallDown(self): + self.notify.debug('enterFallDown') + self.fallDownIval = Sequence() + self.blinkRed = self.blinkColor(COLOR_RED, 2) + if self.isLocal: + self.game.ignoreInputs() + base.playSfx(self.game.assetMgr.fallSound) + pos = self.game.sectionMgr.getLastSpawnPoint() + toonRespawnIval = Sequence(Wait(1), Func(base.localAvatar.setPos, pos)) + self.fallDownIval.append(toonRespawnIval) + self.fallDownIval.append(Func(self.resume)) + self.fallDownIval.append(self.blinkRed) + self.fallDownIval.start() + + def exitFallDown(self): + if self.isLocal: + if self.game.gameFSM.getCurrentState().getName() == 'play': + self.game.acceptInputs() + + def enterSquish(self): + self.notify.debug('enterSquish') + if self.isLocal: + self.game.ignoreInputs() + self.toon.setAnimState('Squish') + self.toon.stunToon() + base.playSfx(self.squishSound, node=self.toon, volume=2) + self.blinkRed = self.blinkColor(COLOR_RED, 3) + self.squishIval = Parallel(self.blinkRed, Sequence(Wait(2.5), Func(self.resume))) + self.squishIval.start() + + def exitSquish(self): + if self.isLocal: + if self.game.gameFSM.getCurrentState().getName() == 'play': + self.game.acceptInputs() + + def enterVictory(self): + self.notify.debug('enterVictory') + outsideElevatorPos = self.game.sectionMgr.exitElevator.find('**/loc_elevator_front').getPos(render) + insideElevatorPos = self.game.sectionMgr.exitElevator.find('**/loc_elevator_inside').getPos(render) + runToElevator = Parallel(LerpPosInterval(self.toon, self.game.timeToRunToElevator, outsideElevatorPos), ActorInterval(self.toon, self.RunAnim, loop=1, duration=self.game.timeToRunToElevator)) + danceIval = Parallel(LerpPosInterval(self.toon, 2, insideElevatorPos), ActorInterval(self.toon, self.Victory, loop=1, duration=ToonBlitzGlobals.GameDuration[self.game.getSafezoneId()])) + waitToLand = 0.0 + if self.toon.getZ(render) > 13: + waitToLand = 1 + self.victoryIval = Sequence(Wait(waitToLand), runToElevator, danceIval) + self.victoryIval.start() + + def exitVictory(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.toon.stopBlink() + self.toon.startLookAround() + self.toon.resetLOD() + self.toon.dropShadow.show() + + def exitCleanup(self): + pass + + def resume(self): + self.fsm.request('normal') + messenger.send('jumpLand') + + def blinkColor(self, color, duration): + blink = Sequence(LerpColorScaleInterval(self.toon, 0.5, color, startColorScale=VBase4(1, 1, 1, 1)), LerpColorScaleInterval(self.toon, 0.5, VBase4(1, 1, 1, 1), startColorScale=color)) + track = Sequence(Func(blink.loop), Wait(duration), Func(blink.finish)) + return track + + def setAnimState(self, newState, playRate): + if not self.unexpectedExit: + self.toon.setAnimState(newState, playRate) + + def createHeadFrame(self, drawNum): + toon = base.cr.doId2do.get(self.avId, None) + self.headFrame = RaceHeadFrame.RaceHeadFrame(av=toon) + eyes = self.headFrame.head.find('**/eyes*') + eyes.setDepthTest(1) + eyes.setDepthWrite(1) + self.headFrame.configure(geom_scale=(0.5, 0.5, 0.5)) + self.headFrame.setZ(self.game.assetMgr.faceStartPos[2]) + self.headFrame.setDepthWrite(True) + self.headFrame.setDepthTest(True) + self.headFrame.reparentTo(self.game.assetMgr.aspect2dRoot) + self.headFrame.setY(-drawNum) + if self.isLocal: + self.headFrame.setScale(0.2) + else: + self.headFrame.setScale(0.15) + self.headFrame.setX(self.game.assetMgr.faceStartPos[0]) + return + + def update(self): + toonCurrX = self.toon.getX(render) + progress = toonCurrX - self.game.gameStartX + headFrameProgress = progress * self.conversionRatio + headFrameX = self.game.assetMgr.faceStartPos[0] + headFrameProgress + headFrameX = max(self.game.assetMgr.faceStartPos[0], headFrameX) + headFrameX = min(self.game.assetMgr.faceEndPos[0], headFrameX) + self.headFrame.setX(headFrameX) + + def showScoreText(self, number, scale = 1.25): + if not number == 0: + if self.scoreText: + self.hideScoreText() + self.ScoreTextGenerator.setFont(OTPGlobals.getSignFont()) + if number < 0: + self.ScoreTextGenerator.setText(str(number)) + else: + self.ScoreTextGenerator.setText('+' + str(number)) + self.ScoreTextGenerator.clearShadow() + self.ScoreTextGenerator.setAlign(TextNode.ACenter) + if number < 0: + r, g, b, a = (0.9, 0, 0, 1) + else: + r, g, b, a = (0.9, 0.9, 0, 1) + self.scoreTextNode = self.ScoreTextGenerator.generate() + self.scoreText = self.toon.attachNewNode(self.scoreTextNode) + self.scoreText.setScale(scale) + self.scoreText.setBillboardPointEye() + self.scoreText.setBin('fixed', 100) + self.scoreText.setPos(0, 0, self.toon.height / 2) + self.scoreText.setTransparency(1) + self.scoreText.setColor(r, g, b, a) + self.scoreText.setDepthTest(0) + self.scoreText.setDepthWrite(0) + seq = Sequence(self.scoreText.posInterval(0.5, Point3(0, 0, self.toon.height + 2), blendType='easeOut'), self.scoreText.colorInterval(0.25, Vec4(r, g, b, 0)), Func(self.hideScoreText)) + seq.start() + + def hideScoreText(self): + if self.scoreText: + taskMgr.remove(self.game.uniqueName('scoreText')) + self.scoreText.removeNode() + self.scoreText = None + return diff --git a/toontown/minigame/TwoDSection.py b/toontown/minigame/TwoDSection.py new file mode 100755 index 00000000..943aa05b --- /dev/null +++ b/toontown/minigame/TwoDSection.py @@ -0,0 +1,127 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.minigame import ToonBlitzGlobals +from toontown.minigame import TwoDBlock +from toontown.minigame import TwoDEnemyMgr +from toontown.minigame import TwoDTreasureMgr +from toontown.minigame import TwoDSpawnPointMgr +from toontown.minigame import TwoDStomperMgr + +class TwoDSection(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDSection') + + def __init__(self, indexNum, sectionInfo, sectionNP, sectionMgr): + self.indexNum = indexNum + self.sectionNP = sectionNP + self.sectionMgr = sectionMgr + self.blocks = [] + self.load(sectionInfo) + + def destroy(self): + for block in self.blocks: + block.destroy() + + self.enemyMgr.destroy() + del self.enemyMgr + self.treasureMgr.destroy() + del self.treasureMgr + self.spawnPointMgr.destroy() + del self.spawnPointMgr + self.stomperMgr.destroy() + del self.stomperMgr + self.sectionMgr = None + self.sectionNP = None + self.blockList = [] + self.enemyList = [] + self.treasureList = [] + self.spawnPointList = [] + return + + def load(self, sectionInfo): + self.sectionTypeNum = sectionInfo[0] + enemyIndicesSelected = sectionInfo[1] + treasureIndicesSelected = sectionInfo[2] + spawnPointIndicesSelected = sectionInfo[3] + stomperIndicesSelected = sectionInfo[4] + attribs = ToonBlitzGlobals.SectionTypes[self.sectionTypeNum] + self.length = attribs[1] + self.blockList = attribs[2] + enemiesPool = attribs[3] + treasuresPool = attribs[4] + spawnPointsPool = attribs[5] + stompersPool = attribs[6] + self.enemyList = [] + for enemyIndex in enemyIndicesSelected: + self.enemyList.append(enemiesPool[enemyIndex]) + + self.treasureList = [] + for treasure in treasureIndicesSelected: + treasureIndex = treasure[0] + treasureValue = treasure[1] + treasureAttribs = treasuresPool[treasureIndex] + self.treasureList.append((treasureAttribs, treasureValue)) + + self.spawnPointList = [] + for spawnPointIndex in spawnPointIndicesSelected: + self.spawnPointList.append(spawnPointsPool[spawnPointIndex]) + + self.stomperList = [] + for stomperIndex in stomperIndicesSelected: + self.stomperList.append(stompersPool[stomperIndex]) + + self.blocksNP = NodePath('Blocks') + self.blocksNP.reparentTo(self.sectionNP) + if self.blockList[0][1][0] != (0, 0, 12): + self.notify.warning('First block of section %s does not start at (0, 0, 12)' % self.sectionTypeNum) + for index in xrange(0, len(self.blockList)): + blockAttribs = self.blockList[index] + fileName = ToonBlitzGlobals.BlockTypes[blockAttribs[0]][0] + blockIndex = int(fileName[-1]) + blockType = self.sectionMgr.game.assetMgr.blockTypes[blockIndex] + sectionizedId = self.getSectionizedId(index) + newBlock = TwoDBlock.TwoDBlock(blockType, sectionizedId, blockAttribs) + newBlock.model.reparentTo(self.blocksNP) + self.blocks.append(newBlock) + + self.enemyMgr = TwoDEnemyMgr.TwoDEnemyMgr(self, self.enemyList) + self.treasureMgr = TwoDTreasureMgr.TwoDTreasureMgr(self, self.treasureList, self.enemyList) + self.spawnPointMgr = TwoDSpawnPointMgr.TwoDSpawnPointMgr(self, self.spawnPointList) + self.stomperMgr = TwoDStomperMgr.TwoDStomperMgr(self, self.stomperList) + if self.sectionTypeNum == 'end': + self.spawnPointMgr.setupLastSavePointHandle() + + def enterPlay(self, elapsedTime): + for block in self.blocks: + block.start(elapsedTime) + + self.enemyMgr.enterPlay(elapsedTime) + self.stomperMgr.enterPlay(elapsedTime) + + def exitPlay(self): + pass + + def enterPause(self): + for block in self.blocks: + block.enterPause() + + self.enemyMgr.enterPause() + self.stomperMgr.enterPause() + + def exitPause(self): + for block in self.blocks: + block.exitPause() + + self.enemyMgr.exitPause() + self.stomperMgr.exitPause() + + def getSectionizedId(self, num): + + def getTwoDigitString(index): + if index < 10: + output = '0' + str(index) + else: + output = str(index) + return output + + return getTwoDigitString(self.indexNum) + '-' + getTwoDigitString(num) diff --git a/toontown/minigame/TwoDSectionMgr.py b/toontown/minigame/TwoDSectionMgr.py new file mode 100755 index 00000000..9f4dd00a --- /dev/null +++ b/toontown/minigame/TwoDSectionMgr.py @@ -0,0 +1,134 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.minigame import ToonBlitzGlobals +from toontown.minigame import TwoDSection +from toontown.minigame import TwoDSpawnPointMgr +from toontown.minigame import TwoDBlock +from direct.gui import DirectGui +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals + +class TwoDSectionMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDSectionMgr') + + def __init__(self, game, sectionsSelected): + self.game = game + self.sectionsPool = [] + self.sectionsSelected = [] + self.sections = [] + self.sectionNPList = [] + self.activeSection = 0 + self.setupStartSection() + self.setupSections(sectionsSelected) + self.setupEndSection(len(sectionsSelected)) + + def destroy(self): + while len(self.sections): + section = self.sections[0] + section.destroy() + self.sections.remove(section) + + self.sections = [] + self.sectionsPool = [] + self.sectionsSelected = [] + self.sectionNPList = [] + self.startWall.removeNode() + del self.startWall + self.startPipe.removeNode() + del self.startPipe + self.startArrow.removeNode() + del self.startArrow + self.endArrow.removeNode() + del self.endArrow + self.game = None + self.activeSection = 0 + return + + def setupStartSection(self): + self.startSectionNP = NodePath('StartSection') + self.startSectionNP.reparentTo(self.game.assetMgr.world) + self.startSectionNP.setX(-48) + self.startWall = self.game.assetMgr.startingWall.copyTo(self.startSectionNP) + self.startWall.setPos(-28, 0, 4) + self.startWall.setScale(0.8) + self.startPipe = self.game.assetMgr.startingPipe.copyTo(self.startSectionNP) + self.startPipe.setPos(12, 0, 44) + self.startArrow = self.game.assetMgr.arrow.copyTo(self.startSectionNP) + self.startArrow.setPos(23, 1.5, 12.76) + for index in xrange(len(ToonBlitzGlobals.BlockListStart)): + blockAttribs = ToonBlitzGlobals.BlockListStart[index] + fileName = ToonBlitzGlobals.BlockTypes[blockAttribs[0]][0] + blockIndex = int(fileName[-1]) + blockType = self.game.assetMgr.blockTypes[blockIndex] + sectionizedId = 'start-' + str(index) + newBlock = TwoDBlock.TwoDBlock(blockType, sectionizedId, blockAttribs) + newBlock.model.reparentTo(self.startSectionNP) + + def setupEndSection(self, index): + aspectSF = 0.7227 + self.endSectionNP = NodePath('EndSection') + self.endSectionNP.reparentTo(self.game.assetMgr.world) + self.endSectionNP.setX(self.incrementX) + self.endWall = self.game.assetMgr.startingWall.copyTo(self.endSectionNP) + self.endWall.setPos(100, 0, 4) + self.endWall.setScale(0.8) + self.endArrow = self.game.assetMgr.arrow.copyTo(self.endSectionNP) + self.endArrow.setPos(6, 1.5, 12.76) + self.exitElevator = self.game.assetMgr.exitElevator.copyTo(self.endSectionNP) + self.exitElevator.setPos(52, -2, 12.7) + cogSignModel = loader.loadModel('phase_4/models/props/sign_sellBotHeadHQ') + cogSign = cogSignModel.find('**/sign_sellBotHeadHQ') + cogSignSF = 23 + elevatorSignSF = 15 + sideDoor = self.exitElevator.find('**/doorway2') + sdSign = cogSign.copyTo(sideDoor) + sdSign.setPosHprScale(0, 1.9, 15, 0, 0, 0, elevatorSignSF, elevatorSignSF, elevatorSignSF * aspectSF) + sdSign.node().setEffect(DecalEffect.make()) + sdText = DirectGui.OnscreenText(text=TTLocalizer.TwoDGameElevatorExit, font=ToontownGlobals.getSuitFont(), pos=(0, -0.34), scale=0.15, mayChange=False, parent=sdSign) + sdText.setDepthWrite(0) + self.sectionNPList.append(self.endSectionNP) + endSectionInfo = ('end', + [], + [], + [0], + []) + endSection = TwoDSection.TwoDSection(index, endSectionInfo, self.endSectionNP, self) + self.sections.append(endSection) + self.incrementX += endSection.length + + def setupSections(self, sectionsSelected): + self.incrementX = -24 + for index in xrange(0, len(sectionsSelected)): + sectionNP = NodePath('Section' + str(index)) + sectionNP.reparentTo(self.game.assetMgr.world) + sectionNP.setX(self.incrementX) + self.sectionNPList.append(sectionNP) + section = TwoDSection.TwoDSection(index, sectionsSelected[index], sectionNP, self) + self.sections.append(section) + self.incrementX += section.length + + def enterPlay(self, elapsedTime): + for section in self.sections: + section.enterPlay(elapsedTime) + + def exitPlay(self): + pass + + def enterPause(self): + for section in self.sections: + section.enterPause() + + def exitPause(self): + for section in self.sections: + section.exitPause() + + def updateActiveSection(self, sectionIndex): + if self.activeSection != sectionIndex: + self.activeSection = sectionIndex + self.notify.debug('Toon is in section %s.' % sectionIndex) + + def getLastSpawnPoint(self): + relativePoint = Point3(self.sections[self.activeSection].spawnPointMgr.getSpawnPoint()) + relativePoint.setX(relativePoint.getX() + self.sectionNPList[self.activeSection].getX()) + return relativePoint diff --git a/toontown/minigame/TwoDSpawnPointMgr.py b/toontown/minigame/TwoDSpawnPointMgr.py new file mode 100755 index 00000000..87c89631 --- /dev/null +++ b/toontown/minigame/TwoDSpawnPointMgr.py @@ -0,0 +1,88 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.minigame import ToonBlitzGlobals +from toontown.toonbase import ToontownGlobals + +class TwoDSpawnPointMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDSpawnPointMgr') + + def __init__(self, section, spawnPointList): + self.section = section + self.game = self.section.sectionMgr.game + self.spawnPointList = spawnPointList + self.lastSavePoint = 0 + self.showCollSpheres = False + self.savePoints = [] + self.loadPoints = [] + self.collNPList = [] + self.collDict = {} + self.load() + + def destroy(self): + while len(self.collNPList): + item = self.collNPList[0] + self.ignore('enter' + self.collNPList[0].node().getName()) + self.collNPList.remove(item) + item.removeNode() + + self.section = None + self.game = None + self.savePoints = None + self.loadPoints = None + self.collNPList = None + self.collDict = None + return + + def load(self): + if len(self.spawnPointList): + self.spawnPointsNP = NodePath('SpawnPoints') + self.spawnPointsNP.reparentTo(self.section.sectionNP) + for point in self.spawnPointList: + if len(point) == 1: + savePoint = point[0] + loadPoint = point[0] + else: + savePoint = point[0] + loadPoint = point[1] + index = len(self.savePoints) + self.savePoints.append(savePoint) + self.loadPoints.append(loadPoint) + self.setupCollision(index) + + def setupCollision(self, index): + collSphere = CollisionSphere(0, 0, 0, 3) + collSphereName = 'savePoint%s' % self.section.getSectionizedId(index) + collSphere.setTangible(0) + collNode = CollisionNode(self.game.uniqueName(collSphereName)) + collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + collNode.addSolid(collSphere) + collNodePath = self.spawnPointsNP.attachNewNode(collNode) + collNodePath.hide() + if self.showCollSpheres: + collNodePath.show() + posX, posY, posZ = self.savePoints[index] + collNodePath.setPos(posX, posY, posZ) + self.collNPList.append(collNodePath) + self.collDict[collNodePath.getName()] = index + self.accept(self.game.uniqueName('enter' + collSphereName), self.handleSavePointCollision) + + def handleSavePointCollision(self, cevent): + savePointName = cevent.getIntoNodePath().getName() + self.lastSavePoint = self.collDict[savePointName] + self.section.sectionMgr.updateActiveSection(self.section.indexNum) + + def getSpawnPoint(self): + if len(self.loadPoints) > 0: + point = self.loadPoints[self.lastSavePoint] + return Point3(point[0], point[1], point[2]) + else: + return Point3(ToonBlitzGlobals.ToonStartingPosition[0], ToonBlitzGlobals.ToonStartingPosition[1], ToonBlitzGlobals.ToonStartingPosition[2]) + + def setupLastSavePointHandle(self): + if len(self.collNPList) > 0: + self.accept('enter' + self.collNPList[-1].getName(), self.handleLastSavePointCollision) + self.gameEndX = self.collNPList[-1].getX(render) + + def handleLastSavePointCollision(self, cevent): + self.game.localToonVictory() diff --git a/toontown/minigame/TwoDStomper.py b/toontown/minigame/TwoDStomper.py new file mode 100755 index 00000000..4f6c1dba --- /dev/null +++ b/toontown/minigame/TwoDStomper.py @@ -0,0 +1,160 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from toontown.minigame import ToonBlitzGlobals +GOING_UP = 1 +GOING_DOWN = 2 +STUCK_DOWN = 3 + +class TwoDStomper(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDStomper') + + def __init__(self, stomperMgr, index, stomperAttribs, model): + self.game = stomperMgr.section.sectionMgr.game + self.index = index + stomperName = 'stomper-' + str(self.index) + self.model = NodePath(stomperName) + self.nodePath = model.copyTo(self.model) + self.ival = None + self.stashCollisionsIval = None + self.removeHeadFloor = 0 + self.stomperState = STUCK_DOWN + self.setupStomper(stomperAttribs) + return + + def destroy(self): + self.game = None + self.ignoreAll() + if self.ival: + self.ival.pause() + del self.ival + self.ival = None + if self.smoke: + self.smoke.removeNode() + del self.smoke + self.smoke = None + if self.stashCollisionsIval: + self.stashCollisionsIval.finish() + del self.stashCollisionsIval + self.stashCollisionsIval = None + for collSolid in self.collSolids: + collSolid.stash() + + self.nodePath.removeNode() + del self.nodePath + self.model.removeNode() + if self.model: + self.model.removeNode() + del self.model + self.model = None + return + + def setupStomper(self, stomperAttribs): + stomperType = stomperAttribs[0] + self.pos = Point3(stomperAttribs[1][0], stomperAttribs[1][1], stomperAttribs[1][2]) + self.period = stomperAttribs[2] + typeAttribs = ToonBlitzGlobals.StomperTypes[stomperType] + self.motionType = typeAttribs[0] + self.scale = typeAttribs[1] + self.headStartZ, self.headEndZ = typeAttribs[2] + self.shaftStartScaleZ, self.shaftEndScaleZ = typeAttribs[3] + self.numCollSolids = typeAttribs[4] + self.stompSound = loader.loadSfx('phase_4/audio/sfx/CHQ_FACT_stomper_small.ogg') + self.model.setPos(self.pos) + self.model.setScale(self.scale) + self.model.find('**/block').setScale(1.0 / self.scale) + self.head = self.model.find('**/head') + self.shaft = self.model.find('**/shaft') + self.collisions = self.model.find('**/stomper_collision') + originalColl = self.model.find('**/stomper_collision') + self.range = self.headEndZ - self.headStartZ + self.collSolids = [] + self.collSolids.append(originalColl) + for i in xrange(self.numCollSolids - 1): + newColl = originalColl.copyTo(self.model) + self.collSolids.append(newColl) + + self.collSolids[-1].reparentTo(self.head) + self.smoke = loader.loadModel('phase_4/models/props/test_clouds') + self.smoke.setZ(self.headEndZ - 1) + self.smoke.setColor(0.8, 0.7, 0.5, 1) + self.smoke.setBillboardPointEye() + self.smoke.setScale(1.0 / self.scale) + self.smoke.setDepthWrite(False) + + def getMotionIval(self): + + def motionFunc(t, self = self): + stickTime = 0.2 + turnaround = 0.95 + t = t % 1 + if t < stickTime: + self.head.setFluidZ(0 + self.headEndZ) + if self.stomperState != STUCK_DOWN: + self.stomperState = STUCK_DOWN + elif t < turnaround: + self.head.setFluidZ((t - stickTime) * -self.range / (turnaround - stickTime) + self.headEndZ) + if self.stomperState != GOING_UP: + self.stomperState = GOING_UP + elif t > turnaround: + self.head.setFluidZ(-self.range + (t - turnaround) * self.range / (1 - turnaround) + self.headEndZ) + if self.stomperState != GOING_DOWN: + self.stomperState = GOING_DOWN + self.checkSquashedToon() + + motionIval = Sequence(LerpFunctionInterval(motionFunc, duration=self.period)) + return motionIval + + def getSmokeTrack(self): + smokeTrack = Sequence(Parallel(LerpScaleInterval(self.smoke, 0.2, Point3(1, 1, 1.5)), LerpColorScaleInterval(self.smoke, 0.4, VBase4(1, 1, 1, 0), VBase4(1, 1, 1, 0.5))), Func(self.smoke.reparentTo, hidden), Func(self.smoke.clearColorScale)) + return smokeTrack + + def adjustShaftScale(self, t): + heightDiff = self.head.getZ() - self.headStartZ + self.shaft.setScale(1, 1, self.shaftStartScaleZ + heightDiff * (self.shaftEndScaleZ - self.shaftStartScaleZ) / self.range) + + def adjustCollSolidHeight(self, t): + heightDiff = self.head.getZ() - self.headStartZ + for i in xrange(1, len(self.collSolids) - 1): + self.collSolids[i].setZ(heightDiff * i / (self.numCollSolids - 1)) + + def start(self, elapsedTime): + if self.ival: + self.ival.pause() + del self.ival + self.ival = None + self.ival = Parallel() + self.ival.append(Sequence(self.getMotionIval(), Func(base.playSfx, self.stompSound, node=self.model, volume=0.3), Func(self.smoke.reparentTo, self.model), self.getSmokeTrack())) + self.ival.append(LerpFunctionInterval(self.adjustShaftScale, duration=self.period)) + self.ival.append(LerpFunctionInterval(self.adjustCollSolidHeight, duration=self.period)) + self.ival.loop() + self.ival.setT(elapsedTime) + return + + def enterPause(self): + if self.ival: + self.ival.pause() + + def exitPause(self): + if self.ival: + self.ival.loop() + + def checkSquashedToon(self): + toonXDiff = (base.localAvatar.getX(render) - self.model.getX(render)) / self.scale + toonZ = base.localAvatar.getZ(render) + headEndZAbs = self.model.getZ(render) + self.headEndZ * self.scale + if toonXDiff > -1.0 and toonXDiff < 1.0 and toonZ > headEndZAbs and toonZ < self.head.getZ(render): + if not base.localAvatar.isStunned: + + def stashCollisions(self = self): + for collSolid in self.collSolids: + collSolid.stash() + + def unstashCollisions(self = self): + for collSolid in self.collSolids: + collSolid.unstash() + + self.stashCollisionsIval = Sequence(Func(stashCollisions), Wait(2.5), Func(unstashCollisions)) + self.stashCollisionsIval.start() + self.game.localToonSquished() diff --git a/toontown/minigame/TwoDStomperMgr.py b/toontown/minigame/TwoDStomperMgr.py new file mode 100755 index 00000000..07257cd1 --- /dev/null +++ b/toontown/minigame/TwoDStomperMgr.py @@ -0,0 +1,56 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.minigame import ToonBlitzGlobals +from toontown.minigame import TwoDStomper + +class TwoDStomperMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDStomperMgr') + + def __init__(self, section, stomperList): + self.section = section + self.stomperList = stomperList + self.load() + + def destroy(self): + self.section = None + while len(self.stompers): + stomper = self.stompers[0] + stomper.destroy() + self.stompers.remove(stomper) + + self.stompers = None + return + + def load(self): + if len(self.stomperList): + self.stompersNP = NodePath('Stompers') + self.stompersNP.reparentTo(self.section.sectionNP) + self.stompers = [] + for index in xrange(len(self.stomperList)): + stomperAttribs = self.stomperList[index] + self.createNewStomper(stomperAttribs) + + def createNewStomper(self, attrib, model = None): + stomperId = self.section.getSectionizedId(len(self.stompers)) + if model == None: + model = self.section.sectionMgr.game.assetMgr.stomper + newStomper = TwoDStomper.TwoDStomper(self, stomperId, attrib, model) + newStomper.model.reparentTo(self.stompersNP) + self.stompers.append(newStomper) + return + + def enterPlay(self, elapsedTime): + for stomper in self.stompers: + stomper.start(elapsedTime) + + def exitPlay(self): + pass + + def enterPause(self): + for stomper in self.stompers: + stomper.enterPause() + + def exitPause(self): + for stomper in self.stompers: + stomper.exitPause() diff --git a/toontown/minigame/TwoDTreasure.py b/toontown/minigame/TwoDTreasure.py new file mode 100755 index 00000000..7d26035a --- /dev/null +++ b/toontown/minigame/TwoDTreasure.py @@ -0,0 +1,115 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from toontown.minigame import ToonBlitzGlobals +from toontown.estate.GardenGlobals import BeanColors +import random + +class TwoDTreasure(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDTreasure') + RADIUS = 1.3 + + def __init__(self, treasureMgr, index, pos, value, isEnemyGenerated, model): + self.game = treasureMgr.section.sectionMgr.game + self.index = index + self.value = value + self.isEnemyGenerated = isEnemyGenerated + center = model.getBounds().getCenter() + center = Point3(0, 0, 0) + treasureName = 'treasure-' + str(index) + model.setScale(2.5) + self.model = NodePath(treasureName) + self.nodePath = model.copyTo(self.model) + self.appearEffect = None + self.flash = None + glowParticle = self.game.assetMgr.particleGlow + self.glowCard = glowParticle.copyTo(self.model) + self.glowCard2 = glowParticle.copyTo(self.glowCard) + self.glowCard.setPos(0, 0.1, 0) + self.glowCard.setColor(model.getColor()) + self.glowCard.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.glowCard2.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.glowScalIval = Sequence(LerpScaleInterval(self.glowCard, 0.4, scale=4.1, startScale=4.9), LerpScaleInterval(self.glowCard, 0.4, scale=4.9, startScale=4.1)) + self.glowScalIval.loop() + self.modelScalIval = Sequence(LerpScaleInterval(self.model, 0.4, scale=1.0, startScale=1.03), LerpScaleInterval(self.model, 0.4, scale=1.03, startScale=1.0)) + self.modelScalIval.loop() + self.sphereName = 'treasureSphere-%s-%s' % (self.game.doId, self.index) + self.collSphere = CollisionSphere(center[0], center[1], center[2], self.RADIUS) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.sphereName) + self.collNode.setIntoCollideMask(WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.model.attachNewNode(self.collNode) + self.collNodePath.hide() + self.accept('enter' + self.sphereName, self.__handleEnterSphere) + self.model.setPos(pos[0] - center[0], 0 - center[1], pos[2] - center[2]) + self.nodePath.flattenLight() + if self.isEnemyGenerated: + self.flash = glowParticle.copyTo(self.model) + self.flash.reparentTo(treasureMgr.treasuresNP) + self.flash.setPos(self.model.getX(), self.model.getY() - 0.2, self.model.getZ()) + self.flash.setScale(6) + self.flash.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.hideTreasure() + return + + def destroy(self): + self.game = None + self.ignoreAll() + if self.glowScalIval: + self.glowScalIval.finish() + del self.glowScalIval + if self.modelScalIval: + self.modelScalIval.finish() + del self.modelScalIval + if self.appearEffect: + self.appearEffect.finish() + del self.appearEffect + if self.flash: + self.flash.removeNode() + del self.flash + self.nodePath.removeNode() + del self.nodePath + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + self.model.removeNode() + del self.model + return + + def setRandomColor(self): + beanIndex = random.randint(0, len(BeanColors) - 1) + colors = BeanColors[beanIndex] + self.model.setColor(colors[0] / 255.0, colors[1] / 255.0, colors[2] / 255.0) + + def __handleEnterSphere(self, cevent): + self.ignoreAll() + self.notify.debug('treasuerGrabbed') + sectionIndex = int(cevent.getIntoNodePath().getName()[-5:-3]) + treasureIndex = int(cevent.getIntoNodePath().getName()[-2:]) + messenger.send('twoDTreasureGrabbed', [sectionIndex, treasureIndex]) + + def hideTreasure(self): + self.model.hide() + self.collNode.setIntoCollideMask(BitMask32(0)) + if self.isEnemyGenerated: + self.flash.hide() + + def showTreasure(self): + self.model.show() + self.collNode.setIntoCollideMask(WallBitmask) + if self.isEnemyGenerated: + self.flash.show() + + def setTreasurePos(self, pos): + self.model.setPos(pos) + if self.isEnemyGenerated: + self.flash.setPos(self.model.getX(), self.model.getY() - 0.2, self.model.getZ()) + + def popupEnemyTreasure(self): + modelFadeIn = LerpFunc(self.model.setAlphaScale, duration=0.5) + flashFadeOut = LerpFunc(self.flash.setAlphaScale, fromData=1, toData=0, duration=0.5) + self.appearEffect = Sequence(Wait(2.4), Func(self.showTreasure), Parallel(modelFadeIn, flashFadeOut, Func(base.playSfx, self.game.assetMgr.sparkleSound))) + self.appearEffect.start() diff --git a/toontown/minigame/TwoDTreasureMgr.py b/toontown/minigame/TwoDTreasureMgr.py new file mode 100755 index 00000000..07b9f1f6 --- /dev/null +++ b/toontown/minigame/TwoDTreasureMgr.py @@ -0,0 +1,65 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.minigame import ToonBlitzGlobals +from toontown.minigame import TwoDTreasure +import random + +class TwoDTreasureMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDTreasureMgr') + + def __init__(self, section, treasureList, enemyList): + self.section = section + self.treasureList = treasureList + self.enemyList = enemyList + self.load() + + def destroy(self): + while len(self.treasures): + treasure = self.treasures[0] + treasure.destroy() + self.treasures.remove(treasure) + + self.treasures = None + self.section = None + return + + def load(self): + if len(self.treasureList): + self.treasuresNP = NodePath('Treasures') + self.treasuresNP.reparentTo(self.section.sectionNP) + self.treasures = [] + for index in xrange(len(self.treasureList)): + treasureAttribs = self.treasureList[index][0] + treasureValue = self.treasureList[index][1] + self.createNewTreasure(treasureAttribs, treasureValue) + + self.enemyTreasures = [] + numPlayers = self.section.sectionMgr.game.numPlayers + pos = Point3(-1, -1, -1) + for index in xrange(len(self.enemyList)): + self.createNewTreasure([pos], numPlayers, isEnemyGenerated=True) + + def createNewTreasure(self, attrib, value, isEnemyGenerated = False, model = None): + treasureId = self.section.getSectionizedId(len(self.treasures)) + if model == None: + model = self.getModel(value, self.section.sectionMgr.game.assetMgr.treasureModelList) + newTreasure = TwoDTreasure.TwoDTreasure(self, treasureId, attrib[0], value, isEnemyGenerated, model) + newTreasure.model.reparentTo(self.treasuresNP) + self.treasures.append(newTreasure) + if isEnemyGenerated: + self.enemyTreasures.append(newTreasure) + return + + def getModel(self, value, modelList): + value -= 1 + model = modelList[value] + if value == 0: + model.setColor(1, 0.8, 0.8, 1) + elif value == 1: + model.setColor(0.8, 1, 0.8, 1) + elif value == 2: + model.setColor(0.9, 0.9, 1, 1) + elif value == 3: + model.setColor(1, 1, 0.6, 1) + return model diff --git a/toontown/minigame/TwoDWalk.py b/toontown/minigame/TwoDWalk.py new file mode 100755 index 00000000..e160dead --- /dev/null +++ b/toontown/minigame/TwoDWalk.py @@ -0,0 +1,13 @@ +from OrthoWalk import * + +class TwoDWalk(OrthoWalk): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDWalk') + BROADCAST_POS_TASK = 'TwoDWalkBroadcastPos' + + def doBroadcast(self, task): + dt = globalClock.getDt() + self.timeSinceLastPosBroadcast += dt + if self.timeSinceLastPosBroadcast >= self.broadcastPeriod: + self.timeSinceLastPosBroadcast = 0 + self.lt.cnode.broadcastPosHprFull() + return Task.cont diff --git a/toontown/minigame/VineBat.py b/toontown/minigame/VineBat.py new file mode 100755 index 00000000..988e387f --- /dev/null +++ b/toontown/minigame/VineBat.py @@ -0,0 +1,107 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * +import VineGameGlobals +from direct.interval.SoundInterval import SoundInterval + +class VineBat(NodePath, DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('VineBat') + notify.setDebug(True) + RADIUS = 1.7 + + def __init__(self, batIndex, timeToTraverseField): + NodePath.__init__(self, 'VineBat') + DirectObject.__init__(self) + pos = Point3(0, 0, 0) + serialNum = 0 + gameId = 0 + self.serialNum = serialNum + self.batIndex = batIndex + self.timeToTraverseField = timeToTraverseField + gameAssets = loader.loadModel('phase_4/models/minigames/vine_game') + bat3 = gameAssets.find('**/bat3') + bat2 = gameAssets.find('**/bat2') + bat1 = gameAssets.find('**/bat__1') + seqNode = SequenceNode('bat') + seqNode.addChild(bat1.node()) + seqNode.addChild(bat2.node()) + seqNode.addChild(bat3.node()) + seqNode.setFrameRate(12) + seqNode.pingpong(False) + self.batModel = self.attachNewNode(seqNode) + self.batModel.reparentTo(self) + gameAssets.removeNode() + self.batModelIcon = self.attachNewNode('batIcon') + self.batModel.copyTo(self.batModelIcon) + regularCamMask = BitMask32.bit(0) + self.batModelIcon.hide(regularCamMask) + self.batModelIcon.show(VineGameGlobals.RadarCameraBitmask) + self.batModelIcon.setScale(0.55) + self.batModel.setScale(0.15) + self.setPos(-100, 0, 0) + center = Point3(0, 0, 0) + self.sphereName = 'batSphere-%s-%s' % (gameId, self.serialNum) + self.collSphere = CollisionSphere(center[0], center[1], center[2], self.RADIUS) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.sphereName) + self.collNode.setIntoCollideMask(VineGameGlobals.SpiderBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.attachNewNode(self.collNode) + self.collNodePath.hide() + self.accept('enter' + self.sphereName, self.__handleEnterSphere) + self.screechSfx = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_bat_shriek_3.ogg') + self.flySfx = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_bat_flying_lp.ogg') + self.oldCutoffDistance = base.sfxPlayer.getCutoffDistance() + base.sfxPlayer.setCutoffDistance(240) + self.soundInterval = SoundInterval(self.flySfx, node=self, listenerNode=base.localAvatar, seamlessLoop=True, volume=0.5, cutOff=240) + self.reparentTo(render) + self.startedFlying = False + self.warnedForThisLap = False + startX = VineGameGlobals.VineXIncrement * VineGameGlobals.NumVines + endX = -VineGameGlobals.VineXIncrement + self.velocity = float(startX - endX) / self.timeToTraverseField + self.warnDistance = 35 + + def destroy(self): + self.ignoreAll() + self.batModel.removeNode() + del self.batModel + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + self.removeNode() + self.soundInterval.finish() + del self.soundInterval + del self.flySfx + del self.screechSfx + base.sfxPlayer.setCutoffDistance(self.oldCutoffDistance) + + def __handleEnterSphere(self, collEntry): + self.ignoreAll() + self.notify.debug('treasuerGrabbed') + messenger.send('VineBatGrabbed', [self.serialNum]) + + def showGrab(self): + self.reparentTo(hidden) + self.collNode.setIntoCollideMask(BitMask32(0)) + + def startFlying(self): + self.startedFlying = True + self.soundInterval.loop() + + def stopFlying(self): + self.flySfx.setVolume(0) + self.soundInterval.finish() + + def startLap(self): + self.warnedForThisLap = False + + def checkScreech(self): + distance = base.localAvatar.getDistance(self) + if distance < self.warnDistance: + if self.getX(render) > base.localAvatar.getX(render): + if not self.warnedForThisLap: + self.screechSfx.play() + self.warnedForThisLap = True diff --git a/toontown/minigame/VineGameGlobals.py b/toontown/minigame/VineGameGlobals.py new file mode 100755 index 00000000..3c7d2643 --- /dev/null +++ b/toontown/minigame/VineGameGlobals.py @@ -0,0 +1,270 @@ +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import BitMask32 +NumVines = 20 +GameDuration = 70 +ShowScoresDuration = 4.0 +VineStartingT = 0.25 +VineFellDownT = 0.1 +EndlessGame = False +BonusPerSecondLeft = 0.4 +JumpTimeBuffer = 0.5 +SpiderBitmask = ToontownGlobals.CatchGameBitmask +TreasureBitmask = ToontownGlobals.PieBitmask +VineXIncrement = 30 +VineHeight = 30 +BatMaxHeight = 28 +BatMinHeight = 10 +RadarCameraBitmask = BitMask32.bit(3) +CourseSections = (((20, + 30, + 4, + 0), + (19, + 39, + 3.1, + 0), + (18, + 41, + 4, + 0), + (19, + 38, + 3.2, + 0), + (20, + 30, + 6, + 0)), + ((20, + 30, + 3, + 0), + (18, + 40, + 4.1, + 0), + (19, + 31, + 5.1, + 0), + (18, + 41, + 4.2, + 0), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (15, + 39, + 4.1, + 0), + (19, + 29, + 5, + 0), + (16, + 38, + 4.2, + 0), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (18, + 36, + 4.1, + 0), + (19, + 30, + 5, + 9), + (18, + 38, + 4.2, + 0), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (18, + 15, + 4.1, + 0), + (19, + 30, + 5, + 11), + (18, + 16, + 4.2, + 0), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (18, + 11, + 4.1, + 0), + (15, + 12, + 5, + 0), + (18, + 16, + 4.2, + 0), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (15, + 39, + 4.1, + 13), + (19, + 29, + 5, + 0), + (16, + 38, + 4.2, + 0), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (18, + 26, + 4.1, + 9), + (19, + 30, + 5, + 0), + (18, + 28, + 4.2, + 12), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (15, + 26, + 4.1, + 9), + (19, + 30, + 5, + 0), + (15, + 28, + 4.2, + 12), + (20, + 30, + 5, + 0)), + ((15, + 50, + 4, + 0), + (15, + 40, + 4.1, + 0), + (19, + 40, + 5, + 0), + (19, + 28, + 4.2, + 0), + (20, + 30, + 5, + 0))) +CourseWeights = {ToontownGlobals.ToontownCentral: ((0, 25), + (1, 25), + (2, 25), + (3, 25)), + ToontownGlobals.DonaldsDock: ((1, 25), + (2, 25), + (3, 25), + (4, 25)), + ToontownGlobals.DaisyGardens: ((2, 25), + (3, 25), + (4, 25), + (5, 25)), + ToontownGlobals.MinniesMelodyland: ((3, 25), + (4, 25), + (5, 25), + (6, 25)), + ToontownGlobals.TheBrrrgh: ((4, 25), + (5, 25), + (6, 25), + (7, 25)), + ToontownGlobals.DonaldsDreamland: ((4, 20), + (5, 20), + (6, 20), + (7, 20), + (8, 20))} +BaseBonusOnEndVine = {ToontownGlobals.ToontownCentral: 4, + ToontownGlobals.DonaldsDock: 5, + ToontownGlobals.DaisyGardens: 6, + ToontownGlobals.MinniesMelodyland: 7, + ToontownGlobals.TheBrrrgh: 8, + ToontownGlobals.DonaldsDreamland: 9} +BatInfo = {ToontownGlobals.ToontownCentral: ((60, 0, 0.35),), + ToontownGlobals.DonaldsDock: ((60, 0, 0.25), (30, 30)), + ToontownGlobals.DaisyGardens: ((60, 0, 0.25), (15, 30)), + ToontownGlobals.MinniesMelodyland: ((60, 0, 0.25), (10, 25)), + ToontownGlobals.TheBrrrgh: ((60, 0, 0.25), (30, 30), (30, 20)), + ToontownGlobals.DonaldsDreamland: ((60, 0, 0.25), (30, 30), (10, 20))} +SpiderLimits = {ToontownGlobals.ToontownCentral: 1, + ToontownGlobals.DonaldsDock: 2, + ToontownGlobals.DaisyGardens: 2, + ToontownGlobals.MinniesMelodyland: 3, + ToontownGlobals.TheBrrrgh: 3, + ToontownGlobals.DonaldsDreamland: 4} + +def getNumSpidersInSection(sectionIndex): + if sectionIndex < 0 or sectionIndex >= len(CourseSections): + return 0 + numSpiders = 0 + for vine in CourseSections[sectionIndex]: + if vine[3]: + numSpiders += 1 + + return numSpiders diff --git a/toontown/minigame/VineHeadFrame.py b/toontown/minigame/VineHeadFrame.py new file mode 100755 index 00000000..815911b8 --- /dev/null +++ b/toontown/minigame/VineHeadFrame.py @@ -0,0 +1,35 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from toontown.toon import ToonHead + +class VineHeadFrame(DirectFrame): + + def __init__(self, av = None, color = Vec4(1, 1, 1, 1), *args, **kwargs): + self.panelGeom = DGG.getDefaultDialogGeom() + opts = {'relief': None, + 'geom': self.panelGeom, + 'geom_scale': (0.5, 1, 0.5), + 'pos': (0, 0, 0)} + opts.update(kwargs) + apply(DirectFrame.__init__, (self,) + args, opts) + self.initialiseoptions(VineHeadFrame) + if av: + self.setAv(av) + self.setScale(0.1) + self.setTransparency(0) + return + + def setAv(self, av): + self.head = self.stateNodePath[0].attachNewNode('head', 20) + self.head.setPosHprScale(0, -0.5, -0.09, 180.0, 0.0, 0.0, 0.2, 0.2, 0.2) + self.headModel = ToonHead.ToonHead() + self.headModel.setupHead(av.style, forGui=1) + self.headModel.reparentTo(self.head) + + def destroy(self): + self.headModel.delete() + del self.headModel + self.head.removeNode() + del self.head + DirectFrame.destroy(self) diff --git a/toontown/minigame/VineSpider.py b/toontown/minigame/VineSpider.py new file mode 100755 index 00000000..d4270f2e --- /dev/null +++ b/toontown/minigame/VineSpider.py @@ -0,0 +1,67 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * +import VineGameGlobals + +class VineSpider(NodePath, DirectObject): + RADIUS = 1.7 + + def __init__(self): + NodePath.__init__(self, 'VineSpider') + DirectObject.__init__(self) + pos = Point3(0, 0, 0) + serialNum = 0 + gameId = 0 + self.serialNum = serialNum + gameAssets = loader.loadModel('phase_4/models/minigames/vine_game') + spider2 = gameAssets.find('**/spider_3') + spider1 = gameAssets.find('**/spider_2') + seqNode = SequenceNode('spider') + seqNode.addChild(spider1.node()) + seqNode.addChild(spider2.node()) + seqNode.setFrameRate(2) + seqNode.loop(False) + self.spiderModel = self.attachNewNode(seqNode) + self.spiderModel.reparentTo(self) + gameAssets.removeNode() + self.spiderModelIcon = self.attachNewNode('spiderIcon') + self.spiderModel.copyTo(self.spiderModelIcon) + regularCamMask = BitMask32.bit(0) + self.spiderModelIcon.hide(regularCamMask) + self.spiderModelIcon.show(VineGameGlobals.RadarCameraBitmask) + self.spiderModel.setScale(0.2) + self.spiderModelIcon.setScale(0.75) + self.setPos(-100, 0, 0) + center = Point3(0, 0, 0) + self.sphereName = 'spiderSphere-%s-%s' % (gameId, self.serialNum) + self.collSphere = CollisionSphere(center[0], center[1], center[2], self.RADIUS) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.sphereName) + self.collNode.setIntoCollideMask(VineGameGlobals.SpiderBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.attachNewNode(self.collNode) + self.collNodePath.hide() + self.accept('enter' + self.sphereName, self.__handleEnterSphere) + self.reparentTo(render) + + def destroy(self): + self.ignoreAll() + self.spiderModel.removeNode() + del self.spiderModel + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + self.removeNode() + + def __handleEnterSphere(self, collEntry): + print 'VineSpider.__handleEnterSphere' + print collEntry + self.ignoreAll() + self.notify.debug('treasuerGrabbed') + messenger.send('VineSpiderGrabbed', [self.serialNum]) + + def showGrab(self): + self.reparentTo(hidden) + self.collNode.setIntoCollideMask(BitMask32(0)) diff --git a/toontown/minigame/VineTreasure.py b/toontown/minigame/VineTreasure.py new file mode 100755 index 00000000..9adac249 --- /dev/null +++ b/toontown/minigame/VineTreasure.py @@ -0,0 +1,45 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal + +class VineTreasure(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('VineTreasure') + RADIUS = 1.7 + + def __init__(self, model, pos, serialNum, gameId): + self.serialNum = serialNum + center = model.getBounds().getCenter() + center = Point3(0, 0, 0) + self.nodePath = model.copyTo(render) + self.nodePath.setPos(pos[0] - center[0], 0 - center[1], pos[2] - center[2]) + self.nodePath.setScale(0.25) + self.sphereName = 'treasureSphere-%s-%s' % (gameId, self.serialNum) + self.collSphere = CollisionSphere(center[0], center[1], center[2], self.RADIUS) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.sphereName) + self.collNode.setIntoCollideMask(WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = render.attachNewNode(self.collNode) + self.collNodePath.setPos(pos[0] - center[0], 0 - center[1], pos[2] - center[2]) + self.collNodePath.hide() + self.accept('enter' + self.sphereName, self.__handleEnterSphere) + self.nodePath.flattenLight() + + def destroy(self): + self.ignoreAll() + self.nodePath.removeNode() + del self.nodePath + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + + def __handleEnterSphere(self, collEntry): + self.ignoreAll() + self.notify.debug('treasuerGrabbed') + messenger.send('VineTreasureGrabbed', [self.serialNum]) + + def showGrab(self): + self.nodePath.hide() + self.collNodePath.hide() + self.collNode.setIntoCollideMask(BitMask32(0)) diff --git a/toontown/minigame/__init__.py b/toontown/minigame/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/parties/ActivityBase.py b/toontown/parties/ActivityBase.py new file mode 100755 index 00000000..29bef7a8 --- /dev/null +++ b/toontown/parties/ActivityBase.py @@ -0,0 +1,17 @@ + + +class ActivityBase: + + def __init__(self, activityId, x, y, h): + self.activityId = activityId + self.x = x + self.y = y + self.h = h + + def __str__(self): + string = ' 12: + newYear += 1 + newMonth -= 12 + + while newMonth < 1: + if newYear - 1 > 2002: + newMonth += 12 + newYear -= 1 + else: + newMonth += 1 + + self.curDate = datetime(newYear, newMonth, 1, self.curDate.time().hour, self.curDate.time().minute, self.curDate.time().second, self.curDate.time().microsecond, self.curDate.tzinfo) + self.monthLabel['text'] = (TTLocalizer.Months[self.curDate.month],) + self.yearLabel['text'] = (str(self.curDate.year),) + startTime = globalClock.getRealTime() + self.changeDateForGuiDays() + endTime = globalClock.getRealTime() + self.notify.debug('changeDate took %f seconds' % (endTime - startTime)) + self.updateSelectedDate() + if monthChange != 0: + if self.onlyFutureMonthsClickable and newMonth == self.startDate.month and newYear == self.startDate.year: + self.monthLeftArrow.hide() + + def __doMonthLeft(self): + self.changeMonth(-1) + + def __doMonthRight(self): + self.monthLeftArrow.show() + self.changeMonth(1) + + def destroy(self): + self.ignoreAll() + self.dayClickCallback = None + self.monthLeftArrow.destroy() + self.monthRightArrow.destroy() + for day in self.guiDays: + if day is not None: + day.destroy() + day = None + + self.filterList.destroy() + DirectFrame.destroy(self) + + def clickedOnDay(self, dayDate): + self.lastSelectedDate = dayDate + self.updateSelectedDate() + + def updateSelectedDate(self): + if self.lastSelectedDate: + for oneGuiDay in self.guiDays: + oneGuiDay.updateSelected(oneGuiDay.myDate.date() == self.lastSelectedDate) + + def clearSelectedDay(self): + for oneGuiDay in self.guiDays: + oneGuiDay.updateSelected(False) + + def filterChanged(self): + newFilter = self.filterList.getSelectedIndex() + for guiDay in self.guiDays: + guiDay.changeFilter(newFilter) diff --git a/toontown/parties/Cannon.py b/toontown/parties/Cannon.py new file mode 100755 index 00000000..2b66b1c8 --- /dev/null +++ b/toontown/parties/Cannon.py @@ -0,0 +1,250 @@ +import math +from panda3d.core import * +from direct.interval.MetaInterval import Sequence, Parallel +from direct.interval.FunctionInterval import Func +from direct.interval.LerpInterval import LerpScaleInterval, LerpColorScaleInterval +from direct.showbase.PythonUtil import bound +from toontown.toon import ToonHead +from toontown.minigame.CannonGameGlobals import * +from toontown.toonbase import ToontownGlobals +from toontown.parties.PartyUtils import toRadians, calcVelocity +from direct.showbase.PythonUtil import StackTrace +from otp.nametag.NametagFloat3d import NametagFloat3d +from otp.nametag.Nametag import Nametag +CANNON_ROTATION_MIN = -70 +CANNON_ROTATION_MAX = 70 +INITIAL_VELOCITY = 80.0 +CANNON_BARREL_TOONHEAD_Y = 6.0 + +class Cannon: + notify = directNotify.newCategory('DistributedPartyCannon') + + def __init__(self, parent, pos = Point3(0, 0, 0)): + self.__previousRotation = 0.0 + self.__previousAngle = 0.0 + self._rotation = 0.0 + self._angle = 0.0 + self._position = pos + self._parent = parent + self.parentNode = None + self.cannonNode = None + self.barrelNode = None + self.sndCannonMove = None + self.sndCannonFire = None + self.collSphere = None + self.collNode = None + self.collNodePath = None + self.toonInside = None + self.toonHead = None + self.toonOriginalScale = 0.0 + self.toonParentNode = None + return + + def reset(self): + self.setRotation(0) + self.setAngle((CANNON_ANGLE_MIN + CANNON_ANGLE_MAX * 0.5) * 0.5) + self.parentNode.setPos(self._position) + self.updateModel() + + def load(self, nodeName): + self.parentNode = NodePath(nodeName) + self.cannonNode = loader.loadModel('phase_4/models/minigames/toon_cannon') + self.cannonNode.reparentTo(self.parentNode) + self.barrelNode = self.cannonNode.find('**/cannon') + self.shadowNode = self.cannonNode.find('**/square_drop_shadow') + self.smokeNode = loader.loadModel('phase_4/models/props/test_clouds') + self.smokeNode.setBillboardPointEye() + self.sndCannonMove = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.ogg') + self.sndCannonFire = base.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.ogg') + self.collSphere = CollisionSphere(0, 0, 0, self.getSphereRadius()) + self.collSphere.setTangible(1) + self.collNode = CollisionNode(self.getCollisionName()) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.parentNode.attachNewNode(self.collNode) + + def unload(self): + self.__cleanupToonInside() + if self.cannonNode: + self.cannonNode.removeNode() + self.cannonNode = None + if self.smokeNode: + self.smokeNode.removeNode() + self.smokeNode = None + del self.sndCannonMove + del self.sndCannonFire + del self._position + self.ignoreAll() + return + + def updateModel(self, rotation = None, angle = None): + if rotation: + self.rotation = rotation + if angle: + self.angle = angle + self.cannonNode.setHpr(self._rotation, 0, 0) + self.barrelNode.setHpr(0, self._angle, 0) + maxP = 90 + newP = self.barrelNode.getP() + yScale = 1 - 0.5 * float(newP) / maxP + self.shadowNode.setScale(1, yScale, 1) + + def playFireSequence(self): + self.smokeNode.reparentTo(self.barrelNode) + self.smokeNode.setPos(0, 6, -3) + self.smokeNode.setScale(0.5) + self.smokeNode.wrtReparentTo(render) + track = Sequence(Parallel(LerpScaleInterval(self.smokeNode, 0.5, 3), LerpColorScaleInterval(self.smokeNode, 0.5, Vec4(2, 2, 2, 0))), Func(self.smokeNode.reparentTo, hidden), Func(self.smokeNode.clearColorScale)) + base.playSfx(self.sndCannonFire) + track.start() + + def loopMovingSound(self): + base.playSfx(self.sndCannonMove, looping=1) + + def stopMovingSound(self): + self.sndCannonMove.stop() + + def show(self): + self.reset() + self.parentNode.reparentTo(self._parent) + + def hide(self): + self.parentNode.reparentTo(hidden) + + def placeToonInside(self, toon): + self.__setToonInside(toon) + self.__createToonHead(toon) + self.__placeToon() + + def __setToonInside(self, toon): + self.toonInside = toon + toonName = 'None' + if toon: + toonName = toon.getName() + self.notify.debug('__setToonInside self.toonInside=%s\nstack=%s' % (toonName, StackTrace().compact())) + self.toonInside.stopSmooth() + self.toonOriginalScale = toon.getScale() + toon.useLOD(1000) + self.toonParentNode = render.attachNewNode('toonOriginChange') + self.toonInside.wrtReparentTo(self.toonParentNode) + self.toonInside.setPosHpr(0, 0, -(self.toonInside.getHeight() / 2.0), 0, -90, 0) + + def __createToonHead(self, toon): + self.toonHead = ToonHead.ToonHead() + self.toonHead.setupHead(toon.style) + self.toonHead.reparentTo(hidden) + tag = NametagFloat3d() + tag.setContents(Nametag.CSpeech | Nametag.CThought) + tag.setBillboardOffset(0) + tag.setAvatar(self.toonHead) + toon.nametag.addNametag(tag) + tagPath = self.toonHead.attachNewNode(tag) + tagPath.setPos(0, 0, 1) + self.toonHead.tag = tag + + def __placeToon(self): + self.showToonHead() + self.toonHead.setPosHpr(0, CANNON_BARREL_TOONHEAD_Y, 0, 0, -45, 0) + scale = self.toonOriginalScale + self.toonHead.setScale(render, scale[0], scale[1], scale[2]) + self.toonParentNode.reparentTo(hidden) + self.toonParentNode.setPos(self.getToonFirePos()) + + def showToonHead(self): + if self.toonHead: + self.toonHead.startBlink() + self.toonHead.startLookAround() + self.toonHead.reparentTo(self.barrelNode) + + def hideToonHead(self): + if self.toonHead: + self.toonHead.stopBlink() + self.toonHead.stopLookAroundNow() + self.toonHead.reparentTo(hidden) + + def removeToonReadyToFire(self): + toon = self.toonInside + self.toonInside.wrtReparentTo(render) + y = self.toonHead.getY(self.barrelNode) - (self.toonInside.getHeight() - self.toonHead.getHeight()) + self.toonInside.setPosHpr(self.barrelNode, 0, y, 0, 0, -90, 0) + return self.__removeToon() + + def removeToonDidNotFire(self): + self.toonInside.reparentTo(self.cannonNode) + self.toonInside.setPos(2, -4, 0) + self.toonInside.wrtReparentTo(render) + return self.__removeToon() + + def __removeToon(self): + self.stopMovingSound() + toonNode = self.toonParentNode + self.toonInside.resetLOD() + self.hideToonHead() + self.__cleanupToonInside() + return toonNode + + def __cleanupToonInside(self): + toonName = 'None' + if self.toonInside: + toonName = self.toonInside.getName() + self.notify.debug('__cleanupToonInside self.toonInside=%s\nstack=%s' % (toonName, StackTrace().compact())) + if self.toonHead != None: + self.hideToonHead() + if hasattr(self.toonInside, 'nametag'): + self.toonInside.nametag.removeNametag(self.toonHead.tag) + self.toonHead.delete() + self.toonHead = None + self.toonInside = None + self.toonParentNode = None + return + + def getSphereRadius(self): + return 1.5 + + def getCollisionName(self): + return self.parentNode.getName() + 'Collision' + + def getEnterCollisionName(self): + return 'enter' + self.getCollisionName() + + def isToonInside(self): + return self.toonHead != None + + def getToonInside(self): + return self.toonInside + + def setRotation(self, rotation): + self.__previousRotation = self._rotation + self._rotation = bound(rotation, CANNON_ROTATION_MIN, CANNON_ROTATION_MAX) + + def getRotation(self): + return self._rotation + + def setAngle(self, angle): + self.__previousAngle = self._angle + self._angle = bound(angle, CANNON_ANGLE_MIN, CANNON_ANGLE_MAX) + + def getAngle(self): + return self._angle + + def hasMoved(self): + return self.__previousRotation != self._rotation or self.__previousAngle != self._angle + + def getBarrelHpr(self, node): + return self.barrelNode.getHpr(node) + + def getToonFirePos(self): + if self.toonHead: + return self.toonHead.getPos(render) + return Point3.zero() + + def getToonFireHpr(self): + if self.toonHead: + return self.toonHead.getHpr(render) + return Point3.zero() + + def getToonFireVel(self): + hpr = self.barrelNode.getHpr(render) + rotation = toRadians(hpr[0]) + angle = toRadians(hpr[1]) + return calcVelocity(rotation, angle, initialVelocity=INITIAL_VELOCITY) diff --git a/toontown/parties/CannonGui.py b/toontown/parties/CannonGui.py new file mode 100755 index 00000000..0cb9e4fc --- /dev/null +++ b/toontown/parties/CannonGui.py @@ -0,0 +1,222 @@ +from direct.showbase.DirectObject import DirectObject +from direct.gui.DirectGui import DirectFrame, DirectButton +import direct.gui.DirectGuiGlobals as DGG +from toontown.parties import PartyUtils + +class CannonGui(DirectObject): + notify = directNotify.newCategory('CannonGui') + FIRE_KEY = 'control' + UP_KEY = 'arrow_up' + DOWN_KEY = 'arrow_down' + LEFT_KEY = 'arrow_left' + RIGHT_KEY = 'arrow_right' + FIRE_PRESSED = 'cannongui_fire_pressed' + + def __init__(self): + self.__loaded = False + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.__aimPad = None + self.__timerPad = None + return + + def load(self): + if self.__loaded: + return + self.__timerPad = PartyUtils.getNewToontownTimer() + guiModel = 'phase_4/models/gui/cannon_game_gui' + guiNode = loader.loadModel(guiModel) + self.__aimPad = DirectFrame(image=guiNode.find('**/CannonFire_PAD'), relief=None, pos=(0.7, 0, -0.553333), scale=0.8) + guiNode.removeNode() + self.fireButton = DirectButton(parent=self.__aimPad, image=((guiModel, '**/Fire_Btn_UP'), (guiModel, '**/Fire_Btn_DN'), (guiModel, '**/Fire_Btn_RLVR')), relief=None, pos=(0.0115741, 0, 0.00505051), scale=1.0, command=self.__firePressed) + self.upButton = DirectButton(parent=self.__aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0115741, 0, 0.221717)) + self.downButton = DirectButton(parent=self.__aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0136112, 0, -0.210101), image_hpr=(0, 0, 180)) + self.leftButton = DirectButton(parent=self.__aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(-0.199352, 0, -0.000505269), image_hpr=(0, 0, -90)) + self.rightButton = DirectButton(parent=self.__aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.219167, 0, -0.00101024), image_hpr=(0, 0, 90)) + self.__aimPad.setColor(1, 1, 1, 0.9) + + def bindButton(button, upHandler, downHandler): + button.bind(DGG.B1PRESS, lambda x, handler = upHandler: handler()) + button.bind(DGG.B1RELEASE, lambda x, handler = downHandler: handler()) + + bindButton(self.upButton, self.__upPressed, self.__upReleased) + bindButton(self.downButton, self.__downPressed, self.__downReleased) + bindButton(self.leftButton, self.__leftPressed, self.__leftReleased) + bindButton(self.rightButton, self.__rightPressed, self.__rightReleased) + self.__loaded = True + return + + def unload(self): + self.ignoreAll() + if not self.__loaded: + return + self.disable() + self.upButton.unbind(DGG.B1PRESS) + self.upButton.unbind(DGG.B1RELEASE) + self.downButton.unbind(DGG.B1PRESS) + self.downButton.unbind(DGG.B1RELEASE) + self.leftButton.unbind(DGG.B1PRESS) + self.leftButton.unbind(DGG.B1RELEASE) + self.rightButton.unbind(DGG.B1PRESS) + self.rightButton.unbind(DGG.B1RELEASE) + self.fireButton.destroy() + self.__aimPad.destroy() + del self.__aimPad + del self.fireButton + del self.upButton + del self.downButton + del self.leftButton + del self.rightButton + self.__timerPad.destroy() + del self.__timerPad + self.__loaded = False + + def enable(self, timer = 0): + self.__aimPad.show() + base.setCellsAvailable([base.bottomCells[2], base.bottomCells[3]], 0) + base.setCellsAvailable([base.rightCells[1]], 0) + if timer > 0: + self.__timerPad.setTime(timer) + self.__timerPad.countdown(timer) + self.__timerPad.show() + self.enableKeys() + + def disable(self): + self.__aimPad.hide() + base.setCellsAvailable([base.bottomCells[2], base.bottomCells[3]], 1) + base.setCellsAvailable([base.rightCells[1]], 1) + self.__timerPad.hide() + self.disableKeys() + + def enableKeys(self): + self.enableAimKeys() + self.enableFireKey() + + def disableKeys(self): + self.__aimPad.hide() + self.disableAimKeys() + self.disableFireKey() + + def enableAimKeys(self): + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.accept(self.UP_KEY, self.__upKeyPressed) + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + + def disableAimKeys(self): + self.ignore(self.UP_KEY) + self.ignore(self.DOWN_KEY) + self.ignore(self.LEFT_KEY) + self.ignore(self.RIGHT_KEY) + messenger.send(self.UP_KEY + '-up') + messenger.send(self.DOWN_KEY + '-up') + messenger.send(self.LEFT_KEY + '-up') + messenger.send(self.RIGHT_KEY + '-up') + self.ignore(self.UP_KEY + '-up') + self.ignore(self.DOWN_KEY + '-up') + self.ignore(self.LEFT_KEY + '-up') + self.ignore(self.RIGHT_KEY + '-up') + + def enableFireKey(self): + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + + def disableFireKey(self): + self.ignore(self.FIRE_KEY) + self.ignore(self.FIRE_KEY + '-up') + + def __fireKeyPressed(self): + self.ignore(self.FIRE_KEY) + self.accept(self.FIRE_KEY + '-up', self.__fireKeyReleased) + self.__firePressed() + + def __upKeyPressed(self): + self.ignore(self.UP_KEY) + self.accept(self.UP_KEY + '-up', self.__upKeyReleased) + self.__upPressed() + + def __downKeyPressed(self): + self.ignore(self.DOWN_KEY) + self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased) + self.__downPressed() + + def __leftKeyPressed(self): + self.ignore(self.LEFT_KEY) + self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased) + self.__leftPressed() + + def __rightKeyPressed(self): + self.ignore(self.RIGHT_KEY) + self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased) + self.__rightPressed() + + def __fireKeyReleased(self): + self.ignore(self.FIRE_KEY + '-up') + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + + def __leftKeyReleased(self): + self.ignore(self.LEFT_KEY + '-up') + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.__leftReleased() + + def __rightKeyReleased(self): + self.ignore(self.RIGHT_KEY + '-up') + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__rightReleased() + + def __upKeyReleased(self): + self.ignore(self.UP_KEY + '-up') + self.accept(self.UP_KEY, self.__upKeyPressed) + self.__upReleased() + + def __downKeyReleased(self): + self.ignore(self.DOWN_KEY + '-up') + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.__downReleased() + + def __upPressed(self): + self.notify.debug('up pressed') + self.upPressed = self.__enterControlActive(self.upPressed) + + def __downPressed(self): + self.notify.debug('down pressed') + self.downPressed = self.__enterControlActive(self.downPressed) + + def __leftPressed(self): + self.notify.debug('left pressed') + self.leftPressed = self.__enterControlActive(self.leftPressed) + + def __rightPressed(self): + self.notify.debug('right pressed') + self.rightPressed = self.__enterControlActive(self.rightPressed) + + def __upReleased(self): + self.notify.debug('up released') + self.upPressed = self.__exitControlActive(self.upPressed) + + def __downReleased(self): + self.notify.debug('down released') + self.downPressed = self.__exitControlActive(self.downPressed) + + def __leftReleased(self): + self.notify.debug('left released') + self.leftPressed = self.__exitControlActive(self.leftPressed) + + def __rightReleased(self): + self.notify.debug('right released') + self.rightPressed = self.__exitControlActive(self.rightPressed) + + def __firePressed(self): + self.notify.debug('fire pressed') + messenger.send(CannonGui.FIRE_PRESSED) + + def __enterControlActive(self, control): + return control + 1 + + def __exitControlActive(self, control): + return max(0, control - 1) diff --git a/toontown/parties/DecorBase.py b/toontown/parties/DecorBase.py new file mode 100755 index 00000000..44d2887c --- /dev/null +++ b/toontown/parties/DecorBase.py @@ -0,0 +1,17 @@ + + +class DecorBase: + + def __init__(self, decorId, x, y, h): + self.decorId = decorId + self.x = x + self.y = y + self.h = h + + def __str__(self): + string = 'decorId=%d ' % self.decorId + string += '(%d,%d,%d) ' % (self.x, self.y, self.h) + return string + + def __repr__(self): + return self.__str__() diff --git a/toontown/parties/Decoration.py b/toontown/parties/Decoration.py new file mode 100755 index 00000000..b24663ec --- /dev/null +++ b/toontown/parties/Decoration.py @@ -0,0 +1,291 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.actor import Actor +from toontown.toonbase import ToontownGlobals +import random +from toontown.parties import PartyGlobals +from toontown.parties.PartyUtils import getCenterPosFromGridSize + +class Decoration(NodePath): + notify = directNotify.newCategory('Decoration') + + def __init__(self, name, x, y, h): + NodePath.__init__(self, name) + self.name = name + decorId = PartyGlobals.DecorationIds.fromString(name) + centerX, centerY = getCenterPosFromGridSize(x, y, PartyGlobals.DecorationInformationDict[decorId]['gridsize']) + self.setPos(centerX, centerY, 0.0) + self.setH(h) + if self.name == 'CakeTower': + self.partyCake = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_cakeTower') + tntSeqNode = self.partyCake.find('**/seqNode_tnt').node() + tntSeqNode.setFrameRate(20) + self.partyCake.reparentTo(self) + elif self.name == 'BannerJellyBean': + partyBannerModel = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_bannerJellybean_model') + banner = [] + banner1 = partyBannerModel.find('**/banner1') + banner2 = partyBannerModel.find('**/banner2') + temp = NodePath('Empty') + banner1.reparentTo(temp) + banner2.reparentTo(temp) + banner.append(banner1) + banner.append(banner2) + self.partyBanner = Actor.Actor(partyBannerModel, {'float': 'phase_13/models/parties/tt_m_ara_pty_bannerJellybean'}) + bannerSeqNodeParent = self.partyBanner.find('**/bannerJoint') + bannerSeqNode = SequenceNode('banner') + for bannerNode in banner: + bannerSeqNode.addChild(bannerNode.node()) + + temp.detachNode() + del temp + bannerSeqNodeParent.attachNewNode(bannerSeqNode) + bannerSeqNode.setFrameRate(4) + bannerSeqNode.loop(True) + bannerSeqNode.setPlayRate(1) + balloonLeft = self.partyBanner.find('**/balloonsLMesh') + balloonRight = self.partyBanner.find('**/balloonsRMesh') + balloonLeft.setBillboardAxis() + balloonRight.setBillboardAxis() + balloonLeftLocator = self.partyBanner.find('**/balloonJointL') + balloonRightLocator = self.partyBanner.find('**/balloonJointR') + balloonLeft.reparentTo(balloonLeftLocator) + balloonRight.reparentTo(balloonRightLocator) + self.partyBanner.loop('float') + self.partyBanner.reparentTo(self) + elif self.name == 'GagGlobe': + self.partyGlobe = Actor.Actor('phase_13/models/parties/tt_m_ara_pty_gagGlobe_model', {'idle': 'phase_13/models/parties/tt_m_ara_pty_gagGlobe'}) + self.partyGlobe.setBillboardAxis() + confettiLocator = self.partyGlobe.find('**/uvj_confetti') + confettiMesh = self.partyGlobe.find('**/innerGlobeMesh') + confettiMesh.setTexProjector(confettiMesh.findTextureStage('default'), confettiLocator, self.partyGlobe) + collisionMesh = self.partyGlobe.find('**/collisionMesh') + collisionMesh.hide() + self.globeSphere = CollisionSphere(confettiMesh.getBounds().getCenter(), confettiMesh.getBounds().getRadius()) + self.globeSphere.setTangible(1) + self.globeSphereNode = CollisionNode('gagGlobe' + str(self.getPos())) + self.globeSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.globeSphereNode.addSolid(self.globeSphere) + self.globeSphereNodePath = self.partyGlobe.attachNewNode(self.globeSphereNode) + self.partyGlobe.loop('idle') + self.partyGlobe.reparentTo(self) + elif self.name == 'FlyingHeart': + flyingHeartModel = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_heartWing_model') + self.flyingHeart = Actor.Actor(flyingHeartModel, {'idle': 'phase_13/models/parties/tt_m_ara_pty_heartWing'}) + wingsSeqNodeParent = self.flyingHeart.find('**/heartWingJoint') + collisionMesh = self.flyingHeart.find('**/collision_heartWing') + collisionMesh.hide() + self.globeSphere = CollisionSphere(collisionMesh.getBounds().getCenter(), collisionMesh.getBounds().getRadius()) + self.globeSphere.setTangible(1) + self.globeSphereNode = CollisionNode('flyingHeart' + str(self.getPos())) + self.globeSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.globeSphereNode.addSolid(self.globeSphere) + self.globeSphereNodePath = self.flyingHeart.attachNewNode(self.globeSphereNode) + self.globeSphereNodePath.reparentTo(wingsSeqNodeParent) + wings = [] + wingsSeqNode = SequenceNode('wingsSeqNode') + temp = NodePath('Empty') + wing1 = self.flyingHeart.find('**/wing1') + wing2 = self.flyingHeart.find('**/wing2') + wing3 = self.flyingHeart.find('**/wing3') + wing4 = self.flyingHeart.find('**/wing4') + wing1.reparentTo(temp) + wing2.reparentTo(temp) + wing3.reparentTo(temp) + wing4.reparentTo(temp) + wings.append(wing1) + wings.append(wing2) + wings.append(wing3) + wings.append(wing4) + wingsSeqNode.addChild(wing1.node()) + wingsSeqNode.addChild(wing2.node()) + wingsSeqNode.addChild(wing3.node()) + wingsSeqNode.addChild(wing4.node()) + wingsSeqNode.addChild(wing3.node()) + wingsSeqNode.addChild(wing2.node()) + temp.detachNode() + del temp + wingsSeqNodeParent.attachNewNode(wingsSeqNode) + wingsSeqNode.setFrameRate(12) + wingsSeqNode.loop(True) + wingsSeqNode.setPlayRate(1) + self.flyingHeart.loop('idle') + self.flyingHeart.reparentTo(self) + elif self.name == 'HeartBanner': + self.heartBanner = Actor.Actor('phase_13/models/parties/tt_m_ara_pty_bannerValentine_model', {'idle': 'phase_13/models/parties/tt_m_ara_pty_bannerValentine'}) + balloonLeft = self.heartBanner.find('**/balloonsL') + balloonRight = self.heartBanner.find('**/balloonsR') + balloonLeft.setBillboardAxis() + balloonRight.setBillboardAxis() + balloonLeftLocator = self.heartBanner.find('**/balloonJointL') + balloonRightLocator = self.heartBanner.find('**/balloonJointR') + balloonLeft.reparentTo(balloonLeftLocator) + balloonRight.reparentTo(balloonRightLocator) + self.heartBanner.loop('idle') + self.heartBanner.reparentTo(self) + elif self.name == 'Hydra' or self.name == 'StageWinter': + if self.name == 'StageWinter': + self.hydra = Actor.Actor('phase_13/models/parties/tt_r_ara_pty_winterProps', {'dance': 'phase_13/models/parties/tt_a_ara_pty_hydra_dance'}) + else: + self.hydra = Actor.Actor('phase_13/models/parties/tt_a_ara_pty_hydra_default', {'dance': 'phase_13/models/parties/tt_a_ara_pty_hydra_dance'}) + st = random.randint(0, 10) + animIval = ActorInterval(self.hydra, 'dance') + animIvalDur = animIval.getDuration() + self.decSfx = loader.loadSfx('phase_13/audio/sfx/tt_s_ara_pty_propsShow_dance.ogg') + soundIval = SoundInterval(self.decSfx, node=self.hydra, listenerNode=base.localAvatar, volume=PartyGlobals.DECORATION_VOLUME, cutOff=PartyGlobals.DECORATION_CUTOFF, duration=animIvalDur) + self.animSeq = Parallel(animIval, soundIval) + self.animSeq.loop(st) + collisions = self.hydra.find('**/*collision*') + collisions.setPos(0, 0, -5) + self.hydra.flattenStrong() + self.hydra.reparentTo(self) + if self.name == 'StageWinter': + stageBounds = self.hydra.find('**/stage').node().getBounds() + self.hydra.node().setBounds(stageBounds) + self.hydra.node().setFinal(1) + elif self.name == 'TubeCogVictory': + self.tubeCog = Actor.Actor('phase_5.5/models/estate/tt_a_ara_pty_tubeCogVictory_default', {'wave': 'phase_5.5/models/estate/tt_a_ara_pty_tubeCogVictory_wave'}) + st = random.randint(0, 10) + animIval = ActorInterval(self.tubeCog, 'wave') + animIvalDur = animIval.getDuration() + self.decSfx = loader.loadSfx('phase_13/audio/sfx/tt_s_ara_pty_tubeCogVictory_wave.ogg') + soundIval = SoundInterval(self.decSfx, node=self.tubeCog, listenerNode=base.localAvatar, volume=PartyGlobals.DECORATION_VOLUME, cutOff=PartyGlobals.DECORATION_CUTOFF, duration=animIvalDur) + self.animSeq = Parallel(animIval, soundIval) + self.animSeq.loop() + self.animSeq.setT(st) + self.tubeCog.flattenStrong() + self.tubeCog.reparentTo(self) + elif self.name == 'BannerVictory': + self.bannerVictory = Actor.Actor('phase_13/models/parties/tt_m_ara_pty_bannerVictory_model', {'idle': 'phase_13/models/parties/tt_m_ara_pty_bannerVictory'}) + balloonLeft = self.bannerVictory.find('**/balloonsLMesh') + balloonRight = self.bannerVictory.find('**/balloonsRMesh') + balloonLeft.setBillboardAxis() + balloonRight.setBillboardAxis() + balloonLeftLocator = self.bannerVictory.find('**/balloonJointL') + balloonRightLocator = self.bannerVictory.find('**/balloonJointR') + balloonLeft.reparentTo(balloonLeftLocator) + balloonRight.reparentTo(balloonRightLocator) + self.bannerVictory.loop('idle') + self.bannerVictory.reparentTo(self) + elif self.name == 'CannonVictory': + self.cannonVictory = Actor.Actor('phase_13/models/parties/tt_m_ara_pty_cannonVictory_model', {'idle': 'phase_13/models/parties/tt_m_ara_pty_cannonVictory'}) + confettiLocator = self.cannonVictory.findAllMatches('**/uvj_confetties')[1] + confettiMesh = self.cannonVictory.find('**/confettis') + confettiMesh.setTexProjector(confettiMesh.findTextureStage('default'), self.cannonVictory, confettiLocator) + self.cannonVictory.flattenStrong() + self.cannonVictory.loop('idle') + self.cannonVictory.reparentTo(self) + elif self.name == 'CogStatueVictory': + self.decorationModel = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_cogDoodleVictory') + self.decorationModel.reparentTo(self) + self.decorationShadow = self.setupAnimSeq() + elif self.name == 'CogIceCreamVictory': + self.decorationModel = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_cogIceCreamVictory') + self.decorationModel.reparentTo(self) + self.decorationShadow = self.setupAnimSeq() + elif self.name == 'cogIceCreamWinter': + self.decorationModel = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_cogIceCreamWinter') + self.decorationModel.reparentTo(self) + self.decorationShadow = self.setupAnimSeq() + elif self.name == 'CogStatueWinter': + self.decorationModel = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_cogDoodleWinter') + self.decorationModel.reparentTo(self) + self.decorationShadow = self.setupAnimSeq() + elif self.name == 'snowman': + self.decorationModel = loader.loadModel('phase_13/models/estate/tt_m_prp_ext_snowman') + self.decorationModel.reparentTo(self) + self.decorationModel.find('**/growthStage_1').hide() + self.decorationModel.find('**/growthStage_2').hide() + elif self.name == 'snowDoodle': + self.decorationModel = loader.loadModel('phase_5.5/models/estate/tt_m_prp_ext_snowDoodle') + self.decorationModel.reparentTo(self) + self.decorationModel.find('**/growthStage_1').hide() + self.decorationModel.find('**/growthStage_2').hide() + else: + self.decorationModels = loader.loadModel('phase_4/models/parties/partyDecorations') + self.decorationModels.copyTo(self) + decors = self.findAllMatches('**/partyDecoration_*') + for i in xrange(decors.getNumPaths()): + decPiece = decors.getPath(i) + n = decPiece.getName() + if n.endswith('shadow') or n.endswith('base') or n.endswith('collision') or n.endswith(name): + pass + else: + decPiece.reparentTo(hidden) + + self.reparentTo(base.cr.playGame.hood.loader.geom) + + def setupAnimSeq(self): + self.startAnim = 1 + self.animSeq = None + shadow = self.find('**/*shadow*;+i') + shadow.wrtReparentTo(base.cr.playGame.hood.loader.geom) + self.startAnimSeq() + return shadow + + def startAnimSeq(self): + if self.animSeq: + self.animSeq.finish() + if self.startAnim == 1: + self.animSeq = Sequence(LerpHprInterval(self.decorationModel, 3.0, Vec3(random.randint(0, 5), random.randint(0, 5), random.randint(0, 5))), Wait(0.05), Func(self.startAnimSeq)) + self.animSeq.start() + + def cleanUpAnimSequences(self): + self.startAnim = 0 + if hasattr(self, 'animSeq'): + self.animSeq.pause() + self.animSeq.finish() + if self.animSeq: + del self.animSeq + + def unload(self): + self.notify.debug('Unloading') + if self.name == 'GagGlobe': + self.globeSphereNodePath.removeNode() + del self.globeSphereNodePath + del self.globeSphereNode + del self.globeSphere + self.partyGlobe.removeNode() + del self.partyGlobe + elif self.name == 'Hydra' or self.name == 'StageWinter': + self.cleanUpAnimSequences() + self.hydra.removeNode() + del self.hydra + if hasattr(self, 'decSfx'): + del self.decSfx + elif self.name == 'TubeCogVictory': + self.cleanUpAnimSequences() + self.tubeCog.removeNode() + del self.tubeCog + if hasattr(self, 'decSfx'): + del self.decSfx + elif self.name == 'BannerJellyBean': + self.partyBanner.removeNode() + elif self.name == 'CakeTower': + self.partyCake.removeNode() + elif self.name == 'FlyingHeart': + self.globeSphereNodePath.removeNode() + del self.globeSphereNodePath + del self.globeSphereNode + del self.globeSphere + self.flyingHeart.removeNode() + elif self.name == 'HeartBanner': + self.heartBanner.removeNode() + elif self.name == 'CannonVictory': + self.cannonVictory.removeNode() + del self.cannonVictory + elif self.name == 'CogIceCreamVictory' or self.name == 'CogStatueVictory' or self.name == 'cogIceCreamWinter' or self.name == 'CogStatueWinter': + self.cleanUpAnimSequences() + self.decorationModel.removeNode() + self.decorationShadow.removeNode() + del self.decorationShadow + elif self.name == 'snowman' or self.name == 'snowDoodle': + self.decorationModel.removeNode() + elif self.name == 'BannerVictory': + self.bannerVictory.removeNode() + del self.bannerVictory + elif self.name == 'CannonVictory': + self.decorationModel.removeNode() + del self.decorationModel + else: + self.decorationModels.removeNode() diff --git a/toontown/parties/DistributedParty.py b/toontown/parties/DistributedParty.py new file mode 100755 index 00000000..940f8f85 --- /dev/null +++ b/toontown/parties/DistributedParty.py @@ -0,0 +1,578 @@ +import random +import time +import datetime +from pandac.PandaModules import Vec4, TextNode, CardMaker, NodePath +from direct.distributed import DistributedObject +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from direct.gui.DirectGui import DirectLabel +from direct.gui import OnscreenText +from toontown.toonbase import ToontownGlobals +from toontown.parties.PartyInfo import PartyInfo +from toontown.toonbase import TTLocalizer +from toontown.toon import Toon +from toontown.parties import PartyGlobals +from toontown.parties.Decoration import Decoration +import PartyUtils + +class DistributedParty(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedParty') + generatedEvent = 'distributedPartyGenerated' + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.partyDoneEvent = 'partyDone' + self.load() + self.avIdsAtParty = [] + base.distributedParty = self + self.titleText = '' + self.isPartyEnding = False + + def setPartyState(self, partyState): + self.isPartyEnding = partyState + messenger.send('partyStateChanged', [partyState]) + + def getPartyState(self): + return self.isPartyEnding + + def setPartyClockInfo(self, x, y, h): + x = PartyUtils.convertDistanceFromPartyGrid(x, 0) + y = PartyUtils.convertDistanceFromPartyGrid(y, 1) + h = PartyUtils.convertDegreesFromPartyGrid(h) + self.partyClockInfo = (x, y, h) + self.loadPartyCountdownTimer() + + def setInviteeIds(self, inviteeIds): + self.inviteeIds = inviteeIds + + def setPartyInfoTuple(self, partyInfoTuple): + self.partyInfo = PartyInfo(*partyInfoTuple) + self.loadDecorations() + allActIds = [ x.activityId for x in self.partyInfo.activityList ] + base.partyHasJukebox = PartyGlobals.ActivityIds.PartyJukebox in allActIds or PartyGlobals.ActivityIds.PartyJukebox40 in allActIds + self.grid = [[False, + False, + False, + False, + False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False, + False, + False], + [False, + False, + False, + False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False, + False, + False], + [False, + False, + False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False, + False], + [False, + False, + False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False, + False], + [False, + False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False], + [False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False, + False, + False, + False], + [False, + False, + False, + False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False, + False, + False, + False, + False], + [False, + False, + False, + False, + False, + True, + True, + True, + True, + True, + True, + True, + False, + False, + False, + False, + False, + False], + [False, + False, + False, + False, + False, + False, + True, + True, + True, + True, + True, + False, + False, + False, + False, + False, + False, + False]] + + def fillGrid(x, y, size): + for i in xrange(-size[1] / 2 + 1, size[1] / 2 + 1): + for j in xrange(-size[0] / 2 + 1, size[0] / 2 + 1): + self.grid[i + y][j + x] = False + + for activityBase in self.partyInfo.activityList: + fillGrid(activityBase.x, activityBase.y, PartyGlobals.ActivityInformationDict[activityBase.activityId]['gridsize']) + + for decorBase in self.partyInfo.decors: + fillGrid(decorBase.x, decorBase.y, PartyGlobals.DecorationInformationDict[decorBase.decorId]['gridsize']) + + self.loadGrass() + + def setPartyStartedTime(self, startedTime): + stime = time.strptime(startedTime, '%Y-%m-%d %H:%M:%S') + self.partyStartedTime = datetime.datetime(year=stime.tm_year, month=stime.tm_mon, day=stime.tm_mday, hour=stime.tm_hour, minute=stime.tm_min, second=stime.tm_sec, tzinfo=base.cr.toontownTimeManager.getCurServerDateTime().tzinfo) + + def disable(self): + self.notify.debug('disable') + DistributedObject.DistributedObject.disable(self) + base.localAvatar.chatMgr.chatInputSpeedChat.removeInsidePartiesMenu() + + def delete(self): + self.notify.debug('delete') + self.unload() + if hasattr(base, 'distributedParty'): + del base.distributedParty + DistributedObject.DistributedObject.delete(self) + + def load(self): + Toon.loadMinigameAnims() + self.defaultSignModel = loader.loadModel('phase_13/models/parties/eventSign') + self.activityIconsModel = loader.loadModel('phase_4/models/parties/eventSignIcons') + self.defaultLeverModel = loader.loadModel('phase_13/models/parties/partyLeverBase') + self.defaultStickModel = loader.loadModel('phase_13/models/parties/partyLeverStick') + + def loadGrass(self): + self.grassRoot = NodePath('GrassRoot') + self.grassRoot.reparentTo(base.cr.playGame.hood.loader.geom) + grass = loader.loadModel('phase_13/models/parties/grass') + clearPositions = self.getClearSquarePositions() + numTufts = min(len(clearPositions) * 3, PartyGlobals.TuftsOfGrass) + for i in xrange(numTufts): + g = grass.copyTo(self.grassRoot) + pos = random.choice(clearPositions) + g.setPos(pos[0] + random.randint(-8, 8), pos[1] + random.randint(-8, 8), 0.0) + + def loadDecorations(self): + self.decorationsList = [] + for decorBase in self.partyInfo.decors: + self.decorationsList.append(Decoration(PartyGlobals.DecorationIds.getString(decorBase.decorId), PartyUtils.convertDistanceFromPartyGrid(decorBase.x, 0), PartyUtils.convertDistanceFromPartyGrid(decorBase.y, 1), PartyUtils.convertDegreesFromPartyGrid(decorBase.h))) + + def unload(self): + if hasattr(self, 'decorationsList') and self.decorationsList: + for decor in self.decorationsList: + decor.unload() + + del self.decorationsList + self.stopPartyClock() + self.grassRoot.removeNode() + del self.grassRoot + if hasattr(self, 'testGrid'): + self.testGrid.removeNode() + del self.testGrid + self.ignoreAll() + Toon.unloadMinigameAnims() + self.removePartyHats() + if hasattr(base, 'partyHasJukebox'): + del base.partyHasJukebox + + def announceGenerate(self): + #TODO - for some reason this is getting called hundreds of times when there are multiple districts + DistributedObject.DistributedObject.announceGenerate(self) + self.sendUpdate('enteredParty', []) + globalClock.syncFrameTime() + self.startPartyClock() + base.localAvatar.chatMgr.chatInputSpeedChat.addInsidePartiesMenu() + self.spawnTitleText() + messenger.send(self.generatedEvent) + if config.GetBool('show-debug-party-grid', 0): + self.testGrid = NodePath('test_grid') + self.testGrid.reparentTo(base.cr.playGame.hood.loader.geom) + for i in xrange(len(self.grid)): + for j in xrange(len(self.grid[i])): + cm = CardMaker('gridsquare') + np = NodePath(cm.generate()) + np.setScale(12) + np.setP(-90.0) + np.setPos(PartyUtils.convertDistanceFromPartyGrid(j, 0) - 6.0, PartyUtils.convertDistanceFromPartyGrid(i, 1) - 6.0, 0.1) + np.reparentTo(self.testGrid) + if self.grid[i][j]: + np.setColorScale(0.0, 1.0, 0.0, 1.0) + else: + np.setColorScale(1.0, 0.0, 0.0, 1.0) + + def removePartyHats(self): + for av in base.cr.doId2do.values(): + if isinstance(av, Toon.Toon): + av.removePartyHat() + + def getClearSquarePos(self): + clearPositions = self.getClearSquarePositions() + if len(clearPositions) == 0: + raise StandardError, 'Party %s has no empty grid squares.' % self.doId + return random.choice(clearPositions) + + def getClearSquarePositions(self): + clearPositions = [] + for y in xrange(len(self.grid)): + for x in xrange(len(self.grid[0])): + if self.grid[y][x]: + pos = (PartyUtils.convertDistanceFromPartyGrid(x, 0), PartyUtils.convertDistanceFromPartyGrid(y, 1), 0.1) + clearPositions.append(pos) + + return clearPositions + + def startPartyClock(self): + self.partyClockModel.reparentTo(base.cr.playGame.hood.loader.geom) + curServerTime = base.cr.toontownTimeManager.getCurServerDateTime() + timePartyWillEnd = self.partyStartedTime + datetime.timedelta(hours=PartyGlobals.DefaultPartyDuration) + timeLeftInParty = timePartyWillEnd - curServerTime + if curServerTime < timePartyWillEnd: + self.secondsLeftInParty = timeLeftInParty.seconds + else: + self.secondsLeftInParty = 0 + taskMgr.doMethodLater(0.5, self.partyClockTask, 'UpdatePartyClock') + self.partyClockSignFront = self.partyClockModel.find('**/signFrontText_locator') + self.partyClockSignBack = self.partyClockModel.find('**/signBackText_locator') + self.attachHostNameToSign(self.partyClockSignFront) + self.attachHostNameToSign(self.partyClockSignBack) + + def attachHostNameToSign(self, locator): + if self.hostName == '': + return + nameText = TextNode('nameText') + nameText.setCardAsMargin(0.1, 0.1, 0.1, 0.1) + nameText.setCardDecal(True) + nameText.setCardColor(1.0, 1.0, 1.0, 0.0) + r = 232.0 / 255.0 + g = 169.0 / 255.0 + b = 23.0 / 255.0 + nameText.setTextColor(r, g, b, 1) + nameText.setAlign(nameText.ACenter) + nameText.setFont(ToontownGlobals.getBuildingNametagFont()) + nameText.setShadowColor(0, 0, 0, 1) + nameText.setBin('fixed') + if TTLocalizer.BuildingNametagShadow: + nameText.setShadow(*TTLocalizer.BuildingNametagShadow) + nameWordWrap = 11.0 + nameText.setWordwrap(nameWordWrap) + scaleMult = 0.48 + houseName = self.hostName + nameText.setText(houseName) + textWidth = nameText.getWidth() + xScale = 1.0 * scaleMult + if textWidth > nameWordWrap: + xScale = nameWordWrap / textWidth * scaleMult + sign_origin = locator + namePlate = sign_origin.attachNewNode(nameText) + namePlate.setDepthWrite(0) + namePlate.setPos(0, 0, 0) + namePlate.setScale(xScale) + + def stopPartyClock(self): + self.partyClockModel.removeNode() + taskMgr.remove('UpdatePartyClock') + + def partyClockTask(self, task): + self.secondsLeftInParty -= 0.5 + if self.secondsLeftInParty < 0: + self.frontTimer['minute']['text'] = '--' + self.backTimer['minute']['text'] = '--' + self.frontTimer['second']['text'] = '--' + self.backTimer['second']['text'] = '--' + return + if self.frontTimer['colon'].isStashed(): + self.frontTimer['colon'].unstash() + self.backTimer['colon'].unstash() + else: + self.frontTimer['colon'].stash() + self.backTimer['colon'].stash() + minutesLeft = int(int(self.secondsLeftInParty / 60) % 60) + if minutesLeft < 10: + minutesLeft = '0%d' % minutesLeft + else: + minutesLeft = '%d' % minutesLeft + secondsLeft = int(self.secondsLeftInParty % 60) + if secondsLeft < 10: + secondsLeft = '0%d' % secondsLeft + else: + secondsLeft = '%d' % secondsLeft + self.frontTimer['minute']['text'] = minutesLeft + self.backTimer['minute']['text'] = minutesLeft + self.frontTimer['second']['text'] = secondsLeft + self.backTimer['second']['text'] = secondsLeft + taskMgr.doMethodLater(0.5, self.partyClockTask, 'UpdatePartyClock') + if self.secondsLeftInParty != int(self.secondsLeftInParty): + self.partyClockModel.find('**/middleRotateFront_grp').setR(-6.0 * (self.secondsLeftInParty % 60)) + self.partyClockModel.find('**/middleRotateBack_grp').setR(6.0 * (self.secondsLeftInParty % 60)) + + def getAvIdsAtParty(self): + return self.avIdsAtParty + + def setAvIdsAtParty(self, avIdsAtParty): + self.avIdsAtParty = avIdsAtParty + + def loadPartyCountdownTimer(self): + self.partyClockModel = loader.loadModel('phase_13/models/parties/partyClock') + self.partyClockModel.setPos(self.partyClockInfo[0], self.partyClockInfo[1], 0.0) + self.partyClockModel.setH(self.partyClockInfo[2]) + self.partyClockModel.reparentTo(base.cr.playGame.hood.loader.geom) + self.partyClockModel.find('**/frontText_locator').setY(-1.1) + self.partyClockModel.find('**/backText_locator').setY(0.633) + self.frontTimer = self.getTimer(self.partyClockModel.find('**/frontText_locator')) + base.frontTimerLoc = self.partyClockModel.find('**/frontText_locator') + base.backTimerLoc = self.partyClockModel.find('**/backText_locator') + self.backTimer = self.getTimer(self.partyClockModel.find('**/backText_locator')) + self.partyClockModel.stash() + + def getTimer(self, parent): + timeFont = ToontownGlobals.getMinnieFont() + timer = {} + timer['minute'] = DirectLabel(parent=parent, pos=TTLocalizer.DPtimerMinutePos, relief=None, text='59', text_align=TextNode.ACenter, text_font=timeFont, text_fg=(0.7, 0.3, 0.3, 1.0), scale=TTLocalizer.DPtimerMinute) + timer['colon'] = DirectLabel(parent=parent, pos=TTLocalizer.DPtimerColonPos, relief=None, text=':', text_align=TextNode.ACenter, text_font=timeFont, text_fg=(0.7, 0.3, 0.3, 1.0), scale=TTLocalizer.DPtimerColon) + timer['second'] = DirectLabel(parent=parent, relief=None, pos=TTLocalizer.DPtimerSecondPos, text='14', text_align=TextNode.ACenter, text_font=timeFont, text_fg=(0.7, 0.3, 0.3, 1.0), scale=TTLocalizer.DPtimerSecond) + timer['textLabel'] = DirectLabel(parent=parent, relief=None, pos=(0.0, 0.0, 1.15), text=TTLocalizer.PartyCountdownClockText, text_font=timeFont, text_fg=(0.7, 0.3, 0.3, 1.0), scale=TTLocalizer.DPtimerTextLabel) + return timer + + def setHostName(self, hostName): + self.hostName = hostName + if hasattr(self, 'partyClockSignFront'): + self.attachHostNameToSign(self.partyClockSignFront) + if hasattr(self, 'partyClockSignBack'): + self.attachHostNameToSign(self.partyClockSignBack) + + def spawnTitleText(self): + if not self.hostName: + return + partyText = TTLocalizer.PartyTitleText % TTLocalizer.GetPossesive(self.hostName) + self.doSpawnTitleText(partyText) + + def doSpawnTitleText(self, text): + self.titleColor = (1.0, 0.5, 0.4, 1.0) + self.titleText = OnscreenText.OnscreenText(text, fg=self.titleColor, font=ToontownGlobals.getSignFont(), pos=(0, -0.5), scale=0.16, drawOrder=0, mayChange=1, wordwrap=16) + self.titleText.setText(text) + self.titleText.show() + self.titleText.setColor(Vec4(*self.titleColor)) + self.titleText.clearColorScale() + self.titleText.setFg(self.titleColor) + seq = Sequence(Wait(0.1), Wait(6.0), self.titleText.colorScaleInterval(0.5, Vec4(1.0, 1.0, 1.0, 0.0)), Func(self.hideTitleText)) + seq.start() + + def hideTitleText(self): + if self.titleText: + self.titleText.hide() diff --git a/toontown/parties/DistributedPartyAI.py b/toontown/parties/DistributedPartyAI.py new file mode 100755 index 00000000..c0ecf45b --- /dev/null +++ b/toontown/parties/DistributedPartyAI.py @@ -0,0 +1,155 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from PartyGlobals import * +import PartyUtils +import time +# ugh all these activities +from toontown.parties.DistributedPartyJukeboxActivityAI import DistributedPartyJukeboxActivityAI +from toontown.parties.DistributedPartyDanceActivityAI import DistributedPartyDanceActivityAI +from toontown.parties.DistributedPartyJukebox40ActivityAI import DistributedPartyJukebox40ActivityAI +from toontown.parties.DistributedPartyDance20ActivityAI import DistributedPartyDance20ActivityAI +from toontown.parties.DistributedPartyCogActivityAI import DistributedPartyCogActivityAI +from toontown.parties.DistributedPartyTrampolineActivityAI import DistributedPartyTrampolineActivityAI +from toontown.parties.DistributedPartyVictoryTrampolineActivityAI import DistributedPartyVictoryTrampolineActivityAI +from toontown.parties.DistributedPartyCatchActivityAI import DistributedPartyCatchActivityAI +from toontown.parties.DistributedPartyTugOfWarActivityAI import DistributedPartyTugOfWarActivityAI +from toontown.parties.DistributedPartyCannonActivityAI import DistributedPartyCannonActivityAI +from toontown.parties.DistributedPartyCannonAI import DistributedPartyCannonAI +from toontown.parties.DistributedPartyFireworksActivityAI import DistributedPartyFireworksActivityAI + +""" +dclass DistributedParty : DistributedObject { + setPartyClockInfo(uint8, uint8, uint8) required broadcast; + setInviteeIds(uint32array) required broadcast; + setPartyState(bool) required broadcast; + setPartyInfoTuple(party) required broadcast; + setAvIdsAtParty(uint32 []) required broadcast; + setPartyStartedTime(string) required broadcast; + setHostName(string) required broadcast; + avIdEnteredParty(uint32) clsend airecv; +}; +""" +class DistributedPartyAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyAI") + + def __init__(self, air, hostId, zoneId, info): + DistributedObjectAI.__init__(self, air) + self.hostId = hostId + self.zoneId = zoneId + self.info = info + # buncha required crap + PARTY_TIME_FORMAT = '%Y-%m-%d %H:%M:%S' + self.startedAt = time.strftime(PARTY_TIME_FORMAT) + self.partyState = 0 + self.avIdsAtParty = [] + # apparently 'partyclockinfo' is the xyz on the party grid + for activity in self.info['activities']: + if activity[0] == ActivityIds.PartyClock: + self.partyClockInfo = (activity[1], activity[2], activity[3]) + + # We'll need to inform the UD later of the host's name so other public parties know the host. Maybe we know who he is.. + self.hostName = '' + host = self.air.doId2do.get(self.hostId, None) + if host: + self.hostName = host.getName() + self.activities = [] + self.cannonActivity = None + + def generate(self): + DistributedObjectAI.generate(self) + # make stuff + actId2Class = { + ActivityIds.PartyJukebox: DistributedPartyJukeboxActivityAI, + ActivityIds.PartyTrampoline: DistributedPartyTrampolineActivityAI, + ActivityIds.PartyVictoryTrampoline: DistributedPartyVictoryTrampolineActivityAI, + ActivityIds.PartyCatch: DistributedPartyCatchActivityAI, + ActivityIds.PartyDance: DistributedPartyDanceActivityAI, + ActivityIds.PartyTugOfWar: DistributedPartyTugOfWarActivityAI, + ActivityIds.PartyFireworks: DistributedPartyFireworksActivityAI, + ActivityIds.PartyJukebox40: DistributedPartyJukebox40ActivityAI, + ActivityIds.PartyDance20: DistributedPartyDance20ActivityAI, + ActivityIds.PartyCog: DistributedPartyCogActivityAI, + } + for activity in self.info['activities']: + actId = activity[0] + if actId in actId2Class: + act = actId2Class[actId](self.air, self.doId, activity) + act.generateWithRequired(self.zoneId) + self.activities.append(act) + elif actId == ActivityIds.PartyCannon: + if not self.cannonActivity: + self.cannonActivity = DistributedPartyCannonActivityAI(self.air, self.doId, activity) + self.cannonActivity.generateWithRequired(self.zoneId) + act = DistributedPartyCannonAI(self.air) + act.setActivityDoId(self.cannonActivity.doId) + x, y, h = activity[1:] # ignore activity ID + x = PartyUtils.convertDistanceFromPartyGrid(x, 0) + y = PartyUtils.convertDistanceFromPartyGrid(y, 1) + h *= PartyGridHeadingConverter + act.setPosHpr(x,y,0,h,0,0) + act.generateWithRequired(self.zoneId) + self.activities.append(act) + def delete(self): + for act in self.activities: + act.requestDelete() + if self.cannonActivity: + self.cannonActivity.requestDelete() + DistributedObjectAI.delete(self) + + def getPartyClockInfo(self): + return self.partyClockInfo + + def getInviteeIds(self): + return self.info.get('inviteeIds', []) + + def getPartyState(self): + return self.partyState + + def b_setPartyState(self, partyState): + self.partyState = partyState + self.sendUpdate('setPartyState', [partyState]) + + def _formatParty(self, partyDict, status=PartyStatus.Started): + start = partyDict['start'] + end = partyDict['end'] + return [partyDict['partyId'], + partyDict['hostId'], + start.year, + start.month, + start.day, + start.hour, + start.minute, + end.year, + end.month, + end.day, + end.hour, + end.minute, + partyDict['isPrivate'], + partyDict['inviteTheme'], + partyDict['activities'], + partyDict['decorations'], + status] + def getPartyInfoTuple(self): + return self._formatParty(self.info) + + def getAvIdsAtParty(self): + return self.avIdsAtParty + + def getPartyStartedTime(self): + return self.startedAt + + def getHostName(self): + return self.hostName + + def enteredParty(self): + avId = self.air.getAvatarIdFromSender() + if not avId in self.avIdsAtParty: + self.air.globalPartyMgr.d_toonJoinedParty(self.info.get('partyId', 0), avId) + self.avIdsAtParty.append(avId) + + def _removeAvatar(self, avId): + if avId in self.avIdsAtParty: + print 'REMOVE FROM PARTY!' + self.air.globalPartyMgr.d_toonLeftParty(self.info.get('partyId', 0), avId) + self.avIdsAtParty.remove(avId) +#Thanks Fooster for the typo fix diff --git a/toontown/parties/DistributedPartyActivity.py b/toontown/parties/DistributedPartyActivity.py new file mode 100755 index 00000000..d2795f68 --- /dev/null +++ b/toontown/parties/DistributedPartyActivity.py @@ -0,0 +1,531 @@ +from pandac.PandaModules import CollisionSphere, CollisionNode, CollisionTube +from pandac.PandaModules import TextNode, NodePath, Vec3, Point3 +from direct.distributed.ClockDelta import globalClockDelta +from direct.distributed import DistributedObject +from direct.showbase import RandomNumGen +from direct.showbase import PythonUtil +from direct.interval.IntervalGlobal import Sequence, Parallel, ActorInterval +from direct.interval.FunctionInterval import Wait +from otp.avatar import Emote +from otp.otpbase import OTPGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.minigame.MinigameRulesPanel import MinigameRulesPanel +from toontown.toontowngui import TTDialog +from toontown.parties.JellybeanRewardGui import JellybeanRewardGui +from toontown.parties.PartyUtils import getPartyActivityIcon, getCenterPosFromGridSize + +class DistributedPartyActivity(DistributedObject.DistributedObject): + def __init__(self, cr, activityId, activityType, wantLever = False, wantRewardGui = False): + DistributedObject.DistributedObject.__init__(self, cr) + self.activityId = activityId + self.activityName = PartyGlobals.ActivityIds.getString(self.activityId) + self.activityType = activityType + self.wantLever = wantLever + self.wantRewardGui = wantRewardGui + self.messageGui = None + self.rewardGui = None + self.toonIds = [] + self._toonId2ror = {} + childName = '%s' % self + childName = childName[childName.rfind('.DistributedParty') + len('.DistributedParty'):childName.rfind('Activity instance')] + if not hasattr(base, 'partyActivityDict'): + base.partyActivityDict = {} + base.partyActivityDict[childName] = self + self.root = NodePath('root') + self.rulesDoneEvent = 'rulesDone' + self.modelCount = 500 + self.cleanupActions = [] + self.usesSmoothing = 0 + self.usesLookAround = 0 + self.difficultyOverride = None + self.trolleyZoneOverride = None + self._localToonRequestStatus = None + return + + def localToonExiting(self): + self._localToonRequestStatus = PartyGlobals.ActivityRequestStatus.Exiting + + def localToonJoining(self): + self._localToonRequestStatus = PartyGlobals.ActivityRequestStatus.Joining + + def d_toonJoinRequest(self): + if self._localToonRequestStatus is None: + self.localToonJoining() + self.sendUpdate('toonJoinRequest') + return + + def d_toonExitRequest(self): + if self._localToonRequestStatus is None: + self.localToonExiting() + self.sendUpdate('toonExitRequest') + return + + def d_toonExitDemand(self): + self.localToonExiting() + self.sendUpdate('toonExitDemand') + + def joinRequestDenied(self, reason): + self._localToonRequestStatus = None + return + + def exitRequestDenied(self, reason): + self._localToonRequestStatus = None + return + + def handleToonJoined(self, toonId): + self.notify.error('BASE: handleToonJoined should be overridden %s' % self.activityName) + + def handleToonExited(self, toonId): + self.notify.error('BASE: handleToonExited should be overridden %s' % self.activityName) + + def handleToonDisabled(self, toonId): + self.notify.error('BASE: handleToonDisabled should be overridden %s' % self.activityName) + + def setToonsPlaying(self, toonIds): + exitedToons, joinedToons = self.getToonsPlayingChanges(self.toonIds, toonIds) + self.setToonIds(toonIds) + self._processExitedToons(exitedToons) + self._processJoinedToons(joinedToons) + + def _processExitedToons(self, exitedToons): + for toonId in exitedToons: + if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus(PartyGlobals.ActivityRequestStatus.Exiting): + toon = self.getAvatar(toonId) + if toon is not None: + self.ignore(toon.uniqueName('disable')) + self.handleToonExited(toonId) + if toonId == base.localAvatar.doId: + self._localToonRequestStatus = None + if toonId in self._toonId2ror: + self.cr.relatedObjectMgr.abortRequest(self._toonId2ror[toonId]) + del self._toonId2ror[toonId] + + return + + def _processJoinedToons(self, joinedToons): + for toonId in joinedToons: + if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus(PartyGlobals.ActivityRequestStatus.Joining): + if toonId not in self._toonId2ror: + request = self.cr.relatedObjectMgr.requestObjects([toonId], allCallback=self._handlePlayerPresent) + if toonId in self._toonId2ror: + del self._toonId2ror[toonId] + else: + self._toonId2ror[toonId] = request + + def _handlePlayerPresent(self, toons): + toon = toons[0] + toonId = toon.doId + if toonId in self._toonId2ror: + del self._toonId2ror[toonId] + else: + self._toonId2ror[toonId] = None + self._enableHandleToonDisabled(toonId) + self.handleToonJoined(toonId) + if toonId == base.localAvatar.doId: + self._localToonRequestStatus = None + return + + def _enableHandleToonDisabled(self, toonId): + toon = self.getAvatar(toonId) + if toon is not None: + self.acceptOnce(toon.uniqueName('disable'), self.handleToonDisabled, [toonId]) + else: + self.notify.warning('BASE: unable to get handle to toon with toonId:%d. Hook for handleToonDisabled not set.' % toonId) + return + + def isLocalToonRequestStatus(self, requestStatus): + return self._localToonRequestStatus == requestStatus + + def setToonIds(self, toonIds): + self.toonIds = toonIds + + def getToonsPlayingChanges(self, oldToonIds, newToonIds): + oldToons = set(oldToonIds) + newToons = set(newToonIds) + exitedToons = oldToons.difference(newToons) + joinedToons = newToons.difference(oldToons) + return (list(exitedToons), list(joinedToons)) + + def setUsesSmoothing(self): + self.usesSmoothing = True + + def setUsesLookAround(self): + self.usesLookAround = True + + def getInstructions(self): + return TTLocalizer.DefaultPartyActivityInstructions + + def getParentNodePath(self): + if hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'loader') and base.cr.playGame.hood.loader and hasattr(base.cr.playGame.hood.loader, 'geom') and base.cr.playGame.hood.loader.geom: + return base.cr.playGame.hood.loader.geom + else: + self.notify.warning('Hood or loader not created, defaulting to render') + return render + + def __createRandomNumGen(self): + self.notify.debug('BASE: self.doId=0x%08X' % self.doId) + self.randomNumGen = RandomNumGen.RandomNumGen(self.doId) + + def destroy(self = self): + self.notify.debug('BASE: destroying random num gen') + del self.randomNumGen + + self.cleanupActions.append(destroy) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.notify.debug('BASE: generate, %s' % self.getTitle()) + self.__createRandomNumGen() + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.notify.debug('BASE: announceGenerate %s' % self.activityName) + self.root.setName(self.activityName + 'Root') + centeredX, centeredY = getCenterPosFromGridSize(self.x, self.y, PartyGlobals.ActivityInformationDict[self.activityId]['gridsize']) + self.root.setPos(centeredX, centeredY, 0.0) + self.root.setH(self.h) + self.normalExit = True + if self.wantLever: + self.leverTriggerEvent = self.uniqueName('leverTriggerEvent') + self.load() + + def cleanup(self = self): + self.notify.debug('BASE: cleanup: normalExit=%s' % self.normalExit) + base.cr.renderFrame() + if self.normalExit: + self.sendUpdate('toonExitRequest') + + self.cleanupActions.append(cleanup) + + def disable(self): + self.notify.debug('BASE: disable') + DistributedObject.DistributedObject.disable(self) + rorToonIds = self._toonId2ror.keys() + for toonId in rorToonIds: + self.cr.relatedObjectMgr.abortRequest(self._toonId2ror[toonId]) + del self._toonId2ror[toonId] + + self.ignore(self.messageDoneEvent) + if self.messageGui is not None and not self.messageGui.isEmpty(): + self.messageGui.cleanup() + self.messageGui = None + return + + def delete(self): + self.notify.debug('BASE: delete') + self.unload() + self.ignoreAll() + DistributedObject.DistributedObject.delete(self) + + def load(self): + self.notify.debug('BASE: load') + self.loadSign() + if self.wantLever: + self.loadLever() + if self.wantRewardGui: + self.showRewardDoneEvent = self.uniqueName('showRewardDoneEvent') + self.rewardGui = JellybeanRewardGui(self.showRewardDoneEvent) + self.messageDoneEvent = self.uniqueName('messageDoneEvent') + self.root.reparentTo(self.getParentNodePath()) + self._enableCollisions() + + def loadSign(self): + actNameForSign = self.activityName + if self.activityId == PartyGlobals.ActivityIds.PartyJukebox40: + actNameForSign = PartyGlobals.ActivityIds.getString(PartyGlobals.ActivityIds.PartyJukebox) + elif self.activityId == PartyGlobals.ActivityIds.PartyDance20: + actNameForSign = PartyGlobals.ActivityIds.getString(PartyGlobals.ActivityIds.PartyDance) + self.sign = self.root.attachNewNode('%sSign' % self.activityName) + self.signModel = self.party.defaultSignModel.copyTo(self.sign) + self.signFlat = self.signModel.find('**/sign_flat') + self.signFlatWithNote = self.signModel.find('**/sign_withNote') + self.signTextLocator = self.signModel.find('**/signText_locator') + textureNodePath = getPartyActivityIcon(self.party.activityIconsModel, actNameForSign) + textureNodePath.setPos(0.0, -0.02, 2.2) + textureNodePath.setScale(2.35) + textureNodePath.copyTo(self.signFlat) + textureNodePath.copyTo(self.signFlatWithNote) + text = TextNode('noteText') + text.setTextColor(0.2, 0.1, 0.7, 1.0) + text.setAlign(TextNode.ACenter) + text.setFont(OTPGlobals.getInterfaceFont()) + text.setWordwrap(10.0) + text.setText('') + self.noteText = self.signFlatWithNote.attachNewNode(text) + self.noteText.setPosHpr(self.signTextLocator, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0) + self.noteText.setScale(0.2) + self.signFlatWithNote.stash() + self.signTextLocator.stash() + + def unloadSign(self): + self.sign.removeNode() + del self.sign + + def loadLever(self): + self.lever = self.root.attachNewNode('%sLever' % self.activityName) + self.leverModel = self.party.defaultLeverModel.copyTo(self.lever) + self.controlColumn = NodePath('cc') + column = self.leverModel.find('**/column') + column.getChildren().reparentTo(self.controlColumn) + self.controlColumn.reparentTo(column) + self.stickHinge = self.controlColumn.attachNewNode('stickHinge') + self.stick = self.party.defaultStickModel.copyTo(self.stickHinge) + self.stickHinge.setHpr(0.0, 90.0, 0.0) + self.stick.setHpr(0, -90.0, 0) + self.stick.flattenLight() + self.bottom = self.leverModel.find('**/bottom') + self.bottom.wrtReparentTo(self.controlColumn) + self.bottomPos = self.bottom.getPos() + cs = CollisionSphere(0.0, 1.35, 2.0, 1.0) + cs.setTangible(False) + cn = CollisionNode(self.leverTriggerEvent) + cn.addSolid(cs) + cn.setIntoCollideMask(OTPGlobals.WallBitmask) + self.leverTrigger = self.root.attachNewNode(cn) + self.leverTrigger.reparentTo(self.lever) + self.leverTrigger.stash() + cs = CollisionTube(0.0, 2.7, 0.0, 0.0, 2.7, 3.0, 1.2) + cn = CollisionNode('levertube') + cn.addSolid(cs) + cn.setIntoCollideMask(OTPGlobals.WallBitmask) + self.leverTube = self.leverModel.attachNewNode(cn) + host = base.cr.doId2do.get(self.party.partyInfo.hostId) + if host is None: + self.notify.debug('%s loadLever : Host has left the game before lever could be created.' % self.activityName) + return + scale = host.getGeomNode().getChild(0).getSz(render) + self.leverModel.setScale(scale) + self.controlColumn.setPos(0, 0, 0) + host.setPosHpr(self.lever, 0, 0, 0, 0, 0, 0) + host.pose('leverNeutral', 0) + host.update() + pos = host.rightHand.getPos(self.controlColumn) + self.controlColumn.setPos(pos[0], pos[1], pos[2] - 1) + self.bottom.setZ(host, 0.0) + self.bottom.setPos(self.bottomPos[0], self.bottomPos[1], self.bottom.getZ()) + lookAtPoint = Point3(0.3, 0, 0.1) + lookAtUp = Vec3(0, -1, 0) + self.stickHinge.lookAt(host.rightHand, lookAtPoint, lookAtUp) + host.play('walk') + host.update() + return + + def unloadLever(self): + self.lever.removeNode() + self.leverModel.removeNode() + self.controlColumn.removeNode() + self.stickHinge.removeNode() + self.stick.removeNode() + self.bottom.removeNode() + self.leverTrigger.removeNode() + self.leverTube.removeNode() + del self.bottomPos + del self.lever + del self.leverModel + del self.controlColumn + del self.stickHinge + del self.stick + del self.bottom + del self.leverTrigger + del self.leverTube + + def _enableCollisions(self): + if self.wantLever: + self.leverTrigger.unstash() + self.accept('enter%s' % self.leverTriggerEvent, self._leverPulled) + + def _disableCollisions(self): + if self.wantLever: + self.leverTrigger.stash() + self.ignore('enter%s' % self.leverTriggerEvent) + + def _leverPulled(self, collEntry): + self.notify.debug('_leverPulled : Someone pulled the lever!!! ') + if self.activityType == PartyGlobals.ActivityTypes.HostInitiated and base.localAvatar.doId != self.party.partyInfo.hostId: + return False + return True + + def getToonPullingLeverInterval(self, toon): + walkTime = 0.2 + reach = ActorInterval(toon, 'leverReach', playRate=2.0) + pull = ActorInterval(toon, 'leverPull', startFrame=6) + origPos = toon.getPos(render) + origHpr = toon.getHpr(render) + newPos = self.lever.getPos(render) + newHpr = self.lever.getHpr(render) + origHpr.setX(PythonUtil.fitSrcAngle2Dest(origHpr[0], newHpr[0])) + toon.setPosHpr(origPos, origHpr) + reachAndPull = Sequence(ActorInterval(toon, 'walk', loop=True, duration=walkTime - reach.getDuration()), reach, pull) + leverSeq = Sequence(Wait(walkTime + reach.getDuration() - 0.1), self.stick.hprInterval(0.55, Point3(0.0, 25.0, 0.0), Point3(0.0, 0.0, 0.0)), Wait(0.3), self.stick.hprInterval(0.4, Point3(0.0, 0.0, 0.0), Point3(0.0, 25.0, 0.0))) + returnSeq = Sequence(Parallel(toon.posInterval(walkTime, newPos, origPos), toon.hprInterval(walkTime, newHpr, origHpr), leverSeq, reachAndPull)) + return returnSeq + + def showMessage(self, message, endState = 'walk'): + base.cr.playGame.getPlace().fsm.request('activity') + self.acceptOnce(self.messageDoneEvent, self.__handleMessageDone) + self.messageGui = TTDialog.TTGlobalDialog(doneEvent=self.messageDoneEvent, message=message, style=TTDialog.Acknowledge) + self.messageGui.endState = endState + + def __handleMessageDone(self): + self.ignore(self.messageDoneEvent) + if hasattr(base.cr.playGame.getPlace(), 'fsm'): + if self.messageGui and hasattr(self.messageGui, 'endState'): + self.notify.info('__handleMessageDone (endState=%s)' % self.messageGui.endState) + base.cr.playGame.getPlace().fsm.request(self.messageGui.endState) + else: + self.notify.warning("messageGui has no endState, defaulting to 'walk'") + base.cr.playGame.getPlace().fsm.request('walk') + if self.messageGui is not None and not self.messageGui.isEmpty(): + self.messageGui.cleanup() + self.messageGui = None + return + + def showJellybeanReward(self, earnedAmount, jarAmount, message): + if not self.isLocalToonInActivity() or base.localAvatar.doId in self.getToonIdsAsList(): + messenger.send('DistributedPartyActivity-showJellybeanReward') + base.cr.playGame.getPlace().fsm.request('activity') + self.acceptOnce(self.showRewardDoneEvent, self.__handleJellybeanRewardDone) + self.rewardGui.showReward(earnedAmount, jarAmount, message) + + def __handleJellybeanRewardDone(self): + self.ignore(self.showRewardDoneEvent) + self.handleRewardDone() + + def handleRewardDone(self): + if base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'fsm'): + base.cr.playGame.getPlace().fsm.request('walk') + + def setSignNote(self, note): + self.noteText.node().setText(note) + if len(note.strip()) > 0: + self.signFlat.stash() + self.signFlatWithNote.unstash() + self.signTextLocator.unstash() + else: + self.signFlat.unstash() + self.signFlatWithNote.stash() + self.signTextLocator.stash() + + def unload(self): + self.notify.debug('BASE: unload') + self.finishRules() + self._disableCollisions() + self.signModel.removeNode() + del self.signModel + if hasattr(self, 'sign'): + self.sign.removeNode() + del self.sign + self.ignoreAll() + if self.wantLever: + self.unloadLever() + self.root.removeNode() + del self.root + del self.activityId + del self.activityName + del self.activityType + del self.wantLever + del self.messageGui + if self.rewardGui is not None: + self.rewardGui.destroy() + del self.rewardGui + if hasattr(self, 'toonIds'): + del self.toonIds + del self.rulesDoneEvent + del self.modelCount + del self.cleanupActions + del self.usesSmoothing + del self.usesLookAround + del self.difficultyOverride + del self.trolleyZoneOverride + if hasattr(base, 'partyActivityDict'): + del base.partyActivityDict + return + + def setPartyDoId(self, partyDoId): + self.party = base.cr.doId2do[partyDoId] + + def setX(self, x): + self.x = x + + def setY(self, y): + self.y = y + + def setH(self, h): + self.h = h + + def setState(self, newState, timestamp): + if newState == 'Active': + self.activityStartTime = globalClockDelta.networkToLocalTime(timestamp) + + def turnOffSmoothingOnGuests(self): + for toonId in self.toonIds: + avatar = self.getAvatar(toonId) + if avatar: + if not self.usesSmoothing: + avatar.stopSmooth() + if not self.usesLookAround: + avatar.stopLookAround() + + def getAvatar(self, toonId): + if toonId in self.cr.doId2do: + return self.cr.doId2do[toonId] + else: + self.notify.warning('BASE: getAvatar: No avatar in doId2do with id: ' + str(toonId)) + return None + return None + + def getAvatarName(self, toonId): + avatar = self.getAvatar(toonId) + if avatar: + return avatar.getName() + else: + return 'Unknown' + + def isLocalToonInActivity(self): + result = False + place = base.cr.playGame.getPlace() + if place and place.__class__.__name__ == 'Party' and hasattr(place, 'fsm') and place.fsm: + result = place.fsm.getCurrentState().getName() == 'activity' + return result + + def getToonIdsAsList(self): + return self.toonIds + + def startRules(self, timeout = PartyGlobals.DefaultRulesTimeout): + self.notify.debug('BASE: startRules') + self.accept(self.rulesDoneEvent, self.handleRulesDone) + self.rulesPanel = MinigameRulesPanel('PartyRulesPanel', self.getTitle(), self.getInstructions(), self.rulesDoneEvent, timeout) + base.setCellsAvailable(base.bottomCells + [base.leftCells[0], base.rightCells[1]], False) + self.rulesPanel.load() + self.rulesPanel.enter() + + def finishRules(self): + self.notify.debug('BASE: finishRules') + self.ignore(self.rulesDoneEvent) + if hasattr(self, 'rulesPanel'): + self.rulesPanel.exit() + self.rulesPanel.unload() + del self.rulesPanel + base.setCellsAvailable(base.bottomCells + [base.leftCells[0], base.rightCells[1]], True) + + def handleRulesDone(self): + self.notify.error('BASE: handleRulesDone should be overridden') + + def getTitle(self): + return TTLocalizer.PartyActivityNameDict[self.activityId]['generic'] + + def local2ActivityTime(self, timestamp): + return timestamp - self.activityStartTime + + def activity2LocalTime(self, timestamp): + return timestamp + self.activityStartTime + + def getCurrentActivityTime(self): + return self.local2ActivityTime(globalClock.getFrameTime()) + + def disableEmotes(self): + Emote.globalEmote.disableAll(base.localAvatar) + + def enableEmotes(self): + Emote.globalEmote.releaseAll(base.localAvatar) diff --git a/toontown/parties/DistributedPartyActivityAI.py b/toontown/parties/DistributedPartyActivityAI.py new file mode 100755 index 00000000..6f026de9 --- /dev/null +++ b/toontown/parties/DistributedPartyActivityAI.py @@ -0,0 +1,80 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.parties import PartyGlobals, PartyUtils +""" +dclass DistributedPartyActivity : DistributedObject { + setX(int16/10) broadcast required; + setY(int16/10) broadcast required; + setH(uint16%360/100) broadcast required; + setPartyDoId(uint32) broadcast required; + toonJoinRequest() airecv clsend; + toonExitRequest() airecv clsend; + toonExitDemand() airecv clsend; + toonReady() airecv clsend; + joinRequestDenied(uint8); + exitRequestDenied(uint8); + setToonsPlaying(uint32 []) broadcast ram; + setState(string, int16) broadcast ram; + showJellybeanReward(uint32, uint8, string); +}; +""" +class DistributedPartyActivityAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyActivityAI") + + def __init__(self, air, parent, activityTuple): + DistributedObjectAI.__init__(self, air) + self.parent = parent + x, y, h = activityTuple[1:] # ignore activity ID + self.x = PartyUtils.convertDistanceFromPartyGrid(x, 0) + self.y = PartyUtils.convertDistanceFromPartyGrid(y, 1) + self.h = h * PartyGlobals.PartyGridHeadingConverter + self.toonsPlaying = [] + + def getX(self): + return self.x + + def getY(self): + return self.y + + def getH(self): + return self.h + + def getPartyDoId(self): + return self.parent + + def updateToonsPlaying(self): + self.sendUpdate('setToonsPlaying', [self.toonsPlaying]) + + def toonJoinRequest(self): + print 'toon join request' + avId = self.air.getAvatarIdFromSender() + #todo hackyfun i should FSM + self.toonsPlaying.append(avId) + self.updateToonsPlaying() + + def toonExitRequest(self): + print 'toon exit request' + + def toonExitDemand(self): + print 'toon exit demand' + avId = self.air.getAvatarIdFromSender() + self.toonsPlaying.remove(avId) + self.updateToonsPlaying() + + def toonReady(self): + print 'toon ready' + + def joinRequestDenied(self, todo0): + pass + + def exitRequestDenied(self, todo0): + pass + + def setToonsPlaying(self, todo0): + pass + + def setState(self, todo0, todo1): + pass + + def showJellybeanReward(self, todo0, todo1, todo2): + pass diff --git a/toontown/parties/DistributedPartyCannon.py b/toontown/parties/DistributedPartyCannon.py new file mode 100755 index 00000000..c0fd55fc --- /dev/null +++ b/toontown/parties/DistributedPartyCannon.py @@ -0,0 +1,449 @@ +from panda3d.core import * +from direct.distributed.DistributedObject import DistributedObject +from direct.task.Task import Task +from toontown.minigame import CannonGameGlobals +from toontown.minigame.CannonGameGlobals import * +from toontown.parties.Cannon import Cannon +from toontown.parties.CannonGui import CannonGui +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyCannonActivity import DistributedPartyCannonActivity +LAND_TIME = 2 +WORLD_SCALE = 2.0 +GROUND_SCALE = 1.4 * WORLD_SCALE +CANNON_SCALE = 1.0 +FAR_PLANE_DIST = 600 * WORLD_SCALE +GROUND_PLANE_MIN = -15 +CANNON_Y = -int(CannonGameGlobals.TowerYRange / 2 * 1.3) +CANNON_X_SPACING = 12 +CANNON_Z = 20 +CANNON_ROTATION_MIN = -55 +CANNON_ROTATION_MAX = 50 +CANNON_ROTATION_VEL = 15.0 +ROTATIONCANNON_ANGLE_MIN = 15 +CANNON_ANGLE_MAX = 85 +CANNON_ANGLE_VEL = 15.0 +CANNON_MOVE_UPDATE_FREQ = 0.5 +CAMERA_PULLBACK_MIN = 20 +CAMERA_PULLBACK_MAX = 40 +MAX_LOOKAT_OFFSET = 80 +TOON_TOWER_THRESHOLD = 150 +SHADOW_Z_OFFSET = 0.5 +TOWER_HEIGHT = 43.85 +TOWER_RADIUS = 10.5 +BUCKET_HEIGHT = 36 +TOWER_Y_RANGE = CannonGameGlobals.TowerYRange +TOWER_X_RANGE = int(TOWER_Y_RANGE / 2.0) +INITIAL_VELOCITY = 80.0 +WHISTLE_SPEED = INITIAL_VELOCITY * 0.35 + +class DistributedPartyCannon(DistributedObject, Cannon): + notify = directNotify.newCategory('DistributedPartyCannon') + LOCAL_CANNON_MOVE_TASK = 'localCannonMoveTask' + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + Cannon.__init__(self, parent=self.getParentNodePath()) + self.localCannonMoving = False + self.active = False + self.activityDoId = 0 + self.activity = None + self.gui = None + self.toonInsideAvId = 0 + self.sign = None + self.controllingToonAvId = None + return + + def generateInit(self): + self.load() + self.activate() + + def load(self): + self.notify.debug('load') + Cannon.load(self, self.uniqueName('Cannon')) + if base.cr and base.cr.partyManager and base.cr.partyManager.getShowDoid(): + nameText = TextNode('nameText') + nameText.setCardAsMargin(0.1, 0.1, 0.1, 0.1) + nameText.setCardDecal(True) + nameText.setCardColor(1.0, 1.0, 1.0, 0.0) + r = 232.0 / 255.0 + g = 169.0 / 255.0 + b = 23.0 / 255.0 + nameText.setTextColor(r, g, b, 1) + nameText.setAlign(nameText.ACenter) + nameText.setShadowColor(0, 0, 0, 1) + nameText.setText(str(self.doId)) + namePlate = self.parentNode.attachNewNode(nameText) + namePlate.setDepthWrite(0) + namePlate.setPos(0, 0, 8) + namePlate.setScale(3) + + def announceGenerate(self): + self.sign = self.activity.sign.instanceUnderNode(self.activity.getParentNodePath(), self.uniqueName('sign')) + self.sign.reparentTo(self.activity.getParentNodePath()) + self.sign.setPos(self.parentNode, self.sign.getPos()) + + def unload(self): + self.notify.debug('unload') + if self.gui is not None: + self.gui.unload() + del self.gui + Cannon.unload(self) + if self.sign is not None: + self.sign.removeNode() + self.sign = None + self.ignoreAll() + return + + def getParentNodePath(self): + if hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'loader') and base.cr.playGame.hood.loader and hasattr(base.cr.playGame.hood.loader, 'geom') and base.cr.playGame.hood.loader.geom: + return base.cr.playGame.hood.loader.geom + else: + self.notify.warning('Hood or loader not created, defaulting to render') + return render + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + self.__disableCannonControl() + self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, 0) + + def delete(self): + self.deactivate() + self.unload() + DistributedObject.delete(self) + + def destroy(self): + self.notify.debug('destroy') + DistributedObject.destroy(self) + + def setPosHpr(self, x, y, z, h, p, r): + self.parentNode.setPosHpr(x, y, z, h, p, r) + + def setActivityDoId(self, doId): + self.activityDoId = doId + self.activity = base.cr.doId2do[doId] + + def activate(self): + self.accept(self.getEnterCollisionName(), self.__handleToonCollisionWithCannon) + Cannon.show(self) + self.active = True + + def deactivate(self): + self.ignore(self.getEnterCollisionName()) + Cannon.hide(self) + self.active = False + + def setMovie(self, mode, avId): + self.notify.debug('%s setMovie(%s, %s)' % (self.doId, avId, mode)) + if mode == PartyGlobals.CANNON_MOVIE_CLEAR: + self.setClear() + elif mode == PartyGlobals.CANNON_MOVIE_FORCE_EXIT: + self.exitCannon(avId) + self.setClear() + elif mode == PartyGlobals.CANNON_MOVIE_LOAD: + self.enterCannon(avId) + elif mode == PartyGlobals.CANNON_MOVIE_LANDED: + self.setLanded(avId) + else: + self.notify.error('setMovie Unhandled case mode=%d avId=%d' % (mode, avId)) + + def __handleToonCollisionWithCannon(self, collEntry): + self.notify.debug('collEntry: %s' % collEntry) + if base.cr.playGame.getPlace().getState() == 'walk' and self.toonInsideAvId == 0: + base.cr.playGame.getPlace().setState('activity') + self.d_requestEnter() + + def d_requestEnter(self): + self.sendUpdate('requestEnter', []) + + def requestExit(self): + self.notify.debug('requestExit') + base.localAvatar.reparentTo(render) + base.cr.playGame.getPlace().setState('walk') + + def __avatarGone(self, avId): + if self.toonInsideAvId == avId: + self.notify.debug('__avatarGone in if') + if self.toonInside and not self.toonInside.isEmpty(): + self.removeToonDidNotFire() + self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, 0) + else: + self.notify.debug('__avatarGone in else, self.toonInsideAvId=%s avId=%s' % (self.toonInsideAvId, avId)) + + def enterCannon(self, avId): + if avId == base.localAvatar.doId: + base.localAvatar.pose('lose', 110) + base.localAvatar.pose('slip-forward', 25) + base.cr.playGame.getPlace().setState('activity') + base.localAvatar.collisionsOff() + camera.reparentTo(self.barrelNode) + camera.setPos(0, -2, 5) + camera.setP(-20) + if not self.activity.hasPlayedBefore(): + self.activity.displayRules() + self.acceptOnce(DistributedPartyCannonActivity.RULES_DONE_EVENT, self.__enableCannonControl) + else: + self.__enableCannonControl() + self.controllingToonAvId = avId + if avId in self.cr.doId2do: + self.toonInsideAvId = avId + self.notify.debug('enterCannon self.toonInsideAvId=%d' % self.toonInsideAvId) + toon = base.cr.doId2do[avId] + if toon: + self.acceptOnce(toon.uniqueName('disable'), self.__avatarGone, extraArgs=[avId]) + toon.stopSmooth() + toon.dropShadow.hide() + self.placeToonInside(toon) + else: + self.__avatarGone(avId) + else: + self.notify.warning('Unknown avatar %d in cannon %d' % (avId, self.doId)) + + def exitCannon(self, avId): + if avId == base.localAvatar.doId: + self.activity.finishRules() + self.ignore(DistributedPartyCannonActivity.RULES_DONE_EVENT) + self.ignoreDisableForAvId(avId) + if self.gui and avId == base.localAvatar.doId: + self.gui.unload() + toon = base.cr.doId2do.get(avId) + if toon and self.getToonInside() == toon: + self.resetToon() + else: + self.notify.debug('not resetting toon, toon=%s, self.getToonInside()=%s' % (toon, self.getToonInside())) + + def resetToon(self, pos = None): + self.notify.debug('resetToon') + toon = self.getToonInside() + toonInsideAvId = self.toonInsideAvId + self.notify.debug('%d resetToon self.toonInsideAvId=%d' % (self.doId, self.toonInsideAvId)) + self.removeToonDidNotFire() + self.__setToonUpright(toon, pos) + if toonInsideAvId == base.localAvatar.doId: + self.notify.debug('%d resetToon toonInsideAvId ==localAvatar.doId' % self.doId) + if pos: + self.notify.debug('toon setting position to %s' % pos) + base.localAvatar.setPos(pos) + camera.reparentTo(base.localAvatar) + base.localAvatar.collisionsOn() + base.localAvatar.startPosHprBroadcast() + base.localAvatar.enableAvatarControls() + self.notify.debug('currentState=%s, requesting walk' % base.cr.playGame.getPlace().getState()) + base.cr.playGame.getPlace().setState('walk') + self.notify.debug('after request walk currentState=%s,' % base.cr.playGame.getPlace().getState()) + toon.dropShadow.show() + self.d_setLanded() + + def __setToonUpright(self, toon, pos = None): + if not pos: + pos = toon.getPos(render) + toon.setPos(render, pos) + toon.loop('neutral') + toon.lookAt(self.parentNode) + toon.setP(0) + toon.setR(0) + toon.setScale(1, 1, 1) + + def d_setLanded(self): + self.notify.debugStateCall(self) + if self.toonInsideAvId == base.localAvatar.doId: + self.sendUpdate('setLanded', [base.localAvatar.doId]) + + def setLanded(self, avId): + self.removeAvFromCannon(avId) + self.ignoreDisableForAvId(avId) + + def removeAvFromCannon(self, avId): + place = base.cr.playGame.getPlace() + av = base.cr.doId2do.get(avId) + print 'removeAvFromCannon' + if place: + if not hasattr(place, 'fsm'): + return + placeState = place.fsm.getCurrentState().getName() + print placeState + if placeState != 'fishing': + if av != None: + av.startSmooth() + self.__destroyToonModels(avId) + return + self.notify.debug('%s removeAvFromCannon' % self.doId) + if av != None: + self.notify.debug('%d removeAvFromCannon: destroying toon models' % self.doId) + av.resetLOD() + if av == base.localAvatar: + if place: + place.fsm.request('walk') + av.setPlayRate(1.0, 'run') + if av.nametag and self.toonHead: + av.nametag.remove(self.toonHead.tag) + if av.getParent().getName() == 'toonOriginChange': + av.wrtReparentTo(render) + self.__setToonUpright(av) + if av == base.localAvatar: + av.startPosHprBroadcast() + av.startSmooth() + av.setScale(1, 1, 1) + self.ignore(av.uniqueName('disable')) + self.__destroyToonModels(avId) + return + + def __destroyToonModels(self, avId): + av = base.cr.doId2do.get(avId) + if not av: + return + if av != None: + av.dropShadow.show() + self.hitBumper = 0 + self.hitTarget = 0 + self.angularVel = 0 + self.vel = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.lastPos = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + av = None + self.lastWakeTime = 0 + self.localToonShooting = 0 + if self.toonHead != None: + self.toonHead.reparentTo(hidden) + self.toonHead.stopBlink() + self.toonHead.stopLookAroundNow() + self.toonHead.delete() + self.toonHead = None + self.model_Created = 0 + return + + def setClear(self): + toon = base.cr.doId2do.get(self.toonInsideAvId) + toonName = 'None' + self.ignoreDisableForAvId(self.toonInsideAvId) + if toon and self.isToonInside(): + toonName = toon.getName() + toon.resetLOD() + toon.setPlayRate(1.0, 'run') + if toon.getParent().getName() == 'toonOriginChange': + toon.wrtReparentTo(render) + self.__setToonUpright(toon) + toon.startSmooth() + toon.setScale(1, 1, 1) + self.ignore(toon.uniqueName('disable')) + if self.toonInsideAvId == base.localAvatar.doId: + toon.startPosHprBroadcast() + try: + base.localAvatar.enableAvatarControls() + except: + self.notify.warning("couldn't enable avatar controls") + + base.cr.playGame.getPlace().setState('walk') + else: + self.notify.debug('setClear in else toon=%s, self.isToonInsde()=%s' % (toonName, self.isToonInside())) + self.toonInsideAvId = 0 + self.notify.debug('setClear self.toonInsideAvId=%d' % self.toonInsideAvId) + if self.controllingToonAvId == base.localAvatar.doId: + self.notify.debug('set_clear turning off cannon control') + self.__disableCannonControl() + self.controllingToonAvId = 0 + + def __enableCannonControl(self): + if not self.gui: + self.gui = self.activity.gui + self.gui.load() + self.gui.enable(timer=PartyGlobals.CANNON_TIMEOUT) + self.d_setTimeout() + self.accept(CannonGui.FIRE_PRESSED, self.__handleFirePressed) + self.__startLocalCannonMoveTask() + + def d_setTimeout(self): + self.sendUpdate('setTimeout') + + def __disableCannonControl(self): + if self.gui: + self.gui.unload() + self.ignore(CannonGui.FIRE_PRESSED) + self.__stopLocalCannonMoveTask() + + def __startLocalCannonMoveTask(self): + self.localCannonMoving = False + task = Task(self.__localCannonMoveTask) + task.lastPositionBroadcastTime = 0.0 + taskMgr.add(task, self.LOCAL_CANNON_MOVE_TASK) + + def __stopLocalCannonMoveTask(self): + taskMgr.remove(self.LOCAL_CANNON_MOVE_TASK) + if self.localCannonMoving: + self.localCannonMoving = False + self.stopMovingSound() + + def __localCannonMoveTask(self, task): + rotVel = 0 + if self.gui.leftPressed: + rotVel += CANNON_ROTATION_VEL + if self.gui.rightPressed: + rotVel -= CANNON_ROTATION_VEL + self.setRotation(self.getRotation() + rotVel * globalClock.getDt()) + angVel = 0 + if self.gui.upPressed: + angVel += CANNON_ANGLE_VEL + if self.gui.downPressed: + angVel -= CANNON_ANGLE_VEL + self.setAngle(self.getAngle() + angVel * globalClock.getDt()) + if self.hasMoved(): + if not self.localCannonMoving: + self.localCannonMoving = True + self.loopMovingSound() + self.updateModel() + if task.time - task.lastPositionBroadcastTime > CANNON_MOVE_UPDATE_FREQ: + self.notify.debug('Broadcast local cannon %s position' % self.doId) + task.lastPositionBroadcastTime = task.time + self.__broadcastLocalCannonPosition() + elif self.localCannonMoving: + self.localCannonMoving = False + self.stopMovingSound() + self.__broadcastLocalCannonPosition() + self.notify.debug('Cannon Rot = %s, Angle = %s' % (self._rotation, self._angle)) + return Task.cont + + def __broadcastLocalCannonPosition(self): + self.d_setCannonPosition(self._rotation, self._angle) + + def d_setCannonPosition(self, zRot, angle): + self.sendUpdate('setCannonPosition', [zRot, angle]) + + def updateCannonPosition(self, avId, zRot, angle): + if avId and avId == self.toonInsideAvId and avId != base.localAvatar.doId: + self.notify.debug('update cannon %s position zRot = %d, angle = %d' % (self.doId, zRot, angle)) + self.setRotation(zRot) + self.setAngle(angle) + self.updateModel() + + def __handleFirePressed(self): + self.notify.debug('fire pressed') + self.__disableCannonControl() + self.__broadcastLocalCannonPosition() + self.d_setCannonLit(self._rotation, self._angle) + + def d_setCannonLit(self, zRot, angle): + self.sendUpdate('setCannonLit', [zRot, angle]) + + def fire(self): + if base.localAvatar.doId == self.controllingToonAvId: + self.__disableCannonControl() + self.d_setFired() + self.playFireSequence() + self.controllingToonAvId = None + return + + def d_setFired(self): + self.sendUpdate('setFired', []) + + def ignoreDisableForAvId(self, avId): + toon = base.cr.doId2do.get(avId) + if toon: + self.notify.debug('ignoring %s' % toon.uniqueName('disable')) + self.ignore(toon.uniqueName('disable')) + else: + self.notify.debug('ignoring disable-%s' % self.toonInsideAvId) + self.ignore('disable-%s' % self.toonInsideAvId) diff --git a/toontown/parties/DistributedPartyCannonAI.py b/toontown/parties/DistributedPartyCannonAI.py new file mode 100755 index 00000000..653a5ecd --- /dev/null +++ b/toontown/parties/DistributedPartyCannonAI.py @@ -0,0 +1,82 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.task import Task +import PartyGlobals + +class DistributedPartyCannonAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyCannonAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.actId = 0 + self.posHpr = [0, 0, 0, 0, 0, 0] + self.avId = 0 + + + def delete(self): + taskMgr.remove('removeToon%d' % self.doId) + DistributedObjectAI.delete(self) + + def setActivityDoId(self, actId): + self.actId = actId + + def getActivityDoId(self): + return self.actId + + def setPosHpr(self, x, y, z, h, p, r): + self.posHpr = [x, y, z, h, p, r] + + def getPosHpr(self): + return self.posHpr + + def requestEnter(self): + if not self.avId: + self.avId = self.air.getAvatarIdFromSender() + self.d_setMovie(PartyGlobals.CANNON_MOVIE_LOAD, self.avId) + + def requestExit(self): + avId = self.air.getAvatarIdFromSender() + if self.avId == avId: + self.sendUpdate('setCannonExit', [avId]) + self.avId = 0 + + def d_setMovie(self, movie, avId): + self.sendUpdate('setMovie', [movie, avId]) + + def setCannonPosition(self, rot, angle): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + self.sendUpdate('updateCannonPosition', [avId, rot, angle]) + + def setCannonLit(self, rot, angle): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + activity = self.air.doId2do[self.actId] + activity.b_setCannonWillFire(self.doId, rot, angle, avId) + self.d_setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, avId) + self.sendUpdate('setCannonExit', [avId]) + self.avId = 0 + + def setFired(self): + self.air.writeServerEvent('suspicious',self.air.getAvatarIdFromSender(),'Toon tried to call unused setFired!') + + def setLanded(self, toonId): + avId = self.air.getAvatarIdFromSender() + if toonId != avId: + self.air.writeServerEvent('suspicious',avId,'Toon claimed to be another toon in cannon!') + return + self.d_setMovie(PartyGlobals.CANNON_MOVIE_LANDED, avId) + + def setTimeout(self): + avId = self.air.getAvatarIdFromSender() + if avId != self.avId: + self.air.writeServerEvent('suspicious',avId,'Toon tried to start timer for someone else!') + taskMgr.doMethodLater(PartyGlobals.CANNON_TIMEOUT, self.__removeToon, 'removeToon%d' % self.doId, extraArgs=[avId]) + + + def __removeToon(self, avId): + if avId != self.avId: + return + self.avId = 0 + self.d_setMovie(PartyGlobals.CANNON_MOVIE_FORCE_EXIT, avId) + self.sendUpdate('setCannonExit', [avId]) diff --git a/toontown/parties/DistributedPartyCannonActivity.py b/toontown/parties/DistributedPartyCannonActivity.py new file mode 100755 index 00000000..7bd5d05c --- /dev/null +++ b/toontown/parties/DistributedPartyCannonActivity.py @@ -0,0 +1,954 @@ +import math +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.task.Task import Task +from panda3d.core import PythonTask +from toontown.toontowngui import TTDialog +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.effects import Splash, DustCloud, Wake +from toontown.minigame import Trajectory +from toontown.minigame import CannonGameGlobals +from toontown.parties import PartyGlobals +from toontown.parties.PartyGlobals import ActivityIds +from toontown.parties.PartyGlobals import ActivityTypes +from toontown.parties.PartyGlobals import FireworksStartedEvent +from toontown.parties.PartyGlobals import FireworksFinishedEvent +from toontown.parties.PartyGlobals import PartyCannonCollisions +from toontown.parties.DistributedPartyActivity import DistributedPartyActivity +from toontown.parties.CannonGui import CannonGui +from toontown.parties.PartyUtils import toRadians, toDegrees +CANNON_ROTATION_VEL = 15.0 +CANNON_ANGLE_VEL = 15.0 +GROUND_PLANE_MIN = -15 +SHADOW_Z_OFFSET = 0.5 +INITIAL_VELOCITY = 90.0 +WHISTLE_SPEED = INITIAL_VELOCITY * 0.35 + +class DistributedPartyCannonActivity(DistributedPartyActivity): + # I never go anywhere without my party cannon! + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyCannonActivity') + HIT_GROUND = 0 + HIT_TOWER = 1 + HIT_WATER = 2 + REACTIVATE_CLOUD_TASK = 'PartyActivity_ReactivateLastCloud' + RULES_DONE_EVENT = 'DistributedPartyCannonActivity_RULES_DONE_EVENT' + LOCAL_TOON_LANDED_EVENT = 'DistributedPartyCannonActivity_LOCAL_TOON_LANDED_EVENT' + NetDivisor = 100 + TimeFactor = 0.75 + BroadcastPeriod = 0.2 + + def __init__(self, cr): + DistributedPartyActivity.__init__(self, cr, ActivityIds.PartyCannon, ActivityTypes.Continuous, wantRewardGui=True) + self.gui = None + self.firingCannon = None + self.shadowNode = None + self.partyDoId = None + self.splash = None + self.dustCloud = None + self.lastWakeTime = 0 + self.localFlyingDropShadow = None + self.localFlyingToon = None + self.localFlyingToonId = 0 + self._lastBroadcastTime = -self.BroadcastPeriod + self._dirtyNewVel = None + self.hitBumper = 0 + self.hitCloud = 0 + self.lastPos = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.vel = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + self._lastCloudHit = None + self.cameraPos = Vec3(0, -15.0, -25.0) + self.cameraSpeed = 5.0 + self.camNode = None + self.flyingToonOffsetRotation = 0 + self.flyingToonOffsetAngle = 0 + self.flyingToonOffsetX = 0 + self.flyingToonOffsetY = 0 + self.flyingToonCloudsHit = 0 + self.initialFlyVel = 0 + self._localPlayedBefore = False + self.hitTrack = None + self.cTrav = None + self.flyColNode = None + self.flyColNodePath = None + self._flyingCollisionTaskName = None + return + + def generateInit(self): + DistributedPartyActivity.generateInit(self) + self.taskNameFireCannon = self.taskName('fireCannon') + self.taskNameShoot = self.taskName('shootTask') + self.taskNameFly = self.taskName('flyTask') + self.gui = CannonGui() + + def load(self): + self.notify.debug('load') + DistributedPartyActivity.load(self) + base.cr.playGame.hood.loader.loadClouds() + base.cr.playGame.hood.loader.setCloudSwitch(1) + self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.shadowNode = hidden.attachNewNode('dropShadow') + self.shadow.copyTo(self.shadowNode) + self.shadowNode.setColor(0, 0, 0, 0.5) + self.shadowNode.setBin('fixed', 0, 1) + self.splash = Splash.Splash(render) + self.dustCloud = DustCloud.DustCloud(render) + self.dustCloud.setBillboardPointEye() + self.sndHitGround = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg') + self.sndHitWater = base.loadSfx('phase_4/audio/sfx/MG_cannon_splash.ogg') + self.sndHitHouse = base.loadSfx('phase_5/audio/sfx/AA_drop_sandbag.ogg') + self.sndBounce1 = base.loadSfx('phase_13/audio/sfx/bounce1.ogg') + self.sndBounce2 = base.loadSfx('phase_13/audio/sfx/bounce2.ogg') + self.sndBounce3 = base.loadSfx('phase_13/audio/sfx/bounce3.ogg') + self.onstage() + self.sign.reparentTo(hidden) + self.sign.setPos(-6.0, 10.0, 0.0) + self.accept(FireworksStartedEvent, self.__handleFireworksStarted) + self.accept(FireworksFinishedEvent, self.__handleFireworksFinished) + + def generate(self): + DistributedPartyActivity.generate(self) + self._doneCannons = False + self._avId2trajectoryInfo = {} + self._remoteToonFlyTaskName = 'remoteToonFlyTask-%s' % self.doId + taskMgr.add(self._remoteToonFlyTask, self._remoteToonFlyTaskName, priority=45) + self.d_cloudsColorRequest() + + def unload(self): + self.notify.debug('unload') + DistributedPartyActivity.unload(self) + if self.shadowNode is not None: + self.shadowNode.removeNode() + del self.shadowNode + if self.splash is not None: + self.splash.destroy() + del self.splash + if self.dustCloud is not None: + self.dustCloud.destroy() + del self.dustCloud + del self.sndHitHouse + del self.sndHitGround + del self.sndHitWater + del self.sndBounce1 + del self.sndBounce2 + del self.sndBounce3 + if self.localFlyingToon: + self.__resetToon(self.localFlyingToon) + self.localFlyingToon.loop('neutral') + self.localFlyingToon.setPlayRate(1.0, 'run') + self.localFlyingToon = None + self.ignoreAll() + return + + def onstage(self): + self.notify.debug('onstage') + self.splash.reparentTo(render) + self.dustCloud.reparentTo(render) + + def offstage(self): + self.notify.debug('offstage') + if self.splash is not None: + self.splash.reparentTo(hidden) + self.splash.stop() + if self.dustCloud is not None: + self.dustCloud.reparentTo(hidden) + self.dustCloud.stop() + return + + def disable(self): + taskMgr.remove(self._remoteToonFlyTaskName) + if self._flyingCollisionTaskName: + taskMgr.remove(self._flyingCollisionTaskName) + taskMgr.remove(self.taskNameFireCannon) + taskMgr.remove(self.taskNameShoot) + taskMgr.remove(self.taskNameFly) + taskMgr.remove(DistributedPartyCannonActivity.REACTIVATE_CLOUD_TASK) + self.ignoreAll() + if self.localFlyingToonId: + self.__stopCollisionHandler(self.localFlyingToon) + self.__stopLocalFlyTask(self.localFlyingToonId) + self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, self.localFlyingToonId) + if self.hitTrack is not None: + self.hitTrack.finish() + del self.hitTrack + self.hitTrack = None + DistributedPartyActivity.disable(self) + return + + def delete(self): + self.offstage() + DistributedPartyActivity.delete(self) + + def setMovie(self, mode, toonId): + self.notify.debug('%s setMovie(%s, %s)' % (self.doId, toonId, mode)) + if toonId != base.localAvatar.doId: + return + if mode == PartyGlobals.CANNON_MOVIE_CLEAR: + self.landToon(toonId) + elif mode == PartyGlobals.CANNON_MOVIE_LANDED: + self.landToon(toonId) + elif mode == PartyGlobals.CANNON_MOVIE_FORCE_EXIT: + self.landToon(toonId) + + def __handleAvatarGone(self): + self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, 0) + + def handleToonDisabled(self, toonId): + self.notify.warning('handleToonDisabled no implementation yet') + + def handleToonJoined(self, toonId): + self.notify.warning('handleToonJoined no implementation yet') + + def isLocalToon(self, av): + return base.localAvatar == av + + def isLocalToonId(self, toonId): + return base.localAvatar.doId == toonId + + def getTitle(self): + return TTLocalizer.PartyCannonActivityTitle + + def getInstructions(self): + return TTLocalizer.PartyCannonActivityInstructions + + def hasPlayedBefore(self): + return self._localPlayedBefore + + def displayRules(self): + self.startRules() + + def handleRulesDone(self): + self.finishRules() + self._localPlayedBefore = True + messenger.send(DistributedPartyCannonActivity.RULES_DONE_EVENT) + + def setCannonWillFire(self, cannonId, zRot, angle): + self.notify.debug('setCannonWillFire: %d %d %d' % (cannonId, zRot, angle)) + cannon = base.cr.doId2do.get(cannonId) + if cannon is None: + self.notify.warning("Cannon has not been created, but we got this message. Don't show firing.") + return + if not cannon.getToonInside(): + self.notify.warning("setCannonWillFire, but no toon insde. Don't show firing") + return + if self.isLocalToon(cannon.getToonInside()): + self.localFlyingToon = base.localAvatar + self.localFlyingToonId = base.localAvatar.doId + self.localFiringCannon = cannon + self.flyingToonCloudsHit = 0 + cannon.updateModel(zRot, angle) + toonId = cannon.getToonInside().doId + task = PythonTask(self.__fireCannonTask) + task.toonId = toonId + task.cannon = cannon + taskMgr.add(task, self.taskNameFireCannon) + self.toonIds.append(toonId) + return + + def __fireCannonTask(self, task): + launchTime = 0.0 + toonId = task.toonId + cannon = task.cannon + toon = cannon.getToonInside() + self.notify.debug(str(self.doId) + ' FIRING CANNON FOR TOON ' + str(toonId)) + if not cannon.isToonInside(): + return Task.done + if self.isLocalToonId(toonId): + self.inWater = 0 + startPos, startHpr, startVel, trajectory = self.__calcFlightResults(cannon, toonId, launchTime) + + self.notify.debug('start position: ' + str(startPos)) + self.notify.debug('start velocity: ' + str(startVel)) + self.notify.debug('time of launch: ' + str(launchTime)) + cannon.removeToonReadyToFire() + shootTask = PythonTask(self.__shootTask, self.taskNameShoot) + shootTask.info = {'toonId': toonId, + 'cannon': cannon} + if self.isLocalToonId(toonId): + self.flyingToonOffsetRotation = 0 + self.flyingToonOffsetAngle = 0 + self.flyingToonOffsetX = 0 + self.flyingToonOffsetY = 0 + self.hitCloud = 0 + self.initialFlyVel = INITIAL_VELOCITY + self.camNode = NodePath(self.uniqueName('flyingCamera')) + self.camNode.setScale(0.5) + self.camNode.setPos(self.localFlyingToon.getPos()) + self.camNode.setHpr(self.localFlyingToon.getHpr()) + self.camNode.reparentTo(render) + self.lastStartVel = startVel + place = base.cr.playGame.getPlace() + place.fsm.request('activity') + toon.dropShadow.hide() + self.localFlyingDropShadow = self.shadowNode.copyTo(hidden) + vel = startVel + toon.lookAt(toon.getPos() + Vec3(vel[0], vel[1], vel[2])) + toon.setP(localAvatar, -90) + hpr = toon.getHpr() + toon.d_setPosHpr(startPos[0], startPos[1], startPos[2], hpr[0], hpr[1], hpr[2]) + self.localFlyingToon.wrtReparentTo(render) + info = {} + info['toonId'] = toonId + info['trajectory'] = trajectory + info['launchTime'] = launchTime + info['toon'] = self.localFlyingToon + info['hRot'] = cannon.getRotation() + camera.wrtReparentTo(self.localFlyingToon) + flyTask = PythonTask(self.__localFlyTask, self.taskNameFly) + flyTask.info = info + seqTask = Task.sequence(shootTask, flyTask) + self.__startCollisionHandler() + self.notify.debug('Disable standard local toon controls.') + base.localAvatar.disableAvatarControls() + frameTime = globalClock.getFrameTime() + netLaunchTime = globalClockDelta.localToNetworkTime(launchTime + frameTime, bits=31) + self.sendUpdate('setToonTrajectoryAi', [netLaunchTime, + startPos[0], + startPos[1], + startPos[2], + startHpr[0], + startHpr[1], + startHpr[2], + startVel[0], + startVel[1], + startVel[2]]) + else: + seqTask = shootTask + taskMgr.add(seqTask, self.taskName('flyingToon') + '-' + str(toonId)) + toon.startSmooth() + return Task.done + + def setToonTrajectory(self, avId, launchTime, x, y, z, h, p, r, vx, vy, vz): + if avId == localAvatar.doId: + return + startPos = Vec3(x, y, z) + startHpr = Vec3(h, p, r) + startVel = Vec3(vx, vy, vz) + startT = globalClockDelta.networkToLocalTime(launchTime, bits=31) + 0.2 + trajectory = Trajectory.Trajectory(0.0, startPos, startVel) + self._avId2trajectoryInfo[avId] = ScratchPad(startPos=startPos, startHpr=startHpr, startVel=startVel, startT=startT, trajectory=trajectory) + + def _remoteToonFlyTask(self, task = None): + ids2del = [] + frameTime = globalClock.getFrameTime() + for avId, trajInfo in self._avId2trajectoryInfo.iteritems(): + trajectory = trajInfo.trajectory + startTime = trajInfo.startT + groundTime = trajectory.calcTimeOfImpactOnPlane(0.0) / self.TimeFactor + startTime + now = frameTime + if now < startTime: + now = startTime + if now > groundTime: + now = groundTime + t = max(0.0, now - startTime) + t *= self.TimeFactor + toon = self.cr.getDo(avId) + if toon is None: + ids2del.append(avId) + else: + toon.setFluidPos(trajectory.getPos(t)) + vel = trajectory.getVel(t) + toon.lookAt(toon.getPos() + Vec3(vel[0], vel[1], vel[2])) + toon.setP(toon, -90) + + for avId in ids2del: + del self._avId2trajectoryInfo[avId] + + return Task.cont + + def __calcFlightResults(self, cannon, toonId, launchTime): + def quantizeVec(vec, divisor): + quantize = lambda value, divisor: float(int(value * int(divisor))) / int(divisor) + vec[0] = quantize(vec[0], divisor) + vec[1] = quantize(vec[1], divisor) + vec[2] = quantize(vec[2], divisor) + + startPos = cannon.getToonFirePos() + startHpr = cannon.getToonFireHpr() + startVel = cannon.getToonFireVel() + quantizeVec(startPos, self.NetDivisor) + quantizeVec(startHpr, self.NetDivisor) + quantizeVec(startVel, self.NetDivisor) + trajectory = Trajectory.Trajectory(launchTime, startPos, startVel) + self.trajectory = trajectory + return startPos, startHpr, startVel, trajectory + + def __shootTask(self, task): + task.info['cannon'].fire() + toonId = task.info['toonId'] + toon = base.cr.doId2do.get(toonId) + if toon: + toon.loop('swim') + else: + self.notify.debug('__shootTask avoided a crash, toon %d not found' % toonId) + if self.isLocalToonId(task.info['toonId']): + self.localFlyingDropShadow.reparentTo(render) + self.gui.enableAimKeys() + return Task.done + + def d_setLanded(self, toonId): + printStack() + self.notify.debug('d_setLanded %s' % toonId) + if self.isLocalToonId(toonId): + if self.cr: + self.sendUpdate('setLanded', [toonId]) + else: + self.notify.debug('we avoided crash 2') + + def setLanded(self, toonId): + if toonId in self._avId2trajectoryInfo: + del self._avId2trajectoryInfo[toonId] + + def landToon(self, toonId): + self.notify.debug('%s landToon' % self.doId) + toon = base.cr.doId2do.get(toonId) + if toon is not None: + toon.resetLOD() + if toon == base.localAvatar: + self.__stopCollisionHandler(base.localAvatar) + toon.wrtReparentTo(render) + self.__setToonUpright(toon) + toon.setPlayRate(1.0, 'run') + toon.startSmooth() + toon.setScale(1.0) + self.ignore(toon.uniqueName('disable')) + self.__cleanupFlyingToonData(toon) + toon.dropShadow.show() + place = base.cr.playGame.getPlace() + if place is not None: + if not hasattr(place, 'fsm'): + return + if toon is not None and toon == base.localAvatar: + self.__localDisplayLandedResults() + return + + def __localDisplayLandedResults(self): + if self.flyingToonCloudsHit > 0: + self._doneCannons = True + else: + self.__localToonDoneLanding() + + def handleRewardDone(self): + DistributedPartyActivity.handleRewardDone(self) + if self._doneCannons: + self.__localToonDoneLanding() + + def __localToonDoneLanding(self): + base.cr.playGame.getPlace().fsm.request('walk') + self.notify.debug('__localToonDoneLanding') + base.localAvatar.collisionsOn() + base.localAvatar.startPosHprBroadcast() + base.localAvatar.enableAvatarControls() + messenger.send(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT) + + def __setToonUpright(self, toon, pos = None): + if toon: + if self.inWater: + toon.setP(0) + toon.setR(0) + return + if not pos: + pos = toon.getPos(render) + toon.setPos(render, pos) + toon.loop('neutral') + if self.localFiringCannon and hasattr(self.localFiringCannon, 'cannonNode'): + if self.localFiringCannon.cannonNode: + toon.lookAt(self.localFiringCannon.cannonNode) + else: + self.notify.debug('we avoided crash 1.') + toon.setP(0) + toon.setR(0) + toon.setScale(1, 1, 1) + + def __resetToonToCannon(self, avatar): + self.notify.debug('__resetToonToCannon') + if not avatar and self.localFlyingToonId: + avatar = base.cr.doId2do.get(self.localFlyingToonId, None) + if avatar: + self.__resetToon(avatar) + return + + def __resetToon(self, avatar, pos = None): + self.notify.debug('__resetToon') + if avatar: + self.__stopCollisionHandler(avatar) + self.__setToonUpright(avatar, pos) + if self.isLocalToonId(avatar.doId): + self.notify.debug('toon setting position to %s' % pos) + if pos: + base.localAvatar.setPos(pos) + camera.reparentTo(avatar) + self.d_setLanded(avatar.doId) + + def __updateFlightVelocity(self, trajectory): + hpr = LRotationf(self.flyingToonOffsetRotation, 0, 0) + newVel = hpr.xform(self.lastStartVel) + hpr = LRotationf(0, self.flyingToonOffsetAngle, 0) + zVel = hpr.xform(self.lastStartVel).getZ() + if zVel < newVel.getZ(): + newVel.setZ(zVel) + trajectory.setStartVel(newVel) + now = globalClock.getFrameTime() + if now - self._lastBroadcastTime >= self.BroadcastPeriod: + self._dirtyNewVel = newVel + if self._dirtyNewVel: + self.sendUpdate('updateToonTrajectoryStartVelAi', [self._dirtyNewVel[0], self._dirtyNewVel[1], self._dirtyNewVel[2]]) + self._lastBroadcastTime = now + self._dirtyNewVel = None + return + + def updateToonTrajectoryStartVel(self, avId, vx, vy, vz): + if avId == localAvatar.doId: + return + if avId in self._avId2trajectoryInfo: + self._avId2trajectoryInfo[avId].trajectory.setStartVel(Vec3(vx, vy, vz)) + + def __isFlightKeyPressed(self): + return self.gui.leftPressed or self.gui.rightPressed or self.gui.upPressed or self.gui.downPressed + + def __moveFlyingToon(self, toon): + toonP = toon.getP(render) + isToonFlyingHorizontal = toonP > -150 and toonP < -30 + OFFSET = 0.25 + rotVel = 0 + if self.gui.leftPressed: + if isToonFlyingHorizontal: + rotVel += CANNON_ROTATION_VEL + else: + self.flyingToonOffsetX -= OFFSET + if self.gui.rightPressed: + if isToonFlyingHorizontal: + rotVel -= CANNON_ROTATION_VEL + else: + self.flyingToonOffsetX += OFFSET + self.flyingToonOffsetRotation += rotVel * globalClock.getDt() + angVel = 0 + if self.gui.upPressed: + if not isToonFlyingHorizontal: + self.flyingToonOffsetY -= OFFSET + if self.gui.downPressed: + if isToonFlyingHorizontal: + angVel += CANNON_ANGLE_VEL + else: + self.flyingToonOffsetY += OFFSET + self.flyingToonOffsetAngle += angVel * globalClock.getDt() + + def __stopLocalFlyTask(self, toonId): + taskMgr.remove(self.taskName('flyingToon') + '-' + str(toonId)) + self.gui.disableAimKeys() + + def __localFlyTask(self, task): + toon = task.info['toon'] + if toon.isEmpty(): + self.__resetToonToCannon(self.localFlyingToon) + return Task.done + curTime = task.time + task.info['launchTime'] + t = curTime + t *= self.TimeFactor + self.lastT = self.t + self.t = t + deltaT = self.t - self.lastT + self.deltaT = deltaT + if self.hitBumper: + pos = self.lastPos + self.lastVel * deltaT + vel = self.lastVel + self.lastVel += Vec3(0, 0, -32.0) * deltaT + self.lastPos = pos + toon.setFluidPos(pos) + lastR = toon.getR() + toon.setR(lastR - deltaT * self.angularVel * 2.0) + cameraView = 0 + else: + if not self.hitCloud and self.__isFlightKeyPressed(): + self.__moveFlyingToon(toon) + self.__updateFlightVelocity(task.info['trajectory']) + if self.hitCloud == 1: + vel = task.info['trajectory'].getVel(t) + startPos = toon.getPos(render) + task.info['trajectory'].setStartTime(t) + task.info['trajectory'].setStartPos(startPos) + task.info['trajectory'].setStartVel(self.lastVel) + toon.lookAt(toon.getPos() + vel) + toon.setH(-toon.getH()) + now = globalClock.getFrameTime() + netLaunchTime = globalClockDelta.localToNetworkTime(now, bits=31) + hpr = toon.getHpr() + self.sendUpdate('setToonTrajectoryAi', [netLaunchTime, + startPos[0], + startPos[1], + startPos[2], + hpr[0], + hpr[1], + hpr[2], + self.lastVel[0], + self.lastVel[1], + self.lastVel[2]]) + self._lastBroadcastTime = now + self._dirtyNewVel = None + self.flyingToonOffsetRotation = 0 + self.flyingToonOffsetAngle = 0 + self.flyingToonOffsetX = 0 + self.flyingToonOffsetY = 0 + self.hitCloud = 2 + pos = task.info['trajectory'].getPos(t) + toon.setFluidPos(pos) + toon.setFluidPos(toon, self.flyingToonOffsetX, self.flyingToonOffsetY, 0) + vel = task.info['trajectory'].getVel(t) + toon.lookAt(toon.getPos() + Vec3(vel[0], vel[1], vel[2])) + toon.setP(toon.getP() - 90) + cameraView = 2 + if self.hitCloud == 2: + self.lastStartVel = vel + self.hitCloud = 0 + shadowPos = toon.getPos() + shadowPos.setZ(SHADOW_Z_OFFSET) + self.localFlyingDropShadow.setPos(shadowPos) + if pos.getZ() < -20 or pos.getZ() > 1000: + self.notify.debug('stopping fly task toon.getZ()=%.2f' % pos.getZ()) + self.__resetToonToCannon(self.localFlyingToon) + return Task.done + self.__setFlyingCameraView(task.info['toon'], cameraView, deltaT) + return Task.cont + + def __setFlyingCameraView(self, toon, view, deltaT): + if toon != base.localAvatar: + return + lookAt = toon.getPos(render) + hpr = toon.getHpr(render) + if view == 0: + camera.wrtReparentTo(render) + camera.lookAt(lookAt) + elif view == 1: + camera.reparentTo(render) + camera.setPos(render, 100, 100, 35.25) + camera.lookAt(render, lookAt) + elif view == 2: + if camera.getParent() != self.camNode: + camera.wrtReparentTo(self.camNode) + camera.setPos(self.cameraPos) + camera.lookAt(toon) + self.camNode.setPos(toon.getPos(render)) + camHpr = self.camNode.getHpr(toon) + vec = -Point3(0, 0, 0) - camHpr + relativeSpeed = math.pow(vec.length() / 60.0, 2) + 0.1 + newHpr = camHpr + vec * deltaT * self.cameraSpeed * relativeSpeed + self.camNode.setHpr(toon, newHpr) + camera.lookAt(self.camNode) + camera.setR(render, 0) + + def __cleanupFlyingToonData(self, toon): + self.notify.debug('__cleanupFlyingToonData') + if toon: + toon.dropShadow.show() + self.toonIds.remove(toon.doId) + if self.isLocalToon(toon): + if self.localFlyingDropShadow != None: + self.localFlyingDropShadow.removeNode() + self.localFlyingDropShadow = None + self.hitBumper = 0 + self.angularVel = 0 + self.vel = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.lastPos = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + self.lastWakeTime = 0 + self.localFlyingToon = None + self.localFlyingToonId = 0 + self.localFiringCannon = None + if hasattr(self, 'camNode') and self.camNode: + self.camNode.removeNode() + self.camNode = None + return + + def __startCollisionHandler(self): + self.flyColSphere = CollisionSphere(0, 0, self.localFlyingToon.getHeight() / 2.0, 1.0) + self.flyColNode = CollisionNode(self.uniqueName('flySphere')) + self.flyColNode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.FloorBitmask) + self.flyColNode.addSolid(self.flyColSphere) + self.flyColNodePath = self.localFlyingToon.attachNewNode(self.flyColNode) + self.flyColNodePath.setColor(1, 0, 0, 1) + self._activeCollisions = set() + self.handler = CollisionHandlerQueue() + self._flyingCollisionTaskName = 'checkFlyingToonCollision-%s' % self.doId + taskMgr.add(self._checkFlyingToonCollision, self._flyingCollisionTaskName) + base.cTrav.addCollider(self.flyColNodePath, self.handler) + + def __stopCollisionHandler(self, avatar): + self.notify.debug('%s __stopCollisionHandler' % self.doId) + if self._flyingCollisionTaskName: + taskMgr.remove(self._flyingCollisionTaskName) + self._flyingCollisionTaskName = None + self._activeCollisions = set() + if avatar: + avatar.loop('neutral') + if self.flyColNode: + self.flyColNode = None + self.flyColSphere = None + if self.flyColNodePath: + base.cTrav.removeCollider(self.flyColNodePath) + self.flyColNodePath.removeNode() + self.flyColNodePath = None + self.handler = None + return + + def _checkFlyingToonCollision(self, task = None): + curCollisions = set() + if self.handler.getNumEntries(): + self.handler.sortEntries() + i = self.handler.getNumEntries() + activeEntry = None + while i > 0: + entry = self.handler.getEntry(i - 1) + k = (str(entry.getFromNodePath()), str(entry.getIntoNodePath())) + curCollisions.add(k) + if activeEntry is None and k not in self._activeCollisions: + activeEntry = entry + self._activeCollisions.add(k) + i -= 1 + + if activeEntry is not None: + self.__handleFlyingToonCollision(activeEntry) + if self.handler: + self.handler.clearEntries() + for k in list(self._activeCollisions): + if k not in curCollisions: + self._activeCollisions.remove(k) + + return Task.cont + + def __handleFlyingToonCollision(self, collisionEntry): + self.notify.debug('%s __handleToonCollision' % self.doId) + if self.localFlyingToon == None or self.flyColNode == None: + return + hitNode = collisionEntry.getIntoNode().getName() + self.notify.debug('hitNode = %s' % hitNode) + self.notify.debug('hitNodePath.getParent = %s' % collisionEntry.getIntoNodePath().getParent()) + self.vel = self.trajectory.getVel(self.t) + vel = self.trajectory.getVel(self.t) + vel.normalize() + if self.hitBumper: + vel = self.lastVel * 1 + vel.normalize() + self.notify.debug('normalized vel=%s' % vel) + solid = collisionEntry.getInto() + intoNormal = collisionEntry.getSurfaceNormal(collisionEntry.getIntoNodePath()) + self.notify.debug('old intoNormal = %s' % intoNormal) + intoNormal = collisionEntry.getSurfaceNormal(render) + self.notify.debug('new intoNormal = %s' % intoNormal) + hitPylonAboveWater = False + hitPylonBelowWater = False + hitNormal = intoNormal + if hitNode.find('cSphere') == 0 or hitNode.find('treasureSphere') == 0 or hitNode.find('prop') == 0 or hitNode.find('distAvatarCollNode') == 0 or hitNode.find('CannonSphere') == 0 or hitNode.find('plotSphere') == 0 or hitNode.find('flySphere') == 0 or hitNode.find('FishingSpotSphere') == 0 or hitNode.find('TrampolineTrigger') == 0 or hitNode == 'gagtree_collision' or hitNode == 'sign_collision' or hitNode == 'FlowerSellBox' or hitPylonBelowWater: + self.notify.debug('--------------hit and ignoring %s' % hitNode) + return + if vel.dot(hitNormal) > 0 and not hitNode == 'collision_roof' and not hitNode == 'collision_fence': + self.notify.debug('--------------hit and ignoring backfacing %s, dot=%s' % (hitNode, vel.dot(hitNormal))) + return + intoNode = collisionEntry.getIntoNodePath() + bumperNodes = ['sky_collision'] + PartyCannonCollisions['bounce'] + PartyCannonCollisions['fence'] + cloudBumpers = PartyCannonCollisions['clouds'] + bumperNodes += cloudBumpers + if hitNode in bumperNodes or hitNode.find('cogPie') == 0 or PartyCannonCollisions['trampoline_bounce'] in hitNode: + if hitNode == 'sky_collision' or hitNode in PartyCannonCollisions['fence'] or hitNode.find('cogPie') == 0: + self.__hitFence(self.localFlyingToon, collisionEntry) + elif PartyCannonCollisions['trampoline_bounce'] in hitNode or hitNode in PartyCannonCollisions['bounce']: + if hitNode == 'wall_collision': + hitSound = self.sndBounce2 + else: + hitSound = self.sndBounce3 + self.hitCloud = 1 + self.__hitBumper(self.localFlyingToon, collisionEntry, hitSound, kr=0.09, angVel=5) + self.hitBumper = 0 + elif hitNode in cloudBumpers: + self.__hitCloudPlatform(self.localFlyingToon, collisionEntry) + elif hitNode == 'statuaryCol': + self.__hitStatuary(self.localFlyingToon, collisionEntry) + else: + self.notify.debug('*************** hit something else ************') + return + else: + self.__stopCollisionHandler(self.localFlyingToon) + self.__stopLocalFlyTask(self.localFlyingToonId) + self.notify.debug('stopping flying since we hit %s' % hitNode) + if self.isLocalToonId(self.localFlyingToon.doId): + camera.wrtReparentTo(render) + if self.localFlyingDropShadow: + self.localFlyingDropShadow.reparentTo(hidden) + pos = collisionEntry.getSurfacePoint(render) + hpr = self.localFlyingToon.getHpr() + hitPos = collisionEntry.getSurfacePoint(render) + pos = hitPos + self.landingPos = pos + self.notify.debug('hitNode, Normal = %s,%s' % (hitNode, intoNormal)) + track = Sequence() + track.append(Func(self.localFlyingToon.wrtReparentTo, render)) + if self.isLocalToonId(self.localFlyingToon.doId): + track.append(Func(self.localFlyingToon.collisionsOff)) + if hitNode in PartyCannonCollisions['ground']: + track.append(Func(self.__hitGround, self.localFlyingToon, pos)) + track.append(Wait(1.0)) + track.append(Func(self.__setToonUpright, self.localFlyingToon, self.landingPos)) + elif hitNode in PartyCannonCollisions['fence']: + track.append(Func(self.__hitFence, self.localFlyingToon, collisionEntry)) + elif hitNode == 'collision3': + track.append(Func(self.__hitWater, self.localFlyingToon, pos, collisionEntry)) + track.append(Wait(2.0)) + track.append(Func(self.__setToonUpright, self.localFlyingToon, self.landingPos)) + elif hitNode.find('cloudSphere') == 0: + track.append(Func(self.__hitCloudPlatform, self.localFlyingToon, collisionEntry)) + else: + self.notify.warning('************* unhandled hitNode=%s parent =%s' % (hitNode, collisionEntry.getIntoNodePath().getParent())) + track.append(Func(self.d_setLanded, self.localFlyingToonId)) + if self.isLocalToonId(self.localFlyingToonId): + track.append(Func(self.localFlyingToon.collisionsOn)) + if self.hitTrack: + self.hitTrack.finish() + self.hitTrack = track + self.hitTrack.start() + return + + def __hitBumper(self, avatar, collisionEntry, sound, kr = 0.6, angVel = 1): + self.hitBumper = 1 + base.playSfx(sound) + hitP = avatar.getPos(render) + self.lastPos = hitP + normal = collisionEntry.getSurfaceNormal(render) + self.notify.debug('normal = %s' % normal) + vel = self.vel * 1 + speed = vel.length() + vel.normalize() + self.notify.debug('old vel = %s' % vel) + if self.hitCloud: + centerVec = Vec3(-avatar.getPos(self.getParentNodePath())) + centerVec.setZ(0) + d = centerVec.length() / 15.0 + centerVec.setZ(abs(centerVec.length() * math.sin(70.0))) + centerVec.normalize() + newVel = centerVec * d + normal * 0.2 + newVel = newVel * (kr * speed) + self.initialFlyVel = kr * speed + else: + newVel = (normal * 2.0 + vel) * (kr * speed) + self.lastVel = newVel + self.notify.debug('new vel = %s' % newVel) + self.angularVel = angVel * 360 + if self.hitCloud: + return + t = Sequence(Func(avatar.pose, 'lose', 110)) + t.start() + + def __hitGround(self, avatar, pos, extraArgs = []): + self.notify.debug('__hitGround') + hitP = avatar.getPos(render) + self.notify.debug('hitGround pos = %s, hitP = %s' % (pos, hitP)) + self.notify.debug('avatar hpr = %s' % avatar.getHpr()) + avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + avatar.setHpr(avatar.getH(), -135, 0) + self.notify.debug('parent = %s' % avatar.getParent()) + self.notify.debug('pos = %s, hpr = %s' % (avatar.getPos(render), avatar.getHpr(render))) + self.__playDustCloud(avatar, pos) + base.playSfx(self.sndHitGround) + avatar.setPlayRate(2.0, 'run') + avatar.loop('run') + + def __playDustCloud(self, toon, pos): + self.dustCloud.setPos(render, pos[0], pos[1], pos[2] + toon.getHeight() / 3.0) + self.dustCloud.setScale(0.35) + self.dustCloud.play() + + def __hitFence(self, avatar, collisionEntry, extraArgs = []): + self.notify.debug('__hitFence') + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.2, angVel=3) + + def __hitWater(self, avatar, pos, collisionEntry, extraArgs = []): + hitP = avatar.getPos(render) + if hitP[2] > ToontownGlobals.EstateWakeWaterHeight: + self.notify.debug('we hit the ground before we hit water') + self.__hitGround(avatar, pos, extraArgs) + return + self.notify.debug('hit water') + hitP = avatar.getPos(render) + avatar.loop('neutral') + self.splash.setPos(hitP) + self.splash.setZ(ToontownGlobals.EstateWakeWaterHeight) + self.splash.setScale(2) + self.splash.play() + base.playSfx(self.sndHitWater) + place = base.cr.playGame.getPlace() + + def __hitStatuary(self, avatar, collisionEntry, extraArgs = []): + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.4, angVel=5) + + def d_cloudsColorRequest(self): + self.notify.debug('cloudsColorRequest') + self.sendUpdate('cloudsColorRequest') + + def cloudsColorResponse(self, cloudColorList): + self.notify.debug('cloudsColorResponse: %s' % cloudColorList) + for cloudColor in cloudColorList: + self.setCloudHit(*cloudColor) + + def d_requestCloudHit(self, cloudNumber, color): + self.sendUpdate('requestCloudHit', [cloudNumber, + color.getX(), + color.getY(), + color.getZ()]) + + def setCloudHit(self, cloudNumber, r, g, b): + cloud = render.find('**/cloud-%d' % cloudNumber) + if not cloud.isEmpty(): + cloud.setColor(r, g, b, 1.0) + else: + self.notify.debug('Could not find cloud-%d' % cloudNumber) + + def __hitCloudPlatform(self, avatar, collisionEntry, extraArgs = []): + if not self.hitBumper and not self.hitCloud: + self.hitCloud = 1 + self.__hitBumper(avatar, collisionEntry, self.sndBounce1, kr=0.35, angVel=5) + self.hitBumper = 0 + if self._lastCloudHit is None: + cloud = collisionEntry.getIntoNodePath().getParent() + self._lastCloudHit = cloud + cloud.setColor(base.localAvatar.style.getHeadColor()) + cloudNumber = int(cloud.getNetTag('number')) + self.d_requestCloudHit(cloudNumber, base.localAvatar.style.getHeadColor()) + self.__playDustCloud(avatar, collisionEntry.getSurfacePoint(render)) + self.flyingToonCloudsHit += 1 + taskMgr.doMethodLater(0.25, self.__reactivateLastCloudHit, DistributedPartyCannonActivity.REACTIVATE_CLOUD_TASK) + return + + def __reactivateLastCloudHit(self, task): + self._lastCloudHit = None + return Task.done + + def __handleFireworksStarted(self): + self.notify.debug('__handleFireworksStarted') + base.cr.playGame.hood.loader.fadeClouds() + + def __handleFireworksFinished(self): + self.notify.debug('__handleFireworksFinished') + if self.__checkHoodValidity(): + base.cr.playGame.hood.loader.fadeClouds() + else: + self.notify.debug('Toon has left the party') + + def __checkHoodValidity(self): + if hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'loader') and base.cr.playGame.hood.loader and hasattr(base.cr.playGame.hood.loader, 'geom') and base.cr.playGame.hood.loader.geom: + return True + else: + return False + + def handleToonExited(self, toonId): + self.notify.debug('DistributedPartyCannonActivity handleToonExited( toonId=%s ) ' % toonId) + if toonId in self.cr.doId2do: + self.notify.warning('handleToonExited is not defined') diff --git a/toontown/parties/DistributedPartyCannonActivityAI.py b/toontown/parties/DistributedPartyCannonActivityAI.py new file mode 100755 index 00000000..2305ef81 --- /dev/null +++ b/toontown/parties/DistributedPartyCannonActivityAI.py @@ -0,0 +1,69 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyActivityAI import DistributedPartyActivityAI +from toontown.toonbase import TTLocalizer +import PartyGlobals + +class DistributedPartyCannonActivityAI(DistributedPartyActivityAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyCannonActivityAI") + + def __init__(self, air, parent, activityTuple): + DistributedPartyActivityAI.__init__(self, air, parent, activityTuple) + self.cloudColors = {} + self.cloudsHit = {} + + def setMovie(self, todo0, todo1): + pass + + def setLanded(self, toonId): + avId = self.air.getAvatarIdFromSender() + if avId != toonId: + self.air.writeServerEvent('suspicious',avId,'Toon tried to land someone else!') + return + if not avId in self.toonsPlaying: + self.air.writeServerEvent('suspicious',avId,'Toon tried to land while not playing the cannon activity!') + return + self.toonsPlaying.remove(avId) + reward = self.cloudsHit[avId] * PartyGlobals.CannonJellyBeanReward + if reward > PartyGlobals.CannonMaxTotalReward: + reward = PartyGlobals.CannonMaxTotalReward + av = self.air.doId2do.get(avId, None) + if not av: + self.air.writeServerEvent('suspicious',avId,'Toon tried to award beans while not in district!') + return + # TODO: Pass a msgId(?) to the client so the client can use whatever localizer it chooses. + # Ideally, we shouldn't even be passing strings that *should* be localized. + self.sendUpdateToAvatarId(avId, 'showJellybeanReward', [reward, av.getMoney(), TTLocalizer.PartyCannonResults % (reward, self.cloudsHit[avId])]) + av.addMoney(reward) + self.sendUpdate('setMovie', [PartyGlobals.CANNON_MOVIE_LANDED, avId]) + del self.cloudsHit[avId] + + def b_setCannonWillFire(self, cannonId, rot, angle, toonId): + self.toonsPlaying.append(toonId) + self.cloudsHit[toonId] = 0 + self.sendUpdate('setCannonWillFire', [cannonId, rot, angle]) + + def cloudsColorRequest(self): + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'cloudsColorResponse', [self.cloudColors.values()]) + + def requestCloudHit(self, cloudId, r, g, b): + avId = self.air.getAvatarIdFromSender() + if not avId in self.toonsPlaying: + self.air.writeServerEvent('suspicious',avId,'Toon tried to hit cloud in cannon activity they\'re not using!') + return + self.cloudColors[cloudId] = [cloudId, r, g, b] + self.sendUpdate('setCloudHit', [cloudId, r, g, b]) + self.cloudsHit[avId] += 1 + + def setToonTrajectoryAi(self, launchTime, x, y, z, h, p, r, vx, vy, vz): + self.sendUpdate('setToonTrajectory', [self.air.getAvatarIdFromSender(), launchTime, x, y, z, h, p, r, vx, vy, vz]) + + def setToonTrajectory(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6, todo7, todo8, todo9, todo10): + pass + + def updateToonTrajectoryStartVelAi(self, vx, vy, vz): + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('updateToonTrajectoryStartVel', [avId, vx, vy, vz]) + + def updateToonTrajectoryStartVel(self, todo0, todo1, todo2, todo3): + pass diff --git a/toontown/parties/DistributedPartyCatchActivity.py b/toontown/parties/DistributedPartyCatchActivity.py new file mode 100755 index 00000000..b4e6f9b0 --- /dev/null +++ b/toontown/parties/DistributedPartyCatchActivity.py @@ -0,0 +1,852 @@ +from pandac.PandaModules import Vec3, Point3, Point4, TextNode, NodePath +from pandac.PandaModules import CollisionHandlerEvent, CollisionNode, CollisionSphere +from direct.distributed.ClockDelta import globalClockDelta +from direct.interval.IntervalGlobal import Sequence, Parallel +from direct.interval.IntervalGlobal import LerpScaleInterval, LerpFunctionInterval, LerpColorScaleInterval, LerpPosInterval +from direct.interval.IntervalGlobal import SoundInterval, WaitInterval +from direct.showbase.PythonUtil import Functor, bound, lerp, SerialNumGen +from direct.showbase.RandomNumGen import RandomNumGen +from direct.task.Task import Task +from direct.distributed import DistributedSmoothNode +from direct.directnotify import DirectNotifyGlobal +from direct.interval.FunctionInterval import Wait, Func +from toontown.toonbase import TTLocalizer +from toontown.toon import Toon +from toontown.toonbase import ToontownGlobals +from toontown.minigame.Trajectory import Trajectory +from toontown.minigame.OrthoDrive import OrthoDrive +from toontown.minigame.OrthoWalk import OrthoWalk +from toontown.minigame.DropPlacer import PartyRegionDropPlacer +from toontown.parties import PartyGlobals +from toontown.parties.PartyCatchActivityToonSD import PartyCatchActivityToonSD +from toontown.parties.DistributedPartyActivity import DistributedPartyActivity +from toontown.parties.DistributedPartyCatchActivityBase import DistributedPartyCatchActivityBase +from toontown.parties.DistributedPartyCannonActivity import DistributedPartyCannonActivity +from toontown.parties.activityFSMs import CatchActivityFSM + +class DistributedPartyCatchActivity(DistributedPartyActivity, DistributedPartyCatchActivityBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyCatchActivity') + DropTaskName = 'dropSomething' + DropObjectPlurals = {'apple': TTLocalizer.PartyCatchActivityApples, + 'orange': TTLocalizer.PartyCatchActivityOranges, + 'pear': TTLocalizer.PartyCatchActivityPears, + 'coconut': TTLocalizer.PartyCatchActivityCoconuts, + 'watermelon': TTLocalizer.PartyCatchActivityWatermelons, + 'pineapple': TTLocalizer.PartyCatchActivityPineapples, + 'anvil': TTLocalizer.PartyCatchActivityAnvils} + + class Generation: + + def __init__(self, generation, startTime, startNetworkTime, numPlayers): + self.generation = generation + self.startTime = startTime + self.startNetworkTime = startNetworkTime + self.numPlayers = numPlayers + self.hasBeenScheduled = False + self.droppedObjNames = [] + self.dropSchedule = [] + self.numItemsDropped = 0 + self.droppedObjCaught = {} + + def __init__(self, cr): + DistributedPartyActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyCatch, PartyGlobals.ActivityTypes.HostInitiated, wantRewardGui=True) + self.setUsesSmoothing() + self.setUsesLookAround() + self._sNumGen = SerialNumGen() + + def getTitle(self): + return TTLocalizer.PartyCatchActivityTitle + + def getInstructions(self): + return TTLocalizer.PartyCatchActivityInstructions % {'badThing': self.DropObjectPlurals['anvil']} + + def generate(self): + DistributedPartyActivity.generate(self) + self.notify.info('localAvatar doId: %s' % base.localAvatar.doId) + self.notify.info('generate()') + self._generateFrame = globalClock.getFrameCount() + self._id2gen = {} + self._orderedGenerations = [] + self._orderedGenerationIndex = None + rng = RandomNumGen(self.doId) + self._generationSeedBase = rng.randrange(1000) + self._lastDropTime = 0.0 + return + + def getCurGeneration(self): + if self._orderedGenerationIndex is None: + return + return self._orderedGenerations[self._orderedGenerationIndex] + + def _addGeneration(self, generation, startTime, startNetworkTime, numPlayers): + self._id2gen[generation] = self.Generation(generation, startTime, startNetworkTime, numPlayers) + i = 0 + while 1: + if i >= len(self._orderedGenerations): + break + gen = self._orderedGenerations[i] + startNetT = self._id2gen[gen].startTime + genId = self._id2gen[gen].generation + if startNetT > startNetworkTime: + break + if startNetT == startNetworkTime and genId > generation: + break + i += 1 + self._orderedGenerations = self._orderedGenerations[:i] + [generation] + self._orderedGenerations[i:] + if self._orderedGenerationIndex is not None: + if self._orderedGenerationIndex >= i: + self._orderedGenerationIndex += 1 + + def _removeGeneration(self, generation): + del self._id2gen[generation] + i = self._orderedGenerations.index(generation) + self._orderedGenerations = self._orderedGenerations[:i] + self._orderedGenerations[i + 1:] + if self._orderedGenerationIndex is not None: + if len(self._orderedGenerations): + if self._orderedGenerationIndex >= i: + self._orderedGenerationIndex -= 1 + else: + self._orderedGenerationIndex = None + return + + def announceGenerate(self): + self.notify.info('announceGenerate()') + self.catchTreeZoneEvent = 'fence_floor' + DistributedPartyActivity.announceGenerate(self) + + def load(self, loadModels = 1, arenaModel = 'partyCatchTree'): + self.notify.info('load()') + DistributedPartyCatchActivity.notify.debug('PartyCatch: load') + self.activityFSM = CatchActivityFSM(self) + self.defineConstants() + self.treesAndFence = loader.loadModel('phase_13/models/parties/%s' % arenaModel) + self.treesAndFence.setScale(0.9) + self.treesAndFence.find('**/fence_floor').setPos(0.0, 0.0, 0.1) + self.treesAndFence.reparentTo(self.root) + ground = self.treesAndFence.find('**/groundPlane') + ground.setBin('ground', 1) + DistributedPartyActivity.load(self) + exitText = TextNode('PartyCatchExitText') + exitText.setCardAsMargin(0.1, 0.1, 0.1, 0.1) + exitText.setCardDecal(True) + exitText.setCardColor(1.0, 1.0, 1.0, 0.0) + exitText.setText(TTLocalizer.PartyCatchActivityExit) + exitText.setTextColor(0.0, 8.0, 0.0, 0.9) + exitText.setAlign(exitText.ACenter) + exitText.setFont(ToontownGlobals.getBuildingNametagFont()) + exitText.setShadowColor(0, 0, 0, 1) + exitText.setBin('fixed') + if TTLocalizer.BuildingNametagShadow: + exitText.setShadow(*TTLocalizer.BuildingNametagShadow) + exitTextLoc = self.treesAndFence.find('**/loc_exitSignText') + exitTextNp = exitTextLoc.attachNewNode(exitText) + exitTextNp.setDepthWrite(0) + exitTextNp.setScale(4) + exitTextNp.setZ(-.5) + self.sign.reparentTo(self.treesAndFence.find('**/loc_eventSign')) + self.sign.wrtReparentTo(self.root) + self.avatarNodePath = NodePath('PartyCatchAvatarNodePath') + self.avatarNodePath.reparentTo(self.root) + self._avatarNodePathParentToken = 3 + base.cr.parentMgr.registerParent(self._avatarNodePathParentToken, self.avatarNodePath) + self.toonSDs = {} + self.dropShadow = loader.loadModelOnce('phase_3/models/props/drop_shadow') + self.dropObjModels = {} + if loadModels: + self.__loadDropModels() + self.sndGoodCatch = base.loadSfx('phase_4/audio/sfx/SZ_DD_treasure.ogg') + self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg') + self.sndAnvilLand = base.loadSfx('phase_4/audio/sfx/AA_drop_anvil_miss.ogg') + self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.ogg') + self.__textGen = TextNode('partyCatchActivity') + self.__textGen.setFont(ToontownGlobals.getSignFont()) + self.__textGen.setAlign(TextNode.ACenter) + self.activityFSM.request('Idle') + + def __loadDropModels(self): + for objType in PartyGlobals.DropObjectTypes: + model = loader.loadModel(objType.modelPath) + self.dropObjModels[objType.name] = model + modelScales = {'apple': 0.7, + 'orange': 0.7, + 'pear': 0.5, + 'coconut': 0.7, + 'watermelon': 0.6, + 'pineapple': 0.45} + if objType.name in modelScales: + model.setScale(modelScales[objType.name]) + if objType == PartyGlobals.Name2DropObjectType['pear']: + model.setZ(-.6) + if objType == PartyGlobals.Name2DropObjectType['coconut']: + model.setP(180) + if objType == PartyGlobals.Name2DropObjectType['watermelon']: + model.setH(135) + model.setZ(-.5) + if objType == PartyGlobals.Name2DropObjectType['pineapple']: + model.setZ(-1.7) + if objType == PartyGlobals.Name2DropObjectType['anvil']: + model.setZ(-self.ObjRadius) + model.flattenStrong() + + def unload(self): + DistributedPartyCatchActivity.notify.debug('unload') + self.finishAllDropIntervals() + self.destroyOrthoWalk() + DistributedPartyActivity.unload(self) + self.stopDropTask() + del self.activityFSM + del self.__textGen + for avId in self.toonSDs.keys(): + if avId in self.toonSDs: + toonSD = self.toonSDs[avId] + toonSD.unload() + + del self.toonSDs + self.treesAndFence.removeNode() + del self.treesAndFence + self.dropShadow.removeNode() + del self.dropShadow + base.cr.parentMgr.unregisterParent(self._avatarNodePathParentToken) + for model in self.dropObjModels.values(): + model.removeNode() + + del self.dropObjModels + del self.sndGoodCatch + del self.sndOof + del self.sndAnvilLand + del self.sndPerfect + + def setStartTimestamp(self, timestamp32): + self.notify.info('setStartTimestamp(%s)' % (timestamp32,)) + self._startTimestamp = globalClockDelta.networkToLocalTime(timestamp32, bits=32) + + def getCurrentCatchActivityTime(self): + return globalClock.getFrameTime() - self._startTimestamp + + def getObjModel(self, objName): + return self.dropObjModels[objName].copyTo(hidden) + + def joinRequestDenied(self, reason): + DistributedPartyActivity.joinRequestDenied(self, reason) + base.cr.playGame.getPlace().fsm.request('walk') + + def handleToonJoined(self, toonId): + if toonId not in self.toonSDs: + toonSD = PartyCatchActivityToonSD(toonId, self) + self.toonSDs[toonId] = toonSD + toonSD.load() + self.notify.debug('handleToonJoined : currentState = %s' % self.activityFSM.state) + self.cr.doId2do[toonId].useLOD(500) + if self.activityFSM.state == 'Active': + if toonId in self.toonSDs: + self.toonSDs[toonId].enter() + if base.localAvatar.doId == toonId: + base.localAvatar.b_setParent(self._avatarNodePathParentToken) + self.putLocalAvatarInActivity() + if toonId in self.toonSDs: + self.toonSDs[toonId].fsm.request('rules') + + def handleToonExited(self, toonId): + self.notify.debug('handleToonExited( toonId=%s )' % toonId) + if toonId in self.cr.doId2do: + self.cr.doId2do[toonId].resetLOD() + if toonId in self.toonSDs: + self.toonSDs[toonId].fsm.request('notPlaying') + self.toonSDs[toonId].exit() + self.toonSDs[toonId].unload() + del self.toonSDs[toonId] + if base.localAvatar.doId == toonId: + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + + def takeLocalAvatarOutOfActivity(self): + self.notify.debug('localToon has left the circle') + camera.reparentTo(base.localAvatar) + base.localAvatar.startUpdateSmartCamera() + base.localAvatar.enableSmartCameraViews() + base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex) + DistributedSmoothNode.activateSmoothing(1, 0) + + def _enableCollisions(self): + DistributedPartyActivity._enableCollisions(self) + self._enteredTree = False + self.accept('enter' + self.catchTreeZoneEvent, self._toonMayHaveEnteredTree) + self.accept('again' + self.catchTreeZoneEvent, self._toonMayHaveEnteredTree) + self.accept('exit' + self.catchTreeZoneEvent, self._toonExitedTree) + self.accept(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT, self._handleCannonLanded) + + def _disableCollisions(self): + self.ignore(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT) + self.ignore('enter' + self.catchTreeZoneEvent) + self.ignore('again' + self.catchTreeZoneEvent) + self.ignore('exit' + self.catchTreeZoneEvent) + DistributedPartyActivity._disableCollisions(self) + + def _handleCannonLanded(self): + x = base.localAvatar.getX() + y = base.localAvatar.getY() + if x > self.x - self.StageHalfWidth and x < self.x + self.StageHalfWidth and y > self.y - self.StageHalfHeight and y < self.y + self.StageHalfHeight: + self._toonEnteredTree(None) + return + + def _toonMayHaveEnteredTree(self, collEntry): + if self._enteredTree: + return + if base.localAvatar.controlManager.currentControls.getIsAirborne(): + return + self._toonEnteredTree(collEntry) + + def _toonEnteredTree(self, collEntry): + self.notify.debug('_toonEnteredTree : avid = %s' % base.localAvatar.doId) + self.notify.debug('_toonEnteredTree : currentState = %s' % self.activityFSM.state) + if self.isLocalToonInActivity(): + return + if self.activityFSM.state == 'Active': + base.cr.playGame.getPlace().fsm.request('activity') + self.d_toonJoinRequest() + elif self.activityFSM.state == 'Idle': + base.cr.playGame.getPlace().fsm.request('activity') + self.d_toonJoinRequest() + self._enteredTree = True + + def _toonExitedTree(self, collEntry): + self.notify.debug('_toonExitedTree : avid = %s' % base.localAvatar.doId) + self._enteredTree = False + if hasattr(base.cr.playGame.getPlace(), 'fsm') and self.activityFSM.state == 'Active' and self.isLocalToonInActivity(): + if base.localAvatar.doId in self.toonSDs: + self.takeLocalAvatarOutOfActivity() + self.toonSDs[base.localAvatar.doId].fsm.request('notPlaying') + self.d_toonExitDemand() + + def setToonsPlaying(self, toonIds): + self.notify.info('setToonsPlaying(%s)' % (toonIds,)) + DistributedPartyActivity.setToonsPlaying(self, toonIds) + if self.isLocalToonInActivity() and base.localAvatar.doId not in toonIds: + if base.localAvatar.doId in self.toonSDs: + self.takeLocalAvatarOutOfActivity() + self.toonSDs[base.localAvatar.doId].fsm.request('notPlaying') + + def __genText(self, text): + self.__textGen.setText(text) + return self.__textGen.generate() + + def getNumPlayers(self): + return len(self.toonIds) + + def defineConstants(self, forceNumPlayers = None): + DistributedPartyCatchActivity.notify.debug('defineConstants') + self.ShowObjSpheres = 0 + self.ShowToonSpheres = 0 + self.useGravity = True + self.trickShadows = True + if forceNumPlayers is None: + numPlayers = self.getNumPlayers() + else: + numPlayers = forceNumPlayers + self.calcDifficultyConstants(numPlayers) + DistributedPartyCatchActivity.notify.debug('ToonSpeed: %s' % self.ToonSpeed) + DistributedPartyCatchActivity.notify.debug('total drops: %s' % self.totalDrops) + DistributedPartyCatchActivity.notify.debug('numFruits: %s' % self.numFruits) + DistributedPartyCatchActivity.notify.debug('numAnvils: %s' % self.numAnvils) + self.ObjRadius = 1.0 + dropRegionTable = PartyRegionDropPlacer.getDropRegionTable(numPlayers) + self.DropRows, self.DropColumns = len(dropRegionTable), len(dropRegionTable[0]) + for objType in PartyGlobals.DropObjectTypes: + DistributedPartyCatchActivity.notify.debug('*** Object Type: %s' % objType.name) + objType.onscreenDuration = objType.onscreenDurMult * self.BaselineOnscreenDropDuration + DistributedPartyCatchActivity.notify.debug('onscreenDuration=%s' % objType.onscreenDuration) + v_0 = 0.0 + t = objType.onscreenDuration + x_0 = self.MinOffscreenHeight + x = 0.0 + g = 2.0 * (x - x_0 - v_0 * t) / (t * t) + DistributedPartyCatchActivity.notify.debug('gravity=%s' % g) + objType.trajectory = Trajectory(0, Vec3(0, 0, x_0), Vec3(0, 0, v_0), gravMult=abs(g / Trajectory.gravity)) + objType.fallDuration = objType.onscreenDuration + self.OffscreenTime + + return + + def grid2world(self, column, row): + x = column / float(self.DropColumns - 1) + y = row / float(self.DropRows - 1) + x = x * 2.0 - 1.0 + y = y * 2.0 - 1.0 + x *= self.StageHalfWidth + y *= self.StageHalfHeight + return (x, y) + + def showPosts(self): + self.hidePosts() + self.posts = [Toon.Toon(), + Toon.Toon(), + Toon.Toon(), + Toon.Toon()] + for i in xrange(len(self.posts)): + tree = self.posts[i] + tree.reparentTo(render) + x = self.StageHalfWidth + y = self.StageHalfHeight + if i > 1: + x = -x + if i % 2: + y = -y + tree.setPos(x + self.x, y + self.y, 0) + + def hidePosts(self): + if hasattr(self, 'posts'): + for tree in self.posts: + tree.removeNode() + + del self.posts + + def showDropGrid(self): + self.hideDropGrid() + self.dropMarkers = [] + for row in xrange(self.DropRows): + self.dropMarkers.append([]) + rowList = self.dropMarkers[row] + for column in xrange(self.DropColumns): + toon = Toon.Toon() + toon.setDNA(base.localAvatar.getStyle()) + toon.reparentTo(self.root) + toon.setScale(1.0 / 3) + x, y = self.grid2world(column, row) + toon.setPos(x, y, 0) + rowList.append(toon) + + def hideDropGrid(self): + if hasattr(self, 'dropMarkers'): + for row in self.dropMarkers: + for marker in row: + marker.removeNode() + + del self.dropMarkers + + def handleToonDisabled(self, avId): + DistributedPartyCatchActivity.notify.debug('handleToonDisabled') + DistributedPartyCatchActivity.notify.debug('avatar ' + str(avId) + ' disabled') + if avId in self.toonSDs: + self.toonSDs[avId].exit(unexpectedExit=True) + del self.toonSDs[avId] + + def turnOffSmoothingOnGuests(self): + pass + + def setState(self, newState, timestamp): + self.notify.info('setState(%s, %s)' % (newState, timestamp)) + DistributedPartyCatchActivity.notify.debug('setState( newState=%s, ... )' % newState) + DistributedPartyActivity.setState(self, newState, timestamp) + self.activityFSM.request(newState) + if newState == 'Active': + if base.localAvatar.doId != self.party.partyInfo.hostId: + if globalClock.getFrameCount() > self._generateFrame: + if base.localAvatar.getX() > self.x - self.StageHalfWidth and base.localAvatar.getX() < self.x + self.StageHalfWidth and base.localAvatar.getY() > self.y - self.StageHalfHeight and base.localAvatar.getY() < self.y + self.StageHalfHeight: + self._toonEnteredTree(None) + return + + def putLocalAvatarInActivity(self): + if base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'fsm'): + base.cr.playGame.getPlace().fsm.request('activity', [False]) + else: + self.notify.info("Avoided crash: toontown.parties.DistributedPartyCatchActivity:632, toontown.parties.DistributedPartyCatchActivity:1198, toontown.parties.activityFSMMixins:49, direct.fsm.FSM:423, AttributeError: 'NoneType' object has no attribute 'fsm'") + base.localAvatar.stopUpdateSmartCamera() + camera.reparentTo(self.treesAndFence) + camera.setPosHpr(0.0, -63.0, 30.0, 0.0, -20.0, 0.0) + if not hasattr(self, 'ltLegsCollNode'): + self.createCatchCollisions() + + def createCatchCollisions(self): + radius = 0.7 + handler = CollisionHandlerEvent() + handler.setInPattern('ltCatch%in') + self.ltLegsCollNode = CollisionNode('catchLegsCollNode') + self.ltLegsCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask) + self.ltHeadCollNode = CollisionNode('catchHeadCollNode') + self.ltHeadCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask) + self.ltLHandCollNode = CollisionNode('catchLHandCollNode') + self.ltLHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask) + self.ltRHandCollNode = CollisionNode('catchRHandCollNode') + self.ltRHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask) + legsCollNodepath = base.localAvatar.attachNewNode(self.ltLegsCollNode) + legsCollNodepath.hide() + head = base.localAvatar.getHeadParts().getPath(2) + headCollNodepath = head.attachNewNode(self.ltHeadCollNode) + headCollNodepath.hide() + lHand = base.localAvatar.getLeftHands()[0] + lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode) + lHandCollNodepath.hide() + rHand = base.localAvatar.getRightHands()[0] + rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode) + rHandCollNodepath.hide() + base.localAvatar.cTrav.addCollider(legsCollNodepath, handler) + base.localAvatar.cTrav.addCollider(headCollNodepath, handler) + base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler) + base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler) + if self.ShowToonSpheres: + legsCollNodepath.show() + headCollNodepath.show() + lHandCollNodepath.show() + rHandCollNodepath.show() + self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius)) + self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius)) + self.ltLHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) + self.ltRHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) + self.toonCollNodes = [legsCollNodepath, + headCollNodepath, + lHandCollNodepath, + rHandCollNodepath] + + def destroyCatchCollisions(self): + if not hasattr(self, 'ltLegsCollNode'): + return + for collNode in self.toonCollNodes: + while collNode.node().getNumSolids(): + collNode.node().removeSolid(0) + + base.localAvatar.cTrav.removeCollider(collNode) + + del self.toonCollNodes + del self.ltLegsCollNode + del self.ltHeadCollNode + del self.ltLHandCollNode + del self.ltRHandCollNode + + def timerExpired(self): + pass + + def __handleCatch(self, generation, objNum): + DistributedPartyCatchActivity.notify.debug('catch: %s' % [generation, objNum]) + if base.localAvatar.doId not in self.toonIds: + return + self.showCatch(base.localAvatar.doId, generation, objNum) + objName = self._id2gen[generation].droppedObjNames[objNum] + objTypeId = PartyGlobals.Name2DOTypeId[objName] + self.sendUpdate('claimCatch', [generation, objNum, objTypeId]) + self.finishDropInterval(generation, objNum) + + def showCatch(self, avId, generation, objNum): + if avId not in self.toonSDs: + return + isLocal = avId == base.localAvatar.doId + if generation not in self._id2gen: + return + if not self._id2gen[generation].hasBeenScheduled: + return + objName = self._id2gen[generation].droppedObjNames[objNum] + objType = PartyGlobals.Name2DropObjectType[objName] + if objType.good: + if objNum not in self._id2gen[generation].droppedObjCaught: + if isLocal: + base.playSfx(self.sndGoodCatch) + fruit = self.getObjModel(objName) + toon = self.getAvatar(avId) + rHand = toon.getRightHands()[1] + self.toonSDs[avId].eatFruit(fruit, rHand) + else: + self.toonSDs[avId].fsm.request('fallForward') + self._id2gen[generation].droppedObjCaught[objNum] = 1 + + def setObjectCaught(self, avId, generation, objNum): + self.notify.info('setObjectCaught(%s, %s, %s)' % (avId, generation, objNum)) + if self.activityFSM.state != 'Active': + DistributedPartyCatchActivity.notify.warning('ignoring msg: object %s caught by %s' % (objNum, avId)) + return + isLocal = avId == base.localAvatar.doId + if not isLocal: + DistributedPartyCatchActivity.notify.debug('AI: avatar %s caught %s' % (avId, objNum)) + self.finishDropInterval(generation, objNum) + self.showCatch(avId, generation, objNum) + self._scheduleGenerations() + gen = self._id2gen[generation] + if gen.hasBeenScheduled: + objName = gen.droppedObjNames[objNum] + if PartyGlobals.Name2DropObjectType[objName].good: + if hasattr(self, 'fruitsCaught'): + self.fruitsCaught += 1 + + def finishDropInterval(self, generation, objNum): + if hasattr(self, 'dropIntervals'): + if (generation, objNum) in self.dropIntervals: + self.dropIntervals[generation, objNum].finish() + + def finishAllDropIntervals(self): + if hasattr(self, 'dropIntervals'): + for dropInterval in self.dropIntervals.values(): + dropInterval.finish() + + def setGenerations(self, generations): + self.notify.info('setGenerations(%s)' % (generations,)) + gen2t = {} + gen2nt = {} + gen2np = {} + for id, timestamp32, numPlayers in generations: + gen2t[id] = globalClockDelta.networkToLocalTime(timestamp32, bits=32) - self._startTimestamp + gen2nt[id] = timestamp32 + gen2np[id] = numPlayers + + ids = self._id2gen.keys() + for id in ids: + if id not in gen2t: + self._removeGeneration(id) + + for id in gen2t: + if id not in self._id2gen: + self._addGeneration(id, gen2t[id], gen2nt[id], gen2np[id]) + + def scheduleDrops(self, genId = None): + if genId is None: + genId = self.getCurGeneration() + gen = self._id2gen[genId] + if gen.hasBeenScheduled: + return + fruitIndex = int((gen.startTime + 0.5 * self.DropPeriod) / PartyGlobals.CatchActivityDuration) + fruitNames = ['apple', + 'orange', + 'pear', + 'coconut', + 'watermelon', + 'pineapple'] + fruitName = fruitNames[fruitIndex % len(fruitNames)] + rng = RandomNumGen(genId + self._generationSeedBase) + gen.droppedObjNames = [fruitName] * self.numFruits + ['anvil'] * self.numAnvils + rng.shuffle(gen.droppedObjNames) + dropPlacer = PartyRegionDropPlacer(self, gen.numPlayers, genId, gen.droppedObjNames, startTime=gen.startTime) + gen.numItemsDropped = 0 + tIndex = gen.startTime % PartyGlobals.CatchActivityDuration + tPercent = float(tIndex) / PartyGlobals.CatchActivityDuration + gen.numItemsDropped += dropPlacer.skipPercent(tPercent) + while not dropPlacer.doneDropping(continuous=True): + nextDrop = dropPlacer.getNextDrop() + gen.dropSchedule.append(nextDrop) + + gen.hasBeenScheduled = True + return + + def startDropTask(self): + taskMgr.add(self.dropTask, self.DropTaskName) + + def stopDropTask(self): + taskMgr.remove(self.DropTaskName) + + def _scheduleGenerations(self): + curT = self.getCurrentCatchActivityTime() + genIndex = self._orderedGenerationIndex + newGenIndex = genIndex + while genIndex is None or genIndex < len(self._orderedGenerations) - 1: + if genIndex is None: + nextGenIndex = 0 + else: + nextGenIndex = genIndex + 1 + nextGenId = self._orderedGenerations[nextGenIndex] + nextGen = self._id2gen[nextGenId] + startT = nextGen.startTime + if curT >= startT: + newGenIndex = nextGenIndex + if not nextGen.hasBeenScheduled: + self.defineConstants(forceNumPlayers=nextGen.numPlayers) + self.scheduleDrops(genId=self._orderedGenerations[nextGenIndex]) + genIndex = nextGenIndex + + self._orderedGenerationIndex = newGenIndex + return + + def dropTask(self, task): + self._scheduleGenerations() + curT = self.getCurrentCatchActivityTime() + if self._orderedGenerationIndex is not None: + i = self._orderedGenerationIndex + genIndex = self._orderedGenerations[i] + gen = self._id2gen[genIndex] + while len(gen.dropSchedule) > 0 and gen.dropSchedule[0][0] < curT: + drop = gen.dropSchedule[0] + gen.dropSchedule = gen.dropSchedule[1:] + dropTime, objName, dropCoords = drop + objNum = gen.numItemsDropped + x, y = self.grid2world(*dropCoords) + dropIval = self.getDropIval(x, y, objName, genIndex, objNum) + + def cleanup(generation, objNum, self = self): + del self.dropIntervals[generation, objNum] + + dropIval.append(Func(Functor(cleanup, genIndex, objNum))) + self.dropIntervals[genIndex, objNum] = dropIval + gen.numItemsDropped += 1 + dropIval.start(curT - dropTime) + self._lastDropTime = dropTime + + return Task.cont + + def getDropIval(self, x, y, dropObjName, generation, num): + objType = PartyGlobals.Name2DropObjectType[dropObjName] + id = (generation, num) + dropNode = hidden.attachNewNode('catchDropNode%s' % (id,)) + dropNode.setPos(x, y, 0) + shadow = self.dropShadow.copyTo(dropNode) + shadow.setZ(PartyGlobals.CatchDropShadowHeight) + shadow.setColor(1, 1, 1, 1) + object = self.getObjModel(dropObjName) + object.reparentTo(hidden) + if dropObjName in ['watermelon', 'anvil']: + objH = object.getH() + absDelta = {'watermelon': 12, + 'anvil': 15}[dropObjName] + delta = (self.randomNumGen.random() * 2.0 - 1.0) * absDelta + newH = objH + delta + else: + newH = self.randomNumGen.random() * 360.0 + object.setH(newH) + sphereName = 'FallObj%s' % (id,) + radius = self.ObjRadius + if objType.good: + radius *= lerp(1.0, 1.3, 0.5) + collSphere = CollisionSphere(0, 0, 0, radius) + collSphere.setTangible(0) + collNode = CollisionNode(sphereName) + collNode.setCollideMask(PartyGlobals.CatchActivityBitmask) + collNode.addSolid(collSphere) + collNodePath = object.attachNewNode(collNode) + collNodePath.hide() + if self.ShowObjSpheres: + collNodePath.show() + catchEventName = 'ltCatch' + sphereName + + def eatCollEntry(forward, collEntry): + forward() + + self.accept(catchEventName, Functor(eatCollEntry, Functor(self.__handleCatch, id[0], id[1]))) + + def cleanup(self = self, dropNode = dropNode, id = id, event = catchEventName): + self.ignore(event) + dropNode.removeNode() + + duration = objType.fallDuration + onscreenDuration = objType.onscreenDuration + targetShadowScale = 0.3 + if self.trickShadows: + intermedScale = targetShadowScale * (self.OffscreenTime / self.BaselineDropDuration) + shadowScaleIval = Sequence(LerpScaleInterval(shadow, self.OffscreenTime, intermedScale, startScale=0)) + shadowScaleIval.append(LerpScaleInterval(shadow, duration - self.OffscreenTime, targetShadowScale, startScale=intermedScale)) + else: + shadowScaleIval = LerpScaleInterval(shadow, duration, targetShadowScale, startScale=0) + targetShadowAlpha = 0.4 + shadowAlphaIval = LerpColorScaleInterval(shadow, self.OffscreenTime, Point4(1, 1, 1, targetShadowAlpha), startColorScale=Point4(1, 1, 1, 0)) + shadowIval = Parallel(shadowScaleIval, shadowAlphaIval) + if self.useGravity: + + def setObjPos(t, objType = objType, object = object): + z = objType.trajectory.calcZ(t) + object.setZ(z) + + setObjPos(0) + dropIval = LerpFunctionInterval(setObjPos, fromData=0, toData=onscreenDuration, duration=onscreenDuration) + else: + startPos = Point3(0, 0, self.MinOffscreenHeight) + object.setPos(startPos) + dropIval = LerpPosInterval(object, onscreenDuration, Point3(0, 0, 0), startPos=startPos, blendType='easeIn') + ival = Sequence(Func(Functor(dropNode.reparentTo, self.root)), Parallel(Sequence(WaitInterval(self.OffscreenTime), Func(Functor(object.reparentTo, dropNode)), dropIval), shadowIval), Func(cleanup), name='drop%s' % (id,)) + if objType == PartyGlobals.Name2DropObjectType['anvil']: + ival.append(Func(self.playAnvil)) + return ival + + def playAnvil(self): + if base.localAvatar.doId in self.toonIds: + base.playSfx(self.sndAnvilLand) + + def initOrthoWalk(self): + DistributedPartyCatchActivity.notify.debug('startOrthoWalk') + + def doCollisions(oldPos, newPos, self = self): + x = bound(newPos[0], self.StageHalfWidth, -self.StageHalfWidth) + y = bound(newPos[1], self.StageHalfHeight, -self.StageHalfHeight) + newPos.setX(x) + newPos.setY(y) + return newPos + + orthoDrive = OrthoDrive(self.ToonSpeed, instantTurn=True) + self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True) + + def destroyOrthoWalk(self): + DistributedPartyCatchActivity.notify.debug('destroyOrthoWalk') + if hasattr(self, 'orthoWalk'): + self.orthoWalk.stop() + self.orthoWalk.destroy() + del self.orthoWalk + + def startIdle(self): + DistributedPartyCatchActivity.notify.debug('startIdle') + + def finishIdle(self): + DistributedPartyCatchActivity.notify.debug('finishIdle') + + def startActive(self): + DistributedPartyCatchActivity.notify.debug('startActive') + for avId in self.toonIds: + if avId in self.toonSDs: + toonSD = self.toonSDs[avId] + toonSD.enter() + toonSD.fsm.request('normal') + + self.fruitsCaught = 0 + self.dropIntervals = {} + self.startDropTask() + if base.localAvatar.doId in self.toonIds: + self.putLocalAvatarInActivity() + + def finishActive(self): + DistributedPartyCatchActivity.notify.debug('finishActive') + self.stopDropTask() + if hasattr(self, 'finishIval'): + self.finishIval.pause() + del self.finishIval + if base.localAvatar.doId in self.toonIds: + self.takeLocalAvatarOutOfActivity() + for ival in self.dropIntervals.values(): + ival.finish() + + del self.dropIntervals + + def startConclusion(self): + DistributedPartyCatchActivity.notify.debug('startConclusion') + for avId in self.toonIds: + if avId in self.toonSDs: + toonSD = self.toonSDs[avId] + toonSD.fsm.request('notPlaying') + + self.destroyCatchCollisions() + if base.localAvatar.doId not in self.toonIds: + return + else: + self.localToonExiting() + if self.fruitsCaught >= self.numFruits: + finishText = TTLocalizer.PartyCatchActivityFinishPerfect + else: + finishText = TTLocalizer.PartyCatchActivityFinish + perfectTextSubnode = hidden.attachNewNode(self.__genText(finishText)) + perfectText = hidden.attachNewNode('perfectText') + perfectTextSubnode.reparentTo(perfectText) + frame = self.__textGen.getCardActual() + offsetY = -abs(frame[2] + frame[3]) / 2.0 + perfectTextSubnode.setPos(0, 0, offsetY) + perfectText.setColor(1, 0.1, 0.1, 1) + + def fadeFunc(t, text = perfectText): + text.setColorScale(1, 1, 1, t) + + def destroyText(text = perfectText): + text.removeNode() + + textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5)) + soundTrack = SoundInterval(self.sndPerfect) + self.finishIval = Parallel(textTrack, soundTrack) + self.finishIval.start() + + def finishConclusion(self): + DistributedPartyCatchActivity.notify.debug('finishConclusion') + if base.localAvatar.doId in self.toonIds: + self.takeLocalAvatarOutOfActivity() + base.cr.playGame.getPlace().fsm.request('walk') + + def showJellybeanReward(self, earnedAmount, jarAmount, message): + if earnedAmount > 0: + DistributedPartyActivity.showJellybeanReward(self, earnedAmount, jarAmount, message) + else: + base.cr.playGame.getPlace().fsm.request('walk') diff --git a/toontown/parties/DistributedPartyCatchActivityAI.py b/toontown/parties/DistributedPartyCatchActivityAI.py new file mode 100755 index 00000000..709032ee --- /dev/null +++ b/toontown/parties/DistributedPartyCatchActivityAI.py @@ -0,0 +1,87 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyActivityAI import DistributedPartyActivityAI +from toontown.parties.DistributedPartyCatchActivityBase import DistributedPartyCatchActivityBase +from direct.task import Task +from direct.distributed.ClockDelta import globalClockDelta +from toontown.toonbase import TTLocalizer +import PartyGlobals + +class DistributedPartyCatchActivityAI(DistributedPartyActivityAI, DistributedPartyCatchActivityBase): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyCatchActivityAI") + + def __init__(self, air, parent, activityTuple): + DistributedPartyActivityAI.__init__(self, air, parent, activityTuple) + self.numGenerations = 1 + self.generations = [] + self.player2catches = {} + self.startTimestamp = globalClockDelta.getRealNetworkTime(bits=32) + self.playing = False + + def delete(self): + taskMgr.remove('newGeneration%d' % self.doId) + DistributedPartyActivityAI.delete(self) + + def getStartTimestamp(self): + return self.startTimestamp + + def setStartTimestamp(self, ts): + self.startTimestamp = ts + + def setGenerations(self, generations): + self.generations = generations + + def toonJoinRequest(self): + DistributedPartyActivityAI.toonJoinRequest(self) + avId = self.air.getAvatarIdFromSender() + self.player2catches[avId] = 0 + if not self.playing: + self.__startGame() + self.sendUpdate('setState', ['Active', globalClockDelta.getRealNetworkTime()]) + + def toonExitDemand(self): + avId = self.air.getAvatarIdFromSender() + if not avId in self.toonsPlaying: + self.air.writeServerEvent('suspicious',avId,'Toon tried to exit a party game they\'re not using!') + return + catches = self.player2catches[avId] + del self.player2catches[avId] + av = self.air.doId2do.get(avId, None) + if not av: + self.air.writeServerEvent('suspicious',avId,'Toon tried to award beans while not in district!') + return + if catches > PartyGlobals.CatchMaxTotalReward: + catches = PartyGlobals.CatchMaxTotalReward + self.sendUpdateToAvatarId(avId, 'showJellybeanReward', [catches, av.getMoney(), TTLocalizer.PartyCatchRewardMessage % (catches, catches)]) + av.addMoney(catches) + DistributedPartyActivityAI.toonExitDemand(self) + + def __startGame(self): + self.playing = True + self.calcDifficultyConstants(len(self.toonsPlaying)) + self.generations.append([self.numGenerations, globalClockDelta.getRealNetworkTime(bits=32), len(self.toonsPlaying)]) + self.numGenerations += 1 + self.sendUpdate('setGenerations', [self.generations]) + taskMgr.doMethodLater(self.generationDuration, self.__newGeneration, 'newGeneration%d' % self.doId, extraArgs=[]) + + def __newGeneration(self): + if len(self.toonsPlaying) > 0: + self.__startGame() + else: + self.playing = False + def getGenerations(self): + return self.generations + + def requestActivityStart(self): + pass + + def startRequestResponse(self, todo0): + pass + + def claimCatch(self, generation, objNum, objType): + avId = self.air.getAvatarIdFromSender() + if not avId in self.toonsPlaying: + self.air.writeServerEvent('suspicious',avId,'Toon tried to catch while not playing!') + return + if PartyGlobals.DOTypeId2Name[objType] != 'anvil': + self.player2catches[avId] += 1 + self.sendUpdate('setObjectCaught', [avId, generation, objNum]) diff --git a/toontown/parties/DistributedPartyCatchActivityBase.py b/toontown/parties/DistributedPartyCatchActivityBase.py new file mode 100755 index 00000000..66730f03 --- /dev/null +++ b/toontown/parties/DistributedPartyCatchActivityBase.py @@ -0,0 +1,60 @@ +import math +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame.DropScheduler import ThreePhaseDropScheduler +from toontown.parties import PartyGlobals + +class DistributedPartyCatchActivityBase: + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyCatchActivityBase') + FallRateCap_Players = 20 + + def calcDifficultyConstants(self, numPlayers): + self.FirstDropDelay = 0.0 + self.NormalDropDelay = int(1.0 / 12 * PartyGlobals.CatchActivityDuration) + self.FasterDropDelay = int(9.0 / 12 * PartyGlobals.CatchActivityDuration) + DistributedPartyCatchActivityBase.notify.debug('will start dropping fast after %s seconds' % self.FasterDropDelay) + self.SlowerDropPeriodMult = 1.5 + self.FasterDropPeriodMult = 1.0 / 3 + self.ToonSpeed = 16.0 + + def scaledDimensions(widthHeight, scale): + w, h = widthHeight + return [math.sqrt(scale * w * w), math.sqrt(scale * h * h)] + + BaseStageDimensions = [36, 36] + self.StageAreaScale = 1.0 + self.StageLinearScale = math.sqrt(self.StageAreaScale) + DistributedPartyCatchActivityBase.notify.debug('StageLinearScale: %s' % self.StageLinearScale) + self.StageDimensions = scaledDimensions(BaseStageDimensions, self.StageAreaScale) + DistributedPartyCatchActivityBase.notify.debug('StageDimensions: %s' % self.StageDimensions) + self.StageHalfWidth = self.StageDimensions[0] / 2.0 + self.StageHalfHeight = self.StageDimensions[1] / 2.0 + self.MinOffscreenHeight = 30 + distance = math.sqrt(self.StageDimensions[0] * self.StageDimensions[0] + self.StageDimensions[1] * self.StageDimensions[1]) + distance /= self.StageLinearScale + ToonRunDuration = distance / self.ToonSpeed + offScreenOnScreenRatio = 1.0 + fraction = 1.0 / 3 * 0.85 + self.BaselineOnscreenDropDuration = ToonRunDuration / (fraction * (1.0 + offScreenOnScreenRatio)) + DistributedPartyCatchActivityBase.notify.debug('BaselineOnscreenDropDuration=%s' % self.BaselineOnscreenDropDuration) + self.OffscreenTime = offScreenOnScreenRatio * self.BaselineOnscreenDropDuration + DistributedPartyCatchActivityBase.notify.debug('OffscreenTime=%s' % self.OffscreenTime) + self.BaselineDropDuration = self.BaselineOnscreenDropDuration + self.OffscreenTime + self.MaxDropDuration = self.BaselineDropDuration + self.DropPeriod = self.BaselineDropDuration / 3.0 + scaledNumPlayers = (min(numPlayers, self.FallRateCap_Players) - 1.0) * 0.85 + 1.0 + self.DropPeriod /= scaledNumPlayers + typeProbs = {'fruit': 3, + 'anvil': 1} + probSum = reduce(lambda x, y: x + y, typeProbs.values()) + for key in typeProbs.keys(): + typeProbs[key] = float(typeProbs[key]) / probSum + + scheduler = ThreePhaseDropScheduler(PartyGlobals.CatchActivityDuration, self.FirstDropDelay, self.DropPeriod, self.MaxDropDuration, self.SlowerDropPeriodMult, self.NormalDropDelay, self.FasterDropDelay, self.FasterDropPeriodMult) + self.totalDrops = 0 + while not scheduler.doneDropping(continuous=True): + scheduler.stepT() + self.totalDrops += 1 + + self.numFruits = int(self.totalDrops * typeProbs['fruit']) + self.numAnvils = int(self.totalDrops - self.numFruits) + self.generationDuration = scheduler.getDuration() diff --git a/toontown/parties/DistributedPartyCogActivity.py b/toontown/parties/DistributedPartyCogActivity.py new file mode 100755 index 00000000..7a6b9720 --- /dev/null +++ b/toontown/parties/DistributedPartyCogActivity.py @@ -0,0 +1,199 @@ +from direct.distributed.ClockDelta import globalClockDelta +from pandac.PandaModules import Point3 +from toontown.toonbase import TTLocalizer +import PartyGlobals +from DistributedPartyTeamActivity import DistributedPartyTeamActivity +from PartyCogActivity import PartyCogActivity + +class DistributedPartyCogActivity(DistributedPartyTeamActivity): + notify = directNotify.newCategory('DistributedPartyCogActivity') + players = {} + localPlayer = None + view = None + + def __init__(self, cr, arenaModel = 'phase_13/models/parties/cogPieArena_model', texture = None): + DistributedPartyTeamActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyCog, startDelay=PartyGlobals.CogActivityStartDelay, balanceTeams=PartyGlobals.CogActivityBalanceTeams) + self.arenaModel = arenaModel + self.texture = texture + + def load(self): + DistributedPartyTeamActivity.load(self) + self.view = PartyCogActivity(self, self.arenaModel, self.texture) + self.view.load() + + def announceGenerate(self): + DistributedPartyTeamActivity.announceGenerate(self) + for i in xrange(len(self.toonIds)): + for toonId in self.toonIds[i]: + toon = base.cr.doId2do.get(toonId, None) + if toon: + self.view.handleToonJoined(toon, i, lateEntry=True) + + return + + def unload(self): + if hasattr(self, 'view') and self.view is not None: + self.view.unload() + del self.view + DistributedPartyTeamActivity.unload(self) + return + + def enable(self): + DistributedPartyTeamActivity.enable(self) + + def disable(self): + DistributedPartyTeamActivity.disable(self) + + def getTitle(self): + return TTLocalizer.PartyCogTitle + + def getInstructions(self): + return TTLocalizer.PartyCogInstructions + + def pieThrow(self, toonId, timestamp, h, x, y, z, power): + if toonId not in self.toonIds: + return + if toonId != base.localAvatar.doId: + self.view.pieThrow(toonId, timestamp, h, Point3(x, y, z), power) + + def b_pieThrow(self, toon, power): + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + pos = toon.getPos() + h = toon.getH() + toonId = toon.doId + self.view.pieThrow(toonId, timestamp, h, pos, power) + self.d_broadcastPieThrow(toonId, timestamp, h, pos[0], pos[1], pos[2], power) + + def d_broadcastPieThrow(self, toonId, timestamp, h, x, y, z, power): + self.sendUpdate('pieThrow', [toonId, + timestamp, + h, + x, + y, + z, + power]) + + def pieHitsToon(self, toonId, timestamp, x, y, z): + if toonId not in self.toonIds: + return + self.view.pieHitsToon(toonId, timestamp, Point3(x, y, z)) + + def d_broadcastPieHitsToon(self, toonId, timestamp, pos): + self.sendUpdate('pieHitsToon', [toonId, + timestamp, + pos[0], + pos[1], + pos[2]]) + + def b_pieHitsToon(self, toonId, timestamp, pos): + self.view.pieHitsToon(toonId, timestamp, pos) + self.d_broadcastPieHitsToon(toonId, timestamp, pos) + + def pieHitsCog(self, toonId, timestamp, hitCogNum, x, y, z, direction, part): + if toonId not in self.toonIds: + return + if toonId != base.localAvatar.doId: + self.view.pieHitsCog(timestamp, hitCogNum, Point3(x, y, z), direction, part) + + def b_pieHitsCog(self, timestamp, hitCogNum, pos, direction, part): + self.view.pieHitsCog(timestamp, hitCogNum, pos, direction, part) + self.d_broadcastSendPieHitsCog(timestamp, hitCogNum, pos, direction, part) + + def d_broadcastSendPieHitsCog(self, timestamp, hitCogNum, pos, direction, part): + self.sendUpdate('pieHitsCog', [base.localAvatar.doId, + timestamp, + hitCogNum, + pos[0], + pos[1], + pos[2], + direction, + part]) + + def setCogDistances(self, distances): + self.view.setCogDistances(distances) + + def setHighScore(self, toonName, score): + self.setSignNote(TTLocalizer.PartyCogSignNote % (toonName, score)) + + def handleToonJoined(self, toonId): + DistributedPartyTeamActivity.handleToonJoined(self, toonId) + toon = base.cr.doId2do.get(toonId, None) + team = self.getTeam(toonId) + if toon is not None and self.view is not None: + self.view.handleToonJoined(toon, team) + return + + def handleToonExited(self, toonId): + toon = base.cr.doId2do.get(toonId, None) + if toon is None: + return + if self.view is not None: + self.view.handleToonExited(toon) + DistributedPartyTeamActivity.handleToonExited(self, toonId) + return + + def handleToonShifted(self, toonId): + toon = base.cr.doId2do.get(toonId, None) + if toon is None: + return + if self.view is not None: + self.view.handleToonShifted(toon) + return + + def handleToonSwitchedTeams(self, toonId): + DistributedPartyTeamActivity.handleToonSwitchedTeams(self, toonId) + toon = base.cr.doId2do.get(toonId, None) + if toon is None: + return + if self.view is not None: + self.view.handleToonSwitchedTeams(toon) + return + + def handleToonDisabled(self, toonId): + if self.view is not None: + self.view.handleToonDisabled(toonId) + return + + def startWaitForEnough(self): + DistributedPartyTeamActivity.startWaitForEnough(self) + self.view.openArenaDoors() + self.view.hideCogs() + + def startRules(self): + DistributedPartyTeamActivity.startRules(self) + self.view.closeArenaDoors() + self.view.showCogs() + + def startActive(self): + DistributedPartyTeamActivity.startActive(self) + self.view.startActivity(self.getCurrentActivityTime()) + self.view.closeArenaDoors() + if not self.isLocalToonPlaying: + self.view.showArenaDoorTimers(self._duration + PartyGlobals.CogActivityConclusionDuration + 1.0 - self.getCurrentActivityTime()) + + def finishActive(self): + DistributedPartyTeamActivity.finishActive(self) + self.view.stopActivity() + + def startConclusion(self, data): + DistributedPartyTeamActivity.startConclusion(self, data) + if self.isLocalToonPlaying: + score = (int(data / 10000), data % 10000) + winner = 2 + if score[PartyGlobals.TeamActivityTeams.LeftTeam] > score[PartyGlobals.TeamActivityTeams.RightTeam]: + winner = PartyGlobals.TeamActivityTeams.LeftTeam + elif score[PartyGlobals.TeamActivityTeams.LeftTeam] < score[PartyGlobals.TeamActivityTeams.RightTeam]: + winner = PartyGlobals.TeamActivityTeams.RightTeam + if winner < 2: + if self.getTeam(base.localAvatar.doId) == winner: + resultsText = TTLocalizer.PartyTeamActivityLocalAvatarTeamWins + else: + resultsText = TTLocalizer.PartyTeamActivityWins % TTLocalizer.PartyCogTeams[winner] + else: + resultsText = TTLocalizer.PartyTeamActivityGameTie + self.view.showResults(resultsText, winner, score) + + def finishConclusion(self): + self.view.hideResults() + DistributedPartyTeamActivity.finishConclusion(self) + self.view.hideArenaDoorTimers() diff --git a/toontown/parties/DistributedPartyCogActivityAI.py b/toontown/parties/DistributedPartyCogActivityAI.py new file mode 100755 index 00000000..328120e4 --- /dev/null +++ b/toontown/parties/DistributedPartyCogActivityAI.py @@ -0,0 +1,20 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyTeamActivityAI import DistributedPartyTeamActivityAI + +class DistributedPartyCogActivityAI(DistributedPartyTeamActivityAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyCogActivityAI") + + def pieThrow(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6): + pass + + def pieHitsToon(self, todo0, todo1, todo2, todo3, todo4): + pass + + def pieHitsCog(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6, todo7): + pass + + def setCogDistances(self, todo0): + pass + + def setHighScore(self, todo0, todo1): + pass diff --git a/toontown/parties/DistributedPartyDance20Activity.py b/toontown/parties/DistributedPartyDance20Activity.py new file mode 100755 index 00000000..5721f6e7 --- /dev/null +++ b/toontown/parties/DistributedPartyDance20Activity.py @@ -0,0 +1,26 @@ +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyDanceActivityBase import DistributedPartyDanceActivityBase +from toontown.toonbase import TTLocalizer + +class DistributedPartyDance20Activity(DistributedPartyDanceActivityBase): + notify = directNotify.newCategory('DistributedPartyDanceActivity') + + def __init__(self, cr): + DistributedPartyDanceActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyDance20, PartyGlobals.DancePatternToAnims20) + + def getInstructions(self): + return TTLocalizer.PartyDanceActivity20Instructions + + def getTitle(self): + return TTLocalizer.PartyDanceActivity20Title + + def load(self): + DistributedPartyDanceActivityBase.load(self) + parentGroup = self.danceFloor.find('**/discoBall_mesh') + correctBall = self.danceFloor.find('**/discoBall_20') + if not correctBall.isEmpty(): + numChildren = parentGroup.getNumChildren() + for i in xrange(numChildren): + child = parentGroup.getChild(i) + if child != correctBall: + child.hide() diff --git a/toontown/parties/DistributedPartyDance20ActivityAI.py b/toontown/parties/DistributedPartyDance20ActivityAI.py new file mode 100755 index 00000000..2b8f0eaa --- /dev/null +++ b/toontown/parties/DistributedPartyDance20ActivityAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyDanceActivityBaseAI import DistributedPartyDanceActivityBaseAI + +class DistributedPartyDance20ActivityAI(DistributedPartyDanceActivityBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyDance20ActivityAI") diff --git a/toontown/parties/DistributedPartyDanceActivity.py b/toontown/parties/DistributedPartyDanceActivity.py new file mode 100755 index 00000000..dfb243d8 --- /dev/null +++ b/toontown/parties/DistributedPartyDanceActivity.py @@ -0,0 +1,27 @@ +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyDanceActivityBase import DistributedPartyDanceActivityBase +from toontown.toonbase import TTLocalizer + +class DistributedPartyDanceActivity(DistributedPartyDanceActivityBase): + notify = directNotify.newCategory('DistributedPartyDanceActivity') + + def __init__(self, cr): + DistributedPartyDanceActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyDance, PartyGlobals.DancePatternToAnims) + + def getInstructions(self): + return TTLocalizer.PartyDanceActivityInstructions + + def getTitle(self): + return TTLocalizer.PartyDanceActivityTitle + + def load(self): + DistributedPartyDanceActivityBase.load(self) + parentGroup = self.danceFloor.find('**/discoBall_mesh') + correctBall = self.danceFloor.find('**/discoBall_10') + origBall = self.danceFloor.find('**/discoBall_mesh_orig') + if not correctBall.isEmpty(): + numChildren = parentGroup.getNumChildren() + for i in xrange(numChildren): + child = parentGroup.getChild(i) + if child != correctBall: + child.hide() diff --git a/toontown/parties/DistributedPartyDanceActivityAI.py b/toontown/parties/DistributedPartyDanceActivityAI.py new file mode 100755 index 00000000..7d78271a --- /dev/null +++ b/toontown/parties/DistributedPartyDanceActivityAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyDanceActivityBaseAI import DistributedPartyDanceActivityBaseAI + +class DistributedPartyDanceActivityAI(DistributedPartyDanceActivityBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyDanceActivityAI") diff --git a/toontown/parties/DistributedPartyDanceActivityBase.py b/toontown/parties/DistributedPartyDanceActivityBase.py new file mode 100755 index 00000000..1b8cfe6e --- /dev/null +++ b/toontown/parties/DistributedPartyDanceActivityBase.py @@ -0,0 +1,369 @@ +import random +from panda3d.core import * +from direct.interval.FunctionInterval import Wait, Func +from direct.interval.MetaInterval import Sequence, Parallel +from direct.showbase.PythonUtil import lerp, Enum +from direct.fsm import FSM +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.minigame.OrthoDrive import OrthoDrive +from toontown.minigame.OrthoWalk import OrthoWalk +from toontown.parties.activityFSMs import DanceActivityFSM +from toontown.parties.PartyGlobals import ActivityIds, ActivityTypes +from toontown.parties.PartyGlobals import DancePatternToAnims, DanceAnimToName +from toontown.parties.DistributedPartyActivity import DistributedPartyActivity +from toontown.parties.PartyDanceActivityToonFSM import PartyDanceActivityToonFSM +from toontown.parties.PartyDanceActivityToonFSM import ToonDancingStates +from toontown.parties.KeyCodes import KeyCodes +from toontown.parties.KeyCodesGui import KeyCodesGui +from toontown.parties import PartyGlobals +DANCE_FLOOR_COLLISION = 'danceFloor_collision' +DanceViews = Enum(('Normal', 'Dancing', 'Isometric')) + +class DistributedPartyDanceActivityBase(DistributedPartyActivity): + notify = directNotify.newCategory('DistributedPartyDanceActivity') + + def __init__(self, cr, actId, dancePatternToAnims, model = 'phase_13/models/parties/danceFloor'): + DistributedPartyActivity.__init__(self, cr, actId, ActivityTypes.Continuous) + self.model = model + self.danceFloor = None + self.localToonDancing = False + self.keyCodes = None + self.gui = None + self.currentCameraMode = None + self.orthoWalk = None + self.cameraParallel = None + self.localToonDanceSequence = None + self.localPatternsMatched = [] + self.dancePatternToAnims = dancePatternToAnims + self.dancingToonFSMs = {} + return + + def generateInit(self): + self.notify.debug('generateInit') + DistributedPartyActivity.generateInit(self) + self.keyCodes = KeyCodes(patterns=self.dancePatternToAnims.keys()) + self.gui = KeyCodesGui(self.keyCodes) + self.__initOrthoWalk() + self.activityFSM = DanceActivityFSM(self) + + def announceGenerate(self): + DistributedPartyActivity.announceGenerate(self) + self.activityFSM.request('Active') + + def load(self): + DistributedPartyActivity.load(self) + self.danceFloor = loader.loadModel(self.model) + self.danceFloor.reparentTo(self.getParentNodePath()) + self.danceFloor.setPos(self.x, self.y, 0.0) + self.danceFloor.setH(self.h) + self.danceFloor.wrtReparentTo(render) + self.sign.setPos(22, -22, 0) + floor = self.danceFloor.find('**/danceFloor_mesh') + self.danceFloorSequence = Sequence(Wait(0.3), Func(floor.setH, floor, 36)) + discoBall = self.danceFloor.find('**/discoBall_mesh') + self.discoBallSequence = Parallel(discoBall.hprInterval(6.0, Vec3(360, 0, 0)), Sequence(discoBall.posInterval(3, Point3(0, 0, 1), blendType='easeInOut'), discoBall.posInterval(3, Point3(0, 0, 0), blendType='easeInOut'))) + + def unload(self): + DistributedPartyActivity.unload(self) + self.activityFSM.request('Disabled') + if self.localToonDanceSequence is not None: + self.localToonDanceSequence.finish() + if self.localToonDancing: + self.__localStopDancing() + self.ignoreAll() + if self.discoBallSequence is not None: + self.discoBallSequence.finish() + if self.danceFloorSequence is not None: + self.danceFloorSequence.finish() + del self.danceFloorSequence + del self.discoBallSequence + del self.localToonDanceSequence + if self.danceFloor is not None: + self.danceFloor.removeNode() + self.danceFloor = None + self.__destroyOrthoWalk() + for toonId in self.dancingToonFSMs.keys(): + self.dancingToonFSMs[toonId].destroy() + del self.dancingToonFSMs[toonId] + + del self.dancingToonFSMs + del self.cameraParallel + del self.currentCameraMode + if self.keyCodes is not None: + self.keyCodes.destroy() + del self.keyCodes + del self.activityFSM + del self.gui + del self.localPatternsMatched + return + + def handleToonDisabled(self, toonId): + self.notify.debug('handleToonDisabled avatar ' + str(toonId) + ' disabled') + if toonId in self.dancingToonFSMs: + self.dancingToonFSMs[toonId].request('cleanup') + self.dancingToonFSMs[toonId].destroy() + del self.dancingToonFSMs[toonId] + + def getTitle(self): + self.notify.warning('define title for this dance activity') + return TTLocalizer.PartyDanceActivityTitle + + def getInstructions(self): + self.notify.warning('define instructions for this dance activity') + return TTLocalizer.PartyDanceActivityInstructions + + def startActive(self): + self.accept('enter' + DANCE_FLOOR_COLLISION, self.__handleEnterDanceFloor) + self.accept('exit' + DANCE_FLOOR_COLLISION, self.__handleExitDanceFloor) + self.danceFloorSequence.loop() + self.discoBallSequence.loop() + + def finishActive(self): + pass + + def startDisabled(self): + self.ignore('enter' + DANCE_FLOOR_COLLISION) + self.ignore('exit' + DANCE_FLOOR_COLLISION) + self.discoBallSequence.pause() + self.danceFloorSequence.pause() + + def finishDisabled(self): + pass + + def __initOrthoWalk(self): + self.notify.debug('Initialize Ortho Walk') + orthoDrive = OrthoDrive(9.778) + self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True) + + def __destroyOrthoWalk(self): + self.notify.debug('Destroy Ortho Walk') + self.orthoWalk.stop() + self.orthoWalk.destroy() + del self.orthoWalk + + def __disableLocalControl(self): + self.orthoWalk.stop() + self.keyCodes.disable() + self.keyCodesGui.disable() + + def __enableLocalControl(self): + self.orthWalk.start() + self.keyCodes.enable() + self.keyCodesGui.enable() + self.keyCodesGui.hideAll() + + def __handleEnterDanceFloor(self, collEntry): + if not self.isLocalToonInActivity() and not self.localToonDancing: + self.notify.debug('Toon enters dance floor collision area.') + place = base.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.fsm.request('activity') + self.d_toonJoinRequest() + place = base.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.fsm.request('activity') + + def joinRequestDenied(self, reason): + DistributedPartyActivity.joinRequestDenied(self, reason) + self.showMessage(TTLocalizer.PartyActivityDefaultJoinDeny) + place = base.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.fsm.request('walk') + + def setToonsPlaying(self, toonIds, toonHeadings): + self.notify.debug('setToonsPlaying') + self.notify.debug('\ttoonIds: %s' % toonIds) + self.notify.debug('\ttoonHeadings: %s' % toonHeadings) + exitedToons, joinedToons = self.getToonsPlayingChanges(self.toonIds, toonIds) + self.notify.debug('\texitedToons: %s' % exitedToons) + self.notify.debug('\tjoinedToons: %s' % joinedToons) + self.setToonIds(toonIds) + self._processExitedToons(exitedToons) + for toonId in joinedToons: + if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus(PartyGlobals.ActivityRequestStatus.Joining): + self._enableHandleToonDisabled(toonId) + self.handleToonJoined(toonId, toonHeadings[toonIds.index(toonId)]) + if toonId == base.localAvatar.doId: + self._localToonRequestStatus = None + + return + + def handleToonJoined(self, toonId, h): + self.notify.debug('handleToonJoined( toonId=%d, h=%.2f )' % (toonId, h)) + if toonId in base.cr.doId2do: + toonFSM = PartyDanceActivityToonFSM(toonId, self, h) + toonFSM.request('Init') + self.dancingToonFSMs[toonId] = toonFSM + if toonId == base.localAvatar.doId: + self.__localStartDancing(h) + + def __localStartDancing(self, h): + if not self.localToonDancing: + place = base.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + self.localToonDancing = True + place.fsm.request('activity') + self.__updateLocalToonState(ToonDancingStates.Run) + self.__setViewMode(DanceViews.Dancing) + self.gui.load() + self.startRules() + self.__localEnableControls() + else: + self.notify.warning('__localStartDancing, failed in playGame.getPlace()') + + def handleRulesDone(self): + self.finishRules() + + def __localEnableControls(self): + if base.localAvatar.doId not in self.dancingToonFSMs: + self.notify.debug('no dancing FSM for local avatar, not enabling controls') + return + self.accept(KeyCodes.PATTERN_MATCH_EVENT, self.__doDanceMove) + self.accept(KeyCodes.PATTERN_NO_MATCH_EVENT, self.__noDanceMoveMatch) + self.acceptOnce(KeyCodes.KEY_DOWN_EVENT, self._handleKeyDown) + self.accept(KeyCodes.KEY_UP_EVENT, self._handleKeyUp) + self.keyCodes.enable() + self.orthoWalk.start() + self.gui.enable() + self.gui.hideAll() + + def __localDisableControls(self): + self.orthoWalk.stop() + self.keyCodes.disable() + self.gui.disable() + self.ignore(KeyCodes.PATTERN_MATCH_EVENT) + self.ignore(KeyCodes.PATTERN_NO_MATCH_EVENT) + self.ignore(KeyCodes.KEY_DOWN_EVENT) + self.ignore(KeyCodes.KEY_UP_EVENT) + + def __handleExitDanceFloor(self, collEntry): + if self.localToonDanceSequence is not None: + self.notify.debug('finishing %s' % self.localToonDanceSequence) + self.localToonDanceSequence.finish() + self.localToonDanceSequence = None + self.finishRules() + self.notify.debug('Toon exits dance floor collision area.') + self.d_toonExitRequest() + return + + def exitRequestDenied(self, reason): + DistributedPartyActivity.exitRequestDenied(self, reason) + if reason != PartyGlobals.DenialReasons.SilentFail: + self.showMessage(TTLocalizer.PartyActivityDefaultExitDeny) + + def handleToonExited(self, toonId): + self.notify.debug('exitDanceFloor %s' % toonId) + if toonId == base.localAvatar.doId: + self.__localStopDancing() + + def __localStopDancing(self): + if self.localToonDancing: + self.__localDisableControls() + self.gui.unload() + self.__setViewMode(DanceViews.Normal) + self.__updateLocalToonState(ToonDancingStates.Cleanup) + if base.cr.playGame.getPlace(): + if hasattr(base.cr.playGame.getPlace(), 'fsm'): + base.cr.playGame.getPlace().fsm.request('walk') + self.localToonDancing = False + + def __doDanceMove(self, pattern): + self.notify.debug('Dance move! %s' % pattern) + anim = self.dancePatternToAnims.get(pattern) + if anim: + self.__updateLocalToonState(ToonDancingStates.DanceMove, anim) + self.gui.setColor(0, 1, 0) + self.gui.showText(DanceAnimToName.get(anim, anim)) + self.finishRules() + if pattern not in self.localPatternsMatched: + camNode = NodePath(self.uniqueName('danceCamNode')) + camNode.reparentTo(base.localAvatar) + camNode.lookAt(camera) + camNode.setHpr(camNode.getH(), 0, 0) + node2 = NodePath('tempCamNode') + node2.reparentTo(camNode) + node2.setPos(Point3(0, 15, 10)) + node2.lookAt(camNode) + h = node2.getH() * (camera.getH(camNode) / abs(camera.getH(camNode))) + node2.removeNode + del node2 + hpr = camera.getHpr() + pos = camera.getPos() + camParent = camera.getParent() + camera.wrtReparentTo(camNode) + self.localToonDanceSequence = Sequence(Func(self.__localDisableControls), Parallel(camera.posInterval(0.5, Point3(0, 15, 10), blendType='easeIn'), camera.hprInterval(0.5, Point3(h, -20, 0), blendType='easeIn')), camNode.hprInterval(4.0, Point3(camNode.getH() - 360, 0, 0)), Func(camera.wrtReparentTo, camParent), Func(camNode.removeNode), Parallel(camera.posInterval(0.5, pos, blendType='easeOut'), camera.hprInterval(0.5, hpr, blendType='easeOut')), Func(self.__localEnableControls)) + else: + self.localToonDanceSequence = Sequence(Func(self.__localDisableControls), Wait(2.0), Func(self.__localEnableControls)) + self.localToonDanceSequence.start() + self.localPatternsMatched.append(pattern) + + def __noDanceMoveMatch(self): + self.gui.setColor(1, 0, 0) + self.gui.showText('No Match!') + self.__updateLocalToonState(ToonDancingStates.DanceMove) + self.localToonDanceSequence = Sequence(Func(self.__localDisableControls), Wait(1.0), Func(self.__localEnableControls)) + self.localToonDanceSequence.start() + + def _handleKeyDown(self, key, index): + self.__updateLocalToonState(ToonDancingStates.Run) + + def _handleKeyUp(self, key): + if not self.keyCodes.isAnyKeyPressed(): + self.__updateLocalToonState(ToonDancingStates.DanceMove) + self.acceptOnce(KeyCodes.KEY_DOWN_EVENT, self._handleKeyDown) + + def __updateLocalToonState(self, state, anim = ''): + self._requestToonState(base.localAvatar.doId, state, anim) + self.d_updateDancingToon(state, anim) + + def d_updateDancingToon(self, state, anim): + self.sendUpdate('updateDancingToon', [state, anim]) + + def setDancingToonState(self, toonId, state, anim): + if toonId != base.localAvatar.doId and toonId in self.dancingToonFSMs: + self._requestToonState(toonId, state, anim) + + def _requestToonState(self, toonId, state, anim): + if toonId in self.dancingToonFSMs: + state = ToonDancingStates.getString(state) + curState = self.dancingToonFSMs[toonId].getCurrentOrNextState() + try: + self.dancingToonFSMs[toonId].request(state, anim) + except FSM.RequestDenied: + self.notify.warning('could not go from state=%s to state %s' % (curState, state)) + + if state == ToonDancingStates.getString(ToonDancingStates.Cleanup): + self.notify.debug('deleting this fsm %s' % self.dancingToonFSMs[toonId]) + del self.dancingToonFSMs[toonId] + if self.localToonDanceSequence: + self.notify.debug('forcing a finish of localToonDanceSequence') + self.localToonDanceSequence.finish() + self.localToonDanceSequence = None + return + + def __setViewMode(self, mode): + toon = base.localAvatar + if mode == DanceViews.Normal: + if self.cameraParallel is not None: + self.cameraParallel.pause() + self.cameraParallel = None + camera.reparentTo(toon) + base.localAvatar.startUpdateSmartCamera() + elif mode == DanceViews.Dancing: + base.localAvatar.stopUpdateSmartCamera() + camera.wrtReparentTo(self.danceFloor) + node = NodePath('temp') + node.reparentTo(toon.getParent()) + node.setPos(Point3(0, -40, 20)) + node2 = NodePath('temp2') + node2.reparentTo(self.danceFloor) + node.reparentTo(node2) + node2.setH(render, toon.getParent().getH()) + pos = node.getPos(self.danceFloor) + node2.removeNode() + node.removeNode() + self.cameraParallel = Parallel(camera.posInterval(0.5, pos, blendType='easeIn'), camera.hprInterval(0.5, Point3(0, -27, 0), other=toon.getParent(), blendType='easeIn')) + self.cameraParallel.start() + self.currentCameraMode = mode + return diff --git a/toontown/parties/DistributedPartyDanceActivityBaseAI.py b/toontown/parties/DistributedPartyDanceActivityBaseAI.py new file mode 100755 index 00000000..19e651cd --- /dev/null +++ b/toontown/parties/DistributedPartyDanceActivityBaseAI.py @@ -0,0 +1,47 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyActivityAI import DistributedPartyActivityAI +from direct.distributed.ClockDelta import * +import PartyGlobals + +class DistributedPartyDanceActivityBaseAI(DistributedPartyActivityAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyDanceActivityBaseAI") + + def __init__(self, air, parent, activityTuple): + DistributedPartyActivityAI.__init__(self, air, parent, activityTuple) + self.toons = [] + self.headings = [] + + def generate(self): + DistributedPartyActivityAI.generate(self) + self.sendUpdate('setState', ['Active', globalClockDelta.getRealNetworkTime()]) + + def updateDancingToon(self, state, anim): + avId = self.air.getAvatarIdFromSender() + if not avId in self.toons: + self.air.writeServerEvent('suspicious',avId,'Toon tried to update their state while not dancing!') + return + self.sendUpdate('setDancingToonState', [avId, state, anim]) + + + def toonJoinRequest(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.toons: + self.air.writeServerEvent('suspicious',avId,'Toon tried to enter dance activity twice!') + return + av = self.air.doId2do.get(avId) + if not av: + self.air.writeServerEvent('suspicious',avId,'Toon tried to interact with a party activity from a different district!') + return + self.toons.append(avId) + self.headings.append(av.getH()) + self.sendUpdate('setToonsPlaying', [self.toons, self.headings]) + + def toonExitRequest(self): + avId = self.air.getAvatarIdFromSender() + if not avId in self.toons: + self.air.writeServerEvent('suspicious',avId,'Toon tried to exit a dance floor they\'re not on!') + return + index = self.toons.index(avId) + self.toons.remove(avId) + self.headings.pop(index) + self.sendUpdate('setToonsPlaying', [self.toons, self.headings]) diff --git a/toontown/parties/DistributedPartyFireworksActivity.py b/toontown/parties/DistributedPartyFireworksActivity.py new file mode 100755 index 00000000..e0b164b2 --- /dev/null +++ b/toontown/parties/DistributedPartyFireworksActivity.py @@ -0,0 +1,158 @@ +from pandac.PandaModules import Vec3 +from pandac.PandaModules import OmniBoundingVolume +from pandac.PandaModules import AlphaTestAttrib +from pandac.PandaModules import RenderAttrib +from direct.actor.Actor import Actor +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import globalClockDelta +from toontown.effects.FireworkShowMixin import FireworkShowMixin +from toontown.effects.RocketExplosion import RocketExplosion +from toontown.toonbase import TTLocalizer +from PartyGlobals import FireworkShows +from PartyGlobals import ActivityIds +from PartyGlobals import ActivityTypes +from PartyGlobals import FireworksStartedEvent +from PartyGlobals import FireworksFinishedEvent +from PartyGlobals import FireworksPostLaunchDelay +from PartyGlobals import RocketSoundDelay +from PartyGlobals import RocketDirectionDelay +from DistributedPartyActivity import DistributedPartyActivity +from activityFSMs import FireworksActivityFSM +import PartyGlobals + +class DistributedPartyFireworksActivity(DistributedPartyActivity, FireworkShowMixin): + notify = directNotify.newCategory('DistributedPartyFireworksActivity') + + def __init__(self, cr): + DistributedPartyFireworksActivity.notify.debug('__init__') + DistributedPartyActivity.__init__(self, cr, ActivityIds.PartyFireworks, ActivityTypes.HostInitiated, wantLever=True) + FireworkShowMixin.__init__(self, restorePlaygroundMusic=True, startDelay=FireworksPostLaunchDelay) + + def setEventId(self, eventId): + DistributedPartyFireworksActivity.notify.debug('setEventId( %s )' % FireworkShows.getString(eventId)) + self.eventId = eventId + + def setShowStyle(self, showStyle): + DistributedPartyFireworksActivity.notify.debug('setShowStyle( %d )' % showStyle) + self.showStyle = showStyle + + def setSongId(self, songId): + self.songId = songId + + def load(self): + DistributedPartyFireworksActivity.notify.debug('load') + DistributedPartyActivity.load(self) + self.eventId = PartyGlobals.FireworkShows.Summer + self.launchPadModel = loader.loadModel('phase_13/models/parties/launchPad') + self.launchPadModel.setH(90.0) + self.launchPadModel.setPos(0.0, -18.0, 0.0) + self.launchPadModel.reparentTo(self.root) + railingsCollection = self.launchPadModel.findAllMatches('**/launchPad_mesh/*railing*') + for i in xrange(railingsCollection.getNumPaths()): + railingsCollection[i].setAttrib(AlphaTestAttrib.make(RenderAttrib.MGreater, 0.75)) + + leverLocator = self.launchPadModel.find('**/RocketLever_locator') + self.lever.setPosHpr(Vec3.zero(), Vec3.zero()) + self.lever.reparentTo(leverLocator) + self.toonPullingLeverInterval = None + self.sign.reparentTo(self.launchPadModel.find('**/launchPad_sign_locator')) + self.rocketActor = Actor('phase_13/models/parties/rocket_model', {'launch': 'phase_13/models/parties/rocket_launch'}) + rocketLocator = self.launchPadModel.find('**/rocket_locator') + self.rocketActor.reparentTo(rocketLocator) + self.rocketActor.node().setBound(OmniBoundingVolume()) + self.rocketActor.node().setFinal(True) + effectsLocator = self.rocketActor.find('**/joint1') + self.rocketExplosionEffect = RocketExplosion(effectsLocator, rocketLocator) + self.rocketParticleSeq = None + self.launchSound = base.loadSfx('phase_13/audio/sfx/rocket_launch.ogg') + self.activityFSM = FireworksActivityFSM(self) + self.activityFSM.request('Idle') + return + + def unload(self): + DistributedPartyFireworksActivity.notify.debug('unload') + taskMgr.remove(self.taskName('delayedStartShow')) + if self.rocketParticleSeq: + self.rocketParticleSeq.pause() + self.rocketParticleSeq = None + self.launchPadModel.removeNode() + del self.launchPadModel + del self.toonPullingLeverInterval + self.rocketActor.delete() + self.rocketExplosionEffect.destroy() + self.activityFSM.request('Disabled') + del self.rocketActor + del self.launchSound + del self.activityFSM + del self.eventId + del self.showStyle + DistributedPartyActivity.unload(self) + return + + def _leverPulled(self, collEntry): + DistributedPartyFireworksActivity.notify.debug('_leverPulled') + hostPulledLever = DistributedPartyActivity._leverPulled(self, collEntry) + if self.activityFSM.getCurrentOrNextState() == 'Active': + self.showMessage(TTLocalizer.PartyFireworksAlreadyActive) + elif self.activityFSM.getCurrentOrNextState() == 'Disabled': + self.showMessage(TTLocalizer.PartyFireworksAlreadyDone) + elif self.activityFSM.getCurrentOrNextState() == 'Idle': + if hostPulledLever: + base.cr.playGame.getPlace().fsm.request('activity') + self.toonPullingLeverInterval = self.getToonPullingLeverInterval(base.localAvatar) + self.toonPullingLeverInterval.append(Func(self.d_toonJoinRequest)) + self.toonPullingLeverInterval.append(Func(base.cr.playGame.getPlace().fsm.request, 'walk')) + self.toonPullingLeverInterval.start() + else: + self.showMessage(TTLocalizer.PartyOnlyHostLeverPull) + + def setState(self, newState, timestamp): + DistributedPartyFireworksActivity.notify.debug('setState( newState=%s, ... )' % newState) + DistributedPartyActivity.setState(self, newState, timestamp) + if newState == 'Active': + self.activityFSM.request(newState, timestamp) + else: + self.activityFSM.request(newState) + + def startIdle(self): + DistributedPartyFireworksActivity.notify.debug('startIdle') + + def finishIdle(self): + DistributedPartyFireworksActivity.notify.debug('finishIdle') + + def startActive(self, showStartTimestamp): + DistributedPartyFireworksActivity.notify.debug('startActive') + messenger.send(FireworksStartedEvent) + timeSinceStart = globalClockDelta.localElapsedTime(showStartTimestamp) + if timeSinceStart > self.rocketActor.getDuration('launch'): + self.rocketActor.hide() + if timeSinceStart < 60: + self.startShow(self.eventId, self.showStyle, self.songId, showStartTimestamp) + else: + self.rocketActor.play('launch') + self.rocketParticleSeq = Sequence(Wait(RocketSoundDelay), Func(base.playSfx, self.launchSound), Func(self.rocketExplosionEffect.start), Wait(RocketDirectionDelay), LerpHprInterval(self.rocketActor, 4.0, Vec3(0, 0, -60)), Func(self.rocketExplosionEffect.end), Func(self.rocketActor.hide)) + self.rocketParticleSeq.start() + taskMgr.doMethodLater(FireworksPostLaunchDelay, self.startShow, self.taskName('delayedStartShow'), extraArgs=[self.eventId, + self.showStyle, + self.songId, + showStartTimestamp, + self.root]) + + def finishActive(self): + self.rocketParticleSeq = None + DistributedPartyFireworksActivity.notify.debug('finishActive') + messenger.send(FireworksFinishedEvent) + taskMgr.remove(self.taskName('delayedStartShow')) + FireworkShowMixin.disable(self) + return + + def startDisabled(self): + DistributedPartyFireworksActivity.notify.debug('startDisabled') + if not self.rocketActor.isEmpty(): + self.rocketActor.hide() + + def finishDisabled(self): + DistributedPartyFireworksActivity.notify.debug('finishDisabled') + + def handleToonDisabled(self, toonId): + self.notify.warning('handleToonDisabled no implementation yet') diff --git a/toontown/parties/DistributedPartyFireworksActivityAI.py b/toontown/parties/DistributedPartyFireworksActivityAI.py new file mode 100755 index 00000000..27f3622a --- /dev/null +++ b/toontown/parties/DistributedPartyFireworksActivityAI.py @@ -0,0 +1,38 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyActivityAI import DistributedPartyActivityAI +from direct.distributed.ClockDelta import globalClockDelta +from direct.fsm.FSM import FSM +from toontown.effects import FireworkShows +import PartyGlobals +import random + +class DistributedPartyFireworksActivityAI(DistributedPartyActivityAI, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyFireworksActivityAI") + + def __init__(self, air, parent, activityTuple): + DistributedPartyActivityAI.__init__(self, air, parent, activityTuple) + FSM.__init__(self, 'DistributedPartyActivityAI') + self.state = 'Idle' + + def getEventId(self): + return PartyGlobals.FireworkShows.Summer + + def getShowStyle(self): + return random.randint(0, len(FireworkShows.shows[PartyGlobals.FireworkShows.Summer]) - 1) + + def getSongId(self): + return random.randint(0, 1) + + def toonJoinRequest(self): + avId = self.air.getAvatarIdFromSender() + host = self.air.doId2do[self.parent].hostId + if avId == host and self.state == 'Idle': + self.request('Active') + return + self.sendUpdateToAvatarId(avId, 'joinRequestDenied', [1]) + + def enterActive(self): + self.sendUpdate('setState', ['Active', globalClockDelta.getRealNetworkTime()]) + + def enterIdle(self): + self.sendUpdate('setState', ['Idle', globalClockDelta.getRealNetworkTime()]) diff --git a/toontown/parties/DistributedPartyJukebox40Activity.py b/toontown/parties/DistributedPartyJukebox40Activity.py new file mode 100755 index 00000000..6358beda --- /dev/null +++ b/toontown/parties/DistributedPartyJukebox40Activity.py @@ -0,0 +1,18 @@ +from toontown.parties.DistributedPartyJukeboxActivityBase import DistributedPartyJukeboxActivityBase +from toontown.parties import PartyGlobals + +class DistributedPartyJukebox40Activity(DistributedPartyJukeboxActivityBase): + notify = directNotify.newCategory('DistributedPartyJukeboxActivity') + + def __init__(self, cr): + DistributedPartyJukeboxActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyJukebox40, PartyGlobals.PhaseToMusicData40) + + def load(self): + DistributedPartyJukeboxActivityBase.load(self) + newTexture = loader.loadTexture('phase_13/maps/tt_t_ara_pty_jukeboxBlue.jpg', 'phase_13/maps/tt_t_ara_pty_jukeboxBlue_a.rgb') + case = self.jukebox.find('**/jukeboxGlass') + if not case.isEmpty(): + case.setTexture(newTexture, 1) + body = self.jukebox.find('**/jukeboxBody') + if not body.isEmpty(): + body.setTexture(newTexture, 1) diff --git a/toontown/parties/DistributedPartyJukebox40ActivityAI.py b/toontown/parties/DistributedPartyJukebox40ActivityAI.py new file mode 100755 index 00000000..576d825e --- /dev/null +++ b/toontown/parties/DistributedPartyJukebox40ActivityAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyJukeboxActivityBaseAI import DistributedPartyJukeboxActivityBaseAI + +class DistributedPartyJukebox40ActivityAI(DistributedPartyJukeboxActivityBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyJukebox40ActivityAI") diff --git a/toontown/parties/DistributedPartyJukeboxActivity.py b/toontown/parties/DistributedPartyJukeboxActivity.py new file mode 100755 index 00000000..bb1f2ca2 --- /dev/null +++ b/toontown/parties/DistributedPartyJukeboxActivity.py @@ -0,0 +1,8 @@ +from toontown.parties.DistributedPartyJukeboxActivityBase import DistributedPartyJukeboxActivityBase +from toontown.parties import PartyGlobals + +class DistributedPartyJukeboxActivity(DistributedPartyJukeboxActivityBase): + notify = directNotify.newCategory('DistributedPartyJukeboxActivity') + + def __init__(self, cr): + DistributedPartyJukeboxActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyJukebox, PartyGlobals.PhaseToMusicData) diff --git a/toontown/parties/DistributedPartyJukeboxActivityAI.py b/toontown/parties/DistributedPartyJukeboxActivityAI.py new file mode 100755 index 00000000..edc65083 --- /dev/null +++ b/toontown/parties/DistributedPartyJukeboxActivityAI.py @@ -0,0 +1,10 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyJukeboxActivityBaseAI import DistributedPartyJukeboxActivityBaseAI +import PartyGlobals + +class DistributedPartyJukeboxActivityAI(DistributedPartyJukeboxActivityBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyJukeboxActivityAI") + + def __init__(self, air, parent, activityTuple): + DistributedPartyJukeboxActivityBaseAI.__init__(self, air, parent, activityTuple) + self.music = PartyGlobals.PhaseToMusicData diff --git a/toontown/parties/DistributedPartyJukeboxActivityBase.py b/toontown/parties/DistributedPartyJukeboxActivityBase.py new file mode 100755 index 00000000..940b6a5e --- /dev/null +++ b/toontown/parties/DistributedPartyJukeboxActivityBase.py @@ -0,0 +1,230 @@ +from direct.actor.Actor import Actor +from direct.task.Task import Task +from panda3d.core import * +from otp.otpbase.OTPBase import OTPBase +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties.DistributedPartyActivity import DistributedPartyActivity +from toontown.parties.PartyGlobals import ActivityIds, ActivityTypes, JUKEBOX_TIMEOUT +from toontown.parties.PartyGlobals import getMusicRepeatTimes, MUSIC_PATH, sanitizePhase +from toontown.parties.JukeboxGui import JukeboxGui + +class DistributedPartyJukeboxActivityBase(DistributedPartyActivity): + notify = directNotify.newCategory('DistributedPartyJukeboxActivityBase') + + def __init__(self, cr, actId, phaseToMusicData): + DistributedPartyActivity.__init__(self, cr, actId, ActivityTypes.Continuous) + self.phaseToMusicData = phaseToMusicData + self.jukebox = None + self.gui = None + self.tunes = [] + self.music = None + self.currentSongData = None + self.localQueuedSongInfo = None + self.localQueuedSongListItem = None + return + + def generateInit(self): + self.gui = JukeboxGui(self.phaseToMusicData) + + def load(self): + DistributedPartyActivity.load(self) + self.jukebox = Actor('phase_13/models/parties/jukebox_model', {'dance': 'phase_13/models/parties/jukebox_dance'}) + self.jukebox.reparentTo(self.root) + self.jukebox.loop('dance', fromFrame=0, toFrame=48) + self.collNode = CollisionNode(self.getCollisionName()) + self.collNode.setCollideMask(ToontownGlobals.CameraBitmask | ToontownGlobals.WallBitmask) + collTube = CollisionTube(0, 0, 0, 0.0, 0.0, 4.25, 2.25) + collTube.setTangible(1) + self.collNode.addSolid(collTube) + self.collNodePath = self.jukebox.attachNewNode(self.collNode) + self.sign.setPos(-5.0, 0, 0) + self.activate() + + def unload(self): + DistributedPartyActivity.unload(self) + self.gui.unload() + if self.music is not None: + self.music.stop() + self.jukebox.stop() + self.jukebox.delete() + self.jukebox = None + self.ignoreAll() + return + + def getCollisionName(self): + return self.uniqueName('jukeboxCollision') + + def activate(self): + self.accept('enter' + self.getCollisionName(), self.__handleEnterCollision) + + def __handleEnterCollision(self, collisionEntry): + if base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'walk': + base.cr.playGame.getPlace().fsm.request('activity') + self.d_toonJoinRequest() + + def joinRequestDenied(self, reason): + DistributedPartyActivity.joinRequestDenied(self, reason) + self.showMessage(TTLocalizer.PartyJukeboxOccupied) + + def handleToonJoined(self, toonId): + toon = base.cr.doId2do.get(toonId) + if toon: + self.jukebox.lookAt(base.cr.doId2do[toonId]) + self.jukebox.setHpr(self.jukebox.getH() + 180.0, 0, 0) + if toonId == base.localAvatar.doId: + self.__localUseJukebox() + + def handleToonExited(self, toonId): + if toonId == base.localAvatar.doId and self.gui.isLoaded(): + self.__deactivateGui() + + def handleToonDisabled(self, toonId): + self.notify.warning('handleToonDisabled no implementation yet') + + def __localUseJukebox(self): + base.localAvatar.disableAvatarControls() + base.localAvatar.stopPosHprBroadcast() + self.__activateGui() + self.accept(JukeboxGui.CLOSE_EVENT, self.__handleGuiClose) + taskMgr.doMethodLater(0.5, self.__localToonWillExitTask, self.uniqueName('toonWillExitJukeboxOnTimeout'), extraArgs=None) + self.accept(JukeboxGui.ADD_SONG_CLICK_EVENT, self.__handleQueueSong) + if self.isUserHost(): + self.accept(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT, self.__handleMoveSongToTop) + return + + def __localToonWillExitTask(self, task): + self.localToonExiting() + return Task.done + + def __activateGui(self): + self.gui.enable(timer=JUKEBOX_TIMEOUT) + self.gui.disableAddSongButton() + if self.currentSongData is not None: + self.gui.setSongCurrentlyPlaying(self.currentSongData[0], self.currentSongData[1]) + self.d_queuedSongsRequest() + return + + def __deactivateGui(self): + self.ignore(JukeboxGui.CLOSE_EVENT) + self.ignore(JukeboxGui.SONG_SELECT_EVENT) + self.ignore(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT) + base.cr.playGame.getPlace().setState('walk') + base.localAvatar.startPosHprBroadcast() + base.localAvatar.enableAvatarControls() + self.gui.unload() + self.__localClearQueuedSong() + + def isUserHost(self): + return self.party.partyInfo.hostId == base.localAvatar.doId + + def d_queuedSongsRequest(self): + self.sendUpdate('queuedSongsRequest') + + def queuedSongsResponse(self, songInfoList, index): + if self.gui.isLoaded(): + for i in xrange(len(songInfoList)): + songInfo = songInfoList[i] + self.__addSongToQueue(songInfo, isLocalQueue=index >= 0 and i == index) + + self.gui.enableAddSongButton() + + def __handleGuiClose(self): + self.__deactivateGui() + self.d_toonExitDemand() + + def __handleQueueSong(self, name, values): + self.d_setNextSong(values[0], values[1]) + + def d_setNextSong(self, phase, filename): + self.sendUpdate('setNextSong', [(phase, filename)]) + + def setSongInQueue(self, songInfo): + if self.gui.isLoaded(): + phase = sanitizePhase(songInfo[0]) + filename = songInfo[1] + data = self.getMusicData(phase, filename) + if data: + if self.localQueuedSongListItem is not None: + self.localQueuedSongListItem['text'] = data[0] + else: + self.__addSongToQueue(songInfo, isLocalQueue=True) + return + + def __addSongToQueue(self, songInfo, isLocalQueue = False): + isHost = isLocalQueue and self.isUserHost() + data = self.getMusicData(sanitizePhase(songInfo[0]), songInfo[1]) + if data: + listItem = self.gui.addSongToQueue(data[0], highlight=isLocalQueue, moveToTopButton=isHost) + if isLocalQueue: + self.localQueuedSongInfo = songInfo + self.localQueuedSongListItem = listItem + + def __localClearQueuedSong(self): + self.localQueuedSongInfo = None + self.localQueuedSongListItem = None + return + + def __play(self, phase, filename, length): + self.music = base.loadMusic((MUSIC_PATH + '%s') % (phase, filename)) + if self.music: + if self.__checkPartyValidity() and hasattr(base.cr.playGame.getPlace().loader, 'music') and base.cr.playGame.getPlace().loader.music: + base.cr.playGame.getPlace().loader.music.stop() + self.music.setTime(getMusicRepeatTimes(length)) + self.music.setLoopCount(0) + self.music.play() + self.currentSongData = (phase, filename) + + def __stop(self): + self.currentSongData = None + if self.music: + self.music.stop() + if self.gui.isLoaded(): + self.gui.clearSongCurrentlyPlaying() + return + + def setSongPlaying(self, songInfo, toonId): + phase = sanitizePhase(songInfo[0]) + filename = songInfo[1] + if not filename: + self.__stop() + return + data = self.getMusicData(phase, filename) + if data: + self.__play(phase, filename, data[1]) + self.setSignNote(data[0]) + if self.gui.isLoaded(): + item = self.gui.popSongFromQueue() + self.gui.setSongCurrentlyPlaying(phase, filename) + if item == self.localQueuedSongListItem: + self.__localClearQueuedSong() + if toonId == localAvatar.doId: + localAvatar.setSystemMessage(0, TTLocalizer.PartyJukeboxNowPlaying) + + def __handleMoveSongToTop(self): + if self.isUserHost() and self.localQueuedSongListItem is not None: + self.d_moveHostSongToTopRequest() + return + + def d_moveHostSongToTopRequest(self): + self.notify.debug('d_moveHostSongToTopRequest') + self.sendUpdate('moveHostSongToTopRequest') + + def moveHostSongToTop(self): + self.notify.debug('moveHostSongToTop') + if self.gui.isLoaded(): + self.gui.pushQueuedItemToTop(self.localQueuedSongListItem) + + def getMusicData(self, phase, filename): + data = [] + phase = sanitizePhase(phase) + phase = self.phaseToMusicData.get(phase) + if phase: + data = phase.get(filename, []) + return data + + def __checkPartyValidity(self): + if hasattr(base.cr.playGame, 'getPlace') and base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'loader') and base.cr.playGame.getPlace().loader: + return True + else: + return False diff --git a/toontown/parties/DistributedPartyJukeboxActivityBaseAI.py b/toontown/parties/DistributedPartyJukeboxActivityBaseAI.py new file mode 100755 index 00000000..bb732470 --- /dev/null +++ b/toontown/parties/DistributedPartyJukeboxActivityBaseAI.py @@ -0,0 +1,130 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyActivityAI import DistributedPartyActivityAI +from direct.task import Task +import PartyGlobals + +class DistributedPartyJukeboxActivityBaseAI(DistributedPartyActivityAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyJukeboxActivityBaseAI") + + def __init__(self, air, parent, activityTuple): + DistributedPartyActivityAI.__init__(self, air, parent, activityTuple) + self.music = PartyGlobals.PhaseToMusicData40 + self.queue = [] + self.owners = [] + self.currentToon = 0 + self.playing = False + + def delete(self): + taskMgr.remove('playSong%d' % self.doId) + DistributedPartyActivityAI.delete(self) + + + def setNextSong(self, song): + avId = self.air.getAvatarIdFromSender() + phase = self.music.get(song[0]) + if avId != self.currentToon: + self.air.writeServerEvent('suspicious',avId,'Toon tried to set song without using the jukebox!') + if not phase: + self.air.writeServerEvent('suspicious',avId,'Toon supplied invalid phase for song!') + return + if song[1] not in phase: + self.air.writeServerEvent('suspicious',avId,'Toon supplied invalid song name!') + return + if avId in self.owners: + self.queue[self.owners.index(avId)] = song + else: + self.queue.append(song) + self.owners.append(avId) + for toon in self.toonsPlaying: + self.sendUpdateToAvatarId(toon, 'setSongInQueue', [song]) + if not self.playing: + #stop default party music... + self.d_setSongPlaying([0, ''], 0) + self.__startPlaying() + + def __startPlaying(self): + if len(self.queue) == 0: + #start default party music! + self.d_setSongPlaying([13, 'party_original_theme.ogg'], 0) + self.playing = False + return + self.playing = True + + #get song information.... + details = self.queue.pop(0) + owner = self.owners.pop(0) + + songInfo = self.music[details[0]][details[1]] + + #play song! + self.d_setSongPlaying(details, owner) + + taskMgr.doMethodLater(songInfo[1]*PartyGlobals.getMusicRepeatTimes(songInfo[1]), self.__pause, 'playSong%d' % self.doId, extraArgs=[]) + + def __pause(self): + #stop music! + self.d_setSongPlaying([0, ''], 0) + #and hold. + taskMgr.doMethodLater(PartyGlobals.MUSIC_GAP, self.__startPlaying, 'playSong%d' % self.doId, extraArgs=[]) + + def toonJoinRequest(self): + avId = self.air.getAvatarIdFromSender() + if self.currentToon: + self.sendUpdateToAvatarId(avId, 'joinRequestDenied', [1]) + return + self.currentToon = avId + taskMgr.doMethodLater(PartyGlobals.JUKEBOX_TIMEOUT, self.__removeToon, 'removeToon%d', extraArgs=[]) + self.toonsPlaying.append(avId) + self.updateToonsPlaying() + + def toonExitRequest(self): + pass + + def toonExitDemand(self): + avId = self.air.getAvatarIdFromSender() + if avId != self.currentToon: + return + taskMgr.remove('removeToon%d' % self.doId) + self.currentToon = 0 + self.toonsPlaying.remove(avId) + self.updateToonsPlaying() + + def __removeToon(self): + if not self.currentToon: + return + self.toonsPlaying.remove(self.currentToon) + self.updateToonsPlaying() + self.currentToon = 0 + + def d_setSongPlaying(self, details, owner): + self.sendUpdate('setSongPlaying', [details, owner]) + + def queuedSongsRequest(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.owners: + index = self.owners.index(avId) + else: + index = -1 + self.sendUpdateToAvatarId(avId, 'queuedSongsResponse', [self.queue, index]) + + def moveHostSongToTopRequest(self): + avId = self.air.getAvatarIdFromSender() + if avId != self.currentToon: + self.air.writeServerEvent('suspicious',avId,'Toon tried to set song without using the jukebox!') + host = self.air.doId2do[self.parent].hostId + if avId != host: + self.air.writeServerEvent('suspicious',avId,'Toon tried to move the host\'s song to the top!') + return + if not host in self.owners: + self.air.writeServerEvent('suspicious',avId,'Host tried to move non-existent song to the top of the queue!') + return + index = self.owners.index(host) + + self.owners.remove(host) + song = self.queue.pop(index) + + self.owners.insert(0, host) + self.queue.insert(0, song) + + for toon in self.toonsPlaying: + self.sendUpdateToAvatarId(toon, 'moveHostSongToTop', []) diff --git a/toontown/parties/DistributedPartyTeamActivity.py b/toontown/parties/DistributedPartyTeamActivity.py new file mode 100755 index 00000000..7324277f --- /dev/null +++ b/toontown/parties/DistributedPartyTeamActivity.py @@ -0,0 +1,378 @@ +from direct.distributed.ClockDelta import globalClockDelta +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyActivity import DistributedPartyActivity +from toontown.parties.activityFSMs import TeamActivityFSM +from toontown.parties.TeamActivityGui import TeamActivityGui + +class DistributedPartyTeamActivity(DistributedPartyActivity): + notify = directNotify.newCategory('DistributedPartyTeamActivity') + + def __init__(self, cr, activityId, startDelay = PartyGlobals.TeamActivityStartDelay, balanceTeams = False): + DistributedPartyActivity.__init__(self, cr, activityId, PartyGlobals.ActivityTypes.GuestInitiated, wantRewardGui=True) + self.notify.debug('__init__') + self.toonIds = ([], []) + self.isLocalToonPlaying = False + self.localToonTeam = None + self.advantage = 1.0 + self.waitToStartTimestamp = None + self._maxPlayersPerTeam = 0 + self._minPlayersPerTeam = 0 + self._duration = 0 + self._startDelay = base.config.GetFloat('party-team-activity-start-delay', startDelay) + self._willBalanceTeams = False # balanceTeams + self._currentStatus = '' + return + + def load(self): + DistributedPartyActivity.load(self) + self.teamActivityGui = TeamActivityGui(self) + self.activityFSM = TeamActivityFSM(self) + + def unload(self): + DistributedPartyActivity.unload(self) + del self.activityFSM + self.teamActivityGui.unload() + del self.teamActivityGui + if hasattr(self, 'toonIds'): + del self.toonIds + del self.isLocalToonPlaying + del self.localToonTeam + del self.advantage + del self.waitToStartTimestamp + + def handleToonShifted(self, toonId): + self.notify.error('handleToonShifted( toonId=%d ) must be overriden by child class.' % toonId) + + def handleToonSwitchedTeams(self, toonId): + if toonId == base.localAvatar.doId and self._canSwitchTeams: + if self.isState('WaitForEnough') or self.isState('WaitToStart'): + self.teamActivityGui.enableExitButton() + self.teamActivityGui.enableSwitchButton() + + def handleToonDisabled(self, toonId): + self.notify.error('handleToonDisabled( toonId=%d ) must be overriden by child class.' % toonId) + + def setPlayersPerTeam(self, min, max): + self._minPlayersPerTeam = min + self._maxPlayersPerTeam = max + + def setDuration(self, duration): + self._duration = duration + + def setCanSwitchTeams(self, canSwitchTeams): + self._canSwitchTeams = canSwitchTeams + + def d_toonJoinRequest(self, team): + if self.isLocalToonInActivity(): + return + if self.activityFSM.state in ['WaitForEnough', 'WaitToStart'] and self._localToonRequestStatus is None: + base.cr.playGame.getPlace().fsm.request('activity') + self.localToonJoining() + self.sendUpdate('toonJoinRequest', [team]) + return + + def d_toonExitRequest(self): + toonId = base.localAvatar.doId + team = self.getTeam(toonId) + if team is not None: + if self._localToonRequestStatus is None: + self.localToonExiting() + self.sendUpdate('toonExitRequest', [team]) + else: + self.notify.warning('Not sending exitRequest as localToon has no team.') + return + + def joinRequestDenied(self, reason): + DistributedPartyActivity.joinRequestDenied(self, reason) + self.notify.debug('joinRequestDenied') + if reason == PartyGlobals.DenialReasons.Full: + self.showMessage(TTLocalizer.PartyTeamActivityTeamFull) + elif reason == PartyGlobals.DenialReasons.Default: + self.showMessage(TTLocalizer.PartyTeamActivityJoinDenied % self.getTitle()) + + def exitRequestDenied(self, reason): + DistributedPartyActivity.exitRequestDenied(self, reason) + if reason == PartyGlobals.DenialReasons.Default: + self.showMessage(TTLocalizer.PartyTeamActivityExitDenied % self.getTitle()) + if self.isLocalToonPlaying and (self.isState('WaitToStart') or self.isState('WaitForEnough')): + self.teamActivityGui.enableExitButton() + if self._canSwitchTeams: + self.teamActivityGui.enableSwitchButton() + + def d_toonSwitchTeamRequest(self): + if not self._canSwitchTeams: + return + self.sendUpdate('toonSwitchTeamRequest') + + def switchTeamRequestDenied(self, reason): + self.notify.debug('switchTeamRequestDenied') + if reason == PartyGlobals.DenialReasons.Full: + self.showMessage(TTLocalizer.PartyTeamActivityTeamFull, endState='activity') + elif reason == PartyGlobals.DenialReasons.Default: + self.showMessage(TTLocalizer.PartyTeamActivitySwitchDenied, endState='activity') + if self.isLocalToonPlaying and (self.isState('WaitToStart') or self.isState('WaitForEnough')) and self._canSwitchTeams: + self.teamActivityGui.enableSwitchButton() + + def setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds): + newToonIds = [leftTeamToonIds, rightTeamToonIds] + exitedToons, joinedToons, shifters, switchers = self.getToonsPlayingChanges(self.toonIds, newToonIds) + if base.localAvatar.doId in exitedToons: + self.localToonExiting() + self._processExitedToons(exitedToons) + self.setToonIds([leftTeamToonIds, rightTeamToonIds]) + self._processJoinedToons(joinedToons) + for toonId in shifters: + self.handleToonShifted(toonId) + + if self._canSwitchTeams: + for toonId in switchers: + self.handleToonSwitchedTeams(toonId) + + if self.isState('WaitForEnough'): + self._updateWaitForEnoughStatus() + + def _updateWaitForEnoughStatus(self): + amount = self.getNumToonsNeededToStart() + if self._willBalanceTeams: + text = TTLocalizer.PartyTeamActivityForMoreWithBalance + else: + text = TTLocalizer.PartyTeamActivityForMore + if amount > 1: + plural = TTLocalizer.PartyTeamActivityForMorePlural + else: + plural = '' + self.setStatus(text % (amount, plural)) + + def setState(self, newState, timestamp, data): + DistributedPartyActivity.setState(self, newState, timestamp) + if newState == 'WaitToStart': + self.activityFSM.request(newState, timestamp) + elif newState == 'Conclusion': + self.activityFSM.request(newState, data) + else: + self.activityFSM.request(newState) + + def d_toonReady(self): + self.sendUpdate('toonReady') + + def setAdvantage(self, advantage): + self.advantage = advantage + + def handleToonJoined(self, toonId): + if toonId == base.localAvatar.doId: + self.isLocalToonPlaying = True + self.localToonTeam = self.getTeam(base.localAvatar.doId) + self.teamActivityGui.load() + self.teamActivityGui.enableExitButton() + if self._canSwitchTeams: + self.teamActivityGui.enableSwitchButton() + if self.activityFSM.state == 'WaitToStart': + self.showWaitToStartCountdown() + else: + self.showStatus() + + def handleToonExited(self, toonId): + if toonId == base.localAvatar.doId: + self.hideStatus() + self.teamActivityGui.disableExitButton() + if self._canSwitchTeams: + self.teamActivityGui.disableSwitchButton() + if self.activityFSM.state == 'WaitToStart': + self.hideWaitToStartCountdown() + self.teamActivityGui.unload() + self.isLocalToonPlaying = False + self.localToonTeam = None + return + + def handleRulesDone(self): + self.notify.debug('handleRulesDone') + self.d_toonReady() + self.activityFSM.request('WaitForServer') + + def handleGameTimerExpired(self): + pass + + def getToonsPlayingChanges(self, oldToonIds, newToonIds): + oldLeftTeam = oldToonIds[0] + oldRightTeam = oldToonIds[1] + newLeftTeam = newToonIds[0] + newRightTeam = newToonIds[1] + oldToons = set(oldLeftTeam + oldRightTeam) + newToons = set(newLeftTeam + newRightTeam) + exitedToons = oldToons.difference(newToons) + joinedToons = newToons.difference(oldToons) + shifters = [] + if self._canSwitchTeams: + switchers = list(set(oldLeftTeam) & set(newRightTeam)) + list(set(oldRightTeam) & set(newLeftTeam)) + else: + switchers = [] + for i in range(len(PartyGlobals.TeamActivityTeams)): + persistentToons = set(oldToonIds[i]) & set(newToonIds[i]) + for toonId in persistentToons: + if oldToonIds[i].index(toonId) != newToonIds[i].index(toonId): + shifters.append(toonId) + + return (list(exitedToons), + list(joinedToons), + shifters, + switchers) + + def getMaxPlayersPerTeam(self): + return self._maxPlayersPerTeam + + def getMinPlayersPerTeam(self): + return self._minPlayersPerTeam + + def getNumToonsNeededToStart(self): + if self._willBalanceTeams: + return abs(self._minPlayersPerTeam * 2 - self.getNumToonsPlaying()) + else: + return self._minPlayersPerTeam + + def getToonIdsAsList(self): + return self.toonIds[0] + self.toonIds[1] + + def getNumToonsPlaying(self): + return len(self.toonIds[0]) + len(self.toonIds[1]) + + def getNumToonsInTeam(self, team): + return len(self.toonIds[team]) + + def getTeam(self, toonId): + for i in range(len(PartyGlobals.TeamActivityTeams)): + if self.toonIds[i].count(toonId) > 0: + return i + else: + return None + + return None + + def getIndex(self, toonId, team): + if self.toonIds[team].count(toonId) > 0: + return self.toonIds[team].index(toonId) + else: + return None + return None + + def _joinLeftTeam(self, collEntry): + if self.isLocalToonInActivity(): + return + self.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.LeftTeam) + + def _joinRightTeam(self, collEntry): + if self.isLocalToonInActivity(): + return + self.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.RightTeam) + + def showWaitToStartCountdown(self): + if self.waitToStartTimestamp is None: + self.notify.warning('showWaitToStartCountdown was called when self.waitToStartTimestamp was None') + return + self.teamActivityGui.showWaitToStartCountdown(self._startDelay, self.waitToStartTimestamp, almostDoneCallback=self._onCountdownAlmostDone) + self.showStatus() + self.teamActivityGui.enableExitButton() + return + + def _onCountdownAlmostDone(self): + if self._canSwitchTeams: + self.teamActivityGui.disableSwitchButton() + + def hideWaitToStartCountdown(self): + self.teamActivityGui.hideWaitToStartCountdown() + self.teamActivityGui.disableExitButton() + self.hideStatus() + + def setStatus(self, text): + self._currentStatus = text + if self.isLocalToonPlaying: + self.showStatus() + + def showStatus(self): + if self.teamActivityGui is not None: + self.teamActivityGui.showStatus(self._currentStatus) + return + + def hideStatus(self): + if self.teamActivityGui is not None: + self.teamActivityGui.hideStatus() + return + + def toonsCanSwitchTeams(self): + return self._canSwitchTeams + + def isState(self, state): + return hasattr(self, 'activityFSM') and self.activityFSM.getCurrentOrNextState() == state + + def startWaitForEnough(self): + self.notify.debug('startWaitForEnough') + self.advantage = 1.0 + self._updateWaitForEnoughStatus() + if self.isLocalToonPlaying: + self.teamActivityGui.enableExitButton() + if self._canSwitchTeams: + self.teamActivityGui.enableSwitchButton() + + def finishWaitForEnough(self): + self.notify.debug('finishWaitForEnough') + + def startWaitToStart(self, waitStartTimestamp): + self.notify.debug('startWaitToStart') + self.waitToStartTimestamp = globalClockDelta.networkToLocalTime(waitStartTimestamp) + self._updateWaitForEnoughStatus() + self.setStatus(TTLocalizer.PartyTeamActivityWaitingToStart) + if self.isLocalToonPlaying: + self.showWaitToStartCountdown() + + def finishWaitToStart(self): + self.notify.debug('finishWaitToStart') + if self.isLocalToonPlaying: + self.hideWaitToStartCountdown() + if self._canSwitchTeams: + self.teamActivityGui.disableSwitchButton() + self.waitToStartTimestamp = None + return + + def startRules(self): + self.notify.debug('startRules') + if self.isLocalToonPlaying: + DistributedPartyActivity.startRules(self) + + def finishRules(self): + self.notify.debug('finishRules') + if self.isLocalToonPlaying: + DistributedPartyActivity.finishRules(self) + if self.activityFSM.getCurrentOrNextState() == 'WaitForEnough': + DistributedPartyActivity.finishRules(self) + + def startWaitForServer(self): + self.notify.debug('startWaitForServer') + self.setStatus(TTLocalizer.PartyTeamActivityWaitingForOtherPlayers) + + def finishWaitForServer(self): + self.notify.debug('finishWaitForServer') + if self.isLocalToonPlaying: + self.hideStatus() + + def startActive(self): + self.notify.debug('startActive') + if self.isLocalToonPlaying: + self.hideStatus() + self.teamActivityGui.showTimer(self._duration) + + def finishActive(self): + self.notify.debug('finishActive') + if self.isLocalToonPlaying: + self.teamActivityGui.hideTimer() + self.hideStatus() + + def startConclusion(self, data): + self.notify.debug('startConclusion') + self.setStatus('') + if self.isLocalToonPlaying: + self.localToonExiting() + + def finishConclusion(self): + self.notify.debug('finishConclusion') + if self.isLocalToonPlaying: + self.hideStatus() diff --git a/toontown/parties/DistributedPartyTeamActivityAI.py b/toontown/parties/DistributedPartyTeamActivityAI.py new file mode 100755 index 00000000..abdfc6de --- /dev/null +++ b/toontown/parties/DistributedPartyTeamActivityAI.py @@ -0,0 +1,216 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import globalClockDelta + +from DistributedPartyActivityAI import DistributedPartyActivityAI +from activityFSMs import TeamActivityAIFSM +import PartyGlobals + +''' +dclass DistributedPartyTeamActivity : DistributedPartyActivity { + toonJoinRequest(uint8(0-1)) airecv clsend; + toonExitRequest(uint8(0-1)) airecv clsend; + toonSwitchTeamRequest() airecv clsend; + setPlayersPerTeam(uint8, uint8) broadcast required; + setDuration(uint8) broadcast required; + setCanSwitchTeams(bool) broadcast required; + setState(string, int16, uint32) broadcast ram; + setToonsPlaying(uint32 [0-8], uint32 [0-8]) required broadcast ram; + setAdvantage(uint16/100); + switchTeamRequestDenied(uint8); +}; +''' + +'''self.defaultTransitions = {'WaitForEnough': ['WaitToStart'], + 'WaitToStart': ['WaitForEnough', 'WaitClientsReady'], + 'WaitClientsReady': ['WaitForEnough', 'Active'], + 'Active': ['WaitForEnough', 'Conclusion'], + 'Conclusion': ['WaitForEnough']}''' + +class DistributedPartyTeamActivityAI(DistributedPartyActivityAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyTeamActivityAI") + forbidTeamChanges = False + startDelay = PartyGlobals.TeamActivityStartDelay + + def __init__(self, air, parent, activityTuple): + self.toonIds = ([], []) + self.responses = set() + self.fsm = TeamActivityAIFSM(self) + + DistributedPartyActivityAI.__init__(self, air, parent, activityTuple) + + def announceGenerate(self): + self.b_setState('WaitForEnough') + DistributedPartyActivityAI.announceGenerate(self) + + def toonJoinRequest(self, team): + av = self._getCaller() + if not av: + return + + if not self.fsm.state in ('WaitForEnough', 'WaitToStart'): + self.sendUpdateToAvatarId(av.doId, 'joinTeamRequestDenied', [PartyGlobals.DenialReasons.Default]) + return + + if len(self.toonIds[team]) >= self.getPlayersPerTeam()[1]: + self.sendUpdateToAvatarId(av.doId, 'joinTeamRequestDenied', [PartyGlobals.DenialReasons.Full]) + return + + if av.doId in self.toonsPlaying: + self.air.writeServerEvent('suspicious', av.doId, 'tried to join party team activity again!') + self.sendUpdateToAvatarId(av.doId, 'joinTeamRequestDenied', [PartyGlobals.DenialReasons.Default]) + return + + # idgaf if they exit unexpectedly in this case + self.toonIds[team].append(av.doId) + DistributedPartyActivityAI.toonJoinRequest(self) + self.__update() + + def toonExitRequest(self, team): + av = self._getCaller() + if not av: + return + + if not self.fsm.state in ('WaitForEnough', 'WaitToStart'): + self.sendUpdateToAvatarId(av.doId, 'exitRequestDenied', [PartyGlobals.DenialReasons.Default]) + return + + if not (av.doId in self.toonIds[0] or av.doId in self.toonIds[1]): + self.air.writeServerEvent('suspicious', avId, 'tried to switch DistributedPartyActivityAI team, but not in one') + self.sendUpdateToAvatarId(av.doId, 'exitRequestDenied', [PartyGlobals.DenialReasons.Default]) + return + + currentTeam = (1, 0)[av.doId in self.toonIds[0]] + self.toonIds[currentTeam].remove(av.doId) + + DistributedPartyActivityAI.toonExitRequest(self) + self.__update() + + def toonSwitchTeamRequest(self): + av = self._getCaller() + if not av: + return + + if not self.getCanSwitchTeams(): + self.air.writeServerEvent('suspicious', avId, 'tried to switch DistributedPartyActivityAI team in bad time') + self.sendUpdateToAvatarId(av.doId, 'switchTeamRequestDenied', [PartyGlobals.DenialReasons.Default]) + return + + if not (av.doId in self.toonIds[0] or av.doId in self.toonIds[1]): + self.air.writeServerEvent('suspicious', avId, 'tried to switch DistributedPartyActivityAI team, but not in one') + self.sendUpdateToAvatarId(av.doId, 'switchTeamRequestDenied', [PartyGlobals.DenialReasons.Default]) + return + + currentTeam = (1, 0)[av.doId in self.toonIds[0]] + otherTeam = (1, 0)[currentTeam] + + if len(self.toonIds[otherTeam]) >= self.getPlayersPerTeam()[1]: + self.sendUpdateToAvatarId(av.doId, 'switchTeamRequestDenied', [PartyGlobals.DenialReasons.Full]) + return + + self.toonIds[currentTeam].remove(av.doId) + self.toonIds[otherTeam].append(av.doId) + + self.__update() + + def getPlayersPerTeam(self): + return (PartyGlobals.CogActivityMinPlayersPerTeam, + PartyGlobals.CogActivityMaxPlayersPerTeam) + + def __areTeamsCorrect(self): + minPlayers = self.getPlayersPerTeam()[0] + return all(len(self.toonIds[i]) >= minPlayers for i in xrange(2)) + + def getDuration(self): + raise NotImplementedError('getDuration() -- pure virtual') + + def getCanSwitchTeams(self): + return self.fsm.state in ('Off', 'WaitForEnough', 'WaitToStart') and not self.forbidTeamChanges + + def updateToonsPlaying(self): + self.sendUpdate('setToonsPlaying', self.getToonsPlaying()) + + def getToonsPlaying(self): + return self.toonIds + + def setAdvantage(self, todo0): + pass + + def b_setState(self, state, data=0): + self.fsm.request(state, data) + self.d_setState(state, data) + + def d_setState(self, state, data=0): + self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime(), data]) + + def _getCaller(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.air.doId2do: + self.air.writeServerEvent('suspicious', avId, 'called some DistributedPartyActivityAI method outside shard') + return None + + return self.air.doId2do[avId] + + def __update(self): + self.updateToonsPlaying() + + if self.fsm.state == 'WaitForEnough': + if self.__areTeamsCorrect(): + self.b_setState('WaitToStart') + + elif self.fsm.state == 'WaitToStart': + if not self.__areTeamsCorrect(): + self.b_setState('WaitForEnough') + + def startWaitForEnough(self, data): + pass + + def finishWaitForEnough(self): + pass + + def startWaitToStart(self, data): + def advance(task): + self.fsm.request('WaitClientsReady') + self.d_setState('Rules') + return task.done + + taskMgr.doMethodLater(self.startDelay, advance, self.taskName('dostart')) + + def finishWaitToStart(self): + taskMgr.remove(self.taskName('dostart')) + + def __doStart(self, task = None): + self.b_setState('Active') + if task: return task.done + + def startWaitClientsReady(self): + self.responses = set() + taskMgr.doMethodLater(15, self.__doStart, self.taskName('clientready')) + + def finishWaitClientsReady(self): + taskMgr.remove(self.taskName('clientready')) + + def toonReady(self): + self.responses.add(self.air.getAvatarIdFromSender()) + if self.responses == set(self.toonsPlaying): + self.__doStart() + + def startActive(self, data): + taskMgr.doMethodLater(self.getDuration(), self.__finish, self.taskName('finish')) + + def finishActive(self): + taskMgr.remove(self.taskName('finish')) + + def __finish(self, task): + self.calcReward() + self.b_setState('Conclusion') + return task.done + + def calcReward(self): + raise NotImplementedError('calcReward() -- pure virtual') + + def startConclusion(self, data): + raise NotImplementedError('startConclusion() -- pure virtual') + + def finishConclusion(self): + raise NotImplementedError('finishConclusion() -- pure virtual') + \ No newline at end of file diff --git a/toontown/parties/DistributedPartyTrampolineActivity.py b/toontown/parties/DistributedPartyTrampolineActivity.py new file mode 100755 index 00000000..c4d9cc7b --- /dev/null +++ b/toontown/parties/DistributedPartyTrampolineActivity.py @@ -0,0 +1,674 @@ +import math +import time +import random +from direct.task import Task +from direct.directnotify import DirectNotifyGlobal +from direct.actor.Actor import Actor +from direct.fsm.FSM import FSM +from direct.gui.DirectGui import DirectButton +from direct.gui.OnscreenText import OnscreenText +from direct.interval.FunctionInterval import Func +from direct.interval.FunctionInterval import Wait +from direct.interval.LerpInterval import LerpFunc +from direct.interval.MetaInterval import Parallel +from direct.interval.MetaInterval import Sequence +from pandac.PandaModules import CardMaker +from pandac.PandaModules import NodePath +from pandac.PandaModules import TextNode +from pandac.PandaModules import Point3 +from pandac.PandaModules import Vec3 +from pandac.PandaModules import VBase3 +from pandac.PandaModules import VBase4 +from pandac.PandaModules import CollisionSphere +from pandac.PandaModules import CollisionTube +from pandac.PandaModules import CollisionNode +from pandac.PandaModules import BitMask32 +from otp.otpbase import OTPGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyActivity import DistributedPartyActivity +from toontown.parties.activityFSMs import TrampolineActivityFSM +from toontown.parties import PartyUtils + +class DistributedPartyTrampolineActivity(DistributedPartyActivity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyTrampolineActivity') + + def __init__(self, cr, doJellyBeans = True, doTricks = False, texture = None): + DistributedPartyTrampolineActivity.notify.debug('__init__') + DistributedPartyActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyTrampoline, PartyGlobals.ActivityTypes.GuestInitiated, wantLever=False, wantRewardGui=True) + self.doJellyBeans = doJellyBeans + self.doTricks = doTricks + self.texture = texture + self.toon = None + self.trampHeight = 3.6 + self.trampK = 400.0 + self.normalTrampB = 2.5 + self.leavingTrampB = 8.0 + self.trampB = self.normalTrampB + self.g = -32.0 + self.jumpBoost = 330.0 + self.beginningBoost = 500.0 + self.beginningBoostThreshold = self.trampHeight + 1.5 + self.earlyJumpThreshold = 75.0 + self.boingThreshold = 300.0 + self.turnFactor = 120.0 + self.stepDT = 0.001 + self.targetCameraPos = Point3(0.0, 40.0, 10.0) + self.cameraSpeed = 2.0 + self.hopOffPos = Point3(16.0, 0.0, 0.0) + self.indicatorFactor = 0.0095 + self.dropShadowCutoff = 15.0 + self.minHeightForText = 15.0 + self.heightTextOffset = -0.065 + self.beanOffset = 0.5 + self.guiBeanOffset = -0.02 + self.jumpTextShown = False + self.toonJumped = False + self.turnLeft = False + self.turnRight = False + self.leavingTrampoline = False + self.toonVelocity = 0.0 + self.topHeight = 0.0 + self.lastPeak = 0.0 + self.beginRoundInterval = None + self.hopOnAnim = None + self.hopOffAnim = None + self.flashTextInterval = None + self.numJellyBeans = PartyGlobals.TrampolineNumJellyBeans + self.jellyBeanBonus = PartyGlobals.TrampolineJellyBeanBonus + self.jellyBeanStartHeight = 20.0 + self.jellyBeanStopHeight = 90.0 + self.jellyBeanColors = [VBase4(1.0, 0.5, 0.5, 1.0), + VBase4(0.5, 1.0, 0.5, 1.0), + VBase4(0.5, 1.0, 1.0, 1.0), + VBase4(1.0, 1.0, 0.4, 1.0), + VBase4(0.4, 0.4, 1.0, 1.0), + VBase4(1.0, 0.5, 1.0, 1.0)] + delta = (self.jellyBeanStopHeight - self.jellyBeanStartHeight) / (self.numJellyBeans - 1) + self.jellyBeanPositions = [ self.jellyBeanStartHeight + n * delta for n in xrange(self.numJellyBeans) ] + self.doSimulateStep = False + return + + def load(self): + DistributedPartyTrampolineActivity.notify.debug('load') + DistributedPartyActivity.load(self) + self.loadModels() + self.loadCollision() + self.loadGUI() + self.loadSounds() + self.loadIntervals() + self.activityFSM = TrampolineActivityFSM(self) + self.activityFSM.request('Idle') + self.animFSM = TrampolineAnimFSM(self) + self.setBestHeightInfo('', 0) + + def loadModels(self): + self.tramp = self.root.attachNewNode(self.uniqueName('tramp')) + self.trampActor = Actor('phase_13/models/parties/trampoline_model', {'emptyAnim': 'phase_13/models/parties/trampoline_anim'}) + self.trampActor.reparentTo(self.tramp) + if self.texture: + reskinNode = self.tramp.find('**/trampoline/__Actor_modelRoot/-GeomNode') + reskinNode.setTexture(loader.loadTexture(self.texture), 100) + self.surface = NodePath(self.uniqueName('trampSurface')) + self.surface.reparentTo(self.tramp) + self.surface.setZ(self.trampHeight) + self.trampActor.controlJoint(self.surface, 'modelRoot', 'trampoline_joint1') + self.sign.setPos(PartyGlobals.TrampolineSignOffset) + self.beans = [ loader.loadModelCopy('phase_4/models/props/jellybean4') for i in xrange(self.numJellyBeans) ] + for bean in self.beans: + bean.find('**/jellybean').setP(-35.0) + bean.setScale(3.0) + bean.setTransparency(True) + bean.reparentTo(self.tramp) + bean.stash() + + self.beans[-1].setScale(8.0) + + def loadCollision(self): + collTube = CollisionTube(0.0, 0.0, 0.0, 0.0, 0.0, 6.0, 5.4) + collTube.setTangible(True) + self.trampolineCollision = CollisionNode(self.uniqueName('TrampolineCollision')) + self.trampolineCollision.addSolid(collTube) + self.trampolineCollision.setCollideMask(OTPGlobals.CameraBitmask | OTPGlobals.WallBitmask) + self.trampolineCollisionNP = self.tramp.attachNewNode(self.trampolineCollision) + collSphere = CollisionSphere(0.0, 0.0, 0.0, 7.0) + collSphere.setTangible(False) + self.trampolineTrigger = CollisionNode(self.uniqueName('TrampolineTrigger')) + self.trampolineTrigger.addSolid(collSphere) + self.trampolineTrigger.setIntoCollideMask(OTPGlobals.WallBitmask) + self.trampolineTriggerNP = self.tramp.attachNewNode(self.trampolineTrigger) + self.accept('enter%s' % self.uniqueName('TrampolineTrigger'), self.onTrampolineTrigger) + + def loadGUI(self): + self.gui = loader.loadModel('phase_13/models/parties/trampolineGUI') + self.gui.reparentTo(base.a2dTopLeft) + self.gui.setPos(0.115, 0, -1) + self.gui.hide() + self.toonIndicator = self.gui.find('**/trampolineGUI_MovingBar') + jumpLineLocator = self.gui.find('**/jumpLine_locator') + guiBean = self.gui.find('**/trampolineGUI_GreenJellyBean') + self.gui.find('**/trampolineGUI_GreenJellyBean').stash() + self.guiBeans = [ guiBean.instanceUnderNode(jumpLineLocator, self.uniqueName('guiBean%d' % i)) for i in xrange(self.numJellyBeans) ] + self.guiBeans[-1].setScale(1.5) + heightTextNode = TextNode(self.uniqueName('TrampolineActivity.heightTextNode')) + heightTextNode.setFont(ToontownGlobals.getSignFont()) + heightTextNode.setAlign(TextNode.ALeft) + heightTextNode.setText('0.0') + heightTextNode.setShadow(0.05, 0.05) + heightTextNode.setShadowColor(0.0, 0.0, 0.0, 1.0) + heightTextNode.setTextColor(1.0, 1.0, 1.0, 1.0) + self.heightText = jumpLineLocator.attachNewNode(heightTextNode) + self.heightText.setX(0.15) + self.heightText.setScale(0.1) + self.heightText.setAlphaScale(0.0) + self.quitEarlyButtonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + quitEarlyUp = self.quitEarlyButtonModels.find('**//InventoryButtonUp') + quitEarlyDown = self.quitEarlyButtonModels.find('**/InventoryButtonDown') + quitEarlyRollover = self.quitEarlyButtonModels.find('**/InventoryButtonRollover') + self.quitEarlyButton = DirectButton(parent=base.a2dTopRight, relief=None, text=TTLocalizer.PartyTrampolineQuitEarlyButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.7, image=(quitEarlyUp, quitEarlyDown, quitEarlyRollover), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(-0.183, 0, -0.4), scale=0.09, command=self.leaveTrampoline) + self.quitEarlyButton.stash() + self.flashText = OnscreenText(text='', pos=(0.0, -0.45), scale=0.2, fg=(1.0, 1.0, 0.65, 1.0), align=TextNode.ACenter, font=ToontownGlobals.getSignFont(), mayChange=True) + self.timer = PartyUtils.getNewToontownTimer() + self.timer.posInTopRightCorner() + return + + def loadSounds(self): + self.jellyBeanSound = base.loadSfx('phase_4/audio/sfx/sparkly.ogg') + self.boingSound = base.loadSfx('phase_4/audio/sfx/target_trampoline_2.ogg') + self.whistleSound = base.loadSfx('phase_4/audio/sfx/AA_sound_whistle.ogg') + + def loadIntervals(self): + + def prepareHeightText(): + self.heightText.node().setText(TTLocalizer.PartyTrampolineGetHeight % int(self.toon.getZ())) + self.heightText.setZ(self.indicatorFactor * self.toon.getZ() + self.heightTextOffset) + + self.heightTextInterval = Sequence(Func(prepareHeightText), LerpFunc(self.heightText.setAlphaScale, fromData=1.0, toData=0.0, duration=1.0)) + + def unload(self): + DistributedPartyTrampolineActivity.notify.debug('unload') + if self.hopOnAnim and self.hopOnAnim.isPlaying(): + self.hopOnAnim.finish() + if self.hopOffAnim and self.hopOffAnim.isPlaying(): + self.hopOffAnim.finish() + if self.beginRoundInterval and self.beginRoundInterval.isPlaying(): + self.beginRoundInterval.finish() + if self.flashTextInterval and self.flashTextInterval.isPlaying(): + self.flashTextInterval.finish() + if self.heightTextInterval and self.heightTextInterval.isPlaying(): + self.heightTextInterval.finish() + self.timer.stop() + DistributedPartyActivity.unload(self) + taskMgr.remove(self.uniqueName('TrampolineActivity.updateTask')) + taskMgr.remove(self.uniqueName('TrampolineActivity.remoteUpdateTask')) + self.ignoreAll() + del self.heightTextInterval + del self.beginRoundInterval + del self.hopOnAnim + del self.hopOffAnim + del self.flashTextInterval + if hasattr(self, 'beanAnims'): + self.cleanupJellyBeans() + self.quitEarlyButton.destroy() + del self.quitEarlyButton + del self.gui + del self.activityFSM + del self.animFSM + return + + def setBestHeightInfo(self, toonName, height): + self.bestHeightInfo = (toonName, height) + DistributedPartyTrampolineActivity.notify.debug('%s has the best height of %d' % (toonName, height)) + if height > 0: + self.setSignNote(TTLocalizer.PartyTrampolineBestHeight % self.bestHeightInfo) + else: + self.setSignNote(TTLocalizer.PartyTrampolineNoHeightYet) + + def leaveTrampoline(self): + if self.toon != None and self.toon.doId == base.localAvatar.doId: + self._showFlashMessage(TTLocalizer.PartyTrampolineTimesUp) + self.leavingTrampoline = True + self.timer.reset() + self.trampB = self.leavingTrampB + self.ignore('control') + self.quitEarlyButton.stash() + self.gui.hide() + return + + def requestAnim(self, request): + self.animFSM.request(request) + + def b_requestAnim(self, request): + self.requestAnim(request) + self.sendUpdate('requestAnim', [request]) + + def requestAnimEcho(self, request): + if self.toon != None and self.toon.doId != base.localAvatar.doId: + self.requestAnim(request) + return + + def removeBeans(self, beansToRemove): + for i in beansToRemove: + height, bean, guiBean, beanAnim = self.beanDetails[i] + guiBean.stash() + if i in self.beansToCollect: + self.beansToCollect.remove(i) + else: + self.notify.warning('removeBeans avoided a crash, %d not in self.beansToCollect' % i) + self.poofBean(bean, beanAnim) + + def b_removeBeans(self, beansToRemove): + self.removeBeans(beansToRemove) + self.sendUpdate('removeBeans', [beansToRemove]) + + def removeBeansEcho(self, beansToRemove): + if self.toon != None and self.toon.doId != base.localAvatar.doId: + self.removeBeans(beansToRemove) + return + + def joinRequestDenied(self, reason): + DistributedPartyActivity.joinRequestDenied(self, reason) + self.showMessage(TTLocalizer.PartyActivityDefaultJoinDeny) + base.cr.playGame.getPlace().fsm.request('walk') + + def exitRequestDenied(self, reason): + DistributedPartyActivity.exitRequestDenied(self, reason) + self.showMessage(TTLocalizer.PartyActivityDefaultExitDeny) + + def setState(self, newState, timestamp): + DistributedPartyTrampolineActivity.notify.debug('setState( newState=%s, ... )' % newState) + DistributedPartyActivity.setState(self, newState, timestamp) + self.activityFSM.request(newState) + + def startIdle(self): + DistributedPartyTrampolineActivity.notify.debug('startIdle') + + def finishIdle(self): + DistributedPartyTrampolineActivity.notify.debug('finishIdle') + + def startRules(self): + DistributedPartyTrampolineActivity.notify.debug('startRules') + if self.doJellyBeans: + self.setupJellyBeans() + if self.toon != None and self.toon.doId == base.localAvatar.doId: + self.acquireToon() + return + + def startActive(self): + DistributedPartyTrampolineActivity.notify.debug('startActive') + if self.toon != None and self.toon.doId == base.localAvatar.doId: + base.setCellsAvailable(base.bottomCells, True) + self.accept('arrow_left', self.onLeft) + self.accept('arrow_left-up', self.onLeftUp) + self.accept('arrow_right', self.onRight) + self.accept('arrow_right-up', self.onRightUp) + self.beginRoundInterval = Sequence(Func(self._showFlashMessage, TTLocalizer.PartyTrampolineReady), Wait(1.2), Func(self.flashMessage, TTLocalizer.PartyTrampolineGo), Func(self.beginRound)) + self.beginRoundInterval.start() + return + + def finishActive(self): + DistributedPartyTrampolineActivity.notify.debug('finishActive') + if self.doJellyBeans: + self.cleanupJellyBeans() + + def setupJellyBeans(self): + self.beanAnims = [] + self.beansToCollect = [] + self.beanDetails = [] + self.numBeansCollected = 0 + for i in xrange(self.numJellyBeans): + bean = self.beans[i] + guiBean = self.guiBeans[i] + height = self.jellyBeanPositions[i] + color = random.choice(self.jellyBeanColors) + bean.find('**/jellybean').setColor(color) + if self.toon.doId == base.localAvatar.doId: + bean.setAlphaScale(1.0) + else: + bean.setAlphaScale(0.5) + guiBean.setColor(color) + bean.setZ(height + self.toon.getHeight() + self.beanOffset) + guiBean.setZ(height * self.indicatorFactor + self.guiBeanOffset) + bean.setH(0.0) + bean.unstash() + guiBean.unstash() + beanAnim = bean.hprInterval(1.5, VBase3((i % 2 * 2 - 1) * 360.0, 0.0, 0.0)) + beanAnim.loop() + self.beanAnims.append(beanAnim) + self.beanDetails.append((height, + bean, + guiBean, + beanAnim)) + + self.beansToCollect = range(self.numJellyBeans) + + def cleanupJellyBeans(self): + for bean in self.beans: + bean.stash() + + for guiBean in self.guiBeans: + guiBean.stash() + + if hasattr(self, 'beanAnims'): + for beanAnim in self.beanAnims: + beanAnim.finish() + + del self.beanAnims + del self.beansToCollect + + def beginRound(self): + base.playSfx(self.whistleSound) + self.timer.setTime(PartyGlobals.TrampolineDuration) + self.timer.countdown(PartyGlobals.TrampolineDuration) + self.timer.show() + self.gui.show() + self.quitEarlyButton.unstash() + self.notify.debug('Accepting contorl') + self.accept('control', self.onJump) + self.notify.debug('setting simulate step to true') + self.doSimulateStep = True + + def acquireToon(self): + self.toon.disableSmartCameraViews() + self.toon.stopUpdateSmartCamera() + camera.wrtReparentTo(render) + self.toon.dropShadow.reparentTo(hidden) + self.toon.startPosHprBroadcast(period=0.2) + self.toonAcceleration = 0.0 + self.toonVelocity = 0.0 + self.topHeight = 0.0 + self.trampB = self.normalTrampB + self.leavingTrampoline = False + self.hopOnAnim = Sequence(Func(self.toon.b_setAnimState, 'jump', 1.0), Wait(0.4), PartyUtils.arcPosInterval(0.75, self.toon, Point3(0.0, 0.0, self.trampHeight), 5.0, self.tramp), Func(self.postHopOn)) + self.hopOnAnim.start() + + def postHopOn(self): + self.toon.setH(self.toon.getH() + 90.0) + self.toon.dropShadow.reparentTo(self.surface) + self.timeLeftToSimulate = 0.0 + self.doSimulateStep = False + taskMgr.add(self.updateTask, self.uniqueName('TrampolineActivity.updateTask')) + base.setCellsAvailable(base.leftCells, False) + base.setCellsAvailable(base.bottomCells, False) + DistributedPartyActivity.startRules(self) + + def releaseToon(self): + self._hideFlashMessage() + self.ignore('arrow_left') + self.ignore('arrow_left-up') + self.ignore('arrow_right') + self.ignore('arrow_right-up') + taskMgr.remove(self.uniqueName('TrampolineActivity.updateTask')) + self.hopOffAnim = Sequence(self.toon.hprInterval(0.5, VBase3(-90.0, 0.0, 0.0), other=self.tramp), Func(self.toon.b_setAnimState, 'jump', 1.0), Func(self.toon.dropShadow.reparentTo, hidden), Wait(0.4), PartyUtils.arcPosInterval(0.75, self.toon, self.hopOffPos, 5.0, self.tramp), Func(self.postHopOff)) + self.hopOffAnim.start() + + def postHopOff(self): + base.setCellsAvailable(base.leftCells, True) + self.timer.stop() + self.timer.hide() + self.toon.dropShadow.reparentTo(self.toon.getShadowJoint()) + self.toon.dropShadow.setAlphaScale(1.0) + self.toon.dropShadow.setScale(1.0) + self.b_requestAnim('Off') + camera.reparentTo(base.localAvatar) + base.localAvatar.startUpdateSmartCamera() + base.localAvatar.enableSmartCameraViews() + base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex) + place = base.cr.playGame.getPlace() + if self.doJellyBeans: + self.sendUpdate('awardBeans', [self.numBeansCollected, int(self.topHeight)]) + if int(self.topHeight) > self.bestHeightInfo[1]: + self.sendUpdate('reportHeightInformation', [int(self.topHeight)]) + self.d_toonExitDemand() + + def onTrampolineTrigger(self, collEntry): + if self.activityFSM.state == 'Idle' and self.toon == None and base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'walk': + base.cr.playGame.getPlace().fsm.request('activity') + self.d_toonJoinRequest() + else: + self.flashMessage(TTLocalizer.PartyTrampolineActivityOccupied, duration=2.0) + return + + def onJump(self): + self.notify.debug('got onJump') + if self.toon != None and self.toon.getZ() < self.trampHeight: + self.toonJumped = True + self.b_requestAnim('Jump') + else: + self.notify.debug('z is less than tramp height') + return + + def onLeft(self): + self.turnLeft = True + + def onLeftUp(self): + self.turnLeft = False + + def onRight(self): + self.turnRight = True + + def onRightUp(self): + self.turnRight = False + + def handleToonJoined(self, toonId): + DistributedPartyTrampolineActivity.notify.debug('handleToonJoined') + self.toon = self.getAvatar(toonId) + if self.toon != None and not self.toon.isEmpty(): + self.oldJumpSquatPlayRate = self.toon.getPlayRate('jump-squat') + self.oldJumpLandPlayRate = self.toon.getPlayRate('jump-land') + self.toon.setPlayRate(2.5, 'jump-squat') + self.toon.setPlayRate(2.0, 'jump-land') + self.turnLeft = False + self.turnRight = False + self.activityFSM.request('Rules') + if self.toon.doId != base.localAvatar.doId: + taskMgr.add(self.remoteUpdateTask, self.uniqueName('TrampolineActivity.remoteUpdateTask')) + else: + self.notify.warning('handleToonJoined could not get toon %d' % toonId) + return + + def handleToonExited(self, toonId): + DistributedPartyTrampolineActivity.notify.debug('handleToonExited') + if self.toon != None: + if self.toon.doId != base.localAvatar.doId: + taskMgr.remove(self.uniqueName('TrampolineActivity.remoteUpdateTask')) + self.surface.setZ(self.trampHeight) + self.toon.setPlayRate(self.oldJumpSquatPlayRate, 'jump-squat') + self.toon.setPlayRate(self.oldJumpLandPlayRate, 'jump-land') + self.toon = None + return + + def handleToonDisabled(self, toonId): + DistributedPartyTrampolineActivity.notify.debug('handleToonDisabled') + DistributedPartyTrampolineActivity.notify.debug('avatar ' + str(toonId) + ' disabled') + if base.localAvatar.doId == toonId: + self.releaseToon() + + def handleRulesDone(self): + self.sendUpdate('toonReady') + self.finishRules() + + def getTitle(self): + if self.doJellyBeans: + return TTLocalizer.PartyTrampolineJellyBeanTitle + elif self.doTricks: + return TTLocalizer.PartyTrampolineTricksTitle + else: + return DistributedPartyActivity.getTitle(self) + + def getInstructions(self): + return TTLocalizer.PartyTrampolineActivityInstructions + + def updateTask(self, task): + z = self.toon.getZ() + dt = globalClock.getDt() + if self.doSimulateStep: + self.timeLeftToSimulate += dt + while self.timeLeftToSimulate >= self.stepDT: + z, a = self.simulateStep(z) + self.timeLeftToSimulate -= self.stepDT + + self.toon.setZ(z) + if z <= self.trampHeight: + self.surface.setZ(z) + else: + self.surface.setZ(self.trampHeight) + self.toonIndicator.setZ((z - self.trampHeight) * self.indicatorFactor) + if self.turnLeft: + self.toon.setH(self.toon.getH() + self.turnFactor * dt) + if self.turnRight: + self.toon.setH(self.toon.getH() - self.turnFactor * dt) + currentPos = base.camera.getPos(self.toon) + vec = self.targetCameraPos - currentPos + newPos = currentPos + vec * (dt * self.cameraSpeed) + base.camera.setPos(self.toon, newPos) + base.camera.lookAt(self.toon) + #if z > self.trampHeight: + # heightFactor = 1.0 - min(1.0, (z - self.trampHeight) / self.dropShadowCutoff) + # self.toon.dropShadow.setAlphaScale(heightFactor) + # self.toon.dropShadow.setScale(max(0.1, heightFactor)) + #else: + # self.toon.dropShadow.setAlphaScale(1.0) + # self.toon.dropShadow.setScale(1.0) + if self.leavingTrampoline and z < self.trampHeight and abs(a) < 0.1: + self.releaseToon() + return Task.cont + + def simulateStep(self, z): + if z >= self.trampHeight: + a = self.g + self.toonJumped = False + else: + a = self.g + self.trampK * (self.trampHeight - z) - self.trampB * self.toonVelocity + if self.toonJumped: + if self.lastPeak > self.earlyJumpThreshold or self.toonVelocity >= -300000.0: + a += self.jumpBoost + if self.lastPeak < self.beginningBoostThreshold: + a += self.beginningBoost + lastVelocity = self.toonVelocity + self.toonVelocity += a * self.stepDT + if lastVelocity > 0.0 and self.toonVelocity <= 0.0: + topOfJump = True + bottomOfJump = False + elif lastVelocity < 0.0 and self.toonVelocity >= 0.0: + topOfJump = False + bottomOfJump = True + else: + topOfJump = False + bottomOfJump = False + newZ = z + self.toonVelocity * self.stepDT + if newZ > self.topHeight: + self.topHeight = newZ + if self.doJellyBeans: + self.collectJellyBeans(newZ) + if topOfJump: + self.lastPeak = newZ + if newZ >= self.minHeightForText: + self.heightTextInterval.start() + if topOfJump: + if newZ > self.trampHeight + 20.0: + self.b_requestAnim('Falling') + elif self.animFSM.state == 'Jump': + self.b_requestAnim('Falling') + if newZ <= self.trampHeight and z > self.trampHeight: + if self.animFSM.state == 'Falling': + self.b_requestAnim('Land') + elif self.animFSM.state != 'Neutral': + self.b_requestAnim('Neutral') + if bottomOfJump and a > self.boingThreshold: + base.playSfx(self.boingSound) + return (newZ, a) + + def collectJellyBeans(self, z): + beansToRemove = [] + for i in self.beansToCollect: + height = self.beanDetails[i][0] + if height <= z: + beansToRemove.append(i) + + if len(beansToRemove) > 0: + base.playSfx(self.jellyBeanSound) + self.numBeansCollected += len(beansToRemove) + self.b_removeBeans(beansToRemove) + + def remoteUpdateTask(self, task): + if self.toon != None and not self.toon.isEmpty(): + z = self.toon.getZ() + if z <= self.trampHeight: + self.surface.setZ(z) + else: + self.surface.setZ(self.trampHeight) + return Task.cont + + def poofBean(self, bean, beanAnim): + if bean == None: + self.notify.warning('poofBean, returning immediately as bean is None') + return + if bean.isEmpty(): + self.notify.warning('poofBean, returning immediately as bean is empty') + return + currentAlpha = bean.getColorScale()[3] + currentScale = bean.getScale() + poofAnim = Sequence(Parallel(LerpFunc(bean.setAlphaScale, fromData=currentAlpha, toData=0.0, duration=0.25), LerpFunc(bean.setScale, fromData=currentScale, toData=currentScale * 5.0, duration=0.25)), Func(bean.stash), Func(beanAnim.finish), Func(bean.setAlphaScale, currentAlpha), Func(bean.setScale, currentScale)) + poofAnim.start() + return + + def _showFlashMessage(self, message): + if self.isDisabled(): + return + if self.flashTextInterval is not None and self.flashTextInterval.isPlaying(): + self.flashTextInterval.finish() + self.flashText.setText(message) + self.flashText.setAlphaScale(1.0) + self.flashText.unstash() + return + + def _hideFlashMessage(self, duration = 0.0): + if self.isDisabled(): + pass + self.flashTextInterval = Sequence(Wait(duration), LerpFunc(self.flashText.setAlphaScale, fromData=1.0, toData=0.0, duration=1.0), Func(self.flashText.stash)) + self.flashTextInterval.start() + + def flashMessage(self, message, duration = 0.5): + self._showFlashMessage(message) + self._hideFlashMessage(duration) + + +class TrampolineAnimFSM(FSM): + + def __init__(self, activity): + FSM.__init__(self, self.__class__.__name__) + self.activity = activity + + def defaultFilter(self, request, args): + if request == self.state: + return None + else: + return FSM.defaultFilter(self, request, args) + return None + + def enterNeutral(self): + self.activity.toon.play('neutral') + + def exitNeutral(self): + pass + + def enterJump(self): + self.activity.toon.play('jump-squat') + + def exitJump(self): + pass + + def enterFalling(self): + self.activity.toon.loop('jump-idle') + + def exitFalling(self): + self.activity.toon.stop('jump-idle') + + def enterLand(self): + self.activity.toon.play('jump-land') + + def exitLand(self): + pass diff --git a/toontown/parties/DistributedPartyTrampolineActivityAI.py b/toontown/parties/DistributedPartyTrampolineActivityAI.py new file mode 100755 index 00000000..8f4bcc3c --- /dev/null +++ b/toontown/parties/DistributedPartyTrampolineActivityAI.py @@ -0,0 +1,142 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.fsm.FSM import FSM +from direct.task import Task +from toontown.parties.DistributedPartyActivityAI import DistributedPartyActivityAI +from toontown.toonbase import TTLocalizer +import PartyGlobals + +class DistributedPartyTrampolineActivityAI(DistributedPartyActivityAI, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyTrampolineActivityAI") + + def __init__(self, air, parent, activityTuple): + DistributedPartyActivityAI.__init__(self, air, parent, activityTuple) + FSM.__init__(self, 'DistributedPartyTrampolineActivityAI') + self.currentAv = 0 + self.record = 0 + self.jellybeans = [] + self.collected = 0 + + def generate(self): + self.demand('Idle') + + def awardBeans(self, numBeans, height): + avId = self.air.getAvatarIdFromSender() + if avId != self.currentAv: + self.air.writeServerEvent('suspicious',avId,'Tried to give beans while not using the trampoline!') + return + if self.state != 'Active': + self.air.writeServerEvent('suspicious',avId,'Toon tried to award beans while the game wasn\'t running!') + return + if numBeans != self.collected: + self.air.writeServerEvent('suspicious',avId,'Toon reported incorrect number of collected jellybeans!') + av = self.air.doId2do.get(avId, None) + if not av: + self.air.writeServerEvent('suspicious',avId,'Toon tried to award beans while not in district!') + return + reward = self.collected*2 + message = TTLocalizer.PartyTrampolineBeanResults % self.collected + if self.collected == PartyGlobals.TrampolineNumJellyBeans: + reward += PartyGlobals.TrampolineJellyBeanBonus + message = TTLocalizer.PartyTrampolineBonusBeanResults % (self.collected, PartyGlobals.TrampolineJellyBeanBonus) + message += '\n\n' + TTLocalizer.PartyTrampolineTopHeightResults % height + # TODO: Pass a msgId(?) to the client so the client can use whatever localizer it chooses. + # Ideally, we shouldn't even be passing strings that *should* be localized. + self.sendUpdateToAvatarId(avId, 'showJellybeanReward', [reward, av.getMoney(), message]) + av.addMoney(reward) + + + def reportHeightInformation(self, height): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId, None) + if not av: + self.air.writeServerEvent('suspicious',avId,'Toon tried to report height without being on the district!') + return + if height > self.record: + self.record = height + self.sendUpdate('setBestHeightInfo', [av.getName(), height]) + else: + self.air.writeServerEvent('suspicious',avId,'Toon incorrectly reported height!') + + def enterActive(self): + self.jellybeans = range(PartyGlobals.TrampolineNumJellyBeans) + taskMgr.doMethodLater(PartyGlobals.TrampolineDuration, self.sendUpdate, 'exitTrampoline%d' % self.doId, extraArgs=['leaveTrampoline', []]) + self.sendUpdate('setState', ['Active', globalClockDelta.getRealNetworkTime()]) + self.collected = 0 + + def enterIdle(self): + self.sendUpdate('setState', ['Idle', globalClockDelta.getRealNetworkTime()]) + self.currentAv = 0 + self.updateToonsPlaying() + + def enterRules(self): + self.sendUpdate('setState', ['Rules', globalClockDelta.getRealNetworkTime()]) + + def requestAnim(self, anim): + avId = self.air.getAvatarIdFromSender() + if self.state != 'Active': + self.air.writeServerEvent('suspicious',avId,'Toon tried to request an animation while not playing!') + return + if self.currentAv != avId: + self.air.writeServerEvent('suspicious',avId, 'Toon tried to request an anim for someone else!') + return + self.sendUpdate('requestAnimEcho', [anim]) + + def removeBeans(self, beans): + avId = self.air.getAvatarIdFromSender() + if self.state != 'Active': + self.air.writeServerEvent('suspicious',avId,'Toon tried to collect jellybeans while not playing!') + return + if self.currentAv != avId: + self.air.writeServerEvent('suspicious',avId, 'Toon tried to collect jellybeans while someone else was playing!') + return + for bean in beans: + if not bean in self.jellybeans: + self.air.writeServerEvent('suspicious',avId,'Toon tried to collect non-existent bean!') + beans.remove(bean) + else: + self.collected += 1 + self.sendUpdate('removeBeansEcho', [beans]) + + def updateToonsPlaying(self): + if self.currentAv == 0: + self.sendUpdate('setToonsPlaying', [[]]) + return + self.sendUpdate('setToonsPlaying', [[self.currentAv]]) + + def toonJoinRequest(self): + avId = self.air.getAvatarIdFromSender() + if self.state == 'Active': + self.sendUpdateToAvatarId(avId, 'joinRequestDenied', [1]) + return + self.currentAv = avId + self.updateToonsPlaying() + self.demand('Rules') + + def toonExitRequest(self): + avId = self.air.getAvatarIdFromSender() + if self.state != 'Active': + self.air.writeServerEvent('suspicious',avId,'Toon tried to leave a trampoline that was not running!') + return + if self.currentAv != avId: + self.air.writeServerEvent('suspicious',avId,'Toon tried to exit trampoline for someone else!') + return + taskMgr.remove('exitTrampoline'%self.doId) + self.sendUpdate('leaveTrampoline', []) + + def toonExitDemand(self): + avId = self.air.getAvatarIdFromSender() + if avId != self.currentAv: + self.air.writeServerEvent('suspicious',avId,'Toon tried to exit trampoline they\'re not using!') + return + self.demand('Idle') + + def toonReady(self): + avId = self.air.getAvatarIdFromSender() + if self.state != 'Rules': + self.air.writeServerEvent('suspicious',avId,'Toon tried to verify rules while the rules were not running!') + return + if avId != self.currentAv: + self.air.writeServerEvent('suspicious',avId,'Toon tried to verify rules for someone else!') + return + self.demand('Active') diff --git a/toontown/parties/DistributedPartyTugOfWarActivity.py b/toontown/parties/DistributedPartyTugOfWarActivity.py new file mode 100755 index 00000000..cadba1a6 --- /dev/null +++ b/toontown/parties/DistributedPartyTugOfWarActivity.py @@ -0,0 +1,697 @@ +import math +from pandac.PandaModules import CollisionTube +from pandac.PandaModules import CollisionNode +from pandac.PandaModules import Point3 +from pandac.PandaModules import VBase3 +from pandac.PandaModules import RopeNode +from direct.interval.IntervalGlobal import LerpPosHprInterval +from direct.interval.IntervalGlobal import LerpPosInterval +from direct.interval.IntervalGlobal import Wait +from direct.interval.IntervalGlobal import ActorInterval +from direct.interval.MetaInterval import Sequence +from direct.interval.MetaInterval import Parallel +from direct.interval.FunctionInterval import Func +from direct.showutil.Rope import Rope +from direct.showbase.PythonUtil import fitDestAngle2Src +from direct.fsm.StatePush import StateVar, FunctionCall +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.effects import Splash +from toontown.minigame.MinigamePowerMeter import MinigamePowerMeter +from toontown.minigame.ArrowKeys import ArrowKeys +import PartyGlobals +import PartyUtils +from DistributedPartyTeamActivity import DistributedPartyTeamActivity + +class DistributedPartyTugOfWarActivity(DistributedPartyTeamActivity): + notify = directNotify.newCategory('DistributedPartyTugOfWarActivity') + + def __init__(self, cr): + DistributedPartyTeamActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyTugOfWar, startDelay=PartyGlobals.TugOfWarStartDelay) + self.buttons = [0, 1] + self.arrowKeys = None + self.keyTTL = [] + self.idealRate = 0.0 + self.keyRate = 0 + self.allOutMode = False + self.rateMatchAward = 0.0 + self.toonIdsToStartPositions = {} + self.toonIdsToIsPullingFlags = {} + self.toonIdsToRightHands = {} + self.fallenToons = [] + self.fallenPositions = [] + self.unusedFallenPositionsIndices = [0, + 1, + 2, + 3] + self.toonIdsToAnimIntervals = {} + self.tugRopes = [] + return + + def generate(self): + DistributedPartyTeamActivity.generate(self) + self._hopOffFinishedSV = StateVar(True) + self._rewardFinishedSV = StateVar(True) + self._isWalkStateReadyFC = FunctionCall(self._testWalkStateReady, self._hopOffFinishedSV, self._rewardFinishedSV) + + def delete(self): + self._isWalkStateReadyFC.destroy() + self._hopOffFinishedSV.destroy() + self._rewardFinishedSV.destroy() + DistributedPartyTeamActivity.delete(self) + + def handleToonJoined(self, toonId): + DistributedPartyTeamActivity.handleToonJoined(self, toonId) + self.toonIdsToAnimIntervals[toonId] = None + if toonId == base.localAvatar.doId: + base.cr.playGame.getPlace().fsm.request('activity') + camera.wrtReparentTo(self.root) + self.cameraMoveIval = LerpPosHprInterval(camera, 1.5, PartyGlobals.TugOfWarCameraPos, PartyGlobals.TugOfWarCameraInitialHpr, other=self.root) + self.cameraMoveIval.start() + self.localToonPosIndex = self.getIndex(base.localAvatar.doId, self.localToonTeam) + self.notify.debug('posIndex: %d' % self.localToonPosIndex) + toon = self.getAvatar(toonId) + targetPos = self.dockPositions[self.localToonTeam][self.localToonPosIndex] + if toon.getZ(self.root) < PartyGlobals.TugOfWarToonPositionZ: + toon.setZ(self.root, PartyGlobals.TugOfWarToonPositionZ) + targetH = fitDestAngle2Src(toon.getH(self.root), PartyGlobals.TugOfWarHeadings[self.localToonTeam]) + travelVector = targetPos - toon.getPos(self.root) + duration = travelVector.length() / 5.0 + if self.toonIdsToAnimIntervals[toonId] is not None: + self.toonIdsToAnimIntervals[toonId].finish() + self.toonIdsToAnimIntervals[toonId] = Sequence(Func(toon.startPosHprBroadcast, 0.1), Func(toon.b_setAnimState, 'run'), LerpPosHprInterval(toon, duration, targetPos, VBase3(targetH, 0.0, 0.0), other=self.root), Func(toon.stopPosHprBroadcast), Func(toon.b_setAnimState, 'neutral')) + self.toonIdsToAnimIntervals[toonId].start() + return + + def handleToonExited(self, toonId): + DistributedPartyTeamActivity.handleToonExited(self, toonId) + if toonId == base.localAvatar.doId: + self.cameraMoveIval.pause() + if toonId not in self.fallenToons: + if toonId in self.toonIdsToAnimIntervals and self.toonIdsToAnimIntervals[toonId] is not None: + self.toonIdsToAnimIntervals[toonId].finish() + toon = self.getAvatar(toonId) + targetH = fitDestAngle2Src(toon.getH(self.root), 180.0) + targetPos = self.hopOffPositions[self.getTeam(toonId)][self.getIndex(toonId, self.getTeam(toonId))] + hopOffAnim = Sequence(Func(toon.startPosHprBroadcast, 0.1), toon.hprInterval(0.2, VBase3(targetH, 0.0, 0.0), other=self.root), Func(toon.b_setAnimState, 'jump', 1.0), Wait(0.4), PartyUtils.arcPosInterval(0.75, toon, targetPos, 5.0, self.root), Func(toon.stopPosHprBroadcast), Func(toon.sendCurrentPosition), Func(self.hopOffFinished, toonId)) + self.toonIdsToAnimIntervals[toonId] = hopOffAnim + self._hopOffFinishedSV.set(False) + self.toonIdsToAnimIntervals[toonId].start() + else: + self._hopOffFinishedSV.set(True) + del self.toonIdsToAnimIntervals[toonId] + return + + def handleRewardDone(self): + self._rewardFinishedSV.set(True) + + def _testWalkStateReady(self, hoppedOff, rewardFinished): + if hoppedOff and rewardFinished: + DistributedPartyTeamActivity.handleRewardDone(self) + + def hopOffFinished(self, toonId): + if hasattr(self, 'toonIdsToAnimIntervals') and toonId in self.toonIdsToAnimIntervals: + del self.toonIdsToAnimIntervals[toonId] + if toonId == base.localAvatar.doId: + if hasattr(self._hopOffFinishedSV, '_value'): + self._hopOffFinishedSV.set(True) + + def handleToonShifted(self, toonId): + if toonId == base.localAvatar.doId: + self.localToonPosIndex = self.getIndex(base.localAvatar.doId, self.localToonTeam) + if self.toonIdsToAnimIntervals[toonId] is not None: + self.toonIdsToAnimIntervals[toonId].finish() + toon = self.getAvatar(toonId) + targetPos = self.dockPositions[self.localToonTeam][self.localToonPosIndex] + self.toonIdsToAnimIntervals[toonId] = Sequence(Wait(0.6), Func(toon.startPosHprBroadcast, 0.1), Func(toon.b_setAnimState, 'run'), toon.posInterval(0.5, targetPos, other=self.root), Func(toon.stopPosHprBroadcast), Func(toon.b_setAnimState, 'neutral')) + self.toonIdsToAnimIntervals[toonId].start() + return + + def handleToonDisabled(self, toonId): + if self.toonIdsToAnimIntervals.has_key(toonId): + if self.toonIdsToAnimIntervals[toonId]: + if self.toonIdsToAnimIntervals[toonId].isPlaying(): + self.toonIdsToAnimIntervals[toonId].finish() + else: + self.notify.debug('self.toonIdsToAnimIntervals[%d] is none' % toonId) + + def setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds): + DistributedPartyTeamActivity.setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds) + self.toonIdsToRightHands.clear() + for toonId in self.getToonIdsAsList(): + toon = self.getAvatar(toonId) + if toon: + self.toonIdsToRightHands[toonId] = toon.getRightHands()[0] + + def load(self): + DistributedPartyTeamActivity.load(self) + self.loadModels() + self.loadGuiElements() + self.loadSounds() + self.loadIntervals() + self.arrowKeys = ArrowKeys() + + def loadModels(self): + self.playArea = loader.loadModel('phase_13/models/parties/partyTugOfWar') + self.playArea.reparentTo(self.root) + self.sign.reparentTo(self.playArea.find('**/TugOfWar_sign_locator')) + self.dockPositions = [[], []] + for i in range(4): + self.dockPositions[0].append(Point3(-PartyGlobals.TugOfWarInitialToonPositionsXOffset - PartyGlobals.TugOfWarToonPositionXSeparation * i, 0.0, PartyGlobals.TugOfWarToonPositionZ)) + + for i in range(4): + self.dockPositions[1].append(Point3(PartyGlobals.TugOfWarInitialToonPositionsXOffset + PartyGlobals.TugOfWarToonPositionXSeparation * i, 0.0, PartyGlobals.TugOfWarToonPositionZ)) + + self.hopOffPositions = [[], []] + for i in range(1, 5): + self.hopOffPositions[PartyGlobals.TeamActivityTeams.LeftTeam].append(self.playArea.find('**/leftTeamHopOff%d_locator' % i).getPos()) + self.hopOffPositions[PartyGlobals.TeamActivityTeams.RightTeam].append(self.playArea.find('**/rightTeamHopOff%d_locator' % i).getPos()) + + for i in range(1, 5): + pos = self.playArea.find('**/fallenToon%d_locator' % i).getPos() + self.fallenPositions.append(pos) + + self.joinCollision = [] + self.joinCollisionNodePaths = [] + for i in range(len(PartyGlobals.TeamActivityTeams)): + collShape = CollisionTube(PartyGlobals.TugOfWarJoinCollisionEndPoints[0], PartyGlobals.TugOfWarJoinCollisionEndPoints[1], PartyGlobals.TugOfWarJoinCollisionRadius) + collShape.setTangible(True) + self.joinCollision.append(CollisionNode('TugOfWarJoinCollision%d' % i)) + self.joinCollision[i].addSolid(collShape) + tubeNp = self.playArea.attachNewNode(self.joinCollision[i]) + tubeNp.node().setCollideMask(ToontownGlobals.WallBitmask) + self.joinCollisionNodePaths.append(tubeNp) + self.joinCollisionNodePaths[i].setPos(PartyGlobals.TugOfWarJoinCollisionPositions[i]) + + self.__enableCollisions() + ropeModel = loader.loadModel('phase_4/models/minigames/tug_of_war_rope') + self.ropeTexture = ropeModel.findTexture('*') + ropeModel.removeNode() + for i in range(PartyGlobals.TugOfWarMaximumPlayersPerTeam * 2 - 1): + rope = Rope(self.uniqueName('TugRope%d' % i)) + if rope.showRope: + rope.ropeNode.setRenderMode(RopeNode.RMBillboard) + rope.ropeNode.setThickness(0.2) + rope.setTexture(self.ropeTexture) + rope.ropeNode.setUvMode(RopeNode.UVDistance) + rope.ropeNode.setUvDirection(1) + rope.setTransparency(1) + rope.setColor(0.89, 0.89, 0.6, 1.0) + rope.reparentTo(self.root) + rope.stash() + self.tugRopes.append(rope) + + self.splash = Splash.Splash(self.root) + self.splash.setScale(2.0, 4.0, 1.0) + pos = self.fallenPositions[0] + self.splash.setPos(pos[0], pos[1], PartyGlobals.TugOfWarSplashZOffset) + self.splash.hide() + + def loadGuiElements(self): + self.powerMeter = MinigamePowerMeter(PartyGlobals.TugOfWarPowerMeterSize) + self.powerMeter.reparentTo(aspect2d) + self.powerMeter.setPos(0.0, 0.0, 0.6) + self.powerMeter.hide() + self.arrows = [None] * 2 + for x in range(len(self.arrows)): + self.arrows[x] = loader.loadModel('phase_3/models/props/arrow') + self.arrows[x].reparentTo(self.powerMeter) + self.arrows[x].setScale(0.2 - 0.4 * x, 0.2, 0.2) + self.arrows[x].setPos(0.12 - 0.24 * x, 0, -.26) + + return + + def loadSounds(self): + self.splashSound = base.loadSfx('phase_4/audio/sfx/MG_cannon_splash.ogg') + self.whistleSound = base.loadSfx('phase_4/audio/sfx/AA_sound_whistle.ogg') + + def loadIntervals(self): + self.updateIdealRateInterval = Sequence() + self.updateIdealRateInterval.append(Wait(PartyGlobals.TugOfWarTargetRateList[0][0])) + for i in range(1, len(PartyGlobals.TugOfWarTargetRateList)): + duration = PartyGlobals.TugOfWarTargetRateList[i][0] + idealRate = PartyGlobals.TugOfWarTargetRateList[i][1] + self.updateIdealRateInterval.append(Func(self.setIdealRate, idealRate)) + if i == len(PartyGlobals.TugOfWarTargetRateList) - 1: + self.updateIdealRateInterval.append(Func(setattr, self, 'allOutMode', True)) + else: + self.updateIdealRateInterval.append(Wait(duration)) + + self.updateKeyPressRateInterval = Sequence(Wait(PartyGlobals.TugOfWarKeyPressUpdateRate), Func(self.updateKeyPressRate)) + self.reportToServerInterval = Sequence(Wait(PartyGlobals.TugOfWarKeyPressReportRate), Func(self.reportToServer)) + self.setupInterval = Parallel() + self.globalSetupInterval = Sequence(Wait(PartyGlobals.TugOfWarReadyDuration + PartyGlobals.TugOfWarGoDuration), Func(self.tightenRopes)) + self.localSetupInterval = Sequence(Func(self.setStatus, TTLocalizer.PartyTugOfWarReady), Func(self.showStatus), Wait(PartyGlobals.TugOfWarReadyDuration), Func(base.playSfx, self.whistleSound), Func(self.setStatus, TTLocalizer.PartyTugOfWarGo), Wait(PartyGlobals.TugOfWarGoDuration), Func(self.enableKeys), Func(self.hideStatus), Func(self.updateIdealRateInterval.start), Func(self.updateKeyPressRateInterval.loop), Func(self.reportToServerInterval.loop)) + self.splashInterval = Sequence(Func(base.playSfx, self.splashSound), Func(self.splash.play)) + + def unload(self): + DistributedPartyTeamActivity.unload(self) + self.arrowKeys.destroy() + self.unloadIntervals() + self.unloadModels() + self.unloadGuiElements() + self.unloadSounds() + if hasattr(self, 'toonIds'): + del self.toonIds + del self.buttons + del self.arrowKeys + del self.keyTTL + del self.idealRate + del self.keyRate + del self.allOutMode + del self.rateMatchAward + del self.toonIdsToStartPositions + del self.toonIdsToIsPullingFlags + del self.toonIdsToRightHands + del self.fallenToons + del self.fallenPositions + del self.unusedFallenPositionsIndices + self.toonIdsToAnimIntervals.clear() + del self.toonIdsToAnimIntervals + + def unloadModels(self): + self.playArea.removeNode() + del self.playArea + del self.dockPositions + del self.hopOffPositions + self.__disableCollisions() + while len(self.joinCollision) > 0: + collNode = self.joinCollision.pop() + del collNode + + while len(self.joinCollisionNodePaths) > 0: + collNodePath = self.joinCollisionNodePaths.pop() + collNodePath.removeNode() + del collNodePath + + while len(self.tugRopes) > 0: + rope = self.tugRopes.pop() + if rope is not None: + rope.removeNode() + del rope + + del self.tugRopes + self.splash.destroy() + del self.splash + return + + def unloadGuiElements(self): + for arrow in self.arrows: + if arrow is not None: + arrow.removeNode() + del arrow + + del self.arrows + if self.powerMeter is not None: + self.powerMeter.cleanup() + del self.powerMeter + return + + def unloadSounds(self): + del self.splashSound + del self.whistleSound + + def unloadIntervals(self): + self.updateIdealRateInterval.pause() + del self.updateIdealRateInterval + self.updateKeyPressRateInterval.pause() + del self.updateKeyPressRateInterval + self.reportToServerInterval.pause() + del self.reportToServerInterval + self.setupInterval.pause() + del self.setupInterval + self.globalSetupInterval.pause() + del self.globalSetupInterval + self.localSetupInterval.pause() + del self.localSetupInterval + self.splashInterval.pause() + del self.splashInterval + + def __enableCollisions(self): + for i in range(len(PartyGlobals.TeamActivityTeams)): + self.accept('enterTugOfWarJoinCollision%d' % i, getattr(self, '_join%s' % PartyGlobals.TeamActivityTeams.getString(i))) + + def __disableCollisions(self): + for i in range(len(PartyGlobals.TeamActivityTeams)): + self.ignore('enterTugOfWarJoinCollision%d' % i) + + def startWaitForEnough(self): + DistributedPartyTeamActivity.startWaitForEnough(self) + self.__enableCollisions() + + def finishWaitForEnough(self): + DistributedPartyTeamActivity.finishWaitForEnough(self) + self.__disableCollisions() + + def startWaitToStart(self, waitStartTimestamp): + DistributedPartyTeamActivity.startWaitToStart(self, waitStartTimestamp) + self.__enableCollisions() + + def finishWaitToStart(self): + DistributedPartyTeamActivity.finishWaitToStart(self) + self.__disableCollisions() + + def startRules(self): + DistributedPartyTeamActivity.startRules(self) + self.setUpRopes() + if self.isLocalToonPlaying: + self.showControls() + + def finishRules(self): + DistributedPartyTeamActivity.finishRules(self) + if self.activityFSM.getCurrentOrNextState() == 'WaitForEnough': + self.hideRopes() + self.hideControls() + + def finishWaitForServer(self): + DistributedPartyTeamActivity.finishWaitForServer(self) + if self.activityFSM.getCurrentOrNextState() == 'WaitForEnough': + self.hideRopes() + self.hideControls() + + def startActive(self): + DistributedPartyTeamActivity.startActive(self) + self.toonIdsToStartPositions.clear() + self.toonIdsToIsPullingFlags.clear() + for toonId in self.getToonIdsAsList(): + self.toonIdsToIsPullingFlags[toonId] = False + toon = self.getAvatar(toonId) + if toon: + self.toonIdsToStartPositions[toonId] = toon.getPos(self.root) + else: + self.notify.warning("couldn't find toon %d assigning 0,0,0 to startPos" % toonId) + self.toonIdsToStartPositions[toonId] = Point3(0, 0, 0) + + self.unusedFallenPositionsIndices = [0, + 1, + 2, + 3] + self.setupInterval = Parallel(self.globalSetupInterval) + if self.isLocalToonPlaying: + self.keyTTL = [] + self.idealForce = 0.0 + self.keyRate = 0 + self.rateMatchAward = 0.0 + self.allOutMode = False + self.setIdealRate(PartyGlobals.TugOfWarTargetRateList[0][1]) + self.setupInterval.append(self.localSetupInterval) + self.setupInterval.start() + + def finishActive(self): + DistributedPartyTeamActivity.finishActive(self) + self.hideControls() + self.disableKeys() + self.setupInterval.pause() + self.reportToServerInterval.pause() + self.updateKeyPressRateInterval.pause() + self.updateIdealRateInterval.pause() + self.hideRopes() + + def startConclusion(self, losingTeam): + DistributedPartyTeamActivity.startConclusion(self, losingTeam) + if self.isLocalToonPlaying: + self._rewardFinishedSV.set(False) + if losingTeam == PartyGlobals.TeamActivityNeitherTeam: + self.setStatus(TTLocalizer.PartyTeamActivityGameTie) + else: + self.setStatus(TTLocalizer.PartyTugOfWarGameEnd) + self.showStatus() + if losingTeam == PartyGlobals.TeamActivityNeitherTeam: + for toonId in self.getToonIdsAsList(): + if self.getAvatar(toonId): + self.getAvatar(toonId).loop('neutral') + + else: + for toonId in self.toonIds[losingTeam]: + if self.getAvatar(toonId): + self.getAvatar(toonId).loop('neutral') + + for toonId in self.toonIds[1 - losingTeam]: + if self.getAvatar(toonId): + self.getAvatar(toonId).loop('victory') + + for ival in self.toonIdsToAnimIntervals.values(): + if ival is not None: + ival.finish() + + return + + def finishConclusion(self): + DistributedPartyTeamActivity.finishConclusion(self) + self.fallenToons = [] + + def getTitle(self): + return TTLocalizer.PartyTugOfWarTitle + + def getInstructions(self): + return TTLocalizer.TugOfWarInstructions + + def showControls(self): + for arrow in self.arrows: + arrow.setColor(PartyGlobals.TugOfWarDisabledArrowColor) + + self.powerMeter.setTarget(PartyGlobals.TugOfWarTargetRateList[0][1]) + self.powerMeter.setPower(PartyGlobals.TugOfWarTargetRateList[0][1]) + self.powerMeter.setBarColor((0.0, 1.0, 0.0, 0.5)) + self.powerMeter.clearTooSlowTooFast() + self.powerMeter.show() + + def hideControls(self): + self.powerMeter.hide() + + def setUpRopes(self): + self.notify.debug('setUpRopes') + ropeIndex = 0 + leftToonId = -1 + if self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam]: + leftToonId = self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0] + rightToonId = -1 + if self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam]: + rightToonId = self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam][0] + if leftToonId in self.toonIdsToRightHands and rightToonId in self.toonIdsToRightHands: + self.tugRopes[ropeIndex].setup(3, ((self.toonIdsToRightHands[leftToonId], (0, 0, 0)), (self.root, (0.0, 0.0, 2.5)), (self.toonIdsToRightHands[rightToonId], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[ropeIndex].unstash() + ropeIndex += 1 + teams = [PartyGlobals.TeamActivityTeams.LeftTeam, PartyGlobals.TeamActivityTeams.RightTeam] + for currTeam in teams: + numToons = len(self.toonIds[currTeam]) + if numToons > 1: + for i in range(numToons - 1, 0, -1): + toon1 = self.toonIds[currTeam][i] + toon2 = self.toonIds[currTeam][i - 1] + if not self.toonIdsToRightHands.has_key(toon1): + self.notify.warning('Toon in tug of war activity but not properly setup: %s' % toon1) + elif not self.toonIdsToRightHands.has_key(toon2): + self.notify.warning('Toon in tug of war activity but not properly setup: %s' % toon2) + else: + self.notify.debug('Connecting rope between toon %d and toon %d of team %d.' % (i, i - 1, currTeam)) + self.tugRopes[ropeIndex].setup(3, ((self.toonIdsToRightHands[toon1], (0, 0, 0)), (self.toonIdsToRightHands[toon1], (0, 0, 0)), (self.toonIdsToRightHands[toon2], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[ropeIndex].unstash() + ropeIndex += 1 + + def tightenRopes(self): + self.notify.debug('tightenRopes') + self.tugRopes[0].setup(3, ((self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0]], (0, 0, 0)), (self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0]], (0, 0, 0)), (self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam][0]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + + def hideRopes(self): + self.notify.debug('hideRopes') + for rope in self.tugRopes: + rope.stash() + + def handleGameTimerExpired(self): + self.disableKeys() + + def setIdealRate(self, idealRate): + self.notify.debug('setIdealRate( %d )' % idealRate) + self.idealRate = idealRate + self.idealForce = self.advantage * (4 + 0.4 * self.idealRate) + + def updateKeyPressRate(self): + for i in range(len(self.keyTTL)): + self.keyTTL[i] -= PartyGlobals.TugOfWarKeyPressUpdateRate + + for i in range(len(self.keyTTL)): + if self.keyTTL[i] <= 0.0: + a = self.keyTTL[0:i] + del self.keyTTL + self.keyTTL = a + break + + self.keyRate = len(self.keyTTL) + if self.keyRate == self.idealRate or self.keyRate == self.idealRate + 1: + self.rateMatchAward += 0.3 + else: + self.rateMatchAward = 0.0 + + def reportToServer(self): + self.currentForce = self.computeForce(self.keyRate) + self.sendUpdate('reportKeyRateForce', [self.keyRate, self.currentForce]) + self.setSpeedGauge() + self.setAnimState(base.localAvatar.doId, self.keyRate) + + def computeForce(self, keyRate): + F = 0 + if self.allOutMode: + F = 0.75 * keyRate + else: + stdDev = 0.25 * self.idealRate + F = self.advantage * (self.rateMatchAward + 4 + 0.4 * self.idealRate) * math.pow(math.e, -math.pow(keyRate - self.idealRate, 2) / (2.0 * math.pow(stdDev, 2))) + return F + + def setSpeedGauge(self): + self.powerMeter.setPower(self.keyRate) + self.powerMeter.setTarget(self.idealRate) + if not self.allOutMode: + self.powerMeter.updateTooSlowTooFast() + index = float(self.currentForce) / self.idealForce + bonus = 0.0 + if index > 1.0: + bonus = max(1.0, index - 1.0) + index = 1.0 + color = (0, + 0.75 * index + 0.25 * bonus, + 0.75 * (1 - index), + 0.5) + self.powerMeter.setBarColor(color) + else: + self.powerMeter.setBarColor((0.0, 1.0, 0.0, 0.5)) + + def updateToonKeyRate(self, toonId, keyRate): + if toonId != base.localAvatar.doId: + self.setAnimState(toonId, keyRate) + + def setAnimState(self, toonId, keyRate): + if self.activityFSM.state != 'Active': + return + toon = self.getAvatar(toonId) + if not self.toonIdsToIsPullingFlags.has_key(toonId): + if self.getTeam(toonId) == None: + self.notify.warning("setAnimState called with toonId (%d) that wasn't in self.toonIds" % toonId) + return + else: + self.notify.warning('setAnimState called with toonId (%d) that was in self.toonIds but not in self.toonIdsToIsPullingFlags. Adding it.' % toonId) + self.toonIdsToIsPullingFlags[toonId] = False + if keyRate > 0 and not self.toonIdsToIsPullingFlags[toonId]: + if toon: + toon.loop('tug-o-war') + else: + self.notify.warning('toon %d is None, skipping toon.loop(tugowar)' % toonId) + self.toonIdsToIsPullingFlags[toonId] = True + if keyRate <= 0 and self.toonIdsToIsPullingFlags[toonId]: + if toon: + toon.pose('tug-o-war', 3) + toon.startLookAround() + else: + self.notify.warning('toon %d is None, skipping toon.startLookAround' % toonId) + self.toonIdsToIsPullingFlags[toonId] = False + return + + def enableKeys(self): + self.notify.debug('enableKeys') + self.arrowKeys.setPressHandlers([lambda : self.__pressHandler(2), + lambda : self.__pressHandler(3), + lambda : self.__pressHandler(1), + lambda : self.__pressHandler(0)]) + self.arrowKeys.setReleaseHandlers([lambda : self.__releaseHandler(2), + lambda : self.__releaseHandler(3), + lambda : self.__releaseHandler(1), + lambda : self.__releaseHandler(0)]) + for arrow in self.arrows: + arrow.setColor(PartyGlobals.TugOfWarEnabledArrowColor) + + def disableKeys(self): + self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS) + self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS) + + def __pressHandler(self, index): + if index == self.buttons[0]: + self.arrows[index].setColor(PartyGlobals.TugOfWarHilightedArrowColor) + self.keyTTL.insert(0, PartyGlobals.TugOfWarKeyPressTimeToLive) + self.buttons.reverse() + + def __releaseHandler(self, index): + if index in self.buttons: + self.arrows[index].setColor(PartyGlobals.TugOfWarEnabledArrowColor) + + def updateToonPositions(self, offset): + if self.activityFSM.state != 'Active': + return + if self.isLocalToonPlaying: + camera.lookAt(self.root, offset, 0.0, PartyGlobals.TugOfWarCameraLookAtHeightOffset) + for toonId in self.getToonIdsAsList(): + if hasattr(self, 'fallenToons') and toonId not in self.fallenToons: + toon = self.getAvatar(toonId) + if toon is not None: + origPos = self.toonIdsToStartPositions[toonId] + curPos = toon.getPos(self.root) + newPos = Point3(origPos[0] + offset, curPos[1], curPos[2]) + if self.toonIdsToAnimIntervals[toonId] != None: + if self.toonIdsToAnimIntervals[toonId].isPlaying(): + self.toonIdsToAnimIntervals[toonId].finish() + self.checkIfFallen(toonId) + if toonId not in self.fallenToons: + self.toonIdsToAnimIntervals[toonId] = Sequence(LerpPosInterval(toon, duration=PartyGlobals.TugOfWarKeyPressReportRate, pos=newPos, other=self.root), Func(self.checkIfFallen, toonId)) + self.toonIdsToAnimIntervals[toonId].start() + + return + + def checkIfFallen(self, toonId): + if hasattr(self, 'fallenToons') and toonId not in self.fallenToons: + toon = self.getAvatar(toonId) + if toon: + curPos = toon.getPos(self.root) + team = self.getTeam(toonId) + if team == PartyGlobals.TeamActivityTeams.LeftTeam and curPos[0] > -2.0 or team == PartyGlobals.TeamActivityTeams.RightTeam and curPos[0] < 2.0: + losingTeam = self.getTeam(toonId) + self.throwTeamInWater(losingTeam) + self.sendUpdate('reportFallIn', [losingTeam]) + + def throwTeamInWater(self, losingTeam): + self.notify.debug('throwTeamInWater( %s )' % PartyGlobals.TeamActivityTeams.getString(losingTeam)) + splashSet = False + for toonId in self.toonIds[losingTeam]: + self.fallenToons.append(toonId) + toon = self.getAvatar(toonId) + fallenPosIndex = self.toonIds[losingTeam].index(toonId) + if fallenPosIndex < 0 or fallenPosIndex >= 4: + fallenPosIndex = 0 + newPos = self.fallenPositions[fallenPosIndex] + if self.toonIdsToAnimIntervals.has_key(toonId) and self.toonIdsToAnimIntervals[toonId] is not None: + if self.toonIdsToAnimIntervals[toonId].isPlaying(): + self.toonIdsToAnimIntervals[toonId].finish() + if toon: + parallel = Parallel(ActorInterval(actor=toon, animName='slip-forward', duration=2.0), LerpPosInterval(toon, duration=2.0, pos=newPos, other=self.root)) + else: + self.notify.warning('toon %d is none, skipping slip-forward' % toonId) + parallel = Parallel() + if not splashSet: + splashSet = True + parallel.append(self.splashInterval) + if toon: + self.toonIdsToAnimIntervals[toonId] = Sequence(parallel, Func(toon.loop, 'neutral')) + else: + self.notify.warning('toon %d is none, skipping toon.loop(neutral)' % toonId) + self.toonIdsToAnimIntervals[toonId] = parallel + self.toonIdsToAnimIntervals[toonId].start() + + return + + def setAdvantage(self, advantage): + DistributedPartyTeamActivity.setAdvantage(self, advantage) + if self.isLocalToonPlaying: + self.setIdealRate(PartyGlobals.TugOfWarTargetRateList[0][1]) diff --git a/toontown/parties/DistributedPartyTugOfWarActivityAI.py b/toontown/parties/DistributedPartyTugOfWarActivityAI.py new file mode 100755 index 00000000..5e97e2c9 --- /dev/null +++ b/toontown/parties/DistributedPartyTugOfWarActivityAI.py @@ -0,0 +1,133 @@ +from direct.directnotify import DirectNotifyGlobal + +from DistributedPartyTeamActivityAI import DistributedPartyTeamActivityAI +from toontown.toonbase import TTLocalizer +import PartyGlobals + +''' +dclass DistributedPartyTugOfWarActivity : DistributedPartyTeamActivity { + reportKeyRateForce(uint32, int16/100) airecv clsend; + reportFallIn(uint8) airecv clsend; + setToonsPlaying(uint32 [0-4], uint32 [0-4]) required broadcast ram; + updateToonKeyRate(uint32, uint32) broadcast; + updateToonPositions(int16/1000) broadcast; +}; +''' + +scoreRef = {'tie': (PartyGlobals.TugOfWarTieReward, PartyGlobals.TugOfWarTieReward), + 0: (PartyGlobals.TugOfWarWinReward, PartyGlobals.TugOfWarLossReward), + 1: (PartyGlobals.TugOfWarLossReward, PartyGlobals.TugOfWarWinReward), + 10:(PartyGlobals.TugOfWarFallInWinReward, PartyGlobals.TugOfWarFallInLossReward), + 11: (PartyGlobals.TugOfWarFallInLossReward, PartyGlobals.TugOfWarFallInWinReward), + } + +class DistributedPartyTugOfWarActivityAI(DistributedPartyTeamActivityAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyTugOfWarActivityAI") + forbidTeamChanges = True + startDelay = PartyGlobals.TugOfWarStartDelay + + def getDuration(self): + return PartyGlobals.TugOfWarDuration + + def reportKeyRateForce(self, keyRate, force): + # Basic sanity check + av = self._getCaller() + if not av: + return + + avId = av.doId + + if not (avId in self.toonIds[0] or avId in self.toonIds[1]): + self.air.writeServerEvent('suspicious', avId, 'sent DistributedPartyTugOfWarActivityAI.reportKeyRateForce, but not playing') + return + + self.forces[avId] = force + self.sendUpdate('updateToonKeyRate', [avId, keyRate]) + self.d_updateToonPositions() + + def d_updateToonPositions(self): + _getTeamForce = lambda team: sum(self.forces.get(avId, 0) for avId in self.toonIds[team]) + + f0 = _getTeamForce(0) + f1 = _getTeamForce(1) + fr = f0 + f1 + + if fr != 0: + delta = (f0 - f1) / fr + self.pos += -delta * PartyGlobals.TugOfWarMovementFactor * 2 + + self.sendUpdate('updateToonPositions', [self.pos]) + + def reportFallIn(self, losingTeam): + if self.fsm.state != 'Active' or self._hasFall: + return + + # Basic sanity check + av = self._getCaller() + if not av: + return + + avId = av.doId + + if not (avId in self.toonIds[0] or avId in self.toonIds[1]): + self.air.writeServerEvent('suspicious', avId, 'sent DistributedPartyTugOfWarActivityAI.reportFallIn, but not playing') + return + + losers = int(self.pos < 0) + if losers != losingTeam: + self.air.writeServerEvent('suspicious', avId, 'called DistributedPartyTugOfWarActivityAI.reportFallIn with incorrect losingTeam') + + self._hasFall = 1 + + def _advance(task): + self.calcReward() + return task.done + + taskMgr.doMethodLater(1, _advance, self.taskName('fallIn-advance')) + taskMgr.remove(self.taskName('finish')) # Mitigate races + + def calcReward(self): + nobodyWins = abs(self.pos) <= 2 + if nobodyWins: + self._winnerTeam = 3 + self._teamScores = scoreRef['tie'] + + else: + self._winnerTeam = int(self.pos < 0) + self._teamScores = scoreRef[self._winnerTeam + self._hasFall * 10] + + self.b_setState('Conclusion', self._winnerTeam) + + def startActive(self, data): + self.forces = {} + self.pos = 0 + self._hasFall = 0 + DistributedPartyTeamActivityAI.startActive(self, data) + + def startConclusion(self, data): + taskMgr.doMethodLater(1, self.__exitConclusion, self.taskName('exitconc')) + + def finishConclusion(self): + taskMgr.remove(self.taskName('exitconc')) + + def __exitConclusion(self, task): + def _sendReward(team): + jb = self._teamScores[team] + msg = TTLocalizer.PartyTeamActivityRewardMessage % jb + + for avId in self.toonIds[team]: + av = self.air.doId2do.get(avId) + if av: + self.sendUpdateToAvatarId(avId, 'showJellybeanReward', [jb, av.getMoney(), msg]) + av.addMoney(jb) + + _sendReward(0) + _sendReward(1) + + self.toonsPlaying = [] + self.toonIds = ([], []) + + self.updateToonsPlaying() + + self.b_setState('WaitForEnough') + return task.done diff --git a/toontown/parties/DistributedPartyValentineDance20Activity.py b/toontown/parties/DistributedPartyValentineDance20Activity.py new file mode 100755 index 00000000..47399e19 --- /dev/null +++ b/toontown/parties/DistributedPartyValentineDance20Activity.py @@ -0,0 +1,26 @@ +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyDanceActivityBase import DistributedPartyDanceActivityBase +from toontown.toonbase import TTLocalizer + +class DistributedPartyValentineDance20Activity(DistributedPartyDanceActivityBase): + notify = directNotify.newCategory('DistributedPartyValentineDance20Activity') + + def __init__(self, cr): + DistributedPartyDanceActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyDance20, PartyGlobals.DancePatternToAnims20, model='phase_13/models/parties/tt_m_ara_pty_danceFloorValentine') + + def getInstructions(self): + return TTLocalizer.PartyDanceActivity20Instructions + + def getTitle(self): + return TTLocalizer.PartyDanceActivity20Title + + def load(self): + DistributedPartyDanceActivityBase.load(self) + parentGroup = self.danceFloor.find('**/discoBall_mesh') + correctBall = self.danceFloor.find('**/discoBall_20') + if not correctBall.isEmpty(): + numChildren = parentGroup.getNumChildren() + for i in xrange(numChildren): + child = parentGroup.getChild(i) + if child != correctBall: + child.hide() diff --git a/toontown/parties/DistributedPartyValentineDance20ActivityAI.py b/toontown/parties/DistributedPartyValentineDance20ActivityAI.py new file mode 100755 index 00000000..68f80906 --- /dev/null +++ b/toontown/parties/DistributedPartyValentineDance20ActivityAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyDanceActivityBaseAI import DistributedPartyDanceActivityBaseAI + +class DistributedPartyValentineDance20ActivityAI(DistributedPartyDanceActivityBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyValentineDance20ActivityAI") diff --git a/toontown/parties/DistributedPartyValentineDanceActivity.py b/toontown/parties/DistributedPartyValentineDanceActivity.py new file mode 100755 index 00000000..d61d5e40 --- /dev/null +++ b/toontown/parties/DistributedPartyValentineDanceActivity.py @@ -0,0 +1,27 @@ +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyDanceActivityBase import DistributedPartyDanceActivityBase +from toontown.toonbase import TTLocalizer + +class DistributedPartyValentineDanceActivity(DistributedPartyDanceActivityBase): + notify = directNotify.newCategory('DistributedPartyValentineDanceActivity') + + def __init__(self, cr): + DistributedPartyDanceActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyDance, PartyGlobals.DancePatternToAnims, model='phase_13/models/parties/tt_m_ara_pty_danceFloorValentine') + + def getInstructions(self): + return TTLocalizer.PartyDanceActivityInstructions + + def getTitle(self): + return TTLocalizer.PartyDanceActivityTitle + + def load(self): + DistributedPartyDanceActivityBase.load(self) + parentGroup = self.danceFloor.find('**/discoBall_mesh') + correctBall = self.danceFloor.find('**/discoBall_10') + origBall = self.danceFloor.find('**/discoBall_mesh_orig') + if not correctBall.isEmpty(): + numChildren = parentGroup.getNumChildren() + for i in xrange(numChildren): + child = parentGroup.getChild(i) + if child != correctBall: + child.hide() diff --git a/toontown/parties/DistributedPartyValentineDanceActivityAI.py b/toontown/parties/DistributedPartyValentineDanceActivityAI.py new file mode 100755 index 00000000..6575d0b7 --- /dev/null +++ b/toontown/parties/DistributedPartyValentineDanceActivityAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyDanceActivityBaseAI import DistributedPartyDanceActivityBaseAI + +class DistributedPartyValentineDanceActivityAI(DistributedPartyDanceActivityBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyValentineDanceActivityAI") diff --git a/toontown/parties/DistributedPartyValentineJukebox40Activity.py b/toontown/parties/DistributedPartyValentineJukebox40Activity.py new file mode 100755 index 00000000..7fc3f28f --- /dev/null +++ b/toontown/parties/DistributedPartyValentineJukebox40Activity.py @@ -0,0 +1,18 @@ +from toontown.parties.DistributedPartyJukeboxActivityBase import DistributedPartyJukeboxActivityBase +from toontown.parties import PartyGlobals + +class DistributedPartyValentineJukebox40Activity(DistributedPartyJukeboxActivityBase): + notify = directNotify.newCategory('DistributedPartyValentineJukebox40Activity') + + def __init__(self, cr): + DistributedPartyJukeboxActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyValentineJukebox40, PartyGlobals.PhaseToMusicData40) + + def load(self): + DistributedPartyJukeboxActivityBase.load(self) + newTexture = loader.loadTexture('phase_13/maps/tt_t_ara_pty_jukeboxValentineB.jpg', 'phase_13/maps/tt_t_ara_pty_jukeboxValentineB_a.rgb') + case = self.jukebox.find('**/jukeboxGlass') + if not case.isEmpty(): + case.setTexture(newTexture, 1) + body = self.jukebox.find('**/jukeboxBody') + if not body.isEmpty(): + body.setTexture(newTexture, 1) diff --git a/toontown/parties/DistributedPartyValentineJukebox40ActivityAI.py b/toontown/parties/DistributedPartyValentineJukebox40ActivityAI.py new file mode 100755 index 00000000..1004610f --- /dev/null +++ b/toontown/parties/DistributedPartyValentineJukebox40ActivityAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyJukeboxActivityBaseAI import DistributedPartyJukeboxActivityBaseAI + +class DistributedPartyValentineJukebox40ActivityAI(DistributedPartyJukeboxActivityBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyValentineJukebox40ActivityAI") diff --git a/toontown/parties/DistributedPartyValentineJukeboxActivity.py b/toontown/parties/DistributedPartyValentineJukeboxActivity.py new file mode 100755 index 00000000..f67cb5a5 --- /dev/null +++ b/toontown/parties/DistributedPartyValentineJukeboxActivity.py @@ -0,0 +1,18 @@ +from toontown.parties.DistributedPartyJukeboxActivityBase import DistributedPartyJukeboxActivityBase +from toontown.parties import PartyGlobals + +class DistributedPartyValentineJukeboxActivity(DistributedPartyJukeboxActivityBase): + notify = directNotify.newCategory('DistributedPartyJukeboxActivity') + + def __init__(self, cr): + DistributedPartyJukeboxActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyValentineJukebox, PartyGlobals.PhaseToMusicData) + + def load(self): + DistributedPartyJukeboxActivityBase.load(self) + newTexture = loader.loadTexture('phase_13/maps/tt_t_ara_pty_jukeboxValentineA.jpg', 'phase_13/maps/tt_t_ara_pty_jukeboxValentineA_a.rgb') + case = self.jukebox.find('**/jukeboxGlass') + if not case.isEmpty(): + case.setTexture(newTexture, 1) + body = self.jukebox.find('**/jukeboxBody') + if not body.isEmpty(): + body.setTexture(newTexture, 1) diff --git a/toontown/parties/DistributedPartyValentineJukeboxActivityAI.py b/toontown/parties/DistributedPartyValentineJukeboxActivityAI.py new file mode 100755 index 00000000..6239747e --- /dev/null +++ b/toontown/parties/DistributedPartyValentineJukeboxActivityAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyJukeboxActivityBaseAI import DistributedPartyJukeboxActivityBaseAI + +class DistributedPartyValentineJukeboxActivityAI(DistributedPartyJukeboxActivityBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyValentineJukeboxActivityAI") diff --git a/toontown/parties/DistributedPartyValentineTrampolineActivity.py b/toontown/parties/DistributedPartyValentineTrampolineActivity.py new file mode 100755 index 00000000..a362d3cf --- /dev/null +++ b/toontown/parties/DistributedPartyValentineTrampolineActivity.py @@ -0,0 +1,6 @@ +from toontown.parties.DistributedPartyTrampolineActivity import DistributedPartyTrampolineActivity + +class DistributedPartyValentineTrampolineActivity(DistributedPartyTrampolineActivity): + + def __init__(self, cr, doJellyBeans = True, doTricks = False, texture = None): + DistributedPartyTrampolineActivity.__init__(self, cr, doJellyBeans, doTricks, 'phase_13/maps/tt_t_ara_pty_trampolineValentine.jpg') diff --git a/toontown/parties/DistributedPartyValentineTrampolineActivityAI.py b/toontown/parties/DistributedPartyValentineTrampolineActivityAI.py new file mode 100755 index 00000000..884a537b --- /dev/null +++ b/toontown/parties/DistributedPartyValentineTrampolineActivityAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyTrampolineActivityAI import DistributedPartyTrampolineActivityAI + +class DistributedPartyValentineTrampolineActivityAI(DistributedPartyTrampolineActivityAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyValentineTrampolineActivityAI") diff --git a/toontown/parties/DistributedPartyVictoryTrampolineActivity.py b/toontown/parties/DistributedPartyVictoryTrampolineActivity.py new file mode 100755 index 00000000..99d156b4 --- /dev/null +++ b/toontown/parties/DistributedPartyVictoryTrampolineActivity.py @@ -0,0 +1,6 @@ +from toontown.parties.DistributedPartyTrampolineActivity import DistributedPartyTrampolineActivity + +class DistributedPartyVictoryTrampolineActivity(DistributedPartyTrampolineActivity): + + def __init__(self, cr, doJellyBeans = True, doTricks = False, texture = None): + DistributedPartyTrampolineActivity.__init__(self, cr, doJellyBeans, doTricks, 'phase_13/maps/tt_t_ara_pty_trampolineVictory.jpg') diff --git a/toontown/parties/DistributedPartyVictoryTrampolineActivityAI.py b/toontown/parties/DistributedPartyVictoryTrampolineActivityAI.py new file mode 100755 index 00000000..b32f900c --- /dev/null +++ b/toontown/parties/DistributedPartyVictoryTrampolineActivityAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyTrampolineActivityAI import DistributedPartyTrampolineActivityAI + +class DistributedPartyVictoryTrampolineActivityAI(DistributedPartyTrampolineActivityAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyVictoryTrampolineActivityAI") diff --git a/toontown/parties/DistributedPartyWinterCatchActivity.py b/toontown/parties/DistributedPartyWinterCatchActivity.py new file mode 100755 index 00000000..9089012b --- /dev/null +++ b/toontown/parties/DistributedPartyWinterCatchActivity.py @@ -0,0 +1,46 @@ +from pandac.PandaModules import NodePath +from toontown.toonbase import TTLocalizer +from toontown.parties.DistributedPartyCatchActivity import DistributedPartyCatchActivity +from toontown.parties import PartyGlobals +from toontown.parties import WinterPartyCatchActivityToonSD + +class DistributedPartyWinterCatchActivity(DistributedPartyCatchActivity): + + def __init__(self, cr): + DistributedPartyCatchActivity.__init__(self, cr) + + def getInstructions(self): + return TTLocalizer.WinterPartyCatchActivityInstructions % {'badThing': self.DropObjectPlurals['anvil']} + + def load(self): + DistributedPartyCatchActivity.load(self, loadModels=0, arenaModel='tt_m_ara_pty_partyCatchTreeWinter') + self.__loadDropModels() + + def __loadDropModels(self): + for objType in PartyGlobals.DropObjectTypes: + model = None + if not objType == PartyGlobals.Name2DropObjectType['anvil']: + model = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_winterPresent') + model.setScale(0.5) + else: + model = loader.loadModel(objType.modelPath) + self.dropObjModels[objType.name] = model + model.flattenStrong() + + return + + def handleToonJoined(self, toonId): + if toonId not in self.toonSDs: + toonSD = WinterPartyCatchActivityToonSD.WinterPartyCatchActivityToonSD(toonId, self) + self.toonSDs[toonId] = toonSD + toonSD.load() + self.notify.debug('handleToonJoined : currentState = %s' % self.activityFSM.state) + self.cr.doId2do[toonId].useLOD(500) + if self.activityFSM.state == 'Active': + if toonId in self.toonSDs: + self.toonSDs[toonId].enter() + if base.localAvatar.doId == toonId: + base.localAvatar.b_setParent(self._avatarNodePathParentToken) + self.putLocalAvatarInActivity() + if toonId in self.toonSDs: + self.toonSDs[toonId].fsm.request('rules') diff --git a/toontown/parties/DistributedPartyWinterCatchActivityAI.py b/toontown/parties/DistributedPartyWinterCatchActivityAI.py new file mode 100755 index 00000000..1ea5f5f1 --- /dev/null +++ b/toontown/parties/DistributedPartyWinterCatchActivityAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyCatchActivityAI import DistributedPartyCatchActivityAI + +class DistributedPartyWinterCatchActivityAI(DistributedPartyCatchActivityAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyWinterCatchActivityAI") diff --git a/toontown/parties/DistributedPartyWinterCogActivity.py b/toontown/parties/DistributedPartyWinterCogActivity.py new file mode 100755 index 00000000..6c5c1092 --- /dev/null +++ b/toontown/parties/DistributedPartyWinterCogActivity.py @@ -0,0 +1,6 @@ +from toontown.parties.DistributedPartyCogActivity import DistributedPartyCogActivity + +class DistributedPartyWinterCogActivity(DistributedPartyCogActivity): + + def __init__(self, cr): + DistributedPartyCogActivity.__init__(self, cr, 'phase_13/models/parties/tt_m_ara_pty_cogPieArenaWinter') diff --git a/toontown/parties/DistributedPartyWinterCogActivityAI.py b/toontown/parties/DistributedPartyWinterCogActivityAI.py new file mode 100755 index 00000000..65360d68 --- /dev/null +++ b/toontown/parties/DistributedPartyWinterCogActivityAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyCogActivityAI import DistributedPartyCogActivityAI + +class DistributedPartyWinterCogActivityAI(DistributedPartyCogActivityAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyWinterCogActivityAI") diff --git a/toontown/parties/DistributedPartyWinterTrampolineActivity.py b/toontown/parties/DistributedPartyWinterTrampolineActivity.py new file mode 100755 index 00000000..a0dcbfda --- /dev/null +++ b/toontown/parties/DistributedPartyWinterTrampolineActivity.py @@ -0,0 +1,6 @@ +from toontown.parties.DistributedPartyTrampolineActivity import DistributedPartyTrampolineActivity + +class DistributedPartyWinterTrampolineActivity(DistributedPartyTrampolineActivity): + + def __init__(self, cr, doJellyBeans = True, doTricks = False, texture = None): + DistributedPartyTrampolineActivity.__init__(self, cr, doJellyBeans, doTricks, 'phase_13/maps/tt_t_ara_pty_trampolineWinter.jpg') diff --git a/toontown/parties/DistributedPartyWinterTrampolineActivityAI.py b/toontown/parties/DistributedPartyWinterTrampolineActivityAI.py new file mode 100755 index 00000000..d681a81e --- /dev/null +++ b/toontown/parties/DistributedPartyWinterTrampolineActivityAI.py @@ -0,0 +1,5 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.DistributedPartyTrampolineActivityAI import DistributedPartyTrampolineActivityAI + +class DistributedPartyWinterTrampolineActivityAI(DistributedPartyTrampolineActivityAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyWinterTrampolineActivityAI") diff --git a/toontown/parties/GlobalPartyManager.py b/toontown/parties/GlobalPartyManager.py new file mode 100755 index 00000000..379afe04 --- /dev/null +++ b/toontown/parties/GlobalPartyManager.py @@ -0,0 +1,6 @@ +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal +from direct.distributed.PyDatagram import * +from direct.directnotify.DirectNotifyGlobal import directNotify + +class GlobalPartyManager(DistributedObjectGlobal): + notify = directNotify.newCategory('GlobalPartyManager') diff --git a/toontown/parties/GlobalPartyManagerAI.py b/toontown/parties/GlobalPartyManagerAI.py new file mode 100755 index 00000000..650f50da --- /dev/null +++ b/toontown/parties/GlobalPartyManagerAI.py @@ -0,0 +1,53 @@ +from direct.distributed.DistributedObjectGlobalAI import DistributedObjectGlobalAI +from direct.distributed.PyDatagram import * +from direct.directnotify.DirectNotifyGlobal import directNotify + +class GlobalPartyManagerAI(DistributedObjectGlobalAI): + notify = directNotify.newCategory('GlobalPartyManagerAI') + + def announceGenerate(self): + DistributedObjectGlobalAI.announceGenerate(self) + # Inform the UD that we as an AI have started up, and tell him the doId of our partymanager, so they can talk + self.sendUpdate('partyManagerAIHello', [simbase.air.partyManager.doId]) + + def sendAddParty(self, avId, partyId, start, end, isPrivate, inviteTheme, activities, decorations, inviteeIds): + self.sendUpdate('addParty', [avId, partyId, start, end, isPrivate, inviteTheme, activities, decorations, inviteeIds]) + + def queryPartyForHost(self, hostId): + self.sendUpdate('queryParty', [hostId]) + + def d_partyStarted(self, partyId, shardId, zoneId, hostName): + self.sendUpdate('partyHasStarted', [partyId, shardId, zoneId, hostName]) + + def partyStarted(self, partyId, shardId, zoneId, hostName): + pass + + def d_partyDone(self, partyId): + self.sendUpdate('partyDone', [partyId]) + + def partyDone(self, partyId): + pass + + def d_toonJoinedParty(self, partyId, avId): + self.sendUpdate('toonJoinedParty', [partyId, avId]) + + def toonJoinedParty(self, partyId, avId): + pass + + def d_toonLeftParty(self, partyId, avId): + self.sendUpdate('toonLeftParty', [partyId, avId]) + + def toonLeftParty(self, partyId, avId): + pass + + def d_requestPartySlot(self, partyId, avId, gateId): + self.sendUpdate('requestPartySlot', [partyId, avId, gateId]) + + def requestPartySlot(self, partyId, avId, gateId): + pass + + def d_allocIds(self, numIds): + self.sendUpdate('allocIds', [numIds]) + + def allocIds(self, numIds): + pass diff --git a/toontown/parties/GlobalPartyManagerUD.py b/toontown/parties/GlobalPartyManagerUD.py new file mode 100755 index 00000000..37f7ba30 --- /dev/null +++ b/toontown/parties/GlobalPartyManagerUD.py @@ -0,0 +1,252 @@ +from direct.distributed.DistributedObjectGlobalUD import DistributedObjectGlobalUD +from direct.distributed.PyDatagram import * +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.task import Task +from PartyGlobals import * +from datetime import datetime, timedelta +from panda3d.core import * + +class GlobalPartyManagerUD(DistributedObjectGlobalUD): + notify = directNotify.newCategory('GlobalPartyManagerUD') + + # This uberdog MUST be up before the AIs, as AIs talk to this UD + + def announceGenerate(self): + DistributedObjectGlobalUD.announceGenerate(self) + self.notify.debug("GPMUD generated") + self.senders2Mgrs = {} + self.host2PartyId = {} # just a reference mapping + self.id2Party = {} # This should be replaced with a longterm datastore + self.party2PubInfo = {} # This should not be longterm + self.tempSlots = {} + PARTY_TIME_FORMAT = '%Y-%m-%d %H:%M:%S' + startTime = datetime.strptime('2014-01-20 11:50:00', PARTY_TIME_FORMAT) + endTime = datetime.strptime('2014-01-20 12:20:00', PARTY_TIME_FORMAT) + self.nextId = 0 + #self.host2Party[100000001] = {'hostId': 100000001, 'start': startTime, 'end': endTime, 'partyId': 1717986918400000, 'decorations': [[3,5,7,6]], 'activities': [[10,13,6,18],[7,8,7,0]],'inviteTheme':1,'isPrivate':0,'inviteeIds':[]} + config = getConfigShowbase() + self.wantInstantParties = config.GetBool('want-instant-parties', 0) + + # Setup tasks + self.runAtNextInterval() + + # GPMUD -> PartyManagerAI messaging + def _makeAIMsg(self, field, values, recipient): + return self.air.dclassesByName['DistributedPartyManagerUD'].getFieldByName(field).aiFormatUpdate(recipient, recipient, simbase.air.ourChannel, values) + + def sendToAI(self, field, values, sender=None): + if not sender: + sender = self.air.getAvatarIdFromSender() + dg = self._makeAIMsg(field, values, self.senders2Mgrs.get(sender, sender + 8)) + self.air.send(dg) + + # GPMUD -> toon messaging + def _makeAvMsg(self, field, values, recipient): + return self.air.dclassesByName['DistributedToonUD'].getFieldByName(field).aiFormatUpdate(recipient, recipient, simbase.air.ourChannel, values) + + def sendToAv(self, avId, field, values): + dg = self._makeAvMsg(field, values, avId) + self.air.send(dg) + + # Task stuff + def runAtNextInterval(self): + now = datetime.now() + howLongUntilAFive = (60 - now.second) + 60 * (4 - (now.minute % 5)) + taskMgr.doMethodLater(howLongUntilAFive, self.__checkPartyStarts, 'GlobalPartyManager_checkStarts') + + def canPartyStart(self, party): + now = datetime.now() + delta = timedelta(minutes=15) + endStartable = party['start'] + delta + if self.wantInstantParties: + return True + else: + return party['start'] < now# and endStartable > now + + def isTooLate(self, party): + now = datetime.now() + delta = timedelta(minutes=15) + endStartable = party['start'] + delta + return endStartable > now + + def __checkPartyStarts(self, task): + now = datetime.now() + for partyId in self.id2Party: + party = self.id2Party[partyId] + hostId = party['hostId'] + if self.canPartyStart(party) and party['status'] == PartyStatus.Pending: + # Time to start party + party['status'] = PartyStatus.CanStart + self.sendToAv(hostId, 'setHostedParties', [[self._formatParty(party)]]) + self.sendToAv(hostId, 'setPartyCanStart', [partyId]) + elif self.isTooLate(party): + party['status'] = PartyStatus.NeverStarted + self.sendToAv(hostId, 'setHostedParties', [[self._formatParty(party)]]) + self.runAtNextInterval() + + # Format a party dict into a party struct suitable for the wire + def _formatParty(self, partyDict): + start = partyDict['start'] + end = partyDict['end'] + return [partyDict['partyId'], + partyDict['hostId'], + start.year, + start.month, + start.day, + start.hour, + start.minute, + end.year, + end.month, + end.day, + end.hour, + end.minute, + partyDict['isPrivate'], + partyDict['inviteTheme'], + partyDict['activities'], + partyDict['decorations'], + partyDict.get('status', PartyStatus.Pending)] + + # Avatar joined the game, invoked by the CSMUD + def avatarJoined(self, avId): +# self.host2PartyId[avId] = (1337 << 32) + 10000 + partyId = self.host2PartyId.get(avId, None) + if partyId: + party = self.id2Party.get(partyId, None) + if not party: + return # There's a partyId without an actual party?? What is this madness. + self.sendToAv(avId, 'setHostedParties', [[self._formatParty(party)]]) + if partyId not in self.party2PubInfo and self.canPartyStart(party): + # The party hasn't started and it can start + self.sendToAv(avId, 'setPartyCanStart', [partyId]) + + # uberdog coordination of public party info + def __updatePartyInfo(self, partyId): + # Update all the AIs about this public party + party = self.party2PubInfo[partyId] + for sender in self.senders2Mgrs.keys(): + actIds = [] + for activity in self.id2Party[partyId]['activities']: + actIds.append(activity[0]) # First part of activity tuple should be actId + minLeft = int((PARTY_DURATION - (datetime.now() - party['started']).seconds) / 60) + self.sendToAI('updateToPublicPartyInfoUdToAllAi', [party['shardId'], party['zoneId'], partyId, self.id2Party[partyId]['hostId'], party['numGuests'], party['maxGuests'], party['hostName'], actIds, minLeft], sender=sender) + + def __updatePartyCount(self, partyId): + # Update the party guest count + for sender in self.senders2Mgrs.keys(): + self.sendToAI('updateToPublicPartyCountUdToAllAi', [self.party2PubInfo[partyId]['numGuests'], partyId], sender=sender) + + def partyHasStarted(self, partyId, shardId, zoneId, hostName): + self.party2PubInfo[partyId] = {'partyId': partyId, 'shardId': shardId, 'zoneId': zoneId, 'hostName': hostName, 'numGuests': 0, 'maxGuests': MaxToonsAtAParty, 'started': datetime.now()} + self.__updatePartyInfo(partyId) + # update the host's book + if partyId not in self.id2Party: + self.notify.warning("didn't find details for starting party id %s hosted by %s" % (partyId, hostName)) + return + self.id2Party[partyId]['status'] = PartyStatus.Started + party = self.id2Party.get(partyId, None) + self.sendToAv(party['hostId'], 'setHostedParties', [[self._formatParty(party)]]) + + def partyDone(self, partyId): + del self.party2PubInfo[partyId] + self.id2Party[partyId]['status'] = PartyStatus.Finished + party = self.id2Party.get(partyId, None) + self.sendToAv(party['hostId'], 'setHostedParties', [[self._formatParty(party)]]) + del self.id2Party[partyId] + self.air.writeServerEvent('party-done', '%s') + + def toonJoinedParty(self, partyId, avId): + if avId in self.tempSlots: + del self.tempSlots[avId] + return + self.party2PubInfo.get(partyId, {'numGuests': 0})['numGuests'] += 1 + self.__updatePartyCount(partyId) + + def toonLeftParty(self, partyId, avId): + self.party2PubInfo.get(partyId, {'numGuests': 0})['numGuests'] -= 1 + self.__updatePartyCount(partyId) + + def partyManagerAIHello(self, channel): + # Upon AI boot, DistributedPartyManagerAIs are supposed to say hello. + # They send along the DPMAI's doId as well, so that I can talk to them later. + print 'AI with base channel %s, will send replies to DPM %s' % (simbase.air.getAvatarIdFromSender(), channel) + self.senders2Mgrs[simbase.air.getAvatarIdFromSender()] = channel + self.sendToAI('partyManagerUdStartingUp', []) + + # In addition, set up a postRemove where we inform this AI that the UD has died + self.air.addPostRemove(self._makeAIMsg('partyManagerUdLost', [], channel)) + + def addParty(self, avId, partyId, start, end, isPrivate, inviteTheme, activities, decorations, inviteeIds): + PARTY_TIME_FORMAT = '%Y-%m-%d %H:%M:%S' + print 'start time: %s' % start + startTime = datetime.strptime(start, PARTY_TIME_FORMAT) + endTime = datetime.strptime(end, PARTY_TIME_FORMAT) + print 'start year: %s' % startTime.year + if avId in self.host2PartyId: + # Sorry, one party at a time + self.sendToAI('addPartyResponseUdToAi', [partyId, AddPartyErrorCode.TooManyHostedParties, self._formatParty(self.id2Party[partyId])]) + self.id2Party[partyId] = {'partyId': partyId, 'hostId': avId, 'start': startTime, 'end': endTime, 'isPrivate': isPrivate, 'inviteTheme': inviteTheme, 'activities': activities, 'decorations': decorations, 'inviteeIds': inviteeIds, 'status': PartyStatus.Pending} + self.host2PartyId[avId] = partyId + self.sendToAI('addPartyResponseUdToAi', [partyId, AddPartyErrorCode.AllOk, self._formatParty(self.id2Party[partyId])]) + if self.wantInstantParties: + taskMgr.remove('GlobalPartyManager_checkStarts') + taskMgr.doMethodLater(15, self.__checkPartyStarts, 'GlobalPartyManager_checkStarts') + return + + def queryParty(self, hostId): + # An AI is wondering if the host has a party. We'll tell em! + if hostId in self.host2PartyId: + # Yep, he has a party. + party = self.id2Party[self.host2PartyId[hostId]] + self.sendToAI('partyInfoOfHostResponseUdToAi', [self._formatParty(party), party.get('inviteeIds', [])]) + return + print 'query failed, av %s isnt hosting anything' % hostId + + def requestPartySlot(self, partyId, avId, gateId): + if partyId not in self.party2PubInfo: + recipient = self.GetPuppetConnectionChannel(avId) + sender = simbase.air.getAvatarIdFromSender() + dg = self.air.dclassesByName['DistributedPartyGateAI'].getFieldByName('partyRequestDenied').aiFormatUpdate(gateId, recipient, sender, [PartyGateDenialReasons.Unavailable]) + self.air.send(dg) + return + party = self.party2PubInfo[partyId] + if party['numGuests'] >= party['maxGuests']: + recipient = self.GetPuppetConnectionChannel(avId) + sender = simbase.air.getAvatarIdFromSender() + dg = self.air.dclassesByName['DistributedPartyGateAI'].getFieldByName('partyRequestDenied').aiFormatUpdate(gateId, recipient, sender, [PartyGateDenialReasons.Full]) + self.air.send(dg) + return + # get them a slot + party['numGuests'] += 1 + self.__updatePartyCount(partyId) + # note that they might not show up + self.tempSlots[avId] = partyId + + #give the client a minute to connect before freeing their slot + taskMgr.doMethodLater(60, self._removeTempSlot, 'partyManagerTempSlot%d' % avId, extraArgs=[avId]) + + # now format the pubPartyInfo + actIds = [] + for activity in self.id2Party[partyId]['activities']: + actIds.append(activity[0]) + info = [party['shardId'], party['zoneId'], party['numGuests'], party['hostName'], actIds, 0] # the param is minleft + # find the hostId + hostId = self.id2Party[party['partyId']]['hostId'] + # send update to client's gate + recipient = self.GetPuppetConnectionChannel(avId) + sender = simbase.air.getAvatarIdFromSender() # try to pretend the AI sent it. ily2 cfsworks + dg = self.air.dclassesByName['DistributedPartyGateAI'].getFieldByName('setParty').aiFormatUpdate(gateId, recipient, sender, [info, hostId]) + self.air.send(dg) + + def _removeTempSlot(self, avId): + partyId = self.tempSlots.get(avId) + if partyId: + del self.tempSlots[avId] + self.party2PubInfo.get(partyId, {'numGuests': 0})['numGuests'] -= 1 + self.__updatePartyCount(partyId) + + def allocIds(self, numIds): + ids = [] + while len(ids) < numIds: + ids.append(self.nextId) + self.nextId += 1 + self.sendToAI('receiveId', ids) diff --git a/toontown/parties/InviteInfo.py b/toontown/parties/InviteInfo.py new file mode 100755 index 00000000..f242bdff --- /dev/null +++ b/toontown/parties/InviteInfo.py @@ -0,0 +1,56 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.parties.PartyGlobals import InviteStatus +from toontown.toonbase import TTLocalizer + +class InviteInfoBase: + + def __init__(self, inviteKey, partyId, status): + self.inviteKey = inviteKey + self.partyId = partyId + self.status = status + + def __str__(self): + string = 'inviteKey=%d ' % self.inviteKey + string += 'partyId=%d ' % self.partyId + string += 'status=%s' % InviteStatus.getString(self.status) + return string + + def __repr__(self): + return self.__str__() + + +class InviteInfo(InviteInfoBase): + notify = DirectNotifyGlobal.directNotify.newCategory('InviteInfo') + + def __init__(self, inviteKey, partyId, status): + InviteInfoBase.__init__(self, inviteKey, partyId, status) + + def acceptItem(self, mailbox, acceptingIndex, callback): + InviteInfo.notify.debug('acceptItem') + mailbox.acceptInvite(self, acceptingIndex, callback) + + def discardItem(self, mailbox, acceptingIndex, callback): + InviteInfo.notify.debug('discardItem') + mailbox.rejectInvite(self, acceptingIndex, callback) + + def getAcceptItemErrorText(self, retcode): + InviteInfo.notify.debug('getAcceptItemErrorText') + if retcode == ToontownGlobals.P_InvalidIndex: + return TTLocalizer.InviteAcceptInvalidError + elif retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.InviteAcceptAllOk + else: + return TTLocalizer.CatalogAcceptGeneralError % retcode + + def getDiscardItemErrorText(self, retcode): + InviteInfo.notify.debug('getDiscardItemErrorText') + if retcode == ToontownGlobals.P_InvalidIndex: + return TTLocalizer.InviteAcceptInvalidError + elif retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.InviteRejectAllOk + else: + return TTLocalizer.CatalogAcceptGeneralError % retcode + + def output(self, store = -1): + return 'InviteInfo %s' % str(self) diff --git a/toontown/parties/InviteVisual.py b/toontown/parties/InviteVisual.py new file mode 100755 index 00000000..6bfd04d9 --- /dev/null +++ b/toontown/parties/InviteVisual.py @@ -0,0 +1,111 @@ +from datetime import datetime +import calendar +from direct.gui.DirectGui import DirectFrame, DirectLabel +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil +from direct.fsm.FSM import FSM +from toontown.parties import PartyGlobals +from toontown.parties import PartyUtils + +class InviteVisual(DirectFrame): + notify = directNotify.newCategory('InviteVisual') + + def __init__(self, parent): + DirectFrame.__init__(self, parent=parent) + self.gui = loader.loadModel('phase_5.5/models/parties/partyInviteGUI') + self.inviteThemesIdToInfo = {PartyGlobals.InviteTheme.Birthday: (self.gui.find('**/birthdayPage'), TTLocalizer.PartyPlannerBirthdayTheme, (0.0, + 0.0, + 0.0, + 1.0)), + PartyGlobals.InviteTheme.GenericMale: (self.gui.find('**/genericMalePage'), TTLocalizer.PartyPlannerGenericMaleTheme, (0.7, + 0.7, + 0.0, + 1.0)), + PartyGlobals.InviteTheme.GenericFemale: (self.gui.find('**/genericFemalePage'), TTLocalizer.PartyPlannerGenericFemaleTheme, (0.0, + 1.0, + 0.5, + 1.0)), + PartyGlobals.InviteTheme.Racing: (self.gui.find('**/racingPage'), TTLocalizer.PartyPlannerRacingTheme, (0.0, + 0.0, + 0.0, + 1.0)), + PartyGlobals.InviteTheme.Valentoons: (self.gui.find('**/valentinePage1'), TTLocalizer.PartyPlannerValentoonsTheme, (0.0, + 0.0, + 0.0, + 1.0)), + PartyGlobals.InviteTheme.VictoryParty: (self.gui.find('**/victoryPartyPage'), TTLocalizer.PartyPlannerVictoryPartyTheme, (0.0, + 0.0, + 0.0, + 1.0)), + PartyGlobals.InviteTheme.Winter: (self.gui.find('**/winterPartyPage1'), TTLocalizer.PartyPlannerWinterPartyTheme, (1.0, + 1.0, + 1.0, + 1.0))} + self.inviteThemeBackground = DirectFrame(parent=self, image=self.inviteThemesIdToInfo[0][0], relief=None) + self.whosePartyLabel = DirectLabel(parent=self, relief=None, pos=self.gui.find('**/who_locator').getPos(), text='.', text_scale=0.067, textMayChange=True) + self.activityTextLabel = DirectLabel(parent=self, relief=None, text='.\n.\n.\n.', pos=self.gui.find('**/what_locator').getPos(), text_scale=TTLocalizer.IVactivityTextLabel, textMayChange=True) + self.whenTextLabel = DirectLabel(parent=self, relief=None, text='.\n.\n.', pos=self.gui.find('**/when_locator').getPos(), text_scale=TTLocalizer.IVwhenTextLabel, textMayChange=True) + self.noFriends = False + return None + + def setNoFriends(self, noFriends): + self.noFriends = noFriends + self.inviteThemeBackground.show() + + def updateInvitation(self, hostsName, partyInfo): + self.partyInfo = partyInfo + hostsName = TTLocalizer.GetPossesive(hostsName) + self.whosePartyLabel['text'] = TTLocalizer.PartyPlannerInvitationWhoseSentence % hostsName + if self.partyInfo.isPrivate: + publicPrivateText = TTLocalizer.PartyPlannerPrivate.lower() + else: + publicPrivateText = TTLocalizer.PartyPlannerPublic.lower() + activities = self.getActivitiesFormattedCorrectly() + if self.noFriends: + self.activityTextLabel['text'] = TTLocalizer.PartyPlannerInvitationThemeWhatSentenceNoFriends % (publicPrivateText, activities) + else: + self.activityTextLabel['text'] = TTLocalizer.PartyPlannerInvitationThemeWhatSentence % (publicPrivateText, activities) + if self.noFriends: + self.whenTextLabel['text'] = TTLocalizer.PartyPlannerInvitationWhenSentenceNoFriends % (PartyUtils.formatDate(self.partyInfo.startTime.year, self.partyInfo.startTime.month, self.partyInfo.startTime.day), PartyUtils.formatTime(self.partyInfo.startTime.hour, self.partyInfo.startTime.minute)) + else: + self.whenTextLabel['text'] = TTLocalizer.PartyPlannerInvitationWhenSentence % (PartyUtils.formatDate(self.partyInfo.startTime.year, self.partyInfo.startTime.month, self.partyInfo.startTime.day), PartyUtils.formatTime(self.partyInfo.startTime.hour, self.partyInfo.startTime.minute)) + self.changeTheme(partyInfo.inviteTheme) + + def getActivitiesFormattedCorrectly(self): + activitiesString = '' + activityList = [] + for activity in self.partyInfo.activityList: + text = TTLocalizer.PartyActivityNameDict[activity.activityId]['invite'] + if text not in activityList: + activityList.append(text) + + if len(activityList) == 1: + return '\n' + TTLocalizer.PartyPlannerInvitationThemeWhatActivitiesBeginning + activityList[0] + conjunction = TTLocalizer.PartyActivityConjunction + for activity in activityList: + activitiesString = '%s, %s' % (activitiesString, activity) + + activitiesString = activitiesString[2:] + activitiesString = activitiesString[:activitiesString.rfind(',')] + conjunction + activitiesString[activitiesString.rfind(',') + 1:] + activitiesString = TTLocalizer.PartyPlannerInvitationThemeWhatActivitiesBeginning + activitiesString + return self.insertCarriageReturn(activitiesString) + + def insertCarriageReturn(self, stringLeft, stringDone = ''): + desiredNumberOfCharactersInLine = 42 + if len(stringLeft) < desiredNumberOfCharactersInLine: + return stringDone + '\n' + stringLeft + for i in xrange(desiredNumberOfCharactersInLine - 6, len(stringLeft)): + if stringLeft[i] == ' ': + return self.insertCarriageReturn(stringLeft[i:], stringDone + '\n' + stringLeft[:i]) + + return stringDone + '\n' + stringLeft + + def changeTheme(self, newTheme): + self.inviteThemeBackground['image'] = self.inviteThemesIdToInfo[newTheme][0] + self.whosePartyLabel['text_fg'] = self.inviteThemesIdToInfo[newTheme][2] + self.activityTextLabel['text_fg'] = self.inviteThemesIdToInfo[newTheme][2] + self.whenTextLabel['text_fg'] = self.inviteThemesIdToInfo[newTheme][2] + + def close(self): + self.destroy() + del self diff --git a/toontown/parties/JellybeanRewardGui.py b/toontown/parties/JellybeanRewardGui.py new file mode 100755 index 00000000..866b5089 --- /dev/null +++ b/toontown/parties/JellybeanRewardGui.py @@ -0,0 +1,91 @@ +from pandac.PandaModules import TextNode +from direct.gui.DirectGui import DirectFrame +from direct.gui.DirectGui import DirectButton +from direct.gui.DirectGui import DirectLabel +from direct.gui import DirectGuiGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class JellybeanRewardGui(DirectFrame): + notify = directNotify.newCategory('JellybeanRewardGui') + PreCountdownDelay = 1.0 + CountDownRate = 0.2 + JarLabelTextColor = (0.95, 0.95, 0.0, 1.0) + JarLabelMaxedTextColor = (1.0, 0.0, 0.0, 1.0) + + def __init__(self, doneEvent): + self.doneEvent = doneEvent + DirectFrame.__init__(self) + self.reparentTo(aspect2d) + self.setPos(0.0, 0.0, 0.16) + self.stash() + publicPartyGui = loader.loadModel('phase_4/models/parties/publicPartyGUI') + self.frame = DirectFrame(parent=self, geom=publicPartyGui.find('**/activities_background'), geom_pos=(-0.8, 0.0, 0.2), geom_scale=2.0, relief=None) + self.earnedLabel = DirectLabel(parent=self, relief=None, text=str(0), text_align=TextNode.ACenter, text_pos=(0.0, -0.07), text_scale=0.2, text_fg=(0.95, 0.95, 0.0, 1.0), text_font=ToontownGlobals.getSignFont(), textMayChange=True, image=DirectGuiGlobals.getDefaultDialogGeom(), image_scale=(0.33, 1.0, 0.33), pos=(-0.3, 0.0, 0.2), scale=0.9) + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + jarImage = purchaseModels.find('**/Jar') + self.jarLabel = DirectLabel(parent=self, relief=None, text=str(0), text_align=TextNode.ACenter, text_pos=(0.0, -0.07), text_scale=0.2, text_fg=JellybeanRewardGui.JarLabelTextColor, text_font=ToontownGlobals.getSignFont(), textMayChange=True, image=jarImage, scale=0.7, pos=(0.3, 0.0, 0.17)) + purchaseModels.removeNode() + del purchaseModels + jarImage.removeNode() + del jarImage + self.messageLabel = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_wordwrap=16.0, text_scale=0.07, pos=(-0.52, 0.0, -0.1), textMayChange=True) + self.doubledJellybeanLabel = DirectLabel(parent=self, relief=None, text=TTLocalizer.PartyRewardDoubledJellybean, text_align=TextNode.ACenter, text_wordwrap=12.0, text_scale=0.09, text_fg=(1.0, 0.125, 0.125, 1.0), pos=(0.0, 0.0, -0.465), textMayChange=False) + self.doubledJellybeanLabel.hide() + self.closeButton = DirectButton(parent=self, relief=None, text=TTLocalizer.PartyJellybeanRewardOK, text_align=TextNode.ACenter, text_scale=0.065, text_pos=(0.0, -0.625), geom=(publicPartyGui.find('**/startButton_up'), + publicPartyGui.find('**/startButton_down'), + publicPartyGui.find('**/startButton_rollover'), + publicPartyGui.find('**/startButton_inactive')), geom_pos=(-0.39, 0.0, 0.125), command=self._close) + publicPartyGui.removeNode() + del publicPartyGui + self.countSound = base.loadSfx('phase_13/audio/sfx/tick_counter_short.ogg') + self.overMaxSound = base.loadSfx('phase_13/audio/sfx/tick_counter_overflow.ogg') + return + + def showReward(self, earnedAmount, jarAmount, message): + JellybeanRewardGui.notify.debug('showReward( earnedAmount=%d, jarAmount=%d, ...)' % (earnedAmount, jarAmount)) + self.earnedCount = earnedAmount + self.earnedLabel['text'] = str(self.earnedCount) + self.jarCount = jarAmount + self.jarMax = base.localAvatar.getMaxMoney() + self.jarLabel['text'] = str(self.jarCount) + self.jarLabel['text_fg'] = JellybeanRewardGui.JarLabelTextColor + self.messageLabel['text'] = message + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_DAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_PARTIES_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_PARTIES_HOLIDAY_MONTH): + self.doubledJellybeanLabel.show() + else: + self.doubledJellybeanLabel.hide() + self.unstash() + taskMgr.doMethodLater(JellybeanRewardGui.PreCountdownDelay, self.transferOneJellybean, 'JellybeanRewardGuiTransferOneJellybean', extraArgs=[]) + + def transferOneJellybean(self): + if self.earnedCount == 0: + return + self.earnedCount -= 1 + self.earnedLabel['text'] = str(self.earnedCount) + self.jarCount += 1 + if self.jarCount <= self.jarMax: + self.jarLabel['text'] = str(self.jarCount) + elif self.jarCount > self.jarMax: + self.jarLabel['text_fg'] = JellybeanRewardGui.JarLabelMaxedTextColor + if self.jarCount <= self.jarMax: + base.playSfx(self.countSound) + else: + base.playSfx(self.overMaxSound) + taskMgr.doMethodLater(JellybeanRewardGui.CountDownRate, self.transferOneJellybean, 'JellybeanRewardGuiTransferOneJellybean', extraArgs=[]) + + def _close(self): + taskMgr.remove('JellybeanRewardGuiTransferOneJellybean') + self.stash() + messenger.send(self.doneEvent) + + def destroy(self): + taskMgr.remove('JellybeanRewardGuiTransferOneJellybean') + del self.countSound + del self.overMaxSound + self.frame.destroy() + self.earnedLabel.destroy() + self.jarLabel.destroy() + self.messageLabel.destroy() + self.closeButton.destroy() + DirectFrame.destroy(self) diff --git a/toontown/parties/JukeboxGui.py b/toontown/parties/JukeboxGui.py new file mode 100755 index 00000000..94fd4e16 --- /dev/null +++ b/toontown/parties/JukeboxGui.py @@ -0,0 +1,168 @@ +from panda3d.core import * +from direct.showbase.DirectObject import DirectObject +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel +from direct.gui.DirectGui import DirectScrolledListItem, DirectScrolledList +from direct.gui import DirectGuiGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyUtils + +class JukeboxGui(DirectObject): + notify = directNotify.newCategory('JukeboxGui') + CLOSE_EVENT = 'JukeboxGui_CLOSE_EVENT' + SONG_SELECT_EVENT = 'JukeboxGui_SONG_SELECT_EVENT' + QUEUE_SELECT_EVENT = 'JukeboxGui_QUEUE_SELECT_EVENT' + ADD_SONG_CLICK_EVENT = 'JukeboxGui_ADD_SONG_CLICK_EVENT' + MOVE_TO_TOP_CLICK_EVENT = 'JukeboxGUI_MOVE_TO_TOP_EVENT' + + def __init__(self, phaseToMusicData): + self._loaded = False + self._timerGui = None + self._windowFrame = None + self.phaseToMusicData = phaseToMusicData + return + + def load(self): + if self.isLoaded(): + return + guiNode = loader.loadModel('phase_13/models/parties/jukeboxGUI') + self._timerGui = PartyUtils.getNewToontownTimer() + self._windowFrame = DirectFrame(image=guiNode.find('**/background'), relief=None, pos=(0, 0, 0), scale=0.7) + self._songFrame = DirectFrame(image=guiNode.find('**/songTitle_background'), parent=self._windowFrame, relief=None) + self._currentlyPlayingLabel = self.__createLabel(guiNode, 'currentlyPlaying', parent=self._windowFrame, text=TTLocalizer.JukeboxCurrentlyPlayingNothing, scale=TTLocalizer.JGcurrentlyPlayingLabel) + self._songNameLabel = self.__createLabel(guiNode, 'songName', parent=self._windowFrame, text=TTLocalizer.JukeboxCurrentSongNothing, scale=TTLocalizer.JGsongNameLabel) + self._queueList, self._queueLabel = self.__createLabeledScrolledList(guiNode, 'queue', label=TTLocalizer.JukeboxQueueLabel, parent=self._windowFrame) + self._songsList, self._songsLabel = self.__createLabeledScrolledList(guiNode, 'songs', label=TTLocalizer.JukeboxSongsLabel, parent=self._windowFrame) + pos = guiNode.find('**/addButton_text_locator').getPos() + self._addSongButton = self.__createButton(guiNode, 'addSongButton', parent=self._windowFrame, command=self.__handleAddSongButtonClick, image3_color=Vec4(0.6, 0.6, 0.6, 0.6), text=TTLocalizer.JukeboxAddSong, text_align=TextNode.ACenter, text_pos=(pos[0], pos[2]), text_scale=TTLocalizer.JGaddSongButton) + self._closeButton = self.__createButton(guiNode, 'can_cancelButton', parent=self._windowFrame, command=self.__handleCloseButtonClick) + pos = guiNode.find('**/close_text_locator').getPos() + self._closeButton = self.__createButton(guiNode, 'close', parent=self._windowFrame, command=self.__handleCloseButtonClick, text=TTLocalizer.JukeboxClose, text_align=TextNode.ACenter, text_pos=(pos[0], pos[2]), text_scale=0.08) + self._moveToTopButton = self.__createButton(guiNode, 'moveToTop', command=self.__handleMoveToTopButtonClick) + guiNode.removeNode() + self._loaded = True + return + + def __createButton(self, guiNode, imagePrefix, parent = hidden, **kwargs): + return DirectButton(parent=parent, relief=None, image=(guiNode.find('**/%s_up' % imagePrefix), guiNode.find('**/%s_down' % imagePrefix), guiNode.find('**/%s_rollover' % imagePrefix)), **kwargs) + + def __createLabel(self, guiNode, locatorPrefix, parent = hidden, **kwargs): + return DirectLabel(parent=parent, relief=None, pos=guiNode.find('**/%s_text_locator' % locatorPrefix).getPos(), **kwargs) + + def __createLabeledScrolledList(self, guiNode, namePrefix, label, parent = hidden, **kwargs): + return (DirectScrolledList(parent=parent, relief=None, incButton_image=(guiNode.find('**/%sButtonDown_up' % namePrefix), guiNode.find('**/%sButtonDown_down' % namePrefix), guiNode.find('**/%sButtonDown_rollover' % namePrefix)), incButton_relief=None, incButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), decButton_image=(guiNode.find('**/%sButtonUp_up' % namePrefix), guiNode.find('**/%sButtonUp_down' % namePrefix), guiNode.find('**/%sButtonUp_rollover' % namePrefix)), decButton_relief=None, decButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), image=guiNode.find('**/%s_background' % namePrefix), itemFrame_relief=None, itemFrame_pos=guiNode.find('**/%sList_locator' % namePrefix).getPos(), itemFrame_scale=0.07, numItemsVisible=TTLocalizer.JGnumItemsVisible, items=[], **kwargs), self.__createLabel(guiNode, namePrefix, parent=parent, text=label, text_fg=(0.5, 1.0, 1.0, 1.0), text_shadow=(0.0, 0.0, 0.0, 1.0), scale=0.12)) + + def enable(self, timer = 0): + if not self.isLoaded(): + self.load() + phase = 13 + tunes = self.phaseToMusicData[13] + for filename, info in tunes.items(): + self.addToSongList(info[0], phase, filename, info[1]) + + for phase, tunes in self.phaseToMusicData.items(): + if phase == 13: + continue + for filename, info in tunes.items(): + self.addToSongList(info[0], phase, filename, info[1]) + + self._windowFrame.show() + if timer > 0: + self._timerGui.setTime(timer) + self._timerGui.countdown(timer) + self._timerGui.show() + + def disable(self): + self._windowFrame.hide() + self._timerGui.hide() + + def unload(self): + self.ignoreAll() + if not self.isLoaded(): + return + if self._windowFrame is not None: + self._windowFrame.destroy() + self._windowFrame = None + self._moveToTopButton.destroy() + del self._moveToTopButton + if self._timerGui is not None: + self._timerGui.destroy() + self._timerGui = None + self._loaded = False + return + + def isLoaded(self): + return self._loaded + + def addToSongList(self, text, phase, filename, length): + listItem = DirectScrolledListItem(relief=None, parent=self._songsList, text=text, text_align=TextNode.ALeft, text_pos=(0.0, 0.0, 0.0), text_scale=TTLocalizer.JGlistItem, text_fg=(0.0, 0.0, 0.0, 1.0), text1_fg=(1.0, 1.0, 1.0, 1.0), text1_bg=(0.0, 0.0, 1.0, 1.0), text2_fg=(0.0, 0.0, 1.0, 1.0), text3_bg=(0.0, 0.8, 0.0, 1.0), command=self.__handleSongListItemSelect, extraArgs=[]) + listItem.setPythonTag('value', (phase, filename, length)) + self._songsList.addItem(listItem) + return listItem + + def __handleCloseButtonClick(self): + self.disable() + messenger.send(JukeboxGui.CLOSE_EVENT) + + def __handleMoveToTopButtonClick(self): + messenger.send(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT) + + def __handleSongListItemSelect(self): + pass + + def __handleAddSongButtonClick(self): + if hasattr(self._songsList, 'currentSelected'): + song = self._songsList.currentSelected + messenger.send(JukeboxGui.ADD_SONG_CLICK_EVENT, [song['text'], song.getPythonTag('value')]) + + def disableAddSongButton(self): + self._addSongButton['state'] = DirectGuiGlobals.DISABLED + + def enableAddSongButton(self): + self._addSongButton['state'] = DirectGuiGlobals.NORMAL + + def addSongToQueue(self, text, highlight = False, moveToTopButton = False): + listItem = DirectLabel(relief=None, parent=self._queueList, text=text, text_align=TextNode.ALeft, text_pos=(0.0, 0.0, 0.0), text_scale=TTLocalizer.JGlistItem) + self._queueList.addItem(listItem) + if highlight: + listItem['text_fg'] = (0.0, 0.5, 0.0, 1.0) + self._addSongButton['text'] = TTLocalizer.JukeboxReplaceSong + listItem.setPythonTag('highlighted', True) + if moveToTopButton and len(self._queueList['items']) > 1: + self._moveToTopButton.reparentTo(listItem) + self._moveToTopButton.setScale(self._windowFrame, 1.0) + self._moveToTopButton.setPos(10.0, 0.0, 0.25) + self._queueList.scrollTo(len(self._queueList['items']) - 1) + return listItem + + def popSongFromQueue(self): + if len(self._queueList['items']) > 0: + item = self._queueList['items'][0] + self._queueList.removeItem(item) + if self._moveToTopButton.getParent() == item: + self._moveToTopButton.reparentTo(hidden) + if self._moveToTopButton.getParent() == item: + self._moveToTopButton.reparentTo(hidden) + if item.getPythonTag('highlighted') == True: + self._addSongButton['text'] = TTLocalizer.JukeboxAddSong + item.removeNode() + return item + return None + + def setSongCurrentlyPlaying(self, phase, filename): + songs = self.phaseToMusicData.get(phase / 1) + if songs: + songName = songs.get(filename) + if songName: + self._songNameLabel['text'] = songName + self._currentlyPlayingLabel['text'] = TTLocalizer.JukeboxCurrentlyPlaying + + def clearSongCurrentlyPlaying(self): + self._currentlyPlayingLabel['text'] = TTLocalizer.JukeboxCurrentlyPlayingNothing + self._songNameLabel['text'] = TTLocalizer.JukeboxCurrentSongNothing + + def pushQueuedItemToTop(self, item): + self._queueList['items'].remove(item) + self._queueList['items'].insert(0, item) + if self._moveToTopButton.getParent() == item: + self._moveToTopButton.reparentTo(hidden) + self._queueList.refresh() diff --git a/toontown/parties/KeyCodes.py b/toontown/parties/KeyCodes.py new file mode 100755 index 00000000..d3210169 --- /dev/null +++ b/toontown/parties/KeyCodes.py @@ -0,0 +1,133 @@ +from panda3d.core import * +from direct.showbase.DirectObject import DirectObject +ARROW_KEYCODE_MAP = {'arrow_up': 'u', + 'arrow_down': 'd', + 'arrow_left': 'l', + 'arrow_right': 'r'} +KEYCODE_TIMEOUT_SECONDS = 1.5 + +class KeyCodes(DirectObject): + notify = directNotify.newCategory('KeyCodes') + PATTERN_MATCH_EVENT = 'KeyCodes-PATTERN_MATCH' + PATTERN_NO_MATCH_EVENT = 'KeyCodes-PATTERN_NO_MATCH' + KEY_DOWN_EVENT = 'KeyCodes-KEY_DOWN_EVENT' + KEY_UP_EVENT = 'KeyCodes-KEY_UP_EVENT' + CLEAR_CODE_EVENT = 'KeyCodes-CLEAR_CODE_EVENT' + + def __init__(self, keyMap = ARROW_KEYCODE_MAP, patterns = None, timeout = KEYCODE_TIMEOUT_SECONDS): + self._keyMap = keyMap + self._timeout = timeout + self._keyCode = '' + self._keyCodeCount = 0 + self._keyCodeTime = 0.0 + self._patterns = [] + self._patternLimit = 0 + self._enabled = False + self._keyDown = None + self._keysPressed = 0 + self.listenForPatterns(patterns) + return + + def destroy(self): + self.disable() + self.ignoreAll() + self._patterns = [] + del self._patterns + del self._keyMap + del self._timeout + + def listenForPatterns(self, patterns): + self._patterns = patterns + for pattern in self._patterns: + if len(pattern) > self._patternLimit: + self._patternLimit = len(pattern) + + if self._enabled: + self.disable() + self.enable() + + def isAnyKeyPressed(self): + return self._keysPressed > 0 + + def getCurrentInputLength(self): + return self._keyCodeCount + 1 + + def getLargestPatternLength(self): + return self._patternLimit + + def getPossibleMatchesList(self): + return [ p for p in self._patterns if p.startswith(self._keyCode) ] + + def reset(self): + self._keyCode = '' + self._keyCodeCount = 0 + self._keyCodeTime = 0.0 + + def enable(self): + if not self._enabled: + self.notify.debug('Key codes enabled') + self._enabled = True + self.__enableControls() + + def disable(self): + if self._enabled: + self.notify.debug('Key codes disabled') + self.__disableControls() + self.reset() + self._enabled = False + self._keyDown = None + self._keysPressed = 0 + return + + def __enableControls(self): + for key in self._keyMap.keys(): + self.__acceptKeyDown(key) + self.__acceptKeyUp(key) + + def __disableControls(self): + self.ignoreAll() + + def __acceptKeyDown(self, key): + self.accept(key, self.__handleKeyDown, [key]) + + def __acceptKeyUp(self, key): + self.accept(key + '-up', self.__handleKeyUp, [key]) + + def __handleKeyDown(self, key): + self._keysPressed += 1 + if self._keyDown is None and self._keysPressed == 1: + self.__updateElapsedTime() + messenger.send(KeyCodes.KEY_DOWN_EVENT, [self._keyMap[key], self._keyCodeCount]) + self._keyCode += self._keyMap[key] + self._keyCodeCount += 1 + self._keyDown = key + self.__checkForPattern() + else: + messenger.send(KeyCodes.KEY_DOWN_EVENT, [-1, -1]) + return + + def __handleKeyUp(self, key): + arg = -1 + if self._keysPressed > 0: + self._keysPressed -= 1 + if self._keyDown == key and self._keysPressed == 0: + arg = self._keyMap[key] + if self._keysPressed == 0: + self._keyDown = None + messenger.send(KeyCodes.KEY_UP_EVENT, [arg]) + return + + def __updateElapsedTime(self): + if self._keyCodeTime != 0.0 and globalClock.getFrameTime() - self._keyCodeTime >= self._timeout: + self.notify.debug('Key code timed out. Resetting...') + self.reset() + messenger.send(KeyCodes.CLEAR_CODE_EVENT) + self._keyCodeTime = globalClock.getFrameTime() + + def __checkForPattern(self): + if self._keyCode in self._patterns: + messenger.send(KeyCodes.PATTERN_MATCH_EVENT, [self._keyCode]) + self.reset() + elif self._keyCodeCount == self._patternLimit or len(self.getPossibleMatchesList()) == 0: + messenger.send(KeyCodes.PATTERN_NO_MATCH_EVENT) + self.reset() diff --git a/toontown/parties/KeyCodesGui.py b/toontown/parties/KeyCodesGui.py new file mode 100755 index 00000000..dbe082c1 --- /dev/null +++ b/toontown/parties/KeyCodesGui.py @@ -0,0 +1,117 @@ +from panda3d.core import * +from direct.showbase.DirectObject import DirectObject +from direct.gui.DirectGui import DirectButton +from direct.gui.OnscreenText import OnscreenText +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from toontown.parties.KeyCodes import KeyCodes, KEYCODE_TIMEOUT_SECONDS +KEY_TO_INDEX = {'u': 0, + 'r': 1, + 'd': 2, + 'l': 3} + +class KeyCodesGui(DirectObject): + notify = directNotify.newCategory('KeyCodesGui') + TIMEOUT_TASK = 'KeyCodeGui_TIMEOUT_TASK' + + def __init__(self, keyCodes, yOffset = 0.55, keyToIndex = KEY_TO_INDEX): + self._keyCodes = keyCodes + self._keyToIndex = keyToIndex + self._arrowWidth = 0.18 + self._arrowSpaceInBetween = 0.05 + self._yOffset = yOffset + self._danceMoveLabel = None + self._arrowNodes = [] + self.timeoutTask = None + return + + def load(self): + matchingGameGui = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + minnieArrow = matchingGameGui.find('**/minnieArrow') + minnieArrow.setScale(0.6) + minnieArrow.setZ(self._yOffset + 0.2) + maxLength = self._keyCodes.getLargestPatternLength() + for i in xrange(maxLength): + arrow = minnieArrow.copyTo(hidden) + self._arrowNodes.append(arrow) + + matchingGameGui.removeNode() + self._danceMoveLabel = OnscreenText(parent=aspect2d, text='', pos=(0, self._yOffset), scale=0.15, align=TextNode.ACenter, font=ToontownGlobals.getSignFont(), fg=Vec4(1, 1, 1, 1), mayChange=True) + self._danceMoveLabel.hide() + self.enable() + + def unload(self): + self.disable() + for arrow in self._arrowNodes: + arrow.removeNode() + arrow = None + + self._arrowNodes = [] + if self._danceMoveLabel is not None: + self._danceMoveLabel.removeNode() + self._danceMoveLabel = None + return + + def enable(self): + self.notify.debug('KeyCodeGui enabled.') + self.accept(KeyCodes.KEY_DOWN_EVENT, self.__handleKeyDown) + self.accept(KeyCodes.CLEAR_CODE_EVENT, self.hideAll) + + def disable(self): + self.notify.debug('KeyCodeGui disabled.') + self.__stopTimeout() + self.ignoreAll() + + def hideArrows(self, startIndex = 0): + length = len(self._arrowNodes) + if startIndex < length: + for i in xrange(startIndex, length): + self._arrowNodes[i].reparentTo(hidden) + + def hideAll(self, startIndex = 0): + self.hideArrows(startIndex) + if self._danceMoveLabel: + self._danceMoveLabel.hide() + + def showArrow(self, index, key): + self._arrowNodes[index].setR(-(90 - 90 * self._keyToIndex[key])) + self._arrowNodes[index].setColor(1, 1, 1, 1) + self.__centerArrows() + self._arrowNodes[index].reparentTo(aspect2d) + self.hideAll(index + 1) + self.__startTimeout() + + def showText(self, text = ''): + self.notify.debug('"Showing text "%s"' % text) + self._danceMoveLabel['text'] = text + self._danceMoveLabel.show() + + def setColor(self, r, g, b): + for arrow in self._arrowNodes: + arrow.setColor(r, g, b) + + self._danceMoveLabel.setColorScale(r, g, b, 1) + + def __startTimeout(self): + self.__stopTimeout() + self.timeoutTask = taskMgr.doMethodLater(KEYCODE_TIMEOUT_SECONDS, self.__handleTimeoutTask, KeyCodesGui.TIMEOUT_TASK) + + def __stopTimeout(self): + if self.timeoutTask is not None: + taskMgr.remove(self.timeoutTask) + self.timeoutTask = None + return + + def __handleTimeoutTask(self, task): + self.hideAll() + return Task.done + + def __centerArrows(self): + length = self._keyCodes.getCurrentInputLength() + for i in xrange(length): + x = -(length * self._arrowWidth * 0.5) + self._arrowWidth * (i + 0.5) + self._arrowNodes[i].setX(x) + + def __handleKeyDown(self, key, index): + if index >= 0: + self.showArrow(index, key) diff --git a/toontown/parties/Party.py b/toontown/parties/Party.py new file mode 100755 index 00000000..69d15adc --- /dev/null +++ b/toontown/parties/Party.py @@ -0,0 +1,254 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from direct.distributed.ClockDelta import * +from toontown.hood import Place +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.task.Task import Task +from toontown.toonbase import TTLocalizer +import random +from direct.showbase import PythonUtil +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs, TLNull +from toontown.hood import Place +from toontown.parties import PartyPlanner +from toontown.parties.DistributedParty import DistributedParty + +class Party(Place.Place): + notify = DirectNotifyGlobal.directNotify.newCategory('Party') + + def __init__(self, loader, avId, zoneId, parentFSMState, doneEvent): + Place.Place.__init__(self, None, doneEvent) + self.id = PartyHood + self.avId = avId + self.zoneId = zoneId + self.loader = loader + self.musicShouldPlay = False + self.partyPlannerDoneEvent = 'partyPlannerGuiDone' + self.fsm = ClassicFSM.ClassicFSM('Party', [State.State('init', self.enterInit, self.exitInit, ['final', 'teleportIn', 'walk']), + State.State('walk', self.enterWalk, self.exitWalk, ['final', + 'sit', + 'stickerBook', + 'options', + 'quest', + 'fishing', + 'stopped', + 'push', + 'activity', + 'teleportOut']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut']), + State.State('sit', self.enterSit, self.exitSit, ['walk']), + State.State('push', self.enterPush, self.exitPush, ['walk']), + State.State('partyPlanning', self.enterPartyPlanning, self.exitPartyPlanning, ['teleportOut']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'sit', + 'quest', + 'fishing', + 'stopped', + 'activity', + 'push', + 'teleportOut']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', 'partyPlanning']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', 'walk', 'final']), + State.State('died', self.enterDied, self.exitDied, ['walk', 'final']), + State.State('final', self.enterFinal, self.exitFinal, ['teleportIn']), + State.State('quest', self.enterQuest, self.exitQuest, ['walk']), + State.State('fishing', self.enterFishing, self.exitFishing, ['walk', 'stopped']), + State.State('activity', self.enterActivity, self.exitActivity, ['walk', 'stopped']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk'])], 'init', 'final') + self.fsm.enterInitialState() + self.doneEvent = doneEvent + self.parentFSMState = parentFSMState + self.isPartyEnding = False + self.accept('partyStateChanged', self.setPartyState) + return + + def delete(self): + self.unload() + + def load(self): + self.fog = Fog('PartyFog') + Place.Place.load(self) + if hasattr(base.localAvatar, 'aboutToPlanParty') and base.localAvatar.aboutToPlanParty: + if not hasattr(self, 'partyPlanner') or self.partyPlanner is None: + self.partyPlanner = PartyPlanner.PartyPlanner(self.partyPlannerDoneEvent) + self.parentFSMState.addChild(self.fsm) + return + + def unload(self): + if hasattr(self, 'partyPlanner'): + self.ignore(self.partyPlannerDoneEvent) + self.partyPlanner.close() + del self.partyPlanner + self.fog = None + self.ignoreAll() + self.parentFSMState.removeChild(self.fsm) + del self.fsm + Place.Place.unload(self) + return + + def enter(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + if config.GetBool('want-party-telemetry-limiter', 1): + limiter = TLGatherAllAvs('Party', RotationLimitToH) + else: + limiter = TLNull() + self._telemLimiter = limiter + self.loader.hood.startSky() + for i in self.loader.nodeList: + self.loader.enterAnimatedProps(i) + + self.loader.geom.reparentTo(render) + self.fsm.request(requestStatus['how'], [requestStatus]) + self.playMusic() + + def playMusic(self): + if not hasattr(base, 'partyHasJukebox') or not base.partyHasJukebox: + base.playMusic(self.loader.music, looping=1, volume=1) + + def exit(self): + base.localAvatar.stopChat() + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + self.loader.geom.reparentTo(hidden) + for i in self.loader.nodeList: + self.loader.exitAnimatedProps(i) + + self._telemLimiter.destroy() + del self._telemLimiter + self.loader.hood.stopSky() + render.setFogOff() + base.cr.cache.flush() + self.loader.music.stop() + self.notify.debug('exit') + self.ignoreAll() + + def __setZoneId(self, zoneId): + self.zoneId = zoneId + + def enterInit(self): + pass + + def exitInit(self): + pass + + def enterPartyPlanning(self, requestStatus): + base.localAvatar.aboutToPlanParty = False + self.accept(self.partyPlannerDoneEvent, self.handlePartyPlanningDone) + + def handlePartyPlanningDone(self): + self.ignore(self.partyPlannerDoneEvent) + self.partyPlanner.close() + del self.partyPlanner + messenger.send('deallocateZoneIdFromPlannedParty', [base.localAvatar.zoneId]) + hoodId = base.localAvatar.lastHood + self.fsm.request('teleportOut', [{'avId': -1, + 'zoneId': hoodId, + 'shardId': None, + 'how': 'teleportIn', + 'hoodId': hoodId, + 'loader': 'safeZoneLoader', + 'where': 'playground'}]) + return + + def exitPartyPlanning(self): + pass + + def enterTeleportIn(self, requestStatus): + self._partyTiToken = None + if hasattr(base, 'distributedParty'): + self.__updateLocalAvatarTeleportIn(requestStatus) + elif hasattr(base.localAvatar, 'aboutToPlanParty') and base.localAvatar.aboutToPlanParty: + self.__updateLocalAvatarTeleportIn(requestStatus) + else: + self.acceptOnce(DistributedParty.generatedEvent, self.__updateLocalAvatarTeleportIn, [requestStatus]) + return + + def exitTeleportIn(self): + Place.Place.exitTeleportIn(self) + self.removeSetZoneCompleteCallback(self._partyTiToken) + + def __updateLocalAvatarTeleportIn(self, requestStatus): + self.ignore(DistributedParty.generatedEvent) + if hasattr(base, 'distributedParty'): + x, y, z = base.distributedParty.getClearSquarePos() + self.accept('generate-' + str(base.distributedParty.partyInfo.hostId), self.__setPartyHat) + self.__setPartyHat() + else: + x, y, z = (0.0, 0.0, 0.1) + base.localAvatar.detachNode() + base.localAvatar.setPos(render, x, y, z) + base.localAvatar.lookAt(0.0, 0.0, 0.1) + base.localAvatar.setScale(1, 1, 1) + Place.Place.enterTeleportIn(self, requestStatus) + if hasattr(base, 'distributedParty') and base.distributedParty: + self.setPartyState(base.distributedParty.getPartyState()) + if hasattr(base.localAvatar, 'aboutToPlanParty') and base.localAvatar.aboutToPlanParty: + self._partyTiToken = self.addSetZoneCompleteCallback(Functor(self._partyTeleportInPostZoneComplete, requestStatus), 150) + + def _partyTeleportInPostZoneComplete(self, requestStatus): + self.nextState = 'partyPlanning' + + def __setPartyHat(self, doId = None): + if hasattr(base, 'distributedParty'): + if base.distributedParty.partyInfo.hostId in base.cr.doId2do: + base.cr.doId2do[base.distributedParty.partyInfo.hostId].setPartyHat() + + def enterTeleportOut(self, requestStatus): + Place.Place.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + avId = requestStatus['avId'] + shardId = requestStatus['shardId'] + if hoodId == ToontownGlobals.PartyHood and zoneId == self.getZoneId() and shardId == None: + self.fsm.request('teleportIn', [requestStatus]) + elif hoodId == ToontownGlobals.MyEstate: + self.doneStatus = requestStatus + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent, [self.doneStatus]) + return + + def goHomeFailed(self, task): + self.notifyUserGoHomeFailed() + self.doneStatus['avId'] = -1 + self.doneStatus['zoneId'] = self.getZoneId() + self.fsm.request('teleportIn', [self.doneStatus]) + return Task.done + + def exitTeleportOut(self): + Place.Place.exitTeleportOut(self) + + def getZoneId(self): + if self.zoneId: + return self.zoneId + else: + self.notify.warning('no zone id available') + + def enterActivity(self, setAnimState = True): + if setAnimState: + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(False) + base.localAvatar.laffMeter.start() + + def exitActivity(self): + base.localAvatar.setTeleportAvailable(True) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + + def setPartyState(self, partyState): + self.isPartyEnding = partyState + + def handleTeleportQuery(self, fromAvatar, toAvatar): + fromAvatar.d_teleportResponse(toAvatar.doId, int(not self.isPartyEnding), + toAvatar.defaultShard, base.cr.playGame.getPlaceId(), + self.getZoneId() + ) diff --git a/toontown/parties/PartyCatchActivityToonSD.py b/toontown/parties/PartyCatchActivityToonSD.py new file mode 100755 index 00000000..16bd2de5 --- /dev/null +++ b/toontown/parties/PartyCatchActivityToonSD.py @@ -0,0 +1,234 @@ +from pandac.PandaModules import Vec3 +from direct.interval.IntervalGlobal import Sequence, Parallel, Wait, Func +from direct.interval.IntervalGlobal import LerpScaleInterval +from direct.interval.IntervalGlobal import WaitInterval, ActorInterval, FunctionInterval +from direct.task.Task import Task +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from toontown.minigame.OrthoWalk import OrthoWalk +from toontown.minigame.MinigameRulesPanel import MinigameRulesPanel +from toontown.parties import PartyGlobals +from direct.fsm import ClassicFSM, State + +class PartyCatchActivityToonSD(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyCatchActivityToonSD') + FallBackAnim = 'slip-backward' + FallFwdAnim = 'slip-forward' + CatchNeutralAnim = 'catch-neutral' + CatchRunAnim = 'catch-run' + EatNeutralAnim = 'catch-eatneutral' + EatNRunAnim = 'catch-eatnrun' + animList = [FallBackAnim, + FallFwdAnim, + CatchNeutralAnim, + CatchRunAnim, + EatNeutralAnim, + EatNRunAnim] + + def __init__(self, avId, activity): + PartyCatchActivityToonSD.notify.debug('init : avId = %s, activity = %s ' % (avId, activity)) + self.avId = avId + self.activity = activity + self.isLocal = avId == base.localAvatar.doId + self.toon = self.activity.getAvatar(self.avId) + self.unexpectedExit = False + self.fsm = ClassicFSM.ClassicFSM('CatchActivityAnimFSM-%s' % self.avId, [State.State('init', self.enterInit, self.exitInit, ['notPlaying', 'normal', 'rules']), + State.State('notPlaying', self.enterNotPlaying, self.exitNotPlaying, ['normal', 'rules', 'cleanup']), + State.State('rules', self.enterRules, self.exitRules, ['normal', 'cleanup']), + State.State('normal', self.enterNormal, self.exitNormal, ['eatFruit', + 'fallBack', + 'fallForward', + 'notPlaying']), + State.State('eatFruit', self.enterEatFruit, self.exitEatFruit, ['normal', + 'fallBack', + 'fallForward', + 'eatFruit', + 'notPlaying']), + State.State('fallBack', self.enterFallBack, self.exitFallBack, ['normal', 'notPlaying']), + State.State('fallForward', self.enterFallForward, self.exitFallForward, ['normal', 'notPlaying']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'init', 'cleanup') + self.enteredAlready = False + + def load(self): + self.setAnimState('off', 1.0) + for anim in self.animList: + self.toon.pose(anim, 0) + + def unload(self): + del self.fsm + + def enter(self): + if not self.enteredAlready: + self.enteredAlready = True + self.fsm.enterInitialState() + self._exiting = False + + def exit(self, unexpectedExit = False): + if self._exiting: + return + self._exiting = True + self.unexpectedExit = unexpectedExit + if not self.unexpectedExit: + self.fsm.requestFinalState() + del self._exiting + + def enterInit(self): + self.notify.debug('enterInit') + self.toon.startBlink() + self.toon.stopLookAround() + if self.isLocal: + self.activity.initOrthoWalk() + self.dropShadow = self.toon.dropShadow + self.origDropShadowColor = self.dropShadow.getColor() + c = self.origDropShadowColor + alpha = 0.35 + self.dropShadow.setColor(c[0], c[1], c[2], alpha) + + def exitInit(self): + pass + + def enterNotPlaying(self): + self.toon.stopBlink() + self.toon.startLookAround() + self.setAnimState('neutral', 1.0) + if self.isLocal: + self.activity.orthoWalk.stop() + self.dropShadow.setColor(self.origDropShadowColor) + + def exitNotPlaying(self): + self.dropShadow = self.toon.dropShadow + self.origDropShadowColor = self.dropShadow.getColor() + c = self.origDropShadowColor + alpha = 0.35 + self.dropShadow.setColor(c[0], c[1], c[2], alpha) + + def enterRules(self): + if self.isLocal: + self.notify.debug('enterNormal') + self.setAnimState('Catching', 1.0) + self.activity.orthoWalk.stop() + self.accept(self.activity.rulesDoneEvent, self.handleRulesDone) + self.rulesPanel = MinigameRulesPanel('PartyRulesPanel', self.activity.getTitle(), self.activity.getInstructions(), self.activity.rulesDoneEvent, PartyGlobals.DefaultRulesTimeout) + base.setCellsAvailable(base.bottomCells + [base.leftCells[0], base.rightCells[1]], False) + self.rulesPanel.load() + self.rulesPanel.enter() + else: + self.fsm.request('normal') + + def handleRulesDone(self): + self.fsm.request('normal') + + def exitRules(self): + self.setAnimState('off', 1.0) + self.ignore(self.activity.rulesDoneEvent) + if hasattr(self, 'rulesPanel'): + self.rulesPanel.exit() + self.rulesPanel.unload() + del self.rulesPanel + base.setCellsAvailable(base.bottomCells + [base.leftCells[0], base.rightCells[1]], True) + + def enterNormal(self): + self.notify.debug('enterNormal') + self.setAnimState('Catching', 1.0) + if self.isLocal: + self.activity.orthoWalk.start() + self.toon.lerpLookAt(Vec3.forward() + Vec3.up(), time=0.2, blink=0) + + def exitNormal(self): + self.setAnimState('off', 1.0) + if self.isLocal: + self.activity.orthoWalk.stop() + self.toon.lerpLookAt(Vec3.forward(), time=0.2, blink=0) + + def eatFruit(self, fruitModel, handNode): + if self.fsm.getCurrentState().getName() == 'eatFruit': + self.fsm.request('normal') + self.fsm.request('eatFruit', [fruitModel, handNode]) + + def enterEatFruit(self, fruitModel, handNode): + self.notify.debug('enterEatFruit') + self.setAnimState('CatchEating', 1.0) + if self.isLocal: + self.activity.orthoWalk.start() + self.fruitModel = fruitModel + renderScale = fruitModel.getScale(render) + fruitModel.reparentTo(handNode) + fruitModel.setScale(render, renderScale) + duration = self.toon.getDuration('catch-eatneutral') + self.eatIval = Sequence(Parallel(WaitInterval(duration), Sequence(LerpScaleInterval(fruitModel, duration / 2.0, fruitModel.getScale() * 0.5, blendType='easeInOut'), Func(fruitModel.hide))), Func(self.fsm.request, 'normal'), name=self.toon.uniqueName('eatingIval')) + self.eatIval.start() + + def exitEatFruit(self): + self.eatIval.pause() + del self.eatIval + self.fruitModel.reparentTo(hidden) + self.fruitModel.removeNode() + del self.fruitModel + self.setAnimState('off', 1.0) + if self.isLocal: + self.activity.orthoWalk.stop() + + def enterFallBack(self): + self.notify.debug('enterFallBack') + if self.isLocal: + base.playSfx(self.activity.sndOof) + duration = 1.0 + animName = self.FallBackAnim + startFrame = 12 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.toon.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + + def resume(self = self): + self.fsm.request('normal') + + self.fallBackIval = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate), FunctionInterval(resume)) + self.fallBackIval.start() + + def exitFallBack(self): + self.fallBackIval.pause() + del self.fallBackIval + + def enterFallForward(self): + self.notify.debug('enterFallForward') + if self.isLocal: + base.playSfx(self.activity.sndOof) + duration = 2.0 + animName = self.FallFwdAnim + startFrame = 12 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + pauseFrame = 19 + frameRate = self.toon.getFrameRate(animName) + newRate = frames / (duration * 0.5) + playRate = newRate / frameRate + + def resume(self = self): + self.fsm.request('normal') + + self.fallFwdIval = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=pauseFrame / newRate, playRate=playRate), WaitInterval(duration / 2.0), ActorInterval(self.toon, animName, startTime=pauseFrame / newRate, endTime=totalFrames / newRate, playRate=playRate), FunctionInterval(resume)) + self.fallFwdIval.start() + + def exitFallForward(self): + self.fallFwdIval.pause() + del self.fallFwdIval + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.toon.stopBlink() + self.toon.startLookAround() + if self.isLocal: + self.activity.orthoWalk.stop() + self.activity.destroyOrthoWalk() + self.dropShadow.setColor(self.origDropShadowColor) + + def exitCleanup(self): + pass + + def setAnimState(self, newState, playRate): + if not self.unexpectedExit: + self.toon.setAnimState(newState, playRate) + else: + self.notify.debug('setAnimState(): Toon unexpectedExit flag is set.') diff --git a/toontown/parties/PartyCog.py b/toontown/parties/PartyCog.py new file mode 100755 index 00000000..d702ad8b --- /dev/null +++ b/toontown/parties/PartyCog.py @@ -0,0 +1,339 @@ +import math +from direct.actor.Actor import Actor +from direct.interval.ActorInterval import ActorInterval +from direct.interval.MetaInterval import Sequence, Parallel +from direct.interval.FunctionInterval import Func, Wait +from direct.interval.SoundInterval import SoundInterval +from direct.interval.LerpInterval import LerpScaleInterval, LerpFunc +from direct.showbase.PythonUtil import bound as clamp +from direct.task import Task +from direct.fsm.FSM import FSM +from pandac.PandaModules import CollisionTube, CollisionNode, CollisionSphere +from pandac.PandaModules import Point3, Vec4, NodePath, TextNode, Mat4 +from toontown.toonbase import ToontownGlobals +from toontown.battle.BattleProps import globalPropPool +from toontown.battle.BattleSounds import globalBattleSoundCache +import PartyGlobals + +class PartyCogManager: + + def __init__(self): + self.cogs = [] + + def generateCog(self, parentNode, bounceSpeed = 3, bounceHeight = 1, rotateSpeed = 1, heightShift = 1, xMoveSpeed = 0, xMoveDistance = 0, bounceOffset = 0): + cog = PartyCog(parentNode, len(self.cogs), bounceSpeed, bounceHeight, rotateSpeed, heightShift, xMoveSpeed, xMoveDistance, bounceOffset) + self.cogs.append(cog) + return cog + + def unload(self): + for cog in self.cogs: + cog.unload() + + def updateDistances(self, distances): + for i in xrange(len(distances)): + self.cogs[i].updateDistance(distances[i]) + + +class PartyCog(FSM): + notify = directNotify.newCategory('PartyCog') + HpTextGenerator = TextNode('HpTextGenerator') + hpText = None + height = 7 + + def __init__(self, parentNode, id, bounceSpeed = 3, bounceHeight = 1, rotateSpeed = 1, heightShift = 1, xMoveSpeed = 0, xMoveDistance = 0, bounceOffset = 0): + self.id = id + FSM.__init__(self, 'PartyCogFSM-%d' % self.id) + self.showFacingStatus = False + self.xMoveSpeed = xMoveSpeed + self.xMoveDistance = xMoveDistance + self.heightShift = heightShift + self.bounceSpeed = bounceSpeed + self.bounceHeight = bounceHeight + self.rotateSpeed = rotateSpeed + self.parentNode = parentNode + self.bounceOffset = bounceOffset + self.hitInterval = None + self.kaboomTrack = None + self.resetRollIval = None + self.netTimeSentToStartByHit = 0 + self.load() + self.request('Down') + return + + def load(self): + self.root = NodePath('PartyCog-%d' % self.id) + self.root.reparentTo(self.parentNode) + path = 'phase_13/models/parties/cogPinata_' + self.actor = Actor(path + 'actor', {'idle': path + 'idle_anim', + 'down': path + 'down_anim', + 'up': path + 'up_anim', + 'bodyHitBack': path + 'bodyHitBack_anim', + 'bodyHitFront': path + 'bodyHitFront_anim', + 'headHitBack': path + 'headHitBack_anim', + 'headHitFront': path + 'headHitFront_anim'}) + self.actor.reparentTo(self.root) + self.temp_transform = Mat4() + self.head_locator = self.actor.attachNewNode('temphead') + self.bodyColl = CollisionTube(0, 0, 1, 0, 0, 5.75, 0.75) + self.bodyColl.setTangible(1) + self.bodyCollNode = CollisionNode('PartyCog-%d-Body-Collision' % self.id) + self.bodyCollNode.setCollideMask(ToontownGlobals.PieBitmask) + self.bodyCollNode.addSolid(self.bodyColl) + self.bodyCollNodePath = self.root.attachNewNode(self.bodyCollNode) + self.headColl = CollisionTube(0, 0, 3, 0, 0, 3.0, 1.5) + self.headColl.setTangible(1) + self.headCollNode = CollisionNode('PartyCog-%d-Head-Collision' % self.id) + self.headCollNode.setCollideMask(ToontownGlobals.PieBitmask) + self.headCollNode.addSolid(self.headColl) + self.headCollNodePath = self.root.attachNewNode(self.headCollNode) + self.arm1Coll = CollisionSphere(1.65, 0, 3.95, 1.0) + self.arm1Coll.setTangible(1) + self.arm1CollNode = CollisionNode('PartyCog-%d-Arm1-Collision' % self.id) + self.arm1CollNode.setCollideMask(ToontownGlobals.PieBitmask) + self.arm1CollNode.addSolid(self.arm1Coll) + self.arm1CollNodePath = self.root.attachNewNode(self.arm1CollNode) + self.arm2Coll = CollisionSphere(-1.65, 0, 3.45, 1.0) + self.arm2Coll.setTangible(1) + self.arm2CollNode = CollisionNode('PartyCog-%d-Arm2-Collision' % self.id) + self.arm2CollNode.setCollideMask(ToontownGlobals.PieBitmask) + self.arm2CollNode.addSolid(self.arm2Coll) + self.arm2CollNodePath = self.root.attachNewNode(self.arm2CollNode) + splatName = 'splat-creampie' + self.splat = globalPropPool.getProp(splatName) + self.splat.setBillboardPointEye() + self.splatType = globalPropPool.getPropType(splatName) + self.pieHitSound = globalBattleSoundCache.getSound('AA_wholepie_only.ogg') + self.upSound = globalBattleSoundCache.getSound('AV_jump_to_side.ogg') + self.hole = loader.loadModel('phase_13/models/parties/cogPinataHole') + self.hole.setTransparency(True) + self.hole.setP(-90.0) + self.hole.setScale(3) + self.hole.setBin('ground', 3) + self.hole.reparentTo(self.parentNode) + + def unload(self): + self.request('Off') + self.clearHitInterval() + if self.hole is not None: + self.hole.removeNode() + self.hole = None + if self.actor is not None: + self.actor.cleanup() + self.actor.removeNode() + self.actor = None + if self.root is not None: + self.root.removeNode() + self.root = None + if self.kaboomTrack is not None and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + self.kaboomTrack = None + if self.resetRollIval is not None and self.resetRollIval.isPlaying(): + self.resetRollIval.finish() + self.resetRollIval = None + if self.hitInterval is not None and self.hitInterval.isPlaying(): + self.hitInterval.finish() + self.hitInterval = None + del self.upSound + del self.pieHitSound + return + + def enterStatic(self): + pass + + def exitStatic(self): + pass + + def enterActive(self, startTime): + self.root.setR(0.0) + updateTask = Task.Task(self.updateTask) + updateTask.startTime = startTime + taskMgr.add(updateTask, 'PartyCog.update-%d' % self.id) + + def exitActive(self): + taskMgr.remove('PartyCog.update-%d' % self.id) + taskMgr.remove('PartyCog.bounceTask-%d' % self.id) + self.clearHitInterval() + self.resetRollIval = self.root.hprInterval(0.5, Point3(self.root.getH(), 0.0, 0.0), blendType='easeInOut') + self.resetRollIval.start() + self.actor.stop() + + def enterDown(self): + if self.oldState == 'Off': + downAnimControl = self.actor.getAnimControl('down') + self.actor.pose('down', downAnimControl.getNumFrames() - 1) + return + self.clearHitInterval() + startScale = self.hole.getScale() + endScale = Point3(5, 5, 5) + self.hitInterval = Sequence(LerpFunc(self.setAlongSpline, duration=1.0, fromData=self.currentT, toData=0.0), LerpScaleInterval(self.hole, duration=0.175, scale=endScale, startScale=startScale, blendType='easeIn'), Parallel(SoundInterval(self.upSound, volume=0.6, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), ActorInterval(self.actor, 'down', loop=0)), LerpScaleInterval(self.hole, duration=0.175, scale=Point3(3, 3, 3), startScale=endScale, blendType='easeOut')) + self.hitInterval.start() + + def exitDown(self): + self.root.setR(0.0) + self.root.setH(0.0) + self.targetDistance = 0.0 + self.targetFacing = 0.0 + self.currentT = 0.0 + self.setAlongSpline(0.0) + self.clearHitInterval() + startScale = self.hole.getScale() + endScale = Point3(5, 5, 5) + self.hitInterval = Sequence(LerpScaleInterval(self.hole, duration=0.175, scale=endScale, startScale=startScale, blendType='easeIn'), Parallel(SoundInterval(self.upSound, volume=0.6, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), ActorInterval(self.actor, 'up', loop=0)), Func(self.actor.loop, 'idle'), LerpScaleInterval(self.hole, duration=0.175, scale=Point3(3, 3, 3), startScale=endScale, blendType='easeOut')) + self.hitInterval.start() + + def filterDown(self, request, args): + if request == 'Down': + return None + else: + return self.defaultFilter(request, args) + return None + + def setEndPoints(self, start, end, amplitude = 1.7): + self.sinAmplitude = amplitude + self.sinPeriod = (end.getX() - start.getX()) / 2 + self.sinDisplacement = start.getY() + self.startPoint = start + self.endPoint = end + self.currentT = 0.0 + self.targetDistance = 0.0 + self.currentFacing = 0.0 + self.targetFacing = 0.0 + self.setAlongSpline(self.currentT) + self.hole.setPos(self.root.getPos()) + self.hole.setZ(0.02) + + def rockBackAndForth(self, task): + t = task.startTime + task.time + angle = math.sin(t) * 20.0 + self.root.setR(angle) + return task.cont + + def updateDistance(self, distance): + self.targetDistance = clamp(distance, -1.0, 1.0) + + def updateTask(self, task): + self.rockBackAndForth(task) + if self.targetDistance > self.currentT: + self.currentT += min(0.01, self.targetDistance - self.currentT) + self.setAlongSpline(self.currentT) + elif self.targetDistance < self.currentT: + self.currentT += max(-0.01, self.targetDistance - self.currentT) + self.setAlongSpline(self.currentT) + if self.currentT < 0.0: + self.targetFacing = -90.0 + elif self.currentT > 0.0: + self.targetFacing = 90.0 + else: + self.targetFacing = 0.0 + if self.targetFacing > self.currentFacing: + self.currentFacing += min(10, self.targetFacing - self.currentFacing) + elif self.targetFacing < self.currentFacing: + self.currentFacing += max(-10, self.targetFacing - self.currentFacing) + self.root.setH(self.currentFacing) + return task.cont + + def setAlongSpline(self, t): + t = t + 1.0 + dist = (self.endPoint.getX() - self.startPoint.getX()) / 2.0 + x = self.startPoint.getX() + t * dist + y = self.startPoint.getY() - math.sin(t * 2 * math.pi) * self.sinAmplitude + self.root.setPos(x, y, 0) + + def startBounce(self): + taskMgr.add(self.bounce, 'PartyCog.bounceTask-%d' % self.id) + + def bounce(self, task): + self.root.setZ(math.sin((self.bounceOffset + task.time) * self.bounceSpeed) * self.bounceHeight + self.heightShift) + return task.cont + + def setPos(self, position): + self.root.setPos(position) + + def respondToPieHit(self, timestamp, position, hot = False, direction = 1.0): + if self.netTimeSentToStartByHit < timestamp: + self.__showSplat(position, direction, hot) + if self.netTimeSentToStartByHit < timestamp: + self.netTimeSentToStartByHit = timestamp + else: + self.notify.debug('respondToPieHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit) + + def clearHitInterval(self): + if self.hitInterval is not None and self.hitInterval.isPlaying(): + self.hitInterval.clearToInitial() + return + + def __showSplat(self, position, direction, hot = False): + if self.kaboomTrack is not None and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + self.clearHitInterval() + splatName = 'splat-creampie' + self.splat = globalPropPool.getProp(splatName) + self.splat.setBillboardPointEye() + self.splat.reparentTo(render) + self.splat.setPos(self.root, position) + self.splat.setAlphaScale(1.0) + if not direction == 1.0: + self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[0]) + if self.currentFacing > 0.0: + facing = 'HitFront' + else: + facing = 'HitBack' + else: + self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[1]) + if self.currentFacing > 0.0: + facing = 'HitBack' + else: + facing = 'HitFront' + if hot: + targetscale = 0.75 + part = 'head' + else: + targetscale = 0.5 + part = 'body' + + def setSplatAlpha(amount): + self.splat.setAlphaScale(amount) + + self.hitInterval = Sequence(ActorInterval(self.actor, part + facing, loop=0), Func(self.actor.loop, 'idle')) + self.hitInterval.start() + self.kaboomTrack = Parallel(SoundInterval(self.pieHitSound, volume=1.0, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), Sequence(Func(self.splat.showThrough), Parallel(Sequence(LerpScaleInterval(self.splat, duration=0.175, scale=targetscale, startScale=Point3(0.1, 0.1, 0.1), blendType='easeOut'), Wait(0.175)), Sequence(Wait(0.1), LerpFunc(setSplatAlpha, duration=1.0, fromData=1.0, toData=0.0, blendType='easeOut'))), Func(self.splat.cleanup), Func(self.splat.removeNode))) + self.kaboomTrack.start() + return + + def showHitScore(self, number, scale = 1): + if number <= 0: + return + if self.hpText: + self.hideHitScore() + self.HpTextGenerator.setFont(ToontownGlobals.getSignFont()) + if number < 0: + self.HpTextGenerator.setText(str(number)) + else: + self.HpTextGenerator.setText('+' + str(number)) + self.HpTextGenerator.clearShadow() + self.HpTextGenerator.setAlign(TextNode.ACenter) + r = 1 + g = 1 + b = 0 + a = 1 + self.HpTextGenerator.setTextColor(r, g, b, a) + self.hpTextNode = self.HpTextGenerator.generate() + self.hpText = render.attachNewNode(self.hpTextNode) + self.hpText.setScale(scale) + self.hpText.setBillboardPointEye() + self.hpText.setBin('fixed', 100) + self.hpText.setPos(self.root, 0, 0, self.height / 2) + seq = Sequence(self.hpText.posInterval(0.25, Point3(self.root.getX(render), self.root.getY(render), self.root.getZ(render) + self.height + 1.0), blendType='easeOut'), Wait(0.25), self.hpText.colorInterval(0.1, Vec4(r, g, b, 0)), Func(self.__hideHitScore)) + seq.start() + + def hideHitScore(self): + if self.hpText: + taskMgr.remove('PartyCogHpText' + str(self.id)) + self.hpText.removeNode() + self.hpText = None + return + + def getHeadLocation(self): + self.actor.getJoints(jointName='head')[0].getNetTransform(self.temp_transform) + self.head_locator.setMat(self.temp_transform) + return self.head_locator.getZ(self.root) diff --git a/toontown/parties/PartyCogActivity.py b/toontown/parties/PartyCogActivity.py new file mode 100755 index 00000000..b6b6699a --- /dev/null +++ b/toontown/parties/PartyCogActivity.py @@ -0,0 +1,679 @@ +from direct.interval.MetaInterval import Sequence, Parallel, Track +from direct.interval.FunctionInterval import Func, Wait +from direct.interval.SoundInterval import SoundInterval +from direct.interval.ActorInterval import ActorInterval +from direct.interval.ProjectileInterval import ProjectileInterval +from direct.distributed.ClockDelta import globalClockDelta +from direct.showbase.PythonUtil import bound, lerp +from direct.showbase.DirectObject import DirectObject +from pandac.PandaModules import NodePath, Point3, TextNode +from pandac.PandaModules import CollisionSphere, CollisionNode, CollisionHandlerEvent +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase.ToontownTimer import ToontownTimer +import PartyGlobals +import PartyCogUtils +from PartyCog import PartyCogManager +from PartyCogActivityPlayer import PartyCogActivityPlayer +from PartyCogActivityPlayer import PartyCogActivityLocalPlayer +from StretchingArrow import StretchingArrow + +class PartyCogActivity(DirectObject): + notify = directNotify.newCategory('PartyCogActivity') + cog = None + arena = None + player = None + players = {} + + def __init__(self, activity, arenaModel = None, texture = None): + self.activity = activity + self.root = self.activity.root + self.toonPieTracks = {} + self.toonPieEventNames = {} + self.toonIdsToAnimIntervals = {} + self.pieIvals = [] + self.resultsIval = None + self.arenaModel = arenaModel + self.texture = texture + return + + def load(self): + self.arena = loader.loadModel(self.arenaModel) + self.arena.reparentTo(self.root) + ground = self.arena.find('**/ground') + ground.setBin('ground', 1) + entranceArrows = self.arena.findAllMatches('**/arrowFlat*') + for arrow in entranceArrows: + arrow.setBin('ground', 5) + + self.leftEntranceLocator = self.arena.find('**/leftEntrance_locator') + self.rightEntranceLocator = self.arena.find('**/rightEntrance_locator') + self.leftExitLocator = self.arena.find('**/leftExit_locator') + self.rightExitLocator = self.arena.find('**/rightExit_locator') + self.teamCamPosLocators = (self.arena.find('**/team0CamPos_locator'), self.arena.find('**/team1CamPos_locator')) + self.teamCamAimLocators = (self.arena.find('**/team0CamAim_locator'), self.arena.find('**/team1CamAim_locator')) + leftTeamLocator = NodePath('TeamLocator-%d' % PartyGlobals.TeamActivityTeams.LeftTeam) + leftTeamLocator.reparentTo(self.root) + leftTeamLocator.setH(90) + rightTeamLocator = NodePath('TeamLocator-%d' % PartyGlobals.TeamActivityTeams.RightTeam) + rightTeamLocator.reparentTo(self.root) + rightTeamLocator.setH(-90) + self.teamLocators = (leftTeamLocator, rightTeamLocator) + self._lengthBetweenEntrances = self.leftEntranceLocator.getY() - self.rightExitLocator.getY() + self._skyCollisionsCollection = self.arena.findAllMatches('**/cogPieArena_sky*_collision') + if len(self._skyCollisionsCollection) > 0: + self._skyCollisionParent = self._skyCollisionsCollection[0].getParent() + else: + self._skyCollisionParent = self.arena + self._wallCollisionsCollection = self.arena.findAllMatches('**/cogPieArena_wall*_collision') + self._arenaFlagGroups = (self.arena.find('**/flagsL_grp'), self.arena.find('**/flagsR_grp')) + self._initArenaDoors() + self.cogManager = PartyCogManager() + self.arrows = [] + self.distanceLabels = [] + self.teamColors = list(PartyGlobals.CogActivityColors) + [PartyGlobals.TeamActivityStatusColor] + for i in xrange(3): + start = self.arena.find('**/cog%d_start_locator' % (i + 1)) + end = self.arena.find('**/cog%d_end_locator' % (i + 1)) + cog = self.cogManager.generateCog(self.arena) + cog.setEndPoints(start.getPos(), end.getPos()) + arrow1 = StretchingArrow(self.arena, useColor='orange') + arrow2 = StretchingArrow(self.arena, useColor='blue') + arrow1.setZ(0.1) + arrow2.setZ(0.1) + self.arrows.append([arrow1, arrow2]) + distanceLabel = self.createDistanceLabel(0, self.teamColors[1]) + distanceLabel[0].stash() + distanceLabel2 = self.createDistanceLabel(0, self.teamColors[0]) + distanceLabel2[0].stash() + self.distanceLabels.append([distanceLabel, distanceLabel2]) + + self.winText = [] + text1 = self.createText(0, Point3(-0.5, 0.0, -0.5), self.teamColors[1]) + text2 = self.createText(1, Point3(0.5, 0.0, -0.5), self.teamColors[0]) + self.winText.append(text1) + self.winText.append(text2) + self.winStatus = self.createText(2, Point3(0.0, 0.0, -0.8), self.teamColors[0]) + signLocator = self.arena.find('**/eventSign_locator') + self.activity.sign.setPos(signLocator.getPos(self.root)) + if self.texture: + textureAlpha = self.texture[:-4] + '_a.rgb' + reskinTexture = loader.loadTexture(self.texture, textureAlpha) + self.arena.find('**/center_grp').setTexture(reskinTexture, 100) + self.arena.find('**/leftSide_grp').setTexture(reskinTexture, 100) + self.arena.find('**/rightSide_grp').setTexture(reskinTexture, 100) + self.enable() + + def _initArenaDoors(self): + self._arenaDoors = (self.arena.find('**/doorL'), self.arena.find('**/doorR')) + arenaDoorLocators = (self.arena.find('**/doorL_locator'), self.arena.find('**/doorR_locator')) + for i in xrange(len(arenaDoorLocators)): + arenaDoorLocators[i].wrtReparentTo(self._arenaDoors[i]) + + self._arenaDoorTimers = (self.createDoorTimer(PartyGlobals.TeamActivityTeams.LeftTeam), self.createDoorTimer(PartyGlobals.TeamActivityTeams.RightTeam)) + self._arenaDoorIvals = [None, None] + self._doorStartPos = [] + for i in xrange(len(self._arenaDoors)): + door = self._arenaDoors[i] + timer = self._arenaDoorTimers[i] + timer.reparentTo(arenaDoorLocators[i]) + timer.hide() + self._doorStartPos.append(door.getPos()) + door.setPos(door, 0, 0, -7.0) + + return + + def _destroyArenaDoors(self): + for ival in self._arenaDoorIvals: + ival.finish() + + self._arenaDoorIvals = None + self._arenaDoors = None + for timer in self._arenaDoorTimers: + timer.stop() + timer.removeNode() + + self._arenaDoorTimers = None + return + + def createDoorTimer(self, team): + timer = ToontownTimer(useImage=False, highlightNearEnd=False) + timer['text_font'] = ToontownGlobals.getMinnieFont() + timer.setFontColor(PartyGlobals.CogActivityColors[team]) + timer.setScale(7.0) + timer.setPos(0.2, -0.03, 0.0) + return timer + + def createText(self, number, position, color): + text = TextNode('winText%d' % number) + text.setAlign(TextNode.ACenter) + text.setTextColor(color) + text.setFont(ToontownGlobals.getSignFont()) + text.setText('') + noteText = aspect2d.attachNewNode(text) + noteText.setScale(0.2) + noteText.setPos(position) + noteText.stash() + return (text, noteText) + + def createDistanceLabel(self, number, color): + text = TextNode('distanceText-%d' % number) + text.setAlign(TextNode.ACenter) + text.setTextColor(color) + text.setFont(ToontownGlobals.getSignFont()) + text.setText('10 ft') + node = self.root.attachNewNode(text) + node.setBillboardPointEye() + node.setScale(2.5) + node.setZ(5.0) + return (node, text) + + def unload(self): + self.disable() + self._cleanupResultsIval() + if self.winText is not None: + for pair in self.winText: + pair[1].reparentTo(hidden) + pair[1].removeNode() + + self.winText = None + if self.winStatus is not None: + self.winStatus[1].reparentTo(hidden) + self.winStatus[1].removeNode() + self.winStatus = None + if self.cogManager is not None: + self.cogManager.unload() + self.cogManager = None + if self.arrows is not None: + for pair in self.arrows: + for arrow in pair: + arrow.destroy() + arrow = None + + pair = None + + self.arrows = None + if self.distanceLabels is not None: + for pair in self.distanceLabels: + for node, text in pair: + node.removeNode() + + pair = None + + self.distanceLabels = None + if len(self.players): + for player in self.players.values(): + player.disable() + player.destroy() + + self.players.clear() + self.player = None + if self.arena is not None: + self.leftEntranceLocator = None + self.rightEntranceLocator = None + self.leftExitLocator = None + self.rightExitLocator = None + self._skyCollisions = None + self._skyCollisionParent = None + self._arenaFlagGroups = None + self._destroyArenaDoors() + self.arena.removeNode() + self.arena = None + for ival in self.toonPieTracks.values(): + if ival is not None and ival.isPlaying(): + try: + ival.finish() + except Exception, theException: + self.notify.warning('Ival could not finish:\n %s \nException %s ' % (str(ival), str(theException))) + + self.toonPieTracks = {} + for ival in self.pieIvals: + if ival is not None and ival.isPlaying(): + try: + ival.finish() + except Exception, theException: + self.notify.warning('Ival could not finish:\n %s \nException %s ' % (str(ival), str(theException))) + + self.pieIvals = [] + self.toonIdsToAnimIntervals = {} + for eventName in self.toonPieEventNames.values(): + self.ignore(eventName) + + self.toonPieEventNames = {} + return + + def enable(self): + self.enableEnterGateCollision() + + def disable(self): + self.disableEnterGateCollision() + self.ignoreAll() + + def hideTeamFlags(self, team): + self._arenaFlagGroups[team].stash() + + def showTeamFlags(self, team): + self._arenaFlagGroups[team].unstash() + + def _playArenaDoorIval(self, team, opening = True): + ival = self._arenaDoorIvals[team] + if ival is not None and ival.isPlaying(): + ival.pause() + if not opening: + pos = self._doorStartPos[team] + else: + pos = (self._doorStartPos[team] + Point3(0, 0, -7.0),) + ival = self._arenaDoors[team].posInterval(0.75, Point3(0, 0, -7.0), blendType='easeIn') + self._arenaDoorIvals[team] = ival + ival.start() + return + + def openArenaDoorForTeam(self, team): + self._playArenaDoorIval(team, opening=False) + + def closeArenaDoorForTeam(self, team): + self._playArenaDoorIval(team, opening=False) + + def openArenaDoors(self): + self.enableEnterGateCollision() + for i in xrange(len(self._arenaDoors)): + self.openArenaDoorForTeam(i) + + def closeArenaDoors(self): + self.disableEnterGateCollision() + for i in xrange(len(self._arenaDoors)): + self.closeArenaDoorForTeam(i) + + def showArenaDoorTimers(self, duration): + for timer in self._arenaDoorTimers: + timer.setTime(duration) + timer.countdown(duration) + timer.show() + + def hideArenaDoorTimers(self): + for timer in self._arenaDoorTimers: + timer.hide() + + def enableEnterGateCollision(self): + self.acceptOnce('entercogPieArena_entranceLeft_collision', self.handleEnterLeftEntranceTrigger) + self.acceptOnce('entercogPieArena_entranceRight_collision', self.handleEnterRightEntranceTrigger) + + def disableEnterGateCollision(self): + self.ignore('entercogPieArena_entranceLeft_collision') + self.ignore('entercogPieArena_entranceRight_collision') + + def enableWallCollisions(self): + self._wallCollisionsCollection.unstash() + + def disableWallCollisions(self): + self._wallCollisionsCollection.stash() + + def enableSkyCollisions(self): + self._skyCollisionsCollection.unstash() + + def disableSkyCollisions(self): + self._skyCollisionsCollection.stash() + + def handleEnterLeftEntranceTrigger(self, collEntry): + self.activity.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.LeftTeam) + + def handleEnterRightEntranceTrigger(self, collEntry): + self.activity.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.RightTeam) + + def checkOrthoDriveCollision(self, oldPos, newPos): + x = bound(newPos[0], -16.8, 16.8) + y = bound(newPos[1], -17.25, -24.1) + newPos.setX(x) + newPos.setY(y) + return newPos + + def getPlayerStartPos(self, team, spot): + if team == PartyGlobals.TeamActivityTeams.LeftTeam: + node = self.leftExitLocator + else: + node = self.rightExitLocator + d = self._lengthBetweenEntrances / (self.activity.getMaxPlayersPerTeam() + 1) + yOffset = node.getY(self.root) + d * (spot + 1) + pos = node.getPos(self.root) + pos.setY(yOffset) + return pos + + def handleToonJoined(self, toon, team, lateEntry = False): + pos = self.getPlayerStartPos(team, self.activity.getIndex(toon.doId, team)) + if toon == base.localAvatar: + player = PartyCogActivityLocalPlayer(self.activity, pos, team, self.handleToonExited) + player.entersActivity() + self.player = player + self.disableSkyCollisions() + self.playPlayerEnterIval() + else: + player = PartyCogActivityPlayer(self.activity, toon, pos, team) + player.entersActivity() + if lateEntry: + player.updateToonPosition() + self.players[toon.doId] = player + + def handleToonSwitchedTeams(self, toon): + toonId = toon.doId + player = self.players.get(toonId) + if player is None: + self.notify.warning('handleToonSwitchedTeams: toonId %s not found' % toonId) + return + team = self.activity.getTeam(toonId) + spot = self.activity.getIndex(toonId, team) + pos = self.getPlayerStartPos(team, spot) + self.finishToonIval(toonId) + player.setTeam(team) + player.setToonStartPosition(pos) + player.updateToonPosition() + return + + def handleToonShifted(self, toon): + toonId = toon.doId + if toonId in self.players: + player = self.players[toonId] + spot = self.activity.getIndex(toonId, player.team) + pos = self.getPlayerStartPos(player.team, spot) + player.setToonStartPosition(pos) + if self.player is not None and toon == self.player.toon: + self.playToonIval(base.localAvatar.doId, self.player.getRunToStartPositionIval()) + return + + def handleToonDisabled(self, toonId): + self.finishToonIval(toonId) + self.finishPieIvals(toonId) + player = self.players.get(toonId) + if player is not None: + player.disable() + if player == self.player: + self.player = None + del self.players[toonId] + return + + def finishPieIvals(self, toonId): + for ival in self.pieIvals: + if ival.isPlaying(): + if ival.getName().find(str(toonId)) != -1: + ival.finish() + + def playPlayerEnterIval(self): + + def conditionallyShowSwitchButton(self = self, enable = True): + if enable and self.activity.activityFSM.state in ['WaitForEnough', 'WaitToStart']: + self.activity.teamActivityGui.enableSwitchButton() + else: + self.activity.teamActivityGui.disableSwitchButton() + + ival = Sequence(Func(self.disableWallCollisions), Func(conditionallyShowSwitchButton, self, False), self.player.getRunToStartPositionIval(), Func(conditionallyShowSwitchButton, self, True), Func(self.enableWallCollisions)) + self.playToonIval(base.localAvatar.doId, ival) + + def finishToonIval(self, toonId): + if self.toonIdsToAnimIntervals.get(toonId) is not None and self.toonIdsToAnimIntervals[toonId].isPlaying(): + self.toonIdsToAnimIntervals[toonId].finish() + return + + def playToonIval(self, toonId, ival): + self.finishToonIval(toonId) + self.toonIdsToAnimIntervals[toonId] = ival + ival.start() + + def startActivity(self, timestamp): + self.pieHandler = CollisionHandlerEvent() + self.pieHandler.setInPattern('pieHit-%fn') + if self.player is not None: + self.player.resetScore() + self.hideTeamFlags(self.player.team) + for player in self.players.values(): + self.finishToonIval(player.toon.doId) + player.enable() + + for cog in self.cogManager.cogs: + cog.request('Active', timestamp) + + for ival in self.pieIvals: + if ival.isPlaying(): + ival.finish() + + self.pieIvals = [] + return + + def stopActivity(self): + for player in self.players.values(): + player.disable() + + for eventName in self.toonPieEventNames.values(): + self.ignore(eventName) + + self.toonPieEventNames.clear() + for cog in self.cogManager.cogs: + cog.request('Static') + + def handleToonExited(self, toon): + self.finishToonIval(toon.doId) + player = self.players[toon.doId] + player.disable() + player.exitsActivity() + player.destroy() + if player == self.player: + self.showTeamFlags(self.activity.getTeam(toon.doId)) + self.player = None + self.enableEnterGateCollision() + self.enableSkyCollisions() + del self.players[toon.doId] + return + + def pieThrow(self, avId, timestamp, heading, pos, power): + toon = self.activity.getAvatar(avId) + if toon is None: + return + tossTrack, pieTrack, flyPie = self.getTossPieInterval(toon, pos[0], pos[1], pos[2], heading, 0, 0, power) + if avId == base.localAvatar.doId: + flyPie.setTag('throwerId', str(avId)) + collSphere = CollisionSphere(0, 0, 0, 0.5) + collSphere.setTangible(0) + name = 'PieSphere-%d' % avId + collSphereName = self.activity.uniqueName(name) + collNode = CollisionNode(collSphereName) + collNode.setFromCollideMask(ToontownGlobals.PieBitmask) + collNode.addSolid(collSphere) + collNP = flyPie.attachNewNode(collNode) + base.cTrav.addCollider(collNP, self.pieHandler) + self.toonPieEventNames[collNP] = 'pieHit-' + collSphereName + self.accept(self.toonPieEventNames[collNP], self.handlePieCollision) + else: + player = self.players.get(avId) + if player is not None: + player.faceForward() + + def matchRunningAnim(toon = toon): + toon.playingAnim = None + toon.setSpeed(toon.forwardSpeed, toon.rotateSpeed) + return + + newTossTrack = Sequence(tossTrack, Func(matchRunningAnim)) + pieTrack = Parallel(newTossTrack, pieTrack, name='PartyCogActivity.pieTrack-%d-%s' % (avId, timestamp)) + elapsedTime = globalClockDelta.localElapsedTime(timestamp) + if elapsedTime < 16.0 / 24.0: + elapsedTime = 16.0 / 24.0 + pieTrack.start(elapsedTime) + self.pieIvals.append(pieTrack) + self.toonPieTracks[avId] = pieTrack + return + + def getTossPieInterval(self, toon, x, y, z, h, p, r, power, beginFlyIval = Sequence()): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + pie = toon.getPieModel() + pie.setScale(0.5) + flyPie = pie.copyTo(NodePath('a')) + pieName = ToontownBattleGlobals.pieNames[toon.pieType] + pieType = BattleProps.globalPropPool.getPropType(pieName) + animPie = Sequence() + if pieType == 'actor': + animPie = ActorInterval(pie, pieName, startFrame=48) + sound = loader.loadSfx('phase_3.5/audio/sfx/AA_pie_throw_only.ogg') + t = power / 100.0 + dist = lerp(PartyGlobals.CogActivityPieMinDist, PartyGlobals.CogActivityPieMaxDist, t) + time = lerp(1.0, 1.5, t) + proj = ProjectileInterval(None, startPos=Point3(0, 0, 0), endPos=Point3(0, dist, 0), duration=time) + relVel = proj.startVel + + def getVelocity(toon = toon, relVel = relVel): + return render.getRelativeVector(toon, relVel) * 0.6 + + def __safeSetAnimState(toon = toon, state = 'Happy'): + if toon and hasattr(toon, 'animFSM'): + toon.setAnimState('Happy') + else: + self.notify.warning('The toon is being destroyed. No attribute animState.') + + toss = Track((0, Sequence(Func(toon.setPosHpr, x, y, z, h, p, r), Func(pie.reparentTo, toon.rightHand), Func(pie.setPosHpr, 0, 0, 0, 0, 0, 0), animPie, Parallel(ActorInterval(toon, 'throw', startFrame=48, playRate=1.5, partName='torso'), animPie), Func(__safeSetAnimState, toon, 'Happy'))), (16.0 / 24.0, Func(pie.detachNode))) + fly = Track((14.0 / 24.0, SoundInterval(sound, node=toon, cutOff=PartyGlobals.PARTY_COG_CUTOFF)), (16.0 / 24.0, Sequence(Func(flyPie.reparentTo, render), Func(flyPie.setPosHpr, toon, 0.52, 0.97, 2.24, 0, -45, 0), beginFlyIval, ProjectileInterval(flyPie, startVel=getVelocity, duration=6), Func(flyPie.detachNode)))) + return (toss, fly, flyPie) + + def handlePieCollision(self, colEntry): + if not self.activity.isState('Active') or self.player is None: + return + handled = False + into = colEntry.getIntoNodePath() + intoName = into.getName() + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + if 'PartyCog' in intoName: + if self.toonPieTracks.get(base.localAvatar.doId) is not None: + self.toonPieTracks[base.localAvatar.doId].finish() + self.toonPieTracks[base.localAvatar.doId] = None + parts = intoName.split('-') + cogID = int(parts[1]) + point = colEntry.getSurfacePoint(self.cogManager.cogs[cogID].root) + cog = self.cogManager.cogs[cogID] + hitHead = point.getZ() > cog.getHeadLocation() and not parts[2].startswith('Arm') + if self.activity.getTeam(base.localAvatar.doId) == PartyGlobals.TeamActivityTeams.LeftTeam: + direction = -1.0 + else: + direction = 1.0 + self.activity.b_pieHitsCog(timestamp, cogID, point, direction, hitHead) + if hitHead: + hitPoints = self.player.hitHead() + else: + hitPoints = self.player.hitBody() + self.player.updateScore() + if hitPoints > 0: + cog.showHitScore(hitPoints) + handled = True + elif 'distAvatarCollNode' in intoName: + parts = intoName.split('-') + hitToonId = int(parts[1]) + toon = base.cr.doId2do.get(hitToonId) + if toon is not None and self.activity.getTeam(hitToonId) != self.player.team: + point = colEntry.getSurfacePoint(toon) + self.activity.b_pieHitsToon(hitToonId, timestamp, point) + handled = True + if handled: + eventName = self.toonPieEventNames.get(colEntry.getFromNodePath()) + if eventName is not None: + self.ignore(eventName) + del self.toonPieEventNames[colEntry.getFromNodePath()] + + def pieHitsCog(self, timestamp, cogNum, pos, direction, part): + cog = self.cogManager.cogs[cogNum] + cog.respondToPieHit(timestamp, pos, part, direction) + + def pieHitsToon(self, toonId, timestamp, pos): + player = self.players.get(toonId) + if player is not None: + player.respondToPieHit(timestamp, pos) + return + + def setCogDistances(self, distances): + self.cogManager.updateDistances(distances) + + def showCogs(self): + for cog in self.cogManager.cogs: + cog.request('Static') + + def hideCogs(self): + for cog in self.cogManager.cogs: + cog.request('Down') + + def showResults(self, resultsText, winner, totals): + if self.player is None: + return + base.localAvatar.showName() + self.resultsIval = Sequence(Wait(0.1), Func(self.activity.setStatus, TTLocalizer.PartyCogTimeUp), Func(self.activity.showStatus), Wait(2.0), Func(self.activity.hideStatus), Wait(0.5), Func(self.player.lookAtArena), Func(self.showTeamFlags, self.activity.getTeam(base.localAvatar.doId)), Wait(1.0), Func(self.showArrow, 0), Wait(1.3), Func(self.showArrow, 1), Wait(1.3), Func(self.showArrow, 2), Wait(1.3), Func(self.showTotals, totals), Wait(1.0), Func(self.showWinner, resultsText, winner), Func(self._cleanupResultsIval), name='PartyCog-conclusionSequence') + self.accept('DistributedPartyActivity-showJellybeanReward', self._cleanupResultsIval) + self.resultsIval.start() + return + + def _cleanupResultsIval(self): + if self.resultsIval: + if self.resultsIval.isPlaying(): + self.resultsIval.pause() + self.resultsIval = None + self.ignore('DistributedPartyActivity-showJellybeanReward') + return + + def showTotals(self, totals): + newtotals = (totals[1] - totals[0] + PartyGlobals.CogActivityArenaLength / 2.0 * 3, totals[0] - totals[1] + PartyGlobals.CogActivityArenaLength / 2.0 * 3) + self.winText[0][0].setText(TTLocalizer.PartyCogDistance % newtotals[0]) + self.winText[1][0].setText(TTLocalizer.PartyCogDistance % newtotals[1]) + for textPair in self.winText: + textPair[1].unstash() + + def hideTotals(self): + for textPair in self.winText: + textPair[0].setText('') + textPair[1].stash() + + def showWinner(self, text, winner): + self.winStatus[0].setText(text) + self.winStatus[0].setTextColor(self.teamColors[winner]) + self.winStatus[1].unstash() + + def hideWinner(self): + self.winStatus[0].setText('') + self.winStatus[1].stash() + + def showArrow(self, arrowNum): + arrows = self.arrows[arrowNum] + cog = self.cogManager.cogs[arrowNum] + points = [self.arena.find('**/cog%d_start_locator' % (arrowNum + 1)), self.arena.find('**/cog%d_end_locator' % (arrowNum + 1))] + Y = cog.root.getY() + for point in points: + point.setY(Y) + + for i in xrange(len(arrows)): + arrow = arrows[i] + arrow.draw(points[i].getPos(), cog.root.getPos(), animate=False) + arrow.unstash() + + i = -1 + length = PartyGlobals.CogActivityArenaLength + for node, text in self.distanceLabels[arrowNum]: + current = bound(i, 0, 1) + node.setPos(cog.root.getPos(self.root) + Point3(i * 4, 2, 4)) + dist = PartyCogUtils.getCogDistanceUnitsFromCenter(cog.currentT) + dist = abs(dist - i * length / 2) + if dist > length - dist: + node.setScale(2.8) + else: + node.setScale(2.2) + text.setText(TTLocalizer.PartyCogDistance % dist) + if dist > 0: + node.unstash() + else: + arrows[current].stash() + i += 2 + + def hideArrows(self): + for pair in self.arrows: + for arrow in pair: + arrow.stash() + + for pair in self.distanceLabels: + for node, text in pair: + node.stash() + + def hideResults(self): + self.hideArrows() + self.hideTotals() + self.hideWinner() diff --git a/toontown/parties/PartyCogActivityGui.py b/toontown/parties/PartyCogActivityGui.py new file mode 100755 index 00000000..4b62767d --- /dev/null +++ b/toontown/parties/PartyCogActivityGui.py @@ -0,0 +1,329 @@ +from direct.gui.DirectGui import DirectWaitBar, DGG +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.DirectObject import DirectObject +from direct.interval.LerpInterval import LerpScaleInterval +from direct.interval.MetaInterval import Sequence +from direct.interval.FunctionInterval import Wait, Func +from pandac.PandaModules import Point3, VBase4 +from pandac.PandaModules import TextNode +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownIntervals +from toontown.toonbase import TTLocalizer +import PartyGlobals + +class PartyCogTrackerGui: + + def __init__(self): + self.cogTracker = loader.loadModel('phase_13/models/parties/cogTrackerGUI') + self.cogTracker.reparentTo(aspect2d) + self.cogTracker.setScale(1.25) + self.cogTracker.setX(1.0) + self.cogTracker.setZ(-0.75) + self.frame = self.cogTracker.find('**/tracker') + self.cogs = [] + self.cogLayers = [] + self.blinkIntervals = [] + i = 0 + self.cogTracker.find('**/shadow').setBin('fixed', 0) + self.cogTracker.find('**/plane').setBin('fixed', 1) + for i in xrange(3): + layers = [self.cogTracker.find('**/cog%d_blue' % i), self.cogTracker.find('**/cog%d_orange' % i), self.cogTracker.find('**/cog%d_white' % i)] + self.cogs.append(self.cogTracker.find('**/cog%d' % i)) + self.cogLayers.append(layers) + self.cogTracker.find('**/cog%d' % i).setBin('fixed', 2) + big = Point3(1.5, 1.5, 1.5) + seq = Sequence(LerpScaleInterval(self.cogs[i], duration=0.1, scale=big, startScale=Point3(1.0, 1.0, 1.0), blendType='easeOut'), LerpScaleInterval(self.cogs[i], duration=0.25, scale=Point3(1.0, 1.0, 1.0), startScale=big, blendType='easeOut'), Wait(0.4)) + self.blinkIntervals.append(seq) + + self.top = self.cogTracker.find('**/cog0_top').getZ() + self.bottom = self.cogTracker.find('**/cog0_bottom').getZ() + self.whiteTextureNp = self.cogTracker.find('**/cog0_white') + self.whiteTexture = self.whiteTextureNp.findTexture('*') + for cog in self.cogs: + cog.setTexture(self.whiteTexture) + + def updateCog(self, cogNumber, cog, team): + theCog = self.cogs[cogNumber] + t = (cog.currentT + 1.0) / 2.0 + Z = self.bottom + (self.top - self.bottom) * t + theCog.setZ(Z) + for layer in self.cogLayers[cogNumber]: + layer.stash() + + if cog.currentT < 0.0: + self.cogLayers[cogNumber][0].unstash() + if team == 1 and cog.currentT < -0.5 and not self.blinkIntervals[cogNumber].isPlaying(): + self.blinkIntervals[cogNumber].start() + elif cog.currentT > 0.0: + self.cogLayers[cogNumber][1].unstash() + if team == 0 and cog.currentT > 0.5 and not self.blinkIntervals[cogNumber].isPlaying(): + self.blinkIntervals[cogNumber].start() + else: + self.cogLayers[cogNumber][2].unstash() + + def destory(self): + if self.blinkIntervals is not None: + for interval in self.blinkIntervals: + if interval is not None: + interval.clearToInitial() + interval = None + + if self.cogTracker is not None: + self.cogTracker.removeNode() + self.cogTracker = None + return + + +class PartyCogActivityGui(DirectObject): + notify = directNotify.newCategory('PartyCogActivityGui') + + def __init__(self): + DirectObject.__init__(self) + self._piePowerMeter = None + self._victoryBalanceBar = None + self._scoreLabel = None + self._cogTracker = None + self._piePowerTitle = None + self._victoryBalanceTitle = None + self._scoreTitle = None + self._spamWarning = None + self._spamWarningIvalName = 'PartyCogActivityGui-SpamWarning' + return + + def load(self): + self._initPiePowerMeter() + self._initScore() + self._initCogTracker() + self._initSpamWarning() + self._initControlGui() + self._initVictoryBalanceBar() + + def unload(self): + if self._cogTracker is not None: + self._cogTracker.destory() + self._cogTracker = None + if self._piePowerMeter is not None: + self._piePowerMeter.destroy() + self._piePowerMeter = None + if self._piePowerTitle is not None: + self._piePowerTitle.destroy() + self._piePowerTitle = None + if self._scoreLabel is not None: + self._scoreLabel.destroy() + self._scoreLabel = None + if self._scoreTitle is not None: + self._scoreTitle.destroy() + self._scoreTitle = None + taskMgr.remove(self._spamWarningIvalName) + if self._spamWarning: + self._spamWarning.destroy() + self._spamWarning = None + if hasattr(self, '_attackKeys'): + self._attackKeys.detachNode() + del self._attackKeys + if hasattr(self, '_moveKeys'): + self._moveKeys.detachNode() + del self._moveKeys + if self._victoryBalanceBar: + self._victoryBalanceBar.detachNode() + self._victoryBalanceBar = None + if self._victoryBalanceBarOrange: + self._victoryBalanceBarOrange.detachNode() + self._victoryBalanceBarOrange = None + if self._victoryBalanceBarPie: + self._victoryBalanceBarPie.detachNode() + self._victoryBalanceBarPie = None + if self._victoryBalanceBarArrow: + self._victoryBalanceBarArrow.detachNode() + self._victoryBalanceBarArrow = None + return + + def _initVictoryBalanceBar(self): + h = PartyGlobals.CogActivityPowerMeterHeight / 2.0 + w = PartyGlobals.CogActivityPowerMeterWidth / 2.0 + victoryBalanceBar = loader.loadModel('phase_13/models/parties/tt_m_gui_pty_pieToss_balanceBar') + self._victoryBalanceBar = victoryBalanceBar.find('**/*tt_t_gui_pty_pieToss_balanceBarBG') + self._victoryBalanceBar.reparentTo(aspect2d) + self._victoryBalanceBar.setBin('fixed', 0) + self._victoryBalanceBar.setPos(PartyGlobals.CogActivityVictoryBarPos) + self._victoryBalanceBar.setScale(1) + self._victoryBalanceBarOrange = victoryBalanceBar.find('**/*tt_t_gui_pty_pieToss_balanceBarOrange') + self._victoryBalanceBarOrange.reparentTo(self._victoryBalanceBar) + self._victoryBalanceBarOrange.setBin('fixed', 1) + self._victoryBalanceBarOrange.setPos(PartyGlobals.CogActivityVictoryBarOrangePos) + self._victoryBalanceBarOrange.setScale(PartyGlobals.CogActivityBarStartScale, 1.0, 1.0) + self._victoryBalanceBarPie = victoryBalanceBar.find('**/*tt_t_gui_pty_pieToss_balanceBarPie') + self._victoryBalanceBarPie.reparentTo(self._victoryBalanceBar) + self._victoryBalanceBarPie.setBin('fixed', 2) + self._victoryBalanceBarPie.setX(PartyGlobals.CogActivityVictoryBarPiePos[0]) + self._victoryBalanceBarPie.setY(PartyGlobals.CogActivityVictoryBarPiePos[1]) + self._victoryBalanceBarPie.setZ(PartyGlobals.CogActivityVictoryBarPiePos[2]) + self._victoryBalanceBarPie.setScale(PartyGlobals.CogActivityBarPieScale) + self._victoryBalanceBarArrow = victoryBalanceBar.find('**/*tt_t_gui_pty_pieToss_balanceArrow') + self._victoryBalanceBarArrow.reparentTo(self._victoryBalanceBarPie) + self._victoryBalanceBarArrow.setBin('fixed', 2) + self._victoryBalanceBarArrow.setPos(PartyGlobals.CogActivityVictoryBarArrow) + self._victoryBalanceBarArrow.setScale(1 / PartyGlobals.CogActivityBarPieScale) + + def _initControlGui(self): + self._attackIvalName = 'PartyCogActivityGui-attackKeys' + self._moveIvalName = 'PartyCogActivityGui-moveKeys' + pieTossControls = loader.loadModel('phase_13/models/parties/tt_m_gui_pty_pieToss_controls') + self._attackKeys = pieTossControls.find('**/*control*') + self._moveKeys = pieTossControls.find('**/*arrow*') + self._moveKeys.reparentTo(aspect2d) + self._moveKeys.setPos(1.0, 0.0, -0.435) + self._moveKeys.setScale(0.15) + self._attackKeys.reparentTo(aspect2d) + self._attackKeys.setPos(0.85, 0.0, -0.45) + self._attackKeys.setScale(0.15) + self._moveKeys.hide() + self._attackKeys.hide() + + def _initPiePowerMeter(self): + h = PartyGlobals.CogActivityPowerMeterHeight / 2.0 + w = PartyGlobals.CogActivityPowerMeterWidth / 2.0 + self._piePowerMeter = DirectWaitBar(frameSize=(-h, + h, + -w, + w), relief=DGG.GROOVE, frameColor=(0.9, 0.9, 0.9, 1.0), borderWidth=(0.01, 0.01), barColor=PartyGlobals.CogActivityColors[0], pos=PartyGlobals.CogActivityPowerMeterPos, hpr=(0.0, 0.0, -90.0)) + self._piePowerMeter.setBin('fixed', 0) + self._piePowerTitle = OnscreenText(text=TTLocalizer.PartyCogGuiPowerLabel, pos=PartyGlobals.CogActivityPowerMeterTextPos, scale=0.05, fg=(1.0, 1.0, 1.0, 1.0), align=TextNode.ACenter) + self._piePowerTitle.setBin('fixed', 0) + self._piePowerMeter.hide() + self._piePowerTitle.hide() + + def _initScore(self): + self._scoreLabel = OnscreenText(text='0', pos=PartyGlobals.CogActivityScorePos, scale=PartyGlobals.TugOfWarTextWordScale, fg=(1.0, 1.0, 0.0, 1.0), align=TextNode.ARight, font=ToontownGlobals.getSignFont(), mayChange=True) + self._scoreTitle = OnscreenText(text=TTLocalizer.PartyCogGuiScoreLabel, pos=PartyGlobals.CogActivityScoreTitle, scale=0.05, fg=(1.0, 1.0, 1.0, 1.0), align=TextNode.ARight) + self._scoreLabel.hide() + self._scoreTitle.hide() + + def _initCogTracker(self): + self._cogTracker = PartyCogTrackerGui() + + def _initSpamWarning(self): + self._spamWarning = OnscreenText(text=TTLocalizer.PartyCogGuiSpamWarning, scale=0.15, fg=(1.0, 1.0, 0, 1.0), shadow=(0, 0, 0, 0.62), mayChange=False, pos=(0, 0.33)) + self._spamWarning.hide() + + def showScore(self): + self._scoreLabel.show() + self._scoreTitle.show() + + def hideScore(self): + self._scoreLabel.hide() + self._scoreTitle.hide() + + def setScore(self, score = 0): + self._scoreLabel['text'] = str(score) + + def resetPiePowerMeter(self): + self._piePowerMeter['value'] = 0 + + def showPiePowerMeter(self): + self._piePowerMeter.show() + self._piePowerTitle.show() + + def hidePiePowerMeter(self): + self._piePowerMeter.hide() + self._piePowerTitle.hide() + + def updatePiePowerMeter(self, value): + self._piePowerMeter['value'] = value + + def getPiePowerMeterValue(self): + return self._piePowerMeter['value'] + + def hideSpamWarning(self): + taskMgr.remove(self._spamWarningIvalName) + if self._spamWarning: + self._spamWarning.hide() + + def showSpamWarning(self): + if self._spamWarning.isHidden(): + self._spamWarning.show() + taskMgr.remove(self._spamWarningIvalName) + Sequence(ToontownIntervals.getPulseLargerIval(self._spamWarning, ''), Wait(PartyGlobals.CogActivitySpamWarningShowTime), Func(self.hideSpamWarning), name=self._spamWarningIvalName, autoFinish=1).start() + + def hide(self): + self.hidePiePowerMeter() + self.hideScore() + self.hideSpamWarning() + self.hideControls() + + def disableToontownHUD(self): + base.localAvatar.hideName() + base.localAvatar.laffMeter.hide() + base.setCellsAvailable(base.bottomCells + [base.rightCells[1]], False) + + def enableToontownHUD(self): + base.localAvatar.showName() + base.localAvatar.laffMeter.show() + base.setCellsAvailable(base.bottomCells + [base.rightCells[1]], True) + + def setTeam(self, team): + self.team = team + if team == 0: + self._cogTracker.frame.setR(180) + self._piePowerMeter['barColor'] = PartyGlobals.CogActivityColors[team] + + def startTrackingCogs(self, cogs): + self.cogs = cogs + taskMgr.add(self.trackCogs, 'trackCogs') + + def trackCogs(self, task): + if self.cogs is None: + return + self._updateVictoryBar() + for i, cog in enumerate(self.cogs): + self._cogTracker.updateCog(i, cog, self.team) + + return task.cont + + def _updateVictoryBar(self): + if not (hasattr(self, '_victoryBalanceBar') and self._victoryBalanceBar): + return + netDistance = 0 + for cog in self.cogs: + netDistance = netDistance + cog.targetDistance + + teamDistance = netDistance / 6.0 + self._victoryBalanceBarOrange.setScale(PartyGlobals.CogActivityBarStartScale + teamDistance * 10 * PartyGlobals.CogActivityBarUnitScale, 1.0, 1.0) + self._victoryBalanceBarPie.setX(PartyGlobals.CogActivityVictoryBarPiePos[0] + teamDistance * 10 * PartyGlobals.CogActivityBarPieUnitMove) + self._victoryBalanceBarPie.setY(PartyGlobals.CogActivityVictoryBarPiePos[1]) + self._victoryBalanceBarPie.setZ(PartyGlobals.CogActivityVictoryBarPiePos[2]) + if teamDistance > 0.0: + self._victoryBalanceBarArrow.setColor(PartyGlobals.CogActivityColors[1]) + elif teamDistance < 0.0: + self._victoryBalanceBarArrow.setColor(PartyGlobals.CogActivityColors[0]) + else: + self._victoryBalanceBarArrow.setColor(VBase4(1.0, 1.0, 1.0, 1.0)) + + def stopTrackingCogs(self): + taskMgr.remove('trackCogs') + + def showAttackControls(self): + if self._attackKeys.isHidden(): + self._attackKeys.show() + taskMgr.remove(self._attackIvalName) + Sequence(ToontownIntervals.getPulseLargerIval(self._attackKeys, '', scale=0.15), Wait(PartyGlobals.CogActivityControlsShowTime), Func(self.hideAttackControls), name=self._attackIvalName, autoFinish=1).start() + + def showMoveControls(self): + if self._moveKeys.isHidden() and not self._attackKeys.isHidden(): + self._moveKeys.show() + taskMgr.remove(self._moveIvalName) + Sequence(ToontownIntervals.getPulseLargerIval(self._moveKeys, '', scale=0.15), Wait(PartyGlobals.CogActivityControlsShowTime), Func(self.hideMoveControls), name=self._moveIvalName, autoFinish=1).start() + + def hideAttackControls(self): + taskMgr.remove(self._attackIvalName) + if hasattr(self, '_attackKeys') and self._attackKeys: + self._attackKeys.hide() + + def hideMoveControls(self): + taskMgr.remove(self._moveIvalName) + if hasattr(self, '_moveKeys') and self._moveKeys: + self._moveKeys.hide() + + def hideControls(self): + self.hideMoveControls() + self.hideAttackControls() diff --git a/toontown/parties/PartyCogActivityInput.py b/toontown/parties/PartyCogActivityInput.py new file mode 100755 index 00000000..b7a010fd --- /dev/null +++ b/toontown/parties/PartyCogActivityInput.py @@ -0,0 +1,158 @@ +from direct.showbase.DirectObject import DirectObject +from pandac.PandaModules import ModifierButtons +ROTATE_LEFT_KEY = 'arrow_left' +ROTATE_RIGHT_KEY = 'arrow_right' +FORWARD_KEY = 'arrow_up' +BACKWARDS_KEY = 'arrow_down' +THROW_PIE_KEYS = ['control', 'delete', 'insert'] + +class PartyCogActivityInput(DirectObject): + notify = directNotify.newCategory('PartyCogActivityInput') + leftPressed = 0 + rightPressed = 0 + upPressed = 0 + downPressed = 0 + throwPiePressed = False + throwPieWasReleased = False + throwPiePressedStartTime = 0 + + def __init__(self, exitActivityCallback): + DirectObject.__init__(self) + self.exitActivityCallback = exitActivityCallback + self._prevModifierButtons = base.mouseWatcherNode.getModifierButtons() + + def enable(self): + self.enableAimKeys() + self.enableThrowPieKeys() + + def disable(self): + self.disableAimKeys() + self.disableThrowPieKeys() + + def enableExitActivityKeys(self): + self.accept('escape', self.exitActivityCallback) + + def disableExitActivityKeys(self): + self.ignore('escape') + + def enableThrowPieKeys(self): + for key in THROW_PIE_KEYS: + self.accept(key, self.handleThrowPieKeyPressed, [key]) + + self.throwPiePressed = False + self.readyToThrowPie = False + + def disableThrowPieKeys(self): + for key in THROW_PIE_KEYS: + self.ignore(key) + self.ignore(key + '-up') + + def handleThrowPieKeyPressed(self, key): + if self.throwPiePressed: + return + self.throwPiePressed = True + self.accept(key + '-up', self.handleThrowPieKeyReleased, [key]) + self.throwPiePressedStartTime = globalClock.getFrameTime() + + def handleThrowPieKeyReleased(self, key): + if not self.throwPiePressed: + return + self.ignore(key + '-up') + self.throwPieWasReleased = True + self.throwPiePressed = False + + def enableAimKeys(self): + self.leftPressed = 0 + self.rightPressed = 0 + base.mouseWatcherNode.setModifierButtons(ModifierButtons()) + base.buttonThrowers[0].node().setModifierButtons(ModifierButtons()) + self.accept(ROTATE_LEFT_KEY, self.__handleLeftKeyPressed) + self.accept(ROTATE_RIGHT_KEY, self.__handleRightKeyPressed) + self.accept(FORWARD_KEY, self.__handleUpKeyPressed) + self.accept(BACKWARDS_KEY, self.__handleDownKeyPressed) + + def disableAimKeys(self): + self.ignore(ROTATE_LEFT_KEY) + self.ignore(ROTATE_RIGHT_KEY) + self.ignore(FORWARD_KEY) + self.ignore(BACKWARDS_KEY) + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.ignore(ROTATE_LEFT_KEY + '-up') + self.ignore(ROTATE_RIGHT_KEY + '-up') + self.ignore(FORWARD_KEY + '-up') + self.ignore(BACKWARDS_KEY + '-up') + base.mouseWatcherNode.setModifierButtons(self._prevModifierButtons) + base.buttonThrowers[0].node().setModifierButtons(self._prevModifierButtons) + + def __handleLeftKeyPressed(self): + self.ignore(ROTATE_LEFT_KEY) + self.accept(ROTATE_LEFT_KEY + '-up', self.__handleLeftKeyReleased) + self.__leftPressed() + + def __handleRightKeyPressed(self): + self.ignore(ROTATE_RIGHT_KEY) + self.accept(ROTATE_RIGHT_KEY + '-up', self.__handleRightKeyReleased) + self.__rightPressed() + + def __handleLeftKeyReleased(self): + self.ignore(ROTATE_LEFT_KEY + '-up') + self.accept(ROTATE_LEFT_KEY, self.__handleLeftKeyPressed) + self.__leftReleased() + + def __handleRightKeyReleased(self): + self.ignore(ROTATE_RIGHT_KEY + '-up') + self.accept(ROTATE_RIGHT_KEY, self.__handleRightKeyPressed) + self.__rightReleased() + + def __handleUpKeyPressed(self): + self.ignore(FORWARD_KEY) + self.accept(FORWARD_KEY + '-up', self.__handleUpKeyReleased) + self.__upPressed() + + def __handleUpKeyReleased(self): + self.ignore(FORWARD_KEY + '-up') + self.accept(FORWARD_KEY, self.__handleUpKeyPressed) + self.__upReleased() + + def __handleDownKeyPressed(self): + self.ignore(BACKWARDS_KEY) + self.accept(BACKWARDS_KEY + '-up', self.__handleDownKeyReleased) + self.__downPressed() + + def __handleDownKeyReleased(self): + self.ignore(BACKWARDS_KEY + '-up') + self.accept(BACKWARDS_KEY, self.__handleDownKeyPressed) + self.__downReleased() + + def __leftPressed(self): + self.leftPressed = self.__enterControlActive(self.leftPressed) + + def __rightPressed(self): + self.rightPressed = self.__enterControlActive(self.rightPressed) + + def __upPressed(self): + self.upPressed = self.__enterControlActive(self.upPressed) + + def __downPressed(self): + self.downPressed = self.__enterControlActive(self.downPressed) + + def __leftReleased(self): + self.leftPressed = self.__exitControlActive(self.leftPressed) + + def __rightReleased(self): + self.rightPressed = self.__exitControlActive(self.rightPressed) + + def __upReleased(self): + self.upPressed = self.__exitControlActive(self.upPressed) + + def __downReleased(self): + self.downPressed = self.__exitControlActive(self.downPressed) + + def __enterControlActive(self, input): + return input + 1 + + def __exitControlActive(self, input): + return max(0, input - 1) diff --git a/toontown/parties/PartyCogActivityPlayer.py b/toontown/parties/PartyCogActivityPlayer.py new file mode 100755 index 00000000..e9cb0057 --- /dev/null +++ b/toontown/parties/PartyCogActivityPlayer.py @@ -0,0 +1,344 @@ +import math +from direct.showbase.PythonUtil import bound, lerp +from direct.task.Task import Task +from direct.interval.MetaInterval import Sequence, Parallel +from direct.interval.FunctionInterval import Func, Wait +from direct.interval.SoundInterval import SoundInterval +from direct.interval.LerpInterval import LerpScaleInterval, LerpFunc +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath, Point3, VBase3 +from toontown.minigame.OrthoDrive import OrthoDrive +from toontown.minigame.OrthoWalk import OrthoWalk +from toontown.battle.BattleProps import globalPropPool +from toontown.battle.BattleSounds import globalBattleSoundCache +import PartyGlobals +from PartyCogActivityInput import PartyCogActivityInput +from PartyCogActivityGui import PartyCogActivityGui +from PartyCogUtils import CameraManager +from PartyCogUtils import StrafingControl +UPDATE_TASK_NAME = 'PartyCogActivityLocalPlayer_UpdateTask' +THROW_PIE_LIMIT_TIME = 0.2 + +class PartyCogActivityPlayer: + toon = None + position = None + team = None + score = 0 + enabled = False + notify = DirectNotifyGlobal.directNotify.newCategory('PartyCogActivityPlayer') + + def __init__(self, activity, toon, position, team): + self.activity = activity + self.position = position + self.toon = toon + self.team = team + self.netTimeSentToStartByHit = 0 + self.kaboomTrack = None + self.locator = None + self.teamSpot = self.activity.getIndex(self.toon.doId, self.team) + splatName = 'splat-creampie' + self.splat = globalPropPool.getProp(splatName) + self.splat.setBillboardPointEye() + self.splatType = globalPropPool.getPropType(splatName) + self.pieHitSound = globalBattleSoundCache.getSound('AA_wholepie_only.ogg') + return + + def destroy(self): + self.cleanUpIvals() + self.toon = None + self.locator = None + self.position = None + self.pieHitSound = None + self.splat = None + return + + def cleanUpIvals(self): + if self.kaboomTrack is not None and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + self.kaboomTrack = None + return + + def faceForward(self): + self.toon.setH(0) + + def setToonStartPosition(self, position): + self.position = position + + def updateToonPosition(self): + self.toon.setPos(self.activity.root, self.position) + self.toon.setH(self.locator, 0.0) + + def setTeam(self, team): + self.team = team + self.locator = self.activity.view.teamLocators[self.team] + self.teamSpot = self.activity.getIndex(self.toon.doId, self.team) + + def entersActivity(self): + self.locator = self.activity.view.teamLocators[self.team] + + def exitsActivity(self): + offset = 0 + if self.teamSpot: + offset = 4.0 * self.teamSpot + self.toon.wrtReparentTo(render) + self.toon.setPos(self.locator, offset, -35.0, 0.0) + self.toon.setH(self.activity.root, 0.0) + self.locator = None + return + + def enable(self): + if self.enabled: + return + self.toon.wrtReparentTo(self.locator) + self.enabled = True + + def disable(self): + if not self.enabled: + return + self.toon.wrtReparentTo(render) + self.enabled = False + self.cleanUpIvals() + + def hitBody(self): + points = PartyGlobals.CogActivityHitPoints + self.score += points + return points + + def hitHead(self): + points = PartyGlobals.CogActivityHitPointsForHead + self.score += points + return points + + def resetScore(self): + self.score = 0 + + def respondToPieHit(self, timestamp, pos): + if self.netTimeSentToStartByHit < timestamp: + self.__showSplat(pos) + if self.netTimeSentToStartByHit < timestamp: + self.netTimeSentToStartByHit = timestamp + else: + self.activity.notify.debug('PartyCogPlayer respondToPieHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit) + + def __showSplat(self, position): + if self.kaboomTrack is not None and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + if not self.pieHitSound: + self.notify.warning('Trying to play hit sound on destroyed player') + return + splatName = 'splat-creampie' + self.splat = globalPropPool.getProp(splatName) + self.splat.setBillboardPointEye() + self.splat.reparentTo(render) + self.splat.setPos(self.toon, position) + self.splat.setY(self.toon, bound(self.splat.getY(), self.toon.getHeight() / 2.0, position.getY())) + self.splat.setAlphaScale(1.0) + targetscale = 0.75 + + def setSplatAlpha(amount): + self.splat.setAlphaScale(amount) + + self.kaboomTrack = Parallel(SoundInterval(self.pieHitSound, node=self.toon, volume=1.0, cutOff=PartyGlobals.PARTY_COG_CUTOFF), Sequence(Func(self.splat.showThrough), Parallel(Sequence(LerpScaleInterval(self.splat, duration=0.175, scale=targetscale, startScale=Point3(0.1, 0.1, 0.1), blendType='easeOut'), Wait(0.175)), Sequence(Wait(0.1), LerpFunc(setSplatAlpha, duration=1.0, fromData=1.0, toData=0.0, blendType='easeOut'))), Func(self.splat.cleanup), Func(self.splat.removeNode))) + self.kaboomTrack.start() + return + + +class PartyCogActivityLocalPlayer(PartyCogActivityPlayer): + + def __init__(self, activity, position, team, exitActivityCallback = None): + PartyCogActivityPlayer.__init__(self, activity, base.localAvatar, position, team) + self.input = PartyCogActivityInput(exitActivityCallback) + self.gui = PartyCogActivityGui() + self.throwPiePrevTime = 0 + self.lastMoved = 0 + if base.localAvatar: + self.prevPos = base.localAvatar.getPos() + self.cameraManager = None + self.control = None + self.consecutiveShortThrows = 0 + return + + def destroy(self): + if self.enabled: + self.disable() + if self.cameraManager is not None: + self.cameraManager.setEnabled(False) + self.cameraManager.destroy() + del self.cameraManager + del self.gui + del self.input + if self.control is not None: + self.control.destroy() + del self.control + PartyCogActivityPlayer.destroy(self) + return + + def _initOrthoWalk(self): + orthoDrive = OrthoDrive(9.778, customCollisionCallback=self.activity.view.checkOrthoDriveCollision) + self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True) + + def _destroyOrthoWalk(self): + self.orthoWalk.stop() + self.orthoWalk.destroy() + del self.orthoWalk + + def getPieThrowingPower(self, time): + elapsed = max(time - self.input.throwPiePressedStartTime, 0.0) + w = 1.0 / PartyGlobals.CogActivityPowerMeterTime * 2.0 * math.pi + power = int(round(-math.cos(w * elapsed) * 50.0 + 50.0)) + return power + + def isShortThrow(self, time): + elapsed = max(time - self.input.throwPiePressedStartTime, 0.0) + return elapsed <= PartyGlobals.CogActivityShortThrowTime + + def checkForThrowSpam(self, time): + if self.isShortThrow(time): + self.consecutiveShortThrows += 1 + else: + self.consecutiveShortThrows = 0 + return self.consecutiveShortThrows >= PartyGlobals.CogActivityShortThrowSpam + + def _startUpdateTask(self): + task = Task(self._updateTask) + task.lastPositionBroadcastTime = 0.0 + self.throwPiePrevTime = 0 + taskMgr.add(task, UPDATE_TASK_NAME) + + def _stopUpdateTask(self): + taskMgr.remove(UPDATE_TASK_NAME) + + def _updateTask(self, task): + self._update() + if base.localAvatar.getPos() != self.prevPos: + self.prevPos = base.localAvatar.getPos() + self.lastMoved = self.activity.getCurrentActivityTime() + if max(self.activity.getCurrentActivityTime() - self.lastMoved, 0) > PartyGlobals.ToonMoveIdleThreshold: + self.gui.showMoveControls() + if max(self.activity.getCurrentActivityTime() - self.throwPiePrevTime, 0) > PartyGlobals.ToonAttackIdleThreshold: + self.gui.showAttackControls() + if self.input.throwPieWasReleased: + if self.checkForThrowSpam(globalClock.getFrameTime()): + self.gui.showSpamWarning() + self.input.throwPieWasReleased = False + self.throwPie(self.getPieThrowingPower(globalClock.getFrameTime())) + return Task.cont + + def throwPie(self, piePower): + if not self.activity.isState('Active'): + return + if self.activity.getCurrentActivityTime() - self.throwPiePrevTime > THROW_PIE_LIMIT_TIME: + self.throwPiePrevTime = self.activity.getCurrentActivityTime() + self.activity.b_pieThrow(self.toon, piePower) + + def _update(self): + self.control.update() + + def getLookat(self, whosLooking, refNode = None): + if refNode is None: + refNode = render + dist = 5.0 + oldParent = self.tempNP.getParent() + self.tempNP.reparentTo(whosLooking) + self.tempNP.setPos(0.0, dist, 0.0) + pos = self.tempNP.getPos(refNode) + self.tempNP.reparentTo(oldParent) + return pos + + def entersActivity(self): + base.cr.playGame.getPlace().setState('activity') + PartyCogActivityPlayer.entersActivity(self) + self.gui.disableToontownHUD() + self.cameraManager = CameraManager(camera) + self.tempNP = NodePath('temp') + self.lookAtMyTeam() + self.control = StrafingControl(self) + + def exitsActivity(self): + PartyCogActivityPlayer.exitsActivity(self) + self.gui.enableToontownHUD() + self.cameraManager.setEnabled(False) + self.tempNP.removeNode() + self.tempNP = None + if not aspect2d.find('**/JellybeanRewardGui*'): + base.cr.playGame.getPlace().setState('walk') + else: + self.toon.startPosHprBroadcast() + return + + def getRunToStartPositionIval(self): + targetH = self.locator.getH() + travelVec = self.position - self.toon.getPos(self.activity.root) + duration = travelVec.length() / 9.778 + startH = 0.0 + if travelVec.getY() < 0.0: + startH = 180.0 + return Sequence(Func(self.toon.startPosHprBroadcast, 0.1), Func(self.toon.b_setAnimState, 'run'), Parallel(self.toon.hprInterval(0.5, VBase3(startH, 0.0, 0.0), other=self.activity.root), self.toon.posInterval(duration, self.position, other=self.activity.root)), Func(self.toon.b_setAnimState, 'neutral'), self.toon.hprInterval(0.25, VBase3(targetH, 0.0, 0.0), other=self.activity.root), Func(self.toon.stopPosHprBroadcast)) + + def enable(self): + if self.enabled: + return + PartyCogActivityPlayer.enable(self) + self.toon.b_setAnimState('Happy') + self._initOrthoWalk() + self.orthoWalk.start() + self.orthoWalking = True + self.input.enable() + self.gui.disableToontownHUD() + self.gui.load() + self.gui.setScore(0) + self.gui.showScore() + self.gui.setTeam(self.team) + self.gui.startTrackingCogs(self.activity.view.cogManager.cogs) + self.control.enable() + self._startUpdateTask() + + def disable(self): + if not self.enabled: + return + self._stopUpdateTask() + self.toon.b_setAnimState('neutral') + PartyCogActivityPlayer.disable(self) + self.orthoWalking = False + self.orthoWalk.stop() + self._destroyOrthoWalk() + self.input.disable() + self._aimMode = False + self.cameraManager.setEnabled(False) + self.gui.hide() + self.gui.stopTrackingCogs() + self.gui.unload() + + def updateScore(self): + self.gui.setScore(self.score) + + def b_updateToonPosition(self): + self.updateToonPosition() + self.d_updateToonPosition() + + def d_updateToonPosition(self): + self.toon.d_setPos(self.toon.getX(), self.toon.getY(), self.toon.getZ()) + self.toon.d_setH(self.toon.getH()) + + def lookAtArena(self): + self.cameraManager.setEnabled(True) + self.cameraManager.setTargetPos(self.activity.view.arena.find('**/conclusionCamPos_locator').getPos(render)) + self.cameraManager.setTargetLookAtPos(self.activity.view.arena.find('**/conclusionCamAim_locator').getPos(render)) + + def lookAtMyTeam(self): + activityView = self.activity.view + arena = activityView.arena + pos = activityView.teamCamPosLocators[self.team].getPos() + aim = activityView.teamCamAimLocators[self.team].getPos() + camera.wrtReparentTo(arena) + self.cameraManager.setPos(camera.getPos(render)) + self.tempNP.reparentTo(arena) + self.tempNP.setPos(arena, pos) + self.cameraManager.setTargetPos(self.tempNP.getPos(render)) + self.cameraManager.setLookAtPos(self.getLookat(camera)) + self.tempNP.reparentTo(arena) + self.tempNP.setPos(arena, aim) + self.cameraManager.setTargetLookAtPos(self.tempNP.getPos(render)) + self.cameraManager.setEnabled(True) + camera.setP(0.0) + camera.setR(0.0) diff --git a/toontown/parties/PartyCogUtils.py b/toontown/parties/PartyCogUtils.py new file mode 100755 index 00000000..f847601e --- /dev/null +++ b/toontown/parties/PartyCogUtils.py @@ -0,0 +1,132 @@ +import math +from pandac.PandaModules import NodePath, Point3 +import PartyGlobals +inverse_e = 1.0 / math.e + +def getCogDistanceUnitsFromCenter(distance): + return int(round(distance * (PartyGlobals.CogActivityArenaLength / 2.0))) + + +class CameraManager: + nextID = 0 + + def __init__(self, cameraNP): + self.cameraNP = cameraNP + self.id = CameraManager.nextID + CameraManager.nextID += 1 + self.otherNP = render + self.lookAtNP = NodePath('CameraManager%d.lookAtNP' % self.id) + self.lookAtEnabled = False + self.targetPos = Point3(0.0, 0.0, 0.0) + self.targetLookAtPos = Point3(0.0, 1.0, 0.0) + self.enabled = False + self.rate = 10.0 + + def destroy(self): + if self.enabled: + self.setEnabled(False) + self.lookAtNP.removeNode() + del self.lookAtNP + del self.targetPos + del self.targetLookAtPos + del self.otherNP + + def setEnabled(self, enabled): + if enabled != self.enabled: + if enabled: + taskMgr.add(self.updateTask, 'CameraManager%d.update' % self.id) + else: + taskMgr.remove('CameraManager%d.update' % self.id) + self.enabled = enabled + + def setTargetPos(self, p): + self.targetPos = p + + def setPos(self, p): + self.targetPos = p + self.cameraNP.setPos(self.otherNP, p) + + def setTargetLookAtPos(self, p): + self.lookAtEnabled = True + self.targetLookAtPos = p + + def setLookAtPos(self, p): + self.lookAtEnabled = True + self.targetLookAtPos = p + self.lookAtNP.setPos(p) + + def setHpr(self, hpr): + self.lookAtEnabled = False + self.cameraNP.setHpr(self.otherNP, hpr) + + def updateTask(self, task): + newCameraPos = self.rateInterpolate(self.cameraNP.getPos(self.otherNP), self.targetPos) + self.cameraNP.setPos(self.otherNP, newCameraPos) + if self.lookAtEnabled: + newLookAtPos = self.rateInterpolate(self.lookAtNP.getPos(self.otherNP), self.targetLookAtPos) + self.lookAtNP.setPos(self.otherNP, newLookAtPos) + self.cameraNP.lookAt(self.lookAtNP) + return task.cont + + def rateInterpolate(self, currentPos, targetPos): + dt = globalClock.getDt() + vec = currentPos - targetPos + return targetPos + vec * inverse_e ** (dt * self.rate) + + +class StrafingControl: + + def __init__(self, player): + self.player = player + self.defaultOffset = Point3(1.0, -7.5, self.player.toon.getHeight() + 1.0) + + def destroy(self): + self.player = None + del self.player + self.defaultOffset = None + del self.defaultOffset + return + + def update(self): + self.player.tempNP.setPos(self.player.locator, self.player.toon.getPos() + self.defaultOffset) + self.player.cameraManager.setTargetPos(self.player.tempNP.getPos(render)) + self.player.tempNP.setPos(self.player.locator, self.player.toon.getPos() + self.defaultOffset + Point3(0, 20, 0)) + self.player.cameraManager.setTargetLookAtPos(self.player.tempNP.getPos(render)) + if not self.player._aimMode and self.player.input.throwPiePressed: + self.toggleAim() + if self.player._aimMode and not self.player.input.throwPiePressed and (self.player.input.upPressed or self.player.input.downPressed or self.player.input.leftPressed or self.player.input.rightPressed): + self.toggleAim() + if not self.player._aimMode: + if not (self.player.input.upPressed or self.player.input.downPressed or self.player.input.leftPressed or self.player.input.rightPressed): + self.player.faceForward() + return + if self.player.input.throwPiePressed: + self.player.gui.updatePiePowerMeter(self.player.getPieThrowingPower(globalClock.getFrameTime())) + + def toggleAim(self): + self.player._aimMode = not self.player._aimMode + if not self.player._aimMode: + self.player.orthoWalking = True + self.player.orthoWalk.start() + self.player._rotation = 0.0 + self.player._prevRotation = 0.0 + self.player.gui.hidePiePowerMeter() + self.player.toon.setH(0.0) + else: + self.player.orthoWalk.stop() + self.player.orthoWalking = False + self.player.toon.setH(0.0) + self.player.gui.showPiePowerMeter() + + def enable(self): + self.player._aimMode = False + camera.wrtReparentTo(self.player.locator) + self.player.cameraManager.setEnabled(True) + activityView = self.player.activity.view + pos = activityView.teamCamPosLocators[self.player.team].getPos(render) + aim = activityView.teamCamAimLocators[self.player.team].getPos(render) + self.player.cameraManager.setPos(pos) + self.player.cameraManager.setLookAtPos(aim) + self.player.tempNP.reparentTo(self.player.locator) + self.player.tempNP.setPos(self.player.locator, self.player.toon.getPos() + self.defaultOffset) + self.player.cameraManager.setTargetPos(self.player.tempNP.getPos(render)) diff --git a/toontown/parties/PartyDanceActivityToonFSM.py b/toontown/parties/PartyDanceActivityToonFSM.py new file mode 100755 index 00000000..a8a6622c --- /dev/null +++ b/toontown/parties/PartyDanceActivityToonFSM.py @@ -0,0 +1,92 @@ +from panda3d.core import * +from direct.fsm.FSM import FSM +from direct.showbase import PythonUtil +from direct.interval.MetaInterval import Sequence +from toontown.parties.PartyGlobals import DanceReverseLoopAnims, ToonDancingStates + +class PartyDanceActivityToonFSM(FSM): + notify = directNotify.newCategory('PartyDanceActivityToonFSM') + + def __init__(self, avId, activity, h): + FSM.__init__(self, self.__class__.__name__) + self.notify.debug('init : avId = %s, activity = %s ' % (avId, activity)) + self.avId = avId + self.activity = activity + self.isLocal = avId == base.localAvatar.doId + self.toon = self.activity.getAvatar(self.avId) + self.toonH = h + self.danceNode = None + self.danceMoveSequence = None + self.lastAnim = None + self.defaultTransitions = {'Init': ['Run', 'DanceMove', 'Cleanup'], + 'DanceMove': ['Run', 'DanceMove', 'Cleanup'], + 'Run': ['Run', 'DanceMove', 'Cleanup'], + 'Cleanup': []} + self.enteredAlready = False + return + + def destroy(self): + self.toon = None + if self.danceNode is not None: + self.danceNode.removeNode() + self.danceNode = None + self.activity = None + self.avId = None + return + + def enterInit(self, *args): + if not self.enteredAlready: + self.danceNode = NodePath('danceNode-%s' % self.avId) + self.danceNode.reparentTo(render) + self.danceNode.setPos(0, 0, 0) + self.danceNode.setH(self.toonH) + pos = self.toon.getPos(self.danceNode) + self.toon.reparentTo(self.danceNode) + self.toon.setPos(pos) + self.enteredAlready = True + + def exitInit(self): + pass + + def enterCleanup(self, *args): + if hasattr(base.cr.playGame.hood, 'loader'): + pos = self.toon.getPos(self.activity.getParentNodePath()) + hpr = self.toon.getHpr(self.activity.getParentNodePath()) + self.toon.reparentTo(self.activity.getParentNodePath()) + self.toon.setPos(pos) + self.toon.setHpr(hpr) + if self.danceNode is not None: + self.danceNode.removeNode() + self.danceNode = None + self.enteredAlready = False + return + + def exitCleanup(self): + pass + + def enterDanceMove(self, anim = ''): + if self.lastAnim is None and anim == '': + self.toon.loop('victory', fromFrame=98, toFrame=122) + else: + if anim == '': + anim = self.lastAnim + if anim in DanceReverseLoopAnims: + self.danceMoveSequence = Sequence(self.toon.actorInterval(anim, loop=0), self.toon.actorInterval(anim, loop=0, playRate=-1.0)) + self.danceMoveSequence.loop() + else: + self.toon.loop(anim) + self.lastAnim = anim + return + + def exitDanceMove(self): + if self.danceMoveSequence and self.danceMoveSequence.isPlaying(): + self.danceMoveSequence.pause() + self.danceMoveSequence = None + return + + def enterRun(self, *args): + if self.toon.getCurrentAnim() != 'run': + self.toon.loop('run') + + def exitRun(self): + pass diff --git a/toontown/parties/PartyEditor.py b/toontown/parties/PartyEditor.py new file mode 100755 index 00000000..f8ff40e9 --- /dev/null +++ b/toontown/parties/PartyEditor.py @@ -0,0 +1,231 @@ +import time +from sets import Set +from pandac.PandaModules import Vec3, Vec4, Point3, TextNode, VBase4 +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel, DirectScrolledList, DirectCheckButton +from direct.gui import DirectGuiGlobals +from direct.showbase.DirectObject import DirectObject +from direct.showbase import PythonUtil +from direct.fsm.FSM import FSM +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.parties.PartyInfo import PartyInfo +from toontown.parties import PartyUtils +from toontown.parties.PartyEditorGrid import PartyEditorGrid +from toontown.parties.PartyEditorListElement import PartyEditorListElement + +class PartyEditor(DirectObject, FSM): + notify = directNotify.newCategory('PartyEditor') + + def __init__(self, partyPlanner, parent): + FSM.__init__(self, self.__class__.__name__) + self.partyPlanner = partyPlanner + self.parent = parent + self.partyEditorGrid = PartyEditorGrid(self) + self.currentElement = None + self.defaultTransitions = {'Hidden': ['Idle', 'Cleanup'], + 'Idle': ['DraggingElement', 'Hidden', 'Cleanup'], + 'DraggingElement': ['Idle', + 'DraggingElement', + 'Hidden', + 'Cleanup'], + 'Cleanup': []} + self.initElementList() + self.initPartyClock() + self.initTrashCan() + return + + def initElementList(self): + self.activityIconsModel = loader.loadModel('phase_4/models/parties/eventSignIcons') + self.decorationModels = loader.loadModel('phase_4/models/parties/partyDecorations') + pos = self.partyPlanner.gui.find('**/step_05_activitiesIcon_locator').getPos() + self.elementList = DirectScrolledList(parent=self.parent, relief=None, decButton_image=(self.partyPlanner.gui.find('**/activitiesButtonUp_up'), + self.partyPlanner.gui.find('**/activitiesButtonUp_down'), + self.partyPlanner.gui.find('**/activitiesButtonUp_rollover'), + self.partyPlanner.gui.find('**/activitiesButtonUp_inactive')), decButton_relief=None, decButton_pos=(-0.05, 0.0, -0.38), incButton_image=(self.partyPlanner.gui.find('**/activitiesButtonDown_up'), + self.partyPlanner.gui.find('**/activitiesButtonDown_down'), + self.partyPlanner.gui.find('**/activitiesButtonDown_rollover'), + self.partyPlanner.gui.find('**/activitiesButtonDown_inactive')), incButton_relief=None, incButton_pos=(-0.05, 0.0, -0.94), itemFrame_pos=(pos[0], pos[1], pos[2] + 0.04), itemFrame_relief=None, numItemsVisible=1, items=[]) + isWinter = base.cr.newsManager.isHolidayRunning(ToontownGlobals.CHRISTMAS) + isVictory = base.cr.newsManager.isHolidayRunning(ToontownGlobals.VICTORY_PARTY_HOLIDAY) + isValentine = base.cr.newsManager.isHolidayRunning(ToontownGlobals.VALENTOONS_DAY) + for activityId in PartyGlobals.PartyEditorActivityOrder: + if not isVictory and activityId in PartyGlobals.VictoryPartyActivityIds or not isWinter and activityId in PartyGlobals.WinterPartyActivityIds or not isValentine and activityId in PartyGlobals.ValentinePartyActivityIds: + pass + elif isVictory and activityId in PartyGlobals.VictoryPartyReplacementActivityIds or isWinter and activityId in PartyGlobals.WinterPartyReplacementActivityIds or isValentine and activityId in PartyGlobals.ValentinePartyReplacementActivityIds: + pass + else: + pele = PartyEditorListElement(self, activityId) + self.elementList.addItem(pele) + if activityId == PartyGlobals.ActivityIds.PartyClock: + self.partyClockElement = pele + + for decorationId in PartyGlobals.DecorationIds: + if not isVictory and decorationId in PartyGlobals.VictoryPartyDecorationIds or not isWinter and decorationId in PartyGlobals.WinterPartyDecorationIds or not isValentine and decorationId in PartyGlobals.ValentinePartyDecorationIds: + pass + elif isVictory and decorationId in PartyGlobals.VictoryPartyReplacementDecorationIds or isValentine and decorationId in PartyGlobals.ValentinePartyReplacementDecorationIds: + pass + elif decorationId in PartyGlobals.TTSUnreleasedDecor: + pass + else: + pele = PartyEditorListElement(self, decorationId, isDecoration=True) + self.elementList.addItem(pele) + + self.elementList.refresh() + self.elementList['command'] = self.scrollItemChanged + return + + def initPartyClock(self): + self.partyClockElement.buyButtonClicked((8, 7)) + + def initTrashCan(self): + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') + self.trashCanButton = DirectButton(parent=self.parent, relief=None, pos=Point3(*PartyGlobals.TrashCanPosition), scale=PartyGlobals.TrashCanScale, geom=(trashcanGui.find('**/TrashCan_CLSD'), + trashcanGui.find('**/TrashCan_OPEN'), + trashcanGui.find('**/TrashCan_RLVR'), + trashcanGui.find('**/TrashCan_RLVR')), command=self.trashCanClicked) + self.trashCanButton.bind(DirectGuiGlobals.ENTER, self.mouseEnterTrash) + self.trashCanButton.bind(DirectGuiGlobals.EXIT, self.mouseExitTrash) + self.mouseOverTrash = False + self.oldInstructionText = '' + self.trashCanLastClickedTime = 0 + return + + def scrollItemChanged(self): + if not self.elementList['items']: + return + self.currentElement = self.elementList['items'][self.elementList.getSelectedIndex()] + self.elementList['items'][self.elementList.getSelectedIndex()].elementSelectedFromList() + if self.elementList['items'][self.elementList.getSelectedIndex()].isDecoration: + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsClickedElementDecoration + else: + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsClickedElementActivity + + def listElementClicked(self): + self.request('DraggingElement') + + def listElementReleased(self): + self.request('Idle', True) + + def trashCanClicked(self): + currentTime = time.time() + if currentTime - self.trashCanLastClickedTime < 0.2: + self.clearPartyGrounds() + self.trashCanLastClickedTime = time.time() + + def clearPartyGrounds(self): + for item in self.elementList['items']: + item.clearPartyGrounds() + + self.initPartyClock() + if self.currentElement: + self.currentElement.checkSoldOutAndAffordability() + + def buyCurrentElement(self): + if self.currentElement: + purchaseSuccessful = self.currentElement.buyButtonClicked() + if purchaseSuccessful: + self.handleMutuallyExclusiveActivities() + else: + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsNoRoom + + def mouseEnterTrash(self, mouseEvent): + self.mouseOverTrash = True + self.oldInstructionText = self.partyPlanner.instructionLabel['text'] + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsTrash + + def mouseExitTrash(self, mouseEvent): + self.mouseOverTrash = False + self.partyPlanner.instructionLabel['text'] = self.oldInstructionText + + def enterHidden(self): + PartyEditor.notify.debug('Enter Hidden') + + def exitHidden(self): + PartyEditor.notify.debug('Exit Hidden') + + def enterIdle(self, fromDragging = False): + PartyEditor.notify.debug('Enter Idle') + if not fromDragging: + self.elementList.scrollTo(0) + self.elementList['items'][0].elementSelectedFromList() + self.currentElement = self.elementList['items'][self.elementList.getSelectedIndex()] + self.currentElement.checkSoldOutAndAffordability() + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsIdle + self.updateCostsAndBank() + self.handleMutuallyExclusiveActivities() + + def handleMutuallyExclusiveActivities(self): + mutSet = self.getMutuallyExclusiveActivities() + if not mutSet: + return + currentActivities = self.partyEditorGrid.getActivitiesElementsOnGrid() + lastActivity = self.partyEditorGrid.lastActivityIdPlaced + for act in currentActivities: + if act.id in mutSet and not lastActivity == act.id: + act.removeFromGrid() + removedName = TTLocalizer.PartyActivityNameDict[act.id]['editor'] + addedName = TTLocalizer.PartyActivityNameDict[lastActivity]['editor'] + instr = TTLocalizer.PartyPlannerEditorInstructionsRemoved % {'removed': removedName, + 'added': addedName} + self.partyPlanner.instructionLabel['text'] = instr + self.updateCostsAndBank() + + def getMutuallyExclusiveActivities(self): + currentActivities = self.partyEditorGrid.getActivitiesOnGrid() + actSet = Set([]) + for act in currentActivities: + actSet.add(act[0]) + + result = None + for mutuallyExclusiveTuples in PartyGlobals.MutuallyExclusiveActivities: + mutSet = Set(mutuallyExclusiveTuples) + inter = mutSet.intersection(actSet) + if len(inter) > 1: + result = inter + break + + return result + + def updateCostsAndBank(self): + currentActivities = self.partyEditorGrid.getActivitiesOnGrid() + currentDecorations = self.partyEditorGrid.getDecorationsOnGrid() + newCost = 0 + for elementTuple in currentActivities: + newCost += PartyGlobals.ActivityInformationDict[elementTuple[0]]['cost'] + + for elementTuple in currentDecorations: + newCost += PartyGlobals.DecorationInformationDict[elementTuple[0]]['cost'] + + self.partyPlanner.costLabel['text'] = TTLocalizer.PartyPlannerTotalCost % newCost + if len(currentActivities) > 0 or len(currentDecorations) > 0: + self.partyPlanner.setNextButtonState(enabled=True) + else: + self.partyPlanner.setNextButtonState(enabled=False) + self.partyPlanner.totalCost = newCost + self.partyPlanner.beanBank['text'] = str(int(self.partyPlanner.totalMoney - self.partyPlanner.totalCost)) + + def exitIdle(self): + PartyEditor.notify.debug('Exit Idle') + + def enterDraggingElement(self): + PartyEditor.notify.debug('Enter DraggingElement') + if self.currentElement.isDecoration: + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsDraggingDecoration + else: + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsDraggingActivity + + def exitDraggingElement(self): + PartyEditor.notify.debug('Exit DraggingElement') + + def enterCleanup(self): + PartyEditor.notify.debug('Enter Cleanup') + self.partyEditorGrid.destroy() + self.elementList.removeAndDestroyAllItems() + self.elementList.destroy() + self.trashCanButton.unbind(DirectGuiGlobals.ENTER) + self.trashCanButton.unbind(DirectGuiGlobals.EXIT) + self.trashCanButton.destroy() + + def exitCleanup(self): + PartyEditor.notify.debug('Exit Cleanup') diff --git a/toontown/parties/PartyEditorGrid.py b/toontown/parties/PartyEditorGrid.py new file mode 100755 index 00000000..f11463d2 --- /dev/null +++ b/toontown/parties/PartyEditorGrid.py @@ -0,0 +1,423 @@ +from pandac.PandaModules import Vec3, Vec4, Point3, TextNode, VBase4 +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel, DirectScrolledList, DirectCheckButton +from direct.gui import DirectGuiGlobals +from direct.showbase import DirectObject +from direct.showbase import PythonUtil +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.parties.PartyInfo import PartyInfo +from toontown.parties import PartyUtils +from toontown.parties.PartyEditorGridSquare import PartyEditorGridSquare + +class PartyEditorGrid: + notify = directNotify.newCategory('PartyEditorGrid') + + def __init__(self, partyEditor): + self.partyEditor = partyEditor + self.initGrid() + self.lastActivityIdPlaced = None + return + + def initGrid(self): + self.grid = [[None, + None, + None, + None, + None, + None, + True, + True, + True, + True, + True, + None, + None, + None, + None, + None, + None, + None], + [None, + None, + None, + None, + None, + True, + True, + True, + True, + True, + True, + True, + None, + None, + None, + None, + None, + None], + [None, + None, + None, + None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None, + None, + None, + None, + None], + [None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None, + None, + None, + None], + [None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [None, + None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None], + [None, + None, + None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None, + None], + [None, + None, + None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None, + None], + [None, + None, + None, + None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None, + None, + None], + [None, + None, + None, + None, + None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None, + None, + None]] + for y in range(len(self.grid)): + for x in range(len(self.grid[0])): + if self.grid[y][x]: + self.grid[y][x] = PartyEditorGridSquare(self.partyEditor, x, y) + + return None + + def getActivitiesOnGrid(self): + activities = [] + for y in range(len(self.grid)): + for x in range(len(self.grid[0])): + if self.grid[y][x] and self.grid[y][x].gridElement: + if not self.grid[y][x].gridElement.isDecoration: + activityTuple = self.grid[y][x].gridElement.getActivityTuple(x, y) + if activityTuple not in activities: + activities.append(activityTuple) + + return activities + + def getActivitiesElementsOnGrid(self): + activities = [] + activityElems = [] + for y in range(len(self.grid)): + for x in range(len(self.grid[0])): + if self.grid[y][x] and self.grid[y][x].gridElement: + if not self.grid[y][x].gridElement.isDecoration: + activityTuple = self.grid[y][x].gridElement.getActivityTuple(x, y) + if activityTuple not in activities: + activities.append(activityTuple) + activityElems.append(self.grid[y][x].gridElement) + + return activityElems + + def getDecorationsOnGrid(self): + decorations = [] + for y in range(len(self.grid)): + for x in range(len(self.grid[0])): + if self.grid[y][x] and self.grid[y][x].gridElement: + if self.grid[y][x].gridElement.isDecoration: + decorationTuple = self.grid[y][x].gridElement.getDecorationTuple(x, y) + if decorationTuple not in decorations: + decorations.append(decorationTuple) + + return decorations + + def getGridSquare(self, x, y): + if y < 0 or y >= PartyGlobals.PartyEditorGridSize[1]: + return None + if x < 0 or x >= PartyGlobals.PartyEditorGridSize[0]: + return None + return self.grid[y][x] + + def checkGridSquareForAvailability(self, gridSquare, size): + xOffsetLow, xOffsetHigh, yOffset = self.getXYOffsets(size) + for y in range(int(gridSquare.y - size[1] / 2), int(gridSquare.y + size[1] / 2) + yOffset): + for x in range(int(gridSquare.x - size[0] / 2) + xOffsetLow, int(gridSquare.x + size[0] / 2) + xOffsetHigh): + testGridSquare = self.getGridSquare(x, y) + if testGridSquare is None: + return False + if testGridSquare.gridElement is not None: + return False + + return True + + def getClearGridSquare(self, size, desiredXY = None): + if desiredXY is not None: + x = desiredXY[0] + y = desiredXY[1] + if self.grid[y][x] is not None: + if self.checkGridSquareForAvailability(self.grid[y][x], size): + return self.grid[y][x] + for y in range(PartyGlobals.PartyEditorGridSize[1]): + for x in range(PartyGlobals.PartyEditorGridSize[0]): + if self.grid[y][x] is not None: + if self.checkGridSquareForAvailability(self.grid[y][x], size): + return self.grid[y][x] + + return + + def getXYOffsets(self, size): + if size[0] % 2 == 0: + xOffsetLow = 1 + xOffsetHigh = 1 + else: + xOffsetLow = 0 + xOffsetHigh = 1 + if size[1] % 2 == 0: + yOffset = 0 + else: + yOffset = 1 + return (xOffsetLow, xOffsetHigh, yOffset) + + def registerNewElement(self, gridElement, centerGridSquare, size): + xOffsetLow, xOffsetHigh, yOffset = self.getXYOffsets(size) + for y in range(int(centerGridSquare.y - size[1] / 2), int(centerGridSquare.y + size[1] / 2) + yOffset): + for x in range(int(centerGridSquare.x - size[0] / 2) + xOffsetLow, int(centerGridSquare.x + size[0] / 2) + xOffsetHigh): + testGridSquare = self.getGridSquare(x, y) + if testGridSquare is None: + return False + if testGridSquare.gridElement is not None: + return False + else: + testGridSquare.gridElement = gridElement + if not gridElement.isDecoration: + self.lastActivityIdPlaced = gridElement.id + + return + + def removeElement(self, centerGridSquare, size): + xOffsetLow, xOffsetHigh, yOffset = self.getXYOffsets(size) + for y in range(int(centerGridSquare.y - size[1] / 2), int(centerGridSquare.y + size[1] / 2) + yOffset): + for x in range(int(centerGridSquare.x - size[0] / 2) + xOffsetLow, int(centerGridSquare.x + size[0] / 2) + xOffsetHigh): + testGridSquare = self.getGridSquare(x, y) + if testGridSquare is None: + return False + if testGridSquare.gridElement is None: + return False + else: + testGridSquare.gridElement = None + + return + + def destroy(self): + self.partyEditor = None + for y in range(len(self.grid)): + for x in range(len(self.grid[0])): + if self.grid[y][x]: + self.grid[y][x].destroy() + + del self.grid + return diff --git a/toontown/parties/PartyEditorGridElement.py b/toontown/parties/PartyEditorGridElement.py new file mode 100755 index 00000000..3023bdb0 --- /dev/null +++ b/toontown/parties/PartyEditorGridElement.py @@ -0,0 +1,263 @@ +from pandac.PandaModules import Vec3, Vec4, Point3, TextNode, VBase4, NodePath +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel, DirectScrolledList, DirectCheckButton +from direct.gui import DirectGuiGlobals +from direct.showbase import DirectObject +from direct.showbase import PythonUtil +from direct.task.Task import Task +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.parties.PartyInfo import PartyInfo +from toontown.parties import PartyUtils + +class PartyEditorGridElement(DirectButton): + notify = directNotify.newCategory('PartyEditorGridElement') + + def __init__(self, partyEditor, id, isDecoration, checkSoldOutAndAffordability, **kw): + self.partyEditor = partyEditor + self.id = id + self.isDecoration = isDecoration + self.checkSoldOutAndAffordability = checkSoldOutAndAffordability + if self.isDecoration: + self.name = TTLocalizer.PartyDecorationNameDict[self.id]['editor'] + colorList = ((1.0, 1.0, 1.0, 1.0), + (0.0, 0.0, 1.0, 1.0), + (0.0, 1.0, 1.0, 1.0), + (0.5, 0.5, 0.5, 1.0)) + self.geom = self.partyEditor.partyPlanner.gui.find('**/%s' % PartyGlobals.DecorationInformationDict[self.id]['gridAsset']) + else: + self.name = TTLocalizer.PartyActivityNameDict[self.id]['editor'] + colorList = ((1.0, 1.0, 1.0, 1.0), + (0.0, 1.0, 0.0, 1.0), + (1.0, 1.0, 0.0, 1.0), + (0.5, 0.5, 0.5, 1.0)) + self.geom = self.partyEditor.partyPlanner.gui.find('**/%s' % PartyGlobals.ActivityInformationDict[self.id]['gridAsset']) + optiondefs = (('geom', self.geom, None), + ('geom_scale', 1.0, None), + ('geom_color', colorList[0], None), + ('geom1_color', colorList[0], None), + ('geom2_color', colorList[0], None), + ('geom3_color', colorList[0], None), + ('relief', None, None)) + self.defineoptions(kw, optiondefs) + DirectButton.__init__(self, self.partyEditor.parent) + self.initialiseoptions(PartyEditorGridElement) + self.setName('%sGridElement' % self.name) + self.bind(DirectGuiGlobals.B1PRESS, self.clicked) + self.bind(DirectGuiGlobals.B1RELEASE, self.released) + self.bind(DirectGuiGlobals.ENTER, self.mouseEnter) + self.bind(DirectGuiGlobals.EXIT, self.mouseExit) + self.uprightNodePath = NodePath('%sUpright' % self.name) + self.uprightNodePath.reparentTo(self) + rollOverZOffset = self.getGridSize()[1] / 30.0 + self.rolloverTitle = DirectLabel(relief=None, parent=self.uprightNodePath, pos=Point3(0.0, 0.0, rollOverZOffset), text=self.name, text_fg=(1.0, 1.0, 1.0, 1.0), text_shadow=(0.0, 0.0, 0.0, 1.0), text_scale=0.075) + self.rolloverTitle.stash() + self.stash() + self.overValidSquare = False + self.lastValidPosition = None + self.setColorScale(0.9, 0.9, 0.9, 0.7) + self.setTransparency(True) + self.mouseOverTrash = False + self.centerGridSquare = None + return + + def getCorrectRotation(self): + r = self.getR() + if r == 90.0: + r = 270.0 + elif r == 270.0: + r = 90.0 + if self.id == PartyGlobals.ActivityIds.PartyCannon: + return PartyUtils.convertDegreesToPartyGrid(r + 180.0) + return PartyUtils.convertDegreesToPartyGrid(r) + + def getDecorationTuple(self, x, y): + return (self.id, + self.centerGridSquare.x, + PartyGlobals.PartyEditorGridSize[1] - 1 - self.centerGridSquare.y, + self.getCorrectRotation()) + + def getActivityTuple(self, x, y): + return (self.id, + self.centerGridSquare.x, + PartyGlobals.PartyEditorGridSize[1] - 1 - self.centerGridSquare.y, + self.getCorrectRotation()) + + def attach(self, mouseEvent): + PartyEditorGridElement.notify.debug('attached grid element %s' % self.name) + taskMgr.remove('gridElementDragTask%s' % self.name) + vWidget2render2d = self.getPos(render2d) + vMouse2render2d = Point3(mouseEvent.getMouse()[0], 0, mouseEvent.getMouse()[1]) + taskMgr.add(self.elementDragTask, 'gridElementDragTask%s' % self.name) + self.unstash() + self.rolloverTitle.unstash() + self.uprightNodePath.reparentTo(self) + self.setPosHprToDefault() + + def elementDragTask(self, state): + mwn = base.mouseWatcherNode + if mwn.hasMouse(): + vMouse2render2d = Point3(mwn.getMouse()[0], 0, mwn.getMouse()[1]) + newPos = vMouse2render2d + if newPos[0] > PartyGlobals.PartyEditorGridBounds[0][0] and newPos[0] < PartyGlobals.PartyEditorGridBounds[1][0] and newPos[2] < PartyGlobals.PartyEditorGridBounds[0][1] and newPos[2] > PartyGlobals.PartyEditorGridBounds[1][1]: + centerGridSquare = self.snapToGrid(newPos) + if centerGridSquare is not None: + self.centerGridSquare = centerGridSquare + if not self.overValidSquare: + self.setOverValidSquare(True) + if self.mouseOverTrash: + self.setOverTrash(False) + return Task.cont + if self.id != PartyGlobals.ActivityIds.PartyClock and newPos[0] > PartyGlobals.PartyEditorTrashBounds[0][0] and newPos[0] < PartyGlobals.PartyEditorTrashBounds[1][0] and newPos[2] < PartyGlobals.PartyEditorTrashBounds[0][1] and newPos[2] > PartyGlobals.PartyEditorTrashBounds[1][1]: + if not self.mouseOverTrash: + self.setOverTrash(True) + elif self.mouseOverTrash: + self.setOverTrash(False) + self.setPos(render2d, newPos) + if self.overValidSquare: + self.setOverValidSquare(False) + return Task.cont + + def setOverTrash(self, value): + self.mouseOverTrash = value + if value: + self.partyEditor.trashCanButton['state'] = DirectGuiGlobals.DISABLED + self.setColorScale(1.0, 0.0, 0.0, 1.0) + else: + self.partyEditor.trashCanButton['state'] = DirectGuiGlobals.NORMAL + self.setColorScale(0.9, 0.9, 0.9, 0.7) + + def setOverValidSquare(self, value): + self.overValidSquare = value + if value: + self.setColorScale(1.0, 1.0, 1.0, 1.0) + else: + self.setColorScale(0.9, 0.9, 0.9, 0.7) + + def removeFromGrid(self): + if self.centerGridSquare is not None: + self.partyEditor.partyEditorGrid.removeElement(self.centerGridSquare, self.getGridSize()) + self.setOverValidSquare(False) + self.lastValidPosition = None + self.stash() + return + + def snapToGrid(self, newPos): + gridSquare = self.getGridSquareFromPosition(newPos) + if gridSquare == None: + self.setPosHprToDefault() + self.setPos(render2d, newPos) + return + elif not self.partyEditor.partyEditorGrid.checkGridSquareForAvailability(gridSquare, self.getGridSize()): + self.setPos(render2d, newPos) + return + self.setPosHprBasedOnGridSquare(gridSquare) + return gridSquare + + def getGridSize(self): + if self.isDecoration: + return PartyGlobals.DecorationInformationDict[self.id]['gridsize'] + else: + return PartyGlobals.ActivityInformationDict[self.id]['gridsize'] + + def setPosHprToDefault(self): + self.setR(0.0) + self.uprightNodePath.setR(0.0) + + def setPosHprBasedOnGridSquare(self, gridSquare): + gridPos = gridSquare.getPos() + if self.getGridSize()[0] % 2 == 0: + gridPos.setX(gridPos[0] + PartyGlobals.PartyEditorGridSquareSize[0] / 2.0) + if self.getGridSize()[1] % 2 == 0: + gridPos.setZ(gridPos[2] + PartyGlobals.PartyEditorGridSquareSize[1] / 2.0) + if self.id != PartyGlobals.ActivityIds.PartyFireworks: + if gridPos[0] > PartyGlobals.PartyEditorGridCenter[0] + PartyGlobals.PartyEditorGridRotateThreshold: + self.setR(90.0) + self.uprightNodePath.setR(-90.0) + elif gridPos[0] < PartyGlobals.PartyEditorGridCenter[0] - PartyGlobals.PartyEditorGridRotateThreshold: + self.setR(270.0) + self.uprightNodePath.setR(-270.0) + elif gridPos[2] < PartyGlobals.PartyEditorGridCenter[1] - PartyGlobals.PartyEditorGridRotateThreshold: + self.setR(180.0) + self.uprightNodePath.setR(-180.0) + else: + self.setR(0.0) + self.uprightNodePath.setR(0.0) + else: + self.setR(270.0) + self.uprightNodePath.setR(-270.0) + self.setPos(render2d, gridPos) + self.lastValidPosition = gridPos + + def getGridSquareFromPosition(self, newPos): + localX = newPos[0] - PartyGlobals.PartyEditorGridBounds[0][0] + localY = newPos[2] - PartyGlobals.PartyEditorGridBounds[1][1] + x = int(localX / PartyGlobals.PartyEditorGridSquareSize[0]) + y = int(localY / PartyGlobals.PartyEditorGridSquareSize[1]) + y = PartyGlobals.PartyEditorGridSize[1] - 1 - y + return self.partyEditor.partyEditorGrid.getGridSquare(x, y) + + def detach(self, mouseEvent): + taskMgr.remove('gridElementDragTask%s' % self.name) + self.rolloverTitle.stash() + if self.overValidSquare: + self.partyEditor.partyEditorGrid.registerNewElement(self, self.centerGridSquare, self.getGridSize()) + self.partyEditor.updateCostsAndBank() + self.partyEditor.handleMutuallyExclusiveActivities() + elif self.lastValidPosition is not None: + if self.mouseOverTrash: + self.partyEditor.trashCanButton['state'] = DirectGuiGlobals.NORMAL + self.lastValidPosition = None + self.partyEditor.updateCostsAndBank() + self.stash() + else: + self.setPos(render2d, self.lastValidPosition) + self.setOverValidSquare(True) + self.partyEditor.partyEditorGrid.registerNewElement(self, self.centerGridSquare, self.getGridSize()) + self.partyEditor.updateCostsAndBank() + self.partyEditor.handleMutuallyExclusiveActivities() + else: + self.stash() + self.checkSoldOutAndAffordability() + return + + def placeInPartyGrounds(self, desiredXY = None): + self.centerGridSquare = self.partyEditor.partyEditorGrid.getClearGridSquare(self.getGridSize(), desiredXY) + if self.centerGridSquare is not None: + self.setOverValidSquare(True) + self.unstash() + self.setPosHprBasedOnGridSquare(self.centerGridSquare) + self.partyEditor.partyEditorGrid.registerNewElement(self, self.centerGridSquare, self.getGridSize()) + self.partyEditor.updateCostsAndBank() + self.partyEditor.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsPartyGrounds + self.checkSoldOutAndAffordability() + return True + else: + return False + return + + def clicked(self, mouseEvent): + PartyEditorGridElement.notify.debug('clicked grid element %s' % self.name) + if self.centerGridSquare is not None: + self.attach(mouseEvent) + self.partyEditor.partyEditorGrid.removeElement(self.centerGridSquare, self.getGridSize()) + return + + def released(self, mouseEvent): + PartyEditorGridElement.notify.debug('released grid element %s' % self.name) + self.detach(mouseEvent) + + def mouseEnter(self, mouseEvent): + parent = self.getParent() + self.reparentTo(parent) + self.rolloverTitle.unstash() + + def mouseExit(self, mouseEvent): + self.rolloverTitle.stash() + + def destroy(self): + self.unbind(DirectGuiGlobals.B1PRESS) + self.unbind(DirectGuiGlobals.B1RELEASE) + self.unbind(DirectGuiGlobals.ENTER) + self.unbind(DirectGuiGlobals.EXIT) + DirectButton.destroy(self) diff --git a/toontown/parties/PartyEditorGridSquare.py b/toontown/parties/PartyEditorGridSquare.py new file mode 100755 index 00000000..66c074dd --- /dev/null +++ b/toontown/parties/PartyEditorGridSquare.py @@ -0,0 +1,26 @@ +from pandac.PandaModules import Vec3, Vec4, Point3, TextNode, VBase4 +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel, DirectScrolledList, DirectCheckButton +from direct.gui import DirectGuiGlobals +from direct.showbase.DirectObject import DirectObject +from direct.showbase import PythonUtil +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.parties.PartyInfo import PartyInfo +from toontown.parties import PartyUtils + +class PartyEditorGridSquare(DirectObject): + notify = directNotify.newCategory('PartyEditorGridSquare') + + def __init__(self, partyEditor, x, y): + self.partyEditor = partyEditor + self.x = x + self.y = y + self.gridElement = None + return + + def getPos(self): + return Point3(PartyGlobals.PartyEditorGridBounds[0][0] + self.x * PartyGlobals.PartyEditorGridSquareSize[0] + PartyGlobals.PartyEditorGridSquareSize[0] / 2.0, 0.0, PartyGlobals.PartyEditorGridBounds[1][1] + (PartyGlobals.PartyEditorGridSize[1] - 1 - self.y) * PartyGlobals.PartyEditorGridSquareSize[1] + PartyGlobals.PartyEditorGridSquareSize[1] / 2.0) + + def destroy(self): + del self.gridElement diff --git a/toontown/parties/PartyEditorListElement.py b/toontown/parties/PartyEditorListElement.py new file mode 100755 index 00000000..dee8d94d --- /dev/null +++ b/toontown/parties/PartyEditorListElement.py @@ -0,0 +1,198 @@ +from pandac.PandaModules import Vec3 +from direct.gui.DirectGui import DirectButton, DirectLabel +from direct.gui import DirectGuiGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.parties.PartyEditorGridElement import PartyEditorGridElement +from toontown.parties.PartyUtils import getPartyActivityIcon + +class PartyEditorListElement(DirectButton): + notify = directNotify.newCategory('PartyEditorListElement') + + def __init__(self, partyEditor, id, isDecoration = False, **kw): + self.partyEditor = partyEditor + self.id = id + self.isDecoration = isDecoration + self.unreleased = self.calcUnreleased(id) + self.comingSoonTextScale = 1.0 + if self.isDecoration: + self.name = TTLocalizer.PartyDecorationNameDict[self.id]['editor'] + colorList = ((1.0, 0.0, 1.0, 1.0), + (0.0, 0.0, 1.0, 1.0), + (0.0, 1.0, 1.0, 1.0), + (0.5, 0.5, 0.5, 1.0)) + assetName = PartyGlobals.DecorationIds.getString(self.id) + if assetName == 'Hydra': + assetName = 'StageSummer' + geom = self.partyEditor.decorationModels.find('**/partyDecoration_%s' % assetName) + if geom.isEmpty() or self.unreleased: + helpGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_brd_help') + helpImageList = (helpGui.find('**/tt_t_gui_brd_helpUp'), + helpGui.find('**/tt_t_gui_brd_helpDown'), + helpGui.find('**/tt_t_gui_brd_helpHover'), + helpGui.find('**/tt_t_gui_brd_helpDown')) + geom = helpImageList[2] + geom3_color = (0.5, 0.5, 0.5, 1.0) + scale = Vec3(2.5, 2.5, 2.5) + geom_pos = (0.0, 0.0, 0.0) + self.comingSoonTextScale = 0.035 + else: + geom_pos = (0.0, 0.0, -3.0) + geom3_color = (0.5, 0.5, 0.5, 1.0) + scale = Vec3(0.06, 0.0001, 0.06) + if self.id in [PartyGlobals.DecorationIds.CogStatueVictory, PartyGlobals.DecorationIds.TubeCogVictory, PartyGlobals.DecorationIds.CogIceCreamVictory]: + geom_pos = (0.0, 0.0, -3.9) + scale = Vec3(0.05, 0.0001, 0.05) + else: + self.name = TTLocalizer.PartyActivityNameDict[self.id]['editor'] + colorList = ((0.0, 0.0, 0.0, 1.0), + (0.0, 1.0, 0.0, 1.0), + (1.0, 1.0, 0.0, 1.0), + (0.5, 0.5, 0.5, 1.0)) + iconString = PartyGlobals.ActivityIds.getString(self.id) + if self.id == PartyGlobals.ActivityIds.PartyJukebox40: + iconString = PartyGlobals.ActivityIds.getString(PartyGlobals.ActivityIds.PartyJukebox) + elif self.id == PartyGlobals.ActivityIds.PartyDance20: + iconString = PartyGlobals.ActivityIds.getString(PartyGlobals.ActivityIds.PartyDance) + geom = getPartyActivityIcon(self.partyEditor.activityIconsModel, iconString) + scale = 0.35 + geom3_color = (0.5, 0.5, 0.5, 1.0) + geom_pos = (0.0, 0.0, 0.0) + self.comingSoonTextScale = 0.25 + optiondefs = (('geom', geom, None), + ('geom3_color', geom3_color, None), + ('geom_pos', geom_pos, None), + ('relief', None, None)) + self.defineoptions(kw, optiondefs) + DirectButton.__init__(self, self.partyEditor.elementList) + self.initialiseoptions(PartyEditorListElement) + self.setName('%sListElement' % self.name) + self.setScale(scale) + self.bind(DirectGuiGlobals.B1PRESS, self.clicked) + self.bind(DirectGuiGlobals.B1RELEASE, self.released) + self.partyEditorGridElements = [] + if self.isDecoration: + for i in range(PartyGlobals.DecorationInformationDict[self.id]['limitPerParty']): + self.partyEditorGridElements.append(PartyEditorGridElement(self.partyEditor, self.id, self.isDecoration, self.checkSoldOutAndAffordability)) + + else: + for i in range(PartyGlobals.ActivityInformationDict[self.id]['limitPerParty']): + self.partyEditorGridElements.append(PartyEditorGridElement(self.partyEditor, self.id, self.isDecoration, self.checkSoldOutAndAffordability)) + + self.activeGridElementIndex = -1 + self.adjustForUnreleased() + return + + def calcUnreleased(self, id): + if base.cr.partyManager.allowUnreleasedClient(): + self.unreleased = False + elif self.isDecoration: + self.unreleased = id in PartyGlobals.UnreleasedDecorationIds + else: + self.unreleased = id in PartyGlobals.UnreleasedActivityIds + return self.unreleased + + def adjustForUnreleased(self): + if self.unreleased: + textScale = self.comingSoonTextScale + comingSoon = DirectLabel(parent=self, text=TTLocalizer.PartyPlannerComingSoon, text_scale=textScale, text_fg=(1.0, 0, 0, 1.0), text_shadow=(0, 0, 0, 1), relief=None) + self['state'] = DirectGuiGlobals.DISABLED + return + + def clearPartyGrounds(self): + for gridElement in self.partyEditorGridElements: + gridElement.removeFromGrid() + + def elementSelectedFromList(self): + PartyEditorListElement.notify.debug('Element %s clicked' % self.name) + if self.isDecoration: + self.partyEditor.partyPlanner.elementDescriptionNode.setText(TTLocalizer.PartyDecorationNameDict[self.id]['description']) + self.partyEditor.partyPlanner.elementPriceNode.setText('%d %s' % (PartyGlobals.DecorationInformationDict[self.id]['cost'], TTLocalizer.PartyPlannerBeans)) + self.partyEditor.partyPlanner.elementTitleLabel['text'] = self.name + else: + self.partyEditor.partyPlanner.elementDescriptionNode.setText(TTLocalizer.PartyActivityNameDict[self.id]['description']) + self.partyEditor.partyPlanner.elementPriceNode.setText('%d %s' % (PartyGlobals.ActivityInformationDict[self.id]['cost'], TTLocalizer.PartyPlannerBeans)) + self.partyEditor.partyPlanner.elementTitleLabel['text'] = self.name + self.checkSoldOutAndAffordability() + + def checkSoldOutAndAffordability(self): + if self.partyEditor.currentElement != self: + if self.partyEditor.currentElement is not None: + self.partyEditor.currentElement.checkSoldOutAndAffordability() + return + if self.isDecoration: + infoDict = PartyGlobals.DecorationInformationDict + else: + infoDict = PartyGlobals.ActivityInformationDict + if infoDict[self.id]['cost'] > self.partyEditor.partyPlanner.totalMoney - self.partyEditor.partyPlanner.totalCost: + self.setTooExpensive(True) + tooExpensive = True + else: + self.setTooExpensive(False) + tooExpensive = False + for i in range(len(self.partyEditorGridElements)): + if not self.partyEditorGridElements[i].overValidSquare: + if not tooExpensive: + self.setSoldOut(False) + return + + self.setSoldOut(True) + + def setTooExpensive(self, value): + self.partyEditor.partyPlanner.elementBuyButton['text'] = TTLocalizer.PartyPlannerBuy + if value: + self['state'] = DirectGuiGlobals.DISABLED + self.partyEditor.partyPlanner.elementBuyButton['state'] = DirectGuiGlobals.DISABLED + else: + self['state'] = DirectGuiGlobals.NORMAL + self.partyEditor.partyPlanner.elementBuyButton['state'] = DirectGuiGlobals.NORMAL + + def setSoldOut(self, value): + if value: + self['state'] = DirectGuiGlobals.DISABLED + self.partyEditor.partyPlanner.elementBuyButton['text'] = TTLocalizer.PartyPlannerSoldOut + self.partyEditor.partyPlanner.elementBuyButton['state'] = DirectGuiGlobals.DISABLED + else: + self['state'] = DirectGuiGlobals.NORMAL + self.partyEditor.partyPlanner.elementBuyButton['text'] = TTLocalizer.PartyPlannerBuy + self.partyEditor.partyPlanner.elementBuyButton['state'] = DirectGuiGlobals.NORMAL + if self.unreleased: + self['state'] = DirectGuiGlobals.DISABLED + self.partyEditor.partyPlanner.elementBuyButton['text'] = TTLocalizer.PartyPlannerCantBuy + self.partyEditor.partyPlanner.elementBuyButton['state'] = DirectGuiGlobals.DISABLED + + def clicked(self, mouseEvent): + PartyEditorListElement.notify.debug("Element %s's icon was clicked" % self.name) + self.partyEditor.listElementClicked() + for i in range(len(self.partyEditorGridElements)): + if not self.partyEditorGridElements[i].overValidSquare: + self.partyEditorGridElements[i].attach(mouseEvent) + self.activeGridElementIndex = i + return + + def buyButtonClicked(self, desiredXY = None): + for i in range(len(self.partyEditorGridElements)): + if not self.partyEditorGridElements[i].overValidSquare: + if self.partyEditorGridElements[i].placeInPartyGrounds(desiredXY): + self.activeGridElementIndex = i + return True + else: + self.checkSoldOutAndAffordability() + return False + + def released(self, mouseEvent): + PartyEditorListElement.notify.debug("Element %s's icon was released" % self.name) + self.partyEditor.listElementReleased() + if self.activeGridElementIndex != -1: + self.partyEditorGridElements[self.activeGridElementIndex].detach(mouseEvent) + + def destroy(self): + self.unbind(DirectGuiGlobals.B1PRESS) + self.unbind(DirectGuiGlobals.B1RELEASE) + for partyEditorGridElement in self.partyEditorGridElements: + partyEditorGridElement.destroy() + + del self.partyEditorGridElements + self.partyEditor = None + DirectButton.destroy(self) + return diff --git a/toontown/parties/PartyGlobals.py b/toontown/parties/PartyGlobals.py new file mode 100755 index 00000000..2876ccb3 --- /dev/null +++ b/toontown/parties/PartyGlobals.py @@ -0,0 +1,856 @@ +from pandac.PandaModules import BitMask32 +from pandac.PandaModules import Point3, VBase4 +from direct.showbase import PythonUtil +from toontown.toonbase import TTLocalizer +KICK_TO_PLAYGROUND_EVENT = 'parties_kickToPlayground' +MaxSetInvites = 1000 +MaxSetPartiesInvitedTo = 100 +MaxSetHostedParties = 50 +MaxPlannedYear = 2030 +MinPlannedYear = 1975 +JellybeanMultiplier = 1.5 +JellyBeanDayMultiplier = 2 +PARTY_DURATION = 1800 +EventsPageGuestNameMaxWidth = 0.42 +EventsPageGuestNameMaxLetters = 18 +EventsPageHostNameMaxWidth = 0.37 +PartyRefundPercentage = 0.95 +PartyPlannerAsapMinuteRounding = 5 +UberdogCheckPartyStartFrequency = 5.0 +UberdogPurgePartyPeriod = 24.0 +UberdogPartiesSanityCheckFrequency = 60 +JarLabelTextColor = (0.95, + 0.95, + 0.0, + 1.0) +JarLabelMaxedTextColor = (1.0, + 0.0, + 0.0, + 1.0) +TuftsOfGrass = 75 +MaxToonsAtAParty = 20 +DefaultPartyDuration = 0.5 +DelayBeforeAutoKick = 1.0 +MaxHostedPartiesPerToon = 1 +PartyEditorGridBounds = ((-0.11, 0.289), (0.55, -0.447)) +PartyEditorGridCenter = (PartyEditorGridBounds[0][0] + (PartyEditorGridBounds[1][0] - PartyEditorGridBounds[0][0]) / 2.0, PartyEditorGridBounds[1][1] + (PartyEditorGridBounds[0][1] - PartyEditorGridBounds[1][1]) / 2.0) +PartyEditorGridSize = (18, 15) +PartyEditorGridSquareSize = ((PartyEditorGridBounds[1][0] - PartyEditorGridBounds[0][0]) / float(PartyEditorGridSize[0]), (PartyEditorGridBounds[0][1] - PartyEditorGridBounds[1][1]) / float(PartyEditorGridSize[1])) +PartyEditorGridRotateThreshold = 0.08 +AvailableGridSquares = 202 +TrashCanPosition = (-0.24, 0.0, -0.65) +TrashCanScale = 0.7 +PartyEditorTrashBounds = ((-0.16, -0.38), (-0.05, -0.56)) +ActivityRequestStatus = PythonUtil.Enum(('Joining', 'Exiting')) +InviteStatus = PythonUtil.Enum(('NotRead', + 'ReadButNotReplied', + 'Accepted', + 'Rejected')) +InviteTheme = PythonUtil.Enum(('Birthday', + 'GenericMale', + 'GenericFemale', + 'Racing', + 'Valentoons', + 'VictoryParty', + 'Winter')) +PartyStatus = PythonUtil.Enum(('Pending', + 'Cancelled', + 'Finished', + 'CanStart', + 'Started', + 'NeverStarted')) +AddPartyErrorCode = PythonUtil.Enum(('AllOk', + 'ValidationError', + 'DatabaseError', + 'TooManyHostedParties')) +ChangePartyFieldErrorCode = PythonUtil.Enum(('AllOk', + 'ValidationError', + 'DatabaseError', + 'AlreadyStarted', + 'AlreadyRefunded')) +ActivityTypes = PythonUtil.Enum(('HostInitiated', 'GuestInitiated', 'Continuous')) +PartyGateDenialReasons = PythonUtil.Enum(('Unavailable', 'Full')) +ActivityIds = PythonUtil.Enum(('PartyJukebox', + 'PartyCannon', + 'PartyTrampoline', + 'PartyCatch', + 'PartyDance', + 'PartyTugOfWar', + 'PartyFireworks', + 'PartyClock', + 'PartyJukebox40', + 'PartyDance20', + 'PartyCog', + 'PartyVictoryTrampoline', + 'PartyWinterCatch', + 'PartyWinterTrampoline', + 'PartyWinterCog', + 'PartyValentineDance', + 'PartyValentineDance20', + 'PartyValentineJukebox', + 'PartyValentineJukebox40', + 'PartyValentineTrampoline')) +PartyEditorActivityOrder = [ ActivityIds.PartyClock, + ActivityIds.PartyJukebox, + ActivityIds.PartyJukebox40, + ActivityIds.PartyValentineJukebox, + ActivityIds.PartyValentineJukebox40, + ActivityIds.PartyCannon, + ActivityIds.PartyTrampoline, + ActivityIds.PartyValentineTrampoline, + ActivityIds.PartyVictoryTrampoline, + ActivityIds.PartyWinterTrampoline, + ActivityIds.PartyDance, + ActivityIds.PartyDance20, + ActivityIds.PartyValentineDance, + ActivityIds.PartyValentineDance20, + ActivityIds.PartyTugOfWar, + ActivityIds.PartyCatch, + ActivityIds.PartyWinterCatch, + ActivityIds.PartyCog, + ActivityIds.PartyWinterCog, + ActivityIds.PartyFireworks] +UnreleasedActivityIds = (ActivityIds.PartyWinterCog, + ActivityIds.PartyValentineJukebox, + ActivityIds.PartyValentineJukebox40, + ActivityIds.PartyValentineTrampoline, + ActivityIds.PartyWinterTrampoline, + ActivityIds.PartyWinterCatch, + ActivityIds.PartyValentineDance, + ActivityIds.PartyValentineDance20) +MutuallyExclusiveActivities = ((ActivityIds.PartyJukebox, ActivityIds.PartyJukebox40), + (ActivityIds.PartyValentineJukebox, ActivityIds.PartyValentineJukebox40), + (ActivityIds.PartyDance, ActivityIds.PartyDance20), + (ActivityIds.PartyValentineDance, ActivityIds.PartyValentineDance20)) +VictoryPartyActivityIds = frozenset([ActivityIds.PartyVictoryTrampoline]) +VictoryPartyReplacementActivityIds = frozenset([ActivityIds.PartyTrampoline]) +WinterPartyActivityIds = frozenset([ActivityIds.PartyWinterCatch, ActivityIds.PartyWinterTrampoline, ActivityIds.PartyWinterCog]) +WinterPartyReplacementActivityIds = frozenset([ActivityIds.PartyCatch, ActivityIds.PartyTrampoline, ActivityIds.PartyCog]) +ValentinePartyActivityIds = frozenset([ActivityIds.PartyValentineDance, + ActivityIds.PartyValentineDance20, + ActivityIds.PartyValentineJukebox, + ActivityIds.PartyValentineJukebox40, + ActivityIds.PartyValentineTrampoline]) +ValentinePartyReplacementActivityIds = frozenset([ActivityIds.PartyDance, + ActivityIds.PartyDance20, + ActivityIds.PartyJukebox, + ActivityIds.PartyJukebox40, + ActivityIds.PartyTrampoline]) +DecorationIds = PythonUtil.Enum(('BalloonAnvil', + 'BalloonStage', + 'Bow', + 'Cake', + 'Castle', + 'GiftPile', + 'Horn', + 'MardiGras', + 'NoiseMakers', + 'Pinwheel', + 'GagGlobe', + 'BannerJellyBean', + 'CakeTower', + 'HeartTarget', + 'HeartBanner', + 'FlyingHeart', + 'Hydra', + 'BannerVictory', + 'CannonVictory', + 'CogStatueVictory', + 'TubeCogVictory', + 'CogIceCreamVictory', + 'cogIceCreamWinter', + 'StageWinter', + 'CogStatueWinter', + 'snowman', + 'snowDoodle', + 'BalloonAnvilValentine')) +TTSUnreleasedDecor = [DecorationIds.HeartTarget, + DecorationIds.HeartBanner, + DecorationIds.FlyingHeart, + DecorationIds.Hydra, + DecorationIds.BannerVictory, + DecorationIds.CannonVictory, + DecorationIds.CogStatueVictory, + DecorationIds.TubeCogVictory, + DecorationIds.CogIceCreamVictory, + DecorationIds.cogIceCreamWinter, + DecorationIds.StageWinter, + DecorationIds.CogStatueWinter, + DecorationIds.snowman, + DecorationIds.snowDoodle, + DecorationIds.BalloonAnvilValentine] +DECORATION_VOLUME = 1.0 +DECORATION_CUTOFF = 45 +VictoryPartyDecorationIds = frozenset([DecorationIds.Hydra, + DecorationIds.BannerVictory, + DecorationIds.CannonVictory, + DecorationIds.CogStatueVictory, + DecorationIds.TubeCogVictory, + DecorationIds.CogIceCreamVictory]) +WinterPartyDecorationIds = frozenset([DecorationIds.cogIceCreamWinter, + DecorationIds.StageWinter, + DecorationIds.CogStatueWinter, + DecorationIds.snowman, + DecorationIds.snowDoodle]) +VictoryPartyReplacementDecorationIds = frozenset([DecorationIds.BannerJellyBean]) +ValentinePartyDecorationIds = frozenset([DecorationIds.BalloonAnvilValentine, + DecorationIds.HeartBanner, + DecorationIds.HeartTarget, + DecorationIds.FlyingHeart]) +ValentinePartyReplacementDecorationIds = frozenset([DecorationIds.BalloonAnvil, DecorationIds.BannerJellyBean]) +UnreleasedDecorationIds = () +GoToPartyStatus = PythonUtil.Enum(('AllowedToGo', + 'PartyFull', + 'PrivateParty', + 'PartyOver', + 'PartyNotActive')) +PlayGroundToPartyClockColors = {'the_burrrgh': (53.0 / 255.0, + 116.0 / 255.0, + 148.0 / 255.0, + 1.0), + 'daisys_garden': (52.0 / 255.0, + 153.0 / 255.0, + 95.0 / 255.0, + 1.0), + 'donalds_dock': (60.0 / 255.0, + 98.0 / 255.0, + 142.0 / 255.0, + 1.0), + 'donalds_dreamland': (79.0 / 255.0, + 92.0 / 255.0, + 120.0 / 255.0, + 1.0), + 'minnies_melody_land': (128.0 / 255.0, + 62.0 / 255.0, + 142.0 / 255.0, + 1.0), + 'toontown_central': (77.0 / 255.0, + 137.0 / 255.0, + 52.0 / 255.0, + 1.0)} +PartyGridUnitLength = [14.4, 14.6] +PartyGridHeadingConverter = 15.0 +PartyGridToPandaOffset = (-PartyGridUnitLength[0] * PartyEditorGridSize[0] / 2.0, -PartyGridUnitLength[1] * PartyEditorGridSize[1] / 2.0) +PartyCostMultiplier = 1 +MinimumPartyCost = 100 * PartyCostMultiplier +ActivityInformationDict = {ActivityIds.PartyJukebox: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyJukebox_activity_1x1'}, + ActivityIds.PartyJukebox40: {'cost': int(100 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyJukebox_activity_1x1'}, + ActivityIds.PartyValentineJukebox: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyJukebox_activity_1x1'}, + ActivityIds.PartyValentineJukebox40: {'cost': int(100 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyJukebox_activity_1x1'}, + ActivityIds.PartyCannon: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 5, + 'limitPerParty': 10, + 'gridAsset': 'PartyCannon_activity_1x1'}, + ActivityIds.PartyTrampoline: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (2, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 8, + 'gridAsset': 'PartyTrampoline_activity_2x2'}, + ActivityIds.PartyValentineTrampoline: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (2, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 8, + 'gridAsset': 'PartyTrampoline_activity_2x2'}, + ActivityIds.PartyVictoryTrampoline: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (2, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 8, + 'gridAsset': 'PartyTrampoline_activity_2x2'}, + ActivityIds.PartyWinterTrampoline: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (2, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 8, + 'gridAsset': 'PartyTrampoline_activity_2x2'}, + ActivityIds.PartyCatch: {'cost': int(300 * PartyCostMultiplier), + 'gridsize': (5, 5), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyCatch_activity_5x5'}, + ActivityIds.PartyWinterCatch: {'cost': int(300 * PartyCostMultiplier), + 'gridsize': (5, 5), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyCatch_activity_5x5'}, + ActivityIds.PartyCog: {'cost': int(300 * PartyCostMultiplier), + 'gridsize': (5, 5), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyCog_activity_5x5'}, + ActivityIds.PartyWinterCog: {'cost': int(300 * PartyCostMultiplier), + 'gridsize': (5, 5), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyCog_activity_5x5'}, + ActivityIds.PartyDance: {'cost': int(100 * PartyCostMultiplier), + 'gridsize': (3, 3), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyDance_activity_3x3'}, + ActivityIds.PartyDance20: {'cost': int(200 * PartyCostMultiplier), + 'gridsize': (3, 3), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyDance_activity_3x3'}, + ActivityIds.PartyValentineDance: {'cost': int(100 * PartyCostMultiplier), + 'gridsize': (3, 3), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyDance_activity_3x3'}, + ActivityIds.PartyValentineDance20: {'cost': int(200 * PartyCostMultiplier), + 'gridsize': (3, 3), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyDance_activity_3x3'}, + ActivityIds.PartyTugOfWar: {'cost': int(200 * PartyCostMultiplier), + 'gridsize': (4, 4), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyTufOfWar_activity_4x4'}, + ActivityIds.PartyFireworks: {'cost': int(200 * PartyCostMultiplier), + 'gridsize': (4, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyFireworks_activity_2x4'}, + ActivityIds.PartyClock: {'cost': MinimumPartyCost, + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'gridAsset': 'PartyClock_activity_1x1'}} +DecorationInformationDict = {DecorationIds.BalloonAnvil: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.BalloonAnvilValentine: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.BalloonStage: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.Bow: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.Cake: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.Castle: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.GiftPile: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.Horn: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.MardiGras: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.NoiseMakers: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.Pinwheel: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.GagGlobe: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.BannerJellyBean: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.CakeTower: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.HeartTarget: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.HeartBanner: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.FlyingHeart: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.Hydra: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (2, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_propStage_2x2'}, + DecorationIds.BannerVictory: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.CannonVictory: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.CogStatueVictory: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.TubeCogVictory: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.CogIceCreamVictory: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.cogIceCreamWinter: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.StageWinter: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (2, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_propStage_2x2'}, + DecorationIds.CogStatueWinter: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.snowman: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.snowDoodle: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'gridAsset': 'decoration_1x1'}} +DefaultRulesTimeout = 10.0 +DenialReasons = PythonUtil.Enum(('Default', 'Full', 'SilentFail'), start=0) +FireworkShows = PythonUtil.Enum(('Summer',), start=200) +FireworksGlobalXOffset = 160.0 +FireworksGlobalYOffset = -20.0 +FireworksPostLaunchDelay = 5.0 +RocketSoundDelay = 2.0 +RocketDirectionDelay = 2.0 +FireworksStartedEvent = 'PartyFireworksStarted' +FireworksFinishedEvent = 'PartyFireworksFinished' +FireworksTransitionToDisabledDelay = 3.0 +TeamActivityTeams = PythonUtil.Enum(('LeftTeam', 'RightTeam'), start=0) +TeamActivityNeitherTeam = 3 +TeamActivityTextScale = 0.135 +TeamActivityStartDelay = 8.0 +TeamActivityClientWaitDelay = 30.0 +TeamActivityDefaultMinPlayersPerTeam = 1 +TeamActivityDefaultMaxPlayersPerTeam = 4 +TeamActivityDefaultDuration = 60.0 +TeamActivityDefaultConclusionDuration = 4.0 +TeamActivityStatusColor = VBase4(1.0, 1.0, 0.65, 1.0) +CogActivityBalanceTeams = True +CogActivityStartDelay = 15.0 +CogActivityConclusionDuration = 12 +CogActivityDuration = 90 +CogActivityMinPlayersPerTeam = 1 +CogActivityMaxPlayersPerTeam = 4 +CogActivityColors = (VBase4(0.22, 0.4, 0.98, 1.0), VBase4(1.0, 0.43, 0.04, 1.0)) +CogActivitySplatColorBase = VBase4(0.98, 0.9, 0.094, 1.0) +CogActivitySplatColors = (VBase4(CogActivityColors[0][0] / CogActivitySplatColorBase[0], CogActivityColors[0][1] / CogActivitySplatColorBase[1], CogActivityColors[0][2] / CogActivitySplatColorBase[2], 1.0), VBase4(CogActivityColors[1][0] / CogActivitySplatColorBase[0], CogActivityColors[1][1] / CogActivitySplatColorBase[1], CogActivityColors[1][2] / CogActivitySplatColorBase[2], 1.0)) +CogPinataHeadZ = 4.7 +CogActivityHitPoints = 1 +CogActivityHitPointsForHead = 3 +CogPinataPushBodyFactor = 0.05 +CogPinataPushHeadFactor = CogPinataPushBodyFactor * abs(CogActivityHitPointsForHead - CogActivityHitPoints) +CogActivityAvgBeansPerSecond = 0.15 +CogActivityBeansToAward = round(CogActivityAvgBeansPerSecond * CogActivityDuration * 2.0) +CogActivityWinBeans = int(round(CogActivityBeansToAward * 0.6)) +CogActivityLossBeans = int(round(CogActivityBeansToAward * 0.4)) +CogActivityTieBeans = int(round(CogActivityBeansToAward * 0.4)) +CogActivityPerfectWinBeans = int(round(CogActivityBeansToAward * 0.75)) +CogActivityPerfectLossBeans = int(round(CogActivityBeansToAward * 0.25)) +CogActivityArenaLength = 50.0 +CogActivityPieMinDist = 0.0 +CogActivityPieMaxDist = 110.0 +CogActivityPowerMeterHeight = 0.4 +CogActivityPowerMeterWidth = 0.1 +CogActivityPowerMeterPos = (0.33, 0.0, 0.0) +CogActivityPowerMeterTextPos = (0.33, -0.26) +CogActivityVictoryBarPos = (-0.55, 0.0, 0.825) +CogActivityVictoryBarOrangePos = (0.1725, 0.0, -0.0325) +CogActivityVictoryBarPiePos = (0.47, 0.0, -0.015) +CogActivityVictoryBarArrow = (0.0, 0.0, 0.1) +CogActivityBarUnitScale = 1.1 +CogActivityBarStartScale = CogActivityBarUnitScale * 5 +CogActivityBarPieUnitMove = 0.07 +CogActivityBarPieScale = 1.5 +CogActivityScorePos = (1.25, -0.45) +CogActivityScoreTitle = (1.24, -0.5) +CogActivityPowerMeterTime = 1.0 +CogActivityShortThrowTime = 0.1 +ToonAttackIdleThreshold = 5.0 +ToonMoveIdleThreshold = 5.0 +CogActivityShortThrowSpam = 3 +CogActivitySpamWarningShowTime = 5.0 +CogActivityControlsShowTime = 2.0 +PARTY_COG_CUTOFF = 60 +TugOfWarStartDelay = 8.0 +TugOfWarReadyDuration = 1.5 +TugOfWarGoDuration = 0.75 +TugOfWarDuration = 40.0 +TugOfWarMinimumPlayersPerTeam = 1 +TugOfWarMaximumPlayersPerTeam = 4 +TugOfWarStartGameTimeout = 8 +TugOfWarJoinCollisionEndPoints = [Point3(6.0, 0.0, 0.0), Point3(-6.0, 0.0, 0.0)] +TugOfWarJoinCollisionRadius = 1.75 +TugOfWarJoinCollisionPositions = [Point3(-10.5, 0.25, 4.5), Point3(10.5, -0.25, 4.5)] +TugOfWarInitialToonPositionsXOffset = 8.0 +TugOfWarToonPositionXSeparation = 2.0 +TugOfWarToonPositionZ = 2.55 +TugOfWarTextWordScale = 0.135 +TugOfWarTextCountdownScale = 4.0 +TugOfWarCameraPos = Point3(0.0, -33.0, 10.0) +TugOfWarCameraInitialHpr = Point3(0.0, -6.91123, 0.0) +TugOfWarCameraLookAtHeightOffset = 6.0 +TugOfWarPowerMeterSize = 17 +TugOfWarPowerMeterRulesTarget = 8 +TugOfWarDisabledArrowColor = VBase4(1.0, 0.0, 0.0, 0.3) +TugOfWarEnabledArrowColor = VBase4(1.0, 0.0, 0.0, 1.0) +TugOfWarHilightedArrowColor = VBase4(1.0, 0.7, 0.0, 1.0) +TugOfWarTargetRateList = [(8.0, 6), + (5.0, 7), + (6.0, 8), + (6.0, 10), + (7.0, 11), + (8.0, 12)] +TugOfWarKeyPressTimeToLive = 1.0 +TugOfWarKeyPressUpdateRate = 0.1 +TugOfWarKeyPressReportRate = 0.2 +TugOfWarMovementFactor = 0.03 +TugOfWarSplashZOffset = 1.0 +TugOfWarHeadings = [240.0, 120.0] +TugOfWarConclusionDuration = 4.0 +TugOfWarFallInWinReward = 15 +TugOfWarFallInLossReward = 4 +TugOfWarWinReward = 12 +TugOfWarLossReward = 8 +TugOfWarTieReward = 5 +TugOfWarTieThreshold = 0.75 +TrampolineDuration = 60.0 +TrampolineSignOffset = Point3(-6.0, -6.0, 0.0) +TrampolineLeverOffset = Point3(-5.0, -9.0, 0.0) +TrampolineNumJellyBeans = 12 +TrampolineJellyBeanBonus = 10 +CatchActivityDuration = 80 +CatchActivityBitmask = BitMask32(16) +CatchLeverOffset = Point3(-3.0, -2.0, 0.0) +CatchDropShadowHeight = 0.5 +CatchConclusionDuration = 3.0 + +class DropObject: + + def __init__(self, name, good, onscreenDurMult, modelPath): + self.name = name + self.good = good + self.onscreenDurMult = onscreenDurMult + self.modelPath = modelPath + + def isBaseline(self): + return self.onscreenDurMult == 1.0 + + +DropObjectTypes = [DropObject('apple', 1, 1.0, 'phase_4/models/minigames/apple'), + DropObject('orange', 1, 1.0, 'phase_4/models/minigames/orange'), + DropObject('pear', 1, 1.0, 'phase_4/models/minigames/pear'), + DropObject('coconut', 1, 1.0, 'phase_4/models/minigames/coconut'), + DropObject('watermelon', 1, 1.0, 'phase_4/models/minigames/watermelon'), + DropObject('pineapple', 1, 1.0, 'phase_4/models/minigames/pineapple'), + DropObject('anvil', 0, 0.4, 'phase_4/models/props/anvil-mod')] +Name2DropObjectType = {} +for type in DropObjectTypes: + Name2DropObjectType[type.name] = type + +Name2DOTypeId = {} +names = Name2DropObjectType.keys() +names.sort() +for i in xrange(len(names)): + Name2DOTypeId[names[i]] = i + +DOTypeId2Name = names +NumFruits = [{2000: 18, + 1000: 19, + 5000: 22, + 4000: 24, + 3000: 27, + 9000: 28}, + {2000: 30, + 1000: 33, + 5000: 38, + 4000: 42, + 3000: 46, + 9000: 50}, + {2000: 42, + 1000: 48, + 5000: 54, + 4000: 60, + 3000: 66, + 9000: 71}, + {2000: 56, + 1000: 63, + 5000: 70, + 4000: 78, + 3000: 85, + 9000: 92}] +DancePatternToAnims = {'dduu': 'slip-backward', + 'ldddud': 'happy-dance', + 'lll': 'left', + 'rdu': 'struggle', + 'rrr': 'right', + 'rulu': 'running-jump', + 'udlr': 'good-putt', + 'udllrr': 'victory', + 'ulu': 'jump', + 'uudd': 'slip-forward'} +DancePatternToAnims20 = {'ddd': 'down', + 'dduu': 'slip-backward', + 'drul': 'sad-walk', + 'ldr': 'push', + 'ldddud': 'happy-dance', + 'ldu': 'sprinkle-dust', + 'lll': 'left', + 'llrr': 'firehose', + 'lrlr': 'wave', + 'rdu': 'struggle', + 'rlrl': 'confused', + 'rrr': 'right', + 'rulu': 'running-jump', + 'uddd': 'reel-neutral', + 'udlr': 'good-putt', + 'udud': 'angry', + 'udllrr': 'victory', + 'ulu': 'jump', + 'uudd': 'slip-forward', + 'uuu': 'up'} +DanceAnimToName = {'right': TTLocalizer.DanceAnimRight, + 'reel-neutral': TTLocalizer.DanceAnimReelNeutral, + 'conked': TTLocalizer.DanceAnimConked, + 'happy-dance': TTLocalizer.DanceAnimHappyDance, + 'confused': TTLocalizer.DanceAnimConfused, + 'walk': TTLocalizer.DanceAnimWalk, + 'jump': TTLocalizer.DanceAnimJump, + 'firehose': TTLocalizer.DanceAnimFirehose, + 'shrug': TTLocalizer.DanceAnimShrug, + 'slip-forward': TTLocalizer.DanceAnimSlipForward, + 'sad-walk': TTLocalizer.DanceAnimSadWalk, + 'wave': TTLocalizer.DanceAnimWave, + 'struggle': TTLocalizer.DanceAnimStruggle, + 'running-jump': TTLocalizer.DanceAnimRunningJump, + 'slip-backward': TTLocalizer.DanceAnimSlipBackward, + 'down': TTLocalizer.DanceAnimDown, + 'up': TTLocalizer.DanceAnimUp, + 'good-putt': TTLocalizer.DanceAnimGoodPutt, + 'victory': TTLocalizer.DanceAnimVictory, + 'push': TTLocalizer.DanceAnimPush, + 'angry': TTLocalizer.DanceAnimAngry, + 'left': TTLocalizer.DanceAnimLeft} +DanceReverseLoopAnims = ['left', + 'right', + 'up', + 'down', + 'good-putt'] +ToonDancingStates = PythonUtil.Enum(('Init', + 'DanceMove', + 'Run', + 'Cleanup')) +JUKEBOX_TIMEOUT = 30.0 +MUSIC_PATH = 'phase_%s/audio/bgm/' +MUSIC_MIN_LENGTH_SECONDS = 50.0 +MUSIC_GAP = 2.5 +PhaseToMusicData = {3.5: {'TC_SZ.ogg': [TTLocalizer.MusicTcSz, 57]}, + 3: {'create_a_toon.ogg': [TTLocalizer.MusicCreateAToon, 175], + 'tt_theme.ogg': [TTLocalizer.MusicTtTheme, 51]}, + 4: {'TC_nbrhood.ogg': [TTLocalizer.MusicTcNbrhood, 59], + 'MG_TwoDGame.ogg': [TTLocalizer.MusicMgTwodgame, 60], + 'MG_Vine.ogg': [TTLocalizer.MusicMgVine, 32], + 'FF_safezone.ogg': [TTLocalizer.MusicFfSafezone, 47]}, + 6: {'DD_SZ.ogg': [TTLocalizer.MusicDdSz, 33], + 'GS_SZ.ogg': [TTLocalizer.MusicGsSz, 60], + 'OZ_SZ.ogg': [TTLocalizer.MusicOzSz, 31], + 'GZ_SZ.ogg': [TTLocalizer.MusicGzSz, 59], + 'MM_SZ.ogg': [TTLocalizer.MusicMmSz, 76]}, + 8: {'DG_SZ.ogg': [TTLocalizer.MusicDgSz, 48], + 'DL_SZ.ogg': [TTLocalizer.MusicDlSz, 33], + 'TB_SZ.ogg': [TTLocalizer.MusicTbSz, 54]}, + 9: {'encntr_hall_of_fame.ogg': [TTLocalizer.MusicEncntrHallOfFame, 51], + 'encntr_head_suit_theme.ogg': [TTLocalizer.MusicEncntrHeadSuitTheme, 29]}, + 11: {'LB_juryBG.ogg': [TTLocalizer.MusicLbJurybg, 30]}, + 13: {'party_original_theme.ogg': [TTLocalizer.MusicPartyOriginalTheme, 56], + 'party_generic_theme_jazzy.ogg': [TTLocalizer.MusicPartyGenericThemeJazzy, 64]}} +PhaseToMusicData40 = {3.5: {'encntr_general_bg.ogg': [TTLocalizer.MusicEncntrGeneralBg, 30], + 'TC_SZ.ogg': [TTLocalizer.MusicTcSz, 57]}, + 3: {'create_a_toon.ogg': [TTLocalizer.MusicCreateAToon, 175], + 'tt_theme.ogg': [TTLocalizer.MusicTtsTheme, 51]}, + 4: {'minigame_race.ogg': [TTLocalizer.MusicMinigameRace, 77], + 'TC_nbrhood.ogg': [TTLocalizer.MusicTcNbrhood, 59], + 'MG_TwoDGame.ogg': [TTLocalizer.MusicMgTwodgame, 60], + 'MG_CogThief.ogg': [TTLocalizer.MusicMgCogthief, 61], + 'MG_Vine.ogg': [TTLocalizer.MusicMgVine, 32], + 'MG_IceGame.ogg': [TTLocalizer.MusicMgIcegame, 56], + 'FF_safezone.ogg': [TTLocalizer.MusicFfSafezone, 47]}, + 6: {'DD_SZ.ogg': [TTLocalizer.MusicDdSz, 33], + 'GZ_PlayGolf.ogg': [TTLocalizer.MusicGzPlaygolf, 61], + 'GS_SZ.ogg': [TTLocalizer.MusicGsSz, 60], + 'OZ_SZ.ogg': [TTLocalizer.MusicOzSz, 31], + 'GS_Race_CC.ogg': [TTLocalizer.MusicGsRaceCc, 58], + 'GS_Race_SS.ogg': [TTLocalizer.MusicGsRaceSs, 61], + 'GS_Race_RR.ogg': [TTLocalizer.MusicGsRaceRr, 60], + 'GZ_SZ.ogg': [TTLocalizer.MusicGzSz, 59], + 'MM_SZ.ogg': [TTLocalizer.MusicMmSz, 76], + 'DD_nbrhood.ogg': [TTLocalizer.MusicDdNbrhood, 67], + 'GS_KartShop.ogg': [TTLocalizer.MusicGsKartshop, 32]}, + 7: {'encntr_general_bg_indoor.ogg': [TTLocalizer.MusicEncntrGeneralBgIndoor, 31], + 'encntr_suit_winning_indoor.ogg': [TTLocalizer.MusicEncntrGeneralSuitWinningIndoor, 36]}, + 8: {'DL_nbrhood.ogg': [TTLocalizer.MusicDlNbrhood, 30], + 'DG_SZ.ogg': [TTLocalizer.MusicDgSz, 48], + 'DL_SZ.ogg': [TTLocalizer.MusicDlSz, 33], + 'TB_SZ.ogg': [TTLocalizer.MusicTbSz, 54]}, + 9: {'encntr_hall_of_fame.ogg': [TTLocalizer.MusicEncntrHallOfFame, 51], + 'CHQ_FACT_bg.ogg': [TTLocalizer.MusicChqFactBg, 50], + 'encntr_suit_winning.ogg': [TTLocalizer.MusicEncntrSuitWinning, 31], + 'encntr_head_suit_theme.ogg': [TTLocalizer.MusicEncntrHeadSuitTheme, 29]}, + 11: {'LB_juryBG.ogg': [TTLocalizer.MusicLbJurybg, 30], + 'LB_courtyard.ogg': [TTLocalizer.MusicLbCourtyard, 32]}, + 12: {'Bossbot_Factory_v1.ogg': [TTLocalizer.MusicBossbotFactoryV1, 30], + 'BossBot_CEO_v1.ogg': [TTLocalizer.MusicBossbotCeoV1, 31]}, + 13: {'party_original_theme.ogg': [TTLocalizer.MusicPartyOriginalTheme, 56], + 'party_polka_dance.ogg': [TTLocalizer.MusicPartyPolkaDance, 63], + 'party_waltz_dance.ogg': [TTLocalizer.MusicPartyWaltzDance, 63], + 'party_generic_theme_jazzy.ogg': [TTLocalizer.MusicPartyGenericThemeJazzy, 64]}} + +def countMusic(): + numMusic = 0 + for key in PhaseToMusicData: + numMusic += len(PhaseToMusicData[key]) + + print 'PhaseToMusicData %d' % numMusic + numMusic = 0 + for key in PhaseToMusicData40: + numMusic += len(PhaseToMusicData40[key]) + + print 'PhaseToMusicData40 %d' % numMusic + + +def getMusicRepeatTimes(length, minLength = MUSIC_MIN_LENGTH_SECONDS): + times = round(float(minLength) / length) + if minLength <= 0 or times < 1.0: + times = 1.0 + return times + + +def sanitizePhase(phase): + if phase == int(phase): + phase = int(phase) + return phase + + +CANNON_TIMEOUT = 30 +CANNON_MOVIE_LOAD = 1 +CANNON_MOVIE_CLEAR = 2 +CANNON_MOVIE_FORCE_EXIT = 3 +CANNON_MOVIE_LANDED = 4 +CannonJellyBeanReward = 2 +CannonMaxTotalReward = 200 +CatchMaxTotalReward = 1000 +PartyCannonCollisions = {'clouds': ['cloudSphere-0'], + 'bounce': ['wall_collision', + 'discoBall_collision', + 'platform_left_collision', + 'platform_right_collision'], + 'trampoline_bounce': 'TrampolineCollision', + 'ground': ['floor_collision', + 'danceFloor_collision', + 'danceFloorRamp_collision', + 'hill_collision', + 'fence_floor'], + 'fence': ['dockTube1_collision', + 'dockTube2_collision', + 'dockTube2_collision', + 'dockTube2_collision', + 'palm_collision_01', + 'palm_collision_02', + 'palm_collision_03', + 'wall_1_collision', + 'wall_2_collision', + 'wall_3_collision', + 'wall_4_collision', + 'wall_5_collision', + 'wall_6_collision', + 'tree_collision', + 'partyDecoration_collision', + 'launchPad_railing_collision', + 'launchPad_floor_collision', + 'launchPad_collision', + 'launchPad_railing2_collision', + 'launchPad__rocket_collision', + 'launchPad_lever_collision', + 'launchPad_bridge_collision', + 'launchPad_sphere2_collision', + 'launchPad_sphere1_collision', + 'partyClock_collision', + 'sign_collision']} + +def getCostOfParty(partyInfo): + newCost = 0 + for activityBase in partyInfo.activityList: + newCost += ActivityInformationDict[activityBase.activityId]['cost'] + + for decorBase in partyInfo.decors: + newCost += DecorationInformationDict[decorBase.decorId]['cost'] + + return newCost diff --git a/toontown/parties/PartyInfo.py b/toontown/parties/PartyInfo.py new file mode 100755 index 00000000..e5fe380e --- /dev/null +++ b/toontown/parties/PartyInfo.py @@ -0,0 +1,70 @@ +from datetime import datetime +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.PartyGlobals import InviteTheme +from toontown.parties.DecorBase import DecorBase +from toontown.parties.ActivityBase import ActivityBase + +class PartyInfoBase: + notify = DirectNotifyGlobal.directNotify.newCategory('PartyInfoBase') + + def __init__(self, partyId, hostId, startYear, startMonth, startDay, startHour, startMinute, endYear, endMonth, endDay, endHour, endMinute, isPrivate, inviteTheme, activityList, decors, status): + self.partyId = partyId + self.hostId = hostId + self.startTime = datetime(startYear, startMonth, startDay, startHour, startMinute) + self.endTime = datetime(endYear, endMonth, endDay, endHour, endMinute) + self.isPrivate = isPrivate + self.inviteTheme = inviteTheme + self.activityList = [] + for oneItem in activityList: + newActivity = ActivityBase(oneItem[0], oneItem[1], oneItem[2], oneItem[3]) + self.activityList.append(newActivity) + + self.decors = [] + for oneItem in decors: + newDecor = DecorBase(oneItem[0], oneItem[1], oneItem[2], oneItem[3]) + self.decors.append(newDecor) + + self.status = status + + def getActivityIds(self): + activities = [] + for activityBase in self.activityList: + activities.append(activityBase.activityId) + + return activities + + def __str__(self): + string = 'partyId=%d ' % self.partyId + string += 'hostId=%d ' % self.hostId + string += 'start=%s ' % self.startTime + string += 'end=%s ' % self.endTime + string += 'isPrivate=%s ' % self.isPrivate + string += 'inviteTheme=%s ' % InviteTheme.getString(self.inviteTheme) + string += 'activityList=%s ' % self.activityList + string += 'decors=%s ' % self.decors + string += 'status=%s' % self.status + string += '\n' + return string + + def __repr__(self): + return self.__str__() + + +class PartyInfo(PartyInfoBase): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyInfo') + + def __init__(self, partyId, hostId, startYear, startMonth, startDay, startHour, startMinute, endYear, endMonth, endDay, endHour, endMinute, isPrivate, inviteTheme, activityList, decors, status): + PartyInfoBase.__init__(self, partyId, hostId, startYear, startMonth, startDay, startHour, startMinute, endYear, endMonth, endDay, endHour, endMinute, isPrivate, inviteTheme, activityList, decors, status) + serverTzInfo = base.cr.toontownTimeManager.serverTimeZone + self.startTime = self.startTime.replace(tzinfo=serverTzInfo) + self.endTime = self.endTime.replace(tzinfo=serverTzInfo) + + +class PartyInfoAI(PartyInfoBase): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyInfo') + + def __init__(self, partyId, hostId, startYear, startMonth, startDay, startHour, startMinute, endYear, endMonth, endDay, endHour, endMinute, isPrivate, inviteTheme, activityList, decors, status): + PartyInfoBase.__init__(self, partyId, hostId, startYear, startMonth, startDay, startHour, startMinute, endYear, endMonth, endDay, endHour, endMinute, isPrivate, inviteTheme, activityList, decors, status) + #serverTzInfo = simbase.air.timeManager.serverTimeZone + self.startTime = self.startTime#.replace(tzinfo=serverTzInfo) + self.endTime = self.endTime#.replacetzinfo=serverTzInfo) diff --git a/toontown/parties/PartyLoader.py b/toontown/parties/PartyLoader.py new file mode 100755 index 00000000..06d7a70f --- /dev/null +++ b/toontown/parties/PartyLoader.py @@ -0,0 +1,301 @@ +import math +import random +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from panda3d.core import * +from pandac.PandaModules import NodePath +from toontown.toonbase.ToontownGlobals import * +from toontown.safezone import SafeZoneLoader +from toontown.parties import Party +from toontown.parties.PartyGlobals import FireworksStartedEvent, FireworksFinishedEvent + +class PartyLoader(SafeZoneLoader.SafeZoneLoader): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyLoader') + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + del self.fsm + self.fsm = ClassicFSM.ClassicFSM('PartyLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'party', 'planning']), + State.State('party', self.enterParty, self.exitParty, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['planning', 'party']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.musicFile = 'phase_13/audio/bgm/party_original_theme.ogg' + self.activityMusicFile = 'phase_13/audio/bgm/party_waltz_dance.ogg' + self.dnaFile = 'phase_13/dna/party_sz.pdna' + self.safeZoneStorageDNAFile = None + self.cloudSwitch = 0 + self.id = PartyHood + self.partyOwnerId = None + self.branchZone = None + self.partyDoneEvent = 'partyDone' + self.barrel = None + self.clouds = [] + self.cloudTrack = None + self.sunMoonNode = None + self.fsm.enterInitialState() + return + + def load(self): + self.oldClear = base.win.getClearColor() + base.win.setClearColor(Vec4(0.47, 0.69, 0.3, 1.0)) + SafeZoneLoader.SafeZoneLoader.load(self) + self.underwaterSound = base.loadSfx('phase_4/audio/sfx/AV_ambient_water.ogg') + self.swimSound = base.loadSfx('phase_4/audio/sfx/AV_swim_single_stroke.ogg') + self.submergeSound = base.loadSfx('phase_5.5/audio/sfx/AV_jump_in_water.ogg') + self.birdSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.ogg', 'phase_4/audio/sfx/SZ_TC_bird2.ogg', 'phase_4/audio/sfx/SZ_TC_bird3.ogg']) + self.cricketSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.ogg', 'phase_4/audio/sfx/SZ_TC_bird2.ogg', 'phase_4/audio/sfx/SZ_TC_bird3.ogg']) + + def unload(self): + self.ignoreAll() + base.win.setClearColor(self.oldClear) + if base.cr.partyManager: + base.cr.partyManager.leaveParty() + self.partyOwnerId = None + self.partyZoneId = None + if self.place: + self.place.exit() + self.place.unload() + del self.place + del self.underwaterSound + del self.swimSound + del self.submergeSound + del self.birdSound + del self.cricketSound + self.__cleanupCloudFadeInterval() + if self.sunMoonNode: + self.sunMoonNode.removeNode() + del self.sunMoonNode + self.sunMoonNode = None + if self.clouds: + for cloud in self.clouds: + cloud[0].removeNode() + del cloud[1] + + del self.clouds + if self.barrel: + self.barrel.removeNode() + SafeZoneLoader.SafeZoneLoader.unload(self) + return + + def loadClouds(self): + self.loadCloudPlatforms() + if base.cloudPlatformsEnabled and 0: + self.setCloudSwitch(1) + if self.cloudSwitch: + self.setCloudSwitch(self.cloudSwitch) + + def enter(self, requestStatus): + self.partyOwnerId = requestStatus.get('ownerId', base.localAvatar.doId) + base.localAvatar.inParty = 1 + self.accept(FireworksStartedEvent, self.__handleFireworksStarted) + self.accept(FireworksFinishedEvent, self.__handleFireworksFinished) + SafeZoneLoader.SafeZoneLoader.enter(self, requestStatus) + + def exit(self): + self.ignoreAll() + base.cr.cache.flush() + base.localAvatar.stopChat() + base.localAvatar.inParty = 0 + self.ignore(FireworksStartedEvent) + self.ignore(FireworksFinishedEvent) + SafeZoneLoader.SafeZoneLoader.exit(self) + + def createSafeZone(self, dnaFile): + SafeZoneLoader.SafeZoneLoader.createSafeZone(self, dnaFile) + parent = self.geom.getParent() + geom = self.geom + n = NodePath('PartyGroundRoot') + n.reparentTo(parent) + geom.reparentTo(n) + geom.setPos(-10.0, 0.0, 0.0) + self.geom = n + self.loadSunMoon() + + def loadSunMoon(self): + self.sun = loader.loadModel('phase_4/models/props/sun.bam') + self.moon = loader.loadModel('phase_5.5/models/props/moon.bam') + self.sunMoonNode = self.geom.attachNewNode('sunMoon') + self.sunMoonNode.setPosHpr(0, 0, 0, 0, 0, 0) + if self.sun: + self.sun.reparentTo(self.sunMoonNode) + self.sun.setY(270) + self.sun.setScale(2) + self.sun.setBillboardPointEye() + if self.moon: + self.moon.reparentTo(self.sunMoonNode) + self.moon.setY(-270) + self.moon.setScale(15) + self.moon.setBillboardPointEye() + self.sunMoonNode.setP(30) + + def enterParty(self, requestStatus): + self.notify.debug('enterParty: requestStatus = %s' % requestStatus) + ownerId = requestStatus.get('ownerId') + if ownerId: + self.partyOwnerId = ownerId + zoneId = requestStatus['zoneId'] + self.notify.debug('enterParty, ownerId = %s, zoneId = %s' % (self.partyOwnerId, zoneId)) + self.accept(self.partyDoneEvent, self.handlePartyDone) + self.place = Party.Party(self, self.partyOwnerId, zoneId, self.fsm.getStateNamed('party'), self.partyDoneEvent) + base.cr.playGame.setPlace(self.place) + self.place.load() + self.place.enter(requestStatus) + self.partyZoneId = zoneId + + def exitParty(self): + self.notify.debug('exitParty') + self.ignore(self.partyDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + base.cr.cache.flush() + return + + def handlePartyDone(self, doneStatus = None): + PartyLoader.notify.debug('handlePartyDone doneStatus = %s' % doneStatus) + if not doneStatus: + doneStatus = self.place.getDoneStatus() + how = doneStatus['how'] + shardId = doneStatus['shardId'] + hoodId = doneStatus['hoodId'] + zoneId = doneStatus['zoneId'] + avId = doneStatus.get('avId', -1) + ownerId = doneStatus.get('ownerId', -1) + self.notify.debug('hoodId = %s, avId = %s' % (hoodId, avId)) + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + + def handleQuietZoneDone(self): + status = self.quietZoneStateData.getRequestStatus() + self.fsm.request(status['where'], [status]) + + def atMyParty(self): + if self.partyOwnerId != None: + if self.partyOwnerId == base.localAvatar.getDoId(): + return 1 + else: + return 0 + else: + self.notify.warning("We aren't in an party") + return + + def startCloudPlatforms(self): + return + if len(self.clouds): + self.cloudTrack = self.__cloudTrack() + self.cloudTrack.loop() + + def stopCloudPlatforms(self): + if self.cloudTrack: + self.cloudTrack.pause() + del self.cloudTrack + self.cloudTrack = None + return + + def __cloudTrack(self): + track = Parallel() + for cloud in self.clouds: + axis = cloud[1] + pos = cloud[0].getPos(render) + newPos = pos + axis * 30 + reversePos = pos - axis * 30 + track.append(Sequence(LerpPosInterval(cloud[0], 10, newPos), LerpPosInterval(cloud[0], 20, reversePos), LerpPosInterval(cloud[0], 10, pos))) + + return track + + def debugGeom(self, decomposed): + print 'numPrimitives = %d' % decomposed.getNumPrimitives() + for primIndex in xrange(decomposed.getNumPrimitives()): + prim = decomposed.getPrimitive(primIndex) + print 'prim = %s' % prim + print 'isIndexed = %d' % prim.isIndexed() + print 'prim.getNumPrimitives = %d' % prim.getNumPrimitives() + for basicPrim in xrange(prim.getNumPrimitives()): + print '%d start=%d' % (basicPrim, prim.getPrimitiveStart(basicPrim)) + print '%d end=%d' % (basicPrim, prim.getPrimitiveEnd(basicPrim)) + + def loadCloud(self, version, radius, zOffset): + self.notify.debug('loadOnePlatform version=%d' % version) + cloud = NodePath('cloud-%d%d' % (radius, version)) + cloudModel = loader.loadModel('phase_5.5/models/estate/bumper_cloud') + cc = cloudModel.copyTo(cloud) + colCube = cc.find('**/collision') + colCube.setName('cloudSphere-0') + dTheta = 2.0 * math.pi / self.numClouds + cloud.reparentTo(self.cloudOrigin) + axes = [Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)] + cloud.setPos(radius * math.cos(version * dTheta), radius * math.sin(version * dTheta), 4 * random.random() + zOffset) + cloud.setScale(4.0) + cloud.setTag('number', '%d%d' % (radius, version)) + self.clouds.append([cloud, random.choice(axes)]) + + def loadSkyCollision(self): + plane = CollisionPlane(Plane(Vec3(0, 0, -1), Point3(0, 0, 200))) + plane.setTangible(0) + planeNode = CollisionNode('sky_collision') + planeNode.addSolid(plane) + self.cloudOrigin.attachNewNode(planeNode) + + def loadCloudPlatforms(self): + self.cloudOrigin = self.geom.attachNewNode('cloudOrigin') + self.cloudOrigin.setZ(30) + self.loadSkyCollision() + self.numClouds = 12 + for i in xrange(self.numClouds): + self.loadCloud(i, 50, 0) + + for i in xrange(self.numClouds): + self.loadCloud(i, 70, 30) + + for i in xrange(self.numClouds): + self.loadCloud(i, 30, 60) + + self.cloudOrigin.stash() + + def __cleanupCloudFadeInterval(self): + if hasattr(self, 'cloudFadeInterval'): + self.cloudFadeInterval.pause() + self.cloudFadeInterval = None + return + + def fadeClouds(self): + self.__cleanupCloudFadeInterval() + self.cloudOrigin.setTransparency(1) + self.cloudFadeInterval = self.cloudOrigin.colorInterval(0.5, Vec4(1, 1, 1, int(self.cloudOrigin.isStashed())), blendType='easeIn') + if self.cloudOrigin.isStashed(): + self.cloudOrigin.setColor(Vec4(1, 1, 1, 0)) + self.setCloudSwitch(1) + else: + self.cloudFadeInterval = Sequence(self.cloudFadeInterval, Func(self.setCloudSwitch, 0), Func(self.cloudOrigin.setTransparency, 0)) + self.cloudFadeInterval.start() + + def setCloudSwitch(self, on): + self.cloudSwitch = on + if hasattr(self, 'cloudOrigin'): + if on: + self.cloudOrigin.unstash() + else: + self.cloudOrigin.stash() + + def _clearDayChangeInterval(self): + if hasattr(self, 'dayChangeInterval'): + self.dayChangeInterval.pause() + self.dayChangeInterval = None + return + + def switchToNight(self): + self._clearDayChangeInterval() + self.dayChangeInterval = Sequence(self.sunMoonNode.hprInterval(5.0, Point3(0, -30, 0), blendType='easeInOut'), Func(base.win.setClearColor, Vec4(0.15, 0.22, 0.14, 1.0))) + self.dayChangeInterval.start() + + def switchToDay(self): + self.dayChangeInterval = Sequence(Func(base.win.setClearColor, Vec4(0.47, 0.69, 0.3, 1.0)), self.sunMoonNode.hprInterval(5.0, Point3(0, 30, 0), blendType='easeInOut')) + self.dayChangeInterval.start() + + def __handleFireworksStarted(self): + self.sunMoonNode.hide() + + def __handleFireworksFinished(self): + self.sunMoonNode.show() diff --git a/toontown/parties/PartyPlanner.py b/toontown/parties/PartyPlanner.py new file mode 100755 index 00000000..04829af0 --- /dev/null +++ b/toontown/parties/PartyPlanner.py @@ -0,0 +1,734 @@ +import calendar +from datetime import datetime +from datetime import timedelta +from direct.directnotify import DirectNotifyGlobal +from direct.fsm.FSM import FSM +from direct.gui import DirectGuiGlobals +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel, DirectScrolledList, DirectCheckButton +from direct.showbase import DirectObject +from direct.showbase import PythonUtil +from panda3d.core import * + +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPLocalizer +from otp.nametag.NametagGroup import NametagGroup +from otp.nametag.Nametag import Nametag +from otp.nametag.NametagFloat2d import * +from toontown.parties import PartyGlobals +from toontown.parties import PartyUtils +from toontown.parties.CalendarGuiMonth import CalendarGuiMonth +from toontown.parties.InviteVisual import InviteVisual +from toontown.parties.PartyEditor import PartyEditor +from toontown.parties.PartyInfo import PartyInfo +from toontown.parties.ScrolledFriendList import ScrolledFriendList +from toontown.toon import ToonHead +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog + + +class PartyPlanner(DirectFrame, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyPlanner') + + def __init__(self, doneEvent = None): + FSM.__init__(self, 'PartyPlannerFSM') + DirectFrame.__init__(self) + self.doneEvent = doneEvent + self.stateArray = ['Off', + 'Welcome', + 'PartyEditor', # 'Guests', skip over the Guests state. + 'Date', + 'Time', + 'Invitation', + 'Farewell'] + self.partyTime = base.cr.toontownTimeManager.getCurServerDateTime() + self.partyNowTime = base.cr.toontownTimeManager.getCurServerDateTime() + minutesToNextFifteen = 15 - self.partyTime.minute % 15 + self.cleanPartyTime = self.partyTime + timedelta(minutes=minutesToNextFifteen, seconds=-self.partyTime.second) + self.partyTime = self.cleanPartyTime + self.guests = [] + self.isPrivate = False + self.selectedCalendarGuiDay = None + self.gui = loader.loadModel('phase_4/models/parties/partyPlannerGUI') + self.partyDuration = timedelta(hours=PartyGlobals.DefaultPartyDuration) + self.timeTypeToMaxValue = {'hour': 23, + 'minute': 59} + self.timeTypeToChangeAmount = {'hour': (1, -1), + 'minute': (15, -15), + 'ampm': (1, -1)} + self.partyInfo = None + self.asapMinuteRounding = base.config.GetInt('party-asap-minute-rounding', PartyGlobals.PartyPlannerAsapMinuteRounding) + self.load() + self.request('Welcome') + return + + def enterWelcome(self, *args): + self.prevButton['state'] = DirectGuiGlobals.DISABLED + self.prevButton.hide() + self.nextButton['state'] = DirectGuiGlobals.NORMAL + self.welcomePage.show() + self.partyPlannerHead.reparentTo(self.welcomePage) + self.partyPlannerHead.startBlink() + self.partyPlannerHead.startLookAround() + self.nametagNP.reparentTo(self.welcomePage) + self.chatNP.reparentTo(self.welcomePage) + + def exitWelcome(self): + self.welcomePage.hide() + self.prevButton.show() + self.partyPlannerHead.stopBlink() + self.partyPlannerHead.stopLookAround() + + def enterPartyEditor(self, *args): + self.prevButton['state'] = DirectGuiGlobals.NORMAL + self.nextButton['state'] = DirectGuiGlobals.DISABLED + self.nextButton.hide() + self.partyEditorPage.show() + self.okWithGroundsGui.doneStatus = '' + self.partyEditor.request('Idle') + + def exitPartyEditor(self): + self.partyEditor.request('Hidden') + self.partyEditorPage.hide() + + def enterGuests(self, *args): + self.prevButton['state'] = DirectGuiGlobals.NORMAL + self.nextButton['state'] = DirectGuiGlobals.NORMAL + self.nextButton.show() + self.guestPage.show() + + def exitGuests(self): + self.guests = [] + for friendCheckBox in self.friendList['items']: + if friendCheckBox['indicatorValue']: + self.guests.append(friendCheckBox.getPythonTag('id')) + + self.guestPage.hide() + + def enterDate(self, *args): + self.prevButton.show() + self.prevButton['state'] = DirectGuiGlobals.NORMAL + if self.selectedCalendarGuiDay is None: + self.nextButton['state'] = DirectGuiGlobals.DISABLED + self.nextButton.hide() + self.makePartyNowButton.show() + self.datePage.show() + return + + def exitDate(self): + self.datePage.hide() + self.nextButton.show() + if self.selectedCalendarGuiDay is not None: + self.partyTime = self.cleanPartyTime + self.alterPartyTime(year=self.selectedCalendarGuiDay.myDate.year, month=self.selectedCalendarGuiDay.myDate.month, day=self.selectedCalendarGuiDay.myDate.day) + else: + self.partyNowTime = self.calcAsapTime() + self.partyTime = self.partyNowTime + return + + def calcAsapTime(self): + curServerTime = base.cr.toontownTimeManager.getCurServerDateTime() + baseTime = curServerTime + baseTime = baseTime.replace(baseTime.year, baseTime.month, baseTime.day, baseTime.hour, baseTime.minute, second=0, microsecond=0) + minute = curServerTime.minute + remainder = minute % self.asapMinuteRounding + if remainder: + baseTime += timedelta(minutes=self.asapMinuteRounding - remainder) + else: + baseTime += timedelta(minutes=self.asapMinuteRounding) + return baseTime + + def enterTime(self, *args): + self.prevButton.show() + self.prevButton['state'] = DirectGuiGlobals.NORMAL + self.nextButton.show() + self.timePage.show() + self.timePageRecapToontownTimeLabel2['text'] = '%s' % PartyUtils.formatDateTime(self.partyTime) + self.timePageRecapLocalTimeLabel['text'] = '%s%s' % (TTLocalizer.PartyPlannerTimeLocalTime, PartyUtils.formatDateTime(self.partyTime, inLocalTime=True)) + + def exitTime(self): + self.timePage.hide() + self.nextButton.show() + + def enterInvitation(self, *args): + self.prevButton['state'] = DirectGuiGlobals.NORMAL + self.nextButton.hide() + defaultInviteTheme = PartyGlobals.InviteTheme.GenericMale + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.VICTORY_PARTY_HOLIDAY): + defaultInviteTheme = PartyGlobals.InviteTheme.VictoryParty + elif base.cr.newsManager.isHolidayRunning(ToontownGlobals.KARTING_TICKETS_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.GRAND_PRIX): + defaultInviteTheme = PartyGlobals.InviteTheme.Racing + elif base.cr.newsManager.isHolidayRunning(ToontownGlobals.VALENTOONS_DAY): + defaultInviteTheme = PartyGlobals.InviteTheme.Valentoons + if self.partyInfo is not None: + del self.partyInfo + activityList = self.partyEditor.partyEditorGrid.getActivitiesOnGrid() + decorationList = self.partyEditor.partyEditorGrid.getDecorationsOnGrid() + endTime = self.partyTime + self.partyDuration + self.partyInfo = PartyInfo(0, 0, self.partyTime.year, self.partyTime.month, self.partyTime.day, self.partyTime.hour, self.partyTime.minute, endTime.year, endTime.month, endTime.day, endTime.hour, endTime.minute, self.isPrivate, defaultInviteTheme, activityList, decorationList, 0) + if self.noFriends or len(self.getInvitees()) == 0: + self.inviteVisual.setNoFriends(True) + self.invitationTitleLabel['text'] = TTLocalizer.PartyPlannerConfirmTitleNoFriends + self.inviteButton['text'] = TTLocalizer.PartyPlannerInviteButtonNoFriends + self.selectedInviteThemeLabel.stash() + self.nextThemeButton.stash() + self.prevThemeButton.stash() + self.setInviteTheme(defaultInviteTheme) + else: + self.inviteVisual.setNoFriends(False) + self.invitationTitleLabel['text'] = TTLocalizer.PartyPlannerConfirmTitle + self.inviteButton['text'] = TTLocalizer.PartyPlannerInviteButton + self.selectedInviteThemeLabel.unstash() + self.nextThemeButton.unstash() + self.prevThemeButton.unstash() + self.setInviteTheme(defaultInviteTheme) + self.inviteVisual.updateInvitation(base.localAvatar.getName(), self.partyInfo) + self.invitationPage.show() + return + + def __prevTheme(self): + self.nextThemeButton.show() + prevTheme = self.currentInvitationTheme - 1 + while prevTheme not in self.inviteThemes: + prevTheme -= 1 + if prevTheme == self.currentInvitationTheme: + self.notify.warning('No previous invite theme found.') + break + elif prevTheme < 0: + prevTheme = len(self.inviteVisual.inviteThemesIdToInfo) - 1 + + self.setInviteTheme(prevTheme) + + def __nextTheme(self): + self.prevThemeButton.show() + nextTheme = self.currentInvitationTheme + 1 + while nextTheme not in self.inviteThemes: + nextTheme += 1 + if nextTheme == self.currentInvitationTheme: + self.notify.warning('No next invite theme found.') + break + elif nextTheme >= len(self.inviteVisual.inviteThemesIdToInfo): + nextTheme = 0 + + self.setInviteTheme(nextTheme) + + def setInviteTheme(self, themeNumber): + self.currentInvitationTheme = themeNumber + self.selectedInviteThemeLabel['text'] = '%s %s (%d/%d)' % (self.inviteVisual.inviteThemesIdToInfo[self.currentInvitationTheme][1], + TTLocalizer.PartyPlannerInvitationTheme, + self.inviteThemes.index(self.currentInvitationTheme) + 1, + len(self.inviteThemes)) + self.partyInfo.inviteTheme = self.currentInvitationTheme + self.inviteVisual.updateInvitation(base.localAvatar.getName(), self.partyInfo) + + def exitInvitation(self): + self.invitationPage.hide() + self.nextButton.show() + + def enterFarewell(self, goingBackAllowed): + self.farewellPage.show() + if goingBackAllowed: + self.prevButton.show() + else: + self.prevButton.hide() + self.nextButton.hide() + self.partyPlannerHead.reparentTo(self.farewellPage) + self.partyPlannerHead.startBlink() + self.partyPlannerHead.startLookAround() + self.nametagNP.reparentTo(self.farewellPage) + self.chatNP.reparentTo(self.farewellPage) + + def exitFarewell(self): + self.farewellPage.hide() + self.nextButton.show() + self.prevButton.show() + self.partyPlannerHead.stopBlink() + self.partyPlannerHead.stopLookAround() + + def load(self): + self.frame = DirectFrame(parent=aspect2d, geom=self.gui.find('**/background'), relief=None, scale=0.85, pos=(0.05, 0.0, 0.1)) + self.titleScale = TTLocalizer.PPtitleScale + self._createNavButtons() + self.welcomePage = self._createWelcomePage() + self.welcomePage.hide() + self.datePage = self._createDatePage() + self.datePage.hide() + self.timePage = self._createTimePage() + self.timePage.hide() + self.guestPage = self._createGuestPage() + self.guestPage.hide() + self.partyEditorPage = self._createPartyEditorPage() + self.partyEditorPage.hide() + self.invitationPage = self._createInvitationPage() + self.invitationPage.hide() + self.farewellPage = self._createFarewellPage() + self.farewellPage.hide() + return + + def _createNavButtons(self): + self.quitButton = DirectButton(parent=self.frame, relief=None, geom=(self.gui.find('**/cancelButton_up'), self.gui.find('**/cancelButton_down'), self.gui.find('**/cancelButton_rollover')), command=self.__acceptExit) + self.nextButton = DirectButton(parent=self.frame, relief=None, geom=(self.gui.find('**/bottomNext_button/nextButton_up'), self.gui.find('**/bottomNext_button/nextButton_down'), self.gui.find('**/bottomNext_button/nextButton_rollover')), command=self.__nextItem, state=DirectGuiGlobals.DISABLED) + self.prevButton = DirectButton(parent=self.frame, relief=None, geom=(self.gui.find('**/bottomPrevious_button/previousButton_up'), self.gui.find('**/bottomPrevious_button/previousButton_down'), self.gui.find('**/bottomPrevious_button/previousButton_rollover')), command=self.__prevItem, state=DirectGuiGlobals.DISABLED) + self.currentItem = None + return + + def __createNametag(self, parent): + if self.nametagGroup == None: + self.nametagGroup = NametagGroup() + self.nametagGroup.setFont(ToontownGlobals.getToonFont()) + self.nametagGroup.setSpeechFont(ToontownGlobals.getToonFont()) + self.nametagGroup.setActive(0) + self.nametagGroup.setAvatar(self.partyPlannerHead) + self.nametagGroup.manage(base.marginManager) + self.nametagGroup.setColorCode(self.nametagGroup.CCNonPlayer) + self.nametagGroup.getNametag2d().setContents(0) + self.nametagNode = NametagFloat2d() + self.nametagNode.setContents(Nametag.CName) + self.nametagGroup.addNametag(self.nametagNode) + self.nametagGroup.setName(base.cr.partyManager.getPartyPlannerName()) + self.nametagNP = parent.attachNewNode(self.nametagNode) + nametagPos = self.gui.find('**/step_01_partymanPeteNametag_locator').getPos() + self.nametagNP.setPosHprScale(nametagPos[0], 0, nametagPos[2], 0, 0, 0, 0.1, 1, 0.1) + self.chatNode = NametagFloat2d() + self.chatNode.setContents(Nametag.CSpeech | Nametag.CThought) + self.nametagGroup.addNametag(self.chatNode) + self.nametagGroup.setChat(TTLocalizer.PartyPlannerInstructions, CFSpeech) + self.chatNP = parent.attachNewNode(self.chatNode) + chatPos = self.gui.find('**/step_01_partymanPeteText_locator').getPos() + self.chatNP.setPosHprScale(chatPos[0], 0, chatPos[2], 0, 0, 0, 0.08, 1, 0.08) + + def clearNametag(self): + if self.nametagGroup != None: + self.nametagGroup.unmanage(base.marginManager) + self.nametagGroup.removeNametag(self.nametagNode) + self.nametagGroup.removeNametag(self.chatNode) + self.nametagNP.removeNode() + self.chatNP.removeNode() + del self.nametagNP + del self.chatNP + del self.nametagNode + del self.chatNode + self.nametagGroup.setAvatar(NodePath()) + self.nametagGroup.destroy() + self.nametagGroup = None + return + + def _createWelcomePage(self): + self.nametagGroup = None + page = DirectFrame(self.frame) + page.setName('PartyPlannerWelcomePage') + self.welcomeTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerWelcomeTitle, pos=self.gui.find('**/title_locator').getPos(), scale=self.titleScale) + self.partyPlannerHead = ToonHead.ToonHead() + partyPlannerStyle = base.cr.partyManager.getPartyPlannerStyle() + self.partyPlannerHead.setupHead(partyPlannerStyle, forGui=True) + self.partyPlannerHead.setPos(self.gui.find('**/step_01_partymanPete_locator').getPos()) + animal = partyPlannerStyle.getAnimal() + if animal == 'cat' or animal == 'pig': + headScale = 0.4 + elif animal == 'dog' or animal == 'bear': + headScale = 0.45 + elif animal == 'rabbit': + headScale = 0.35 + else: + headScale = 0.3 + self.partyPlannerHead.setScale(headScale) + self.partyPlannerHead.setH(180.0) + self.partyPlannerHead.reparentTo(page) + self.__createNametag(page) + return page + + def _createDatePage(self): + page = DirectFrame(self.frame) + page.setName('PartyPlannerDatePage') + self.createDateTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerDateTitle, pos=self.gui.find('**/title_locator').getPos(), scale=self.titleScale) + pos = self.gui.find('**/step_06_sendInvitation_locator').getPos() + self.makePartyNowButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/send_up'), self.gui.find('**/send_down'), self.gui.find('**/send_rollover')), text=TTLocalizer.PartyPlannerPartyNow, text_pos=(pos[0], pos[2]), text_scale=0.05, command=self.__doMakePartyNow) + curServerDate = base.cr.toontownTimeManager.getCurServerDateTime() + self.calendarGuiMonth = CalendarGuiMonth(page, curServerDate, scale=0.95, pos=(-0.05, 0.0, -0.33), dayClickCallback=self._dayClickCallback, onlyFutureDaysClickable=True) + return page + + def __doMakePartyNow(self): + self.request('Invitation') + + def _dayClickCallback(self, calendarGuiDay): + self.selectedCalendarGuiDay = calendarGuiDay + self.nextButton['state'] = DirectGuiGlobals.NORMAL + self.makePartyNowButton.hide() + self.nextButton.show() + + def alterPartyTime(self, year = None, month = None, day = None, hour = None, minute = None): + self.partyTime = datetime(year=self.positiveTime('year', year), month=self.positiveTime('month', month), day=self.positiveTime('day', day), hour=self.positiveTime('hour', hour), minute=self.positiveTime('minute', minute), tzinfo=self.partyTime.tzinfo) + + def positiveTime(self, type, amount): + if amount is None: + return getattr(self.partyTime, type) + if type == 'hour' or type == 'minute': + if amount < 0: + return self.timeTypeToMaxValue[type] + 1 + self.timeTypeToChangeAmount[type][1] + elif amount > self.timeTypeToMaxValue[type]: + return 0 + return amount + + def _createTimePage(self): + page = DirectFrame(self.frame) + page.setName('PartyPlannerTimePage') + self.createTimeTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerTimeTitle, pos=self.gui.find('**/title_locator').getPos(), scale=self.titleScale) + self.clockImage = DirectFrame(parent=page, relief=None, geom=self.gui.find('**/toontownTime_background')) + self.timePageToontownLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerTimeToontown, pos=self.gui.find('**/step_03_toontown_locator').getPos(), scale=0.15, text_fg=(1.0, 0.0, 0.0, 1.0), text_font=ToontownGlobals.getSignFont()) + self.timePageTimeLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerTimeTime, pos=self.gui.find('**/step_03_time_locator').getPos(), scale=0.15, text_fg=(1.0, 0.0, 0.0, 1.0), text_font=ToontownGlobals.getSignFont()) + self.timePageRecapLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerTimeRecap, pos=self.gui.find('**/step_03_partyDateAndTime_locator').getPos(), scale=0.09) + self.timePageRecapToontownTimeLabel1 = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerTimeToontownTime, pos=self.gui.find('**/step_03_toontownTime_locator').getPos(), scale=0.06) + self.timePageRecapToontownTimeLabel2 = DirectLabel(parent=page, relief=None, text='%s' % PartyUtils.formatDateTime(self.partyTime), pos=self.gui.find('**/step_03_toontownDateAndTime_loactor').getPos(), textMayChange=True, scale=0.06) + self.timePageRecapLocalTimeLabel = DirectLabel(parent=page, relief=None, text='%s%s' % (TTLocalizer.PartyPlannerTimeLocalTime, PartyUtils.formatDateTime(self.partyTime, inLocalTime=True)), pos=self.gui.find('**/step_03_localDateAndTime_loactor').getPos(), textMayChange=True, scale=0.06, text_fg=(1.0, 0.0, 0.0, 1.0)) + self.timeInputHourLabel, self.timeInputHourUpButton, self.timeInputHourDownButton = self.getTimeWidgets(page, 'hour') + self.timeInputMinuteLabel, self.timeInputMinuteUpButton, self.timeInputMinuteDownButton = self.getTimeWidgets(page, 'minute') + self.timeInputAmPmLabel, self.timeInputAmPmUpButton, self.timeInputAmPmDownButton = self.getTimeWidgets(page, 'ampm') + self.timePagecolonLabel = DirectLabel(parent=page, relief=None, text=':', pos=self.gui.find('**/step_03_colon_locator').getPos(), scale=0.15) + return page + + def getTimeWidgets(self, page, type): + if type == 'ampm': + data = self.getCurrentAmPm() + else: + data = getattr(self.partyTime, type) + if data == 0 and type == 'minute': + data = '00' + else: + if type == 'hour': + data = data % 12 + if data == 0: + data = 12 + data = '%d' % data + label = DirectLabel(parent=page, relief=None, text='%s' % data, textMayChange=True, pos=self.gui.find('**/step_03_%s_locator' % type).getPos(), scale=0.12) + + def changeValue(self, amount): + if type == 'ampm': + self.alterPartyTime(hour=(self.partyTime.hour + 12) % 24) + newAmount = self.getCurrentAmPm() + label['text'] = newAmount + else: + if type == 'hour': + newAmount = getattr(self.partyTime, type) + amount + newAmount = newAmount % 12 + if self.timeInputAmPmLabel['text'] == TTLocalizer.PartyTimeFormatMeridiemPM: + newAmount = newAmount % 12 + 12 + self.alterPartyTime(hour=newAmount) + elif type == 'minute': + newAmount = getattr(self.partyTime, type) + amount + self.alterPartyTime(minute=newAmount) + else: + PartyPlanner.notify.error('Invalid type for changeValue in PartyPlanner: %s' % type) + newAmount = getattr(self.partyTime, type) + if newAmount < 10 and type == 'minute': + label['text'] = '0%d' % newAmount + else: + if type == 'hour': + newAmount = newAmount % 12 + if newAmount == 0: + newAmount = 12 + label['text'] = '%d' % newAmount + self.timePageRecapToontownTimeLabel2['text'] = '%s' % PartyUtils.formatDateTime(self.partyTime) + self.timePageRecapLocalTimeLabel['text'] = '%s%s' % (TTLocalizer.PartyPlannerTimeLocalTime, PartyUtils.formatDateTime(self.partyTime, inLocalTime=True)) + + upButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/%sButtonUp_up' % type), self.gui.find('**/%sButtonUp_down' % type), self.gui.find('**/%sButtonUp_rollover' % type)), command=changeValue, extraArgs=[self, self.timeTypeToChangeAmount[type][0]]) + downButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/%sButtonDown_up' % type), self.gui.find('**/%sButtonDown_down' % type), self.gui.find('**/%sButtonDown_rollover' % type)), command=changeValue, extraArgs=[self, self.timeTypeToChangeAmount[type][1]]) + return (label, upButton, downButton) + + def getCurrentAmPm(self): + if self.partyTime.hour < 12: + return TTLocalizer.PartyTimeFormatMeridiemAM + else: + return TTLocalizer.PartyTimeFormatMeridiemPM + + def _createGuestPage(self): + page = DirectFrame(self.frame) + page.setName('PartyPlannerGuestPage') + self.guestTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerGuestTitle, pos=self.gui.find('**/title_locator').getPos(), scale=self.titleScale) + self.guestBackgroundLabel = DirectLabel(parent=page, relief=None, image=self.gui.find('**/guestListBackground_flat'), scale=(1.2, 1.0, 1.0)) + self.friendList = ScrolledFriendList(page, self.gui, makeItemsCheckBoxes=True) + if len(base.localAvatar.friendsList) == 0: + self.noFriends = True + else: + self.noFriends = False + for friendId in base.localAvatar.friendsList: + self.friendList.addFriend(friendId) + + self.friendList.scrollTo(0) + pos = self.gui.find('**/step_04_partyWillBe_locator').getPos() + self.publicPrivateLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerPublicPrivateLabel, text_align=TextNode.ACenter, text_scale=0.065, pos=pos) + self.publicDescriptionLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerPublicDescription, text_align=TextNode.ACenter, text_scale=TTLocalizer.PPpbulicDescriptionLabel, pos=(pos[0] - 0.52, pos[1], pos[2])) + self.publicDescriptionLabel.stash() + self.privateDescriptionLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerPrivateDescription, text_align=TextNode.ACenter, text_scale=TTLocalizer.PPprivateDescriptionLabel, pos=(pos[0] + 0.55, pos[1], pos[2])) + self.privateDescriptionLabel.stash() + pos = self.gui.find('**/step_04_public_locator').getPos() + self.publicButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/publicButton_up'), + self.gui.find('**/publicButton_down'), + self.gui.find('**/publicButton_rollover'), + self.gui.find('**/publicButton_inactive')), text=TTLocalizer.PartyPlannerPublic, text_pos=(pos[0], pos[2]), text_scale=TTLocalizer.PPpublicButton, command=self.__doTogglePublicPrivate) + self.publicButton['state'] = DirectGuiGlobals.DISABLED + self.publicButton.bind(DirectGuiGlobals.ENTER, self.__enterPublic) + self.publicButton.bind(DirectGuiGlobals.EXIT, self.__exitPublic) + pos = self.gui.find('**/step_04_private_locator').getPos() + self.privateButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/privateButton_up'), + self.gui.find('**/privateButton_down'), + self.gui.find('**/privateButton_rollover'), + self.gui.find('**/privateButton_inactive')), text=TTLocalizer.PartyPlannerPrivate, text_pos=(pos[0], pos[2]), text_scale=TTLocalizer.PPprivateButton, command=self.__doTogglePublicPrivate) + self.privateButton.bind(DirectGuiGlobals.ENTER, self.__enterPrivate) + self.privateButton.bind(DirectGuiGlobals.EXIT, self.__exitPrivate) + self.checkAllButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/checkAllButton_up'), self.gui.find('**/checkAllButton_down'), self.gui.find('**/checkAllButton_rollover')), command=self.__doCheckAll) + self.uncheckAllButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/uncheckAllButton_up'), self.gui.find('**/uncheckAllButton_down'), self.gui.find('**/uncheckAllButton_rollover')), command=self.__doUncheckAll) + return page + + def __doCheckAll(self): + for friendBox in self.friendList['items']: + friendBox['indicatorValue'] = True + + def __doUncheckAll(self): + for friendBox in self.friendList['items']: + friendBox['indicatorValue'] = False + + def __enterPrivate(self, mouseEvent): + self.privateDescriptionLabel.unstash() + + def __exitPrivate(self, mouseEvent): + self.privateDescriptionLabel.stash() + + def __enterPublic(self, mouseEvent): + self.publicDescriptionLabel.unstash() + + def __exitPublic(self, mouseEvent): + self.publicDescriptionLabel.stash() + + def __doTogglePublicPrivate(self): + if self.isPrivate: + self.isPrivate = False + self.privateButton['state'] = DirectGuiGlobals.NORMAL + self.publicButton['state'] = DirectGuiGlobals.DISABLED + else: + self.isPrivate = True + self.privateButton['state'] = DirectGuiGlobals.DISABLED + self.publicButton['state'] = DirectGuiGlobals.NORMAL + + def _createPartyEditorPage(self): + page = DirectFrame(self.frame) + page.setName('PartyPlannerEditorPage') + self.LayoutTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerEditorTitle, pos=self.gui.find('**/title_locator').getPos() + Point3(0.0, 0.0, 0.075), scale=self.titleScale) + self.costLabel = DirectLabel(parent=page, pos=(-0.74, 0.0, 0.17), relief=None, text=TTLocalizer.PartyPlannerTotalCost % 0, text_align=TextNode.ACenter, scale=TTLocalizer.PPcostLabel, textMayChange=True) + self.partyGridBackground = DirectFrame(parent=page, relief=None, geom=self.gui.find('**/partyGrid_flat')) + self.partyGroundsLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerPartyGrounds, text_font=ToontownGlobals.getSignFont(), text_fg=VBase4(1.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.PPpartyGroundsLabel, pos=self.gui.find('**/step_05_partyGrounds_text_locator').getPos(), scale=0.1) + self.activityBackground = DirectFrame(parent=page, relief=None, geom=self.gui.find('**/activitiesDecorations_flat1'), pos=(0.0, 0.0, 0.04)) + pos = self.gui.find('**/step_05_instructions_locator').getPos() + self.instructionLabel = DirectLabel(parent=page, relief=None, text=' ', text_pos=(pos[0], pos[2]), text_scale=TTLocalizer.PPinstructionLabel, textMayChange=True, geom=self.gui.find('**/instructions_flat')) + self.elementTitleLabel = DirectLabel(parent=page, relief=None, text=' ', pos=self.gui.find('**/step_05_activitiesName_text_locator').getPos() + Point3(0.0, 0.0, 0.04), text_scale=TTLocalizer.PPelementTitleLabel, textMayChange=True) + self.elementPriceNode = TextNode('ElementPrice') + self.elementPriceNode.setAlign(TextNode.ALeft) + self.elementPriceNode.setTextColor(0.0, 0.0, 0.0, 1.0) + self.elementPriceNode.setFont(ToontownGlobals.getToonFont()) + self.elementPrice = page.attachNewNode(self.elementPriceNode) + self.elementPrice.setScale(TTLocalizer.PPelementPriceNode) + self.elementPrice.setPos(self.gui.find('**/step_05_activityPrice_text_locator').getPos() + Point3(-0.02, 0.0, 0.04)) + self.elementDescriptionNode = TextNode('ElementDescription') + self.elementDescriptionNode.setAlign(TextNode.ACenter) + self.elementDescriptionNode.setWordwrap(8) + self.elementDescriptionNode.setFont(ToontownGlobals.getToonFont()) + self.elementDescriptionNode.setTextColor(0.0, 0.0, 0.0, 1.0) + self.elementDescription = page.attachNewNode(self.elementDescriptionNode) + self.elementDescription.setScale(TTLocalizer.PPelementDescription) + self.elementDescription.setPos(self.gui.find('**/step_05_activityDescription_text_locator').getPos() + Point3(0.0, 0.0, 0.04)) + self.totalMoney = base.localAvatar.getTotalMoney() + catalogGui = loader.loadModel('phase_5.5/models/gui/catalog_gui') + self.beanBank = DirectLabel(parent=page, relief=None, text=str(self.totalMoney), text_align=TextNode.ARight, text_scale=0.075, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0.495, -0.53), text_font=ToontownGlobals.getSignFont(), textMayChange=True, image=catalogGui.find('**/bean_bank'), image_scale=(0.65, 0.65, 0.65), scale=0.9, pos=(-0.75, 0.0, 0.6)) + catalogGui.removeNode() + del catalogGui + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.accept(localAvatar.uniqueName('bankMoneyChange'), self.__moneyChange) + self.partyEditor = PartyEditor(self, page) + self.partyEditor.request('Hidden') + pos = self.gui.find('**/step_05_add_text_locator').getPos() + self.elementBuyButton = DirectButton(parent=page, relief=None, text=TTLocalizer.PartyPlannerBuy, text_pos=(pos[0], pos[2]), text_scale=TTLocalizer.PPelementBuyButton, geom=(self.gui.find('**/add_up'), self.gui.find('**/add_down'), self.gui.find('**/add_rollover')), geom3_color=VBase4(0.5, 0.5, 0.5, 1.0), textMayChange=True, pos=(0.0, 0.0, 0.04), command=self.partyEditor.buyCurrentElement) + self.okWithPartyGroundsLayoutEvent = 'okWithPartyGroundsLayoutEvent' + self.accept(self.okWithPartyGroundsLayoutEvent, self.okWithPartyGroundsLayout) + self.okWithGroundsGui = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('PartyEditorOkGui'), doneEvent=self.okWithPartyGroundsLayoutEvent, message=TTLocalizer.PartyPlannerOkWithGroundsLayout, style=TTDialog.YesNo, okButtonText=OTPLocalizer.DialogYes, cancelButtonText=OTPLocalizer.DialogNo) + self.okWithGroundsGui.doneStatus = '' + self.okWithGroundsGui.hide() + return page + + def okWithPartyGroundsLayout(self): + self.okWithGroundsGui.hide() + if self.okWithGroundsGui.doneStatus == 'ok': + self.__nextItem() + + def setNextButtonState(self, enabled): + if enabled: + self.nextButton['state'] = DirectGuiGlobals.NORMAL + self.nextButton.show() + else: + self.nextButton['state'] = DirectGuiGlobals.DISABLED + self.nextButton.hide() + + def _createInvitationPage(self): + self.__handleHolidays() + page = DirectFrame(self.frame) + page.setName('PartyPlannerInvitationPage') + self.invitationTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerConfirmTitle, textMayChange=True, pos=self.gui.find('**/title_locator').getPos(), scale=self.titleScale) + self.invitationBackground = DirectFrame(parent=page, relief=None, geom=self.gui.find('**/invitationBackground')) + self.inviteVisual = InviteVisual(page) + self.selectedInviteThemeLabel = DirectLabel(parent=page, relief=None, pos=self.gui.find('**/step_06_theme_locator').getPos(), text='', text_scale=0.06, textMayChange=True) + self.nextThemeButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/topNext_button/nextButton_up'), self.gui.find('**/topNext_button/nextButton_down'), self.gui.find('**/topNext_button/nextButton_rollover')), command=self.__nextTheme) + self.prevThemeButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/topPrevious_button/previousButton_up'), self.gui.find('**/topPrevious_button/previousButton_down'), self.gui.find('**/topPrevious_button/previousButton_rollover')), command=self.__prevTheme) + pos = self.gui.find('**/step_06_sendInvitation_locator').getPos() + self.inviteButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/send_up'), self.gui.find('**/send_down'), self.gui.find('**/send_rollover')), text=TTLocalizer.PartyPlannerInviteButton, textMayChange=True, text_scale=0.05, text_pos=(pos[0], pos[2]), command=self.__handleComplete) + return page + + def __handleHolidays(self): + self.inviteThemes = range(len(PartyGlobals.InviteTheme)) + if not base.cr.newsManager.isHolidayRunning(ToontownGlobals.VALENTOONS_DAY): + self.inviteThemes.remove(PartyGlobals.InviteTheme.Valentoons) + if not base.cr.newsManager.isHolidayRunning(ToontownGlobals.VICTORY_PARTY_HOLIDAY): + self.inviteThemes.remove(PartyGlobals.InviteTheme.VictoryParty) + if not base.cr.newsManager.isHolidayRunning(ToontownGlobals.CHRISTMAS): + self.inviteThemes.remove(PartyGlobals.InviteTheme.Winter) + + def _createFarewellPage(self): + page = DirectFrame(self.frame) + page.setName('PartyPlannerFarewellPage') + self.confirmTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerConfirmationAllOkTitle, textMayChange=True, pos=self.gui.find('**/title_locator').getPos(), scale=self.titleScale) + pos = self.gui.find('**/step_07_close_text_locator').getPos() + self.closePlannerButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/close_up'), self.gui.find('**/close_down'), self.gui.find('**/close_rollover')), text=TTLocalizer.PartyPlannerClosePlanner, text_scale=0.055, text_pos=(pos[0], pos[2]), command=self.__acceptExit) + return page + + def close(self): + self.ignore('addPartyResponseReceived') + self.ignore(localAvatar.uniqueName('moneyChange')) + self.ignore(localAvatar.uniqueName('bankMoneyChange')) + self.timeInputHourUpButton.destroy() + self.timeInputHourDownButton.destroy() + self.timeInputMinuteUpButton.destroy() + self.timeInputMinuteDownButton.destroy() + self.timeInputAmPmUpButton.destroy() + self.timeInputAmPmDownButton.destroy() + self.privateButton.destroy() + self.publicButton.destroy() + self.makePartyNowButton.destroy() + self.checkAllButton.destroy() + self.uncheckAllButton.destroy() + self.elementBuyButton.destroy() + self.nextThemeButton.destroy() + self.prevThemeButton.destroy() + self.inviteButton.destroy() + self.closePlannerButton.destroy() + self.ignore(self.okWithPartyGroundsLayoutEvent) + if hasattr(self, 'okWithGroundsGui'): + self.okWithGroundsGui.cleanup() + del self.okWithGroundsGui + if hasattr(self, 'frame') and not self.frame.isEmpty(): + messenger.send(self.doneEvent) + self.hide() + self.cleanup() + self.friendList.removeAndDestroyAllItems() + self.friendList.destroy() + self.calendarGuiMonth.destroy() + self.frame.destroy() + self.partyPlannerHead.delete() + self.partyPlannerHead.removeNode() + self.clearNametag() + self.partyEditor.request('Cleanup') + self.partyEditor = None + self.destroy() + del self + return + + def __handleComplete(self): + self.inviteButton['state'] = DirectGuiGlobals.DISABLED + self.prevButton['state'] = DirectGuiGlobals.DISABLED + endTime = self.partyTime + self.partyDuration + hostId = base.localAvatar.doId + self.partyActivities = self.partyEditor.partyEditorGrid.getActivitiesOnGrid() + decorations = self.partyEditor.partyEditorGrid.getDecorationsOnGrid() + invitees = self.getInvitees() + self.accept('addPartyResponseReceived', self.processAddPartyResponse) + base.cr.partyManager.sendAddParty(hostId, self.partyTime.strftime('%Y-%m-%d %H:%M:%S'), endTime.strftime('%Y-%m-%d %H:%M:%S'), self.isPrivate, self.currentInvitationTheme, self.partyActivities, decorations, invitees) + + def getInvitees(self): + invitees = [] + for friendBox in self.friendList['items']: + if friendBox['indicatorValue']: + invitees.append(friendBox.getPythonTag('id')) + + return invitees + + def processAddPartyResponse(self, hostId, errorCode): + PartyPlanner.notify.debug('processAddPartyResponse : hostId=%d errorCode=%s' % (hostId, PartyGlobals.AddPartyErrorCode.getString(errorCode))) + goingBackAllowed = False + if errorCode == PartyGlobals.AddPartyErrorCode.AllOk: + goingBackAllowed = False + self.confirmTitleLabel['text'] = TTLocalizer.PartyPlannerConfirmationAllOkTitle + if self.noFriends or len(self.getInvitees()) == 0: + confirmRecapText = TTLocalizer.PartyPlannerConfirmationAllOkTextNoFriends + else: + confirmRecapText = TTLocalizer.PartyPlannerConfirmationAllOkText + elif errorCode == PartyGlobals.AddPartyErrorCode.ValidationError: + self.confirmTitleLabel['text'] = TTLocalizer.PartyPlannerConfirmationErrorTitle + confirmRecapText = TTLocalizer.PartyPlannerConfirmationValidationErrorText + elif errorCode == PartyGlobals.AddPartyErrorCode.DatabaseError: + self.confirmTitleLabel['text'] = TTLocalizer.PartyPlannerConfirmationErrorTitle + confirmRecapText = TTLocalizer.PartyPlannerConfirmationDatabaseErrorText + elif errorCode == PartyGlobals.AddPartyErrorCode.TooManyHostedParties: + goingBackAllowed = False + self.confirmTitleLabel['text'] = TTLocalizer.PartyPlannerConfirmationErrorTitle + confirmRecapText = TTLocalizer.PartyPlannerConfirmationTooManyText + self.nametagGroup.setChat(confirmRecapText, CFSpeech) + self.request('Farewell', goingBackAllowed) + + def __acceptExit(self): + PartyPlanner.notify.debug('__acceptExit') + if hasattr(self, 'frame'): + self.hide() + messenger.send(self.doneEvent) + + def __nextItem(self): + messenger.send('wakeup') + if self.state == 'PartyEditor' and self.okWithGroundsGui.doneStatus != 'ok': + self.okWithGroundsGui.show() + return + if self.state == 'PartyEditor' and self.noFriends: + self.request('Date') + self.selectedCalendarGuiDay = None + self.calendarGuiMonth.clearSelectedDay() + return + if self.state == 'Guests': + self.selectedCalendarGuiDay = None + self.calendarGuiMonth.clearSelectedDay() + if self.state == 'Time': + if self.partyTime < base.cr.toontownTimeManager.getCurServerDateTime(): + self.okChooseFutureTimeEvent = 'okChooseFutureTimeEvent' + self.acceptOnce(self.okChooseFutureTimeEvent, self.okChooseFutureTime) + self.chooseFutureTimeDialog = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('chooseFutureTimeDialog'), doneEvent=self.okChooseFutureTimeEvent, message=TTLocalizer.PartyPlannerChooseFutureTime, style=TTDialog.Acknowledge) + self.chooseFutureTimeDialog.show() + return + self.requestNext() + return + + def okChooseFutureTime(self): + if hasattr(self, 'chooseFutureTimeDialog'): + self.chooseFutureTimeDialog.cleanup() + del self.chooseFutureTimeDialog + if hasattr(self, 'okChooseFutureTimeEvent'): + self.ignore(self.okChooseFutureTimeEvent) + + def __prevItem(self): + messenger.send('wakeup') + if self.state == 'Date' and self.noFriends: + self.request('PartyEditor') + return + if self.state == 'Invitation' and self.selectedCalendarGuiDay is None: + self.request('Guests') + return + self.requestPrev() + return + + def __moneyChange(self, newMoney): + if hasattr(self, 'totalMoney'): + self.totalMoney = base.localAvatar.getTotalMoney() + if hasattr(self, 'beanBank'): + self.beanBank['text'] = str(int(self.totalMoney)) diff --git a/toontown/parties/PartyReplyInfo.py b/toontown/parties/PartyReplyInfo.py new file mode 100755 index 00000000..c30355d8 --- /dev/null +++ b/toontown/parties/PartyReplyInfo.py @@ -0,0 +1,23 @@ + + +class SingleReply: + + def __init__(self, inviteeId, status): + self.inviteeId = inviteeId + self.status = status + + +class PartyReplyInfoBase: + + def __init__(self, partyId, partyReplies): + self.partyId = partyId + self.replies = [] + for oneReply in partyReplies: + self.replies.append(SingleReply(*oneReply)) + + def __str__(self): + string = 'partyId=%d ' % self.partyId + for reply in self.replies: + string += '(%d:%d) ' % (reply.inviteeId, reply.status) + + return string diff --git a/toontown/parties/PartyUtils.py b/toontown/parties/PartyUtils.py new file mode 100755 index 00000000..ad9331ab --- /dev/null +++ b/toontown/parties/PartyUtils.py @@ -0,0 +1,207 @@ +import math +import time +import datetime +from direct.directnotify import DirectNotifyGlobal +from direct.interval.LerpInterval import LerpFunc +from pandac.PandaModules import Vec3 +from toontown.toonbase import TTLocalizer +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.parties import PartyGlobals +notify = DirectNotifyGlobal.directNotify.newCategory('PartyUtils') + +def getNewToontownTimer(): + timer = ToontownTimer() + timer.hide() + timer.posInTopRightCorner() + timer.setColor(1, 1, 1, 0.75) + return timer + + +def getPartyActivityIcon(activityIconsModel, activityName): + activityIconsDict = {'PartyValentineDance': 'tt_t_ara_pty_iconDanceFloorValentine', + 'PartyValentineDance20': 'tt_t_ara_pty_iconDanceFloorValentine', + 'PartyValentineJukebox': 'tt_t_ara_pty_iconJukeboxValentine', + 'PartyValentineJukebox40': 'tt_t_ara_pty_iconJukeboxValentine', + 'PartyValentineTrampoline': 'tt_t_ara_pty_iconTrampolineValentine'} + iconName = activityIconsDict.get(activityName) + if iconName: + icon = activityIconsModel.find('**/%s' % iconName) + else: + icon = activityIconsModel.find('**/%sIcon' % activityName) + if icon.isEmpty(): + icon = activityIconsModel.find('**/PartyClockIcon') + notify.warning("Couldn't find %sIcon in %s, using PartyClockIcon" % (activityName, activityIconsModel.getName())) + return icon + + +def arcPosInterval(duration, object, pos, arcHeight, other): + startPos = object.getPos() + endPos = object.getParent().getRelativePoint(other, pos) + startX = startPos.getX() + startY = startPos.getY() + startZ = startPos.getZ() + dx = endPos.getX() - startPos.getX() + dy = endPos.getY() - startPos.getY() + dz = endPos.getZ() - startPos.getZ() + + def setArcPos(t): + newX = startX + dx * t + newY = startY + dy * t + newZ = startZ + dz * t + arcHeight * (-(2.0 * t - 1.0) ** 2 + 1.0) + object.setPos(newX, newY, newZ) + + return LerpFunc(setArcPos, duration=duration) + + +def formatDate(year, month, day): + monthString = TTLocalizer.DateOfBirthEntryMonths[month - 1] + return TTLocalizer.PartyDateFormat % {'mm': monthString, + 'dd': day, + 'yyyy': year} + + +def truncateTextOfLabelBasedOnWidth(directGuiObject, textToTruncate, maxWidth): + text0 = directGuiObject.component('text0') + tempNode = text0.textNode + currentText = textToTruncate[:] + scale = text0.getScale()[0] + width = tempNode.calcWidth(currentText) * scale + while width > maxWidth: + currentText = currentText[:-1] + width = tempNode.calcWidth(currentText) * scale + + directGuiObject['text'] = currentText + if directGuiObject['text'] != textToTruncate: + directGuiObject['text'] = '%s...' % directGuiObject['text'] + + +def truncateTextOfLabelBasedOnMaxLetters(directGuiObject, textToTruncate, maxLetters): + curStr = directGuiObject['text'] + if maxLetters < len(curStr): + curStr = curStr[:maxLetters] + curStr += '...' + directGuiObject['text'] = curStr + + +def scaleTextOfGuiObjectBasedOnWidth(directGuiObject, textToScale, maxWidth): + width = directGuiObject.getWidth() + scale = 0.01 + while width > maxWidth: + directGuiObject['text_scale'] = scale + directGuiObject.resetFrameSize() + width = directGuiObject.getWidth() + scale += 0.005 + + +def formatTime(hour, minute): + meridiemString = TTLocalizer.PartyTimeFormatMeridiemAM + if hour == 0: + hour = 12 + elif hour > 11: + meridiemString = TTLocalizer.PartyTimeFormatMeridiemPM + if hour > 12: + hour -= 12 + return TTLocalizer.PartyTimeFormat % (hour, minute, meridiemString) + + +SecondsInOneDay = 60 * 60 * 24 + +def getTimeDeltaInSeconds(td): + result = td.days * SecondsInOneDay + td.seconds + td.microseconds / 1000000.0 + return result + + +def formatDateTime(dateTimeToShow, inLocalTime = False): + if inLocalTime: + curServerTime = base.cr.toontownTimeManager.getCurServerDateTime() + ltime = time.localtime() + localTime = datetime.datetime(year=ltime.tm_year, month=ltime.tm_mon, day=ltime.tm_mday, hour=ltime.tm_hour, minute=ltime.tm_min, second=ltime.tm_sec) + naiveServerTime = curServerTime.replace(tzinfo=None) + newTimeDelta = localTime - naiveServerTime + localDifference = getTimeDeltaInSeconds(newTimeDelta) + dateTimeToShow = dateTimeToShow + datetime.timedelta(seconds=localDifference) + return '%s %s' % (formatDate(dateTimeToShow.year, dateTimeToShow.month, dateTimeToShow.day), formatTime(dateTimeToShow.hour, dateTimeToShow.minute)) + else: + return '%s %s' % (formatDate(dateTimeToShow.year, dateTimeToShow.month, dateTimeToShow.day), formatTime(dateTimeToShow.hour, dateTimeToShow.minute)) + return + + +def convertDistanceToPartyGrid(d, index): + return int((d - PartyGlobals.PartyGridToPandaOffset[index]) / PartyGlobals.PartyGridUnitLength[index]) + + +def convertDistanceFromPartyGrid(d, index): + return d * PartyGlobals.PartyGridUnitLength[index] + PartyGlobals.PartyGridToPandaOffset[index] + PartyGlobals.PartyGridUnitLength[index] / 2.0 + + +def convertDegreesToPartyGrid(h): + while h < 0.0: + h = h + 360.0 + + h = h % 360.0 + return int(h / PartyGlobals.PartyGridHeadingConverter) + + +def convertDegreesFromPartyGrid(h): + return h * PartyGlobals.PartyGridHeadingConverter + + +def getCenterPosFromGridSize(x, y, gridsize): + if gridsize[0] % 2 == 0: + xMod = PartyGlobals.PartyGridUnitLength[0] / 2.0 + else: + xMod = 0 + if gridsize[1] % 2 == 0: + yMod = PartyGlobals.PartyGridUnitLength[1] / 2.0 + else: + yMod = 0 + return (x + xMod, y + yMod) + + +def toRadians(angle): + return angle * math.pi / 180.0 + + +def toDegrees(angle): + return angle * 180.0 / math.pi + + +def calcVelocity(rotation, angle, initialVelocity = 1.0): + horizVel = initialVelocity * math.cos(angle) + xVel = horizVel * -math.sin(rotation) + yVel = horizVel * math.cos(rotation) + zVel = initialVelocity * math.sin(angle) + return Vec3(xVel, yVel, zVel) + + +class LineSegment: + + def __init__(self, pt1, pt2): + self.pt1 = pt1 + self.pt2 = pt2 + + def isIntersecting(self, line, compare = None): + x1 = self.pt1.getX() + x2 = self.pt2.getX() + x3 = line.pt1.getX() + x4 = line.pt2.getX() + y1 = self.pt1.getY() + y2 = self.pt2.getY() + y3 = line.pt1.getY() + y4 = line.pt2.getY() + top1 = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3) + top2 = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3) + bot = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1) + if bot == 0.0: + return False + u1 = top1 / bot + u2 = top2 / bot + if compare is None: + return 0 <= u1 and u1 <= 1 and 0 <= u2 and u2 <= 1 + elif compare == 'segment-ray': + return 0 <= u1 and u1 <= 1 and 0 <= u2 + elif compare == 'ray-ray': + return 0 <= u1 and 0 <= u2 + elif compare == 'ray-segment': + return 0 <= u1 and 0 <= u2 and u2 <= 1 + return diff --git a/toontown/parties/PublicPartyGui.py b/toontown/parties/PublicPartyGui.py new file mode 100755 index 00000000..f5ba382f --- /dev/null +++ b/toontown/parties/PublicPartyGui.py @@ -0,0 +1,251 @@ +from pandac.PandaModules import Vec3, Vec4, Point3, TextNode, VBase4 +from direct.gui.DirectGui import DGG, DirectFrame, DirectButton, DirectLabel, DirectScrolledList, DirectCheckButton +from direct.gui import DirectGuiGlobals +from direct.showbase import DirectObject +from direct.showbase import PythonUtil +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.parties import PartyGlobals +from toontown.parties import PartyUtils + +class PublicPartyGui(DirectFrame): + notify = directNotify.newCategory('PublicPartyGui') + + def __init__(self, doneEvent): + DirectFrame.__init__(self) + self.doneEvent = doneEvent + self.gui = loader.loadModel('phase_4/models/parties/publicPartyGUI') + self.setPos(0.1, 0.0, 0.1) + self.doneStatus = None + self.activityIconsModel = loader.loadModel('phase_4/models/parties/eventSignIcons') + self.normalFrameColor = Vec4(130 / 255.0, 174 / 255.0, 249 / 255.0, 1.0) + self.selectedFrameColor = Vec4(1.0, 1.0, 0.0, 1.0) + self.load() + self.gui.removeNode() + self.accept('stoppedAsleep', self._close) + return + + def load(self): + for backgroundName in ['background', 'parties_background', 'activities_background']: + background = DirectFrame(parent=self, geom=self.gui.find('**/%s' % backgroundName), relief=None) + + self.titleLabel = DirectLabel(parent=self, relief=None, text=TTLocalizer.PartyGateTitle, pos=self.gui.find('**/title_locator').getPos(), scale=0.1) + self.partyList, self.partyListLabel = self.createPartyListAndLabel('parties', 14) + self.activityList, self.activityListLabel = self.createListAndLabel('activities', 1) + pos = self.gui.find('**/startText_locator').getPos() + self.partyStartButton = DirectButton(parent=self, relief=None, text=TTLocalizer.PartyGateGoToParty, text_align=TextNode.ACenter, text_scale=TTLocalizer.PPGpartyStartButton, text_pos=(pos[0], pos[2]), geom=(self.gui.find('**/startButton_up'), + self.gui.find('**/startButton_down'), + self.gui.find('**/startButton_rollover'), + self.gui.find('**/startButton_inactive')), command=self._startParty) + self.closeButton = DirectButton(parent=self, relief=None, geom=(self.gui.find('**/cancelButton_up'), self.gui.find('**/cancelButton_down'), self.gui.find('**/cancelButton_rollover')), command=self._close) + instructionPos = (0, -0.9) + if not self.gui.find('**/helpText_locator').isEmpty(): + tempPos = self.gui.find('**/helpText_locator').getPos() + instructionPos = (tempPos.getX(), tempPos.getZ()) + self.instructionsLabel = DirectLabel(parent=self, relief=None, text=TTLocalizer.PartyGateInstructions, text_align=TextNode.ACenter, text_scale=TTLocalizer.PPGinstructionsLabel, text_pos=instructionPos) + return + + def createListAndLabel(self, typeString, numItems): + list = DirectScrolledList(parent=self, relief=None, incButton_image=(self.gui.find('**/%sButtonDown_up' % typeString), + self.gui.find('**/%sButtonDown_down' % typeString), + self.gui.find('**/%sButtonDown_rollover' % typeString), + self.gui.find('**/%sButtonDown_inactive' % typeString)), incButton_relief=None, decButton_image=(self.gui.find('**/%sButtonUp_up' % typeString), + self.gui.find('**/%sButtonUp_down' % typeString), + self.gui.find('**/%sButtonUp_rollover' % typeString), + self.gui.find('**/%sButtonUp_inactive' % typeString)), decButton_relief=None, itemFrame_pos=self.gui.find('**/%s_locator' % typeString).getPos(), itemFrame_relief=None, numItemsVisible=numItems, forceHeight=0.055) + strings = {'activities': TTLocalizer.EventsPageHostingTabActivityListTitle, + 'parties': TTLocalizer.PartyGatePartiesListTitle} + label = DirectLabel(parent=self, relief=None, text=strings[typeString], text_scale=0.06, pos=self.gui.find('**/%sText_locator' % typeString).getPos()) + return (list, label) + + def refresh(self, partyInfoTupleList): + PublicPartyGui.notify.debug('refresh : partyInfoTupleList = %s' % partyInfoTupleList) + self.selectedItem = None + self.partyList.removeAndDestroyAllItems() + self.activityList.removeAndDestroyAllItems() + self.partyStartButton['state'] = DirectGuiGlobals.DISABLED + sortedList = partyInfoTupleList[:] + + def cmp(left, right): + if left[2] < right[2]: + return -1 + elif left[2] == right[2]: + if len(left[4]) < len(right[4]): + return -1 + elif len(left[4]) == len(right[4]): + return 0 + else: + return 1 + else: + return 1 + + sortedList.sort(cmp, reverse=True) + indexToCut = -1 + for index, partyTuple in enumerate(sortedList): + numberOfGuests = partyTuple[2] + if numberOfGuests < PartyGlobals.MaxToonsAtAParty: + indexToCut = index + break + + if indexToCut > 0: + sortedList = sortedList[indexToCut:] + sortedList[:indexToCut] + for index, partyTuple in enumerate(sortedList): + shardId = partyTuple[0] + zoneId = partyTuple[1] + numberOfGuests = partyTuple[2] + hostName = partyTuple[3] + activityIds = partyTuple[4] + minLeft = partyTuple[5] + item = DirectButton(relief=DGG.RIDGE, borderWidth=(0.01, 0.01), frameSize=(-0.01, + 0.45, + -0.015, + 0.04), frameColor=self.normalFrameColor, text=hostName, text_align=TextNode.ALeft, text_bg=Vec4(0.0, 0.0, 0.0, 0.0), text_scale=0.045, command=self.partyClicked) + otherInfoWidth = 0.08 + numActivities = len(activityIds) + PartyUtils.truncateTextOfLabelBasedOnWidth(item, hostName, PartyGlobals.EventsPageGuestNameMaxWidth) + num = DirectLabel(relief=DGG.RIDGE, borderWidth=(0.01, 0.01), frameSize=(0.0, + otherInfoWidth, + -0.015, + 0.04), frameColor=self.normalFrameColor, text='%d' % numberOfGuests, text_align=TextNode.ALeft, text_scale=0.045, text_pos=(0.01, 0, 0), pos=(0.45, 0.0, 0.0)) + num.reparentTo(item) + item.numLabel = num + actLabelPos = num.getPos() + actLabelPos.setX(actLabelPos.getX() + otherInfoWidth) + actLabel = DirectLabel(relief=DGG.RIDGE, borderWidth=(0.01, 0.01), frameSize=(0.0, + otherInfoWidth, + -0.015, + 0.04), frameColor=self.normalFrameColor, text='%d' % numActivities, text_align=TextNode.ALeft, text_scale=0.045, text_pos=(0.01, 0, 0), pos=actLabelPos) + actLabel.reparentTo(item) + item.actLabel = actLabel + minLabelPos = actLabel.getPos() + minLabelPos.setX(minLabelPos.getX() + otherInfoWidth) + minLabel = DirectLabel(relief=DGG.RIDGE, borderWidth=(0.01, 0.01), frameSize=(0.0, + otherInfoWidth, + -0.015, + 0.04), frameColor=self.normalFrameColor, text='%d' % minLeft, text_align=TextNode.ALeft, text_scale=0.045, text_pos=(0.01, 0, 0), pos=minLabelPos) + minLabel.reparentTo(item) + item.minLabel = minLabel + item['extraArgs'] = [item] + item.setPythonTag('shardId', shardId) + item.setPythonTag('zoneId', zoneId) + item.setPythonTag('activityIds', activityIds) + self.partyList.addItem(item) + + return + + def partyClicked(self, item): + self.partyStartButton['state'] = DirectGuiGlobals.NORMAL + if self.selectedItem is not None: + self.selectedItem['state'] = DirectGuiGlobals.NORMAL + self.selectedItem['frameColor'] = self.normalFrameColor + numLabel = self.selectedItem.numLabel + if not numLabel.isEmpty(): + numLabel['frameColor'] = self.normalFrameColor + actLabel = self.selectedItem.actLabel + if not actLabel.isEmpty(): + actLabel['frameColor'] = self.normalFrameColor + minLabel = self.selectedItem.minLabel + if not minLabel.isEmpty(): + minLabel['frameColor'] = self.normalFrameColor + self.selectedItem = item + self.selectedItem['state'] = DirectGuiGlobals.DISABLED + self.selectedItem['frameColor'] = self.selectedFrameColor + numLabel = self.selectedItem.numLabel + if not numLabel.isEmpty(): + numLabel['frameColor'] = self.selectedFrameColor + actLabel = self.selectedItem.actLabel + if not actLabel.isEmpty(): + actLabel['frameColor'] = self.selectedFrameColor + minLabel = self.selectedItem.minLabel + if not minLabel.isEmpty(): + minLabel['frameColor'] = self.selectedFrameColor + self.fillActivityList(item.getPythonTag('activityIds')) + return + + def fillActivityList(self, activityIds): + self.activityList.removeAndDestroyAllItems() + sortedList = activityIds[:] + sortedList.sort() + lastActivityId = -1 + for activityId in sortedList: + if activityId == lastActivityId: + continue + lastActivityId = activityId + number = sortedList.count(activityId) + text = TTLocalizer.PartyActivityNameDict[activityId]['generic'] + if number > 1: + text += ' X %d' % number + item = DirectLabel(relief=None, text=text, text_align=TextNode.ACenter, text_scale=0.05, text_pos=(0.0, -0.15), geom_scale=0.3, geom_pos=Vec3(0.0, 0.0, 0.07), geom=PartyUtils.getPartyActivityIcon(self.activityIconsModel, PartyGlobals.ActivityIds.getString(activityId))) + self.activityList.addItem(item) + + return + + def _startParty(self): + if self.selectedItem is None: + self.partyStartButton['state'] = DirectGuiGlobals.DISABLED + return + self.doneStatus = (self.selectedItem.getPythonTag('shardId'), self.selectedItem.getPythonTag('zoneId')) + messenger.send(self.doneEvent) + return + + def _close(self): + self.doneStatus = None + messenger.send(self.doneEvent) + return + + def destroy(self): + self.activityIconsModel.removeNode() + del self.activityIconsModel + self.partyList.removeAndDestroyAllItems() + try: + for item in self.partyList['items']: + item.actLabel = None + item.numLabel = None + item.minLabel = None + + except: + pass + + self.activityList.removeAndDestroyAllItems() + del self.partyList + del self.activityList + self.ignoreAll() + DirectFrame.destroy(self) + return + + def createPartyListAndLabel(self, typeString, numItems): + list = DirectScrolledList(parent=self, relief=None, incButton_image=(self.gui.find('**/%sButtonDown_up' % typeString), + self.gui.find('**/%sButtonDown_down' % typeString), + self.gui.find('**/%sButtonDown_rollover' % typeString), + self.gui.find('**/%sButtonDown_inactive' % typeString)), incButton_relief=None, decButton_image=(self.gui.find('**/%sButtonUp_up' % typeString), + self.gui.find('**/%sButtonUp_down' % typeString), + self.gui.find('**/%sButtonUp_rollover' % typeString), + self.gui.find('**/%sButtonUp_inactive' % typeString)), decButton_relief=None, itemFrame_pos=self.gui.find('**/%s_locator' % typeString).getPos(), itemFrame_relief=None, numItemsVisible=numItems, forceHeight=0.055) + strings = {'activities': TTLocalizer.EventsPageHostingTabActivityListTitle, + 'parties': TTLocalizer.PartyGatePartiesListTitle} + hostPos = self.gui.find('**/%sText_locator' % typeString).getPos() + label = DirectLabel(parent=self, text_align=TextNode.ALeft, relief=None, text=strings[typeString], text_scale=0.06, pos=hostPos) + curPos = label.getPos() + curPos.setX(curPos.getX() + 0.5) + if not self.gui.find('**/partiesText_locator1').isEmpty(): + curPos = self.gui.find('**/partiesText_locator1').getPos() + hpr = Point3(0, 0, -40) + toonsLabel = DirectLabel(parent=self, text_align=TextNode.ALeft, relief=None, text=TTLocalizer.PartyGatesPartiesListToons, text_scale=TTLocalizer.PPGtoonsLabel, pos=curPos, hpr=hpr) + curPos.setX(curPos.getX() + 0.1) + if not self.gui.find('**/partiesText_locator2').isEmpty(): + curPos = self.gui.find('**/partiesText_locator2').getPos() + activitiesLabel = DirectLabel(parent=self, text_align=TextNode.ALeft, relief=None, text=TTLocalizer.PartyGatesPartiesListActivities, text_scale=TTLocalizer.PPGactivitiesLabel, pos=curPos, hpr=hpr) + curPos.setX(curPos.getX() + 0.1) + if not self.gui.find('**/partiesText_locator3').isEmpty(): + curPos = self.gui.find('**/partiesText_locator3').getPos() + minLeftLabel = DirectLabel(parent=self, text_align=TextNode.ALeft, relief=None, text=TTLocalizer.PartyGatesPartiesListMinLeft, text_scale=TTLocalizer.PPGminLeftLabel, pos=curPos, hpr=hpr) + return (list, label) + + def stash(self): + base.setCellsAvailable(base.bottomCells, 1) + DirectFrame.stash(self) + + def unstash(self): + base.setCellsAvailable(base.bottomCells, 0) + DirectFrame.unstash(self) diff --git a/toontown/parties/ScrolledFriendList.py b/toontown/parties/ScrolledFriendList.py new file mode 100755 index 00000000..180504d5 --- /dev/null +++ b/toontown/parties/ScrolledFriendList.py @@ -0,0 +1,38 @@ +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel +from direct.gui.DirectGui import DirectScrolledList, DirectCheckButton +from direct.gui.DirectCheckBox import DirectCheckBox +from direct.gui import DirectGuiGlobals +from toontown.friends.FriendsListPanel import determineFriendName +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import Vec3, Vec4, PlaneNode, Plane, Point3, TextNode, VBase4, NodePath + +class ScrolledFriendList(DirectScrolledList): + + def __init__(self, parent, gui, clickCallback = None, makeItemsCheckBoxes = False): + self.makeItemsCheckBoxes = makeItemsCheckBoxes + self.clickCallback = clickCallback + self.parent = parent + self.gui = gui + self.scrollSpeed = 1 + DirectScrolledList.__init__(self, parent=parent, relief=None, incButton_image=(self.gui.find('**/inviteButtonDown_up'), self.gui.find('**/inviteButtonDown_down'), self.gui.find('**/inviteButtonDown_rollover')), incButton_relief=None, incButton_pos=(0.0, 0.0, -0.03), incButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), decButton_image=(self.gui.find('**/inviteButtonUp_up'), self.gui.find('**/inviteButtonUp_down'), self.gui.find('**/inviteButtonUp_rollover')), decButton_relief=None, decButton_pos=(0.0, 0.0, 0.02), decButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), itemFrame_relief=None, forceHeight=0.084, numItemsVisible=8, items=[], incButtonCallback=self.scrollButtonPressed, decButtonCallback=self.scrollButtonPressed, itemFrame_pos=(0.0, 0.0, -0.01)) + self.incButtonCallback = None + self.decButtonCallback = None + self.setForceHeight() + return + + def scrollButtonPressed(self): + pass + + def addFriend(self, id): + name = determineFriendName(id) + + if self.makeItemsCheckBoxes: + checkedImage = self.gui.find('**/inviteButtonChecked') + uncheckedImage = self.gui.find('**/inviteButtonUnchecked') + widget = DirectCheckButton(relief=None, scale=0.1, boxBorder=0.08, boxImage=(uncheckedImage, checkedImage, None), boxImageScale=10.0, boxRelief=None, text=name, text_align=TextNode.ALeft, text_scale=0.7, text_pos=(-3.7, -0.25), command=self.clickCallback, indicator_pos=(-4.8, 0.0, 0.0)) + widget['extraArgs'] = [widget] + else: + widget = DirectLabel(relief=None, text=name, text_align=TextNode.ALeft, text_pos=(-0.6, 0.0, 0.0), scale=0.055) + widget.setPythonTag('id', id) + self.addItem(widget) + return diff --git a/toontown/parties/ServerTimeGui.py b/toontown/parties/ServerTimeGui.py new file mode 100755 index 00000000..6adcc4b6 --- /dev/null +++ b/toontown/parties/ServerTimeGui.py @@ -0,0 +1,52 @@ +from pandac.PandaModules import TextNode +from direct.gui.DirectGui import DirectFrame, DirectLabel +from direct.interval.IntervalGlobal import Func, Sequence, Wait +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class ServerTimeGui(DirectFrame): + + def __init__(self, parent, pos = (0, 0, 0), hourCallback = None): + DirectFrame.__init__(self, parent=parent, pos=pos) + self.createGuiObjects() + self.hourCallback = hourCallback + self.lastHour = -1 + self.lastMinute = -1 + + def createGuiObjects(self): + textScale = 0.075 + timeFont = ToontownGlobals.getMinnieFont() + self.hourLabel = DirectLabel(parent=self, pos=(-0.015, 0, 0), relief=None, text='', text_scale=textScale, text_align=TextNode.ARight, text_font=timeFont) + self.colonLabel = DirectLabel(parent=self, relief=None, text=':', text_scale=textScale, text_align=TextNode.ACenter, text_font=timeFont) + self.minutesLabel = DirectLabel(relief=None, parent=self, pos=(0.015, 0, 0), text='', text_scale=textScale, text_align=TextNode.ALeft, text_font=timeFont) + self.amLabel = DirectLabel(relief=None, parent=self, pos=(0.14, 0, 0), text='', text_scale=textScale, text_align=TextNode.ALeft, text_font=timeFont) + self.ival = Sequence(Func(self.colonLabel.show), Wait(0.75), Func(self.colonLabel.hide), Wait(0.25), Func(self.updateTime)) + self.ival.loop() + return + + def destroy(self): + self.ival.finish() + self.ival = None + DirectFrame.destroy(self) + return + + def updateTime(self): + curServerDate = base.cr.toontownTimeManager.getCurServerDateTime() + if TTLocalizer.HourFormat == '24': + self.hour = '%H' + self.ampm = '' + else: + self.hour = '%I' + self.ampm = '%p' + if self.hourCallback is not None: + if curServerDate.hour != self.lastHour and self.lastHour != -1: + self.lastHour = curServerDate.hour + self.hourCallback(curServerDate.hour) + if not curServerDate.minute == self.lastMinute: + self.hourLabel['text'] = curServerDate.strftime(self.hour) + self.lastHour = curServerDate.hour + if self.hourLabel['text'][0] == '0': + self.hourLabel['text'] = self.hourLabel['text'][1:] + self.minutesLabel['text'] = curServerDate.strftime('%M') + self.amLabel['text'] = curServerDate.strftime(self.ampm) + return diff --git a/toontown/parties/SimpleMailBase.py b/toontown/parties/SimpleMailBase.py new file mode 100755 index 00000000..1b192815 --- /dev/null +++ b/toontown/parties/SimpleMailBase.py @@ -0,0 +1,18 @@ + + +class SimpleMailBase: + + def __init__(self, msgId, senderId, year, month, day, body): + self.msgId = msgId + self.senderId = senderId + self.year = year + self.month = month + self.day = day + self.body = body + + def __str__(self): + string = 'msgId=%d ' % self.msgId + string += 'senderId=%d ' % self.senderId + string += 'sent=%s-%s-%s ' % (self.year, self.month, self.day) + string += 'body=%s' % self.body + return string diff --git a/toontown/parties/StretchingArrow.py b/toontown/parties/StretchingArrow.py new file mode 100755 index 00000000..172da019 --- /dev/null +++ b/toontown/parties/StretchingArrow.py @@ -0,0 +1,94 @@ +import math +from direct.gui.DirectGui import DirectFrame +from pandac.PandaModules import Point3 + +class StretchingArrow(DirectFrame): + notify = directNotify.newCategory('StretchingArrow') + arrowMoving = 0 + arrowBegin = 1 + arrowComplete = 2 + body = None + head = None + + def __init__(self, parent, useColor = 'blue', autoload = True): + DirectFrame.__init__(self, parent) + self.useColor = useColor + self.endOffset = 1.5 + self.startOffset = 0.0 + self.shrinkRange = 7.0 + self.ratioDrawn = 0.0 + if autoload: + self.load() + self.stash() + + def load(self): + model = loader.loadModel('phase_13/models/parties/stretchingArrow') + model.setP(-90) + self.body = model.find('**/arrowBody_' + self.useColor) + self.body.wrtReparentTo(self) + self.head = model.find('**/arrowHead_' + self.useColor) + self.head.wrtReparentTo(self) + model.removeNode() + + def unload(self): + if self.body is not None: + self.body.removeNode() + self.body = None + if self.head is not None: + self.body.removeNode() + self.body = None + return + + def reset(self): + self.ratioDrawn = 0.0 + + def draw(self, fromPoint, toPoint, rotation = 0, animate = True): + arrowlength = 2.72 + if self.body is None or self.head is None: + return + actualDifference = fromPoint - toPoint + actualLength = actualDifference.length() + oldRatio = self.ratioDrawn + drawSpeed = 1.6 + drawSpeedMin = 0.6 + downTime = 1.0 + fadeOutTime = 0.5 + drawRate = max(drawSpeedMin, drawSpeed * actualLength / arrowlength) + self.ratioDrawn += globalClock.getDt() / drawRate + result = StretchingArrow.arrowMoving + if self.ratioDrawn >= 1.0: + result = StretchingArrow.arrowComplete + self.ratioDrawn = -downTime + if cmp(oldRatio, 0) != cmp(self.ratioDrawn, 0) and result != StretchingArrow.arrowComplete: + result = StretchingArrow.arrowBegin + if not animate: + self.ratioDrawn = 1.0 + normal = Point3(actualDifference.getX(), actualDifference.getY(), actualDifference.getZ()) + normal.normalize() + rotation = math.degrees(math.atan2(actualDifference.getY(), actualDifference.getX())) + endPoint = toPoint + normal * self.endOffset + startPoint = fromPoint - normal * self.startOffset + newlength = (endPoint - startPoint).length() / arrowlength + newScale = min(actualLength / self.shrinkRange, 1.0) + self.head.setScale(newScale) + ratio = max(0.0, self.ratioDrawn) + if ratio == 0.0: + ratio = 1.0 + newlength *= ratio + if actualLength < self.endOffset: + self.stash() + else: + self.unstash() + self.body.setPos(startPoint) + self.body.setH(rotation) + self.head.setH(rotation - 90) + self.body.setScale(newlength - 0.013 * newScale, newScale, newScale) + vec = startPoint - endPoint + vec *= ratio + self.head.setPos(startPoint - vec) + self.head.setZ(render, self.body.getZ(render) + 0.001) + if self.ratioDrawn < 0.0: + self.setAlphaScale(abs(self.ratioDrawn) - (downTime - fadeOutTime)) + else: + self.setAlphaScale(1.0) + return diff --git a/toontown/parties/TeamActivityGui.py b/toontown/parties/TeamActivityGui.py new file mode 100755 index 00000000..0e31bc36 --- /dev/null +++ b/toontown/parties/TeamActivityGui.py @@ -0,0 +1,137 @@ +from pandac.PandaModules import TextNode +from direct.gui.DirectButton import DirectButton +from direct.gui.OnscreenText import OnscreenText +from direct.task.Task import Task +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyUtils +from toontown.parties import PartyGlobals + +class TeamActivityGui: + COUNTDOWN_TASK_NAME = 'updateCountdownTask' + timer = None + statusText = None + countdownText = None + exitButton = None + switchButton = None + + def __init__(self, activity): + self.activity = activity + + def load(self): + buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + upButton = buttonModels.find('**//InventoryButtonUp') + downButton = buttonModels.find('**/InventoryButtonDown') + rolloverButton = buttonModels.find('**/InventoryButtonRollover') + self.exitButton = DirectButton(relief=None, text=TTLocalizer.PartyTeamActivityExitButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.15), text_scale=0.5, image=(upButton, downButton, rolloverButton), image_color=(1, 0, 0, 1), image_scale=(14.5, 1, 9), pos=(0, 0, 0.8), scale=0.15, command=self.handleExitButtonClick) + self.exitButton.hide() + if self.activity.toonsCanSwitchTeams(): + self.switchButton = DirectButton(relief=None, text=TTLocalizer.PartyTeamActivitySwitchTeamsButton, text_fg=(1, 1, 1, 1), text_pos=(0, 0.1), text_scale=0.5, image=(upButton, downButton, rolloverButton), image_color=(0, 1, 0, 1), image_scale=(15, 1, 15), pos=(0, 0, 0.5), scale=0.15, command=self.handleSwitchButtonClick) + self.switchButton.hide() + else: + self.switchButton = None + buttonModels.removeNode() + self.countdownText = OnscreenText(text='', pos=(0.0, -0.2), scale=PartyGlobals.TeamActivityTextScale * 1.2, fg=(1.0, 1.0, 0.65, 1.0), align=TextNode.ACenter, font=ToontownGlobals.getSignFont(), mayChange=True) + self.countdownText.hide() + self.statusText = OnscreenText(text='', pos=(0.0, 0.0), scale=PartyGlobals.TeamActivityTextScale, fg=PartyGlobals.TeamActivityStatusColor, align=TextNode.ACenter, font=ToontownGlobals.getSignFont(), mayChange=True) + self.statusText.hide() + self.timer = PartyUtils.getNewToontownTimer() + self.timer.hide() + return + + def unload(self): + self.hideWaitToStartCountdown() + if self.exitButton is not None: + self.exitButton.destroy() + self.exitButton = None + if self.switchButton is not None: + self.switchButton.destroy() + self.switchButton = None + if self.countdownText is not None: + self.countdownText.destroy() + self.countdownText.removeNode() + self.countdownText = None + if self.statusText is not None: + self.statusText.destroy() + self.statusText.removeNode() + self.statusText = None + if self.timer is not None: + self.timer.destroy() + del self.timer + return + + def showStatus(self, text): + self.statusText.setText(text) + self.statusText.show() + + def hideStatus(self): + self.statusText.hide() + + def enableExitButton(self): + self.exitButton.show() + + def disableExitButton(self): + self.exitButton.hide() + + def handleExitButtonClick(self): + self.disableExitButton() + self.disableSwitchButton() + self.activity.d_toonExitRequest() + + def enableSwitchButton(self): + self.switchButton.show() + + def disableSwitchButton(self): + if self.switchButton is not None: + self.switchButton.hide() + return + + def handleSwitchButtonClick(self): + self.disableSwitchButton() + self.disableExitButton() + self.activity.d_toonSwitchTeamRequest() + + def showWaitToStartCountdown(self, duration, waitToStartTimestamp, almostDoneCallback = None): + self._countdownAlmostDoneCallback = almostDoneCallback + currentTime = globalClock.getRealTime() + waitTimeElapsed = currentTime - waitToStartTimestamp + if duration - waitTimeElapsed > 1.0: + countdownTask = Task(self._updateCountdownTask) + countdownTask.duration = duration - waitTimeElapsed + self.countdownText.setText(str(int(countdownTask.duration))) + self.countdownText.show() + taskMgr.remove(TeamActivityGui.COUNTDOWN_TASK_NAME) + taskMgr.add(countdownTask, TeamActivityGui.COUNTDOWN_TASK_NAME) + + def hideWaitToStartCountdown(self): + taskMgr.remove(TeamActivityGui.COUNTDOWN_TASK_NAME) + self._countdownAlmostDoneCallback = None + if self.countdownText is not None: + self.countdownText.hide() + return + + def _updateCountdownTask(self, task): + countdownTime = int(task.duration - task.time) + seconds = str(countdownTime) + if self.countdownText['text'] != seconds: + self.countdownText['text'] = seconds + if countdownTime == 3 and self._countdownAlmostDoneCallback is not None: + self._countdownAlmostDoneCallback() + self._countdownAlmostDoneCallback = None + if task.time >= task.duration: + return Task.done + else: + return Task.cont + return + + def showTimer(self, duration): + self.timer.setTime(duration) + self.timer.countdown(duration, self._handleTimerExpired) + self.timer.show() + + def hideTimer(self): + self.timer.hide() + self.timer.stop() + + def _handleTimerExpired(self): + self.activity.handleGameTimerExpired() diff --git a/toontown/parties/ToontownTimeManager.py b/toontown/parties/ToontownTimeManager.py new file mode 100755 index 00000000..bcd49188 --- /dev/null +++ b/toontown/parties/ToontownTimeManager.py @@ -0,0 +1,47 @@ +from datetime import datetime, timedelta +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.distributed import DistributedObject +import time + +from toontown.parties.ToontownTimeZone import ToontownTimeZone, UTC + + +class ToontownTimeManager(DistributedObject.DistributedObject): + notify = directNotify.newCategory('ToontownTimeManager') + ClockFormat = '%I:%M:%S %p' + formatStr = '%Y-%m-%d %H:%M:%S' + + def __init__(self, serverTimeAtLogin=0, clientTimeAtLogin=0, + realTimeAtLogin=0): + self.serverTimeZone = ToontownTimeZone() + self.updateLoginTimes(serverTimeAtLogin, clientTimeAtLogin, + realTimeAtLogin) + + def updateLoginTimes(self, serverTimeAtLogin, clientTimeAtLogin, + realTimeAtLogin): + self.serverTimeAtLogin = serverTimeAtLogin + self.clientTimeAtLogin = clientTimeAtLogin + self.realTimeAtLogin = realTimeAtLogin + + self.serverDateTime = datetime.fromtimestamp( + self.serverTimeAtLogin, self.serverTimeZone) + + def getCurServerDateTime(self): + secondsPassed = globalClock.getRealTime() - self.realTimeAtLogin + dt = self.serverDateTime + timedelta(seconds=secondsPassed) + return dt.astimezone(self.serverTimeZone) + + def convertStrToToontownTime(self, dateStr): + try: + timeStruct = time.strptime(dateStr, self.formatStr) + return datetime.fromtimestamp(time.mktime(timeStruct), self.serverTimeZone) + except: + self.notify.warning('error parsing date string: "%s"' % dateStr) + + def convertUtcStrToToontownTime(self, dateStr): + try: + timeStruct = time.strptime(dateStr, self.formatStr) + dtUtc = datetime(timeStruct[:6], UTC) + return dtUtc.astimezone(self.serverTimeZone) + except: + self.notify.warning('error parsing date string: "%s"' % dateStr) diff --git a/toontown/parties/ToontownTimeZone.py b/toontown/parties/ToontownTimeZone.py new file mode 100755 index 00000000..13b19a9f --- /dev/null +++ b/toontown/parties/ToontownTimeZone.py @@ -0,0 +1,53 @@ +from datetime import datetime, timedelta, tzinfo + + +# In the U.S., DST starts at 2AM (standard time) on the first Sunday in April: +DST_START = datetime(1, 4, 1, 2) + +# ...and it ends at 2AM (DST time; 1AM standard time) on the last Sunday in +# October, which is the first Sunday on or after October 25th: +DST_END = datetime(1, 10, 25, 1) + + +def forwardToSunday(dt): + daysLeft = 6 - dt.weekday() + if daysLeft: + dt += timedelta(daysLeft) + return dt + + +class UTC(tzinfo): + def tzname(self, dt): + return 'UTC' + + def utcoffset(self, dt): + return timedelta(0) + + def dst(self, dt): + return timedelta(0) + + +class ToontownTimeZone(tzinfo): + def __init__(self): + timeZoneInfo = config.GetString('server-timezone', 'EST/EDT/-5') + self.stdName, self.dstName, self.stdOffset = timeZoneInfo.split('/') + self.stdOffset = int(self.stdOffset) + + def tzname(self, dt): + if self.dst(dt): + return self.dstName + else: + return self.stdName + + def utcoffset(self, dt): + return timedelta(hours=self.stdOffset) + self.dst(dt) + + def dst(self, dt): + # Find the first Sunday in April, and the last in October: + start = forwardToSunday(DST_START.replace(year=dt.year)) + end = forwardToSunday(DST_END.replace(year=dt.year)) + + if start <= dt.replace(tzinfo=None) < end: + return timedelta(hours=1) + else: + return timedelta(0) diff --git a/toontown/parties/WinterPartyCatchActivityToonSD.py b/toontown/parties/WinterPartyCatchActivityToonSD.py new file mode 100755 index 00000000..b50f0854 --- /dev/null +++ b/toontown/parties/WinterPartyCatchActivityToonSD.py @@ -0,0 +1,38 @@ +import PartyCatchActivityToonSD +from pandac.PandaModules import Vec4 +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import Sequence, Parallel, Wait, Func +from direct.interval.IntervalGlobal import LerpColorScaleInterval +from direct.interval.IntervalGlobal import WaitInterval, ActorInterval, FunctionInterval +from direct.fsm import ClassicFSM, State + +class WinterPartyCatchActivityToonSD(PartyCatchActivityToonSD.PartyCatchActivityToonSD): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyCatchActivityToonSD') + + def __init__(self, avId, activity): + WinterPartyCatchActivityToonSD.notify.debug('init : avId = %s, activity = %s ' % (avId, activity)) + PartyCatchActivityToonSD.PartyCatchActivityToonSD.__init__(self, avId, activity) + + def enterEatFruit(self, fruitModel, handNode): + self.notify.debug('enterEatFruit') + if self.isLocal: + self.activity.orthoWalk.start() + self.setAnimState('Catching', 1.0) + self.fruitModel = fruitModel + renderScale = fruitModel.getScale(render) + fruitModel.reparentTo(handNode) + fruitModel.setScale(render, renderScale) + fruitModel.setTransparency(1) + duration = self.toon.getDuration('catch-eatneutral') + self.eatIval = Sequence(Parallel(WaitInterval(duration), Sequence(LerpColorScaleInterval(fruitModel, duration / 2.0, Vec4(1.0, 1.0, 1.0, 0.0)))), Func(self.fsm.request, 'normal'), name=self.toon.uniqueName('eatingIval')) + self.eatIval.start() + + def exitEatFruit(self): + self.eatIval.pause() + del self.eatIval + self.fruitModel.reparentTo(hidden) + self.fruitModel.removeNode() + del self.fruitModel + self.setAnimState('off', 1.0) + if self.isLocal: + self.activity.orthoWalk.stop() diff --git a/toontown/parties/__init__.py b/toontown/parties/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/parties/activityFSMMixins.py b/toontown/parties/activityFSMMixins.py new file mode 100755 index 00000000..f9ce2866 --- /dev/null +++ b/toontown/parties/activityFSMMixins.py @@ -0,0 +1,198 @@ +from BaseActivityFSM import BaseActivityFSM + +class IdleMixin: + + def enterIdle(self, *args): + BaseActivityFSM.notify.info("enterIdle: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startIdle(*args) + else: + self.activity.startIdle() + + def filterIdle(self, request, args): + BaseActivityFSM.notify.debug("filterIdle( '%s', '%s' )" % (request, args)) + if request == 'Idle': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitIdle(self): + BaseActivityFSM.notify.debug("exitIdle: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishIdle() + + +class ActiveMixin: + + def enterActive(self, *args): + BaseActivityFSM.notify.info("enterActive: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startActive(*args) + else: + self.activity.startActive() + + def filterActive(self, request, args): + BaseActivityFSM.notify.debug("filterActive( '%s', '%s' )" % (request, args)) + if request == 'Active': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitActive(self): + BaseActivityFSM.notify.debug("exitActive: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishActive() + + +class DisabledMixin: + + def enterDisabled(self, *args): + BaseActivityFSM.notify.info("enterDisabled: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startDisabled(*args) + else: + self.activity.startDisabled() + + def filterDisabled(self, request, args): + BaseActivityFSM.notify.debug("filterDisabled( '%s', '%s' )" % (request, args)) + if request == 'Disabled': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitDisabled(self): + BaseActivityFSM.notify.debug("exitDisabled: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishDisabled() + + +class RulesMixin: + + def enterRules(self, *args): + BaseActivityFSM.notify.info("enterRules: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startRules(*args) + else: + self.activity.startRules() + + def filterRules(self, request, args): + BaseActivityFSM.notify.debug("filterRules( '%s', '%s' )" % (request, args)) + if request == 'Rules': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitRules(self): + BaseActivityFSM.notify.debug("exitRules: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishRules() + + +class WaitForEnoughMixin: + + def enterWaitForEnough(self, *args): + BaseActivityFSM.notify.info("enterWaitForEnough: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startWaitForEnough(*args) + else: + self.activity.startWaitForEnough() + + def filterWaitForEnough(self, request, args): + BaseActivityFSM.notify.debug("filterWaitForEnough( '%s', '%s' )" % (request, args)) + if request == 'WaitForEnough': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitWaitForEnough(self): + BaseActivityFSM.notify.debug("exitWaitForEnough: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishWaitForEnough() + + +class WaitToStartMixin: + + def enterWaitToStart(self, *args): + BaseActivityFSM.notify.info("enterWaitToStart: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startWaitToStart(*args) + else: + self.activity.startWaitToStart() + + def filterWaitToStart(self, request, args): + BaseActivityFSM.notify.debug("filterWaitToStart( '%s', '%s' )" % (request, args)) + if request == 'WaitToStart': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitWaitToStart(self): + BaseActivityFSM.notify.debug("exitWaitToStart: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishWaitToStart() + + +class WaitClientsReadyMixin: + + def enterWaitClientsReady(self, *args): + BaseActivityFSM.notify.info("enterWaitClientsReady: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startWaitClientsReady(*args) + else: + self.activity.startWaitClientsReady() + + def filterWaitClientsReady(self, request, args): + BaseActivityFSM.notify.debug("filterWaitClientsReady( '%s', '%s' )" % (request, args)) + if request == 'WaitClientsReady': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitWaitClientsReady(self): + BaseActivityFSM.notify.debug("exitWaitClientsReady: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishWaitClientsReady() + + +class WaitForServerMixin: + + def enterWaitForServer(self, *args): + BaseActivityFSM.notify.info("enterWaitForServer: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startWaitForServer(*args) + else: + self.activity.startWaitForServer() + + def filterWaitForServer(self, request, args): + BaseActivityFSM.notify.debug("filterWaitForServer( '%s', '%s' )" % (request, args)) + if request == 'WaitForServer': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitWaitForServer(self): + BaseActivityFSM.notify.debug("exitWaitForServer: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishWaitForServer() + + +class ConclusionMixin: + + def enterConclusion(self, *args): + BaseActivityFSM.notify.info("enterConclusion: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startConclusion(*args) + else: + self.activity.startConclusion() + + def filterConclusion(self, request, args): + BaseActivityFSM.notify.debug("filterConclusion( '%s', '%s' )" % (request, args)) + if request == 'Conclusion': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitConclusion(self): + BaseActivityFSM.notify.debug("exitConclusion: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishConclusion() diff --git a/toontown/parties/activityFSMs.py b/toontown/parties/activityFSMs.py new file mode 100755 index 00000000..a90e9536 --- /dev/null +++ b/toontown/parties/activityFSMs.py @@ -0,0 +1,79 @@ +from direct.directnotify import DirectNotifyGlobal +from BaseActivityFSM import BaseActivityFSM +from activityFSMMixins import IdleMixin +from activityFSMMixins import RulesMixin +from activityFSMMixins import ActiveMixin +from activityFSMMixins import DisabledMixin +from activityFSMMixins import ConclusionMixin +from activityFSMMixins import WaitForEnoughMixin +from activityFSMMixins import WaitToStartMixin +from activityFSMMixins import WaitClientsReadyMixin +from activityFSMMixins import WaitForServerMixin + +class FireworksActivityFSM(BaseActivityFSM, IdleMixin, ActiveMixin, DisabledMixin): + notify = DirectNotifyGlobal.directNotify.newCategory('FireworksActivityFSM') + + def __init__(self, activity): + FireworksActivityFSM.notify.debug('__init__') + BaseActivityFSM.__init__(self, activity) + self.defaultTransitions = {'Idle': ['Active', 'Disabled'], + 'Active': ['Disabled'], + 'Disabled': []} + + +class CatchActivityFSM(BaseActivityFSM, IdleMixin, ActiveMixin, ConclusionMixin): + notify = DirectNotifyGlobal.directNotify.newCategory('CatchActivityFSM') + + def __init__(self, activity): + CatchActivityFSM.notify.debug('__init__') + BaseActivityFSM.__init__(self, activity) + self.defaultTransitions = {'Idle': ['Active', 'Conclusion'], + 'Active': ['Conclusion'], + 'Conclusion': ['Idle']} + + +class TrampolineActivityFSM(BaseActivityFSM, IdleMixin, RulesMixin, ActiveMixin): + notify = DirectNotifyGlobal.directNotify.newCategory('TrampolineActivityFSM') + + def __init__(self, activity): + TrampolineActivityFSM.notify.debug('__init__') + BaseActivityFSM.__init__(self, activity) + self.defaultTransitions = {'Idle': ['Rules', 'Active'], + 'Rules': ['Active', 'Idle'], + 'Active': ['Idle']} + + +class DanceActivityFSM(BaseActivityFSM, IdleMixin, ActiveMixin, DisabledMixin): + notify = DirectNotifyGlobal.directNotify.newCategory('DanceActivityFSM') + + def __init__(self, activity): + DanceActivityFSM.notify.debug('__init__') + BaseActivityFSM.__init__(self, activity) + self.defaultTransitions = {'Active': ['Disabled'], + 'Disabled': ['Active']} + + +class TeamActivityAIFSM(BaseActivityFSM, WaitForEnoughMixin, WaitToStartMixin, WaitClientsReadyMixin, ActiveMixin, ConclusionMixin): + notify = DirectNotifyGlobal.directNotify.newCategory('TeamActivityAIFSM') + + def __init__(self, activity): + BaseActivityFSM.__init__(self, activity) + self.notify.debug('__init__') + self.defaultTransitions = {'WaitForEnough': ['WaitToStart'], + 'WaitToStart': ['WaitForEnough', 'WaitClientsReady'], + 'WaitClientsReady': ['WaitForEnough', 'Active'], + 'Active': ['WaitForEnough', 'Conclusion'], + 'Conclusion': ['WaitForEnough']} + + +class TeamActivityFSM(BaseActivityFSM, WaitForEnoughMixin, WaitToStartMixin, RulesMixin, WaitForServerMixin, ActiveMixin, ConclusionMixin): + notify = DirectNotifyGlobal.directNotify.newCategory('TeamActivityFSM') + + def __init__(self, activity): + BaseActivityFSM.__init__(self, activity) + self.defaultTransitions = {'WaitForEnough': ['WaitToStart'], + 'WaitToStart': ['WaitForEnough', 'Rules'], + 'Rules': ['WaitForServer', 'Active', 'WaitForEnough'], + 'WaitForServer': ['Active', 'WaitForEnough'], + 'Active': ['Conclusion', 'WaitForEnough'], + 'Conclusion': ['WaitForEnough']} diff --git a/toontown/pets/DistributedPet.py b/toontown/pets/DistributedPet.py new file mode 100755 index 00000000..7a8f39de --- /dev/null +++ b/toontown/pets/DistributedPet.py @@ -0,0 +1,487 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.showbase.PythonUtil import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedSmoothNode +from direct.distributed.ClockDelta import globalClockDelta +from direct.distributed.MsgTypes import * +from direct.task import Task +from otp.otpbase import OTPGlobals +from toontown.pets import Pet, PetBase, PetTraits, PetConstants, PetManager, PetAvatarPanel +from toontown.pets import PetMood, PetTricks +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.distributed import DelayDelete +from toontown.distributed.DelayDeletable import DelayDeletable +import random +BeanColors = (VBase4(1.0, 0.2, 0.2, 1.0), + VBase4(0.2, 1.0, 0.2, 1.0), + VBase4(0.2, 0.2, 1.0, 1.0), + VBase4(0.0, 1.0, 1.0, 1.0), + VBase4(1.0, 1.0, 0.0, 1.0), + VBase4(1.0, 0.6, 1.0, 1.0), + VBase4(0.6, 0.0, 0.6, 1.0)) + +class DistributedPet(DistributedSmoothNode.DistributedSmoothNode, Pet.Pet, PetBase.PetBase, DelayDeletable): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPet') + swallowSfx = None + callSfx = None + petSfx = None + + def __init__(self, cr, bFake = False): + DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr) + Pet.Pet.__init__(self) + self.bFake = bFake + self.isLocalToon = 0 + self.inWater = 0 + self.__funcsToDelete = [] + self.__generateDistTraitFuncs() + self.__generateDistMoodFuncs() + self.trickAptitudes = [] + self.avDelayDelete = None + return + + def generate(self): + DistributedPet.notify.debug('generate(), fake=%s' % self.bFake) + if not self.bFake: + PetManager.acquirePetManager() + DistributedSmoothNode.DistributedSmoothNode.generate(self) + self.trickIval = None + self.movieTrack = None + self.traitList = [0] * PetTraits.PetTraits.NumTraits + self.requiredMoodComponents = {} + return + + def b_setLocation(self, parentId, zoneId): + if not self.bFake: + DistributedSmoothNode.DistributedSmoothNode.b_setLocation(self, parentId, zoneId) + + def d_setLocation(self, parentId, zoneId): + if not self.bFake: + DistributedSmoothNode.DistributedSmoothNode.d_setLocation(self, parentId, zoneId) + + def setLocation(self, parentId, zoneId): + if not self.bFake: + DistributedSmoothNode.DistributedSmoothNode.setLocation(self, parentId, zoneId) + + def getDisplayPrefix(self): + return 'pet%s' % self.doId + + def display(self, key, value, category = ''): + if self.bFake: + return 1 + if len(category) > 0: + category = '-' + category + onScreenDebug.add('%s%s-%s' % (self.getDisplayPrefix(), category, key), value) + return 1 + + def clearDisplay(self): + onScreenDebug.removeAllWithPrefix(self.getDisplayPrefix()) + return 1 + + def moodComponentChanged(self, components = []): + if len(components) == 0: + components = PetMood.PetMood.Components + for comp in components: + self.display(comp, self.mood.getComponent(comp), 'mood') + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + + def getOwnerId(self): + return self.ownerId + + def setPetName(self, petName): + self.petName = petName + DistributedSmoothNode.DistributedSmoothNode.setName(self, self.petName) + if self.isGenerated(): + Pet.Pet.setName(self, self.petName) + messenger.send('petNameChanged', [self]) + + def setTraitSeed(self, traitSeed): + self.traitSeed = traitSeed + + def setSafeZone(self, safeZone): + self.safeZone = safeZone + + def __generateDistTraitFuncs(self): + for i in xrange(PetTraits.PetTraits.NumTraits): + traitName = PetTraits.getTraitNames()[i] + setterName = self.getSetterName(traitName) + + def traitSetter(value, self = self, i = i): + self.traitList[i] = value + + self.__dict__[setterName] = traitSetter + self.__funcsToDelete.append(setterName) + + def setHead(self, head): + DistributedPet.notify.debug('setHead: %s' % head) + self.head = head + + def setEars(self, ears): + DistributedPet.notify.debug('setEars: %s' % ears) + self.ears = ears + + def setNose(self, nose): + DistributedPet.notify.debug('setNose: %s' % nose) + self.nose = nose + + def setTail(self, tail): + DistributedPet.notify.debug('setTail: %s' % tail) + self.tail = tail + + def setBodyTexture(self, bodyTexture): + DistributedPet.notify.debug('setBodyTexture: %s' % bodyTexture) + self.bodyTexture = bodyTexture + + def setColor(self, color): + DistributedPet.notify.debug('setColor: %s' % color) + self.color = color + + def setColorScale(self, colorScale): + DistributedPet.notify.debug('setColorScale: %s' % colorScale) + self.colorScale = colorScale + + def setEyeColor(self, eyeColor): + DistributedPet.notify.debug('setEyeColor: %s' % eyeColor) + self.eyeColor = eyeColor + + def setGender(self, gender): + DistributedPet.notify.debug('setGender: %s' % gender) + self.gender = gender + + def setLastSeenTimestamp(self, timestamp): + DistributedPet.notify.debug('setLastSeenTimestamp: %s' % timestamp) + self.lastSeenTimestamp = timestamp + + def getTimeSinceLastSeen(self): + t = self.cr.getServerTimeOfDay() - self.lastSeenTimestamp + return max(0.0, t) + + def updateOfflineMood(self): + self.mood.driftMood(dt=self.getTimeSinceLastSeen(), curMood=self.lastKnownMood) + + def __handleMoodSet(self, component, value): + if self.isGenerated(): + self.mood.setComponent(component, value) + else: + self.requiredMoodComponents[component] = value + + def __generateDistMoodFuncs(self): + for compName in PetMood.PetMood.Components: + setterName = self.getSetterName(compName) + + def moodSetter(value, self = self, compName = compName): + self.__handleMoodSet(compName, value) + + self.__dict__[setterName] = moodSetter + self.__funcsToDelete.append(setterName) + + def setMood(self, *componentValues): + for value, name in zip(componentValues, PetMood.PetMood.Components): + setterName = self.getSetterName(name) + self.__dict__[setterName](value) + + def doTrick(self, trickId, timestamp): + if not self.isLockedDown(): + if self.trickIval is not None and self.trickIval.isPlaying(): + self.trickIval.finish() + self.trickIval = PetTricks.getTrickIval(self, trickId) + if trickId == PetTricks.Tricks.BALK: + mood = self.getDominantMood() + self.trickIval = Parallel(self.trickIval, Sequence(Func(self.handleMoodChange, 'confusion'), Wait(1.0), Func(self.handleMoodChange, mood))) + self.trickIval.start(globalClockDelta.localElapsedTime(timestamp)) + return + + def getName(self): + return Pet.Pet.getName(self) + + def announceGenerate(self): + DistributedPet.notify.debug('announceGenerate(), fake=%s' % self.bFake) + DistributedSmoothNode.DistributedSmoothNode.announceGenerate(self) + if hasattr(self, 'petName'): + Pet.Pet.setName(self, self.petName) + self.traits = PetTraits.PetTraits(self.traitSeed, self.safeZone) + self.mood = PetMood.PetMood(self) + for mood, value in self.requiredMoodComponents.items(): + self.mood.setComponent(mood, value, announce=0) + + self.requiredMoodComponents = {} + DistributedPet.notify.debug('time since last seen: %s' % self.getTimeSinceLastSeen()) + self.setDNA([self.head, + self.ears, + self.nose, + self.tail, + self.bodyTexture, + self.color, + self.colorScale, + self.eyeColor, + self.gender]) + av = self.cr.doId2do.get(self.ownerId) + if av: + av.petDNA = self.style + if self.bFake: + self.lastKnownMood = self.mood.makeCopy() + self.updateOfflineMood() + else: + self.__initCollisions() + self.startSmooth() + self.setActiveShadow(1) + self.setPetName(self.petName) + if not self.bFake: + self.addActive() + self.startBlink() + if not self.swallowSfx: + self.swallowSfx = loader.loadSfx('phase_5.5/audio/sfx/beg_eat_swallow.ogg') + if not self.callSfx: + self.callSfx = loader.loadSfx('phase_5.5/audio/sfx/call_pet.ogg') + if not self.petSfx: + self.petSfx = loader.loadSfx('phase_5.5/audio/sfx/pet_the_pet.ogg') + self.handleMoodChange() + self.accept(self.mood.getDominantMoodChangeEvent(), self.handleMoodChange) + self.accept(self.mood.getMoodChangeEvent(), self.moodComponentChanged) + + def disable(self): + DistributedPet.notify.debug('disable(), fake=%s' % self.bFake) + if self.isLocalToon: + base.localAvatar.enableSmartCameraViews() + self.freeAvatar() + self.ignore(self.mood.getDominantMoodChangeEvent()) + self.ignore(self.mood.getMoodChangeEvent()) + if hasattr(self, 'lastKnownMood'): + self.lastKnownMood.destroy() + del self.lastKnownMood + self.mood.destroy() + del self.mood + del self.traits + self.removeActive() + if not self.bFake: + self.stopSmooth() + self.__cleanupCollisions() + self.stopAnimations() + if self.doId == localAvatar.getPetId(): + bboard.post(PetConstants.OurPetsMoodChangedKey, True) + taskMgr.remove(self.uniqueName('lerpCamera')) + self.clearDisplay() + DistributedSmoothNode.DistributedSmoothNode.disable(self) + + def delete(self): + DistributedPet.notify.debug('delete(), fake=%s' % self.bFake) + if self.trickIval is not None: + self.trickIval.finish() + del self.trickIval + if self.movieTrack is not None: + self.movieTrack.finish() + del self.movieTrack + taskMgr.remove(self.uniqueName('Pet-Movie-%s' % self.getDoId())) + self.clearMovie() + for funcName in self.__funcsToDelete: + del self.__dict__[funcName] + + Pet.Pet.delete(self) + DistributedSmoothNode.DistributedSmoothNode.delete(self) + if not self.bFake: + PetManager.releasePetManager() + return + + def __initCollisions(self): + cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0) + cRayNode = CollisionNode('pet-cRayNode-%s' % self.doId) + cRayNode.addSolid(cRay) + cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + cRayNode.setIntoCollideMask(BitMask32.allOff()) + self.cRayNodePath = self.attachNewNode(cRayNode) + self.lifter = CollisionHandlerFloor() + self.lifter.setInPattern('enter%in') + self.lifter.setOutPattern('exit%in') + self.lifter.setOffset(OTPGlobals.FloorOffset) + self.lifter.setReach(4.0) + self.lifter.addCollider(self.cRayNodePath, self) + self.cTrav = base.petManager.cTrav + self.cTrav.addCollider(self.cRayNodePath, self.lifter) + taskMgr.add(self._detectWater, self.getDetectWaterTaskName(), priority=32) + self.initializeBodyCollisions('pet-%s' % self.doId) + + def __cleanupCollisions(self): + self.disableBodyCollisions() + taskMgr.remove(self.getDetectWaterTaskName()) + self.cTrav.removeCollider(self.cRayNodePath) + del self.cTrav + self.cRayNodePath.removeNode() + del self.cRayNodePath + del self.lifter + + def lockPet(self): + if not self.lockedDown: + self.prevAnimState = self.animFSM.getCurrentState().getName() + self.animFSM.request('neutral') + self.lockedDown += 1 + + def isLockedDown(self): + return self.lockedDown != 0 + + def unlockPet(self): + if self.lockedDown <= 0: + DistributedPet.notify.warning('%s: unlockPet called on unlockedPet' % self.doId) + else: + self.lockedDown -= 1 + if not self.lockedDown: + self.animFSM.request(self.prevAnimState) + self.prevAnimState = None + return + + def smoothPosition(self): + DistributedSmoothNode.DistributedSmoothNode.smoothPosition(self) + if not self.lockedDown: + self.trackAnimToSpeed(self.smoother.getSmoothForwardVelocity(), self.smoother.getSmoothRotationalVelocity()) + + def getDetectWaterTaskName(self): + return self.uniqueName('detectWater') + + def _detectWater(self, task): + showWake, wakeWaterHeight = ZoneUtil.getWakeInfo() + self.inWater = 0 + if showWake: + if self.getZ() <= wakeWaterHeight: + self.setZ(wakeWaterHeight - PetConstants.SubmergeDistance) + self.inWater = 1 + return Task.cont + + def isInWater(self): + return self.inWater + + def isExcited(self): + return PetBase.PetBase.isExcited(self) + + def isSad(self): + return PetBase.PetBase.isSad(self) + + def handleMoodChange(self, mood = None): + if mood is None: + mood = self.mood.getDominantMood() + if mood == PetMood.PetMood.Neutral: + self.clearChat() + self.clearMood() + else: + self.showMood(mood) + messenger.send('petStateUpdated', [self]) + return + + def getDominantMood(self): + if not hasattr(self, 'mood'): + return PetMood.PetMood.Neutral + return self.mood.getDominantMood() + + def getRequestID(self): + return CLIENT_GET_PET_DETAILS + + def teleportIn(self, timestamp): + self.lockPet() + self.animFSM.request('teleportIn', [timestamp]) + self.unlockPet() + + def teleportOut(self, timestamp): + self.lockPet() + self.animFSM.request('teleportOut', [timestamp]) + self.unlockPet() + + def avatarInteract(self, avId): + place = base.cr.playGame.getPlace() + place.setState('pet') + base.localAvatar.disableSmartCameraViews() + + def freeAvatar(self): + place = base.cr.playGame.getPlace() + if place: + place.setState('walk') + base.localAvatar.unlock() + messenger.send('pet-interaction-done') + + def setUpMovieAvatar(self, av): + self.avDelayDelete = DelayDelete.DelayDelete(av, 'Pet.setUpMovieAvatar') + av.headsUp(self, 0, 0, 0) + av.stopLookAround() + + def holdPetDownForMovie(self): + self.lockPet() + self.stopSmooth() + + def releasePetFromHoldDown(self): + self.unlockPet() + self.startSmooth() + + def clearMovieAvatar(self): + if self.avDelayDelete: + self.avDelayDelete.destroy() + self.avDelayDelete = None + return + + def clearMovie(self): + self.clearMovieAvatar() + return Task.done + + def resetAvatarAndPet(self, task = None): + if self.isLocalToon: + base.localAvatar.enableSmartCameraViews() + base.localAvatar.setH(base.localAvatar, 30) + self.freeAvatar() + self.isLocalToon = 0 + return Task.done + + def _petMovieStart(self, av): + if not self.isLocalToon: + av.stopSmooth() + self.setUpMovieAvatar(av) + if self.isLocalToon: + base.localAvatar.setCameraPosForPetInteraction() + base.localAvatar.lock() + + def _getPetMovieCompleteIval(self, av): + + def _petMovieComplete(self = self): + if self.isLocalToon: + base.localAvatar.unsetCameraPosForPetInteraction() + else: + av.startSmooth() + + return Sequence(Func(_petMovieComplete), Wait(0.8), Func(self.resetAvatarAndPet)) + + def setMovie(self, mode, avId, timestamp): + timeStamp = globalClockDelta.localElapsedTime(timestamp) + if mode in (PetConstants.PET_MOVIE_CALL, PetConstants.PET_MOVIE_SCRATCH, PetConstants.PET_MOVIE_FEED): + if self.movieTrack is not None and self.movieTrack.isPlaying(): + self.movieTrack.finish() + if avId != 0: + self.isLocalToon = avId == base.localAvatar.doId + av = base.cr.doId2do.get(avId) + if av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + if mode == PetConstants.PET_MOVIE_CLEAR: + self.clearMovie() + return + if mode == PetConstants.PET_MOVIE_CALL: + try: + self.movieTrack = Sequence(Func(self._petMovieStart, av), Parallel(av.getCallPetIval(), Sequence(Wait(0.54), SoundInterval(self.callSfx))), self._getPetMovieCompleteIval(av)) + self.movieTrack.start() + except StandardError, error: + print str(error) + + if mode == PetConstants.PET_MOVIE_SCRATCH: + try: + self.movieTrack = Sequence(Func(self._petMovieStart, av), Func(self.holdPetDownForMovie), Parallel(self.getInteractIval(self.Interactions.SCRATCH), av.getScratchPetIval(), SoundInterval(self.petSfx)), Func(self.releasePetFromHoldDown), self._getPetMovieCompleteIval(av)) + self.movieTrack.start() + except StandardError, error: + print str(error) + + if mode == PetConstants.PET_MOVIE_FEED: + self.bean = loader.loadModel('phase_4/models/props/jellybean4') + bean = self.bean.find('**/jellybean') + bean.setColor(random.choice(BeanColors)) + self.movieTrack = Sequence(Func(self._petMovieStart, av), Func(self.holdPetDownForMovie), Parallel(Func(base.playSfx, self.swallowSfx, 0, 1, 1, 2.5, self.bean), Sequence(ActorInterval(self, 'toBeg'), ActorInterval(self, 'beg'), ActorInterval(self, 'fromBeg'), ActorInterval(self, 'eat'), ActorInterval(self, 'swallow'), Func(self.loop, 'neutral')), Sequence(Wait(0.3), ActorInterval(av, 'feedPet'), Func(av.animFSM.request, 'neutral')), Sequence(Wait(0.3), Func(self.bean.reparentTo, av.rightHand), Func(self.bean.setPos, 0.1, 0.0, 0.2), Wait(2.1), Func(av.update, 0), Func(av.update, 1), Func(av.update, 2), Func(self.bean.wrtReparentTo, render), Parallel(LerpHprInterval(self.bean, hpr=Point3(random.random() * 360.0 * 2, random.random() * 360.0 * 2, random.random() * 360.0 * 2), duration=1.2), ProjectileInterval(self.bean, endPos=self.find('**/joint_tongueBase').getPos(render), duration=1.2, gravityMult=0.45)), Func(self.bean.removeNode))), Func(self.releasePetFromHoldDown), self._getPetMovieCompleteIval(av)) + self.movieTrack.start() + return + + def setTrickAptitudes(self, aptitudes): + self.trickAptitudes = aptitudes diff --git a/toontown/pets/DistributedPetAI.py b/toontown/pets/DistributedPetAI.py new file mode 100755 index 00000000..60ea14ea --- /dev/null +++ b/toontown/pets/DistributedPetAI.py @@ -0,0 +1,985 @@ +from panda3d.core import * +from direct.showbase.PythonUtil import weightedChoice, randFloat, lerp +from direct.showbase.PythonUtil import contains, list2dict +from toontown.toonbase.PythonUtil import clampScalar +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedSmoothNodeAI +from direct.distributed import DistributedSmoothNodeBase +from direct.distributed import ClockDelta +from direct.fsm import ClassicFSM, State +from direct.interval.IntervalGlobal import * +from toontown.toonbase import ToontownGlobals +from direct.task import Task +from toontown.pets import PetLookerAI +from toontown.pets import PetConstants, PetDNA, PetTraits +from toontown.pets import PetObserve, PetBrain, PetMood +from toontown.pets import PetActionFSM, PetBase, PetGoal, PetTricks +from direct.fsm import FSM +from toontown.toon import DistributedToonAI +import random +import time +import string +import copy +from direct.showbase.PythonUtil import StackTrace + +from PetMoverAI import PetMoverAI + +class DistributedPetAI(DistributedSmoothNodeAI.DistributedSmoothNodeAI, PetLookerAI.PetLookerAI, PetBase.PetBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPetAI') + movieTimeSwitch = {PetConstants.PET_MOVIE_FEED: PetConstants.FEED_TIME, + PetConstants.PET_MOVIE_SCRATCH: PetConstants.SCRATCH_TIME, + PetConstants.PET_MOVIE_CALL: PetConstants.CALL_TIME} + movieDistSwitch = {PetConstants.PET_MOVIE_FEED: PetConstants.FEED_DIST.get, + PetConstants.PET_MOVIE_SCRATCH: PetConstants.SCRATCH_DIST.get} + + def __init__(self, air, dna = None): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air) + PetLookerAI.PetLookerAI.__init__(self) + self.ownerId = 0 + self.petName = 'unnamed' + self.traitSeed = 0 + self.safeZone = ToontownGlobals.ToontownCentral + self.initialDNA = dna + self.active = 1 + self.activated = 0 + self._outOfBounds = False + self.traitList = [0] * PetTraits.PetTraits.NumTraits + self.head = -1 + self.ears = -1 + self.nose = -1 + self.tail = -1 + self.bodyTexture = 0 + self.color = 0 + self.colorScale = 0 + self.eyeColor = 0 + self.gender = 0 + self.movieMode = None + self.lockMoverEnabled = 0 + self.trickAptitudes = [] + self.inEstate = 0 + self.estateOwnerId = None + self.estateZones = [] + self.lastSeenTimestamp = self.getCurEpochTimestamp() + self.requiredMoodComponents = {} + self.__funcsToDelete = [] + self.__generateDistTraitFuncs() + self.__generateDistMoodFuncs() + self.busy = 0 + self.gaitFSM = ClassicFSM.ClassicFSM('petGaitFSM', [State.State('off', self.gaitEnterOff, self.gaitExitOff), + State.State('neutral', self.gaitEnterNeutral, self.gaitExitNeutral), + State.State('happy', self.gaitEnterHappy, self.gaitExitHappy), + State.State('sad', self.gaitEnterSad, self.gaitExitSad)], 'off', 'off') + self.gaitFSM.enterInitialState() + self.unstickFSM = ClassicFSM.ClassicFSM('unstickFSM', [State.State('off', self.unstickEnterOff, self.unstickExitOff), State.State('on', self.unstickEnterOn, self.unstickExitOn)], 'off', 'off') + self.unstickFSM.enterInitialState() + return + + def setInactive(self): + self.active = 0 + + def _initDBVals(self, ownerId, name = None, traitSeed = 0, dna = None, safeZone = ToontownGlobals.ToontownCentral): + self.b_setOwnerId(ownerId) + if name is None: + name = 'pet%s' % self.doId + self.b_setPetName(name) + self.b_setTraitSeed(traitSeed) + self.b_setSafeZone(safeZone) + traits = PetTraits.PetTraits(traitSeed, safeZone) + for traitName in PetTraits.getTraitNames(): + setter = self.getSetterName(traitName, 'b_set') + self.__dict__[setter](traits.getTraitValue(traitName)) + + self.traits = traits + for component in PetMood.PetMood.Components: + setterName = self.getSetterName(component, 'b_set') + self.__dict__[setterName](0.0) + + if not dna: + dna = PetDNA.getRandomPetDNA() + self.setDNA(dna) + self.b_setLastSeenTimestamp(self.getCurEpochTimestamp()) + for component in PetMood.PetMood.Components: + self.setMoodComponent(component, 0.0) + + self.b_setTrickAptitudes([]) + return + + def setDNA(self, dna): + head, ears, nose, tail, body, color, colorScale, eyes, gender = dna + self.b_setHead(head) + self.b_setEars(ears) + self.b_setNose(nose) + self.b_setTail(tail) + self.b_setBodyTexture(body) + self.b_setColor(color) + self.b_setColorScale(colorScale) + self.b_setEyeColor(eyes) + self.b_setGender(gender) + + def handleZoneChange(self, newZoneId, oldZoneId): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.handleZoneChange(self, newZoneId, oldZoneId) + self.ignore(PetObserve.getEventName(oldZoneId)) + self.accept(PetObserve.getEventName(newZoneId), self.brain.observe) + + def handleLogicalZoneChange(self, newZoneId, oldZoneId): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.handleLogicalZoneChange(self, newZoneId, oldZoneId) + self.announceZoneChange(newZoneId, oldZoneId) + + def announceZoneChange(self, newZoneId, oldZoneId): + DistributedPetAI.notify.debug('%s.announceZoneChange: %s->%s' % (self.doId, oldZoneId, newZoneId)) + broadcastZones = list2dict([newZoneId, oldZoneId]) + self.estateOwnerId = simbase.air.estateManager.getOwnerFromZone(newZoneId) + if self.estateOwnerId: + self.inEstate = 1 + self.estateZones = simbase.air.estateManager.getEstateZones(self.estateOwnerId) + else: + self.inEstate = 0 + self.estateZones = [] + PetObserve.send(broadcastZones.keys(), PetObserve.PetActionObserve(PetObserve.Actions.CHANGE_ZONE, self.doId, (oldZoneId, newZoneId))) + + def getOwnerId(self): + return self.ownerId + + def b_setOwnerId(self, ownerId): + self.d_setOwnerId(ownerId) + self.setOwnerId(ownerId) + + def d_setOwnerId(self, ownerId): + self.sendUpdate('setOwnerId', [ownerId]) + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + + def getPetName(self): + return self.petName + + def b_setPetName(self, petName): + self.d_setPetName(petName) + self.setPetName(petName) + + def d_setPetName(self, petName): + self.sendUpdate('setPetName', [petName]) + + def setPetName(self, petName): + self.petName = petName + DistributedSmoothNodeAI.DistributedSmoothNodeAI.setName(self, self.petName) + + def getTraitSeed(self): + return self.traitSeed + + def b_setTraitSeed(self, traitSeed): + self.d_setTraitSeed(traitSeed) + self.setTraitSeed(traitSeed) + + def d_setTraitSeed(self, traitSeed): + self.sendUpdate('setTraitSeed', [traitSeed]) + + def setTraitSeed(self, traitSeed): + self.traitSeed = traitSeed + + def getSafeZone(self): + return self.safeZone + + def b_setSafeZone(self, safeZone): + self.d_setSafeZone(safeZone) + self.setSafeZone(safeZone) + + def d_setSafeZone(self, safeZone): + self.sendUpdate('setSafeZone', [safeZone]) + + def setSafeZone(self, safeZone): + self.safeZone = safeZone + + def getPetName(self): + return self.petName + + def b_setPetName(self, petName): + self.d_setPetName(petName) + self.setPetName(petName) + + def d_setPetName(self, petName): + self.sendUpdate('setPetName', [petName]) + + def setPetName(self, petName): + self.petName = petName + DistributedSmoothNodeAI.DistributedSmoothNodeAI.setName(self, self.petName) + + def setTraits(self, traitList): + self.traitList = traitList + + def __generateDistTraitFuncs(self): + for i in xrange(PetTraits.PetTraits.NumTraits): + traitName = PetTraits.getTraitNames()[i] + getterName = self.getSetterName(traitName, 'get') + b_setterName = self.getSetterName(traitName, 'b_set') + d_setterName = self.getSetterName(traitName, 'd_set') + setterName = self.getSetterName(traitName) + + def traitGetter(i = i): + return self.traitList[i] + + def b_traitSetter(value, setterName = setterName, d_setterName = d_setterName): + self.__dict__[d_setterName](value) + self.__dict__[setterName](value) + + def d_traitSetter(value, setterName = setterName): + self.sendUpdate(setterName, [value]) + + def traitSetter(value, i = i): + self.traitList[i] = value + + self.__dict__[getterName] = traitGetter + self.__dict__[b_setterName] = b_traitSetter + self.__dict__[d_setterName] = d_traitSetter + self.__dict__[setterName] = traitSetter + self.__funcsToDelete.append(getterName) + self.__funcsToDelete.append(b_setterName) + self.__funcsToDelete.append(d_setterName) + self.__funcsToDelete.append(setterName) + + def getHead(self): + return self.head + + def b_setHead(self, head): + self.d_setHead(head) + self.setHead(head) + + def d_setHead(self, head): + self.sendUpdate('setHead', [head]) + + def setHead(self, head): + self.head = head + + def getEars(self): + return self.ears + + def b_setEars(self, ears): + self.d_setEars(ears) + self.setEars(ears) + + def d_setEars(self, ears): + self.sendUpdate('setEars', [ears]) + + def setEars(self, ears): + self.ears = ears + + def getNose(self): + return self.nose + + def b_setNose(self, nose): + self.d_setNose(nose) + self.setNose(nose) + + def d_setNose(self, nose): + self.sendUpdate('setNose', [nose]) + + def setNose(self, nose): + self.nose = nose + + def getTail(self): + return self.tail + + def b_setTail(self, tail): + self.d_setTail(tail) + self.setTail(tail) + + def d_setTail(self, tail): + self.sendUpdate('setTail', [tail]) + + def setTail(self, tail): + self.tail = tail + + def getBodyTexture(self): + return self.bodyTexture + + def b_setBodyTexture(self, bodyTexture): + self.d_setBodyTexture(bodyTexture) + self.setBodyTexture(bodyTexture) + + def d_setBodyTexture(self, bodyTexture): + self.sendUpdate('setBodyTexture', [bodyTexture]) + + def setBodyTexture(self, bodyTexture): + self.bodyTexture = bodyTexture + + def getColor(self): + return self.color + + def b_setColor(self, color): + self.d_setColor(color) + self.setColor(color) + + def d_setColor(self, color): + self.sendUpdate('setColor', [color]) + + def setColor(self, color): + self.color = color + + def getColorScale(self): + return self.colorScale + + def b_setColorScale(self, colorScale): + self.d_setColorScale(colorScale) + self.setColorScale(colorScale) + + def d_setColorScale(self, colorScale): + self.sendUpdate('setColorScale', [colorScale]) + + def setColorScale(self, colorScale): + self.colorScale = colorScale + + def getEyeColor(self): + return self.eyeColor + + def b_setEyeColor(self, eyeColor): + self.d_setEyeColor(eyeColor) + self.setEyeColor(eyeColor) + + def d_setEyeColor(self, eyeColor): + self.sendUpdate('setEyeColor', [eyeColor]) + + def setEyeColor(self, eyeColor): + self.eyeColor = eyeColor + + def getGender(self): + return self.gender + + def b_setGender(self, gender): + self.d_setGender(gender) + self.setGender(gender) + + def d_setGender(self, gender): + self.sendUpdate('setGender', [gender]) + + def setGender(self, gender): + self.gender = gender + + def teleportIn(self, timestamp = None): + self.notify.debug('DPAI: teleportIn') + timestamp = ClockDelta.globalClockDelta.getRealNetworkTime() + self.notify.debug('DPAI: sending update @ ts = %s' % timestamp) + self.sendUpdate('teleportIn', [timestamp]) + return None + + def teleportOut(self, timestamp = None): + self.notify.debug('DPAI: teleportOut') + timestamp = ClockDelta.globalClockDelta.getRealNetworkTime() + self.notify.debug('DPAI: sending update @ ts = %s' % timestamp) + self.sendUpdate('teleportOut', [timestamp]) + return None + + def getLastSeenTimestamp(self): + return self.lastSeenTimestamp + + def b_setLastSeenTimestamp(self, timestamp): + self.d_setLastSeenTimestamp(timestamp) + self.setLastSeenTimestamp(timestamp) + + def d_setLastSeenTimestamp(self, timestamp): + self.sendUpdate('setLastSeenTimestamp', [timestamp]) + + def setLastSeenTimestamp(self, timestamp): + self.lastSeenTimestamp = timestamp + + def getCurEpochTimestamp(self): + return int(time.time()) + + def getTimeSinceLastSeen(self): + t = time.time() - self.lastSeenTimestamp + return max(0.0, t) + + def __handleMoodSet(self, component, value): + if self.isGenerated(): + self.mood.setComponent(component, value) + else: + self.requiredMoodComponents[component] = value + + def __handleMoodGet(self, component): + if self.isGenerated(): + return self.mood.getComponent(component) + else: + return 0.0 + + def __generateDistMoodFuncs(self): + for compName in PetMood.PetMood.Components: + getterName = self.getSetterName(compName, 'get') + setterName = self.getSetterName(compName) + + def moodGetter(compName = compName): + return self.__handleMoodGet(compName) + + def b_moodSetter(value, setterName = setterName): + self.__dict__[setterName](value) + + def d_moodSetter(value, setterName = setterName): + self.sendUpdate(setterName, [value]) + + def moodSetter(value, compName = compName): + self.__handleMoodSet(compName, value) + + self.__dict__[getterName] = moodGetter + self.__dict__['b_%s' % setterName] = b_moodSetter + self.__dict__['d_%s' % setterName] = d_moodSetter + self.__dict__[setterName] = moodSetter + self.__funcsToDelete.append(getterName) + self.__funcsToDelete.append('b_%s' % setterName) + self.__funcsToDelete.append('d_%s' % setterName) + self.__funcsToDelete.append(setterName) + + def getTrickAptitudes(self): + return self.trickAptitudes + + def b_setTrickAptitudes(self, aptitudes): + self.setTrickAptitudes(aptitudes, local=1) + self.d_setTrickAptitudes(aptitudes) + + def d_setTrickAptitudes(self, aptitudes): + while len(aptitudes) < len(PetTricks.Tricks) - 1: + aptitudes.append(0.0) + + self.sendUpdate('setTrickAptitudes', [aptitudes]) + + def setTrickAptitudes(self, aptitudes, local = 0): + if not local: + DistributedPetAI.notify.debug('setTrickAptitudes: %s' % aptitudes) + while len(self.trickAptitudes) < len(PetTricks.Tricks) - 1: + self.trickAptitudes.append(0.0) + + self.trickAptitudes = aptitudes + + def getTrickAptitude(self, trickId): + if trickId > len(self.trickAptitudes) - 1: + return 0.0 + return self.trickAptitudes[trickId] + + def setTrickAptitude(self, trickId, aptitude, send = 1): + aptitude = clampScalar(aptitude, 0.0, 1.0) + aptitudes = self.trickAptitudes + while len(aptitudes) - 1 < trickId: + aptitudes.append(0.0) + + if aptitudes[trickId] != aptitude: + aptitudes[trickId] = aptitude + if send: + self.b_setTrickAptitudes(aptitudes) + else: + self.setTrickAptitudes(aptitudes, local=1) + + def announceGenerate(self): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.announceGenerate(self) + self._hasCleanedUp = False + self.setHasRequestedDelete(False) + self.b_setParent(ToontownGlobals.SPHidden) + self.lockedDown = 0 + self.leashMode = 0 + self.leashAvId = None + self.leashGoal = None + self.traits = PetTraits.PetTraits(self.traitSeed, self.safeZone) + if not hasattr(self, '_beingCreatedInDB'): + for i in xrange(len(self.traitList)): + value = self.traitList[i] + if value == 0.0: + traitName = PetTraits.getTraitNames()[i] + traitValue = self.traits.getTraitValue(traitName) + DistributedPetAI.notify.info("%s: initializing new trait '%s' to %s, seed=%s" % (self.doId, + traitName, + traitValue, + self.traitSeed)) + setterName = self.getSetterName(traitName, 'b_set') + self.__dict__[setterName](traitValue) + + self.mood = PetMood.PetMood(self) + if not self.active: + return + self.activated = 1 + self.announceZoneChange(self.zoneId, ToontownGlobals.QuietZone) + self.b_setParent(ToontownGlobals.SPRender) + self.setPos(randFloat(-20, 20), randFloat(-20, 20), 0) + self.setH(randFloat(360)) + if self.initialDNA: + self.setDNA(self.initialDNA) + for mood, value in self.requiredMoodComponents.items(): + self.mood.setComponent(mood, value, announce=0) + + self.requiredMoodComponents = {} + self.brain = PetBrain.PetBrain(self) + self.mover = PetMoverAI(self) + self.enterPetLook() + self.actionFSM = PetActionFSM.PetActionFSM(self) + self.teleportIn() + self.handleMoodChange(distribute=0) + taskMgr.doMethodLater(simbase.petMovePeriod * random.random(), self.move, self.getMoveTaskName()) + self.startPosHprBroadcast() + self.accept(PetObserve.getEventName(self.zoneId), self.brain.observe) + self.accept(self.mood.getMoodChangeEvent(), self.handleMoodChange) + self.mood.start() + self.brain.start() + return + + def _isPet(self): + return 1 + + def setHasRequestedDelete(self, flag): + self._requestedDeleteFlag = flag + + def hasRequestedDelete(self): + return self._requestedDeleteFlag + + def requestDelete(self, task = None): + DistributedPetAI.notify.info('PetAI.requestDelete: %s, owner=%s' % (self.doId, self.ownerId)) + if self.hasRequestedDelete(): + DistributedPetAI.notify.info('PetAI.requestDelete: %s, owner=%s returning immediately' % (self.doId, self.ownerId)) + return + self.setHasRequestedDelete(True) + self.b_setLastSeenTimestamp(self.getCurEpochTimestamp()) + DistributedSmoothNodeAI.DistributedSmoothNodeAI.requestDelete(self) + + def _doDeleteCleanup(self): + taskMgr.remove(self.uniqueName('clearMovie')) + taskMgr.remove(self.uniqueName('PetMovieWait')) + taskMgr.remove(self.uniqueName('PetMovieClear')) + taskMgr.remove(self.uniqueName('PetMovieComplete')) + taskMgr.remove(self.getLockMoveTaskName()) + taskMgr.remove(self.getMoveTaskName()) + if hasattr(self, 'zoneId'): + self.announceZoneChange(ToontownGlobals.QuietZone, self.zoneId) + else: + myDoId = 'No doId' + myTaskName = 'No task name' + myStackTrace = StackTrace().trace + myOldStackTrace = 'No Trace' + if hasattr(self, 'doId'): + myDoId = self.doId + if task: + myTaskName = task.name + if hasattr(self, 'destroyDoStackTrace'): + myOldStackTrace = self.destroyDoStackTrace.trace + simbase.air.writeServerEvent('Pet RequestDelete duplicate', myDoId, 'from task %s' % myTaskName) + simbase.air.writeServerEvent('Pet RequestDelete duplicate StackTrace', myDoId, '%s' % myStackTrace) + simbase.air.writeServerEvent('Pet RequestDelete duplicate OldStackTrace', myDoId, '%s' % myOldStackTrace) + DistributedPetAI.notify.warning('double requestDelete from task %s' % myTaskName) + self.setParent(ToontownGlobals.SPHidden) + if hasattr(self, 'activated'): + if self.activated: + self.activated = 0 + self.brain.destroy() + del self.brain + self.actionFSM.destroy() + del self.actionFSM + self.exitPetLook() + self.mover.destroy() + del self.mover + self.stopPosHprBroadcast() + if hasattr(self, 'mood'): + self.mood.destroy() + del self.mood + if hasattr(self, 'traits'): + del self.traits + try: + for funcName in self.__funcsToDelete: + del self.__dict__[funcName] + + except: + pass + + if hasattr(self, 'gaitFSM'): + if self.gaitFSM: + self.gaitFSM.requestFinalState() + del self.gaitFSM + if hasattr(self, 'unstickFSM'): + if self.unstickFSM: + self.unstickFSM.requestFinalState() + del self.unstickFSM + PetLookerAI.PetLookerAI.destroy(self) + self.ignoreAll() + self._hasCleanedUp = True + + def delete(self): + DistributedPetAI.notify.info('PetAI.delete: %s, owner=%s' % (self.doId, self.ownerId)) + if not self._hasCleanedUp: + self._doDeleteCleanup() + self.setHasRequestedDelete(False) + DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self) + + def patchDelete(self): + for funcName in self.__funcsToDelete: + del self.__dict__[funcName] + + del self.gaitFSM + del self.unstickFSM + PetLookerAI.PetLookerAI.destroy(self) + self.doNotDeallocateChannel = True + self.zoneId = None + DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self) + self.ignoreAll() + return + + def getMoveTaskName(self): + return 'petMove-%s' % self.doId + + def getLockMoveTaskName(self): + return 'petLockMove-%s' % self.doId + + def move(self, task = None): + if self.isEmpty(): + self.air.writeServerEvent('Late Pet Move Call', self.doId, ' ') + taskMgr.remove(task.name) + return Task.done + + numNearby = len(self.brain.nearbyAvs) - 1 + minNearby = 5 + if numNearby > minNearby: + delay = 0.08 * (numNearby - minNearby) + self.setPosHprBroadcastPeriod(PetConstants.PosBroadcastPeriod + lerp(delay * 0.75, delay, random.random())) + maxDist = 1000 + if abs(self.getX()) > maxDist or abs(self.getY()) > maxDist: + DistributedPetAI.notify.warning('deleting pet %s before he wanders off too far' % self.doId) + self._outOfBounds = True + self.stopPosHprBroadcast() + self.requestDelete() + return Task.done + taskMgr.doMethodLater(simbase.petMovePeriod, self.move, self.getMoveTaskName()) + return Task.done + + def startPosHprBroadcast(self): + if self._outOfBounds: + return + DistributedSmoothNodeAI.DistributedSmoothNodeAI.startPosHprBroadcast(self, period=simbase.petPosBroadcastPeriod, type=DistributedSmoothNodeBase.DistributedSmoothNodeBase.BroadcastTypes.XYH) + + def setMoodComponent(self, component, value): + setter = self.getSetterName(component, 'b_set') + self.__dict__[setter](value) + + def addToMood(self, component, delta): + value = self.mood.getComponent(component) + value += delta + self.setMoodComponent(component, clampScalar(value, 0.0, 1.0)) + + def lerpMood(self, component, factor): + curVal = self.mood.getComponent(component) + if factor < 0: + self.setMoodComponent(component, lerp(curVal, 0.0, -factor)) + else: + self.setMoodComponent(component, lerp(curVal, 1.0, factor)) + + def addToMoods(self, mood2delta): + for mood, delta in mood2delta.items(): + self.addToMood(mood, delta) + + def lerpMoods(self, mood2factor): + for mood, factor in mood2factor.items(): + self.lerpMood(mood, factor) + + def handleMoodChange(self, components = [], distribute = 1): + if len(components) == 0: + components = PetMood.PetMood.Components + if distribute: + if len(components) == len(PetMood.PetMood.Components): + values = [] + for comp in PetMood.PetMood.Components: + values.append(self.mood.getComponent(comp)) + + self.sendUpdate('setMood', values) + else: + for comp in components: + setter = self.getSetterName(comp, 'd_set') + self.__dict__[setter](self.mood.getComponent(comp)) + + if self.isExcited(): + self.gaitFSM.request('happy') + elif self.isSad(): + self.gaitFSM.request('sad') + else: + self.gaitFSM.request('neutral') + + def isContented(self): + return self.mood.getDominantMood() in PetMood.PetMood.ContentedMoods + + def call(self, avatar): + self.brain.observe(PetObserve.PetPhraseObserve(PetObserve.Phrases.COME, avatar.doId)) + self.__petMovieStart(avatar.doId) + + def feed(self, avatar): + if avatar.takeMoney(PetConstants.FEED_AMOUNT): + self.startLockPetMove(avatar.doId) + self.brain.observe(PetObserve.PetActionObserve(PetObserve.Actions.FEED, avatar.doId)) + + def scratch(self, avatar): + self.startLockPetMove(avatar.doId) + self.brain.observe(PetObserve.PetActionObserve(PetObserve.Actions.SCRATCH, avatar.doId)) + + def lockPet(self): + DistributedPetAI.notify.debug('%s: lockPet' % self.doId) + if not self.lockedDown: + self.mover.lock() + self.stopPosHprBroadcast() + self.lockedDown += 1 + + def isLockedDown(self): + return self.lockedDown != 0 + + def unlockPet(self): + DistributedPetAI.notify.debug('%s: unlockPet' % self.doId) + if self.lockedDown <= 0: + DistributedPetAI.notify.warning('%s: unlockPet called on unlocked pet' % self.doId) + else: + self.lockedDown -= 1 + if not self.lockedDown and not self.isDeleted(): + self.startPosHprBroadcast() + + def handleStay(self, avatar): + self.brain.observe(PetObserve.PetPhraseObserve(PetObserve.Phrases.STAY, avatar.doId)) + + def handleShoo(self, avatar): + self.brain.observe(PetObserve.PetPhraseObserve(PetObserve.Phrases.GO_AWAY, avatar.doId)) + + def gaitEnterOff(self): + pass + + def gaitExitOff(self): + pass + + def gaitEnterNeutral(self): + self.mover.setFwdSpeed(PetConstants.FwdSpeed) + self.mover.setRotSpeed(PetConstants.RotSpeed) + + def gaitExitNeutral(self): + pass + + def gaitEnterHappy(self): + self.mover.setFwdSpeed(PetConstants.HappyFwdSpeed) + self.mover.setRotSpeed(PetConstants.HappyRotSpeed) + + def gaitExitHappy(self): + pass + + def gaitEnterSad(self): + self.mover.setFwdSpeed(PetConstants.SadFwdSpeed) + self.mover.setRotSpeed(PetConstants.SadRotSpeed) + + def gaitExitSad(self): + pass + + def unstickEnterOff(self): + pass + + def unstickExitOff(self): + pass + + def unstickEnterOn(self): + self._collisionTimestamps = [] + + def unstickExitOn(self): + pass + + def ownerIsOnline(self): + return self.ownerId in simbase.air.doId2do + + def ownerIsInSameZone(self): + if not self.ownerIsOnline(): + return 0 + return self.zoneId == simbase.air.doId2do[self.ownerId].zoneId + + def _getOwnerDict(self): + if self.owner is not None: + if self.ownerIsInSameZone(): + return {self.ownerId: self.owner} + return {} + + def _getFullNearbyToonDict(self): + toons = self.air.getObjectsOfClassInZone(self.air.districtId, self.zoneId, DistributedToonAI.DistributedToonAI) + return toons + + def _getNearbyToonDict(self): + toons = self._getFullNearbyToonDict() + if self.ownerId in toons: + del toons[self.ownerId] + return toons + + def _getNearbyPetDict(self): + pets = self.air.getObjectsOfClassInZone(self.air.districtId, self.zoneId, DistributedPetAI) + if self.doId in pets: + del pets[self.doId] + return pets + + def _getNearbyAvatarDict(self): + avs = self._getFullNearbyToonDict() + avs.update(self._getNearbyPetDict()) + return avs + + def _getOwner(self): + return self.air.doId2do.get(self.ownerId) + + def _getNearbyToon(self): + nearbyToonDict = self._getFullNearbyToonDict() + if not len(nearbyToonDict): + return None + return nearbyToonDict[random.choice(nearbyToonDict.keys())] + + def _getNearbyToonNonOwner(self): + nearbyToonDict = self._getNearbyToonDict() + if not len(nearbyToonDict): + return None + return nearbyToonDict[random.choice(nearbyToonDict.keys())] + + def _getNearbyPet(self): + nearbyPetDict = self._getNearbyPetDict() + if not len(nearbyPetDict): + return None + return nearbyPetDict[random.choice(nearbyPetDict.keys())] + + def _getNearbyAvatar(self): + nearbyAvDict = self._getNearbyAvatarDict() + if not len(nearbyAvDict): + return None + return nearbyAvDict[random.choice(nearbyAvDict.keys())] + + def isBusy(self): + return self.busy > 0 + + def freeAvatar(self, avId): + self.sendUpdateToAvatarId(avId, 'freeAvatar', []) + + def avatarInteract(self, avId): + av = self.air.doId2do.get(avId) + if av is None: + self.notify.warning('Avatar: %s not found' % avId) + return 0 + if self.isBusy(): + self.notify.debug('freeing avatar!') + self.freeAvatar(avId) + return 0 + self.busy = avId + self.notify.debug('sending update') + self.sendUpdateToAvatarId(avId, 'avatarInteract', [avId]) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + return 1 + + def rejectAvatar(self, avId): + self.notify.error('rejectAvatar: should not be called by a pet!') + + def d_setMovie(self, avId, flag): + self.sendUpdate('setMovie', [flag, avId, ClockDelta.globalClockDelta.getRealNetworkTime()]) + + def sendClearMovie(self, task = None): + if self.air != None: + self.ignore(self.air.getAvatarExitEvent(self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.busy = 0 + self.d_setMovie(0, PetConstants.PET_MOVIE_CLEAR) + return Task.done + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie() + + def handleAvPetInteraction(self, mode, avId): + if mode not in (PetConstants.PET_MOVIE_SCRATCH, PetConstants.PET_MOVIE_FEED, PetConstants.PET_MOVIE_CALL): + self.air.writeServerEvent('suspicious', avId, 'DistributedPetAI: unknown mode: %s' % mode) + return + if self.avatarInteract(avId): + self.notify.debug('handleAvPetInteraction() avatarInteract calling callback') + self.movieMode = mode + callback = {PetConstants.PET_MOVIE_SCRATCH: self.scratch, + PetConstants.PET_MOVIE_FEED: self.feed, + PetConstants.PET_MOVIE_CALL: self.call}.get(mode) + callback(self.air.doId2do.get(avId)) + else: + self.notify.debug('handleAvPetInteraction() avatarInteract was busy or unhappy') + + def __petMovieStart(self, avId): + self.d_setMovie(avId, self.movieMode) + time = self.movieTimeSwitch.get(self.movieMode) + taskMgr.doMethodLater(time, self.__petMovieComplete, self.uniqueName('PetMovieComplete')) + + def __petMovieComplete(self, task = None): + self.disableLockMover() + self.unlockPet() + self.sendClearMovie() + self.movieMode = None + return Task.done + + def startLockPetMove(self, avId): + self.enableLockMover() + dist_Callable = self.movieDistSwitch.get(self.movieMode) + dist = dist_Callable(self.air.doId2do.get(avId).getStyle().getLegSize()) + self.distList = [0, 0, 0] + self.mover.walkToAvatar(self.air.doId2do[avId], callback=lambda: self.endLockPetMove(avId)) + self.__lockPetMoveTask(avId) + + def getAverageDist(self): + sum = 0 + for i in self.distList: + sum += i + + return sum / 3.0 + + def __lockPetMoveTask(self, avId): + return Task.done + + def endLockPetMove(self, avId): + del self.distList + taskMgr.remove(self.getLockMoveTaskName()) + self.lockPet() + self.__petMovieStart(avId) + + def enableLockMover(self): + if not hasattr(self, 'brain'): + return + + if self.lockMoverEnabled == 0: + self.brain._startMovie() + self.lockMoverEnabled += 1 + + def isLockMoverEnabled(self): + return self.lockMoverEnabled > 0 + + def disableLockMover(self): + if not hasattr(self, 'brain'): + return + + if self.lockMoverEnabled > 0: + self.lockMoverEnabled -= 1 + if self.lockMoverEnabled == 0: + self.brain._endMovie() + + def _willDoTrick(self, trickId): + if self.isContented(): + minApt = PetTricks.MinActualTrickAptitude + maxApt = PetTricks.MaxActualTrickAptitude + else: + minApt = PetTricks.NonHappyMinActualTrickAptitude + maxApt = PetTricks.NonHappyMaxActualTrickAptitude + randVal = random.random() + cutoff = lerp(minApt, maxApt, self.getTrickAptitude(trickId)) + if self.mood.isComponentActive('fatigue'): + cutoff *= 0.5 + cutoff *= PetTricks.TrickAccuracies[trickId] + DistributedPetAI.notify.info('_willDoTrick: %s / %s' % (randVal, cutoff)) # .debug + return randVal < cutoff + + def _handleDidTrick(self, trickId): + DistributedPetAI.notify.debug('_handleDidTrick: %s' % trickId) + if trickId == PetTricks.Tricks.BALK: + return + aptitude = self.getTrickAptitude(trickId) + self.setTrickAptitude(trickId, aptitude + PetTricks.AptitudeIncrementDidTrick) + self.addToMood('fatigue', lerp(PetTricks.MaxTrickFatigue, PetTricks.MinTrickFatigue, aptitude)) + + def _handleGotPositiveTrickFeedback(self, trickId, magnitude): + if trickId == PetTricks.Tricks.BALK: + return + self.setTrickAptitude(trickId, self.getTrickAptitude(trickId) + PetTricks.MaxAptitudeIncrementGotPraise * magnitude) + + def toggleLeash(self, avId): + if self.leashMode: + self.leashMode = 0 + self.leashAvId = None + response = 'leash OFF' + else: + self.leashMode = 1 + self.leashAvId = avId + response = 'leash ON' + return response diff --git a/toontown/pets/DistributedPetProxy.py b/toontown/pets/DistributedPetProxy.py new file mode 100755 index 00000000..0c2268e8 --- /dev/null +++ b/toontown/pets/DistributedPetProxy.py @@ -0,0 +1,189 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.pets import PetTraits +from toontown.pets import PetMood, PetTricks +from toontown.toonbase import ToontownGlobals +import string + +class DistributedPetProxy(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPetProxy') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.__funcsToDelete = [] + self.__generateDistTraitFuncs() + self.__generateDistMoodFuncs() + self.dominantMood = 'neutral' + self.sendGenerateMessage = 0 + self.trickAptitudes = [] + self.ghostMode = False + self.bFake = False + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.traitList = [0] * PetTraits.PetTraits.NumTraits + self.requiredMoodComponents = {} + + def getSetterName(self, valueName, prefix = 'set'): + return '%s%s%s' % (prefix, valueName[0].upper(), valueName[1:]) + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + + def getOwnerId(self): + return self.ownerId + + def setPetName(self, petName): + self.petName = petName + + def setTraitSeed(self, traitSeed): + self.traitSeed = traitSeed + + def setSafeZone(self, safeZone): + self.safeZone = safeZone + + def __generateDistTraitFuncs(self): + for i in xrange(PetTraits.PetTraits.NumTraits): + traitName = PetTraits.getTraitNames()[i] + setterName = self.getSetterName(traitName) + + def traitSetter(value, self = self, i = i): + self.traitList[i] = value + + self.__dict__[setterName] = traitSetter + self.__funcsToDelete.append(setterName) + + def setHead(self, head): + DistributedPetProxy.notify.debug('setHead: %s' % head) + self.head = head + + def setEars(self, ears): + DistributedPetProxy.notify.debug('setEars: %s' % ears) + self.ears = ears + + def setNose(self, nose): + DistributedPetProxy.notify.debug('setNose: %s' % nose) + self.nose = nose + + def setTail(self, tail): + DistributedPetProxy.notify.debug('setTail: %s' % tail) + self.tail = tail + + def setBodyTexture(self, bodyTexture): + DistributedPetProxy.notify.debug('setBodyTexture: %s' % bodyTexture) + self.bodyTexture = bodyTexture + + def setColor(self, color): + DistributedPetProxy.notify.debug('setColor: %s' % color) + self.color = color + + def setColorScale(self, colorScale): + DistributedPetProxy.notify.debug('setColorScale: %s' % colorScale) + self.colorScale = colorScale + + def setEyeColor(self, eyeColor): + DistributedPetProxy.notify.debug('setEyeColor: %s' % eyeColor) + self.eyeColor = eyeColor + + def setGender(self, gender): + DistributedPetProxy.notify.debug('setGender: %s' % gender) + self.gender = gender + + def getDNA(self): + return self.style + + def getName(self): + return self.petName + + def getFont(self): + return ToontownGlobals.getToonFont() + + def setLastSeenTimestamp(self, timestamp): + DistributedPetProxy.notify.debug('setLastSeenTimestamp: %s' % timestamp) + self.lastSeenTimestamp = timestamp + + def getTimeSinceLastSeen(self): + t = self.cr.getServerTimeOfDay() - self.lastSeenTimestamp + return max(0.0, t) + + def updateOfflineMood(self): + self.mood.driftMood(dt=self.getTimeSinceLastSeen(), curMood=self.lastKnownMood) + + def __handleMoodSet(self, component, value): + if self.isGenerated(): + self.mood.setComponent(component, value) + else: + self.requiredMoodComponents[component] = value + + def __generateDistMoodFuncs(self): + for compName in PetMood.PetMood.Components: + setterName = self.getSetterName(compName) + + def moodSetter(value, self = self, compName = compName): + self.__handleMoodSet(compName, value) + + self.__dict__[setterName] = moodSetter + self.__funcsToDelete.append(setterName) + + def setMood(self, *componentValues): + for value, name in zip(componentValues, PetMood.PetMood.Components): + setterName = self.getSetterName(name) + self.__dict__[setterName](value) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.traits = PetTraits.PetTraits(self.traitSeed, self.safeZone) + print self.traits.traits + self.mood = PetMood.PetMood(self) + self.lastKnownMood = self.mood.makeCopy() + for mood, value in self.requiredMoodComponents.items(): + self.mood.setComponent(mood, value, announce=0) + + self.requiredMoodComponents = {} + DistributedPetProxy.notify.debug('time since last seen: %s' % self.getTimeSinceLastSeen()) + self.style = [self.head, + self.ears, + self.nose, + self.tail, + self.bodyTexture, + self.color, + self.colorScale, + self.eyeColor, + self.gender] + self.setLastSeenTimestamp(self.lastSeenTimestamp) + self.updateOfflineMood() + self.sendGenerateMessage = 1 + + def disable(self): + if hasattr(self, 'lastKnownMood'): + self.lastKnownMood.destroy() + del self.lastKnownMood + self.mood.destroy() + del self.mood + del self.traits + DistributedObject.DistributedObject.disable(self) + + def delete(self): + for funcName in self.__funcsToDelete: + del self.__dict__[funcName] + + DistributedObject.DistributedObject.delete(self) + + def setDominantMood(self, dominantMood): + self.dominantMood = dominantMood + if self.sendGenerateMessage == 1: + proxyGenerateMessage = 'petProxy-%d-generated' % self.doId + messenger.send(proxyGenerateMessage) + self.sendGenerateMessage = 0 + + def getDominantMood(self): + return self.dominantMood + + def setTrickAptitudes(self, aptitudes): + self.trickAptitudes = aptitudes + + def isPet(self): + return True + + def isProxy(self): + return True diff --git a/toontown/pets/DistributedPetProxyAI.py b/toontown/pets/DistributedPetProxyAI.py new file mode 100755 index 00000000..fd658abb --- /dev/null +++ b/toontown/pets/DistributedPetProxyAI.py @@ -0,0 +1,469 @@ +from direct.showbase.PythonUtil import contains, lerp +from toontown.toonbase.PythonUtil import clampScalar +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.pets import PetTraits, PetTricks +from toontown.pets import PetMood +from toontown.toonbase import ToontownGlobals +import random +import time +import copy +BATTLE_TRICK_HP_MULTIPLIER = 10.0 + +class DistributedPetProxyAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPetProxyAI') + + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.ownerId = 0 + self.petName = 'unnamed' + self.traitSeed = 0 + self.safeZone = ToontownGlobals.ToontownCentral + self.traitList = [0] * PetTraits.PetTraits.NumTraits + self.head = -1 + self.ears = -1 + self.nose = -1 + self.tail = -1 + self.bodyTexture = 0 + self.color = 0 + self.colorScale = 0 + self.eyeColor = 0 + self.gender = 0 + self.trickAptitudes = [] + self.lastSeenTimestamp = self.getCurEpochTimestamp() + self.requiredMoodComponents = {} + self.__funcsToDelete = [] + self.__generateDistTraitFuncs() + self.__generateDistMoodFuncs() + + def getSetterName(self, valueName, prefix = 'set'): + return '%s%s%s' % (prefix, valueName[0].upper(), valueName[1:]) + + def setDNA(self, dna): + head, ears, nose, tail, body, color, colorScale, eyes, gender = dna + self.b_setHead(head) + self.b_setEars(ears) + self.b_setNose(nose) + self.b_setTail(tail) + self.b_setBodyTexture(body) + self.b_setColor(color) + self.b_setColorScale(colorScale) + self.b_setEyeColor(eyes) + self.b_setGender(gender) + + def getOwnerId(self): + return self.ownerId + + def b_setOwnerId(self, ownerId): + self.d_setOwnerId(ownerId) + self.setOwnerId(ownerId) + + def d_setOwnerId(self, ownerId): + self.sendUpdate('setOwnerId', [ownerId]) + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + + def getPetName(self): + return self.petName + + def b_setPetName(self, petName): + self.d_setPetName(petName) + self.setPetName(petName) + + def d_setPetName(self, petName): + self.sendUpdate('setPetName', [petName]) + + def setPetName(self, petName): + self.petName = petName + + def getTraitSeed(self): + return self.traitSeed + + def b_setTraitSeed(self, traitSeed): + self.d_setTraitSeed(traitSeed) + self.setTraitSeed(traitSeed) + + def d_setTraitSeed(self, traitSeed): + self.sendUpdate('setTraitSeed', [traitSeed]) + + def setTraitSeed(self, traitSeed): + self.traitSeed = traitSeed + + def getSafeZone(self): + return self.safeZone + + def b_setSafeZone(self, safeZone): + self.d_setSafeZone(safeZone) + self.setSafeZone(safeZone) + + def d_setSafeZone(self, safeZone): + self.sendUpdate('setSafeZone', [safeZone]) + + def setSafeZone(self, safeZone): + self.safeZone = safeZone + + def setTraits(self, traitList): + self.traitList = traitList + + def __generateDistTraitFuncs(self): + for i in xrange(PetTraits.PetTraits.NumTraits): + traitName = PetTraits.getTraitNames()[i] + getterName = self.getSetterName(traitName, 'get') + b_setterName = self.getSetterName(traitName, 'b_set') + d_setterName = self.getSetterName(traitName, 'd_set') + setterName = self.getSetterName(traitName) + + def traitGetter(i = i): + return self.traitList[i] + + def b_traitSetter(value, setterName = setterName, d_setterName = d_setterName): + self.__dict__[d_setterName](value) + self.__dict__[setterName](value) + + def d_traitSetter(value, setterName = setterName): + self.sendUpdate(setterName, [value]) + + def traitSetter(value, i = i): + self.traitList[i] = value + + self.__dict__[getterName] = traitGetter + self.__dict__[b_setterName] = b_traitSetter + self.__dict__[d_setterName] = d_traitSetter + self.__dict__[setterName] = traitSetter + self.__funcsToDelete.append(getterName) + self.__funcsToDelete.append(b_setterName) + self.__funcsToDelete.append(d_setterName) + self.__funcsToDelete.append(setterName) + + def getHead(self): + return self.head + + def b_setHead(self, head): + self.d_setHead(head) + self.setHead(head) + + def d_setHead(self, head): + self.sendUpdate('setHead', [head]) + + def setHead(self, head): + self.head = head + + def getEars(self): + return self.ears + + def b_setEars(self, ears): + self.d_setEars(ears) + self.setEars(ears) + + def d_setEars(self, ears): + self.sendUpdate('setEars', [ears]) + + def setEars(self, ears): + self.ears = ears + + def getNose(self): + return self.nose + + def b_setNose(self, nose): + self.d_setNose(nose) + self.setNose(nose) + + def d_setNose(self, nose): + self.sendUpdate('setNose', [nose]) + + def setNose(self, nose): + self.nose = nose + + def getTail(self): + return self.tail + + def b_setTail(self, tail): + self.d_setTail(tail) + self.setTail(tail) + + def d_setTail(self, tail): + self.sendUpdate('setTail', [tail]) + + def setTail(self, tail): + self.tail = tail + + def getBodyTexture(self): + return self.bodyTexture + + def b_setBodyTexture(self, bodyTexture): + self.d_setBodyTexture(bodyTexture) + self.setBodyTexture(bodyTexture) + + def d_setBodyTexture(self, bodyTexture): + self.sendUpdate('setBodyTexture', [bodyTexture]) + + def setBodyTexture(self, bodyTexture): + self.bodyTexture = bodyTexture + + def getColor(self): + return self.color + + def b_setColor(self, color): + self.d_setColor(color) + self.setColor(color) + + def d_setColor(self, color): + self.sendUpdate('setColor', [color]) + + def setColor(self, color): + self.color = color + + def getColorScale(self): + return self.colorScale + + def b_setColorScale(self, colorScale): + self.d_setColorScale(colorScale) + self.setColorScale(colorScale) + + def d_setColorScale(self, colorScale): + self.sendUpdate('setColorScale', [colorScale]) + + def setColorScale(self, colorScale): + self.colorScale = colorScale + + def getEyeColor(self): + return self.eyeColor + + def b_setEyeColor(self, eyeColor): + self.d_setEyeColor(eyeColor) + self.setEyeColor(eyeColor) + + def d_setEyeColor(self, eyeColor): + self.sendUpdate('setEyeColor', [eyeColor]) + + def setEyeColor(self, eyeColor): + self.eyeColor = eyeColor + + def getGender(self): + return self.gender + + def b_setGender(self, gender): + self.d_setGender(gender) + self.setGender(gender) + + def d_setGender(self, gender): + self.sendUpdate('setGender', [gender]) + + def setGender(self, gender): + self.gender = gender + + def getLastSeenTimestamp(self): + return self.lastSeenTimestamp + + def b_setLastSeenTimestamp(self, timestamp): + self.d_setLastSeenTimestamp(timestamp) + self.setLastSeenTimestamp(timestamp) + + def d_setLastSeenTimestamp(self, timestamp): + self.sendUpdate('setLastSeenTimestamp', [timestamp]) + + def setLastSeenTimestamp(self, timestamp): + self.lastSeenTimestamp = timestamp + + def getCurEpochTimestamp(self): + return int(time.time()) + + def getTimeSinceLastSeen(self): + t = time.time() - self.lastSeenTimestamp + return max(0.0, t) + + def __handleMoodSet(self, component, value): + if self.isGenerated(): + self.mood.setComponent(component, value) + else: + self.requiredMoodComponents[component] = value + + def __handleMoodGet(self, component): + if self.isGenerated(): + return self.mood.getComponent(component) + else: + return 0.0 + + def __generateDistMoodFuncs(self): + for compName in PetMood.PetMood.Components: + getterName = self.getSetterName(compName, 'get') + setterName = self.getSetterName(compName) + + def moodGetter(compName = compName): + return self.__handleMoodGet(compName) + + def b_moodSetter(value, setterName = setterName): + self.__dict__[setterName](value) + + def d_moodSetter(value, setterName = setterName): + self.sendUpdate(setterName, [value]) + + def moodSetter(value, compName = compName): + self.__handleMoodSet(compName, value) + + self.__dict__[getterName] = moodGetter + self.__dict__['b_%s' % setterName] = b_moodSetter + self.__dict__['d_%s' % setterName] = d_moodSetter + self.__dict__[setterName] = moodSetter + self.__funcsToDelete.append(getterName) + self.__funcsToDelete.append('b_%s' % setterName) + self.__funcsToDelete.append('d_%s' % setterName) + self.__funcsToDelete.append(setterName) + + def getTrickAptitudes(self): + return self.trickAptitudes + + def b_setTrickAptitudes(self, aptitudes): + self.setTrickAptitudes(aptitudes, local=1) + self.d_setTrickAptitudes(aptitudes) + + def d_setTrickAptitudes(self, aptitudes): + while len(aptitudes) < len(PetTricks.Tricks) - 1: + aptitudes.append(0.0) + + self.sendUpdate('setTrickAptitudes', [aptitudes]) + + def setTrickAptitudes(self, aptitudes, local = 0): + if not local: + DistributedPetProxyAI.notify.debug('setTrickAptitudes: %s' % aptitudes) + while len(self.trickAptitudes) < len(PetTricks.Tricks) - 1: + self.trickAptitudes.append(0.0) + + self.trickAptitudes = aptitudes + + def getTrickAptitude(self, trickId): + if trickId > len(self.trickAptitudes) - 1: + return 0.0 + return self.trickAptitudes[trickId] + + def setTrickAptitude(self, trickId, aptitude, send = 1): + aptitude = clampScalar(aptitude, 0.0, 1.0) + aptitudes = self.trickAptitudes + while len(aptitudes) - 1 < trickId: + aptitudes.append(0.0) + + if aptitudes[trickId] != aptitude: + aptitudes[trickId] = aptitude + if send: + self.b_setTrickAptitudes(aptitudes) + else: + self.setTrickAptitudes(aptitudes, local=1) + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.traits = PetTraits.PetTraits(self.traitSeed, self.safeZone) + print self.traits.traits + for i in xrange(len(self.traitList)): + value = self.traitList[i] + if value == 0.0: + traitName = PetTraits.getTraitNames()[i] + traitValue = self.traits.getTraitValue(traitName) + DistributedPetProxyAI.notify.info("%s: initializing new trait '%s' to %s, seed=%s" % (self.doId, + traitName, + traitValue, + self.traitSeed)) + setterName = self.getSetterName(traitName, 'b_set') + self.__dict__[setterName](traitValue) + + self.mood = PetMood.PetMood(self) + for mood, value in self.requiredMoodComponents.items(): + self.mood.setComponent(mood, value, announce=0) + + self.requiredMoodComponents = {} + self.accept(self.mood.getMoodChangeEvent(), self.handleMoodChange) + self.mood.start() + + def broadcastDominantMood(self): + self.d_setDominantMood(self.mood.getDominantMood()) + + def delete(self): + self.ignore(self.mood.getMoodChangeEvent()) + self.mood.destroy() + del self.mood + del self.traits + for funcName in self.__funcsToDelete: + del self.__dict__[funcName] + + DistributedObjectAI.DistributedObjectAI.delete(self) + + def setMoodComponent(self, component, value): + setter = self.getSetterName(component, 'b_set') + self.__dict__[setter](value) + + def addToMood(self, component, delta): + value = self.mood.getComponent(component) + value += delta + self.setMoodComponent(component, clampScalar(value, 0.0, 1.0)) + + def lerpMood(self, component, factor): + curVal = self.mood.getComponent(component) + if factor < 0: + self.setMoodComponent(component, lerp(curVal, 0.0, -factor)) + else: + self.setMoodComponent(component, lerp(curVal, 1.0, factor)) + + def addToMoods(self, mood2delta): + for mood, delta in mood2delta.items(): + self.addToMood(mood, delta) + + def lerpMoods(self, mood2factor): + for mood, factor in mood2factor.items(): + self.lerpMood(mood, factor) + + def isContented(self): + return self.mood.getDominantMood() in PetMood.PetMood.ContentedMoods + + def _willDoTrick(self, trickId): + if self.isContented(): + minApt = PetTricks.MinActualTrickAptitude + maxApt = PetTricks.MaxActualTrickAptitude + else: + minApt = PetTricks.NonHappyMinActualTrickAptitude + maxApt = PetTricks.NonHappyMaxActualTrickAptitude + randVal = random.random() + cutoff = lerp(minApt, maxApt, self.getTrickAptitude(trickId)) + if self.mood.isComponentActive('fatigue'): + cutoff *= 0.5 + cutoff *= PetTricks.TrickAccuracies[trickId] + DistributedPetProxyAI.notify.debug('_willDoTrick: %s / %s' % (randVal, cutoff)) + return randVal < cutoff + + def _handleDidTrick(self, trickId): + DistributedPetProxyAI.notify.debug('_handleDidTrick: %s' % trickId) + if trickId == PetTricks.Tricks.BALK: + return + aptitude = self.getTrickAptitude(trickId) + self.setTrickAptitude(trickId, aptitude + PetTricks.AptitudeIncrementDidTrick) + self.addToMood('fatigue', lerp(PetTricks.MaxTrickFatigue, PetTricks.MinTrickFatigue, aptitude)) + self.d_setDominantMood(self.mood.getDominantMood()) + + def attemptBattleTrick(self, trickId): + self.lerpMoods({'boredom': -.1, + 'excitement': 0.05, + 'loneliness': -.05}) + if self._willDoTrick(trickId): + self._handleDidTrick(trickId) + self.b_setLastSeenTimestamp(self.getCurEpochTimestamp()) + return 0 + else: + self.b_setLastSeenTimestamp(self.getCurEpochTimestamp()) + return 1 + + def handleMoodChange(self, components = [], distribute = 1): + if len(components) == 0: + components = PetMood.PetMood.Components + if distribute: + if len(components) == len(PetMood.PetMood.Components): + values = [] + for comp in PetMood.PetMood.Components: + values.append(self.mood.getComponent(comp)) + + self.sendUpdate('setMood', values) + else: + for comp in components: + setter = self.getSetterName(comp, 'd_set') + self.__dict__[setter](self.mood.getComponent(comp)) + + def d_setDominantMood(self, dominantMood): + self.sendUpdate('setDominantMood', [dominantMood]) diff --git a/toontown/pets/Pet.py b/toontown/pets/Pet.py new file mode 100755 index 00000000..6ccaf0d8 --- /dev/null +++ b/toontown/pets/Pet.py @@ -0,0 +1,675 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.fsm.ClassicFSM import * +from direct.fsm.State import * +from direct.distributed.ClockDelta import globalClockDelta +from otp.avatar import Avatar +from direct.actor import Actor +from direct.task import Task +from toontown.pets import PetDNA +from PetDNA import HeadParts, EarParts, NoseParts, TailParts, BodyTypes, BodyTextures, AllPetColors, getColors, ColorScales, PetEyeColors, EarTextures, TailTextures, getFootTexture, getEarTexture, GiraffeTail, LeopardTail, PetGenders +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from direct.showbase import PythonUtil +import random +import types +Component2IconDict = {'boredom': 'Bored', + 'restlessness': None, + 'playfulness': 'Play', + 'loneliness': 'Lonely', + 'sadness': 'Sad', + 'fatigue': 'Sleepy', + 'hunger': 'Hungry', + 'confusion': 'Confused', + 'excitement': 'Surprised', + 'anger': 'Angry', + 'surprise': 'Surprised', + 'affection': 'Love'} + +from otp.nametag import * +from otp.nametag.NametagConstants import * +from otp.nametag.NametagGroup import * + +class Pet(Avatar.Avatar): + notify = DirectNotifyGlobal.directNotify.newCategory('Pet') + SerialNum = 0 + Interactions = PythonUtil.Enum('SCRATCH, BEG, EAT, NEUTRAL') + InteractAnims = {Interactions.SCRATCH: ('toPet', 'pet', 'fromPet'), + Interactions.BEG: ('toBeg', 'beg', 'fromBeg'), + Interactions.EAT: ('eat', 'swallow', 'neutral'), + Interactions.NEUTRAL: 'neutral'} + + def __init__(self, forGui = 0): + Avatar.Avatar.__init__(self) + self.serialNum = Pet.SerialNum + Pet.SerialNum += 1 + self.lockedDown = 0 + self.setPickable(1) + self.setPlayerType(NametagGroup.CCSpeedChat) + self.animFSM = ClassicFSM('petAnimFSM', [State('off', self.enterOff, self.exitOff), + State('neutral', self.enterNeutral, self.exitNeutral), + State('neutralHappy', self.enterNeutralHappy, self.exitNeutralHappy), + State('neutralSad', self.enterNeutralSad, self.exitNeutralSad), + State('run', self.enterRun, self.exitRun), + State('swim', self.enterSwim, self.exitSwim), + State('teleportIn', self.enterTeleportIn, self.exitTeleportOut), + State('teleportOut', self.enterTeleportOut, self.exitTeleportOut), + State('walk', self.enterWalk, self.exitWalk), + State('walkHappy', self.enterWalkHappy, self.exitWalkHappy), + State('walkSad', self.enterWalkSad, self.exitWalkSad)], 'off', 'off') + self.animFSM.enterInitialState() + self.forGui = forGui + self.moodModel = None + self.__blinkName = 'petblink-' + str(self.this) + self.track = None + self.soundBackflip = None + self.soundRollover = None + self.soundPlaydead = None + self.soundTeleportIn = None + self.soundTeleportOut = None + self.teleportHole = None + return + + def isPet(self): + return True + + def stopAnimations(self): + if self.track: + self.track.pause() + self.stopBlink() + self.animFSM.request('off') + + def delete(self): + self.stopAnimations() + self.track = None + self.soundBackflip = None + self.soundRollover = None + self.soundPlaydead = None + self.soundTeleportIn = None + self.soundTeleportOut = None + if self.teleportHole: + self.teleportHole.cleanup() + self.teleportHole = None + self.eyesOpenTexture = None + self.eyesClosedTexture = None + self.animFSM = None + self.eyes = None + self.rightPupil = None + self.rightHighlight = None + self.rightBrow = None + self.leftPupil = None + self.leftHighlight = None + self.leftBrow = None + self.color = None + Avatar.Avatar.delete(self) + return + + def getDNA(self): + return self.style + + def setDNA(self, dna): + if self.style: + pass + else: + self.style = dna + self.generatePet() + self.initializeDropShadow() + self.initializeNametag3d() + self.generateMoods() + self.dropShadow.setScale(0.75) + + def generatePet(self): + self.loadModel('phase_4/models/char/TT_pets-mod') + self.loadAnims({'toBeg': 'phase_5/models/char/TT_pets-intoBeg', + 'beg': 'phase_5/models/char/TT_pets-beg', + 'fromBeg': 'phase_5/models/char/TT_pets-begOut', + 'backflip': 'phase_5/models/char/TT_pets-backflip', + 'dance': 'phase_5/models/char/TT_pets-heal', + 'toDig': 'phase_5/models/char/TT_pets-intoDig', + 'dig': 'phase_5/models/char/TT_pets-dig', + 'fromDig': 'phase_5/models/char/TT_pets-digToNeutral', + 'disappear': 'phase_5/models/char/TT_pets-disappear', + 'eat': 'phase_5.5/models/char/TT_pets-eat', + 'jump': 'phase_5/models/char/TT_pets-jump', + 'neutral': 'phase_4/models/char/TT_pets-neutral', + 'neutralHappy': 'phase_4/models/char/TT_pets-neutralHappy', + 'neutralSad': 'phase_4/models/char/TT_pets-neutral_sad', + 'toPet': 'phase_5.5/models/char/TT_pets-petin', + 'pet': 'phase_5.5/models/char/TT_pets-petloop', + 'fromPet': 'phase_5.5/models/char/TT_pets-petend', + 'playDead': 'phase_5/models/char/TT_pets-playdead', + 'fromPlayDead': 'phase_5/models/char/TT_pets-deadend', + 'reappear': 'phase_5/models/char/TT_pets-reappear', + 'run': 'phase_5.5/models/char/TT_pets-run', + 'rollover': 'phase_5/models/char/TT_pets-rollover', + 'walkSad': 'phase_5.5/models/char/TT_pets-sadwalk', + 'speak': 'phase_5/models/char/TT_pets-speak', + 'swallow': 'phase_5.5/models/char/TT_pets-swallow', + 'swim': 'phase_5.5/models/char/TT_pets-swim', + 'toBall': 'phase_5.5/models/char/TT_pets-toBall', + 'walk': 'phase_5.5/models/char/TT_pets-walk', + 'walkHappy': 'phase_5.5/models/char/TT_pets-walkHappy'}) + self.setHeight(2) + color = None + colorIndex = self.style[5] + color = AllPetColors[colorIndex] + self.color = color + bodyType = self.style[4] + body = self.find('**/body') + tex = loader.loadTexture(BodyTextures[BodyTypes[bodyType]]) + tex.setMinfilter(Texture.FTLinear) + tex.setMagfilter(Texture.FTLinear) + body.setTexture(tex, 1) + body.setColor(color) + leftFoot = self.find('**/leftFoot') + rightFoot = self.find('**/rightFoot') + texName = getFootTexture(bodyType) + tex = loader.loadTexture(texName) + tex.setMinfilter(Texture.FTLinear) + tex.setMagfilter(Texture.FTLinear) + leftFoot.setTexture(tex, 1) + rightFoot.setTexture(tex, 1) + leftFoot.setColor(color) + rightFoot.setColor(color) + for part in HeadParts + EarParts + NoseParts + TailParts: + self.find('**/' + part).stash() + + colorScale = ColorScales[self.style[6]] + partColor = self.amplifyColor(color, colorScale) + headIndex = self.style[0] + if headIndex != -1: + head = self.find('**/@@' + HeadParts[headIndex]) + head.setColor(partColor) + head.unstash() + earsIndex = self.style[1] + if earsIndex != -1: + ears = self.find('**/@@' + EarParts[earsIndex]) + ears.setColor(partColor) + texName = getEarTexture(bodyType, EarParts[earsIndex]) + if texName: + tex = loader.loadTexture(texName) + tex.setMinfilter(Texture.FTLinear) + tex.setMagfilter(Texture.FTLinear) + ears.setTexture(tex, 1) + ears.unstash() + noseIndex = self.style[2] + if noseIndex != -1: + nose = self.find('**/@@' + NoseParts[noseIndex]) + nose.setColor(partColor) + nose.unstash() + tailIndex = self.style[3] + if tailIndex != -1: + tail = self.find('**/@@' + TailParts[tailIndex]) + tail.setColor(partColor) + texName = TailTextures[TailParts[tailIndex]] + if texName: + if BodyTypes[bodyType] == 'giraffe': + texName = GiraffeTail + elif BodyTypes[bodyType] == 'leopard': + texName = LeopardTail + tex = loader.loadTexture(texName) + tex.setMinfilter(Texture.FTLinear) + tex.setMagfilter(Texture.FTLinear) + tail.setTexture(tex, 1) + tail.unstash() + if not self.forGui: + self.drawInFront('eyeWhites', 'body', 1) + self.drawInFront('rightPupil', 'eyeWhites', 2) + self.drawInFront('leftPupil', 'eyeWhites', 2) + self.drawInFront('rightHighlight', 'rightPupil', 3) + self.drawInFront('leftHighlight', 'leftPupil', 3) + else: + self.drawInFront('eyeWhites', 'body', -2) + self.drawInFront('rightPupil', 'eyeWhites', -2) + self.drawInFront('leftPupil', 'eyeWhites', -2) + self.find('**/rightPupil').adjustAllPriorities(1) + self.find('**/leftPupil').adjustAllPriorities(1) + eyes = self.style[7] + eyeColor = PetEyeColors[eyes] + self.eyes = self.find('**/eyeWhites') + self.rightPupil = self.find('**/rightPupil') + self.leftPupil = self.find('**/leftPupil') + self.rightHighlight = self.find('**/rightHighlight') + self.leftHighlight = self.find('**/leftHighlight') + self.rightBrow = self.find('**/rightBrow') + self.leftBrow = self.find('**/leftBrow') + self.eyes.setColor(1, 1, 1, 1) + self.rightPupil.setColor(eyeColor, 2) + self.leftPupil.setColor(eyeColor, 2) + self.rightHighlight.setColor(1, 1, 1, 1) + self.leftHighlight.setColor(1, 1, 1, 1) + self.rightBrow.setColor(0, 0, 0, 1) + self.leftBrow.setColor(0, 0, 0, 1) + self.eyes.setTwoSided(1, 1) + self.rightPupil.setTwoSided(1, 1) + self.leftPupil.setTwoSided(1, 1) + self.rightHighlight.setTwoSided(1, 1) + self.leftHighlight.setTwoSided(1, 1) + self.rightBrow.setTwoSided(1, 1) + self.leftBrow.setTwoSided(1, 1) + if self.forGui: + self.rightHighlight.hide() + self.leftHighlight.hide() + if self.style[8]: + self.eyesOpenTexture = loader.loadTexture('phase_4/maps/BeanEyeBoys2.jpg', 'phase_4/maps/BeanEyeBoys2_a.rgb') + self.eyesClosedTexture = loader.loadTexture('phase_4/maps/BeanEyeBoysBlink.jpg', 'phase_4/maps/BeanEyeBoysBlink_a.rgb') + else: + self.eyesOpenTexture = loader.loadTexture('phase_4/maps/BeanEyeGirlsNew.jpg', 'phase_4/maps/BeanEyeGirlsNew_a.rgb') + self.eyesClosedTexture = loader.loadTexture('phase_4/maps/BeanEyeGirlsBlinkNew.jpg', 'phase_4/maps/BeanEyeGirlsBlinkNew_a.rgb') + self.eyesOpenTexture.setMinfilter(Texture.FTLinear) + self.eyesOpenTexture.setMagfilter(Texture.FTLinear) + self.eyesClosedTexture.setMinfilter(Texture.FTLinear) + self.eyesClosedTexture.setMagfilter(Texture.FTLinear) + self.eyesOpen() + return None + + def initializeBodyCollisions(self, collIdStr): + Avatar.Avatar.initializeBodyCollisions(self, collIdStr) + if not self.ghostMode: + self.collNode.setCollideMask(self.collNode.getIntoCollideMask() | ToontownGlobals.PieBitmask) + + def amplifyColor(self, color, scale): + color = color * scale + for i in (0, 1, 2): + if color[i] > 1.0: + color.setCell(i, 1.0) + + return color + + def generateMoods(self): + nodePath = NodePath(self.nametag.getNameIcon()) + + if not nodePath: + return + + moodIcons = loader.loadModel('phase_4/models/char/petEmotes') + self.moodIcons = nodePath.attachNewNode('moodIcons') + self.moodIcons.setScale(6.0) + self.moodIcons.setZ(3.5) + + moods = moodIcons.findAllMatches('**/+GeomNode') + for moodNum in xrange(0, moods.getNumPaths()): + mood = moods.getPath(moodNum) + mood.reparentTo(self.moodIcons) + mood.setBillboardPointEye() + mood.hide() + + moodIcons.removeNode() + + def clearMood(self): + if self.moodModel: + self.moodModel.hide() + self.moodModel = None + return + + def showMood(self, mood): + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.APRIL_TOONS_WEEK) and mood != 'confusion': + self.speakMood(mood) + else: + self.clearChat() + mood = Component2IconDict[mood] + if mood is None: + moodModel = None + else: + moodModel = self.moodIcons.find('**/*' + mood + '*') + if moodModel.isEmpty(): + self.notify.warning('No such mood!: %s' % mood) + return + if self.moodModel == moodModel: + return + if self.moodModel: + self.moodModel.hide() + self.moodModel = moodModel + if self.moodModel: + self.moodModel.show() + return + + def speakMood(self, mood): + self.setChatAbsolute(random.choice(TTLocalizer.SpokenMoods[mood]), CFSpeech) + + def getGenderString(self): + if self.style: + if self.style[8]: + return TTLocalizer.GenderShopBoyButtonText + else: + return TTLocalizer.GenderShopGirlButtonText + + def getShadowJoint(self): + if hasattr(self, 'shadowJoint'): + return self.shadowJoint + shadowJoint = self.find('**/attachShadow') + if shadowJoint.isEmpty(): + self.shadowJoint = self + else: + self.shadowJoint = shadowJoint + return self.shadowJoint + + def getNametagJoints(self): + joints = [] + bundle = self.getPartBundle('modelRoot') + joint = bundle.findChild('attachNametag') + if joint: + joints.append(joint) + return joints + + def fitAndCenterHead(self, maxDim, forGui = 0): + p1 = Point3() + p2 = Point3() + self.calcTightBounds(p1, p2) + if forGui: + h = 180 + t = p1[0] + p1.setX(-p2[0]) + p2.setX(-t) + self.getGeomNode().setDepthWrite(1) + self.getGeomNode().setDepthTest(1) + else: + h = 0 + d = p2 - p1 + biggest = max(d[0], d[2]) + s = (maxDim + 0.0) / biggest + mid = (p1 + d / 2.0) * s + self.setPosHprScale(-mid[0], -mid[1] + 1, -mid[2], h, 0, 0, s, s, s) + + def makeRandomPet(self): + dna = PetDNA.getRandomPetDNA() + self.setDNA(dna) + + def enterOff(self): + self.stop() + + def exitOff(self): + pass + + def enterBall(self): + self.setPlayRate(1, 'toBall') + self.play('toBall') + + def exitBall(self): + self.setPlayRate(-1, 'toBall') + self.play('toBall') + + def enterBackflip(self): + self.play('backflip') + + def exitBackflip(self): + self.stop('backflip') + + def enterBeg(self): + delay = self.getDuration('toBeg') + self.track = Sequence(Func(self.play, 'toBeg'), Wait(delay), Func(self.loop, 'beg')) + self.track.start() + + def exitBeg(self): + self.track.pause() + self.play('fromBeg') + + def enterEat(self): + self.loop('eat') + + def exitEat(self): + self.stop('swallow') + + def enterDance(self): + self.loop('dance') + + def exitDance(self): + self.stop('dance') + + def enterNeutral(self): + anim = 'neutral' + self.pose(anim, random.choice(range(0, self.getNumFrames(anim)))) + self.loop(anim, restart=0) + + def exitNeutral(self): + self.stop('neutral') + + def enterNeutralHappy(self): + anim = 'neutralHappy' + self.pose(anim, random.choice(range(0, self.getNumFrames(anim)))) + self.loop(anim, restart=0) + + def exitNeutralHappy(self): + self.stop('neutralHappy') + + def enterNeutralSad(self): + anim = 'neutralSad' + self.pose(anim, random.choice(range(0, self.getNumFrames(anim)))) + self.loop(anim, restart=0) + + def exitNeutralSad(self): + self.stop('neutralSad') + + def enterRun(self): + self.loop('run') + + def exitRun(self): + self.stop('run') + + def enterSwim(self): + self.loop('swim') + + def exitSwim(self): + self.stop('swim') + + def getTeleportInTrack(self): + if not self.teleportHole: + self.teleportHole = Actor.Actor('phase_3.5/models/props/portal-mod', {'hole': 'phase_3.5/models/props/portal-chan'}) + track = Sequence(Wait(1.0), Parallel(self.getTeleportInSoundInterval(), Sequence(Func(self.showHole), ActorInterval(self.teleportHole, 'hole', startFrame=81, endFrame=71), ActorInterval(self, 'reappear'), ActorInterval(self.teleportHole, 'hole', startFrame=71, endFrame=81), Func(self.cleanupHole), Func(self.loop, 'neutral')), Sequence(Func(self.dropShadow.hide), Wait(1.0), Func(self.dropShadow.show)))) + return track + + def enterTeleportIn(self, timestamp): + self.track = self.getTeleportInTrack() + self.track.start(globalClockDelta.localElapsedTime(timestamp)) + + def exitTeleportIn(self): + self.track.pause() + + def getTeleportOutTrack(self): + if not self.teleportHole: + self.teleportHole = Actor.Actor('phase_3.5/models/props/portal-mod', {'hole': 'phase_3.5/models/props/portal-chan'}) + track = Sequence(Wait(1.0), Parallel(self.getTeleportOutSoundInterval(), Sequence(ActorInterval(self, 'toDig'), Parallel(ActorInterval(self, 'dig'), Func(self.showHole), ActorInterval(self.teleportHole, 'hole', startFrame=81, endFrame=71)), ActorInterval(self, 'disappear'), ActorInterval(self.teleportHole, 'hole', startFrame=71, endFrame=81), Func(self.cleanupHole)), Sequence(Wait(1.0), Func(self.dropShadow.hide)))) + return track + + def enterTeleportOut(self, timestamp): + self.track = self.getTeleportOutTrack() + self.track.start(globalClockDelta.localElapsedTime(timestamp)) + + def exitTeleportOut(self): + self.track.pause() + + def showHole(self): + if self.teleportHole: + self.teleportHole.setBin('shadow', 0) + self.teleportHole.setDepthTest(0) + self.teleportHole.setDepthWrite(0) + self.teleportHole.reparentTo(self) + self.teleportHole.setScale(0.75) + self.teleportHole.setPos(0, -1, 0) + + def cleanupHole(self): + if self.teleportHole: + self.teleportHole.reparentTo(hidden) + self.teleportHole.clearBin() + self.teleportHole.clearDepthTest() + self.teleportHole.clearDepthWrite() + + def getTeleportInSoundInterval(self): + if not self.soundTeleportIn: + self.soundTeleportIn = loader.loadSfx('phase_5/audio/sfx/teleport_reappear.ogg') + return SoundInterval(self.soundTeleportIn) + + def getTeleportOutSoundInterval(self): + if not self.soundTeleportOut: + self.soundTeleportOut = loader.loadSfx('phase_5/audio/sfx/teleport_disappear.ogg') + return SoundInterval(self.soundTeleportOut) + + def enterWalk(self): + self.loop('walk') + + def exitWalk(self): + self.stop('walk') + + def enterWalkHappy(self): + self.loop('walkHappy') + + def exitWalkHappy(self): + self.stop('walkHappy') + + def enterWalkSad(self): + self.loop('walkSad') + + def exitWalkSad(self): + self.stop('walkSad') + + def trackAnimToSpeed(self, forwardVel, rotVel, inWater = 0): + action = 'neutral' + if self.isInWater(): + action = 'swim' + elif forwardVel > 0.1 or abs(rotVel) > 0.1: + action = 'walk' + self.setAnimWithMood(action) + + def setAnimWithMood(self, action): + how = '' + if self.isExcited(): + how = 'Happy' + elif self.isSad(): + how = 'Sad' + if action == 'swim': + anim = action + else: + anim = '%s%s' % (action, how) + if anim != self.animFSM.getCurrentState().getName(): + self.animFSM.request(anim) + + def isInWater(self): + return 0 + + def isExcited(self): + return 0 + + def isSad(self): + return 0 + + def startTrackAnimToSpeed(self): + self.lastPos = self.getPos(render) + self.lastH = self.getH(render) + taskMgr.add(self._trackAnimTask, self.getTrackAnimTaskName()) + + def stopTrackAnimToSpeed(self): + taskMgr.remove(self.getTrackAnimTaskName()) + del self.lastPos + del self.lastH + + def getTrackAnimTaskName(self): + return 'trackPetAnim-%s' % self.serialNum + + def _trackAnimTask(self, task): + curPos = self.getPos(render) + curH = self.getH(render) + self.trackAnimToSpeed(curPos - self.lastPos, curH - self.lastH) + self.lastPos = curPos + self.lastH = curH + return Task.cont + + def __blinkOpen(self, task): + self.eyesOpen() + r = random.random() + if r < 0.1: + t = 0.2 + else: + t = r * 4.0 + 1 + taskMgr.doMethodLater(t, self.__blinkClosed, self.__blinkName) + return Task.done + + def __blinkClosed(self, task): + self.eyesClose() + taskMgr.doMethodLater(0.125, self.__blinkOpen, self.__blinkName) + return Task.done + + def startBlink(self): + taskMgr.remove(self.__blinkName) + self.eyesOpen() + t = random.random() * 4.0 + 1 + taskMgr.doMethodLater(t, self.__blinkClosed, self.__blinkName) + + def stopBlink(self): + taskMgr.remove(self.__blinkName) + self.eyesOpen() + + def eyesOpen(self): + self.eyes.setColor(1, 1, 1, 1) + self.eyes.setTexture(self.eyesOpenTexture, 1) + self.rightPupil.show() + self.leftPupil.show() + if not self.forGui: + self.rightHighlight.show() + self.leftHighlight.show() + + def eyesClose(self): + self.eyes.setColor(self.color) + self.eyes.setTexture(self.eyesClosedTexture, 1) + self.rightPupil.hide() + self.leftPupil.hide() + if not self.forGui: + self.rightHighlight.hide() + self.leftHighlight.hide() + + def lockPet(self): + if not self.lockedDown: + self.prevAnimState = self.animFSM.getCurrentState().getName() + self.animFSM.request('neutral') + self.lockedDown += 1 + + def isLockedDown(self): + return self.lockedDown != 0 + + def unlockPet(self): + self.lockedDown -= 1 + if not self.lockedDown: + self.animFSM.request(self.prevAnimState) + self.prevAnimState = None + return + + def getInteractIval(self, interactId): + anims = self.InteractAnims[interactId] + if type(anims) == types.StringType: + animIval = ActorInterval(self, anims) + else: + animIval = Sequence() + for anim in anims: + animIval.append(ActorInterval(self, anim)) + + return animIval + + +def gridPets(): + pets = [] + offsetX = 0 + offsetY = 0 + startPos = base.localAvatar.getPos() + for body in xrange(0, len(BodyTypes)): + colors = getColors(body) + for color in colors: + p = Pet() + p.setDNA([random.choice(range(-1, len(HeadParts))), + random.choice(range(-1, len(EarParts))), + random.choice(range(-1, len(NoseParts))), + random.choice(range(-1, len(TailParts))), + body, + color, + random.choice(range(-1, len(ColorScales))), + random.choice(range(0, len(PetEyeColors))), + random.choice(range(0, len(PetGenders)))]) + p.setPos(startPos[0] + offsetX, startPos[1] + offsetY, startPos[2]) + p.animFSM.request('neutral') + p.reparentTo(render) + pets.append(p) + offsetX += 3 + + offsetY += 3 + offsetX = 0 + + return pets diff --git a/toontown/pets/PetActionFSM.py b/toontown/pets/PetActionFSM.py new file mode 100755 index 00000000..6566e70f --- /dev/null +++ b/toontown/pets/PetActionFSM.py @@ -0,0 +1,84 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import FSM +from direct.interval.IntervalGlobal import * +from direct.task import Task +from direct.distributed.ClockDelta import globalClockDelta +from direct.showbase.PythonUtil import lerp +from toontown.pets import PetTricks +from toontown.toon import DistributedToonAI + +class PetActionFSM(FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('PetActionFSM') + + def __init__(self, pet): + FSM.FSM.__init__(self, PetActionFSM.__name__) + self.pet = pet + self.trickSerialNum = 0 + + def destroy(self): + self.cleanup() + + def enterChase(self, target): + PetActionFSM.notify.debug('enterChase: %s' % target) + self.pet.unstickFSM.request('on') + + def exitChase(self): + self.pet.unstickFSM.request('off') + + def enterFlee(self, chaser): + PetActionFSM.notify.debug('enterFlee: %s' % chaser) + self.pet.unstickFSM.request('on') + + def exitFlee(self): + self.pet.unstickFSM.request('off') + + def enterInspectSpot(self, spot): + PetActionFSM.notify.debug('enterInspectSpot') + self.pet.unstickFSM.request('on') + + def exitInspectSpot(self): + self.pet.unstickFSM.request('off') + + def enterHeal(self, avatar): + PetActionFSM.notify.debug('enterHeal') + avatar.toonUp(3) + + def enterTrick(self, avatar, trickId): + PetActionFSM.notify.debug('enterTrick') + if not self.pet.isLockedDown(): + self.pet.lockPet() + self.pet.sendUpdate('doTrick', [trickId, globalClockDelta.getRealNetworkTime()]) + + def finish(avatar = avatar, trickId = trickId, self = self): + if hasattr(self.pet, 'brain'): + healRange = PetTricks.TrickHeals[trickId] + aptitude = self.pet.getTrickAptitude(trickId) + healAmt = int(lerp(healRange[0], healRange[1], aptitude)) + if healAmt: + for avId, av in self.pet._getFullNearbyToonDict().items(): + if isinstance(av, DistributedToonAI.DistributedToonAI): + av.toonUp(healAmt) + + self.pet._handleDidTrick(trickId) + if not self.pet.isLockedDown(): + self.pet.unlockPet() + messenger.send(self.getTrickDoneEvent()) + + self.trickDoneEvent = 'trickDone-%s-%s' % (self.pet.doId, self.trickSerialNum) + self.trickSerialNum += 1 + self.trickFinishIval = Sequence(WaitInterval(PetTricks.TrickLengths[trickId]), Func(finish), name='petTrickFinish-%s' % self.pet.doId) + self.trickFinishIval.start() + + def getTrickDoLaterName(self): + return 'petTrickDoLater-%s' % self.pet.doId + + def getTrickDoneEvent(self): + return self.trickDoneEvent + + def exitTrick(self): + if self.trickFinishIval.isPlaying(): + self.trickFinishIval.finish() + del self.trickFinishIval + if self.pet.isLockedDown(): + self.pet.unlockPet() + del self.trickDoneEvent diff --git a/toontown/pets/PetAvatarPanel.py b/toontown/pets/PetAvatarPanel.py new file mode 100755 index 00000000..131f091e --- /dev/null +++ b/toontown/pets/PetAvatarPanel.py @@ -0,0 +1,302 @@ +from panda3d.core import * +from direct.directnotify.DirectNotifyGlobal import * +from direct.gui.DirectGui import * +from direct.showbase import DirectObject +from direct.showbase.PythonUtil import Functor +from direct.task.Task import Task +from direct.distributed import DistributedObject +from otp.avatar import Avatar, AvatarPanel +from toontown.toon import ToonHead +from toontown.toon import LaffMeter +from toontown.toon import ToonAvatarDetailPanel +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.pets import Pet, PetConstants, PetDetailPanel + +class PetAvatarPanel(AvatarPanel.AvatarPanel): + notify = directNotify.newCategory('PetAvatarPanel') + + def __init__(self, avatar): + self.notify.debug('Init(): doId=%s' % avatar.doId) + distPet = base.cr.doId2do.get(avatar.doId) + if distPet and not distPet.isProxy(): + self.avatar = distPet + self.petIsLocal = True + else: + self.avatar = avatar + self.petIsLocal = False + from toontown.friends import FriendsListPanel + AvatarPanel.AvatarPanel.__init__(self, self.avatar, FriendsListPanel=FriendsListPanel) + base.localAvatar.obscureFriendsListButton(1) + base.panel = self + gui = loader.loadModel('phase_3.5/models/gui/PetControlPannel') + guiScale = 0.116 + guiPos = (-0.213, 0, -0.70) + self.frame = DirectFrame(parent=base.a2dTopRight, image=gui, scale=guiScale, pos=guiPos, relief=None) + disabledImageColor = Vec4(0.6, 0.6, 0.6, 1) + text0Color = Vec4(1, 1, 1, 1) + text1Color = Vec4(0.5, 1, 0.5, 1) + text2Color = Vec4(1, 1, 0.5, 1) + text3Color = Vec4(0.6, 0.6, 0.6, 1) + self.closeButton = DirectButton(parent=self.frame, image=(gui.find('**/CancelButtonUp'), gui.find('**/CancelButtonDown'), gui.find('**/CancelButtonRollover')), relief=None, command=self.__handleClose) + self.feedButton = DirectButton(parent=self.frame, image=(gui.find('**/ButtonFeedUp'), + gui.find('**/ButtonFeedDown'), + gui.find('**/ButtonFeedRollover'), + gui.find('**/ButtonFeedUp')), geom=gui.find('**/PetControlFeedIcon'), geom3_color=disabledImageColor, image3_color=disabledImageColor, relief=None, text=TTLocalizer.PetPanelFeed, text_scale=TTLocalizer.PAPfeedButton, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, text_pos=(-0.5, 2.8), text_align=TextNode.ALeft, command=self.__handleFeed) + if not self.petIsLocal: + self.feedButton['state'] = DGG.DISABLED + else: + self.feedButton['state'] = self.feedButtonState() + self.callButton = DirectButton(parent=self.frame, image=(gui.find('**/ButtonGoToUp'), + gui.find('**/ButtonGoToDown'), + gui.find('**/ButtonGoToRollover'), + gui.find('**/ButtonGoToUp')), geom=gui.find('**/PetControlGoToIcon'), geom3_color=disabledImageColor, image3_color=disabledImageColor, relief=None, text=TTLocalizer.PetPanelCall, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, text_scale=TTLocalizer.PAPcallButton, text_pos=(-0.5, 1.3), text_align=TextNode.ALeft, command=self.__handleCall) + if not self.petIsLocal: + self.callButton['state'] = DGG.DISABLED + self.scratchButton = DirectButton(parent=self.frame, image=(gui.find('**/ButtonScratchUp'), + gui.find('**/ButtonScratchDown'), + gui.find('**/ButtonScratchRollover'), + gui.find('**/ButtonScratchUp')), geom=gui.find('**/PetControlScratchIcon'), geom3_color=disabledImageColor, image3_color=disabledImageColor, relief=None, text=TTLocalizer.PetPanelScratch, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, text_scale=TTLocalizer.PAPscratchButton, text_pos=(-0.5, 2.05), text_align=TextNode.ALeft, command=self.__handleScratch) + if not self.petIsLocal: + self.scratchButton['state'] = DGG.DISABLED + self.ownerButton = DirectButton(parent=self.frame, image=(gui.find('**/PetControlToonButtonUp'), gui.find('**/PetControlToonButtonDown'), gui.find('**/PetControlToonButtonRollover')), geom=gui.find('**/PetControlToonIcon'), geom3_color=disabledImageColor, relief=None, image3_color=disabledImageColor, text=('', + TTLocalizer.PetPanelOwner, + TTLocalizer.PetPanelOwner, + ''), text_fg=text2Color, text_shadow=(0, 0, 0, 1), text_scale=TTLocalizer.PAPownerButton, text_pos=(0.3, 1.05), text_align=TextNode.ACenter, command=self.__handleToOwner) + if self.avatar.getOwnerId() == base.localAvatar.doId: + self.ownerButton['state'] = DGG.DISABLED + toonGui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.detailButton = DirectButton(parent=self.frame, image=(toonGui.find('**/ChtBx_BackBtn_UP'), + toonGui.find('**/ChtBx_BackBtn_DN'), + toonGui.find('**/ChtBx_BackBtn_Rllvr'), + toonGui.find('**/ChtBx_BackBtn_UP')), relief=None, pos=(-1.3, 0, 0.67), image3_color=disabledImageColor, text=('', + TTLocalizer.PetPanelDetail, + TTLocalizer.PetPanelDetail, + ''), text_fg=text2Color, text_shadow=(0, 0, 0, 1), text_scale=0.05, text_pos=(0.05, 0.05), text_align=TextNode.ACenter, command=self.__handleDetail) + self.detailButton.setScale(7.5) + if not self.petIsLocal: + self.detailButton['state'] = DGG.DISABLED + gui.removeNode() + toonGui.removeNode() + self.petDetailPanel = None + self.__fillPetInfo(self.avatar) + self.accept('petNameChanged', self.__refreshPetInfo) + self.accept('petStateUpdated', self.__refreshPetInfo) + self.frame.show() + if self.petIsLocal: + proxTask = Task.loop(Task(self.__checkPetProximity), Task.pause(0.5)) + taskMgr.add(proxTask, 'petpanel-proximity-check', priority=ToontownGlobals.PetPanelProximityPriority) + if base.localAvatar.isLockedDown(): + self.disableInteractionButtons() + if self.petIsLocal: + self.listenForInteractionDone() + messenger.send('petPanelDone') + if not self.petIsLocal and hasattr(self.avatar, 'updateMoodFromServer'): + if self.avatar.doId != localAvatar.getPetId() or bboard.get(PetConstants.OurPetsMoodChangedKey, False): + self.stateLabel['text'] = '' + + def refresh(self = self, av = self.avatar): + bboard.remove(PetConstants.OurPetsMoodChangedKey) + self.__refreshPetInfo(av) + + self.avatar.updateMoodFromServer(refresh) + return + + def __checkPetProximity(self, task = None): + if self.petIsLocal: + if base.localAvatar.isInWater(): + self.scratchButton['state'] = DGG.DISABLED + self.feedButton['state'] = DGG.DISABLED + self.callButton['state'] = DGG.DISABLED + self.notify.debug('local avatar is in water') + else: + petPos = self.avatar.getPos() + toonPos = base.localAvatar.getPos() + diff = Vec3(petPos - toonPos) + distance = diff.length() + self.notify.debug('pet distance is %s' % distance) + if distance > 20.0: + self.scratchButton['state'] = DGG.DISABLED + self.feedButton['state'] = DGG.DISABLED + else: + tooVert = abs(diff[2]) > 5.0 + if tooVert: + self.scratchButton['state'] = DGG.DISABLED + else: + self.scratchButton['state'] = DGG.NORMAL + self.feedButton['state'] = self.feedButtonState() + self.callButton['state'] = DGG.NORMAL + return Task.done + + def enableInteractionButtons(self): + self.notify.debug('enable buttons') + proxTask = Task.loop(Task(self.__checkPetProximity), Task.pause(0.5)) + taskMgr.add(proxTask, 'petpanel-proximity-check', priority=ToontownGlobals.PetPanelProximityPriority) + self.__checkPetProximity() + + def disableInteractionButtons(self): + self.notify.debug('disable buttons') + taskMgr.remove('petpanel-proximity-check') + self.scratchButton['state'] = DGG.DISABLED + self.feedButton['state'] = DGG.DISABLED + self.callButton['state'] = DGG.DISABLED + + def listenForInteractionDone(self): + self.accept('pet-interaction-done', self.enableInteractionButtons) + + def cancelListenForInteractionDone(self): + self.ignore('pet-interaction-done') + + def feedButtonState(self): + if base.localAvatar.getMoney() >= PetConstants.FEED_AMOUNT: + return DGG.NORMAL + else: + return DGG.DISABLED + + def cleanup(self): + self.notify.debug('cleanup(): doId=%s' % self.avatar.doId) + if self.frame == None: + return + self.cancelListenForInteractionDone() + taskMgr.remove('petpanel-proximity-check') + if hasattr(self, 'toonDetail'): + del self.toonDetail + self.frame.destroy() + del self.frame + self.frame = None + self.petView.removeNode() + del self.petView + self.petModel.delete() + del self.petModel + base.localAvatar.obscureFriendsListButton(-1) + self.ignore('petStateUpdated') + self.ignore('petNameChanged') + if self.avatar.bFake: + self.avatar.disable() + self.avatar.delete() + AvatarPanel.AvatarPanel.cleanup(self) + base.panel = None + return + + def disableAll(self): + self.disableInteractionButtons() + self.ownerButton['state'] = DGG.DISABLED + self.closeButton['state'] = DGG.DISABLED + self.detailButton['state'] = DGG.DISABLED + + def __handleDetailDone(self): + if self.petDetailPanel != None: + self.petDetailPanel.cleanup() + self.petDetailPanel = None + self.detailButton['state'] = DGG.NORMAL + return + + def __handleDetail(self): + self.petDetailPanel = PetDetailPanel.PetDetailPanel(pet=self.avatar, closeCallback=self.__handleDetailDone, parent=self.frame) + self.detailButton['state'] = DGG.DISABLED + + def __handleToOwner(self): + self.notify.debug('__handleToOwner(): doId=%s' % self.avatar.doId) + handle = base.cr.identifyFriend(self.avatar.ownerId) + if handle != None: + self.cleanup() + messenger.send('clickedNametag', [handle]) + else: + self.disableAll() + from toontown.toon import ToonDetail + self.toonDetail = ToonDetail.ToonDetail(self.avatar.ownerId, self.__ownerDetailsLoaded) + return + + def __ownerDetailsLoaded(self, avatar): + self.notify.debug('__ownerDetailsLoaded(): doId=%s' % self.avatar.doId) + self.cleanup() + if avatar is not None: + messenger.send('clickedNametag', [avatar]) + return + + def __handleCall(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: PET: Call') + self.notify.debug('__handleCall(): doId=%s' % self.avatar.doId) + base.localAvatar.b_setPetMovie(self.avId, PetConstants.PET_MOVIE_CALL) + base.panel.disableInteractionButtons() + if self.avatar.trickIval is not None and self.avatar.trickIval.isPlaying(): + self.avatar.trickIval.finish() + base.cr.playGame.getPlace().setState('pet') + base.localAvatar.lock() + return + + def __handleFeed(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: PET: Feed') + self.notify.debug('__handleFeed(): doId=%s' % self.avatar.doId) + base.localAvatar.b_setPetMovie(self.avId, PetConstants.PET_MOVIE_FEED) + base.panel.disableInteractionButtons() + if self.avatar.trickIval is not None and self.avatar.trickIval.isPlaying(): + self.avatar.trickIval.finish() + base.cr.playGame.getPlace().setState('pet') + base.localAvatar.lock() + return + + def __handleScratch(self): + if base.config.GetBool('want-qa-regression', 1): + self.notify.info('QA-REGRESSION: PET: Scratch') + self.notify.debug('__handleScratch(): doId=%s' % self.avatar.doId) + base.localAvatar.b_setPetMovie(self.avId, PetConstants.PET_MOVIE_SCRATCH) + base.panel.disableInteractionButtons() + if self.avatar.trickIval is not None and self.avatar.trickIval.isPlaying(): + self.avatar.trickIval.finish() + base.cr.playGame.getPlace().setState('pet') + base.localAvatar.lock() + return + + def __handleDisableAvatar(self): + self.notify.debug('__handleDisableAvatar(): doId=%s' % self.avatar.doId) + self.cleanup() + AvatarPanel.currentAvatarPanel = None + return + + def __handleGenerateAvatar(self, avatar): + pass + + def __handleClose(self): + self.notify.debug('__handleClose(): doId=%s' % self.avatar.doId) + self.cleanup() + AvatarPanel.currentAvatarPanel = None + if self.friendsListShown: + self.FriendsListPanel.showFriendsList() + return + + def __fillPetInfo(self, avatar): + self.notify.debug('__fillPetInfo(): doId=%s' % avatar.doId) + self.petView = self.frame.attachNewNode('petView') + self.petView.setPos(0, 0, 5) + if hasattr(avatar, 'announceGenerate'): + avatar.announceGenerate() + self.petModel = Pet.Pet(forGui=1) + self.petModel.setDNA(avatar.getDNA()) + self.petModel.fitAndCenterHead(3.575, forGui=1) + self.petModel.reparentTo(self.petView) + self.petModel.enterNeutralHappy() + self.petModel.startBlink() + self.petModel.setScale(0.75) + self.nameLabel = DirectLabel(parent=self.frame, pos=(0, 0, 5.2), relief=None, text=avatar.getName(), text_font=avatar.getFont(), text_fg=Vec4(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.4, text_wordwrap=7.5, text_shadow=(1, 1, 1, 1)) + self.stateLabel = DirectLabel(parent=self.frame, pos=TTLocalizer.PAPstateLabelPos, relief=None, text='', text_font=avatar.getFont(), text_fg=Vec4(0, 0, 0, 1), text_scale=TTLocalizer.PAPstateLabel, text_wordwrap=TTLocalizer.PAPstateLabelWordwrap, text_shadow=(1, 1, 1, 1)) + self.__refreshPetInfo(avatar) + return + + def __refreshPetInfo(self, avatar): + self.notify.debug('__refreshPetInfo(): doId=%s' % avatar.doId) + if avatar.doId != self.avatar.doId: + self.notify.warning('avatar not self!') + return + if self.frame == None: + return + if not self.petIsLocal: + self.avatar.updateOfflineMood() + mood = self.avatar.getDominantMood() + self.stateLabel['text'] = TTLocalizer.PetMoodAdjectives[mood] + self.nameLabel['text'] = avatar.getName() + if self.petDetailPanel != None: + self.petDetailPanel.update(avatar) + return diff --git a/toontown/pets/PetBase.py b/toontown/pets/PetBase.py new file mode 100755 index 00000000..9e1f4829 --- /dev/null +++ b/toontown/pets/PetBase.py @@ -0,0 +1,21 @@ +from toontown.pets.PetConstants import AnimMoods +from toontown.pets import PetMood + +class PetBase: + + def getSetterName(self, valueName, prefix = 'set'): + return '%s%s%s' % (prefix, valueName[0].upper(), valueName[1:]) + + def getAnimMood(self): + if self.mood.getDominantMood() in PetMood.PetMood.ExcitedMoods: + return AnimMoods.EXCITED + elif self.mood.getDominantMood() in PetMood.PetMood.UnhappyMoods: + return AnimMoods.SAD + else: + return AnimMoods.NEUTRAL + + def isExcited(self): + return self.getAnimMood() == AnimMoods.EXCITED + + def isSad(self): + return self.getAnimMood() == AnimMoods.SAD diff --git a/toontown/pets/PetBrain.py b/toontown/pets/PetBrain.py new file mode 100755 index 00000000..ab325846 --- /dev/null +++ b/toontown/pets/PetBrain.py @@ -0,0 +1,536 @@ +from panda3d.core import * +from direct.showbase.PythonUtil import weightedChoice, randFloat, Functor +from direct.showbase.PythonUtil import list2dict +from direct.showbase import DirectObject +from direct.distributed import DistributedObject, DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from direct.fsm import FSM +from toontown.pets import PetConstants, PetObserve, PetGoal, PetGoalMgr +from toontown.pets import PetTricks, PetLookerAI +import random, types + +class PetBrain(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('PetBrain') + + def __init__(self, pet): + self.pet = pet + self.focus = None + self.started = 0 + self.inMovie = 0 + self.chaseNode = self.pet.getRender().attachNewNode('PetChaseNode') + self.goalMgr = PetGoalMgr.PetGoalMgr(self.pet) + self.doId2goals = {} + self.nearbyAvs = {} + self.avAwareness = {} + self.lastInteractTime = {} + self.nextAwarenessIndex = 0 + + def destroy(self): + taskMgr.remove(self.getTeleportTaskName()) + self.stop() + self.goalMgr.destroy() + self.chaseNode.removeNode() + del self.chaseNode + del self.focus + del self.pet + if self.doId2goals: + self.notify.warning('destroy(): self.doId2goals is not empty: %s' % self.doId2goals.keys()) + for goalList in self.doId2goals.values(): + for goal in goalList: + goal.destroy() + + del self.doId2goals + del self.avAwareness + + def getThinkTaskName(self): + return 'petThink-%s' % self.pet.doId + + def getTeleportTaskName(self): + return 'petTeleport-%s' % self.pet.doId + + def getObserveEventAttendedByAvStart(self, otherDoId): + return 'petObserveAttendedByAvStart-%s-%s' % (self.pet.doId, otherDoId) + + def getObserveEventAttendedByAvStop(self, otherDoId): + return 'petObserveAttendedByAvStop-%s-%s' % (self.pet.doId, otherDoId) + + def getObserveEventAttendingAvStart(self, otherDoId): + return 'petObserveAttendingAvStart-%s-%s' % (self.pet.doId, otherDoId) + + def getObserveEventAttendingAvStop(self, otherDoId): + return 'petObserveAttendingAvStop-%s-%s' % (self.pet.doId, otherDoId) + + def start(self): + PetBrain.notify.debug('start: %s' % self.pet.doId) + self.lookers = {} + self.lookees = {} + self.accept(PetLookerAI.getStartLookedAtByOtherEvent(self.pet.doId), self._handleLookedAtByOtherStart) + self.accept(PetLookerAI.getStopLookedAtByOtherEvent(self.pet.doId), self._handleLookedAtByOtherStop) + self.accept(PetLookerAI.getStartLookingAtOtherEvent(self.pet.doId), self._handleLookingAtOtherStart) + self.accept(PetLookerAI.getStopLookingAtOtherEvent(self.pet.doId), self._handleLookingAtOtherStop) + self.globalGoals = [PetGoal.Wander()] + for goal in self.globalGoals: + self.goalMgr.addGoal(goal) + + for doId in self.pet._getNearbyAvatarDict(): + self._handleAvatarArrive(doId) + + self.tLastLonelinessUpdate = globalClock.getFrameTime() + taskMgr.doMethodLater(simbase.petThinkPeriod * random.random(), self._think, self.getThinkTaskName()) + self.started = 1 + + def stop(self): + PetBrain.notify.debug('stop: %s' % self.pet.doId) + if not self.started: + return + self.started = 0 + del self.lookers + del self.lookees + for doId in self.pet._getNearbyAvatarDict(): + self._handleAvatarLeave(doId) + + for goal in self.globalGoals: + self.goalMgr.removeGoal(goal) + goal.destroy() + + del self.globalGoals + self.clearFocus() + taskMgr.remove(self.getThinkTaskName()) + self.ignore(PetLookerAI.getStartLookedAtByOtherEvent(self.pet.doId)) + self.ignore(PetLookerAI.getStopLookedAtByOtherEvent(self.pet.doId)) + self.ignore(PetLookerAI.getStartLookingAtOtherEvent(self.pet.doId)) + self.ignore(PetLookerAI.getStopLookingAtOtherEvent(self.pet.doId)) + + def observe(self, petObserve): + if petObserve.isForgettable(): + if random.random() < 0.05 * self.pet.traits.forgetfulness: + return + petObserve._influence(self) + + def updateLastInteractTime(self, avId): + if avId in self.lastInteractTime: + self.lastInteractTime[avId] = globalClock.getFrameTime() + + def _think(self, task = None): + if not self.inMovie: + self._updatePriorities() + if len(self.nearbyAvs) > PetConstants.MaxAvatarAwareness: + self.nextAwarenessIndex %= len(self.nearbyAvs) + self._considerBecomeAwareOf(self.nearbyAvs.keys()[self.nextAwarenessIndex]) + self.nextAwarenessIndex += 1 + curT = globalClock.getFrameTime() + tSinceLastLonelinessUpdate = curT - self.tLastLonelinessUpdate + if tSinceLastLonelinessUpdate >= PetConstants.LonelinessUpdatePeriod: + self.tLastLonelinessUpdate = curT + numLookers = len(self.lookers) + if numLookers: + dt = tSinceLastLonelinessUpdate + self.pet.lerpMood('loneliness', max(-1.0, dt * -.003 * numLookers)) + if numLookers > 5: + self.pet.lerpMood('excitement', min(1.0, dt * 0.001 * numLookers)) + taskMgr.doMethodLater(simbase.petThinkPeriod, self._think, self.getThinkTaskName()) + return Task.done + + def _updatePriorities(self): + self.goalMgr.updatePriorities() + + def _handleLookingAtOtherStart(self, avId): + if avId in self.lookees: + PetBrain.notify.warning('%s: already looking at av %s' % (self.pet.doId, avId)) + return + self.lookees[avId] = avId + self.observe(PetObserve.PetActionObserve(PetObserve.Actions.ATTENDING_START, avId)) + + def _handleLookingAtOtherStop(self, avId): + if avId not in self.lookees: + PetBrain.notify.warning('%s: not looking at av %s' % (self.pet.doId, avId)) + return + del self.lookees[avId] + self.observe(PetObserve.PetActionObserve(PetObserve.Actions.ATTENDING_STOP, avId)) + + def _handleLookedAtByOtherStart(self, avId): + if avId in self.lookers: + PetBrain.notify.warning('%s: av %s already looking at me' % (self.pet.doId, avId)) + return + self.lookers[avId] = avId + self.observe(PetObserve.PetActionObserve(PetObserve.Actions.ATTENDED_START, avId)) + + def _handleLookedAtByOtherStop(self, avId): + if avId not in self.lookers: + PetBrain.notify.warning('%s: av %s not looking at me' % (self.pet.doId, avId)) + return + del self.lookers[avId] + self.observe(PetObserve.PetActionObserve(PetObserve.Actions.ATTENDED_STOP, avId)) + + def lookedAtBy(self, avId): + return avId in self.lookers + + def lookingAt(self, avId): + return avId in self.lookees + + def getAvIdsLookingAtUs(self): + return self.lookers + + def getAvIdsWeAreLookingAt(self): + return self.lookees + + def setFocus(self, object): + if isinstance(self.focus, DistributedObjectAI.DistributedObjectAI): + self.ignore(self.focus.getDeleteEvent()) + self.lastInteractTime.setdefault(self.focus.doId, 0) + PetBrain.notify.debug('setFocus: %s' % object) + self.focus = object + if isinstance(self.focus, DistributedObjectAI.DistributedObjectAI): + self.accept(self.focus.getDeleteEvent(), self._handleFocusHasLeft) + + def getFocus(self): + return self.focus + + def clearFocus(self): + self.setFocus(None) + return + + def _handleFocusHasLeft(self): + if self.focus.isEmpty(): + self.chaseNode.setPos(self.pet, 0, 0, 0) + else: + self.chaseNode.setPos(self.focus, 0, 0, 0) + self._inspectSpot(self.chaseNode) + + def _chase(self, target): + if callable(target): + target = target() + if target is None: + return 0 + self.setFocus(target) + self.pet.actionFSM.request('Chase', target) + return 1 + + def _wander(self): + self.clearFocus() + self.pet.actionFSM.request('Wander') + return 1 + + def _unstick(self): + self.clearFocus() + self.pet.actionFSM.request('Unstick') + return 1 + + def _flee(self, chaser): + if callable(chaser): + chaser = chaser() + if chaser is None: + return 0 + self.setFocus(chaser) + self.pet.actionFSM.request('Flee', chaser) + return 1 + + def _inspectSpot(self, spot = None): + if spot is None: + spot = NodePath('randomSpot') + spot.setPos(randFloat(-20, 20), randFloat(-20, 20), 0) + self.setFocus(spot) + self.pet.actionFSM.request('InspectSpot', spot) + return 1 + + def _stay(self, avatar): + self.setFocus(avatar) + self.pet.actionFSM.request('Stay', avatar) + return 1 + + def _doTrick(self, trickId, avatar): + self.setFocus(avatar) + self.pet.actionFSM.request('Trick', avatar, trickId) + return 1 + + def _heal(self, avatar): + if callable(avatar): + avatar = avatar() + if avatar is None: + return 0 + self.setFocus(avatar) + self.pet.actionFSM.request('Heal', avatar) + return 1 + + def _startMovie(self): + self.setFocus(None) + self.pet.actionFSM.request('Movie') + self.inMovie = 1 + return + + def _endMovie(self): + self.inMovie = 0 + + def _handleGenericObserve(self, observe): + pass + + def _handleActionObserve(self, observe): + action = observe.getAction() + avId = observe.getAvId() + OA = PetObserve.Actions + dbg = PetBrain.notify.debug + if action == OA.ATTENDED_START: + dbg('avatar %s is looking at me' % avId) + self.pet.lerpMoods({'boredom': -.1, + 'excitement': 0.05, + 'loneliness': -.05}) + messenger.send(self.getObserveEventAttendedByAvStart(avId)) + elif action == OA.ATTENDED_STOP: + dbg('avatar %s is no longer looking at me' % avId) + messenger.send(self.getObserveEventAttendedByAvStop(avId)) + elif action == OA.ATTENDING_START: + dbg('I am looking at avatar %s' % avId) + messenger.send(self.getObserveEventAttendingAvStart(avId)) + elif action == OA.ATTENDING_STOP: + dbg('I am no longer looking at avatar %s' % avId) + messenger.send(self.getObserveEventAttendingAvStop(avId)) + elif action == OA.CHANGE_ZONE: + if avId != self.pet.doId: + oldZoneId, newZoneId = observe.getData() + PetBrain.notify.debug('%s.CHANGE_ZONE: %s, %s->%s' % (self.pet.doId, + avId, + oldZoneId, + newZoneId)) + myZoneId = self.pet.zoneId + if newZoneId != oldZoneId: + if newZoneId == myZoneId: + self._handleAvatarArrive(avId) + elif oldZoneId == myZoneId: + self._handleAvatarLeave(avId) + if self.pet.inEstate: + if avId in (self.pet.ownerId, self.pet.estateOwnerId): + if oldZoneId in self.pet.estateZones and newZoneId not in self.pet.estateZones: + if avId == self.pet.ownerId: + self._handleOwnerLeave() + else: + self._handleEstateOwnerLeave() + elif action == OA.LOGOUT: + if avId == self.pet.ownerId: + self._handleOwnerLeave() + elif avId == self.pet.estateOwnerId: + self._handleEstateOwnerLeave() + elif action == OA.FEED: + dbg('avatar %s is feeding me' % avId) + self.pet.lerpMoods({'affection': 0.35, + 'anger': -.07, + 'boredom': -.5, + 'excitement': 0.5, + 'fatigue': -.2, + 'hunger': -.5, + 'loneliness': -.08, + 'playfulness': 0.1, + 'restlessness': -.05, + 'sadness': -.2}) + self.updateLastInteractTime(avId) + avatar = simbase.air.doId2do.get(avId) + if avatar is not None: + avatar.setHatePets(0) + elif action == OA.SCRATCH: + dbg('avatar %s is scratching me' % avId) + self.pet.lerpMoods({'affection': 0.45, + 'anger': -.1, + 'boredom': -.8, + 'excitement': 0.5, + 'fatigue': -.25, + 'loneliness': -.2, + 'playfulness': 0.1, + 'restlessness': -.2, + 'sadness': -.2}) + self.updateLastInteractTime(avId) + avatar = simbase.air.doId2do.get(avId) + if avatar is not None: + avatar.setHatePets(0) + elif action == OA.GARDEN: + dbg('avatar %s is gardening' % avId) + avatar = simbase.air.doId2do.get(avId) + if avatar is not None: + if self.getFocus() == avatar: + self._wander() + return + + def _handlePhraseObserve(self, observe): + + def _handleGettingFriendlyAttention(avId, self = self): + self.pet.lerpMoods({'boredom': -.85, + 'restlessness': -.1, + 'playfulness': 0.2, + 'loneliness': -.4, + 'sadness': -.1, + 'fatigue': -.05, + 'excitement': 0.05, + 'anger': -.05}) + self.updateLastInteractTime(avId) + + def _handleComeHere(avId, self = self): + avatar = simbase.air.doId2do.get(avId) + if avatar: + self.pet.mover.walkToAvatar(avatar) + avatar.setHatePets(0) + + def _handleFollowMe(avId, self = self): + avatar = simbase.air.doId2do.get(avId) + if avatar: + self.pet.mover.walkToAvatar(avatar) + avatar.setHatePets(0) + + def _handleStay(avId, self = self): + avatar = simbase.air.doId2do.get(avId) + if avatar: + self._stay(avatar) + + def _handleCriticism(avId, self = self): + ownerFactor = 0.5 + if avId == self.pet.ownerId: + ownerFactor = 1.0 + self.pet.lerpMoods({'affection': -.4, + 'anger': 0.4, + 'boredom': -.3, + 'confusion': 0.05, + 'fatigue': 0.2, + 'playfulness': -.1, + 'sadness': 0.5 * ownerFactor}) + + def _handleGoAway(avId, self = self): + avatar = simbase.air.doId2do.get(avId) + if avatar is not None: + if self.getFocus() == avatar: + self._wander() + return + + def _handleDoTrick(trickId, avId, self = self): + looked = self.lookedAtBy(avId) or config.GetBool('pet-brain-ignore-looked-tricks', True) + avatar = simbase.air.doId2do.get(avId) + if avatar: + if looked: + if not self.pet._willDoTrick(trickId): + trickId = PetTricks.Tricks.BALK + self._doTrick(trickId, avatar) + + phrase = observe.getPetPhrase() + avId = observe.getAvId() + OP = PetObserve.Phrases + if phrase in list2dict([OP.COME, + OP.FOLLOW_ME, + OP.STAY, + OP.NEED_LAFF, + OP.NEED_GAGS, + OP.NEED_JB, + OP.HI, + OP.SOOTHE, + OP.PRAISE, + OP.HAPPY, + OP.QUESTION, + OP.FRIENDLY, + OP.LETS_PLAY, + OP.DO_TRICK]): + _handleGettingFriendlyAttention(avId) + if phrase == OP.COME: + _handleComeHere(avId) + if phrase == OP.FOLLOW_ME: + _handleFollowMe(avId) + if phrase == OP.STAY: + _handleStay(avId) + if phrase == OP.CRITICISM: + _handleCriticism(avId) + if phrase == OP.GO_AWAY: + _handleGoAway(avId) + if phrase == OP.DO_TRICK: + _handleDoTrick(observe.getTrickId(), avId) + + def _addGoalsReAvatar(self, avId): + av = self.pet.air.doId2do.get(avId) + if av is None: + PetBrain.notify.warning('%s._addGoalsReAvatar: %s not in doId2do' % (self.pet.doId, avId)) + return + if avId not in self.doId2goals: + goals = [PetGoal.ChaseAvatar(av), PetGoal.FleeFromAvatar(av)] + self.doId2goals[avId] = goals + self.lastInteractTime.setdefault(avId, 0) + for goal in self.doId2goals[avId]: + self.goalMgr.addGoal(goal) + + return + + def _removeGoalsReAvatar(self, avId): + if avId not in self.doId2goals: + PetBrain.notify.warning('no goals re av %s to remove' % avId) + return + for goal in self.doId2goals[avId]: + self.goalMgr.removeGoal(goal) + goal.destroy() + + del self.doId2goals[avId] + + def _considerBecomeAwareOf(self, avId): + av = simbase.air.doId2do.get(avId) + if av is None: + PetBrain.notify.warning('_considerBecomeAwareOf: av %s does not exist' % avId) + return + if avId in self.avAwareness: + return + + def becomeAwareOf(avId, self = self): + self.avAwareness[avId] = None + self._addGoalsReAvatar(avId) + return + + if len(self.avAwareness) < PetConstants.MaxAvatarAwareness: + becomeAwareOf(avId) + return + + def calcInterest(avId, self = self): + if avId == self.pet.ownerId: + return 100.0 + return random.random() + + avInterest = calcInterest(avId) + minInterest = avInterest + minInterestAvId = avId + for awAvId in self.avAwareness: + i = calcInterest(awAvId) + if i < minInterest: + minInterest = i + minInterestAvId = awAvId + break + + if minInterestAvId != avId: + self._removeAwarenessOf(minInterestAvId) + becomeAwareOf(avId) + return + + def _removeAwarenessOf(self, avId): + if avId in self.avAwareness: + self._removeGoalsReAvatar(avId) + del self.avAwareness[avId] + + def _handleAvatarArrive(self, avId): + PetBrain.notify.debug('%s._handleAvatarArrive: %s' % (self.pet.doId, avId)) + if avId in self.nearbyAvs: + PetBrain.notify.warning('%s already in self.nearbyAvs' % avId) + return + self.nearbyAvs[avId] = None + excitement = 0.3 + if avId == self.pet.ownerId: + excitement = 0.7 + self.pet.lerpMoods({'excitement': 0.7, + 'loneliness': -.4}) + self._considerBecomeAwareOf(avId) + return + + def _handleAvatarLeave(self, avId): + PetBrain.notify.debug('%s._handleAvatarLeave: %s' % (self.pet.doId, avId)) + if avId not in self.nearbyAvs: + PetBrain.notify.warning('av %s not in self.nearbyAvs' % avId) + return + del self.nearbyAvs[avId] + self.pet.lerpMoods({'loneliness': 0.1}) + self._removeAwarenessOf(avId) + + def _handleOwnerLeave(self): + self.pet.teleportOut() + taskMgr.doMethodLater(PetConstants.TELEPORT_OUT_DURATION, self.pet.requestDelete, self.getTeleportTaskName()) + + def _handleEstateOwnerLeave(self): + self.pet.teleportOut() + taskMgr.doMethodLater(PetConstants.TELEPORT_OUT_DURATION, self.pet.requestDelete, self.getTeleportTaskName()) diff --git a/toontown/pets/PetConstants.py b/toontown/pets/PetConstants.py new file mode 100755 index 00000000..608ad7bf --- /dev/null +++ b/toontown/pets/PetConstants.py @@ -0,0 +1,61 @@ +from panda3d.core import * +from direct.showbase.PythonUtil import Enum, invertDictLossless +import math +from toontown.toonbase import ToontownGlobals +OurPetsMoodChangedKey = 'OurPetsMoodChanged' +ThinkPeriod = 1.5 +MoodDriftPeriod = 300.0 +MovePeriod = 1.0 / 4 +PosBroadcastPeriod = 1.0 / 5 +LonelinessUpdatePeriod = 100.0 +SubmergeDistance = 0.7 +MaxAvatarAwareness = 3 +NonPetSphereRadius = 5.0 +PetSphereRadius = 3.0 +UnstickSampleWindow = 20 +UnstickCollisionThreshold = int(0.5 * UnstickSampleWindow) +PriorityFleeFromAvatar = 0.6 +PriorityDefault = 1.0 +PriorityChaseAv = 1.0 +PriorityDebugLeash = 50.0 +PriorityDoTrick = 100.0 +PrimaryGoalDecayDur = 60.0 +PrimaryGoalScale = 1.3 +HungerChaseToonScale = 1.2 +FleeFromOwnerScale = 0.5 +GettingAttentionGoalScale = 1.2 +GettingAttentionGoalScaleDur = 7.0 +AnimMoods = Enum('EXCITED, SAD, NEUTRAL') +FwdSpeed = 12.0 +RotSpeed = 360.0 +_HappyMult = 1.0 +HappyFwdSpeed = FwdSpeed * _HappyMult +HappyRotSpeed = RotSpeed * _HappyMult +_SadMult = 0.3 +SadFwdSpeed = FwdSpeed * _SadMult +SadRotSpeed = RotSpeed * _SadMult +PETCLERK_TIMER = 180 +PET_MOVIE_CLEAR = 0 +PET_MOVIE_START = 1 +PET_MOVIE_COMPLETE = 2 +PET_MOVIE_FEED = 3 +PET_MOVIE_SCRATCH = 4 +PET_MOVIE_CALL = 5 +FEED_TIME = 10.0 +SCRATCH_TIME = 8.042 +CALL_TIME = 8.0 / 3 +FEED_DIST = {'long': 4.0, + 'medium': 4.0, + 'short': 4.0} +FEED_AMOUNT = 1 +SCRATCH_DIST = {'long': 2.0, + 'medium': 1.5, + 'short': 1.0} +TELEPORT_IN_DURATION = 2.34 +TELEPORT_OUT_DURATION = 4.5 +ZoneToCostRange = {ToontownGlobals.ToontownCentral: (100, 500), + ToontownGlobals.DonaldsDock: (600, 1700), + ToontownGlobals.DaisyGardens: (1000, 2500), + ToontownGlobals.MinniesMelodyland: (1500, 3000), + ToontownGlobals.TheBrrrgh: (2500, 4000), + ToontownGlobals.DonaldsDreamland: (3000, 5000)} diff --git a/toontown/pets/PetDNA.py b/toontown/pets/PetDNA.py new file mode 100755 index 00000000..6b5d61ed --- /dev/null +++ b/toontown/pets/PetDNA.py @@ -0,0 +1,275 @@ +from toontown.toon import ToonDNA +from pandac.PandaModules import VBase4 +from toontown.toonbase import TTLocalizer, ToontownGlobals +from direct.showbase import PythonUtil +NumFields = 9 +Fields = {'head': 0, + 'ears': 1, + 'nose': 2, + 'tail': 3, + 'body': 4, + 'color': 5, + 'colorScale': 6, + 'eyes': 7, + 'gender': 8} +HeadParts = ['feathers'] +EarParts = ['horns', + 'antennae', + 'dogEars', + 'catEars', + 'rabbitEars'] +EarTextures = {'horns': None, + 'antennae': None, + 'dogEars': None, + 'catEars': 'phase_4/maps/BeanCatEar6.jpg', + 'rabbitEars': 'phase_4/maps/BeanBunnyEar6.jpg'} +ExoticEarTextures = {'horns': None, + 'antennae': None, + 'dogEars': None, + 'catEars': 'phase_4/maps/BeanCatEar3Yellow.jpg', + 'rabbitEars': 'phase_4/maps/BeanBunnyEar6.jpg'} +NoseParts = ['clownNose', + 'dogNose', + 'ovalNose', + 'pigNose'] +TailParts = ['catTail', + 'longTail', + 'birdTail', + 'bunnyTail'] +TailTextures = {'catTail': 'phase_4/maps/beanCatTail6.jpg', + 'longTail': 'phase_4/maps/BeanLongTail6.jpg', + 'birdTail': None, + 'bunnyTail': None} +GiraffeTail = 'phase_4/maps/BeanLongTailGiraffe.jpg' +LeopardTail = 'phase_4/maps/BeanLongTailLepord.jpg' +GenericBodies = ['dots', + 'threeStripe', + 'tigerStripe', + 'tummy'] +SpecificBodies = ['turtle', 'giraffe', 'leopard'] +BodyTypes = GenericBodies + SpecificBodies +PetRarities2 = (('leopard', 0.005), + ('giraffe', 0.015), + ('turtle', 0.045), + ('tigerStripe', 0.115), + ('dots', 0.265), + ('tummy', 0.525), + ('threeStripe', 1.0)) +PetRarities = {'body': {ToontownGlobals.ToontownCentral: {'threeStripe': 50, + 'tummy': 30, + 'dots': 20}, + ToontownGlobals.DonaldsDock: {'threeStripe': 35, + 'tummy': 30, + 'dots': 20, + 'tigerStripe': 15}, + ToontownGlobals.DaisyGardens: {'threeStripe': 15, + 'tummy': 20, + 'dots': 20, + 'tigerStripe': 20, + 'turtle': 15}, + ToontownGlobals.MinniesMelodyland: {'threeStripe': 10, + 'tummy': 15, + 'dots': 30, + 'tigerStripe': 25, + 'turtle': 20}, + ToontownGlobals.TheBrrrgh: {'threeStripe': 5, + 'tummy': 10, + 'dots': 20, + 'tigerStripe': 25, + 'turtle': 25, + 'giraffe': 15}, + ToontownGlobals.DonaldsDreamland: {'threeStripe': 5, + 'tummy': 5, + 'dots': 15, + 'tigerStripe': 20, + 'turtle': 25, + 'giraffe': 20, + 'leopard': 10} + }} +BodyTextures = {'dots': 'phase_4/maps/BeanbodyDots6.jpg', + 'threeStripe': 'phase_4/maps/Beanbody3stripes6.jpg', + 'tigerStripe': 'phase_4/maps/BeanbodyZebraStripes6.jpg', + 'turtle': 'phase_4/maps/BeanbodyTurtle.jpg', + 'giraffe': 'phase_4/maps/BeanbodyGiraffe1.jpg', + 'leopard': 'phase_4/maps/BeanbodyLepord2.jpg', + 'tummy': 'phase_4/maps/BeanbodyTummy6.jpg'} +FeetTextures = {'normal': 'phase_4/maps/BeanFoot6.jpg', + 'turtle': 'phase_4/maps/BeanFootTurttle.jpg', + 'giraffe': 'phase_4/maps/BeanFootYellow3.jpg', + 'leopard': 'phase_4/maps/BeanFootYellow3.jpg'} +AllPetColors = (VBase4(1.0, 1.0, 1.0, 1.0), + VBase4(0.96875, 0.691406, 0.699219, 1.0), + VBase4(0.933594, 0.265625, 0.28125, 1.0), + VBase4(0.863281, 0.40625, 0.417969, 1.0), + VBase4(0.710938, 0.234375, 0.4375, 1.0), + VBase4(0.570312, 0.449219, 0.164062, 1.0), + VBase4(0.640625, 0.355469, 0.269531, 1.0), + VBase4(0.996094, 0.695312, 0.511719, 1.0), + VBase4(0.832031, 0.5, 0.296875, 1.0), + VBase4(0.992188, 0.480469, 0.167969, 1.0), + VBase4(0.996094, 0.898438, 0.320312, 1.0), + VBase4(0.996094, 0.957031, 0.597656, 1.0), + VBase4(0.855469, 0.933594, 0.492188, 1.0), + VBase4(0.550781, 0.824219, 0.324219, 1.0), + VBase4(0.242188, 0.742188, 0.515625, 1.0), + VBase4(0.304688, 0.96875, 0.402344, 1.0), + VBase4(0.433594, 0.90625, 0.835938, 1.0), + VBase4(0.347656, 0.820312, 0.953125, 1.0), + VBase4(0.191406, 0.5625, 0.773438, 1.0), + VBase4(0.558594, 0.589844, 0.875, 1.0), + VBase4(0.285156, 0.328125, 0.726562, 1.0), + VBase4(0.460938, 0.378906, 0.824219, 1.0), + VBase4(0.546875, 0.28125, 0.75, 1.0), + VBase4(0.726562, 0.472656, 0.859375, 1.0), + VBase4(0.898438, 0.617188, 0.90625, 1.0), + VBase4(0.7, 0.7, 0.8, 1.0)) +GenericPetColors = [0, + 1, + 3, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 21, + 22, + 23, + 24, + 25] +SpecificPetColors = [0, + 1, + 3, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 16, + 17, + 18, + 23, + 24, + 25] +ColorScales = [0.8, + 0.85, + 0.9, + 0.95, + 1.0, + 1.05, + 1.1, + 1.15, + 1.2] +PetEyeColors = (VBase4(0.29, 0.29, 0.69, 1.0), + VBase4(0.49, 0.49, 0.99, 1.0), + VBase4(0.69, 0.49, 0.29, 1.0), + VBase4(0.99, 0.69, 0.49, 1.0), + VBase4(0.29, 0.69, 0.29, 1.0), + VBase4(0.49, 0.99, 0.49, 1.0)) +PetGenders = [0, 1] + +def getRandomPetDNA(zoneId = ToontownGlobals.DonaldsDreamland): + from random import choice + head = choice(range(-1, len(HeadParts))) + ears = choice(range(-1, len(EarParts))) + nose = choice(range(-1, len(NoseParts))) + tail = choice(range(-1, len(TailParts))) + body = getSpecies(zoneId) + color = choice(range(0, len(getColors(body)))) + colorScale = choice(range(0, len(ColorScales))) + eyes = choice(range(0, len(PetEyeColors))) + gender = choice(range(0, len(PetGenders))) + return [head, + ears, + nose, + tail, + body, + color, + colorScale, + eyes, + gender] + + +def getSpecies(zoneId): + body = PythonUtil.weightedRand(PetRarities['body'][zoneId]) + return BodyTypes.index(body) + + +def getColors(bodyType): + if BodyTypes[bodyType] in GenericBodies: + return GenericPetColors + else: + return SpecificPetColors + + +def getFootTexture(bodyType): + if BodyTypes[bodyType] == 'turtle': + texName = FeetTextures['turtle'] + elif BodyTypes[bodyType] == 'giraffe': + texName = FeetTextures['giraffe'] + elif BodyTypes[bodyType] == 'leopard': + texName = FeetTextures['leopard'] + else: + texName = FeetTextures['normal'] + return texName + + +def getEarTexture(bodyType, earType): + if BodyTypes[bodyType] == 'giraffe' or BodyTypes[bodyType] == 'leopard': + dict = ExoticEarTextures + else: + dict = EarTextures + return dict[earType] + + +def getBodyRarity(bodyIndex): + bodyName = BodyTypes[bodyIndex] + totalWeight = 0.0 + weight = {} + for zoneId in PetRarities['body']: + for body in PetRarities['body'][zoneId]: + totalWeight += PetRarities['body'][zoneId][body] + if body in weight: + weight[body] += PetRarities['body'][zoneId][body] + else: + weight[body] = PetRarities['body'][zoneId][body] + + minWeight = min(weight.values()) + rarity = (weight[bodyName] - minWeight) / (totalWeight - minWeight) + return rarity + + +def getRarity(dna): + body = dna[Fields['body']] + rarity = getBodyRarity(body) + return rarity + + +def getGender(dna): + return dna[Fields['gender']] + + +def setGender(dna, gender): + dna[Fields['gender']] = gender + + +def getGenderString(dna = None, gender = -1): + if dna != None: + gender = getGender(dna) + if gender: + return TTLocalizer.GenderShopBoyButtonText + else: + return TTLocalizer.GenderShopGirlButtonText + return diff --git a/toontown/pets/PetDetail.py b/toontown/pets/PetDetail.py new file mode 100755 index 00000000..37786c36 --- /dev/null +++ b/toontown/pets/PetDetail.py @@ -0,0 +1,16 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.avatar import AvatarDetail +from toontown.pets import DistributedPet + +class PetDetail(AvatarDetail.AvatarDetail): + notify = DirectNotifyGlobal.directNotify.newCategory('PetDetail') + + def getDClass(self): + return 'DistributedPet' + + def createHolder(self): + pet = DistributedPet.DistributedPet(base.cr, bFake=True) + pet.forceAllowDelayDelete() + pet.generateInit() + pet.generate() + return pet diff --git a/toontown/pets/PetDetailPanel.py b/toontown/pets/PetDetailPanel.py new file mode 100755 index 00000000..4c384e32 --- /dev/null +++ b/toontown/pets/PetDetailPanel.py @@ -0,0 +1,77 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer +from toontown.pets import PetTricks +from otp.otpbase import OTPLocalizer +from direct.showbase.PythonUtil import lerp +FUDGE_FACTOR = 0.01 + +class PetDetailPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PetDetailPanel') + + def __init__(self, pet, closeCallback, parent = aspect2d, **kw): + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + detailPanel = gui.find('**/PetBattlePannel2') + optiondefs = (('pos', (-4.52, 0.0, 3.05), None), + ('scale', 3.58, None), + ('relief', None, None), + ('image', detailPanel, None), + ('image_color', GlobalDialogColor, None), + ('text', TTLocalizer.PetDetailPanelTitle, None), + ('text_wordwrap', 10.4, None), + ('text_scale', 0.132, None), + ('text_pos', (-0.2, 0.6125), None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.dataText = DirectLabel(self, text='', text_scale=0.09, text_align=TextNode.ALeft, text_wordwrap=15, relief=None, pos=(-0.7, 0.0, 0.55)) + self.bCancel = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=TTLocalizer.AvatarDetailPanelCancel, text_scale=0.05, text_pos=(0.12, -0.01), pos=(-0.88, 0.0, -0.68), scale=2.0, command=closeCallback) + self.initialiseoptions(PetDetailPanel) + self.labels = {} + self.bars = {} + self.update(pet) + buttons.removeNode() + gui.removeNode() + return + + def cleanup(self): + del self.labels + del self.bars + self.destroy() + + def update(self, pet): + if not pet: + return + for trickId in PetTricks.TrickId2scIds.keys(): + trickText = TTLocalizer.PetTrickStrings[trickId] + if trickId < len(pet.trickAptitudes): + aptitude = pet.trickAptitudes[trickId] + bar = self.bars.get(trickId) + label = self.bars.get(trickId) + if aptitude != 0: + healRange = PetTricks.TrickHeals[trickId] + hp = lerp(healRange[0], healRange[1], aptitude) + if hp == healRange[1]: + hp = healRange[1] + length = 1 + barColor = (0.7, 0.8, 0.5, 1) + else: + hp, length = divmod(hp, 1) + barColor = (0.9, 1, 0.7, 1) + if not label: + self.labels[trickId] = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.43 - trickId * 0.155), scale=0.7, text=trickText, text_scale=TTLocalizer.PDPtrickText, text_fg=(0.05, 0.14, 0.4, 1), text_align=TextNode.ALeft, text_pos=(-1.4, -0.05)) + else: + label['text'] = trickText + if not bar: + self.bars[trickId] = DirectWaitBar(parent=self, pos=(0, 0, 0.43 - trickId * 0.155), relief=DGG.SUNKEN, frameSize=(-0.5, + 0.9, + -0.1, + 0.1), borderWidth=(0.025, 0.025), scale=0.7, frameColor=(0.4, 0.6, 0.4, 1), barColor=barColor, range=1.0 + FUDGE_FACTOR, value=length + FUDGE_FACTOR, text=str(int(hp)) + ' ' + TTLocalizer.Laff, text_scale=TTLocalizer.PDPlaff, text_fg=(0.05, 0.14, 0.4, 1), text_align=TextNode.ALeft, text_pos=TTLocalizer.PDPlaffPos) + else: + bar['value'] = length + FUDGE_FACTOR + bar['text'] = (str(int(hp)) + ' ' + TTLocalizer.Laff,) + + return diff --git a/toontown/pets/PetGoal.py b/toontown/pets/PetGoal.py new file mode 100755 index 00000000..108ad015 --- /dev/null +++ b/toontown/pets/PetGoal.py @@ -0,0 +1,264 @@ +from direct.task import Task +from direct.fsm import FSM, ClassicFSM, State +from direct.showbase.PythonUtil import randFloat, Functor +from direct.directnotify import DirectNotifyGlobal +from toontown.pets import PetConstants +from toontown.toon import DistributedToonAI + +class PetGoal(FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('PetGoal') + SerialNum = 0 + + def __init__(self): + FSM.FSM.__init__(self, self.__class__.__name__) + self.goalMgr = None + self.pet = None + self.brain = None + self.removeOnDone = 0 + self.serialNum = PetGoal.SerialNum + PetGoal.SerialNum += 1 + self.fsm = ClassicFSM.ClassicFSM('PetGoalFSM', [State.State('off', self.enterOff, self.exitOff, ['background']), State.State('background', self.enterBackground, self.exitBackground, ['foreground']), State.State('foreground', self.enterForeground, self.exitForeground, ['background'])], 'off', 'off') + self.fsm.enterInitialState() + return + + def destroy(self): + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + del self.fsm + self.cleanup() + + def _removeSelf(self): + self.goalMgr.removeGoal(self) + + def getDoneEvent(self): + return 'PetGoalDone-%s' % self.serialNum + + def announceDone(self): + if self.removeOnDone: + self._removeSelf() + messenger.send(self.getDoneEvent()) + if self.removeOnDone: + self.destroy() + + def setGoalMgr(self, goalMgr): + self.goalMgr = goalMgr + self.pet = goalMgr.pet + self.brain = self.pet.brain + self.fsm.request('background') + + def clearGoalMgr(self): + self.goalMgr = None + self.pet = None + self.brain = None + self.fsm.requestFinalState() + return + + def getPriority(self): + return PetConstants.PriorityDefault + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterBackground(self): + pass + + def exitBackground(self): + pass + + def enterForeground(self): + pass + + def exitForeground(self): + pass + + def __repr__(self): + return self.__str__() + + def __str__(self): + return '%s: %s' % (self.__class__.__name__, self.getPriority()) + + +class InteractWithAvatar(PetGoal): + SerialNum = 0 + + def __init__(self, avatar): + PetGoal.__init__(self) + self.avatar = avatar + self.serialNum = InteractWithAvatar.SerialNum + InteractWithAvatar.SerialNum += 1 + self.transitionDoLaterName = '%s-doLater-%s' % (InteractWithAvatar.__name__, self.serialNum) + + def destroy(self): + PetGoal.destroy(self) + if hasattr(self, 'avatar'): + del self.avatar + + def enterForeground(self): + self.request('Chase') + + def exitForeground(self): + self.request('Off') + + def enterChase(self): + PetGoal.notify.debug('enterChase') + if self.brain.lookingAt(self.avatar.doId): + + def goToInteract(task = None, self = self): + self.request('Interact') + return Task.done + + taskMgr.doMethodLater(0.0001, goToInteract, self.transitionDoLaterName) + else: + self.accept(self.brain.getObserveEventAttendingAvStart(self.avatar.doId), Functor(self.request, 'Interact')) + self.brain._chase(self.avatar) + return + + def exitChase(self): + self.ignore(self.brain.getObserveEventAttendingAvStart(self.avatar.doId)) + taskMgr.remove(self.transitionDoLaterName) + + def enterInteract(self): + PetGoal.notify.debug('enterInteract') + if self._chaseAvInInteractMode(): + self.accept(self.brain.getObserveEventAttendingAvStop(self.avatar.doId), Functor(self.request, 'Chase')) + self.startInteract() + + def exitInteract(self): + self.stopInteract() + self.ignore(self.brain.getObserveEventAttendingAvStop(self.avatar.doId)) + + def startInteract(self): + pass + + def stopInteract(self): + pass + + def _chaseAvInInteractMode(self): + return True + + def __str__(self): + return '%s-%s: %s' % (self.__class__.__name__, self.avatar.doId, self.getPriority()) + + +class Wander(PetGoal): + + def enterForeground(self): + self.brain._wander() + + +class ChaseAvatar(PetGoal): + + def __init__(self, avatar): + PetGoal.__init__(self) + self.avatar = avatar + self.isToon = isinstance(self.avatar, DistributedToonAI.DistributedToonAI) + + def destroy(self): + PetGoal.destroy(self) + if hasattr(self, 'avatar'): + del self.avatar + + def setGoalMgr(self, goalMgr): + PetGoal.setGoalMgr(self, goalMgr) + self.basePriority = PetConstants.PriorityChaseAv + + def getPriority(self): + priority = self.basePriority + if self.isToon and self.pet.mood.getDominantMood() == 'hunger': + priority *= PetConstants.HungerChaseToonScale + lastInteractTime = self.brain.lastInteractTime.get(self.avatar.doId) + if lastInteractTime is not None: + elapsed = globalClock.getFrameTime() - lastInteractTime + if elapsed < PetConstants.GettingAttentionGoalScaleDur: + priority *= PetConstants.GettingAttentionGoalScale + return priority + + def enterForeground(self): + self.brain._chase(self.avatar) + + def __str__(self): + return '%s-%s: %s' % (self.__class__.__name__, self.avatar.doId, self.getPriority()) + + +class ChaseAvatarLeash(PetGoal): + + def __init__(self, avId): + PetGoal.__init__(self) + self.avId = avId + + def getPriority(self): + return PetConstants.PriorityDebugLeash + + def enterForeground(self): + av = simbase.air.doId2do.get(self.avId) + if av: + self.brain._chase(av) + else: + self._removeSelf() + + def __str__(self): + return '%s-%s: %s' % (self.__class__.__name__, self.avatar.doId, self.getPriority()) + + +class FleeFromAvatar(PetGoal): + + def __init__(self, avatar): + PetGoal.__init__(self) + self.avatar = avatar + + def destroy(self): + PetGoal.destroy(self) + if hasattr(self, 'avatar'): + del self.avatar + + def getPriority(self): + priority = PetConstants.PriorityFleeFromAvatar + if self.avatar.doId == self.goalMgr.pet.ownerId: + priority *= PetConstants.FleeFromOwnerScale + return priority + + def enterForeground(self): + self.brain._chase(self.avatar) + + def __str__(self): + return '%s-%s: %s' % (self.__class__.__name__, self.avatar.doId, self.getPriority()) + + +class DoTrick(InteractWithAvatar): + + def __init__(self, avatar, trickId): + InteractWithAvatar.__init__(self, avatar) + self.trickId = trickId + self.removeOnDone = 1 + + def getPriority(self): + return PetConstants.PriorityDoTrick + + def setGoalMgr(self, goalMgr): + goalMgr._setHasTrickGoal(True) + InteractWithAvatar.setGoalMgr(self, goalMgr) + + def clearGoalMgr(self): + self.goalMgr._setHasTrickGoal(False) + InteractWithAvatar.clearGoalMgr(self) + + def _chaseAvInInteractMode(self): + return False + + def startInteract(self): + self.brain._doTrick(self.trickId, self.avatar) + self.trickDoneEvent = self.pet.actionFSM.getTrickDoneEvent() + self.accept(self.trickDoneEvent, self.announceDone) + + def stopInteract(self): + self.ignore(self.trickDoneEvent) + del self.trickDoneEvent + + def __str__(self): + return '%s-%s-%s: %s' % (self.__class__.__name__, + self.avatar.doId, + self.trickId, + self.getPriority()) diff --git a/toontown/pets/PetGoalMgr.py b/toontown/pets/PetGoalMgr.py new file mode 100755 index 00000000..87eb1f8e --- /dev/null +++ b/toontown/pets/PetGoalMgr.py @@ -0,0 +1,99 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import DirectObject +from direct.showbase.PythonUtil import randFloat, lerp +from toontown.pets import PetConstants +import random + +class PetGoalMgr(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('PetGoalMgr') + + def __init__(self, pet): + self.pet = pet + self.goals = {} + self._hasTrickGoal = False + self.primaryGoal = None + self.primaryStartT = 0 + + def destroy(self): + goals = self.goals.keys() + for goal in goals: + self.removeGoal(goal) + goal.destroy() + + del self.goals + + def hasTrickGoal(self): + return self._hasTrickGoal + + def _setHasTrickGoal(self, hasTrickGoal): + self._hasTrickGoal = hasTrickGoal + + def addGoal(self, goal): + self.goals[goal] = None + goal.setGoalMgr(self) + return + + def removeGoal(self, goal): + if self.primaryGoal == goal: + self._setPrimaryGoal(None) + goal.clearGoalMgr() + del self.goals[goal] + return + + def updatePriorities(self): + if len(self.goals) == 0: + return + if self.primaryGoal is None: + highestPriority = -99999.0 + candidates = [] + else: + highestPriority = self.primaryGoal.getPriority() + candidates = [self.primaryGoal] + decayDur = PetConstants.PrimaryGoalDecayDur + priFactor = PetConstants.PrimaryGoalScale + elapsed = min(decayDur, globalClock.getFrameTime() - self.primaryStartT) + highestPriority *= lerp(priFactor, 1.0, elapsed / decayDur) + for goal in self.goals: + thisPri = goal.getPriority() + if thisPri >= highestPriority: + if thisPri > highestPriority: + highestPriority = thisPri + candidates = [goal] + else: + candidates.append(goal) + + newPrimary = random.choice(candidates) + if self.primaryGoal != newPrimary: + self.pet.notify.debug('new goal: %s, priority=%s' % (newPrimary.__class__.__name__, highestPriority)) + self._setPrimaryGoal(newPrimary) + return + + def _setPrimaryGoal(self, goal): + if self.primaryGoal == goal: + return + if self.primaryGoal is not None: + self.primaryGoal.fsm.request('background') + self.primaryGoal = goal + self.primaryStartT = globalClock.getFrameTime() + if goal is not None: + goal.fsm.request('foreground') + return + + def _handlePrimaryGoalDone(self): + self._setPrimaryGoal(None) + return + + def __repr__(self): + string = '%s' % self.__class__.__name__ + string += '\n Primary: %s' % self.primaryGoal + goalPairs = [] + for goal in self.goals: + goalPairs.append((goal.getPriority(), goal)) + + goalPairs.sort() + goalPairs.reverse() + for goalPair in goalPairs: + string += '\n %s' % goalPair[1] + + return string diff --git a/toontown/pets/PetHandle.py b/toontown/pets/PetHandle.py new file mode 100755 index 00000000..c377310a --- /dev/null +++ b/toontown/pets/PetHandle.py @@ -0,0 +1,67 @@ +from toontown.toonbase import ToontownGlobals +from toontown.pets import PetMood, PetTraits, PetDetail + +class PetHandle: + + def __init__(self, avatar): + self.doId = avatar.doId + self.name = avatar.name + self.style = avatar.style + self.ownerId = avatar.ownerId + self.bFake = False + self.cr = avatar.cr + self.traits = PetTraits.PetTraits(avatar.traitSeed, avatar.safeZone, traitValueList=avatar.traitList) + self._grabMood(avatar) + + def _grabMood(self, avatar): + self.mood = avatar.lastKnownMood.makeCopy() + self.mood.setPet(self) + self.lastKnownMood = self.mood.makeCopy() + self.setLastSeenTimestamp(avatar.lastSeenTimestamp) + self.updateOfflineMood() + + def getDoId(self): + return self.doId + + def getOwnerId(self): + return self.ownerId + + def isPet(self): + return True + + def getName(self): + return self.name + + def getDNA(self): + return self.style + + def getFont(self): + return ToontownGlobals.getToonFont() + + def setLastSeenTimestamp(self, timestamp): + self.lastSeenTimestamp = timestamp + + def getTimeSinceLastSeen(self): + t = self.cr.getServerTimeOfDay() - self.lastSeenTimestamp + return max(0.0, t) + + def updateOfflineMood(self): + self.mood.driftMood(dt=self.getTimeSinceLastSeen(), curMood=self.lastKnownMood) + + def getDominantMood(self): + if not hasattr(self, 'mood'): + return PetMood.PetMood.Neutral + return self.mood.getDominantMood() + + def uniqueName(self, idString): + return idString + '-' + str(self.getDoId()) + + def updateMoodFromServer(self, callWhenDone = None): + + def handleGotDetails(avatar, callWhenDone = callWhenDone): + avatar.announceGenerate() + self._grabMood(avatar) + if callWhenDone: + callWhenDone() + + PetDetail.PetDetail(self.doId, handleGotDetails) diff --git a/toontown/pets/PetLookerAI.py b/toontown/pets/PetLookerAI.py new file mode 100755 index 00000000..a3780c61 --- /dev/null +++ b/toontown/pets/PetLookerAI.py @@ -0,0 +1,184 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import DirectObject +from otp.ai.AIZoneData import AIZoneData +from toontown.toonbase import ToontownGlobals +from toontown.pets import PetConstants + +def getStartLookingAtOtherEvent(lookingAvId): + return 'PetLookerAI-%s-startLookingAtOther' % lookingAvId + + +def getStopLookingAtOtherEvent(lookingAvId): + return 'PetLookerAI-%s-stopLookingAtOther' % lookingAvId + + +def getStartLookedAtByOtherEvent(lookedAtAvId): + return 'PetLookerAI-%s-startLookedAtByOther' % lookedAtAvId + + +def getStopLookedAtByOtherEvent(lookedAtAvId): + return 'PetLookerAI-%s-stopLookedAtByOther' % lookedAtAvId + + +class PetLookerAI: + notify = DirectNotifyGlobal.directNotify.newCategory('PetLookerAI') + + def __init__(self): + self.__active = 0 + self.others = {} + + def destroy(self): + if self.__active: + self.exitPetLook() + if len(self.others): + PetLookerAI.notify.warning('%s: self.others not empty: %s' % (self.doId, self.others.keys())) + self.others = {} + + def _getPetLookerBodyNode(self): + return self + + def _isPet(self): + return 0 + + def enterPetLook(self): + PetLookerAI.notify.debug('enterPetLook: %s' % self.doId) + if self.__active: + PetLookerAI.notify.warning('enterPetLook: %s already active!' % self.doId) + return + if len(self.others): + PetLookerAI.notify.warning('%s: len(self.others) != 0: %s' % (self.doId, self.others.keys())) + self.others = {} + self.__active = 1 + self.__collNode = self._getPetLookerBodyNode().attachNewNode('PetLookerCollNode') + self._createPetLookSphere() + + def exitPetLook(self): + PetLookerAI.notify.debug('exitPetLook: %s' % self.doId) + if not self.__active: + PetLookerAI.notify.warning('exitPetLook: %s not active!' % self.doId) + return + if len(self.others): + otherIds = self.others.keys() + PetLookerAI.notify.warning('%s: still in otherIds: %s' % (self.doId, otherIds)) + for otherId in otherIds: + self._handleLookingAtOtherStop(otherId) + + if len(self.others): + PetLookerAI.notify.warning('%s: self.others still not empty: %s' % (self.doId, self.others.keys())) + self.others = {} + self._destroyPetLookSphere() + self.__collNode.removeNode() + del self.__collNode + self.__active = 0 + + def _createPetLookSphere(self): + isPet = self._isPet() + if isPet: + radius = PetConstants.PetSphereRadius + else: + radius = PetConstants.NonPetSphereRadius + lookSphere = CollisionSphere(0, 0, 0, radius) + lookSphereNode = CollisionNode('petLookSphere-%s' % self.doId) + lookSphereNode.addSolid(lookSphere) + lookSphereNode.setFromCollideMask(BitMask32.allOff()) + if isPet: + intoCollideMask = ToontownGlobals.PetLookatPetBitmask + fromCollideMask = ToontownGlobals.PetLookatPetBitmask | ToontownGlobals.PetLookatNonPetBitmask + else: + intoCollideMask = ToontownGlobals.PetLookatNonPetBitmask + fromCollideMask = ToontownGlobals.PetLookatPetBitmask + lookSphereNode.setIntoCollideMask(intoCollideMask) + lookSphereNode.setFromCollideMask(fromCollideMask) + self.lookSphereNodePath = self.__collNode.attachNewNode(lookSphereNode) + self.lookSphereNodePath.setTag('petLooker', '%s' % self.doId) + self._cHandler = CollisionHandlerEvent() + self._cHandler.addInPattern(self._getLookingStartEvent()) + self._cHandler.addOutPattern(self._getLookingStopEvent()) + collTrav = self.getCollTrav() + if collTrav: + collTrav.addCollider(self.lookSphereNodePath, self._cHandler) + self.accept(self._getLookingStartEvent(), self._handleLookingAtOtherStart) + self.accept(self._getLookingStopEvent(), self._handleLookingAtOtherStop) + if hasattr(self, 'eventProxy'): + PetLookerAI.notify.warning('%s: already have an eventProxy!' % self.doId) + else: + self.eventProxy = DirectObject.DirectObject() + self.eventProxy.accept(self.getZoneChangeEvent(), self._handleZoneChange) + + def _destroyPetLookSphere(self): + collTrav = self.getCollTrav() + if collTrav: + collTrav.removeCollider(self.lookSphereNodePath) + del self._cHandler + self.lookSphereNodePath.removeNode() + del self.lookSphereNodePath + self.ignore(self._getLookingStartEvent()) + self.ignore(self._getLookingStopEvent()) + self.eventProxy.ignoreAll() + del self.eventProxy + + def _handleZoneChange(self, newZoneId, oldZoneId): + PetLookerAI.notify.debug('_handleZoneChange: %s' % self.doId) + if not self.__active: + PetLookerAI.notify.warning('%s: _handleZoneChange: not active!' % self.doId) + return + oldZoneData = AIZoneData(self.air, self.parentId, oldZoneId) + if oldZoneData.hasCollTrav(): + oldZoneData.getCollTrav().removeCollider(self.lookSphereNodePath) + oldZoneData.destroy() + newZoneData = AIZoneData(self.air, self.parentId, newZoneId) + if newZoneData.hasCollTrav(): + newZoneData.getCollTrav().addCollider(self.lookSphereNodePath, self._cHandler) + newZoneData.destroy() + + def _getLookingStartEvent(self): + return 'PetLookerAI-lookingStart-%s' % self.doId + + def _getLookingStopEvent(self): + return 'PetLookerAI-lookingStop-%s' % self.doId + + def __getOtherLookerDoIdFromCollEntry(self, collEntry): + into = collEntry.getIntoNodePath() + if not into.hasTag('petLooker'): + return 0 + return int(into.getTag('petLooker')) + + def _handleLookingAtOtherStart(self, other): + if not self.__active: + PetLookerAI.notify.warning('%s: _handleLookingAtOtherStart: not active!' % self.doId) + return + if isinstance(other, CollisionEntry): + other = self.__getOtherLookerDoIdFromCollEntry(other) + if other == 0: + PetLookerAI.notify.warning('%s: looking at unknown other avatar' % self.doId) + return + PetLookerAI.notify.debug('_handleLookingAtOtherStart: %s looking at %s' % (self.doId, other)) + if other in self.others: + PetLookerAI.notify.warning('%s: other (%s) is already in self.others!' % (self.doId, other)) + if not hasattr(self, '_cHandler'): + PetLookerAI.notify.warning('-->The looker sphere has already been destroyed') + else: + self.others[other] = None + messenger.send(getStartLookingAtOtherEvent(self.doId), [other]) + messenger.send(getStartLookedAtByOtherEvent(other), [self.doId]) + return + + def _handleLookingAtOtherStop(self, other): + if not self.__active: + PetLookerAI.notify.warning('%s: _handleLookingAtOtherStop: not active!' % self.doId) + return + if isinstance(other, CollisionEntry): + other = self.__getOtherLookerDoIdFromCollEntry(other) + if other == 0: + PetLookerAI.notify.warning('%s: stopped looking at unknown other avatar' % self.doId) + return + PetLookerAI.notify.debug('_handleLookingAtOtherStop: %s no longer looking at %s' % (self.doId, other)) + if other not in self.others: + PetLookerAI.notify.warning('%s: other (%s) is not in self.others!' % (self.doId, other)) + if not hasattr(self, '_cHandler'): + PetLookerAI.notify.warning('-->The looker sphere has already been destroyed') + else: + del self.others[other] + messenger.send(getStopLookingAtOtherEvent(self.doId), [other]) + messenger.send(getStopLookedAtByOtherEvent(other), [self.doId]) diff --git a/toontown/pets/PetManager.py b/toontown/pets/PetManager.py new file mode 100755 index 00000000..472edab2 --- /dev/null +++ b/toontown/pets/PetManager.py @@ -0,0 +1,39 @@ +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from direct.task import Task + +def acquirePetManager(): + if not hasattr(base, 'petManager'): + PetManager() + base.petManager.incRefCount() + + +def releasePetManager(): + base.petManager.decRefCount() + + +class PetManager: + CollTaskName = 'petFloorCollisions' + + def __init__(self): + base.petManager = self + self.refCount = 0 + self.cTrav = CollisionTraverser('petFloorCollisions') + taskMgr.add(self._doCollisions, PetManager.CollTaskName, priority=ToontownGlobals.PetFloorCollPriority) + + def _destroy(self): + taskMgr.remove(PetManager.CollTaskName) + del self.cTrav + + def _doCollisions(self, task): + self.cTrav.traverse(render) + return Task.cont + + def incRefCount(self): + self.refCount += 1 + + def decRefCount(self): + self.refCount -= 1 + if self.refCount == 0: + self._destroy() + del base.petManager diff --git a/toontown/pets/PetManagerAI.py b/toontown/pets/PetManagerAI.py new file mode 100755 index 00000000..a7128304 --- /dev/null +++ b/toontown/pets/PetManagerAI.py @@ -0,0 +1,68 @@ +import PetUtil, PetDNA +from toontown.toonbase import ToontownGlobals, TTLocalizer +import time, random, os + +MINUTE = 60 +HOUR = 60 * MINUTE +DAY = 24 * HOUR + +def getDayId(): + return int(time.time() // DAY) + +class PetManagerAI: + NUM_DAILY_PETS = 5 # per hood + + def __init__(self, air): + self.air = air + self.generateSeeds() + + def generateSeeds(self): + seeds = range(0, 255) + random.Random(getDayId()).shuffle(seeds) + + self.seeds = {} + for hood in (ToontownGlobals.ToontownCentral, ToontownGlobals.DonaldsDock, ToontownGlobals.DaisyGardens, + ToontownGlobals.MinniesMelodyland, ToontownGlobals.TheBrrrgh, ToontownGlobals.DonaldsDreamland, + ToontownGlobals.FunnyFarm): + self.seeds[hood] = [seeds.pop() for _ in xrange(self.NUM_DAILY_PETS)] + + self.seeds['day'] = getDayId() + + def getAvailablePets(self, safezoneId): + if self.seeds.get('day', -1) != getDayId(): + self.generateSeeds() + + return list(set(self.seeds.get(safezoneId, [random.randint(0, 255)]))) + + def createNewPetFromSeed(self, avId, seed, nameIndex, gender, safeZoneId): + av = self.air.doId2do[avId] + + name = TTLocalizer.getPetName(nameIndex) + _, dna, traitSeed = PetUtil.getPetInfoFromSeed(seed, safeZoneId) + head, ears, nose, tail, body, color, cs, eye, _ = dna + numGenders = len(PetDNA.PetGenders) + gender %= numGenders + + fields = {'setOwnerId' : avId, 'setPetName' : name, 'setTraitSeed' : traitSeed, 'setSafeZone' : safeZoneId, + 'setHead' : head, 'setEars' : ears, 'setNose' : nose, 'setTail' : tail, 'setBodyTexture' : body, + 'setColor' : color, 'setColorScale' : cs, 'setEyeColor' : eye, 'setGender' : gender} + + def response(doId): + if not doId: + self.air.notify.warning("Cannot create pet for %s!" % avId) + return + + self.air.writeServerEvent('bought-pet', avId, doId) + av.b_setPetId(doId) + + self.air.dbInterface.createObject(self.air.dbId, self.air.dclassesByName['DistributedPetAI'], + {k: (v,) for k,v in fields.items()}, response) + + def deleteToonsPet(self, avId): + av = self.air.doId2do[avId] + pet = av.getPetId() + if pet: + if pet in self.air.doId2do: + self.air.doId2do[pet].requestDelete() + + av.b_setPetId(0) \ No newline at end of file diff --git a/toontown/pets/PetMood.py b/toontown/pets/PetMood.py new file mode 100755 index 00000000..79c535b4 --- /dev/null +++ b/toontown/pets/PetMood.py @@ -0,0 +1,215 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from direct.showbase.PythonUtil import lerp, average +from toontown.toonbase.PythonUtil import clampScalar +from toontown.toonbase import TTLocalizer +import random, time, weakref + +class PetMood: + notify = DirectNotifyGlobal.directNotify.newCategory('PetMood') + Neutral = 'neutral' + Components = ('boredom', 'restlessness', 'playfulness', 'loneliness', 'sadness', 'affection', 'hunger', 'confusion', 'excitement', 'fatigue', 'anger', 'surprise') + SerialNum = 0 + ContentedMoods = ('neutral', 'excitement', 'playfulness', 'affection') + ExcitedMoods = ('excitement', 'playfulness') + UnhappyMoods = ('boredom', 'restlessness', 'loneliness', 'sadness', 'fatigue', 'hunger', 'anger') + DisabledDominants = ('restlessness', 'playfulness') + AssertiveDominants = ('fatigue',) + HOUR = 1.0 + MINUTE = HOUR / 60.0 + DAY = 24.0 * HOUR + WEEK = 7 * DAY + LONGTIME = 5000 * WEEK + TBoredom = 12 * HOUR + TRestlessness = 18 * HOUR + TPlayfulness = -1 * HOUR + TLoneliness = 24 * HOUR + TSadness = -1 * HOUR + TFatigue = -15 * MINUTE + THunger = 24 * HOUR + TConfusion = -5 * MINUTE + TExcitement = -5 * MINUTE + TSurprise = -5 * MINUTE + TAffection = -10 * MINUTE + TAngerDec = -20 * MINUTE + TAngerInc = 2 * WEEK + + def __init__(self, pet = None): + self.setPet(pet) + self.started = 0 + self.serialNum = PetMood.SerialNum + PetMood.SerialNum += 1 + for comp in PetMood.Components: + self.__dict__[comp] = 0.0 + + def calcDrift(baseT, trait, fasterDriftIsBetter = False): + value = trait.percentile + if not trait.higherIsBetter: + value = 1.0 - value + if fasterDriftIsBetter: + if value < 0.5: + factor = lerp(2.0, 1.0, value * 2.0) + else: + rebased = (value - 0.5) * 2.0 + factor = lerp(1.0, 0.1, rebased * rebased) + elif value < 0.5: + factor = lerp(0.75, 1.0, value * 2.0) + else: + rebased = (value - 0.5) * 2.0 + factor = lerp(1.0, 28.0, rebased * rebased) + return baseT * factor + + pet = self.getPet() + self.tBoredom = calcDrift(PetMood.TBoredom, pet.traits.traits['boredomThreshold']) + self.tRestlessness = calcDrift(PetMood.TRestlessness, pet.traits.traits['restlessnessThreshold']) + self.tPlayfulness = calcDrift(PetMood.TPlayfulness, pet.traits.traits['playfulnessThreshold']) + self.tLoneliness = calcDrift(PetMood.TLoneliness, pet.traits.traits['lonelinessThreshold']) + self.tSadness = calcDrift(PetMood.TSadness, pet.traits.traits['sadnessThreshold'], True) + self.tFatigue = calcDrift(PetMood.TFatigue, pet.traits.traits['fatigueThreshold'], True) + self.tHunger = calcDrift(PetMood.THunger, pet.traits.traits['hungerThreshold']) + self.tConfusion = calcDrift(PetMood.TConfusion, pet.traits.traits['confusionThreshold'], True) + self.tExcitement = calcDrift(PetMood.TExcitement, pet.traits.traits['excitementThreshold']) + self.tSurprise = calcDrift(PetMood.TSurprise, pet.traits.traits['surpriseThreshold'], True) + self.tAffection = calcDrift(PetMood.TAffection, pet.traits.traits['affectionThreshold']) + self.tAngerDec = calcDrift(PetMood.TAngerDec, pet.traits.traits['angerThreshold'], True) + self.tAngerInc = calcDrift(PetMood.TAngerInc, pet.traits.traits['angerThreshold']) + self.dominantMood = PetMood.Neutral + + def destroy(self): + self.stop() + del self.petRef + + def setPet(self, pet): + self.petRef = weakref.ref(pet) + + def getPet(self): + pet = self.petRef() + if pet is None: + self.notify.error('pet has been deleted') + return pet + + def getMoodDriftTaskName(self): + return 'petMoodDrift-%s' % self.serialNum + + def getMoodChangeEvent(self): + return 'petMoodChange-%s' % self.serialNum + + def getDominantMoodChangeEvent(self): + return 'petDominantMoodChange-%s' % self.serialNum + + def announceChange(self, components = []): + oldMood = self.dominantMood + if hasattr(self, 'dominantMood'): + del self.dominantMood + newMood = self.getDominantMood() + messenger.send(self.getMoodChangeEvent(), [components]) + if newMood != oldMood: + messenger.send(self.getDominantMoodChangeEvent(), [newMood]) + + def getComponent(self, compName): + return self.__dict__[compName] + + def setComponent(self, compName, value, announce = 1): + different = self.__dict__[compName] != value + self.__dict__[compName] = value + if announce and different: + self.announceChange([compName]) + + def _getComponentThreshold(self, compName): + threshName = compName + 'Threshold' + pet = self.getPet() + return pet.traits.__dict__[threshName] + + def isComponentActive(self, compName): + return self.getComponent(compName) >= self._getComponentThreshold(compName) + + def anyActive(self, compNames): + for comp in compNames: + if self.isComponentActive(comp): + return 1 + + return 0 + + def getDominantMood(self): + if hasattr(self, 'dominantMood'): + return self.dominantMood + dominantMood = PetMood.Neutral + priority = 1.0 + for comp in PetMood.Components: + if comp in PetMood.DisabledDominants: + continue + value = self.getComponent(comp) + pri = value / max(self._getComponentThreshold(comp), 0.01) + if pri >= priority: + dominantMood = comp + priority = pri + elif comp in PetMood.AssertiveDominants and pri >= 1.0: + dominantMood = comp + + self.dominantMood = dominantMood + return dominantMood + + def makeCopy(self): + other = PetMood(self.getPet()) + for comp in PetMood.Components: + other.__dict__[comp] = self.__dict__[comp] + + return other + + def start(self): + pet = self.getPet() + taskMgr.doMethodLater(simbase.petMoodDriftPeriod / simbase.petMoodTimescale * random.random(), self._driftMoodTask, self.getMoodDriftTaskName()) + self.started = 1 + + def stop(self): + if not self.started: + return + self.started = 0 + taskMgr.remove(self.getMoodDriftTaskName()) + + def driftMood(self, dt = None, curMood = None): + now = globalClock.getFrameTime() + if not hasattr(self, 'lastDriftTime'): + self.lastDriftTime = now + if dt is None: + dt = now - self.lastDriftTime + self.lastDriftTime = now + if dt <= 0.0: + return + if curMood is None: + curMood = self + + def doDrift(curValue, timeToMedian, dt = float(dt)): + newValue = curValue + dt / (timeToMedian * 7200) + return clampScalar(newValue, 0.0, 1.0) + + self.boredom = doDrift(curMood.boredom, self.tBoredom) + self.loneliness = doDrift(curMood.loneliness, self.tLoneliness) + self.sadness = doDrift(curMood.sadness, self.tSadness) + self.fatigue = doDrift(curMood.fatigue, self.tFatigue) + self.hunger = doDrift(curMood.hunger, self.tHunger) + self.confusion = doDrift(curMood.confusion, self.tConfusion) + self.excitement = doDrift(curMood.excitement, self.tExcitement) + self.surprise = doDrift(curMood.surprise, self.tSurprise) + self.affection = doDrift(curMood.affection, self.tAffection) + abuse = average(curMood.hunger, curMood.hunger, curMood.hunger, curMood.boredom, curMood.loneliness) + tipPoint = 0.6 + if abuse < tipPoint: + tAnger = lerp(self.tAngerDec, -PetMood.LONGTIME, abuse / tipPoint) + else: + tAnger = lerp(PetMood.LONGTIME, self.tAngerInc, (abuse - tipPoint) / (1.0 - tipPoint)) + self.anger = doDrift(curMood.anger, tAnger) + self.announceChange() + return + + def _driftMoodTask(self, task = None): + self.driftMood() + taskMgr.doMethodLater(simbase.petMoodDriftPeriod / simbase.petMoodTimescale, self._driftMoodTask, self.getMoodDriftTaskName()) + return Task.done + + def __repr__(self): + s = '%s' % self.__class__.__name__ + for comp in PetMood.Components: + s += '\n %s: %s' % (comp, self.__dict__[comp]) + + return s diff --git a/toontown/pets/PetMoverAI.py b/toontown/pets/PetMoverAI.py new file mode 100755 index 00000000..a9c9df27 --- /dev/null +++ b/toontown/pets/PetMoverAI.py @@ -0,0 +1,187 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.fsm.FSM import * +import random, math + +estateRadius = 130 +estateCenter = (0, -40) + +houseRadius = 15 +houses = ((60, 10), (42, 75), (-37, 35), (80, -80), (-70, -120), (-55, -40)) + +dist = 2 + +def inCircle(x, y, c=estateCenter, r=estateRadius): + center_x, center_y = c + square_dist = (center_x - x) ** 2 + (center_y - y) ** 2 + return square_dist <= r ** 2 + +def housePointCollision(x, y): + for i, h in enumerate(houses): + if inCircle(x, y, h, houseRadius): + return 1 + + return 0 + +def generatePos(): + def get(): + r = random.randint(0, estateRadius) - estateRadius / 2 + r2 = random.randint(0, estateRadius) - estateRadius / 2 + x = r + estateCenter[0] + y = r2 + estateCenter[1] + assert inCircle(x, y) + return x, y + + p = get() + while housePointCollision(*p): + p = get() + + return p + +def lineInCircle(pt1, pt2, circlePoint, circleRadius=houseRadius): + x1, y1 = pt1 + x2, y2 = pt2 + + dist = math.hypot(x2 - x1, y2 - y1) + if dist == 0: + return 0 + + dx = (x2 - x1) / dist + dy = (y2 - y1) / dist + + t = dx * (circlePoint[0] - x1) + dy * (circlePoint[1] - y1) + + ex = t * dx + x1 + ey = t * dy + y1 + + d2 = math.hypot(ex - circlePoint[0], ey - circlePoint[1]) + return d2 <= circleRadius + +def houseCollision(pt1, pt2): + for i, h in enumerate(houses): + if lineInCircle(pt1, pt2, h): + return 1 + + return 0 + +def generatePath(start, end): + points = [start] + if not houseCollision(start, end): + points.append(end) + return points + + while True: + next = generatePos() + while houseCollision(points[-1], next): + next = generatePos() + + points.append(next) + if not houseCollision(next, end): + points.append(end) + return points + +def angle(A, B): + ax = A.getX() + ay = A.getY() + + bx = B.getX() + by = B.getY() + + return math.atan2(by-ay, bx-ax) + +class PetMoverAI(FSM): + def __init__(self, pet): + self.pet = pet + FSM.__init__(self, 'PetMoverAI-%d' % self.pet.doId) + self.chaseTarget = None + self.__seq = None + self.fwdSpeed = 10.0 + self.rotSpeed = 360.0 + self.__moveFromStill() + self.__chaseCallback = None + + def enterStill(self): + taskMgr.doMethodLater(random.randint(15, 60), self.__moveFromStill, self.pet.uniqueName('next-state')) + + def exitStill(self): + taskMgr.remove(self.pet.uniqueName('next-state')) + + def __moveFromStill(self, task=None): + choices = ["Wander"] + nextState = random.choice(choices) + self.request(nextState) + + def enterWander(self): + target = self.getPoint() + self.walkToPoint(target) + + def getPoint(self): + x, y = generatePos() + return Point3(x, y, 0) + + def walkToPoint(self, target): + here = self.pet.getPos() + dist = Vec3(here - target).length() + + self.__seq = Sequence(Func(self.pet.lookAt, target), Func(self.pet.setP, 0), self.pet.posInterval(dist / self.fwdSpeed, target, here), + Func(self.__stateComplete)) + self.__seq.start() + + def exitWander(self): + if self.__seq: + self.__seq.pause() + + self.__seq = None + + def __stateComplete(self): + try: + self.request("Still") + except: + pass + + def destroy(self): + self.demand("Off") + + def setFwdSpeed(self, speed): + self.fwdSpeed = speed + + def getFwdSpeed(self): + return self.fwdSpeed + + def setRotSpeed(self, speed): + self.rotSpeed = speed + + def getRotSpeed(self): + return self.rotSpeed + + def lock(self): + if self.state != "Still": + self.demand("Still") + + def enterChase(self, target=None): + if not target: + target = hidden.attachNewNode('target') + target.setPos(self.getPoint()) + + pos = target.getPos() + theta = angle(self.pet.getPos(), pos) * (math.pi / 180) + dx = dist * math.cos(theta) + dy = dist * math.sin(theta) + + self.walkToPoint(Point3(pos.getX() - dx, pos.getY() - dy, pos.getZ())) + + def exitChase(self): + if self.__chaseCallback: + self.__chaseCallback() + self.__chaseCallback = None + + if self.__seq: + self.__seq.pause() + + self.__seq = None + + def walkToAvatar(self, av, callback=None): + if callback: + self.__chaseCallback = callback + + self.demand("Chase", av) diff --git a/toontown/pets/PetObserve.py b/toontown/pets/PetObserve.py new file mode 100755 index 00000000..e5021188 --- /dev/null +++ b/toontown/pets/PetObserve.py @@ -0,0 +1,203 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.PythonUtil import list2dict, Enum +from toontown.pets import PetTricks +import types +notify = DirectNotifyGlobal.directNotify.newCategory('PetObserve') + +def getEventName(zoneId): + return 'PetObserve-%s' % zoneId + + +def send(zoneIds, petObserve): + if petObserve.isValid(): + if type(zoneIds) not in (types.ListType, types.TupleType): + zoneIds = [zoneIds] + for zoneId in zoneIds: + messenger.send(getEventName(zoneId), [petObserve]) + + +Phrases = Enum('HI, BYE, YES, NO, SOOTHE, PRAISE, CRITICISM, HAPPY,SAD, ANGRY, HURRY, QUESTION, FRIENDLY, LETS_PLAY,COME, FOLLOW_ME, STAY, NEED_LAFF, NEED_GAGS, NEED_JB,GO_AWAY, DO_TRICK,') +Actions = Enum('FEED, SCRATCH,ATTENDED_START, ATTENDED_STOP,ATTENDING_START, ATTENDING_STOP,CHANGE_ZONE, LOGOUT,GARDEN') + +class PetObserve: + + def isValid(self): + return 1 + + def isForgettable(self): + return 0 + + def _influence(self, petBrain): + petBrain._handleGenericObserve(self) + + def __repr__(self): + return '%s()' % self.__class__.__name__ + + +class PetActionObserve(PetObserve): + + def __init__(self, action, avId, data = None): + self.action = action + self.avId = avId + self.data = data + + def getAction(self): + return self.action + + def getAvId(self): + return self.avId + + def getData(self): + return self.data + + def _influence(self, petBrain): + petBrain._handleActionObserve(self) + + def __repr__(self): + return '%s(%s,%s)' % (self.__class__.__name__, Actions.getString(self.action), self.avId) + + +class PetPhraseObserve(PetObserve): + + def __init__(self, petPhrase, avId): + self.petPhrase = petPhrase + self.avId = avId + + def getPetPhrase(self): + return self.petPhrase + + def getAvId(self): + return self.avId + + def isForgettable(self): + return 1 + + def _influence(self, petBrain): + petBrain._handlePhraseObserve(self) + + def __repr__(self): + return '%s(%s,%s)' % (self.__class__.__name__, Phrases.getString(self.petPhrase), self.avId) + + +class SCObserve(PetPhraseObserve): + + def __init__(self, msgId, petPhrase, avId): + self.msgId = msgId + PetPhraseObserve.__init__(self, petPhrase, avId) + + def isValid(self): + return self.petPhrase is not None + + +class TrickRequestObserve(PetPhraseObserve): + + def __init__(self, trickId, avId): + self.trickId = trickId + PetPhraseObserve.__init__(self, Phrases.DO_TRICK, avId) + + def isForgettable(self): + return 0 + + def getTrickId(self): + return self.trickId + + +OP = Phrases +_scPhrase2petPhrase = {1: OP.YES, + 2: OP.NO, + 3: OP.SOOTHE, + 100: OP.HI, + 101: OP.HI, + 102: OP.HI, + 103: OP.HI, + 104: OP.HI, + 105: OP.HI, + 107: OP.HI, + 108: OP.HI, + 200: OP.BYE, + 201: OP.BYE, + 202: OP.BYE, + 203: OP.BYE, + 204: OP.BYE, + 205: OP.BYE, + 206: OP.BYE, + 207: OP.BYE, + 300: OP.HAPPY, + 301: OP.HAPPY, + 302: OP.HAPPY, + 303: OP.HAPPY, + 304: OP.HAPPY, + 305: OP.HAPPY, + 306: OP.HAPPY, + 307: OP.HAPPY, + 308: OP.HAPPY, + 309: OP.HAPPY, + 310: OP.HAPPY, + 311: OP.HAPPY, + 312: OP.HAPPY, + 313: OP.HAPPY, + 314: OP.HAPPY, + 315: OP.HAPPY, + 400: OP.SAD, + 401: OP.SAD, + 402: OP.SAD, + 403: OP.SAD, + 404: OP.SAD, + 405: OP.SAD, + 406: OP.SAD, + 407: OP.NO, + 410: OP.NEED_LAFF, + 500: OP.FRIENDLY, + 505: OP.PRAISE, + 506: OP.HAPPY, + 507: OP.FRIENDLY, + 508: OP.FRIENDLY, + 509: OP.FRIENDLY, + 510: OP.QUESTION, + 511: OP.QUESTION, + 513: OP.QUESTION, + 514: OP.NEED_LAFF, + 600: OP.PRAISE, + 601: OP.PRAISE, + 602: OP.PRAISE, + 603: OP.PRAISE, + 700: OP.PRAISE, + 701: OP.PRAISE, + 900: OP.CRITICISM, + 901: OP.CRITICISM, + 902: OP.CRITICISM, + 903: OP.CRITICISM, + 904: OP.CRITICISM, + 905: OP.CRITICISM, + 1006: OP.FOLLOW_ME, + 1007: OP.STAY, + 1010: OP.STAY, + 1015: OP.STAY, + 1201: OP.CRITICISM, + 1300: OP.NEED_LAFF, + 1400: OP.HURRY, + 1404: OP.PRAISE, + 1405: OP.PRAISE, + 1413: OP.NEED_GAGS, + 1414: OP.NEED_LAFF, + 1601: OP.NEED_JB, + 1603: OP.HURRY, + 1605: OP.LETS_PLAY, + 1606: OP.LETS_PLAY, + 21000: OP.COME, + 21001: OP.COME, + 21002: OP.STAY, + 21003: OP.PRAISE, + 21004: OP.PRAISE, + 21005: OP.PRAISE} +for scId in PetTricks.ScId2trickId: + _scPhrase2petPhrase[scId] = OP.DO_TRICK + +del OP + +def getSCObserve(msgId, speakerDoId): + phrase = _scPhrase2petPhrase.get(msgId) + if phrase == Phrases.DO_TRICK: + trickId = PetTricks.ScId2trickId[msgId] + return TrickRequestObserve(trickId, speakerDoId) + return SCObserve(msgId, phrase, speakerDoId) diff --git a/toontown/pets/PetTraits.py b/toontown/pets/PetTraits.py new file mode 100755 index 00000000..27a49bd1 --- /dev/null +++ b/toontown/pets/PetTraits.py @@ -0,0 +1,248 @@ +from direct.showbase.PythonUtil import randFloat, normalDistrib, Enum +from toontown.toonbase.PythonUtil import clampScalar +from toontown.toonbase import TTLocalizer, ToontownGlobals +import random, copy +TraitDivisor = 10000 + +def getTraitNames(): + if not hasattr(PetTraits, 'TraitNames'): + traitNames = [] + for desc in PetTraits.TraitDescs: + traitNames.append(desc[0]) + PetTraits.TraitNames = traitNames + + return PetTraits.TraitNames + + +def uniform(min, max, rng): + return randFloat(min, max, rng.random) + + +def gaussian(min, max, rng): + return normalDistrib(min, max, rng.gauss) + + +class TraitDistribution: + TraitQuality = Enum('VERY_BAD, BAD, AVERAGE, GOOD, VERY_GOOD') + TraitTypes = Enum('INCREASING, DECREASING') + Sz2MinMax = None + TraitType = None + TraitCutoffs = {TraitTypes.INCREASING: {TraitQuality.VERY_BAD: 0.1, + TraitQuality.BAD: 0.25, + TraitQuality.GOOD: 0.75, + TraitQuality.VERY_GOOD: 0.9}, + TraitTypes.DECREASING: {TraitQuality.VERY_BAD: 0.9, + TraitQuality.BAD: 0.75, + TraitQuality.GOOD: 0.25, + TraitQuality.VERY_GOOD: 0.1}} + + def __init__(self, rndFunc = gaussian): + self.rndFunc = rndFunc + if not hasattr(self.__class__, 'GlobalMinMax'): + _min = 1.0 + _max = 0.0 + minMax = self.Sz2MinMax + for sz in minMax: + thisMin, thisMax = minMax[sz] + _min = min(_min, thisMin) + _max = max(_max, thisMax) + + self.__class__.GlobalMinMax = [_min, _max] + + def getRandValue(self, szId, rng = random): + min, max = self.getMinMax(szId) + return self.rndFunc(min, max, rng) + + def getHigherIsBetter(self): + return self.TraitType == TraitDistribution.TraitTypes.INCREASING + + def getMinMax(self, szId): + return (self.Sz2MinMax[szId][0], self.Sz2MinMax[szId][1]) + + def getGlobalMinMax(self): + return (self.GlobalMinMax[0], self.GlobalMinMax[1]) + + def _getTraitPercent(self, traitValue): + gMin, gMax = self.getGlobalMinMax() + if traitValue < gMin: + gMin = traitValue + elif traitValue > gMax: + gMax = traitValue + return (traitValue - gMin) / (gMax - gMin) + + def getPercentile(self, traitValue): + if self.TraitType is TraitDistribution.TraitTypes.INCREASING: + return self._getTraitPercent(traitValue) + else: + return 1.0 - self._getTraitPercent(traitValue) + + def getQuality(self, traitValue): + TraitQuality = TraitDistribution.TraitQuality + TraitCutoffs = self.TraitCutoffs[self.TraitType] + percent = self._getTraitPercent(traitValue) + if self.TraitType is TraitDistribution.TraitTypes.INCREASING: + if percent <= TraitCutoffs[TraitQuality.VERY_BAD]: + return TraitQuality.VERY_BAD + elif percent <= TraitCutoffs[TraitQuality.BAD]: + return TraitQuality.BAD + elif percent >= TraitCutoffs[TraitQuality.VERY_GOOD]: + return TraitQuality.VERY_GOOD + elif percent >= TraitCutoffs[TraitQuality.GOOD]: + return TraitQuality.GOOD + else: + return TraitQuality.AVERAGE + elif percent <= TraitCutoffs[TraitQuality.VERY_GOOD]: + return TraitQuality.VERY_GOOD + elif percent <= TraitCutoffs[TraitQuality.GOOD]: + return TraitQuality.GOOD + elif percent >= TraitCutoffs[TraitQuality.VERY_BAD]: + return TraitQuality.VERY_BAD + elif percent >= TraitCutoffs[TraitQuality.BAD]: + return TraitQuality.BAD + else: + return TraitQuality.AVERAGE + + def getExtremeness(self, traitValue): + percent = self._getTraitPercent(traitValue) + if percent < 0.5: + howExtreme = (0.5 - percent) * 2.0 + else: + howExtreme = (percent - 0.5) * 2.0 + return clampScalar(howExtreme, 0.0, 1.0) + + +class PetTraits: + + class StdIncDistrib(TraitDistribution): + TraitType = TraitDistribution.TraitTypes.INCREASING + Sz2MinMax = {ToontownGlobals.ToontownCentral: (0.2, 0.65), + ToontownGlobals.DonaldsDock: (0.3, 0.7), + ToontownGlobals.DaisyGardens: (0.4, 0.75), + ToontownGlobals.MinniesMelodyland: (0.5, 0.8), + ToontownGlobals.TheBrrrgh: (0.6, 0.85), + ToontownGlobals.DonaldsDreamland: (0.7, 0.9)} + + class StdDecDistrib(TraitDistribution): + TraitType = TraitDistribution.TraitTypes.DECREASING + Sz2MinMax = {ToontownGlobals.ToontownCentral: (0.35, 0.8), + ToontownGlobals.DonaldsDock: (0.3, 0.7), + ToontownGlobals.DaisyGardens: (0.25, 0.6), + ToontownGlobals.MinniesMelodyland: (0.2, 0.5), + ToontownGlobals.TheBrrrgh: (0.15, 0.4), + ToontownGlobals.DonaldsDreamland: (0.1, 0.3)} + + class ForgetfulnessDistrib(TraitDistribution): + TraitType = TraitDistribution.TraitTypes.DECREASING + Sz2MinMax = {ToontownGlobals.ToontownCentral: (0.0, 1.0), + ToontownGlobals.DonaldsDock: (0.0, 0.9), + ToontownGlobals.DaisyGardens: (0.0, 0.8), + ToontownGlobals.MinniesMelodyland: (0.0, 0.7), + ToontownGlobals.TheBrrrgh: (0.0, 0.6), + ToontownGlobals.DonaldsDreamland: (0.0, 0.5)} + + TraitDescs = (('forgetfulness', ForgetfulnessDistrib(), True), + ('boredomThreshold', StdIncDistrib(), True), + ('restlessnessThreshold', StdIncDistrib(), True), + ('playfulnessThreshold', StdDecDistrib(), True), + ('lonelinessThreshold', StdIncDistrib(), True), + ('sadnessThreshold', StdIncDistrib(), True), + ('fatigueThreshold', StdIncDistrib(), True), + ('hungerThreshold', StdIncDistrib(), True), + ('confusionThreshold', StdIncDistrib(), True), + ('excitementThreshold', StdDecDistrib(), True), + ('angerThreshold', StdIncDistrib(), True), + ('surpriseThreshold', StdIncDistrib(), False), + ('affectionThreshold', StdDecDistrib(), True)) + NumTraits = len(TraitDescs) + + class Trait: + + def __init__(self, index, traitsObj, value = None): + self.name, distrib, self.hasWorth = PetTraits.TraitDescs[index] + if value is not None: + self.value = value + else: + szId = traitsObj.safeZoneId + self.value = distrib.getRandValue(szId, traitsObj.rng) + self.value = int(self.value * TraitDivisor) / float(TraitDivisor) + self.higherIsBetter = distrib.getHigherIsBetter() + self.percentile = distrib.getPercentile(self.value) + self.quality = distrib.getQuality(self.value) + self.howExtreme = distrib.getExtremeness(self.value) + return + + def __repr__(self): + return 'Trait: %s, %s, %s, %s' % (self.name, + self.value, + TraitDistribution.TraitQuality.getString(self.quality), + self.howExtreme) + + def __init__(self, traitSeed, safeZoneId, traitValueList = []): + self.traitSeed = traitSeed + self.safeZoneId = safeZoneId + self.rng = random.Random(self.traitSeed) + self.traits = {} + for i in xrange(len(PetTraits.TraitDescs)): + if i < len(traitValueList) and traitValueList[i] > 0.0: + trait = PetTraits.Trait(i, self, traitValueList[i]) + else: + trait = PetTraits.Trait(i, self) + self.traits[trait.name] = trait + self.__dict__[trait.name] = trait.value + + extremeTraits = [] + for trait in self.traits.values(): + if not trait.hasWorth: + continue + if trait.quality == TraitDistribution.TraitQuality.AVERAGE: + continue + i = 0 + while i < len(extremeTraits) and extremeTraits[i].howExtreme > trait.howExtreme: + i += 1 + + extremeTraits.insert(i, trait) + + self.extremeTraits = [] + for trait in extremeTraits: + self.extremeTraits.append((trait.name, trait.quality)) + + def getValueList(self): + traitValues = [] + for desc in PetTraits.TraitDescs: + traitName = desc[0] + traitValues.append(self.traits[traitName].value) + + return traitValues + + def getTraitValue(self, traitName): + return self.traits[traitName].value + + def getExtremeTraits(self): + return copy.copy(self.extremeTraits) + + def getOverallValue(self): + total = 0 + numUsed = 0 + for trait in self.traits.values(): + if trait.hasWorth: + if trait.higherIsBetter: + value = trait.value + else: + value = 1.0 - trait.value + total += value + numUsed += 1 + + value = total / len(self.traits.values()) + return value + + def getExtremeTraitDescriptions(self): + descs = [] + TraitQuality = TraitDistribution.TraitQuality + Quality2index = {TraitQuality.VERY_BAD: 0, + TraitQuality.BAD: 1, + TraitQuality.GOOD: 2, + TraitQuality.VERY_GOOD: 3} + for name, quality in self.extremeTraits: + descs.append(TTLocalizer.PetTrait2descriptions[name][Quality2index[quality]]) + + return descs diff --git a/toontown/pets/PetTricks.py b/toontown/pets/PetTricks.py new file mode 100755 index 00000000..e5fcecf1 --- /dev/null +++ b/toontown/pets/PetTricks.py @@ -0,0 +1,104 @@ +from direct.showbase.PythonUtil import Enum, invertDictLossless +from direct.interval.IntervalGlobal import * +import types +import random +Tricks = Enum('JUMP, BEG, PLAYDEAD, ROLLOVER, BACKFLIP, DANCE, SPEAK, BALK,') +NonHappyMinActualTrickAptitude = 0.1 +NonHappyMaxActualTrickAptitude = 0.6 +MinActualTrickAptitude = 0.5 +MaxActualTrickAptitude = 0.97 +AptitudeIncrementDidTrick = 0.0005 +MaxAptitudeIncrementGotPraise = 0.0003 +MaxTrickFatigue = 0.65 +MinTrickFatigue = 0.1 +ScId2trickId = {21200: Tricks.JUMP, + 21201: Tricks.BEG, + 21202: Tricks.PLAYDEAD, + 21203: Tricks.ROLLOVER, + 21204: Tricks.BACKFLIP, + 21205: Tricks.DANCE, + 21206: Tricks.SPEAK} +TrickId2scIds = invertDictLossless(ScId2trickId) +TrickAnims = {Tricks.JUMP: 'jump', + Tricks.BEG: ('toBeg', 'beg', 'fromBeg'), + Tricks.PLAYDEAD: ('playDead', 'fromPlayDead'), + Tricks.ROLLOVER: 'rollover', + Tricks.BACKFLIP: 'backflip', + Tricks.DANCE: 'dance', + Tricks.SPEAK: 'speak', + Tricks.BALK: 'neutral'} +TrickLengths = {Tricks.JUMP: 2.0, + Tricks.BEG: 5.167, + Tricks.PLAYDEAD: 15.21, + Tricks.ROLLOVER: 8.0, + Tricks.BACKFLIP: 4.88, + Tricks.DANCE: 7.42, + Tricks.SPEAK: 0.75, + Tricks.BALK: 1.0} +TrickAccuracies = {Tricks.JUMP: 1.0, + Tricks.BEG: 0.9, + Tricks.PLAYDEAD: 0.8, + Tricks.ROLLOVER: 0.7, + Tricks.BACKFLIP: 0.6, + Tricks.DANCE: 0.5, + Tricks.SPEAK: 0.4, + Tricks.BALK: 1.0} +TrickHeals = {Tricks.JUMP: (5, 10), + Tricks.BEG: (6, 12), + Tricks.PLAYDEAD: (7, 14), + Tricks.ROLLOVER: (8, 16), + Tricks.BACKFLIP: (9, 18), + Tricks.DANCE: (10, 20), + Tricks.SPEAK: (11, 22), + Tricks.BALK: (0, 0)} +TrickSounds = {Tricks.BACKFLIP: 'phase_5/audio/sfx/backflip.ogg', + Tricks.ROLLOVER: 'phase_5/audio/sfx/rollover.ogg', + Tricks.PLAYDEAD: 'phase_5/audio/sfx/play_dead.ogg', + Tricks.BEG: 'phase_5/audio/sfx/beg.ogg', + Tricks.DANCE: 'phase_5/audio/sfx/heal_dance.ogg', + Tricks.JUMP: 'phase_5/audio/sfx/jump.ogg', + Tricks.SPEAK: 'phase_5/audio/sfx/speak_v1.ogg'} + +def getSoundIval(trickId): + sounds = TrickSounds.get(trickId, None) + if sounds: + if type(sounds) == types.StringType: + sound = loader.loadSfx(sounds) + return SoundInterval(sound) + else: + soundIval = Sequence() + for s in sounds: + sound = loader.loadSfx(s) + soundIval.append(SoundInterval(sound)) + + return soundIval + return + + +def getTrickIval(pet, trickId): + anims = TrickAnims[trickId] + animRate = random.uniform(0.9, 1.1) + waitTime = random.uniform(0.0, 1.0) + if type(anims) == types.StringType: + if trickId == Tricks.JUMP: + animIval = Parallel() + animIval.append(ActorInterval(pet, anims, playRate=animRate)) + animIval.append(Sequence(Wait(0.36), ProjectileInterval(pet, startPos=pet.getPos(), endPos=pet.getPos(), duration=1.0, gravityMult=0.5))) + elif trickId == Tricks.ROLLOVER: + animIval = Sequence() + animIval.append(ActorInterval(pet, anims, playRate=animRate)) + animIval.append(ActorInterval(pet, anims, playRate=-1.0 * animRate)) + elif trickId == Tricks.SPEAK: + animIval = ActorInterval(pet, anims, startFrame=10, playRate=animRate) + else: + animIval = ActorInterval(pet, anims, playRate=animRate) + else: + animIval = Sequence() + for anim in anims: + animIval.append(ActorInterval(pet, anim, playRate=animRate)) + + trickIval = Parallel(animIval) + soundIval = getSoundIval(trickId) + if soundIval: + trickIval.append(soundIval) + return Sequence(Func(pet.lockPet), Wait(waitTime), trickIval, Func(pet.unlockPet)) diff --git a/toontown/pets/PetTutorial.py b/toontown/pets/PetTutorial.py new file mode 100755 index 00000000..aeac8268 --- /dev/null +++ b/toontown/pets/PetTutorial.py @@ -0,0 +1,80 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.fsm import FSM +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class PetTutorial(DirectFrame, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('PetTutorial') + + def __init__(self, doneEvent): + FSM.FSM.__init__(self, 'PetTutorial') + self.doneEvent = doneEvent + self.setStateArray(['Page1', 'Page2', 'Page3']) + DirectFrame.__init__(self, pos=(0.0, 0.0, 0.0), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.5, 1.5, 0.9), text='', text_scale=0.06) + self['image'] = DGG.getDefaultDialogGeom() + self.title = DirectLabel(self, relief=None, text='', text_pos=(0.0, 0.32), text_fg=(1, 0, 0, 1), text_scale=TTLocalizer.PTtitle, text_font=ToontownGlobals.getSignFont()) + images = loader.loadModel('phase_5.5/models/gui/PetTutorial') + self.iPage1 = DirectFrame(self, image=images.find('**/PetTutorialPanel'), scale=0.75, pos=(-0.55, -0.1, 0)) + self.iPage1.hide() + self.iPage2 = DirectFrame(self, image=images.find('**/PetTutorialSpeedChat'), scale=0.75, pos=(0.43, -0.1, 0.05)) + self.iPage2.hide() + self.iPage3 = DirectFrame(self, image=images.find('**/PetTutorialCattlelog'), scale=0.75, pos=(-0.55, -0.1, 0)) + self.iPage3.hide() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.bNext = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), relief=None, text=TTLocalizer.PetTutorialNext, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.2, -0.3, -0.25), command=self.requestNext) + self.bPrev = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), image_scale=(-1.0, 1.0, 1.0), relief=None, text=TTLocalizer.PetTutorialPrev, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.2, -0.3, -0.25), command=self.requestPrev) + self.bQuit = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.PetTutorialDone, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.55, -0.3, -0.25), command=self.__handleQuit) + self.bQuit.hide() + buttons.removeNode() + gui.removeNode() + self.request('Page1') + return + + def enterPage1(self, *args): + self.title['text'] = (TTLocalizer.PetTutorialTitle1,) + self['text'] = TTLocalizer.PetTutorialPage1 + self['text_pos'] = TTLocalizer.PTenterPage1Pos + self['text_wordwrap'] = 16.5 + self.bPrev['state'] = DGG.DISABLED + self.iPage1.show() + + def exitPage1(self, *args): + self.bPrev['state'] = DGG.NORMAL + self.iPage1.hide() + + def enterPage2(self, *args): + self.title['text'] = (TTLocalizer.PetTutorialTitle2,) + self['text'] = TTLocalizer.PetTutorialPage2 + self['text_pos'] = TTLocalizer.PTenterPage2Pos + self['text_wordwrap'] = 13.5 + self.iPage2.show() + + def exitPage2(self, *args): + self.iPage2.hide() + + def enterPage3(self, *args): + self.title['text'] = (TTLocalizer.PetTutorialTitle3,) + self['text'] = TTLocalizer.PetTutorialPage3 + self['text_pos'] = TTLocalizer.PTenterPage3Pos + self['text_wordwrap'] = 16.5 + self.bQuit.show() + self.bNext['state'] = DGG.DISABLED + self.iPage3.show() + + def exitPage3(self, *args): + self.bNext['state'] = DGG.NORMAL + self.iPage3.hide() + self.bQuit.hide() + + def __handleQuit(self): + base.localAvatar.b_setPetTutorialDone(True) + messenger.send(self.doneEvent) diff --git a/toontown/pets/PetUtil.py b/toontown/pets/PetUtil.py new file mode 100755 index 00000000..254387a0 --- /dev/null +++ b/toontown/pets/PetUtil.py @@ -0,0 +1,29 @@ +from toontown.pets import PetDNA, PetTraits, PetConstants +from direct.showbase import PythonUtil +from toontown.toonbase import TTLocalizer +import random + +def getPetInfoFromSeed(seed, safezoneId): + S = random.getstate() + random.seed(seed) + dnaArray = PetDNA.getRandomPetDNA(safezoneId) + gender = PetDNA.getGender(dnaArray) + nameString = TTLocalizer.getRandomPetName(gender=gender, seed=seed) + traitSeed = PythonUtil.randUint31() + random.setstate(S) + return (nameString, dnaArray, traitSeed) + + +def getPetCostFromSeed(seed, safezoneId): + name, dna, traitSeed = getPetInfoFromSeed(seed, safezoneId) + traits = PetTraits.PetTraits(traitSeed, safezoneId) + traitValue = traits.getOverallValue() + traitValue -= 0.3 + traitValue = max(0, traitValue) + rarity = PetDNA.getRarity(dna) + rarity *= 1.0 - traitValue + rarity = pow(0.001, rarity) - 0.001 + minCost, maxCost = PetConstants.ZoneToCostRange[safezoneId] + cost = rarity * (maxCost - minCost) + minCost + cost = int(cost) + return cost diff --git a/toontown/pets/PetshopGUI.py b/toontown/pets/PetshopGUI.py new file mode 100755 index 00000000..6034631a --- /dev/null +++ b/toontown/pets/PetshopGUI.py @@ -0,0 +1,523 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownTimer +from direct.task import Task +from otp.namepanel import NameTumbler +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPLocalizer +from toontown.fishing import FishSellGUI +from toontown.pets import Pet, PetConstants +from toontown.pets import PetDNA +from toontown.pets import PetUtil +from toontown.pets import PetDetail +from toontown.pets import PetTraits +from toontown.hood import ZoneUtil +import string +import random +Dialog_MainMenu = 0 +Dialog_AdoptPet = 1 +Dialog_ChoosePet = 2 +Dialog_ReturnPet = 3 +Dialog_SellFish = 4 +Dialog_NamePicker = 5 +Dialog_GoHome = 6 +disabledImageColor = Vec4(0.6, 0.6, 0.6, 1) +text0Color = Vec4(0.65, 0, 0.87, 1) +text1Color = Vec4(0.65, 0, 0.87, 1) +text2Color = Vec4(1, 1, 0.5, 1) +text3Color = Vec4(0.4, 0.4, 0.4, 1) + +class PetshopGUI(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('PetshopGui') + + class GoHomeDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('GoHomeDlg') + + def __init__(self, doneEvent): + DirectFrame.__init__(self, pos=(0.0, 0.0, 0.0), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.6), text='', text_wordwrap=13.5, text_scale=0.06, text_pos=(0.0, 0.13)) + self['image'] = DGG.getDefaultDialogGeom() + self['text'] = TTLocalizer.PetshopGoHomeText + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.bYes = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.TutorialYes, text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.15, 0.0, -0.1), command=lambda : messenger.send(doneEvent, [1])) + self.bNo = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=TTLocalizer.TutorialNo, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.15, 0.0, -0.1), command=lambda : messenger.send(doneEvent, [0])) + buttons.removeNode() + gui.removeNode() + return + + class NamePicker(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PetshopGUI.NamePicker') + + def __init__(self, doneEvent, petSeed, gender): + zoneId = ZoneUtil.getCanonicalSafeZoneId(base.localAvatar.getZoneId()) + name, dna, traitSeed = PetUtil.getPetInfoFromSeed(petSeed, zoneId) + self.gui = loader.loadModel('phase_4/models/gui/PetNamePanel') + self.guiScale = 0.09 + DirectFrame.__init__(self, relief=None, geom=self.gui, geom_scale=self.guiScale, state='normal', frameSize=(-1, 1, -1, 1)) + self.initialiseoptions(PetshopGUI.NamePicker) + self.petView = self.attachNewNode('petView') + self.petView.setPos(-0.21, 0, -0.04) + self.petModel = Pet.Pet(forGui=1) + self.petModel.setDNA(dna) + self.petModel.fitAndCenterHead(0.435, forGui=1) + self.petModel.reparentTo(self.petView) + self.petModel.setH(225) + self.petModel.setScale(0.125) + self.petModel.enterNeutralHappy() + self.allNames = TTLocalizer.NeutralPetNames + if gender == 0: + self.allNames += TTLocalizer.BoyPetNames + else: + self.allNames += TTLocalizer.GirlPetNames + self.allNames.sort() + self.letters = [] + for name in self.allNames: + if name[0:TTLocalizer.PGUIcharLength] not in self.letters: + self.letters.append(name[0:TTLocalizer.PGUIcharLength]) + + self.curLetter = self.letters[0] + self.curNames = [] + self.curName = '' + self.alphabetList = self.makeScrollList(self.gui, (-0.012, 0, -0.075), (1, 0.8, 0.8, 1), self.letters, self.makeLabel, [TextNode.ACenter, 'alphabet'], 6) + self.nameList = None + self.rebuildNameList() + self.randomButton = DirectButton(parent=self, relief=None, image=(self.gui.find('**/RandomUpButton'), self.gui.find('**/RandomDownButton'), self.gui.find('**/RandomRolloverButton')), scale=self.guiScale, text=TTLocalizer.RandomButton, text_pos=(-0.8, -5.7), text_scale=0.8, text_fg=text2Color, pressEffect=False, command=self.randomName) + self.nameResult = DirectLabel(parent=self, relief=None, scale=self.guiScale, text='', text_align=TextNode.ACenter, text_pos=(-1.85, 2.6), text_fg=text0Color, text_scale=0.6, text_wordwrap=8) + self.submitButton = DirectButton(parent=self, relief=None, image=(self.gui.find('**/SubmitUpButton'), self.gui.find('**/SubmitDownButton'), self.gui.find('**/SubmitRolloverButton')), scale=self.guiScale, text=TTLocalizer.PetshopAdopt, text_pos=(3.3, -5.7), text_scale=TTLocalizer.PGUIsubmitButton, text_fg=text0Color, pressEffect=False, command=lambda : messenger.send(doneEvent, [TTLocalizer.getPetNameId(self.curName)])) + model = loader.loadModel('phase_4/models/gui/PetShopInterface') + modelScale = 0.1 + cancelImageList = (model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')) + cancelIcon = model.find('**/CancelIcon') + self.cancelButton = DirectButton(parent=self, relief=None, pos=(-0.04, 0, -0.47), image=cancelImageList, geom=cancelIcon, scale=modelScale, pressEffect=False, command=lambda : messenger.send(doneEvent, [-1])) + self.randomName() + return + + def destroy(self): + self.petModel.delete() + DirectFrame.destroy(self) + + def rebuildNameList(self): + self.curNames = [] + for name in self.allNames: + if name[0:TTLocalizer.PGUIcharLength] == self.curLetter: + self.curNames += [name] + + if self.nameList: + self.nameList.destroy() + self.nameList = self.makeScrollList(self.gui, (0.277, 0, -0.075), (1, 0.8, 0.8, 1), self.curNames, self.makeLabel, [TextNode.ACenter, 'name'], 5) + + def updateNameText(self): + self.nameResult['text'] = self.curName + + def nameClickedOn(self, listType, index): + if listType == 'alphabet': + self.curLetter = self.letters[index] + self.rebuildNameList() + elif listType == 'name': + self.curName = self.curNames[index] + self.updateNameText() + + def makeLabel(self, te, index, others): + alig = others[0] + listName = others[1] + if alig == TextNode.ARight: + newpos = (0.44, 0, 0) + elif alig == TextNode.ALeft: + newpos = (0, 0, 0) + else: + newpos = (0.2, 0, 0) + df = DirectButton(parent=self, state='normal', relief=None, text=te, text_scale=0.1, text_pos=(0.2, 0, 0), text_align=alig, textMayChange=0, command=lambda : self.nameClickedOn(listName, index)) + return df + + def makeScrollList(self, gui, ipos, mcolor, nitems, nitemMakeFunction, nitemMakeExtraArgs, nVisibleItems): + decScale = self.guiScale / 0.44 + incScale = (decScale, decScale, -decScale) + it = nitems[:] + listType = nitemMakeExtraArgs[1] + if listType == 'alphabet': + arrowList = (gui.find('**/ArrowSmUpButton'), + gui.find('**/ArrowSmUpRollover'), + gui.find('**/ArrowSmUpRollover'), + gui.find('**/ArrowSmUpButton')) + fHeight = 0.09 + elif listType == 'name': + arrowList = (gui.find('**/ArrowUpBigButton'), + gui.find('**/ArrowUpBigRollover'), + gui.find('**/ArrowUpBigRollover'), + gui.find('**/ArrowUpBigButton')) + fHeight = 0.119 + ds = DirectScrolledList(parent=self, items=it, itemMakeFunction=nitemMakeFunction, itemMakeExtraArgs=nitemMakeExtraArgs, relief=None, command=None, pos=ipos, scale=0.44, incButton_image=arrowList, incButton_image_pos=(1.015, 0, 3.32), incButton_relief=None, incButton_scale=incScale, incButton_image3_color=Vec4(0.4, 0.4, 0.4, 1), decButton_image=arrowList, decButton_image_pos=(1.015, 0, 1.11), decButton_relief=None, decButton_scale=decScale, decButton_image3_color=Vec4(0.4, 0.4, 0.4, 1), numItemsVisible=nVisibleItems, forceHeight=fHeight) + return ds + + def randomName(self): + numNames = len(self.allNames) + self.curName = self.allNames[random.randrange(numNames)] + self.curLetter = self.curName[0:TTLocalizer.PGUIcharLength] + self.rebuildNameList() + self.updateNameText() + self.alphabetList.scrollTo(self.letters.index(self.curLetter)) + self.nameList.scrollTo(self.curNames.index(self.curName)) + + class MainMenuDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PetshopGUI.MainMenuDlg') + + def __init__(self, doneEvent): + model = loader.loadModel('phase_4/models/gui/AdoptReturnSell') + modelPos = (0, 0, -0.3) + modelScale = 0.055 + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_scale=(modelScale, modelScale, modelScale), pos=modelPos, frameSize=(-1, 1, -1, 1)) + self.initialiseoptions(PetshopGUI.MainMenuDlg) + textScale = TTLocalizer.PGUItextScale + sellFishImageList = (model.find('**/SellButtonUp'), + model.find('**/SellButtonDown'), + model.find('**/SellButtonRollover'), + model.find('**/SellButtonDown')) + fishLogoImageList = model.find('**/Fish') + cancelImageList = (model.find('**/CancelButtonUp'), model.find('**/cancelButtonDown'), model.find('**/CancelButtonRollover')) + XImageList = model.find('**/CancelIcon') + adoptImageList = (model.find('**/AdoptButtonUp'), model.find('**/AdoptButtonDown'), model.find('**/AdoptButtonRollover')) + pawLogoAdoptImageList = model.find('**/PawPink') + returnImageList = (model.find('**/ReturnButtonUp'), + model.find('**/ReturnButtonDown'), + model.find('**/ReturnButtonRollover'), + model.find('**/ReturnButtonDown')) + pawLogoReturnImageList = model.find('**/PawYellow') + self.cancelButton = DirectButton(parent=self, relief=None, scale=(modelScale, modelScale, modelScale), geom=XImageList, image=cancelImageList, text=('', TTLocalizer.PetshopCancel), text_pos=TTLocalizer.PGUIcancelButtonPos, text_scale=0.8, pressEffect=False, command=lambda : messenger.send(doneEvent, [0])) + self.sellFishButton = DirectButton(parent=self, relief=None, image=sellFishImageList, image3_color=disabledImageColor, geom=fishLogoImageList, scale=(modelScale, modelScale, modelScale), text=TTLocalizer.PetshopSell, text_scale=textScale, text_pos=(0, 6), text0_fg=text2Color, text1_fg=text2Color, text2_fg=text0Color, text3_fg=text3Color, pressEffect=False, command=lambda : messenger.send(doneEvent, [1])) + fishValue = base.localAvatar.fishTank.getTotalValue() + if fishValue == 0: + self.sellFishButton['state'] = DGG.DISABLED + self.adoptPetButton = DirectButton(parent=self, relief=None, image=adoptImageList, geom=pawLogoAdoptImageList, scale=(modelScale, modelScale, modelScale), text=TTLocalizer.PetshopAdoptAPet, text_scale=textScale, text_pos=(0, 12.5), text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, pressEffect=False, command=lambda : messenger.send(doneEvent, [2])) + self.returnPetButton = DirectButton(parent=self, relief=None, image=returnImageList, geom=pawLogoReturnImageList, image3_color=disabledImageColor, scale=(modelScale, modelScale, modelScale), text=TTLocalizer.PetshopReturnPet, text_scale=textScale, text_pos=(-0.6, 9.2), text0_fg=text2Color, text1_fg=text2Color, text2_fg=text0Color, text3_fg=text3Color, pressEffect=False, command=lambda : messenger.send(doneEvent, [3])) + if not base.localAvatar.hasPet(): + self.returnPetButton['state'] = DGG.DISABLED + model.removeNode() + return + + class AdoptPetDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PetshopGUI.AdoptPetDlg') + + def __init__(self, doneEvent, petSeed, petNameIndex): + zoneId = ZoneUtil.getCanonicalSafeZoneId(base.localAvatar.getZoneId()) + name, dna, traitSeed = PetUtil.getPetInfoFromSeed(petSeed, zoneId) + name = TTLocalizer.getPetName(petNameIndex) + cost = PetUtil.getPetCostFromSeed(petSeed, zoneId) + model = loader.loadModel('phase_4/models/gui/AdoptPet') + modelPos = (0, 0, -0.3) + modelScale = 0.055 + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=modelScale, frameSize=(-1, 1, -1, 1), pos=modelPos, text=TTLocalizer.PetshopAdoptConfirm % (name, cost), text_wordwrap=12, text_scale=0.05, text_pos=(0, 0.55), text_fg=text0Color) + self.initialiseoptions(PetshopGUI.AdoptPetDlg) + self.petView = self.attachNewNode('petView') + self.petView.setPos(-0.13, 0, 0.8) + self.petModel = Pet.Pet(forGui=1) + self.petModel.setDNA(dna) + self.petModel.fitAndCenterHead(0.395, forGui=1) + self.petModel.reparentTo(self.petView) + self.petModel.setH(130) + self.petModel.setScale(0.125) + self.petModel.enterNeutralHappy() + self.moneyDisplay = DirectLabel(parent=self, relief=None, text=str(base.localAvatar.getTotalMoney()), text_scale=0.075, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0.225, 0.33), text_font=ToontownGlobals.getSignFont()) + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.accept(localAvatar.uniqueName('bankMoneyChange'), self.__moneyChange) + okImageList = (model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckButtonRollover')) + cancelImageList = (model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelRollover')) + cancelIcon = model.find('**/CancelIcon') + checkIcon = model.find('**/CheckIcon') + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, geom=cancelIcon, scale=modelScale, text=('', TTLocalizer.PetshopGoBack), text_pos=(-5.8, 4.4), text_scale=0.7, pressEffect=False, command=lambda : messenger.send(doneEvent, [0])) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, geom=checkIcon, scale=modelScale, text=('', TTLocalizer.PetshopAdopt), text_pos=(5.8, 4.4), text_scale=0.7, pressEffect=False, command=lambda : messenger.send(doneEvent, [1])) + model.removeNode() + return + + def destroy(self): + self.ignore(localAvatar.uniqueName('moneyChange')) + self.ignore(localAvatar.uniqueName('bankMoneyChange')) + self.petModel.delete() + DirectFrame.destroy(self) + + def __moneyChange(self, money): + self.moneyDisplay['text'] = str(base.localAvatar.getTotalMoney()) + + class ReturnPetDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PetshopGUI.ReturnPetDlg') + + def __init__(self, doneEvent): + + def showDialog(avatar): + model = loader.loadModel('phase_4/models/gui/ReturnPet') + modelPos = (0, 0, -0.3) + modelScale = (0.055, 0.055, 0.055) + base.r = self + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_scale=modelScale, frameSize=(-1, 1, -1, 1), pos=modelPos, text=TTLocalizer.PetshopReturnConfirm % avatar.getName(), text_wordwrap=12, text_scale=TTLocalizer.PGUIreturnConfirm, text_pos=(0, 0.45), text_fg=text2Color) + self.initialiseoptions(PetshopGUI.ReturnPetDlg) + okImageList = (model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckRollover')) + cancelImageList = (model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelRollover')) + cancelIcon = model.find('**/CancelIcon') + checkIcon = model.find('**/CheckIcon') + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, geom=cancelIcon, scale=modelScale, text=('', TTLocalizer.PetshopGoBack), text_pos=(-5.8, 4.4), text_scale=0.7, pressEffect=False, command=lambda : messenger.send(doneEvent, [0])) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, geom=checkIcon, scale=modelScale, text=('', TTLocalizer.PetshopReturn), text_pos=(5.8, 4.4), text_scale=0.7, pressEffect=False, command=lambda : messenger.send(doneEvent, [1])) + self.petView = self.attachNewNode('petView') + self.petView.setPos(-0.15, 0, 0.8) + avatar.announceGenerate() + self.petModel = Pet.Pet(forGui=1) + self.petModel.setDNA(avatar.getDNA()) + self.petModel.fitAndCenterHead(0.395, forGui=1) + self.petModel.reparentTo(self.petView) + self.petModel.setH(130) + self.petModel.setScale(0.125) + self.petModel.enterNeutralSad() + model.removeNode() + self.initialized = True + return + + self.initialized = False + self.petPanel = PetDetail.PetDetail(base.localAvatar.getPetId(), showDialog) + + def destroy(self): + if self.initialized: + self.petPanel.avatar.disable() + self.petPanel.avatar.delete() + self.petPanel.avatar = None + self.PetPanel = None + self.petModel.delete() + DirectFrame.destroy(self) + return + + class ChoosePetDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PetshopGUI.ChoosePetDlg') + + def __init__(self, doneEvent, petSeeds): + model = loader.loadModel('phase_4/models/gui/PetShopInterface') + modelPos = (0, 0, -0.9) + modelScale = (0.185, 0.185, 0.185) + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_scale=modelScale, frameSize=(-1, 1, -1, 1), pos=modelPos, text=TTLocalizer.PetshopChooserTitle, text_wordwrap=26, text_scale=TTLocalizer.PGUIchooserTitle, text_fg=Vec4(0.36, 0.94, 0.93, 1), text_pos=(0, 1.58)) + self.initialiseoptions(PetshopGUI.ChoosePetDlg) + adoptImageList = (model.find('**/AdoptButtonUp'), + model.find('**/AdoptButtonDown'), + model.find('**/AdoptButtonRollover'), + model.find('**/AdoptButtonRollover')) + cancelImageList = (model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')) + cancelIcon = model.find('**/CancelIcon') + pawLImageList = (model.find('**/Paw1Up'), model.find('**/Paw1Down'), model.find('**/Paw1Rollover')) + pawLArrowImageList = model.find('**/Arrow1') + pawRImageList = (model.find('**/Paw2Up'), model.find('**/Paw2Down'), model.find('**/Paw2Rollover')) + pawRArrowImageList = model.find('**/Arrow2') + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, geom=cancelIcon, scale=modelScale, pressEffect=False, command=lambda : messenger.send(doneEvent, [-1])) + self.pawLButton = DirectButton(parent=self, relief=None, image=pawLImageList, geom=pawLArrowImageList, scale=modelScale, pressEffect=False, command=lambda : self.__handlePetChange(-1)) + self.pawRButton = DirectButton(parent=self, relief=None, image=pawRImageList, geom=pawRArrowImageList, scale=modelScale, pressEffect=False, command=lambda : self.__handlePetChange(1)) + self.okButton = DirectButton(parent=self, relief=None, image=adoptImageList, image3_color=disabledImageColor, scale=modelScale, text=TTLocalizer.PetshopAdopt, text_scale=TTLocalizer.PGUIokButton, text_pos=TTLocalizer.PGUIokButtonPos, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, pressEffect=False, command=lambda : messenger.send(doneEvent, [self.curPet])) + self.moneyDisplay = DirectLabel(parent=self, relief=None, text=str(base.localAvatar.getTotalMoney()), text_scale=0.1, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0.34, 0.12), text_font=ToontownGlobals.getSignFont()) + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.accept(localAvatar.uniqueName('bankMoneyChange'), self.__moneyChange) + self.petView = self.attachNewNode('petView') + self.petView.setPos(-0.05, 0, 1.15) + model.removeNode() + self.petSeeds = petSeeds + self.makePetList() + self.showPet() + return + + def makePetList(self): + self.numPets = len(self.petSeeds) + self.curPet = 0 + self.petDNA = [] + self.petName = [] + self.petDesc = [] + self.petCost = [] + for i in xrange(self.numPets): + random.seed(self.petSeeds[i]) + zoneId = ZoneUtil.getCanonicalSafeZoneId(base.localAvatar.getZoneId()) + name, dna, traitSeed = PetUtil.getPetInfoFromSeed(self.petSeeds[i], zoneId) + cost = PetUtil.getPetCostFromSeed(self.petSeeds[i], zoneId) + traits = PetTraits.PetTraits(traitSeed, zoneId) + traitList = traits.getExtremeTraitDescriptions() + numGenders = len(PetDNA.PetGenders) + gender = i % numGenders + PetDNA.setGender(dna, gender) + self.petDNA.append(dna) + self.petName.append(TTLocalizer.PetshopUnknownName) + descList = [] + descList.append(TTLocalizer.PetshopDescGender % PetDNA.getGenderString(gender=gender)) + if traitList: + descList.append(TTLocalizer.PetshopDescTrait % traitList[0]) + else: + descList.append(TTLocalizer.PetshopDescTrait % TTLocalizer.PetshopDescStandard) + traitList.extend(['', + '', + '', + '']) + for trait in traitList[1:4]: + descList.append('\t%s' % trait) + + descList.append(TTLocalizer.PetshopDescCost % cost) + self.petDesc.append('\n'.join(descList)) + self.petCost.append(cost) + + def destroy(self): + self.ignore(localAvatar.uniqueName('moneyChange')) + self.ignore(localAvatar.uniqueName('bankMoneyChange')) + self.petModel.delete() + DirectFrame.destroy(self) + + def __handlePetChange(self, nDir): + self.curPet = (self.curPet + nDir) % self.numPets + self.nameLabel.destroy() + self.petModel.delete() + self.descLabel.destroy() + self.showPet() + + def showPet(self): + self.nameLabel = DirectLabel(parent=self, pos=(0, 0, 1.35), relief=None, text=self.petName[self.curPet], text_fg=Vec4(0.45, 0, 0.61, 1), text_pos=(0, 0), text_scale=0.08, text_shadow=(1, 1, 1, 1)) + self.petModel = Pet.Pet(forGui=1) + self.petModel.setDNA(self.petDNA[self.curPet]) + self.petModel.fitAndCenterHead(0.57, forGui=1) + self.petModel.reparentTo(self.petView) + self.petModel.setH(130) + self.petModel.setScale(0.125) + self.petModel.enterNeutralHappy() + self.descLabel = DirectLabel(parent=self, pos=(-0.4, 0, 0.72), relief=None, scale=0.05, text=self.petDesc[self.curPet], text_align=TextNode.ALeft, text_wordwrap=TTLocalizer.PGUIwordwrap, text_scale=TTLocalizer.PGUIdescLabel) + if self.petCost[self.curPet] > base.localAvatar.getTotalMoney(): + self.okButton['state'] = DGG.DISABLED + else: + self.okButton['state'] = DGG.NORMAL + return + + def __moneyChange(self, money): + self.moneyDisplay['text'] = str(base.localAvatar.getTotalMoney()) + + def __init__(self, eventDict, petSeeds): + self.eventDict = eventDict + self.mainMenuDoneEvent = 'MainMenuGuiDone' + self.adoptPetDoneEvent = 'AdoptPetGuiDone' + self.returnPetDoneEvent = 'ReturnPetGuiDone' + self.petChooserDoneEvent = 'PetChooserGuiDone' + self.fishGuiDoneEvent = 'MyFishGuiDone' + self.namePickerDoneEvent = 'NamePickerGuiDone' + self.goHomeDlgDoneEvent = 'GoHomeDlgDone' + self.dialog = None + self.dialogStack = [] + self.petSeeds = petSeeds + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(aspect2d) + self.timer.posInTopRightCorner() + self.timer.countdown(PetConstants.PETCLERK_TIMER, self.__timerExpired) + self.doDialog(Dialog_MainMenu) + return + + def __timerExpired(self): + messenger.send(self.eventDict['guiDone'], [True]) + + def destroy(self): + self.destroyDialog() + self.timer.destroy() + del self.timer + self.ignore(self.mainMenuDoneEvent) + self.ignore(self.adoptPetDoneEvent) + self.ignore(self.returnPetDoneEvent) + self.ignore(self.petChooserDoneEvent) + self.ignore(self.fishGuiDoneEvent) + self.ignore(self.namePickerDoneEvent) + self.ignore(self.goHomeDlgDoneEvent) + + def destroyDialog(self): + if self.dialog != None: + self.dialog.destroy() + self.dialog = None + return + + def popDialog(self): + self.dialogStack.pop() + self.doDialog(self.dialogStack.pop()) + + def doDialog(self, nDialog): + self.destroyDialog() + self.dialogStack.append(nDialog) + if nDialog == Dialog_MainMenu: + self.acceptOnce(self.mainMenuDoneEvent, self.__handleMainMenuDlg) + self.dialog = self.MainMenuDlg(self.mainMenuDoneEvent) + elif nDialog == Dialog_AdoptPet: + self.acceptOnce(self.adoptPetDoneEvent, self.__handleAdoptPetDlg) + self.dialog = self.AdoptPetDlg(self.adoptPetDoneEvent, self.petSeeds[self.adoptPetNum], self.adoptPetNameIndex) + elif nDialog == Dialog_ChoosePet: + self.acceptOnce(self.petChooserDoneEvent, self.__handleChoosePetDlg) + self.dialog = self.ChoosePetDlg(self.petChooserDoneEvent, self.petSeeds) + elif nDialog == Dialog_ReturnPet: + self.acceptOnce(self.returnPetDoneEvent, self.__handleReturnPetDlg) + self.dialog = self.ReturnPetDlg(self.returnPetDoneEvent) + elif nDialog == Dialog_SellFish: + self.acceptOnce(self.fishGuiDoneEvent, self.__handleFishSellDlg) + self.dialog = FishSellGUI.FishSellGUI(self.fishGuiDoneEvent) + elif nDialog == Dialog_NamePicker: + self.acceptOnce(self.namePickerDoneEvent, self.__handleNamePickerDlg) + self.dialog = self.NamePicker(self.namePickerDoneEvent, self.petSeeds[self.adoptPetNum], gender=self.adoptPetNum % 2) + elif nDialog == Dialog_GoHome: + self.acceptOnce(self.goHomeDlgDoneEvent, self.__handleGoHomeDlg) + self.dialog = self.GoHomeDlg(self.goHomeDlgDoneEvent) + + def __handleMainMenuDlg(self, exitVal): + if exitVal == 0: + messenger.send(self.eventDict['guiDone']) + elif exitVal == 1: + self.doDialog(Dialog_SellFish) + elif exitVal == 2: + self.doDialog(Dialog_ChoosePet) + elif exitVal == 3: + self.doDialog(Dialog_ReturnPet) + + def __handleFishSellDlg(self, exitVal): + if exitVal == 0: + self.popDialog() + elif exitVal == 1: + self.destroyDialog() + messenger.send(self.eventDict['fishSold']) + + def __handleChoosePetDlg(self, exitVal): + if exitVal == -1: + self.popDialog() + else: + self.adoptPetNum = exitVal + self.doDialog(Dialog_NamePicker) + + def __handleNamePickerDlg(self, exitVal): + if exitVal == -1: + self.popDialog() + else: + self.adoptPetNameIndex = exitVal + if base.localAvatar.hasPet(): + self.doDialog(Dialog_ReturnPet) + else: + self.doDialog(Dialog_AdoptPet) + + def __handleAdoptPetDlg(self, exitVal): + if exitVal == 0: + self.popDialog() + elif exitVal == 1: + self.destroyDialog() + messenger.send(self.eventDict['petAdopted'], [self.adoptPetNum, self.adoptPetNameIndex]) + messenger.send(self.eventDict['guiDone']) + + def __handleGoHomeDlg(self, exitVal): + if exitVal == 0: + messenger.send(self.eventDict['guiDone']) + elif exitVal == 1: + messenger.send(self.eventDict['guiDone']) + place = base.cr.playGame.getPlace() + if place == None: + self.notify.warning('Tried to go home, but place is None.') + return + place.goHomeNow(base.localAvatar.lastHood) + return + + def __handleReturnPetDlg(self, exitVal): + if exitVal == 0: + self.popDialog() + elif exitVal == 1: + if self.dialogStack[len(self.dialogStack) - 2] == Dialog_NamePicker: + self.doDialog(Dialog_AdoptPet) + else: + self.destroyDialog() + messenger.send(self.eventDict['petReturned']) + messenger.send(self.eventDict['guiDone']) diff --git a/toontown/pets/__init__.py b/toontown/pets/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/quest/BlinkingArrows.py b/toontown/quest/BlinkingArrows.py new file mode 100755 index 00000000..3d6aa06b --- /dev/null +++ b/toontown/quest/BlinkingArrows.py @@ -0,0 +1,67 @@ +from direct.interval.IntervalGlobal import * +from panda3d.core import * + +class BlinkingArrows: + + def __init__(self, parent = aspect2d, otherNode = None): + self.arrow1 = loader.loadModel('phase_3/models/props/arrow') + self.arrow2 = loader.loadModel('phase_3/models/props/arrow') + self.arrowTrack = None + self.parent = parent + self.otherNode = otherNode + return + + def delete(self): + self.arrowsOff() + self.arrow1.removeNode() + self.arrow2.removeNode() + del self.arrow1 + del self.arrow2 + + def arrowsOn(self, x1, y1, h1, x2, y2, h2, onTime = 0.75, offTime = 0.75): + self.stopArrowsFlashing() + self.arrow1.setBin('gui-popup', 0) + self.arrow2.setBin('gui-popup', 0) + self.arrow1.reparentTo(self.parent) + self.arrow2.reparentTo(self.parent) + self.arrow1.setScale(0.2) + self.arrow2.setScale(0.2) + self.arrow1.setPos(x1, 0, y1) + self.arrow2.setPos(x2, 0, y2) + self.arrow1.setR(h1) + self.arrow2.setR(h2) + self.onTime = onTime + self.offTime = offTime + self.startArrowsFlashing() + + def arrowsOff(self): + self.stopArrowsFlashing() + self.arrow1.reparentTo(hidden) + self.arrow2.reparentTo(hidden) + + def reparentTo(self, parent): + self.parent = parent + self.arrow1.reparentTo(self.parent) + self.arrow2.reparentTo(self.parent) + + def startArrowsFlashing(self): + onColor = Vec4(1, 1, 1, 1) + offColor = Vec4(1, 1, 1, 0.25) + self.arrow1.show() + self.arrow2.show() + if self.otherNode: + self.otherNode.show() + self.arrowTrack = Sequence(Parallel(self.arrow1.colorScaleInterval(self.onTime, onColor, offColor), self.arrow2.colorScaleInterval(self.onTime, onColor, offColor), self.otherNode.colorScaleInterval(self.onTime, onColor, offColor)), Parallel(self.arrow1.colorScaleInterval(self.offTime, offColor, onColor), self.arrow2.colorScaleInterval(self.offTime, offColor, onColor), self.otherNode.colorScaleInterval(self.offTime, offColor, onColor))) + else: + self.arrowTrack = Sequence(Parallel(self.arrow1.colorScaleInterval(self.onTime, onColor, offColor), self.arrow2.colorScaleInterval(self.onTime, onColor, offColor)), Parallel(self.arrow1.colorScaleInterval(self.offTime, offColor, onColor), self.arrow2.colorScaleInterval(self.offTime, offColor, onColor))) + self.arrowTrack.loop() + + def stopArrowsFlashing(self): + if self.arrowTrack: + self.arrowTrack.finish() + self.arrowTrack = None + self.arrow1.hide() + self.arrow2.hide() + if self.otherNode: + self.otherNode.hide() + return diff --git a/toontown/quest/QuestBookPoster.py b/toontown/quest/QuestBookPoster.py new file mode 100755 index 00000000..ea9bea1e --- /dev/null +++ b/toontown/quest/QuestBookPoster.py @@ -0,0 +1,61 @@ +from QuestPoster import * +IMAGE_SCALE_LARGE = 0.15 +IMAGE_SCALE_SMALL = 0.1 +POSTER_WIDTH = 0.7 +TEXT_SCALE = TTLocalizer.QPtextScale * 0.7 +TEXT_WORDWRAP = TTLocalizer.QPtextWordwrap * 0.8 + +class QuestBookPoster(QuestPoster): + notify = DirectNotifyGlobal.directNotify.newCategory('QuestPoster') + colors = {'white': (1, 1, 1, 1), + 'blue': (0.45, 0.45, 0.8, 1), + 'lightBlue': (0.42, 0.671, 1.0, 1.0), + 'green': (0.45, 0.8, 0.45, 1), + 'lightGreen': (0.784, 1, 0.863, 1), + 'red': (0.8, 0.45, 0.45, 1), + 'rewardRed': (0.8, 0.3, 0.3, 1), + 'brightRed': (1.0, 0.16, 0.16, 1.0), + 'brown': (0.52, 0.42, 0.22, 1)} + normalTextColor = (0.3, 0.25, 0.2, 1) + confirmDeleteButtonEvent = 'confirmDeleteButtonEvent' + + def __init__(self, parent = aspect2d, **kw): + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + questCard = bookModel.find('**/questCard') + optiondefs = (('relief', None, None), + ('reverse', 0, None), + ('mapIndex', 0, None), + ('image', questCard, None), + ('image_scale', (0.8, 1.0, 0.58), None), + ('state', DGG.NORMAL, None)) + self.defineoptions(kw, optiondefs) + QuestPoster.__init__(self, relief=None) + self.initialiseoptions(QuestBookPoster) + self._deleteCallback = None + self.questFrame = DirectFrame(parent=self, relief=None) + gui = loader.loadModel('phase_4/models/parties/schtickerbookHostingGUI') + icon = gui.find('**/startPartyButton_inactive') + iconNP = aspect2d.attachNewNode('iconNP') + icon.reparentTo(iconNP) + icon.setX((-12.0792 + 0.2) / 30.48) + icon.setZ((-9.7404 + 1) / 30.48) + self.mapIndex = DirectLabel(parent=self.questFrame, relief=None, text='%s' % self['mapIndex'], text_fg=(1, 1, 1, 1), text_scale=0.035, text_align=TextNode.ACenter, image=iconNP, image_scale=0.3, image_color=(1, 0, 0, 1), pos=(-0.3, 0, 0.15)) + self.mapIndex.hide() + iconNP.removeNode() + gui.removeNode() + bookModel.removeNode() + self.reverseBG(self['reverse']) + self.laffMeter = None + return + + def reverseBG(self, reverse = 0): + try: + self.initImageScale + except AttributeError: + self.initImageScale = self['image_scale'] + if reverse: + self.initImageScale.setX(-abs(self.initImageScale[0])) + self.questFrame.setX(0.015) + else: + self.initImageScale.setX(abs(self.initImageScale[0])) + self['image_scale'] = self.initImageScale diff --git a/toontown/quest/QuestChoiceGui.py b/toontown/quest/QuestChoiceGui.py new file mode 100755 index 00000000..ba9a6a9a --- /dev/null +++ b/toontown/quest/QuestChoiceGui.py @@ -0,0 +1,65 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +import QuestPoster +from toontown.toonbase import ToontownTimer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal + +class QuestChoiceGui(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('QuestChoiceGui') + + def __init__(self): + DirectFrame.__init__(self, relief=None, parent=base.a2dLeftCenter, geom=DGG.getDefaultDialogGeom(), geom_color=Vec4(0.8, 0.6, 0.4, 1), geom_scale=(1.85, 1, 0.9), geom_hpr=(0, 0, -90), pos=(0.5, 0, 0)) + self.initialiseoptions(QuestChoiceGui) + self.questChoicePosters = [] + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.cancelButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.7, 1, 1), text=TTLocalizer.QuestChoiceGuiCancel, text_scale=0.06, text_pos=(0, -0.02), command=self.chooseQuest, extraArgs=[0]) + guiButton.removeNode() + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(self) + self.timer.setScale(0.3) + base.setCellsAvailable(base.leftCells, 0) + base.setCellsAvailable([base.bottomCells[0], base.bottomCells[1]], 0) + + def setQuests(self, quests, fromNpcId, timeout): + for i in xrange(0, len(quests), 3): + questId, rewardId, toNpcId = quests[i:i + 3] + qp = QuestPoster.QuestPoster() + qp.reparentTo(self) + qp.showChoicePoster(questId, fromNpcId, toNpcId, rewardId, self.chooseQuest) + qp.teleportButton.hide() + self.questChoicePosters.append(qp) + + if len(quests) == 1 * 3: + self['geom_scale'] = (1, 1, 0.9) + self.questChoicePosters[0].setPos(0, 0, 0.1) + self.cancelButton.setPos(0.15, 0, -0.375) + self.timer.setPos(-0.2, 0, -0.35) + elif len(quests) == 2 * 3: + self['geom_scale'] = (1.5, 1, 0.9) + self.questChoicePosters[0].setPos(0, 0, -0.2) + self.questChoicePosters[1].setPos(0, 0, 0.4) + self.cancelButton.setPos(0.15, 0, -0.625) + self.timer.setPos(-0.2, 0, -0.6) + elif len(quests) == 3 * 3: + self['geom_scale'] = (1.85, 1, 0.9) + map(lambda x: x.setScale(0.95), self.questChoicePosters) + self.questChoicePosters[0].setPos(0, 0, -0.4) + self.questChoicePosters[1].setPos(0, 0, 0.125) + self.questChoicePosters[2].setPos(0, 0, 0.65) + self.cancelButton.setPos(0.15, 0, -0.8) + self.timer.setPos(-0.2, 0, -0.775) + self.timer.countdown(timeout, self.timeout) + + def chooseQuest(self, questId): + if questId != 0: + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: CREATEATASK: Create A Task.') + base.setCellsAvailable(base.leftCells, 1) + base.setCellsAvailable([base.bottomCells[0], base.bottomCells[1]], 1) + self.timer.stop() + messenger.send('chooseQuest', [questId]) + + def timeout(self): + messenger.send('chooseQuest', [0]) diff --git a/toontown/quest/QuestMap.py b/toontown/quest/QuestMap.py new file mode 100755 index 00000000..1cb2dbab --- /dev/null +++ b/toontown/quest/QuestMap.py @@ -0,0 +1,290 @@ +import math +from pandac.PandaModules import CardMaker, TextNode +from direct.gui.DirectGui import DirectFrame, DirectLabel, DirectButton +from direct.task import Task +from toontown.toon import NPCToons +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.quest import Quests +from toontown.suit import SuitPlannerBase +import QuestMapGlobals + +class QuestMap(DirectFrame): + + def __init__(self, av, **kw): + DirectFrame.__init__(self, relief=None, sortOrder=50) + self.initialiseoptions(QuestMap) + self.container = DirectFrame(parent=self, relief=None) + self.marker = DirectFrame(parent=self.container, relief=None) + self.cogInfoFrame = DirectFrame(parent=self.container, relief=None) + cm = CardMaker('bg') + cm.setFrame(-0.5, 0.5, -0.5, 0.5) + bg = self.cogInfoFrame.attachNewNode(cm.generate()) + bg.setTransparency(1) + bg.setColor(0.5, 0.5, 0.5, 0.5) + bg.setBin('fixed', 0) + self.cogInfoFrame['geom'] = bg + self.cogInfoFrame['geom_pos'] = (0, 0, 0) + self.cogInfoFrame['geom_scale'] = (6, 1, 2) + self.cogInfoFrame.setScale(0.05) + self.cogInfoFrame.setPos(0, 0, 0.6) + self.buildingMarkers = [] + self.av = av + self.updateMarker = True + self.cornerPosInfo = None + self.hqPosInfo = None + self.fishingSpotInfo = None + self.load() + self.setScale(1.5) + bg.removeNode() + self.hoodId = None + self.zoneId = None + self.suitPercentage = {} + for currHoodInfo in SuitPlannerBase.SuitPlannerBase.SuitHoodInfo: + tracks = currHoodInfo[SuitPlannerBase.SuitPlannerBase.SUIT_HOOD_INFO_TRACK] + self.suitPercentage[currHoodInfo[SuitPlannerBase.SuitPlannerBase.SUIT_HOOD_INFO_ZONE]] = tracks + + def load(self): + gui = loader.loadModel('phase_4/models/questmap/questmap_gui') + icon = gui.find('**/tt_t_gui_qst_arrow') + iconNP = aspect2d.attachNewNode('iconNP') + icon.reparentTo(iconNP) + icon.setR(90) + self.marker['geom'] = iconNP + self.marker['image'] = iconNP + self.marker.setScale(0.05) + iconNP.removeNode() + self.mapOpenButton = DirectButton(image=(gui.find('**/tt_t_gui_qst_mapClose'), gui.find('**/tt_t_gui_qst_mapClose'), gui.find('**/tt_t_gui_qst_mapTryToOpen')), relief=None, pos=(-0.08, 0, 0.37), parent=base.a2dBottomRight, scale=0.205, command=self.show) + self.mapCloseButton = DirectButton(image=(gui.find('**/tt_t_gui_qst_mapOpen'), gui.find('**/tt_t_gui_qst_mapOpen'), gui.find('**/tt_t_gui_qst_mapTryToClose')), relief=None, pos=(-0.08, 0, 0.37), parent=base.a2dBottomRight, scale=0.205, command=self.hide) + self.mapOpenButton.hide() + self.mapCloseButton.hide() + gui.removeNode() + icons = loader.loadModel('phase_3/models/gui/cog_icons') + cIcon = icons.find('**/CorpIcon') + lIcon = icons.find('**/LegalIcon') + mIcon = icons.find('**/MoneyIcon') + sIcon = icons.find('**/SalesIcon') + cogInfoTextColor = (0.2, 0.2, 0.2, 1) + textPos = (1.2, -0.2) + textScale = 0.8 + self.cInfo = DirectLabel(parent=self.cogInfoFrame, text='', text_fg=cogInfoTextColor, text_pos=textPos, text_scale=textScale, geom=cIcon, geom_pos=(-0.2, 0, 0), geom_scale=0.8, relief=None) + self.cInfo.setPos(-2.2, 0, 0.5) + self.lInfo = DirectLabel(parent=self.cogInfoFrame, text_fg=cogInfoTextColor, text='', text_pos=textPos, text_scale=textScale, geom=lIcon, geom_pos=(-0.2, 0, 0), geom_scale=0.8, relief=None) + self.lInfo.setPos(-2.2, 0, -0.5) + self.mInfo = DirectLabel(parent=self.cogInfoFrame, text_fg=cogInfoTextColor, text='', text_pos=textPos, text_scale=textScale, geom=mIcon, geom_pos=(-0.2, 0, 0), geom_scale=0.8, relief=None) + self.mInfo.setPos(0.8, 0, 0.5) + self.sInfo = DirectLabel(parent=self.cogInfoFrame, text_fg=cogInfoTextColor, text='', text_pos=textPos, text_scale=textScale, geom=sIcon, geom_pos=(-0.2, 0, 0), geom_scale=0.8, relief=None) + self.sInfo.setPos(0.8, 0, -0.5) + icons.removeNode() + return + + def updateCogInfo(self): + currPercentage = self.suitPercentage.get(self.zoneId) + if currPercentage is None: + return + self.cInfo['text'] = '%s%%' % currPercentage[0] + self.lInfo['text'] = '%s%%' % currPercentage[1] + self.mInfo['text'] = '%s%%' % currPercentage[2] + self.sInfo['text'] = '%s%%' % currPercentage[3] + return + + def destroy(self): + self.ignore('questPageUpdated') + self.mapOpenButton.destroy() + self.mapCloseButton.destroy() + del self.mapOpenButton + del self.mapCloseButton + DirectFrame.destroy(self) + + def putBuildingMarker(self, pos, hpr = (0, 0, 0), mapIndex = None, zoneId=None): + marker = DirectLabel(parent=self.container, text='', text_pos=(-0.05, -0.15), text_fg=(1, 1, 1, 1), relief=None) + gui = loader.loadModel('phase_4/models/parties/schtickerbookHostingGUI') + icon = gui.find('**/startPartyButton_inactive') + iconNP = aspect2d.attachNewNode('iconNP') + icon.reparentTo(iconNP) + icon.setX(-12.0792 / 30.48) + icon.setZ(-9.7404 / 30.48) + marker['text'] = '%s' % mapIndex + marker['text_scale'] = 0.7 + marker['image'] = iconNP + marker['image_color'] = (1, 0, 0, 1) + marker['image_scale'] = 6 + marker.setScale(0.05) + relX, relY = self.transformAvPos(pos) + marker.setPos(relX, 0, relY) + self.buildingMarkers.append(marker) + iconNP.removeNode() + gui.removeNode() + + if zoneId: + base.cr.buildingQueryMgr.d_isSuit(zoneId, lambda isSuit: self.updateMarkerColor(marker, isSuit)) + + def updateMarkerColor(self, marker, isSuit): + if isSuit: + marker['image_color'] = (0.4, 0.4, 0.4, 1.0) + + def updateQuestInfo(self): + for marker in self.buildingMarkers: + marker.destroy() + + self.buildingMarkers = [] + + for (i, questDesc) in enumerate(self.av.quests): + mapIndex = i + 1 + quest = Quests.getQuest(questDesc[0]) + toNpcId = questDesc[2] + + completed = quest.getCompletionStatus(self.av, questDesc) == Quests.COMPLETE + if not completed: + if quest.getType() == Quests.RecoverItemQuest: + if quest.getHolder() == Quests.AnyFish: + self.putBuildingMarker(self.fishingSpotInfo, mapIndex=mapIndex) + continue + elif quest.getType() not in ( + Quests.DeliverGagQuest, Quests.DeliverItemQuest, + Quests.VisitQuest, Quests.TrackChoiceQuest): + continue + + if toNpcId == Quests.ToonHQ: + self.putBuildingMarker(self.hqPosInfo, mapIndex=mapIndex) + continue + + npcZoneId = NPCToons.getNPCZone(toNpcId) + hoodId = ZoneUtil.getCanonicalHoodId(npcZoneId) + branchId = ZoneUtil.getCanonicalBranchZone(npcZoneId) + + if (self.hoodId != hoodId) or (self.zoneId != branchId): + continue + + for blockIndex in xrange(base.cr.playGame.dnaStore.getNumBlockNumbers()): + blockNumber = base.cr.playGame.dnaStore.getBlockNumberAt(blockIndex) + zoneId = base.cr.playGame.dnaStore.getZoneFromBlockNumber(blockNumber) + interiorZoneId = (zoneId - (zoneId%100)) + 500 + blockNumber + if npcZoneId == interiorZoneId: + self.putBuildingMarker( + base.cr.playGame.dnaStore.getDoorPosHprFromBlockNumber(blockNumber).getPos(render), + base.cr.playGame.dnaStore.getDoorPosHprFromBlockNumber(blockNumber).getHpr(render), + mapIndex=mapIndex, zoneId=zoneId) + + def transformAvPos(self, pos): + if self.cornerPosInfo is None: + return (0, 0) + topRight = self.cornerPosInfo[0] + bottomLeft = self.cornerPosInfo[1] + relativeX = (pos.getX() - bottomLeft.getX()) / (topRight.getX() - bottomLeft.getX()) - 0.5 + relativeY = (pos.getY() - bottomLeft.getY()) / (topRight.getY() - bottomLeft.getY()) - 0.5 + return (relativeX, relativeY) + + def update(self, task): + if self.av: + if self.updateMarker: + relX, relY = self.transformAvPos(self.av.getPos()) + self.marker.setPos(relX, 0, relY) + self.marker.setHpr(0, 0, -180 - self.av.getH()) + i = 0 + for buildingMarker in self.buildingMarkers: + buildingMarker.setScale((math.sin(task.time * 16.0 + i * math.pi / 3.0) + 1) * 0.005 + 0.04) + i = i + 1 + + return Task.cont + + def updateMap(self): + if self.av: + hoodId = ZoneUtil.getCanonicalHoodId(self.av.getLocation()[1]) + zoneId = ZoneUtil.getCanonicalBranchZone(self.av.getLocation()[1]) + try: + mapsGeom = loader.loadModel('phase_4/models/questmap/%s_maps' % ToontownGlobals.dnaMap[hoodId]) + except: + self.stop() + return + mapImage = mapsGeom.find('**/%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId)) + if not mapImage.isEmpty(): + self.container['image'] = mapImage + self.resetFrameSize() + self.cornerPosInfo = QuestMapGlobals.CornerPosTable.get('%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId)) + self.hqPosInfo = QuestMapGlobals.HQPosTable.get('%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId)) + self.fishingSpotInfo = QuestMapGlobals.FishingSpotPosTable.get('%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId)) + self.cogInfoPos = QuestMapGlobals.CogInfoPosTable.get('%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId)) + self.cogInfoFrame.setPos(self.cogInfoPos) + self.hide() + self.hoodId = hoodId + self.zoneId = zoneId + self.updateQuestInfo() + self.updateCogInfo() + taskMgr.add(self.update, 'questMapUpdate') + else: + self.stop() + mapsGeom.removeNode() + + def start(self): + self.container.show() + self.accept('questPageUpdated', self.updateMap) + self.handleMarker() + self.updateMap() + + def initMarker(self, task): + if self.av: + if not hasattr(base.cr.playGame.getPlace(), 'isInterior') or not base.cr.playGame.getPlace().isInterior: + relX, relY = self.transformAvPos(self.av.getPos()) + self.marker.setPos(relX, 0, relY) + self.marker.setHpr(0, 0, -180 - self.av.getH()) + self.marker['geom_scale'] = 1.4 * task.time % 0.5 * 10 + 1 + self.marker['geom_color'] = (1, + 1, + 1, + 0.8 - 1.4 * task.time % 0.5 * 2 / 0.8 + 0.2) + if task.time < 1: + return Task.cont + else: + self.marker['geom_color'] = (1, 1, 1, 0) + return Task.done + + def show(self): + taskMgr.add(self.initMarker, 'questMapInit') + DirectFrame.show(self) + self.mapOpenButton.hide() + if self.container['image']: + self.mapCloseButton.show() + + def hide(self): + taskMgr.remove('questMapInit') + DirectFrame.hide(self) + if self.container['image']: + self.mapOpenButton.show() + self.mapCloseButton.hide() + + def toggle(self): + if self.isHidden(): + self.show() + else: + self.hide() + + def obscureButton(self): + self.mapOpenButton.hide() + self.mapCloseButton.hide() + + def stop(self): + self.container['image'] = None + for marker in self.buildingMarkers: + marker.destroy() + + self.buildingMarkers = [] + self.container.hide() + self.hide() + self.obscureButton() + self.ignore('questPageUpdated') + taskMgr.remove('questMapUpdate') + + def handleMarker(self): + if hasattr(base.cr.playGame.getPlace(), 'isInterior') and base.cr.playGame.getPlace().isInterior: + self.updateMarker = False + else: + self.updateMarker = True + + def acceptOnscreenHooks(self): + self.accept(ToontownGlobals.MapHotkey, self.toggle) + self.updateMap() + + def ignoreOnscreenHooks(self): + self.ignore(ToontownGlobals.MapHotkey) + self.obscureButton() diff --git a/toontown/quest/QuestMapGlobals.py b/toontown/quest/QuestMapGlobals.py new file mode 100755 index 00000000..f1f042dd --- /dev/null +++ b/toontown/quest/QuestMapGlobals.py @@ -0,0 +1,70 @@ +from pandac.PandaModules import Point3 +CogInfoPosTable = {'toontown_central_2100_english': Point3(-0.3, 0, -0.2), + 'toontown_central_2200_english': Point3(0.2, 0, -0.2), + 'toontown_central_2300_english': Point3(0.35, 0, -0.2), + 'minnies_melody_land_4100_english': Point3(0.45, 0, -0.2), + 'minnies_melody_land_4200_english': Point3(0.3, 0, -0.3), + 'minnies_melody_land_4300_english': Point3(0.4, 0, -0.2), + 'donalds_dock_1100_english': Point3(0.35, 0, -0.2), + 'donalds_dock_1200_english': Point3(0.3, 0, -0.35), + 'donalds_dock_1300_english': Point3(0.3, 0, -0.25), + 'daisys_garden_5100_english': Point3(0.3, 0, -0.2), + 'daisys_garden_5200_english': Point3(0.3, 0, -0.2), + 'daisys_garden_5300_english': Point3(-0.3, 0, -0.3), + 'the_burrrgh_3100_english': Point3(0.37, 0, -0.2), + 'the_burrrgh_3200_english': Point3(0.35, 0, -0.35), + 'the_burrrgh_3300_english': Point3(0.3, 0, -0.2), + 'donalds_dreamland_9100_english': Point3(0.2, 0, -0.2), + 'donalds_dreamland_9200_english': Point3(-0.25, 0, -0.2)} +HQPosTable = {'toontown_central_2100_english': Point3(-55, -350, 0), + 'toontown_central_2200_english': Point3(-385, 70, 0), + 'toontown_central_2300_english': Point3(490, 140, 0), + 'minnies_melody_land_4100_english': Point3(-330, -40, 0), + 'minnies_melody_land_4200_english': Point3(0, 555, 0), + 'minnies_melody_land_4300_english': Point3(326, 172, 5), + 'donalds_dock_1100_english': Point3(365, 100, 0), + 'donalds_dock_1200_english': Point3(-25, -410, 0), + 'donalds_dock_1300_english': Point3(210, -200, 0), + 'daisys_garden_5100_english': Point3(357.935, 28.45, 0), + 'daisys_garden_5200_english': Point3(345, 150, 0), + 'daisys_garden_5300_english': Point3(-138.597, 77.1236, -0.5), + 'the_burrrgh_3100_english': Point3(129.737, 51.9612, 0), + 'the_burrrgh_3200_english': Point3(230, 200, 0), + 'the_burrrgh_3300_english': Point3(268.052, 228.052, 0), + 'donalds_dreamland_9100_english': Point3(-250, -160, 0), + 'donalds_dreamland_9200_english': Point3(322.91, -67.1147, 0)} +FishingSpotPosTable = {'toontown_central_2100_english': Point3(4.45088, -658.174, 0.431656), + 'toontown_central_2200_english': Point3(-223.506, 161.474, 0.477789), + 'toontown_central_2300_english': Point3(512.4, -69.9997, 0.485466), + 'minnies_melody_land_4100_english': Point3(-570.662, -101.811, 1.9065), + 'minnies_melody_land_4200_english': Point3(-205.722, 238.179, 1.92272), + 'minnies_melody_land_4300_english': Point3(705.052, -26.4757, 1.93412), + 'donalds_dock_1100_english': Point3(368.148, -339.916, 0.419537), + 'donalds_dock_1200_english': Point3(-394.913, -243.085, 0.462966), + 'donalds_dock_1300_english': Point3(346.219, 88.2058, 0.643978), + 'daisys_garden_5100_english': Point3(162.961, 57.4756, 0.360527), + 'daisys_garden_5200_english': Point3(162.961, 113.59, 0.404519), + 'daisys_garden_5300_english': Point3(120.848, -55.6079, 0.388995), + 'the_burrrgh_3100_english': Point3(455.445, 15.6579, 0.376913), + 'the_burrrgh_3200_english': Point3(340.009, 462.856, 0.415845), + 'the_burrrgh_3300_english': Point3(54.17, 75.405, 0.141113), + 'donalds_dreamland_9100_english': Point3(106.796, -197.427, 0.354809), + 'donalds_dreamland_9200_english': Point3(239.646, -330.58, 0.347881)} +CornerPosTable = {'daisys_garden_5200_english': [Point3(781.219727, 476.963623, 0), Point3(-123.380096, -427.636261, 0)], + 'minnies_melody_land_4100_english': [Point3(-98.880981, 349.36908, 0), Point3(-658.680908, -210.430862, 0)], + 'donalds_dock_1300_english': [Point3(713.403076, 361.576324, 0), Point3(-83.596741, -435.423615, 0)], + 'toontown_central_sz_english': [Point3(225.733368, 209.740448, 0), Point3(-207.066559, -223.059525, 0)], + 'donalds_dreamland_9200_english': [Point3(420.817871, 204.719116, 0), Point3(-220.182007, -436.280853, 0)], + 'the_burrrgh_3100_english': [Point3(535.648132, 267.004578, 0), Point3(59.248276, -209.395325, 0)], + 'the_burrrgh_3300_english': [Point3(461.503418, 467.348511, 0), Point3(-27.896454, -22.051422, 0)], + 'toontown_central_2200_english': [Point3(1.816254, 335.417358, 0), Point3(-641.983643, -308.382629, 0)], + 'minnies_melody_land_4200_english': [Point3(283.292847, 658.077393, 0), Point3(-289.50708, 85.277466, 0)], + 'donalds_dock_1200_english': [Point3(34.281967, -47.717041, 0), Point3(-464.517944, -546.516968, 0)], + 'donalds_dock_1100_english': [Point3(596.439575, 146.409653, 0), Point3(28.039612, -421.990356, 0)], + 'minnies_melody_land_4300_english': [Point3(801.294067, 430.992218, 0), Point3(-5.505768, -375.807678, 0)], + 'daisys_garden_5300_english': [Point3(203.715881, 429.168274, 0), Point3(-390.284027, -164.83165, 0)], + 'donalds_dreamland_9100_english': [Point3(279.354584, 111.658554, 0), Point3(-423.445221, -591.141357, 0)], + 'daisys_garden_5100_english': [Point3(760.089722, 526.236206, 0), Point3(-139.510132, -373.3638, 0)], + 'toontown_central_2300_english': [Point3(870.224243, 536.165771, 0), Point3(-89.175751, -423.234344, 0)], + 'toontown_central_2100_english': [Point3(161.735336, -59.985107, 0), Point3(-512.664612, -734.385193, 0)], + 'the_burrrgh_3200_english': [Point3(429.647949, 561.224304, 0), Point3(-31.951935, 99.624283, 0)]} diff --git a/toontown/quest/QuestParser.py b/toontown/quest/QuestParser.py new file mode 100755 index 00000000..fbe13bc4 --- /dev/null +++ b/toontown/quest/QuestParser.py @@ -0,0 +1,906 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.showbase import DirectObject +from otp.speedchat import SpeedChatGlobals +from otp.nametag.NametagConstants import * +from toontown.suit import Suit, SuitDNA +from toontown.toon import ToonHeadFrame +from toontown.toonbase import TTLocalizer, ToontownBattleGlobals +from toontown.quest import QuestScripts +import copy, re, tokenize, BlinkingArrows, StringIO + +notify = DirectNotifyGlobal.directNotify.newCategory('QuestParser') +lineDict = {} +globalVarDict = {} +curId = None +FLOAT = re.compile(r'[+-]?\d+[.]\d*([e][+-]\d+)?') + +def init(): + globalVarDict.update({'render': render, + 'camera': camera, + 'hidden': hidden, + 'aspect2d': aspect2d, + 'topLeft': base.a2dTopLeft, + 'bottomLeft': base.a2dBottomLeft, + 'bottomRight': base.a2dBottomRight, + 'localToon': base.localAvatar, + 'laffMeter': base.localAvatar.laffMeter, + 'inventory': base.localAvatar.inventory, + 'bFriendsList': base.localAvatar.bFriendsList, + 'book': base.localAvatar.book, + 'bookPrevArrow': base.localAvatar.book.prevArrow, + 'bookNextArrow': base.localAvatar.book.nextArrow, + 'bookOpenButton': base.localAvatar.book.bookOpenButton, + 'bookCloseButton': base.localAvatar.book.bookCloseButton, + 'chatNormalButton': base.localAvatar.chatMgr.normalButton, + 'chatScButton': base.localAvatar.chatMgr.scButton, + 'arrows': BlinkingArrows.BlinkingArrows()}) + +def clear(): + globalVarDict.clear() + +def readFile(): + global curId + script = StringIO.StringIO(QuestScripts.script) + + def readLine(): + return script.readline().replace('\r', '') + + gen = tokenize.generate_tokens(readLine) + line = getLineOfTokens(gen) + + while line is not None: + if line == []: + line = getLineOfTokens(gen) + continue + if line[0] == 'ID': + parseId(line) + elif curId is None: + notify.error('A script must begin with an ID') + else: + lineDict[curId].append(line) + line = getLineOfTokens(gen) + + script.close() + +def getLineOfTokens(gen): + tokens = [] + nextNeg = 0 + try: + token = gen.next() + except StopIteration: + return None + if token[0] == tokenize.ENDMARKER: + return None + while token[0] != tokenize.NEWLINE and token[0] != tokenize.NL: + if token[0] == tokenize.COMMENT: + pass + elif token[0] == tokenize.OP and token[1] == '-': + nextNeg = 1 + elif token[0] == tokenize.NUMBER: + if re.match(FLOAT, token[1]): + number = float(token[1]) + else: + number = int(token[1]) + if nextNeg: + tokens.append(-number) + nextNeg = 0 + else: + tokens.append(number) + elif token[0] == tokenize.STRING: + tokens.append(eval(token[1])) + elif token[0] == tokenize.NAME: + tokens.append(token[1]) + else: + notify.warning('Ignored token type: %s on line: %s' % (tokenize.tok_name[token[0]], token[2][0])) + + try: + token = gen.next() + except StopIteration: + break + + return tokens + +def parseId(line): + global curId + curId = line[1] + notify.debug('Setting current scriptId to: %s' % curId) + if questDefined(curId): + notify.error('Already defined scriptId: %s' % curId) + else: + lineDict[curId] = [] + + +def questDefined(scriptId): + return scriptId in lineDict + + +class NPCMoviePlayer(DirectObject.DirectObject): + + def __init__(self, scriptId, toon, npc): + DirectObject.DirectObject.__init__(self) + self.scriptId = scriptId + self.toon = toon + self.isLocalToon = self.toon == base.localAvatar + self.npc = npc + self.privateVarDict = {} + self.toonHeads = {} + self.uniqueId = 'scriptMovie_' + str(self.scriptId) + '_' + str(toon.getDoId()) + '_' + str(npc.getDoId()) + self.setVar('toon', self.toon) + self.setVar('npc', self.npc) + self.chapterDict = {} + self.timeoutTrack = None + self.currentTrack = None + return + + def getVar(self, varName): + if varName in self.privateVarDict: + return self.privateVarDict[varName] + elif varName in globalVarDict: + return globalVarDict[varName] + elif varName.find('tomDialogue') > -1 or varName.find('harryDialogue') > -1: + notify.warning('%s getting referenced. Tutorial Ack: %d Place: %s' % (varName, base.localAvatar.tutorialAck, base.cr.playGame.hood)) + return None + else: + notify.error('Variable not defined: %s' % varName) + return None + + def delVar(self, varName): + if varName in self.privateVarDict: + del self.privateVarDict[varName] + elif varName in globalVarDict: + del globalVarDict[varName] + else: + notify.warning('Variable not defined: %s' % varName) + + def setVar(self, varName, var): + self.privateVarDict[varName] = var + + def cleanup(self): + if self.currentTrack: + self.currentTrack.pause() + self.currentTrack = None + self.ignoreAll() + taskMgr.remove(self.uniqueId) + for toonHeadFrame in self.toonHeads.values(): + toonHeadFrame.destroy() + + del self.toonHeads + del self.privateVarDict + del self.chapterDict + del self.toon + del self.npc + del self.timeoutTrack + return + + def timeout(self, fFinish = 0): + if self.timeoutTrack: + if fFinish: + self.timeoutTrack.finish() + else: + self.timeoutTrack.start() + + def finishMovie(self): + self.npc.finishMovie(self.toon, self.isLocalToon, 0.0) + + def playNextChapter(self, eventName, timeStamp = 0.0): + trackList = self.chapterDict[eventName] + if trackList: + self.currentTrack = trackList.pop(0) + self.currentTrack.start() + else: + notify.debug('Movie ended waiting for an event (%s)' % eventName) + + def play(self): + lineNum = 0 + self.currentEvent = 'start' + lines = lineDict.get(self.scriptId) + if lines is None: + notify.error('No movie defined for scriptId: %s' % self.scriptId) + chapterList = [] + timeoutList = [] + for line in lines: + lineNum += 1 + command = line[0] + if command == 'UPON_TIMEOUT': + uponTimeout = 1 + iList = timeoutList + line = line[1:] + command = line[0] + else: + uponTimeout = 0 + iList = chapterList + if command == 'CALL': + if uponTimeout: + self.notify.error('CALL not allowed in an UPON_TIMEOUT') + iList.append(self.parseCall(line)) + continue + elif command == 'DEBUG': + iList.append(self.parseDebug(line)) + continue + elif command == 'WAIT': + if uponTimeout: + self.notify.error('WAIT not allowed in an UPON_TIMEOUT') + iList.append(self.parseWait(line)) + continue + elif command == 'CHAT': + iList.append(self.parseChat(line)) + continue + elif command == 'CLEAR_CHAT': + iList.append(self.parseClearChat(line)) + continue + elif command == 'FINISH_QUEST_MOVIE': + chapterList.append(Func(self.finishMovie)) + continue + elif command == 'CHAT_CONFIRM': + if uponTimeout: + self.notify.error('CHAT_CONFIRM not allowed in an UPON_TIMEOUT') + avatarName = line[1] + avatar = self.getVar(avatarName) + nextEvent = avatar.uniqueName('doneChatPage') + iList.append(Func(self.acceptOnce, nextEvent, self.playNextChapter, [nextEvent])) + iList.append(self.parseChatConfirm(line)) + self.closePreviousChapter(iList) + chapterList = [] + self.currentEvent = nextEvent + continue + elif command == 'LOCAL_CHAT_CONFIRM': + if uponTimeout: + self.notify.error('LOCAL_CHAT_CONFIRM not allowed in an UPON_TIMEOUT') + avatarName = line[1] + avatar = self.getVar(avatarName) + nextEvent = avatar.uniqueName('doneChatPage') + iList.append(Func(self.acceptOnce, nextEvent, self.playNextChapter, [nextEvent])) + iList.append(self.parseLocalChatConfirm(line)) + self.closePreviousChapter(iList) + chapterList = [] + self.currentEvent = nextEvent + continue + elif command == 'LOCAL_CHAT_PERSIST': + iList.append(self.parseLocalChatPersist(line)) + continue + elif command == 'LOCAL_CHAT_TO_CONFIRM': + if uponTimeout: + self.notify.error('LOCAL_CHAT_TO_CONFIRM not allowed in an UPON_TIMEOUT') + avatarName = line[1] + avatar = self.getVar(avatarName) + nextEvent = avatar.uniqueName('doneChatPage') + iList.append(Func(self.acceptOnce, nextEvent, self.playNextChapter, [nextEvent])) + iList.append(self.parseLocalChatToConfirm(line)) + self.closePreviousChapter(iList) + chapterList = [] + self.currentEvent = nextEvent + continue + if self.isLocalToon: + if command == 'LOAD': + self.parseLoad(line) + elif command == 'LOAD_SFX': + self.parseLoadSfx(line) + elif command == 'LOAD_SUIT': + self.parseLoadSuit(line) + elif command == 'SET': + self.parseSet(line) + elif command == 'LOCK_LOCALTOON': + iList.append(self.parseLockLocalToon(line)) + elif command == 'FREE_LOCALTOON': + iList.append(self.parseFreeLocalToon(line)) + elif command == 'REPARENTTO': + iList.append(self.parseReparent(line)) + elif command == 'WRTREPARENTTO': + iList.append(self.parseWrtReparent(line)) + elif command == 'SHOW': + iList.append(self.parseShow(line)) + elif command == 'HIDE': + iList.append(self.parseHide(line)) + elif command == 'POS': + iList.append(self.parsePos(line)) + elif command == 'HPR': + iList.append(self.parseHpr(line)) + elif command == 'SCALE': + iList.append(self.parseScale(line)) + elif command == 'POSHPRSCALE': + iList.append(self.parsePosHprScale(line)) + elif command == 'COLOR': + iList.append(self.parseColor(line)) + elif command == 'COLOR_SCALE': + iList.append(self.parseColorScale(line)) + elif command == 'ADD_LAFFMETER': + iList.append(self.parseAddLaffMeter(line)) + elif command == 'LAFFMETER': + iList.append(self.parseLaffMeter(line)) + elif command == 'OBSCURE_LAFFMETER': + iList.append(self.parseObscureLaffMeter(line)) + elif command == 'ARROWS_ON': + iList.append(self.parseArrowsOn(line)) + elif command == 'ARROWS_OFF': + iList.append(self.parseArrowsOff(line)) + elif command == 'START_THROB': + iList.append(self.parseStartThrob(line)) + elif command == 'STOP_THROB': + iList.append(self.parseStopThrob(line)) + elif command == 'SHOW_FRIENDS_LIST': + iList.append(self.parseShowFriendsList(line)) + elif command == 'HIDE_FRIENDS_LIST': + iList.append(self.parseHideFriendsList(line)) + elif command == 'SHOW_BOOK': + iList.append(self.parseShowBook(line)) + elif command == 'HIDE_BOOK': + iList.append(self.parseHideBook(line)) + elif command == 'ENABLE_CLOSE_BOOK': + iList.append(self.parseEnableCloseBook(line)) + elif command == 'OBSCURE_BOOK': + iList.append(self.parseObscureBook(line)) + elif command == 'OBSCURE_CHAT': + iList.append(self.parseObscureChat(line)) + elif command == 'ADD_INVENTORY': + iList.append(self.parseAddInventory(line)) + elif command == 'SET_INVENTORY': + iList.append(self.parseSetInventory(line)) + elif command == 'SET_INVENTORY_YPOS': + iList.append(self.parseSetInventoryYPos(line)) + elif command == 'SET_INVENTORY_DETAIL': + iList.append(self.parseSetInventoryDetail(line)) + elif command == 'PLAY_SFX': + iList.append(self.parsePlaySfx(line)) + elif command == 'STOP_SFX': + iList.append(self.parseStopSfx(line)) + elif command == 'PLAY_ANIM': + iList.append(self.parsePlayAnim(line)) + elif command == 'LOOP_ANIM': + iList.append(self.parseLoopAnim(line)) + elif command == 'LERP_POS': + iList.append(self.parseLerpPos(line)) + elif command == 'LERP_HPR': + iList.append(self.parseLerpHpr(line)) + elif command == 'LERP_SCALE': + iList.append(self.parseLerpScale(line)) + elif command == 'LERP_POSHPRSCALE': + iList.append(self.parseLerpPosHprScale(line)) + elif command == 'LERP_COLOR': + iList.append(self.parseLerpColor(line)) + elif command == 'LERP_COLOR_SCALE': + iList.append(self.parseLerpColorScale(line)) + elif command == 'DEPTH_WRITE_ON': + iList.append(self.parseDepthWriteOn(line)) + elif command == 'DEPTH_WRITE_OFF': + iList.append(self.parseDepthWriteOff(line)) + elif command == 'DEPTH_TEST_ON': + iList.append(self.parseDepthTestOn(line)) + elif command == 'DEPTH_TEST_OFF': + iList.append(self.parseDepthTestOff(line)) + elif command == 'SET_BIN': + iList.append(self.parseSetBin(line)) + elif command == 'CLEAR_BIN': + iList.append(self.parseClearBin(line)) + elif command == 'TOON_HEAD': + iList.append(self.parseToonHead(line)) + elif command == 'SEND_EVENT': + iList.append(self.parseSendEvent(line)) + elif command == 'FUNCTION': + iList.append(self.parseFunction(line)) + elif command == 'BLACK_CAT_LISTEN': + iList.append(self.parseBlackCatListen(line)) + elif command == 'SHOW_PREVIEW': + if uponTimeout: + self.notify.error('SHOW_PREVIEW not allowed in an UPON_TIMEOUT') + nextEvent = 'donePreview' + iList.append(Func(self.acceptOnce, nextEvent, self.playNextChapter, [nextEvent])) + iList.append(self.parsePreview(line)) + self.closePreviousChapter(iList) + chapterList = [] + self.currentEvent = nextEvent + elif command == 'WAIT_EVENT': + if uponTimeout: + self.notify.error('WAIT_EVENT not allowed in an UPON_TIMEOUT') + nextEvent = self.parseWaitEvent(line) + + def proceed(self = self, nextEvent = nextEvent): + self.playNextChapter(nextEvent) + + def handleEvent(*args): + proceed = args[0] + proceed() + + iList.append(Func(self.acceptOnce, nextEvent, handleEvent, [proceed])) + self.closePreviousChapter(iList) + chapterList = [] + self.currentEvent = nextEvent + elif command == 'TUTORIAL_ACK_DONE': + iList.append(Func(base.localAvatar.setTutorialAck, True)) + else: + notify.warning('Unknown command token: %s for scriptId: %s on line: %s' % (command, self.scriptId, lineNum)) + + + self.closePreviousChapter(chapterList) + if timeoutList: + self.timeoutTrack = Sequence(*timeoutList) + self.playNextChapter('start') + return + + def closePreviousChapter(self, iList): + trackList = self.chapterDict.setdefault(self.currentEvent, []) + trackList.append(Sequence(*iList)) + + def parseLoad(self, line): + if len(line) == 3: + token, varName, modelPath = line + node = loader.loadModel(modelPath.replace('"', '')) + elif len(line) == 4: + token, varName, modelPath, subNodeName = line + node = loader.loadModel(modelPath.replace('"', '')).find('**/' + subNodeName) + else: + notify.error('invalid parseLoad command') + self.setVar(varName, node) + + def parseLoadSfx(self, line): + token, varName, fileName = line + sfx = base.loadSfx(fileName) + self.setVar(varName, sfx) + + def parseLoadSuit(self, line): + token, name, suitType = line + suit = Suit.Suit() + dna = SuitDNA.SuitDNA() + dna.newSuit(suitType) + suit.setDNA(dna) + self.setVar(name, suit) + + def parseSet(self, line): + token, varName, value = line + self.setVar(varName, value) + + def parseCall(self, line): + token, scriptId = line + nmp = NPCMoviePlayer(scriptId, self.toon, self.npc) + return Func(nmp.play) + + def parseLockLocalToon(self, line): + return Sequence(Func(self.toon.detachCamera), Func(self.toon.collisionsOff), Func(self.toon.disableAvatarControls), Func(self.toon.stopTrackAnimToSpeed), Func(self.toon.stopUpdateSmartCamera)) + + def parseFreeLocalToon(self, line): + return Sequence(Func(self.toon.attachCamera), Func(self.toon.startTrackAnimToSpeed), Func(self.toon.collisionsOn), Func(self.toon.enableAvatarControls), Func(self.toon.startUpdateSmartCamera)) + + def parseDebug(self, line): + token, str = line + return Func(notify.debug, str) + + def parseReparent(self, line): + if len(line) == 3: + token, childNodeName, parentNodeName = line + subNodeName = None + elif len(line) == 4: + token, childNodeName, parentNodeName, subNodeName = line + childNode = self.getVar(childNodeName) + if subNodeName: + parentNode = self.getVar(parentNodeName).find(subNodeName) + else: + parentNode = self.getVar(parentNodeName) + return ParentInterval(childNode, parentNode) + + def parseWrtReparent(self, line): + if len(line) == 3: + token, childNodeName, parentNodeName = line + subNodeName = None + elif len(line) == 4: + token, childNodeName, parentNodeName, subNodeName = line + childNode = self.getVar(childNodeName) + if subNodeName: + parentNode = self.getVar(parentNodeName).find(subNodeName) + else: + parentNode = self.getVar(parentNodeName) + return WrtParentInterval(childNode, parentNode) + + def parseShow(self, line): + token, nodeName = line + node = self.getVar(nodeName) + return Func(node.show) + + def parseHide(self, line): + token, nodeName = line + node = self.getVar(nodeName) + return Func(node.hide) + + def parsePos(self, line): + token, nodeName, x, y, z = line + node = self.getVar(nodeName) + return Func(node.setPos, x, y, z) + + def parseHpr(self, line): + token, nodeName, h, p, r = line + node = self.getVar(nodeName) + return Func(node.setHpr, h, p, r) + + def parseScale(self, line): + token, nodeName, x, y, z = line + node = self.getVar(nodeName) + return Func(node.setScale, x, y, z) + + def parsePosHprScale(self, line): + token, nodeName, x, y, z, h, p, r, sx, sy, sz = line + node = self.getVar(nodeName) + return Func(node.setPosHprScale, x, y, z, h, p, r, sx, sy, sz) + + def parseColor(self, line): + token, nodeName, r, g, b, a = line + node = self.getVar(nodeName) + return Func(node.setColor, r, g, b, a) + + def parseColorScale(self, line): + token, nodeName, r, g, b, a = line + node = self.getVar(nodeName) + return Func(node.setColorScale, r, g, b, a) + + def parseWait(self, line): + token, waitTime = line + return Wait(waitTime) + + def parseChat(self, line): + toonId = self.toon.getDoId() + avatarName = line[1] + avatar = self.getVar(avatarName) + chatString = eval('TTLocalizer.' + line[2]) + chatFlags = CFSpeech | CFTimeout + quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) + if extraChatFlags: + chatFlags |= extraChatFlags + if len(dialogueList) > 0: + dialogue = dialogueList[0] + else: + dialogue = None + return Func(avatar.setChatAbsolute, chatString, chatFlags, dialogue) + + def parseClearChat(self, line): + toonId = self.toon.getDoId() + avatarName = line[1] + avatar = self.getVar(avatarName) + chatFlags = CFSpeech | CFTimeout + return Func(avatar.setChatAbsolute, '', chatFlags) + + def parseExtraChatArgs(self, args): + quitButton = 0 + extraChatFlags = None + dialogueList = [] + for arg in args: + if type(arg) == type(0): + quitButton = arg + elif type(arg) == type(''): + if len(arg) > 2 and arg[:2] == 'CF': + extraChatFlags = eval(arg) + else: + dialogueList.append(self.getVar(arg)) + else: + pass + #notify.error('invalid argument type') + + return (quitButton, extraChatFlags, dialogueList) + + def parseChatConfirm(self, line): + lineLength = len(line) + toonId = self.toon.getDoId() + avatarName = line[1] + avatar = self.getVar(avatarName) + chatString = eval('TTLocalizer.' + line[2]) + quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) + return Func(avatar.setPageChat, toonId, 0, chatString, quitButton, extraChatFlags, dialogueList) + + def parseLocalChatConfirm(self, line): + lineLength = len(line) + avatarName = line[1] + avatar = self.getVar(avatarName) + chatString = eval('TTLocalizer.' + line[2]) + quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) + return Func(avatar.setLocalPageChat, chatString, quitButton, extraChatFlags, dialogueList) + + def parseLocalChatPersist(self, line): + lineLength = len(line) + avatarName = line[1] + avatar = self.getVar(avatarName) + chatString = eval('TTLocalizer.' + line[2]) + quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) + if len(dialogueList) > 0: + dialogue = dialogueList[0] + else: + dialogue = None + return Func(avatar.setChatAbsolute, chatString, CFSpeech, dialogue) + + def parseLocalChatToConfirm(self, line): + lineLength = len(line) + avatarKey = line[1] + avatar = self.getVar(avatarKey) + toAvatarKey = line[2] + toAvatar = self.getVar(toAvatarKey) + localizerAvatarName = toAvatar.getName().capitalize() + toAvatarName = eval('TTLocalizer.' + localizerAvatarName) + chatString = eval('TTLocalizer.' + line[3]) + chatString = chatString.replace('%s', toAvatarName) + quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[4:]) + return Func(avatar.setLocalPageChat, chatString, quitButton, extraChatFlags, dialogueList) + + def parsePlaySfx(self, line): + if len(line) == 2: + token, sfxName = line + looping = 0 + elif len(line) == 3: + token, sfxName, looping = line + else: + notify.error('invalid number of arguments') + sfx = self.getVar(sfxName) + return Func(base.playSfx, sfx, looping) + + def parseStopSfx(self, line): + token, sfxName = line + sfx = self.getVar(sfxName) + return Func(sfx.stop) + + def parsePlayAnim(self, line): + if len(line) == 3: + token, actorName, animName = line + playRate = 1.0 + elif len(line) == 4: + token, actorName, animName, playRate = line + else: + notify.error('invalid number of arguments') + actor = self.getVar(actorName) + return Sequence(Func(actor.setPlayRate, playRate, animName), Func(actor.play, animName)) + + def parseLoopAnim(self, line): + if len(line) == 3: + token, actorName, animName = line + playRate = 1.0 + elif len(line) == 4: + token, actorName, animName, playRate = line + else: + notify.error('invalid number of arguments') + actor = self.getVar(actorName) + return Sequence(Func(actor.setPlayRate, playRate, animName), Func(actor.loop, animName)) + + def parseLerpPos(self, line): + token, nodeName, x, y, z, t = line + node = self.getVar(nodeName) + return Sequence(LerpPosInterval(node, t, Point3(x, y, z), blendType='easeInOut'), duration=0.0) + + def parseLerpHpr(self, line): + token, nodeName, h, p, r, t = line + node = self.getVar(nodeName) + return Sequence(LerpHprInterval(node, t, VBase3(h, p, r), blendType='easeInOut'), duration=0.0) + + def parseLerpScale(self, line): + token, nodeName, x, y, z, t = line + node = self.getVar(nodeName) + return Sequence(LerpScaleInterval(node, t, VBase3(x, y, z), blendType='easeInOut'), duration=0.0) + + def parseLerpPosHprScale(self, line): + token, nodeName, x, y, z, h, p, r, sx, sy, sz, t = line + node = self.getVar(nodeName) + return Sequence(LerpPosHprScaleInterval(node, t, VBase3(x, y, z), VBase3(h, p, r), VBase3(sx, sy, sz), blendType='easeInOut'), duration=0.0) + + def parseLerpColor(self, line): + token, nodeName, sr, sg, sb, sa, er, eg, eb, ea, t = line + node = self.getVar(nodeName) + return Sequence(LerpColorInterval(node, t, VBase4(er, eg, eb, ea), startColorScale=VBase4(sr, sg, sb, sa), blendType='easeInOut'), duration=0.0) + + def parseLerpColorScale(self, line): + token, nodeName, sr, sg, sb, sa, er, eg, eb, ea, t = line + node = self.getVar(nodeName) + return Sequence(LerpColorScaleInterval(node, t, VBase4(er, eg, eb, ea), startColorScale=VBase4(sr, sg, sb, sa), blendType='easeInOut'), duration=0.0) + + def parseDepthWriteOn(self, line): + token, nodeName, depthWrite = line + node = self.getVar(nodeName) + return Sequence(Func(node.setDepthWrite, depthWrite)) + + def parseDepthWriteOff(self, line): + token, nodeName = line + node = self.getVar(nodeName) + return Sequence(Func(node.clearDepthWrite)) + + def parseDepthTestOn(self, line): + token, nodeName, depthTest = line + node = self.getVar(nodeName) + return Sequence(Func(node.setDepthTest, depthTest)) + + def parseDepthTestOff(self, line): + token, nodeName = line + node = self.getVar(nodeName) + return Sequence(Func(node.clearDepthTest)) + + def parseSetBin(self, line): + if len(line) == 3: + token, nodeName, binName = line + sortOrder = 0 + else: + token, nodeName, binName, sortOrder = line + node = self.getVar(nodeName) + return Sequence(Func(node.setBin, binName, sortOrder)) + + def parseClearBin(self, line): + token, nodeName = line + node = self.getVar(nodeName) + return Sequence(Func(node.clearBin)) + + def parseWaitEvent(self, line): + token, eventName = line + return eventName + + def parseSendEvent(self, line): + token, eventName = line + return Func(messenger.send, eventName) + + def parseFunction(self, line): + token, objectName, functionName = line + object = self.getVar(objectName) + cfunc = compile('object' + '.' + functionName, '', 'eval') + return Func(eval(cfunc)) + + def parseAddLaffMeter(self, line): + token, maxHpDelta = line + newMaxHp = maxHpDelta + self.toon.getMaxHp() + newHp = newMaxHp + laffMeter = self.getVar('laffMeter') + return Func(laffMeter.adjustFace, newHp, newMaxHp) + + def parseLaffMeter(self, line): + token, newHp, newMaxHp = line + laffMeter = self.getVar('laffMeter') + return Func(laffMeter.adjustFace, newHp, newMaxHp) + + def parseObscureLaffMeter(self, line): + token, val = line + return Func(self.toon.laffMeter.obscure, val) + + def parseAddInventory(self, line): + token, track, level, number = line + inventory = self.getVar('inventory') + countSound = base.loadSfx('phase_3.5/audio/sfx/tick_counter.ogg') + return Sequence(Func(base.playSfx, countSound), Func(inventory.buttonBoing, track, level), Func(inventory.addItems, track, level, number), Func(inventory.updateGUI, track, level)) + + def parseSetInventory(self, line): + token, track, level, number = line + inventory = self.getVar('inventory') + return Sequence(Func(inventory.setItem, track, level, number), Func(inventory.updateGUI, track, level)) + + def parseSetInventoryYPos(self, line): + token, track, level, yPos = line + inventory = self.getVar('inventory') + button = inventory.buttons[track][level].stateNodePath[0] + text = button.find('**/+TextNode') + return Sequence(Func(text.setY, yPos)) + + def parseSetInventoryDetail(self, line): + if len(line) == 2: + token, val = line + elif len(line) == 4: + token, val, track, level = line + else: + notify.error('invalid line for parseSetInventoryDetail: %s' % line) + inventory = self.getVar('inventory') + if val == -1: + return Func(inventory.noDetail) + elif val == 0: + return Func(inventory.hideDetail) + elif val == 1: + return Func(inventory.showDetail, track, level) + else: + notify.error('invalid inventory detail level: %s' % val) + + def parseShowFriendsList(self, line): + from toontown.friends import FriendsListPanel + return Func(FriendsListPanel.showFriendsListTutorial) + + def parseHideFriendsList(self, line): + from toontown.friends import FriendsListPanel + return Func(FriendsListPanel.hideFriendsListTutorial) + + def parseShowBook(self, line): + return Sequence(Func(self.toon.book.setPage, self.toon.mapPage), Func(self.toon.book.enter), Func(self.toon.book.disableBookCloseButton)) + + def parseEnableCloseBook(self, line): + return Sequence(Func(self.toon.book.enableBookCloseButton)) + + def parseHideBook(self, line): + return Func(self.toon.book.exit) + + def parseObscureBook(self, line): + token, val = line + return Func(self.toon.book.obscureButton, val) + + def parseObscureChat(self, line): + token, val0, val1 = line + return Func(self.toon.chatMgr.obscure, val0, val1) + + def parseArrowsOn(self, line): + arrows = self.getVar('arrows') + token, x1, y1, h1, x2, y2, h2 = line + return Func(arrows.arrowsOn, x1, y1, h1, x2, y2, h2) + + def parseArrowsOff(self, line): + arrows = self.getVar('arrows') + return Func(arrows.arrowsOff) + + def parseStartThrob(self, line): + token, nodeName, r, g, b, a, r2, g2, b2, a2, t = line + node = self.getVar(nodeName) + startCScale = Point4(r, g, b, a) + destCScale = Point4(r2, g2, b2, a2) + self.throbIval = Sequence(LerpColorScaleInterval(node, t / 2.0, destCScale, startColorScale=startCScale, blendType='easeInOut'), LerpColorScaleInterval(node, t / 2.0, startCScale, startColorScale=destCScale, blendType='easeInOut')) + return Func(self.throbIval.loop) + + def parseStopThrob(self, line): + return Func(self.throbIval.finish) + + def parseToonHead(self, line): + if len(line) == 5: + token, toonName, x, z, toggle = line + scale = 1.0 + else: + token, toonName, x, z, toggle, scale = line + toon = self.getVar(toonName) + toonId = toon.getDoId() + toonHeadFrame = self.toonHeads.get(toonId) + if not toonHeadFrame: + toonHeadFrame = ToonHeadFrame.ToonHeadFrame(toon) + toonHeadFrame.tag1Node + toonHeadFrame.hide() + self.toonHeads[toonId] = toonHeadFrame + self.setVar('%sToonHead' % toonName, toonHeadFrame) + if toggle: + return Sequence(Func(toonHeadFrame.setPos, x, 0, z), Func(toonHeadFrame.setScale, scale), Func(toonHeadFrame.show)) + else: + return Func(toonHeadFrame.hide) + + def parseToonHeadScale(self, line): + token, toonName, scale = line + toon = self.getVar(toonName) + toonId = toon.getDoId() + toonHeadFrame = self.toonHeads.get(toonId) + return Func(toonHeadFrame.setScale, scale) + + def parseBlackCatListen(self, line): + token, enable = line + if enable: + + def phraseSaid(phraseId): + toontastic = 315 + if phraseId == toontastic: + base.cr.blackCatMgr.requestBlackCatTransformation() + + def enableBlackCatListen(): + self.acceptOnce(SpeedChatGlobals.SCStaticTextMsgEvent, phraseSaid) + + return Func(enableBlackCatListen) + else: + + def disableBlackCatListen(): + self.ignore(SpeedChatGlobals.SCStaticTextMsgEvent) + + return Func(disableBlackCatListen) + + def parsePreview(self, line): + self.oldTrackAccess = None + + def grabCurTrackAccess(): + self.oldTrackAccess = copy.deepcopy(base.localAvatar.getTrackAccess()) + + def restoreTrackAccess(): + base.localAvatar.setTrackAccess(self.oldTrackAccess) + + minGagLevel = ToontownBattleGlobals.MIN_LEVEL_INDEX + 1 + maxGagLevel = ToontownBattleGlobals.MAX_LEVEL_INDEX + 1 + curGagLevel = minGagLevel + + def updateGagLevel(t, curGagLevel = curGagLevel): + newGagLevel = int(round(t)) + if newGagLevel == curGagLevel: + return + curGagLevel = newGagLevel + access = [0, 0, 0, 0, 0, 0, 0] + + for i in xrange(len(self.oldTrackAccess)): + access[i] = curGagLevel if self.oldTrackAccess[i] > 0 else 0 + + base.localAvatar.setTrackAccess(access) + + return Sequence(Func(grabCurTrackAccess), LerpFunctionInterval(updateGagLevel, fromData=1, toData=7, duration=0.3), WaitInterval(3.5), LerpFunctionInterval(updateGagLevel, fromData=7, toData=1, duration=0.3), Func(restoreTrackAccess), Func(messenger.send, 'donePreview')) + +readFile() diff --git a/toontown/quest/QuestPoster.py b/toontown/quest/QuestPoster.py new file mode 100755 index 00000000..9ccb40cf --- /dev/null +++ b/toontown/quest/QuestPoster.py @@ -0,0 +1,738 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +import Quests +from toontown.toon import NPCToons +from toontown.toon import ToonHead +from toontown.toon import ToonDNA +from toontown.suit import SuitDNA +from toontown.suit import Suit +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import string, types, random +from toontown.toon import LaffMeter +from toontown.toonbase.ToontownBattleGlobals import AvPropsNew +from direct.directnotify import DirectNotifyGlobal +from toontown.toontowngui import TTDialog +from otp.otpbase import OTPLocalizer +from toontown.building import DistributedBuildingQueryMgr + +IMAGE_SCALE_LARGE = 0.2 +IMAGE_SCALE_SMALL = 0.15 +POSTER_WIDTH = 0.7 +TEXT_SCALE = TTLocalizer.QPtextScale +TEXT_WORDWRAP = TTLocalizer.QPtextWordwrap + +class QuestPoster(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('QuestPoster') + colors = { + 'white':(1,1,1,1), + 'blue':(0.45,0.45,0.8,1), + 'lightBlue':(0.42,0.671,1.0,1.0), + 'green':(0.45,0.8,0.45,1), + 'lightGreen':(0.784,1,0.863,1), + 'red':(0.8,0.45,0.45,1), + 'rewardRed':(0.8,0.3,0.3,1), + 'brightRed':(1.0,0.16,0.16,1.0), + 'brown':(0.52,0.42,0.22,1) + } + normalTextColor=(0.3, 0.25, 0.2, 1) + confirmDeleteButtonEvent = 'confirmDeleteButtonEvent' + + def __init__(self, parent = aspect2d, **kw): + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + circleModel = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_nameShop').find('**/tt_t_gui_mat_namePanelCircle') + questCard = bookModel.find('**/questCard') + optiondefs = (('relief', None, None), + ('image', questCard, None), + ('image_scale', (0.8, 1.0, 0.58), None), + ('state', DGG.NORMAL, None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(QuestPoster) + self._deleteCallback = None + self.questFrame = DirectFrame(parent=self, relief=None) + self.headline = DirectLabel(parent=self.questFrame, relief=None, text='', text_font=ToontownGlobals.getMinnieFont(), text_fg=self.normalTextColor, text_scale=0.05, text_align=TextNode.ACenter, text_wordwrap=12.0, textMayChange=1, pos=(0, 0, 0.23)) + self.questInfo = DirectLabel(parent=self.questFrame, relief=None, text='', text_fg=self.normalTextColor, text_scale=TEXT_SCALE, text_align=TextNode.ACenter, text_wordwrap=TEXT_WORDWRAP, textMayChange=1, pos=(0, 0, -0.0625)) + self.rewardText = DirectLabel(parent=self.questFrame, relief=None, text='', text_fg=self.colors['rewardRed'], text_scale=0.0425, text_align=TextNode.ALeft, text_wordwrap=17.0, textMayChange=1, pos=(-0.36, 0, -0.26)) + self.rewardText.hide() + self.lPictureFrame = DirectFrame(parent=self.questFrame, relief=None, image=bookModel.find('**/questPictureFrame'), image_scale=IMAGE_SCALE_SMALL, text='', text_pos=(0, -0.11), text_fg=self.normalTextColor, text_scale=TEXT_SCALE, text_align=TextNode.ACenter, text_wordwrap=11.0, textMayChange=1) + self.lPictureFrame.hide() + self.rPictureFrame = DirectFrame(parent=self.questFrame, relief=None, image=bookModel.find('**/questPictureFrame'), image_scale=IMAGE_SCALE_SMALL, text='', text_pos=(0, -0.11), text_fg=self.normalTextColor, text_scale=TEXT_SCALE, text_align=TextNode.ACenter, text_wordwrap=11.0, textMayChange=1, pos=(0.18, 0, 0.13)) + self.rPictureFrame.hide() + self.lQuestIcon = DirectFrame(parent=self.lPictureFrame, relief=None, text=' ', text_font=ToontownGlobals.getSuitFont(), text_pos=(0, -0.03), text_fg=self.normalTextColor, text_scale=0.13, text_align=TextNode.ACenter, text_wordwrap=13.0, textMayChange=1) + self.lQuestIcon.setColorOff(-1) + self.rQuestIcon = DirectFrame(parent=self.rPictureFrame, relief=None, text=' ', text_font=ToontownGlobals.getSuitFont(), text_pos=(0, -0.03), text_fg=self.normalTextColor, text_scale=0.13, text_align=TextNode.ACenter, text_wordwrap=13.0, textMayChange=1) + self.rQuestIcon.setColorOff(-1) + self.auxText = DirectLabel(parent=self.questFrame, relief=None, text='', text_scale=TTLocalizer.QPauxText, text_fg=self.normalTextColor, text_align=TextNode.ACenter, textMayChange=1) + self.auxText.hide() + self.questProgress = DirectWaitBar(parent=self.questFrame, relief=DGG.SUNKEN, frameSize=(-0.95, 0.95, -0.1, 0.12), borderWidth=(0.025, 0.025), scale=0.2, frameColor=(0.945, 0.875, 0.706, 1.0), barColor=(0.5, 0.7, 0.5, 1), text='0/0', text_scale=0.19, text_fg=(0.05, 0.14, 0.4, 1), text_align=TextNode.ACenter, text_pos=(0, -0.04), pos=(0, 0, -0.195)) + self.questProgress.hide() + self.funQuest = DirectLabel(parent=self.questFrame, relief=None, text=TTLocalizer.QuestPosterFun, text_fg=(0.0, 0.439, 1.0, 1.0), text_shadow=(0, 0, 0, 1), pos=(0, 0, -0.125), scale=0.04) + self.funQuest.hide() + self.teleportButton = DirectButton(parent=self.questFrame, relief=None, image=circleModel, text=TTLocalizer.TeleportButton, text_scale=0.035, text_pos=(-0.0025, -0.015), pos=(0.175, 0, 0.125), scale=0.75) #, text_bg=(0, 0.75, 1, 1) + self.teleportButton.hide() + self.laffMeter = None + self.dialog = None + + def destroy(self): + self._deleteGeoms() + self.destroyDialog() + DirectFrame.destroy(self) + + def destroyDialog(self, extra=None): + if self.dialog: + self.dialog.destroy() + self.dialog = None + base.cr.playGame.getPlace().setState('walk') + + def _deleteGeoms(self): + for icon in (self.lQuestIcon, self.rQuestIcon): + geom = icon['geom'] + if geom: + if hasattr(geom, 'delete'): + geom.delete() + + def mouseEnterPoster(self, event): + self.reparentTo(self.getParent()) + sc = Vec3(self.initImageScale) + sc.setZ(sc[2] + 0.07) + self['image_scale'] = sc + self.questFrame.setZ(0.03) + self.headline.setZ(0.23 + 0.03) + self.lPictureFrame.setZ(0.13 + 0.03) + self.rPictureFrame.setZ(0.13 + 0.03) + self.questInfo.setZ(-0.0625 + 0.03) + self.questProgress.setZ(-0.195 + 0.03) + self.auxText.setZ(0.12 + 0.03) + self.funQuest.setZ(-0.125 + 0.03) + self.rewardText.setZ(-0.26 + 0.03) + self.rewardText.show() + + def mouseExitPoster(self, event): + self['image_scale'] = self.initImageScale + self.questFrame.setZ(0) + self.headline.setZ(0.23) + self.lPictureFrame.setZ(0.13) + self.rPictureFrame.setZ(0.13) + self.questInfo.setZ(-0.0625) + self.questProgress.setZ(-0.195) + self.auxText.setZ(0.12) + self.funQuest.setZ(-0.125) + self.rewardText.setZ(-0.26) + self.rewardText.hide() + + def createNpcToonHead(self, toNpcId): + npcInfo = NPCToons.NPCToonDict[toNpcId] + dnaList = npcInfo[2] + gender = npcInfo[3] + if dnaList == 'r': + dnaList = NPCToons.getRandomDNA(toNpcId, gender) + dna = ToonDNA.ToonDNA() + dna.newToonFromProperties(*dnaList) + head = ToonHead.ToonHead() + head.setupHead(dna, forGui=1) + self.fitGeometry(head, fFlip=1) + return head + + def createLaffMeter(self, hp): + lm = LaffMeter.LaffMeter(base.localAvatar.style, hp, hp) + lm.adjustText() + return lm + + def createSuitHead(self, suitName): + suitDNA = SuitDNA.SuitDNA() + suitDNA.newSuit(suitName) + suit = Suit.Suit() + suit.setDNA(suitDNA) + headParts = suit.getHeadParts() + head = hidden.attachNewNode('head') + for part in headParts: + copyPart = part.copyTo(head) + copyPart.setDepthTest(1) + copyPart.setDepthWrite(1) + + self.fitGeometry(head, fFlip=1) + suit.delete() + suit = None + return head + + def loadElevator(self, building, numFloors): + elevatorNodePath = hidden.attachNewNode('elevatorNodePath') + elevatorModel = loader.loadModel('phase_4/models/modules/elevator') + floorIndicator = [None] * 5 + npc = elevatorModel.findAllMatches('**/floor_light_?;+s') + for i in xrange(npc.getNumPaths()): + np = npc.getPath(i) + floor = int(np.getName()[-1:]) - 1 + floorIndicator[floor] = np + if floor < numFloors: + np.setColor(Vec4(0.5, 0.5, 0.5, 1.0)) + else: + np.hide() + + elevatorModel.reparentTo(elevatorNodePath) + suitDoorOrigin = building.find('**/*_door_origin') + elevatorNodePath.reparentTo(suitDoorOrigin) + elevatorNodePath.setPosHpr(0, 0, 0, 0, 0, 0) + + def teleportToShop(self, npcId): + if base.cr.playGame.getPlace().getState() != 'walk': + return + + npcZone = NPCToons.getNPCZone(npcId) + npcHood = ZoneUtil.getCanonicalHoodId(npcZone) + hqZone = {2000:2520, 1000:1507, 3000:3508, 4000:4504, 5000:5502, 7000:7503, 9000:9505} + + if npcZone in (-1, 0, None): + zoneId = base.localAvatar.getZoneId() + if ZoneUtil.isDynamicZone(zoneId) or ZoneUtil.isCogHQZone(zoneId): + zoneId = 2000 + npcHood = ZoneUtil.getCanonicalHoodId(zoneId) + npcZone = hqZone.get(npcHood, 2520) + + cost = ToontownGlobals.getTeleportButtonCost(npcHood) + self.destroyDialog() + base.cr.playGame.getPlace().setState('stopped') + + if base.localAvatar.getTotalMoney() < cost: + self.dialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.TeleportButtonNoMoney % cost, command=self.destroyDialog) + else: + self.dialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.TeleportButtonConfirm % cost, command=lambda value: self.teleportToShopConfirm(npcZone, npcHood, cost, value)) + + self.dialog.show() + + def teleportToShopConfirm(self, npcZone, npcHood, cost, value): + self.destroyDialog() + + if value > 0: + base.cr.buildingQueryMgr.d_isSuit(npcZone, lambda isSuit: self.teleportToShopCallback(npcZone, npcHood, cost, isSuit)) + + def teleportToShopCallback(self, npcZone, npcHood, cost, flag): + if flag: + base.cr.playGame.getPlace().setState('stopped') + self.dialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.TeleportButtonTakenOver, command=self.destroyDialog) + self.dialog.show() + return + + base.localAvatar.takeMoney(cost) + base.cr.playGame.getPlace().requestTeleport(npcHood, npcZone, base.localAvatar.defaultShard, -1) + + def fitGeometry(self, geom, fFlip = 0, dimension = 0.8): + p1 = Point3() + p2 = Point3() + geom.calcTightBounds(p1, p2) + if fFlip: + t = p1[0] + p1.setX(-p2[0]) + p2.setX(-t) + d = p2 - p1 + biggest = max(d[0], d[2]) + s = dimension / biggest + mid = (p1 + d / 2.0) * s + geomXform = hidden.attachNewNode('geomXform') + for child in geom.getChildren(): + child.reparentTo(geomXform) + + geomXform.setPosHprScale(-mid[0], -mid[1] + 1, -mid[2], 180, 0, 0, s, s, s) + geomXform.reparentTo(geom) + + def clear(self): + self['image_color'] = Vec4(*self.colors['white']) + self.headline['text'] = '' + self.headline['text_fg'] = self.normalTextColor + self.questInfo['text'] = '' + self.questInfo['text_fg'] = self.normalTextColor + self.rewardText['text'] = '' + self.auxText['text'] = '' + self.auxText['text_fg'] = self.normalTextColor + self.funQuest.hide() + self.lPictureFrame.hide() + self.rPictureFrame.hide() + self.questProgress.hide() + self.teleportButton.hide() + self.destroyDialog() + if hasattr(self, 'chooseButton'): + self.chooseButton.destroy() + del self.chooseButton + if hasattr(self, 'deleteButton'): + self.deleteButton.destroy() + del self.deleteButton + self.ignore(self.confirmDeleteButtonEvent) + if hasattr(self, 'confirmDeleteButton'): + self.confirmDeleteButton.cleanup() + del self.confirmDeleteButton + if self.laffMeter != None: + self.laffMeter.reparentTo(hidden) + self.laffMeter.destroy() + self.laffMeter = None + return + + def showChoicePoster(self, questId, fromNpcId, toNpcId, rewardId, callback): + self.update((questId, + fromNpcId, + toNpcId, + rewardId, + 0)) + quest = Quests.getQuest(questId) + self.rewardText.show() + self.rewardText.setZ(-0.205) + self.questProgress.hide() + if not hasattr(self, 'chooseButton'): + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.chooseButton = DirectButton(parent=self.questFrame, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.7, 1, 1), text=TTLocalizer.QuestPageChoose, text_scale=0.06, text_pos=(0, -0.02), pos=(0.285, 0, 0.245), scale=0.65) + guiButton.removeNode() + npcZone = NPCToons.getNPCZone(toNpcId) + hoodId = ZoneUtil.getCanonicalHoodId(npcZone) + self.chooseButton['command'] = callback + self.chooseButton['extraArgs'] = [questId] + self.unbind(DGG.WITHIN) + self.unbind(DGG.WITHOUT) + if not quest.getType() == Quests.TrackChoiceQuest: + self.questInfo.setZ(-0.0625) + return + + def update(self, questDesc): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + quest = Quests.getQuest(questId) + self.teleportButton['command'] = self.teleportToShop + self.teleportButton['extraArgs'] = [toNpcId] + if quest == None: + self.notify.warning('Tried to display poster for unknown quest %s' % questId) + return + if rewardId == Quests.NA: + finalReward = Quests.getFinalRewardId(questId, fAll=1) + transformedReward = Quests.transformReward(finalReward, base.localAvatar) + reward = Quests.getReward(transformedReward) + else: + reward = Quests.getReward(rewardId) + if reward and questId not in Quests.NoRewardTierZeroQuests: + rewardString = reward.getPosterString() + else: + rewardString = '' + self.rewardText['text'] = rewardString + self.fitLabel(self.rewardText) + + if Quests.isQuestJustForFun(questId, rewardId): + self.funQuest.show() + else: + self.funQuest.hide() + + if self._deleteCallback: + self.showDeleteButton(questDesc) + else: + self.hideDeleteButton() + + fComplete = quest.getCompletionStatus(base.localAvatar, questDesc) == Quests.COMPLETE + + if toNpcId == Quests.ToonHQ: + toNpcName = TTLocalizer.QuestPosterHQOfficer + toNpcBuildingName = TTLocalizer.QuestPosterHQBuildingName + toNpcStreetName = TTLocalizer.QuestPosterHQStreetName + toNpcLocationName = TTLocalizer.QuestPosterHQLocationName + elif toNpcId == Quests.ToonTailor: + toNpcName = TTLocalizer.QuestPosterTailor + toNpcBuildingName = TTLocalizer.QuestPosterTailorBuildingName + toNpcStreetName = TTLocalizer.QuestPosterTailorStreetName + toNpcLocationName = TTLocalizer.QuestPosterTailorLocationName + else: + toNpcName = NPCToons.getNPCName(toNpcId) + toNpcZone = NPCToons.getNPCZone(toNpcId) + toNpcHoodId = ZoneUtil.getCanonicalHoodId(toNpcZone) + toNpcLocationName = base.cr.hoodMgr.getFullnameFromId(toNpcHoodId) + toNpcBuildingName = NPCToons.getBuildingTitle(toNpcZone) + toNpcBranchId = ZoneUtil.getBranchZone(toNpcZone) + toNpcStreetName = ZoneUtil.getStreetName(toNpcBranchId) + lPos = Vec3(0, 0, 0.13) + lIconGeom = None + lIconGeomScale = 1 + rIconGeom = None + rIconGeomScale = 1 + infoText = '' + infoZ = TTLocalizer.QPinfoZ + auxText = None + auxTextPos = Vec3(0, 0, 0.12) + headlineString = quest.getHeadlineString() + objectiveStrings = quest.getObjectiveStrings() + captions = map(string.capwords, quest.getObjectiveStrings()) + imageColor = Vec4(*self.colors['white']) + self.teleportButton.hide() + + if base.localAvatar.tutorialAck and (fComplete or quest.getType() in (Quests.DeliverGagQuest, Quests.DeliverItemQuest, Quests.VisitQuest, Quests.TrackChoiceQuest)): + self.teleportButton.show() + self.teleportButton.setPos(0.3, 0, -0.15) + + if isinstance(quest, Quests.TexturedQuest) and quest.hasFrame(): + frame = quest.getFrame() + frameBgColor = frame[1] + lIconGeom = frame[0] + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.DeliverGagQuest or quest.getType() == Quests.DeliverItemQuest: + frameBgColor = 'red' + if quest.getType() == Quests.DeliverGagQuest: + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + track, item = quest.getGagType() + lIconGeom = invModel.find('**/' + AvPropsNew[track][item]) + invModel.removeNode() + else: + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/package') + lIconGeomScale = 0.12 + bookModel.removeNode() + if not fComplete: + captions.append(toNpcName) + auxText = TTLocalizer.QuestPosterAuxTo + auxTextPos.setZ(0.12) + lPos.setX(-0.18) + infoText = TTLocalizer.QuestPageDestination % (toNpcBuildingName, toNpcStreetName, toNpcLocationName) + rIconGeom = self.createNpcToonHead(toNpcId) + rIconGeomScale = IMAGE_SCALE_SMALL + elif quest.getType() == Quests.RecoverItemQuest: + frameBgColor = 'green' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/package') + lIconGeomScale = 0.12 + bookModel.removeNode() + if not fComplete: + rIconGeomScale = IMAGE_SCALE_SMALL + holder = quest.getHolder() + holderType = quest.getHolderType() + if holder == Quests.Any: + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + rIconGeom = cogIcons.find('**/cog') + cogIcons.removeNode() + lPos.setX(-0.18) + auxText = TTLocalizer.QuestPosterAuxFrom + elif holder == Quests.AnyFish: + headlineString = TTLocalizer.QuestPosterFishing + auxText = TTLocalizer.QuestPosterAuxFor + auxTextPos.setX(-0.18) + captions = captions[:1] + else: + if holderType == 'track': + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + if holder in SuitDNA.suitDeptModelPaths: + icon = cogIcons.find(SuitDNA.suitDeptModelPaths[holder]) + rIconGeom = icon.copyTo(hidden) + rIconGeom.setColor(Suit.Suit.medallionColors[holder]) + rIconGeomScale = 0.12 + cogIcons.removeNode() + elif holderType == 'level': + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + rIconGeom = cogIcons.find('**/cog') + rIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + else: + rIconGeom = self.createSuitHead(holder) + lPos.setX(-0.18) + auxText = TTLocalizer.QuestPosterAuxFrom + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.VisitQuest: + frameBgColor = 'brown' + captions[0] = '%s' % toNpcName + lIconGeom = self.createNpcToonHead(toNpcId) + lIconGeomScale = IMAGE_SCALE_SMALL + if not fComplete: + infoText = TTLocalizer.QuestPageDestination % (toNpcBuildingName, toNpcStreetName, toNpcLocationName) + elif quest.getType() == Quests.TrackChoiceQuest: + frameBgColor = 'green' + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + + track1, track2 = quest.getChoices() + lIconGeom = invModel.find('**/' + AvPropsNew[track1][1]) + + if not fComplete: + auxText = TTLocalizer.QuestPosterAuxOr + lPos.setX(-0.18) + rIconGeom = invModel.find('**/' + AvPropsNew[track2][1]) + + infoText = TTLocalizer.QuestPageNameAndDestination % (toNpcName, + toNpcBuildingName, + toNpcStreetName, + toNpcLocationName) + infoZ = -0.02 + invModel.removeNode() + elif quest.getType() == Quests.BuildingQuest: + frameBgColor = 'blue' + track = quest.getBuildingTrack() + numFloors = quest.getNumFloors() + if track == 'c': + lIconGeom = loader.loadModel('phase_4/models/modules/suit_landmark_corp') + elif track == 'l': + lIconGeom = loader.loadModel('phase_4/models/modules/suit_landmark_legal') + elif track == 'm': + lIconGeom = loader.loadModel('phase_4/models/modules/suit_landmark_money') + elif track == 's': + lIconGeom = loader.loadModel('phase_4/models/modules/suit_landmark_sales') + else: + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/COG_building') + bookModel.removeNode() + if lIconGeom and track != Quests.Any: + self.loadElevator(lIconGeom, numFloors) + lIconGeom.setH(180) + self.fitGeometry(lIconGeom, fFlip=0) + lIconGeomScale = IMAGE_SCALE_SMALL + else: + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.FactoryQuest: + frameBgColor = 'blue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/factoryIcon2') + bookModel.removeNode() + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.MintQuest: + frameBgColor = 'blue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/CashBotMint') + bookModel.removeNode() + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.CogPartQuest: + frameBgColor = 'green' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/CogArmIcon2') + bookModel.removeNode() + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.ForemanQuest or quest.getType() == Quests.SupervisorQuest: + frameBgColor = 'blue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/skelecog5') + bookModel.removeNode() + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.RescueQuest: + frameBgColor = 'blue' + lIconGeom = self.createNpcToonHead(random.choice(NPCToons.HQnpcFriends.keys())) + lIconGeomScale = 0.13 + infoText = quest.getLocationName().strip() + elif quest.getType() == Quests.FriendQuest: + frameBgColor = 'brown' + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + lIconGeom = gui.find('**/FriendsBox_Closed') + lIconGeomScale = 0.45 + gui.removeNode() + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.TrolleyQuest: + frameBgColor = 'lightBlue' + gui = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = gui.find('**/trolley') + lIconGeomScale = 0.13 + gui.removeNode() + infoText = TTLocalizer.QuestPosterPlayground + elif quest.getType() == Quests.MailboxQuest: + frameBgColor = 'lightBlue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/package') + lIconGeomScale = 0.12 + bookModel.removeNode() + infoText = TTLocalizer.QuestPosterAtHome + elif quest.getType() == Quests.PhoneQuest: + frameBgColor = 'lightBlue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/clarabelleCow') + lIconGeomScale = 0.12 + bookModel.removeNode() + infoText = TTLocalizer.QuestPosterOnPhone + else: + frameBgColor = 'blue' + if quest.getType() == Quests.CogTrackQuest: + dept = quest.getCogTrack() + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + lIconGeomScale = 0.13 + if dept in SuitDNA.suitDeptModelPaths: + icon = cogIcons.find(SuitDNA.suitDeptModelPaths[dept]) + lIconGeom = icon.copyTo(hidden) + lIconGeom.setColor(Suit.Suit.medallionColors[dept]) + cogIcons.removeNode() + elif quest.getType() == Quests.CogQuest: + if quest.getCogType() != Quests.Any: + lIconGeom = self.createSuitHead(quest.getCogType()) + lIconGeomScale = IMAGE_SCALE_SMALL + else: + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + lIconGeom = cogIcons.find('**/cog') + lIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + elif quest.getType() == Quests.CogLevelQuest: + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + lIconGeom = cogIcons.find('**/cog') + lIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + elif quest.getType() == Quests.SkelecogTrackQuest: + dept = quest.getCogTrack() + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + lIconGeomScale = 0.13 + if dept in SuitDNA.suitDeptModelPaths: + icon = cogIcons.find(SuitDNA.suitDeptModelPaths[dept]) + lIconGeom = icon.copyTo(hidden) + lIconGeom.setColor(Suit.Suit.medallionColors[dept]) + cogIcons.removeNode() + elif quest.getType() == Quests.SkelecogQuest: + cogIcons = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = cogIcons.find('**/skelecog5') + lIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + elif quest.getType() == Quests.SkelecogLevelQuest: + cogIcons = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = cogIcons.find('**/skelecog5') + lIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + elif quest.getType() == Quests.SkeleReviveQuest: + cogIcons = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = cogIcons.find('**/skelecog5') + lIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + if fComplete: + textColor = (0, 0.3, 0, 1) + imageColor = Vec4(*self.colors['lightGreen']) + lPos.setX(-0.18) + rIconGeom = self.createNpcToonHead(toNpcId) + rIconGeomScale = IMAGE_SCALE_SMALL + captions = captions[:1] + captions.append(toNpcName) + auxText = TTLocalizer.QuestPosterAuxReturnTo + headlineString = TTLocalizer.QuestPosterComplete + infoText = TTLocalizer.QuestPageDestination % (toNpcBuildingName, toNpcStreetName, toNpcLocationName) + if self.laffMeter != None: + self.laffMeter.reparentTo(hidden) + self.laffMeter.destroy() + self.laffMeter = None + else: + textColor = self.normalTextColor + self.show() + self['image_color'] = imageColor + self.headline['text_fg'] = textColor + self.headline['text'] = headlineString + self.lPictureFrame.show() + self.lPictureFrame.setPos(lPos) + self.lPictureFrame['text_scale'] = TEXT_SCALE + if lPos[0] != 0: + self.lPictureFrame['text_scale'] = 0.0325 + self.lPictureFrame['text'] = captions[0] + self.lPictureFrame['image_color'] = Vec4(*self.colors[frameBgColor]) + if len(captions) > 1: + self.rPictureFrame.show() + self.rPictureFrame['text'] = captions[1] + self.rPictureFrame['text_scale'] = 0.0325 + self.rPictureFrame['image_color'] = Vec4(*self.colors[frameBgColor]) + else: + self.rPictureFrame.hide() + self._deleteGeoms() + self.lQuestIcon['geom'] = lIconGeom + self.lQuestIcon['geom_pos'] = (0, 10, 0) + if lIconGeom: + try: + self.lQuestIcon['geom_scale'] = lIconGeomScale + except: pass + try: + self.lQuestIcon['geom_pos'] = Point3(lIconGeomPos[0], lIconGeomPos[1], lIconGeomPos[2]) + except: pass + try: + self.lQuestIcon['geom_hpr'] = Point3(lIconGeomHpr[0], lIconGeomHpr[1], lIconGeomHpr[2]) + except: pass + if self.laffMeter != None: + self.laffMeter.reparentTo(self.lQuestIcon) + self.rQuestIcon['geom'] = rIconGeom + self.rQuestIcon['geom_pos'] = (0, 10, 0) + if rIconGeom: + self.rQuestIcon['geom_scale'] = rIconGeomScale + if auxText: + self.auxText.show() + self.auxText['text'] = auxText + self.auxText.setPos(auxTextPos) + else: + self.auxText.hide() + self.bind(DGG.WITHIN, self.mouseEnterPoster) + self.bind(DGG.WITHOUT, self.mouseExitPoster) + numQuestItems = quest.getNumQuestItems() + if fComplete or numQuestItems <= 1: + self.questProgress.hide() + if not quest.getType() == Quests.TrackChoiceQuest: + infoZ = -0.075 + else: + self.questProgress.show() + self.questProgress['value'] = toonProgress & pow(2, 16) - 1 + self.questProgress['range'] = numQuestItems + self.questProgress['text'] = quest.getProgressString(base.localAvatar, questDesc) + self.questInfo['text'] = infoText + self.questInfo.setZ(infoZ) + self.fitLabel(self.questInfo) + return + + def unbindMouseEnter(self): + self.unbind(DGG.WITHIN) + + def showDeleteButton(self, questDesc): + self.hideDeleteButton() + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') + self.deleteButton = DirectButton(parent=self.questFrame, image=(trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_RLVR')), text=('', TTLocalizer.QuestPosterDeleteBtn, TTLocalizer.QuestPosterDeleteBtn), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.18, text_pos=(0, -0.12), relief=None, pos=(0.3, 0, 0.145), scale=0.3, command=self.onPressedDeleteButton, extraArgs=[questDesc]) + trashcanGui.removeNode() + return + + def hideDeleteButton(self): + if hasattr(self, 'deleteButton'): + self.deleteButton.destroy() + del self.deleteButton + + def setDeleteCallback(self, callback): + self._deleteCallback = callback + + def onPressedDeleteButton(self, questDesc): + self.deleteButton['state'] = DGG.DISABLED + self.accept(self.confirmDeleteButtonEvent, self.confirmedDeleteButton) + self.confirmDeleteButton = TTDialog.TTGlobalDialog(doneEvent=self.confirmDeleteButtonEvent, message=TTLocalizer.QuestPosterConfirmDelete, style=TTDialog.YesNo, okButtonText=TTLocalizer.QuestPosterDialogYes, cancelButtonText=TTLocalizer.QuestPosterDialogNo) + self.confirmDeleteButton.quest = questDesc + self.confirmDeleteButton.doneStatus = '' + self.confirmDeleteButton.show() + + def confirmedDeleteButton(self): + questDesc = self.confirmDeleteButton.quest + self.ignore(self.confirmDeleteButtonEvent) + if self.confirmDeleteButton.doneStatus == 'ok': + if self._deleteCallback: + self._deleteCallback(questDesc) + else: + self.deleteButton['state'] = DGG.NORMAL + self.confirmDeleteButton.cleanup() + del self.confirmDeleteButton + + def fitLabel(self, label, lineNo = 0): + text = label['text'] + label['text_scale'] = TEXT_SCALE + label['text_wordwrap'] = TEXT_WORDWRAP + if len(text) > 0: + lines = text.split('\n') + lineWidth = label.component('text0').textNode.calcWidth(lines[lineNo]) + if lineWidth > 0: + textScale = POSTER_WIDTH / lineWidth + label['text_scale'] = min(TEXT_SCALE, textScale) + label['text_wordwrap'] = max(TEXT_WORDWRAP, lineWidth + 0.05) diff --git a/toontown/quest/QuestRewardCounter.py b/toontown/quest/QuestRewardCounter.py new file mode 100755 index 00000000..f5efa13a --- /dev/null +++ b/toontown/quest/QuestRewardCounter.py @@ -0,0 +1,150 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +import Quests +from toontown.toonbase import ToontownGlobals +from toontown.fishing import FishGlobals +from toontown.suit import SuitDNA +from toontown.racing import RaceGlobals +from toontown.estate import GardenGlobals +from toontown.golf import GolfGlobals + +class QuestRewardCounter: + notify = directNotify.newCategory('QuestRewardCounter') + + def __init__(self): + self.reset() + + def reset(self): + self.maxHp = 15 + self.maxCarry = 20 + self.maxMoney = 40 + self.questCarryLimit = 1 + self.teleportAccess = [] + self.trackAccess = [0, + 0, + 0, + 0, + 1, + 1, + 0] + self.trackProgressId = -1 + self.trackProgress = 0 + + def addTeleportAccess(self, zoneId): + if zoneId not in self.teleportAccess: + self.teleportAccess.append(zoneId) + + def addTrackAccess(self, track): + self.trackAccess[track] = 1 + + def addTrackProgress(self, trackId, progressIndex): + if self.trackProgressId != trackId: + self.notify.warning('tried to update progress on a track toon is not training') + self.trackProgress = self.trackProgress | 1 << progressIndex + + def getTrackProgress(self): + return (self.trackProgressId, self.trackProgress) + + def clearTrackProgress(self): + self.trackProgressId = -1 + self.trackProgress = 0 + + def setFromAvatar(self, av): + rewardIds = [] + for q in av.quests: + questId, fromNpcId, toNpcId, rewardId, toonProgress = q + if rewardId == Quests.NA: + rewardId = Quests.getFinalRewardId(questId, fAll=1) + rewardIds.append(rewardId) + + self.notify.debug('Ignoring rewards: %s' % rewardIds) + self.setRewardIndex(av.rewardTier, rewardIds, av.rewardHistory) + fishHp = int(len(av.fishCollection) / FishGlobals.FISH_PER_BONUS) + self.notify.debug('Adding %s hp for fish collection' % fishHp) + self.maxHp += fishHp + flowerHp = int(len(av.flowerCollection) / GardenGlobals.FLOWERS_PER_BONUS) + self.notify.debug('Adding %s hp for fish collection' % flowerHp) + self.maxHp += flowerHp + HQdepts = (ToontownGlobals.cogHQZoneId2deptIndex(ToontownGlobals.SellbotHQ), ToontownGlobals.cogHQZoneId2deptIndex(ToontownGlobals.LawbotHQ), ToontownGlobals.cogHQZoneId2deptIndex(ToontownGlobals.CashbotHQ)) + levels = av.getCogLevels() + cogTypes = av.getCogTypes() + suitHp = 0 + for dept in HQdepts: + level = levels[dept] + type = cogTypes[dept] + if type >= SuitDNA.suitsPerDept - 1: + for milestoneLevel in ToontownGlobals.CogSuitHPLevels: + if level >= milestoneLevel: + suitHp += 1 + else: + break + + self.notify.debug('Adding %s hp for cog suits' % suitHp) + self.maxHp += suitHp + kartingHp = int(av.kartingTrophies.count(1) / RaceGlobals.TrophiesPerCup) + self.notify.debug('Adding %s hp for karting trophies' % kartingHp) + self.maxHp += kartingHp + golfHp = int(av.golfTrophies.count(True) / GolfGlobals.TrophiesPerCup) + self.notify.debug('Adding %s hp for golf trophies' % golfHp) + self.maxHp += golfHp + + def setRewardIndex(self, tier, rewardIds, rewardHistory): + self.reset() + for tierNum in xrange(tier): + for rewardId in Quests.getRewardsInTier(tierNum): + reward = Quests.getReward(rewardId) + reward.countReward(self) + self.notify.debug('Assigning reward %d' % rewardId) + + for rewardId in rewardHistory: + if rewardId in Quests.getRewardsInTier(tier): + if rewardIds.count(rewardId) != 0: + rewardIds.remove(rewardId) + self.notify.debug('Ignoring reward %d' % rewardId) + else: + reward = Quests.getReward(rewardId) + reward.countReward(self) + self.notify.debug('Assigning reward %d' % rewardId) + + self.notify.debug('Remaining rewardIds %s' % rewardIds) + self.maxHp = min(ToontownGlobals.MaxHpLimit, self.maxHp) + + def assignReward(self, rewardId, rewardIds): + if rewardIds.count(rewardId) != 0: + rewardIds.remove(rewardId) + self.notify.debug('Ignoring reward %d' % rewardId) + else: + reward = Quests.getReward(rewardId) + reward.countReward(self) + self.notify.debug('Assigning reward %d' % rewardId) + + def fixAvatar(self, av): + self.setFromAvatar(av) + anyChanged = 0 + if self.maxHp != av.maxHp: + self.notify.info('Changed avatar %d to have maxHp %d instead of %d' % (av.doId, self.maxHp, av.maxHp)) + av.b_setMaxHp(self.maxHp) + anyChanged = 1 + if self.maxCarry != av.maxCarry: + self.notify.info('Changed avatar %d to have maxCarry %d instead of %d' % (av.doId, self.maxCarry, av.maxCarry)) + av.b_setMaxCarry(self.maxCarry) + anyChanged = 1 + if self.maxMoney != av.maxMoney: + self.notify.info('Changed avatar %d to have maxMoney %d instead of %d' % (av.doId, self.maxMoney, av.maxMoney)) + av.b_setMaxMoney(self.maxMoney) + anyChanged = 1 + if self.questCarryLimit != av.questCarryLimit: + self.notify.info('Changed avatar %d to have questCarryLimit %d instead of %d' % (av.doId, self.questCarryLimit, av.questCarryLimit)) + av.b_setQuestCarryLimit(self.questCarryLimit) + anyChanged = 1 + if self.teleportAccess != av.teleportZoneArray: + self.notify.info('Changed avatar %d to have teleportAccess %s instead of %s' % (av.doId, self.teleportAccess, av.teleportZoneArray)) + av.b_setTeleportAccess(self.teleportAccess) + anyChanged = 1 + if self.trackAccess != av.trackArray: + self.notify.info('Changed avatar %d to have trackAccess %s instead of %s' % (av.doId, self.trackAccess, av.trackArray)) + av.b_setTrackAccess(self.trackAccess) + anyChanged = 1 + if av.fixTrackAccess(): + anyChanged = 1 + return anyChanged diff --git a/toontown/quest/QuestScripts.py b/toontown/quest/QuestScripts.py new file mode 100755 index 00000000..77f59772 --- /dev/null +++ b/toontown/quest/QuestScripts.py @@ -0,0 +1,379 @@ +script = ''' +ID quest_assign_101 +CLEAR_CHAT npc +LOAD squirt1 "phase_3.5/models/gui/tutorial_gui" "squirt1" +LOAD squirt2 "phase_3.5/models/gui/tutorial_gui" "squirt2" +LOAD toonBuilding "phase_3.5/models/gui/tutorial_gui" "toon_buildings" +LOAD cogBuilding "phase_3.5/models/gui/tutorial_gui" "suit_buildings" +LOAD cogs "phase_3.5/models/gui/tutorial_gui" "suits" +POSHPRSCALE cogs -1.05 7 0 0 0 0 1 1 1 +POSHPRSCALE toonBuilding -1.05 7 0 0 0 0 1.875 1.875 1.875 +POSHPRSCALE cogBuilding -1.05 7 0 0 0 0 1.875 1.875 1.875 +POSHPRSCALE squirt1 -1.05 7 0 0 0 0 1.875 1.875 1.875 +POSHPRSCALE squirt2 -1.05 7 0 0 0 0 1.875 1.875 1.875 +WRTREPARENTTO camera npc +LERP_POSHPRSCALE camera 2.8 0.0 3.5 35 5 0 1 1 1 1.5 +WRTREPARENTTO camera localToon +PLAY_ANIM npc "right-hand-start" 1 +WAIT 1 +REPARENTTO cogs camera +LERP_SCALE cogs 1.875 1.875 1.875 0.5 +WAIT 1.0833 +LOOP_ANIM npc "right-hand" 1 +FUNCTION npc "angryEyes" +FUNCTION npc "blinkEyes" +LOCAL_CHAT_CONFIRM npc QuestScript101_1 "CFReversed" +LOCAL_CHAT_CONFIRM npc QuestScript101_2 "CFReversed" +REPARENTTO cogs hidden +REPARENTTO toonBuilding camera +LOCAL_CHAT_CONFIRM npc QuestScript101_3 "CFReversed" +REPARENTTO toonBuilding hidden +REPARENTTO cogBuilding camera +FUNCTION npc "sadEyes" +FUNCTION npc "blinkEyes" +LOCAL_CHAT_CONFIRM npc QuestScript101_4 "CFReversed" +REPARENTTO cogBuilding hidden +REPARENTTO squirt1 camera +FUNCTION npc "normalEyes" +FUNCTION npc "blinkEyes" +LOCAL_CHAT_CONFIRM npc QuestScript101_5 "CFReversed" +REPARENTTO squirt1 hidden +REPARENTTO squirt2 camera +LOCAL_CHAT_CONFIRM npc QuestScript101_6 "CFReversed" +LERP_SCALE squirt2 1 1 0.01 0.5 +WAIT 0.5 +REPARENTTO squirt2 hidden +OBSCURE_LAFFMETER 0 +SHOW laffMeter +POS laffMeter 0.153 0.0 0.13 +SCALE laffMeter 0.0 0.0 0.0 +WRTREPARENTTO laffMeter aspect2d +LERP_POS laffMeter -0.25 0 -0.15 1 +LERP_SCALE laffMeter 0.2 0.2 0.2 0.6 +WAIT 1.0833 +LOOP_ANIM npc "right-hand" +LOCAL_CHAT_CONFIRM npc QuestScript101_8 "CFReversed" +LOCAL_CHAT_CONFIRM npc QuestScript101_9 "CFReversed" +FUNCTION npc "sadEyes" +FUNCTION npc "blinkEyes" +LAFFMETER 15 15 +WAIT 0.1 +LAFFMETER 14 15 +WAIT 0.1 +LAFFMETER 13 15 +WAIT 0.1 +LAFFMETER 12 15 +WAIT 0.1 +LAFFMETER 11 15 +WAIT 0.1 +LAFFMETER 10 15 +WAIT 0.1 +LAFFMETER 9 15 +WAIT 0.1 +LAFFMETER 8 15 +WAIT 0.1 +LAFFMETER 7 15 +WAIT 0.1 +LAFFMETER 6 15 +WAIT 0.1 +LAFFMETER 5 15 +WAIT 0.1 +LAFFMETER 4 15 +WAIT 0.1 +LAFFMETER 3 15 +WAIT 0.1 +LAFFMETER 2 15 +WAIT 0.1 +LAFFMETER 1 15 +WAIT 0.1 +LAFFMETER 0 15 +LOCAL_CHAT_CONFIRM npc QuestScript101_10 "CFReversed" +FUNCTION npc "normalEyes" +FUNCTION npc "blinkEyes" +LAFFMETER 15 15 +WRTREPARENTTO laffMeter bottomLeft +WAIT 0.5 +LERP_POS laffMeter 0.153 0.0 0.13 0.6 +LERP_SCALE laffMeter 0.075 0.075 0.075 0.6 +PLAY_ANIM npc "right-hand-start" -2 +WAIT 1.0625 +LOOP_ANIM npc "neutral" +WAIT 0.5 +LERP_HPR npc -50 0 0 0.5 +FUNCTION npc "surpriseEyes" +FUNCTION npc "showSurpriseMuzzle" +PLAY_ANIM npc "right-point-start" 1.5 +WAIT 0.6944 +LOOP_ANIM npc "right-point" +LOCAL_CHAT_CONFIRM npc QuestScript101_11 "CFReversed" +LOCAL_CHAT_CONFIRM npc QuestScript101_12 "CFReversed" +PLAY_ANIM npc "right-point-start" -1 +LERP_HPR npc -0.068 0 0 0.75 +WAIT 1.0417 +FUNCTION npc "angryEyes" +FUNCTION npc "blinkEyes" +FUNCTION npc "hideSurpriseMuzzle" +LOOP_ANIM npc "neutral" +FUNCTION localToon "questPage.showQuestsOnscreenTutorial" +LOCAL_CHAT_CONFIRM npc QuestScript101_13 "CFReversed" +FUNCTION localToon "questPage.hideQuestsOnscreenTutorial" +LOCAL_CHAT_CONFIRM npc QuestScript101_14 1 "CFReversed" +FUNCTION npc "normalEyes" +FUNCTION npc "blinkEyes" +UPON_TIMEOUT FUNCTION cogs "removeNode" +UPON_TIMEOUT FUNCTION toonBuilding "removeNode" +UPON_TIMEOUT FUNCTION cogBuilding "removeNode" +UPON_TIMEOUT FUNCTION squirt1 "removeNode" +UPON_TIMEOUT FUNCTION squirt2 "removeNode" +UPON_TIMEOUT LOOP_ANIM npc "neutral" +UPON_TIMEOUT SHOW laffMeter +UPON_TIMEOUT REPARENTTO laffMeter bottomLeft +UPON_TIMEOUT POS laffMeter 0.15 0 0.13 +UPON_TIMEOUT SCALE laffMeter 0.075 0.075 0.075 +POS localToon 0.776 14.6 0 +HPR localToon 47.5 0 0 +FINISH_QUEST_MOVIE + + +ID quest_incomplete_110 +LOCAL_CHAT_CONFIRM npc QuestScript110_1 +OBSCURE_BOOK 0 +REPARENTTO bookOpenButton aspect2d +SHOW bookOpenButton +POS bookOpenButton 0 0 0 +SCALE bookOpenButton 0.5 0.5 0.5 +LERP_COLOR_SCALE bookOpenButton 1 1 1 0 1 1 1 1 0.5 +WRTREPARENTTO bookOpenButton bottomRight +WAIT 1.5 +LERP_POS bookOpenButton -0.158 0 0.17 1 +LERP_SCALE bookOpenButton 0.305 0.305 0.305 1 +WAIT 1 +LOCAL_CHAT_CONFIRM npc QuestScript110_2 +REPARENTTO arrows bottomRight +ARROWS_ON -0.41 0.11 0 -0.11 0.36 90 +LOCAL_CHAT_PERSIST npc QuestScript110_3 +WAIT_EVENT "enterStickerBook" +ARROWS_OFF +SHOW_BOOK +HIDE bookPrevArrow +HIDE bookNextArrow +CLEAR_CHAT npc +WAIT 0.5 +TOON_HEAD npc -0.2 -0.45 1 +LOCAL_CHAT_CONFIRM npc QuestScript110_4 +REPARENTTO arrows aspect2d +ARROWS_ON 0.85 -0.75 -90 0.85 -0.75 -90 +SHOW bookNextArrow +LOCAL_CHAT_PERSIST npc QuestScript110_5 +WAIT_EVENT "stickerBookPageChange-3" +HIDE bookPrevArrow +HIDE bookNextArrow +ARROWS_OFF +CLEAR_CHAT npc +WAIT 0.5 +LOCAL_CHAT_CONFIRM npc QuestScript110_6 +ARROWS_ON 0.85 -0.75 -90 0.85 -0.75 -90 +SHOW bookNextArrow +LOCAL_CHAT_PERSIST npc QuestScript110_7 +WAIT_EVENT "stickerBookPageChange-4" +HIDE bookNextArrow +HIDE bookPrevArrow +ARROWS_OFF +CLEAR_CHAT npc +LOCAL_CHAT_CONFIRM npc QuestScript110_8 +LOCAL_CHAT_CONFIRM npc QuestScript110_9 +LOCAL_CHAT_PERSIST npc QuestScript110_10 +ENABLE_CLOSE_BOOK +REPARENTTO arrows bottomRight +ARROWS_ON -0.41 0.11 0 -0.11 0.36 90 +WAIT_EVENT "exitStickerBook" +ARROWS_OFF +TOON_HEAD npc 0 0 0 +HIDE_BOOK +HIDE bookOpenButton +LOCAL_CHAT_CONFIRM npc QuestScript110_11 1 +UPON_TIMEOUT OBSCURE_BOOK 0 +UPON_TIMEOUT ARROWS_OFF +UPON_TIMEOUT REPARENTTO arrows aspect2d +UPON_TIMEOUT HIDE_BOOK +UPON_TIMEOUT COLOR_SCALE bookOpenButton 1 1 1 1 +UPON_TIMEOUT POS bookOpenButton -0.158 0 0.17 +UPON_TIMEOUT SCALE bookOpenButton 0.305 0.305 0.305 +UPON_TIMEOUT TOON_HEAD npc 0 0 0 +UPON_TIMEOUT SHOW bookOpenButton +FINISH_QUEST_MOVIE + +ID tutorial_blocker +HIDE localToon +REPARENTTO camera npc +FUNCTION npc "stopLookAround" +POS camera 0.0 6.0 4.0 +HPR camera 180.0 0.0 0.0 +LOCAL_CHAT_CONFIRM npc QuestScriptTutorialBlocker_1 +WAIT 0.8 +LOCAL_CHAT_CONFIRM npc QuestScriptTutorialBlocker_2 +WAIT 0.8 +POS camera -5.0 -9.0 6.0 +HPR camera -25.0 -10.0 0.0 +POS localToon 203.8 18.64 -0.475 +HPR localToon -90.0 0.0 0.0 +SHOW localToon +LOCAL_CHAT_CONFIRM npc QuestScriptTutorialBlocker_3 +OBSCURE_CHAT 1 0 +REPARENTTO chatScButton aspect2d +SHOW chatScButton +POS chatScButton -0.3 0 -0.1 +SCALE chatScButton 2.0 2.0 2.0 +LERP_COLOR_SCALE chatScButton 1 1 1 0 1 1 1 1 0.5 +WRTREPARENTTO chatScButton topLeft +WAIT 0.5 +LERP_POS chatScButton 0.204 0 -0.072 0.6 +LERP_SCALE chatScButton 1.179 1.179 1.179 0.6 +WAIT 0.6 +REPARENTTO arrows topLeft +ARROWS_ON 0.41 -0.09 180 0.21 -0.26 -90 +LOCAL_CHAT_PERSIST npc QuestScriptTutorialBlocker_4 +WAIT_EVENT "enterSpeedChat" +ARROWS_OFF +BLACK_CAT_LISTEN 1 +WAIT_EVENT "SCChatEvent" +BLACK_CAT_LISTEN 0 +WAIT 0.5 +CLEAR_CHAT localToon +REPARENTTO camera localToon +LOCAL_CHAT_CONFIRM npc QuestScriptTutorialBlocker_5 "CFReversed" +LOCAL_CHAT_CONFIRM npc QuestScriptTutorialBlocker_6 "CFReversed" +OBSCURE_CHAT 0 0 +REPARENTTO chatNormalButton aspect2d +SHOW chatNormalButton +POS chatNormalButton -0.3 0 -0.1 +SCALE chatNormalButton 2.0 2.0 2.0 +LERP_COLOR_SCALE chatNormalButton 1 1 1 0 1 1 1 1 0.5 +WAIT 0.5 +WRTREPARENTTO chatNormalButton topLeft +LERP_POS chatNormalButton 0.068 0 -0.072 0.6 +LERP_SCALE chatNormalButton 1.179 1.179 1.179 0.6 +WAIT 0.6 +TUTORIAL_ACK_DONE +LOCAL_CHAT_CONFIRM npc QuestScriptTutorialBlocker_7 "CFReversed" +LOCAL_CHAT_CONFIRM npc QuestScriptTutorialBlocker_8 1 "CFReversed" +LOOP_ANIM npc "walk" +LERP_HPR npc 270 0 0 0.5 +WAIT 0.5 +LOOP_ANIM npc "run" +LERP_POS npc 217.4 18.81 -0.475 0.75 +LERP_HPR npc 240 0 0 0.75 +WAIT 0.75 +LERP_POS npc 222.4 15.0 -0.475 0.35 +LERP_HPR npc 180 0 0 0.35 +WAIT 0.35 +LERP_POS npc 222.4 5.0 -0.475 0.75 +WAIT 0.75 +REPARENTTO npc hidden +FREE_LOCALTOON +UPON_TIMEOUT ARROWS_OFF +UPON_TIMEOUT REPARENTTO arrows aspect2d +UPON_TIMEOUT POS chatScButton 0.204 0 -0.072 +UPON_TIMEOUT SCALE chatScButton 1.179 1.179 1.179 +UPON_TIMEOUT POS chatNormalButton 0.068 0 -0.072 +UPON_TIMEOUT SCALE chatNormalButton 1.179 1.179 1.179 +UPON_TIMEOUT OBSCURE_CHAT 0 0 +UPON_TIMEOUT REPARENTTO camera localToon +FINISH_QUEST_MOVIE + +ID gag_intro +SEND_EVENT "disableGagPanel" +SEND_EVENT "disableBackToPlayground" +HIDE inventory +TOON_HEAD npc 0 0 1 +WAIT 0.1 +LOCAL_CHAT_CONFIRM npc QuestScriptGagShop_1 +LERP_POS npcToonHead -0.64 0 -0.74 0.7 +LERP_SCALE npcToonHead 0.82 0.82 0.82 0.7 +LERP_COLOR_SCALE purchaseBg 1 1 1 1 0.6 0.6 0.6 1 0.7 +WAIT 0.7 +SHOW inventory +LOCAL_CHAT_CONFIRM npc QuestScriptGagShop_1a +ARROWS_ON -0.19 0.04 180 -0.4 0.26 90 +LOCAL_CHAT_PERSIST npc QuestScriptGagShop_3 +SEND_EVENT "enableGagPanel" +WAIT_EVENT "inventory-selection" +ARROWS_OFF +CLEAR_CHAT npc +WAIT 0.5 +LOCAL_CHAT_CONFIRM npc QuestScriptGagShop_4 +LOCAL_CHAT_PERSIST npc QuestScriptGagShop_5 +WAIT 0.5 +SHOW_PREVIEW +CLEAR_CHAT npc +WAIT 0.5 +SET_BIN backToPlaygroundButton "gui-popup" +LERP_POS backToPlaygroundButton -0.12 0 0.18 0.5 +LERP_SCALE backToPlaygroundButton 2 2 2 0.5 +LERP_COLOR_SCALE backToPlaygroundButton 1 1 1 1 2.78 2.78 2.78 1 0.5 +LERP_COLOR_SCALE inventory 1 1 1 1 0.6 0.6 0.6 1 0.5 +WAIT 0.5 +START_THROB backToPlaygroundButton 2.78 2.78 2.78 1 2.78 2.78 2.78 0.7 2 +LOCAL_CHAT_CONFIRM npc QuestScriptGagShop_6 +STOP_THROB +LERP_POS backToPlaygroundButton 0.72 0 -0.045 0.5 +LERP_SCALE backToPlaygroundButton 1.04 1.04 1.04 0.5 +LERP_COLOR_SCALE backToPlaygroundButton 2.78 2.78 2.78 1 1 1 1 1 0.5 +WAIT 0.5 +CLEAR_BIN backToPlaygroundButton +SET_BIN playAgainButton "gui-popup" +LERP_POS playAgainButton -0.12 0 0.18 0.5 +LERP_SCALE playAgainButton 2 2 2 0.5 +LERP_COLOR_SCALE playAgainButton 1 1 1 1 2.78 2.78 2.78 1 0.5 +WAIT 0.5 +START_THROB playAgainButton 2.78 2.78 2.78 1 2.78 2.78 2.78 0.7 2 +LOCAL_CHAT_CONFIRM npc QuestScriptGagShop_7 +STOP_THROB +LERP_POS playAgainButton 0.72 0 -0.24 0.5 +LERP_SCALE playAgainButton 1.04 1.04 1.04 0.5 +LERP_COLOR_SCALE playAgainButton 2.78 2.78 2.78 1 1 1 1 1 0.5 +WAIT 0.5 +CLEAR_BIN playAgainButton +LOCAL_CHAT_CONFIRM npc QuestScriptGagShop_8 1 +TOON_HEAD npc 0 0 0 +LERP_COLOR_SCALE inventory 0.6 0.6 0.6 1 1 1 1 1 0.5 +LERP_COLOR_SCALE purchaseBg 0.6 0.6 0.6 1 1 1 1 1 0.5 +WAIT 0.5 +SEND_EVENT "enableBackToPlayground" +UPON_TIMEOUT TOON_HEAD npc 0 0 0 +UPON_TIMEOUT ARROWS_OFF +UPON_TIMEOUT SHOW inventory +UPON_TIMEOUT SEND_EVENT "enableGagPanel" +UPON_TIMEOUT SEND_EVENT "enableBackToPlayground" + +ID quest_incomplete_145 +CHAT_CONFIRM npc QuestScript145_1 1 +LOAD frame "phase_4/models/gui/tfa_images" "FrameBlankA" +LOAD tunnel "phase_4/models/gui/tfa_images" "tunnelSignA" +POSHPRSCALE tunnel 0 0 0 0 0 0 0.8 0.8 0.8 +REPARENTTO tunnel frame +POSHPRSCALE frame 0 0 0 0 0 0 0.1 0.1 0.1 +REPARENTTO frame aspect2d +LERP_SCALE frame 1.0 1.0 1.0 1.0 +WAIT 3.0 +LERP_SCALE frame 0.1 0.1 0.1 0.5 +WAIT 0.5 +REPARENTTO frame hidden +CHAT_CONFIRM npc QuestScript145_2 1 +UPON_TIMEOUT FUNCTION frame "removeNode" +FINISH_QUEST_MOVIE + +ID quest_incomplete_150 +CHAT_CONFIRM npc QuestScript150_1 +ARROWS_ON 1.05 0.51 -120 1.05 0.51 -120 +SHOW_FRIENDS_LIST +CHAT_CONFIRM npc QuestScript150_2 +ARROWS_OFF +HIDE_FRIENDS_LIST +CHAT_CONFIRM npc QuestScript150_3 +HIDE bFriendsList +CHAT_CONFIRM npc QuestScript150_4 1 +UPON_TIMEOUT HIDE_FRIENDS_LIST +UPON_TIMEOUT ARROWS_OFF +FINISH_QUEST_MOVIE +''' \ No newline at end of file diff --git a/toontown/quest/Quests.py b/toontown/quest/Quests.py new file mode 100755 index 00000000..ce631be8 --- /dev/null +++ b/toontown/quest/Quests.py @@ -0,0 +1,4752 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import PythonUtil +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.battle import SuitBattleGlobals +from toontown.coghq import CogDisguiseGlobals +from toontown.toon import NPCToons +from toontown.hood import ZoneUtil +from otp.otpbase import OTPGlobals +import random +import copy +import string +import time +import types +import random + +notify = DirectNotifyGlobal.directNotify.newCategory('Quests') +ItemDict = TTLocalizer.QuestsItemDict +CompleteString = TTLocalizer.QuestsCompleteString +NotChosenString = TTLocalizer.QuestsNotChosenString +DefaultGreeting = TTLocalizer.QuestsDefaultGreeting +DefaultIncomplete = TTLocalizer.QuestsDefaultIncomplete +DefaultIncompleteProgress = TTLocalizer.QuestsDefaultIncompleteProgress +DefaultIncompleteWrongNPC = TTLocalizer.QuestsDefaultIncompleteWrongNPC +DefaultComplete = TTLocalizer.QuestsDefaultComplete +DefaultLeaving = TTLocalizer.QuestsDefaultLeaving +DefaultReject = TTLocalizer.QuestsDefaultReject +DefaultTierNotDone = TTLocalizer.QuestsDefaultTierNotDone +DefaultQuest = TTLocalizer.QuestsDefaultQuest +DefaultVisitQuestDialog = TTLocalizer.QuestsDefaultVisitQuestDialog +GREETING = 0 +QUEST = 1 +INCOMPLETE = 2 +INCOMPLETE_PROGRESS = 3 +INCOMPLETE_WRONG_NPC = 4 +COMPLETE = 5 +LEAVING = 6 +Any = 1 +OBSOLETE = 'OBSOLETE' +Start = 1 +Cont = 0 +Anywhere = 1 +NA = 2 +Same = 3 +AnyFish = 4 +AnyCashbotSuitPart = 5 +AnyLawbotSuitPart = 6 +AnyBossbotSuitPart = 7 +ToonTailor = 999 +ToonHQ = 1000 +InVP = 0 +InFO = 1 +WithCheat = 2 +QuestDictTierIndex = 0 +QuestDictStartIndex = 1 +QuestDictDescIndex = 2 +QuestDictFromNpcIndex = 3 +QuestDictToNpcIndex = 4 +QuestDictRewardIndex = 5 +QuestDictNextQuestIndex = 6 +QuestDictDialogIndex = 7 +VeryEasy = 100 +Easy = 75 +Medium = 50 +Hard = 25 +VeryHard = 20 +Fun = 15 +TT_TIER = 0 +DD_TIER = 4 +DG_TIER = 7 +MM_TIER = 8 +BR_TIER = 11 +DL_TIER = 14 +LAWBOT_HQ_TIER = 18 +BOSSBOT_HQ_TIER = 32 +PRE_ELDER_TIER = 49 +ELDER_TIER = 50 +LOOPING_FINAL_TIER = ELDER_TIER +VISIT_QUEST_ID = 1000 +TROLLEY_QUEST_ID = 110 +FIRST_COG_QUEST_ID = 145 +FRIEND_QUEST_ID = 150 +PHONE_QUEST_ID = 175 +from toontown.toonbase.ToontownGlobals import FT_FullSuit, FT_Leg, FT_Arm, FT_Torso +QuestRandGen = random.Random() + +def seedRandomGen(npcId, avId, tier, rewardHistory): + QuestRandGen.seed(npcId * 100 + avId + tier + len(rewardHistory)) + + +def seededRandomChoice(seq): + return QuestRandGen.choice(seq) + + +def getCompleteStatusWithNpc(questComplete, toNpcId, npc): + if questComplete: + if npc: + if npcMatches(toNpcId, npc): + return COMPLETE + else: + return INCOMPLETE_WRONG_NPC + else: + return COMPLETE + elif npc: + if npcMatches(toNpcId, npc): + return INCOMPLETE_PROGRESS + else: + return INCOMPLETE + else: + return INCOMPLETE + + +def npcMatches(toNpcId, npc): + return toNpcId == npc.getNpcId() or toNpcId == Any or toNpcId == ToonHQ and npc.getHq() or toNpcId == ToonTailor and npc.getTailor() + + +def calcRecoverChance(numberNotDone, chance, cap = 1): + avgNum2Kill = 1.0 / (chance / 100.0) + diff = float(numberNotDone - avgNum2Kill * 0.5) + luck = 1.0 + abs(diff / (avgNum2Kill * 0.5)) + chance *= luck + return chance + + +def simulateRecoveryVar(numNeeded, baseChance, list = 0, cap = 1): + numHave = 0 + numTries = 0 + greatestFailChain = 0 + currentFail = 0 + capHits = 0 + attemptList = {} + while numHave < numNeeded: + numTries += 1 + chance = calcRecoverChance(currentFail, baseChance, cap) + test = random.random() * 100 + if chance == 1000: + capHits += 1 + if test < chance: + numHave += 1 + if currentFail > greatestFailChain: + greatestFailChain = currentFail + if attemptList.get(currentFail): + attemptList[currentFail] += 1 + else: + attemptList[currentFail] = 1 + currentFail = 0 + else: + currentFail += 1 + + print 'Test results: %s tries, %s longest failure chain, %s cap hits' % (numTries, greatestFailChain, capHits) + if list: + print 'failures for each succes %s' % attemptList + + +def simulateRecoveryFix(numNeeded, baseChance, list = 0): + numHave = 0 + numTries = 0 + greatestFailChain = 0 + currentFail = 0 + attemptList = {} + while numHave < numNeeded: + numTries += 1 + chance = baseChance + test = random.random() * 100 + if test < chance: + numHave += 1 + if currentFail > greatestFailChain: + greatestFailChain = currentFail + if attemptList.get(currentFail): + attemptList[currentFail] += 1 + else: + attemptList[currentFail] = 1 + currentFail = 0 + else: + currentFail += 1 + + print 'Test results: %s tries, %s longest failure chain' % (numTries, greatestFailChain) + if list: + print 'failures for each success %s' % attemptList + + +class Quest: + _cogTracks = [Any, + 'c', + 'l', + 'm', + 's'] + _factoryTypes = [Any, + FT_FullSuit, + FT_Leg, + FT_Arm, + FT_Torso] + + def check(self, cond, msg): + pass + + def checkLocation(self, location): + locations = [Anywhere] + TTLocalizer.GlobalStreetNames.keys() + self.check(location in locations, 'invalid location: %s' % location) + + def checkNumCogs(self, num): + self.check(1, 'invalid number of cogs: %s' % num) + + def checkCogType(self, type): + types = [Any] + SuitBattleGlobals.SuitAttributes.keys() + self.check(type in types, 'invalid cog type: %s' % type) + + def checkCogTrack(self, track): + self.check(track in self._cogTracks, 'invalid cog track: %s' % track) + + def checkCogLevel(self, level): + self.check(level >= 1 and level <= 12, 'invalid cog level: %s' % level) + + def checkNumSkelecogs(self, num): + self.check(1, 'invalid number of cogs: %s' % num) + + def checkSkelecogTrack(self, track): + self.check(track in self._cogTracks, 'invalid cog track: %s' % track) + + def checkSkelecogLevel(self, level): + self.check(level >= 1 and level <= 12, 'invalid cog level: %s' % level) + + def checkNumSkeleRevives(self, num): + self.check(1, 'invalid number of cogs: %s' % num) + + def checkNumForemen(self, num): + self.check(num > 0, 'invalid number of foremen: %s' % num) + + def checkNumBosses(self, num): + self.check(num > 0, 'invalid number of bosses: %s' % num) + + def checkNumSupervisors(self, num): + self.check(num > 0, 'invalid number of supervisors: %s' % num) + + def checkNumBuildings(self, num): + self.check(1, 'invalid num buildings: %s' % num) + + def checkBuildingTrack(self, track): + self.check(track in self._cogTracks, 'invalid building track: %s' % track) + + def checkBuildingFloors(self, floors): + self.check(floors >= 1 and floors <= 5, 'invalid num floors: %s' % floors) + + def checkBuildingType(self, cogdo): + self.check(cogdo != 0 and cogdo != 1, 'invalid cogdo value: %s' % cogdo) + + def checkNumFactories(self, num): + self.check(1, 'invalid num factories: %s' % num) + + def checkFactoryType(self, type): + self.check(type in self._factoryTypes, 'invalid factory type: %s' % type) + + def checkNumMints(self, num): + self.check(1, 'invalid num mints: %s' % num) + + def checkNumCogParts(self, num): + self.check(1, 'invalid num cog parts: %s' % num) + + def checkNumGags(self, num): + self.check(1, 'invalid num gags: %s' % num) + + def checkGagTrack(self, track): + self.check(track >= ToontownBattleGlobals.MIN_TRACK_INDEX and track <= ToontownBattleGlobals.MAX_TRACK_INDEX, 'invalid gag track: %s' % track) + + def checkGagItem(self, item): + self.check(item >= ToontownBattleGlobals.MIN_LEVEL_INDEX and item <= ToontownBattleGlobals.MAX_LEVEL_INDEX, 'invalid gag item: %s' % item) + + def checkDeliveryItem(self, item): + self.check(item in ItemDict, 'invalid delivery item: %s' % item) + + def checkNumItems(self, num): + self.check(1, 'invalid num items: %s' % num) + + def checkRecoveryItem(self, item): + self.check(item in ItemDict, 'invalid recovery item: %s' % item) + + def checkPercentChance(self, chance): + self.check(chance > 0 and chance <= 100, 'invalid percent chance: %s' % chance) + + def checkRecoveryItemHolderAndType(self, holder, holderType = 'type'): + holderTypes = ['type', 'level', 'track'] + self.check(holderType in holderTypes, 'invalid recovery item holderType: %s' % holderType) + if holderType == 'type': + holders = [Any, AnyFish] + SuitBattleGlobals.SuitAttributes.keys() + self.check(holder in holders, 'invalid recovery item holder: %s for holderType: %s' % (holder, holderType)) + elif holderType == 'level': + pass + elif holderType == 'track': + self.check(holder in self._cogTracks, 'invalid recovery item holder: %s for holderType: %s' % (holder, holderType)) + + def checkTrackChoice(self, option): + self.check(option >= ToontownBattleGlobals.MIN_TRACK_INDEX and option <= ToontownBattleGlobals.MAX_TRACK_INDEX, 'invalid track option: %s' % option) + + def checkNumFriends(self, num): + self.check(1, 'invalid number of friends: %s' % num) + + def checkNumMinigames(self, num): + self.check(1, 'invalid number of minigames: %s' % num) + + def filterFunc(avatar): + return 1 + + filterFunc = staticmethod(filterFunc) + + def __init__(self, id, quest): + self.id = id + self.quest = quest + + def getId(self): + return self.id + + def getType(self): + return self.__class__ + + def getObjectiveStrings(self): + return [''] + + def getString(self): + return self.getObjectiveStrings()[0] + + def getRewardString(self, progressString): + return self.getString() + ' : ' + progressString + + def getChooseString(self): + return self.getString() + + def getPosterString(self): + return self.getString() + + def getHeadlineString(self): + return self.getString() + + def getDefaultQuestDialog(self): + return self.getString() + TTLocalizer.Period + + def getNumQuestItems(self): + return -1 + + def addArticle(self, num, oString): + if len(oString) == 0: + return oString + if num == 1: + return oString + else: + return '%d %s' % (num, oString) + + def __repr__(self): + return 'Quest type: %s id: %s params: %s' % (self.__class__.__name__, self.id, self.quest[0:]) + + def doesCogCount(self, avId, cogDict, zoneId): + return 0 + + def doesFactoryCount(self, avId, location): + return 0 + + def doesMintCount(self, avId, location): + return 0 + + def doesCogPartCount(self, avId, location): + return 0 + + def getCompletionStatus(self, av, questDesc, npc = None): + notify.error('Pure virtual - please override me') + return None + + +class LocationBasedQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + self.checkLocation(self.quest[0]) + + def getLocation(self): + return self.quest[0] + + def getLocationName(self): + loc = self.getLocation() + if loc == Anywhere: + locName = '' + elif loc in ToontownGlobals.hoodNameMap: + locName = TTLocalizer.QuestInLocationString % {'inPhrase': ToontownGlobals.hoodNameMap[loc][1], + 'location': ToontownGlobals.hoodNameMap[loc][-1] + TTLocalizer.QuestsLocationArticle} + elif loc in ToontownGlobals.StreetBranchZones: + locName = TTLocalizer.QuestInLocationString % {'inPhrase': ToontownGlobals.StreetNames[loc][1], + 'location': ToontownGlobals.StreetNames[loc][-1] + TTLocalizer.QuestsLocationArticle} + return locName + + def isLocationMatch(self, zoneId): + loc = self.getLocation() + if loc is Anywhere: + return 1 + if ZoneUtil.isPlayground(loc): + if loc == ZoneUtil.getCanonicalHoodId(zoneId): + return 1 + else: + return 0 + elif loc == ZoneUtil.getCanonicalBranchZone(zoneId): + return 1 + elif loc == zoneId: + return 1 + else: + return 0 + + def getChooseString(self): + return TTLocalizer.QuestsLocationString % {'string': self.getString(), + 'location': self.getLocationName()} + + def getPosterString(self): + return TTLocalizer.QuestsLocationString % {'string': self.getString(), + 'location': self.getLocationName()} + + def getDefaultQuestDialog(self): + return (TTLocalizer.QuestsLocationString + TTLocalizer.Period) % {'string': self.getString(), + 'location': self.getLocationName()} + +class CogQuest(LocationBasedQuest): + def __init__(self, id, quest): + LocationBasedQuest.__init__(self, id, quest) + if self.__class__ == CogQuest: + self.checkNumCogs(self.quest[1]) + self.checkCogType(self.quest[2]) + + def getCogType(self): + return self.quest[2] + + def getNumQuestItems(self): + return self.getNumCogs() + + def getNumCogs(self): + return self.quest[1] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= self.getNumCogs() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumCogs() == 1: + return '' + else: + return TTLocalizer.QuestsCogQuestProgress % {'progress': questDesc[4], + 'numCogs': self.getNumCogs()} + + def getCogNameString(self): + numCogs = self.getNumCogs() + cogType = self.getCogType() + if numCogs == 1: + if cogType == Any: + return TTLocalizer.Cog + else: + return SuitBattleGlobals.SuitAttributes[cogType]['singularname'] + elif cogType == Any: + return TTLocalizer.Cogs + else: + return SuitBattleGlobals.SuitAttributes[cogType]['pluralname'] + + def getObjectiveStrings(self): + cogName = self.getCogNameString() + numCogs = self.getNumCogs() + if numCogs == 1: + text = cogName + else: + text = TTLocalizer.QuestsCogQuestDefeatDesc % {'numCogs': numCogs, + 'cogName': cogName} + return (text,) + + def getString(self): + return TTLocalizer.QuestsCogQuestDefeat % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumCogs(): + return getFinishToonTaskSCStrings(toNpcId) + cogName = self.getCogNameString() + numCogs = self.getNumCogs() + if numCogs == 1: + text = TTLocalizer.QuestsCogQuestSCStringS + else: + text = TTLocalizer.QuestsCogQuestSCStringP + cogLoc = self.getLocationName() + return text % {'cogName': cogName, + 'cogLoc': cogLoc} + + def getHeadlineString(self): + return TTLocalizer.QuestsCogQuestHeadline + + def doesCogCount(self, avId, cogDict, zoneId): + questCogType = self.getCogType() + return (questCogType is Any or questCogType is cogDict['type']) and avId in cogDict['activeToons'] and self.isLocationMatch(zoneId) + + +class CogTrackQuest(CogQuest): + trackCodes = ['c', + 'l', + 'm', + 's'] + trackNamesS = [TTLocalizer.BossbotS, + TTLocalizer.LawbotS, + TTLocalizer.CashbotS, + TTLocalizer.SellbotS] + trackNamesP = [TTLocalizer.BossbotP, + TTLocalizer.LawbotP, + TTLocalizer.CashbotP, + TTLocalizer.SellbotP] + + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + if self.__class__ == CogTrackQuest: + self.checkNumCogs(self.quest[1]) + self.checkCogTrack(self.quest[2]) + + def getCogTrack(self): + return self.quest[2] + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumCogs() == 1: + return '' + else: + return TTLocalizer.QuestsCogTrackQuestProgress % {'progress': questDesc[4], + 'numCogs': self.getNumCogs()} + + def getObjectiveStrings(self): + numCogs = self.getNumCogs() + track = self.trackCodes.index(self.getCogTrack()) + if numCogs == 1: + text = self.trackNamesS[track] + else: + text = TTLocalizer.QuestsCogTrackDefeatDesc % {'numCogs': numCogs, + 'trackName': self.trackNamesP[track]} + return (text,) + + def getString(self): + return TTLocalizer.QuestsCogTrackQuestDefeat % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumCogs(): + return getFinishToonTaskSCStrings(toNpcId) + numCogs = self.getNumCogs() + track = self.trackCodes.index(self.getCogTrack()) + if numCogs == 1: + cogText = self.trackNamesS[track] + text = TTLocalizer.QuestsCogTrackQuestSCStringS + else: + cogText = self.trackNamesP[track] + text = TTLocalizer.QuestsCogTrackQuestSCStringP + cogLocName = self.getLocationName() + return text % {'cogText': cogText, + 'cogLoc': cogLocName} + + def getHeadlineString(self): + return TTLocalizer.QuestsCogTrackQuestHeadline + + def doesCogCount(self, avId, cogDict, zoneId): + questCogTrack = self.getCogTrack() + return questCogTrack == cogDict['track'] and avId in cogDict['activeToons'] and self.isLocationMatch(zoneId) + + +class CogLevelQuest(CogQuest): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumCogs(self.quest[1]) + self.checkCogLevel(self.quest[2]) + + def getCogType(self): + return Any + + def getCogLevel(self): + return self.quest[2] + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumCogs() == 1: + return '' + else: + return TTLocalizer.QuestsCogLevelQuestProgress % {'progress': questDesc[4], + 'numCogs': self.getNumCogs()} + + def getObjectiveStrings(self): + count = self.getNumCogs() + level = self.getCogLevel() + name = self.getCogNameString() + if count == 1: + text = TTLocalizer.QuestsCogLevelQuestDesc + else: + text = TTLocalizer.QuestsCogLevelQuestDescC + return (text % {'count': count, + 'level': level, + 'name': name},) + + def getString(self): + return TTLocalizer.QuestsCogLevelQuestDefeat % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumCogs(): + return getFinishToonTaskSCStrings(toNpcId) + count = self.getNumCogs() + level = self.getCogLevel() + name = self.getCogNameString() + if count == 1: + text = TTLocalizer.QuestsCogLevelQuestDesc + else: + text = TTLocalizer.QuestsCogLevelQuestDescI + objective = text % {'level': level, + 'name': name} + location = self.getLocationName() + return TTLocalizer.QuestsCogLevelQuestSCString % {'objective': objective, + 'location': location} + + def getHeadlineString(self): + return TTLocalizer.QuestsCogLevelQuestHeadline + + def doesCogCount(self, avId, cogDict, zoneId): + questCogLevel = self.getCogLevel() + return questCogLevel <= cogDict['level'] and avId in cogDict['activeToons'] and self.isLocationMatch(zoneId) + + +class SkelecogQBase: + def getCogNameString(self): + numCogs = self.getNumCogs() + if numCogs == 1: + return TTLocalizer.ASkeleton + else: + return TTLocalizer.SkeletonP + + def doesCogCount(self, avId, cogDict, zoneId): + return cogDict['isSkelecog'] and avId in cogDict['activeToons'] and self.isLocationMatch(zoneId) + + +class SkelecogQuest(CogQuest, SkelecogQBase): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumSkelecogs(self.quest[1]) + + def getCogType(self): + return Any + + def getCogNameString(self): + return SkelecogQBase.getCogNameString(self) + + def doesCogCount(self, avId, cogDict, zoneId): + return SkelecogQBase.doesCogCount(self, avId, cogDict, zoneId) + +class SkelecogTrackQuest(CogTrackQuest, SkelecogQBase): + trackNamesS = [TTLocalizer.BossbotSkelS, + TTLocalizer.LawbotSkelS, + TTLocalizer.CashbotSkelS, + TTLocalizer.SellbotSkelS] + trackNamesP = [TTLocalizer.BossbotSkelP, + TTLocalizer.LawbotSkelP, + TTLocalizer.CashbotSkelP, + TTLocalizer.SellbotSkelP] + + def __init__(self, id, quest): + CogTrackQuest.__init__(self, id, quest) + self.checkNumSkelecogs(self.quest[1]) + self.checkSkelecogTrack(self.quest[2]) + + def getCogNameString(self): + return SkelecogQBase.getCogNameString(self) + + def doesCogCount(self, avId, cogDict, zoneId): + return SkelecogQBase.doesCogCount(self, avId, cogDict, zoneId) and self.getCogTrack() == cogDict['track'] + + +class SkelecogLevelQuest(CogLevelQuest, SkelecogQBase): + def __init__(self, id, quest): + CogLevelQuest.__init__(self, id, quest) + self.checkNumSkelecogs(self.quest[1]) + self.checkSkelecogLevel(self.quest[2]) + + def getCogType(self): + return Any + + def getCogNameString(self): + return SkelecogQBase.getCogNameString(self) + + def doesCogCount(self, avId, cogDict, zoneId): + return SkelecogQBase.doesCogCount(self, avId, cogDict, zoneId) and self.getCogLevel() <= cogDict['level'] + + +class SkeleReviveQBase: + def getCogNameString(self): + numCogs = self.getNumCogs() + if numCogs == 1: + return TTLocalizer.Av2Cog + else: + return TTLocalizer.v2CogP + + def doesCogCount(self, avId, cogDict, zoneId): + return cogDict['hasRevives'] and avId in cogDict['activeToons'] and self.isLocationMatch(zoneId) + + +class SkeleReviveQuest(CogQuest, SkeleReviveQBase): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumSkeleRevives(self.quest[1]) + + def getCogType(self): + return Any + + def getCogNameString(self): + return SkeleReviveQBase.getCogNameString(self) + + def doesCogCount(self, avId, cogDict, zoneId): + return SkeleReviveQBase.doesCogCount(self, avId, cogDict, zoneId) + + +class ForemanQuest(CogQuest): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumForemen(self.quest[1]) + + def getCogType(self): + return Any + + def getCogNameString(self): + numCogs = self.getNumCogs() + if numCogs == 1: + return TTLocalizer.AForeman + else: + return TTLocalizer.ForemanP + + def doesCogCount(self, avId, cogDict, zoneId): + return bool(CogQuest.doesCogCount(self, avId, cogDict, zoneId) and cogDict['isForeman']) + +BOSS_NAMES = { + Anywhere: [TTLocalizer.ACogBoss, TTLocalizer.CogBosses, 'phase_3.5/maps/boss_icon.jpg', 'blue'], + ToontownGlobals.SellbotHQ: [TTLocalizer.ACogVP, TTLocalizer.CogVPs, 'phase_3.5/maps/vp_icon.jpg', 'red'], + ToontownGlobals.CashbotHQ: [TTLocalizer.ACogCFO, TTLocalizer.CogCFOs, 'phase_3.5/maps/cfo_icon.jpg', 'green'], + ToontownGlobals.LawbotHQ: [TTLocalizer.ACogCJ, TTLocalizer.CogCJs, 'phase_3.5/maps/cj_icon.jpg', 'blue'], + ToontownGlobals.BossbotHQ: [TTLocalizer.ACogCEO, TTLocalizer.CogCEOs, 'phase_3.5/maps/ceo_icon.jpg', 'brown'] +} + +class TexturedQuest: + def getModelFromTexture(self, texture): + cardMaker = CardMaker('cm-%s' % time.time()) + cardMaker.setFrame(-0.5, 0.5, -0.5, 0.5) + node = NodePath(cardMaker.generate()) + + node.setTexture(loader.loadTexture(texture)) + return node + + def hasFrame(self): + return True + + def getFrame(self): + print 'getFrame from TexturedQuest not implemented!' + return [None, None] + +class BossQuest(CogQuest, TexturedQuest): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumBosses(self.quest[1]) + + def getFrame(self): + boss = BOSS_NAMES[self.quest[0]] + + return [self.getModelFromTexture(boss[2]), boss[3]] + + def getCogType(self): + return Any + + def getCogNameString(self): + return BOSS_NAMES[self.quest[0]][self.getNumCogs() > 1] + + def doesCogCount(self, avId, cogDict, zoneId): + return cogDict['isBoss'] > 0 and self.isLocationMatch(zoneId) + +class SupervisorQuest(CogQuest): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumSupervisors(self.quest[1]) + + def getCogType(self): + return Any + + def getCogNameString(self): + numCogs = self.getNumCogs() + if numCogs == 1: + return TTLocalizer.ASupervisor + else: + return TTLocalizer.SupervisorP + + def doesCogCount(self, avId, cogDict, zoneId): + return bool(CogQuest.doesCogCount(self, avId, cogDict, zoneId) and cogDict['isSupervisor']) + +class RescueQuest(LocationBasedQuest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + self.checkNumCogs(self.quest[1]) + + def getNumToons(self): + return self.quest[1] + + def getNumQuestItems(self): + return self.getNumToons() + + def getLocationName(self): + return ' ' + TTLocalizer.InVP if self.quest[0] == InVP else TTLocalizer.InFieldOffice + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= self.getNumToons() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumToons() == 1: + return '' + else: + return TTLocalizer.QuestsRescueQuestProgress % {'progress': questDesc[4], + 'numToons': self.getNumToons()} + + def getObjectiveStrings(self): + numToons = self.getNumToons() + if numToons == 1: + text = TTLocalizer.QuestsRescueQuestToonS + else: + text = TTLocalizer.QuestsRescueQuestRescueDesc % {'numToons': numToons} + return (text,) + + def getString(self): + return TTLocalizer.QuestsRescueQuestRescue % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + numToons = self.getNumToons() + if progress >= numToons: + return getFinishToonTaskSCStrings(toNpcId) + if numToons == 1: + text = TTLocalizer.QuestsRescueQuestSCStringS + else: + text = TTLocalizer.QuestsRescueQuestSCStringP + return text % {'toonLoc': self.getLocationName().strip()} + + def getHeadlineString(self): + return TTLocalizer.QuestsRescueQuestHeadline + + def isMethodMatch(self, method): + return self.quest[0] == method + +BUILDING_NAMES = { + Any: ['phase_3.5/maps/cogdo_icon.jpg', 'brown'], + 'l': ['phase_3.5/maps/lawbot_cogdo_icon.jpg', 'blue'], + 's': ['phase_3.5/maps/sellbot_cogdo_icon.jpg', 'red'] +} + +class BuildingQuest(CogQuest, TexturedQuest): + trackCodes = ['c', + 'l', + 'm', + 's'] + trackNames = [TTLocalizer.Bossbot, + TTLocalizer.Lawbot, + TTLocalizer.Cashbot, + TTLocalizer.Sellbot] + + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumBuildings(self.quest[1]) + self.checkBuildingTrack(self.quest[2]) + self.checkBuildingFloors(self.quest[3]) + self.checkBuildingType(self.quest[4]) + + def hasFrame(self): + return self.isCogdo() + + def getFrame(self): + building = BUILDING_NAMES[self.quest[2]] + + return [self.getModelFromTexture(building[0]), building[1]] + + def getNumFloors(self): + return self.quest[3] + + def getBuildingTrack(self): + return self.quest[2] + + def isCogdo(self): + return self.quest[4] + + def getNumQuestItems(self): + return self.getNumBuildings() + + def getNumBuildings(self): + return self.quest[1] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= self.getNumBuildings() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumBuildings() == 1: + return '' + else: + return TTLocalizer.QuestsBuildingQuestProgressString % {'progress': questDesc[4], + 'num': self.getNumBuildings()} + + def getObjectiveStrings(self): + count = self.getNumBuildings() + buildingTrack = self.getBuildingTrack() + if buildingTrack == Any: + type = TTLocalizer.Cog + else: + type = self.trackNames[self.trackCodes.index(buildingTrack)] + floors = '' + + if self.isCogdo(): + if buildingTrack == Any: + text = TTLocalizer.QuestsCogdoQuestDescU if count == 1 else TTLocalizer.QuestsCogdoQuestDescUM + else: + text = TTLocalizer.QuestsCogdoQuestDesc if count == 1 else TTLocalizer.QuestsCogdoQuestDescM + else: + floors = TTLocalizer.QuestsBuildingQuestFloorNumbers[self.getNumFloors() - 1] + + if count == 1: + text = TTLocalizer.QuestsBuildingQuestDesc if floors == '' else TTLocalizer.QuestsBuildingQuestDescF + else: + text = TTLocalizer.QuestsBuildingQuestDescC if floors == '' else TTLocalizer.QuestsBuildingQuestDescCF + + return (text % {'count': count, + 'floors': floors, + 'type': type},) + + def getString(self): + return TTLocalizer.QuestsBuildingQuestString % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumBuildings(): + return getFinishToonTaskSCStrings(toNpcId) + count = self.getNumBuildings() + buildingTrack = self.getBuildingTrack() + floors = '' + + if buildingTrack == Any: + type = TTLocalizer.Cog + else: + type = self.trackNames[self.trackCodes.index(buildingTrack)] + + if self.isCogdo(): + if buildingTrack == Any: + text = TTLocalizer.QuestsCogdoQuestDescU if count == 1 else TTLocalizer.QuestsCogdoQuestDescMI + else: + text = TTLocalizer.QuestsCogdoQuestDesc if count == 1 else TTLocalizer.QuestsCogdoQuestDescMUI + else: + floors = TTLocalizer.QuestsBuildingQuestFloorNumbers[self.getNumFloors() - 1] + + if count == 1: + text = TTLocalizer.QuestsBuildingQuestDesc if floors == '' else TTLocalizer.QuestsBuildingQuestDescF + else: + text = TTLocalizer.QuestsBuildingQuestDescI if floors == '' else TTLocalizer.QuestsBuildingQuestDescIF + objective = text % {'floors': floors, + 'type': type} + return TTLocalizer.QuestsBuildingQuestSCString % {'objective': objective, + 'location': self.getLocationName()} + + def getHeadlineString(self): + return TTLocalizer.QuestsBuildingQuestHeadline + + def doesCogCount(self, avId, cogDict, zoneId): + return 0 + + def doesBuildingTypeCount(self, type): + buildingTrack = self.getBuildingTrack() + if buildingTrack == Any or buildingTrack == type: + return True + return False + +class FactoryQuest(LocationBasedQuest): + factoryTypeNames = {FT_FullSuit: TTLocalizer.Cog, + FT_Leg: TTLocalizer.FactoryTypeLeg, + FT_Arm: TTLocalizer.FactoryTypeArm, + FT_Torso: TTLocalizer.FactoryTypeTorso} + + def __init__(self, id, quest): + LocationBasedQuest.__init__(self, id, quest) + self.checkNumFactories(self.quest[1]) + + def getNumQuestItems(self): + return self.getNumFactories() + + def getNumFactories(self): + return self.quest[1] + + def getFactoryType(self): + loc = self.getLocation() + type = Any + if loc in ToontownGlobals.factoryId2factoryType: + type = ToontownGlobals.factoryId2factoryType[loc] + return type + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= self.getNumFactories() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumFactories() == 1: + return '' + else: + return TTLocalizer.QuestsFactoryQuestProgressString % {'progress': questDesc[4], + 'num': self.getNumFactories()} + + def getObjectiveStrings(self): + count = self.getNumFactories() + factoryType = self.getFactoryType() + if factoryType == Any: + type = TTLocalizer.Cog + else: + type = FactoryQuest.factoryTypeNames[factoryType] + if count == 1: + text = TTLocalizer.QuestsFactoryQuestDesc + else: + text = TTLocalizer.QuestsFactoryQuestDescC + return (text % {'count': count, + 'type': type},) + + def getString(self): + return TTLocalizer.QuestsFactoryQuestString % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumFactories(): + return getFinishToonTaskSCStrings(toNpcId) + factoryType = self.getFactoryType() + if factoryType == Any: + type = TTLocalizer.Cog + else: + type = FactoryQuest.factoryTypeNames[factoryType] + count = self.getNumFactories() + if count == 1: + text = TTLocalizer.QuestsFactoryQuestDesc + else: + text = TTLocalizer.QuestsFactoryQuestDescI + objective = text % {'type': type} + location = self.getLocationName() + return TTLocalizer.QuestsFactoryQuestSCString % {'objective': objective, + 'location': location} + + def getHeadlineString(self): + return TTLocalizer.QuestsFactoryQuestHeadline + + def doesFactoryCount(self, avId, location): + return self.isLocationMatch(location) + +class MintQuest(LocationBasedQuest): + def __init__(self, id, quest): + LocationBasedQuest.__init__(self, id, quest) + self.checkNumMints(self.quest[1]) + + def getNumQuestItems(self): + return self.getNumMints() + + def getNumMints(self): + return self.quest[1] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= self.getNumMints() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumMints() == 1: + return '' + else: + return TTLocalizer.QuestsMintQuestProgressString % {'progress': questDesc[4], + 'num': self.getNumMints()} + + def getObjectiveStrings(self): + count = self.getNumMints() + if count == 1: + text = TTLocalizer.QuestsMintQuestDesc + else: + text = TTLocalizer.QuestsMintQuestDescC % {'count': count} + return (text,) + + def getString(self): + return TTLocalizer.QuestsMintQuestString % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumMints(): + return getFinishToonTaskSCStrings(toNpcId) + count = self.getNumMints() + if count == 1: + objective = TTLocalizer.QuestsMintQuestDesc + else: + objective = TTLocalizer.QuestsMintQuestDescI + location = self.getLocationName() + return TTLocalizer.QuestsMintQuestSCString % {'objective': objective, + 'location': location} + + def getHeadlineString(self): + return TTLocalizer.QuestsMintQuestHeadline + + def doesMintCount(self, avId, location): + return self.isLocationMatch(location) + +class CogPartQuest(LocationBasedQuest): + def __init__(self, id, quest): + LocationBasedQuest.__init__(self, id, quest) + self.checkNumCogParts(self.quest[1]) + + def getNumQuestItems(self): + return self.getNumParts() + + def getNumParts(self): + return self.quest[1] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= self.getNumParts() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumParts() == 1: + return '' + else: + return TTLocalizer.QuestsCogPartQuestProgressString % {'progress': questDesc[4], + 'num': self.getNumParts()} + + def getObjectiveStrings(self): + count = self.getNumParts() + if count == 1: + text = TTLocalizer.QuestsCogPartQuestDesc + else: + text = TTLocalizer.QuestsCogPartQuestDescC + return (text % {'count': count},) + + def getString(self): + return TTLocalizer.QuestsCogPartQuestString % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumParts(): + return getFinishToonTaskSCStrings(toNpcId) + count = self.getNumParts() + if count == 1: + text = TTLocalizer.QuestsCogPartQuestDesc + else: + text = TTLocalizer.QuestsCogPartQuestDescI + objective = text + location = self.getLocationName() + return TTLocalizer.QuestsCogPartQuestSCString % {'objective': objective, + 'location': location} + + def getHeadlineString(self): + return TTLocalizer.QuestsCogPartQuestHeadline + + def doesCogPartCount(self, avId, location): + return self.isLocationMatch(location) + +class DeliverGagQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + self.checkNumGags(self.quest[0]) + self.checkGagTrack(self.quest[1]) + self.checkGagItem(self.quest[2]) + + def getGagType(self): + return (self.quest[1], self.quest[2]) + + def getNumQuestItems(self): + return self.getNumGags() + + def getNumGags(self): + return self.quest[0] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + gag = self.getGagType() + num = self.getNumGags() + track = gag[0] + level = gag[1] + questComplete = npc and av.inventory and av.inventory.numItem(track, level) >= num + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumGags() == 1: + return '' + else: + return TTLocalizer.QuestsDeliverGagQuestProgress % {'progress': questDesc[4], + 'numGags': self.getNumGags()} + + def getObjectiveStrings(self): + track, item = self.getGagType() + num = self.getNumGags() + if num == 1: + text = ToontownBattleGlobals.AvPropStringsSingular[track][item] + else: + gagName = ToontownBattleGlobals.AvPropStringsPlural[track][item] + text = TTLocalizer.QuestsItemNameAndNum % {'num': TTLocalizer.getLocalNum(num), + 'name': gagName} + return (text,) + + def getString(self): + return TTLocalizer.QuestsDeliverGagQuestString % self.getObjectiveStrings()[0] + + def getRewardString(self, progress): + return TTLocalizer.QuestsDeliverGagQuestStringLong % self.getObjectiveStrings()[0] + + def getDefaultQuestDialog(self): + return TTLocalizer.QuestsDeliverGagQuestStringLong % self.getObjectiveStrings()[0] + '\x07' + TTLocalizer.QuestsDeliverGagQuestInstructions + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumGags(): + return getFinishToonTaskSCStrings(toNpcId) + track, item = self.getGagType() + num = self.getNumGags() + if num == 1: + text = TTLocalizer.QuestsDeliverGagQuestToSCStringS + gagName = ToontownBattleGlobals.AvPropStringsSingular[track][item] + else: + text = TTLocalizer.QuestsDeliverGagQuestToSCStringP + gagName = ToontownBattleGlobals.AvPropStringsPlural[track][item] + return [text % {'gagName': gagName}, TTLocalizer.QuestsDeliverGagQuestSCString] + getVisitSCStrings(toNpcId) + + def getHeadlineString(self): + return TTLocalizer.QuestsDeliverGagQuestHeadline + + def removeGags(self, av): + gag = self.getGagType() + inventory = av.inventory + takenGags = 0 + for i in xrange(self.getNumGags()): + if inventory.useItem(gag[0], gag[1]): + takenGags += 1 + av.b_setInventory(inventory.makeNetString()) + return takenGags + +class DeliverItemQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + self.checkDeliveryItem(self.quest[0]) + + def getItem(self): + return self.quest[0] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + if npc and npcMatches(toNpcId, npc): + return COMPLETE + else: + return INCOMPLETE_WRONG_NPC + + def getProgressString(self, avatar, questDesc): + return TTLocalizer.QuestsDeliverItemQuestProgress + + def getObjectiveStrings(self): + iDict = ItemDict[self.getItem()] + article = iDict[2] + itemName = iDict[0] + return [article + itemName] + + def getString(self): + return TTLocalizer.QuestsDeliverItemQuestString % self.getObjectiveStrings()[0] + + def getRewardString(self, progress): + return TTLocalizer.QuestsDeliverItemQuestStringLong % self.getObjectiveStrings()[0] + + def getDefaultQuestDialog(self): + return TTLocalizer.QuestsDeliverItemQuestStringLong % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + iDict = ItemDict[self.getItem()] + article = iDict[2] + itemName = iDict[0] + return [TTLocalizer.QuestsDeliverItemQuestSCString % {'article': article, + 'itemName': itemName}] + getVisitSCStrings(toNpcId) + + def getHeadlineString(self): + return TTLocalizer.QuestsDeliverItemQuestHeadline + + +class VisitQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + if npc and npcMatches(toNpcId, npc): + return COMPLETE + else: + return INCOMPLETE_WRONG_NPC + + def getProgressString(self, avatar, questDesc): + return TTLocalizer.QuestsVisitQuestProgress + + def getObjectiveStrings(self): + return [''] + + def getString(self): + return TTLocalizer.QuestsVisitQuestStringShort + + def getChooseString(self): + return TTLocalizer.QuestsVisitQuestStringLong + + def getRewardString(self, progress): + return TTLocalizer.QuestsVisitQuestStringLong + + def getDefaultQuestDialog(self): + return random.choice(DefaultVisitQuestDialog) + + def getSCStrings(self, toNpcId, progress): + return getVisitSCStrings(toNpcId) + + def getHeadlineString(self): + return TTLocalizer.QuestsVisitQuestHeadline + + +class RecoverItemQuest(LocationBasedQuest): + def __init__(self, id, quest): + LocationBasedQuest.__init__(self, id, quest) + self.checkNumItems(self.quest[1]) + self.checkRecoveryItem(self.quest[2]) + self.checkPercentChance(self.quest[3]) + if len(self.quest) > 5: + self.checkRecoveryItemHolderAndType(self.quest[4], self.quest[5]) + else: + self.checkRecoveryItemHolderAndType(self.quest[4]) + + def testRecover(self, progress): + test = random.random() * 100 + chance = self.getPercentChance() + numberDone = progress & pow(2, 16) - 1 + numberNotDone = progress >> 16 + returnTest = None + avgNum2Kill = 1.0 / (chance / 100.0) + if numberNotDone >= avgNum2Kill * 1.5: + chance = 100 + elif numberNotDone > avgNum2Kill * 0.5: + diff = float(numberNotDone - avgNum2Kill * 0.5) + luck = 1.0 + abs(diff / (avgNum2Kill * 0.5)) + chance *= luck + if test <= chance: + returnTest = 1 + numberNotDone = 0 + numberDone += 1 + else: + returnTest = 0 + numberNotDone += 1 + numberDone += 0 + returnCount = numberNotDone << 16 + returnCount += numberDone + return (returnTest, returnCount) + + def testDone(self, progress): + numberDone = progress & pow(2, 16) - 1 + print 'Quest number done %s' % numberDone + if numberDone >= self.getNumItems(): + return 1 + else: + return 0 + + def getNumQuestItems(self): + return self.getNumItems() + + def getNumItems(self): + return self.quest[1] + + def getItem(self): + return self.quest[2] + + def getPercentChance(self): + return self.quest[3] + + def getHolder(self): + return self.quest[4] + + def getHolderType(self): + if len(self.quest) == 5: + return 'type' + else: + return self.quest[5] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + forwardProgress = toonProgress & pow(2, 16) - 1 + questComplete = forwardProgress >= self.getNumItems() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumItems() == 1: + return '' + else: + progress = questDesc[4] & pow(2, 16) - 1 + return TTLocalizer.QuestsRecoverItemQuestProgress % {'progress': progress, + 'numItems': self.getNumItems()} + + def getObjectiveStrings(self): + holder = self.getHolder() + holderType = self.getHolderType() + if holder == Any: + holderName = TTLocalizer.TheCogs + elif holder == AnyFish: + holderName = TTLocalizer.AFish + elif holderType == 'type': + holderName = SuitBattleGlobals.SuitAttributes[holder]['pluralname'] + elif holderType == 'level': + holderName = TTLocalizer.QuestsRecoverItemQuestHolderString % {'level': TTLocalizer.Level, + 'holder': holder, + 'cogs': TTLocalizer.Cogs} + elif holderType == 'track': + if holder == 'c': + holderName = TTLocalizer.BossbotP + elif holder == 's': + holderName = TTLocalizer.SellbotP + elif holder == 'm': + holderName = TTLocalizer.CashbotP + elif holder == 'l': + holderName = TTLocalizer.LawbotP + item = self.getItem() + num = self.getNumItems() + if num == 1: + itemName = ItemDict[item][2] + ItemDict[item][0] + else: + itemName = TTLocalizer.QuestsItemNameAndNum % {'num': TTLocalizer.getLocalNum(num), + 'name': ItemDict[item][1]} + return [itemName, holderName] + + def getString(self): + return TTLocalizer.QuestsRecoverItemQuestString % {'item': self.getObjectiveStrings()[0], + 'holder': self.getObjectiveStrings()[1]} + + def getSCStrings(self, toNpcId, progress): + item = self.getItem() + num = self.getNumItems() + forwardProgress = progress & pow(2, 16) - 1 + if forwardProgress >= self.getNumItems(): + if num == 1: + itemName = ItemDict[item][2] + ItemDict[item][0] + else: + itemName = TTLocalizer.QuestsItemNameAndNum % {'num': TTLocalizer.getLocalNum(num), + 'name': ItemDict[item][1]} + if toNpcId == ToonHQ: + strings = [TTLocalizer.QuestsRecoverItemQuestReturnToHQSCString % itemName, TTLocalizer.QuestsRecoverItemQuestGoToHQSCString] + elif toNpcId: + npcName, hoodName, buildingArticle, buildingName, toStreet, streetName, isInPlayground = getNpcInfo(toNpcId) + strings = [TTLocalizer.QuestsRecoverItemQuestReturnToSCString % {'item': itemName, + 'npcName': npcName}] + if isInPlayground: + strings.append(TTLocalizer.QuestsRecoverItemQuestGoToPlaygroundSCString % hoodName) + else: + strings.append(TTLocalizer.QuestsRecoverItemQuestGoToStreetSCString % {'to': toStreet, + 'street': streetName, + 'hood': hoodName}) + strings.extend([TTLocalizer.QuestsRecoverItemQuestVisitBuildingSCString % (buildingArticle, buildingName), TTLocalizer.QuestsRecoverItemQuestWhereIsBuildingSCString % (buildingArticle, buildingName)]) + return strings + holder = self.getHolder() + holderType = self.getHolderType() + locName = self.getLocationName() + if holder == Any: + holderName = TTLocalizer.TheCogs + elif holder == AnyFish: + holderName = TTLocalizer.TheFish + elif holderType == 'type': + holderName = SuitBattleGlobals.SuitAttributes[holder]['pluralname'] + elif holderType == 'level': + holderName = TTLocalizer.QuestsRecoverItemQuestHolderString % {'level': TTLocalizer.Level, + 'holder': holder, + 'cogs': TTLocalizer.Cogs} + elif holderType == 'track': + if holder == 'c': + holderName = TTLocalizer.BossbotP + elif holder == 's': + holderName = TTLocalizer.SellbotP + elif holder == 'm': + holderName = TTLocalizer.CashbotP + elif holder == 'l': + holderName = TTLocalizer.LawbotP + if num == 1: + itemName = ItemDict[item][2] + ItemDict[item][0] + else: + itemName = TTLocalizer.QuestsItemNameAndNum % {'num': TTLocalizer.getLocalNum(num), + 'name': ItemDict[item][1]} + return TTLocalizer.QuestsRecoverItemQuestRecoverFromSCString % {'item': itemName, + 'holder': holderName, + 'loc': locName} + + def getHeadlineString(self): + return TTLocalizer.QuestsRecoverItemQuestHeadline + + def doesCogCount(self, avId, cogDict, zoneId): + questCogType = self.getHolder() + return (questCogType is Any or questCogType is cogDict[self.getHolderType()]) and avId in cogDict['activeToons'] and self.isLocationMatch(zoneId) + + +class TrackChoiceQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + self.checkTrackChoice(self.quest[0]) + self.checkTrackChoice(self.quest[1]) + + def getChoices(self): + return (self.quest[0], self.quest[1]) + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + if npc and npcMatches(toNpcId, npc): + return COMPLETE + else: + return INCOMPLETE_WRONG_NPC + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + else: + return NotChosenString + + def getObjectiveStrings(self): + trackA, trackB = self.getChoices() + trackAName = ToontownBattleGlobals.Tracks[trackA].capitalize() + trackBName = ToontownBattleGlobals.Tracks[trackB].capitalize() + return [trackAName, trackBName] + + def getString(self): + return TTLocalizer.QuestsTrackChoiceQuestString % {'trackA': self.getObjectiveStrings()[0], + 'trackB': self.getObjectiveStrings()[1]} + + def getSCStrings(self, toNpcId, progress): + trackA, trackB = self.getChoices() + trackAName = ToontownBattleGlobals.Tracks[trackA].capitalize() + trackBName = ToontownBattleGlobals.Tracks[trackB].capitalize() + return [TTLocalizer.QuestsTrackChoiceQuestSCString % {'trackA': trackAName, + 'trackB': trackBName}, TTLocalizer.QuestsTrackChoiceQuestMaybeSCString % trackAName, TTLocalizer.QuestsTrackChoiceQuestMaybeSCString % trackBName] + getVisitSCStrings(toNpcId) + + def getHeadlineString(self): + return TTLocalizer.QuestsTrackChoiceQuestHeadline + + +class FriendQuest(Quest): + def filterFunc(avatar): + if len(avatar.getFriendsList()) == 0: + return 1 + else: + return 0 + + filterFunc = staticmethod(filterFunc) + + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= 1 or len(av.getFriendsList()) > 0 + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + else: + return '' + + def getString(self): + return TTLocalizer.QuestsFriendQuestString + + def getSCStrings(self, toNpcId, progress): + if progress: + return getFinishToonTaskSCStrings(toNpcId) + return TTLocalizer.QuestsFriendQuestSCString + + def getHeadlineString(self): + return TTLocalizer.QuestsFriendQuestHeadline + + def getObjectiveStrings(self): + return [TTLocalizer.QuestsFriendQuestString] + +class TrolleyQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= 1 + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + else: + return '' + + def getString(self): + return TTLocalizer.QuestsFriendQuestString + + def getSCStrings(self, toNpcId, progress): + if progress: + return getFinishToonTaskSCStrings(toNpcId) + return TTLocalizer.QuestsTrolleyQuestSCString + + def getHeadlineString(self): + return TTLocalizer.QuestsTrolleyQuestHeadline + + def getObjectiveStrings(self): + return [TTLocalizer.QuestsTrolleyQuestString] + + +class MailboxQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= 1 + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + else: + return '' + + def getString(self): + return TTLocalizer.QuestsMailboxQuestString + + def getSCStrings(self, toNpcId, progress): + if progress: + return getFinishToonTaskSCStrings(toNpcId) + return TTLocalizer.QuestsMailboxQuestSCString + + def getHeadlineString(self): + return TTLocalizer.QuestsMailboxQuestHeadline + + def getObjectiveStrings(self): + return [TTLocalizer.QuestsMailboxQuestString] + + +class PhoneQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= 1 + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + else: + return '' + + def getString(self): + return TTLocalizer.QuestsPhoneQuestString + + def getSCStrings(self, toNpcId, progress): + if progress: + return getFinishToonTaskSCStrings(toNpcId) + return TTLocalizer.QuestsPhoneQuestSCString + + def getHeadlineString(self): + return TTLocalizer.QuestsPhoneQuestHeadline + + def getObjectiveStrings(self): + return [TTLocalizer.QuestsPhoneQuestString] + + +DefaultDialog = {GREETING: DefaultGreeting, + QUEST: DefaultQuest, + INCOMPLETE: DefaultIncomplete, + INCOMPLETE_PROGRESS: DefaultIncompleteProgress, + INCOMPLETE_WRONG_NPC: DefaultIncompleteWrongNPC, + COMPLETE: DefaultComplete, + LEAVING: DefaultLeaving} + +def getQuestFromNpcId(id): + return QuestDict.get(id)[QuestDictFromNpcIndex] + + +def getQuestToNpcId(id): + return QuestDict.get(id)[QuestDictToNpcIndex] + +def getQuestDialog(id): + return QuestDict.get(id)[QuestDictDialogIndex] + + +def getQuestReward(id, av): + baseRewardId = QuestDict.get(id)[QuestDictRewardIndex] + return transformReward(baseRewardId, av) + + +def isQuestJustForFun(questId, rewardId): + questEntry = QuestDict.get(questId) + if questEntry: + tier = questEntry[QuestDictTierIndex] + return isRewardOptional(tier, rewardId) + else: + return False + +NoRewardTierZeroQuests = (101, 110, 120, 121, 130, 131, 140, 141, 142, 145, 150, 160, 161, 162, 163) +RewardTierZeroQuests = () +PreClarabelleQuestIds = NoRewardTierZeroQuests + RewardTierZeroQuests +QuestDict = { + 101: (TT_TIER, Start, (CogQuest, Anywhere, 1, 'f'), Any, ToonHQ, NA, 110, DefaultDialog), + 110: (TT_TIER, Cont, (TrolleyQuest,), Any, ToonHQ, NA, (120, 130, 140, 145), DefaultDialog), + 120: (TT_TIER, Cont, (DeliverItemQuest, 5), ToonHQ, 2002, NA, 121, TTLocalizer.QuestDialogDict[120]), + 121: (TT_TIER, Cont, (RecoverItemQuest, ToontownGlobals.ToontownCentral, 1, 2, VeryEasy, Any, 'type'), Same, 2002, NA, 142, TTLocalizer.QuestDialogDict[121]), + 130: (TT_TIER, Cont, (DeliverItemQuest, 6), ToonHQ, 2003, NA, 131, TTLocalizer.QuestDialogDict[130]), + 131: (TT_TIER, Cont, (RecoverItemQuest, ToontownGlobals.ToontownCentral, 1, 3, VeryEasy, Any, 'type'), Same, 2003, NA, 142, TTLocalizer.QuestDialogDict[131]), + 140: (TT_TIER, Cont, (DeliverItemQuest, 4), ToonHQ, 2005, NA, 141, TTLocalizer.QuestDialogDict[140]), + 141: (TT_TIER, Cont, (RecoverItemQuest, ToontownGlobals.ToontownCentral, 1, 1, VeryEasy, Any, 'type'), Same, 2005, NA, 142, TTLocalizer.QuestDialogDict[141]), + 142: (TT_TIER, Cont, (VisitQuest,), Same, ToonHQ, NA, 150, TTLocalizer.QuestDialogDict[142]), + 145: (TT_TIER, Cont, (RecoverItemQuest, ToontownGlobals.ToontownCentral, 1, 20, VeryEasy, Any, 'type'), ToonHQ, ToonHQ, NA, 150, DefaultDialog), + 150: (TT_TIER, Cont, (FriendQuest,), Same, Same, NA, 175, DefaultDialog), + 160: (TT_TIER, OBSOLETE, (CogTrackQuest, ToontownGlobals.ToontownCentral, 3, 'c'), Same, ToonHQ, NA, 175, TTLocalizer.QuestDialogDict[160]), + 161: (TT_TIER, OBSOLETE, (CogTrackQuest, ToontownGlobals.ToontownCentral, 3, 'l'), Same, ToonHQ, NA, 175, TTLocalizer.QuestDialogDict[161]), + 162: (TT_TIER, OBSOLETE, (CogTrackQuest, ToontownGlobals.ToontownCentral, 3, 's'), Same, ToonHQ, NA, 175, TTLocalizer.QuestDialogDict[162]), + 163: (TT_TIER, OBSOLETE, (CogTrackQuest, ToontownGlobals.ToontownCentral, 3, 'm'), Same, ToonHQ, NA, 175, TTLocalizer.QuestDialogDict[163]), + 175: (TT_TIER, Cont, (PhoneQuest,), Same, ToonHQ, 100, NA, TTLocalizer.QuestDialogDict[175]), + 164: (TT_TIER + 1, Start, (VisitQuest,), Any, 2001, NA, 165, TTLocalizer.QuestDialogDict[164]), + 165: (TT_TIER + 1, Start, (CogQuest, Anywhere, 4, Any), 2001, Same, NA, (166, 167, 168, 169), TTLocalizer.QuestDialogDict[165]), + 166: (TT_TIER + 1, Cont, (CogTrackQuest, Anywhere, 4, 'c'), Same, Same, NA, 170, TTLocalizer.QuestDialogDict[166]), + 167: (TT_TIER + 1, Cont, (CogTrackQuest, Anywhere, 4, 'l'), Same, Same, NA, 170, TTLocalizer.QuestDialogDict[167]), + 168: (TT_TIER + 1, Cont, (CogTrackQuest, Anywhere, 4, 's'), Same, Same, NA, 170, TTLocalizer.QuestDialogDict[168]), + 169: (TT_TIER + 1, Cont, (CogTrackQuest, Anywhere, 4, 'm'), Same, Same, NA, 170, TTLocalizer.QuestDialogDict[169]), + 170: (TT_TIER + 1, Cont, (VisitQuest,), Same, 2005, NA, 400, TTLocalizer.QuestDialogDict[170]), + 171: (TT_TIER + 1, Cont, (VisitQuest,), Same, 2311, NA, 400, TTLocalizer.QuestDialogDict[171]), + 172: (TT_TIER + 1, Cont, (VisitQuest,), Same, 2119, NA, 400, TTLocalizer.QuestDialogDict[172]), + 400: (TT_TIER + 1, Cont, (TrackChoiceQuest, ToontownBattleGlobals.SOUND_TRACK, ToontownBattleGlobals.HEAL_TRACK), Same, Same, 400, NA, TTLocalizer.QuestDialogDict[400]), + 1001: (TT_TIER + 2, Start, (CogQuest, ToontownGlobals.ToontownCentral, 3, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 1002: (TT_TIER + 2, Start, (CogQuest, ToontownGlobals.ToontownCentral, 4, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 1003: (TT_TIER + 2, Start, (CogQuest, ToontownGlobals.ToontownCentral, 5, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 1004: (TT_TIER + 2, Start, (CogQuest, ToontownGlobals.ToontownCentral, 6, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 1005: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'f'), Any, ToonHQ, Any, NA, DefaultDialog), + 1006: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'p'), Any, ToonHQ, Any, NA, DefaultDialog), + 1007: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'bf'), Any, ToonHQ, Any, NA, DefaultDialog), + 1008: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'b'), Any, ToonHQ, Any, NA, DefaultDialog), + 1009: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'sc'), Any, ToonHQ, Any, NA, DefaultDialog), + 1010: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'pp'), Any, ToonHQ, Any, NA, DefaultDialog), + 1011: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'cc'), Any, ToonHQ, Any, NA, DefaultDialog), + 1012: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'tm'), Any, ToonHQ, Any, NA, DefaultDialog), + 1013: (TT_TIER + 2, Start, (CogQuest, Anywhere, 4, 'f'), Any, ToonHQ, Any, NA, DefaultDialog), + 1014: (TT_TIER + 2, Start, (CogQuest, Anywhere, 4, 'p'), Any, ToonHQ, Any, NA, DefaultDialog), + 1015: (TT_TIER + 2, Start, (CogQuest, Anywhere, 4, 'bf'), Any, ToonHQ, Any, NA, DefaultDialog), + 1016: (TT_TIER + 2, Start, (CogQuest, Anywhere, 4, 'b'), Any, ToonHQ, Any, NA, DefaultDialog), + 1017: (TT_TIER + 2, Start, (CogQuest, Anywhere, 1, 'ym'), Any, ToonHQ, Any, NA, DefaultDialog), + 1018: (TT_TIER + 2, Start, (CogQuest, Anywhere, 1, 'nd'), Any, ToonHQ, Any, NA, DefaultDialog), + 1019: (TT_TIER + 2, Start, (CogQuest, Anywhere, 1, 'tw'), Any, ToonHQ, Any, NA, DefaultDialog), + 1020: (TT_TIER + 2, Start, (CogQuest, Anywhere, 1, 'dt'), Any, ToonHQ, Any, NA, DefaultDialog), + 1021: (TT_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.ToontownCentral, 2, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 1022: (TT_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.ToontownCentral, 6, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 1023: (TT_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.ToontownCentral, 3, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 1024: (TT_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.ToontownCentral, 4, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 1025: (TT_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.ToontownCentral, 4, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 1026: (TT_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.ToontownCentral, 6, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 1027: (TT_TIER + 2, Start, (CogTrackQuest, ToontownGlobals.ToontownCentral, 2, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 1028: (TT_TIER + 2, Start, (CogTrackQuest, ToontownGlobals.ToontownCentral, 2, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 1029: (TT_TIER + 2, Start, (CogTrackQuest, ToontownGlobals.ToontownCentral, 2, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 1030: (TT_TIER + 2, Start, (CogTrackQuest, ToontownGlobals.ToontownCentral, 2, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 1031: (TT_TIER + 2, Start, (CogTrackQuest, ToontownGlobals.ToontownCentral, 3, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 1032: (TT_TIER + 2, Start, (CogTrackQuest, ToontownGlobals.ToontownCentral, 3, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 1033: (TT_TIER + 2, Start, (CogTrackQuest, ToontownGlobals.ToontownCentral, 3, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 1034: (TT_TIER + 2, Start, (CogTrackQuest, ToontownGlobals.ToontownCentral, 3, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 1035: (TT_TIER + 2, Start, (CogTrackQuest, ToontownGlobals.ToontownCentral, 5, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 1036: (TT_TIER + 2, Start, (CogTrackQuest, ToontownGlobals.ToontownCentral, 5, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 1037: (TT_TIER + 2, Start, (CogTrackQuest, ToontownGlobals.ToontownCentral, 5, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 1038: (TT_TIER + 2, Start, (CogTrackQuest, ToontownGlobals.ToontownCentral, 5, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 1039: (TT_TIER + 2, Start, (VisitQuest,), Any, 2135, NA, (1041, 1042, 1043), TTLocalizer.QuestDialogDict[1039]), + 1040: (TT_TIER + 2, Start, (VisitQuest,), Any, 2207, NA, (1041, 1042, 1043), TTLocalizer.QuestDialogDict[1040]), + 1041: (TT_TIER + 2, Cont, (VisitQuest,), Same, 2211, NA, 1044, TTLocalizer.QuestDialogDict[1041]), + 1042: (TT_TIER + 2, Cont, (VisitQuest,), Same, 2209, NA, 1044, TTLocalizer.QuestDialogDict[1042]), + 1043: (TT_TIER + 2, Cont, (VisitQuest,), Same, 2210, NA, 1044, TTLocalizer.QuestDialogDict[1043]), + 1044: (TT_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 4, 7, VeryEasy, Any, 'type'), Same, Same, NA, 1045, TTLocalizer.QuestDialogDict[1044]), + 1045: (TT_TIER + 2, Cont, (DeliverItemQuest, 8), Same, ToonHQ, 300, NA, TTLocalizer.QuestDialogDict[1045]), + 1046: (TT_TIER + 2, Start, (VisitQuest,), Any, 2127, NA, 1047, TTLocalizer.QuestDialogDict[1046]), + 1047: (TT_TIER + 2, Start, (RecoverItemQuest, Anywhere, 5, 9, VeryEasy, 'm', 'track'), 2127, Same, NA, 1048, TTLocalizer.QuestDialogDict[1047]), + 1048: (TT_TIER + 2, Cont, (DeliverItemQuest, 9), Same, 2131, NA, 1049, TTLocalizer.QuestDialogDict[1048]), + 1049: (TT_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 10, 2007, VeryEasy, 3, 'level'), Same, Same, NA, 1053, TTLocalizer.QuestDialogDict[1049]), + 1053: (TT_TIER + 2, Cont, (DeliverItemQuest, 9), Same, 2127, 700, NA, TTLocalizer.QuestDialogDict[1053]), + 1054: (TT_TIER + 2, Start, (VisitQuest,), Any, 2128, NA, 1055, TTLocalizer.QuestDialogDict[1054]), + 1055: (TT_TIER + 2, Start, (RecoverItemQuest, Anywhere, 4, 10, Easy, AnyFish), 2128, Same, NA, 1056, TTLocalizer.QuestDialogDict[1055]), + 1056: (TT_TIER + 2, Cont, (VisitQuest,), Same, 2213, NA, 1057, TTLocalizer.QuestDialogDict[1056]), + 1057: (TT_TIER + 2, Cont, (CogLevelQuest, ToontownGlobals.ToontownCentral, 6, 3), Same, Same, NA, 1058, TTLocalizer.QuestDialogDict[1057]), + 1058: (TT_TIER + 2, Cont, (DeliverItemQuest, 11), Same, 2128, 200, NA, TTLocalizer.QuestDialogDict[1058]), + 1059: (TT_TIER + 2, Start, (VisitQuest,), Any, 2302, NA, 1060, TTLocalizer.QuestDialogDict[1059]), + 1060: (TT_TIER + 2, Start, (RecoverItemQuest, Anywhere, 1, 12, Medium, AnyFish), 2302, Same, NA, 1062, TTLocalizer.QuestDialogDict[1060]), + 1061: (TT_TIER + 2, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 6, 'p'), Same, Same, 101, NA, TTLocalizer.QuestDialogDict[1061]), + 1062: (TT_TIER + 2, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 6, 'b'), Same, Same, 101, NA, TTLocalizer.QuestDialogDict[1062]), + 900: (TT_TIER + 3, Start, (VisitQuest,), Any, 2201, NA, 1063, TTLocalizer.QuestDialogDict[900]), + 1063: (TT_TIER + 3, Start, (RecoverItemQuest, Anywhere, 1, 13, Medium, 3, 'level'), 2201, Same, NA, 1067, TTLocalizer.QuestDialogDict[1063]), + 1067: (TT_TIER + 3, Cont, (DeliverItemQuest, 13), Same, 2112, NA, 1068, TTLocalizer.QuestDialogDict[1067]), + 1068: (TT_TIER + 3, Cont, (CogQuest, ToontownGlobals.ToontownCentral, 10, Any), Same, Same, NA, (1069, 1070, 1071), TTLocalizer.QuestDialogDict[1068]), + 1069: (TT_TIER + 3, Cont, (RecoverItemQuest, Anywhere, 1, 13, Medium, 'm', 'track'), Same, Same, NA, 1072, TTLocalizer.QuestDialogDict[1069]), + 1070: (TT_TIER + 3, Cont, (RecoverItemQuest, Anywhere, 1, 13, Medium, 's', 'track'), Same, Same, NA, 1072, TTLocalizer.QuestDialogDict[1070]), + 1071: (TT_TIER + 3, Cont, (RecoverItemQuest, Anywhere, 1, 13, Medium, 'c', 'track'), Same, Same, NA, 1072, TTLocalizer.QuestDialogDict[1071]), + 1072: (TT_TIER + 3, Cont, (DeliverItemQuest, 13), Same, 2301, NA, 1073, TTLocalizer.QuestDialogDict[1072]), + 1073: (TT_TIER + 3, Cont, (VisitQuest,), Any, 2201, NA, 1074, TTLocalizer.QuestDialogDict[1073]), + 1074: (TT_TIER + 3, Cont, (RecoverItemQuest, Anywhere, 1, 13, Hard, Any), Same, Same, NA, 1075, TTLocalizer.QuestDialogDict[1074]), + 1075: (TT_TIER + 3, Cont, (DeliverItemQuest, 13), Same, 2301, 900, NA, TTLocalizer.QuestDialogDict[1075]), + 1076: (TT_TIER + 2, Start, (VisitQuest,), Any, 2217, NA, 1077, TTLocalizer.QuestDialogDict[1076]), + 1077: (TT_TIER + 2, Start, (RecoverItemQuest, Anywhere, 1, 14, Medium, Any), 2217, Same, NA, 1078, TTLocalizer.QuestDialogDict[1077]), + 1078: (TT_TIER + 2, Cont, (DeliverItemQuest, 14), Same, 2302, NA, 1079, TTLocalizer.QuestDialogDict[1078]), + 1079: (TT_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 1, 15, Easy, 'f'), Same, 2217, NA, 1080, TTLocalizer.QuestDialogDict[1079]), + 1092: (TT_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 1, 15, Easy, 'sc'), Same, 2217, NA, 1080, TTLocalizer.QuestDialogDict[1092]), + 1080: (TT_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 4, 15, Easy, AnyFish), Same, Same, 500, NA, TTLocalizer.QuestDialogDict[1080]), + 1081: (TT_TIER + 2, Start, (VisitQuest,), Any, 2208, NA, 1082, TTLocalizer.QuestDialogDict[1081]), + 1082: (TT_TIER + 2, Start, (RecoverItemQuest, Anywhere, 1, 16, Medium, 's', 'track'), 2208, Same, NA, 1083, TTLocalizer.QuestDialogDict[1082]), + 1083: (TT_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 1, 17, Medium, 'l', 'track'), Same, Same, NA, 1084, TTLocalizer.QuestDialogDict[1083]), + 1084: (TT_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 1, 18, Medium, 'm', 'track'), Same, Same, 102, NA, TTLocalizer.QuestDialogDict[1084]), + 1085: (TT_TIER + 2, Start, (VisitQuest,), Any, 2003, NA, 1086, TTLocalizer.QuestDialogDict[1085]), + 1086: (TT_TIER + 2, Start, (RecoverItemQuest, Anywhere, 5, 2007, Easy, 2, 'level'), 2003, Same, NA, 1089, TTLocalizer.QuestDialogDict[1086]), + 1089: (TT_TIER + 2, Cont, (DeliverItemQuest, 19), Same, ToonHQ, 100, NA, TTLocalizer.QuestDialogDict[1089]), + 1090: (TT_TIER + 2, Start, (VisitQuest,), Any, 2119, NA, 1091, TTLocalizer.QuestDialogDict[1090]), + 1091: (TT_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.ToontownCentral, 8, 2), 2119, ToonHQ, 101, NA, TTLocalizer.QuestDialogDict[1091]), + 1100: (TT_TIER + 2, Start, (CogQuest, ToontownGlobals.ToontownCentral, 10, Any), Any, ToonHQ, NA, 1101, DefaultDialog), + 1101: (TT_TIER + 2, Cont, (DeliverItemQuest, 1000), Any, 2004, 1000, NA, DefaultDialog), + 1102: (TT_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.ToontownCentral, 8, 3), Any, ToonHQ, NA, 1103, DefaultDialog), + 1103: (TT_TIER + 2, Cont, (DeliverItemQuest, 1000), Any, 2004, 1000, NA, DefaultDialog), + 1105: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'f'), Any, ToonHQ, Any, NA, DefaultDialog), + 1106: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'p'), Any, ToonHQ, Any, NA, DefaultDialog), + 1107: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'bf'), Any, ToonHQ, Any, NA, DefaultDialog), + 1108: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'b'), Any, ToonHQ, Any, NA, DefaultDialog), + 1109: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'sc'), Any, ToonHQ, Any, NA, DefaultDialog), + 1110: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'pp'), Any, ToonHQ, Any, NA, DefaultDialog), + 1111: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'cc'), Any, ToonHQ, Any, NA, DefaultDialog), + 1112: (TT_TIER + 2, Start, (CogQuest, Anywhere, 2, 'tm'), Any, ToonHQ, Any, NA, DefaultDialog), + 1205: (TT_TIER + 3, Start, (CogQuest, Anywhere, 4, 'f'), Any, ToonHQ, Any, NA, DefaultDialog), + 1206: (TT_TIER + 3, Start, (CogQuest, Anywhere, 4, 'p'), Any, ToonHQ, Any, NA, DefaultDialog), + 1207: (TT_TIER + 3, Start, (CogQuest, Anywhere, 4, 'bf'), Any, ToonHQ, Any, NA, DefaultDialog), + 1208: (TT_TIER + 3, Start, (CogQuest, Anywhere, 4, 'b'), Any, ToonHQ, Any, NA, DefaultDialog), + 1209: (TT_TIER + 3, Start, (CogQuest, Anywhere, 4, 'sc'), Any, ToonHQ, Any, NA, DefaultDialog), + 1210: (TT_TIER + 3, Start, (CogQuest, Anywhere, 4, 'pp'), Any, ToonHQ, Any, NA, DefaultDialog), + 1211: (TT_TIER + 3, Start, (CogQuest, Anywhere, 4, 'cc'), Any, ToonHQ, Any, NA, DefaultDialog), + 1212: (TT_TIER + 3, Start, (CogQuest, Anywhere, 4, 'tm'), Any, ToonHQ, Any, NA, DefaultDialog), + 401: (DD_TIER, Start, (TrackChoiceQuest, ToontownBattleGlobals.DROP_TRACK, ToontownBattleGlobals.LURE_TRACK), Any, ToonHQ, 400, NA, TTLocalizer.QuestDialogDict[401]), + 2001: (DD_TIER, Start, (CogQuest, Anywhere, 3, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2002: (DD_TIER, Start, (CogQuest, Anywhere, 4, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2003: (DD_TIER, Start, (CogQuest, Anywhere, 5, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2004: (DD_TIER, Start, (CogQuest, Anywhere, 6, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2005: (DD_TIER, Start, (CogQuest, Anywhere, 7, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2006: (DD_TIER, Start, (CogQuest, Anywhere, 8, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2007: (DD_TIER, Start, (CogQuest, Anywhere, 9, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2008: (DD_TIER, Start, (CogQuest, Anywhere, 10, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2009: (DD_TIER, Start, (CogQuest, Anywhere, 12, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2010: (DD_TIER, Start, (CogLevelQuest, Anywhere, 2, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 2011: (DD_TIER, Start, (CogLevelQuest, Anywhere, 3, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 2012: (DD_TIER, Start, (CogLevelQuest, Anywhere, 2, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 2013: (DD_TIER, Start, (CogLevelQuest, Anywhere, 4, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 2014: (DD_TIER, Start, (CogLevelQuest, Anywhere, 4, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 2015: (DD_TIER, Start, (CogLevelQuest, Anywhere, 5, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 2816: (DD_TIER, Start, (CogLevelQuest, Anywhere, 4, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 2817: (DD_TIER, Start, (CogLevelQuest, Anywhere, 5, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 2818: (DD_TIER, Start, (CogLevelQuest, Anywhere, 6, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 2819: (DD_TIER, Start, (CogLevelQuest, Anywhere, 7, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 2020: (DD_TIER, Start, (CogQuest, Anywhere, 10, Any), Any, ToonHQ, NA, 2021, DefaultDialog), + 2021: (DD_TIER, Cont, (DeliverItemQuest, 1000), Any, 1007, 1000, NA, DefaultDialog), + 2101: (DD_TIER + 1, Start, (CogQuest, ToontownGlobals.DonaldsDock, 3, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2102: (DD_TIER + 1, Start, (CogQuest, ToontownGlobals.DonaldsDock, 4, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2103: (DD_TIER + 1, Start, (CogQuest, ToontownGlobals.DonaldsDock, 5, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2104: (DD_TIER + 1, Start, (CogQuest, Anywhere, 6, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2105: (DD_TIER + 1, Start, (CogQuest, Anywhere, 7, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2106: (DD_TIER + 1, Start, (CogQuest, Anywhere, 8, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2107: (DD_TIER + 1, Start, (CogQuest, Anywhere, 6, 'f'), Any, ToonHQ, Any, NA, DefaultDialog), + 2108: (DD_TIER + 1, Start, (CogQuest, Anywhere, 4, 'p'), Any, ToonHQ, Any, NA, DefaultDialog), + 2109: (DD_TIER + 1, Start, (CogQuest, Anywhere, 4, 'ym'), Any, ToonHQ, Any, NA, DefaultDialog), + 2110: (DD_TIER + 1, Start, (CogQuest, Anywhere, 3, 'mm'), Any, ToonHQ, Any, NA, DefaultDialog), + 2111: (DD_TIER + 1, Start, (CogQuest, Anywhere, 2, 'ds'), Any, ToonHQ, Any, NA, DefaultDialog), + 2112: (DD_TIER + 1, Start, (CogQuest, Anywhere, 1, 'hh'), Any, ToonHQ, Any, NA, DefaultDialog), + 2113: (DD_TIER + 1, Start, (CogQuest, Anywhere, 6, 'cc'), Any, ToonHQ, Any, NA, DefaultDialog), + 2114: (DD_TIER + 1, Start, (CogQuest, Anywhere, 4, 'tm'), Any, ToonHQ, Any, NA, DefaultDialog), + 2115: (DD_TIER + 1, Start, (CogQuest, Anywhere, 4, 'nd'), Any, ToonHQ, Any, NA, DefaultDialog), + 2116: (DD_TIER + 1, Start, (CogQuest, Anywhere, 3, 'gh'), Any, ToonHQ, Any, NA, DefaultDialog), + 2117: (DD_TIER + 1, Start, (CogQuest, Anywhere, 2, 'ms'), Any, ToonHQ, Any, NA, DefaultDialog), + 2118: (DD_TIER + 1, Start, (CogQuest, Anywhere, 1, 'tf'), Any, ToonHQ, Any, NA, DefaultDialog), + 2119: (DD_TIER + 1, Start, (CogQuest, Anywhere, 6, 'sc'), Any, ToonHQ, Any, NA, DefaultDialog), + 2120: (DD_TIER + 1, Start, (CogQuest, Anywhere, 4, 'pp'), Any, ToonHQ, Any, NA, DefaultDialog), + 2121: (DD_TIER + 1, Start, (CogQuest, Anywhere, 4, 'tw'), Any, ToonHQ, Any, NA, DefaultDialog), + 2122: (DD_TIER + 1, Start, (CogQuest, Anywhere, 3, 'bc'), Any, ToonHQ, Any, NA, DefaultDialog), + 2123: (DD_TIER + 1, Start, (CogQuest, Anywhere, 2, 'nc'), Any, ToonHQ, Any, NA, DefaultDialog), + 2124: (DD_TIER + 1, Start, (CogQuest, Anywhere, 1, 'mb'), Any, ToonHQ, Any, NA, DefaultDialog), + 2125: (DD_TIER + 1, Start, (CogQuest, Anywhere, 6, 'bf'), Any, ToonHQ, Any, NA, DefaultDialog), + 2126: (DD_TIER + 1, Start, (CogQuest, Anywhere, 4, 'b'), Any, ToonHQ, Any, NA, DefaultDialog), + 2127: (DD_TIER + 1, Start, (CogQuest, Anywhere, 4, 'dt'), Any, ToonHQ, Any, NA, DefaultDialog), + 2128: (DD_TIER + 1, Start, (CogQuest, Anywhere, 3, 'ac'), Any, ToonHQ, Any, NA, DefaultDialog), + 2129: (DD_TIER + 1, Start, (CogQuest, Anywhere, 2, 'bs'), Any, ToonHQ, Any, NA, DefaultDialog), + 2130: (DD_TIER + 1, Start, (CogQuest, Anywhere, 1, 'sd'), Any, ToonHQ, Any, NA, DefaultDialog), + 2131: (DD_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.DonaldsDock, 2, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 2132: (DD_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.DonaldsDock, 3, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 2133: (DD_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.DonaldsDock, 2, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 2134: (DD_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.DonaldsDock, 4, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 2135: (DD_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.DonaldsDock, 4, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 2136: (DD_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.DonaldsDock, 5, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 2137: (DD_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.DonaldsDock, 4, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 2138: (DD_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.DonaldsDock, 6, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 2139: (DD_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.DonaldsDock, 3, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 2140: (DD_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.DonaldsDock, 3, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 2141: (DD_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.DonaldsDock, 3, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 2142: (DD_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.DonaldsDock, 3, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 2143: (DD_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.DonaldsDock, 5, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 2144: (DD_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.DonaldsDock, 5, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 2145: (DD_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.DonaldsDock, 5, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 2146: (DD_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.DonaldsDock, 5, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 2147: (DD_TIER + 1, Start, (CogTrackQuest, Anywhere, 7, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 2148: (DD_TIER + 1, Start, (CogTrackQuest, Anywhere, 7, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 2149: (DD_TIER + 1, Start, (CogTrackQuest, Anywhere, 7, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 2150: (DD_TIER + 1, Start, (CogTrackQuest, Anywhere, 7, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 2151: (DD_TIER + 1, Start, (BuildingQuest, Anywhere, 1, Any, 1, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 2152: (DD_TIER + 1, Start, (BuildingQuest, Anywhere, 1, Any, 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 2153: (DD_TIER + 1, Start, (BuildingQuest, Anywhere, 2, Any, 1, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 2154: (DD_TIER + 1, Start, (BuildingQuest, Anywhere, 2, Any, 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 2155: (DD_TIER + 1, Start, (BuildingQuest, Anywhere, 1, 'm', 1, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 2156: (DD_TIER + 1, Start, (BuildingQuest, Anywhere, 1, 's', 1, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 2157: (DD_TIER + 1, Start, (BuildingQuest, Anywhere, 1, 'c', 1, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 2158: (DD_TIER + 1, Start, (BuildingQuest, Anywhere, 1, 'l', 1, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 2159: (DD_TIER + 1, Start, (DeliverGagQuest, 2, ToontownBattleGlobals.THROW_TRACK, 1), Any, Any, Any, NA, DefaultDialog), + 2160: (DD_TIER + 1, Start, (DeliverGagQuest, 1, ToontownBattleGlobals.SQUIRT_TRACK, 1), Any, Any, Any, NA, DefaultDialog), + 2161: (DD_TIER + 1, Start, (DeliverGagQuest, 1, ToontownBattleGlobals.SQUIRT_TRACK, 2), Any, Any, Any, NA, DefaultDialog), + 2162: (DD_TIER + 1, Start, (DeliverGagQuest, 2, ToontownBattleGlobals.THROW_TRACK, 2), Any, Any, Any, NA, DefaultDialog), + 2201: (DD_TIER + 1, Start, (VisitQuest,), Any, 1101, NA, 2202, TTLocalizer.QuestDialogDict[2201]), + 2202: (DD_TIER + 1, Start, (RecoverItemQuest, Anywhere, 1, 2001, Medium, 'pp'), 1101, Same, 101, NA, TTLocalizer.QuestDialogDict[2202]), + 2203: (DD_TIER + 1, Start, (VisitQuest,), Any, 1102, NA, 2204, TTLocalizer.QuestDialogDict[2203]), + 2204: (DD_TIER + 1, Start, (DeliverItemQuest, 2002), 1102, 1104, NA, 2205, TTLocalizer.QuestDialogDict[2204]), + 2205: (DD_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 2003, Medium, 'f'), Same, Same, NA, 2206, TTLocalizer.QuestDialogDict[2205]), + 2206: (DD_TIER + 1, Cont, (DeliverItemQuest, 2004), Same, 1102, 201, NA, TTLocalizer.QuestDialogDict[2206]), + 2207: (DD_TIER + 1, Start, (VisitQuest,), Any, 1201, NA, 2208, TTLocalizer.QuestDialogDict[2207]), + 2208: (DD_TIER + 1, Start, (RecoverItemQuest, Anywhere, 1, 2005, Easy, 'bs'), 1201, Same, 701, NA, TTLocalizer.QuestDialogDict[2208]), + 2209: (DD_TIER + 1, Start, (VisitQuest,), Any, 1302, NA, 2210, TTLocalizer.QuestDialogDict[2209]), + 2210: (DD_TIER + 1, Start, (VisitQuest,), 1302, 1301, NA, 2211, TTLocalizer.QuestDialogDict[2210]), + 2211: (DD_TIER + 1, Cont, (CogQuest, ToontownGlobals.DonaldsDock, 5, 'mm'), Same, Same, NA, 2212, TTLocalizer.QuestDialogDict[2211]), + 2212: (DD_TIER + 1, Cont, (DeliverItemQuest, 2006), Same, 1302, NA, 2213, TTLocalizer.QuestDialogDict[2212]), + 2213: (DD_TIER + 1, Cont, (VisitQuest,), Same, 1202, NA, 2214, TTLocalizer.QuestDialogDict[2213]), + 2214: (DD_TIER + 1, Cont, (RecoverItemQuest, ToontownGlobals.DonaldsDock, 3, 2007, Hard, Any), Same, Same, NA, 2215, TTLocalizer.QuestDialogDict[2214]), + 2215: (DD_TIER + 1, Cont, (DeliverItemQuest, 2008), Same, 1302, 301, NA, TTLocalizer.QuestDialogDict[2215]), + 2500: (DD_TIER + 1, Start, (CogQuest, ToontownGlobals.DonaldsDock, 15, Any), Any, ToonHQ, NA, 2501, DefaultDialog), + 2501: (DD_TIER + 1, Cont, (DeliverItemQuest, 1000), Any, 1007, 1000, NA, DefaultDialog), + 2801: (DD_TIER + 2, Start, (CogQuest, Anywhere, 3, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2802: (DD_TIER + 2, Start, (CogQuest, Anywhere, 4, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2803: (DD_TIER + 2, Start, (CogQuest, Anywhere, 5, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2804: (DD_TIER + 2, Start, (CogQuest, Anywhere, 6, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2805: (DD_TIER + 2, Start, (CogQuest, Anywhere, 7, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2806: (DD_TIER + 2, Start, (CogQuest, Anywhere, 8, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2807: (DD_TIER + 2, Start, (CogQuest, Anywhere, 9, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2808: (DD_TIER + 2, Start, (CogQuest, Anywhere, 10, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2809: (DD_TIER + 2, Start, (CogQuest, Anywhere, 12, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 2810: (DD_TIER + 2, Start, (CogLevelQuest, Anywhere, 2, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 2811: (DD_TIER + 2, Start, (CogLevelQuest, Anywhere, 3, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 2812: (DD_TIER + 2, Start, (CogLevelQuest, Anywhere, 2, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 2813: (DD_TIER + 2, Start, (CogLevelQuest, Anywhere, 4, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 2814: (DD_TIER + 2, Start, (CogLevelQuest, Anywhere, 4, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 2815: (DD_TIER + 2, Start, (CogLevelQuest, Anywhere, 5, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 2816: (DD_TIER + 2, Start, (CogLevelQuest, Anywhere, 4, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 2817: (DD_TIER + 2, Start, (CogLevelQuest, Anywhere, 5, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 2818: (DD_TIER + 2, Start, (CogLevelQuest, Anywhere, 6, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 2819: (DD_TIER + 2, Start, (CogLevelQuest, Anywhere, 7, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 2820: (DD_TIER + 2, Start, (CogQuest, Anywhere, 20, Any), Any, ToonHQ, NA, 2821, DefaultDialog), + 2821: (DD_TIER + 2, Cont, (DeliverItemQuest, 1000), Any, 1007, 1000, NA, DefaultDialog), + 901: (DD_TIER + 2, Start, (VisitQuest,), Any, 1203, NA, 2902, TTLocalizer.QuestDialogDict[901]), + 2902: (DD_TIER + 2, Start, (VisitQuest,), 1203, 1303, NA, 2903, TTLocalizer.QuestDialogDict[2902]), + 2903: (DD_TIER + 2, Cont, (DeliverItemQuest, 2009), Same, 1106, NA, 2904, TTLocalizer.QuestDialogDict[2903]), + 2904: (DD_TIER + 2, Cont, (DeliverItemQuest, 2010), Same, 1203, NA, 2905, TTLocalizer.QuestDialogDict[2904]), + 2905: (DD_TIER + 2, Cont, (VisitQuest, 2009), Same, 1105, NA, 2906, TTLocalizer.QuestDialogDict[2905]), + 2906: (DD_TIER + 2, Cont, (DeliverGagQuest, 3, ToontownBattleGlobals.SQUIRT_TRACK, 2), Same, Same, NA, 2907, TTLocalizer.QuestDialogDict[2906]), + 2907: (DD_TIER + 2, Cont, (DeliverItemQuest, 2011), Same, 1203, NA, (2910, 2915, 2920), TTLocalizer.QuestDialogDict[2907]), + 2910: (DD_TIER + 2, Cont, (VisitQuest,), Same, 1107, NA, 2911, TTLocalizer.QuestDialog_2910), + 2911: (DD_TIER + 2, Cont, (CogTrackQuest, ToontownGlobals.DonaldsDock, 4, 'm'), Same, Same, NA, 2925, TTLocalizer.QuestDialogDict[2911]), + 2915: (DD_TIER + 2, Cont, (VisitQuest,), Same, 1204, NA, 2916, TTLocalizer.QuestDialog_2910), + 2916: (DD_TIER + 2, Cont, (CogTrackQuest, ToontownGlobals.DonaldsDock, 2, 's'), Same, Same, NA, 2925, TTLocalizer.QuestDialogDict[2916]), + 2920: (DD_TIER + 2, Cont, (VisitQuest,), Same, 1204, NA, 2921, TTLocalizer.QuestDialog_2910), + 2921: (DD_TIER + 2, Cont, (CogTrackQuest, ToontownGlobals.DonaldsDock, 6, 'c'), Same, Same, NA, 2925, TTLocalizer.QuestDialogDict[2921]), + 2925: (DD_TIER + 2, Cont, (DeliverItemQuest, 2012), Same, 1203, NA, 2926, TTLocalizer.QuestDialogDict[2925]), + 2926: (DD_TIER + 2, Cont, (BuildingQuest, ToontownGlobals.DonaldsDock, 1, Any, 2, 0), Same, Same, 900, NA, TTLocalizer.QuestDialogDict[2926]), + 3101: (DG_TIER, Start, (CogQuest, ToontownGlobals.DaisyGardens, 8, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 3102: (DG_TIER, Start, (CogQuest, ToontownGlobals.DaisyGardens, 10, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 3103: (DG_TIER, Start, (CogQuest, ToontownGlobals.DaisyGardens, 12, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 3104: (DG_TIER, Start, (CogQuest, Anywhere, 14, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 3105: (DG_TIER, Start, (CogQuest, Anywhere, 16, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 3106: (DG_TIER, Start, (CogQuest, Anywhere, 18, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 3107: (DG_TIER, Start, (CogQuest, Anywhere, 10, 'f'), Any, ToonHQ, Any, NA, DefaultDialog), + 3108: (DG_TIER, Start, (CogQuest, Anywhere, 8, 'p'), Any, ToonHQ, Any, NA, DefaultDialog), + 3109: (DG_TIER, Start, (CogQuest, Anywhere, 8, 'ym'), Any, ToonHQ, Any, NA, DefaultDialog), + 3110: (DG_TIER, Start, (CogQuest, Anywhere, 6, 'mm'), Any, ToonHQ, Any, NA, DefaultDialog), + 3111: (DG_TIER, Start, (CogQuest, Anywhere, 6, 'ds'), Any, ToonHQ, Any, NA, DefaultDialog), + 3112: (DG_TIER, Start, (CogQuest, Anywhere, 6, 'hh'), Any, ToonHQ, Any, NA, DefaultDialog), + 3113: (DG_TIER, Start, (CogQuest, Anywhere, 10, 'cc'), Any, ToonHQ, Any, NA, DefaultDialog), + 3114: (DG_TIER, Start, (CogQuest, Anywhere, 8, 'tm'), Any, ToonHQ, Any, NA, DefaultDialog), + 3115: (DG_TIER, Start, (CogQuest, Anywhere, 8, 'nd'), Any, ToonHQ, Any, NA, DefaultDialog), + 3116: (DG_TIER, Start, (CogQuest, Anywhere, 6, 'gh'), Any, ToonHQ, Any, NA, DefaultDialog), + 3117: (DG_TIER, Start, (CogQuest, Anywhere, 6, 'ms'), Any, ToonHQ, Any, NA, DefaultDialog), + 3118: (DG_TIER, Start, (CogQuest, Anywhere, 6, 'tf'), Any, ToonHQ, Any, NA, DefaultDialog), + 3119: (DG_TIER, Start, (CogQuest, Anywhere, 10, 'sc'), Any, ToonHQ, Any, NA, DefaultDialog), + 3120: (DG_TIER, Start, (CogQuest, Anywhere, 8, 'pp'), Any, ToonHQ, Any, NA, DefaultDialog), + 3121: (DG_TIER, Start, (CogQuest, Anywhere, 8, 'tw'), Any, ToonHQ, Any, NA, DefaultDialog), + 3122: (DG_TIER, Start, (CogQuest, Anywhere, 6, 'bc'), Any, ToonHQ, Any, NA, DefaultDialog), + 3123: (DG_TIER, Start, (CogQuest, Anywhere, 6, 'nc'), Any, ToonHQ, Any, NA, DefaultDialog), + 3124: (DG_TIER, Start, (CogQuest, Anywhere, 6, 'mb'), Any, ToonHQ, Any, NA, DefaultDialog), + 3125: (DG_TIER, Start, (CogQuest, Anywhere, 10, 'bf'), Any, ToonHQ, Any, NA, DefaultDialog), + 3126: (DG_TIER, Start, (CogQuest, Anywhere, 8, 'b'), Any, ToonHQ, Any, NA, DefaultDialog), + 3127: (DG_TIER, Start, (CogQuest, Anywhere, 8, 'dt'), Any, ToonHQ, Any, NA, DefaultDialog), + 3128: (DG_TIER, Start, (CogQuest, Anywhere, 6, 'ac'), Any, ToonHQ, Any, NA, DefaultDialog), + 3129: (DG_TIER, Start, (CogQuest, Anywhere, 6, 'bs'), Any, ToonHQ, Any, NA, DefaultDialog), + 3130: (DG_TIER, Start, (CogQuest, Anywhere, 6, 'sd'), Any, ToonHQ, Any, NA, DefaultDialog), + 3131: (DG_TIER, Start, (CogLevelQuest, Anywhere, 10, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 3132: (DG_TIER, Start, (CogLevelQuest, Anywhere, 15, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 3133: (DG_TIER, Start, (CogLevelQuest, Anywhere, 8, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 3134: (DG_TIER, Start, (CogLevelQuest, Anywhere, 12, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 3135: (DG_TIER, Start, (CogLevelQuest, Anywhere, 4, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 3136: (DG_TIER, Start, (CogLevelQuest, Anywhere, 6, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 3137: (DG_TIER, Start, (CogLevelQuest, Anywhere, 8, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 3138: (DG_TIER, Start, (CogLevelQuest, Anywhere, 12, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 3139: (DG_TIER, Start, (CogTrackQuest, ToontownGlobals.DaisyGardens, 6, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 3140: (DG_TIER, Start, (CogTrackQuest, ToontownGlobals.DaisyGardens, 6, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 3141: (DG_TIER, Start, (CogTrackQuest, ToontownGlobals.DaisyGardens, 6, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 3142: (DG_TIER, Start, (CogTrackQuest, ToontownGlobals.DaisyGardens, 6, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 3143: (DG_TIER, Start, (CogTrackQuest, ToontownGlobals.DaisyGardens, 10, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 3144: (DG_TIER, Start, (CogTrackQuest, ToontownGlobals.DaisyGardens, 10, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 3145: (DG_TIER, Start, (CogTrackQuest, ToontownGlobals.DaisyGardens, 10, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 3146: (DG_TIER, Start, (CogTrackQuest, ToontownGlobals.DaisyGardens, 10, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 3147: (DG_TIER, Start, (CogTrackQuest, Anywhere, 14, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 3148: (DG_TIER, Start, (CogTrackQuest, Anywhere, 14, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 3149: (DG_TIER, Start, (CogTrackQuest, Anywhere, 14, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 3150: (DG_TIER, Start, (CogTrackQuest, Anywhere, 14, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 3151: (DG_TIER, Start, (BuildingQuest, Anywhere, 1, Any, 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 3152: (DG_TIER, Start, (BuildingQuest, Anywhere, 2, Any, 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 3153: (DG_TIER, Start, (BuildingQuest, Anywhere, 3, Any, 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 3154: (DG_TIER, Start, (BuildingQuest, Anywhere, 4, Any, 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 3155: (DG_TIER, Start, (BuildingQuest, Anywhere, 2, 'm', 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 3156: (DG_TIER, Start, (BuildingQuest, Anywhere, 2, 's', 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 3157: (DG_TIER, Start, (BuildingQuest, Anywhere, 2, 'c', 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 3158: (DG_TIER, Start, (BuildingQuest, Anywhere, 2, 'l', 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 3200: (DG_TIER, Start, (VisitQuest,), Any, 5101, NA, 3201, TTLocalizer.QuestDialogDict[3200]), + 3201: (DG_TIER, Start, (DeliverItemQuest, 5001), 5101, 5206, NA, 3203, TTLocalizer.QuestDialogDict[3201]), + 3203: (DG_TIER, Cont, (RecoverItemQuest, ToontownGlobals.DaisyGardens, 1, 5002, VeryHard, Any), Same, Same, 100, NA, TTLocalizer.QuestDialogDict[3203]), + 3204: (DG_TIER, Start, (VisitQuest,), Any, 5106, NA, 3205, TTLocalizer.QuestDialogDict[3204]), + 3205: (DG_TIER, Start, (RecoverItemQuest, Anywhere, 1, 5003, Medium, 'b'), 5106, Same, 100, NA, TTLocalizer.QuestDialogDict[3205]), + 3206: (DG_TIER, Start, (VisitQuest,), Any, 5107, NA, 3207, TTLocalizer.QuestDialogDict[3206]), + 3207: (DG_TIER, Start, (RecoverItemQuest, ToontownGlobals.DaisyGardens, 10, 5004, VeryEasy, 'dt'), 5107, Same, 101, NA, TTLocalizer.QuestDialogDict[3207]), + 3208: (DG_TIER, OBSOLETE, (CogQuest, ToontownGlobals.DaisyGardens, 10, 'cc'), Any, ToonHQ, NA, 3209, TTLocalizer.QuestDialogDict[3208]), + 3209: (DG_TIER, OBSOLETE, (CogQuest, ToontownGlobals.DaisyGardens, 10, 'tm'), Same, Same, 202, NA, TTLocalizer.QuestDialogDict[3209]), + 3247: (DG_TIER, OBSOLETE, (CogQuest, ToontownGlobals.DaisyGardens, 20, 'b'), Any, ToonHQ, 202, NA, TTLocalizer.QuestDialogDict[3247]), + 3210: (DG_TIER, Start, (DeliverGagQuest, 10, ToontownBattleGlobals.SQUIRT_TRACK, 0), Any, 5207, NA, 3211, TTLocalizer.QuestDialogDict[3210]), + 3211: (DG_TIER, Cont, (CogQuest, 5200, 20, Any), Same, Same, 100, NA, TTLocalizer.QuestDialogDict[3211]), + 3212: (DG_TIER, OBSOLETE, (VisitQuest,), Any, 5208, NA, 3213, TTLocalizer.QuestDialogDict[3212]), + 3213: (DG_TIER, OBSOLETE, (RecoverItemQuest, ToontownGlobals.DaisyGardens, 1, 5005, VeryHard, Any), 5208, Same, NA, 3214, TTLocalizer.QuestDialogDict[3213]), + 3214: (DG_TIER, OBSOLETE, (RecoverItemQuest, ToontownGlobals.DaisyGardens, 1, 5006, VeryHard, Any), Same, Same, NA, 3215, TTLocalizer.QuestDialogDict[3214]), + 3215: (DG_TIER, OBSOLETE, (RecoverItemQuest, ToontownGlobals.DaisyGardens, 1, 5007, VeryHard, Any), Same, Same, NA, 3216, TTLocalizer.QuestDialogDict[3215]), + 3216: (DG_TIER, OBSOLETE, (RecoverItemQuest, ToontownGlobals.DaisyGardens, 1, 5008, VeryHard, Any), Same, Same, 202, NA, TTLocalizer.QuestDialogDict[3216]), + 3217: (DG_TIER, Start, (RecoverItemQuest, Anywhere, 1, 5010, VeryEasy, 'nd'), ToonHQ, ToonHQ, NA, 3218, TTLocalizer.QuestDialogDict[3217]), + 3218: (DG_TIER, Cont, (RecoverItemQuest, Anywhere, 1, 5010, VeryHard, 'gh'), Same, Same, NA, 3219, TTLocalizer.QuestDialogDict[3218]), + 3219: (DG_TIER, Cont, (RecoverItemQuest, Anywhere, 1, 5010, Easy, 'ms'), Same, Same, 101, NA, TTLocalizer.QuestDialogDict[3219]), + 3244: (DG_TIER, Start, (RecoverItemQuest, Anywhere, 1, 5010, VeryEasy, 'ac'), ToonHQ, ToonHQ, NA, 3245, TTLocalizer.QuestDialogDict[3244]), + 3245: (DG_TIER, Cont, (RecoverItemQuest, Anywhere, 1, 5010, VeryHard, 'bs'), Same, Same, NA, 3246, TTLocalizer.QuestDialogDict[3245]), + 3246: (DG_TIER, Cont, (RecoverItemQuest, Anywhere, 1, 5010, VeryHard, 'sd'), Same, Same, 101, NA, TTLocalizer.QuestDialogDict[3246]), + 3220: (DG_TIER, Start, (VisitQuest,), Any, 5207, NA, 3221, TTLocalizer.QuestDialogDict[3220]), + 3221: (DG_TIER, Start, (CogQuest, ToontownGlobals.DaisyGardens, 20, Any), 5207, Same, 100, NA, TTLocalizer.QuestDialogDict[3221]), + 3222: (DG_TIER, Start, (BuildingQuest, Anywhere, 2, Any, 1, 0), ToonHQ, ToonHQ, NA, 3223, TTLocalizer.QuestDialogDict[3222]), + 3223: (DG_TIER, Cont, (BuildingQuest, Anywhere, 2, Any, 2, 0), Same, Same, NA, 3224, TTLocalizer.QuestDialogDict[3223]), + 3224: (DG_TIER, Cont, (BuildingQuest, Anywhere, 2, Any, 3, 0), Same, Same, 501, NA, TTLocalizer.QuestDialogDict[3224]), + 3225: (DG_TIER, Start, (VisitQuest,), Any, 5108, NA, (3226, 3227, 3228, 3229, 3230, 3231, 3232, 3233, 3234), TTLocalizer.QuestDialogDict[3225]), + 3226: (DG_TIER, Start, (DeliverItemQuest, 5011), 5108, 5201, NA, 3235, TTLocalizer.QuestDialog_3225), + 3227: (DG_TIER, Start, (DeliverItemQuest, 5011), 5108, 5203, NA, 3235, TTLocalizer.QuestDialog_3225), + 3228: (DG_TIER, Start, (DeliverItemQuest, 5011), 5108, 5204, NA, 3235, TTLocalizer.QuestDialog_3225), + 3229: (DG_TIER, Start, (DeliverItemQuest, 5011), 5108, 5205, NA, 3235, TTLocalizer.QuestDialog_3225), + 3230: (DG_TIER, Start, (DeliverItemQuest, 5011), 5108, 5102, NA, 3235, TTLocalizer.QuestDialog_3225), + 3231: (DG_TIER, Start, (DeliverItemQuest, 5011), 5108, 5103, NA, 3235, TTLocalizer.QuestDialog_3225), + 3232: (DG_TIER, Start, (DeliverItemQuest, 5011), 5108, 5104, NA, 3235, TTLocalizer.QuestDialog_3225), + 3233: (DG_TIER, Start, (DeliverItemQuest, 5011), 5108, 5105, NA, 3235, TTLocalizer.QuestDialog_3225), + 3234: (DG_TIER, Start, (DeliverItemQuest, 5011), 5108, 5207, NA, 3235, TTLocalizer.QuestDialog_3225), + 3235: (DG_TIER, Cont, (CogQuest, ToontownGlobals.DaisyGardens, 10, Any), Same, 5108, 100, NA, TTLocalizer.QuestDialogDict[3235]), + 3236: (DG_TIER, OBSOLETE, (BuildingQuest, Anywhere, 3, 'l', 2, 0), Any, ToonHQ, NA, 3237, TTLocalizer.QuestDialogDict[3236]), + 3237: (DG_TIER, OBSOLETE, (BuildingQuest, Anywhere, 3, 's', 2, 0), Same, Same, 702, NA, TTLocalizer.QuestDialogDict[3237]), + 3238: (DG_TIER, Start, (RecoverItemQuest, Anywhere, 1, 2, VeryEasy, 'm'), Any, ToonHQ, NA, 3239, TTLocalizer.QuestDialogDict[3238]), + 3239: (DG_TIER, Cont, (RecoverItemQuest, Anywhere, 1, 5012, Hard, 'm'), Same, Same, 302, NA, TTLocalizer.QuestDialogDict[3239]), + 3242: (DG_TIER, Start, (RecoverItemQuest, Anywhere, 1, 2, VeryEasy, 'le'), Any, ToonHQ, NA, 3243, TTLocalizer.QuestDialogDict[3242]), + 3243: (DG_TIER, Cont, (RecoverItemQuest, Anywhere, 1, 5012, Hard, 'le'), Same, Same, 302, NA, TTLocalizer.QuestDialogDict[3243]), + 3240: (DG_TIER, OBSOLETE, (RecoverItemQuest, Anywhere, 1, 5009, Hard, 'le'), Any, 5103, 102, NA, TTLocalizer.QuestDialogDict[3240]), + 3241: (DG_TIER, OBSOLETE, (BuildingQuest, Anywhere, 5, Any, 3, 0), Any, ToonHQ, 102, NA, TTLocalizer.QuestDialogDict[3241]), + 3250: (DG_TIER, Start, (VisitQuest,), Any, 5317, NA, 3251, TTLocalizer.QuestDialogDict[3250]), + 3251: (DG_TIER, Start, (CogTrackQuest, ToontownGlobals.SellbotHQ, 5, 's'), 5317, Same, NA, 3252, TTLocalizer.QuestDialogDict[3251]), + 3252: (DG_TIER, Cont, (VisitQuest,), Same, 5311, NA, 3253, TTLocalizer.QuestDialogDict[3252]), + 3253: (DG_TIER, Cont, (RecoverItemQuest, ToontownGlobals.SellbotHQ, 1, 5013, Medium, 's', 'track'), Same, Same, NA, 3254, TTLocalizer.QuestDialogDict[3253]), + 3254: (DG_TIER, Cont, (DeliverItemQuest, 5013), Same, 5317, 202, NA, TTLocalizer.QuestDialogDict[3254]), + 3255: (DG_TIER, Start, (VisitQuest,), Any, 5314, NA, 3258, TTLocalizer.QuestDialogDict[3255]), + 3256: (DG_TIER, Start, (VisitQuest,), Any, 5315, NA, 3258, TTLocalizer.QuestDialogDict[3256]), + 3257: (DG_TIER, Start, (VisitQuest,), Any, 5316, NA, 3258, TTLocalizer.QuestDialogDict[3257]), + 3258: (DG_TIER, Cont, (RecoverItemQuest, ToontownGlobals.SellbotHQ, 1, 5014, VeryEasy, 's', 'track'), Same, Same, NA, 3259, TTLocalizer.QuestDialogDict[3258]), + 3259: (DG_TIER, Cont, (RecoverItemQuest, ToontownGlobals.SellbotHQ, 1, 5015, Easy, 's', 'track'), Same, Same, NA, 3260, TTLocalizer.QuestDialogDict[3259]), + 3260: (DG_TIER, Cont, (RecoverItemQuest, ToontownGlobals.SellbotHQ, 1, 5016, Easy, 's', 'track'), Same, Same, NA, 3261, TTLocalizer.QuestDialogDict[3260]), + 3261: (DG_TIER, Cont, (RecoverItemQuest, ToontownGlobals.SellbotHQ, 1, 5017, Medium, 's', 'track'), Same, Same, 102, NA, TTLocalizer.QuestDialogDict[3261]), + 3262: (DG_TIER, Start, (VisitQuest,), Any, 5313, NA, 3263, TTLocalizer.QuestDialogDict[3262]), + 3263: (DG_TIER, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 1), 5313, 5313, 702, NA, TTLocalizer.QuestDialogDict[3263]), + 3500: (DG_TIER, Start, (CogQuest, ToontownGlobals.DaisyGardens, 25, Any), Any, ToonHQ, NA, 3501, DefaultDialog), + 3501: (DG_TIER, Cont, (DeliverItemQuest, 1000), Any, 5007, 1000, NA, DefaultDialog), + 3502: (DG_TIER, Start, (RescueQuest, InVP, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 3503: (DG_TIER, Start, (RescueQuest, InFO, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 4001: (MM_TIER, Start, (TrackChoiceQuest, ToontownBattleGlobals.TRAP_TRACK, ToontownBattleGlobals.HEAL_TRACK), Any, ToonHQ, 400, NA, TTLocalizer.QuestDialogDict[4001]), + 4002: (MM_TIER, Start, (TrackChoiceQuest, ToontownBattleGlobals.TRAP_TRACK, ToontownBattleGlobals.SOUND_TRACK), Any, ToonHQ, 400, NA, TTLocalizer.QuestDialogDict[4002]), + 4010: (MM_TIER, Start, (CogQuest, Anywhere, 16, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4011: (MM_TIER, Start, (CogQuest, Anywhere, 18, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4012: (MM_TIER, Start, (CogQuest, Anywhere, 20, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4013: (MM_TIER, Start, (CogQuest, Anywhere, 22, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4014: (MM_TIER, Start, (CogQuest, Anywhere, 24, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4015: (MM_TIER, Start, (CogQuest, Anywhere, 26, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4016: (MM_TIER, Start, (CogQuest, Anywhere, 28, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4017: (MM_TIER, Start, (CogQuest, Anywhere, 30, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4018: (MM_TIER, Start, (CogQuest, Anywhere, 32, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4019: (MM_TIER, Start, (CogQuest, Anywhere, 34, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4020: (MM_TIER, Start, (CogLevelQuest, Anywhere, 20, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 4021: (MM_TIER, Start, (CogLevelQuest, Anywhere, 25, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 4022: (MM_TIER, Start, (CogLevelQuest, Anywhere, 16, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 4023: (MM_TIER, Start, (CogLevelQuest, Anywhere, 20, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 4024: (MM_TIER, Start, (CogLevelQuest, Anywhere, 10, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 4025: (MM_TIER, Start, (CogLevelQuest, Anywhere, 20, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 4026: (MM_TIER, Start, (CogLevelQuest, Anywhere, 16, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 4027: (MM_TIER, Start, (CogLevelQuest, Anywhere, 18, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 4028: (MM_TIER, Start, (CogLevelQuest, Anywhere, 20, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 4029: (MM_TIER, Start, (CogLevelQuest, Anywhere, 24, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 4030: (MM_TIER, Start, (CogQuest, Anywhere, 45, Any), Any, ToonHQ, NA, 4031, DefaultDialog), + 4031: (MM_TIER, Cont, (DeliverItemQuest, 1000), Any, 4008, 1000, NA, DefaultDialog), + 4040: (MM_TIER, Start, (CogQuest, ToontownGlobals.SellbotHQ, 6, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4041: (MM_TIER, Start, (CogQuest, ToontownGlobals.SellbotHQ, 6, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4042: (MM_TIER, Start, (CogLevelQuest, ToontownGlobals.SellbotHQ, 3, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 4043: (MM_TIER, Start, (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 4044: (MM_TIER, Start, (RescueQuest, InVP, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 4045: (MM_TIER, Start, (RescueQuest, InFO, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 4101: (MM_TIER + 1, Start, (CogQuest, ToontownGlobals.MinniesMelodyland, 16, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4102: (MM_TIER + 1, Start, (CogQuest, ToontownGlobals.MinniesMelodyland, 18, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4103: (MM_TIER + 1, Start, (CogQuest, ToontownGlobals.MinniesMelodyland, 20, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4104: (MM_TIER + 1, Start, (CogQuest, ToontownGlobals.MinniesMelodyland, 24, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4105: (MM_TIER + 1, Start, (CogQuest, Anywhere, 28, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4106: (MM_TIER + 1, Start, (CogQuest, Anywhere, 32, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4107: (MM_TIER + 1, Start, (CogQuest, Anywhere, 20, 'f'), Any, ToonHQ, Any, NA, DefaultDialog), + 4108: (MM_TIER + 1, Start, (CogQuest, Anywhere, 16, 'p'), Any, ToonHQ, Any, NA, DefaultDialog), + 4109: (MM_TIER + 1, Start, (CogQuest, Anywhere, 16, 'ym'), Any, ToonHQ, Any, NA, DefaultDialog), + 4110: (MM_TIER + 1, Start, (CogQuest, Anywhere, 12, 'mm'), Any, ToonHQ, Any, NA, DefaultDialog), + 4111: (MM_TIER + 1, Start, (CogQuest, Anywhere, 12, 'ds'), Any, ToonHQ, Any, NA, DefaultDialog), + 4112: (MM_TIER + 1, Start, (CogQuest, Anywhere, 12, 'hh'), Any, ToonHQ, Any, NA, DefaultDialog), + 4113: (MM_TIER + 1, Start, (CogQuest, Anywhere, 20, 'cc'), Any, ToonHQ, Any, NA, DefaultDialog), + 4114: (MM_TIER + 1, Start, (CogQuest, Anywhere, 16, 'tm'), Any, ToonHQ, Any, NA, DefaultDialog), + 4115: (MM_TIER + 1, Start, (CogQuest, Anywhere, 16, 'nd'), Any, ToonHQ, Any, NA, DefaultDialog), + 4116: (MM_TIER + 1, Start, (CogQuest, Anywhere, 12, 'gh'), Any, ToonHQ, Any, NA, DefaultDialog), + 4117: (MM_TIER + 1, Start, (CogQuest, Anywhere, 12, 'ms'), None, ToonHQ, Any, NA, DefaultDialog), + 4118: (MM_TIER + 1, Start, (CogQuest, Anywhere, 12, 'tf'), None, ToonHQ, Any, NA, DefaultDialog), + 4119: (MM_TIER + 1, Start, (CogQuest, Anywhere, 20, 'sc'), Any, ToonHQ, Any, NA, DefaultDialog), + 4120: (MM_TIER + 1, Start, (CogQuest, Anywhere, 16, 'pp'), Any, ToonHQ, Any, NA, DefaultDialog), + 4121: (MM_TIER + 1, Start, (CogQuest, Anywhere, 16, 'tw'), Any, ToonHQ, Any, NA, DefaultDialog), + 4122: (MM_TIER + 1, Start, (CogQuest, Anywhere, 12, 'bc'), Any, ToonHQ, Any, NA, DefaultDialog), + 4123: (MM_TIER + 1, Start, (CogQuest, Anywhere, 12, 'nc'), Any, ToonHQ, Any, NA, DefaultDialog), + 4124: (MM_TIER + 1, Start, (CogQuest, Anywhere, 12, 'mb'), Any, ToonHQ, Any, NA, DefaultDialog), + 4125: (MM_TIER + 1, Start, (CogQuest, Anywhere, 20, 'bf'), Any, ToonHQ, Any, NA, DefaultDialog), + 4126: (MM_TIER + 1, Start, (CogQuest, Anywhere, 16, 'b'), Any, ToonHQ, Any, NA, DefaultDialog), + 4127: (MM_TIER + 1, Start, (CogQuest, Anywhere, 16, 'dt'), Any, ToonHQ, Any, NA, DefaultDialog), + 4128: (MM_TIER + 1, Start, (CogQuest, Anywhere, 12, 'ac'), Any, ToonHQ, Any, NA, DefaultDialog), + 4129: (MM_TIER + 1, Start, (CogQuest, Anywhere, 12, 'bs'), Any, ToonHQ, Any, NA, DefaultDialog), + 4130: (MM_TIER + 1, Start, (CogQuest, Anywhere, 12, 'sd'), Any, ToonHQ, Any, NA, DefaultDialog), + 4131: (MM_TIER + 1, Start, (CogLevelQuest, Anywhere, 20, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 4132: (MM_TIER + 1, Start, (CogLevelQuest, Anywhere, 25, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 4133: (MM_TIER + 1, Start, (CogLevelQuest, Anywhere, 16, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 4134: (MM_TIER + 1, Start, (CogLevelQuest, Anywhere, 20, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 4135: (MM_TIER + 1, Start, (CogLevelQuest, Anywhere, 10, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 4136: (MM_TIER + 1, Start, (CogLevelQuest, Anywhere, 20, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 4137: (MM_TIER + 1, Start, (CogLevelQuest, Anywhere, 16, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 4138: (MM_TIER + 1, Start, (CogLevelQuest, Anywhere, 24, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 4139: (MM_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.MinniesMelodyland, 15, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 4140: (MM_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.MinniesMelodyland, 15, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 4141: (MM_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.MinniesMelodyland, 15, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 4142: (MM_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.MinniesMelodyland, 15, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 4143: (MM_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.MinniesMelodyland, 24, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 4144: (MM_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.MinniesMelodyland, 24, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 4145: (MM_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.MinniesMelodyland, 24, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 4146: (MM_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.MinniesMelodyland, 24, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 4147: (MM_TIER + 1, Start, (CogTrackQuest, Anywhere, 30, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 4148: (MM_TIER + 1, Start, (CogTrackQuest, Anywhere, 30, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 4149: (MM_TIER + 1, Start, (CogTrackQuest, Anywhere, 30, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 4150: (MM_TIER + 1, Start, (CogTrackQuest, Anywhere, 30, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 4151: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 1, Any, 3, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 4152: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 2, Any, 3, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 4153: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 3, Any, 3, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 4154: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 4, Any, 3, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 4155: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 3, 'm', 3, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 4156: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 3, 's', 3, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 4157: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 3, 'c', 3, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 4158: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 3, 'l', 3, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 4160: (MM_TIER + 1, Start, (CogQuest, ToontownGlobals.SellbotHQ, 10, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4161: (MM_TIER + 1, Start, (CogQuest, ToontownGlobals.SellbotHQ, 12, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4162: (MM_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.SellbotHQ, 6, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 4163: (MM_TIER + 1, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 4164: (MM_TIER + 1, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 4165: (MM_TIER + 1, Start, (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 4166: (MM_TIER + 1, Start, (ForemanQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 4167: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 1, Any, 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 4168: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 2, Any, 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 4169: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 3, Any, 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 4170: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 4, Any, 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 4171: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 3, 's', 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 4172: (MM_TIER + 1, Start, (BuildingQuest, Anywhere, 3, 'l', 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 4200: (MM_TIER + 1, Start, (VisitQuest,), Any, 4101, NA, 4201, TTLocalizer.QuestDialogDict[4200]), + 4201: (MM_TIER + 1, Start, (VisitQuest,), 4101, 4201, NA, 4202, TTLocalizer.QuestDialogDict[4201]), + 4202: (MM_TIER + 1, Cont, (DeliverItemQuest, 4001), Same, 4101, NA, 4203, TTLocalizer.QuestDialogDict[4202]), + 4203: (MM_TIER + 1, Cont, (VisitQuest,), Same, 4301, NA, 4204, TTLocalizer.QuestDialogDict[4203]), + 4204: (MM_TIER + 1, Cont, (CogQuest, ToontownGlobals.MinniesMelodyland, 10, Any), Same, Same, NA, 4205, TTLocalizer.QuestDialogDict[4204]), + 4205: (MM_TIER + 1, Cont, (DeliverItemQuest, 4002), Same, 4101, NA, 4206, TTLocalizer.QuestDialogDict[4205]), + 4206: (MM_TIER + 1, Cont, (VisitQuest,), Same, 4102, NA, 4207, TTLocalizer.QuestDialogDict[4206]), + 4207: (MM_TIER + 1, Cont, (VisitQuest,), Same, 4108, NA, 4208, TTLocalizer.QuestDialogDict[4207]), + 4208: (MM_TIER + 1, Cont, (DeliverGagQuest, 1, ToontownBattleGlobals.THROW_TRACK, 4), Same, Same, NA, 4209, TTLocalizer.QuestDialogDict[4208]), + 4209: (MM_TIER + 1, Cont, (DeliverItemQuest, 4003), Same, 4102, NA, 4210, TTLocalizer.QuestDialogDict[4209]), + 4210: (MM_TIER + 1, Cont, (DeliverItemQuest, 4004), Same, 4101, 203, NA, TTLocalizer.QuestDialogDict[4210]), + 4211: (MM_TIER + 1, Start, (VisitQuest,), ToonHQ, 4103, NA, 4212, TTLocalizer.QuestDialogDict[4211]), + 4212: (MM_TIER + 1, Start, (CogQuest, ToontownGlobals.MinniesMelodyland, 10, 'nc'), 4103, Same, NA, 4213, TTLocalizer.QuestDialogDict[4212]), + 4213: (MM_TIER + 1, Cont, (CogTrackQuest, ToontownGlobals.MinniesMelodyland, 20, 'm'), Same, Same, NA, 4214, TTLocalizer.QuestDialogDict[4213]), + 4214: (MM_TIER + 1, Cont, (BuildingQuest, Anywhere, 1, 'm', Any, 0), Same, Same, 303, NA, TTLocalizer.QuestDialogDict[4214]), + 4215: (MM_TIER + 1, Start, (VisitQuest,), Any, 4302, NA, 4216, TTLocalizer.QuestDialogDict[4215]), + 4216: (MM_TIER + 1, Start, (RecoverItemQuest, Anywhere, 1, 4005, VeryHard, 'gh'), 4302, Same, NA, 4217, TTLocalizer.QuestDialogDict[4216]), + 4217: (MM_TIER + 1, Cont, (DeliverItemQuest, 4005), Same, 4203, NA, 4218, TTLocalizer.QuestDialogDict[4217]), + 4218: (MM_TIER + 1, Cont, (VisitQuest,), Any, 4302, NA, 4219, TTLocalizer.QuestDialogDict[4218]), + 4219: (MM_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 4006, VeryHard, 'gh'), Same, Same, NA, 4220, TTLocalizer.QuestDialogDict[4219]), + 4220: (MM_TIER + 1, Cont, (DeliverItemQuest, 4006), Same, 4308, NA, 4221, TTLocalizer.QuestDialogDict[4220]), + 4221: (MM_TIER + 1, Cont, (VisitQuest,), Any, 4302, NA, 4222, TTLocalizer.QuestDialogDict[4221]), + 4222: (MM_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 4007, VeryHard, 'gh'), Same, Same, NA, 4223, TTLocalizer.QuestDialogDict[4222]), + 4223: (MM_TIER + 1, Cont, (DeliverItemQuest, 4007), Same, 4202, NA, 4224, TTLocalizer.QuestDialogDict[4223]), + 4224: (MM_TIER + 1, Cont, (VisitQuest,), Any, 4302, 703, NA, TTLocalizer.QuestDialogDict[4224]), + 4500: (MM_TIER + 1, Start, (CogQuest, ToontownGlobals.MinniesMelodyland, 40, Any), Any, ToonHQ, NA, 4501, DefaultDialog), + 4501: (MM_TIER + 1, Cont, (DeliverItemQuest, 1000), Any, 4008, 1000, NA, DefaultDialog), + 4502: (MM_TIER + 1, Start, (RescueQuest, InVP, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 4503: (MM_TIER + 1, Start, (RescueQuest, InFO, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 902: (MM_TIER + 2, Start, (VisitQuest,), Any, 4303, NA, 4903, TTLocalizer.QuestDialogDict[902]), + 4903: (MM_TIER + 2, Start, (DeliverItemQuest, 4008), 4303, 4109, NA, 4904, TTLocalizer.QuestDialogDict[4903]), + 4904: (MM_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 1, 4009, VeryHard, AnyFish), Same, Same, NA, 4905, TTLocalizer.QuestDialogDict[4904]), + 4905: (MM_TIER + 2, Cont, (BuildingQuest, Anywhere, 1, Any, 1, 0), Same, Same, NA, 4906, TTLocalizer.QuestDialogDict[4905]), + 4906: (MM_TIER + 2, Cont, (DeliverItemQuest, 4010), Same, 4303, NA, 4907, TTLocalizer.QuestDialogDict[4906]), + 4907: (MM_TIER + 2, Cont, (VisitQuest,), Same, 4208, NA, 4908, TTLocalizer.QuestDialogDict[4907]), + 4908: (MM_TIER + 2, Cont, (BuildingQuest, Anywhere, 1, Any, 2, 0), Same, Same, NA, 4909, TTLocalizer.QuestDialogDict[4908]), + 4909: (MM_TIER + 2, Cont, (BuildingQuest, Anywhere, 1, Any, 3, 0), Same, Same, NA, 4910, TTLocalizer.QuestDialogDict[4909]), + 4910: (MM_TIER + 2, Cont, (DeliverItemQuest, 4011), Same, 4303, 900, NA, TTLocalizer.QuestDialogDict[4910]), + 4810: (MM_TIER + 2, Start, (CogQuest, Anywhere, 16, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4811: (MM_TIER + 2, Start, (CogQuest, Anywhere, 18, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4812: (MM_TIER + 2, Start, (CogQuest, Anywhere, 20, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4813: (MM_TIER + 2, Start, (CogQuest, Anywhere, 22, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4814: (MM_TIER + 2, Start, (CogQuest, Anywhere, 24, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4815: (MM_TIER + 2, Start, (CogQuest, Anywhere, 26, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4816: (MM_TIER + 2, Start, (CogQuest, Anywhere, 28, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4817: (MM_TIER + 2, Start, (CogQuest, Anywhere, 30, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4818: (MM_TIER + 2, Start, (CogQuest, Anywhere, 32, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4819: (MM_TIER + 2, Start, (CogQuest, Anywhere, 34, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4820: (MM_TIER + 2, Start, (CogLevelQuest, Anywhere, 20, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 4821: (MM_TIER + 2, Start, (CogLevelQuest, Anywhere, 25, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 4822: (MM_TIER + 2, Start, (CogLevelQuest, Anywhere, 16, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 4823: (MM_TIER + 2, Start, (CogLevelQuest, Anywhere, 20, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 4824: (MM_TIER + 2, Start, (CogLevelQuest, Anywhere, 10, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 4825: (MM_TIER + 2, Start, (CogLevelQuest, Anywhere, 20, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 4826: (MM_TIER + 2, Start, (CogLevelQuest, Anywhere, 16, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 4827: (MM_TIER + 2, Start, (CogLevelQuest, Anywhere, 18, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 4828: (MM_TIER + 2, Start, (CogLevelQuest, Anywhere, 20, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 4829: (MM_TIER + 2, Start, (CogLevelQuest, Anywhere, 24, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 4830: (MM_TIER + 2, Start, (CogQuest, Anywhere, 45, Any), Any, ToonHQ, NA, 4831, DefaultDialog), + 4831: (MM_TIER + 2, Cont, (DeliverItemQuest, 1000), Any, 4008, 1000, NA, DefaultDialog), + 4840: (MM_TIER + 2, Start, (CogQuest, ToontownGlobals.SellbotHQ, 12, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4841: (MM_TIER + 2, Start, (CogQuest, ToontownGlobals.SellbotHQ, 15, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 4842: (MM_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.SellbotHQ, 12, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 4843: (MM_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 10, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 4844: (MM_TIER + 2, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 4845: (MM_TIER + 2, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 4846: (MM_TIER + 2, Start, (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 4847: (MM_TIER + 2, Start, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 3, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 4848: (MM_TIER + 2, Start, (ForemanQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 4849: (MM_TIER + 2, Start, (RescueQuest, InVP, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 4850: (MM_TIER + 2, Start, (RescueQuest, InFO, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 5247: (BR_TIER, Start, (VisitQuest,), Any, 3112, NA, 5248, TTLocalizer.QuestDialogDict[5247]), + 5248: (BR_TIER, Start, (CogLevelQuest, Anywhere, 10, 8), 3112, Same, NA, 5249, TTLocalizer.QuestDialogDict[5248]), + 5249: (BR_TIER, Cont, (RecoverItemQuest, Anywhere, 3, 3018, VeryHard, AnyFish), Same, Same, NA, (5250, 5258, 5259, 5260), TTLocalizer.QuestDialogDict[5249]), + 5250: (BR_TIER, Cont, (BuildingQuest, Anywhere, 2, 'l', 4, 0), Same, Same, NA, (5001, 5002, 5003, 5004, 5005, 5006, 5007, 5008), TTLocalizer.QuestDialogDict[5250]), + 5258: (BR_TIER, Cont, (BuildingQuest, Anywhere, 2, 'c', 4, 0), Same, Same, NA, (5001, 5002, 5003, 5004, 5005, 5006, 5007, 5008), TTLocalizer.QuestDialogDict[5258]), + 5259: (BR_TIER, Cont, (BuildingQuest, Anywhere, 2, 'm', 4, 0), Same, Same, NA, (5001, 5002, 5003, 5004, 5005, 5006, 5007, 5008), TTLocalizer.QuestDialogDict[5259]), + 5260: (BR_TIER, Cont, (BuildingQuest, Anywhere, 2, 's', 4, 0), Same, Same, NA, (5001, 5002, 5003, 5004, 5005, 5006, 5007, 5008), TTLocalizer.QuestDialogDict[5260]), + 5001: (BR_TIER, Cont, (TrackChoiceQuest, ToontownBattleGlobals.SOUND_TRACK, ToontownBattleGlobals.DROP_TRACK), Same, Same, 400, NA, TTLocalizer.TheBrrrghTrackQuestDict), + 5002: (BR_TIER, Cont, (TrackChoiceQuest, ToontownBattleGlobals.SOUND_TRACK, ToontownBattleGlobals.LURE_TRACK), Same, Same, 400, NA, TTLocalizer.TheBrrrghTrackQuestDict), + 5003: (BR_TIER, Cont, (TrackChoiceQuest, ToontownBattleGlobals.HEAL_TRACK, ToontownBattleGlobals.DROP_TRACK), Same, Same, 400, NA, TTLocalizer.TheBrrrghTrackQuestDict), + 5004: (BR_TIER, Cont, (TrackChoiceQuest, ToontownBattleGlobals.HEAL_TRACK, ToontownBattleGlobals.LURE_TRACK), Same, Same, 400, NA, TTLocalizer.TheBrrrghTrackQuestDict), + 5005: (BR_TIER, Cont, (TrackChoiceQuest, ToontownBattleGlobals.TRAP_TRACK, ToontownBattleGlobals.SOUND_TRACK), Same, Same, 400, NA, TTLocalizer.TheBrrrghTrackQuestDict), + 5006: (BR_TIER, Cont, (TrackChoiceQuest, ToontownBattleGlobals.TRAP_TRACK, ToontownBattleGlobals.HEAL_TRACK), Same, Same, 400, NA, TTLocalizer.TheBrrrghTrackQuestDict), + 5007: (BR_TIER, Cont, (TrackChoiceQuest, ToontownBattleGlobals.TRAP_TRACK, ToontownBattleGlobals.DROP_TRACK), Same, Same, 400, NA, TTLocalizer.TheBrrrghTrackQuestDict), + 5008: (BR_TIER, Cont, (TrackChoiceQuest, ToontownBattleGlobals.TRAP_TRACK, ToontownBattleGlobals.LURE_TRACK), Same, Same, 400, NA, TTLocalizer.TheBrrrghTrackQuestDict), + 5020: (BR_TIER, Start, (CogQuest, Anywhere, 36, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5021: (BR_TIER, Start, (CogQuest, Anywhere, 38, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5022: (BR_TIER, Start, (CogQuest, Anywhere, 40, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5023: (BR_TIER, Start, (CogQuest, Anywhere, 42, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5024: (BR_TIER, Start, (CogQuest, Anywhere, 44, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5025: (BR_TIER, Start, (CogQuest, Anywhere, 46, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5026: (BR_TIER, Start, (CogQuest, Anywhere, 48, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5027: (BR_TIER, Start, (CogQuest, Anywhere, 50, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5028: (BR_TIER, Start, (CogQuest, Anywhere, 52, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5029: (BR_TIER, Start, (CogQuest, Anywhere, 54, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5030: (BR_TIER, Start, (CogLevelQuest, Anywhere, 25, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5031: (BR_TIER, Start, (CogLevelQuest, Anywhere, 30, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5032: (BR_TIER, Start, (CogLevelQuest, Anywhere, 35, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 5033: (BR_TIER, Start, (CogLevelQuest, Anywhere, 6, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 5034: (BR_TIER, Start, (CogLevelQuest, Anywhere, 10, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 5035: (BR_TIER, Start, (CogLevelQuest, Anywhere, 20, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 5036: (BR_TIER, Start, (CogLevelQuest, Anywhere, 2, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 5037: (BR_TIER, Start, (CogLevelQuest, Anywhere, 8, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 5038: (BR_TIER, Start, (CogLevelQuest, Anywhere, 10, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 5039: (BR_TIER, Start, (CogLevelQuest, Anywhere, 12, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 5040: (BR_TIER, Start, (CogQuest, ToontownGlobals.TheBrrrgh, 75, Any), Any, ToonHQ, NA, 5041, DefaultDialog), + 5041: (BR_TIER, Cont, (DeliverItemQuest, 1000), Any, 3008, 1000, NA, DefaultDialog), + 5070: (BR_TIER, Start, (CogQuest, ToontownGlobals.SellbotHQ, 20, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5071: (BR_TIER, Start, (CogQuest, ToontownGlobals.SellbotHQ, 22, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5072: (BR_TIER, Start, (CogLevelQuest, ToontownGlobals.SellbotHQ, 15, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 5073: (BR_TIER, Start, (CogLevelQuest, ToontownGlobals.SellbotHQ, 10, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5074: (BR_TIER, Start, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 12, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5075: (BR_TIER, Start, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 8, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 5076: (BR_TIER, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5077: (BR_TIER, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 5078: (BR_TIER, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5079: (BR_TIER, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 5080: (BR_TIER, Start, (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 5081: (BR_TIER, Start, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 5, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5082: (BR_TIER, Start, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 2, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 5083: (BR_TIER, Start, (ForemanQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5084: (BR_TIER, Start, (ForemanQuest, ToontownGlobals.SellbotHQ, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 5085: (BR_TIER, Start, (RescueQuest, InVP, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 5086: (BR_TIER, Start, (RescueQuest, InFO, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 5101: (BR_TIER + 1, Start, (CogQuest, ToontownGlobals.TheBrrrgh, 36, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5102: (BR_TIER + 1, Start, (CogQuest, ToontownGlobals.TheBrrrgh, 40, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5103: (BR_TIER + 1, Start, (CogQuest, ToontownGlobals.TheBrrrgh, 42, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5104: (BR_TIER + 1, Start, (CogQuest, Anywhere, 45, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5105: (BR_TIER + 1, Start, (CogQuest, Anywhere, 50, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5106: (BR_TIER + 1, Start, (CogQuest, Anywhere, 55, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5107: (BR_TIER + 1, Start, (CogQuest, Anywhere, 25, 'p'), Any, ToonHQ, Any, NA, DefaultDialog), + 5108: (BR_TIER + 1, Start, (CogQuest, Anywhere, 20, 'ym'), Any, ToonHQ, Any, NA, DefaultDialog), + 5109: (BR_TIER + 1, Start, (CogQuest, Anywhere, 20, 'mm'), Any, ToonHQ, Any, NA, DefaultDialog), + 5110: (BR_TIER + 1, Start, (CogQuest, Anywhere, 15, 'ds'), Any, ToonHQ, Any, NA, DefaultDialog), + 5111: (BR_TIER + 1, Start, (CogQuest, Anywhere, 15, 'hh'), Any, ToonHQ, Any, NA, DefaultDialog), + 5112: (BR_TIER + 1, Start, (CogQuest, Anywhere, 8, 'cr'), Any, ToonHQ, Any, NA, DefaultDialog), + 5113: (BR_TIER + 1, Start, (CogQuest, Anywhere, 25, 'tm'), Any, ToonHQ, Any, NA, DefaultDialog), + 5114: (BR_TIER + 1, Start, (CogQuest, Anywhere, 20, 'nd'), Any, ToonHQ, Any, NA, DefaultDialog), + 5115: (BR_TIER + 1, Start, (CogQuest, Anywhere, 20, 'gh'), Any, ToonHQ, Any, NA, DefaultDialog), + 5116: (BR_TIER + 1, Start, (CogQuest, Anywhere, 15, 'ms'), Any, ToonHQ, Any, NA, DefaultDialog), + 5117: (BR_TIER + 1, Start, (CogQuest, Anywhere, 15, 'tf'), Any, ToonHQ, Any, NA, DefaultDialog), + 5118: (BR_TIER + 1, Start, (CogQuest, Anywhere, 8, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 5119: (BR_TIER + 1, Start, (CogQuest, Anywhere, 25, 'pp'), Any, ToonHQ, Any, NA, DefaultDialog), + 5120: (BR_TIER + 1, Start, (CogQuest, Anywhere, 20, 'tw'), Any, ToonHQ, Any, NA, DefaultDialog), + 5121: (BR_TIER + 1, Start, (CogQuest, Anywhere, 20, 'bc'), Any, ToonHQ, Any, NA, DefaultDialog), + 5122: (BR_TIER + 1, Start, (CogQuest, Anywhere, 15, 'nc'), Any, ToonHQ, Any, NA, DefaultDialog), + 5123: (BR_TIER + 1, Start, (CogQuest, Anywhere, 15, 'mb'), Any, ToonHQ, Any, NA, DefaultDialog), + 5124: (BR_TIER + 1, Start, (CogQuest, Anywhere, 8, 'ls'), Any, ToonHQ, Any, NA, DefaultDialog), + 5125: (BR_TIER + 1, Start, (CogQuest, Anywhere, 25, 'b'), Any, ToonHQ, Any, NA, DefaultDialog), + 5126: (BR_TIER + 1, Start, (CogQuest, Anywhere, 20, 'dt'), Any, ToonHQ, Any, NA, DefaultDialog), + 5127: (BR_TIER + 1, Start, (CogQuest, Anywhere, 20, 'ac'), Any, ToonHQ, Any, NA, DefaultDialog), + 5128: (BR_TIER + 1, Start, (CogQuest, Anywhere, 15, 'bs'), Any, ToonHQ, Any, NA, DefaultDialog), + 5129: (BR_TIER + 1, Start, (CogQuest, Anywhere, 15, 'sd'), Any, ToonHQ, Any, NA, DefaultDialog), + 5130: (BR_TIER + 1, Start, (CogQuest, Anywhere, 8, 'le'), Any, ToonHQ, Any, NA, DefaultDialog), + 5131: (BR_TIER + 1, Start, (CogLevelQuest, Anywhere, 25, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5132: (BR_TIER + 1, Start, (CogLevelQuest, Anywhere, 30, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5133: (BR_TIER + 1, Start, (CogLevelQuest, Anywhere, 35, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 5134: (BR_TIER + 1, Start, (CogLevelQuest, Anywhere, 6, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 5135: (BR_TIER + 1, Start, (CogLevelQuest, Anywhere, 10, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 5136: (BR_TIER + 1, Start, (CogLevelQuest, Anywhere, 20, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 5137: (BR_TIER + 1, Start, (CogLevelQuest, Anywhere, 2, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 5138: (BR_TIER + 1, Start, (CogLevelQuest, Anywhere, 8, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 5139: (BR_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.TheBrrrgh, 32, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 5140: (BR_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.TheBrrrgh, 32, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 5141: (BR_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.TheBrrrgh, 32, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 5142: (BR_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.TheBrrrgh, 32, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 5143: (BR_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.TheBrrrgh, 40, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 5144: (BR_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.TheBrrrgh, 40, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 5145: (BR_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.TheBrrrgh, 40, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 5146: (BR_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.TheBrrrgh, 40, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 5147: (BR_TIER + 1, Start, (CogTrackQuest, Anywhere, 45, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 5148: (BR_TIER + 1, Start, (CogTrackQuest, Anywhere, 45, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 5149: (BR_TIER + 1, Start, (CogTrackQuest, Anywhere, 45, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 5150: (BR_TIER + 1, Start, (CogTrackQuest, Anywhere, 45, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 5151: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 8, Any, 3, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 5152: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 2, Any, 4, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 5153: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 5, Any, 4, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 5154: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 6, Any, 4, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 5155: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 2, 'm', 4, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 5156: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 2, 's', 4, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 5157: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 2, 'c', 4, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 5158: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 2, 'l', 4, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 5160: (BR_TIER + 1, Start, (CogQuest, ToontownGlobals.SellbotHQ, 22, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5161: (BR_TIER + 1, Start, (CogQuest, ToontownGlobals.SellbotHQ, 25, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5162: (BR_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.SellbotHQ, 16, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 5163: (BR_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.SellbotHQ, 12, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5164: (BR_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 14, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5165: (BR_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 10, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 5166: (BR_TIER + 1, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5167: (BR_TIER + 1, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 5168: (BR_TIER + 1, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5169: (BR_TIER + 1, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 5170: (BR_TIER + 1, Start, (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 5171: (BR_TIER + 1, Start, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 6, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5172: (BR_TIER + 1, Start, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 3, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 5173: (BR_TIER + 1, Start, (ForemanQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5174: (BR_TIER + 1, Start, (ForemanQuest, ToontownGlobals.SellbotHQ, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 5176: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 4, Any, 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5177: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 5, Any, 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5178: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 6, Any, 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5179: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 3, 's', 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5180: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 3, 'l', 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5200: (BR_TIER + 1, Start, (VisitQuest,), Any, 3110, NA, (5201, 5261, 5262, 5263), TTLocalizer.QuestDialogDict[5200]), + 5201: (BR_TIER + 1, Start, (RecoverItemQuest, Anywhere, 1, 3001, VeryHard, 'hh'), 3110, Same, 100, NA, TTLocalizer.QuestDialogDict[5201]), + 5261: (BR_TIER + 1, Start, (RecoverItemQuest, Anywhere, 1, 3001, VeryHard, 'tf'), 3110, Same, 100, NA, TTLocalizer.QuestDialogDict[5261]), + 5262: (BR_TIER + 1, Start, (RecoverItemQuest, Anywhere, 1, 3001, VeryHard, 'mb'), 3110, Same, 100, NA, TTLocalizer.QuestDialogDict[5262]), + 5263: (BR_TIER + 1, Start, (RecoverItemQuest, Anywhere, 1, 3001, VeryHard, 'sd'), 3110, Same, 100, NA, TTLocalizer.QuestDialogDict[5263]), + 5202: (BR_TIER + 1, Start, (VisitQuest,), Any, 3108, NA, 5203, TTLocalizer.QuestDialogDict[5202]), + 5203: (BR_TIER + 1, Start, (RecoverItemQuest, ToontownGlobals.TheBrrrgh, 1, 3002, VeryHard, Any), 3108, Same, NA, 5204, TTLocalizer.QuestDialogDict[5203]), + 5204: (BR_TIER + 1, Cont, (VisitQuest,), Same, 3205, NA, 5205, TTLocalizer.QuestDialogDict[5204]), + 5205: (BR_TIER + 1, Cont, (RecoverItemQuest, ToontownGlobals.TheBrrrgh, 3, 3003, Hard, AnyFish), Same, Same, NA, 5206, TTLocalizer.QuestDialogDict[5205]), + 5206: (BR_TIER + 1, Cont, (VisitQuest,), Same, 3210, NA, 5207, TTLocalizer.QuestDialogDict[5206]), + 5207: (BR_TIER + 1, Cont, (BuildingQuest, Anywhere, 5, Any, 4, 0), Same, Same, NA, 5208, TTLocalizer.QuestDialogDict[5207]), + 5208: (BR_TIER + 1, Cont, (VisitQuest,), Same, 3114, NA, 5209, TTLocalizer.QuestDialogDict[5208]), + 5209: (BR_TIER + 1, Cont, (CogLevelQuest, Anywhere, 20, 7), Same, Same, 204, NA, TTLocalizer.QuestDialogDict[5209]), + 5210: (BR_TIER + 1, Start, (VisitQuest,), Any, 3206, NA, (5211, 5264, 5265, 5266), TTLocalizer.QuestDialogDict[5210]), + 5211: (BR_TIER + 1, Start, (RecoverItemQuest, ToontownGlobals.TheBrrrgh, 1, 3004, Medium, 'le'), 3206, Same, NA, 5212, TTLocalizer.QuestDialogDict[5211]), + 5264: (BR_TIER + 1, Start, (RecoverItemQuest, ToontownGlobals.TheBrrrgh, 1, 3004, Hard, 'ls'), 3206, Same, NA, 5212, TTLocalizer.QuestDialogDict[5264]), + 5265: (BR_TIER + 1, Start, (RecoverItemQuest, ToontownGlobals.TheBrrrgh, 1, 3004, Hard, 'm'), 3206, Same, NA, 5212, TTLocalizer.QuestDialogDict[5265]), + 5266: (BR_TIER + 1, Start, (RecoverItemQuest, ToontownGlobals.TheBrrrgh, 1, 3004, Hard, 'cr'), 3206, Same, NA, 5212, TTLocalizer.QuestDialogDict[5266]), + 5212: (BR_TIER + 1, Cont, (DeliverItemQuest, 3004), Same, 3111, NA, 5213, TTLocalizer.QuestDialogDict[5212]), + 5213: (BR_TIER + 1, Cont, (RecoverItemQuest, ToontownGlobals.TheBrrrgh, 10, 3005, Hard, Any), Same, Same, NA, 5214, TTLocalizer.QuestDialogDict[5213]), + 5214: (BR_TIER + 1, Cont, (VisitQuest,), Same, 3119, NA, 5215, TTLocalizer.QuestDialogDict[5214]), + 5215: (BR_TIER + 1, Cont, (CogLevelQuest, Anywhere, 10, 8), Same, Same, NA, 5216, TTLocalizer.QuestDialogDict[5215]), + 5216: (BR_TIER + 1, Cont, (DeliverItemQuest, 3006), Same, 3206, 704, NA, TTLocalizer.QuestDialogDict[5216]), + 5217: (BR_TIER + 1, Start, (VisitQuest,), Any, 3113, NA, 5218, TTLocalizer.QuestDialogDict[5217]), + 5218: (BR_TIER + 1, Start, (CogQuest, Anywhere, 10, 'm'), 3113, Same, NA, 5219, TTLocalizer.QuestDialogDict[5218]), + 5219: (BR_TIER + 1, Cont, (CogQuest, Anywhere, 10, 'cr'), Same, Same, NA, 5220, TTLocalizer.QuestDialogDict[5219]), + 5220: (BR_TIER + 1, Cont, (CogQuest, Anywhere, 10, 'ls'), Same, Same, NA, 5221, TTLocalizer.QuestDialogDict[5220]), + 5221: (BR_TIER + 1, Cont, (VisitQuest,), Same, 3211, NA, 5222, TTLocalizer.QuestDialogDict[5221]), + 5222: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 2, 3007, Hard, AnyFish), Same, Same, NA, 5223, TTLocalizer.QuestDialogDict[5222]), + 5223: (BR_TIER + 1, Cont, (DeliverItemQuest, 3008), Same, 3113, NA, 5224, TTLocalizer.QuestDialogDict[5223]), + 5224: (BR_TIER + 1, Cont, (CogQuest, Anywhere, 5, 'le'), Same, Same, 502, NA, TTLocalizer.QuestDialogDict[5224]), + 5225: (BR_TIER + 1, Start, (VisitQuest,), Any, 3106, NA, 5226, TTLocalizer.QuestDialogDict[5225]), + 5226: (BR_TIER + 1, Start, (BuildingQuest, Anywhere, 3, 'm', 4, 0), 3106, Same, NA, 5227, TTLocalizer.QuestDialogDict[5226]), + 5227: (BR_TIER + 1, Cont, (VisitQuest,), Same, 3208, NA, 5228, TTLocalizer.QuestDialogDict[5227]), + 5228: (BR_TIER + 1, Cont, (DeliverItemQuest, 3009), Same, 3207, NA, (5229, 5267, 5268, 5269), TTLocalizer.QuestDialogDict[5228]), + 5229: (BR_TIER + 1, Cont, (CogTrackQuest, ToontownGlobals.TheBrrrgh, 8, 'm'), Same, Same, NA, 5230, TTLocalizer.QuestDialogDict[5229]), + 5267: (BR_TIER + 1, Cont, (CogTrackQuest, ToontownGlobals.TheBrrrgh, 8, 's'), Same, Same, NA, 5230, TTLocalizer.QuestDialogDict[5267]), + 5268: (BR_TIER + 1, Cont, (CogTrackQuest, ToontownGlobals.TheBrrrgh, 8, 'l'), Same, Same, NA, 5230, TTLocalizer.QuestDialogDict[5268]), + 5269: (BR_TIER + 1, Cont, (CogTrackQuest, ToontownGlobals.TheBrrrgh, 8, 'c'), Same, Same, NA, (5230, 5270, 5271, 5272), TTLocalizer.QuestDialogDict[5269]), + 5230: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 3010, Hard, 'rb'), Same, Same, NA, 5231, TTLocalizer.QuestDialogDict[5230]), + 5270: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 3010, Hard, 'tbc'), Same, Same, NA, 5231, TTLocalizer.QuestDialogDict[5270]), + 5271: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 3010, Hard, 'mh'), Same, Same, NA, 5231, TTLocalizer.QuestDialogDict[5271]), + 5272: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 3010, Medium, 'bw'), Same, Same, NA, 5231, TTLocalizer.QuestDialogDict[5272]), + 5231: (BR_TIER + 1, Cont, (DeliverItemQuest, 3010), Same, 3208, NA, 5232, TTLocalizer.QuestDialogDict[5231]), + 5232: (BR_TIER + 1, Cont, (VisitQuest,), Same, 3106, NA, 5233, TTLocalizer.QuestDialogDict[5232]), + 5233: (BR_TIER + 1, Cont, (DeliverItemQuest, 3011), Same, 3208, 304, NA, TTLocalizer.QuestDialogDict[5233]), + 5243: (BR_TIER + 1, Start, (VisitQuest,), Any, 3217, NA, 5244, TTLocalizer.QuestDialogDict[5243]), + 5244: (BR_TIER + 1, Start, (RecoverItemQuest, Anywhere, 1, 2007, VeryHard, 'mm'), 3217, Same, NA, 5245, TTLocalizer.QuestDialogDict[5244]), + 5245: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 3017, Hard, AnyFish), Same, Same, NA, 5246, TTLocalizer.QuestDialogDict[5245]), + 5246: (BR_TIER + 1, Cont, (BuildingQuest, ToontownGlobals.TheBrrrgh, 5, Any, 1, 0), Same, Same, 101, NA, TTLocalizer.QuestDialogDict[5246]), + 5251: (BR_TIER + 1, Start, (VisitQuest,), Any, 3134, NA, 5252, TTLocalizer.QuestDialogDict[5251]), + 5252: (BR_TIER + 1, Start, (RecoverItemQuest, Anywhere, 1, 3019, VeryHard, Any), 3134, Same, NA, (5253, 5273, 5274, 5275), TTLocalizer.QuestDialogDict[5252]), + 5253: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 3020, VeryHard, 'cr'), Same, Same, NA, (5254, 5282, 5283, 5284), TTLocalizer.QuestDialogDict[5253]), + 5273: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 3020, VeryHard, 'm'), Same, Same, NA, (5254, 5282, 5283, 5284), TTLocalizer.QuestDialogDict[5273]), + 5274: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 3020, VeryHard, 'ls'), Same, Same, NA, (5254, 5282, 5283, 5284), TTLocalizer.QuestDialogDict[5274]), + 5275: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 3020, Hard, 'le'), Same, Same, NA, (5254, 5282, 5283, 5284), TTLocalizer.QuestDialogDict[5275]), + 5254: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 3021, VeryHard, 'mh'), Same, Same, 102, NA, TTLocalizer.QuestDialogDict[5254]), + 5282: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 3021, VeryHard, 'tbc'), Same, Same, 102, NA, TTLocalizer.QuestDialogDict[5282]), + 5283: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 3021, VeryHard, 'rb'), Same, Same, 102, NA, TTLocalizer.QuestDialogDict[5283]), + 5284: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 1, 3021, Hard, 'bw'), Same, Same, 102, NA, TTLocalizer.QuestDialogDict[5284]), + 5255: (BR_TIER + 1, Start, (VisitQuest,), Any, 3228, NA, (5256, 5276), TTLocalizer.QuestDialogDict[5255]), + 5256: (BR_TIER + 1, Cont, (CogTrackQuest, Anywhere, 45, 'c'), 3228, Same, NA, (5257, 5277), TTLocalizer.QuestDialogDict[5256]), + 5276: (BR_TIER + 1, Cont, (CogTrackQuest, Anywhere, 40, 'l'), 3228, Same, NA, (5257, 5277), TTLocalizer.QuestDialogDict[5276]), + 5257: (BR_TIER + 1, Cont, (CogTrackQuest, Anywhere, 45, 's'), Same, Same, 100, NA, TTLocalizer.QuestDialogDict[5257]), + 5277: (BR_TIER + 1, Cont, (CogTrackQuest, Anywhere, 45, 'm'), Same, Same, 100, NA, TTLocalizer.QuestDialogDict[5277]), + 5301: (BR_TIER + 1, Start, (VisitQuest,), Any, 3304, NA, 5302, TTLocalizer.QuestDialogDict[5301]), + 5302: (BR_TIER + 1, Cont, (CogTrackQuest, Anywhere, 90, 'l'), Same, Same, 100, NA, TTLocalizer.QuestDialogDict[5302]), + 5303: (BR_TIER + 1, Start, (VisitQuest,), Any, 3318, NA, 5304, TTLocalizer.QuestDialogDict[5303]), + 5304: (BR_TIER + 1, Cont, (RecoverItemQuest, ToontownGlobals.TheBrrrgh, 1, 3024, VeryHard, 'l', 'track'), Same, Same, NA, 5305, TTLocalizer.QuestDialogDict[5304]), + 5305: (BR_TIER + 1, Cont, (CogLevelQuest, Anywhere, 20, 7), Same, Same, NA, 5306, TTLocalizer.QuestDialogDict[5305]), + 5306: (BR_TIER + 1, Cont, (RecoverItemQuest, ToontownGlobals.TheBrrrgh, 2, 3025, Hard, AnyFish), Same, Same, NA, 5307, TTLocalizer.QuestDialogDict[5306]), + 5307: (BR_TIER + 1, Cont, (BuildingQuest, Anywhere, 5, Any, 4, 0), Same, Same, 204, NA, TTLocalizer.QuestDialogDict[5307]), + 5308: (BR_TIER + 1, Start, (VisitQuest,), Any, 3312, NA, 5309, TTLocalizer.QuestDialogDict[5308]), + 5309: (BR_TIER + 1, Start, (CogTrackQuest, ToontownGlobals.PolarPlace, 30, 'l'), Same, Same, NA, 5310, TTLocalizer.QuestDialogDict[5309]), + 5310: (BR_TIER + 1, Cont, (VisitQuest,), Same, 3113, NA, 5311, TTLocalizer.QuestDialogDict[5310]), + 5311: (BR_TIER + 1, Cont, (RecoverItemQuest, Anywhere, 2, 3026, Medium, 'le'), Same, Same, NA, 5312, TTLocalizer.QuestDialogDict[5311]), + 5312: (BR_TIER + 1, Cont, (DeliverItemQuest, 3026), Same, 3312, 502, NA, TTLocalizer.QuestDialogDict[5312]), + 5500: (BR_TIER + 1, Start, (CogQuest, ToontownGlobals.TheBrrrgh, 75, Any), Any, ToonHQ, NA, 5501, DefaultDialog), + 5501: (BR_TIER + 1, Cont, (DeliverItemQuest, 1000), Any, 3008, 1000, NA, DefaultDialog), + 5502: (BR_TIER + 1, Start, (RescueQuest, InVP, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 5503: (BR_TIER + 1, Start, (RescueQuest, InFO, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 903: (BR_TIER + 2, Start, (VisitQuest,), Any, 3112, NA, (5234, 5278), TTLocalizer.QuestDialogDict[903]), + 5234: (BR_TIER + 2, Start, (RecoverItemQuest, Anywhere, 6, 3012, Medium, 'tbc'), 3112, Same, NA, (5235, 5279), TTLocalizer.QuestDialogDict[5234]), + 5278: (BR_TIER + 2, Start, (RecoverItemQuest, Anywhere, 6, 3022, Medium, 'mh'), 3112, Same, NA, (5235, 5279), TTLocalizer.QuestDialogDict[5278]), + 5235: (BR_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 1, 3013, Hard, 'rb'), Same, Same, NA, 5236, TTLocalizer.QuestDialogDict[5235]), + 5279: (BR_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 1, 3013, Medium, 'bw'), Same, Same, NA, 5236, TTLocalizer.QuestDialogDict[5279]), + 5236: (BR_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 1, 3014, VeryHard, AnyFish), Same, Same, NA, 5237, TTLocalizer.QuestDialogDict[5236]), + 5237: (BR_TIER + 2, Cont, (VisitQuest,), Same, 3128, NA, (5238, 5280), TTLocalizer.QuestDialogDict[5237]), + 5238: (BR_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 10, 3015, VeryEasy, 'mh'), Same, Same, NA, 5239, TTLocalizer.QuestDialogDict[5238]), + 5280: (BR_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 10, 3015, VeryEasy, 'tbc'), Same, Same, NA, 5239, TTLocalizer.QuestDialogDict[5280]), + 5239: (BR_TIER + 2, Cont, (DeliverItemQuest, 3015), Same, 3112, NA, (5240, 5281), TTLocalizer.QuestDialogDict[5239]), + 5240: (BR_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 1, 3016, Hard, 'bw'), Same, Same, NA, 5241, TTLocalizer.QuestDialogDict[5240]), + 5281: (BR_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 1, 3023, Hard, 'mh'), Same, Same, NA, 5241, TTLocalizer.QuestDialogDict[5281]), + 5241: (BR_TIER + 2, Cont, (BuildingQuest, Anywhere, 20, Any, 4, 0), Same, Same, NA, 5242, TTLocalizer.QuestDialogDict[5241]), + 5242: (BR_TIER + 2, Cont, (RecoverItemQuest, Anywhere, 1, 3014, VeryHard, AnyFish), Same, Same, 900, NA, TTLocalizer.QuestDialogDict[5242]), + 5320: (BR_TIER + 2, Start, (CogQuest, Anywhere, 36, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5321: (BR_TIER + 2, Start, (CogQuest, Anywhere, 38, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5322: (BR_TIER + 2, Start, (CogQuest, Anywhere, 40, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5323: (BR_TIER + 2, Start, (CogQuest, Anywhere, 42, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5324: (BR_TIER + 2, Start, (CogQuest, Anywhere, 44, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5325: (BR_TIER + 2, Start, (CogQuest, Anywhere, 46, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5326: (BR_TIER + 2, Start, (CogQuest, Anywhere, 48, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5327: (BR_TIER + 2, Start, (CogQuest, Anywhere, 53, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5328: (BR_TIER + 2, Start, (CogQuest, Anywhere, 52, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5329: (BR_TIER + 2, Start, (CogQuest, Anywhere, 54, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5330: (BR_TIER + 2, Start, (CogLevelQuest, Anywhere, 25, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5331: (BR_TIER + 2, Start, (CogLevelQuest, Anywhere, 30, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5332: (BR_TIER + 2, Start, (CogLevelQuest, Anywhere, 35, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 5333: (BR_TIER + 2, Start, (CogLevelQuest, Anywhere, 6, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 5334: (BR_TIER + 2, Start, (CogLevelQuest, Anywhere, 10, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 5335: (BR_TIER + 2, Start, (CogLevelQuest, Anywhere, 20, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 5336: (BR_TIER + 2, Start, (CogLevelQuest, Anywhere, 2, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 5337: (BR_TIER + 2, Start, (CogLevelQuest, Anywhere, 8, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 5338: (BR_TIER + 2, Start, (CogLevelQuest, Anywhere, 10, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 5339: (BR_TIER + 2, Start, (CogLevelQuest, Anywhere, 12, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 5340: (BR_TIER + 2, Start, (CogQuest, ToontownGlobals.TheBrrrgh, 75, Any), Any, ToonHQ, NA, 5341, DefaultDialog), + 5341: (BR_TIER + 2, Cont, (DeliverItemQuest, 1000), Any, 3008, 1000, NA, DefaultDialog), + 5370: (BR_TIER + 2, Start, (CogQuest, ToontownGlobals.SellbotHQ, 22, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5371: (BR_TIER + 2, Start, (CogQuest, ToontownGlobals.SellbotHQ, 25, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 5372: (BR_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.SellbotHQ, 16, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 5373: (BR_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.SellbotHQ, 12, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5374: (BR_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 14, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5375: (BR_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 10, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 5376: (BR_TIER + 2, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5377: (BR_TIER + 2, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 5378: (BR_TIER + 2, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5379: (BR_TIER + 2, Start, (FactoryQuest, ToontownGlobals.SellbotHQ, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 5380: (BR_TIER + 2, Start, (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 5381: (BR_TIER + 2, Start, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 6, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 5382: (BR_TIER + 2, Start, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 3, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 5383: (BR_TIER + 2, Start, (ForemanQuest, ToontownGlobals.SellbotHQ, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 5384: (BR_TIER + 2, Start, (ForemanQuest, ToontownGlobals.SellbotHQ, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 5385: (BR_TIER + 2, Start, (RescueQuest, InVP, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 5386: (BR_TIER + 2, Start, (RescueQuest, InFO, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 6101: (DL_TIER, Start, (CogQuest, ToontownGlobals.DonaldsDreamland, 60, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 6102: (DL_TIER, Start, (CogQuest, ToontownGlobals.DonaldsDreamland, 65, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 6103: (DL_TIER, OBSOLETE, (CogQuest, ToontownGlobals.DonaldsDreamland, 70, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 6104: (DL_TIER, Start, (CogQuest, Anywhere, 80, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 6105: (DL_TIER, Start, (CogQuest, Anywhere, 90, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 6106: (DL_TIER, Start, (CogQuest, Anywhere, 100, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 6107: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'ym'), Any, ToonHQ, Any, NA, DefaultDialog), + 6108: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'mm'), Any, ToonHQ, Any, NA, DefaultDialog), + 6109: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'ds'), Any, ToonHQ, Any, NA, DefaultDialog), + 6110: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'hh'), Any, ToonHQ, Any, NA, DefaultDialog), + 6111: (DL_TIER, Start, (CogQuest, Anywhere, 15, 'cr'), Any, ToonHQ, Any, NA, DefaultDialog), + 6112: (DL_TIER, Start, (CogQuest, Anywhere, 8, 'tbc'), Any, ToonHQ, Any, NA, DefaultDialog), + 6113: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'nd'), Any, ToonHQ, Any, NA, DefaultDialog), + 6114: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'gh'), Any, ToonHQ, Any, NA, DefaultDialog), + 6115: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'ms'), Any, ToonHQ, Any, NA, DefaultDialog), + 6116: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'tf'), Any, ToonHQ, Any, NA, DefaultDialog), + 6117: (DL_TIER, Start, (CogQuest, Anywhere, 15, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 6118: (DL_TIER, Start, (CogQuest, Anywhere, 8, 'mh'), Any, ToonHQ, Any, NA, DefaultDialog), + 6119: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'tw'), Any, ToonHQ, Any, NA, DefaultDialog), + 6120: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'bc'), Any, ToonHQ, Any, NA, DefaultDialog), + 6121: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'nc'), Any, ToonHQ, Any, NA, DefaultDialog), + 6122: (DL_TIER, OBSOLETE, (CogQuest, Anywhere, 25, 'mb'), Any, ToonHQ, Any, NA, DefaultDialog), + 6123: (DL_TIER, Start, (CogQuest, Anywhere, 15, 'ls'), Any, ToonHQ, Any, NA, DefaultDialog), + 6124: (DL_TIER, Start, (CogQuest, Anywhere, 8, 'rb'), Any, ToonHQ, Any, NA, DefaultDialog), + 6125: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'dt'), Any, ToonHQ, Any, NA, DefaultDialog), + 6126: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'ac'), Any, ToonHQ, Any, NA, DefaultDialog), + 6127: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'bs'), Any, ToonHQ, Any, NA, DefaultDialog), + 6128: (DL_TIER, Start, (CogQuest, Anywhere, 25, 'sd'), Any, ToonHQ, Any, NA, DefaultDialog), + 6129: (DL_TIER, Start, (CogQuest, Anywhere, 15, 'le'), Any, ToonHQ, Any, NA, DefaultDialog), + 6130: (DL_TIER, Start, (CogQuest, Anywhere, 8, 'bw'), Any, ToonHQ, Any, NA, DefaultDialog), + 6131: (DL_TIER, Start, (CogLevelQuest, Anywhere, 50, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 6132: (DL_TIER, Start, (CogLevelQuest, Anywhere, 40, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 6133: (DL_TIER, Start, (CogLevelQuest, Anywhere, 35, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 6134: (DL_TIER, Start, (CogLevelQuest, Anywhere, 30, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 6135: (DL_TIER, Start, (CogLevelQuest, Anywhere, 25, 9), Any, ToonHQ, Any, NA, DefaultDialog), + 6136: (DL_TIER, Start, (CogLevelQuest, Anywhere, 20, 9), Any, ToonHQ, Any, NA, DefaultDialog), + 6137: (DL_TIER, Start, (CogLevelQuest, Anywhere, 15, 9), Any, ToonHQ, Any, NA, DefaultDialog), + 6138: (DL_TIER, Start, (CogLevelQuest, Anywhere, 10, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 6139: (DL_TIER, Start, (CogTrackQuest, ToontownGlobals.DonaldsDreamland, 50, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 6140: (DL_TIER, Start, (CogTrackQuest, ToontownGlobals.DonaldsDreamland, 50, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 6141: (DL_TIER, OBSOLETE, (CogTrackQuest, ToontownGlobals.DonaldsDreamland, 50, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 6142: (DL_TIER, Start, (CogTrackQuest, ToontownGlobals.DonaldsDreamland, 50, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 6143: (DL_TIER, OBSOLETE, (CogTrackQuest, ToontownGlobals.DonaldsDreamland, 55, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 6144: (DL_TIER, Start, (CogTrackQuest, ToontownGlobals.DonaldsDreamland, 55, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 6145: (DL_TIER, Start, (CogTrackQuest, ToontownGlobals.DonaldsDreamland, 55, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 6146: (DL_TIER, Start, (CogTrackQuest, ToontownGlobals.DonaldsDreamland, 55, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 6147: (DL_TIER, OBSOLETE, (CogTrackQuest, Anywhere, 70, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 6148: (DL_TIER, Start, (CogTrackQuest, Anywhere, 70, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 6149: (DL_TIER, Start, (CogTrackQuest, Anywhere, 70, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 6150: (DL_TIER, Start, (CogTrackQuest, Anywhere, 70, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 6151: (DL_TIER, Start, (BuildingQuest, Anywhere, 10, Any, 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 6152: (DL_TIER, Start, (BuildingQuest, Anywhere, 6, Any, 4, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 6153: (DL_TIER, OBSOLETE, (BuildingQuest, Anywhere, 8, Any, 4, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 6154: (DL_TIER, Start, (BuildingQuest, Anywhere, 6, Any, 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 6155: (DL_TIER, Start, (BuildingQuest, Anywhere, 2, 'm', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 6156: (DL_TIER, Start, (BuildingQuest, Anywhere, 2, 's', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 6157: (DL_TIER, Start, (BuildingQuest, Anywhere, 2, 'c', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 6158: (DL_TIER, Start, (BuildingQuest, Anywhere, 2, 'l', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 6170: (DL_TIER, OBSOLETE, (CogQuest, ToontownGlobals.SellbotHQ, 40, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 6171: (DL_TIER, OBSOLETE, (CogQuest, ToontownGlobals.SellbotHQ, 45, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 6172: (DL_TIER, OBSOLETE, (CogQuest, ToontownGlobals.SellbotHQ, 50, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 6173: (DL_TIER, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotHQ, 30, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 6174: (DL_TIER, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotHQ, 20, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 6175: (DL_TIER, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotHQ, 20, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 6176: (DL_TIER, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 15, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 6177: (DL_TIER, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 10, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 6178: (DL_TIER, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 10, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 6179: (DL_TIER, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 6180: (DL_TIER, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 6181: (DL_TIER, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 6182: (DL_TIER, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 6183: (DL_TIER, OBSOLETE, (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 20), Any, ToonHQ, Any, NA, DefaultDialog), + 6184: (DL_TIER, OBSOLETE, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 10, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 6185: (DL_TIER, OBSOLETE, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 4, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 6186: (DL_TIER, OBSOLETE, (ForemanQuest, ToontownGlobals.SellbotHQ, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 6187: (DL_TIER, OBSOLETE, (ForemanQuest, ToontownGlobals.SellbotHQ, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 6197: (DL_TIER, Start, (BuildingQuest, Anywhere, 10, Any, 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 6198: (DL_TIER, Start, (BuildingQuest, Anywhere, 8, Any, 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 6199: (DL_TIER, Start, (BuildingQuest, Anywhere, 6, 's', 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 6200: (DL_TIER, Start, (BuildingQuest, Anywhere, 5, 'l', 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 6201: (DL_TIER, Start, (VisitQuest,), Any, 9111, NA, 6202, TTLocalizer.QuestDialogDict[6201]), + 6202: (DL_TIER, Start, (CogQuest, ToontownGlobals.DonaldsDreamland, 70, Any), 9111, Same, 100, NA, TTLocalizer.QuestDialogDict[6202]), + 6206: (DL_TIER, Start, (VisitQuest,), Any, 9131, NA, 6207, TTLocalizer.QuestDialogDict[6206]), + 6207: (DL_TIER, Start, (BuildingQuest, ToontownGlobals.DonaldsDreamland, 8, Any, 4, 0), 9131, Same, 205, NA, TTLocalizer.QuestDialogDict[6207]), + 6211: (DL_TIER, Start, (VisitQuest,), Any, 9217, NA, 6212, TTLocalizer.QuestDialogDict[6211]), + 6212: (DL_TIER, Start, (RecoverItemQuest, Anywhere, 3, 6002, Medium, 'bc'), 9217, Same, NA, 6213, TTLocalizer.QuestDialogDict[6212]), + 6213: (DL_TIER, Cont, (RecoverItemQuest, Anywhere, 1, 6003, Hard, 'mb'), Same, Same, NA, 6214, TTLocalizer.QuestDialogDict[6213]), + 6214: (DL_TIER, Cont, (RecoverItemQuest, Anywhere, 1, 6004, VeryHard, 'pp'), Same, Same, 101, NA, TTLocalizer.QuestDialogDict[6214]), + 6221: (DL_TIER, Start, (VisitQuest,), Any, 9119, NA, 6222, TTLocalizer.QuestDialogDict[6221]), + 6222: (DL_TIER, Start, (CogTrackQuest, ToontownGlobals.DonaldsDreamland, 50, 'c'), 9119, Same, 102, NA, TTLocalizer.QuestDialogDict[6222]), + 6231: (DL_TIER, Start, (VisitQuest,), Any, 9203, NA, 6232, TTLocalizer.QuestDialogDict[6231]), + 6232: (DL_TIER, Start, (VisitQuest,), 9203, 9103, NA, 6233, TTLocalizer.QuestDialogDict[6232]), + 6233: (DL_TIER, Cont, (CogTrackQuest, ToontownGlobals.CashbotHQ, 10, 'm'), Same, Same, NA, 6234, TTLocalizer.QuestDialogDict[6233]), + 6234: (DL_TIER, Cont, (VisitQuest,), Same, 9203, NA, 6235, TTLocalizer.QuestDialogDict[6234]), + 6235: (DL_TIER, Cont, (RecoverItemQuest, ToontownGlobals.CashbotHQ, 1, 6001, VeryHard, 'm', 'track'), Same, Same, 4000, NA, TTLocalizer.QuestDialogDict[6235]), + 6241: (DL_TIER, Start, (VisitQuest,), Any, 9219, NA, 6242, TTLocalizer.QuestDialogDict[6241]), + 6242: (DL_TIER, Start, (CogQuest, ToontownGlobals.DonaldsDreamland, 25, 'nc'), 9219, Same, 705, NA, TTLocalizer.QuestDialogDict[6242]), + 6251: (DL_TIER, Start, (VisitQuest,), Any, 9221, NA, 6252, TTLocalizer.QuestDialogDict[6251]), + 6252: (DL_TIER, Start, (DeliverItemQuest, 6006), 9221, 9222, NA, 6253, TTLocalizer.QuestDialogDict[6252]), + 6253: (DL_TIER, Cont, (VisitQuest,), Same, 9221, NA, 6254, TTLocalizer.QuestDialogDict[6253]), + 6254: (DL_TIER, Cont, (DeliverItemQuest, 6007), Same, 9210, NA, 6255, TTLocalizer.QuestDialogDict[6254]), + 6255: (DL_TIER, Cont, (CogTrackQuest, Anywhere, 70, 'm'), Same, Same, NA, 6256, TTLocalizer.QuestDialogDict[6255]), + 6256: (DL_TIER, Cont, (VisitQuest,), Same, 9221, NA, 6257, TTLocalizer.QuestDialogDict[6256]), + 6257: (DL_TIER, Cont, (DeliverItemQuest, 6008), Same, 9205, NA, 6258, TTLocalizer.QuestDialogDict[6257]), + 6258: (DL_TIER, Cont, (CogQuest, Anywhere, 25, 'ms'), Same, Same, NA, 6259, TTLocalizer.QuestDialogDict[6258]), + 6259: (DL_TIER, Cont, (VisitQuest,), Same, 9221, NA, 6260, TTLocalizer.QuestDialogDict[6259]), + 6260: (DL_TIER, Cont, (DeliverItemQuest, 6009), Same, 9229, NA, 6261, TTLocalizer.QuestDialogDict[6260]), + 6261: (DL_TIER, Cont, (VisitQuest,), Same, 9221, NA, 6262, TTLocalizer.QuestDialogDict[6261]), + 6262: (DL_TIER, Cont, (DeliverItemQuest, 6010), Same, 9126, NA, 6263, TTLocalizer.QuestDialogDict[6262]), + 6263: (DL_TIER, Cont, (DeliverItemQuest, 6010), Same, 9112, NA, 6264, TTLocalizer.QuestDialogDict[6263]), + 6264: (DL_TIER, Cont, (DeliverItemQuest, 6011), Same, 9221, NA, 6265, TTLocalizer.QuestDialogDict[6264]), + 6265: (DL_TIER, Cont, (DeliverItemQuest, 6012), Same, 9115, NA, 6266, TTLocalizer.QuestDialogDict[6265]), + 6266: (DL_TIER, Cont, (VisitQuest,), Same, 9221, 103, NA, TTLocalizer.QuestDialogDict[6266]), + 6271: (DL_TIER, Start, (VisitQuest,), Any, 9208, NA, 6272, TTLocalizer.QuestDialogDict[6271]), + 6272: (DL_TIER, Start, (BuildingQuest, ToontownGlobals.DonaldsDreamland, 2, 'm', 5, 0), 9208, Same, 305, NA, TTLocalizer.QuestDialogDict[6272]), + 6281: (DL_TIER, Start, (VisitQuest,), Any, 9123, NA, 6282, TTLocalizer.QuestDialogDict[6281]), + 6282: (DL_TIER, Start, (CogTrackQuest, ToontownGlobals.DonaldsDreamland, 55, 'm'), 9123, Same, 4001, NA, TTLocalizer.QuestDialogDict[6282]), + 6291: (DL_TIER, Start, (VisitQuest,), Any, 9226, NA, 6292, TTLocalizer.QuestDialogDict[6291]), + 6292: (DL_TIER, Start, (RecoverItemQuest, ToontownGlobals.DonaldsDreamland, 1, 6005, VeryHard, 'm', 'track'), 9226, Same, 4002, NA, TTLocalizer.QuestDialogDict[6292]), + 6301: (DL_TIER, Start, (CogQuest, ToontownGlobals.CashbotHQ, 40, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 6302: (DL_TIER, Start, (CogQuest, ToontownGlobals.CashbotHQ, 45, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 6303: (DL_TIER, Start, (CogQuest, ToontownGlobals.CashbotHQ, 50, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 6304: (DL_TIER, Start, (CogLevelQuest, ToontownGlobals.CashbotHQ, 30, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 6305: (DL_TIER, Start, (CogLevelQuest, ToontownGlobals.CashbotHQ, 20, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 6306: (DL_TIER, Start, (CogLevelQuest, ToontownGlobals.CashbotHQ, 15, 9), Any, ToonHQ, Any, NA, DefaultDialog), + 6307: (DL_TIER, Start, (CogLevelQuest, ToontownGlobals.CashbotMintIntA, 12, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 6308: (DL_TIER, OBSOLETE, (CogLevelQuest, ToontownGlobals.CashbotMintIntB, 10, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 6309: (DL_TIER, OBSOLETE, (CogLevelQuest, ToontownGlobals.CashbotMintIntC, 8, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 6310: (DL_TIER, Start, (MintQuest, ToontownGlobals.CashbotMintIntA, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 6311: (DL_TIER, OBSOLETE, (MintQuest, ToontownGlobals.CashbotMintIntB, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 6312: (DL_TIER, OBSOLETE, (MintQuest, ToontownGlobals.CashbotMintIntC, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 6313: (DL_TIER, Start, (SkelecogQuest, ToontownGlobals.CashbotHQ, 20), Any, ToonHQ, Any, NA, DefaultDialog), + 6314: (DL_TIER, Start, (SkelecogLevelQuest, ToontownGlobals.CashbotHQ, 10, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 6315: (DL_TIER, Start, (SkelecogLevelQuest, ToontownGlobals.CashbotHQ, 6, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 6318: (DL_TIER, Start, (SupervisorQuest, ToontownGlobals.CashbotMintIntA, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 6319: (DL_TIER, OBSOLETE, (SupervisorQuest, ToontownGlobals.CashbotMintIntB, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 6320: (DL_TIER, OBSOLETE, (SupervisorQuest, ToontownGlobals.CashbotMintIntC, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 6321: (DL_TIER, Start, (CogLevelQuest, ToontownGlobals.CashbotMintIntA, 10, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 6322: (DL_TIER, Start, (CogLevelQuest, ToontownGlobals.CashbotMintIntA, 8, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 6323: (DL_TIER, Start, (MintQuest, ToontownGlobals.CashbotMintIntA, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 6324: (DL_TIER, Start, (MintQuest, ToontownGlobals.CashbotMintIntA, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 6325: (DL_TIER, Start, (SupervisorQuest, ToontownGlobals.CashbotMintIntA, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 6326: (DL_TIER, Start, (SupervisorQuest, ToontownGlobals.CashbotMintIntA, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 6327: (DL_TIER, Start, (RescueQuest, InVP, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 6328: (DL_TIER, Start, (RescueQuest, InFO, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 7101: (DL_TIER + 1, Start, (CogQuest, Anywhere, 120, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 7102: (DL_TIER + 1, Start, (CogQuest, Anywhere, 130, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 7103: (DL_TIER + 1, OBSOLETE, (CogQuest, Anywhere, 140, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 7104: (DL_TIER + 1, Start, (CogQuest, Anywhere, 160, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 7105: (DL_TIER + 1, Start, (CogQuest, Anywhere, 180, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 7106: (DL_TIER + 1, Start, (CogQuest, Anywhere, 200, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 7107: (DL_TIER + 1, Start, (CogQuest, Anywhere, 70, 'ym'), Any, ToonHQ, Any, NA, DefaultDialog), + 7108: (DL_TIER + 1, Start, (CogQuest, Anywhere, 60, 'mm'), Any, ToonHQ, Any, NA, DefaultDialog), + 7109: (DL_TIER + 1, Start, (CogQuest, Anywhere, 50, 'ds'), Any, ToonHQ, Any, NA, DefaultDialog), + 7110: (DL_TIER + 1, Start, (CogQuest, Anywhere, 50, 'hh'), Any, ToonHQ, Any, NA, DefaultDialog), + 7111: (DL_TIER + 1, Start, (CogQuest, Anywhere, 30, 'cr'), Any, ToonHQ, Any, NA, DefaultDialog), + 7112: (DL_TIER + 1, Start, (CogQuest, Anywhere, 20, 'tbc'), Any, ToonHQ, Any, NA, DefaultDialog), + 7113: (DL_TIER + 1, Start, (CogQuest, Anywhere, 70, 'nd'), Any, ToonHQ, Any, NA, DefaultDialog), + 7114: (DL_TIER + 1, Start, (CogQuest, Anywhere, 60, 'gh'), Any, ToonHQ, Any, NA, DefaultDialog), + 7115: (DL_TIER + 1, Start, (CogQuest, Anywhere, 50, 'ms'), Any, ToonHQ, Any, NA, DefaultDialog), + 7116: (DL_TIER + 1, Start, (CogQuest, Anywhere, 50, 'tf'), Any, ToonHQ, Any, NA, DefaultDialog), + 7117: (DL_TIER + 1, Start, (CogQuest, Anywhere, 30, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 7118: (DL_TIER + 1, Start, (CogQuest, Anywhere, 20, 'mh'), Any, ToonHQ, Any, NA, DefaultDialog), + 7119: (DL_TIER + 1, Start, (CogQuest, Anywhere, 70, 'tw'), Any, ToonHQ, Any, NA, DefaultDialog), + 7120: (DL_TIER + 1, Start, (CogQuest, Anywhere, 60, 'bc'), Any, ToonHQ, Any, NA, DefaultDialog), + 7121: (DL_TIER + 1, OBSOLETE, (CogQuest, Anywhere, 50, 'nc'), Any, ToonHQ, Any, NA, DefaultDialog), + 7122: (DL_TIER + 1, Start, (CogQuest, Anywhere, 50, 'mb'), Any, ToonHQ, Any, NA, DefaultDialog), + 7123: (DL_TIER + 1, Start, (CogQuest, Anywhere, 30, 'ls'), Any, ToonHQ, Any, NA, DefaultDialog), + 7124: (DL_TIER + 1, Start, (CogQuest, Anywhere, 20, 'rb'), Any, ToonHQ, Any, NA, DefaultDialog), + 7125: (DL_TIER + 1, Start, (CogQuest, Anywhere, 70, 'dt'), Any, ToonHQ, Any, NA, DefaultDialog), + 7126: (DL_TIER + 1, Start, (CogQuest, Anywhere, 60, 'ac'), Any, ToonHQ, Any, NA, DefaultDialog), + 7127: (DL_TIER + 1, Start, (CogQuest, Anywhere, 50, 'bs'), Any, ToonHQ, Any, NA, DefaultDialog), + 7128: (DL_TIER + 1, Start, (CogQuest, Anywhere, 50, 'sd'), Any, ToonHQ, Any, NA, DefaultDialog), + 7129: (DL_TIER + 1, Start, (CogQuest, Anywhere, 30, 'le'), Any, ToonHQ, Any, NA, DefaultDialog), + 7130: (DL_TIER + 1, Start, (CogQuest, Anywhere, 20, 'bw'), Any, ToonHQ, Any, NA, DefaultDialog), + 7131: (DL_TIER + 1, Start, (CogLevelQuest, Anywhere, 100, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 7132: (DL_TIER + 1, Start, (CogLevelQuest, Anywhere, 80, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 7133: (DL_TIER + 1, Start, (CogLevelQuest, Anywhere, 60, 9), Any, ToonHQ, Any, NA, DefaultDialog), + 7134: (DL_TIER + 1, Start, (CogLevelQuest, Anywhere, 70, 9), Any, ToonHQ, Any, NA, DefaultDialog), + 7135: (DL_TIER + 1, Start, (CogLevelQuest, Anywhere, 40, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 7136: (DL_TIER + 1, Start, (CogLevelQuest, Anywhere, 50, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 7137: (DL_TIER + 1, Start, (CogLevelQuest, Anywhere, 20, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 7138: (DL_TIER + 1, Start, (CogLevelQuest, Anywhere, 30, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 7139: (DL_TIER + 1, Start, (CogTrackQuest, Anywhere, 100, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 7140: (DL_TIER + 1, Start, (CogTrackQuest, Anywhere, 100, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 7141: (DL_TIER + 1, Start, (CogTrackQuest, Anywhere, 100, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 7142: (DL_TIER + 1, Start, (CogTrackQuest, Anywhere, 100, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 7143: (DL_TIER + 1, OBSOLETE, (CogTrackQuest, Anywhere, 120, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 7144: (DL_TIER + 1, Start, (CogTrackQuest, Anywhere, 120, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 7145: (DL_TIER + 1, Start, (CogTrackQuest, Anywhere, 120, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 7146: (DL_TIER + 1, Start, (CogTrackQuest, Anywhere, 120, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 7147: (DL_TIER + 1, Start, (CogTrackQuest, Anywhere, 140, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 7148: (DL_TIER + 1, Start, (CogTrackQuest, Anywhere, 140, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 7149: (DL_TIER + 1, Start, (CogTrackQuest, Anywhere, 140, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 7150: (DL_TIER + 1, Start, (CogTrackQuest, Anywhere, 140, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 7151: (DL_TIER + 1, Start, (BuildingQuest, Anywhere, 20, Any, 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 7152: (DL_TIER + 1, OBSOLETE, (BuildingQuest, Anywhere, 10, Any, 3, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 7153: (DL_TIER + 1, Start, (BuildingQuest, Anywhere, 10, Any, 4, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 7154: (DL_TIER + 1, Start, (BuildingQuest, Anywhere, 10, Any, 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 7155: (DL_TIER + 1, OBSOLETE, (BuildingQuest, Anywhere, 5, 'm', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 7156: (DL_TIER + 1, Start, (BuildingQuest, Anywhere, 5, 's', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 7157: (DL_TIER + 1, Start, (BuildingQuest, Anywhere, 5, 'c', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 7158: (DL_TIER + 1, Start, (BuildingQuest, Anywhere, 5, 'l', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 7170: (DL_TIER + 1, OBSOLETE, (CogQuest, ToontownGlobals.SellbotHQ, 80, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 7171: (DL_TIER + 1, OBSOLETE, (CogQuest, ToontownGlobals.SellbotHQ, 90, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 7172: (DL_TIER + 1, OBSOLETE, (CogQuest, ToontownGlobals.SellbotHQ, 100, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 7173: (DL_TIER + 1, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotHQ, 50, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 7174: (DL_TIER + 1, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotHQ, 35, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 7175: (DL_TIER + 1, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotHQ, 35, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 7176: (DL_TIER + 1, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 30, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 7177: (DL_TIER + 1, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 20, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 7178: (DL_TIER + 1, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 20, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 7179: (DL_TIER + 1, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 7180: (DL_TIER + 1, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 7181: (DL_TIER + 1, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 7182: (DL_TIER + 1, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 7183: (DL_TIER + 1, OBSOLETE, (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 40), Any, ToonHQ, Any, NA, DefaultDialog), + 7184: (DL_TIER + 1, OBSOLETE, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 20, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 7185: (DL_TIER + 1, OBSOLETE, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 8, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 7186: (DL_TIER + 1, OBSOLETE, (ForemanQuest, ToontownGlobals.SellbotHQ, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 7187: (DL_TIER + 1, OBSOLETE, (ForemanQuest, ToontownGlobals.SellbotHQ, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 7201: (DL_TIER + 1, Start, (VisitQuest,), Any, 9124, NA, 7202, TTLocalizer.QuestDialogDict[7201]), + 7202: (DL_TIER + 1, Start, (VisitQuest,), 9124, 9128, NA, 7203, TTLocalizer.QuestDialogDict[7202]), + 7203: (DL_TIER + 1, Cont, (DeliverItemQuest, 7001), Same, 9124, NA, 7204, TTLocalizer.QuestDialogDict[7203]), + 7204: (DL_TIER + 1, Cont, (VisitQuest,), Same, 9128, NA, 7205, TTLocalizer.QuestDialogDict[7204]), + 7205: (DL_TIER + 1, Cont, (CogQuest, ToontownGlobals.DonaldsDreamland, 140, Any), Same, Same, NA, 7206, TTLocalizer.QuestDialogDict[7205]), + 7206: (DL_TIER + 1, Cont, (DeliverItemQuest, 7002), Same, 9124, 4003, NA, TTLocalizer.QuestDialogDict[7206]), + 7209: (DL_TIER + 1, Start, (VisitQuest,), Any, 9232, NA, 7210, TTLocalizer.QuestDialogDict[7209]), + 7210: (DL_TIER + 1, Start, (VisitQuest,), 9232, 9101, NA, 7211, TTLocalizer.QuestDialogDict[7210]), + 7211: (DL_TIER + 1, Cont, (CogQuest, Anywhere, 30, 'nc'), Same, Same, NA, 7212, TTLocalizer.QuestDialogDict[7211]), + 7212: (DL_TIER + 1, Cont, (DeliverItemQuest, 7003), Same, 9232, NA, 7213, TTLocalizer.QuestDialogDict[7212]), + 7213: (DL_TIER + 1, Cont, (VisitQuest,), Same, 9101, NA, 7214, TTLocalizer.QuestDialogDict[7213]), + 7214: (DL_TIER + 1, Cont, (CogTrackQuest, Anywhere, 65, 'm'), Same, Same, NA, 7215, TTLocalizer.QuestDialogDict[7214]), + 7215: (DL_TIER + 1, Cont, (DeliverItemQuest, 7004), Same, 9232, 4004, NA, TTLocalizer.QuestDialogDict[7215]), + 7218: (DL_TIER + 1, Start, (VisitQuest,), Any, 9109, NA, 7219, TTLocalizer.QuestDialogDict[7218]), + 7219: (DL_TIER + 1, Start, (VisitQuest,), 9109, 9230, NA, 7220, TTLocalizer.QuestDialogDict[7219]), + 7220: (DL_TIER + 1, Cont, (DeliverItemQuest, 7005), Same, 9109, NA, 7221, TTLocalizer.QuestDialogDict[7220]), + 7221: (DL_TIER + 1, Cont, (VisitQuest,), Same, 9230, NA, 7222, TTLocalizer.QuestDialogDict[7221]), + 7222: (DL_TIER + 1, Cont, (BuildingQuest, Anywhere, 10, Any, 3, 0), Same, Same, NA, 7223, TTLocalizer.QuestDialogDict[7222]), + 7223: (DL_TIER + 1, Cont, (DeliverItemQuest, 7006), Same, 9109, 4005, NA, TTLocalizer.QuestDialogDict[7223]), + 7226: (DL_TIER + 1, Start, (VisitQuest,), Any, 9224, NA, 7227, TTLocalizer.QuestDialogDict[7226]), + 7227: (DL_TIER + 1, Start, (VisitQuest,), 9224, 9102, NA, 7228, TTLocalizer.QuestDialogDict[7227]), + 7228: (DL_TIER + 1, Cont, (DeliverItemQuest, 7007), Same, 9224, NA, 7229, TTLocalizer.QuestDialogDict[7228]), + 7229: (DL_TIER + 1, Cont, (VisitQuest,), Same, 9102, NA, 7230, TTLocalizer.QuestDialogDict[7229]), + 7230: (DL_TIER + 1, Cont, (DeliverItemQuest, 7008), Same, 9224, NA, 7231, TTLocalizer.QuestDialogDict[7230]), + 7231: (DL_TIER + 1, Cont, (VisitQuest,), Same, 9102, NA, 7232, TTLocalizer.QuestDialogDict[7231]), + 7232: (DL_TIER + 1, Cont, (VisitQuest,), Same, 9216, NA, 7233, TTLocalizer.QuestDialogDict[7232]), + 7233: (DL_TIER + 1, Cont, (DeliverItemQuest, 7009), Same, 9224, NA, 7234, TTLocalizer.QuestDialogDict[7233]), + 7234: (DL_TIER + 1, Cont, (VisitQuest,), Same, 9216, NA, 7235, TTLocalizer.QuestDialogDict[7234]), + 7235: (DL_TIER + 1, Cont, (BuildingQuest, Anywhere, 5, 'm', 5, 0), Same, Same, NA, 7236, TTLocalizer.QuestDialogDict[7235]), + 7236: (DL_TIER + 1, Cont, (DeliverItemQuest, 7010), Same, 9224, 4006, NA, TTLocalizer.QuestDialogDict[7236]), + 7239: (DL_TIER + 1, Start, (VisitQuest,), Any, 9114, NA, 7240, TTLocalizer.QuestDialogDict[7239]), + 7240: (DL_TIER + 1, Start, (VisitQuest,), 9114, 9215, NA, 7241, TTLocalizer.QuestDialogDict[7240]), + 7241: (DL_TIER + 1, Cont, (RecoverItemQuest, ToontownGlobals.DonaldsDreamland, 1, 7011, Hard, AnyFish), Same, Same, NA, 7242, TTLocalizer.QuestDialogDict[7241]), + 7242: (DL_TIER + 1, Cont, (RecoverItemQuest, ToontownGlobals.DonaldsDreamland, 1, 7012, VeryHard, AnyFish), Same, Same, NA, 7243, TTLocalizer.QuestDialogDict[7242]), + 7243: (DL_TIER + 1, Cont, (RecoverItemQuest, ToontownGlobals.CashbotHQ, 1, 7013, Hard, 'ls'), Same, Same, NA, 7244, TTLocalizer.QuestDialogDict[7243]), + 7244: (DL_TIER + 1, Cont, (DeliverItemQuest, 7014), Same, 9114, 4007, NA, TTLocalizer.QuestDialogDict[7244]), + 7250: (DL_TIER + 1, Start, (CogQuest, ToontownGlobals.CashbotHQ, 80, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 7251: (DL_TIER + 1, Start, (CogQuest, ToontownGlobals.CashbotHQ, 90, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 7252: (DL_TIER + 1, Start, (CogQuest, ToontownGlobals.CashbotHQ, 100, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 7253: (DL_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.CashbotHQ, 50, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 7254: (DL_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.CashbotHQ, 35, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 7255: (DL_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.CashbotHQ, 35, 9), Any, ToonHQ, Any, NA, DefaultDialog), + 7256: (DL_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.CashbotMintIntA, 30, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 7257: (DL_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.CashbotMintIntB, 25, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 7258: (DL_TIER + 1, OBSOLETE, (CogLevelQuest, ToontownGlobals.CashbotMintIntC, 20, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 7259: (DL_TIER + 1, Start, (MintQuest, ToontownGlobals.CashbotMintIntA, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 7260: (DL_TIER + 1, Start, (MintQuest, ToontownGlobals.CashbotMintIntB, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 7261: (DL_TIER + 1, OBSOLETE, (MintQuest, ToontownGlobals.CashbotMintIntC, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 7262: (DL_TIER + 1, Start, (SkelecogQuest, ToontownGlobals.CashbotHQ, 30), Any, ToonHQ, Any, NA, DefaultDialog), + 7263: (DL_TIER + 1, Start, (SkelecogLevelQuest, ToontownGlobals.CashbotHQ, 20, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 7264: (DL_TIER + 1, Start, (SkelecogLevelQuest, ToontownGlobals.CashbotHQ, 10, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 7265: (DL_TIER + 1, Start, (SupervisorQuest, ToontownGlobals.CashbotMintIntA, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 7266: (DL_TIER + 1, Start, (SupervisorQuest, ToontownGlobals.CashbotMintIntB, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 7267: (DL_TIER + 1, OBSOLETE, (SupervisorQuest, ToontownGlobals.CashbotMintIntC, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 7268: (DL_TIER + 1, Start, (CogLevelQuest, ToontownGlobals.CashbotMintIntB, 20, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 7269: (DL_TIER + 1, Start, (MintQuest, ToontownGlobals.CashbotMintIntB, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 7270: (DL_TIER + 1, Start, (SupervisorQuest, ToontownGlobals.CashbotMintIntB, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 7500: (DL_TIER + 1, Start, (CogQuest, ToontownGlobals.DonaldsDreamland, 100, Any), Any, ToonHQ, NA, 7501, DefaultDialog), + 7501: (DL_TIER + 1, Cont, (DeliverItemQuest, 1000), Any, 9010, 1000, NA, DefaultDialog), + 7502: (DL_TIER + 1, Start, (BuildingQuest, Anywhere, 20, Any, 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 7503: (DL_TIER + 1, Start, (BuildingQuest, Anywhere, 15, Any, 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 7504: (DL_TIER + 1, Start, (BuildingQuest, Anywhere, 10, 's', 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 7505: (DL_TIER + 1, Start, (BuildingQuest, Anywhere, 10, 'l', 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 7506: (DL_TIER + 1, Start, (RescueQuest, InVP, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 7507: (DL_TIER + 1, Start, (RescueQuest, InFO, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 8101: (DL_TIER + 2, Start, (CogQuest, Anywhere, 240, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 8102: (DL_TIER + 2, Start, (CogQuest, Anywhere, 260, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 8103: (DL_TIER + 2, Start, (CogQuest, Anywhere, 280, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 8104: (DL_TIER + 2, Start, (CogQuest, Anywhere, 320, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 8105: (DL_TIER + 2, Start, (CogQuest, Anywhere, 360, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 8106: (DL_TIER + 2, Start, (CogQuest, Anywhere, 400, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 8107: (DL_TIER + 2, Start, (CogQuest, Anywhere, 140, 'ym'), Any, ToonHQ, Any, NA, DefaultDialog), + 8108: (DL_TIER + 2, Start, (CogQuest, Anywhere, 120, 'mm'), Any, ToonHQ, Any, NA, DefaultDialog), + 8109: (DL_TIER + 2, Start, (CogQuest, Anywhere, 100, 'ds'), Any, ToonHQ, Any, NA, DefaultDialog), + 8110: (DL_TIER + 2, Start, (CogQuest, Anywhere, 100, 'hh'), Any, ToonHQ, Any, NA, DefaultDialog), + 8111: (DL_TIER + 2, Start, (CogQuest, Anywhere, 60, 'cr'), Any, ToonHQ, Any, NA, DefaultDialog), + 8112: (DL_TIER + 2, Start, (CogQuest, Anywhere, 40, 'tbc'), Any, ToonHQ, Any, NA, DefaultDialog), + 8113: (DL_TIER + 2, Start, (CogQuest, Anywhere, 140, 'nd'), Any, ToonHQ, Any, NA, DefaultDialog), + 8114: (DL_TIER + 2, Start, (CogQuest, Anywhere, 120, 'gh'), Any, ToonHQ, Any, NA, DefaultDialog), + 8115: (DL_TIER + 2, Start, (CogQuest, Anywhere, 100, 'ms'), Any, ToonHQ, Any, NA, DefaultDialog), + 8116: (DL_TIER + 2, Start, (CogQuest, Anywhere, 100, 'tf'), Any, ToonHQ, Any, NA, DefaultDialog), + 8117: (DL_TIER + 2, Start, (CogQuest, Anywhere, 60, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 8118: (DL_TIER + 2, Start, (CogQuest, Anywhere, 40, 'mh'), Any, ToonHQ, Any, NA, DefaultDialog), + 8119: (DL_TIER + 2, Start, (CogQuest, Anywhere, 140, 'tw'), Any, ToonHQ, Any, NA, DefaultDialog), + 8120: (DL_TIER + 2, Start, (CogQuest, Anywhere, 120, 'bc'), Any, ToonHQ, Any, NA, DefaultDialog), + 8121: (DL_TIER + 2, Start, (CogQuest, Anywhere, 100, 'nc'), Any, ToonHQ, Any, NA, DefaultDialog), + 8122: (DL_TIER + 2, Start, (CogQuest, Anywhere, 100, 'mb'), Any, ToonHQ, Any, NA, DefaultDialog), + 8123: (DL_TIER + 2, Start, (CogQuest, Anywhere, 60, 'ls'), Any, ToonHQ, Any, NA, DefaultDialog), + 8124: (DL_TIER + 2, Start, (CogQuest, Anywhere, 40, 'rb'), Any, ToonHQ, Any, NA, DefaultDialog), + 8125: (DL_TIER + 2, Start, (CogQuest, Anywhere, 140, 'dt'), Any, ToonHQ, Any, NA, DefaultDialog), + 8126: (DL_TIER + 2, Start, (CogQuest, Anywhere, 120, 'ac'), Any, ToonHQ, Any, NA, DefaultDialog), + 8127: (DL_TIER + 2, Start, (CogQuest, Anywhere, 100, 'bs'), Any, ToonHQ, Any, NA, DefaultDialog), + 8128: (DL_TIER + 2, Start, (CogQuest, Anywhere, 100, 'sd'), Any, ToonHQ, Any, NA, DefaultDialog), + 8129: (DL_TIER + 2, Start, (CogQuest, Anywhere, 60, 'le'), Any, ToonHQ, Any, NA, DefaultDialog), + 8130: (DL_TIER + 2, Start, (CogQuest, Anywhere, 40, 'bw'), Any, ToonHQ, Any, NA, DefaultDialog), + 8131: (DL_TIER + 2, Start, (CogLevelQuest, Anywhere, 160, 9), Any, ToonHQ, Any, NA, DefaultDialog), + 8132: (DL_TIER + 2, Start, (CogLevelQuest, Anywhere, 200, 9), Any, ToonHQ, Any, NA, DefaultDialog), + 8133: (DL_TIER + 2, Start, (CogLevelQuest, Anywhere, 120, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 8134: (DL_TIER + 2, Start, (CogLevelQuest, Anywhere, 140, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 8135: (DL_TIER + 2, Start, (CogLevelQuest, Anywhere, 80, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 8136: (DL_TIER + 2, Start, (CogLevelQuest, Anywhere, 100, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 8137: (DL_TIER + 2, Start, (CogLevelQuest, Anywhere, 40, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 8138: (DL_TIER + 2, Start, (CogLevelQuest, Anywhere, 60, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 8139: (DL_TIER + 2, Start, (CogTrackQuest, Anywhere, 200, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 8140: (DL_TIER + 2, Start, (CogTrackQuest, Anywhere, 200, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 8141: (DL_TIER + 2, Start, (CogTrackQuest, Anywhere, 200, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 8142: (DL_TIER + 2, Start, (CogTrackQuest, Anywhere, 200, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 8143: (DL_TIER + 2, Start, (CogTrackQuest, Anywhere, 250, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 8144: (DL_TIER + 2, Start, (CogTrackQuest, Anywhere, 250, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 8145: (DL_TIER + 2, Start, (CogTrackQuest, Anywhere, 250, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 8146: (DL_TIER + 2, Start, (CogTrackQuest, Anywhere, 250, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 8147: (DL_TIER + 2, Start, (CogTrackQuest, Anywhere, 300, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 8148: (DL_TIER + 2, Start, (CogTrackQuest, Anywhere, 300, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 8149: (DL_TIER + 2, Start, (CogTrackQuest, Anywhere, 300, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 8150: (DL_TIER + 2, Start, (CogTrackQuest, Anywhere, 300, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 8151: (DL_TIER + 2, Start, (BuildingQuest, Anywhere, 40, Any, 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 8152: (DL_TIER + 2, Start, (BuildingQuest, Anywhere, 20, Any, 3, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 8153: (DL_TIER + 2, Start, (BuildingQuest, Anywhere, 20, Any, 4, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 8154: (DL_TIER + 2, Start, (BuildingQuest, Anywhere, 20, Any, 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 8155: (DL_TIER + 2, Start, (BuildingQuest, Anywhere, 10, 'm', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 8156: (DL_TIER + 2, Start, (BuildingQuest, Anywhere, 10, 's', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 8157: (DL_TIER + 2, Start, (BuildingQuest, Anywhere, 10, 'c', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 8158: (DL_TIER + 2, Start, (BuildingQuest, Anywhere, 10, 'l', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 8170: (DL_TIER + 2, OBSOLETE, (CogQuest, ToontownGlobals.SellbotHQ, 160, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 8171: (DL_TIER + 2, OBSOLETE, (CogQuest, ToontownGlobals.SellbotHQ, 180, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 8172: (DL_TIER + 2, OBSOLETE, (CogQuest, ToontownGlobals.SellbotHQ, 200, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 8173: (DL_TIER + 2, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotHQ, 100, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 8174: (DL_TIER + 2, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotHQ, 70, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 8175: (DL_TIER + 2, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotHQ, 70, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 8176: (DL_TIER + 2, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 60, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 8177: (DL_TIER + 2, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 40, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 8178: (DL_TIER + 2, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 40, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 8179: (DL_TIER + 2, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 8180: (DL_TIER + 2, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 16), Any, ToonHQ, Any, NA, DefaultDialog), + 8181: (DL_TIER + 2, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 8182: (DL_TIER + 2, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 16), Any, ToonHQ, Any, NA, DefaultDialog), + 8183: (DL_TIER + 2, OBSOLETE, (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 80), Any, ToonHQ, Any, NA, DefaultDialog), + 8184: (DL_TIER + 2, OBSOLETE, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 40, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 8185: (DL_TIER + 2, OBSOLETE, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 16, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 8186: (DL_TIER + 2, OBSOLETE, (ForemanQuest, ToontownGlobals.SellbotHQ, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 8187: (DL_TIER + 2, OBSOLETE, (ForemanQuest, ToontownGlobals.SellbotHQ, 16), Any, ToonHQ, Any, NA, DefaultDialog), + 8188: (DL_TIER + 2, Start, (BossQuest, ToontownGlobals.SellbotHQ, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 8189: (DL_TIER + 2, Start, (RescueQuest, InVP, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 8197: (DL_TIER + 2, Start, (RescueQuest, InFO, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 8201: (DL_TIER + 2, Start, (CogQuest, ToontownGlobals.CashbotHQ, 160, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 8202: (DL_TIER + 2, Start, (CogQuest, ToontownGlobals.CashbotHQ, 180, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 8203: (DL_TIER + 2, Start, (CogQuest, ToontownGlobals.CashbotHQ, 200, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 8204: (DL_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.CashbotHQ, 100, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 8205: (DL_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.CashbotHQ, 90, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 8206: (DL_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.CashbotHQ, 80, 9), Any, ToonHQ, Any, NA, DefaultDialog), + 8207: (DL_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.CashbotMintIntA, 60, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 8208: (DL_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.CashbotMintIntB, 50, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 8209: (DL_TIER + 2, Start, (CogLevelQuest, ToontownGlobals.CashbotMintIntC, 40, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 8210: (DL_TIER + 2, Start, (MintQuest, ToontownGlobals.CashbotMintIntA, 16), Any, ToonHQ, Any, NA, DefaultDialog), + 8211: (DL_TIER + 2, Start, (MintQuest, ToontownGlobals.CashbotMintIntB, 14), Any, ToonHQ, Any, NA, DefaultDialog), + 8212: (DL_TIER + 2, Start, (MintQuest, ToontownGlobals.CashbotMintIntC, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 8213: (DL_TIER + 2, Start, (SkelecogQuest, ToontownGlobals.CashbotMintIntA, 80), Any, ToonHQ, Any, NA, DefaultDialog), + 8214: (DL_TIER + 2, Start, (SkelecogQuest, ToontownGlobals.CashbotMintIntB, 60), Any, ToonHQ, Any, NA, DefaultDialog), + 8215: (DL_TIER + 2, Start, (SkelecogQuest, ToontownGlobals.CashbotMintIntC, 40), Any, ToonHQ, Any, NA, DefaultDialog), + 8216: (DL_TIER + 2, Start, (SupervisorQuest, ToontownGlobals.CashbotMintIntA, 16), Any, ToonHQ, Any, NA, DefaultDialog), + 8217: (DL_TIER + 2, Start, (SupervisorQuest, ToontownGlobals.CashbotMintIntB, 14), Any, ToonHQ, Any, NA, DefaultDialog), + 8218: (DL_TIER + 2, Start, (SupervisorQuest, ToontownGlobals.CashbotMintIntC, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 8219: (DL_TIER + 2, Start, (BossQuest, ToontownGlobals.CashbotHQ, 2), Any, ToonHQ, 621, NA, DefaultDialog), + 8220: (DL_TIER + 2, Start, (BuildingQuest, Anywhere, 25, Any, 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 8221: (DL_TIER + 2, Start, (BuildingQuest, Anywhere, 20, Any, 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 8224: (DL_TIER + 2, Start, (BuildingQuest, Anywhere, 15, 's', 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 8225: (DL_TIER + 2, Start, (BuildingQuest, Anywhere, 15, 'l', 0, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 8226: (DL_TIER + 2, Start, (RescueQuest, InFO, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 9101: (DL_TIER + 3, Start, (CogQuest, Anywhere, 500, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 9102: (DL_TIER + 3, Start, (CogQuest, Anywhere, 600, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 9103: (DL_TIER + 3, Start, (CogQuest, Anywhere, 700, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 9104: (DL_TIER + 3, Start, (CogQuest, Anywhere, 800, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 9105: (DL_TIER + 3, Start, (CogQuest, Anywhere, 900, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 9106: (DL_TIER + 3, Start, (CogQuest, Anywhere, 1000, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 9107: (DL_TIER + 3, Start, (CogQuest, Anywhere, 300, 'ym'), Any, ToonHQ, Any, NA, DefaultDialog), + 9108: (DL_TIER + 3, Start, (CogQuest, Anywhere, 250, 'mm'), Any, ToonHQ, Any, NA, DefaultDialog), + 9109: (DL_TIER + 3, Start, (CogQuest, Anywhere, 200, 'ds'), Any, ToonHQ, Any, NA, DefaultDialog), + 9110: (DL_TIER + 3, Start, (CogQuest, Anywhere, 200, 'hh'), Any, ToonHQ, Any, NA, DefaultDialog), + 9111: (DL_TIER + 3, Start, (CogQuest, Anywhere, 120, 'cr'), Any, ToonHQ, Any, NA, DefaultDialog), + 9112: (DL_TIER + 3, Start, (CogQuest, Anywhere, 80, 'tbc'), Any, ToonHQ, Any, NA, DefaultDialog), + 9113: (DL_TIER + 3, Start, (CogQuest, Anywhere, 280, 'nd'), Any, ToonHQ, Any, NA, DefaultDialog), + 9114: (DL_TIER + 3, Start, (CogQuest, Anywhere, 240, 'gh'), Any, ToonHQ, Any, NA, DefaultDialog), + 9115: (DL_TIER + 3, Start, (CogQuest, Anywhere, 200, 'ms'), Any, ToonHQ, Any, NA, DefaultDialog), + 9116: (DL_TIER + 3, Start, (CogQuest, Anywhere, 200, 'tf'), Any, ToonHQ, Any, NA, DefaultDialog), + 9117: (DL_TIER + 3, Start, (CogQuest, Anywhere, 120, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 9118: (DL_TIER + 3, Start, (CogQuest, Anywhere, 80, 'mh'), Any, ToonHQ, Any, NA, DefaultDialog), + 9119: (DL_TIER + 3, Start, (CogQuest, Anywhere, 280, 'tw'), Any, ToonHQ, Any, NA, DefaultDialog), + 9120: (DL_TIER + 3, Start, (CogQuest, Anywhere, 240, 'bc'), Any, ToonHQ, Any, NA, DefaultDialog), + 9121: (DL_TIER + 3, Start, (CogQuest, Anywhere, 200, 'nc'), Any, ToonHQ, Any, NA, DefaultDialog), + 9122: (DL_TIER + 3, Start, (CogQuest, Anywhere, 200, 'mb'), Any, ToonHQ, Any, NA, DefaultDialog), + 9123: (DL_TIER + 3, Start, (CogQuest, Anywhere, 120, 'ls'), Any, ToonHQ, Any, NA, DefaultDialog), + 9124: (DL_TIER + 3, Start, (CogQuest, Anywhere, 80, 'rb'), Any, ToonHQ, Any, NA, DefaultDialog), + 9125: (DL_TIER + 3, Start, (CogQuest, Anywhere, 280, 'dt'), Any, ToonHQ, Any, NA, DefaultDialog), + 9126: (DL_TIER + 3, Start, (CogQuest, Anywhere, 240, 'ac'), Any, ToonHQ, Any, NA, DefaultDialog), + 9127: (DL_TIER + 3, Start, (CogQuest, Anywhere, 200, 'bs'), Any, ToonHQ, Any, NA, DefaultDialog), + 9128: (DL_TIER + 3, Start, (CogQuest, Anywhere, 200, 'sd'), Any, ToonHQ, Any, NA, DefaultDialog), + 9129: (DL_TIER + 3, Start, (CogQuest, Anywhere, 120, 'le'), Any, ToonHQ, Any, NA, DefaultDialog), + 9130: (DL_TIER + 3, Start, (CogQuest, Anywhere, 80, 'bw'), Any, ToonHQ, Any, NA, DefaultDialog), + 9131: (DL_TIER + 3, Start, (CogLevelQuest, Anywhere, 320, 9), Any, ToonHQ, Any, NA, DefaultDialog), + 9132: (DL_TIER + 3, Start, (CogLevelQuest, Anywhere, 400, 9), Any, ToonHQ, Any, NA, DefaultDialog), + 9133: (DL_TIER + 3, Start, (CogLevelQuest, Anywhere, 240, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 9134: (DL_TIER + 3, Start, (CogLevelQuest, Anywhere, 280, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 9135: (DL_TIER + 3, Start, (CogLevelQuest, Anywhere, 160, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 9136: (DL_TIER + 3, Start, (CogLevelQuest, Anywhere, 200, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 9137: (DL_TIER + 3, Start, (CogLevelQuest, Anywhere, 80, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 9138: (DL_TIER + 3, Start, (CogLevelQuest, Anywhere, 120, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 9139: (DL_TIER + 3, Start, (CogTrackQuest, Anywhere, 400, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 9140: (DL_TIER + 3, Start, (CogTrackQuest, Anywhere, 400, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 9141: (DL_TIER + 3, Start, (CogTrackQuest, Anywhere, 400, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 9142: (DL_TIER + 3, Start, (CogTrackQuest, Anywhere, 400, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 9143: (DL_TIER + 3, Start, (CogTrackQuest, Anywhere, 500, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 9144: (DL_TIER + 3, Start, (CogTrackQuest, Anywhere, 500, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 9145: (DL_TIER + 3, Start, (CogTrackQuest, Anywhere, 500, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 9146: (DL_TIER + 3, Start, (CogTrackQuest, Anywhere, 500, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 9147: (DL_TIER + 3, Start, (CogTrackQuest, Anywhere, 600, 'm'), Any, ToonHQ, Any, NA, DefaultDialog), + 9148: (DL_TIER + 3, Start, (CogTrackQuest, Anywhere, 600, 's'), Any, ToonHQ, Any, NA, DefaultDialog), + 9149: (DL_TIER + 3, Start, (CogTrackQuest, Anywhere, 600, 'c'), Any, ToonHQ, Any, NA, DefaultDialog), + 9150: (DL_TIER + 3, Start, (CogTrackQuest, Anywhere, 600, 'l'), Any, ToonHQ, Any, NA, DefaultDialog), + 9151: (DL_TIER + 3, Start, (BuildingQuest, Anywhere, 400, Any, 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 9152: (DL_TIER + 3, Start, (BuildingQuest, Anywhere, 200, Any, 3, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 9153: (DL_TIER + 3, Start, (BuildingQuest, Anywhere, 200, Any, 4, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 9154: (DL_TIER + 3, Start, (BuildingQuest, Anywhere, 200, Any, 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 9155: (DL_TIER + 3, Start, (BuildingQuest, Anywhere, 100, Any, 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 9156: (DL_TIER + 3, Start, (BuildingQuest, Anywhere, 100, Any, 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 9157: (DL_TIER + 3, Start, (BuildingQuest, Anywhere, 100, Any, 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 9158: (DL_TIER + 3, Start, (BuildingQuest, Anywhere, 100, Any, 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 9170: (DL_TIER + 3, OBSOLETE, (CogQuest, ToontownGlobals.SellbotHQ, 350, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 9171: (DL_TIER + 3, OBSOLETE, (CogQuest, ToontownGlobals.SellbotHQ, 400, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 9172: (DL_TIER + 3, OBSOLETE, (CogQuest, ToontownGlobals.SellbotHQ, 500, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 9173: (DL_TIER + 3, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotHQ, 200, 4), Any, ToonHQ, Any, NA, DefaultDialog), + 9174: (DL_TIER + 3, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotHQ, 150, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 9175: (DL_TIER + 3, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotHQ, 150, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 9176: (DL_TIER + 3, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 150, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 9177: (DL_TIER + 3, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 100, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 9178: (DL_TIER + 3, OBSOLETE, (CogLevelQuest, ToontownGlobals.SellbotFactoryInt, 100, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 9179: (DL_TIER + 3, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 25), Any, ToonHQ, Any, NA, DefaultDialog), + 9180: (DL_TIER + 3, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 35), Any, ToonHQ, Any, NA, DefaultDialog), + 9181: (DL_TIER + 3, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 25), Any, ToonHQ, Any, NA, DefaultDialog), + 9182: (DL_TIER + 3, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 35), Any, ToonHQ, Any, NA, DefaultDialog), + 9183: (DL_TIER + 3, OBSOLETE, (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 150), Any, ToonHQ, Any, NA, DefaultDialog), + 9184: (DL_TIER + 3, OBSOLETE, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 80, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 9185: (DL_TIER + 3, OBSOLETE, (SkelecogLevelQuest, ToontownGlobals.SellbotHQ, 32, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 9186: (DL_TIER + 3, OBSOLETE, (ForemanQuest, ToontownGlobals.SellbotHQ, 25), Any, ToonHQ, Any, NA, DefaultDialog), + 9187: (DL_TIER + 3, OBSOLETE, (ForemanQuest, ToontownGlobals.SellbotHQ, 35), Any, ToonHQ, Any, NA, DefaultDialog), + 9188: (DL_TIER + 3, Start, (BossQuest, ToontownGlobals.SellbotHQ, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 9189: (DL_TIER + 3, Start, (RescueQuest, InVP, 5), Any, ToonHQ, Any, NA, DefaultDialog), + 9201: (DL_TIER + 3, Start, (CogQuest, ToontownGlobals.CashbotHQ, 350, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 9202: (DL_TIER + 3, Start, (CogQuest, ToontownGlobals.CashbotHQ, 400, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 9203: (DL_TIER + 3, Start, (CogQuest, ToontownGlobals.CashbotHQ, 450, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 9204: (DL_TIER + 3, Start, (CogLevelQuest, ToontownGlobals.CashbotHQ, 200, 7), Any, ToonHQ, Any, NA, DefaultDialog), + 9205: (DL_TIER + 3, Start, (CogLevelQuest, ToontownGlobals.CashbotHQ, 150, 8), Any, ToonHQ, Any, NA, DefaultDialog), + 9206: (DL_TIER + 3, Start, (CogLevelQuest, ToontownGlobals.CashbotHQ, 100, 9), Any, ToonHQ, Any, NA, DefaultDialog), + 9207: (DL_TIER + 3, Start, (CogLevelQuest, ToontownGlobals.CashbotMintIntA, 200, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 9208: (DL_TIER + 3, Start, (CogLevelQuest, ToontownGlobals.CashbotMintIntB, 150, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 9209: (DL_TIER + 3, Start, (CogLevelQuest, ToontownGlobals.CashbotMintIntC, 100, 11), Any, ToonHQ, Any, NA, DefaultDialog), + 9210: (DL_TIER + 3, Start, (MintQuest, ToontownGlobals.CashbotMintIntA, 35), Any, ToonHQ, Any, NA, DefaultDialog), + 9211: (DL_TIER + 3, Start, (MintQuest, ToontownGlobals.CashbotMintIntB, 30), Any, ToonHQ, Any, NA, DefaultDialog), + 9212: (DL_TIER + 3, Start, (MintQuest, ToontownGlobals.CashbotMintIntC, 25), Any, ToonHQ, Any, NA, DefaultDialog), + 9213: (DL_TIER + 3, Start, (SkelecogQuest, ToontownGlobals.CashbotMintIntA, 150), Any, ToonHQ, Any, NA, DefaultDialog), + 9214: (DL_TIER + 3, Start, (SkelecogQuest, ToontownGlobals.CashbotMintIntB, 100), Any, ToonHQ, Any, NA, DefaultDialog), + 9215: (DL_TIER + 3, Start, (SkelecogQuest, ToontownGlobals.CashbotMintIntC, 50), Any, ToonHQ, Any, NA, DefaultDialog), + 9216: (DL_TIER + 3, Start, (SupervisorQuest, ToontownGlobals.CashbotMintIntA, 35), Any, ToonHQ, Any, NA, DefaultDialog), + 9217: (DL_TIER + 3, Start, (SupervisorQuest, ToontownGlobals.CashbotMintIntB, 30), Any, ToonHQ, Any, NA, DefaultDialog), + 9218: (DL_TIER + 3, Start, (SupervisorQuest, ToontownGlobals.CashbotMintIntC, 25), Any, ToonHQ, Any, NA, DefaultDialog), + 9219: (DL_TIER + 3, Start, (BossQuest, ToontownGlobals.CashbotHQ, 3), Any, ToonHQ, 622, NA, DefaultDialog), + 9500: (DL_TIER + 3, Start, (CogQuest, ToontownGlobals.DonaldsDreamland, 1000, Any), Any, ToonHQ, NA, 9501, DefaultDialog), + 9501: (DL_TIER + 3, Cont, (DeliverItemQuest, 1000), Any, 2004, 1000, NA, DefaultDialog), + 9502: (DL_TIER + 3, Start, (BuildingQuest, Anywhere, 30, Any, 2, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 9503: (DL_TIER + 3, Start, (BuildingQuest, Anywhere, 25, Any, 3, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 9504: (DL_TIER + 3, Start, (BuildingQuest, Anywhere, 20, 's', 5, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 9505: (DL_TIER + 3, Start, (BuildingQuest, Anywhere, 20, 'l', 5, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 9506: (DL_TIER + 3, Start, (RescueQuest, InFO, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 10100: (ELDER_TIER, Start, (CogQuest, Anywhere, 80, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 10101: (ELDER_TIER, Start, (CogQuest, Anywhere, 100, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 10102: (ELDER_TIER, Start, (CogQuest, Anywhere, 120, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 10103: (ELDER_TIER, Start, (CogQuest, Anywhere, 200, Any), Any, ToonHQ, 613, NA, DefaultDialog), + 10104: (ELDER_TIER, Start, (CogQuest, Anywhere, 250, Any), Any, ToonHQ, 615, NA, DefaultDialog), + 10105: (ELDER_TIER, Start, (CogQuest, Anywhere, 300, Any), Any, ToonHQ, 616, NA, DefaultDialog), + 10106: (ELDER_TIER, Start, (CogQuest, Anywhere, 400, Any), Any, ToonHQ, 618, NA, DefaultDialog), + 10110: (ELDER_TIER, Start, (BuildingQuest, Anywhere, 40, Any, 2, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 10111: (ELDER_TIER, Start, (BuildingQuest, Anywhere, 30, Any, 3, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 10112: (ELDER_TIER, Start, (BuildingQuest, Anywhere, 25, Any, 4, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 10113: (ELDER_TIER, Start, (BuildingQuest, Anywhere, 20, Any, 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 10114: (ELDER_TIER, Start, (BuildingQuest, Anywhere, 20, 'm', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 10115: (ELDER_TIER, Start, (BuildingQuest, Anywhere, 20, 's', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 10116: (ELDER_TIER, Start, (BuildingQuest, Anywhere, 20, 'c', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 10117: (ELDER_TIER, Start, (BuildingQuest, Anywhere, 20, 'l', 5, 0), Any, ToonHQ, Any, NA, DefaultDialog), + 10118: (ELDER_TIER, Start, (BuildingQuest, Anywhere, 50, Any, 1, 0), Any, ToonHQ, 620, NA, DefaultDialog), + 10120: (ELDER_TIER, OBSOLETE, (CogQuest, ToontownGlobals.SellbotHQ, 60, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 10121: (ELDER_TIER, OBSOLETE, (FactoryQuest, ToontownGlobals.SellbotHQ, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 10122: (ELDER_TIER, OBSOLETE, (ForemanQuest, ToontownGlobals.SellbotHQ, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 10123: (ELDER_TIER, Start, (BossQuest, ToontownGlobals.SellbotHQ, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 10124: (ELDER_TIER, Start, (RescueQuest, InVP, 6), Any, ToonHQ, Any, NA, DefaultDialog), + 10125: (ELDER_TIER, Start, (RescueQuest, InFO, 12), Any, ToonHQ, Any, NA, DefaultDialog), + 10140: (ELDER_TIER, Start, (CogQuest, ToontownGlobals.CashbotHQ, 60, Any), Any, ToonHQ, Any, NA, DefaultDialog), + 10141: (ELDER_TIER, Start, (MintQuest, ToontownGlobals.CashbotHQ, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 10142: (ELDER_TIER, Start, (SupervisorQuest, ToontownGlobals.CashbotHQ, 10), Any, ToonHQ, Any, NA, DefaultDialog), + 10143: (ELDER_TIER, Start, (BossQuest, ToontownGlobals.CashbotHQ, 2), Any, ToonHQ, 623, NA, DefaultDialog), + 10148: (ELDER_TIER, Start, (BossQuest, ToontownGlobals.LawbotHQ, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 10149: (ELDER_TIER, Start, (BossQuest, ToontownGlobals.BossbotHQ, 2), Any, ToonHQ, Any, NA, DefaultDialog), + 10150: (ELDER_TIER, Start, (BossQuest, Anywhere, 3), Any, ToonHQ, Any, NA, DefaultDialog), + 10200: (ELDER_TIER, Start, (CogQuest, Anywhere, 100, Any), Any, ToonHQ, NA, 10201, DefaultDialog), + 10201: (ELDER_TIER, Cont, (DeliverItemQuest, 1000), Any, ToonTailor, 1000, NA, DefaultDialog), + 10202: (ELDER_TIER, Start, (BuildingQuest, Anywhere, 25, Any, 1, 0), Any, ToonHQ, NA, 10203, DefaultDialog), + 10203: (ELDER_TIER, Cont, (DeliverItemQuest, 1000), Any, ToonTailor, 1000, NA, DefaultDialog), + 10208: (ELDER_TIER, Start, (BuildingQuest, Anywhere, 25, Any, 4, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 10209: (ELDER_TIER, Start, (BuildingQuest, Anywhere, 20, 's', 5, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 10210: (ELDER_TIER, Start, (BuildingQuest, Anywhere, 20, 'l', 5, 1), Any, ToonHQ, Any, NA, DefaultDialog), + 11000: (LAWBOT_HQ_TIER, Start, (VisitQuest,), Any, 3310, NA, 11001, TTLocalizer.QuestDialogDict[11000]), + 11001: (LAWBOT_HQ_TIER, Start, (RecoverItemQuest, ToontownGlobals.SillyStreet, 1, 3027, Hard, Any), Same, Same, 4100, NA, TTLocalizer.QuestDialogDict[11001]), + 11002: (LAWBOT_HQ_TIER + 1, Start, (VisitQuest,), Any, 3310, NA, 11003, TTLocalizer.QuestDialogDict[11002]), + 11003: (LAWBOT_HQ_TIER + 1, Start, (RecoverItemQuest, ToontownGlobals.LoopyLane, 1, 3027, Hard, Any), Same, Same, 4101, NA, TTLocalizer.QuestDialogDict[11003]), + 11004: (LAWBOT_HQ_TIER + 2, Start, (VisitQuest,), Any, 3310, NA, 11005, TTLocalizer.QuestDialogDict[11004]), + 11005: (LAWBOT_HQ_TIER + 2, Start, (RecoverItemQuest, ToontownGlobals.PunchlinePlace, 1, 3027, Hard, Any), Same, Same, 4102, NA, TTLocalizer.QuestDialogDict[11005]), + 11006: (LAWBOT_HQ_TIER + 3, Start, (VisitQuest,), Any, 3310, NA, 11007, TTLocalizer.QuestDialogDict[11006]), + 11007: (LAWBOT_HQ_TIER + 3, Start, (RecoverItemQuest, ToontownGlobals.BarnacleBoulevard, 1, 3027, Hard, Any), Same, Same, 4103, NA, TTLocalizer.QuestDialogDict[11007]), + 11008: (LAWBOT_HQ_TIER + 4, Start, (VisitQuest,), Any, 3310, NA, 11009, TTLocalizer.QuestDialogDict[11008]), + 11009: (LAWBOT_HQ_TIER + 4, Start, (RecoverItemQuest, ToontownGlobals.SeaweedStreet, 1, 3027, Hard, Any), Same, Same, 4104, NA, TTLocalizer.QuestDialogDict[11009]), + 11010: (LAWBOT_HQ_TIER + 5, Start, (VisitQuest,), Any, 3310, NA, 11011, TTLocalizer.QuestDialogDict[11010]), + 11011: (LAWBOT_HQ_TIER + 5, Start, (RecoverItemQuest, ToontownGlobals.LighthouseLane, 1, 3027, Hard, Any), Same, Same, 4105, NA, TTLocalizer.QuestDialogDict[11011]), + 11012: (LAWBOT_HQ_TIER + 6, Start, (VisitQuest,), Any, 3310, NA, 11013, TTLocalizer.QuestDialogDict[11012]), + 11013: (LAWBOT_HQ_TIER + 6, Start, (RecoverItemQuest, ToontownGlobals.ElmStreet, 1, 3027, Hard, Any), Same, Same, 4106, NA, TTLocalizer.QuestDialogDict[11013]), + 11014: (LAWBOT_HQ_TIER + 7, Start, (VisitQuest,), Any, 3310, NA, 11015, TTLocalizer.QuestDialogDict[11014]), + 11015: (LAWBOT_HQ_TIER + 7, Start, (RecoverItemQuest, ToontownGlobals.MapleStreet, 1, 3027, Hard, Any), Same, Same, 4107, NA, TTLocalizer.QuestDialogDict[11015]), + 11016: (LAWBOT_HQ_TIER + 8, Start, (VisitQuest,), Any, 3310, NA, 11017, TTLocalizer.QuestDialogDict[11016]), + 11017: (LAWBOT_HQ_TIER + 8, Start, (RecoverItemQuest, ToontownGlobals.OakStreet, 1, 3027, Hard, Any), Same, Same, 4108, NA, TTLocalizer.QuestDialogDict[11017]), + 11018: (LAWBOT_HQ_TIER + 9, Start, (VisitQuest,), Any, 3310, NA, 11019, TTLocalizer.QuestDialogDict[11018]), + 11019: (LAWBOT_HQ_TIER + 9, Start, (RecoverItemQuest, ToontownGlobals.AltoAvenue, 1, 3027, Hard, Any), Same, Same, 4109, NA, TTLocalizer.QuestDialogDict[11019]), + 11020: (LAWBOT_HQ_TIER + 10, Start, (VisitQuest,), Any, 3310, NA, 11021, TTLocalizer.QuestDialogDict[11020]), + 11021: (LAWBOT_HQ_TIER + 10, Start, (RecoverItemQuest, ToontownGlobals.BaritoneBoulevard, 1, 3027, Hard, Any), Same, Same, 4110, NA, TTLocalizer.QuestDialogDict[11021]), + 11022: (LAWBOT_HQ_TIER + 11, Start, (VisitQuest,), Any, 3310, NA, 11023, TTLocalizer.QuestDialogDict[11022]), + 11023: (LAWBOT_HQ_TIER + 11, Start, (RecoverItemQuest, ToontownGlobals.TenorTerrace, 1, 3027, Hard, Any), Same, Same, 4111, NA, TTLocalizer.QuestDialogDict[11023]), + 11024: (LAWBOT_HQ_TIER + 12, Start, (VisitQuest,), Any, 3310, NA, 11025, TTLocalizer.QuestDialogDict[11024]), + 11025: (LAWBOT_HQ_TIER + 12, Start, (RecoverItemQuest, ToontownGlobals.LullabyLane, 1, 3027, Hard, Any), Same, Same, 4112, NA, TTLocalizer.QuestDialogDict[11025]), + 11026: (LAWBOT_HQ_TIER + 13, Start, (VisitQuest,), Any, 3310, NA, 11027, TTLocalizer.QuestDialogDict[11026]), + 11027: (LAWBOT_HQ_TIER + 13, Start, (RecoverItemQuest, ToontownGlobals.PajamaPlace, 1, 3027, Hard, Any), Same, Same, 4113, NA, TTLocalizer.QuestDialogDict[11027]), + 12000: (BOSSBOT_HQ_TIER, Start, (VisitQuest,), Any, 1222, NA, 12001, TTLocalizer.QuestDialogDict[12000]), + 12001: (BOSSBOT_HQ_TIER, Start, (CogQuest, Anywhere, 1, 'f'), Same, Same, 4200, NA, TTLocalizer.QuestDialogDict[12001]), + 12002: (BOSSBOT_HQ_TIER + 1, Start, (VisitQuest,), Any, 1222, NA, 12003, TTLocalizer.QuestDialogDict[12002]), + 12003: (BOSSBOT_HQ_TIER + 1, Start, (CogQuest, Anywhere, 1, 'p'), Same, Same, 4201, NA, TTLocalizer.QuestDialogDict[12003]), + 12004: (BOSSBOT_HQ_TIER + 2, Start, (VisitQuest,), Any, 1222, NA, 12005, TTLocalizer.QuestDialogDict[12004]), + 12005: (BOSSBOT_HQ_TIER + 2, Start, (CogQuest, Anywhere, 1, 'ym'), Same, Same, 4202, NA, TTLocalizer.QuestDialogDict[12005]), + 12006: (BOSSBOT_HQ_TIER + 3, Start, (VisitQuest,), Any, 1222, NA, 12007, TTLocalizer.QuestDialogDict[12006]), + 12007: (BOSSBOT_HQ_TIER + 3, Start, (CogQuest, Anywhere, 1, 'mm'), Same, Same, 4203, NA, TTLocalizer.QuestDialogDict[12007]), + 12008: (BOSSBOT_HQ_TIER + 4, Start, (VisitQuest,), Any, 1222, NA, 12009, TTLocalizer.QuestDialogDict[12008]), + 12009: (BOSSBOT_HQ_TIER + 4, Start, (CogQuest, Anywhere, 1, 'ds'), Same, Same, 4204, NA, TTLocalizer.QuestDialogDict[12009]), + 12010: (BOSSBOT_HQ_TIER + 5, Start, (VisitQuest,), Any, 1222, NA, 12011, TTLocalizer.QuestDialogDict[12010]), + 12011: (BOSSBOT_HQ_TIER + 5, Start, (CogQuest, Anywhere, 1, 'hh'), Same, Same, 4205, NA, TTLocalizer.QuestDialogDict[12011]), + 12012: (BOSSBOT_HQ_TIER + 6, Start, (VisitQuest,), Any, 1222, NA, 12013, TTLocalizer.QuestDialogDict[12012]), + 12013: (BOSSBOT_HQ_TIER + 6, Start, (CogQuest, Anywhere, 1, 'cr'), Same, Same, 4206, NA, TTLocalizer.QuestDialogDict[12013]), + 12014: (BOSSBOT_HQ_TIER + 7, Start, (VisitQuest,), Any, 1222, NA, 12015, TTLocalizer.QuestDialogDict[12014]), + 12015: (BOSSBOT_HQ_TIER + 7, Start, (CogQuest, Anywhere, 1, 'tbc'), Same, Same, 4207, NA, TTLocalizer.QuestDialogDict[12015]), + 12016: (BOSSBOT_HQ_TIER + 8, Start, (VisitQuest,), Any, 1222, NA, 12017, TTLocalizer.QuestDialogDict[12016]), + 12017: (BOSSBOT_HQ_TIER + 8, Start, (SkeleReviveQuest, Anywhere, 1), Same, Same, 4208, NA, TTLocalizer.QuestDialogDict[12017]), + 12018: (BOSSBOT_HQ_TIER + 9, Start, (VisitQuest,), Any, 1222, NA, 12019, TTLocalizer.QuestDialogDict[12018]), + 12019: (BOSSBOT_HQ_TIER + 9, Start, (SkeleReviveQuest, Anywhere, 1), Same, Same, 4209, NA, TTLocalizer.QuestDialogDict[12019]), + 12020: (BOSSBOT_HQ_TIER + 10, Start, (VisitQuest,), Any, 1222, NA, 12021, TTLocalizer.QuestDialogDict[12020]), + 12021: (BOSSBOT_HQ_TIER + 10, Start, (SkeleReviveQuest, Anywhere, 1), Same, Same, 4210, NA, TTLocalizer.QuestDialogDict[12021]), + 12022: (BOSSBOT_HQ_TIER + 11, Start, (VisitQuest,), Any, 1222, NA, 12023, TTLocalizer.QuestDialogDict[12022]), + 12023: (BOSSBOT_HQ_TIER + 11, Start, (SkeleReviveQuest, Anywhere, 1), Same, Same, 4211, NA, TTLocalizer.QuestDialogDict[12023]), + 12024: (BOSSBOT_HQ_TIER + 12, Start, (VisitQuest,), Any, 1222, NA, 12025, TTLocalizer.QuestDialogDict[12024]), + 12025: (BOSSBOT_HQ_TIER + 12, Start, (SkeleReviveQuest, Anywhere, 1), Same, Same, 4212, NA, TTLocalizer.QuestDialogDict[12025]), + 12026: (BOSSBOT_HQ_TIER + 13, Start, (VisitQuest,), Any, 1222, NA, 12027, TTLocalizer.QuestDialogDict[12026]), + 12027: (BOSSBOT_HQ_TIER + 13, Start, (SkeleReviveQuest, Anywhere, 1), Same, Same, 4213, NA, TTLocalizer.QuestDialogDict[12027]), + 12028: (BOSSBOT_HQ_TIER + 14, Start, (VisitQuest,), Any, 1222, NA, 12029, TTLocalizer.QuestDialogDict[12028]), + 12029: (BOSSBOT_HQ_TIER + 14, Start, (SkeleReviveQuest, Anywhere, 1), Same, Same, 4214, NA, TTLocalizer.QuestDialogDict[12029]), + 12030: (BOSSBOT_HQ_TIER + 15, Start, (VisitQuest,), Any, 1222, NA, 12031, TTLocalizer.QuestDialogDict[12030]), + 12031: (BOSSBOT_HQ_TIER + 15, Start, (SkeleReviveQuest, Anywhere, 1), Same, Same, 4215, NA, TTLocalizer.QuestDialogDict[12031]), + 12032: (BOSSBOT_HQ_TIER + 16, Start, (VisitQuest,), Any, 2001, 4216, NA, TTLocalizer.QuestDialogDict[12032])} + +Tier2QuestsDict = {} + +for questId, questDesc in QuestDict.items(): + if questDesc[QuestDictStartIndex] == Start: + tier = questDesc[QuestDictTierIndex] + if tier in Tier2QuestsDict: + Tier2QuestsDict[tier].append(questId) + else: + Tier2QuestsDict[tier] = [questId] + +Quest2RewardDict = {} +Tier2Reward2QuestsDict = {} +Quest2RemainingStepsDict = {} + +def getAllRewardIdsForReward(rewardId): + if rewardId is AnyCashbotSuitPart: + return range(4000, 4011 + 1) + if rewardId is AnyLawbotSuitPart: + return range(4100, 4113 + 1) + if rewardId is AnyBossbotSuitPart: + return range(4200, 4216 + 1) + return (rewardId,) + +def findFinalRewardId(questId): + finalRewardId = Quest2RewardDict.get(questId) + if finalRewardId: + remainingSteps = Quest2RemainingStepsDict.get(questId) + else: + try: + questDesc = QuestDict[questId] + except KeyError: + print 'findFinalRewardId: Quest ID: %d not found' % questId + return -1 + + nextQuestId = questDesc[QuestDictNextQuestIndex] + if nextQuestId == NA: + finalRewardId = questDesc[QuestDictRewardIndex] + remainingSteps = 1 + else: + if type(nextQuestId) == type(()): + finalRewardId, remainingSteps = findFinalRewardId(nextQuestId[0]) + for id in nextQuestId[1:]: + findFinalRewardId(id) + + else: + finalRewardId, remainingSteps = findFinalRewardId(nextQuestId) + remainingSteps += 1 + if finalRewardId != OBSOLETE: + if questDesc[QuestDictStartIndex] == Start: + tier = questDesc[QuestDictTierIndex] + tier2RewardDict = Tier2Reward2QuestsDict.setdefault(tier, {}) + rewardIds = getAllRewardIdsForReward(finalRewardId) + for rewardId in rewardIds: + questList = tier2RewardDict.setdefault(rewardId, []) + questList.append(questId) + + else: + finalRewardId = None + Quest2RewardDict[questId] = finalRewardId + Quest2RemainingStepsDict[questId] = remainingSteps + return (finalRewardId, remainingSteps) + + +for questId in QuestDict.keys(): + findFinalRewardId(questId) + +def getStartingQuests(tier = None): + startingQuests = [] + for questId in QuestDict.keys(): + if isStartingQuest(questId): + if tier is None: + startingQuests.append(questId) + elif questId in Tier2QuestsDict[tier]: + startingQuests.append(questId) + + startingQuests.sort() + return startingQuests + + +def getFinalRewardId(questId, fAll = 0): + if fAll or isStartingQuest(questId): + return Quest2RewardDict.get(questId) + else: + return None + return None + + +def isStartingQuest(questId): + try: + return QuestDict[questId][QuestDictStartIndex] == Start + except KeyError: + return None + + return None + + +def getNumChoices(tier): + if tier in (0,): + return 0 + if tier in (1,): + return 2 + else: + return 3 + + +def getAvatarRewardId(av, questId): + for quest in av.quests: + if questId == quest[0]: + return quest[3] + + notify.warning('getAvatarRewardId(): quest not found on avatar') + return None + + +def getNextQuest(id, currentNpc, av): + validTrackTiers = [MM_TIER, BR_TIER, DD_TIER, TT_TIER + 1] + nextQuest = QuestDict[id][QuestDictNextQuestIndex] + if nextQuest == NA: + return (NA, NA) + elif type(nextQuest) == type(()): + nextReward = QuestDict[nextQuest[0]][QuestDictRewardIndex] + nextNextQuest, nextNextToNpcId = getNextQuest(nextQuest[0], currentNpc, av) + if nextReward == 400 and nextNextQuest == NA and av.getRewardTier() in validTrackTiers: + nextQuest = chooseTrackChoiceQuest(av.getRewardTier(), av) + else: + nextQuest = random.choice(nextQuest) + if not getQuestClass(nextQuest).filterFunc(av): + return getNextQuest(nextQuest, currentNpc, av) + nextToNpcId = getQuestToNpcId(nextQuest) + if nextToNpcId == Any: + nextToNpcId = 2004 + elif nextToNpcId == Same: + if currentNpc.getHq(): + nextToNpcId = ToonHQ + else: + nextToNpcId = currentNpc.getNpcId() + elif nextToNpcId == ToonHQ: + nextToNpcId = ToonHQ + return (nextQuest, nextToNpcId) + + +def filterQuests(entireQuestPool, currentNpc, av): + if notify.getDebug(): + notify.debug('filterQuests: entireQuestPool: %s' % entireQuestPool) + validQuestPool = dict([ (questId, 1) for questId in entireQuestPool ]) + if isLoopingFinalTier(av.getRewardTier()): + history = map(lambda questDesc: questDesc[0], av.quests) + else: + history = av.getQuestHistory() + if notify.getDebug(): + notify.debug('filterQuests: av quest history: %s' % history) + currentQuests = av.quests + for questId in entireQuestPool: + if questId in history: + if notify.getDebug(): + notify.debug('filterQuests: Removed %s because in history' % questId) + validQuestPool[questId] = 0 + continue + potentialFromNpc = getQuestFromNpcId(questId) + if not npcMatches(potentialFromNpc, currentNpc): + if notify.getDebug(): + notify.debug('filterQuests: Removed %s: potentialFromNpc does not match currentNpc' % questId) + validQuestPool[questId] = 0 + continue + potentialToNpc = getQuestToNpcId(questId) + if currentNpc.getNpcId() == potentialToNpc: + if notify.getDebug(): + notify.debug('filterQuests: Removed %s because potentialToNpc is currentNpc' % questId) + validQuestPool[questId] = 0 + continue + if not getQuestClass(questId).filterFunc(av): + if notify.getDebug(): + notify.debug('filterQuests: Removed %s because of filterFunc' % questId) + validQuestPool[questId] = 0 + continue + for quest in currentQuests: + fromNpcId = quest[1] + toNpcId = quest[2] + if potentialToNpc == toNpcId and toNpcId != ToonHQ: + validQuestPool[questId] = 0 + if notify.getDebug(): + notify.debug('filterQuests: Removed %s because npc involved' % questId) + break + + finalQuestPool = filter(lambda key: validQuestPool[key], validQuestPool.keys()) + if notify.getDebug(): + notify.debug('filterQuests: finalQuestPool: %s' % finalQuestPool) + return finalQuestPool + + +def chooseTrackChoiceQuest(tier, av, fixed = 0): + + def fixAndCallAgain(): + if not fixed and av.fixTrackAccess(): + notify.info('av %s trackAccess fixed: %s' % (av.getDoId(), trackAccess)) + return chooseTrackChoiceQuest(tier, av, fixed=1) + else: + return None + return None + + bestQuest = None + trackAccess = av.getTrackAccess() + if tier == MM_TIER: + if trackAccess[ToontownBattleGlobals.HEAL_TRACK] == 1: + bestQuest = 4002 + elif trackAccess[ToontownBattleGlobals.SOUND_TRACK] == 1: + bestQuest = 4001 + else: + notify.warning('av %s has bogus trackAccess: %s' % (av.getDoId(), trackAccess)) + return fixAndCallAgain() + elif tier == BR_TIER: + if trackAccess[ToontownBattleGlobals.SOUND_TRACK] + trackAccess[ToontownBattleGlobals.DROP_TRACK] == 0: + bestQuest = 5001 + elif trackAccess[ToontownBattleGlobals.SOUND_TRACK] + trackAccess[ToontownBattleGlobals.LURE_TRACK] == 0: + bestQuest = 5002 + elif trackAccess[ToontownBattleGlobals.HEAL_TRACK] + trackAccess[ToontownBattleGlobals.DROP_TRACK] == 0: + bestQuest = 5003 + elif trackAccess[ToontownBattleGlobals.HEAL_TRACK] + trackAccess[ToontownBattleGlobals.LURE_TRACK] == 0: + bestQuest = 5004 + elif trackAccess[ToontownBattleGlobals.TRAP_TRACK] + trackAccess[ToontownBattleGlobals.SOUND_TRACK] == 0: + bestQuest = 5005 + elif trackAccess[ToontownBattleGlobals.TRAP_TRACK] + trackAccess[ToontownBattleGlobals.HEAL_TRACK] == 0: + bestQuest = 5006 + elif trackAccess[ToontownBattleGlobals.TRAP_TRACK] + trackAccess[ToontownBattleGlobals.DROP_TRACK] == 0: + bestQuest = 5007 + elif trackAccess[ToontownBattleGlobals.TRAP_TRACK] + trackAccess[ToontownBattleGlobals.LURE_TRACK] == 0: + bestQuest = 5008 + else: + notify.warning('av %s has bogus trackAccess: %s' % (av.getDoId(), trackAccess)) + return fixAndCallAgain() + else: + if notify.getDebug(): + notify.debug('questPool for reward 400 had no dynamic choice, tier: %s' % tier) + bestQuest = seededRandomChoice(Tier2Reward2QuestsDict[tier][400]) + if notify.getDebug(): + notify.debug('chooseTrackChoiceQuest: avId: %s trackAccess: %s tier: %s bestQuest: %s' % (av.getDoId(), + trackAccess, + tier, + bestQuest)) + return bestQuest + + +def chooseMatchingQuest(tier, validQuestPool, rewardId, npc, av): + questsMatchingReward = Tier2Reward2QuestsDict[tier].get(rewardId, []) + if notify.getDebug(): + notify.debug('questsMatchingReward: %s tier: %s = %s' % (rewardId, tier, questsMatchingReward)) + if rewardId == 400 and QuestDict[questsMatchingReward[0]][QuestDictNextQuestIndex] == NA: + bestQuest = chooseTrackChoiceQuest(tier, av) + if notify.getDebug(): + notify.debug('single part track choice quest: %s tier: %s avId: %s trackAccess: %s bestQuest: %s' % (rewardId, + tier, + av.getDoId(), + av.getTrackAccess(), + bestQuest)) + else: + validQuestsMatchingReward = PythonUtil.intersection(questsMatchingReward, validQuestPool) + if notify.getDebug(): + notify.debug('validQuestsMatchingReward: %s tier: %s = %s' % (rewardId, tier, validQuestsMatchingReward)) + if validQuestsMatchingReward: + bestQuest = seededRandomChoice(validQuestsMatchingReward) + else: + questsMatchingReward = Tier2Reward2QuestsDict[tier].get(AnyCashbotSuitPart, []) + if notify.getDebug(): + notify.debug('questsMatchingReward: AnyCashbotSuitPart tier: %s = %s' % (tier, questsMatchingReward)) + validQuestsMatchingReward = PythonUtil.intersection(questsMatchingReward, validQuestPool) + if validQuestsMatchingReward: + if notify.getDebug(): + notify.debug('validQuestsMatchingReward: AnyCashbotSuitPart tier: %s = %s' % (tier, validQuestsMatchingReward)) + bestQuest = seededRandomChoice(validQuestsMatchingReward) + else: + questsMatchingReward = Tier2Reward2QuestsDict[tier].get(AnyLawbotSuitPart, []) + if notify.getDebug(): + notify.debug('questsMatchingReward: AnyLawbotSuitPart tier: %s = %s' % (tier, questsMatchingReward)) + validQuestsMatchingReward = PythonUtil.intersection(questsMatchingReward, validQuestPool) + if validQuestsMatchingReward: + if notify.getDebug(): + notify.debug('validQuestsMatchingReward: AnyLawbotSuitPart tier: %s = %s' % (tier, validQuestsMatchingReward)) + bestQuest = seededRandomChoice(validQuestsMatchingReward) + else: + questsMatchingReward = Tier2Reward2QuestsDict[tier].get(Any, []) + if notify.getDebug(): + notify.debug('questsMatchingReward: Any tier: %s = %s' % (tier, questsMatchingReward)) + if not questsMatchingReward: + notify.warning('chooseMatchingQuests, no questsMatchingReward') + return None + validQuestsMatchingReward = PythonUtil.intersection(questsMatchingReward, validQuestPool) + if not validQuestsMatchingReward: + notify.warning('chooseMatchingQuests, no validQuestsMatchingReward') + return None + if notify.getDebug(): + notify.debug('validQuestsMatchingReward: Any tier: %s = %s' % (tier, validQuestsMatchingReward)) + bestQuest = seededRandomChoice(validQuestsMatchingReward) + return bestQuest + + +def transformReward(baseRewardId, av): + if baseRewardId == 900: + trackId, progress = av.getTrackProgress() + if trackId == -1: + notify.warning('transformReward: asked to transform 900 but av is not training') + actualRewardId = baseRewardId + else: + actualRewardId = 900 + 1 + trackId + return actualRewardId + elif baseRewardId > 800 and baseRewardId < 900: + trackId, progress = av.getTrackProgress() + if trackId < 0: + notify.warning('transformReward: av: %s is training a track with none chosen!' % av.getDoId()) + return 601 + else: + actualRewardId = baseRewardId + 200 + trackId * 100 + return actualRewardId + else: + return baseRewardId + + +def chooseBestQuests(tier, currentNpc, av): + if isLoopingFinalTier(tier): + rewardHistory = map(lambda questDesc: questDesc[3], av.quests) + else: + rewardHistory = av.getRewardHistory()[1] + + seedRandomGen(currentNpc.getNpcId(), av.getDoId(), tier, rewardHistory) + numChoices = getNumChoices(tier) + rewards = getNextRewards(numChoices, tier, av) + if not rewards: + return [] + possibleQuests = [] + possibleRewards = list(rewards) + if Any not in possibleRewards: + possibleRewards.append(Any) + for rewardId in possibleRewards: + possibleQuests.extend(Tier2Reward2QuestsDict[tier].get(rewardId, [])) + + validQuestPool = filterQuests(possibleQuests, currentNpc, av) + if not validQuestPool: + return [] + if numChoices == 0: + numChoices = 1 + bestQuests = [] + for i in xrange(numChoices): + if len(validQuestPool) == 0: + break + if len(rewards) == 0: + break + rewardId = rewards.pop(0) + bestQuestId = chooseMatchingQuest(tier, validQuestPool, rewardId, currentNpc, av) + if bestQuestId is None: + continue + validQuestPool.remove(bestQuestId) + bestQuestToNpcId = getQuestToNpcId(bestQuestId) + if bestQuestToNpcId == Any: + bestQuestToNpcId = 2003 + elif bestQuestToNpcId == Same: + if currentNpc.getHq(): + bestQuestToNpcId = ToonHQ + else: + bestQuestToNpcId = currentNpc.getNpcId() + elif bestQuestToNpcId == ToonHQ: + bestQuestToNpcId = ToonHQ + bestQuests.append([bestQuestId, rewardId, bestQuestToNpcId]) + + for quest in bestQuests: + quest[1] = transformReward(quest[1], av) + + return bestQuests + + +def questExists(id): + return id in QuestDict + + +def getQuest(id): + questEntry = QuestDict.get(id) + if questEntry: + questDesc = questEntry[QuestDictDescIndex] + questClass = questDesc[0] + return questClass(id, questDesc[1:]) + else: + return None + return None + + +def getQuestClass(id): + questEntry = QuestDict.get(id) + if questEntry: + return questEntry[QuestDictDescIndex][0] + else: + return None + return None + + +def getVisitSCStrings(npcId): + if npcId == ToonHQ: + strings = [TTLocalizer.QuestsRecoverItemQuestSeeHQSCString, TTLocalizer.QuestsRecoverItemQuestGoToHQSCString] + elif npcId == ToonTailor: + strings = [TTLocalizer.QuestsTailorQuestSCString] + elif npcId: + npcName, hoodName, buildingArticle, buildingName, toStreet, streetName, isInPlayground = getNpcInfo(npcId) + strings = [TTLocalizer.QuestsVisitQuestSeeSCString % npcName] + if isInPlayground: + strings.append(TTLocalizer.QuestsRecoverItemQuestGoToPlaygroundSCString % hoodName) + else: + strings.append(TTLocalizer.QuestsRecoverItemQuestGoToStreetSCString % {'to': toStreet, + 'street': streetName, + 'hood': hoodName}) + strings.extend([TTLocalizer.QuestsRecoverItemQuestVisitBuildingSCString % (buildingArticle, buildingName), TTLocalizer.QuestsRecoverItemQuestWhereIsBuildingSCString % (buildingArticle, buildingName)]) + return strings + + +def getFinishToonTaskSCStrings(npcId): + return [TTLocalizer.QuestsGenericFinishSCString] + getVisitSCStrings(npcId) + + +def chooseQuestDialog(id, status): + questDialog = getQuestDialog(id).get(status) + if questDialog == None: + if status == QUEST: + quest = getQuest(id) + questDialog = quest.getDefaultQuestDialog() + else: + questDialog = DefaultDialog[status] + if type(questDialog) == type(()): + return random.choice(questDialog) + else: + return questDialog + return + + +def chooseQuestDialogReject(): + return random.choice(DefaultReject) + + +def chooseQuestDialogTierNotDone(): + return random.choice(DefaultTierNotDone) + + +def getNpcInfo(npcId): + npcName = NPCToons.getNPCName(npcId) + npcZone = NPCToons.getNPCZone(npcId) + hoodId = ZoneUtil.getCanonicalHoodId(npcZone) + hoodName = base.cr.hoodMgr.getFullnameFromId(hoodId) + buildingArticle = NPCToons.getBuildingArticle(npcZone) + buildingName = NPCToons.getBuildingTitle(npcZone) + branchId = ZoneUtil.getCanonicalBranchZone(npcZone) + toStreet = ToontownGlobals.StreetNames[branchId][0] + streetName = ToontownGlobals.StreetNames[branchId][-1] + isInPlayground = ZoneUtil.isPlayground(branchId) + return (npcName, + hoodName, + buildingArticle, + buildingName, + toStreet, + streetName, + isInPlayground) + + +def getNpcLocationDialog(fromNpcId, toNpcId): + if not toNpcId: + return (None, None, None) + fromNpcZone = None + fromBranchId = None + if fromNpcId: + fromNpcZone = NPCToons.getNPCZone(fromNpcId) + fromBranchId = ZoneUtil.getCanonicalBranchZone(fromNpcZone) + toNpcZone = NPCToons.getNPCZone(toNpcId) + toBranchId = ZoneUtil.getCanonicalBranchZone(toNpcZone) + toNpcName, toHoodName, toBuildingArticle, toBuildingName, toStreetTo, toStreetName, isInPlayground = getNpcInfo(toNpcId) + if fromBranchId == toBranchId: + if isInPlayground: + streetDesc = TTLocalizer.QuestsStreetLocationThisPlayground + else: + streetDesc = TTLocalizer.QuestsStreetLocationThisStreet + elif isInPlayground: + streetDesc = TTLocalizer.QuestsStreetLocationNamedPlayground % toHoodName + else: + streetDesc = TTLocalizer.QuestsStreetLocationNamedStreet % {'toStreetName': toStreetName, + 'toHoodName': toHoodName} + paragraph = TTLocalizer.QuestsLocationParagraph % {'building': TTLocalizer.QuestsLocationBuilding % toNpcName, + 'buildingName': toBuildingName, + 'buildingVerb': TTLocalizer.QuestsLocationBuildingVerb, + 'street': streetDesc} + return (paragraph, toBuildingName, streetDesc) + + +def fillInQuestNames(text, avName = None, fromNpcId = None, toNpcId = None): + text = copy.deepcopy(text) + if avName != None: + text = text.replace('_avName_', avName) + if toNpcId: + if toNpcId == ToonHQ: + toNpcName = TTLocalizer.QuestsHQOfficerFillin + where = TTLocalizer.QuestsHQWhereFillin + buildingName = TTLocalizer.QuestsHQBuildingNameFillin + streetDesc = TTLocalizer.QuestsHQLocationNameFillin + elif toNpcId == ToonTailor: + toNpcName = TTLocalizer.QuestsTailorFillin + where = TTLocalizer.QuestsTailorWhereFillin + buildingName = TTLocalizer.QuestsTailorBuildingNameFillin + streetDesc = TTLocalizer.QuestsTailorLocationNameFillin + else: + toNpcName = str(NPCToons.getNPCName(toNpcId)) + where, buildingName, streetDesc = getNpcLocationDialog(fromNpcId, toNpcId) + text = text.replace('_toNpcName_', toNpcName) + text = text.replace('_where_', where) + text = text.replace('_buildingName_', buildingName) + text = text.replace('_streetDesc_', streetDesc) + return text + + +def getVisitingQuest(): + return VisitQuest(VISIT_QUEST_ID) + + +class Reward: + def __init__(self, id, reward): + self.id = id + self.reward = reward + + def getId(self): + return self.id + + def getType(self): + return self.__class__ + + def getAmount(self): + return None + + def sendRewardAI(self, av): + raise 'not implemented' + + def countReward(self, qrc): + raise 'not implemented' + + def getString(self): + return 'undefined' + + def getPosterString(self): + return 'base class' + + +class MaxHpReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getAmount(self): + return self.reward[0] + + def sendRewardAI(self, av): + maxHp = av.getMaxHp() + maxHp = min(ToontownGlobals.MaxHpLimit, maxHp + self.getAmount()) + av.b_setMaxHp(maxHp) + av.toonUp(maxHp) + + def countReward(self, qrc): + qrc.maxHp += self.getAmount() + + def getString(self): + return TTLocalizer.QuestsMaxHpReward % self.getAmount() + + def getPosterString(self): + return TTLocalizer.QuestsMaxHpRewardPoster % self.getAmount() + + +class MoneyReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getAmount(self): + return self.reward[0] + + def sendRewardAI(self, av): + money = av.getMoney() + maxMoney = av.getMaxMoney() + av.addMoney(self.getAmount()) + + def countReward(self, qrc): + qrc.money += self.getAmount() + + def getString(self): + amt = self.getAmount() + if amt == 1: + return TTLocalizer.QuestsMoneyRewardSingular + else: + return TTLocalizer.QuestsMoneyRewardPlural % amt + + def getPosterString(self): + amt = self.getAmount() + if amt == 1: + return TTLocalizer.QuestsMoneyRewardPosterSingular + else: + return TTLocalizer.QuestsMoneyRewardPosterPlural % amt + + +class MaxMoneyReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getAmount(self): + return self.reward[0] + + def sendRewardAI(self, av): + av.b_setMaxMoney(self.getAmount()) + + def countReward(self, qrc): + qrc.maxMoney = self.getAmount() + + def getString(self): + amt = self.getAmount() + if amt == 1: + return TTLocalizer.QuestsMaxMoneyRewardSingular + else: + return TTLocalizer.QuestsMaxMoneyRewardPlural % amt + + def getPosterString(self): + amt = self.getAmount() + if amt == 1: + return TTLocalizer.QuestsMaxMoneyRewardPosterSingular + else: + return TTLocalizer.QuestsMaxMoneyRewardPosterPlural % amt + + +class MaxGagCarryReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getAmount(self): + return self.reward[0] + + def getName(self): + return self.reward[1] + + def sendRewardAI(self, av): + av.b_setMaxCarry(self.getAmount()) + + def countReward(self, qrc): + qrc.maxCarry = self.getAmount() + + def getString(self): + name = self.getName() + amt = self.getAmount() + return TTLocalizer.QuestsMaxGagCarryReward % {'name': name, + 'num': amt} + + def getPosterString(self): + name = self.getName() + amt = self.getAmount() + return TTLocalizer.QuestsMaxGagCarryRewardPoster % {'name': name, + 'num': amt} + + +class MaxQuestCarryReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getAmount(self): + return self.reward[0] + + def sendRewardAI(self, av): + av.b_setQuestCarryLimit(self.getAmount()) + + def countReward(self, qrc): + qrc.questCarryLimit = self.getAmount() + + def getString(self): + amt = self.getAmount() + return TTLocalizer.QuestsMaxQuestCarryReward % amt + + def getPosterString(self): + amt = self.getAmount() + return TTLocalizer.QuestsMaxQuestCarryRewardPoster % amt + + +class TeleportReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getZone(self): + return self.reward[0] + + def sendRewardAI(self, av): + av.addTeleportAccess(self.getZone()) + + def countReward(self, qrc): + qrc.addTeleportAccess(self.getZone()) + + def getString(self): + hoodName = ToontownGlobals.hoodNameMap[self.getZone()][-1] + return TTLocalizer.QuestsTeleportReward % hoodName + + def getPosterString(self): + hoodName = ToontownGlobals.hoodNameMap[self.getZone()][-1] + return TTLocalizer.QuestsTeleportRewardPoster % hoodName + + +TrackTrainingQuotas = {ToontownBattleGlobals.HEAL_TRACK: 15, + ToontownBattleGlobals.TRAP_TRACK: 15, + ToontownBattleGlobals.LURE_TRACK: 15, + ToontownBattleGlobals.SOUND_TRACK: 15, + ToontownBattleGlobals.THROW_TRACK: 15, + ToontownBattleGlobals.SQUIRT_TRACK: 15, + ToontownBattleGlobals.DROP_TRACK: 15} + +class TrackTrainingReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getTrack(self): + track = self.reward[0] + if track == None: + track = 0 + return track + + def sendRewardAI(self, av): + av.b_setTrackProgress(self.getTrack(), 0) + + def countReward(self, qrc): + qrc.trackProgressId = self.getTrack() + qrc.trackProgress = 0 + + def getString(self): + trackName = ToontownBattleGlobals.Tracks[self.getTrack()].capitalize() + return TTLocalizer.QuestsTrackTrainingReward % trackName + + def getPosterString(self): + return TTLocalizer.QuestsTrackTrainingRewardPoster + + +class TrackProgressReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getTrack(self): + track = self.reward[0] + if track == None: + track = 0 + return track + + def getProgressIndex(self): + return self.reward[1] + + def sendRewardAI(self, av): + av.addTrackProgress(self.getTrack(), self.getProgressIndex()) + + def countReward(self, qrc): + qrc.addTrackProgress(self.getTrack(), self.getProgressIndex()) + + def getString(self): + trackName = ToontownBattleGlobals.Tracks[self.getTrack()].capitalize() + return TTLocalizer.QuestsTrackProgressReward % {'frameNum': self.getProgressIndex(), + 'trackName': trackName} + + def getPosterString(self): + trackName = ToontownBattleGlobals.Tracks[self.getTrack()].capitalize() + return TTLocalizer.QuestsTrackProgressRewardPoster % {'trackName': trackName, + 'frameNum': self.getProgressIndex()} + + +class TrackCompleteReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getTrack(self): + track = self.reward[0] + if track == None: + track = 0 + return track + + def sendRewardAI(self, av): + av.addTrackAccess(self.getTrack()) + av.clearTrackProgress() + + def countReward(self, qrc): + qrc.addTrackAccess(self.getTrack()) + qrc.clearTrackProgress() + + def getString(self): + trackName = ToontownBattleGlobals.Tracks[self.getTrack()].capitalize() + return TTLocalizer.QuestsTrackCompleteReward % trackName + + def getPosterString(self): + trackName = ToontownBattleGlobals.Tracks[self.getTrack()].capitalize() + return TTLocalizer.QuestsTrackCompleteRewardPoster % trackName + + +class ClothingTicketReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def sendRewardAI(self, av): + pass + + def countReward(self, qrc): + pass + + def getString(self): + return TTLocalizer.QuestsClothingTicketReward + + def getPosterString(self): + return TTLocalizer.QuestsClothingTicketRewardPoster + + +class TIPClothingTicketReward(ClothingTicketReward): + def __init__(self, id, reward): + ClothingTicketReward.__init__(self, id, reward) + + def getString(self): + return TTLocalizer.TIPQuestsClothingTicketReward + + def getPosterString(self): + return TTLocalizer.TIPQuestsClothingTicketRewardPoster + + +class CheesyEffectReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getEffect(self): + return self.reward[0] + + def getHoodId(self): + return self.reward[1] + + def getDurationMinutes(self): + return self.reward[2] + + def sendRewardAI(self, av): + expireTime = int(time.time() / 60 + 0.5) + self.getDurationMinutes() + av.b_setCheesyEffect(self.getEffect(), self.getHoodId(), expireTime) + + def countReward(self, qrc): + pass + + def getString(self): + effect = self.getEffect() + hoodId = self.getHoodId() + duration = self.getDurationMinutes() + string = TTLocalizer.CheesyEffectMinutes + if duration > 90: + duration = int((duration + 30) / 60) + string = TTLocalizer.CheesyEffectHours + if duration > 36: + duration = int((duration + 12) / 24) + string = TTLocalizer.CheesyEffectDays + desc = TTLocalizer.CheesyEffectDescriptions[effect][1] + if hoodId == 0: + whileStr = '' + elif hoodId == 1: + whileStr = TTLocalizer.CheesyEffectExceptIn % TTLocalizer.ToontownCentral[-1] + else: + hoodName = base.cr.hoodMgr.getFullnameFromId(hoodId) + whileStr = TTLocalizer.CheesyEffectWhileYouAreIn % hoodName + if duration: + return string % {'time': duration, + 'effectName': desc, + 'whileIn': whileStr} + else: + return TTLocalizer.CheesyEffectIndefinite % {'effectName': desc, + 'whileIn': whileStr} + + def getPosterString(self): + effect = self.getEffect() + desc = TTLocalizer.CheesyEffectDescriptions[effect][0] + return TTLocalizer.QuestsCheesyEffectRewardPoster % desc + + +class CogSuitPartReward(Reward): + trackNames = [TTLocalizer.Bossbot, + TTLocalizer.Lawbot, + TTLocalizer.Cashbot, + TTLocalizer.Sellbot] + + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getCogTrack(self): + return self.reward[0] + + def getCogPart(self): + return self.reward[1] + + def sendRewardAI(self, av): + dept = self.getCogTrack() + part = self.getCogPart() + av.giveCogPart(part, dept) + + def countReward(self, qrc): + pass + + def getCogTrackName(self): + index = ToontownGlobals.cogDept2index[self.getCogTrack()] + return CogSuitPartReward.trackNames[index] + + def getCogPartName(self): + index = ToontownGlobals.cogDept2index[self.getCogTrack()] + return CogDisguiseGlobals.PartsQueryNames[index][self.getCogPart()] + + def getString(self): + return TTLocalizer.QuestsCogSuitPartReward % {'cogTrack': self.getCogTrackName(), + 'part': self.getCogPartName()} + + def getPosterString(self): + return TTLocalizer.QuestsCogSuitPartRewardPoster % {'cogTrack': self.getCogTrackName(), + 'part': self.getCogPartName()} + + +class BuffReward(Reward): + def sendRewardAI(self, av): + av.addBuff(self.getBuffId(), self.getBuffTime()) + + def getBuffId(self): + return self.reward[0] + + def getBuffTime(self): + return self.reward[1] + + def getString(self): + return TTLocalizer.getBuffString(self.getBuffId(), self.getBuffTime()) + + def getPosterString(self): + return TTLocalizer.getBuffPosterString(self.getBuffId()) + +class EPPReward(Reward): + trackNames = [TTLocalizer.Bossbot, + TTLocalizer.Lawbot, + TTLocalizer.Cashbot, + TTLocalizer.Sellbot] + + def sendRewardAI(self, av): + av.addEPP(self.reward[0]) + + def countReward(self, qrc): + pass + + def getCogTrackName(self): + return EPPReward.trackNames[self.reward[0]] + + def getString(self): + return TTLocalizer.QuestsEPPReward % self.getCogTrackName() + + def getPosterString(self): + return TTLocalizer.QuestsEPPRewardPoster % self.getCogTrackName() + + +def getRewardClass(id): + reward = RewardDict.get(id) + if reward: + return reward[0] + else: + return None + return None + + +def getReward(id): + reward = RewardDict.get(id) + if reward: + rewardClass = reward[0] + return rewardClass(id, reward[1:]) + else: + notify.warning('getReward(): id %s not found.' % id) + return None + return None + + +def getNextRewards(numChoices, tier, av): + rewardTier = list(getRewardsInTier(tier)) + optRewards = list(getOptionalRewardsInTier(tier)) + if tier == TT_TIER + 3: + optRewards = [] + if isLoopingFinalTier(tier): + rewardHistory = map(lambda questDesc: questDesc[3], av.quests) + if notify.getDebug(): + notify.debug('getNextRewards: current rewards (history): %s' % rewardHistory) + else: + rewardHistory = av.getRewardHistory()[1] + if notify.getDebug(): + notify.debug('getNextRewards: rewardHistory: %s' % rewardHistory) + if notify.getDebug(): + notify.debug('getNextRewards: rewardTier: %s' % rewardTier) + notify.debug('getNextRewards: numChoices: %s' % numChoices) + for rewardId in getRewardsInTier(tier): + if getRewardClass(rewardId) == CogSuitPartReward: + deptStr = RewardDict.get(rewardId)[1] + cogPart = RewardDict.get(rewardId)[2] + dept = ToontownGlobals.cogDept2index[deptStr] + if av.hasCogPart(cogPart, dept): + notify.debug('getNextRewards: already has cog part: %s dept: %s' % (cogPart, dept)) + rewardTier.remove(rewardId) + else: + notify.debug('getNextRewards: keeping quest for cog part: %s dept: %s' % (cogPart, dept)) + + for rewardId in rewardHistory: + if rewardId in rewardTier: + rewardTier.remove(rewardId) + elif rewardId in optRewards: + optRewards.remove(rewardId) + elif rewardId in (901, 902, 903, 904, 905, 906, 907): + genericRewardId = 900 + if genericRewardId in rewardTier: + rewardTier.remove(genericRewardId) + elif rewardId > 1000 and rewardId < 1699: + index = rewardId % 100 + genericRewardId = 800 + index + if genericRewardId in rewardTier: + rewardTier.remove(genericRewardId) + + if numChoices == 0: + if len(rewardTier) == 0: + return [] + else: + return [rewardTier[0]] + rewardPool = rewardTier[:numChoices] + for i in xrange(len(rewardPool), numChoices * 2): + if optRewards: + optionalReward = seededRandomChoice(optRewards) + optRewards.remove(optionalReward) + rewardPool.append(optionalReward) + else: + break + + if notify.getDebug(): + notify.debug('getNextRewards: starting reward pool: %s' % rewardPool) + if len(rewardPool) == 0: + if notify.getDebug(): + notify.debug('getNextRewards: no rewards left at all') + return [] + finalRewardPool = [rewardPool.pop(0)] + for i in xrange(numChoices - 1): + if len(rewardPool) == 0: + break + selectedReward = seededRandomChoice(rewardPool) + rewardPool.remove(selectedReward) + finalRewardPool.append(selectedReward) + + if notify.getDebug(): + notify.debug('getNextRewards: final reward pool: %s' % finalRewardPool) + return finalRewardPool + + +RewardDict = { + 100: (MaxHpReward, 1), + 101: (MaxHpReward, 2), + 102: (MaxHpReward, 3), + 103: (MaxHpReward, 4), + 104: (MaxHpReward, 5), + 105: (MaxHpReward, 6), + 106: (MaxHpReward, 7), + 107: (MaxHpReward, 8), + 108: (MaxHpReward, 9), + 109: (MaxHpReward, 10), + 200: (MaxGagCarryReward, 25, TTLocalizer.QuestsMediumPouch), + 201: (MaxGagCarryReward, 30, TTLocalizer.QuestsLargePouch), + 202: (MaxGagCarryReward, 35, TTLocalizer.QuestsSmallBag), + 203: (MaxGagCarryReward, 40, TTLocalizer.QuestsMediumBag), + 204: (MaxGagCarryReward, 50, TTLocalizer.QuestsLargeBag), + 205: (MaxGagCarryReward, 60, TTLocalizer.QuestsSmallBackpack), + 206: (MaxGagCarryReward, 70, TTLocalizer.QuestsMediumBackpack), + 207: (MaxGagCarryReward, 80, TTLocalizer.QuestsLargeBackpack), + 300: (TeleportReward, ToontownGlobals.ToontownCentral), + 301: (TeleportReward, ToontownGlobals.DonaldsDock), + 302: (TeleportReward, ToontownGlobals.DaisyGardens), + 303: (TeleportReward, ToontownGlobals.MinniesMelodyland), + 304: (TeleportReward, ToontownGlobals.TheBrrrgh), + 305: (TeleportReward, ToontownGlobals.DonaldsDreamland), + 400: (TrackTrainingReward, None), + 401: (TrackTrainingReward, ToontownBattleGlobals.HEAL_TRACK), + 402: (TrackTrainingReward, ToontownBattleGlobals.TRAP_TRACK), + 403: (TrackTrainingReward, ToontownBattleGlobals.LURE_TRACK), + 404: (TrackTrainingReward, ToontownBattleGlobals.SOUND_TRACK), + 405: (TrackTrainingReward, ToontownBattleGlobals.THROW_TRACK), + 406: (TrackTrainingReward, ToontownBattleGlobals.SQUIRT_TRACK), + 407: (TrackTrainingReward, ToontownBattleGlobals.DROP_TRACK), + 500: (MaxQuestCarryReward, 2), + 501: (MaxQuestCarryReward, 3), + 502: (MaxQuestCarryReward, 4), + 600: (MoneyReward, 10), + 601: (MoneyReward, 20), + 602: (MoneyReward, 40), + 603: (MoneyReward, 60), + 604: (MoneyReward, 100), + 605: (MoneyReward, 150), + 606: (MoneyReward, 200), + 607: (MoneyReward, 250), + 608: (MoneyReward, 300), + 609: (MoneyReward, 400), + 610: (MoneyReward, 500), + 611: (MoneyReward, 600), + 612: (MoneyReward, 700), + 613: (MoneyReward, 800), + 614: (MoneyReward, 900), + 615: (MoneyReward, 1000), + 616: (MoneyReward, 1100), + 617: (MoneyReward, 1200), + 618: (MoneyReward, 1300), + 619: (MoneyReward, 1400), + 620: (MoneyReward, 1500), + 621: (MoneyReward, 1750), + 622: (MoneyReward, 2000), + 623: (MoneyReward, 2500), + 700: (MaxMoneyReward, 50), + 701: (MaxMoneyReward, 60), + 702: (MaxMoneyReward, 80), + 703: (MaxMoneyReward, 100), + 704: (MaxMoneyReward, 120), + 705: (MaxMoneyReward, 150), + 706: (MaxMoneyReward, 200), + 707: (MaxMoneyReward, 250), + 801: (TrackProgressReward, None, 1), + 802: (TrackProgressReward, None, 2), + 803: (TrackProgressReward, None, 3), + 804: (TrackProgressReward, None, 4), + 805: (TrackProgressReward, None, 5), + 806: (TrackProgressReward, None, 6), + 807: (TrackProgressReward, None, 7), + 808: (TrackProgressReward, None, 8), + 809: (TrackProgressReward, None, 9), + 810: (TrackProgressReward, None, 10), + 811: (TrackProgressReward, None, 11), + 812: (TrackProgressReward, None, 12), + 813: (TrackProgressReward, None, 13), + 814: (TrackProgressReward, None, 14), + 815: (TrackProgressReward, None, 15), + 110: (TIPClothingTicketReward,), + 1000: (ClothingTicketReward,), + 1001: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 1), + 1002: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 2), + 1003: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 3), + 1004: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 4), + 1005: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 5), + 1006: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 6), + 1007: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 7), + 1008: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 8), + 1009: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 9), + 1010: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 10), + 1011: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 11), + 1012: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 12), + 1013: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 13), + 1014: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 14), + 1015: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 15), + 1101: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 1), + 1102: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 2), + 1103: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 3), + 1104: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 4), + 1105: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 5), + 1106: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 6), + 1107: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 7), + 1108: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 8), + 1109: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 9), + 1110: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 10), + 1111: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 11), + 1112: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 12), + 1113: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 13), + 1114: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 14), + 1115: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 15), + 1201: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 1), + 1202: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 2), + 1203: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 3), + 1204: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 4), + 1205: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 5), + 1206: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 6), + 1207: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 7), + 1208: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 8), + 1209: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 9), + 1210: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 10), + 1211: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 11), + 1212: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 12), + 1213: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 13), + 1214: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 14), + 1215: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 15), + 1301: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 1), + 1302: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 2), + 1303: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 3), + 1304: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 4), + 1305: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 5), + 1306: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 6), + 1307: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 7), + 1308: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 8), + 1309: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 9), + 1310: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 10), + 1311: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 11), + 1312: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 12), + 1313: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 13), + 1314: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 14), + 1315: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 15), + 1601: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 1), + 1602: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 2), + 1603: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 3), + 1604: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 4), + 1605: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 5), + 1606: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 6), + 1607: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 7), + 1608: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 8), + 1609: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 9), + 1610: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 10), + 1611: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 11), + 1612: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 12), + 1613: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 13), + 1614: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 14), + 1615: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 15), + 900: (TrackCompleteReward, None), + 901: (TrackCompleteReward, ToontownBattleGlobals.HEAL_TRACK), + 902: (TrackCompleteReward, ToontownBattleGlobals.TRAP_TRACK), + 903: (TrackCompleteReward, ToontownBattleGlobals.LURE_TRACK), + 904: (TrackCompleteReward, ToontownBattleGlobals.SOUND_TRACK), + 905: (TrackCompleteReward, ToontownBattleGlobals.THROW_TRACK), + 906: (TrackCompleteReward, ToontownBattleGlobals.SQUIRT_TRACK), + 907: (TrackCompleteReward, ToontownBattleGlobals.DROP_TRACK), + 2205: (CheesyEffectReward, ToontownGlobals.CEBigToon, 2000, 10), + 2206: (CheesyEffectReward, ToontownGlobals.CESmallToon, 2000, 10), + 2101: (CheesyEffectReward, ToontownGlobals.CEBigHead, 1000, 10), + 2102: (CheesyEffectReward, ToontownGlobals.CESmallHead, 1000, 10), + 2105: (CheesyEffectReward, ToontownGlobals.CEBigToon, 0, 20), + 2106: (CheesyEffectReward, ToontownGlobals.CESmallToon, 0, 20), + 2501: (CheesyEffectReward, ToontownGlobals.CEBigHead, 5000, 60), + 2502: (CheesyEffectReward, ToontownGlobals.CESmallHead, 5000, 60), + 2503: (CheesyEffectReward, ToontownGlobals.CEBigLegs, 5000, 20), + 2504: (CheesyEffectReward, ToontownGlobals.CESmallLegs, 5000, 20), + 2505: (CheesyEffectReward, ToontownGlobals.CEBigToon, 0, 60), + 2506: (CheesyEffectReward, ToontownGlobals.CESmallToon, 0, 60), + 2401: (CheesyEffectReward, ToontownGlobals.CEBigHead, 1, 120), + 2402: (CheesyEffectReward, ToontownGlobals.CESmallHead, 1, 120), + 2403: (CheesyEffectReward, ToontownGlobals.CEBigLegs, 4000, 60), + 2404: (CheesyEffectReward, ToontownGlobals.CESmallLegs, 4000, 60), + 2405: (CheesyEffectReward, ToontownGlobals.CEBigToon, 0, 120), + 2406: (CheesyEffectReward, ToontownGlobals.CESmallToon, 0, 120), + 2407: (CheesyEffectReward, ToontownGlobals.CEFlatPortrait, 4000, 30), + 2408: (CheesyEffectReward, ToontownGlobals.CEFlatProfile, 4000, 30), + 2409: (CheesyEffectReward, ToontownGlobals.CETransparent, 4000, 30), + 2410: (CheesyEffectReward, ToontownGlobals.CENoColor, 4000, 30), + 2301: (CheesyEffectReward, ToontownGlobals.CEBigHead, 1, 360), + 2302: (CheesyEffectReward, ToontownGlobals.CESmallHead, 1, 360), + 2303: (CheesyEffectReward, ToontownGlobals.CEBigLegs, 1, 360), + 2304: (CheesyEffectReward, ToontownGlobals.CESmallLegs, 1, 360), + 2305: (CheesyEffectReward, ToontownGlobals.CEBigToon, 0, 1440), + 2306: (CheesyEffectReward, ToontownGlobals.CESmallToon, 0, 1440), + 2307: (CheesyEffectReward, ToontownGlobals.CEFlatPortrait, 3000, 240), + 2308: (CheesyEffectReward, ToontownGlobals.CEFlatProfile, 3000, 240), + 2309: (CheesyEffectReward, ToontownGlobals.CETransparent, 1, 120), + 2310: (CheesyEffectReward, ToontownGlobals.CENoColor, 1, 120), + 2311: (CheesyEffectReward, ToontownGlobals.CEInvisible, 3000, 120), + 2900: (CheesyEffectReward, ToontownGlobals.CENormal, 0, 0), + 2901: (CheesyEffectReward, ToontownGlobals.CEBigHead, 1, 1440), + 2902: (CheesyEffectReward, ToontownGlobals.CESmallHead, 1, 1440), + 2903: (CheesyEffectReward, ToontownGlobals.CEBigLegs, 1, 1440), + 2904: (CheesyEffectReward, ToontownGlobals.CESmallLegs, 1, 1440), + 2905: (CheesyEffectReward, ToontownGlobals.CEBigToon, 0, 1440), + 2906: (CheesyEffectReward, ToontownGlobals.CESmallToon, 0, 1440), + 2907: (CheesyEffectReward, ToontownGlobals.CEFlatPortrait, 1, 1440), + 2908: (CheesyEffectReward, ToontownGlobals.CEFlatProfile, 1, 1440), + 2909: (CheesyEffectReward, ToontownGlobals.CETransparent, 1, 1440), + 2910: (CheesyEffectReward, ToontownGlobals.CENoColor, 1, 1440), + 2911: (CheesyEffectReward, ToontownGlobals.CEInvisible, 1, 1440), + 2920: (CheesyEffectReward, ToontownGlobals.CENormal, 0, 0), + 2921: (CheesyEffectReward, ToontownGlobals.CEBigHead, 1, 2880), + 2922: (CheesyEffectReward, ToontownGlobals.CESmallHead, 1, 2880), + 2923: (CheesyEffectReward, ToontownGlobals.CEBigLegs, 1, 2880), + 2924: (CheesyEffectReward, ToontownGlobals.CESmallLegs, 1, 2880), + 2925: (CheesyEffectReward, ToontownGlobals.CEBigToon, 0, 2880), + 2926: (CheesyEffectReward, ToontownGlobals.CESmallToon, 0, 2880), + 2927: (CheesyEffectReward, ToontownGlobals.CEFlatPortrait, 1, 2880), + 2928: (CheesyEffectReward, ToontownGlobals.CEFlatProfile, 1, 2880), + 2929: (CheesyEffectReward, ToontownGlobals.CETransparent, 1, 2880), + 2930: (CheesyEffectReward, ToontownGlobals.CENoColor, 1, 2880), + 2931: (CheesyEffectReward, ToontownGlobals.CEInvisible, 1, 2880), + 2940: (CheesyEffectReward, ToontownGlobals.CENormal, 0, 0), + 2941: (CheesyEffectReward, ToontownGlobals.CEBigHead, 1, 10080), + 2942: (CheesyEffectReward, ToontownGlobals.CESmallHead, 1, 10080), + 2943: (CheesyEffectReward, ToontownGlobals.CEBigLegs, 1, 10080), + 2944: (CheesyEffectReward, ToontownGlobals.CESmallLegs, 1, 10080), + 2945: (CheesyEffectReward, ToontownGlobals.CEBigToon, 0, 10080), + 2946: (CheesyEffectReward, ToontownGlobals.CESmallToon, 0, 10080), + 2947: (CheesyEffectReward, ToontownGlobals.CEFlatPortrait, 1, 10080), + 2948: (CheesyEffectReward, ToontownGlobals.CEFlatProfile, 1, 10080), + 2949: (CheesyEffectReward, ToontownGlobals.CETransparent, 1, 10080), + 2950: (CheesyEffectReward, ToontownGlobals.CENoColor, 1, 10080), + 2951: (CheesyEffectReward, ToontownGlobals.CEInvisible, 1, 10080), + 2960: (CheesyEffectReward, ToontownGlobals.CENormal, 0, 0), + 2961: (CheesyEffectReward, ToontownGlobals.CEBigHead, 1, 43200), + 2962: (CheesyEffectReward, ToontownGlobals.CESmallHead, 1, 43200), + 2963: (CheesyEffectReward, ToontownGlobals.CEBigLegs, 1, 43200), + 2964: (CheesyEffectReward, ToontownGlobals.CESmallLegs, 1, 43200), + 2965: (CheesyEffectReward, ToontownGlobals.CEBigToon, 0, 43200), + 2966: (CheesyEffectReward, ToontownGlobals.CESmallToon, 0, 43200), + 2967: (CheesyEffectReward, ToontownGlobals.CEFlatPortrait, 1, 43200), + 2968: (CheesyEffectReward, ToontownGlobals.CEFlatProfile, 1, 43200), + 2969: (CheesyEffectReward, ToontownGlobals.CETransparent, 1, 43200), + 2970: (CheesyEffectReward, ToontownGlobals.CENoColor, 1, 43200), + 2971: (CheesyEffectReward, ToontownGlobals.CEInvisible, 1, 43200), + 4000: (CogSuitPartReward, 'm', CogDisguiseGlobals.leftLegUpper), + 4001: (CogSuitPartReward, 'm', CogDisguiseGlobals.leftLegLower), + 4002: (CogSuitPartReward, 'm', CogDisguiseGlobals.leftLegFoot), + 4003: (CogSuitPartReward, 'm', CogDisguiseGlobals.rightLegUpper), + 4004: (CogSuitPartReward, 'm', CogDisguiseGlobals.rightLegLower), + 4005: (CogSuitPartReward, 'm', CogDisguiseGlobals.rightLegFoot), + 4006: (CogSuitPartReward, 'm', CogDisguiseGlobals.upperTorso), + 4007: (CogSuitPartReward, 'm', CogDisguiseGlobals.torsoPelvis), + 4008: (CogSuitPartReward, 'm', CogDisguiseGlobals.leftArmUpper), + 4009: (CogSuitPartReward, 'm', CogDisguiseGlobals.leftArmLower), + 4010: (CogSuitPartReward, 'm', CogDisguiseGlobals.rightArmUpper), + 4011: (CogSuitPartReward, 'm', CogDisguiseGlobals.rightArmLower), + 4100: (CogSuitPartReward, 'l', CogDisguiseGlobals.leftLegUpper), + 4101: (CogSuitPartReward, 'l', CogDisguiseGlobals.leftLegLower), + 4102: (CogSuitPartReward, 'l', CogDisguiseGlobals.leftLegFoot), + 4103: (CogSuitPartReward, 'l', CogDisguiseGlobals.rightLegUpper), + 4104: (CogSuitPartReward, 'l', CogDisguiseGlobals.rightLegLower), + 4105: (CogSuitPartReward, 'l', CogDisguiseGlobals.rightLegFoot), + 4106: (CogSuitPartReward, 'l', CogDisguiseGlobals.upperTorso), + 4107: (CogSuitPartReward, 'l', CogDisguiseGlobals.torsoPelvis), + 4108: (CogSuitPartReward, 'l', CogDisguiseGlobals.leftArmUpper), + 4109: (CogSuitPartReward, 'l', CogDisguiseGlobals.leftArmLower), + 4110: (CogSuitPartReward, 'l', CogDisguiseGlobals.leftArmHand), + 4111: (CogSuitPartReward, 'l', CogDisguiseGlobals.rightArmUpper), + 4112: (CogSuitPartReward, 'l', CogDisguiseGlobals.rightArmLower), + 4113: (CogSuitPartReward, 'l', CogDisguiseGlobals.rightArmHand), + 4200: (CogSuitPartReward, 'c', CogDisguiseGlobals.leftLegUpper), + 4201: (CogSuitPartReward, 'c', CogDisguiseGlobals.leftLegLower), + 4202: (CogSuitPartReward, 'c', CogDisguiseGlobals.leftLegFoot), + 4203: (CogSuitPartReward, 'c', CogDisguiseGlobals.rightLegUpper), + 4204: (CogSuitPartReward, 'c', CogDisguiseGlobals.rightLegLower), + 4205: (CogSuitPartReward, 'c', CogDisguiseGlobals.rightLegFoot), + 4206: (CogSuitPartReward, 'c', CogDisguiseGlobals.torsoLeftShoulder), + 4207: (CogSuitPartReward, 'c', CogDisguiseGlobals.torsoRightShoulder), + 4208: (CogSuitPartReward, 'c', CogDisguiseGlobals.torsoChest), + 4209: (CogSuitPartReward, 'c', CogDisguiseGlobals.torsoHealthMeter), + 4210: (CogSuitPartReward, 'c', CogDisguiseGlobals.torsoPelvis), + 4211: (CogSuitPartReward, 'c', CogDisguiseGlobals.leftArmUpper), + 4212: (CogSuitPartReward, 'c', CogDisguiseGlobals.leftArmLower), + 4213: (CogSuitPartReward, 'c', CogDisguiseGlobals.leftArmHand), + 4214: (CogSuitPartReward, 'c', CogDisguiseGlobals.rightArmUpper), + 4215: (CogSuitPartReward, 'c', CogDisguiseGlobals.rightArmLower), + 4216: (CogSuitPartReward, 'c', CogDisguiseGlobals.rightArmHand), + # Buff rewards (BuffID, Time): + # Movement Speed Increase + 3001: (BuffReward, ToontownGlobals.BMovementSpeed, 30), + 3002: (BuffReward, ToontownGlobals.BMovementSpeed, 60), + 3003: (BuffReward, ToontownGlobals.BMovementSpeed, 180), + 3004: (BuffReward, ToontownGlobals.BMovementSpeed, 360), + 10000: (EPPReward, 0), # Bossbot + 10001: (EPPReward, 1), # Lawbot + 10002: (EPPReward, 2), # Cashbot + 10003: (EPPReward, 3) # Sellbot +} + +BuffRewardIds = [3001, 3002, 3003, 3004] + +def getNumTiers(): + return len(RequiredRewardTrackDict) - 1 + + +def isLoopingFinalTier(tier): + return tier == LOOPING_FINAL_TIER + + +def getRewardsInTier(tier): + return RequiredRewardTrackDict.get(tier, []) + + +def getNumRewardsInTier(tier): + return len(RequiredRewardTrackDict.get(tier, [])) + + +def rewardTierExists(tier): + return tier in RequiredRewardTrackDict + + +def getOptionalRewardsInTier(tier): + return OptionalRewardTrackDict.get(tier, []) + + +def getRewardIdFromTrackId(trackId): + return 401 + trackId + + +RequiredRewardTrackDict = { + TT_TIER: (100,), + TT_TIER + 1: (400,), + TT_TIER + 2: (100, 801, 200, 802, 803, 101, 804, 805, 102, 806, 807, 100, 808, 809, 101, 810, 811, 500, 812, 813, 700, 814, 815, 300), + TT_TIER + 3: (900,), + DD_TIER: (400,), + DD_TIER + 1: (100, 801, 802, 201, 803, 804, 101, 805, 806, 102, 807, 808, 100, 809, 810, 101, 811, 812, 701, 813, 814, 815, 301), + DD_TIER + 2: (900,), + DG_TIER: (100, 202, 101, 102, 100, 101, 501, 702, 302), + MM_TIER: (400,), + MM_TIER + 1: (100, 801, 802, 203, 803, 804, 101, 805, 806, 102, 807, 808, 100, 809, 810, 101, 811, 812, 703, 813, 814, 815, 303), + MM_TIER + 2: (900,), + BR_TIER: (400,), + BR_TIER + 1: (100, 801, 802, 704, 803, 804, 101, 805, 806, 502, 807, 808, 102, 809, 810, 204, 811, 812, 100, 813, 814, 101, 815, 304, 10003), + BR_TIER + 2: (900,), + DL_TIER: (4000, 100, 205, 101, 102, 705, 103, 305, 4001, 4002), + DL_TIER + 1: (100, 206, 101, 4003, 4004, 4005, 102, 4006, 4007, 4008, 706, 103, 4009, 4010, 4011, 4000, 4001, 4002), + DL_TIER + 2: (4006, 4007, 4008, 100, 4000, 4001, 4002, 4003, 101, 4004, 4005, 4009, 102, 103, 4010, 4011), + DL_TIER + 3: (4009, 4010, 4011, 100, 4000, 4001, 101, 4002, 4003, 102, 4004, 4005, 102, 4006, 4007, 707, 207, 4008, 10002), + LAWBOT_HQ_TIER: (4100,), + LAWBOT_HQ_TIER + 1: (4101,), + LAWBOT_HQ_TIER + 2: (4102,), + LAWBOT_HQ_TIER + 3: (4103,), + LAWBOT_HQ_TIER + 4: (4104,), + LAWBOT_HQ_TIER + 5: (4105,), + LAWBOT_HQ_TIER + 6: (4106,), + LAWBOT_HQ_TIER + 7: (4107,), + LAWBOT_HQ_TIER + 8: (4108,), + LAWBOT_HQ_TIER + 9: (4109,), + LAWBOT_HQ_TIER + 10: (4110,), + LAWBOT_HQ_TIER + 11: (4111,), + LAWBOT_HQ_TIER + 12: (4112,), + LAWBOT_HQ_TIER + 13: (4113,), + BOSSBOT_HQ_TIER: (4200,), + BOSSBOT_HQ_TIER + 1: (4201,), + BOSSBOT_HQ_TIER + 2: (4202,), + BOSSBOT_HQ_TIER + 3: (4203,), + BOSSBOT_HQ_TIER + 4: (4204,), + BOSSBOT_HQ_TIER + 5: (4205,), + BOSSBOT_HQ_TIER + 6: (4206,), + BOSSBOT_HQ_TIER + 7: (4207,), + BOSSBOT_HQ_TIER + 8: (4208,), + BOSSBOT_HQ_TIER + 9: (4209,), + BOSSBOT_HQ_TIER + 10: (4210,), + BOSSBOT_HQ_TIER + 11: (4211,), + BOSSBOT_HQ_TIER + 12: (4212,), + BOSSBOT_HQ_TIER + 13: (4213,), + BOSSBOT_HQ_TIER + 14: (4214,), + BOSSBOT_HQ_TIER + 15: (4215,), + BOSSBOT_HQ_TIER + 16: (4216,), + PRE_ELDER_TIER: (10001, 10000), + ELDER_TIER: (4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 4009, 4010, 4011)} + +OptionalRewardTrackDict = { + TT_TIER: (), + TT_TIER + 1: (), + TT_TIER + 2: (1000, 601, 601, 602, 602, 2205, 2206, 2205, 2206, 3001, 3001, 3001, 3001), + TT_TIER + 3: (601, 601, 602, 602, 2205, 2206, 2205, 2206, 3002, 3001, 3001, 3001), + DD_TIER: (1000, 602, 602, 603, 603, 2101, 2102, 2105, 2106, 3002, 3002, 3002, 3001), + DD_TIER + 1: (1000, 602, 602, 603, 603, 2101, 2102, 2105, 2106, 3002, 3002, 3002, 3001), + DD_TIER + 2: (1000, 602, 602, 603, 603, 2101, 2102, 2105, 2106, 3002, 3002, 3002, 3002), + DG_TIER: (1000, 603, 603, 604, 604, 2501, 2502, 2503, 2504, 2505, 2506, 3002, 3002, 3002, 3002), + MM_TIER: (1000, 604, 604, 605, 605, 2403, 2404, 2405, 2406, 2407, 2408, 2409, 3002, 3002, 3002, 3002), + MM_TIER + 1: (1000, 604, 604, 605, 605, 2403, 2404, 2405, 2406, 2407, 2408, 2409, 3003, 3003, 3002, 3002), + MM_TIER + 2: (1000, 604, 604, 605, 605, 2403, 2404, 2405, 2406, 2407, 2408, 2409, 3003, 3003, 3002, 3002), + BR_TIER: (1000, 2305, 2306, 2307, 2308, 2309, 2310, 2311, 3003, 3003, 3003, 3003), + BR_TIER + 1: (1000, 2305, 2306, 2307, 2308, 2309, 2310, 2311, 3003, 3003, 3003, 3003), + BR_TIER + 2: (1000, 2305, 2306, 2307, 2308, 2309, 2310, 2311, 3003, 3003, 3003, 3003), + DL_TIER: (2901, 2902, 2907, 2908, 2909, 2910, 2911, 3003, 3003, 3004, 3004), + DL_TIER + 1: (1000, 2923, 2924, 2927, 2928, 2929, 2930, 2931, 3003, 3003, 3004, 3004), + DL_TIER + 2: (2941, 2942, 2943, 2944, 2947, 2948, 2949, 2950, 2951, 3004, 3004, 3004, 3004), + DL_TIER + 3: (1000, 2961, 2962, 2963, 2964, 2965, 2966, 2967, 2968, 2969, 2970, 2971, 3004, 3004, 3004, 3004), + ELDER_TIER: (1000, 1000, 613, 614, 615, 616, 617, 618, 2961, 2962, 2963, 2964, 2965, 2966, 2967, 2968, 2969, 2970, 2971, 3004, 3004, 3004) +} + +def isRewardOptional(tier, rewardId): + return tier in OptionalRewardTrackDict and rewardId in OptionalRewardTrackDict[tier] + + +def getItemName(itemId): + return ItemDict[itemId][0] + + +def getPluralItemName(itemId): + return ItemDict[itemId][1] + + +def avatarHasTrolleyQuest(av): + return len(av.quests) == 1 and av.quests[0][0] == TROLLEY_QUEST_ID + + +def avatarHasCompletedTrolleyQuest(av): + return av.quests[0][4] > 0 + + +def avatarHasFirstCogQuest(av): + return len(av.quests) == 1 and av.quests[0][0] == FIRST_COG_QUEST_ID + + +def avatarHasCompletedFirstCogQuest(av): + return av.quests[0][4] > 0 + + +def avatarHasFriendQuest(av): + return len(av.quests) == 1 and av.quests[0][0] == FRIEND_QUEST_ID + + +def avatarHasCompletedFriendQuest(av): + return av.quests[0][4] > 0 + + +def avatarHasPhoneQuest(av): + return len(av.quests) == 1 and av.quests[0][0] == PHONE_QUEST_ID + + +def avatarHasCompletedPhoneQuest(av): + return av.quests[0][4] > 0 + + +def avatarWorkingOnRequiredRewards(av): + tier = av.getRewardTier() + rewardList = list(getRewardsInTier(tier)) + for i in xrange(len(rewardList)): + actualRewardId = transformReward(rewardList[i], av) + rewardList[i] = actualRewardId + + for questDesc in av.quests: + questId = questDesc[0] + rewardId = questDesc[3] + if rewardId in rewardList: + return 1 + elif rewardId == NA: + rewardId = transformReward(getFinalRewardId(questId, fAll=1), av) + if rewardId in rewardList: + return 1 + + return 0 + + +def avatarHasAllRequiredRewards(av, tier): + # Get the reward history. + rewardHistory = list(av.getRewardHistory()[1]) + + # Delete quests we're working on from the reward History. + avQuests = av.getQuests() + + # Iterate through the current quests. + for i in xrange(0, len(avQuests), 5): + questDesc = avQuests[i:i + 5] + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + transformedRewardId = transformReward(rewardId, av) + + if rewardId in rewardHistory: + rewardHistory.remove(rewardId) + + if transformedRewardId in rewardHistory: + rewardHistory.remove(transformedRewardId) + + rewardList = getRewardsInTier(tier) + notify.debug('checking avatarHasAllRequiredRewards: history: %s, tier: %s' % (rewardHistory, rewardList)) + for rewardId in rewardList: + if rewardId == 900: + found = 0 + for actualRewardId in (901, 902, 903, 904, 905, 906, 907): + if actualRewardId in rewardHistory: + found = 1 + rewardHistory.remove(actualRewardId) + if notify.getDebug(): + notify.debug('avatarHasAllRequiredRewards: rewardId 900 found as: %s' % actualRewardId) + break + + if not found: + if notify.getDebug(): + notify.debug('avatarHasAllRequiredRewards: rewardId 900 not found') + return 0 + else: + actualRewardId = transformReward(rewardId, av) + if actualRewardId in rewardHistory: + rewardHistory.remove(actualRewardId) + elif getRewardClass(rewardId) == CogSuitPartReward: + deptStr = RewardDict.get(rewardId)[1] + cogPart = RewardDict.get(rewardId)[2] + dept = ToontownGlobals.cogDept2index[deptStr] + if av.hasCogPart(cogPart, dept): + if notify.getDebug(): + notify.debug('avatarHasAllRequiredRewards: rewardId: %s counts, avatar has cog part: %s dept: %s' % (actualRewardId, cogPart, dept)) + else: + if notify.getDebug(): + notify.debug('avatarHasAllRequiredRewards: CogSuitPartReward: %s not found' % actualRewardId) + return 0 + else: + if notify.getDebug(): + notify.debug('avatarHasAllRequiredRewards: rewardId %s not found' % actualRewardId) + return 0 + + if notify.getDebug(): + notify.debug('avatarHasAllRequiredRewards: remaining rewards: %s' % rewardHistory) + for rewardId in rewardHistory: + if not isRewardOptional(tier, rewardId): + notify.warning('required reward found, expected only optional: %s' % rewardId) + + return 1 + + +def nextQuestList(nextQuest): + if nextQuest == NA: + return None + seqTypes = (types.ListType, types.TupleType) + if type(nextQuest) in seqTypes: + return nextQuest + else: + return (nextQuest,) + return None + + +def checkReward(questId, forked = 0): + quest = QuestDict[questId] + reward = quest[5] + nextQuests = nextQuestList(quest[6]) + if nextQuests is None: + validRewards = RewardDict.keys() + [Any, + AnyCashbotSuitPart, + AnyLawbotSuitPart, + OBSOLETE] + if reward is OBSOLETE: + print 'warning: quest %s is obsolete' % questId + return reward + else: + forked = forked or len(nextQuests) > 1 + firstReward = checkReward(nextQuests[0], forked) + for qId in nextQuests[1:]: + thisReward = checkReward(qId, forked) + + return firstReward + return + + +def assertAllQuestsValid(): + print 'checking quests...' + for questId in QuestDict.keys(): + try: + quest = getQuest(questId) + except AssertionError, e: + err = 'invalid quest: %s' % questId + print err + raise + + for questId in QuestDict.keys(): + quest = QuestDict[questId] + tier, start, questDesc, fromNpc, toNpc, reward, nextQuest, dialog = quest + if start: + checkReward(questId) diff --git a/toontown/quest/TrackChoiceGui.py b/toontown/quest/TrackChoiceGui.py new file mode 100755 index 00000000..76d698c2 --- /dev/null +++ b/toontown/quest/TrackChoiceGui.py @@ -0,0 +1,69 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import ToontownTimer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import TTLocalizer + +class TrackPoster(DirectFrame): + normalTextColor = (0.3, 0.25, 0.2, 1) + + def __init__(self, trackId, callback): + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(TrackPoster) + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + trackName = ToontownBattleGlobals.Tracks[trackId].capitalize() + self.poster = DirectFrame(parent=self, relief=None, image=bookModel.find('**/questCard'), image_scale=(0.8, 0.58, 0.58)) + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + iconGeom = invModel.find('**/' + ToontownBattleGlobals.AvPropsNew[trackId][1]) + invModel.removeNode() + self.pictureFrame = DirectFrame(parent=self.poster, relief=None, image=bookModel.find('**/questPictureFrame'), image_scale=0.25, image_color=(0.45, 0.8, 0.45, 1), text=trackName, text_font=ToontownGlobals.getInterfaceFont(), text_pos=(0, -0.16), text_fg=self.normalTextColor, text_scale=0.05, text_align=TextNode.ACenter, text_wordwrap=8.0, textMayChange=0, geom=iconGeom, pos=(-0.2, 0, 0.06)) + bookModel.removeNode() + if trackId == ToontownBattleGlobals.HEAL_TRACK: + help = TTLocalizer.TrackChoiceGuiHEAL + elif trackId == ToontownBattleGlobals.TRAP_TRACK: + help = TTLocalizer.TrackChoiceGuiTRAP + elif trackId == ToontownBattleGlobals.LURE_TRACK: + help = TTLocalizer.TrackChoiceGuiLURE + elif trackId == ToontownBattleGlobals.SOUND_TRACK: + help = TTLocalizer.TrackChoiceGuiSOUND + elif trackId == ToontownBattleGlobals.DROP_TRACK: + help = TTLocalizer.TrackChoiceGuiDROP + else: + help = '' + self.helpText = DirectFrame(parent=self.poster, relief=None, text=help, text_font=ToontownGlobals.getInterfaceFont(), text_fg=self.normalTextColor, text_scale=0.05, text_align=TextNode.ALeft, text_wordwrap=8.0, textMayChange=0, pos=(-0.05, 0, 0.14)) + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.chooseButton = DirectButton(parent=self.poster, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.7, 1, 1), text=TTLocalizer.TrackChoiceGuiChoose, text_scale=0.06, text_pos=(0, -0.02), command=callback, extraArgs=[trackId], pos=(0, 0, -0.16), scale=0.8) + guiButton.removeNode() + return + + +class TrackChoiceGui(DirectFrame): + + def __init__(self, tracks, timeout): + DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=Vec4(0.8, 0.6, 0.4, 1), geom_scale=(1.5, 1, 0.9), geom_hpr=(0, 0, -90), pos=(-0.85, 0, 0)) + self.initialiseoptions(TrackChoiceGui) + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.cancelButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.7, 1, 1), text=TTLocalizer.TrackChoiceGuiCancel, pos=(0.15, 0, -0.625), text_scale=0.06, text_pos=(0, -0.02), command=self.chooseTrack, extraArgs=[-1]) + guiButton.removeNode() + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(self) + self.timer.setScale(0.35) + self.timer.setPos(-0.2, 0, -0.6) + self.timer.countdown(timeout, self.timeout) + self.trackChoicePosters = [] + for trackId in tracks: + tp = TrackPoster(trackId, self.chooseTrack) + tp.reparentTo(self) + self.trackChoicePosters.append(tp) + + self.trackChoicePosters[0].setPos(0, 0, -0.2) + self.trackChoicePosters[1].setPos(0, 0, 0.4) + return + + def chooseTrack(self, trackId): + self.timer.stop() + messenger.send('chooseTrack', [trackId]) + + def timeout(self): + messenger.send('chooseTrack', [-1]) diff --git a/toontown/quest/__init__.py b/toontown/quest/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/racing/DistributedGag.py b/toontown/racing/DistributedGag.py new file mode 100755 index 00000000..10c90d3b --- /dev/null +++ b/toontown/racing/DistributedGag.py @@ -0,0 +1,106 @@ +from panda3d.core import * +from direct.distributed import DistributedObject +from direct.interval.ProjectileInterval import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.racing.DistributedVehicle import DistributedVehicle +from DroppedGag import * + +class DistributedGag(DistributedObject.DistributedObject): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.nodePath = None + self.billboard = False + self.scale = 1 + self.shadow = True + self.dropShadow = None + self.type = 0 + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.nodePath.delete() + self.ignoreAll() + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + if not self.nodePath: + self.makeNodePath() + self.delta = -globalClockDelta.networkToLocalTime(self.initTime, globalClock.getFrameTime(), 16, 100) + globalClock.getFrameTime() + if self.type == 0: + self.name = self.uniqueName('banana') + elif self.type == 1: + self.name = self.uniqueName('pie') + self.nodePath.reparentTo(self.race.geom) + if self.ownerId == localAvatar.doId: + base.race.thrownGags[0].removeNode() + base.race.thrownGags = base.race.thrownGags[1:] + self.nodePath.setPos(self.pos[0], self.pos[1], self.pos[2]) + else: + startPos = base.cr.doId2do[self.ownerId].getPos(render) + endPos = Vec3(self.pos[0], self.pos[1], self.pos[2]) + throwIt = ProjectileInterval(self.nodePath, startPos=startPos, endPos=endPos, duration=1) + throwIt.start() + taskMgr.doMethodLater(0.8 - self.delta, self.addCollider, self.uniqueName('addCollider')) + + def addCollider(self, t): + bs = CollisionSphere(0, 0, 0, 2) + bn = CollisionNode(self.name) + self.bnp = NodePath(bn) + if not self.nodePath.isEmpty(): + self.bnp.reparentTo(self.nodePath) + self.bnp.node().addSolid(bs) + self.bnp.node().setIntoCollideMask(BitMask32(32768)) + self.bnp.node().setFromCollideMask(BitMask32(32768)) + self.accept('imIn-' + self.name, self.b_imHit) + + def b_imHit(self, cevent): + self.ignoreAll() + self.sendUpdate('hitSomebody', [localAvatar.doId, globalClockDelta.getFrameNetworkTime(16, 100)]) + if self.type == 0: + base.race.localKart.hitBanana() + elif self.type == 1: + base.race.localKart.hitPie() + self.nodePath.hide() + if hasattr(self, 'bnp'): + self.bnp.removeNode() + + def hitSomebody(self, avId, timeStamp): + if localAvatar.doId != avId: + kart = DistributedVehicle.getKartFromAvId(avId) + if kart: + self.nodePath.hide() + if hasattr(self, 'bnp'): + self.bnp.removeNode() + kart.playSpin(timeStamp) + + def setActivateTime(self, actTime): + self.activateTime = actTime + + def setInitTime(self, initTime): + self.initTime = initTime + + def setRace(self, doId): + self.race = base.cr.doId2do.get(doId) + + def setPos(self, x, y, z): + self.pos = (x, y, z) + + def makeNodePath(self): + if self.type == 0: + self.nodePath = DroppedGag(self.uniqueName('gag'), base.race.banana) + if self.billboard: + self.nodePath.setBillboardPointEye() + self.nodePath.setScale(0.9 * self.scale) + if self.type == 1: + self.nodePath = DroppedGag(self.uniqueName('gag'), base.race.banana) + if self.billboard: + self.nodePath.setBillboardPointEye() + self.nodePath.setScale(4.0 * self.scale) + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + + def setType(self, type): + self.type = type diff --git a/toontown/racing/DistributedGagAI.py b/toontown/racing/DistributedGagAI.py new file mode 100755 index 00000000..adaf6ebe --- /dev/null +++ b/toontown/racing/DistributedGagAI.py @@ -0,0 +1,48 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI + +class DistributedGagAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedGagAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.owner = 0 + + def setInitTime(self, initTime): + self.initTime = initTime + + def getInitTime(self): + return self.initTime + + def setActivateTime(self, activateTime): + self.activateTime = activateTime + + def getActivateTime(self): + return self.activateTime + + def setPos(self, x, y, z): + self.pos = [x, y, z] + + def getPos(self): + return self.pos + + def setRace(self, raceId): + self.race = self.air.doId2do[raceId] + + def getRace(self): + return self.race.getDoId() + + def setOwnerId(self, ownerId): + self.owner = ownerId + + def getOwnerId(self): + return self.owner + + def setType(self, type): + self.type = type + + def getType(self): + return self.type + + def hitSomebody(self, avId, timestamp): + self.requestDelete() diff --git a/toontown/racing/DistributedKartPad.py b/toontown/racing/DistributedKartPad.py new file mode 100755 index 00000000..17700bb2 --- /dev/null +++ b/toontown/racing/DistributedKartPad.py @@ -0,0 +1,23 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObject import DistributedObject + +class DistributedKartPad(DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedKartPad') + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + self.startingBlocks = [] + + def delete(self): + del self.startingBlocks + DistributedObject.delete(self) + + def setArea(self, area): + self.area = area + + def getArea(self): + return self.area + + def addStartingBlock(self, block): + self.startingBlocks.append(block) + self.notify.debug('KartPad %s has added starting block %s' % (self.doId, block.doId)) diff --git a/toontown/racing/DistributedKartPadAI.py b/toontown/racing/DistributedKartPadAI.py new file mode 100755 index 00000000..118f32c8 --- /dev/null +++ b/toontown/racing/DistributedKartPadAI.py @@ -0,0 +1,34 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI + +class DistributedKartPadAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedKartPadAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.air = air + self.startingBlocks = [] + self.area = None + + def setArea(self, area): + self.area = area + + def d_setArea(self, area): + self.sendUpdate('setArea', [area]) + + def b_setArea(self, area): + self.setArea(area) + self.d_setArea(self, area) + + def getArea(self): + return self.area + + def addStartingBlock(self, block): + self.startingBlocks.append(block) + + def updateMovieState(self): + pass + + def removeStartingBlock(self, block): + if self.startingBlocks.count(block): + self.startingBlocks.remove(block) diff --git a/toontown/racing/DistributedLeaderBoard.py b/toontown/racing/DistributedLeaderBoard.py new file mode 100755 index 00000000..161a43cd --- /dev/null +++ b/toontown/racing/DistributedLeaderBoard.py @@ -0,0 +1,152 @@ +from panda3d.core import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.racing import KartShopGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * + +class DistributedLeaderBoard(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DisributedLeaderBoard') + + def __init__(self, cr): + self.notify.debug('__init__: initialization of local leaderboard') + DistributedObject.DistributedObject.__init__(self, cr) + self.corner = 0 + self.length = 0 + self.width = 0 + self.updateCount = 0 + self.board = None + self.surface = None + + def generateInit(self): + DistributedObject.DistributedObject.generateInit(self) + self.board = NodePath(self.uniqueName('LeaderBoard')) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.buildListParts() + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.board.reparentTo(render) + + def setPosHpr(self, x, y, z, h, p, r): + self.surface.setPosHpr(x, y, z, h, p, r) + + def setDisplay(self, track, type, results): + if not track in TTLocalizer.KartRace_TrackNames or len(TTLocalizer.RecordPeriodStrings) <= type: + return + + trackName = TTLocalizer.KartRace_TrackNames[track] + recordTitle = TTLocalizer.RecordPeriodStrings[type] + self.display(trackName, recordTitle, results) + + def buildListParts(self): + self.surface = self.board.attachNewNode('surface') + z = 7.7 + dz = 0.4 + x = -3.7 + row, trackName = self.buildTrackRow() + self.trackNameNode = trackName + row.reparentTo(self.surface) + row.setPos(0, 1.6, z) + z = 7.3 + row, self.titleTextNode = self.buildTitleRow() + row.reparentTo(self.surface) + row.setPos(0, 1.6, z) + zListTop = 6.9 + z = zListTop + self.nameTextNodes = [] + self.timeTextNodes = [] + for i in xrange(10): + row, nameText, timeText, placeText = self.buildLeaderRow() + self.nameTextNodes.append(nameText) + placeText.setText(str(len(self.nameTextNodes)) + '.') + self.timeTextNodes.append(timeText) + row.reparentTo(self.surface) + if len(self.nameTextNodes) == 6: + z = zListTop + x = 0.35 + row.setX(x) + row.setZ(z) + row.setY(1.6) + z -= dz + + self.surface.flattenLight() + + def display(self, pTrackTitle = 'Track Title', pPeriodTitle = 'Period Title', pLeaderList = []): + self.titleTextNode.setText(pPeriodTitle) + self.trackNameNode.setText(pTrackTitle) + self.updateCount += 1 + for i in xrange(10): + if i >= len(pLeaderList): + self.nameTextNodes[i].setText('-') + self.timeTextNodes[i].setText('-') + else: + self.nameTextNodes[i].setText(pLeaderList[i][0][:22]) + self.timeTextNodes[i].setText(TTLocalizer.convertSecondsToDate(pLeaderList[i][1])) + + def buildTitleRow(self): + row = hidden.attachNewNode('TitleRow') + nameText = TextNode('titleRow') + nameText.setFont(ToontownGlobals.getSignFont()) + nameText.setAlign(TextNode.ACenter) + nameText.setTextColor(0.3, 0.75, 0.6, 1) + nameText.setText('Score Title') + namePath = row.attachNewNode(nameText) + namePath.setScale(TTLocalizer.DLBbuildTitleRow) + namePath.setDepthWrite(0) + return (row, nameText) + + def buildTrackRow(self): + row = hidden.attachNewNode('trackRow') + nameText = TextNode('trackRow') + nameText.setFont(ToontownGlobals.getSignFont()) + nameText.setAlign(TextNode.ACenter) + nameText.setTextColor(0.5, 0.75, 0.7, 1) + nameText.setText('Track Title') + namePath = row.attachNewNode(nameText) + namePath.setScale(0.55) + namePath.setDepthWrite(0) + return (row, nameText) + + def buildLeaderRow(self): + row = hidden.attachNewNode('leaderRow') + nameText = TextNode('nameText') + nameText.setFont(ToontownGlobals.getToonFont()) + nameText.setAlign(TextNode.ALeft) + nameText.setTextColor(0.125, 0, 0.5, 1) + nameText.setText('-') + namePath = row.attachNewNode(nameText) + namePath.setPos(1.1, 0, 0) + namePath.setScale(0.23) + namePath.setDepthWrite(0) + timeText = TextNode('timeText') + timeText.setFont(ToontownGlobals.getToonFont()) + timeText.setAlign(TextNode.ARight) + timeText.setTextColor(0, 0, 0, 1) + timeText.setText('-') + timePath = row.attachNewNode(timeText) + timePath.setPos(1.0, 0, 0) + timePath.setScale(0.23) + timePath.setDepthWrite(0) + placeText = TextNode('placeText') + placeText.setFont(ToontownGlobals.getSignFont()) + placeText.setAlign(TextNode.ARight) + placeText.setTextColor(1, 1, 0.1, 1) + placeText.setText('-') + placePath = row.attachNewNode(placeText) + placePath.setPos(-0.1, 0, -0.05) + placePath.setScale(0.3) + placePath.setDepthWrite(0) + return (row, + nameText, + timeText, + placeText) + + def delete(self): + self.notify.debug('delete: deleting local leaderboard') + self.ignoreAll() + self.board.removeNode() + DistributedObject.DistributedObject.delete(self) diff --git a/toontown/racing/DistributedLeaderBoardAI.py b/toontown/racing/DistributedLeaderBoardAI.py new file mode 100755 index 00000000..8f252d8f --- /dev/null +++ b/toontown/racing/DistributedLeaderBoardAI.py @@ -0,0 +1,87 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.racing import RaceGlobals +import random, time + +class DistributedLeaderBoardAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedLeaderBoardAI") + + def __init__(self, air, displays): + DistributedObjectAI.__init__(self, air) + self.displays = displays + self.display = [0, 0, []] + self.currentId = 0 + self.posHpr = (0, 0, 0, 0, 0, 0) + + def generateWithRequired(self, zoneId): + DistributedObjectAI.generateWithRequired(self, zoneId) + self.accept('goofyLeaderboardChange', self.__setDisplay) + self.accept('goofyLeaderboardDisplay', self.__setDisplayRace) + self.__updateDisplay() + self.switchTask = taskMgr.doMethodLater(15, self.nextDisplay, 'leaderboardSwitchTask-%s' % random.random()) + + def delete(self): + DistributedObjectAI.delete(self) + self.ignoreAll() + taskMgr.remove(self.switchTask) + + def setPosHpr(self, x, y, z, h, p, r): + self.posHpr = (x, y, z, h, p, r) + + def getPosHpr(self): + return self.posHpr + + def getDisplay(self): + return self.display + + def getDisplayName(self): + return '%s, %s' % (self.display[0], self.display[1]) + + def getMaxTimeDifference(self): + return RaceGlobals.MaxTimeDifference[self.display[1]] + + def hasMaxTimeDifference(self): + return self.display[1] in RaceGlobals.MaxTimeDifference + + def nextDisplay(self, task=None): + self.__updateDisplay() + self.currentId += 1 + + if self.currentId >= len(self.displays): + self.currentId = 0 + + return Task.again + + def __setDisplayRace(self, race): + self.currentId = race + self.__updateDisplay() + + def __updateDisplay(self): + race = self.displays[self.currentId] + + self.display = [race[0], race[1], []] + self.__setDisplay() + + def __setDisplay(self): + database = self.air.leaderboardMgr.getDatabase() + displayName = self.getDisplayName() + + if not displayName in database: + self.sendDisplayUpdate([]) + return + + displayEntry = database[displayName] + + if self.hasMaxTimeDifference(): + difference = time.time() - displayEntry[0] + + if difference >= self.getMaxTimeDifference(): + self.air.leaderboardMgr.clearRace(displayName) + return + + self.sendDisplayUpdate(self.air.leaderboardMgr.trimList(displayEntry[1])) + + def sendDisplayUpdate(self, players): + self.display[2] = players + self.sendUpdate('setDisplay', self.display) diff --git a/toontown/racing/DistributedProjectile.py b/toontown/racing/DistributedProjectile.py new file mode 100755 index 00000000..dc57b642 --- /dev/null +++ b/toontown/racing/DistributedProjectile.py @@ -0,0 +1,40 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.gui.DirectGui import * +from direct.fsm import FSM +from direct.distributed.DistributedSmoothNode import DistributedSmoothNode +from otp.avatar.ShadowCaster import ShadowCaster + +class DistributedProjectile(DistributedSmoothNode, ShadowCaster, NodePath): + + def __init__(self, cr): + ShadowCaster.__init__(self) + DistributedSmoothNode.__init__(self, cr) + NodePath.__init__(self, 'Projectile') + + def announceGenerate(self): + DistributedSmoothNode.announceGenerate(self) + self.name = self.uniqueName('projectile') + self.posHprBroadcastName = self.uniqueName('projectileBroadcast') + geom = loader.loadModel('models/smiley') + self.geom = geom + self.geom.reparentTo(self) + self.startSmooth() + self.reparentTo(render) + + def generate(self): + DistributedSmoothNode.generate(self) + self.name = self.uniqueName('projectile') + self.posHprBroadcastName = self.uniqueName('projectileBroadcast') + geom = loader.loadModel('models/smiley') + self.geom = geom + self.geom.reparentTo(self) + self.startSmooth() + self.reparentTo(render) + + def setAvId(self, avId): + self.avId = avId + + def delete(self): + DistributedSmoothNode.delete(self) diff --git a/toontown/racing/DistributedProjectileAI.py b/toontown/racing/DistributedProjectileAI.py new file mode 100755 index 00000000..33a4ca0b --- /dev/null +++ b/toontown/racing/DistributedProjectileAI.py @@ -0,0 +1,23 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI + +class DistributedProjectileAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedProjectileAI") + + def setInitTime(self, todo0): + pass + + def setPos(self, todo0, todo1, todo2): + pass + + def setRace(self, todo0): + pass + + def setOwnerId(self, todo0): + pass + + def setType(self, todo0): + pass + + def hitSomebody(self, todo0, todo1): + pass diff --git a/toontown/racing/DistributedRace.py b/toontown/racing/DistributedRace.py new file mode 100755 index 00000000..24ffa597 --- /dev/null +++ b/toontown/racing/DistributedRace.py @@ -0,0 +1,1248 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectLabel import * +from direct.gui.DirectButton import * +from direct.showbase import BulletinBoardWatcher +from direct.interval.IntervalGlobal import * +from otp.otpbase import OTPGlobals +from direct.interval.IntervalGlobal import * +from RaceGag import RaceGag +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.toon import ToonHeadFrame +from toontown.racing.KartDNA import InvalidEntry, getAccessory, getDefaultColor +from pandac.PandaModules import CardMaker, OrthographicLens, LineSegs +from direct.distributed import DistributedSmoothNode +from math import fmod +from math import sqrt +from RaceGUI import RaceGUI +import RaceGlobals +from direct.task.Task import Task +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.battle.BattleProps import * +from toontown.minigame import MinigameRulesPanel +from toontown.racing import Piejectile +from toontown.racing import EffectManager +from toontown.racing import PiejectileManager +from toontown.dna.DNAParser import * +from otp.ai.MagicWordGlobal import * +from toontown.safezone import SZUtil + +class DistributedRace(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedRace') + ReadyPost = 'RaceReady' + WinEvent = 'RaceWinEvent' + BGM_BaseDir = 'phase_6/audio/bgm/' + SFX_BaseDir = 'phase_6/audio/sfx/' + SFX_StartBoop = SFX_BaseDir + 'KART_raceStart1.ogg' + SFX_StartBoop2 = SFX_BaseDir + 'KART_raceStart2.ogg' + SFX_Applause = SFX_BaseDir + 'KART_Applause_%d.ogg' + + def __init__(self, cr): + if hasattr(base, 'race') and base.race: + base.race.delete() + self.qbox = loader.loadModel('phase_6/models/karting/qbox') + self.boostArrowTexture = loader.loadTexture('phase_6/maps/boost_arrow.jpg', 'phase_6/maps/boost_arrow_a.rgb') + self.boostArrowTexture.setMinfilter(Texture.FTLinear) + DistributedObject.DistributedObject.__init__(self, cr) + self.kartMap = {} + self.fsm = ClassicFSM.ClassicFSM('Race', [State.State('join', self.enterJoin, self.exitJoin, ['prep', 'leave']), + State.State('prep', self.enterPrep, self.exitPrep, ['tutorial', 'leave']), + State.State('tutorial', self.enterTutorial, self.exitTutorial, ['start', 'waiting', 'leave']), + State.State('waiting', self.enterWaiting, self.exitWaiting, ['start', 'leave']), + State.State('start', self.enterStart, self.exitStart, ['racing', 'leave']), + State.State('racing', self.enterRacing, self.exitRacing, ['finished', 'leave']), + State.State('finished', self.enterFinished, self.exitFinished, ['leave']), + State.State('leave', self.enterLeave, self.exitLeave, [])], 'join', 'leave') + self.gui = RaceGUI(self) + base.race = self + self.currT = 0 + self.currLapT = 0 + self.currGag = 0 + self.tdelay = 0 + self.finished = False + self.thrownGags = [] + self.effectManager = EffectManager.EffectManager() + self.piejectileManager = PiejectileManager.PiejectileManager() + self.lastTimeUpdate = globalClock.getFrameTime() + self.initGags() + self.canShoot = True + self.isUrbanTrack = False + self.hasFog = False + self.dummyNode = None + self.fog = None + self.bananaSound = base.loadSfx('phase_6/audio/sfx/KART_tossBanana.ogg') + self.anvilFall = base.loadSfx('phase_6/audio/sfx/KART_Gag_Hit_Anvil.ogg') + self.accept('leaveRace', self.leaveRace) + self.accept('finishRace', self.finishRace) + self.toonsToLink = [] + self.curveTs = [] + self.curvePoints = [] + self.localKart = None + self.musicTrack = None + self.victory = None + self.miscTaskNames = [] + self.boostDir = {} + self.knownPlace = {} + self.placeFixup = [] + self.curve = None + self.barricadeSegments = 100.0 + self.outerBarricadeDict = {} + self.innerBarricadeDict = {} + self.maxLap = 0 + self.oldT = 0 + self.debugIt = 0 + self.startPos = None + return + + def generate(self): + self.notify.debug('generate: %s' % self.doId) + DistributedObject.DistributedObject.generate(self) + bboard.post('race', self) + self.roomWatcher = None + self.cutoff = 0.01 + self.startBoopSfx = base.loadSfx(self.SFX_StartBoop) + self.startBoop2Sfx = base.loadSfx(self.SFX_StartBoop2) + return + + def announceGenerate(self): + self.notify.debug('announceGenerate: %s' % self.doId) + DistributedObject.DistributedObject.announceGenerate(self) + musicFile = self.BGM_BaseDir + RaceGlobals.TrackDict[self.trackId][7] + self.raceMusic = base.loadMusic(musicFile) + base.playMusic(self.raceMusic, looping=1, volume=0.8) + camera.reparentTo(render) + if self.trackId in (RaceGlobals.RT_Urban_1, + RaceGlobals.RT_Urban_1_rev, + RaceGlobals.RT_Urban_2, + RaceGlobals.RT_Urban_2_rev): + self.isUrbanTrack = True + self.oldFarPlane = base.camLens.getFar() + base.camLens.setFar(12000) + localAvatar.startPosHprBroadcast() + localAvatar.d_broadcastPositionNow() + DistributedSmoothNode.activateSmoothing(1, 1) + self.reversed = self.trackId / 2.0 > int(self.trackId / 2.0) + for i in xrange(3): + base.loader.tick() + + self.sky = loader.loadModel('phase_3.5/models/props/TT_sky') + self.sky.setPos(0, 0, 0) + self.sky.setScale(20.0) + self.sky.setFogOff() + if self.trackId in (RaceGlobals.RT_Urban_1, + RaceGlobals.RT_Urban_1_rev, + RaceGlobals.RT_Urban_2, + RaceGlobals.RT_Urban_2_rev): + self.loadFog() + self.setupGeom() + self.startSky() + for i in xrange(5): + base.loader.tick() + + def disable(self): + self.notify.debug('disable %s' % self.doId) + if self.musicTrack: + self.musicTrack.finish() + self.raceMusic.stop() + self.stopSky() + if self.sky is not None: + self.sky.removeNode() + if self.dummyNode: + self.dummyNode.removeNode() + self.dummyNode = None + for taskName in self.miscTaskNames: + taskMgr.remove(taskName) + + taskMgr.remove('raceWatcher') + self.ignoreAll() + DistributedSmoothNode.activateSmoothing(1, 0) + if self.isUrbanTrack: + self.unloadUrbanTrack() + if self.fog: + render.setFogOff() + del self.fog + self.fog = None + if self.geom is not None: + self.geom.hide() + base.camLens.setFar(self.oldFarPlane) + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + self.notify.debug('delete %s' % self.doId) + if self.gui: + self.gui.destroy() + self.gui = None + if self.geom is not None: + self.geom.removeNode() + self.geom = None + for i in self.gags: + i.delete() + del i + + self.piejectileManager.delete() + if not hasattr(base, 'race') or not hasattr(self, 'curveTs'): + return + if self.curveTs: + del self.curveTs + if self.curvePoints: + del self.curvePoints + if self.curve: + del self.curve + if self.victory: + del self.victory + del self.fsm + del self.anvilFall + del self.bananaSound + del self.localKart + taskMgr.remove(self.uniqueName('countdownTimerTask')) + taskMgr.remove('raceWatcher') + bboard.remove('race') + self.ignoreAll() + DistributedObject.DistributedObject.delete(self) + del base.race + + def d_requestThrow(self, x, y, z): + self.sendUpdate('requestThrow', [x, y, z]) + + def d_requestKart(self): + self.sendUpdate('requestKart', []) + + def waitingForJoin(self): + self.notify.debug('I got the barrier') + self.fsm.enterInitialState() + + def racerDisconnected(self, avId): + self.notify.debug('lost racer: %s' % avId) + if avId in self.kartMap: + if avId in self.toonsToLink: + self.toonsToLink.remove(avId) + toon = base.cr.doId2do.get(avId, None) + kart = base.cr.doId2do.get(self.kartMap.get(avId, None), None) + self.avIds.remove(avId) + del self.kartMap[avId] + self.gui.racerLeft(avId, unexpected=True) + if kart: + kart.reparentTo(hidden) + if toon: + toon.reparentTo(hidden) + if len(self.toonsToLink) == 0: + self.doneBarrier('waitingForPrep') + return + + def setPlace(self, avId, totalTime, place, entryFee, qualify, winnings, bonus, trophies, circuitPoints, circuitTime): + if self.fsm.getCurrentState().getName() == 'leaving': + return + if avId == localAvatar.doId: + cheerToPlay = place + (4 - self.numRacers) + if cheerToPlay > 4: + cheerToPlay = 4 + self.victory = base.loadSfx(self.SFX_Applause % cheerToPlay) + self.victory.play() + self.knownPlace[avId] = place + kart = base.cr.doId2do.get(self.kartMap.get(avId, None), None) + avatar = base.cr.doId2do.get(avId, None) + if avatar: + self.gui.racerFinished(avId, self.trackId, place, totalTime, entryFee, qualify, winnings, bonus, trophies, circuitPoints, circuitTime) + taskName = 'hideAv: %s' % avId + taskMgr.doMethodLater(6, avatar.reparentTo, taskName, extraArgs=[hidden]) + self.miscTaskNames.append(taskName) + if kart: + taskName = 'hideKart: %s' % self.localKart.doId + taskMgr.doMethodLater(6, kart.reparentTo, taskName, extraArgs=[hidden]) + self.miscTaskNames.append(taskName) + return + + def setCircuitPlace(self, avId, place, entryFee, winnings, bonus, trophies): + if self.fsm.getCurrentState().getName() == 'leaving': + return + if avId == localAvatar.doId: + cheerToPlay = place + (4 - self.numRacers) + self.victory = base.loadSfx(self.SFX_Applause % cheerToPlay) + self.victory.play() + oldPlace = 0 + if self.knownPlace.get(avId): + oldPlace = self.knownPlace[avId] + self.placeFixup.append([oldPlace - 1, place - 1]) + avatar = base.cr.doId2do.get(avId, None) + if avatar: + self.gui.racerFinishedCircuit(avId, oldPlace, entryFee, winnings, bonus, trophies) + + def endCircuitRace(self): + self.gui.circuitFinished(self.placeFixup) + + def prepForRace(self): + self.fsm.request('prep') + + def startRace(self, startTime = 0): + self.baseTime = globalClockDelta.networkToLocalTime(startTime) + self.fsm.request('start') + + def startTutorial(self): + self.fsm.request('tutorial') + + def genGag(self, slot, number, type): + self.notify.debug('making gag...') + if not self.gags[slot].isActive(): + self.gags[slot].genGag(number, type) + + def dropAnvilOn(self, ownerId, avId, timeStamp): + kart = base.cr.doId2do.get(self.kartMap.get(avId, None), None) + if kart: + if avId != ownerId: + if avId == localAvatar.doId: + self.anvilFall.play() + kart.dropOnMe(timeStamp) + else: + kart.dropOnHim(timeStamp) + return + + def shootPiejectile(self, sourceId, targetId, type = 0): + kart = base.cr.doId2do.get(self.kartMap.get(sourceId, None), None) + if kart: + self.piejectileManager.addPiejectile(sourceId, targetId, type) + return + + def goToSpeedway(self, avIds, reason = RaceGlobals.Exit_UserReq): + self.notify.debug('goToSpeedway %s %s' % (avIds, reason)) + if localAvatar.doId in avIds: + base.loader.endBulkLoad('atRace') + self.kartCleanup() + self.doneBarrier('waitingForExit') + self.sendUpdate('racerLeft', [localAvatar.doId]) + out = {'loader': 'safeZoneLoader', + 'where': 'playground', + 'how': 'teleportIn', + 'hoodId': localAvatar.lastHood, + 'zoneId': localAvatar.lastHood, + 'shardId': None, + 'avId': -1, + 'reason': reason} + base.cr.playGame.fsm.request('quietZone', [out]) + return + + def kartCleanup(self): + kart = self.localKart + if kart: + kart.setState('P', 0) + for i in self.avIds: + if i != localAvatar.doId: + toon = base.cr.doId2do.get(i, None) + if toon: + toon.stopSmooth() + toon.setScale(1) + toon.setShear(0, 0, 0) + toon.reparentTo(render) + kart.doHeadScale(toon, None) + + localAvatar.setPos(0, 14, 0) + localAvatar.sendCurrentPosition() + return + + def heresMyT(self, avId, avNumLaps, avTime, timestamp): + self.gui.updateRacerInfo(avId, curvetime=avNumLaps + avTime) + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def setRaceType(self, raceType): + self.raceType = raceType + + def setCircuitLoop(self, circuitLoop): + self.circuitLoop = circuitLoop + + def setTrackId(self, id): + DistributedRace.notify.debug('setTrackId: %s' % id) + self.trackId = id + + def setAvatars(self, avIds): + ids = '' + for i in avIds: + ids += str(i) + ' ' + + DistributedRace.notify.debug('setAvatars: %s' % ids) + self.avIds = avIds + self.avT = [0] * len(self.avIds) + + def setLapCount(self, lapCount): + self.lapCount = lapCount + + def setStartingPlaces(self, startList): + self.startingPlaces = startList + + def enterJoin(self): + self.doneBarrier('waitingForJoin') + self.notify.debug('entering Join') + + def exitJoin(self): + pass + + def setEnteredRacers(self, avAndKarts): + self.notify.debug('setEnteredRacers %s' % avAndKarts) + avatarsGone = [] + avatarsLeft = [] + self.numRacers = len(avAndKarts) + for i in avAndKarts: + if i[0] in self.avIds: + self.kartMap[i[0]] = i[1] + avatarsLeft.append(i[0]) + + for i in self.avIds: + if i not in avatarsLeft: + avatarsGone.append(i) + + base.loader.tick() + for i in avatarsGone: + self.avIds.remove(i) + + self.toonsToLink = list(self.avIds) + for i in avAndKarts: + self.cr.relatedObjectMgr.requestObjects(i, allCallback=self.__gotKartAvatarLink) + + def __gotKartAvatarLink(self, avAndKart): + self.notify.debug('got a Link') + toon = avAndKart[0] + kart = avAndKart[1] + base.loader.tick() + if toon.doId in self.toonsToLink: + self.toonsToLink.remove(toon.doId) + if toon.doId == localAvatar.doId: + self.localKart = kart + if len(self.toonsToLink) == 0: + self.doneBarrier('waitingForPrep') + + def enterPrep(self): + self.d_requestKart() + self.notify.debug('entering Prep State') + if self.reversed: + self.spin = Vec3(180, 0, 0) + else: + self.spin = Vec3(0, 0, 0) + for i in xrange(4): + base.loader.tick() + + self.gui.initRaceMode() + self.gui.initResultMode() + self.myPos = self.startingPos[self.startingPlaces[self.avIds.index(localAvatar.doId)]] + self.localKart.setPosHpr(self.myPos[0], self.myPos[1] + self.spin) + self.localKart.setupLapCollisions() + if self.dummyNode: + self.dummyNode.setPosHpr(self.myPos[0], self.myPos[1] + self.spin) + self.currentPole = self.findSegmentStart() + self.rabbitPoint = Vec3(0, 0, 0) + self.doneBarrier('waitingForReady') + + def exitPrep(self): + pass + + def enterTutorial(self): + self.notify.debug('entering Tutorial State') + base.loader.endBulkLoad('atRace') + self.localKart.setPosHpr(self.myPos[0], self.myPos[1] + self.spin) + base.transitions.irisIn() + self.rulesDoneEvent = 'finishedRules' + self.accept(self.rulesDoneEvent, self.handleRulesDone) + self.rulesPanel = MinigameRulesPanel.MinigameRulesPanel('RacingRulesPanel', self.getTitle(), self.getInstructions(), self.rulesDoneEvent, 10) + self.rulesPanel.load() + self.rulesPanel.frame.setPos(0, 0, -0.6667) + self.rulesPanel.enter() + + def exitTutorial(self): + self.ignore(self.rulesDoneEvent) + self.rulesPanel.exit() + self.rulesPanel.unload() + del self.rulesPanel + + def getTitle(self): + return TTLocalizer.KartRace_TitleInfo + + def getInstructions(self): + return TTLocalizer.KartRace_TrackInfo[self.trackId] + + def handleRulesDone(self): + self.doneBarrier('readRules') + self.fsm.request('waiting') + + def enterWaiting(self): + self.waitingLabel = DirectLabel() + self.waitingLabel['text'] = TTLocalizer.WaitingForOtherToons + self.waitingLabel.setScale(TTLocalizer.DRenterWaiting) + + def exitWaiting(self): + self.waitingLabel.removeNode() + + def enterStart(self): + waitTime = self.baseTime - globalClock.getFrameTime() + taskName = 'enableRaceModeLater' + taskMgr.doMethodLater(1, self.gui.enableRaceMode, taskName, extraArgs=[]) + self.miscTaskNames.append(taskName) + for i in self.avIds: + self.gui.racerEntered(i) + + self.startCountdownClock(waitTime, 0) + taskMgr.doMethodLater(waitTime, self.fsm.request, 'goToRacing', extraArgs=['racing']) + + def exitStart(self): + pass + + def enterRacing(self): + self.localKart.setInput(1) + self.gui.setTimerEnabled(True) + self.raceTask = taskMgr.add(self.raceWatcher, 'raceWatcher') + + def exitRacing(self): + pass + + def raceWatcher(self, task): + kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None) + if self.localKart.amIClampingPosition(): + self.notify.debug('teleporting kart %d back to main track' % localAvatar.doId) + self.localKart.setPos(self.curvePoints[self.currentPole]) + kartPoint = self.localKart.getPos() + direction = 0 + while True: + currPoint = self.curvePoints[self.currentPole] + nextPole = (self.currentPole + 1) % len(self.curvePoints) + nextPoint = self.curvePoints[nextPole] + segment = nextPoint - currPoint + segment.setZ(0) + segLength2 = segment.lengthSquared() + kartVector = kartPoint - currPoint + kartVector.setZ(0) + project = segment * (segment.dot(kartVector) / segLength2) + projLength2 = project.lengthSquared() + if project.dot(segment) < 0: + if direction == 1: + break + prevPole = (self.currentPole - 1) % len(self.curvePoints) + self.currentPole = prevPole + direction = -1 + elif projLength2 > segLength2: + if direction == -1: + break + self.currentPole = nextPole + direction = 1 + else: + break + + if self.dummyNode: + self.dummyNode.setPos(kartPoint[0], kartPoint[1], 0) + self.dummyNode.setHpr(self.localKart.getH(), 0, 0) + t = projLength2 / segLength2 + if self.debugIt: + self.notify.debug('self.debugIt = %d' % self.debugIt) + if nextPole < self.currentPole: + newT = self.curveTs[self.currentPole] * (1 - t) + self.curve.getMaxT() * t + else: + newT = self.curveTs[self.currentPole] * (1 - t) + self.curveTs[nextPole] * t + kartDirection = self.localKart.forward.getPos(render) - self.localKart.getPos(render) + kartDirection.normalize() + project.normalize() + globalDirection = kartDirection.dot(project) + if globalDirection < 0: + self.wrongWay = True + elif globalDirection > 0.1: + self.wrongWay = False + newLapT = (newT - self.startT) / self.curve.getMaxT() % 1.0 + if newLapT - self.currLapT < -0.5: + self.laps += 1 + self.changeMusicTempo(1 + self.laps * 0.5) + self.notify.debug('crossed the start line: %s, %s, %s, %s' % (self.laps, + self.startT, + self.currT, + newT)) + elif newLapT - self.currLapT > 0.5: + self.laps -= 1 + self.changeMusicTempo(1 + self.laps * 0.5) + self.notify.debug('crossed the start line - wrong way: %s, %s, %s, %s' % (self.laps, + self.startT, + self.currT, + newT)) + self.currT = newT + self.currLapT = newLapT + if self.isUrbanTrack: + self.showBuildings(self.currT) + now = globalClock.getFrameTime() + timestamp = globalClockDelta.localToNetworkTime(now) + if self.laps == self.lapCount: + self.finishRace() + if self.laps > self.maxLap: + self.maxLap = self.laps + self.sendUpdate('heresMyT', [localAvatar.doId, + self.laps, + self.currLapT, + timestamp]) + if now - self.lastTimeUpdate > 0.5: + self.lastTimeUpdate = now + self.sendUpdate('heresMyT', [localAvatar.doId, + self.laps, + self.currLapT, + timestamp]) + self.gui.updateRacerInfo(localAvatar.doId, curvetime=self.currLapT + self.laps) + self.gui.update(now) + return Task.cont + + def enterFinished(self): + taskMgr.remove('raceWatcher') + self.fadeOutMusic() + self.localKart.interruptTurbo() + self.localKart.disableControls() + taskName = 'parkIt' + taskMgr.doMethodLater(2, self.stopDriving, taskName, extraArgs=[]) + self.miscTaskNames.append(taskName) + self.finished = True + camera.reparentTo(render) + camera.setPos(self.localKart.getPos(render) + Vec3(0, 0, 10)) + camera.setH(self.localKart.getH(render) + 180) + self.gui.disableRaceMode() + self.gui.enableResultMode() + localAvatar.reparentTo(hidden) + self.localKart.reparentTo(hidden) + + def exitFinished(self): + pass + + def stopDriving(self): + kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None) + cpos = camera.getPos() + chpr = camera.getHpr() + localAvatar.reparentTo(hidden) + self.localKart.reparentTo(hidden) + self.localKart.stopSmooth() + self.localKart.stopPosHprBroadcast() + camera.setPos(cpos) + camera.setHpr(chpr) + return + + def enterLeave(self): + kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None) + taskMgr.remove('raceWatcher') + self.gui.disable() + if self.localKart: + self.localKart.disableControls() + base.transitions.irisOut() + if self.raceType == RaceGlobals.Circuit and not len(self.circuitLoop) == 0: + self.sendUpdate('racerLeft', [localAvatar.doId]) + else: + taskMgr.doMethodLater(1, self.goToSpeedway, 'leaveRace', extraArgs=[[localAvatar.doId], RaceGlobals.Exit_UserReq]) + if self.victory: + self.victory.stop() + self.bananaSound.stop() + self.anvilFall.stop() + return + + def exitLeave(self): + pass + + def getCountdownColor(self, countdownTimeInt): + clockNodeColors = [Vec4(0, 1, 0, 1), + Vec4(1, 1, 0, 1), + Vec4(1, 0.5, 0, 1), + Vec4(1, 0, 0, 1)] + i = max(min(countdownTimeInt, len(clockNodeColors) - 1), 0) + return clockNodeColors[i] + + def startCountdownClock(self, countdownTime, ts): + self.clockNode = TextNode('k') + self.clockNode.setFont(ToontownGlobals.getSignFont()) + self.clockNode.setAlign(TextNode.ACenter) + countdownInt = int(countdownTime) + self.clockNode.setTextColor(self.getCountdownColor(countdownInt)) + self.clockNode.setText(str(countdownInt)) + self.clock = render2d.attachNewNode(self.clockNode) + rs = TTLocalizer.DRrollScale + self.clock.setPosHprScale(0, 0, 0, 0, 0, 0, rs, rs, rs) + self.clock.hide() + if ts < countdownTime: + self.countdown(countdownTime - ts) + + def timerTask(self, task): + countdownTime = int(task.duration - task.time) + timeStr = str(countdownTime + 1) + if self.clock.isHidden(): + if task.duration - task.time <= task.maxCount: + self.clock.show() + if self.clockNode.getText() != timeStr: + self.startBoopSfx.play() + self.clockNode.setText(timeStr) + self.clockNode.setTextColor(self.getCountdownColor(countdownTime + 1)) + if task.time >= task.duration: + self.startBoop2Sfx.play() + self.clockNode.setText(TTLocalizer.KartRace_Go) + self.clockNode.setTextColor(self.getCountdownColor(-1)) + taskMgr.doMethodLater(1, self.endGoSign, 'removeGoSign') + return Task.done + else: + return Task.cont + + def endGoSign(self, t): + self.clock.removeNode() + + def countdown(self, duration): + countdownTask = Task(self.timerTask) + countdownTask.duration = duration + countdownTask.maxCount = RaceGlobals.RaceCountdown + taskMgr.remove(self.uniqueName('countdownTimerTask')) + return taskMgr.add(countdownTask, self.uniqueName('countdownTimerTask')) + + def initGags(self): + self.banana = globalPropPool.getProp('banana') + self.banana.setScale(2) + self.pie = globalPropPool.getProp('creampie') + self.pie.setScale(1) + + def makeCheckPoint(self, trigger, location, event): + cs = CollisionSphere(0, 0, 0, 140) + cs.setTangible(0) + triggerEvent = 'imIn-' + trigger + cn = CollisionNode(trigger) + cn.addSolid(cs) + cn.setIntoCollideMask(BitMask32(32768)) + cn.setFromCollideMask(BitMask32(32768)) + cnp = NodePath(cn) + cnp.reparentTo(self.geom) + cnp.setPos(location) + self.accept(triggerEvent, event) + + def loadUrbanTrack(self): + self.dnaStore = DNAStorage() + files = ('phase_4/dna/storage.pdna', 'phase_5/dna/storage_town.pdna', + 'phase_4/dna/storage_TT.pdna', 'phase_5/dna/storage_TT_town.pdna', + 'phase_8/dna/storage_BR.pdna', 'phase_8/dna/storage_BR_town.pdna', + 'phase_8/dna/storage_DL.pdna', 'phase_8/dna/storage_DL_town.pdna') + dnaBulk = DNABulkLoader(self.dnaStore, files) + dnaBulk.loadDNAFiles() + dnaFile = 'phase_6/dna/urban_track_town.pdna' + if self.trackId in (RaceGlobals.RT_Urban_2, RaceGlobals.RT_Urban_2_rev): + dnaFile = 'phase_6/dna/urban_track_town_B.pdna' + node = loader.loadDNAFile(self.dnaStore, dnaFile) + self.geomNode = node + self.townGeom = self.geom.attachNewNode(node) + self.townGeom.findAllMatches('**/+CollisionNode').stash() + self.buildingGroups = {} + self.currBldgInd = {} + self.currBldgGroups = {} + bgGeom = self.geom.find('**/polySurface8') + if self.dummyNode: + bgGeom.reparentTo(self.dummyNode) + else: + bgGeom.reparentTo(localAvatar) + bgGeom.setScale(0.1) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot) + bgGeom.node().setEffect(ce) + bgGeom.setDepthTest(0) + bgGeom.setDepthWrite(0) + bgGeom.setBin('background', 102) + bgGeom.setZ(-1) + self.bgGeom = bgGeom + l = self.geom.findAllMatches('**/+ModelNode') + for n in l: + n.node().setPreserveTransform(0) + self.geom.flattenLight() + maxNum = 0 + for side in ['inner', 'outer']: + self.buildingGroups[side] = [] + self.currBldgInd[side] = None + self.currBldgGroups[side] = None + i = 0 + while 1: + bldgGroup = self.townGeom.find('**/Buildings_' + side + '-' + str(i)) + if bldgGroup.isEmpty(): + break + l = bldgGroup.findAllMatches('**/+ModelNode') + for n in l: + n2 = n.getParent().attachNewNode(n.getName()) + n.getChildren().reparentTo(n2) + n.removeNode() + bldgGroup.flattenStrong() + if not bldgGroup.getNode(0).getBounds().isEmpty(): + self.buildingGroups[side].append(bldgGroup) + i += 1 + if i > maxNum: + maxNum = i + for side in ['innersidest', 'outersidest']: + self.buildingGroups[side] = [] + self.currBldgInd[side] = None + self.currBldgGroups[side] = None + for i in xrange(maxNum): + for barricade in ('innerbarricade', 'outerbarricade'): + bldgGroup = self.townGeom.find('**/Buildings_' + side + '-' + barricade + '_' + str(i)) + if bldgGroup.isEmpty(): + continue + l = bldgGroup.findAllMatches('**/+ModelNode') + for n in l: + n2 = n.getParent().attachNewNode(n.getName()) + n.getChildren().reparentTo(n2) + n.removeNode() + self.buildingGroups[side].append(bldgGroup) + treeNodes = self.townGeom.findAllMatches('**/prop_tree_*') + for tree in treeNodes: + tree.flattenStrong() + snowTreeNodes = self.townGeom.findAllMatches('**/prop_snow_tree_*') + for snowTree in snowTreeNodes: + snowTree.flattenStrong() + for side in ['inner', 'outer', 'innersidest', 'outersidest']: + for grp in self.buildingGroups[side]: + grp.stash() + self.showBuildings(0) + + def unloadUrbanTrack(self): + del self.buildingGroups + self.townGeom.removeNode() + + def loadFog(self): + self.hasFog = True + if self.isUrbanTrack: + base.camLens.setFar(650) + else: + base.camLens.setFar(650) + self.dummyNode = render.attachNewNode('dummyNode') + if base.wantFog: + self.fog = Fog('TrackFog') + self.fog.setColor(Vec4(0.6, 0.7, 0.8, 1.0)) + if self.isUrbanTrack: + self.fog.setLinearRange(1000.0, 1450.0) + else: + self.fog.setLinearRange(1000.0, 1800.0) + render.setFog(self.fog) + self.sky.setScale(1.725) + self.sky.reparentTo(self.dummyNode) + + def showBuildings(self, t, forceRecompute = False): + firstTimeCalled = 0 + if self.curve: + t = t / self.curve.getMaxT() + else: + firstTimeCalled = 1 + if self.reversed: + t = 1.0 - t + numGroupsShown = 5 + for side in ['inner', 'outer']: + numBldgGroups = len(self.buildingGroups[side]) + bldgInd = int(t * numBldgGroups) + bldgInd = bldgInd % numBldgGroups + if self.trackId in (RaceGlobals.RT_Urban_2, RaceGlobals.RT_Urban_2_rev): + oldBldgInd = int(self.oldT * numBldgGroups) + newBldgInd = int(t * numBldgGroups) + kartPoint = self.startPos + kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None) + if kart: + kartPoint = self.localKart.getPos() + if not self.currBldgInd[side]: + self.currBldgInd[side] = 0 + curInd = self.currBldgInd[side] + myCurGroup = self.buildingGroups[side][curInd] + prevGrp = (curInd - 1) % numBldgGroups + myPrevGroup = self.buildingGroups[side][prevGrp] + nextGrp = (curInd + 1) % numBldgGroups + myNextGroup = self.buildingGroups[side][nextGrp] + curVector = myCurGroup.getNode(0).getBounds().getCenter() - kartPoint + curDistance = curVector.lengthSquared() + prevVector = myPrevGroup.getNode(0).getBounds().getCenter() - kartPoint + prevDistance = prevVector.lengthSquared() + nextVector = myNextGroup.getNode(0).getBounds().getCenter() - kartPoint + nextDistance = nextVector.lengthSquared() + if curDistance <= prevDistance and curDistance <= nextDistance: + bldgInd = self.currBldgInd[side] + elif prevDistance <= curDistance and prevDistance <= nextDistance: + bldgInd = prevGrp + elif nextDistance <= curDistance and nextDistance <= prevDistance: + bldgInd = nextGrp + else: + self.notify.warning('unhandled case!!!!') + bldgInd = self.currBldgInd[side] + if bldgInd != self.currBldgInd[side]: + currBldgGroups = self.currBldgGroups[side] + if currBldgGroups: + for i in currBldgGroups: + self.buildingGroups[side][i].stash() + + prevGrp2 = (bldgInd - 2) % numBldgGroups + prevGrp = (bldgInd - 1) % numBldgGroups + currGrp = bldgInd % numBldgGroups + nextGrp = (bldgInd + 1) % numBldgGroups + nextGrp2 = (bldgInd + 2) % numBldgGroups + self.currBldgGroups[side] = [prevGrp2, + prevGrp, + currGrp, + nextGrp, + nextGrp2] + for i in self.currBldgGroups[side]: + self.buildingGroups[side][i].unstash() + + self.currBldgInd[side] = bldgInd + + if self.currBldgGroups['inner'] != self.currBldgGroups['outer']: + pass + if t != self.oldT: + self.oldT = t + if self.trackId in (RaceGlobals.RT_Urban_2, RaceGlobals.RT_Urban_2_rev): + if self.reversed: + t = 1.0 - t + for side in ['innersidest', 'outersidest']: + segmentInd = int(t * self.barricadeSegments) + seglmentInd = segmentInd % self.barricadeSegments + if segmentInd != self.currBldgInd[side] or forceRecompute: + currBldgGroups = self.currBldgGroups[side] + if currBldgGroups: + for i in currBldgGroups: + self.buildingGroups[side][i].stash() + + self.currBldgGroups[side] = [] + if side == 'innersidest': + dict = self.innerBarricadeDict + elif side == 'outersidest': + dict = self.outerBarricadeDict + if segmentInd in dict: + self.currBldgGroups[side] = dict[segmentInd] + for i in self.currBldgGroups[side]: + self.buildingGroups[side][i].unstash() + + self.currBldgInd[side] = segmentInd + + return + + def setupGeom(self): + trackFilepath = RaceGlobals.TrackDict[self.trackId][0] + self.geom = loader.loadModel(trackFilepath) + for i in xrange(10): + base.loader.tick() + + self.geom.reparentTo(render) + if self.reversed: + lapStartPos = self.geom.find('**/lap_start_rev').getPos() + else: + lapStartPos = self.geom.find('**/lap_start').getPos() + self.startPos = lapStartPos + lapMidPos = self.geom.find('**/lap_middle').getPos() + for i in xrange(5): + base.loader.tick() + + self.startingPos = [] + posLocators = self.geom.findAllMatches('**/start_pos*') + for i in xrange(posLocators.getNumPaths()): + base.loader.tick() + self.startingPos.append([posLocators[i].getPos(), posLocators[i].getHpr()]) + + self.notify.debug('self.startingPos: %s' % self.startingPos) + self.wrongWay = False + self.laps = 0 + if self.isUrbanTrack: + self.loadUrbanTrack() + self.genArrows() + if self.reversed: + self.curve = self.geom.find('**/curve_reverse').node() + else: + self.curve = self.geom.find('**/curve_forward').node() + for i in xrange(4000): + self.curvePoints.append(Point3(0, 0, 0)) + self.curve.getPoint(i / 4000.0 * (self.curve.getMaxT() - 1e-11), self.curvePoints[-1]) + self.curveTs.append(i / 4000.0 * (self.curve.getMaxT() - 1e-11)) + + if self.trackId in (RaceGlobals.RT_Urban_2, RaceGlobals.RT_Urban_2_rev): + self.precomputeSideStreets() + for i in xrange(10): + base.loader.tick() + + self.startT = self.getNearestT(lapStartPos) + self.midT = self.getNearestT(lapMidPos) + self.gags = [] + gagList = RaceGlobals.TrackDict[self.trackId][4] + for i in xrange(len(gagList)): + self.notify.debug('generating gag: %s' % i) + self.gags.append(RaceGag(self, i, Vec3(*gagList[i]) + Vec3(0, 0, 3))) + + for i in xrange(5): + base.loader.tick() + + def precomputeSideStreets(self): + farDist = base.camLens.getFar() + 300 + farDistSquared = farDist * farDist + for i in xrange(int(self.barricadeSegments)): + testPoint = Point3(0, 0, 0) + self.curve.getPoint(i / self.barricadeSegments * (self.curve.getMaxT() - 1e-11), testPoint) + for side in ('innersidest', 'outersidest'): + for bldgGroupIndex in xrange(len(self.buildingGroups[side])): + bldgGroup = self.buildingGroups[side][bldgGroupIndex] + if not bldgGroup.getNode(0).getBounds().isEmpty(): + bldgPoint = bldgGroup.getNode(0).getBounds().getCenter() + vector = testPoint - bldgPoint + if vector.lengthSquared() < farDistSquared: + if side == 'innersidest': + dict = self.innerBarricadeDict + elif side == 'outersidest': + dict = self.outerBarricadeDict + else: + self.notify.error('unhandled side') + if i in dict: + if bldgGroupIndex not in dict[i]: + dict[i].append(bldgGroupIndex) + else: + dict[i] = [bldgGroupIndex] + for childIndex in (0,): + if childIndex >= bldgGroup.getNumChildren(): + continue + childNodePath = bldgGroup.getChild(childIndex) + bldgPoint = childNodePath.node().getBounds().getCenter() + vector = testPoint - bldgPoint + if vector.lengthSquared() < farDistSquared: + if side == 'innersidest': + dict = self.innerBarricadeDict + elif side == 'outersidest': + dict = self.outerBarricadeDict + else: + self.notify.error('unhandled side') + if i in dict: + if bldgGroupIndex not in dict[i]: + dict[i].append(bldgGroupIndex) + else: + dict[i] = [bldgGroupIndex] + + for side in ('innersidest', 'outersidest'): + for bldgGroup in self.buildingGroups[side]: + bldgGroup.flattenStrong() + + if self.isUrbanTrack: + self.showBuildings(0, forceRecompute=True) + + def findSegmentStart(self): + kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None) + minLength2 = 1000000 + minIndex = -1 + currPoint = Point3(0, 0, 0) + kartPoint = self.localKart.getPos() + for i in xrange(len(self.curvePoints)): + currPoint = self.curvePoints[i] + currLength2 = (kartPoint - currPoint).lengthSquared() + if currLength2 < minLength2: + minLength2 = currLength2 + minIndex = i + + currPoint = self.curvePoints[minIndex] + if minIndex + 1 == len(self.curvePoints): + nextPoint = self.curvePoints[0] + else: + nextPoint = self.curvePoints[minIndex + 1] + if minIndex - 1 < 0: + prevIndex = len(self.curvePoints) - 1 + else: + prevIndex = minIndex - 1 + forwardSegment = nextPoint - currPoint + if (kartPoint - currPoint).dot(forwardSegment) > 0: + return minIndex + else: + return prevIndex + return + + def getNearestT(self, pos): + minLength2 = 1000000 + minIndex = -1 + currPoint = Point3(0, 0, 0) + for i in xrange(len(self.curvePoints)): + currPoint = self.curvePoints[i] + currLength2 = (pos - currPoint).lengthSquared() + if currLength2 < minLength2: + minLength2 = currLength2 + minIndex = i + + currPoint = self.curvePoints[minIndex] + if minIndex + 1 == len(self.curvePoints): + nextPoint = self.curvePoints[0] + else: + nextPoint = self.curvePoints[minIndex + 1] + if minIndex - 1 < 0: + prevIndex = len(self.curvePoints) - 1 + else: + prevIndex = minIndex - 1 + forwardSegment = nextPoint - currPoint + if (pos - currPoint).dot(forwardSegment) > 0: + pole = minIndex + else: + pole = prevIndex + currPoint = self.curvePoints[pole] + nextPole = (pole + 1) % len(self.curvePoints) + nextPoint = self.curvePoints[nextPole] + segment = nextPoint - currPoint + segment.setZ(0) + segLength2 = segment.lengthSquared() + posVector = pos - currPoint + posVector.setZ(0) + project = segment * (segment.dot(posVector) / segLength2) + percent = project.lengthSquared() / segLength2 + if nextPole < pole: + t = self.curveTs[pole] * (1 - percent) + self.curve.getMaxT() * percent + else: + t = self.curveTs[pole] * (1 - percent) + self.curveTs[nextPole] * percent + return t + + def hasGag(self, slot, type, index): + if self.gags[slot].isActive(): + self.gags[slot].disableGag() + + def leaveRace(self): + self.fsm.request('leave') + + def finishRace(self): + self.sendUpdate('heresMyT', [localAvatar.doId, self.lapCount, self.currLapT, globalClockDelta.localToNetworkTime(globalClock.getFrameTime())]) + self.fsm.request('finished') + + def racerLeft(self, avId): + if avId != localAvatar.doId: + self.gui.racerLeft(avId, unexpected=False) + + def skyTrack(self, task): + return SZUtil.cloudSkyTrack(task) + + def startSky(self): + if self.hasFog: + SZUtil.startCloudSky(self, parent=self.dummyNode, effects=CompassEffect.PRot) + else: + SZUtil.startCloudSky(self, parent=render) + + def stopSky(self): + taskMgr.remove('skyTrack') + + def pickupGag(self, slot, index): + self.canShoot = False + standing = self.gui.racerDict[localAvatar.doId].place - 1 + self.currGag = RaceGlobals.GagFreq[standing][index] + cycleTime = 2 + self.gui.waitingOnGag(cycleTime) + taskMgr.doMethodLater(cycleTime, self.enableShoot, 'enableShoot') + self.sendUpdate('hasGag', [slot, self.currGag, index]) + + def shootGag(self): + if self.canShoot: + if self.currGag == 1: + self.bananaSound.play() + self.shootBanana() + elif self.currGag == 2: + self.d_requestThrow(0, 0, 0) + self.localKart.startTurbo() + elif self.currGag == 3: + self.d_requestThrow(0, 0, 0) + elif self.currGag == 4: + self.bananaSound.play() + self.shootPie() + self.currGag = 0 + self.gui.updateGag(0) + + def enableShoot(self, t): + self.canShoot = True + if self.gui: + self.gui.updateGag(self.currGag) + + def shootBanana(self): + pos = self.localKart.getPos(render) + banana = self.banana.copyTo(self.geom) + banana.setPos(pos) + self.thrownGags.append(banana) + self.d_requestThrow(pos[0], pos[1], pos[2]) + + def shootPie(self): + pos = self.localKart.getPos(render) + self.d_requestThrow(pos[0], pos[1], pos[2]) + + def genArrows(self): + base.arrows = [] + arrowId = 0 + for boost in RaceGlobals.TrackDict[self.trackId][5]: + self.genArrow(boost[0], boost[1], arrowId) + arrowId += 1 + + def genArrow(self, pos, hpr, id): + factory = CardMaker('factory') + factory.setFrame(-.5, 0.5, -.5, 0.5) + arrowNode = factory.generate() + arrowRoot = NodePath('root') + baseArrow = NodePath(arrowNode) + baseArrow.setTransparency(1) + baseArrow.setTexture(self.boostArrowTexture) + baseArrow.reparentTo(arrowRoot) + arrow2 = baseArrow.copyTo(baseArrow) + arrow2.setPos(0, 0, 1) + arrow3 = arrow2.copyTo(arrow2) + arrowRoot.setPos(*pos) + arrowRoot.setHpr(*hpr) + baseArrow.setHpr(0, -90, 0) + baseArrow.setScale(24) + arrowRoot.reparentTo(self.geom) + trigger = 'boostArrow' + str(id) + cs = CollisionTube(Point3(0.6, -6, 0), Point3(0.6, 54, 0), 4.8) + cs.setTangible(0) + triggerEvent = 'imIn-' + trigger + cn = CollisionNode(trigger) + cn.addSolid(cs) + cn.setIntoCollideMask(BitMask32(32768)) + cn.setFromCollideMask(BitMask32(32768)) + cnp = NodePath(cn) + cnp.reparentTo(arrowRoot) + self.accept(triggerEvent, self.hitBoostArrow) + arrowVec = arrow2.getPos(self.geom) - baseArrow.getPos(self.geom) + arrowVec.normalize() + idStr = str(id) + cnp.setTag('boostId', idStr) + self.boostDir[idStr] = arrowVec + base.arrows.append(arrowRoot) + + def hitBoostArrow(self, cevent): + into = cevent.getIntoNodePath() + idStr = into.getTag('boostId') + arrowVec = self.boostDir.get(idStr) + if arrowVec == None: + print 'Unknown boost arrow %s' % idStr + return + fvec = self.localKart.forward.getPos(self.geom) - self.localKart.getPos(self.geom) + fvec.normalize() + dotP = arrowVec.dot(fvec) + if dotP > 0.7: + self.localKart.startTurbo() + + def fadeOutMusic(self): + if self.musicTrack: + self.musicTrack.finish() + curVol = self.raceMusic.getVolume() + interval = LerpFunctionInterval(self.raceMusic.setVolume, fromData=curVol, toData=0, duration=3) + self.musicTrack = Sequence(interval) + self.musicTrack.start() + + def changeMusicTempo(self, newPR): + if self.musicTrack: + self.musicTrack.finish() + self.musicTrack = Sequence(LerpFunctionInterval(self.raceMusic.setPlayRate, fromData=self.raceMusic.getPlayRate(), toData=newPR, duration=3)) + self.musicTrack.start() + + def setRaceZone(self, zoneId, trackId): + hoodId = self.cr.playGame.hood.hoodId + #base.loader.endBulkLoad('atRace') + #self.kartCleanup() + self.doneBarrier('waitingForExit') + self.sendUpdate('racerLeft', [localAvatar.doId]) + out = {'loader': 'racetrack', + 'where': 'racetrack', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'trackId': trackId, + 'shardId': None, + 'reason': RaceGlobals.Exit_UserReq} + base.cr.playGame.hood.loader.fsm.request('quietZone', [out]) + return + + +# TODO: Move this command to the AI server, and add more features to it. +@magicWord(category=CATEGORY_PROGRAMMER, types=[str]) +def race(command): + """ + A command set for races. + """ + command = command.lower() + if command == 'leave': + messenger.send('leaveRace') + return 'You left the race!' + elif command == 'finish': + messenger.send('finishRace') + return 'You finished the race!' + return 'Invalid command!' diff --git a/toontown/racing/DistributedRaceAI.py b/toontown/racing/DistributedRaceAI.py new file mode 100755 index 00000000..b36acebe --- /dev/null +++ b/toontown/racing/DistributedRaceAI.py @@ -0,0 +1,501 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.fsm.FSM import FSM +from direct.task import Task +from toontown.uberdog import TopToonsGlobals +import random + +from toontown.racing import RaceGlobals +from toontown.racing.DistributedGagAI import DistributedGagAI +from toontown.racing.DistributedVehicleAI import DistributedVehicleAI +from toontown.toonbase import TTLocalizer, ToontownGlobals + + +class DistributedRaceAI(DistributedObjectAI, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedRaceAI") + + def __init__(self, air, circuitPoints=[], circuitWinnings=[]): + DistributedObjectAI.__init__(self, air) + FSM.__init__(self, 'DistributedRaceAI') + self.air = air + self.zoneId = 0 + self.trackId = 0 + self.raceType = 0 + self.circuitLoop = [] + self.avatars = [] + self.finishedAvatars = [] + self.startingPlaces = [] + self.avatarKarts = [] + self.lapCount = 1 + self.gags = {} + self.avatarGags = {} + self.livingGags = [] + self.currentlyAffectedByAnvil = {} + self.avatarProgress = {} + self.circuitPoints = circuitPoints + self.circuitWinnings = circuitWinnings + self.quitAvatars = [] + self.startTime = globalClockDelta.networkToLocalTime(globalClockDelta.getRealNetworkTime()) + 3 + + def generate(self): + for avatar in self.avatars: + self.acceptOnce(self.air.getAvatarExitEvent(avatar), self.playerLeave, [avatar]) + self.demand('Join') + + def delete(self): + for aK in self.avatarKarts: + kart = self.air.doId2do[aK[1]] + kart.requestDelete() + for gag in self.livingGags: + gag.requestDelete() + if not self.circuitLoop: + self.air.deallocateZone(self.zoneId) + for i in xrange(len(self.gags)): + taskMgr.remove('regenGag%i-%i' % (i, self.doId)) + taskMgr.remove(self.uniqueName('next-race')) + DistributedObjectAI.delete(self) + + def enterJoin(self): + self.beginBarrier('waitingForJoin', self.avatars, 60, self.joinBarrierCallback) + self.d_waitingForJoin() + + def exitJoin(self): + pass + + def enterPrep(self): + self.beginBarrier('waitingForReady', self.avatars, 60, self.readyBarrierCallback) + self.gagPoints = RaceGlobals.TrackDict[self.trackId][4] + if self.raceType != RaceGlobals.Practice: + for i in xrange(len(self.gagPoints)): + gagId = random.randint(0, 5) + self.b_genGag(i, 1, gagId) + self.d_prepForRace() + + def exitPrep(self): + pass + + def enterTutorial(self): + self.beginBarrier('readRules', self.avatars, 30, self.readRulesCallback) + self.d_startTutorial() + + def exitTutorial(self): + pass + + def enterStart(self): + self.startTime = globalClockDelta.networkToLocalTime(globalClockDelta.getRealNetworkTime()) + 3 + self.b_startRace(3) + + def exitStart(self): + pass + + def readyBarrierCallback(self, avatars): + self.demand('Tutorial') + + def readRulesCallback(self, avatars): + self.demand('Start') + + def joinBarrierCallback(self, avatars): + for av in self.avatars: + if not av in avatars: + self.playerLeave(av) + for av in avatars: + kart = DistributedVehicleAI(self.air, av) + kart.generateWithRequired(self.zoneId) + self.avatarKarts.append([av, kart.getDoId()]) + self.beginBarrier('waitingForPrep', self.avatars, 60, self.prepBarrierCallback) + self.sendUpdate('setEnteredRacers', [self.avatarKarts]) + + def prepBarrierCallback(self, avatars): + self.demand('Prep') + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def d_setZoneId(self, zoneId): + self.sendUpdate('setZoneId', [zoneId]) + + def b_setZoneId(self, zoneId): + self.setZoneId(zoneId) + self.d_setZoneId(zoneId) + + def getZoneId(self): + return self.zoneId + + def setTrackId(self, trackId): + self.trackId = trackId + + def getTrackId(self): + return self.trackId + + def setRaceType(self, raceType): + self.raceType = raceType + + def getRaceType(self): + return self.raceType + + def setCircuitLoop(self, circuitLoop): + self.circuitLoop = circuitLoop + if self.circuitLoop and not self.circuitPoints: + self.circuitPoints = [[0, 0]] * len(self.avatars) + self.circuitWinnings = [0] * len(self.avatars) + + def getCircuitLoop(self): + return self.circuitLoop + + def setAvatars(self, avatarList): + self.avatars = avatarList + + def getAvatars(self): + return self.avatars + + def setStartingPlaces(self, startingPlaces): + self.startingPlaces = startingPlaces + + def getStartingPlaces(self): + return self.startingPlaces + + def setLapCount(self, lapCount): + self.lapCount = lapCount + + def getLapCount(self): + return self.lapCount + + def waitingForJoin(self): + self.beginBarrier('waitingForJoin', self.avatars, 60, self.b_prepForRace) + + def d_waitingForJoin(self): + self.sendUpdate('waitingForJoin', []) + + def b_waitingForJoin(self): + self.waitingForJoin() + self.d_waitingForJoin() + + def setEnteredRacers(self, todo0): + pass + + def d_prepForRace(self): + self.sendUpdate('prepForRace', []) + + def b_prepForRace(self, avatars): + self.prepForRace() + self.d_prepForRace() + + def startTutorial(self): + self.beginBarrier('readRules', self.avatars, 60, self.raceStart) + + def d_startTutorial(self): + self.sendUpdate('startTutorial', []) + + def b_startTutorial(self, avatars): + self.startTutorial() + self.d_startTutorial() + + def startRace(self, timeUntilStart): + taskMgr.doMethodLater(timeUntilStart, self.startKarts, 'startKarts%i' % self.doId, []) + + def startKarts(self): + for avatarKart in self.avatarKarts: + if avatarKart[1] in self.air.doId2do: + kart = self.air.doId2do[avatarKart[1]] + kart.sendUpdate('setInput', [1]) + self.avatarProgress[avatarKart[0]] = 0 + self.avatarGags[avatarKart[0]] = 0 + self.currentlyAffectedByAnvil[avatarKart[0]] = False + + def b_startRace(self, timeUntilStart): + self.startRace(timeUntilStart) + self.d_startRace(timeUntilStart) + + def d_startRace(self, timeUntilStart): + self.sendUpdate('startRace', [globalClockDelta.localToNetworkTime(globalClockDelta.globalClock.getRealTime() + timeUntilStart)]) + + def goToSpeedway(self, todo0, todo1): + pass + + def genGag(self, slot, number, type): + self.gags[slot] = [number, type] + + def d_genGag(self, slot, number, type): + self.sendUpdate('genGag', [slot, number, type]) + + def b_genGag(self, slot, number, type): + self.genGag(slot, number, type) + self.d_genGag(slot, number, type) + + def dropAnvilOn(self, todo0, todo1, todo2): + pass + + def shootPiejectile(self, todo0, todo1, todo2): + pass + + def racerDisconnected(self, todo0): + pass + + def setPlace(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6, todo7, todo8, todo9): + pass + + def setCircuitPlace(self, todo0, todo1, todo2, todo3, todo4, todo5): + pass + + def endCircuitRace(self): + pass + + def setRaceZone(self, todo0, todo1): + pass + + def hasGag(self, slot, requestedGag, index): + avId = self.air.getAvatarIdFromSender() + if not avId in self.avatars: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to get gag in a race they\'re not in!') + return + if self.raceType == RaceGlobals.Practice: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to gain gag in a practice race!') + return + places = sorted(self.avatarProgress, key=self.avatarProgress.get) + avPlace = places.index(avId) + gag = self.gags[slot] + if not gag[0]: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to pick up a gag that doesn\'t exist!') + return + gagIndex = gag[1] + realGag = RaceGlobals.GagFreq[avPlace][gagIndex] + if realGag != requestedGag: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to get the wrong gag!') + return + self.gags[slot] = [0, 0] + self.avatarGags[avId] = requestedGag + taskMgr.doMethodLater(5, self.__regenGag, 'regenGag%i-%i' % (slot, self.doId), [slot]) + + def __regenGag(self, index): + gagId = random.randint(0, 5) + self.b_genGag(index, 1, gagId) + + def racerLeft(self, avId): + realAvId = self.air.getAvatarIdFromSender() + if realAvId != avId: + self.air.writeServerEvent('suspicious', realAvId, 'Toon tried to make another quit race!') + return + if not avId in self.avatars: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to leave race they\'re not in!') + return + + if self.circuitLoop and self.finishedAvatars: + if avId in self.quitAvatars: + return + + self.quitAvatars.append(avId) + self.checkForNextRace() + else: + self.avatars.remove(avId) + if avId in self.quitAvatars: + self.quitAvatars.remove(avId) + if set(self.finishedAvatars) == set(self.avatars) or len(self.avatars) == 0: + self.requestDelete() + + def checkForNextRace(self): + if len(self.quitAvatars) >= len(self.avatars): + trackId = self.circuitLoop[0] + self.nextRace = DistributedRaceAI(self.air, self.circuitPoints, self.circuitWinnings) + self.nextRace.setZoneId(self.zoneId) + self.nextRace.setTrackId(trackId) + self.nextRace.setRaceType(self.raceType) + self.nextRace.setAvatars(self.avatars) + self.nextRace.setCircuitLoop(self.circuitLoop) + self.nextRace.setStartingPlaces(range(len(self.avatars))) + self.nextRace.setLapCount(3) + taskMgr.doMethodLater(3, self.startNewRace, self.uniqueName('next-race'), extraArgs=[trackId]) + + def startNewRace(self, trackId, task=None): + self.nextRace.generateWithRequired(self.zoneId) + self.sendUpdate('setRaceZone', [self.zoneId, trackId]) + + def heresMyT(self, avId, laps, currentLapT, timestamp): + realAvId = self.air.getAvatarIdFromSender() + if not avId == realAvId: + self.air.writeServerEvent('suspicious', realAvId, 'Toon tried to send a message as another toon!') + return + if not avId in self.avatars: + self.air.writeServerEvent('suspicious', avId, 'Toon not in race tried to send update to race!') + return + if laps == self.lapCount: + self.avatarFinished(avId) + self.avatarProgress[avId] = laps + currentLapT + + def avatarFinished(self, avId): + if not avId in self.avatars: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to finish in a race they\'re not in!') + return + + if avId in self.finishedAvatars: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to finish in a race twice!') + return + self.finishedAvatars.append(avId) + + av = self.air.doId2do.get(avId) + place = len(self.finishedAvatars) + listPlace = place + (4 - len(self.avatarProgress)) - 1 + entryFee = RaceGlobals.getEntryFee(self.trackId, self.raceType) + bonus = 0 + totalTime = globalClockDelta.networkToLocalTime(globalClockDelta.getRealNetworkTime()) - self.startTime + qualify = False + if totalTime < RaceGlobals.getQualifyingTime(self.trackId): + qualify = True + self.air.leaderboardMgr.submitRace(self.trackId, av.getName(), totalTime) + if self.raceType == RaceGlobals.Practice: + winnings = RaceGlobals.PracticeWinnings + trophies = [] + elif qualify: + winnings = entryFee * RaceGlobals.Winnings[listPlace] + trophies = self.calculateTrophies(avId, place == 1, qualify, totalTime) + else: + winnings = 0 + trophies = [] + av.b_setTickets(av.getTickets() + winnings) + if av.getTickets() > RaceGlobals.MaxTickets: + av.b_setTickets(RaceGlobals.MaxTickets) + av.addStat(ToontownGlobals.STAT_RACING) + points = [] + if self.circuitPoints: + avIndex = self.avatars.index(avId) + points = self.circuitPoints[avIndex] + points[0] += points[1] + points[1] = RaceGlobals.CircuitPoints[place - 1] + self.sendUpdate('setPlace', [avId, totalTime, place, entryFee, qualify, max((winnings-entryFee), 0), bonus, trophies, points, 0]) + if self.circuitPoints: + self.circuitWinnings[avIndex] += winnings + self.sendUpdate('setCircuitPlace', [avId, place, entryFee, self.circuitWinnings[avIndex], bonus, trophies]) + + if len(self.finishedAvatars) == len(self.avatars): + del self.circuitLoop[0] + self.sendUpdate('setCircuitLoop', [self.circuitLoop]) + self.sendUpdate('endCircuitRace') + + def calculateTrophies(self, avId, won, qualify, time): + if won: + messenger.send('topToonsManager-event', [avId, TopToonsGlobals.CAT_RACE_WON, 1]) + av = self.air.doId2do[avId] + kartingHistory = av.getKartingHistory() + avTrophies = av.getKartingTrophies() + numTrophies = 0 + for i in xrange(30): + if avTrophies[i] != 0: + numTrophies += 1 + oldLaffBoost = int(numTrophies/10) + genre = RaceGlobals.getTrackGenre(self.trackId) + trophies = [] + if won: + kartingHistory[genre] += 1 + kartingHistory[3] += 1 + if kartingHistory[3] > RaceGlobals.TotalWonRaces: + avTrophies[RaceGlobals.TotalWins] = 1 + trophies.append(RaceGlobals.TotalWins) + for i in xrange(3): + if kartingHistory[genre] >= RaceGlobals.WonRaces[i] and avTrophies[RaceGlobals.AllWinsList[genre][i]] != 1: + avTrophies[RaceGlobals.AllWinsList[genre][i]] = 1 + trophies.append(RaceGlobals.AllWinsList[genre][i]) + if qualify: + kartingHistory[genre + 4] += 1 + kartingHistory[7] += 1 + if kartingHistory[7] >= RaceGlobals.TotalQualifiedRaces and avTrophies[RaceGlobals.TotalQuals] != 1: + avTrophies[RaceGlobals.TotalQuals] = 1 + trophies.append(RaceGlobals.TotalQuals) + for i in xrange(3): + if kartingHistory[genre + 4] >= RaceGlobals.QualifiedRaces[i] and avTrophies[RaceGlobals.AllQualsList[genre][i]] != 1: + avTrophies[RaceGlobals.AllQualsList[genre][i]] = 1 + trophies.append(RaceGlobals.AllQualsList[genre][i]) + for i, history in enumerate(kartingHistory): + if history > 255: + kartingHistory[i] = 255 + av.b_setKartingHistory(kartingHistory) + pKartingBest = av.getKartingPersonalBestAll() + trackIndex = TTLocalizer.KartRace_TrackNames.keys().index(self.trackId) + if pKartingBest[trackIndex] > time or not pKartingBest[trackIndex]: + pKartingBest[trackIndex] = time + av.b_setKartingPersonalBest(pKartingBest) + gTourTrophy = True + for bestTime in pKartingBest: + if not bestTime: + gTourTrophy = False + if gTourTrophy: + if avTrophies[RaceGlobals.GrandTouring] != 1: + avTrophies[RaceGlobals.GrandTouring] = 1 + trophies.append(RaceGlobals.GrandTouring) + newLaffBoost = int((len(trophies) + numTrophies)/10) + if newLaffBoost - oldLaffBoost != 0: + for i in xrange(newLaffBoost): + if avTrophies[RaceGlobals.TrophyCups[i]] != 1: + avTrophies[RaceGlobals.TrophyCups[i]] = 1 + trophies.append(RaceGlobals.TrophyCups[i]) + av.b_setMaxHp(av.getMaxHp() + newLaffBoost - oldLaffBoost) + av.toonUp(av.getMaxHp()) + av.b_setKartingTrophies(avTrophies) + return trophies + + def requestThrow(self, x, y, z): + avId = self.air.getAvatarIdFromSender() + if not avId in self.avatars: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to throw a gag in a race they\'re not in!') + if self.avatarGags[avId] == RaceGlobals.BANANA: + gag = DistributedGagAI(self.air) + gag.setRace(self.doId) + gag.setOwnerId(avId) + gag.setPos(x, y, z) + gag.setType(0) + gag.setInitTime(globalClockDelta.getRealNetworkTime()) + gag.setActivateTime(globalClockDelta.getRealNetworkTime()) + gag.generateWithRequired(self.zoneId) + self.livingGags.append(gag) + elif self.avatarGags[avId] == RaceGlobals.TURBO: + pass + elif self.avatarGags[avId] == RaceGlobals.ANVIL: + places = sorted(self.avatarProgress, key=self.avatarProgress.get, reverse=True) + for i in places: + if not i in self.finishedAvatars and not self.currentlyAffectedByAnvil[i]: + currAvatar = i + break + self.currentlyAffectedByAnvil[avId] = True + taskMgr.doMethodLater(RaceGlobals.AnvilSquishDuration, self.unsquish, 'unsquish-%i' % currAvatar, [currAvatar]) + self.sendUpdate('dropAnvilOn', [avId, currAvatar, globalClockDelta.getRealNetworkTime()]) + elif self.avatarGags[avId] == RaceGlobals.PIE: + places = sorted(self.avatarProgress, key=self.avatarProgress.get) + avPlace = places.index(avId) + if avPlace + 1 == len(places): + target = 0 + else: + target = places[avPlace + 1] + self.sendUpdate('shootPiejectile', [avId, target, 0]) + else: + self.air.writeServerEvent('suspicious', avId, 'Toon use race gag while not having one!') + self.avatarGags[avId] = 0 + + def unsquish(self, avId): + self.currentlyAffectedByAnvil[avId] = False + + def playerLeave(self, avId): + self.sendUpdate('racerDisconnected', [avId]) + if avId in self.avatars: + self.avatars.remove(avId) + count = 0 + for aK in self.avatarKarts: + if aK[0] == avId and aK[1] in self.air.doId2do: + self.air.doId2do[aK[1]].handleUnexpectedExit() + del self.avatarKarts[count] + break + count += 1 + if len(self.avatars) == 0: + self.requestDelete() + else: + self.checkForNextRace() + + def requestKart(self): + pass + avId = self.air.getAvatarIdFromSender() + accId = self.air.getAccountIdFromSender() + if not avId in self.avatars: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to request kart in race they\'re not in!') + return + for aK in self.avatarKarts: + if aK[0] == avId: + self.air.doId2do[aK[1]].request('Controlled', avId, accId) + self.air.doId2do[aK[1]].sendUpdate('setInput', [0]) diff --git a/toontown/racing/DistributedRacePad.py b/toontown/racing/DistributedRacePad.py new file mode 100755 index 00000000..61f8e8ea --- /dev/null +++ b/toontown/racing/DistributedRacePad.py @@ -0,0 +1,277 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.task.Task import Task +from direct.distributed.ClockDelta import * +from direct.fsm.FSM import FSM +from direct.interval.IntervalGlobal import * +from toontown.racing.DistributedKartPad import DistributedKartPad +from toontown.racing import RaceGlobals +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.racing.KartShopGlobals import KartGlobals + +class DistributedRacePad(DistributedKartPad, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedRacePad') + defaultTransitions = {'Off': ['WaitEmpty'], + 'WaitEmpty': ['WaitCountdown', 'Off'], + 'WaitCountdown': ['WaitEmpty', + 'WaitBoarding', + 'Off', + 'AllAboard'], + 'WaitBoarding': ['AllAboard', 'WaitEmpty', 'Off'], + 'AllAboard': ['Off', 'WaitEmpty', 'WaitCountdown']} + id = 0 + + def __init__(self, cr): + self.cr = cr + DistributedKartPad.__init__(self, cr) + FSM.__init__(self, 'RacePad_%s_FSM' % self.id) + self.id = DistributedRacePad.id + DistributedRacePad.id += 1 + self.trackId = None + self.trackType = None + self.timeStamp = None + self.clockNodepath = None + self.timerTask = None + self.tunnelSign = None + self.trackNameNode = None + self.tunnelSignInterval = None + return + + def disable(self): + self.notify.debug('Disable') + self.ignore('enterPlayground') + self.request('Off') + if self.tunnelSignInterval: + self.tunnelSignInterval = None + DistributedKartPad.disable(self) + return + + def enableStartingBlocks(self): + self.notify.debug('Enabling starting blocks') + for block in self.startingBlocks: + block.setActive(0) + + def disableStartingBlocks(self): + for block in self.startingBlocks: + self.notify.debug('Disabling kart block: %s' % block.getDoId()) + block.setActive(1) + + def isPractice(self): + return self.trackType == RaceGlobals.Practice + + def setState(self, state, timestamp): + self.request(state, [timestamp]) + + def setRaceZone(self, zoneId): + for block in self.startingBlocks: + if block.avId == base.localAvatar.getDoId(): + hoodId = self.cr.playGame.hood.hoodId + self.cr.playGame.getPlace().doneStatus = {'loader': 'racetrack', + 'where': 'racetrack', + 'zoneId': zoneId, + 'trackId': self.trackId, + 'hoodId': hoodId} + messenger.send(base.cr.playGame.getPlace().doneEvent) + + def setTrackInfo(self, trackInfo): + if self.isDisabled(): + return + self.trackId, self.trackType = trackInfo + self.notify.debugStateCall(self) + self.setTunnelSignText() + self.ignore('enterPlayground') + self.acceptOnce('enterPlayground', self.setTunnelSignText) + + def enterOff(self, *args): + self.notify.debug('enterOff: Entering Off State for RacePad %s' % self.id) + if self.tunnelSignInterval: + self.tunnelSignInterval.finish() + self.cleanupTunnelText() + + def exitOff(self): + self.notify.debug('exitOff: Exiting Off state for RacePad %s' % self.id) + + def enterWaitEmpty(self, args): + self.notify.debug('enterWaitEmpty: Entering WaitEmpty State for RacePad %s' % self.id) + if self.tunnelSignInterval: + self.tunnelSignInterval.finish() + + def exitWaitEmpty(self): + self.notify.debug('exitWaitEmpty: Exiting WaitEmpty State for RacePad %s' % self.id) + + def enterWaitCountdown(self, args): + self.notify.debug('enterWaitCountdown: Entering WaitCountdown State for RacePad %s' % self.id) + self.timeStamp = args[0] + self.startCountdown() + + def exitWaitCountdown(self): + self.notify.debug('exitWaitCountdown: Exiting WaitCountdown State for RacePad %s' % self.id) + self.stopCountdown() + + def enterWaitBoarding(self, args): + self.notify.debug('enterWaitBoarding: Entering WaitBoarding State for RacePad %s' % self.id) + self.timeStamp = args[0] + for block in self.startingBlocks: + block.hideGui() + + def exitWaitBoarding(self): + self.notify.debug('exitWaitBoarding: Exiting WaitBording State for RacePad %s' % self.id) + + def enterAllAboard(self, args): + self.notify.debug('enterAllAboard: Entering AllAboard State for RacePad %s' % self.id) + for block in self.startingBlocks: + block.request('Off') + if block.av and block.kartNode: + self.notify.debug('enterAllAboard: Avatar %s is in the race.' % block.av.doId) + block.doExitToRaceTrack() + + def exitAllAboard(self): + self.notify.debug('enterAllAboard: Exiting AllAboard State for RacePad %s' % self.id) + + def getTimestamp(self, avId = None): + error = 'DistributedRacePad::getTimeStamp - timestamp not yet set!' + return self.timeStamp + + def stopCountdown(self): + if self.timerTask: + taskMgr.remove(self.timerTask) + self.clockNodepath.removeNode() + self.clockNodepath = None + self.clockNode = None + self.timerTask = None + return + + def updateTimerTask(self, task): + countdownTime = int(task.duration - task.time) + timeStr = str(countdownTime) + if self.clockNode.getText() != timeStr: + self.clockNode.setText(timeStr) + if task.time >= task.duration: + return Task.done + else: + return Task.cont + + def startCountdown(self): + if not self.timerTask and self.startingBlocks: + self.makeClockGui() + duration = KartGlobals.COUNTDOWN_TIME - globalClockDelta.localElapsedTime(self.getTimestamp()) + countdownTask = Task(self.updateTimerTask) + countdownTask.duration = duration + self.timerTask = taskMgr.add(countdownTask, self.uniqueName('racePadTimerTask')) + + def addStartingBlock(self, block): + DistributedKartPad.addStartingBlock(self, block) + if self.state == 'WaitCountdown': + self.startCountdown() + + def makeClockGui(self): + self.notify.debugStateCall(self) + if self.clockNodepath is not None: + return + self.clockNode, self.clockNodepath = self.getSignTextNodes('racePadClock') + self.clockNodepath.setPos(0, 0.125, -3.0) + self.clockNodepath.setScale(2.5) + self.clockNodepath.flattenLight() + return + + def getTunnelSign(self): + cPadId = RaceGlobals.RaceInfo2RacePadId(self.trackId, self.trackType) + genreId = RaceGlobals.getTrackGenre(self.trackId) + tunnelName = RaceGlobals.getTunnelSignName(genreId, cPadId) + self.tunnelSign = self.cr.playGame.hood.loader.geom.find('**/' + tunnelName) + + def getSignTextNodes(self, nodeName, font = ToontownGlobals.getSignFont()): + signTextNode = TextNode(nodeName) + signTextNode.setFont(font) + signTextNode.setAlign(TextNode.ACenter) + signTextNode.setTextColor(0.5, 0.5, 0.5, 1) + signTextNodepath = self.tunnelSign.attachNewNode(signTextNode) + signTextNodepath.setPos(0, 0.25, 0) + signTextNodepath.setH(165.0) + signTextNodepath.setDepthWrite(0) + return (signTextNode, signTextNodepath) + + def setTunnelSignText(self): + self.notify.debugStateCall(self) + self.getTunnelSign() + if not self.tunnelSign or self.tunnelSign.isEmpty(): + return + if not self.trackNameNode: + self.makeTextNodes() + if self.tunnelSignInterval: + self.tunnelSignInterval.finish() + self.tunnelSignInterval = Sequence(Func(self.hideTunnelSignText), Wait(0.2), Func(self.showTunnelSignText), Wait(0.2), Func(self.hideTunnelSignText), Wait(0.2), Func(self.showTunnelSignText), Wait(0.2), Func(self.hideTunnelSignText), Wait(0.2), Func(self.updateTunnelSignText), Func(self.showTunnelSignText)) + self.tunnelSignInterval.start() + + def hideTunnelSignText(self): + if self.tunnelSign: + textNodePaths = self.tunnelSign.findAllMatches('**/+TextNode') + numTextNodePaths = textNodePaths.getNumPaths() + for i in xrange(numTextNodePaths): + textNodePath = textNodePaths.getPath(i) + textNodePath.hide() + + def showTunnelSignText(self): + if self.tunnelSign: + textNodePaths = self.tunnelSign.findAllMatches('**/+TextNode') + numTextNodePaths = textNodePaths.getNumPaths() + for i in xrange(numTextNodePaths): + textNodePath = textNodePaths.getPath(i) + textNodePath.show() + + def updateTunnelSignText(self): + self.notify.debugStateCall(self) + trackNameString = TTLocalizer.KartRace_TrackNames[self.trackId] + if not self.trackNameNode: + self.notify.warning('invalid trackNameNode, just returning') + return + self.trackNameNode.setText(trackNameString) + trackTypeString = TTLocalizer.KartRace_RaceNames[self.trackType] + self.trackTypeNode.setText(trackTypeString) + deposit = 0 + if self.trackType: + deposit = RaceGlobals.getEntryFee(self.trackId, self.trackType) + depositString = TTLocalizer.KartRace_DepositPhrase + str(deposit) + self.depositNode.setText(depositString) + time = RaceGlobals.TrackDict[self.trackId][1] + secs, hundredths = divmod(time, 1) + min, sec = divmod(secs, 60) + timeText = '%02d:%02d:%02d' % (min, sec, hundredths * 100) + qualifyString = TTLocalizer.KartRace_QualifyPhrase + timeText + self.qualifyNode.setText(qualifyString) + + def makeTextNodes(self): + self.notify.debugStateCall(self) + self.trackNameNode, trackNameNodePath = self.getSignTextNodes('trackNameNode') + trackNameNodePath.setZ(0.7) + trackNameNodePath.setScale(0.875) + trackNameNodePath.flattenLight() + self.trackTypeNode, trackTypeNodePath = self.getSignTextNodes('trackTypeNode') + trackTypeNodePath.setZ(-0.35) + trackTypeNodePath.setScale(0.875) + trackTypeNodePath.flattenLight() + self.depositNode, depositNodePath = self.getSignTextNodes('depositNode', ToontownGlobals.getToonFont()) + self.depositNode.setTextColor(0, 0, 0, 1) + depositNodePath.setPos(4.0, -1.0, -2.0) + depositNodePath.setScale(0.75) + depositNodePath.flattenLight() + self.qualifyNode, qualifyNodePath = self.getSignTextNodes('qualifyNode', ToontownGlobals.getToonFont()) + self.qualifyNode.setTextColor(0, 0, 0, 1) + qualifyNodePath.setPos(-4.0, 1.2, -2.0) + qualifyNodePath.setScale(0.75) + qualifyNodePath.flattenLight() + + def cleanupTunnelText(self): + self.notify.debugStateCall(self) + if self.tunnelSign: + textNodePaths = self.tunnelSign.findAllMatches('**/+TextNode') + numTextNodePaths = textNodePaths.getNumPaths() + for i in xrange(numTextNodePaths): + textNodePath = textNodePaths.getPath(i) + textNodePath.removeNode() + textNodePath = None + + self.tunnelSign = None + self.trackNameNode = None + return diff --git a/toontown/racing/DistributedRacePadAI.py b/toontown/racing/DistributedRacePadAI.py new file mode 100755 index 00000000..44e665ac --- /dev/null +++ b/toontown/racing/DistributedRacePadAI.py @@ -0,0 +1,174 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.racing.DistributedKartPadAI import DistributedKartPadAI +from toontown.racing.DistributedRaceAI import DistributedRaceAI +from toontown.racing import RaceGlobals +from direct.fsm.FSM import FSM +from direct.distributed.ClockDelta import * +from direct.task import * +from toontown.racing.KartShopGlobals import KartGlobals + +#TODO - change race type + +class DistributedRacePadAI(DistributedKartPadAI, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedRacePadAI") + defaultTransitions = {'Off': ['WaitEmpty'], + 'WaitEmpty': ['WaitCountdown', 'Off'], + 'WaitCountdown': ['WaitEmpty', + 'WaitBoarding', + 'Off', + 'AllAboard'], + 'WaitBoarding': ['AllAboard', 'WaitEmpty', 'Off'], + 'AllAboard': ['Off', 'WaitEmpty', 'WaitCountdown']} + + def __init__(self, air): + DistributedKartPadAI.__init__(self, air) + FSM.__init__(self, 'DistributedRacePadAI') + self.air = air + self.trackId, self.trackType = [None, None] + self.lastTime = globalClockDelta.getRealNetworkTime() + self.shouldStart = False + self.index = -1 + self.nameType = 'urban' + + def generate(self): + DistributedKartPadAI.generate(self) + self.b_setState('WaitEmpty', globalClockDelta.getRealNetworkTime()) + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterWaitEmpty(self): + for block in self.startingBlocks: + block.b_setOccupied(0) + self.shouldStart = False + taskMgr.doMethodLater(30, DistributedRacePadAI.changeTrack, 'changeTrack%i' % self.doId, [self]) + + def exitWaitEmpty(self): + taskMgr.remove('changeTrack%i' % self.doId) + + def enterWaitCountdown(self): + taskMgr.doMethodLater(11, DistributedRacePadAI.startRace, 'startRace%i' % self.doId, [self]) + + def exitWaitCountdown(self): + taskMgr.remove('startRace%i' % self.doId) + + def enterWaitBoarding(self): + pass + + def exitWaitBoarding(self): + pass + + def enterAllAboard(self): + taskMgr.doMethodLater(2, DistributedRacePadAI.createRace, 'createRace%i' % self.doId, [self]) + + def exitAllAboard(self): + pass + + def changeTrack(self): + nri = RaceGlobals.getNextRaceInfo(self.trackId, self.nameType, self.index) + self.b_setTrackInfo([nri[0], nri[1]]) + taskMgr.doMethodLater(30, DistributedRacePadAI.changeTrack, 'changeTrack%i' % self.doId, [self]) + + def updateTimer(self): + hasAvatars = False + for block in self.startingBlocks: + if block.avId != 0: + hasAvatars = True + break + if hasAvatars and self.state == 'WaitEmpty': + self.b_setState('WaitCountdown', globalClockDelta.getRealNetworkTime()) + elif not hasAvatars and self.state == 'WaitCountdown': + self.b_setState('WaitEmpty', globalClockDelta.getRealNetworkTime()) + + def updateMovieState(self): + if self.state == 'WaitBoarding': + for block in self.startingBlocks: + if block.currentMovie != 0: + return + self.runningMovie = False + self.startRace() + else: + for block in self.startingBlocks: + if block.currentMovie != 0: + self.runningMovie = True + return + self.runningMovie = False + def startRace(self): + if self.runningMovie: + self.request('WaitBoarding') + return + if self.trackType != RaceGlobals.Practice and False: + count = 0 + for block in self.startingBlocks: + if block.avId != 0: + count += 1 + if count < 2: + for block in self.startingBlocks: + if block.avId != 0: + block.b_setMovie(KartGlobals.EXIT_MOVIE) + self.b_setState('WaitEmpty', globalClockDelta.getRealNetworkTime()) + return + self.b_setState('AllAboard', globalClockDelta.getRealNetworkTime()) + + def createRace(self): + self.raceZone = self.air.allocateZone() + avatars = [] + for block in self.startingBlocks: + if block.avId != 0: + avatars.append(block.avId) + self.sendUpdateToAvatarId(block.avId, 'setRaceZone', [self.raceZone]) + race = DistributedRaceAI(self.air) + race.setZoneId(self.raceZone) + race.setTrackId(self.trackId) + race.setRaceType(self.trackType) + race.setAvatars(avatars) + if self.trackType == RaceGlobals.Circuit: + race.setCircuitLoop(RaceGlobals.getCircuitLoop(self.trackId)) + else: + race.setCircuitLoop([]) + race.setStartingPlaces(range(len(avatars))) + race.setLapCount(3) + race.generateWithRequired(self.raceZone) + for avId in avatars: + if avId in self.air.doId2do: + av = self.air.doId2do[avId] + entryFee = RaceGlobals.getEntryFee(self.trackId, self.trackType) + if av.getTickets() < entryFee: + self.air.writeServerEvent('suspicious', avId, 'Toon somehow lost tickets between entering a race and it leaving!') + av.b_setTickets(0) + else: + av.b_setTickets(av.getTickets() - entryFee) + self.b_setState('WaitEmpty', globalClockDelta.getRealNetworkTime()) + + def setState(self, state, timeStamp): + self.lastTime = globalClockDelta.getRealNetworkTime() + self.request(state) + + def d_setState(self, state, timeStamp): + self.sendUpdate('setState', [state, timeStamp]) + + def b_setState(self, state, timeStamp): + self.setState(state, timeStamp) + self.d_setState(state, timeStamp) + + def getState(self): + return [self.state, self.lastTime] + + def setRaceZone(self, todo0): + pass + + def getTrackInfo(self): + return [self.trackId, self.trackType] + + def setTrackInfo(self, trackInfo): + self.trackId, self.trackType = trackInfo + + def d_setTrackInfo(self, trackInfo): + self.sendUpdate('setTrackInfo', [trackInfo]) + + def b_setTrackInfo(self, trackInfo): + self.setTrackInfo(trackInfo) + self.d_setTrackInfo(trackInfo) diff --git a/toontown/racing/DistributedStartingBlock.py b/toontown/racing/DistributedStartingBlock.py new file mode 100755 index 00000000..66f9e3a3 --- /dev/null +++ b/toontown/racing/DistributedStartingBlock.py @@ -0,0 +1,695 @@ +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from toontown.building.ElevatorConstants import * +from toontown.building.ElevatorUtils import * +from toontown.building import DistributedElevatorExt +from toontown.building import DistributedElevator +from toontown.toonbase import ToontownGlobals +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.gui import DirectGui +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from direct.distributed import DistributedObject +from direct.distributed import DistributedSmoothNode +from direct.actor import Actor +from direct.fsm.FSM import FSM +from direct.showbase import PythonUtil +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.racing.Kart import Kart +from toontown.racing.KartShopGlobals import KartGlobals +from toontown.racing import RaceGlobals +from toontown.toontowngui.TTDialog import TTGlobalDialog + +class DistributedStartingBlock(DistributedObject.DistributedObject, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStartingBlock') + sphereRadius = 1.5 + id = 0 + cameraPos = Point3(0, -23, 10) + cameraHpr = Point3(0, -10, 0) + SFX_BaseDir = 'phase_6/audio/sfx/' + SFX_KartAppear = SFX_BaseDir + 'KART_Appear.ogg' + defaultTransitions = {'Off': ['EnterMovie'], + 'EnterMovie': ['Off', 'Waiting', 'ExitMovie'], + 'Waiting': ['ExitMovie', 'Off'], + 'ExitMovie': ['Off', 'ExitMovie']} + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.__init__(self, 'staringBlock_%s_FSM' % DistributedStartingBlock.id) + self.avId = 0 + self.av = None + self.lastAvId = 0 + self.avatar = None + self.kartPad = None + self.collNode = None + self.movieNode = None + self.movieTrack = None + self.collSphere = None + self.collNodePath = None + self.localToonKarting = 0 + self.kartNode = None + self.kart = None + self.holeActor = None + self.exitRequested = False + if (__debug__): + self.testLOD = False + self.id = DistributedStartingBlock.id + DistributedStartingBlock.id += 1 + return + + def disable(self): + FSM.cleanup(self) + self.ignore(self.uniqueName('enterStartingBlockSphere')) + self.ignore('stoppedAsleep') + self.setOccupied(0) + self.avId = 0 + self.nodePath.detachNode() + self.kartPad = None + if self.holeActor: + self.holeActor.cleanup() + self.holeActor = None + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + if hasattr(self, 'dialog'): + if not self.dialog.removed(): + self.dialog.ignoreAll() + if not self.dialog.isEmpty(): + self.dialog.cleanup() + del self.dialog + self.finishMovie() + if hasattr(self, 'cancelButton'): + self.cancelButton.destroy() + del self.cancelButton + del self.kartPad + if self.nodePath: + self.nodePath.removeNode() + del self.nodePath + DistributedObject.DistributedObject.delete(self) + + def generateInit(self): + self.notify.debugStateCall(self) + DistributedObject.DistributedObject.generateInit(self) + self.nodePath = NodePath(self.uniqueName('StartingBlock')) + self.collSphere = CollisionSphere(0, 0, 0, self.sphereRadius) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.uniqueName('StartingBlockSphere')) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.nodePath.attachNewNode(self.collNode) + + def announceGenerate(self): + self.notify.debugStateCall(self) + DistributedObject.DistributedObject.announceGenerate(self) + self.nodePath.reparentTo(render) + self.accept(self.uniqueName('enterStartingBlockSphere'), self.__handleEnterSphere) + if (__debug__): + if self.testLOD: + self.__generateKartAppearTrack() + + def setPadDoId(self, padDoId): + if padDoId in self.cr.doId2do: + self.setPad(self.cr.doId2do[padDoId]) + else: + self.acceptOnce('generate-%d' % padDoId, self.setPad) + + def setPad(self, pad): + self.kartPad = pad + self.kartPad.addStartingBlock(self) + + def setPosHpr(self, x, y, z, h, p, r): + self.notify.debugStateCall(self) + self.nodePath.setPosHpr(x, y, z, h + 180, 0, 0) + + def setPadLocationId(self, padLocationId): + self.notify.debugStateCall(self) + self.movieNode = self.nodePath.attachNewNode(self.uniqueName('MovieNode')) + self.exitMovieNode = self.movieNode + if padLocationId % 2: + self.movieNode.setPosHpr(3.0, 0, 0, 90.0, 0, 0) + else: + self.movieNode.setPosHpr(-3.0, 0, 0, -90.0, 0, 0) + + def setActive(self, isTangible): + self.collSphere.setTangible(isTangible) + + def __handleEnterSphere(self, collEntry): + if base.localAvatar.doId == self.lastAvId and globalClock.getFrameCount() <= self.lastFrame + 1: + self.notify.debug('Ignoring duplicate entry for avatar.') + return + if base.localAvatar.hp > 0: + + def handleEnterRequest(self = self): + self.ignore('stoppedAsleep') + if hasattr(self.dialog, 'doneStatus') and self.dialog.doneStatus == 'ok': + self.d_requestEnter() + elif self.cr and not self.isDisabled(): + self.cr.playGame.getPlace().setState('walk') + else: + self.notify.warning('Warning! Object has already been disabled.') + self.dialog.ignoreAll() + self.dialog.cleanup() + del self.dialog + + self.cr.playGame.getPlace().fsm.request('stopped') + self.accept('stoppedAsleep', handleEnterRequest) + doneEvent = 'enterRequest|dialog' + if self.kartPad.isPractice(): + msg = TTLocalizer.StartingBlock_EnterPractice + else: + raceName = TTLocalizer.KartRace_RaceNames[self.kartPad.trackType] + numTickets = RaceGlobals.getEntryFee(self.kartPad.trackId, self.kartPad.trackType) + msg = TTLocalizer.StartingBlock_EnterNonPractice % (raceName, numTickets) + self.dialog = TTGlobalDialog(msg, doneEvent, 4) + self.dialog.accept(doneEvent, handleEnterRequest) + + def d_movieFinished(self): + self.notify.debugStateCall(self) + self.sendUpdate('movieFinished', []) + + def d_requestEnter(self): + self.notify.debugStateCall(self) + self.sendUpdate('requestEnter') + + def d_requestExit(self): + self.notify.debugStateCall(self) + self.exitRequested = True + self.hideGui() + self.sendUpdate('requestExit', []) + + def rejectEnter(self, errCode): + self.notify.debugStateCall(self) + + def handleTicketError(self = self): + self.ignore('stoppedAsleep') + self.dialog.ignoreAll() + self.dialog.cleanup() + del self.dialog + self.cr.playGame.getPlace().setState('walk') + + doneEvent = 'errorCode|dialog' + if errCode == KartGlobals.ERROR_CODE.eTickets: + msg = TTLocalizer.StartingBlock_NotEnoughTickets + self.dialog = TTGlobalDialog(msg, doneEvent, 2) + self.dialog.accept(doneEvent, handleTicketError) + self.accept('stoppedAsleep', handleTicketError) + elif errCode == KartGlobals.ERROR_CODE.eBoardOver: + msg = TTLocalizer.StartingBlock_NoBoard + self.dialog = TTGlobalDialog(msg, doneEvent, 2) + self.dialog.accept(doneEvent, handleTicketError) + self.accept('stoppedAsleep', handleTicketError) + elif errCode == KartGlobals.ERROR_CODE.eNoKart: + msg = TTLocalizer.StartingBlock_NoKart + self.dialog = TTGlobalDialog(msg, doneEvent, 2) + self.dialog.accept(doneEvent, handleTicketError) + self.accept('stoppedAsleep', handleTicketError) + elif errCode == KartGlobals.ERROR_CODE.eOccupied: + msg = TTLocalizer.StartingBlock_Occupied + self.dialog = TTGlobalDialog(msg, doneEvent, 2) + self.dialog.accept(doneEvent, handleTicketError) + self.accept('stoppedAsleep', handleTicketError) + elif errCode == KartGlobals.ERROR_CODE.eTrackClosed: + msg = TTLocalizer.StartingBlock_TrackClosed + self.dialog = TTGlobalDialog(msg, doneEvent, 2) + self.dialog.accept(doneEvent, handleTicketError) + self.accept('stoppedAsleep', handleTicketError) + else: + self.cr.playGame.getPlace().setState('walk') + + def finishMovie(self): + if self.movieTrack: + self.movieTrack.finish() + self.movieTrack = None + return + + def setOccupied(self, avId): + self.notify.debug('%d setOccupied: %d' % (self.doId, avId)) + if self.av != None: + self.finishMovie() + if not self.av.isEmpty() and not self.av.isDisabled(): + self.av.loop('neutral') + self.av.setParent(ToontownGlobals.SPRender) + self.av.startSmooth() + self.finishMovie() + if self.kart: + self.kart.delete() + self.kart = None + if self.kartNode: + self.kartNode.removeNode() + self.kartNode = None + self.placedAvatar = 0 + self.ignore(self.av.uniqueName('disable')) + self.av = None + wasLocalToon = self.localToonKarting + self.lastAvId = self.avId + self.lastFrame = globalClock.getFrameCount() + self.avId = avId + self.localToonKarting = 0 + if self.avId == 0: + self.collSphere.setTangible(0) + self.request('Off') + else: + self.collSphere.setTangible(1) + av = self.cr.doId2do.get(self.avId) + self.placedAvatar = 0 + if self.avId == base.localAvatar.doId: + self.localToonKarting = 1 + if av != None: + self.av = av + self.av.stopSmooth() + self.placedAvatar = 0 + self.acceptOnce(self.av.uniqueName('disable'), self.__avatarGone) + self.kartNode = render.attachNewNode(self.av.uniqueName('KartNode')) + self.kartNode.setPosHpr(self.nodePath.getPos(render), self.nodePath.getHpr(render)) + self.kart = Kart() + self.kart.baseScale = 1.6 + self.kart.setDNA(self.av.getKartDNA()) + self.kart.generateKart() + self.kart.resetGeomPos() + self.av.wrtReparentTo(self.nodePath) + self.av.setAnimState('neutral', 1.0) + if not self.localToonKarting: + av.stopSmooth() + self.__placeAvatar() + self.avParent = av.getParent() + else: + self.notify.warning('Unknown avatar %d in kart block %d ' % (self.avId, self.doId)) + self.avId = 0 + if wasLocalToon and not self.localToonKarting: + place = base.cr.playGame.getPlace() + if place: + if self.exitRequested: + place.setState('walk') + else: + + def handleDialogOK(self = self): + self.ignore('stoppedAsleep') + place.setState('walk') + self.dialog.ignoreAll() + self.dialog.cleanup() + del self.dialog + + doneEvent = 'kickedOutDialog' + msg = TTLocalizer.StartingBlock_KickSoloRacer + self.dialog = TTGlobalDialog(msg, doneEvent, style=1) + self.dialog.accept(doneEvent, handleDialogOK) + self.accept('stoppedAsleep', handleDialogOK) + return + + def __avatarGone(self): + self.notify.debugStateCall(self) + self.setOccupied(0) + + def __placeAvatar(self): + self.notify.debugStateCall(self) + if not self.placedAvatar: + self.placedAvatar = 1 + self.av.setPosHpr(0, 0, 0, 0, 0, 0) + + def setMovie(self, mode): + self.notify.debugStateCall(self) + if self.avId == 0: + return + self.finishMovie() + if mode == 0: + pass + elif mode == KartGlobals.ENTER_MOVIE: + self.request('EnterMovie') + elif mode == KartGlobals.EXIT_MOVIE: + self.request('ExitMovie') + + def makeGui(self): + self.notify.debugStateCall(self) + if hasattr(self, 'cancelButton'): + return + fishGui = loader.loadModel('phase_4/models/gui/fishingGui') + self.cancelButton = DirectGui.DirectButton(relief=None, scale=0.67, pos=(1.16, 0, -0.9), text=('', TTLocalizer.FishingExit, TTLocalizer.FishingExit), text_align=TextNode.ACenter, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0.0, -0.12), textMayChange=0, text_scale=0.1, image=(fishGui.find('**/exit_buttonUp'), fishGui.find('**/exit_buttonDown'), fishGui.find('**/exit_buttonRollover')), text_font=ToontownGlobals.getInterfaceFont(), command=self.d_requestExit) + self.cancelButton.hide() + return + + def showGui(self): + self.notify.debugStateCall(self) + if hasattr(self.kartPad, 'state'): + if not self.kartPad.state == 'WaitCountdown': + return + self.cancelButton.show() + + def hideGui(self): + self.notify.debugStateCall(self) + if not hasattr(self, 'cancelButton'): + return + self.cancelButton.hide() + + def generateToonMoveTrack(self): + hpr = self.movieNode.getHpr(render) + heading = PythonUtil.fitDestAngle2Src(self.av.getH(render), hpr[0]) + hpr.setX(heading) + self.av.setAnimState('run', 1.0) + toonTrack = Sequence(Wait(0.5), Parallel(LerpPosInterval(self.av, 1.0, Point3(self.movieNode.getX(self.avParent), self.movieNode.getY(self.avParent), 0)), LerpHprInterval(self.av, 1.0, hpr=hpr, other=render)), Func(self.av.loop, 'neutral')) + return toonTrack + + def generateKartAppearTrack(self): + if not self.av: + if not self.kartNode: + self.kartNode = render.attachNewNode(str(self) + 'kartNode') + self.kartNode.setPosHpr(self.nodePath.getPos(render), self.nodePath.getHpr(render)) + self.kart.setScale(0.85) + self.kart.reparentTo(self.kartNode) + return Parallel() + self.kart.setScale(0.1) + kartTrack = Parallel( + Sequence( + ActorInterval(self.av, 'feedPet'), + Func(self.av.loop, 'neutral')), + Sequence( + Func(self.kart.setActiveShadow, False), + Func(self.kart.reparentTo, self.av.rightHand), + Wait(2.1), + Func(self.kart.wrtReparentTo, render), + Func(self.kart.setShear, 0, 0, 0), + Parallel( + LerpHprInterval(self.kart, hpr=self.kartNode.getHpr(render), duration=1.2), + ProjectileInterval(self.kart, endPos=self.kartNode.getPos(render), duration=1.2, gravityMult=0.45)), + Wait(0.2), + Func(self.kart.setActiveShadow, True), + Sequence( + LerpScaleInterval(self.kart, scale=Point3(1.1, 1.1, 0.1), duration=0.2), + LerpScaleInterval(self.kart, scale=Point3(0.9, 0.9, 0.1), duration=0.1), + LerpScaleInterval(self.kart, scale=Point3(1.0, 1.0, 0.1), duration=0.1), + LerpScaleInterval(self.kart, scale=Point3(1.0, 1.0, 1.1), duration=0.2), + LerpScaleInterval(self.kart, scale=Point3(1.0, 1.0, 0.9), duration=0.1), + LerpScaleInterval(self.kart, scale=Point3(1.0, 1.0, 1.0), duration=0.1), + Func(self.kart.wrtReparentTo, self.kartNode)))) + return kartTrack + + def generateToonJumpTrack(self): + base.sb = self + + def getToonJumpTrack(av, kart): + + def getJumpDest(av = av, node = kart.toonNode[0]): + dest = node.getPos(av.getParent()) + return dest + + def getJumpHpr(av = av, node = kart.toonNode[0]): + hpr = node.getHpr(av.getParent()) + return hpr + + toonJumpTrack = Parallel( + ActorInterval(av, 'jump'), + Sequence(Wait(0.43), + Parallel( + LerpHprInterval(av, hpr=getJumpHpr, duration=0.9), + ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) + return toonJumpTrack + + def getToonSitTrack(av): + toonSitTrack = Sequence(ActorInterval(av, 'sit-start'), Func(av.loop, 'sit')) + return toonSitTrack + + toonJumpTrack = getToonJumpTrack(self.av, self.kart) + toonSitTrack = getToonSitTrack(self.av) + jumpTrack = Sequence( + Parallel( + toonJumpTrack, + Sequence( + Wait(1), + toonSitTrack)), + Func(self.av.setPosHpr, 0, 0.45, -.25, 0, 0, 0), + Func(self.av.reparentTo, self.kart.toonSeat)) + return jumpTrack + + def generateToonReverseJumpTrack(self): + + def getToonJumpTrack(av, destNode): + + def getJumpDest(av = av, node = destNode): + dest = node.getPos(av.getParent()) + return dest + + def getJumpHpr(av = av, node = destNode): + hpr = node.getHpr(av.getParent()) + return hpr + + toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.1), Parallel(LerpHprInterval(av, hpr=getJumpHpr, duration=0.9), ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) + return toonJumpTrack + + toonJumpTrack = getToonJumpTrack(self.av, self.exitMovieNode) + jumpTrack = Sequence(toonJumpTrack, Func(self.av.loop, 'neutral'), Func(self.av.reparentTo, render), Func(self.av.setPosHpr, self.exitMovieNode, 0, 0, 0, 0, 0, 0)) + return jumpTrack + + def generateCameraMoveTrack(self): + self.cPos = camera.getPos(self.av) + self.cHpr = camera.getHpr(self.av) + camera.wrtReparentTo(self.nodePath) + cameraTrack = LerpPosHprInterval(camera, 1.5, self.cameraPos, self.cameraHpr) + return cameraTrack + + def generateCameraReturnMoveTrack(self): + cameraTrack = Sequence(Func(camera.wrtReparentTo, self.av), LerpPosHprInterval(camera, 1.5, self.cPos, self.cHpr)) + return cameraTrack + + def generateKartDisappearTrack(self): + + def getHoleTrack(hole, holeParent): + holeTrack = Sequence( + Wait(0.2), + Func(hole.setBin, 'shadow', 0), + Func(hole.setDepthTest, 0), + Func(hole.setDepthWrite, 0), + Func(hole.reparentTo, holeParent), + Func(hole.setPos, holeParent, Point3(0, 0.0, -.6)), + ActorInterval(hole, 'hole', startTime=3.4, endTime=3.1), + Wait(0.4), + ActorInterval(hole, 'hole', startTime=3.1, endTime=3.4)) + return holeTrack + + def getKartShrinkTrack(kart): + pos = kart.getPos() + pos.addZ(-1.0) + kartTrack = Sequence(LerpScaleInterval(kart, scale=Point3(1.0, 1.0, 0.9), duration=0.1), LerpScaleInterval(kart, scale=Point3(1.0, 1.0, 1.1), duration=0.1), LerpScaleInterval(kart, scale=Point3(1.0, 1.0, 0.1), duration=0.2), LerpScaleInterval(kart, scale=Point3(0.9, 0.9, 0.1), duration=0.1), LerpScaleInterval(kart, scale=Point3(1.1, 1.1, 0.1), duration=0.1), LerpScaleInterval(kart, scale=Point3(0.1, 0.1, 0.1), duration=0.2), Wait(0.2), LerpPosInterval(kart, pos=pos, duration=0.2), Func(kart.hide)) + return kartTrack + + if not self.holeActor: + self.holeActor = Actor.Actor('phase_3.5/models/props/portal-mod', {'hole': 'phase_3.5/models/props/portal-chan'}) + holeTrack = getHoleTrack(self.holeActor, self.kartNode) + shrinkTrack = getKartShrinkTrack(self.kart) + kartTrack = Parallel(shrinkTrack, holeTrack) + return kartTrack + + def enterOff(self): + self.notify.debug('%d enterOff: Entering the Off State.' % self.doId) + self.hideGui() + + def exitOff(self): + self.notify.debug('%d exitOff: Exiting the Off State.' % self.doId) + + def enterEnterMovie(self): + self.notify.debug('%d enterEnterMovie: Entering the Enter Movie State.' % self.doId) + if base.config.GetBool('want-qa-regression', 0): + raceName = TTLocalizer.KartRace_RaceNames[self.kartPad.trackType] + self.notify.info('QA-REGRESSION: KARTING: %s' % raceName) + toonTrack = self.generateToonMoveTrack() + kartTrack = self.generateKartAppearTrack() + jumpTrack = self.generateToonJumpTrack() + name = self.av.uniqueName('EnterRaceTrack') + if self.av is not None and self.localToonKarting: + kartAppearSfx = base.loadSfx(self.SFX_KartAppear) + cameraTrack = self.generateCameraMoveTrack() + engineStartTrack = self.kart.generateEngineStartTrack() + self.finishMovie() + self.movieTrack = Sequence(Parallel(cameraTrack, toonTrack), Parallel(SoundInterval(kartAppearSfx), Sequence(kartTrack, jumpTrack, engineStartTrack, Func(self.makeGui), Func(self.showGui), Func(self.request, 'Waiting'), Func(self.d_movieFinished))), name=name, autoFinish=1) + self.exitRequested = False + else: + self.finishMovie() + self.movieTrack = Sequence(toonTrack, kartTrack, jumpTrack, name=name, autoFinish=1) + self.movieTrack.start() + return + + def exitEnterMovie(self): + self.notify.debug('%d exitEnterMovie: Exiting the Enter Movie State.' % self.doId) + + def enterWaiting(self): + self.notify.debug('%d enterWaiting: Entering the Waiting State.' % self.doId) + + def exitWaiting(self): + self.notify.debug('%d exitWaiting: Exiting the Waiting State.' % self.doId) + + def enterExitMovie(self): + self.notify.debug('%d enterExitMovie: Entering the Exit Movie State.' % self.doId) + self.hideGui() + jumpTrack = self.generateToonReverseJumpTrack() + kartTrack = self.generateKartDisappearTrack() + self.finishMovie() + self.movieTrack = Sequence(Func(self.kart.kartLoopSfx.stop), jumpTrack, kartTrack, name=self.av.uniqueName('ExitRaceTrack'), autoFinish=1) + if self.av is not None and self.localToonKarting: + cameraTrack = self.generateCameraReturnMoveTrack() + self.movieTrack.append(cameraTrack) + self.movieTrack.append(Func(self.d_movieFinished)) + self.movieTrack.start() + return + + def exitExitMovie(self): + self.notify.debug('%d exitExitMovie: Exiting the Exit Movie State.' % self.doId) + + def doExitToRaceTrack(self): + self.hideGui() + self.finishMovie() + oldBlockPos = self.kartNode.getPos(render) + self.kartNode.setPos(self.kartNode, 0, 40, 0) + newBlockPos = self.kartNode.getPos(render) + oldBlockScale = self.kartNode.getScale() + self.kart.LODnode.setSwitch(0, 60, 0) + self.kartNode.setPos(render, oldBlockPos) + blockLerpIval = LerpPosInterval(self.kartNode, pos=newBlockPos, duration=2.0) + scaleLerpIval = LerpScaleInterval(self.kartNode, scale=oldBlockScale * 0.2, duration=2.0) + engineStopTrack = self.kart.generateEngineStopTrack(2) + self.finishMovie() + self.movieTrack = Parallel() + if self.av == base.localAvatar: + self.movieTrack.insert(0, Func(base.transitions.irisOut, 1.5, 0)) + (self.movieTrack.append(engineStopTrack),) + taskMgr.doMethodLater(1.6, self.bulkLoad, 'loadIt', extraArgs=[]) + self.movieTrack.append(Sequence(Parallel(blockLerpIval, scaleLerpIval), Func(self.kartNode.hide), Func(self.kartNode.setPos, render, oldBlockPos), Func(self.kartNode.setScale, oldBlockScale))) + self.movieTrack.start() + + def bulkLoad(self): + zoneId = 0 #TODO: get zoneId for certain tracks + base.loader.beginBulkLoad('atRace', TTLocalizer.StartingBlock_Loading, 60, 1, TTLocalizer.TIP_KARTING, zoneId) + + +class DistributedViewingBlock(DistributedStartingBlock): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedViewingBlock') + sphereRadius = 6 + + def __init__(self, cr): + DistributedStartingBlock.__init__(self, cr) + self.timer = None + return + + def delete(self): + if self.timer is not None: + self.timer.destroy() + del self.timer + DistributedStartingBlock.delete(self) + return + + def generateInit(self): + self.notify.debugStateCall(self) + DistributedObject.DistributedObject.generateInit(self) + self.nodePath = NodePath(self.uniqueName('StartingBlock')) + self.collSphere = CollisionSphere(-1, 6.75, -1, self.sphereRadius) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.uniqueName('StartingBlockSphere')) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.nodePath.attachNewNode(self.collNode) + + def announceGenerate(self): + self.notify.debugStateCall(self) + DistributedObject.DistributedObject.announceGenerate(self) + self.nodePath.reparentTo(render) + self.accept(self.uniqueName('enterStartingBlockSphere'), self.__handleEnterSphere) + if (__debug__): + if self.testLOD: + self.__generateKartAppearTrack() + + def setPadLocationId(self, padLocationId): + self.notify.debugStateCall(self) + self.movieNode = self.nodePath.attachNewNode(self.uniqueName('MovieNode')) + self.exitMovieNode = self.nodePath.attachNewNode(self.uniqueName('ExitMovieNode')) + if padLocationId % 2: + self.movieNode.setPosHpr(0, 6.5, 0, 0, 0, 0) + else: + self.movieNode.setPosHpr(0, -6.5, 0, 0, 0, 0) + self.exitMovieNode.setPosHpr(3, 6.5, 0, 270, 0, 0) + self.collNodePath.reparentTo(self.movieNode) + + def __handleEnterSphere(self, collEntry): + if base.localAvatar.doId == self.lastAvId and globalClock.getFrameCount() <= self.lastFrame + 1: + self.notify.debug('Ignoring duplicate entry for avatar.') + return + if base.localAvatar.hp > 0: + + def handleEnterRequest(self = self): + self.ignore('stoppedAsleep') + if hasattr(self.dialog, 'doneStatus') and self.dialog.doneStatus == 'ok': + self.d_requestEnter() + else: + self.cr.playGame.getPlace().setState('walk') + self.dialog.ignoreAll() + self.dialog.cleanup() + del self.dialog + + self.cr.playGame.getPlace().fsm.request('stopped') + self.accept('stoppedAsleep', handleEnterRequest) + doneEvent = 'enterRequest|dialog' + msg = TTLocalizer.StartingBlock_EnterShowPad + self.dialog = TTGlobalDialog(msg, doneEvent, 4) + self.dialog.accept(doneEvent, handleEnterRequest) + + def generateCameraMoveTrack(self): + self.cPos = camera.getPos(self.av) + self.cHpr = camera.getHpr(self.av) + cameraPos = Point3(23, -10, 7) + cameraHpr = Point3(65, -10, 0) + camera.wrtReparentTo(self.nodePath) + cameraTrack = LerpPosHprInterval(camera, 1.5, cameraPos, cameraHpr) + return cameraTrack + + def makeGui(self): + self.notify.debugStateCall(self) + if self.timer is not None: + return + self.timer = ToontownTimer() + self.timer.setScale(0.3) + self.timer.setPos(1.16, 0, -.73) + self.timer.hide() + DistributedStartingBlock.makeGui(self) + return + + def showGui(self): + self.notify.debugStateCall(self) + self.timer.show() + DistributedStartingBlock.showGui(self) + + def hideGui(self): + self.notify.debugStateCall(self) + if not hasattr(self, 'timer') or self.timer is None: + return + self.timer.reset() + self.timer.hide() + DistributedStartingBlock.hideGui(self) + return + + def countdown(self): + countdownTime = KartGlobals.COUNTDOWN_TIME - globalClockDelta.localElapsedTime(self.kartPad.getTimestamp(self.avId)) + self.timer.countdown(countdownTime) + + def enterEnterMovie(self): + self.notify.debug('%d enterEnterMovie: Entering the Enter Movie State.' % self.doId) + if base.config.GetBool('want-qa-regression', 0): + raceName = TTLocalizer.KartRace_RaceNames[self.kartPad.trackType] + self.notify.info('QA-REGRESSION: KARTING: %s' % raceName) + pos = self.nodePath.getPos(render) + hpr = self.nodePath.getHpr(render) + pos.addZ(1.7) + hpr.addX(270) + self.kartNode.setPosHpr(pos, hpr) + toonTrack = self.generateToonMoveTrack() + kartTrack = self.generateKartAppearTrack() + jumpTrack = self.generateToonJumpTrack() + name = self.av.uniqueName('EnterRaceTrack') + if self.av is not None and self.localToonKarting: + cameraTrack = self.generateCameraMoveTrack() + self.finishMovie() + self.movieTrack = Sequence(Parallel(cameraTrack, Sequence()), kartTrack, jumpTrack, Func(self.makeGui), Func(self.showGui), Func(self.countdown), Func(self.request, 'Waiting'), Func(self.d_movieFinished), name=name, autoFinish=1) + else: + self.finishMovie() + self.movieTrack = Sequence(toonTrack, kartTrack, jumpTrack, name=name, autoFinish=1) + self.movieTrack.start() + self.exitRequested = True + return diff --git a/toontown/racing/DistributedStartingBlockAI.py b/toontown/racing/DistributedStartingBlockAI.py new file mode 100755 index 00000000..30e8fdba --- /dev/null +++ b/toontown/racing/DistributedStartingBlockAI.py @@ -0,0 +1,124 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.racing.KartShopGlobals import KartGlobals +from toontown.racing import RaceGlobals + +class DistributedStartingBlockAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedStartingBlockAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.air = air + self.pad = None + self.currentMovie = False + self.avId = 0 + self.posHpr = [0, 0, 0, 0, 0, 0] + + def setPadDoId(self, padDoId): + self.pad = self.air.doId2do[padDoId] + + def getPadDoId(self): + return self.pad.getDoId() + + def setPosHpr(self, x, y, z, h, p, r): + self.posHpr = [x, y, z, h, p, r] + + def getPosHpr(self): + return self.posHpr + + def setPadLocationId(self, padLocationId): + self.padLocationId = padLocationId + + def getPadLocationId(self): + return self.padLocationId + + def requestEnter(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if not av: + return + if not av.hasKart(): + self.sendUpdateToAvatarId(avId, 'rejectEnter', [KartGlobals.ERROR_CODE.eNoKart]) + return + if av.getTickets() < RaceGlobals.getEntryFee(self.pad.trackId, self.pad.trackType): + self.sendUpdateToAvatarId(avId, 'rejectEnter', [KartGlobals.ERROR_CODE.eTickets]) + return + if self.pad.state == 'AllAboard' or self.pad.state == 'WaitBoarding' : + self.sendUpdateToAvatarId(avId, 'rejectEnter', [KartGlobals.ERROR_CODE.eBoardOver]) + return + if self.avId != 0: + if self.avId == avId: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to board the same starting block twice!') + self.sendUpdateToAvatarId(avId, 'rejectEnter', [KartGlobals.ERROR_CODE.eOccupied]) + return + self.b_setOccupied(avId) + self.b_setMovie(KartGlobals.ENTER_MOVIE) + + def rejectEnter(self, errCode): + pass + + def requestExit(self): + avId = self.air.getAvatarIdFromSender() + if avId != self.avId: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to get off a starting block they\'re not on!') + self.b_setMovie(KartGlobals.EXIT_MOVIE) + + def setOccupied(self, avId): + self.avId = avId + self.pad.updateTimer() + + def d_setOccupied(self, avId): + self.sendUpdate('setOccupied', [avId]) + + def b_setOccupied(self, avId): + self.setOccupied(avId) + self.d_setOccupied(avId) + + def setMovie(self, movie): + self.currentMovie = movie + self.pad.updateMovieState() + + def d_setMovie(self, movie): + self.sendUpdate('setMovie', [movie]) + + def b_setMovie(self, movie): + self.setMovie(movie) + self.d_setMovie(movie) + + def movieFinished(self): + avId = self.air.getAvatarIdFromSender() + if self.avId != avId: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to end movie of another toon!') + return + if not self.currentMovie: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to end non-existent movie!') + return + if self.currentMovie == KartGlobals.EXIT_MOVIE: + self.b_setOccupied(0) + self.b_setMovie(0) + +class DistributedViewingBlockAI(DistributedStartingBlockAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedViewingBlockAI") + + def __init__(self, air): + DistributedStartingBlockAI.__init__(self, air) + self.air = air + + def requestEnter(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do[avId] + if not av.hasKart(): + self.sendUpdateToAvatarId(avId, 'rejectEnter', [KartGlobals.ERROR_CODE.eNoKart]) + return + if self.avId != 0: + if self.avId == avId: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to board the same starting block twice!') + self.sendUpdateToAvatarId(avId, 'rejectEnter', [KartGlobals.ERROR_CODE.eOccupied]) + return + self.b_setOccupied(avId) + self.b_setMovie(KartGlobals.ENTER_MOVIE) + taskMgr.doMethodLater(30, DistributedViewingBlockAI.b_setMovie, 'removePlayer%i' % self.doId, [self, KartGlobals.EXIT_MOVIE]) + + def requestExit(self): + DistributedStartingBlockAI.requestExit(self) + taskMgr.remove('removePlayer%i' % self.doId) diff --git a/toontown/racing/DistributedVehicle.py b/toontown/racing/DistributedVehicle.py new file mode 100755 index 00000000..a3898c43 --- /dev/null +++ b/toontown/racing/DistributedVehicle.py @@ -0,0 +1,1224 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.gui.DirectGui import * +from direct.fsm import FSM +from direct.distributed import DistributedSmoothNode +from direct.interval.IntervalGlobal import * +from toontown.toonbase.PythonUtil import clampScalar +from otp.otpbase import OTPGlobals +from otp.avatar import ShadowCaster +from toontown.racing import Kart +from toontown.racing.KartDNA import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.effects.Drift import Drift +from toontown.effects.Sparks import Sparks +from direct.interval.ProjectileInterval import * +from toontown.battle.BattleProps import * +import random +from direct.showbase.PythonUtil import randFloat +from direct.task.Task import Task +from otp.nametag import NametagGlobals +import math +iceTurnFactor = 0.25 +iceAccelFactor = 0.4 + +class DistributedVehicle(DistributedSmoothNode.DistributedSmoothNode, Kart.Kart, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedVehicle') + cheatFactor = 1.0 + proRacer = 0 + physicsCalculationsPerSecond = 60 + maxPhysicsDt = 1.0 + physicsDt = 1.0 / float(physicsCalculationsPerSecond) + maxPhysicsFrames = maxPhysicsDt * physicsCalculationsPerSecond + maxSpeed = 200 * cheatFactor + turnRatio = 1.0 / 0.025 * math.sqrt(cheatFactor) + accelerationMult = 35 + accelerationBase = 20 + if proRacer: + accelerationMult = 35 + accelerationBase = 30 + surfaceModifiers = {'asphalt': {'shake': 0.1, + 'driftMin': 65, + 'windResistance': 0.2, + 'particleColor': Vec4(0.7, 0.7, 0.7, 1.0)}, + 'gravel': {'shake': 0.2, + 'driftMin': 65, + 'windResistance': 0.2, + 'particleColor': Vec4(0.53, 0.53, 0.53, 1.0)}, + 'dirt': {'shake': 0.4, + 'driftMin': 35, + 'windResistance': 0.3, + 'particleColor': Vec4(1.0, 1.0, 1.0, 1.0)}, + 'grass': {'shake': 0.8, + 'driftMin': 15, + 'windResistance': 0.4, + 'particleColor': Vec4(0.8, 0.42, 0.8, 1.0)}, + 'ice': {'shake': 0.0, + 'driftMin': 0, + 'windResistance': 0.01, + 'particleColor': Vec4(1.0, 1.0, 1.0, 1.0)}, + '': {'shake': 0, + 'driftMin': 1, + 'windResistance': 0.2, + 'particleColor': Vec4(1.0, 1.0, 1.0, 1.0)}} + SFX_BaseDir = 'phase_6/audio/sfx/' + SFX_WallHits = [SFX_BaseDir + 'KART_Hitting_Wood_Fence.ogg', + SFX_BaseDir + 'KART_Hitting_Wood_Fence_1.ogg', + SFX_BaseDir + 'KART_Hitting_Metal_Fence.ogg', + SFX_BaseDir + 'KART_Hitting_Wall.ogg'] + SFX_SkidLoop_Grass = SFX_BaseDir + 'KART_Skidding_On_Grass.ogg' + SFX_SkidLoop_Asphalt = SFX_BaseDir + 'KART_Skidding_On_Asphalt.ogg' + SFX_TurboStart = SFX_BaseDir + 'KART_turboStart.ogg' + SFX_TurboLoop = SFX_BaseDir + 'KART_turboLoop.ogg' + AvId2kart = {} + + @classmethod + def getKartFromAvId(cls, avId): + return cls.AvId2kart.get(avId) + + def __init__(self, cr): + DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedVehicle') + Kart.Kart.__init__(self) + if base.config.GetBool('want-racer', 0) == 1: + DistributedVehicle.proRacer = 1 + DistributedVehicle.accelerationMult = 35 + DistributedVehicle.accelerationBase = 30 + self.speedometer = None + self.speedGauge = None + self.leanAmount = 0 + self.leftHeld = 0 + self.stopped = False + self.rightHeld = 0 + self.canRace = False + self.groundType = 'gravel' + self.offGround = 0 + self.turbo = False + self.ownerId = 0 + self.cameraTrack = None + self.curSpeed = 0 + self.acceleration = 0 + self.imHitMult = 1 + self.skidding = False + self.hittingWall = 0 + base.kart = self + self.wipeOut = None + self.gagMovie = None + self.spinAnim = None + self.wrongWay = False + self.wallCollideTrack = None + self.wheelMaxTurn = 1.0 + self.wheelMinTurn = 0.15 + self.speedMaxTurn = 90 + self.speedMinTurn = 300 + self.wheelTurnTime = 0.6 + self.wheelReturnTime = 0.3 + self.wheelFightTime = 0.2 + self.wheelPosition = 0.0 + self.outPutCounter = 0 + self.proCameraHeading = 0 + self.proCameraDummyNode = None + self.cameraArmNode = None + self.armSwingSpeedPerp = 0.2 + self.armSwingSpeedPara = 0.7 + self.cameraTightener = 3.0 + self.cameraArmBase = 0 + self.cameraArmExtend = 20 + self.pieCount = 0 + self.numPieChunks = 6 + self.pieSlideSpeed = [] + for piece in xrange(self.numPieChunks): + self.pieSlideSpeed.append(randFloat(0.0, 0.2)) + + self.wantSmoke = ConfigVariableBool('want-kart-smoke', 1).getValue() + self.wantSparks = ConfigVariableBool('want-kart-sparks', 1).getValue() + self.__loadTextures() + return + + def __loadTextures(self): + self.pieSplatter = loader.loadModel('phase_6/models/karting/pie_splat_1.bam') + + def announceGenerate(self): + DistributedSmoothNode.DistributedSmoothNode.announceGenerate(self) + self.generateKart() + self.name = self.uniqueName('vehicle') + self.posHprBroadcastName = self.uniqueName('vehicleBroadcast') + self.LODnode.forceSwitch(self.LODnode.getHighestSwitch()) + self.cameraNode = NodePath('cameraNode') + self.cameraNode.reparentTo(self) + if self.proRacer: + self.cameraArmNode = render.attachNewNode('cameraArm') + self.cameraArmNode.reparentTo(self) + self.cameraNode.reparentTo(self.cameraArmNode) + self.proCameraDummyNode = render.attachNewNode('proCameraDummy') + self.proCameraDummyNode.reparentTo(render) + self.localVehicle = False + if hasattr(base, 'localAvatar'): + self.localVehicle = self.ownerId == base.localAvatar.doId + if self.localVehicle: + self.setupPhysics() + self.setupParticles() + self.__enableCollisions() + self.forward = NodePath('forward') + self.forward.setPos(0, 1, 0) + self.wallHitsSfx = [] + for wallHit in self.SFX_WallHits: + self.wallHitsSfx.append(base.loadSfx(wallHit)) + + self.skidLoopAsphaltSfx = base.loadSfx(self.SFX_SkidLoop_Asphalt) + self.skidLoopAsphaltSfx.setLoop() + self.skidLoopGrassSfx = base.loadSfx(self.SFX_SkidLoop_Grass) + self.skidLoopGrassSfx.setLoop() + self.turboStartSfx = base.loadSfx(self.SFX_TurboStart) + self.turboLoopSfx = base.loadSfx(self.SFX_TurboLoop) + self.turboLoopSfx.setLoop() + self.forward.reparentTo(self.geom[0]) + self.anvil = globalPropPool.getProp('anvil') + self.anvil.setScale(5) + self.anvil.reparentTo(hidden) + self.anvil.setColor(1, 1, 1, 1) + self.anvil.setTransparency(1) + self.reparentTo(render) + + def setupPhysics(self): + self.__setupCollisions() + self.physicsMgr = PhysicsManager() + self.physicsEpoch = globalClock.getFrameTime() + self.lastPhysicsFrame = 0 + integrator = LinearEulerIntegrator() + self.physicsMgr.attachLinearIntegrator(integrator) + fn = ForceNode('windResistance') + fnp = NodePath(fn) + fnp.reparentTo(render) + windResistance = LinearFrictionForce(0.2) + fn.addForce(windResistance) + self.physicsMgr.addLinearForce(windResistance) + self.windResistance = windResistance + fn = ForceNode('engine') + fnp = NodePath(fn) + fnp.reparentTo(self) + engine = LinearVectorForce(0, 0, 0) + fn.addForce(engine) + self.physicsMgr.addLinearForce(engine) + self.engine = engine + + def disable(self): + if self.ownerId not in DistributedVehicle.AvId2kart: + return + + DistributedVehicle.AvId2kart.pop(self.ownerId) + self.finishMovies() + self.request('Off') + self.stopSkid() + self.stopSmooth() + if self.localVehicle: + self.__disableCollisions() + self.__undoCollisions() + self.physicsMgr.clearLinearForces() + self.detachNode() + DistributedSmoothNode.DistributedSmoothNode.disable(self) + taskMgr.remove('slidePies') + + def delete(self): + self.stopSmooth() + Kart.Kart.delete(self) + DistributedSmoothNode.DistributedSmoothNode.delete(self) + if self.localVehicle: + self.ignoreAll() + if hasattr(self, 'piePieces'): + for piece in self.piePieces: + del piece + + def getVelocity(self): + return self.actorNode.getPhysicsObject().getVelocity() + + def __updateWallCollision(self, entry = None): + vol = self.curSpeed / 160 + for wallHit in self.wallHitsSfx: + wallHit.setVolume(vol) + + def __wallCollisionStart(self, entry): + self.entry = entry + self.__wallCollisionStop() + self.hittingWall = 1 + hitToPlay = random.randrange(0, len(self.wallHitsSfx)) + self.wallCollideTrack = Parallel(Func(self.wallHitsSfx[hitToPlay].play)) + self.__updateWallCollision() + self.wallCollideTrack.start() + curSpeed = self.actorNode.getPhysicsObject().getVelocity().length() + if self.wantSparks and curSpeed > 10: + if entry.getSurfaceNormal(self)[0] < 0: + self.fireSparkParticles('right') + else: + self.fireSparkParticles('left') + + def __wallCollisionStop(self, entry = None): + self.hittingWall = 0 + if self.wallCollideTrack: + self.wallCollideTrack.pause() + + def __setupCollisions(self): + self.cWallTrav = CollisionTraverser('KartWall') + self.cWallTrav.setRespectPrevTransform(True) + self.collisionNode = CollisionNode(self.uniqueName('vehicle')) + self.collisionNode.setFromCollideMask(OTPGlobals.WallBitmask) + self.collisionNode.setIntoCollideMask(ToontownGlobals.PieBitmask) + cs = CollisionSphere(0, 0, 4, 4) + self.collisionNode.addSolid(cs) + self.collisionNodePath = NodePath(self.collisionNode) + self.wallHandler = PhysicsCollisionHandler() + self.wallHandler.setStaticFrictionCoef(0.0) + self.wallHandler.setDynamicFrictionCoef(0.1) + self.wallHandler.addCollider(self.collisionNodePath, self) + self.wallHandler.setInPattern('enterWallCollision') + self.wallHandler.setOutPattern('exitWallCollision') + self.cWallTrav.addCollider(self.collisionNodePath, self.wallHandler) + self.accept('enterWallCollision', self.__wallCollisionStart) + self.accept('exitWallCollision', self.__wallCollisionStop) + cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0) + cRayNode = CollisionNode(self.uniqueName('vehicle-FloorRay')) + cRayNode.addSolid(cRay) + cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + cRayNode.setIntoCollideMask(BitMask32.allOff()) + self.cRayNodePath = self.attachNewNode(cRayNode) + self.lifter = CollisionHandlerGravity() + self.lifter.setGravity(32.174 * 3.0) + self.lifter.addInPattern('floorCollision') + self.lifter.addAgainPattern('floorCollision') + self.lifter.setOffset(OTPGlobals.FloorOffset) + self.lifter.setReach(40.0) + self.lifter.addCollider(self.cRayNodePath, self) + base.cTrav.addCollider(self.cRayNodePath, self.lifter) + self.accept('floorCollision', self.__groundCollision) + self.accept('imIn-banana', self.hitBanana) + base.localAvatar.collisionsOff() + + def __undoCollisions(self): + base.cTrav.removeCollider(self.cRayNodePath) + base.localAvatar.collisionsOn() + + def __groundCollision(self, entry): + ground = entry.getIntoNodePath() + if ground.getNetTag('surface') == '': + return + self.groundType = ground.getNetTag('surface') + + def __enableCollisions(self): + self.cQueue = [] + self.cRays = NodePath('stickVehicleToFloor') + self.cRays.reparentTo(self.geom[0]) + for wheelNode in self.wheelBases: + rayNode = CollisionNode(wheelNode.getName()) + x = wheelNode.getX() + y = wheelNode.getY() + ray = CollisionRay(x, y, 40000.0, 0.0, 0.0, -1.0) + rayNode.addSolid(ray) + rayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + rayNode.setIntoCollideMask(BitMask32.allOff()) + rayNodePath = self.cRays.attachNewNode(rayNode) + cQueue = CollisionHandlerQueue() + self.cWallTrav.addCollider(rayNodePath, cQueue) + self.cQueue.append(cQueue) + + self.collisionNodePath.reparentTo(self) + + def setupLapCollisions(self): + self.lapBit = BitMask32(32768) + self.lapHandler = CollisionHandlerEvent() + self.lapHandler.addInPattern('imIn-%in') + self.cSphere = CollisionSphere(0, 0, 0, 3) + self.lapNode = CollisionNode('lapChecker') + self.lapNode.addSolid(self.cSphere) + self.lapNode.setIntoCollideMask(BitMask32.allOff()) + self.lapNode.setFromCollideMask(self.lapBit) + self.lapNodePath = NodePath(self.lapNode) + self.lapNodePath.reparentTo(self) + self.cWallTrav.addCollider(self.lapNodePath, self.lapHandler) + + def __disableCollisions(self): + self.ignore('imIn-startLine') + self.ignore('imIn-quarterLine') + self.ignore('imIn-midLine') + + def __handleCollisionSphereEnter(self, collEntry = None): + self.d_requestControl() + + def setupParticles(self): + if self.wantSmoke: + self.setupDriftParticles() + if self.wantSparks: + self.setupSparkParticles() + + def updateParticles(self, leanAmount): + if self.wantSmoke: + self.updateDriftParticles(leanAmount) + + def cleanupParticles(self): + if self.wantSmoke: + self.cleanupDriftParticles() + if self.wantSparks: + self.cleanupSparkParticles() + + def setupDriftParticles(self): + smokeMount = self.geom[0].attachNewNode('Smoke Effect') + backLeft = smokeMount.attachNewNode('Back Left Smokemount') + backLeft.setPos(self.geom[0].find('**/' + self.wheelData[self.LRWHEEL]['node']).getPos() + Vec3(-0.25, -1.0, -0.5)) + backRight = smokeMount.attachNewNode('Back Right Smokemount') + backRight.setPos(self.geom[0].find('**/' + self.wheelData[self.RRWHEEL]['node']).getPos() + Vec3(0.25, -1.0, -0.5)) + driftEffects = (Drift(backLeft, backLeft), Drift(backRight, backRight)) + for x in driftEffects: + x.start() + + self.drifts = driftEffects + self.driftSeq = Sequence(Func(backLeft.show), Func(backRight.show), Wait(1000000), Func(self.drifts[0].effect.getParticlesNamed('particles-1').getRenderer().setColor, Vec4(1.0, 1.0, 1.0, 1.0)), Func(self.drifts[1].effect.getParticlesNamed('particles-1').getRenderer().setColor, Vec4(1.0, 1.0, 1.0, 1.0)), Func(backLeft.hide), Func(backRight.hide)) + self.driftSeqStarted = False + self.driftParticleForces = [ x.effect.getParticlesNamed('particles-1').getLinearForce(0) for x in self.drifts ] + self.smokeMount = smokeMount + backLeft.hide() + backRight.hide() + + def updateDriftParticles(self, leanAmount): + for x in self.driftParticleForces: + x.setAmplitude(leanAmount * 30) + + if self.skidding and not self.driftSeqStarted: + self.driftSeqStarted = True + self.driftSeq.start() + elif not self.skidding and self.driftSeqStarted: + self.driftSeqStarted = False + self.driftSeq.finish() + + def cleanupDriftParticles(self): + self.driftSeq.finish() + for x in self.drifts: + x.destroy() + + self.smokeMount.removeNode() + del self.driftSeq + del self.driftParticleForces + del self.drifts + del self.smokeMount + + def setupSparkParticles(self): + bodyType = self.kartDNA[KartDNA.bodyType] + endPts = KartDict[bodyType][7] + self.sparkMount = self.geom[0].attachNewNode('Spark Effect') + left = self.sparkMount.attachNewNode('Left Sparkmount') + left.setPos((self.geom[0].find('**/' + self.wheelData[self.LFWHEEL]['node']).getPos() + self.geom[0].find('**/' + self.wheelData[self.LRWHEEL]['node']).getPos()) / 2.0 + Vec3(0.0, -1.0, 1.0)) + left.setScale(0.5) + right = self.sparkMount.attachNewNode('Right Sparkmount') + right.setPos((self.geom[0].find('**/' + self.wheelData[self.RFWHEEL]['node']).getPos() + self.geom[0].find('**/' + self.wheelData[self.RRWHEEL]['node']).getPos()) / 2.0 + Vec3(0.0, -1.0, 1.0)) + right.setScale(0.5) + self.sparks = (Sparks(right, self.sparkMount), Sparks(left, self.sparkMount)) + self.sparks[0].effect.getParticlesNamed('particles-1').getEmitter().setEndpoint1(endPts[0]) + self.sparks[0].effect.getParticlesNamed('particles-1').getEmitter().setEndpoint2(endPts[1]) + self.sparks[0].effect.getParticlesNamed('particles-1').getLinearForce(0).setAmplitude(-50) + self.sparks[1].effect.getParticlesNamed('particles-1').getEmitter().setEndpoint1(Point3(-endPts[0][0], endPts[0][1], endPts[0][2])) + self.sparks[1].effect.getParticlesNamed('particles-1').getEmitter().setEndpoint2(Point3(-endPts[1][0], endPts[1][1], endPts[1][2])) + self.sparks[1].effect.getParticlesNamed('particles-1').getLinearForce(0).setAmplitude(50) + + def fireSparkParticles(self, side): + spark = self.sparks[{'right': 0, + 'left': 1}[side]] + if not spark.effect.isEnabled(): + spark.effect.getParticlesNamed('particles-1').setBirthRate(0.02) + spark.start() + taskMgr.doMethodLater(0.25, self.stopSparkParticles, 'sparkTimer-' + side, extraArgs=[side]) + + def stopSparkParticles(self, side = None): + sides = {0: 'right', + 1: 'left'} + if side == None: + for x in sides.keys(): + self.sparks[x].effect.getParticlesNamed('particles-1').setBirthRate(1000) + taskMgr.doMethodLater(0.75, self.sparks[x].stop, 'stopSparks-' + sides[x], extraArgs=[]) + + else: + spark = self.sparks[{'right': 0, + 'left': 1}[side]] + spark.effect.getParticlesNamed('particles-1').setBirthRate(1000) + taskMgr.doMethodLater(0.75, spark.stop, 'stopSparks-' + side, extraArgs=[]) + return + + def cleanupSparkParticles(self): + taskMgr.remove('sparkTimer-left') + taskMgr.remove('sparkTimer-right') + taskMgr.remove('stopSparks-left') + taskMgr.remove('stopSparks-right') + for x in self.sparks: + x.destroy() + + self.sparkMount.removeNode() + del self.sparks + del self.sparkMount + + def setState(self, state, avId): + self.notify.debug('SetState received: %s avId: %s' % (state, avId)) + if state == 'C': + self.demand('Controlled', avId) + elif state == 'P': + self.demand('Parked') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def d_requestControl(self): + self.sendUpdate('requestControl') + + def d_requestParked(self): + self.sendUpdate('requestParked') + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterParked(self): + pass + + def exitParked(self): + pass + + def enterControlled(self, avId): + self.avId = avId + self.toon = base.cr.doId2do.get(avId, None) + if self.toon: + self.toon.stopSmooth() + self.toon.stopPosHprBroadcast() + for feet in self.toon.getPieces(('legs', 'feet')): + feet.hide() + + self.toon.reparentTo(self.toonSeat) + self.toon.dropShadow.hide() + self.notify.debug('setting pos of toon%s' % self.toon.doId) + self.toon.setPosHpr(0, 0, 0, 0, 0, 0) + self.toon.loop('sit') + if self.toon.doId == localAvatar.doId: + self.notify.debug('calling send currentPosition') + self.toon.sendCurrentPosition() + self.doHeadScale(self.toon, 1.75) + self.toon.setShear(0, 0, 0) + NametagGlobals.setOnscreenChatForced(1) + if self.localVehicle: + camera.reparentTo(self.cameraNode) + camera.setPosHpr(0, -33, 16, 0, -10, 0) + self.physicsMgr.attachPhysicalNode(self.node()) + self.__enableControlInterface() + self.__createPieWindshield() + self.startPosHprBroadcast() + self.engineSfxTrack = self.generateEngineStartTrack() + self.engineSfxTrack.start() + else: + self.startSmooth() + taskName = 'updateNonlocalVehicle-%s' % avId + self.updateNonLocalTask = taskMgr.add(self.__updateNonlocalVehicle, taskName) + return + + def exitControlled(self): + if self.localVehicle: + self.stopPosHprBroadcast() + self.__disableControlInterface() + self.physicsMgr.removePhysicalNode(self.node()) + self.cleanupParticles() + camera.reparentTo(localAvatar) + camera.setPos(localAvatar.cameraPositions[0][0]) + camera.setHpr(0, 0, 0) + self.engineSfxTrack.finish() + self.engineSfxTrack = self.generateEngineStopTrack() + else: + self.stopSmooth() + taskMgr.remove(self.updateNonLocalTask) + if self.toon and not self.toon.isDisabled() and not self.toon.isEmpty(): + self.toon.dropShadow.show() + self.doHeadScale(self.toon, None) + self.toon.setPosHpr(self.geom[0], 0, 8, 0, 0, 0, 0) + for feet in self.toon.getPieces(('legs', 'feet')): + feet.show() + + self.toon.reparentTo(render) + self.toon.loop('neutral') + self.toon.startSmooth() + NametagGlobals.setOnscreenChatForced(0) + base.camLens.setMinFov(settings['fov']/(4./3.)) + + def doHeadScale(self, model, scale): + if scale == None: + scale = ToontownGlobals.toonHeadScales[model.style.getAnimal()] + base.localAvatar.clearCheesyEffect() + for hi in xrange(model.headParts.getNumPaths()): + head = model.headParts[hi] + head.setScale(scale) + + return + + def __createPieWindshield(self): + self.piePieces = [] + for piece in xrange(self.numPieChunks): + self.piePieces.append(DirectLabel(relief=None, pos=(0.0, 0.0, 0.0), image=self.pieSplatter, image_scale=(0.5, 0.5, 0.5), text=' ', text_scale=0.18, text_fg=(1, 0, 1, 1), text_pos=(-0.0, 0.0, 0), text_font=ToontownGlobals.getSignFont(), textMayChange=1)) + self.piePieces[piece].hide() + + return + + def showPieces(self): + xRange = 0.3 + for piece in self.piePieces: + piece.setPos(randFloat(-xRange, xRange), randFloat(-0.1, 3.5), randFloat(-0.9, 0.9)) + piece.setScale(randFloat(1.1, 2.0), 1, randFloat(1.1, 2.0)) + piece.show() + xRange += 2.5 / self.numPieChunks + + for piece in xrange(self.numPieChunks): + self.pieSlideSpeed[piece] = randFloat(0.0, 0.2) + + def splatPie(self): + self.showPieces() + if self.pieCount == 0: + self.pieCount = 15 + taskMgr.doMethodLater(1, self.__countPies, ' ', extraArgs=[]) + taskMgr.add(self.__slidePies, 'slidePies', priority=25) + else: + self.pieCount = 15 + + def __countPies(self): + if self.pieCount > 0: + taskMgr.doMethodLater(1, self.__countPies, ' ', extraArgs=[]) + self.pieCount -= 1 + else: + for piece in self.piePieces: + piece.hide() + + def __slidePies(self, optional = None): + dt = globalClock.getDt() + for piece in xrange(self.numPieChunks): + self.pieSlideSpeed[piece] += randFloat(0.0, 0.25 * dt) + pieSpeed = self.pieSlideSpeed[piece] * dt + self.piePieces[piece].setPos(self.piePieces[piece].getPos()[0], self.piePieces[piece].getPos()[1] - pieSpeed, self.piePieces[piece].getPos()[2] - pieSpeed) + + if self.pieCount == 0: + return Task.done + else: + return Task.cont + + def __enableControlInterface(self): + if self.canRace: + self.enableControls() + taskMgr.remove('watchVehicleControls') + taskMgr.add(self.__watchControls, 'watchVehicleControls', priority=25) + if not self.speedometer: + cm = CardMaker('speed') + cm.setFrame(-0.5, 0.5, -0.5, 0.5) + self.speedometerImages = base.a2dBottomRight.attachNewNode('SpeedometerImages') + self.speedometerImages.setTransparency(True) + self.speedometerImages.setPos(-0.1, 0.0, 0.03) + self.speedometerImages.setScale(0.75) + m = loader.loadModel('phase_6/models/karting/speedometer') + if self.getBodyColor() == InvalidEntry: + bodyColor = getDefaultColor() + else: + bodyColor = getAccessory(self.getBodyColor()) + if self.getAccessoryColor() == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.getAccessoryColor()) + self.speedometerImages.attachNewNode(m.find('**/*gauge').node()).setColorScale(1, 1, 1, 1) + self.speedGauge = self.speedometerImages.attachNewNode(m.find('**/*spin').node()) + self.speedGauge.setColor(1, 1, 1, 1) + c = (Vec4(1, 1, 1, 1) + accColor) / 2.0 + c.setW(1.0) + self.speedometerImages.attachNewNode(m.find('**/*face').node()).setColorScale(c) + c = (Vec4(2, 2, 2, 2) - accColor) / 2.0 + c = (bodyColor + Vec4(1, 1, 1, 1)) / 2.0 + c.setW(1.0) + self.speedometerImages.attachNewNode(m.find('**/*tics').node()).setColorScale(c) + c = (bodyColor + Vec4(1, 1, 1, 1)) / 2.0 + c.setW(1.0) + self.speedometerImages.attachNewNode(m.find('**/*ring').node()).setColorScale(c) + self.speedometer = DirectLabel(relief=None, pos=(-0.1, 0.0, 0.03), text=str(0), text_scale=0.18, text_fg=bodyColor, text_pos=(-0.04, 0.02, 0), text_font=ToontownGlobals.getSignFont()) + self.speedometer.reparentTo(base.a2dBottomRight) + else: + self.showSpeedometer() + self.arrowVert = 0 + self.arrowHorz = 0 + return + + def showSpeedometer(self): + if self.speedometer: + self.speedometer.show() + self.speedometerImages.show() + + def hideSpeedometer(self): + if self.speedometer: + self.speedometer.hide() + self.speedometerImages.hide() + + def __disableControlInterface(self): + self.hideSpeedometer() + self.disableControls() + self.stopSmooth() + taskMgr.remove('watchVehicleControls') + + def __deleteControlInterface(self): + if self.speedometer: + self.speedometer.destroy() + self.speedometer = None + self.speedGauge = None + self.speedometerImages.removeNode() + self.speedometerImages = None + return + + def setTurbo(self, setting): + self.turbo = setting + if self.turbo: + self.turboLoopSfx.play() + else: + self.turboLoopSfx.stop() + + def interruptTurbo(self): + self.__stopTurbo() + if self.cameraTrack: + self.cameraTrack.finish() + self.cameraTrack = None + return + + def startTurbo(self): + newCameraPos = Point3(0, -25, 16) + newCameraFov = 70 + turboDuration = 3 + startFov = settings['fov']/(4./3.) + if self.cameraTrack: + self.cameraTrack.pause() + cameraZoomIn = Parallel(LerpPosInterval(camera, 2, newCameraPos), LerpFunc(base.camLens.setMinFov, fromData=startFov, toData=newCameraFov, duration=2)) + cameraToNormal = Parallel(LerpPosInterval(camera, 1, Point3(0, -33, 16), newCameraPos), LerpFunc(base.camLens.setMinFov, fromData=newCameraFov, toData=startFov, duration=1)) + self.cameraTrack = Sequence(Func(self.turboStartSfx.play), cameraZoomIn, Func(lambda : self.setTurbo(True)), Wait(turboDuration), Func(self.__stopTurbo), cameraToNormal) + self.cameraTrack.start() + + def __stopTurbo(self, task = None): + self.setTurbo(False) + + def __controlPressed(self): + self.__throwGag() + + def __controlReleased(self): + pass + + def __upArrow(self, pressed): + if pressed: + self.arrowVert = 1 + elif self.arrowVert > 0: + self.arrowVert = 0 + + def __downArrow(self, pressed): + if pressed: + self.arrowVert = -1 + elif self.arrowVert < 0: + self.arrowVert = 0 + + def __rightArrow(self, pressed): + if pressed: + self.arrowHorz = 1 + self.rightHeld += 1 + elif self.arrowHorz > 0: + self.arrowHorz = 0 + self.rightHeld = 0 + + def __leftArrow(self, pressed): + if pressed: + self.arrowHorz = -1 + self.leftHeld += 1 + elif self.arrowHorz < 0: + self.arrowHorz = 0 + self.leftHeld = 0 + + def __updateNonlocalVehicle(self, task = None): + self.curSpeed = self.smoother.getSmoothForwardVelocity() + rotSpeed = -1 * self.smoother.getSmoothRotationalVelocity() + self.leanAmount = self.curSpeed * rotSpeed / 500.0 + self.leanAmount = clampScalar(self.leanAmount, -10, 10) + self.__animate() + return Task.cont + + def __animate(self): + speed = self.curSpeed + self.spinWheels(speed / 10) + enginePitch = clampScalar(speed / 120.0, 0.5, 15) + self.kartLoopSfx.setPlayRate(enginePitch) + if not self.localVehicle: + dist = (self.getPos() - localAvatar.getPos()).length() + if self.localVehicle: + if self.lifter.isOnGround(): + if self.offGround > 10: + kart = self.geom[0].find('**/main*') + bumpDown1 = kart.posInterval(0.1, Vec3(0, 0, -1)) + bumpUp1 = kart.posInterval(0.15, Vec3(0, 0, 0)) + bumpDown2 = kart.posInterval(0.5, Vec3(0, 0, -.4)) + bumpUp2 = kart.posInterval(0.7, Vec3(0, 0, 0)) + bumpSeq = Sequence(bumpDown1, bumpUp1, bumpDown2, bumpUp2) + bumpSeq.start() + self.offGround = 0 + else: + self.offGround += 1 + if self.offGround == 0: + modifier = self.surfaceModifiers[self.groundType]['shake'] * (speed / 50.0) + else: + modifier = 1 + roll = self.leanAmount * 1.5 + roll += (random.random() * 2 - 1) * modifier + pitch = self.acceleration * -.005 + pitch += (random.random() * 2 - 1) * modifier + self.rollSuspension(roll) + self.pitchSuspension(pitch) + return Task.cont + + def __clampPosition(self): + retVal = False + if self.getX() < -3276: + self.notify.debug('clamping X %d' % self.getX()) + self.setX(-3276) + retVal = True + if self.getX() > 3276: + self.notify.debug('clamping X %d' % self.getX()) + self.setX(3276) + retVal = True + if self.getY() < -3276: + self.notify.debug('clamping Y %d' % self.getY()) + self.setY(-3276) + retVal = True + if self.getY() > 3276: + self.notify.debug('clamping Y %d' % self.getY()) + self.setY(3276) + retVal = True + if self.getZ() < -3276: + self.notify.debug('clamping Z %d' % self.getZ()) + self.setZ(-3276) + retVal = True + if self.getZ() > 3276: + self.notify.debug('clamping Z %d' % self.getZ()) + self.setZ(3276) + retVal = True + return retVal + + def amIClampingPosition(self): + return self.getX() == -3276 or self.getX() == 3276 or self.getY() == -3276 or self.getY() == 3276 or self.getZ() == -3276 or self.getZ() == 3276 + + def __computeTurnRatio(self, speed): + effectiveSpeed = speed + if effectiveSpeed > self.speedMinTurn: + effectiveSpeed = self.speedMinTurn + if effectiveSpeed < self.speedMaxTurn: + turnRatio = 1.0 * effectiveSpeed + elif effectiveSpeed >= self.speedMaxTurn: + turnRatio = 1.0 * (effectiveSpeed * (self.speedMaxTurn / speed)) + + def __updateWheelPos(self, dt, curSpeed): + ratioIntoMax = (curSpeed - self.speedMinTurn) / (self.speedMaxTurn - self.speedMinTurn) + if ratioIntoMax > 1.0: + ratioIntoMax = 1.0 + if ratioIntoMax < 0.0: + ratioIntoMax = 0.0 + maxWheelDeflection = self.wheelMaxTurn * ratioIntoMax + self.wheelMinTurn * (1.0 - ratioIntoMax) + if self.wheelPosition * self.arrowHorz > 0: + self.wheelPosition += self.arrowHorz * dt / self.wheelTurnTime + else: + self.wheelPosition += self.arrowHorz * dt / self.wheelFightTime + if not self.arrowHorz: + if self.wheelPosition > 0: + self.wheelPosition -= dt / self.wheelReturnTime + if self.wheelPosition < 0: + self.wheelPosition = 0 + else: + self.wheelPosition += dt / self.wheelReturnTime + if self.wheelPosition > 0: + self.wheelPosition = 0 + if abs(self.wheelPosition) >= maxWheelDeflection: + if self.wheelPosition > 0: + self.wheelPosition = maxWheelDeflection + else: + self.wheelPosition = -maxWheelDeflection + + def __watchControls(self, task): + dt = globalClock.getDt() + curVelocity = self.actorNode.getPhysicsObject().getVelocity() + curSpeed = curVelocity.length() + fvec = self.forward.getPos(render) - self.getPos(render) + fvec.normalize() + dotProduct = curVelocity.dot(fvec) + goingBack = -1 + if dotProduct < 0: + goingBack = 1 + if self.proRacer: + self.__computeTurnRatio(curSpeed) + self.__updateWheelPos(dt, curSpeed) + newHForTurning = self.getH() + if self.proRacer or not self.stopped and self.arrowHorz and curSpeed > 1: + if self.proRacer: + turnHelp = 0 + if self.hittingWall or goingBack == 1: + turnHelp = self.arrowHorz + effectiveSpeed = curSpeed + if effectiveSpeed > self.speedMinTurn: + effectiveSpeed = self.speedMinTurn + rotation = -goingBack * (self.wheelPosition * dt * self.turnRatio * -1.8 * curSpeed / 100 + turnHelp * dt * self.turnRatio * -1.2) + self.outPutCounter += 1 + if self.outPutCounter > 5: + self.outPutCounter = 0 + else: + rotation = self.arrowHorz * dt * self.turnRatio * -1.2 + oldH = self.getH() + newH = (oldH + rotation) % 360 + self.setH(newH) + newHForTurning = newH + if self.groundType == 'ice': + newHForTurning = (oldH + rotation * iceTurnFactor) % 360 + pitch = -self.getP() + 5 + accelBase = self.accelerationBase + pitch += accelBase + pitch = clampScalar(pitch, accelBase - 5, accelBase + 5) + self.accelerationMult = pitch * 2 + if self.groundType == 'ice': + self.accelerationMult *= iceAccelFactor + if self.stopped: + self.acceleration = 0 + else: + self.acceleration = self.arrowVert * self.accelerationMult * self.cheatFactor + if self.proRacer: + if self.skidding: + self.acceleration = self.arrowVert * self.accelerationMult * self.cheatFactor * 0.5 + if self.turbo: + self.acceleration += self.accelerationMult * 1.5 + self.engine.setVector(Vec3(0, self.acceleration, 0)) + if self.groundType == 'ice': + rotMat = Mat3.rotateMatNormaxis(newHForTurning, Vec3.up()) + else: + rotMat = Mat3.rotateMatNormaxis(self.getH(), Vec3.up()) + curHeading = rotMat.xform(Vec3.forward()) + push = (3 - self.getP()) * 0.02 + curHeading.setZ(curHeading.getZ() - min(0.2, max(-.2, push))) + onScreenDebug.append('vehicle curHeading = %s\n' % curHeading.pPrintValues()) + onScreenDebug.append('vehicle H = %s newHForTurning=%f\n' % (self.getH(), newHForTurning)) + windResistance = self.surfaceModifiers[self.groundType]['windResistance'] + self.windResistance.setCoef(windResistance) + physicsFrame = int((globalClock.getFrameTime() - self.physicsEpoch) * self.physicsCalculationsPerSecond) + numFrames = min(physicsFrame - self.lastPhysicsFrame, self.maxPhysicsFrames) + self.lastPhysicsFrame = physicsFrame + leanIncrement = self.arrowHorz * self.physicsDt * self.turnRatio + if self.stopped: + leanIncrement = 0 + driftMin = self.surfaceModifiers[self.groundType]['driftMin'] + if self.proRacer: + driftMin = self.surfaceModifiers[self.groundType]['driftMin'] * 0.2 + if self.skidding: + driftMin = self.surfaceModifiers[self.groundType]['driftMin'] + for i in xrange(int(numFrames)): + self.physicsMgr.doPhysics(self.physicsDt) + curVelocity = self.actorNode.getPhysicsObject().getVelocity() + idealVelocity = curHeading * curSpeed + curVelocity *= self.imHitMult + driftVal = abs(self.leanAmount) * 16 / self.cheatFactor + 15 / self.cheatFactor + curVelocity = Vec3((curVelocity * driftVal + idealVelocity) / (driftVal + 1)) + curSpeed = curVelocity.length() + curVelocity.normalize() + curVelocity *= min(curSpeed, 600) + self.actorNode.getPhysicsObject().setVelocity(curVelocity) + curSpeed = curVelocity.length() + speedFactor = min(curSpeed, 150) / 162.0 + self.leanAmount = (self.leanAmount + leanIncrement) * speedFactor + self.leanAmount = clampScalar(self.leanAmount, -10, 10) + + self.cWallTrav.traverse(render) + self.curSpeed = curSpeed + if self.proRacer: + self.turnWheels(self.wheelPosition * -45) + else: + self.turnWheels(self.arrowHorz * -10) + self.__animate() + if self.proRacer: + speedProporation = 1.0 + if curSpeed < self.speedMaxTurn: + speedProporation = 0.0 + else: + speedProporation = (curSpeed - self.speedMaxTurn) / self.speedMinTurn + cameraDist = self.cameraArmBase + self.cameraArmExtend * speedProporation + cameraOffset = Point3(0, -cameraDist, 0) + self.cameraNode.setPos(cameraOffset) + behindPos = render.getRelativePoint(self, Point3(0, -30, 0)) + self.proCameraDummyNode.setPos(render, behindPos) + self.proCameraDummyNode.lookAt(self) + self.cameraNode.lookAt(self) + dir1 = self.proCameraDummyNode.getH() + dir2 = self.proCameraHeading + if dir1 > 180: + dir1 -= 360 + elif dir1 < -180: + dir1 += 360 + if dir2 > 180: + dir2 -= 360 + elif dir2 < -180: + dir2 += 360 + self.proCameraHeading = dir2 + dif = dir1 - dir2 + if dif > 180: + dif -= 360 + elif dif < -180: + dif += 360 + speedDif = abs(dif) + if speedDif > 90: + speedDif = 90 + cameraTightener = 1.0 + if curSpeed > self.speedMinTurn: + cameraTightener = self.cameraTightener + else: + cameraTightener = 1.0 + curSpeed / self.speedMinTurn * (self.cameraTightener - 1.0) + swingSpeedRatio = speedDif / 90 + swingSpeed = self.armSwingSpeedPerp * swingSpeedRatio + self.armSwingSpeedPara * (1 - swingSpeedRatio) + self.proCameraHeading += dif * cameraTightener * (dt / swingSpeed) + self.cameraArmNode.setH(self.proCameraHeading - self.getH()) + elif not self.stopped: + self.cameraNode.setH(self.leanAmount) + self.updateParticles(self.leanAmount) + if (self.leanAmount > 8 or self.leanAmount < -8) and self.offGround <= 0: + self.startSkid() + else: + self.stopSkid() + if self.speedometer: + self.speedometer['text'] = str(int(curSpeed / 3)) + self.speedGauge.setR(min(110, max(0, curSpeed / 3 / 120 * 110))) + if not self.stopped: + self.stickCarToGround() + if self.__clampPosition(): + self.notify.debug('did a clampPosition on %d' % self.doId) + return Task.cont + + def enableControls(self): + self.canRace = True + self.accept('control', self.__controlPressed) + self.accept('control-up', self.__controlReleased) + self.accept('InputState-forward', self.__upArrow) + self.accept('InputState-reverse', self.__downArrow) + self.accept('InputState-turnLeft', self.__leftArrow) + self.accept('InputState-turnRight', self.__rightArrow) + + def disableControls(self): + self.arrowVert = 0 + self.arrowHorz = 0 + self.ignore('control') + self.ignore('control-up') + self.ignore('tab') + self.ignore('InputState-forward') + self.ignore('InputState-reverse') + self.ignore('InputState-turnLeft') + self.ignore('InputState-turnRight') + + def setInput(self, on): + if localAvatar.doId == self.ownerId: + if on: + self.enableControls() + else: + self.disableControls() + + def shakeWheel(self, nWheel, floorNode): + groundType = floorNode.getNetTag('surface') + modifier = self.surfaceModifiers[groundType]['shake'] + shakeFactor = (random.random() * 2 - 1) * modifier + shakeFactor *= self.curSpeed / 200 + node = self.wheelCenters[nWheel].find('*Wheel') + node.setZ(shakeFactor) + + def stickCarToGround(self): + posList = [] + nWheels = len(self.wheelData) + for nWheel in xrange(nWheels): + cQueue = self.cQueue[nWheel] + cQueue.sortEntries() + if cQueue.getNumEntries() == 0: + return + entry = cQueue.getEntry(0) + self.shakeWheel(nWheel, entry.getIntoNodePath()) + pos = entry.getSurfacePoint(render) + wheelPos = self.wheelBases[nWheel].getPos(render) + pos = wheelPos - pos + posList.append(pos) + cQueue.clearEntries() + + rf = posList[0].getZ() + lf = posList[1].getZ() + rr = posList[2].getZ() + lr = posList[3].getZ() + right = (rf + rr) / 2 + left = (lf + lr) / 2 + rollVal = right - left + rollVal = clampScalar(rollVal, -1, 1) + curRoll = self.getR() + newRoll = curRoll + rollVal * 2.0 + self.setR(newRoll) + if not self.stopped: + camera.setR(-newRoll) + front = (rf + lf) / 2 + rear = (rr + lr) / 2 + center = (front + rear) / 2 + pitchVal = front - rear + pitchVal = clampScalar(pitchVal, -1, 1) + curPitch = self.getP() + newPitch = curPitch - pitchVal * 2.0 + self.setP((newPitch + curPitch) / 2.0) + if self.proRacer: + self.cameraNode.setP(-newPitch) + elif not self.stopped: + self.cameraNode.setP(-newPitch) + + def setBodyType(self, bodyType): + self.kartDNA[KartDNA.bodyType] = bodyType + + def setBodyColor(self, bodyColor): + self.kartDNA[KartDNA.bodyColor] = bodyColor + + def setAccessoryColor(self, accColor): + self.kartDNA[KartDNA.accColor] = accColor + + def setEngineBlockType(self, ebType): + self.kartDNA[KartDNA.ebType] = ebType + + def setSpoilerType(self, spType): + self.kartDNA[KartDNA.spType] = spType + + def setFrontWheelWellType(self, fwwType): + self.kartDNA[KartDNA.fwwType] = fwwType + + def setBackWheelWellType(self, bwwType): + self.kartDNA[KartDNA.bwwType] = bwwType + + def setRimType(self, rimsType): + self.kartDNA[KartDNA.rimsType] = rimsType + + def setDecalType(self, decalType): + self.kartDNA[KartDNA.decalType] = decalType + + def setOwner(self, avId): + self.ownerId = avId + DistributedVehicle.AvId2kart[avId] = self + + def stopCar(self, level): + self.imHitMult = level + self.__stopTurbo() + self.stopped = True + + def startCar(self): + self.imHitMult = 1 + self.stopped = False + + def hideAnvil(self): + self.anvil.reparentTo(hidden) + self.anvil.setAlphaScale(1) + self.dropShadow.setScale(self.ShadowScale) + + def spinCar(self, spin): + self.geom[0].setH(2 * spin) + + def playSpin(self, timeStamp): + delta = -globalClockDelta.networkToLocalTime(timeStamp, globalClock.getFrameTime(), 16, 100) + globalClock.getFrameTime() + self.spinAnim = LerpFunc(self.spinCar, fromData=0, toData=360, duration=min(1 - delta, 0)) + self.spinAnim.start() + + def __throwGag(self): + base.race.shootGag() + + def dropOnMe(self, timestamp): + self.anvil.setScale(7, 9, 7) + self.anvil.setPos(0, 0, 75) + self.anvil.reparentTo(self) + anvilDrop = ProjectileInterval(self.anvil, startPos=Point3(0, 0, 100), endPos=Point3(0, 0, 0), duration=1) + shadowScale = self.dropShadow.scaleInterval(1, self.ShadowScale * 2) + fadeOut = LerpFunc(self.anvil.setAlphaScale, fromData=1, toData=0, duration=0.5) + flattenMe = self.geom[0].scaleInterval(0.08, Vec3(1, 1, 0.1)) + unFlatten = Sequence(self.geom[0].scaleInterval(0.2, Vec3(1, 1, 2)), self.geom[0].scaleInterval(0.1, 1)) + anvilSquish = Parallel(Parallel(anvilDrop, shadowScale), Sequence(Wait(0.92), flattenMe)) + anvilStretchFade = Sequence(Parallel(self.anvil.scaleInterval(0.2, Vec3(7, 9, 5)), Func(self.lookUp)), fadeOut) + self.gagMovie = Sequence(anvilSquish, Func(self.stopCar, 0.9), anvilStretchFade, Wait(0.5), Parallel(unFlatten, Func(self.lookNormal)), Func(self.startCar), Func(self.hideAnvil)) + self.gagMovie.start() + + def lookUp(self): + if self.toon and self.toon.headParts: + headParts = self.toon.headParts + for hi in xrange(headParts.getNumPaths()): + head = headParts[hi] + head.setP(90) + + def lookNormal(self): + if self.toon and self.toon.headParts: + headParts = self.toon.headParts + for hi in xrange(headParts.getNumPaths()): + head = headParts[hi] + head.setP(0) + + def flattenMe(self): + self.geom[0].setScale(3, 3, 0.1) + + def unFlattenMe(self): + self.geom[0].setScale(2) + + def startSkid(self): + if self.skidding == False: + self.skidding = True + self.skidLoopAsphaltSfx.play() + + def stopSkid(self): + if self.skidding == True: + self.skidding = False + self.skidLoopAsphaltSfx.stop() + self.skidLoopGrassSfx.stop() + + def updateSkid(self): + if self.skidding: + if self.groundType == 'grass': + self.skidLoopGrassSfx.setVolume(1) + self.skidLoopAsphaltSfx.setVolume(0) + else: + self.skidLoopGrassSfx.setVolume(0) + self.skidLoopAsphaltSfx.setVolume(1) + + def dropOnHim(self, timestamp): + self.anvil.setScale(10, 13, 10) + self.anvil.setPos(0, 0, 75) + self.anvil.reparentTo(self) + anvilDrop = ProjectileInterval(self.anvil, startPos=Point3(0, 0, 100), endPos=Point3(0, 0, 0), duration=1) + shadowScale = self.dropShadow.scaleInterval(1, self.ShadowScale * 4) + fadeOut = LerpFunc(self.anvil.setAlphaScale, fromData=1, toData=0, duration=0.5) + flattenMe = self.geom[0].scaleInterval(0.08, Vec3(1, 1, 0.1)) + unFlatten = Sequence(self.geom[0].scaleInterval(0.2, Vec3(1, 1, 2)), self.geom[0].scaleInterval(0.1, 1)) + anvilSquish = Parallel(Parallel(anvilDrop, shadowScale), Sequence(Wait(0.92), flattenMe)) + anvilStretchFade = Sequence(Parallel(self.anvil.scaleInterval(0.2, Vec3(10, 13, 4)), Func(self.lookUp)), fadeOut) + self.gagMovie = Sequence(anvilSquish, anvilStretchFade, Wait(0.5), Parallel(unFlatten, Func(self.lookNormal)), Func(self.hideAnvil)) + self.gagMovie.start() + + def hitBanana(self): + if self.wipeOut: + self.wipeOut.pause() + spinAnim = LerpFunc(self.spinCar, fromData=self.geom[0].getH(), toData=360, duration=1) + else: + spinAnim = LerpFunc(self.spinCar, fromData=0, toData=360, duration=1) + self.wipeOut = Sequence(Func(self.stopCar, 0.99), spinAnim, Func(self.startCar)) + self.wipeOut.start() + + def hitPie(self): + print 'yar, got Me with pi!' + self.splatPie() + if self.wipeOut: + self.wipeOut.pause() + spinAnim = LerpFunc(self.spinCar, fromData=self.geom[0].getH(), toData=1080, duration=0.5) + else: + spinAnim = LerpFunc(self.spinCar, fromData=0, toData=1080, duration=0.5) + self.wipeOut = Sequence(Func(self.stopCar, 0.99), spinAnim, Func(self.startCar)) + self.wipeOut.start() + + def finishMovies(self): + if self.gagMovie: + self.gagMovie.finish() + self.gagMovie = None + if self.wipeOut: + self.wipeOut.finish() + self.wipeOut = None + if self.spinAnim: + self.spinAnim.finish() + self.spinAnim = None + return diff --git a/toontown/racing/DistributedVehicleAI.py b/toontown/racing/DistributedVehicleAI.py new file mode 100755 index 00000000..caf86fcb --- /dev/null +++ b/toontown/racing/DistributedVehicleAI.py @@ -0,0 +1,143 @@ +from otp.ai.AIBase import * +from toontown.toonbase.ToontownGlobals import * +from toontown.racing.KartDNA import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedSmoothNodeAI +from direct.fsm import FSM +from direct.task import Task +from direct.distributed.PyDatagram import * + +class DistributedVehicleAI(DistributedSmoothNodeAI.DistributedSmoothNodeAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedVehicleAI') + + def __init__(self, air, avId): + self.ownerId = avId + DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedVehicleAI') + self.driverId = 0 + self.kartDNA = [-1] * getNumFields() + self.__initDNA() + self.request('Off') + + def generate(self): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.generate(self) + + def delete(self): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self) + + def __initDNA(self): + owner = self.air.doId2do.get(self.ownerId) + if owner: + self.kartDNA[KartDNA.bodyType] = owner.getKartBodyType() + self.kartDNA[KartDNA.bodyColor] = owner.getKartBodyColor() + self.kartDNA[KartDNA.accColor] = owner.getKartAccessoryColor() + self.kartDNA[KartDNA.ebType] = owner.getKartEngineBlockType() + self.kartDNA[KartDNA.spType] = owner.getKartSpoilerType() + self.kartDNA[KartDNA.fwwType] = owner.getKartFrontWheelWellType() + self.kartDNA[KartDNA.bwwType] = owner.getKartBackWheelWellType() + self.kartDNA[KartDNA.rimsType] = owner.getKartRimType() + self.kartDNA[KartDNA.decalType] = owner.getKartDecalType() + else: + self.notify.warning('__initDNA - OWNER %s OF KART NOT FOUND!' % self.ownerId) + + def d_setState(self, state, avId): + self.sendUpdate('setState', [state, avId]) + + def requestControl(self): + avId = self.air.getAvatarIdFromSender() + accId = self.air.getAccountIdFromSender() + if self.driverId == 0: + self.request('Controlled', avId, accId) + + def requestParked(self): + avId = self.air.getAvatarIdFromSender() + if avId == self.driverId: + self.request('Parked') + + def start(self): + self.request('Parked') + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterParked(self): + self.driverId = 0 + self.d_setState('P', 0) + return None + + def exitParked(self): + return None + + def enterControlled(self, avId, accId): + self.driverId = avId + fieldList = ['setComponentL', + 'setComponentX', + 'setComponentY', + 'setComponentZ', + 'setComponentH', + 'setComponentP', + 'setComponentR', + 'setComponentT', + 'setSmStop', + 'setSmH', + 'setSmZ', + 'setSmXY', + 'setSmXZ', + 'setSmPos', + 'setSmHpr', + 'setSmXYH', + 'setSmXYZH', + 'setSmPosHpr', + 'setSmPosHprL', + 'clearSmoothing', + 'suggestResync', + 'returnResync'] + #self.air.setAllowClientSend(avId, self, fieldList, accId) + #hack until CLIENTAGENT_SET_FIELDS_SENDABLE works + #probably should not be kept for any longer than it needs to + dg = PyDatagram() + dg.addServerHeader(self.doId, self.air.ourChannel, STATESERVER_OBJECT_SET_OWNER) + dg.addUint64(accId << 32 | avId) + self.air.send(dg) + self.d_setState('C', self.driverId) + + def exitControlled(self): + pass + + def handleUnexpectedExit(self): + self.notify.warning('toon: %d exited unexpectedly, resetting vehicle %d' % (self.driverId, self.doId)) + self.request('Parked') + self.requestDelete() + + def getBodyType(self): + return self.kartDNA[KartDNA.bodyType] + + def getBodyColor(self): + return self.kartDNA[KartDNA.bodyColor] + + def getAccessoryColor(self): + return self.kartDNA[KartDNA.accColor] + + def getEngineBlockType(self): + return self.kartDNA[KartDNA.ebType] + + def getSpoilerType(self): + return self.kartDNA[KartDNA.spType] + + def getFrontWheelWellType(self): + return self.kartDNA[KartDNA.fwwType] + + def getBackWheelWellType(self): + return self.kartDNA[KartDNA.bwwType] + + def getRimType(self): + return self.kartDNA[KartDNA.rimsType] + + def getDecalType(self): + return self.kartDNA[KartDNA.decalType] + + def getOwner(self): + return self.ownerId diff --git a/toontown/racing/DistributedViewPad.py b/toontown/racing/DistributedViewPad.py new file mode 100755 index 00000000..ca73a864 --- /dev/null +++ b/toontown/racing/DistributedViewPad.py @@ -0,0 +1,26 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.task import Task +from panda3d.core import * +from toontown.racing.DistributedKartPad import DistributedKartPad +from toontown.racing.KartShopGlobals import KartGlobals + +class DistributedViewPad(DistributedKartPad): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedViewPad') + id = 0 + + def __init__(self, cr): + DistributedKartPad.__init__(self, cr) + self.id = DistributedViewPad.id + DistributedViewPad.id += 1 + + def setLastEntered(self, timeStamp): + self.timeStamp = timeStamp + + def getTimestamp(self, avId): + return self.timeStamp + + def addStartingBlock(self, block): + block.cameraPos = Point3(0, 23, 7) + block.cameraHpr = Point3(180, -10, 0) + DistributedKartPad.addStartingBlock(self, block) diff --git a/toontown/racing/DistributedViewPadAI.py b/toontown/racing/DistributedViewPadAI.py new file mode 100755 index 00000000..df7bff07 --- /dev/null +++ b/toontown/racing/DistributedViewPadAI.py @@ -0,0 +1,26 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.racing.DistributedKartPadAI import DistributedKartPadAI +from direct.distributed.ClockDelta import * + +class DistributedViewPadAI(DistributedKartPadAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedViewPadAI") + + def __init__(self, air): + DistributedKartPadAI.__init__(self, air) + self.timestamp = globalClockDelta.getRealNetworkTime() + + def setLastEntered(self, timestamp): + self.timestamp = timestamp + + def d_setLastEntered(self, timestamp): + self.sendUpdate('setLastEntered', [timestamp]) + + def b_setLastEntered(self, timestamp): + self.setLastEntered(timestamp) + self.d_setLastEntered(timestamp) + + def getLastEntered(self): + return self.timestamp + + def updateTimer(self): + self.b_setLastEntered(globalClockDelta.getRealNetworkTime()) diff --git a/toontown/racing/DroppedGag.py b/toontown/racing/DroppedGag.py new file mode 100755 index 00000000..a4289d68 --- /dev/null +++ b/toontown/racing/DroppedGag.py @@ -0,0 +1,23 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from otp.avatar import ShadowCaster + +class DroppedGag(NodePath, ShadowCaster.ShadowCaster): + + def __init__(self, name, geom): + NodePath.__init__(self, name) + ShadowCaster.ShadowCaster.__init__(self, False) + self.gag = geom.copyTo(self) + self.initializeDropShadow() + self.setActiveShadow() + self.dropShadow.setScale(1) + + def delete(self): + ShadowCaster.ShadowCaster.delete(self) + NodePath.removeNode(self) + self.gag = None + return + + def getGeomNode(self): + return self.gag diff --git a/toontown/racing/EffectManager.py b/toontown/racing/EffectManager.py new file mode 100755 index 00000000..8122c1a5 --- /dev/null +++ b/toontown/racing/EffectManager.py @@ -0,0 +1,42 @@ +from panda3d.core import * +from direct.showbase.DirectObject import DirectObject +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import * +from toontown.battle import MovieUtil + +class EffectManager(DirectObject): + + def __init__(self): + self.effectList = [] + + def delete(self): + for effect in effectList: + self.__removeEffect(effect) + + def addSplatEffect(self, spawner, splatName = 'splat-creampie', time = 1, size = 6, parent = render): + splat = globalPropPool.getProp(splatName) + splatSeq = Sequence() + splatType = globalPropPool.getPropType(splatName) + splatShow = Func(self.__showProp, splat, size, parent, spawner.getPos(parent)) + splatAnim = ActorInterval(splat, splatName) + splatHide = Func(MovieUtil.removeProp, splat) + splatRemove = Func(self.__removeEffect, splatSeq) + splatSeq.append(splatShow) + splatSeq.append(splatAnim) + splatSeq.append(splatHide) + splatSeq.append(splatRemove) + splatSeq.start() + self.effectList.append(splatSeq) + + def __showProp(self, prop, size, parent, pos): + prop.reparentTo(parent) + prop.setScale(size) + prop.setBillboardPointEye() + prop.setPos(pos + Vec3(0, 0, size / 2)) + + def __removeEffect(self, effect): + if effect.isPlaying(): + effect.finish() + self.effectList.remove(effect) + effect = None + return diff --git a/toontown/racing/FlyingGag.py b/toontown/racing/FlyingGag.py new file mode 100755 index 00000000..92a09b5c --- /dev/null +++ b/toontown/racing/FlyingGag.py @@ -0,0 +1,34 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from otp.avatar.ShadowCaster import ShadowCaster + +class FlyingGag(NodePath, ShadowCaster): + + def __init__(self, name, geom = None): + an = ActorNode('flyingGagAN') + NodePath.__init__(self, an) + self.actorNode = an + self.gag = None + self.gagNode = None + ShadowCaster.__init__(self, False) + if geom: + self.gagNode = self.attachNewNode('PieNode') + self.gag = geom.copyTo(self.gagNode) + self.gag.setScale(3) + self.gagNode.setHpr(0, -45, 0) + self.gagNode.setPos(0, 0, 2) + self.initializeDropShadow() + self.setActiveShadow() + self.dropShadow.setPos(0, 0, 2) + self.dropShadow.setScale(3) + return + + def delete(self): + ShadowCaster.delete(self) + NodePath.remove(self) + self.gag = None + return + + def getGeomNode(self): + return self.gag diff --git a/toontown/racing/Kart.py b/toontown/racing/Kart.py new file mode 100755 index 00000000..e51aeb7b --- /dev/null +++ b/toontown/racing/Kart.py @@ -0,0 +1,529 @@ +from direct.directnotify import DirectNotifyGlobal +from panda3d.core import * +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from otp.avatar import ShadowCaster +from toontown.racing.KartDNA import * +from toontown.toonbase import TTLocalizer + +class Kart(NodePath, ShadowCaster.ShadowCaster): + notify = DirectNotifyGlobal.directNotify.newCategory('Kart') + index = 0 + baseScale = 2.0 + RFWHEEL = 0 + LFWHEEL = 1 + RRWHEEL = 2 + LRWHEEL = 3 + wheelData = [{'node': 'wheel*Node2'}, + {'node': 'wheel*Node1'}, + {'node': 'wheel*Node3'}, + {'node': 'wheel*Node4'}] + ShadowScale = 2.5 + SFX_BaseDir = 'phase_6/audio/sfx/' + SFX_KartStart = SFX_BaseDir + 'KART_Engine_start_%d.ogg' + SFX_KartLoop = SFX_BaseDir + 'KART_Engine_loop_%d.ogg' + + def __init__(self): + NodePath.__init__(self) + an = ActorNode('vehicle-test') + anp = NodePath(an) + NodePath.assign(self, anp) + self.actorNode = an + ShadowCaster.ShadowCaster.__init__(self, False) + Kart.index += 1 + self.updateFields = [] + self.kartDNA = [-1] * getNumFields() + self.kartAccessories = {KartDNA.ebType: None, + KartDNA.spType: None, + KartDNA.fwwType: (None, None), + KartDNA.bwwType: (None, None)} + self.texCount = 1 + return + + def delete(self): + self.__stopWheelSpin() + del self.kartDNA + del self.updateFields + self.kartLoopSfx.stop() + NodePath.removeNode(self) + ShadowCaster.ShadowCaster.delete(self) + + def getKartBounds(self): + return self.geom[0].getTightBounds() + + def generateKart(self, forGui = 0): + self.LODnode = FadeLODNode('lodNode') + self.LODpath = self.attachNewNode(self.LODnode) + self.LODnode.setFadeTime(0.15) + self.geom = {} + self.pitchNode = {} + self.toonNode = {} + self.rotateNode = self.attachNewNode('rotate') + levelIn = [base.config.GetInt('lod1-in', 30), base.config.GetInt('lod2-in', 80), base.config.GetInt('lod2-in', 200)] + levelOut = [base.config.GetInt('lod1-out', 0), base.config.GetInt('lod2-out', 30), base.config.GetInt('lod2-out', 80)] + lodRequired = 3 + if forGui: + lodRequired = 1 + levelIn[0] = base.config.GetInt('lod1-in', 2500) + levelIn[1] = base.config.GetInt('lod1-out', 0) + self.toonSeat = NodePath('toonSeat') + for level in xrange(lodRequired): + self.__createLODKart(level) + self.LODnode.addSwitch(levelIn[level], levelOut[level]) + + self.setScale(self.baseScale) + self.flattenMedium() + for level in xrange(lodRequired): + self.toonSeat = self.toonSeat.instanceTo(self.toonNode[level]) + + self.LODpath.reparentTo(self.rotateNode) + tempNode = NodePath('tempNode') + self.accGeomScale = tempNode.getScale(self.pitchNode[0]) * self.baseScale + tempNode.removeNode() + self.__applyBodyColor() + self.__applyEngineBlock() + self.__applySpoiler() + self.__applyFrontWheelWells() + self.__applyBackWheelWells() + self.__applyRims() + self.__applyDecals() + self.__applyAccessoryColor() + self.wheelCenters = [] + self.wheelBases = [] + for wheel in self.wheelData: + center = self.geom[0].find('**/' + wheel['node']) + self.wheelCenters.append(center) + wheelBase = center.getParent().attachNewNode('wheelBase') + wheelBase.setPos(center.getPos()) + wheelBase.setZ(0) + self.wheelBases.append(wheelBase) + + self.wheelBaseH = self.wheelCenters[0].getH() + self.__startWheelSpin() + self.setWheelSpinSpeed(0) + if not forGui: + self.shadowJoint = self.geom[0] + self.initializeDropShadow() + self.setActiveShadow() + self.dropShadow.setScale(self.ShadowScale) + else: + self.shadowJoint = self.LODpath + self.initializeDropShadow() + self.setActiveShadow() + self.dropShadow.setScale(1.3, 3, 1) + kartType = self.kartDNA[KartDNA.bodyType] + self.kartStartSfx = base.loadSfx(self.SFX_KartStart % kartType) + self.kartLoopSfx = base.loadSfx(self.SFX_KartLoop % kartType) + self.kartLoopSfx.setLoop() + + def __createLODKart(self, level): + kartBodyPath = getKartModelPath(self.kartDNA[KartDNA.bodyType], level) + self.geom[level] = loader.loadModel(kartBodyPath) + self.geom[level].reparentTo(self.LODpath) + self.geom[level].setH(180) + self.geom[level].setPos(0.0, 0, 0.025) + self.pitchNode[level] = self.geom[level].find('**/suspensionNode') + self.toonNode[level] = self.geom[level].find('**/toonNode') + scale = 1.0 / self.pitchNode[level].getScale()[0] + scale /= self.baseScale + self.toonNode[level].setScale(scale) + h = (180 + self.pitchNode[level].getH()) % 360 + self.toonNode[level].setH(h) + pos = Point3(0, -1.3, -7) + self.toonNode[level].setPos(pos) + + def resetGeomPos(self): + for level in self.geom.keys(): + self.geom[level].setPos(0, 0, 0.025) + + def __update(self): + for field in self.updateFields: + if field == KartDNA.bodyType: + if hasattr(self, 'geom'): + for kart in self.geom: + self.geom[kart].removeNode() + self.__createLODKart(kart) + self.geom[kart].reparentTo(self.rotateNode) + + self.__applyBodyColor() + self.__applyEngineBlock() + self.__applySpoiler() + self.__applyFrontWheelWells() + self.__applyRims() + self.__applyDecals() + self.__applyAccessoryColor() + else: + raise StandardError, 'Kart::__update - Has this method been called before generateKart?' + elif field == KartDNA.bodyColor: + self.__applyBodyColor() + elif field == KartDNA.accColor: + self.__applyAccessoryColor() + elif field == KartDNA.ebType: + if self.kartAccessories[KartDNA.ebType] != None: + name = self.kartAccessories[KartDNA.ebType].getName() + for key in self.geom.keys(): + self.geom[key].find('**/%s' % name).removeNode() + + self.kartAccessories[KartDNA.ebType].removeNode() + self.kartAccessories[KartDNA.ebType] = None + self.__applyEngineBlock() + elif field == KartDNA.spType: + if self.kartAccessories[KartDNA.spType] != None: + name = self.kartAccessories[KartDNA.spType].getName() + for key in self.geom.keys(): + self.geom[key].find('**/%s' % name).removeNode() + + self.kartAccessories[KartDNA.spType].removeNode() + self.kartAccessories[KartDNA.spType] = None + self.__applySpoiler() + elif field == KartDNA.fwwType: + if self.kartAccessories[KartDNA.fwwType] != (None, None): + left, right = self.kartAccessories[KartDNA.fwwType] + for key in self.geom.keys(): + self.geom[key].find('**/%s' % left.getName()).removeNode() + self.geom[key].find('**/%s' % right.getName()).removeNode() + + left.removeNode() + right.removeNode() + self.kartAccessories[KartDNA.fwwType] = (None, None) + self.__applyFrontWheelWells() + elif field == KartDNA.bwwType: + if self.kartAccessories[KartDNA.bwwType] != (None, None): + left, right = self.kartAccessories[KartDNA.bwwType] + for key in self.geom.keys(): + self.geom[key].find('**/%s' % left.getName()).removeNode() + self.geom[key].find('**/%s' % right.getName()).removeNode() + + left.removeNode() + right.removeNode() + self.kartAccessories[KartDNA.bwwType] = (None, None) + self.__applyBackWheelWells() + else: + if field == KartDNA.rimsType: + self.__applyRims() + elif field == KartDNA.decalType: + self.__applyDecals() + self.__applyAccessoryColor() + + self.updateFields = [] + return + + def updateDNAField(self, field, fieldValue): + if field == KartDNA.bodyType: + self.setBodyType(fieldValue) + elif field == KartDNA.bodyColor: + self.setBodyColor(fieldValue) + elif field == KartDNA.accColor: + self.setAccessoryColor(fieldValue) + elif field == KartDNA.ebType: + self.setEngineBlockType(fieldValue) + elif field == KartDNA.spType: + self.setSpoilerType(fieldValue) + elif field == KartDNA.fwwType: + self.setFrontWheelWellType(fieldValue) + elif field == KartDNA.bwwType: + self.setBackWheelWellType(fieldValue) + elif field == KartDNA.rimsType: + self.setRimType(fieldValue) + elif field == KartDNA.decalType: + self.setDecalType(fieldValue) + self.updateFields.append(field) + self.__update() + + def __applyBodyColor(self): + if self.kartDNA[KartDNA.bodyColor] == InvalidEntry: + bodyColor = getDefaultColor() + else: + bodyColor = getAccessory(self.kartDNA[KartDNA.bodyColor]) + for kart in self.geom: + kartBody = self.geom[kart].find('**/chasse') + kartBody.setColorScale(bodyColor) + + def __applyAccessoryColor(self): + if self.kartDNA[KartDNA.accColor] == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.kartDNA[KartDNA.accColor]) + for kart in self.geom: + hoodDecal = self.geom[kart].find('**/hoodDecal') + rightSideDecal = self.geom[kart].find('**/rightSideDecal') + leftSideDecal = self.geom[kart].find('**/leftSideDecal') + hoodDecal.setColorScale(accColor) + rightSideDecal.setColorScale(accColor) + leftSideDecal.setColorScale(accColor) + + for type in [KartDNA.ebType, KartDNA.spType]: + model = self.kartAccessories.get(type, None) + if model != None and not model.find('**/vertex').isEmpty(): + if self.kartDNA[KartDNA.accColor] == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.kartDNA[KartDNA.accColor]) + model.find('**/vertex').setColorScale(accColor) + + for type in [KartDNA.fwwType, KartDNA.bwwType]: + lModel, rModel = self.kartAccessories.get(type, (None, None)) + if lModel != None and not lModel.find('**/vertex').isEmpty(): + if self.kartDNA[KartDNA.accColor] == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.kartDNA[KartDNA.accColor]) + lModel.find('**/vertex').setColorScale(accColor) + rModel.find('**/vertex').setColorScale(accColor) + + return + + def __applyEngineBlock(self): + ebType = self.kartDNA[KartDNA.ebType] + if ebType == InvalidEntry: + return + ebPath = getAccessory(ebType) + attachNode = getAccessoryAttachNode(ebType) + model = loader.loadModel(ebPath) + self.kartAccessories[KartDNA.ebType] = model + model.setScale(self.accGeomScale) + if not model.find('**/vertex').isEmpty(): + if self.kartDNA[KartDNA.accColor] == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.kartDNA[KartDNA.accColor]) + model.find('**/vertex').setColorScale(accColor) + for kart in self.geom: + engineBlockNode = self.geom[kart].find('**/%s' % attachNode) + model.setPos(engineBlockNode.getPos(self.pitchNode[kart])) + model.setHpr(engineBlockNode.getHpr(self.pitchNode[kart])) + model.instanceTo(self.pitchNode[kart]) + + def __applySpoiler(self): + spType = self.kartDNA[KartDNA.spType] + if spType == InvalidEntry: + return + spPath = getAccessory(spType) + attachNode = getAccessoryAttachNode(spType) + model = loader.loadModel(spPath) + self.kartAccessories[KartDNA.spType] = model + model.setScale(self.accGeomScale) + for kart in self.geom: + spoilerNode = self.geom[kart].find('**/%s' % attachNode) + model.setPos(spoilerNode.getPos(self.pitchNode[kart])) + model.setHpr(spoilerNode.getHpr(self.pitchNode[kart])) + model.instanceTo(self.pitchNode[kart]) + + def __applyRims(self): + if self.kartDNA[KartDNA.rimsType] == InvalidEntry: + rimTexPath = getAccessory(getDefaultRim()) + else: + rimTexPath = getAccessory(self.kartDNA[KartDNA.rimsType]) + rimTex = loader.loadTexture('%s.jpg' % rimTexPath, '%s_a.rgb' % rimTexPath) + for kart in self.geom: + leftFrontWheelRim = self.geom[kart].find('**/leftFrontWheelRim') + rightFrontWheelRim = self.geom[kart].find('**/rightFrontWheelRim') + leftRearWheelRim = self.geom[kart].find('**/leftRearWheelRim') + rightRearWheelRim = self.geom[kart].find('**/rightRearWheelRim') + rimTex.setMinfilter(Texture.FTLinearMipmapLinear) + leftFrontWheelRim.setTexture(rimTex, self.texCount) + rightFrontWheelRim.setTexture(rimTex, self.texCount) + leftRearWheelRim.setTexture(rimTex, self.texCount) + rightRearWheelRim.setTexture(rimTex, self.texCount) + + self.texCount += 1 + + def __applyFrontWheelWells(self): + fwwType = self.kartDNA[KartDNA.fwwType] + if fwwType == InvalidEntry: + return + fwwPath = getAccessory(fwwType) + attachNode = getAccessoryAttachNode(fwwType) + leftAttachNode = attachNode % 'left' + rightAttachNode = attachNode % 'right' + leftModel = loader.loadModel(fwwPath) + rightModel = loader.loadModel(fwwPath) + self.kartAccessories[KartDNA.fwwType] = (leftModel, rightModel) + if not leftModel.find('**/vertex').isEmpty(): + if self.kartDNA[KartDNA.accColor] == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.kartDNA[KartDNA.accColor]) + leftModel.find('**/vertex').setColorScale(accColor) + rightModel.find('**/vertex').setColorScale(accColor) + for kart in self.geom: + leftNode = self.geom[kart].find('**/%s' % leftAttachNode) + rightNode = self.geom[kart].find('**/%s' % rightAttachNode) + leftNodePath = leftModel.instanceTo(self.pitchNode[kart]) + leftNodePath.setPos(rightNode.getPos(self.pitchNode[kart])) + leftNodePath.setHpr(rightNode.getHpr(self.pitchNode[kart])) + leftNodePath.setScale(self.accGeomScale) + leftNodePath.setSx(-1.0 * leftNodePath.getSx()) + leftNodePath.setTwoSided(True) + rightNodePath = rightModel.instanceTo(self.pitchNode[kart]) + rightNodePath.setPos(leftNode.getPos(self.pitchNode[kart])) + rightNodePath.setHpr(leftNode.getHpr(self.pitchNode[kart])) + rightNodePath.setScale(self.accGeomScale) + + def __applyBackWheelWells(self): + bwwType = self.kartDNA[KartDNA.bwwType] + if bwwType == InvalidEntry: + return + bwwPath = getAccessory(bwwType) + attachNode = getAccessoryAttachNode(bwwType) + leftAttachNode = attachNode % 'left' + rightAttachNode = attachNode % 'right' + leftModel = loader.loadModel(bwwPath) + rightModel = loader.loadModel(bwwPath) + self.kartAccessories[KartDNA.bwwType] = (leftModel, rightModel) + if not leftModel.find('**/vertex').isEmpty(): + if self.kartDNA[KartDNA.accColor] == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.kartDNA[KartDNA.accColor]) + leftModel.find('**/vertex').setColorScale(accColor) + rightModel.find('**/vertex').setColorScale(accColor) + for kart in self.geom: + leftNode = self.geom[kart].find('**/%s' % leftAttachNode) + rightNode = self.geom[kart].find('**/%s' % rightAttachNode) + leftNodePath = leftModel.instanceTo(self.pitchNode[kart]) + leftNodePath.setPos(rightNode.getPos(self.pitchNode[kart])) + leftNodePath.setHpr(rightNode.getHpr(self.pitchNode[kart])) + leftNodePath.setScale(self.accGeomScale) + leftNodePath.setSx(-1.0 * leftNodePath.getSx()) + leftNodePath.setTwoSided(True) + rightNodePath = rightModel.instanceTo(self.pitchNode[kart]) + rightNodePath.setPos(leftNode.getPos(self.pitchNode[kart])) + rightNodePath.setHpr(leftNode.getHpr(self.pitchNode[kart])) + rightNodePath.setScale(self.accGeomScale) + + def __applyDecals(self): + if self.kartDNA[KartDNA.decalType] != InvalidEntry: + decalId = getAccessory(self.kartDNA[KartDNA.decalType]) + kartDecal = getDecalId(self.kartDNA[KartDNA.bodyType]) + hoodDecalTex = loader.loadTexture('phase_6/maps/%s_HoodDecal_%s.jpg' % (kartDecal, decalId), 'phase_6/maps/%s_HoodDecal_%s_a.rgb' % (kartDecal, decalId)) + sideDecalTex = loader.loadTexture('phase_6/maps/%s_SideDecal_%s.jpg' % (kartDecal, decalId), 'phase_6/maps/%s_SideDecal_%s_a.rgb' % (kartDecal, decalId)) + hoodDecalTex.setMinfilter(Texture.FTLinearMipmapLinear) + sideDecalTex.setMinfilter(Texture.FTLinearMipmapLinear) + for kart in self.geom: + hoodDecal = self.geom[kart].find('**/hoodDecal') + rightSideDecal = self.geom[kart].find('**/rightSideDecal') + leftSideDecal = self.geom[kart].find('**/leftSideDecal') + hoodDecal.setTexture(hoodDecalTex, self.texCount) + rightSideDecal.setTexture(sideDecalTex, self.texCount) + leftSideDecal.setTexture(sideDecalTex, self.texCount) + hoodDecal.show() + rightSideDecal.show() + leftSideDecal.show() + + else: + for kart in self.geom: + hoodDecal = self.geom[kart].find('**/hoodDecal') + rightSideDecal = self.geom[kart].find('**/rightSideDecal') + leftSideDecal = self.geom[kart].find('**/leftSideDecal') + hoodDecal.hide() + rightSideDecal.hide() + leftSideDecal.hide() + + self.texCount += 1 + + def rollSuspension(self, roll): + for kart in self.pitchNode: + self.pitchNode[kart].setR(roll) + + def pitchSuspension(self, pitch): + for kart in self.pitchNode: + self.pitchNode[kart].setP(pitch) + + def getDNA(self): + return self.kartDNA + + def setDNA(self, dna): + if self.kartDNA != [-1] * getNumFields(): + for field in xrange(len(self.kartDNA)): + if dna[field] != self.kartDNA[field]: + self.updateDNAField(field, dna[field]) + + return + self.kartDNA = dna + + def setBodyType(self, bodyType): + self.kartDNA[KartDNA.bodyType] = bodyType + + def getBodyType(self): + return self.kartDNA[KartDNA.bodyType] + + def setBodyColor(self, bodyColor): + self.kartDNA[KartDNA.bodyColor] = bodyColor + + def getBodyColor(self): + return self.kartDNA[KartDNA.bodyColor] + + def setAccessoryColor(self, accColor): + self.kartDNA[KartDNA.accColor] = accColor + + def getAccessoryColor(self): + return self.kartDNA[KartDNA.accColor] + + def setEngineBlockType(self, ebType): + self.kartDNA[KartDNA.ebType] = ebType + + def getEngineBlockType(self): + return self.kartDNA[KartDNA.ebType] + + def setSpoilerType(self, spType): + self.kartDNA[KartDNA.spType] = spType + + def getSpoilerType(self): + return self.kartDNA[KartDNA.spType] + + def setFrontWheelWellType(self, fwwType): + self.kartDNA[KartDNA.fwwType] = fwwType + + def getFrontWheelWellType(self): + return self.kartDNA[KartDNA.fwwType] + + def setBackWheelWellType(self, bwwType): + self.kartDNA[KartDNA.bwwType] = bwwType + + def getBackWheelWellType(self): + return self.kartDNA[KartDNA.bwwType] + + def setRimType(self, rimsType): + self.kartDNA[KartDNA.rimsType] = rimsType + + def getRimType(self): + return self.kartDNA[KartDNA.rimsType] + + def setDecalType(self, decalType): + self.kartDNA[KartDNA.decalType] = decalType + + def getDecalType(self): + return self.kartDNA[KartDNA.decalType] + + def getGeomNode(self): + return self.geom[0] + + def spinWheels(self, amount): + newSpin = (self.oldSpinAmount + amount) % 360 + for wheelNode in self.wheelCenters: + wheelNode.setP(newSpin) + + self.oldSpinAmount = newSpin + + def setWheelSpinSpeed(self, speed): + pass + + def __startWheelSpin(self): + self.oldSpinAmount = 0 + + def __stopWheelSpin(self): + pass + + def turnWheels(self, amount): + amount += self.wheelBaseH + node = self.wheelCenters[self.RFWHEEL] + node.setH(amount) + node = self.wheelCenters[self.LFWHEEL] + node.setH(amount) + + def generateEngineStartTrack(self): + return Parallel(SoundInterval(self.kartStartSfx), Func(self.kartLoopSfx.play), LerpFunctionInterval(self.kartLoopSfx.setVolume, fromData=0, toData=0.4, duration=self.kartStartSfx.length())) + + def generateEngineStopTrack(self, duration = 0): + return Parallel(LerpFunctionInterval(self.kartLoopSfx.setVolume, fromData=0.4, toData=0, duration=duration)) diff --git a/toontown/racing/KartDNA.py b/toontown/racing/KartDNA.py new file mode 100755 index 00000000..300bbd60 --- /dev/null +++ b/toontown/racing/KartDNA.py @@ -0,0 +1,619 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import PythonUtil +from toontown.toonbase import TTLocalizer +from panda3d.core import * +from KartShopGlobals import * +import types, copy + +KartDNA = PythonUtil.Enum('bodyType, bodyColor, accColor, ebType, spType, fwwType, bwwType, rimsType, decalType') +InvalidEntry = -1 +KartInfo = PythonUtil.Enum('name, model, cost, viewDist, decalId, LODmodel1, LODmodel2') +AccInfo = PythonUtil.Enum('name, model, cost, texCard, attach') +kNames = TTLocalizer.KartDNA_KartNames +KartDict = {0: (kNames[0], + 'phase_6/models/karting/Kart1_Final', + 100, + 7.0, + 'kart1', + 'phase_6/models/karting/Kart1_LOD_Final', + 'phase_6/models/karting/Kart1_LOD_Final', + (Point3(1.5, 8.0, -0.5), Point3(1.5, 0.0, 2.0))), + 1: (kNames[1], + 'phase_6/models/karting/Kart2_Final', + 7500, + 7.0, + 'kart2', + 'phase_6/models/karting/Kart2_LOD2_Final', + 'phase_6/models/karting/Kart2_LOD3_Final', + (Point3(0.25, 7, -2), Point3(1.25, -3, 0))), + 2: (kNames[2], + 'phase_6/models/karting/Kart3_Final', + 2500, + 8.5, + 'kart3', + 'phase_6/models/karting/Kart3_Final_LOD2', + 'phase_6/models/karting/Kart3_Final_LOD3', + (Point3(1.25, 4.0, 1.0), Point3(1.25, -3.0, 2.5)))} +aNames = TTLocalizer.KartDNA_AccNames +AccessoryDict = {0: (aNames[1000], + 'phase_6/models/karting/accessory_frontMiddle_0', + 200, + 'accessory_frontMiddle_0_gui', + 'ebNode_0'), + 1: (aNames[1001], + 'phase_6/models/karting/accessory_frontMiddle_1', + 1250, + 'accessory_frontMiddle_1_gui', + 'ebNode_0'), + 2: (aNames[1002], + 'phase_6/models/karting/accessory_frontMiddle_2', + 450, + 'accessory_frontMiddle_2_gui', + 'ebNode_1'), + 3: (aNames[1003], + 'phase_6/models/karting/accessory_frontMiddle_3', + 5000, + 'accessory_frontMiddle_3_gui', + 'ebNode_1'), + 4: (aNames[1004], + 'phase_6/models/karting/accessory_frontMiddle_4', + 800, + 'accessory_frontMiddle_4_gui', + 'ebNode_0'), + 5: (aNames[1005], + 'phase_6/models/karting/accessory_frontMiddle_5', + 200, + 'accessory_frontMiddle_5_gui', + 'ebNode_0'), + 6: (aNames[1006], + 'phase_6/models/karting/accessory_frontMiddle_6', + 450, + 'accessory_frontMiddle_6_gui', + 'ebNode_0'), + 7: (aNames[1007], + 'phase_6/models/karting/accessory_frontMiddle_7', + 800, + 'accessory_frontMiddle_7_gui', + 'ebNode_0'), + 8: (aNames[1008], + 'phase_6/models/karting/accessory_frontMiddle_8', + 200, + 'accessory_frontMiddle_8_gui', + 'ebNode_1'), + 9: (aNames[1009], + 'phase_6/models/karting/accessory_frontMiddle_9', + 800, + 'accessory_frontMiddle_9_gui', + 'ebNode_1'), + 10: (aNames[1010], + 'phase_6/models/karting/accessory_frontMiddle_10', + 1250, + 'accessory_frontMiddle_10_gui', + 'ebNode_0'), + 11: (aNames[2000], + 'phase_6/models/karting/accessory_backMiddle_0', + 450, + 'accessory_backMiddle_0_gui', + 'spNode_1'), + 12: (aNames[2001], + 'phase_6/models/karting/accessory_backMiddle_1', + 200, + 'accessory_backMiddle_1_gui', + 'spNode_0'), + 13: (aNames[2002], + 'phase_6/models/karting/accessory_backMiddle_2', + 800, + 'accessory_backMiddle_2_gui', + 'spNode_1'), + 14: (aNames[2003], + 'phase_6/models/karting/accessory_backMiddle_3', + 1250, + 'accessory_backMiddle_3_gui', + 'spNode_1'), + 15: (aNames[2004], + 'phase_6/models/karting/accessory_backMiddle_4', + 5000, + 'accessory_backMiddle_4_gui', + 'spNode_1'), + 16: (aNames[2005], + 'phase_6/models/karting/accessory_backMiddle_5', + 800, + 'accessory_backMiddle_5_gui', + 'spNode_1'), + 17: (aNames[2006], + 'phase_6/models/karting/accessory_backMiddle_6', + 450, + 'accessory_backMiddle_6_gui', + 'spNode_0'), + 18: (aNames[2007], + 'phase_6/models/karting/accessory_backMiddle_7', + 1250, + 'accessory_backMiddle_7_gui', + 'spNode_1'), + 19: (aNames[3000], + 'phase_6/models/karting/accessory_front_ww_0', + 200, + 'accessory_front_ww_0_gui', + '%sFWWNode_0'), + 20: (aNames[3001], + 'phase_6/models/karting/accessory_front_ww_1', + 200, + 'accessory_front_ww_1_gui', + '%sFWWNode_1'), + 21: (aNames[3002], + 'phase_6/models/karting/accessory_front_ww_2', + 800, + 'accessory_front_ww_2_gui', + '%sFWWNode_2'), + 22: (aNames[3003], + 'phase_6/models/karting/accessory_front_ww_3', + 5000, + 'accessory_front_ww_3_gui', + '%sFWWNode_2'), + 23: (aNames[3004], + 'phase_6/models/karting/accessory_front_ww_4', + 1250, + 'accessory_front_ww_4_gui', + '%sFWWNode_2'), + 24: (aNames[3005], + 'phase_6/models/karting/accessory_front_ww_5', + 800, + 'accessory_front_ww_5_gui', + '%sFWWNode_1'), + 25: (aNames[3006], + 'phase_6/models/karting/accessory_front_ww_6', + 1250, + 'accessory_front_ww_6_gui', + '%sFWWNode_2'), + 26: (aNames[3007], + 'phase_6/models/karting/accessory_front_ww_7', + 450, + 'accessory_front_ww_7_gui', + '%sFWWNode_2'), + 27: (aNames[4000], + 'phase_6/models/karting/accessory_rear_ww_0', + 800, + 'accessory_rear_ww_0_gui', + '%sBWWNode_0'), + 28: (aNames[4001], + 'phase_6/models/karting/accessory_rear_ww_1', + 200, + 'accessory_rear_ww_1_gui', + '%sBWWNode_1'), + 29: (aNames[4002], + 'phase_6/models/karting/accessory_rear_ww_2', + 200, + 'accessory_rear_ww_2_gui', + '%sBWWNode_2'), + 30: (aNames[4003], + 'phase_6/models/karting/accessory_rear_ww_3', + 1250, + 'accessory_rear_ww_3_gui', + '%sBWWNode_0'), + 31: (aNames[4004], + 'phase_6/models/karting/accessory_rear_ww_4', + 200, + 'accessory_rear_ww_4_gui', + '%sBWWNode_2'), + 32: (aNames[4005], + 'phase_6/models/karting/accessory_rear_ww_5', + 800, + 'accessory_rear_ww_5_gui', + '%sBWWNode_2'), + 33: (aNames[4006], + 'phase_6/models/karting/accessory_rear_ww_6', + 450, + 'accessory_rear_ww_6_gui', + '%sBWWNode_2'), + 34: (aNames[4007], + 'phase_6/models/karting/accessory_rear_ww_7', + 5000, + 'accessory_rear_ww_7_gui', + '%sBWWNode_2'), + 35: (aNames[4008], + 'phase_6/models/karting/accessory_rear_ww_8', + 1250, + 'accessory_rear_ww_8_gui', + '%sBWWNode_0'), + 36: (aNames[4009], + 'phase_6/models/karting/accessory_rear_ww_9', + 1250, + 'accessory_rear_ww_9_gui', + '%sBWWNode_0'), + 37: (aNames[4010], + 'phase_6/models/karting/accessory_rear_ww_10', + 450, + 'accessory_rear_ww_10_gui', + '%sBWWNode_2'), + 38: (aNames[4011], + 'phase_6/models/karting/accessory_rear_ww_11', + 800, + 'accessory_rear_ww_11_gui', + '%sBWWNode_2'), + 39: (aNames[4012], + 'phase_6/models/karting/accessory_rear_ww_12', + 1250, + 'accessory_rear_ww_12_gui', + '%sBWWNode_2'), + 40: (aNames[5000], + 'phase_6/maps/kart_Rim_1', + InvalidEntry, + 'kart_Rim_1'), + 41: (aNames[5001], + 'phase_6/maps/kart_Rim_2', + 450, + 'kart_Rim_2'), + 42: (aNames[5002], + 'phase_6/maps/kart_Rim_3', + 100, + 'kart_Rim_3'), + 43: (aNames[5003], + 'phase_6/maps/kart_Rim_4', + 800, + 'kart_Rim_4'), + 44: (aNames[5004], + 'phase_6/maps/kart_Rim_5', + 100, + 'kart_Rim_5'), + 45: (aNames[5005], + 'phase_6/maps/kart_Rim_6', + 200, + 'kart_Rim_6'), + 46: (aNames[5006], + 'phase_6/maps/kart_Rim_7', + 200, + 'kart_Rim_7'), + 47: (aNames[5007], + 'phase_6/maps/kart_Rim_8', + 200, + 'kart_Rim_8'), + 48: (aNames[5008], + 'phase_6/maps/kart_Rim_9', + 200, + 'kart_Rim_9'), + 49: (aNames[5009], + 'phase_6/maps/kart_Rim_10', + 200, + 'kart_Rim_10'), + 50: (aNames[5010], + 'phase_6/maps/kart_Rim_11', + 200, + 'kart_Rim_11'), + 51: (aNames[5011], + 'phase_6/maps/kart_Rim_12', + 800, + 'kart_Rim_12'), + 52: (aNames[5012], + 'phase_6/maps/kart_Rim_13', + 450, + 'kart_Rim_13'), + 53: (aNames[5013], + 'phase_6/maps/kart_Rim_14', + 1250, + 'kart_Rim_14'), + 54: (aNames[5014], + 'phase_6/maps/kart_Rim_15', + 5000, + 'kart_Rim_15'), + 55: (aNames[6000], + 1, + 200, + '%s_SideDecal_1'), + 56: (aNames[6001], + 2, + 450, + '%s_SideDecal_2'), + 57: (aNames[6002], + 3, + 100, + '%s_SideDecal_3'), + 58: (aNames[6003], + 4, + 5000, + '%s_SideDecal_4'), + 59: (aNames[6004], + 5, + 200, + '%s_SideDecal_5'), + 60: (aNames[6005], + 6, + 200, + '%s_SideDecal_6'), + 61: (aNames[6006], + 7, + 450, + '%s_SideDecal_7'), + 62: (aNames[6007], + 8, + 100, + '%s_SideDecal_8'), + 63: (aNames[6008], + 9, + 1250, + '%s_SideDecal_9'), + 64: (aNames[6009], + 10, + 800, + '%s_SideDecal_10'), + 65: (aNames[7000], VBase4(0.8, 1.0, 0.5, 1.0), 100), + 66: (aNames[7001], VBase4(0.96875, 0.691406, 0.699219, 1.0), 100), + 67: (aNames[7002], VBase4(0.933594, 0.265625, 0.28125, 1.0), 2500), + 68: (aNames[7003], VBase4(0.863281, 0.40625, 0.417969, 1.0), 900), + 69: (aNames[7004], VBase4(0.710938, 0.234375, 0.4375, 1.0), 1600), + 70: (aNames[7005], VBase4(0.570312, 0.449219, 0.164062, 1.0), 1600), + 71: (aNames[7006], VBase4(0.640625, 0.355469, 0.269531, 1.0), 1600), + 72: (aNames[7007], VBase4(0.996094, 0.695312, 0.511719, 1.0), 100), + 73: (aNames[7008], VBase4(0.832031, 0.5, 0.296875, 1.0), 1600), + 74: (aNames[7009], VBase4(0.992188, 0.480469, 0.167969, 1.0), 1600), + 75: (aNames[7010], VBase4(0.996094, 0.898438, 0.320312, 1.0), 2500), + 76: (aNames[7011], VBase4(0.996094, 0.957031, 0.597656, 1.0), 900), + 77: (aNames[7012], VBase4(0.855469, 0.933594, 0.492188, 1.0), 900), + 78: (aNames[7013], VBase4(0.550781, 0.824219, 0.324219, 1.0), 900), + 79: (aNames[7014], VBase4(0.242188, 0.742188, 0.515625, 1.0), 1600), + 80: (aNames[7015], VBase4(0.304688, 0.96875, 0.402344, 1.0), 2500), + 81: (aNames[7016], VBase4(0.433594, 0.90625, 0.835938, 1.0), 900), + 82: (aNames[7017], VBase4(0.347656, 0.820312, 0.953125, 1.0), 900), + 83: (aNames[7018], VBase4(0.191406, 0.5625, 0.773438, 1.0), 1600), + 84: (aNames[7019], VBase4(0.558594, 0.589844, 0.875, 1.0), 900), + 85: (aNames[7020], VBase4(0.285156, 0.328125, 0.726562, 1.0), 2500), + 86: (aNames[7021], VBase4(0.460938, 0.378906, 0.824219, 1.0), 900), + 87: (aNames[7022], VBase4(0.546875, 0.28125, 0.75, 1.0), 1600), + 88: (aNames[7023], VBase4(0.726562, 0.472656, 0.859375, 1.0), 1600), + 89: (aNames[7024], VBase4(0.898438, 0.617188, 0.90625, 1.0), 1600), + 90: (aNames[7025], VBase4(0.7, 0.7, 0.8, 1.0), 2500), + 91: (aNames[7026], VBase4(0.3, 0.3, 0.35, 1.0), 10000)} +AccessoryTypeDict = {KartDNA.ebType: [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10], + KartDNA.spType: [11, + 12, + 13, + 14, + 15, + 16, + 17, + 18], + KartDNA.fwwType: [19, + 20, + 21, + 22, + 23, + 24, + 25, + 26], + KartDNA.bwwType: [27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39], + KartDNA.rimsType: [40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54], + KartDNA.decalType: [55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64], + KartDNA.bodyColor: [65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91]} +AccessoryTypeNameDict = [None, + 'KartShtikerBodyColor', + 'KartShtikerAccColor', + 'KartShtikerEngineBlock', + 'KartShtikerSpoiler', + 'KartShtikerFrontWheelWell', + 'KartShtikerBackWheelWell', + 'KartShtikerRim', + 'KartShtikerDecal'] + +def checkNumFieldsValidity(numFields): + return KartDNA.decalType == numFields - 1 + + +def checkKartFieldValidity(field): + if field < KartDNA.bodyType or field > KartDNA.decalType: + return 0 + return 1 + + +def getNumFields(): + return KartDNA.decalType + 1 + + +def getKartModelPath(kartType, lodLevel = 0): + if lodLevel == 1: + return KartDict.get(kartType)[KartInfo.LODmodel1] + if lodLevel == 2: + return KartDict.get(kartType)[KartInfo.LODmodel2] + return KartDict.get(kartType)[KartInfo.model] + + +def getKartViewDist(kartType): + return KartDict.get(kartType)[KartInfo.viewDist] + + +def getDecalId(kartType): + return KartDict.get(kartType)[KartInfo.decalId] + + +def getAccessory(accId): + return AccessoryDict.get(accId)[KartInfo.model] + + +def getAccessoryAttachNode(accId): + accInfo = AccessoryDict.get(accId) + if len(accInfo) == 5: + return accInfo[4] + return None + + +def getTexCardNode(accId): + accInfo = AccessoryDict.get(accId) + if len(accInfo) <= 5: + return accInfo[3] + return None + + +def checkKartDNAValidity(dna): + if not checkNumFieldsValidity(len(dna)): + return 0 + for field in xrange(len(dna)): + if field == KartDNA.bodyType: + if dna[field] not in KartDict.keys(): + return 0 + elif field == KartDNA.bodyColor or field == KartDNA.accColor: + accList = [InvalidEntry] + AccessoryTypeDict.get(KartDNA.bodyColor) + if dna[field] not in accList: + return 0 + else: + accList = [InvalidEntry] + AccessoryTypeDict.get(field) + if dna[field] not in accList: + return 0 + + return 1 + + +def getDefaultColor(): + return VBase4(1, 1, 1, 1) + + +def getDefaultRim(): + return AccessoryTypeDict[KartDNA.rimsType][0] + + +def getDefaultAccessory(category): + if category in [KartDNA.bodyColor, KartDNA.accColor]: + return getDefaultColor() + elif category == KartDNA.rimsType: + return getDefaultRim() + else: + return InvalidEntry + + +def getAccessoryItemList(accessoryType): + return [ AccessoryDict[itemId] for itemId in AccessoryTypeDict[accessoryType] ] + + +def getKartTypeInfo(type): + if type in KartDict.keys(): + return KartDict[type] + return InvalidEntry + + +def getAccessoryInfo(index): + if index in AccessoryDict.keys(): + return AccessoryDict[index] + return InvalidEntry + + +def getAccessoryType(accessoryId): + for key in AccessoryTypeDict.keys(): + if accessoryId in AccessoryTypeDict[key]: + return key + + return InvalidEntry + + +def getAccessoryDictFromOwned(accessoryOwnedList, pType = -1): + accessDict = copy.deepcopy(AccessoryTypeDict) + accessDict[KartDNA.rimsType].remove(getDefaultRim()) + for accOwnedId in accessoryOwnedList: + type = getAccessoryType(accOwnedId) + if type != InvalidEntry and accOwnedId in accessDict[type]: + accessDict[type].remove(accOwnedId) + + if pType != -1: + return accessDict[pType] + else: + return accessDict + + +def getAccessDictByType(accessoryOwnedList): + accessDict = {} + if type(accessoryOwnedList) == types.ListType: + for accOwnedId in accessoryOwnedList: + accType = getAccessoryType(accOwnedId) + if accType != InvalidEntry: + if accType not in accessDict: + accessDict[accType] = [] + accessDict[accType].append(accOwnedId) + + else: + print 'KartDNA: getAccessDictByType: bad accessory list: ', accessoryOwnedList + return accessDict + + +def getKartCost(kartID): + if kartID in KartDict: + return KartDict[kartID][KartInfo.cost] + else: + return 'key error' + + +def getAccCost(accID): + return AccessoryDict[accID][AccInfo.cost] + + +def getAccName(accID): + try: + return AccessoryDict[accID][AccInfo.name] + except: + return TTLocalizer.KartShtikerDefault diff --git a/toontown/racing/KartShopGlobals.py b/toontown/racing/KartShopGlobals.py new file mode 100755 index 00000000..6769c9ed --- /dev/null +++ b/toontown/racing/KartShopGlobals.py @@ -0,0 +1,28 @@ +from direct.showbase import PythonUtil + +class KartShopGlobals: + EVENTDICT = {'guiDone': 'guiDone', + 'returnKart': 'returnKart', + 'buyKart': 'buyAKart', + 'buyAccessory': 'buyAccessory'} + KARTCLERK_TIMER = 180 + MAX_KART_ACC = 16 + + +class KartGlobals: + ENTER_MOVIE = 1 + EXIT_MOVIE = 2 + COUNTDOWN_TIME = 30 + BOARDING_TIME = 10.0 + ENTER_RACE_TIME = 6.0 + ERROR_CODE = PythonUtil.Enum('success, eGeneric, eTickets, eBoardOver, eNoKart, eOccupied, eTrackClosed, eTooLate') + FRONT_LEFT_SPOT = 0 + FRONT_RIGHT_SPOT = 1 + REAR_LEFT_SPOT = 2 + REAR_RIGHT_SPOT = 3 + PAD_GROUP_NUM = 4 + + def getPadLocation(padId): + return padId % KartGlobals.PAD_GROUP_NUM + + getPadLocation = staticmethod(getPadLocation) diff --git a/toontown/racing/KartShopGui.py b/toontown/racing/KartShopGui.py new file mode 100755 index 00000000..ebc41297 --- /dev/null +++ b/toontown/racing/KartShopGui.py @@ -0,0 +1,973 @@ +if __name__ == '__main__': + from direct.directbase import DirectStart +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from direct.showbase import DirectObject, PythonUtil +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownTimer +from KartShopGlobals import * +from toontown.racing.Kart import Kart +from toontown.shtiker.KartPage import KartViewer +from KartDNA import * +MENUS = PythonUtil.Enum('MainMenu, BuyKart, BuyAccessory, ReturnKart, ConfirmBuyAccessory, ConfirmBuyKart, BoughtKart, BoughtAccessory') +MM_OPTIONS = PythonUtil.Enum('Cancel, BuyAccessory, BuyKart', -1) +BK_OPTIONS = PythonUtil.Enum('Cancel, BuyKart', -1) +BA_OPTIONS = PythonUtil.Enum('Cancel, BuyAccessory', -1) +RK_OPTIONS = PythonUtil.Enum('Cancel, ReturnKart', -1) +CBK_OPTIONS = PythonUtil.Enum('Cancel, BuyKart', -1) +CBA_OPTIONS = PythonUtil.Enum('Cancel, BuyAccessory', -1) +BTK_OPTIONS = PythonUtil.Enum('Ok', -1) +BTA_OPTIONS = PythonUtil.Enum('Ok', -1) +KS_TEXT_SIZE_BIG = TTLocalizer.KSGtextSizeBig +KS_TEXT_SIZE_SMALL = TTLocalizer.KSGtextSizeSmall + +class KartShopGuiMgr(object, DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr') + + class MainMenuDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.MainMenuDlg') + + def __init__(self, doneEvent): + model = loader.loadModel('phase_6/models/gui/Kart_MainMenuPanel') + self.modelScale = 0.75 + DirectFrame.__init__(self, relief=None, state='normal', geom=model, text_scale=0.1, geom_scale=self.modelScale, pos=(0, 0, -.01), frameSize=(-1, 1, -1, 1)) + self.initialiseoptions(KartShopGuiMgr.MainMenuDlg) + self.cancelButton = DirectButton( + parent=self, + relief=None, + geom=model.find('**/CancelIcon'), + image=(model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [MM_OPTIONS.Cancel])) + self.buyKartButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/BuyKartButtonUp'), model.find('**/BuyKartButtonDown'), model.find('**/BuyKartButtonRollover'), model.find('**/BuyKartButtonDisabled')), + scale=self.modelScale, + geom=model.find('**/BuyKartIcon'), + text=TTLocalizer.KartShop_BuyKart, + text_scale=KS_TEXT_SIZE_BIG, + text_pos=(-0.2, 0.34), + pressEffect=False, + command=lambda : messenger.send(doneEvent, [MM_OPTIONS.BuyKart])) + self.buyAccessoryButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/BuyAccessoryButtonUp'), model.find('**/BuyAccessoryButtonDown'), model.find('**/BuyAccessoryButtonRollover'), model.find('**/BuyAccessoryButtonDisabled')), + geom=model.find('**/BuyAccessoryIcon'), + image3_color=Vec4(0.6, 0.6, 0.6, 1), + scale=self.modelScale, + text=TTLocalizer.KartShop_BuyAccessories, + text_scale=KS_TEXT_SIZE_BIG, + text_pos=(-0.1, 0.036), + pressEffect=False, + command=lambda : messenger.send(doneEvent, [MM_OPTIONS.BuyAccessory])) + self.updateButtons() + return + + def updateButtons(self): + if not base.localAvatar.hasKart(): + self.buyAccessoryButton['state'] = DGG.DISABLED + else: + self.buyAccessoryButton['state'] = DGG.NORMAL + + def show(self): + self.updateButtons() + DirectFrame.DirectFrame.show(self) + + class BuyKartDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.BuyKartDlg') + + def __init__(self, doneEvent): + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/BuyKartPanel') + self.unownedKartList = KartDict.keys() + if base.localAvatar.hasKart(): + k = base.localAvatar.getKartBodyType() + if k in self.unownedKartList: + self.unownedKartList.remove(k) + self.numKarts = len(self.unownedKartList) + self.curKart = 0 + DirectFrame.__init__( + self, + relief=None, + state='normal', + geom=model, + geom_scale=self.modelScale, + frameSize=(-1, 1, -1, 1), + pos=(0, 0, -0.01), + text_wordwrap=26, + text_scale=KS_TEXT_SIZE_BIG, + text_pos=(0, 0)) + self.initialiseoptions(KartShopGuiMgr.BuyKartDlg) + self.ticketDisplay = DirectLabel( + parent=self, + relief=None, + text=str(base.localAvatar.getTickets()), + text_scale=KS_TEXT_SIZE_SMALL, + text_fg=(0.95, 0.95, 0.0, 1.0), + text_shadow=(0, 0, 0, 1), + text_pos=(0.44, -0.55), + text_font=ToontownGlobals.getSignFont()) + self.buyKartButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/BuyKartButtonUp'), model.find('**/BuyKartButtonDown'), model.find('**/BuyKartButtonRollover'), model.find('**/BuyKartButtonDisabled')), + scale=self.modelScale, + text=TTLocalizer.KartShop_BuyKart, + text_scale=KS_TEXT_SIZE_BIG, + text_pos=(0, -.534), + pressEffect=False, + command=lambda : messenger.send(doneEvent, [self.unownedKartList[self.curKart]])) + self.cancelButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')), + geom=model.find('**/CancelIcon'), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [BK_OPTIONS.Cancel])) + self.arrowLeftButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/ArrowLeftButtonUp'), model.find('**/ArrowLeftButtonDown'), model.find('**/ArrowLeftButtonRollover'), model.find('**/ArrowLeftButtonInactive')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleKartChange, + extraArgs=[-1]) + self.arrowRightButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/ArrowRightButtonUp'), model.find('**/ArrowRightButtonDown'), model.find('**/ArrowRightButtonRollover'), model.find('**/ArrowRightButtonInactive')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleKartChange, + extraArgs=[1]) + self.kartView = KartViewer([self.curKart, -1, -1, -1, -1, -1, -1, -1, -1], parent=self) + self.kartView.setPos(model.find('**/KartViewerFrame').getPos()) + self.kartView.load(model, 'KartViewerFrame', ['rotate_right_up', + 'rotate_right_down', + 'rotate_right_roll', + 'rotate_right_down', + (0.255, -.054)], ['rotate_left_up', + 'rotate_left_down', + 'rotate_left_roll', + 'rotate_left_down', + (-.24, -.054)], (0, -.055)) + self.kartView.setBounds(-0.38, 0.38, 0.0035, 0.53) + self.kartView.setBgColor(1.0, 1.0, 0.8, 1.0) + self.showKart() + self.initialize = True + model.removeNode() + return + + def showKart(self): + self.buyKartButton.configure(text=TTLocalizer.KartShop_BuyKart) + self.buyKartButton.configure(text_scale=KS_TEXT_SIZE_BIG) + if self.numKarts > 0: + info = getKartTypeInfo(self.unownedKartList[self.curKart]) + description = info[KartInfo.name] + cost = TTLocalizer.KartShop_Cost % info[KartInfo.cost] + self.kartDescription = DirectButton( + parent=self, + relief=None, + scale=self.modelScale, + text=description, + text_pos=(0, -.29), + text_scale=KS_TEXT_SIZE_SMALL, + pressEffect=False, + textMayChange=True) + self.kartCost = DirectButton( + parent=self, + relief=None, + scale=self.modelScale, + text=cost, + text_pos=(0, -.365), + text_scale=KS_TEXT_SIZE_SMALL, + pressEffect=False, + textMayChange=True) + self.buyKartButton['state'] = DGG.NORMAL + self.arrowRightButton['state'] = DGG.DISABLED + self.arrowLeftButton['state'] = DGG.DISABLED + if self.numKarts > self.curKart + 1: + self.arrowRightButton['state'] = DGG.NORMAL + if self.curKart > 0: + self.arrowLeftButton['state'] = DGG.NORMAL + if info[KartInfo.cost] > base.localAvatar.getTickets(): + self.buyKartButton['state'] = DGG.DISABLED + self.buyKartButton.configure(text_scale=KS_TEXT_SIZE_SMALL * 0.75) + self.buyKartButton.configure(text=TTLocalizer.KartShop_NotEnoughTickets) + self.kartCost.configure(text_fg=(0.95, 0, 0.0, 1.0)) + self.kartView.refresh([self.unownedKartList[self.curKart], -1, -1, -1, -1, -1, -1, -1, -1]) + self.kartView.show() + return + + def __handleKartChange(self, nDir): + self.curKart = (self.curKart + nDir) % self.numKarts + self.kartDescription.destroy() + self.kartCost.destroy() + self.showKart() + + def destroy(self): + if self.initialize: + self.kartView.destroy() + DirectFrame.destroy(self) + + class ReturnKartDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.ReturnKartDlg') + + def __init__(self, doneEvent): + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/ReturnKartPanel') + DirectFrame.__init__( + self, + relief=None, + state='normal', + geom=model, + geom_scale=self.modelScale, + frameSize=(-1, 1, -1, 1), + pos=(0, 0, -0.01), + text=TTLocalizer.KartShop_ConfirmReturnKart, + text_wordwrap=11, + text_scale=KS_TEXT_SIZE_SMALL * 0.9, + text_pos=(0, -0.26)) + self.initialiseoptions(KartShopGuiMgr.ReturnKartDlg) + self.cancelButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')), + geom=model.find('**/CancelIcon'), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [RK_OPTIONS.Cancel])) + self.okButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckButtonRollover')), + geom=model.find('**/CheckIcon'), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [RK_OPTIONS.ReturnKart])) + oldDNA = list(base.localAvatar.getKartDNA()) + for d in xrange(len(oldDNA)): + if d == KartDNA.bodyType: + continue + else: + oldDNA[d] = InvalidEntry + + self.kartView = KartViewer(oldDNA, parent=self) + self.kartView.setPos(model.find('**/KartViewerFrame').getPos()) + self.kartView.load(model, 'KartViewerFrame', [], [], None) + self.kartView.setBounds(-0.38, 0.38, -.04, 0.49) + self.kartView.setBgColor(1.0, 1.0, 0.8, 1.0) + self.kartView.show() + model.removeNode() + self.initialize = True + return + + def destroy(self): + if self.initialize: + self.kartView.destroy() + DirectFrame.destroy(self) + + class BoughtKartDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.BoughtKartDlg') + + def __init__(self, doneEvent, kartID): + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/BoughtKartPanel') + kartInfo = getKartTypeInfo(kartID) + name = kartInfo[KartInfo.name] + DirectFrame.__init__( + self, + relief=None, + state='normal', + geom=model, + geom_scale=self.modelScale, + frameSize=(-1, 1, -1, 1), + pos=(0, 0, -0.01), + text=TTLocalizer.KartShop_ConfirmBoughtTitle, + text_wordwrap=26, + text_scale=KS_TEXT_SIZE_SMALL, + text_pos=(0, -0.26)) + self.initialiseoptions(KartShopGuiMgr.BoughtKartDlg) + self.ticketDisplay = DirectLabel( + parent=self, + relief=None, + text=str(base.localAvatar.getTickets()), + text_scale=KS_TEXT_SIZE_SMALL, + text_fg=(0.95, 0.95, 0.0, 1.0), + text_shadow=(0, 0, 0, 1), + text_pos=(0.43, -0.5), + text_font=ToontownGlobals.getSignFont()) + self.okButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckButtonRollover')), + geom=model.find('**/CheckIcon'), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [BTK_OPTIONS.Ok])) + self.kartView = KartViewer([kartID, -1, -1, -1, -1, -1, -1, -1, -1], parent=self) + self.kartView.setPos(model.find('**/KartViewerFrame').getPos()) + self.kartView.load(model, 'KartViewerFrame', [], []) + self.kartView.setBounds(-0.38, 0.38, -.0425, 0.49) + self.kartView.setBgColor(1.0, 1.0, 0.8, 1.0) + self.kartView.show() + model.removeNode() + self.initialize = True + return + + def destroy(self): + if self.initialize: + self.kartView.destroy() + DirectFrame.destroy(self) + + class ConfirmBuyKartDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.ConfirmBuyKartDlg') + + def __init__(self, doneEvent, kartNum): + self.kartNum = kartNum + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/ConfirmBuyKartPanel') + kartInfo = getKartTypeInfo(kartNum) + name = kartInfo[KartInfo.name] + cost = kartInfo[KartInfo.cost] + DirectFrame.__init__( + self, + relief=None, + state='normal', + geom=model, + geom_scale=self.modelScale, + frameSize=(-1, 1, -1, 1), + pos=(0, 0, -0.01), + text=TTLocalizer.KartShop_ConfirmBuy % (name, cost), + text_wordwrap=11, + text_scale=KS_TEXT_SIZE_SMALL, + text_pos=(0, -0.26)) + self.initialiseoptions(KartShopGuiMgr.ConfirmBuyKartDlg) + self.ticketDisplay = DirectLabel( + parent=self, + relief=None, + text=str(base.localAvatar.getTickets()), + text_scale=KS_TEXT_SIZE_SMALL, + text_fg=(0.95, 0.95, 0.0, 1.0), + text_shadow=(0, 0, 0, 1), + text_pos=(0.43, -0.5), + text_font=ToontownGlobals.getSignFont()) + self.cancelButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')), + geom=model.find('**/CancelIcon'), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [CBK_OPTIONS.Cancel])) + self.okButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckButtonRollover')), + geom=model.find('**/CheckIcon'), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [CBK_OPTIONS.BuyKart])) + self.kartView = KartViewer([self.kartNum, -1, -1, -1, -1, -1, -1, -1, -1], parent=self) + self.kartView.setPos(model.find('**/KartViewerFrame').getPos()) + self.kartView.load(model, 'KartViewerFrame', [], [], None) + self.kartView.setBounds(-0.38, 0.38, -.0425, 0.49) + self.kartView.setBgColor(1.0, 1.0, 0.8, 1.0) + self.initialize = True + self.kartView.show() + model.removeNode() + return + + def destroy(self): + if self.initialize: + self.kartView.destroy() + DirectFrame.destroy(self) + + class BuyAccessoryDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.buyAccessoryDlg') + + def __init__(self, doneEvent): + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/BuyAccessoryPanel') + self.doneEvent = doneEvent + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_scale=self.modelScale, frameSize=(-1, 1, -1, 1), pos=(0, 0, -0.01), text_wordwrap=26, text_scale=0.1, text_fg=Vec4(0.36, 0.94, 0.93, 1.0), text_pos=(0, 0)) + self.initialiseoptions(KartShopGuiMgr.BuyAccessoryDlg) + self.ticketDisplay = DirectLabel( + parent=self, + relief=None, + text=str(base.localAvatar.getTickets()), + text_scale=KS_TEXT_SIZE_SMALL, + text_fg=(0.95, 0.95, 0.0, 1.0), + text_shadow=(0, 0, 0, 1), + text_pos=(0.42, -0.6), + text_font=ToontownGlobals.getSignFont()) + self.arrowLeftButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/ArrowLeftButtonUp'), model.find('**/ArrowLeftButtonDown'), model.find('**/ArrowLeftButtonRollover'), model.find('**/ArrowLeftButtonInactive')), + scale=self.modelScale, + text_pos=(0, 0), + text_scale=0.1, + pressEffect=False, + command=self.__handleAccessoryChange, + extraArgs=[-1]) + self.arrowRightButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/ArrowRightButtonUp'), model.find('**/ArrowRightButtonDown'), model.find('**/ArrowRightButtonRollover'), model.find('**/ArrowRightButtonInactive')), + scale=self.modelScale, + text_pos=(0, 0), + text_scale=0.1, + pressEffect=False, + command=self.__handleAccessoryChange, + extraArgs=[1]) + self.cancelButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')), + geom=model.find('**/CancelIcon'), + scale=self.modelScale, + command=lambda : messenger.send(doneEvent, [BA_OPTIONS.Cancel]), + pressEffect=False) + self.decalAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/DecalButtonUp'), model.find('**/DecalButtonDown'), model.find('**/DecalButtonRollover'), model.find('**/DecalButtonDown')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.decalType]) + self.spoilerAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/SpoilerButtonUp'), model.find('**/SpoilerButtonDown'), model.find('**/SpoilerButtonRollover'), model.find('**/SpoilerButtonDown')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.spType]) + self.eBlockAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/EBlockButtonUp'), model.find('**/EBlockButtonDown'), model.find('**/EBlockButtonRollover'), model.find('**/EBlockButtonDown')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.ebType]) + self.rearAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/RearButtonUp'), model.find('**/RearButtonDown'), model.find('**/RearButtonRollover'), model.find('**/RearButtonDown')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.bwwType]) + self.frontAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/FrontButtonUp'), model.find('**/FrontButtonDown'), model.find('**/FrontButtonRollover'), model.find('**/FrontButtonDown')), + scale=self.modelScale, + text_pos=(0, 0), + text_scale=0.1, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.fwwType]) + self.rimAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/RimButtonUp'), model.find('**/RimButtonDown'), model.find('**/RimButtonRollover'), model.find('**/RimButtonDown')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.rimsType]) + self.paintAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/PaintButtonUp'), model.find('**/PaintButtonDown'), model.find('**/PaintButtonRollover'), model.find('**/PaintButtonDown')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.bodyColor]) + self.accButtonsDict = { + KartDNA.ebType: self.eBlockAccButton, + KartDNA.spType: self.spoilerAccButton, + KartDNA.fwwType: self.frontAccButton, + KartDNA.bwwType: self.rearAccButton, + KartDNA.rimsType: self.rimAccButton, + KartDNA.decalType: self.decalAccButton, + KartDNA.bodyColor: self.paintAccButton} + self.buyAccessoryButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/BuyAccessoryButtonUp'), model.find('**/BuyAccessoryButtonDown'), model.find('**/BuyAccessoryButtonRollover'), model.find('**/BuyAccessoryButtonDisabled')), + scale=self.modelScale, + text=TTLocalizer.KartShop_BuyAccessory, + text_pos=(0, -.57), + text_scale=KS_TEXT_SIZE_SMALL, + pressEffect=False, + command=self.__handleBuyAccessory) + self.ownedAccList = base.localAvatar.getKartAccessoriesOwned() + while -1 in self.ownedAccList: + self.ownedAccList.remove(-1) + + self.unownedAccDict = getAccessoryDictFromOwned(self.ownedAccList) + self.curAccType = KartDNA.ebType + self.curAccIndex = {} + for type in self.unownedAccDict: + self.curAccIndex[type] = 0 + + self.kartView = KartViewer(list(base.localAvatar.getKartDNA()), parent=self) + self.kartView.setPos(model.find('**/KartViewerFrame').getPos()) + self.kartView.load(model, 'KartViewerFrame', ['rotate_right_up', + 'rotate_right_down', + 'rotate_right_roll', + 'rotate_right_down', + (0.255, 0)], ['rotate_left_up', + 'rotate_left_down', + 'rotate_left_roll', + 'rotate_left_down', + (-.24, 0)], (0, 0)) + self.kartView.setBounds(-0.38, 0.38, 0.044, 0.58) + self.kartView.setBgColor(1.0, 1.0, 0.87, 1.0) + self.initialize = True + self.showAccessory() + model.removeNode() + return + + def __handleBuyAccessory(self): + accessoryID = self.unownedAccDict[self.curAccType][self.curAccIndex[self.curAccType]] + self.ownedAccList.append(accessoryID) + self.unownedAccDict = getAccessoryDictFromOwned(self.ownedAccList) + self.__handleAccessoryChange(0) + messenger.send(self.doneEvent, [accessoryID]) + + def __handleAccessoryChange(self, nDir): + if len(self.unownedAccDict[self.curAccType]) < 1: + self.curAccIndex[self.curAccType] = -1 + else: + self.curAccIndex[self.curAccType] = (self.curAccIndex[self.curAccType] + nDir) % len(self.unownedAccDict[self.curAccType]) + if hasattr(self, 'accDescription'): + self.accDescription.destroy() + self.accCost.destroy() + self.showAccessory() + + def __handleAccessoryTypeChange(self, type): + self.curAccType = type + try: + self.accDescription.destroy() + self.accCost.destroy() + except: + pass + + for b in self.accButtonsDict: + self.accButtonsDict[b]['state'] = DGG.NORMAL + + self.accButtonsDict[self.curAccType]['state'] = DGG.DISABLED + self.showAccessory() + + def showAccessory(self): + self.arrowRightButton['state'] = DGG.DISABLED + self.arrowLeftButton['state'] = DGG.DISABLED + self.buyAccessoryButton['state'] = DGG.DISABLED + self.accDescription = DirectButton( + parent=self, + relief=None, + scale=self.modelScale, + text='', + text_pos=(0, -.33), + text_scale=KS_TEXT_SIZE_SMALL, + pressEffect=False, + text_wordwrap=TTLocalizer.KSGaccDescriptionWordwrap, + textMayChange=True) + self.buyAccessoryButton.configure(text_fg=(0, 0, 0.0, 1.0)) + self.buyAccessoryButton.configure(text=TTLocalizer.KartShop_BuyAccessory) + self.buyAccessoryButton.configure(text_scale=KS_TEXT_SIZE_SMALL) + self.buyAccessoryButton['state'] = DGG.NORMAL + if len(self.unownedAccDict[self.curAccType]) < 1: + self.kartView.setDNA(None) + self.kartView.hide() + self.accDescription.configure(text=TTLocalizer.KartShop_NoAvailableAcc) + self.buyAccessoryButton['state'] = DGG.DISABLED + else: + if self.curAccIndex[self.curAccType] + 1 < len(self.unownedAccDict[self.curAccType]): + self.arrowRightButton['state'] = DGG.NORMAL + if self.curAccIndex[self.curAccType] > 0: + self.arrowLeftButton['state'] = DGG.NORMAL + curDNA = None + curDNA = list(base.localAvatar.getKartDNA()) + for d in xrange(len(curDNA)): + if d == KartDNA.bodyType or d == KartDNA.accColor or d == KartDNA.bodyColor: + continue + else: + curDNA[d] = -1 + + curAcc = self.unownedAccDict[self.curAccType][self.curAccIndex[self.curAccType]] + curDNA[self.curAccType] = curAcc + self.kartView.refresh(curDNA) + self.accDescription.configure(text=AccessoryDict[curAcc][KartInfo.name]) + cost = TTLocalizer.KartShop_Cost % AccessoryDict[curAcc][KartInfo.cost] + self.accCost = DirectButton( + parent=self, + relief=None, + scale=self.modelScale, + text=cost, + text_pos=(0, -.4), + text_scale=KS_TEXT_SIZE_SMALL, + text_fg=(0, 0, 0.0, 1.0), + pressEffect=False, + textMayChange=True) + if AccessoryDict[curAcc][KartInfo.cost] > base.localAvatar.getTickets(): + self.buyAccessoryButton['state'] = DGG.DISABLED + self.buyAccessoryButton.configure(text_scale=KS_TEXT_SIZE_SMALL * 0.75) + self.buyAccessoryButton.configure(text=TTLocalizer.KartShop_NotEnoughTickets) + self.accCost.configure(text_fg=(0.95, 0, 0.0, 1.0)) + if len(base.localAvatar.getKartAccessoriesOwned()) >= KartShopGlobals.MAX_KART_ACC: + self.buyAccessoryButton['state'] = DGG.DISABLED + self.buyAccessoryButton.configure(text_fg=(0.95, 0, 0.0, 1.0)) + self.buyAccessoryButton.configure(text_scale=KS_TEXT_SIZE_SMALL * 0.8) + self.buyAccessoryButton.configure(text=TTLocalizer.KartShop_FullTrunk) + self.kartView.show() + return + + def destroy(self): + if self.initialize: + try: + self.accDescription.destroy() + except: + pass + + try: + self.kartView.destroy() + except: + pass + + DirectFrame.destroy(self) + + class BoughtAccessoryDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.BoughtAccessoryDlg') + + def __init__(self, doneEvent, accID): + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/BoughtAccessoryPanel') + accInfo = getAccessoryInfo(accID) + name = accInfo[AccInfo.name] + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_scale=self.modelScale, frameSize=(-1, 1, -1, 1), pos=(0, 0, -0.01), text=TTLocalizer.KartShop_ConfirmBoughtTitle, text_wordwrap=26, text_scale=KS_TEXT_SIZE_SMALL, text_pos=(0, -0.28)) + self.initialiseoptions(KartShopGuiMgr.BoughtAccessoryDlg) + self.ticketDisplay = DirectLabel(parent=self, relief=None, text=str(base.localAvatar.getTickets()), text_scale=KS_TEXT_SIZE_SMALL, text_fg=(0.95, 0.95, 0.0, 1.0), text_shadow=(0, 0, 0, 1), text_pos=(0.43, -0.5), text_font=ToontownGlobals.getSignFont()) + self.okButton = DirectButton(parent=self, relief=None, image=(model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckButtonRollover')), geom=model.find('**/CheckIcon'), scale=self.modelScale, pressEffect=False, command=lambda : messenger.send(doneEvent, [BTA_OPTIONS.Ok])) + self.kartView = DirectFrame(parent=self, relief=None, geom=model.find('**/KartViewerFrame'), scale=1.0) + bounds = self.kartView.getBounds() + radius = (bounds[3] - bounds[2]) / 2 + xCenter = self.kartView.getCenter()[0] + cm = CardMaker('accViewer') + cm.setFrame(xCenter - radius, xCenter + radius, bounds[2], bounds[3]) + self.kartView['geom'] = NodePath(cm.generate()) + self.kartView.component('geom0').setColorScale(1.0, 1.0, 0.8, 1.0) + self.kartView.component('geom0').setTransparency(True) + accType = getAccessoryType(accID) + texNodePath = None + tex = None + if accType in [KartDNA.ebType, + KartDNA.spType, + KartDNA.fwwType, + KartDNA.bwwType]: + texNodePath = getTexCardNode(accID) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath, 'phase_6/maps/%s_a.rgb' % texNodePath) + elif accType == KartDNA.rimsType: + if accID == InvalidEntry: + texNodePath = getTexCardNode(getDefaultRim()) + else: + texNodePath = getTexCardNode(accID) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath, 'phase_6/maps/%s_a.rgb' % texNodePath) + elif accType in [KartDNA.bodyColor, KartDNA.accColor]: + tex = loader.loadTexture('phase_6/maps/Kartmenu_paintbucket.jpg', 'phase_6/maps/Kartmenu_paintbucket_a.rgb') + if accID == InvalidEntry: + self.kartView.component('geom0').setColorScale(getDefaultColor()) + else: + self.kartView.component('geom0').setColorScale(getAccessory(accID)) + elif accType == KartDNA.decalType: + kartDecal = getDecalId(base.localAvatar.getKartBodyType()) + texNodePath = getTexCardNode(accID) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath % kartDecal, 'phase_6/maps/%s_a.rgb' % texNodePath % kartDecal) + else: + tex = loader.loadTexture('phase_6/maps/NoAccessoryIcon3.jpg', 'phase_6/maps/NoAccessoryIcon3_a.rgb') + tex.setMinfilter(Texture.FTLinearMipmapLinear) + self.kartView.component('geom0').setTexture(tex, 1) + self.initialize = True + return + + def destroy(self): + if self.initialize: + DirectFrame.destroy(self) + + class ConfirmBuyAccessoryDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.ConfirmBuyAccessoryDlg') + + def __init__(self, doneEvent, accID): + self.accID = accID + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/ConfirmBuyAccessory') + accInfo = getAccessoryInfo(accID) + cost = accInfo[AccInfo.cost] + name = accInfo[AccInfo.name] + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_scale=self.modelScale, frameSize=(-1, 1, -1, 1), pos=(0, 0, -0.01), text=TTLocalizer.KartShop_ConfirmBuy % (name, cost), text_wordwrap=14, text_scale=KS_TEXT_SIZE_SMALL, text_pos=(0, -0.25)) + self.initialiseoptions(KartShopGuiMgr.ConfirmBuyAccessoryDlg) + self.ticketDisplay = DirectLabel(parent=self, relief=None, text=str(base.localAvatar.getTickets()), text_scale=KS_TEXT_SIZE_SMALL, text_fg=(0.95, 0.95, 0.0, 1.0), text_shadow=(0, 0, 0, 1), text_pos=(0.43, -0.5), text_font=ToontownGlobals.getSignFont()) + self.cancelButton = DirectButton(parent=self, relief=None, image=(model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')), geom=model.find('**/CancelIcon'), scale=self.modelScale, pressEffect=False, command=lambda : messenger.send(doneEvent, [CBA_OPTIONS.Cancel])) + self.okButton = DirectButton(parent=self, relief=None, image=(model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckButtonRollover')), geom=model.find('**/CheckIcon'), scale=self.modelScale, pressEffect=False, command=lambda : messenger.send(doneEvent, [CBA_OPTIONS.BuyAccessory])) + self.kartView = DirectFrame(parent=self, relief=None, geom=model.find('**/KartViewerFrame'), scale=1.0) + bounds = self.kartView.getBounds() + radius = (bounds[3] - bounds[2]) / 3 + xCenter, yCenter = self.kartView.getCenter() + cm = CardMaker('accViewer') + cm.setFrame(xCenter - radius, xCenter + radius, yCenter - radius, yCenter + radius) + self.kartView['geom'] = NodePath(cm.generate()) + self.kartView.component('geom0').setColorScale(1.0, 1.0, 0.8, 1.0) + self.kartView.component('geom0').setTransparency(True) + accType = getAccessoryType(accID) + texNodePath = None + tex = None + if accType in [KartDNA.ebType, + KartDNA.spType, + KartDNA.fwwType, + KartDNA.bwwType]: + texNodePath = getTexCardNode(accID) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath, 'phase_6/maps/%s_a.rgb' % texNodePath) + elif accType == KartDNA.rimsType: + if accID == InvalidEntry: + texNodePath = getTexCardNode(getDefaultRim()) + else: + texNodePath = getTexCardNode(accID) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath, 'phase_6/maps/%s_a.rgb' % texNodePath) + elif accType in [KartDNA.bodyColor, KartDNA.accColor]: + tex = loader.loadTexture('phase_6/maps/Kartmenu_paintbucket.jpg', 'phase_6/maps/Kartmenu_paintbucket_a.rgb') + if accID == InvalidEntry: + self.kartView.component('geom0').setColorScale(getDefaultColor()) + else: + self.kartView.component('geom0').setColorScale(getAccessory(accID)) + elif accType == KartDNA.decalType: + kartDecal = getDecalId(base.localAvatar.getKartBodyType()) + texNodePath = getTexCardNode(accID) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath % kartDecal, 'phase_6/maps/%s_a.rgb' % texNodePath % kartDecal) + else: + tex = loader.loadTexture('phase_6/maps/NoAccessoryIcon3.jpg', 'phase_6/maps/NoAccessoryIcon3_a.rgb') + tex.setMinfilter(Texture.FTLinearMipmapLinear) + self.kartView.component('geom0').setTexture(tex, 1) + self.initialize = True + return + + def destroy(self): + if self.initialize: + DirectFrame.destroy(self) + + def __init__(self, eventDict): + self.dialog = None + self.dialogStack = [] + self.eventDict = eventDict + self.dialogEventDict = {MENUS.MainMenu: ('MainMenuGuiDone', self.__handleMainMenuDlg, self.MainMenuDlg), + MENUS.BuyKart: ('BuyKartGuiDone', self.__handleBuyKartDlg, self.BuyKartDlg), + MENUS.BuyAccessory: ('BuyAccessoryGuiDone', self.__handleBuyAccessoryDlg, self.BuyAccessoryDlg), + MENUS.ReturnKart: ('ReturnKartGuiDone', self.__handleReturnKartDlg, self.ReturnKartDlg), + MENUS.ConfirmBuyKart: ('ConfirmBuyKartGuiDone', self.__handleConfirmBuyKartDlg, self.ConfirmBuyKartDlg), + MENUS.ConfirmBuyAccessory: ('ConfirmBuyAccessoryGuiDone', self.__handleConfirmBuyAccessoryDlg, self.ConfirmBuyAccessoryDlg), + MENUS.BoughtKart: ('BoughtKartGuiDone', self.__handleBoughtKartDlg, self.BoughtKartDlg), + MENUS.BoughtAccessory: ('BoughtAccessoryGuiDone', self.__handleBoughtAccessoryDlg, self.BoughtAccessoryDlg)} + self.kartID = -1 + self.accID = -1 + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(aspect2d) + self.timer.posInTopRightCorner() + self.timer.accept('RESET_KARTSHOP_TIMER', self.__resetTimer) + self.timer.countdown(KartShopGlobals.KARTCLERK_TIMER, self.__timerExpired) + self.__doDialog(MENUS.MainMenu) + return + + def __resetTimer(self): + if hasattr(self, 'timer') and self.timer: + self.timer.stop() + self.timer.countdown(KartShopGlobals.KARTCLERK_TIMER, self.__timerExpired) + + def __isActive(self, dlgName): + for d in self.dialogStack: + if d == dlgName: + return True + + return False + + def __timerExpired(self): + messenger.send(self.eventDict['guiDone'], [True]) + + def destroy(self): + self.__destroyDialog() + self.ignoreAll() + self.timer.destroy() + del self.timer + for event in self.dialogEventDict.values(): + self.ignore(event) + + self.dialogEventDict = None + return + + def __destroyDialog(self): + if hasattr(self, 'timer'): + self.ignoreAll() + if self.dialog != None: + self.dialog.destroy() + self.dialog = None + return + + def __removeDialog(self, dlgName = None): + if self.dialog != None: + if dlgName != None: + for d in self.dialogStack: + if d == dlgName: + self.dialogStack.remove(d) + + return + + def __popDialog(self): + if self.dialog != None: + if dlgName != None: + for d in self.dialogStack: + if d == dlgName: + self.dialogStack.remove(d) + + else: + d = self.dialogStack.pop() + event = self.dialogEventDict.get(d) + eventType = event[0] + self.ignore(eventType) + if self.dialogStack: + self.__doDialog(self.dialogStack[-1]) + return + + def __doDialog(self, dialogType): + self.__destroyDialog() + messenger.send('wakeup') + event = self.dialogEventDict.get(dialogType) + eventType = event[0] + eventHandler = event[1] + eventDlg = event[2] + self.acceptOnce(eventType, eventHandler) + if dialogType == MENUS.ConfirmBuyKart: + self.dialog = eventDlg(eventType, self.kartID) + elif dialogType == MENUS.BoughtKart: + self.dialog = eventDlg(eventType, self.kartID) + elif dialogType == MENUS.ConfirmBuyAccessory: + self.dialog = eventDlg(eventType, self.accID) + elif dialogType == MENUS.BoughtAccessory: + self.dialog = eventDlg(eventType, self.accID) + else: + self.dialog = eventDlg(eventType) + self.lastMenu = dialogType + + def __handleMainMenuDlg(self, exitType, args = []): + self.notify.debug('__handleMainMenuDlg: Handling MainMenu Dialog Selection.') + if exitType == MM_OPTIONS.Cancel: + messenger.send(self.eventDict['guiDone']) + elif exitType == MM_OPTIONS.BuyKart: + self.__doDialog(MENUS.BuyKart) + elif exitType == MM_OPTIONS.BuyAccessory: + self.__doDialog(MENUS.BuyAccessory) + + def __handleBoughtKartDlg(self, exitType): + self.notify.debug('__handleBoughtKartDlg: Telling the player their purchase was successful') + if not hasattr(base.localAvatar, 'kartPage'): + base.localAvatar.addKartPage() + self.kartID = -1 + self.__doDialog(MENUS.MainMenu) + + def __handleBoughtAccessoryDlg(self, exitType): + self.notify.debug('__handleBoughtAccessoryDlg: Telling the player their purchase was successful') + self.accID = -1 + self.__doDialog(MENUS.BuyAccessory) + + def __handleBuyKartDlg(self, exitType, args = []): + self.notify.debug('__handleBuyKartDlg: Handling BuyKart Dialog Selection.') + if exitType == BK_OPTIONS.Cancel: + self.__doDialog(MENUS.MainMenu) + else: + self.kartID = exitType + if base.localAvatar.hasKart(): + self.__doDialog(MENUS.ReturnKart) + else: + self.__doDialog(MENUS.ConfirmBuyKart) + + def __handleBuyAccessoryDlg(self, exitType, args = []): + self.notify.debug('__handleBuyKartDlg: Handling BuyKart Dialog Selection.') + if exitType == BA_OPTIONS.Cancel: + self.__doDialog(MENUS.MainMenu) + else: + self.accID = exitType + self.__doDialog(MENUS.ConfirmBuyAccessory) + + def __handleReturnKartDlg(self, exitType, args = []): + self.notify.debug('__handleReturnKartDlg: Handling ReturnKart Dialog Selection.') + if exitType == RK_OPTIONS.Cancel: + self.__doDialog(MENUS.BuyKart) + elif exitType == RK_OPTIONS.ReturnKart: + self.__doDialog(MENUS.ConfirmBuyKart) + + def __handleConfirmBuyAccessoryDlg(self, exitType, args = []): + self.notify.debug('__handleConfirmBuyAccessoryDlg: Handling ConfirmBuyAccessory Dialog Selection.') + if exitType == CBA_OPTIONS.Cancel: + self.__doDialog(MENUS.BuyAccessory) + self.accID = -1 + elif exitType == CBA_OPTIONS.BuyAccessory: + if self.accID != -1: + messenger.send(self.eventDict['buyAccessory'], [self.accID]) + oldTickets = base.localAvatar.getTickets() + accInfo = getAccessoryInfo(self.accID) + cost = accInfo[AccInfo.cost] + base.localAvatar.setTickets(oldTickets - cost) + accList = base.localAvatar.getKartAccessoriesOwned() + accList.append(self.accID) + base.localAvatar.setKartAccessoriesOwned(accList) + self.accID = -1 + self.__doDialog(MENUS.BuyAccessory) + + def __handleConfirmBuyKartDlg(self, exitType, args = []): + self.notify.debug('__handleConfirmBuyKartDlg: Handling ConfirmBuyKart Dialog Selection.') + if exitType == CBK_OPTIONS.Cancel: + self.__doDialog(MENUS.BuyKart) + self.kartID = -1 + elif exitType == CBK_OPTIONS.BuyKart: + if self.kartID != -1: + messenger.send(self.eventDict['buyKart'], [self.kartID]) + self.__doDialog(MENUS.BoughtKart) + if __name__ == '__main__': + + class Main(DirectObject.DirectObject): + + def __init__(self): + self.acceptOnce('1', self.__popupKartShopGui) + self.accept(KartShopGlobals.EVENTDICT['buyAccessory'], self.__handleBuyAccessory) + self.accept(KartShopGlobals.EVENTDICT['buyKart'], self.__handleBuyKart) + + def __popupKartShopGui(self): + if not hasattr(self, 'kartShopGui') or self.kartShopGui == None: + self.acceptOnce(KartShopGlobals.EVENTDICT['guiDone'], self.__handleGuiDone) + self.kartShopGui = KartShopGuiMgr(KartShopGlobals.EVENTDICT) + return + + def __handleGuiDone(self, args = []): + if hasattr(self, 'kartShopGui') and self.kartShopGui != None: + self.ignoreAll() + self.kartShopGui.destroy() + del self.kartShopGui + self.acceptOnce('1', self.__popupKartShopGui) + return + + def __handleBuyAccessory(self, accID = -1): + requestAddOwnedAccessory(self, accID) + + def __handleBuyKart(self, kartID = -1): + pass + + m = Main() + run() diff --git a/toontown/racing/LeaderboardMgrAI.py b/toontown/racing/LeaderboardMgrAI.py new file mode 100644 index 00000000..dd122c24 --- /dev/null +++ b/toontown/racing/LeaderboardMgrAI.py @@ -0,0 +1,79 @@ +from otp.ai.MagicWordGlobal import * +from toontown.toonbase import TTLocalizer +import RaceGlobals, operator, time + +class LeaderboardMgrAI: + + def __init__(self, air): + self.air = air + if self.air.dbConn: + self.air.dbGlobalCursor.leaderboards.ensure_index([('ai', 1)]) + shard = {'ai': self.air.districtId} + doc = self.air.dbGlobalCursor.leaderboards.find_one(shard) + if not doc: + self.database = ({}) + else: + self.database = doc.get('database', ({})) + + else: + self.database = simbase.backups.load('leaderboard', (self.air.districtId,), default=({})) + + def getDatabase(self): + return self.database + + def saveDatabase(self): + if self.air.dbConn: + shard = {'ai': self.air.districtId} + self.air.dbGlobalCursor.leaderboards.update(shard, + {'$setOnInsert': shard, + '$set': {'database': self.database}}, + upsert = True) + else: + simbase.backups.save('leaderboard', (self.air.districtId,), self.database) + messenger.send('goofyLeaderboardChange') + + def trimList(self, list): + return list[:RaceGlobals.NumRecordsPerPeriod] + + def clearRace(self, race): + if race in self.database: + del self.database[race] + self.saveDatabase() + + def submitRace(self, raceId, name, timestamp): + for i in xrange(len(TTLocalizer.RecordPeriodStrings)): + race = '%s, %s' % (raceId, i) + + if race in self.database: + originalRace = self.database[race][1] + newRace = list(originalRace) + + newRace.append([name, timestamp]) + sortedRace = self.trimList(sorted(newRace, key=operator.itemgetter(1))) + + if originalRace != sortedRace: + self.database[race][1] = sortedRace + self.saveDatabase() + else: + self.database[race] = [time.time(), [(name, timestamp)]] + self.saveDatabase() + +@magicWord(category=CATEGORY_PROGRAMMER, types=[str, int, int, str, int]) +def leaderboard(command, raceId=0, type=0, name='', time=0): + command = command.lower() + race = '%s, %s' % (raceId, type) + + if command == 'clear': + simbase.air.leaderboardMgr.clearRace(race) + return 'Cleared race %s!' % race + elif command == 'submit': + simbase.air.leaderboardMgr.submitRace(raceId, name, time) + return 'Submitted race %s for %s with %s seconds!' % (raceId, name, time) + elif command == 'refresh': + messenger.send('goofyLeaderboardChange') + return 'Refreshed leaderboards!' + elif command == 'change': + messenger.send('goofyLeaderboardDisplay', [raceId]) + return 'Made all leaderboards show %s!' % raceId + else: + return 'Unknown command! Commands:\n- clear\n- submit\n- refresh\n- change' diff --git a/toontown/racing/Piejectile.py b/toontown/racing/Piejectile.py new file mode 100755 index 00000000..7de3ef99 --- /dev/null +++ b/toontown/racing/Piejectile.py @@ -0,0 +1,245 @@ +import math +import random +from direct.showbase.PythonUtil import * +from direct.showbase.DirectObject import DirectObject +from direct.task import Task +from panda3d.core import * +from direct.fsm import FSM +from direct.distributed import DistributedSmoothNode +from otp.avatar import ShadowCaster +from otp.otpbase import OTPGlobals +from toontown.racing.FlyingGag import FlyingGag +from toontown.battle import MovieUtil + +class Piejectile(DirectObject, FlyingGag): + physicsCalculationsPerSecond = 60 + maxPhysicsDt = 1.0 + physicsDt = 1.0 / float(physicsCalculationsPerSecond) + maxPhysicsFrames = maxPhysicsDt * physicsCalculationsPerSecond + + def __init__(self, sourceId, targetId, type, name): + FlyingGag.__init__(self, 'flyingGag', base.race.pie) + self.billboard = False + self.race = base.race + self.scale = 1 + self.imHitMult = 1 + self.wallCollideTrack = None + self.curSpeed = 0 + self.acceleration = 0 + self.count = 0 + self.name = name + self.physicsObj = None + self.ownerId = sourceId + self.targetId = targetId + self.ownerAv = None + self.ownerKart = None + self.hasTarget = 0 + self.deleting = 0 + self.d2t = 0 + self.lastD2t = 0 + self.startTime = globalClock.getFrameTime() + self.timeRatio = 0 + self.maxTime = 8 + self.rotH = randFloat(-360, 360) + self.rotP = randFloat(-90, 90) + self.rotR = randFloat(-90, 90) + self.ownerKart = base.cr.doId2do.get(base.race.kartMap.get(sourceId, None), None) + if targetId != 0: + self.targetKart = base.cr.doId2do.get(base.race.kartMap.get(targetId, None), None) + self.hasTarget = 1 + if self.ownerId == localAvatar.doId: + startPos = self.ownerKart.getPos(render) + else: + startPos = self.ownerKart.getPos(render) + self.setPos(startPos[0], startPos[1], startPos[2]) + self.__setupCollisions() + self.setupPhysics() + self.__enableCollisions() + self.forward = NodePath('forward') + self.forward.setPos(0, 1, 0) + self.splatTaskName = 'splatTask %s' % self.name + if self.hasTarget: + self.splatTask = taskMgr.doMethodLater(self.maxTime, self.splat, self.splatTaskName) + else: + self.splatTask = taskMgr.doMethodLater(self.maxTime / 2.5, self.splat, self.splatTaskName) + self.reparentTo(render) + + def delete(self): + taskMgr.remove(self.taskName) + self.__undoCollisions() + self.physicsMgr.clearLinearForces() + FlyingGag.delete(self) + self.deleting = 1 + self.ignoreAll() + + def remove(self): + self.delete() + + def setAvId(self, avId): + self.avId = avId + + def setRace(self, doId): + self.race = base.cr.doId2do.get(doId) + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + + def setType(self, type): + self.type = type + + def setPos(self, x, y, z): + DistributedSmoothNode.DistributedSmoothNode.setPos(self, x, y, z) + + def getVelocity(self): + return self.actorNode.getPhysicsObject().getVelocity() + + def setupPhysics(self): + self.physicsMgr = PhysicsManager() + self.physicsEpoch = globalClock.getFrameTime() + self.lastPhysicsFrame = 0 + integrator = LinearEulerIntegrator() + self.physicsMgr.attachLinearIntegrator(integrator) + fn = ForceNode('windResistance') + fnp = NodePath(fn) + fnp.reparentTo(render) + windResistance = LinearFrictionForce(0.2) + fn.addForce(windResistance) + self.physicsMgr.addLinearForce(windResistance) + self.windResistance = windResistance + fn = ForceNode('engine') + fnp = NodePath(fn) + fnp.reparentTo(self) + engine = LinearVectorForce(0, 0, 3) + fn.addForce(engine) + self.physicsMgr.addLinearForce(engine) + self.engine = engine + self.physicsMgr.attachPhysicalNode(self.node()) + self.physicsObj = self.actorNode.getPhysicsObject() + ownerAv = base.cr.doId2do[self.ownerId] + ownerVel = self.ownerKart.getVelocity() + ownerSpeed = ownerVel.length() + rotMat = Mat3.rotateMatNormaxis(self.ownerKart.getH(), Vec3.up()) + ownerHeading = rotMat.xform(Vec3.forward()) + throwSpeed = 50 + throwVel = ownerHeading * throwSpeed + throwVelCast = Vec3(throwVel[0], throwVel[1], throwVel[2] + 50) + self.actorNode.getPhysicsObject().setVelocity(self.ownerKart.getVelocity() + throwVelCast) + lookPoint = render.getRelativePoint(self.ownerKart, Point3(0, 10, 0)) + self.lookAt(lookPoint) + self.taskName = 'updatePhysics%s' % self.name + taskMgr.add(self.__updatePhysics, self.taskName, priority=25) + + def checkTargetDistance(self): + if self.hasTarget: + return self.getDistance(self.targetKart) + else: + return 0 + + def splatTarget(self): + if self.targetId == base.localAvatar.getDoId() and base.race.localKart: + base.race.localKart.splatPie() + self.race.effectManager.addSplatEffect(spawner=self.targetKart, parent=self.targetKart) + taskMgr.remove(self.splatTaskName) + self.removeNode() + self.remove() + + def splat(self, optional = None): + self.race.effectManager.addSplatEffect(spawner=self) + taskMgr.remove(self.splatTaskName) + self.removeNode() + self.remove() + + def __updatePhysics(self, task): + if self.deleting: + return Task.done + self.timeRatio = (globalClock.getFrameTime() - self.startTime) / self.maxTime + self.windResistance.setCoef(0.2 + 0.8 * self.timeRatio) + if base.cr.doId2do.get(self.targetId) == None: + self.hasTarget = 0 + self.lastD2t = self.d2t + self.d2t = self.checkTargetDistance() + if self.hasTarget: + targetDistance = self.d2t + distMax = 100 + if self.d2t > distMax: + targetDistance = distMax + targetVel = self.targetKart.getVelocity() + targetPos = self.targetKart.getPos() + targetAim = Point3(targetPos[0] + targetVel[0] * (targetDistance / distMax), targetPos[1] + targetVel[1] * (targetDistance / distMax), targetPos[2] + targetVel[2] * (targetDistance / distMax)) + self.lookAt(targetPos) + if self.d2t < 7 and self.hasTarget: + self.splatTarget() + return Task.done + self.count += 1 + dt = globalClock.getDt() + physicsFrame = int((globalClock.getFrameTime() - self.physicsEpoch) * self.physicsCalculationsPerSecond) + numFrames = min(physicsFrame - self.lastPhysicsFrame, self.maxPhysicsFrames) + self.lastPhysicsFrame = physicsFrame + if self.hasTarget: + targetVel = self.targetKart.getVelocity() + targetSpeed = targetVel.length() + if self.d2t - 10 * self.physicsDt > self.lastD2t: + self.engine.setVector(Vec3(0, 150 + 150 * self.timeRatio + targetSpeed * (1.0 + 1.0 * self.timeRatio) + self.d2t * (1.0 + 1.0 * self.timeRatio), 12)) + else: + self.engine.setVector(Vec3(0, 10 + 10 * self.timeRatio + targetSpeed * (0.5 + 0.5 * self.timeRatio) + self.d2t * (0.5 + 0.5 * self.timeRatio), 12)) + else: + self.engine.setVector(Vec3(0, 100, 3)) + for i in xrange(int(numFrames)): + pitch = self.gagNode.getP() + self.gagNode.setP(pitch + self.rotH * self.physicsDt) + roll = self.gagNode.getR() + self.gagNode.setR(roll + self.rotP * self.physicsDt) + heading = self.gagNode.getH() + self.gagNode.setH(heading + self.rotR * self.physicsDt) + self.physicsMgr.doPhysics(self.physicsDt) + + if self.count % 60 == 0: + pass + return Task.cont + + def __setupCollisions(self): + self.cWallTrav = CollisionTraverser('ProjectileWall') + self.cWallTrav.setRespectPrevTransform(True) + self.collisionNode = CollisionNode(self.name) + self.collisionNode.setFromCollideMask(OTPGlobals.WallBitmask) + self.collisionNode.setIntoCollideMask(BitMask32.allOff()) + cs = CollisionSphere(0, 0, 0, 7) + self.collisionNode.addSolid(cs) + sC = self.attachNewNode(self.collisionNode) + self.collisionNodePath = NodePath(self.collisionNode) + self.wallHandler = CollisionHandlerPusher() + base.cTrav.addCollider(sC, self.wallHandler) + self.wallHandler.addCollider(self.collisionNodePath, self) + cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0) + pieFloorRayName = 'pieFloorRay%s' % self.name + cRayNode = CollisionNode(pieFloorRayName) + cRayNode.addSolid(cRay) + cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + cRayNode.setIntoCollideMask(BitMask32.allOff()) + self.cRayNodePath = self.attachNewNode(cRayNode) + self.lifter = CollisionHandlerGravity() + self.lifter.setGravity(32.174 * 3.0) + self.lifter.setOffset(OTPGlobals.FloorOffset) + self.lifter.setReach(40.0) + self.lifter.addCollider(self.cRayNodePath, self) + base.cTrav.addCollider(self.cRayNodePath, self.lifter) + + def __undoCollisions(self): + base.cTrav.removeCollider(self.cRayNodePath) + + def __enableCollisions(self): + self.cQueue = [] + self.cRays = NodePath('stickProjectileToFloor') + self.cRays.reparentTo(self.gag) + x = self.gag.getX() + y = self.gag.getY() + rayNode = CollisionNode('floorcast') + ray = CollisionRay(x, y, 40000.0, 0.0, 0.0, -1.0) + rayNode.addSolid(ray) + rayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + rayNode.setIntoCollideMask(BitMask32.allOff()) + rayNodePath = self.cRays.attachNewNode(rayNode) + cQueue = CollisionHandlerQueue() + self.cWallTrav.addCollider(rayNodePath, cQueue) + self.cQueue.append(cQueue) + self.collisionNodePath.reparentTo(self) diff --git a/toontown/racing/PiejectileManager.py b/toontown/racing/PiejectileManager.py new file mode 100755 index 00000000..5583cd92 --- /dev/null +++ b/toontown/racing/PiejectileManager.py @@ -0,0 +1,27 @@ +from panda3d.core import * +from direct.showbase.DirectObject import DirectObject +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import * +from toontown.racing import Piejectile + +class PiejectileManager(DirectObject): + pieCounter = 0 + + def __init__(self): + self.piejectileList = [] + + def delete(self): + for piejectile in self.piejectileList: + self.__removePiejectile(piejectile) + + def addPiejectile(self, sourceId, targetId = 0, type = 0): + name = 'PiejectileManager Pie %s' % PiejectileManager.pieCounter + pie = Piejectile.Piejectile(sourceId, targetId, type, name) + self.piejectileList.append(pie) + PiejectileManager.pieCounter += 1 + + def __removePiejectile(self, piejectile): + self.piejectileList.remove(piejectile) + piejectile.delete() + piejectile = None + return diff --git a/toontown/racing/RaceEndPanels.py b/toontown/racing/RaceEndPanels.py new file mode 100755 index 00000000..2f424fc8 --- /dev/null +++ b/toontown/racing/RaceEndPanels.py @@ -0,0 +1,410 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.racing import RaceGlobals +from toontown.shtiker.KartPage import RacingTrophy +from toontown.racing import RaceGlobals + +class RaceResultsPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('RaceEndPanels') + + def __init__(self, numRacers, race, raceEndPanel, *args, **kwargs): + opts = {'relief': None, + 'geom': DGG.getDefaultDialogGeom(), + 'geom_color': ToontownGlobals.GlobalDialogColor[:3] + (0.8,), + 'geom_scale': (1.75, 1, 0.75)} + opts.update(kwargs) + DirectFrame.__init__(self, *args, **opts) + self.initialiseoptions(RaceResultsPanel) + self.entryList = [] + self.entryListSeqs = [] + self.pointSeqs = [] + self.numRacers = numRacers + base.resultsPanel = self + self.circuitFinishSeq = None + self.tickets = {} + self.race = race + self.raceEndPanel = raceEndPanel + self.pointsLabel = DirectLabel(parent=self, relief=None, pos=(0.7, 0, 0.3), text=TTLocalizer.KartRace_CircuitPoints, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPsmallLabel) + self.pointsLabel.hide() + self.rowFrame = [] + for x in xrange(self.numRacers): + frame = DirectFrame(parent=self, relief=None, pos=self.getRowPos(x)) + self.rowFrame.append(frame) + pLabel = DirectLabel(parent=frame, relief=None, pos=(0.0, 0.0, -0.01), text=`(x + 1)` + ' -', text_fg=(0.5, 0.5, 0.5, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + fFrame = DirectFrame(parent=frame, relief=None, pos=(0.1, -0.01, 0.01)) + nLabel = DirectLabel(parent=frame, relief=None, pos=(0.46, 0.0, 0.0), text='', text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPsmallLabel, text_align=TextNode.ACenter, text_font=DGG.getDefaultFont()) + tLabel = DirectLabel(parent=frame, relief=None, pos=(0.9, 0.0, 0.0), text="--'--''--", text_fg=(0.5, 0.5, 0.5, 1.0), text_scale=TTLocalizer.REPsmallLabel, text_font=DGG.getDefaultFont()) + wLabel = DirectLabel(parent=frame, relief=None, pos=(1.14, 0.0, 0.0), text='', text_fg=(0, 0, 0, 1), text_scale=TTLocalizer.REPsmallLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + cpLabel = DirectLabel(parent=frame, relief=None, pos=(1.4, 0.0, 0.0), text='', text_fg=(0, 0, 0, 1), text_scale=TTLocalizer.REPsmallLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + ncpLabel = DirectLabel(parent=frame, relief=None, pos=(1.44, 0.0, 0.0), text='', text_fg=(1, 0, 0, 1), text_scale=TTLocalizer.REPsmallLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + self.entryList.append((pLabel, + fFrame, + nLabel, + tLabel, + wLabel, + cpLabel, + ncpLabel)) + + return + + def getRowPos(self, place): + return Point3(-0.72, -0.01, 0.25 - place * 0.18) + + def displayRacer(self, place, headFrame, name, time, qualify, tickets, bonus, trophies, circuitPoints, circuitTime): + self.notify.debug('displayRacer: place=%d name=%s tickets=%d bonus=%d' % (place, + name, + tickets, + bonus)) + self.tickets[place] = tickets + self.entryList[place - 1][0].configure(text_fg=(0.0, 0.0, 0.0, 1.0)) + if headFrame: + headFrame.reparentTo(self.entryList[place - 1][1]) + headFrame.setPos(0, -1.0, 0) + headFrame.setScale(0.25) + headFrame.show() + self.entryList[place - 1][2]['text'] = name + if len(name) > 24: + self.entryList[place - 1][2]['text_scale'] = 0.036 + else: + self.entryList[place - 1][2]['text_scale'] = 0.04 + minutes = int(time / 60) + time -= minutes * 60 + seconds = int(time) + padding = (seconds < 10 and ['0'] or [''])[0] + time -= seconds + fraction = str(time)[2:4] + fraction = fraction + '0' * (2 - len(fraction)) + timeStr = "%d'%s%d''%s" % (minutes, + padding, + seconds, + fraction) + self.entryList[place - 1][3].configure(text_fg=(0.0, 0.0, 0.0, 1.0)) + self.entryList[place - 1][3]['text'] = timeStr + + def flipText(flip, label = self.entryList[place - 1][3], timeStr = timeStr, recStr = TTLocalizer.KartRace_Record): + self.entryList[place - 1][3].configure(text_scale=0.06) + self.entryList[place - 1][3].configure(text_fg=(0.95, 0.0, 0.0, 1.0)) + if flip: + self.entryList[place - 1][3].configure(text_font=DGG.getDefaultFont()) + self.entryList[place - 1][3]['text'] = timeStr + else: + self.entryList[place - 1][3].configure(text_font=ToontownGlobals.getSignFont()) + self.entryList[place - 1][3]['text'] = recStr + + bonusSeq = Sequence() + if qualify and bonus: + qText = TTLocalizer.KartRace_Qualified + for i in xrange(1, 7): + bonusSeq.append(Func(flipText, 0, recStr=qText)) + bonusSeq.append(Wait(0.5)) + bonusSeq.append(Func(flipText, 1)) + bonusSeq.append(Wait(0.5)) + bonusSeq.append(Func(flipText, 0)) + bonusSeq.append(Wait(0.5)) + bonusSeq.append(Func(flipText, 1)) + bonusSeq.append(Wait(0.5)) + + elif qualify: + qText = TTLocalizer.KartRace_Qualified + for i in xrange(0, 12): + bonusSeq.append(Func(flipText, i % 2, recStr=qText)) + bonusSeq.append(Wait(0.5)) + + elif bonus: + for i in xrange(0, 12): + bonusSeq.append(Func(flipText, i % 2)) + bonusSeq.append(Wait(0.5)) + + if trophies: + DirectFrame(parent=headFrame, relief=None, image=loader.loadModel('phase_6/models/karting/trophy'), image_pos=(0.25, -1.01, -0.25), image_scale=0.25) + + def ticketTicker(t, label = self.entryList[place - 1][4], tickets = tickets): + label['text'] = TTLocalizer.KartRace_TicketPhrase % int(t * tickets) + + ticketSeq = LerpFunc(ticketTicker, duration=2) + displayPar = Parallel(bonusSeq, ticketSeq) + displayPar.start() + self.entryListSeqs.append(displayPar) + if circuitPoints: + self.pointsLabel.show() + newPoints = circuitPoints[1] + currentPoints = circuitPoints[0] + self.entryList[place - 1][5]['text'] = '%s' % currentPoints + self.entryList[place - 1][6]['text'] = ' + %s' % newPoints + + def totalPointTicker(t, label = self.entryList[place - 1][5], current = currentPoints, new = newPoints): + label['text'] = '%s' % int(currentPoints + t * new) + + def newPointTicker(t, label = self.entryList[place - 1][6], new = newPoints): + label['text'] = '+%s' % int(new - t * new) + + def endTicker(newLabel = self.entryList[place - 1][6]): + newLabel.hide() + + seq = Sequence(Wait(1), Parallel(LerpFunc(totalPointTicker, duration=1), LerpFunc(newPointTicker, duration=1)), Func(endTicker)) + self.pointSeqs.append(seq) + + def updateWinnings(self, place, newTotalTickets): + self.notify.debug('updateWinnings: self.tickets=%s place=%d newTotalTickets=%d' % (self.tickets, place, newTotalTickets)) + winnings = newTotalTickets - self.tickets[place] + if winnings != 0: + self.entryListSeqs[place - 1].finish() + newTickets = self.tickets[place] + winnings + + def ticketTicker(t, label = self.entryList[place - 1][4], tickets = newTickets): + label['text'] = TTLocalizer.KartRace_TicketPhrase % int(t * tickets) + + ticketSeq = LerpFunc(ticketTicker, duration=2) + ticketSeq.start() + self.entryListSeqs[place - 1] = ticketSeq + + def circuitFinished(self, placeFixup): + calcPointsSeq = Parallel() + for seq in self.pointSeqs: + calcPointsSeq.append(seq) + + shiftRacersSeq = Parallel() + for oldPlace, newPlace in placeFixup: + if oldPlace != newPlace: + + def fixPlaceValue(oldPlace = oldPlace, newPlace = newPlace): + self.entryList[oldPlace][0]['text'] = '%s -' % str(newPlace + 1) + + newPos = self.getRowPos(newPlace) + shiftRacersSeq.append(Parallel(Func(fixPlaceValue), LerpPosInterval(self.rowFrame[oldPlace], 1, newPos))) + + self.circuitFinishSeq = Sequence(calcPointsSeq, shiftRacersSeq) + if len(self.race.circuitLoop) > 1: + self.notify.debug('Not the last race in a circuit, pressing next race in 30 secs') + self.circuitFinishSeq.append(Wait(30)) + self.circuitFinishSeq.append(Func(self.raceEndPanel.closeButtonPressed)) + self.circuitFinishSeq.start() + + def destroy(self): + for seq in self.entryListSeqs: + seq.finish() + del seq + + del self.entryListSeqs + if self.circuitFinishSeq: + self.circuitFinishSeq.finish() + del self.circuitFinishSeq + del self.entryList + del self.pointsLabel + DirectFrame.destroy(self) + + +class RaceWinningsPanel(DirectFrame): + + def __init__(self, race, *args, **kwargs): + opts = {'relief': None, + 'geom': DGG.getDefaultDialogGeom(), + 'geom_color': ToontownGlobals.GlobalDialogColor[:3] + (0.8,), + 'geom_scale': (1.75, 1, 0.75)} + opts.update(kwargs) + DirectFrame.__init__(self, *args, **opts) + self.initialiseoptions(RaceWinningsPanel) + self.race = race + frame = DirectFrame(parent=self, relief=None, pos=(0, -0.01, 0)) + tFrame = DirectFrame(parent=frame, relief=None, pos=(0, 0, 0)) + tLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.0, 0.0, 0.25), text=TTLocalizer.KartRace_Tickets, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=0.1, text_font=DGG.getDefaultFont()) + DirectLabel(parent=tFrame, relief=None, pos=(TTLocalizer.REPtextPosX, 0.0, 0.1), text=TTLocalizer.KartRace_Deposit + TTLocalizer.KartRace_Colon, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + dLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.05, 0.0, 0.1), text=TTLocalizer.KartRace_Zero, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + DirectLabel(parent=tFrame, relief=None, pos=(TTLocalizer.REPtextPosX, 0.0, 0.0), text=TTLocalizer.KartRace_Winnings + TTLocalizer.KartRace_Colon, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + wLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.05, 0.0, 0.0), text=TTLocalizer.KartRace_Zero, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + DirectLabel(parent=tFrame, relief=None, pos=(TTLocalizer.REPtextPosX, 0.0, -0.1), text=TTLocalizer.KartRace_Bonus + TTLocalizer.KartRace_Colon, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + bLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.05, 0.0, -0.1), text=TTLocalizer.KartRace_Zero, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + self.raceTotalLabel = DirectLabel(parent=tFrame, relief=None, pos=(TTLocalizer.REPtextPosX, 0.0, -0.2), text=TTLocalizer.KartRace_RaceTotal + TTLocalizer.KartRace_Colon, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + self.circuitTotalLabel = DirectLabel(parent=tFrame, relief=None, pos=(TTLocalizer.REPtextPosX, 0.0, -0.2), text=TTLocalizer.KartRace_CircuitTotal + TTLocalizer.KartRace_Colon, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + self.doubleTicketsLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.5, 0.0, -0.26), text=TTLocalizer.KartRace_DoubleTickets, text_fg=(1.0, 0.125, 0.125, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ACenter, text_shadow=(0, 0, 0, 1), text_font=DGG.getDefaultFont()) + fLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.05, 0.0, -0.2), text=TTLocalizer.KartRace_Zero, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + ticketPic = DirectFrame(parent=tFrame, relief=None, image=loader.loadModel('phase_6/models/karting/tickets'), image_pos=(0.5, 0, -0.02), image_scale=0.4) + self.ticketFrame = tFrame + self.ticketComponents = (dLabel, + wLabel, + bLabel, + fLabel) + tFrame = DirectFrame(parent=frame, relief=None, pos=(0, 0, 0)) + tLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.0, 0.0, 0.25), text=TTLocalizer.KartRace_Bonus, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=0.1, text_font=DGG.getDefaultFont()) + textFrame = DirectFrame(parent=tFrame, relief=None, text='', text_scale=TTLocalizer.REPlargeLabel, text_font=DGG.getDefaultFont(), text_pos=(-0.3, 0.1, 0)) + bonusPic = DirectFrame(parent=tFrame, relief=None, image=loader.loadModel('phase_6/models/karting/tickets'), image_pos=(0.5, 0, -0.02), image_scale=0.4) + self.bonusFrame = tFrame + self.bonusComponents = (textFrame, bonusPic) + tFrame = DirectFrame(parent=frame, relief=None, pos=(0, 0, 0)) + tLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.0, 0.0, 0.25), text=TTLocalizer.KartRace_Trophies, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=0.1, text_font=DGG.getDefaultFont()) + textFrame = DirectFrame(parent=tFrame, relief=None, text='', text_scale=TTLocalizer.REPlargeLabel, text_font=DGG.getDefaultFont(), text_pos=(-0.3, 0.1, 0)) + trophyPic = DirectFrame(parent=tFrame, relief=None) + self.trophyFrame = tFrame + self.trophyComponents = (textFrame, trophyPic) + self.ticketFrame.hide() + self.bonusFrame.hide() + self.trophyFrame.hide() + return + + def generateDisplaySequences(self, track, ticDeposit, ticWon, ticBonus, trophies, endOfCircuitRace = False): + + def ticketTicker(t, label, startTickets, endTickets): + label['text'] = str(int(t * endTickets + (1 - t) * startTickets)) + + def wrapStr(str = '', maxWidth = 10, font = DGG.getDefaultFont()): + textNode = TextNode('WidthChecker') + textNode.setFont(font) + tokens = str.split() + outStr = '' + lineStr = '' + tempStr = '' + while tokens: + if lineStr: + tempStr = ' '.join([lineStr, tokens[0]]) + else: + tempStr = tokens[0] + if textNode.calcWidth(tempStr) > maxWidth: + if not outStr: + outStr = lineStr + else: + outStr = '\n'.join([outStr, lineStr]) + lineStr = tokens.pop(0) + else: + lineStr = tempStr + tokens.pop(0) + + if lineStr: + if not outStr: + outStr = lineStr + else: + outStr = '\n'.join([outStr, lineStr]) + return outStr + + ticketSeq = Sequence() + origTicBonus = ticBonus + bonusType = None + if ticBonus > 0: + if not endOfCircuitRace: + bonusType = RaceGlobals.PeriodDict.values().index(ticBonus) + else: + ticBonus = 0 + if endOfCircuitRace: + self.circuitTotalLabel.unstash() + self.raceTotalLabel.stash() + else: + self.circuitTotalLabel.stash() + self.raceTotalLabel.unstash() + if (not base.cr.newsManager.isHolidayRunning(ToontownGlobals.KARTING_TICKETS_HOLIDAY)) or self.race.raceType != RaceGlobals.Practice: + self.doubleTicketsLabel.stash() + if ticBonus: + ticketSeq.append(Sequence(Func(self.ticketFrame.hide), Func(self.bonusFrame.show), Func(self.trophyFrame.hide), Func(self.bonusComponents[0].configure, text=wrapStr(TTLocalizer.KartRace_RecordString % (TTLocalizer.KartRecordStrings[bonusType], TTLocalizer.KartRace_TrackNames[track], str(ticBonus)))), Wait(3))) + ticketSeq.append(Sequence(Func(self.bonusFrame.hide), Func(self.trophyFrame.hide), Func(self.ticketFrame.show), Func(self.ticketComponents[3].configure, text_color=Vec4(0, 0, 0, 1)), Func(self.ticketComponents[0].configure, text_color=Vec4(1, 0, 0, 1)), Parallel(LerpFunc(ticketTicker, duration=(ticDeposit and [1] or [0])[0], extraArgs=[self.ticketComponents[0], 0, ticDeposit]), LerpFunc(ticketTicker, duration=(ticDeposit and [1] or [0])[0], extraArgs=[self.ticketComponents[3], 0, ticDeposit])), Func(self.ticketComponents[0].configure, text_color=Vec4(0, 0, 0, 1)), Func(self.ticketComponents[1].configure, text_color=Vec4(1, 0, 0, 1)), Parallel(LerpFunc(ticketTicker, duration=(ticWon and [1] or [0])[0], extraArgs=[self.ticketComponents[1], 0, ticWon]), LerpFunc(ticketTicker, duration=(ticWon and [1] or [0])[0], extraArgs=[self.ticketComponents[3], ticDeposit, ticDeposit + ticWon])), Func(self.ticketComponents[1].configure, text_color=Vec4(0, 0, 0, 1)), Func(self.ticketComponents[2].configure, text_color=Vec4(1, 0, 0, 1)), Parallel(LerpFunc(ticketTicker, duration=(origTicBonus and [1] or [0])[0], extraArgs=[self.ticketComponents[2], 0, origTicBonus]), LerpFunc(ticketTicker, duration=(origTicBonus and [1] or [0])[0], extraArgs=[self.ticketComponents[3], ticDeposit + ticWon, ticDeposit + ticWon + origTicBonus])), Func(self.ticketComponents[2].configure, text_color=Vec4(0, 0, 0, 1)), Func(self.ticketComponents[3].configure, text_color=Vec4(1, 0, 0, 1)))) + winningsSeq = Sequence(Func(self.ticketFrame.show), Func(self.bonusFrame.hide), Func(self.trophyFrame.hide), Wait(5)) + if ticBonus: + winningsSeq.append(Sequence(Func(self.ticketFrame.hide), Func(self.bonusFrame.show), Func(self.trophyFrame.hide), Wait(5))) + + def showCorrectTrophy(trophyId): + if hasattr(self, 'trophyImage'): + self.trophyImage.destroy() + self.trophyImage = RacingTrophy(level=trophyId, parent=self.trophyComponents[1], pos=(0.5, 0, -0.25)) + if trophyId == RaceGlobals.GrandTouring or trophyId == RaceGlobals.TotalQuals or trophyId == RaceGlobals.TotalWins: + scale = self.trophyImage.getScale() + scale = scale * 0.5 + self.trophyImage.setScale(scale) + + base.trop = self + if trophies: + winningsSeq.append(Sequence(Func(self.ticketFrame.hide), Func(self.bonusFrame.hide), Func(self.trophyFrame.show))) + for x in trophies: + winningsSeq.append(Sequence(Func(self.trophyComponents[0].configure, text=wrapStr(TTLocalizer.KartTrophyDescriptions[x])), Func(showCorrectTrophy, x), Wait(5))) + + return (ticketSeq, winningsSeq) + + +class RaceEndPanel(DirectFrame): + + def __init__(self, numRacers, race, *args, **kwargs): + opts = {'relief': None} + opts.update(kwargs) + DirectFrame.__init__(self, *args, **opts) + self.initialiseoptions(RaceEndPanel) + self.enabled = False + self.race = race + self.results = RaceResultsPanel(numRacers, race, self, parent=self, pos=(0, 0, 0.525)) + self.winnings = RaceWinningsPanel(race, parent=self, pos=(0, 0, -0.525)) + if len(self.race.circuitLoop) <= 1: + exitText = TTLocalizer.KartRace_Exit + else: + exitText = TTLocalizer.KartRace_NextRace + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.closeButton = DirectButton(parent=self, image=(gui.find('**/CloseBtn_UP'), + gui.find('**/CloseBtn_DN'), + gui.find('**/CloseBtn_Rllvr'), + gui.find('**/CloseBtn_UP')), relief=None, scale=2.0, text=exitText, text_scale=TTLocalizer.REPsmallLabel, text_pos=(0, -0.1), text_fg=VBase4(1, 1, 1, 1), pos=(1.1, 0, -0.5), command=self.closeButtonPressed) + self.closeButton.hide() + self.disable() + return + + def destroy(self): + taskMgr.remove('showExitButton') + try: + if self.seq: + self.seq.pause() + self.seq = None + except AttributeError: + pass + + DirectFrame.destroy(self) + return + + def enable(self): + self.show() + self.enabled = True + + def disable(self): + self.hide() + self.enabled = False + + def closeButtonPressed(self): + messenger.send('leaveRace') + + def displayRacer(self, place, entryFee, qualify, winnings, track, bonus, trophies, headFrame, name, time, circuitPoints, circuitTime): + self.results.displayRacer(place, headFrame, name, time, qualify, entryFee + winnings + bonus, bonus, trophies, circuitPoints, circuitTime) + + def updateWinnings(self, place, winnings): + self.results.updateWinnings(place, winnings) + + def updateWinningsFromCircuit(self, place, entryFee, winnings, bonus, trophies = ()): + self.seq.finish() + self.results.updateWinnings(place, winnings + entryFee + bonus) + self.startWinningsPanel(entryFee, winnings, 0, bonus, trophies, True) + + def startWinningsPanel(self, entryFee, winnings, track, bonus = None, trophies = (), endOfCircuitRace = False): + if not self.enabled: + return + taskMgr.remove('showExitButton') + try: + if self.seq: + self.seq.pause() + self.seq = None + except AttributeError: + pass + + tSeq, wSeq = self.winnings.generateDisplaySequences(track, entryFee, winnings, bonus, trophies, endOfCircuitRace) + + def showButton(s = self, w = wSeq): + s.seq.finish() + s.seq = Sequence(Func(self.closeButton.show), wSeq) + s.seq.loop() + + self.seq = Sequence(tSeq, wSeq) + if self.race.raceType != RaceGlobals.Circuit: + if self.seq.getDuration() < 5.0: + taskMgr.doMethodLater(5.0, showButton, 'showExitButton', extraArgs=[]) + else: + taskMgr.doMethodLater(self.seq.getDuration(), showButton, 'showExitButton', extraArgs=[]) + self.seq.start() + return + + def circuitFinished(self, placeFixup): + self.closeButton.show() + self.results.circuitFinished(placeFixup) diff --git a/toontown/racing/RaceGUI.py b/toontown/racing/RaceGUI.py new file mode 100755 index 00000000..cde41644 --- /dev/null +++ b/toontown/racing/RaceGUI.py @@ -0,0 +1,489 @@ +from panda3d.core import * +from toontown.toonbase.PythonUtil import clampScalar +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectLabel import * +from direct.gui.DirectButton import * +from direct.showbase import BulletinBoardWatcher +from direct.interval.IntervalGlobal import * +from otp.otpbase import OTPGlobals +from direct.interval.IntervalGlobal import * +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.racing.KartDNA import InvalidEntry, getAccessory, getDefaultColor +from toontown.racing.RaceHeadFrame import RaceHeadFrame +from toontown.racing.RaceGag import RaceGag +from toontown.racing.RaceEndPanels import RaceEndPanel +from toontown.racing import RaceGlobals +from pandac.PandaModules import CardMaker, OrthographicLens, LineSegs +from direct.particles.ParticleEffect import * +from math import fmod +from math import sqrt +import time +import operator +from direct.gui.DirectGui import DirectFrame +import random + +class RaceGUI: + GagPie = 0 + gagRoot = 'phase_3.5/maps/inventory_' + + class RacerInfo: + + def __init__(self, face, mapSpot): + self.curvetime = 0 + self.maxlaphit = 0 + self.face = face + self.mapspot = mapSpot + self.place = 1 + self.enabled = True + self.finished = False + self.gag = None + return + + def update(self, curvetime = None, maxlaphit = None, faceX = None, mapspotPt = None, place = None, finished = None): + if self.enabled: + if not curvetime == None: + self.curvetime = curvetime + if not maxlaphit == None: + self.maxlaphit = maxlaphit + if not faceX == None: + self.face.setX(faceX) + if not mapspotPt == None: + self.mapspot.setPos(mapspotPt) + if not place == None: + self.place = place + if not finished == None: + self.finished = finished + return + + def disable(self): + self.enabled = False + if not self.finished: + self.face.hide() + self.mapspot.hide() + + def enable(self): + self.enabled = True + self.face.show() + self.mapspot.show() + + def __init__(self, distRace): + self.race = distRace + self.timerEnabled = False + self.maxLapHit = 0 + self.photoFinish = False + toonInteriorTextures = loader.loadModel('phase_3.5/models/modules/toon_interior_textures') + invTextures = loader.loadModel('phase_3.5/models/gui/inventory_icons') + racingTextures = loader.loadModel('phase_6/models/karting/racing_textures') + self.gagTextures = [toonInteriorTextures.find('**/couch'), + invTextures.find('**/inventory_bannana_peel'), + racingTextures.find('**/boost_arrow'), + invTextures.find('**/inventory_anvil'), + invTextures.find('**/inventory_creampie')] + self.gagTextures[1].setScale(7.5) + self.gagTextures[3].setScale(7.5) + self.gagTextures[4].setScale(7.5) + self.cardMaker = CardMaker('card') + self.racerDict = {} + self.render2dRoot = render2d.attachNewNode('RaceGuiRender2dRoot') + self.render2dRoot.setDepthWrite(1) + self.directObjList = [] + self.aspect2dRoot = aspect2d.attachNewNode('RaceGuiAspect2dRoot') + self.aspect2dRoot.setDepthWrite(1) + self.raceModeRoot = self.aspect2dRoot.attachNewNode('RaceModeRoot') + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.closeButton = DirectButton(image=(gui.find('**/CloseBtn_UP'), + gui.find('**/CloseBtn_DN'), + gui.find('**/CloseBtn_Rllvr'), + gui.find('**/CloseBtn_UP')), relief=None, scale=1.05, text=TTLocalizer.KartRace_Leave, text_scale=0.04, text_pos=(0, -0.07), text_fg=VBase4(1, 1, 1, 1), pos=(-0.99, 0, 0.925), command=self.race.leaveRace) + self.closeButton.reparentTo(self.aspect2dRoot) + self.directObjList.append(self.closeButton) + self.raceTimeDelta = 0 + self.raceModeReady = False + self.resultModeReady = False + self.gagCycleSound = base.loadSfx('phase_3.5/audio/sfx/tick_counter.ogg') + if hasattr(self.gagCycleSound, 'setPlayRate'): + self.gagCycleSound.setPlayRate(0.2) + self.gagCycleSound.setLoop(1) + self.gagAcquireSound = base.loadSfx('phase_6/audio/sfx/SZ_MM_gliss.ogg') + self.disable() + return + + def initRaceMode(self): + self.mapScene = base.a2dTopRight.attachNewNode('MapScene') + self.mapScene.setPos(-0.2, 0, -0.2) + self.mapScene.setScale(0.25, 0.001, 0.25) + maxT = self.race.curve.getMaxT() + pt = Vec3(0, 0, 0) + ls = LineSegs('MapLines') + ls.setColor(1, 1, 1, 1) + ls.setThickness(2) + for x in xrange(101): + self.race.curve.getPoint(x / 100.0 * maxT, pt) + if x == 0: + ls.moveTo(pt[0], pt[1], pt[2]) + else: + ls.drawTo(pt[0], pt[1], pt[2]) + + self.mapLines = self.mapScene.attachNewNode(ls.create()) + self.mapLines.setScale(0.00025 * RaceGlobals.TrackDict[self.race.trackId][6]) + self.mapLines.setP(90) + self.faceStartPos = Vec3(-0.8, 0, 0.93) + self.faceEndPos = Vec3(0.8, 0, 0.93) + self.placeLabelNum = DirectLabel(relief=None, pos=TTLocalizer.RGUIplaceLabelNumPos, text='1', text_scale=0.35, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont()) + self.placeLabelNum.reparentTo(base.a2dBottomLeft) + self.directObjList.append(self.placeLabelNum) + self.placeLabelStr = DirectLabel(relief=None, pos=TTLocalizer.RGUIplaceLabelStrPos, text=TTLocalizer.KartRace_FirstSuffix, text_scale=0.1, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont()) + self.placeLabelStr.reparentTo(base.a2dBottomLeft) + self.directObjList.append(self.placeLabelStr) + self.lapLabel = DirectLabel(relief=None, pos=(-0.22, 0, -0.5), text='1/' + str(self.race.lapCount), text_scale=0.1, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont()) + self.lapLabel.reparentTo(base.a2dTopRight) + self.directObjList.append(self.lapLabel) + self.photoFinishLabel = DirectLabel(relief=None, pos=(0, 0, -0.1), text=TTLocalizer.KartRace_PhotoFinish, text_scale=TTLocalizer.RGUIphotoFinish, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont()) + self.photoFinishLabel.hide() + self.directObjList.append(self.photoFinishLabel) + self.wrongWayLabel = DirectLabel(relief=None, pos=(-0.22, 0, -0.2), text=TTLocalizer.KartRace_WrongWay, text_scale=0.1, text_fg=(0.95, 0, 0, 1), text_font=ToontownGlobals.getSignFont()) + self.wrongWayLabel.reparentTo(base.a2dTopRight) + self.directObjList.append(self.wrongWayLabel) + self.wrongWayLabel.setColorScale(Vec4(1, 1, 1, 0)) + self.wrongWaySeq = Sequence(self.wrongWayLabel.colorScaleInterval(0.25, colorScale=Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0)), self.wrongWayLabel.colorScaleInterval(0.25, colorScale=Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1))) + interpolateFacePos = lambda x: self.faceStartPos * (1.0 - x) + self.faceEndPos * x + self.timeLabels = [] + for x in xrange(self.race.lapCount): + minLabel = DirectLabel(relief=None, pos=(interpolateFacePos((2.0 * x + 1) / (self.race.lapCount * 2))[0] - 0.06, 0, 0.84), text="0'", text_scale=0.06, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ARight) + minLabel.reparentTo(self.raceModeRoot) + self.directObjList.append(minLabel) + secLabel = DirectLabel(relief=None, pos=(interpolateFacePos((2.0 * x + 1) / (self.race.lapCount * 2))[0] + 0.06, 0, 0.84), text="00''", text_scale=0.06, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ARight) + secLabel.reparentTo(self.raceModeRoot) + self.directObjList.append(secLabel) + fractionLabel = DirectLabel(relief=None, pos=(interpolateFacePos((2.0 * x + 1) / (self.race.lapCount * 2))[0] + 0.14, 0, 0.84), text='00', text_scale=0.06, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ARight) + fractionLabel.reparentTo(self.raceModeRoot) + self.directObjList.append(fractionLabel) + self.timeLabels.append((minLabel, secLabel, fractionLabel)) + + self.cardMaker.reset() + self.cardMaker.setName('GagIndicator') + self.cardMaker.setFrame(-0.5, 0.5, -0.5, 0.5) + self.cardMaker.setColor(1, 1, 1, 1) + self.gagPanel = DirectFrame(parent=base.a2dBottomLeft, relief=None, image=loader.loadModel('phase_6/models/karting/gag_panel'), image_scale=0.25, pos=(0.2, 0, 0.55)) + self.directObjList.append(self.gagPanel) + self.gag = self.gagPanel.attachNewNode('gag') + self.gag.setScale(0.2) + for gag in self.gagTextures: + gag.reparentTo(self.gag) + gag.hide() + + self.cardMaker.reset() + self.cardMaker.setName('RaceProgressLine') + self.cardMaker.setFrame(-0.5, 0.5, -0.5, 0.5) + line = self.raceModeRoot.attachNewNode(self.cardMaker.generate()) + line.setScale(self.faceEndPos[0] - self.faceStartPos[0], 1, 0.01) + line.setPos(0, 0, self.faceStartPos[2]) + self.cardMaker.setName('RaceProgressLineHash') + for n in xrange(self.race.lapCount + 1): + hash = self.raceModeRoot.attachNewNode(self.cardMaker.generate()) + hash.setScale(line.getScale()[2], 1, line.getScale()[2] * 5) + t = float(n) / self.race.lapCount + hash.setPos(self.faceStartPos[0] * (1 - t) + self.faceEndPos[0] * t, self.faceStartPos[1], self.faceStartPos[2]) + + self.raceModeReady = True + self.disable() + return + + def initResultMode(self): + self.endPanel = RaceEndPanel(len(self.race.avIds), self.race) + self.endPanel.reparentTo(self.aspect2dRoot) + self.directObjList.append(self.endPanel) + self.resultModeReady = True + self.disable() + + def showGag(self, gagIndex): + if gagIndex < len(self.gagTextures): + for gag in self.gagTextures: + gag.hide() + + self.gagTextures[gagIndex].show() + + def updateGag(self, gagIndex): + if self.gag: + if hasattr(self, 'gagCycleInterval'): + self.gagCycleInterval.finish() + del self.gagCycleInterval + self.gag.setHpr(0, 0, 0) + self.showGag(gagIndex) + if gagIndex == 0: + self.gag.hide() + else: + self.gag.show() + self.gagAcquireSound.play() + self.gagAcquireInterval = LerpHprInterval(self.gag, duration=0.5, blendType='easeOut', startHpr=Point3(0, -90, 0), hpr=Point3(0, 0, 0)) + self.gagAcquireInterval.start() + + def waitingOnGag(self, cycleTime): + if self.gag: + numTextures = len(self.gagTextures) + startOffset = random.choice(range(0, numTextures)) + self.gag.show() + self.gagCycleInterval = Parallel(LerpFunc(self.showNextGag, fromData=startOffset, toData=numTextures * 2 * cycleTime + startOffset, blendType='easeOut', duration=cycleTime), LerpHprInterval(self.gag, duration=cycleTime, hpr=Point3(0, 180 * numTextures * 2 * cycleTime - 90, 0), blendType='easeOut', startHpr=Point3(0, 0, 0)), SoundInterval(self.gagCycleSound, loop=1, duration=cycleTime, startTime=0), name='gagCycleInterval') + self.gagCycleInterval.start() + + def showNextGag(self, t): + if self.gag: + currGagIndex = int(t % (len(self.gagTextures) - 1)) + 1 + self.showGag(currGagIndex) + + def enableSpeedometer(self): + self.race.localKart.showSpeedometer() + + def disableSpeedometer(self): + self.race.localKart.hideSpeedometer() + + def disableRaceMode(self): + self.disableSpeedometer() + self.render2dRoot.hide() + self.raceModeRoot.hide() + for x in self.timeLabels: + for y in x: + y.hide() + + self.setTimerEnabled(False) + + def disableResultMode(self): + self.endPanel.disable() + + def disable(self): + self.closeButton.hide() + taskMgr.removeTasksMatching('clearRaceEndPanel') + if self.raceModeReady: + self.disableRaceMode() + if self.resultModeReady: + self.disableResultMode() + + def enableRaceMode(self): + self.enableSpeedometer() + self.render2dRoot.show() + self.raceModeRoot.show() + self.maxLapHit = min(self.maxLapHit, self.race.lapCount - 1) + for x in xrange(self.maxLapHit + 1): + for y in self.timeLabels[x]: + y.configure(text_font=ToontownGlobals.getSignFont()) + y.show() + + for y in self.timeLabels[self.maxLapHit]: + y.configure(text_font=ToontownGlobals.getSignFont()) + + def enableResultMode(self): + self.endPanel.enable() + if len(self.race.circuitLoop) > 1: + taskMgr.doMethodLater(180, self.endPanel.closeButtonPressed, 'clearRaceEndPanel', extraArgs=[]) + + def destroy(self): + self.disable() + if hasattr(self, 'wrongWaySeq'): + self.wrongWaySeq.finish() + self.wrongWaySeq = None + taskMgr.removeTasksMatching('removeIt') + taskMgr.removeTasksMatching('removeCam*') + taskMgr.removeTasksMatching('clearRaceEndPanel') + for obj in self.directObjList: + obj.destroy() + + if hasattr(self, 'mapScene'): + self.mapScene.removeNode() + self.mapScene = None + self.aspect2dRoot.removeNode() + self.aspect2dRoot = None + self.raceModeRoot.removeNode() + self.raceModeRoot = None + self.render2dRoot.removeNode() + self.render2dRoot = None + self.closeButton = None + self.gag = None + self.lapLabel = None + self.timeLabels = None + self.placeLabelStr = None + self.placeLabelNum = None + self.photoFinishLabel = None + self.mapScene = None + self.race = None + return + + def setSpotAsymptotic(self, diffT, spot): + p = (-1, 1)[diffT > 0] * (1 - 1 / pow(abs(diffT) / self.cutoff + 1, 2)) + spot.setX(p) + + def setSpotRaceLinear(self, t, spot): + spot.setX(-1.0 + 2.0 * (t / self.lapCount)) + + def setSpotLapLinear(self, t, spot): + spot.setX(-1.0 + 2.0 * (t - int(t))) + + def update(self, time): + placeSorter = [] + placeCount = 0 + for key in self.racerDict.keys(): + racer = self.racerDict[key] + curvetime = racer.curvetime + face = racer.face + mapspot = racer.mapspot + maxlaphit = racer.maxlaphit + if not racer.finished and racer.enabled: + placeSorter.append((curvetime, key)) + if racer.finished or racer.enabled: + placeCount += 1 + pt = Vec3(0, 0, 0) + mapT = (curvetime % 1 + self.race.startT / self.race.curve.getMaxT()) % 1 * self.race.curve.getMaxT() + self.race.curve.getPoint(mapT, pt) + self.race.curve.getPoint(mapT % self.race.curve.getMaxT(), pt) + lapT = clampScalar(curvetime / self.race.lapCount, 0.0, 1.0) + faceX = self.faceStartPos[0] * (1 - lapT) + self.faceEndPos[0] * lapT + racer.update(faceX=faceX, mapspotPt=pt) + t = time - self.race.baseTime - self.raceTimeDelta + if key == localAvatar.doId: + if self.race.laps > maxlaphit: + racer.update(maxlaphit=self.race.laps) + self.maxLapHit = racer.maxlaphit + if self.maxLapHit < self.race.lapCount: + for y in self.timeLabels[self.maxLapHit - 1]: + y.configure(text_font=ToontownGlobals.getSignFont()) + + for y in self.timeLabels[self.maxLapHit]: + y.show() + + for y in self.timeLabels[self.maxLapHit]: + y.configure(text_font=ToontownGlobals.getSignFont()) + + self.raceTimeDelta = globalClock.getFrameTime() - self.race.baseTime + lapNotice = DirectLabel() + lapNotice.setScale(0.1) + if self.maxLapHit == self.race.lapCount - 1: + lapNotice['text'] = TTLocalizer.KartRace_FinalLapText + else: + lapNotice['text'] = TTLocalizer.KartRace_LapText % str(self.maxLapHit + 1) + taskMgr.doMethodLater(2, lapNotice.remove, 'removeIt', extraArgs=[]) + self.lapLabel['text'] = str(clampScalar(self.maxLapHit + 1, 1, self.race.lapCount)) + '/' + str(self.race.lapCount) + + suffix = {1: TTLocalizer.KartRace_FirstSuffix, + 2: TTLocalizer.KartRace_SecondSuffix, + 3: TTLocalizer.KartRace_ThirdSuffix, + 4: TTLocalizer.KartRace_FourthSuffix} + placeSorter.sort() + for x, p in zip(placeSorter, xrange(len(placeSorter), 0, -1)): + self.racerDict[x[1]].update(place=p + placeCount - len(placeSorter)) + + localRacer = self.racerDict[localAvatar.doId] + nearDiff, farDiff = RaceGlobals.TrackDict[self.race.trackId][8] + if not localRacer.finished and self.faceEndPos[0] - localRacer.face.getX() < nearDiff: + for racerId in self.racerDict.keys(): + racer = self.racerDict[racerId] + if not racer.enabled or racerId == localAvatar.doId or racer.face.getX() >= self.faceEndPos[0]: + continue + if self.faceEndPos[0] - racer.face.getX() < farDiff: + self.photoFinish = True + + if self.photoFinish: + self.photoFinishLabel.show() + self.placeLabelNum['text'] = '' + self.placeLabelStr['text'] = '' + else: + self.photoFinishLabel.hide() + self.placeLabelNum['text'] = str(self.racerDict[localAvatar.doId].place) + self.placeLabelStr['text'] = suffix[self.racerDict[localAvatar.doId].place] + minutes = int(t / 60) + t -= minutes * 60 + seconds = int(t) + padding = (seconds < 10 and ['0'] or [''])[0] + t -= seconds + fraction = str(t)[2:4] + fraction = fraction + '0' * (2 - len(fraction)) + if self.timerEnabled and self.maxLapHit < self.race.lapCount: + self.timeLabels[self.maxLapHit][0]['text'] = "%d'" % minutes + self.timeLabels[self.maxLapHit][1]['text'] = "%s%d''" % (padding, seconds) + self.timeLabels[self.maxLapHit][2]['text'] = '%s' % fraction + if self.race.wrongWay and not self.wrongWaySeq.isPlaying(): + self.wrongWaySeq.loop() + elif not self.race.wrongWay and self.wrongWaySeq.isPlaying(): + self.wrongWaySeq.finish() + + def updateRacerInfo(self, avId, curvetime = None, maxlaphit = None): + if avId in self.racerDict.keys(): + self.racerDict[avId].update(curvetime=curvetime, maxlaphit=maxlaphit) + + def racerEntered(self, avId): + toon = base.cr.doId2do.get(avId, None) + kart = base.cr.doId2do.get(self.race.kartMap.get(avId, None), None) + if not toon or not kart: + return + if kart.getBodyColor() == InvalidEntry: + bodyColor = getDefaultColor() + else: + bodyColor = getAccessory(kart.getBodyColor()) + headframe = RaceHeadFrame(av=toon, color=bodyColor) + eyes = headframe.head.find('**/eyes*') + eyes.setDepthTest(1) + eyes.setDepthWrite(1) + headframe.configure(geom_scale=(0.5, 0.5, 0.5)) + headframe.setZ(self.faceStartPos[2]) + headframe.setDepthWrite(True) + headframe.setDepthTest(True) + headframe.reparentTo(self.raceModeRoot) + self.directObjList.append(headframe) + mapspot = loader.loadModel('phase_6/models/karting/race_mapspot') + mapspot.setColor(bodyColor) + mapspot.reparentTo(self.mapLines) + mapspot.setHpr(self.mapScene, 0, 0, 0) + self.racerDict[avId] = self.RacerInfo(headframe, mapspot) + for key, i in zip(self.racerDict.keys(), range(len(self.racerDict.keys()))): + face = self.racerDict[key].face + mapspot = self.racerDict[key].mapspot + face.setX(self.faceStartPos[0]) + face.setY(-1 - 5 * (i + 1)) + face.setScale(0.15) + mapspot.getChild(0).setY((-5 - 5 * (i + 1)) * 1000) + mapspot.setScale(self.mapScene, 0.15) + mapspot.setPos(self.race.startingPos[0][0]) + if key == localAvatar.doId: + face.setY(-1) + face.setScale(face.getScale() * 1.25) + mapspot.getChild(0).setY(-5 * 1000) + mapspot.setScale(mapspot.getScale() * 1.25) + self.face = face + self.mapspot = mapspot + + return + + def racerLeft(self, avId, unexpected = False): + racer = self.racerDict.get(avId, None) + if racer: + racer.disable() + return + + def racerFinished(self, avId, trackId, place, totalTime, entryFee, qualify, winnings, bonus, trophies, circuitPoints, circuitTime): + racer = self.racerDict.get(avId, None) + if racer: + racer.update(finished=True) + racer.disable() + self.endPanel.displayRacer(place, entryFee, qualify, winnings, trackId, bonus, trophies, racer.face, base.cr.doId2do[avId].getName(), totalTime, circuitPoints, circuitTime) + if racer.face in self.directObjList: + self.directObjList.remove(racer.face) + if avId == localAvatar.doId: + self.disableRaceMode() + self.enableResultMode() + self.endPanel.startWinningsPanel(entryFee, winnings, trackId, bonus, trophies) + return + + def racerFinishedCircuit(self, avId, place, entryFee, winnings, bonus, trophies): + racer = self.racerDict.get(avId, None) + if racer: + newTotalTickets = winnings + entryFee + bonus + self.endPanel.updateWinnings(place, newTotalTickets) + if avId == localAvatar.doId: + self.endPanel.updateWinningsFromCircuit(place, entryFee, winnings, bonus, trophies) + return + + def circuitFinished(self, placeFixup): + self.endPanel.circuitFinished(placeFixup) + + def setTimerEnabled(self, enabled): + self.timerEnabled = enabled diff --git a/toontown/racing/RaceGag.py b/toontown/racing/RaceGag.py new file mode 100755 index 00000000..fc81f462 --- /dev/null +++ b/toontown/racing/RaceGag.py @@ -0,0 +1,76 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.showbase import DirectObject +from DroppedGag import * +types = ['', + 'Pie', + 'Banana', + 'Anvil'] + +class RaceGag(DirectObject.DirectObject): + + def __init__(self, parent, slot, testPos): + DirectObject.DirectObject.__init__(self) + self.parent = parent + self.name = 'gag-' + str(slot) + self.geom = DroppedGag(self.name, base.race.qbox) + self.geom.dropShadow.setScale(0.7) + self.geom.setPos(testPos + Vec3(0, 0, -1)) + qc = CollisionTube(Point3(0, 0, -2.5), Point3(0, 0, 2.5), 1) + self.gagnp = NodePath(CollisionNode(self.name)) + self.gagnp.node().addSolid(qc) + self.gagnp.reparentTo(self.geom) + self.gagnp.node().setIntoCollideMask(BitMask32(32768)) + self.gagnp.node().setFromCollideMask(BitMask32(32768)) + self.slot = slot + self.type = 0 + self.accept('imIn-' + self.name, self.hitGag) + self.pickupSound = base.loadSfx('phase_6/audio/sfx/KART_getGag.ogg') + self.fadeout = None + return + + def delete(self): + if self.fadeout: + self.fadeout.finish() + self.fadeout = None + self.gagnp.removeNode() + self.gagnp = None + self.geom.delete() + self.geom = None + del self.parent + self.ignore('imIn-' + self.name) + return + + def getType(self): + return self.type + + def isActive(self): + if self.type: + return True + else: + return False + + def genGag(self, spot, type): + self.type = type + fadein = self.geom.scaleInterval(1, 4) + self.geom.setScale(0.001) + self.geom.reparentTo(self.parent.geom) + self.gagnp.reparentTo(self.geom) + fadein.start() + + def hideGeom(self): + self.geom.detachNode() + + def disableGag(self): + self.gagnp.reparentTo(hidden) + self.type = 0 + if self.fadeout: + self.fadeout.finish() + self.fadeout = Sequence(self.geom.scaleInterval(0.2, 0), Func(self.hideGeom)) + self.fadeout.start() + + def hitGag(self, cevent): + if not self.parent.currGag: + self.pickupSound.play() + self.parent.pickupGag(self.slot, self.type) + self.disableGag() diff --git a/toontown/racing/RaceGlobals.py b/toontown/racing/RaceGlobals.py new file mode 100755 index 00000000..f410c360 --- /dev/null +++ b/toontown/racing/RaceGlobals.py @@ -0,0 +1,562 @@ +TrackSignDuration = 15 +RaceCountdown = 3 +MaxRacers = 4 +MaxTickets = 99999 +Practice = 0 +ToonBattle = 1 +Circuit = 2 +Speedway = 0 +Rural = 1 +Urban = 2 +RT_Speedway_1 = 0 +RT_Speedway_1_rev = 1 +RT_Rural_1 = 20 +RT_Rural_1_rev = 21 +RT_Urban_1 = 40 +RT_Urban_1_rev = 41 +RT_Speedway_2 = 60 +RT_Speedway_2_rev = 61 +RT_Rural_2 = 62 +RT_Rural_2_rev = 63 +RT_Urban_2 = 64 +RT_Urban_2_rev = 65 +KARTING_TICKETS_HOLIDAY_MULTIPLIER = 2 + +def getTrackGenre(trackId): + if trackId in (RT_Speedway_1, + RT_Speedway_1_rev, + RT_Speedway_2, + RT_Speedway_2_rev): + return Speedway + elif trackId in (RT_Rural_1, + RT_Rural_1_rev, + RT_Rural_2, + RT_Rural_2_rev): + return Rural + else: + return Urban + + +RT_Speedway_1_Gags = ((923.052, -1177.431, 0.024), + (926.099, -1187.345, 0.024), + (925.68, -1197.327, 0.024), + (925.169, -1209.502, 0.024), + (394.009, 209.219, 0.025), + (279.109, 279.744, 0.025), + (204.366, 316.238, 0.025), + (118.646, 358.009, 0.025), + (-1462.098, 791.722, 0.025), + (-1459.446, 730.064, 0.025), + (-1450.731, 666.811, 0.025), + (-1438.388, 615.1, 0.025)) +RT_Speedway_2_Gags = ((-355.18, -2430.1, -0.126728), + (-343.456, -2421.43, -0.0116951), + (-329.644, -2411.06, -0.0169053), + (-315.054, -2402.91, -0.0800667), + (243.293, -906.412, 0.021832), + (216.555, -910.885, -0.146125), + (192.16, -915.93, -0.242366), + (165.941, -922.381, -0.247588), + (-840.626, 2405.96, 58.4195), + (-868.154, 2370.54, 56.7396), + (-896.126, 2332.55, 53.8607), + (-921.952, 2291.16, 49.8209)) +RT_Speedway_1_rev_Gags = ((1364.601, -664.452, 0.025), + (1312.491, -588.218, 0.025), + (1251.775, -509.556, 0.025), + (1214.052, -461.743, 0.025), + (-976.044, 995.072, 0.025), + (-1043.917, 1018.78, 0.025), + (-1124.555, 1038.362, 0.025), + (-1187.95, 1047.006, 0.025), + (-1174.542, -208.968, 0.025), + (-1149.34, -270.698, 0.025), + (-1121.2, -334.367, 0.025), + (-1090.627, -392.662, 0.026)) +RT_Rural_1_Gags = ((814.276, -552.928, 2.107), + (847.738, -551.97, 2.106), + (889.265, -549.569, 2.107), + (922.022, -554.813, 2.106), + (1791.42, 2523.91, 2.106), + (1754.14, 2540.25, 2.107), + (1689.66, 2557.28, 2.107), + (1614.01, 2577.16, 2.106), + (-1839.0, 654.477, 86.83), + (-1894.33, 640.125, 80.39), + (-1955.3, 625.09, 73.07), + (-2016.99, 611.746, 65.86)) +RT_Rural_2_Gags = ((2001.53, 560.532, 198.912), + (2002.45, 574.292, 198.912), + (2003.42, 588.612, 198.912), + (2004, 602.849, 198.912), + (-2107.4, 2209.67, 198.913), + (-2086.13, 2224.31, 198.913), + (-2058.11, 2244.31, 198.912), + (-2023.85, 2268.77, 198.912), + (-331.746, -1010.57, 222.332), + (-358.595, -1007.68, 225.129), + (-388.556, -1004.87, 228.239), + (-410.122, -1003.03, 230.482), + (69.763, -2324.5, 198.912), + (63.5314, -2334.02, 198.913), + (57.9662, -2349.14, 198.913), + (51.8838, -2363.87, 198.913)) +RT_Urban_1_Gags = ((51.9952, 2431.62, 55.7053), + (39.5407, 2421.64, 65.7053), + (27.7504, 2411.67, 55.7053), + (15.55, 2401.65, 65.7053), + (-1008.36, 2116.41, 0.0246798), + (-1050.31, 2099.78, 0.025), + (-1092.26, 2083.15, 0.0253202), + (-1134.21, 2066.52, 0.0256404), + (-1966.68, 1139.32, 1.76981), + (-1970.46, 1120.57, 1.76981), + (-1974.18, 1101.82, 1.76981), + (-1977.93, 1084.07, 1.76981), + (1419.05, -2987.18, 0.025), + (1411.09, -3004.09, 0.025), + (1403.13, -3021.01, 0.025), + (1395.17, -3037.92, 0.025), + (948.131, -1216.77, 0.025), + (935.545, -1204.09, 0.025), + (922.959, -1191.41, 0.025), + (909.959, -1177.41, 0.025)) +RT_Urban_2_Gags = ((-2761.49, -3070.97, -0.255122), + (-2730.18, -3084.09, -0.255153), + (-2701.45, -3096.26, -0.255669), + (-2669.81, -3108.9, -0.255252), + (735.479, -423.828, 23.7334), + (759.026, -427.198, 23.0068), + (783.232, -430.659, 22.2569), + (809.914, -434.476, 21.4326), + (3100.09, 240.411, 23.4672), + (3089.09, 242.019, 23.5251), + (3077.68, 243.688, 23.6857), + (3064.82, 245.567, 23.8771), + (-10.7389, 2980.48, -0.255609), + (-41.2644, 2974.53, -0.255122), + (-69.8423, 2989.98, -0.255682), + (-102.331, 2986.1, -0.255637), + (-1978.67, 588.981, -0.255685), + (-1977.07, 560.797, -0.255415), + (-1948.58, 544.782, -0.255122), + (-1943.42, 510.262, -0.255866)) +RT_Urban_1_rev_Gags = ((1034.43, -366.371, 0.025), + (1051.84, -360.473, 0.025), + (1069.25, -354.575, 0.025), + (1086.66, -348.677, 0.025), + (1849.66, -2807.21, 0.0246158), + (1858.55, -2795.99, 0.0246158), + (1867.44, -2784.76, 0.0246158), + (1876.33, -2773.53, 0.0246158), + (316.342, -44.9529, 0.025), + (305.173, -63.4405, 0.025), + (294.004, -81.9281, 0.025), + (282.835, -100.416, 0.025), + (-762.377, 2979.25, 0.025), + (-753.029, 2995.69, 0.025), + (-743.681, 3012.14, 0.025), + (-734.333, 3028.58, 0.025), + (470.628, 1828.32, 55.0), + (481.284, 1836.89, 55.0), + (491.941, 1845.47, 55.0), + (502.597, 1854.04, 55.0)) +Speedway_1_Boosts = (((-320, 685, 1), (415, 0, 0)),) +Speedway_1_Rev_Boosts = (((-320, 685, 0.1), (235, 0, 0)),) +Speedway_2_Boosts = (((-120, 430, 1.0), (-50, 0, 0)),) +Speedway_2_Rev_Boosts = (((176, 625, 1.0), (130, 0, 0)),) +Rural_1_Boosts = (((3132.64, 859.56, 5.0), (384.44, 363.5, 0)), ((-3050.33, -1804.97, 207.7), (229.4, 353.25, 342.9))) +Rural_1_Rev_Boosts = (((3132.64, 859.56, 5.0), (197.1, -2.25, 0)), ((-3151.34, -1569.56, 200.621), (189.46, 182.75, 195.255))) +Rural_2_Boosts = (((873.255, -593.664, 199.5), (87.715, 0, 0)), ((-1747.62, 801.56, 199.5), (-126.516, 0, 0))) +Rural_2_Rev_Boosts = (((-428.004, -243.692, 324.516), (51.428, 6, 1)), ((-384.043, 211.62, 193.5), (-127.859, 1, 0))) +Urban_1_Boosts = (((677.057, 1618.24, 0.025), (35.9995, 0, 0)), ((-2250.35, 1618.1, 0.0241526), (-154.8, 0, 0)), ((400.13, -1090.26, 0.025), (-175.204, 0, 0))) +Urban_1_Rev_Boosts = (((488.739, -2055.07, 0.025), (3.59753, 0, 0)), ((-1737.29, 588.138, 0.025), (26.3975, 0, 0)), ((-212.314, 2638.34, 0.025), (-128.404, 0, 0))) +Urban_2_Boosts = (((358.134, -1655.42, 0.3), (-4.95, 1, 0)), ((2058.77, 2560.03, 0.3), (77.31, 0, 0)), ((-3081.33, -1037.55, 0.25), (177.359, 0, 0))) +Urban_2_Rev_Boosts = (((-2007.38, 484.878, 0.25), (30.9102, 0, 0)), ((2646.51, 1455.15, 0.25), (-120.172, 0, 0)), ((-472.215, -2048.21, 0.25), (136.192, 0, 0))) + +def RaceInfo2RacePadId(trackId, trackType): + rev = trackId % 2 + if not rev: + if trackType == Practice: + padId = 0 + else: + padId = 2 + elif trackType == Practice: + padId = 1 + else: + padId = 3 + return padId + + +def getTrackGenreString(genreId): + genreStrings = ['Speedway', 'Country', 'City'] + return genreStrings[genreId].lower() + + +def getTunnelSignName(genreId, padId): + if genreId == 2 and padId == 0: + return 'tunne1l_citysign' + elif genreId == 1 and padId == 0: + return 'tunnel_countrysign1' + else: + return 'tunnel%s_%ssign' % (padId + 1, getTrackGenreString(genreId)) + + +RacePadId2RaceInfo = {0: (0, Practice, 3), + 1: (1, Practice, 3), + 2: (0, ToonBattle, 3), + 3: (1, Circuit, 3)} + +def getGenreFromString(string): + if string == 'town': + return Urban + elif string == 'stadium': + return Speedway + else: + return Rural + + +def getTrackListByType(genre, type): + return Rural + + +def getTrackListByType(genre, type): + genreDict = {Urban: [[RT_Urban_1, RT_Urban_2], [RT_Urban_1_rev, RT_Urban_2_rev]], + Rural: [[RT_Rural_1, RT_Rural_2], [RT_Rural_1_rev, RT_Rural_2_rev]], + Speedway: [[RT_Speedway_1, RT_Speedway_2], [RT_Speedway_1_rev, RT_Speedway_2_rev]]} + trackIdList = genreDict.get(genre) + return trackIdList[type] + + +def getCanonicalPadId(padId): + return padId % 4 + + +def getNextRaceInfo(prevTrackId, genreString, padId): + genre = getGenreFromString(genreString) + cPadId = getCanonicalPadId(padId) + raceInfo = RacePadId2RaceInfo.get(cPadId) + raceType = raceInfo[1] + if raceType == Circuit and not simbase.air.newsManager.isGrandPrixRunning(): + raceType = ToonBattle + trackList = getTrackListByType(genre, raceInfo[0]) + if trackList.count(prevTrackId) == 0: + trackId = trackList[1] + else: + index = trackList.index(prevTrackId) + index += 1 + index %= len(trackList) + trackId = trackList[index] + return (trackId, raceType, raceInfo[2]) + + +TrackPath = 'phase_6/models/karting/' +TrackDict = {RT_Speedway_1: (TrackPath + 'RT_SpeedwayA', + 240.0, + 115.0, + (50, 500), + RT_Speedway_1_Gags, + Speedway_1_Boosts, + 1.0, + 'GS_Race_SS.ogg', + (0.01, 0.015)), + RT_Speedway_1_rev: (TrackPath + 'RT_SpeedwayA', + 240.0, + 115.0, + (50, 500), + RT_Speedway_1_rev_Gags, + Speedway_1_Rev_Boosts, + 1.0, + 'GS_Race_SS.ogg', + (0.01, 0.015)), + RT_Speedway_2: (TrackPath + 'RT_SpeedwayB', + 335.0, + 210.0, + (75, 1000), + RT_Speedway_2_Gags, + Speedway_2_Boosts, + 1.0, + 'GS_Race_SS.ogg', + (0.01, 0.015)), + RT_Speedway_2_rev: (TrackPath + 'RT_SpeedwayB', + 335.0, + 210.0, + (75, 1000), + RT_Speedway_2_Gags, + Speedway_2_Rev_Boosts, + 1.0, + 'GS_Race_SS.ogg', + (0.01, 0.015)), + RT_Rural_1: (TrackPath + 'RT_RuralB', + 360.0, + 230.0, + (100, 500), + RT_Rural_1_Gags, + Rural_1_Boosts, + 0.75, + 'GS_Race_RR.ogg', + (0.003, 0.004)), + RT_Rural_1_rev: (TrackPath + 'RT_RuralB', + 360.0, + 230.0, + (100, 500), + RT_Rural_1_Gags, + Rural_1_Rev_Boosts, + 0.75, + 'GS_Race_RR.ogg', + (0.003, 0.004)), + RT_Rural_2: (TrackPath + 'RT_RuralB2', + 480.0, + 360.0, + (150, 1000), + RT_Rural_2_Gags, + Rural_2_Boosts, + 0.75, + 'GS_Race_RR.ogg', + (0.003, 0.004)), + RT_Rural_2_rev: (TrackPath + 'RT_RuralB2', + 480.0, + 360.0, + (150, 1000), + RT_Rural_2_Gags, + Rural_2_Rev_Boosts, + 0.75, + 'GS_Race_RR.ogg', + (0.003, 0.004)), + RT_Urban_1: (TrackPath + 'RT_UrbanA', + 480.0, + 305.0, + (300, 500), + RT_Urban_1_Gags, + Urban_1_Boosts, + 1.0, + 'GS_Race_CC.ogg', + (0.002, 0.003)), + RT_Urban_1_rev: (TrackPath + 'RT_UrbanA', + 480.0, + 305.0, + (300, 500), + RT_Urban_1_rev_Gags, + Urban_1_Rev_Boosts, + 1.0, + 'GS_Race_CC.ogg', + (0.002, 0.003)), + RT_Urban_2: (TrackPath + 'RT_UrbanB', + 480.0, + 280.0, + (400, 1000), + RT_Urban_2_Gags, + Urban_2_Boosts, + 1.0, + 'GS_Race_CC.ogg', + (0.002, 0.003)), + RT_Urban_2_rev: (TrackPath + 'RT_UrbanB', + 480.0, + 280.0, + (400, 1000), + RT_Urban_2_Gags, + Urban_2_Rev_Boosts, + 1.0, + 'GS_Race_CC.ogg', + (0.002, 0.003))} +TrackIds = TrackDict.keys() +TrackIds.sort() + +def getEntryFee(trackId, raceType): + fee = 0 + if raceType == ToonBattle: + fee = TrackDict[trackId][3][0] + elif raceType == Circuit: + fee = TrackDict[trackId][3][1] + return fee + + +def getQualifyingTime(trackId): + return TrackDict[trackId][1] + + +def getDefaultRecordTime(trackId): + return TrackDict[trackId][2] + + +Daily = 0 +Weekly = 1 +AllTime = 2 +PeriodDict = {Daily: 10, + Weekly: 100, + AllTime: 1000} +PeriodIds = PeriodDict.keys() +NumRecordPeriods = len(PeriodIds) +NumRecordsPerPeriod = 10 +Winnings = [4.0, + 2.0, + 1.5, + 1.15] +PracticeWinnings = 20 +SpeedwayQuals = 0 +RuralQuals = 1 +UrbanQuals = 2 +SpeedwayWins = 3 +RuralWins = 4 +UrbanWins = 5 +CircuitWins = 6 +TwoPlayerWins = 7 +ThreePlayerWins = 8 +FourPlayerWins = 9 +CircuitSweeps = 10 +CircuitQuals = 11 +QualsList = [SpeedwayQuals, RuralQuals, UrbanQuals] +WinsList = [SpeedwayWins, RuralWins, UrbanWins] +SpeedwayQuals1 = 0 +SpeedwayQuals2 = 1 +SpeedwayQuals3 = 2 +RuralQuals1 = 3 +RuralQuals2 = 4 +RuralQuals3 = 5 +UrbanQuals1 = 6 +UrbanQuals2 = 7 +UrbanQuals3 = 8 +TotalQuals = 9 +SpeedwayWins1 = 10 +SpeedwayWins2 = 11 +SpeedwayWins3 = 12 +RuralWins1 = 13 +RuralWins2 = 14 +RuralWins3 = 15 +UrbanWins1 = 16 +UrbanWins2 = 17 +UrbanWins3 = 18 +TotalWins = 19 +CircuitQuals1 = 20 +CircuitQuals2 = 21 +CircuitQuals3 = 22 +CircuitWins1 = 23 +CircuitWins2 = 24 +CircuitWins3 = 25 +CircuitSweeps1 = 26 +CircuitSweeps2 = 27 +CircuitSweeps3 = 28 +GrandTouring = 29 +NumTrophies = 30 +TenTrophyCup = 30 +TwentyTrophyCup = 31 +ThirtyTrophyCup = 32 +TrophyCups = [TenTrophyCup, TwentyTrophyCup, ThirtyTrophyCup] +NumCups = 3 +SpeedwayQualsList = [SpeedwayQuals1, SpeedwayQuals2, SpeedwayQuals3] +RuralQualsList = [RuralQuals1, RuralQuals2, RuralQuals3] +UrbanQualsList = [UrbanQuals1, UrbanQuals2, UrbanQuals3] +SpeedwayWinsList = [SpeedwayWins1, SpeedwayWins2, SpeedwayWins3] +RuralWinsList = [RuralWins1, RuralWins2, RuralWins3] +UrbanWinsList = [UrbanWins1, UrbanWins2, UrbanWins3] +CircuitWinsList = [CircuitWins1, CircuitWins2, CircuitWins3] +CircuitSweepsList = [CircuitSweeps1, CircuitSweeps2, CircuitSweeps3] +CircuitQualList = [CircuitQuals1, CircuitQuals2, CircuitQuals3] +AllQualsList = [SpeedwayQualsList, RuralQualsList, UrbanQualsList] +AllWinsList = [SpeedwayWinsList, RuralWinsList, UrbanWinsList] +TrophiesPerCup = NumTrophies / NumCups +QualifiedRaces = [1, 10, 100] +TotalQualifiedRaces = 100 +WonRaces = [1, 10, 100] +TotalWonRaces = 100 +WonCircuitRaces = [1, 5, 25] +SweptCircuitRaces = [1, 5, 25] +QualifiedCircuitRaces = [1, 5, 25] +LBSubscription = {'stadium': [(RT_Speedway_1, Daily), + (RT_Speedway_1, Weekly), + (RT_Speedway_1, AllTime), + (RT_Speedway_1_rev, Daily), + (RT_Speedway_1_rev, Weekly), + (RT_Speedway_1_rev, AllTime), + (RT_Speedway_2, Daily), + (RT_Speedway_2, Weekly), + (RT_Speedway_2, AllTime), + (RT_Speedway_2_rev, Daily), + (RT_Speedway_2_rev, Weekly), + (RT_Speedway_2_rev, AllTime)], + 'country': [(RT_Rural_1, Daily), + (RT_Rural_1, Weekly), + (RT_Rural_1, AllTime), + (RT_Rural_1_rev, Daily), + (RT_Rural_1_rev, Weekly), + (RT_Rural_1_rev, AllTime), + (RT_Rural_2, Daily), + (RT_Rural_2, Weekly), + (RT_Rural_2, AllTime), + (RT_Rural_2_rev, Daily), + (RT_Rural_2_rev, Weekly), + (RT_Rural_2_rev, AllTime)], + 'city': [(RT_Urban_1, Daily), + (RT_Urban_1, Weekly), + (RT_Urban_1, AllTime), + (RT_Urban_1_rev, Daily), + (RT_Urban_1_rev, Weekly), + (RT_Urban_1_rev, AllTime), + (RT_Urban_2, Daily), + (RT_Urban_2, Weekly), + (RT_Urban_2, AllTime), + (RT_Urban_2_rev, Daily), + (RT_Urban_2_rev, Weekly), + (RT_Urban_2_rev, AllTime)]} +BANANA = 1 +TURBO = 2 +ANVIL = 3 +PIE = 4 +GagFreq = [[PIE, + BANANA, + BANANA, + BANANA, + TURBO, + PIE], + [PIE, + BANANA, + BANANA, + TURBO, + ANVIL, + PIE], + [PIE, + BANANA, + TURBO, + TURBO, + ANVIL, + PIE], + [BANANA, + TURBO, + TURBO, + TURBO, + ANVIL, + PIE]] + +AnvilSquishDuration = 3 + +CircuitLoops = [[RT_Speedway_1, RT_Rural_1, RT_Urban_1], + [RT_Speedway_1_rev, RT_Rural_1_rev, RT_Urban_1_rev], + [RT_Speedway_2, RT_Rural_2, RT_Urban_2], + [RT_Speedway_2_rev, RT_Rural_2_rev, RT_Urban_2_rev]] +CircuitPoints = [10, + 8, + 6, + 4] + +def getCircuitLoop(startingTrack): + circuitLoop = [startingTrack] + for loop in CircuitLoops: + if startingTrack in loop: + numTracks = len(loop) + tempLoop = loop * 2 + startingIndex = tempLoop.index(startingTrack) + circuitLoop = tempLoop[startingIndex:startingIndex + numTracks] + break + + return circuitLoop + + +Exit_UserReq = 0 +Exit_Barrier = 1 +Exit_Slow = 2 +Exit_BarrierNoRefund = 3 + +MaxTimeDifference = { + 0: 86400, + 1: 604800 +} diff --git a/toontown/racing/RaceHeadFrame.py b/toontown/racing/RaceHeadFrame.py new file mode 100755 index 00000000..fd26f5b3 --- /dev/null +++ b/toontown/racing/RaceHeadFrame.py @@ -0,0 +1,34 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from toontown.toon import ToonHead + +class RaceHeadFrame(DirectFrame): + + def __init__(self, av = None, color = Vec4(1, 1, 1, 1), *args, **kwargs): + self.panelGeom = loader.loadModel('phase_4/models/karting/racing_panel') + self.panelGeom.find('**/*fg').setColor(color) + opts = {'relief': None, + 'geom': self.panelGeom, + 'geom_scale': (1, 1, 0.5), + 'pos': (0, 0, 0)} + opts.update(kwargs) + apply(DirectFrame.__init__, (self,) + args, opts) + self.initialiseoptions(RaceHeadFrame) + if av: + self.setAv(av) + return + + def setAv(self, av): + self.head = self.stateNodePath[0].attachNewNode('head', 20) + self.head.setPosHprScale(0, -0.5, -0.09, 180.0, 0.0, 0.0, 0.2, 0.2, 0.2) + self.headModel = ToonHead.ToonHead() + self.headModel.setupHead(av.style, forGui=1) + self.headModel.reparentTo(self.head) + + def destroy(self): + self.headModel.delete() + del self.headModel + self.head.removeNode() + del self.head + DirectFrame.destroy(self) diff --git a/toontown/racing/__init__.py b/toontown/racing/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/rpc/ToontownRPCConnection.py b/toontown/rpc/ToontownRPCConnection.py new file mode 100755 index 00000000..3841cba5 --- /dev/null +++ b/toontown/rpc/ToontownRPCConnection.py @@ -0,0 +1,312 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.stdpy import threading +from direct.stdpy import threading2 +import httplib +import json +import socket +import time + +from toontown.rpc.ToontownRPCDispatcher import ToontownRPCDispatcher + + +class ToontownRPCConnection: + notify = directNotify.newCategory('ToontownRPCConnection') + + def __init__(self, socket, handler): + self.socket = socket + + self.dispatcher = ToontownRPCDispatcher(handler) + + self.socketLock = threading.Lock() + self.readLock = threading.RLock() + self.writeLock = threading.RLock() + + self.readBuffer = '' + + self.writeQueue = [] + self.writeSemaphore = threading.Semaphore(0) + self.writeThread = threading.Thread(target=self.__writeThread) + self.writeThread.start() + + def __readHeaders(self): + # Acquire a read lock so that nothing intervenes: + self.readLock.acquire() + + # Read data until we find the '\r\n\r\n' terminator: + while '\r\n\r\n' not in self.readBuffer: + try: + self.readBuffer += self.socket.recv(2048) + except: + self.readLock.release() + return {} + + if not self.readBuffer: + # It looks like we have nothing to read. + self.readLock.release() + return {} + + # Collect the data before the terminator: + terminatorIndex = self.readBuffer.find('\r\n\r\n') + data = self.readBuffer[:terminatorIndex] + + # Truncate the remaining data: + self.readBuffer = self.readBuffer[terminatorIndex + 4:] + + # We're done working with the read buffer, so: + self.readLock.release() + + # Return the parsed headers in the form of a dictionary: + return self.__parseHeaders(data) + + def __parseHeaders(self, data): + headers = {} + + for i, line in enumerate(data.split('\n')): + line = line.rstrip('\r') + + if i == 0: + # This is the HTTP request. + words = line.split(' ') + if len(words) != 3: + self.writeHTTPError(400) + return {} + + command, _, version = words + + if command != 'POST': + self.writeHTTPError(501) + return {} + + if version not in ('HTTP/1.0', 'HTTP/1.1'): + self.writeHTTPError(505) + return {} + else: + # This is an HTTP header. + words = line.split(': ', 1) + if len(words) != 2: + self.writeHTTPError(400) + return {} + + headers[words[0].lower()] = words[1] + + return headers + + def read(self, timeout=None): + """ + Read an HTTP POST request from the socket, and return the body. + """ + self.socketLock.acquire() + self.socket.settimeout(timeout) + + # Read our HTTP headers: + headers = self.__readHeaders() + + if not headers: + # It looks like we have nothing to read. + self.socketLock.release() + return '' + + # We need a content-length in order to read POST data: + contentLength = headers.get('content-length', '') + if (not contentLength) or (not contentLength.isdigit()): + self.socketLock.release() + self.writeHTTPError(400) + return '' + + # Acquire a read lock so nothing touches the read buffer while we work: + self.readLock.acquire() + + contentLength = int(contentLength) + + # Ensure we have all of our content: + while len(self.readBuffer) < contentLength: + try: + self.readBuffer += self.socket.recv(2048) + except: + self.readLock.release() + self.socketLock.release() + return '' + + if not self.readBuffer: + # It looks like we have nothing to read. + self.readLock.release() + self.socketLock.release() + return '' + + # Collect the content: + data = self.readBuffer[:contentLength] + + # Truncate the remaining data: + self.readBuffer = self.readBuffer[contentLength + 1:] + + # Release our thread locks: + self.readLock.release() + self.socketLock.release() + + try: + return data.decode('utf-8') + except UnicodeDecodeError: + return '' + + def __writeNow(self, data, timeout=None): + # Acquire a write lock so nothing intervenes: + self.writeLock.acquire() + + self.socket.settimeout(timeout) + + # Ensure the data ends with a new line: + if not data.endswith('\n'): + data += '\n' + + while data: + try: + sent = self.socket.send(data) + except: + break + if sent == 0: + break + data = data[sent:] + + # Release our write lock: + self.writeLock.release() + + def __writeThread(self): + while True: + self.writeSemaphore.acquire() + + # Ensure we have a request in the write queue: + if not self.writeQueue: + continue + + request = self.writeQueue.pop(0) + + terminate = request.get('terminate') + if terminate is not None: + # Clear the write queue, and stop: + self.writeQueue = [] + terminate.set() + break + + # Write the data to the socket: + data = request.get('data') + if data: + self.__writeNow(data, timeout=request.get('timeout')) + + def write(self, data, timeout=None): + """ + Add data to the write queue. + """ + self.writeQueue.append({'data': data, 'timeout': timeout}) + self.writeSemaphore.release() + + def writeHTTPResponse(self, body, contentType=None, code=200): + """ + Write an HTTP response to the socket. + """ + response = 'HTTP/1.1 %d %s\r\n' % (code, httplib.responses.get(code)) + + # Add the standard headers: + response += 'Date: %s\r\n' % time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime()) + response += 'Server: TTS-RPCServer/0.1\r\n' + + # Add the content headers: + response += 'Content-Length: %d\r\n' % len(body) + if contentType is not None: + response += 'Content-Type: %s\r\n' % contentType + + # Add the body: + response += '\r\n' + body + + # Finally, send it out: + self.write(response, timeout=5) + + def writeHTTPError(self, code): + """ + Write an HTTP error response to the socket. + """ + self.notify.warning('Received a bad HTTP request: ' + str(code)) + body = '%d %s' % (code, httplib.responses.get(code)) + self.writeHTTPResponse(body, contentType='text/plain', code=code) + + def writeJSONResponse(self, response, id=None): + """ + Write a JSON response object to the socket. + """ + response.update({'jsonrpc': '2.0', 'id': id}) + try: + body = json.dumps(response, encoding='latin-1') + except TypeError: + self.writeJSONError(-32603, 'Internal error') + return + self.writeHTTPResponse(body, contentType='application/json') + + def writeJSONError(self, code, message, id=None): + """ + Write a JSON error response object to the socket. + """ + self.notify.warning('Received a bad JSON request: %d %s' % (code, message)) + response = {'error': {'code': code, 'message': message}} + self.writeJSONResponse(response, id=id) + + def close(self): + """ + Wait until the write queue is empty, then shutdown and close our + socket. + """ + terminate = threading2.Event() + self.writeQueue.append({'terminate': terminate}) + self.writeSemaphore.release() + terminate.wait() + + try: + self.socket.shutdown(socket.SHUT_RDWR) + except socket.error: + pass + self.socket.close() + + def dispatchUntilEmpty(self): + """ + Read and dispatch until there is nothing left. + """ + while True: + data = self.read(timeout=5) + + if not data: + # We have nothing left to read. + break + + try: + request = json.loads(data) + except ValueError: + self.writeJSONError(-32700, 'Parse error') + continue + + request = ToontownRPCRequest( + self, request.get('method'), params=request.get('params') or (), + id=request.get('id'), notification=('id' not in request)) + self.dispatcher.dispatch(request) + + +class ToontownRPCRequest: + def __init__(self, connection, method, params=(), id=None, notification=False): + self.connection = connection + self.method = method + self.params = params + self.id = id + self.notification = notification + + def result(self, result): + """ + Write a result response object to the connection as long as this isn't + a notification. + """ + if not self.notification: + self.connection.writeJSONResponse({'result': result}, id=self.id) + + def error(self, code, message): + """ + Write an error response object to the connection as long as this isn't + a notification. + """ + if not self.notification: + self.connection.writeJSONError(code, message, id=self.id) diff --git a/toontown/rpc/ToontownRPCDispatcher.py b/toontown/rpc/ToontownRPCDispatcher.py new file mode 100755 index 00000000..9e21669c --- /dev/null +++ b/toontown/rpc/ToontownRPCDispatcher.py @@ -0,0 +1,50 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify + + +class ToontownRPCDispatcher: + notify = directNotify.newCategory('ToontownRPCDispatcher') + + def __init__(self, handler): + self.handler = handler + + def dispatch(self, request): + """ + Handle a JSON-RPC 2.0 request. + """ + if (not isinstance(request.method, basestring)) or \ + (not isinstance(request.params, (tuple, list, dict))): + request.error(-32600, 'Invalid Request') + return + + # Grab the method from the handler: + method = getattr(self.handler, 'rpc_' + request.method, None) + if method is None: + request.error(-32601, 'Method not found') + return + + # Find the token in the params, authenticate it, and then remove it + # from the params: + token = None + if isinstance(request.params, dict): + token = request.params.get('token') + del request.params['token'] + elif len(request.params) > 0: + token = request.params[0] + params = request.params[1:] + if not isinstance(token, basestring): + request.error(-32000, 'No token provided') + return + error = self.handler.authenticate(token, method) + if error is not None: + # Authentication wasn't successful. Send the error: + request.error(*error) + return + + # Finally, attempt to call the method: + try: + if isinstance(params, dict): + request.result(method(**params)) + else: + request.result(method(*params)) + except: + request.error(-32603, describeException()) diff --git a/toontown/rpc/ToontownRPCHandler.py b/toontown/rpc/ToontownRPCHandler.py new file mode 100755 index 00000000..ff16e881 --- /dev/null +++ b/toontown/rpc/ToontownRPCHandler.py @@ -0,0 +1,827 @@ +import datetime +from direct.distributed.MsgTypes import CLIENTAGENT_EJECT +from direct.distributed.PyDatagram import PyDatagram +from direct.stdpy import threading2 +import re, json + +from otp.distributed import OtpDoGlobals +from toontown.distributed.ShardStatusReceiver import ShardStatusReceiver +from toontown.rpc.ToontownRPCHandlerBase import * +from toontown.suit.SuitInvasionGlobals import INVASION_TYPE_NORMAL +from toontown.toon import ToonDNA +from toontown.toonbase import TTLocalizer +from toontown.uberdog.ClientServicesManagerUD import executeHttpRequest + + +class ToontownRPCHandler(ToontownRPCHandlerBase): + def __init__(self, air): + ToontownRPCHandlerBase.__init__(self, air) + + self.shardStatus = ShardStatusReceiver(air) + + # --- TESTS --- + + @rpcmethod(accessLevel=COMMUNITY_MANAGER) + def rpc_ping(self, data): + """ + Summary: + Responds with the provided [data]. This method exists for testing + purposes. + + Parameters: + [any data] = The data to be given back in response. + + Example response: 'pong' + """ + return data + + # --- GENERAL --- + + @rpcmethod(accessLevel=SYSTEM_ADMINISTRATOR) + def rpc_queryObject(self, doId): + """ + Summary: + Responds with the values of all database fields associated with the + provided [doId]. + + Parameters: + [int doId] = The ID of the object to query database fields on. + + Example response: + On success: ['DistributedObject', {'fieldName': ('arg1', ...), ...}] + On failure: [None, None] + """ + result = [] + unblocked = threading2.Event() + + + def callback(dclass, fields): + if dclass is not None: + dclass = dclass.getName() + result.extend([dclass, fields]) + unblocked.set() + + + self.air.dbInterface.queryObject(self.air.dbId, doId, callback) + + # Block until the callback is executed: + unblocked.wait() + + return result + + @rpcmethod(accessLevel=SYSTEM_ADMINISTRATOR) + def rpc_updateObject(self, doId, dclassName, newFields, oldFields=None): + """ + Summary: + Update the field(s) of the object associated with the provided + [doId]. If is provided, then this method will fail if + the object's current fields don't match. + + Parameters: + [int doId] = The ID of the object whose fields are to be updated. + [str dclassName] = The name of the object's DClass. + [dict newFields] = The new field values. + = The old field values to assert. + + Example response: + On success: True + On failure: False + """ + # Ensure that the provided DClass exists: + if dclassName not in self.air.dclassesByName: + dclassName += 'UD' + if dclassName not in self.air.dclassesByName: + return False + + dclass = self.air.dclassesByName[dclassName] + + if oldFields is None: + self.air.dbInterface.updateObject( + self.air.dbId, doId, dclass, newFields) + return True + + result = [True] + unblocked = threading2.Event() + + + def callback(fields): + if fields is not None: + result[0] = False + unblocked.set() + + + self.air.dbInterface.updateObject( + self.air.dbId, doId, dclass, newFields, oldFields=oldFields, + callback=callback) + + # Block until the callback is executed: + unblocked.wait() + + return result[0] + + @rpcmethod(accessLevel=SYSTEM_ADMINISTRATOR) + def rpc_setField(self, doId, dclassName, fieldName, args=[]): + """ + Summary: + Set the value of the field named [fieldName] on the suggested + object. + + Parameters: + [int doId] = The ID of the object whose field is being modified. + [str dclassName] = The name of the object's DClass. + [str fieldName] = The name of the field to be modified. + [list args] = The new value for the field. + + Example response: + On success: True + On failure: False + """ + # Ensure that the provided DClass exists: + if dclassName not in self.air.dclassesByName: + dclassName += 'UD' + if dclassName not in self.air.dclassesByName: + return False + + dclass = self.air.dclassesByName[dclassName] + + datagram = dclass.aiFormatUpdate( + fieldName, doId, doId, self.air.ourChannel, args) + self.air.send(datagram) + + return True + + # --- MESSAGES --- + + @rpcmethod(accessLevel=SYSTEM_ADMINISTRATOR) + def rpc_messageChannel(self, channel, message): + """ + Summary: + Broadcasts a [message] to any client whose Client Agent is + subscribed to the provided [channel]. + + Parameters: + [int channel] = The channel to direct the message to. + [str message] = The message to broadcast. + """ + dclass = self.air.dclassesByName['ClientServicesManagerUD'] + datagram = dclass.aiFormatUpdate( + 'systemMessage', OtpDoGlobals.OTP_DO_ID_CLIENT_SERVICES_MANAGER, + channel, 1000000, [message]) + self.air.send(datagram) + + @rpcmethod(accessLevel=SYSTEM_ADMINISTRATOR) + def rpc_messageAll(self, message): + """ + Summary: Broadcasts a [message] to all clients. + + Parameters: + [str message] = The message to broadcast. + """ + self.rpc_messageChannel(10, message) + + @rpcmethod(accessLevel=SYSTEM_ADMINISTRATOR) + def rpc_messageShard(self, shardId, message): + """ + Summary: + Broadcasts a [message] to all clients under the provided [shardId]. + + Parameters: + [int shardId] = The ID of the shard to direct the message to. + [str message] = The message to broadcast. + """ + # Get the ID of the ToontownDistrict object: + districtId = shardId + 1 + + # Use it to get the uber zone's channel: + channel = (districtId<<32) | 2 + + self.rpc_messageChannel(channel, message) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_messageStaff(self, message): + """ + Summary: + Broadcasts a [message] to any client whose access level is higher + than that of a standard user. + + Parameters: + [str message] = The message to broadcast. + """ + self.rpc_messageChannel(OtpDoGlobals.OTP_STAFF_CHANNEL, message) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_messageUser(self, userId, message): + """ + Summary: + Sends a [message] to the client associated with the provided + [userId]. + + Parameters: + [int/str userId] = The ID of the user to send the message to. + [str message] = The message to send. + """ + accountId = self.rpc_getUserAccountId(userId) + if accountId is not None: + self.rpc_messageAccount(accountId, message) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_messageAccount(self, accountId, message): + """ + Summary: + Sends a [message] to the client associated with the provided + [accountId]. + + Parameters: + [int accountId] = The ID of the account to send the message to. + [str message] = The message to send. + """ + channel = accountId + (1003L<<32) + self.rpc_messageChannel(channel, message) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_messageAvatar(self, avId, message): + """ + Summary: + Sends a [message] to the client associated with the provided + [avId]. + + Parameters: + [int avId] = The ID of the avatar to send the message to. + [str message] = The message to send. + """ + channel = avId + (1001L<<32) + self.rpc_messageChannel(channel, message) + + # --- KICKS --- + + @rpcmethod(accessLevel=SYSTEM_ADMINISTRATOR) + def rpc_kickChannel(self, channel, code, reason): + """ + Summary: + Kicks any client whose Client Agent is subscribed to the provided + [channel]. + + Parameters: + [int channel] = The channel to kick. + [int code] = The code for the kick. + [str reason] = The reason for the kick. + """ + datagram = PyDatagram() + datagram.addServerHeader(channel, self.air.ourChannel, CLIENTAGENT_EJECT) + datagram.addUint16(code) + datagram.addString(reason) + self.air.send(datagram) + + @rpcmethod(accessLevel=SYSTEM_ADMINISTRATOR) + def rpc_kickAll(self, code, reason): + """ + Summary: Kicks all clients. + + Parameters: + [int code] = The code for the kick. + [str reason] = The reason for the kick. + """ + self.rpc_kickChannel(10, code, reason) + + @rpcmethod(accessLevel=SYSTEM_ADMINISTRATOR) + def rpc_kickShard(self, shardId, code, reason): + """ + Summary: Kicks all clients under the provided [shardId]. + + Parameters: + [int shardId] = The ID of the shard to kick. + [int code] = The code for the kick. + [str reason] = The reason for the kick. + """ + # Get the ID of the ToontownDistrict object: + districtId = shardId + 1 + + # Use it to get the uber zone's channel: + channel = (districtId<<32) | 2 + + self.rpc_kickChannel(channel, code, reason) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_kickUser(self, userId, code, reason): + """ + Summary: Kicks the client associated with the provided [userId]. + + Parameters: + [int/str userId] = The ID of the user to kick. + [int code] = The code for the kick. + [str reason] = The reason for the kick. + """ + accountId = self.rpc_getUserAccountId(userId) + if accountId is not None: + self.rpc_kickAccount(accountId, code, reason) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_kickAccount(self, accountId, code, reason): + """ + Summary: Kicks the client associated with the provided [accountId]. + + Parameters: + [int accountId] = The ID of the account to kick. + [int code] = The code for the kick. + [str reason] = The reason for the kick. + """ + channel = accountId + (1003L<<32) + self.rpc_kickChannel(channel, code, reason) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_kickAvatar(self, avId, code, reason): + """ + Summary: Kicks the client associated with the provided [avId]. + + Parameters: + [int avId] = The ID of the avatar to kick. + [int code] = The code for the kick. + [str reason] = The reason for the kick. + """ + channel = avId + (1001L<<32) + self.rpc_kickChannel(channel, code, reason) + + # --- BANS --- + + @rpcmethod(accessLevel=MODERATOR) + def rpc_banUser(self, userId, duration, reason): + """ + Summary: + Bans the user associated with the provided [userId] for the + specified [duration]. + + Parameters: + [int/str userId] = The ID of the user to ban. + [int duration] = The ban's duration in hours. If this is 0 or less, + the user will be permanently banned. + [str reason] = A short description of why this user is being + banned. This can be one of the following values: 'hacking', + 'language', 'other'. + + Example response: + On success: True + On failure: False + """ + if reason not in ('hacking', 'language', 'other'): + return False + self.air.writeServerEvent('ban', userId, duration, reason) + if duration > 0: + now = datetime.date.today() + release = str(now + datetime.timedelta(hours=duration)) + else: + release = '0000-00-00' # Permanent ban. + executeHttpRequest('accounts/ban/', Id=userId, Release=release, + Reason=reason) + self.rpc_kickUser(userId, 152, reason) + return True + + @rpcmethod(accessLevel=MODERATOR) + def rpc_banAccount(self, accountId, duration, reason): + """ + Summary: + Bans the user associated with the provided [accountId] for the + specified [duration]. + + Parameters: + [int accountId] = The ID of the account associated with the user to + ban. + [int duration] = The ban's duration in hours. If this is 0 or less, + the user will be permanently banned. + [str reason] = A short description of why this user is being + banned. This can be one of the following values: 'hacking', + 'language', 'other'. + + Example response: + On success: True + On failure: False + """ + userId = self.rpc_getAccountUserId(accountId) + return self.rpc_banUser(userId, duration, reason) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_banAvatar(self, avId, duration, reason): + """ + Summary: + Bans the user associated with the provided [avId] for the specified + [duration]. + + Parameters: + [int/str avId] = The ID of the avatar associated with the user to + be banned. + [int duration] = The ban's duration in hours. If this is 0 or less, + the user will be permanently banned. + [str reason] = A short description of why this user is being + banned. This can be one of the following values: 'hacking', + 'language', 'other'. + + Example response: + On success: True + On failure: False + """ + userId = self.rpc_getAvatarUserId(avId) + return self.rpc_banUser(userId, duration, reason) + + # --- USERS --- + + @rpcmethod(accessLevel=MODERATOR) + def rpc_getUserAccountId(self, userId): + """ + Summary: + Responds with the ID of the account associated with the provided + [userId]. + + Parameters: + [int/str userId] = The ID of the user to query the account ID on. + + Example response: + On success: 100000000 + On failure: None + """ + response = executeHttpRequest('accountid', username=str(userId)) + if response is not None: + response = json.loads(response) + if response['success'] is True: + return int(response['accountId']) + else: + print response['error'] + return False + + @rpcmethod(accessLevel=MODERATOR) + def rpc_getUserAvatars(self, userId): + """ + Summary: + Responds with a list of avatar IDs associated with the provided + [userId]. + + Parameters: + [int/str userId] = The ID of the user to query the avatars on. + + Example response: + On success: [0, 100000001, 0, 0, 0, 0] + On failure: None + """ + accountId = self.rpc_getUserAccountId(userId) + if accountId is not None: + return self.rpc_getAccountAvatars(accountId) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_getUserDeletedAvatars(self, userId): + """ + Summary: + Responds with a list of deleted avatar IDs associated with the + provided [userId], along with the time at which each avatar was + deleted. + + Parameters: + [int/str userId] = The ID of the user to query the deleted avatars + on. + + Example response: + On success: [[100000001, 1409665000], ...] + On failure: None + """ + accountId = self.rpc_getUserAccountId(userId) + if accountId is not None: + return self.rpc_getAccountDeletedAvatars(accountId) + + # --- ACCOUNTS --- + + @rpcmethod(accessLevel=MODERATOR) + def rpc_getAccountUserId(self, accountId): + """ + Summary: + Responds with the ID of the user associated with the provided + [accountId]. + + Parameters: + [int accountId] = The ID of the account to query the user ID on. + + Example response: + On success: 1 + On failure: None + """ + dclassName, fields = self.rpc_queryObject(accountId) + if dclassName == 'Account': + return fields['ACCOUNT_ID'] + + @rpcmethod(accessLevel=MODERATOR) + def rpc_getAccountAvatars(self, accountId): + """ + Summary: + Responds with a list of avatar IDs associated with the provided + [accountId]. + + Parameters: + [int accountId] = The ID of the account to query the avatar IDs on. + + Example response: + On success: [0, 100000001, 0, 0, 0, 0] + On failure: None + """ + dclassName, fields = self.rpc_queryObject(accountId) + if dclassName == 'Account': + return fields['ACCOUNT_AV_SET'] + + @rpcmethod(accessLevel=MODERATOR) + def rpc_getAccountDeletedAvatars(self, accountId): + """ + Summary: + Responds with a list of deleted avatar IDs associated with the + provided [accountId], along with the time at which each avatar was + deleted. + + Parameters: + [int accountId] = The ID of the account to query the deleted + avatars on. + + Example response: + On success: [[100000001, 1409665000], ...] + On failure: None + """ + dclassName, fields = self.rpc_queryObject(accountId) + if dclassName == 'Account': + return fields['ACCOUNT_AV_SET_DEL'] + + # --- AVATARS --- + + @rpcmethod(accessLevel=MODERATOR) + def rpc_getAvatarUserId(self, avId): + """ + Summary: + Responds with the ID of the user associated with the provided + [avId]. + + Parameters: + [int avId] = The ID of the avatar to query the user ID on. + + Example response: + On success: 1 + On failure: None + """ + accountId = self.rpc_getAvatarAccountId(avId) + if accountId is not None: + return self.rpc_getAccountUserId(accountId) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_getAvatarAccountId(self, avId): + """ + Summary: + Responds with the ID of the account associated with the provided + [avId]. + + Parameters: + [int avId] = The ID of the avatar to query the account ID on. + + Example response: + On success: 100000000 + On failure: None + """ + dclassName, fields = self.rpc_queryObject(avId) + if dclassName == 'DistributedToon': + return fields['setDISLid'][0] + + @rpcmethod(accessLevel=MODERATOR) + def rpc_getAvatarAvatars(self, avId): + """ + Summary: + Responds with a list of avatar IDs associated with the provided + [avId]. + + Parameters: + [int avId] = The ID of the avatar to query the avatar IDs on. + + Example response: + On success: [0, 100000001, 0, 0, 0, 0] + On failure: None + """ + accountId = self.rpc_getAvatarAccountId(avId) + if accountId is not None: + return self.rpc_getAccountAvatars(accountId) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_getAvatarDeletedAvatars(self, avId): + """ + Summary: + Responds with a list of deleted avatar IDs associated with the + provided [avId], along with the time at which each avatar was + deleted. + + Parameters: + [int avId] = The ID of the avatar to query the deleted avatars on. + + Example response: + On success: [[100000001, 1409665000], ...] + On failure: None + """ + accountId = self.rpc_getAvatarAccountId(avId) + if accountId is not None: + return self.rpc_getAccountDeletedAvatars(accountId) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_getAvatarDetails(self, avId): + """ + Summary: + Responds with basic details on the avatar associated with the + provided [avId]. + + Parameters: + [int avId] = The ID of the avatar to query basic details on. + + Example response: + On success: + { + 'name': 'Toon Name', + 'species': 'cat', + 'head-color': (1, 0, 0, 1), + 'max-hp': 15, + 'online': True + } + On failure: None + """ + dclassName, fields = self.rpc_queryObject(avId) + if dclassName == 'DistributedToon': + result = {} + + result['name'] = fields['setName'][0] + + dna = ToonDNA.ToonDNA() + dna.makeFromNetString(fields['setDNAString'][0]) + result['species'] = ToonDNA.getSpeciesName(dna.head) + + result['head_color'] = dna.headColor + result['max_hp'] = fields['setMaxHp'][0] + result['hp'] = fields['setHp'][0] + result['online'] = (avId in self.air.friendsManager.onlineToons) + + return result + + @rpcmethod(accessLevel=MODERATOR) + def rpc_findAvatarsByName(self, needle): + """ + Summary: + Responds with the IDs of each avatar whose name matches, or + contains the provided [needle]. + + Parameters: + [str needle] = The string to filter avatars by name with. This is + case insensitive. + + Example response: [100000001, ...] + """ + if not config.GetBool('want-mongo', False): + return [] + if not needle: + return [] + self.air.database.astron.objects.ensure_index('fields.setName') + exp = re.compile('.*%s.*' % needle, re.IGNORECASE) + result = self.air.database.astron.objects.find({'fields.setName._0': exp}) + return [avatar['_id'] for avatar in result] + + # --- SHARDS --- + + @rpcmethod(accessLevel=USER) + def rpc_listShards(self): + """ + Summary: + Responds with the current status of each shard that has ever been + created in the lifetime of the UberDOG. + + Example response: + { + 401000000: { + 'name': 'District Name' + 'available': True, + 'created': 1409665000, + 'population': 150, + 'invasion': { + 'type': 'Flunky', + 'flags': 0, + 'remaining': 1000, + 'total': 1000, + 'start': 1409665000 + } + }, + ... + } + """ + return self.shardStatus.getShards() + + # --- INVASIONS --- + + @rpcmethod(accessLevel=ADMINISTRATOR) + def rpc_startInvasion(self, shardId, suitDeptIndex=None, suitTypeIndex=None, + flags=0, type=INVASION_TYPE_NORMAL): + """ + Summary: + Starts an invasion under the provided [shardId] with the specified + configuration. + + Parameters: + [int shardId] = The ID of the shard to start the invasion in. + = The invading Cog's department index. + = The invading Cog's type index. + = Extra invasion flags. + = The invasion type. + """ + self.air.sendNetEvent( + 'startInvasion', + [shardId, suitDeptIndex, suitTypeIndex, flags, type]) + + @rpcmethod(accessLevel=ADMINISTRATOR) + def rpc_stopInvasion(self, shardId): + """ + Summary: + Stops any invasion currently running under the provided [shardId]. + + Parameters: + [int shardId] = The ID of the shard that is running the invasion to + be terminated. + """ + self.air.sendNetEvent('stopInvasion', [shardId]) + + # --- NAME APPROVAL --- + + @rpcmethod(accessLevel=MODERATOR) + def rpc_approveName(self, avId): + """ + Summary: + Approves the pending name of the avatar associated with the + provided [avId]. + + Parameters: + [int avId] = The ID of the avatar whose pending name is to be + approved. + + Example response: + On success: True + On failure: False + """ + newFields = {'setWishNameState': 'APPROVED'} + oldFields = {'setWishNameState': 'PENDING'} + return self.rpc_updateObject( + avId, 'DistributedToonUD', newFields, oldFields=oldFields) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_rejectName(self, avId): + """ + Summary: + Rejects the pending name of the avatar associated with the provided + [avId]. + + Parameters: + [int avId] = The ID of the avatar whose pending name is to be + rejected. + + Example response: + On success: True + On failure: False + """ + newFields = {'setWishNameState': 'REJECTED'} + oldFields = {'setWishNameState': 'PENDING'} + return self.rpc_updateObject( + avId, 'DistributedToonUD', newFields, oldFields=oldFields) + + @rpcmethod(accessLevel=MODERATOR) + def rpc_getChatSettings(self, accId): + """ + Summary: + Retrieves the chat settings of the account associated with the provided + [accId]. + + Parameters: + [int accId] = The ID of the account whose chat settings + are to be retreived. + + Example response: + On success: [uint8[sp+, tf]] + On failure: False + """ + dclassName, fields = self.rpc_queryObject(int(accId)) + if dclassName == 'Account': + if 'CHAT_SETTINGS' in fields: + return fields['CHAT_SETTINGS'] + # The chat settings haven't been set yet, so we'll + # want to do that now. + self.rpc_setChatSettings(accId) + return [1, 1] + return False + + @rpcmethod(accessLevel=MODERATOR) + def rpc_setChatSettings(self, accId, speedChatPlus = 1, trueFriends = 1): + """ + Summary: + Sets the chat settings of the account associated with the provided + [accId]. + + Parameters: + [int accId] = The ID of the account whose chat settings + are to be changed. + [uint8[sp+, tf]] = The chat settings - SpeedChat Plus and + True Friends + + Example response: + On success: True + On failure: False + """ + return self.rpc_updateObject(accId, 'AccountUD', {'CHAT_SETTINGS': + [int(speedChatPlus), int(trueFriends)]}) diff --git a/toontown/rpc/ToontownRPCHandlerBase.py b/toontown/rpc/ToontownRPCHandlerBase.py new file mode 100755 index 00000000..a5aaec0f --- /dev/null +++ b/toontown/rpc/ToontownRPCHandlerBase.py @@ -0,0 +1,84 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +import json +import time + +from Crypto.Cipher import AES + + +UNKNOWN = 700 +USER = 100 +COMMUNITY_MANAGER = 200 +MODERATOR = 300 +ARTIST = 400 +PROGRAMMER = 500 +ADMINISTRATOR = 600 +SYSTEM_ADMINISTRATOR = 700 + + +class RPCMethod: + def __init__(self, accessLevel=UNKNOWN): + self.accessLevel = accessLevel + + def __call__(self, method): + method.accessLevel = self.accessLevel + return method + + +rpcmethod = RPCMethod + + +class ToontownRPCHandlerBase: + notify = directNotify.newCategory('ToontownRPCHandlerBase') + + def __init__(self, air): + self.air = air + + def authenticate(self, token, method): + """ + Ensure the provided token is valid, and meets the access level + requirements of the method. + """ + # First, base64 decode the token: + try: + token = token.decode('base64') + except TypeError: + return (-32001, 'Token decode failure') + + # Ensure this token is a valid size: + if (not token) or ((len(token) % 16) != 0): + return (-32002, 'Invalid token length') + + # Next, decrypt the token using AES-128 in CBC mode: + rpcServerSecret = config.GetString('rpc-server-secret', '6163636f756e7473') + + # Ensure that our secret is the correct size: + if len(rpcServerSecret) > AES.block_size: + self.notify.error('rpc-server-secret is too big!') + elif len(rpcServerSecret) < AES.block_size: + self.notify.error('rpc-server-secret is too small!') + + # Take the initialization vector off the front of the token: + iv = token[:AES.block_size] + + # Truncate the token to get our cipher text: + cipherText = token[AES.block_size:] + + # Decrypt! + cipher = AES.new(rpcServerSecret, mode=AES.MODE_CBC, IV=iv) + try: + token = json.loads(cipher.decrypt(cipherText).replace('\x00', '')) + if ('timestamp' not in token) or (not isinstance(token['timestamp'], int)): + raise ValueError + if ('accesslevel' not in token) or (not isinstance(token['accesslevel'], int)): + raise ValueError + except ValueError: + return (-32003, 'Invalid token') + + # Next, check if this token has expired: + period = config.GetInt('rpc-token-period', 5) + delta = int(time.time()) - token['timestamp'] + if delta > period: + return (-32004, 'Token expired') + + if token['accesslevel'] < method.accessLevel: + return (-32005, 'Insufficient access') diff --git a/toontown/rpc/ToontownRPCServer.py b/toontown/rpc/ToontownRPCServer.py new file mode 100755 index 00000000..8f38a49f --- /dev/null +++ b/toontown/rpc/ToontownRPCServer.py @@ -0,0 +1,150 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.stdpy import threading +import errno +from panda3d.core import TP_normal +import select +import socket +import urlparse + +from toontown.rpc.ToontownRPCConnection import ToontownRPCConnection + + +class ToontownRPCServer: + notify = directNotify.newCategory('ToontownRPCServer') + + def __init__(self, endpoint, handler): + self.handler = handler + + # Parse the endpoint: + url = urlparse.urlparse(endpoint) + + # We only support the http scheme: + if url.scheme != 'http': + self.notify.warning('Invalid scheme for endpoint: ' + str(url.scheme)) + + # Parse the hostname, and port: + self.hostname = url.hostname or 'localhost' + self.port = url.port or 8080 + + self.listenerSocket = None + self.connections = {} + self.dispatchThreads = {} + + def getUniqueName(self): + """ + Returns a unique identifier for this instance. This is primarily used + for creating unique task names. + """ + return 'ToontownRPCServer-' + str(id(self)) + + def start(self, useTaskChain=False): + """ + Serve until stop() is called. + """ + taskChain = None + if useTaskChain and (not taskMgr.hasTaskChain('ToontownRPCServer')): + taskChain = 'ToontownRPCServer' + taskMgr.setupTaskChain(taskChain, numThreads=1, threadPriority=TP_normal) + + # Create a socket to listen for incoming connections: + self.listenerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.listenerSocket.setblocking(0) + self.listenerSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.listenerSocket.bind((self.hostname, self.port)) + self.listenerSocket.listen(5) + + # Start polling: + taskName = self.getUniqueName() + '-pollTask' + taskMgr.add(self.pollTask, taskName, taskChain=taskChain) + + def stop(self): + """ + Stop serving. + """ + # Stop polling: + taskName = self.getUniqueName() + '-pollTask' + assert taskMgr.hasTaskNamed(taskName) + taskMgr.remove(taskName) + + # Close any open connections: + for k, v in self.connections.items(): + v.close() + del self.connections[k] + + # Shutdown and close the listener socket: + try: + self.listenerSocket.shutdown(socket.SHUT_RDWR) + except socket.error: + pass + self.listenerSocket.close() + self.listenerSocket = None + + def dispatchThread(self, socket): + """ + Call dispatchUntilEmpty() on the provided socket's connection, and then + clean up. + """ + connection = self.connections[socket] + connection.dispatchUntilEmpty() + connection.close() + del self.connections[socket] + del self.dispatchThreads[socket] + + def pollOnce(self): + """ + Poll for incoming data once. + """ + try: + rlist = select.select([self.listenerSocket] + self.connections.keys(), [], [])[0] + except: + # It's likely that one or more of our sockets is no longer valid. + + # If it's our listener socket, we can't continue: + try: + self.listenerSocket.fileno() + except: + self.notify.error('The listener socket is no longer valid!') + + # Otherwise, discard the faulty sockets, and wait for the next poll + # iteration: + for socket in self.connections.keys(): + try: + socket.fileno() + socket.getpeername() + except: + del self.connections[socket] + if socket in self.dispatchThreads: + del self.dispatchThreads[socket] + + return + + if self.listenerSocket in rlist: + self.handleNewConnection() + + for socket in rlist: + connection = self.connections.get(socket) + if connection is None: + continue + if socket in self.dispatchThreads: + continue + self.dispatchThreads[socket] = threading.Thread( + target=self.dispatchThread, args=[socket]) + self.dispatchThreads[socket].start() + + def pollTask(self, task): + """ + Continuously poll for incoming data. + """ + self.pollOnce() + return task.cont + + def handleNewConnection(self): + """ + Handle an incoming connection. + """ + try: + conn = self.listenerSocket.accept()[0] + except socket.error, e: + if e.args[0] != errno.EWOULDBLOCK: + raise e + self.connections[conn] = ToontownRPCConnection(conn, self.handler) diff --git a/toontown/rpc/__init__.py b/toontown/rpc/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/safezone/BRPlayground.py b/toontown/safezone/BRPlayground.py new file mode 100755 index 00000000..9138dea5 --- /dev/null +++ b/toontown/safezone/BRPlayground.py @@ -0,0 +1,18 @@ +from direct.task.Task import Task +from toontown.safezone import Playground +import random + +class BRPlayground(Playground.Playground): + def enter(self, requestStatus): + Playground.Playground.enter(self, requestStatus) + taskMgr.doMethodLater(1, self.__windTask, 'BR-wind') + + def exit(self): + Playground.Playground.exit(self) + taskMgr.remove('BR-wind') + + def __windTask(self, task): + base.playSfx(random.choice(self.loader.windSound)) + time = random.random() * 8.0 + 1 + taskMgr.doMethodLater(time, self.__windTask, 'BR-wind') + return Task.done diff --git a/toontown/safezone/BRSafeZoneLoader.py b/toontown/safezone/BRSafeZoneLoader.py new file mode 100755 index 00000000..ccff4642 --- /dev/null +++ b/toontown/safezone/BRSafeZoneLoader.py @@ -0,0 +1,34 @@ +from toontown.safezone import BRPlayground +from toontown.safezone import SafeZoneLoader +import SZUtil + +class BRSafeZoneLoader(SafeZoneLoader.SafeZoneLoader): + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.playgroundClass = BRPlayground.BRPlayground + self.musicFile = 'phase_8/audio/bgm/TB_nbrhood.ogg' + self.activityMusicFile = 'phase_8/audio/bgm/TB_SZ_activity.ogg' + self.dnaFile = 'phase_8/dna/the_burrrgh_sz.pdna' + self.safeZoneStorageDNAFile = 'phase_8/dna/storage_BR_sz.pdna' + + def load(self): + SafeZoneLoader.SafeZoneLoader.load(self) + self.windSound = map(base.loadSfx, ['phase_8/audio/sfx/SZ_TB_wind_1.ogg', + 'phase_8/audio/sfx/SZ_TB_wind_2.ogg', + 'phase_8/audio/sfx/SZ_TB_wind_3.ogg']) + self.snow, self.snowRender = SZUtil.createSnow(self.geom) + + def unload(self): + SafeZoneLoader.SafeZoneLoader.unload(self) + del self.windSound + del self.snow + del self.snowRender + + def enter(self, requestStatus): + SafeZoneLoader.SafeZoneLoader.enter(self, requestStatus) + self.snow.start(camera, self.snowRender) + + def exit(self): + SafeZoneLoader.SafeZoneLoader.exit(self) + self.snow.cleanup() + self.snowRender.removeNode() diff --git a/toontown/safezone/ButterflyGlobals.py b/toontown/safezone/ButterflyGlobals.py new file mode 100755 index 00000000..44cbc57c --- /dev/null +++ b/toontown/safezone/ButterflyGlobals.py @@ -0,0 +1,262 @@ +from panda3d.core import * +import random +OFF = 0 +FLYING = 1 +LANDED = 2 +states = {OFF: 'off', + FLYING: 'Flying', + LANDED: 'Landed'} +NUM_BUTTERFLIES = (6, 36, 5) +NUM_BUTTERFLY_AREAS = (4, 1, 4) +BUTTERFLY_SPEED = 2.0 +BUTTERFLY_HEIGHT = (2.2, 3.2, 2.2) +BUTTERFLY_TAKEOFF = (1.4, 1.8, 1.4) +BUTTERFLY_LANDING = (1.4, 1.8, 1.4) +MAX_LANDED_TIME = 20.0 +TTC = 0 +DG = 1 +ESTATE = 2 +ButterflyPoints = (((Point3(84.0, -116.0, 3.5), + Point3(95.0, -144.0, 2.6), + Point3(94.0, -145.0, 2.6), + Point3(95.0, -149.0, 2.6), + Point3(50.0, -155.0, 2.6), + Point3(51.0, -147.0, 2.6), + Point3(51.0, -145.0, 2.6), + Point3(14.0, -99.0, 3.1), + Point3(17.0, -94.0, 3.1), + Point3(50.0, -79.0, 3.1), + Point3(47.0, -86.0, 3.1), + Point3(54.0, -127.0, 2.6), + Point3(84.0, -113.0, 3.8)), + (Point3(-57.0, -70.0, 0.1), + Point3(-55.0, -68.0, 0.1), + Point3(-90.0, -77.0, 0.6), + Point3(-90.0, -72.0, 0.1), + Point3(-133.0, -50.0, 0.6), + Point3(-129.0, -48.0, 0.6), + Point3(-127.0, -25.0, 0.1), + Point3(-125.0, -22.0, 0.1), + Point3(-123.0, -22.0, 0.1), + Point3(-103.0, -10.0, -3.0), + Point3(-104.0, -13.0, -2.5), + Point3(-100.0, -28.0, -2.7), + Point3(-89.0, -41.0, -4.4), + Point3(-58.0, -34.0, -4.1), + Point3(-69.0, -18.0, -1.9), + Point3(-65.0, -19.0, -1.9), + Point3(-65.0, -16.0, -1.9), + Point3(6.0, -49.0, -0.1), + Point3(2.6, -47.0, 0.1), + Point3(-33.6, -43.0, 0.0)), + (Point3(-53.0, 3.0, -1.8), + Point3(-58.0, 2.0, -1.8), + Point3(-58.0, 2.0, -1.8), + Point3(-76.0, 2.0, -1.8), + Point3(-69.0, 11.0, -1.8), + Point3(-100.0, 14.0, -4.1), + Point3(-104.0, 17.0, -2.6), + Point3(-125.0, 34.0, 0.1), + Point3(-124.0, 30.0, 0.1), + Point3(-113.0, 73.0, 0.6), + Point3(-33.0, 78.0, 0.1), + Point3(-65.0, 48.0, -3.0), + Point3(-51.0, 33.0, -3.0), + Point3(-30.0, 71.0, 0.1), + Point3(-26.0, 71.0, 0.1), + Point3(-23.0, 69.0, 0.1), + Point3(-23.0, 64.0, 0.1), + Point3(-5.0, 42.0, 0.1), + Point3(-22.0, 22.0, 0.1), + Point3(-27.0, 22.0, 0.1)), + (Point3(14.0, 93.0, 3.1), + Point3(17.0, 93.0, 3.1), + Point3(20.0, 122.0, 2.6), + Point3(21.0, 127.0, 2.6), + Point3(23.0, 123.0, 2.6), + Point3(32.0, 130.0, 2.6), + Point3(48.0, 148.0, 2.6), + Point3(64.0, 111.0, 2.6), + Point3(32.0, 82.0, 2.6), + Point3(63.0, 90.0, 3.1), + Point3(68.0, 85.0, 3.1), + Point3(65.0, 85.0, 3.1), + Point3(70.0, 95.0, 3.1))), ((Point3(-7.9, 22.9, 0.05), + Point3(-8.0, 17.0, 2.1), + Point3(-7.5, 18.0, 2.1), + Point3(-27.5, 70.7, 0.05), + Point3(-30.0, 70.0, 1.0), + Point3(-31.0, 69.0, 1.0), + Point3(-1.0, 53.0, 2.2), + Point3(-0.5, 53.0, 2.2), + Point3(35.0, 71.5, 1.0), + Point3(33.0, 69.0, 0.05), + Point3(45.0, 61.0, 0.05), + Point3(55.0, 62.0, 0.05), + Point3(80.0, 74.0, 0.05), + Point3(80.0, 73.0, 0.05), + Point3(76.0, 46.0, 0.05), + Point3(76.0, 45.0, 0.05), + Point3(77.0, 41.0, 0.05), + Point3(62.0, 28.0, 0.05), + Point3(48.0, 24.0, 0.05), + Point3(83.0, 122.0, 0.05), + Point3(82.0, 123.0, 0.05), + Point3(81.0, 81.0, 0.05), + Point3(38.0, 77.0, 0.05), + Point3(-26.0, 69.0, 0.05), + Point3(-26.0, 70.0, 0.05), + Point3(-61.0, 71.0, 0.05), + Point3(-61.0, 70.0, 0.05), + Point3(-78.0, 79.0, 0.05), + Point3(-99.0, 106.0, 0.05), + Point3(-99.0, 108.0, 0.05), + Point3(-80.0, 123.0, 0.05), + Point3(-77.0, 125.0, 0.05), + Point3(-32.0, 162.0, 0.05), + Point3(-3.0, 186.5, 2.2), + Point3(-3.2, 186.8, 2.2), + Point3(-1.0, 185.0, 2.2), + Point3(39.0, 165.0, 0.05), + Point3(42.0, 162.0, 0.05), + Point3(62.0, 145.0, 0.05), + Point3(64.0, 145.0, 0.05), + Point3(59.0, 102.0, 0.05), + Point3(32.7, 93.7, 0.05), + Point3(31.2, 90.8, 0.05), + Point3(29.8, 140.1, 0.05), + Point3(16.5, 146.3, 0.05), + Point3(15.3, 146.9, 0.05), + Point3(-24.3, 128.6, 0.05), + Point3(-67.9, 117.9, 0.05), + Point3(-41.6, 88.4, 0.05), + Point3(-13.6, 120.3, 0.05), + Point3(26.0, 117.8, 0.05), + Point3(22.6, 112.3, 0.05), + Point3(-8.2, 107.9, 0.05), + Point3(-18.1, 97.0, 0.05), + Point3(-21.4, 92.9, 0.05), + Point3(-2.1, 74.0, 0.05), + Point3(19.8, 93.5, 0.05), + Point3(21.4, 95.4, 0.05), + Point3(19.2, 97.5, 0.05), + Point3(-10.7, 143.3, 0.05), + Point3(38.2, 120.7, 0.05), + Point3(34.1, 101.5, 0.05), + Point3(32.4, 96.5, 0.05), + Point3(72.9, 121.8, 0.05)),), ((Point3(-40, -137, 0.025), + Point3(2.35, -167.95, 0.025), + Point3(70.8, -125.3, 0.025), + Point3(63.49, -67.4, 0.025), + Point3(17.5, -59.25, 0.623), + Point3(-51.87, -107.0, 0.723), + Point3(-20.325, -48.716, 4.884), + Point3(51.03, -67.244, 0.244), + Point3(20.02, -34.271, 7.105), + Point3(24.731, -20.905, 9.247)), + (Point3(88, -57.4, 0.025), + Point3(92.347, -7.71, 0.169), + Point3(129.39, 0.85, 0.025), + Point3(121.14, 37, 0.025), + Point3(126, 30.3, 0.025), + Point3(100.3, 21.2, 0.05), + Point3(103.42, 1.544, 0.025), + Point3(82.37, -45, 0.025), + Point3(103.8, 4.306, 0.05), + Point3(119.195, -42.042, 0.025)), + (Point3(10, 98.5, -0.028), + Point3(11.65, 92.52, -0.079), + Point3(-16.25, 86.67, 0.216), + Point3(-65.3, 67.8, 0.025), + Point3(-41.6, 67.0, 0.025), + Point3(-34.8, 68.9, 0.025), + Point3(-32.272, 56.65, 1.192), + Point3(-63.956, 39.678, 0.281), + Point3(-79.65, 36.99, 0.025), + Point3(-14.769, 72.399, 0.244)), + (Point3(-79.6, 36.9, 0.025), + Point3(-57.6, 27.24, 2.355), + Point3(-69.642, -28.137, 3.98), + Point3(-111, -58.1, 0.025), + Point3(-152.223, 25.627, 0.025), + Point3(-104.4, 43.5, 0.278), + Point3(-85.25, 10.513, 0.111), + Point3(-43.6, 1.644, 3.838), + Point3(-48.993, -21.968, 3.98), + Point3(-30.088, -5.987, 7.025)))) +allocatedIndexes = {} + +def generateIndexes(doId, playground): + usedI = [] + unusedI = [] + for area in ButterflyPoints[playground]: + usedI.append(range(0, len(area))) + unusedI.append([]) + + allocatedIndexes[doId] = (usedI, unusedI) + + +def clearIndexes(doId): + if doId in allocatedIndexes: + del allocatedIndexes[doId] + + +def getFirstRoute(playground, area, doId): + curPos, curIndex = __getCurrentPos(playground, area, doId) + destPos, destIndex, time = getNextPos(curPos, playground, area, doId) + return (curPos, + curIndex, + destPos, + destIndex, + time) + + +def __getCurrentPos(playground, area, doId): + if doId in allocatedIndexes: + unusedI = allocatedIndexes[doId][0][area] + usedI = allocatedIndexes[doId][1][area] + else: + return (ButterflyPoints[playground][area][0], 0) + if len(unusedI) == 0: + index = random.choice(usedI) + return (ButterflyPoints[playground][area][index], index) + index = random.choice(unusedI) + unusedI.remove(index) + usedI.append(index) + return (ButterflyPoints[playground][area][index], index) + + +def getNextPos(currentPos, playground, area, doId): + if doId in allocatedIndexes: + unusedI = allocatedIndexes[doId][0][area] + usedI = allocatedIndexes[doId][1][area] + else: + return (ButterflyPoints[playground][area][0], 0, 4.0) + nextPos = currentPos + while nextPos == currentPos: + if len(unusedI) == 0: + index = random.choice(usedI) + nextPos = ButterflyPoints[playground][area][index] + else: + index = random.choice(unusedI) + nextPos = ButterflyPoints[playground][area][index] + if nextPos != currentPos: + unusedI.remove(index) + usedI.append(index) + + dist = Vec3(nextPos - currentPos).length() + time = dist / BUTTERFLY_SPEED + BUTTERFLY_TAKEOFF[playground] + BUTTERFLY_LANDING[playground] + return (nextPos, index, time) + + +def recycleIndex(index, playground, area, doId): + if doId in allocatedIndexes: + unusedI = allocatedIndexes[doId][0][area] + usedI = allocatedIndexes[doId][1][area] + else: + return None + if usedI.count(index) > 0: + usedI.remove(index) + if unusedI.count(index) == 0: + unusedI.append(index) + return None diff --git a/toontown/safezone/CheckersBoard.py b/toontown/safezone/CheckersBoard.py new file mode 100755 index 00000000..1677a9f4 --- /dev/null +++ b/toontown/safezone/CheckersBoard.py @@ -0,0 +1,336 @@ + + +class CheckersBoard: + + def __init__(self): + self.squareList = [] + for x in xrange(32): + self.squareList.append(CheckersTile(x)) + + self.squareList[0].setAdjacent([None, + None, + 4, + None]) + self.squareList[1].setAdjacent([None, + 4, + 5, + None]) + self.squareList[2].setAdjacent([None, + 5, + 6, + None]) + self.squareList[3].setAdjacent([None, + 6, + 7, + None]) + self.squareList[4].setAdjacent([0, + 8, + 9, + 1]) + self.squareList[5].setAdjacent([1, + 9, + 10, + 2]) + self.squareList[6].setAdjacent([2, + 10, + 11, + 3]) + self.squareList[7].setAdjacent([3, + 11, + None, + None]) + self.squareList[8].setAdjacent([None, + None, + 12, + 4]) + self.squareList[9].setAdjacent([4, + 12, + 13, + 5]) + self.squareList[10].setAdjacent([5, + 13, + 14, + 6]) + self.squareList[11].setAdjacent([6, + 14, + 15, + 7]) + self.squareList[12].setAdjacent([8, + 16, + 17, + 9]) + self.squareList[13].setAdjacent([9, + 17, + 18, + 10]) + self.squareList[14].setAdjacent([10, + 18, + 19, + 11]) + self.squareList[15].setAdjacent([11, + 19, + None, + None]) + self.squareList[16].setAdjacent([None, + None, + 20, + 12]) + self.squareList[17].setAdjacent([12, + 20, + 21, + 13]) + self.squareList[18].setAdjacent([13, + 21, + 22, + 14]) + self.squareList[19].setAdjacent([14, + 22, + 23, + 15]) + self.squareList[20].setAdjacent([16, + 24, + 25, + 17]) + self.squareList[21].setAdjacent([17, + 25, + 26, + 18]) + self.squareList[22].setAdjacent([18, + 26, + 27, + 19]) + self.squareList[23].setAdjacent([19, + 27, + None, + None]) + self.squareList[24].setAdjacent([None, + None, + 28, + 20]) + self.squareList[25].setAdjacent([20, + 28, + 29, + 21]) + self.squareList[26].setAdjacent([21, + 29, + 30, + 22]) + self.squareList[27].setAdjacent([22, + 30, + 31, + 23]) + self.squareList[28].setAdjacent([24, + None, + None, + 25]) + self.squareList[29].setAdjacent([25, + None, + None, + 26]) + self.squareList[30].setAdjacent([26, + None, + None, + 27]) + self.squareList[31].setAdjacent([27, + None, + None, + None]) + self.squareList[0].setJumps([None, + None, + 9, + None]) + self.squareList[1].setJumps([None, + 8, + 10, + None]) + self.squareList[2].setJumps([None, + 9, + 11, + None]) + self.squareList[3].setJumps([None, + 10, + None, + None]) + self.squareList[4].setJumps([None, + None, + 13, + None]) + self.squareList[5].setJumps([None, + 12, + 14, + None]) + self.squareList[6].setJumps([None, + 13, + 15, + None]) + self.squareList[7].setJumps([None, + 14, + None, + None]) + self.squareList[8].setJumps([None, + None, + 17, + 1]) + self.squareList[9].setJumps([0, + 16, + 18, + 2]) + self.squareList[10].setJumps([1, + 17, + 19, + 3]) + self.squareList[11].setJumps([2, + 18, + None, + None]) + self.squareList[12].setJumps([None, + None, + 21, + 5]) + self.squareList[13].setJumps([4, + 20, + 22, + 6]) + self.squareList[14].setJumps([5, + 21, + 23, + 7]) + self.squareList[15].setJumps([6, + 22, + None, + None]) + self.squareList[16].setJumps([None, + None, + 25, + 9]) + self.squareList[17].setJumps([8, + 24, + 26, + 10]) + self.squareList[18].setJumps([9, + 25, + 27, + 11]) + self.squareList[19].setJumps([10, + 26, + None, + None]) + self.squareList[20].setJumps([None, + None, + 29, + 13]) + self.squareList[21].setJumps([12, + 28, + 30, + 14]) + self.squareList[22].setJumps([13, + 29, + 31, + 15]) + self.squareList[23].setJumps([14, + 30, + None, + None]) + self.squareList[24].setJumps([None, + None, + None, + 17]) + self.squareList[25].setJumps([16, + None, + None, + 18]) + self.squareList[26].setJumps([17, + None, + None, + 19]) + self.squareList[27].setJumps([18, + None, + None, + None]) + self.squareList[28].setJumps([None, + None, + None, + 21]) + self.squareList[29].setJumps([20, + None, + None, + 22]) + self.squareList[30].setJumps([21, + None, + None, + 23]) + self.squareList[31].setJumps([22, + None, + None, + None]) + return + + def delete(self): + for x in self.squareList: + x.delete() + + del self.squareList + + def getSquare(self, arrayLoc): + return self.squareList[arrayLoc] + + def getState(self, squareNum): + return self.squareList[squareNum].getState() + + def setState(self, squareNum, newState): + self.squareList[squareNum].setState(newState) + + def getAdjacent(self, squareNum): + return self.squareList[squareNum].adjacent + + def getStates(self): + retList = [] + for x in xrange(32): + retList.append(self.squareList[x].getState()) + + return retList + + def setStates(self, squares): + y = 0 + for x in xrange(32): + self.squareList[x].setState(squares[x]) + + def getJumps(self, squareNum): + return self.squareList[squareNum].jumps + + +class CheckersTile: + + def __init__(self, tileNum): + self.tileNum = tileNum + self.state = 0 + self.adjacent = [] + self.jumps = [] + + def delete(self): + del self.tileNum + del self.state + del self.adjacent + + def setJumps(self, jumpList): + for x in jumpList: + self.jumps.append(x) + + def getJumps(self): + return self.jumps + + def setAdjacent(self, adjList): + for x in adjList: + self.adjacent.append(x) + + def getAdjacent(self): + return self.adjacent + + def setState(self, newState): + self.state = newState + + def getState(self): + return self.state + + def getNum(self): + return self.tileNum diff --git a/toontown/safezone/ChineseCheckersBoard.py b/toontown/safezone/ChineseCheckersBoard.py new file mode 100755 index 00000000..0ddd4144 --- /dev/null +++ b/toontown/safezone/ChineseCheckersBoard.py @@ -0,0 +1,807 @@ + + +class ChineseCheckersBoard: + + def __init__(self): + self.squareList = [] + for x in xrange(121): + self.squareList.append(CheckersSquare(x)) + + self.squareList[0].setAdjacent([None, + 1, + 2, + None, + None, + None]) + self.squareList[1].setAdjacent([None, + 3, + 4, + 2, + 0, + None]) + self.squareList[2].setAdjacent([1, + 4, + 5, + None, + None, + 0]) + self.squareList[3].setAdjacent([None, + 6, + 7, + 4, + 1, + None]) + self.squareList[4].setAdjacent([3, + 7, + 8, + 5, + 2, + 1]) + self.squareList[5].setAdjacent([4, + 8, + 9, + None, + None, + 2]) + self.squareList[6].setAdjacent([None, + 14, + 15, + 7, + 3, + None]) + self.squareList[7].setAdjacent([6, + 15, + 16, + 8, + 4, + 3]) + self.squareList[8].setAdjacent([7, + 16, + 17, + 9, + 5, + 4]) + self.squareList[9].setAdjacent([8, + 17, + 18, + None, + None, + 5]) + self.squareList[10].setAdjacent([None, + None, + 23, + 11, + None, + None]) + self.squareList[11].setAdjacent([10, + 23, + 24, + 12, + None, + None]) + self.squareList[12].setAdjacent([11, + 24, + 25, + 13, + None, + None]) + self.squareList[13].setAdjacent([12, + 25, + 26, + 14, + None, + None]) + self.squareList[14].setAdjacent([13, + 26, + 27, + 15, + 6, + None]) + self.squareList[15].setAdjacent([14, + 27, + 28, + 16, + 7, + 6]) + self.squareList[16].setAdjacent([15, + 28, + 29, + 17, + 8, + 7]) + self.squareList[17].setAdjacent([16, + 29, + 30, + 18, + 9, + 8]) + self.squareList[18].setAdjacent([17, + 30, + 31, + 19, + None, + 9]) + (self.squareList[19].setAdjacent([18, + 31, + 32, + 20, + None, + None]),) + self.squareList[20].setAdjacent([19, + 32, + 33, + 21, + None, + None]) + self.squareList[21].setAdjacent([20, + 33, + 34, + 22, + None, + None]) + self.squareList[22].setAdjacent([21, + 34, + None, + None, + None, + None]) + self.squareList[23].setAdjacent([None, + None, + 35, + 24, + 11, + 10]) + self.squareList[24].setAdjacent([23, + 35, + 36, + 25, + 12, + 11]) + self.squareList[25].setAdjacent([24, + 36, + 37, + 26, + 13, + 12]) + self.squareList[26].setAdjacent([25, + 37, + 38, + 27, + 14, + 13]) + self.squareList[27].setAdjacent([26, + 38, + 39, + 28, + 15, + 14]) + self.squareList[28].setAdjacent([27, + 39, + 40, + 29, + 16, + 15]) + self.squareList[29].setAdjacent([28, + 40, + 41, + 30, + 17, + 16]) + self.squareList[30].setAdjacent([29, + 41, + 42, + 31, + 18, + 17]) + self.squareList[31].setAdjacent([30, + 42, + 43, + 32, + 19, + 18]) + self.squareList[32].setAdjacent([31, + 43, + 44, + 33, + 20, + 19]) + self.squareList[33].setAdjacent([32, + 44, + 45, + 34, + 21, + 20]) + self.squareList[34].setAdjacent([33, + 45, + None, + None, + 22, + 21]) + self.squareList[35].setAdjacent([None, + None, + 46, + 36, + 24, + 23]) + self.squareList[36].setAdjacent([35, + 46, + 47, + 37, + 25, + 24]) + self.squareList[37].setAdjacent([36, + 47, + 48, + 38, + 26, + 25]) + self.squareList[38].setAdjacent([37, + 48, + 49, + 39, + 27, + 26]) + self.squareList[39].setAdjacent([38, + 49, + 50, + 40, + 28, + 27]) + self.squareList[40].setAdjacent([39, + 50, + 51, + 41, + 29, + 28]) + self.squareList[41].setAdjacent([40, + 51, + 52, + 42, + 30, + 29]) + self.squareList[42].setAdjacent([41, + 52, + 53, + 43, + 31, + 30]) + self.squareList[43].setAdjacent([42, + 53, + 54, + 44, + 32, + 31]) + self.squareList[44].setAdjacent([43, + 54, + 55, + 45, + 33, + 32]) + self.squareList[45].setAdjacent([44, + 55, + None, + None, + 34, + 33]) + self.squareList[46].setAdjacent([None, + None, + 56, + 47, + 36, + 35]) + self.squareList[47].setAdjacent([46, + 56, + 57, + 48, + 37, + 36]) + self.squareList[48].setAdjacent([47, + 57, + 58, + 49, + 38, + 37]) + self.squareList[49].setAdjacent([48, + 58, + 59, + 50, + 39, + 38]) + self.squareList[50].setAdjacent([49, + 59, + 60, + 51, + 40, + 39]) + self.squareList[51].setAdjacent([50, + 60, + 61, + 52, + 41, + 40]) + self.squareList[52].setAdjacent([51, + 61, + 62, + 53, + 42, + 41]) + self.squareList[53].setAdjacent([52, + 62, + 63, + 54, + 43, + 42]) + self.squareList[54].setAdjacent([53, + 63, + 64, + 55, + 44, + 43]) + self.squareList[55].setAdjacent([54, + 64, + None, + None, + 45, + 44]) + self.squareList[56].setAdjacent([None, + 65, + 66, + 57, + 47, + 46]) + self.squareList[57].setAdjacent([56, + 66, + 67, + 58, + 48, + 47]) + self.squareList[58].setAdjacent([57, + 67, + 68, + 59, + 49, + 48]) + self.squareList[59].setAdjacent([58, + 68, + 69, + 60, + 50, + 49]) + self.squareList[60].setAdjacent([59, + 69, + 70, + 61, + 51, + 50]) + self.squareList[61].setAdjacent([60, + 70, + 71, + 62, + 52, + 51]) + self.squareList[62].setAdjacent([61, + 71, + 72, + 63, + 53, + 52]) + self.squareList[63].setAdjacent([62, + 72, + 73, + 64, + 54, + 53]) + self.squareList[64].setAdjacent([63, + 73, + 74, + None, + 55, + 54]) + self.squareList[65].setAdjacent([None, + 75, + 76, + 66, + 56, + None]) + self.squareList[66].setAdjacent([65, + 76, + 77, + 67, + 57, + 56]) + self.squareList[67].setAdjacent([66, + 77, + 78, + 68, + 58, + 57]) + self.squareList[68].setAdjacent([67, + 78, + 79, + 69, + 59, + 58]) + self.squareList[69].setAdjacent([68, + 79, + 80, + 70, + 60, + 61]) + self.squareList[70].setAdjacent([69, + 80, + 81, + 71, + 61, + 60]) + self.squareList[71].setAdjacent([70, + 81, + 82, + 72, + 62, + 61]) + self.squareList[72].setAdjacent([71, + 82, + 83, + 73, + 63, + 62]) + self.squareList[73].setAdjacent([72, + 83, + 84, + 74, + 64, + 63]) + self.squareList[74].setAdjacent([73, + 84, + 85, + None, + None, + 64]) + self.squareList[75].setAdjacent([None, + 86, + 87, + 76, + 65, + None]) + self.squareList[76].setAdjacent([75, + 87, + 88, + 77, + 66, + 65]) + self.squareList[77].setAdjacent([76, + 88, + 89, + 78, + 67, + 66]) + self.squareList[78].setAdjacent([77, + 89, + 90, + 79, + 68, + 67]) + self.squareList[79].setAdjacent([78, + 90, + 91, + 80, + 69, + 68]) + self.squareList[80].setAdjacent([79, + 91, + 92, + 81, + 70, + 69]) + self.squareList[81].setAdjacent([80, + 92, + 93, + 82, + 71, + 70]) + self.squareList[82].setAdjacent([81, + 93, + 94, + 83, + 72, + 71]) + self.squareList[83].setAdjacent([82, + 94, + 95, + 84, + 73, + 72]) + self.squareList[84].setAdjacent([83, + 95, + 96, + 85, + 74, + 73]) + self.squareList[85].setAdjacent([84, + 96, + 97, + None, + None, + 74]) + self.squareList[86].setAdjacent([None, + 98, + 99, + 87, + 75, + None]) + self.squareList[87].setAdjacent([86, + 99, + 100, + 88, + 76, + 75]) + self.squareList[88].setAdjacent([87, + 100, + 101, + 89, + 77, + 76]) + self.squareList[89].setAdjacent([88, + 101, + 102, + 90, + 78, + 77]) + self.squareList[90].setAdjacent([89, + 102, + 103, + 91, + 79, + 78]) + self.squareList[91].setAdjacent([90, + 103, + 104, + 92, + 80, + 79]) + self.squareList[92].setAdjacent([91, + 104, + 105, + 93, + 81, + 80]) + self.squareList[93].setAdjacent([92, + 105, + 106, + 94, + 82, + 81]) + self.squareList[94].setAdjacent([93, + 106, + 107, + 95, + 83, + 82]) + self.squareList[95].setAdjacent([94, + 107, + 108, + 96, + 84, + 83]) + self.squareList[96].setAdjacent([95, + 108, + 109, + 97, + 85, + 84]) + self.squareList[97].setAdjacent([96, + 109, + 110, + None, + None, + 85]) + self.squareList[98].setAdjacent([None, + None, + None, + 99, + 86, + None]) + self.squareList[99].setAdjacent([98, + None, + None, + 100, + 87, + 86]) + self.squareList[100].setAdjacent([99, + None, + None, + 101, + 88, + 87]) + self.squareList[101].setAdjacent([100, + None, + None, + 102, + 89, + 88]) + self.squareList[102].setAdjacent([101, + None, + 111, + 103, + 90, + 89]) + self.squareList[103].setAdjacent([102, + 111, + 112, + 104, + 91, + 90]) + self.squareList[104].setAdjacent([103, + 112, + 113, + 105, + 92, + 91]) + self.squareList[105].setAdjacent([104, + 113, + 114, + 106, + 93, + 92]) + self.squareList[106].setAdjacent([105, + 114, + None, + 107, + 94, + 93]) + self.squareList[107].setAdjacent([106, + None, + None, + 108, + 95, + 94]) + self.squareList[108].setAdjacent([107, + None, + None, + 109, + 96, + 95]) + self.squareList[109].setAdjacent([108, + None, + None, + 110, + 97, + 96]) + self.squareList[110].setAdjacent([109, + None, + None, + None, + None, + 97]) + self.squareList[111].setAdjacent([None, + None, + 115, + 112, + 103, + 102]) + self.squareList[112].setAdjacent([111, + 115, + 116, + 113, + 104, + 103]) + self.squareList[113].setAdjacent([112, + 116, + 117, + 114, + 105, + 104]) + self.squareList[114].setAdjacent([113, + 117, + None, + None, + 106, + 105]) + self.squareList[115].setAdjacent([None, + None, + 118, + 116, + 112, + 111]) + self.squareList[116].setAdjacent([115, + 118, + 119, + 117, + 113, + 112]) + self.squareList[117].setAdjacent([116, + 119, + None, + None, + 114, + 113]) + self.squareList[118].setAdjacent([None, + None, + 120, + 119, + 116, + 115]) + self.squareList[119].setAdjacent([118, + 120, + None, + None, + 117, + 116]) + self.squareList[120].setAdjacent([None, + None, + None, + None, + 119, + 118]) + return + + def delete(self): + for x in self.squareList: + x.delete() + + del self.squareList + + def getSquare(self, arrayLoc): + return self.squareList[arrayLoc] + + def getSquareOffset(self, arrayLoc): + return self.squareList[arrayLoc - 1] + + def getState(self, squareNum): + return self.squareList[squareNum].getState() + + def getStateOffset(self, arrayLoc): + return self.squareList[squareNum - 1].getState() + + def setState(self, squareNum, newState): + self.squareList[squareNum].setState(newState) + + def setStateOffset(self, squareNum, newState): + self.squareList[squareNum - 1].setState(newState) + + def getAdjacent(self, squareNum): + return self.squareList[squareNum].adjacent + + def getAdjacentOffset(self, squareNum): + return self.squareList[squareNum - 1].adjacent + + def getStates(self): + retList = [] + for x in xrange(121): + retList.append(self.squareList[x].getState()) + + return retList + + def setStates(self, squares): + y = 0 + for x in xrange(121): + self.squareList[x].setState(squares[x]) + + +class CheckersSquare: + + def __init__(self, tileNu): + self.tileNum = tileNu + self.state = 0 + self.adjacent = [] + + def delete(self): + del self.tileNum + del self.state + del self.adjacent + + def setAdjacent(self, adjList): + for x in adjList: + self.adjacent.append(x) + + def getAdjacent(self): + return self.adjacent + + def setState(self, newState): + self.state = newState + + def getState(self): + return self.state + + def getNum(self): + return self.tileNum diff --git a/toontown/safezone/DDPlayground.py b/toontown/safezone/DDPlayground.py new file mode 100755 index 00000000..f7ba4019 --- /dev/null +++ b/toontown/safezone/DDPlayground.py @@ -0,0 +1,135 @@ +from panda3d.core import * +import Playground +from direct.task.Task import Task +import random +from direct.fsm import ClassicFSM, State +from direct.actor import Actor +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import Place + +class DDPlayground(Playground.Playground): + notify = DirectNotifyGlobal.directNotify.newCategory('DDPlayground') + + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + self.cameraSubmerged = -1 + self.toonSubmerged = -1 + self.activityFsm = ClassicFSM.ClassicFSM('Activity', [State.State('off', self.enterOff, self.exitOff, ['OnBoat']), State.State('OnBoat', self.enterOnBoat, self.exitOnBoat, ['off'])], 'off', 'off') + self.activityFsm.enterInitialState() + + def load(self): + Playground.Playground.load(self) + + def unload(self): + del self.activityFsm + Playground.Playground.unload(self) + + def enter(self, requestStatus): + self.nextSeagullTime = 0 + taskMgr.add(self.__seagulls, 'dd-seagulls') + self.loader.hood.setWhiteFog() + Playground.Playground.enter(self, requestStatus) + + def exit(self): + Playground.Playground.exit(self) + taskMgr.remove('dd-check-toon-underwater') + taskMgr.remove('dd-check-cam-underwater') + taskMgr.remove('dd-seagulls') + self.loader.hood.setNoFog() + + def enterStart(self): + self.cameraSubmerged = 0 + self.toonSubmerged = 0 + taskMgr.add(self.__checkToonUnderwater, 'dd-check-toon-underwater') + taskMgr.add(self.__checkCameraUnderwater, 'dd-check-cam-underwater') + + def enterDoorOut(self): + taskMgr.remove('dd-check-toon-underwater') + + def exitDoorOut(self): + pass + + def enterDoorIn(self, requestStatus): + Playground.Playground.enterDoorIn(self, requestStatus) + taskMgr.add(self.__checkToonUnderwater, 'dd-check-toon-underwater') + + def __checkCameraUnderwater(self, task): + if camera.getZ(render) < 1.0: + self.__submergeCamera() + else: + self.__emergeCamera() + return Task.cont + + def __checkToonUnderwater(self, task): + if base.localAvatar.getZ() < -2.3314585: + self.__submergeToon() + else: + self.__emergeToon() + return Task.cont + + def __submergeCamera(self): + if self.cameraSubmerged == 1: + return + self.loader.hood.setUnderwaterFog() + base.playSfx(self.loader.underwaterSound, looping=1, volume=0.8) + self.loader.seagullSound.stop() + taskMgr.remove('dd-seagulls') + self.cameraSubmerged = 1 + + def __emergeCamera(self): + if self.cameraSubmerged == 0: + return + self.loader.hood.setWhiteFog() + self.loader.underwaterSound.stop() + self.nextSeagullTime = random.random() * 8.0 + taskMgr.add(self.__seagulls, 'dd-seagulls') + self.cameraSubmerged = 0 + + def __submergeToon(self): + if self.toonSubmerged == 1: + return + base.playSfx(self.loader.submergeSound) + if base.config.GetBool('disable-flying-glitch') == 0: + self.fsm.request('walk') + self.walkStateData.fsm.request('swimming', [self.loader.swimSound]) + pos = base.localAvatar.getPos(render) + base.localAvatar.d_playSplashEffect(pos[0], pos[1], 1.675) + self.toonSubmerged = 1 + + def __emergeToon(self): + if self.toonSubmerged == 0: + return + self.walkStateData.fsm.request('walking') + self.toonSubmerged = 0 + + def __seagulls(self, task): + if task.time < self.nextSeagullTime: + return Task.cont + base.playSfx(self.loader.seagullSound) + self.nextSeagullTime = task.time + random.random() * 4.0 + 8.0 + return Task.cont + + def enterTeleportIn(self, requestStatus): + self.toonSubmerged = -1 + taskMgr.remove('dd-check-toon-underwater') + Playground.Playground.enterTeleportIn(self, requestStatus) + + def teleportInDone(self): + self.toonSubmerged = -1 + taskMgr.add(self.__checkToonUnderwater, 'dd-check-toon-underwater') + Playground.Playground.teleportInDone(self) + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterOnBoat(self): + base.localAvatar.b_setParent(ToontownGlobals.SPDonaldsBoat) + base.playSfx(self.loader.waterSound, looping=1) + + def exitOnBoat(self): + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + self.loader.waterSound.stop() diff --git a/toontown/safezone/DDSafeZoneLoader.py b/toontown/safezone/DDSafeZoneLoader.py new file mode 100755 index 00000000..2f358932 --- /dev/null +++ b/toontown/safezone/DDSafeZoneLoader.py @@ -0,0 +1,68 @@ +from toontown.safezone import SafeZoneLoader, DDPlayground +from toontown.toon import NPCToons +from toontown.toonbase import TTLocalizer +import random + +class DDSafeZoneLoader(SafeZoneLoader.SafeZoneLoader): + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.playgroundClass = DDPlayground.DDPlayground + self.musicFile = 'phase_6/audio/bgm/DD_nbrhood.ogg' + self.activityMusicFile = 'phase_6/audio/bgm/DD_SZ_activity.ogg' + self.dnaFile = 'phase_6/dna/donalds_dock_sz.pdna' + self.safeZoneStorageDNAFile = 'phase_6/dna/storage_DD_sz.pdna' + + def load(self): + SafeZoneLoader.SafeZoneLoader.load(self) + self.seagullSound = base.loadSfx('phase_6/audio/sfx/SZ_DD_Seagull.ogg') + self.underwaterSound = base.loadSfx('phase_4/audio/sfx/AV_ambient_water.ogg') + self.swimSound = base.loadSfx('phase_4/audio/sfx/AV_swim_single_stroke.ogg') + self.submergeSound = base.loadSfx('phase_5.5/audio/sfx/AV_jump_in_water.ogg') + self.boat = self.geom.find('**/donalds_boat') + self.dockSound = base.loadSfx('phase_6/audio/sfx/SZ_DD_dockcreak.ogg') + self.foghornSound = base.loadSfx('phase_5/audio/sfx/SZ_DD_foghorn.ogg') + self.bellSound = base.loadSfx('phase_6/audio/sfx/SZ_DD_shipbell.ogg') + self.waterSound = base.loadSfx('phase_6/audio/sfx/SZ_DD_waterlap.ogg') + + if not self.boat.isEmpty(): + wheel = self.boat.find('**/wheel') + + if not wheel.isEmpty(): + wheel.hide() + + self.boat.stash() + self.donald = NPCToons.createLocalNPC(7011) + + self.donald.setPos(0, -1, 3.95) + self.donald.reparentTo(self.boat) + self.donald.setHat(48, 0, 0) + self.donald.hideShadow() + + random.shuffle(TTLocalizer.DonaldChatter) + self.donaldSpeech = self.donald.createTalkSequence(TTLocalizer.DonaldChatter, 15) + self.donaldSpeech.loop(0) + + water = self.geom.find('**/water') + + water.setColorScale(1, 1, 1, 0.7) + water.setTransparency(1) + + def unload(self): + SafeZoneLoader.SafeZoneLoader.unload(self) + + if hasattr(self, 'donald'): + self.donaldSpeech.pause() + self.donald.delete() + del self.donaldSpeech + del self.donald + + del self.seagullSound + del self.underwaterSound + del self.swimSound + del self.dockSound + del self.foghornSound + del self.bellSound + del self.waterSound + del self.submergeSound + del self.boat diff --git a/toontown/safezone/DGPlayground.py b/toontown/safezone/DGPlayground.py new file mode 100755 index 00000000..7d6e8ded --- /dev/null +++ b/toontown/safezone/DGPlayground.py @@ -0,0 +1,18 @@ +from direct.task import Task +from toontown.safezone import Playground +import random + +class DGPlayground(Playground.Playground): + def enter(self, requestStatus): + Playground.Playground.enter(self, requestStatus) + taskMgr.doMethodLater(1, self.__birds, 'DG-birds') + + def exit(self): + Playground.Playground.exit(self) + taskMgr.remove('DG-birds') + + def __birds(self, task): + base.playSfx(random.choice(self.loader.birdSound)) + time = random.random() * 20.0 + 1 + taskMgr.doMethodLater(time, self.__birds, 'DG-birds') + return Task.done diff --git a/toontown/safezone/DGSafeZoneLoader.py b/toontown/safezone/DGSafeZoneLoader.py new file mode 100755 index 00000000..be30a0a2 --- /dev/null +++ b/toontown/safezone/DGSafeZoneLoader.py @@ -0,0 +1,23 @@ +from toontown.safezone import DGPlayground +from toontown.safezone import SafeZoneLoader + + +class DGSafeZoneLoader(SafeZoneLoader.SafeZoneLoader): + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.playgroundClass = DGPlayground.DGPlayground + self.musicFile = 'phase_8/audio/bgm/DG_nbrhood.ogg' + self.activityMusicFile = 'phase_8/audio/bgm/DG_SZ.ogg' + self.dnaFile = 'phase_8/dna/daisys_garden_sz.pdna' + self.safeZoneStorageDNAFile = 'phase_8/dna/storage_DG_sz.pdna' + + def load(self): + SafeZoneLoader.SafeZoneLoader.load(self) + self.birdSound = map(base.loadSfx, ['phase_8/audio/sfx/SZ_DG_bird_01.ogg', + 'phase_8/audio/sfx/SZ_DG_bird_02.ogg', + 'phase_8/audio/sfx/SZ_DG_bird_03.ogg', + 'phase_8/audio/sfx/SZ_DG_bird_04.ogg']) + + def unload(self): + SafeZoneLoader.SafeZoneLoader.unload(self) + del self.birdSound diff --git a/toontown/safezone/DLSafeZoneLoader.py b/toontown/safezone/DLSafeZoneLoader.py new file mode 100755 index 00000000..248586e1 --- /dev/null +++ b/toontown/safezone/DLSafeZoneLoader.py @@ -0,0 +1,12 @@ +from toontown.safezone import Playground +from toontown.safezone import SafeZoneLoader + + +class DLSafeZoneLoader(SafeZoneLoader.SafeZoneLoader): + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.playgroundClass = Playground.Playground + self.musicFile = 'phase_8/audio/bgm/DL_nbrhood.ogg' + self.activityMusicFile = 'phase_8/audio/bgm/DL_SZ_activity.ogg' + self.dnaFile = 'phase_8/dna/donalds_dreamland_sz.pdna' + self.safeZoneStorageDNAFile = 'phase_8/dna/storage_DL_sz.pdna' diff --git a/toontown/safezone/DistributedBoat.py b/toontown/safezone/DistributedBoat.py new file mode 100755 index 00000000..ba55828c --- /dev/null +++ b/toontown/safezone/DistributedBoat.py @@ -0,0 +1,127 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.distributed import DistributedObject +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from pandac.PandaModules import NodePath +from direct.directutil import Mopath +from toontown.toonbase import ToontownGlobals + +class DistributedBoat(DistributedObject.DistributedObject): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.eastWestMopath = Mopath.Mopath() + self.westEastMopath = Mopath.Mopath() + self.eastWestMopathInterval = None + self.westEastMopathInterval = None + self.fsm = ClassicFSM.ClassicFSM('DistributedBoat', [State.State('off', self.enterOff, self.exitOff, ['DockedEast', + 'SailingWest', + 'DockedWest', + 'SailingEast']), + State.State('DockedEast', self.enterDockedEast, self.exitDockedEast, ['SailingWest', 'SailingEast', 'DockedWest']), + State.State('SailingWest', self.enterSailingWest, self.exitSailingWest, ['DockedWest', 'SailingEast', 'DockedEast']), + State.State('DockedWest', self.enterDockedWest, self.exitDockedWest, ['SailingEast', 'SailingWest', 'DockedEast']), + State.State('SailingEast', self.enterSailingEast, self.exitSailingEast, ['DockedEast', 'DockedWest', 'SailingWest'])], 'off', 'off') + self.fsm.enterInitialState() + return + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.boat = base.cr.playGame.hood.loader.boat + base.cr.parentMgr.registerParent(ToontownGlobals.SPDonaldsBoat, self.boat) + self.setupTracks() + self.accept('enterdonalds_boat_floor', self.__handleOnFloor) + self.accept('exitdonalds_boat_floor', self.__handleOffFloor) + + def setupTracks(self): + boat = self.boat + boat.unstash() + dockSound = self.cr.playGame.hood.loader.dockSound + foghornSound = self.cr.playGame.hood.loader.foghornSound + bellSound = self.cr.playGame.hood.loader.bellSound + self.eastWestMopath.loadFile('phase_6/paths/dd-e-w') + self.eastWestMopathInterval = MopathInterval(self.eastWestMopath, boat) + ewBoatTrack = ParallelEndTogether(Parallel(self.eastWestMopathInterval, SoundInterval(bellSound, node=boat)), SoundInterval(foghornSound, node=boat), name='ew-boat') + self.westEastMopath.loadFile('phase_6/paths/dd-w-e') + self.westEastMopathInterval = MopathInterval(self.westEastMopath, boat) + weBoatTrack = ParallelEndTogether(Parallel(self.westEastMopathInterval, SoundInterval(bellSound, node=boat)), SoundInterval(foghornSound, node=boat), name='we-boat') + PIER_TIME = 5.0 + eastPier = self.cr.playGame.hood.loader.geom.find('**/east_pier') + ePierHpr = VBase3(90, -44.2601, 0) + ePierTargetHpr = VBase3(90, 0.25, 0) + westPier = self.cr.playGame.hood.loader.geom.find('**/west_pier') + wPierHpr = VBase3(-90, -44.2601, 0) + wPierTargetHpr = VBase3(-90, 0.25, 0) + ePierDownTrack = Parallel(LerpHprInterval(eastPier, PIER_TIME, ePierHpr, ePierTargetHpr), SoundInterval(dockSound, node=eastPier), name='e-pier-down') + ePierUpTrack = Parallel(LerpHprInterval(eastPier, PIER_TIME, ePierTargetHpr, ePierHpr), SoundInterval(dockSound, node=eastPier), name='e-pier-up') + wPierDownTrack = Parallel(LerpHprInterval(westPier, PIER_TIME, wPierHpr, wPierTargetHpr), SoundInterval(dockSound, node=westPier), name='w-pier-down') + wPierUpTrack = Parallel(LerpHprInterval(westPier, PIER_TIME, wPierTargetHpr, wPierHpr), SoundInterval(dockSound, node=westPier), name='w-pier-up') + self.ewTrack = ParallelEndTogether(Parallel(ewBoatTrack, ePierDownTrack), wPierUpTrack, name='ew-track') + self.weTrack = ParallelEndTogether(Parallel(weBoatTrack, wPierDownTrack), ePierUpTrack, name='we-track') + + def disable(self): + base.cr.parentMgr.unregisterParent(ToontownGlobals.SPDonaldsBoat) + self.ignore('enterdonalds_boat_floor') + self.ignore('exitdonalds_boat_floor') + self.fsm.request('off') + DistributedObject.DistributedObject.disable(self) + self.ewTrack.finish() + self.weTrack.finish() + + def delete(self): + self.eastWestMopath.reset() + self.westEastMopath.reset() + if self.eastWestMopathInterval.mopath: + self.eastWestMopathInterval.destroy() + if self.westEastMopathInterval.mopath: + self.westEastMopathInterval.destroy() + del self.eastWestMopath + del self.westEastMopath + del self.ewTrack + del self.weTrack + del self.fsm + DistributedObject.DistributedObject.delete(self) + + def setState(self, state, timestamp): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def __handleOnFloor(self, collEntry): + self.cr.playGame.getPlace().activityFsm.request('OnBoat') + + def __handleOffFloor(self, collEntry): + self.cr.playGame.getPlace().activityFsm.request('off') + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterDockedEast(self, ts): + self.weTrack.finish() + return None + + def exitDockedEast(self): + return None + + def enterSailingWest(self, ts): + self.ewTrack.start(ts) + + def exitSailingWest(self): + self.ewTrack.finish() + + def enterDockedWest(self, ts): + self.ewTrack.finish() + return None + + def exitDockedWest(self): + return None + + def enterSailingEast(self, ts): + self.weTrack.start(ts) + + def exitSailingEast(self): + self.weTrack.finish() + return None diff --git a/toontown/safezone/DistributedBoatAI.py b/toontown/safezone/DistributedBoatAI.py new file mode 100755 index 00000000..81717874 --- /dev/null +++ b/toontown/safezone/DistributedBoatAI.py @@ -0,0 +1,86 @@ +from direct.distributed import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from otp.ai.AIBase import * +from toontown.toonbase.ToontownGlobals import * + + +class DistributedBoatAI(DistributedObjectAI.DistributedObjectAI): + + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.fsm = ClassicFSM.ClassicFSM( + 'DistributedBoatAI', + [ + State.State('off', self.enterOff, self.exitOff, + ['DockedEast']), + State.State('DockedEast', self.enterDockedEast, self.exitDockedEast, + ['SailingWest']), + State.State('SailingWest', self.enterSailingWest, self.exitSailingWest, + ['DockedWest']), + State.State('DockedWest', self.enterDockedWest, self.exitDockedWest, + ['SailingEast']), + State.State('SailingEast', self.enterSailingEast, self.exitSailingEast, + ['DockedEast']) + ], 'off', 'off') + self.fsm.enterInitialState() + + def delete(self): + self.fsm.request('off') + DistributedObjectAI.DistributedObjectAI.delete(self) + + def b_setState(self, state): + self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()]) + self.fsm.request(state) + + def getState(self): + return [self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()] + + def start(self): + self.b_setState('DockedEast') + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterDockedEast(self): + taskMgr.doMethodLater(10.0, self.__departEast, 'depart-east') + + def exitDockedEast(self): + taskMgr.remove('depart-east') + + def __departEast(self, task): + self.b_setState('SailingWest') + return Task.done + + def enterSailingWest(self): + taskMgr.doMethodLater(20.0, self.__dockWest, 'dock-west') + + def exitSailingWest(self): + taskMgr.remove('dock-west') + + def __dockWest(self, task): + self.b_setState('DockedWest') + return Task.done + + def enterDockedWest(self): + taskMgr.doMethodLater(10.0, self.__departWest, 'depart-west') + + def exitDockedWest(self): + taskMgr.remove('depart-west') + + def __departWest(self, task): + self.b_setState('SailingEast') + return Task.done + + def enterSailingEast(self): + taskMgr.doMethodLater(20.0, self.__dockEast, 'dock-east') + + def exitSailingEast(self): + taskMgr.remove('dock-east') + + def __dockEast(self, task): + self.b_setState('DockedEast') + return Task.done diff --git a/toontown/safezone/DistributedButterfly.py b/toontown/safezone/DistributedButterfly.py new file mode 100755 index 00000000..7e2c54ea --- /dev/null +++ b/toontown/safezone/DistributedButterfly.py @@ -0,0 +1,223 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from pandac.PandaModules import NodePath +from direct.directutil import Mopath +from toontown.toonbase import ToontownGlobals +from direct.actor import Actor +import ButterflyGlobals +from direct.showbase import RandomNumGen +import random + +class DistributedButterfly(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedButterfly') + id = 0 + wingTypes = ('wings_1', 'wings_2', 'wings_3', 'wings_4', 'wings_5', 'wings_6') + yellowColors = (Vec4(1, 1, 1, 1), Vec4(0.2, 0, 1, 1), Vec4(0.8, 0, 1, 1)) + whiteColors = (Vec4(0.8, 0, 0.8, 1), + Vec4(0, 0.8, 0.8, 1), + Vec4(0.9, 0.4, 0.6, 1), + Vec4(0.9, 0.4, 0.4, 1), + Vec4(0.8, 0.5, 0.9, 1), + Vec4(0.4, 0.1, 0.7, 1)) + paleYellowColors = (Vec4(0.8, 0, 0.8, 1), + Vec4(0.6, 0.6, 0.9, 1), + Vec4(0.7, 0.6, 0.9, 1), + Vec4(0.8, 0.6, 0.9, 1), + Vec4(0.9, 0.6, 0.9, 1), + Vec4(1, 0.6, 0.9, 1)) + shadowScaleBig = Point3(0.07, 0.07, 0.07) + shadowScaleSmall = Point3(0.01, 0.01, 0.01) + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.fsm = ClassicFSM.ClassicFSM('DistributedButterfly', [State.State('off', self.enterOff, self.exitOff, ['Flying', 'Landed']), State.State('Flying', self.enterFlying, self.exitFlying, ['Landed']), State.State('Landed', self.enterLanded, self.exitLanded, ['Flying'])], 'off', 'off') + self.butterfly = None + self.butterflyNode = None + self.curIndex = 0 + self.destIndex = 0 + self.time = 0.0 + self.ival = None + self.fsm.enterInitialState() + return + + def generate(self): + DistributedObject.DistributedObject.generate(self) + if self.butterfly: + return + self.butterfly = Actor.Actor() + self.butterfly.loadModel('phase_4/models/props/SZ_butterfly-mod.bam') + self.butterfly.loadAnims({'flutter': 'phase_4/models/props/SZ_butterfly-flutter.bam', + 'glide': 'phase_4/models/props/SZ_butterfly-glide.bam', + 'land': 'phase_4/models/props/SZ_butterfly-land.bam'}) + index = self.doId % len(self.wingTypes) + chosenType = self.wingTypes[index] + node = self.butterfly.getGeomNode() + for type in self.wingTypes: + wing = node.find('**/' + type) + if type != chosenType: + wing.removeNode() + else: + if index == 0 or index == 1: + color = self.yellowColors[self.doId % len(self.yellowColors)] + elif index == 2 or index == 3: + color = self.whiteColors[self.doId % len(self.whiteColors)] + elif index == 4: + color = self.paleYellowColors[self.doId % len(self.paleYellowColors)] + else: + color = Vec4(1, 1, 1, 1) + wing.setColor(color) + + self.butterfly2 = Actor.Actor(other=self.butterfly) + self.butterfly.enableBlend(blendType=PartBundle.BTLinear) + self.butterfly.loop('flutter') + self.butterfly.loop('land') + self.butterfly.loop('glide') + rng = RandomNumGen.RandomNumGen(self.doId) + playRate = 0.6 + 0.8 * rng.random() + self.butterfly.setPlayRate(playRate, 'flutter') + self.butterfly.setPlayRate(playRate, 'land') + self.butterfly.setPlayRate(playRate, 'glide') + self.butterfly2.setPlayRate(playRate, 'flutter') + self.butterfly2.setPlayRate(playRate, 'land') + self.butterfly2.setPlayRate(playRate, 'glide') + self.glideWeight = rng.random() * 2 + lodNode = LODNode('butterfly-node') + lodNode.addSwitch(100, 40) + lodNode.addSwitch(40, 0) + self.butterflyNode = NodePath(lodNode) + self.butterfly2.setH(180.0) + self.butterfly2.reparentTo(self.butterflyNode) + self.butterfly.setH(180.0) + self.butterfly.reparentTo(self.butterflyNode) + self.__initCollisions() + self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.dropShadow.setColor(0, 0, 0, 0.3) + self.dropShadow.setPos(0, 0.1, -0.05) + self.dropShadow.setScale(self.shadowScaleBig) + self.dropShadow.reparentTo(self.butterfly) + + def disable(self): + self.butterflyNode.reparentTo(hidden) + if self.ival != None: + self.ival.finish() + self.__ignoreAvatars() + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + self.butterfly.cleanup() + self.butterfly = None + self.butterfly2.cleanup() + self.butterfly2 = None + self.butterflyNode.removeNode() + self.__deleteCollisions() + self.ival = None + del self.fsm + DistributedObject.DistributedObject.delete(self) + return + + def uniqueButterflyName(self, name): + DistributedButterfly.id += 1 + return name + '-%d' % DistributedButterfly.id + + def __detectAvatars(self): + self.accept('enter' + self.cSphereNode.getName(), self.__handleCollisionSphereEnter) + + def __ignoreAvatars(self): + self.ignore('enter' + self.cSphereNode.getName()) + + def __initCollisions(self): + self.cSphere = CollisionSphere(0.0, 1.0, 0.0, 3.0) + self.cSphere.setTangible(0) + self.cSphereNode = CollisionNode(self.uniqueButterflyName('cSphereNode')) + self.cSphereNode.addSolid(self.cSphere) + self.cSphereNodePath = self.butterflyNode.attachNewNode(self.cSphereNode) + self.cSphereNodePath.hide() + self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + + def __deleteCollisions(self): + del self.cSphere + del self.cSphereNode + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + + def __handleCollisionSphereEnter(self, collEntry): + self.sendUpdate('avatarEnter', []) + + def setArea(self, playground, area): + self.playground = playground + self.area = area + + def setState(self, stateIndex, curIndex, destIndex, time, timestamp): + self.curIndex = curIndex + self.destIndex = destIndex + self.time = time + self.fsm.request(ButterflyGlobals.states[stateIndex], [globalClockDelta.localElapsedTime(timestamp)]) + + def enterOff(self, ts = 0.0): + if self.butterflyNode != None: + self.butterflyNode.reparentTo(hidden) + return + + def exitOff(self): + if self.butterflyNode != None: + self.butterflyNode.reparentTo(render) + return + + def enterFlying(self, ts): + self.__detectAvatars() + curPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][self.curIndex] + destPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][self.destIndex] + flyHeight = max(curPos[2], destPos[2]) + ButterflyGlobals.BUTTERFLY_HEIGHT[self.playground] + curPosHigh = Point3(curPos[0], curPos[1], flyHeight) + destPosHigh = Point3(destPos[0], destPos[1], flyHeight) + if ts <= self.time: + flyTime = self.time - (ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground] + ButterflyGlobals.BUTTERFLY_LANDING[self.playground]) + self.butterflyNode.setPos(curPos) + self.dropShadow.show() + self.dropShadow.setScale(self.shadowScaleBig) + oldHpr = self.butterflyNode.getHpr() + self.butterflyNode.headsUp(destPos) + newHpr = self.butterflyNode.getHpr() + self.butterflyNode.setHpr(oldHpr) + takeoffShadowT = 0.2 * ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground] + landShadowT = 0.2 * ButterflyGlobals.BUTTERFLY_LANDING[self.playground] + self.butterfly2.loop('flutter') + self.ival = Sequence(Parallel(LerpPosHprInterval(self.butterflyNode, ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground], curPosHigh, newHpr), LerpAnimInterval(self.butterfly, ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground], 'land', 'flutter'), LerpAnimInterval(self.butterfly, ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground], None, 'glide', startWeight=0, endWeight=self.glideWeight), Sequence(LerpScaleInterval(self.dropShadow, takeoffShadowT, self.shadowScaleSmall, startScale=self.shadowScaleBig), HideInterval(self.dropShadow))), LerpPosInterval(self.butterflyNode, flyTime, destPosHigh), Parallel(LerpPosInterval(self.butterflyNode, ButterflyGlobals.BUTTERFLY_LANDING[self.playground], destPos), LerpAnimInterval(self.butterfly, ButterflyGlobals.BUTTERFLY_LANDING[self.playground], 'flutter', 'land'), LerpAnimInterval(self.butterfly, ButterflyGlobals.BUTTERFLY_LANDING[self.playground], None, 'glide', startWeight=self.glideWeight, endWeight=0), Sequence(Wait(ButterflyGlobals.BUTTERFLY_LANDING[self.playground] - landShadowT), ShowInterval(self.dropShadow), LerpScaleInterval(self.dropShadow, landShadowT, self.shadowScaleBig, startScale=self.shadowScaleSmall))), name=self.uniqueName('Butterfly')) + self.ival.start(ts) + else: + self.ival = None + self.butterflyNode.setPos(destPos) + self.butterfly.setControlEffect('land', 1.0) + self.butterfly.setControlEffect('flutter', 0.0) + self.butterfly.setControlEffect('glide', 0.0) + self.butterfly2.loop('land') + return + + def exitFlying(self): + self.__ignoreAvatars() + if self.ival != None: + self.ival.finish() + self.ival = None + return + + def enterLanded(self, ts): + self.__detectAvatars() + curPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][self.curIndex] + self.butterflyNode.setPos(curPos) + self.dropShadow.show() + self.dropShadow.setScale(self.shadowScaleBig) + self.butterfly.setControlEffect('land', 1.0) + self.butterfly.setControlEffect('flutter', 0.0) + self.butterfly.setControlEffect('glide', 0.0) + self.butterfly2.pose('land', random.randrange(self.butterfly2.getNumFrames('land'))) + return None + + def exitLanded(self): + self.__ignoreAvatars() + return None diff --git a/toontown/safezone/DistributedButterflyAI.py b/toontown/safezone/DistributedButterflyAI.py new file mode 100755 index 00000000..7e6a748e --- /dev/null +++ b/toontown/safezone/DistributedButterflyAI.py @@ -0,0 +1,91 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.task import Task +import ButterflyGlobals, random + +class DistributedButterflyAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedButterflyAI") + + def __init__(self, air, playground, area, ownerId): + DistributedObjectAI.__init__(self, air) + self.playground = playground + self.area = area + self.ownerId = ownerId + self.fsm = ClassicFSM.ClassicFSM('DistributedButterfliesAI', [State.State('off', self.enterOff, self.exitOff, ['Flying', 'Landed']), State.State('Flying', self.enterFlying, self.exitFlying, ['Landed']), State.State('Landed', self.enterLanded, self.exitLanded, ['Flying'])], 'off', 'off') + self.fsm.enterInitialState() + self.curPos, self.curIndex, self.destPos, self.destIndex, self.time = ButterflyGlobals.getFirstRoute(self.playground, self.area, self.ownerId) + return None + + def delete(self): + ButterflyGlobals.recycleIndex(self.curIndex, self.playground, self.area, self.ownerId) + ButterflyGlobals.recycleIndex(self.destIndex, self.playground, self.area, self.ownerId) + self.fsm.request('off') + del self.fsm + DistributedObjectAI.delete(self) + + def d_setState(self, stateIndex, curIndex, destIndex, time): + self.sendUpdate('setState', [stateIndex, + curIndex, + destIndex, + time, + globalClockDelta.getRealNetworkTime()]) + + def getArea(self): + return [self.playground, self.area] + + def getState(self): + return [self.stateIndex, + self.curIndex, + self.destIndex, + self.time, + globalClockDelta.getRealNetworkTime()] + + def start(self): + self.fsm.request('Flying') + + def avatarEnter(self): + if self.fsm.getCurrentState().getName() == 'Landed': + self.__ready() + return None + + def enterOff(self): + self.stateIndex = ButterflyGlobals.OFF + return None + + def exitOff(self): + return None + + def enterFlying(self): + self.stateIndex = ButterflyGlobals.FLYING + ButterflyGlobals.recycleIndex(self.curIndex, self.playground, self.area, self.ownerId) + self.d_setState(ButterflyGlobals.FLYING, self.curIndex, self.destIndex, self.time) + taskMgr.doMethodLater(self.time, self.__handleArrival, self.uniqueName('butter-flying')) + return None + + def exitFlying(self): + taskMgr.remove(self.uniqueName('butter-flying')) + return None + + def __handleArrival(self, task): + self.curPos = self.destPos + self.curIndex = self.destIndex + self.fsm.request('Landed') + return Task.done + + def enterLanded(self): + self.stateIndex = ButterflyGlobals.LANDED + self.time = random.random() * ButterflyGlobals.MAX_LANDED_TIME + self.d_setState(ButterflyGlobals.LANDED, self.curIndex, self.destIndex, self.time) + taskMgr.doMethodLater(self.time, self.__ready, self.uniqueName('butter-ready')) + return None + + def exitLanded(self): + taskMgr.remove(self.uniqueName('butter-ready')) + return None + + def __ready(self, task = None): + self.destPos, self.destIndex, self.time = ButterflyGlobals.getNextPos(self.curPos, self.playground, self.area, self.ownerId) + self.fsm.request('Flying') + return Task.done diff --git a/toontown/safezone/DistributedCheckers.py b/toontown/safezone/DistributedCheckers.py new file mode 100755 index 00000000..a4f191b0 --- /dev/null +++ b/toontown/safezone/DistributedCheckers.py @@ -0,0 +1,738 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from direct.distributed import DistributedNode +from direct.distributed.ClockDelta import globalClockDelta +from CheckersBoard import CheckersBoard +from direct.fsm import ClassicFSM, State +from direct.fsm import StateData +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from otp.otpbase import OTPGlobals +from direct.showbase import PythonUtil + +class DistributedCheckers(DistributedNode.DistributedNode): + + def __init__(self, cr): + NodePath.__init__(self, 'DistributedCheckers') + DistributedNode.DistributedNode.__init__(self, cr) + self.cr = cr + self.reparentTo(render) + self.boardNode = loader.loadModel('phase_6/models/golf/regular_checker_game.bam') + self.boardNode.reparentTo(self) + self.board = CheckersBoard() + self.exitButton = None + self.inGame = False + self.waiting = True + self.startButton = None + self.playerNum = None + self.turnText = None + self.isMyTurn = False + self.wantTimer = True + self.leaveButton = None + self.screenText = None + self.turnText = None + self.exitButton = None + self.numRandomMoves = 0 + self.blinker = Sequence() + self.moveList = [] + self.mySquares = [] + self.myKings = [] + self.isRotated = False + self.accept('mouse1', self.mouseClick) + self.traverser = base.cTrav + self.pickerNode = CollisionNode('mouseRay') + self.pickerNP = camera.attachNewNode(self.pickerNode) + self.pickerNode.setFromCollideMask(ToontownGlobals.WallBitmask) + self.pickerRay = CollisionRay() + self.pickerNode.addSolid(self.pickerRay) + self.myHandler = CollisionHandlerQueue() + self.traverser.addCollider(self.pickerNP, self.myHandler) + self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + self.upButton = self.buttonModels.find('**//InventoryButtonUp') + self.downButton = self.buttonModels.find('**/InventoryButtonDown') + self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover') + self.clockNode = ToontownTimer() + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.setScale(0.3) + self.clockNode.hide() + self.playerColors = [Vec4(0, 0, 1, 1), Vec4(0, 1, 0, 1)] + self.tintConstant = Vec4(0.25, 0.25, 0.25, 0.5) + self.ghostConstant = Vec4(0, 0, 0, 0.8) + self.startingPositions = [[0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11], [20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31]] + self.knockSound = base.loadSfx('phase_5/audio/sfx/GUI_knock_1.ogg') + self.clickSound = base.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.ogg') + self.moveSound = base.loadSfx('phase_6/audio/sfx/CC_move.ogg') + self.accept('stoppedAsleep', self.handleSleep) + self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, ['playing', 'gameOver']), State.State('playing', self.enterPlaying, self.exitPlaying, ['gameOver']), State.State('gameOver', self.enterGameOver, self.exitGameOver, ['waitingToBegin'])], 'waitingToBegin', 'waitingToBegin') + x = self.boardNode.find('**/locator*') + self.locatorList = x.getChildren() + tempList = [] + for x in xrange(0, 32): + self.locatorList[x].setTag('GamePeiceLocator', '%d' % x) + tempList.append(self.locatorList[x].attachNewNode(CollisionNode('picker%d' % x))) + tempList[x].node().addSolid(CollisionSphere(0, 0, 0, 0.39)) + + for z in self.locatorList: + y = loader.loadModel('phase_6/models/golf/regular_checker_piecewhite.bam') + y.find('**/checker_k*').hide() + zz = loader.loadModel('phase_6/models/golf/regular_checker_pieceblack.bam') + zz.find('**/checker_k*').hide() + y.reparentTo(z) + y.hide() + zz.reparentTo(z) + zz.hide() + + return + + def setName(self, name): + self.name = name + + def announceGenerate(self): + DistributedNode.DistributedNode.announceGenerate(self) + if self.table.fsm.getCurrentState().getName() != 'observing': + if base.localAvatar.doId in self.table.tableState: + self.seatPos = self.table.tableState.index(base.localAvatar.doId) + + def handleSleep(self, task = None): + if self.fsm.getCurrentState().getName() == 'waitingToBegin': + self.exitButtonPushed() + if task != None: + task.done + return + + def setTableDoId(self, doId): + self.tableDoId = doId + self.table = self.cr.doId2do[doId] + self.table.setTimerFunc(self.startButtonPushed) + self.fsm.enterInitialState() + self.table.setGameDoId(self.doId) + + def disable(self): + DistributedNode.DistributedNode.disable(self) + if self.leaveButton: + self.leaveButton.destroy() + self.leavebutton = None + if self.screenText: + self.screenText.destroy() + self.screenText = None + if self.turnText: + self.turnText.destroy() + self.turnText = None + self.clockNode.stop() + self.clockNode.hide() + self.ignore('mouse1') + self.ignore('stoppedAsleep') + self.fsm = None + return + + def delete(self): + DistributedNode.DistributedNode.delete(self) + self.table.gameDoId = None + self.table.game = None + if self.exitButton: + self.exitButton.destroy() + if self.startButton: + self.startButton.destroy() + self.clockNode.stop() + self.clockNode.hide() + self.table.startButtonPushed = None + self.ignore('mouse1') + self.ignore('stoppedAsleep') + self.fsm = None + self.table = None + return + + def getTimer(self): + self.sendUpdate('requestTimer', []) + + def setTimer(self, timerEnd): + if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState().getName() == 'observing': + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(timerEnd) + timeLeft = int(time - globalClock.getRealTime()) + if timeLeft > 0 and timerEnd != 0: + if timeLeft > 60: + timeLeft = 60 + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.countdown(timeLeft, self.startButtonPushed) + self.clockNode.show() + else: + self.clockNode.stop() + self.clockNode.hide() + return + + def setTurnTimer(self, turnEnd): + if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'playing': + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(turnEnd) + timeLeft = int(time - globalClock.getRealTime()) + if timeLeft > 0: + self.clockNode.setPos(-.74, 0, -0.2) + if self.isMyTurn: + self.clockNode.countdown(timeLeft, self.doNothing) + else: + self.clockNode.countdown(timeLeft, self.doNothing) + self.clockNode.show() + return + + def gameStart(self, playerNum): + if playerNum != 255: + self.playerNum = playerNum + if self.playerNum == 1: + self.playerColorString = 'white' + else: + self.playerColorString = 'black' + self.playerColor = self.playerColors[playerNum - 1] + self.moveCameraForGame() + self.fsm.request('playing') + + def sendTurn(self, playersTurn): + if self.fsm.getCurrentState().getName() == 'playing': + if playersTurn == self.playerNum: + self.isMyTurn = True + self.enableTurnScreenText(playersTurn) + + def illegalMove(self): + self.exitButtonPushed() + + def moveCameraForGame(self): + if self.table.cameraBoardTrack.isPlaying(): + self.table.cameraBoardTrack.finish() + rotation = 0 + if self.seatPos > 2: + if self.playerNum == 1: + rotation = 180 + elif self.playerNum == 2: + rotation = 0 + for x in self.locatorList: + x.setH(180) + + self.isRotated = True + elif self.playerNum == 1: + rotation = 0 + elif self.playerNum == 2: + rotation = 180 + for x in self.locatorList: + x.setH(180) + + self.isRotated = True + int = LerpHprInterval(self.boardNode, 4.2, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr()) + int.start() + + def enterWaitingToBegin(self): + if self.table.fsm.getCurrentState().getName() != 'observing': + self.enableExitButton() + self.enableStartButton() + + def exitWaitingToBegin(self): + if self.exitButton: + self.exitButton.destroy() + self.exitButton = None + if self.startButton: + self.startButton.destroy() + self.exitButton = None + self.clockNode.stop() + self.clockNode.hide() + return + + def enterPlaying(self): + self.inGame = True + self.enableScreenText() + if self.table.fsm.getCurrentState().getName() != 'observing': + self.enableLeaveButton() + + def exitPlaying(self): + self.inGame = False + if self.leaveButton: + self.leaveButton.destroy() + self.leavebutton = None + self.playerNum = None + if self.screenText: + self.screenText.destroy() + self.screenText = None + if self.turnText: + self.turnText.destroy() + self.turnText = None + self.clockNode.stop() + self.clockNode.hide() + return + + def enterGameOver(self): + pass + + def exitGameOver(self): + pass + + def exitWaitCountdown(self): + self.__disableCollisions() + self.ignore('trolleyExitButton') + self.clockNode.reset() + + def enableExitButton(self): + self.exitButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersGetUpButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.4), scale=0.15, command=lambda self = self: self.exitButtonPushed()) + return + + def enableScreenText(self): + defaultPos = (-.8, -0.4) + if self.playerNum == 1: + message = TTLocalizer.CheckersColorWhite + color = Vec4(1, 1, 1, 1) + elif self.playerNum == 2: + message = TTLocalizer.CheckersColorBlack + color = Vec4(0, 0, 0, 1) + else: + message = TTLocalizer.CheckersObserver + color = Vec4(0, 0, 0, 1) + defaultPos = (-.8, -0.4) + self.screenText = OnscreenText(text=message, pos=defaultPos, scale=0.1, fg=color, align=TextNode.ACenter, mayChange=1) + + def enableStartButton(self): + self.startButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersStartButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.6, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.1), scale=0.15, command=lambda self = self: self.startButtonPushed()) + return + + def enableLeaveButton(self): + self.leaveButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersQuitButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.13), text_scale=0.5, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.4), scale=0.15, command=lambda self = self: self.exitButtonPushed()) + return + + def enableTurnScreenText(self, player): + playerOrder = [1, + 4, + 2, + 5, + 3, + 6] + message1 = TTLocalizer.CheckersIts + if self.turnText != None: + self.turnText.destroy() + if player == self.playerNum: + message2 = TTLocalizer.ChineseCheckersYourTurn + color = (0, 0, 0, 1) + elif player == 1: + message2 = TTLocalizer.CheckersWhiteTurn + color = (1, 1, 1, 1) + elif player == 2: + message2 = TTLocalizer.CheckersBlackTurn + color = (0, 0, 0, 1) + self.turnText = OnscreenText(text=message1 + message2, pos=(-0.8, -0.5), scale=0.092, fg=color, align=TextNode.ACenter, mayChange=1) + return + + def startButtonPushed(self): + self.sendUpdate('requestBegin') + self.startButton.hide() + self.clockNode.stop() + self.clockNode.hide() + + def exitButtonPushed(self): + self.fsm.request('gameOver') + self.table.fsm.request('off') + self.clockNode.stop() + self.clockNode.hide() + self.table.sendUpdate('requestExit') + + def mouseClick(self): + messenger.send('wakeup') + if self.isMyTurn == True and self.inGame == True: + mpos = base.mouseWatcherNode.getMouse() + self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) + self.traverser.traverse(render) + if self.myHandler.getNumEntries() > 0: + self.myHandler.sortEntries() + pickedObj = self.myHandler.getEntry(0).getIntoNodePath() + pickedObj = pickedObj.getNetTag('GamePeiceLocator') + if pickedObj: + self.handleClicked(int(pickedObj)) + + def handleClicked(self, index): + self.sound = Sequence(SoundInterval(self.clickSound)) + if self.moveList == []: + if index not in self.mySquares and index not in self.myKings: + return + self.moveList.append(index) + type = self.board.squareList[index].getState() + if type == 3 or type == 4: + self.moverType = 'king' + else: + self.moverType = 'normal' + self.blinker = Sequence() + col = self.locatorList[index].getColor() + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.tintConstant, col)) + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, col, self.tintConstant)) + self.blinker.loop() + self.sound.start() + elif index in self.mySquares or index in self.myKings: + for x in self.moveList: + self.locatorList[x].setColor(1, 1, 1, 1) + self.locatorList[x].hide() + + self.blinker.finish() + self.blinker = Sequence() + col = self.locatorList[index].getColor() + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.tintConstant, col)) + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, col, self.tintConstant)) + self.blinker.loop() + self.sound.start() + self.locatorList[self.moveList[0]].show() + self.moveList = [] + self.moveList.append(index) + type = self.board.squareList[index].getState() + if type == 3 or type == 4: + self.moverType = 'king' + else: + self.moverType = 'normal' + else: + self.currentMove = index + lastItem = self.board.squareList[self.moveList[len(self.moveList) - 1]] + thisItem = self.board.squareList[index] + if self.mustJump == True: + if lastItem.getNum() == index: + self.blinker.finish() + self.d_requestMove(self.moveList) + self.isMyTurn = False + self.moveList = [] + return + if self.checkLegalJump(lastItem, thisItem, self.moverType) == True: + col = self.locatorList[index].getColor() + self.locatorList[index].show() + self.sound.start() + if self.existsLegalJumpsFrom(index, self.moverType) == False: + self.moveList.append(index) + self.blinker.finish() + self.d_requestMove(self.moveList) + self.moveList = [] + self.isMyTurn = False + else: + self.moveList.append(index) + if self.playerColorString == 'white': + x = self.locatorList[index].getChildren()[1] + x.show() + else: + x = self.locatorList[index].getChildren()[2] + x.show() + if self.moverType == 'king': + x.find('**/checker_k*').show() + self.locatorList[index].setColor(Vec4(0.5, 0.5, 0.5, 0.5)) + elif self.checkLegalMove(lastItem, thisItem, self.moverType) == True: + self.moveList.append(index) + col = self.locatorList[index].getColor() + self.locatorList[index].show() + self.sound.start() + self.blinker.finish() + self.d_requestMove(self.moveList) + self.moveList = [] + self.isMyTurn = False + + def existsLegalJumpsFrom(self, index, peice): + if peice == 'king': + for x in xrange(4): + if self.board.squareList[index].getAdjacent()[x] != None and \ + self.board.squareList[index].getJumps()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + jump = self.board.squareList[self.board.squareList[index].getJumps()[x]] + if adj.getState() == 0: + pass + elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2: + pass + elif jump.getState() == 0: + if index not in self.moveList and jump.getNum() not in self.moveList: + return True + return False + elif peice == 'normal': + if self.playerNum == 1: + moveForward = [1, 2] + elif self.playerNum == 2: + moveForward = [0, 3] + for x in moveForward: + if self.board.squareList[index].getAdjacent()[x] != None and \ + self.board.squareList[index].getJumps()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + jump = self.board.squareList[self.board.squareList[index].getJumps()[x]] + if adj.getState() == 0: + pass + elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2: + pass + elif jump.getState() == 0: + if index not in self.moveList: + return True + return False + + def existsLegalMovesFrom(self, index, peice): + if peice == 'king': + for x in self.board.squareList[index].getAdjacent(): + if x != None: + if self.board.squareList[x].getState() == 0: + return True + + return False + elif peice == 'normal': + if self.playerNum == 1: + moveForward = [1, 2] + elif self.playerNum == 2: + moveForward = [0, 3] + for x in moveForward: + if self.board.squareList[index].getAdjacent()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + if adj.getState() == 0: + return True + + return False + return + + def checkLegalMove(self, firstSquare, secondSquare, peice): + if firstSquare.getNum() not in self.mySquares and firstSquare.getNum() not in self.myKings: + return False + if self.playerNum == 1: + moveForward = [1, 2] + else: + moveForward = [0, 3] + if peice == 'king': + for x in xrange(4): + if firstSquare.getAdjacent()[x] != None: + if self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0 and secondSquare.getNum() in firstSquare.getAdjacent(): + return True + + return False + elif peice == 'normal': + for x in moveForward: + if firstSquare.getAdjacent()[x] != None and secondSquare.getNum() in firstSquare.getAdjacent(): + if self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0 and firstSquare.getAdjacent().index(secondSquare.getNum()) == x: + return True + + return False + return + + def checkLegalJump(self, firstSquare, secondSquare, peice): + if firstSquare.getNum() not in self.mySquares and firstSquare.getNum() not in self.myKings and len(self.moveList) == 1: + return False + if self.playerNum == 1: + moveForward = [1, 2] + opposingPeices = [2, 4] + else: + moveForward = [0, 3] + opposingPeices = [1, 3] + if peice == 'king': + if secondSquare.getNum() in firstSquare.getJumps(): + index = firstSquare.getJumps().index(secondSquare.getNum()) + if self.board.squareList[firstSquare.getAdjacent()[index]].getState() in opposingPeices: + return True + else: + return False + elif peice == 'normal': + if secondSquare.getNum() in firstSquare.getJumps(): + index = firstSquare.getJumps().index(secondSquare.getNum()) + if index in moveForward: + if self.board.squareList[firstSquare.getAdjacent()[index]].getState() in opposingPeices: + return True + else: + return False + else: + return False + else: + return False + + def d_requestMove(self, moveList): + self.sendUpdate('requestMove', [moveList]) + + def setGameState(self, tableState, moveList): + if moveList != []: + if self.board.squareList[moveList[0]].getState() == 1 or self.board.squareList[moveList[0]].getState() == 3: + playerColor = 'white' + else: + playerColor = 'black' + if self.board.squareList[moveList[0]].getState() <= 2: + self.animatePeice(tableState, moveList, 'normal', playerColor) + else: + self.animatePeice(tableState, moveList, 'king', playerColor) + else: + self.updateGameState(tableState) + + def updateGameState(self, squares): + self.board.setStates(squares) + self.mySquares = [] + self.myKings = [] + messenger.send('wakeup') + isObserve = False + if self.playerNum == None: + self.playerNum = 1 + self.playerColorString = 'white' + isObserve = True + for xx in xrange(32): + for blah in self.locatorList[xx].getChildren(): + blah.hide() + if self.locatorList[xx].getChildren().index(blah) != 0: + blah1 = blah.find('**/checker_k*') + + owner = self.board.squareList[xx].getState() + if owner == self.playerNum: + if self.playerColorString == 'white': + x = self.locatorList[xx].getChildren()[1] + x.show() + x.find('**/checker_k*').hide() + else: + x = self.locatorList[xx].getChildren()[2] + x.show() + x.find('**/checker_k*').hide() + self.mySquares.append(xx) + elif owner == 0: + self.hideChildren(self.locatorList[xx].getChildren()) + elif owner == self.playerNum + 2: + if self.playerColorString == 'white': + x = self.locatorList[xx].getChildren()[1] + x.show() + x.find('**/checker_k*').show() + else: + x = self.locatorList[xx].getChildren()[2] + x.show() + x.find('**/checker_k*').show() + self.myKings.append(xx) + elif owner <= 2: + if self.playerColorString == 'white': + x = self.locatorList[xx].getChildren()[2] + x.show() + x.find('**/checker_k*').hide() + else: + x = self.locatorList[xx].getChildren()[1] + x.show() + x.find('**/checker_k*').hide() + elif self.playerColorString == 'white': + x = self.locatorList[xx].getChildren()[2] + x.show() + x.find('**/checker_k*').show() + else: + x = self.locatorList[xx].getChildren()[1] + x.show() + x.find('**/checker_k*').show() + + if isObserve == True: + self.playerNum = None + self.playerColorString = None + return + self.mustJump = False + self.hasNormalMoves = False + for x in self.myKings: + if self.existsLegalJumpsFrom(x, 'king') == True: + self.mustJump = True + break + else: + self.mustJump = False + + if self.mustJump == False: + for x in self.mySquares: + if self.existsLegalJumpsFrom(x, 'normal') == True: + self.mustJump = True + break + else: + self.mustJump = False + + if self.mustJump != True: + for x in self.mySquares: + if self.existsLegalMovesFrom(x, 'normal') == True: + self.hasNormalMoves = True + break + else: + self.hasNormalMoves = False + if self.hasNormalMoves == False: + for x in self.myKings: + if self.existsLegalMovesFrom(x, 'king') == True: + self.hasNormalMoves = True + break + else: + self.hasNormalMoves = False + + if self.mustJump == False and self.hasNormalMoves == False: + pass + return + + def hideChildren(self, nodeList): + for x in xrange(1, 2): + nodeList[x].hide() + + def animatePeice(self, tableState, moveList, type, playerColor): + messenger.send('wakeup') + if playerColor == 'white': + gamePeiceForAnimation = loader.loadModel('phase_6/models/golf/regular_checker_piecewhite.bam') + else: + gamePeiceForAnimation = loader.loadModel('phase_6/models/golf/regular_checker_pieceblack.bam') + if type == 'king': + gamePeiceForAnimation.find('**/checker_k*').show() + else: + gamePeiceForAnimation.find('**/checker_k*').hide() + gamePeiceForAnimation.reparentTo(self.boardNode) + gamePeiceForAnimation.setPos(self.locatorList[moveList[0]].getPos()) + if self.isRotated == True: + gamePeiceForAnimation.setH(180) + for x in self.locatorList[moveList[0]].getChildren(): + x.hide() + + checkersPeiceTrack = Sequence() + length = len(moveList) + for x in xrange(length - 1): + checkersPeiceTrack.append(Parallel(SoundInterval(self.moveSound), ProjectileInterval(gamePeiceForAnimation, endPos=self.locatorList[moveList[x + 1]].getPos(), duration=0.5))) + + checkersPeiceTrack.append(Func(gamePeiceForAnimation.removeNode)) + checkersPeiceTrack.append(Func(self.updateGameState, tableState)) + checkersPeiceTrack.append(Func(self.unAlpha, moveList)) + checkersPeiceTrack.start() + + def announceWin(self, avId): + self.fsm.request('gameOver') + + def unAlpha(self, moveList): + for x in moveList: + self.locatorList[x].setColorOff() + + def doRandomMove(self): + import random + move = [] + foundLegal = False + self.blinker.pause() + self.numRandomMoves += 1 + while not foundLegal: + x = random.randint(0, 9) + for y in self.board.getAdjacent(self.mySquares[x]): + if y != None and self.board.getState(y) == 0: + move.append(self.mySquares[x]) + move.append(y) + foundLegal = True + break + + if move == []: + pass + playSound = Sequence(SoundInterval(self.knockSound)) + playSound.start() + self.d_requestMove(move) + self.moveList = [] + self.isMyTurn = False + if self.numRandomMoves >= 5: + self.exitButtonPushed() + return + + def doNothing(self): + pass diff --git a/toontown/safezone/DistributedCheckersAI.py b/toontown/safezone/DistributedCheckersAI.py new file mode 100755 index 00000000..f0f2c86e --- /dev/null +++ b/toontown/safezone/DistributedCheckersAI.py @@ -0,0 +1,672 @@ +# File: D (Python 2.4) + +from direct.distributed.DistributedNodeAI import DistributedNodeAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from direct.distributed.ClockDelta import * +from toontown.safezone import CheckersBoard + +class DistributedCheckersAI(DistributedNodeAI): + + def __init__(self, air, parent, name, x, y, z, h, p, r): + DistributedNodeAI.__init__(self, air) + self.name = name + self.air = air + self.setPos(x, y, z) + self.setHpr(h, p, r) + self.myPos = (x, y, z) + self.myHpr = (h, p, r) + self.board = CheckersBoard.CheckersBoard() + self.parent = self.air.doId2do[parent] + self.parentDo = parent + self.wantStart = [] + self.playersPlaying = [] + self.playersSitting = 0 + self.playersTurn = 1 + self.movesMade = 0 + self.playerNum = 1 + self.hasWon = False + self.playersGamePos = [ + None, + None] + self.wantTimer = True + self.timerEnd = 0 + self.turnEnd = 0 + self.playersObserving = [] + self.winLaffPoints = 20 + self.movesRequiredToWin = 10 + self.zoneId = self.air.allocateZone() + self.generateOtpObject(air.districtId, self.zoneId, optionalFields = [ + 'setX', + 'setY', + 'setZ', + 'setH', + 'setP', + 'setR']) + self.parent.setCheckersZoneId(self.zoneId) + self.startingPositions = [ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11], + [ + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31]] + self.kingPositions = [ + [ + 31, + 30, + 29, + 28], + [ + 0, + 1, + 2, + 3]] + self.timerStart = None + self.fsm = ClassicFSM.ClassicFSM('Checkers', [ + State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, [ + 'playing']), + State.State('playing', self.enterPlaying, self.exitPlaying, [ + 'gameOver']), + State.State('gameOver', self.enterGameOver, self.exitGameOver, [ + 'waitingToBegin'])], 'waitingToBegin', 'waitingToBegin') + self.fsm.enterInitialState() + + + def announceGenerate(self): + self.parent.setGameDoId(self.doId) + + + def getTableDoId(self): + return self.parentDo + + + def delete(self): + self.fsm.requestFinalState() + self.board.delete() + del self.fsm + DistributedNodeAI.delete(self) + + + def informGameOfPlayer(self): + self.playersSitting += 1 + if self.playersSitting < 2: + self.timerEnd = 0 + elif self.playersSitting == 2: + self.timerEnd = globalClock.getRealTime() + 20 + self.parent.isAccepting = False + self.parent.sendUpdate('setIsPlaying', [ + 1]) + elif self.playersSitting > 2: + pass + + self.sendUpdate('setTimer', [ + globalClockDelta.localToNetworkTime(self.timerEnd)]) + + + def informGameOfPlayerLeave(self): + self.playersSitting -= 1 + if self.playersSitting < 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin': + self.timerEnd = 0 + self.parent.isAccepting = True + self.parent.sendUpdate('setIsPlaying', [ + 0]) + + if self.playersSitting > 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin': + pass + 1 + self.timerEnd = 0 + if self.timerEnd != 0: + self.sendUpdate('setTimer', [ + globalClockDelta.localToNetworkTime(self.timerEnd)]) + else: + self.sendUpdate('setTimer', [ + 0]) + + + def setGameCountdownTime(self): + self.timerEnd = globalClock.getRealTime() + 10 + + + def setTurnCountdownTime(self): + self.turnEnd = globalClock.getRealTime() + 40 + + + def getTimer(self): + if self.timerEnd != 0: + return 0 + else: + return 0 + + + def getTurnTimer(self): + return globalClockDelta.localToNetworkTime(self.turnEnd) + + + def requestTimer(self): + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'setTimer', [ + globalClockDelta.localToNetworkTime(self.timerEnd)]) + + + def handlePlayerExit(self, avId): + if avId in self.wantStart: + self.wantStart.remove(avId) + + if self.fsm.getCurrentState().getName() == 'playing': + gamePos = self.playersGamePos.index(avId) + self.playersGamePos[gamePos] = None + self.fsm.request('gameOver') + + + + def handleEmptyGame(self): + self.movesMade = 0 + self.playersTurn = 1 + self.playerNum = 1 + self.fsm.request('waitingToBegin') + self.parent.isAccepting = True + + + def requestWin(self): + avId = self.air.getAvatarIdFromSender() + + + def distributeLaffPoints(self): + for x in self.parent.seats: + if x != None: + av = self.air.doId2do.get(x) + av.toonUp(self.winLaffPoints) + continue + + + + def enterWaitingToBegin(self): + self.setGameCountdownTime() + self.parent.isAccepting = True + + + def exitWaitingToBegin(self): + self.turnEnd = 0 + + + def enterPlaying(self): + self.parent.isAccepting = False + for x in self.playersGamePos: + if x != None: + self.playersTurn = self.playersGamePos.index(x) + self.d_sendTurn(self.playersTurn + 1) + break + continue + + self.setTurnCountdownTime() + self.sendUpdate('setTurnTimer', [ + globalClockDelta.localToNetworkTime(self.turnEnd)]) + + + def exitPlaying(self): + pass + + + def enterGameOver(self): + self.timerEnd = 0 + isAccepting = True + self.parent.handleGameOver() + self.playersObserving = [] + self.playersTurn = 1 + self.playerNum = 1 + self.clearBoard() + self.sendGameState([]) + self.movesMade = 0 + self.playersGamePos = [ + None, + None, + None, + None, + None, + None] + self.parent.isAccepting = True + self.fsm.request('waitingToBegin') + + + def exitGameOver(self): + pass + + + def requestBegin(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.wantStart: + self.wantStart.append(avId) + + numPlayers = 0 + for x in self.parent.seats: + if x != None: + numPlayers = numPlayers + 1 + continue + + if len(self.wantStart) == numPlayers and numPlayers >= 2: + self.d_gameStart(avId) + self.parent.sendIsPlaying() + + + + def d_gameStart(self, avId): + for x in self.playersObserving: + self.sendUpdateToAvatarId(x, 'gameStart', [ + 255]) + + zz = 0 + numPlayers = 0 + for x in self.parent.seats: + if x != None: + numPlayers += 1 + self.playersPlaying.append(x) + continue + + if numPlayers == 2: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [ + 1]) + self.playersGamePos[0] = player1 + for x in self.startingPositions[0]: + self.board.setState(x, 1) + + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [ + 2]) + self.playersGamePos[1] = player2 + for x in self.startingPositions[1]: + self.board.setState(x, 2) + + + self.sendGameState([]) + self.wantStart = [] + self.fsm.request('playing') + self.parent.getTableState() + + + def d_sendTurn(self, playersTurn): + self.sendUpdate('sendTurn', [ + playersTurn]) + + + def advancePlayerTurn(self): + if self.playersTurn == 0: + self.playersTurn = 1 + self.playerNum = 2 + else: + self.playerNum = 1 + self.playersTurn = 0 + + + def requestMove(self, moveList): + if self.checkLegalMoves(moveList) == True: + self.makeMove(moveList) + self.advancePlayerTurn() + self.d_sendTurn(self.playersTurn + 1) + self.setTurnCountdownTime() + self.sendUpdate('setTurnTimer', [ + globalClockDelta.localToNetworkTime(self.turnEnd)]) + else: + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'illegalMove', []) + self.air.writeServerEvent('suspicious', avId, 'has requested an illegal move in Regular checkers - not possible') + + + def checkLegalMoves(self, moveList): + if self.board.squareList[moveList[0]].getState() >= 3: + moveType = 'king' + else: + moveType = 'normal' + if len(moveList) == 2: + firstSquare = self.board.squareList[moveList[0]] + secondSquare = self.board.squareList[moveList[1]] + if self.checkLegalMove(firstSquare, secondSquare, moveType) == True: + return True + else: + for x in xrange(len(moveList) - 1): + y = self.checkLegalJump(self.board.getSquare(moveList[x]), self.board.getSquare(moveList[x + 1]), moveType) + if y == False: + return False + else: + return True + return False + + elif len(moveList) > 2: + for x in xrange(len(moveList) - 1): + y = self.checkLegalJump(self.board.getSquare(moveList[x]), self.board.getSquare(moveList[x + 1]), moveType) + if y == False: + return False + continue + + return True + + + + def makeMove(self, moveList): + for x in xrange(len(moveList) - 1): + firstSquare = self.board.squareList[moveList[x]] + secondSquare = self.board.squareList[moveList[x + 1]] + if firstSquare.getNum() in secondSquare.getAdjacent(): + break + + index = firstSquare.jumps.index(secondSquare.getNum()) + self.board.squareList[firstSquare.getAdjacent()[index]].setState(0) + + haveMoved = False + squareState = self.board.squareList[moveList[0]].getState() + if squareState <= 2: + piecetype = 'normal' + if squareState == 1: + playerNum = 1 + else: + playerNum = 2 + else: + piecetype = 'king' + if squareState == 3: + playerNum = 1 + else: + playerNum = 2 + if piecetype == 'normal': + lastElement = moveList[len(moveList) - 1] + if playerNum == 1: + if lastElement in self.kingPositions[0]: + self.board.squareList[moveList[0]].setState(0) + self.board.squareList[lastElement].setState(3) + haveMoved = True + self.sendGameState(moveList) + + elif lastElement in self.kingPositions[1]: + self.board.squareList[moveList[0]].setState(0) + self.board.squareList[lastElement].setState(4) + haveMoved = True + self.sendGameState(moveList) + + + if haveMoved == False: + spot1 = self.board.squareList[moveList[0]].getState() + self.board.squareList[moveList[0]].setState(0) + self.board.squareList[moveList[len(moveList) - 1]].setState(spot1) + self.sendGameState(moveList) + + temp = self.playerNum + self.playerNum = 1 + if self.hasWon == True: + return None + + if self.hasPeicesAndMoves(1, 3) == False: + self.parent.announceWinner('Checkers', self.playersPlaying[1]) + self.fsm.request('gameOver') + self.hasWon = True + return None + + self.playerNum = temp + temp = self.playerNum + self.playerNum = 2 + if self.hasPeicesAndMoves(2, 4) == False: + self.parent.announceWinner('Checkers', self.playersPlaying[0]) + self.fsm.request('gameOver') + self.hasWon = True + return None + + self.playerNum = temp + + + def hasPeicesAndMoves(self, normalNum, kingNum): + for x in self.board.squareList: + if x.getState() == normalNum: + if self.existsLegalMovesFrom(x.getNum(), 'normal') == True: + return True + + if self.existsLegalJumpsFrom(x.getNum(), 'normal') == True: + return True + + self.existsLegalJumpsFrom(x.getNum(), 'normal') == True + if x.getState() == kingNum: + if self.existsLegalMovesFrom(x.getNum(), 'king') == True: + return True + + if self.existsLegalJumpsFrom(x.getNum(), 'king') == True: + return True + + self.existsLegalJumpsFrom(x.getNum(), 'king') == True + + return False + + + def getState(self): + return self.fsm.getCurrentState().getName() + + + def getName(self): + return self.name + + + def getGameState(self): + return [ + self.board.getStates(), + []] + + + def sendGameState(self, moveList): + gameState = self.board.getStates() + self.sendUpdate('setGameState', [ + gameState, + moveList]) + + + def clearBoard(self): + for x in self.board.squareList: + x.setState(0) + + + + def getPosHpr(self): + return self.posHpr + + + def existsLegalJumpsFrom(self, index, peice): + if peice == 'king': + for x in xrange(4): + if self.board.squareList[index].getAdjacent()[x] != None and self.board.squareList[index].getJumps()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + jump = self.board.squareList[self.board.squareList[index].getJumps()[x]] + if adj.getState() == 0: + pass + elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2: + pass + elif jump.getState() == 0: + return True + + adj.getState() == self.playerNum + 2 + + return False + elif peice == 'normal': + if self.playerNum == 1: + moveForward = [ + 1, + 2] + elif self.playerNum == 2: + moveForward = [ + 0, + 3] + + for x in moveForward: + if self.board.squareList[index].getAdjacent()[x] != None and self.board.squareList[index].getJumps()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + jump = self.board.squareList[self.board.squareList[index].getJumps()[x]] + if adj.getState() == 0: + pass + elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2: + pass + elif jump.getState() == 0: + return True + + adj.getState() == self.playerNum + 2 + + return False + + + + def existsLegalMovesFrom(self, index, peice): + if peice == 'king': + for x in self.board.squareList[index].getAdjacent(): + if x != None: + if self.board.squareList[x].getState() == 0: + return True + + self.board.squareList[x].getState() == 0 + + return False + elif peice == 'normal': + if self.playerNum == 1: + moveForward = [ + 1, + 2] + elif self.playerNum == 2: + moveForward = [ + 0, + 3] + + for x in moveForward: + if self.board.squareList[index].getAdjacent()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + if adj.getState() == 0: + return True + + adj.getState() == 0 + + return False + + + + def existsLegalJumpsFrom(self, index, peice): + if peice == 'king': + for x in xrange(4): + if self.board.squareList[index].getAdjacent()[x] != None and self.board.squareList[index].getJumps()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + jump = self.board.squareList[self.board.squareList[index].getJumps()[x]] + if adj.getState() == 0: + pass + elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2: + pass + elif jump.getState() == 0: + return True + + adj.getState() == self.playerNum + 2 + + return False + elif peice == 'normal': + if self.playerNum == 1: + moveForward = [ + 1, + 2] + elif self.playerNum == 2: + moveForward = [ + 0, + 3] + + for x in moveForward: + if self.board.squareList[index].getAdjacent()[x] != None and self.board.squareList[index].getJumps()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + jump = self.board.squareList[self.board.squareList[index].getJumps()[x]] + if adj.getState() == 0: + pass + elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2: + pass + elif jump.getState() == 0: + return True + + adj.getState() == self.playerNum + 2 + + return False + + + + def checkLegalMove(self, firstSquare, secondSquare, peice): + if self.playerNum == 1: + moveForward = [ + 1, + 2] + else: + moveForward = [ + 0, + 3] + if peice == 'king': + for x in xrange(4): + if firstSquare.getAdjacent()[x] != None: + if self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0: + return True + + self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0 + + return False + elif peice == 'normal': + for x in moveForward: + if firstSquare.getAdjacent()[x] != None: + if self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0: + return True + + self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0 + + return False + + + + def checkLegalJump(self, firstSquare, secondSquare, peice): + if self.playerNum == 1: + moveForward = [ + 1, + 2] + opposingPeices = [ + 2, + 4] + else: + moveForward = [ + 0, + 3] + opposingPeices = [ + 1, + 3] + if peice == 'king': + if secondSquare.getNum() in firstSquare.getJumps(): + index = firstSquare.getJumps().index(secondSquare.getNum()) + if self.board.squareList[firstSquare.getAdjacent()[index]].getState() in opposingPeices: + return True + else: + return False + + elif peice == 'normal': + if secondSquare.getNum() in firstSquare.getJumps(): + index = firstSquare.getJumps().index(secondSquare.getNum()) + if index in moveForward: + if self.board.squareList[firstSquare.getAdjacent()[index]].getState() in opposingPeices: + return True + else: + return False + else: + return False + else: + return False diff --git a/toontown/safezone/DistributedChineseCheckers.py b/toontown/safezone/DistributedChineseCheckers.py new file mode 100755 index 00000000..badbcbae --- /dev/null +++ b/toontown/safezone/DistributedChineseCheckers.py @@ -0,0 +1,746 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from direct.distributed import DistributedNode +from direct.distributed.ClockDelta import globalClockDelta +from ChineseCheckersBoard import ChineseCheckersBoard +from direct.fsm import ClassicFSM, State +from direct.fsm import StateData +from toontown.distributed import DelayDelete +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from otp.otpbase import OTPGlobals +from direct.showbase import PythonUtil + +class DistributedChineseCheckers(DistributedNode.DistributedNode): + + def __init__(self, cr): + NodePath.__init__(self, 'DistributedChineseCheckers') + DistributedNode.DistributedNode.__init__(self, cr) + self.cr = cr + self.reparentTo(render) + self.boardNode = loader.loadModel('phase_6/models/golf/checker_game.bam') + self.boardNode.reparentTo(self) + self.board = ChineseCheckersBoard() + self.playerTags = render.attachNewNode('playerTags') + self.playerTagList = [] + self.exitButton = None + self.inGame = False + self.waiting = True + self.startButton = None + self.playerNum = None + self.turnText = None + self.isMyTurn = False + self.wantTimer = True + self.leaveButton = None + self.screenText = None + self.turnText = None + self.exitButton = None + self.numRandomMoves = 0 + self.blinker = Sequence() + self.playersTurnBlinker = Sequence() + self.yourTurnBlinker = Sequence() + self.moveList = [] + self.mySquares = [] + self.playerSeats = None + self.accept('mouse1', self.mouseClick) + self.traverser = base.cTrav + self.pickerNode = CollisionNode('mouseRay') + self.pickerNP = camera.attachNewNode(self.pickerNode) + self.pickerNode.setFromCollideMask(ToontownGlobals.WallBitmask) + self.pickerRay = CollisionRay() + self.pickerNode.addSolid(self.pickerRay) + self.myHandler = CollisionHandlerQueue() + self.traverser.addCollider(self.pickerNP, self.myHandler) + self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + self.upButton = self.buttonModels.find('**//InventoryButtonUp') + self.downButton = self.buttonModels.find('**/InventoryButtonDown') + self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover') + self.clockNode = ToontownTimer() + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.setScale(0.3) + self.clockNode.hide() + self.playerColors = [Vec4(0, 0.9, 0, 1), + Vec4(0.9, 0.9, 0, 1), + Vec4(0.45, 0, 0.45, 1), + Vec4(0.2, 0.4, 0.8, 1), + Vec4(1, 0.45, 1, 1), + Vec4(0.8, 0, 0, 1)] + self.tintConstant = Vec4(0.25, 0.25, 0.25, 0) + self.ghostConstant = Vec4(0, 0, 0, 0.5) + self.startingPositions = [[0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9], + [10, + 11, + 12, + 13, + 23, + 24, + 25, + 35, + 36, + 46], + [65, + 75, + 76, + 86, + 87, + 88, + 98, + 99, + 100, + 101], + [111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120], + [74, + 84, + 85, + 95, + 96, + 97, + 107, + 108, + 109, + 110], + [19, + 20, + 21, + 22, + 32, + 33, + 34, + 44, + 45, + 55]] + self.nonOpposingPositions = [] + self.knockSound = base.loadSfx('phase_5/audio/sfx/GUI_knock_1.ogg') + self.clickSound = base.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.ogg') + self.moveSound = base.loadSfx('phase_6/audio/sfx/CC_move.ogg') + self.accept('stoppedAsleep', self.handleSleep) + from direct.fsm import ClassicFSM, State + self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, ['playing', 'gameOver']), State.State('playing', self.enterPlaying, self.exitPlaying, ['gameOver']), State.State('gameOver', self.enterGameOver, self.exitGameOver, ['waitingToBegin'])], 'waitingToBegin', 'waitingToBegin') + x = self.boardNode.find('**/locators') + self.locatorList = x.getChildren() + tempList = [] + for x in xrange(0, 121): + self.locatorList[x].setTag('GamePeiceLocator', '%d' % x) + tempList.append(self.locatorList[x].attachNewNode(CollisionNode('picker%d' % x))) + tempList[x].node().addSolid(CollisionSphere(0, 0, 0, 0.115)) + + for z in self.locatorList: + y = loader.loadModel('phase_6/models/golf/checker_marble.bam') + z.setColor(0, 0, 0, 0) + y.reparentTo(z) + + return + + def setName(self, name): + self.name = name + + def announceGenerate(self): + DistributedNode.DistributedNode.announceGenerate(self) + if self.table.fsm.getCurrentState().getName() != 'observing': + if base.localAvatar.doId in self.table.tableState: + self.seatPos = self.table.tableState.index(base.localAvatar.doId) + self.playerTags.setPos(self.getPos()) + + def handleSleep(self, task = None): + if self.fsm.getCurrentState().getName() == 'waitingToBegin': + self.exitButtonPushed() + if task != None: + task.done + return + + def setTableDoId(self, doId): + self.tableDoId = doId + self.table = self.cr.doId2do[doId] + self.table.setTimerFunc(self.startButtonPushed) + self.fsm.enterInitialState() + self.table.setGameDoId(self.doId) + + def disable(self): + DistributedNode.DistributedNode.disable(self) + if self.leaveButton: + self.leaveButton.destroy() + self.leavebutton = None + if self.screenText: + self.screenText.destroy() + self.screenText = None + if self.turnText: + self.turnText.destroy() + self.turnText = None + self.clockNode.stop() + self.clockNode.hide() + self.ignore('mouse1') + self.ignore('stoppedAsleep') + self.fsm = None + self.cleanPlayerTags() + return + + def delete(self): + DistributedNode.DistributedNode.delete(self) + self.table.gameDoId = None + self.table.game = None + if self.exitButton: + self.exitButton.destroy() + if self.startButton: + self.startButton.destroy() + self.clockNode.stop() + self.clockNode.hide() + self.table.startButtonPushed = None + self.ignore('mouse1') + self.ignore('stoppedAsleep') + self.fsm = None + self.table = None + self.cleanPlayerTags() + del self.playerTags + del self.playerTagList + self.playerSeats = None + self.yourTurnBlinker.finish() + return + + def getTimer(self): + self.sendUpdate('requestTimer', []) + + def setTimer(self, timerEnd): + if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState().getName() == 'observing': + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(timerEnd) + timeLeft = int(time - globalClock.getRealTime()) + if timeLeft > 0 and timerEnd != 0: + if timeLeft > 60: + timeLeft = 60 + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.countdown(timeLeft, self.startButtonPushed) + self.clockNode.show() + else: + self.clockNode.stop() + self.clockNode.hide() + return + + def setTurnTimer(self, turnEnd): + if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'playing': + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(turnEnd) + timeLeft = int(time - globalClock.getRealTime()) + if timeLeft > 0: + self.clockNode.setPos(-.74, 0, -0.2) + if self.isMyTurn: + self.clockNode.countdown(timeLeft, self.doRandomMove) + else: + self.clockNode.countdown(timeLeft, self.doNothing) + self.clockNode.show() + return + + def gameStart(self, playerNum): + if playerNum != 255: + self.playerNum = playerNum + self.playerColor = self.playerColors[playerNum - 1] + self.moveCameraForGame() + playerPos = playerNum - 1 + import copy + self.nonOpposingPositions = copy.deepcopy(self.startingPositions) + if playerPos == 0: + self.nonOpposingPositions.pop(0) + self.opposingPositions = self.nonOpposingPositions.pop(2) + elif playerPos == 1: + self.nonOpposingPositions.pop(1) + self.opposingPositions = self.nonOpposingPositions.pop(3) + elif playerPos == 2: + self.nonOpposingPositions.pop(2) + self.opposingPositions = self.nonOpposingPositions.pop(4) + elif playerPos == 3: + self.nonOpposingPositions.pop(3) + self.opposingPositions = self.nonOpposingPositions.pop(0) + elif playerPos == 4: + self.nonOpposingPositions.pop(4) + self.opposingPositions = self.nonOpposingPositions.pop(1) + elif playerPos == 5: + self.nonOpposingPositions.pop(5) + self.opposingPositions = self.nonOpposingPositions.pop(2) + self.fsm.request('playing') + + def sendTurn(self, playersTurn): + self.playersTurnBlinker.finish() + if self.fsm.getCurrentState().getName() == 'playing': + if self.playerSeats == None: + self.sendUpdate('requestSeatPositions', []) + else: + if playersTurn == self.playerNum: + self.isMyTurn = True + self.enableTurnScreenText(playersTurn) + self.playersTurnBlinker = Sequence() + origColor = self.playerColors[playersTurn - 1] + self.playersTurnBlinker.append(LerpColorInterval(self.playerTagList[self.playerSeats.index(playersTurn)], 0.4, origColor - self.tintConstant - self.ghostConstant, origColor)) + self.playersTurnBlinker.append(LerpColorInterval(self.playerTagList[self.playerSeats.index(playersTurn)], 0.4, origColor, origColor - self.tintConstant - self.ghostConstant)) + self.playersTurnBlinker.loop() + return + + def announceSeatPositions(self, playerPos): + self.playerSeats = playerPos + for x in xrange(6): + pos = self.table.seats[x].getPos(render) + renderedPeice = loader.loadModel('phase_6/models/golf/checker_marble.bam') + renderedPeice.reparentTo(self.playerTags) + renderedPeice.setPos(pos) + renderedPeice.setScale(1.5) + if x == 1: + renderedPeice.setZ(renderedPeice.getZ() + 3.3) + renderedPeice.setScale(1.3) + elif x == 4: + renderedPeice.setZ(renderedPeice.getZ() + 3.3) + renderedPeice.setScale(1.45) + else: + renderedPeice.setZ(renderedPeice.getZ() + 3.3) + renderedPeice.hide() + + self.playerTagList = self.playerTags.getChildren() + for x in playerPos: + if x != 0: + self.playerTagList[playerPos.index(x)].setColor(self.playerColors[x - 1]) + self.playerTagList[playerPos.index(x)].show() + + def cleanPlayerTags(self): + for x in self.playerTagList: + x.removeNode() + + self.playerTagList = [] + self.playerTags.removeNode() + + def moveCameraForGame(self): + if self.table.cameraBoardTrack.isPlaying(): + self.table.cameraBoardTrack.finish() + rotation = 0 + if self.seatPos > 2: + if self.playerNum == 1: + rotation = 180 + elif self.playerNum == 2: + rotation = -120 + elif self.playerNum == 3: + rotation = -60 + elif self.playerNum == 4: + rotation = 0 + elif self.playerNum == 5: + rotation = 60 + elif self.playerNum == 6: + rotation = 120 + elif self.playerNum == 1: + rotation = 0 + elif self.playerNum == 2: + rotation = 60 + elif self.playerNum == 3: + rotation = 120 + elif self.playerNum == 4: + rotation = 180 + elif self.playerNum == 5: + rotation = -120 + elif self.playerNum == 6: + rotation = -60 + if rotation == 60 or rotation == -60: + int = LerpHprInterval(self.boardNode, 2.5, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr()) + elif rotation == 120 or rotation == -120: + int = LerpHprInterval(self.boardNode, 3.5, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr()) + else: + int = LerpHprInterval(self.boardNode, 4.2, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr()) + int.start() + + def enterWaitingToBegin(self): + if self.table.fsm.getCurrentState().getName() != 'observing': + self.enableExitButton() + self.enableStartButton() + + def exitWaitingToBegin(self): + if self.exitButton: + self.exitButton.destroy() + self.exitButton = None + if self.startButton: + self.startButton.destroy() + self.exitButton = None + self.clockNode.stop() + self.clockNode.hide() + return + + def enterPlaying(self): + self.inGame = True + self.enableScreenText() + if self.table.fsm.getCurrentState().getName() != 'observing': + self.enableLeaveButton() + + def exitPlaying(self): + self.inGame = False + if self.leaveButton: + self.leaveButton.destroy() + self.leavebutton = None + self.playerNum = None + if self.screenText: + self.screenText.destroy() + self.screenText = None + if self.turnText: + self.turnText.destroy() + self.turnText = None + self.clockNode.stop() + self.clockNode.hide() + self.cleanPlayerTags() + return + + def enterGameOver(self): + pass + + def exitGameOver(self): + pass + + def exitWaitCountdown(self): + self.__disableCollisions() + self.ignore('trolleyExitButton') + self.clockNode.reset() + + def enableExitButton(self): + self.exitButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersGetUpButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.4), scale=0.15, command=lambda self = self: self.exitButtonPushed()) + return + + def enableScreenText(self): + defaultPos = (-.8, -0.4) + if self.playerNum == 1: + message = TTLocalizer.ChineseCheckersColorG + color = self.playerColors[0] + elif self.playerNum == 2: + message = TTLocalizer.ChineseCheckersColorY + color = self.playerColors[1] + elif self.playerNum == 3: + message = TTLocalizer.ChineseCheckersColorP + color = self.playerColors[2] + elif self.playerNum == 4: + message = TTLocalizer.ChineseCheckersColorB + color = self.playerColors[3] + elif self.playerNum == 5: + message = TTLocalizer.ChineseCheckersColorPink + color = self.playerColors[4] + elif self.playerNum == 6: + message = TTLocalizer.ChineseCheckersColorR + color = self.playerColors[5] + else: + message = TTLocalizer.ChineseCheckersColorO + color = Vec4(0.0, 0.0, 0.0, 1.0) + defaultPos = (-.8, -0.4) + self.screenText = OnscreenText(text=message, pos=defaultPos, scale=0.1, fg=color, align=TextNode.ACenter, mayChange=1) + + def enableStartButton(self): + self.startButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersStartButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.6, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.1), scale=0.15, command=lambda self = self: self.startButtonPushed()) + return + + def enableLeaveButton(self): + self.leaveButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersQuitButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.13), text_scale=0.5, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.4), scale=0.15, command=lambda self = self: self.exitButtonPushed()) + return + + def enableTurnScreenText(self, player): + self.yourTurnBlinker.finish() + playerOrder = [1, + 4, + 2, + 5, + 3, + 6] + message1 = TTLocalizer.ChineseCheckersIts + if self.turnText != None: + self.turnText.destroy() + if player == self.playerNum: + message2 = TTLocalizer.ChineseCheckersYourTurn + color = (0, 0, 0, 1) + elif player == 1: + message2 = TTLocalizer.ChineseCheckersGreenTurn + color = self.playerColors[0] + elif player == 2: + message2 = TTLocalizer.ChineseCheckersYellowTurn + color = self.playerColors[1] + elif player == 3: + message2 = TTLocalizer.ChineseCheckersPurpleTurn + color = self.playerColors[2] + elif player == 4: + message2 = TTLocalizer.ChineseCheckersBlueTurn + color = self.playerColors[3] + elif player == 5: + message2 = TTLocalizer.ChineseCheckersPinkTurn + color = self.playerColors[4] + elif player == 6: + message2 = TTLocalizer.ChineseCheckersRedTurn + color = self.playerColors[5] + self.turnText = OnscreenText(text=message1 + message2, pos=(-0.8, -0.5), scale=0.092, fg=color, align=TextNode.ACenter, mayChange=1) + if player == self.playerNum: + self.yourTurnBlinker = Sequence() + self.yourTurnBlinker.append(LerpScaleInterval(self.turnText, 0.6, 1.045, 1)) + self.yourTurnBlinker.append(LerpScaleInterval(self.turnText, 0.6, 1, 1.045)) + self.yourTurnBlinker.loop() + return + + def startButtonPushed(self): + self.sendUpdate('requestBegin') + self.startButton.hide() + self.clockNode.stop() + self.clockNode.hide() + + def exitButtonPushed(self): + self.fsm.request('gameOver') + self.table.fsm.request('off') + self.clockNode.stop() + self.clockNode.hide() + self.table.sendUpdate('requestExit') + + def mouseClick(self): + messenger.send('wakeup') + if self.isMyTurn == True and self.inGame == True: + mpos = base.mouseWatcherNode.getMouse() + self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) + self.traverser.traverse(render) + if self.myHandler.getNumEntries() > 0: + self.myHandler.sortEntries() + pickedObj = self.myHandler.getEntry(0).getIntoNodePath() + pickedObj = pickedObj.getNetTag('GamePeiceLocator') + if pickedObj: + self.handleClicked(int(pickedObj)) + + def handleClicked(self, index): + self.sound = Sequence(SoundInterval(self.clickSound)) + if self.moveList == []: + if index not in self.mySquares: + return + self.moveList.append(index) + if index in self.opposingPositions: + self.isOpposing = True + else: + self.isOpposing = False + self.blinker = Sequence() + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.playerColor - self.tintConstant, self.playerColor)) + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.playerColor, self.playerColor - self.tintConstant)) + self.blinker.loop() + self.sound.start() + elif self.board.squareList[index].getState() == self.playerNum: + for x in self.moveList: + self.locatorList[x].setColor(1, 1, 1, 1) + self.locatorList[x].hide() + + self.blinker.finish() + self.blinker = Sequence() + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.playerColor - self.tintConstant, self.playerColor)) + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.playerColor, self.playerColor - self.tintConstant)) + self.blinker.loop() + self.sound.start() + self.locatorList[self.moveList[0]].setColor(self.playerColor) + self.locatorList[self.moveList[0]].show() + self.moveList = [] + self.moveList.append(index) + if index in self.opposingPositions: + self.isOpposing = True + else: + self.isOpposing = False + elif self.board.squareList[index].getState() != 0: + return + else: + if len(self.moveList) == 1 and self.board.squareList[index].getState() == 0: + if index in self.board.squareList[self.moveList[0]].getAdjacent(): + for x in self.nonOpposingPositions: + if index in x: + return + + self.moveList.append(index) + self.blinker.finish() + self.d_requestMove(self.moveList) + self.moveList = [] + self.isMyTurn = False + self.sound.start() + if len(self.moveList) >= 1: + if index == self.moveList[len(self.moveList) - 1]: + for x in self.nonOpposingPositions: + if index in x: + return + + if self.existsLegalJumpsFrom(index) == True: + self.blinker.finish() + self.d_requestMove(self.moveList) + self.moveList = [] + self.isMyTurn = False + self.sound.start() + elif self.checkLegalMove(self.board.getSquare(self.moveList[len(self.moveList) - 1]), self.board.getSquare(index)) == True: + if index not in self.board.squareList[self.moveList[len(self.moveList) - 1]].getAdjacent(): + for x in self.nonOpposingPositions: + if self.existsLegalJumpsFrom(index) == False: + if index in x: + return + + self.moveList.append(index) + self.locatorList[index].setColor(self.playerColor - self.tintConstant - self.ghostConstant) + self.locatorList[index].show() + self.sound.start() + if self.existsLegalJumpsFrom(index) == False: + self.blinker.finish() + self.d_requestMove(self.moveList) + self.moveList = [] + self.isMyTurn = False + + def existsLegalJumpsFrom(self, index): + for x in self.board.squareList[index].getAdjacent(): + if x == None: + pass + elif x in self.moveList: + pass + elif self.board.getState(x) == 0: + pass + elif self.board.squareList[x].getAdjacent()[self.board.squareList[index].getAdjacent().index(x)] == None: + pass + elif self.board.getState(self.board.squareList[x].getAdjacent()[self.board.squareList[index].getAdjacent().index(x)]) == 0 and self.board.squareList[x].getAdjacent()[self.board.squareList[index].getAdjacent().index(x)] not in self.moveList: + return True + + return False + + def checkLegalMove(self, firstSquare, secondSquare): + if secondSquare.getNum() in firstSquare.getAdjacent(): + return True + else: + for x in firstSquare.getAdjacent(): + if x == None: + pass + elif self.board.squareList[x].getState() == 0: + pass + elif self.board.squareList[x].getAdjacent()[firstSquare.getAdjacent().index(x)] == secondSquare.getNum(): + return True + + return False + return + + def d_requestMove(self, moveList): + self.sendUpdate('requestMove', [moveList]) + + def setGameState(self, tableState, moveList): + if moveList != []: + self.animatePeice(tableState, moveList) + else: + self.updateGameState(tableState) + + def updateGameState(self, squares): + self.board.setStates(squares) + self.mySquares = [] + messenger.send('wakeup') + for x in xrange(121): + self.locatorList[x].clearColor() + owner = self.board.squareList[x].getState() + if owner == self.playerNum: + self.mySquares.append(x) + if owner == 0: + self.locatorList[x].hide() + else: + self.locatorList[x].show() + if owner == 1: + self.locatorList[x].setColor(self.playerColors[0]) + elif owner == 2: + self.locatorList[x].setColor(self.playerColors[1]) + elif owner == 3: + self.locatorList[x].setColor(self.playerColors[2]) + elif owner == 4: + self.locatorList[x].setColor(self.playerColors[3]) + elif owner == 5: + self.locatorList[x].setColor(self.playerColors[4]) + elif owner == 6: + self.locatorList[x].setColor(self.playerColors[5]) + + self.mySquares.sort() + self.checkForWin() + + def animatePeice(self, tableState, moveList): + messenger.send('wakeup') + gamePeiceForAnimation = loader.loadModel('phase_6/models/golf/checker_marble.bam') + gamePeiceForAnimation.setColor(self.locatorList[moveList[0]].getColor()) + gamePeiceForAnimation.reparentTo(self.boardNode) + gamePeiceForAnimation.setPos(self.locatorList[moveList[0]].getPos()) + self.locatorList[moveList[0]].hide() + checkersPeiceTrack = Sequence() + length = len(moveList) + for x in xrange(length - 1): + checkersPeiceTrack.append(Parallel(SoundInterval(self.moveSound), ProjectileInterval(gamePeiceForAnimation, endPos=self.locatorList[moveList[x + 1]].getPos(), duration=0.5))) + + checkersPeiceTrack.append(Func(gamePeiceForAnimation.removeNode)) + checkersPeiceTrack.append(Func(self.updateGameState, tableState)) + checkersPeiceTrack.start() + + def checkForWin(self): + if self.playerNum == 1: + if self.mySquares == self.startingPositions[3]: + self.sendUpdate('requestWin', []) + elif self.playerNum == 2: + if self.mySquares == self.startingPositions[4]: + self.sendUpdate('requestWin', []) + elif self.playerNum == 3: + if self.mySquares == self.startingPositions[5]: + self.sendUpdate('requestWin', []) + elif self.playerNum == 4: + if self.mySquares == self.startingPositions[0]: + self.sendUpdate('requestWin', []) + elif self.playerNum == 5: + if self.mySquares == self.startingPositions[1]: + self.sendUpdate('requestWin', []) + elif self.playerNum == 6: + if self.mySquares == self.startingPositions[2]: + self.sendUpdate('requestWin', []) + + def announceWin(self, avId): + self.fsm.request('gameOver') + + def doRandomMove(self): + if len(self.moveList) >= 2: + self.blinker.finish() + self.d_requestMove(self.moveList) + self.moveList = [] + self.isMyTurn = False + self.playSound = Sequence(SoundInterval(self.knockSound)) + self.playSound.start() + else: + import random + move = [] + foundLegal = False + self.blinker.pause() + self.numRandomMoves += 1 + while not foundLegal: + x = random.randint(0, 9) + for y in self.board.getAdjacent(self.mySquares[x]): + if y != None and self.board.getState(y) == 0: + for zz in self.nonOpposingPositions: + if y not in zz: + move.append(self.mySquares[x]) + move.append(y) + foundLegal = True + break + + break + + if move == []: + pass + playSound = Sequence(SoundInterval(self.knockSound)) + playSound.start() + self.d_requestMove(move) + self.moveList = [] + self.isMyTurn = False + if self.numRandomMoves >= 5: + self.exitButtonPushed() + return + + def doNothing(self): + pass diff --git a/toontown/safezone/DistributedChineseCheckersAI.py b/toontown/safezone/DistributedChineseCheckersAI.py new file mode 100755 index 00000000..a42c02e8 --- /dev/null +++ b/toontown/safezone/DistributedChineseCheckersAI.py @@ -0,0 +1,692 @@ +# File: D (Python 2.4) + +from direct.distributed.DistributedNodeAI import DistributedNodeAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from direct.distributed.ClockDelta import * +from toontown.safezone import ChineseCheckersBoard + +class DistributedChineseCheckersAI(DistributedNodeAI): + + def __init__(self, air, parent, name, x, y, z, h, p, r): + DistributedNodeAI.__init__(self, air) + self.name = name + self.air = air + self.setPos(x, y, z) + self.setHpr(h, p, r) + self.myPos = (x, y, z) + self.myHpr = (h, p, r) + self.board = ChineseCheckersBoard.ChineseCheckersBoard() + self.parent = self.air.doId2do[parent] + self.parentDo = parent + self.wantStart = [] + self.playersPlaying = [] + self.playersSitting = 0 + self.playersTurn = 1 + self.movesMade = 0 + self.playersGamePos = [ + None, + None, + None, + None, + None, + None] + self.wantTimer = True + self.timerEnd = 0 + self.turnEnd = 0 + self.playersObserving = [] + self.winLaffPoints = 20 + self.movesRequiredToWin = 10 + self.zoneId = self.air.allocateZone() + self.generateOtpObject(air.districtId, self.zoneId, optionalFields = [ + 'setX', + 'setY', + 'setZ', + 'setH', + 'setP', + 'setR']) + self.parent.setCheckersZoneId(self.zoneId) + self.startingPositions = [ + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9], + [ + 10, + 11, + 12, + 13, + 23, + 24, + 25, + 35, + 36, + 46], + [ + 65, + 75, + 76, + 86, + 87, + 88, + 98, + 99, + 100, + 101], + [ + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120], + [ + 74, + 84, + 85, + 95, + 96, + 97, + 107, + 108, + 109, + 110], + [ + 19, + 20, + 21, + 22, + 32, + 33, + 34, + 44, + 45, + 55]] + self.timerStart = None + self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [ + State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, [ + 'playing']), + State.State('playing', self.enterPlaying, self.exitPlaying, [ + 'gameOver']), + State.State('gameOver', self.enterGameOver, self.exitGameOver, [ + 'waitingToBegin'])], 'waitingToBegin', 'waitingToBegin') + self.fsm.enterInitialState() + + + def announceGenerate(self): + self.parent.setGameDoId(self.doId) + + + def getTableDoId(self): + return self.parentDo + + + def delete(self): + self.fsm.requestFinalState() + self.board.delete() + self.playerSeatPos = None + del self.fsm + DistributedNodeAI.delete(self) + + + def requestSeatPositions(self): + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'announceSeatPositions', [ + self.playerSeatPos]) + self.sendUpdateToAvatarId(avId, 'sendTurn', [ + self.playersTurn + 1]) + + + def informGameOfPlayer(self): + self.playersSitting += 1 + if self.playersSitting < 2: + self.timerEnd = 0 + elif self.playersSitting == 2: + self.timerEnd = globalClock.getRealTime() + 60 + elif self.playersSitting > 2: + pass + + self.sendUpdate('setTimer', [ + globalClockDelta.localToNetworkTime(self.timerEnd)]) + + + def informGameOfPlayerLeave(self): + self.playersSitting -= 1 + if self.playersSitting < 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin': + self.timerEnd = 0 + + if self.playersSitting > 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin': + pass + 1 + self.timerEnd = 0 + if self.timerEnd != 0: + self.sendUpdate('setTimer', [ + globalClockDelta.localToNetworkTime(self.timerEnd)]) + else: + self.sendUpdate('setTimer', [ + 0]) + + + def setGameCountdownTime(self): + self.timerEnd = globalClock.getRealTime() + 60 + + + def setTurnCountdownTime(self): + self.turnEnd = globalClock.getRealTime() + 60 + + + def getTimer(self): + if self.timerEnd != 0: + return 0 + else: + return 0 + + + def getTurnTimer(self): + return globalClockDelta.localToNetworkTime(self.turnEnd) + + + def requestTimer(self): + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'setTimer', [ + globalClockDelta.localToNetworkTime(self.timerEnd)]) + + + def handlePlayerExit(self, avId): + playerOrder = [ + 1, + 4, + 2, + 5, + 3, + 6] + if avId in self.wantStart: + self.wantStart.remove(avId) + + playstate = self.fsm.getStateNamed('playing') + if self.fsm.getCurrentState().getName() == 'playing': + gamePos = self.playersGamePos.index(avId) + self.playersGamePos[gamePos] = None + for x in self.board.squareList: + if x.getState() == gamePos + 1: + x.setState(0) + continue + + self.sendGameState([]) + if self.playersTurn == gamePos: + self.advancePlayerTurn() + self.d_sendTurn(self.playersTurn + 1) + + remainingPlayers = 0 + for x in self.playersGamePos: + if x != None: + remainingPlayers += 1 + continue + + if remainingPlayers == 1: + for x in self.playersGamePos: + if x != None: + self.clearBoard() + self.sendGameState([]) + if self.movesMade >= self.movesRequiredToWin: + self.distributeLaffPoints() + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', x) + else: + self.fsm.request('gameOver') + + def handleEmptyGame(self): + self.movesMade = 0 + self.playersPlaying = [] + self.playersTurn = 1 + self.fsm.request('waitingToBegin') + + + def requestWin(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.playersGamePos: + self.air.writeServerEvent('suspicious', avId, 'Has requested a Chinese Checkers win and is NOT playing! SeatList of the table - %s - PlayersGamePos - %s' % (self.parent.seats, self.playersGamePos)) + return None + + requestWinGamePos = self.playersGamePos.index(avId) + 1 + checkSquares = [] + for x in self.board.squareList: + if x.getState() == requestWinGamePos: + checkSquares.append(x.getNum()) + continue + + if requestWinGamePos == 1: + if checkSquares == self.startingPositions[3]: + self.distributeLaffPoints() + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', avId) + + elif requestWinGamePos == 2: + if checkSquares == self.startingPositions[4]: + self.distributeLaffPoints() + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', avId) + + elif requestWinGamePos == 3: + if checkSquares == self.startingPositions[5]: + self.distributeLaffPoints() + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', avId) + + elif requestWinGamePos == 4: + if checkSquares == self.startingPositions[0]: + self.distributeLaffPoints() + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', avId) + + elif requestWinGamePos == 5: + if checkSquares == self.startingPositions[1]: + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', avId) + + elif requestWinGamePos == 6: + if checkSquares == self.startingPositions[2]: + self.distributeLaffPoints() + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', avId) + + + self.parent = None + + + def distributeLaffPoints(self): + for x in self.parent.seats: + if x != None: + av = self.air.doId2do.get(x) + av.toonUp(self.winLaffPoints) + continue + + + + def enterWaitingToBegin(self): + self.setGameCountdownTime() + self.parent.isAccepting = True + + + def exitWaitingToBegin(self): + self.turnEnd = 0 + + + def enterPlaying(self): + self.parent.isAccepting = False + for x in self.playersGamePos: + if x != None: + self.playersTurn = self.playersGamePos.index(x) + self.d_sendTurn(self.playersTurn + 1) + break + continue + + self.setTurnCountdownTime() + self.sendUpdate('setTurnTimer', [ + globalClockDelta.localToNetworkTime(self.turnEnd)]) + + + def exitPlaying(self): + pass + + + def enterGameOver(self): + self.timerEnd = 0 + isAccepting = True + self.playersObserving = [] + self.parent.handleGameOver() + self.playersTurn = 1 + self.playersPlaying = [] + self.clearBoard() + self.sendGameState([]) + self.movesMade = 0 + self.playersGamePos = [ + None, + None, + None, + None, + None, + None] + self.fsm.request('waitingToBegin') + + + def exitGameOver(self): + pass + + + def requestBegin(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.wantStart: + self.wantStart.append(avId) + + numPlayers = 0 + for x in self.parent.seats: + if x != None: + numPlayers = numPlayers + 1 + continue + + if len(self.wantStart) == numPlayers and numPlayers >= 2: + self.d_gameStart(avId) + self.parent.sendIsPlaying() + + + + def d_gameStart(self, avId): + for x in self.playersObserving: + self.sendUpdateToAvatarId(x, 'gameStart', [ + 255]) + + playerJoinOrder = [ + 1, + 4, + 2, + 5, + 3, + 6] + zz = 0 + numPlayers = 0 + for x in self.parent.seats: + if x != None: + numPlayers += 1 + self.playersPlaying.append(x) + continue + + if numPlayers == 2: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [ + 1]) + self.playersGamePos[0] = player1 + for x in self.startingPositions[0]: + self.board.setState(x, 1) + + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [ + 4]) + self.playersGamePos[3] = player2 + for x in self.startingPositions[3]: + self.board.setState(x, 4) + + elif numPlayers == 3: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [ + 2]) + self.playersGamePos[1] = player1 + for x in self.startingPositions[1]: + self.board.setState(x, 2) + + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [ + 4]) + self.playersGamePos[3] = player2 + for x in self.startingPositions[3]: + self.board.setState(x, 4) + + player3 = self.playersPlaying[2] + self.sendUpdateToAvatarId(player3, 'gameStart', [ + 6]) + self.playersGamePos[5] = player3 + for x in self.startingPositions[5]: + self.board.setState(x, 6) + + elif numPlayers == 4: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [ + 1]) + self.playersGamePos[0] = player1 + for x in self.startingPositions[0]: + self.board.setState(x, 1) + + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [ + 4]) + self.playersGamePos[3] = player2 + for x in self.startingPositions[3]: + self.board.setState(x, 4) + + player3 = self.playersPlaying[2] + self.sendUpdateToAvatarId(player3, 'gameStart', [ + 2]) + self.playersGamePos[1] = player3 + for x in self.startingPositions[1]: + self.board.setState(x, 2) + + player4 = self.playersPlaying[3] + self.sendUpdateToAvatarId(player4, 'gameStart', [ + 5]) + self.playersGamePos[4] = player4 + for x in self.startingPositions[4]: + self.board.setState(x, 5) + + elif numPlayers == 5: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [ + 1]) + self.playersGamePos[0] = player1 + for x in self.startingPositions[0]: + self.board.setState(x, 1) + + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [ + 4]) + self.playersGamePos[3] = player2 + for x in self.startingPositions[3]: + self.board.setState(x, 4) + + player3 = self.playersPlaying[2] + self.sendUpdateToAvatarId(player3, 'gameStart', [ + 2]) + self.playersGamePos[1] = player3 + for x in self.startingPositions[1]: + self.board.setState(x, 2) + + player4 = self.playersPlaying[3] + self.sendUpdateToAvatarId(player4, 'gameStart', [ + 5]) + self.playersGamePos[4] = player4 + for x in self.startingPositions[4]: + self.board.setState(x, 5) + + player5 = self.playersPlaying[4] + self.sendUpdateToAvatarId(player5, 'gameStart', [ + 3]) + self.playersGamePos[2] = player5 + for x in self.startingPositions[2]: + self.board.setState(x, 3) + + elif numPlayers == 6: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [ + 1]) + self.playersGamePos[0] = player1 + for x in self.startingPositions[0]: + self.board.setState(x, 1) + + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [ + 4]) + self.playersGamePos[3] = player2 + for x in self.startingPositions[3]: + self.board.setState(x, 4) + + player3 = self.playersPlaying[2] + self.sendUpdateToAvatarId(player3, 'gameStart', [ + 2]) + self.playersGamePos[1] = player3 + for x in self.startingPositions[1]: + self.board.setState(x, 2) + + player4 = self.playersPlaying[3] + self.sendUpdateToAvatarId(player4, 'gameStart', [ + 5]) + self.playersGamePos[4] = player4 + for x in self.startingPositions[4]: + self.board.setState(x, 5) + + player5 = self.playersPlaying[4] + self.sendUpdateToAvatarId(player5, 'gameStart', [ + 3]) + self.playersGamePos[2] = player5 + for x in self.startingPositions[2]: + self.board.setState(x, 3) + + player6 = self.playersPlaying[5] + self.sendUpdateToAvatarId(player6, 'gameStart', [ + 6]) + self.playersGamePos[5] = player6 + for x in self.startingPositions[5]: + self.board.setState(x, 6) + + + playerSeatPos = [ + 0, + 0, + 0, + 0, + 0, + 0] + for x in xrange(6): + id = self.playersGamePos[x] + if id != None: + playerSeatPos[self.parent.seats.index(id)] = x + 1 + continue + + self.sendUpdate('announceSeatPositions', [ + playerSeatPos]) + self.playerSeatPos = playerSeatPos + self.sendGameState([]) + self.wantStart = [] + self.fsm.request('playing') + self.parent.getTableState() + + + def d_sendTurn(self, playersTurn): + self.sendUpdate('sendTurn', [ + playersTurn]) + + + def advancePlayerTurn(self): + foundNewPlayer = False + while foundNewPlayer == False: + self.playersTurn += 1 + if self.playersTurn > 5: + self.playersTurn = 0 + + if self.playersGamePos[self.playersTurn] != None: + foundNewPlayer = True + continue + + + def requestMove(self, moveList): + playerOrder = [ + 1, + 4, + 2, + 5, + 3, + 6] + if self.checkLegalMoves(moveList) == True: + self.movesMade += 1 + self.makeMove(moveList) + self.advancePlayerTurn() + self.d_sendTurn(self.playersTurn + 1) + self.setTurnCountdownTime() + self.sendUpdate('setTurnTimer', [ + globalClockDelta.localToNetworkTime(self.turnEnd)]) + + + + def checkLegalMoves(self, moveList): + if not moveList: + return False + elif self.board.squareList[moveList[0]].getState() == 0: + return False + + for x in xrange(len(moveList) - 1): + y = self.checkLegalMove(self.board.getSquare(moveList[x]), self.board.getSquare(moveList[x + 1])) + if y == False: + return False + continue + + return True + + + def checkLegalMove(self, firstSquare, secondSquare): + if secondSquare.getNum() in firstSquare.getAdjacent(): + return True + else: + for x in firstSquare.getAdjacent(): + if x == None: + continue + if self.board.squareList[x].getState() == 0: + continue + if self.board.squareList[x].getAdjacent()[firstSquare.getAdjacent().index(x)] == secondSquare.getNum(): + return True + continue + + return False + + + def makeMove(self, moveList): + spot1 = self.board.squareList[moveList[0]].getState() + self.board.squareList[moveList[0]].setState(0) + self.board.squareList[moveList[len(moveList) - 1]].setState(spot1) + self.sendGameState(moveList) + + + def getState(self): + return self.fsm.getCurrentState().getName() + + + def getName(self): + return self.name + + + def getGameState(self): + return [ + self.board.getStates(), + []] + + + def sendGameState(self, moveList): + gameState = self.board.getStates() + self.sendUpdate('setGameState', [ + gameState, + moveList]) + + + def clearBoard(self): + for x in self.board.squareList: + x.setState(0) + + + + def getPosHpr(self): + return self.posHpr + + + def testWin(self): + self.clearBoard() + for x in self.startingPositions[0]: + self.board.squareList[x].setState(4) + + self.board.squareList[self.startingPositions[0][len(self.startingPositions[0]) - 1]].setState(0) + self.board.squareList[51].setState(4) + for x in self.startingPositions[3]: + self.board.squareList[x].setState(1) + + self.board.squareList[120].setState(0) + self.board.squareList[104].setState(1) + self.sendGameState([]) diff --git a/toontown/safezone/DistributedDGFlower.py b/toontown/safezone/DistributedDGFlower.py new file mode 100755 index 00000000..378e3f80 --- /dev/null +++ b/toontown/safezone/DistributedDGFlower.py @@ -0,0 +1,59 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from toontown.toonbase import ToontownGlobals +from direct.task import Task +SPIN_RATE = 12.5 + +class DistributedDGFlower(DistributedObject.DistributedObject): + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.bigFlower = loader.loadModel('phase_8/models/props/DG_flower-mod.bam') + self.bigFlower.setPos(1.39, 92.91, 2.0) + self.bigFlower.setScale(2.5) + self.bigFlower.reparentTo(render) + self.flowerCollSphere = CollisionSphere(0, 0, 0, 4.5) + self.flowerCollSphereNode = CollisionNode('bigFlowerCollide') + self.flowerCollSphereNode.addSolid(self.flowerCollSphere) + self.flowerCollSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + self.bigFlower.attachNewNode(self.flowerCollSphereNode) + self.flowerTrigSphere = CollisionSphere(0, 0, 0, 6.0) + self.flowerTrigSphere.setTangible(0) + self.flowerTrigSphereNode = CollisionNode('bigFlowerTrigger') + self.flowerTrigSphereNode.addSolid(self.flowerTrigSphere) + self.flowerTrigSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + self.bigFlower.attachNewNode(self.flowerTrigSphereNode) + taskMgr.add(self.__flowerSpin, self.taskName('DG-flowerSpin')) + self.accept('enterbigFlowerTrigger', self.__flowerEnter) + self.accept('exitbigFlowerTrigger', self.__flowerExit) + + def disable(self): + DistributedObject.DistributedObject.disable(self) + taskMgr.remove(self.taskName('DG-flowerRaise')) + taskMgr.remove(self.taskName('DG-flowerSpin')) + self.ignore('enterbigFlowerTrigger') + self.ignore('exitbigFlowerTrigger') + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.bigFlower.removeNode() + del self.bigFlower + del self.flowerCollSphere + del self.flowerCollSphereNode + + def __flowerSpin(self, task): + self.bigFlower.setH(self.bigFlower.getH() + SPIN_RATE*globalClock.getDt()) + return Task.cont + + def __flowerEnter(self, collisionEntry): + self.sendUpdate('avatarEnter', []) + + def __flowerExit(self, collisionEntry): + self.sendUpdate('avatarExit', []) + + def setHeight(self, newHeight): + pos = self.bigFlower.getPos() + self.bigFlower.posInterval(0.5, (pos[0], pos[1], newHeight)).start() diff --git a/toontown/safezone/DistributedDGFlowerAI.py b/toontown/safezone/DistributedDGFlowerAI.py new file mode 100755 index 00000000..09837ef9 --- /dev/null +++ b/toontown/safezone/DistributedDGFlowerAI.py @@ -0,0 +1,39 @@ +from direct.distributed import DistributedObjectAI +from direct.distributed.ClockDelta import * +from otp.ai.AIBase import * +from toontown.toonbase.ToontownGlobals import * + + +HEIGHT_DELTA = 0.5 +MAX_HEIGHT = 10.0 +MIN_HEIGHT = 2.0 + + +class DistributedDGFlowerAI(DistributedObjectAI.DistributedObjectAI): + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + + self.height = MIN_HEIGHT + self.avList = [] + + def delete(self): + DistributedObjectAI.DistributedObjectAI.delete(self) + + def start(self): + pass + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.avList: + self.avList.append(avId) + if self.height + HEIGHT_DELTA <= MAX_HEIGHT: + self.height += HEIGHT_DELTA + self.sendUpdate('setHeight', [self.height]) + + def avatarExit(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.avList: + self.avList.remove(avId) + if self.height - HEIGHT_DELTA >= MIN_HEIGHT: + self.height -= HEIGHT_DELTA + self.sendUpdate('setHeight', [self.height]) diff --git a/toontown/safezone/DistributedEFlyingTreasure.py b/toontown/safezone/DistributedEFlyingTreasure.py new file mode 100755 index 00000000..a398761d --- /dev/null +++ b/toontown/safezone/DistributedEFlyingTreasure.py @@ -0,0 +1,26 @@ +from DistributedTreasure import DistributedTreasure +import math, random + +class DistributedEFlyingTreasure(DistributedTreasure): + + def __init__(self, cr): + DistributedTreasure.__init__(self, cr) + self.scale = 2 + self.delT = math.pi * 2.0 * random.random() + self.shadow = 0 + + def disable(self): + DistributedTreasure.disable(self) + taskMgr.remove(self.taskName('flying-treasure')) + + def setPosition(self, x, y, z): + DistributedTreasure.setPosition(self, x, y, z) + self.initPos = self.nodePath.getPos() + taskMgr.add(self.animateTask, self.taskName('flying-treasure')) + + def animateTask(self, task): + pos = self.initPos + t = 0.5 * math.pi * globalClock.getFrameTime() + dZ = 5.0 * math.sin(t + self.delT) + self.nodePath.setPos(pos[0], pos[1], pos[2] + dZ) + return task.cont diff --git a/toontown/safezone/DistributedEFlyingTreasureAI.py b/toontown/safezone/DistributedEFlyingTreasureAI.py new file mode 100644 index 00000000..06e9952c --- /dev/null +++ b/toontown/safezone/DistributedEFlyingTreasureAI.py @@ -0,0 +1,4 @@ +import DistributedTreasureAI + +class DistributedEFlyingTreasureAI(DistributedTreasureAI.DistributedTreasureAI): + pass \ No newline at end of file diff --git a/toontown/safezone/DistributedFindFour.py b/toontown/safezone/DistributedFindFour.py new file mode 100755 index 00000000..8c939952 --- /dev/null +++ b/toontown/safezone/DistributedFindFour.py @@ -0,0 +1,852 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from direct.distributed import DistributedNode +from direct.distributed.ClockDelta import globalClockDelta +from ChineseCheckersBoard import ChineseCheckersBoard +from direct.fsm import ClassicFSM, State +from direct.fsm import StateData +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from otp.otpbase import OTPGlobals +from direct.showbase import PythonUtil +import random + +class DistributedFindFour(DistributedNode.DistributedNode): + + def __init__(self, cr): + NodePath.__init__(self, 'DistributedFindFour') + DistributedNode.DistributedNode.__init__(self, cr) + self.cr = cr + self.reparentTo(render) + self.boardNode = loader.loadModel('phase_6/models/golf/findfour_game.bam') + self.boardNode.reparentTo(self) + self.board = [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]] + self.exitButton = None + self.inGame = False + self.waiting = True + self.startButton = None + self.playerNum = None + self.turnText = None + self.isMyTurn = False + self.wantTimer = True + self.leaveButton = None + self.screenText = None + self.turnText = None + self.exitButton = None + self.numRandomMoves = 0 + self.blinker = Sequence() + self.playersTurnBlinker = Sequence() + self.yourTurnBlinker = Sequence() + self.winningSequence = Sequence() + self.moveSequence = Sequence() + self.moveList = [] + self.mySquares = [] + self.playerSeats = None + self.moveCol = None + self.move = None + self.accept('mouse1', self.mouseClick) + self.traverser = base.cTrav + self.pickerNode = CollisionNode('mouseRay') + self.pickerNP = camera.attachNewNode(self.pickerNode) + self.pickerNode.setFromCollideMask(BitMask32(4096)) + self.pickerRay = CollisionRay() + self.pickerNode.addSolid(self.pickerRay) + self.myHandler = CollisionHandlerQueue() + self.traverser.addCollider(self.pickerNP, self.myHandler) + self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + self.upButton = self.buttonModels.find('**//InventoryButtonUp') + self.downButton = self.buttonModels.find('**/InventoryButtonDown') + self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover') + self.clockNode = ToontownTimer() + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.setScale(0.3) + self.clockNode.hide() + self.tintConstant = Vec4(0.25, 0.25, 0.25, 0) + self.ghostConstant = Vec4(0, 0, 0, 0.5) + self.knockSound = base.loadSfx('phase_5/audio/sfx/GUI_knock_1.ogg') + self.clickSound = base.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.ogg') + self.moveSound = base.loadSfx('phase_6/audio/sfx/CC_move.ogg') + self.accept('stoppedAsleep', self.handleSleep) + from direct.fsm import ClassicFSM, State + self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, ['playing', 'gameOver']), State.State('playing', self.enterPlaying, self.exitPlaying, ['gameOver']), State.State('gameOver', self.enterGameOver, self.exitGameOver, ['waitingToBegin'])], 'waitingToBegin', 'waitingToBegin') + startLoc = self.boardNode.find('**/locators') + self.locatorList = list(startLoc.getChildren()) + self.startingPositions = self.locatorList.pop(0) + self.startingPositions = self.startingPositions.getChildren() + instancePiece = self.boardNode.find('**/pieces') + tempList = [] + for x in xrange(7): + self.startingPositions[x].setTag('StartLocator', '%d' % x) + collNode = CollisionNode('startpicker%d' % x) + collNode.setIntoCollideMask(BitMask32(4096)) + tempList.append(self.startingPositions[x].attachNewNode(collNode)) + tempList[x].node().addSolid(CollisionTube(0, 0, 0.23, 0, 0, -.23, 0.2)) + + for z in self.startingPositions: + y = instancePiece.copyTo(z) + for val in y.getChildren(): + val.hide() + + tempList = [] + for x in xrange(42): + self.locatorList[x].setTag('GamePeiceLocator', '%d' % x) + collNode = CollisionNode('startpicker%d' % x) + collNode.setIntoCollideMask(BitMask32(4096)) + tempList.append(self.locatorList[x].attachNewNode(collNode)) + tempList[x].node().addSolid(CollisionSphere(0, 0, 0, 0.2)) + + for z in self.locatorList: + y = instancePiece.copyTo(z) + for val in y.getChildren(): + val.hide() + + dummyHide = instancePiece.getParent().attachNewNode('DummyHider') + instancePiece.reparentTo(dummyHide) + dummyHide.hide() + return + + def setName(self, name): + self.name = name + + def announceGenerate(self): + DistributedNode.DistributedNode.announceGenerate(self) + if self.table.fsm.getCurrentState().getName() != 'observing': + if base.localAvatar.doId in self.table.tableState: + self.seatPos = self.table.tableState.index(base.localAvatar.doId) + if self.seatPos <= 2: + for x in self.startingPositions: + x.setH(0) + + for x in self.locatorList: + x.setH(0) + + else: + for x in self.startingPositions: + x.setH(180) + + for x in self.locatorList: + x.setH(180) + + self.moveCameraForGame() + else: + self.seatPos = self.table.seatBumpForObserve + if self.seatPos > 2: + for x in self.startingPositions: + x.setH(180) + + for x in self.locatorList: + x.setH(180) + + self.moveCameraForGame() + + def handleSleep(self, task = None): + if self.fsm.getCurrentState().getName() == 'waitingToBegin': + self.exitButtonPushed() + if task != None: + task.done + return + + def setTableDoId(self, doId): + self.tableDoId = doId + self.table = self.cr.doId2do[doId] + self.table.setTimerFunc(self.startButtonPushed) + self.fsm.enterInitialState() + self.table.setGameDoId(self.doId) + + def disable(self): + DistributedNode.DistributedNode.disable(self) + if self.leaveButton: + self.leaveButton.destroy() + self.leavebutton = None + if self.screenText: + self.screenText.destroy() + self.screenText = None + if self.turnText: + self.turnText.destroy() + self.turnText = None + self.clockNode.stop() + self.clockNode.hide() + self.ignore('mouse1') + self.ignore('stoppedAsleep') + self.fsm = None + taskMgr.remove('playerTurnTask') + return + + def delete(self): + DistributedNode.DistributedNode.delete(self) + self.table.gameDoId = None + self.table.game = None + if self.exitButton: + self.exitButton.destroy() + if self.startButton: + self.startButton.destroy() + self.clockNode.stop() + self.clockNode.hide() + self.table.startButtonPushed = None + self.ignore('mouse1') + self.ignore('stoppedAsleep') + self.fsm = None + self.table = None + self.winningSequence.finish() + taskMgr.remove('playerTurnTask') + return + + def getTimer(self): + self.sendUpdate('requestTimer', []) + + def setTimer(self, timerEnd): + if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState().getName() == 'observing': + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(timerEnd) + timeLeft = int(time - globalClock.getRealTime()) + if timeLeft > 0 and timerEnd != 0: + if timeLeft > 60: + timeLeft = 60 + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.countdown(timeLeft, self.startButtonPushed) + self.clockNode.show() + else: + self.clockNode.stop() + self.clockNode.hide() + return + + def setTurnTimer(self, turnEnd): + if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'playing': + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(turnEnd) + timeLeft = int(time - globalClock.getRealTime()) + if timeLeft > 0: + self.clockNode.setPos(0.64, 0, -0.27) + self.clockNode.countdown(timeLeft, self.doRandomMove) + self.clockNode.show() + return + + def gameStart(self, playerNum): + if playerNum != 255: + self.playerNum = playerNum + if self.playerNum == 1: + self.playerColorString = 'Red' + else: + self.playerColorString = 'Yellow' + self.moveCameraForGame() + self.fsm.request('playing') + + def sendTurn(self, playersTurn): + if self.fsm.getCurrentState().getName() == 'playing': + if playersTurn == self.playerNum: + self.isMyTurn = True + taskMgr.add(self.turnTask, 'playerTurnTask') + self.enableTurnScreenText(playersTurn) + + def illegalMove(self): + self.exitButtonPushed() + + def moveCameraForGame(self): + if self.table.cameraBoardTrack.isPlaying(): + self.table.cameraBoardTrack.pause() + rotation = 0 + if self.seatPos <= 2: + position = self.table.seats[1].getPos() + position = position + Vec3(0, -8, 12.8) + int = LerpPosHprInterval(camera, 2, position, Vec3(0, -38, 0), camera.getPos(), camera.getHpr()) + else: + position = self.table.seats[4].getPos() + position = position + Vec3(0, -8, 12.8) + if camera.getH() < 0: + int = LerpPosHprInterval(camera, 2, position, Vec3(-180, -20, 0), camera.getPos(), camera.getHpr()) + else: + int = LerpPosHprInterval(camera, 2, position, Vec3(180, -20, 0), camera.getPos(), camera.getHpr()) + int.start() + + def enterWaitingToBegin(self): + if self.table.fsm.getCurrentState().getName() != 'observing': + self.enableExitButton() + self.enableStartButton() + + def exitWaitingToBegin(self): + if self.exitButton: + self.exitButton.destroy() + self.exitButton = None + if self.startButton: + self.startButton.destroy() + self.exitButton = None + self.clockNode.stop() + self.clockNode.hide() + return + + def enterPlaying(self): + self.inGame = True + self.enableScreenText() + if self.table.fsm.getCurrentState().getName() != 'observing': + self.enableLeaveButton() + + def exitPlaying(self): + self.inGame = False + if self.leaveButton: + self.leaveButton.destroy() + self.leavebutton = None + self.playerNum = None + if self.screenText: + self.screenText.destroy() + self.screenText = None + if self.turnText: + self.turnText.destroy() + self.turnText = None + self.clockNode.stop() + self.clockNode.hide() + return + + def enterGameOver(self): + pass + + def exitGameOver(self): + pass + + def exitWaitCountdown(self): + self.__disableCollisions() + self.ignore('trolleyExitButton') + self.clockNode.reset() + + def enableExitButton(self): + self.exitButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersGetUpButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.8), scale=0.15, command=lambda self = self: self.exitButtonPushed()) + return + + def enableScreenText(self): + defaultPos = (-.7, -0.29) + if self.playerNum == 1: + message = 'You are Red' + color = Vec4(1, 0, 0, 1) + elif self.playerNum == 2: + message = 'You are Yellow' + color = Vec4(1, 1, 0, 1) + else: + message = TTLocalizer.CheckersObserver + color = Vec4(0, 0, 0, 1) + self.screenText = OnscreenText(text=message, pos=defaultPos, scale=0.1, fg=color, align=TextNode.ACenter, mayChange=1) + + def enableStartButton(self): + self.startButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersStartButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.6, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.57), scale=0.15, command=lambda self = self: self.startButtonPushed()) + return + + def enableLeaveButton(self): + self.leaveButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersQuitButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.13), text_scale=0.5, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.8), scale=0.15, command=lambda self = self: self.exitButtonPushed()) + return + + def enableTurnScreenText(self, player): + playerOrder = [1, + 4, + 2, + 5, + 3, + 6] + message1 = TTLocalizer.CheckersIts + if self.turnText != None: + self.turnText.destroy() + if player == self.playerNum: + message2 = TTLocalizer.ChineseCheckersYourTurn + color = (0, 0, 0, 1) + elif player == 1: + message2 = "Red's Turn" + color = (1, 0, 0, 1) + elif player == 2: + message2 = "Yellow's Turn" + color = (1, 1, 0, 1) + self.turnText = OnscreenText(text=message1 + message2, pos=(-0.7, -0.39), scale=0.092, fg=color, align=TextNode.ACenter, mayChange=1) + return + + def startButtonPushed(self): + self.sendUpdate('requestBegin') + self.startButton.hide() + self.clockNode.stop() + self.clockNode.hide() + + def exitButtonPushed(self): + self.fsm.request('gameOver') + self.table.fsm.request('off') + self.clockNode.stop() + self.clockNode.hide() + self.table.sendUpdate('requestExit') + + def mouseClick(self): + messenger.send('wakeup') + if self.isMyTurn == True and self.inGame == True and not self.moveSequence.isPlaying(): + if self.moveCol != None: + self.d_requestMove(self.moveCol) + self.moveCol = None + self.isMyTurn = False + taskMgr.remove('playerTurnTask') + return + + def handleClicked(self, index): + pass + + def turnTask(self, task): + if base.mouseWatcherNode.hasMouse() == False: + return task.cont + if self.isMyTurn == False: + return task.cont + if self.moveSequence.isPlaying(): + return task.cont + mpos = base.mouseWatcherNode.getMouse() + self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) + self.traverser.traverse(render) + if self.myHandler.getNumEntries() > 0: + self.myHandler.sortEntries() + pickedObj = self.myHandler.getEntry(0).getIntoNodePath() + pickedObj = pickedObj.getNetTag('StartLocator') + if pickedObj: + colVal = int(pickedObj) + if colVal == self.moveCol: + return task.cont + if self.board[0][colVal] == 0: + if self.moveCol != None: + for x in self.startingPositions[self.moveCol].getChild(1).getChildren(): + x.hide() + + self.moveCol = colVal + if self.playerNum == 1: + self.startingPositions[self.moveCol].getChild(1).getChild(2).show() + elif self.playerNum == 2: + self.startingPositions[self.moveCol].getChild(1).getChild(3).show() + return task.cont + + def d_requestMove(self, moveCol): + self.sendUpdate('requestMove', [moveCol]) + + def setGameState(self, tableState, moveCol, movePos, turn): + messenger.send('wakeup') + if self.table.fsm.getCurrentState().getName() == 'observing': + isBlank = True + for x in xrange(7): + if self.board[5][x] != 0: + isBlank = False + break + + gameBlank = True + for x in xrange(7): + if tableState[5][x] != 0: + gameBlank = False + break + + if isBlank == True and gameBlank == False: + for x in xrange(6): + for y in xrange(7): + self.board[x][y] = tableState[x][y] + + self.updateGameState() + return + if moveCol == 0 and movePos == 0 and turn == 0: + for x in xrange(6): + for y in xrange(7): + self.board[x][y] = tableState[x][y] + + self.updateGameState() + else: + self.animatePeice(tableState, moveCol, movePos, turn) + didIWin = self.checkForWin() + if didIWin != None: + self.sendUpdate('requestWin', [didIWin]) + return + + def updateGameState(self): + for x in xrange(6): + for y in xrange(7): + for z in self.locatorList[x * 7 + y].getChild(1).getChildren(): + z.hide() + + for x in xrange(6): + for y in xrange(7): + state = self.board[x][y] + if state == 1: + self.locatorList[x * 7 + y].getChild(1).getChild(0).show() + elif state == 2: + self.locatorList[x * 7 + y].getChild(1).getChild(1).show() + + def checkForWin(self): + for x in xrange(6): + for y in xrange(7): + if self.board[x][y] == self.playerNum: + if self.checkHorizontal(x, y, self.playerNum) == True: + return [x, y] + elif self.checkVertical(x, y, self.playerNum) == True: + return [x, y] + elif self.checkDiagonal(x, y, self.playerNum) == True: + return [x, y] + + return None + + def announceWinnerPosition(self, x, y, winDirection, playerNum): + self.isMyturn = False + if self.turnText: + self.turnText.hide() + self.clockNode.stop() + self.clockNode.hide() + if winDirection == 0: + blinkList = self.findHorizontal(x, y, playerNum) + elif winDirection == 1: + blinkList = self.findVertical(x, y, playerNum) + elif winDirection == 2: + blinkList = self.findDiagonal(x, y, playerNum) + if blinkList != []: + print blinkList + val0 = x * 7 + y + x = blinkList[0][0] + y = blinkList[0][1] + val1 = x * 7 + y + x = blinkList[1][0] + y = blinkList[1][1] + val2 = x * 7 + y + x = blinkList[2][0] + y = blinkList[2][1] + val3 = x * 7 + y + self.winningSequence = Sequence() + downBlinkerParallel = Parallel(LerpColorInterval(self.locatorList[val0], 0.3, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)), LerpColorInterval(self.locatorList[val1], 0.3, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)), LerpColorInterval(self.locatorList[val2], 0.3, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)), LerpColorInterval(self.locatorList[val3], 0.3, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1))) + upBlinkerParallel = Parallel(LerpColorInterval(self.locatorList[val0], 0.3, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)), LerpColorInterval(self.locatorList[val1], 0.3, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)), LerpColorInterval(self.locatorList[val2], 0.3, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)), LerpColorInterval(self.locatorList[val3], 0.3, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5))) + self.winningSequence.append(downBlinkerParallel) + self.winningSequence.append(upBlinkerParallel) + self.winningSequence.loop() + + def tie(self): + self.tieSequence = Sequence(autoFinish=1) + self.clockNode.stop() + self.clockNode.hide() + self.isMyTurn = False + self.moveSequence.finish() + if self.turnText: + self.turnText.hide() + for x in xrange(41): + self.tieSequence.append(Parallel(LerpColorInterval(self.locatorList[x], 0.15, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)), LerpColorInterval(self.locatorList[x], 0.15, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)))) + + whisper = WhisperPopup('This Find Four game has resulted in a Tie!', OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + whisper.manage(base.marginManager) + self.tieSequence.start() + + def hideChildren(self, nodeList): + pass + + def animatePeice(self, tableState, moveCol, movePos, turn): + messenger.send('wakeup') + for x in xrange(6): + for y in xrange(7): + self.board[x][y] = tableState[x][y] + + pos = self.startingPositions[moveCol].getPos() + if turn == 0: + peice = self.startingPositions[moveCol].getChild(1).getChildren()[2] + peice.show() + elif turn == 1: + peice = self.startingPositions[moveCol].getChild(1).getChildren()[3] + peice.show() + self.moveSequence = Sequence() + startPos = self.startingPositions[moveCol].getPos() + arrayLoc = movePos * 7 + moveCol + self.moveSequence.append(LerpPosInterval(self.startingPositions[moveCol], 1.5, self.locatorList[arrayLoc].getPos(self), startPos)) + self.moveSequence.append(Func(peice.hide)) + self.moveSequence.append(Func(self.startingPositions[moveCol].setPos, startPos)) + self.moveSequence.append(Func(self.updateGameState)) + self.moveSequence.start() + + def announceWin(self, avId): + self.fsm.request('gameOver') + + def doRandomMove(self): + if self.isMyTurn: + if self.moveCol != None: + self.d_requestMove(self.moveCol) + self.moveCol = None + self.isMyTurn = False + taskMgr.remove('playerTurnTask') + else: + hasfound = False + while hasfound == False: + x = random.randint(0, 6) + if self.board[0][x] == 0: + self.d_requestMove(x) + self.moveCol = None + self.isMyTurn = False + taskMgr.remove('playerTurnTask') + hasfound = True + + return + + def doNothing(self): + pass + + def checkHorizontal(self, rVal, cVal, playerNum): + if cVal == 3: + for x in xrange(1, 4): + if self.board[rVal][cVal - x] != playerNum: + break + if self.board[rVal][cVal - x] == playerNum and x == 3: + return True + + for x in xrange(1, 4): + if self.board[rVal][cVal + x] != playerNum: + break + if self.board[rVal][cVal + x] == playerNum and x == 3: + return True + + return False + elif cVal == 2: + for x in xrange(1, 4): + if self.board[rVal][cVal + x] != playerNum: + break + if self.board[rVal][cVal + x] == playerNum and x == 3: + return True + + return False + elif cVal == 4: + for x in xrange(1, 4): + if self.board[rVal][cVal - x] != playerNum: + break + if self.board[rVal][cVal - x] == playerNum and x == 3: + return True + + return False + else: + return False + + def checkVertical(self, rVal, cVal, playerNum): + if rVal == 2: + for x in xrange(1, 4): + if self.board[rVal + x][cVal] != playerNum: + break + if self.board[rVal + x][cVal] == playerNum and x == 3: + return True + + return False + elif rVal == 3: + for x in xrange(1, 4): + if self.board[rVal - x][cVal] != playerNum: + break + if self.board[rVal - x][cVal] == playerNum and x == 3: + return True + + return False + else: + return False + + def checkDiagonal(self, rVal, cVal, playerNum): + if cVal <= 2: + if rVal == 2: + for x in xrange(1, 4): + if self.board[rVal + x][cVal + x] != playerNum: + break + if self.board[rVal + x][cVal + x] == playerNum and x == 3: + return True + + return False + elif rVal == 3: + for x in xrange(1, 4): + if self.board[rVal - x][cVal + x] != playerNum: + break + if self.board[rVal - x][cVal + x] == playerNum and x == 3: + return True + + return False + elif cVal >= 4: + if rVal == 2: + for x in xrange(1, 4): + if self.board[rVal + x][cVal - x] != playerNum: + break + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return True + + return False + elif rVal == 3: + for x in xrange(1, 4): + if self.board[rVal - x][cVal - x] != playerNum: + break + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return True + + return False + elif rVal == 3 or rVal == 4 or rVal == 5: + for x in xrange(1, 4): + if self.board[rVal - x][cVal - x] != playerNum: + break + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return True + + for x in xrange(1, 4): + if self.board[rVal - x][cVal - x] != playerNum: + break + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return True + + return False + elif rVal == 0 or rVal == 1 or rVal == 2: + for x in xrange(1, 4): + if self.board[rVal + x][cVal - x] != playerNum: + break + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return True + + for x in xrange(1, 4): + if self.board[rVal + x][cVal + x] != playerNum: + break + if self.board[rVal + x][cVal + x] == playerNum and x == 3: + return True + + return False + return False + + def findHorizontal(self, rVal, cVal, playerNum): + if cVal == 3: + retList = [] + for x in xrange(1, 4): + retList.append([rVal, cVal - x]) + if self.board[rVal][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal][cVal - x] == playerNum and x == 3: + return retList + + for x in xrange(1, 4): + retList.append([rVal, cVal + x]) + if self.board[rVal][cVal + x] != playerNum: + retList = [] + break + if self.board[rVal][cVal + x] == playerNum and x == 3: + return retList + + return [] + elif cVal == 2: + retList = [] + for x in xrange(1, 4): + retList.append([rVal, cVal + x]) + if self.board[rVal][cVal + x] != playerNum: + retList = [] + break + if self.board[rVal][cVal + x] == playerNum and x == 3: + return retList + + return [] + elif cVal == 4: + retList = [] + for x in xrange(1, 4): + retList.append([rVal, cVal - x]) + if self.board[rVal][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal][cVal - x] == playerNum and x == 3: + return retList + + return [] + else: + return [] + + def findVertical(self, rVal, cVal, playerNum): + if rVal == 2: + retList = [] + for x in xrange(1, 4): + retList.append([rVal + x, cVal]) + if self.board[rVal + x][cVal] != playerNum: + retList = [] + break + if self.board[rVal + x][cVal] == playerNum and x == 3: + return retList + + return [] + elif rVal == 3: + retList = [] + for x in xrange(1, 4): + retList.append([rVal - x, cVal]) + if self.board[rVal - x][cVal] != playerNum: + retList = [] + break + if self.board[rVal - x][cVal] == playerNum and x == 3: + return retList + + return [] + else: + return [] + + def findDiagonal(self, rVal, cVal, playerNum): + retList = [] + if cVal <= 2: + if rVal == 2: + for x in xrange(1, 4): + retList.append([rVal + x, cVal + x]) + if self.board[rVal + x][cVal + x] != playerNum: + retList = [] + break + if self.board[rVal + x][cVal + x] == playerNum and x == 3: + return retList + + return [] + elif rVal == 3: + for x in xrange(1, 4): + retList.append([rVal - x, cVal + x]) + if self.board[rVal - x][cVal + x] != playerNum: + retList = [] + break + if self.board[rVal - x][cVal + x] == playerNum and x == 3: + return retList + + return [] + elif cVal >= 4: + if rVal == 2: + for x in xrange(1, 4): + retList.append([rVal + x, cVal - x]) + if self.board[rVal + x][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return retList + + return [] + elif rVal == 3: + for x in xrange(1, 4): + retList.append([rVal - x, cVal - x]) + if self.board[rVal - x][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return retList + + return [] + elif rVal == 3 or rVal == 4 or rVal == 5: + for x in xrange(1, 4): + retList.append([rVal - x, cVal - x]) + if self.board[rVal - x][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return retList + + for x in xrange(1, 4): + retList.append([rVal + x, cVal - x]) + if self.board[rVal + x][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return retList + + return [] + elif rVal == 0 or rVal == 1 or rVal == 2: + for x in xrange(1, 4): + retList.append([rVal + x, cVal - x]) + if self.board[rVal + x][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return retList + + for x in xrange(1, 4): + retList.append([rVal + x, cVal + x]) + if self.board[rVal + x][cVal + x] != playerNum: + retList = [] + break + if self.board[rVal + x][cVal + x] == playerNum and x == 3: + return retList + + return [] + return [] diff --git a/toontown/safezone/DistributedFindFourAI.py b/toontown/safezone/DistributedFindFourAI.py new file mode 100755 index 00000000..b9bf42b3 --- /dev/null +++ b/toontown/safezone/DistributedFindFourAI.py @@ -0,0 +1,636 @@ +# File: D (Python 2.4) + +from direct.distributed.DistributedNodeAI import DistributedNodeAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * + +class DistributedFindFourAI(DistributedNodeAI): + + def __init__(self, air, parent, name, x, y, z, h, p, r): + DistributedNodeAI.__init__(self, air) + self.name = name + self.air = air + self.setPos(x, y, z) + self.setHpr(h, p, r) + self.myPos = (x, y, z) + self.myHpr = (h, p, r) + self.board = [ + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0]] + self.parent = self.air.doId2do[parent] + self.parentDo = parent + self.wantStart = [] + self.playersPlaying = [] + self.playersSitting = 0 + self.playersTurn = 1 + self.movesMade = 0 + self.playerNum = 1 + self.winDirection = None + self.playersGamePos = [ + None, + None] + self.wantTimer = True + self.timerEnd = 0 + self.turnEnd = 0 + self.playersObserving = [] + self.winLaffPoints = 20 + self.movesRequiredToWin = 10 + self.zoneId = self.air.allocateZone() + self.generateOtpObject(air.districtId, self.zoneId, optionalFields = [ + 'setX', + 'setY', + 'setZ', + 'setH', + 'setP', + 'setR']) + self.parent.setCheckersZoneId(self.zoneId) + self.timerStart = None + self.fsm = ClassicFSM.ClassicFSM('Checkers', [ + State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, [ + 'playing']), + State.State('playing', self.enterPlaying, self.exitPlaying, [ + 'gameOver']), + State.State('gameOver', self.enterGameOver, self.exitGameOver, [ + 'waitingToBegin'])], 'waitingToBegin', 'waitingToBegin') + self.fsm.enterInitialState() + + + def announceGenerate(self): + self.parent.setGameDoId(self.doId) + + + def getTableDoId(self): + return self.parentDo + + + def delete(self): + self.fsm.requestFinalState() + self.parent = None + self.parentDo = None + del self.board + del self.fsm + DistributedNodeAI.delete(self) + + + def informGameOfPlayer(self): + self.playersSitting += 1 + if self.playersSitting < 2: + self.timerEnd = 0 + elif self.playersSitting == 2: + self.timerEnd = globalClock.getRealTime() + 20 + self.parent.isAccepting = False + self.parent.sendUpdate('setIsPlaying', [ + 1]) + elif self.playersSitting > 2: + pass + + self.sendUpdate('setTimer', [ + globalClockDelta.localToNetworkTime(self.timerEnd)]) + + + def informGameOfPlayerLeave(self): + self.playersSitting -= 1 + if self.playersSitting < 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin': + self.timerEnd = 0 + self.parent.isAccepting = True + self.parent.sendUpdate('setIsPlaying', [ + 0]) + + if self.playersSitting > 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin': + pass + 1 + self.timerEnd = 0 + if self.timerEnd != 0: + self.sendUpdate('setTimer', [ + globalClockDelta.localToNetworkTime(self.timerEnd)]) + else: + self.sendUpdate('setTimer', [ + 0]) + + + def setGameCountdownTime(self): + self.timerEnd = globalClock.getRealTime() + 10 + + + def setTurnCountdownTime(self): + self.turnEnd = globalClock.getRealTime() + 25 + + + def getTimer(self): + if self.timerEnd != 0: + return 0 + else: + return 0 + + + def getTurnTimer(self): + return globalClockDelta.localToNetworkTime(self.turnEnd) + + + def requestTimer(self): + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'setTimer', [ + globalClockDelta.localToNetworkTime(self.timerEnd)]) + + + def handlePlayerExit(self, avId): + if avId in self.wantStart: + self.wantStart.remove(avId) + + if self.fsm.getCurrentState().getName() == 'playing': + gamePos = self.playersGamePos.index(avId) + self.playersGamePos[gamePos] = None + self.fsm.request('gameOver') + + + + def handleEmptyGame(self): + self.movesMade = 0 + self.playersPlaying = [] + self.playersTurn = 1 + self.playerNum = 1 + self.fsm.request('waitingToBegin') + self.parent.isAccepting = True + + + def requestWin(self, pieceNum): + avId = self.air.getAvatarIdFromSender() + playerNum = self.playersGamePos.index(avId) + 1 + x = pieceNum[0] + y = pieceNum[1] + if self.checkWin(x, y, playerNum) == True: + self.sendUpdate('announceWinnerPosition', [ + x, + y, + self.winDirection, + playerNum]) + winnersSequence = Sequence(Wait(5.0), Func(self.fsm.request, 'gameOver'), Func(self.parent.announceWinner, 'Find Four', avId)) + winnersSequence.start() + else: + self.sendUpdateToAvatarId(avId, 'illegalMove', []) + + + def distributeLaffPoints(self): + for x in self.parent.seats: + if x != None: + av = self.air.doId2do.get(x) + av.toonUp(self.winLaffPoints) + continue + + + + def enterWaitingToBegin(self): + self.setGameCountdownTime() + self.parent.isAccepting = True + + + def exitWaitingToBegin(self): + self.turnEnd = 0 + + + def enterPlaying(self): + self.parent.isAccepting = False + for x in self.playersGamePos: + if x != None: + self.playersTurn = self.playersGamePos.index(x) + self.d_sendTurn(self.playersTurn + 1) + break + continue + + self.setTurnCountdownTime() + self.sendUpdate('setTurnTimer', [ + globalClockDelta.localToNetworkTime(self.turnEnd)]) + + + def exitPlaying(self): + pass + + + def enterGameOver(self): + self.timerEnd = 0 + isAccepting = True + self.parent.handleGameOver() + self.playersObserving = [] + self.playersTurn = 1 + self.playerNum = 1 + self.playersPlaying = [] + self.movesMade = 0 + self.playersGamePos = [ + None, + None] + self.parent.isAccepting = True + self.fsm.request('waitingToBegin') + + + def exitGameOver(self): + pass + + + def requestBegin(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.wantStart: + self.wantStart.append(avId) + + numPlayers = 0 + for x in self.parent.seats: + if x != None: + numPlayers = numPlayers + 1 + continue + + if len(self.wantStart) == numPlayers and numPlayers >= 2: + self.d_gameStart(avId) + self.parent.sendIsPlaying() + + + + def d_gameStart(self, avId): + for x in self.playersObserving: + self.sendUpdateToAvatarId(x, 'gameStart', [ + 255]) + + zz = 0 + numPlayers = 0 + for x in self.parent.seats: + if x != None: + numPlayers += 1 + self.playersPlaying.append(x) + continue + + if numPlayers == 2: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [ + 1]) + self.playersGamePos[0] = player1 + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [ + 2]) + self.playersGamePos[1] = player2 + + self.wantStart = [] + self.fsm.request('playing') + self.parent.getTableState() + + + def d_sendTurn(self, playersTurn): + self.sendUpdate('sendTurn', [ + playersTurn]) + + + def advancePlayerTurn(self): + if self.playersTurn == 0: + self.playersTurn = 1 + self.playerNum = 2 + else: + self.playerNum = 1 + self.playersTurn = 0 + + + def requestMove(self, moveColumn): + avId = self.air.getAvatarIdFromSender() + turn = self.playersTurn + if avId in self.playersGamePos: + if self.playersGamePos.index(avId) != self.playersTurn: + pass + + + if self.board[0][moveColumn] != 0: + self.sendUpdateToAvatarId(avId, 'illegalMove', []) + + for x in xrange(6): + if self.board[x][moveColumn] == 0: + movePos = x + continue + + self.board[movePos][moveColumn] = self.playersTurn + 1 + if self.checkForTie() == True: + self.sendUpdate('setGameState', [ + self.board, + moveColumn, + movePos, + turn]) + self.sendUpdate('tie', []) + winnersSequence = Sequence(Wait(8.0), Func(self.fsm.request, 'gameOver')) + winnersSequence.start() + return None + + self.movesMade += 1 + self.advancePlayerTurn() + self.setTurnCountdownTime() + self.sendUpdate('setTurnTimer', [ + globalClockDelta.localToNetworkTime(self.turnEnd)]) + self.d_sendTurn(self.playersTurn + 1) + self.sendUpdate('setGameState', [ + self.board, + moveColumn, + movePos, + turn]) + + + def checkForTie(self): + for x in xrange(7): + if self.board[0][x] == 0: + return False + continue + + return True + + + def getState(self): + return self.fsm.getCurrentState().getName() + + + def getName(self): + return self.name + + + def getGameState(self): + return [ + self.board, + 0, + 0, + 0] + + + def clearBoard(self): + for x in self.board.squareList: + x.setState(0) + + + + def getPosHpr(self): + return self.posHpr + + + def tempSetBoardState(self): + self.board = [ + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0], + [ + 1, + 2, + 1, + 2, + 2, + 2, + 1], + [ + 2, + 2, + 1, + 2, + 1, + 2, + 1], + [ + 2, + 1, + 1, + 2, + 2, + 1, + 2], + [ + 1, + 2, + 2, + 1, + 2, + 1, + 1], + [ + 1, + 2, + 1, + 2, + 1, + 2, + 1]] + self.sendUpdate('setGameState', [ + self.board, + 0, + 0, + 1]) + + + def checkWin(self, rVal, cVal, playerNum): + if self.checkHorizontal(rVal, cVal, playerNum) == True: + self.winDirection = 0 + return True + elif self.checkVertical(rVal, cVal, playerNum) == True: + self.winDirection = 1 + return True + elif self.checkDiagonal(rVal, cVal, playerNum) == True: + self.winDirection = 2 + return True + else: + self.winDirection = None + return False + + + def checkHorizontal(self, rVal, cVal, playerNum): + if cVal == 3: + for x in xrange(1, 4): + if self.board[rVal][cVal - x] != playerNum: + break + + if self.board[rVal][cVal - x] == playerNum and x == 3: + return True + continue + + for x in xrange(1, 4): + if self.board[rVal][cVal + x] != playerNum: + break + + if self.board[rVal][cVal + x] == playerNum and x == 3: + return True + continue + + return False + elif cVal == 2: + for x in xrange(1, 4): + if self.board[rVal][cVal + x] != playerNum: + break + + if self.board[rVal][cVal + x] == playerNum and x == 3: + return True + continue + + return False + elif cVal == 4: + for x in xrange(1, 4): + if self.board[rVal][cVal - x] != playerNum: + break + + if self.board[rVal][cVal - x] == playerNum and x == 3: + return True + continue + + return False + else: + return False + + + def checkVertical(self, rVal, cVal, playerNum): + if rVal == 2: + for x in xrange(1, 4): + if self.board[rVal + x][cVal] != playerNum: + break + + if self.board[rVal + x][cVal] == playerNum and x == 3: + return True + continue + + return False + elif rVal == 3: + for x in xrange(1, 4): + if self.board[rVal - x][cVal] != playerNum: + break + + if self.board[rVal - x][cVal] == playerNum and x == 3: + return True + continue + + return False + else: + return False + + + def checkDiagonal(self, rVal, cVal, playerNum): + if cVal <= 2: + if rVal == 2: + for x in xrange(1, 4): + if self.board[rVal + x][cVal + x] != playerNum: + break + + if self.board[rVal + x][cVal + x] == playerNum and x == 3: + return True + continue + + return False + elif rVal == 3: + for x in xrange(1, 4): + if self.board[rVal - x][cVal + x] != playerNum: + break + + if self.board[rVal - x][cVal + x] == playerNum and x == 3: + return True + continue + + return False + + elif cVal >= 4: + if rVal == 2: + for x in xrange(1, 4): + if self.board[rVal + x][cVal - x] != playerNum: + break + + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return True + continue + + return False + elif rVal == 3: + for x in xrange(1, 4): + if self.board[rVal - x][cVal - x] != playerNum: + break + + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return True + continue + + return False + + elif rVal == 3 and rVal == 4 or rVal == 5: + for x in xrange(1, 4): + if self.board[rVal - x][cVal - x] != playerNum: + break + + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return True + continue + + for x in xrange(1, 4): + if self.board[rVal + x][cVal - x] != playerNum: + break + + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return True + continue + + return False + elif rVal == 0 and rVal == 1 or rVal == 2: + for x in xrange(1, 4): + if self.board[rVal + x][cVal - x] != playerNum: + break + + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return True + continue + + for x in xrange(1, 4): + if self.board[rVal + x][cVal + x] != playerNum: + break + + if self.board[rVal + x][cVal + x] == playerNum and x == 3: + return True + continue + + return False + + return False diff --git a/toontown/safezone/DistributedFishingSpot.py b/toontown/safezone/DistributedFishingSpot.py new file mode 100755 index 00000000..d4a4e1d4 --- /dev/null +++ b/toontown/safezone/DistributedFishingSpot.py @@ -0,0 +1,1054 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.gui.DirectGui import * +from direct.directtools.DirectGeometry import LineNodePath +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.fishing import FishGlobals +from toontown.shtiker import FishPage +from toontown.toonbase import TTLocalizer +from toontown.quest import Quests +from direct.actor import Actor +from direct.showutil import Rope +import math +from direct.task.Task import Task +import random +import random +from toontown.fishing import FishingTargetGlobals +from toontown.fishing import FishBase +from toontown.fishing import FishPanel +from toontown.effects import Ripples +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownTimer +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.hood import ZoneUtil + +class DistributedFishingSpot(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFishingSpot') + vZeroMax = 25.0 + angleMax = 30.0 + + def __init__(self, cr): + if hasattr(self, 'fishInit'): + return + self.fishInit = 1 + DistributedObject.DistributedObject.__init__(self, cr) + self.lastAvId = 0 + self.lastFrame = 0 + self.avId = 0 + self.av = None + self.placedAvatar = 0 + self.localToonFishing = 0 + self.nodePath = None + self.collSphere = None + self.collNode = None + self.collNodePath = None + self.castTrack = None + self.pond = None + self.guiTrack = None + self.madeGui = 0 + self.castGui = None + self.itemGui = None + self.pole = None + self.line = None + self.poleNode = [] + self.ptop = None + self.bob = None + self.bobBobTask = None + self.splashSounds = None + self.ripples = None + self.line = None + self.lineSphere = None + self.power = 0.0 + self.startAngleNP = 0 + self.firstCast = 1 + self.fishPanel = None + self.fsm = ClassicFSM.ClassicFSM('DistributedFishingSpot', [State.State('off', self.enterOff, self.exitOff, ['waiting', + 'distCasting', + 'fishing', + 'reward', + 'leaving']), + State.State('waiting', self.enterWaiting, self.exitWaiting, ['localAdjusting', + 'distCasting', + 'leaving', + 'sellFish']), + State.State('localAdjusting', self.enterLocalAdjusting, self.exitLocalAdjusting, ['localCasting', 'leaving']), + State.State('localCasting', self.enterLocalCasting, self.exitLocalCasting, ['localAdjusting', 'fishing', 'leaving']), + State.State('distCasting', self.enterDistCasting, self.exitDistCasting, ['fishing', 'leaving', 'reward']), + State.State('fishing', self.enterFishing, self.exitFishing, ['localAdjusting', + 'distCasting', + 'waitForAI', + 'reward', + 'leaving']), + State.State('sellFish', self.enterSellFish, self.exitSellFish, ['waiting', 'leaving']), + State.State('waitForAI', self.enterWaitForAI, self.exitWaitForAI, ['reward', 'leaving']), + State.State('reward', self.enterReward, self.exitReward, ['localAdjusting', + 'distCasting', + 'leaving', + 'sellFish']), + State.State('leaving', self.enterLeaving, self.exitLeaving, [])], 'off', 'off') + self.fsm.enterInitialState() + return + + def disable(self): + self.ignore(self.uniqueName('enterFishingSpotSphere')) + self.setOccupied(0) + self.avId = 0 + if self.castTrack != None: + if self.castTrack.isPlaying(): + self.castTrack.finish() + self.castTrack = None + if self.guiTrack != None: + if self.guiTrack.isPlaying(): + self.guiTrack.finish() + self.guiTrack = None + self.__hideBob() + self.nodePath.detachNode() + self.__unmakeGui() + self.pond.stopCheckingTargets() + self.pond = None + for event in self.getAllAccepting(): + if event.startswith('generate-'): + self.ignore(event) + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + if hasattr(self, 'fishDeleted'): + return + self.fishDeleted = 1 + del self.pond + del self.fsm + if self.nodePath: + self.nodePath.removeNode() + del self.nodePath + DistributedObject.DistributedObject.delete(self) + if self.ripples: + self.ripples.destroy() + + def generateInit(self): + DistributedObject.DistributedObject.generateInit(self) + self.nodePath = NodePath(self.uniqueName('FishingSpot')) + self.angleNP = self.nodePath.attachNewNode(self.uniqueName('FishingSpotAngleNP')) + self.collSphere = CollisionSphere(0, 0, 0, self.getSphereRadius()) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.uniqueName('FishingSpotSphere')) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.nodePath.attachNewNode(self.collNode) + self.bobStartPos = Point3(0.0, 3.0, 8.5) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.nodePath.reparentTo(self.getParentNodePath()) + self.accept(self.uniqueName('enterFishingSpotSphere'), self.__handleEnterSphere) + + def setPondDoId(self, pondDoId): + self.pondDoId = pondDoId + if pondDoId in self.cr.doId2do: + self.setPond(self.cr.doId2do[pondDoId]) + else: + self.acceptOnce('generate-%d' % pondDoId, self.setPond) + + def setPond(self, pond): + self.pond = pond + self.area = self.pond.getArea() + self.waterLevel = FishingTargetGlobals.getWaterLevel(self.area) + + def __handleEnterSphere(self, collEntry): + if base.localAvatar.doId == self.lastAvId and globalClock.getFrameCount() <= self.lastFrame + 1: + self.notify.debug('Ignoring duplicate entry for avatar.') + return + if base.localAvatar.hp > 0 and base.cr.playGame.getPlace().fsm.getCurrentState().getName() != 'fishing': + self.cr.playGame.getPlace().detectedFishingCollision() + self.d_requestEnter() + + def d_requestEnter(self): + self.sendUpdate('requestEnter', []) + + def rejectEnter(self): + self.cr.playGame.getPlace().setState('walk') + + def d_requestExit(self): + self.sendUpdate('requestExit', []) + + def d_doCast(self, power, heading): + self.sendUpdate('doCast', [power, heading]) + + def getSphereRadius(self): + return 1.5 + + def getParentNodePath(self): + return render + + def setPosHpr(self, x, y, z, h, p, r): + self.nodePath.setPosHpr(x, y, z, h, p, r) + self.angleNP.setH(render, self.nodePath.getH(render)) + + def setOccupied(self, avId): + if avId and avId not in self.cr.doId2do: + def tryAgain(av): + def reposition(task): + self.setOccupied(avId) + return task.done + taskMgr.doMethodLater(0.1, reposition, self.uniqueName('reposition')) + self.acceptOnce('generate-%d' % avId, tryAgain) + return + if self.av != None: + if not self.av.isEmpty(): + self.__dropPole() + self.av.loop('neutral') + self.av.setParent(ToontownGlobals.SPRender) + self.av.startSmooth() + self.ignore(self.av.uniqueName('disable')) + self.__hideBob() + self.fsm.requestFinalState() + self.__removePole() + self.av = None + self.placedAvatar = 0 + self.angleNP.setH(render, self.nodePath.getH(render)) + self.__hideLine() + wasLocalToon = self.localToonFishing + self.lastAvId = self.avId + self.lastFrame = globalClock.getFrameCount() + self.avId = avId + self.localToonFishing = 0 + if self.avId == 0: + self.collSphere.setTangible(0) + else: + self.collSphere.setTangible(1) + if self.avId == base.localAvatar.doId: + base.setCellsAvailable(base.bottomCells, 0) + self.localToonFishing = 1 + if base.wantBingo: + self.pond.setLocalToonSpot(self) + self.av = self.cr.doId2do.get(self.avId) + self.__loadStuff() + self.placedAvatar = 0 + self.firstCast = 1 + self.acceptOnce(self.av.uniqueName('disable'), self.__avatarGone) + self.av.stopSmooth() + self.av.wrtReparentTo(self.angleNP) + self.av.setAnimState('neutral', 1.0) + self.createCastTrack() + if wasLocalToon and not self.localToonFishing: + self.__hideCastGui() + if base.wantBingo: + self.pond.setLocalToonSpot() + base.setCellsAvailable([base.bottomCells[1], base.bottomCells[2]], 1) + base.setCellsAvailable(base.rightCells, 1) + place = base.cr.playGame.getPlace() + if place: + place.setState('walk') + return + + def __avatarGone(self): + self.setOccupied(0) + + def setMovie(self, mode, code, itemDesc1, itemDesc2, itemDesc3, power, h): + if self.av == None: + return + if mode == FishGlobals.NoMovie: + pass + elif mode == FishGlobals.EnterMovie: + self.fsm.request('waiting') + elif mode == FishGlobals.ExitMovie: + self.fsm.request('leaving') + elif mode == FishGlobals.CastMovie: + if not self.localToonFishing: + self.fsm.request('distCasting', [power, h]) + elif mode == FishGlobals.PullInMovie: + self.fsm.request('reward', [code, + itemDesc1, + itemDesc2, + itemDesc3]) + return + + def getStareAtNodeAndOffset(self): + return (self.nodePath, Point3()) + + def __loadStuff(self): + rodId = self.av.getFishingRod() + rodPath = FishGlobals.RodFileDict.get(rodId) + if not rodPath: + self.notify.warning('Rod id: %s model not found' % rodId) + rodPath = RodFileDict[0] + self.pole = Actor.Actor() + self.pole.loadModel(rodPath) + self.pole.loadAnims({'cast': 'phase_4/models/props/fishing-pole-chan'}) + self.pole.pose('cast', 0) + self.ptop = self.pole.find('**/joint_attachBill') + if self.line == None: + self.line = Rope.Rope(self.uniqueName('Line')) + self.line.setColor(1, 1, 1, 0.4) + self.line.setTransparency(1) + self.lineSphere = BoundingSphere(Point3(-0.6, -2, -5), 5.5) + if self.bob == None: + self.bob = loader.loadModel('phase_4/models/props/fishing_bob') + self.bob.setScale(1.5) + self.ripples = Ripples.Ripples(self.nodePath) + self.ripples.setScale(0.4) + self.ripples.hide() + if self.splashSounds == None: + self.splashSounds = (base.loadSfx('phase_4/audio/sfx/TT_splash1.ogg'), base.loadSfx('phase_4/audio/sfx/TT_splash2.ogg')) + return + + def __placeAvatar(self): + if not self.placedAvatar: + self.placedAvatar = 1 + self.__holdPole() + self.av.setPosHpr(0, 0, 0, 0, 0, 0) + + def __holdPole(self): + if self.poleNode != []: + self.__dropPole() + np = NodePath('pole-holder') + hands = self.av.getRightHands() + for h in hands: + self.poleNode.append(np.instanceTo(h)) + + self.pole.reparentTo(self.poleNode[0]) + + def __dropPole(self): + self.__hideBob() + self.__hideLine() + if self.pole != None: + self.pole.clearMat() + self.pole.detachNode() + for pn in self.poleNode: + pn.removeNode() + + self.poleNode = [] + return + + def __removePole(self): + self.pole.cleanup() + self.pole.removeNode() + self.poleNode = [] + self.ptop.removeNode() + self.pole = None + self.ptop = None + return + + def __showLineWaiting(self): + self.line.setup(4, ((None, (0, 0, 0)), + (None, (0, -2, -4)), + (self.bob, (0, -1, 0)), + (self.bob, (0, 0, 0)))) + self.line.ropeNode.setBounds(self.lineSphere) + self.line.reparentTo(self.ptop) + return + + def __showLineCasting(self): + self.line.setup(2, ((None, (0, 0, 0)), (self.bob, (0, 0, 0)))) + self.line.ropeNode.setBounds(self.lineSphere) + self.line.reparentTo(self.ptop) + return + + def __showLineReeling(self): + self.line.setup(2, ((None, (0, 0, 0)), (self.bob, (0, 0, 0)))) + self.line.ropeNode.setBounds(self.lineSphere) + self.line.reparentTo(self.ptop) + return + + def __hideLine(self): + if self.line: + self.line.detachNode() + + def __showBobFloat(self): + self.__hideBob() + self.bob.reparentTo(self.angleNP) + self.ripples.reparentTo(self.angleNP) + self.ripples.setPos(self.bob.getPos()) + self.ripples.setZ(self.waterLevel + 0.025) + self.ripples.play() + splashSound = random.choice(self.splashSounds) + base.playSfx(splashSound, volume=0.8, node=self.bob) + self.bobBobTask = taskMgr.add(self.__doBobBob, self.taskName('bob')) + + def __hideBob(self): + if self.bob: + self.bob.detachNode() + if self.bobBobTask: + taskMgr.remove(self.bobBobTask) + self.bobBobTask = None + if self.ripples: + self.ripples.stop() + self.ripples.detachNode() + return + + def __doBobBob(self, task): + z = math.sin(task.time * 1.8) * 0.08 + self.bob.setZ(self.waterLevel + z) + return Task.cont + + def __userExit(self, event = None): + if self.localToonFishing: + self.fsm.request('leaving') + self.d_requestExit() + + def __sellFish(self, result = None): + if self.localToonFishing: + if result == DGG.DIALOG_OK: + self.sendUpdate('sellFish', []) + for button in self.sellFishDialog.buttonList: + button['state'] = DGG.DISABLED + + else: + self.fsm.request('leaving') + self.d_requestExit() + + def __sellFishConfirm(self, result = None): + if self.localToonFishing: + self.fsm.request('waiting', [False]) + + def __showCastGui(self): + self.__hideCastGui() + self.__makeGui() + self.castButton.show() + self.arrow.hide() + self.exitButton.show() + self.timer.show() + self.__updateFishTankGui() + self.castGui.reparentTo(aspect2d) + self.castButton['state'] = DGG.NORMAL + self.jar['text'] = str(self.av.getMoney()) + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.accept(localAvatar.uniqueName('fishTankChange'), self.__updateFishTankGui) + target = base.cr.doFind('DistributedTarget') + if target: + target.hideGui() + if base.wantBingo: + self.__setBingoCastGui() + + def requestLocalAdjusting(mouseEvent): + if self.av.isFishTankFull() and self.__allowSellFish(): + self.fsm.request('sellFish') + else: + self.fsm.request('localAdjusting') + + def requestLocalCasting(mouseEvent): + if not (self.av.isFishTankFull() and self.__allowSellFish()): + self.fsm.request('localCasting') + + self.castButton.bind(DGG.B1PRESS, requestLocalAdjusting) + self.castButton.bind(DGG.B3PRESS, requestLocalAdjusting) + self.castButton.bind(DGG.B1RELEASE, requestLocalCasting) + self.castButton.bind(DGG.B3RELEASE, requestLocalCasting) + if self.firstCast and len(self.av.fishCollection) == 0 and len(self.av.fishTank) == 0: + self.__showHowTo(TTLocalizer.FishingHowToFirstTime) + elif base.wantBingo and self.pond.hasPondBingoManager() and not self.av.fishBingoTutorialDone: + self.__showHowTo(TTLocalizer.FishBingoHelpMain) + self.av.b_setFishBingoTutorialDone(True) + + def __moneyChange(self, money): + self.jar['text'] = str(money) + + def __initCastGui(self): + self.timer.countdown(FishGlobals.CastTimeout) + + def __showQuestItem(self, itemId): + self.__makeGui() + itemName = Quests.getItemName(itemId) + self.itemLabel['text'] = itemName + self.itemGui.reparentTo(aspect2d) + self.itemPackage.show() + self.itemJellybean.hide() + self.itemBoot.hide() + + def __showBootItem(self): + self.__makeGui() + itemName = TTLocalizer.FishingBootItem + self.itemLabel['text'] = itemName + self.itemGui.reparentTo(aspect2d) + self.itemBoot.show() + self.itemJellybean.hide() + self.itemPackage.hide() + + def __setItemLabel(self): + if self.pond.hasPondBingoManager(): + self.itemLabel['text'] = str(itemName + '\n\n' + 'BINGO WILDCARD') + else: + self.itemLabel['text'] = itemName + + def __showJellybeanItem(self, amount): + self.__makeGui() + itemName = TTLocalizer.FishingJellybeanItem % amount + self.itemLabel['text'] = itemName + self.itemGui.reparentTo(aspect2d) + self.jar['text'] = str(self.av.getMoney()) + self.itemJellybean.show() + self.itemBoot.hide() + self.itemPackage.hide() + + def __showFishItem(self, code, fish): + self.fishPanel = FishPanel.FishPanel(fish) + self.__setFishItemPos() + self.fishPanel.setSwimBounds(-0.3, 0.3, -0.235, 0.25) + self.fishPanel.setSwimColor(1.0, 1.0, 0.74901, 1.0) + self.fishPanel.load() + self.fishPanel.show(code) + self.__updateFishTankGui() + + def __setFishItemPos(self): + if base.wantBingo: + if self.pond.hasPondBingoManager(): + self.fishPanel.setPos(0.65, 0, 0.4) + else: + self.fishPanel.setPos(0, 0, 0.5) + else: + self.fishPanel.setPos(0, 0, 0.5) + + def __updateFishTankGui(self): + fishTank = self.av.getFishTank() + lenFishTank = len(fishTank) + maxFishTank = self.av.getMaxFishTank() + self.bucket['text'] = '%s/%s' % (lenFishTank, maxFishTank) + + def __showFailureReason(self, code): + self.__makeGui() + reason = '' + if code == FishGlobals.OverTankLimit: + reason = TTLocalizer.FishingOverTankLimit + self.failureDialog.setMessage(reason) + self.failureDialog.show() + + def __showSellFishDialog(self): + self.__makeGui() + self.sellFishDialog.show() + + def __hideSellFishDialog(self): + self.__makeGui() + self.sellFishDialog.hide() + + def __showSellFishConfirmDialog(self, numFishCaught): + self.__makeGui() + msg = TTLocalizer.STOREOWNER_TROPHY % (numFishCaught, FishGlobals.getTotalNumFish()) + self.sellFishConfirmDialog.setMessage(msg) + self.sellFishConfirmDialog.show() + + def __hideSellFishConfirmDialog(self): + self.__makeGui() + self.sellFishConfirmDialog.hide() + + def __showBroke(self): + self.__makeGui() + self.brokeDialog.show() + self.castButton['state'] = DGG.DISABLED + + def __showHowTo(self, message): + self.__makeGui() + self.howToDialog.setMessage(message) + self.howToDialog.show() + + def __hideHowTo(self, event = None): + self.__makeGui() + self.howToDialog.hide() + + def __showFishTankFull(self): + self.__makeGui() + self.__showFailureReason(FishGlobals.OverTankLimit) + self.castButton['state'] = DGG.DISABLED + + def __hideCastGui(self): + target = base.cr.doFind('DistributedTarget') + if target: + target.showGui() + if self.madeGui: + self.timer.hide() + self.castGui.detachNode() + self.itemGui.detachNode() + self.failureDialog.hide() + self.sellFishDialog.hide() + self.sellFishConfirmDialog.hide() + self.brokeDialog.hide() + self.howToDialog.hide() + self.exitButton.hide() + self.castButton.unbind(DGG.B1PRESS) + self.castButton.unbind(DGG.B3PRESS) + self.castButton.unbind(DGG.B1RELEASE) + self.castButton.unbind(DGG.B3RELEASE) + self.ignore(localAvatar.uniqueName('moneyChange')) + self.ignore(localAvatar.uniqueName('fishTankChange')) + + def __itemGuiClose(self): + self.itemGui.detachNode() + + def __makeGui(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: FISHING: ZoneId: %s' % self.pond.getArea()) + if self.madeGui: + return + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.hide() + self.castGui = loader.loadModel('phase_4/models/gui/fishingGui') + self.castGui.setBin("background", 10) + self.castGui.setScale(0.67) + self.castGui.setPos(0, 1, 0) + for nodeName in ('bucket', 'jar', 'display_bucket', 'display_jar'): + self.castGui.find('**/' + nodeName).reparentTo(self.castGui) + self.exitButton = DirectButton(parent=base.a2dBottomRight, relief=None, text=('', TTLocalizer.FishingExit, TTLocalizer.FishingExit), text_align=TextNode.ACenter, text_scale=0.1, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0.0, -0.12), pos=(-0.218, 0, 0.11), scale=0.8, textMayChange=0, image=(self.castGui.find('**/exit_buttonUp'), self.castGui.find('**/exit_buttonDown'), self.castGui.find('**/exit_buttonRollover')), command=self.__userExit) + self.castGui.find('**/exitButton').removeNode() + self.castButton = DirectButton(parent=self.castGui, relief=None, text=TTLocalizer.FishingCast, text_align=TextNode.ACenter, text_scale=(3, 3 * 0.75, 3 * 0.75), text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -4), image=self.castGui.find('**/castButton'), image0_color=(1, 0, 0, 1), image1_color=(0, 1, 0, 1), image2_color=(1, 1, 0, 1), image3_color=(0.8, 0.5, 0.5, 1), pos=(0, -0.05, -0.666), scale=(0.036, 1, 0.048)) + self.castGui.find('**/castButton').removeNode() + self.arrow = self.castGui.find('**/arrow') + self.arrowTip = self.arrow.find('**/arrowTip') + self.arrowTail = self.arrow.find('**/arrowTail') + self.arrow.reparentTo(self.castGui) + self.arrow.setColorScale(0.9, 0.9, 0.1, 0.7) + self.arrow.hide() + self.jar = DirectLabel(parent=self.castGui, relief=None, text=str(self.av.getMoney()), text_scale=0.16, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont(), pos=(-1.12, 0, -1.3)) + self.bucket = DirectLabel(parent=self.castGui, relief=None, text='', text_scale=0.09, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), pos=(1.14, 0, -1.33)) + self.__updateFishTankGui() + self.itemGui = NodePath('itemGui') + self.itemFrame = DirectFrame(parent=self.itemGui, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1, 1, 0.6), text=TTLocalizer.FishingItemFound, text_pos=(0, 0.2), text_scale=0.08, pos=(0, 0, 0.587)) + self.itemLabel = DirectLabel(parent=self.itemFrame, text='', text_scale=0.06, pos=(0, 0, -0.25)) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.itemGuiCloseButton = DirectButton(parent=self.itemFrame, pos=(0.44, 0, -0.24), relief=None, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(0.7, 1, 0.7), command=self.__itemGuiClose) + buttons.removeNode() + jarGui = loader.loadModel('phase_3.5/models/gui/jar_gui') + bootGui = loader.loadModel('phase_4/models/gui/fishing_boot') + packageGui = loader.loadModel('phase_3.5/models/gui/stickerbook_gui').find('**/package') + self.itemJellybean = DirectFrame(parent=self.itemFrame, relief=None, image=jarGui, scale=0.5) + self.itemBoot = DirectFrame(parent=self.itemFrame, relief=None, image=bootGui, scale=0.2) + self.itemPackage = DirectFrame(parent=self.itemFrame, relief=None, image=packageGui, scale=0.25) + self.itemJellybean.hide() + self.itemBoot.hide() + self.itemPackage.hide() + self.failureDialog = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('failureDialog'), doneEvent=self.uniqueName('failureDialog'), command=self.__userExit, message=TTLocalizer.FishingFailure, style=TTDialog.CancelOnly, cancelButtonText=TTLocalizer.FishingExit) + self.failureDialog.hide() + self.sellFishDialog = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('sellFishDialog'), doneEvent=self.uniqueName('sellFishDialog'), command=self.__sellFish, message=TTLocalizer.FishBingoOfferToSellFish, style=TTDialog.YesNo) + self.sellFishDialog.hide() + self.sellFishConfirmDialog = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('sellFishConfirmDialog'), doneEvent=self.uniqueName('sellFishConfirmDialog'), command=self.__sellFishConfirm, message=TTLocalizer.STOREOWNER_TROPHY, style=TTDialog.Acknowledge) + self.sellFishConfirmDialog.hide() + self.brokeDialog = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('brokeDialog'), doneEvent=self.uniqueName('brokeDialog'), command=self.__userExit, message=TTLocalizer.FishingBroke, style=TTDialog.CancelOnly, cancelButtonText=TTLocalizer.FishingExit) + self.brokeDialog.hide() + self.howToDialog = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('howToDialog'), doneEvent=self.uniqueName('howToDialog'), fadeScreen=0, message=TTLocalizer.FishingHowToFailed, style=TTDialog.Acknowledge) + self.howToDialog['command'] = self.__hideHowTo + self.howToDialog.setPos(-0.3, 0, 0.5) + self.howToDialog.hide() + self.madeGui = 1 + return + + def __setBingoCastGui(self): + if self.pond.hasPondBingoManager(): + self.notify.debug('__setBingoCastGui: Has PondBing Manager %s' % self.pond.getPondBingoManager().getDoId()) + bucket = self.castGui.find('**/bucket') + self.castGui.find('**/display_bucket').reparentTo(bucket) + self.bucket.reparentTo(bucket) + jar = self.castGui.find('**/jar') + self.castGui.find('**/display_jar').reparentTo(jar) + self.jar.reparentTo(jar) + base.setCellsAvailable(base.rightCells, 0) + bucket.setScale(0.9) + bucket.setX(-1.9) + bucket.setZ(-.11) + jar.setScale(0.9) + jar.setX(-.375) + jar.setZ(-.135) + else: + self.notify.debug('__setItemFramePos: Has No Pond Bingo Manager') + bucket = self.castGui.find('**/bucket') + bucket.setScale(1) + bucket.setPos(0, 0, 0) + jar = self.castGui.find('**/jar') + jar.setScale(1) + jar.setPos(0, 0, 0) + + def resetCastGui(self): + if not self.castGui: + return + self.notify.debug('resetCastGui: Bingo Night Ends - resetting Gui') + bucket = self.castGui.find('**/bucket') + jar = self.castGui.find('**/jar') + bucketPosInt = bucket.posInterval(5.0, Point3(0, 0, 0), startPos=bucket.getPos(), blendType='easeInOut') + bucketScaleInt = bucket.scaleInterval(5.0, VBase3(1.0, 1.0, 1.0), startScale=bucket.getScale(), blendType='easeInOut') + bucketTrack = Parallel(bucketPosInt, bucketScaleInt) + jarPosInt = jar.posInterval(5.0, Point3(0, 0, 0), startPos=jar.getPos(), blendType='easeInOut') + jarScaleInt = jar.scaleInterval(5.0, VBase3(1.0, 1.0, 1.0), startScale=jar.getScale(), blendType='easeInOut') + jarTrack = Parallel(jarPosInt, jarScaleInt) + self.guiTrack = Parallel(bucketTrack, jarTrack) + self.guiTrack.start() + + def setCastGui(self): + self.notify.debug('setCastGui: Bingo Night Starts - setting Gui') + bucket = self.castGui.find('**/bucket') + self.castGui.find('**/display_bucket').reparentTo(bucket) + self.bucket.reparentTo(bucket) + jar = self.castGui.find('**/jar') + self.castGui.find('**/display_jar').reparentTo(jar) + self.jar.reparentTo(jar) + bucketPosInt = bucket.posInterval(3.0, Point3(-1.9, 0, -.11), startPos=bucket.getPos(), blendType='easeInOut') + bucketScaleInt = bucket.scaleInterval(3.0, VBase3(0.9, 0.9, 0.9), startScale=bucket.getScale(), blendType='easeInOut') + bucketTrack = Parallel(bucketPosInt, bucketScaleInt) + jarPosInt = jar.posInterval(3.0, Point3(-.375, 0, -.135), startPos=jar.getPos(), blendType='easeInOut') + jarScaleInt = jar.scaleInterval(3.0, VBase3(0.9, 0.9, 0.9), startScale=jar.getScale(), blendType='easeInOut') + jarTrack = Parallel(jarPosInt, jarScaleInt) + self.guiTrack = Parallel(bucketTrack, jarTrack) + self.guiTrack.start() + + def setJarAmount(self, amount): + if self.madeGui: + money = int(self.jar['text']) + amount + pocketMoney = min(money, self.av.getMaxMoney()) + self.jar.setProp('text', str(pocketMoney)) + + def __unmakeGui(self): + if not self.madeGui: + return + self.timer.destroy() + del self.timer + self.exitButton.destroy() + self.castButton.destroy() + self.jar.destroy() + self.bucket.destroy() + self.itemFrame.destroy() + self.itemGui.removeNode() + self.failureDialog.cleanup() + self.sellFishDialog.cleanup() + self.sellFishConfirmDialog.cleanup() + self.brokeDialog.cleanup() + self.howToDialog.cleanup() + self.castGui.removeNode() + self.madeGui = 0 + + def localAdjustingCastTask(self, state): + self.getMouse() + deltaX = self.mouseX - self.initMouseX + deltaY = self.mouseY - self.initMouseY + if deltaY >= 0: + if self.power == 0: + self.arrowTail.setScale(0.075, 0.075, 0) + self.arrow.setR(0) + self.castTrack.pause() + return Task.cont + dist = math.sqrt(deltaX * deltaX + deltaY * deltaY) + delta = dist / 0.5 + self.power = max(min(abs(delta), 1.0), 0.0) + self.castTrack.setT(0.2 + self.power * 0.7) + angle = rad2Deg(math.atan(deltaX / deltaY)) + if self.power < 0.25: + angle = angle * math.pow(self.power * 4, 3) + if delta < 0: + angle += 180 + minAngle = -FishGlobals.FishingAngleMax + maxAngle = FishGlobals.FishingAngleMax + if angle < minAngle: + self.arrow.setColorScale(1, 0, 0, 1) + angle = minAngle + elif angle > maxAngle: + self.arrow.setColorScale(1, 0, 0, 1) + angle = maxAngle + else: + self.arrow.setColorScale(1, 1 - math.pow(self.power, 3), 0.1, 0.7) + self.arrowTail.setScale(0.075, 0.075, self.power * 0.2) + self.arrow.setR(angle) + self.angleNP.setH(-angle) + return Task.cont + + def localAdjustingCastTaskIndAxes(self, state): + self.getMouse() + deltaX = self.mouseX - self.initMouseX + deltaY = self.mouseY - self.initMouseY + self.power = max(min(abs(deltaY) * 1.5, 1.0), 0.0) + self.castTrack.setT(0.4 + self.power * 0.5) + angle = deltaX * -180.0 + self.angleNP.setH(self.startAngleNP - angle) + return Task.cont + + def getMouse(self): + if base.mouseWatcherNode.hasMouse(): + self.mouseX = base.mouseWatcherNode.getMouseX() + self.mouseY = base.mouseWatcherNode.getMouseY() + else: + self.mouseX = 0 + self.mouseY = 0 + + def createCastTrack(self): + self.castTrack = Sequence(ActorInterval(self.av, 'castlong', playRate=4), ActorInterval(self.av, 'cast', startFrame=20), Func(self.av.loop, 'fish-neutral')) + + def startMoveBobTask(self): + self.__showBob() + taskMgr.add(self.moveBobTask, self.taskName('moveBobTask')) + + def moveBobTask(self, task): + g = 32.2 + t = task.time + vZero = self.power * self.vZeroMax + angle = deg2Rad(self.power * self.angleMax) + deltaY = vZero * math.cos(angle) * t + deltaZ = vZero * math.sin(angle) * t - g * t * t / 2.0 + deltaPos = Point3(0, deltaY, deltaZ) + self.bobStartPos = Point3(0.0, 3.0, 8.5) + pos = self.bobStartPos + deltaPos + self.bob.setPos(pos) + if pos[2] < self.waterLevel: + self.fsm.request('fishing') + return Task.done + else: + return Task.cont + + def __showBob(self): + self.__hideBob() + self.bob.reparentTo(self.angleNP) + self.bob.setPos(self.ptop, 0, 0, 0) + self.av.update(0) + + def hitTarget(self): + self.fsm.request('waitForAI') + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterWaiting(self, doAnimation = True): + self.av.stopLookAround() + self.__hideLine() + self.track = Parallel() + if doAnimation: + toonTrack = Sequence(Func(self.av.setPlayRate, 1.0, 'run'), Func(self.av.loop, 'run'), LerpPosHprInterval(self.av, 1.0, Point3(0, 0, 0), Point3(0, 0, 0)), Func(self.__placeAvatar), Parallel(ActorInterval(self.av, 'pole'), Func(self.pole.pose, 'cast', 0), LerpScaleInterval(self.pole, duration=0.5, scale=1.0, startScale=0.01)), Func(self.av.loop, 'pole-neutral')) + if self.localToonFishing: + camera.wrtReparentTo(render) + self.track.append(LerpPosHprInterval(nodePath=camera, other=self.av, duration=1.5, pos=Point3(0, -12, 15), hpr=VBase3(0, -38, 0), blendType='easeInOut')) + toonTrack.append(Func(self.__showCastGui)) + toonTrack.append(Func(self.__initCastGui)) + if base.wantBingo: + self.__appendBingoMethod(toonTrack, self.pond.showBingoGui) + self.track.append(toonTrack) + else: + self.__showCastGui() + self.track.start() + + def __appendBingoMethod(self, interval, callback): + interval.append(Func(callback)) + + def exitWaiting(self): + self.track.finish() + self.track = None + return + + def enterLocalAdjusting(self, guiEvent = None): + if self.track: + self.track.pause() + if self.castTrack: + self.castTrack.pause() + self.power = 0.0 + self.firstCast = 0 + self.castButton['image0_color'] = Vec4(0, 1, 0, 1) + self.castButton['text'] = '' + self.av.stopLookAround() + self.__hideLine() + self.__hideBob() + self.howToDialog.hide() + castCost = FishGlobals.getCastCost(self.av.getFishingRod()) + if self.av.getMoney() < castCost: + self.__hideCastGui() + self.__showBroke() + self.av.loop('pole-neutral') + return + if self.av.isFishTankFull(): + self.__hideCastGui() + self.__showFishTankFull() + self.av.loop('pole-neutral') + return + self.arrow.show() + self.arrow.setColorScale(1, 1, 0, 0.7) + self.startAngleNP = self.angleNP.getH() + self.getMouse() + self.initMouseX = self.mouseX + self.initMouseY = self.mouseY + self.__hideBob() + if config.GetBool('fishing-independent-axes', 0): + taskMgr.add(self.localAdjustingCastTaskIndAxes, self.taskName('adjustCastTask')) + else: + taskMgr.add(self.localAdjustingCastTask, self.taskName('adjustCastTask')) + if base.wantBingo: + bingoMgr = self.pond.getPondBingoManager() + if bingoMgr: + bingoMgr.castingStarted() + + def exitLocalAdjusting(self): + taskMgr.remove(self.taskName('adjustCastTask')) + self.castButton['image0_color'] = Vec4(1, 0, 0, 1) + self.castButton['text'] = TTLocalizer.FishingCast + self.arrow.hide() + + def enterLocalCasting(self): + if self.power == 0.0 and len(self.av.fishCollection) == 0: + self.__showHowTo(TTLocalizer.FishingHowToFailed) + if self.castTrack: + self.castTrack.pause() + self.av.loop('pole-neutral') + self.track = None + return + castCost = FishGlobals.getCastCost(self.av.getFishingRod()) + self.jar['text'] = str(max(self.av.getMoney() - castCost, 0)) + if not self.castTrack: + self.createCastTrack() + self.castTrack.pause() + startT = 0.7 + (1 - self.power) * 0.3 + self.castTrack.start(startT) + self.track = Sequence(Wait(1.2 - startT), Func(self.startMoveBobTask), Func(self.__showLineCasting)) + self.track.start() + heading = self.angleNP.getH() + self.d_doCast(self.power, heading) + self.timer.countdown(FishGlobals.CastTimeout) + return + + def exitLocalCasting(self): + taskMgr.remove(self.taskName('moveBobTask')) + if self.track: + self.track.pause() + self.track = None + if self.castTrack: + self.castTrack.pause() + self.__hideLine() + self.__hideBob() + return + + def enterDistCasting(self, power, h): + self.av.stopLookAround() + self.__placeAvatar() + self.__hideLine() + self.__hideBob() + self.angleNP.setH(h) + self.power = power + self.track = Parallel(Sequence(ActorInterval(self.av, 'cast'), Func(self.pole.pose, 'cast', 0), Func(self.av.loop, 'fish-neutral')), Sequence(Wait(1.0), Func(self.startMoveBobTask), Func(self.__showLineCasting))) + self.track.start() + + def exitDistCasting(self): + self.track.finish() + self.track = None + taskMgr.remove(self.taskName('moveBobTask')) + self.__hideLine() + self.__hideBob() + return + + def enterFishing(self): + if self.localToonFishing: + self.track = Sequence(ActorInterval(self.av, 'cast'), Func(self.pole.pose, 'cast', 0), Func(self.av.loop, 'fish-neutral')) + self.track.start(self.castTrack.getT()) + else: + self.track = None + self.av.loop('fish-neutral') + self.__showBobFloat() + self.__showLineWaiting() + if self.localToonFishing: + self.pond.startCheckingTargets(self, self.bob.getPos(render)) + return + + def exitFishing(self): + if self.localToonFishing: + self.pond.stopCheckingTargets() + if self.track: + self.track.finish() + self.track = None + return + + def enterWaitForAI(self): + self.castButton['state'] = DGG.DISABLED + + def exitWaitForAI(self): + self.castButton['state'] = DGG.NORMAL + + def enterReward(self, code, itemDesc1, itemDesc2, itemDesc3): + self.__placeAvatar() + self.bob.reparentTo(self.angleNP) + self.waterLevel = FishingTargetGlobals.getWaterLevel(self.area) + self.bob.setZ(self.waterLevel) + self.__showLineReeling() + self.castTrack.pause() + if self.localToonFishing: + self.__showCastGui() + if code == FishGlobals.QuestItem: + self.__showQuestItem(itemDesc1) + elif code in (FishGlobals.FishItem, FishGlobals.FishItemNewEntry, FishGlobals.FishItemNewRecord): + genus, species, weight = itemDesc1, itemDesc2, itemDesc3 + fish = FishBase.FishBase(genus, species, weight) + self.__showFishItem(code, fish) + if base.wantBingo: + self.pond.handleBingoCatch((genus, species)) + elif code == FishGlobals.BootItem: + self.__showBootItem() + if base.wantBingo: + self.pond.handleBingoCatch(FishGlobals.BingoBoot) + elif code == FishGlobals.JellybeanItem: + amount = itemDesc1 + self.__showJellybeanItem(amount) + elif code == FishGlobals.OverTankLimit: + self.__hideCastGui() + else: + self.__showFailureReason(code) + self.track = Sequence(Parallel(ActorInterval(self.av, 'reel'), ActorInterval(self.pole, 'cast', startFrame=63, endFrame=127)), ActorInterval(self.av, 'reel-neutral'), Func(self.__hideLine), Func(self.__hideBob), ActorInterval(self.av, 'fish-again'), Func(self.av.loop, 'pole-neutral')) + self.track.start() + + def cleanupFishPanel(self): + if self.fishPanel: + self.fishPanel.hide() + self.fishPanel.destroy() + self.fishPanel = None + return + + def hideBootPanel(self): + if self.madeGui and self.itemBoot: + self.__itemGuiClose() + + def exitReward(self): + if self.localToonFishing: + self.itemGui.detachNode() + self.cleanupFishPanel() + self.track.finish() + self.track = None + return + + def enterLeaving(self): + if self.localToonFishing: + self.__hideCastGui() + if base.wantBingo: + self.pond.cleanupBingoMgr() + self.av.stopLookAround() + self.av.startLookAround() + self.__placeAvatar() + self.__hideLine() + self.__hideBob() + self.track = Sequence(Parallel(ActorInterval(self.av, 'fish-end'), Func(self.pole.pose, 'cast', 0), LerpScaleInterval(self.pole, duration=0.5, scale=0.01, startScale=1.0)), Func(self.__dropPole), Func(self.av.loop, 'neutral')) + if self.localToonFishing: + self.track.append(Func(self.fsm.requestFinalState)) + self.track.start() + + def exitLeaving(self): + self.track.pause() + self.track = None + return + + def enterSellFish(self): + self.castButton['state'] = DGG.DISABLED + self.__showSellFishDialog() + self.__hideHowTo() + + def exitSellFish(self): + self.castButton['state'] = DGG.NORMAL + self.__hideSellFishDialog() + self.__hideSellFishConfirmDialog() + + def sellFishComplete(self, trophyResult, numFishCaught): + for button in self.sellFishDialog.buttonList: + button['state'] = DGG.NORMAL + + if self.localToonFishing: + if trophyResult: + self.__hideSellFishDialog() + self.__showSellFishConfirmDialog(numFishCaught) + else: + self.fsm.request('waiting', [False]) + + def __allowSellFish(self): + return base.wantBingo and self.pond.hasPondBingoManager() diff --git a/toontown/safezone/DistributedFishingSpotAI.py b/toontown/safezone/DistributedFishingSpotAI.py new file mode 100755 index 00000000..2fef583a --- /dev/null +++ b/toontown/safezone/DistributedFishingSpotAI.py @@ -0,0 +1,151 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.fishing import FishGlobals +from toontown.fishing.FishBase import FishBase +from direct.task import Task +from toontown.toonbase import ToontownGlobals + + + +class DistributedFishingSpotAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedFishingSpotAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.avId = None + self.pondDoId = None + self.posHpr = [None, None, None, None, None, None] + self.cast = False + self.lastFish = [None, None, None, None] + + def generate(self): + DistributedObjectAI.generate(self) + pond = self.air.doId2do[self.pondDoId] + pond.addSpot(self) + + + def setPondDoId(self, pondDoId): + self.pondDoId = pondDoId + + def getPondDoId(self): + return self.pondDoId + + def setPosHpr(self, x, y, z, h, p, r): + self.posHpr = [x, y, z, h, p, r] + + def getPosHpr(self): + return self.posHpr + + def requestEnter(self): + avId = self.air.getAvatarIdFromSender() + if self.avId != None: + if self.avId == avId: + self.air.writeServerEvent('suspicious', avId, 'Toon requested to enter a pier twice!') + self.sendUpdateToAvatarId(avId, 'rejectEnter', []) + return + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.removeFromPier) + self.b_setOccupied(avId) + self.d_setMovie(FishGlobals.EnterMovie, 0, 0, 0, 0, 0, 0) + taskMgr.remove('cancelAnimation%d' % self.doId) + taskMgr.doMethodLater(2, DistributedFishingSpotAI.cancelAnimation, 'cancelAnimation%d' % self.doId, [self]) + taskMgr.remove('timeOut%d' % self.doId) + taskMgr.doMethodLater(45, DistributedFishingSpotAI.removeFromPierWithAnim, 'timeOut%d' % self.doId, [self]) + self.lastFish = [None, None, None] + self.cast = False + if self.air.doId2do[self.pondDoId].bingoMgr and self.air.doId2do[self.pondDoId].bingoMgr.state != 'Off': + self.air.doId2do[self.pondDoId].bingoMgr.activateBingoForPlayer(avId) + + def rejectEnter(self): + pass + + def requestExit(self): + avId = self.air.getAvatarIdFromSender() + if self.avId != avId: + self.air.writeServerEvent('suspicious', avId, 'Toon requested to exit a pier they\'re not on!') + return + self.ignore(self.air.getAvatarExitEvent(avId)) + self.removeFromPierWithAnim() + + def setOccupied(self, avId): + self.avId = avId + + def d_setOccupied(self, avId): + self.sendUpdate('setOccupied', [avId]) + + def b_setOccupied(self, avId): + self.setOccupied(avId) + self.d_setOccupied(avId) + + def doCast(self, p, h): + avId = self.air.getAvatarIdFromSender() + if self.avId != avId: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to cast from a pier they\'re not on!') + return + av = self.air.doId2do[avId] + money = av.getMoney() + cost = FishGlobals.getCastCost(av.getFishingRod()) + if money < cost: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to cast without enough jellybeans!') + return + if len(av.fishTank) >= av.getMaxFishTank(): + self.air.writeServerEvent('suspicious', avId, 'Toon tried to cast with too many fish!') + return + av.takeMoney(cost, False) + self.d_setMovie(FishGlobals.CastMovie, 0, 0, 0, 0, p, h) + taskMgr.remove('cancelAnimation%d' % self.doId) + taskMgr.doMethodLater(2, DistributedFishingSpotAI.cancelAnimation, 'cancelAnimation%d' % self.doId, [self]) + taskMgr.remove('timeOut%d' % self.doId) + taskMgr.doMethodLater(45, DistributedFishingSpotAI.removeFromPierWithAnim, 'timeOut%d' % self.doId, [self]) + self.cast = True + + def sellFish(self): + avId = self.air.getAvatarIdFromSender() + if self.avId != avId: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to sell fish at a pier they\'re not using!') + return + if self.air.doId2do[self.pondDoId].getArea() != ToontownGlobals.MyEstate: + self.air.writeServerEvent('suspicious', avId, 'Toon tried to sell fish at a pier not in their estate!') + av = self.air.doId2do[avId] + result = self.air.fishManager.creditFishTank(av) + totalFish = len(av.fishCollection) + self.sendUpdateToAvatarId(avId, 'sellFishComplete', [result, totalFish]) + taskMgr.remove('timeOut%d' % self.doId) + taskMgr.doMethodLater(45, DistributedFishingSpotAI.removeFromPierWithAnim, 'timeOut%d' % self.doId, [self]) + + def sellFishComplete(self, todo0, todo1): + pass + + def setMovie(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6): + pass + + def d_setMovie(self, mode, code, genus, species, weight, p, h): + self.sendUpdate('setMovie', [mode, code, genus, species, weight, p, h]) + + def removeFromPier(self): + taskMgr.remove('timeOut%d' % self.doId) + self.cancelAnimation() + self.d_setOccupied(0) + self.avId = None + + def removeFromPierWithAnim(self): + taskMgr.remove('cancelAnimation%d' % self.doId) + self.d_setMovie(FishGlobals.ExitMovie, 0, 0, 0, 0, 0, 0) + taskMgr.doMethodLater(1, DistributedFishingSpotAI.removeFromPier, 'remove%d' % self.doId, [self]) + + def rewardIfValid(self, target): + if not self.cast: + self.air.writeServerEvent('suspicious', self.avId, 'Toon tried to fish without casting!') + return + av = self.air.doId2do[self.avId] + + catch = self.air.fishManager.generateCatch(av, self.air.doId2do[self.pondDoId].getArea()) + + self.lastFish = catch + + self.d_setMovie(FishGlobals.PullInMovie, catch[0], catch[1], catch[2], catch[3], 0, 0) + self.cast = False + + + + def cancelAnimation(self): + self.d_setMovie(FishGlobals.NoMovie, 0, 0, 0, 0, 0, 0) diff --git a/toontown/safezone/DistributedGolfKart.py b/toontown/safezone/DistributedGolfKart.py new file mode 100755 index 00000000..256941f5 --- /dev/null +++ b/toontown/safezone/DistributedGolfKart.py @@ -0,0 +1,481 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from toontown.golf import GolfGlobals +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.distributed import DelayDelete +from direct.task.Task import Task +from direct.showbase import PythonUtil +from toontown.toon import ToonDNA +from toontown.hood import ZoneUtil + +class DistributedGolfKart(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGolfKart') + SeatOffsets = ((0.5, -0.5, 0), + (-0.5, -0.5, 0), + (0.5, 0.5, 0), + (-0.5, 0.5, 0)) + JumpOutOffsets = ((3, 5, 0), + (1.5, 4, 0), + (-1.5, 4, 0), + (-3, 4, 0)) + KART_ENTER_TIME = 400 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.localToonOnBoard = 0 + self.trolleyCountdownTime = base.config.GetFloat('trolley-countdown-time', TROLLEY_COUNTDOWN_TIME) + self.fsm = ClassicFSM.ClassicFSM('DistributedTrolley', [State.State('off', self.enterOff, self.exitOff, ['entering', + 'waitEmpty', + 'waitCountdown', + 'leaving']), + State.State('entering', self.enterEntering, self.exitEntering, ['waitEmpty']), + State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), + State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty', 'leaving']), + State.State('leaving', self.enterLeaving, self.exitLeaving, ['entering'])], 'off', 'off') + self.fsm.enterInitialState() + self.trolleyAwaySfx = base.loadSfx('phase_4/audio/sfx/SZ_trolley_away.ogg') + self.trolleyBellSfx = base.loadSfx('phase_4/audio/sfx/SZ_trolley_bell.ogg') + self.__toonTracks = {} + self.avIds = [0, + 0, + 0, + 0] + self.kartModelPath = 'phase_6/models/golf/golf_cart3.bam' + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.loader = self.cr.playGame.hood.loader + if self.loader: + self.notify.debug('Loader has been loaded') + self.notify.debug(str(self.loader)) + else: + self.notify.debug('Loader has not been loaded') + self.golfKart = render.attachNewNode('golfKartNode') + self.kart = loader.loadModel(self.kartModelPath) + self.kart.setPos(0, 0, 0) + self.kart.setScale(1) + self.kart.reparentTo(self.golfKart) + self.golfKart.reparentTo(self.loader.geom) + self.wheels = self.kart.findAllMatches('**/wheelNode*') + self.numWheels = self.wheels.getNumPaths() + trolleyExitBellInterval = SoundInterval(self.trolleyBellSfx, node=self.golfKart) + trolleyExitAwayInterval = SoundInterval(self.trolleyAwaySfx, node=self.golfKart) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.golfKartSphereNode = self.golfKart.attachNewNode(CollisionNode('golfkart_sphere_%d' % self.getDoId())) + self.golfKartSphereNode.node().addSolid(CollisionSphere(0, 0, 0, 2)) + angle = self.startingHpr[0] + angle -= 90 + radAngle = deg2Rad(angle) + unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0) + unitVec *= 45.0 + self.endPos = self.startingPos + unitVec + dist = Vec3(self.endPos - self.enteringPos).length() + wheelAngle = dist / (4.8 * 1.4 * math.pi) * 360 + self.kartEnterAnimateInterval = Parallel(LerpHprInterval(self.wheels[0], 5.0, Vec3(self.wheels[0].getH(), wheelAngle, self.wheels[0].getR())), LerpHprInterval(self.wheels[1], 5.0, Vec3(self.wheels[1].getH(), wheelAngle, self.wheels[1].getR())), LerpHprInterval(self.wheels[2], 5.0, Vec3(self.wheels[2].getH(), wheelAngle, self.wheels[2].getR())), LerpHprInterval(self.wheels[3], 5.0, Vec3(self.wheels[3].getH(), wheelAngle, self.wheels[3].getR())), name='KartAnimate') + trolleyExitTrack1 = Parallel(LerpPosInterval(self.golfKart, 5.0, self.endPos), self.kartEnterAnimateInterval, name='KartExitTrack') + self.trolleyExitTrack = Sequence(trolleyExitTrack1, Func(self.hideSittingToons)) + self.trolleyEnterTrack = Sequence(LerpPosInterval(self.golfKart, 5.0, self.startingPos, startPos=self.enteringPos)) + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.fsm.request('off') + self.clearToonTracks() + del self.wheels + del self.numWheels + del self.golfKartSphereNode + self.notify.debug('Deleted self loader ' + str(self.getDoId())) + del self.loader + self.golfKart.removeNode() + self.kart.removeNode() + del self.kart + del self.golfKart + self.trolleyEnterTrack.pause() + self.trolleyEnterTrack = None + del self.kartEnterAnimateInterval + del self.trolleyEnterTrack + self.trolleyExitTrack.pause() + self.trolleyExitTrack = None + del self.trolleyExitTrack + return + + def delete(self): + self.notify.debug('Golf kart getting deleted: %s' % self.getDoId()) + del self.trolleyAwaySfx + del self.trolleyBellSfx + DistributedObject.DistributedObject.delete(self) + del self.fsm + + def setState(self, state, timestamp): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def handleEnterTrolleySphere(self, collEntry): + self.notify.debug('Entering Trolley Sphere....') + self.loader.place.detectedTrolleyCollision() + + def handleEnterGolfKartSphere(self, collEntry): + self.notify.debug('Entering Golf Kart Sphere.... %s' % self.getDoId()) + self.loader.place.detectedGolfKartCollision(self) + + def handleEnterTrolley(self): + toon = base.localAvatar + self.sendUpdate('requestBoard', []) + + def handleEnterGolfKart(self): + toon = base.localAvatar + self.sendUpdate('requestBoard', []) + + def fillSlot0(self, avId): + self.fillSlot(0, avId) + + def fillSlot1(self, avId): + self.fillSlot(1, avId) + + def fillSlot2(self, avId): + self.fillSlot(2, avId) + + def fillSlot3(self, avId): + self.fillSlot(3, avId) + + def fillSlot(self, index, avId): + self.avIds[index] = avId + if avId == 0: + pass + else: + if avId == base.localAvatar.getDoId(): + self.loader.place.trolley.fsm.request('boarding', [self.golfKart]) + self.localToonOnBoard = 1 + if avId == base.localAvatar.getDoId(): + self.loader.place.trolley.fsm.request('boarded') + if avId in self.cr.doId2do: + toon = self.cr.doId2do[avId] + toon.stopSmooth() + toon.wrtReparentTo(self.golfKart) + sitStartDuration = toon.getDuration('sit-start') + jumpTrack = self.generateToonJumpTrack(toon, index) + track = Sequence(jumpTrack, Func(toon.setAnimState, 'Sit', 1.0), Func(self.clearToonTrack, avId), name=toon.uniqueName('fillTrolley'), autoPause=1) + track.delayDelete = DelayDelete.DelayDelete(toon, 'GolfKart.fillSlot') + self.storeToonTrack(avId, track) + track.start() + else: + self.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot board the trolley!') + + def emptySlot0(self, avId, timestamp): + self.emptySlot(0, avId, timestamp) + + def emptySlot1(self, avId, timestamp): + self.emptySlot(1, avId, timestamp) + + def emptySlot2(self, avId, timestamp): + self.emptySlot(2, avId, timestamp) + + def emptySlot3(self, avId, timestamp): + self.emptySlot(3, avId, timestamp) + + def notifyToonOffTrolley(self, toon): + toon.setAnimState('neutral', 1.0) + if toon == base.localAvatar: + self.loader.place.trolley.handleOffTrolley() + self.localToonOnBoard = 0 + else: + toon.startSmooth() + + def emptySlot(self, index, avId, timestamp): + if avId == 0: + pass + else: + self.avIds[index] = 0 + if avId in self.cr.doId2do: + toon = self.cr.doId2do[avId] + toon.stopSmooth() + sitStartDuration = toon.getDuration('sit-start') + jumpOutTrack = self.generateToonReverseJumpTrack(toon, index) + track = Sequence(jumpOutTrack, Func(self.notifyToonOffTrolley, toon), Func(self.clearToonTrack, avId), name=toon.uniqueName('emptyTrolley'), autoPause=1) + track.delayDelete = DelayDelete.DelayDelete(toon, 'GolfKart.emptySlot') + self.storeToonTrack(avId, track) + track.start() + if avId == base.localAvatar.getDoId(): + self.loader.place.trolley.fsm.request('exiting') + else: + self.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot exit the trolley!') + + def rejectBoard(self, avId): + self.loader.place.trolley.handleRejectBoard() + + def setMinigameZone(self, zoneId, minigameId): + self.localToonOnBoard = 0 + messenger.send('playMinigame', [zoneId, minigameId]) + + def setGolfZone(self, zoneId, courseId): + self.localToonOnBoard = 0 + messenger.send('playGolf', [zoneId, courseId]) + + def __enableCollisions(self): + self.accept('entertrolley_sphere', self.handleEnterTrolleySphere) + self.accept('enterTrolleyOK', self.handleEnterTrolley) + self.accept('entergolfkart_sphere_%d' % self.getDoId(), self.handleEnterGolfKartSphere) + self.accept('enterGolfKartOK_%d' % self.getDoId(), self.handleEnterGolfKart) + self.golfKartSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + + def __disableCollisions(self): + self.ignore('entertrolley_sphere') + self.ignore('enterTrolleyOK') + self.ignore('entergolfkart_sphere_%d' % self.getDoId()) + self.ignore('enterTrolleyOK_%d' % self.getDoId()) + self.ignore('enterGolfKartOK_%d' % self.getDoId()) + self.golfKartSphereNode.setCollideMask(BitMask32(0)) + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterEntering(self, ts): + self.trolleyEnterTrack.start(ts) + + def exitEntering(self): + self.trolleyEnterTrack.finish() + + def enterWaitEmpty(self, ts): + self.__enableCollisions() + + def exitWaitEmpty(self): + self.__disableCollisions() + + def enterWaitCountdown(self, ts): + self.__enableCollisions() + self.accept('trolleyExitButton', self.handleExitButton) + self.clockNode = TextNode('trolleyClock') + self.clockNode.setFont(ToontownGlobals.getSignFont()) + self.clockNode.setAlign(TextNode.ACenter) + self.clockNode.setTextColor(0.9, 0.1, 0.1, 1) + self.clockNode.setText('10') + self.clock = self.golfKart.attachNewNode(self.clockNode) + self.clock.setBillboardAxis() + self.clock.setPosHprScale(0, -1, 7.0, -0.0, 0.0, 0.0, 2.0, 2.0, 2.0) + if ts < self.trolleyCountdownTime: + self.countdown(self.trolleyCountdownTime - ts) + + def timerTask(self, task): + countdownTime = int(task.duration - task.time) + timeStr = str(countdownTime) + if self.clockNode.getText() != timeStr: + self.clockNode.setText(timeStr) + if task.time >= task.duration: + return Task.done + else: + return Task.cont + + def countdown(self, duration): + countdownTask = Task(self.timerTask) + countdownTask.duration = duration + taskMgr.remove(self.uniqueName('golfKartTimerTask')) + return taskMgr.add(countdownTask, self.uniqueName('golfKartTimerTask')) + + def handleExitButton(self): + self.sendUpdate('requestExit') + + def exitWaitCountdown(self): + self.__disableCollisions() + self.ignore('trolleyExitButton') + taskMgr.remove(self.uniqueName('golfKartTimerTask')) + self.clock.removeNode() + del self.clock + del self.clockNode + + def enterLeaving(self, ts): + self.trolleyExitTrack.start(ts) + if self.localToonOnBoard: + if hasattr(self.loader.place, 'trolley') and self.loader.place.trolley: + self.loader.place.trolley.fsm.request('trolleyLeaving') + + def exitLeaving(self): + self.trolleyExitTrack.finish() + + def getStareAtNodeAndOffset(self): + return (self.golfKart, Point3(0, 0, 4)) + + def storeToonTrack(self, avId, track): + self.clearToonTrack(avId) + self.__toonTracks[avId] = track + + def clearToonTrack(self, avId): + oldTrack = self.__toonTracks.get(avId) + if oldTrack: + oldTrack.pause() + if self.__toonTracks.get(avId): + DelayDelete.cleanupDelayDeletes(self.__toonTracks[avId]) + del self.__toonTracks[avId] + + def clearToonTracks(self): + keyList = [] + for key in self.__toonTracks: + keyList.append(key) + + for key in keyList: + if key in self.__toonTracks: + self.clearToonTrack(key) + + def setGolfCourse(self, golfCourse): + self.golfCourse = golfCourse + + def setPosHpr(self, x, y, z, h, p, r): + self.startingPos = Vec3(x, y, z) + self.enteringPos = Vec3(x, y, z - 10) + self.startingHpr = Vec3(h, 0, 0) + self.golfKart.setPosHpr(x, y, z, h, 0, 0) + + def setColor(self, r, g, b): + kartBody = self.kart.find('**/main_body') + kartBody.setColor(r / 255.0, g / 255.0, b / 255.0, 1) + cartBase = self.kart.find('**/cart_base*') + red = r / 255.0 + green = g / 255.0 + blue = b / 255.0 + if red >= green and red > blue: + s = (red - blue) / float(red) + v = red + if green > blue: + h = (green - blue) / (red - blue) + else: + h = (green - blue) / (red - green) + elif green >= blue: + s = (green - blue) / float(green) + v = green + if red > blue: + h = 2 + (blue - red) / (green - blue) + else: + h = 2 + (blue - red) / (green - red) + else: + if red > green: + s = (blue - green) / blue + h = 4 + (red - green) / (blue - green) + else: + s = (blue - red) / blue + h = 4 + (red - green) / (blue - red) + v = blue + if h < 0: + h *= 60 + h += 360 + h /= 60 + s /= 3 + if s == 0: + red = green = blue = v + else: + i = int(h) + f = h - i + p = v * (1 - s) + q = v * (1 - s * f) + t = v * (1 - s * (1 - f)) + if i == 0: + red = v + green = t + blue = p + elif i == 1: + red = q + green = v + blue = p + elif i == 2: + red = p + green = v + blue = t + elif i == 3: + red = p + green = q + blue = v + elif i == 4: + red = t + green = p + blue = v + elif i == 5: + red = v + green = p + blue = q + cartBase.setColorScale(red, green, blue, 1) + + def generateToonJumpTrack(self, av, seatIndex): + av.pose('sit', 47) + hipOffset = av.getHipsParts()[2].getPos(av) + + def getToonJumpTrack(av, seatIndex): + + def getJumpDest(av = av, node = self.golfKart): + dest = Point3(0, 0, 0) + if hasattr(self, 'golfKart') and self.golfKart: + dest = Vec3(self.golfKart.getPos(av.getParent())) + seatNode = self.golfKart.find('**/seat' + str(seatIndex + 1)) + dest += seatNode.getPos(self.golfKart) + dna = av.getStyle() + dest -= hipOffset + if seatIndex < 2: + dest.setY(dest.getY() + 2 * hipOffset.getY()) + dest.setZ(dest.getZ() + 0.1) + else: + self.notify.warning('getJumpDestinvalid golfKart, returning (0,0,0)') + return dest + + def getJumpHpr(av = av, node = self.golfKart): + hpr = Point3(0, 0, 0) + if hasattr(self, 'golfKart') and self.golfKart: + hpr = self.golfKart.getHpr(av.getParent()) + if seatIndex < 2: + hpr.setX(hpr.getX() + 180) + else: + hpr.setX(hpr.getX()) + angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) + hpr.setX(angle) + else: + self.notify.warning('getJumpHpr invalid golfKart, returning (0,0,0)') + return hpr + + toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.43), Parallel(LerpHprInterval(av, hpr=getJumpHpr, duration=0.9), ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) + return toonJumpTrack + + def getToonSitTrack(av): + toonSitTrack = Sequence(ActorInterval(av, 'sit-start'), Func(av.loop, 'sit')) + return toonSitTrack + + toonJumpTrack = getToonJumpTrack(av, seatIndex) + toonSitTrack = getToonSitTrack(av) + jumpTrack = Sequence(Parallel(toonJumpTrack, Sequence(Wait(1), toonSitTrack)), Func(av.wrtReparentTo, self.golfKart)) + return jumpTrack + + def generateToonReverseJumpTrack(self, av, seatIndex): + self.notify.debug('av.getH() = %s' % av.getH()) + + def getToonJumpTrack(av, destNode): + + def getJumpDest(av = av, node = destNode): + dest = node.getPos(av.getParent()) + dest += Vec3(*self.JumpOutOffsets[seatIndex]) + return dest + + def getJumpHpr(av = av, node = destNode): + hpr = node.getHpr(av.getParent()) + hpr.setX(hpr.getX() + 180) + angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) + hpr.setX(angle) + return hpr + + toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.1), Parallel(ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) + return toonJumpTrack + + toonJumpTrack = getToonJumpTrack(av, self.golfKart) + jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render)) + return jumpTrack + + def hideSittingToons(self): + for avId in self.avIds: + if avId: + av = base.cr.doId2do.get(avId) + if av: + av.hide() diff --git a/toontown/safezone/DistributedGolfKartAI.py b/toontown/safezone/DistributedGolfKartAI.py new file mode 100755 index 00000000..7627069f --- /dev/null +++ b/toontown/safezone/DistributedGolfKartAI.py @@ -0,0 +1,320 @@ +import random + +from TrolleyConstants import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from otp.ai.AIBase import * +from toontown.golf import GolfGlobals +from toontown.golf import GolfManagerAI +from toontown.toonbase.ToontownGlobals import * + + +class DistributedGolfKartAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGolfKartAI') + + def __init__(self, air, golfCourse, x, y, z, h, p, r): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.seats = [None, None, None, None] + self.golfCourse = golfCourse + self.posHpr = (x, y, z, h, p, r) + self.chooseColor() + self.accepting = 0 + self.trolleyCountdownTime = simbase.config.GetFloat( + 'trolley-countdown-time', TROLLEY_COUNTDOWN_TIME) + self.fsm = ClassicFSM.ClassicFSM( + 'DistributedGolfKartAI', + [ + State.State('off', self.enterOff, self.exitOff, ['entering']), + State.State('entering', self.enterEntering, self.exitEntering, ['waitEmpty']), + State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), + State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty', 'allAboard']), + State.State('allAboard', self.enterAllAboard, self.exitAllAboard, ['leaving', 'waitEmpty']), + State.State('leaving', self.enterLeaving, self.exitLeaving, ['entering']) + ], 'off', 'off') + self.fsm.enterInitialState() + + def delete(self): + self.fsm.requestFinalState() + del self.fsm + DistributedObjectAI.DistributedObjectAI.delete(self) + + def findAvailableSeat(self): + for i in xrange(len(self.seats)): + if self.seats[i] is None: + return i + + def findAvatar(self, avId): + for i in xrange(len(self.seats)): + if self.seats[i] == avId: + return i + + def countFullSeats(self): + avCounter = 0 + for i in self.seats: + if i: + avCounter += 1 + return avCounter + + def rejectingBoardersHandler(self, avId): + self.rejectBoarder(avId) + + def rejectBoarder(self, avId): + self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId]) + + def acceptingBoardersHandler(self, avId): + self.notify.debug('acceptingBoardersHandler') + seatIndex = self.findAvailableSeat() + if seatIndex is None: + self.rejectBoarder(avId) + else: + self.acceptBoarder(avId, seatIndex) + + def acceptBoarder(self, avId, seatIndex): + self.notify.debug('acceptBoarder') + if self.findAvatar(avId) is not None: + return + self.seats[seatIndex] = avId + self.acceptOnce( + self.air.getAvatarExitEvent(avId), + self.__handleUnexpectedExit, extraArgs=[avId]) + self.timeOfBoarding = globalClock.getRealTime() + self.sendUpdate('fillSlot' + str(seatIndex), [avId]) + self.waitCountdown() + + def __handleUnexpectedExit(self, avId): + self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly') + seatIndex = self.findAvatar(avId) + if seatIndex is None: + return + self.clearFullNow(seatIndex) + self.clearEmptyNow(seatIndex) + if self.countFullSeats() == 0: + self.waitEmpty() + + def rejectingExitersHandler(self, avId): + self.rejectExiter(avId) + + def rejectExiter(self, avId): + pass + + def acceptingExitersHandler(self, avId): + self.acceptExiter(avId) + + def acceptExiter(self, avId): + seatIndex = self.findAvatar(avId) + if seatIndex is None: + return + self.clearFullNow(seatIndex) + self.sendUpdate('emptySlot' + str(seatIndex), [avId, globalClockDelta.getRealNetworkTime()]) + if self.countFullSeats() == 0: + self.waitEmpty() + taskMgr.doMethodLater( + TOON_EXIT_TIME, self.clearEmptyNow, + self.uniqueName('clearEmpty-%s' % seatIndex), + extraArgs=(seatIndex,)) + + def clearEmptyNow(self, seatIndex): + self.sendUpdate('emptySlot' + str(seatIndex), [0, globalClockDelta.getRealNetworkTime()]) + + def clearFullNow(self, seatIndex): + avId = self.seats[seatIndex] + if avId == 0: + self.notify.warning('Clearing an empty seat index: ' + str(seatIndex) + ' ... Strange...') + else: + self.seats[seatIndex] = None + self.sendUpdate('fillSlot' + str(seatIndex), [0]) + if avId: + self.ignore(self.air.getAvatarExitEvent(avId)) + + def d_setState(self, state): + self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()]) + + def getState(self): + return self.fsm.getCurrentState().getName() + + def requestBoard(self, *args): + self.notify.debug('requestBoard') + avId = self.air.getAvatarIdFromSender() + if self.findAvatar(avId) != None: + self.notify.warning('Ignoring multiple requests from %s to board.' % avId) + return None + av = self.air.doId2do.get(avId) + if av: + newArgs = (avId,) + args + if av.hp > 0 and self.accepting: + self.acceptingBoardersHandler(*newArgs) + else: + self.rejectingBoardersHandler(*newArgs) + else: + self.notify.warning('avid: %s does not exist, but tried to board a trolley' % avId) + + def requestExit(self, *args): + self.notify.debug('requestExit') + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av: + newArgs = (avId,) + args + if self.accepting: + self.acceptingExitersHandler(*newArgs) + else: + self.rejectingExitersHandler(*newArgs) + else: + self.notify.warning('avId: %s does not exist, but tried to exit a trolley' % avId) + + def start(self): + self.enter() + + def enterOff(self): + self.accepting = 0 + if hasattr(self, 'doId'): + for seatIndex in xrange(4): + taskMgr.remove(self.uniqueName('clearEmpty-' + str(seatIndex))) + + def exitOff(self): + self.accepting = 0 + + def enter(self): + self.fsm.request('entering') + + def enterEntering(self): + self.d_setState('entering') + self.accepting = 0 + self.seats = [None, None, None, None] + taskMgr.doMethodLater( + TROLLEY_ENTER_TIME, self.waitEmptyTask, + self.uniqueName('entering-timer')) + + def exitEntering(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('entering-timer')) + + def waitEmptyTask(self, task): + self.waitEmpty() + return Task.done + + def waitEmpty(self): + self.fsm.request('waitEmpty') + + def enterWaitEmpty(self): + self.d_setState('waitEmpty') + self.accepting = 1 + + def exitWaitEmpty(self): + self.accepting = 0 + + def waitCountdown(self): + self.fsm.request('waitCountdown') + + def enterWaitCountdown(self): + self.d_setState('waitCountdown') + self.accepting = 1 + taskMgr.doMethodLater( + self.trolleyCountdownTime, self.timeToGoTask, + self.uniqueName('countdown-timer')) + + def timeToGoTask(self, task): + if self.countFullSeats() > 0: + self.allAboard() + else: + self.waitEmpty() + return Task.done + + def exitWaitCountdown(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('countdown-timer')) + + def allAboard(self): + self.fsm.request('allAboard') + + def enterAllAboard(self): + self.accepting = 0 + currentTime = globalClock.getRealTime() + elapsedTime = currentTime - self.timeOfBoarding + self.notify.debug('elapsed time: ' + str(elapsedTime)) + waitTime = max(TOON_BOARD_TIME - elapsedTime, 0) + taskMgr.doMethodLater( + waitTime, self.leaveTask, self.uniqueName('waitForAllAboard')) + + def exitAllAboard(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('waitForAllAboard')) + + def leaveTask(self, task): + if self.countFullSeats() > 0: + self.leave() + else: + self.waitEmpty() + return Task.done + + def leave(self): + self.fsm.request('leaving') + + def enterLeaving(self): + self.d_setState('leaving') + self.accepting = 0 + taskMgr.doMethodLater( + TROLLEY_EXIT_TIME, self.trolleyLeftTask, + self.uniqueName('leaving-timer')) + + def trolleyLeftTask(self, task): + self.trolleyLeft() + return Task.done + + def trolleyLeft(self): + numPlayers = self.countFullSeats() + avIdList = [] + if numPlayers > 0: + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + avIdList.append(avId) + self.clearFullNow(seatIndex) + golfManager = GolfManagerAI.GolfManagerAI() + golfZone = golfManager.readyGolfCourse(avIdList, self.golfCourse) + for avId in avIdList: + if avId: + self.sendUpdateToAvatarId(avId, 'setGolfZone', [golfZone, 0]) + else: + self.notify.warning('The trolley left, but was empty.') + self.enter() + + def exitLeaving(self): + self.accepting = 0 + self.chooseColor() + self.sendUpdate('setColor', [self.color[0], self.color[1], self.color[2]]) + taskMgr.remove(self.uniqueName('leaving-timer')) + + def getGolfCourse(self): + return self.golfCourse + + def getPosHpr(self): + return self.posHpr + + def getColor(self): + return self.color + + def chooseColor(self): + self.color = ( + random.randint( + GolfGlobals.KartColors[self.golfCourse][0][0], + GolfGlobals.KartColors[self.golfCourse][0][1] + ), + random.randint( + GolfGlobals.KartColors[self.golfCourse][1][0], + GolfGlobals.KartColors[self.golfCourse][1][1] + ), + random.randint( + GolfGlobals.KartColors[self.golfCourse][2][0], + GolfGlobals.KartColors[self.golfCourse][2][1] + ) + ) + if self.golfCourse == 1: + if self.color[0] + self.color[1] <= 255: + self.color = ( + self.color[0], + self.color[0] + self.color[1], + self.color[2] + ) + else: + self.color = (self.color[0], 255, self.color[2]) diff --git a/toontown/safezone/DistributedMMPiano.py b/toontown/safezone/DistributedMMPiano.py new file mode 100755 index 00000000..1d76e156 --- /dev/null +++ b/toontown/safezone/DistributedMMPiano.py @@ -0,0 +1,118 @@ +import random +from panda3d.core import * +from direct.task.Task import Task +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.distributed import DistributedObject +from pandac.PandaModules import NodePath +from toontown.toonbase import ToontownGlobals +ChangeDirectionDebounce = 1.0 +ChangeDirectionTime = 1.0 + +class DistributedMMPiano(DistributedObject.DistributedObject): + whitePartNodeName = 'midkey_floor_1' + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.spinStartTime = 0.0 + self.rpm = 0.0 + self.degreesPerSecond = self.rpm / 60.0 * 360.0 + self.offset = 0.0 + self.speedUpSound = None + self.changeDirectionSound = None + self.lastChangeDirection = 0.0 + + def generate(self): + DistributedObject.DistributedObject.generate(self) + taskMgr.doMethodLater(4, self.setupGeom, self.uniqueName('setup-geom')) + + def setupGeom(self, task): + geom = self.cr.playGame.getPlace().loader.geom + self.piano = geom.find('**/center_icon') + if self.piano.isEmpty(): + loader.notify.error('Piano not found') + return + + geom.find('**/center_icon').setPos(0,-20.1,0) + geom.find('**/midkey_floor').setPos(0,20.1,0) + geom.find('**/pond_floor').setPos(0,20.1,0) + geom.find('**/pond_floor').setScale(1.01,1.01,1) + geom.find('**/MMsz_water').setPos(0,20.0,0) + geom.find('**/midkey_floor').setScale(1.01,1.01,1) + geom.find('**/midkey_floor_1').setScale(1.01,1.01,1) + base.cr.parentMgr.registerParent(ToontownGlobals.SPMinniesPiano, self.piano) + self.accept('enter' + self.whitePartNodeName, self.__handleOnFloor) + self.accept('exit' + self.whitePartNodeName, self.__handleOffFloor) + self.accept('entermid_fishpond', self.__handleChangeDirectionButton) + self.speedUpSound = base.loadSfx('phase_6/audio/sfx/SZ_MM_gliss.ogg') + self.changeDirectionSound = base.loadSfx('phase_6/audio/sfx/SZ_MM_cymbal.ogg') + self.__setupSpin() + return task.done + + def __setupSpin(self): + taskMgr.add(self.__updateSpin, self.taskName('pianoSpinTask')) + + def __stopSpin(self): + taskMgr.remove(self.taskName('pianoSpinTask')) + + def __updateSpin(self, task): + if self.degreesPerSecond == 0: + return Task.cont + + elapsed = globalClock.getRealTime() - self.spinStartTime + offset = self.offset + heading = ((self.degreesPerSecond * elapsed) + offset) % 360 + self.piano.setH(heading) + return Task.cont + + def disable(self): + if hasattr(self, 'piano'): + del self.piano + base.cr.parentMgr.unregisterParent(ToontownGlobals.SPMinniesPiano) + self.ignoreAll() + self.speedUpSound = None + self.changeDirectionSound = None + self.__stopSpin() + DistributedObject.DistributedObject.disable(self) + + def setSpeed(self, rpm, offset, timestamp): + timestamp = globalClockDelta.networkToLocalTime(timestamp) + degreesPerSecond = rpm / 60.0 * 360.0 + self.rpm = rpm + self.degreesPerSecond = degreesPerSecond + self.offset = offset + self.spinStartTime = timestamp + + def playSpeedUp(self, avId): + if avId != base.localAvatar.doId: + base.playSfx(self.speedUpSound) + + def playChangeDirection(self, avId): + if avId != base.localAvatar.doId: + base.playSfx(self.changeDirectionSound) + + def __handleOnFloor(self, collEntry): + self.cr.playGame.getPlace().activityFsm.request('OnPiano') + self.sendUpdate('requestSpeedUp', []) + base.playSfx(self.speedUpSound) + + def __handleOffFloor(self, collEntry): + self.cr.playGame.getPlace().activityFsm.request('off') + self.sendUpdate('requestSlowDown', []) + + def __handleSpeedUpButton(self, collEntry): + self.sendUpdate('requestSpeedUp', []) + base.playSfx(self.speedUpSound) + + def __handleChangeDirectionButton(self, collEntry): + now = globalClock.getFrameTime() + if now - self.lastChangeDirection < ChangeDirectionDebounce: + loader.notify.debug('Rejecting change direction.') + return + shouldChange = random.randint(1,10) + if int(shouldChange) == 10: + self.lastChangeDirection = now + self.sendUpdate('requestChangeDirection', []) + base.playSfx(self.changeDirectionSound) + else: + loader.notify.debug('Rejecting change direction.') diff --git a/toontown/safezone/DistributedMMPianoAI.py b/toontown/safezone/DistributedMMPianoAI.py new file mode 100755 index 00000000..eebc2ec3 --- /dev/null +++ b/toontown/safezone/DistributedMMPianoAI.py @@ -0,0 +1,73 @@ +from otp.ai.AIBase import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObjectAI +from direct.task import Task +PianoSpeeds = [2, 4, 6, 8, 10, 12, 15, 20] +PianoMaxSpeed = PianoSpeeds[-1] +PianoSlowDownFactor = 0.7 +PianoSlowDownInterval = 6.0 +PianoSlowDownMinimum = 0.1 + +class DistributedMMPianoAI(DistributedObjectAI.DistributedObjectAI): + + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.spinStartTime = 0.0 + self.rpm = 0.0 + self.degreesPerSecond = (self.rpm / 60.0) * 360.0 + self.offset = 0.0 + self.direction = 1 + + def requestSpeedUp(self): + if self.rpm < PianoMaxSpeed: + for speed in PianoSpeeds: + if speed > self.rpm: + break + + self.updateSpeed(speed, self.direction) + + self.d_playSpeedUp(self.air.getAvatarIdFromSender()) + self.__slowDownLater() + + def requestChangeDirection(self): + rpm = self.rpm + if rpm == 0.0: + rpm = PianoSpeeds[0] + + self.updateSpeed(rpm, -(self.direction)) + self.__slowDownLater() + self.d_playChangeDirection(self.air.getAvatarIdFromSender()) + + def d_setSpeed(self, rpm, offset, startTime): + self.sendUpdate('setSpeed', [rpm, offset, globalClockDelta.localToNetworkTime(startTime)]) + + def d_playSpeedUp(self, avId): + self.sendUpdate('playSpeedUp', [avId]) + + def d_playChangeDirection(self, avId): + self.sendUpdate('playChangeDirection', [avId]) + + def updateSpeed(self, rpm, direction): + now = globalClock.getFrameTime() + heading = self.degreesPerSecond * (now - self.spinStartTime) + self.offset + self.rpm = rpm + self.direction = direction + self.degreesPerSecond = (rpm / 60.0) * 360.0 * direction + self.offset = heading % 360.0 + self.spinStartTime = now + self.d_setSpeed(self.rpm * self.direction, self.offset, self.spinStartTime) + + def __slowDownLater(self): + taskName = self.uniqueName('slowDown') + taskMgr.remove(taskName) + taskMgr.doMethodLater(PianoSlowDownInterval, self.__slowDown, taskName) + + def __slowDown(self, task): + rpm = self.rpm * PianoSlowDownFactor + if rpm < PianoSlowDownMinimum: + self.updateSpeed(0.0, self.direction) + else: + self.updateSpeed(rpm, self.direction) + self.__slowDownLater() + return Task.done diff --git a/toontown/safezone/DistributedPartyGate.py b/toontown/safezone/DistributedPartyGate.py new file mode 100755 index 00000000..ca4a0069 --- /dev/null +++ b/toontown/safezone/DistributedPartyGate.py @@ -0,0 +1,207 @@ +from pandac.PandaModules import Point3, CollisionSphere, CollisionNode, BitMask32, Vec3, NodePath, TextNode, Vec4 +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPLocalizer +from direct.interval.IntervalGlobal import Sequence, Parallel, SoundInterval +from direct.interval.FunctionInterval import Wait +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.gui import DirectLabel +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.parties.ServerTimeGui import ServerTimeGui +from toontown.parties.PublicPartyGui import PublicPartyGui +from toontown.parties import PartyGlobals + +class DistributedPartyGate(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyGate') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.publicPartyChooseGuiDoneEvent = 'doneChoosingPublicParty' + self.publicPartyGui = PublicPartyGui(self.publicPartyChooseGuiDoneEvent) + self.publicPartyGui.stash() + self.loadClockSounds() + self.hourSoundInterval = Sequence() + self.accept('stoppedAsleep', self.handleSleep) + + def loadClockSounds(self): + self.clockSounds = [] + for i in xrange(1, 13): + if i < 10: + si = '0%d' % i + else: + si = '%d' % i + self.clockSounds.append(base.loadSfx('phase_4/audio/sfx/clock%s.ogg' % si)) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + loader = self.cr.playGame.hood.loader + partyGate = loader.geom.find('**/partyGate_grp') + if partyGate.isEmpty(): + self.notify.warning('Could not find partyGate_grp in loader.geom') + return + self.clockFlat = partyGate.find('**/clock_flat') + collSphere = CollisionSphere(0, 0, 0, 6.9) + collSphere.setTangible(1) + self.partyGateSphere = CollisionNode('PartyGateSphere') + self.partyGateSphere.addSolid(collSphere) + self.partyGateCollNodePath = partyGate.find('**/partyGate_stepsLocator').attachNewNode(self.partyGateSphere) + self.__enableCollisions() + self.toontownTimeGui = ServerTimeGui(partyGate, hourCallback=self.hourChange) + self.toontownTimeGui.setPos(partyGate.find('**/clockText_locator').getPos() + Point3(0.0, 0.0, -0.2)) + self.toontownTimeGui.setHpr(partyGate.find('**/clockText_locator').getHpr()) + self.toontownTimeGui.setScale(12.0, 1.0, 26.0) + self.toontownTimeGui.amLabel.setPos(-0.035, 0, -0.032) + self.toontownTimeGui.amLabel.setScale(0.5) + self.toontownTimeGui.updateTime() + self.setupSignText() + + def setupSignText(self): + loader = self.cr.playGame.hood.loader + partyGate = loader.geom.find('**/partyGateSignGroup') + if partyGate.isEmpty(): + self.notify.warning('Could not find partyGate_grp in loader.geom') + return + gateFont = ToontownGlobals.getMinnieFont() + leftSign = partyGate.find('**/signTextL_locatorBack') + signScale = 0.35 + wordWrap = 8 + leftText = DirectLabel.DirectLabel(parent=leftSign, pos=(0, 0.0, 0.0), relief=None, text=TTLocalizer.PartyGateLeftSign, text_align=TextNode.ACenter, text_font=gateFont, text_wordwrap=wordWrap, text_fg=Vec4(0.7, 0.3, 0.3, 1.0), scale=signScale) + rightSign = partyGate.find('**/signTextR_locatorFront') + rightText = DirectLabel.DirectLabel(parent=rightSign, pos=(0, 0.0, 0.0), relief=None, text=TTLocalizer.PartyGateRightSign, text_align=TextNode.ACenter, text_font=gateFont, text_wordwrap=wordWrap, text_fg=Vec4(0.7, 0.3, 0.3, 1.0), scale=signScale) + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + if self.zoneId in ToontownGlobals.dnaMap: + playground = ToontownGlobals.dnaMap[self.zoneId] + else: + playground = ToontownGlobals.dnaMap[2000] + self.toontownTimeGui.hourLabel['text_fg'] = PartyGlobals.PlayGroundToPartyClockColors[playground] + self.toontownTimeGui.colonLabel['text_fg'] = PartyGlobals.PlayGroundToPartyClockColors[playground] + self.toontownTimeGui.minutesLabel['text_fg'] = PartyGlobals.PlayGroundToPartyClockColors[playground] + self.toontownTimeGui.amLabel['text_fg'] = PartyGlobals.PlayGroundToPartyClockColors[playground] + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.__disableCollisions() + self.toontownTimeGui.ival.finish() + self.hourSoundInterval.finish() + if self.publicPartyGui: + self.publicPartyGui.stash() + self.publicPartyGui.destroy() + self.publicPartyGui = None + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.toontownTimeGui.destroy() + del self.toontownTimeGui + self.hourSoundInterval.finish() + del self.hourSoundInterval + del self.clockFlat + if self.publicPartyGui: + self.publicPartyGui.destroy() + del self.publicPartyGui + self.partyGateCollNodePath.removeNode() + del self.partyGateCollNodePath + self.ignoreAll() + + def showMessage(self, message): + self.messageDoneEvent = self.uniqueName('messageDoneEvent') + self.acceptOnce(self.messageDoneEvent, self.__handleMessageDone) + self.messageGui = TTDialog.TTGlobalDialog(doneEvent=self.messageDoneEvent, message=message, style=TTDialog.Acknowledge) + + def __handleMessageDone(self): + self.ignore(self.messageDoneEvent) + self.freeAvatar() + self.messageGui.cleanup() + self.messageGui = None + return + + def __handleAskDone(self): + DistributedPartyGate.notify.debug('__handleAskDone') + self.ignore(self.publicPartyChooseGuiDoneEvent) + doneStatus = self.publicPartyGui.doneStatus + self.publicPartyGui.stash() + if doneStatus is None: + self.freeAvatar() + return + self.sendUpdate('partyChoiceRequest', [base.localAvatar.doId, doneStatus[0], doneStatus[1]]) + return + + def partyRequestDenied(self, reason): + DistributedPartyGate.notify.debug('partyRequestDenied( reason=%s )' % PartyGlobals.PartyGateDenialReasons.getString(reason)) + if reason == PartyGlobals.PartyGateDenialReasons.Unavailable: + self.showMessage(TTLocalizer.PartyGatePartyUnavailable) + elif reason == PartyGlobals.PartyGateDenialReasons.Full: + self.showMessage(TTLocalizer.PartyGatePartyFull) + + def setParty(self, partyInfoTuple, hostId): + DistributedPartyGate.notify.debug('setParty') + self.freeAvatar() + if partyInfoTuple[0] == 0: + DistributedPartyGate.notify.debug('Public Party closed before toon could get to it.') + return + shardId, zoneId, numberOfGuests, hostName, activityIds, lane = partyInfoTuple + #For some reason, the party gate is attempting to teleport to the host of a party rather than a random spot in the party. + #This temporarily fixes a hang on the loading screen + if base.localAvatar.defaultShard != shardId: + shardId = None + base.cr.playGame.getPlace().requestLeave({'loader': 'safeZoneLoader', + 'where': 'party', + 'how': 'teleportIn', + 'hoodId': ToontownGlobals.PartyHood, + 'zoneId': zoneId, + 'shardId': None, + 'avId': hostId}) + return + + def freeAvatar(self): + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + + def hourChange(self, currentHour): + currentHour = currentHour % 12 + if currentHour == 0: + currentHour = 12 + self.hourSoundInterval = Parallel() + seq1 = Sequence() + for i in xrange(currentHour): + seq1.append(SoundInterval(self.clockSounds[i])) + seq1.append(Wait(0.2)) + + timeForEachDeformation = seq1.getDuration() / currentHour + seq2 = Sequence() + for i in xrange(currentHour): + seq2.append(self.clockFlat.scaleInterval(timeForEachDeformation / 2.0, Vec3(0.9, 1.0, 1.2), blendType='easeInOut')) + seq2.append(self.clockFlat.scaleInterval(timeForEachDeformation / 2.0, Vec3(1.2, 1.0, 0.9), blendType='easeInOut')) + + seq2.append(self.clockFlat.scaleInterval(timeForEachDeformation / 2.0, Vec3(1.0, 1.0, 1.0), blendType='easeInOut')) + self.hourSoundInterval.append(seq1) + self.hourSoundInterval.append(seq2) + self.hourSoundInterval.start() + + def handleEnterGateSphere(self, collEntry): + self.notify.debug('Entering steps Sphere....') + base.cr.playGame.getPlace().fsm.request('stopped') + self.sendUpdate('getPartyList', [base.localAvatar.doId]) + + def listAllPublicParties(self, publicPartyInfo): + self.notify.debug('listAllPublicParties : publicPartyInfo = %s' % publicPartyInfo) + self.acceptOnce(self.publicPartyChooseGuiDoneEvent, self.__handleAskDone) + self.publicPartyGui.refresh(publicPartyInfo) + self.publicPartyGui.unstash() + + def __enableCollisions(self): + self.accept('enterPartyGateSphere', self.handleEnterGateSphere) + self.partyGateSphere.setCollideMask(OTPGlobals.WallBitmask) + + def __disableCollisions(self): + self.ignore('enterPartyGateSphere') + self.partyGateSphere.setCollideMask(BitMask32(0)) + + def handleSleep(self): + if hasattr(self, 'messageGui') and self.messageGui: + self.__handleMessageDone() diff --git a/toontown/safezone/DistributedPartyGateAI.py b/toontown/safezone/DistributedPartyGateAI.py new file mode 100755 index 00000000..4faf9e1a --- /dev/null +++ b/toontown/safezone/DistributedPartyGateAI.py @@ -0,0 +1,36 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from toontown.parties import PartyGlobals + +class DistributedPartyGateAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyGateAI") + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + self.area = None + + def setArea(self, area): + self.area = area + + def getArea(self): + return self.area + + def getPartyList(self, avId): + partyManager = simbase.air.partyManager + self.sendUpdateToAvatarId(avId, 'listAllPublicParties', [partyManager.getPublicParties()]) + + def partyChoiceRequest(self, avId, shardId, zoneId): + # Try to get a spot for them in the party + # find partyId + party = None + pid = 0 + for partyId in self.air.partyManager.pubPartyInfo: + p = self.air.partyManager.pubPartyInfo[partyId] + if p.get('shardId', 0) == shardId and p.get('zoneId', 0) == zoneId: + party = p + pid = partyId + break + if not party: + self.sendUpdateToAvatarId(self.air.getAvatarIdFromSender(), 'partyRequestDenied', [PartyGlobals.PartyGateDenialReasons.Unavailable]) + return #dafuq + self.air.globalPartyMgr.d_requestPartySlot(pid, self.air.getAvatarIdFromSender(), self.doId) diff --git a/toontown/safezone/DistributedPicnicBasket.py b/toontown/safezone/DistributedPicnicBasket.py new file mode 100755 index 00000000..931811a2 --- /dev/null +++ b/toontown/safezone/DistributedPicnicBasket.py @@ -0,0 +1,572 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from toontown.golf import GolfGlobals +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.distributed import DelayDelete +from toontown.toonbase.ToontownTimer import ToontownTimer +from direct.task.Task import Task +from direct.showbase import PythonUtil +from toontown.toon import ToonDNA +from direct.showbase import RandomNumGen +from toontown.battle.BattleSounds import * + +class DistributedPicnicBasket(DistributedObject.DistributedObject): + seatState = Enum('Empty, Full, Eating') + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPicnicBasket') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.localToonOnBoard = 0 + self.seed = 0 + self.random = None + self.picnicCountdownTime = base.config.GetFloat('picnic-countdown-time', ToontownGlobals.PICNIC_COUNTDOWN_TIME) + self.picnicBasketTrack = None + self.fsm = ClassicFSM.ClassicFSM('DistributedTrolley', [State.State('off', self.enterOff, self.exitOff, ['waitEmpty', 'waitCountdown']), State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty'])], 'off', 'off') + self.fsm.enterInitialState() + self.__toonTracks = {} + return + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.loader = self.cr.playGame.hood.loader + self.foodLoader = ['phase_6/models/golf/picnic_sandwich.bam', + 'phase_6/models/golf/picnic_apple.bam', + 'phase_6/models/golf/picnic_cupcake.bam', + 'phase_6/models/golf/picnic_chocolate_cake.bam'] + self.fullSeat = [] + self.food = [] + for i in xrange(4): + self.food.append(None) + self.fullSeat.append(self.seatState.Empty) + + self.picnicItem = 0 + return + + def announceGenerate(self): + self.picnicTable = self.loader.geom.find('**/*picnic_table_' + str(self.tableNumber)) + self.picnicTableSphereNodes = [] + self.numSeats = 4 + self.seats = [] + self.jumpOffsets = [] + self.basket = None + for i in xrange(self.numSeats): + self.seats.append(self.picnicTable.find('**/*seat%d' % (i + 1))) + self.jumpOffsets.append(self.picnicTable.find('**/*jumpOut%d' % (i + 1))) + + self.tablecloth = self.picnicTable.find('**/basket_locator') + DistributedObject.DistributedObject.announceGenerate(self) + for i in xrange(self.numSeats): + self.picnicTableSphereNodes.append(self.seats[i].attachNewNode(CollisionNode('picnicTable_sphere_%d_%d' % (self.getDoId(), i)))) + self.picnicTableSphereNodes[i].node().addSolid(CollisionSphere(0, 0, 0, 2)) + + self.tableclothSphereNode = self.tablecloth.attachNewNode(CollisionNode('tablecloth_sphere')) + self.tableclothSphereNode.node().addSolid(CollisionSphere(0, 0, -1, 4)) + angle = self.startingHpr[0] + angle -= 90 + radAngle = deg2Rad(angle) + unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0) + unitVec *= 30.0 + self.endPos = self.startingPos + unitVec + dist = Vec3(self.endPos - self.enteringPos).length() + wheelAngle = dist / (0.5 * 1.4 * math.pi) * 360 + self.seatNumber = 0 + self.clockNode = ToontownTimer() + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.setScale(0.3) + self.clockNode.hide() + return + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.fsm.request('off') + self.clearToonTracks() + for i in xrange(self.numSeats): + del self.picnicTableSphereNodes[0] + + del self.picnicTableSphereNodes + self.notify.debug('Deleted self loader ' + str(self.getDoId())) + self.picnicTable.removeNode() + self.picnicBasketTrack = None + return + + def delete(self): + self.notify.debug('Golf kart getting deleted: %s' % self.getDoId()) + DistributedObject.DistributedObject.delete(self) + del self.fsm + + def setState(self, state, seed, timestamp): + self.seed = seed + if not self.random: + self.random = RandomNumGen.RandomNumGen(seed) + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def handleEnterPicnicTableSphere(self, i, collEntry): + self.seatNumber = i + self.notify.debug('Entering Picnic Table Sphere.... %s' % self.getDoId()) + self.loader.place.detectedPicnicTableSphereCollision(self) + + def handleEnterPicnicTable(self, i): + toon = base.localAvatar + self.sendUpdate('requestBoard', [i]) + + def fillSlot0(self, avId): + self.fillSlot(0, avId) + + def fillSlot1(self, avId): + self.fillSlot(1, avId) + + def fillSlot2(self, avId): + self.fillSlot(2, avId) + + def fillSlot3(self, avId): + self.fillSlot(3, avId) + + def fillSlot(self, index, avId): + self.notify.debug('fill Slot: %d for %d' % (index, avId)) + if avId == 0: + pass + else: + self.fullSeat[index] = self.seatState.Full + if avId == base.localAvatar.getDoId(): + self.clockNode.show() + if index == 0 or index == 3: + side = -1 + else: + side = 1 + if hasattr(self.loader.place, 'trolley'): + self.loader.place.trolley.fsm.request('boarding', [self.tablecloth, side]) + else: + self.notify.warning('fillSlot no trolley in place') + self.localToonOnBoard = 1 + if avId == base.localAvatar.getDoId(): + if hasattr(self.loader.place, 'trolley'): + self.loader.place.trolley.fsm.request('boarded') + self.loader.place.trolley.exitButton.hide() + if avId in self.cr.doId2do: + toon = self.cr.doId2do[avId] + toon.stopSmooth() + toon.wrtReparentTo(self.tablecloth) + sitStartDuration = toon.getDuration('sit-start') + jumpTrack = self.generateToonJumpTrack(toon, index) + track = Sequence(jumpTrack, Func(toon.setAnimState, 'Sit', 1.0)) + self.notify.debug('### fillSlot: fullSeat = %s' % self.fullSeat) + if self.fullSeat.count(0) == 3: + self.notify.debug('### fillSlot: adding basketAppear') + if self.picnicBasketTrack: + self.picnicBasketTrack.finish() + waitDuration = track.getDuration() + self.picnicBasketTrack = Sequence(Wait(waitDuration), self.generateBasketAppearTrack()) + self.picnicBasketTrack.start() + track.append(self.generateFoodAppearTrack(index)) + track.append(Sequence(Func(self.clearToonTrack, avId), name=toon.uniqueName('fillTrolley'), autoPause=1)) + if avId == base.localAvatar.getDoId(): + if hasattr(self.loader.place, 'trolley'): + track.append(Func(self.loader.place.trolley.exitButton.show)) + track.delayDelete = DelayDelete.DelayDelete(toon, 'PicnicBasket.fillSlot') + self.storeToonTrack(avId, track) + track.start() + + def emptySlot0(self, avId, timestamp): + self.emptySlot(0, avId, timestamp) + + def emptySlot1(self, avId, timestamp): + self.emptySlot(1, avId, timestamp) + + def emptySlot2(self, avId, timestamp): + self.emptySlot(2, avId, timestamp) + + def emptySlot3(self, avId, timestamp): + self.emptySlot(3, avId, timestamp) + + def notifyToonOffTrolley(self, toon): + toon.setAnimState('neutral', 1.0) + if hasattr(base, 'localAvatar') and toon == base.localAvatar: + if hasattr(self.loader.place, 'trolley'): + self.loader.place.trolley.handleOffTrolley() + self.localToonOnBoard = 0 + else: + toon.startSmooth() + + def emptySlot(self, index, avId, timestamp): + + def emptySeat(index): + self.notify.debug('### seat %s now empty' % index) + self.fullSeat[index] = self.seatState.Empty + + if avId == 0: + pass + elif avId == 1: + self.fullSeat[index] = self.seatState.Empty + track = Sequence(self.generateFoodDisappearTrack(index)) + self.notify.debug('### empty slot - unexpetected: fullSeat = %s' % self.fullSeat) + if self.fullSeat.count(0) == 4: + self.notify.debug('### empty slot - unexpected: losing basket') + if self.picnicBasketTrack: + self.picnicBasketTrack.finish() + waitDuration = track.getDuration() + self.picnicBasketTrack = Sequence(Wait(waitDuration), self.generateBasketDisappearTrack()) + self.picnicBasketTrack.start() + track.start() + else: + self.fullSeat[index] = self.seatState.Empty + if avId in self.cr.doId2do: + if avId == base.localAvatar.getDoId(): + if self.clockNode: + self.clockNode.hide() + toon = self.cr.doId2do[avId] + toon.stopSmooth() + sitStartDuration = toon.getDuration('sit-start') + jumpOutTrack = self.generateToonReverseJumpTrack(toon, index) + track = Sequence(jumpOutTrack) + track.append(self.generateFoodDisappearTrack(index)) + self.notify.debug('### empty slot: fullSeat = %s' % self.fullSeat) + if self.fullSeat.count(0) == 4: + self.notify.debug('### empty slot: losing basket') + if self.picnicBasketTrack: + self.picnicBasketTrack.finish() + waitDuration = track.getDuration() + self.picnicBasketTrack = Sequence(Wait(waitDuration), self.generateBasketDisappearTrack()) + self.picnicBasketTrack.start() + track.append(Sequence(Func(self.notifyToonOffTrolley, toon), Func(self.clearToonTrack, avId), Func(self.doneExit, avId), Func(emptySeat, index), name=toon.uniqueName('emptyTrolley'), autoPause=1)) + track.delayDelete = DelayDelete.DelayDelete(toon, 'PicnicBasket.emptySlot') + self.storeToonTrack(avId, track) + track.start() + + def rejectBoard(self, avId): + self.loader.place.trolley.handleRejectBoard() + + def __enableCollisions(self): + for i in xrange(self.numSeats): + self.accept('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTableSphere, [i]) + self.accept('enterPicnicTableOK_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTable, [i]) + self.picnicTableSphereNodes[i].setCollideMask(ToontownGlobals.WallBitmask) + + def __disableCollisions(self): + for i in xrange(self.numSeats): + self.ignore('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i)) + self.ignore('enterPicnicTableOK_%d_%d' % (self.getDoId(), i)) + + for i in xrange(self.numSeats): + self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0)) + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterWaitEmpty(self, ts): + self.__enableCollisions() + + def exitWaitEmpty(self): + self.__disableCollisions() + + def enterWaitCountdown(self, ts): + self.__enableCollisions() + self.accept('trolleyExitButton', self.handleExitButton) + self.clockNode.countdown(self.picnicCountdownTime, self.handleExitButton) + + def handleExitButton(self): + self.sendUpdate('requestExit') + self.clockNode.hide() + + def exitWaitCountdown(self): + self.__disableCollisions() + self.ignore('trolleyExitButton') + self.clockNode.reset() + + def getStareAtNodeAndOffset(self): + return (self.tablecloth, Point3(0, 0, 4)) + + def storeToonTrack(self, avId, track): + self.clearToonTrack(avId) + self.__toonTracks[avId] = track + + def clearToonTrack(self, avId): + oldTrack = self.__toonTracks.get(avId) + if oldTrack: + oldTrack.pause() + DelayDelete.cleanupDelayDeletes(oldTrack) + del self.__toonTracks[avId] + + def clearToonTracks(self): + keyList = [] + for key in self.__toonTracks: + keyList.append(key) + + for key in keyList: + if key in self.__toonTracks: + self.clearToonTrack(key) + + def doneExit(self, avId): + if avId == base.localAvatar.getDoId(): + self.sendUpdate('doneExit') + + def setPosHpr(self, x, y, z, h, p, r): + self.startingPos = Vec3(x, y, z) + self.enteringPos = Vec3(x, y, z - 10) + self.startingHpr = Vec3(h, 0, 0) + + def setTableNumber(self, tn): + self.tableNumber = tn + + def generateToonJumpTrack(self, av, seatIndex): + av.pose('sit', 47) + hipOffset = av.getHipsParts()[2].getPos(av) + + def getToonJumpTrack(av, seatIndex): + + def getJumpDest(av = av, node = self.tablecloth): + dest = Vec3(self.tablecloth.getPos(av.getParent())) + seatNode = self.picnicTable.find('**/seat' + str(seatIndex + 1)) + dest += seatNode.getPos(self.tablecloth) + dna = av.getStyle() + dest -= hipOffset + if seatIndex == 2 or seatIndex == 3: + dest.setY(dest.getY() + 2 * hipOffset.getY()) + dest.setZ(dest.getZ() + 0.2) + return dest + + def getJumpHpr(av = av, node = self.tablecloth): + hpr = self.seats[seatIndex].getHpr(av.getParent()) + angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) + hpr.setX(angle) + return hpr + + toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.43), Parallel(LerpHprInterval(av, hpr=getJumpHpr, duration=0.9), ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) + return toonJumpTrack + + def getToonSitTrack(av): + toonSitTrack = Sequence(ActorInterval(av, 'sit-start'), Func(av.loop, 'sit')) + return toonSitTrack + + toonJumpTrack = getToonJumpTrack(av, seatIndex) + toonSitTrack = getToonSitTrack(av) + jumpTrack = Sequence(Parallel(toonJumpTrack, Sequence(Wait(1), toonSitTrack)), Func(av.wrtReparentTo, self.tablecloth)) + return jumpTrack + + def generateToonReverseJumpTrack(self, av, seatIndex): + self.notify.debug('av.getH() = %s' % av.getH()) + + def getToonJumpTrack(av, destNode): + + def getJumpDest(av = av, node = destNode): + dest = node.getPos(self.tablecloth) + dest += self.jumpOffsets[seatIndex].getPos(self.tablecloth) + return dest + + def getJumpHpr(av = av, node = destNode): + hpr = node.getHpr(av.getParent()) + hpr.setX(hpr.getX() + 180) + angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) + hpr.setX(angle) + return hpr + + toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.1), Parallel(ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) + return toonJumpTrack + + toonJumpTrack = getToonJumpTrack(av, self.tablecloth) + jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render)) + return jumpTrack + + def generateBasketAppearTrack(self): + if self.basket == None: + self.basket = loader.loadModel('phase_6/models/golf/picnic_basket.bam') + self.basket.setScale(0.1) + basketTrack = Sequence( + Func(self.basket.show), + SoundInterval( + globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'), + node=self.basket), + Func(self.basket.reparentTo, self.tablecloth), + Func(self.basket.setPos, 0, 0, 0.2), + Func(self.basket.setHpr, 45, 0, 0), + Func(self.basket.wrtReparentTo, render), + Func(self.basket.setShear, 0, 0, 0), + Sequence( + LerpScaleInterval( + self.basket, + scale=Point3(1.1, 1.1, 0.1), + duration=0.2), + LerpScaleInterval( + self.basket, + scale=Point3(1.6, 1.6, 0.2), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(1.0, 1.0, 0.4), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(1.5, 1.5, 2.5), + duration=0.2), + LerpScaleInterval( + self.basket, + scale=Point3(2.5, 2.5, 1.5), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(2.0, 2.0, 2.0), + duration=0.1), + Func(self.basket.wrtReparentTo, self.tablecloth), + Func(self.basket.setPos, 0, 0, 0))) + return basketTrack + + def generateBasketDisappearTrack(self): + if not self.basket: + return Sequence() + pos = self.basket.getPos() + pos.addZ(-1) + basketTrack = Sequence( + LerpScaleInterval( + self.basket, + scale=Point3(2.0, 2.0, 1.8), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(1.0, 1.0, 2.5), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(2.0, 2.0, 0.5), + duration=0.2), + LerpScaleInterval( + self.basket, + scale=Point3(0.5, 0.5, 1.0), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(1.1, 1.1, 0.1), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(0.1, 0.1, 0.1), + duration=0.2), + SoundInterval( + globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'), + node=self.basket), + Wait(0.2), + LerpPosInterval( + self.basket, + pos=pos, + duration=0.2), + Func(self.basket.hide)) + return basketTrack + + def generateFoodAppearTrack(self, seat): + if self.fullSeat[seat] == self.seatState.Full: + self.notify.debug('### food appear: self.fullSeat = %s' % self.fullSeat) + if not self.food[seat]: + self.food[seat] = loader.loadModel(self.random.choice(self.foodLoader)) + self.notify.debug('### food appear: self.food = %s' % self.food) + self.food[seat].setScale(0.1) + self.food[seat].reparentTo(self.tablecloth) + self.food[seat].setPos(self.seats[seat].getPos(self.tablecloth)[0] / 2, self.seats[seat].getPos(self.tablecloth)[1] / 2, 0) + foodTrack = Sequence( + Func(self.food[seat].show), + SoundInterval( + globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'), + node=self.food[seat]), + Func(self.food[seat].reparentTo, self.tablecloth), + Func(self.food[seat].setHpr, 45, 0, 0), + Func(self.food[seat].wrtReparentTo, render), + Func(self.food[seat].setShear, 0, 0, 0), + Sequence( + LerpScaleInterval( + self.food[seat], + scale=Point3(1.1, 1.1, 0.1), + duration=0.2), + LerpScaleInterval( + self.food[seat], + scale=Point3(1.6, 1.6, 0.2), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(1.0, 1.0, 0.4), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(1.5, 1.5, 2.5), + duration=0.2), + LerpScaleInterval( + self.food[seat], + scale=Point3(2.5, 2.5, 1.5), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(2.0, 2.0, 2.0), + duration=0.1), + Func(self.food[seat].wrtReparentTo, self.tablecloth))) + return foodTrack + else: + return Sequence() + + def generateFoodDisappearTrack(self, seat): + if not self.food[seat]: + return Sequence() + pos = self.food[seat].getPos() + pos.addZ(-1.0) + foodTrack = Sequence( + LerpScaleInterval( + self.food[seat], + scale=Point3(2.0, 2.0, 1.8), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(1.0, 1.0, 2.5), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(2.0, 2.0, 0.5), + duration=0.2), + LerpScaleInterval( + self.food[seat], + scale=Point3(0.5, 0.5, 1.0), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(1.1, 1.1, 0.1), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(0.1, 0.1, 0.1), + duration=0.2), + SoundInterval( + globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'), + node=self.food[seat]), + Wait(0.2), + LerpPosInterval( + self.food[seat], + pos=pos, + duration=0.2), + Func(self.food[seat].hide)) + return foodTrack + + def destroy(self, node): + node.removeNode() + node = None + self.basket.removeNode() + self.basket = None + for food in self.food: + food.removeNode() + + self.food = None + self.clockNode.removeNode() + del self.clockNode + self.clockNode = None + return + + def setPicnicDone(self): + if self.localToonOnBoard: + if hasattr(self.loader.place, 'trolley'): + self.loader.place.trolley.fsm.request('final') + self.loader.place.trolley.fsm.request('start') + self.localToonOnBoard = 0 + messenger.send('picnicDone') diff --git a/toontown/safezone/DistributedPicnicBasketAI.py b/toontown/safezone/DistributedPicnicBasketAI.py new file mode 100755 index 00000000..fc865193 --- /dev/null +++ b/toontown/safezone/DistributedPicnicBasketAI.py @@ -0,0 +1,236 @@ +from TrolleyConstants import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.showbase import RandomNumGen +from direct.task import Task +from otp.ai.AIBase import * +from toontown.golf import GolfGlobals +from toontown.golf import GolfManagerAI +from toontown.minigame import MinigameCreatorAI +from toontown.quest import Quests +from toontown.toonbase import ToontownGlobals +from toontown.toonbase.ToontownGlobals import * + + +class DistributedPicnicBasketAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPicnicBasketAI') + + def __init__(self, air, tableNumber, x, y, z, h, p, r): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.seats = [None, None, None, None] + self.posHpr = (x, y, z, h, p, r) + self.tableNumber = int(tableNumber) + self.seed = RandomNumGen.randHash(globalClock.getRealTime()) + self.accepting = 0 + self.numPlayersExiting = 0 + self.trolleyCountdownTime = simbase.config.GetFloat('picnic-countdown-time', ToontownGlobals.PICNIC_COUNTDOWN_TIME) + self.fsm = ClassicFSM.ClassicFSM( + 'DistributedPicnicBasketAI', + [ + State.State('off', self.enterOff, self.exitOff, ['waitEmpty']), + State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), + State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty']) + ], 'off', 'off') + self.fsm.enterInitialState() + + + def delete(self): + self.fsm.requestFinalState() + del self.fsm + DistributedObjectAI.DistributedObjectAI.delete(self) + + def findAvailableSeat(self): + for i in xrange(len(self.seats)): + if self.seats[i] is None: + return i + + def findAvatar(self, avId): + for i in xrange(len(self.seats)): + if self.seats[i] == avId: + return i + + def countFullSeats(self): + avCounter = 0 + for i in self.seats: + if i: + avCounter += 1 + return avCounter + + def rejectingBoardersHandler(self, avId, si): + self.rejectBoarder(avId) + + def rejectBoarder(self, avId): + self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId]) + + def acceptingBoardersHandler(self, avId, si): + self.notify.debug('acceptingBoardersHandler') + seatIndex = si + if not seatIndex is None: + self.acceptBoarder(avId, seatIndex) + + def acceptBoarder(self, avId, seatIndex): + self.notify.debug('acceptBoarder %d' % avId) + if self.findAvatar(avId) is not None: + return None + self.seats[seatIndex] = avId + self.acceptOnce( + self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, + extraArgs=[avId]) + self.timeOfBoarding = globalClock.getRealTime() + self.sendUpdate('fillSlot' + str(seatIndex), [avId]) + self.waitCountdown() + + def _DistributedPicnicBasketAI__handleUnexpectedExit(self, avId): + self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly') + seatIndex = self.findAvatar(avId) + if seatIndex == None: + return + self.clearFullNow(seatIndex) + self.clearEmptyNowUnexpected(seatIndex) + if self.countFullSeats() == 0: + self.waitEmpty() + + def clearEmptyNowUnexpected(self, seatIndex): + self.sendUpdate('emptySlot' + str(seatIndex), [1, globalClockDelta.getRealNetworkTime()]) + + def rejectingExitersHandler(self, avId): + self.rejectExiter(avId) + + def rejectExiter(self, avId): + pass + + def acceptingExitersHandler(self, avId): + self.acceptExiter(avId) + + def acceptExiter(self, avId): + seatIndex = self.findAvatar(avId) + if seatIndex == None: + return + self.clearFullNow(seatIndex) + self.sendUpdate('emptySlot' + str(seatIndex), [avId, globalClockDelta.getRealNetworkTime()]) + taskMgr.doMethodLater( + TOON_EXIT_TIME, self.clearEmptyNow, + self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs=(seatIndex,)) + + def clearEmptyNow(self, seatIndex): + self.notify.debugStateCall(self) + self.sendUpdate('emptySlot' + str(seatIndex), [0, globalClockDelta.getRealNetworkTime()]) + + def clearFullNow(self, seatIndex): + avId = self.seats[seatIndex] + if avId == 0: + self.notify.warning('Clearing an empty seat index: ' + str(seatIndex) + ' ... Strange...') + else: + self.seats[seatIndex] = None + self.sendUpdate('fillSlot' + str(seatIndex), [0]) + self.ignore(self.air.getAvatarExitEvent(avId)) + + def d_setState(self, state, seed): + self.sendUpdate('setState', [state, seed, globalClockDelta.getRealNetworkTime()]) + + def getState(self): + return self.fsm.getCurrentState().getName() + + def requestBoard(self, si): + self.notify.debug('requestBoard') + avId = self.air.getAvatarIdFromSender() + if self.findAvatar(avId) != None: + self.notify.warning('Ignoring multiple requests from %s to board.' % avId) + return None + av = self.air.doId2do.get(avId) + if av: + if av.hp > 0 and self.accepting and self.seats[si] == None: + self.notify.debug('accepting boarder %d' % avId) + self.acceptingBoardersHandler(avId, si) + else: + self.notify.debug('rejecting boarder %d' % avId) + self.rejectingBoardersHandler(avId, si) + else: + self.notify.warning('avid: %s does not exist, but tried to board a trolley' % avId) + + def requestExit(self, *args): + self.notify.debug('requestExit') + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av: + if self.countFullSeats() > 0: + newArgs = (avId,) + args + self.numPlayersExiting += 1 + if self.accepting: + self.acceptingExitersHandler(*newArgs) + else: + self.rejectingExitersHandler(*newArgs) + else: + self.notify.debug('Player tried to exit after AI already kicked everyone out') + else: + self.notify.warning('avId: %s does not exist, but tried to exit a trolley' % avId) + + def doneExit(self): + if self.numPlayersExiting > 0: + self.numPlayersExiting -= 1 + if self.numPlayersExiting == 0 and self.countFullSeats() == 0: + self.waitEmpty() + + def start(self): + self.waitEmpty() + + def enterOff(self): + self.accepting = 0 + if hasattr(self, 'doId'): + for seatIndex in xrange(4): + taskMgr.remove(self.uniqueName('clearEmpty-' + str(seatIndex))) + + def exitOff(self): + self.accepting = 0 + + def waitEmptyTask(self, task): + self.waitEmpty() + return Task.done + + def waitEmpty(self): + self.fsm.request('waitEmpty') + + def enterWaitEmpty(self): + self.notify.debugStateCall(self) + self.d_setState('waitEmpty', self.seed) + self.seats = [None, None, None, None] + self.accepting = 1 + + def exitWaitEmpty(self): + self.notify.debugStateCall(self) + self.accepting = 0 + + def waitCountdown(self): + self.notify.debugStateCall(self) + self.fsm.request('waitCountdown') + + def enterWaitCountdown(self): + self.notify.debugStateCall(self) + self.d_setState('waitCountdown', self.seed) + self.accepting = 1 + taskMgr.doMethodLater(self.trolleyCountdownTime, self.timeToGoTask, self.uniqueName('countdown-timer')) + + def timeToGoTask(self, task): + self.accepting = 0 + if self.countFullSeats() > 0: + for x in xrange(len(self.seats)): + if not self.seats[x] == None: + self.sendUpdateToAvatarId(self.seats[x], 'setPicnicDone', []) + self.acceptExiter(self.seats[x]) + self.numPlayersExiting += 1 + self.waitEmpty() + return Task.done + + def exitWaitCountdown(self): + self.notify.debugStateCall(self) + self.accepting = 0 + taskMgr.remove(self.uniqueName('countdown-timer')) + + def getPosHpr(self): + return self.posHpr + + def getTableNumber(self): + return self.tableNumber diff --git a/toontown/safezone/DistributedPicnicTable.py b/toontown/safezone/DistributedPicnicTable.py new file mode 100755 index 00000000..cd01b482 --- /dev/null +++ b/toontown/safezone/DistributedPicnicTable.py @@ -0,0 +1,651 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from direct.distributed import DistributedNode +from direct.distributed.ClockDelta import globalClockDelta +from ChineseCheckersBoard import ChineseCheckersBoard +from GameTutorials import * +from GameMenu import GameMenu +from direct.fsm import ClassicFSM, State +from direct.fsm import StateData +from toontown.distributed import DelayDelete +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.toonbase import ToontownGlobals +from direct.showbase import PythonUtil +from otp.otpbase import OTPGlobals + +class DistributedPicnicTable(DistributedNode.DistributedNode): + + def __init__(self, cr): + self.cr = cr + NodePath.__init__(self, 'DistributedPicnicTable') + DistributedNode.DistributedNode.__init__(self, cr) + self.reparentTo(render) + self.picnicTable = loader.loadModel('phase_6/models/golf/game_table.bam') + self.picnicTable.reparentTo(self) + self.picnicTableSphereNodes = [] + self.numSeats = 6 + self.seats = [] + self.jumpOffsets = [] + self.inGame = False + self.requestSeat = None + self.gameState = None + self.cameraBoardTrack = Func(self.doNothing) + self.seatBumpForObserve = 0 + self.winTrack = Sequence() + self.outTrack = Sequence() + self.joinButton = None + self.observeButton = None + self.tutorialButton = None + self.exitButton = None + self.isPlaying = False + self.gameMenu = None + self.game = None + self.gameZone = None + self.tutorial = None + self.timerFunc = None + self.gameDoId = None + self.gameWantTimer = False + self.tableState = [None, + None, + None, + None, + None, + None] + self.haveAnimated = [] + self.winSound = base.loadSfx('phase_6/audio/sfx/KART_Applause_1.ogg') + self.happyDance = base.loadSfx('phase_5/audio/sfx/AA_heal_happydance.ogg') + self.accept('stoppedAsleep', self.handleSleep) + base.localAvatar.startSleepWatch(self.handleSleep) + self.__toonTracks = {} + self.fsm = ClassicFSM.ClassicFSM('PicnicTable', [State.State('off', self.enterOff, self.exitOff, ['chooseMode', 'observing']), + State.State('chooseMode', self.enterChooseMode, self.exitChooseMode, ['sitting', 'off', 'observing']), + State.State('sitting', self.enterSitting, self.exitSitting, ['off']), + State.State('observing', self.enterObserving, self.exitObserving, ['off'])], 'off', 'off') + self.fsm.enterInitialState() + for i in xrange(self.numSeats): + self.seats.append(self.picnicTable.find('**/*seat%d' % (i + 1))) + self.jumpOffsets.append(self.picnicTable.find('**/*jumpOut%d' % (i + 1))) + + self.tableCloth = self.picnicTable.find('**/basket_locator') + self.tableclothSphereNode = self.tableCloth.attachNewNode(CollisionNode('tablecloth_sphere')) + self.tableclothSphereNode.node().addSolid(CollisionSphere(0, 0, -2, 5.5)) + self.clockNode = ToontownTimer() + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.setScale(0.3) + self.clockNode.hide() + return + + def announceGenerate(self): + DistributedNode.DistributedNode.announceGenerate(self) + for i in xrange(self.numSeats): + self.picnicTableSphereNodes.append(self.seats[i].attachNewNode(CollisionNode('picnicTable_sphere_%d_%d' % (self.getDoId(), i)))) + self.picnicTableSphereNodes[i].node().addSolid(CollisionSphere(0, 0, 0, 2)) + + self.tableState = [None, + None, + None, + None, + None, + None] + self.requestTableState() + self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + self.upButton = self.buttonModels.find('**//InventoryButtonUp') + self.downButton = self.buttonModels.find('**/InventoryButtonDown') + self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover') + angle = self.getH() + angle -= 90 + radAngle = deg2Rad(angle) + unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0) + unitVec *= 30.0 + self.endPos = self.getPos() + unitVec + dist = Vec3(self.endPos - self.getPos()).length() + wheelAngle = dist / (0.5 * 1.4 * math.pi) * 360 + self.__enableCollisions() + return + + def handleSleep(self, task = None): + if self.fsm.getCurrentState().getName() == 'chooseMode': + self.cancelButtonPushed() + elif self.fsm.getCurrentState().getName() == 'sitting': + self.sendUpdate('requestExit', []) + if self.gameMenu != None: + self.gameMenu.removeButtons() + self.gameMenu.picnicFunction = None + self.gameMenu = None + if task != None: + task.done + return + + def disable(self): + DistributedNode.DistributedNode.disable(self) + self.ignore('stoppedAsleep') + self.clearToonTracks() + self.__disableCollisions() + self.disableChoiceButtons() + self.picnicTable.removeNode() + self.cameraBoardTrack = None + return + + def delete(self): + self.__disableCollisions() + self.ignore('stoppedAsleep') + DistributedNode.DistributedNode.delete(self) + self.disableChoiceButtons() + self.cameraBoardTrack = None + del self.winTrack + del self.outTrack + self.fsm = None + self.gameZone = None + self.clearToonTracks() + self.cameraBoardTrack = None + return + + def setName(self, name): + self.name = name + + def setGameDoId(self, doId): + self.gameDoId = doId + self.game = self.cr.doId2do[doId] + self.game.setHpr(self.getHpr()) + self.gameWantTimer = self.game.wantTimer + if self.gameState == 1: + self.game.fsm.request('playing') + + def setTimerFunc(self, function): + self.timerFunc = function + + def setTimer(self, timerEnd): + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(timerEnd) + self.timeLeft = int(time - globalClock.getRealTime()) + if self.gameWantTimer and self.game != None: + self.showTimer() + return + + def showTimer(self): + self.clockNode.stop() + self.clockNode.countdown(self.timeLeft, self.timerFunc) + self.clockNode.show() + + def requestTableState(self): + self.sendUpdate('requestTableState', []) + + def setTableState(self, tableStateList, isplaying): + y = 0 + print 'SET TABLE STATE' + if isplaying == 0: + self.isPlaying = False + else: + self.isPlaying = True + for x in tableStateList: + if x != 0: + if x not in self.tableState and x in self.cr.doId2do and x not in self.haveAnimated: + seatIndex = tableStateList.index(x) + toon = self.cr.doId2do[x] + toon.stopSmooth() + toon.setAnimState('Sit', 1.0) + dest = self.seats[seatIndex].getPos(self.tableCloth) + hpr = self.seats[seatIndex].getHpr(render) + toon.setHpr(hpr) + if seatIndex > 2: + toon.setH(self.getH() + 180) + toon.wrtReparentTo(self) + toon.setPos(dest) + toon.setZ(toon.getZ() + 1.35) + if seatIndex > 2: + toon.setY(toon.getY() - 1.0) + else: + toon.setY(toon.getY() + 1.0) + if x != 0: + self.tableState[y] = x + else: + self.tableState[y] = None + y = y + 1 + + numPlayers = 0 + for x in self.tableState: + if x != None: + numPlayers += 1 + + print ' GETTING 2', self.gameMenu, numPlayers + if self.gameMenu: + if numPlayers > 2: + print ' GETTING HERE!!' + self.gameMenu.FindFour.setColor(0.7, 0.7, 0.7, 0.7) + self.gameMenu.FindFour['command'] = self.doNothing + self.gameMenu.findFourText['fg'] = (0.7, 0.7, 0.7, 0.7) + self.gameMenu.Checkers.setColor(0.7, 0.7, 0.7, 0.7) + self.gameMenu.Checkers['command'] = self.doNothing + self.gameMenu.checkersText['fg'] = (0.7, 0.7, 0.7, 0.7) + return + + def setIsPlaying(self, isPlaying): + if isPlaying == 0: + self.isPlaying = False + elif isPlaying == 1: + self.isPlaying = True + + def announceWinner(self, winString, avId): + if avId == base.localAvatar.getDoId(): + sound = Sequence(Wait(2.0), Parallel(SoundInterval(self.winSound), SoundInterval(self.happyDance))) + sound.start() + base.cr.playGame.getPlace().setState('walk') + if winString == 'Chinese Checkers': + whisper = WhisperPopup(TTLocalizer.ChineseCheckersYouWon, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + elif winString == 'Checkers': + whisper = WhisperPopup(TTLocalizer.RegularCheckersYouWon, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + elif winString == 'Find Four': + whisper = WhisperPopup('You won a game of Find Four!', OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + elif avId in self.cr.doId2do: + stateString = self.fsm.getCurrentState().getName() + if stateString == 'sitting' or stateString == 'observing': + base.cr.playGame.getPlace().setState('walk') + av = self.cr.doId2do[avId] + if winString == 'Chinese Checkers': + whisper = WhisperPopup(av.getName() + TTLocalizer.ChineseCheckersGameOf + TTLocalizer.ChineseCheckers, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + elif winString == 'Checkers': + whisper = WhisperPopup(av.getName() + TTLocalizer.RegularCheckersGameOf + TTLocalizer.RegularCheckers, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + elif winString == 'Find Four': + whisper = WhisperPopup(av.getName() + ' has won a game of' + ' Find Four!', OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + if avId in self.cr.doId2do: + toon = self.cr.doId2do[avId] + self.winTrack = Sequence(autoFinish=1) + if self.outTrack.isPlaying(): + self.winTrack.append(Wait(2.0)) + if avId == base.localAvatar.getDoId(): + self.winTrack.append(Func(self.stopToWalk)) + self.winTrack.append(ActorInterval(toon, 'happy-dance')) + if avId == base.localAvatar.getDoId(): + self.winTrack.append(Func(self.allowToWalk)) + self.winTrack.start() + whisper.manage(base.marginManager) + + def handleEnterPicnicTableSphere(self, i, collEntry): + self.notify.debug('Entering Picnic Table Sphere.... %s' % self.getDoId()) + self.requestSeat = i + self.seatBumpForObserve = i + self.fsm.request('chooseMode') + + def enableChoiceButtons(self): + if self.tableState[self.seatBumpForObserve] == None and self.isPlaying == False: + self.joinButton = DirectButton( + relief=None, + text=TTLocalizer.PicnicTableJoinButton, + text_fg=(1, 1, 0.65, 1), + text_pos=(0, -.23), + text_scale=0.8, + image=(self.upButton, self.downButton, self.rolloverButton), + image_color=(1, 0, 0, 1), + image_scale=(20, 1, 11), + pos=(0, 0, 0.8), + scale=0.15, + command=lambda self = self: self.joinButtonPushed()) + if self.isPlaying == True: + self.observeButton = DirectButton( + relief=None, + text=TTLocalizer.PicnicTableObserveButton, + text_fg=(1, 1, 0.65, 1), + text_pos=(0, -.23), + text_scale=0.8, + image=(self.upButton, self.downButton, self.rolloverButton), + image_color=(1, 0, 0, 1), + image_scale=(20, 1, 11), + pos=(0, 0, 0.6), + scale=0.15, + command=lambda self = self: self.observeButtonPushed()) + self.exitButton = DirectButton( + relief=None, + text=TTLocalizer.PicnicTableCancelButton, + text_fg=(1, 1, 0.65, 1), + text_pos=(0, -.23), + text_scale=0.8, + image=(self.upButton, self.downButton, self.rolloverButton), + image_color=(1, 0, 0, 1), + image_scale=(20, 1, 11), + pos=(1, 0, 0.6), + scale=0.15, + command=lambda self = self: self.cancelButtonPushed()) + self.tutorialButton = DirectButton( + relief=None, + text=TTLocalizer.PicnicTableTutorial, + text_fg=(1, 1, 0.65, 1), + text_pos=(-.05, -.13), + text_scale=0.55, + image=(self.upButton, self.downButton, self.rolloverButton), + image_color=(1, 0, 0, 1), + image_scale=(20, 1, 11), + pos=(-1, 0, 0.6), + scale=0.15, + command=lambda self = self: self.tutorialButtonPushed()) + base.cr.playGame.getPlace().setState('stopped') + return + + def tutorialButtonPushed(self): + self.disableChoiceButtons() + self.gameMenu = GameMenu(self.tutorialFunction, 1) + self.tutorialButton.destroy() + self.tutorialButton = None + return + + def tutorialFunction(self, tutVal): + if tutVal == 1: + self.tutorial = ChineseTutorial(self.tutorialDone) + elif tutVal == 2: + self.tutorial = CheckersTutorial(self.tutorialDone) + self.gameMenu.picnicFunction = None + self.gameMenu = None + return + + def tutorialDone(self): + self.requestSeat = None + self.fsm.request('off') + self.tutorial = None + return + + def joinButtonPushed(self): + toon = base.localAvatar + self.sendUpdate('requestJoin', [self.requestSeat, + toon.getX(), + toon.getY(), + toon.getZ(), + toon.getH(), + toon.getP(), + toon.getR()]) + self.requestSeat = None + self.fsm.request('sitting') + return + + def rejectJoin(self): + self.fsm.request('off') + self.allowToWalk() + + def cancelButtonPushed(self): + base.cr.playGame.getPlace().setState('walk') + self.requestSeat = None + self.fsm.request('off') + return + + def disableChoiceButtons(self): + if self.joinButton: + self.joinButton.destroy() + if self.observeButton: + self.observeButton.destroy() + if self.exitButton: + self.exitButton.destroy() + if self.tutorialButton: + self.tutorialButton.destroy() + + def pickFunction(self, gameNum): + if gameNum == 1: + self.sendUpdate('requestPickedGame', [gameNum]) + elif gameNum == 2: + self.sendUpdate('requestPickedGame', [gameNum]) + elif gameNum == 3: + self.sendUpdate('requestPickedGame', [gameNum]) + + def allowPick(self): + self.gameMenu = GameMenu(self.pickFunction, 2) + + def setZone(self, zoneId): + if self.fsm.getCurrentState().getName() == 'sitting' or self.fsm.getCurrentState().getName() == 'observing': + if self.tutorial == None: + self.gameZone = base.cr.addInterest(base.localAvatar.defaultShard, zoneId, 'gameBoard') + if self.gameMenu != None: + self.gameMenu.removeButtons() + self.gameMenu.picnicFunction = None + self.gameMenu = None + return + + def fillSlot(self, avId, index, x, y, z, h, p, r, timestamp, parentDoId): + self.notify.debug('fill Slot: %d for %d' % (index, avId)) + if avId not in self.haveAnimated: + self.haveAnimated.append(avId) + if avId == base.localAvatar.getDoId(): + if self.inGame == True: + return + else: + self.inGame = True + self.seatPos = index + if avId in self.cr.doId2do: + toon = self.cr.doId2do[avId] + toon.stopSmooth() + toon.wrtReparentTo(self.tableCloth) + sitStartDuration = toon.getDuration('sit-start') + jumpTrack = self.generateToonJumpTrack(toon, index) + track = Sequence(autoFinish=1) + if avId == base.localAvatar.getDoId(): + if not base.cr.playGame.getPlace() == None: + self.moveCamera(index) + track.append(Func(self.__disableCollisions)) + track.append(jumpTrack) + track.append(Func(toon.setAnimState, 'Sit', 1.0)) + track.append(Func(self.clearToonTrack, avId)) + self.storeToonTrack(avId, track) + track.start() + return + + def emptySlot(self, avId, index, timestamp): + self.notify.debug('### seat %s now empty' % index) + if index == 255 and self.game != None: + self.stopObserveButtonPushed() + return + if avId in self.haveAnimated: + self.haveAnimated.remove(avId) + if avId in self.cr.doId2do: + if avId == base.localAvatar.getDoId(): + if self.gameZone: + base.cr.removeInterest(self.gameZone) + if self.inGame == True: + self.inGame = False + else: + return + toon = self.cr.doId2do[avId] + toon.stopSmooth() + sitStartDuration = toon.getDuration('sit-start') + jumpOutTrack = self.generateToonReverseJumpTrack(toon, index) + self.outTrack = Sequence(jumpOutTrack) + if base.localAvatar.getDoId() == avId: + self.outTrack.append(Func(self.__enableCollisions)) + self.outTrack.append(Func(self.allowToWalk)) + self.fsm.request('off') + val = self.jumpOffsets[index].getPos(render) + self.outTrack.append(Func(toon.setPos, val)) + self.outTrack.append(Func(toon.startSmooth)) + self.outTrack.start() + return + + def stopToWalk(self): + base.cr.playGame.getPlace().setState('stopped') + + def allowToWalk(self): + base.cr.playGame.getPlace().setState('walk') + + def moveCamera(self, seatIndex): + self.oldCameraPos = camera.getPos() + self.oldCameraHpr = camera.getHpr() + camera.wrtReparentTo(self.picnicTable) + heading = PythonUtil.fitDestAngle2Src(camera.getH(), 90) + if seatIndex < 3: + self.cameraBoardTrack = LerpPosHprInterval(camera, 2.0, Point3(0, 0, 17), Point3(0, -90, 0)) + elif camera.getH() < 0: + self.cameraBoardTrack = LerpPosHprInterval(camera, 2.0, Point3(0, 0, 17), Point3(-180, -90, 0)) + else: + self.cameraBoardTrack = LerpPosHprInterval(camera, 2.0, Point3(0, 0, 17), Point3(180, -90, 0)) + self.cameraBoardTrack.start() + + def moveCameraBack(self): + self.cameraBoardTrack = LerpPosHprInterval(camera, 2.5, self.oldCameraPos, self.oldCameraHpr) + self.cameraBoardTrack.start() + + def __enableCollisions(self): + for i in xrange(self.numSeats): + self.accept('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTableSphere, [i]) + self.picnicTableSphereNodes[i].setCollideMask(ToontownGlobals.WallBitmask) + + self.tableclothSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + + def __disableCollisions(self): + for i in xrange(self.numSeats): + self.ignore('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i)) + self.ignore('enterPicnicTableOK_%d_%d' % (self.getDoId(), i)) + + for i in xrange(self.numSeats): + self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0)) + + self.tableclothSphereNode.setCollideMask(BitMask32(0)) + + def enterOff(self): + base.setCellsAvailable(base.leftCells + base.bottomCells, 0) + + def exitOff(self): + base.setCellsAvailable(base.bottomCells, 0) + + def enterChooseMode(self): + self.winTrack = Sequence(autoFinish=1) + self.enableChoiceButtons() + + def exitChooseMode(self): + self.disableChoiceButtons() + + def enterObserving(self): + self.enableStopObserveButton() + self.moveCamera(self.seatBumpForObserve) + self.sendUpdate('requestGameZone') + + def exitObserving(self): + if self.cameraBoardTrack.isPlaying(): + self.cameraBoardTrack.pause() + self.allowToWalk() + self.stopObserveButton.destroy() + + def enterSitting(self): + pass + + def exitSitting(self): + self.gameMenu = None + return + + def setGameZone(self, zoneId, gamestate): + self.gameZone = base.cr.addInterest(base.localAvatar.defaultShard, zoneId, 'gameBoard') + self.gameState = gamestate + + def observeButtonPushed(self): + self.requestSeat = None + self.fsm.request('observing') + return + + def enableStopObserveButton(self): + self.stopObserveButton = DirectButton( + relief=None, + text='Stop Observing', + text_fg=(1, 1, 0.65, 1), + text_pos=(0, -.23), + text_scale=0.45, + image=(self.upButton, self.downButton, self.rolloverButton), + image_color=(1, 0, 0, 1), + image_scale=(20, 1, 11), + pos=(0.92, 0, 0.4), + scale=0.15, + command=lambda self = self: self.stopObserveButtonPushed()) + return + + def stopObserveButtonPushed(self): + self.sendUpdate('leaveObserve', []) + self.gameState = None + if self.game: + self.game.fsm.request('gameOver') + base.cr.removeInterest(self.gameZone) + self.fsm.request('off') + return + + def generateToonReverseJumpTrack(self, av, seatIndex): + self.notify.debug('av.getH() = %s' % av.getH()) + + def getToonJumpTrack(av, destNode): + + def getJumpDest(av = av, node = destNode): + dest = node.getPos(self.tableCloth) + dest += self.jumpOffsets[seatIndex].getPos(self.tableCloth) + return dest + + def getJumpHpr(av = av, node = destNode): + hpr = node.getHpr(av.getParent()) + hpr.setX(hpr.getX() + 180) + angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) + hpr.setX(angle) + return hpr + + toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.1), Parallel(ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) + return toonJumpTrack + + toonJumpTrack = getToonJumpTrack(av, self.tableCloth) + jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render)) + return jumpTrack + + def generateToonJumpTrack(self, av, seatIndex): + av.pose('sit', 47) + hipOffset = av.getHipsParts()[2].getPos(av) + + def getToonJumpTrack(av, seatIndex): + + def getJumpDest(av = av, node = self.tableCloth): + dest = Vec3(self.tableCloth.getPos(av.getParent())) + seatNode = self.picnicTable.find('**/seat' + str(seatIndex + 1)) + dest += seatNode.getPos(self.tableCloth) + dna = av.getStyle() + dest -= hipOffset + if seatIndex > 2: + dest.setY(dest.getY() - 2.0) + if seatIndex == 1: + dest.setY(dest.getY() - 0.5) + dest.setZ(dest.getZ() + 0.2) + return dest + + def getJumpHpr(av = av, node = self.tableCloth): + hpr = self.seats[seatIndex].getHpr(av.getParent()) + if seatIndex < 3: + hpr.setX(hpr.getX()) + elif av.getH() < 0: + hpr.setX(hpr.getX() - 180) + else: + hpr.setX(hpr.getX() + 180) + return hpr + + toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.43), Parallel(LerpHprInterval(av, hpr=getJumpHpr, duration=1), ProjectileInterval(av, endPos=getJumpDest, duration=1)))) + return toonJumpTrack + + def getToonSitTrack(av): + toonSitTrack = Sequence(ActorInterval(av, 'sit-start'), Func(av.loop, 'sit')) + return toonSitTrack + + toonJumpTrack = getToonJumpTrack(av, seatIndex) + toonSitTrack = getToonSitTrack(av) + jumpTrack = Sequence(Parallel(toonJumpTrack, Sequence(Wait(1), toonSitTrack)), Func(av.wrtReparentTo, self.tableCloth)) + return jumpTrack + + def storeToonTrack(self, avId, track): + self.clearToonTrack(avId) + self.__toonTracks[avId] = track + + def clearToonTrack(self, avId): + oldTrack = self.__toonTracks.get(avId) + if oldTrack: + oldTrack.pause() + DelayDelete.cleanupDelayDeletes(oldTrack) + + def clearToonTracks(self): + keyList = [] + for key in self.__toonTracks: + keyList.append(key) + + for key in keyList: + if key in self.__toonTracks: + self.clearToonTrack(key) + + def doNothing(self): + pass diff --git a/toontown/safezone/DistributedPicnicTableAI.py b/toontown/safezone/DistributedPicnicTableAI.py new file mode 100755 index 00000000..7be339f8 --- /dev/null +++ b/toontown/safezone/DistributedPicnicTableAI.py @@ -0,0 +1,343 @@ +from direct.distributed.DistributedNodeAI import DistributedNodeAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from toontown.safezone import DistributedChineseCheckersAI +from toontown.safezone import DistributedCheckersAI +from toontown.safezone import DistributedFindFourAI +from toontown.safezone import GameGlobals + +class DistributedPicnicTableAI(DistributedNodeAI): + + def __init__(self, air, zone, name, x, y, z, h, p, r): + DistributedNodeAI.__init__(self, air) + self.name = name + self.zoneId = zone + self.air = air + self.seats = [ + None, + None, + None, + None, + None, + None] + self.setPos(x, y, z) + self.setHpr(h, p, r) + self.playersSitting = 0 + self.playerIdList = [] + self.checkersZoneId = None + self.observers = [] + self.allowPickers = [] + self.hasPicked = False + self.game = None + self.gameDoId = None + self.isAccepting = True + + def announceGenerate(self): + pass + + def delete(self): + DistributedNodeAI.delete(self) + self.game = None + self.gameDoId = None + + def setGameDoId(self, doId): + self.gameDoId = doId + self.game = self.air.doId2do.get(doId) + + def requestTableState(self): + avId = self.air.getAvatarIdFromSender() + self.getTableState() + + def getTableState(self): + tableStateList = [] + for x in self.seats: + if x == None: + tableStateList.append(0) + continue + tableStateList.append(x) + + if self.game and self.game.fsm.getCurrentState().getName() == 'playing': + self.sendUpdate('setTableState', [ + tableStateList, + 1]) + else: + self.sendUpdate('setTableState', [ + tableStateList, + 0]) + + def sendIsPlaying(self): + if self.game.fsm.getCurrentState().getName() == 'playing': + self.sendUpdate('setIsPlaying', [ + 1]) + else: + self.sendUpdate('setIsPlaying', [ + 0]) + + def announceWinner(self, gameName, avId): + self.sendUpdate('announceWinner', [ + gameName, + avId]) + self.gameDoId = None + self.game = None + + def requestJoin(self, si, x, y, z, h, p, r): + avId = self.air.getAvatarIdFromSender() + if self.findAvatar(avId) != None: + self.notify.warning('Ignoring multiple requests from %s to board.' % avId) + return None + + av = self.air.doId2do.get(avId) + if av: + if av.hp > 0 and self.isAccepting and self.seats[si] == None: + self.notify.debug('accepting boarder %d' % avId) + self.acceptBoarder(avId, si, x, y, z, h, p, r) + else: + self.notify.debug('rejecting boarder %d' % avId) + self.sendUpdateToAvatarId(avId, 'rejectJoin', []) + else: + self.notify.warning('avid: %s does not exist, but tried to board a picnicTable' % avId) + + def acceptBoarder(self, avId, seatIndex, x, y, z, h, p, r): + self.notify.debug('acceptBoarder %d' % avId) + if self.findAvatar(avId) != None: + return None + + isEmpty = True + for xx in self.seats: + if xx != None: + isEmpty = False + break + continue + + if isEmpty == True or self.hasPicked == False: + self.sendUpdateToAvatarId(avId, 'allowPick', []) + self.allowPickers.append(avId) + + if self.hasPicked == True: + self.sendUpdateToAvatarId(avId, 'setZone', [ + self.game.zoneId]) + + self.seats[seatIndex] = avId + self.acceptOnce(self.air.getAvatarExitEvent(avId), self._DistributedPicnicTableAI__handleUnexpectedExit, extraArgs = [ + avId]) + self.timeOfBoarding = globalClock.getRealTime() + if self.game: + self.game.informGameOfPlayer() + + self.sendUpdate('fillSlot', [ + avId, + seatIndex, + x, + y, + z, + h, + p, + r, + globalClockDelta.localToNetworkTime(self.timeOfBoarding), + self.doId]) + self.getTableState() + + def requestPickedGame(self, gameNum): + avId = self.air.getAvatarIdFromSender() + if self.hasPicked == False and avId in self.allowPickers: + self.hasPicked = True + numPickers = len(self.allowPickers) + self.allowPickers = [] + self.pickGame(gameNum) + if self.game: + for x in xrange(numPickers): + self.game.informGameOfPlayer() + + def pickGame(self, gameNum): + if self.game: + return + + x = 0 + for x in self.seats: + if x != None: + x += 1 + continue + + if gameNum == GameGlobals.ChineseCheckersGameIndex: + if simbase.config.GetBool('want-chinese', 1): + self.game = DistributedChineseCheckersAI.DistributedChineseCheckersAI(self.air, self.doId, 'chinese', self.getX(), self.getY(), self.getZ() + 2.8300000000000001, self.getH(), self.getP(), self.getR()) + self.sendUpdate('setZone', [ + self.game.zoneId]) + + elif gameNum == GameGlobals.CheckersGameIndex: + if x <= 2: + if simbase.config.GetBool('want-checkers', 1): + self.game = DistributedCheckersAI.DistributedCheckersAI(self.air, self.doId, 'checkers', self.getX(), self.getY(), self.getZ() + 2.8300000000000001, self.getH(), self.getP(), self.getR()) + self.sendUpdate('setZone', [ + self.game.zoneId]) + + elif gameNum == GameGlobals.FindFourGameIndex: + if x <= 2: + if simbase.config.GetBool('want-findfour', 1): + self.game = DistributedFindFourAI.DistributedFindFourAI(self.air, self.doId, 'findFour', self.getX(), self.getY(), self.getZ() + 2.8300000000000001, self.getH(), self.getP(), self.getR()) + self.sendUpdate('setZone', [ + self.game.zoneId]) + + def requestZone(self): + if not self.game: + return + + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'setZone', [ + self.game.zoneId]) + + def requestGameZone(self): + if self.hasPicked == True: + avId = self.air.getAvatarIdFromSender() + if self.game: + self.game.playersObserving.append(avId) + + self.observers.append(avId) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleObserverExit, extraArgs = [ + avId]) + if self.game: + if self.game.fsm.getCurrentState().getName() == 'playing': + self.sendUpdateToAvatarId(avId, 'setGameZone', [ + self.checkersZoneId, + 1]) + else: + self.sendUpdateToAvatarId(avId, 'setGameZone', [ + self.checkersZoneId, + 0]) + + def leaveObserve(self): + avId = self.air.getAvatarIdFromSender() + if self.game: + if avId in self.game.playersObserving: + self.game.playersObserving.remove(avId) + + def handleObserverExit(self, avId): + if self.game and avId in self.game.playersObserving: + if self.game: + self.game.playersObserving.remove(avId) + self.ignore(self.air.getAvatarExitEvent(avId)) + + def requestExit(self): + self.notify.debug('requestExit') + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av: + if self.countFullSeats() > 0: + self.acceptExiter(avId) + else: + self.notify.debug('Player tried to exit after AI already kicked everyone out') + else: + self.notify.warning('avId: %s does not exist, but tried to exit picnicTable' % avId) + + def acceptExiter(self, avId): + seatIndex = self.findAvatar(avId) + if seatIndex == None: + if avId in self.observers: + self.sendUpdateToAvatarId(avId, 'emptySlot', [ + avId, + 255, + globalClockDelta.getRealNetworkTime()]) + + else: + self.seats[seatIndex] = None + self.ignore(self.air.getAvatarExitEvent(avId)) + self.sendUpdate('emptySlot', [ + avId, + seatIndex, + globalClockDelta.getRealNetworkTime()]) + self.getTableState() + numActive = 0 + for x in self.seats: + if x != None: + numActive = numActive + 1 + continue + + if self.game: + self.game.informGameOfPlayerLeave() + self.game.handlePlayerExit(avId) + + if numActive == 0: + self.isAccepting = True + if self.game: + self.game.handleEmptyGame() + self.game.requestDelete() + self.game = None + self.hasPicked = False + + def _DistributedPicnicTableAI__handleUnexpectedExit(self, avId): + self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly') + seatIndex = self.findAvatar(avId) + if seatIndex == None: + pass + + self.seats[seatIndex] = None + self.ignore(self.air.getAvatarExitEvent(avId)) + if self.game: + self.game.informGameOfPlayerLeave() + self.game.handlePlayerExit(avId) + self.hasPicked = False + + self.getTableState() + numActive = 0 + for x in self.seats: + if x != None: + numActive = numActive + 1 + continue + + if numActive == 0 and self.game: + simbase.air.deallocateZone(self.game.zoneId) + self.game.requestDelete() + self.game = None + self.gameDoId = None + + def informGameOfPlayerExit(self, avId): + self.game.handlePlayerExit(avId) + + def handleGameOver(self): + for x in self.observers: + self.acceptExiter(x) + self.observers.remove(x) + + if self.game: + self.game.playersObserving = [] + + for x in self.seats: + if x != None: + self.acceptExiter(x) + continue + + self.game = None + self.gameDoId = None + self.hasPicked = False + + def findAvatar(self, avId): + for i in xrange(len(self.seats)): + if self.seats[i] == avId: + return i + continue + + def countFullSeats(self): + avCounter = 0 + for i in self.seats: + if i: + avCounter += 1 + continue + + return avCounter + + def findAvailableSeat(self): + for i in xrange(len(self.seats)): + if self.seats[i] == None: + return i + continue + + def setCheckersZoneId(self, zoneId): + self.checkersZoneId = zoneId + + def setTableIndex(self, index): + self._tableIndex = index + + def getTableIndex(self): + return self._tableIndex diff --git a/toontown/safezone/DistributedTreasure.py b/toontown/safezone/DistributedTreasure.py new file mode 100755 index 00000000..707dc000 --- /dev/null +++ b/toontown/safezone/DistributedTreasure.py @@ -0,0 +1,165 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.safezone import TreasureGlobals + +class DistributedTreasure(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTreasure') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.av = None + self.treasureFlyTrack = None + self.nodePath = None + self.dropShadow = None + self.rejectSoundPath = 'phase_4/audio/sfx/ring_miss.ogg' + self.playSoundForRemoteToons = 1 + self.scale = 1.0 + self.shadow = 1 + self.fly = 1 + self.zOffset = 0.0 + self.billboard = 0 + self.treasureType = None + return + + def disable(self): + self.ignoreAll() + self.nodePath.detachNode() + DistributedObject.DistributedObject.disable(self) + + def delete(self): + if self.treasureFlyTrack: + self.treasureFlyTrack.finish() + self.treasureFlyTrack = None + DistributedObject.DistributedObject.delete(self) + self.nodePath.removeNode() + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.loadModel() + self.startAnimation() + self.nodePath.wrtReparentTo(render) + self.accept(self.uniqueName('entertreasureSphere'), self.handleEnterSphere) + + def handleEnterSphere(self, collEntry = None): + localAvId = base.localAvatar.getDoId() + if not self.fly: + self.handleGrab(localAvId) + self.d_requestGrab() + + def d_requestGrab(self): + self.sendUpdate('requestGrab', []) + + def getSphereRadius(self): + return 2.0 + + def loadModel(self): + modelPath, grabSoundPath = TreasureGlobals.TreasureModels[self.treasureType] + + if base.cr.newsManager.isHolidayRunning(VALENTOONS_DAY): + modelPath = TreasureGlobals.ValentineTreasureModel + + self.grabSound = base.loadSfx(grabSoundPath) + self.rejectSound = base.loadSfx(self.rejectSoundPath) + if self.nodePath == None: + self.makeNodePath() + else: + self.treasure.getChildren().detach() + model = loader.loadModel(modelPath) + model.instanceTo(self.treasure) + return + + def makeNodePath(self): + self.nodePath = NodePath(self.uniqueName('treasure')) + if self.billboard: + self.nodePath.setBillboardPointEye() + self.nodePath.setScale(0.9 * self.scale) + self.treasure = self.nodePath.attachNewNode('treasure') + if self.shadow: + if not self.dropShadow: + self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.dropShadow.setColor(0, 0, 0, 0.5) + self.dropShadow.setPos(0, 0, 0.025) + self.dropShadow.setScale(0.4 * self.scale) + self.dropShadow.flattenLight() + self.dropShadow.reparentTo(self.nodePath) + collSphere = CollisionSphere(0, 0, 0, self.getSphereRadius()) + collSphere.setTangible(0) + collNode = CollisionNode(self.uniqueName('treasureSphere')) + collNode.setIntoCollideMask(WallBitmask) + collNode.addSolid(collSphere) + self.collNodePath = self.nodePath.attachNewNode(collNode) + self.collNodePath.stash() + + def getParentNodePath(self): + return render + + def setTreasureType(self, treasureType): + self.treasureType = treasureType + + def setPosition(self, x, y, z): + if not self.nodePath: + self.makeNodePath() + self.nodePath.reparentTo(self.getParentNodePath()) + self.nodePath.setPos(x, y, z + self.zOffset) + self.collNodePath.unstash() + + def setGrab(self, avId): + if avId == 0: + return + if self.fly or avId != base.localAvatar.getDoId(): + self.handleGrab(avId) + + def setReject(self): + if self.treasureFlyTrack: + self.treasureFlyTrack.finish() + self.treasureFlyTrack = None + base.playSfx(self.rejectSound, node=self.nodePath) + self.treasureFlyTrack = Sequence(LerpColorScaleInterval(self.nodePath, 0.8, colorScale=VBase4(0, 0, 0, 0), startColorScale=VBase4(1, 1, 1, 1), blendType='easeIn'), LerpColorScaleInterval(self.nodePath, 0.2, colorScale=VBase4(1, 1, 1, 1), startColorScale=VBase4(0, 0, 0, 0), blendType='easeOut'), name=self.uniqueName('treasureFlyTrack')) + self.treasureFlyTrack.start() + return + + def handleGrab(self, avId): + self.collNodePath.stash() + self.avId = avId + if avId in self.cr.doId2do: + av = self.cr.doId2do[avId] + self.av = av + else: + self.nodePath.detachNode() + return + if self.playSoundForRemoteToons or self.avId == base.localAvatar.getDoId(): + base.playSfx(self.grabSound, node=self.nodePath) + if not self.fly: + self.nodePath.detachNode() + return + self.nodePath.wrtReparentTo(av) + if self.treasureFlyTrack: + self.treasureFlyTrack.finish() + self.treasureFlyTrack = None + avatarGoneName = self.av.uniqueName('disable') + self.accept(avatarGoneName, self.handleUnexpectedExit) + flytime = 1.0 + track = Sequence(LerpPosInterval(self.nodePath, flytime, pos=Point3(0, 0, 3), startPos=self.nodePath.getPos(), blendType='easeInOut'), Func(self.nodePath.detachNode), Func(self.ignore, avatarGoneName)) + if self.shadow: + self.treasureFlyTrack = Sequence(HideInterval(self.dropShadow), track, ShowInterval(self.dropShadow), name=self.uniqueName('treasureFlyTrack')) + else: + self.treasureFlyTrack = Sequence(track, name=self.uniqueName('treasureFlyTrack')) + self.treasureFlyTrack.start() + return + + def handleUnexpectedExit(self): + self.notify.warning('While getting treasure, ' + str(self.avId) + ' disconnected.') + if self.treasureFlyTrack: + self.treasureFlyTrack.finish() + self.treasureFlyTrack = None + return + + def getStareAtNodeAndOffset(self): + return (self.nodePath, Point3()) + + def startAnimation(self): + pass diff --git a/toontown/safezone/DistributedTreasureAI.py b/toontown/safezone/DistributedTreasureAI.py new file mode 100755 index 00000000..e9bc7f9c --- /dev/null +++ b/toontown/safezone/DistributedTreasureAI.py @@ -0,0 +1,40 @@ +from otp.ai.AIBase import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObjectAI + +class DistributedTreasureAI(DistributedObjectAI.DistributedObjectAI): + + def __init__(self, air, treasurePlanner, treasureType, x, y, z): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.treasurePlanner = treasurePlanner + self.treasureType = treasureType + self.pos = (x, y, z) + + def requestGrab(self): + avId = self.air.getAvatarIdFromSender() + self.treasurePlanner.grabAttempt(avId, self.getDoId()) + + def validAvatar(self, av): + return 1 + + def getTreasureType(self): + return self.treasureType + + def d_setGrab(self, avId): + self.sendUpdate('setGrab', [avId]) + + def d_setReject(self): + self.sendUpdate('setReject', []) + + def getPosition(self): + return self.pos + + def setPosition(self, x, y, z): + self.pos = (x, y, z) + + def b_setPosition(self, x, y, z): + self.setPosition(x, y, z) + self.d_setPosition(x, y, z) + + def d_setPosition(self, x, y, z): + self.sendUpdate('setPosition', [x, y, z]) diff --git a/toontown/safezone/DistributedTrolley.py b/toontown/safezone/DistributedTrolley.py new file mode 100755 index 00000000..54db5ea2 --- /dev/null +++ b/toontown/safezone/DistributedTrolley.py @@ -0,0 +1,387 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.distributed import DelayDelete +from direct.task.Task import Task +from toontown.hood import ZoneUtil + +class DistributedTrolley(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTrolley') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.localToonOnBoard = 0 + self.trolleyCountdownTime = base.config.GetFloat('trolley-countdown-time', TROLLEY_COUNTDOWN_TIME) + self.fsm = ClassicFSM.ClassicFSM('DistributedTrolley', [State.State('off', self.enterOff, self.exitOff, ['entering', + 'waitEmpty', + 'waitCountdown', + 'leaving']), + State.State('entering', self.enterEntering, self.exitEntering, ['waitEmpty']), + State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), + State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty', 'leaving']), + State.State('leaving', self.enterLeaving, self.exitLeaving, ['entering'])], 'off', 'off') + self.fsm.enterInitialState() + self.trolleyAwaySfx = base.loadSfx('phase_4/audio/sfx/SZ_trolley_away.ogg') + self.trolleyBellSfx = base.loadSfx('phase_4/audio/sfx/SZ_trolley_bell.ogg') + self.__toonTracks = {} + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.loader = self.cr.playGame.hood.loader + self.trolleyStation = self.loader.geom.find('**/*trolley_station*') + self.trolleyCar = self.trolleyStation.find('**/trolley_car') + self.trolleySphereNode = self.trolleyStation.find('**/trolley_sphere').node() + exitFog = Fog('TrolleyExitFog') + exitFog.setColor(0.0, 0.0, 0.0) + exitFog.setLinearOnsetPoint(30.0, 14.0, 0.0) + exitFog.setLinearOpaquePoint(37.0, 14.0, 0.0) + exitFog.setLinearFallback(70.0, 999.0, 1000.0) + self.trolleyExitFog = self.trolleyStation.attachNewNode(exitFog) + self.trolleyExitFogNode = exitFog + enterFog = Fog('TrolleyEnterFog') + enterFog.setColor(0.0, 0.0, 0.0) + enterFog.setLinearOnsetPoint(0.0, 14.0, 0.0) + enterFog.setLinearOpaquePoint(-7.0, 14.0, 0.0) + enterFog.setLinearFallback(70.0, 999.0, 1000.0) + self.trolleyEnterFog = self.trolleyStation.attachNewNode(enterFog) + self.trolleyEnterFogNode = enterFog + self.trolleyCar.setFogOff() + self.keys = self.trolleyCar.findAllMatches('**/key') + self.numKeys = self.keys.getNumPaths() + self.keyInit = [] + self.keyRef = [] + for i in xrange(self.numKeys): + key = self.keys[i] + key.setTwoSided(1) + ref = self.trolleyCar.attachNewNode('key' + `i` + 'ref') + ref.iPosHpr(key) + self.keyRef.append(ref) + self.keyInit.append(key.getTransform()) + + self.frontWheels = self.trolleyCar.findAllMatches('**/front_wheels') + self.numFrontWheels = self.frontWheels.getNumPaths() + self.frontWheelInit = [] + self.frontWheelRef = [] + for i in xrange(self.numFrontWheels): + wheel = self.frontWheels[i] + ref = self.trolleyCar.attachNewNode('frontWheel' + `i` + 'ref') + ref.iPosHpr(wheel) + self.frontWheelRef.append(ref) + self.frontWheelInit.append(wheel.getTransform()) + + self.backWheels = self.trolleyCar.findAllMatches('**/back_wheels') + self.numBackWheels = self.backWheels.getNumPaths() + self.backWheelInit = [] + self.backWheelRef = [] + for i in xrange(self.numBackWheels): + wheel = self.backWheels[i] + ref = self.trolleyCar.attachNewNode('backWheel' + `i` + 'ref') + ref.iPosHpr(wheel) + self.backWheelRef.append(ref) + self.backWheelInit.append(wheel.getTransform()) + + trolleyAnimationReset = Func(self.resetAnimation) + trolleyEnterStartPos = Point3(-20, 14, -1) + trolleyEnterEndPos = Point3(15, 14, -1) + trolleyEnterPos = Sequence(name='TrolleyEnterPos') + if base.wantFog: + trolleyEnterPos.append(Func(self.trolleyCar.setFog, self.trolleyEnterFogNode)) + trolleyEnterPos.append(self.trolleyCar.posInterval(TROLLEY_ENTER_TIME, trolleyEnterEndPos, startPos=trolleyEnterStartPos, blendType='easeOut')) + if base.wantFog: + trolleyEnterPos.append(Func(self.trolleyCar.setFogOff)) + trolleyEnterTrack = Sequence(trolleyAnimationReset, trolleyEnterPos, name='trolleyEnter') + keyAngle = round(TROLLEY_ENTER_TIME) * 360 + dist = Vec3(trolleyEnterEndPos - trolleyEnterStartPos).length() + wheelAngle = dist / (2.0 * math.pi * 0.95) * 360 + trolleyEnterAnimateInterval = LerpFunctionInterval(self.animateTrolley, duration=TROLLEY_ENTER_TIME, blendType='easeOut', extraArgs=[keyAngle, wheelAngle], name='TrolleyAnimate') + trolleyEnterSoundTrack = SoundInterval(self.trolleyAwaySfx, node=self.trolleyCar) + self.trolleyEnterTrack = Parallel(trolleyEnterTrack, trolleyEnterAnimateInterval, trolleyEnterSoundTrack) + trolleyExitStartPos = Point3(15, 14, -1) + trolleyExitEndPos = Point3(50, 14, -1) + trolleyExitPos = Sequence(name='TrolleyExitPos') + if base.wantFog: + trolleyExitPos.append(Func(self.trolleyCar.setFog, self.trolleyExitFogNode)) + trolleyExitPos.append(self.trolleyCar.posInterval(TROLLEY_EXIT_TIME, trolleyExitEndPos, startPos=trolleyExitStartPos, blendType='easeIn')) + if base.wantFog: + trolleyExitPos.append(Func(self.trolleyCar.setFogOff)) + trolleyExitBellInterval = SoundInterval(self.trolleyBellSfx, node=self.trolleyCar) + trolleyExitAwayInterval = SoundInterval(self.trolleyAwaySfx, node=self.trolleyCar) + keyAngle = round(TROLLEY_EXIT_TIME) * 360 + dist = Vec3(trolleyExitEndPos - trolleyExitStartPos).length() + wheelAngle = dist / (2.0 * math.pi * 0.95) * 360 + trolleyExitAnimateInterval = LerpFunctionInterval(self.animateTrolley, duration=TROLLEY_EXIT_TIME, blendType='easeIn', extraArgs=[keyAngle, wheelAngle], name='TrolleyAnimate') + self.trolleyExitTrack = Parallel(trolleyExitPos, trolleyExitBellInterval, trolleyExitAwayInterval, trolleyExitAnimateInterval, name=self.uniqueName('trolleyExit')) + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.fsm.request('off') + self.clearToonTracks() + self.trolleyExitFog.removeNode() + del self.trolleyExitFog + del self.trolleyExitFogNode + self.trolleyEnterFog.removeNode() + del self.trolleyEnterFog + del self.trolleyEnterFogNode + del self.loader + self.trolleyEnterTrack.pause() + self.trolleyEnterTrack = None + del self.trolleyEnterTrack + self.trolleyExitTrack.pause() + self.trolleyExitTrack = None + del self.trolleyExitTrack + del self.trolleyStation + del self.trolleyCar + del self.keys + del self.numKeys + del self.keyInit + del self.keyRef + del self.frontWheels + del self.numFrontWheels + del self.frontWheelInit + del self.frontWheelRef + del self.backWheels + del self.numBackWheels + del self.backWheelInit + del self.backWheelRef + return + + def delete(self): + del self.trolleyAwaySfx + del self.trolleyBellSfx + DistributedObject.DistributedObject.delete(self) + del self.fsm + + def setState(self, state, timestamp): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def handleEnterTrolleySphere(self, collEntry): + self.notify.debug('Entering Trolley Sphere....') + if base.localAvatar.getPos(render).getZ() < self.trolleyCar.getPos(render).getZ(): + return + self.loader.place.detectedTrolleyCollision() + + def handleEnterTrolley(self): + toon = base.localAvatar + self.sendUpdate('requestBoard', []) + + def fillSlot0(self, avId): + self.fillSlot(0, avId) + + def fillSlot1(self, avId): + self.fillSlot(1, avId) + + def fillSlot2(self, avId): + self.fillSlot(2, avId) + + def fillSlot3(self, avId): + self.fillSlot(3, avId) + + def fillSlot(self, index, avId): + if avId == 0: + pass + else: + if avId == base.localAvatar.getDoId(): + if not (self.fsm.getCurrentState().getName() == 'waitEmpty' or self.fsm.getCurrentState().getName() == 'waitCountdown'): + self.notify.warning("Can't board the trolley while in the '%s' state." % self.fsm.getCurrentState().getName()) + self.loader.place.fsm.request('walk') + return + if hasattr(self.loader.place, 'trolley') and self.loader.place.trolley: + self.loader.place.trolley.fsm.request('boarding', [self.trolleyCar]) + self.localToonOnBoard = 1 + self.loader.place.trolley.fsm.request('boarded') + else: + self.notify.warning("Can't board the trolley because it doesn't exist") + self.sendUpdate('requestExit') + if avId in self.cr.doId2do: + toon = self.cr.doId2do[avId] + toon.stopSmooth() + toon.wrtReparentTo(self.trolleyCar) + toon.setAnimState('run', 1.0) + toon.headsUp(-5, -4.5 + index * 3, 1.4) + sitStartDuration = toon.getDuration('sit-start') + track = Sequence(LerpPosInterval(toon, TOON_BOARD_TIME * 0.75, Point3(-5, -4.5 + index * 3, 1.4)), LerpHprInterval(toon, TOON_BOARD_TIME * 0.25, Point3(90, 0, 0)), Parallel(Sequence(Wait(sitStartDuration * 0.25), LerpPosInterval(toon, sitStartDuration * 0.25, Point3(-3.9, -4.5 + index * 3, 3.0))), ActorInterval(toon, 'sit-start')), Func(toon.setAnimState, 'Sit', 1.0), Func(self.clearToonTrack, avId), name=toon.uniqueName('fillTrolley'), autoPause=1) + track.delayDelete = DelayDelete.DelayDelete(toon, 'Trolley.fillSlot') + self.storeToonTrack(avId, track) + track.start() + else: + DistributedTrolley.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot board the trolley!') + + def emptySlot0(self, avId, timestamp): + self.emptySlot(0, avId, timestamp) + + def emptySlot1(self, avId, timestamp): + self.emptySlot(1, avId, timestamp) + + def emptySlot2(self, avId, timestamp): + self.emptySlot(2, avId, timestamp) + + def emptySlot3(self, avId, timestamp): + self.emptySlot(3, avId, timestamp) + + def notifyToonOffTrolley(self, toon): + toon.setAnimState('neutral', 1.0) + if toon == base.localAvatar: + if hasattr(self.loader.place, 'trolley') and self.loader.place.trolley: + self.loader.place.trolley.handleOffTrolley() + self.localToonOnBoard = 0 + else: + toon.startSmooth() + + def emptySlot(self, index, avId, timestamp): + if avId == 0: + pass + elif avId in self.cr.doId2do: + toon = self.cr.doId2do[avId] + toon.setHpr(self.trolleyCar, 90, 0, 0) + toon.wrtReparentTo(render) + toon.stopSmooth() + sitStartDuration = toon.getDuration('sit-start') + track = Sequence(Parallel(ActorInterval(toon, 'sit-start', startTime=sitStartDuration, endTime=0.0), Sequence(Wait(sitStartDuration * 0.5), LerpPosInterval(toon, sitStartDuration * 0.25, Point3(-5, -4.5 + index * 3, 1.4), other=self.trolleyCar))), Func(toon.setAnimState, 'run', 1.0), LerpPosInterval(toon, TOON_EXIT_TIME, Point3(21 - index * 3, -5, 0.02), other=self.trolleyStation), Func(self.notifyToonOffTrolley, toon), Func(self.clearToonTrack, avId), name=toon.uniqueName('emptyTrolley'), autoPause=1) + track.delayDelete = DelayDelete.DelayDelete(toon, 'Trolley.emptySlot') + self.storeToonTrack(avId, track) + track.start() + if avId == base.localAvatar.getDoId() and hasattr(self.loader.place, 'trolley') and self.loader.place.trolley: + self.loader.place.trolley.fsm.request('exiting') + else: + DistributedTrolley.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot exit the trolley!') + + def rejectBoard(self, avId): + self.loader.place.trolley.handleRejectBoard() + + def setMinigameZone(self, zoneId, minigameId): + self.localToonOnBoard = 0 + messenger.send('playMinigame', [zoneId, minigameId]) + + def __enableCollisions(self): + self.accept('entertrolley_sphere', self.handleEnterTrolleySphere) + self.accept('enterTrolleyOK', self.handleEnterTrolley) + self.trolleySphereNode.setCollideMask(ToontownGlobals.WallBitmask) + + def __disableCollisions(self): + self.ignore('entertrolley_sphere') + self.ignore('enterTrolleyOK') + self.trolleySphereNode.setCollideMask(BitMask32(0)) + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterEntering(self, ts): + self.trolleyEnterTrack.start(ts) + + def exitEntering(self): + self.trolleyEnterTrack.finish() + + def enterWaitEmpty(self, ts): + self.__enableCollisions() + + def exitWaitEmpty(self): + self.__disableCollisions() + + def enterWaitCountdown(self, ts): + self.__enableCollisions() + self.accept('trolleyExitButton', self.handleExitButton) + self.clockNode = TextNode('trolleyClock') + self.clockNode.setFont(ToontownGlobals.getSignFont()) + self.clockNode.setAlign(TextNode.ACenter) + self.clockNode.setTextColor(0.9, 0.1, 0.1, 1) + self.clockNode.setText('10') + self.clock = self.trolleyStation.attachNewNode(self.clockNode) + self.clock.setBillboardAxis() + self.clock.setPosHprScale(15.86, 13.82, 11.68, -0.0, 0.0, 0.0, 3.02, 3.02, 3.02) + if ts < self.trolleyCountdownTime: + self.countdown(self.trolleyCountdownTime - ts) + + def timerTask(self, task): + countdownTime = int(task.duration - task.time) + timeStr = str(countdownTime) + if self.clockNode.getText() != timeStr: + self.clockNode.setText(timeStr) + if task.time >= task.duration: + return Task.done + else: + return Task.cont + + def countdown(self, duration): + countdownTask = Task(self.timerTask) + countdownTask.duration = duration + taskMgr.remove('trolleyTimerTask') + return taskMgr.add(countdownTask, 'trolleyTimerTask') + + def handleExitButton(self): + self.sendUpdate('requestExit') + + def exitWaitCountdown(self): + self.__disableCollisions() + self.ignore('trolleyExitButton') + taskMgr.remove('trolleyTimerTask') + self.clock.removeNode() + del self.clock + del self.clockNode + + def enterLeaving(self, ts): + self.trolleyExitTrack.start(ts) + if self.localToonOnBoard: + if hasattr(self.loader.place, 'trolley') and self.loader.place.trolley: + self.loader.place.trolley.fsm.request('trolleyLeaving') + + def exitLeaving(self): + self.trolleyExitTrack.finish() + + def animateTrolley(self, t, keyAngle, wheelAngle): + for i in xrange(self.numKeys): + key = self.keys[i] + ref = self.keyRef[i] + key.setH(ref, t * keyAngle) + + for i in xrange(self.numFrontWheels): + frontWheel = self.frontWheels[i] + ref = self.frontWheelRef[i] + frontWheel.setH(ref, t * wheelAngle) + + for i in xrange(self.numBackWheels): + backWheel = self.backWheels[i] + ref = self.backWheelRef[i] + backWheel.setH(ref, t * wheelAngle) + + def resetAnimation(self): + for i in xrange(self.numKeys): + self.keys[i].setTransform(self.keyInit[i]) + + for i in xrange(self.numFrontWheels): + self.frontWheels[i].setTransform(self.frontWheelInit[i]) + + for i in xrange(self.numBackWheels): + self.backWheels[i].setTransform(self.backWheelInit[i]) + + def getStareAtNodeAndOffset(self): + return (self.trolleyCar, Point3(0, 0, 4)) + + def storeToonTrack(self, avId, track): + self.clearToonTrack(avId) + self.__toonTracks[avId] = track + + def clearToonTrack(self, avId): + oldTrack = self.__toonTracks.get(avId) + if oldTrack: + oldTrack.pause() + DelayDelete.cleanupDelayDeletes(oldTrack) + del self.__toonTracks[avId] + + def clearToonTracks(self): + keyList = [] + for key in self.__toonTracks: + keyList.append(key) + + for key in keyList: + if key in self.__toonTracks: + self.clearToonTrack(key) diff --git a/toontown/safezone/DistributedTrolleyAI.py b/toontown/safezone/DistributedTrolleyAI.py new file mode 100755 index 00000000..91d78dd2 --- /dev/null +++ b/toontown/safezone/DistributedTrolleyAI.py @@ -0,0 +1,286 @@ +from TrolleyConstants import * +from direct.directnotify.DirectNotifyGlobal import * +from direct.distributed import DistributedObjectAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +from otp.ai.AIBase import * +from toontown.minigame import MinigameCreatorAI +from toontown.quest import Quests +from toontown.toonbase.ToontownGlobals import * + + +class DistributedTrolleyAI(DistributedObjectAI.DistributedObjectAI): + notify = directNotify.newCategory('DistributedTrolleyAI') + + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.seats = [None, None, None, None] + self.accepting = 0 + self.trolleyCountdownTime = simbase.config.GetFloat('trolley-countdown-time', TROLLEY_COUNTDOWN_TIME) + self.fsm = ClassicFSM.ClassicFSM( + 'DistributedTrolleyAI', + [ + State.State('off', self.enterOff, self.exitOff, + ['entering']), + State.State('entering', self.enterEntering, self.exitEntering, + ['waitEmpty']), + State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, + ['waitCountdown']), + State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, + ['waitEmpty', 'allAboard']), + State.State('allAboard', self.enterAllAboard, self.exitAllAboard, + ['leaving', 'waitEmpty']), + State.State('leaving', self.enterLeaving, self.exitLeaving, + ['entering']) + ], 'off', 'off') + self.fsm.enterInitialState() + + def delete(self): + self.fsm.requestFinalState() + del self.fsm + DistributedObjectAI.DistributedObjectAI.delete(self) + + def findAvailableSeat(self): + for i in xrange(len(self.seats)): + if self.seats[i] is None: + return i + + def findAvatar(self, avId): + for i in xrange(len(self.seats)): + if self.seats[i] == avId: + return i + + def countFullSeats(self): + avCounter = 0 + for i in self.seats: + if i: + avCounter += 1 + return avCounter + + def rejectingBoardersHandler(self, avId): + self.rejectBoarder(avId) + + def rejectBoarder(self, avId): + self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId]) + + def acceptingBoardersHandler(self, avId): + self.notify.debug('acceptingBoardersHandler') + seatIndex = self.findAvailableSeat() + if seatIndex == None: + self.rejectBoarder(avId) + else: + self.acceptBoarder(avId, seatIndex) + + def acceptBoarder(self, avId, seatIndex): + self.notify.debug('acceptBoarder') + if self.findAvatar(avId) is not None: + return + self.seats[seatIndex] = avId + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + self.timeOfBoarding = globalClock.getRealTime() + self.sendUpdate('fillSlot' + str(seatIndex), [avId]) + self.waitCountdown() + + def __handleUnexpectedExit(self, avId): + self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly') + seatIndex = self.findAvatar(avId) + if seatIndex is None: + return + self.clearFullNow(seatIndex) + self.clearEmptyNow(seatIndex) + if self.countFullSeats() == 0: + self.waitEmpty() + + def rejectingExitersHandler(self, avId): + self.rejectExiter(avId) + + def rejectExiter(self, avId): + pass + + def acceptingExitersHandler(self, avId): + self.acceptExiter(avId) + + def acceptExiter(self, avId): + seatIndex = self.findAvatar(avId) + if seatIndex is None: + return + self.clearFullNow(seatIndex) + self.sendUpdate('emptySlot' + str(seatIndex), [ + avId, + globalClockDelta.getRealNetworkTime()]) + if self.countFullSeats() == 0: + self.waitEmpty() + taskMgr.doMethodLater(TOON_EXIT_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs = (seatIndex,)) + + def clearEmptyNow(self, seatIndex): + self.sendUpdate('emptySlot' + str(seatIndex), [0, globalClockDelta.getRealNetworkTime()]) + + def clearFullNow(self, seatIndex): + avId = self.seats[seatIndex] + if avId == 0: + self.notify.warning('Clearing an empty seat index: ' + str(seatIndex) + ' ... Strange...') + else: + self.seats[seatIndex] = None + self.sendUpdate('fillSlot' + str(seatIndex), [0]) + self.ignore(self.air.getAvatarExitEvent(avId)) + + def d_setState(self, state): + self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()]) + + def getState(self): + return self.fsm.getCurrentState().getName() + + def requestBoard(self, *args): + self.notify.debug('requestBoard') + avId = self.air.getAvatarIdFromSender() + if self.findAvatar(avId) != None: + self.notify.warning('Ignoring multiple requests from %s to board.' % avId) + return + av = self.air.doId2do.get(avId) + if av: + newArgs = (avId,) + args + if (av.hp > 0) and self.accepting: + self.acceptingBoardersHandler(*newArgs) + else: + self.rejectingBoardersHandler(*newArgs) + else: + self.notify.warning('avid: %s does not exist, but tried to board a trolley' % avId) + + def requestExit(self, *args): + self.notify.debug('requestExit') + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av: + newArgs = (avId,) + args + if self.accepting: + self.acceptingExitersHandler(*newArgs) + else: + self.rejectingExitersHandler(*newArgs) + else: + self.notify.warning('avId: %s does not exist, but tried to exit a trolley' % avId) + + def start(self): + self.enter() + + def enterOff(self): + self.accepting = 0 + if hasattr(self, 'doId'): + for seatIndex in xrange(4): + taskMgr.remove(self.uniqueName('clearEmpty-' + str(seatIndex))) + + def exitOff(self): + self.accepting = 0 + + def enter(self): + self.fsm.request('entering') + + def enterEntering(self): + self.d_setState('entering') + self.accepting = 0 + self.seats = [None, None, None, None] + taskMgr.doMethodLater(TROLLEY_ENTER_TIME, self.waitEmptyTask, self.uniqueName('entering-timer')) + + def exitEntering(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('entering-timer')) + + def waitEmptyTask(self, task): + self.waitEmpty() + return Task.done + + def waitEmpty(self): + self.fsm.request('waitEmpty') + + def enterWaitEmpty(self): + self.d_setState('waitEmpty') + self.accepting = 1 + + def exitWaitEmpty(self): + self.accepting = 0 + + def waitCountdown(self): + self.fsm.request('waitCountdown') + + def enterWaitCountdown(self): + self.d_setState('waitCountdown') + self.accepting = 1 + taskMgr.doMethodLater(self.trolleyCountdownTime, self.timeToGoTask, self.uniqueName('countdown-timer')) + + def timeToGoTask(self, task): + if self.countFullSeats() > 0: + self.allAboard() + else: + self.waitEmpty() + return Task.done + + def exitWaitCountdown(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('countdown-timer')) + + def allAboard(self): + self.fsm.request('allAboard') + + def enterAllAboard(self): + self.accepting = 0 + currentTime = globalClock.getRealTime() + elapsedTime = currentTime - self.timeOfBoarding + self.notify.debug('elapsed time: ' + str(elapsedTime)) + waitTime = max(TOON_BOARD_TIME - elapsedTime, 0) + taskMgr.doMethodLater(waitTime, self.leaveTask, self.uniqueName('waitForAllAboard')) + + def exitAllAboard(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('waitForAllAboard')) + + def leaveTask(self, task): + if self.countFullSeats() > 0: + self.leave() + else: + self.waitEmpty() + return Task.done + + def leave(self): + self.fsm.request('leaving') + + def enterLeaving(self): + self.d_setState('leaving') + self.accepting = 0 + taskMgr.doMethodLater(TROLLEY_EXIT_TIME, self.trolleyLeftTask, self.uniqueName('leaving-timer')) + + def trolleyLeftTask(self, task): + self.trolleyLeft() + return Task.done + + def trolleyLeft(self): + numPlayers = self.countFullSeats() + if numPlayers > 0: + newbieIds = [] + for avId in self.seats: + if avId: + toon = self.air.doId2do.get(avId) + if toon: + if Quests.avatarHasTrolleyQuest(toon): + if not Quests.avatarHasCompletedTrolleyQuest(toon): + newbieIds.append(avId) + playerArray = [] + for i in self.seats: + if i not in [None, 0]: + playerArray.append(i) + mgDict = MinigameCreatorAI.createMinigame( + self.air, playerArray, self.zoneId, newbieIds=newbieIds) + minigameZone = mgDict['minigameZone'] + minigameId = mgDict['minigameId'] + for seatIndex in xrange(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.sendUpdateToAvatarId(avId, 'setMinigameZone', [minigameZone, minigameId]) + self.clearFullNow(seatIndex) + else: + self.notify.warning('The trolley left, but was empty.') + self.enter() + + def exitLeaving(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('leaving-timer')) diff --git a/toontown/safezone/GSPlayground.py b/toontown/safezone/GSPlayground.py new file mode 100755 index 00000000..7f7d2630 --- /dev/null +++ b/toontown/safezone/GSPlayground.py @@ -0,0 +1,93 @@ +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +import Playground +from toontown.building import Elevator +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from toontown.racing import RaceGlobals +from direct.fsm import State + +class GSPlayground(Playground.Playground): + + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + self.parentFSM = parentFSM + self.startingBlockDoneEvent = 'startingBlockDone' + self.fsm.addState(State.State('startingBlock', self.enterStartingBlock, self.exitStartingBlock, ['walk'])) + state = self.fsm.getStateNamed('walk') + state.addTransition('startingBlock') + + def load(self): + Playground.Playground.load(self) + + def unload(self): + Playground.Playground.unload(self) + + def enter(self, requestStatus): + Playground.Playground.enter(self, requestStatus) + blimp = base.cr.playGame.hood.loader.geom.find('**/GS_blimp') + blimp.setPos(-70, 250, -70) + blimpBase = NodePath('blimpBase') + blimpBase.setPos(0, -200, 25) + blimpBase.setH(-40) + blimp.reparentTo(blimpBase) + blimpRoot = NodePath('blimpRoot') + blimpRoot.setPos(0, -70, 40) + blimpRoot.reparentTo(base.cr.playGame.hood.loader.geom) + blimpBase.reparentTo(blimpRoot) + self.rotateBlimp = blimpRoot.hprInterval(360, Vec3(360, 0, 0)) + self.rotateBlimp.loop() + + def exit(self): + Playground.Playground.exit(self) + self.rotateBlimp.finish() + + def enterTeleportIn(self, requestStatus): + reason = requestStatus.get('reason') + if reason == RaceGlobals.Exit_Barrier: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RaceTimeout, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + elif reason == RaceGlobals.Exit_Slow: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RacerTooSlow, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + elif reason == RaceGlobals.Exit_BarrierNoRefund: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RaceTimeoutNoRefund, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + Playground.Playground.enterTeleportIn(self, requestStatus) + + def __cleanupDialog(self, value): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + if hasattr(self, 'fsm'): + self.fsm.request('walk', [1]) + return + + def enterStartingBlock(self, distStartingBlock): + self.accept(self.startingBlockDoneEvent, self.handleStartingBlockDone) + self.startingBlock = Elevator.Elevator(self.fsm.getStateNamed('startingBlock'), self.startingBlockDoneEvent, distStartingBlock) + distStartingBlock.elevatorFSM = self.startingBlock + self.startingBlock.load() + self.startingBlock.enter() + + def exitStartingBlock(self): + self.ignore(self.startingBlockDoneEvent) + self.startingBlock.unload() + self.startingBlock.exit() + del self.startingBlock + + def detectedStartingBlockCollision(self, distStartingBlock): + self.fsm.request('startingBlock', [distStartingBlock]) + + def handleStartingBlockDone(self, doneStatus): + self.notify.debug('handling StartingBlock done event') + where = doneStatus['where'] + if where == 'reject': + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'racetrack': + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleStartingBlockDone') diff --git a/toontown/safezone/GSSafeZoneLoader.py b/toontown/safezone/GSSafeZoneLoader.py new file mode 100755 index 00000000..f29b38a1 --- /dev/null +++ b/toontown/safezone/GSSafeZoneLoader.py @@ -0,0 +1,81 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from toontown.hood import ZoneUtil +from toontown.safezone.SafeZoneLoader import SafeZoneLoader +from toontown.safezone.GSPlayground import GSPlayground + +class GSSafeZoneLoader(SafeZoneLoader): + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.musicFile = 'phase_6/audio/bgm/GS_SZ.ogg' + self.activityMusicFile = 'phase_6/audio/bgm/GS_KartShop.ogg' + self.dnaFile = 'phase_6/dna/goofy_speedway_sz.pdna' + self.safeZoneStorageDNAFile = 'phase_6/dna/storage_GS_sz.pdna' + del self.fsm + self.fsm = ClassicFSM.ClassicFSM('SafeZoneLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'playground', 'toonInterior']), + State.State('playground', self.enterPlayground, self.exitPlayground, ['quietZone', 'racetrack']), + State.State('toonInterior', self.enterToonInterior, self.exitToonInterior, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['playground', 'toonInterior', 'racetrack']), + State.State('racetrack', self.enterRacetrack, self.exitRacetrack, ['quietZone', 'playground']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + SafeZoneLoader.load(self) + self.birdSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.ogg', 'phase_4/audio/sfx/SZ_TC_bird2.ogg', 'phase_4/audio/sfx/SZ_TC_bird3.ogg']) + + def unload(self): + del self.birdSound + SafeZoneLoader.unload(self) + + def enterPlayground(self, requestStatus): + self.playgroundClass = GSPlayground + SafeZoneLoader.enterPlayground(self, requestStatus) + + def exitPlayground(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + SafeZoneLoader.exitPlayground(self) + self.playgroundClass = None + + def handlePlaygroundDone(self): + status = self.place.doneStatus + if self.enteringARace(status) and status.get('shardId') == None: + zoneId = status['zoneId'] + self.fsm.request('quietZone', [status]) + elif ZoneUtil.getBranchZone(status['zoneId']) == self.hood.hoodId and status['shardId'] == None: + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + messenger.send(self.doneEvent) + + def enteringARace(self, status): + if not status['where'] == 'racetrack': + return 0 + if ZoneUtil.isDynamicZone(status['zoneId']): + return status['hoodId'] == self.hood.hoodId + else: + return ZoneUtil.getHoodId(status['zoneId']) == self.hood.hoodId + + def enterRacetrack(self, requestStatus): + self.trackId = requestStatus['trackId'] + self.accept('raceOver', self.handleRaceOver) + self.accept('leavingRace', self.handleLeftRace) + base.transitions.fadeOut(t=0) + + def exitRacetrack(self): + del self.trackId + + def handleRaceOver(self): + print 'you done!!' + + def handleLeftRace(self): + req = {'loader': 'safeZoneLoader', + 'where': 'playground', + 'how': 'teleportIn', + 'zoneId': 8000, + 'hoodId': 8000, + 'shardId': None} + self.fsm.request('quietZone', [req]) + return diff --git a/toontown/safezone/GZPlayground.py b/toontown/safezone/GZPlayground.py new file mode 100755 index 00000000..f7de9b08 --- /dev/null +++ b/toontown/safezone/GZPlayground.py @@ -0,0 +1,127 @@ +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +import Playground +from toontown.building import Elevator +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from toontown.racing import RaceGlobals +from direct.fsm import State +from toontown.safezone import GolfKart + +class GZPlayground(Playground.Playground): + + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + self.parentFSM = parentFSM + self.golfKartBlockDoneEvent = 'golfKartBlockDone' + self.fsm.addState(State.State('golfKartBlock', self.enterGolfKartBlock, self.exitGolfKartBlock, ['walk'])) + state = self.fsm.getStateNamed('walk') + state.addTransition('golfKartBlock') + self.golfKartDoneEvent = 'golfKartDone' + + def load(self): + Playground.Playground.load(self) + self.hub = loader.loadModel('phase_6/models/golf/golf_hub2') + self.hub.reparentTo(render) + self.dnaroot = render.find('**/goofy_speedway_DNARoot') + self.dnaroot = base.cr.playGame.hood.loader.geom.find('**/goofy_speedway_DNARoot') + if not self.dnaroot.isEmpty(): + self.dnaroot.removeNode() + + def unload(self): + Playground.Playground.unload(self) + self.hub.removeNode() + + def enter(self, requestStatus): + Playground.Playground.enter(self, requestStatus) + blimp = base.cr.playGame.hood.loader.geom.find('**/GS_blimp') + if blimp.isEmpty(): + return + blimp.setPos(-70, 250, -70) + blimpBase = NodePath('blimpBase') + blimpBase.setPos(0, -200, 25) + blimpBase.setH(-40) + blimp.reparentTo(blimpBase) + blimpRoot = NodePath('blimpRoot') + blimpRoot.setPos(0, -70, 40) + blimpRoot.reparentTo(base.cr.playGame.hood.loader.geom) + blimpBase.reparentTo(blimpRoot) + self.rotateBlimp = blimpRoot.hprInterval(360, Vec3(360, 0, 0)) + self.rotateBlimp.loop() + + def exit(self): + Playground.Playground.exit(self) + if hasattr(self, 'rotateBlimp'): + self.rotateBlimp.finish() + + def enterTeleportIn(self, requestStatus): + reason = requestStatus.get('reason') + if reason == RaceGlobals.Exit_Barrier: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RaceTimeout, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + elif reason == RaceGlobals.Exit_Slow: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RacerTooSlow, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + elif reason == RaceGlobals.Exit_BarrierNoRefund: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RaceTimeoutNoRefund, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + Playground.Playground.enterTeleportIn(self, requestStatus) + + def __cleanupDialog(self, value): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + if hasattr(self, 'fsm'): + self.fsm.request('walk', [1]) + return + + def enterGolfKartBlock(self, golfKart): + base.localAvatar.laffMeter.start() + base.localAvatar.b_setAnimState('off', 1) + self.accept(self.golfKartDoneEvent, self.handleGolfKartDone) + self.trolley = GolfKart.GolfKart(self, self.fsm, self.golfKartDoneEvent, golfKart.getDoId()) + self.trolley.load() + self.trolley.enter() + + def exitGolfKartBlock(self): + base.localAvatar.laffMeter.stop() + self.ignore(self.trolleyDoneEvent) + self.trolley.unload() + self.trolley.exit() + del self.trolley + + def detectedGolfKartCollision(self, golfKart): + self.notify.debug('detectedGolfkartCollision()') + self.fsm.request('golfKartBlock', [golfKart]) + + def handleStartingBlockDone(self, doneStatus): + self.notify.debug('handling StartingBlock done event') + where = doneStatus['where'] + if where == 'reject': + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'racetrack': + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleStartingBlockDone') + + def handleGolfKartDone(self, doneStatus): + self.notify.debug('handling golf kart done event') + mode = doneStatus['mode'] + if mode == 'reject': + self.fsm.request('walk') + elif mode == 'exit': + self.fsm.request('walk') + elif mode == 'golfcourse': + self.doneStatus = {'loader': 'golfcourse', + 'where': 'golfcourse', + 'hoodId': self.loader.hood.id, + 'zoneId': doneStatus['zoneId'], + 'shardId': None, + 'courseId': doneStatus['courseId']} + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + mode + ' in handleGolfKartDone') + return diff --git a/toontown/safezone/GZSafeZoneLoader.py b/toontown/safezone/GZSafeZoneLoader.py new file mode 100755 index 00000000..e2103fb5 --- /dev/null +++ b/toontown/safezone/GZSafeZoneLoader.py @@ -0,0 +1,106 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.gui import DirectGui +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from panda3d.core import * +from toontown.hood import ZoneUtil +from toontown.safezone.SafeZoneLoader import SafeZoneLoader +from toontown.safezone.GZPlayground import GZPlayground +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +import random + +class GZSafeZoneLoader(SafeZoneLoader): + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.musicFile = 'phase_6/audio/bgm/GZ_SZ.ogg' + self.activityMusicFile = 'phase_6/audio/bgm/GS_KartShop.ogg' + self.dnaFile = 'phase_6/dna/golf_zone_sz.pdna' + self.safeZoneStorageDNAFile = 'phase_6/dna/storage_GZ_sz.pdna' + del self.fsm + self.fsm = ClassicFSM.ClassicFSM('SafeZoneLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'playground', 'toonInterior']), + State.State('playground', self.enterPlayground, self.exitPlayground, ['quietZone', 'golfcourse']), + State.State('toonInterior', self.enterToonInterior, self.exitToonInterior, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['playground', 'toonInterior', 'golfcourse']), + State.State('golfcourse', self.enterGolfCourse, self.exitGolfCourse, ['quietZone', 'playground']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + SafeZoneLoader.load(self) + self.birdSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.ogg', 'phase_4/audio/sfx/SZ_TC_bird2.ogg', 'phase_4/audio/sfx/SZ_TC_bird3.ogg']) + + def unload(self): + del self.birdSound + SafeZoneLoader.unload(self) + + def enterPlayground(self, requestStatus): + self.playgroundClass = GZPlayground + SafeZoneLoader.enterPlayground(self, requestStatus) + top = self.geom.find('**/linktunnel_bosshq_10000_DNARoot') + sign = top.find('**/Sign_5') + sign.node().setEffect(DecalEffect.make()) + locator = top.find('**/sign_origin') + signText = DirectGui.OnscreenText(text=TextEncoder.upper(TTLocalizer.BossbotHQ[-1]), font=ToontownGlobals.getSuitFont(), scale=TTLocalizer.GZSZLsignText, fg=(0, 0, 0, 1), mayChange=False, parent=sign) + signText.setPosHpr(locator, 0, 0, -0.3, 0, 0, 0) + signText.setDepthWrite(0) + + def exitPlayground(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + SafeZoneLoader.exitPlayground(self) + self.playgroundClass = None + return + + def handlePlaygroundDone(self): + status = self.place.doneStatus + if self.enteringAGolfCourse(status) and status.get('shardId') == None: + zoneId = status['zoneId'] + self.fsm.request('quietZone', [status]) + elif ZoneUtil.getBranchZone(status['zoneId']) == self.hood.hoodId and status['shardId'] == None: + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + messenger.send(self.doneEvent) + return + + def enteringARace(self, status): + if not status['where'] == 'golfcourse': + return 0 + if ZoneUtil.isDynamicZone(status['zoneId']): + return status['hoodId'] == self.hood.hoodId + else: + return ZoneUtil.getHoodId(status['zoneId']) == self.hood.hoodId + + def enteringAGolfCourse(self, status): + if not status['where'] == 'golfcourse': + return 0 + if ZoneUtil.isDynamicZone(status['zoneId']): + return status['hoodId'] == self.hood.hoodId + else: + return ZoneUtil.getHoodId(status['zoneId']) == self.hood.hoodId + + def enterGolfCourse(self, requestStatus): + if 'curseId' in requestStatus: + self.golfCourseId = requestStatus['courseId'] + else: + self.golfCourseId = 0 + self.accept('raceOver', self.handleRaceOver) + self.accept('leavingGolf', self.handleLeftGolf) + base.transitions.irisOut(t=0.2) + + def exitGolfCourse(self): + del self.golfCourseId + + def handleRaceOver(self): + print 'you done!!' + + def handleLeftGolf(self): + req = {'loader': 'safeZoneLoader', + 'where': 'playground', + 'how': 'teleportIn', + 'zoneId': 17000, + 'hoodId': 17000, + 'shardId': None} + self.fsm.request('quietZone', [req]) + return diff --git a/toontown/safezone/GameGlobals.py b/toontown/safezone/GameGlobals.py new file mode 100755 index 00000000..63c7723e --- /dev/null +++ b/toontown/safezone/GameGlobals.py @@ -0,0 +1,5 @@ +TutorialMenu = 1 +GameMenu = 1 +CheckersGameIndex = 0 +ChineseCheckersGameIndex = 1 +FindFourGameIndex = 2 diff --git a/toontown/safezone/GameMenu.py b/toontown/safezone/GameMenu.py new file mode 100755 index 00000000..ec7b82a7 --- /dev/null +++ b/toontown/safezone/GameMenu.py @@ -0,0 +1,160 @@ +from panda3d.core import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.safezone import GameGlobals + +class GameMenu(DirectFrame): + + def __init__(self, picnicFunction, menuType): + self.picnicFunction = picnicFunction + DirectFrame.__init__( + self, + pos=(0.0, 0.0, 0.85), + image_color=ToontownGlobals.GlobalDialogColor, + image_scale=(1.8, 0.9, 0.13), + text='', + text_scale=0.05) + self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui.bam') + self.upButton = self.buttonModels.find('**//InventoryButtonUp') + self.downButton = self.buttonModels.find('**/InventoryButtonDown') + self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover') + self['image'] = DGG.getDefaultDialogGeom() + if menuType == GameGlobals.TutorialMenu: + self.title = DirectLabel( + self, + relief=None, + text=TTLocalizer.PicnicTableMenuTutorial, + text_pos=(0.0, -0.038), + text_fg=(1, 0, 0, 1), + text_scale=0.09, + text_font=ToontownGlobals.getSignFont(), + text_shadow=(1, 1, 1, 1)) + else: + self.title = DirectLabel( + self, + relief=None, + text=TTLocalizer.PicnicTableMenuSelect, + text_pos=(0.0, -0.04), + text_fg=(1, 0, 0, 1), + text_scale=0.09, + text_font=ToontownGlobals.getSignFont(), + text_shadow=(1, 1, 1, 1)) + self.selectionButtons = loader.loadModel('phase_6/models/golf/picnic_game_menu.bam') + btn1 = self.selectionButtons.find('**/Btn1') + btn2 = self.selectionButtons.find('**/Btn2') + btn3 = self.selectionButtons.find('**/Btn3') + self.ChineseCheckers = DirectButton( + self, + image=(btn1.find('**/checkersBtnUp'), btn1.find('**/checkersBtnDn'), btn1.find('**/checkersBtnHi'), btn1.find('**/checkersBtnUp')), + scale=0.36, + relief=0, + pos=(0, 0, -0.7), + command=self.chineseCheckersSelected) + self.Checkers = DirectButton( + self, + image=(btn2.find('**/regular_checkersBtnUp'), btn2.find('**/regular_checkersBtnDn'), btn2.find('**/regular_checkersBtnHi'), btn2.find('**/regular_checkersBtnUp')), + scale=0.36, + relief=0, + pos=(0.8, 0, -0.7), + command=self.checkersSelected) + self.FindFour = DirectButton( + self, + image=(btn3.find('**/findfourBtnUp'), btn3.find('**/findfourBtnDn'), btn3.find('**/findfourBtnHi'), btn3.find('**/findfourBtnUp')), + scale=0.36, + relief=0, + pos=(-0.8, 0, -0.7), + command=self.findFourSelected) + if not base.config.GetBool('want-checkers', 1): + self.Checkers['command'] = self.doNothing() + self.Checkers.setColor(0.7, 0.7, 0.7, 0.7) + if not base.config.GetBool('want-chinese-checkers', 1): + self.ChineseCheckers['command'] = self.doNothing() + self.ChineseCheckers.setColor(0.7, 0.7, 0.7, 0.7) + if not base.config.GetBool('want-find-four', 1): + self.FindFour['command'] = self.doNothing() + self.FindFour.setColor(0.7, 0.7, 0.7, 0.7) + self.chineseText = OnscreenText( + text='Chinese Checkers', + pos=(0, 0.56, -0.8), + scale=0.15, + fg=Vec4(1, 1, 1, 1), + align=TextNode.ACenter, + font=ToontownGlobals.getMinnieFont(), + wordwrap=7, + shadow=(0, 0, 0, 0.8), + shadowOffset=(-0.1, -0.1), + mayChange=True) + self.chineseText.setR(-8) + self.checkersText = OnscreenText( + text='Checkers', + pos=(0.81, -.1, -0.8), + scale=0.15, + fg=Vec4(1, 1, 1, 1), + align=TextNode.ACenter, + font=ToontownGlobals.getMinnieFont(), + wordwrap=7, + shadow=(0, 0, 0, 0.8), + shadowOffset=(0.1, -0.1), + mayChange=True) + self.findFourText = OnscreenText( + text='Find Four', + pos=(-0.81, -.08, -0.8), + scale=0.15, + fg=Vec4(1, 1, 1, 1), + align=TextNode.ACenter, + font=ToontownGlobals.getMinnieFont(), + wordwrap=8, + shadow=(0, 0, 0, 0.8), + shadowOffset=(-0.1, -0.1), + mayChange=True) + self.exitButton = DirectButton( + relief=None, + text=TTLocalizer.PicnicTableCancelButton, + text_fg=(1, 1, 0.65, 1), + text_pos=(0, -0.23), + text_scale=0.8, + image=(self.upButton, self.downButton, self.rolloverButton), + image_color=(1, 0, 0, 1), + image_scale=(20, 1, 11), + pos=(0, 0, -0.4), + scale=0.15, + command=lambda self=self: self.cancelButtonPushed()) + self.findFourText.setR(-8) + self.checkersText.setR(8) + + def delete(self): + self.removeButtons() + + def removeButtons(self): + self.ChineseCheckers.destroy() + self.Checkers.destroy() + self.FindFour.destroy() + self.chineseText.destroy() + self.checkersText.destroy() + self.findFourText.destroy() + self.exitButton.destroy() + DirectFrame.destroy(self) + + def checkersSelected(self): + self.picnicFunction(GameGlobals.CheckersGameIndex) + self.picnicFunction = lambda gameIndex: None + + def chineseCheckersSelected(self): + self.picnicFunction(GameGlobals.ChineseCheckersGameIndex) + self.picnicFunction = lambda gameIndex: None + + def findFourSelected(self): + self.picnicFunction(GameGlobals.FindFourGameIndex) + self.picnicFunction = lambda gameIndex: None + + def cancelButtonPushed(self): + self.picnicFunction(-1) + self.picnicFunction = lambda gameIndex: None + + def doNothing(self): + pass diff --git a/toontown/safezone/GameTutorials.py b/toontown/safezone/GameTutorials.py new file mode 100755 index 00000000..b5faf4bd --- /dev/null +++ b/toontown/safezone/GameTutorials.py @@ -0,0 +1,336 @@ +from direct.gui.DirectGui import * +from direct.fsm import FSM +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from panda3d.core import * +from direct.interval.IntervalGlobal import * + +class ChineseTutorial(DirectFrame, FSM.FSM): + + def __init__(self, doneFunction, doneEvent = None, callback = None): + FSM.FSM.__init__(self, 'ChineseTutorial') + self.doneFunction = doneFunction + base.localAvatar.startSleepWatch(self.handleQuit) + self.doneEvent = doneEvent + self.callback = callback + self.setStateArray(['Page1', 'Page2', 'Quit']) + base.localAvatar.startSleepWatch(self.handleQuit) + DirectFrame.__init__(self, pos=(-0.7, 0.0, 0.0), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.5, 1.0), text='', text_scale=0.06) + self.accept('stoppedAsleep', self.handleQuit) + self['image'] = DGG.getDefaultDialogGeom() + self.title = DirectLabel(self, relief=None, text='', text_pos=(0.0, 0.4), text_fg=(1, 0, 0, 1), text_scale=0.13, text_font=ToontownGlobals.getSignFont()) + images = loader.loadModel('phase_6/models/golf/checker_tutorial.bam') + images.setTransparency(1) + self.iPage1 = images.find('**/tutorialPage1*') + self.iPage1.reparentTo(aspect2d) + self.iPage1.setPos(0.43, -0.1, 0.0) + self.iPage1.setScale(13.95) + self.iPage1.setTransparency(1) + self.iPage1.hide() + self.iPage1.getChildren()[1].hide() + self.iPage2 = images.find('**/tutorialPage3*') + self.iPage2.reparentTo(aspect2d) + self.iPage2.setPos(0.43, -0.1, 0.5) + self.iPage2.setScale(13.95) + self.iPage2.setTransparency(1) + self.iPage2.hide() + self.iPage3 = images.find('**/tutorialPage2*') + self.iPage3.reparentTo(aspect2d) + self.iPage3.setPos(0.43, -0.1, -0.5) + self.iPage3.setScale(13.95) + self.iPage3.setTransparency(1) + self.iPage3.hide() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.bNext = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), relief=None, text=TTLocalizer.ChineseTutorialNext, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.35, -0.3, -0.33), command=self.requestNext) + self.bPrev = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), image_scale=(-1.0, 1.0, 1.0), relief=None, text=TTLocalizer.ChineseTutorialPrev, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.35, -0.3, -0.33), command=self.requestPrev) + self.bQuit = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.ChineseTutorialDone, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, -0.3, -0.33), command=self.handleQuit) + self.bQuit.hide() + buttons.removeNode() + gui.removeNode() + self.request('Page1') + return + + def __del__(self): + self.cleanup() + + def enterPage1(self, *args): + self.bNext.show() + self.title['text'] = (TTLocalizer.ChineseTutorialTitle1,) + self['text'] = TTLocalizer.ChinesePage1 + self['text_pos'] = (0.0, 0.23) + self['text_wordwrap'] = 13.5 + self.bPrev['state'] = DGG.DISABLED + self.bPrev.hide() + self.bNext['state'] = DGG.NORMAL + self.iPage1.show() + self.blinker = Sequence() + obj = self.iPage1.getChildren()[1] + self.iPage1.getChildren()[1].show() + self.blinker.append(LerpColorInterval(obj, 0.5, Vec4(0.5, 0.5, 0, 0.0), Vec4(0.2, 0.2, 0.2, 1))) + self.blinker.append(LerpColorInterval(obj, 0.5, Vec4(0.2, 0.2, 0.2, 1), Vec4(0.5, 0.5, 0, 0.0))) + self.blinker.loop() + + def exitPage1(self, *args): + self.bPrev['state'] = DGG.NORMAL + self.iPage1.hide() + self.iPage1.getChildren()[1].hide() + self.blinker.finish() + + def enterPage2(self, *args): + self.bPrev.show() + self.title['text'] = (TTLocalizer.ChineseTutorialTitle2,) + self['text'] = TTLocalizer.ChinesePage2 + self['text_pos'] = (0.0, 0.28) + self['text_wordwrap'] = 12.5 + self.bNext['state'] = DGG.DISABLED + self.bNext.hide() + self.iPage2.show() + self.iPage3.show() + self.bQuit.show() + + def exitPage2(self, *args): + self.iPage2.hide() + self.bQuit.hide() + self.iPage3.hide() + + def enterQuit(self, *args): + self.iPage1.removeNode() + self.iPage2.removeNode() + self.iPage3.removeNode() + self.bNext.destroy() + self.bPrev.destroy() + self.bQuit.destroy() + DirectFrame.destroy(self) + + def exitQuit(self, *args): + pass + + def handleQuit(self, task = None): + base.cr.playGame.getPlace().setState('walk') + self.forceTransition('Quit') + self.doneFunction() + if task != None: + task.done + return + + +class CheckersTutorial(DirectFrame, FSM.FSM): + + def __init__(self, doneFunction, doneEvent = None, callback = None): + FSM.FSM.__init__(self, 'CheckersTutorial') + self.doneFunction = doneFunction + base.localAvatar.startSleepWatch(self.handleQuit) + self.doneEvent = doneEvent + self.callback = callback + self.setStateArray(['Page1', + 'Page2', + 'Page3', + 'Quit']) + DirectFrame.__init__(self, pos=(-0.7, 0.0, 0.0), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.5, 1.0), text='', text_scale=0.06) + self.accept('stoppedAsleep', self.handleQuit) + self['image'] = DGG.getDefaultDialogGeom() + self.title = DirectLabel(self, relief=None, text='', text_pos=(0.0, 0.4), text_fg=(1, 0, 0, 1), text_scale=0.13, text_font=ToontownGlobals.getSignFont()) + images = loader.loadModel('phase_6/models/golf/regularchecker_tutorial.bam') + images.setTransparency(1) + self.iPage1 = images.find('**/tutorialPage1*') + self.iPage1.reparentTo(aspect2d) + self.iPage1.setPos(0.43, -0.1, 0.0) + self.iPage1.setScale(0.4) + self.iPage1.setTransparency(1) + self.iPage1.hide() + self.iPage2 = images.find('**/tutorialPage2*') + self.iPage2.reparentTo(aspect2d) + self.iPage2.setPos(0.43, -0.1, 0.0) + self.iPage2.setScale(0.4) + self.iPage2.setTransparency(1) + self.iPage2.hide() + self.iPage3 = images.find('**/tutorialPage3*') + self.iPage3.reparentTo(aspect2d) + self.iPage3.setPos(0.6, -0.1, 0.5) + self.iPage3.setScale(0.4) + self.iPage3.setTransparency(1) + self.obj = self.iPage3.find('**/king*') + self.iPage3.hide() + self.iPage4 = images.find('**/tutorialPage4*') + self.iPage4.reparentTo(aspect2d) + self.iPage4.setPos(0.6, -0.1, -0.5) + self.iPage4.setScale(0.4) + self.iPage4.setTransparency(1) + self.iPage4.hide() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.bNext = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), relief=None, text=TTLocalizer.ChineseTutorialNext, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.08), pos=(0.35, -0.3, -0.38), command=self.requestNext) + self.bPrev = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), image_scale=(-1.0, 1.0, 1.0), relief=None, text=TTLocalizer.ChineseTutorialPrev, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.08), pos=(-0.35, -0.3, -0.38), command=self.requestPrev) + self.bQuit = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.ChineseTutorialDone, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, -0.3, -0.38), command=self.handleQuit) + self.bQuit.hide() + buttons.removeNode() + gui.removeNode() + self.request('Page1') + return + + def __del__(self): + self.cleanup() + + def enterPage1(self, *args): + self.bNext.show() + self.title['text'] = (TTLocalizer.ChineseTutorialTitle1,) + self['text'] = TTLocalizer.CheckersPage1 + self['text_pos'] = (0.0, 0.23) + self['text_wordwrap'] = 13.5 + self['text_scale'] = 0.06 + self.bPrev['state'] = DGG.DISABLED + self.bPrev.hide() + self.bNext['state'] = DGG.NORMAL + self.iPage1.show() + + def exitPage1(self, *args): + self.bPrev['state'] = DGG.NORMAL + self.iPage1.hide() + + def enterPage2(self, *args): + self.bPrev.show() + self.bNext.show() + self.title['text'] = (TTLocalizer.ChineseTutorialTitle2,) + self['text'] = TTLocalizer.CheckersPage2 + self['text_pos'] = (0.0, 0.28) + self['text_wordwrap'] = 12.5 + self['text_scale'] = 0.06 + self.bNext['state'] = DGG.NORMAL + self.iPage2.show() + + def exitPage2(self, *args): + self.iPage2.hide() + + def enterPage3(self, *args): + self.bPrev.show() + self.title['text'] = (TTLocalizer.ChineseTutorialTitle2,) + self['text'] = TTLocalizer.CheckersPage3 + '\n\n' + TTLocalizer.CheckersPage4 + self['text_pos'] = (0.0, 0.32) + self['text_wordwrap'] = 19 + self['text_scale'] = 0.05 + self.bNext['state'] = DGG.DISABLED + self.blinker = Sequence() + self.blinker.append(LerpColorInterval(self.obj, 0.5, Vec4(0.5, 0.5, 0, 0.0), Vec4(0.9, 0.9, 0, 1))) + self.blinker.append(LerpColorInterval(self.obj, 0.5, Vec4(0.9, 0.9, 0, 1), Vec4(0.5, 0.5, 0, 0.0))) + self.blinker.loop() + self.bNext.hide() + self.iPage3.show() + self.iPage4.show() + self.bQuit.show() + + def exitPage3(self, *args): + self.blinker.finish() + self.iPage3.hide() + self.bQuit.hide() + self.iPage4.hide() + + def enterQuit(self, *args): + self.iPage1.removeNode() + self.iPage2.removeNode() + self.iPage3.removeNode() + self.bNext.destroy() + self.bPrev.destroy() + self.bQuit.destroy() + DirectFrame.destroy(self) + + def exitQuit(self, *args): + pass + + def handleQuit(self, task = None): + self.forceTransition('Quit') + base.cr.playGame.getPlace().setState('walk') + self.doneFunction() + if task != None: + task.done + return + +class FindFourTutorial(DirectFrame, FSM.FSM): + + def __init__(self, doneFunction, doneEvent = None, callback = None): + FSM.FSM.__init__(self, 'FindFourTutorial') + self.doneFunction = doneFunction + base.localAvatar.startSleepWatch(self.handleQuit) + self.doneEvent = doneEvent + self.callback = callback + self.setStateArray(['Page1', 'Page2', 'Quit']) + base.localAvatar.startSleepWatch(self.handleQuit) + DirectFrame.__init__(self, pos=(-0.7, 0.0, 0.0), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.5, 1.0), text='', text_scale=0.06) + self.accept('stoppedAsleep', self.handleQuit) + self['image'] = DGG.getDefaultDialogGeom() + self.title = DirectLabel(self, relief=None, text='', text_pos=(0.0, 0.4), text_fg=(1, 0, 0, 1), text_scale=0.13, text_font=ToontownGlobals.getSignFont()) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.bNext = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), relief=None, text=TTLocalizer.ChineseTutorialNext, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.35, -0.3, -0.33), command=self.requestNext) + self.bPrev = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), image_scale=(-1.0, 1.0, 1.0), relief=None, text=TTLocalizer.ChineseTutorialPrev, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.35, -0.3, -0.33), command=self.requestPrev) + self.bQuit = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.ChineseTutorialDone, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, -0.3, -0.33), command=self.handleQuit) + self.bQuit.hide() + buttons.removeNode() + gui.removeNode() + self.request('Page1') + return + + def __del__(self): + self.cleanup() + + def enterPage1(self, *args): + self.bNext.show() + self.title['text'] = (TTLocalizer.ChineseTutorialTitle1,) + self['text'] = TTLocalizer.FindFourPage1 + self['text_pos'] = (0.0, 0.23) + self['text_wordwrap'] = 13.5 + self.bPrev['state'] = DGG.DISABLED + self.bPrev.hide() + self.bNext['state'] = DGG.NORMAL + + def exitPage1(self, *args): + self.bPrev['state'] = DGG.NORMAL + + def enterPage2(self, *args): + self.bPrev.show() + self.title['text'] = (TTLocalizer.ChineseTutorialTitle2,) + self['text'] = TTLocalizer.FindFourPage2 + self['text_pos'] = (0.0, 0.28) + self['text_wordwrap'] = 12.5 + self.bNext['state'] = DGG.DISABLED + self.bNext.hide() + self.bQuit.show() + + def exitPage2(self, *args): + self.bQuit.hide() + + def enterQuit(self, *args): + self.bNext.destroy() + self.bPrev.destroy() + self.bQuit.destroy() + DirectFrame.destroy(self) + + def exitQuit(self, *args): + pass + + def handleQuit(self, task = None): + base.cr.playGame.getPlace().setState('walk') + self.forceTransition('Quit') + self.doneFunction() + if task != None: + task.done + return diff --git a/toontown/safezone/GolfKart.py b/toontown/safezone/GolfKart.py new file mode 100755 index 00000000..c08f92ae --- /dev/null +++ b/toontown/safezone/GolfKart.py @@ -0,0 +1,222 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil + +class GolfKart(StateData.StateData): + + def __init__(self, safeZone, parentFSM, doneEvent, golfCourse): + StateData.StateData.__init__(self, doneEvent) + self.golfCourse = golfCourse + self.fsm = ClassicFSM.ClassicFSM('GolfKart', [ + State.State('start', + self.enterStart, + self.exitStart, [ + 'requestBoard', + 'trolleyHFA', + 'trolleyTFA']), + State.State('trolleyHFA', + self.enterTrolleyHFA, + self.exitTrolleyHFA, [ + 'final']), + State.State('trolleyTFA', + self.enterTrolleyTFA, + self.exitTrolleyTFA, [ + 'final']), + State.State('requestBoard', + self.enterRequestBoard, + self.exitRequestBoard, [ + 'boarding']), + State.State('boarding', + self.enterBoarding, + self.exitBoarding, [ + 'boarded']), + State.State('boarded', + self.enterBoarded, + self.exitBoarded, [ + 'requestExit', + 'trolleyLeaving', + 'final']), + State.State('requestExit', + self.enterRequestExit, + self.exitRequestExit, [ + 'exiting', + 'trolleyLeaving']), + State.State('trolleyLeaving', + self.enterTrolleyLeaving, + self.exitTrolleyLeaving, [ + 'final']), + State.State('exiting', + self.enterExiting, + self.exitExiting, [ + 'final']), + State.State('final', + self.enterFinal, + self.exitFinal, [ + 'start'])], + 'start', 'final') + self.parentFSM = parentFSM + return None + + def load(self): + self.parentFSM.getStateNamed('golfKartBlock').addChild(self.fsm) + self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + self.upButton = self.buttonModels.find('**//InventoryButtonUp') + self.downButton = self.buttonModels.find('**/InventoryButtonDown') + self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover') + + def unload(self): + self.parentFSM.getStateNamed('trolley').removeChild(self.fsm) + del self.fsm + del self.parentFSM + self.buttonModels.removeNode() + del self.buttonModels + del self.upButton + del self.downButton + del self.rolloverButton + + def enter(self): + self.fsm.enterInitialState() + if base.localAvatar.hp > 0: + messenger.send('enterGolfKartOK_%d' % self.golfCourse) + self.fsm.request('requestBoard') + else: + self.fsm.request('trolleyHFA') + return None + + def exit(self): + self.ignoreAll() + return None + + def enterStart(self): + return None + + def exitStart(self): + return None + + def enterTrolleyHFA(self): + self.noTrolleyBox = TTDialog.TTGlobalDialog(message=TTLocalizer.TrolleyHFAMessage, doneEvent='noTrolleyAck', style=TTDialog.Acknowledge) + self.noTrolleyBox.show() + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('noTrolleyAck', self.__handleNoTrolleyAck) + + def exitTrolleyHFA(self): + self.ignore('noTrolleyAck') + self.noTrolleyBox.cleanup() + del self.noTrolleyBox + + def enterTrolleyTFA(self): + self.noTrolleyBox = TTDialog.TTGlobalDialog(message=TTLocalizer.TrolleyTFAMessage, doneEvent='noTrolleyAck', style=TTDialog.Acknowledge) + self.noTrolleyBox.show() + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('noTrolleyAck', self.__handleNoTrolleyAck) + + def exitTrolleyTFA(self): + self.ignore('noTrolleyAck') + self.noTrolleyBox.cleanup() + del self.noTrolleyBox + + def __handleNoTrolleyAck(self): + ntdoneStatus = self.noTrolleyBox.doneStatus + if ntdoneStatus == 'ok': + doneStatus = {} + doneStatus['mode'] = 'reject' + messenger.send(self.doneEvent, [doneStatus]) + else: + self.notify.error('Unrecognized doneStatus: ' + str(ntdoneStatus)) + + def enterRequestBoard(self): + return None + + def handleRejectBoard(self): + doneStatus = {} + doneStatus['mode'] = 'reject' + messenger.send(self.doneEvent, [doneStatus]) + + def exitRequestBoard(self): + return None + + def enterBoarding(self, nodePath): + camera.wrtReparentTo(nodePath) + heading = PythonUtil.fitDestAngle2Src(camera.getH(nodePath), 180) + self.cameraBoardTrack = LerpPosHprInterval(camera, 1.5, Point3(0, 18, 8), Point3(heading, -10, 0)) + self.cameraBoardTrack.start() + return None + + def exitBoarding(self): + self.ignore('boardedTrolley') + return None + + def enterBoarded(self): + self.enableExitButton() + return None + + def exitBoarded(self): + self.cameraBoardTrack.finish() + self.disableExitButton() + return None + + def enableExitButton(self): + self.exitButton = DirectButton(relief=None, text=TTLocalizer.TrolleyHopOff, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0, 0, 0.8), scale=0.15, command=lambda self = self: self.fsm.request('requestExit')) + return + + def disableExitButton(self): + self.exitButton.destroy() + + def enterRequestExit(self): + messenger.send('trolleyExitButton') + return None + + def exitRequestExit(self): + return None + + def enterTrolleyLeaving(self): + self.acceptOnce('playMinigame', self.handlePlayMinigame) + self.acceptOnce('playGolf', self.handlePlayGolf) + return None + + def handlePlayMinigame(self, zoneId, minigameId): + base.localAvatar.b_setParent(ToontownGlobals.SPHidden) + doneStatus = {} + doneStatus['mode'] = 'minigame' + doneStatus['zoneId'] = zoneId + doneStatus['minigameId'] = minigameId + messenger.send(self.doneEvent, [doneStatus]) + + def handlePlayGolf(self, zoneId, courseId): + base.localAvatar.b_setParent(ToontownGlobals.SPHidden) + doneStatus = {} + doneStatus['mode'] = 'golfcourse' + doneStatus['zoneId'] = zoneId + doneStatus['courseId'] = courseId + messenger.send(self.doneEvent, [doneStatus]) + + def exitTrolleyLeaving(self): + self.ignore('playMinigame') + taskMgr.remove('leavingCamera') + return None + + def enterExiting(self): + return None + + def handleOffTrolley(self): + doneStatus = {} + doneStatus['mode'] = 'exit' + messenger.send(self.doneEvent, [doneStatus]) + return None + + def exitExiting(self): + return None + + def enterFinal(self): + return None + + def exitFinal(self): + return None diff --git a/toontown/safezone/MMPlayground.py b/toontown/safezone/MMPlayground.py new file mode 100755 index 00000000..2cc964b0 --- /dev/null +++ b/toontown/safezone/MMPlayground.py @@ -0,0 +1,22 @@ +from direct.fsm import ClassicFSM, State +from toontown.safezone import Playground +from toontown.toonbase import ToontownGlobals +import random + +class MMPlayground(Playground.Playground): + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + self.activityFsm = ClassicFSM.ClassicFSM('Activity', [State.State('off', self.enterOff, self.exitOff, ['OnPiano']), State.State('OnPiano', self.enterOnPiano, self.exitOnPiano, ['off'])], 'off', 'off') + self.activityFsm.enterInitialState() + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterOnPiano(self): + base.localAvatar.b_setParent(ToontownGlobals.SPMinniesPiano) + + def exitOnPiano(self): + base.localAvatar.b_setParent(ToontownGlobals.SPRender) diff --git a/toontown/safezone/MMSafeZoneLoader.py b/toontown/safezone/MMSafeZoneLoader.py new file mode 100755 index 00000000..06b399f8 --- /dev/null +++ b/toontown/safezone/MMSafeZoneLoader.py @@ -0,0 +1,12 @@ +from toontown.safezone import MMPlayground +from toontown.safezone import SafeZoneLoader + + +class MMSafeZoneLoader(SafeZoneLoader.SafeZoneLoader): + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.playgroundClass = MMPlayground.MMPlayground + self.musicFile = 'phase_6/audio/bgm/MM_nbrhood.ogg' + self.activityMusicFile = 'phase_6/audio/bgm/MM_SZ_activity.ogg' + self.dnaFile = 'phase_6/dna/minnies_melody_land_sz.pdna' + self.safeZoneStorageDNAFile = 'phase_6/dna/storage_MM_sz.pdna' diff --git a/toontown/safezone/OZPlayground.py b/toontown/safezone/OZPlayground.py new file mode 100755 index 00000000..e33ed860 --- /dev/null +++ b/toontown/safezone/OZPlayground.py @@ -0,0 +1,162 @@ +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +import Playground +from toontown.building import Elevator +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from toontown.racing import RaceGlobals +from direct.fsm import State +from toontown.safezone import PicnicBasket +from toontown.safezone import GolfKart +from direct.task.Task import Task + +class OZPlayground(Playground.Playground): + waterLevel = -0.53 + + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + self.parentFSM = parentFSM + self.picnicBasketBlockDoneEvent = 'picnicBasketBlockDone' + self.cameraSubmerged = -1 + self.toonSubmerged = -1 + self.fsm.addState(State.State('picnicBasketBlock', self.enterPicnicBasketBlock, self.exitPicnicBasketBlock, ['walk'])) + state = self.fsm.getStateNamed('walk') + state.addTransition('picnicBasketBlock') + self.picnicBasketDoneEvent = 'picnicBasketDone' + + def load(self): + Playground.Playground.load(self) + + def unload(self): + Playground.Playground.unload(self) + + def enter(self, requestStatus): + Playground.Playground.enter(self, requestStatus) + + def exit(self): + Playground.Playground.exit(self) + taskMgr.remove('oz-check-toon-underwater') + taskMgr.remove('oz-check-cam-underwater') + self.loader.hood.setNoFog() + + def enterStart(self): + self.cameraSubmerged = 0 + self.toonSubmerged = 0 + taskMgr.add(self.__checkToonUnderwater, 'oz-check-toon-underwater') + taskMgr.add(self.__checkCameraUnderwater, 'oz-check-cam-underwater') + + def __checkCameraUnderwater(self, task): + if camera.getZ(render) < self.waterLevel: + self.__submergeCamera() + else: + self.__emergeCamera() + return Task.cont + + def __checkToonUnderwater(self, task): + if base.localAvatar.getZ() < -4.0: + self.__submergeToon() + else: + self.__emergeToon() + return Task.cont + + def __submergeCamera(self): + if self.cameraSubmerged == 1: + return + self.loader.hood.setUnderwaterFog() + base.playSfx(self.loader.underwaterSound, looping=1, volume=0.8) + self.cameraSubmerged = 1 + + def __emergeCamera(self): + if self.cameraSubmerged == 0: + return + self.loader.hood.setNoFog() + self.loader.underwaterSound.stop() + self.cameraSubmerged = 0 + + def __submergeToon(self): + if self.toonSubmerged == 1: + return + base.playSfx(self.loader.submergeSound) + if base.config.GetBool('disable-flying-glitch') == 0: + self.fsm.request('walk') + self.walkStateData.fsm.request('swimming', [self.loader.swimSound]) + pos = base.localAvatar.getPos(render) + base.localAvatar.d_playSplashEffect(pos[0], pos[1], self.waterLevel) + self.toonSubmerged = 1 + + def __emergeToon(self): + if self.toonSubmerged == 0: + return + self.walkStateData.fsm.request('walking') + self.toonSubmerged = 0 + + def enterTeleportIn(self, requestStatus): + reason = requestStatus.get('reason') + if reason == RaceGlobals.Exit_Barrier: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RaceTimeout, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + elif reason == RaceGlobals.Exit_Slow: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RacerTooSlow, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + elif reason == RaceGlobals.Exit_BarrierNoRefund: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RaceTimeoutNoRefund, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + self.toonSubmerged = -1 + taskMgr.remove('oz-check-toon-underwater') + Playground.Playground.enterTeleportIn(self, requestStatus) + + def teleportInDone(self): + self.toonSubmerged = -1 + taskMgr.add(self.__checkToonUnderwater, 'oz-check-toon-underwater') + Playground.Playground.teleportInDone(self) + + def __cleanupDialog(self, value): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + if hasattr(self, 'fsm'): + self.fsm.request('walk', [1]) + return + + def enterPicnicBasketBlock(self, picnicBasket): + base.localAvatar.laffMeter.start() + base.localAvatar.b_setAnimState('off', 1) + base.localAvatar.cantLeaveGame = 1 + self.accept(self.picnicBasketDoneEvent, self.handlePicnicBasketDone) + self.trolley = PicnicBasket.PicnicBasket(self, self.fsm, self.picnicBasketDoneEvent, picnicBasket.getDoId(), picnicBasket.seatNumber) + self.trolley.load() + self.trolley.enter() + + def exitPicnicBasketBlock(self): + base.localAvatar.laffMeter.stop() + base.localAvatar.cantLeaveGame = 0 + self.ignore(self.trolleyDoneEvent) + self.trolley.unload() + self.trolley.exit() + del self.trolley + + def detectedPicnicTableSphereCollision(self, picnicBasket): + self.fsm.request('picnicBasketBlock', [picnicBasket]) + + def handleStartingBlockDone(self, doneStatus): + self.notify.debug('handling StartingBlock done event') + where = doneStatus['where'] + if where == 'reject': + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'racetrack': + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleStartingBlockDone') + + def handlePicnicBasketDone(self, doneStatus): + self.notify.debug('handling picnic basket done event') + mode = doneStatus['mode'] + if mode == 'reject': + self.fsm.request('walk') + elif mode == 'exit': + self.fsm.request('walk') + else: + self.notify.error('Unknown mode: ' + mode + ' in handlePicnicBasketDone') diff --git a/toontown/safezone/OZSafeZoneLoader.py b/toontown/safezone/OZSafeZoneLoader.py new file mode 100755 index 00000000..63b2d1d1 --- /dev/null +++ b/toontown/safezone/OZSafeZoneLoader.py @@ -0,0 +1,373 @@ +import copy +from direct.actor import Actor +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.interval.IntervalGlobal import * +from panda3d.core import * +import random + +from otp.avatar import Avatar +from otp.otpbase import OTPGlobals +from toontown.distributed import DelayDelete +from toontown.effects import Bubbles +from toontown.hood import ZoneUtil +from toontown.safezone.OZPlayground import OZPlayground +from toontown.safezone.SafeZoneLoader import SafeZoneLoader +from toontown.toon import Toon, ToonDNA + + +class OZSafeZoneLoader(SafeZoneLoader): + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.musicFile = 'phase_6/audio/bgm/OZ_SZ.ogg' + self.activityMusicFile = 'phase_6/audio/bgm/GS_KartShop.ogg' + self.dnaFile = 'phase_6/dna/outdoor_zone_sz.pdna' + self.safeZoneStorageDNAFile = 'phase_6/dna/storage_OZ_sz.pdna' + self.__toonTracks = {} + del self.fsm + self.fsm = ClassicFSM.ClassicFSM('SafeZoneLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'playground', 'toonInterior']), + State.State('playground', self.enterPlayground, self.exitPlayground, ['quietZone', 'golfcourse']), + State.State('toonInterior', self.enterToonInterior, self.exitToonInterior, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['playground', 'toonInterior', 'golfcourse']), + State.State('golfcourse', self.enterGolfCourse, self.exitGolfCourse, ['quietZone', 'playground']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + self.done = 0 + self.geyserTrack = None + SafeZoneLoader.load(self) + self.birdSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.ogg', 'phase_4/audio/sfx/SZ_TC_bird2.ogg', 'phase_4/audio/sfx/SZ_TC_bird3.ogg']) + self.underwaterSound = base.loadSfx('phase_4/audio/sfx/AV_ambient_water.ogg') + self.swimSound = base.loadSfx('phase_4/audio/sfx/AV_swim_single_stroke.ogg') + self.submergeSound = base.loadSfx('phase_5.5/audio/sfx/AV_jump_in_water.ogg') + geyserPlacer = self.geom.find('**/geyser*') + waterfallPlacer = self.geom.find('**/waterfall*') + binMgr = CullBinManager.getGlobalPtr() + binMgr.addBin('water', CullBinManager.BTFixed, 29) + pool = self.geom.find('**/pPlane5*') + pool.setTransparency(1) + pool.setColorScale(1.0, 1.0, 1.0, 1.0) + pool.setBin('water', 50, 1) + self.geyserModel = loader.loadModel('phase_6/models/golf/golf_geyser_model') + self.geyserSound = loader.loadSfx('phase_6/audio/sfx/OZ_Geyser.ogg') + self.geyserSoundInterval = SoundInterval(self.geyserSound, node=geyserPlacer, listenerNode=base.camera, seamlessLoop=False, volume=1.0, cutOff=120) + self.geyserSoundNoToon = loader.loadSfx('phase_6/audio/sfx/OZ_Geyser_No_Toon.ogg') + self.geyserSoundNoToonInterval = SoundInterval(self.geyserSoundNoToon, node=geyserPlacer, listenerNode=base.camera, seamlessLoop=False, volume=1.0, cutOff=120) + if self.geyserModel: + self.geyserActor = Actor.Actor(self.geyserModel) + self.geyserActor.loadAnims({'idle': 'phase_6/models/golf/golf_geyser'}) + self.geyserActor.reparentTo(render) + self.geyserActor.setPlayRate(8.6, 'idle') + self.geyserActor.loop('idle') + self.geyserActor.setDepthWrite(0) + self.geyserActor.setTwoSided(True, 11) + self.geyserActor.setColorScale(1.0, 1.0, 1.0, 1.0) + self.geyserActor.setBin('fixed', 0) + mesh = self.geyserActor.find('**/mesh_tide1') + joint = self.geyserActor.find('**/uvj_WakeWhiteTide1') + mesh.setTexProjector(mesh.findTextureStage('default'), joint, self.geyserActor) + self.geyserActor.setPos(geyserPlacer.getPos()) + self.geyserActor.setZ(geyserPlacer.getZ() - 100.0) + self.geyserPos = geyserPlacer.getPos() + self.geyserPlacer = geyserPlacer + self.startGeyser() + base.sfxPlayer.setCutoffDistance(160) + self.geyserPoolSfx = loader.loadSfx('phase_6/audio/sfx/OZ_Geyser_BuildUp_Loop.ogg') + self.geyserPoolSoundInterval = SoundInterval(self.geyserPoolSfx, node=self.geyserPlacer, listenerNode=base.camera, seamlessLoop=True, volume=1.0, cutOff=120) + self.geyserPoolSoundInterval.loop() + self.bubbles = Bubbles.Bubbles(self.geyserPlacer, render) + self.bubbles.renderParent.setDepthWrite(0) + self.bubbles.start() + self.collBase = render.attachNewNode('collisionBase') + self.geyserCollSphere = CollisionSphere(0, 0, 0, 7.5) + self.geyserCollSphere.setTangible(1) + self.geyserCollNode = CollisionNode('barrelSphere') + self.geyserCollNode.setIntoCollideMask(OTPGlobals.WallBitmask) + self.geyserCollNode.addSolid(self.geyserCollSphere) + self.geyserNodePath = self.collBase.attachNewNode(self.geyserCollNode) + self.geyserNodePath.setPos(self.geyserPos[0], self.geyserPos[1], self.geyserPos[2] - 100.0) + self.waterfallModel = loader.loadModel('phase_6/models/golf/golf_waterfall_model') + if self.waterfallModel: + self.waterfallActor = Actor.Actor(self.waterfallModel) + self.waterfallActor.loadAnims({'idle': 'phase_6/models/golf/golf_waterfall'}) + self.waterfallActor.reparentTo(render) + self.waterfallActor.setPlayRate(3.5, 'idle') + self.waterfallActor.loop('idle') + mesh = self.waterfallActor.find('**/mesh_tide1') + joint = self.waterfallActor.find('**/uvj_WakeWhiteTide1') + mesh.setTexProjector(mesh.findTextureStage('default'), joint, self.waterfallActor) + self.waterfallActor.setPos(waterfallPlacer.getPos()) + self.accept('clientLogout', self._handleLogout) + + def exit(self): + self.clearToonTracks() + SafeZoneLoader.exit(self) + self.ignore('clientLogout') + + def startGeyser(self, task = None): + if hasattr(base.cr, 'DTimer') and base.cr.DTimer: + self.geyserCycleTime = 20.0 + useTime = base.cr.DTimer.getTime() + timeToNextGeyser = 20.0 - useTime % 20.0 + taskMgr.doMethodLater(timeToNextGeyser, self.doGeyser, 'geyser Task') + else: + taskMgr.doMethodLater(5.0, self.startGeyser, 'start geyser Task') + + def doGeyser(self, task = None): + if not self.done: + self.setGeyserAnim() + useTime = base.cr.DTimer.getTime() + timeToNextGeyser = 20.0 - useTime % 20.0 + taskMgr.doMethodLater(timeToNextGeyser, self.doGeyser, 'geyser Task') + return task.done + + def restoreLocal(self, task = None): + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('walk') + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.collisionsOn() + base.localAvatar.dropShadow.show() + + def restoreRemote(self, remoteAv, task = None): + if remoteAv in Avatar.Avatar.ActiveAvatars: + remoteAv.startSmooth() + remoteAv.dropShadow.show() + + def setGeyserAnim(self, task = None): + if self.done: + return + maxSize = 0.4 * random.random() + 0.75 + time = 1.0 + self.geyserTrack = Sequence() + upPos = Vec3(self.geyserPos[0], self.geyserPos[1], self.geyserPos[2]) + downPos = Vec3(self.geyserPos[0], self.geyserPos[1], self.geyserPos[2] - 8.0) + avList = copy.copy(Avatar.Avatar.ActiveAvatars) + avList.append(base.localAvatar) + playSound = 0 + for av in avList: + distance = self.geyserPlacer.getDistance(av) + if distance < 7.0: + place = base.cr.playGame.getPlace() + local = 0 + avPos = av.getPos() + upToon = Vec3(avPos[0], avPos[1], maxSize * self.geyserPos[2] + 40.0) + midToon = Vec3(avPos[0], avPos[1], maxSize * self.geyserPos[2] + 30.0) + downToon = Vec3(avPos[0], avPos[1], self.geyserPos[2]) + returnPoints = [(7, 7), + (8, 0), + (-8, 3), + (-7, 7), + (3, -7), + (0, 8), + (-10, 0), + (8, -3), + (5, 8), + (-8, 5), + (-1, 7)] + pick = int((float(av.doId) - 11.0) / 13.0 % len(returnPoints)) + returnChoice = returnPoints[pick] + toonReturn = Vec3(self.geyserPos[0] + returnChoice[0], self.geyserPos[1] + returnChoice[1], self.geyserPos[2] - 1.5) + topTrack = Sequence() + av.dropShadow.hide() + playSound = 1 + if av == base.localAvatar: + base.cr.playGame.getPlace().setState('fishing') + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.collisionsOff() + local = 1 + else: + topTrack.delayDeletes = [DelayDelete.DelayDelete(av, 'OZSafeZoneLoader.setGeyserAnim')] + av.stopSmooth() + animTrack = Parallel() + toonTrack = Sequence() + toonTrack.append(Wait(0.5)) + animTrack.append(ActorInterval(av, 'jump-idle', loop=1, endTime=11.5 * time)) + animTrack.append(ActorInterval(av, 'neutral', loop=0, endTime=0.25 * time)) + holder = render.attachNewNode('toon hold') + base.holder = holder + toonPos = av.getPos(render) + toonHpr = av.getHpr(render) + print 'av Pos %s' % av.getPos() + base.toonPos = toonPos + holder.setPos(toonPos) + av.reparentTo(holder) + av.setPos(0, 0, 0) + lookAt = 180 + toonH = (lookAt + toonHpr[0]) % 360 + newHpr = Vec3(toonH, toonHpr[1], toonHpr[2]) + if toonH < 180: + lookIn = Vec3(0 + lookAt, -30, 0) + else: + lookIn = Vec3(360 + lookAt, -30, 0) + print 'Camera Hprs toon %s; lookIn %s; final %s' % (newHpr, lookIn, lookIn - newHpr) + if local == 1: + camPosOriginal = camera.getPos() + camHprOriginal = camera.getHpr() + camParentOriginal = camera.getParent() + cameraPivot = holder.attachNewNode('camera pivot') + chooseHeading = random.choice([-10.0, 15.0, 40.0]) + cameraPivot.setHpr(chooseHeading, -20.0, 0.0) + cameraArm = cameraPivot.attachNewNode('camera arm') + cameraArm.setPos(0.0, -23.0, 3.0) + camPosStart = Point3(0.0, 0.0, 0.0) + camHprStart = Vec3(0.0, 0.0, 0.0) + self.changeCamera(cameraArm, camPosStart, camHprStart) + cameraTrack = Sequence() + cameraTrack.append(Wait(11.0 * time)) + cameraTrack.append(Func(self.changeCamera, camParentOriginal, camPosOriginal, camHprOriginal)) + cameraTrack.start() + moveTrack = Sequence() + moveTrack.append(Wait(0.5)) + moveTrack.append(LerpPosInterval(holder, 3.0 * time, pos=upToon, startPos=downToon, blendType='easeOut')) + moveTrack.append(LerpPosInterval(holder, 2.0 * time, pos=midToon, startPos=upToon, blendType='easeInOut')) + moveTrack.append(LerpPosInterval(holder, 1.0 * time, pos=upToon, startPos=midToon, blendType='easeInOut')) + moveTrack.append(LerpPosInterval(holder, 2.0 * time, pos=midToon, startPos=upToon, blendType='easeInOut')) + moveTrack.append(LerpPosInterval(holder, 1.0 * time, pos=upToon, startPos=midToon, blendType='easeInOut')) + moveTrack.append(LerpPosInterval(holder, 2.5 * time, pos=toonReturn, startPos=upToon, blendType='easeIn')) + animTrack.append(moveTrack) + animTrack.append(toonTrack) + topTrack.append(animTrack) + topTrack.append(Func(av.setPos, toonReturn)) + topTrack.append(Func(av.reparentTo, render)) + topTrack.append(Func(holder.remove)) + if local == 1: + topTrack.append(Func(self.restoreLocal)) + else: + topTrack.append(Func(self.restoreRemote, av)) + topTrack.append(Func(self.clearToonTrack, av.doId)) + self.storeToonTrack(av.doId, topTrack) + topTrack.start() + + self.geyserTrack.append(Func(self.doPrint, 'geyser start')) + self.geyserTrack.append(Func(self.geyserNodePath.setPos, self.geyserPos[0], self.geyserPos[1], self.geyserPos[2])) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, 2.0 * time, 0.75, 0.01), LerpPosInterval(self.geyserActor, 2.0 * time, pos=downPos, startPos=downPos))) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, time, maxSize, 0.75), LerpPosInterval(self.geyserActor, time, pos=upPos, startPos=downPos))) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, 2.0 * time, 0.75, maxSize), LerpPosInterval(self.geyserActor, 2.0 * time, pos=downPos, startPos=upPos))) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, time, maxSize, 0.75), LerpPosInterval(self.geyserActor, time, pos=upPos, startPos=downPos))) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, 2.0 * time, 0.75, maxSize), LerpPosInterval(self.geyserActor, 2.0 * time, pos=downPos, startPos=upPos))) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, time, maxSize, 0.75), LerpPosInterval(self.geyserActor, time, pos=upPos, startPos=downPos))) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, 4.0 * time, 0.01, maxSize), LerpPosInterval(self.geyserActor, 4.0 * time, pos=downPos, startPos=upPos))) + self.geyserTrack.append(Func(self.geyserNodePath.setPos, self.geyserPos[0], self.geyserPos[1], self.geyserPos[2] - 100.0)) + self.geyserTrack.append(Func(self.doPrint, 'geyser end')) + self.geyserTrack.start() + if playSound: + self.geyserSoundInterval.start() + else: + self.geyserSoundNoToonInterval.start() + + def changeCamera(self, newParent, newPos, newHpr): + camera.reparentTo(newParent) + camera.setPosHpr(newPos, newHpr) + + def doPrint(self, thing): + return 0 + print thing + + def unload(self): + del self.birdSound + SafeZoneLoader.unload(self) + self.done = 1 + self.collBase.removeNode() + if self.geyserTrack: + self.geyserTrack.finish() + self.geyserTrack = None + self.geyserActor.cleanup() + self.geyserModel.removeNode() + self.waterfallActor.cleanup() + self.waterfallModel.removeNode() + self.bubbles.destroy() + del self.bubbles + self.geyserPoolSoundInterval.finish() + self.geyserPoolSfx.stop() + self.geyserPoolSfx = None + self.geyserPoolSoundInterval = None + self.geyserSoundInterval.finish() + self.geyserSound.stop() + self.geyserSoundInterval = None + self.geyserSound = None + self.geyserSoundNoToonInterval.finish() + self.geyserSoundNoToon.stop() + self.geyserSoundNoToonInterval = None + self.geyserSoundNoToon = None + + def enterPlayground(self, requestStatus): + self.playgroundClass = OZPlayground + SafeZoneLoader.enterPlayground(self, requestStatus) + + def exitPlayground(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + SafeZoneLoader.exitPlayground(self) + self.playgroundClass = None + return + + def handlePlaygroundDone(self): + status = self.place.doneStatus + self.doneStatus = status + messenger.send(self.doneEvent) + + def enteringARace(self, status): + if not status['where'] == 'golfcourse': + return 0 + if ZoneUtil.isDynamicZone(status['zoneId']): + return status['hoodId'] == self.hood.hoodId + else: + return ZoneUtil.getHoodId(status['zoneId']) == self.hood.hoodId + + def enteringAGolfCourse(self, status): + if not status['where'] == 'golfcourse': + return 0 + if ZoneUtil.isDynamicZone(status['zoneId']): + return status['hoodId'] == self.hood.hoodId + else: + return ZoneUtil.getHoodId(status['zoneId']) == self.hood.hoodId + + def enterGolfCourse(self, requestStatus): + if 'curseId' in requestStatus: + self.golfCourseId = requestStatus['courseId'] + else: + self.golfCourseId = 0 + self.accept('raceOver', self.handleRaceOver) + self.accept('leavingGolf', self.handleLeftGolf) + base.transitions.irisOut(t=0.2) + + def exitGolfCourse(self): + del self.golfCourseId + + def handleRaceOver(self): + print 'you done!!' + + def handleLeftGolf(self): + req = {'loader': 'safeZoneLoader', + 'where': 'playground', + 'how': 'teleportIn', + 'zoneId': 6000, + 'hoodId': 6000, + 'shardId': None} + self.fsm.request('quietZone', [req]) + return + + def _handleLogout(self): + self.clearToonTracks() + + def storeToonTrack(self, avId, track): + self.clearToonTrack(avId) + self.__toonTracks[avId] = track + + def clearToonTrack(self, avId): + oldTrack = self.__toonTracks.get(avId) + if oldTrack: + oldTrack.pause() + DelayDelete.cleanupDelayDeletes(oldTrack) + del self.__toonTracks[avId] + + def clearToonTracks(self): + keyList = [] + for key in self.__toonTracks: + keyList.append(key) + + for key in keyList: + if key in self.__toonTracks: + self.clearToonTrack(key) diff --git a/toontown/safezone/PicnicBasket.py b/toontown/safezone/PicnicBasket.py new file mode 100755 index 00000000..997b94d3 --- /dev/null +++ b/toontown/safezone/PicnicBasket.py @@ -0,0 +1,221 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil + +class PicnicBasket(StateData.StateData): + + def __init__(self, safeZone, parentFSM, doneEvent, tableNumber, seatNumber): + StateData.StateData.__init__(self, doneEvent) + self.tableNumber = tableNumber + self.seatNumber = seatNumber + self.fsm = ClassicFSM.ClassicFSM('PicnicBasket', [ + State.State('start', + self.enterStart, + self.exitStart, [ + 'requestBoard', + 'trolleyHFA', + 'trolleyTFA']), + State.State('trolleyHFA', + self.enterTrolleyHFA, + self.exitTrolleyHFA, [ + 'final']), + State.State('trolleyTFA', + self.enterTrolleyTFA, + self.exitTrolleyTFA, [ + 'final']), + State.State('requestBoard', + self.enterRequestBoard, + self.exitRequestBoard, [ + 'boarding']), + State.State('boarding', + self.enterBoarding, + self.exitBoarding, [ + 'boarded']), + State.State('boarded', + self.enterBoarded, + self.exitBoarded, [ + 'requestExit', + 'trolleyLeaving', + 'final', + 'exiting']), + State.State('requestExit', + self.enterRequestExit, + self.exitRequestExit, [ + 'exiting', + 'trolleyLeaving']), + State.State('trolleyLeaving', + self.enterTrolleyLeaving, + self.exitTrolleyLeaving, [ + 'final']), + State.State('exiting', + self.enterExiting, + self.exitExiting, [ + 'final']), + State.State('final', + self.enterFinal, + self.exitFinal, [ + 'start'])], + 'start', 'final') + self.parentFSM = parentFSM + return None + + def load(self): + self.parentFSM.getStateNamed('picnicBasketBlock').addChild(self.fsm) + self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + self.upButton = self.buttonModels.find('**//InventoryButtonUp') + self.downButton = self.buttonModels.find('**/InventoryButtonDown') + self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover') + + def unload(self): + self.parentFSM.getStateNamed('trolley').removeChild(self.fsm) + del self.fsm + del self.parentFSM + self.buttonModels.removeNode() + del self.buttonModels + del self.upButton + del self.downButton + del self.rolloverButton + + def enter(self): + self.fsm.enterInitialState() + if base.localAvatar.hp > 0: + messenger.send('enterPicnicTableOK_%d_%d' % (self.tableNumber, self.seatNumber)) + self.fsm.request('requestBoard') + else: + self.fsm.request('trolleyHFA') + return None + + def exit(self): + self.ignoreAll() + return None + + def enterStart(self): + return None + + def exitStart(self): + return None + + def enterTrolleyHFA(self): + self.noTrolleyBox = TTDialog.TTGlobalDialog(message=TTLocalizer.TrolleyHFAMessage, doneEvent='noTrolleyAck', style=TTDialog.Acknowledge) + self.noTrolleyBox.show() + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('noTrolleyAck', self.__handleNoTrolleyAck) + + def exitTrolleyHFA(self): + self.ignore('noTrolleyAck') + self.noTrolleyBox.cleanup() + del self.noTrolleyBox + + def enterTrolleyTFA(self): + self.noTrolleyBox = TTDialog.TTGlobalDialog(message=TTLocalizer.TrolleyTFAMessage, doneEvent='noTrolleyAck', style=TTDialog.Acknowledge) + self.noTrolleyBox.show() + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('noTrolleyAck', self.__handleNoTrolleyAck) + + def exitTrolleyTFA(self): + self.ignore('noTrolleyAck') + self.noTrolleyBox.cleanup() + del self.noTrolleyBox + + def __handleNoTrolleyAck(self): + ntdoneStatus = self.noTrolleyBox.doneStatus + if ntdoneStatus == 'ok': + doneStatus = {} + doneStatus['mode'] = 'reject' + messenger.send(self.doneEvent, [doneStatus]) + else: + self.notify.error('Unrecognized doneStatus: ' + str(ntdoneStatus)) + + def enterRequestBoard(self): + return None + + def handleRejectBoard(self): + doneStatus = {} + doneStatus['mode'] = 'reject' + messenger.send(self.doneEvent, [doneStatus]) + + def exitRequestBoard(self): + return None + + def enterBoarding(self, nodePath, side): + camera.wrtReparentTo(nodePath) + heading = PythonUtil.fitDestAngle2Src(camera.getH(nodePath), 90 * side) + self.cameraBoardTrack = LerpPosHprInterval(camera, 1.5, Point3(14.4072 * side, 0, 3.8667), Point3(heading, -15, 0)) + self.cameraBoardTrack.start() + return None + + def exitBoarding(self): + self.ignore('boardedTrolley') + return None + + def enterBoarded(self): + self.enableExitButton() + return None + + def exitBoarded(self): + self.cameraBoardTrack.finish() + self.disableExitButton() + return None + + def enableExitButton(self): + self.exitButton = DirectButton(relief=None, text=TTLocalizer.TrolleyHopOff, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0, 0, 0.8), scale=0.15, command=lambda self = self: self.fsm.request('requestExit')) + return + + def disableExitButton(self): + self.exitButton.destroy() + + def enterRequestExit(self): + messenger.send('trolleyExitButton') + return None + + def exitRequestExit(self): + return None + + def enterTrolleyLeaving(self): + self.acceptOnce('playMinigame', self.handlePlayMinigame) + self.acceptOnce('picnicDone', self.handlePicnicDone) + return None + + def handlePlayMinigame(self, zoneId, minigameId): + base.localAvatar.b_setParent(ToontownGlobals.SPHidden) + doneStatus = {} + doneStatus['mode'] = 'minigame' + doneStatus['zoneId'] = zoneId + doneStatus['minigameId'] = minigameId + messenger.send(self.doneEvent, [doneStatus]) + + def handlePicnicDone(self): + doneStatus = {} + doneStatus['mode'] = 'exit' + messenger.send(self.doneEvent, [doneStatus]) + + def exitTrolleyLeaving(self): + self.ignore('playMinigame') + taskMgr.remove('leavingCamera') + return self.notify.debug('handling golf kart done event') + + def enterExiting(self): + return None + + def handleOffTrolley(self): + doneStatus = {} + doneStatus['mode'] = 'exit' + messenger.send(self.doneEvent, [doneStatus]) + return None + + def exitExiting(self): + return None + + def enterFinal(self): + return None + + def exitFinal(self): + return None diff --git a/toontown/safezone/PicnicGameGlobals.py b/toontown/safezone/PicnicGameGlobals.py new file mode 100755 index 00000000..d6b5799e --- /dev/null +++ b/toontown/safezone/PicnicGameGlobals.py @@ -0,0 +1,8 @@ +# Menu Types: +TutorialMenu = 1 +GameMenu = 1 + +# Game Indices: +CheckersGameIndex = 0 +ChineseCheckersGameIndex = 1 +FindFourGameIndex = 2 diff --git a/toontown/safezone/PicnicGameSelectMenu.py b/toontown/safezone/PicnicGameSelectMenu.py new file mode 100755 index 00000000..5a3230bc --- /dev/null +++ b/toontown/safezone/PicnicGameSelectMenu.py @@ -0,0 +1,121 @@ +from TrolleyConstants import * +from direct.distributed.ClockDelta import * +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.safezone import PicnicGameGlobals + + +class PicnicGameSelectMenu(DirectFrame): + def __init__(self, picnicFunction, menuType): + self.picnicFunction = picnicFunction + DirectFrame.__init__( + self, pos=(0.0, 0.0, 0.85), + image_color=ToontownGlobals.GlobalDialogColor, + image_scale=(1.8, 0.9, 0.13), text='', text_scale=0.05) + self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui.bam') + self.upButton = self.buttonModels.find('**//InventoryButtonUp') + self.downButton = self.buttonModels.find('**/InventoryButtonDown') + self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover') + if menuType == PicnicGameGlobals.TutorialMenu: + self.title = DirectLabel( + self, relief=None, + text=TTLocalizer.PicnicTableMenuTutorial, + text_pos=(0.0, -0.038), text_fg=(1, 1, 1, 1), + text_scale=0.09, text_font=ToontownGlobals.getSignFont(), + text_shadow=(0, 0, 0, 0.8), text_shadowOffset=(-0.1, -0.1)) + elif menuType == PicnicGameGlobals.GameMenu: + self.title = DirectLabel( + self, relief=None, text=TTLocalizer.PicnicTableMenuSelect, + text_pos=(0.0, -0.038), text_fg=(1, 1, 1, 1), text_scale=0.09, + text_font=ToontownGlobals.getSignFont(), + text_shadow=(0, 0, 0, 0.8), text_shadowOffset=(-0.1, -0.1)) + self.selectionButtons = loader.loadModel('phase_6/models/golf/picnic_game_menu.bam') + btn1 = self.selectionButtons.find('**/Btn1') + btn2 = self.selectionButtons.find('**/Btn2') + btn3 = self.selectionButtons.find('**/Btn3') + self.ChineseCheckers = DirectButton( + self, image=(btn1.find('**/checkersBtnUp'), + btn1.find('**/checkersBtnDn'), + btn1.find('**/checkersBtnHi'), + btn1.find('**/checkersBtnUp')), + scale=0.36, relief=0, pos=(0, 0, -0.7), + command=self.chineseCheckersSelected) + self.Checkers = DirectButton( + self, image=(btn2.find('**/regular_checkersBtnUp'), + btn2.find('**/regular_checkersBtnDn'), + btn2.find('**/regular_checkersBtnHi'), + btn2.find('**/regular_checkersBtnUp')), + scale=0.36, relief=0, pos=(0.8, 0, -0.7), + command=self.checkersSelected) + self.FindFour = DirectButton( + self, image=(btn3.find('**/findfourBtnUp'), + btn3.find('**/findfourBtnDn'), + btn3.find('**/findfourBtnHi'), + btn3.find('**/findfourBtnUp')), + scale=0.36, relief=0, pos=(-0.8, 0, -0.7), + command=self.findFourSelected) + if not base.config.GetBool('want-checkers', 0): + self.Checkers['command'] = lambda:None + self.Checkers.setColor(0.7, 0.7, 0.7, 0.7) + if not base.config.GetBool('want-chinese-checkers', 0): + self.ChineseCheckers['command'] = lambda:None + self.ChineseCheckers.setColor(0.7, 0.7, 0.7, 0.7) + if not base.config.GetBool('want-find-four', 0): + self.FindFour['command'] = lambda:None + self.FindFour.setColor(0.7, 0.7, 0.7, 0.7) + self.chineseText = OnscreenText( + text='Chinese Checkers', pos=(0, 0.56, -0.8), scale=0.15, + fg=Vec4(1, 1, 1, 1), align=TextNode.ACenter, + font=ToontownGlobals.getMinnieFont(), wordwrap=7, + shadow=(0, 0, 0, 0.8), shadowOffset=(-0.1, -0.1), mayChange=True) + self.chineseText.setR(-8) + self.checkersText = OnscreenText( + text='Checkers', pos=(0.81, -.1, -0.8), scale=0.15, + fg=Vec4(1, 1, 1, 1), align=TextNode.ACenter, + font=ToontownGlobals.getMinnieFont(), wordwrap=7, + shadow=(0, 0, 0, 0.8), shadowOffset=(0.1, -0.1), mayChange=True) + self.findFourText = OnscreenText( + text='Find Four', pos=(-0.81, -.08, -0.8), scale=0.15, + fg=Vec4(1, 1, 1, 1), align=TextNode.ACenter, + font=ToontownGlobals.getMinnieFont(), wordwrap=8, + shadow=(0, 0, 0, 0.8), shadowOffset=(-0.1, -0.1), mayChange=True) + self.exitButton = DirectButton( + relief=None, text=TTLocalizer.PicnicTableCancelButton, + text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.8, + image=(self.upButton, self.downButton, self.rolloverButton), + image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0, 0, -0.4), + scale=0.15, command=lambda self=self: self.cancelButtonPushed()) + self.findFourText.setR(-8) + self.checkersText.setR(8) + + def delete(self): + self.removeButtons() + + def removeButtons(self): + self.ChineseCheckers.destroy() + self.Checkers.destroy() + self.FindFour.destroy() + self.chineseText.destroy() + self.checkersText.destroy() + self.findFourText.destroy() + self.exitButton.destroy() + DirectFrame.destroy(self) + + def checkersSelected(self): + self.picnicFunction(PicnicGameGlobals.CheckersGameIndex) + self.picnicFunction = lambda gameIndex: None + + def chineseCheckersSelected(self): + self.picnicFunction(PicnicGameGlobals.ChineseCheckersGameIndex) + self.picnicFunction = lambda gameIndex: None + + def findFourSelected(self): + self.picnicFunction(PicnicGameGlobals.FindFourGameIndex) + self.picnicFunction = lambda gameIndex: None + + def cancelButtonPushed(self): + self.picnicFunction(-1) + self.picnicFunction = lambda gameIndex: None diff --git a/toontown/safezone/PicnicGameTutorial.py b/toontown/safezone/PicnicGameTutorial.py new file mode 100755 index 00000000..2b3ce596 --- /dev/null +++ b/toontown/safezone/PicnicGameTutorial.py @@ -0,0 +1,276 @@ +import inspect + +from direct.fsm import FSM +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals + + +class GameTutorial(DirectFrame, FSM.FSM): + def __init__(self, doneFunction, doneEvent=None, callback=None): + FSM.FSM.__init__(self, 'GameTutorial') + + self.doneFunction = doneFunction + self.doneEvent = doneEvent + self.callback = callback + + base.localAvatar.startSleepWatch(self.handleQuit) + self.accept('stoppedAsleep', self.handleQuit) + + stateArray = [] + pageNum = 1 + for name, _ in inspect.getmembers(self): + if name.startswith('enterPage'): + stateArray.append('Page' + str(pageNum)) + pageNum += 1 + stateArray.append('Quit') + self.setStateArray(stateArray) + + DirectFrame.__init__( + self, pos=(-0.5, 0, 0), + image_color=ToontownGlobals.GlobalDialogColor, + image_scale=(1.0, 1.5, 1.0), text='', text_scale=0.06) + self['image'] = DGG.getDefaultDialogGeom() + + self.title = DirectLabel( + self, relief=None, text='', text_pos=(0.0, 0.35), + text_fg=(1, 0, 0, 1), text_scale=0.13, + text_font=ToontownGlobals.getSignFont()) + + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui.bam') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui.bam') + + self.nextButton = DirectButton( + self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), + image3_color=Vec4(1, 1, 1, 0.5), relief=None, + text=TTLocalizer.ChineseTutorialNext, text3_fg=Vec4(0, 0, 0, 0.5), + text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.35, -0.3, -0.33), + command=self.requestNext) + self.nextButton.hide() + self.prevButton = DirectButton( + self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), + image3_color=Vec4(1, 1, 1, 0.5), image_scale=(-1.0, 1.0, 1.0), + relief=None, text=TTLocalizer.ChineseTutorialPrev, + text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), + pos=(-0.3, -0.3, -0.33), command=self.requestPrev) + self.prevButton.hide() + self.quitButton = DirectButton( + self, image=(buttons.find('**/ChtBx_OKBtn_UP'), + buttons.find('**/ChtBx_OKBtn_DN'), + buttons.find('**/ChtBx_OKBtn_Rllvr')), + relief=None, text=TTLocalizer.ChineseTutorialDone, text_scale=0.05, + text_pos=(0.0, -0.1), pos=(0.35, -0.3, -0.33), + command=self.handleQuit) + self.quitButton.hide() + + buttons.removeNode() + gui.removeNode() + + def __del__(self): + self.cleanup() + + def enterQuit(self, *args): + self.nextButton.destroy() + self.prevButton.destroy() + self.quitButton.destroy() + DirectFrame.destroy(self) + + def exitQuit(self, *args): + pass + + def handleQuit(self, task=None): + self.forceTransition('Quit') + base.cr.playGame.getPlace().setState('walk') + self.doneFunction() + if task is not None: + task.done + + +class CheckersTutorial(GameTutorial): + def __init__(self, doneFunction): + GameTutorial.__init__(self, doneFunction) + + images = loader.loadModel('phase_6/models/golf/regularchecker_tutorial.bam') + images.setTransparency(1) + + self.page1 = images.find('**/tutorialPage1*') + self.page1.reparentTo(aspect2d) + self.page1.setPos(0.63, -0.1, 0.0) + self.page1.setScale(0.4) + self.page1.setTransparency(1) + self.page1.hide() + + self.page2 = images.find('**/tutorialPage2*') + self.page2.reparentTo(aspect2d) + self.page2.setPos(0.63, -0.1, 0.0) + self.page2.setScale(0.4) + self.page2.setTransparency(1) + self.page2.hide() + + self.page3 = images.find('**/tutorialPage3*') + self.page3.reparentTo(aspect2d) + self.page3.setPos(0.63, -0.1, 0.0) + self.page3.setScale(0.4) + self.page3.setTransparency(1) + self.obj = self.page3.find('**/king*') + self.page3.hide() + + self.page4 = images.find('**/tutorialPage4*') + self.page4.reparentTo(aspect2d) + self.page4.setPos(0.63, -0.1, 0.0) + self.page4.setScale(0.4) + self.page4.setTransparency(1) + self.page4.hide() + + self.request('Page1') + + def enterPage1(self, *args): + self.nextButton.show() + self.prevButton.hide() + self.nextButton['state'] = DGG.NORMAL + self.prevButton['state'] = DGG.DISABLED + self.title['text'] = (TTLocalizer.ChineseTutorialTitle1,) + self['text'] = TTLocalizer.CheckersPage1 + self['text_pos'] = (0.0, 0.23) + self['text_wordwrap'] = 13.5 + self['text_scale'] = 0.06 + self.page1.show() + + def exitPage1(self, *args): + self.page1.hide() + + def enterPage2(self, *args): + self.nextButton.show() + self.prevButton.show() + self.nextButton['state'] = DGG.NORMAL + self.prevButton['state'] = DGG.NORMAL + self.title['text'] = (TTLocalizer.ChineseTutorialTitle2,) + self['text'] = TTLocalizer.CheckersPage2 + self['text_pos'] = (0.0, 0.28) + self['text_wordwrap'] = 12.5 + self['text_scale'] = 0.06 + self.page2.show() + + def exitPage2(self, *args): + self.page2.hide() + + def enterPage3(self, *args): + self.nextButton.show() + self.prevButton.show() + self.nextButton['state'] = DGG.NORMAL + self.prevButton['state'] = DGG.NORMAL + self.title['text'] = (TTLocalizer.ChineseTutorialTitle2,) + self['text'] = TTLocalizer.CheckersPage3 + self.blinker = Sequence( + LerpColorInterval(self.obj, 0.5, Vec4(0.5, 0.5, 0, 0), Vec4(0.9, 0.9, 0, 1)), + LerpColorInterval(self.obj, 0.5, Vec4(0.9, 0.9, 0, 1), Vec4(0.5, 0.5, 0, 0)) + ) + self.blinker.loop() + self.page3.show() + + def exitPage3(self, *args): + self.blinker.finish() + self.page3.hide() + + def enterPage4(self, *args): + self.nextButton.hide() + self.prevButton.show() + self.nextButton['state'] = DGG.DISABLED + self.prevButton['state'] = DGG.NORMAL + self.title['text'] = (TTLocalizer.ChineseTutorialTitle2,) + self['text'] = TTLocalizer.CheckersPage4 + self.quitButton.show() + self.page4.show() + + def exitPage4(self, *args): + self.quitButton.hide() + self.page4.hide() + + +class ChineseCheckersTutorial(GameTutorial): + def __init__(self, doneFunction, doneEvent=None, callback=None): + GameTutorial.__init__(self, doneFunction) + + images = loader.loadModel('phase_6/models/golf/checker_tutorial.bam') + images.setTransparency(1) + + self.page1 = images.find('**/tutorialPage1*') + self.page1.reparentTo(aspect2d) + self.page1.setPos(0.63, -0.1, 0.0) + self.page1.setScale(13.95) + self.page1.setTransparency(1) + self.page1.hide() + self.page1.getChildren()[1].hide() + + self.page2 = images.find('**/tutorialPage3*') + self.page2.reparentTo(aspect2d) + self.page2.setPos(0.63, -0.1, 0.5) + self.page2.setScale(13.95) + self.page2.setTransparency(1) + self.page2.hide() + + self.page3 = images.find('**/tutorialPage2*') + self.page3.reparentTo(aspect2d) + self.page3.setPos(0.63, -0.1, -0.5) + self.page3.setScale(13.95) + self.page3.setTransparency(1) + self.page3.hide() + + self.request('Page1') + + def enterPage1(self, *args): + self.nextButton.show() + self.prevButton.hide() + self.nextButton['state'] = DGG.NORMAL + self.prevButton['state'] = DGG.DISABLED + self.title['text'] = (TTLocalizer.ChineseTutorialTitle1,) + self['text'] = TTLocalizer.ChinesePage1 + self['text_pos'] = (0.0, 0.23) + self['text_wordwrap'] = 13.5 + obj = self.page1.getChildren()[1] + obj.show() + self.blinker = Sequence( + LerpColorInterval( + obj, 0.5, + Vec4(0.5, 0.5, 0, 0.0), + Vec4(0.2, 0.2, 0.2, 1) + ), + LerpColorInterval( + obj, 0.5, + Vec4(0.2, 0.2, 0.2, 1), + Vec4(0.5, 0.5, 0, 0.0) + ) + ) + self.blinker.loop() + self.page1.show() + + def exitPage1(self, *args): + self.page1.hide() + self.page1.getChildren()[1].hide() + self.blinker.finish() + + def enterPage2(self, *args): + self.nextButton.hide() + self.prevButton.show() + self.nextButton['state'] = DGG.DISABLED + self.prevButton['state'] = DGG.NORMAL + self.title['text'] = (TTLocalizer.ChineseTutorialTitle2,) + self['text'] = TTLocalizer.ChinesePage2 + self['text_pos'] = (0.0, 0.28) + self['text_wordwrap'] = 12.5 + self.page2.show() + self.page3.show() + self.quitButton.show() + + def exitPage2(self, *args): + self.page2.hide() + self.page3.hide() + self.quitButton.hide() diff --git a/toontown/safezone/Playground.py b/toontown/safezone/Playground.py new file mode 100755 index 00000000..3701b780 --- /dev/null +++ b/toontown/safezone/Playground.py @@ -0,0 +1,586 @@ +from direct.interval.IntervalGlobal import * +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import Place +from direct.showbase import DirectObject +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +from toontown.toon import DeathForceAcknowledge +from toontown.toon import HealthForceAcknowledge +from toontown.toon import NPCForceAcknowledge +from toontown.trolley import Trolley +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownGlobals +from toontown.toon.Toon import teleportDebug +from toontown.toonbase import TTLocalizer +from direct.gui import DirectLabel +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.quest import Quests +from toontown.battle import BattleParticles +from toontown.dna.DNAParser import DNABulkLoader + +class Playground(Place.Place): + notify = DirectNotifyGlobal.directNotify.newCategory('Playground') + + def __init__(self, loader, parentFSM, doneEvent): + Place.Place.__init__(self, loader, doneEvent) + self.fsm = ClassicFSM.ClassicFSM('Playground', [ + State.State('start', + self.enterStart, + self.exitStart, [ + 'walk', + 'deathAck', + 'doorIn', + 'tunnelIn']), + State.State('walk', + self.enterWalk, + self.exitWalk, [ + 'drive', + 'sit', + 'stickerBook', + 'NPCFA', + 'trolley', + 'final', + 'doorOut', + 'options', + 'quest', + 'purchase', + 'stopped', + 'fishing']), + State.State('stickerBook', + self.enterStickerBook, + self.exitStickerBook, [ + 'walk', + 'trolley', + 'final', + 'doorOut', + 'quest', + 'purchase', + 'stopped', + 'fishing', + 'NPCFA']), + State.State('sit', + self.enterSit, + self.exitSit, ['walk']), + State.State('drive', + self.enterDrive, + self.exitDrive, ['walk']), + State.State('trolley', + self.enterTrolley, + self.exitTrolley, [ + 'walk']), + State.State('doorIn', + self.enterDoorIn, + self.exitDoorIn, [ + 'walk', + 'stopped']), + State.State('doorOut', + self.enterDoorOut, + self.exitDoorOut, [ + 'walk', + 'stopped']), + State.State('NPCFA', + self.enterNPCFA, + self.exitNPCFA, [ + 'NPCFAReject', + 'HFA']), + State.State('NPCFAReject', + self.enterNPCFAReject, + self.exitNPCFAReject, [ + 'walk']), + State.State('HFA', + self.enterHFA, + self.exitHFA, [ + 'HFAReject', + 'teleportOut', + 'tunnelOut']), + State.State('HFAReject', + self.enterHFAReject, + self.exitHFAReject, [ + 'walk']), + State.State('deathAck', + self.enterDeathAck, + self.exitDeathAck, [ + 'teleportIn']), + State.State('teleportIn', + self.enterTeleportIn, + self.exitTeleportIn, [ + 'walk', + 'popup']), + State.State('popup', + self.enterPopup, + self.exitPopup, [ + 'walk']), + State.State('teleportOut', + self.enterTeleportOut, + self.exitTeleportOut, [ + 'deathAck', + 'teleportIn']), + State.State('died', + self.enterDied, + self.exitDied, [ + 'final']), + State.State('tunnelIn', + self.enterTunnelIn, + self.exitTunnelIn, [ + 'walk']), + State.State('tunnelOut', + self.enterTunnelOut, + self.exitTunnelOut, [ + 'final']), + State.State('quest', + self.enterQuest, + self.exitQuest, [ + 'walk']), + State.State('purchase', + self.enterPurchase, + self.exitPurchase, [ + 'walk']), + State.State('stopped', + self.enterStopped, + self.exitStopped, [ + 'walk']), + State.State('fishing', + self.enterFishing, + self.exitFishing, [ + 'walk']), + State.State('final', + self.enterFinal, + self.exitFinal, [ + 'start'])], + 'start', 'final') + self.parentFSM = parentFSM + self.tunnelOriginList = [] + self.trolleyDoneEvent = 'trolleyDone' + self.hfaDoneEvent = 'hfaDoneEvent' + self.npcfaDoneEvent = 'npcfaDoneEvent' + self.dialog = None + self.deathAckBox = None + return + + def enter(self, requestStatus): + self.fsm.enterInitialState() + messenger.send('enterPlayground') + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + base.playMusic(self.loader.music, looping=1, volume=0.8) + self.loader.geom.reparentTo(render) + + for i in self.loader.nodeList: + self.loader.enterAnimatedProps(i) + + self._telemLimiter = TLGatherAllAvs('Playground', RotationLimitToH) + + def __lightDecorationOn__(): + geom = base.cr.playGame.hood.loader.geom + self.loader.hood.eventLights = geom.findAllMatches('**/*light*') + self.loader.hood.eventLights += geom.findAllMatches('**/*lamp*') + self.loader.hood.eventLights += geom.findAllMatches('**/prop_snow_tree*') + self.loader.hood.eventLights += geom.findAllMatches('**/prop_tree*') + self.loader.hood.eventLights += geom.findAllMatches('**/*christmas*') + + for light in self.loader.hood.eventLights: + light.setColorScaleOff(0) + + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.HALLOWEEN) and self.loader.hood.spookySkyFile: + lightsOff = Sequence(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(0.55, 0.55, 0.65, 1)), Func(self.loader.hood.startSpookySky), Func(__lightDecorationOn__)) + lightsOff.start() + elif base.cr.newsManager.isHolidayRunning(ToontownGlobals.CHRISTMAS) and self.loader.hood.snowySkyFile: + lightsOff = Sequence(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(0.7, 0.7, 0.8, 1)), Func(self.loader.hood.startSnowySky), Func(__lightDecorationOn__)) + lightsOff.start() + self.snowEvent = BattleParticles.loadParticleFile('snowdisk.ptf') + self.snowEvent.setPos(0, 30, 10) + self.snowEventRender = base.cr.playGame.hood.loader.geom.attachNewNode('snowRender') + self.snowEventRender.setDepthWrite(2) + self.snowEventRender.setBin('fixed', 1) + self.snowEventFade = None + self.snowEvent.start(camera, self.snowEventRender) + else: + self.loader.hood.startSky() + lightsOn = LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(1, 1, 1, 1)) + lightsOn.start() + + NametagGlobals.setMasterArrowsOn(1) + self.zoneId = requestStatus['zoneId'] + self.tunnelOriginList = base.cr.hoodMgr.addLinkTunnelHooks(self, self.loader.nodeList) + how = requestStatus['how'] + if how == 'teleportIn': + how = 'deathAck' + self.fsm.request(how, [requestStatus]) + + def exit(self): + self.ignoreAll() + messenger.send('exitPlayground') + self._telemLimiter.destroy() + del self._telemLimiter + for node in self.tunnelOriginList: + node.removeNode() + + del self.tunnelOriginList + self.loader.geom.reparentTo(hidden) + + def __lightDecorationOff__(): + for light in self.loader.hood.eventLights: + light.reparentTo(hidden) + + NametagGlobals.setMasterArrowsOn(0) + for i in self.loader.nodeList: + self.loader.exitAnimatedProps(i) + + self.loader.hood.stopSky() + self.loader.music.stop() + + def load(self): + Place.Place.load(self) + self.parentFSM.getStateNamed('playground').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('playground').removeChild(self.fsm) + del self.parentFSM + del self.fsm + if self.dialog: + self.dialog.cleanup() + self.dialog = None + if self.deathAckBox: + self.deathAckBox.cleanup() + self.deathAckBox = None + TTDialog.cleanupDialog('globalDialog') + self.ignoreAll() + Place.Place.unload(self) + return + + def showTreasurePoints(self, points): + self.hideDebugPointText() + for i in xrange(len(points)): + p = points[i] + self.showDebugPointText(str(i), p) + + def showDropPoints(self, points): + self.hideDebugPointText() + for i in xrange(len(points)): + p = points[i] + self.showDebugPointText(str(i), p) + + def showPaths(self): + pass + + def hidePaths(self): + self.hideDebugPointText() + + def hideDebugPointText(self): + if hasattr(self, 'debugText'): + children = self.debugText.getChildren() + for i in xrange(children.getNumPaths()): + children[i].removeNode() + + def showDebugPointText(self, text, point): + if not hasattr(self, 'debugText'): + self.debugText = self.loader.geom.attachNewNode('debugText') + self.debugTextNode = TextNode('debugTextNode') + self.debugTextNode.setTextColor(1, 0, 0, 1) + self.debugTextNode.setAlign(TextNode.ACenter) + self.debugTextNode.setFont(ToontownGlobals.getSignFont()) + self.debugTextNode.setText(text) + np = self.debugText.attachNewNode(self.debugTextNode.generate()) + np.setPos(point[0], point[1], point[2]) + np.setScale(4.0) + np.setBillboardPointEye() + + def enterTrolley(self): + base.localAvatar.laffMeter.start() + base.localAvatar.b_setAnimState('off', 1) + base.localAvatar.cantLeaveGame = 1 + self.accept(self.trolleyDoneEvent, self.handleTrolleyDone) + self.trolley = Trolley.Trolley(self, self.fsm, self.trolleyDoneEvent) + self.trolley.load() + self.trolley.enter() + + def exitTrolley(self): + base.localAvatar.laffMeter.stop() + base.localAvatar.cantLeaveGame = 0 + self.ignore(self.trolleyDoneEvent) + self.trolley.unload() + self.trolley.exit() + del self.trolley + + def detectedTrolleyCollision(self): + self.fsm.request('trolley') + + def handleTrolleyDone(self, doneStatus): + self.notify.debug('handling trolley done event') + mode = doneStatus['mode'] + if mode == 'reject': + self.fsm.request('walk') + elif mode == 'exit': + self.fsm.request('walk') + elif mode == 'minigame': + self.doneStatus = {'loader': 'minigame', + 'where': 'minigame', + 'hoodId': self.loader.hood.id, + 'zoneId': doneStatus['zoneId'], + 'shardId': None, + 'minigameId': doneStatus['minigameId']} + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + mode + ' in handleTrolleyDone') + return + + def debugStartMinigame(self, zoneId, minigameId): + self.doneStatus = {'loader': 'minigame', + 'where': 'minigame', + 'hoodId': self.loader.hood.id, + 'zoneId': zoneId, + 'shardId': None, + 'minigameId': minigameId} + messenger.send(self.doneEvent) + return + + def doRequestLeave(self, requestStatus): + self.fsm.request('NPCFA', [requestStatus]) + + def enterHFA(self, requestStatus): + self.acceptOnce(self.hfaDoneEvent, self.enterHFACallback, [requestStatus]) + self.hfa = HealthForceAcknowledge.HealthForceAcknowledge(self.hfaDoneEvent) + self.hfa.enter(1) + + def exitHFA(self): + self.ignore(self.hfaDoneEvent) + + def enterHFACallback(self, requestStatus, doneStatus): + self.hfa.exit() + del self.hfa + if doneStatus['mode'] == 'complete': + if requestStatus.get('partyHat', 0): + outHow = {'teleportIn': 'tunnelOut'} + else: + outHow = {'teleportIn': 'teleportOut', + 'tunnelIn': 'tunnelOut', + 'doorIn': 'doorOut'} + self.fsm.request(outHow[requestStatus['how']], [requestStatus]) + elif doneStatus['mode'] == 'incomplete': + self.fsm.request('HFAReject') + else: + self.notify.error('Unknown done status for HealthForceAcknowledge: ' + `doneStatus`) + + def enterHFAReject(self): + self.fsm.request('walk') + + def exitHFAReject(self): + pass + + def enterNPCFA(self, requestStatus): + self.acceptOnce(self.npcfaDoneEvent, self.enterNPCFACallback, [requestStatus]) + self.npcfa = NPCForceAcknowledge.NPCForceAcknowledge(self.npcfaDoneEvent) + self.npcfa.enter() + + def exitNPCFA(self): + self.ignore(self.npcfaDoneEvent) + + def enterNPCFACallback(self, requestStatus, doneStatus): + self.npcfa.exit() + del self.npcfa + if doneStatus['mode'] == 'complete': + self.fsm.request('HFA', [requestStatus]) + elif doneStatus['mode'] == 'incomplete': + self.fsm.request('NPCFAReject') + else: + self.notify.error('Unknown done status for NPCForceAcknowledge: ' + `doneStatus`) + + def enterNPCFAReject(self): + self.fsm.request('walk') + + def exitNPCFAReject(self): + pass + + def enterWalk(self, teleportIn = 0): + if self.deathAckBox: + self.ignore('deathAck') + self.deathAckBox.cleanup() + self.deathAckBox = None + Place.Place.enterWalk(self, teleportIn) + return + + def enterDeathAck(self, requestStatus): + self.deathAckBox = None + self.fsm.request('teleportIn', [requestStatus]) + return + + def exitDeathAck(self): + if self.deathAckBox: + self.ignore('deathAck') + self.deathAckBox.cleanup() + self.deathAckBox = None + return + + def enterTeleportIn(self, requestStatus): + imgScale = 0.25 + if self.dialog: + x, y, z, h, p, r = base.cr.hoodMgr.getPlaygroundCenterFromId(self.loader.hood.id) + elif base.localAvatar.hp < 1: + requestStatus['nextState'] = 'popup' + x, y, z, h, p, r = base.cr.hoodMgr.getPlaygroundCenterFromId(self.loader.hood.id) + self.accept('deathAck', self.__handleDeathAck, extraArgs=[requestStatus]) + self.deathAckBox = DeathForceAcknowledge.DeathForceAcknowledge(doneEvent='deathAck') + elif base.localAvatar.hp > 0 and (Quests.avatarHasTrolleyQuest(base.localAvatar) or Quests.avatarHasFirstCogQuest(base.localAvatar) or Quests.avatarHasFriendQuest(base.localAvatar) or Quests.avatarHasPhoneQuest(base.localAvatar) and Quests.avatarHasCompletedPhoneQuest(base.localAvatar)) and self.loader.hood.id == ToontownGlobals.ToontownCentral: + requestStatus['nextState'] = 'popup' + imageModel = loader.loadModel('phase_4/models/gui/tfa_images') + if base.localAvatar.quests[0][0] == Quests.TROLLEY_QUEST_ID: + if not Quests.avatarHasCompletedTrolleyQuest(base.localAvatar): + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralInitialDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage3 + imgNodePath = imageModel.find('**/trolley-dialog-image') + imgPos = (0, 0, 0.04) + imgScale = 0.5 + else: + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralHQDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage4 + imgNodePath = imageModel.find('**/hq-dialog-image') + imgPos = (0, 0, -0.02) + imgScale = 0.5 + elif base.localAvatar.quests[0][0] == Quests.FIRST_COG_QUEST_ID: + if not Quests.avatarHasCompletedFirstCogQuest(base.localAvatar): + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralTunnelDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage5 + imgNodePath = imageModel.find('**/tunnelSignA') + imgPos = (0, 0, 0.04) + imgScale = 0.5 + else: + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralHQDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage6 + imgNodePath = imageModel.find('**/hq-dialog-image') + imgPos = (0, 0, 0.05) + imgScale = 0.5 + elif base.localAvatar.quests[0][0] == Quests.FRIEND_QUEST_ID: + if not Quests.avatarHasCompletedFriendQuest(base.localAvatar): + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralInitialDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage7 + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + imgNodePath = gui.find('**/FriendsBox_Closed') + imgPos = (0, 0, 0.04) + imgScale = 1.0 + gui.removeNode() + else: + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralHQDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage8 + imgNodePath = imageModel.find('**/hq-dialog-image') + imgPos = (0, 0, 0.05) + imgScale = 0.5 + elif base.localAvatar.quests[0][0] == Quests.PHONE_QUEST_ID: + if Quests.avatarHasCompletedPhoneQuest(base.localAvatar): + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralHQDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage9 + imgNodePath = imageModel.find('**/hq-dialog-image') + imgPos = (0, 0, 0.05) + imgScale = 0.5 + self.dialog = TTDialog.TTDialog(text=msg, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + imgLabel = DirectLabel.DirectLabel(parent=self.dialog, relief=None, pos=imgPos, scale=TTLocalizer.PimgLabel, image=imgNodePath, image_scale=imgScale) + imageModel.removeNode() + else: + requestStatus['nextState'] = 'walk' + x, y, z, h, p, r = base.cr.hoodMgr.getPlaygroundCenterFromId(self.loader.hood.id) + base.localAvatar.detachNode() + base.localAvatar.setPosHpr(render, x, y, z, h, p, r) + Place.Place.enterTeleportIn(self, requestStatus) + return + + def __cleanupDialog(self, value): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + if hasattr(self, 'fsm'): + self.fsm.request('walk', [1]) + return + + def __handleDeathAck(self, requestStatus): + if self.deathAckBox: + self.ignore('deathAck') + self.deathAckBox.cleanup() + self.deathAckBox = None + self.fsm.request('walk', [1]) + return + + def enterPopup(self, teleportIn = 0): + if base.localAvatar.hp < 1: + base.localAvatar.b_setAnimState('Sad', 1) + else: + base.localAvatar.b_setAnimState('neutral', 1.0) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.startSleepWatch(self.__handleFallingAsleepPopup) + + def exitPopup(self): + base.localAvatar.stopSleepWatch() + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + + def __handleFallingAsleepPopup(self, task): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + base.localAvatar.forceGotoSleep() + return Task.done + + def enterTeleportOut(self, requestStatus): + Place.Place.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + teleportDebug(requestStatus, 'Playground.__teleportOutDone(%s)' % (requestStatus,)) + if hasattr(self, 'activityFsm'): + self.activityFsm.requestFinalState() + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + avId = requestStatus['avId'] + shardId = requestStatus['shardId'] + if hoodId == self.loader.hood.hoodId and zoneId == self.loader.hood.hoodId and shardId == None: + teleportDebug(requestStatus, 'same playground') + self.fsm.request('deathAck', [requestStatus]) + elif hoodId == ToontownGlobals.MyEstate: + teleportDebug(requestStatus, 'estate') + self.getEstateZoneAndGoHome(requestStatus) + else: + teleportDebug(requestStatus, 'different hood/zone') + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + def exitTeleportOut(self): + Place.Place.exitTeleportOut(self) + + def createPlayground(self, dnaFile): + dnaBulk = DNABulkLoader(self.loader.dnaStore, (self.safeZoneStorageDNAFile,)) + dnaBulk.loadDNAFiles() + node = loader.loadDNAFile(self.loader.dnaStore, dnaFile) + if node.getNumParents() == 1: + self.geom = NodePath(node.getParent(0)) + self.geom.reparentTo(hidden) + else: + self.geom = hidden.attachNewNode(node) + self.makeDictionaries(self.loader.dnaStore) + self.tunnelOriginList = base.cr.hoodMgr.addLinkTunnelHooks(self, self.nodeList) + self.geom.flattenMedium() + gsg = base.win.getGsg() + if gsg: + self.geom.prepareScene(gsg) + + def makeDictionaries(self, dnaStore): + self.nodeList = [] + for i in xrange(dnaStore.getNumDNAVisGroups()): + groupFullName = dnaStore.getDNAVisGroupName(i) + groupName = base.cr.hoodMgr.extractGroupName(groupFullName) + groupNode = self.geom.find('**/' + groupFullName) + if groupNode.isEmpty(): + self.notify.error('Could not find visgroup') + self.nodeList.append(groupNode) + + self.removeLandmarkBlockNodes() + self.loader.dnaStore.resetPlaceNodes() + self.loader.dnaStore.resetDNAGroups() + self.loader.dnaStore.resetDNAVisGroups() + self.loader.dnaStore.resetDNAVisGroupsAI() + + def removeLandmarkBlockNodes(self): + npc = self.geom.findAllMatches('**/suit_building_origin') + for i in xrange(npc.getNumPaths()): + npc.getPath(i).removeNode() diff --git a/toontown/safezone/PublicWalk.py b/toontown/safezone/PublicWalk.py new file mode 100755 index 00000000..9745ac0e --- /dev/null +++ b/toontown/safezone/PublicWalk.py @@ -0,0 +1,60 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +import Walk + +class PublicWalk(Walk.Walk): + notify = DirectNotifyGlobal.directNotify.newCategory('PublicWalk') + + def __init__(self, parentFSM, doneEvent): + Walk.Walk.__init__(self, doneEvent) + self.parentFSM = parentFSM + + def load(self): + Walk.Walk.load(self) + + def unload(self): + Walk.Walk.unload(self) + del self.parentFSM + + def enter(self, slowWalk = 0): + Walk.Walk.enter(self, slowWalk) + base.localAvatar.book.showButton() + self.accept(StickerBookHotkey, self.__handleStickerBookEntry) + self.accept('enterStickerBook', self.__handleStickerBookEntry) + self.accept(OptionsPageHotkey, self.__handleOptionsEntry) + base.localAvatar.laffMeter.start() + base.localAvatar.beginAllowPies() + + def exit(self): + Walk.Walk.exit(self) + base.localAvatar.book.hideButton() + self.ignore(StickerBookHotkey) + self.ignore('enterStickerBook') + self.ignore(OptionsPageHotkey) + base.localAvatar.laffMeter.stop() + base.localAvatar.endAllowPies() + + def __handleStickerBookEntry(self): + currentState = base.localAvatar.animFSM.getCurrentState().getName() + if currentState == 'jumpAirborne': + return + if base.localAvatar.book.isObscured(): + return + else: + doneStatus = {} + doneStatus['mode'] = 'StickerBook' + messenger.send(self.doneEvent, [doneStatus]) + return + + def __handleOptionsEntry(self): + currentState = base.localAvatar.animFSM.getCurrentState().getName() + if currentState == 'jumpAirborne': + return + if base.localAvatar.book.isObscured(): + return + else: + doneStatus = {} + doneStatus['mode'] = 'Options' + messenger.send(self.doneEvent, [doneStatus]) + return diff --git a/toontown/safezone/RegenTreasurePlannerAI.py b/toontown/safezone/RegenTreasurePlannerAI.py new file mode 100755 index 00000000..85fadf6a --- /dev/null +++ b/toontown/safezone/RegenTreasurePlannerAI.py @@ -0,0 +1,43 @@ +from direct.distributed.ClockDelta import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +import random +import TreasurePlannerAI + +class RegenTreasurePlannerAI(TreasurePlannerAI.TreasurePlannerAI): + notify = DirectNotifyGlobal.directNotify.newCategory('RegenTreasurePlannerAI') + + def __init__(self, zoneId, treasureType, taskName, spawnInterval, maxTreasures, callback = None): + TreasurePlannerAI.TreasurePlannerAI.__init__(self, zoneId, treasureType, callback) + self.taskName = '%s-%s' % (taskName, zoneId) + self.spawnInterval = spawnInterval + self.maxTreasures = maxTreasures + + def start(self): + self.preSpawnTreasures() + self.startSpawning() + + def stop(self): + self.stopSpawning() + + def stopSpawning(self): + taskMgr.remove(self.taskName) + + def startSpawning(self): + self.stopSpawning() + taskMgr.doMethodLater(self.spawnInterval, self.upkeepTreasurePopulation, self.taskName) + + def upkeepTreasurePopulation(self, task): + if self.numTreasures() < self.maxTreasures: + self.placeRandomTreasure() + taskMgr.doMethodLater(self.spawnInterval, self.upkeepTreasurePopulation, self.taskName) + return Task.done + + def placeRandomTreasure(self): + spawnPointIndex = self.nthEmptyIndex(random.randrange(self.countEmptySpawnPoints())) + self.placeTreasure(spawnPointIndex) + + def preSpawnTreasures(self): + for i in xrange(self.maxTreasures): + self.placeRandomTreasure() diff --git a/toontown/safezone/SZTreasurePlannerAI.py b/toontown/safezone/SZTreasurePlannerAI.py new file mode 100755 index 00000000..04a3f7d3 --- /dev/null +++ b/toontown/safezone/SZTreasurePlannerAI.py @@ -0,0 +1,23 @@ +from RegenTreasurePlannerAI import RegenTreasurePlannerAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals + +class SZTreasurePlannerAI(RegenTreasurePlannerAI): + notify = DirectNotifyGlobal.directNotify.newCategory('SZTreasurePlannerAI') + + def __init__(self, zoneId, treasureType, healAmount, spawnPoints, spawnRate, maxTreasures): + self.zoneId = zoneId + self.spawnPoints = spawnPoints + self.healAmount = healAmount + RegenTreasurePlannerAI.__init__(self, zoneId, treasureType, 'SZTreasurePlanner-%d' % zoneId, spawnRate, maxTreasures) + + def initSpawnPoints(self): + pass + + def validAvatar(self, treasure, av): + # Avatars can only heal if they are missing some health, but aren't sad. + if av.getHp() < av.getMaxHp() and av.getHp() > 0: + av.toonUp(self.healAmount * 2 if simbase.air.newsManager.isHolidayRunning(ToontownGlobals.VALENTOONS_DAY) else self.healAmount) + return True + else: + return False diff --git a/toontown/safezone/SZUtil.py b/toontown/safezone/SZUtil.py new file mode 100644 index 00000000..3a63ad86 --- /dev/null +++ b/toontown/safezone/SZUtil.py @@ -0,0 +1,60 @@ +from panda3d.core import * +from direct.task.Task import Task +from toontown.battle import BattleParticles +import colorsys + +def createSnow(geom): + snow = BattleParticles.loadParticleFile('snowdisk.ptf') + snow.setPos(0, 0, 5) + snowRender = geom.attachNewNode('snowRender') + snowRender.setDepthWrite(0) + snowRender.setBin('fixed', 1) + + return snow, snowRender + +def startUnderwaterFog(): + if not base.wantFog: + return + + stopUnderwaterFog() + taskMgr.add(__updateUnderwaterFog, 'underwaterFog') + +def stopUnderwaterFog(): + taskMgr.remove('underwaterFog') + +def __updateUnderwaterFog(task): + fog = base.cr.playGame.hood.fog if hasattr(base.cr.playGame.hood, 'fog') else base.cr.playGame.place.fog + saturation = min(max((base.localAvatar.getZ() / -12.3), 0.51), 1) + fog.setColor(*colorsys.hsv_to_rgb(0.616, saturation, 0.5)) + return task.cont + +def cloudSkyTrack(task): + task.h += globalClock.getDt() * 0.25 + + if task.cloud1.isEmpty() or task.cloud2.isEmpty(): + return + + task.cloud1.setH(task.h) + task.cloud2.setH(-task.h * 0.8) + return task.cont + +def startCloudSky(hood, parent=camera, effects=CompassEffect.PRot | CompassEffect.PZ): + hood.sky.reparentTo(parent) + hood.sky.setDepthTest(0) + hood.sky.setDepthWrite(0) + hood.sky.setBin('background', 100) + hood.sky.find('**/Sky').reparentTo(hood.sky, -1) + hood.sky.reparentTo(parent) + hood.sky.setZ(0.0) + hood.sky.setHpr(0.0, 0.0, 0.0) + + ce = CompassEffect.make(NodePath(), effects) + + hood.sky.node().setEffect(ce) + skyTrackTask = Task(hood.skyTrack) + skyTrackTask.h = 0 + skyTrackTask.cloud1 = hood.sky.find('**/cloud1') + skyTrackTask.cloud2 = hood.sky.find('**/cloud2') + + if not skyTrackTask.cloud1.isEmpty() and not skyTrackTask.cloud2.isEmpty(): + taskMgr.add(skyTrackTask, 'skyTrack') \ No newline at end of file diff --git a/toontown/safezone/SafeZoneLoader.py b/toontown/safezone/SafeZoneLoader.py new file mode 100755 index 00000000..ebdfd2b4 --- /dev/null +++ b/toontown/safezone/SafeZoneLoader.py @@ -0,0 +1,244 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.distributed.ToontownMsgTypes import * +from toontown.hood import ZoneUtil +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import Place +from direct.showbase import DirectObject +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +from toontown.toon import HealthForceAcknowledge +from toontown.toon.Toon import teleportDebug +from toontown.toonbase.ToontownGlobals import * +from toontown.building import ToonInterior +from toontown.hood import QuietZoneState +from toontown.dna.DNAParser import * +from direct.stdpy.file import * + +class SafeZoneLoader(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('SafeZoneLoader') + + def __init__(self, hood, parentFSMState, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.hood = hood + self.parentFSMState = parentFSMState + self.fsm = ClassicFSM.ClassicFSM('SafeZoneLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'playground', 'toonInterior']), + State.State('playground', self.enterPlayground, self.exitPlayground, ['quietZone']), + State.State('toonInterior', self.enterToonInterior, self.exitToonInterior, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['playground', 'toonInterior']), + State.State('golfcourse', self.enterGolfcourse, self.exitGolfcourse, ['quietZone', 'playground']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.placeDoneEvent = 'placeDone' + self.place = None + self.playgroundClass = None + return + + def load(self): + self.music = base.loadMusic(self.musicFile) + self.activityMusic = base.loadMusic(self.activityMusicFile) + self.createSafeZone(self.dnaFile) + self.parentFSMState.addChild(self.fsm) + + def unload(self): + self.parentFSMState.removeChild(self.fsm) + del self.parentFSMState + self.geom.removeNode() + del self.geom + del self.fsm + del self.hood + del self.nodeList + del self.playgroundClass + del self.music + del self.activityMusic + del self.holidayPropTransforms + self.deleteAnimatedProps() + self.ignoreAll() + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def enter(self, requestStatus): + self.fsm.enterInitialState() + messenger.send('enterSafeZone') + self.setState(requestStatus['where'], requestStatus) + if not base.config.GetBool('want-parties', True): + partyGate = self.geom.find('**/prop_party_gate_DNARoot') + if not partyGate.isEmpty(): + partyGate.removeNode() + + def exit(self): + messenger.send('exitSafeZone') + + def setState(self, stateName, requestStatus): + self.fsm.request(stateName, [requestStatus]) + + def createSafeZone(self, dnaFile): + if self.safeZoneStorageDNAFile: + dnaBulk = DNABulkLoader(self.hood.dnaStore, (self.safeZoneStorageDNAFile,)) + dnaBulk.loadDNAFiles() + node = loadDNAFile(self.hood.dnaStore, dnaFile) + if node.getNumParents() == 1: + self.geom = NodePath(node.getParent(0)) + self.geom.reparentTo(hidden) + else: + self.geom = hidden.attachNewNode(node) + self.makeDictionaries(self.hood.dnaStore) + self.createAnimatedProps(self.nodeList) + self.holidayPropTransforms = {} + npl = self.geom.findAllMatches('**/=DNARoot=holiday_prop') + for i in xrange(npl.getNumPaths()): + np = npl.getPath(i) + np.setTag('transformIndex', `i`) + self.holidayPropTransforms[i] = np.getNetTransform() + gsg = base.win.getGsg() + if gsg: + self.geom.prepareScene(gsg) + self.geom.flattenMedium() + + def makeDictionaries(self, dnaStore): + self.nodeList = [] + for i in xrange(dnaStore.getNumDNAVisGroups()): + groupFullName = dnaStore.getDNAVisGroupName(i) + groupName = base.cr.hoodMgr.extractGroupName(groupFullName) + groupNode = self.geom.find('**/' + groupFullName) + if groupNode.isEmpty(): + self.notify.error('Could not find visgroup') + groupNode.flattenMedium() + self.nodeList.append(groupNode) + + self.removeLandmarkBlockNodes() + self.hood.dnaStore.resetPlaceNodes() + self.hood.dnaStore.resetDNAGroups() + self.hood.dnaStore.resetDNAVisGroups() + self.hood.dnaStore.resetDNAVisGroupsAI() + + def removeLandmarkBlockNodes(self): + npc = self.geom.findAllMatches('**/suit_building_origin') + for i in xrange(npc.getNumPaths()): + npc.getPath(i).removeNode() + + def enterStart(self): + pass + + def exitStart(self): + pass + + def enterPlayground(self, requestStatus): + self.acceptOnce(self.placeDoneEvent, self.handlePlaygroundDone) + self.place = self.playgroundClass(self, self.fsm, self.placeDoneEvent) + self.place.load() + self.place.enter(requestStatus) + base.cr.playGame.setPlace(self.place) + + def exitPlayground(self): + self.ignore(self.placeDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def handlePlaygroundDone(self): + status = self.place.doneStatus + teleportDebug(status, 'handlePlaygroundDone, doneStatus=%s' % (status,)) + if ZoneUtil.getBranchZone(status['zoneId']) == self.hood.hoodId and status['shardId'] == None: + teleportDebug(status, 'same branch') + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + teleportDebug(status, 'different hood') + messenger.send(self.doneEvent) + return + + def enterToonInterior(self, requestStatus): + self.acceptOnce(self.placeDoneEvent, self.handleToonInteriorDone) + self.place = ToonInterior.ToonInterior(self, self.fsm.getStateNamed('toonInterior'), self.placeDoneEvent) + base.cr.playGame.setPlace(self.place) + self.place.load() + self.place.enter(requestStatus) + + def exitToonInterior(self): + self.ignore(self.placeDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def handleToonInteriorDone(self): + status = self.place.doneStatus + if ZoneUtil.getBranchZone(status['zoneId']) == self.hood.hoodId and status['shardId'] == None: + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + messenger.send(self.doneEvent) + return + + def enterQuietZone(self, requestStatus): + self.quietZoneDoneEvent = uniqueName('quietZoneDone') + self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone) + self.quietZoneStateData = QuietZoneState.QuietZoneState(self.quietZoneDoneEvent) + self.quietZoneStateData.load() + self.quietZoneStateData.enter(requestStatus) + + def exitQuietZone(self): + self.ignore(self.quietZoneDoneEvent) + del self.quietZoneDoneEvent + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + return + + def handleQuietZoneDone(self): + status = self.quietZoneStateData.getRequestStatus() + if status['where'] == 'estate': + self.doneStatus = status + messenger.send(self.doneEvent) + else: + self.fsm.request(status['where'], [status]) + + def enterFinal(self): + pass + + def exitFinal(self): + pass + + def createAnimatedProps(self, nodeList): + self.animPropDict = {} + for i in nodeList: + animPropNodes = i.findAllMatches('**/animated_prop_*') + numAnimPropNodes = animPropNodes.getNumPaths() + for j in xrange(numAnimPropNodes): + animPropNode = animPropNodes.getPath(j) + if animPropNode.getName().startswith('animated_prop_generic'): + className = 'GenericAnimatedProp' + else: + className = animPropNode.getName()[14:-8] + symbols = {} + base.cr.importModule(symbols, 'toontown.hood', [className]) + classObj = getattr(symbols[className], className) + animPropObj = classObj(animPropNode) + animPropList = self.animPropDict.setdefault(i, []) + animPropList.append(animPropObj) + + def deleteAnimatedProps(self): + for zoneNode, animPropList in self.animPropDict.items(): + for animProp in animPropList: + animProp.delete() + + del self.animPropDict + + def enterAnimatedProps(self, zoneNode): + for animProp in self.animPropDict.get(zoneNode, ()): + animProp.enter() + + def exitAnimatedProps(self, zoneNode): + for animProp in self.animPropDict.get(zoneNode, ()): + animProp.exit() + + def enterGolfcourse(self, requestStatus): + base.transitions.fadeOut(t=0) + + def exitGolfcourse(self): + pass diff --git a/toontown/safezone/SafeZoneManager.py b/toontown/safezone/SafeZoneManager.py new file mode 100755 index 00000000..1f32875e --- /dev/null +++ b/toontown/safezone/SafeZoneManager.py @@ -0,0 +1,25 @@ +from panda3d.core import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal + +class SafeZoneManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('SafeZoneManager') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.accept('enterSafeZone', self.d_enterSafeZone) + self.accept('exitSafeZone', self.d_exitSafeZone) + + def disable(self): + self.ignoreAll() + DistributedObject.DistributedObject.disable(self) + + def d_enterSafeZone(self): + self.sendUpdate('enterSafeZone', []) + + def d_exitSafeZone(self): + self.sendUpdate('exitSafeZone', []) diff --git a/toontown/safezone/SafeZoneManagerAI.py b/toontown/safezone/SafeZoneManagerAI.py new file mode 100755 index 00000000..52854b1c --- /dev/null +++ b/toontown/safezone/SafeZoneManagerAI.py @@ -0,0 +1,20 @@ +from direct.directnotify.DirectNotifyGlobal import * +from direct.distributed import DistributedObjectAI +from toontown.toonbase import ToontownGlobals + +class SafeZoneManagerAI(DistributedObjectAI.DistributedObjectAI): + notify = directNotify.newCategory('SafeZoneManagerAI') + + def enterSafeZone(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if not av: + return + av.startToonUp(ToontownGlobals.TOONUP_FREQUENCY) + + def exitSafeZone(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if not av: + return + av.stopToonUp() diff --git a/toontown/safezone/TTPlayground.py b/toontown/safezone/TTPlayground.py new file mode 100755 index 00000000..20893438 --- /dev/null +++ b/toontown/safezone/TTPlayground.py @@ -0,0 +1,18 @@ +from direct.task.Task import Task +from toontown.safezone import Playground +import random + +class TTPlayground(Playground.Playground): + def enter(self, requestStatus): + Playground.Playground.enter(self, requestStatus) + taskMgr.doMethodLater(1, self.__birds, 'TT-birds') + + def exit(self): + Playground.Playground.exit(self) + taskMgr.remove('TT-birds') + + def __birds(self, task): + base.playSfx(random.choice(self.loader.birdSound)) + time = random.random() * 20.0 + 1 + taskMgr.doMethodLater(time, self.__birds, 'TT-birds') + return Task.done diff --git a/toontown/safezone/TTSafeZoneLoader.py b/toontown/safezone/TTSafeZoneLoader.py new file mode 100755 index 00000000..ae73f407 --- /dev/null +++ b/toontown/safezone/TTSafeZoneLoader.py @@ -0,0 +1,27 @@ +from toontown.safezone import SafeZoneLoader +from toontown.safezone import TTPlayground + +class TTSafeZoneLoader(SafeZoneLoader.SafeZoneLoader): + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.playgroundClass = TTPlayground.TTPlayground + self.musicFile = 'phase_4/audio/bgm/TC_nbrhood.ogg' + self.activityMusicFile = 'phase_3.5/audio/bgm/TC_SZ_activity.ogg' + self.dnaFile = 'phase_4/dna/toontown_central_sz.pdna' + self.safeZoneStorageDNAFile = 'phase_4/dna/storage_TT_sz.pdna' + + def load(self): + SafeZoneLoader.SafeZoneLoader.load(self) + self.birdSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.ogg', + 'phase_4/audio/sfx/SZ_TC_bird2.ogg', + 'phase_4/audio/sfx/SZ_TC_bird3.ogg']) + bank = self.geom.find('**/*toon_landmark_TT_bank_DNARoot') + library = self.geom.find('**/library/square_drop_shadow') + doorTrigger = bank.find('**/door_trigger*') + doorTrigger.setY(doorTrigger.getY() - 1.5) + library.find('**/building_front').setY(0.3) + library.find('**/front_entrance_flag').setY(0.1) + + def unload(self): + SafeZoneLoader.SafeZoneLoader.unload(self) + del self.birdSound diff --git a/toontown/safezone/Train.py b/toontown/safezone/Train.py new file mode 100755 index 00000000..0742f537 --- /dev/null +++ b/toontown/safezone/Train.py @@ -0,0 +1,164 @@ +from panda3d.core import * +from direct.showbase.DirectObject import DirectObject +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import globalClockDelta +from direct.distributed.ClockDelta import NetworkTimePrecision +import random +from direct.task.Task import Task +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.directutil import Mopath +from toontown.toonbase import ToontownGlobals +from direct.actor import Actor + +class Train(DirectObject): + notify = directNotify.newCategory('Train') + nameId = 0 + Sfx_TrainPass = 'phase_10/audio/sfx/CBHQ_TRAIN_pass.ogg' + Sfx_TrainStopStart = 'phase_10/audio/sfx/CBHQ_TRAIN_stopstart.ogg' + LocomotiveFile = 'phase_10/models/cogHQ/CashBotLocomotive' + CarFiles = ['phase_10/models/cogHQ/CashBotBoxCar', 'phase_10/models/cogHQ/CashBotTankCar', 'phase_10/models/cogHQ/CashBotFlatCar'] + CarLength = 88 + MarkDelta = 15 + + def __init__(self, trackStartPos, trackEndPos, trackNum, numTotalTracks): + self.trackStartPos = trackStartPos + self.trackEndPos = trackEndPos + self.numCars = len(self.CarFiles) + self.locomotive = loader.loadModel(self.LocomotiveFile) + self.locomotive.flattenStrong() + self.cars = [] + self.trainPassingSfx = base.loadSfx(self.Sfx_TrainPass) + self.trainStopStartSfx = base.loadSfx(self.Sfx_TrainStopStart) + self.trainId = trackNum + self.bFlipped = False + if trackStartPos[0] < trackEndPos[0]: + self.locomotive.setHpr(180, 0, 0) + self.bFlipped = True + self.collNodeName = 'CollNode-%s' % self.trainId + self.firstMark = self.MarkDelta / numTotalTracks * trackNum + currentTime = self.__networkTimeInSeconds() + currentRun = int((currentTime - self.firstMark) / self.MarkDelta) + self.lastMark = currentRun * self.MarkDelta + self.firstMark + self.doNextRun(True) + self.hide() + + def hide(self): + if self.locomotive: + self.locomotive.reparentTo(hidden) + + def show(self): + if self.locomotive: + self.locomotive.reparentTo(render) + + def __networkTimeInSeconds(self): + time = globalClockDelta.getRealNetworkTime(bits=32) / NetworkTimePrecision + return time + + def doNextRun(self, bFirstRun = False): + if self.locomotive: + if bFirstRun: + nextMark = self.lastMark + else: + nextMark = self.lastMark + self.MarkDelta + self.nextRun.finish() + self.notify.debug('Next mark %s' % nextMark) + currentTime = self.__networkTimeInSeconds() + timeTillNextMark = nextMark - currentTime + self.notify.debug('Time diff %s' % timeTillNextMark) + runNumber = int((nextMark - self.firstMark) / self.MarkDelta) + S = random.getstate() + random.seed(self.trainId + runNumber) + self.nextRun = self.__getNextRun() + random.setstate(S) + self.__startNextRun(timeTillNextMark) + self.lastMark = nextMark + return Task.done + + def __startNextRun(self, timeTillMark): + if self.locomotive: + self.__disableCollisions() + if timeTillMark > 0: + self.nextRun = Sequence(Wait(timeTillMark), self.nextRun) + self.nextRun.start() + else: + self.nextRun.start(-1 * timeTillMark) + self.__enableCollisions() + return Task.done + + def __cleanupCars(self): + self.__disableCollisions() + for car in self.cars: + car.removeNode() + + self.cars = [] + + def __getCars(self): + self.__cleanupCars() + numCarsThisRun = random.randrange(1, 10) + for nCar in xrange(numCarsThisRun): + carType = random.randrange(0, self.numCars) + car = loader.loadModel(self.CarFiles[carType]) + car.flattenStrong() + car.reparentTo(self.locomotive) + car.setPos(self.CarLength * (nCar + 1), 0, 0) + self.cars.append(car) + + def __showStart(self): + self.notify.debug('Starting train %s at %s.' % (self.trainId, self.__networkTimeInSeconds())) + + def __getNextRun(self): + self.__getCars() + trainShouldStop = random.randrange(0, 4) + nextRun = Sequence(Func(self.__showStart)) + if trainShouldStop is 0: + waitTime = 3 + totalTime = random.randrange(4, (self.MarkDelta - waitTime) / 2) + sfxStopTime = 4.3 + halfway = (self.trackStartPos + self.trackEndPos) / 2 + halfway.setX(150) + nextRun.append(Parallel(Sequence(Wait(totalTime - sfxStopTime), SoundInterval(self.trainStopStartSfx, volume=0.5)), Sequence(LerpPosInterval(self.locomotive, totalTime, halfway, self.trackStartPos, blendType='easeInOut'), WaitInterval(waitTime), LerpPosInterval(self.locomotive, totalTime, self.trackEndPos, halfway, blendType='easeIn')))) + else: + totalTime = random.randrange(6, self.MarkDelta - 1) + sfxTime = 7 + sfxStartTime = totalTime / 2 - sfxTime / 2 + if self.bFlipped: + sfxStartTime -= 1 + else: + sfxStartTime += 1 + nextRun.append(Parallel(Sequence(Wait(sfxStartTime), SoundInterval(self.trainPassingSfx, volume=0.5)), LerpPosInterval(self.locomotive, totalTime, self.trackEndPos, self.trackStartPos))) + nextRun.append(Func(self.doNextRun)) + return nextRun + + def delete(self): + self.__cleanupCars() + self.locomotive.removeNode() + self.locomotive = None + self.nextRun.finish() + self.nextRun = None + del self.trainPassingSfx + del self.trainStopStartSfx + return + + def uniqueName(self, name): + Train.nameId += 1 + return name + '-%d' % Train.nameId + + def __enableCollisions(self): + allColls = self.locomotive.findAllMatches('**/+CollisionNode') + for car in self.cars: + carColls = car.findAllMatches('**/+CollisionNode') + allColls += carColls + + for collNode in allColls: + collNode.setName(self.collNodeName) + collNode.setCollideMask(ToontownGlobals.WallBitmask) + + self.accept('enter' + self.collNodeName, self.__handleCollisionSphereEnter) + + def __disableCollisions(self): + self.ignore('enter' + self.collNodeName) + + def __handleCollisionSphereEnter(self, collEntry = None): + base.localAvatar.b_squish(10) diff --git a/toontown/safezone/TreasureGlobals.py b/toontown/safezone/TreasureGlobals.py new file mode 100755 index 00000000..0d77e106 --- /dev/null +++ b/toontown/safezone/TreasureGlobals.py @@ -0,0 +1,257 @@ +from toontown.toonbase import ToontownGlobals + +TreasureTT = 0 +TreasureDD = 1 +TreasureDG = 2 +TreasureBR = 3 +TreasureMM = 4 +TreasureDL = 5 +TreasureOZ = 6 +TreasureE = 7 + +ValentineTreasureModel = 'phase_4/models/props/tt_m_ara_ext_heart' + +TreasureModels = { + TreasureTT: ( + 'phase_4/models/props/icecream', + 'phase_4/audio/sfx/SZ_DD_treasure.ogg', + ), + TreasureDD: ( + 'phase_6/models/props/starfish_treasure', + 'phase_4/audio/sfx/SZ_DD_treasure.ogg', + ), + TreasureDG: ( + 'phase_8/models/props/flower_treasure', + 'phase_4/audio/sfx/SZ_DD_treasure.ogg', + ), + TreasureBR: ( + 'phase_8/models/props/snowflake_treasure', + 'phase_4/audio/sfx/SZ_DD_treasure.ogg', + ), + TreasureMM: ( + 'phase_6/models/props/music_treasure', + 'phase_4/audio/sfx/SZ_DD_treasure.ogg', + ), + TreasureDL: ( + 'phase_8/models/props/zzz_treasure', + 'phase_4/audio/sfx/SZ_DD_treasure.ogg', + ), + TreasureOZ: ( + 'phase_6/models/props/acorn_treasure', + 'phase_4/audio/sfx/SZ_DD_treasure.ogg', + ), + TreasureE: ( + 'phase_5.5/models/props/popsicle_treasure', + 'phase_4/audio/sfx/SZ_DD_treasure.ogg', + ), +} + +SafeZoneTreasureSpawns = { + ToontownGlobals.ToontownCentral: ( + TreasureTT, 3, # TTTreasure heals 3 each... + [ + (-60.976, -8.866, 1.3), + (-90.632, -5.828, -0.63), + (27.1, -93.5, 2.5), + (94.2, 33.5, 4), + (31.554, 56.915, 4), + (67.1, 105.5, 2.5), + (-99.15, -87.3407, 0.52499), + (8.183, -127.016, 3.025), + (39.684, -80.356, 2.525), + (129.137, -61.9039, 2.525), + (92.99, -158.399, 3.025), + (111.749, -8.59927, 4.57466), + (37.983, -26.281, 4.025), + (31.0649, -43.9149, 4.025), + (10.0156, 105.218, 2.525), + (46.9667, 169.143, 3.025), + (100.68, 93.9896, 2.525), + (129.285, 58.6107, 2.525), + (-28.6272, 85.9833, 0.525), + (-111.589, 79.414, 0.525), + (-136.296, 32.794, 0.525), + ], + 10, # Rate + 5 # Maximum + ), + ToontownGlobals.DonaldsDock: ( + TreasureDD, 6, # DDTreasure heals 6 each... + [ + (52.9072, -23.4768, -12.308), + (35.3827, -51.9196, -12.308), + (17.4252, -57.3107, -12.308), + (-0.716054, -68.5, -12.308), + (-29.0169, -66.8887, -12.308), + (-63.492, -64.2191, -12.308), + (-72.2423, -58.3686, -12.308), + (-97.9602, -42.8905, -12.308), + (-102.215, -34.1519, -12.308), + (-102.978, -4.09065, -12.308), + (-101.305, 30.6454, -12.308), + (-45.0621, -21.0088, -12.308), + (-11.4043, -29.0816, -12.308), + (2.33548, -7.71722, -12.308), + (-8.643, 33.9891, -12.308), + (-53.224, 18.1293, -12.308), + (-99.7225, -8.1298, -12.308), + (-100.457, 28.351, -12.308), + (-76.7946, 4.21199, -12.308), + (-64.9137, 37.5765, -12.308), + (-17.6075, 102.135, -12.308), + (-23.4112, 127.777, -12.308), + (-11.3513, 128.991, -12.308), + (-14.1068, 83.2043, -12.308), + (53.2685, 24.3585, -12.308), + (41.4197, 4.35384, -12.308), + ], + 10, # Rate + 2 # Maximum + ), + ToontownGlobals.DaisyGardens: ( + TreasureDG, 9, # DGTreasure heals 9 each... + [ + (-49, 156, 0.0), + (-59, 50, 0.0), + (19, 16, 0.0), + (76, 38, 1.1), + (102, 121, 0.0), + (69, 123, 0.0), + (49, 105, 0.0), + (24, 156, 0.0), + (-27, 127, 0.0), + (-56, 105, 0.0), + (-40, 113, 0.0), + (25, 114, 0.0), + (-6, 84, 0.0), + (19, 96, 0.0), + (0, 114, 0.0), + (-78, 157, 10.0), + (-33.4, 218.2, 10.0), + (57, 205, 10.0), + (32, 77, 0.0), + (-102, 101, 0.0), + ], + 7, # Rate + 2 # Maximum + ), + ToontownGlobals.TheBrrrgh: ( + TreasureBR, 15, # +15 laff + [ + (-108, 46, 6.2), + (-111, 74, 6.2), + (-126, 81, 6.2), + (-74, -75, 3.0), + (-136, -51, 3.0), + (-20, 35, 6.2), + (-55, 109, 6.2), + (58, -57, 6.2), + (-42, -134, 6.2), + (-68, -148, 6.2), + (-1, -62, 6.2), + (25, 2, 6.2), + (-133, 53, 6.2), + (-99, 86, 6.2), + (30, 63, 6.2), + (-147, 3, 6.2), + (-135, -102, 6.2), + (35, -98, 6.2), + ], + 10, # Rate + 2 # Maximum + ), + ToontownGlobals.MinniesMelodyland: ( + TreasureMM, 12, # +12 laff + [ + (118, -39, 3.3), + (118, 1, 3.3), + (112, -22, 0.8), + (108, -74, -4.5), + (110, -65, -4.5), + (102, 23.5, -4.5), + (60, -115, 6.5), + (-5, -115, 6.5), + (-64, -77, 6.5), + (-77, -44, 6.5), + (-76, 3, 6.5), + (44, 76, 6.5), + (136, -96, -13.5), + (85, -6.7, -13.5), + (60, -95, -14.5), + (72, 60, -13.5), + (-55, -23, -14.5), + (-21, 47, -14.5), + (-24, -75, -14.5), + ], + 10, # Rate + 2 # Maximum + ), + ToontownGlobals.DonaldsDreamland: ( + TreasureDL, 18, # +18 laff + [ + (86, 69, -17.4), + (34, -48, -16.4), + (87, -70, -17.5), + (-98, 99, 0.0), + (51, 100, 0.0), + (-45, -12, -15.0), + (9, 8, -15.0), + (-24, 64, -17.2), + (-100, -99, 0.0), + (21, -101, 0.0), + (88, -17, -15.0), + (32, 70, -17.4), + (53, 35, -15.8), + (2, -30, -15.5), + (-40, -56, -16.8), + (-28, 18, -15.0), + (-34, -88, 0.0), + ], + 10, # Rate + 2 #Maximum + ), + ToontownGlobals.OutdoorZone: ( + TreasureOZ, 3, # +3 laff + [ + (-156.9, -118.9, 0.025), + (-35.6, 86.0, 1.25), + (116.8, 10.8, 0.104), + (-35, 145.7, 0.025), + (-198.8, -45.1, 0.025), + (-47.1, -25.5, 0.809), + (59.15, 34.8, 1.767), + (-81.02, -72.2, 0.026), + (-167.9, 124.5, 0.025), + (-226.7, -27.6, 0.025), + (-16.0, -108.9, 0.025), + (18.0, 58.5, 5.919), + (91.4, 127.8, 0.025), + (-86.5, -75.9, 0.025), + (-48.751, -32.3, 1.143), + ], + 10, # Rate + 5 # Maximum + ), + ToontownGlobals.MyEstate: ( + TreasureE, 2, # +2 laff + [ + (102.9, 14.17, 0.57), + (131.3, 45.31, 0.42), + (24.58, -1.28, 11.75), + (-1.5, -99.63, 4.3), + (14.04, -133.65, -10.0), + (-89.45, -134.26, 0.42), + (-99.15, -87.3407, 0.52), + (-132.528, 31.255, 0.42), + (-44.8, 42.61, 11.8), + (1.31, 65.17, 5.2), + (56.9, 13.06, 29.1), + (-57.5, 14.0, 2.88), + (17.88, 93.89, 0.4), + (-14.39, -164.3, 0.5), + (-125.6, -64.82, 0.5), + ], + 10, # Rate + 4 # Maximum + ), +} diff --git a/toontown/safezone/TreasurePlannerAI.py b/toontown/safezone/TreasurePlannerAI.py new file mode 100755 index 00000000..0326ff63 --- /dev/null +++ b/toontown/safezone/TreasurePlannerAI.py @@ -0,0 +1,145 @@ +from direct.distributed.ClockDelta import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from DistributedTreasureAI import DistributedTreasureAI +import random + +class TreasurePlannerAI(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TreasurePlannerAI') + + def __init__(self, zoneId, treasureType, callback = None): + self.zoneId = zoneId + self.treasureType = treasureType + self.callback = callback + self.initSpawnPoints() + self.treasures = [] + for spawnPoint in self.spawnPoints: + self.treasures.append(None) + + self.deleteTaskNames = set() + self.lastRequestId = None + self.requestStartTime = None + self.requestCount = None + return + + def initSpawnPoints(self): + self.spawnPoints = [] + return self.spawnPoints + + def numTreasures(self): + counter = 0 + for treasure in self.treasures: + if treasure: + counter += 1 + + return counter + + def countEmptySpawnPoints(self): + counter = 0 + for treasure in self.treasures: + if treasure == None: + counter += 1 + + return counter + + def nthEmptyIndex(self, n): + emptyCounter = -1 + spawnPointCounter = -1 + while emptyCounter < n: + spawnPointCounter += 1 + if self.treasures[spawnPointCounter] == None: + emptyCounter += 1 + + return spawnPointCounter + + def findIndexOfTreasureId(self, treasureId): + counter = 0 + for treasure in self.treasures: + if treasure == None: + pass + elif treasureId == treasure.getDoId(): + return counter + counter += 1 + + return + + def placeAllTreasures(self): + index = 0 + for treasure in self.treasures: + if not treasure: + self.placeTreasure(index) + index += 1 + + def placeTreasure(self, index): + spawnPoint = self.spawnPoints[index] + treasure = DistributedTreasureAI(simbase.air, self, self.treasureType, spawnPoint[0], spawnPoint[1], spawnPoint[2]) + treasure.generateWithRequired(self.zoneId) + self.treasures[index] = treasure + + def validAvatar(self, treasure, av): + return treasure.validAvatar(av) + + def grabAttempt(self, avId, treasureId): + if self.lastRequestId == avId: + self.requestCount += 1 + now = globalClock.getFrameTime() + elapsed = now - self.requestStartTime + if elapsed > 10: + self.requestCount = 1 + self.requestStartTime = now + else: + secondsPerGrab = elapsed / self.requestCount + if self.requestCount >= 3 and secondsPerGrab <= 0.4: + simbase.air.writeServerEvent('suspicious', avId, 'TreasurePlannerAI.grabAttempt %s treasures in %s seconds' % (self.requestCount, elapsed)) + else: + self.lastRequestId = avId + self.requestCount = 1 + self.requestStartTime = globalClock.getFrameTime() + index = self.findIndexOfTreasureId(treasureId) + if index == None: + pass + else: + av = simbase.air.doId2do.get(avId) + if av == None: + simbase.air.writeServerEvent('suspicious', avId, 'TreasurePlannerAI.grabAttempt unknown avatar') + self.notify.warning('avid: %s does not exist' % avId) + else: + treasure = self.treasures[index] + if self.validAvatar(treasure, av): + self.treasures[index] = None + if self.callback: + self.callback(avId) + treasure.d_setGrab(avId) + self.deleteTreasureSoon(treasure) + else: + treasure.d_setReject() + return + + def deleteTreasureSoon(self, treasure): + taskName = treasure.uniqueName('deletingTreasure') + taskMgr.doMethodLater(5, self.__deleteTreasureNow, taskName, extraArgs=(treasure, taskName)) + self.deleteTaskNames.add(taskName) + + def deleteAllTreasuresNow(self): + for treasure in self.treasures: + if treasure: + treasure.requestDelete() + + for taskName in self.deleteTaskNames: + tasks = taskMgr.getTasksNamed(taskName) + if len(tasks): + treasure = tasks[0].getArgs()[0] + treasure.requestDelete() + taskMgr.remove(taskName) + + self.deleteTaskNames = set() + self.treasures = [] + for spawnPoint in self.spawnPoints: + self.treasures.append(None) + + return + + def __deleteTreasureNow(self, treasure, taskName): + treasure.requestDelete() + self.deleteTaskNames.remove(taskName) diff --git a/toontown/safezone/TrolleyConstants.py b/toontown/safezone/TrolleyConstants.py new file mode 100755 index 00000000..7c1198e5 --- /dev/null +++ b/toontown/safezone/TrolleyConstants.py @@ -0,0 +1,5 @@ +TROLLEY_ENTER_TIME = 2.0 +TROLLEY_EXIT_TIME = 5.0 +TROLLEY_COUNTDOWN_TIME = 10.0 +TOON_BOARD_TIME = 1.0 +TOON_EXIT_TIME = 1.0 diff --git a/toontown/safezone/Walk.py b/toontown/safezone/Walk.py new file mode 100755 index 00000000..d61fcc18 --- /dev/null +++ b/toontown/safezone/Walk.py @@ -0,0 +1,103 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, StateData, State + +class Walk(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('Walk') + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.fsm = ClassicFSM.ClassicFSM('Walk', [State.State('off', self.enterOff, self.exitOff, ['walking', 'swimming', 'slowWalking']), + State.State('walking', self.enterWalking, self.exitWalking, ['swimming', 'slowWalking']), + State.State('swimming', self.enterSwimming, self.exitSwimming, ['walking', 'slowWalking']), + State.State('slowWalking', self.enterSlowWalking, self.exitSlowWalking, ['walking', 'swimming'])], 'off', 'off') + self.fsm.enterInitialState() + self.swimSoundPlaying = 0 + + def load(self): + pass + + def unload(self): + del self.fsm + + def enter(self, slowWalk = 0): + base.localAvatar.startPosHprBroadcast() + base.localAvatar.startBlink() + base.localAvatar.attachCamera() + shouldPush = 1 + if len(base.localAvatar.cameraPositions) > 0: + shouldPush = not base.localAvatar.cameraPositions[base.localAvatar.cameraIndex][4] + base.localAvatar.startUpdateSmartCamera(shouldPush) + base.localAvatar.showName() + base.localAvatar.collisionsOn() + base.localAvatar.startGlitchKiller() + base.localAvatar.enableAvatarControls() + + def exit(self): + self.fsm.request('off') + self.ignore('control') + base.localAvatar.disableAvatarControls() + if not base.localAvatar.preventCameraDisable: + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.detachCamera() + base.localAvatar.stopPosHprBroadcast() + base.localAvatar.stopBlink() + base.localAvatar.stopGlitchKiller() + base.localAvatar.collisionsOff() + base.localAvatar.controlManager.placeOnFloor() + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterWalking(self): + if base.localAvatar.hp > 0: + base.localAvatar.startTrackAnimToSpeed() + base.localAvatar.setWalkSpeedNormal() + base.localAvatar.applyBuffs() + else: + self.fsm.request('slowWalking') + + def exitWalking(self): + base.localAvatar.stopTrackAnimToSpeed() + + def enterSwimming(self, swimSound): + base.localAvatar.setWalkSpeedNormal() + base.localAvatar.applyBuffs() + self.swimSound = swimSound + self.swimSoundPlaying = 0 + base.localAvatar.b_setAnimState('swim', base.localAvatar.animMultiplier) + taskMgr.add(self.__swimSoundTest, 'localToonSwimming') + + def exitSwimming(self): + taskMgr.remove('localToonSwimming') + self.swimSound.stop() + del self.swimSound + self.swimSoundPlaying = 0 + + def __swimSoundTest(self, task): + speed, rotSpeed, slideSpeed = base.localAvatar.controlManager.getSpeeds() + + if (speed or rotSpeed): + if not self.swimSoundPlaying: + self.swimSoundPlaying = 1 + base.playSfx(self.swimSound, looping=1) + elif self.swimSoundPlaying: + self.swimSoundPlaying = 0 + self.swimSound.stop() + + return task.cont + + def enterSlowWalking(self): + self.accept(base.localAvatar.uniqueName('positiveHP'), self.__handlePositiveHP) + base.localAvatar.startTrackAnimToSpeed() + base.localAvatar.setWalkSpeedSlow() + + def __handlePositiveHP(self): + self.fsm.request('walking') + + def exitSlowWalking(self): + base.localAvatar.stopTrackAnimToSpeed() + self.ignore(base.localAvatar.uniqueName('positiveHP')) diff --git a/toontown/safezone/__init__.py b/toontown/safezone/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/shtiker/CogPageGlobals.py b/toontown/shtiker/CogPageGlobals.py new file mode 100755 index 00000000..0871916c --- /dev/null +++ b/toontown/shtiker/CogPageGlobals.py @@ -0,0 +1,6 @@ +COG_QUOTAS = ((30, 25, 20, 15, 10, 5, 2, 1), (45, 40, 35, 30, 25, 20, 15, 10)) +COG_UNSEEN = 1 +COG_BATTLED = 2 +COG_DEFEATED = 3 +COG_COMPLETE1 = 4 +COG_COMPLETE2 = 5 diff --git a/toontown/shtiker/DeleteManager.py b/toontown/shtiker/DeleteManager.py new file mode 100755 index 00000000..81f579ca --- /dev/null +++ b/toontown/shtiker/DeleteManager.py @@ -0,0 +1,21 @@ +from panda3d.core import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal + +class DeleteManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DeleteManager') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.accept('deleteItems', self.d_setInventory) + + def disable(self): + self.ignore('deleteItems') + DistributedObject.DistributedObject.disable(self) + + def d_setInventory(self, newInventoryString): + self.sendUpdate('setInventory', [newInventoryString]) diff --git a/toontown/shtiker/DeleteManagerAI.py b/toontown/shtiker/DeleteManagerAI.py new file mode 100755 index 00000000..dd0343df --- /dev/null +++ b/toontown/shtiker/DeleteManagerAI.py @@ -0,0 +1,8 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI + +class DeleteManagerAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DeleteManagerAI") + + def setInventory(self, todo0): + pass diff --git a/toontown/shtiker/DisguisePage.py b/toontown/shtiker/DisguisePage.py new file mode 100755 index 00000000..9acdffe8 --- /dev/null +++ b/toontown/shtiker/DisguisePage.py @@ -0,0 +1,196 @@ +import ShtikerPage +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.suit import SuitDNA +from toontown.battle import SuitBattleGlobals +from toontown.minigame import MinigamePowerMeter +from toontown.coghq import CogDisguiseGlobals +DeptColors = (Vec4(0.647, 0.608, 0.596, 1.0), + Vec4(0.588, 0.635, 0.671, 1.0), + Vec4(0.596, 0.714, 0.659, 1.0), + Vec4(0.761, 0.678, 0.69, 1.0)) +NumParts = max(CogDisguiseGlobals.PartsPerSuit) +PartNames = ('lUpleg', 'lLowleg', 'lShoe', 'rUpleg', 'rLowleg', 'rShoe', 'lShoulder', 'rShoulder', 'chest', 'waist', 'hip', 'lUparm', 'lLowarm', 'lHand', 'rUparm', 'rLowarm', 'rHand') + +class DisguisePage(ShtikerPage.ShtikerPage): + meterColor = Vec4(0.87, 0.87, 0.827, 1.0) + meterActiveColor = Vec4(0.7, 0.3, 0.3, 1) + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.activeTab = 0 + self.progressTitle = None + return + + def load(self): + ShtikerPage.ShtikerPage.load(self) + gui = loader.loadModel('phase_9/models/gui/cog_disguises') + self.frame = DirectFrame(parent=self, relief=None, scale=0.47, pos=(0.02, 1, 0)) + self.bkgd = DirectFrame(parent=self.frame, geom=gui.find('**/base'), relief=None, scale=(0.98, 1, 1)) + self.bkgd.setTextureOff(1) + self.tabs = [] + self.pageFrame = DirectFrame(parent=self.frame, relief=None) + for dept in SuitDNA.suitDepts: + if dept == 'c': + tabIndex = 1 + textPos = (1.57, 0.75) + elif dept == 'l': + tabIndex = 2 + textPos = (1.57, 0.12) + elif dept == 'm': + tabIndex = 3 + textPos = (1.57, -0.47) + elif dept == 's': + tabIndex = 4 + textPos = (1.57, -1.05) + pageGeom = gui.find('**/page%d' % tabIndex) + tabGeom = gui.find('**/tab%d' % tabIndex) + tab = DirectButton(parent=self.pageFrame, relief=None, geom=tabGeom, geom_color=DeptColors[tabIndex - 1], text=SuitDNA.suitDeptFullnames[dept], text_font=ToontownGlobals.getSuitFont(), text_pos=textPos, text_roll=-90, text_scale=TTLocalizer.DPtab, text_align=TextNode.ACenter, text1_fg=Vec4(1, 0, 0, 1), text2_fg=Vec4(0.5, 0.4, 0.4, 1), text3_fg=Vec4(0.4, 0.4, 0.4, 1), command=self.doTab, extraArgs=[len(self.tabs)], pressEffect=0) + self.tabs.append(tab) + page = DirectFrame(parent=tab, relief=None, geom=pageGeom) + + self.deptLabel = DirectLabel(parent=self.frame, text='', text_font=ToontownGlobals.getSuitFont(), text_scale=TTLocalizer.DPdeptLabel, text_pos=(-0.1, 0.8)) + DirectFrame(parent=self.frame, relief=None, geom=gui.find('**/pipe_frame')) + self.tube = DirectFrame(parent=self.frame, relief=None, geom=gui.find('**/tube')) + DirectFrame(parent=self.frame, relief=None, geom=gui.find('**/robot/face')) + DirectLabel(parent=self.frame, relief=None, geom=gui.find('**/text_cog_disguises'), geom_pos=(0, 0.1, 0)) + self.meritTitle = DirectLabel(parent=self.frame, relief=None, geom=gui.find('**/text_merit_progress'), geom_pos=(0, 0.1, 0)) + self.meritTitle.hide() + self.cogbuckTitle = DirectLabel(parent=self.frame, relief=None, geom=gui.find('**/text_cashbuck_progress'), geom_pos=(0, 0.1, 0)) + self.cogbuckTitle.hide() + self.juryNoticeTitle = DirectLabel(parent=self.frame, relief=None, geom=gui.find('**/text_jury_notice_progress'), geom_pos=(0, 0.1, 0)) + self.juryNoticeTitle.hide() + self.stockOptionTitle = DirectLabel(parent=self.frame, relief=None, geom=gui.find('**/text_stock_option_progress'), geom_pos=(0, 0.1, 0)) + self.stockOptionTitle.hide() + self.progressTitle = self.meritTitle + self.promotionTitle = DirectLabel(parent=self.frame, relief=None, geom=gui.find('**/text_ready4promotion'), geom_pos=(0, 0.1, 0)) + self.cogName = DirectLabel(parent=self.frame, relief=None, text='', text_font=ToontownGlobals.getSuitFont(), text_scale=TTLocalizer.DPcogName, text_align=TextNode.ACenter, pos=(-0.948, 0, -1.15)) + self.cogLevel = DirectLabel(parent=self.frame, relief=None, text='', text_font=ToontownGlobals.getSuitFont(), text_scale=0.09, text_align=TextNode.ACenter, pos=(-0.91, 0, -1.02)) + self.partFrame = DirectFrame(parent=self.frame, relief=None) + self.parts = [] + for partNum in xrange(0, NumParts): + self.parts.append(DirectFrame(parent=self.partFrame, relief=None, geom=gui.find('**/robot/' + PartNames[partNum]))) + + self.holes = [] + for partNum in xrange(0, NumParts): + self.holes.append(DirectFrame(parent=self.partFrame, relief=None, geom=gui.find('**/robot_hole/' + PartNames[partNum]))) + + self.cogPartRatio = DirectLabel(parent=self.frame, relief=None, text='', text_font=ToontownGlobals.getSuitFont(), text_scale=0.08, text_align=TextNode.ACenter, pos=(-0.91, 0, -0.82)) + self.cogMeritRatio = DirectLabel(parent=self.frame, relief=None, text='', text_font=ToontownGlobals.getSuitFont(), text_scale=0.08, text_align=TextNode.ACenter, pos=(0.45, 0, -0.36)) + meterFace = gui.find('**/meter_face_whole') + meterFaceHalf = gui.find('**/meter_face_half') + self.meterFace = DirectLabel(parent=self.frame, relief=None, geom=meterFace, color=self.meterColor, pos=(0.455, 0.0, 0.04)) + self.meterFaceHalf1 = DirectLabel(parent=self.frame, relief=None, geom=meterFaceHalf, color=self.meterActiveColor, pos=(0.455, 0.0, 0.04)) + self.meterFaceHalf2 = DirectLabel(parent=self.frame, relief=None, geom=meterFaceHalf, color=self.meterColor, pos=(0.455, 0.0, 0.04)) + self.frame.hide() + self.activeTab = 3 + self.updatePage() + return + + def unload(self): + ShtikerPage.ShtikerPage.unload(self) + + def enter(self): + self.frame.show() + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + self.frame.hide() + ShtikerPage.ShtikerPage.exit(self) + + def updatePage(self): + self.doTab(self.activeTab) + + def updatePartsDisplay(self, index, numParts, numPartsRequired): + partBitmask = 1 + groupingBitmask = CogDisguiseGlobals.PartsPerSuitBitmasks[index] + previousPart = 0 + for part in self.parts: + groupingBit = groupingBitmask & partBitmask + if numParts & partBitmask & groupingBit: + part.show() + self.holes[self.parts.index(part)].hide() + if groupingBit: + previousPart = 1 + elif not groupingBit and previousPart: + part.show() + self.holes[self.parts.index(part)].hide() + else: + self.holes[self.parts.index(part)].show() + part.hide() + previousPart = 0 + partBitmask = partBitmask << 1 + + def updateMeritBar(self, dept): + merits = base.localAvatar.cogMerits[dept] + totalMerits = CogDisguiseGlobals.getTotalMerits(base.localAvatar, dept) + if totalMerits == 0: + progress = 1 + else: + progress = min(merits / float(totalMerits), 1) + self.updateMeritDial(progress) + if base.localAvatar.readyForPromotion(dept): + self.cogMeritRatio['text'] = TTLocalizer.DisguisePageMeritFull + self.promotionTitle.show() + self.progressTitle.hide() + else: + self.cogMeritRatio['text'] = '%d/%d' % (merits, totalMerits) + self.promotionTitle.hide() + self.progressTitle.show() + + def updateMeritDial(self, progress): + if progress == 0: + self.meterFaceHalf1.hide() + self.meterFaceHalf2.hide() + self.meterFace.setColor(self.meterColor) + elif progress == 1: + self.meterFaceHalf1.hide() + self.meterFaceHalf2.hide() + self.meterFace.setColor(self.meterActiveColor) + else: + self.meterFaceHalf1.show() + self.meterFaceHalf2.show() + self.meterFace.setColor(self.meterColor) + if progress < 0.5: + self.meterFaceHalf2.setColor(self.meterColor) + else: + self.meterFaceHalf2.setColor(self.meterActiveColor) + progress = progress - 0.5 + self.meterFaceHalf2.setR(180 * (progress / 0.5)) + + def doTab(self, index): + self.activeTab = index + self.tabs[index].reparentTo(self.pageFrame) + for i in xrange(len(self.tabs)): + tab = self.tabs[i] + if i == index: + tab['text0_fg'] = (1, 0, 0, 1) + tab['text2_fg'] = (1, 0, 0, 1) + else: + tab['text0_fg'] = (0, 0, 0, 1) + tab['text2_fg'] = (0.5, 0.4, 0.4, 1) + + self.bkgd.setColor(DeptColors[index]) + self.deptLabel['text'] = (SuitDNA.suitDeptFullnames[SuitDNA.suitDepts[index]],) + cogIndex = base.localAvatar.cogTypes[index] + SuitDNA.suitsPerDept * index + cog = SuitDNA.suitHeadTypes[cogIndex] + self.progressTitle.hide() + if SuitDNA.suitDepts[index] == 'm': + self.progressTitle = self.cogbuckTitle + elif SuitDNA.suitDepts[index] == 'l': + self.progressTitle = self.juryNoticeTitle + elif SuitDNA.suitDepts[index] == 'c': + self.progressTitle = self.stockOptionTitle + else: + self.progressTitle = self.meritTitle + self.progressTitle.show() + self.cogName['text'] = SuitBattleGlobals.SuitAttributes[cog]['name'] + cogLevel = base.localAvatar.cogLevels[index] + self.cogLevel['text'] = TTLocalizer.DisguisePageCogLevel % str(cogLevel + 1) + numParts = base.localAvatar.cogParts[index] + numPartsRequired = CogDisguiseGlobals.PartsPerSuit[index] + self.updatePartsDisplay(index, numParts, numPartsRequired) + self.updateMeritBar(index) + self.cogPartRatio['text'] = '%d/%d' % (CogDisguiseGlobals.getTotalParts(numParts), numPartsRequired) diff --git a/toontown/shtiker/DisplaySettingsDialog.py b/toontown/shtiker/DisplaySettingsDialog.py new file mode 100755 index 00000000..c979192c --- /dev/null +++ b/toontown/shtiker/DisplaySettingsDialog.py @@ -0,0 +1,403 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.fsm import StateData +from direct.gui.DirectGui import * +from direct.task.Task import Task +from panda3d.core import * + +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog + + +class DisplaySettingsDialog(DirectFrame, StateData.StateData): + notify = directNotify.newCategory('DisplaySettingsDialog') + ApplyTimeoutSeconds = 15 + TimeoutCountdownTask = 'DisplaySettingsTimeoutCountdown' + WindowedMode = 0 + FullscreenMode = 1 + + def __init__(self): + DirectFrame.__init__( + self, pos=(0, 0, 0.3), relief=None, + image=DGG.getDefaultDialogGeom(), image_scale=(1.6, 1, 1.2), + image_pos=(0, 0, -0.05), + image_color=ToontownGlobals.GlobalDialogColor, + text=TTLocalizer.DisplaySettingsTitle, text_scale=0.12, + text_pos=(0, 0.4), borderWidth=(0.01, 0.01)) + StateData.StateData.__init__(self, 'display-settings-done') + + self.setBin('gui-popup', 0) + self.initialiseoptions(DisplaySettingsDialog) + + def unload(self): + if self.isLoaded == 0: + return + self.isLoaded = 0 + self.exit() + + DirectFrame.destroy(self) + + def load(self): + if self.isLoaded == 1: + return + + self.isLoaded = 1 + self.anyChanged = 0 + self.apiChanged = 0 + + self.screenSizes = ((640, 480), + (800, 600), + (1024, 768), + (1280, 720), + (1280, 1024), + (1440, 900), + (1600, 900), + (1600, 1200), + (1920, 1080), + (2560, 1440)) + + guiButton = loader.loadModel('phase_3/models/gui/quit_button.bam') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui.bam') + nameShopGui = loader.loadModel('phase_3/models/gui/nameshop_gui.bam') + + circle = nameShopGui.find('**/namePanelCircle') + innerCircle = circle.copyTo(hidden) + innerCircle.setPos(0, 0, 0.2) + self.c1b = circle.copyTo(self, -1) + self.c1b.setColor(0, 0, 0, 1) + self.c1b.setPos(0.044, 0, -0.21) + self.c1b.setScale(0.4) + c1f = circle.copyTo(self.c1b) + c1f.setColor(1, 1, 1, 1) + c1f.setScale(0.8) + self.c2b = circle.copyTo(self, -2) + self.c2b.setColor(0, 0, 0, 1) + self.c2b.setPos(0.044, 0, -0.3) + self.c2b.setScale(0.4) + c2f = circle.copyTo(self.c2b) + c2f.setColor(1, 1, 1, 1) + c2f.setScale(0.8) + self.c3b = circle.copyTo(self, -2) + self.c3b.setColor(0, 0, 0, 1) + self.c3b.setPos(0.044, 0, -0.4) + self.c3b.setScale(0.4) + c3f = circle.copyTo(self.c3b) + c3f.setColor(1, 1, 1, 1) + c3f.setScale(0.8) + + self.introText = DirectLabel(parent=self, relief=None, scale=TTLocalizer.DSDintroText, text=TTLocalizer.DisplaySettingsIntro, text_wordwrap=TTLocalizer.DSDintroTextWordwrap, text_align=TextNode.ALeft, pos=(-0.725, 0, 0.3)) + self.introTextSimple = DirectLabel(parent=self, relief=None, scale=0.06, text=TTLocalizer.DisplaySettingsIntroSimple, text_wordwrap=25, text_align=TextNode.ALeft, pos=(-0.725, 0, 0.3)) + self.apiLabel = DirectLabel(parent=self, relief=None, scale=0.06, text=TTLocalizer.DisplaySettingsApi, text_align=TextNode.ARight, pos=(-0.08, 0, 0)) + self.apiMenu = DirectOptionMenu(parent=self, relief=DGG.RAISED, scale=0.06, items=['x'], pos=(0, 0, 0)) + self.screenSizeLabel = DirectLabel(parent=self, relief=None, scale=0.06, text=TTLocalizer.DisplaySettingsResolution, text_align=TextNode.ARight, pos=(-0.08, 0, -0.1)) + self.screenSizeLeftArrow = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), gui.find('**/Horiz_Arrow_DN'), gui.find('**/Horiz_Arrow_Rllvr'), gui.find('**/Horiz_Arrow_UP')), scale=(-1.0, 1.0, 1.0), pos=(0.04, 0, -0.085), command=self.__doScreenSizeLeft) + self.screenSizeRightArrow = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), gui.find('**/Horiz_Arrow_DN'), gui.find('**/Horiz_Arrow_Rllvr'), gui.find('**/Horiz_Arrow_UP')), pos=(0.54, 0, -0.085), command=self.__doScreenSizeRight) + self.screenSizeValueText = DirectLabel(parent=self, relief=None, text='x', text_align=TextNode.ACenter, text_scale=0.06, pos=(0.29, 0, -0.1)) + self.windowedButton = DirectCheckButton(parent=self, relief=None, text=TTLocalizer.DisplaySettingsWindowed, text_align=TextNode.ALeft, text_scale=0.6, scale=0.1, boxImage=innerCircle, boxImageScale=2.5, boxImageColor=VBase4(0, 0.25, 0.5, 1), boxRelief=None, pos=TTLocalizer.DSDwindowedButtonPos, command=self.__doWindowed) + self.fullscreenButton = DirectCheckButton(parent=self, relief=None, text=TTLocalizer.DisplaySettingsFullscreen, text_align=TextNode.ALeft, text_scale=0.6, scale=0.1, boxImage=innerCircle, boxImageScale=2.5, boxImageColor=VBase4(0, 0.25, 0.5, 1), boxRelief=None, pos=TTLocalizer.DSDfullscreenButtonPos, command=self.__doFullscreen) + self.apply = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.6, 1, 1), text=TTLocalizer.DisplaySettingsApply, text_scale=0.06, text_pos=(0, -0.02), pos=(0.52, 0, -0.53), command=self.__apply) + self.cancel = DirectButton(parent=self, relief=None, text=TTLocalizer.DisplaySettingsCancel, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.6, 1, 1), text_scale=TTLocalizer.DSDcancel, text_pos=TTLocalizer.DSDcancelPos, pos=(0.2, 0, -0.53), command=self.__cancel) + + guiButton.removeNode() + gui.removeNode() + nameShopGui.removeNode() + innerCircle.removeNode() + + self.hide() + + def enter(self, changeDisplaySettings, changeDisplayAPI): + if self.isEntered == 1: + return + + self.isEntered = 1 + + if self.isLoaded == 0: + self.load() + + self.applyDialog = None + self.timeoutDialog = None + self.restoreDialog = None + self.revertDialog = None + + base.transitions.fadeScreen(0.5) + + properties = base.win.getProperties() + self.screenSizeIndex = self.chooseClosestScreenSize(properties.getXSize(), properties.getYSize()) + + self.isFullscreen = properties.getFullscreen() + if self.isFullscreen: + self.displayMode = self.FullscreenMode + else: + self.displayMode = self.WindowedMode + + self.updateApiMenu(changeDisplaySettings, changeDisplayAPI) + self.updateWindowed() + self.updateScreenSize() + + if changeDisplaySettings: + self.introText.show() + self.introTextSimple.hide() + if changeDisplayAPI and len(self.apis) > 1: + self.apiLabel.show() + self.apiMenu.show() + else: + self.apiLabel.hide() + self.apiMenu.hide() + self.c1b.show() + self.windowedButton.show() + self.c2b.show() + self.fullscreenButton.show() + self.c3b.hide() + else: + self.introText.hide() + self.introTextSimple.show() + self.apiLabel.hide() + self.apiMenu.hide() + self.windowedButton.hide() + self.fullscreenButton.hide() + self.c1b.hide() + self.c2b.hide() + self.c3b.hide() + + self.anyChanged = 0 + self.apiChanged = 0 + + self.show() + + def exit(self): + if self.isEntered == 0: + return + + self.isEntered = 0 + self.cleanupDialogs() + + base.transitions.noTransitions() + taskMgr.remove(self.TimeoutCountdownTask) + + self.ignoreAll() + self.hide() + + messenger.send(self.doneEvent, [self.anyChanged, self.apiChanged]) + + def cleanupDialogs(self): + if self.applyDialog is not None: + self.applyDialog.cleanup() + self.applyDialog = None + if self.timeoutDialog is not None: + self.timeoutDialog.cleanup() + self.timeoutDialog = None + if self.restoreDialog is not None: + self.restoreDialog.cleanup() + self.restoreDialog = None + if self.revertDialog is not None: + self.revertDialog.cleanup() + self.revertDialog = None + + def updateApiMenu(self, changeDisplaySettings, changeDisplayAPI): + self.apis = [] + self.apiPipes = [] + if changeDisplayAPI: + base.makeAllPipes() + for pipe in base.pipeList: + if pipe.isValid(): + self.apiPipes.append(pipe) + self.apis.append(pipe.getInterfaceName()) + + self.apiMenu['items'] = self.apis + self.apiMenu.set(base.pipe.getInterfaceName()) + + def updateWindowed(self): + if self.displayMode == self.FullscreenMode: + self.windowedButton['indicatorValue'] = 0 + self.fullscreenButton['indicatorValue'] = 1 + elif self.displayMode == self.WindowedMode: + self.windowedButton['indicatorValue'] = 1 + self.fullscreenButton['indicatorValue'] = 0 + + def updateScreenSize(self): + xSize, ySize = self.screenSizes[self.screenSizeIndex] + self.screenSizeValueText['text'] = '%s x %s' % (xSize, ySize) + if self.screenSizeIndex > 0: + self.screenSizeLeftArrow.show() + else: + self.screenSizeLeftArrow.hide() + if self.screenSizeIndex < len(self.screenSizes) - 1: + self.screenSizeRightArrow.show() + else: + self.screenSizeRightArrow.hide() + + def chooseClosestScreenSize(self, currentXSize, currentYSize): + for i in xrange(len(self.screenSizes)): + xSize, ySize = self.screenSizes[i] + if currentXSize == xSize and currentYSize == ySize: + return i + + currentCount = currentXSize * currentYSize + bestDiff = None + bestI = None + for i in xrange(len(self.screenSizes)): + xSize, ySize = self.screenSizes[i] + diff = abs(xSize * ySize - currentCount) + if bestI == None or diff < bestDiff: + bestI = i + bestDiff = diff + + return bestI + + def __doWindowed(self, value): + self.displayMode = self.WindowedMode + self.updateWindowed() + + def __doFullscreen(self, value): + self.displayMode = self.FullscreenMode + self.updateWindowed() + + def __doScreenSizeLeft(self): + if self.screenSizeIndex > 0: + self.screenSizeIndex = self.screenSizeIndex - 1 + self.updateScreenSize() + + def __doScreenSizeRight(self): + if self.screenSizeIndex < len(self.screenSizes) - 1: + self.screenSizeIndex = self.screenSizeIndex + 1 + self.updateScreenSize() + + def __apply(self): + self.cleanupDialogs() + self.clearBin() + self.applyDialog = TTDialog.TTDialog(dialogName='DisplaySettingsApply', style=TTDialog.TwoChoice, text=TTLocalizer.DisplaySettingsApplyWarning % self.ApplyTimeoutSeconds, text_wordwrap=15, command=self.__applyDone) + self.applyDialog.setBin('gui-popup', 0) + + def __applyDone(self, command): + self.applyDialog.cleanup() + self.applyDialog = None + self.setBin('gui-popup', 0) + base.transitions.fadeScreen(0.5) + if command != DGG.DIALOG_OK: + return + self.origPipe = base.pipe + self.origProperties = base.win.getProperties() + pipe = self.apiPipes[self.apiMenu.selectedIndex] + properties = WindowProperties() + xSize, ySize = self.screenSizes[self.screenSizeIndex] + properties.setSize(xSize, ySize) + properties.setFullscreen(self.displayMode == self.FullscreenMode) + fullscreen = self.displayMode == self.FullscreenMode + if not self.changeDisplayProperties(pipe, xSize, ySize, fullscreen): + self.__revertBack(1) + return + self.clearBin() + self.timeoutDialog = TTDialog.TTDialog(dialogName='DisplaySettingsTimeout', style=TTDialog.TwoChoice, text=TTLocalizer.DisplaySettingsAccept % self.ApplyTimeoutSeconds, text_wordwrap=15, command=self.__timeoutDone) + self.timeoutDialog.setBin('gui-popup', 0) + self.timeoutRemaining = self.ApplyTimeoutSeconds + self.timeoutStart = None + taskMgr.add(self.__timeoutCountdown, self.TimeoutCountdownTask) + + def changeDisplayProperties(self, pipe, width, height, fullscreen = False): + result = False + self.current_pipe = base.pipe + self.current_properties = WindowProperties(base.win.getProperties()) + properties = self.current_properties + + if self.current_pipe == pipe and self.current_properties.getFullscreen() == fullscreen and self.current_properties.getXSize() == width and self.current_properties.getYSize() == height: + self.notify.info('DISPLAY NO CHANGE REQUIRED') + state = True + else: + properties = WindowProperties() + properties.setSize(width, height) + properties.setFullscreen(fullscreen) + properties.setParentWindow(0) + original_sort = base.win.getSort() + if self.resetDisplayProperties(pipe, properties): + properties = base.win.getProperties() + if properties.getFullscreen() == fullscreen and properties.getXSize() == width and properties.getYSize() == height: + self.notify.info('DISPLAY CHANGE VERIFIED') + result = True + else: + self.notify.warning('DISPLAY CHANGE FAILED, RESTORING PREVIOUS DISPLAY') + else: + self.notify.warning('DISPLAY CHANGE FAILED') + base.win.setSort(original_sort) + base.graphicsEngine.renderFrame() + base.graphicsEngine.renderFrame() + return result + + def __timeoutCountdown(self, task): + if self.timeoutStart == None: + self.timeoutStart = globalClock.getRealTime() + elapsed = int(globalClock.getFrameTime() - self.timeoutStart) + remaining = max(self.ApplyTimeoutSeconds - elapsed, 0) + if remaining < self.timeoutRemaining: + self.timeoutRemaining = remaining + self.timeoutDialog['text'] = (TTLocalizer.DisplaySettingsAccept % remaining,) + if remaining == 0: + self.__timeoutDone('cancel') + return Task.done + return Task.cont + + def __timeoutDone(self, command): + taskMgr.remove(self.TimeoutCountdownTask) + self.timeoutDialog.cleanup() + self.timeoutDialog = None + self.setBin('gui-popup', 0) + base.transitions.fadeScreen(0.5) + if command == DGG.DIALOG_OK: + self.anyChanged = 1 + self.exit() + return + self.__revertBack(0) + + def __revertBack(self, reason): + if not self.resetDisplayProperties(self.origPipe, self.origProperties): + self.notify.warning("Couldn't restore original display settings!") + base.panda3dRenderError() + self.clearBin() + if reason == 0: + revertText = TTLocalizer.DisplaySettingsRevertUser + else: + revertText = TTLocalizer.DisplaySettingsRevertFailed + self.revertDialog = TTDialog.TTDialog(dialogName='DisplaySettingsRevert', style=TTDialog.Acknowledge, text=revertText, text_wordwrap=15, command=self.__revertDone) + self.revertDialog.setBin('gui-popup', 0) + + def __revertDone(self, command): + self.revertDialog.cleanup() + self.revertDialog = None + self.setBin('gui-popup', 0) + base.transitions.fadeScreen(0.5) + + def __cancel(self): + self.exit() + + def resetDisplayProperties(self, pipe, properties): + if base.win: + currentProperties = base.win.getProperties() + gsg = base.win.getGsg() + else: + currentProperties = WindowProperties.getDefault() + gsg = None + newProperties = WindowProperties(currentProperties) + newProperties.addProperties(properties) + if base.pipe != pipe: + self.apiChanged = 1 + gsg = None + if gsg == None or currentProperties.getFullscreen() != newProperties.getFullscreen() or currentProperties.getParentWindow() != newProperties.getParentWindow(): + self.notify.debug('window properties: %s' % properties) + self.notify.debug('gsg: %s' % gsg) + base.pipe = pipe + if not base.openMainWindow(props=properties, gsg=gsg, keepCamera=True): + self.notify.warning('OPEN MAIN WINDOW FAILED') + return 0 + self.notify.info('OPEN MAIN WINDOW PASSED') + base.disableShowbaseMouse() + base.graphicsEngine.renderFrame() + base.graphicsEngine.renderFrame() + base.graphicsEngine.openWindows() + if base.win.isClosed(): + self.notify.info('Window did not open, removing.') + base.closeWindow(base.win) + return 0 + else: + self.notify.debug('Adjusting properties') + base.win.requestProperties(properties) + base.graphicsEngine.renderFrame() + return 1 diff --git a/toontown/shtiker/EventsPage.py b/toontown/shtiker/EventsPage.py new file mode 100755 index 00000000..e2d1e98b --- /dev/null +++ b/toontown/shtiker/EventsPage.py @@ -0,0 +1,582 @@ +import urllib +from pandac.PandaModules import Vec4, Vec3, TextNode +from direct.task.Task import Task +from direct.gui.DirectGui import DirectFrame, DirectLabel, DirectButton, DirectScrolledList, DirectCheckButton +from direct.gui import DirectGuiGlobals +from direct.directnotify import DirectNotifyGlobal +from otp.otpbase import OTPLocalizer +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog +from toontown.parties import PartyGlobals +from toontown.parties import PartyUtils +from toontown.parties.CalendarGuiMonth import CalendarGuiMonth +from toontown.parties.PartyUtils import getPartyActivityIcon +from toontown.parties.Party import Party +from toontown.parties.ServerTimeGui import ServerTimeGui +import ShtikerPage +EventsPage_Host = 0 +EventsPage_Invited = 1 +EventsPage_Calendar = 2 + +class EventsPage(ShtikerPage.ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('EventsPage') + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.mode = EventsPage_Calendar + self.setMode(self.mode) + self.noTeleport = config.GetBool('Parties-page-disable', 0) + self.isPrivate = True + self.hostedPartyInfo = None + return + + def load(self): + self.scrollButtonGui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.hostingGui = loader.loadModel('phase_4/models/parties/schtickerbookHostingGUI') + self.invitationGui = loader.loadModel('phase_4/models/parties/schtickerbookInvitationGUI') + self.activityIconsModel = loader.loadModel('phase_4/models/parties/eventSignIcons') + self.decorationModels = loader.loadModel('phase_4/models/parties/partyDecorations') + self.loadTabs() + self.loadHostingTab() + self.loadInvitationsTab() + self.loadCalendarTab() + self.titleLabel = DirectLabel(parent=self, relief=None, text=TTLocalizer.EventsPageHostTabTitle, text_scale=TTLocalizer.EPtitleLabel, textMayChange=True, pos=self.hostingGui.find('**/myNextParty_text_locator').getPos()) + return + + def loadTabs(self): + normalColor = (1.0, 1.0, 1.0, 1.0) + clickColor = (0.8, 0.8, 0.0, 1.0) + rolloverColor = (0.15, 0.82, 1.0, 1.0) + diabledColor = (1.0, 0.98, 0.15, 1.0) + gui = loader.loadModel('phase_3.5/models/gui/fishingBook') + self.hostTab = DirectButton(parent=self, relief=None, text=TTLocalizer.EventsPageHostTabName, text_scale=TTLocalizer.EPhostTab, text_align=TextNode.ACenter, text_pos=(0.12, 0.0), image=gui.find('**/tabs/polySurface1'), image_pos=(0.55, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[EventsPage_Host], pos=(-0.13, 0, 0.775)) + self.invitedTab = DirectButton(parent=self, relief=None, text=TTLocalizer.EventsPageInvitedTabName, text_scale=TTLocalizer.EPinvitedTab, text_pos=(0.12, 0.0), text_align=TextNode.ACenter, image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[EventsPage_Invited], pos=(0.28, 0, 0.775)) + self.calendarTab = DirectButton(parent=self, relief=None, text=TTLocalizer.EventsPageCalendarTabName, text_scale=TTLocalizer.EPcalendarTab, text_pos=(0.12, 0.0), text_align=TextNode.ACenter, image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[EventsPage_Calendar], pos=(-0.55, 0, 0.775)) + return + + def loadHostingTab(self): + self.hostedPartyDisplay = self.attachNewNode('Hosting') + self.hostedPartyDisplay.setPos(0.0, 0.0, 0.04) + self.hostingBackgroundFlat = DirectFrame(parent=self.hostedPartyDisplay, relief=None, geom=self.hostingGui.find('**/background_flat')) + self.hostingGuestList, self.hostingGuestLabel = self.createListAndLabel(self.hostedPartyDisplay, self.hostingGui, 'guests', 7) + self.hostingActivityList, self.hostingActivityLabel = self.createListAndLabel(self.hostedPartyDisplay, self.hostingGui, 'activities', 1) + self.hostingDecorationList, self.hostingDecorationLabel = self.createListAndLabel(self.hostedPartyDisplay, self.hostingGui, 'decorations', 1) + self.hostingDateLabel = DirectLabel(parent=self.hostedPartyDisplay, relief=None, text='', scale=TTLocalizer.EPhostingDateLabel, text_align=TextNode.ACenter, text_wordwrap=10, textMayChange=True, pos=self.hostingGui.find('**/date_locator').getPos()) + pos = self.hostingGui.find('**/cancel_text_locator').getPos() + self.hostingCancelButton = DirectButton(parent=self.hostedPartyDisplay, relief=None, geom=(self.hostingGui.find('**/cancelPartyButton_up'), + self.hostingGui.find('**/cancelPartyButton_down'), + self.hostingGui.find('**/cancelPartyButton_rollover'), + self.hostingGui.find('**/cancelPartyButton_inactive')), text=TTLocalizer.EventsPageHostTabCancelButton, text_scale=TTLocalizer.EPhostingCancelButton, text_pos=(pos[0], pos[2]), command=self.__doCancelParty) + pos = self.hostingGui.find('**/startParty_text_locator').getPos() + self.partyGoButton = DirectButton(parent=self.hostedPartyDisplay, relief=None, geom=(self.hostingGui.find('**/startPartyButton_up'), + self.hostingGui.find('**/startPartyButton_down'), + self.hostingGui.find('**/startPartyButton_rollover'), + self.hostingGui.find('**/startPartyButton_inactive')), text=TTLocalizer.EventsPageGoButton, text_scale=TTLocalizer.EPpartyGoButton, text_pos=(pos[0], pos[2]), textMayChange=True, command=self._startParty) + self.publicPrivateLabel = DirectLabel(parent=self.hostedPartyDisplay, relief=None, text=TTLocalizer.EventsPageHostTabPublicPrivateLabel, text_scale=TTLocalizer.EPpublicPrivateLabel, text_align=TextNode.ACenter, pos=self.hostingGui.find('**/thisPartyIs_text_locator').getPos()) + pos = self.hostingGui.find('**/public_text_locator').getPos() + checkedImage = self.hostingGui.find('**/checked_button') + uncheckedImage = self.hostingGui.find('**/unchecked_button') + self.publicButton = DirectCheckButton(parent=self.hostedPartyDisplay, relief=None, scale=0.1, boxBorder=0.08, boxImage=(uncheckedImage, checkedImage, None), boxImageScale=10, boxRelief=None, text=TTLocalizer.EventsPageHostTabToggleToPublic, text_align=TextNode.ALeft, text_scale=TTLocalizer.EPpublicButton, pos=pos, command=self.__changePublicPrivate, indicator_pos=(-0.7, 0, 0.2)) + pos = self.hostingGui.find('**/private_text_locator').getPos() + self.privateButton = DirectCheckButton(parent=self.hostedPartyDisplay, relief=None, scale=0.1, boxBorder=0.08, boxImage=(uncheckedImage, checkedImage, None), boxImageScale=10, boxRelief=None, text=TTLocalizer.EventsPageHostTabToggleToPrivate, text_align=TextNode.ALeft, text_scale=TTLocalizer.EPprivateButton, pos=pos, command=self.__changePublicPrivate, indicator_pos=(-0.7, 0, 0.2)) + self.confirmCancelPartyEvent = 'confirmCancelPartyEvent' + self.accept(self.confirmCancelPartyEvent, self.confirmCancelOfParty) + self.confirmCancelPartyGui = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('confirmCancelPartyGui'), doneEvent=self.confirmCancelPartyEvent, message=TTLocalizer.EventsPageConfirmCancel % int(PartyGlobals.PartyRefundPercentage * 100.0), style=TTDialog.YesNo, okButtonText=OTPLocalizer.DialogYes, cancelButtonText=OTPLocalizer.DialogNo) + self.confirmCancelPartyGui.doneStatus = '' + self.confirmCancelPartyGui.hide() + self.confirmTooLatePartyEvent = 'confirmTooLatePartyEvent' + self.accept(self.confirmTooLatePartyEvent, self.confirmTooLateParty) + self.confirmTooLatePartyGui = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('confirmTooLatePartyGui'), doneEvent=self.confirmTooLatePartyEvent, message=TTLocalizer.EventsPageTooLateToStart, style=TTDialog.Acknowledge) + self.confirmTooLatePartyGui.hide() + self.confirmPublicPrivateChangeEvent = 'confirmPublicPrivateChangeEvent' + self.accept(self.confirmPublicPrivateChangeEvent, self.confirmPublicPrivateChange) + self.confirmPublicPrivateGui = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('confirmPublicPrivateGui'), doneEvent=self.confirmPublicPrivateChangeEvent, message=TTLocalizer.EventsPagePublicPrivateNoGo, style=TTDialog.Acknowledge) + self.confirmPublicPrivateGui.hide() + self.cancelPartyResultGuiEvent = 'cancelPartyResultGuiEvent' + self.accept(self.cancelPartyResultGuiEvent, self.cancelPartyResultGuiCommand) + self.cancelPartyResultGui = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('cancelPartyResultGui'), doneEvent=self.cancelPartyResultGuiEvent, message=TTLocalizer.EventsPageCancelPartyResultOk % 0, style=TTDialog.Acknowledge) + self.cancelPartyResultGui.doneStatus = '' + self.cancelPartyResultGui.hide() + self.__setPublicPrivateButton() + return + + def loadInvitationsTab(self): + self.invitationDisplay = self.attachNewNode('invitations') + self.invitationDisplay.setPos(0.0, 0.0, 0.04) + self.invitationBackgroundFlat = DirectFrame(parent=self.invitationDisplay, relief=None, geom=self.invitationGui.find('**/background_flat')) + self.invitationPartiesFlat = DirectFrame(parent=self.invitationDisplay, relief=None, geom=self.invitationGui.find('**/parties_background')) + self.invitationActivtiesFlat = DirectFrame(parent=self.invitationDisplay, relief=None, geom=self.invitationGui.find('**/activities_background')) + self.invitationPartyList, self.invitationPartyLabel = self.createListAndLabel(self.invitationDisplay, self.invitationGui, 'parties', 7, 'ButtonDown', 'ButtonUp', 'Text_locator') + self.invitationActivityList, self.invitationActivityLabel = self.createListAndLabel(self.invitationDisplay, self.invitationGui, 'activities', 1, 'ButtonDown', 'ButtonUp', 'Text_locator') + pos = self.invitationGui.find('**/startText_locator').getPos() + self.invitePartyGoButton = DirectButton(parent=self.invitationDisplay, relief=None, geom=(self.invitationGui.find('**/startButton_up'), + self.invitationGui.find('**/startButton_down'), + self.invitationGui.find('**/startButton_rollover'), + self.invitationGui.find('**/startButton_inactive')), text=TTLocalizer.EventsPageInviteGoButton, text_scale=TTLocalizer.EPinvitePartyGoButton, text_pos=(pos[0], pos[2]), textMayChange=True, command=self._inviteStartParty) + self.invitationDateTimeLabel = DirectLabel(parent=self.invitationDisplay, relief=None, text='', textMayChange=True, text_scale=0.07, pos=(0, 0, -0.65)) + return + + def loadCalendarTab(self): + self.calendarDisplay = self.attachNewNode('calendar') + self.toontownTimeLabel = DirectLabel(parent=self.calendarDisplay, pos=(0.175, 0, -0.69), text_align=TextNode.ARight, relief=None, text=TTLocalizer.EventsPageToontownTimeIs, text_scale=0.065, text_font=ToontownGlobals.getMinnieFont(), text_fg=(255 / 255.0, + 146 / 255.0, + 113 / 255.0, + 1), textMayChange=0) + self.calendarGuiMonth = None # To be set upon tab's first opening. + pos = (0.35, 0, -0.69) + self.toontownTimeGui = ServerTimeGui(self.calendarDisplay, pos) + return + + def getGuestItem(self, name, inviteStatus): + label = DirectLabel(relief=None, text=name, text_scale=0.045, text_align=TextNode.ALeft, textMayChange=True) + dot = DirectFrame(relief=None, geom=self.hostingGui.find('**/questionMark'), pos=(0.5, 0.0, 0.01)) + if inviteStatus == PartyGlobals.InviteStatus.Accepted: + dot['geom'] = (self.hostingGui.find('**/checkmark'),) + elif inviteStatus == PartyGlobals.InviteStatus.Rejected: + dot['geom'] = (self.hostingGui.find('**/x'),) + PartyUtils.truncateTextOfLabelBasedOnWidth(label, name, PartyGlobals.EventsPageGuestNameMaxWidth) + dot.reparentTo(label) + return label + + def getActivityItem(self, activityBase, count = 1): + activityName = TTLocalizer.PartyActivityNameDict[activityBase.activityId]['generic'] + if count == 1: + textForActivity = activityName + else: + textForActivity = '%s x %d' % (activityName, count) + iconString = PartyGlobals.ActivityIds.getString(activityBase.activityId) + geom = getPartyActivityIcon(self.activityIconsModel, iconString) + label = DirectLabel(relief=None, geom=geom, geom_scale=0.38, geom_pos=Vec3(0.0, 0.0, -0.17), text=textForActivity, text_scale=TTLocalizer.EPactivityItemLabel, text_align=TextNode.ACenter, text_pos=(-0.01, -0.43), text_wordwrap=7.0) + return label + + def getDecorationItem(self, decorBase, count = 1): + decorationName = TTLocalizer.PartyDecorationNameDict[decorBase.decorId]['editor'] + if count == 1: + textForDecoration = decorationName + else: + textForDecoration = decorationName + ' x ' + str(count) + assetName = PartyGlobals.DecorationIds.getString(decorBase.decorId) + label = DirectLabel(relief=None, geom=self.decorationModels.find('**/partyDecoration_%s' % assetName), text=textForDecoration, text_scale=TTLocalizer.EPdecorationItemLabel, text_align=TextNode.ACenter, text_pos=(-0.01, -0.43), text_wordwrap=7.0) + label['geom_scale'] = (2.6, 0.01, 0.05) + label['geom_pos'] = (0.0, 0.0, -0.33) + return label + + def getToonNameFromAvId(self, avId): + result = TTLocalizer.EventsPageUnknownToon + sender = base.cr.identifyAvatar(avId) + if sender: + result = sender.getName() + return result + + def loadInvitations(self): + EventsPage.notify.debug('loadInvitations') + self.selectedInvitationItem = None + self.invitationPartyList.removeAndDestroyAllItems() + self.invitationActivityList.removeAndDestroyAllItems() + self.invitePartyGoButton['state'] = DirectGuiGlobals.DISABLED + for partyInfo in base.localAvatar.partiesInvitedTo: + if partyInfo.status == PartyGlobals.PartyStatus.Cancelled or partyInfo.status == PartyGlobals.PartyStatus.Finished: + continue + inviteInfo = None + for inviteInfo in base.localAvatar.invites: + if partyInfo.partyId == inviteInfo.partyId: + break + + if inviteInfo is None: + EventsPage.notify.error('No invitation info for party id %d' % partyInfo.partyId) + return + if inviteInfo.status == PartyGlobals.InviteStatus.NotRead: + continue + hostName = self.getToonNameFromAvId(partyInfo.hostId) + item = DirectButton(relief=None, text=hostName, text_align=TextNode.ALeft, text_bg=Vec4(0.0, 0.0, 0.0, 0.0), text_scale=0.045, textMayChange=True, command=self.invitePartyClicked) + PartyUtils.truncateTextOfLabelBasedOnWidth(item, hostName, PartyGlobals.EventsPageHostNameMaxWidth) + item['extraArgs'] = [item] + item.setPythonTag('activityIds', partyInfo.getActivityIds()) + item.setPythonTag('partyStatus', partyInfo.status) + item.setPythonTag('hostId', partyInfo.hostId) + item.setPythonTag('startTime', partyInfo.startTime) + self.invitationPartyList.addItem(item) + + return + + def invitePartyClicked(self, item): + if item.getPythonTag('partyStatus') == PartyGlobals.PartyStatus.Started: + self.invitePartyGoButton['state'] = DirectGuiGlobals.NORMAL + else: + self.invitePartyGoButton['state'] = DirectGuiGlobals.DISABLED + if self.selectedInvitationItem is not None: + self.selectedInvitationItem['state'] = DirectGuiGlobals.NORMAL + self.selectedInvitationItem['text_bg'] = Vec4(0.0, 0.0, 0.0, 0.0) + self.selectedInvitationItem = item + self.selectedInvitationItem['state'] = DirectGuiGlobals.DISABLED + self.selectedInvitationItem['text_bg'] = Vec4(1.0, 1.0, 0.0, 1.0) + self.fillInviteActivityList(item.getPythonTag('activityIds')) + startTime = item.getPythonTag('startTime') + self.invitationDateTimeLabel['text'] = TTLocalizer.EventsPageInvitedTabTime % (PartyUtils.formatDate(startTime.year, startTime.month, startTime.day), PartyUtils.formatTime(startTime.hour, startTime.minute)) + return + + def fillInviteActivityList(self, activityIds): + self.invitationActivityList.removeAndDestroyAllItems() + countDict = {} + for actId in activityIds: + if actId not in countDict: + countDict[actId] = 1 + else: + countDict[actId] += 1 + + for activityId in countDict: + if countDict[activityId] == 1: + textOfActivity = TTLocalizer.PartyActivityNameDict[activityId]['generic'] + else: + textOfActivity = TTLocalizer.PartyActivityNameDict[activityId]['generic'] + ' x ' + str(countDict[activityId]) + geom = getPartyActivityIcon(self.activityIconsModel, PartyGlobals.ActivityIds.getString(activityId)) + item = DirectLabel(relief=None, text=textOfActivity, text_align=TextNode.ACenter, text_scale=0.05, text_pos=(0.0, -0.15), geom_scale=0.3, geom_pos=Vec3(0.0, 0.0, 0.07), geom=geom) + self.invitationActivityList.addItem(item) + + return + + def _inviteStartParty(self): + if self.selectedInvitationItem is None: + self.invitePartyGoButton['state'] = DirectGuiGlobals.DISABLED + return + self.doneStatus = {'mode': 'startparty', + 'firstStart': False, + 'hostId': self.selectedInvitationItem.getPythonTag('hostId')} + messenger.send(self.doneEvent) + return + + def loadHostedPartyInfo(self): + self.unloadGuests() + self.unloadActivities() + self.unloadDecorations() + self.hostedPartyInfo = None + self.confirmCancelPartyGui.doneStatus = '' + self.confirmCancelPartyGui.hide() + self.cancelPartyResultGui.doneStatus = '' + self.cancelPartyResultGui.hide() + if base.localAvatar.hostedParties is not None and len(base.localAvatar.hostedParties) > 0: + for partyInfo in base.localAvatar.hostedParties: + if partyInfo.status == PartyGlobals.PartyStatus.Pending or partyInfo.status == PartyGlobals.PartyStatus.CanStart or partyInfo.status == PartyGlobals.PartyStatus.NeverStarted or partyInfo.status == PartyGlobals.PartyStatus.Started: + self.hostedPartyInfo = partyInfo + self.loadGuests() + self.loadActivities() + self.loadDecorations() + self.hostingDateLabel['text'] = TTLocalizer.EventsPageHostTabDateTimeLabel % (PartyUtils.formatDate(partyInfo.startTime.year, partyInfo.startTime.month, partyInfo.startTime.day), PartyUtils.formatTime(partyInfo.startTime.hour, partyInfo.startTime.minute)) + self.isPrivate = partyInfo.isPrivate + self.__setPublicPrivateButton() + if partyInfo.status == PartyGlobals.PartyStatus.CanStart: + self.partyGoButton['state'] = DirectGuiGlobals.NORMAL + self.partyGoButton['text'] = (TTLocalizer.EventsPageGoButton,) + elif partyInfo.status == PartyGlobals.PartyStatus.Started: + place = base.cr.playGame.getPlace() + if isinstance(place, Party): + if hasattr(base, 'distributedParty'): + if base.distributedParty.partyInfo.hostId == base.localAvatar.doId: + self.partyGoButton['state'] = DirectGuiGlobals.DISABLED + else: + self.partyGoButton['state'] = DirectGuiGlobals.NORMAL + else: + self.partyGoButton['state'] = DirectGuiGlobals.NORMAL + self.notify.warning('base.distributedParty is not defined when base.cr.playGame.getPlace is party. This should never happen.') + else: + self.partyGoButton['state'] = DirectGuiGlobals.NORMAL + self.partyGoButton['text'] = (TTLocalizer.EventsPageGoBackButton,) + else: + self.partyGoButton['text'] = (TTLocalizer.EventsPageGoButton,) + self.partyGoButton['state'] = DirectGuiGlobals.DISABLED + if partyInfo.status not in (PartyGlobals.PartyStatus.Pending, PartyGlobals.PartyStatus.CanStart): + self.hostingCancelButton['state'] = DirectGuiGlobals.DISABLED + else: + self.hostingCancelButton['state'] = DirectGuiGlobals.NORMAL + self.hostingDateLabel.show() + self.hostedPartyDisplay.show() + return + + self.hostingDateLabel['text'] = TTLocalizer.EventsPageHostingTabNoParty + self.hostingCancelButton['state'] = DirectGuiGlobals.DISABLED + self.partyGoButton['state'] = DirectGuiGlobals.DISABLED + self.publicButton['state'] = DirectGuiGlobals.DISABLED + self.privateButton['state'] = DirectGuiGlobals.DISABLED + self.hostedPartyDisplay.show() + return + + def checkCanStartHostedParty(self): + result = True + if self.hostedPartyInfo.endTime < base.cr.toontownTimeManager.getCurServerDateTime() and self.hostedPartyInfo.status == PartyGlobals.PartyStatus.CanStart: + result = False + self.confirmTooLatePartyGui.show() + return result + + def confirmTooLateParty(self): + if hasattr(self, 'confirmTooLatePartyGui'): + self.confirmTooLatePartyGui.hide() + + def confirmPublicPrivateChange(self): + if hasattr(self, 'confirmPublicPrivateGui'): + self.confirmPublicPrivateGui.hide() + + def _startParty(self): + if not self.checkCanStartHostedParty(): + return + if self.hostedPartyInfo.status == PartyGlobals.PartyStatus.CanStart: + firstStart = True + else: + firstStart = False + self.doneStatus = {'mode': 'startparty', + 'firstStart': firstStart, + 'hostId': None} + messenger.send(self.doneEvent) + return + + def loadGuests(self): + for partyReplyInfoBase in base.localAvatar.partyReplyInfoBases: + if partyReplyInfoBase.partyId == self.hostedPartyInfo.partyId: + for singleReply in partyReplyInfoBase.replies: + toonName = self.getToonNameFromAvId(singleReply.inviteeId) + self.hostingGuestList.addItem(self.getGuestItem(toonName, singleReply.status)) + + def loadActivities(self): + countDict = {} + for activityBase in self.hostedPartyInfo.activityList: + if activityBase.activityId not in countDict: + countDict[activityBase.activityId] = 1 + else: + countDict[activityBase.activityId] += 1 + + idsUsed = [] + for activityBase in self.hostedPartyInfo.activityList: + if activityBase.activityId not in idsUsed: + idsUsed.append(activityBase.activityId) + count = countDict[activityBase.activityId] + self.hostingActivityList.addItem(self.getActivityItem(activityBase, count)) + + def loadDecorations(self): + countDict = {} + for decorBase in self.hostedPartyInfo.decors: + if decorBase.decorId not in countDict: + countDict[decorBase.decorId] = 1 + else: + countDict[decorBase.decorId] += 1 + + idsUsed = [] + for decorBase in self.hostedPartyInfo.decors: + if decorBase.decorId not in idsUsed: + count = countDict[decorBase.decorId] + self.hostingDecorationList.addItem(self.getDecorationItem(decorBase, count)) + idsUsed.append(decorBase.decorId) + + def unloadGuests(self): + self.hostingGuestList.removeAndDestroyAllItems() + + def unloadActivities(self): + self.hostingActivityList.removeAndDestroyAllItems() + + def unloadDecorations(self): + self.hostingDecorationList.removeAndDestroyAllItems() + + def unload(self): + self.scrollButtonGui.removeNode() + self.hostingGui.removeNode() + self.invitationGui.removeNode() + self.activityIconsModel.removeNode() + self.decorationModels.removeNode() + del self.titleLabel + self.hostingGuestList.removeAndDestroyAllItems() + self.hostingGuestList.destroy() + del self.hostingGuestList + self.hostingActivityList.removeAndDestroyAllItems() + self.hostingActivityList.destroy() + del self.hostingActivityList + self.hostingDecorationList.removeAndDestroyAllItems() + self.hostingDecorationList.destroy() + del self.hostingDecorationList + self.invitationPartyList.removeAndDestroyAllItems() + self.invitationPartyList.destroy() + del self.invitationPartyList + self.invitationActivityList.removeAndDestroyAllItems() + self.invitationActivityList.destroy() + del self.invitationActivityList + self.confirmCancelPartyGui.cleanup() + del self.confirmCancelPartyGui + self.confirmTooLatePartyGui.cleanup() + del self.confirmTooLatePartyGui + self.confirmPublicPrivateGui.cleanup() + del self.confirmPublicPrivateGui + self.ignore('changePartyPrivateResponseReceived') + taskMgr.remove('changePartyPrivateResponseReceivedTimeOut') + self.cancelPartyResultGui.cleanup() + del self.cancelPartyResultGui + self.ignore(self.confirmCancelPartyEvent) + self.ignore(self.cancelPartyResultGuiEvent) + self.avatar = None + self.hostingCancelButton.destroy() + del self.hostingCancelButton + self.partyGoButton.destroy() + del self.partyGoButton + self.publicButton.destroy() + del self.publicButton + self.privateButton.destroy() + del self.privateButton + self.invitePartyGoButton.destroy() + del self.invitePartyGoButton + self.hostTab.destroy() + self.invitedTab.destroy() + self.calendarTab.destroy() + if self.calendarGuiMonth: + self.calendarGuiMonth.destroy() + self.toontownTimeGui.destroy() + taskMgr.remove('EventsPageUpdateTask-doLater') + ShtikerPage.ShtikerPage.unload(self) + return + + def enter(self): + self.updatePage() + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + ShtikerPage.ShtikerPage.exit(self) + self.unloadGuests() + self.unloadActivities() + self.unloadDecorations() + + def __handleConfirm(self): + self.ignore('confirmDone') + self.confirm.cleanup() + del self.confirm + + def createListAndLabel(self, parent, gui, typeString, itemsVisible, downString = 'DownArrow', upString = 'UpArrow', textString = '_text_locator'): + list = DirectScrolledList(parent=parent, relief=None, incButton_image=(gui.find('**/%s%s_up' % (typeString, downString)), + gui.find('**/%s%s_down' % (typeString, downString)), + gui.find('**/%s%s_rollover' % (typeString, downString)), + gui.find('**/%s%s_inactive' % (typeString, downString))), incButton_relief=None, decButton_image=(gui.find('**/%s%s_up' % (typeString, upString)), + gui.find('**/%s%s_down' % (typeString, upString)), + gui.find('**/%s%s_rollover' % (typeString, upString)), + gui.find('**/%s%s_inactive' % (typeString, upString))), decButton_relief=None, itemFrame_pos=gui.find('**/%s_locator' % typeString).getPos(), itemFrame_relief=None, numItemsVisible=itemsVisible, forceHeight=0.07) + strings = {'guests': TTLocalizer.EventsPageHostingTabGuestListTitle, + 'activities': TTLocalizer.EventsPageHostingTabActivityListTitle, + 'decorations': TTLocalizer.EventsPageHostingTabDecorationsListTitle, + 'parties': TTLocalizer.EventsPageHostingTabPartiesListTitle} + label = DirectLabel(parent=parent, relief=None, text=strings[typeString], text_scale=TTLocalizer.EPcreateListAndLabel, pos=gui.find('**/%s%s' % (typeString, textString)).getPos()) + return (list, label) + + def setMode(self, mode, updateAnyways = 0): + messenger.send('wakeup') + if updateAnyways == False: + if self.mode == mode: + return + else: + self.mode = mode + self.show() + self.updatePage() + + def getMode(self): + return self.mode + + def updatePage(self): + if self.mode == EventsPage_Host: + self.hostTab['state'] = DirectGuiGlobals.DISABLED + self.invitedTab['state'] = DirectGuiGlobals.NORMAL + self.calendarTab['state'] = DirectGuiGlobals.NORMAL + self.invitationDisplay.hide() + self.hostedPartyDisplay.show() + self.calendarDisplay.hide() + self.loadHostedPartyInfo() + if self.hostedPartyInfo is None: + self.titleLabel['text'] = TTLocalizer.EventsPageHostTabTitleNoParties + else: + self.titleLabel['text'] = TTLocalizer.EventsPageHostTabTitle + elif self.mode == EventsPage_Invited: + self.titleLabel['text'] = TTLocalizer.EventsPageInvitedTabTitle + self.hostTab['state'] = DirectGuiGlobals.NORMAL + self.invitedTab['state'] = DirectGuiGlobals.DISABLED + self.calendarTab['state'] = DirectGuiGlobals.NORMAL + self.hostedPartyDisplay.hide() + self.invitationDisplay.show() + self.calendarDisplay.hide() + self.loadInvitations() + elif self.mode == EventsPage_Calendar: + self.titleLabel['text'] = '' + self.hostTab['state'] = DirectGuiGlobals.NORMAL + self.invitedTab['state'] = DirectGuiGlobals.NORMAL + self.calendarTab['state'] = DirectGuiGlobals.DISABLED + self.hostedPartyDisplay.hide() + self.invitationDisplay.hide() + self.calendarDisplay.show() + if not self.calendarGuiMonth: + curServerDate = base.cr.toontownTimeManager.getCurServerDateTime() + self.calendarGuiMonth = CalendarGuiMonth(self.calendarDisplay, curServerDate, onlyFutureMonthsClickable=True) + self.calendarGuiMonth.changeMonth(0) + return + + def __setPublicPrivateButton(self): + if self.isPrivate: + self.privateButton['indicatorValue'] = True + self.publicButton['indicatorValue'] = False + self.privateButton['state'] = DirectGuiGlobals.DISABLED + self.publicButton['state'] = DirectGuiGlobals.NORMAL + else: + self.privateButton['indicatorValue'] = False + self.publicButton['indicatorValue'] = True + self.privateButton['state'] = DirectGuiGlobals.NORMAL + self.publicButton['state'] = DirectGuiGlobals.DISABLED + + def __changePublicPrivate(self, indicator): + self.__setPublicPrivateButton() + self.confirmPublicPrivateGui['text'] = TTLocalizer.EventsPagePublicPrivateChange + self.confirmPublicPrivateGui.buttonList[0].hide() + self.confirmPublicPrivateGui.show() + base.cr.partyManager.sendChangePrivateRequest(self.hostedPartyInfo.partyId, not self.isPrivate) + self.accept('changePartyPrivateResponseReceived', self.changePartyPrivateResponseReceived) + taskMgr.doMethodLater(5.0, self.changePartyPrivateResponseReceived, 'changePartyPrivateResponseReceivedTimeOut', [0, 0, PartyGlobals.ChangePartyFieldErrorCode.DatabaseError]) + + def changePartyPrivateResponseReceived(self, partyId, newPrivateStatus, errorCode): + EventsPage.notify.debug('changePartyPrivateResponseReceived called with partyId = %d, newPrivateStatus = %d, errorCode = %d' % (partyId, newPrivateStatus, errorCode)) + taskMgr.remove('changePartyPrivateResponseReceivedTimeOut') + self.ignore('changePartyPrivateResponseReceived') + if errorCode == PartyGlobals.ChangePartyFieldErrorCode.AllOk: + self.isPrivate = newPrivateStatus + self.confirmPublicPrivateGui.hide() + else: + self.confirmPublicPrivateGui.buttonList[0].show() + if errorCode == PartyGlobals.ChangePartyFieldErrorCode.AlreadyStarted: + self.confirmPublicPrivateGui['text'] = TTLocalizer.EventsPagePublicPrivateAlreadyStarted + else: + self.confirmPublicPrivateGui['text'] = TTLocalizer.EventsPagePublicPrivateNoGo + self.__setPublicPrivateButton() + + def __doCancelParty(self): + if self.hostedPartyInfo: + if self.hostedPartyInfo.status == PartyGlobals.PartyStatus.Pending or self.hostedPartyInfo.status == PartyGlobals.PartyStatus.CanStart or self.hostedPartyInfo.status == PartyGlobals.PartyStatus.NeverStarted: + self.hostingCancelButton['state'] = DirectGuiGlobals.DISABLED + self.confirmCancelPartyGui.show() + + def confirmCancelOfParty(self): + self.confirmCancelPartyGui.hide() + if self.confirmCancelPartyGui.doneStatus == 'ok': + base.cr.partyManager.sendChangePartyStatusRequest(self.hostedPartyInfo.partyId, PartyGlobals.PartyStatus.Cancelled) + self.accept('changePartyStatusResponseReceived', self.changePartyStatusResponseReceived) + else: + self.hostingCancelButton['state'] = DirectGuiGlobals.NORMAL + + def changePartyStatusResponseReceived(self, partyId, newPartyStatus, errorCode, beansRefunded): + EventsPage.notify.debug('changePartyStatusResponseReceived called with partyId = %d, newPartyStatus = %d, errorCode = %d' % (partyId, newPartyStatus, errorCode)) + if errorCode == PartyGlobals.ChangePartyFieldErrorCode.AllOk: + if newPartyStatus == PartyGlobals.PartyStatus.Cancelled: + self.loadHostedPartyInfo() + self.cancelPartyResultGui['text'] = TTLocalizer.EventsPageCancelPartyResultOk % beansRefunded + self.cancelPartyResultGui.show() + elif errorCode == PartyGlobals.ChangePartyFieldErrorCode.AlreadyRefunded and newPartyStatus == PartyGlobals.PartyStatus.NeverStarted: + self.loadHostedPartyInfo() + self.cancelPartyResultGui['text'] = TTLocalizer.EventsPageCancelPartyAlreadyRefunded + self.cancelPartyResultGui.show() + else: + self.cancelPartyResultGui['text'] = TTLocalizer.EventsPageCancelPartyResultError + self.cancelPartyResultGui.show() + self.hostingCancelButton['state'] = DirectGuiGlobals.NORMAL + + def cancelPartyResultGuiCommand(self): + self.cancelPartyResultGui.hide() + + def updateToontownTime(self): + self.toontownTimeGui.updateTime() diff --git a/toontown/shtiker/FishPage.py b/toontown/shtiker/FishPage.py new file mode 100755 index 00000000..d2fedbd6 --- /dev/null +++ b/toontown/shtiker/FishPage.py @@ -0,0 +1,300 @@ +from toontown.toonbase import ToontownGlobals +import ShtikerPage +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer +from toontown.fishing import FishPicker +from toontown.fishing import FishBrowser +from toontown.fishing import FishGlobals +FishPage_Tank = 0 +FishPage_Collection = 1 +FishPage_Trophy = 2 +TROPHIES_PER_ROW = 5 + +class FishPage(ShtikerPage.ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('FishPage') + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.avatar = None + self.mode = FishPage_Tank + return + + def enter(self): + if not hasattr(self, 'title'): + self.load() + self.setMode(self.mode, 1) + self.accept(localAvatar.uniqueName('fishTankChange'), self.updatePage) + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + if hasattr(self, 'picker'): + self.picker.hide() + if hasattr(self, 'browser'): + self.browser.hide() + self.ignore(localAvatar.uniqueName('fishTankChange')) + ShtikerPage.ShtikerPage.exit(self) + + def setAvatar(self, av): + self.avatar = av + + def getAvatar(self): + return self.avatar + + def load(self): + ShtikerPage.ShtikerPage.load(self) + gui = loader.loadModel('phase_3.5/models/gui/fishingBook') + rodFrame = gui.find('**/bucket/fram1') + rodFrame.removeNode() + trophyCase = gui.find('**/trophyCase1') + trophyCase.find('glass1').reparentTo(trophyCase, -1) + trophyCase.find('shelf').reparentTo(trophyCase, -1) + self.trophyCase = trophyCase + self.title = DirectLabel(parent=self, relief=None, text='', text_scale=0.1, pos=(0, 0, 0.65)) + normalColor = (1, 1, 1, 1) + clickColor = (0.8, 0.8, 0, 1) + rolloverColor = (0.15, 0.82, 1.0, 1) + diabledColor = (1.0, 0.98, 0.15, 1) + self.tankTab = DirectButton(parent=self, relief=None, text=TTLocalizer.FishPageTankTab, text_scale=TTLocalizer.FPtankTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface1'), image_pos=(0.55, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[FishPage_Tank], pos=(0.92, 0, 0.55)) + self.collectionTab = DirectButton(parent=self, relief=None, text=TTLocalizer.FishPageCollectionTab, text_scale=TTLocalizer.FPcollectionTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[FishPage_Collection], pos=(0.92, 0, 0.1)) + self.trophyTab = DirectButton(parent=self, relief=None, text=TTLocalizer.FishPageTrophyTab, text_scale=TTLocalizer.FPtrophyTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface3'), image_pos=(-0.28, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[FishPage_Trophy], pos=(0.92, 0, -0.3)) + self.tankTab.setPos(-0.55, 0, 0.775) + self.collectionTab.setPos(-0.13, 0, 0.775) + self.trophyTab.setPos(0.28, 0, 0.775) + return + + def createFishPicker(self): + if not hasattr(self, 'picker'): + self.picker = FishPicker.FishPicker(self) + self.picker.setPos(-0.555, 0, 0.1) + self.picker.setScale(0.95) + self.rod = DirectLabel(parent=self.picker, relief=None, text='', text_align=TextNode.ALeft, text_scale=0.06, pos=(0.9, 0, -0.65)) + return + + def createFishBrowser(self): + if not hasattr(self, 'browser'): + self.browser = FishBrowser.FishBrowser(self) + self.browser.setScale(1.1) + self.collectedTotal = DirectLabel(parent=self.browser, relief=None, text='', text_scale=0.06, pos=(0, 0, -0.61)) + return + + def createFishTrophyFrame(self): + if not hasattr(self, 'trophyFrame'): + self.trophyFrame = DirectFrame(parent=self, relief=None, image=self.trophyCase, image_pos=(0, 1, 0), image_scale=0.034) + self.trophyFrame.hide() + self.trophies = [] + hOffset = -0.5 + vOffset = 0.4 + for level, trophyDesc in FishGlobals.TrophyDict.items(): + trophy = FishingTrophy(-1) + trophy.nameLabel['text'] = trophyDesc[0] + trophy.reparentTo(self.trophyFrame) + trophy.setScale(0.36) + if level % TROPHIES_PER_ROW == 0: + hOffset = -0.5 + vOffset -= 0.4 + trophy.setPos(hOffset, 0, vOffset) + hOffset += 0.25 + self.trophies.append(trophy) + + return + + def setMode(self, mode, updateAnyways = 0): + messenger.send('wakeup') + if not updateAnyways: + if self.mode == mode: + return + else: + self.mode = mode + self.show() + if mode == FishPage_Tank: + self.title['text'] = TTLocalizer.FishPageTitleTank + if not hasattr(self, 'picker'): + self.createFishPicker() + self.picker.show() + if hasattr(self, 'browser'): + self.browser.hide() + if hasattr(self, 'trophyFrame'): + self.trophyFrame.hide() + self.tankTab['state'] = DGG.DISABLED + self.collectionTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.NORMAL + elif mode == FishPage_Collection: + self.title['text'] = TTLocalizer.FishPageTitleCollection + if hasattr(self, 'picker'): + self.picker.hide() + if not hasattr(self, 'browser'): + self.createFishBrowser() + self.browser.show() + if hasattr(self, 'trophyFrame'): + self.trophyFrame.hide() + self.tankTab['state'] = DGG.NORMAL + self.collectionTab['state'] = DGG.DISABLED + self.trophyTab['state'] = DGG.NORMAL + elif mode == FishPage_Trophy: + self.title['text'] = TTLocalizer.FishPageTitleTrophy + if hasattr(self, 'picker'): + self.picker.hide() + if hasattr(self, 'browser'): + self.browser.hide() + if not hasattr(self, 'trophyFrame'): + self.createFishTrophyFrame() + self.trophyFrame.show() + self.tankTab['state'] = DGG.NORMAL + self.collectionTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.DISABLED + self.updatePage() + + def unload(self): + self.avatar = None + if hasattr(self, 'trophies'): + del self.trophies + if hasattr(self, 'trophyCase'): + del self.trophyCase + self.tankTab.destroy() + self.collectionTab.destroy() + self.trophyTab.destroy() + ShtikerPage.ShtikerPage.unload(self) + return + + def updatePage(self): + if hasattr(self, 'collectedTotal'): + self.collectedTotal['text'] = TTLocalizer.FishPageCollectedTotal % (len(base.localAvatar.fishCollection), FishGlobals.getTotalNumFish()) + if hasattr(self, 'rod'): + rod = base.localAvatar.fishingRod + rodName = TTLocalizer.FishingRodNameDict[rod] + rodWeightRange = FishGlobals.getRodWeightRange(rod) + self.rod['text'] = TTLocalizer.FishPageRodInfo % (rodName, rodWeightRange[0], rodWeightRange[1]) + if self.mode == FishPage_Tank: + if hasattr(self, 'picker'): + newTankFish = base.localAvatar.fishTank.getFish() + self.picker.update(newTankFish) + elif self.mode == FishPage_Collection: + if hasattr(self, 'browser'): + self.browser.update() + elif self.mode == FishPage_Trophy: + if hasattr(self, 'trophies'): + for trophy in self.trophies: + trophy.setLevel(-1) + + for trophyId in base.localAvatar.getFishingTrophies(): + self.trophies[trophyId].setLevel(trophyId) + + def destroy(self): + self.notify.debug('destroy') + DirectFrame.destroy(self) + + +class FishingTrophy(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FishingTrophy') + + def __init__(self, level): + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(FishingTrophy) + self.trophy = loader.loadModel('phase_3.5/models/gui/fishingTrophy') + self.trophy.reparentTo(self) + self.trophy.setPos(0, 1, 0) + self.trophy.setScale(0.1) + self.base = self.trophy.find('**/trophyBase') + self.column = self.trophy.find('**/trophyColumn') + self.top = self.trophy.find('**/trophyTop') + self.topBase = self.trophy.find('**/trophyTopBase') + self.statue = self.trophy.find('**/trophyStatue') + self.base.setColorScale(1, 1, 0.8, 1) + self.bowl = loader.loadModel('phase_3.5/models/gui/fishingTrophyBowl') + self.bowl.reparentTo(self) + self.bowl.setPos(0, 1, 0) + self.bowl.setScale(2.0) + self.bowlTop = self.bowl.find('**/fishingTrophyGreyBowl') + self.bowlBase = self.bowl.find('**/fishingTrophyBase') + self.bowlBase.setScale(1.25, 1, 1) + self.bowlBase.setColorScale(1, 1, 0.8, 1) + self.nameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.15), text='Trophy Text', text_scale=0.125, text_fg=Vec4(0.9, 0.9, 0.4, 1)) + self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.shadow.reparentTo(self) + self.shadow.setColor(1, 1, 1, 0.2) + self.shadow.setPosHprScale(0, 1, 0.35, 0, 90, 0, 0.1, 0.14, 0.1) + self.setLevel(level) + return + + def setLevel(self, level): + self.level = level + if level == -1: + self.trophy.hide() + self.bowl.hide() + self.nameLabel.hide() + elif level == 0: + self.trophy.show() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 1.11878) + self.top.setPos(0, 0, -1) + self.__bronze() + elif level == 1: + self.trophy.show() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 1.61878) + self.top.setPos(0, 0, -0.5) + self.__bronze() + elif level == 2: + self.trophy.show() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 2.11878) + self.top.setPos(0, 0, 0) + self.__silver() + elif level == 3: + self.trophy.show() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 2.61878) + self.top.setPos(0, 0, 0.5) + self.__silver() + elif level == 4: + self.trophy.show() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 3.11878) + self.top.setPos(0, 0, 1) + self.__gold() + elif level == 5: + self.trophy.hide() + self.bowl.show() + self.bowlTop.setScale(1.75) + self.nameLabel.show() + self.__bronze() + elif level == 6: + self.trophy.hide() + self.bowl.show() + self.bowlTop.setScale(2.0) + self.nameLabel.show() + self.__silver() + elif level >= 7: + self.trophy.hide() + self.bowl.show() + self.bowlTop.setScale(2.25) + self.nameLabel.show() + self.__gold() + + def __bronze(self): + self.top.setColorScale(0.9, 0.6, 0.33, 1) + self.bowlTop.setColorScale(0.9, 0.6, 0.33, 1) + + def __silver(self): + self.top.setColorScale(0.9, 0.9, 1, 1) + self.bowlTop.setColorScale(0.9, 0.9, 1, 1) + + def __gold(self): + self.top.setColorScale(1, 0.95, 0.1, 1) + self.bowlTop.setColorScale(1, 0.95, 0.1, 1) + + def destroy(self): + self.trophy.removeNode() + self.bowl.removeNode() + self.shadow.removeNode() + DirectFrame.destroy(self) + + def show(self): + ShtikerPage.show(self) diff --git a/toontown/shtiker/GardenPage.py b/toontown/shtiker/GardenPage.py new file mode 100755 index 00000000..1a4406cd --- /dev/null +++ b/toontown/shtiker/GardenPage.py @@ -0,0 +1,481 @@ +from direct.directnotify import DirectNotifyGlobal +import ShtikerPage +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer +from toontown.estate import FlowerBrowser +from toontown.estate import GardenGlobals +from toontown.estate import FlowerPicker +from toontown.estate import SpecialsPhoto +from toontown.toontowngui import TTDialog +GardenPage_Basket = 0 +GardenPage_Collection = 1 +GardenPage_Trophy = 2 +GardenPage_Specials = 3 +TROPHIES_PER_ROW = 5 + +class GardenPage(ShtikerPage.ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('GardenPage') + + def __init__(self): + self.notify.debug('__init__') + ShtikerPage.ShtikerPage.__init__(self) + self.mode = GardenPage_Basket + self.accept('use-special-response', self.useSpecialDone) + self.resultDialog = None + return + + def enter(self): + self.notify.debug('enter') + if not hasattr(self, 'title'): + self.load() + self.setMode(self.mode, 1) + self.accept(localAvatar.uniqueName('flowerBasketChange'), self.updatePage) + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + self.notify.debug('exit') + if hasattr(self, 'picker'): + self.picker.hide() + if hasattr(self, 'browser'): + self.browser.hide() + if hasattr(self, 'specialsFrame'): + self.specialsFrame.hide() + if hasattr(self, 'specialsPhoto'): + self.specialsPhoto.hide() + if hasattr(self, 'useSpecialButton'): + self.hide() + self.cleanupResultDialog() + ShtikerPage.ShtikerPage.exit(self) + + def load(self): + self.notify.debug('load') + ShtikerPage.ShtikerPage.load(self) + gui = loader.loadModel('phase_3.5/models/gui/fishingBook') + trophyCase = gui.find('**/trophyCase1') + trophyCase.find('glass1').reparentTo(trophyCase, -1) + trophyCase.find('shelf').reparentTo(trophyCase, -1) + self.trophyCase = trophyCase + self.title = DirectLabel(parent=self, relief=None, text='', text_scale=0.1, pos=(0, 0, 0.65)) + normalColor = (1, 1, 1, 1) + clickColor = (0.8, 0.8, 0, 1) + rolloverColor = (0.15, 0.82, 1.0, 1) + diabledColor = (1.0, 0.98, 0.15, 1) + self.basketTab = DirectButton(parent=self, relief=None, text=TTLocalizer.GardenPageBasketTab, text_scale=TTLocalizer.GPbasketTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface1'), image_pos=(0.55, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[GardenPage_Basket], pos=(0.92, 0, 0.55)) + self.collectionTab = DirectButton(parent=self, relief=None, text=TTLocalizer.GardenPageCollectionTab, text_scale=TTLocalizer.GPcollectionTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[GardenPage_Collection], pos=(0.92, 0, 0.1)) + self.trophyTab = DirectButton(parent=self, relief=None, text=TTLocalizer.GardenPageTrophyTab, text_scale=TTLocalizer.GPtrophyTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface3'), image_pos=(-0.28, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[GardenPage_Trophy], pos=(0.92, 0, -0.3)) + self.specialsTab = DirectButton(parent=self, relief=None, text=TTLocalizer.GardenPageSpecialsTab, text_scale=TTLocalizer.GPspecialsTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface3'), image_pos=(-0.28, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[GardenPage_Specials], pos=(0.92, 0, -0.3)) + self.basketTab.setPos(-0.75, 0, 0.775) + self.collectionTab.setPos(-0.33, 0, 0.775) + self.trophyTab.setPos(0.09, 0, 0.775) + self.specialsTab.setPos(0.51, 0, 0.775) + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.gardenSpecialsList = DirectScrolledList(parent=self, relief=None, incButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_pos=(0.0, 0.0, -1.1), incButton_image1_color=Vec4(1.0, 0.9, 0.4, 1.0), incButton_image3_color=Vec4(1.0, 1.0, 0.6, 0.5), incButton_scale=(1.0, 1.0, -1.0), decButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_pos=(0.0, 0.0, 0.117), decButton_image1_color=Vec4(1.0, 1.0, 0.6, 1.0), decButton_image3_color=Vec4(1.0, 1.0, 0.6, 0.6), itemFrame_pos=(-0.2, 0.0, 0.05), itemFrame_relief=None, numItemsVisible=18, items=[], pos=(-0.6, 0, 0.45)) + self.gardenSpecialsList.hide() + self.specialsFrame = DirectFrame(parent=self, relief=None, pos=(0.45, 0.0, 0.25), text='', text_wordwrap=14.4, text_pos=(0, -0.46), text_scale=0.06) + self.specialsInfo = DirectLabel(parent=self.specialsFrame, relief=None, pos=(0.0, 0.0, -0.0), text=' ', text_wordwrap=12.4, text_pos=(0, -0.46), text_scale=0.06) + self.specialsPhoto = SpecialsPhoto.SpecialsPhoto(-1, parent=self.specialsFrame) + self.specialsPhoto.setBackBounds(-0.3, 0.3, -0.235, 0.25) + self.specialsPhoto.setBackColor(1.0, 1.0, 0.74901, 1.0) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.useSpecialButton = DirectButton(parent=self, relief=None, image=okImageList, pos=(0.45, 0, -0.5), text=TTLocalizer.UseSpecial, text_scale=0.06, text_pos=(0, -0.1), command=self.__useSpecial) + buttons.removeNode() + return + + def setMode(self, mode, updateAnyways = 0): + messenger.send('wakeup') + if not updateAnyways: + if self.mode == mode: + return + else: + self.mode = mode + self.gardenSpecialsList.hide() + self.specialsPhoto.hide() + self.specialsFrame.hide() + self.useSpecialButton.hide() + if mode == GardenPage_Basket: + self.title['text'] = TTLocalizer.GardenPageTitleBasket + if not hasattr(self, 'picker'): + self.createFlowerPicker() + self.picker.show() + if hasattr(self, 'browser'): + self.browser.hide() + if hasattr(self, 'trophyFrame'): + self.trophyFrame.hide() + self.basketTab['state'] = DGG.DISABLED + self.collectionTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.NORMAL + self.specialsTab['state'] = DGG.NORMAL + elif mode == GardenPage_Collection: + self.title['text'] = TTLocalizer.GardenPageTitleCollection + if hasattr(self, 'picker'): + self.picker.hide() + if not hasattr(self, 'browser'): + self.createAlbumBrowser() + self.browser.show() + if hasattr(self, 'trophyFrame'): + self.trophyFrame.hide() + self.basketTab['state'] = DGG.NORMAL + self.collectionTab['state'] = DGG.DISABLED + self.trophyTab['state'] = DGG.NORMAL + self.specialsTab['state'] = DGG.NORMAL + elif mode == GardenPage_Trophy: + self.title['text'] = TTLocalizer.GardenPageTitleTrophy + if hasattr(self, 'picker'): + self.picker.hide() + if hasattr(self, 'browser'): + self.browser.hide() + if not hasattr(self, 'trophyFrame'): + self.createGardenTrophyFrame() + self.trophyFrame.show() + self.basketTab['state'] = DGG.NORMAL + self.collectionTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.DISABLED + self.specialsTab['state'] = DGG.NORMAL + elif mode == GardenPage_Specials: + self.title['text'] = TTLocalizer.GardenPageTitleSpecials + if hasattr(self, 'picker'): + self.picker.hide() + if hasattr(self, 'browser'): + self.browser.hide() + if hasattr(self, 'trophyFrame'): + self.trophyFrame.hide() + self.basketTab['state'] = DGG.NORMAL + self.collectionTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.NORMAL + self.specialsTab['state'] = DGG.DISABLED + self.gardenSpecialsList.show() + specialsList = localAvatar.getGardenSpecials() + self.specialsPhoto.show() + self.specialsFrame.show() + self.createGardenSpecialsList() + self.updatePage() + + def createGardenSpecialsList(self): + self.clearGS() + self.specialsInfo['text'] = '' + self.useSpecialButton.hide() + self.specialsPhoto.hide() + self.specialsPhoto.update(-1) + self.specialsPhoto.show() + specialsList = localAvatar.getGardenSpecials() + firstEntry = None + if len(specialsList) == 0: + self.gardenSpecialsList['incButton_image1_color'] = Vec4(1.0, 0.9, 0.4, 0.0) + self.gardenSpecialsList['incButton_image3_color'] = Vec4(1.0, 0.9, 0.4, 0.0) + self.gardenSpecialsList['decButton_image1_color'] = Vec4(1.0, 0.9, 0.4, 0.0) + self.gardenSpecialsList['decButton_image3_color'] = Vec4(1.0, 0.9, 0.4, 0.0) + else: + self.gardenSpecialsList['incButton_image1_color'] = Vec4(1.0, 0.9, 0.4, 1.0) + self.gardenSpecialsList['incButton_image3_color'] = Vec4(1.0, 0.9, 0.4, 1.0) + self.gardenSpecialsList['decButton_image1_color'] = Vec4(1.0, 0.9, 0.4, 1.0) + self.gardenSpecialsList['decButton_image3_color'] = Vec4(1.0, 0.9, 0.4, 1.0) + for entry in specialsList: + if not firstEntry: + firstEntry = entry + someItem = DirectScrolledListItem(parent=self.gardenSpecialsList, text='%s x %s' % (GardenGlobals.Specials[entry[0]]['photoName'], entry[1]), text_align=TextNode.ALeft, text_fg=(0.0, 0.0, 0.0, 1), text_bg=(1.0, 1.0, 1, 0), text_scale=0.06, relief=None, command=self.showSpecialsPanel, extraArgs=[entry]) + self.gardenSpecialsList.addItem(someItem) + self.specialsPhoto.show() + + if firstEntry: + self.showSpecialsPanel(firstEntry) + return + + def showSpecialsPanel(self, entry): + type = entry[0] + number = entry[1] + self.specialsPhoto.hide() + self.specialsPhoto.update(type) + self.specialsPhoto.show() + self.specialsInfo['text'] = GardenGlobals.Specials[entry[0]]['description'] + self.selectedSpecial = type + specialInfo = GardenGlobals.Specials[entry[0]] + if 'useFromShtiker' in specialInfo and specialInfo['useFromShtiker']: + self.useSpecialButton.show() + else: + self.useSpecialButton.hide() + + def __useSpecial(self): + self.useSpecialButton['state'] = DGG.DISABLED + localAvatar.sendUpdate('reqUseSpecial', [self.selectedSpecial]) + + def clearGS(self): + while len(self.gardenSpecialsList['items']) > 0: + for item in self.gardenSpecialsList['items']: + self.gardenSpecialsList.removeItem(item, 1) + if hasattr(item, 'destroy'): + item.destroy() + if hasattr(item, 'delete'): + item.delete() + del item + + def createAlbumBrowser(self): + if not hasattr(self, 'browser'): + self.browser = FlowerBrowser.FlowerBrowser(self) + self.browser.setScale(1.1) + self.collectedTotal = DirectLabel(parent=self.browser, relief=None, text='', text_scale=0.06, pos=(0, 0, -0.61)) + return + + def createGardenTrophyFrame(self): + if not hasattr(self, 'trophyFrame'): + self.trophyFrame = DirectFrame(parent=self, relief=None, image=self.trophyCase, image_pos=(0, 1, 0), image_scale=0.034) + self.trophyFrame.hide() + self.trophies = [] + hOffset = -0.5 + vOffset = 0.4 + for level, trophyDesc in GardenGlobals.TrophyDict.items(): + trophy = GardenTrophy(-1) + trophy.nameLabel['text'] = trophyDesc[0] + trophy.reparentTo(self.trophyFrame) + trophy.setScale(0.36) + if level % TROPHIES_PER_ROW == 0: + hOffset = -0.5 + vOffset -= 0.4 + trophy.setPos(hOffset, 0, vOffset) + hOffset += 0.25 + self.trophies.append(trophy) + + return + + def createFlowerPicker(self): + if not hasattr(self, 'picker'): + self.picker = FlowerPicker.FlowerPicker(self) + self.picker.setPos(-0.555, 0, 0.1) + self.picker.setScale(0.95) + self.FUDGE_FACTOR = 0.01 + self.barLength = 1.1 + self.shovelBar = DirectWaitBar(parent=self.picker, pos=(0.95, 0, -0.55), relief=DGG.SUNKEN, frameSize=(-0.65, + 1.05, + -0.1, + 0.1), borderWidth=(0.025, 0.025), scale=0.45, frameColor=(0.8, 0.8, 0.7, 1), barColor=(0.6, 0.4, 0.2, 1), range=self.barLength + self.FUDGE_FACTOR, value=self.barLength * 0.5 + self.FUDGE_FACTOR, text=' ' + TTLocalizer.Laff, text_scale=0.11, text_fg=(0.05, 0.14, 0.2, 1), text_align=TextNode.ALeft, text_pos=(-0.57, -0.035)) + self.wateringCanBar = DirectWaitBar(parent=self.picker, pos=(0.95, 0, -0.75), relief=DGG.SUNKEN, frameSize=(-0.65, + 1.05, + -0.1, + 0.1), borderWidth=(0.025, 0.025), scale=0.45, frameColor=(0.8, 0.8, 0.7, 1), barColor=(0.4, 0.6, 1.0, 1), range=self.barLength + self.FUDGE_FACTOR, value=self.barLength * 0.5 + self.FUDGE_FACTOR, text=' ' + TTLocalizer.Laff, text_scale=0.11, text_fg=(0.05, 0.14, 0.2, 1), text_align=TextNode.ALeft, text_pos=(-0.57, -0.035)) + + def unload(self): + print 'gardenPage Unloading' + if hasattr(self, 'specialsPhoto'): + del self.specialsPhoto + if hasattr(self, 'trophies'): + del self.trophies + if hasattr(self, 'trophyCase'): + del self.trophyCase + if hasattr(self, 'useSpecialButton'): + self.useSpecialButton.destroy() + del self.useSpecialButton + self.cleanupResultDialog() + self.gardenSpecialsList.destroy() + self.basketTab.destroy() + self.collectionTab.destroy() + self.trophyTab.destroy() + self.specialsTab.destroy() + ShtikerPage.ShtikerPage.unload(self) + + def updatePage(self): + if hasattr(self, 'collectedTotal'): + self.collectedTotal['text'] = TTLocalizer.GardenPageCollectedTotal % (len(base.localAvatar.flowerCollection), GardenGlobals.getNumberOfFlowerVarieties()) + if hasattr(self, 'shovelBar'): + shovel = base.localAvatar.shovel + shovelName = TTLocalizer.ShovelNameDict[shovel] + curShovelSkill = base.localAvatar.shovelSkill + maxShovelSkill = GardenGlobals.ShovelAttributes[shovel]['skillPts'] + if shovel == GardenGlobals.MAX_SHOVELS - 1: + maxShovelSkill -= 1 + wateringCan = base.localAvatar.wateringCan + wateringCanName = TTLocalizer.WateringCanNameDict[wateringCan] + curWateringCanSkill = base.localAvatar.wateringCanSkill + maxWateringCanSkill = GardenGlobals.WateringCanAttributes[wateringCan]['skillPts'] + if wateringCan == GardenGlobals.MAX_WATERING_CANS - 1: + maxWateringCanSkill -= 1 + textToUse = TTLocalizer.GardenPageShovelInfo % (shovelName, curShovelSkill, maxShovelSkill) + self.shovelBar['text'] = textToUse + self.shovelBar['value'] = float(curShovelSkill) / float(maxShovelSkill) * self.barLength + self.FUDGE_FACTOR + textToUse = TTLocalizer.GardenPageWateringCanInfo % (wateringCanName, curWateringCanSkill, maxWateringCanSkill) + self.wateringCanBar['text'] = textToUse + self.wateringCanBar['value'] = float(curWateringCanSkill) / float(maxWateringCanSkill) * self.barLength + self.FUDGE_FACTOR + else: + print 'no shovel bar' + if self.mode == GardenPage_Collection: + if hasattr(self, 'browser'): + self.browser.update() + elif self.mode == GardenPage_Basket: + if hasattr(self, 'picker'): + newBasketFlower = base.localAvatar.flowerBasket.getFlower() + self.picker.update(newBasketFlower) + elif self.mode == GardenPage_Trophy: + if hasattr(self, 'trophies'): + for trophy in self.trophies: + trophy.setLevel(-1) + + for trophyId in base.localAvatar.getGardenTrophies(): + self.trophies[trophyId].setLevel(trophyId) + + elif self.mode == GardenPage_Specials: + self.createGardenSpecialsList() + if not base.cr.playGame.getPlace().getState() == 'stickerBook': + self.specialsPhoto.hide() + + def destroy(self): + self.notify.debug('destroy') + self.useSpecialButton.destroy() + if hasattr(self, 'gardenSpecialsList'): + self.clearGS() + self.gardenSpecialsList.destroy() + self.ignoreAll() + self.cleanupResultDialog() + DirectFrame.destroy(self) + + def useSpecialDone(self, response): + stringToShow = '' + if response == 'success': + stringToShow = TTLocalizer.UseSpecialSuccess + elif response == 'badlocation': + stringToShow = TTLocalizer.UseSpecialBadLocation + else: + stringToShow = 'Unknown response %s' % response + self.resultDialog = TTDialog.TTDialog(parent=aspect2dp, style=TTDialog.Acknowledge, text=stringToShow, command=self.cleanupResultDialog) + + def cleanupResultDialog(self, value = None): + if self.resultDialog: + self.resultDialog.destroy() + self.resultDialog = None + self.useSpecialButton['state'] = DGG.NORMAL + return + + +class GardenTrophy(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('GardenTrophy') + + def __init__(self, level): + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(GardenTrophy) + self.trophy = loader.loadModel('phase_3.5/models/gui/fishingTrophy') + self.trophy.reparentTo(self) + self.trophy.setPos(0, 1, 0) + self.trophy.setScale(0.1) + self.base = self.trophy.find('**/trophyBase') + self.column = self.trophy.find('**/trophyColumn') + self.top = self.trophy.find('**/trophyTop') + self.topBase = self.trophy.find('**/trophyTopBase') + self.statue = self.trophy.find('**/trophyStatue') + self.base.setColorScale(1, 1, 0.8, 1) + self.bowl = loader.loadModel('phase_3.5/models/gui/fishingTrophyBowl') + self.bowl.reparentTo(self) + self.bowl.setPos(0, 1, 0) + self.bowl.setScale(2.0) + self.bowlTop = self.bowl.find('**/fishingTrophyGreyBowl') + self.bowlBase = self.bowl.find('**/fishingTrophyBase') + self.bowlBase.setScale(1.25, 1, 1) + self.bowlBase.setColorScale(1, 1, 0.8, 1) + self.nameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.15), text='Trophy Text', text_scale=0.125, text_fg=Vec4(0.9, 0.9, 0.4, 1)) + self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.shadow.reparentTo(self) + self.shadow.setColor(1, 1, 1, 0.2) + self.shadow.setPosHprScale(0, 1, 0.35, 0, 90, 0, 0.1, 0.14, 0.1) + self.setLevel(level) + return + + def setLevel(self, level): + self.level = level + order = ('C', 'D', 'B', 'A') + scales = (0.25, 0.25, 0.22, 0.25) + metalTrophy = ('wheelbarrel', 'shovels', 'flower', 'watering_can') + if self.level >= 0 and self.level < len(order): + modelStr = 'phase_5.5/models/estate/trophy' + modelStr += order[level] + self.gardenTrophy = loader.loadModel(modelStr) + self.gardenTrophy.setScale(scales[level]) + self.gardenTrophy.reparentTo(self) + self.metalTrophy = self.gardenTrophy.find('**/%s' % metalTrophy[level]) + if level == -1: + self.trophy.hide() + self.bowl.hide() + self.nameLabel.hide() + elif level == 0: + self.trophy.show() + self.trophy.hide() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 1.11878) + self.top.setPos(0, 0, -1) + self.__bronze() + elif level == 1: + self.trophy.show() + self.trophy.hide() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 1.61878) + self.top.setPos(0, 0, -0.5) + self.__bronze() + elif level == 2: + self.trophy.show() + self.trophy.hide() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 2.11878) + self.top.setPos(0, 0, 0) + self.__silver() + elif level == 3: + self.trophy.show() + self.trophy.hide() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 2.61878) + self.top.setPos(0, 0, 0.5) + self.__silver() + elif level == 4: + self.trophy.show() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 3.11878) + self.top.setPos(0, 0, 1) + self.__gold() + elif level == 5: + self.trophy.hide() + self.bowl.show() + self.bowlTop.setScale(1.75) + self.nameLabel.show() + self.__bronze() + elif level == 6: + self.trophy.hide() + self.bowl.show() + self.bowlTop.setScale(2.0) + self.nameLabel.show() + self.__silver() + elif level >= 7: + self.trophy.hide() + self.bowl.show() + self.bowlTop.setScale(2.25) + self.nameLabel.show() + self.__gold() + + def __bronze(self): + self.top.setColorScale(0.9, 0.6, 0.33, 1) + self.bowlTop.setColorScale(0.9, 0.6, 0.33, 1) + self.metalTrophy.setColorScale(0.9, 0.6, 0.33, 1) + + def __silver(self): + self.top.setColorScale(0.9, 0.9, 1, 1) + self.bowlTop.setColorScale(0.9, 0.9, 1, 1) + self.metalTrophy.setColorScale(0.9, 0.9, 1, 1) + + def __gold(self): + self.top.setColorScale(1, 0.95, 0.1, 1) + self.bowlTop.setColorScale(1, 0.95, 0.1, 1) + self.metalTrophy.setColorScale(1, 0.95, 0.1, 1) + + def destroy(self): + self.trophy.removeNode() + self.bowl.removeNode() + self.shadow.removeNode() + if hasattr(self, 'gardenTrophy'): + self.gardenTrophy.removeNode() + DirectFrame.destroy(self) diff --git a/toontown/shtiker/GolfPage.py b/toontown/shtiker/GolfPage.py new file mode 100755 index 00000000..e8675c56 --- /dev/null +++ b/toontown/shtiker/GolfPage.py @@ -0,0 +1,448 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.showbase import PythonUtil +from direct.task import Task +from toontown.fishing.FishPhoto import DirectRegion +from toontown.shtiker.ShtikerPage import ShtikerPage +from toontown.toonbase import ToontownGlobals, TTLocalizer +from FishPage import FishingTrophy +from toontown.golf import GolfGlobals +PageMode = PythonUtil.Enum('Records, Trophy') + +class GolfPage(ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('GolfPage') + + def __init__(self): + ShtikerPage.__init__(self) + self.avatar = None + self.mode = PageMode.Trophy + return + + def enter(self): + if not hasattr(self, 'title'): + self.load() + self.setMode(self.mode, 1) + ShtikerPage.enter(self) + + def exit(self): + self.golfTrophies.hide() + self.golfRecords.hide() + ShtikerPage.exit(self) + + def setAvatar(self, av): + self.avatar = av + + def getAvatar(self): + return self.avatar + + def load(self): + ShtikerPage.load(self) + self.golfRecords = GolfingRecordsUI(self.avatar, self) + self.golfRecords.hide() + self.golfRecords.load() + self.golfTrophies = GolfTrophiesUI(self.avatar, self) + self.golfTrophies.hide() + self.golfTrophies.load() + self.title = DirectLabel(parent=self, relief=None, text='', text_scale=0.1, pos=(0, 0, 0.65)) + normalColor = (1, 1, 1, 1) + clickColor = (0.8, 0.8, 0, 1) + rolloverColor = (0.15, 0.82, 1.0, 1) + diabledColor = (1.0, 0.98, 0.15, 1) + gui = loader.loadModel('phase_3.5/models/gui/fishingBook') + self.recordsTab = DirectButton(parent=self, relief=None, text=TTLocalizer.GolfPageRecordsTab, text_scale=TTLocalizer.GPrecordsTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[PageMode.Records], pos=TTLocalizer.GPrecordsTabPos) + self.trophyTab = DirectButton(parent=self, relief=None, text=TTLocalizer.GolfPageTrophyTab, text_scale=TTLocalizer.GPtrophyTab, text_pos=TTLocalizer.GPtrophyTabTextPos, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface3'), image_pos=(-0.28, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[PageMode.Trophy], pos=TTLocalizer.GPtrophyTabPos) + self.recordsTab.setPos(-0.13, 0, 0.775) + self.trophyTab.setPos(0.28, 0, 0.775) + adjust = -0.2 + self.recordsTab.setX(self.recordsTab.getX() + adjust) + self.trophyTab.setX(self.trophyTab.getX() + adjust) + gui.removeNode() + return + + def unload(self): + self.avatar = None + ShtikerPage.unload(self) + return + + def setMode(self, mode, updateAnyways = 0): + messenger.send('wakeup') + if not updateAnyways: + if self.mode == mode: + return + else: + self.mode = mode + if mode == PageMode.Records: + self.title['text'] = TTLocalizer.GolfPageTitleRecords + self.recordsTab['state'] = DGG.DISABLED + self.trophyTab['state'] = DGG.NORMAL + elif mode == PageMode.Trophy: + self.title['text'] = TTLocalizer.GolfPageTitleTrophy + self.recordsTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.DISABLED + else: + raise StandardError, 'GolfPage::setMode - Invalid Mode %s' % mode + self.updatePage() + + def updatePage(self): + if self.mode == PageMode.Records: + self.golfTrophies.hide() + self.golfRecords.show() + elif self.mode == PageMode.Trophy: + self.golfTrophies.show() + self.golfRecords.hide() + else: + raise StandardError, 'GolfPage::updatePage - Invalid Mode %s' % self.mode + + +class GolfingRecordsUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('GolfingRecordsUI') + + def __init__(self, avatar, parent = aspect2d): + self.avatar = avatar + self.bestDisplayList = [] + self.lastHoleBest = [] + self.lastCourseBest = [] + self.scrollList = None + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + self.gui.removeNode() + self.scrollList.destroy() + del self.avatar + del self.lastHoleBest + del self.lastCourseBest + del self.bestDisplayList + del self.scrollList + DirectFrame.destroy(self) + + def load(self): + self.gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.listXorigin = -0.5 + self.listFrameSizeX = 1.5 + self.listZorigin = -0.9 + self.listFrameSizeZ = 1.04 + self.arrowButtonScale = 1.3 + self.itemFrameXorigin = -0.237 + self.itemFrameZorigin = 0.365 + self.labelXstart = self.itemFrameXorigin + 0.293 + self.scrollList = DirectScrolledList(parent=self, relief=None, pos=(0, 0, 0), incButton_image=(self.gui.find('**/FndsLst_ScrollUp'), + self.gui.find('**/FndsLst_ScrollDN'), + self.gui.find('**/FndsLst_ScrollUp_Rllvr'), + self.gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_scale=(self.arrowButtonScale, self.arrowButtonScale, -self.arrowButtonScale), incButton_pos=(self.labelXstart, 0, self.itemFrameZorigin - 0.999), incButton_image3_color=Vec4(1, 1, 1, 0.2), decButton_image=(self.gui.find('**/FndsLst_ScrollUp'), + self.gui.find('**/FndsLst_ScrollDN'), + self.gui.find('**/FndsLst_ScrollUp_Rllvr'), + self.gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_scale=(self.arrowButtonScale, self.arrowButtonScale, self.arrowButtonScale), decButton_pos=(self.labelXstart, 0, self.itemFrameZorigin + 0.227), decButton_image3_color=Vec4(1, 1, 1, 0.2), itemFrame_pos=(self.itemFrameXorigin, 0, self.itemFrameZorigin), itemFrame_scale=1.0, itemFrame_relief=DGG.SUNKEN, itemFrame_frameSize=(self.listXorigin, + self.listXorigin + self.listFrameSizeX, + self.listZorigin, + self.listZorigin + self.listFrameSizeZ), itemFrame_frameColor=(0.85, 0.95, 1, 1), itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=12, forceHeight=0.083, items=[]) + for courseId in GolfGlobals.CourseInfo: + courseName = GolfGlobals.getCourseName(courseId) + frame = DirectFrame(parent=self.scrollList, relief=None) + courseNameDisplay = DirectLabel(parent=frame, relief=None, pos=(-0.475, 0, 0.05), text=courseName, text_align=TextNode.ALeft, text_scale=0.075, text_fg=(0.85, 0.64, 0.13, 1.0), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont()) + bestScoreDisplay = DirectLabel(parent=frame, relief=None, pos=(0.9, 0, 0.05), text=TTLocalizer.KartRace_Unraced, text_scale=0.06, text_fg=(0.0, 0.0, 0.0, 1.0), text_font=ToontownGlobals.getToonFont()) + self.bestDisplayList.append(bestScoreDisplay) + self.scrollList.addItem(frame) + + for holeId in GolfGlobals.HoleInfo: + holeName = GolfGlobals.getHoleName(holeId) + frame = DirectFrame(parent=self.scrollList, relief=None) + holeNameDisplay = DirectLabel(parent=frame, relief=None, pos=(-0.475, 0, 0.05), text=holeName, text_align=TextNode.ALeft, text_scale=0.075, text_fg=(0.95, 0.95, 0.0, 1.0), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont()) + bestScoreDisplay = DirectLabel(parent=frame, relief=None, pos=(0.9, 0, 0.05), text=TTLocalizer.KartRace_Unraced, text_scale=0.06, text_fg=(0.0, 0.0, 0.0, 1.0), text_font=ToontownGlobals.getToonFont()) + self.bestDisplayList.append(bestScoreDisplay) + self.scrollList.addItem(frame) + + return + + def show(self): + bestHoles = self.avatar.getGolfHoleBest() + bestCourses = self.avatar.getGolfCourseBest() + if bestHoles != self.lastHoleBest or bestCourses != self.lastCourseBest: + numCourse = len(GolfGlobals.CourseInfo.keys()) + numHoles = len(GolfGlobals.HoleInfo.keys()) + for i in xrange(numCourse): + score = bestCourses[i] + if score != 0: + self.bestDisplayList[i]['text'] = (str(score),) + else: + self.bestDisplayList[i]['text'] = TTLocalizer.KartRace_Unraced + + for i in xrange(numHoles): + score = bestHoles[i] + if score != 0: + self.bestDisplayList[i + numCourse]['text'] = str(score) + else: + self.bestDisplayList[i + numCourse]['text'] = TTLocalizer.KartRace_Unraced + + self.lastHoleBest = bestHoles[:] + self.lastCourseBest = bestCourses[:] + DirectFrame.show(self) + + def regenerateScrollList(self): + selectedIndex = 0 + if self.scrollList: + selectedIndex = self.scrollList.getSelectedIndex() + for label in self.bestDisplayList: + label.detachNode() + + self.scrollList.destroy() + self.scrollList = None + self.scrollList.scrollTo(selectedIndex) + return + + +class GolfTrophiesUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('GolfTrophiesUI') + + def __init__(self, avatar, parent = aspect2d): + self.avatar = avatar + self.trophyPanels = [] + self.cupPanels = [] + self.trophies = None + self.cups = None + self.trophyTextDisplay = None + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + for panel in self.trophyPanels: + panel.destroy() + + for panel in self.cupPanels: + panel.destroy() + + self.currentHistory.destroy() + self.trophyTextDisplay.destroy() + del self.avatar + del self.currentHistory + del self.trophyPanels + del self.trophies + del self.trophyTextDisplay + del self.cups + del self.cupPanels + DirectFrame.destroy(self) + + def load(self): + self.trophies = base.localAvatar.getGolfTrophies()[:] + self.cups = base.localAvatar.getGolfCups()[:] + xStart = -0.76 + yStart = 0.475 + xOffset = 0.17 + yOffset = 0.23 + for j in xrange(GolfGlobals.NumCups): + for i in xrange(GolfGlobals.TrophiesPerCup): + trophyPanel = DirectLabel(parent=self, relief=None, pos=(xStart + i * xOffset, 0.0, yStart - j * yOffset), state=DGG.NORMAL, image=DGG.getDefaultDialogGeom(), image_scale=(0.75, 1, 1), image_color=(0.8, 0.8, 0.8, 1), text=TTLocalizer.SuitPageMystery[0], text_scale=0.45, text_fg=(0, 0, 0, 1), text_pos=(0, 0, -0.25), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=5.5) + trophyPanel.scale = 0.2 + trophyPanel.setScale(trophyPanel.scale) + self.trophyPanels.append(trophyPanel) + + xStart = -0.25 + yStart = -0.38 + xOffset = 0.25 + for i in xrange(GolfGlobals.NumCups): + cupPanel = DirectLabel(parent=self, relief=None, pos=(xStart + i * xOffset, 0.0, yStart), state=DGG.NORMAL, image=DGG.getDefaultDialogGeom(), image_scale=(0.75, 1, 1), image_color=(0.8, 0.8, 0.8, 1), text=TTLocalizer.SuitPageMystery[0], text_scale=0.45, text_fg=(0, 0, 0, 1), text_pos=(0, 0, -0.25), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=5.5) + cupPanel.scale = 0.3 + cupPanel.setScale(cupPanel.scale) + self.cupPanels.append(cupPanel) + + self.currentHistory = DirectLabel(parent=self, relief=None, text='', text_scale=0.05, text_fg=(0, 0, 0.95, 1.0), text_pos=(0, -0.65)) + self.trophyTextDisplay = DirectLabel(parent=self, relief=None, text='', text_scale=0.07, text_fg=(1, 0, 0, 1), text_shadow=(0, 0, 0, 0), text_pos=(0.0, -0.175), text_font=ToontownGlobals.getInterfaceFont()) + self.updateTrophies() + return + + def grow(self, index, pos): + self.trophyPanels[index]['image_color'] = Vec4(1.0, 1.0, 0.8, 1.0) + if index < GolfGlobals.NumTrophies: + self.trophyTextDisplay['text'] = TTLocalizer.GolfTrophyTextDisplay % {'number': index + 1, + 'desc': TTLocalizer.GolfTrophyDescriptions[index]} + historyIndex = GolfGlobals.getHistoryIndexForTrophy(index) + if historyIndex >= 0: + self.currentHistory['text'] = TTLocalizer.GolfCurrentHistory % {'historyDesc': TTLocalizer.GolfHistoryDescriptions[historyIndex], + 'num': self.avatar.getGolfHistory()[historyIndex]} + + def shrink(self, index, pos): + self.trophyPanels[index]['image_color'] = Vec4(1.0, 1.0, 1.0, 1.0) + self.trophyTextDisplay['text'] = '' + self.currentHistory['text'] = '' + + def growCup(self, index, pos): + self.cupPanels[index]['image_color'] = Vec4(1.0, 1.0, 0.8, 1.0) + if index < GolfGlobals.NumTrophies: + self.trophyTextDisplay['text'] = TTLocalizer.GolfCupTextDisplay % {'number': index + 1, + 'desc': TTLocalizer.GolfCupDescriptions[index]} + + def shrinkCup(self, index, pos): + self.cupPanels[index]['image_color'] = Vec4(1.0, 1.0, 1.0, 1.0) + self.trophyTextDisplay['text'] = '' + + def show(self): + self.currentHistory['text'] = '' + if self.trophies != base.localAvatar.getGolfTrophies(): + self.trophies = base.localAvatar.getGolfTrophies() + self.cups = base.localAvatar.getGolfCups() + self.updateTrophies() + DirectFrame.show(self) + + def updateTrophies(self): + for t in xrange(len(self.trophyPanels)): + if self.trophies[t]: + trophyPanel = self.trophyPanels[t] + trophyPanel['text'] = '' + golfTrophy = trophyPanel.find('**/*GolfTrophy*') + if golfTrophy.isEmpty(): + trophyModel = GolfTrophy(t) + trophyModel.reparentTo(trophyPanel) + trophyModel.nameLabel.hide() + trophyModel.setPos(0, 0, -0.4) + trophyPanel['image_color'] = Vec4(1.0, 1.0, 1.0, 1.0) + trophyPanel.bind(DGG.ENTER, self.grow, extraArgs=[t]) + trophyPanel.bind(DGG.EXIT, self.shrink, extraArgs=[t]) + else: + trophyPanel = self.trophyPanels[t] + toBeNukedGolfTrophy = trophyPanel.find('**/*GolfTrophy*') + if not toBeNukedGolfTrophy.isEmpty(): + toBeNukedGolfTrophy.removeNode() + trophyPanel['text'] = TTLocalizer.SuitPageMystery[0] + trophyPanel['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + trophyPanel.unbind(DGG.ENTER) + trophyPanel.unbind(DGG.EXIT) + + for t in xrange(len(self.cupPanels)): + if self.cups[t]: + cupPanel = self.cupPanels[t] + cupPanel['text'] = '' + cupTrophy = cupPanel.find('**/*GolfTrophy*') + if cupTrophy.isEmpty(): + cupModel = GolfTrophy(t + GolfGlobals.NumTrophies) + cupModel.reparentTo(cupPanel) + cupModel.nameLabel.hide() + cupModel.setPos(0, 0, -0.4) + cupPanel['image_color'] = Vec4(1.0, 1.0, 1.0, 1.0) + cupPanel.bind(DGG.ENTER, self.growCup, extraArgs=[t]) + cupPanel.bind(DGG.EXIT, self.shrinkCup, extraArgs=[t]) + else: + cupPanel = self.cupPanels[t] + toBeNukedGolfCup = cupPanel.find('**/*GolfTrophy*') + if not toBeNukedGolfCup.isEmpty(): + toBeNukedGolfCup.removeNode() + cupPanel['text'] = TTLocalizer.SuitPageMystery[0] + cupPanel['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + cupPanel.unbind(DGG.ENTER) + cupPanel.unbind(DGG.EXIT) + + +class GolfTrophy(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('GolfTrophy') + + def __init__(self, level, *args, **kwargs): + opts = {'relief': None} + opts.update(kwargs) + DirectFrame.__init__(self, *args, **opts) + self.trophy = loader.loadModel('phase_6/models/golf/golfTrophy') + self.trophy.reparentTo(self) + self.trophy.setPos(0, 1, 0) + self.trophy.setScale(0.1) + self.base = self.trophy.find('**/trophyBase') + self.column = self.trophy.find('**/trophyColumn') + self.top = self.trophy.find('**/trophyTop') + self.topBase = self.trophy.find('**/trophyTopBase') + self.statue = self.trophy.find('**/trophyStatue') + self.base.setColorScale(1, 1, 0.8, 1) + self.topBase.setColorScale(1, 1, 0.8, 1) + self.greyBowl = loader.loadModel('phase_6/models/gui/racingTrophyBowl2') + self.greyBowl.reparentTo(self) + self.greyBowl.setPos(0, 0.5, 0) + self.greyBowl.setScale(2.0) + self.goldBowl = loader.loadModel('phase_6/models/gui/racingTrophyBowl') + self.goldBowl.reparentTo(self) + self.goldBowl.setPos(0, 0.5, 0) + self.goldBowl.setScale(2.0) + self.goldBowlBase = self.goldBowl.find('**/fishingTrophyBase') + self.goldBowlBase.hide() + self.nameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.15), text='', text_scale=0.125, text_fg=Vec4(0.9, 0.9, 0.4, 1)) + self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.shadow.reparentTo(self) + self.shadow.setColor(1, 1, 1, 0.2) + self.shadow.setPosHprScale(0, 1, 0.35, 0, 90, 0, 0.1, 0.14, 0.1) + self.setLevel(level) + return + + def setLevel(self, level): + self.level = level + if level == -1: + self.trophy.hide() + self.greyBowl.hide() + self.goldBowl.hide() + self.nameLabel.hide() + else: + self.nameLabel.show() + if level >= 30: + self.trophy.hide() + self.greyBowl.hide() + self.goldBowl.show() + self.goldBowlBase.hide() + else: + self.trophy.show() + self.goldBowl.hide() + self.greyBowl.hide() + if level == 30: + self.goldBowl.setScale(4.4, 3.1, 3.1) + elif level == 31: + self.goldBowl.setScale(3.6, 3.5, 3.5) + elif level >= 32: + self.goldBowl.setScale(5.6, 3.9, 3.9) + if level % 3 == 0: + self.column.setScale(1.3229, 1.26468, 1.11878) + self.top.setPos(0, 0, -1) + self.__bronze() + elif level % 3 == 1: + self.column.setScale(1.3229, 1.26468, 1.61878) + self.top.setPos(0, 0, -.5) + self.__silver() + elif level % 3 == 2: + self.column.setScale(1.3229, 1.26468, 2.11878) + self.top.setPos(0, 0, 0) + self.__gold() + if level < 10: + self.__tealColumn() + elif level < 20: + self.__purpleColumn() + elif level < 30: + self.__blueColumn() + else: + self.__redColumn() + + def __bronze(self): + self.statue.setColorScale(0.9, 0.6, 0.33, 1) + + def __silver(self): + self.statue.setColorScale(0.9, 0.9, 1, 1) + + def __gold(self): + self.statue.setColorScale(1, 0.95, 0.1, 1) + + def __platinum(self): + self.statue.setColorScale(1, 0.95, 0.1, 1) + + def __tealColumn(self): + self.column.setColorScale(0.5, 1.2, 0.85, 1) + + def __purpleColumn(self): + self.column.setColorScale(1, 0.7, 0.95, 1) + + def __redColumn(self): + self.column.setColorScale(1.2, 0.6, 0.6, 1) + + def __yellowColumn(self): + self.column.setColorScale(1, 1, 0.8, 1) + + def __blueColumn(self): + self.column.setColorScale(0.6, 0.75, 1.2, 1) + + def destroy(self): + self.trophy.removeNode() + self.goldBowl.removeNode() + self.greyBowl.removeNode() + self.shadow.removeNode() + DirectFrame.destroy(self) diff --git a/toontown/shtiker/GroupPage.py b/toontown/shtiker/GroupPage.py new file mode 100644 index 00000000..c76696df --- /dev/null +++ b/toontown/shtiker/GroupPage.py @@ -0,0 +1,34 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import TTLocalizerEnglish +from ShtikerPage import ShtikerPage + +class GroupPage(ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('GroupPage') + + def __init__(self): + ShtikerPage.__init__(self) + + def load(self): + ShtikerPage.load(self) + + def unload(self): + SktikerPage.unload(self) + + def enter(self): + ShtikerPage.enter(self) + + def exit(self): + ShtikerPage.exit(self) + + def destroy(self): + DirectFrame.destroy(self) + + def cleanupDialog(self, value=0): + self.confirmDialog.cleanup() + + def create(self): + self.background = OnscreenImage(parent = render2d, image="phase_3.5/map/toontown_central_tutorial_palette_4amla_1.jpg") diff --git a/toontown/shtiker/InventoryPage.py b/toontown/shtiker/InventoryPage.py new file mode 100755 index 00000000..247918f2 --- /dev/null +++ b/toontown/shtiker/InventoryPage.py @@ -0,0 +1,223 @@ +import ShtikerPage, DisguisePage +from toontown.toonbase import ToontownBattleGlobals +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.coghq import CogDisguiseGlobals +from toontown.suit import SuitDNA + +class InventoryPage(ShtikerPage.ShtikerPage): + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.meritBars = [] + self.currentTrackInfo = None + self.onscreen = 0 + self.lastInventoryTime = globalClock.getRealTime() + + def load(self): + ShtikerPage.ShtikerPage.load(self) + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.InventoryPageTitle, text_scale=0.12, textMayChange=1, pos=(0, 0, 0.62)) + self.gagFrame = DirectFrame(parent=self, relief=None, pos=(-0.05, 0, -0.47), scale=(0.35, 0.35, 0.35), geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor) + self.trackInfo = DirectFrame(parent=self, relief=None, pos=(-0.55, 0, -0.47), scale=(0.35, 0.35, 0.35), geom=DGG.getDefaultDialogGeom(), geom_scale=(1.4, 1, 1), geom_color=ToontownGlobals.GlobalDialogColor, text='', text_wordwrap=11, text_align=TextNode.ALeft, text_scale=0.12, text_pos=(-0.65, 0.3), text_fg=(0.05, 0.14, 0.4, 1)) + self.trackProgress = DirectWaitBar(parent=self.trackInfo, pos=(0, 0, -0.2), relief=DGG.SUNKEN, frameSize=(-0.6, + 0.6, + -0.1, + 0.1), borderWidth=(0.025, 0.025), scale=1.1, frameColor=(0.4, 0.6, 0.4, 1), barColor=(0.9, 1, 0.7, 1), text='0/0', text_scale=0.15, text_fg=(0.05, 0.14, 0.4, 1), text_align=TextNode.ACenter, text_pos=(0, -0.22)) + self.trackProgress.hide() + jarGui = loader.loadModel('phase_3.5/models/gui/jar_gui') + catalogGui = loader.loadModel('phase_5.5/models/gui/catalog_gui') + self.moneyDisplay = DirectLabel(parent=self, relief=None, pos=(0.35, 0, -0.5), scale=0.8, text=str(base.localAvatar.getMoney()), text_scale=0.18, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0, -0.1, 0), image=jarGui.find('**/Jar'), text_font=ToontownGlobals.getSignFont()) + self.bankMoneyDisplay = DirectLabel(self, relief=None, pos=(0.35, 0, -0.1), scale=0.6, image=catalogGui.find('**/bean_bank'), text=str(base.localAvatar.getBankMoney()), text_align=TextNode.ARight, text_scale=0.11, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0.75, -0.81), text_font=ToontownGlobals.getSignFont()) + self.createMeritBars() + jarGui.removeNode() + catalogGui.removeNode() + + def unload(self): + self.ignoreAll() + del self.title + self.destroyMeritBars() + ShtikerPage.ShtikerPage.unload(self) + + def createMeritBars(self): + if self.meritBars: + return + + for i in xrange(len(SuitDNA.suitDepts)): + self.meritBars.append(DirectWaitBar(parent=self.trackInfo, relief=DGG.SUNKEN, frameSize=(-1, 1, -0.15, 0.15), + borderWidth=(0.02, 0.02), scale=0.65, text='', text_scale=0.18, text_fg=(0, 0, 0, 1), text_align=TextNode.ALeft, + text_pos=(-0.96, -0.05), pos=(0, 0, 0.365 - 0.24 * i), frameColor=(DisguisePage.DeptColors[i][0] * 0.7, + DisguisePage.DeptColors[i][1] * 0.7, DisguisePage.DeptColors[i][2] * 0.7, 1), barColor=(DisguisePage.DeptColors[i][0] * 0.8, + DisguisePage.DeptColors[i][1] * 0.8, DisguisePage.DeptColors[i][2] * 0.8, 1))) + + self.accept(localAvatar.uniqueName('cogMeritsChange'), self.updateMeritBars) + self.updateMeritBars() + + def destroyMeritBars(self): + if not self.meritBars: + return + + for meritBar in self.meritBars: + meritBar.destroy() + + self.meritBars = [] + + def changeMeritBars(self, hide): + if not self.meritBars: + return + + for i in xrange(len(self.meritBars)): + meritBar = self.meritBars[i] + + if CogDisguiseGlobals.isSuitComplete(base.localAvatar.cogParts, i): + meritBar.hide() if hide else meritBar.show() + else: + meritBar.hide() + + def updateMeritBars(self): + if not self.meritBars: + return + + for i in xrange(len(self.meritBars)): + meritBar = self.meritBars[i] + + if CogDisguiseGlobals.isSuitComplete(base.localAvatar.cogParts, i): + meritBar.show() + totalMerits = CogDisguiseGlobals.getTotalMerits(base.localAvatar, i) + merits = base.localAvatar.cogMerits[i] + + if totalMerits: + meritBar['range'] = totalMerits + meritBar['value'] = merits + + if merits == totalMerits: + meritBar['text'] = TTLocalizer.RewardPanelMeritAlert + else: + meritBar['text'] = '%s/%s %s' % (merits, totalMerits, TTLocalizer.RewardPanelMeritBarLabels[i]) + else: + meritBar['range'] = 1 + meritBar['value'] = 1 + meritBar['text'] = TTLocalizer.RewardPanelMeritsMaxed + else: + meritBar.hide() + + def __moneyChange(self, money): + self.moneyDisplay['text'] = str(money) + + def __bankMoneyChange(self, bankMoney): + self.bankMoneyDisplay['text'] = str(bankMoney) + + def enter(self): + ShtikerPage.ShtikerPage.enter(self) + base.localAvatar.inventory.setActivateMode('book') + base.localAvatar.inventory.show() + base.localAvatar.inventory.reparentTo(self) + self.__moneyChange(base.localAvatar.getMoney()) + self.__bankMoneyChange(base.localAvatar.getBankMoney()) + self.accept('enterTrackFrame', self.updateTrackInfo) + self.accept('exitTrackFrame', self.clearTrackInfo) + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.accept(localAvatar.uniqueName('bankMoneyChange'), self.__bankMoneyChange) + + def exit(self): + ShtikerPage.ShtikerPage.exit(self) + self.clearTrackInfo(self.currentTrackInfo) + self.ignore('enterTrackFrame') + self.ignore('exitTrackFrame') + self.ignore(localAvatar.uniqueName('moneyChange')) + self.ignore(localAvatar.uniqueName('bankMoneyChange')) + self.makePageWhite(None) + base.localAvatar.inventory.hide() + base.localAvatar.inventory.reparentTo(hidden) + + def updateTrackInfo(self, trackIndex): + self.currentTrackInfo = trackIndex + trackName = TextEncoder.upper(ToontownBattleGlobals.Tracks[trackIndex]) + self.changeMeritBars(True) + self.trackInfo.show() + if base.localAvatar.hasTrackAccess(trackIndex): + curExp, nextExp = base.localAvatar.inventory.getCurAndNextExpValues(trackIndex) + trackText = '%s / %s' % (curExp, nextExp) + self.trackProgress['range'] = nextExp + self.trackProgress['value'] = curExp + if curExp >= ToontownBattleGlobals.regMaxSkill: + str = TTLocalizer.InventoryPageTrackFull % trackName + trackText = TTLocalizer.InventoryUberTrackExp % {'nextExp': ToontownBattleGlobals.MaxSkill - curExp} + self.trackProgress['range'] = ToontownBattleGlobals.UberSkill + uberCurrExp = curExp - ToontownBattleGlobals.regMaxSkill + self.trackProgress['value'] = uberCurrExp + else: + morePoints = nextExp - curExp + if morePoints == 1: + str = TTLocalizer.InventoryPageSinglePoint % {'trackName': trackName, + 'numPoints': morePoints} + else: + str = TTLocalizer.InventoryPagePluralPoints % {'trackName': trackName, + 'numPoints': morePoints} + self.trackInfo['text'] = str + self.trackProgress['text'] = trackText + self.trackProgress['frameColor'] = (ToontownBattleGlobals.TrackColors[trackIndex][0] * 0.6, + ToontownBattleGlobals.TrackColors[trackIndex][1] * 0.6, + ToontownBattleGlobals.TrackColors[trackIndex][2] * 0.6, + 1) + self.trackProgress['barColor'] = (ToontownBattleGlobals.TrackColors[trackIndex][0], + ToontownBattleGlobals.TrackColors[trackIndex][1], + ToontownBattleGlobals.TrackColors[trackIndex][2], + 1) + self.trackProgress.show() + else: + str = TTLocalizer.InventoryPageNoAccess % trackName + self.trackInfo['text'] = str + self.trackProgress.hide() + + def clearTrackInfo(self, trackIndex): + if self.currentTrackInfo == trackIndex: + self.trackInfo['text'] = '' + self.trackProgress.hide() + self.currentTrackInfo = None + self.changeMeritBars(False) + + def acceptOnscreenHooks(self): + self.accept(ToontownGlobals.InventoryHotkeyOn, self.showInventoryOnscreen) + self.accept(ToontownGlobals.InventoryHotkeyOff, self.hideInventoryOnscreen) + + def ignoreOnscreenHooks(self): + self.ignore(ToontownGlobals.InventoryHotkeyOn) + self.ignore(ToontownGlobals.InventoryHotkeyOff) + + def showInventoryOnscreen(self): + messenger.send('wakeup') + timedif = globalClock.getRealTime() - self.lastInventoryTime + if timedif < 0.7: + return + self.lastInventoryTime = globalClock.getRealTime() + if self.onscreen or base.localAvatar.questPage.onscreen: + return + self.onscreen = 1 + base.localAvatar.inventory.setActivateMode('book') + base.localAvatar.inventory.show() + base.localAvatar.inventory.reparentTo(self) + self.__moneyChange(base.localAvatar.getMoney()) + self.__bankMoneyChange(base.localAvatar.getBankMoney()) + self.accept('enterTrackFrame', self.updateTrackInfo) + self.accept('exitTrackFrame', self.clearTrackInfo) + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.accept(localAvatar.uniqueName('bankMoneyChange'), self.__bankMoneyChange) + self.reparentTo(aspect2d) + self.title.hide() + self.show() + + def hideInventoryOnscreen(self): + if not self.onscreen: + return + self.onscreen = 0 + self.ignore('enterTrackFrame') + self.ignore('exitTrackFrame') + self.ignore(localAvatar.uniqueName('moneyChange')) + self.ignore(localAvatar.uniqueName('bankMoneyChange')) + base.localAvatar.inventory.hide() + base.localAvatar.inventory.reparentTo(hidden) + self.reparentTo(self.book) + self.title.show() + self.hide() diff --git a/toontown/shtiker/KartPage.py b/toontown/shtiker/KartPage.py new file mode 100755 index 00000000..a226868a --- /dev/null +++ b/toontown/shtiker/KartPage.py @@ -0,0 +1,1047 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.showbase import PythonUtil +from direct.task import Task +from toontown.fishing.FishPhoto import DirectRegion +from toontown.racing.KartDNA import * +from toontown.racing.Kart import Kart +from toontown.racing import RaceGlobals +from toontown.shtiker.ShtikerPage import ShtikerPage +from toontown.toonbase import ToontownGlobals, TTLocalizer +from FishPage import FishingTrophy +PageMode = PythonUtil.Enum('Customize, Records, Trophy') + +class KartPage(ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('KartPage') + + def __init__(self): + ShtikerPage.__init__(self) + self.avatar = None + self.mode = PageMode.Customize + return + + def enter(self): + if not hasattr(self, 'title'): + self.load() + self.setMode(self.mode, 1) + ShtikerPage.enter(self) + + def exit(self): + self.kartCustomizer.hide() + self.racingTrophies.hide() + self.racingRecords.hide() + ShtikerPage.exit(self) + + def setAvatar(self, av): + self.avatar = av + + def getAvatar(self): + return self.avatar + + def load(self): + ShtikerPage.load(self) + self.kartCustomizer = KartCustomizeUI(self.avatar, self) + self.kartCustomizer.hide() + self.kartCustomizer.load() + self.racingRecords = RacingRecordsUI(self.avatar, self) + self.racingRecords.hide() + self.racingRecords.load() + self.racingTrophies = RacingTrophiesUI(self.avatar, self) + self.racingTrophies.hide() + self.racingTrophies.load() + self.title = DirectLabel(parent=self, relief=None, text='', text_scale=0.1, pos=(0, 0, 0.65)) + normalColor = (1, 1, 1, 1) + clickColor = (0.8, 0.8, 0, 1) + rolloverColor = (0.15, 0.82, 1.0, 1) + diabledColor = (1.0, 0.98, 0.15, 1) + gui = loader.loadModel('phase_3.5/models/gui/fishingBook') + self.customizeTab = DirectButton(parent=self, relief=None, text=TTLocalizer.KartPageCustomizeTab, text_scale=TTLocalizer.KPkartTab, text_align=TextNode.ALeft, text_pos=(-0.025, 0.0, 0.0), image=gui.find('**/tabs/polySurface1'), image_pos=(0.55, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[PageMode.Customize], pos=(0.92, 0, 0.55)) + self.recordsTab = DirectButton(parent=self, relief=None, text=TTLocalizer.KartPageRecordsTab, text_scale=TTLocalizer.KPkartTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[PageMode.Records], pos=(0.92, 0, 0.1)) + self.trophyTab = DirectButton(parent=self, relief=None, text=TTLocalizer.KartPageTrophyTab, text_scale=TTLocalizer.KPkartTab, text_pos=(0.03, 0.0, 0.0), text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface3'), image_pos=(-0.28, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[PageMode.Trophy], pos=(0.92, 0, -0.3)) + self.customizeTab.setPos(-0.55, 0, 0.775) + self.recordsTab.setPos(-0.13, 0, 0.775) + self.trophyTab.setPos(0.28, 0, 0.775) + gui.removeNode() + return + + def unload(self): + ShtikerPage.unload(self) + + def setMode(self, mode, updateAnyways = 0): + messenger.send('wakeup') + if not updateAnyways: + if self.mode == mode: + return + else: + self.mode = mode + if mode == PageMode.Customize: + self.title['text'] = TTLocalizer.KartPageTitleCustomize + self.customizeTab['state'] = DGG.DISABLED + self.recordsTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.NORMAL + elif mode == PageMode.Records: + self.title['text'] = TTLocalizer.KartPageTitleRecords + self.customizeTab['state'] = DGG.NORMAL + self.recordsTab['state'] = DGG.DISABLED + self.trophyTab['state'] = DGG.NORMAL + elif mode == PageMode.Trophy: + self.title['text'] = TTLocalizer.KartPageTitleTrophy + self.customizeTab['state'] = DGG.NORMAL + self.recordsTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.DISABLED + else: + raise StandardError, 'KartPage::setMode - Invalid Mode %s' % mode + self.updatePage() + + def updatePage(self): + if self.mode == PageMode.Customize: + self.kartCustomizer.show() + self.racingTrophies.hide() + self.racingRecords.hide() + elif self.mode == PageMode.Records: + self.kartCustomizer.hide() + self.racingTrophies.hide() + self.racingRecords.show() + elif self.mode == PageMode.Trophy: + self.kartCustomizer.hide() + self.racingTrophies.show() + self.racingRecords.hide() + else: + raise StandardError, 'KartPage::updatePage - Invalid Mode %s' % self.mode + + +class KartCustomizeUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartCustomizeUI') + + def __init__(self, avatar, parent = aspect2d): + self.avatar = avatar + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + self.itemSelector.destroy() + self.kartViewer.destroy() + del self.avatar + del self.itemSelector + del self.kartViewer + DirectFrame.destroy(self) + + def load(self): + uiRootNode = loader.loadModel('phase_6/models/gui/ShtikerBookUI') + self.itemSelector = ItemSelector(self.avatar, parent=self) + self.itemSelector.setPos(uiRootNode.find('**/uiAccessoryIcons').getPos()) + self.itemSelector.load(uiRootNode) + self.kartViewer = KartViewer(list(self.avatar.getKartDNA()), parent=self) + self.kartViewer.setPos(uiRootNode.find('**/uiKartView').getPos()) + self.kartViewer.load(uiRootNode, 'uiKartViewerFrame1', ['rotate_right_up', + 'rotate_right_down', + 'rotate_right_roll', + 'rotate_right_down', + (0.275, -.08)], ['rotate_left_up', + 'rotate_left_down', + 'rotate_left_roll', + 'rotate_left_down', + (-.27, -.08)], (0, -.08)) + self.kartViewer.uiRotateLeft.setZ(-.25) + self.kartViewer.uiRotateRight.setZ(-.25) + self.itemSelector.itemViewers['main'].leftArrowButton.setZ(self.kartViewer.getZ() + 0.25) + self.itemSelector.itemViewers['main'].rightArrowButton.setZ(self.kartViewer.getZ() + 0.25) + self.kartViewer.setBounds(-0.38, 0.38, -0.25, 0.325) + self.kartViewer.setBgColor(1.0, 1.0, 0.8, 1.0) + uiRootNode.removeNode() + + def getKartViewer(self): + return self.kartViewer + + def show(self): + self.itemSelector.itemViewers['main'].initUpdatedDNA() + self.itemSelector.setupAccessoryIcons() + self.itemSelector.show() + self.kartViewer.show(list(self.avatar.getKartDNA())) + DirectFrame.show(self) + + def hide(self): + if hasattr(self, 'itemSelector'): + if hasattr(self.itemSelector.itemViewers['main'], 'updatedDNA'): + self.itemSelector.itemViewers['main'].setUpdatedDNA() + self.itemSelector.resetAccessoryIcons() + if hasattr(self.itemSelector.itemViewers['main'], 'confirmDlg'): + self.itemSelector.itemViewers['main'].confirmDlg.hide() + self.itemSelector.hide() + if hasattr(self, 'kartViewer'): + self.kartViewer.hide() + DirectFrame.hide(self) + + +class RacingRecordsUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('RacingRecordsUI') + + def __init__(self, avatar, parent = aspect2d): + self.avatar = avatar + self.timeDisplayList = [] + self.lastTimes = [] + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + del self.avatar + del self.lastTimes + del self.timeDisplayList + DirectFrame.destroy(self) + + def load(self): + offset = 0 + trackNameArray = TTLocalizer.KartRace_TrackNames + for trackId in RaceGlobals.TrackIds: + trackName = trackNameArray[trackId] + trackNameDisplay = DirectLabel(parent=self, relief=None, text=trackName, text_align=TextNode.ALeft, text_scale=0.075, text_fg=(0.95, 0.95, 0.0, 1.0), text_shadow=(0, 0, 0, 1), text_pos=(-0.8, 0.5 - offset), text_font=ToontownGlobals.getSignFont()) + bestTimeDisplay = DirectLabel(parent=self, relief=None, text=TTLocalizer.KartRace_Unraced, text_scale=0.06, text_fg=(0.0, 0.0, 0.0, 1.0), text_pos=(0.65, 0.5 - offset), text_font=ToontownGlobals.getToonFont()) + offset += 0.1 + self.timeDisplayList.append(bestTimeDisplay) + + return + + def show(self): + bestTimes = self.avatar.getKartingPersonalBestAll() + if bestTimes != self.lastTimes: + for i in xrange(0, len(bestTimes)): + time = bestTimes[i] + if time != 0.0: + whole, part = divmod(time, 1) + min, sec = divmod(whole, 60) + timeText = '%02d:%02d:%02d' % (min, sec, part * 100) + self.timeDisplayList[i]['text'] = (timeText,) + + self.lastTimes = bestTimes + DirectFrame.show(self) + + +class RacingTrophiesUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('RacingTrophiesUI') + + def __init__(self, avatar, parent = aspect2d): + self.avatar = avatar + self.trophyPanels = [] + self.trophies = None + self.trophyTextDisplay = None + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + for panel in self.trophyPanels: + panel.destroy() + + self.ticketDisplay.destroy() + self.trophyTextDisplay.destroy() + del self.avatar + del self.ticketDisplay + del self.trophyPanels + del self.trophies + del self.trophyTextDisplay + DirectFrame.destroy(self) + + def load(self): + self.trophies = base.localAvatar.getKartingTrophies() + xStart = -0.76 + yStart = 0.475 + xOffset = 0.17 + yOffset = 0.23 + for j in xrange(RaceGlobals.NumCups): + for i in xrange(RaceGlobals.TrophiesPerCup): + trophyPanel = DirectLabel(parent=self, relief=None, pos=(xStart + i * xOffset, 0.0, yStart - j * yOffset), state=DGG.NORMAL, image=DGG.getDefaultDialogGeom(), image_scale=(0.75, 1, 1), image_color=(0.8, 0.8, 0.8, 1), text=TTLocalizer.SuitPageMystery[0], text_scale=0.45, text_fg=(0, 0, 0, 1), text_pos=(0, 0, -0.25), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=5.5) + trophyPanel.scale = 0.2 + trophyPanel.setScale(trophyPanel.scale) + self.trophyPanels.append(trophyPanel) + + xStart = -0.25 + yStart = -0.38 + xOffset = 0.25 + for i in xrange(RaceGlobals.NumCups): + cupPanel = DirectLabel(parent=self, relief=None, pos=(xStart + i * xOffset, 0.0, yStart), state=DGG.NORMAL, image=DGG.getDefaultDialogGeom(), image_scale=(0.75, 1, 1), image_color=(0.8, 0.8, 0.8, 1), text=TTLocalizer.SuitPageMystery[0], text_scale=0.45, text_fg=(0, 0, 0, 1), text_pos=(0, 0, -0.25), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=5.5) + cupPanel.scale = 0.3 + cupPanel.setScale(cupPanel.scale) + self.trophyPanels.append(cupPanel) + + self.ticketDisplay = DirectLabel(parent=self, relief=None, image=loader.loadModel('phase_6/models/karting/tickets'), image_pos=(0.2, 0, -0.635), image_scale=0.2, text=TTLocalizer.KartPageTickets + str(self.avatar.getTickets()), text_scale=0.07, text_fg=(0, 0, 0.95, 1.0), text_pos=(0, -0.65), text_font=ToontownGlobals.getSignFont()) + self.trophyTextDisplay = DirectLabel(parent=self, relief=None, text='', text_scale=0.07, text_fg=(1, 0, 0, 1), text_shadow=(0, 0, 0, 0), text_pos=(0.0, -0.175), text_font=ToontownGlobals.getInterfaceFont()) + self.updateTrophies() + return + + def grow(self, index, pos): + self.trophyPanels[index]['image_color'] = Vec4(1.0, 1.0, 0.8, 1.0) + self.trophyTextDisplay['text'] = TTLocalizer.KartPageTrophyDetail % (index + 1, TTLocalizer.KartTrophyDescriptions[index]) + + def shrink(self, index, pos): + self.trophyPanels[index]['image_color'] = Vec4(1.0, 1.0, 1.0, 1.0) + self.trophyTextDisplay['text'] = '' + + def show(self): + self.ticketDisplay['text'] = (TTLocalizer.KartPageTickets + str(self.avatar.getTickets()),) + if self.trophies != base.localAvatar.getKartingTrophies(): + self.trophies = base.localAvatar.getKartingTrophies() + self.updateTrophies() + DirectFrame.show(self) + + def updateTrophies(self): + for t in xrange(len(self.trophyPanels)): + if self.trophies[t]: + trophyPanel = self.trophyPanels[t] + trophyPanel['text'] = '' + trophyModel = RacingTrophy(t) + trophyModel.reparentTo(trophyPanel) + trophyModel.nameLabel.hide() + trophyModel.setPos(0, 0, -0.4) + trophyPanel['image_color'] = Vec4(1.0, 1.0, 1.0, 1.0) + trophyPanel.bind(DGG.ENTER, self.grow, extraArgs=[t]) + trophyPanel.bind(DGG.EXIT, self.shrink, extraArgs=[t]) + + +class ItemSelector(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('ItemSelector') + + class ItemViewer(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('ItemViewer') + + def __init__(self, avatar, parent = aspect2d): + self.currItem = None + self.itemList = None + self.parent = parent + self.avatar = avatar + self.currAccessoryType = None + self.texCount = 1 + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0, 0, 0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + self.uiBgFrame.destroy() + self.uiImagePlane.destroy() + self.uiTextBox.destroy() + self.leftArrowButton.destroy() + self.rightArrowButton.destroy() + del self.avatar + del self.parent + del self.currItem + del self.itemList + del self.uiBgFrame + del self.uiImagePlane + del self.uiTextBox + del self.leftArrowButton + del self.rightArrowButton + del self.deleteButton + DirectFrame.destroy(self) + + def setCurrentItem(self, currItem): + self.currItem = currItem + + def getCurrentItem(self): + return self.currItem + + def initUpdatedDNA(self): + self.updatedDNA = list(self.avatar.getKartDNA()) + + def setUpdatedDNA(self): + currKartDNA = self.avatar.getKartDNA() + for i in xrange(len(self.updatedDNA)): + if self.updatedDNA[i] != currKartDNA[i]: + self.avatar.requestKartDNAFieldUpdate(i, self.updatedDNA[i]) + + del self.updatedDNA + + def setItemList(self, itemList): + self.itemList = itemList + + def load(self, uiRootNode): + self.uiBgFrame = DirectFrame(parent=self, relief=None, geom=uiRootNode.find('**/uiAccessoryViewerFrame'), scale=1.0) + self.uiImagePlane = DirectFrame(parent=self, relief=None, geom=uiRootNode.find('**/uiAccessoryImagePlane'), scale=0.75) + bounds = self.uiImagePlane.getBounds() + cm = CardMaker('uiImagePlane') + cm.setFrame(bounds[0], bounds[1], bounds[2], bounds[3]) + self.uiImagePlane['geom'] = NodePath(cm.generate()) + self.uiImagePlane.component('geom0').setColorScale(1.0, 1.0, 0.8, 1.0) + self.uiImagePlane.component('geom0').setTransparency(True) + self.locator1 = self.attachNewNode('locator1') + self.locator2 = self.attachNewNode('locator2') + self.locator1.setPos(0, 0, 0.035) + self.locator2.setPos(0.0, 0.0, 0.0) + tex = loader.loadTexture('phase_6/maps/NoAccessoryIcon3.jpg', 'phase_6/maps/NoAccessoryIcon3_a.rgb') + self.uiImagePlane.component('geom0').setTexture(tex, self.texCount) + self.texCount += 1 + self.uiTextBox = DirectFrame(parent=self, relief=None, scale=1.0, text='', text_font=ToontownGlobals.getInterfaceFont(), text_fg=(0.5, 0, 0, 1), text_shadow=(0, 0, 0, 1), text_scale=0.0715, text_pos=(0.0, -0.23, 0.0)) + self.deleteButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/uiAccessorydelete_up'), + uiRootNode.find('**/uiAccessorydelete_down'), + uiRootNode.find('**/uiAccessorydelete_rollover'), + uiRootNode.find('**/uiAccessorydelete_rollover')), text=TTLocalizer.KartShtikerDelete, text_font=ToontownGlobals.getSignFont(), text_pos=(0, -0.125, 0), text_scale=TTLocalizer.KPdeleteButton, text_fg=(1, 1, 1, 1), scale=1.0, pressEffect=False, command=lambda : self.__handleItemDeleteConfirm()) + self.deleteButton.hide() + self.leftArrowButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/ArrowLeftButtonUp'), + uiRootNode.find('**/ArrowLeftButtonDown'), + uiRootNode.find('**/ArrowLeftButtonRollover'), + uiRootNode.find('**/ArrowLeftButtonInactive')), scale=1.0, pressEffect=False, command=lambda : self.__handleItemChange(-1)) + self.rightArrowButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/ArrowRightButtonUp'), + uiRootNode.find('**/ArrowRightButtonDown'), + uiRootNode.find('**/ArrowRightButtonRollover'), + uiRootNode.find('**/ArrowRightButtonInactive')), scale=1.0, pressEffect=False, command=lambda : self.__handleItemChange(1)) + return + + def enable(self): + self.leftArrowButton['state'] = DGG.NORMAL + self.rightArrowButton['state'] = DGG.NORMAL + + def disable(self): + self.leftArrowButton['state'] = DGG.DISABLED + self.rightArrowButton['state'] = DGG.DISABLED + + def setupViewer(self, category): + colorTypeList = [KartDNA.bodyColor, KartDNA.accColor] + if category == InvalidEntry: + self.__handleHideItem() + self.__updateDeleteButton(DGG.DISABLED) + self.disable() + else: + accessDict = getAccessDictByType(self.avatar.getKartAccessoriesOwned()) + self.currAccessoryType = category + if category in colorTypeList: + self.itemList = list(accessDict.get(KartDNA.bodyColor, [])) + self.itemList.append(InvalidEntry) + elif category == KartDNA.rimsType: + self.itemList = list(accessDict.get(KartDNA.rimsType, [])) + self.itemList.append(InvalidEntry) + else: + self.itemList = list(accessDict.get(category, [])) + self.currItem = self.updatedDNA[category] + if category in colorTypeList: + if self.currItem == InvalidEntry or self.currItem not in accessDict.get(KartDNA.bodyColor): + self.__updateDeleteButton(DGG.DISABLED) + else: + self.__updateDeleteButton(DGG.NORMAL, TTLocalizer.KartShtikerDelete) + self.__handleShowItem() + elif category == KartDNA.rimsType: + if self.currItem == InvalidEntry: + self.__updateDeleteButton(DGG.DISABLED) + else: + self.__updateDeleteButton(DGG.NORMAL, TTLocalizer.KartShtikerDelete) + self.__handleShowItem() + elif self.currItem != InvalidEntry and self.itemList != []: + if self.currItem in self.avatar.accessories: + self.__handleShowItem() + self.__updateDeleteButton(DGG.NORMAL, TTLocalizer.KartShtikerDelete) + else: + self.__handleHideItem() + self.__updateDeleteButton(DGG.DISABLED) + if len(self.itemList) == 1: + if self.currAccessoryType == KartDNA.rimsType: + self.disable() + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + elif self.currAccessoryType in colorTypeList: + self.disable() + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + else: + self.enable() + elif len(self.itemList) == 0: + self.disable() + self.setViewerText(TTLocalizer.KartShtikerNo % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + else: + if self.currAccessoryType == KartDNA.rimsType: + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + elif self.currAccessoryType in colorTypeList: + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + elif self.currItem == InvalidEntry: + self.setViewerText(TTLocalizer.KartShtikerNo % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + self.enable() + + def resetViewer(self): + self.itemList = None + self.currItem = None + self.disable() + return + + def __updateDeleteButton(self, state, text = TTLocalizer.KartShtikerDelete): + self.deleteButton['state'] = state + self.deleteButton['text'] = text + if state == DGG.NORMAL: + self.uiImagePlane.setPos(self.locator1.getPos()) + self.deleteButton.show() + else: + self.uiImagePlane.setPos(self.locator2.getPos()) + self.deleteButton.hide() + + def setViewerText(self, text): + self.uiTextBox['text'] = text + + def __updateViewerUI(self): + accList = [KartDNA.bodyColor, KartDNA.accColor, KartDNA.rimsType] + if self.currItem != InvalidEntry: + self.__handleShowItem() + if self.currItem not in self.avatar.accessories and self.currAccessoryType in accList: + self.__updateDeleteButton(DGG.DISABLED) + else: + self.__updateDeleteButton(DGG.NORMAL, TTLocalizer.KartShtikerDelete) + else: + if self.currAccessoryType in accList: + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + self.__handleShowItem() + else: + self.__handleHideItem() + self.setViewerText(TTLocalizer.KartShtikerNo % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + self.__updateDeleteButton(DGG.DISABLED) + + def __handleItemChange(self, direction): + self.notify.debug('__handleItemChange: currItem %s' % self.currItem) + + def updateItem(self = self, direction = direction): + if self.itemList.count(self.currItem) != 0: + index = self.itemList.index(self.currItem) + index += direction + if index < 0 or index >= len(self.itemList): + invalidList = [KartDNA.bodyColor, KartDNA.accColor, KartDNA.rimsType] + if self.currAccessoryType not in invalidList: + self.currItem = InvalidEntry + elif direction > 0: + self.currItem = self.itemList[0] + else: + self.currItem = self.itemList[-1] + else: + self.currItem = self.itemList[index] + elif self.itemList == []: + self.currItem = InvalidEntry + elif direction > 0: + self.currItem = self.itemList[0] + else: + self.currItem = self.itemList[-1] + + messenger.send('wakeup') + updateItem() + self.__updateViewerUI() + self.notify.debug('__handleItemChange: currItem %s' % self.currItem) + self.updatedDNA[self.currAccessoryType] = self.currItem + kart = self.parent.parent.getKartViewer().getKart() + kart.updateDNAField(self.currAccessoryType, self.currItem) + + def __handleShowItem(self): + self.uiImagePlane.component('geom0').setColorScale(1.0, 1.0, 1.0, 1.0) + if self.currAccessoryType in [KartDNA.ebType, + KartDNA.spType, + KartDNA.fwwType, + KartDNA.bwwType]: + texNodePath = getTexCardNode(self.currItem) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath, 'phase_6/maps/%s_a.rgb' % texNodePath) + elif self.currAccessoryType == KartDNA.rimsType: + if self.currItem == InvalidEntry: + texNodePath = getTexCardNode(getDefaultRim()) + else: + texNodePath = getTexCardNode(self.currItem) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath, 'phase_6/maps/%s_a.rgb' % texNodePath) + elif self.currAccessoryType in [KartDNA.bodyColor, KartDNA.accColor]: + tex = loader.loadTexture('phase_6/maps/Kartmenu_paintbucket.jpg', 'phase_6/maps/Kartmenu_paintbucket_a.rgb') + if self.currItem == InvalidEntry: + self.uiImagePlane.component('geom0').setColorScale(getDefaultColor()) + else: + self.uiImagePlane.component('geom0').setColorScale(getAccessory(self.currItem)) + elif self.currAccessoryType == KartDNA.decalType: + kart = self.parent.parent.getKartViewer().getKart() + kartDecal = getDecalId(kart.kartDNA[KartDNA.bodyType]) + texNodePath = getTexCardNode(self.currItem) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath % kartDecal, 'phase_6/maps/%s_a.rgb' % texNodePath % kartDecal) + else: + tex = loader.loadTexture('phase_6/maps/NoAccessoryIcon3.jpg', 'phase_6/maps/NoAccessoryIcon3_a.rgb') + colorTypeList = [KartDNA.bodyColor, KartDNA.accColor] + if self.currItem == InvalidEntry: + if self.currAccessoryType == KartDNA.rimsType: + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + elif self.currAccessoryType in colorTypeList: + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + elif self.currItem == InvalidEntry: + self.setViewerText(TTLocalizer.KartShtikerNo % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + else: + self.setViewerText(getAccName(self.currItem) + ' ' + getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + self.uiImagePlane.component('geom0').setTexture(tex, self.texCount) + self.texCount += 1 + + def __handleHideItem(self): + self.uiImagePlane.component('geom0').setColorScale(1.0, 1.0, 1.0, 1.0) + self.uiImagePlane.component('geom0').setTexture(loader.loadTexture('phase_6/maps/NoAccessoryIcon3.jpg', 'phase_6/maps/NoAccessoryIcon3_a.rgb'), self.texCount) + self.texCount += 1 + + def __handleItemDeleteConfirm(self): + self.notify.debug('__handleItemDeleteConfirm:') + if not hasattr(self, 'confirmDlg'): + uiRootNode = loader.loadModel('phase_6/models/gui/ShtikerBookUI') + self.confirmDlg = DirectFrame(parent=aspect2d, relief=None, geom=uiRootNode.find('**/uiAccessoryNotice'), geom_scale=1.0, text=TTLocalizer.KartPageConfirmDelete, text_scale=0.07, text_pos=(0, 0.022)) + self.confirmDlg.hide() + self.confirmDlg.setPos(aspect2d, 0, -.195, -.195) + self.cancelButton = DirectButton(parent=self.confirmDlg, relief=None, image=(uiRootNode.find('**/CancelButtonUp'), uiRootNode.find('**/CancelButtonDown'), uiRootNode.find('**/CancelButtonRollover')), geom=uiRootNode.find('**/CancelIcon'), scale=1.0, pressEffect=False, command=self.confirmDlg.hide) + self.confirmButton = DirectButton(parent=self.confirmDlg, relief=None, image=(uiRootNode.find('**/CheckButtonUp'), uiRootNode.find('**/CheckButtonDown'), uiRootNode.find('**/CheckButtonRollover')), geom=uiRootNode.find('**/CheckIcon'), scale=1.0, pressEffect=False, command=self.__handleItemDelete) + self.confirmDlg.show() + return + + def __handleItemDelete(self): + + def handleColorDelete(self = self): + if self.currAccessoryType == KartDNA.bodyColor: + if self.updatedDNA[KartDNA.accColor] == deletedItem: + self.avatar.requestKartDNAFieldUpdate(KartDNA.accColor, self.currItem) + self.updatedDNA[KartDNA.accColor] = self.currItem + kart = self.parent.parent.getKartViewer().getKart() + kart.updateDNAField(KartDNA.accColor, self.currItem) + elif self.currAccessoryType == KartDNA.accColor: + if self.updatedDNA[KartDNA.bodyColor] == deletedItem: + self.avatar.requestKartDNAFieldUpdate(KartDNA.bodyColor, self.currItem) + self.updatedDNA[KartDNA.bodyColor] = self.currItem + kart = self.parent.parent.getKartViewer().getKart() + kart.updateDNAField(KartDNA.bodyColor, self.currItem) + + self.notify.debug('__handleItemDelete: Delete request on accessory %s' % self.currItem) + self.confirmDlg.hide() + messenger.send('wakeup') + deletedItem = self.currItem + self.avatar.requestRemoveOwnedAccessory(deletedItem) + index = self.itemList.index(self.currItem) + self.itemList.pop(index) + self.currItem = InvalidEntry + self.__updateViewerUI() + self.updatedDNA[self.currAccessoryType] = self.currItem + kart = self.parent.parent.getKartViewer().getKart() + kart.updateDNAField(self.currAccessoryType, self.currItem) + if self.avatar.getAccessoryByType(self.currAccessoryType) == deletedItem: + self.avatar.requestKartDNAFieldUpdate(self.currAccessoryType, self.currItem) + if self.currAccessoryType in [KartDNA.bodyColor, KartDNA.accColor]: + handleColorDelete() + if self.itemList == [] or self.itemList[0] == InvalidEntry: + self.disable() + self.setViewerText(TTLocalizer.KartShtikerNo % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + + def __init__(self, avatar, parent = aspect2d): + self.state = InvalidEntry + self.avatar = avatar + self.itemViewers = {} + self.buttonDict = {} + self.parent = parent + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0, 0, 0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + for key in self.buttonDict.keys(): + self.buttonDict[key].destroy() + del self.buttonDict[key] + + for key in self.itemViewers.keys(): + self.itemViewers[key].destroy() + del self.itemViewers[key] + + del self.avatar + del self.itemViewers + del self.buttonDict + del self.ebButton + del self.fwwButton + del self.bwwButton + del self.rimButton + del self.decalButton + del self.paintKartButton + del self.paintAccessoryButton + DirectFrame.destroy(self) + + def load(self, uiRootNode): + self.itemViewers['main'] = ItemSelector.ItemViewer(self.avatar, self) + self.itemViewers['main'].load(uiRootNode) + self.itemViewers['main'].setPos(self.getParent(), uiRootNode.find('**/uiAccessoryView').getPos()) + self.ebButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/eBlockButton_up'), + uiRootNode.find('**/eBlockButton_rollover'), + uiRootNode.find('**/eBlockButton_rollover'), + uiRootNode.find('**/eBlockButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.ebType)) + self.buttonDict[KartDNA.ebType] = self.ebButton + self.spButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/spoilerButton_up'), + uiRootNode.find('**/spoilerButton_rollover'), + uiRootNode.find('**/spoilerButton_rollover'), + uiRootNode.find('**/spoilerButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.spType)) + self.buttonDict[KartDNA.spType] = self.spButton + self.fwwButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/frontButton_up'), + uiRootNode.find('**/frontButton_rollover'), + uiRootNode.find('**/frontButton_rollover'), + uiRootNode.find('**/frontButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.fwwType)) + self.buttonDict[KartDNA.fwwType] = self.fwwButton + self.bwwButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/rearButton_up'), + uiRootNode.find('**/rearButton_rollover'), + uiRootNode.find('**/rearButton_rollover'), + uiRootNode.find('**/rearButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.bwwType)) + self.buttonDict[KartDNA.bwwType] = self.bwwButton + self.rimButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/rimButton_up'), + uiRootNode.find('**/rimButton_rollover'), + uiRootNode.find('**/rimButton_rollover'), + uiRootNode.find('**/rimButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.rimsType)) + self.buttonDict[KartDNA.rimsType] = self.rimButton + self.decalButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/decalButton_up'), + uiRootNode.find('**/decalButton_rollover'), + uiRootNode.find('**/decalButton_rollover'), + uiRootNode.find('**/decalButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.decalType)) + self.buttonDict[KartDNA.decalType] = self.decalButton + self.paintKartButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/paintKartButton_up'), + uiRootNode.find('**/paintKartButton_rollover'), + uiRootNode.find('**/paintKartButton_rollover'), + uiRootNode.find('**/paintKartButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.bodyColor)) + self.buttonDict[KartDNA.bodyColor] = self.paintKartButton + self.paintAccessoryButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/paintAccessoryButton_up'), + uiRootNode.find('**/paintAccessoryButton_rollover'), + uiRootNode.find('**/paintAccessoryButton_rollover'), + uiRootNode.find('**/paintAccessoryButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.accColor)) + self.buttonDict[KartDNA.accColor] = self.paintAccessoryButton + return + + def setupAccessoryIcons(self): + accessDict = getAccessDictByType(self.avatar.getKartAccessoriesOwned()) + if accessDict == {}: + self.itemViewers['main'].disable() + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerNoAccessories) + return + self.__changeItemCategory(self.state) + + def resetAccessoryIcons(self): + for key in self.buttonDict.keys(): + self.buttonDict[key].setProp('state', DGG.NORMAL) + + self.itemViewers['main'].show() + self.itemViewers['main'].setViewerText('') + self.state = InvalidEntry + self.itemViewers['main'].resetViewer() + + def __changeItemCategory(self, buttonType): + if buttonType == KartDNA.ebType: + self.ebButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerEngineBlocks) + self.itemViewers['main'].setupViewer(KartDNA.ebType) + elif buttonType == KartDNA.spType: + self.spButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerSpoilers) + self.itemViewers['main'].setupViewer(KartDNA.spType) + elif buttonType == KartDNA.fwwType: + self.fwwButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerFrontWheelWells) + self.itemViewers['main'].setupViewer(KartDNA.fwwType) + elif buttonType == KartDNA.bwwType: + self.bwwButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerBackWheelWells) + self.itemViewers['main'].setupViewer(KartDNA.bwwType) + elif buttonType == KartDNA.rimsType: + self.rimButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerRims) + self.itemViewers['main'].setupViewer(KartDNA.rimsType) + elif buttonType == KartDNA.decalType: + self.decalButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerDecals) + self.itemViewers['main'].setupViewer(KartDNA.decalType) + elif buttonType == KartDNA.bodyColor: + self.paintKartButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerBodyColors) + self.itemViewers['main'].setupViewer(KartDNA.bodyColor) + elif buttonType == KartDNA.accColor: + self.paintAccessoryButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerAccColors) + self.itemViewers['main'].setupViewer(KartDNA.accColor) + elif buttonType == InvalidEntry: + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerSelect) + self.itemViewers['main'].setupViewer(buttonType) + else: + raise StandardError, 'KartPage.py::__changeItemCategory - INVALID Category Type!' + if self.state != buttonType and self.state != InvalidEntry: + self.buttonDict[self.state]['state'] = DGG.NORMAL + self.buttonDict[self.state].setColorScale(1, 1, 1, 1) + self.state = buttonType + + +class KartViewer(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartViewer') + + def __init__(self, dna, parent): + self.kart = None + self.dna = dna + self.parent = parent + self.kartFrame = None + self.bounds = None + self.colors = None + self.uiRotateRight = None + self.uiRotateLeft = None + self.uiRotateLabel = None + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0, 0, 0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + taskMgr.remove('kartRotateTask') + if self.kart != None: + self.kart.delete() + self.kart = None + if hasattr(self, 'kartDisplayRegion'): + self.kartDisplayRegion.unload() + if hasattr(self, 'uiBgFrame'): + self.uiBgFrame.destroy() + del self.uiBgFrame + if hasattr(self, 'uiRotateLeft') and self.uiRotateLeft: + self.uiRotateLeft.destroy() + del self.uiRotateLeft + if hasattr(self, 'uiRotateRight') and self.uiRotateRight: + self.uiRotateRight.destroy() + del self.uiRotateRight + if hasattr(self, 'uiRotateLabelt') and self.uiRotateLabel: + self.uiRotateLabel.destroy() + del self.uiRotateLabel + if hasattr(self, 'dna'): + del self.dna + if hasattr(self, 'parent'): + del self.parent + DirectFrame.destroy(self) + return + + def load(self, uiRootNode, bgFrame = 'uiKartViewerFrame1', rightArrow = ['rotate_right_up', + 'rotate_right_down', + 'rotate_right_roll', + 'rotate_right_down', + (0, 0)], leftArrow = ['rotate_left_up', + 'rotate_left_down', + 'rotate_left_roll', + 'rotate_left_down', + (0, 0)], rotatePos = (0, 0)): + self.uiBgFrame = DirectFrame(parent=self, relief=None, geom=uiRootNode.find('**/' + bgFrame), scale=1.0) + if leftArrow and len(leftArrow) == 5: + self.uiRotateLeft = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/' + leftArrow[0]), + uiRootNode.find('**/' + leftArrow[1]), + uiRootNode.find('**/' + leftArrow[2]), + uiRootNode.find('**/' + leftArrow[3])), scale=1.0, text=TTLocalizer.KartView_Left, text_scale=TTLocalizer.KProtateButton, text_pos=(leftArrow[4][0], leftArrow[4][1], 0), text_fg=(1, 1, 1, 1.0), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), pressEffect=False) + self.uiRotateLeft.bind(DGG.B1PRESS, self.__handleKartRotate, [-3]) + self.uiRotateLeft.bind(DGG.B1RELEASE, self.__endKartRotate) + if rightArrow and len(rightArrow) == 5: + self.uiRotateRight = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/' + rightArrow[0]), + uiRootNode.find('**/' + rightArrow[1]), + uiRootNode.find('**/' + rightArrow[2]), + uiRootNode.find('**/' + rightArrow[3])), scale=1.0, text=TTLocalizer.KartView_Right, text_scale=TTLocalizer.KProtateButton, text_pos=(rightArrow[4][0], rightArrow[4][1], 0), text_fg=(1, 1, 1, 1.0), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), pressEffect=False) + self.uiRotateRight.bind(DGG.B1PRESS, self.__handleKartRotate, [3]) + self.uiRotateRight.bind(DGG.B1RELEASE, self.__endKartRotate) + return + + def setBounds(self, *bounds): + self.bounds = bounds + + def setBgColor(self, *colors): + self.colors = colors + + def makeKartFrame(self): + if self.kart != None: + self.kart.delete() + self.kart = None + if not hasattr(self, 'kartDisplayRegion'): + self.kartDisplayRegion = DirectRegion(parent=self) + apply(self.kartDisplayRegion.setBounds, self.bounds) + apply(self.kartDisplayRegion.setColor, self.colors) + frame = self.kartDisplayRegion.load() + if self.dna: + self.kart = Kart() + self.kart.setDNA(self.dna) + self.kart.generateKart(forGui=1) + self.kart.setDepthTest(1) + self.kart.setDepthWrite(1) + self.pitch = frame.attachNewNode('pitch') + self.rotate = self.pitch.attachNewNode('rotate') + self.scale = self.rotate.attachNewNode('scale') + self.kart.reparentTo(self.scale) + bMin, bMax = self.kart.getKartBounds() + center = (bMin + bMax) / 2.0 + self.kart.setPos(-center[0], -center[1], -center[2]) + self.scale.setScale(0.5) + self.rotate.setH(-35) + self.pitch.setP(0) + self.pitch.setY(getKartViewDist(self.kart.getBodyType())) + self.kart.setScale(1, 1, 1.5) + self.kart.setTwoSided(1) + if self.uiRotateRight: + self.uiRotateRight.show() + if self.uiRotateLeft: + self.uiRotateLeft.show() + if self.uiRotateLabel: + self.uiRotateLabel.show() + else: + if self.uiRotateRight: + self.uiRotateRight.hide() + if self.uiRotateLeft: + self.uiRotateLeft.hide() + if self.uiRotateLabel: + self.uiRotateLabel.hide() + return frame + + def show(self, dna = None): + if self.kartFrame: + if self.kart != None: + self.kart.delete() + self.kart = None + if hasattr(self, 'kartDisplayRegion'): + self.kartDisplayRegion.unload() + self.hide() + self.uiBgFrame.show() + self.refresh(dna) + self.__handleKartRotate(1) + return + + def hide(self): + self.uiBgFrame.hide() + if self.kart != None: + self.kart.delete() + self.kart = None + if hasattr(self, 'kartDisplayRegion'): + self.kartDisplayRegion.unload() + return + + def __handleKartRotate(self, direction, extraArgs = []): + taskMgr.add(self.__rotateTask, 'kartRotateTask', extraArgs=[direction]) + + def __rotateTask(self, direction): + if hasattr(self, 'pitch'): + self.pitch.setH(self.pitch.getH() + 0.4 * direction) + return Task.cont + else: + return Task.done + + def __endKartRotate(self, extraArgs = []): + taskMgr.remove('kartRotateTask') + + def getKart(self): + return self.kart + + def setDNA(self, dna): + self.dna = dna + + def refresh(self, dna = None): + taskMgr.removeTasksMatching('kartRotateTask') + if dna: + self.dna = dna + curPitch = 0 + if hasattr(self, 'pitch'): + curPitch = self.pitch.getH() + else: + curPitch = 0 + if self.kart != None: + self.kart.delete() + self.kart = None + del self.kartFrame + self.kartFrame = self.makeKartFrame() + if hasattr(self, 'pitch'): + self.pitch.setH(curPitch) + return + + +class RacingTrophy(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('RacingTrophy') + + def __init__(self, level, *args, **kwargs): + opts = {'relief': None} + opts.update(kwargs) + DirectFrame.__init__(self, *args, **opts) + self.trophy = loader.loadModel('phase_6/models/gui/racingTrophy') + self.trophy.reparentTo(self) + self.trophy.setPos(0, 1, 0) + self.trophy.setScale(0.1) + self.base = self.trophy.find('**/trophyBase') + self.column = self.trophy.find('**/trophyColumn') + self.top = self.trophy.find('**/trophyTop') + self.topBase = self.trophy.find('**/trophyTopBase') + self.statue = self.trophy.find('**/trophyStatue') + self.base.setColorScale(1, 1, 0.8, 1) + self.topBase.setColorScale(1, 1, 0.8, 1) + self.greyBowl = loader.loadModel('phase_6/models/gui/racingTrophyBowl2') + self.greyBowl.reparentTo(self) + self.greyBowl.setPos(0, 0.5, 0) + self.greyBowl.setScale(2.0) + self.goldBowl = loader.loadModel('phase_6/models/gui/racingTrophyBowl') + self.goldBowl.reparentTo(self) + self.goldBowl.setPos(0, 0.5, 0) + self.goldBowl.setScale(2.0) + self.goldBowlBase = self.goldBowl.find('**/fishingTrophyBase') + self.goldBowlBase.hide() + self.nameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.15), text='', text_scale=0.125, text_fg=Vec4(0.9, 0.9, 0.4, 1)) + self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.shadow.reparentTo(self) + self.shadow.setColor(1, 1, 1, 0.2) + self.shadow.setPosHprScale(0, 1, 0.35, 0, 90, 0, 0.1, 0.14, 0.1) + self.setLevel(level) + return + + def setLevel(self, level): + self.level = level + if level == -1: + self.trophy.hide() + self.greyBowl.hide() + self.goldBowl.hide() + self.nameLabel.hide() + else: + self.nameLabel.show() + if level < 30 and level % 10 == 9: + self.trophy.hide() + self.goldBowl.hide() + self.greyBowl.show() + self.greyBowl.setScale(8.25, 3.5, 3.5) + elif level >= 30: + self.trophy.hide() + self.greyBowl.hide() + self.goldBowl.show() + self.goldBowlBase.hide() + else: + self.trophy.show() + self.goldBowl.hide() + self.greyBowl.hide() + if level == 30: + self.goldBowl.setScale(4.4, 3.1, 3.1) + elif level == 31: + self.goldBowl.setScale(3.6, 3.5, 3.5) + elif level >= 32: + self.goldBowl.setScale(5.6, 3.9, 3.9) + if level % 10 == 9: + pass + elif level % 10 % 3 == 0: + self.column.setScale(1.3229, 1.26468, 1.11878) + self.top.setPos(0, 0, -1) + self.__bronze() + elif level % 10 % 3 == 1: + self.column.setScale(1.3229, 1.26468, 1.61878) + self.top.setPos(0, 0, -.5) + self.__silver() + elif level % 10 % 3 == 2: + self.column.setScale(1.3229, 1.26468, 2.11878) + self.top.setPos(0, 0, 0) + self.__gold() + if level < 10: + self.__tealColumn() + elif level < 20: + self.__purpleColumn() + elif level < 30: + self.__blueColumn() + else: + self.__redColumn() + + def __bronze(self): + self.statue.setColorScale(0.9, 0.6, 0.33, 1) + + def __silver(self): + self.statue.setColorScale(0.9, 0.9, 1, 1) + + def __gold(self): + self.statue.setColorScale(1, 0.95, 0.1, 1) + + def __platinum(self): + self.statue.setColorScale(1, 0.95, 0.1, 1) + + def __tealColumn(self): + self.column.setColorScale(0.5, 1.2, 0.85, 1) + + def __purpleColumn(self): + self.column.setColorScale(1, 0.7, 0.95, 1) + + def __redColumn(self): + self.column.setColorScale(1.2, 0.6, 0.6, 1) + + def __yellowColumn(self): + self.column.setColorScale(1, 1, 0.8, 1) + + def __blueColumn(self): + self.column.setColorScale(0.6, 0.75, 1.2, 1) + + def destroy(self): + self.trophy.removeNode() + self.goldBowl.removeNode() + self.greyBowl.removeNode() + self.shadow.removeNode() + DirectFrame.destroy(self) diff --git a/toontown/shtiker/MapPage.py b/toontown/shtiker/MapPage.py new file mode 100755 index 00000000..835e55c2 --- /dev/null +++ b/toontown/shtiker/MapPage.py @@ -0,0 +1,232 @@ +import ShtikerPage +from toontown.toonbase import ToontownGlobals +from direct.showbase import PythonUtil +from toontown.hood import ZoneUtil +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer + +class MapPage(ShtikerPage.ShtikerPage): + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + + def load(self): + ShtikerPage.ShtikerPage.load(self) + mapModel = loader.loadModel('phase_3.5/models/gui/toontown_map') + self.map = DirectFrame(parent=self, relief=None, image=mapModel.find('**/toontown_map'), image_scale=(1.8, 1, 1.35), scale=0.97, pos=(0, 0, 0.0775)) + mapModel.removeNode() + self.allZones = [] + for hood in ToontownGlobals.Hoods: + if hood not in [ToontownGlobals.GolfZone, ToontownGlobals.FunnyFarm]: + self.allZones.append(hood) + + self.cloudScaleList = (((0.55, 0, 0.4), (0.35, 0, 0.25)), + (), + ((0.45, 0, 0.45), (0.5, 0, 0.4)), + ((0.7, 0, 0.45),), + ((0.55, 0, 0.4),), + ((0.6, 0, 0.4), (0.5332, 0, 0.32)), + ((0.7, 0, 0.45), (0.7, 0, 0.45)), + ((0.7998, 0, 0.39),), + ((0.5, 0, 0.4),), + ((-0.45, 0, 0.4),), + ((-0.45, 0, 0.35),), + ((0.5, 0, 0.35),), + ((0.5, 0, 0.35),)) + self.cloudPosList = (((0.575, 0.0, -0.04), (0.45, 0.0, -0.25)), + (), + ((0.375, 0.0, 0.4), (0.5625, 0.0, 0.2)), + ((-0.02, 0.0, 0.23),), + ((-0.3, 0.0, -0.4),), + ((0.25, 0.0, -0.425), (0.125, 0.0, -0.36)), + ((-0.5625, 0.0, -0.07), (-0.45, 0.0, 0.2125)), + ((-0.125, 0.0, 0.5),), + ((0.66, 0.0, -0.4),), + ((-0.68, 0.0, -0.444),), + ((-0.6, 0.0, 0.45),), + ((0.66, 0.0, 0.5),), + ((0.4, 0.0, -0.35),)) + self.labelPosList = ((0.594, 0.0, -0.075), + (0.0, 0.0, -0.1), + (0.475, 0.0, 0.25), + (0.1, 0.0, 0.15), + (-0.3, 0.0, -0.375), + (0.2, 0.0, -0.45), + (-0.55, 0.0, 0.0), + (-0.088, 0.0, 0.47), + (0.7, 0.0, -0.5), + (-0.7, 0.0, -0.5), + (-0.7, 0.0, 0.5), + (0.7, 0.0, 0.5), + (0.45, 0.0, -0.45)) + self.labels = [] + self.clouds = [] + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + buttonLoc = (0.45, 0, - 0.74) + if base.housingEnabled: + buttonLoc = (0.55, 0, -0.74) + self.safeZoneButton = DirectButton( + parent=self.map, + relief=None, + image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), + image_scale=(1.3, 1.1, 1.1), + pos=buttonLoc, + text=TTLocalizer.MapPageBackToPlayground, + text_scale=TTLocalizer.MPsafeZoneButton, + text_pos=(0, -0.02), + command=self.backToSafeZone) + self.goHomeButton = DirectButton( + parent=self.map, + relief=None, + image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), + image_scale=(0.66, 1.1, 1.1), + pos=(0.15, 0, -.74), + text=TTLocalizer.MapPageGoHome, + text_scale=TTLocalizer.MPgoHomeButton, + text_pos=(0, -0.02), + command=self.goHome) + self.goHomeButton.hide() + guiButton.removeNode() + self.hoodLabel = DirectLabel( + parent=self.map, + relief=None, + pos=(-0.43, 0, -0.726), + text='', + text_scale=TTLocalizer.MPhoodLabel, + text_pos=(0, 0), + text_wordwrap=TTLocalizer.MPhoodLabelWordwrap) + self.hoodLabel.hide() + cloudModel = loader.loadModel('phase_3.5/models/gui/cloud') + cloudImage = cloudModel.find('**/cloud') + for hood in self.allZones: + abbrev = base.cr.hoodMgr.getNameFromId(hood) + fullname = base.cr.hoodMgr.getFullnameFromId(hood) + hoodIndex = self.allZones.index(hood) + label = DirectButton( + parent=self.map, + relief=None, + pos=self.labelPosList[hoodIndex], + pad=(0.2, 0.16), + text=('', fullname, fullname), + text_bg=Vec4(1, 1, 1, 0.4), + text_scale=0.055, + text_wordwrap=8, + rolloverSound=None, + clickSound=None, + pressEffect=0, + command=self.__buttonCallback, + extraArgs=[hood], + sortOrder=1) + label.resetFrameSize() + self.labels.append(label) + hoodClouds = [] + for cloudScale, cloudPos in zip(self.cloudScaleList[hoodIndex], self.cloudPosList[hoodIndex]): + cloud = DirectFrame( + parent=self.map, + relief=None, + state=DGG.DISABLED, + image=cloudImage, + scale=(cloudScale[0], cloudScale[1], cloudScale[2]), + pos=(cloudPos[0], cloudPos[1], cloudPos[2])) + cloud.hide() + hoodClouds.append(cloud) + + self.clouds.append(hoodClouds) + + cloudModel.removeNode() + self.resetFrameSize() + return + + def unload(self): + for labelButton in self.labels: + labelButton.destroy() + + del self.labels + del self.clouds + self.safeZoneButton.destroy() + self.goHomeButton.destroy() + ShtikerPage.ShtikerPage.unload(self) + + def enter(self): + ShtikerPage.ShtikerPage.enter(self) + try: + zone = base.cr.playGame.getPlace().getZoneId() + except: + zone = 0 + + if base.localAvatar.lastHood >= ToontownGlobals.BossbotHQ: + self.safeZoneButton['text'] = TTLocalizer.MapPageBackToCogHQ + else: + self.safeZoneButton['text'] = TTLocalizer.MapPageBackToPlayground + if zone and ZoneUtil.isPlayground(zone) or self.book.safeMode: + self.safeZoneButton.hide() + else: + self.safeZoneButton.show() + if base.cr.playGame.getPlaceId() == ToontownGlobals.MyEstate and base.cr.playGame.hood.loader.atMyEstate() or self.book.safeMode: + self.goHomeButton.hide() + elif base.housingEnabled: + self.goHomeButton.show() + if base.cr.playGame.getPlaceId() == ToontownGlobals.MyEstate: + if base.cr.playGame.hood.loader.atMyEstate(): + self.hoodLabel['text'] = TTLocalizer.MapPageYouAreAtHome + self.hoodLabel.show() + else: + avatar = base.cr.identifyAvatar(base.cr.playGame.hood.loader.estateOwnerId) + if avatar: + avName = avatar.getName() + self.hoodLabel['text'] = TTLocalizer.MapPageYouAreAtSomeonesHome % TTLocalizer.GetPossesive(avName) + self.hoodLabel.show() + elif zone: + hoodName = ToontownGlobals.hoodNameMap.get(ZoneUtil.getCanonicalHoodId(zone), ('',))[-1] + streetName = ToontownGlobals.StreetNames.get(ZoneUtil.getCanonicalBranchZone(zone), ('',))[-1] + if hoodName: + self.hoodLabel['text'] = TTLocalizer.MapPageYouAreHere % (hoodName, streetName) + self.hoodLabel.show() + else: + self.hoodLabel.hide() + else: + self.hoodLabel.hide() + hoodsVisited = base.localAvatar.hoodsVisited + hoodTeleportList = base.localAvatar.getTeleportAccess() + for hood in self.allZones: + label = self.labels[self.allZones.index(hood)] + clouds = self.clouds[self.allZones.index(hood)] + if not self.book.safeMode and hood in hoodsVisited: + label['text_fg'] = (0, 0, 0, 1) + label.show() + for cloud in clouds: + cloud.hide() + + fullname = base.cr.hoodMgr.getFullnameFromId(hood) + if hood in hoodTeleportList: + text = TTLocalizer.MapPageGoTo % fullname + label['text'] = ('', text, text) + else: + label['text'] = ('', fullname, fullname) + else: + label.hide() + for cloud in clouds: + cloud.show() + + def exit(self): + ShtikerPage.ShtikerPage.exit(self) + + def backToSafeZone(self): + self.doneStatus = {'mode': 'teleport', + 'hood': base.localAvatar.lastHood} + messenger.send(self.doneEvent) + + def goHome(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: VISITESTATE: Visit estate') + self.doneStatus = {'mode': 'gohome', + 'hood': base.localAvatar.lastHood} + messenger.send(self.doneEvent) + + def __buttonCallback(self, hood): + if hood in base.localAvatar.getTeleportAccess(): + base.localAvatar.sendUpdate('checkTeleportAccess', [hood]) + self.doneStatus = {'mode': 'teleport', + 'hood': hood} + messenger.send(self.doneEvent) diff --git a/toontown/shtiker/NPCFriendPage.py b/toontown/shtiker/NPCFriendPage.py new file mode 100755 index 00000000..24318142 --- /dev/null +++ b/toontown/shtiker/NPCFriendPage.py @@ -0,0 +1,33 @@ +import ShtikerPage +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toon import NPCFriendPanel +from toontown.toonbase import TTLocalizer + +class NPCFriendPage(ShtikerPage.ShtikerPage): + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + + def load(self): + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.NPCFriendPageTitle, text_scale=0.12, textMayChange=0, pos=(0, 0, 0.6)) + self.friendPanel = NPCFriendPanel.NPCFriendPanel(parent=self, callable=False) + self.friendPanel.setScale(0.1225) + self.friendPanel.setZ(-0.03) + return + + def unload(self): + ShtikerPage.ShtikerPage.unload(self) + del self.title + del self.friendPanel + + def updatePage(self): + self.friendPanel.setFriends(base.localAvatar.NPCFriendsDict) + self.friendPanel.update() + + def enter(self): + self.updatePage() + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + ShtikerPage.ShtikerPage.exit(self) diff --git a/toontown/shtiker/NewbiePurchaseManager.py b/toontown/shtiker/NewbiePurchaseManager.py new file mode 100755 index 00000000..f4b48f59 --- /dev/null +++ b/toontown/shtiker/NewbiePurchaseManager.py @@ -0,0 +1,55 @@ +import PurchaseManager +from toontown.quest import QuestParser +from toontown.toon import NPCToons + +class NewbiePurchaseManager(PurchaseManager.PurchaseManager): + + def setOwnedNewbieId(self, ownedNewbieId): + self.ownedNewbieId = ownedNewbieId + + def calcHasLocalToon(self): + return base.localAvatar.doId == self.ownedNewbieId + + def announceGenerate(self): + PurchaseManager.PurchaseManager.announceGenerate(self) + if self.hasLocalToon: + self.npc = NPCToons.createLocalNPC(2011) + self.npc.addActive() + + def getDoId(): + return 0 + + self.npc.getDoId = getDoId + + def acquireDelayDelete(name): + return serialNum() + + self.npc.acquireDelayDelete = acquireDelayDelete + + def releaseDelayDelete(token): + pass + + self.npc.releaseDelayDelete = releaseDelayDelete + + def uniqueName(string): + return string + + self.npc.uniqueName = uniqueName + self.accept('gagScreenIsUp', self.playMovie) + self.purchase = base.cr.playGame.hood.purchase + self.purchase.enterTutorialMode(self.ownedNewbieId) + + def disable(self): + PurchaseManager.PurchaseManager.disable(self) + if hasattr(self, 'movie'): + self.npc.removeActive() + self.npc.delete() + del self.npc + del self.movie + + def playMovie(self): + self.movie = QuestParser.NPCMoviePlayer('gag_intro', base.localAvatar, self.npc) + self.movie.setVar('backToPlaygroundButton', self.purchase.backToPlayground) + self.movie.setVar('playAgainButton', self.purchase.playAgain) + self.movie.setVar('purchaseBg', self.purchase.bg) + self.movie.play() diff --git a/toontown/shtiker/NewbiePurchaseManagerAI.py b/toontown/shtiker/NewbiePurchaseManagerAI.py new file mode 100755 index 00000000..a55897e5 --- /dev/null +++ b/toontown/shtiker/NewbiePurchaseManagerAI.py @@ -0,0 +1,22 @@ +import PurchaseManagerAI + +class NewbiePurchaseManagerAI(PurchaseManagerAI.PurchaseManagerAI): + + def __init__(self, air, newbieId, avArray, mpArray, previousMinigameId, trolleyZone): + self.ownedNewbieId = newbieId + newbieList = [] + PurchaseManagerAI.PurchaseManagerAI.__init__(self, air, avArray, mpArray, previousMinigameId, trolleyZone, newbieList) + + def startCountdown(self): + pass + + def getOwnedNewbieId(self): + return self.ownedNewbieId + + def getInvolvedAvIds(self): + return [self.ownedNewbieId] + + def handlePlayerLeaving(self, avId): + toon = self.air.doId2do.get(avId) + if toon: + self.air.questManager.toonRodeTrolleyFirstTime(toon) diff --git a/toontown/shtiker/OptionChooser.py b/toontown/shtiker/OptionChooser.py new file mode 100644 index 00000000..14f9345f --- /dev/null +++ b/toontown/shtiker/OptionChooser.py @@ -0,0 +1,57 @@ +from direct.gui.DirectGui import * + +class OptionChooser: + + def __init__(self, book, labelText, row, indexCommand, extraArgs, exitCommand): + options_text_scale = 0.052 + leftMargin = -0.72 + buttonbase_xcoord = 0.35 + textStartHeight = 0.45 + textRowHeight = 0.145 + y = textStartHeight - row * textRowHeight + matGui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + arrow_image = (matGui.find('**/tt_t_gui_mat_shuffleArrowUp'), matGui.find('**/tt_t_gui_mat_shuffleArrowDown')) + + self.indexCommand = indexCommand + self.extraArgs = extraArgs + self.exit = exitCommand + self.label = DirectLabel(book, relief=None, text=labelText, text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=16, pos=(leftMargin, 0, y)) + self.display = DirectLabel(book, relief=None, scale=0.06, text_wordwrap=9, pos=(buttonbase_xcoord, 0, y)) + self.leftButton = DirectButton(book, relief=None, image=arrow_image, scale=0.45, pos=(textStartHeight - 0.4, 0, y), command=self.offsetIndex, extraArgs=[-1]) + self.rightButton = DirectButton(book, relief=None, image=arrow_image, scale=-0.45, pos=(textStartHeight + 0.2, 0, y), command=self.offsetIndex, extraArgs=[1]) + self.index = -1 + matGui.removeNode() + + def unload(self): + self.label.destroy() + del self.label + self.display.destroy() + del self.display + self.leftButton.destroy() + del self.leftButton + self.rightButton.destroy() + del self.rightButton + + def offsetIndex(self, offset): + self.index += offset + self.indexCommand(*self.extraArgs) + + def setIndex(self, index): + self.index = index + + def setDisplayText(self, text): + self.display['text'] = text + + def setDisplayFont(self, font): + self.display['text_font'] = font + + def decideButtons(self, minCount, maxCount): + if self.index <= minCount: + self.leftButton.hide() + else: + self.leftButton.show() + + if self.index >= maxCount: + self.rightButton.hide() + else: + self.rightButton.show() \ No newline at end of file diff --git a/toontown/shtiker/OptionsPage.py b/toontown/shtiker/OptionsPage.py new file mode 100755 index 00000000..cda4d2f5 --- /dev/null +++ b/toontown/shtiker/OptionsPage.py @@ -0,0 +1,895 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.gui.DirectGui import * +from direct.showbase import PythonUtil +from direct.task import Task +from panda3d.core import * + +import DisplaySettingsDialog +import ShtikerPage +import OptionChooser +from otp.speedchat import SCColorScheme +from otp.speedchat import SCStaticTextTerminal +from otp.speedchat import SpeedChat +from toontown.toonbase import TTLocalizer, ToontownGlobals +from toontown.toon import Toon +from toontown.toontowngui import TTDialog +import webbrowser + + +speedChatStyles = ( + ( + 2000, + (200 / 255.0, 60 / 255.0, 229 / 255.0), + (200 / 255.0, 135 / 255.0, 255 / 255.0), + (220 / 255.0, 195 / 255.0, 229 / 255.0) + ), + ( + 2012, + (142 / 255.0, 151 / 255.0, 230 / 255.0), + (173 / 255.0, 180 / 255.0, 237 / 255.0), + (220 / 255.0, 195 / 255.0, 229 / 255.0) + ), + ( + 2001, + (0 / 255.0, 0 / 255.0, 255 / 255.0), + (140 / 255.0, 150 / 255.0, 235 / 255.0), + (201 / 255.0, 215 / 255.0, 255 / 255.0) + ), + ( + 2010, + (0 / 255.0, 119 / 255.0, 190 / 255.0), + (53 / 255.0, 180 / 255.0, 255 / 255.0), + (201 / 255.0, 215 / 255.0, 255 / 255.0) + ), + ( + 2014, + (0 / 255.0, 64 / 255.0, 128 / 255.0), + (0 / 255.0, 64 / 255.0, 128 / 255.0), + (201 / 255.0, 215 / 255.0, 255 / 255.0) + ), + ( + 2002, + (90 / 255.0, 175 / 255.0, 225 / 255.0), + (120 / 255.0, 215 / 255.0, 255 / 255.0), + (208 / 255.0, 230 / 255.0, 250 / 255.0) + ), + ( + 2003, + (130 / 255.0, 235 / 255.0, 235 / 255.0), + (120 / 255.0, 225 / 255.0, 225 / 255.0), + (234 / 255.0, 255 / 255.0, 255 / 255.0) + ), + ( + 2004, + (0 / 255.0, 200 / 255.0, 70 / 255.0), + (0 / 255.0, 200 / 255.0, 80 / 255.0), + (204 / 255.0, 255 / 255.0, 204 / 255.0) + ), + ( + 2015, + (13 / 255.0, 255 / 255.0, 100 / 255.0), + (64 / 255.0, 255 / 255.0, 131 / 255.0), + (204 / 255.0, 255 / 255.0, 204 / 255.0) + ), + ( + 2005, + (235 / 255.0, 230 / 255.0, 0 / 255.0), + (255 / 255.0, 250 / 255.0, 100 / 255.0), + (255 / 255.0, 250 / 255.0, 204 / 255.0) + ), + ( + 2006, + (255 / 255.0, 153 / 255.0, 0 / 255.0), + (229 / 255.0, 147 / 255.0, 0 / 255.0), + (255 / 255.0, 234 / 255.0, 204 / 255.0) + ), + ( + 2011, + (255 / 255.0, 177 / 255.0, 62 / 255.0), + (255 / 255.0, 200 / 255.0, 117 / 255.0), + (255 / 255.0, 234 / 255.0, 204 / 255.0) + ), + ( + 2007, + (255 / 255.0, 0 / 255.0, 50 / 255.0), + (229 / 255.0, 0 / 255.0, 50 / 255.0), + (255 / 255.0, 204 / 255.0, 204 / 255.0) + ), + ( + 2013, + (130 / 255.0, 0 / 255.0, 26 / 255.0), + (179 / 255.0, 0 / 255.0, 50 / 255.0), + (255 / 255.0, 204 / 255.0, 204 / 255.0) + ), + ( + 2016, + (176 / 255.0, 35 / 255.0, 0 / 255.0), + (240 / 255.0, 48 / 255.0, 0 / 255.0), + (255 / 255.0, 204 / 255.0, 204 / 255.0) + ), + ( + 2008, + (255 / 255.0, 153 / 255.0, 193 / 255.0), + (240 / 255.0, 157 / 255.0, 192 / 255.0), + (255 / 255.0, 215 / 255.0, 238 / 255.0) + ), + ( + 2009, + (170 / 255.0, 120 / 255.0, 20 / 255.0), + (165 / 255.0, 120 / 255.0, 50 / 255.0), + (210 / 255.0, 200 / 255.0, 180 / 255.0) + ) +) +PageMode = PythonUtil.Enum('Options, Codes, Extra') + + +class OptionsPage(ShtikerPage.ShtikerPage): + notify = directNotify.newCategory('OptionsPage') + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + + self.optionsTabPage = None + self.codesTabPage = None + self.extraOptionsTabPage = None + self.title = None + self.optionsTab = None + self.codesTab = None + self.extraOptionsTab = None + + def load(self): + ShtikerPage.ShtikerPage.load(self) + + self.optionsTabPage = OptionsTabPage(self) + self.optionsTabPage.hide() + self.codesTabPage = CodesTabPage(self) + self.codesTabPage.hide() + self.extraOptionsTabPage = ExtraOptionsTabPage(self) + self.extraOptionsTabPage.hide() + + self.title = DirectLabel( + parent=self, relief=None, text=TTLocalizer.OptionsPageTitle, + text_scale=0.12, pos=(0, 0, 0.61)) + + gui = loader.loadModel('phase_3.5/models/gui/fishingBook.bam') + normalColor = (1, 1, 1, 1) + clickColor = (0.8, 0.8, 0, 1) + rolloverColor = (0.15, 0.82, 1.0, 1) + diabledColor = (1.0, 0.98, 0.15, 1) + self.optionsTab = DirectButton( + parent=self, relief=None, text=TTLocalizer.OptionsPageTitle, + text_scale=TTLocalizer.OPoptionsTab, text_align=TextNode.ALeft, + text_pos=(0.01, 0.0, 0.0), image=gui.find('**/tabs/polySurface1'), + image_pos=(0.55, 1, -0.91), image_hpr=(0, 0, -90), + image_scale=(0.033, 0.033, 0.035), image_color=normalColor, + image1_color=clickColor, image2_color=rolloverColor, + image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), + command=self.setMode, extraArgs=[PageMode.Options], + pos=(-0.64, 0, 0.77)) + self.codesTab = DirectButton( + parent=self, relief=None, text=TTLocalizer.OptionsPageCodesTab, + text_scale=TTLocalizer.OPoptionsTab, text_align=TextNode.ALeft, + text_pos=(-0.035, 0.0, 0.0), + image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), + image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), + image_color=normalColor, image1_color=clickColor, + image2_color=rolloverColor, image3_color=diabledColor, + text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, + extraArgs=[PageMode.Codes], pos=(-0.12, 0, 0.77)) + self.extraOptionsTab = DirectButton( + parent=self, relief=None, text=TTLocalizer.ExtraOptionsPageTitle, + text_scale=TTLocalizer.OPextraOptionsTab, text_align=TextNode.ALeft, + text_pos=(0.027, 0.0, 0.0), + image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), + image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), + image_color=normalColor, image1_color=clickColor, + image2_color=rolloverColor, image3_color=diabledColor, + text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, + extraArgs=[PageMode.Extra], pos=(0.42, 0, 0.77)) + gui.removeNode() + + def enter(self): + self.setMode(PageMode.Options, updateAnyways=1) + + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + self.optionsTabPage.exit() + self.codesTabPage.exit() + self.extraOptionsTabPage.exit() + + ShtikerPage.ShtikerPage.exit(self) + + def unload(self): + if self.optionsTabPage is not None: + self.optionsTabPage.unload() + self.optionsTabPage = None + + if self.codesTabPage is not None: + self.codesTabPage.unload() + self.codesTabPage = None + + if self.title is not None: + self.title.destroy() + self.title = None + + if self.optionsTab is not None: + self.optionsTab.destroy() + self.optionsTab = None + + if self.codesTab is not None: + self.codesTab.destroy() + self.codesTab = None + + if self.extraOptionsTab is not None: + self.extraOptionsTab.destroy() + self.extraOptionsTab = None + + ShtikerPage.ShtikerPage.unload(self) + + def setMode(self, mode, updateAnyways=0): + messenger.send('wakeup') + + if not updateAnyways: + if self.mode == mode: + return + + self.mode = mode + + if mode == PageMode.Options: + self.title['text'] = TTLocalizer.OptionsPageTitle + self.optionsTab['state'] = DGG.DISABLED + self.optionsTabPage.enter() + self.codesTab['state'] = DGG.NORMAL + self.codesTabPage.exit() + self.extraOptionsTab['state'] = DGG.NORMAL + self.extraOptionsTabPage.exit() + elif mode == PageMode.Codes: + self.title['text'] = TTLocalizer.CdrPageTitle + self.optionsTab['state'] = DGG.NORMAL + self.optionsTabPage.exit() + self.extraOptionsTab['state'] = DGG.NORMAL + self.extraOptionsTabPage.exit() + self.codesTab['state'] = DGG.DISABLED + self.codesTabPage.enter() + elif mode == PageMode.Extra: + self.title['text'] = TTLocalizer.ExtraOptionsPageTitle + self.optionsTab['state'] = DGG.NORMAL + self.optionsTabPage.exit() + self.codesTab['state'] = DGG.NORMAL + self.codesTabPage.exit() + self.extraOptionsTab['state'] = DGG.DISABLED + self.extraOptionsTabPage.enter() + +class OptionsTabPage(DirectFrame): + notify = directNotify.newCategory('OptionsTabPage') + DisplaySettingsTaskName = 'save-display-settings' + DisplaySettingsDelay = 60 + ChangeDisplaySettings = base.config.GetBool('change-display-settings', 1) + ChangeDisplayAPI = base.config.GetBool('change-display-api', 0) + + def __init__(self, parent = aspect2d): + self.parent = parent + self.currentSizeIndex = None + + DirectFrame.__init__(self, parent=self.parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + + self.load() + + def destroy(self): + self.parent = None + + DirectFrame.destroy(self) + + def load(self): + self.displaySettings = None + self.displaySettingsChanged = 0 + self.displaySettingsSize = (None, None) + self.displaySettingsFullscreen = None + self.displaySettingsApi = None + self.displaySettingsApiChanged = 0 + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + circleModel = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_nameShop') + titleHeight = 0.61 + textStartHeight = 0.45 + textRowHeight = 0.145 + leftMargin = -0.72 + buttonbase_xcoord = 0.35 + buttonbase_ycoord = 0.45 + button_image_scale = (0.7, 1, 1) + button_textpos = (0, -0.02) + options_text_scale = 0.052 + disabled_arrow_color = Vec4(0.6, 0.6, 0.6, 1.0) + self.speed_chat_scale = 0.055 + self.Music_Label = DirectLabel(parent=self, relief=None, text=TTLocalizer.OptionsPageMusic, text_align=TextNode.ALeft, text_scale=options_text_scale, pos=(leftMargin, 0, textStartHeight)) + self.SoundFX_Label = DirectLabel(parent=self, relief=None, text=TTLocalizer.OptionsPageSFX, text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=16, pos=(leftMargin, 0, textStartHeight - textRowHeight)) + self.Friends_Label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=16, pos=(leftMargin, 0, textStartHeight - 3 * textRowHeight)) + self.Whispers_Label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=16, pos=(leftMargin, 0, textStartHeight - 4 * textRowHeight)) + self.DisplaySettings_Label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=10, pos=(leftMargin, 0, textStartHeight - 5 * textRowHeight)) + self.SpeedChatStyle_Label = DirectLabel(parent=self, relief=None, text=TTLocalizer.OptionsPageSpeedChatStyleLabel, text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=10, pos=(leftMargin, 0, textStartHeight - 6 * textRowHeight)) + self.ToonChatSounds_Label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=15, pos=(leftMargin, 0, textStartHeight - 2 * textRowHeight + 0.025)) + self.ToonChatSounds_Label.setScale(0.9) + self.Music_toggleSlider = DirectSlider(parent=self, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord), + value=settings['musicVol']*100, pageSize=5, range=(0, 100), command=self.__doMusicLevel, + thumb_geom=(circleModel.find('**/tt_t_gui_mat_namePanelCircle')), thumb_relief=None, thumb_geom_scale=2) + self.Music_toggleSlider.setScale(0.25) + self.SoundFX_toggleSlider = DirectSlider(parent=self, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - textRowHeight), + value=settings['sfxVol']*100, pageSize=5, range=(0, 100), command=self.__doSfxLevel, + thumb_geom=(circleModel.find('**/tt_t_gui_mat_namePanelCircle')), thumb_relief=None, thumb_geom_scale=2) + self.SoundFX_toggleSlider.setScale(0.25) + self.Friends_toggleButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=button_image_scale, text='', text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - textRowHeight * 3), command=self.__doToggleAcceptFriends) + self.Whispers_toggleButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=button_image_scale, text='', text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - textRowHeight * 4), command=self.__doToggleAcceptWhispers) + self.DisplaySettingsButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image3_color=Vec4(0.5, 0.5, 0.5, 0.5), image_scale=button_image_scale, text=TTLocalizer.OptionsPageChange, text3_fg=(0.5, 0.5, 0.5, 0.75), text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - textRowHeight * 5), command=self.__doDisplaySettings) + self.speedChatStyleLeftArrow = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), scale=(-1.0, 1.0, 1.0), pos=(0.25, 0, buttonbase_ycoord - textRowHeight * 6), command=self.__doSpeedChatStyleLeft) + self.speedChatStyleRightArrow = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), pos=(0.65, 0, buttonbase_ycoord - textRowHeight * 6), command=self.__doSpeedChatStyleRight) + self.ToonChatSounds_toggleButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), + guiButton.find('**/QuitBtn_DN'), + guiButton.find('**/QuitBtn_RLVR'), + guiButton.find('**/QuitBtn_UP')), image3_color=Vec4(0.5, 0.5, 0.5, 0.5), image_scale=button_image_scale, text='', text3_fg=(0.5, 0.5, 0.5, 0.75), text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - textRowHeight * 2 + 0.025), command=self.__doToggleToonChatSounds) + self.ToonChatSounds_toggleButton.setScale(0.8) + self.speedChatStyleText = SpeedChat.SpeedChat(name='OptionsPageStyleText', structure=[2000], backgroundModelName='phase_3/models/gui/ChatPanel', guiModelName='phase_3.5/models/gui/speedChatGui') + self.speedChatStyleText.setScale(self.speed_chat_scale) + self.speedChatStyleText.setPos(0.37, 0, buttonbase_ycoord - textRowHeight * 6 + 0.03) + self.speedChatStyleText.reparentTo(self, DGG.FOREGROUND_SORT_INDEX) + self.exitButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=1.15, text=TTLocalizer.OptionsPageExitToontown, text_scale=options_text_scale, text_pos=button_textpos, textMayChange=0, pos=(0.45, 0, -0.6), command=self.__handleExitShowWithConfirm) + guiButton.removeNode() + gui.removeNode() + + def enter(self): + self.show() + taskMgr.remove(self.DisplaySettingsTaskName) + self.settingsChanged = 0 + self.__setAcceptFriendsButton() + self.__setAcceptWhispersButton() + self.__setDisplaySettings() + self.__setToonChatSoundsButton() + self.speedChatStyleText.enter() + self.speedChatStyleIndex = base.localAvatar.getSpeedChatStyleIndex() + self.updateSpeedChatStyle() + if self.parent.book.safeMode: + self.exitButton.hide() + else: + self.exitButton.show() + + def exit(self): + self.ignore('confirmDone') + self.hide() + self.speedChatStyleText.exit() + if self.displaySettingsChanged: + taskMgr.doMethodLater(self.DisplaySettingsDelay, self.writeDisplaySettings, self.DisplaySettingsTaskName) + + def unload(self): + self.writeDisplaySettings() + taskMgr.remove(self.DisplaySettingsTaskName) + if self.displaySettings != None: + self.ignore(self.displaySettings.doneEvent) + self.displaySettings.unload() + self.displaySettings = None + self.exitButton.destroy() + self.Music_toggleSlider.destroy() + self.SoundFX_toggleSlider.destroy() + self.Friends_toggleButton.destroy() + self.Whispers_toggleButton.destroy() + self.DisplaySettingsButton.destroy() + self.speedChatStyleLeftArrow.destroy() + self.speedChatStyleRightArrow.destroy() + del self.exitButton + del self.SoundFX_Label + del self.Music_Label + del self.Friends_Label + del self.Whispers_Label + del self.SpeedChatStyle_Label + del self.SoundFX_toggleSlider + del self.Music_toggleSlider + del self.Friends_toggleButton + del self.Whispers_toggleButton + del self.speedChatStyleLeftArrow + del self.speedChatStyleRightArrow + self.speedChatStyleText.exit() + self.speedChatStyleText.destroy() + del self.speedChatStyleText + self.currentSizeIndex = None + + def __doMusicLevel(self): + vol = self.Music_toggleSlider['value'] + vol = float(vol) / 100 + settings['musicVol'] = vol + base.musicManager.setVolume(vol) + base.musicActive = vol > 0.0 + + def __doSfxLevel(self): + vol = self.SoundFX_toggleSlider['value'] + vol = float(vol) / 100 + settings['sfxVol'] = vol + for sfm in base.sfxManagerList: + sfm.setVolume(vol) + base.sfxActive = vol > 0.0 + self.__setToonChatSoundsButton() + + def __doToggleToonChatSounds(self): + messenger.send('wakeup') + if base.toonChatSounds: + base.toonChatSounds = 0 + settings['toonChatSounds'] = False + else: + base.toonChatSounds = 1 + settings['toonChatSounds'] = True + self.settingsChanged = 1 + self.__setToonChatSoundsButton() + + def __setToonChatSoundsButton(self): + if base.toonChatSounds: + self.ToonChatSounds_Label['text'] = TTLocalizer.OptionsPageToonChatSoundsOnLabel + self.ToonChatSounds_toggleButton['text'] = TTLocalizer.OptionsPageToggleOff + else: + self.ToonChatSounds_Label['text'] = TTLocalizer.OptionsPageToonChatSoundsOffLabel + self.ToonChatSounds_toggleButton['text'] = TTLocalizer.OptionsPageToggleOn + if base.sfxActive: + self.ToonChatSounds_Label.setColorScale(1.0, 1.0, 1.0, 1.0) + self.ToonChatSounds_toggleButton['state'] = DGG.NORMAL + else: + self.ToonChatSounds_Label.setColorScale(0.5, 0.5, 0.5, 0.5) + self.ToonChatSounds_toggleButton['state'] = DGG.DISABLED + + def __doToggleAcceptFriends(self): + messenger.send('wakeup') + acceptingNewFriends = settings.get('acceptingNewFriends', {}) + if base.localAvatar.acceptingNewFriends: + base.localAvatar.acceptingNewFriends = 0 + acceptingNewFriends[str(base.localAvatar.doId)] = False + else: + base.localAvatar.acceptingNewFriends = 1 + acceptingNewFriends[str(base.localAvatar.doId)] = True + settings['acceptingNewFriends'] = acceptingNewFriends + self.settingsChanged = 1 + self.__setAcceptFriendsButton() + + def __doToggleAcceptWhispers(self): + messenger.send('wakeup') + acceptingNonFriendWhispers = settings.get('acceptingNonFriendWhispers', {}) + if base.localAvatar.acceptingNonFriendWhispers: + base.localAvatar.acceptingNonFriendWhispers = 0 + acceptingNonFriendWhispers[str(base.localAvatar.doId)] = False + else: + base.localAvatar.acceptingNonFriendWhispers = 1 + acceptingNonFriendWhispers[str(base.localAvatar.doId)] = True + settings['acceptingNonFriendWhispers'] = acceptingNonFriendWhispers + self.settingsChanged = 1 + self.__setAcceptWhispersButton() + + def __setAcceptFriendsButton(self): + if base.localAvatar.acceptingNewFriends: + self.Friends_Label['text'] = TTLocalizer.OptionsPageFriendsEnabledLabel + self.Friends_toggleButton['text'] = TTLocalizer.OptionsPageToggleOff + else: + self.Friends_Label['text'] = TTLocalizer.OptionsPageFriendsDisabledLabel + self.Friends_toggleButton['text'] = TTLocalizer.OptionsPageToggleOn + + def __setAcceptWhispersButton(self): + if base.localAvatar.acceptingNonFriendWhispers: + self.Whispers_Label['text'] = TTLocalizer.OptionsPageWhisperEnabledLabel + self.Whispers_toggleButton['text'] = TTLocalizer.OptionsPageToggleOff + else: + self.Whispers_Label['text'] = TTLocalizer.OptionsPageWhisperDisabledLabel + self.Whispers_toggleButton['text'] = TTLocalizer.OptionsPageToggleOn + + def __doDisplaySettings(self): + if self.displaySettings == None: + self.displaySettings = DisplaySettingsDialog.DisplaySettingsDialog() + self.displaySettings.load() + self.accept(self.displaySettings.doneEvent, self.__doneDisplaySettings) + self.displaySettings.enter(self.ChangeDisplaySettings, self.ChangeDisplayAPI) + + def __doneDisplaySettings(self, anyChanged, apiChanged): + if anyChanged: + self.__setDisplaySettings() + properties = base.win.getProperties() + self.displaySettingsChanged = 1 + self.displaySettingsSize = (properties.getXSize(), properties.getYSize()) + self.displaySettingsFullscreen = properties.getFullscreen() + self.displaySettingsApi = base.pipe.getInterfaceName() + self.displaySettingsApiChanged = apiChanged + + def __setDisplaySettings(self): + properties = base.win.getProperties() + if properties.getFullscreen(): + screensize = '%s x %s' % (properties.getXSize(), properties.getYSize()) + else: + screensize = TTLocalizer.OptionsPageDisplayWindowed + api = base.pipe.getInterfaceName() + settings = {'screensize': screensize, + 'api': api} + if self.ChangeDisplayAPI: + OptionsPage.notify.debug('change display settings...') + text = TTLocalizer.OptionsPageDisplaySettings % settings + else: + OptionsPage.notify.debug('no change display settings...') + text = TTLocalizer.OptionsPageDisplaySettingsNoApi % settings + self.DisplaySettings_Label['text'] = text + + def __doSpeedChatStyleLeft(self): + if self.speedChatStyleIndex > 0: + self.speedChatStyleIndex = self.speedChatStyleIndex - 1 + self.updateSpeedChatStyle() + + def __doSpeedChatStyleRight(self): + if self.speedChatStyleIndex < len(speedChatStyles) - 1: + self.speedChatStyleIndex = self.speedChatStyleIndex + 1 + self.updateSpeedChatStyle() + + def updateSpeedChatStyle(self): + nameKey, arrowColor, rolloverColor, frameColor = speedChatStyles[self.speedChatStyleIndex] + newSCColorScheme = SCColorScheme.SCColorScheme(arrowColor=arrowColor, rolloverColor=rolloverColor, frameColor=frameColor) + self.speedChatStyleText.setColorScheme(newSCColorScheme) + self.speedChatStyleText.clearMenu() + colorName = SCStaticTextTerminal.SCStaticTextTerminal(nameKey) + self.speedChatStyleText.append(colorName) + self.speedChatStyleText.finalize() + self.speedChatStyleText.setPos(0.445 - self.speedChatStyleText.getWidth() * self.speed_chat_scale / 2, 0, self.speedChatStyleText.getPos()[2]) + if self.speedChatStyleIndex > 0: + self.speedChatStyleLeftArrow['state'] = DGG.NORMAL + else: + self.speedChatStyleLeftArrow['state'] = DGG.DISABLED + if self.speedChatStyleIndex < len(speedChatStyles) - 1: + self.speedChatStyleRightArrow['state'] = DGG.NORMAL + else: + self.speedChatStyleRightArrow['state'] = DGG.DISABLED + base.localAvatar.b_setSpeedChatStyleIndex(self.speedChatStyleIndex) + + def writeDisplaySettings(self, task=None): + if not self.displaySettingsChanged: + return + taskMgr.remove(self.DisplaySettingsTaskName) + settings['res'] = (self.displaySettingsSize[0], self.displaySettingsSize[1]) + settings['fullscreen'] = self.displaySettingsFullscreen + return Task.done + + def __handleExitShowWithConfirm(self): + self.confirm = TTDialog.TTGlobalDialog(doneEvent='confirmDone', message=TTLocalizer.OptionsPageExitConfirm, style=TTDialog.TwoChoice) + self.confirm.show() + self.parent.doneStatus = {'mode': 'exit', + 'exitTo': 'closeShard'} + self.accept('confirmDone', self.__handleConfirm) + + def __handleConfirm(self): + status = self.confirm.doneStatus + self.ignore('confirmDone') + self.confirm.cleanup() + del self.confirm + if status == 'ok': + base.cr._userLoggingOut = True + messenger.send(self.parent.doneEvent) + + +class CodesTabPage(DirectFrame): + notify = directNotify.newCategory('CodesTabPage') + + def __init__(self, parent = aspect2d): + self.parent = parent + DirectFrame.__init__(self, parent=self.parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + self.load() + return + + def destroy(self): + self.parent = None + DirectFrame.destroy(self) + return + + def load(self): + self.notice = DirectLabel(parent=self, relief=None, text='NOTICE: All codes can only be entered once!', text_scale=0.06, pos=(0.0, 0, 0.53), text_fg=Vec4(1.0, 0, 0, 1)) + cdrGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_sbk_codeRedemptionGui') + instructionGui = cdrGui.find('**/tt_t_gui_sbk_cdrPresent') + flippyGui = cdrGui.find('**/tt_t_gui_sbk_cdrFlippy') + codeBoxGui = cdrGui.find('**/tt_t_gui_sbk_cdrCodeBox') + self.resultPanelSuccessGui = cdrGui.find('**/tt_t_gui_sbk_cdrResultPanel_success') + self.resultPanelFailureGui = cdrGui.find('**/tt_t_gui_sbk_cdrResultPanel_failure') + self.resultPanelErrorGui = cdrGui.find('**/tt_t_gui_sbk_cdrResultPanel_error') + self.successSfx = base.loadSfx('phase_3.5/audio/sfx/tt_s_gui_sbk_cdrSuccess.ogg') + self.failureSfx = base.loadSfx('phase_3.5/audio/sfx/tt_s_gui_sbk_cdrFailure.ogg') + self.instructionPanel = DirectFrame(parent=self, relief=None, image=instructionGui, image_scale=0.8, text=TTLocalizer.CdrInstructions, text_pos=TTLocalizer.OPCodesInstructionPanelTextPos, text_align=TextNode.ACenter, text_scale=TTLocalizer.OPCodesResultPanelTextScale, text_wordwrap=TTLocalizer.OPCodesInstructionPanelTextWordWrap, pos=(-0.429, 0, -0.05)) + self.codeBox = DirectFrame(parent=self, relief=None, image=codeBoxGui, pos=(0.433, 0, 0.35)) + self.flippyFrame = DirectFrame(parent=self, relief=None, image=flippyGui, pos=(0.44, 0, -0.353)) + self.codeInput = DirectEntry(parent=self.codeBox, relief=DGG.GROOVE, scale=0.08, pos=(-0.33, 0, -0.006), borderWidth=(0.05, 0.05), frameColor=((1, 1, 1, 1), (1, 1, 1, 1), (0.5, 0.5, 0.5, 0.5)), state=DGG.NORMAL, text_align=TextNode.ALeft, text_scale=TTLocalizer.OPCodesInputTextScale, width=10.5, numLines=1, focus=1, backgroundFocus=0, cursorKeys=1, text_fg=(0, 0, 0, 1), suppressMouse=1, autoCapitalize=0, command=self.__submitCode) + submitButtonGui = loader.loadModel('phase_3/models/gui/quit_button') + self.submitButton = DirectButton(parent=self, relief=None, image=(submitButtonGui.find('**/QuitBtn_UP'), + submitButtonGui.find('**/QuitBtn_DN'), + submitButtonGui.find('**/QuitBtn_RLVR'), + submitButtonGui.find('**/QuitBtn_UP')), image3_color=Vec4(0.5, 0.5, 0.5, 0.5), image_scale=1.15, state=DGG.NORMAL, text=TTLocalizer.NameShopSubmitButton, text_scale=TTLocalizer.OPCodesSubmitTextScale, text_align=TextNode.ACenter, text_pos=TTLocalizer.OPCodesSubmitTextPos, text3_fg=(0.5, 0.5, 0.5, 0.75), textMayChange=0, pos=(0.45, 0.0, 0.0896), command=self.__submitCode) + self.resultPanel = DirectFrame(parent=self, relief=None, image=self.resultPanelSuccessGui, text='', text_pos=TTLocalizer.OPCodesResultPanelTextPos, text_align=TextNode.ACenter, text_scale=TTLocalizer.OPCodesResultPanelTextScale, text_wordwrap=TTLocalizer.OPCodesResultPanelTextWordWrap, pos=(-0.42, 0, -0.0567)) + self.resultPanel.hide() + closeButtonGui = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.closeButton = DirectButton(parent=self.resultPanel, pos=(0.296, 0, -0.466), relief=None, state=DGG.NORMAL, image=(closeButtonGui.find('**/CloseBtn_UP'), closeButtonGui.find('**/CloseBtn_DN'), closeButtonGui.find('**/CloseBtn_Rllvr')), image_scale=(1, 1, 1), command=self.__hideResultPanel) + closeButtonGui.removeNode() + cdrGui.removeNode() + submitButtonGui.removeNode() + return + + def enter(self): + self.show() + localAvatar.chatMgr.fsm.request('otherDialog') + self.codeInput['focus'] = 1 + self.codeInput.enterText('') + self.__enableCodeEntry() + + def exit(self): + self.resultPanel.hide() + self.hide() + localAvatar.chatMgr.fsm.request('mainMenu') + + def unload(self): + self.instructionPanel.destroy() + self.instructionPanel = None + self.codeBox.destroy() + self.codeBox = None + self.flippyFrame.destroy() + self.flippyFrame = None + self.codeInput.destroy() + self.codeInput = None + self.submitButton.destroy() + self.submitButton = None + self.resultPanel.destroy() + self.resultPanel = None + self.closeButton.destroy() + self.closeButton = None + del self.successSfx + del self.failureSfx + return + + def __submitCode(self, input = None): + if input == None: + input = self.codeInput.get() + self.codeInput['focus'] = 1 + if input == '': + return + messenger.send('wakeup') + if hasattr(base.cr, 'codeRedemptionMgr'): + base.cr.codeRedemptionMgr.redeemCode(input, self.__getCodeResult) + self.codeInput.enterText('') + self.__disableCodeEntry() + return + + def __getCodeResult(self, result): + self.notify.debug('result = %s' % result) + self.__enableCodeEntry() + if result == 0: + self.resultPanel['image'] = self.resultPanelSuccessGui + self.resultPanel['text'] = TTLocalizer.CdrResultSuccess + elif result == 1: + self.resultPanel['image'] = self.resultPanelFailureGui + self.resultPanel['text'] = TTLocalizer.CdrResultInvalidCode + elif result == 2: + self.resultPanel['image'] = self.resultPanelFailureGui + self.resultPanel['text'] = TTLocalizer.CdrResultExpiredCode + elif result == 3: + self.resultPanel['image'] = self.resultPanelErrorGui + elif result == 4: + self.resultPanel['image'] = self.resultPanelErrorGui + self.resultPanel['text'] = TTLocalizer.CdrResultAlreadyRedeemed + elif result == 5: + self.resultPanel['image'] = self.resultPanelErrorGui + self.resultPanel['text'] = TTLocalizer.CdrResultNotReady + elif result == 6: + self.resultPanel['image'] = self.resultPanelErrorGui + self.resultPanel['text'] = TTLocalizer.CdrResultNotEligible + if result == 0: + self.successSfx.play() + else: + self.failureSfx.play() + self.resultPanel.show() + + def __hideResultPanel(self): + self.resultPanel.hide() + + def __disableCodeEntry(self): + self.codeInput['state'] = DGG.DISABLED + self.submitButton['state'] = DGG.DISABLED + + def __enableCodeEntry(self): + self.codeInput['state'] = DGG.NORMAL + self.codeInput['focus'] = 1 + self.submitButton['state'] = DGG.NORMAL + +class ExtraOptionsTabPage(DirectFrame): + notify = directNotify.newCategory('ExtraOptionsTabPage') + + def __init__(self, parent = aspect2d): + self.parent = parent + self.currentSizeIndex = None + self.optionChoosers = {} + + DirectFrame.__init__(self, parent=self.parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + + self.load() + + def destroy(self): + self.parent = None + DirectFrame.destroy(self) + + def load(self): + self.optionChoosers = {} + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + circleModel = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_nameShop') + titleHeight = 0.61 + textStartHeight = 0.45 + textRowHeight = 0.145 + leftMargin = -0.72 + buttonbase_xcoord = 0.35 + buttonbase_ycoord = 0.45 + button_image_scale = (0.7, 1, 1) + button_textpos = (0, -0.02) + options_text_scale = 0.052 + disabled_arrow_color = Vec4(0.6, 0.6, 0.6, 1.0) + button_image = (guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')) + self.speed_chat_scale = 0.055 + self.fov_label = DirectLabel(parent=self, relief=None, text=TTLocalizer.FieldOfViewLabel, text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=16, pos=(leftMargin, 0, textStartHeight)) + self.cogInterface_label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=16, pos=(leftMargin, 0, textStartHeight - textRowHeight)) + self.tpTransition_label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=16, pos=(leftMargin, 0, textStartHeight - 2 * textRowHeight)) + self.fpsMeter_label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=16, pos=(leftMargin, 0, textStartHeight - 3 * textRowHeight)) + self.teleport_label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=16, pos=(leftMargin, 0, textStartHeight - 4 * textRowHeight)) + self.fov_slider = DirectSlider(parent=self, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord), + value=settings['fov'], pageSize=5, range=(ToontownGlobals.DefaultCameraFov, ToontownGlobals.MaxCameraFov), command=self.__doFov, + thumb_geom=(circleModel.find('**/tt_t_gui_mat_namePanelCircle')), thumb_relief=None, thumb_geom_scale=2) + self.fov_slider.setScale(0.25) + self.cogInterface_toggleButton = DirectButton(parent=self, relief=None, image=button_image, image_scale=button_image_scale, text='', text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - textRowHeight), command=self.__doToggleCogInterface) + self.tpTransition_toggleButton = DirectButton(parent=self, relief=None, image=button_image, image_scale=button_image_scale, text='', text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - 2 * textRowHeight), command=self.__doToggleTpTransition) + self.fpsMeter_toggleButton = DirectButton(parent=self, relief=None, image=button_image, image_scale=button_image_scale, text='', text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - 3 * textRowHeight), command=self.__doToggleFpsMeter) + self.teleport_toggleButton = DirectButton(parent=self, relief=None, image=button_image, image_scale=button_image_scale, text='', text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - 4 * textRowHeight), command=self.__doToggleTeleport) + self.bugReportButton = DirectButton(parent=self, relief=None, text=TTLocalizer.BugReportButton, image=button_image, image_scale=button_image_scale, text_pos=(0, -0.01), text_fg=(0, 0, 0, 1), + command=self.showReportNotice, pos=(0.0, 0.0, -0.6), text_scale=(0.045)) + guiButton.removeNode() + circleModel.removeNode() + + self.optionChoosers['pole'] = OptionChooser.OptionChooser(self, TTLocalizer.FishingPoleLabel, 5, self.__updateFishingPole, [False], self.__applyFishingPole) + self.optionChoosers['nametag_style'] = OptionChooser.OptionChooser(self, TTLocalizer.NametagStyleLabel, 6, self.__updateNametagStyle, [False], self.__applyNametagStyle) + + def enter(self): + self.show() + self.settingsChanged = 0 + self.__setCogInterfaceButton() + self.__setTpTransitionButton() + self.__setFpsMeterButton() + self.__setTeleportButton() + self.__updateNametagStyle() + self.__updateFishingPole() + self.accept('refreshNametagStyle', self.__updateNametagStyle) + self.accept('refreshFishingRod', self.__updateFishingPole) + + def exit(self): + self.ignoreAll() + self.destroyReportNotice() + self.hide() + + for chooser in self.optionChoosers.values(): + chooser.exit(chooser.index) + + def unload(self): + self.fov_label.destroy() + del self.fov_label + self.fov_slider.destroy() + del self.fov_slider + self.cogInterface_label.destroy() + del self.cogInterface_label + self.cogInterface_toggleButton.destroy() + del self.cogInterface_label + self.tpTransition_label.destroy() + del self.tpTransition_label + self.tpTransition_toggleButton.destroy() + del self.tpTransition_toggleButton + self.fpsMeter_label.destroy() + del self.fpsMeter_label + self.fpsMeter_toggleButton.destroy() + del self.fpsMeter_toggleButton + self.teleport_label.destroy() + del self.teleport_label + self.teleport_toggleButton.destroy() + del self.teleport_toggleButton + self.bugReportButton.destroy() + del self.bugReportButton + self.destroyReportNotice() + + for chooser in self.optionChoosers.values(): + optionChooser.unload() + + def __doFov(self): + fov = self.fov_slider['value'] + settings['fov'] = fov + base.camLens.setMinFov(fov/(4./3.)) + + def __doToggleCogInterface(self): + messenger.send('wakeup') + settings['cogInterface'] = not settings['cogInterface'] + self.settingsChanged = 1 + self.__setCogInterfaceButton() + + def __setCogInterfaceButton(self): + self.cogInterface_label['text'] = TTLocalizer.CogInterfaceLabelOn if settings['cogInterface'] else TTLocalizer.CogInterfaceLabelOff + self.cogInterface_toggleButton['text'] = TTLocalizer.OptionsPageToggleOff if settings['cogInterface'] else TTLocalizer.OptionsPageToggleOn + + def __doToggleTpTransition(self): + messenger.send('wakeup') + settings['tpTransition'] = not settings['tpTransition'] + self.settingsChanged = 1 + self.__setTpTransitionButton() + + def __setTpTransitionButton(self): + self.tpTransition_label['text'] = TTLocalizer.TpTransitionLabelOn if settings['tpTransition'] else TTLocalizer.TpTransitionLabelOff + self.tpTransition_toggleButton['text'] = TTLocalizer.OptionsPageToggleOff if settings['tpTransition'] else TTLocalizer.OptionsPageToggleOn + + def __doToggleFpsMeter(self): + messenger.send('wakeup') + settings['fpsMeter'] = not settings['fpsMeter'] + base.setFrameRateMeter(settings['fpsMeter']) + self.settingsChanged = 1 + self.__setFpsMeterButton() + + def __setFpsMeterButton(self): + self.fpsMeter_label['text'] = TTLocalizer.FpsMeterLabelOn if settings['fpsMeter'] else TTLocalizer.FpsMeterLabelOff + self.fpsMeter_toggleButton['text'] = TTLocalizer.OptionsPageToggleOff if settings['fpsMeter'] else TTLocalizer.OptionsPageToggleOn + + def __doToggleTeleport(self): + messenger.send('wakeup') + acceptingTeleport = settings.get('acceptingTeleport', {}) + if base.localAvatar.acceptingTeleport: + base.localAvatar.acceptingTeleport = 0 + acceptingTeleport[str(base.localAvatar.doId)] = False + else: + base.localAvatar.acceptingTeleport = 1 + acceptingTeleport[str(base.localAvatar.doId)] = True + settings['acceptingTeleport'] = acceptingTeleport + self.settingsChanged = 1 + self.__setTeleportButton() + + def __setTeleportButton(self): + self.teleport_label['text'] = TTLocalizer.TeleportLabelOn if base.localAvatar.acceptingTeleport else TTLocalizer.TeleportLabelOff + self.teleport_toggleButton['text'] = TTLocalizer.OptionsPageToggleOff if base.localAvatar.acceptingTeleport else TTLocalizer.OptionsPageToggleOn + + def __updateNametagStyle(self, resetIndex=True): + chooser = self.optionChoosers['nametag_style'] + + if resetIndex: + chooser.setIndex(base.localAvatar.nametagStyles.index(base.localAvatar.getNametagStyle())) + + nametagId = base.localAvatar.nametagStyles[chooser.index] + chooser.setDisplayText('%s\n%s' % (base.localAvatar.getName(), TTLocalizer.NametagFontNames[nametagId])) + chooser.setDisplayFont(ToontownGlobals.getNametagFont(nametagId)) + chooser.decideButtons(0, len(base.localAvatar.nametagStyles) - 1) + + def __applyNametagStyle(self, index): + if index != -1 and index != base.localAvatar.nametagStyles.index(base.localAvatar.getNametagStyle()): + base.localAvatar.requestNametagStyle(base.localAvatar.nametagStyles[index]) + + def __updateFishingPole(self, resetIndex=True): + chooser = self.optionChoosers['pole'] + + if resetIndex: + chooser.setIndex(base.localAvatar.getFishingRod()) + + chooser.setDisplayText(TTLocalizer.FishingRodNameDict[chooser.index]) + chooser.decideButtons(0, base.localAvatar.maxFishingRod) + + def __applyFishingPole(self, index): + if index != -1 and index != base.localAvatar.getFishingRod(): + base.localAvatar.requestFishingRod(index) + + def destroyReportNotice(self): + if hasattr(self, 'dialog'): + self.dialog.destroy() + del self.dialog + + def showReportNotice(self): + self.destroyReportNotice() + self.dialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.BugReportNotice, command=self.confirmBugReport) + self.dialog.show() + + def confirmBugReport(self, value): + self.destroyReportNotice() + + if value > 0: + webbrowser.open(ToontownGlobals.BugReportSite, new=2, autoraise=True) diff --git a/toontown/shtiker/PhotoAlbumPage.py b/toontown/shtiker/PhotoAlbumPage.py new file mode 100755 index 00000000..c3a008dd --- /dev/null +++ b/toontown/shtiker/PhotoAlbumPage.py @@ -0,0 +1,314 @@ +from panda3d.core import * +import ShtikerPage +import ShtikerBook +from direct.gui.DirectGui import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer +import os +import string +from toontown.toonbase import ToontownGlobals +from sys import platform + +class PhotoAlbumPage(ShtikerPage.ShtikerPage): + + notify = DirectNotifyGlobal.directNotify.newCategory('PhotoAlbumPage') + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.textRolloverColor = Vec4(1, 1, 0, 1) + self.textDownColor = Vec4(0.5, 0.9, 1, 1) + self.textDisabledColor = Vec4(0.4, 0.8, 0.4, 1) + self.photos = {} + self.selectedFileName = None + self.selectedFilePath = None + self.installPath = os.getcwd() + self.photoPath = TTLocalizer.ScreenshotPath + self.photoIndex = 0 + return + + def load(self): + if not os.path.exists(self.photoPath): + os.mkdir(self.photoPath) + self.notify.info('Made new directory to save screenshots.') + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.PhotoPageTitle, text_scale=0.1, pos=(0, 0, 0.6)) + self.pictureImage = loader.loadModel('phase_3.5/models/gui/photo_frame') + self.pictureImage.setScale(0.2) + self.pictureImage.setPos(0.44, 0, 0.25) + self.pictureImage.reparentTo(self) + self.pictureFg = self.pictureImage.find('**/fg') + self.pictureFg.setColor(1, 1, 1, 0.1) + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.pictureCaption = DirectLabel(parent=self, relief=None, text=TTLocalizer.PhotoPageAddName, text_scale=0.05, text_wordwrap=10, text_align=TextNode.ACenter, pos=(0.45, 0, -0.22)) + self.renameButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(1, 1, 1), pos=(0.40, 0, -0.35), text=TTLocalizer.PhotoPageAddName, text_scale=0.06, text_pos=(0, -0.02), command=self.renameImage, state=DGG.DISABLED) + self.directoryButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(1.3, 1, 1), pos=(0.46, 0, -0.50), text=TTLocalizer.PhotoPageDirectory, text_scale=0.06, text_pos=(0, -0.02), command=self.openPhotoDirectory, state=DGG.NORMAL) + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') + self.deleteButton = DirectButton(parent=self, image=(trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_RLVR')), text=('', TTLocalizer.AvatarChoiceDelete, TTLocalizer.AvatarChoiceDelete), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_pos=(0, -0.1), text_font=ToontownGlobals.getInterfaceFont(), textMayChange=0, relief=None, pos=(0.68, 0, -0.33), scale=0.4, state=DGG.DISABLED, command=self.deleteImage) + guiButton.removeNode() + trashcanGui.removeNode() + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.scrollList = DirectScrolledList(parent=self, relief=None, forceHeight=0.07, pos=(-0.5, 0, 0), incButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_scale=(1.3, 1.3, -1.3), incButton_pos=(0.08, 0, -0.60), incButton_image3_color=Vec4(1, 1, 1, 0.2), decButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_scale=(1.3, 1.3, 1.3), decButton_pos=(0.08, 0, 0.52), decButton_image3_color=Vec4(1, 1, 1, 0.2), itemFrame_pos=(-0.237, 0, 0.41), itemFrame_scale=1.0, itemFrame_relief=DGG.SUNKEN, itemFrame_frameSize=(-0.05, + 0.66, + -0.98, + 0.07), itemFrame_frameColor=(0.85, 0.95, 1, 1), itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=13, items=[]) + self.renamePanel = DirectFrame(parent=self, relief=None, pos=(0.45, 0, -0.45), image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.6), text=TTLocalizer.PhotoPageAddNamePanel, text_scale=0.06, text_pos=(0.0, 0.13), sortOrder=NO_FADE_SORT_INDEX) + self.renameEntry = DirectEntry(parent=self.renamePanel, relief=DGG.SUNKEN, scale=0.06, pos=(-0.3, 0, 0), borderWidth=(0.1, 0.1), numLines=1, cursorKeys=1, frameColor=(0.8, 0.8, 0.5, 1), frameSize=(-0.2, + 10, + -0.4, + 1.1), command=self.renameDialog) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.bCancel = DirectButton(parent=self.renamePanel, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=TTLocalizer.PhotoPageCancel, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, 0.0, -0.1), command=self.renameCancel) + self.renamePanel.hide() + self.deletePanel = DirectFrame(parent=self, relief=None, pos=(0.45, 0, -0.45), image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.6), text='', text_scale=0.06, text_pos=(0.0, 0.13), sortOrder=NO_FADE_SORT_INDEX) + self.dOk = DirectButton(parent=self.deletePanel, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.PhotoPageConfirm, text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.1, 0.0, -0.1), command=self.deleteConfirm) + self.dCancel = DirectButton(parent=self.deletePanel, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=TTLocalizer.PhotoPageCancel, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.1, 0.0, -0.1), command=self.deleteCancel) + self.deletePanel.hide() + self.errorPanel = DirectFrame(parent=self, relief=None, pos=(0.45, 0, -0.45), image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.6), text='', text_wordwrap=16, text_scale=0.06, text_pos=(0.0, 0.13), sortOrder=NO_FADE_SORT_INDEX) + self.bClose = DirectButton(parent=self.errorPanel, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=TTLocalizer.PhotoPageClose, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, 0.0, -0.1), command=self.errorConfirm) + self.errorPanel.hide() + self.scroll = loader.loadModel('phase_3/models/gui/toon_council').find('**/scroll') + self.scroll.reparentTo(self) + self.scroll.setPos(0.0, 1.0, 0.2) + self.scroll.setScale(0.6, 0.6, 0.6) + self.tip = DirectLabel(parent=self.scroll, relief=None, text=TTLocalizer.PhotoPageTutorial, text_scale=0.13, pos=(0.0, 0.0, 0.1), text_fg=(0.4, 0.3, 0.2, 1), text_wordwrap=18, text_align=TextNode.ACenter) + self.leftArrow = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), scale=(-1.0, 1.0, 1.0), pos=(0.15, 0, -0.21), command=self.prevPhoto) + self.rightArrow = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), pos=(0.75, 0, -0.21), command=self.nextPhoto) + gui.removeNode() + buttons.removeNode() + return + + def unload(self): + del self.title + del self.scrollList + del self.pictureImage + del self.pictureFg + del self.pictureCaption + del self.deleteButton + del self.renameButton + del self.directoryButton + del self.renamePanel + del self.renameEntry + del self.scroll + del self.tip + del self.errorPanel + del self.bCancel + del self.bClose + del self.deletePanel + del self.dOk + del self.dCancel + del self.leftArrow + del self.rightArrow + ShtikerPage.ShtikerPage.unload(self) + + def renameDialog(self, str): + if os.path.isfile(self.photoPath + self.selectedFileName): + separator = '_' + validChars = string.letters + string.digits + ' -#&.,' + str = filter(lambda s: s in validChars, str) + oldName = self.selectedFileName + numUnders = oldName.count(separator) + if numUnders == 0: + newName = oldName[0:18] + separator + str + separator + oldName[17:] + elif numUnders == 2: + sp = oldName.split(separator) + newName = sp[0] + separator + str + separator + sp[2] + else: + self.renameCleanup() + return 0 + if str.isspace() or str == '': + self.renameCancel() + else: + os.rename(self.photoPath + oldName, self.photoPath + newName) + self.renameCleanup() + self.updateScrollList() + self.chosePhoto(newName) + return 1 + else: + self.renameCancel() + self.errorPanel['text'] = 'Huh. It looks like this snapshot has been deleted or removed.' + self.errorPanel.show() + self.updateScrollList() + + def renameCancel(self): + self.renameCleanup() + + def renameCleanup(self): + self.renamePanel.hide() + chatEntry = base.localAvatar.chatMgr.chatInputNormal.chatEntry + chatEntry['backgroundFocus'] = self.oldFocus + + def renameImage(self): + self.deleteCleanup() + if self.getPhotoName(self.selectedFileName) == TTLocalizer.PhotoPageNoName: + self.renameEntry.set('') + else: + self.renameEntry.set(self.getPhotoName(self.selectedFileName)) + self.renamePanel.show() + chatEntry = base.localAvatar.chatMgr.chatInputNormal.chatEntry + chatEntry['backgroundFocus'] = 0 + self.renameEntry['focus'] = 1 + self.notify.debug(self.selectedFileName) + + def deleteConfirm(self): + if os.path.isfile(self.photoPath + self.selectedFileName): + os.remove(self.photoPath + self.selectedFileName) + self.selectedFileName = None + self.deleteCleanup() + self.updateScrollList() + return + else: + self.deleteCancel() + self.errorPanel['text'] = 'Huh. It looks like this snapshot has already been deleted.' + self.errorPanel.show() + self.updateScrollList() + + def deleteCancel(self): + self.deleteCleanup() + + def deleteCleanup(self): + self.deletePanel.hide() + + def deleteImage(self): + self.renameCleanup() + self.deletePanel['text'] = TTLocalizer.PhotoPageDelete + '\n"%s"?' % self.getPhotoName(self.selectedFileName) + self.deletePanel.show() + def makePhotoButton(self, fileName): + return DirectButton(relief=None, text=self.getPhotoName(fileName), text_scale=0.06, text_align=TextNode.ALeft, text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, text3_fg=self.textDisabledColor, command=self.chosePhoto, extraArgs=[fileName]) + + def errorConfirm(self): + self.errorPanel.hide() + + def getPhotoName(self, fileName): + separator = '_' + numUnders = fileName.count(separator) + if numUnders == 0: + return TTLocalizer.PhotoPageNoName + elif numUnders == 2: + return fileName.split(separator)[1] + else: + return TTLocalizer.PhotoPageUnknownName + + def chosePhoto(self, fileName): + if fileName: + self.selectedFileName = fileName + self.selectedFilePath = self.photoPath + fileName + if os.path.isfile(self.photoPath + self.selectedFileName): + photoTexture = loader.loadTexture(self.selectedFilePath) + photoName = self.getPhotoName(fileName) + self.pictureFg.setTexture(photoTexture, 1) + self.pictureFg.setColor(1, 1, 1, 1) + self.pictureCaption['text'] = photoName + self.renameButton['state'] = DGG.NORMAL + self.deleteButton['state'] = DGG.NORMAL + self.renameEntry.set(photoName) + else: + self.errorPanel['text'] = 'Huh. It looks like this snapshot has been deleted or removed.' + self.errorPanel.show() + self.updateScrollList() + else: + self.selectedFileName = None + self.pictureFg.clearTexture() + self.pictureFg.setColor(1, 1, 1, 0.1) + self.pictureCaption['text'] = '' + self.renameButton['state'] = DGG.DISABLED + self.deleteButton['state'] = DGG.DISABLED + self.renameEntry.set('') + return + + def getPhotos(self): + files = os.listdir(self.photoPath) + photos = [] + for fileName in files: + if fileName[0:17] == 'stride-screenshot' and fileName[-4:] == '.jpg': + photos.append(fileName) + + return photos + + def openPhotoDirectory(self): + if platform == "darwin": + OSXPhotoDir = self.installPath + '/user/screenshots' + os.system('open "%s"' % OSXPhotoDir) + self.notify.debug(OSXPhotoDir) + elif platform == "win32": + PhotoDir = self.installPath + '/user/screenshots/' + os.startfile(PhotoDir) + self.notify.debug(PhotoDir) + + def newScreenshot(self, filename): + self.updateScrollList() + + def updateScrollList(self): + newPhotos = self.getPhotos() + for photo in self.photos.keys(): + if photo not in newPhotos: + photoButton = self.photos[photo] + self.scrollList.removeItem(photoButton) + photoButton.destroy() + del self.photos[photo] + + for photo in newPhotos: + if not photo in self.photos: + photoButton = self.makePhotoButton(photo) + self.scrollList.addItem(photoButton) + self.photos[photo] = photoButton + + if self.photos.keys(): + self.chosePhoto(self.photos.keys()[0]) + self.scroll.hide() + self.scrollList.show() + self.pictureImage.show() + self.rightArrow.show() + self.leftArrow.show() + self.renameButton.show() + self.deleteButton.show() + self.scrollList.show() + else: + self.chosePhoto(None) + self.scroll.show() + self.scrollList.hide() + self.pictureImage.hide() + self.rightArrow.hide() + self.leftArrow.hide() + self.renameButton.hide() + self.deleteButton.hide() + return + + def enter(self): + self.accept('screenshot', self.newScreenshot) + self.updateScrollList() + chatEntry = base.localAvatar.chatMgr.chatInputNormal.chatEntry + self.oldFocus = chatEntry['backgroundFocus'] + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + self.ignore('screenshot') + self.renameCleanup() + self.deleteCleanup() + ShtikerPage.ShtikerPage.exit(self) + + def updateArrows(self): + self.photoIndex = 0 + self.chosePhoto(self.getPhotos()[self.photoIndex]) + + def prevPhoto(self): + try: + self.chosePhoto(self.getPhotos()[self.photoIndex]) + self.photoIndex -= 1 + except:self.photoIndex = 0 + + def nextPhoto(self): + try: + self.chosePhoto(self.getPhotos()[self.photoIndex]) + self.photoIndex += 1 + except:self.photoIndex = 0 diff --git a/toontown/shtiker/PurchaseManager.py b/toontown/shtiker/PurchaseManager.py new file mode 100755 index 00000000..788e13e0 --- /dev/null +++ b/toontown/shtiker/PurchaseManager.py @@ -0,0 +1,95 @@ +from panda3d.core import * +from PurchaseManagerConstants import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal + +class PurchaseManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('PurchaseManager') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.playAgain = 0 + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.ignoreAll() + + def setAvIds(self, *avIds): + self.notify.debug('setAvIds: %s' % (avIds,)) + self.avIds = avIds + + def setNewbieIds(self, newbieIds): + self.notify.debug('setNewbieIds: %s' % (newbieIds,)) + self.newbieIds = newbieIds + + def setMinigamePoints(self, *mpArray): + self.notify.debug('setMinigamePoints: %s' % (mpArray,)) + self.mpArray = mpArray + + def setPlayerMoney(self, *moneyArray): + self.notify.debug('setPlayerMoney: %s' % (moneyArray,)) + self.moneyArray = moneyArray + + def setPlayerStates(self, *stateArray): + self.notify.debug('setPlayerStates: %s' % (stateArray,)) + self.playerStates = stateArray + if self.isGenerated() and self.hasLocalToon: + self.announcePlayerStates() + + def setCountdown(self, timestamp): + self.countdownTimestamp = timestamp + + def announcePlayerStates(self): + messenger.send('purchaseStateChange', [self.playerStates]) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.hasLocalToon = self.calcHasLocalToon() + if self.hasLocalToon: + self.announcePlayerStates() + et = globalClockDelta.localElapsedTime(self.countdownTimestamp) + remain = PURCHASE_COUNTDOWN_TIME - et + self.acceptOnce('purchasePlayAgain', self.playAgainHandler) + self.acceptOnce('purchaseBackToToontown', self.backToToontownHandler) + self.acceptOnce('purchaseTimeout', self.setPurchaseExit) + self.accept('boughtGag', self.__handleBoughtGag) + base.cr.playGame.hood.fsm.request('purchase', [self.mpArray, + self.moneyArray, + self.avIds, + self.playerStates, + remain]) + + def calcHasLocalToon(self): + retval = base.localAvatar.doId not in self.newbieIds and base.localAvatar.doId in self.avIds + self.notify.debug('calcHasLocalToon returning %s' % retval) + return retval + + def playAgainHandler(self): + self.d_requestPlayAgain() + + def backToToontownHandler(self): + self.notify.debug('requesting exit to toontown...') + self.d_requestExit() + self.playAgain = 0 + self.setPurchaseExit() + + def d_requestExit(self): + self.sendUpdate('requestExit', []) + + def d_requestPlayAgain(self): + self.notify.debug('requesting play again...') + self.sendUpdate('requestPlayAgain', []) + self.playAgain = 1 + + def d_setInventory(self, invString, money, done): + self.sendUpdate('setInventory', [invString, money, done]) + + def __handleBoughtGag(self): + self.d_setInventory(base.localAvatar.inventory.makeNetString(), base.localAvatar.getMoney(), 0) + + def setPurchaseExit(self): + if self.hasLocalToon: + self.ignore('boughtGag') + self.d_setInventory(base.localAvatar.inventory.makeNetString(), base.localAvatar.getMoney(), 1) + messenger.send('purchaseOver', [self.playAgain]) diff --git a/toontown/shtiker/PurchaseManagerAI.py b/toontown/shtiker/PurchaseManagerAI.py new file mode 100755 index 00000000..c37f48b3 --- /dev/null +++ b/toontown/shtiker/PurchaseManagerAI.py @@ -0,0 +1,317 @@ +from otp.ai.AIBaseGlobal import * +from panda3d.core import * +from direct.distributed.ClockDelta import * +from PurchaseManagerConstants import * +import copy +from direct.task.Task import Task +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.minigame import MinigameGlobals + +class PurchaseManagerAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('PurchaseManagerAI') + + def __init__(self, air, avArray, mpArray, previousMinigameId, trolleyZone, newbieIdList = []): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.avIds = copy.deepcopy(avArray) + self.minigamePoints = copy.deepcopy(mpArray) + self.previousMinigameId = previousMinigameId + self.trolleyZone = trolleyZone + self.newbieIds = copy.deepcopy(newbieIdList) + self.isShutdown = 0 + for i in xrange(len(self.avIds), 4): + self.avIds.append(0) + + for i in xrange(len(self.minigamePoints), 4): + self.minigamePoints.append(0) + + self.playerStates = [None, + None, + None, + None] + self.playersReported = [None, + None, + None, + None] + self.playerMoney = [0, + 0, + 0, + 0] + for i in xrange(len(self.avIds)): + avId = self.avIds[i] + if avId <= 3: + self.playerStates[i] = PURCHASE_NO_CLIENT_STATE + self.playersReported[i] = PURCHASE_CANTREPORT_STATE + elif avId in self.air.doId2do: + if avId not in self.getInvolvedAvIds(): + self.playerStates[i] = PURCHASE_EXIT_STATE + self.playersReported[i] = PURCHASE_REPORTED_STATE + else: + self.playerStates[i] = PURCHASE_WAITING_STATE + self.playersReported[i] = PURCHASE_UNREPORTED_STATE + else: + self.playerStates[i] = PURCHASE_DISCONNECTED_STATE + self.playersReported[i] = PURCHASE_CANTREPORT_STATE + + for avId in self.getInvolvedAvIds(): + if avId > 3 and avId in self.air.doId2do: + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + av = self.air.doId2do[avId] + avIndex = self.findAvIndex(avId) + money = av.getMoney() + if avIndex == None: + self.notify.warning('__init__ avIndex is none but avId=%s' % avId) + continue + self.playerMoney[avIndex] = money + if self.playerMoney[avIndex] < 0: + simbase.air.writeServerEvent('suspicious', avId, 'toon has invalid money %s, forcing to zero' % money) + self.playerMoney[avIndex] = 0 + av.addMoney(self.minigamePoints[avIndex]) + self.air.writeServerEvent('minigame', avId, '%s|%s|%s|%s' % (self.previousMinigameId, + self.trolleyZone, + self.avIds, + self.minigamePoints[avIndex])) + + self.receivingInventory = 1 + self.receivingButtons = 1 + return + + def delete(self): + taskMgr.remove(self.uniqueName('countdown-timer')) + self.ignoreAll() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getInvolvedAvIds(self): + avIds = [] + for avId in self.avIds: + if avId not in self.newbieIds: + avIds.append(avId) + + return avIds + + def getMinigamePoints(self): + return self.minigamePoints + + def getAvIds(self): + return self.avIds + + def getNewbieIds(self): + return self.newbieIds + + def getPlayerMoney(self): + return self.playerMoney + + def d_setPlayerStates(self, stateArray): + self.sendUpdate('setPlayerStates', stateArray) + return None + + def getPlayerStates(self): + return self.playerStates + + def getCountdown(self): + self.startCountdown() + return globalClockDelta.getRealNetworkTime() + + def startCountdown(self): + if not config.GetBool('disable-purchase-timer', 0): + taskMgr.doMethodLater(PURCHASE_COUNTDOWN_TIME, self.timeIsUpTask, self.uniqueName('countdown-timer')) + + def requestExit(self): + avId = self.air.getAvatarIdFromSender() + avIndex = self.findAvIndex(avId) + if avIndex is None: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestExit: unknown avatar: %s' % (avId,)) + return + if self.receivingButtons: + if avId in self.air.doId2do: + av = self.air.doId2do[avId] + if avIndex == None: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestExit not on list') + self.notify.warning('Avatar ' + str(avId) + ' requested Exit, but is not on the list!') + else: + avState = self.playerStates[avIndex] + if avState == PURCHASE_PLAYAGAIN_STATE or avState == PURCHASE_WAITING_STATE: + self.playerStates[avIndex] = PURCHASE_EXIT_STATE + self.handlePlayerLeaving(avId) + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestExit invalid transition to exit') + self.notify.warning('Invalid transition to exit state.') + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestExit unknown avatar') + self.notify.warning('Avatar ' + str(avId) + ' requested Exit, but is not in doId2do.' + ' Assuming disconnected.') + self.playerStates[avIndex] = PURCHASE_DISCONNECTED_STATE + self.playersReported[avIndex] = PURCHASE_CANTREPORT_STATE + self.ignore(self.air.getAvatarExitEvent(avId)) + self.d_setPlayerStates(self.playerStates) + if self.getNumUndecided() == 0: + self.timeIsUp() + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestExit not receiving requests now') + self.notify.warning('Avatar ' + str(avId) + ' requested Exit, but I am not receiving button requests now.') + return + + def requestPlayAgain(self): + avId = self.air.getAvatarIdFromSender() + if self.findAvIndex(avId) == None: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestPlayAgain: unknown avatar') + return + if self.receivingButtons: + if avId in self.air.doId2do: + av = self.air.doId2do[avId] + avIndex = self.findAvIndex(avId) + if avIndex == None: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestPlayAgain not on list') + self.notify.warning('Avatar ' + str(avId) + ' requested PlayAgain, but is not on the list!') + else: + avState = self.playerStates[avIndex] + if avState == PURCHASE_WAITING_STATE: + self.notify.debug(str(avId) + ' wants to play again') + self.playerStates[avIndex] = PURCHASE_PLAYAGAIN_STATE + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestPlayAgain invalid transition to PlayAgain') + self.notify.warning('Invalid transition to PlayAgain state.') + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestPlayAgain unknown avatar') + self.notify.warning('Avatar ' + str(avId) + ' requested PlayAgain, but is not in doId2do.' + ' Assuming disconnected.') + avIndex = self.findAvIndex(avId) + self.playerStates[avIndex] = PURCHASE_DISCONNECTED_STATE + self.playersReported[avIndex] = PURCHASE_CANTREPORT_STATE + self.ignore(self.air.getAvatarExitEvent(avId)) + self.d_setPlayerStates(self.playerStates) + if self.getNumUndecided() == 0: + self.timeIsUp() + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestPlayAgain not receiving requests now') + self.notify.warning('Avatar ' + str(avId) + ' requested PlayAgain, but I am not receiving button ' + 'requests now.') + return + + def setInventory(self, blob, newMoney, done): + avId = self.air.getAvatarIdFromSender() + if self.receivingInventory: + if avId in self.air.doId2do: + av = self.air.doId2do[avId] + avIndex = self.findAvIndex(avId) + if avIndex == None: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.setInventory not on list') + self.notify.warning('Avatar ' + str(avId) + ' requested purchase, but is not on the list!') + else: + newInventory = av.inventory.makeFromNetString(blob) + currentMoney = av.getMoney() + if av.inventory.validatePurchase(newInventory, currentMoney, newMoney): + av.setMoney(newMoney) + if not done: + return + if self.playersReported[avIndex] != PURCHASE_UNREPORTED_STATE: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.setInventory bad report state') + self.notify.warning('Bad report state: ' + str(self.playersReported[avIndex])) + else: + av.d_setInventory(av.inventory.makeNetString()) + av.d_setMoney(newMoney) + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.setInventory invalid purchase') + self.notify.warning('Avatar ' + str(avId) + ' attempted an invalid purchase.') + av.d_setInventory(av.inventory.makeNetString()) + av.d_setMoney(av.getMoney()) + self.playersReported[avIndex] = PURCHASE_REPORTED_STATE + if self.getNumUnreported() == 0: + self.shutDown() + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.setInventory not receiving inventory') + self.notify.warning('Not receiving inventory. Ignored ' + str(avId) + "'s request") + return + + def d_setPurchaseExit(self): + self.sendUpdate('setPurchaseExit', []) + return None + + def timeIsUpTask(self, task): + self.timeIsUp() + return Task.done + + def timeIsUp(self): + self.d_setPurchaseExit() + taskMgr.remove(self.uniqueName('countdown-timer')) + self.receivingButtons = 0 + self.receivingInventory = 1 + return None + + def shutDown(self): + if self.isShutdown: + self.notify.warning('Got shutDown twice') + return + self.isShutdown = 1 + from toontown.minigame import MinigameCreatorAI + playAgainNum = self.getNumPlayAgain() + if playAgainNum > 0: + MinigameCreatorAI.createMinigame(self.air, self.getPlayAgainList(), self.trolleyZone, minigameZone=self.zoneId, previousGameId=self.previousMinigameId, newbieIds=self.newbieIds) + else: + MinigameCreatorAI.releaseMinigameZone(self.zoneId) + self.requestDelete() + self.ignoreAll() + return None + + def findAvIndex(self, avId): + for i in xrange(len(self.avIds)): + if avId == self.avIds[i]: + return i + + return None + + def getNumUndecided(self): + undecidedCounter = 0 + for playerState in self.playerStates: + if playerState == PURCHASE_WAITING_STATE: + undecidedCounter += 1 + + return undecidedCounter + + def getPlayAgainList(self): + playAgainList = [] + for i in xrange(len(self.playerStates)): + if self.playerStates[i] == PURCHASE_PLAYAGAIN_STATE: + playAgainList.append(self.avIds[i]) + + return playAgainList + + def getNumPlayAgain(self): + playAgainCounter = 0 + for playerState in self.playerStates: + if playerState == PURCHASE_PLAYAGAIN_STATE: + playAgainCounter += 1 + + return playAgainCounter + + def getNumUnreported(self): + unreportedCounter = 0 + for playerState in self.playersReported: + if playerState == PURCHASE_UNREPORTED_STATE: + unreportedCounter += 1 + elif playerState == PURCHASE_REPORTED_STATE: + pass + elif playerState == PURCHASE_CANTREPORT_STATE: + pass + else: + self.notify.warning('Weird report state: ' + str(playerState)) + + return unreportedCounter + + def __handleUnexpectedExit(self, avId): + self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly') + index = self.findAvIndex(avId) + if index == None: + self.notify.warning('Something is seriously screwed up...' + 'An avatar exited unexpectedly, and they' + ' are not on my list!') + else: + self.playerStates[index] = PURCHASE_DISCONNECTED_STATE + self.playersReported[index] = PURCHASE_CANTREPORT_STATE + self.d_setPlayerStates(self.playerStates) + if self.receivingButtons: + if self.getNumUndecided() == 0: + self.timeIsUp() + if self.receivingInventory: + if self.getNumUnreported() == 0: + self.shutDown() + return + + def handlePlayerLeaving(self, avId): + pass diff --git a/toontown/shtiker/PurchaseManagerConstants.py b/toontown/shtiker/PurchaseManagerConstants.py new file mode 100755 index 00000000..1114f9f3 --- /dev/null +++ b/toontown/shtiker/PurchaseManagerConstants.py @@ -0,0 +1,9 @@ +PURCHASE_NO_CLIENT_STATE = 0 +PURCHASE_WAITING_STATE = 1 +PURCHASE_PLAYAGAIN_STATE = 2 +PURCHASE_EXIT_STATE = 3 +PURCHASE_DISCONNECTED_STATE = 4 +PURCHASE_UNREPORTED_STATE = 10 +PURCHASE_REPORTED_STATE = 11 +PURCHASE_CANTREPORT_STATE = 12 +PURCHASE_COUNTDOWN_TIME = 120 diff --git a/toontown/shtiker/QuestPage.py b/toontown/shtiker/QuestPage.py new file mode 100755 index 00000000..0222b3ee --- /dev/null +++ b/toontown/shtiker/QuestPage.py @@ -0,0 +1,174 @@ +from panda3d.core import * +import ShtikerPage +from direct.gui.DirectGui import * +from toontown.quest import Quests +from toontown.toon import NPCToons +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.quest import QuestBookPoster +from direct.directnotify import DirectNotifyGlobal + +class QuestPage(ShtikerPage.ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('QuestPage') + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.quests = {0: None, + 1: None, + 2: None, + 3: None} + self.textRolloverColor = Vec4(1, 1, 0, 1) + self.textDownColor = Vec4(0.5, 0.9, 1, 1) + self.textDisabledColor = Vec4(0.4, 0.8, 0.4, 1) + self.onscreen = 0 + self.lastQuestTime = globalClock.getRealTime() + return + + def load(self): + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.QuestPageToonTasks, text_scale=0.12, textMayChange=0, pos=(0, 0, 0.6)) + questFramePlaceList = ((-0.45, + 0, + 0.25, + 0, + 0, + 0), + (-0.45, + 0, + -0.35, + 0, + 0, + 0), + (0.45, 0, 0.25, 0, 0, 0), + (0.45, + 0, + -0.35, + 0, + 0, + 0)) + self.questFrames = [] + for i in xrange(ToontownGlobals.MaxQuestCarryLimit): + frame = QuestBookPoster.QuestBookPoster(reverse=i > 1, mapIndex=i + 1) + frame.reparentTo(self) + frame.setPosHpr(*questFramePlaceList[i]) + frame.setScale(1.06) + self.questFrames.append(frame) + + self.accept('questsChanged', self.updatePage) + return + + def acceptOnscreenHooks(self): + self.accept(ToontownGlobals.QuestsHotkeyOn, self.showQuestsOnscreen) + self.accept(ToontownGlobals.QuestsHotkeyOff, self.hideQuestsOnscreen) + + def ignoreOnscreenHooks(self): + self.ignore(ToontownGlobals.QuestsHotkeyOn) + self.ignore(ToontownGlobals.QuestsHotkeyOff) + + def unload(self): + self.ignore('questsChanged') + del self.title + del self.quests + del self.questFrames + loader.unloadModel('phase_3.5/models/gui/stickerbook_gui') + ShtikerPage.ShtikerPage.unload(self) + + def clearQuestFrame(self, index): + self.questFrames[index].clear() + self.quests[index] = None + return + + def fillQuestFrame(self, questDesc, index): + self.questFrames[index].update(questDesc) + self.quests[index] = questDesc + + def getLowestUnusedIndex(self): + for i in xrange(ToontownGlobals.MaxQuestCarryLimit): + if self.quests[i] == None: + return i + + return -1 + + def updatePage(self): + self.notify.debug('updatePage()') + newQuests = base.localAvatar.quests + carryLimit = base.localAvatar.getQuestCarryLimit() + for i in xrange(ToontownGlobals.MaxQuestCarryLimit): + if i < carryLimit: + self.questFrames[i].show() + else: + self.questFrames[i].hide() + + for index, questDesc in self.quests.items(): + if questDesc is not None and list(questDesc) not in newQuests: + self.clearQuestFrame(index) + + for questDesc in newQuests: + newQuestDesc = tuple(questDesc) + if newQuestDesc not in self.quests.values(): + index = self.getLowestUnusedIndex() + self.fillQuestFrame(newQuestDesc, index) + + for i, questDesc in self.quests.iteritems(): + if questDesc: + if self.canDeleteQuest(questDesc): + self.questFrames[i].setDeleteCallback(self.__deleteQuest) + else: + self.questFrames[i].setDeleteCallback(None) + self.questFrames[i].update(questDesc) + else: + self.questFrames[i].unbindMouseEnter() + + messenger.send('questPageUpdated') + return + + def enter(self): + self.updatePage() + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + ShtikerPage.ShtikerPage.exit(self) + + def showQuestsOnscreenTutorial(self): + self.setPos(0, 0, -0.2) + self.showQuestsOnscreen() + + def showQuestsOnscreen(self): + messenger.send('wakeup') + timedif = globalClock.getRealTime() - self.lastQuestTime + if timedif < 0.7: + return + self.lastQuestTime = globalClock.getRealTime() + if self.onscreen or base.localAvatar.invPage.onscreen: + return + self.onscreen = 1 + for i in xrange(ToontownGlobals.MaxQuestCarryLimit): + if hasattr(self.questFrames[i], 'mapIndex'): + self.questFrames[i].mapIndex.show() + + self.updatePage() + self.reparentTo(aspect2d) + self.title.hide() + self.show() + + def hideQuestsOnscreenTutorial(self): + self.setPos(0, 0, 0) + self.hideQuestsOnscreen() + + def hideQuestsOnscreen(self): + if not self.onscreen: + return + self.onscreen = 0 + for i in xrange(ToontownGlobals.MaxQuestCarryLimit): + if hasattr(self.questFrames[i], 'mapIndex'): + self.questFrames[i].mapIndex.hide() + + self.reparentTo(self.book) + self.title.show() + self.hide() + + def canDeleteQuest(self, questDesc): + return Quests.isQuestJustForFun(questDesc[0], questDesc[3]) and self.onscreen == 0 + + def __deleteQuest(self, questDesc): + base.localAvatar.d_requestDeleteQuest(questDesc) diff --git a/toontown/shtiker/ShardPage.py b/toontown/shtiker/ShardPage.py new file mode 100755 index 00000000..876e1beb --- /dev/null +++ b/toontown/shtiker/ShardPage.py @@ -0,0 +1,501 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from direct.task.Task import Task +from panda3d.core import * +from toontown.distributed import ToontownDistrictStats +from toontown.hood import ZoneUtil +from toontown.shtiker import ShtikerPage +from toontown.coghq import CogDisguiseGlobals +from toontown.suit import SuitDNA +from toontown.suit import Suit +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog + + +ICON_COLORS = (Vec4(0.863, 0.776, 0.769, 1.0), Vec4(0.749, 0.776, 0.824, 1.0), + Vec4(0.749, 0.769, 0.749, 1.0), Vec4(0.843, 0.745, 0.745, 1.0)) + +POP_COLORS = (Vec4(0.4, 0.4, 1.0, 1.0), Vec4(0.4, 1.0, 0.4, 1.0), + Vec4(1.0, 0.4, 0.4, 1.0)) + +def compareShardTuples(a, b): + if a[1] < b[1]: + return -1 + elif b[1] < a[1]: + return 1 + else: + return 0 + +def setupInvasionMarkerAny(node): + pass + +def setupInvasionMarker(node, invasionStatus): + if node.find('**/*invasion-marker'): + return + + markerNode = node.attachNewNode('invasion-marker') + + if invasionStatus == 5: + setupInvasionMarkerAny(markerNode) + return + + icons = loader.loadModel('phase_3/models/gui/cog_icons') + iconStatus = invasionStatus - 1 + + if iconStatus in SuitDNA.suitDeptModelPaths: + icon = icons.find(SuitDNA.suitDeptModelPaths[iconStatus]).copyTo(markerNode) + + icons.removeNode() + + icon.setColor(ICON_COLORS[iconStatus]) + icon.setPos(0.50, 0, 0.0125) + icon.setScale(0.0535) + +def removeInvasionMarker(node): + markerNode = node.find('**/*invasion-marker') + + if not markerNode.isEmpty(): + markerNode.removeNode() + + +class ShardPage(ShtikerPage.ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('ShardPage') + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.shardButtonMap = {} + self.shardButtons = [] + self.scrollList = None + self.currentBTP = None + self.currentBTL = None + self.currentBTR = None + self.currentBTI = None + self.currentO = None + self.textRolloverColor = Vec4(1, 1, 0, 1) + self.textDownColor = Vec4(0.5, 0.9, 1, 1) + self.textDisabledColor = Vec4(0.4, 0.8, 0.4, 1) + self.ShardInfoUpdateInterval = 5.0 + self.lowPop = config.GetInt('shard-low-pop', 150) + self.midPop = config.GetInt('shard-mid-pop', 300) + self.highPop = -1 + self.showPop = config.GetBool('show-population', 0) + self.showTotalPop = config.GetBool('show-total-population', 0) + self.noTeleport = config.GetBool('shard-page-disable', 0) + self.shardGroups = [] + self.shardText = [] + self.groupDialog = None + + def load(self): + matchGui = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + main_text_scale = 0.06 + title_text_scale = 0.12 + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.ShardPageTitle, text_scale=title_text_scale, textMayChange=0, pos=(0, 0, 0.6)) + helpText_ycoord = 0.403 + self.helpText = DirectLabel(parent=self, relief=None, text='', text_scale=main_text_scale, text_wordwrap=12, text_align=TextNode.ALeft, textMayChange=1, pos=(0.058, 0, helpText_ycoord)) + shardPop_ycoord = helpText_ycoord - 0.523 + totalPop_ycoord = shardPop_ycoord - 0.44 + self.districtInfo = NodePath('Selected-Shard-Info') + self.districtInfo.reparentTo(self) + self.preferredButton = DirectButton(parent=self, relief=None, image=matchGui.find('**/minnieCircle'), pos=(0.1, 0, -0.575), scale=0.35, text=TTLocalizer.ShardPagePreferred, text_scale=0.11, text_pos=(0, -0.2), command=self.setPreferredShard) + self.totalPopulationText = DirectLabel(parent=self.districtInfo, relief=None, text=TTLocalizer.ShardPagePopulationTotal % 1, text_scale=main_text_scale, text_wordwrap=8, textMayChange=1, text_align=TextNode.ACenter, pos=(0.4247, 0, totalPop_ycoord)) + if self.showTotalPop: + self.totalPopulationText.show() + else: + self.totalPopulationText.hide() + self.gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.listXorigin = -0.02 + self.listFrameSizeX = 0.67 + self.listZorigin = -0.96 + self.listFrameSizeZ = 1.04 + self.arrowButtonScale = 1.3 + self.itemFrameXorigin = -0.247 + self.itemFrameZorigin = 0.365 + self.buttonXstart = self.itemFrameXorigin + 0.293 + self.regenerateScrollList() + scrollTitle = DirectFrame(parent=self.scrollList, text=TTLocalizer.ShardPageScrollTitle, text_scale=main_text_scale, text_align=TextNode.ACenter, relief=None, pos=(self.buttonXstart, 0, self.itemFrameZorigin + 0.127)) + matchGui.removeNode() + + def firstLoadShard(self, buttonTuple): + curShardTuples = base.cr.listActiveShards() + curShardTuples.sort(compareShardTuples) + actualShardId = base.localAvatar.defaultShard + for i in xrange(len(curShardTuples)): + shardId, name, pop, invasionStatus, groupAvCount = curShardTuples[i] + if shardId == actualShardId: + self.currentBTP = buttonTuple[0] + self.currentBTL = buttonTuple[1] + self.currentBTR = buttonTuple[2] + self.currentBTI = buttonTuple[3] + self.currentO = [pop, name, shardId] + self.currentBTL['state'] = DGG.DISABLED + self.currentBTR['state'] = DGG.DISABLED + self.reloadRightBrain(pop, name, groupAvCount, shardId, buttonTuple) + + def unload(self): + self.gui.removeNode() + del self.title + self.scrollList.destroy() + del self.scrollList + del self.shardButtons + taskMgr.remove('ShardPageUpdateTask-doLater') + + ShtikerPage.ShtikerPage.unload(self) + + def regenerateScrollList(self): + selectedIndex = 0 + + if self.scrollList: + selectedIndex = self.scrollList.getSelectedIndex() + for button in self.shardButtons: + button.detachNode() + self.scrollList.destroy() + self.scrollList = None + + self.scrollList = DirectScrolledList(parent=self, relief=None, pos=(-0.51, 0, 0), incButton_image=(self.gui.find('**/FndsLst_ScrollUp'), + self.gui.find('**/FndsLst_ScrollDN'), + self.gui.find('**/FndsLst_ScrollUp_Rllvr'), + self.gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_scale=(self.arrowButtonScale, self.arrowButtonScale, -self.arrowButtonScale), incButton_pos=(self.buttonXstart + 0.005, 0, self.itemFrameZorigin - 1.000), incButton_image3_color=Vec4(1, 1, 1, 0.2), decButton_image=(self.gui.find('**/FndsLst_ScrollUp'), + self.gui.find('**/FndsLst_ScrollDN'), + self.gui.find('**/FndsLst_ScrollUp_Rllvr'), + self.gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_scale=(self.arrowButtonScale, self.arrowButtonScale, self.arrowButtonScale), decButton_pos=(self.buttonXstart, 0.0025, self.itemFrameZorigin + 0.130), decButton_image3_color=Vec4(1, 1, 1, 0.2), itemFrame_pos=(self.itemFrameXorigin, 0, self.itemFrameZorigin), itemFrame_scale=1.0, itemFrame_relief=DGG.SUNKEN, itemFrame_frameSize=(self.listXorigin, + self.listXorigin + self.listFrameSizeX, + self.listZorigin, + self.listZorigin + self.listFrameSizeZ), itemFrame_frameColor=(0.85, 0.95, 1, 1), itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=15, forceHeight=0.065, items=self.shardButtons) + self.scrollList.scrollTo(selectedIndex) + + def askForShardInfoUpdate(self, task = None): + ToontownDistrictStats.refresh('shardInfoUpdated') + taskMgr.doMethodLater(self.ShardInfoUpdateInterval, self.askForShardInfoUpdate, 'ShardPageUpdateTask-doLater') + return Task.done + + def makeShardButton(self, shardId, groupAvCount, shardName, shardPop): + shardButtonParent = DirectFrame() + shardButtonL = DirectButton(parent=shardButtonParent, relief=None, text=shardName, text_scale=0.06, text_align=TextNode.ALeft, text_fg=Vec4(0, 0, 0, 1), text3_fg=self.textDisabledColor, text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, textMayChange=0, command=self.reloadRightBrain) + popText = str(shardPop) + if popText is None: + popText = '' + model = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + button = model.find('**/minnieCircle') + + shardButtonR = DirectButton(parent=shardButtonParent, relief=None, + image=button, + image_scale=(0.3, 1, 0.3), + image2_scale=(0.35, 1, 0.35), + image_color=self.getPopColor(shardPop), + pos=(0.58, 0, 0.0125), + text=popText, + text_scale=0.06, + text_align=TextNode.ARight, + text_pos=(-0.14, -0.0165), text_fg=Vec4(0, 0, 0, 1), text3_fg=self.textDisabledColor, text1_bg=self.textRolloverColor, text2_bg=self.textRolloverColor, textMayChange=1, command=self.reloadRightBrain) + model.removeNode() + button.removeNode() + + invasionMarker = NodePath('InvasionMarker-%s' % shardId) + invasionMarker.reparentTo(shardButtonParent) + + buttonTuple = (shardButtonParent, shardButtonR, shardButtonL, invasionMarker) + shardButtonL['extraArgs'] = extraArgs=[shardPop, shardName, groupAvCount, shardId, buttonTuple] + shardButtonR['extraArgs'] = extraArgs=[shardPop, shardName, groupAvCount, shardId, buttonTuple] + + return buttonTuple + + def makeGroupButton(self, shardId, group, population): + groupButtonParent = DirectFrame() + groupButtonL = DirectButton(parent=groupButtonParent, relief=None, text=TTLocalizer.GlobalStreetNames[group][-1], text_pos=(0.0, -0.0225), text_scale=0.048, text_align=TextNode.ALeft, text_fg=Vec4(0, 0, 0, 1), text3_fg=self.textDisabledColor, text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, command=self.joinGroup, extraArgs=[group, shardId]) + + groupButtonR = DirectButton(parent=groupButtonParent, relief=None, + pos=(0.58, 0, -0.0125), + text=str(population), + text_scale=0.055, + text_align=TextNode.ACenter, + command=self.joinGroup, + extraArgs=[group, shardId], + text_pos=(-0.00575, -0.0125), text_fg=Vec4(0, 0, 0, 1), text3_fg=Vec4(0, 0, 0, 1), text1_bg=self.textRolloverColor, text2_bg=self.textRolloverColor) + + return (groupButtonParent, groupButtonL, groupButtonR) + + def joinGroup(self, group, shardId): + canonicalHoodId = ZoneUtil.getCanonicalHoodId(group) + shardName = base.cr.activeDistrictMap[shardId].name + hoodName = TTLocalizer.GlobalStreetNames[canonicalHoodId] + teleportAccess = base.localAvatar.hasTeleportAccess(canonicalHoodId) + + if teleportAccess: + message = TTLocalizer.GroupAskAccess + elif base.localAvatar.defaultShard == shardId: + self.acceptOnce('groupDialogDone', self.cleanupGroupDialog) + self.groupDialog = TTDialog.TTGlobalDialog(style=TTDialog.Acknowledge, text=TTLocalizer.GroupAskNoAccessSame % (hoodName[0], hoodName[-1]), doneEvent='groupDialogDone') + self.groupDialog.show() + return + else: + message = TTLocalizer.GroupAskNoAccess + + self.acceptOnce('groupDialogDone', self.__handleGroupDialog, extraArgs=[canonicalHoodId if teleportAccess else base.localAvatar.lastHood, shardId]) + self.groupDialog = TTDialog.TTGlobalDialog(style=TTDialog.TwoChoice, text=message % (hoodName[0], hoodName[-1], shardName), doneEvent='groupDialogDone') + self.groupDialog.show() + + def cleanupGroupDialog(self): + self.ignore('groupDialogDone') + self.groupDialog.cleanup() + del self.groupDialog + + def __handleGroupDialog(self, canonicalHoodId, shardId): + response = self.groupDialog.doneStatus + self.cleanupGroupDialog() + + if response == 'ok': + self.requestTeleport(canonicalHoodId, shardId) + + def removeRightBrain(self): + self.districtInfo.find('**/*district-info').removeNode() + + def reloadRightBrain(self, shardPop, shardName, groupAvCount, shardId, buttonTuple): + self.currentRightBrain = (shardPop, shardName, shardId, buttonTuple) + if self.districtInfo.find('**/*district-info'): + self.removeRightBrain() + if self.currentBTL: + self.currentBTL['state'] = DGG.NORMAL + if self.currentBTR: + self.currentBTR['state'] = DGG.NORMAL + popText = self.getPopText(shardPop) + districtInfoNode = self.districtInfo.attachNewNode('district-info') + self.districtStatusLabel = DirectLabel(parent=districtInfoNode, relief=None, pos=(0.4247, 0, 0.45), text=popText, text_scale=0.09, text_fg=Vec4(0, 0, 0, 1), textMayChange=1) + pText = TTLocalizer.ShardPageShardTitle % (shardName, str(shardPop)) + self.populationStatusLabel = DirectLabel(parent=districtInfoNode, relief=None, pos=(0.4247, 0, 0.38), text=pText, text_scale=0.04, text_fg=Vec4(0, 0, 0, 1), textMayChange=1) + tText = TTLocalizer.ShardPageTeleport % shardName + tImage = loader.loadModel('phase_4/models/gui/purchase_gui') + tImage.setSz(1.35) + self.shardTeleportButton = DirectButton(parent=districtInfoNode, relief=None, pos=(0.4247, 0, 0.25), image=(tImage.find('**/PurchScrn_BTN_UP'), tImage.find('**/PurchScrn_BTN_DN'), tImage.find('**/PurchScrn_BTN_RLVR')), text=tText, text_scale=0.065, text_pos=(0.0, 0.015), text_fg=Vec4(0, 0, 0, 1), textMayChange=1, command=self.choseShard, extraArgs=[shardId]) + + self.currentBTP = buttonTuple[0] + self.currentBTL = buttonTuple[1] + self.currentBTR = buttonTuple[2] + self.currentBTI = buttonTuple[3] + self.currentO = [shardPop, shardName, shardId] + self.currentBTL['state'] = DGG.DISABLED + self.currentBTR['state'] = DGG.DISABLED + self.preferredButton.setColor((0, 1, 0, 1) if settings.get('preferredShard') == shardName else (1, 0, 0, 1)) + + if shardId == base.localAvatar.defaultShard: + self.shardTeleportButton['state'] = DGG.DISABLED + + for button in self.shardGroups + self.shardText: + button.removeNode() + + self.shardGroups = [] + self.shardText = [] + + for i, group in enumerate(ToontownGlobals.GROUP_ZONES): + btuple = self.makeGroupButton(shardId, group, groupAvCount[i]) + if ZoneUtil.getCanonicalHoodId(base.localAvatar.zoneId) == ZoneUtil.getCanonicalHoodId(group): + btuple[1]['state'] = DGG.DISABLED + btuple[2]['state'] = DGG.DISABLED + self.shardGroups.append(btuple[0]) + self.shardText.append(btuple[2]) + + buttonImage = (self.gui.find('**/FndsLst_ScrollUp'), self.gui.find('**/FndsLst_ScrollDN'), self.gui.find('**/FndsLst_ScrollUp_Rllvr'), self.gui.find('**/FndsLst_ScrollUp')) + self.districtGroups = DirectScrolledList(parent=districtInfoNode, relief=None, + pos=(0.38, 0, -0.34), + incButton_image=buttonImage, + incButton_relief=None, + incButton_scale=(self.arrowButtonScale, + self.arrowButtonScale, + -self.arrowButtonScale), + incButton_pos=(self.buttonXstart + 0.005, 0, -0.125), + incButton_image3_color=Vec4(1, 1, 1, 0.2), + decButton_image=buttonImage, + decButton_relief=None, + decButton_scale=(self.arrowButtonScale, + self.arrowButtonScale, + self.arrowButtonScale), + decButton_pos=(self.buttonXstart, 0.0025, 0.445), + decButton_image3_color=Vec4(1, 1, 1, 0.2), + itemFrame_pos=(self.itemFrameXorigin, 0, self.itemFrameZorigin), + itemFrame_scale=1.0, + itemFrame_relief=DGG.SUNKEN, + itemFrame_frameSize=(self.listXorigin, + (self.listXorigin + self.listFrameSizeX), + self.listZorigin/2.1, + (self.listZorigin + self.listFrameSizeZ)/2.1), + itemFrame_frameColor=(0.85, 0.95, 1, 1), + itemFrame_borderWidth=(0.01, 0.01), + numItemsVisible=7, + forceHeight=0.065, + items=self.shardGroups) + + def getPopColor(self, pop): + if pop <= self.lowPop: + newColor = POP_COLORS[0] + elif pop <= self.midPop: + newColor = POP_COLORS[1] + else: + newColor = POP_COLORS[2] + return newColor + + def getPopText(self, pop): + if pop <= self.lowPop: + popText = TTLocalizer.ShardPageLow + elif pop <= self.midPop: + popText = TTLocalizer.ShardPageMed + else: + popText = TTLocalizer.ShardPageHigh + return popText + + def getPopChoiceHandler(self, pop): + if pop <= self.midPop: + if self.noTeleport and not self.showPop: + handler = self.shardChoiceReject + else: + handler = self.choseShard + elif self.showPop: + handler = self.choseShard + else: + if localAvatar.isAdmin(): + handler = self.choseShard + else: + handler = self.shardChoiceReject + return handler + + def getCurrentZoneId(self): + try: + zoneId = base.cr.playGame.getPlace().getZoneId() + except: + zoneId = None + return zoneId + + def createSuitHead(self, suitName): + suitDNA = SuitDNA.SuitDNA() + suitDNA.newSuit(suitName) + suit = Suit.Suit() + suit.setDNA(suitDNA) + headParts = suit.getHeadParts() + head = hidden.attachNewNode('head') + for part in headParts: + copyPart = part.copyTo(head) + copyPart.setDepthTest(1) + copyPart.setDepthWrite(1) + self.fitGeometry(head, fFlip=1) + suit.delete() + suit = None + return head + + def updateScrollList(self): + curShardTuples = base.cr.listActiveShards() + curShardTuples.sort(compareShardTuples) + + actualShardId = base.localAvatar.defaultShard + actualShardName = None + anyChanges = 0 + totalPop = 0 + currentMap = {} + self.shardButtons = [] + + for i in xrange(len(curShardTuples)): + + shardId, name, pop, invasionStatus, groupAvCount = curShardTuples[i] + + if shardId == actualShardId: + actualShardName = name + + totalPop += pop + currentMap[shardId] = 1 + buttonTuple = self.shardButtonMap.get(shardId) + + if buttonTuple == None: + buttonTuple = self.makeShardButton(shardId, groupAvCount, name, pop) + self.shardButtonMap[shardId] = buttonTuple + anyChanges = 1 + else: + buttonTuple[1]['image_color'] = self.getPopColor(pop) + buttonTuple[1]['text'] = str(pop) + buttonTuple[1]['command'] = self.reloadRightBrain + buttonTuple[1]['extraArgs'] = [pop, name, groupAvCount, shardId, buttonTuple] + buttonTuple[2]['command'] = self.reloadRightBrain + buttonTuple[2]['extraArgs'] = [pop, name, groupAvCount, shardId, buttonTuple] + + for i, button in enumerate(self.shardText): + button['text'] = str(groupAvCount[i]) + + self.shardButtons.append(buttonTuple[0]) + + if invasionStatus: + setupInvasionMarker(buttonTuple[3], invasionStatus) + else: + removeInvasionMarker(buttonTuple[3]) + + for shardId, buttonTuple in self.shardButtonMap.items(): + + if shardId not in currentMap: + buttonTuple[3].removeNode() + buttonTuple[0].destroy() + del self.shardButtonMap[shardId] + anyChanges = 1 + + if anyChanges: + self.regenerateScrollList() + + self.totalPopulationText['text'] = TTLocalizer.ShardPagePopulationTotal % totalPop + helpText = TTLocalizer.ShardPageHelpIntro + + if actualShardName: + helpText += TTLocalizer.ShardPageHelpWhere % actualShardName + + if not self.book.safeMode: + helpText += TTLocalizer.ShardPageHelpMove + + def enter(self): + self.askForShardInfoUpdate() + self.updateScrollList() + currentShardId = base.localAvatar.defaultShard + buttonTuple = self.shardButtonMap.get(currentShardId) + if buttonTuple: + i = self.shardButtons.index(buttonTuple[0]) + self.scrollList.scrollTo(i, centered=1) + self.firstLoadShard(buttonTuple) + ShtikerPage.ShtikerPage.enter(self) + self.accept('shardInfoUpdated', self.updateScrollList) + + def exit(self): + for shardId, buttonTuple in self.shardButtonMap.items(): + buttonTuple[1]['state'] = DGG.NORMAL + buttonTuple[2]['state'] = DGG.NORMAL + self.ignore('shardInfoUpdated') + self.ignore('ShardPageConfirmDone') + taskMgr.remove('ShardPageUpdateTask-doLater') + ShtikerPage.ShtikerPage.exit(self) + + def shardChoiceReject(self, shardId): + self.confirm = TTDialog.TTGlobalDialog(doneEvent='ShardPageConfirmDone', message=TTLocalizer.ShardPageChoiceReject, style=TTDialog.Acknowledge) + self.confirm.show() + self.accept('ShardPageConfirmDone', self.__handleConfirm) + + def __handleConfirm(self): + self.ignore('ShardPageConfirmDone') + self.confirm.cleanup() + del self.confirm + + def choseShard(self, shardId): + if not base.localAvatar.defaultShard == shardId: + self.requestTeleport(base.localAvatar.lastHood, shardId) + + def requestTeleport(self, hood, shardId): + canonicalHoodId = ZoneUtil.getCanonicalHoodId(hood) + + try: + place = base.cr.playGame.getPlace() + except: + try: + place = base.cr.playGame.hood.loader.place + except: + place = base.cr.playGame.hood.place + + place.requestTeleport(canonicalHoodId, canonicalHoodId, shardId, -1) + + def setPreferredShard(self): + if settings.get('preferredShard', '') == self.currentO[1]: + self.preferredButton.setColor(1, 0, 0, 1) + del settings['preferredShard'] + else: + self.preferredButton.setColor(0, 1, 0, 1) + settings['preferredShard'] = self.currentO[1] \ No newline at end of file diff --git a/toontown/shtiker/ShtikerBook.py b/toontown/shtiker/ShtikerBook.py new file mode 100755 index 00000000..baddb5e7 --- /dev/null +++ b/toontown/shtiker/ShtikerBook.py @@ -0,0 +1,408 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.gui.DirectGui import * +from direct.showbase import DirectObject +from panda3d.core import * + +from toontown.effects import DistributedFireworkShow +from otp.nametag import NametagGlobals +from toontown.parties import DistributedPartyFireworksActivity +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals + + +class ShtikerBook(DirectFrame, StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('ShtikerBook') + + def __init__(self, doneEvent): + DirectFrame.__init__(self, relief=None, sortOrder=DGG.BACKGROUND_SORT_INDEX) + self.initialiseoptions(ShtikerBook) + StateData.StateData.__init__(self, doneEvent) + self.pages = [] + self.pageTabs = [] + self.currPageTabIndex = None + self.pageTabFrames = [self.createPageTabFrame(x) for x in (-0.93, 0.93)] + self.currPageIndex = None + self.entered = 0 + self.safeMode = 0 + self.__obscured = 0 + self.__shown = 0 + self.__isOpen = 0 + self.hide() + self.setPos(0, 0, 0.1) + self.pageOrder = [TTLocalizer.OptionsPageTitle, + TTLocalizer.ShardPageTitle, + TTLocalizer.MapPageTitle, + TTLocalizer.InventoryPageTitle, + TTLocalizer.QuestPageToonTasks, + TTLocalizer.TrackPageShortTitle, + TTLocalizer.SuitPageTitle, + TTLocalizer.FishPageTitle, + TTLocalizer.KartPageTitle, + TTLocalizer.DisguisePageTitle, + TTLocalizer.NPCFriendPageTitle, + TTLocalizer.GardenPageTitle, + TTLocalizer.GolfPageTitle, + TTLocalizer.PhotoPageTitle, + TTLocalizer.EventsPageName, + TTLocalizer.StatPageTitle] + + def createPageTabFrame(self, x): + frame = DirectFrame(parent=self, relief=None, pos=(x, 0, 0.66), scale=1.25) + frame.hide() + return frame + + def setSafeMode(self, setting): + self.safeMode = setting + + def enter(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: SHTICKERBOOK: Open') + if self.entered: + return + self.entered = 1 + messenger.send('releaseDirector') + messenger.send('stickerBookEntered') + base.playSfx(self.openSound) + base.disableMouse() + base.render.hide() + base.setBackgroundColor(0.05, 0.15, 0.4) + base.setCellsAvailable([base.rightCells[0]], 0) + self.oldMin2dAlpha = NametagGlobals.getMin2dAlpha() + self.oldMax2dAlpha = NametagGlobals.getMax2dAlpha() + NametagGlobals.setMin2dAlpha(0.8) + NametagGlobals.setMax2dAlpha(1.0) + self.__isOpen = 1 + self.__setButtonVisibility() + self.show() + self.showPageArrows() + if not self.safeMode: + self.accept('shtiker-page-done', self.__pageDone) + self.accept(ToontownGlobals.StickerBookHotkey, self.__close) + self.accept(ToontownGlobals.OptionsPageHotkey, self.__close) + for tab in self.pageTabFrames: + tab.show() + self.pages[self.currPageIndex].enter() + + def exit(self): + if not self.entered: + return + self.entered = 0 + messenger.send('stickerBookExited') + base.playSfx(self.closeSound) + self.pages[self.currPageIndex].exit() + base.render.show() + setBlackBackground = 0 + for obj in base.cr.doId2do.values(): + if isinstance(obj, DistributedFireworkShow.DistributedFireworkShow) or isinstance(obj, DistributedPartyFireworksActivity.DistributedPartyFireworksActivity): + setBlackBackground = 1 + + if setBlackBackground: + base.setBackgroundColor(Vec4(0, 0, 0, 1)) + else: + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + gsg = base.win.getGsg() + if gsg: + base.render.prepareScene(gsg) + NametagGlobals.setMin2dAlpha(self.oldMin2dAlpha) + NametagGlobals.setMax2dAlpha(self.oldMax2dAlpha) + base.setCellsAvailable([base.rightCells[0]], 1) + self.__isOpen = 0 + self.hide() + self.hideButton() + cleanupDialog('globalDialog') + for tab in self.pageTabFrames: + tab.hide() + self.ignore('shtiker-page-done') + self.ignore(ToontownGlobals.StickerBookHotkey) + self.ignore(ToontownGlobals.OptionsPageHotkey) + self.ignore('arrow_right') + self.ignore('arrow_left') + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: SHTICKERBOOK: Close') + + def load(self): + self.checkGardenStarted = localAvatar.getGardenStarted() + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + self['image'] = bookModel.find('**/big_book') + self['image_scale'] = (2, 1, 1.5) + self.resetFrameSize() + self.bookOpenButton = DirectButton(image=(bookModel.find('**/BookIcon_CLSD'), bookModel.find('**/BookIcon_OPEN'), bookModel.find('**/BookIcon_RLVR')), relief=None, pos=(-0.158, 0, 0.17), parent=base.a2dBottomRight, scale=0.305, command=self.__open) + self.bookCloseButton = DirectButton(image=(bookModel.find('**/BookIcon_OPEN'), bookModel.find('**/BookIcon_CLSD'), bookModel.find('**/BookIcon_RLVR2')), relief=None, pos=(-0.158, 0, 0.17), parent=base.a2dBottomRight, scale=0.305, command=self.__close) + self.bookOpenButton.hide() + self.bookCloseButton.hide() + self.nextArrow = DirectButton(parent=self, relief=None, image=(bookModel.find('**/arrow_button'), bookModel.find('**/arrow_down'), bookModel.find('**/arrow_rollover')), scale=(0.1, 0.1, 0.1), pos=(0.838, 0, -0.661), command=self.__pageChange, extraArgs=[1]) + self.prevArrow = DirectButton(parent=self, relief=None, image=(bookModel.find('**/arrow_button'), bookModel.find('**/arrow_down'), bookModel.find('**/arrow_rollover')), scale=(-0.1, 0.1, 0.1), pos=(-0.838, 0, -0.661), command=self.__pageChange, extraArgs=[-1]) + bookModel.removeNode() + self.openSound = base.loadSfx('phase_3.5/audio/sfx/GUI_stickerbook_open.ogg') + self.closeSound = base.loadSfx('phase_3.5/audio/sfx/GUI_stickerbook_delete.ogg') + self.pageSound = base.loadSfx('phase_3.5/audio/sfx/GUI_stickerbook_turn.ogg') + return + + def unload(self): + loader.unloadModel('phase_3.5/models/gui/stickerbook_gui') + self.destroy() + self.bookOpenButton.destroy() + del self.bookOpenButton + self.bookCloseButton.destroy() + del self.bookCloseButton + self.nextArrow.destroy() + del self.nextArrow + self.prevArrow.destroy() + del self.prevArrow + for page in self.pages: + page.unload() + + del self.pages + for pageTab in self.pageTabs: + pageTab.destroy() + + del self.pageTabs + del self.currPageTabIndex + del self.openSound + del self.closeSound + del self.pageSound + + def addPage(self, page, pageName = 'Page'): + if pageName not in self.pageOrder: + self.notify.error('Trying to add page %s in the ShtickerBook. Page not listed in the order.' % pageName) + return + self.pages.append(page) + pageIndex = len(self.pages) - 1 + page.setBook(self) + page.setPageName(pageName) + page.reparentTo(self) + self.addPageTab(page, pageIndex, pageName) + + def addPageTab(self, page, pageIndex, pageName = 'Page'): + tabIndex = len(self.pageTabs) + + def goToPage(): + messenger.send('wakeup') + base.playSfx(self.pageSound) + self.setPage(page) + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: SHTICKERBOOK: Browse tabs %s' % page.pageName) + + yOffset = 0.07 * (pageIndex % 16) + iconGeom = None + iconImage = None + iconScale = 1 + iconColor = (1, 1, 1, 1) + buttonPressedCommand = goToPage + extraArgs = [] + if pageName == TTLocalizer.OptionsPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/switch') + iconModels.detachNode() + elif pageName == TTLocalizer.ShardPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/district') + iconModels.detachNode() + elif pageName == TTLocalizer.MapPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/teleportIcon') + iconModels.detachNode() + elif pageName == TTLocalizer.InventoryPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/inventory_icons') + iconGeom = iconModels.find('**/inventory_tart') + iconScale = 7 + iconModels.detachNode() + elif pageName == TTLocalizer.QuestPageToonTasks: + iconModels = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + iconGeom = iconModels.find('**/questCard') + iconScale = 0.9 + iconModels.detachNode() + elif pageName == TTLocalizer.TrackPageShortTitle: + iconGeom = iconModels = loader.loadModel('phase_3.5/models/gui/filmstrip') + iconScale = 1.1 + iconColor = Vec4(0.7, 0.7, 0.7, 1) + iconModels.detachNode() + elif pageName == TTLocalizer.SuitPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/gui_gear') + iconModels.detachNode() + elif pageName == TTLocalizer.FishPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/fish') + iconModels.detachNode() + elif pageName == TTLocalizer.GardenPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/gardenIcon') + iconModels.detachNode() + elif pageName == TTLocalizer.DisguisePageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/disguise2') + iconColor = Vec4(0.7, 0.7, 0.7, 1) + iconModels.detachNode() + elif pageName == TTLocalizer.NPCFriendPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/playingCard') + iconImage = iconModels.find('**/card_back') + iconGeom = iconModels.find('**/logo') + iconScale = 0.22 + iconModels.detachNode() + elif pageName == TTLocalizer.KartPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/kartIcon') + iconModels.detachNode() + elif pageName == TTLocalizer.GolfPageTitle: + iconModels = loader.loadModel('phase_6/models/golf/golf_gui') + iconGeom = iconModels.find('**/score_card_icon') + iconModels.detachNode() + elif pageName == TTLocalizer.EventsPageName: + iconModels = loader.loadModel('phase_4/models/parties/partyStickerbook') + iconGeom = iconModels.find('**/Stickerbook_PartyIcon') + iconModels.detachNode() + elif pageName == TTLocalizer.PhotoPageTitle: + iconGeom = iconModels = loader.loadModel('phase_4/models/minigames/photogame_filmroll') + iconScale = (1.9, 1.5, 1.5) + iconModels.detachNode() + elif pageName == TTLocalizer.StatPageTitle: + iconGeom = iconModels = loader.loadModel('phase_3.5/models/gui/name_star') + iconColor = (0, 0.6, 1, 1) + iconModels.detachNode() + if pageName == TTLocalizer.OptionsPageTitle: + pageName = TTLocalizer.OptionsTabTitle + rightSide = pageIndex < 16 + pageTab = DirectButton(parent=self.pageTabFrames[rightSide], relief=DGG.RAISED, frameSize=(-0.575, + 0.575, + -0.575, + 0.575), borderWidth=(0.05, 0.05), text=('', + '', + pageName, + ''), text_align=TextNode.ALeft, text_pos=(1 if rightSide else -4, -0.2), text_scale=TTLocalizer.SBpageTab, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image=iconImage, image_scale=iconScale, geom=iconGeom, geom_scale=iconScale, geom_color=iconColor, pos=(0, 0, -yOffset), scale=0.06, command=buttonPressedCommand, extraArgs=extraArgs) + self.pageTabs.insert(pageIndex, pageTab) + return + + def setPage(self, page, enterPage = True): + if self.currPageIndex is not None: + self.pages[self.currPageIndex].exit() + self.currPageIndex = self.pages.index(page) + self.setPageTabIndex(self.currPageIndex) + if enterPage: + self.showPageArrows() + page.enter() + return + + def setPageTabIndex(self, pageTabIndex): + if self.currPageTabIndex is not None and pageTabIndex != self.currPageTabIndex: + self.pageTabs[self.currPageTabIndex]['relief'] = DGG.RAISED + self.currPageTabIndex = pageTabIndex + self.pageTabs[self.currPageTabIndex]['relief'] = DGG.SUNKEN + return + + def isOnPage(self, page): + result = False + if self.currPageIndex is not None: + curPage = self.pages[self.currPageIndex] + if curPage == page: + result = True + return result + + def obscureButton(self, obscured): + self.__obscured = obscured + self.__setButtonVisibility() + + def isObscured(self): + return self.__obscured + + def showButton(self): + self.__shown = 1 + self.__setButtonVisibility() + + def hideButton(self): + self.__shown = 0 + self.__setButtonVisibility() + + def __setButtonVisibility(self): + if self.__isOpen: + self.bookOpenButton.hide() + self.bookCloseButton.show() + elif self.__shown and not self.__obscured: + self.bookOpenButton.show() + self.bookCloseButton.hide() + else: + self.bookOpenButton.hide() + self.bookCloseButton.hide() + + def shouldBookButtonBeHidden(self): + result = False + if self.__isOpen: + pass + elif self.__shown and not self.__obscured: + pass + else: + result = True + return result + + def __open(self): + messenger.send('enterStickerBook') + if not localAvatar.getGardenStarted(): + for tab in self.pageTabs: + if tab['text'][2] == TTLocalizer.GardenPageTitle: + tab.hide() + + def __close(self): + base.playSfx(self.closeSound) + self.doneStatus = {'mode': 'close'} + messenger.send('exitStickerBook') + messenger.send(self.doneEvent) + + def closeBook(self): + self.__close() + + def __pageDone(self): + page = self.pages[self.currPageIndex] + pageDoneStatus = page.getDoneStatus() + if pageDoneStatus: + if pageDoneStatus['mode'] == 'close': + self.__close() + else: + self.doneStatus = pageDoneStatus + messenger.send(self.doneEvent) + + def __pageChange(self, offset): + messenger.send('wakeup') + base.playSfx(self.pageSound) + self.pages[self.currPageIndex].exit() + self.currPageIndex = self.currPageIndex + offset + messenger.send('stickerBookPageChange-' + str(self.currPageIndex)) + self.currPageIndex = max(self.currPageIndex, 0) + self.currPageIndex = min(self.currPageIndex, len(self.pages) - 1) + self.setPageTabIndex(self.currPageIndex) + self.showPageArrows() + page = self.pages[self.currPageIndex] + page.enter() + + def showPageArrows(self): + if self.currPageIndex == len(self.pages) - 1: + self.prevArrow.show() + self.nextArrow.hide() + else: + self.prevArrow.show() + self.nextArrow.show() + self.__checkForPage() + if self.currPageIndex == 0: + self.prevArrow.hide() + self.nextArrow.show() + + def __checkForPage(self): + self.accept('arrow_right', self.__pageChange, [1]) + self.accept('arrow_left', self.__pageChange, [-1]) + + def disableBookCloseButton(self): + if self.bookCloseButton: + self.bookCloseButton['command'] = None + return + + def enableBookCloseButton(self): + if self.bookCloseButton: + self.bookCloseButton['command'] = self.__close + + def disableAllPageTabs(self): + for button in self.pageTabs: + button['state'] = DGG.DISABLED + + def enableAllPageTabs(self): + for button in self.pageTabs: + button['state'] = DGG.NORMAL diff --git a/toontown/shtiker/ShtikerPage.py b/toontown/shtiker/ShtikerPage.py new file mode 100755 index 00000000..eb84e018 --- /dev/null +++ b/toontown/shtiker/ShtikerPage.py @@ -0,0 +1,45 @@ +import ShtikerBook +from direct.fsm import StateData +from direct.gui.DirectGui import * +from panda3d.core import * + +class ShtikerPage(DirectFrame, StateData.StateData): + + def __init__(self): + DirectFrame.__init__(self, relief=None, sortOrder=DGG.BACKGROUND_SORT_INDEX) + self.initialiseoptions(ShtikerPage) + StateData.StateData.__init__(self, 'shtiker-page-done') + self.book = None + self.hide() + return + + def load(self): + pass + + def unload(self): + self.ignoreAll() + del self.book + + def enter(self): + self.show() + + def exit(self): + self.hide() + + def setBook(self, book): + self.book = book + + def setPageName(self, pageName): + self.pageName = pageName + + def makePageWhite(self, item): + white = Vec4(1, 1, 1, 1) + self.book['image_color'] = white + self.book.nextArrow['image_color'] = white + self.book.prevArrow['image_color'] = white + + def makePageRed(self, item): + red = Vec4(1, 0.5, 0.5, 1) + self.book['image_color'] = red + self.book.nextArrow['image_color'] = red + self.book.prevArrow['image_color'] = red diff --git a/toontown/shtiker/StatPage.py b/toontown/shtiker/StatPage.py new file mode 100644 index 00000000..a945aae3 --- /dev/null +++ b/toontown/shtiker/StatPage.py @@ -0,0 +1,77 @@ +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer, ToontownGlobals +from toontown.toontowngui import TTDialog +import ShtikerPage + +class StatPage(ShtikerPage.ShtikerPage): + def __init__(self): + + ShtikerPage.ShtikerPage.__init__(self) + self.dialog = None + self.chunkCount = 11 + + def load(self): + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + + self.rows = [self.createRow(pos) for pos in ((-0.8, 0, 0.435), (0.08, 0, 0.435))] + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.StatPageTitle, text_scale=0.12, textMayChange=0, pos=(-0.625, 0, 0.625)) + self.resetButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(1.1, 1, 1), text=TTLocalizer.StatPageClear, text_scale=0.055, text_pos=(0, -0.02), pos=(0.605, 0, 0.66), command=self.__showDialog) + guiButton.removeNode() + + def enter(self): + self.show() + self.updateStats() + self.accept('refreshStats', self.updateStats) + + def exit(self): + self.ignoreAll() + self.unloadDialog() + self.hide() + + def unload(self): + for row in self.rows: + row.destroy() + + del self.rows + self.unloadDialog() + self.title.destroy() + del self.title + self.resetButton.destroy() + del self.resetButton + ShtikerPage.ShtikerPage.unload(self) + + def unloadDialog(self, arg=None): + if self.dialog: + self.dialog.destroy() + self.dialog = None + + def createRow(self, pos): + row = DirectLabel(parent=self, relief=None, text_align=TextNode.ALeft, text='', text_scale=0.045, text_wordwrap=16, text_font=ToontownGlobals.getChalkFont(), text_fg=(1, 1, 1, 1), image='phase_3/maps/stat_board.png', image_scale=(0.42, 0, 0.6), image_pos=(0.35, 0, -0.45)) + row.setPos(pos) + return row + + def cutToChunks(self, list, size): + for i in xrange(0, len(list), size): + yield list[i:i+size] + + def updateStats(self): + stats = base.localAvatar.stats + allStats = [TTLocalizer.Stats[i] % '{:,}'.format(stats[i]) for i in xrange(len(stats))] + textChunks = list(self.cutToChunks(allStats, self.chunkCount)) + + for i, chunk in enumerate(textChunks): + self.rows[i]['text'] = '\n\n'.join(chunk) + + def __showDialog(self): + self.dialog = TTDialog.TTDialog(style=TTDialog.TwoChoice, text=TTLocalizer.StatPageClearAsk, text_wordwrap=15, command=self.__handleDialogResponse) + self.dialog.show() + + def __handleDialogResponse(self, response): + self.unloadDialog() + + if response <= 0: + return + + base.localAvatar.wipeStats() + self.dialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.StatPageClearDone, text_wordwrap=15, command=self.unloadDialog) + self.dialog.show() \ No newline at end of file diff --git a/toontown/shtiker/SuitPage.py b/toontown/shtiker/SuitPage.py new file mode 100755 index 00000000..b4606f0f --- /dev/null +++ b/toontown/shtiker/SuitPage.py @@ -0,0 +1,605 @@ +import ShtikerPage +from direct.task.Task import Task +import SummonCogDialog +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.suit import SuitDNA +from toontown.suit import Suit +from toontown.battle import SuitBattleGlobals +from CogPageGlobals import * +SCALE_FACTOR = 1.5 +RADAR_DELAY = 0.2 +BUILDING_RADAR_POS = (0.375, + 0.065, + -0.225, + -0.5) +PANEL_COLORS = (Vec4(0.8, 0.78, 0.77, 1), + Vec4(0.75, 0.78, 0.8, 1), + Vec4(0.75, 0.82, 0.79, 1), + Vec4(0.825, 0.76, 0.77, 1)) +PANEL_COLORS_COMPLETE1 = (Vec4(0.7, 0.725, 0.545, 1), + Vec4(0.625, 0.725, 0.65, 1), + Vec4(0.6, 0.75, 0.525, 1), + Vec4(0.675, 0.675, 0.55, 1)) +PANEL_COLORS_COMPLETE2 = (Vec4(0.9, 0.725, 0.32, 1), + Vec4(0.825, 0.725, 0.45, 1), + Vec4(0.8, 0.75, 0.325, 1), + Vec4(0.875, 0.675, 0.35, 1)) +SHADOW_SCALE_POS = ((1.225, + 0, + 10, + -0.03), + (0.9, + 0, + 10, + 0), + (1.125, + 0, + 10, + -0.015), + (1.0, + 0, + 10, + -0.02), + (1.0, + -0.02, + 10, + -0.01), + (1.05, + 0, + 10, + -0.0425), + (1.0, + 0, + 10, + -0.05), + (0.9, + -0.0225, + 10, + -0.025), + (1.25, + 0, + 10, + -0.03), + (1.0, + 0, + 10, + -0.01), + (1.0, + 0.005, + 10, + -0.01), + (1.0, + 0, + 10, + -0.01), + (1.05, + 0, + 10, + -0.01), + (0.95, + 0, + 10, + -0.01), + (1.125, + 0.005, + 10, + -0.035), + (0.85, + -0.005, + 10, + -0.035), + (1.2, + 0, + 10, + -0.01), + (1.05, + 0, + 10, + 0), + (1.1, + 0, + 10, + -0.04), + (1.0, + 0, + 10, + 0), + (0.95, + 0.0175, + 10, + -0.015), + (1.0, + 0, + 10, + -0.06), + (0.95, + 0.02, + 10, + -0.0175), + (0.9, + 0, + 10, + -0.03), + (1.15, + 0, + 10, + -0.01), + (1.0, + 0, + 10, + 0), + (1.0, + 0, + 10, + 0), + (1.1, + 0, + 10, + -0.04), + (0.93, + 0.005, + 10, + -0.01), + (0.95, + 0.005, + 10, + -0.01), + (1.0, + 0, + 10, + -0.02), + (0.9, + 0.0025, + 10, + -0.03)) + +class SuitPage(ShtikerPage.ShtikerPage): + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + + def load(self): + ShtikerPage.ShtikerPage.load(self) + frameModel = loader.loadModel('phase_3.5/models/gui/suitpage_frame') + frameModel.setScale(0.0253125, 0.03, 0.045) + frameModel.setPos(0, 10, -0.575) + self.guiTop = NodePath('guiTop') + self.guiTop.reparentTo(self) + self.frameNode = NodePath('frameNode') + self.frameNode.reparentTo(self.guiTop) + self.panelNode = NodePath('panelNode') + self.panelNode.reparentTo(self.guiTop) + self.iconNode = NodePath('iconNode') + self.iconNode.reparentTo(self.guiTop) + self.enlargedPanelNode = NodePath('enlargedPanelNode') + self.enlargedPanelNode.reparentTo(self.guiTop) + frame = frameModel.find('**/frame') + frame.wrtReparentTo(self.frameNode) + screws = frameModel.find('**/screws') + screws.wrtReparentTo(self.iconNode) + icons = frameModel.find('**/icons') + del frameModel + self.title = DirectLabel(parent=self.iconNode, relief=None, text=TTLocalizer.SuitPageTitle, text_scale=0.1, text_pos=(0.04, 0), textMayChange=0) + self.radarButtons = [] + icon = icons.find('**/corp_icon') + self.corpRadarButton = DirectButton(parent=self.iconNode, relief=None, state=DGG.DISABLED, image=icon, image_scale=(0.03375, 1, 0.045), image2_color=Vec4(1.0, 1.0, 1.0, 0.75), pos=(-0.2, 10, -0.575), command=self.toggleRadar, extraArgs=[0]) + self.radarButtons.append(self.corpRadarButton) + icon = icons.find('**/legal_icon') + self.legalRadarButton = DirectButton(parent=self.iconNode, relief=None, state=DGG.DISABLED, image=icon, image_scale=(0.03375, 1, 0.045), image2_color=Vec4(1.0, 1.0, 1.0, 0.75), pos=(-0.2, 10, -0.575), command=self.toggleRadar, extraArgs=[1]) + self.radarButtons.append(self.legalRadarButton) + icon = icons.find('**/money_icon') + self.moneyRadarButton = DirectButton(parent=self.iconNode, relief=None, state=DGG.DISABLED, image=(icon, icon, icon), image_scale=(0.03375, 1, 0.045), image2_color=Vec4(1.0, 1.0, 1.0, 0.75), pos=(-0.2, 10, -0.575), command=self.toggleRadar, extraArgs=[2]) + self.radarButtons.append(self.moneyRadarButton) + icon = icons.find('**/sales_icon') + self.salesRadarButton = DirectButton(parent=self.iconNode, relief=None, state=DGG.DISABLED, image=(icon, icon, icon), image_scale=(0.03375, 1, 0.045), image2_color=Vec4(1.0, 1.0, 1.0, 0.75), pos=(-0.2, 10, -0.575), command=self.toggleRadar, extraArgs=[3]) + self.radarButtons.append(self.salesRadarButton) + for radarButton in self.radarButtons: + radarButton.building = 0 + radarButton.buildingRadarLabel = None + gui = loader.loadModel('phase_3.5/models/gui/suitpage_gui') + self.panelModel = gui.find('**/card') + self.shadowModels = [] + for index in xrange(1, len(SuitDNA.suitHeadTypes) + 1): + self.shadowModels.append(gui.find('**/shadow' + str(index))) + del gui + self.makePanels() + self.radarOn = [0, + 0, + 0, + 0] + priceScale = 0.1 + emblemIcon = loader.loadModel('phase_3.5/models/gui/tt_m_gui_gen_emblemIcons') + silverModel = emblemIcon.find('**/tt_t_gui_gen_emblemSilver') + goldModel = emblemIcon.find('**/tt_t_gui_gen_emblemGold') + self.silverLabel = DirectLabel(parent=self, relief=None, pos=(-0.25, 0, -0.69), scale=priceScale, image=silverModel, image_pos=(-0.4, 0, 0.4), text=str(localAvatar.emblems[ToontownGlobals.EmblemTypes.Silver]), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft) + self.goldLabel = DirectLabel(parent=self, relief=None, pos=(0.25, 0, -0.69), scale=priceScale, image=goldModel, image_pos=(-0.4, 0, 0.4), text=str(localAvatar.emblems[ToontownGlobals.EmblemTypes.Gold]), text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft) + if not base.cr.wantEmblems: + self.silverLabel.hide() + self.goldLabel.hide() + self.accept(localAvatar.uniqueName('emblemsChange'), self.__emblemChange) + self.guiTop.setZ(0.625) + return + + def unload(self): + self.ignoreAll() + self.title.destroy() + self.corpRadarButton.destroy() + self.legalRadarButton.destroy() + self.moneyRadarButton.destroy() + self.salesRadarButton.destroy() + self.rolloverFrame.destroy() + for panel in self.panels: + panel.destroy() + del self.panels + for shadow in self.shadowModels: + shadow.removeNode() + + self.panelModel.removeNode() + ShtikerPage.ShtikerPage.unload(self) + + def enter(self): + self.updatePage() + self.bigPanel = None + self.nextPanel = None + ShtikerPage.ShtikerPage.enter(self) + return + + def exit(self): + taskMgr.remove('buildingListResponseTimeout-later') + taskMgr.remove('suitListResponseTimeout-later') + taskMgr.remove('showCogRadarLater') + taskMgr.remove('showBuildingRadarLater') + for index in xrange(0, len(self.radarOn)): + if self.radarOn[index]: + self.toggleRadar(index) + self.radarButtons[index]['state'] = DGG.NORMAL + + ShtikerPage.ShtikerPage.exit(self) + + def __emblemChange(self, newEmblems): + self.silverLabel['text'] = str(newEmblems[0]) + self.goldLabel['text'] = str(newEmblems[1]) + + def grow(self, panel, pos): + if self.bigPanel: + print 'setting next panel - ' + str(panel) + self.nextPanel = panel + self.nextPanelPos = pos + return + print 'big panel - ' + str(panel) + self.bigPanel = panel + panel.reparentTo(self.enlargedPanelNode) + panel.setScale(panel.getScale() * SCALE_FACTOR) + if panel.summonButton: + panel.summonButton.show() + panel.summonButton['state'] = DGG.NORMAL + + def shrink(self, panel, pos): + print 'trying to shrink - ' + str(panel) + if panel != self.bigPanel: + self.nextPanel = None + return + print 'shrink panel - ' + str(panel) + self.bigPanel = None + panel.setScale(panel.scale) + panel.reparentTo(self.panelNode) + if panel.summonButton: + panel.summonButton.hide() + panel.summonButton['state'] = DGG.DISABLED + if self.nextPanel: + self.grow(self.nextPanel, self.nextPanelPos) + return + + def toggleRadar(self, deptNum): + messenger.send('wakeup') + if self.radarOn[deptNum]: + self.radarOn[deptNum] = 0 + else: + self.radarOn[deptNum] = 1 + deptSize = SuitDNA.suitsPerDept + panels = self.panels[deptSize * deptNum:SuitDNA.suitsPerDept * (deptNum + 1)] + if self.radarOn[deptNum]: + if hasattr(base.cr, 'currSuitPlanner'): + if base.cr.currSuitPlanner != None: + base.cr.currSuitPlanner.d_suitListQuery() + self.acceptOnce('suitListResponse', self.updateCogRadar, extraArgs=[deptNum, panels]) + taskMgr.doMethodLater(1.0, self.suitListResponseTimeout, 'suitListResponseTimeout-later', extraArgs=(deptNum, panels)) + if self.radarButtons[deptNum].building: + base.cr.currSuitPlanner.d_buildingListQuery() + self.acceptOnce('buildingListResponse', self.updateBuildingRadar, extraArgs=[deptNum]) + taskMgr.doMethodLater(1.0, self.buildingListResponseTimeout, 'buildingListResponseTimeout-later', extraArgs=(deptNum,)) + else: + self.updateCogRadar(deptNum, panels) + self.updateBuildingRadar(deptNum) + else: + self.updateCogRadar(deptNum, panels) + self.updateBuildingRadar(deptNum) + self.radarButtons[deptNum]['state'] = DGG.DISABLED + else: + self.updateCogRadar(deptNum, panels) + self.updateBuildingRadar(deptNum) + return + + def suitListResponseTimeout(self, deptNum, panels): + self.updateCogRadar(deptNum, panels, 1) + + def buildingListResponseTimeout(self, deptNum): + self.updateBuildingRadar(deptNum, 1) + + def makePanels(self): + self.panels = [] + base.panels = [] + xStart = -0.66 + yStart = -0.18 + xOffset = 0.199 + yOffset = 0.284 + gui = loader.loadModel('phase_3.5/models/gui/suit_detail_panel') + gui.find('**/avatar_panel/shadow').setColor(1, 1, 1, 0.5) + self.rolloverFrame = DirectFrame(parent=self.panelNode, relief=None, geom=gui.find('**/avatar_panel'), geom_color=(0.5, 0.5, 0.5, 1), geom_scale=(0.59, 0, 0.21), text_scale=0.06, text_pos=(0, 0.35), text='', text_fg=(1, 1, 1, 1), text_font=ToontownGlobals.getSuitFont(), pos=(0.8, 0, 0)) + self.rolloverFrame.setBin('gui-popup', 0) + self.rolloverFrame.hide() + gui.removeNode() + for dept in xrange(0, len(SuitDNA.suitDepts)): + row = [] + color = PANEL_COLORS[dept] + for type in xrange(0, SuitDNA.suitsPerDept): + panel = DirectLabel(parent=self.panelNode, pos=(xStart + type * xOffset, 0.0, yStart - dept * yOffset), relief=None, state=DGG.NORMAL, image=self.panelModel, image_scale=(1, 1, 1), image_color=color, text=TTLocalizer.SuitPageMystery, text_scale=0.045, text_fg=(0, 0, 0, 1), text_pos=(0, 0.185, 0), text_font=ToontownGlobals.getSuitFont(), text_wordwrap=7) + panel.scale = 0.6 + panel.setScale(panel.scale) + panel.quotaLabel = None + panel.head = None + panel.shadow = None + panel.count = 0 + panel.summonButton = None + panel.hoverButton = DirectButton(parent=panel, relief=None, image_scale=(0.15, 0, 0.225), image='phase_3/maps/invisible.png', pressEffect=0) + panel.hoverButton.setTransparency(True) + panel.hoverButton.panel = panel + self.addCogRadarLabel(panel) + self.panels.append(panel) + base.panels.append(panel) + + def showInfo(self, panel, text, extra): + self.rolloverFrame.reparentTo(panel) + self.rolloverFrame.show() + self.rolloverFrame['text'] = text + + def hideInfo(self, extra): + self.rolloverFrame.hide() + + def addQuotaLabel(self, panel): + index = self.panels.index(panel) + count = str(base.localAvatar.cogCounts[index]) + if base.localAvatar.cogs[index] < COG_COMPLETE1: + quota = str(COG_QUOTAS[0][index % SuitDNA.suitsPerDept]) + else: + quota = str(COG_QUOTAS[1][index % SuitDNA.suitsPerDept]) + quotaLabel = DirectLabel(parent=panel, pos=(0.0, 0.0, -0.215), relief=None, state=DGG.DISABLED, text=TTLocalizer.SuitPageQuota % (count, quota), text_scale=0.045, text_fg=(0, 0, 0, 1), text_font=ToontownGlobals.getSuitFont()) + panel.quotaLabel = quotaLabel + return + + def addSuitHead(self, panel, suitName): + panelIndex = self.panels.index(panel) + shadow = panel.attachNewNode('shadow') + shadowModel = self.shadowModels[panelIndex] + shadowModel.copyTo(shadow) + coords = SHADOW_SCALE_POS[panelIndex] + shadow.setScale(coords[0]) + shadow.setPos(coords[1], coords[2], coords[3]) + panel.shadow = shadow + panel.head = Suit.attachSuitHead(panel, suitName) + + def addCogRadarLabel(self, panel): + cogRadarLabel = DirectLabel(parent=panel, pos=(0.0, 0.0, -0.215), relief=None, state=DGG.DISABLED, text='', text_scale=0.05, text_fg=(0, 0, 0, 1), text_font=ToontownGlobals.getSuitFont()) + panel.cogRadarLabel = cogRadarLabel + return + + def addSummonButton(self, panel): + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + gui = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + iconGeom = gui.find('**/summons') + summonButton = DirectButton(parent=panel, pos=(0.1, 0.0, -0.13), scale=0.1, relief=None, state=DGG.NORMAL, image=okButtonList, image_scale=13.0, geom=iconGeom, geom_scale=0.7, text=('', + TTLocalizer.IssueSummons, + TTLocalizer.IssueSummons, + ''), text_scale=0.4, text_pos=(-1.1, -0.4), command=self.summonButtonPressed, extraArgs=[panel]) + panel.summonButton = summonButton + return + + def summonButtonPressed(self, panel): + panelIndex = self.panels.index(panel) + self.summonDialog = SummonCogDialog.SummonCogDialog(panelIndex) + self.summonDialog.load() + self.accept(self.summonDialog.doneEvent, self.summonDone, extraArgs=[panel]) + self.summonDialog.enter() + + def summonDone(self, panel): + if self.summonDialog: + self.summonDialog.unload() + self.summonDialog = None + index = self.panels.index(panel) + if not base.localAvatar.hasCogSummons(index): + panel.summonButton.hide() + + def addBuildingRadarLabel(self, button): + gui = loader.loadModel('phase_3.5/models/gui/suit_detail_panel') + zPos = BUILDING_RADAR_POS[self.radarButtons.index(button)] + buildingRadarLabel = DirectLabel(parent=button, relief=None, pos=(0.225, 0.0, zPos), state=DGG.DISABLED, image=gui.find('**/avatar_panel'), image_hpr=(0, 0, 90), image_scale=(0.05, 1, 0.1), image_pos=(0, 0, 0.015), text=TTLocalizer.SuitPageBuildingRadarP % '0', text_scale=0.05, text_fg=(1, 0, 0, 1), text_font=ToontownGlobals.getSuitFont()) + gui.removeNode() + button.buildingRadarLabel = buildingRadarLabel + + def resetPanel(self, dept, type): + panel = self.panels[dept * SuitDNA.suitsPerDept + type] + panel['text'] = TTLocalizer.SuitPageMystery + if panel.cogRadarLabel: + panel.cogRadarLabel.hide() + if panel.quotaLabel: + panel.quotaLabel.hide() + if panel.head: + panel.head.hide() + if panel.shadow: + panel.shadow.hide() + if panel.summonButton: + panel.summonButton.hide() + self.rolloverFrame.hide() + panel.hoverButton.unbind(DGG.ENTER) + panel.hoverButton.unbind(DGG.EXIT) + color = PANEL_COLORS[dept] + panel['image_color'] = color + for button in self.radarButtons: + if button.buildingRadarLabel: + button.buildingRadarLabel.hide() + + def setPanelStatus(self, panel, status): + index = self.panels.index(panel) + if status == COG_UNSEEN: + panel['text'] = TTLocalizer.SuitPageMystery + elif status == COG_BATTLED: + suitName = SuitDNA.suitHeadTypes[index] + suitFullName = SuitBattleGlobals.SuitAttributes[suitName]['name'] + panel['text'] = suitFullName + if panel.quotaLabel: + panel.quotaLabel.show() + else: + self.addQuotaLabel(panel) + if panel.head and panel.shadow: + panel.head.show() + panel.shadow.show() + else: + self.addSuitHead(panel, suitName) + if base.localAvatar.hasCogSummons(index): + if panel.summonButton: + panel.summonButton.show() + else: + self.addSummonButton(panel) + elif status == COG_DEFEATED: + count = str(base.localAvatar.cogCounts[index]) + if base.localAvatar.cogs[index] < COG_COMPLETE1: + quota = str(COG_QUOTAS[0][index % SuitDNA.suitsPerDept]) + else: + quota = str(COG_QUOTAS[1][index % SuitDNA.suitsPerDept]) + panel.quotaLabel['text'] = TTLocalizer.SuitPageQuota % (count, quota) + elif status == COG_COMPLETE1: + panel['image_color'] = PANEL_COLORS_COMPLETE1[index / SuitDNA.suitsPerDept] + elif status == COG_COMPLETE2: + panel['image_color'] = PANEL_COLORS_COMPLETE2[index / SuitDNA.suitsPerDept] + if status in (COG_DEFEATED, COG_COMPLETE1, COG_COMPLETE2): + name = SuitDNA.suitHeadTypes[index] + attributes = SuitBattleGlobals.SuitAttributes[name] + level = attributes['level'] + groupAttacks, singleAttacks = SuitBattleGlobals.getAttacksByType(attributes) + info = TTLocalizer.SuitPageAttackFormat % (level + 1, level + 5, self.getAttackStrings(groupAttacks), self.getAttackStrings(singleAttacks)) + + panel.hoverButton.bind(DGG.ENTER, self.showInfo, extraArgs=[panel, info]) + panel.hoverButton.bind(DGG.EXIT, self.hideInfo) + + def getAttackStrings(self, attacks): + string = '\n'.join(['%s %s' % (TTLocalizer.SuitAttackNames[attack[0]], '-'.join(str(x) for x in attack[1])) for attack in attacks]) + return string if string else TTLocalizer.SuitPageNoAttacks + + def updateAllCogs(self, status): + for index in xrange(0, len(base.localAvatar.cogs)): + base.localAvatar.cogs[index] = status + self.updatePage() + + def updatePage(self): + index = 0 + cogs = base.localAvatar.cogs + for dept in xrange(0, len(SuitDNA.suitDepts)): + for type in xrange(0, SuitDNA.suitsPerDept): + self.updateCogStatus(dept, type, cogs[index]) + index += 1 + self.updateCogRadarButtons(base.localAvatar.cogRadar) + self.updateBuildingRadarButtons(base.localAvatar.buildingRadar) + + def updateCogStatus(self, dept, type, status): + if dept < 0 or dept > len(SuitDNA.suitDepts): + print 'ucs: bad cog dept: ', dept + elif type < 0 or type > SuitDNA.suitsPerDept: + print 'ucs: bad cog type: ', type + elif status < COG_UNSEEN or status > COG_COMPLETE2: + print 'ucs: bad status: ', status + else: + self.resetPanel(dept, type) + panel = self.panels[dept * SuitDNA.suitsPerDept + type] + if status == COG_UNSEEN: + self.setPanelStatus(panel, COG_UNSEEN) + elif status == COG_BATTLED: + self.setPanelStatus(panel, COG_BATTLED) + elif status == COG_DEFEATED: + self.setPanelStatus(panel, COG_BATTLED) + self.setPanelStatus(panel, COG_DEFEATED) + elif status == COG_COMPLETE1: + self.setPanelStatus(panel, COG_BATTLED) + self.setPanelStatus(panel, COG_DEFEATED) + self.setPanelStatus(panel, COG_COMPLETE1) + elif status == COG_COMPLETE2: + self.setPanelStatus(panel, COG_BATTLED) + self.setPanelStatus(panel, COG_DEFEATED) + self.setPanelStatus(panel, COG_COMPLETE2) + + def updateCogRadarButtons(self, radars): + for index in xrange(0, len(radars)): + if radars[index] == 1: + self.radarButtons[index]['state'] = DGG.NORMAL + + def updateCogRadar(self, deptNum, panels, timeout = 0): + taskMgr.remove('suitListResponseTimeout-later') + if not timeout and hasattr(base.cr, 'currSuitPlanner') and base.cr.currSuitPlanner != None: + cogList = base.cr.currSuitPlanner.suitList + else: + cogList = [] + for panel in panels: + panel.count = 0 + for cog in cogList: + self.panels[cog].count += 1 + for panel in panels: + panel.cogRadarLabel['text'] = TTLocalizer.SuitPageCogRadar % panel.count + if self.radarOn[deptNum]: + panel.quotaLabel.hide() + def showLabel(label): + label.show() + taskMgr.doMethodLater(RADAR_DELAY * panels.index(panel), showLabel, 'showCogRadarLater', extraArgs=(panel.cogRadarLabel,)) + def activateButton(s = self, index = deptNum): + self.radarButtons[index]['state'] = DGG.NORMAL + return Task.done + if not self.radarButtons[deptNum].building: + taskMgr.doMethodLater(RADAR_DELAY * len(panels), activateButton, 'activateButtonLater') + else: + panel.cogRadarLabel.hide() + panel.quotaLabel.show() + return + + def updateBuildingRadarButtons(self, radars): + for index in xrange(0, len(radars)): + if radars[index] == 1: + self.radarButtons[index].building = 1 + + def updateBuildingRadar(self, deptNum, timeout = 0): + taskMgr.remove('buildingListResponseTimeout-later') + if not timeout and hasattr(base.cr, 'currSuitPlanner') and base.cr.currSuitPlanner != None: + buildingList = base.cr.currSuitPlanner.buildingList + else: + buildingList = [0, + 0, + 0, + 0] + button = self.radarButtons[deptNum] + if button.building: + if not button.buildingRadarLabel: + self.addBuildingRadarLabel(button) + if self.radarOn[deptNum]: + num = buildingList[deptNum] + if num == 1: + button.buildingRadarLabel['text'] = TTLocalizer.SuitPageBuildingRadarS % num + else: + button.buildingRadarLabel['text'] = TTLocalizer.SuitPageBuildingRadarP % num + def showLabel(button): + button.buildingRadarLabel.show() + button['state'] = DGG.NORMAL + + taskMgr.doMethodLater(RADAR_DELAY * SuitDNA.suitsPerDept, showLabel, 'showBuildingRadarLater', extraArgs=(button,)) + else: + button.buildingRadarLabel.hide() diff --git a/toontown/shtiker/SummonCogDialog.py b/toontown/shtiker/SummonCogDialog.py new file mode 100755 index 00000000..65a8aaac --- /dev/null +++ b/toontown/shtiker/SummonCogDialog.py @@ -0,0 +1,190 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownGlobals +from toontown.suit import SuitDNA +from toontown.suit import Suit +from toontown.battle import SuitBattleGlobals +from toontown.toon import NPCToons + +class SummonCogDialog(DirectFrame, StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('SummonCogDialog') + notify.setInfo(True) + + def __init__(self, suitIndex): + DirectFrame.__init__(self, parent=aspect2dp, pos=(0, 0, 0.3), relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(1.6, 1, 1.07), image_pos=(0, 0, 0.09), image_color=ToontownGlobals.GlobalDialogColor, text=TTLocalizer.SummonDlgTitle, text_scale=0.12, text_pos=(0, 0.5), borderWidth=(0.01, 0.01), sortOrder=NO_FADE_SORT_INDEX) + StateData.StateData.__init__(self, 'summon-cog-done') + self.initialiseoptions(SummonCogDialog) + self.suitIndex = suitIndex + base.summonDialog = self + self.popup = None + self.suitName = SuitDNA.suitHeadTypes[self.suitIndex] + self.suitFullName = SuitBattleGlobals.SuitAttributes[self.suitName]['name'] + return + + def unload(self): + if self.isLoaded == 0: + return None + self.isLoaded = 0 + self.exit() + DirectFrame.destroy(self) + return None + + def load(self): + if self.isLoaded == 1: + return None + self.isLoaded = 1 + gui = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.head = Suit.attachSuitHead(self, self.suitName) + z = self.head.getZ() + self.head.setPos(-0.4, -0.1, z + 0.14) + self.suitLabel = DirectLabel(parent=self, relief=None, text=self.suitFullName, text_font=ToontownGlobals.getSuitFont(), pos=(-0.4, 0, -0.085), scale=0.07) + closeButtonImage = (gui.find('**/CloseBtn_UP'), gui.find('**/CloseBtn_DN'), gui.find('**/CloseBtn_Rllvr')) + buttonImage = (guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')) + disabledColor = Vec4(0.5, 0.5, 0.5, 1) + base.cr.lmao = self + self.summonBuildingButton = DirectButton(parent=self, relief=None, text=TTLocalizer.SummonDlgButton1, image=buttonImage, image_scale=(1.7, 1, 1), image3_color=disabledColor, text_scale=0.06, text_pos=(0, -0.01), pos=(0.3, 0, 0.35), command=self.issueSummons, extraArgs=['building']) + self.summonInvasionButton = DirectButton(parent=self, relief=None, text=TTLocalizer.SummonDlgButton2, image=buttonImage, image_scale=(1.7, 1, 1), image3_color=disabledColor, text_scale=0.06, text_pos=(0, -0.01), pos=(0.3, 0, 0.225), command=self.issueSummons, extraArgs=['invasion']) + self.summonCogdoButton = DirectButton(parent=self, relief=None, text=TTLocalizer.SummonDlgButton3, image=buttonImage, image_scale=(1.7, 1, 1), image3_color=disabledColor, text_scale=0.06, text_pos=(0, -0.01), pos=(0.3, 0, 0.1), command=self.issueSummons, extraArgs=['cogdo']) + self.summonSkelInvasionButton = DirectButton(parent=self, relief=None, text=TTLocalizer.SummonDlgButton4, image=buttonImage, image_scale=(1.7, 1, 1), image3_color=disabledColor, text_scale=0.051, text_pos=(0, -0.01), pos=(0.3, 0, -0.025), command=self.issueSummons, extraArgs=['skelinvasion']) + self.summonWaiterInvasionButton = DirectButton(parent=self, relief=None, text=TTLocalizer.SummonDlgButton5, image=buttonImage, image_scale=(1.7, 1, 1), image3_color=disabledColor, text_scale=0.056, text_pos=(0, -0.01), pos=(0.3, 0, -0.15), command=self.issueSummons, extraArgs=['waiterinvasion']) + self.summonV2InvasionButton = DirectButton(parent=self, relief=None, text=TTLocalizer.SummonDlgButton6, image=buttonImage, image_scale=(1.7, 1, 1), image3_color=disabledColor, text_scale=0.048, text_pos=(0, -0.01), pos=(0.3, 0, -0.275), command=self.issueSummons, extraArgs=['v2invasion']) + self.statusLabel = DirectLabel(parent=self, relief=None, text='', text_wordwrap=12, pos=(0.3, 0, 0.3), scale=0.07) + self.cancel = DirectButton(parent=self, relief=None, image=closeButtonImage, pos=(0.7, 0, -0.366), command=self.__cancel) + gui.removeNode() + guiButton.removeNode() + self.hide() + return + + def enter(self): + if self.isEntered == 1: + return None + self.isEntered = 1 + if self.isLoaded == 0: + self.load() + self.disableButtons() + self.enableButtons() + self.popup = None + base.transitions.fadeScreen(0.5) + self.show() + return + + def exit(self): + if self.isEntered == 0: + return None + self.isEntered = 0 + self.cleanupDialogs() + base.transitions.noTransitions() + self.ignoreAll() + self.hide() + messenger.send(self.doneEvent, []) + return None + + def cleanupDialogs(self): + self.head = None + if self.popup != None: + self.popup.cleanup() + self.popup = None + return + + def cogSummonsDone(self, returnCode, suitIndex, buildingId): + self.cancel['state'] = DGG.NORMAL + if self.summonsType == 'building' or self.summonsType == 'cogdo': + if returnCode == 'success': + building = base.cr.doId2do.get(buildingId) + dnaStore = base.cr.playGame.dnaStore + buildingTitle = dnaStore.getTitleFromBlockNumber(building.block) + buildingInteriorZone = building.zoneId + 500 + building.block + npcName = TTLocalizer.SummonDlgShopkeeper + npcId = NPCToons.zone2NpcDict.get(buildingInteriorZone) + if npcId: + npcName = NPCToons.getNPCName(npcId[0]) + if buildingTitle: + self.statusLabel['text'] = TTLocalizer.SummonDlgBldgSuccess % (npcName, buildingTitle) + else: + self.statusLabel['text'] = TTLocalizer.SummonDlgBldgSuccess2 + elif returnCode == 'badlocation': + self.statusLabel['text'] = TTLocalizer.SummonDlgBldgBadLoc + elif returnCode == 'fail': + self.statusLabel['text'] = TTLocalizer.SummonDlgInvasionFail + elif returnCode == 'disabled': + self.statusLabel['text'] = TTLocalizer.SummonDlgBldgNoCogdos + elif self.summonsType.endswith('invasion'): + if returnCode == 'success': + self.statusLabel['text'] = TTLocalizer.SummonDlgInvasionSuccess + elif returnCode == 'busy': + self.statusLabel['text'] = TTLocalizer.SummonDlgInvasionBusy % self.suitFullName + elif returnCode == 'fail': + self.statusLabel['text'] = TTLocalizer.SummonDlgInvasionFail + + def hideSummonButtons(self): + self.summonBuildingButton.hide() + self.summonInvasionButton.hide() + self.summonCogdoButton.hide() + self.summonSkelInvasionButton.hide() + self.summonWaiterInvasionButton.hide() + self.summonV2InvasionButton.hide() + + def issueSummons(self, summonsType): + if summonsType == 'building' or summonsType == 'cogdo': + text = TTLocalizer.SummonDlgBuildingConf + elif summonsType == 'invasion': + text = TTLocalizer.SummonDlgInvasionConf + elif summonsType == 'skelinvasion': + text = TTLocalizer.SummonDlgSkelInvasionConf + elif summonsType == 'waiterinvasion': + text = TTLocalizer.SummonDlgWaiterInvasionConf + elif summonsType == 'v2invasion': + text = TTLocalizer.SummonDlgV2InvasionConf + text = text % self.suitFullName + + def handleResponse(resp): + self.popup.cleanup() + self.popup = None + self.reparentTo(self.getParent(), NO_FADE_SORT_INDEX) + base.transitions.fadeScreen(0.5) + if resp == DGG.DIALOG_OK: + self.notify.info('issuing %s summons for %s' % (summonsType, self.suitIndex)) + self.accept('cog-summons-response', self.cogSummonsDone) + self.summonsType = summonsType + self.doIssueSummonsText() + base.localAvatar.d_reqCogSummons(self.summonsType, self.suitIndex) + self.hideSummonButtons() + self.cancel['state'] = DGG.DISABLED + return + + self.reparentTo(self.getParent(), 0) + self.popup = TTDialog.TTDialog(parent=aspect2dp, style=TTDialog.YesNo, text=text, fadeScreen=1, command=handleResponse) + + def doIssueSummonsText(self): + self.disableButtons() + self.statusLabel['text'] = TTLocalizer.SummonDlgDelivering + + def disableButtons(self): + self.summonBuildingButton['state'] = DGG.DISABLED + self.summonInvasionButton['state'] = DGG.DISABLED + self.summonCogdoButton['state'] = DGG.DISABLED + self.summonSkelInvasionButton['state'] = DGG.DISABLED + self.summonWaiterInvasionButton['state'] = DGG.DISABLED + self.summonV2InvasionButton['state'] = DGG.DISABLED + + def enableButtons(self): + if base.localAvatar.hasCogSummons(self.suitIndex, 'building'): + self.summonBuildingButton['state'] = DGG.NORMAL + if base.localAvatar.hasCogSummons(self.suitIndex, 'invasion'): + self.summonInvasionButton['state'] = DGG.NORMAL + if base.localAvatar.hasCogSummons(self.suitIndex, 'cogdo'): + self.summonCogdoButton['state'] = DGG.NORMAL + if base.localAvatar.hasCogSummons(self.suitIndex, 'skelinvasion'): + self.summonSkelInvasionButton['state'] = DGG.NORMAL + if base.localAvatar.hasCogSummons(self.suitIndex, 'waiterinvasion'): + self.summonWaiterInvasionButton['state'] = DGG.NORMAL + if base.localAvatar.hasCogSummons(self.suitIndex, 'v2invasion'): + self.summonV2InvasionButton['state'] = DGG.NORMAL + + def __cancel(self): + self.exit() diff --git a/toontown/shtiker/TrackPage.py b/toontown/shtiker/TrackPage.py new file mode 100755 index 00000000..db246a6f --- /dev/null +++ b/toontown/shtiker/TrackPage.py @@ -0,0 +1,172 @@ +from panda3d.core import * +import ShtikerPage +from direct.gui.DirectGui import * +from toontown.quest import Quests +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import TTLocalizer +from toontown.toon import Toon +MAX_FRAMES = 18 +Track2Anim = {ToontownBattleGlobals.HEAL_TRACK: 'juggle', + ToontownBattleGlobals.TRAP_TRACK: 'toss', + ToontownBattleGlobals.LURE_TRACK: 'hypnotize', + ToontownBattleGlobals.SOUND_TRACK: 'sound', + ToontownBattleGlobals.THROW_TRACK: 'throw', + ToontownBattleGlobals.SQUIRT_TRACK: 'firehose', + ToontownBattleGlobals.DROP_TRACK: 'pushbutton'} + +class TrackFrame(DirectFrame): + + def __init__(self, index): + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(TrackFrame) + filmstrip = loader.loadModel('phase_3.5/models/gui/filmstrip') + self.index = index + self.frame = DirectFrame(parent=self, relief=None, image=filmstrip, image_scale=1, text=str(self.index - 1), text_pos=(0.26, -0.22), text_fg=(1, 1, 1, 1), text_scale=0.1) + self.question = DirectLabel(parent=self.frame, relief=None, pos=(0, 0, -0.15), text='?', text_scale=0.4, text_pos=(0, 0.04), text_fg=(0.72, 0.72, 0.72, 1)) + self.toon = None + filmstrip.removeNode() + return + + def makeToon(self): + if not self.toon: + self.toon = Toon.Toon() + self.toon.setDNA(base.localAvatar.getStyle()) + self.toon.getGeomNode().setDepthWrite(1) + self.toon.getGeomNode().setDepthTest(1) + self.toon.useLOD(500) + self.toon.reparentTo(self.frame) + self.toon.setPosHprScale(0, 10, -0.25, 210, 0, 0, 0.12, 0.12, 0.12) + self.ignore('nametagAmbientLightChanged') + + def play(self, trackId): + anim = Track2Anim[trackId] + if self.toon: + numFrames = self.toon.getNumFrames(anim) - 1 + fromFrame = 0 + toFrame = (self.toon.getNumFrames(anim) - 1) / MAX_FRAMES * self.index + self.toon.play(anim, None, fromFrame, toFrame - 1) + return + + def setTrained(self, trackId): + if self.toon == None: + self.makeToon() + anim = Track2Anim[trackId] + frame = (self.toon.getNumFrames(anim) - 1) / MAX_FRAMES * self.index + self.toon.pose(anim, frame) + self.toon.show() + self.question.hide() + trackColorR, trackColorG, trackColorB = ToontownBattleGlobals.TrackColors[trackId] + self.frame['image_color'] = Vec4(trackColorR, trackColorG, trackColorB, 1) + self.frame['text_fg'] = Vec4(trackColorR * 0.3, trackColorG * 0.3, trackColorB * 0.3, 1) + return + + def setUntrained(self, trackId): + if self.toon: + self.toon.delete() + self.toon = None + self.question.show() + if trackId == -1: + self.frame['image_color'] = Vec4(0.7, 0.7, 0.7, 1) + self.frame['text_fg'] = Vec4(0.5, 0.5, 0.5, 1) + self.question['text_fg'] = Vec4(0.6, 0.6, 0.6, 1) + else: + trackColorR, trackColorG, trackColorB = ToontownBattleGlobals.TrackColors[trackId] + self.frame['image_color'] = Vec4(trackColorR * 0.7, trackColorG * 0.7, trackColorB * 0.7, 1) + self.frame['text_fg'] = Vec4(trackColorR * 0.3, trackColorG * 0.3, trackColorB * 0.3, 1) + self.question['text_fg'] = Vec4(trackColorR * 0.6, trackColorG * 0.6, trackColorB * 0.6, 1) + return + + +class TrackPage(ShtikerPage.ShtikerPage): + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.trackFrames = [] + + def placeFrames(self): + rowY = 0.38 + rowSpace = -0.32 + rowPos = [] + for i in xrange(3): + rowPos.append(rowY) + rowY += rowSpace + + colX = -0.7 + colSpace = 0.276 + colPos = [] + for i in xrange(6): + colPos.append(colX) + colX += colSpace + + for index in xrange(1, MAX_FRAMES + 1): + frame = self.trackFrames[index - 1] + col = (index - 1) % 6 + row = (index - 1) / 6 + frame.setPos(colPos[col], 0, rowPos[row]) + frame.setScale(0.39) + + def load(self): + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.TrackPageTitle, text_scale=0.1, pos=(0, 0, 0.65)) + self.subtitle = DirectLabel(parent=self, relief=None, text=TTLocalizer.TrackPageSubtitle, text_scale=0.05, text_fg=(0.5, 0.1, 0.1, 1), pos=(0, 0, 0.56)) + self.trackText = DirectLabel(parent=self, relief=None, text='', text_scale=0.05, text_fg=(0.5, 0.1, 0.1, 1), pos=(0, 0, -0.5)) + for index in xrange(1, MAX_FRAMES + 1): + frame = TrackFrame(index) + frame.reparentTo(self) + self.trackFrames.append(frame) + + self.placeFrames() + self.startFrame = self.trackFrames[0] + self.endFrame = self.trackFrames[-1] + self.startFrame.frame['text'] = '' + self.startFrame.frame['text_scale'] = TTLocalizer.TPstartFrame + self.startFrame.frame['image_color'] = Vec4(0.2, 0.2, 0.2, 1) + self.startFrame.frame['text_fg'] = (1, 1, 1, 1) + self.startFrame.frame['text_pos'] = (0, 0.08) + self.startFrame.question.hide() + self.endFrame.frame['text'] = TTLocalizer.TrackPageDone + self.endFrame.frame['text_scale'] = TTLocalizer.TPendFrame + self.endFrame.frame['image_color'] = Vec4(0.2, 0.2, 0.2, 1) + self.endFrame.frame['text_fg'] = (1, 1, 1, 1) + self.endFrame.frame['text_pos'] = (0, 0) + self.endFrame.question.hide() + return + + def unload(self): + del self.title + del self.subtitle + del self.trackText + del self.trackFrames + ShtikerPage.ShtikerPage.unload(self) + + def clearPage(self): + for index in xrange(1, MAX_FRAMES - 1): + self.trackFrames[index].setUntrained(-1) + + self.startFrame.frame['text'] = '' + self.trackText['text'] = TTLocalizer.TrackPageClear + + def updatePage(self): + trackId, trackProgress = base.localAvatar.getTrackProgress() + if trackId == -1: + self.clearPage() + else: + trackName = ToontownBattleGlobals.Tracks[trackId].capitalize() + self.trackText['text'] = TTLocalizer.TrackPageTraining % (trackName, trackName) + trackProgressArray = base.localAvatar.getTrackProgressAsArray() + for index in xrange(1, MAX_FRAMES - 2): + if trackProgressArray[index - 1]: + self.trackFrames[index].setTrained(trackId) + else: + self.trackFrames[index].setUntrained(trackId) + + self.trackFrames[MAX_FRAMES - 2].setUntrained(trackId) + self.startFrame.frame['text'] = TTLocalizer.TrackPageFilmTitle % trackName + + def enter(self): + self.updatePage() + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + self.clearPage() + ShtikerPage.ShtikerPage.exit(self) diff --git a/toontown/shtiker/__init__.py b/toontown/shtiker/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/speedchat/SCSpecialMenu.py b/toontown/speedchat/SCSpecialMenu.py new file mode 100644 index 00000000..c5346c89 --- /dev/null +++ b/toontown/speedchat/SCSpecialMenu.py @@ -0,0 +1,35 @@ +from otp.otpbase import OTPLocalizer +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat import SCMenuHolder, SCStaticTextTerminal + +class SCSpecialMenu(SCMenu): + + def __init__(self, sections): + SCMenu.__init__(self) + self.sections = sections + self.__messagesChanged() + + def appendPhrases(self, section, menu): + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link speedchat menu phrase %s which does not seem to exist' % phrase + break + + menu.append(SCStaticTextTerminal.SCStaticTextTerminal(phrase)) + + def __messagesChanged(self): + self.clearMenu() + + try: + lt = base.localAvatar + except: + return + + for section in self.sections: + if section[0] == -1: + self.appendPhrases(section, self) + else: + menu = SCMenu() + + self.appendPhrases(section, menu) + self.append(SCMenuHolder.SCMenuHolder(str(section[0]), menu)) diff --git a/toontown/speedchat/TTSCBoardingMenu.py b/toontown/speedchat/TTSCBoardingMenu.py new file mode 100755 index 00000000..de84ae70 --- /dev/null +++ b/toontown/speedchat/TTSCBoardingMenu.py @@ -0,0 +1,72 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +BoardingMenuGuide = [(OTPLocalizer.BoardingMenuSections[0], []), + (OTPLocalizer.BoardingMenuSections[1], []), + (OTPLocalizer.BoardingMenuSections[2], []), + (OTPLocalizer.BoardingMenuSections[3], [5005, + 5006, + 5007, + 5008, + 5009])] +GroupPhrases = [5000, + 5001, + 5002, + 5003, + 5004] +ZoneIdsToMsgs = {10000: [GroupPhrases, [5100, 5101, 5102], [5200, 5201, 5202]], + 10100: [GroupPhrases, [5103], [5203]], + 11100: [GroupPhrases, [5104], [5204]], + 11200: [GroupPhrases, [5105, 5106], [5205, 5206]], + 12000: [GroupPhrases, [5107, 5108, 5109], [5207, 5208, 5209]], + 12100: [GroupPhrases, [5110], [5210]], + 13100: [GroupPhrases, [5111], [5211]], + 13200: [GroupPhrases, [5112, + 5113, + 5114, + 5115], [5212, + 5213, + 5214, + 5215]]} + +class TTSCBoardingMenu(SCMenu): + + def __init__(self, zoneId): + SCMenu.__init__(self) + self.__boardingMessagesChanged(zoneId) + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __boardingMessagesChanged(self, zoneId): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for count in xrange(len(BoardingMenuGuide)): + section = BoardingMenuGuide[count] + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link boarding phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + phrases = ZoneIdsToMsgs[zoneId][count] + for phrase in phrases: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link boarding phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCCogMenu.py b/toontown/speedchat/TTSCCogMenu.py new file mode 100755 index 00000000..12ce1948 --- /dev/null +++ b/toontown/speedchat/TTSCCogMenu.py @@ -0,0 +1,13 @@ +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal + +class TTSCCogMenu(SCMenu): + + def __init__(self, indices): + SCMenu.__init__(self) + for index in indices: + term = SCStaticTextTerminal(index) + self.append(term) + + def destroy(self): + SCMenu.destroy(self) diff --git a/toontown/speedchat/TTSCDecoders.py b/toontown/speedchat/TTSCDecoders.py new file mode 100755 index 00000000..2bd89c33 --- /dev/null +++ b/toontown/speedchat/TTSCDecoders.py @@ -0,0 +1,2 @@ +from TTSCToontaskTerminal import decodeTTSCToontaskMsg +from TTSCResistanceTerminal import decodeTTSCResistanceMsg diff --git a/toontown/speedchat/TTSCFactoryMenu.py b/toontown/speedchat/TTSCFactoryMenu.py new file mode 100755 index 00000000..6f1f7496 --- /dev/null +++ b/toontown/speedchat/TTSCFactoryMenu.py @@ -0,0 +1,91 @@ +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +ZoneToMsgs = {3: [1803, 1903], + 4: [1804, 1904], + 5: [1805, 1905], + 6: [1806, 1906], + 7: [1807, 1907], + 8: [1808, 1908], + 9: [1809, 1909], + 10: [1810, 1910], + 11: [1811, 1911], + 12: [1812, 1912], + 13: [1813, 1913], + 14: [1814, 1914], + 15: [1815, 1915], + 16: [1816, 1916], + 17: [1817, 1917], + 18: [1818, 1918], + 19: [1819, 1919], + 20: [1820, 1920], + 21: [1821, 1921], + 22: [1822, 1922], + 23: [1823, 1923], + 24: [1824, 1924], + 25: [1825, 1925], + 27: [1827, 1927], + 30: [1830, 1930], + 31: [1831, 1931], + 32: [1832, 1932], + 33: [1833, 1933], + 34: [1834, 1934], + 35: [1835, 1935], + 36: [1836, 1936], + 37: [1837, 1937], + 38: [1838, 1938], + 40: [1840, 1940], + 41: [1841, 1941], + 60: [1860, 1960], + 61: [1861, 1961]} +GLOBAL_MSGS = [1700, + 1701, + 1702, + 1703, + 1704] + +class TTSCFactoryMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.meetMenuHolder = None + zoneId = base.cr.playGame.getPlaceId() + if zoneId and zoneId == 11000: + meetMenu = SCMenu() + for msgIndex in OTPLocalizer.SCFactoryMeetMenuIndexes: + term = SCStaticTextTerminal(msgIndex) + meetMenu.append(term) + + self.meetMenuHolder = SCMenuHolder.SCMenuHolder(OTPLocalizer.SCMenuFactoryMeet, meetMenu) + self[0:0] = [self.meetMenuHolder] + self.accept('factoryZoneChanged', self.__zoneChanged) + self.__zoneChanged() + return + + def destroy(self): + self.ignore('factoryZoneChanged') + SCMenu.destroy(self) + + def __zoneChanged(self, zoneId = 0): + if self.meetMenuHolder: + del self[0] + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + phrases = [] + + def addTerminal(terminal, self = self, phrases = phrases): + displayText = terminal.getDisplayText() + if displayText not in phrases: + self.append(terminal) + phrases.append(displayText) + + for msg in GLOBAL_MSGS + ZoneToMsgs.get(zoneId, []): + addTerminal(SCStaticTextTerminal(msg)) + + if self.meetMenuHolder: + self[0:0] = [self.meetMenuHolder] diff --git a/toontown/speedchat/TTSCPetTrickMenu.py b/toontown/speedchat/TTSCPetTrickMenu.py new file mode 100755 index 00000000..5830e911 --- /dev/null +++ b/toontown/speedchat/TTSCPetTrickMenu.py @@ -0,0 +1,32 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +from toontown.pets import PetTricks + +class TTSCPetTrickMenu(SCMenu): + notify = DirectNotifyGlobal.directNotify.newCategory('TTSCPetTrickMenu') + + def __init__(self): + SCMenu.__init__(self) + self.accept('petTrickPhrasesChanged', self.__phrasesChanged) + self.__phrasesChanged() + + def destroy(self): + self.ignore('petTrickPhrasesChanged') + SCMenu.destroy(self) + + def __phrasesChanged(self, zoneId = 0): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for trickId in lt.petTrickPhrases: + if trickId not in PetTricks.TrickId2scIds: + TTSCPetTrickMenu.notify.warning('unknown trick ID: %s' % trickId) + else: + for msg in PetTricks.TrickId2scIds[trickId]: + self.append(SCStaticTextTerminal(msg)) diff --git a/toontown/speedchat/TTSCResistanceMenu.py b/toontown/speedchat/TTSCResistanceMenu.py new file mode 100755 index 00000000..ac039830 --- /dev/null +++ b/toontown/speedchat/TTSCResistanceMenu.py @@ -0,0 +1,39 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from toontown.chat import ResistanceChat +from TTSCResistanceTerminal import TTSCResistanceTerminal + +class TTSCResistanceMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.accept('resistanceMessagesChanged', self.__resistanceMessagesChanged) + self.__resistanceMessagesChanged() + submenus = [] + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __resistanceMessagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + phrases = lt.resistanceMessages + for menuIndex in ResistanceChat.resistanceMenu: + menu = SCMenu() + for itemIndex in ResistanceChat.getItems(menuIndex): + textId = ResistanceChat.encodeId(menuIndex, itemIndex) + charges = lt.getResistanceMessageCharges(textId) + if charges > 0: + menu.append(TTSCResistanceTerminal(textId, charges)) + + textId = ResistanceChat.encodeId(menuIndex, 0) + menuName = ResistanceChat.getMenuName(textId) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCResistanceTerminal.py b/toontown/speedchat/TTSCResistanceTerminal.py new file mode 100755 index 00000000..1655c4b4 --- /dev/null +++ b/toontown/speedchat/TTSCResistanceTerminal.py @@ -0,0 +1,22 @@ +from otp.speedchat.SCTerminal import SCTerminal +from toontown.chat import ResistanceChat + +TTSCResistanceMsgEvent = 'TTSCResistanceMsg' + +def decodeTTSCResistanceMsg(textId): + return ResistanceChat.getChatText(textId) + +class TTSCResistanceTerminal(SCTerminal): + + def __init__(self, textId, charges): + SCTerminal.__init__(self) + self.setCharges(charges) + self.textId = textId + self.text = ResistanceChat.getItemText(self.textId) + + def isWhisperable(self): + return False + + def handleSelect(self): + SCTerminal.handleSelect(self) + messenger.send(self.getEventName(TTSCResistanceMsgEvent), [self.textId]) diff --git a/toontown/speedchat/TTSCToontaskMenu.py b/toontown/speedchat/TTSCToontaskMenu.py new file mode 100755 index 00000000..2e6ec68c --- /dev/null +++ b/toontown/speedchat/TTSCToontaskMenu.py @@ -0,0 +1,47 @@ +from otp.speedchat.SCMenu import SCMenu +from TTSCToontaskTerminal import TTSCToontaskTerminal +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from toontown.quest import Quests + +class TTSCToontaskMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.accept('questsChanged', self.__tasksChanged) + self.__tasksChanged() + + def destroy(self): + SCMenu.destroy(self) + + def __tasksChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + phrases = [] + + def addTerminal(terminal, self = self, phrases = phrases): + displayText = terminal.getDisplayText() + if displayText not in phrases: + self.append(terminal) + phrases.append(displayText) + + for task in lt.quests: + taskId, fromNpcId, toNpcId, rewardId, toonProgress = task + q = Quests.getQuest(taskId) + if q is None: + continue + msgs = q.getSCStrings(toNpcId, toonProgress) + if type(msgs) != type([]): + msgs = [msgs] + for i in xrange(len(msgs)): + addTerminal(TTSCToontaskTerminal(msgs[i], taskId, toNpcId, toonProgress, i)) + + needToontask = 1 + if hasattr(lt, 'questCarryLimit'): + needToontask = len(lt.quests) != lt.questCarryLimit + if needToontask: + addTerminal(SCStaticTextTerminal(1299)) + return diff --git a/toontown/speedchat/TTSCToontaskTerminal.py b/toontown/speedchat/TTSCToontaskTerminal.py new file mode 100755 index 00000000..755e0c3b --- /dev/null +++ b/toontown/speedchat/TTSCToontaskTerminal.py @@ -0,0 +1,39 @@ +from otp.speedchat.SCTerminal import * +from toontown.quest import Quests +from toontown.toon import NPCToons +TTSCToontaskMsgEvent = 'SCToontaskMsg' + +def decodeTTSCToontaskMsg(taskId, toNpcId, toonProgress, msgIndex): + q = Quests.getQuest(taskId) + if q is None: + return + name = NPCToons.getNPCName(toNpcId) + if name is None: + return + msgs = q.getSCStrings(toNpcId, toonProgress) + if type(msgs) != type([]): + msgs = [msgs] + if msgIndex >= len(msgs): + return + return msgs[msgIndex] + + +class TTSCToontaskTerminal(SCTerminal): + + def __init__(self, msg, taskId, toNpcId, toonProgress, msgIndex): + SCTerminal.__init__(self) + self.msg = msg + self.taskId = taskId + self.toNpcId = toNpcId + self.toonProgress = toonProgress + self.msgIndex = msgIndex + + def getDisplayText(self): + return self.msg + + def handleSelect(self): + SCTerminal.handleSelect(self) + messenger.send(self.getEventName(TTSCToontaskMsgEvent), [self.taskId, + self.toNpcId, + self.toonProgress, + self.msgIndex]) diff --git a/toontown/speedchat/TTSpeedChatGlobals.py b/toontown/speedchat/TTSpeedChatGlobals.py new file mode 100755 index 00000000..21a588df --- /dev/null +++ b/toontown/speedchat/TTSpeedChatGlobals.py @@ -0,0 +1,2 @@ +from TTSCToontaskTerminal import TTSCToontaskMsgEvent +from TTSCResistanceTerminal import TTSCResistanceMsgEvent diff --git a/toontown/speedchat/TTSpeedChatTypes.py b/toontown/speedchat/TTSpeedChatTypes.py new file mode 100755 index 00000000..b8a235b6 --- /dev/null +++ b/toontown/speedchat/TTSpeedChatTypes.py @@ -0,0 +1,92 @@ +from TTSCToontaskMenu import TTSCToontaskMenu +from TTSCFactoryMenu import TTSCFactoryMenu +from TTSCCogMenu import TTSCCogMenu +from TTSCToontaskTerminal import TTSCToontaskTerminal +from TTSCResistanceMenu import TTSCResistanceMenu +from TTSCResistanceTerminal import TTSCResistanceTerminal +from TTSCBoardingMenu import TTSCBoardingMenu + +if hasattr(base, 'wantPets') and base.wantPets: + from TTSCPetTrickMenu import TTSCPetTrickMenu + +from otp.otpbase import OTPLocalizer +from SCSpecialMenu import SCSpecialMenu + +AprilToonsMenu = [ + (OTPLocalizer.AprilToonsMenuSections[1], [30100]), + (OTPLocalizer.AprilToonsMenuSections[2], [30130, 30131, 30132, 30133]), + (OTPLocalizer.AprilToonsMenuSections[0], [30140, 30141]) +] + +GolfMenu = [ + (OTPLocalizer.GolfMenuSections[1], [4100, 4101, 4102, 4103, 4104, 4105]), + (OTPLocalizer.GolfMenuSections[2], [4200, 4201, 4202, 4203, 4204, 4205, 4206, 4207]), + (OTPLocalizer.GolfMenuSections[3], [4300, 4301, 4302, 4303, 4304, 4305, 4306, 4307]), + (OTPLocalizer.GolfMenuSections[0], [4000, 4001, 4002]) +] + +JellybeanJamMenu = [ + (OTPLocalizer.JellybeanJamMenuSections[0], [30180, 30181, 30182, 30183, 30184, 30185]), + (OTPLocalizer.JellybeanJamMenuSections[1], [30186, 30187, 30188, 30189, 30190]) +] + +KartRacingMenu = [ + (OTPLocalizer.KartRacingMenuSections[1], [3130, 3160, 3190, 3170, 3180, 3150, 3110]), + (OTPLocalizer.KartRacingMenuSections[2], [3200, 3201, 3210, 3211, 3220, 3221, 3222, 3223, 3224, 3225, 3230, 3231, 3232, 3233, 3234, 3235]), + (OTPLocalizer.KartRacingMenuSections[3], [3600, 3601, 3602, 3603, 3640, 3641, 3642, 3643, 3660, 3661, 3662, 3663]), + (OTPLocalizer.KartRacingMenuSections[4], [3300, 3301, 3310, 3320, 3330, 3340, 3350, 3360]), + (OTPLocalizer.KartRacingMenuSections[5], [3410, 3400, 3430, 3450, 3451, 3452, 3453, 3460, 3461, 3462, 3470]), + (OTPLocalizer.KartRacingMenuSections[0], [3010, 3020, 3030, 3040, 3050, 3060, 3061]) +] + +SellbotFieldOfficeMenu = [ + (OTPLocalizer.SellbotFieldOfficeMenuSections[1], range(30409, 30419)), + (OTPLocalizer.SellbotFieldOfficeMenuSections[0], range(30404, 30409)) +] + +SellbotNerfMenu = [ + (OTPLocalizer.SellbotNerfMenuSections[1], [30157, 30158, 30159, 30160, 30161, 30162, 30163, 30164]), + (OTPLocalizer.SellbotNerfMenuSections[2], [30165, 30166, 30167, 30168, 30169, 30170, 30171, 30172, 30173, 30174, 30175]), + (OTPLocalizer.SellbotNerfMenuSections[0], [30150, 30151, 30152, 30153, 30154, 30155, 30156]), +] + +SillyPhaseFiveMenu = [ + (OTPLocalizer.SillyHolidayMenuSections[1], [30325, 30326, 30327]), + (OTPLocalizer.SillyHolidayMenuSections[2], [30328, 30329, 30330, 30331, 30332]) +] + +SillyPhaseFourMenu = [ + (OTPLocalizer.SillyHolidayMenuSections[1], [30325, 30326, 30327]), + (OTPLocalizer.SillyHolidayMenuSections[2], [30329, 30330, 30331, 30332]) +] + +SillyPhaseThreeMenu = [ + (OTPLocalizer.SillyHolidayMenuSections[1], [30323, 30324, 30325, 30326, 30327]), + (OTPLocalizer.SillyHolidayMenuSections[2], [30318, 30319, 30320, 30321, 30322]) +] + +SillyPhaseTwoMenu = [ + (OTPLocalizer.SillyHolidayMenuSections[1], [30310, 30311, 30312, 30313, 30314, 30315]), + (OTPLocalizer.SillyHolidayMenuSections[2], [30316, 30317]), + (OTPLocalizer.SillyHolidayMenuSections[0], [30309]) +] + +SillyPhaseOneMenu = [ + (OTPLocalizer.SillyHolidayMenuSections[1], [30303, 30304, 30305, 30306]), + (OTPLocalizer.SillyHolidayMenuSections[2], [30307, 30308]), + (OTPLocalizer.SillyHolidayMenuSections[0], [30301, 30302]) +] + +VictoryPartiesMenu = [ + (OTPLocalizer.VictoryPartiesMenuSections[1], [30350, 30351, 30352, 30353, 30354]), + (OTPLocalizer.VictoryPartiesMenuSections[2], [30355, 30356, 30357, 30358, 30359, 30360, 30361]) +] + +WinterMenu = [ + (OTPLocalizer.WinterMenuSections[0], range(30200, 30206)), + (OTPLocalizer.WinterMenuSections[1], [30275, 30276, 30277]) +] + +HalloweenMenu = [(OTPLocalizer.HalloweenMenuSections[0], [30250, 30251, 30252, 10003])] +IdesOfMarchMenu = [(OTPLocalizer.IdesOfMarchMenuSections[0], [30450, 30451, 30452])] +SellbotInvasionMenu = [(OTPLocalizer.SellbotInvasionMenuSections[0], range(30400, 30404))] diff --git a/toontown/speedchat/__init__.py b/toontown/speedchat/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/suit/BossCog.py b/toontown/suit/BossCog.py new file mode 100755 index 00000000..e247384a --- /dev/null +++ b/toontown/suit/BossCog.py @@ -0,0 +1,478 @@ +from panda3d.core import * +from direct.actor import Actor +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import FSM +from direct.interval.IntervalGlobal import * +from direct.task.Task import Task +from otp.avatar import Avatar +from otp.nametag.NametagGroup import NametagGroup +from otp.nametag.NametagConstants import * +from toontown.battle import BattleParticles, BattleProps +from toontown.toonbase import TTLocalizer, ToontownGlobals +import Suit, SuitDNA, SuitHealthBar +import types, random + +GenericModel = 'phase_9/models/char/bossCog' +ModelDict = {'s': 'phase_9/models/char/sellbotBoss', + 'm': 'phase_10/models/char/cashbotBoss', + 'l': 'phase_11/models/char/lawbotBoss', + 'c': 'phase_12/models/char/bossbotBoss'} +AnimList = ('Ff_speech', 'ltTurn2Wave', 'wave', 'Ff_lookRt', 'turn2Fb', 'Ff_neutral', 'Bb_neutral', 'Ff2Bb_spin', 'Bb2Ff_spin', 'Fb_neutral', 'Bf_neutral', 'Fb_firstHit', 'Fb_downNeutral', 'Fb_downHit', 'Fb_fall', 'Fb_down2Up', 'Fb_downLtSwing', 'Fb_downRtSwing', 'Fb_DownThrow', 'Fb_UpThrow', 'Fb_jump', 'golf_swing') + +class BossCog(Avatar.Avatar): + notify = DirectNotifyGlobal.directNotify.newCategory('BossCog') + + def __init__(self): + Avatar.Avatar.__init__(self) + self.setFont(ToontownGlobals.getSuitFont()) + self.nametag.setSpeechFont(ToontownGlobals.getSuitFont()) + self.setPlayerType(NametagGroup.CCSuit) + self.setPickable(0) + self.doorA = None + self.doorB = None + self.bubbleL = None + self.bubbleR = None + self.raised = 1 + self.forward = 1 + self.happy = 1 + self.dizzy = 0 + self.nowRaised = 1 + self.nowForward = 1 + self.nowHappy = 1 + self.currentAnimIval = None + self.queuedAnimIvals = [] + self.treadsLeftPos = 0 + self.treadsRightPos = 0 + self.healthBar = SuitHealthBar.SuitHealthBar() + self.animDoneEvent = 'BossCogAnimDone' + self.animIvalName = 'BossCogAnimIval' + self.warningSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_GOON_tractor_beam_alarmed.ogg') + + def delete(self): + Avatar.Avatar.delete(self) + self.healthBar.delete() + self.setDizzy(0) + self.stopAnimate() + if self.doorA: + self.doorA.request('Off') + self.doorB.request('Off') + self.doorA = None + self.doorB = None + + def setDNAString(self, dnaString): + self.dna = SuitDNA.SuitDNA() + self.dna.makeFromNetString(dnaString) + self.setDNA(self.dna) + + def setDNA(self, dna): + if self.style: + pass + else: + self.style = dna + self.generateBossCog() + self.initializeDropShadow() + if base.wantNametags: + self.initializeNametag3d() + + def generateBossCog(self): + self.throwSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_frisbee_gears.ogg') + self.swingSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_swipe.ogg') + self.spinSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_spin.ogg') + self.rainGearsSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_raining_gears.ogg') + self.swishSfx = loader.loadSfx('phase_5/audio/sfx/General_throw_miss.ogg') + self.boomSfx = loader.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart_%s.ogg' % random.randint(1, 6)) + self.deathSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_big_death.ogg') + self.upSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_raise_up.ogg') + self.downSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_collapse.ogg') + self.reelSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_reeling_backwards.ogg') + self.birdsSfx = loader.loadSfx('phase_4/audio/sfx/SZ_TC_bird1.ogg') + self.dizzyAlert = loader.loadSfx('phase_5/audio/sfx/AA_sound_aoogah.ogg') + self.grunt = loader.loadSfx('phase_9/audio/sfx/Boss_COG_VO_grunt.ogg') + self.murmur = loader.loadSfx('phase_9/audio/sfx/Boss_COG_VO_murmur.ogg') + self.statement = loader.loadSfx('phase_9/audio/sfx/Boss_COG_VO_statement.ogg') + self.question = loader.loadSfx('phase_9/audio/sfx/Boss_COG_VO_question.ogg') + self.dialogArray = [self.grunt, + self.murmur, + self.statement, + self.question, + self.statement, + self.statement] + dna = self.style + filePrefix = ModelDict[dna.dept] + self.loadModel(GenericModel + '-legs-zero', 'legs') + self.loadModel(filePrefix + '-torso-zero', 'torso') + self.loadModel(filePrefix + '-head-zero', 'head') + self.twoFaced = dna.dept == 's' + self.attach('head', 'torso', 'joint34') + self.attach('torso', 'legs', 'joint_pelvis') + self.rotateNode = self.attachNewNode('rotate') + geomNode = self.getGeomNode() + geomNode.reparentTo(self.rotateNode) + self.frontAttack = self.rotateNode.attachNewNode('frontAttack') + self.frontAttack.setPos(0, -10, 10) + self.frontAttack.setScale(2) + self.setHeight(26) + self.nametag3d.setScale(2) + for partName in ('legs', 'torso', 'head'): + animDict = {} + for anim in AnimList: + animDict[anim] = '%s-%s-%s' % (GenericModel, partName, anim) + + self.loadAnims(animDict, partName) + + self.stars = BattleProps.globalPropPool.getProp('stun') + self.stars.setPosHprScale(7, 0, 0, 0, 0, -90, 3, 3, 3) + self.stars.loop('stun') + self.pelvis = self.getPart('torso') + self.pelvisForwardHpr = VBase3(0, 0, 0) + self.pelvisReversedHpr = VBase3(-180, 0, 0) + self.neck = self.getPart('head') + self.neckForwardHpr = VBase3(0, 0, 0) + self.neckReversedHpr = VBase3(0, -540, 0) + self.axle = self.find('**/joint_axle') + self.doorA = self.__setupDoor('**/joint_doorFront', 'doorA', self.doorACallback, VBase3(0, 0, 0), VBase3(0, 0, -80), CollisionPolygon(Point3(5, -4, 0.32), Point3(0, -4, 0), Point3(0, 4, 0), Point3(5, 4, 0.32))) + self.doorB = self.__setupDoor('**/joint_doorRear', 'doorB', self.doorBCallback, VBase3(0, 0, 0), VBase3(0, 0, 80), CollisionPolygon(Point3(-5, 4, 0.84), Point3(0, 4, 0), Point3(0, -4, 0), Point3(-5, -4, 0.84))) + treadsModel = loader.loadModel('%s-treads' % GenericModel) + treadsModel.reparentTo(self.axle) + self.treadsLeft = treadsModel.find('**/right_tread') + self.treadsRight = treadsModel.find('**/left_tread') + self.doorA.request('Closed') + self.doorB.request('Closed') + + def initializeBodyCollisions(self, collIdStr): + Avatar.Avatar.initializeBodyCollisions(self, collIdStr) + if not self.ghostMode: + self.collNode.setCollideMask(self.collNode.getIntoCollideMask() | ToontownGlobals.PieBitmask) + + def generateHealthBar(self): + self.healthBar.generate() + self.healthBar.geom.reparentTo(self.find('**/joint_lifeMeter')) + self.healthBar.geom.setScale(6.0) + self.healthBar.geom.setHpr(0, -20, 0) + self.healthBar.geom.show() + + def updateHealthBar(self): + if not self.healthBar: + return + + self.healthBar.update(1.0 - float(self.bossDamage) / float(self.bossMaxDamage)) + + def reverseHead(self): + self.neck.setHpr(self.neckReversedHpr) + + def forwardHead(self): + self.neck.setHpr(self.neckForwardHpr) + + def reverseBody(self): + self.pelvis.setHpr(self.pelvisReversedHpr) + + def forwardBody(self): + self.pelvis.setHpr(self.pelvisForwardHpr) + + def getShadowJoint(self): + return self.getGeomNode() + + def getNametagJoints(self): + return [] + + def getDialogueArray(self): + return self.dialogArray + + def doorACallback(self, isOpen): + pass + + def doorBCallback(self, isOpen): + pass + + def __rollTreadsInterval(self, object, start = 0, duration = 0, rate = 1): + + def rollTexMatrix(t, object = object): + object.setTexOffset(TextureStage.getDefault(), t, 0) + + return LerpFunctionInterval(rollTexMatrix, fromData=start, toData=start + rate * duration, duration=duration) + + def rollLeftTreads(self, duration, rate): + start = self.treadsLeftPos + self.treadsLeftPos += duration * rate + return self.__rollTreadsInterval(self.treadsLeft, start=start, duration=duration, rate=rate) + + def rollRightTreads(self, duration, rate): + start = self.treadsRightPos + self.treadsRightPos += duration * rate + return self.__rollTreadsInterval(self.treadsRight, start=start, duration=duration, rate=rate) + + class DoorFSM(FSM.FSM): + + def __init__(self, name, animate, callback, openedHpr, closedHpr, uniqueName): + FSM.FSM.__init__(self, name) + self.animate = animate + self.callback = callback + self.openedHpr = openedHpr + self.closedHpr = closedHpr + self.uniqueName = uniqueName + self.ival = 0 + self.openSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_door_open.ogg') + self.closeSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_door_close.ogg') + self.request('Closed') + + def filterOpening(self, request, args): + if request == 'close': + return 'Closing' + return self.defaultFilter(request, args) + + def enterOpening(self): + intervalName = self.uniqueName('open-%s' % self.animate.getName()) + self.callback(0) + ival = Parallel(SoundInterval(self.openSfx, node=self.animate, volume=0.2), self.animate.hprInterval(1, self.openedHpr, blendType='easeInOut'), Sequence(Wait(0.2), Func(self.callback, 1)), name=intervalName) + ival.start() + self.ival = ival + + def exitOpening(self): + self.ival.pause() + self.ival = None + return + + def filterOpened(self, request, args): + if request == 'close': + return 'Closing' + return self.defaultFilter(request, args) + + def enterOpened(self): + self.animate.setHpr(self.openedHpr) + self.callback(1) + + def filterClosing(self, request, args): + if request == 'open': + return 'Opening' + return self.defaultFilter(request, args) + + def enterClosing(self): + intervalName = self.uniqueName('close-%s' % self.animate.getName()) + self.callback(1) + ival = Parallel(SoundInterval(self.closeSfx, node=self.animate, volume=0.2), self.animate.hprInterval(1, self.closedHpr, blendType='easeInOut'), Sequence(Wait(0.8), Func(self.callback, 0)), name=intervalName) + ival.start() + self.ival = ival + + def exitClosing(self): + self.ival.pause() + self.ival = None + return + + def filterClosed(self, request, args): + if request == 'open': + return 'Opening' + return self.defaultFilter(request, args) + + def enterClosed(self): + self.animate.setHpr(self.closedHpr) + self.callback(0) + + def __setupDoor(self, jointName, name, callback, openedHpr, closedHpr, cPoly): + joint = self.find(jointName) + children = joint.getChildren() + animate = joint.attachNewNode(name) + children.reparentTo(animate) + cnode = CollisionNode('BossZap') + cnode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.WallBitmask | ToontownGlobals.CameraBitmask) + cnode.addSolid(cPoly) + animate.attachNewNode(cnode) + fsm = self.DoorFSM(name, animate, callback, openedHpr, closedHpr, self.uniqueName) + return fsm + + def doAnimate(self, anim = None, now = 0, queueNeutral = 1, raised = None, forward = None, happy = None): + if now: + self.stopAnimate() + if not self.twoFaced: + happy = 1 + if raised == None: + raised = self.raised + if forward == None: + forward = self.forward + if happy == None: + happy = self.happy + if now: + self.raised = raised + self.forward = forward + self.happy = happy + if self.currentAnimIval == None: + self.accept(self.animDoneEvent, self.__getNextAnim) + else: + queueNeutral = 0 + ival, changed = self.__getAnimIval(anim, raised, forward, happy) + if changed or queueNeutral: + self.queuedAnimIvals.append((ival, + self.raised, + self.forward, + self.happy)) + if self.currentAnimIval == None: + self.__getNextAnim() + return + + def stopAnimate(self): + self.ignore(self.animDoneEvent) + self.queuedAnimIvals = [] + if self.currentAnimIval: + self.currentAnimIval.setDoneEvent('') + self.currentAnimIval.finish() + self.currentAnimIval = None + self.raised = self.nowRaised + self.forward = self.nowForward + self.happy = self.nowHappy + return + + def __getNextAnim(self): + if self.queuedAnimIvals: + ival, raised, forward, happy = self.queuedAnimIvals[0] + del self.queuedAnimIvals[0] + else: + ival, changed = self.__getAnimIval(None, self.raised, self.forward, self.happy) + raised = self.raised + forward = self.forward + happy = self.happy + if self.currentAnimIval: + self.currentAnimIval.setDoneEvent('') + self.currentAnimIval.finish() + self.currentAnimIval = ival + self.currentAnimIval.start() + self.nowRaised = raised + self.nowForward = forward + self.nowHappy = happy + return + + def __getAnimIval(self, anim, raised, forward, happy): + ival, changed = self.__doGetAnimIval(anim, raised, forward, happy) + seq = Sequence(ival, name=self.animIvalName) + seq.setDoneEvent(self.animDoneEvent) + return (seq, changed) + + def __doGetAnimIval(self, anim, raised, forward, happy): + if raised == self.raised and forward == self.forward and happy == self.happy: + return (self.getAnim(anim), anim != None) + startsHappy = self.happy + endsHappy = self.happy + ival = Sequence() + if raised and not self.raised: + upIval = self.getAngryActorInterval('Fb_down2Up') + if self.forward: + ival = upIval + else: + ival = Sequence(Func(self.reverseBody), upIval, Func(self.forwardBody)) + ival = Parallel(SoundInterval(self.upSfx, node=self), ival) + if forward != self.forward: + if forward: + animName = 'Bb2Ff_spin' + else: + animName = 'Ff2Bb_spin' + ival = Sequence(ival, ActorInterval(self, animName)) + startsHappy = 1 + endsHappy = 1 + startNeckHpr = self.neckForwardHpr + endNeckHpr = self.neckForwardHpr + if self.happy != startsHappy: + startNeckHpr = self.neckReversedHpr + if happy != endsHappy: + endNeckHpr = self.neckReversedHpr + if startNeckHpr != endNeckHpr: + ival = Sequence(Func(self.neck.setHpr, startNeckHpr), ParallelEndTogether(ival, Sequence(self.neck.hprInterval(0.5, endNeckHpr, startHpr=startNeckHpr, blendType='easeInOut'), Func(self.neck.setHpr, self.neckForwardHpr)))) + elif endNeckHpr != self.neckForwardHpr: + ival = Sequence(Func(self.neck.setHpr, startNeckHpr), ival, Func(self.neck.setHpr, self.neckForwardHpr)) + if not raised and self.raised: + downIval = self.getAngryActorInterval('Fb_down2Up', playRate=-1) + if forward: + ival = Sequence(ival, downIval) + else: + ival = Sequence(ival, Func(self.reverseBody), downIval, Func(self.forwardBody)) + ival = Parallel(SoundInterval(self.downSfx, node=self), ival) + self.raised = raised + self.forward = forward + self.happy = happy + if anim != None: + ival = Sequence(ival, self.getAnim(anim)) + return (ival, 1) + + def setDizzy(self, dizzy): + if dizzy and not self.dizzy: + base.playSfx(self.dizzyAlert) + self.dizzy = dizzy + if dizzy: + self.stars.reparentTo(self.neck) + base.playSfx(self.birdsSfx, looping=1) + else: + self.stars.detachNode() + self.birdsSfx.stop() + + def getAngryActorInterval(self, animName, **kw): + if self.happy: + ival = Sequence(Func(self.reverseHead), ActorInterval(self, animName, **kw), Func(self.forwardHead)) + else: + ival = ActorInterval(self, animName, **kw) + return ival + + def getAnim(self, anim): + ival = None + if anim == None: + partName = None + if self.happy: + animName = 'Ff_neutral' + else: + animName = 'Fb_neutral' + if self.raised: + ival = ActorInterval(self, animName) + else: + ival = Parallel(ActorInterval(self, animName, partName=['torso', 'head']), ActorInterval(self, 'Fb_downNeutral', partName='legs')) + if not self.forward: + ival = Sequence(Func(self.reverseBody), ival, Func(self.forwardBody)) + elif anim == 'down2Up': + ival = Parallel(SoundInterval(self.upSfx, node=self), self.getAngryActorInterval('Fb_down2Up')) + self.raised = 1 + elif anim == 'up2Down': + ival = Parallel(SoundInterval(self.downSfx, node=self), self.getAngryActorInterval('Fb_down2Up', playRate=-1)) + self.raised = 0 + elif anim == 'throw': + self.doAnimate(None, raised=1, happy=0, queueNeutral=0) + ival = Parallel(Sequence(SoundInterval(self.throwSfx, node=self), duration=0), self.getAngryActorInterval('Fb_UpThrow')) + elif anim == 'hit': + if self.raised: + self.raised = 0 + ival = self.getAngryActorInterval('Fb_firstHit') + else: + ival = self.getAngryActorInterval('Fb_downHit') + ival = Parallel(SoundInterval(self.reelSfx, node=self), ival) + elif anim == 'ltSwing' or anim == 'rtSwing': + self.doAnimate(None, raised=0, happy=0, queueNeutral=0) + if anim == 'ltSwing': + ival = Sequence(Track((0, self.getAngryActorInterval('Fb_downLtSwing')), (0.9, SoundInterval(self.swingSfx, node=self)), (1, Func(self.bubbleL.unstash))), Func(self.bubbleL.stash)) + else: + ival = Sequence(Track((0, self.getAngryActorInterval('Fb_downRtSwing')), (0.9, SoundInterval(self.swingSfx, node=self)), (1, Func(self.bubbleR.unstash))), Func(self.bubbleR.stash)) + elif anim == 'frontAttack': + self.doAnimate(None, raised=1, happy=0, queueNeutral=0) + pe = BattleParticles.loadParticleFile('bossCogFrontAttack.ptf') + ival = Sequence(Func(self.reverseHead), ActorInterval(self, 'Bb2Ff_spin'), Func(self.forwardHead)) + if self.forward: + ival = Sequence(Func(self.reverseBody), ParallelEndTogether(ival, self.pelvis.hprInterval(0.5, self.pelvisForwardHpr, blendType='easeInOut'))) + ival = Sequence(Track((0, ival), (0, Sequence(Func(self.setChatAbsolute, random.choice(TTLocalizer.VPSpinMessages), CFSpeech | CFTimeout), SoundInterval(self.spinSfx, node=self))), (0.9, Parallel(SoundInterval(self.rainGearsSfx, node=self), ParticleInterval(pe, self.frontAttack, worldRelative=0, duration=1.5, cleanup=True), duration=0)), (1.9, Func(self.bubbleF.unstash))), Func(self.bubbleF.stash)) + self.forward = 1 + self.happy = 0 + self.raised = 1 + elif anim == 'areaAttack': + if self.twoFaced: + self.doAnimate(None, raised=1, happy=0, queueNeutral=0) + else: + self.doAnimate(None, raised=1, happy=1, queueNeutral=1) + ival = Sequence() + if self.dna.dept == 'm': + ival.append(Func(self.loop, 'Ff_neutral')) + ival.append(Parallel(SoundInterval(self.warningSfx, node=self, volume=2.0), Wait(3.0))) + ival.append(Parallel(ActorInterval(self, 'Fb_jump'), Sequence(Func(self.setChatAbsolute, random.choice(TTLocalizer.JumpBossTaunts[self.dna.dept]), CFSpeech | CFTimeout), SoundInterval(self.swishSfx, duration=1.1, node=self), SoundInterval(self.boomSfx, duration=1.9)), Sequence(Wait(1.21), Func(self.announceAreaAttack)))) + if self.twoFaced: + self.happy = 0 + else: + self.happy = 1 + self.raised = 1 + elif anim == 'Fb_fall': + ival = Parallel(ActorInterval(self, 'Fb_fall'), Sequence(SoundInterval(self.reelSfx, node=self), SoundInterval(self.deathSfx))) + elif isinstance(anim, types.StringType): + ival = ActorInterval(self, anim) + else: + ival = anim + return ival diff --git a/toontown/suit/DistributedBossCog.py b/toontown/suit/DistributedBossCog.py new file mode 100755 index 00000000..89704c59 --- /dev/null +++ b/toontown/suit/DistributedBossCog.py @@ -0,0 +1,1158 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from direct.gui import OnscreenText +from otp.avatar import DistributedAvatar +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.battle import BattleExperience +from toontown.battle import BattleBase +import BossCog +import SuitDNA +from toontown.coghq import CogDisguiseGlobals +from direct.showbase import Transitions +from toontown.hood import ZoneUtil +from toontown.building import ElevatorUtils +from toontown.building import ElevatorConstants +from toontown.distributed import DelayDelete +from toontown.effects import DustCloud +from toontown.toonbase import TTLocalizer +from toontown.friends import FriendsListManager +from direct.controls.ControlManager import CollisionHandlerRayStart +from direct.showbase import PythonUtil +import random +from otp.nametag import NametagGlobals +from otp.nametag.NametagConstants import * + +class DistributedBossCog(DistributedAvatar.DistributedAvatar, BossCog.BossCog): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBossCog') + allowClickedNameTag = True + + def __init__(self, cr): + DistributedAvatar.DistributedAvatar.__init__(self, cr) + BossCog.BossCog.__init__(self) + self.gotAllToons = 0 + self.toonsA = [] + self.toonsB = [] + self.involvedToons = [] + self.toonRequest = None + self.battleNumber = 0 + self.battleAId = None + self.battleBId = None + self.battleA = None + self.battleB = None + self.battleRequest = None + self.arenaSide = 0 + self.keyReward = False + self.toonSphere = None + self.localToonIsSafe = 0 + self.__toonsStuckToFloor = [] + self.cqueue = None + self.rays = None + self.ray1 = None + self.ray2 = None + self.ray3 = None + self.e1 = None + self.e2 = None + self.e3 = None + self.battleANode = self.attachNewNode('battleA') + self.battleBNode = self.attachNewNode('battleB') + self.battleANode.setPosHpr(*ToontownGlobals.BossCogBattleAPosHpr) + self.battleBNode.setPosHpr(*ToontownGlobals.BossCogBattleBPosHpr) + self.activeIntervals = {} + self.flashInterval = None + self.elevatorType = ElevatorConstants.ELEVATOR_VP + return + + def announceGenerate(self): + DistributedAvatar.DistributedAvatar.announceGenerate(self) + self.prevCogSuitLevel = localAvatar.getCogLevels()[CogDisguiseGlobals.dept2deptIndex(self.style.dept)] + nearBubble = CollisionSphere(0, 0, 0, 50) + nearBubble.setTangible(0) + nearBubbleNode = CollisionNode('NearBoss') + nearBubbleNode.setCollideMask(ToontownGlobals.WallBitmask) + nearBubbleNode.addSolid(nearBubble) + self.attachNewNode(nearBubbleNode) + self.accept('enterNearBoss', self.avatarNearEnter) + self.accept('exitNearBoss', self.avatarNearExit) + self.collNode.removeSolid(0) + tube1 = CollisionTube(6.5, -7.5, 2, 6.5, 7.5, 2, 2.5) + tube2 = CollisionTube(-6.5, -7.5, 2, -6.5, 7.5, 2, 2.5) + roof = CollisionPolygon(Point3(-4.4, 7.1, 5.5), Point3(-4.4, -7.1, 5.5), Point3(4.4, -7.1, 5.5), Point3(4.4, 7.1, 5.5)) + side1 = CollisionPolygon(Point3(-4.4, -7.1, 5.5), Point3(-4.4, 7.1, 5.5), Point3(-4.4, 7.1, 0), Point3(-4.4, -7.1, 0)) + side2 = CollisionPolygon(Point3(4.4, 7.1, 5.5), Point3(4.4, -7.1, 5.5), Point3(4.4, -7.1, 0), Point3(4.4, 7.1, 0)) + front1 = CollisionPolygon(Point3(4.4, -7.1, 5.5), Point3(-4.4, -7.1, 5.5), Point3(-4.4, -7.1, 5.2), Point3(4.4, -7.1, 5.2)) + back1 = CollisionPolygon(Point3(-4.4, 7.1, 5.5), Point3(4.4, 7.1, 5.5), Point3(4.4, 7.1, 5.2), Point3(-4.4, 7.1, 5.2)) + self.collNode.addSolid(tube1) + self.collNode.addSolid(tube2) + self.collNode.addSolid(roof) + self.collNode.addSolid(side1) + self.collNode.addSolid(side2) + self.collNode.addSolid(front1) + self.collNode.addSolid(back1) + self.collNodePath.reparentTo(self.axle) + self.collNode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.WallBitmask | ToontownGlobals.CameraBitmask) + self.collNode.setName('BossZap') + self.setTag('attackCode', str(ToontownGlobals.BossCogElectricFence)) + self.accept('enterBossZap', self.__touchedBoss) + bubbleL = CollisionSphere(10, -5, 0, 10) + bubbleL.setTangible(0) + bubbleLNode = CollisionNode('BossZap') + bubbleLNode.setCollideMask(ToontownGlobals.WallBitmask) + bubbleLNode.addSolid(bubbleL) + self.bubbleL = self.axle.attachNewNode(bubbleLNode) + self.bubbleL.setTag('attackCode', str(ToontownGlobals.BossCogSwatLeft)) + self.bubbleL.stash() + bubbleR = CollisionSphere(-10, -5, 0, 10) + bubbleR.setTangible(0) + bubbleRNode = CollisionNode('BossZap') + bubbleRNode.setCollideMask(ToontownGlobals.WallBitmask) + bubbleRNode.addSolid(bubbleR) + self.bubbleR = self.axle.attachNewNode(bubbleRNode) + self.bubbleR.setTag('attackCode', str(ToontownGlobals.BossCogSwatRight)) + self.bubbleR.stash() + bubbleF = CollisionSphere(0, -25, 0, 12) + bubbleF.setTangible(0) + bubbleFNode = CollisionNode('BossZap') + bubbleFNode.setCollideMask(ToontownGlobals.WallBitmask) + bubbleFNode.addSolid(bubbleF) + self.bubbleF = self.rotateNode.attachNewNode(bubbleFNode) + self.bubbleF.setTag('attackCode', str(ToontownGlobals.BossCogFrontAttack)) + self.bubbleF.stash() + + def disable(self): + DistributedAvatar.DistributedAvatar.disable(self) + self.removeHeadMeters() + self.battleAId = None + self.battleBId = None + self.battleA = None + self.battleB = None + self.cr.relatedObjectMgr.abortRequest(self.toonRequest) + self.toonRequest = None + self.cr.relatedObjectMgr.abortRequest(self.battleRequest) + self.battleRequest = None + self.stopAnimate() + self.cleanupIntervals() + self.cleanupFlash() + self.disableLocalToonSimpleCollisions() + self.ignoreAll() + return + + def delete(self): + try: + self.DistributedBossCog_deleted + except: + self.DistributedBossCog_deleted = 1 + self.ignoreAll() + DistributedAvatar.DistributedAvatar.delete(self) + BossCog.BossCog.delete(self) + + def setDNAString(self, dnaString): + BossCog.BossCog.setDNAString(self, dnaString) + + def getDNAString(self): + return self.dna.makeNetString() + + def setDNA(self, dna): + BossCog.BossCog.setDNA(self, dna) + + def setToonIds(self, involvedToons, toonsA, toonsB): + self.involvedToons = involvedToons + self.toonsA = toonsA + self.toonsB = toonsB + self.cr.relatedObjectMgr.abortRequest(self.toonRequest) + self.gotAllToons = 0 + self.toonRequest = self.cr.relatedObjectMgr.requestObjects(self.involvedToons, allCallback=self.__gotAllToons, eachCallback=self.gotToon) + + def getDialogueArray(self, *args): + return BossCog.BossCog.getDialogueArray(self, *args) + + def storeInterval(self, interval, name): + if name in self.activeIntervals: + ival = self.activeIntervals[name] + if hasattr(ival, 'delayDelete') or hasattr(ival, 'delayDeletes'): + self.clearInterval(name, finish=1) + self.activeIntervals[name] = interval + + def cleanupIntervals(self): + for interval in self.activeIntervals.values(): + interval.finish() + DelayDelete.cleanupDelayDeletes(interval) + + self.activeIntervals = {} + + def clearInterval(self, name, finish = 1): + if name in self.activeIntervals: + ival = self.activeIntervals[name] + if finish: + ival.finish() + else: + ival.pause() + if name in self.activeIntervals: + DelayDelete.cleanupDelayDeletes(ival) + del self.activeIntervals[name] + else: + self.notify.debug('interval: %s already cleared' % name) + + def finishInterval(self, name): + if name in self.activeIntervals: + interval = self.activeIntervals[name] + interval.finish() + + def d_avatarEnter(self): + self.sendUpdate('avatarEnter', []) + + def d_avatarExit(self): + self.sendUpdate('avatarExit', []) + + def avatarNearEnter(self, entry): + self.sendUpdate('avatarNearEnter', []) + + def avatarNearExit(self, entry): + self.sendUpdate('avatarNearExit', []) + + def hasLocalToon(self): + doId = localAvatar.doId + return doId in self.toonsA or doId in self.toonsB + + def setBattleExperience(self, id0, origExp0, earnedExp0, origQuests0, items0, missedItems0, origMerits0, merits0, parts0, id1, origExp1, earnedExp1, origQuests1, items1, missedItems1, origMerits1, merits1, parts1, id2, origExp2, earnedExp2, origQuests2, items2, missedItems2, origMerits2, merits2, parts2, id3, origExp3, earnedExp3, origQuests3, items3, missedItems3, origMerits3, merits3, parts3, id4, origExp4, earnedExp4, origQuests4, items4, missedItems4, origMerits4, merits4, parts4, id5, origExp5, earnedExp5, origQuests5, items5, missedItems5, origMerits5, merits5, parts5, id6, origExp6, earnedExp6, origQuests6, items6, missedItems6, origMerits6, merits6, parts6, id7, origExp7, earnedExp7, origQuests7, items7, missedItems7, origMerits7, merits7, parts7, deathList, uberList, helpfulToons): + self.deathList = deathList + self.uberList = uberList + self.helpfulToons = helpfulToons + entries = ((id0, + origExp0, + earnedExp0, + origQuests0, + items0, + missedItems0, + origMerits0, + merits0, + parts0), + (id1, + origExp1, + earnedExp1, + origQuests1, + items1, + missedItems1, + origMerits1, + merits1, + parts1), + (id2, + origExp2, + earnedExp2, + origQuests2, + items2, + missedItems2, + origMerits2, + merits2, + parts2), + (id3, + origExp3, + earnedExp3, + origQuests3, + items3, + missedItems3, + origMerits3, + merits3, + parts3), + (id4, + origExp4, + earnedExp4, + origQuests4, + items4, + missedItems4, + origMerits4, + merits4, + parts4), + (id5, + origExp5, + earnedExp5, + origQuests5, + items5, + missedItems5, + origMerits5, + merits5, + parts5), + (id6, + origExp6, + earnedExp6, + origQuests6, + items6, + missedItems6, + origMerits6, + merits6, + parts6), + (id7, + origExp7, + earnedExp7, + origQuests7, + items7, + missedItems7, + origMerits7, + merits7, + parts7)) + self.toonRewardDicts = BattleExperience.genRewardDicts(entries) + self.toonRewardIds = [id0, + id1, + id2, + id3, + id4, + id5, + id6, + id7] + + def setArenaSide(self, arenaSide): + self.arenaSide = arenaSide + + def setKeyReward(self, reward): + self.keyReward = reward + + def setState(self, state): + self.request(state) + + def gotToon(self, toon): + stateName = self.state + + def __gotAllToons(self, toons): + self.gotAllToons = 1 + messenger.send('gotAllToons') + + def setBattleIds(self, battleNumber, battleAId, battleBId): + self.battleNumber = battleNumber + self.battleAId = battleAId + self.battleBId = battleBId + self.cr.relatedObjectMgr.abortRequest(self.battleRequest) + self.battleRequest = self.cr.relatedObjectMgr.requestObjects([self.battleAId, self.battleBId], allCallback=self.__gotBattles) + + def __gotBattles(self, battles): + self.battleRequest = None + if self.battleA and self.battleA != battles[0]: + self.battleA.cleanupBattle() + if self.battleB and self.battleB != battles[1]: + self.battleB.cleanupBattle() + self.battleA = battles[0] + self.battleB = battles[1] + return + + def cleanupBattles(self): + if self.battleA: + self.battleA.cleanupBattle() + if self.battleB: + self.battleB.cleanupBattle() + + def makeEndOfBattleMovie(self, hasLocalToon): + return Sequence() + + def controlToons(self): + for panel in self.cr.openAvatarPanels: + if panel: + panel.cleanupDialog() + + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.stopLookAround() + toon.stopSmooth() + + if self.hasLocalToon(): + self.toMovieMode() + + def enableLocalToonSimpleCollisions(self): + if not self.toonSphere: + sphere = CollisionSphere(0, 0, 1, 1) + sphere.setRespectEffectiveNormal(0) + sphereNode = CollisionNode('SimpleCollisions') + sphereNode.setFromCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.FloorBitmask) + sphereNode.setIntoCollideMask(BitMask32.allOff()) + sphereNode.addSolid(sphere) + self.toonSphere = NodePath(sphereNode) + self.toonSphereHandler = CollisionHandlerPusher() + self.toonSphereHandler.addCollider(self.toonSphere, localAvatar) + self.toonSphere.reparentTo(localAvatar) + base.cTrav.addCollider(self.toonSphere, self.toonSphereHandler) + + def disableLocalToonSimpleCollisions(self): + if self.toonSphere: + base.cTrav.removeCollider(self.toonSphere) + self.toonSphere.detachNode() + + def toOuchMode(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.setState('ouch') + + def toCraneMode(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.setState('crane') + + def toMovieMode(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.setState('movie') + + def toWalkMode(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.setState('walk') + + def toFinalBattleMode(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.setState('finalBattle') + self.createHeadMeters() + + def releaseToons(self, finalBattle = 0): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + if self.battleA and toon in self.battleA.toons: + pass + elif self.battleB and toon in self.battleB.toons: + pass + else: + toon.startLookAround() + toon.startSmooth() + toon.wrtReparentTo(render) + if toon == localAvatar: + if finalBattle: + self.toFinalBattleMode() + else: + self.toWalkMode() + + def createHeadMeters(self): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + + if toon: + toon.createHeadMeter() + + def removeHeadMeters(self): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + + if toon: + toon.removeHeadMeter() + + def stickToonsToFloor(self): + self.unstickToons() + rayNode = CollisionNode('stickToonsToFloor') + rayNode.addSolid(CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)) + rayNode.setFromCollideMask(ToontownGlobals.FloorBitmask) + rayNode.setIntoCollideMask(BitMask32.allOff()) + ray = NodePath(rayNode) + lifter = CollisionHandlerFloor() + lifter.setOffset(ToontownGlobals.FloorOffset) + lifter.setReach(10.0) + for toonId in self.involvedToons: + toon = base.cr.doId2do.get(toonId) + if toon: + toonRay = ray.instanceTo(toon) + lifter.addCollider(toonRay, toon) + base.cTrav.addCollider(toonRay, lifter) + self.__toonsStuckToFloor.append(toonRay) + + def unstickToons(self): + for toonRay in self.__toonsStuckToFloor: + base.cTrav.removeCollider(toonRay) + toonRay.removeNode() + + self.__toonsStuckToFloor = [] + + def stickBossToFloor(self): + self.unstickBoss() + self.ray1 = CollisionRay(0.0, 10.0, 20.0, 0.0, 0.0, -1.0) + self.ray2 = CollisionRay(0.0, 0.0, 20.0, 0.0, 0.0, -1.0) + self.ray3 = CollisionRay(0.0, -10.0, 20.0, 0.0, 0.0, -1.0) + rayNode = CollisionNode('stickBossToFloor') + rayNode.addSolid(self.ray1) + rayNode.addSolid(self.ray2) + rayNode.addSolid(self.ray3) + rayNode.setFromCollideMask(ToontownGlobals.FloorBitmask) + rayNode.setIntoCollideMask(BitMask32.allOff()) + self.rays = self.attachNewNode(rayNode) + self.cqueue = CollisionHandlerQueue() + base.cTrav.addCollider(self.rays, self.cqueue) + + def rollBoss(self, t, fromPos, deltaPos): + self.setPos(fromPos + deltaPos * t) + if not self.cqueue: + return + self.cqueue.sortEntries() + numEntries = self.cqueue.getNumEntries() + if numEntries != 0: + for i in xrange(self.cqueue.getNumEntries() - 1, -1, -1): + entry = self.cqueue.getEntry(i) + solid = entry.getFrom() + if solid == self.ray1: + self.e1 = entry + elif solid == self.ray2: + self.e2 = entry + elif solid == self.ray3: + self.e3 = entry + else: + self.notify.warning('Unexpected ray in __liftBoss') + return + + self.cqueue.clearEntries() + if not (self.e1 and self.e2 and self.e3): + self.notify.debug('Some points missed in __liftBoss') + return + p1 = self.e1.getSurfacePoint(self) + p2 = self.e2.getSurfacePoint(self) + p3 = self.e3.getSurfacePoint(self) + p2a = (p1 + p3) / 2 + if p2a[2] > p2[2]: + center = p2a + else: + center = p2 + self.setZ(self, center[2]) + if p1[2] > p2[2] + 0.01 or p3[2] > p2[2] + 0.01: + mat = Mat4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + if abs(p3[2] - center[2]) < abs(p1[2] - center[2]): + lookAt(mat, Vec3(p1 - center), CSDefault) + else: + lookAt(mat, Vec3(center - p3), CSDefault) + self.rotateNode.setMat(mat) + else: + self.rotateNode.clearTransform() + + def unstickBoss(self): + if self.rays: + base.cTrav.removeCollider(self.rays) + self.rays.removeNode() + self.rays = None + self.ray1 = None + self.ray2 = None + self.ray3 = None + self.e1 = None + self.e2 = None + self.e3 = None + self.rotateNode.clearTransform() + self.cqueue = None + return + + def rollBossToPoint(self, fromPos, fromHpr, toPos, toHpr, reverse): + vector = Vec3(toPos - fromPos) + distance = vector.length() + if toHpr == None: + mat = Mat3(0, 0, 0, 0, 0, 0, 0, 0, 0) + headsUp(mat, vector, CSDefault) + scale = VBase3(0, 0, 0) + shear = VBase3(0, 0, 0) + toHpr = VBase3(0, 0, 0) + decomposeMatrix(mat, scale, shear, toHpr, CSDefault) + if fromHpr: + newH = PythonUtil.fitDestAngle2Src(fromHpr[0], toHpr[0]) + toHpr = VBase3(newH, 0, 0) + else: + fromHpr = toHpr + turnTime = abs(toHpr[0] - fromHpr[0]) / ToontownGlobals.BossCogTurnSpeed + if toHpr[0] < fromHpr[0]: + leftRate = ToontownGlobals.BossCogTreadSpeed + else: + leftRate = -ToontownGlobals.BossCogTreadSpeed + if reverse: + rollTreadRate = -ToontownGlobals.BossCogTreadSpeed + else: + rollTreadRate = ToontownGlobals.BossCogTreadSpeed + rollTime = distance / ToontownGlobals.BossCogRollSpeed + deltaPos = toPos - fromPos + track = Sequence(Func(self.setPos, fromPos), Func(self.headsUp, toPos), Parallel(self.hprInterval(turnTime, toHpr, fromHpr), self.rollLeftTreads(turnTime, leftRate), self.rollRightTreads(turnTime, -leftRate)), Parallel(LerpFunctionInterval(self.rollBoss, duration=rollTime, extraArgs=[fromPos, deltaPos]), self.rollLeftTreads(rollTime, rollTreadRate), self.rollRightTreads(rollTime, rollTreadRate))) + return (track, toHpr) + + def setupElevator(self, elevatorModel): + self.elevatorModel = elevatorModel + self.leftDoor = self.elevatorModel.find('**/left-door') + if self.leftDoor.isEmpty(): + self.leftDoor = self.elevatorModel.find('**/left_door') + self.rightDoor = self.elevatorModel.find('**/right-door') + if self.rightDoor.isEmpty(): + self.rightDoor = self.elevatorModel.find('**/right_door') + self.openSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.ogg') + self.finalOpenSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.ogg') + self.closeSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.ogg') + self.finalCloseSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.ogg') + self.openDoors = ElevatorUtils.getOpenInterval(self, self.leftDoor, self.rightDoor, self.openSfx, self.finalOpenSfx, self.elevatorType) + self.closeDoors = ElevatorUtils.getCloseInterval(self, self.leftDoor, self.rightDoor, self.closeSfx, self.finalCloseSfx, self.elevatorType) + self.closeDoors.start() + self.closeDoors.finish() + + def putToonInCogSuit(self, toon): + if not toon.isDisguised: + deptIndex = SuitDNA.suitDepts.index(self.style.dept) + toon.setCogIndex(deptIndex) + toon.getGeomNode().hide() + + def placeToonInElevator(self, toon): + self.putToonInCogSuit(toon) + toonIndex = self.involvedToons.index(toon.doId) + toon.reparentTo(self.elevatorModel) + toon.setPos(*ElevatorConstants.BigElevatorPoints[toonIndex]) + toon.setHpr(180, 0, 0) + toon.suit.loop('neutral') + + def toonNormalEyes(self, toons, bArrayOfObjs = False): + if bArrayOfObjs: + toonObjs = toons + else: + toonObjs = [] + for toonId in toons: + toon = base.cr.doId2do.get(toonId) + if toon: + toonObjs.append(toon) + + seq = Sequence() + for toon in toonObjs: + seq.append(Func(toon.normalEyes)) + seq.append(Func(toon.blinkEyes)) + + return seq + + def toonDied(self, avId): + if avId == localAvatar.doId: + self.localToonDied() + + def localToonToSafeZone(self): + target_sz = ZoneUtil.getSafeZoneId(localAvatar.defaultZone) + place = self.cr.playGame.getPlace() + place.fsm.request('teleportOut', [{'loader': ZoneUtil.getLoaderName(target_sz), + 'where': ZoneUtil.getWhereName(target_sz, 1), + 'how': 'teleportIn', + 'hoodId': target_sz, + 'zoneId': target_sz, + 'shardId': None, + 'avId': -1, + 'battle': 1}]) + return + + def localToonDied(self): + target_sz = ZoneUtil.getSafeZoneId(localAvatar.defaultZone) + place = self.cr.playGame.getPlace() + place.fsm.request('died', [{'loader': ZoneUtil.getLoaderName(target_sz), + 'where': ZoneUtil.getWhereName(target_sz, 1), + 'how': 'teleportIn', + 'hoodId': target_sz, + 'zoneId': target_sz, + 'shardId': None, + 'avId': -1, + 'battle': 1}]) + return + + def toonsToBattlePosition(self, toonIds, battleNode): + points = BattleBase.BattleBase.toonPoints[len(toonIds) - 1] + self.notify.debug('toonsToBattlePosition: points = %s' % points[0][0]) + for i in xrange(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + toon.reparentTo(render) + pos, h = points[i] + self.notify.debug('toonsToBattlePosition: battleNode=%s %.2f %.2f %.2f %.2f %.2f %.2f' % (battleNode, + pos[0], + pos[1], + pos[2], + h, + 0, + 0)) + self.notify.debug('old toon pos %s' % toon.getPos()) + self.notify.debug('pos=%.2f %.2f %.2f h=%.2f' % (pos[0], + pos[1], + pos[2], + h)) + self.notify.debug('battleNode.pos = %s' % battleNode.getPos()) + self.notify.debug('battleNode.hpr = %s' % battleNode.getHpr()) + toon.setPosHpr(battleNode, pos[0], pos[1], pos[2], h, 0, 0) + self.notify.debug('new toon pos %s ' % toon.getPos()) + + def __touchedBoss(self, entry): + self.notify.debug('%s' % entry) + self.notify.debug('fromPos = %s' % entry.getFromNodePath().getPos(render)) + self.notify.debug('intoPos = %s' % entry.getIntoNodePath().getPos(render)) + attackCodeStr = entry.getIntoNodePath().getNetTag('attackCode') + if attackCodeStr == '': + self.notify.warning('Node %s has no attackCode tag.' % repr(entry.getIntoNodePath())) + return + attackCode = int(attackCodeStr) + if attackCode == ToontownGlobals.BossCogLawyerAttack and self.dna.dept != 'l': + self.notify.warning('got lawyer attack but not in CJ boss battle') + return + self.zapLocalToon(attackCode) + + def zapLocalToon(self, attackCode, origin = None): + if self.localToonIsSafe or localAvatar.ghostMode or localAvatar.isStunned: + return + messenger.send('interrupt-pie') + place = self.cr.playGame.getPlace() + currentState = None + if place: + currentState = place.fsm.getCurrentState().getName() + if currentState != 'walk' and currentState != 'finalBattle' and currentState != 'crane': + return + toon = localAvatar + fling = 1 + shake = 0 + if attackCode == ToontownGlobals.BossCogAreaAttack: + fling = 0 + shake = 1 + if fling: + if origin == None: + origin = self + camera.wrtReparentTo(render) + toon.headsUp(origin) + camera.wrtReparentTo(toon) + bossRelativePos = toon.getPos(self.getGeomNode()) + bp2d = Vec2(bossRelativePos[0], bossRelativePos[1]) + bp2d.normalize() + pos = toon.getPos() + hpr = toon.getHpr() + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('zapToon', [pos[0], + pos[1], + pos[2], + hpr[0] % 360.0, + hpr[1], + hpr[2], + bp2d[0], + bp2d[1], + attackCode, + timestamp]) + self.doZapToon(toon, fling=fling, shake=shake) + return + + def showZapToon(self, toonId, x, y, z, h, p, r, attackCode, timestamp): + if toonId == localAvatar.doId: + return + ts = globalClockDelta.localElapsedTime(timestamp) + pos = Point3(x, y, z) + hpr = VBase3(h, p, r) + fling = 1 + toon = self.cr.doId2do.get(toonId) + if toon: + if attackCode == ToontownGlobals.BossCogAreaAttack: + pos = None + hpr = None + fling = 0 + else: + ts -= toon.smoother.getDelay() + self.doZapToon(toon, pos=pos, hpr=hpr, ts=ts, fling=fling) + return + + def doZapToon(self, toon, pos = None, hpr = None, ts = 0, fling = 1, shake = 1): + zapName = toon.uniqueName('zap') + self.clearInterval(zapName) + zapTrack = Sequence(name=zapName) + if toon == localAvatar: + self.toOuchMode() + messenger.send('interrupt-pie') + self.enableLocalToonSimpleCollisions() + else: + zapTrack.append(Func(toon.stopSmooth)) + + def getSlideToPos(toon = toon): + return render.getRelativePoint(toon, Point3(0, -5, 0)) + + if pos != None and hpr != None: + (zapTrack.append(Func(toon.setPosHpr, pos, hpr)),) + toonTrack = Parallel() + if shake and toon == localAvatar: + toonTrack.append(Sequence(Func(camera.setZ, camera, 1), Wait(0.15), Func(camera.setZ, camera, -2), Wait(0.15), Func(camera.setZ, camera, 1))) + if fling: + toonTrack += [ActorInterval(toon, 'slip-backward'), toon.posInterval(0.5, getSlideToPos, fluid=1)] + else: + toonTrack += [ActorInterval(toon, 'slip-forward')] + zapTrack.append(toonTrack) + if toon == localAvatar: + zapTrack.append(Func(self.disableLocalToonSimpleCollisions)) + currentState = self.state + if currentState == 'BattleThree': + zapTrack.append(Func(self.toFinalBattleMode)) + elif hasattr(self, 'chairs'): + zapTrack.append(Func(self.toFinalBattleMode)) + else: + zapTrack.append(Func(self.toWalkMode)) + else: + zapTrack.append(Func(toon.startSmooth)) + if ts > 0: + startTime = ts + else: + zapTrack = Sequence(Wait(-ts), zapTrack) + startTime = 0 + zapTrack.append(Func(self.clearInterval, zapName)) + zapTrack.delayDelete = DelayDelete.DelayDelete(toon, 'BossCog.doZapToon') + zapTrack.start(startTime) + self.storeInterval(zapTrack, zapName) + return + + def setAttackCode(self, attackCode, avId = 0): + self.attackCode = attackCode + self.attackAvId = avId + if attackCode == ToontownGlobals.BossCogDizzy: + self.setDizzy(1) + self.cleanupAttacks() + self.doAnimate(None, raised=0, happy=1) + elif attackCode == ToontownGlobals.BossCogDizzyNow: + self.setDizzy(1) + self.cleanupAttacks() + self.doAnimate('hit', happy=1, now=1) + elif attackCode == ToontownGlobals.BossCogSwatLeft: + self.setDizzy(0) + self.doAnimate('ltSwing', now=1) + elif attackCode == ToontownGlobals.BossCogSwatRight: + self.setDizzy(0) + self.doAnimate('rtSwing', now=1) + elif attackCode == ToontownGlobals.BossCogAreaAttack: + self.setDizzy(0) + self.doAnimate('areaAttack', now=1) + elif attackCode == ToontownGlobals.BossCogFrontAttack: + self.setDizzy(0) + self.doAnimate('frontAttack', now=1) + elif attackCode == ToontownGlobals.BossCogRecoverDizzyAttack: + self.setDizzy(0) + self.doAnimate('frontAttack', now=1) + elif attackCode == ToontownGlobals.BossCogDirectedAttack or attackCode == ToontownGlobals.BossCogSlowDirectedAttack: + self.setDizzy(0) + self.doDirectedAttack(avId, attackCode) + elif attackCode == ToontownGlobals.BossCogNoAttack: + self.setDizzy(0) + self.doAnimate(None, raised=1) + return + + def cleanupAttacks(self): + pass + + def cleanupFlash(self): + if self.flashInterval: + self.flashInterval.finish() + self.flashInterval = None + return + + def flashRed(self): + self.cleanupFlash() + self.setColorScale(1, 1, 1, 1) + i = Sequence(self.colorScaleInterval(0.1, colorScale=VBase4(1, 0, 0, 1)), self.colorScaleInterval(0.3, colorScale=VBase4(1, 1, 1, 1))) + self.flashInterval = i + i.start() + + def flashGreen(self): + self.cleanupFlash() + if not self.isEmpty(): + self.setColorScale(1, 1, 1, 1) + i = Sequence(self.colorScaleInterval(0.1, colorScale=VBase4(0, 1, 0, 1)), self.colorScaleInterval(0.3, colorScale=VBase4(1, 1, 1, 1))) + self.flashInterval = i + i.start() + + def getGearFrisbee(self): + return loader.loadModel('phase_9/models/char/gearProp') + + def backupToonsToBattlePosition(self, toonIds, battleNode): + self.notify.debug('backupToonsToBattlePosition:') + ival = Parallel() + points = BattleBase.BattleBase.toonPoints[len(toonIds) - 1] + for i in xrange(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + pos, h = points[i] + pos = render.getRelativePoint(battleNode, pos) + ival.append(Sequence(Func(toon.setPlayRate, -0.8, 'walk'), Func(toon.loop, 'walk'), toon.posInterval(3, pos), Func(toon.setPlayRate, 1, 'walk'), Func(toon.loop, 'neutral'))) + + return ival + + def loseCogSuits(self, toons, battleNode, camLoc, arrayOfObjs = False): + seq = Sequence() + if not toons: + return seq + self.notify.debug('battleNode=%s camLoc=%s' % (battleNode, camLoc)) + seq.append(Func(camera.setPosHpr, battleNode, *camLoc)) + suitsOff = Parallel() + if arrayOfObjs: + toonArray = toons + else: + toonArray = [] + for toonId in toons: + toon = base.cr.doId2do.get(toonId) + if toon: + toonArray.append(toon) + + for toon in toonArray: + dustCloud = DustCloud.DustCloud() + dustCloud.setPos(0, 2, 3) + dustCloud.setScale(0.5) + dustCloud.setDepthWrite(0) + dustCloud.setBin('fixed', 0) + dustCloud.createTrack() + suitsOff.append(Sequence(Func(dustCloud.reparentTo, toon), Parallel(dustCloud.track, Sequence(Wait(0.3), Func(toon.takeOffSuit), Func(toon.sadEyes), Func(toon.blinkEyes), Func(toon.play, 'slip-backward'), Wait(0.7))), Func(dustCloud.detachNode), Func(dustCloud.destroy))) + + seq.append(suitsOff) + return seq + + def doDirectedAttack(self, avId, attackCode): + toon = base.cr.doId2do.get(avId) + if toon: + gearRoot = self.rotateNode.attachNewNode('gearRoot') + gearRoot.setZ(10) + gearRoot.setTag('attackCode', str(attackCode)) + gearModel = self.getGearFrisbee() + gearModel.setScale(0.2) + gearRoot.headsUp(toon) + toToonH = PythonUtil.fitDestAngle2Src(0, gearRoot.getH() + 180) + gearRoot.lookAt(toon) + neutral = 'Fb_neutral' + if not self.twoFaced: + neutral = 'Ff_neutral' + gearTrack = Parallel() + for i in xrange(4): + node = gearRoot.attachNewNode(str(i)) + node.hide() + node.setPos(0, 5.85, 4.0) + gear = gearModel.instanceTo(node) + x = random.uniform(-5, 5) + z = random.uniform(-3, 3) + h = random.uniform(-720, 720) + gearTrack.append(Sequence(Wait(i * 0.15), Func(node.show), Parallel(node.posInterval(1, Point3(x, 50, z), fluid=1), node.hprInterval(1, VBase3(h, 0, 0), fluid=1)), Func(node.detachNode))) + + if not self.raised: + neutral1Anim = self.getAnim('down2Up') + self.raised = 1 + else: + neutral1Anim = ActorInterval(self, neutral, startFrame=48) + throwAnim = self.getAnim('throw') + neutral2Anim = ActorInterval(self, neutral) + extraAnim = Sequence() + if attackCode == ToontownGlobals.BossCogSlowDirectedAttack: + extraAnim = ActorInterval(self, neutral) + seq = Sequence(ParallelEndTogether(self.pelvis.hprInterval(1, VBase3(toToonH, 0, 0)), neutral1Anim), extraAnim, Parallel(Sequence(Wait(0.19), gearTrack, Func(gearRoot.detachNode), self.pelvis.hprInterval(0.2, VBase3(0, 0, 0))), Sequence(Func(self.setChatAbsolute, random.choice(TTLocalizer.DirectedAttackBossTaunts[self.dna.dept]) % {'toon': toon.getName()}, CFSpeech | CFTimeout), throwAnim, neutral2Anim))) + self.doAnimate(seq, now=1, raised=1) + + def announceAreaAttack(self): + if not getattr(localAvatar.controlManager.currentControls, 'isAirborne', 0): + self.zapLocalToon(ToontownGlobals.BossCogAreaAttack) + + def loadEnvironment(self): + self.elevatorMusic = base.loadMusic('phase_7/audio/bgm/tt_elevator.ogg') + self.stingMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.ogg') + self.battleOneMusic = base.loadMusic('phase_3.5/audio/bgm/encntr_general_bg.ogg') + self.battleThreeMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.ogg') + self.epilogueMusic = base.loadMusic('phase_9/audio/bgm/encntr_hall_of_fame.ogg') + + def unloadEnvironment(self): + pass + + def enterOff(self): + self.cleanupIntervals() + self.hide() + self.clearChat() + self.toWalkMode() + + def exitOff(self): + self.show() + + def enterWaitForToons(self): + self.cleanupIntervals() + self.hide() + if self.gotAllToons: + self.__doneWaitForToons() + else: + self.accept('gotAllToons', self.__doneWaitForToons) + self.transitions = Transitions.Transitions(loader) + self.transitions.IrisModelName = 'phase_3/models/misc/iris' + self.transitions.FadeModelName = 'phase_3/models/misc/fade' + self.transitions.fadeScreen(alpha=1) + NametagGlobals.setMasterArrowsOn(0) + + def __doneWaitForToons(self): + self.doneBarrier('WaitForToons') + + def exitWaitForToons(self): + self.show() + self.transitions.noFade() + del self.transitions + NametagGlobals.setMasterArrowsOn(1) + + def enterElevator(self): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.stopLookAround() + toon.stopSmooth() + self.placeToonInElevator(toon) + + self.toMovieMode() + camera.reparentTo(self.elevatorModel) + camera.setPosHpr(0, 30, 8, 180, 0, 0) + base.playMusic(self.elevatorMusic, looping=1, volume=1.0) + ival = Sequence(ElevatorUtils.getRideElevatorInterval(self.elevatorType), ElevatorUtils.getRideElevatorInterval(self.elevatorType), self.openDoors, Func(camera.wrtReparentTo, render), Func(self.__doneElevator)) + intervalName = 'ElevatorMovie' + ival.start() + self.storeInterval(ival, intervalName) + + def __doneElevator(self): + self.doneBarrier('Elevator') + + def exitElevator(self): + intervalName = 'ElevatorMovie' + self.clearInterval(intervalName) + self.elevatorMusic.stop() + ElevatorUtils.closeDoors(self.leftDoor, self.rightDoor, self.elevatorType) + + def enterIntroduction(self): + self.controlToons() + ElevatorUtils.openDoors(self.leftDoor, self.rightDoor, self.elevatorType) + NametagGlobals.setMasterArrowsOn(0) + intervalName = 'IntroductionMovie' + delayDeletes = [] + seq = Parallel(self.showTitleText(), Sequence(self.makeIntroductionMovie(delayDeletes), Func(self.__beginBattleOne)), name=intervalName) + seq.delayDeletes = delayDeletes + seq.start() + self.storeInterval(seq, intervalName) + + def __beginBattleOne(self): + intervalName = 'IntroductionMovie' + self.clearInterval(intervalName) + self.doneBarrier('Introduction') + + def exitIntroduction(self): + self.notify.debug('DistributedBossCog.exitIntroduction:') + intervalName = 'IntroductionMovie' + self.clearInterval(intervalName) + self.unstickToons() + self.releaseToons() + NametagGlobals.setMasterArrowsOn(1) + ElevatorUtils.closeDoors(self.leftDoor, self.rightDoor, self.elevatorType) + + def enterBattleOne(self): + self.cleanupIntervals() + mult = ToontownBattleGlobals.getBossBattleCreditMultiplier(1) + localAvatar.inventory.setBattleCreditMultiplier(mult) + self.toonsToBattlePosition(self.toonsA, self.battleANode) + self.toonsToBattlePosition(self.toonsB, self.battleBNode) + self.releaseToons() + base.playMusic(self.battleOneMusic, looping=1, volume=0.9) + + def exitBattleOne(self): + self.cleanupBattles() + self.battleOneMusic.stop() + localAvatar.inventory.setBattleCreditMultiplier(1) + + def enterBattleThree(self): + self.cleanupIntervals() + self.releaseToons(finalBattle=1) + self.accept('clickedNametag', self.__clickedNameTag) + self.accept('friendAvatar', self.__handleFriendAvatar) + self.accept('avatarDetails', self.__handleAvatarDetails) + NametagGlobals.setMasterArrowsOn(0) + NametagGlobals.setMasterNametagsActive(1) + + def exitBattleThree(self): + self.ignore('clickedNameTag') + self.ignore('friendAvatar') + self.ignore('avatarDetails') + self.cleanupIntervals() + + def __clickedNameTag(self, avatar): + self.notify.debug('__clickedNameTag') + if not (self.state == 'BattleThree' or self.state == 'BattleFour'): + return + if not self.allowClickedNameTag: + return + FriendsListManager.FriendsListManager._FriendsListManager__handleClickedNametag(FriendsListManager.FriendsListManager, avatar) + + def __handleFriendAvatar(self, avId, avName, avDisableName): + self.notify.debug('__handleFriendAvatar') + if not (self.state == 'BattleThree' or self.state == 'BattleFour'): + return + if not self.allowClickedNameTag: + return + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + FriendsListManager.FriendsListManager._FriendsListManager__handleFriendAvatar(FriendsListManager.FriendsListManager, place, avId, avName, avDisableName) + + def __handleAvatarDetails(self, avId, avName): + self.notify.debug('__handleAvatarDetails') + if not (self.state == 'BattleThree' or self.state == 'BattleFour'): + return + if not self.allowClickedNameTag: + return + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + FriendsListManager.FriendsListManager._FriendsListManager__handleAvatarDetails(FriendsListManager.FriendsListManager, place, avId, avName) + + def enterBattleFour(self): + self.cleanupIntervals() + self.releaseToons(finalBattle=1) + self.accept('clickedNametag', self.__clickedNameTag) + self.accept('friendAvatar', self.__handleFriendAvatar) + self.accept('avatarDetails', self.__handleAvatarDetails) + NametagGlobals.setMasterArrowsOn(0) + NametagGlobals.setMasterNametagsActive(1) + + def exitBattleFour(self): + self.ignore('clickedNameTag') + self.ignore('friendAvatar') + self.ignore('avatarDetails') + self.cleanupIntervals() + + def enterFrolic(self): + self.cleanupIntervals() + self.clearChat() + self.reparentTo(render) + self.stopAnimate() + self.pose('Ff_neutral', 0) + self.releaseToons() + + def exitFrolic(self): + pass + + def setToonsToNeutral(self, toonIds): + for i in xrange(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + if toon.isDisguised: + toon.suit.loop('neutral') + toon.loop('neutral') + + def wearCogSuits(self, toons, battleNode, camLoc, arrayOfObjs = False, waiter = False): + seq = Sequence() + if not toons: + return seq + self.notify.debug('battleNode=%s camLoc=%s' % (battleNode, camLoc)) + if camLoc: + seq.append(Func(camera.setPosHpr, battleNode, *camLoc)) + suitsOff = Parallel() + if arrayOfObjs: + toonArray = toons + else: + toonArray = [] + for toonId in toons: + toon = base.cr.doId2do.get(toonId) + if toon: + toonArray.append(toon) + + for toon in toonArray: + dustCloud = DustCloud.DustCloud() + dustCloud.setPos(0, 2, 3) + dustCloud.setScale(0.5) + dustCloud.setDepthWrite(0) + dustCloud.setBin('fixed', 0) + dustCloud.createTrack() + makeWaiter = Sequence() + if waiter: + makeWaiter = Func(toon.makeWaiter) + suitsOff.append(Sequence(Func(dustCloud.reparentTo, toon), Parallel(dustCloud.track, Sequence(Wait(0.3), Func(self.putToonInCogSuit, toon), makeWaiter, Wait(0.7))), Func(dustCloud.detachNode))) + + seq.append(suitsOff) + return seq + + def showTitleText(self): + if self.style.dept not in TTLocalizer.BossLocations: + return Sequence() + + titleText = OnscreenText.OnscreenText(TTLocalizer.BossLocations[self.style.dept], fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1), font=ToontownGlobals.getSuitFont(), pos=(0, -0.5), scale=0.16, wordwrap=16) + return Sequence(Wait(5.0), titleText.colorScaleInterval(0.5, (1, 1, 1, 0)), Func(titleText.removeNode)) diff --git a/toontown/suit/DistributedBossCogAI.py b/toontown/suit/DistributedBossCogAI.py new file mode 100755 index 00000000..c790e670 --- /dev/null +++ b/toontown/suit/DistributedBossCogAI.py @@ -0,0 +1,638 @@ +import random +from direct.directnotify import DirectNotifyGlobal +from otp.avatar import DistributedAvatarAI +from toontown.battle import BattleExperienceAI +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.toon import InventoryBase +from toontown.battle import DistributedBattleFinalAI +from toontown.building import SuitPlannerInteriorAI +from toontown.battle import BattleBase +from panda3d.core import * +import SuitDNA +import random +AllBossCogs = [] + +BOSS_TO_STAT = { + 's': ToontownGlobals.STAT_VP, + 'm': ToontownGlobals.STAT_CFO, + 'l': ToontownGlobals.STAT_CJ, + 'c': ToontownGlobals.STAT_CEO +} + +class DistributedBossCogAI(DistributedAvatarAI.DistributedAvatarAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBossCogAI') + + def __init__(self, air, dept): + DistributedAvatarAI.DistributedAvatarAI.__init__(self, air) + self.dept = dept + self.dna = SuitDNA.SuitDNA() + self.dna.newBossCog(self.dept) + self.deptIndex = SuitDNA.suitDepts.index(self.dept) + self.resetBattleCounters() + self.looseToons = [] + self.involvedToons = [] + self.toonsA = [] + self.toonsB = [] + self.nearToons = [] + self.suitsA = [] + self.activeSuitsA = [] + self.suitsB = [] + self.activeSuitsB = [] + self.reserveSuits = [] + self.barrier = None + self.keyStates = ['BattleOne', + 'BattleTwo', + 'BattleThree', + 'Victory'] + self.bossDamage = 0 + self.battleThreeStart = 0 + self.battleThreeDuration = 1800 + self.attackCode = None + self.attackAvId = 0 + self.hitCount = 0 + self.keyReward = config.GetBool('get-key-reward-always', False) or random.random() <= 0.15 + AllBossCogs.append(self) + + def delete(self): + self.ignoreAll() + if self in AllBossCogs: + i = AllBossCogs.index(self) + del AllBossCogs[i] + return DistributedAvatarAI.DistributedAvatarAI.delete(self) + + def getDNAString(self): + return self.dna.makeNetString() + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + self.addToon(avId) + + def avatarExit(self): + avId = self.air.getAvatarIdFromSender() + self.removeToon(avId) + + def avatarNearEnter(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.nearToons: + self.nearToons.append(avId) + + def avatarNearExit(self): + avId = self.air.getAvatarIdFromSender() + try: + self.nearToons.remove(avId) + except: + pass + + def __handleUnexpectedExit(self, avId): + self.removeToon(avId) + + def addToon(self, avId): + if avId not in self.looseToons and avId not in self.involvedToons: + self.looseToons.append(avId) + event = self.air.getAvatarExitEvent(avId) + self.acceptOnce(event, self.__handleUnexpectedExit, extraArgs=[avId]) + + def removeToon(self, avId): + if avId in self.looseToons: + self.looseToons.remove(avId) + + if avId in self.involvedToons: + self.involvedToons.remove(avId) + + if avId in self.toonsA: + self.toonsA.remove(avId) + + if avId in self.toonsB: + self.toonsB.remove(avId) + + if avId in self.nearToons: + self.nearToons.remove(avId) + + event = self.air.getAvatarExitEvent(avId) + self.ignore(event) + if not self.hasToons(): + taskMgr.doMethodLater(10, self.__bossDone, self.uniqueName('BossDone')) + + def __bossDone(self, task): + self.b_setState('Off') + messenger.send(self.uniqueName('BossDone')) + self.ignoreAll() + + def hasToons(self): + return self.looseToons or self.involvedToons + + def hasToonsAlive(self): + alive = 0 + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + hp = toon.getHp() + if hp > 0: + alive = 1 + + return alive + + def sendBattleIds(self): + self.sendUpdate('setBattleIds', [self.battleNumber, self.battleAId, self.battleBId]) + + def sendToonIds(self): + self.sendUpdate('setToonIds', [self.involvedToons, self.toonsA, self.toonsB]) + + def damageToon(self, toon, deduction): + toon.takeDamage(deduction) + if toon.getHp() <= 0: + self.sendUpdate('toonDied', [toon.doId]) + empty = InventoryBase.InventoryBase(toon) + toon.b_setInventory(empty.makeNetString()) + self.removeToon(toon.doId) + + def healToon(self, toon, increment): + toon.toonUp(increment) + + def d_setBattleExperience(self): + self.sendUpdate('setBattleExperience', self.getBattleExperience()) + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + self.air.topToonsMgr.toonKilledBoss(toon, self.BossName) + + def getBattleExperience(self): + result = BattleExperienceAI.getBattleExperience(8, self.involvedToons, self.toonExp, self.toonSkillPtsGained, self.toonOrigQuests, self.toonItems, self.toonOrigMerits, self.toonMerits, self.toonParts, self.suitsKilled, self.helpfulToons) + return result + + def b_setArenaSide(self, arenaSide): + self.setArenaSide(arenaSide) + self.d_setArenaSide(arenaSide) + + def setArenaSide(self, arenaSide): + self.arenaSide = arenaSide + + def d_setArenaSide(self, arenaSide): + self.sendUpdate('setArenaSide', [arenaSide]) + + def b_setState(self, state): + self.setState(state) + self.d_setState(state) + + def d_setState(self, state): + self.sendUpdate('setState', [state]) + + def setState(self, state): + self.demand(state) + if self.air: + if state in self.keyStates: + self.air.writeServerEvent('bossBattle', self.doId, '%s|%s|%s|%s' % (self.dept, + state, + self.involvedToons, + self.formatReward())) + + def getState(self): + return self.state + + def getKeyReward(self): + return self.keyReward + + def formatReward(self): + return 'unspecified' + + def enterOff(self): + self.resetBattles() + self.resetToons() + self.resetBattleCounters() + + def exitOff(self): + pass + + def enterWaitForToons(self): + self.acceptNewToons() + self.barrier = self.beginBarrier('WaitForToons', self.involvedToons, 5, self.__doneWaitForToons) + + def __doneWaitForToons(self, toons): + self.b_setState('Elevator') + + def exitWaitForToons(self): + self.ignoreBarrier(self.barrier) + + def enterElevator(self): + if self.notify.getDebug(): + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + self.notify.debug('%s. involved toon %s, %s/%s' % (self.doId, + toonId, + toon.getHp(), + toon.getMaxHp())) + + self.resetBattles() + self.barrier = self.beginBarrier('Elevator', self.involvedToons, 30, self.__doneElevator) + + def __doneElevator(self, avIds): + self.b_setState('Introduction') + + def exitElevator(self): + self.ignoreBarrier(self.barrier) + + def enterIntroduction(self): + self.resetBattles() + self.arenaSide = None + self.makeBattleOneBattles() + self.barrier = self.beginBarrier('Introduction', self.involvedToons, 50, self.doneIntroduction) + + def doneIntroduction(self, avIds): + self.b_setState('BattleOne') + + def exitIntroduction(self): + self.ignoreBarrier(self.barrier) + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.b_setCogIndex(-1) + + def enterBattleOne(self): + if self.battleA: + self.battleA.startBattle(self.toonsA, self.suitsA) + if self.battleB: + self.battleB.startBattle(self.toonsB, self.suitsB) + + def exitBattleOne(self): + self.resetBattles() + + def enterReward(self): + self.resetBattles() + self.barrier = self.beginBarrier('Reward', self.involvedToons, BattleBase.BUILDING_REWARD_TIMEOUT, self.__doneReward) + + def __doneReward(self, avIds): + self.b_setState('Epilogue') + + def exitReward(self): + pass + + def enterEpilogue(self): + self.giveKeyReward() + + def exitEpilogue(self): + pass + + def enterFrolic(self): + self.resetBattles() + + def exitFrolic(self): + pass + + def resetBattleCounters(self): + self.battleNumber = 0 + self.battleA = None + self.battleAId = 0 + self.battleB = None + self.battleBId = 0 + self.arenaSide = None + self.toonSkillPtsGained = {} + self.toonExp = {} + self.toonOrigQuests = {} + self.toonItems = {} + self.toonOrigMerits = {} + self.toonMerits = {} + self.toonParts = {} + self.suitsKilled = [] + self.helpfulToons = [] + return + + def resetBattles(self): + sendReset = 0 + if self.battleA: + self.battleA.requestDelete() + self.battleA = None + self.battleAId = 0 + sendReset = 1 + if self.battleB: + self.battleB.requestDelete() + self.battleB = None + self.battleBId = 0 + sendReset = 1 + for suit in self.suitsA + self.suitsB: + suit.requestDelete() + + for suit, joinChance in self.reserveSuits: + suit.requestDelete() + + self.suitsA = [] + self.activeSuitsA = [] + self.suitsB = [] + self.activeSuitsB = [] + self.reserveSuits = [] + self.battleNumber = 0 + if sendReset: + self.sendBattleIds() + return + + def resetToons(self): + if self.toonsA or self.toonsB: + self.looseToons = self.looseToons + self.involvedToons + self.involvedToons = [] + self.toonsA = [] + self.toonsB = [] + self.sendToonIds() + + def divideToons(self): + toons = self.involvedToons[:] + random.shuffle(toons) + numToons = min(len(toons), 8) + if numToons < 4: + numToonsB = numToons / 2 + else: + numToonsB = (numToons + random.choice([0, 1])) / 2 + self.toonsA = toons[numToonsB:numToons] + self.toonsB = toons[:numToonsB] + self.looseToons += toons[numToons:] + self.sendToonIds() + + def acceptNewToons(self): + sourceToons = self.looseToons + self.looseToons = [] + for toonId in sourceToons: + toon = self.air.doId2do.get(toonId) + if toon and not toon.ghostMode: + self.involvedToons.append(toonId) + else: + self.looseToons.append(toonId) + + for avId in self.involvedToons: + toon = self.air.doId2do.get(avId) + if toon: + p = [] + for t in ToontownBattleGlobals.Tracks: + p.append(toon.experience.getExp(t)) + + self.toonExp[avId] = p + self.toonOrigMerits[avId] = toon.cogMerits[:] + + self.divideToons() + + def initializeBattles(self, battleNumber, bossCogPosHpr): + self.resetBattles() + if not self.involvedToons: + self.notify.warning('initializeBattles: no toons!') + return + self.battleNumber = battleNumber + suitHandles = self.generateSuits(battleNumber) + self.suitsA = suitHandles['activeSuits'] + self.activeSuitsA = self.suitsA[:] + self.reserveSuits = suitHandles['reserveSuits'] + suitHandles = self.generateSuits(battleNumber) + self.suitsB = suitHandles['activeSuits'] + self.activeSuitsB = self.suitsB[:] + self.reserveSuits += suitHandles['reserveSuits'] + if self.toonsA: + self.battleA = self.makeBattle(bossCogPosHpr, ToontownGlobals.BossCogBattleAPosHpr, self.handleRoundADone, self.handleBattleADone, battleNumber, 0) + self.battleAId = self.battleA.doId + else: + self.moveSuits(self.activeSuitsA) + self.suitsA = [] + self.activeSuitsA = [] + if self.arenaSide == None: + self.b_setArenaSide(0) + if self.toonsB: + self.battleB = self.makeBattle(bossCogPosHpr, ToontownGlobals.BossCogBattleBPosHpr, self.handleRoundBDone, self.handleBattleBDone, battleNumber, 1) + self.battleBId = self.battleB.doId + else: + self.moveSuits(self.activeSuitsB) + self.suitsB = [] + self.activeSuitsB = [] + if self.arenaSide == None: + self.b_setArenaSide(1) + self.sendBattleIds() + return + + def makeBattle(self, bossCogPosHpr, battlePosHpr, roundCallback, finishCallback, battleNumber, battleSide): + battle = DistributedBattleFinalAI.DistributedBattleFinalAI(self.air, self, roundCallback, finishCallback, battleSide) + self.setBattlePos(battle, bossCogPosHpr, battlePosHpr) + battle.suitsKilled = self.suitsKilled + battle.battleCalc.toonSkillPtsGained = self.toonSkillPtsGained + battle.toonExp = self.toonExp + battle.toonOrigQuests = self.toonOrigQuests + battle.toonItems = self.toonItems + battle.toonOrigMerits = self.toonOrigMerits + battle.toonMerits = self.toonMerits + battle.toonParts = self.toonParts + battle.helpfulToons = self.helpfulToons + mult = ToontownBattleGlobals.getBossBattleCreditMultiplier(battleNumber) + battle.battleCalc.setSkillCreditMultiplier(mult) + battle.generateWithRequired(self.zoneId) + return battle + + def setBattlePos(self, battle, cogPosHpr, battlePosHpr): + bossNode = NodePath('bossNode') + bossNode.setPosHpr(*cogPosHpr) + battleNode = bossNode.attachNewNode('battleNode') + battleNode.setPosHpr(*battlePosHpr) + suitNode = battleNode.attachNewNode('suitNode') + suitNode.setPos(0, 1, 0) + battle.pos = battleNode.getPos(NodePath()) + battle.initialSuitPos = suitNode.getPos(NodePath()) + + def moveSuits(self, active): + for suit in active: + self.reserveSuits.append((suit, 0)) + + def handleRoundADone(self, toonIds, totalHp, deadSuits): + if self.battleA: + self.handleRoundDone(self.battleA, self.suitsA, self.activeSuitsA, toonIds, totalHp, deadSuits) + + def handleRoundBDone(self, toonIds, totalHp, deadSuits): + if self.battleB: + self.handleRoundDone(self.battleB, self.suitsB, self.activeSuitsB, toonIds, totalHp, deadSuits) + + def handleBattleADone(self, zoneId, toonIds): + if self.battleA: + self.battleA.requestDelete() + self.battleA = None + self.battleAId = 0 + self.sendBattleIds() + if self.arenaSide == None: + self.b_setArenaSide(0) + if not self.battleB and self.hasToons() and self.hasToonsAlive(): + self.b_setState(self.postBattleState) + return + + def handleBattleBDone(self, zoneId, toonIds): + if self.battleB: + self.battleB.requestDelete() + self.battleB = None + self.battleBId = 0 + self.sendBattleIds() + if self.arenaSide == None: + self.b_setArenaSide(1) + if not self.battleA and self.hasToons() and self.hasToonsAlive(): + self.b_setState(self.postBattleState) + return + + def invokeSuitPlanner(self, buildingCode, skelecog, skelecogRandom=0): + planner = SuitPlannerInteriorAI.SuitPlannerInteriorAI(1, buildingCode, self.dna.dept, self.zoneId) + planner.respectInvasions = 0 + suits = planner.genFloorSuits(0) + if skelecog: + for suit in suits['activeSuits']: + wantSkelecog = 1 + if skelecogRandom: + wantSkelecog = random.randint(0, 1) + suit.b_setSkelecog(wantSkelecog) + + for reserve in suits['reserveSuits']: + wantSkelecog = 1 + if skelecogRandom: + wantSkelecog = random.randint(0, 1) + suit = reserve[0] + suit.b_setSkelecog(wantSkelecog) + + return suits + + def generateSuits(self, battleNumber): + raise StandardError, 'generateSuits unimplemented' + + def handleRoundDone(self, battle, suits, activeSuits, toonIds, totalHp, deadSuits): + totalMaxHp = 0 + for suit in suits: + totalMaxHp += suit.maxHP + + for suit in deadSuits: + activeSuits.remove(suit) + + joinedReserves = [] + if len(self.reserveSuits) > 0 and len(activeSuits) < 4: + hpPercent = 100 - totalHp / totalMaxHp * 100.0 + for info in self.reserveSuits: + if info[1] <= hpPercent and len(activeSuits) < 4: + suits.append(info[0]) + activeSuits.append(info[0]) + joinedReserves.append(info) + + for info in joinedReserves: + self.reserveSuits.remove(info) + + battle.resume(joinedReserves) + + def getBattleThreeTime(self): + elapsed = globalClock.getFrameTime() - self.battleThreeStart + t1 = elapsed / float(self.battleThreeDuration) + return t1 + + def progressValue(self, fromValue, toValue): + t0 = float(self.bossDamage) / float(self.bossMaxDamage) + elapsed = globalClock.getFrameTime() - self.battleThreeStart + t1 = elapsed / float(self.battleThreeDuration) + t = max(t0, t1) + return fromValue + (toValue - fromValue) * min(t, 1) + + def progressRandomValue(self, fromValue, toValue, radius = 0.2): + t = self.progressValue(0, 1) + radius = radius * (1.0 - abs(t - 0.5) * 2.0) + t += radius * random.uniform(-1, 1) + t = max(min(t, 1.0), 0.0) + return fromValue + (toValue - fromValue) * t + + def reportToonHealth(self): + if self.notify.getDebug(): + str = '' + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + str += ', %s (%s/%s)' % (toonId, toon.getHp(), toon.getMaxHp()) + + self.notify.debug('%s.toons = %s' % (self.doId, str[2:])) + + def getDamageMultiplier(self): + return 1.0 + + def zapToon(self, x, y, z, h, p, r, bpx, bpy, attackCode, timestamp): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'zapToon from unknown avatar'): + return + if attackCode == ToontownGlobals.BossCogLawyerAttack and self.dna.dept != 'l': + self.notify.warning('got lawyer attack but not in CJ boss battle') + return + toon = simbase.air.doId2do.get(avId) + if toon: + self.d_showZapToon(avId, x, y, z, h, p, r, attackCode, timestamp) + damage = ToontownGlobals.BossCogDamageLevels.get(attackCode) + if damage == None: + self.notify.warning('No damage listed for attack code %s' % attackCode) + damage = 5 + damage *= self.getDamageMultiplier() + self.damageToon(toon, damage) + currState = self.getCurrentOrNextState() + if attackCode == ToontownGlobals.BossCogElectricFence and (currState == 'RollToBattleTwo' or currState == 'BattleThree') and self.attackCode not in (ToontownGlobals.BossCogDizzy, ToontownGlobals.BossCogDizzyNow): + if bpy < 0 and abs(bpx / bpy) > 0.5: + if bpx < 0: + self.b_setAttackCode(ToontownGlobals.BossCogSwatRight) + else: + self.b_setAttackCode(ToontownGlobals.BossCogSwatLeft) + return + + def d_showZapToon(self, avId, x, y, z, h, p, r, attackCode, timestamp): + self.sendUpdate('showZapToon', [avId, + x, + y, + z, + h, + p, + r, + attackCode, + timestamp]) + + def b_setAttackCode(self, attackCode, avId = 0): + self.d_setAttackCode(attackCode, avId) + self.setAttackCode(attackCode, avId) + + def setAttackCode(self, attackCode, avId = 0): + self.attackCode = attackCode + self.attackAvId = avId + if attackCode == ToontownGlobals.BossCogDizzy or attackCode == ToontownGlobals.BossCogDizzyNow: + delayTime = self.progressValue(20, 5) + self.hitCount = 0 + elif attackCode == ToontownGlobals.BossCogSlowDirectedAttack: + delayTime = ToontownGlobals.BossCogAttackTimes.get(attackCode) + delayTime += self.progressValue(10, 0) + else: + delayTime = ToontownGlobals.BossCogAttackTimes.get(attackCode) + if delayTime == None: + return + if self.dept == 'm' and attackCode == ToontownGlobals.BossCogAreaAttack: + delayTime += 5.0 + self.waitForNextAttack(delayTime) + + def d_setAttackCode(self, attackCode, avId = 0): + self.sendUpdate('setAttackCode', [attackCode, avId]) + + def waitForNextAttack(self, delayTime): + currState = self.getCurrentOrNextState() + if currState == 'BattleThree': + taskName = self.uniqueName('NextAttack') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.doNextAttack, taskName) + + def stopAttacks(self): + taskName = self.uniqueName('NextAttack') + taskMgr.remove(taskName) + + def doNextAttack(self, task): + self.b_setAttackCode(ToontownGlobals.BossCogNoAttack) + + def giveKeyReward(self): + if not self.keyReward: + return + + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + + if toon: + toon.addCrateKeys(1) + + def addStats(self): + stat = BOSS_TO_STAT[self.dept] + + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + + if toon: + toon.addStat(stat) \ No newline at end of file diff --git a/toontown/suit/DistributedBossbotBoss.py b/toontown/suit/DistributedBossbotBoss.py new file mode 100755 index 00000000..d9ca6b89 --- /dev/null +++ b/toontown/suit/DistributedBossbotBoss.py @@ -0,0 +1,1529 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import globalClockDelta +from direct.fsm import FSM +from direct.interval.IntervalGlobal import Sequence, Wait, Func, LerpHprInterval, Parallel, LerpPosInterval, Track, ActorInterval, ParallelEndTogether, LerpFunctionInterval, LerpScaleInterval, LerpPosHprInterval, SoundInterval +from direct.showbase import PythonUtil +from direct.task import Task +import math +from pandac.PandaModules import VBase3, CollisionPlane, CollisionNode, CollisionSphere, CollisionTube, NodePath, Plane, Vec3, Vec2, Point3, BitMask32, CollisionHandlerEvent, TextureStage, VBase4, BoundingSphere +import random + +from toontown.battle import MovieToonVictory +from toontown.battle import RewardPanel +from toontown.battle import SuitBattleGlobals +from toontown.building import ElevatorConstants +from toontown.coghq import CogDisguiseGlobals +from toontown.distributed import DelayDelete +from toontown.effects import DustCloud +from toontown.suit import DistributedBossCog +from toontown.suit import Suit +from toontown.suit import SuitDNA +from toontown.toon import Toon, NPCToons +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownTimer +from otp.nametag import NametagGroup +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals + + +OneBossCog = None +TTL = TTLocalizer + + +class DistributedBossbotBoss(DistributedBossCog.DistributedBossCog, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBossbotBoss') + BallLaunchOffset = Point3(10.5, 8.5, -5) + + def __init__(self, cr): + self.notify.debug('----- __init___') + DistributedBossCog.DistributedBossCog.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedBossbotBoss') + self.bossDamage = 0 + self.bossMaxDamage = ToontownGlobals.BossbotBossMaxDamage + self.elevatorType = ElevatorConstants.ELEVATOR_BB + self.resistanceToon = None + self.resistanceToonOnstage = 0 + self.battleANode.setPosHpr(*ToontownGlobals.WaiterBattleAPosHpr) + self.battleBNode.setPosHpr(*ToontownGlobals.WaiterBattleBPosHpr) + self.toonFoodStatus = {} + self.belts = [None, None] + self.tables = {} + self.golfSpots = {} + self.servingTimer = None + self.notDeadList = None + self.moveTrack = None + self.speedDamage = 0 + self.maxSpeedDamage = ToontownGlobals.BossbotMaxSpeedDamage + self.speedRecoverRate = 0 + self.speedRecoverStartTime = 0 + self.ballLaunch = None + self.moveTrack = None + self.lastZapLocalTime = 0 + self.numAttacks = 0 + return + + def announceGenerate(self): + global OneBossCog + DistributedBossCog.DistributedBossCog.announceGenerate(self) + self.setName(TTLocalizer.BossbotBossName) + nameInfo = TTLocalizer.BossCogNameWithDept % {'name': self.name, + 'dept': SuitDNA.getDeptFullname(self.style.dept)} + self.setDisplayName(nameInfo) + self.loadEnvironment() + self.__makeResistanceToon() + base.localAvatar.chatMgr.chatInputSpeedChat.addCEOMenu() + if OneBossCog != None: + self.notify.warning('Multiple BossCogs visible.') + OneBossCog = self + render.setTag('pieCode', str(ToontownGlobals.PieCodeNotBossCog)) + self.setTag('attackCode', str(ToontownGlobals.BossCogGolfAttack)) + target = CollisionTube(0, -2, -2, 0, -1, 9, 4.0) + targetNode = CollisionNode('BossZap') + targetNode.addSolid(target) + targetNode.setCollideMask(ToontownGlobals.PieBitmask) + self.targetNodePath = self.pelvis.attachNewNode(targetNode) + self.targetNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossCog)) + self.axle.getParent().setTag('pieCode', str(ToontownGlobals.PieCodeBossCog)) + disk = loader.loadModel('phase_9/models/char/bossCog-gearCollide') + disk.find('**/+CollisionNode').setName('BossZap') + disk.reparentTo(self.pelvis) + disk.setZ(0.8) + closeBubble = CollisionSphere(0, 0, 0, 10) + closeBubble.setTangible(0) + closeBubbleNode = CollisionNode('CloseBoss') + closeBubbleNode.setIntoCollideMask(BitMask32(0)) + closeBubbleNode.setFromCollideMask(ToontownGlobals.BanquetTableBitmask) + closeBubbleNode.addSolid(closeBubble) + self.closeBubbleNode = closeBubbleNode + self.closeHandler = CollisionHandlerEvent() + self.closeHandler.addInPattern('closeEnter') + self.closeHandler.addOutPattern('closeExit') + self.closeBubbleNodePath = self.attachNewNode(closeBubbleNode) + (base.cTrav.addCollider(self.closeBubbleNodePath, self.closeHandler),) + self.accept('closeEnter', self.closeEnter) + self.accept('closeExit', self.closeExit) + self.treads = self.find('**/treads') + demotedCeo = Suit.Suit() + demotedCeo.dna = SuitDNA.SuitDNA() + demotedCeo.dna.newSuit('f') + demotedCeo.setDNA(demotedCeo.dna) + demotedCeo.reparentTo(self.geom) + demotedCeo.loop('neutral') + demotedCeo.stash() + self.demotedCeo = demotedCeo + self.bossClub = loader.loadModel('phase_12/models/char/bossbotBoss-golfclub') + overtimeOneClubSequence = Sequence(self.bossClub.colorScaleInterval(0.1, colorScale=VBase4(0, 1, 0, 1)), self.bossClub.colorScaleInterval(0.3, colorScale=VBase4(1, 1, 1, 1))) + overtimeTwoClubSequence = Sequence(self.bossClub.colorScaleInterval(0.1, colorScale=VBase4(1, 0, 0, 1)), self.bossClub.colorScaleInterval(0.3, colorScale=VBase4(1, 1, 1, 1))) + self.bossClubIntervals = [overtimeOneClubSequence, overtimeTwoClubSequence] + self.rightHandJoint = self.find('**/joint17') + self.setPosHpr(*ToontownGlobals.BossbotBossBattleOnePosHpr) + self.reparentTo(render) + self.toonUpSfx = loader.loadSfx('phase_11/audio/sfx/LB_toonup.ogg') + self.warningSfx = loader.loadSfx('phase_5/audio/sfx/Skel_COG_VO_grunt.ogg') + self.swingClubSfx = loader.loadSfx('phase_5/audio/sfx/SA_hardball.ogg') + self.moveBossTaskName = 'CEOMoveTask' + return + + def disable(self): + global OneBossCog + self.notify.debug('----- disable') + DistributedBossCog.DistributedBossCog.disable(self) + self.demotedCeo.delete() + base.cTrav.removeCollider(self.closeBubbleNodePath) + taskMgr.remove('RecoverSpeedDamage') + self.request('Off') + self.unloadEnvironment() + self.__cleanupResistanceToon() + if self.servingTimer: + self.servingTimer.destroy() + del self.servingTimer + base.localAvatar.chatMgr.chatInputSpeedChat.removeCEOMenu() + if OneBossCog == self: + OneBossCog = None + self.promotionMusic.stop() + self.betweenPhaseMusic.stop() + self.phaseTwoMusic.stop() + self.phaseFourMusic.stop() + self.interruptMove() + for ival in self.bossClubIntervals: + ival.finish() + + self.belts = [] + self.tables = {} + self.removeAllTasks() + return + + def loadEnvironment(self): + self.notify.debug('----- loadEnvironment') + DistributedBossCog.DistributedBossCog.loadEnvironment(self) + self.geom = loader.loadModel('phase_12/models/bossbotHQ/BanquetInterior_1') + self.elevatorEntrance = self.geom.find('**/elevator_origin') + elevatorModel = loader.loadModel('phase_12/models/bossbotHQ/BB_Inside_Elevator') + if not elevatorModel: + elevatorModel = loader.loadModel('phase_12/models/bossbotHQ/BB_Elevator') + elevatorModel.reparentTo(self.elevatorEntrance) + self.setupElevator(elevatorModel) + self.banquetDoor = self.geom.find('**/door3') + plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, -50))) + planeNode = CollisionNode('dropPlane') + planeNode.addSolid(plane) + planeNode.setCollideMask(ToontownGlobals.PieBitmask) + self.geom.attachNewNode(planeNode) + self.geom.reparentTo(render) + self.promotionMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.ogg') + self.betweenPhaseMusic = base.loadMusic('phase_9/audio/bgm/encntr_toon_winning.ogg') + self.phaseTwoMusic = base.loadMusic('phase_12/audio/bgm/BossBot_CEO_v1.ogg') + self.phaseFourMusic = base.loadMusic('phase_12/audio/bgm/BossBot_CEO_v2.ogg') + self.pickupFoodSfx = loader.loadSfx('phase_6/audio/sfx/SZ_MM_gliss.ogg') + self.explodeSfx = loader.loadSfx('phase_4/audio/sfx/firework_distance_02.ogg') + + def unloadEnvironment(self): + self.notify.debug('----- unloadEnvironment') + for belt in self.belts: + if belt: + belt.cleanup() + + for spot in self.golfSpots.values(): + if spot: + spot.cleanup() + + self.golfSpots = {} + self.geom.removeNode() + del self.geom + DistributedBossCog.DistributedBossCog.unloadEnvironment(self) + + def __makeResistanceToon(self): + if self.resistanceToon: + return + self.resistanceToon = NPCToons.createLocalNPC(10002) + self.resistanceToon.setPosHpr(*ToontownGlobals.BossbotRTIntroStartPosHpr) + state = random.getstate() + random.seed(self.doId) + self.resistanceToon.suitType = SuitDNA.getRandomSuitByDept('c') + random.setstate(state) + + def __cleanupResistanceToon(self): + self.__hideResistanceToon() + if self.resistanceToon: + self.resistanceToon.takeOffSuit() + self.resistanceToon.removeActive() + self.resistanceToon.delete() + self.resistanceToon = None + return + + def __showResistanceToon(self, withSuit): + if not self.resistanceToonOnstage: + self.resistanceToon.addActive() + self.resistanceToon.reparentTo(self.geom) + self.resistanceToonOnstage = 1 + if withSuit: + suit = self.resistanceToon.suitType + self.resistanceToon.putOnSuit(suit, False) + else: + self.resistanceToon.takeOffSuit() + + def __hideResistanceToon(self): + if self.resistanceToonOnstage: + self.resistanceToon.removeActive() + self.resistanceToon.detachNode() + self.resistanceToonOnstage = 0 + + def enterElevator(self): + DistributedBossCog.DistributedBossCog.enterElevator(self) + self.resistanceToon.removeActive() + self.__showResistanceToon(True) + self.resistanceToon.suit.loop('neutral') + base.camera.setPos(0, 21, 7) + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.BossbotBossBattleOnePosHpr) + self.loop('Ff_neutral') + self.show() + base.camLens.setMinFov(ToontownGlobals.CEOElevatorFov/(4./3.)) + + def enterIntroduction(self): + if not self.resistanceToonOnstage: + self.__showResistanceToon(True) + DistributedBossCog.DistributedBossCog.enterIntroduction(self) + base.playMusic(self.promotionMusic, looping=1, volume=0.9) + + def exitIntroduction(self): + DistributedBossCog.DistributedBossCog.exitIntroduction(self) + self.promotionMusic.stop() + + def makeIntroductionMovie(self, delayDeletes): + rToon = self.resistanceToon + rToonStartPos = Point3(ToontownGlobals.BossbotRTIntroStartPosHpr[0], ToontownGlobals.BossbotRTIntroStartPosHpr[1], ToontownGlobals.BossbotRTIntroStartPosHpr[2]) + rToonEndPos = rToonStartPos + Point3(40, 0, 0) + elevCamPosHpr = ToontownGlobals.BossbotElevCamPosHpr + closeUpRTCamPos = Point3(elevCamPosHpr[0], elevCamPosHpr[1], elevCamPosHpr[2]) + closeUpRTCamHpr = Point3(elevCamPosHpr[3], elevCamPosHpr[4], elevCamPosHpr[5]) + closeUpRTCamPos.setY(closeUpRTCamPos.getY() + 20) + closeUpRTCamPos.setZ(closeUpRTCamPos.getZ() + -2) + closeUpRTCamHpr = Point3(0, 5, 0) + loseSuitCamPos = Point3(rToonStartPos) + loseSuitCamPos += Point3(0, -5, 4) + loseSuitCamHpr = Point3(180, 0, 0) + waiterCamPos = Point3(rToonStartPos) + waiterCamPos += Point3(-5, -10, 5) + waiterCamHpr = Point3(-30, 0, 0) + track = Sequence(Func(camera.reparentTo, render), Func(camera.setPosHpr, *elevCamPosHpr), Func(rToon.setChatAbsolute, TTL.BossbotRTWelcome, CFSpeech), LerpPosHprInterval(camera, 3, closeUpRTCamPos, closeUpRTCamHpr), Func(rToon.setChatAbsolute, TTL.BossbotRTRemoveSuit, CFSpeech), Wait(3), Func(self.clearChat), self.loseCogSuits(self.toonsA + self.toonsB, render, (loseSuitCamPos[0], + loseSuitCamPos[1], + loseSuitCamPos[2], + loseSuitCamHpr[0], + loseSuitCamHpr[1], + loseSuitCamHpr[2])), self.toonNormalEyes(self.involvedToons), Wait(2), Func(camera.setPosHpr, closeUpRTCamPos, closeUpRTCamHpr), Func(rToon.setChatAbsolute, TTL.BossbotRTFightWaiter, CFSpeech), Wait(1), LerpHprInterval(camera, 2, Point3(-15, 5, 0)), Sequence(Func(rToon.suit.loop, 'walk'), rToon.hprInterval(1, VBase3(270, 0, 0)), rToon.posInterval(2.5, rToonEndPos), Func(rToon.suit.loop, 'neutral')), Wait(3), Func(rToon.clearChat), Func(self.__hideResistanceToon)) + return track + + def enterFrolic(self): + self.notify.debug('----- enterFrolic') + self.setPosHpr(*ToontownGlobals.BossbotBossBattleOnePosHpr) + DistributedBossCog.DistributedBossCog.enterFrolic(self) + self.show() + + def enterPrepareBattleTwo(self): + self.controlToons() + self.setToonsToNeutral(self.involvedToons) + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.takeOffSuit() + + self.__showResistanceToon(True) + self.resistanceToon.setPosHpr(*ToontownGlobals.BossbotRTPreTwoPosHpr) + self.__arrangeToonsAroundResistanceToon() + intervalName = 'PrepareBattleTwoMovie' + delayDeletes = [] + seq = Sequence(self.makePrepareBattleTwoMovie(delayDeletes), Func(self.__onToBattleTwo), name=intervalName) + seq.delayDeletes = delayDeletes + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.betweenPhaseMusic, looping=1, volume=0.9) + + def makePrepareBattleTwoMovie(self, delayDeletes): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'BossbotBoss.makePrepareBattleTwoMovie')) + + rToon = self.resistanceToon + rToonStartPos = Point3(ToontownGlobals.BossbotRTPreTwoPosHpr[0], ToontownGlobals.BossbotRTPreTwoPosHpr[1], ToontownGlobals.BossbotRTPreTwoPosHpr[2]) + rToonEndPos = rToonStartPos + Point3(-40, 0, 0) + bossPos = Point3(ToontownGlobals.BossbotBossPreTwoPosHpr[0], ToontownGlobals.BossbotBossPreTwoPosHpr[1], ToontownGlobals.BossbotBossPreTwoPosHpr[2]) + bossEndPos = Point3(ToontownGlobals.BossbotBossBattleOnePosHpr[0], ToontownGlobals.BossbotBossBattleOnePosHpr[1], ToontownGlobals.BossbotBossBattleOnePosHpr[2]) + tempNode = self.attachNewNode('temp') + tempNode.setPos(0, -40, 18) + + def getCamBossPos(tempNode = tempNode): + return tempNode.getPos(render) + + rNode = rToon.attachNewNode('temp2') + rNode.setPos(-5, 25, 12) + + def getCamRTPos(rNode = rNode): + return rNode.getPos(render) + + track = Sequence( + Func(camera.reparentTo, render), + Func(camera.setPos, rToon, 0, 22, 6), + Func(camera.setHpr, 0, 0, 0), + Func(rToon.setChatAbsolute, TTL.BossbotRTWearWaiter, CFSpeech), + Wait(3.0), + self.wearCogSuits(self.toonsA + self.toonsB, render, None, waiter=True), + Func(rToon.clearChat), + Func(self.setPosHpr, bossPos, Point3(0, 0, 0)), + Parallel(LerpHprInterval(self.banquetDoor, 2, Point3(90, 0, 0)), + LerpPosInterval(camera, 2, getCamBossPos)), + Func(self.setChatAbsolute, TTL.BossbotBossPreTwo1, CFSpeech), + Wait(3.0), + Func(self.setChatAbsolute, TTL.BossbotBossPreTwo2, CFSpeech), + Wait(3.0), + Parallel( + LerpHprInterval(self.banquetDoor, 2, Point3(0, 0, 0)), + LerpPosHprInterval(camera, 2, getCamRTPos, Point3(10, -8, 0))), + Func(self.setPos, bossEndPos), + Func(self.clearChat), + Func(rToon.setChatAbsolute, TTL.BossbotRTServeFood1, CFSpeech), + Wait(3.0), + Func(rToon.setChatAbsolute, TTL.BossbotRTServeFood2, CFSpeech), + Wait(1.0), + LerpHprInterval(self.banquetDoor, 2, Point3(120, 0, 0)), + Sequence( + Func(rToon.suit.loop, 'walk'), + rToon.hprInterval(1, VBase3(90, 0, 0)), + rToon.posInterval(2.5, rToonEndPos), + Func(rToon.suit.loop, 'neutral')), + self.createWalkInInterval(), + Func(self.banquetDoor.setH, 0), + Func(rToon.clearChat), + Func(self.__hideResistanceToon)) + return track + + def createWalkInInterval(self): + retval = Parallel() + delay = 0 + index = 0 + for toonId in self.involvedToons: + toon = base.cr.doId2do.get(toonId) + if not toon: + continue + destPos = Point3(-14 + index * 4, 25, 0) + + def toWalk(toon): + if hasattr(toon, 'suit') and toon.suit: + toon.suit.loop('walk') + + def toNeutral(toon): + if hasattr(toon, 'suit') and toon.suit: + toon.suit.loop('neutral') + + retval.append(Sequence(Wait(delay), Func(toon.wrtReparentTo, render), Func(toWalk, toon), Func(toon.headsUp, 0, 0, 0), LerpPosInterval(toon, 3, Point3(0, 0, 0)), Func(toon.headsUp, destPos), LerpPosInterval(toon, 3, destPos), LerpHprInterval(toon, 1, Point3(0, 0, 0)), Func(toNeutral, toon))) + if toon == base.localAvatar: + retval.append(Sequence(Wait(delay), Func(camera.reparentTo, toon), Func(camera.setPos, toon.cameraPositions[0][0]), Func(camera.setHpr, 0, 0, 0))) + delay += 1.0 + index += 1 + + return retval + + def __onToBattleTwo(self, elapsedTime = 0): + self.doneBarrier('PrepareBattleTwo') + + def exitPrepareBattleTwo(self): + self.clearInterval('PrepareBattleTwoMovie') + self.betweenPhaseMusic.stop() + + def __arrangeToonsAroundResistanceToon(self): + radius = 9 + numToons = len(self.involvedToons) + center = (numToons - 1) / 2.0 + for i in xrange(numToons): + toon = self.cr.doId2do.get(self.involvedToons[i]) + if toon: + angle = 90 - 25 * (i - center) + radians = angle * math.pi / 180.0 + x = math.cos(radians) * radius + y = math.sin(radians) * radius + toon.reparentTo(render) + toon.setPos(self.resistanceToon, x, y, 0) + toon.headsUp(self.resistanceToon) + toon.loop('neutral') + toon.show() + + def enterBattleTwo(self): + self.releaseToons(finalBattle=1) + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + self.putToonInCogSuit(toon) + + self.servingTimer = ToontownTimer.ToontownTimer() + self.servingTimer.posInTopRightCorner() + self.servingTimer.countdown(ToontownGlobals.BossbotBossServingDuration) + base.playMusic(self.phaseTwoMusic, looping=1, volume=0.9) + + intervalName = 'BattleTwoSpeech' + seq = self.createTalkSequence(TTLocalizer.CEOSpeech, 10, intervalName) + seq.start() + self.storeInterval(seq, intervalName) + + def exitBattleTwo(self): + if self.servingTimer: + self.servingTimer.destroy() + del self.servingTimer + self.servingTimer = None + for toonId in self.involvedToons: + self.removeFoodFromToon(toonId) + + self.clearInterval('BattleTwoSpeech') + self.phaseTwoMusic.stop() + return + + def setBelt(self, belt, beltIndex): + if beltIndex < len(self.belts): + self.belts[beltIndex] = belt + + def localToonTouchedBeltFood(self, beltIndex, foodIndex, foodNum): + avId = base.localAvatar.doId + doRequest = False + if avId not in self.toonFoodStatus: + doRequest = True + elif not self.toonFoodStatus[avId]: + doRequest = True + if doRequest: + self.sendUpdate('requestGetFood', [beltIndex, foodIndex, foodNum]) + + def toonGotFood(self, avId, beltIndex, foodIndex, foodNum): + if self.belts[beltIndex]: + self.belts[beltIndex].removeFood(foodIndex) + self.putFoodOnToon(avId, beltIndex, foodNum) + + def putFoodOnToon(self, avId, beltIndex, foodNum): + self.toonFoodStatus[avId] = (beltIndex, foodNum) + av = base.cr.doId2do.get(avId) + if av: + if hasattr(av, 'suit'): + intervalName = self.uniqueName('loadFoodSoundIval-%d' % avId) + seq = SoundInterval(self.pickupFoodSfx, node=av, name=intervalName) + oldSeq = self.activeIntervals.get(intervalName) + if oldSeq: + oldSeq.finish() + seq.start() + self.activeIntervals[intervalName] = seq + foodModel = loader.loadModel('phase_12/models/bossbotHQ/canoffood') + foodModel.setName('cogFood') + foodModel.setScale(ToontownGlobals.BossbotFoodModelScale) + foodModel.reparentTo(av.suit.getRightHand()) + foodModel.setHpr(52.1961, 180.4983, -4.2882) + curAnim = av.suit.getCurrentAnim() + self.notify.debug('curAnim=%s' % curAnim) + if curAnim in ('walk', 'run'): + av.suit.loop('tray-walk') + elif curAnim == 'neutral': + self.notify.debug('looping tray-netural') + av.suit.loop('tray-neutral') + else: + self.notify.warning("don't know what to do with anim=%s" % curAnim) + + def removeFoodFromToon(self, avId): + self.toonFoodStatus[avId] = None + av = base.cr.doId2do.get(avId) + if av: + cogFood = av.find('**/cogFood') + if not cogFood.isEmpty(): + cogFood.removeNode() + return + + def detachFoodFromToon(self, avId): + cogFood = None + self.toonFoodStatus[avId] = None + av = base.cr.doId2do.get(avId) + if av: + cogFood = av.find('**/cogFood') + if not cogFood.isEmpty(): + retval = cogFood + cogFood.wrtReparentTo(render) + curAnim = av.suit.getCurrentAnim() + self.notify.debug('curAnim=%s' % curAnim) + if curAnim == 'tray-walk': + av.suit.loop('run') + elif curAnim == 'tray-neutral': + av.suit.loop('neutral') + else: + self.notify.warning("don't know what to do with anim=%s" % curAnim) + return cogFood + + def setTable(self, table, tableIndex): + self.tables[tableIndex] = table + + def localToonTouchedChair(self, tableIndex, chairIndex): + avId = base.localAvatar.doId + if avId in self.toonFoodStatus and self.toonFoodStatus[avId] != None: + self.sendUpdate('requestServeFood', [tableIndex, chairIndex]) + return + + def toonServeFood(self, avId, tableIndex, chairIndex): + food = self.detachFoodFromToon(avId) + table = self.tables[tableIndex] + table.serveFood(food, chairIndex) + + def enterPrepareBattleThree(self): + self.calcNotDeadList() + self.battleANode.setPosHpr(*ToontownGlobals.DinerBattleAPosHpr) + self.battleBNode.setPosHpr(*ToontownGlobals.DinerBattleBPosHpr) + self.cleanupIntervals() + self.controlToons() + self.setToonsToNeutral(self.involvedToons) + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + self.putToonInCogSuit(toon) + + intervalName = 'PrepareBattleThreeMovie' + seq = Sequence(self.makePrepareBattleThreeMovie(), Func(self.__onToBattleThree), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.betweenPhaseMusic, looping=1, volume=0.9) + + def calcNotDeadList(self): + if not self.notDeadList: + self.notDeadList = [] + for tableIndex in xrange(len(self.tables)): + table = self.tables[tableIndex] + tableInfo = table.getNotDeadInfo() + self.notDeadList += tableInfo + + def exitPrepareBattleThree(self): + self.clearInterval('PrepareBattleThreeMovie') + self.betweenPhaseMusic.stop() + + def __onToBattleThree(self, elapsedTime = 0): + self.doneBarrier('PrepareBattleThree') + + def makePrepareBattleThreeMovie(self): + loseSuitCamAngle = (0, 19, 6, -180, -5, 0) + track = Sequence( + Func(camera.reparentTo, self), + Func(camera.setPos, Point3(0, -45, 5)), + Func(camera.setHpr, Point3(0, 14, 0)), + Func(self.setChatAbsolute, TTL.BossbotPhase3Speech1, CFSpeech), + Wait(3.0), + Func(self.setChatAbsolute, TTL.BossbotPhase3Speech2, CFSpeech), + Wait(3.0), + Func(camera.setPosHpr, base.localAvatar, *loseSuitCamAngle), + Wait(1.0), + self.loseCogSuits(self.toonsA + self.toonsB, base.localAvatar, loseSuitCamAngle), + self.toonNormalEyes(self.involvedToons), + Wait(2), + Func(camera.reparentTo, self), + Func(camera.setPos, Point3(0, -45, 5)), + Func(camera.setHpr, Point3(0, 14, 0)), + Func(self.setChatAbsolute, TTL.BossbotPhase3Speech3, CFSpeech), + Wait(3.0), + Func(self.clearChat)) + return track + + def enterBattleThree(self): + self.cleanupIntervals() + self.calcNotDeadList() + for table in self.tables.values(): + table.setAllDinersToSitNeutral() + + self.battleANode.setPosHpr(*ToontownGlobals.DinerBattleAPosHpr) + self.battleBNode.setPosHpr(*ToontownGlobals.DinerBattleBPosHpr) + self.setToonsToNeutral(self.involvedToons) + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.takeOffSuit() + + mult = 1 + localAvatar.inventory.setBattleCreditMultiplier(mult) + self.toonsToBattlePosition(self.toonsA, self.battleANode) + self.toonsToBattlePosition(self.toonsB, self.battleBNode) + self.releaseToons() + base.playMusic(self.battleOneMusic, looping=1, volume=0.9) + + def exitBattleThree(self): + self.cleanupBattles() + self.battleOneMusic.stop() + localAvatar.inventory.setBattleCreditMultiplier(1) + + def claimOneChair(self): + chairInfo = None + if self.notDeadList: + chairInfo = self.notDeadList.pop() + return chairInfo + + def enterPrepareBattleFour(self): + self.controlToons() + intervalName = 'PrepareBattleFourMovie' + seq = Sequence(self.makePrepareBattleFourMovie(), Func(self.__onToBattleFour), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.phaseFourMusic, looping=1, volume=0.9) + + def exitPrepareBattleFour(self): + self.clearInterval('PrepareBattleFourMovie') + self.phaseFourMusic.stop() + + def makePrepareBattleFourMovie(self): + rToon = self.resistanceToon + offsetZ = rToon.suit.getHeight() / 2.0 + track = Sequence( + Func(self.__showResistanceToon, True), + Func(rToon.setPos, Point3(0, -5, 0)), + Func(rToon.setHpr, Point3(0, 0, 0)), + Func(camera.reparentTo, rToon), + Func(camera.setPos, Point3(0, 13, 3 + offsetZ)), + Func(camera.setHpr, Point3(-180, 0, 0)), + Func(self.banquetDoor.setH, 90), + Func(rToon.setChatAbsolute, TTL.BossbotRTPhase4Speech1, CFSpeech), + Wait(4.0), + Func(rToon.setChatAbsolute, TTL.BossbotRTPhase4Speech2, CFSpeech), + Wait(4.0), + Func(self.__hideResistanceToon), + Func(camera.reparentTo, self), + Func(camera.setPos, Point3(0, -45, 5)), + Func(camera.setHpr, Point3(0, 14, 0)), + Func(self.setChatAbsolute, TTL.BossbotPhase4Speech1, CFSpeech), + Func(self.banquetDoor.setH, 0), + Wait(3.0), + Func(self.setChatAbsolute, TTL.BossbotPhase4Speech2, CFSpeech), + Func(self.bossClub.setScale, 0.01), + Func(self.bossClub.reparentTo, self.rightHandJoint), + LerpScaleInterval(self.bossClub, 3, Point3(1, 1, 1)), + Func(self.clearChat)) + return track + + def __onToBattleFour(self, elapsedTime = 0): + self.doneBarrier('PrepareBattleFour') + + def enterBattleFour(self): + DistributedBossCog.DistributedBossCog.enterBattleFour(self) + self.releaseToons(finalBattle=1) + self.setToonsToNeutral(self.involvedToons) + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.takeOffSuit() + + self.bossClub.reparentTo(self.rightHandJoint) + self.generateHealthBar() + self.updateHealthBar() + base.playMusic(self.phaseFourMusic, looping=1, volume=0.9) + + def exitBattleFour(self): + DistributedBossCog.DistributedBossCog.exitBattleFour(self) + self.phaseFourMusic.stop() + + def d_hitBoss(self, bossDamage): + self.sendUpdate('hitBoss', [bossDamage]) + + def d_ballHitBoss(self, bossDamage): + self.sendUpdate('ballHitBoss', [bossDamage]) + + def setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + if bossDamage > self.bossDamage: + delta = bossDamage - self.bossDamage + self.flashRed() + self.showHpText(-delta, scale=5) + self.bossDamage = bossDamage + self.updateHealthBar() + + def setGolfSpot(self, golfSpot, golfSpotIndex): + self.golfSpots[golfSpotIndex] = golfSpot + + def enterVictory(self): + self.notify.debug('----- enterVictory') + self.cleanupIntervals() + self.cleanupAttacks() + self.doAnimate('Ff_neutral', now=1) + self.stopMoveTask() + if hasattr(self, 'tableIndex'): + table = self.tables[self.tableIndex] + table.tableGroup.hide() + self.loop('neutral') + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.clearChat() + self.controlToons() + self.setToonsToNeutral(self.involvedToons) + self.happy = 1 + self.raised = 1 + self.forward = 1 + intervalName = 'VictoryMovie' + seq = Sequence(self.makeVictoryMovie(), Func(self.__continueVictory), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.phaseFourMusic, looping=1, volume=0.9) + + def __continueVictory(self): + self.notify.debug('----- __continueVictory') + self.stopAnimate() + self.doneBarrier('Victory') + + def exitVictory(self): + self.notify.debug('----- exitVictory') + self.stopAnimate() + self.unstash() + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.phaseFourMusic.stop() + + def makeVictoryMovie(self): + self.show() + dustCloud = DustCloud.DustCloud(fBillboard=0, wantSound=1) + dustCloud.reparentTo(self) + dustCloud.setPos(0, -10, 3) + dustCloud.setScale(4) + dustCloud.wrtReparentTo(self.geom) + dustCloud.createTrack(12) + newHpr = self.getHpr() + newHpr.setX(newHpr.getX() + 180) + bossTrack = Sequence( + Func(self.show), + Func(camera.reparentTo, self), + Func(camera.setPos, Point3(0, -35, 25)), + Func(camera.setHpr, Point3(0, -20, 0)), + Func(self.setChatAbsolute, TTL.BossbotRewardSpeech1, CFSpeech), + Wait(3.0), + Func(self.setChatAbsolute, TTL.BossbotRewardSpeech2, CFSpeech), + Wait(2.0), + Func(self.clearChat), + Parallel( + Sequence( + Wait(0.5), + Func(self.demotedCeo.setPos, self.getPos()), + Func(self.demotedCeo.setHpr, newHpr), + Func(self.hide), + Wait(0.5), + Func(self.demotedCeo.reparentTo, self.geom), + Func(self.demotedCeo.unstash)), + Sequence(dustCloud.track)), + Wait(2.0), + Func(dustCloud.destroy)) + return bossTrack + + def enterReward(self): + self.cleanupIntervals() + self.clearChat() + self.resistanceToon.clearChat() + self.stash() + self.stopAnimate() + self.controlToons() + panelName = self.uniqueName('reward') + self.rewardPanel = RewardPanel.RewardPanel(panelName) + victory, camVictory, skipper = MovieToonVictory.doToonVictory(1, self.involvedToons, self.toonRewardIds, self.toonRewardDicts, self.deathList, self.rewardPanel, allowGroupShot=0, uberList=self.uberList, noSkip=True) + ival = Sequence(Parallel(victory, camVictory), Func(self.__doneReward)) + intervalName = 'RewardMovie' + delayDeletes = [] + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'BossbotBoss.enterReward')) + + ival.delayDeletes = delayDeletes + ival.start() + self.storeInterval(ival, intervalName) + base.playMusic(self.betweenPhaseMusic, looping=1, volume=0.9) + + def __doneReward(self): + self.notify.debug('----- __doneReward') + self.doneBarrier('Reward') + self.toWalkMode() + + def exitReward(self): + self.notify.debug('----- exitReward') + intervalName = 'RewardMovie' + self.clearInterval(intervalName) + self.unstash() + self.rewardPanel.destroy() + del self.rewardPanel + self.betweenPhaseMusic.stop() + + def enterEpilogue(self): + self.cleanupIntervals() + self.clearChat() + self.resistanceToon.clearChat() + self.stash() + self.stopAnimate() + self.controlToons() + self.__showResistanceToon(False) + self.resistanceToon.reparentTo(render) + self.resistanceToon.setPosHpr(*ToontownGlobals.BossbotRTEpiloguePosHpr) + self.resistanceToon.loop('Sit') + self.__arrangeToonsAroundResistanceToonForReward() + camera.reparentTo(render) + camera.setPos(self.resistanceToon, -9, 12, 6) + camera.lookAt(self.resistanceToon, 0, 0, 3) + intervalName = 'EpilogueMovie' + seq = Sequence(self.makeEpilogueMovie(), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + self.accept('doneChatPage', self.__doneEpilogue) + base.playMusic(self.epilogueMusic, looping=1, volume=0.9) + + def __doneEpilogue(self, elapsedTime = 0): + self.notify.debug('----- __doneEpilogue') + intervalName = 'EpilogueMovieToonAnim' + self.clearInterval(intervalName) + track = Parallel(Sequence(Wait(0.5), Func(self.localToonToSafeZone))) + self.storeInterval(track, intervalName) + track.start() + + def exitEpilogue(self): + self.notify.debug('----- exitEpilogue') + self.clearInterval('EpilogueMovieToonAnim') + self.unstash() + self.epilogueMusic.stop() + + def makeEpilogueMovie(self): + epSpeech = TTLocalizer.BossbotRTCongratulations + epSpeech = self.__talkAboutPromotion(epSpeech) + bossTrack = Sequence(Func(self.resistanceToon.animFSM.request, 'neutral'), Func(self.resistanceToon.setLocalPageChat, epSpeech, 0)) + return bossTrack + + def __talkAboutPromotion(self, speech): + if self.prevCogSuitLevel < ToontownGlobals.MaxCogSuitLevel: + newCogSuitLevel = localAvatar.getCogLevels()[CogDisguiseGlobals.dept2deptIndex(self.style.dept)] + if newCogSuitLevel == ToontownGlobals.MaxCogSuitLevel: + speech += TTLocalizer.BossbotRTLastPromotion % (ToontownGlobals.MaxCogSuitLevel + 1) + if newCogSuitLevel in ToontownGlobals.CogSuitHPLevels: + speech += TTLocalizer.BossbotRTHPBoost + else: + speech += TTLocalizer.BossbotRTMaxed % (ToontownGlobals.MaxCogSuitLevel + 1) + + if self.keyReward: + speech += TTLocalizer.BossRTKeyReward + + return speech + + def __arrangeToonsAroundResistanceToonForReward(self): + radius = 7 + numToons = len(self.involvedToons) + center = (numToons - 1) / 2.0 + for i in xrange(numToons): + toon = self.cr.doId2do.get(self.involvedToons[i]) + if toon: + angle = 90 - 15 * (i - center) + radians = angle * math.pi / 180.0 + x = math.cos(radians) * radius + y = math.sin(radians) * radius + toon.setPos(self.resistanceToon, x, y, 0) + toon.headsUp(self.resistanceToon) + toon.loop('neutral') + toon.show() + + def doDirectedAttack(self, avId, attackCode): + toon = base.cr.doId2do.get(avId) + if toon: + distance = toon.getDistance(self) + gearRoot = self.rotateNode.attachNewNode('gearRoot-atk%d' % self.numAttacks) + gearRoot.setZ(10) + gearRoot.setTag('attackCode', str(attackCode)) + gearModel = self.getGearFrisbee() + gearModel.setScale(0.2) + gearRoot.headsUp(toon) + toToonH = PythonUtil.fitDestAngle2Src(0, gearRoot.getH() + 180) + gearRoot.lookAt(toon) + neutral = 'Fb_neutral' + if not self.twoFaced: + neutral = 'Ff_neutral' + gearTrack = Parallel() + for i in xrange(4): + nodeName = '%s-%s' % (str(i), globalClock.getFrameTime()) + node = gearRoot.attachNewNode(nodeName) + node.hide() + node.setPos(0, 5.85, 4.0) + gear = gearModel.instanceTo(node) + x = random.uniform(-5, 5) + z = random.uniform(-3, 3) + h = random.uniform(-720, 720) + if i == 2: + x = 0 + z = 0 + + def detachNode(node): + if not node.isEmpty(): + node.detachNode() + return Task.done + + def detachNodeLater(node = node): + if node.isEmpty(): + return + center = node.node().getBounds().getCenter() + node.node().setBounds(BoundingSphere(center, distance * 1.5)) + node.node().setFinal(1) + self.doMethodLater(0.005, detachNode, 'detach-%s-%s' % (gearRoot.getName(), node.getName()), extraArgs=[node]) + + gearTrack.append(Sequence(Wait(i * 0.15), Func(node.show), Parallel(node.posInterval(1, Point3(x, distance, z), fluid=1), node.hprInterval(1, VBase3(h, 0, 0), fluid=1)), Func(detachNodeLater))) + + if not self.raised: + neutral1Anim = self.getAnim('down2Up') + self.raised = 1 + else: + neutral1Anim = ActorInterval(self, neutral, startFrame=48) + throwAnim = self.getAnim('throw') + neutral2Anim = ActorInterval(self, neutral) + extraAnim = Sequence() + if attackCode == ToontownGlobals.BossCogSlowDirectedAttack: + extraAnim = ActorInterval(self, neutral) + + def detachGearRoot(task, gearRoot = gearRoot): + if not gearRoot.isEmpty(): + gearRoot.detachNode() + return task.done + + def detachGearRootLater(gearRoot = gearRoot): + if gearRoot.isEmpty(): + return + self.doMethodLater(0.01, detachGearRoot, 'detach-%s' % gearRoot.getName()) + + seq = Sequence(ParallelEndTogether(self.pelvis.hprInterval(1, VBase3(toToonH, 0, 0)), neutral1Anim), extraAnim, Parallel(Sequence(Wait(0.19), gearTrack, Func(detachGearRootLater), self.pelvis.hprInterval(0.2, VBase3(0, 0, 0))), Sequence(Func(self.setChatAbsolute, random.choice(TTLocalizer.DirectedAttackBossTaunts[self.dna.dept]) % {'toon': toon.getName()}, CFSpeech | CFTimeout), throwAnim, neutral2Anim))) + self.doAnimate(seq, now=1, raised=1) + + def setBattleDifficulty(self, diff): + self.notify.debug('battleDifficulty = %d' % diff) + self.battleDifficulty = diff + + def doMoveAttack(self, tableIndex): + self.tableIndex = tableIndex + table = self.tables[tableIndex] + fromPos = self.getPos() + fromHpr = self.getHpr() + toPos = table.getPos() + foo = render.attachNewNode('foo') + foo.setPos(self.getPos()) + foo.setHpr(self.getHpr()) + foo.lookAt(table.getLocator()) + toHpr = foo.getHpr() + toHpr.setX(toHpr.getX() - 180) + foo.removeNode() + reverse = False + moveTrack, hpr = self.moveBossToPoint(fromPos, fromHpr, toPos, toHpr, reverse) + self.moveTrack = moveTrack + self.moveTrack.start() + self.storeInterval(self.moveTrack, 'moveTrack') + + def interruptMove(self): + if self.moveTrack and self.moveTrack.isPlaying(): + self.moveTrack.pause() + self.stopMoveTask() + + def setAttackCode(self, attackCode, avId = 0): + if self.state != 'BattleFour': + return + self.numAttacks += 1 + self.notify.debug('numAttacks=%d' % self.numAttacks) + self.attackCode = attackCode + self.attackAvId = avId + if attackCode == ToontownGlobals.BossCogMoveAttack: + self.interruptMove() + self.doMoveAttack(avId) + elif attackCode == ToontownGlobals.BossCogGolfAttack: + self.interruptMove() + self.cleanupAttacks() + self.doGolfAttack(avId, attackCode) + elif attackCode == ToontownGlobals.BossCogDizzy: + self.setDizzy(1) + self.cleanupAttacks() + self.doAnimate(None, raised=0, happy=1) + elif attackCode == ToontownGlobals.BossCogDizzyNow: + self.setDizzy(1) + self.cleanupAttacks() + self.doAnimate('hit', happy=1, now=1) + elif attackCode == ToontownGlobals.BossCogSwatLeft: + self.setDizzy(0) + self.doAnimate('ltSwing', now=1) + elif attackCode == ToontownGlobals.BossCogSwatRight: + self.setDizzy(0) + self.doAnimate('rtSwing', now=1) + elif attackCode == ToontownGlobals.BossCogAreaAttack: + self.setDizzy(0) + self.doAnimate('areaAttack', now=1) + elif attackCode == ToontownGlobals.BossCogFrontAttack: + self.setDizzy(0) + self.doAnimate('frontAttack', now=1) + elif attackCode == ToontownGlobals.BossCogRecoverDizzyAttack: + self.setDizzy(0) + self.doAnimate('frontAttack', now=1) + elif attackCode == ToontownGlobals.BossCogDirectedAttack or attackCode == ToontownGlobals.BossCogSlowDirectedAttack or attackCode == ToontownGlobals.BossCogGearDirectedAttack: + self.interruptMove() + self.setDizzy(0) + self.doDirectedAttack(avId, attackCode) + elif attackCode == ToontownGlobals.BossCogGolfAreaAttack: + self.interruptMove() + self.setDizzy(0) + self.doGolfAreaAttack() + elif attackCode == ToontownGlobals.BossCogNoAttack: + self.setDizzy(0) + self.doAnimate(None, raised=1) + elif attackCode == ToontownGlobals.BossCogOvertimeAttack: + self.interruptMove() + self.setDizzy(0) + self.cleanupAttacks() + self.doOvertimeAttack(avId) + return + + def signalAtTable(self): + self.sendUpdate('reachedTable', [self.tableIndex]) + + def closeEnter(self, colEntry): + tableStr = colEntry.getIntoNodePath().getNetTag('tableIndex') + if tableStr: + tableIndex = int(tableStr) + self.sendUpdate('hitTable', [tableIndex]) + + def closeExit(self, colEntry): + tableStr = colEntry.getIntoNodePath().getNetTag('tableIndex') + if tableStr: + tableIndex = int(tableStr) + if self.tableIndex != tableIndex: + self.sendUpdate('awayFromTable', [tableIndex]) + + def setSpeedDamage(self, speedDamage, recoverRate, timestamp): + recoverStartTime = globalClockDelta.networkToLocalTime(timestamp) + self.speedDamage = speedDamage + self.speedRecoverRate = recoverRate + self.speedRecoverStartTime = recoverStartTime + speedFraction = max(1 - speedDamage / self.maxSpeedDamage, 0) + self.treads.setColorScale(1, speedFraction, speedFraction, 1) + taskName = 'RecoverSpeedDamage' + taskMgr.remove(taskName) + if self.speedRecoverRate: + taskMgr.add(self.__recoverSpeedDamage, taskName) + + def getSpeedDamage(self): + now = globalClock.getFrameTime() + elapsed = now - self.speedRecoverStartTime + return max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0) + + def getFractionalSpeedDamage(self): + result = self.getSpeedDamage() / self.maxSpeedDamage + return result + + def __recoverSpeedDamage(self, task): + speedDamage = self.getSpeedDamage() + speedFraction = max(1 - speedDamage / self.maxSpeedDamage, 0) + self.treads.setColorScale(1, speedFraction, speedFraction, 1) + return task.cont + + def moveBossToPoint(self, fromPos, fromHpr, toPos, toHpr, reverse): + vector = Vec3(toPos - fromPos) + distance = vector.length() + self.distanceToTravel = distance + self.notify.debug('self.distanceToTravel = %s' % self.distanceToTravel) + if toHpr == None: + mat = Mat3(0, 0, 0, 0, 0, 0, 0, 0, 0) + headsUp(mat, vector, CSDefault) + scale = VBase3(0, 0, 0) + shear = VBase3(0, 0, 0) + toHpr = VBase3(0, 0, 0) + decomposeMatrix(mat, scale, shear, toHpr, CSDefault) + if fromHpr: + newH = PythonUtil.fitDestAngle2Src(fromHpr[0], toHpr[0]) + toHpr = VBase3(newH, 0, 0) + else: + fromHpr = toHpr + turnTime = abs(toHpr[0] - fromHpr[0]) / self.getCurTurnSpeed() + if toHpr[0] < fromHpr[0]: + leftRate = ToontownGlobals.BossCogTreadSpeed + else: + leftRate = -ToontownGlobals.BossCogTreadSpeed + if reverse: + rollTreadRate = -ToontownGlobals.BossCogTreadSpeed + else: + rollTreadRate = ToontownGlobals.BossCogTreadSpeed + rollTime = distance / ToontownGlobals.BossCogRollSpeed + deltaPos = toPos - fromPos + self.toPos = toPos + self.fromPos = fromPos + self.dirVector = self.toPos - self.fromPos + self.dirVector.normalize() + track = Sequence(Func(self.setPos, fromPos), Func(self.headsUp, toPos), Parallel(self.hprInterval(turnTime, toHpr, fromHpr), self.rollLeftTreads(turnTime, leftRate), self.rollRightTreads(turnTime, -leftRate)), Func(self.startMoveTask)) + return (track, toHpr) + + def getCurTurnSpeed(self): + result = ToontownGlobals.BossbotTurnSpeedMax - (ToontownGlobals.BossbotTurnSpeedMax - ToontownGlobals.BossbotTurnSpeedMin) * self.getFractionalSpeedDamage() + return result + + def getCurRollSpeed(self): + result = ToontownGlobals.BossbotRollSpeedMax - (ToontownGlobals.BossbotRollSpeedMax - ToontownGlobals.BossbotRollSpeedMin) * self.getFractionalSpeedDamage() + return result + + def getCurTreadSpeed(self): + result = ToontownGlobals.BossbotTreadSpeedMax - (ToontownGlobals.BossbotTreadSpeedMax - ToontownGlobals.BossbotTreadSpeedMin) * self.getFractionalSpeedDamage() + return result + + def startMoveTask(self): + taskMgr.add(self.moveBossTask, self.moveBossTaskName) + + def stopMoveTask(self): + taskMgr.remove(self.moveBossTaskName) + + def moveBossTask(self, task): + dt = globalClock.getDt() + distanceTravelledThisFrame = dt * self.getCurRollSpeed() + diff = self.toPos - self.getPos() + distanceLeft = diff.length() + + def rollTexMatrix(t, object = object): + object.setTexOffset(TextureStage.getDefault(), t, 0) + + self.treadsLeftPos += dt * self.getCurTreadSpeed() + self.treadsRightPos += dt * self.getCurTreadSpeed() + rollTexMatrix(self.treadsLeftPos, self.treadsLeft) + rollTexMatrix(self.treadsRightPos, self.treadsRight) + if distanceTravelledThisFrame >= distanceLeft: + self.setPos(self.toPos) + self.signalAtTable() + return Task.done + else: + newPos = self.getPos() + self.dirVector * dt * self.getCurRollSpeed() + self.setPos(newPos) + return Task.cont + + def doZapToon(self, toon, pos = None, hpr = None, ts = 0, fling = 1, shake = 1): + zapName = toon.uniqueName('zap') + self.clearInterval(zapName) + zapTrack = Sequence(name=zapName) + if toon == localAvatar: + self.toOuchMode() + messenger.send('interrupt-pie') + self.enableLocalToonSimpleCollisions() + else: + zapTrack.append(Func(toon.stopSmooth)) + + def getSlideToPos(toon = toon): + return render.getRelativePoint(toon, Point3(0, -5, 0)) + + if pos != None and hpr != None: + (zapTrack.append(Func(toon.setPosHpr, pos, hpr)),) + toonTrack = Parallel() + if shake and toon == localAvatar: + toonTrack.append(Sequence(Func(camera.setZ, camera, 1), Wait(0.15), Func(camera.setZ, camera, -2), Wait(0.15), Func(camera.setZ, camera, 1))) + if fling: + if self.isToonRoaming(toon.doId): + toonTrack += [ActorInterval(toon, 'slip-backward')] + toonTrack += [toon.posInterval(0.5, getSlideToPos, fluid=1)] + else: + toonTrack += [ActorInterval(toon, 'slip-forward')] + zapTrack.append(toonTrack) + if toon == localAvatar: + zapTrack.append(Func(self.disableLocalToonSimpleCollisions)) + currentState = self.state + if currentState in ('BattleFour', 'BattleTwo'): + zapTrack.append(Func(self.toFinalBattleMode)) + else: + self.notify.warning('doZapToon going to walkMode, how did this happen?') + zapTrack.append(Func(self.toWalkMode)) + else: + zapTrack.append(Func(toon.startSmooth)) + if ts > 0: + startTime = ts + else: + zapTrack = Sequence(Wait(-ts), zapTrack) + startTime = 0 + zapTrack.append(Func(self.clearInterval, zapName)) + zapTrack.delayDelete = DelayDelete.DelayDelete(toon, 'BossbotBoss.doZapToon') + zapTrack.start(startTime) + self.storeInterval(zapTrack, zapName) + return + + def zapLocalToon(self, attackCode, origin = None): + if self.localToonIsSafe or localAvatar.ghostMode or localAvatar.isStunned: + return + if globalClock.getFrameTime() < self.lastZapLocalTime + 1.0: + return + else: + self.lastZapLocalTime = globalClock.getFrameTime() + self.notify.debug('zapLocalToon frameTime=%s' % globalClock.getFrameTime()) + messenger.send('interrupt-pie') + place = self.cr.playGame.getPlace() + currentState = None + if place: + currentState = place.fsm.getCurrentState().getName() + if currentState != 'walk' and currentState != 'finalBattle' and currentState != 'crane': + return + self.notify.debug('continuing zap') + toon = localAvatar + fling = 1 + shake = 0 + if attackCode == ToontownGlobals.BossCogAreaAttack: + fling = 0 + shake = 1 + if fling: + if origin == None: + origin = self + if self.isToonRoaming(toon.doId): + camera.wrtReparentTo(render) + toon.headsUp(origin) + camera.wrtReparentTo(toon) + bossRelativePos = toon.getPos(self.getGeomNode()) + bp2d = Vec2(bossRelativePos[0], bossRelativePos[1]) + bp2d.normalize() + pos = toon.getPos() + hpr = toon.getHpr() + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('zapToon', [pos[0], + pos[1], + pos[2], + hpr[0], + hpr[1], + hpr[2], + bp2d[0], + bp2d[1], + attackCode, + timestamp]) + self.doZapToon(toon, fling=fling, shake=shake) + return + + def getToonTableIndex(self, toonId): + tableIndex = -1 + for table in self.tables.values(): + if table.avId == toonId: + tableIndex = table.index + break + + return tableIndex + + def getToonGolfSpotIndex(self, toonId): + golfSpotIndex = -1 + for golfSpot in self.golfSpots.values(): + if golfSpot.avId == toonId: + golfSpotIndex = golfSpot.index + break + + return golfSpotIndex + + def isToonOnTable(self, toonId): + result = self.getToonTableIndex(toonId) != -1 + return result + + def isToonOnGolfSpot(self, toonId): + result = self.getToonGolfSpotIndex(toonId) != -1 + return result + + def isToonRoaming(self, toonId): + result = not self.isToonOnTable(toonId) and not self.isToonOnGolfSpot(toonId) + return result + + def getGolfBall(self): + golfRoot = NodePath('golfRoot') + golfBall = loader.loadModel('phase_6/models/golf/golf_ball') + golfBall.setColorScale(0.75, 0.75, 0.75, 0.5) + golfBall.setTransparency(1) + ballScale = 5 + golfBall.setScale(ballScale) + golfBall.reparentTo(golfRoot) + cs = CollisionSphere(0, 0, 0, ballScale * 0.25) + cs.setTangible(0) + cn = CollisionNode('BossZap') + cn.addSolid(cs) + cn.setIntoCollideMask(ToontownGlobals.WallBitmask) + cnp = golfRoot.attachNewNode(cn) + return golfRoot + + def doGolfAttack(self, avId, attackCode): + toon = base.cr.doId2do.get(avId) + if toon: + distance = toon.getDistance(self) + self.notify.debug('distance = %s' % distance) + gearRoot = self.rotateNode.attachNewNode('gearRoot-atk%d' % self.numAttacks) + gearRoot.setZ(10) + gearRoot.setTag('attackCode', str(attackCode)) + gearModel = self.getGolfBall() + self.ballLaunch = NodePath('') + self.ballLaunch.reparentTo(gearRoot) + self.ballLaunch.setPos(self.BallLaunchOffset) + gearRoot.headsUp(toon) + toToonH = PythonUtil.fitDestAngle2Src(0, gearRoot.getH() + 180) + gearRoot.lookAt(toon) + neutral = 'Fb_neutral' + if not self.twoFaced: + neutral = 'Ff_neutral' + gearTrack = Parallel() + for i in xrange(5): + nodeName = '%s-%s' % (str(i), globalClock.getFrameTime()) + node = gearRoot.attachNewNode(nodeName) + node.hide() + node.reparentTo(self.ballLaunch) + node.wrtReparentTo(gearRoot) + distance = toon.getDistance(node) + gear = gearModel.instanceTo(node) + x = random.uniform(-5, 5) + z = random.uniform(-3, 3) + p = random.uniform(-720, -90) + y = distance + random.uniform(5, 15) + if i == 2: + x = 0 + z = 0 + y = distance + 10 + + def detachNode(node): + if not node.isEmpty(): + node.detachNode() + return Task.done + + def detachNodeLater(node = node): + if node.isEmpty(): + return + node.node().setBounds(BoundingSphere(Point3(0, 0, 0), distance * 1.5)) + node.node().setFinal(1) + self.doMethodLater(0.005, detachNode, 'detach-%s-%s' % (gearRoot.getName(), node.getName()), extraArgs=[node]) + + gearTrack.append(Sequence(Wait(26.0 / 24.0), Wait(i * 0.15), Func(node.show), Parallel(node.posInterval(1, Point3(x, y, z), fluid=1), node.hprInterval(1, VBase3(0, p, 0), fluid=1)), Func(detachNodeLater))) + + if not self.raised: + neutral1Anim = self.getAnim('down2Up') + self.raised = 1 + else: + neutral1Anim = ActorInterval(self, neutral, startFrame=48) + throwAnim = self.getAnim('golf_swing') + neutral2Anim = ActorInterval(self, neutral) + extraAnim = Sequence() + if attackCode == ToontownGlobals.BossCogSlowDirectedAttack: + extraAnim = ActorInterval(self, neutral) + + def detachGearRoot(task, gearRoot = gearRoot): + if not gearRoot.isEmpty(): + gearRoot.detachNode() + return task.done + + def detachGearRootLater(gearRoot = gearRoot): + self.doMethodLater(0.01, detachGearRoot, 'detach-%s' % gearRoot.getName()) + + seq = Sequence(ParallelEndTogether(self.pelvis.hprInterval(1, VBase3(toToonH, 0, 0)), neutral1Anim), extraAnim, Parallel(Sequence(Wait(0.19), gearTrack, Func(detachGearRootLater), self.pelvis.hprInterval(0.2, VBase3(0, 0, 0))), Sequence(Func(self.setChatAbsolute, random.choice(TTLocalizer.DirectedAttackBossTaunts[self.dna.dept]) % {'toon': toon.getName()}, CFSpeech | CFTimeout), throwAnim, neutral2Anim), Sequence(Wait(0.85), SoundInterval(self.swingClubSfx, node=self, duration=0.45, cutOff=300, listenerNode=base.localAvatar)))) + self.doAnimate(seq, now=1, raised=1) + + def doGolfAreaAttack(self): + toons = [] + for toonId in self.involvedToons: + toon = base.cr.doId2do.get(toonId) + if toon: + toons.append(toon) + + if not toons: + return + neutral = 'Fb_neutral' + if not self.twoFaced: + neutral = 'Ff_neutral' + if not self.raised: + neutral1Anim = self.getAnim('down2Up') + self.raised = 1 + else: + neutral1Anim = ActorInterval(self, neutral, startFrame=48) + throwAnim = self.getAnim('golf_swing') + neutral2Anim = ActorInterval(self, neutral) + extraAnim = Sequence() + if False: + extraAnim = ActorInterval(self, neutral) + gearModel = self.getGolfBall() + toToonH = self.rotateNode.getH() + 360 + self.notify.debug('toToonH = %s' % toToonH) + gearRoots = [] + allGearTracks = Parallel() + for toon in toons: + gearRoot = self.rotateNode.attachNewNode('gearRoot-atk%d-%d' % (self.numAttacks, toons.index(toon))) + gearRoot.setZ(10) + gearRoot.setTag('attackCode', str(ToontownGlobals.BossCogGolfAreaAttack)) + gearRoot.lookAt(toon) + ballLaunch = NodePath('') + ballLaunch.reparentTo(gearRoot) + ballLaunch.setPos(self.BallLaunchOffset) + gearTrack = Parallel() + for i in xrange(5): + nodeName = '%s-%s' % (str(i), globalClock.getFrameTime()) + node = gearRoot.attachNewNode(nodeName) + node.hide() + node.reparentTo(ballLaunch) + node.wrtReparentTo(gearRoot) + distance = toon.getDistance(node) + toonPos = toon.getPos(render) + nodePos = node.getPos(render) + vector = toonPos - nodePos + gear = gearModel.instanceTo(node) + x = random.uniform(-5, 5) + z = random.uniform(-3, 3) + p = random.uniform(-720, -90) + y = distance + random.uniform(5, 15) + if i == 2: + x = 0 + z = 0 + y = distance + 10 + + def detachNode(node): + if not node.isEmpty(): + node.detachNode() + return Task.done + + def detachNodeLater(node = node): + if node.isEmpty(): + return + node.node().setBounds(BoundingSphere(Point3(0, 0, 0), distance * 1.5)) + node.node().setFinal(1) + self.doMethodLater(0.005, detachNode, 'detach-%s-%s' % (gearRoot.getName(), node.getName()), extraArgs=[node]) + + gearTrack.append(Sequence(Wait(26.0 / 24.0), Wait(i * 0.15), Func(node.show), Parallel(node.posInterval(1, Point3(x, y, z), fluid=1), node.hprInterval(1, VBase3(0, p, 0), fluid=1)), Func(detachNodeLater))) + + allGearTracks.append(gearTrack) + + def detachGearRoots(gearRoots = gearRoots): + for gearRoot in gearRoots: + + def detachGearRoot(task, gearRoot = gearRoot): + if not gearRoot.isEmpty(): + gearRoot.detachNode() + return task.done + + if gearRoot.isEmpty(): + continue + self.doMethodLater(0.01, detachGearRoot, 'detach-%s' % gearRoot.getName()) + + gearRoots = [] + + rotateFire = Parallel(self.pelvis.hprInterval(2, VBase3(toToonH + 1440, 0, 0)), allGearTracks) + seq = Sequence(Func(base.playSfx, self.warningSfx), Func(self.saySomething, TTLocalizer.GolfAreaAttackTaunt), ParallelEndTogether(self.pelvis.hprInterval(2, VBase3(toToonH, 0, 0)), neutral1Anim), extraAnim, Parallel(Sequence(rotateFire, Func(detachGearRoots), Func(self.pelvis.setHpr, VBase3(0, 0, 0))), Sequence(throwAnim, neutral2Anim), Sequence(Wait(0.85), SoundInterval(self.swingClubSfx, node=self, duration=0.45, cutOff=300, listenerNode=base.localAvatar)))) + self.doAnimate(seq, now=1, raised=1) + + def saySomething(self, chatString): + intervalName = 'CEOTaunt' + seq = Sequence(name=intervalName) + seq.append(Func(self.setChatAbsolute, chatString, CFSpeech)) + seq.append(Wait(4.0)) + seq.append(Func(self.clearChat)) + oldSeq = self.activeIntervals.get(intervalName) + if oldSeq: + oldSeq.finish() + seq.start() + self.activeIntervals[intervalName] = seq + + def d_hitToon(self, toonId): + self.notify.debug('----- d_hitToon') + self.sendUpdate('hitToon', [toonId]) + + def toonGotHealed(self, toonId): + toon = base.cr.doId2do.get(toonId) + if toon: + base.playSfx(self.toonUpSfx, node=toon) + + def localToonTouchedBeltToonup(self, beltIndex, toonupIndex, toonupNum): + avId = base.localAvatar.doId + doRequest = True + if doRequest: + self.sendUpdate('requestGetToonup', [beltIndex, toonupIndex, toonupNum]) + + def toonGotToonup(self, avId, beltIndex, toonupIndex, toonupNum): + if self.belts[beltIndex]: + self.belts[beltIndex].removeToonup(toonupIndex) + toon = base.cr.doId2do.get(avId) + if toon: + base.playSfx(self.toonUpSfx, node=toon) + + def doOvertimeAttack(self, index): + attackCode = ToontownGlobals.BossCogOvertimeAttack + attackBelts = Sequence() + if index < len(self.belts): + belt = self.belts[index] + self.saySomething(TTLocalizer.OvertimeAttackTaunts[index]) + if index: + self.bossClubIntervals[0].finish() + self.bossClubIntervals[1].loop() + else: + self.bossClubIntervals[1].finish() + self.bossClubIntervals[0].loop() + distance = belt.beltModel.getDistance(self) + gearRoot = self.rotateNode.attachNewNode('gearRoot') + gearRoot.setZ(10) + gearRoot.setTag('attackCode', str(attackCode)) + gearModel = self.getGearFrisbee() + gearModel.setScale(0.2) + gearRoot.headsUp(belt.beltModel) + toToonH = PythonUtil.fitDestAngle2Src(0, gearRoot.getH() + 180) + gearRoot.lookAt(belt.beltModel) + neutral = 'Fb_neutral' + if not self.twoFaced: + neutral = 'Ff_neutral' + gearTrack = Parallel() + for i in xrange(4): + node = gearRoot.attachNewNode(str(i)) + node.hide() + node.setPos(0, 5.85, 4.0) + gear = gearModel.instanceTo(node) + x = random.uniform(-5, 5) + z = random.uniform(-3, 3) + h = random.uniform(-720, 720) + gearTrack.append(Sequence(Wait(i * 0.15), Func(node.show), Parallel(node.posInterval(1, Point3(x, distance, z), fluid=1), node.hprInterval(1, VBase3(h, 0, 0), fluid=1)), Func(node.detachNode))) + + if not self.raised: + neutral1Anim = self.getAnim('down2Up') + self.raised = 1 + else: + neutral1Anim = ActorInterval(self, neutral, startFrame=48) + throwAnim = self.getAnim('throw') + neutral2Anim = ActorInterval(self, neutral) + extraAnim = Sequence() + if attackCode == ToontownGlobals.BossCogSlowDirectedAttack: + extraAnim = ActorInterval(self, neutral) + seq = Sequence(ParallelEndTogether(self.pelvis.hprInterval(1, VBase3(toToonH, 0, 0)), neutral1Anim), extraAnim, Parallel(Sequence(Wait(0.19), gearTrack, Func(gearRoot.detachNode), Func(self.explodeSfx.play), self.pelvis.hprInterval(0.2, VBase3(0, 0, 0))), Sequence(throwAnim, neutral2Anim)), Func(belt.request, 'Inactive')) + attackBelts.append(seq) + self.notify.debug('attackBelts duration= %.2f' % attackBelts.getDuration()) + self.doAnimate(attackBelts, now=1, raised=1) diff --git a/toontown/suit/DistributedBossbotBossAI.py b/toontown/suit/DistributedBossbotBossAI.py new file mode 100755 index 00000000..c90dd4f1 --- /dev/null +++ b/toontown/suit/DistributedBossbotBossAI.py @@ -0,0 +1,971 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import globalClockDelta +from direct.fsm import FSM +from direct.interval.IntervalGlobal import LerpPosInterval +import math +from pandac.PandaModules import Point3 +import random + +from otp.ai.MagicWordGlobal import * +from toontown.battle import BattleExperienceAI +from toontown.battle import DistributedBattleDinersAI +from toontown.battle import DistributedBattleWaitersAI +from toontown.building import SuitBuildingGlobals +from toontown.coghq import DistributedBanquetTableAI +from toontown.coghq import DistributedFoodBeltAI +from toontown.coghq import DistributedGolfSpotAI +from toontown.suit import DistributedBossCogAI +from toontown.suit import DistributedSuitAI +from toontown.suit import SuitDNA +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import ToontownGlobals + + +class DistributedBossbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBossbotBossAI') + maxToonLevels = 77 + toonUpLevels = [1, + 2, + 3, + 4] + BossName = "CEO" + + def __init__(self, air): + DistributedBossCogAI.DistributedBossCogAI.__init__(self, air, 'c') + FSM.FSM.__init__(self, 'DistributedBossbotBossAI') + self.battleOneBattlesMade = False + self.battleThreeBattlesMade = False + self.battleFourSetup = False + self.foodBelts = [] + self.numTables = 1 + self.numDinersPerTable = 3 + self.tables = [] + self.numGolfSpots = 4 + self.golfSpots = [] + self.toonFoodStatus = {} + self.bossMaxDamage = ToontownGlobals.BossbotBossMaxDamage + self.threatDict = {} + self.keyStates.append('BattleFour') + self.battleFourStart = 0 + self.battleDifficulty = 0 + self.movingToTable = False + self.tableDest = -1 + self.curTable = -1 + self.speedDamage = 0 + self.maxSpeedDamage = ToontownGlobals.BossbotMaxSpeedDamage + self.speedRecoverRate = ToontownGlobals.BossbotSpeedRecoverRate + self.speedRecoverStartTime = 0 + self.battleFourTimeStarted = 0 + self.numDinersExploded = 0 + self.numMoveAttacks = 0 + self.numGolfAttacks = 0 + self.numGearAttacks = 0 + self.numGolfAreaAttacks = 0 + self.numToonupGranted = 0 + self.totalLaffHealed = 0 + self.toonupsGranted = [] + self.doneOvertimeOneAttack = False + self.doneOvertimeTwoAttack = False + self.overtimeOneTime = simbase.air.config.GetInt('overtime-one-time', 1200) + self.battleFourDuration = simbase.air.config.GetInt('battle-four-duration', 1800) + self.overtimeOneStart = float(self.overtimeOneTime) / self.battleFourDuration + self.moveAttackAllowed = True + + def delete(self): + self.notify.debug('DistributedBossbotBossAI.delete') + self.deleteBanquetTables() + self.deleteFoodBelts() + self.deleteGolfSpots() + return DistributedBossCogAI.DistributedBossCogAI.delete(self) + + def enterElevator(self): + DistributedBossCogAI.DistributedBossCogAI.enterElevator(self) + self.makeBattleOneBattles() + + def enterIntroduction(self): + self.arenaSide = None + self.makeBattleOneBattles() + self.barrier = self.beginBarrier('Introduction', self.involvedToons, 45, self.doneIntroduction) + return + + def makeBattleOneBattles(self): + if not self.battleOneBattlesMade: + self.postBattleState = 'PrepareBattleTwo' + self.initializeBattles(1, ToontownGlobals.BossbotBossBattleOnePosHpr) + self.battleOneBattlesMade = True + + def getHoodId(self): + return ToontownGlobals.LawbotHQ + + def generateSuits(self, battleNumber): + if battleNumber == 1: + weakenedValue = ((1, 1), + (2, 2), + (2, 2), + (1, 1), + (1, 1, 1, 1, 1)) + listVersion = list(SuitBuildingGlobals.SuitBuildingInfo) + if simbase.config.GetBool('bossbot-boss-cheat', 0): + listVersion[14] = weakenedValue + SuitBuildingGlobals.SuitBuildingInfo = tuple(listVersion) + retval = self.invokeSuitPlanner(14, 0) + return retval + else: + suits = self.generateDinerSuits() + return suits + + def invokeSuitPlanner(self, buildingCode, skelecog): + suits = DistributedBossCogAI.DistributedBossCogAI.invokeSuitPlanner(self, buildingCode, skelecog) + activeSuits = suits['activeSuits'][:] + reserveSuits = suits['reserveSuits'][:] + if len(activeSuits) + len(reserveSuits) >= 4: + while len(activeSuits) < 4: + activeSuits.append(reserveSuits.pop()[0]) + + retval = {'activeSuits': activeSuits, + 'reserveSuits': reserveSuits} + return retval + + def makeBattle(self, bossCogPosHpr, battlePosHpr, roundCallback, finishCallback, battleNumber, battleSide): + if battleNumber == 1: + battle = DistributedBattleWaitersAI.DistributedBattleWaitersAI(self.air, self, roundCallback, finishCallback, battleSide) + else: + battle = DistributedBattleDinersAI.DistributedBattleDinersAI(self.air, self, roundCallback, finishCallback, battleSide) + self.setBattlePos(battle, bossCogPosHpr, battlePosHpr) + battle.suitsKilled = self.suitsKilled + battle.battleCalc.toonSkillPtsGained = self.toonSkillPtsGained + battle.toonExp = self.toonExp + battle.toonOrigQuests = self.toonOrigQuests + battle.toonItems = self.toonItems + battle.toonOrigMerits = self.toonOrigMerits + battle.toonMerits = self.toonMerits + battle.toonParts = self.toonParts + battle.helpfulToons = self.helpfulToons + mult = ToontownBattleGlobals.getBossBattleCreditMultiplier(battleNumber) + battle.battleCalc.setSkillCreditMultiplier(mult) + activeSuits = self.activeSuitsA + if battleSide: + activeSuits = self.activeSuitsB + for suit in activeSuits: + battle.addSuit(suit) + + battle.generateWithRequired(self.zoneId) + return battle + + def initializeBattles(self, battleNumber, bossCogPosHpr): + self.resetBattles() + if not self.involvedToons: + self.notify.warning('initializeBattles: no toons!') + return + self.battleNumber = battleNumber + suitHandles = self.generateSuits(battleNumber) + self.suitsA = suitHandles['activeSuits'] + self.activeSuitsA = self.suitsA[:] + self.reserveSuits = suitHandles['reserveSuits'] + if battleNumber == 3: + if self.toonsB: + movedSuit = self.suitsA.pop() + self.suitsB = [movedSuit] + self.activeSuitsB = [movedSuit] + self.activeSuitsA.remove(movedSuit) + else: + self.suitsB = [] + self.activeSuitsB = [] + else: + suitHandles = self.generateSuits(battleNumber) + self.suitsB = suitHandles['activeSuits'] + self.activeSuitsB = self.suitsB[:] + self.reserveSuits += suitHandles['reserveSuits'] + if self.toonsA: + if battleNumber == 1: + self.battleA = self.makeBattle(bossCogPosHpr, ToontownGlobals.WaiterBattleAPosHpr, self.handleRoundADone, self.handleBattleADone, battleNumber, 0) + self.battleAId = self.battleA.doId + else: + self.battleA = self.makeBattle(bossCogPosHpr, ToontownGlobals.DinerBattleAPosHpr, self.handleRoundADone, self.handleBattleADone, battleNumber, 0) + self.battleAId = self.battleA.doId + else: + self.moveSuits(self.activeSuitsA) + self.suitsA = [] + self.activeSuitsA = [] + if self.arenaSide == None: + self.b_setArenaSide(0) + if self.toonsB: + if battleNumber == 1: + self.battleB = self.makeBattle(bossCogPosHpr, ToontownGlobals.WaiterBattleBPosHpr, self.handleRoundBDone, self.handleBattleBDone, battleNumber, 1) + self.battleBId = self.battleB.doId + else: + self.battleB = self.makeBattle(bossCogPosHpr, ToontownGlobals.DinerBattleBPosHpr, self.handleRoundBDone, self.handleBattleBDone, battleNumber, 1) + self.battleBId = self.battleB.doId + else: + self.moveSuits(self.activeSuitsB) + self.suitsB = [] + self.activeSuitsB = [] + if self.arenaSide == None: + self.b_setArenaSide(1) + self.sendBattleIds() + return + + def enterPrepareBattleTwo(self): + self.barrier = self.beginBarrier('PrepareBattleTwo', self.involvedToons, 45, self.__donePrepareBattleTwo) + self.createFoodBelts() + self.createBanquetTables() + + def __donePrepareBattleTwo(self, avIds): + self.b_setState('BattleTwo') + + def exitPrepareBattleTwo(self): + self.ignoreBarrier(self.barrier) + + def createFoodBelts(self): + if self.foodBelts: + return + for i in xrange(2): + newBelt = DistributedFoodBeltAI.DistributedFoodBeltAI(self.air, self, i) + self.foodBelts.append(newBelt) + newBelt.generateWithRequired(self.zoneId) + + def deleteFoodBelts(self): + for belt in self.foodBelts: + belt.requestDelete() + + self.foodBelts = [] + + def createBanquetTables(self): + if self.tables: + return + self.calcAndSetBattleDifficulty() + diffInfo = ToontownGlobals.BossbotBossDifficultySettings[self.battleDifficulty] + self.diffInfo = diffInfo + self.numTables = diffInfo[0] + self.numDinersPerTable = diffInfo[1] + dinerLevel = diffInfo[2] + for i in xrange(self.numTables): + newTable = DistributedBanquetTableAI.DistributedBanquetTableAI(self.air, self, i, self.numDinersPerTable, dinerLevel) + self.tables.append(newTable) + newTable.generateWithRequired(self.zoneId) + + def deleteBanquetTables(self): + for table in self.tables: + table.requestDelete() + + self.tables = [] + + def enterBattleTwo(self): + self.resetBattles() + self.createFoodBelts() + self.createBanquetTables() + for belt in self.foodBelts: + belt.turnOn() + + for table in self.tables: + table.turnOn() + + self.barrier = self.beginBarrier('BattleTwo', self.involvedToons, ToontownGlobals.BossbotBossServingDuration + 1, self.__doneBattleTwo) + + def exitBattleTwo(self): + self.ignoreBarrier(self.barrier) + for table in self.tables: + table.goInactive() + + for belt in self.foodBelts: + belt.goInactive() + + def __doneBattleTwo(self, avIds): + self.b_setState('PrepareBattleThree') + + def requestGetFood(self, beltIndex, foodIndex, foodNum): + grantRequest = False + avId = self.air.getAvatarIdFromSender() + if self.state != 'BattleTwo': + grantRequest = False + elif (beltIndex, foodNum) not in self.toonFoodStatus.values(): + if avId not in self.toonFoodStatus: + grantRequest = True + elif self.toonFoodStatus[avId] == None: + grantRequest = True + if grantRequest: + self.toonFoodStatus[avId] = (beltIndex, foodNum) + self.sendUpdate('toonGotFood', [avId, + beltIndex, + foodIndex, + foodNum]) + return + + def requestServeFood(self, tableIndex, chairIndex): + grantRequest = False + avId = self.air.getAvatarIdFromSender() + if self.state != 'BattleTwo': + grantRequest = False + elif tableIndex < len(self.tables): + table = self.tables[tableIndex] + dinerStatus = table.getDinerStatus(chairIndex) + if dinerStatus in (table.HUNGRY, table.ANGRY): + if self.toonFoodStatus[avId]: + grantRequest = True + if grantRequest: + self.toonFoodStatus[avId] = None + table.foodServed(chairIndex) + self.sendUpdate('toonServeFood', [avId, tableIndex, chairIndex]) + return + + def enterPrepareBattleThree(self): + self.barrier = self.beginBarrier('PrepareBattleThree', self.involvedToons, ToontownGlobals.BossbotBossServingDuration + 1, self.__donePrepareBattleThree) + self.divideToons() + self.makeBattleThreeBattles() + + def exitPrepareBattleThree(self): + self.ignoreBarrier(self.barrier) + + def __donePrepareBattleThree(self, avIds): + self.b_setState('BattleThree') + + def makeBattleThreeBattles(self): + if not self.battleThreeBattlesMade: + if not self.tables: + self.createBanquetTables() + for table in self.tables: + table.turnOn() + table.goInactive() + + notDeadList = [] + for table in self.tables: + tableInfo = table.getNotDeadInfo() + notDeadList += tableInfo + + self.notDeadList = notDeadList + self.postBattleState = 'PrepareBattleFour' + self.initializeBattles(3, ToontownGlobals.BossbotBossBattleThreePosHpr) + self.battleThreeBattlesMade = True + + def generateDinerSuits(self): + diners = [] + for i in xrange(len(self.notDeadList)): + if simbase.config.GetBool('bossbot-boss-cheat', 0): + suit = self.__genSuitObject(self.zoneId, 2, 'c', 2, 0) + else: + info = self.notDeadList[i] + suitType = info[2] - 4 + suitLevel = info[2] + suit = self.__genSuitObject(self.zoneId, suitType, 'c', suitLevel, 1) + diners.append((suit, 100)) + + active = [] + for i in xrange(2): + if simbase.config.GetBool('bossbot-boss-cheat', 0): + suit = self.__genSuitObject(self.zoneId, 2, 'c', 2, 0) + else: + suitType = 8 + suitLevel = 12 + suit = self.__genSuitObject(self.zoneId, suitType, 'c', suitLevel, 1) + active.append(suit) + + return {'activeSuits': active, + 'reserveSuits': diners} + + def __genSuitObject(self, suitZone, suitType, bldgTrack, suitLevel, revives = 0): + newSuit = DistributedSuitAI.DistributedSuitAI(simbase.air, None) + skel = self.__setupSuitInfo(newSuit, bldgTrack, suitLevel, suitType) + if skel: + newSuit.setSkelecog(1) + newSuit.setSkeleRevives(revives) + newSuit.generateWithRequired(suitZone) + newSuit.node().setName('suit-%s' % newSuit.doId) + return newSuit + + def __setupSuitInfo(self, suit, bldgTrack, suitLevel, suitType): + dna = SuitDNA.SuitDNA() + dna.newSuitRandom(suitType, bldgTrack) + suit.dna = dna + self.notify.debug('Creating suit type ' + suit.dna.name + ' of level ' + str(suitLevel) + ' from type ' + str(suitType) + ' and track ' + str(bldgTrack)) + suit.setLevel(suitLevel) + return False + + def enterBattleThree(self): + self.makeBattleThreeBattles() + self.notify.debug('self.battleA = %s' % self.battleA) + if self.battleA: + self.battleA.startBattle(self.toonsA, self.suitsA) + if self.battleB: + self.battleB.startBattle(self.toonsB, self.suitsB) + + def exitBattleThree(self): + self.resetBattles() + + def enterPrepareBattleFour(self): + self.resetBattles() + self.setupBattleFourObjects() + self.barrier = self.beginBarrier('PrepareBattleFour', self.involvedToons, 45, self.__donePrepareBattleFour) + + def __donePrepareBattleFour(self, avIds): + self.b_setState('BattleFour') + + def exitPrepareBattleFour(self): + self.ignoreBarrier(self.barrier) + + def enterBattleFour(self): + self.battleFourTimeStarted = globalClock.getFrameTime() + self.numToonsAtStart = len(self.involvedToons) + self.resetBattles() + self.setupBattleFourObjects() + self.battleFourStart = globalClock.getFrameTime() + self.waitForNextAttack(5) + + def exitBattleFour(self): + self.recordCeoInfo() + for belt in self.foodBelts: + belt.goInactive() + + def recordCeoInfo(self): + didTheyWin = 0 + if self.bossDamage == self.bossMaxDamage: + didTheyWin = 1 + self.battleFourTimeInMin = globalClock.getFrameTime() - self.battleFourTimeStarted + self.battleFourTimeInMin /= 60.0 + self.numToonsAtEnd = 0 + toonHps = [] + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + self.numToonsAtEnd += 1 + toonHps.append(toon.hp) + + self.air.writeServerEvent('ceoInfo', self.doId, '%d|%.2f|%d|%d|%d|%d|%d|%d|%s|%s|%.1f|%d|%d|%d|%d|%d}%d|%s|' % (didTheyWin, + self.battleFourTimeInMin, + self.battleDifficulty, + self.numToonsAtStart, + self.numToonsAtEnd, + self.numTables, + self.numTables * self.numDinersPerTable, + self.numDinersExploded, + toonHps, + self.involvedToons, + self.speedDamage, + self.numMoveAttacks, + self.numGolfAttacks, + self.numGearAttacks, + self.numGolfAreaAttacks, + self.numToonupGranted, + self.totalLaffHealed, + 'ceoBugfixes')) + + def setupBattleFourObjects(self): + if self.battleFourSetup: + return + if not self.tables: + self.createBanquetTables() + for table in self.tables: + table.goFree() + + if not self.golfSpots: + self.createGolfSpots() + self.createFoodBelts() + for belt in self.foodBelts: + belt.goToonup() + + self.battleFourSetup = True + + def hitBoss(self, bossDamage): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'hitBoss from unknown avatar'): + return + self.validate(avId, bossDamage <= 3, 'invalid bossDamage %s' % bossDamage) + if bossDamage < 1: + return + currState = self.getCurrentOrNextState() + if currState != 'BattleFour': + return + bossDamage *= 2 + bossDamage = min(self.getBossDamage() + bossDamage, self.bossMaxDamage) + self.b_setBossDamage(bossDamage, 0, 0) + if self.bossDamage >= self.bossMaxDamage: + self.b_setState('Victory') + else: + self.__recordHit(bossDamage) + + def __recordHit(self, bossDamage): + now = globalClock.getFrameTime() + self.hitCount += 1 + avId = self.air.getAvatarIdFromSender() + self.addThreat(avId, bossDamage) + + def getBossDamage(self): + return self.bossDamage + + def b_setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + self.d_setBossDamage(bossDamage, recoverRate, recoverStartTime) + self.setBossDamage(bossDamage, recoverRate, recoverStartTime) + + def setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + self.bossDamage = bossDamage + self.recoverRate = recoverRate + self.recoverStartTime = recoverStartTime + + def d_setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + timestamp = globalClockDelta.localToNetworkTime(recoverStartTime) + self.sendUpdate('setBossDamage', [bossDamage, recoverRate, timestamp]) + + def getSpeedDamage(self): + now = globalClock.getFrameTime() + elapsed = now - self.speedRecoverStartTime + self.notify.debug('elapsed=%s' % elapsed) + floatSpeedDamage = max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0) + self.notify.debug('floatSpeedDamage = %s' % floatSpeedDamage) + return int(max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0)) + + def getFloatSpeedDamage(self): + now = globalClock.getFrameTime() + elapsed = now - self.speedRecoverStartTime + floatSpeedDamage = max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0) + self.notify.debug('floatSpeedDamage = %s' % floatSpeedDamage) + return max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0) + + def b_setSpeedDamage(self, speedDamage, recoverRate, recoverStartTime): + self.d_setSpeedDamage(speedDamage, recoverRate, recoverStartTime) + self.setSpeedDamage(speedDamage, recoverRate, recoverStartTime) + + def setSpeedDamage(self, speedDamage, recoverRate, recoverStartTime): + self.speedDamage = speedDamage + self.speedRecoverRate = recoverRate + self.speedRecoverStartTime = recoverStartTime + + def d_setSpeedDamage(self, speedDamage, recoverRate, recoverStartTime): + timestamp = globalClockDelta.localToNetworkTime(recoverStartTime) + self.sendUpdate('setSpeedDamage', [speedDamage, recoverRate, timestamp]) + + def createGolfSpots(self): + if self.golfSpots: + return + for i in xrange(self.numGolfSpots): + newGolfSpot = DistributedGolfSpotAI.DistributedGolfSpotAI(self.air, self, i) + self.golfSpots.append(newGolfSpot) + newGolfSpot.generateWithRequired(self.zoneId) + newGolfSpot.forceFree() + + def deleteGolfSpots(self): + for spot in self.golfSpots: + spot.requestDelete() + + self.golfSpots = [] + + def ballHitBoss(self, speedDamage): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'hitBoss from unknown avatar'): + return + if speedDamage < 1: + return + currState = self.getCurrentOrNextState() + if currState != 'BattleFour': + return + now = globalClock.getFrameTime() + newDamage = self.getSpeedDamage() + speedDamage + self.notify.debug('newDamage = %s' % newDamage) + speedDamage = min(self.getFloatSpeedDamage() + speedDamage, self.maxSpeedDamage) + self.b_setSpeedDamage(speedDamage, self.speedRecoverRate, now) + self.addThreat(avId, 0.1) + + def enterVictory(self): + self.resetBattles() + for table in self.tables: + table.turnOff() + + for golfSpot in self.golfSpots: + golfSpot.turnOff() + + self.suitsKilled.append({'type': None, + 'level': None, + 'track': self.dna.dept, + 'isSkelecog': 0, + 'isForeman': 0, + 'isBoss': 1, + 'isSupervisor': 0, + 'isVirtual': 0, + 'activeToons': self.involvedToons[:]}) + self.addStats() + self.barrier = self.beginBarrier('Victory', self.involvedToons, 30, self.__doneVictory) + return + + def __doneVictory(self, avIds): + self.d_setBattleExperience() + self.b_setState('Reward') + BattleExperienceAI.assignRewards(self.involvedToons, self.toonSkillPtsGained, self.suitsKilled, ToontownGlobals.dept2cogHQ(self.dept), self.helpfulToons) + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + self.givePinkSlipReward(toon) + toon.b_promote(self.deptIndex) + + def givePinkSlipReward(self, toon): + toon.addPinkSlips(self.battleDifficulty + 1) + + def getThreat(self, toonId): + if toonId in self.threatDict: + return self.threatDict[toonId] + else: + return 0 + + def addThreat(self, toonId, threat): + if toonId in self.threatDict: + self.threatDict[toonId] += threat + else: + self.threatDict[toonId] = threat + + def subtractThreat(self, toonId, threat): + if toonId in self.threatDict: + self.threatDict[toonId] -= threat + else: + self.threatDict[toonId] = 0 + if self.threatDict[toonId] < 0: + self.threatDict[toonId] = 0 + + def waitForNextAttack(self, delayTime): + currState = self.getCurrentOrNextState() + if currState == 'BattleFour': + taskName = self.uniqueName('NextAttack') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.doNextAttack, taskName) + + def doNextAttack(self, task): + attackCode = -1 + optionalParam = None + if self.movingToTable: + self.waitForNextAttack(5) + elif self.attackCode == ToontownGlobals.BossCogDizzyNow: + attackCode = ToontownGlobals.BossCogRecoverDizzyAttack + elif self.getBattleFourTime() > self.overtimeOneStart and not self.doneOvertimeOneAttack: + attackCode = ToontownGlobals.BossCogOvertimeAttack + self.doneOvertimeOneAttack = True + optionalParam = 0 + elif self.getBattleFourTime() > 1.0 and not self.doneOvertimeTwoAttack: + attackCode = ToontownGlobals.BossCogOvertimeAttack + self.doneOvertimeTwoAttack = True + optionalParam = 1 + else: + attackCode = random.choice([ToontownGlobals.BossCogGolfAreaAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack]) + if attackCode == ToontownGlobals.BossCogAreaAttack: + self.__doAreaAttack() + if attackCode == ToontownGlobals.BossCogGolfAreaAttack: + self.__doGolfAreaAttack() + elif attackCode == ToontownGlobals.BossCogDirectedAttack: + self.__doDirectedAttack() + elif attackCode >= 0: + self.b_setAttackCode(attackCode, optionalParam) + return + + def progressValue(self, fromValue, toValue): + t0 = float(self.bossDamage) / float(self.bossMaxDamage) + elapsed = globalClock.getFrameTime() - self.battleFourStart + t1 = elapsed / float(self.battleThreeDuration) + t = max(t0, t1) + progVal = fromValue + (toValue - fromValue) * min(t, 1) + self.notify.debug('progVal=%s' % progVal) + return progVal + + def __doDirectedAttack(self): + toonId = self.getMaxThreatToon() + self.notify.debug('toonToAttack=%s' % toonId) + unflattenedToons = self.getUnflattenedToons() + attackTotallyRandomToon = random.random() < 0.1 + if unflattenedToons and (attackTotallyRandomToon or toonId == 0): + toonId = random.choice(unflattenedToons) + if toonId: + toonThreat = self.getThreat(toonId) + toonThreat *= 0.25 + threatToSubtract = max(toonThreat, 10) + self.subtractThreat(toonId, threatToSubtract) + if self.isToonRoaming(toonId): + self.b_setAttackCode(ToontownGlobals.BossCogGolfAttack, toonId) + self.numGolfAttacks += 1 + elif self.isToonOnTable(toonId): + doesMoveAttack = simbase.air.config.GetBool('ceo-does-move-attack', 1) + if doesMoveAttack: + chanceToShoot = 0.25 + else: + chanceToShoot = 1.0 + if not self.moveAttackAllowed: + self.notify.debug('moveAttack is not allowed, doing gearDirectedAttack') + chanceToShoot = 1.0 + if random.random() < chanceToShoot: + self.b_setAttackCode(ToontownGlobals.BossCogGearDirectedAttack, toonId) + self.numGearAttacks += 1 + else: + tableIndex = self.getToonTableIndex(toonId) + self.doMoveAttack(tableIndex) + else: + self.b_setAttackCode(ToontownGlobals.BossCogGolfAttack, toonId) + else: + uprightTables = self.getUprightTables() + if uprightTables: + tableToMoveTo = random.choice(uprightTables) + self.doMoveAttack(tableToMoveTo) + else: + self.waitForNextAttack(4) + + def doMoveAttack(self, tableIndex): + self.numMoveAttacks += 1 + self.movingToTable = True + self.tableDest = tableIndex + self.b_setAttackCode(ToontownGlobals.BossCogMoveAttack, tableIndex) + + def getUnflattenedToons(self): + result = [] + uprightTables = self.getUprightTables() + for toonId in self.involvedToons: + toonTable = self.getToonTableIndex(toonId) + if toonTable >= 0 and toonTable not in uprightTables: + pass + else: + result.append(toonId) + + return result + + def getMaxThreatToon(self): + returnedToonId = 0 + maxThreat = 0 + maxToons = [] + for toonId in self.threatDict: + curThreat = self.threatDict[toonId] + tableIndex = self.getToonTableIndex(toonId) + if tableIndex > -1 and self.tables[tableIndex].state == 'Flat': + pass + elif curThreat > maxThreat: + maxToons = [toonId] + maxThreat = curThreat + elif curThreat == maxThreat: + maxToons.append(toonId) + + if maxToons: + returnedToonId = random.choice(maxToons) + return returnedToonId + + def getToonDifficulty(self): + totalCogSuitTier = 0 + totalToons = 0 + + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + totalToons += 1 + totalCogSuitTier += toon.cogTypes[1] + + averageTier = math.floor(totalCogSuitTier / totalToons) + 1 + return int(averageTier) + + def calcAndSetBattleDifficulty(self): + self.toonLevels = self.getToonDifficulty() + battleDifficulty = int(math.floor(self.toonLevels / 2)) + self.b_setBattleDifficulty(battleDifficulty) + + def b_setBattleDifficulty(self, batDiff): + self.setBattleDifficulty(batDiff) + self.d_setBattleDifficulty(batDiff) + + def setBattleDifficulty(self, batDiff): + self.battleDifficulty = batDiff + + def d_setBattleDifficulty(self, batDiff): + self.sendUpdate('setBattleDifficulty', [batDiff]) + + def getUprightTables(self): + tableList = [] + for table in self.tables: + if table.state != 'Flat': + tableList.append(table.index) + + return tableList + + def getToonTableIndex(self, toonId): + tableIndex = -1 + for table in self.tables: + if table.avId == toonId: + tableIndex = table.index + break + + return tableIndex + + def getToonGolfSpotIndex(self, toonId): + golfSpotIndex = -1 + for golfSpot in self.golfSpots: + if golfSpot.avId == toonId: + golfSpotIndex = golfSpot.index + break + + return golfSpotIndex + + def isToonOnTable(self, toonId): + result = self.getToonTableIndex(toonId) != -1 + return result + + def isToonOnGolfSpot(self, toonId): + result = self.getToonGolfSpotIndex(toonId) != -1 + return result + + def isToonRoaming(self, toonId): + result = not self.isToonOnTable(toonId) and not self.isToonOnGolfSpot(toonId) + return result + + def reachedTable(self, tableIndex): + if self.movingToTable and self.tableDest == tableIndex: + self.movingToTable = False + self.curTable = self.tableDest + self.tableDest = -1 + + def hitTable(self, tableIndex): + self.notify.debug('hitTable tableIndex=%d' % tableIndex) + if tableIndex < len(self.tables): + table = self.tables[tableIndex] + if table.state != 'Flat': + table.goFlat() + + def awayFromTable(self, tableIndex): + self.notify.debug('awayFromTable tableIndex=%d' % tableIndex) + if tableIndex < len(self.tables): + taskName = 'Unflatten-%d' % tableIndex + unflattenTime = self.diffInfo[3] + taskMgr.doMethodLater(unflattenTime, self.unflattenTable, taskName, extraArgs=[tableIndex]) + + def unflattenTable(self, tableIndex): + if tableIndex < len(self.tables): + table = self.tables[tableIndex] + if table.state == 'Flat': + if table.avId and table.avId in self.involvedToons: + table.forceControl(table.avId) + else: + table.goFree() + + def incrementDinersExploded(self): + self.numDinersExploded += 1 + + def magicWordHit(self, damage, avId): + self.hitBoss(damage) + + def __doAreaAttack(self): + self.b_setAttackCode(ToontownGlobals.BossCogAreaAttack) + + def __doGolfAreaAttack(self): + self.numGolfAreaAttacks += 1 + self.b_setAttackCode(ToontownGlobals.BossCogGolfAreaAttack) + + def hitToon(self, toonId): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId != toonId, 'hitToon on self'): + return + if avId not in self.involvedToons or toonId not in self.involvedToons: + return + toon = self.air.doId2do.get(toonId) + if toon: + self.healToon(toon, 1) + self.sendUpdate('toonGotHealed', [toonId]) + + def requestGetToonup(self, beltIndex, toonupIndex, toonupNum): + grantRequest = False + avId = self.air.getAvatarIdFromSender() + if self.state != 'BattleFour': + grantRequest = False + elif (beltIndex, toonupNum) not in self.toonupsGranted: + toon = simbase.air.doId2do.get(avId) + if toon: + grantRequest = True + if grantRequest: + self.toonupsGranted.insert(0, (beltIndex, toonupNum)) + if len(self.toonupsGranted) > 8: + self.toonupsGranted = self.toonupsGranted[0:8] + self.sendUpdate('toonGotToonup', [avId, + beltIndex, + toonupIndex, + toonupNum]) + if toonupIndex < len(self.toonUpLevels): + self.healToon(toon, self.toonUpLevels[toonupIndex]) + self.numToonupGranted += 1 + self.totalLaffHealed += self.toonUpLevels[toonupIndex] + else: + self.notify.warning('requestGetToonup this should not happen') + self.healToon(toon, 1) + + def toonLeftTable(self, tableIndex): + if self.movingToTable and self.tableDest == tableIndex: + if random.random() < 0.5: + self.movingToTable = False + self.waitForNextAttack(0) + + def getBattleFourTime(self): + if self.state != 'BattleFour': + t1 = 0 + else: + elapsed = globalClock.getFrameTime() - self.battleFourStart + t1 = elapsed / float(self.battleFourDuration) + return t1 + + def getDamageMultiplier(self): + mult = 1.0 + if self.doneOvertimeOneAttack and not self.doneOvertimeTwoAttack: + mult = 1.25 + if self.getBattleFourTime() > 1.0: + mult = self.getBattleFourTime() + 1 + return mult + + def toggleMove(self): + self.moveAttackAllowed = not self.moveAttackAllowed + return self.moveAttackAllowed + + +def getCEO(toon): + for object in simbase.air.doId2do.values(): + if isinstance(object, DistributedBossbotBossAI): + if toon.doId in object.involvedToons: + return object + + return None + +@magicWord(category=CATEGORY_ADMINISTRATOR) +def skipCEOBanquet(): + """ + Skips to the banquet stage of the CEO. + """ + boss = getCEO(spellbook.getInvoker()) + if not boss: + return "You aren't in a CEO!" + if boss.state in ('PrepareBattleTwo', 'BattleTwo'): + return "You can't skip this round." + boss.exitIntroduction() + boss.b_setState('PrepareBattleTwo') + +@magicWord(category=CATEGORY_ADMINISTRATOR) +def skipCEO(): + """ + Skips to the third round of the CEO. + """ + boss = getCEO(spellbook.getInvoker()) + if not boss: + return "You aren't in a CEO!" + if boss.state in ('PrepareBattleThree', 'BattleThree'): + return "You can't skip this round." + boss.exitIntroduction() + boss.b_setState('PrepareBattleThree') + +@magicWord(category=CATEGORY_ADMINISTRATOR) +def skipCEOFinal(): + """ + Skips to the final round of the CEO. + """ + boss = getCEO(spellbook.getInvoker()) + if not boss: + return "You aren't in a CEO!" + if boss.state in ('PrepareBattleFour', 'BattleFour'): + return "You can't skip this round." + boss.exitIntroduction() + boss.b_setState('PrepareBattleFour') + +@magicWord(category=CATEGORY_ADMINISTRATOR) +def killCEO(): + """ + Kills the CEO. + """ + boss = getCEO(spellbook.getInvoker()) + if not boss: + return "You aren't in a CEO!" + boss.b_setState('Victory') + return 'Killed CEO.' diff --git a/toontown/suit/DistributedCashbotBoss.py b/toontown/suit/DistributedCashbotBoss.py new file mode 100755 index 00000000..45aa8c19 --- /dev/null +++ b/toontown/suit/DistributedCashbotBoss.py @@ -0,0 +1,948 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import FSM +from direct.interval.IntervalGlobal import * +from direct.task.Task import Task +from direct.task.TaskManagerGlobal import * +import math +from panda3d.core import * +import random + +import DistributedBossCog +import DistributedCashbotBossGoon +import SuitDNA +from otp.otpbase import OTPGlobals +from toontown.battle import MovieToonVictory +from toontown.battle import RewardPanel +from toontown.battle import SuitBattleGlobals +from toontown.building import ElevatorConstants +from toontown.building import ElevatorUtils +from toontown.chat import ResistanceChat +from toontown.coghq import CogDisguiseGlobals +from toontown.distributed import DelayDelete +from toontown.toon import Toon, NPCToons +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from otp.nametag import NametagGroup +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals + + +OneBossCog = None +TTL = TTLocalizer + + +class DistributedCashbotBoss(DistributedBossCog.DistributedBossCog, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCashbotBoss') + numFakeGoons = 3 + + def __init__(self, cr): + DistributedBossCog.DistributedBossCog.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedCashbotBoss') + self.resistanceToon = None + self.resistanceToonOnstage = 0 + self.cranes = {} + self.safes = {} + self.goons = [] + self.bossMaxDamage = ToontownGlobals.CashbotBossMaxDamage + self.elevatorType = ElevatorConstants.ELEVATOR_CFO + base.boss = self + return + + def announceGenerate(self): + DistributedBossCog.DistributedBossCog.announceGenerate(self) + self.setName(TTLocalizer.CashbotBossName) + nameInfo = TTLocalizer.BossCogNameWithDept % {'name': self.name, + 'dept': SuitDNA.getDeptFullname(self.style.dept)} + self.setDisplayName(nameInfo) + target = CollisionSphere(2, 0, 0, 3) + targetNode = CollisionNode('headTarget') + targetNode.addSolid(target) + targetNode.setCollideMask(ToontownGlobals.PieBitmask) + self.headTarget = self.neck.attachNewNode(targetNode) + shield = CollisionSphere(0, 0, 0.8, 7) + shieldNode = CollisionNode('shield') + shieldNode.addSolid(shield) + shieldNode.setCollideMask(ToontownGlobals.PieBitmask) + shieldNodePath = self.pelvis.attachNewNode(shieldNode) + self.heldObject = None + self.bossDamage = 0 + self.intermissionMusic = base.loadMusic('phase_9/audio/bgm/CBHQ_Boss_intermission.ogg') + self.loadEnvironment() + self.__makeResistanceToon() + self.physicsMgr = PhysicsManager() + integrator = LinearEulerIntegrator() + self.physicsMgr.attachLinearIntegrator(integrator) + fn = ForceNode('gravity') + self.fnp = self.geom.attachNewNode(fn) + gravity = LinearVectorForce(0, 0, -32) + fn.addForce(gravity) + self.physicsMgr.addLinearForce(gravity) + base.localAvatar.chatMgr.chatInputSpeedChat.addCFOMenu() + global OneBossCog + if OneBossCog != None: + self.notify.warning('Multiple BossCogs visible.') + OneBossCog = self + return + + def disable(self): + global OneBossCog + DistributedBossCog.DistributedBossCog.disable(self) + self.demand('Off') + self.unloadEnvironment() + self.__cleanupResistanceToon() + self.fnp.removeNode() + self.physicsMgr.clearLinearForces() + self.battleThreeMusic.stop() + self.intermissionMusic.stop() + self.epilogueMusic.stop() + base.localAvatar.chatMgr.chatInputSpeedChat.removeCFOMenu() + if OneBossCog == self: + OneBossCog = None + return + + def __makeResistanceToon(self): + if self.resistanceToon: + return + self.resistanceToon = NPCToons.createLocalNPC(12002) + self.resistanceToon.setPosHpr(*ToontownGlobals.CashbotRTBattleOneStartPosHpr) + state = random.getstate() + random.seed(self.doId) + self.resistanceToon.suitType = SuitDNA.getRandomSuitByDept('m') + random.setstate(state) + self.fakeGoons = [] + for i in xrange(self.numFakeGoons): + goon = DistributedCashbotBossGoon.DistributedCashbotBossGoon(base.cr) + goon.doId = -1 - i + goon.setBossCogId(self.doId) + goon.generate() + goon.announceGenerate() + self.fakeGoons.append(goon) + + self.__hideFakeGoons() + + def __cleanupResistanceToon(self): + self.__hideResistanceToon() + if self.resistanceToon: + self.resistanceToon.removeActive() + self.resistanceToon.delete() + self.resistanceToon = None + for i in xrange(self.numFakeGoons): + self.fakeGoons[i].disable() + self.fakeGoons[i].delete() + self.fakeGoons[i] = None + + return + + def __showResistanceToon(self, withSuit): + if not self.resistanceToonOnstage: + self.resistanceToon.addActive() + self.resistanceToon.reparentTo(self.geom) + self.resistanceToonOnstage = 1 + if withSuit: + suit = self.resistanceToon.suitType + self.resistanceToon.putOnSuit(suit, False) + else: + self.resistanceToon.takeOffSuit() + + def __hideResistanceToon(self): + if self.resistanceToonOnstage: + self.resistanceToon.removeActive() + self.resistanceToon.detachNode() + self.resistanceToonOnstage = 0 + + def __hideFakeGoons(self): + if self.fakeGoons: + for goon in self.fakeGoons: + goon.request('Off') + + def __showFakeGoons(self, state): + if self.fakeGoons: + for goon in self.fakeGoons: + goon.request(state) + + def loadEnvironment(self): + DistributedBossCog.DistributedBossCog.loadEnvironment(self) + self.midVault = loader.loadModel('phase_10/models/cogHQ/MidVault.bam') + self.endVault = loader.loadModel('phase_10/models/cogHQ/EndVault.bam') + self.lightning = loader.loadModel('phase_10/models/cogHQ/CBLightning.bam') + self.magnet = loader.loadModel('phase_10/models/cogHQ/CBMagnet.bam') + self.craneArm = loader.loadModel('phase_10/models/cogHQ/CBCraneArm.bam') + self.controls = loader.loadModel('phase_10/models/cogHQ/CBCraneControls.bam') + self.stick = loader.loadModel('phase_10/models/cogHQ/CBCraneStick.bam') + self.safe = loader.loadModel('phase_10/models/cogHQ/CBSafe.bam') + self.eyes = loader.loadModel('phase_10/models/cogHQ/CashBotBossEyes.bam') + self.cableTex = self.craneArm.findTexture('MagnetControl') + self.eyes.setPosHprScale(4.5, 0, -2.5, 90, 90, 0, 0.4, 0.4, 0.4) + self.eyes.reparentTo(self.neck) + self.eyes.hide() + self.midVault.setPos(0, -222, -70.7) + self.endVault.setPos(84, -201, -6) + self.geom = NodePath('geom') + self.midVault.reparentTo(self.geom) + self.endVault.reparentTo(self.geom) + self.endVault.findAllMatches('**/MagnetArms').detach() + self.endVault.findAllMatches('**/Safes').detach() + self.endVault.findAllMatches('**/MagnetControlsAll').detach() + cn = self.endVault.find('**/wallsCollision').node() + cn.setIntoCollideMask(OTPGlobals.WallBitmask | ToontownGlobals.PieBitmask) + self.door1 = self.midVault.find('**/SlidingDoor1/') + self.door2 = self.midVault.find('**/SlidingDoor/') + self.door3 = self.endVault.find('**/SlidingDoor/') + elevatorModel = loader.loadModel('phase_10/models/cogHQ/CFOElevator') + elevatorOrigin = self.midVault.find('**/elevator_origin') + elevatorOrigin.setScale(1) + elevatorModel.reparentTo(elevatorOrigin) + leftDoor = elevatorModel.find('**/left_door') + leftDoor.setName('left-door') + rightDoor = elevatorModel.find('**/right_door') + rightDoor.setName('right-door') + self.setupElevator(elevatorOrigin) + ElevatorUtils.closeDoors(leftDoor, rightDoor, ElevatorConstants.ELEVATOR_CFO) + walls = self.endVault.find('**/RollUpFrameCillison') + walls.detachNode() + self.evWalls = self.replaceCollisionPolysWithPlanes(walls) + self.evWalls.reparentTo(self.endVault) + self.evWalls.stash() + floor = self.endVault.find('**/EndVaultFloorCollision') + floor.detachNode() + self.evFloor = self.replaceCollisionPolysWithPlanes(floor) + self.evFloor.reparentTo(self.endVault) + self.evFloor.setName('floor') + plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, -50))) + planeNode = CollisionNode('dropPlane') + planeNode.addSolid(plane) + planeNode.setCollideMask(ToontownGlobals.PieBitmask) + self.geom.attachNewNode(planeNode) + self.geom.reparentTo(render) + + def unloadEnvironment(self): + DistributedBossCog.DistributedBossCog.unloadEnvironment(self) + self.geom.removeNode() + + def replaceCollisionPolysWithPlanes(self, model): + newCollisionNode = CollisionNode('collisions') + newCollideMask = BitMask32(0) + planes = [] + collList = model.findAllMatches('**/+CollisionNode') + if not collList: + collList = [model] + for cnp in collList: + cn = cnp.node() + if not isinstance(cn, CollisionNode): + self.notify.warning('Not a collision node: %s' % repr(cnp)) + break + newCollideMask = newCollideMask | cn.getIntoCollideMask() + for i in xrange(cn.getNumSolids()): + solid = cn.getSolid(i) + if isinstance(solid, CollisionPolygon): + plane = Plane(solid.getPlane()) + planes.append(plane) + else: + self.notify.warning('Unexpected collision solid: %s' % repr(solid)) + newCollisionNode.addSolid(plane) + + newCollisionNode.setIntoCollideMask(newCollideMask) + threshold = 0.1 + planes.sort(lambda p1, p2: p1.compareTo(p2, threshold)) + lastPlane = None + for plane in planes: + if lastPlane == None or plane.compareTo(lastPlane, threshold) != 0: + cp = CollisionPlane(plane) + newCollisionNode.addSolid(cp) + lastPlane = plane + + return NodePath(newCollisionNode) + + def __makeGoonMovieForIntro(self): + goonTrack = Parallel() + goon = self.fakeGoons[0] + goonTrack.append(Sequence( + goon.posHprInterval(0, Point3(111, -287, 0), VBase3(165, 0, 0)), + goon.posHprInterval(9, Point3(101, -323, 0), VBase3(165, 0, 0)), + goon.hprInterval(1, VBase3(345, 0, 0)), + goon.posHprInterval(9, Point3(111, -287, 0), VBase3(345, 0, 0)), + goon.hprInterval(1, VBase3(165, 0, 0)), + goon.posHprInterval(9.5, Point3(104, -316, 0), VBase3(165, 0, 0)), + Func(goon.request, 'Stunned'), + Wait(1))) + goon = self.fakeGoons[1] + goonTrack.append(Sequence( + goon.posHprInterval(0, Point3(119, -315, 0), VBase3(357, 0, 0)), + goon.posHprInterval(9, Point3(121, -280, 0), VBase3(357, 0, 0)), + goon.hprInterval(1, VBase3(177, 0, 0)), + goon.posHprInterval(9, Point3(119, -315, 0), VBase3(177, 0, 0)), + goon.hprInterval(1, VBase3(357, 0, 0)), + goon.posHprInterval(9, Point3(121, -280, 0), VBase3(357, 0, 0)))) + goon = self.fakeGoons[2] + goonTrack.append(Sequence( + goon.posHprInterval(0, Point3(102, -320, 0), VBase3(231, 0, 0)), + goon.posHprInterval(9, Point3(127, -337, 0), VBase3(231, 0, 0)), + goon.hprInterval(1, VBase3(51, 0, 0)), + goon.posHprInterval(9, Point3(102, -320, 0), VBase3(51, 0, 0)), + goon.hprInterval(1, VBase3(231, 0, 0)), + goon.posHprInterval(9, Point3(127, -337, 0), VBase3(231, 0, 0)))) + return Sequence(Func(self.__showFakeGoons, 'Walk'), goonTrack, Func(self.__hideFakeGoons)) + + def makeIntroductionMovie(self, delayDeletes): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'CashbotBoss.makeIntroductionMovie')) + + rtTrack = Sequence() + startPos = Point3(ToontownGlobals.CashbotBossOffstagePosHpr[0], ToontownGlobals.CashbotBossOffstagePosHpr[1], ToontownGlobals.CashbotBossOffstagePosHpr[2]) + battlePos = Point3(ToontownGlobals.CashbotBossBattleOnePosHpr[0], ToontownGlobals.CashbotBossBattleOnePosHpr[1], ToontownGlobals.CashbotBossBattleOnePosHpr[2]) + battleHpr = VBase3(ToontownGlobals.CashbotBossBattleOnePosHpr[3], ToontownGlobals.CashbotBossBattleOnePosHpr[4], ToontownGlobals.CashbotBossBattleOnePosHpr[5]) + bossTrack = Sequence() + bossTrack.append(Func(self.reparentTo, render)) + bossTrack.append(Func(self.getGeomNode().setH, 180)) + bossTrack.append(Func(self.pelvis.setHpr, self.pelvisForwardHpr)) + bossTrack.append(Func(self.loop, 'Ff_neutral')) + track, hpr = self.rollBossToPoint(startPos, None, battlePos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(battlePos, hpr, battlePos, battleHpr, 0) + bossTrack.append(track) + bossTrack.append(Func(self.getGeomNode().setH, 0)) + bossTrack.append(Func(self.pelvis.setHpr, self.pelvisReversedHpr)) + goonTrack = self.__makeGoonMovieForIntro() + attackToons = TTL.CashbotBossCogAttack + rToon = self.resistanceToon + rToon.setPosHpr(*ToontownGlobals.CashbotRTBattleOneStartPosHpr) + track = Sequence( + Func(base.camera.setPosHpr, 82, -219, 5, 267, 0, 0), + Func(rToon.setChatAbsolute, TTL.ResistanceToonWelcome, CFSpeech), + Wait(3), + Sequence(goonTrack, duration=0), + Parallel( + base.camera.posHprInterval(4, Point3(108, -244, 4), VBase3(211.5, 0, 0)), + Sequence( + Func(rToon.suit.setPlayRate, 1.4, 'walk'), + Func(rToon.suit.loop, 'walk'), + Parallel( + rToon.hprInterval(1, VBase3(180, 0, 0)), + rToon.posInterval(3, VBase3(120, -255, 0)), + Sequence( + Wait(2), + Func(rToon.clearChat))), + Func(rToon.suit.loop, 'neutral'), + self.door2.posInterval(3, VBase3(0, 0, 30)))), + Func(rToon.setHpr, 0, 0, 0), + Func(rToon.setChatAbsolute, TTL.ResistanceToonTooLate, CFSpeech), + Func(base.camera.reparentTo, render), + Func(base.camera.setPosHpr, 61.1, -228.8, 10.2, -90, 0, 0), + self.door1.posInterval(2, VBase3(0, 0, 30)), + Parallel( + bossTrack, + Sequence( + Wait(3), + Func(rToon.clearChat), + self.door1.posInterval(3, VBase3(0, 0, 0)))), + Func(self.setChatAbsolute, TTL.CashbotBossDiscoverToons1, CFSpeech), + base.camera.posHprInterval(1.5, Point3(93.3, -230, 0.7), VBase3(-92.9, 39.7, 8.3)), + Func(self.setChatAbsolute, TTL.CashbotBossDiscoverToons2, CFSpeech), + Wait(4), + Func(self.clearChat), + self.loseCogSuits(self.toonsA + self.toonsB, render, (113, -228, 10, 90, 0, 0)), + Wait(1), + Func(rToon.setHpr, 0, 0, 0), + self.loseCogSuits([rToon], render, (133, -243, 5, 143, 0, 0), True), + Func(rToon.setChatAbsolute, TTL.ResistanceToonKeepHimBusy, CFSpeech), + Wait(1), + Func(self.__showResistanceToon, False), + Sequence( + Func(rToon.animFSM.request, 'run'), + rToon.hprInterval(1, VBase3(180, 0, 0)), + Parallel( + Sequence( + rToon.posInterval(1.5, VBase3(109, -294, 0)), + Parallel(Func(rToon.animFSM.request, 'jump')), + rToon.posInterval(1.5, VBase3(93.935, -341.065, 2))), + self.door2.posInterval(3, VBase3(0, 0, 0))), + Func(rToon.animFSM.request, 'neutral')), + self.toonNormalEyes(self.involvedToons), + self.toonNormalEyes([self.resistanceToon], True), + Func(rToon.clearChat), + Func(base.camera.setPosHpr, 93.3, -230, 0.7, -92.9, 39.7, 8.3), + Func(self.setChatAbsolute, attackToons, CFSpeech), + Wait(2), + Func(self.clearChat)) + return Sequence(Func(base.camera.reparentTo, render), track) + + def __makeGoonMovieForBattleThree(self): + goonPosHprs = [[Point3(111, -287, 0), + VBase3(165, 0, 0), + Point3(101, -323, 0), + VBase3(165, 0, 0)], [Point3(119, -315, 0), + VBase3(357, 0, 0), + Point3(121, -280, 0), + VBase3(357, 0, 0)], [Point3(102, -320, 0), + VBase3(231, 0, 0), + Point3(127, -337, 0), + VBase3(231, 0, 0)]] + mainGoon = self.fakeGoons[0] + goonLoop = Parallel() + for i in xrange(1, self.numFakeGoons): + goon = self.fakeGoons[i] + goonLoop.append(Sequence(goon.posHprInterval(8, goonPosHprs[i][0], goonPosHprs[i][1]), goon.posHprInterval(8, goonPosHprs[i][2], goonPosHprs[i][3]))) + + goonTrack = Sequence(Func(self.__showFakeGoons, 'Walk'), Func(mainGoon.request, 'Stunned'), Func(goonLoop.loop), Wait(20)) + return goonTrack + + def makePrepareBattleThreeMovie(self, delayDeletes, crane, safe): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'CashbotBoss.makePrepareBattleThreeMovie')) + + startPos = Point3(ToontownGlobals.CashbotBossBattleOnePosHpr[0], ToontownGlobals.CashbotBossBattleOnePosHpr[1], ToontownGlobals.CashbotBossBattleOnePosHpr[2]) + battlePos = Point3(ToontownGlobals.CashbotBossBattleThreePosHpr[0], ToontownGlobals.CashbotBossBattleThreePosHpr[1], ToontownGlobals.CashbotBossBattleThreePosHpr[2]) + startHpr = Point3(ToontownGlobals.CashbotBossBattleOnePosHpr[3], ToontownGlobals.CashbotBossBattleOnePosHpr[4], ToontownGlobals.CashbotBossBattleOnePosHpr[5]) + battleHpr = VBase3(ToontownGlobals.CashbotBossBattleThreePosHpr[3], ToontownGlobals.CashbotBossBattleThreePosHpr[4], ToontownGlobals.CashbotBossBattleThreePosHpr[5]) + finalHpr = VBase3(135, 0, 0) + bossTrack = Sequence() + bossTrack.append(Func(base.playMusic, self.intermissionMusic, 1, 0.9)) + bossTrack.append(Func(self.reparentTo, render)) + bossTrack.append(Func(self.getGeomNode().setH, 180)) + bossTrack.append(Func(self.pelvis.setHpr, self.pelvisForwardHpr)) + bossTrack.append(Func(self.loop, 'Ff_neutral')) + track, hpr = self.rollBossToPoint(startPos, startHpr, startPos, battleHpr, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(startPos, None, battlePos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(battlePos, battleHpr, battlePos, finalHpr, 0) + bossTrack.append(track) + rToon = self.resistanceToon + rToon.setPosHpr(93.935, -341.065, 0, -45, 0, 0) + goon = self.fakeGoons[0] + crane = self.cranes[0] + track = Sequence( + Func(self.__hideToons), + Func(crane.request, 'Movie'), + Func(crane.accomodateToon, rToon), + Func(goon.request, 'Stunned'), + Func(goon.setPosHpr, 104, -316, 0, 165, 0, 0), + Parallel( + self.door2.posInterval(4.5, VBase3(0, 0, 30)), + self.door3.posInterval(4.5, VBase3(0, 0, 30)), + bossTrack), + Func(rToon.loop, 'leverNeutral'), + Func(base.camera.reparentTo, self.geom), + Func(base.camera.setPosHpr, 105, -326, 5, 136.3, 0, 0), + Func(rToon.setChatAbsolute, TTL.ResistanceToonWatchThis, CFSpeech), + Wait(2), + Func(rToon.clearChat), + Func(base.camera.setPosHpr, 105, -326, 20, -45.3, 11, 0), + Func(self.setChatAbsolute, TTL.CashbotBossGetAwayFromThat, CFSpeech), + Wait(2), + Func(self.clearChat), + base.camera.posHprInterval(1.5, Point3(105, -326, 5), Point3(136.3, 0, 0), blendType='easeInOut'), + Func(rToon.setChatAbsolute, TTL.ResistanceToonCraneInstructions1, CFSpeech), + Wait(4), + Func(rToon.setChatAbsolute, TTL.ResistanceToonCraneInstructions2, CFSpeech), + Wait(4), + Func(rToon.setChatAbsolute, TTL.ResistanceToonCraneInstructions3, CFSpeech), + Wait(4), + Func(rToon.setChatAbsolute, TTL.ResistanceToonCraneInstructions4, CFSpeech), + Wait(4), + Func(rToon.clearChat), + Func(base.camera.setPosHpr, 102, -323.6, 0.9, -10.6, 14, 0), + Func(goon.request, 'Recovery'), + Wait(2), + Func(base.camera.setPosHpr, 95.4, -332.6, 4.2, 167.1, -13.2, 0), + Func(rToon.setChatAbsolute, TTL.ResistanceToonGetaway, CFSpeech), + Func(rToon.animFSM.request, 'jump'), + Wait(1.8), + Func(rToon.clearChat), + Func(base.camera.setPosHpr, 109.1, -300.7, 13.9, -15.6, -13.6, 0), + Func(rToon.animFSM.request, 'run'), + Func(goon.request, 'Walk'), + Parallel( + self.door3.posInterval(3, VBase3(0, 0, 0)), + rToon.posHprInterval(3, Point3(136, -212.9, 0), VBase3(-14, 0, 0), startPos=Point3(110.8, -292.7, 0), startHpr=VBase3(-14, 0, 0)), + goon.posHprInterval(3, Point3(125.2, -243.5, 0), VBase3(-14, 0, 0), startPos=Point3(104.8, -309.5, 0), startHpr=VBase3(-14, 0, 0))), + Func(self.__hideFakeGoons), + Func(self.intermissionMusic.stop), + Func(crane.request, 'Free'), + Func(self.getGeomNode().setH, 0), + self.moveToonsToBattleThreePos(self.involvedToons), + Func(self.__showToons)) + return Sequence(Func(base.camera.reparentTo, self), Func(base.camera.setPosHpr, 0, -27, 25, 0, -18, 0), track) + + def moveToonsToBattleThreePos(self, toons): + track = Parallel() + for i in xrange(len(toons)): + toon = base.cr.doId2do.get(toons[i]) + if toon: + posHpr = ToontownGlobals.CashbotToonsBattleThreeStartPosHpr[i] + pos = Point3(*posHpr[0:3]) + hpr = VBase3(*posHpr[3:6]) + track.append(toon.posHprInterval(0.2, pos, hpr)) + + return track + + def makeBossFleeMovie(self): + hadEnough = TTLocalizer.CashbotBossHadEnough + outtaHere = TTLocalizer.CashbotBossOuttaHere + loco = loader.loadModel('phase_10/models/cogHQ/CashBotLocomotive') + car1 = loader.loadModel('phase_10/models/cogHQ/CashBotBoxCar') + car2 = loader.loadModel('phase_10/models/cogHQ/CashBotTankCar') + trainPassingSfx = base.loadSfx('phase_10/audio/sfx/CBHQ_TRAIN_pass.ogg') + boomSfx = loader.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart_%s.ogg' % random.randint(1, 6)) + rollThroughDoor = self.rollBossToPoint(fromPos=Point3(120, -280, 0), fromHpr=None, toPos=Point3(120, -250, 0), toHpr=None, reverse=0) + rollTrack = Sequence(Func(self.getGeomNode().setH, 180), rollThroughDoor[0], Func(self.getGeomNode().setH, 0)) + g = 80.0 / 300.0 + trainTrack = Track( + (0 * g, loco.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (1 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (2 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (3 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (4 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (5 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (6 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (7 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (8 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (9 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (10 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (11 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (12 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (13 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (14 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0)))) + bossTrack = Track( + (0.0, Sequence( + Func(base.camera.reparentTo, render), + Func(base.camera.setPosHpr, 105, -280, 20, -158, -3, 0), + Func(self.reparentTo, render), + Func(self.show), + Func(self.clearChat), + Func(self.setPosHpr, *ToontownGlobals.CashbotBossBattleThreePosHpr), + Func(self.reverseHead), + ActorInterval(self, 'Fb_firstHit'), + ActorInterval(self, 'Fb_down2Up'))), + (1.0, Func(self.setChatAbsolute, hadEnough, CFSpeech)), + (5.5, Parallel( + Func(base.camera.setPosHpr, 100, -315, 16, -20, 0, 0), + Func(self.hideBattleThreeObjects), + Func(self.forwardHead), + Func(self.loop, 'Ff_neutral'), + rollTrack, + self.door3.posInterval(2.5, Point3(0, 0, 25), startPos=Point3(0, 0, 18)))), + (5.5, Func(self.setChatAbsolute, outtaHere, CFSpeech)), + (5.5, SoundInterval(trainPassingSfx)), + (8.1, Func(self.clearChat)), + (9.4, Sequence( + Func(loco.reparentTo, render), + Func(car1.reparentTo, render), + Func(car2.reparentTo, render), + trainTrack, + Func(loco.detachNode), + Func(car1.detachNode), + Func(car2.detachNode), + Wait(2))), + (9.5, SoundInterval(boomSfx)), + (9.5, Sequence( + self.posInterval(0.4, Point3(0, -250, 0)), + Func(self.stash)))) + return bossTrack + + def grabObject(self, obj): + obj.wrtReparentTo(self.neck) + obj.hideShadows() + obj.stashCollisions() + if obj.lerpInterval: + obj.lerpInterval.finish() + obj.lerpInterval = Parallel(obj.posInterval(ToontownGlobals.CashbotBossToMagnetTime, Point3(-1, 0, 0.2)), obj.quatInterval(ToontownGlobals.CashbotBossToMagnetTime, VBase3(0, -90, 90)), Sequence(Wait(ToontownGlobals.CashbotBossToMagnetTime), ShowInterval(self.eyes)), obj.toMagnetSoundInterval) + obj.lerpInterval.start() + self.heldObject = obj + + def dropObject(self, obj): + if obj.lerpInterval: + obj.lerpInterval.finish() + obj.lerpInterval = None + obj = self.heldObject + obj.wrtReparentTo(render) + obj.setHpr(obj.getH(), 0, 0) + self.eyes.hide() + obj.showShadows() + obj.unstashCollisions() + self.heldObject = None + return + + def setBossDamage(self, bossDamage): + if bossDamage > self.bossDamage: + delta = bossDamage - self.bossDamage + self.flashRed() + self.doAnimate('hit', now=1) + self.showHpText(-delta, scale=5) + self.bossDamage = bossDamage + self.updateHealthBar() + + def setRewardId(self, rewardId): + self.rewardId = rewardId + + def d_applyReward(self): + self.sendUpdate('applyReward', []) + + def stunAllGoons(self): + for goon in self.goons: + if goon.state == 'Walk' or goon.state == 'Battle': + goon.demand('Stunned') + goon.sendUpdate('requestStunned', [0]) + + def destroyAllGoons(self): + for goon in self.goons: + if goon.state != 'Off' and not goon.isDead: + goon.b_destroyGoon() + + def deactivateCranes(self): + for crane in self.cranes.values(): + crane.demand('Free') + + def hideBattleThreeObjects(self): + for goon in self.goons: + goon.demand('Off') + + for safe in self.safes.values(): + safe.demand('Off') + + for crane in self.cranes.values(): + crane.demand('Off') + + def __doPhysics(self, task): + dt = globalClock.getDt() + self.physicsMgr.doPhysics(dt) + return Task.cont + + def __hideToons(self): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.hide() + + def __showToons(self): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.show() + + def __arrangeToonsAroundResistanceToon(self): + radius = 7 + numToons = len(self.involvedToons) + center = (numToons - 1) / 2.0 + for i in xrange(numToons): + toon = self.cr.doId2do.get(self.involvedToons[i]) + if toon: + angle = 90 - 15 * (i - center) + radians = angle * math.pi / 180.0 + x = math.cos(radians) * radius + y = math.sin(radians) * radius + toon.setPos(self.resistanceToon, x, y, 0) + toon.headsUp(self.resistanceToon) + toon.loop('neutral') + toon.show() + + def __talkAboutPromotion(self, speech): + if self.prevCogSuitLevel < ToontownGlobals.MaxCogSuitLevel: + newCogSuitLevel = localAvatar.getCogLevels()[CogDisguiseGlobals.dept2deptIndex(self.style.dept)] + if newCogSuitLevel == ToontownGlobals.MaxCogSuitLevel: + speech += TTLocalizer.ResistanceToonLastPromotion % (ToontownGlobals.MaxCogSuitLevel + 1) + if newCogSuitLevel in ToontownGlobals.CogSuitHPLevels: + speech += TTLocalizer.ResistanceToonHPBoost + else: + speech += TTLocalizer.ResistanceToonMaxed % (ToontownGlobals.MaxCogSuitLevel + 1) + + if self.keyReward: + speech += TTLocalizer.BossRTKeyReward + + return speech + + def enterOff(self): + DistributedBossCog.DistributedBossCog.enterOff(self) + if self.resistanceToon: + self.resistanceToon.clearChat() + + def enterWaitForToons(self): + DistributedBossCog.DistributedBossCog.enterWaitForToons(self) + self.detachNode() + self.geom.hide() + self.resistanceToon.removeActive() + + def exitWaitForToons(self): + DistributedBossCog.DistributedBossCog.exitWaitForToons(self) + self.geom.show() + self.resistanceToon.addActive() + + def enterElevator(self): + DistributedBossCog.DistributedBossCog.enterElevator(self) + self.detachNode() + self.resistanceToon.removeActive() + self.endVault.stash() + self.midVault.unstash() + self.__showResistanceToon(True) + base.camLens.setMinFov(ToontownGlobals.CFOElevatorFov/(4./3.)) + + def exitElevator(self): + DistributedBossCog.DistributedBossCog.exitElevator(self) + self.resistanceToon.addActive() + + def enterIntroduction(self): + self.detachNode() + self.stopAnimate() + self.endVault.unstash() + self.evWalls.stash() + self.midVault.unstash() + base.playMusic(self.stingMusic, looping=1, volume=0.9) + DistributedBossCog.DistributedBossCog.enterIntroduction(self) + + def exitIntroduction(self): + DistributedBossCog.DistributedBossCog.exitIntroduction(self) + self.stingMusic.stop() + + def enterBattleOne(self): + DistributedBossCog.DistributedBossCog.enterBattleOne(self) + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.CashbotBossBattleOnePosHpr) + self.show() + self.pelvis.setHpr(self.pelvisReversedHpr) + self.doAnimate() + self.endVault.stash() + self.midVault.unstash() + self.__hideResistanceToon() + + def exitBattleOne(self): + DistributedBossCog.DistributedBossCog.exitBattleOne(self) + + def enterPrepareBattleThree(self): + self.controlToons() + NametagGlobals.setMasterArrowsOn(0) + intervalName = 'PrepareBattleThreeMovie' + delayDeletes = [] + self.movieCrane = self.cranes[0] + self.movieSafe = self.safes[1] + self.movieCrane.request('Movie') + seq = Sequence(self.makePrepareBattleThreeMovie(delayDeletes, self.movieCrane, self.movieSafe), Func(self.__beginBattleThree), name=intervalName) + seq.delayDeletes = delayDeletes + seq.start() + self.storeInterval(seq, intervalName) + self.endVault.unstash() + self.evWalls.stash() + self.midVault.unstash() + self.__showResistanceToon(False) + taskMgr.add(self.__doPhysics, self.uniqueName('physics'), priority=25) + + def __beginBattleThree(self): + intervalName = 'PrepareBattleThreeMovie' + self.clearInterval(intervalName) + self.doneBarrier('PrepareBattleThree') + + def exitPrepareBattleThree(self): + intervalName = 'PrepareBattleThreeMovie' + self.clearInterval(intervalName) + self.unstickToons() + self.releaseToons() + if self.newState == 'BattleThree': + self.movieCrane.request('Free') + self.movieSafe.request('Initial') + NametagGlobals.setMasterArrowsOn(1) + ElevatorUtils.closeDoors(self.leftDoor, self.rightDoor, ElevatorConstants.ELEVATOR_CFO) + taskMgr.remove(self.uniqueName('physics')) + + def enterBattleThree(self): + DistributedBossCog.DistributedBossCog.enterBattleThree(self) + self.clearChat() + self.resistanceToon.clearChat() + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.CashbotBossBattleThreePosHpr) + self.happy = 1 + self.raised = 1 + self.forward = 1 + self.doAnimate() + self.endVault.unstash() + self.evWalls.unstash() + self.midVault.stash() + self.__hideResistanceToon() + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.generateHealthBar() + self.updateHealthBar() + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9) + taskMgr.add(self.__doPhysics, self.uniqueName('physics'), priority=25) + + def exitBattleThree(self): + DistributedBossCog.DistributedBossCog.exitBattleThree(self) + bossDoneEventName = self.uniqueName('DestroyedBoss') + self.ignore(bossDoneEventName) + self.stopAnimate() + self.cleanupAttacks() + self.setDizzy(0) + self.healthBar.delete() + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + if self.newState != 'Victory': + self.battleThreeMusic.stop() + taskMgr.remove(self.uniqueName('physics')) + + def enterVictory(self): + self.cleanupIntervals() + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.CashbotBossBattleThreePosHpr) + self.stopAnimate() + self.endVault.unstash() + self.evWalls.unstash() + self.midVault.unstash() + self.__hideResistanceToon() + self.__hideToons() + self.clearChat() + self.resistanceToon.clearChat() + self.deactivateCranes() + if self.cranes: + self.cranes[1].demand('Off') + self.releaseToons(finalBattle=1) + if self.hasLocalToon(): + self.toMovieMode() + intervalName = 'VictoryMovie' + seq = Sequence(self.makeBossFleeMovie(), Func(self.__continueVictory), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + if self.oldState != 'BattleThree': + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9) + + def __continueVictory(self): + self.doneBarrier('Victory') + + def exitVictory(self): + self.cleanupIntervals() + if self.newState != 'Reward': + if self.hasLocalToon(): + self.toWalkMode() + self.__showToons() + self.door3.setPos(0, 0, 0) + if self.newState != 'Reward': + self.battleThreeMusic.stop() + + def enterReward(self): + self.cleanupIntervals() + self.clearChat() + self.resistanceToon.clearChat() + self.stash() + self.stopAnimate() + self.controlToons() + panelName = self.uniqueName('reward') + self.rewardPanel = RewardPanel.RewardPanel(panelName) + victory, camVictory, skipper = MovieToonVictory.doToonVictory(1, self.involvedToons, self.toonRewardIds, self.toonRewardDicts, self.deathList, self.rewardPanel, allowGroupShot=0, uberList=self.uberList, noSkip=True) + ival = Sequence(Parallel(victory, camVictory), Func(self.__doneReward)) + intervalName = 'RewardMovie' + delayDeletes = [] + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'CashbotBoss.enterReward')) + + ival.delayDeletes = delayDeletes + ival.start() + self.storeInterval(ival, intervalName) + if self.oldState != 'Victory': + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9) + + def __doneReward(self): + self.doneBarrier('Reward') + self.toWalkMode() + + def exitReward(self): + intervalName = 'RewardMovie' + self.clearInterval(intervalName) + if self.newState != 'Epilogue': + self.releaseToons() + self.unstash() + self.rewardPanel.destroy() + del self.rewardPanel + self.battleThreeMusic.stop() + + def enterEpilogue(self): + self.cleanupIntervals() + self.clearChat() + self.resistanceToon.clearChat() + self.stash() + self.stopAnimate() + self.controlToons() + self.__showResistanceToon(False) + self.resistanceToon.setPosHpr(*ToontownGlobals.CashbotBossBattleThreePosHpr) + self.resistanceToon.loop('neutral') + self.__arrangeToonsAroundResistanceToon() + base.camera.reparentTo(render) + base.camera.setPos(self.resistanceToon, -9, 12, 6) + base.camera.lookAt(self.resistanceToon, 0, 0, 3) + intervalName = 'EpilogueMovie' + text = ResistanceChat.getChatText(self.rewardId) + menuIndex, itemIndex = ResistanceChat.decodeId(self.rewardId) + value = ResistanceChat.getItemValue(self.rewardId) + if menuIndex == ResistanceChat.RESISTANCE_TOONUP: + if value == -1: + instructions = TTLocalizer.ResistanceToonToonupAllInstructions + else: + instructions = TTLocalizer.ResistanceToonToonupInstructions % value + elif menuIndex == ResistanceChat.RESISTANCE_MONEY: + if value == -1: + instructions = TTLocalizer.ResistanceToonMoneyAllInstructions + else: + instructions = TTLocalizer.ResistanceToonMoneyInstructions % value + elif menuIndex == ResistanceChat.RESISTANCE_RESTOCK: + if value == -1: + instructions = TTLocalizer.ResistanceToonRestockAllInstructions + else: + trackName = TTLocalizer.BattleGlobalTracks[value] + instructions = TTLocalizer.ResistanceToonRestockInstructions % trackName + elif menuIndex == ResistanceChat.RESISTANCE_MERITS: + if value == -1: + instructions = TTLocalizer.ResistanceToonMeritsAllInstructions + else: + instructions = TTLocalizer.ResistanceToonMeritsInstructions % TTLocalizer.RewardPanelMeritBarLabels[value] + elif menuIndex == ResistanceChat.RESISTANCE_TICKETS: + instructions = TTLocalizer.ResistanceToonTicketsInstructions % value + speech = TTLocalizer.ResistanceToonCongratulations % (text, instructions) + speech = self.__talkAboutPromotion(speech) + self.resistanceToon.setLocalPageChat(speech, 0) + self.accept('nextChatPage', self.__epilogueChatNext) + self.accept('doneChatPage', self.__epilogueChatDone) + base.playMusic(self.epilogueMusic, looping=1, volume=0.9) + + def __epilogueChatNext(self, pageNumber, elapsed): + if pageNumber == 1: + toon = self.resistanceToon + playRate = 0.75 + track = Sequence(ActorInterval(toon, 'victory', playRate=playRate, startFrame=0, endFrame=9), ActorInterval(toon, 'victory', playRate=playRate, startFrame=9, endFrame=0), Func(self.resistanceToon.loop, 'neutral')) + intervalName = 'EpilogueMovieToonAnim' + self.storeInterval(track, intervalName) + track.start() + elif pageNumber == 3: + self.d_applyReward() + ResistanceChat.doEffect(self.rewardId, self.resistanceToon, self.involvedToons) + + def __epilogueChatDone(self, elapsed): + self.resistanceToon.setChatAbsolute(TTLocalizer.CagedToonGoodbye, CFSpeech) + self.ignore('nextChatPage') + self.ignore('doneChatPage') + intervalName = 'EpilogueMovieToonAnim' + self.clearInterval(intervalName) + track = Parallel(Sequence(ActorInterval(self.resistanceToon, 'wave'), Func(self.resistanceToon.loop, 'neutral')), Sequence(Wait(0.5), Func(self.localToonToSafeZone))) + self.storeInterval(track, intervalName) + track.start() + + def exitEpilogue(self): + self.clearInterval('EpilogueMovieToonAnim') + self.unstash() + self.epilogueMusic.stop() + + def enterFrolic(self): + DistributedBossCog.DistributedBossCog.enterFrolic(self) + self.setPosHpr(*ToontownGlobals.CashbotBossBattleOnePosHpr) + self.releaseToons() + if self.hasLocalToon(): + self.toWalkMode() + self.door3.setZ(25) + self.door2.setZ(25) + self.endVault.unstash() + self.evWalls.stash() + self.midVault.unstash() + self.__hideResistanceToon() + + def exitFrolic(self): + self.door3.setZ(0) + self.door2.setZ(0) diff --git a/toontown/suit/DistributedCashbotBossAI.py b/toontown/suit/DistributedCashbotBossAI.py new file mode 100755 index 00000000..77e8535a --- /dev/null +++ b/toontown/suit/DistributedCashbotBossAI.py @@ -0,0 +1,551 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.coghq import DistributedCashbotBossCraneAI +from toontown.coghq import DistributedCashbotBossSafeAI +from toontown.suit import DistributedCashbotBossGoonAI +from toontown.coghq import DistributedCashbotBossTreasureAI +from toontown.battle import BattleExperienceAI +from toontown.chat import ResistanceChat +from direct.fsm import FSM +import DistributedBossCogAI +import SuitDNA +import random +from otp.ai.MagicWordGlobal import * +import math + +class DistributedCashbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCashbotBossAI') + maxGoons = 8 + BossName = "CFO" + + def __init__(self, air): + DistributedBossCogAI.DistributedBossCogAI.__init__(self, air, 'm') + FSM.FSM.__init__(self, 'DistributedCashbotBossAI') + self.cranes = None + self.safes = None + self.goons = None + self.treasures = {} + self.grabbingTreasures = {} + self.recycledTreasures = [] + self.healAmount = 0 + self.rewardId = ResistanceChat.getRandomId() + self.rewardedToons = [] + self.scene = NodePath('scene') + self.reparentTo(self.scene) + cn = CollisionNode('walls') + cs = CollisionSphere(0, 0, 0, 13) + cn.addSolid(cs) + cs = CollisionInvSphere(0, 0, 0, 42) + cn.addSolid(cs) + self.attachNewNode(cn) + self.heldObject = None + self.waitingForHelmet = 0 + self.avatarHelmets = {} + self.bossMaxDamage = ToontownGlobals.CashbotBossMaxDamage + return + + def generate(self): + DistributedBossCogAI.DistributedBossCogAI.generate(self) + + def getHoodId(self): + return ToontownGlobals.CashbotHQ + + def formatReward(self): + return str(self.rewardId) + + def makeBattleOneBattles(self): + self.postBattleState = 'PrepareBattleThree' + self.initializeBattles(1, ToontownGlobals.CashbotBossBattleOnePosHpr) + + def generateSuits(self, battleNumber): + cogs = self.invokeSuitPlanner(11, 0) + skelecogs = self.invokeSuitPlanner(12, 1) + activeSuits = cogs['activeSuits'] + skelecogs['activeSuits'] + reserveSuits = cogs['reserveSuits'] + skelecogs['reserveSuits'] + random.shuffle(activeSuits) + while len(activeSuits) > 4: + suit = activeSuits.pop() + reserveSuits.append((suit, 100)) + + def compareJoinChance(a, b): + return cmp(a[1], b[1]) + + reserveSuits.sort(compareJoinChance) + return {'activeSuits': activeSuits, + 'reserveSuits': reserveSuits} + + def removeToon(self, avId): + if self.cranes != None: + for crane in self.cranes: + crane.removeToon(avId) + + if self.safes != None: + for safe in self.safes: + safe.removeToon(avId) + + if self.goons != None: + for goon in self.goons: + goon.removeToon(avId) + + DistributedBossCogAI.DistributedBossCogAI.removeToon(self, avId) + return + + def __makeBattleThreeObjects(self): + if self.cranes == None: + self.cranes = [] + for index in xrange(len(ToontownGlobals.CashbotBossCranePosHprs)): + crane = DistributedCashbotBossCraneAI.DistributedCashbotBossCraneAI(self.air, self, index) + crane.generateWithRequired(self.zoneId) + self.cranes.append(crane) + + if self.safes == None: + self.safes = [] + for index in xrange(len(ToontownGlobals.CashbotBossSafePosHprs)): + safe = DistributedCashbotBossSafeAI.DistributedCashbotBossSafeAI(self.air, self, index) + safe.generateWithRequired(self.zoneId) + self.safes.append(safe) + + if self.goons == None: + self.goons = [] + return + + def __resetBattleThreeObjects(self): + if self.cranes != None: + for crane in self.cranes: + crane.request('Free') + + if self.safes != None: + for safe in self.safes: + safe.request('Initial') + + return + + def __deleteBattleThreeObjects(self): + if self.cranes != None: + for crane in self.cranes: + crane.request('Off') + crane.requestDelete() + + self.cranes = None + if self.safes != None: + for safe in self.safes: + safe.request('Off') + safe.requestDelete() + + self.safes = None + if self.goons != None: + for goon in self.goons: + goon.request('Off') + goon.requestDelete() + + self.goons = None + return + + def doNextAttack(self, task): + if random.random() <= 0.2: + self.b_setAttackCode(ToontownGlobals.BossCogAreaAttack) + taskMgr.doMethodLater(7.36, self.__reviveGoons, self.uniqueName('reviveGoons')) + else: + self.__doDirectedAttack() + if self.heldObject == None and not self.waitingForHelmet: + self.waitForNextHelmet() + + def __reviveGoons(self, task): + for goon in self.goons: + if goon.state == 'Stunned': + goon.request('Recovery') + + def __doDirectedAttack(self): + if self.toonsToAttack: + toonId = self.toonsToAttack.pop(0) + while toonId not in self.involvedToons: + if not self.toonsToAttack: + self.b_setAttackCode(ToontownGlobals.BossCogNoAttack) + return + toonId = self.toonsToAttack.pop(0) + + self.toonsToAttack.append(toonId) + self.b_setAttackCode(ToontownGlobals.BossCogSlowDirectedAttack, toonId) + + def reprieveToon(self, avId): + if avId in self.toonsToAttack: + i = self.toonsToAttack.index(avId) + del self.toonsToAttack[i] + self.toonsToAttack.append(avId) + + def makeTreasure(self, goon): + if self.state != 'BattleThree': + return + pos = goon.getPos(self) + v = Vec3(pos[0], pos[1], 0.0) + if not v.normalize(): + v = Vec3(1, 0, 0) + v = v * 27 + angle = random.uniform(0.0, 2.0 * math.pi) + radius = 10 + dx = radius * math.cos(angle) + dy = radius * math.sin(angle) + fpos = self.scene.getRelativePoint(self, Point3(v[0] + dx, v[1] + dy, 0)) + if goon.strength <= 10: + style = ToontownGlobals.ToontownCentral + healAmount = 3 + elif goon.strength <= 15: + style = random.choice([ToontownGlobals.DonaldsDock, ToontownGlobals.DaisyGardens, ToontownGlobals.MinniesMelodyland]) + healAmount = 10 + else: + style = random.choice([ToontownGlobals.TheBrrrgh, ToontownGlobals.DonaldsDreamland]) + healAmount = 12 + if self.recycledTreasures: + treasure = self.recycledTreasures.pop(0) + treasure.d_setGrab(0) + treasure.b_setGoonId(goon.doId) + treasure.b_setStyle(style) + treasure.b_setPosition(pos[0], pos[1], 0) + treasure.b_setFinalPosition(fpos[0], fpos[1], 0) + else: + treasure = DistributedCashbotBossTreasureAI.DistributedCashbotBossTreasureAI(self.air, self, goon, style, fpos[0], fpos[1], 0) + treasure.generateWithRequired(self.zoneId) + treasure.healAmount = healAmount + self.treasures[treasure.doId] = treasure + + def grabAttempt(self, avId, treasureId): + av = self.air.doId2do.get(avId) + if not av: + return + treasure = self.treasures.get(treasureId) + if treasure: + if treasure.validAvatar(av): + del self.treasures[treasureId] + treasure.d_setGrab(avId) + self.grabbingTreasures[treasureId] = treasure + taskMgr.doMethodLater(5, self.__recycleTreasure, treasure.uniqueName('recycleTreasure'), extraArgs=[treasure]) + else: + treasure.d_setReject() + + def __recycleTreasure(self, treasure): + if treasure.doId in self.grabbingTreasures: + del self.grabbingTreasures[treasure.doId] + self.recycledTreasures.append(treasure) + + def deleteAllTreasures(self): + for treasure in self.treasures.values(): + treasure.requestDelete() + + self.treasures = {} + for treasure in self.grabbingTreasures.values(): + taskMgr.remove(treasure.uniqueName('recycleTreasure')) + treasure.requestDelete() + + self.grabbingTreasures = {} + for treasure in self.recycledTreasures: + treasure.requestDelete() + + self.recycledTreasures = [] + + def getMaxGoons(self): + t = self.getBattleThreeTime() + if t <= 1.0: + return self.maxGoons + elif t <= 1.1: + return self.maxGoons + 1 + elif t <= 1.2: + return self.maxGoons + 2 + elif t <= 1.3: + return self.maxGoons + 3 + elif t <= 1.4: + return self.maxGoons + 4 + else: + return self.maxGoons + 8 + + def makeGoon(self, side = None): + if side == None: + side = random.choice(['EmergeA', 'EmergeB']) + goon = self.__chooseOldGoon() + if goon == None: + if len(self.goons) >= self.getMaxGoons(): + return + goon = DistributedCashbotBossGoonAI.DistributedCashbotBossGoonAI(self.air, self) + goon.generateWithRequired(self.zoneId) + self.goons.append(goon) + if self.getBattleThreeTime() > 1.0: + goon.STUN_TIME = 4 + goon.b_setupGoon(velocity=8, hFov=90, attackRadius=20, strength=30, scale=1.8) + else: + goon.STUN_TIME = self.progressValue(30, 8) + goon.b_setupGoon(velocity=self.progressRandomValue(3, 7), hFov=self.progressRandomValue(70, 80), attackRadius=self.progressRandomValue(6, 15), strength=int(self.progressRandomValue(5, 25)), scale=self.progressRandomValue(0.5, 1.5)) + goon.request(side) + return + + def __chooseOldGoon(self): + for goon in self.goons: + if goon.state == 'Off': + return goon + + def waitForNextGoon(self, delayTime): + currState = self.getCurrentOrNextState() + if currState == 'BattleThree': + taskName = self.uniqueName('NextGoon') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.doNextGoon, taskName) + + def stopGoons(self): + taskName = self.uniqueName('NextGoon') + taskMgr.remove(taskName) + taskMgr.remove(self.uniqueName('reviveGoons')) + + def doNextGoon(self, task): + if self.attackCode != ToontownGlobals.BossCogDizzy: + self.makeGoon() + delayTime = self.progressValue(10, 2) + self.waitForNextGoon(delayTime) + + def waitForNextHelmet(self): + currState = self.getCurrentOrNextState() + if currState == 'BattleThree': + taskName = self.uniqueName('NextHelmet') + taskMgr.remove(taskName) + delayTime = self.progressValue(45, 15) + taskMgr.doMethodLater(delayTime, self.__donHelmet, taskName) + self.waitingForHelmet = 1 + + def __donHelmet(self, task): + self.waitingForHelmet = 0 + if self.heldObject == None: + safe = self.safes[0] + safe.request('Grabbed', self.doId, self.doId) + self.heldObject = safe + return + + def stopHelmets(self): + self.waitingForHelmet = 0 + taskName = self.uniqueName('NextHelmet') + taskMgr.remove(taskName) + + def acceptHelmetFrom(self, avId): + now = globalClock.getFrameTime() + then = self.avatarHelmets.get(avId, None) + if then == None or now - then > 300: + self.avatarHelmets[avId] = now + return 1 + return 0 + + def magicWordHit(self, damage, avId): + if self.heldObject: + self.heldObject.demand('Dropped', avId, self.doId) + self.heldObject.avoidHelmet = 1 + self.heldObject = None + self.waitForNextHelmet() + else: + self.recordHit(damage) + return + + def magicWordReset(self): + if self.state == 'BattleThree': + self.__resetBattleThreeObjects() + + def magicWordResetGoons(self): + if self.state == 'BattleThree': + if self.goons != None: + for goon in self.goons: + goon.request('Off') + goon.requestDelete() + + self.goons = None + self.__makeBattleThreeObjects() + return + + def recordHit(self, damage): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'recordHit from unknown avatar'): + return + if self.state != 'BattleThree': + return + self.b_setBossDamage(self.bossDamage + damage) + if self.bossDamage >= self.bossMaxDamage: + self.b_setState('Victory') + elif self.attackCode != ToontownGlobals.BossCogDizzy: + if damage >= ToontownGlobals.CashbotBossKnockoutDamage: + self.b_setAttackCode(ToontownGlobals.BossCogDizzy) + self.stopHelmets() + else: + self.b_setAttackCode(ToontownGlobals.BossCogNoAttack) + self.stopHelmets() + self.waitForNextHelmet() + + def b_setBossDamage(self, bossDamage): + self.d_setBossDamage(bossDamage) + self.setBossDamage(bossDamage) + + def setBossDamage(self, bossDamage): + self.reportToonHealth() + self.bossDamage = bossDamage + + def d_setBossDamage(self, bossDamage): + self.sendUpdate('setBossDamage', [bossDamage]) + + def d_setRewardId(self, rewardId): + self.sendUpdate('setRewardId', [rewardId]) + + def applyReward(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.involvedToons and avId not in self.rewardedToons: + self.rewardedToons.append(avId) + toon = self.air.doId2do.get(avId) + if toon: + toon.doResistanceEffect(self.rewardId) + + def enterOff(self): + DistributedBossCogAI.DistributedBossCogAI.enterOff(self) + self.rewardedToons = [] + + def exitOff(self): + DistributedBossCogAI.DistributedBossCogAI.exitOff(self) + + def enterIntroduction(self): + DistributedBossCogAI.DistributedBossCogAI.enterIntroduction(self) + self.__makeBattleThreeObjects() + self.__resetBattleThreeObjects() + + def exitIntroduction(self): + DistributedBossCogAI.DistributedBossCogAI.exitIntroduction(self) + self.__deleteBattleThreeObjects() + + def enterPrepareBattleThree(self): + self.resetBattles() + self.__makeBattleThreeObjects() + self.__resetBattleThreeObjects() + self.barrier = self.beginBarrier('PrepareBattleThree', self.involvedToons, 55, self.__donePrepareBattleThree) + + def __donePrepareBattleThree(self, avIds): + self.b_setState('BattleThree') + + def exitPrepareBattleThree(self): + if self.newState != 'BattleThree': + self.__deleteBattleThreeObjects() + self.ignoreBarrier(self.barrier) + + def enterBattleThree(self): + self.setPosHpr(*ToontownGlobals.CashbotBossBattleThreePosHpr) + self.__makeBattleThreeObjects() + self.__resetBattleThreeObjects() + self.reportToonHealth() + self.toonsToAttack = self.involvedToons[:] + random.shuffle(self.toonsToAttack) + self.b_setBossDamage(0) + self.battleThreeStart = globalClock.getFrameTime() + self.resetBattles() + self.waitForNextAttack(15) + self.waitForNextHelmet() + self.makeGoon(side='EmergeA') + self.makeGoon(side='EmergeB') + taskName = self.uniqueName('NextGoon') + taskMgr.remove(taskName) + taskMgr.doMethodLater(2, self.__doInitialGoons, taskName) + + def __doInitialGoons(self, task): + self.makeGoon(side='EmergeA') + self.makeGoon(side='EmergeB') + self.waitForNextGoon(10) + + def exitBattleThree(self): + helmetName = self.uniqueName('helmet') + taskMgr.remove(helmetName) + if self.newState != 'Victory': + self.__deleteBattleThreeObjects() + self.deleteAllTreasures() + self.stopAttacks() + self.stopGoons() + self.stopHelmets() + self.heldObject = None + return + + def enterVictory(self): + self.resetBattles() + self.suitsKilled.append({'type': None, + 'level': None, + 'track': self.dna.dept, + 'isSkelecog': 0, + 'isForeman': 0, + 'isBoss': 1, + 'isSupervisor': 0, + 'isVirtual': 0, + 'activeToons': self.involvedToons[:]}) + self.addStats() + self.barrier = self.beginBarrier('Victory', self.involvedToons, 30, self.__doneVictory) + return + + def __doneVictory(self, avIds): + self.d_setBattleExperience() + self.b_setState('Reward') + BattleExperienceAI.assignRewards(self.involvedToons, self.toonSkillPtsGained, self.suitsKilled, ToontownGlobals.dept2cogHQ(self.dept), self.helpfulToons) + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + toon.addResistanceMessage(self.rewardId) + toon.b_promote(self.deptIndex) + + def exitVictory(self): + self.__deleteBattleThreeObjects() + + def enterEpilogue(self): + DistributedBossCogAI.DistributedBossCogAI.enterEpilogue(self) + self.d_setRewardId(self.rewardId) + + +@magicWord(category=CATEGORY_ADMINISTRATOR) +def restartCraneRound(): + """ + Restarts the crane round in the CFO. + """ + invoker = spellbook.getInvoker() + boss = None + for do in simbase.air.doId2do.values(): + if isinstance(do, DistributedCashbotBossAI): + if invoker.doId in do.involvedToons: + boss = do + break + if not boss: + return "You aren't in a CFO!" + boss.exitIntroduction() + boss.b_setState('PrepareBattleThree') + boss.b_setState('BattleThree') + return 'Restarting the crane round...' + + +@magicWord(category=CATEGORY_ADMINISTRATOR) +def skipCFO(): + """ + Skips to the final round of the CFO. + """ + invoker = spellbook.getInvoker() + boss = None + for do in simbase.air.doId2do.values(): + if isinstance(do, DistributedCashbotBossAI): + if invoker.doId in do.involvedToons: + boss = do + break + if not boss: + return "You aren't in a CFO!" + if boss.state in ('PrepareBattleThree', 'BattleThree'): + return "You can't skip this round." + boss.exitIntroduction() + boss.b_setState('PrepareBattleThree') + return 'Skipping the first round...' + +@magicWord(category=CATEGORY_ADMINISTRATOR) +def killCFO(): + """ + Kills the CFO. + """ + invoker = spellbook.getInvoker() + boss = None + for do in simbase.air.doId2do.values(): + if isinstance(do, DistributedCashbotBossAI): + if invoker.doId in do.involvedToons: + boss = do + break + if not boss: + return "You aren't in a CFO" + boss.b_setState('Victory') + return 'Killed CFO.' diff --git a/toontown/suit/DistributedCashbotBossGoon.py b/toontown/suit/DistributedCashbotBossGoon.py new file mode 100755 index 00000000..2a3065cb --- /dev/null +++ b/toontown/suit/DistributedCashbotBossGoon.py @@ -0,0 +1,254 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.task.TaskManagerGlobal import * +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +import GoonGlobals +from direct.task.Task import Task +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from toontown.coghq import DistributedCashbotBossObject +from direct.showbase import PythonUtil +import DistributedGoon + +class DistributedCashbotBossGoon(DistributedGoon.DistributedGoon, DistributedCashbotBossObject.DistributedCashbotBossObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCashbotBossGoon') + walkGrabZ = -3.6 + stunGrabZ = -2.2 + wiggleFreeTime = 2 + craneFrictionCoef = 0.15 + craneSlideSpeed = 10 + craneRotateSpeed = 20 + + def __init__(self, cr): + DistributedCashbotBossObject.DistributedCashbotBossObject.__init__(self, cr) + DistributedGoon.DistributedGoon.__init__(self, cr) + self.target = None + self.arrivalTime = None + self.flyToMagnetSfx = loader.loadSfx('phase_5/audio/sfx/TL_rake_throw_only.ogg') + self.hitMagnetSfx = loader.loadSfx('phase_4/audio/sfx/AA_drop_anvil_miss.ogg') + self.toMagnetSoundInterval = Sequence(SoundInterval(self.flyToMagnetSfx, duration=ToontownGlobals.CashbotBossToMagnetTime, node=self), SoundInterval(self.hitMagnetSfx, node=self)) + self.hitFloorSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_flowerpot.ogg') + self.hitFloorSoundInterval = SoundInterval(self.hitFloorSfx, duration=1.0, node=self) + self.wiggleSfx = loader.loadSfx('phase_5/audio/sfx/SA_finger_wag.ogg') + return + + def generate(self): + DistributedCashbotBossObject.DistributedCashbotBossObject.generate(self) + DistributedGoon.DistributedGoon.generate(self) + + def announceGenerate(self): + DistributedCashbotBossObject.DistributedCashbotBossObject.announceGenerate(self) + self.setupPhysics('goon') + DistributedGoon.DistributedGoon.announceGenerate(self) + self.name = 'goon-%s' % self.doId + self.setName(self.name) + self.setTag('doId', str(self.doId)) + self.collisionNode.setName('goon') + cs = CollisionSphere(0, 0, 4, 4) + self.collisionNode.addSolid(cs) + self.collisionNode.setIntoCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CashbotBossObjectBitmask) + self.wiggleTaskName = self.uniqueName('wiggleTask') + self.wiggleFreeName = self.uniqueName('wiggleFree') + self.boss.goons.append(self) + self.reparentTo(render) + + def disable(self): + i = self.boss.goons.index(self) + del self.boss.goons[i] + DistributedGoon.DistributedGoon.disable(self) + DistributedCashbotBossObject.DistributedCashbotBossObject.disable(self) + + def delete(self): + DistributedGoon.DistributedGoon.delete(self) + DistributedCashbotBossObject.DistributedCashbotBossObject.delete(self) + + def hideShadows(self): + self.dropShadow.hide() + + def showShadows(self): + self.dropShadow.show() + + def getMinImpact(self): + return ToontownGlobals.CashbotBossGoonImpact + + def doHitBoss(self, impact): + self.d_hitBoss(impact) + self.b_destroyGoon() + + def __startWalk(self): + self.__stopWalk() + if self.target: + now = globalClock.getFrameTime() + availableTime = self.arrivalTime - now + if availableTime > 0: + origH = self.getH() + h = PythonUtil.fitDestAngle2Src(origH, self.targetH) + delta = abs(h - origH) + turnTime = delta / (self.velocity * 5) + dist = Vec3(self.target - self.getPos()).length() + walkTime = dist / self.velocity + denom = turnTime + walkTime + if denom != 0: + timeCompress = availableTime / denom + self.walkTrack = Sequence(self.hprInterval(turnTime * timeCompress, VBase3(h, 0, 0)), self.posInterval(walkTime * timeCompress, self.target)) + self.walkTrack.start() + else: + self.setPos(self.target) + self.setH(self.targetH) + + def __stopWalk(self): + if self.walkTrack: + self.walkTrack.pause() + self.walkTrack = None + return + + def __wiggleTask(self, task): + elapsed = globalClock.getFrameTime() - self.wiggleStart + h = math.sin(elapsed * 17) * 5 + p = math.sin(elapsed * 29) * 10 + self.crane.wiggleMagnet.setHpr(h, p, 0) + return Task.cont + + def __wiggleFree(self, task): + self.crane.releaseObject() + self.stashCollisions() + return Task.done + + def fellOut(self): + self.b_destroyGoon() + + def handleToonDetect(self, collEntry = None): + if self.boss.localToonIsSafe: + return + DistributedGoon.DistributedGoon.handleToonDetect(self, collEntry) + + def prepareGrab(self): + DistributedCashbotBossObject.DistributedCashbotBossObject.prepareGrab(self) + if self.isStunned or self.boss.localToonIsSafe: + self.pose('collapse', 48) + self.grabPos = (0, 0, self.stunGrabZ * self.scale) + else: + self.setPlayRate(4, 'walk') + self.loop('walk') + self.grabPos = (0, 0, self.walkGrabZ * self.scale) + self.wiggleStart = globalClock.getFrameTime() + taskMgr.add(self.__wiggleTask, self.wiggleTaskName) + base.sfxPlayer.playSfx(self.wiggleSfx, node=self) + if self.avId == localAvatar.doId: + taskMgr.doMethodLater(self.wiggleFreeTime, self.__wiggleFree, self.wiggleFreeName) + self.radar.hide() + + def prepareRelease(self): + DistributedCashbotBossObject.DistributedCashbotBossObject.prepareRelease(self) + self.crane.wiggleMagnet.setHpr(0, 0, 0) + taskMgr.remove(self.wiggleTaskName) + taskMgr.remove(self.wiggleFreeName) + self.setPlayRate(self.animMultiplier, 'walk') + + def setObjectState(self, state, avId, craneId): + if state == 'W': + self.demand('Walk') + elif state == 'B': + if self.state != 'Battle': + self.demand('Battle') + elif state == 'S': + if self.state != 'Stunned': + self.demand('Stunned') + elif state == 'R': + if self.state != 'Recovery': + self.demand('Recovery') + elif state == 'a': + self.demand('EmergeA') + elif state == 'b': + self.demand('EmergeB') + else: + DistributedCashbotBossObject.DistributedCashbotBossObject.setObjectState(self, state, avId, craneId) + + def setTarget(self, x, y, h, arrivalTime): + self.target = Point3(x, y, 0) + self.targetH = h + now = globalClock.getFrameTime() + self.arrivalTime = globalClockDelta.networkToLocalTime(arrivalTime, now) + if self.state == 'Walk': + self.__startWalk() + + def d_destroyGoon(self): + self.sendUpdate('destroyGoon') + + def b_destroyGoon(self): + if not self.isDead: + self.d_destroyGoon() + self.destroyGoon() + + def destroyGoon(self): + if not self.isDead: + self.playCrushMovie(None, None) + self.demand('Off') + return + + def enterOff(self): + DistributedGoon.DistributedGoon.enterOff(self) + DistributedCashbotBossObject.DistributedCashbotBossObject.enterOff(self) + + def exitOff(self): + DistributedCashbotBossObject.DistributedCashbotBossObject.exitOff(self) + DistributedGoon.DistributedGoon.exitOff(self) + + def enterWalk(self, avId = None, ts = 0): + self.startToonDetect() + self.isStunned = 0 + self.__startWalk() + self.loop('walk', 0) + self.unstashCollisions() + + def exitWalk(self): + self.__stopWalk() + self.stopToonDetect() + self.stop() + + def enterEmergeA(self): + self.undead() + self.reparentTo(render) + self.stopToonDetect() + self.boss.doorA.request('open') + self.radar.hide() + self.__startWalk() + self.loop('walk', 0) + + def exitEmergeA(self): + if self.boss.doorA: + self.boss.doorA.request('close') + self.radar.show() + self.__stopWalk() + + def enterEmergeB(self): + self.undead() + self.reparentTo(render) + self.stopToonDetect() + self.boss.doorB.request('open') + self.radar.hide() + self.__startWalk() + self.loop('walk', 0) + + def exitEmergeB(self): + if self.boss.doorB: + self.boss.doorB.request('close') + self.radar.show() + self.__stopWalk() + + def enterBattle(self, avId = None, ts = 0): + DistributedGoon.DistributedGoon.enterBattle(self, avId, ts) + avatar = base.cr.doId2do.get(avId) + if avatar: + messenger.send('exitCrane') + avatar.stunToon() + self.unstashCollisions() + + def enterStunned(self, ts = 0): + DistributedGoon.DistributedGoon.enterStunned(self, ts) + self.unstashCollisions() + + def enterRecovery(self, ts = 0, pauseTime = 0): + DistributedGoon.DistributedGoon.enterRecovery(self, ts, pauseTime) + self.unstashCollisions() diff --git a/toontown/suit/DistributedCashbotBossGoonAI.py b/toontown/suit/DistributedCashbotBossGoonAI.py new file mode 100755 index 00000000..02d4085b --- /dev/null +++ b/toontown/suit/DistributedCashbotBossGoonAI.py @@ -0,0 +1,304 @@ +from panda3d.core import * +from direct.task.TaskManagerGlobal import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import GoonGlobals +from direct.task.Task import Task +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from toontown.coghq import DistributedCashbotBossObjectAI +from direct.showbase import PythonUtil +import DistributedGoonAI +import math +import random + +class DistributedCashbotBossGoonAI(DistributedGoonAI.DistributedGoonAI, DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI): + legLength = 10 + directionTable = [(0, 15), + (10, 10), + (-10, 10), + (20, 8), + (-20, 8), + (40, 5), + (-40, 5), + (60, 4), + (-60, 4), + (80, 3), + (-80, 3), + (120, 2), + (-120, 2), + (180, 1)] + offMask = BitMask32(0) + onMask = CollisionNode.getDefaultCollideMask() + + def __init__(self, air, boss): + DistributedGoonAI.DistributedGoonAI.__init__(self, air, 0) + DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.__init__(self, air, boss) + cn = CollisionNode('tubeNode') + self.tube = CollisionTube(0, 0, 0, 0, 0, 0, 2) + cn.addSolid(self.tube) + self.tubeNode = cn + self.tubeNodePath = self.attachNewNode(self.tubeNode) + self.feelers = [] + cn = CollisionNode('feelerNode') + self.feelerLength = self.legLength * 1.5 + feelerStart = 1 + for heading, weight in self.directionTable: + rad = deg2Rad(heading) + x = -math.sin(rad) + y = math.cos(rad) + seg = CollisionSegment(x * feelerStart, y * feelerStart, 0, x * self.feelerLength, y * self.feelerLength, 0) + cn.addSolid(seg) + self.feelers.append(seg) + + cn.setIntoCollideMask(self.offMask) + self.feelerNodePath = self.attachNewNode(cn) + self.isWalking = 0 + self.cTrav = CollisionTraverser('goon') + self.cQueue = CollisionHandlerQueue() + self.cTrav.addCollider(self.feelerNodePath, self.cQueue) + + def requestBattle(self, pauseTime): + avId = self.air.getAvatarIdFromSender() + avatar = self.air.doId2do.get(avId) + if avatar: + self.boss.damageToon(avatar, self.strength) + DistributedGoonAI.DistributedGoonAI.requestBattle(self, pauseTime) + + def sendMovie(self, type, avId = 0, pauseTime = 0): + if type == GoonGlobals.GOON_MOVIE_WALK: + self.demand('Walk') + elif type == GoonGlobals.GOON_MOVIE_BATTLE: + self.demand('Battle') + elif type == GoonGlobals.GOON_MOVIE_STUNNED: + self.demand('Stunned') + elif type == GoonGlobals.GOON_MOVIE_RECOVERY: + self.demand('Recovery') + else: + self.notify.warning('Ignoring movie type %s' % type) + + def __chooseTarget(self, extraDelay = 0): + direction = self.__chooseDirection() + if direction == None: + self.target = None + self.arrivalTime = None + self.b_destroyGoon() + return + heading, dist = direction + dist = min(dist, self.legLength) + targetH = PythonUtil.reduceAngle(self.getH() + heading) + origH = self.getH() + h = PythonUtil.fitDestAngle2Src(origH, targetH) + delta = abs(h - origH) + turnTime = delta / (self.velocity * 5) + walkTime = dist / self.velocity + self.setH(targetH) + self.target = self.boss.scene.getRelativePoint(self, Point3(0, dist, 0)) + self.departureTime = globalClock.getFrameTime() + self.arrivalTime = self.departureTime + turnTime + walkTime + extraDelay + self.d_setTarget(self.target[0], self.target[1], h, globalClockDelta.localToNetworkTime(self.arrivalTime)) + return + + def __chooseDirection(self): + self.tubeNode.setIntoCollideMask(self.offMask) + self.cTrav.traverse(self.boss.scene) + self.tubeNode.setIntoCollideMask(self.onMask) + entries = {} + self.cQueue.sortEntries() + for i in xrange(self.cQueue.getNumEntries() - 1, -1, -1): + entry = self.cQueue.getEntry(i) + dist = Vec3(entry.getSurfacePoint(self)).length() + if dist < 1.2: + dist = 0 + entries[entry.getFrom()] = dist + + netScore = 0 + scoreTable = [] + for i in xrange(len(self.directionTable)): + heading, weight = self.directionTable[i] + seg = self.feelers[i] + dist = entries.get(seg, self.feelerLength) + score = dist * weight + netScore += score + scoreTable.append(score) + + if netScore == 0: + self.notify.info('Could not find a path for %s' % self.doId) + return None + s = random.uniform(0, netScore) + for i in xrange(len(self.directionTable)): + s -= scoreTable[i] + if s <= 0: + heading, weight = self.directionTable[i] + seg = self.feelers[i] + dist = entries.get(seg, self.feelerLength) + return (heading, dist) + + self.notify.warning('Fell off end of weighted table.') + return (0, self.legLength) + + def __startWalk(self): + if self.arrivalTime == None: + return + now = globalClock.getFrameTime() + availableTime = self.arrivalTime - now + if availableTime > 0: + point = self.getRelativePoint(self.boss.scene, self.target) + self.tube.setPointB(point) + self.node().resetPrevTransform() + taskMgr.doMethodLater(availableTime, self.__reachedTarget, self.uniqueName('reachedTarget')) + self.isWalking = 1 + else: + self.__reachedTarget(None) + return + + def __stopWalk(self, pauseTime = None): + if self.isWalking: + taskMgr.remove(self.uniqueName('reachedTarget')) + if pauseTime == None: + now = globalClock.getFrameTime() + t = (now - self.departureTime) / (self.arrivalTime - self.departureTime) + else: + t = pauseTime / (self.arrivalTime - self.departureTime) + t = min(t, 1.0) + pos = self.getPos() + self.setPos(pos + (self.target - pos) * t) + self.tube.setPointB(0, 0, 0) + self.isWalking = 0 + return + + def __reachedTarget(self, task): + self.__stopWalk() + self.__chooseTarget() + self.__startWalk() + + def __recoverWalk(self, task): + self.demand('Walk') + return Task.done + + def doFree(self, task): + DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.doFree(self, task) + self.demand('Walk') + return Task.done + + def requestStunned(self, pauseTime): + avId = self.air.getAvatarIdFromSender() + if avId not in self.boss.involvedToons: + return + if self.state == 'Stunned' or self.state == 'Grabbed': + return + self.__stopWalk(pauseTime) + self.boss.makeTreasure(self) + DistributedGoonAI.DistributedGoonAI.requestStunned(self, pauseTime) + + def hitBoss(self, impact): + avId = self.air.getAvatarIdFromSender() + self.validate(avId, impact <= 1.0, 'invalid hitBoss impact %s' % impact) + if avId not in self.boss.involvedToons: + return + if self.state == 'Dropped' or self.state == 'Grabbed': + if not self.boss.heldObject: + damage = int(impact * 25 * self.scale) + self.boss.recordHit(max(damage, 2)) + self.b_destroyGoon() + + def d_setTarget(self, x, y, h, arrivalTime): + self.sendUpdate('setTarget', [x, + y, + h, + arrivalTime]) + + def d_destroyGoon(self): + self.sendUpdate('destroyGoon') + + def b_destroyGoon(self): + self.d_destroyGoon() + self.destroyGoon() + + def destroyGoon(self): + self.demand('Off') + + def enterOff(self): + self.tubeNodePath.stash() + self.feelerNodePath.stash() + + def exitOff(self): + self.tubeNodePath.unstash() + self.feelerNodePath.unstash() + + def enterGrabbed(self, avId, craneId): + DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.enterGrabbed(self, avId, craneId) + taskMgr.remove(self.taskName('recovery')) + taskMgr.remove(self.taskName('resumeWalk')) + + def enterWalk(self): + self.avId = 0 + self.craneId = 0 + self.__chooseTarget() + self.__startWalk() + self.d_setObjectState('W', 0, 0) + + def exitWalk(self): + self.__stopWalk() + + def enterEmergeA(self): + self.avId = 0 + self.craneId = 0 + h = 0 + dist = 15 + pos = self.boss.getPos() + walkTime = dist / self.velocity + self.setPosHpr(pos[0], pos[1], pos[2], h, 0, 0) + self.d_setPosHpr(pos[0], pos[1], pos[2], h, 0, 0) + self.target = self.boss.scene.getRelativePoint(self, Point3(0, dist, 0)) + self.departureTime = globalClock.getFrameTime() + self.arrivalTime = self.departureTime + walkTime + self.d_setTarget(self.target[0], self.target[1], h, globalClockDelta.localToNetworkTime(self.arrivalTime)) + self.__startWalk() + self.d_setObjectState('a', 0, 0) + taskMgr.doMethodLater(walkTime, self.__recoverWalk, self.uniqueName('recoverWalk')) + + def exitEmergeA(self): + self.__stopWalk() + taskMgr.remove(self.uniqueName('recoverWalk')) + + def enterEmergeB(self): + self.avId = 0 + self.craneId = 0 + h = 180 + dist = 15 + pos = self.boss.getPos() + walkTime = dist / self.velocity + self.setPosHpr(pos[0], pos[1], pos[2], h, 0, 0) + self.d_setPosHpr(pos[0], pos[1], pos[2], h, 0, 0) + self.target = self.boss.scene.getRelativePoint(self, Point3(0, dist, 0)) + self.departureTime = globalClock.getFrameTime() + self.arrivalTime = self.departureTime + walkTime + self.d_setTarget(self.target[0], self.target[1], h, globalClockDelta.localToNetworkTime(self.arrivalTime)) + self.__startWalk() + self.d_setObjectState('b', 0, 0) + taskMgr.doMethodLater(walkTime, self.__recoverWalk, self.uniqueName('recoverWalk')) + + def exitEmergeB(self): + self.__stopWalk() + taskMgr.remove(self.uniqueName('recoverWalk')) + + def enterBattle(self): + self.d_setObjectState('B', 0, 0) + + def exitBattle(self): + taskMgr.remove(self.taskName('resumeWalk')) + + def enterStunned(self): + self.d_setObjectState('S', 0, 0) + + def exitStunned(self): + taskMgr.remove(self.taskName('recovery')) + + def enterRecovery(self): + self.d_setObjectState('R', 0, 0) + taskMgr.doMethodLater(2.0, self.__recoverWalk, self.uniqueName('recoverWalk')) + + def exitRecovery(self): + self.__stopWalk() + taskMgr.remove(self.uniqueName('recoverWalk')) diff --git a/toontown/suit/DistributedFactorySuit.py b/toontown/suit/DistributedFactorySuit.py new file mode 100755 index 00000000..0a267fa5 --- /dev/null +++ b/toontown/suit/DistributedFactorySuit.py @@ -0,0 +1,414 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +import DistributedSuitBase +from direct.task.Task import Task +import random +from toontown.toonbase import ToontownGlobals +from otp.level import LevelConstants +from toontown.distributed.DelayDeletable import DelayDeletable + +class DistributedFactorySuit(DistributedSuitBase.DistributedSuitBase, DelayDeletable): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFactorySuit') + + def __init__(self, cr): + try: + self.DistributedSuit_initialized + except: + self.DistributedSuit_initialized = 1 + DistributedSuitBase.DistributedSuitBase.__init__(self, cr) + self.fsm = ClassicFSM.ClassicFSM('DistributedSuit', [State.State('Off', self.enterOff, self.exitOff, ['Walk', 'Battle']), + State.State('Walk', self.enterWalk, self.exitWalk, ['WaitForBattle', 'Battle', 'Chase']), + State.State('Chase', self.enterChase, self.exitChase, ['WaitForBattle', 'Battle', 'Return']), + State.State('Return', self.enterReturn, self.exitReturn, ['WaitForBattle', 'Battle', 'Walk']), + State.State('Battle', self.enterBattle, self.exitBattle, ['Walk', 'Chase', 'Return']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['Battle'])], 'Off', 'Off') + self.path = None + self.walkTrack = None + self.chaseTrack = None + self.returnTrack = None + self.fsm.enterInitialState() + self.chasing = 0 + self.paused = 0 + self.pauseTime = 0 + self.velocity = 3 + self.factoryRequest = None + + return + + def generate(self): + DistributedSuitBase.DistributedSuitBase.generate(self) + + def setLevelDoId(self, levelDoId): + self.notify.debug('setLevelDoId(%s)' % levelDoId) + self.levelDoId = levelDoId + + def setCogId(self, cogId): + self.cogId = cogId + + def setReserve(self, reserve): + self.reserve = reserve + + def denyBattle(self): + self.notify.warning('denyBattle()') + place = self.cr.playGame.getPlace() + if place.fsm.getCurrentState().getName() == 'WaitForBattle': + place.setState('walk') + + def doReparent(self): + self.notify.debug('Suit requesting reparenting') + if not hasattr(self, 'factory'): + self.notify.warning('no factory, get Redmond to look at DistributedFactorySuit.announceGenerate()') + self.factory.requestReparent(self, self.spec['parentEntId']) + if self.pathEntId: + self.factory.setEntityCreateCallback(self.pathEntId, self.setPath) + else: + self.setPath() + + def setCogSpec(self, spec): + self.spec = spec + self.setPos(spec['pos']) + self.setH(spec['h']) + self.originalPos = spec['pos'] + self.escapePos = spec['pos'] + self.pathEntId = spec['path'] + self.behavior = spec['behavior'] + self.skeleton = spec['skeleton'] + self.revives = spec.get('revives') + self.boss = spec['boss'] + if self.reserve: + self.reparentTo(hidden) + else: + self.doReparent() + + def comeOutOfReserve(self): + self.doReparent() + + def getCogSpec(self, cogId): + if self.reserve: + return self.factory.getReserveCogSpec(cogId) + else: + return self.factory.getCogSpec(cogId) + + def announceGenerate(self): + self.notify.debug('announceGenerate %s' % self.doId) + + def onFactoryGenerate(factoryList, self = self): + self.factory = factoryList[0] + + def onFactoryReady(self = self): + self.notify.debug('factory ready, read spec') + spec = self.getCogSpec(self.cogId) + self.setCogSpec(spec) + self.factoryRequest = None + return + + self.factory.setEntityCreateCallback(LevelConstants.LevelMgrEntId, onFactoryReady) + + self.factoryRequest = self.cr.relatedObjectMgr.requestObjects([self.levelDoId], onFactoryGenerate) + DistributedSuitBase.DistributedSuitBase.announceGenerate(self) + + def disable(self): + self.ignoreAll() + if self.factoryRequest is not None: + self.cr.relatedObjectMgr.abortRequest(self.factoryRequest) + self.factoryRequest = None + self.notify.debug('DistributedSuit %d: disabling' % self.getDoId()) + self.setState('Off') + if self.walkTrack: + del self.walkTrack + self.walkTrack = None + DistributedSuitBase.DistributedSuitBase.disable(self) + taskMgr.remove(self.taskName('returnTask')) + taskMgr.remove(self.taskName('checkStray')) + taskMgr.remove(self.taskName('chaseTask')) + return + + def delete(self): + try: + self.DistributedSuit_deleted + except: + self.DistributedSuit_deleted = 1 + self.notify.debug('DistributedSuit %d: deleting' % self.getDoId()) + del self.fsm + DistributedSuitBase.DistributedSuitBase.delete(self) + + def d_requestBattle(self, pos, hpr): + self.cr.playGame.getPlace().setState('WaitForBattle') + self.factory.lockVisibility(zoneNum=self.factory.getEntityZoneEntId(self.spec['parentEntId'])) + self.sendUpdate('requestBattle', [pos[0], + pos[1], + pos[2], + hpr[0], + hpr[1], + hpr[2]]) + + def handleBattleBlockerCollision(self): + self.__handleToonCollision(None) + return + + def __handleToonCollision(self, collEntry): + if collEntry: + if collEntry.getFromNodePath().getParent().getKey() != localAvatar.getKey(): + return + if hasattr(self, 'factory') and hasattr(self.factory, 'lastToonZone'): + factoryZone = self.factory.lastToonZone + unitsBelow = self.getPos(render)[2] - base.localAvatar.getPos(render)[2] + if factoryZone == 24 and unitsBelow > 10.0: + self.notify.warning('Ignoring toon collision in %d from %f below.' % (factoryZone, unitsBelow)) + return + if not base.localAvatar.wantBattles: + return + toonId = base.localAvatar.getDoId() + self.notify.debug('Distributed suit %d: requesting a Battle with toon: %d' % (self.doId, toonId)) + self.d_requestBattle(self.getPos(), self.getHpr()) + self.setState('WaitForBattle') + return None + + def setPath(self): + self.notify.debug('setPath %s' % self.doId) + if self.pathEntId != None: + parent = self.factory.entities.get(self.spec['parentEntId']) + self.path = self.factory.entities.get(self.pathEntId) + self.idealPathNode = self.path.attachNewNode('idealPath') + self.reparentTo(self.idealPathNode) + self.setPos(0, 0, 0) + self.path.reparentTo(parent) + self.walkTrack = self.path.makePathTrack(self.idealPathNode, self.velocity, self.uniqueName('suitWalk')) + self.setState('Walk') + return + + def initializeBodyCollisions(self, collIdStr): + DistributedSuitBase.DistributedSuitBase.initializeBodyCollisions(self, collIdStr) + self.sSphere = CollisionSphere(0.0, 0.0, 0.0, 15) + name = self.uniqueName('toonSphere') + self.sSphereNode = CollisionNode(name) + self.sSphereNode.addSolid(self.sSphere) + self.sSphereNodePath = self.attachNewNode(self.sSphereNode) + self.sSphereNodePath.hide() + self.sSphereBitMask = ToontownGlobals.WallBitmask + self.sSphereNode.setCollideMask(self.sSphereBitMask) + self.sSphere.setTangible(0) + self.accept('enter' + name, self.__handleToonCollision) + + def enableBattleDetect(self, name, handler): + DistributedSuitBase.DistributedSuitBase.enableBattleDetect(self, name, handler) + self.lookForToon(1) + + def disableBattleDetect(self): + DistributedSuitBase.DistributedSuitBase.disableBattleDetect(self) + self.lookForToon(0) + + def subclassManagesParent(self): + return 1 + + def enterWalk(self, ts = 0): + self.enableBattleDetect('walk', self.__handleToonCollision) + if self.path: + if self.walkTrack: + self.walkTrack.loop() + self.walkTrack.pause() + if self.paused: + self.walkTrack.setT(self.pauseTime) + else: + self.walkTrack.setT(ts) + self.walkTrack.resume() + self.loop('walk', 0) + self.paused = 0 + else: + self.loop('neutral', 0) + + def exitWalk(self): + self.disableBattleDetect() + if self.walkTrack: + self.pauseTime = self.walkTrack.pause() + self.paused = 1 + + def lookForToon(self, on = 1): + if self.behavior in ['chase']: + if on: + self.accept(self.uniqueName('entertoonSphere'), self.__handleToonAlert) + else: + self.ignore(self.uniqueName('entertoonSphere')) + + def __handleToonAlert(self, collEntry): + self.notify.debug('%s: ahah! i saw you' % self.doId) + toonZ = base.localAvatar.getZ(render) + suitZ = self.getZ(render) + dZ = abs(toonZ - suitZ) + if dZ < 8.0: + self.sendUpdate('setAlert', [base.localAvatar.doId]) + + def resumePath(self, state): + self.setState('Walk') + + def enterChase(self): + self.enableBattleDetect('walk', self.__handleToonCollision) + self.startChaseTime = globalClock.getFrameTime() + self.startCheckStrayTask(1) + self.startChaseTask() + + def exitChase(self): + self.disableBattleDetect() + taskMgr.remove(self.taskName('chaseTask')) + if self.chaseTrack: + self.chaseTrack.pause() + del self.chaseTrack + self.chaseTrack = None + self.chasing = 0 + self.startCheckStrayTask(0) + return + + def setConfrontToon(self, avId): + self.notify.debug('DistributedFactorySuit.setConfrontToon %d' % avId) + self.chasing = avId + self.setState('Chase') + + def startChaseTask(self, delay = 0): + self.notify.debug('DistributedFactorySuit.startChaseTask delay=%s' % delay) + taskMgr.remove(self.taskName('chaseTask')) + taskMgr.doMethodLater(delay, self.chaseTask, self.taskName('chaseTask')) + + def chaseTask(self, task): + if not self.chasing: + return Task.done + av = base.cr.doId2do.get(self.chasing, None) + if not av: + self.notify.warning("avatar %s isn't here to chase" % self.chasing) + return Task.done + if globalClock.getFrameTime() - self.startChaseTime > 3.0: + self.setReturn() + return Task.done + toonPos = av.getPos(self.getParent()) + suitPos = self.getPos() + distance = Vec3(suitPos - toonPos).length() + if self.chaseTrack: + self.chaseTrack.pause() + del self.chaseTrack + self.chaseTrack = None + import random + rand1 = 0.5 + rand2 = 0.5 + rand3 = 0.5 + targetPos = Vec3(toonPos[0] + 4.0 * (rand1 - 0.5), toonPos[1] + 4.0 * (rand2 - 0.5), suitPos[2]) + track = Sequence(Func(self.headsUp, targetPos[0], targetPos[1], targetPos[2]), Func(self.loop, 'walk', 0)) + chaseSpeed = 4.0 + duration = distance / chaseSpeed + track.extend([LerpPosInterval(self, duration=duration, pos=Point3(targetPos), startPos=Point3(suitPos))]) + self.chaseTrack = track + self.chaseTrack.start() + self.startChaseTask(1.0) + return + + def startCheckStrayTask(self, on = 1): + taskMgr.remove(self.taskName('checkStray')) + if on: + taskMgr.add(self.checkStrayTask, self.taskName('checkStray')) + + def checkStrayTask(self, task): + curPos = self.getPos() + distance = Vec3(curPos - self.originalPos).length() + if distance > 10.0: + self.sendUpdate('setStrayed', []) + + def enterReturn(self): + self.enableBattleDetect('walk', self.__handleToonCollision) + self.lookForToon(0) + self.startReturnTask() + + def exitReturn(self): + self.disableBattleDetect() + taskMgr.remove(self.taskName('checkStray')) + taskMgr.remove(self.taskName('returnTask')) + if self.returnTrack: + self.returnTrack.pause() + self.returnTrack = None + return + + def setReturn(self): + self.notify.debug('DistributedFactorySuit.setReturn') + self.setState('Return') + + def startReturnTask(self, delay = 0): + taskMgr.remove(self.taskName('returnTask')) + taskMgr.doMethodLater(delay, self.returnTask, self.taskName('returnTask')) + + def returnTask(self, task): + self.factory.requestReparent(self, self.spec['parentEntId']) + if self.returnTrack: + self.returnTrack.pause() + self.returnTrack = None + if self.path: + targetPos = VBase3(0, 0, 0) + else: + targetPos = self.originalPos + track = Sequence(Func(self.headsUp, targetPos[0], targetPos[1], targetPos[2]), Func(self.loop, 'walk', 0)) + curPos = self.getPos() + distance = Vec3(curPos - targetPos).length() + duration = distance / 3.0 + track.append(LerpPosInterval(self, duration=duration, pos=Point3(targetPos), startPos=Point3(curPos))) + track.append(Func(self.returnDone)) + self.returnTrack = track + self.returnTrack.start() + return + + def returnDone(self): + self.setHpr(self.spec['h'], 0, 0) + self.setState('Walk') + if not self.path: + self.loop('neutral') + + def setActive(self, active): + if active: + self.setState('Walk') + else: + self.setState('Off') + + def disableBattleDetect(self): + if self.battleDetectName: + self.ignore('enter' + self.battleDetectName) + self.battleDetectName = None + if self.collNodePath: + self.collNodePath.removeNode() + self.collNodePath = None + return + + def disableBodyCollisions(self): + self.disableBattleDetect() + self.enableRaycast(0) + if self.cRayNodePath: + self.cRayNodePath.removeNode() + if hasattr(self, 'cRayNode'): + del self.cRayNode + if hasattr(self, 'cRay'): + del self.cRay + if hasattr(self, 'lifter'): + del self.lifter + + def removeCollisions(self): + self.enableRaycast(0) + self.cRay = None + self.cRayNode = None + self.cRayNodePath = None + self.lifter = None + self.cTrav = None + return + + def setVirtual(self, isVirtual = 1): + self.virtual = isVirtual + if self.virtual: + actorNode = self.find('**/__Actor_modelRoot') + actorCollection = actorNode.findAllMatches('*') + parts = () + for thingIndex in xrange(0, actorCollection.getNumPaths()): + thing = actorCollection[thingIndex] + if thing.getName() not in ('joint_attachMeter', 'joint_nameTag', 'def_nameTag'): + thing.setColorScale(1.0, 0.0, 0.0, 1.0) + thing.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + thing.setDepthWrite(False) + thing.setBin('fixed', 1) + + def getVirtual(self): + return self.virtual diff --git a/toontown/suit/DistributedFactorySuitAI.py b/toontown/suit/DistributedFactorySuitAI.py new file mode 100755 index 00000000..4b0adc22 --- /dev/null +++ b/toontown/suit/DistributedFactorySuitAI.py @@ -0,0 +1,110 @@ +from otp.ai.AIBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import SuitBattleGlobals +import DistributedSuitBaseAI +import SuitDialog + +class DistributedFactorySuitAI(DistributedSuitBaseAI.DistributedSuitBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFactorySuitAI') + + def __init__(self, air, suitPlanner): + DistributedSuitBaseAI.DistributedSuitBaseAI.__init__(self, air, suitPlanner) + self.blocker = None + self.battleCellIndex = None + self.chasing = 0 + self.factoryGone = 0 + return + + def factoryIsGoingDown(self): + self.factoryGone = 1 + + def delete(self): + if not self.factoryGone: + self.setBattleCellIndex(None) + del self.blocker + self.ignoreAll() + DistributedSuitBaseAI.DistributedSuitBaseAI.delete(self) + return + + def setLevelDoId(self, levelDoId): + self.levelDoId = levelDoId + + def getLevelDoId(self): + return self.levelDoId + + def setCogId(self, cogId): + self.cogId = cogId + + def getCogId(self): + return self.cogId + + def setReserve(self, reserve): + self.reserve = reserve + + def getReserve(self): + return self.reserve + + def requestBattle(self, x, y, z, h, p, r): + toonId = self.air.getAvatarIdFromSender() + if self.notify.getDebug(): + self.notify.debug(str(self.getDoId()) + str(self.zoneId) + ': request battle with toon: %d' % toonId) + self.confrontPos = Point3(x, y, z) + self.confrontHpr = Vec3(h, p, r) + if self.sp.requestBattle(self, toonId): + if self.notify.getDebug(): + self.notify.debug('Suit %d requesting battle in zone %d with toon %d' % (self.getDoId(), self.zoneId, toonId)) + else: + if self.notify.getDebug(): + self.notify.debug('requestBattle from suit %d, toon %d- denied by battle manager' % (toonId, self.getDoId())) + self.b_setBrushOff(SuitDialog.getBrushOffIndex(self.getStyleName())) + self.d_denyBattle(toonId) + + def getConfrontPosHpr(self): + return (self.confrontPos, self.confrontHpr) + + def setBattleCellIndex(self, battleCellIndex): + self.sp.suitBattleCellChange(self, oldCell=self.battleCellIndex, newCell=battleCellIndex) + self.battleCellIndex = battleCellIndex + self.attachBattleBlocker() + self.accept(self.sp.getBattleBlockerEvent(self.battleCellIndex), self.attachBattleBlocker) + + def getBattleCellIndex(self): + return self.battleCellIndex + + def attachBattleBlocker(self): + blocker = self.sp.battleMgr.battleBlockers.get(self.battleCellIndex) + self.blocker = blocker + + def setAlert(self, avId): + if avId == self.air.getAvatarIdFromSender(): + av = self.air.doId2do.get(avId) + if av: + self.chasing = avId + if self.sp.battleMgr.cellHasBattle(self.battleCellIndex): + pass + else: + self.sendUpdate('setConfrontToon', [avId]) + + def setStrayed(self): + if self.chasing > 0: + self.chasing = 0 + self.sendUpdate('setReturn', []) + + def resume(self): + self.notify.debug('Suit %s resume' % self.doId) + if self.currHP <= 0: + messenger.send(self.getDeathEvent()) + self.notify.debug('Suit %s dead after resume' % self.doId) + self.requestRemoval() + else: + self.sendUpdate('setReturn', []) + return None + + def isForeman(self): + return self.boss + + def setVirtual(self, isVirtual = 1): + self.virtual = isVirtual + + def getVirtual(self): + return self.virtual diff --git a/toontown/suit/DistributedGoon.py b/toontown/suit/DistributedGoon.py new file mode 100755 index 00000000..f40b7bc4 --- /dev/null +++ b/toontown/suit/DistributedGoon.py @@ -0,0 +1,479 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import * +from GoonGlobals import * +from direct.fsm import FSM +from direct.distributed import ClockDelta +from otp.level import BasicEntities +from otp.level import DistributedEntity +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import DistributedCrushableEntity +from toontown.toonbase import ToontownGlobals +from toontown.coghq import MovingPlatform +import Goon +from direct.task.Task import Task +from otp.level import PathEntity +import GoonDeath +import random + +class DistributedGoon(DistributedCrushableEntity.DistributedCrushableEntity, Goon.Goon, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGoon') + + def __init__(self, cr): + try: + self.DistributedGoon_initialized + except: + self.DistributedGoon_initialized = 1 + DistributedCrushableEntity.DistributedCrushableEntity.__init__(self, cr) + Goon.Goon.__init__(self) + FSM.FSM.__init__(self, 'DistributedGoon') + + self.setCacheable(0) + self.rayNode = None + self.checkForWalls = 0 + self.triggerEvent = None + self.animTrack = None + self.walkTrack = None + self.pauseTime = 0 + self.paused = 0 + self.path = None + self.dir = GOON_FORWARD + self.animMultiplier = 1.0 + self.isDead = 0 + self.isStunned = 0 + self.collapseSound = loader.loadSfx('phase_9/audio/sfx/CHQ_GOON_hunker_down.ogg') + self.recoverSound = loader.loadSfx('phase_9/audio/sfx/CHQ_GOON_rattle_shake.ogg') + self.attackSound = loader.loadSfx('phase_9/audio/sfx/CHQ_GOON_tractor_beam_alarmed.ogg') + return + + def announceGenerate(self): + DistributedCrushableEntity.DistributedCrushableEntity.announceGenerate(self) + if hasattr(self, 'goonType'): + self.initGoon(self.goonType) + else: + self.initGoon('pg') + self.scaleRadar() + self.colorHat() + if self.level: + self.initClipPlanes() + self.level.setEntityCreateCallback(self.parentEntId, self.initPath) + else: + self.enterOff() + taskMgr.doMethodLater(0.1, self.makeCollidable, self.taskName('makeCollidable')) + self.setGoonScale(self.scale) + self.animMultiplier = self.velocity / (ANIM_WALK_RATE * self.scale) + self.setPlayRate(self.animMultiplier, 'walk') + + def initPath(self): + self.enterOff() + self.setPath() + taskMgr.doMethodLater(0.1, self.makeCollidable, self.taskName('makeCollidable')) + + def makeCollidable(self, task): + self.initCollisions() + self.initializeBodyCollisions() + triggerName = self.uniqueName('GoonTrigger') + self.trigger.setName(triggerName) + self.triggerEvent = 'enter%s' % triggerName + self.startToonDetect() + + def generate(self): + DistributedCrushableEntity.DistributedCrushableEntity.generate(self) + + def scaleRadar(self): + Goon.Goon.scaleRadar(self) + self.trigger = self.radar.find('**/trigger') + triggerName = self.uniqueName('GoonTrigger') + self.trigger.setName(triggerName) + + def initCollisions(self): + self.cSphere = CollisionSphere(0.0, 0.0, 1.0, 1.0) + self.cSphereNode = CollisionNode('goonCollSphere') + self.cSphereNode.addSolid(self.cSphere) + self.cSphereNodePath = self.head.attachNewNode(self.cSphereNode) + self.cSphereNodePath.hide() + self.cSphereBitMask = ToontownGlobals.WallBitmask + self.cSphereNode.setCollideMask(self.cSphereBitMask) + self.cSphere.setTangible(1) + self.sSphere = CollisionSphere(0.0, 0.0, self.headHeight + 0.8, 1.2) + self.sSphereNode = CollisionNode('toonSphere') + self.sSphereNode.addSolid(self.sSphere) + self.sSphereNodePath = self.head.attachNewNode(self.sSphereNode) + self.sSphereNodePath.hide() + self.sSphereBitMask = ToontownGlobals.WallBitmask + self.sSphereNode.setCollideMask(self.sSphereBitMask) + self.sSphere.setTangible(1) + + def initializeBodyCollisions(self): + self.cSphereNode.setName(self.uniqueName('goonCollSphere')) + self.sSphereNode.setName(self.uniqueName('toonSphere')) + self.accept(self.uniqueName('entertoonSphere'), self.__handleStun) + + def disableBodyCollisions(self): + self.ignore(self.uniqueName('entertoonSphere')) + + def deleteCollisions(self): + if hasattr(self, 'sSphereNodePath'): + self.sSphereNodePath.removeNode() + del self.sSphereNodePath + del self.sSphereNode + del self.sSphere + if hasattr(self, 'cSphereNodePath'): + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + del self.cSphereNode + del self.cSphere + + def initClipPlanes(self): + zoneNum = self.getZoneEntity().getZoneNum() + clipList = self.level.goonClipPlanes.get(zoneNum) + if clipList: + for id in clipList: + clipPlane = self.level.getEntity(id) + self.radar.setClipPlane(clipPlane.getPlane()) + + def disableClipPlanes(self): + if self.radar: + self.radar.clearClipPlane() + + def setPath(self): + self.path = self.level.getEntity(self.parentEntId) + if self.walkTrack: + self.walkTrack.pause() + self.walkTrack = None + self.walkTrack = self.path.makePathTrack(self, self.velocity, self.uniqueName('goonWalk'), turnTime=T_TURN) + if self.gridId != None: + self.sendUpdate('setParameterize', [self.path.pos[0], + self.path.pos[1], + self.path.pos[2], + self.path.pathIndex]) + + def disable(self): + self.notify.debug('DistributedGoon %d: disabling' % self.getDoId()) + self.ignoreAll() + self.stopToonDetect() + taskMgr.remove(self.taskName('resumeWalk')) + taskMgr.remove(self.taskName('recoveryDone')) + self.request('Off') + self.disableBodyCollisions() + self.disableClipPlanes() + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + if self.walkTrack: + self.walkTrack.pause() + self.walkTrack = None + DistributedCrushableEntity.DistributedCrushableEntity.disable(self) + return + + def delete(self): + try: + self.DistributedSuit_deleted + except: + self.DistributedSuit_deleted = 1 + self.notify.debug('DistributedGoon %d: deleting' % self.getDoId()) + taskMgr.remove(self.taskName('makeCollidable')) + self.deleteCollisions() + self.head.removeNode() + del self.head + del self.attackSound + del self.collapseSound + del self.recoverSound + DistributedCrushableEntity.DistributedCrushableEntity.delete(self) + Goon.Goon.delete(self) + + def enterOff(self, *args): + self.nametag3d.stash() + self.nametag.destroy() + self.hide() + self.isStunned = 0 + self.isDead = 0 + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + if self.walkTrack: + self.walkTrack.pause() + self.walkTrack = None + + def exitOff(self): + self.show() + + def enterWalk(self, avId = None, ts = 0): + self.notify.debug('enterWalk, ts = %s' % ts) + self.startToonDetect() + self.loop('walk', 0) + self.isStunned = 0 + if self.path: + if not self.walkTrack: + self.walkTrack = self.path.makePathTrack(self, self.velocity, self.uniqueName('goonWalk'), turnTime=T_TURN) + self.startWalk(ts) + + def startWalk(self, ts): + tOffset = ts % self.walkTrack.getDuration() + self.walkTrack.loop() + self.walkTrack.pause() + self.walkTrack.setT(tOffset) + self.walkTrack.resume() + self.paused = 0 + + def exitWalk(self): + self.notify.debug('exitWalk') + self.stopToonDetect() + if self.walkTrack and not self.paused: + self.pauseTime = self.walkTrack.pause() + self.paused = 1 + self.stop() + + def enterBattle(self, avId = None, ts = 0): + self.notify.debug('enterBattle') + self.stopToonDetect() + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + self.isStunned = 0 + if avId == base.localAvatar.doId: + if self.level: + self.level.b_setOuch(self.strength) + self.animTrack = self.makeAttackTrack() + self.animTrack.loop() + return + + def exitBattle(self): + self.notify.debug('exitBattle') + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + self.head.setHpr(0, 0, 0) + return + + def enterStunned(self, ts = 0): + self.ignore(self.uniqueName('entertoonSphere')) + self.isStunned = 1 + self.notify.debug('enterStunned') + if self.radar: + self.radar.hide() + self.animTrack = Parallel(Sequence(ActorInterval(self, 'collapse'), Func(self.pose, 'collapse', 48)), SoundInterval(self.collapseSound, node=self)) + self.animTrack.start(ts) + + def exitStunned(self): + self.notify.debug('exitStunned') + if self.radar: + self.radar.show() + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + self.accept(self.uniqueName('entertoonSphere'), self.__handleStun) + return + + def enterRecovery(self, ts = 0, pauseTime = 0): + self.notify.debug('enterRecovery') + self.ignore(self.uniqueName('entertoonSphere')) + self.isStunned = 1 + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + self.animTrack = self.getRecoveryTrack() + duration = self.animTrack.getDuration() + self.animTrack.start(ts) + delay = max(0, duration - ts) + taskMgr.remove(self.taskName('recoveryDone')) + taskMgr.doMethodLater(delay, self.recoveryDone, self.taskName('recoveryDone'), extraArgs=(pauseTime,)) + return + + def getRecoveryTrack(self): + return Parallel(Sequence(ActorInterval(self, 'recovery'), Func(self.pose, 'recovery', 96)), Func(base.playSfx, self.recoverSound, node=self)) + + def recoveryDone(self, pauseTime): + self.request('Walk', None, pauseTime) + return + + def exitRecovery(self): + self.notify.debug('exitRecovery') + taskMgr.remove(self.taskName('recoveryDone')) + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + self.accept(self.uniqueName('entertoonSphere'), self.__handleStun) + return + + def makeAttackTrack(self): + h = self.head.getH() + freakDeg = 60 + hatZ = self.hat.getZ() + track = Parallel(Sequence(LerpColorScaleInterval(self.eye, 0.2, Vec4(1, 0, 0, 1)), LerpColorScaleInterval(self.eye, 0.2, Vec4(0, 0, 1, 1)), LerpColorScaleInterval(self.eye, 0.2, Vec4(1, 0, 0, 1)), LerpColorScaleInterval(self.eye, 0.2, Vec4(0, 0, 1, 1)), Func(self.eye.clearColorScale)), SoundInterval(self.attackSound, node=self, volume=0.4)) + return track + + def doDetect(self): + pass + + def doAttack(self, avId): + pass + + def __startResumeWalkTask(self, ts): + resumeTime = 1.5 + if ts < resumeTime: + taskMgr.remove(self.taskName('resumeWalk')) + taskMgr.doMethodLater(resumeTime - ts, self.request, self.taskName('resumeWalk'), extraArgs=('Walk',)) + else: + self.request('Walk', ts - resumeTime) + + def __reverseWalk(self, task): + self.request('Walk') + return Task.done + + def __startRecoverTask(self, ts): + stunTime = 4.0 + if ts < stunTime: + taskMgr.remove(self.taskName('resumeWalk')) + taskMgr.doMethodLater(stunTime - ts, self.request, self.taskName('resumeWalk'), extraArgs=('Recovery',)) + else: + self.request('Recovery', ts - stunTime) + + def startToonDetect(self): + self.radar.show() + if self.triggerEvent: + self.accept(self.triggerEvent, self.handleToonDetect) + + def stopToonDetect(self): + if self.triggerEvent: + self.ignore(self.triggerEvent) + + def handleToonDetect(self, collEntry = None): + if base.localAvatar.isStunned: + return + if self.state == 'Off': + return + self.stopToonDetect() + self.request('Battle', base.localAvatar.doId) + if self.walkTrack: + self.pauseTime = self.walkTrack.pause() + self.paused = 1 + if self.dclass and hasattr(self, 'dclass'): + self.sendUpdate('requestBattle', [self.pauseTime]) + else: + self.notify.info('Goon deleted and still trying to call handleToonDetect()') + + def __handleStun(self, collEntry): + toon = base.localAvatar + if toon: + toonDistance = self.getPos(toon).length() + if toonDistance > self.attackRadius: + self.notify.warning('Stunned a good, but outside of attack radius') + return + else: + self.request('Stunned') + if self.walkTrack: + self.pauseTime = self.walkTrack.pause() + self.paused = 1 + self.sendUpdate('requestStunned', [self.pauseTime]) + + def setMovie(self, mode, avId, pauseTime, timestamp): + if self.isDead: + return + ts = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.notify.debug('%s: setMovie(%s,%s,%s,%s)' % (self.doId, + mode, + avId, + pauseTime, + ts)) + if mode == GOON_MOVIE_BATTLE: + if self.state != 'Battle': + self.request('Battle', avId, ts) + elif mode == GOON_MOVIE_STUNNED: + if self.state != 'Stunned': + toon = base.cr.doId2do.get(avId) + if toon: + toonDistance = self.getPos(toon).length() + if toonDistance > self.attackRadius: + self.notify.warning('Stunned a goon, but outside of attack radius') + return + else: + self.request('Stunned', ts) + elif mode == GOON_MOVIE_RECOVERY: + if self.state != 'Recovery': + self.request('Recovery', ts, pauseTime) + elif mode == GOON_MOVIE_SYNC: + if self.walkTrack: + self.walkTrack.pause() + self.paused = 1 + if self.state == 'Off' or self.state == 'Walk': + self.request('Walk', avId, pauseTime + ts) + else: + if self.walkTrack: + self.walkTrack.pause() + self.walkTrack = None + self.request('Walk', avId, pauseTime + ts) + return + + def stunToon(self, avId): + self.notify.debug('stunToon(%s)' % avId) + av = base.cr.doId2do.get(avId) + if av != None: + av.stunToon() + return + + def isLocalToon(self, avId): + if avId == base.localAvatar.doId: + return 1 + return 0 + + def playCrushMovie(self, crusherId, axis): + goonPos = self.getPos() + sx = random.uniform(0.3, 0.8) * self.scale + sz = random.uniform(0.3, 0.8) * self.scale + crushTrack = Sequence(GoonDeath.createGoonExplosion(self.getParent(), goonPos, VBase3(sx, 1, sz)), name=self.uniqueName('crushTrack'), autoFinish=1) + self.dead() + crushTrack.start() + + def setVelocity(self, velocity): + self.velocity = velocity + self.animMultiplier = velocity / (ANIM_WALK_RATE * self.scale) + self.setPlayRate(self.animMultiplier, 'walk') + + def dead(self): + if not self.isDead and not self.isDisabled(): + self.stopToonDetect() + self.detachNode() + self.isDead = 1 + + def undead(self): + if self.isDead: + self.startToonDetect() + self.reparentTo(render) + self.isDead = 0 + + def resync(self): + if not self.isDead: + self.sendUpdate('requestResync') + + def setHFov(self, hFov): + if hFov != self.hFov: + self.hFov = hFov + if self.isGenerated(): + self.scaleRadar() + + def setAttackRadius(self, attackRadius): + if attackRadius != self.attackRadius: + self.attackRadius = attackRadius + if self.isGenerated(): + self.scaleRadar() + + def setStrength(self, strength): + if strength != self.strength: + self.strength = strength + if self.isGenerated(): + self.colorHat() + + def setGoonScale(self, scale): + if scale != self.scale: + self.scale = scale + if self.isGenerated(): + self.getGeomNode().setScale(self.scale) + self.scaleRadar() + + def setupGoon(self, velocity, hFov, attackRadius, strength, scale): + self.setVelocity(velocity) + self.setHFov(hFov) + self.setAttackRadius(attackRadius) + self.setStrength(strength) + self.setGoonScale(scale) diff --git a/toontown/suit/DistributedGoonAI.py b/toontown/suit/DistributedGoonAI.py new file mode 100755 index 00000000..0060959c --- /dev/null +++ b/toontown/suit/DistributedGoonAI.py @@ -0,0 +1,247 @@ +from otp.ai.AIBaseGlobal import * +from GoonGlobals import * +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import SuitBattleGlobals +from toontown.coghq import DistributedCrushableEntityAI +import GoonPathData +from direct.distributed import ClockDelta +import random +from direct.task import Task + +class DistributedGoonAI(DistributedCrushableEntityAI.DistributedCrushableEntityAI): + UPDATE_TIMESTAMP_INTERVAL = 180.0 + STUN_TIME = 4 + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGoonAI') + + def __init__(self, level, entId): + self.hFov = 70 + self.attackRadius = 15 + self.strength = 15 + self.velocity = 4 + self.scale = 1.0 + DistributedCrushableEntityAI.DistributedCrushableEntityAI.__init__(self, level, entId) + self.curInd = 0 + self.dir = GOON_FORWARD + self.parameterized = 0 + self.width = 1 + self.crushed = 0 + self.pathStartTime = None + self.walkTrackTime = 0.0 + self.totalPathTime = 1.0 + return + + def delete(self): + taskMgr.remove(self.taskName('sync')) + taskMgr.remove(self.taskName('resumeWalk')) + taskMgr.remove(self.taskName('recovery')) + taskMgr.remove(self.taskName('deleteGoon')) + DistributedCrushableEntityAI.DistributedCrushableEntityAI.delete(self) + + def generate(self): + self.notify.debug('generate') + DistributedCrushableEntityAI.DistributedCrushableEntityAI.generate(self) + if self.level: + self.level.setEntityCreateCallback(self.parentEntId, self.startGoon) + + def startGoon(self): + ts = 100 * random.random() + self.sendMovie(GOON_MOVIE_WALK, pauseTime=ts) + + def requestBattle(self, pauseTime): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('requestBattle, avId = %s' % avId) + self.sendMovie(GOON_MOVIE_BATTLE, avId, pauseTime) + taskMgr.remove(self.taskName('resumeWalk')) + taskMgr.doMethodLater(5, self.sendMovie, self.taskName('resumeWalk'), extraArgs=(GOON_MOVIE_WALK, avId, pauseTime)) + + def requestStunned(self, pauseTime): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('requestStunned(%s)' % avId) + self.sendMovie(GOON_MOVIE_STUNNED, avId, pauseTime) + taskMgr.remove(self.taskName('recovery')) + taskMgr.doMethodLater(self.STUN_TIME, self.sendMovie, self.taskName('recovery'), extraArgs=(GOON_MOVIE_RECOVERY, avId, pauseTime)) + + def requestResync(self, task = None): + self.notify.debug('resyncGoon') + self.sendMovie(GOON_MOVIE_SYNC) + self.updateGrid() + + def sendMovie(self, type, avId = 0, pauseTime = 0): + if type == GOON_MOVIE_WALK: + self.pathStartTime = globalClock.getFrameTime() + if self.parameterized: + self.walkTrackTime = pauseTime % self.totalPathTime + else: + self.walkTrackTime = pauseTime + self.notify.debug('GOON_MOVIE_WALK doId = %s, pathStartTime = %s, walkTrackTime = %s' % (self.doId, self.pathStartTime, self.walkTrackTime)) + if type == GOON_MOVIE_WALK or type == GOON_MOVIE_SYNC: + curT = globalClock.getFrameTime() + elapsedT = curT - self.pathStartTime + pathT = self.walkTrackTime + elapsedT + if self.parameterized: + pathT = pathT % self.totalPathTime + self.sendUpdate('setMovie', [type, + avId, + pathT, + ClockDelta.globalClockDelta.localToNetworkTime(curT)]) + taskMgr.remove(self.taskName('sync')) + taskMgr.doMethodLater(self.UPDATE_TIMESTAMP_INTERVAL, self.requestResync, self.taskName('sync'), extraArgs=None) + else: + self.sendUpdate('setMovie', [type, + avId, + pauseTime, + ClockDelta.globalClockDelta.getFrameNetworkTime()]) + return + + def updateGrid(self): + if not self.parameterized: + return + if self.grid and hasattr(self, 'entId'): + self.grid.removeObject(self.entId) + if not self.crushed: + curT = globalClock.getFrameTime() + if self.pathStartTime: + elapsedT = curT - self.pathStartTime + else: + elapsedT = 0 + pathT = (self.walkTrackTime + elapsedT) % self.totalPathTime + pt = self.getPathPoint(pathT) + if not self.grid.addObjectByPos(self.entId, pt): + self.notify.warning("updateGrid: couldn't put goon in grid") + + def doCrush(self, crusherId, axis): + self.notify.debug('doCrush %s' % self.doId) + DistributedCrushableEntityAI.DistributedCrushableEntityAI.doCrush(self, crusherId, axis) + self.crushed = 1 + self.grid.removeObject(self.entId) + taskMgr.doMethodLater(5.0, self.doDelete, self.taskName('deleteGoon')) + + def doDelete(self, task): + self.requestDelete() + return Task.done + + def setParameterize(self, x, y, z, pathIndex): + if not hasattr(self, 'level') or not self.level: + return + pathId = GoonPathData.taskZoneId2pathId[self.level.getTaskZoneId()] + pathData = GoonPathData.Paths[pathId] + self.pathOrigin = Vec3(x, y, z) + if pathIndex > len(pathData): + self.notify.warning('Invalid path index given, using 0') + pathIndex = 0 + pathPts = pathData[pathIndex] + [pathData[pathIndex][0]] + invVel = 1.0 / self.velocity + t = 0 + self.tSeg = [t] + self.pathSeg = [] + for i in xrange(len(pathPts) - 1): + ptA = pathPts[i] + ptB = pathPts[i + 1] + t += T_TURN + self.tSeg.append(t) + self.pathSeg.append([Vec3(0, 0, 0), 0, ptA]) + seg = Vec3(ptB - ptA) + segLength = seg.length() + t += invVel * segLength + self.tSeg.append(t) + self.pathSeg.append([seg, segLength, ptA]) + + self.totalPathTime = t + self.pathPts = pathPts + self.parameterized = 1 + + def getPathPoint(self, t): + for i in xrange(len(self.tSeg) - 1): + if t >= self.tSeg[i] and t < self.tSeg[i + 1]: + tSeg = t - self.tSeg[i] + t = tSeg / (self.tSeg[i + 1] - self.tSeg[i]) + seg = self.pathSeg[i][0] + ptA = self.pathSeg[i][2] + pt = ptA + seg * t + return self.pathOrigin + pt + + self.notify.warning("Couldn't find valid path point") + return Vec3(0, 0, 0) + + def b_setVelocity(self, velocity): + self.setVelocity(velocity) + self.d_setVelocity(velocity) + + def setVelocity(self, velocity): + self.velocity = velocity + + def d_setVelocity(self, velocity): + self.sendUpdate('setVelocity', [velocity]) + + def getVelocity(self): + return self.velocity + + def b_setHFov(self, hFov): + self.setHFov(hFov) + self.d_setHFov(hFov) + + def setHFov(self, hFov): + self.hFov = hFov + + def d_setHFov(self, hFov): + self.sendUpdate('setHFov', [hFov]) + + def getHFov(self): + return self.hFov + + def b_setAttackRadius(self, attackRadius): + self.setAttackRadius(attackRadius) + self.d_setAttackRadius(attackRadius) + + def setAttackRadius(self, attackRadius): + self.attackRadius = attackRadius + + def d_setAttackRadius(self, attackRadius): + self.sendUpdate('setAttackRadius', [attackRadius]) + + def getAttackRadius(self): + return self.attackRadius + + def b_setStrength(self, strength): + self.setStrength(strength) + self.d_setStrength(strength) + + def setStrength(self, strength): + self.strength = strength + + def d_setStrength(self, strength): + self.sendUpdate('setStrength', [strength]) + + def getStrength(self): + return self.strength + + def b_setGoonScale(self, scale): + self.setGoonScale(scale) + self.d_setGoonScale(scale) + + def setGoonScale(self, scale): + self.scale = scale + + def d_setGoonScale(self, scale): + self.sendUpdate('setGoonScale', [scale]) + + def getGoonScale(self): + return self.scale + + def b_setupGoon(self, velocity, hFov, attackRadius, strength, scale): + self.setupGoon(velocity, hFov, attackRadius, strength, scale) + self.d_setupGoon(velocity, hFov, attackRadius, strength, scale) + + def setupGoon(self, velocity, hFov, attackRadius, strength, scale): + self.setVelocity(velocity) + self.setHFov(hFov) + self.setAttackRadius(attackRadius) + self.setStrength(strength) + self.setGoonScale(scale) + + def d_setupGoon(self, velocity, hFov, attackRadius, strength, scale): + self.sendUpdate('setupGoon', [velocity, + hFov, + attackRadius, + strength, + scale]) diff --git a/toontown/suit/DistributedGridGoon.py b/toontown/suit/DistributedGridGoon.py new file mode 100755 index 00000000..d9d49a12 --- /dev/null +++ b/toontown/suit/DistributedGridGoon.py @@ -0,0 +1,52 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import * +from direct.directnotify import DirectNotifyGlobal +import DistributedGoon +from toontown.toonbase import ToontownGlobals +from toontown.coghq import MovingPlatform + +class DistributedGridGoon(DistributedGoon.DistributedGoon): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGoon') + + def __init__(self, cr, type = 'sg'): + try: + self.DistributedGridGoon_initialized + except: + self.DistributedGridGoon_initialized = 1 + DistributedGoon.DistributedGoon.__init__(self, cr, type) + + def generate(self): + DistributedGoon.DistributedGoon.generate(self) + self.ignore(self.uniqueName('wallHit')) + self.mazeWalkTrack = None + return + + def delete(self): + if self.mazeWalkTrack: + self.mazeWalkTrack.pause() + del self.mazeWalkTrack + DistributedGoon.DistributedGoon.delete(self) + + def setH(self, h): + self.h = h + + def setPathPts(self, xi, yi, zi, xf, yf, zf): + self.notify.debug('setPathPts') + if self.mazeWalkTrack: + self.mazeWalkTrack.pause() + del self.mazeWalkTrack + self.mazeWalkTrack = None + curPos = Point3(xi, yi, zi) + nextPos = Point3(xf, yf, zf) + distance = Vec3(curPos - nextPos).length() + duration = distance / self.velocity + self.mazeWalkTrack = Sequence(Func(self.headsUp, nextPos[0], nextPos[1], nextPos[2]), LerpPosInterval(self, duration=duration, pos=nextPos, startPos=curPos), name=self.uniqueName('mazeWalkTrack')) + self.mazeWalkTrack.start() + return + + def enterWalk(self, avId = None, ts = 0): + pass + + def exitWalk(self): + pass diff --git a/toontown/suit/DistributedGridGoonAI.py b/toontown/suit/DistributedGridGoonAI.py new file mode 100755 index 00000000..dc4f4960 --- /dev/null +++ b/toontown/suit/DistributedGridGoonAI.py @@ -0,0 +1,8 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.suit.DistributedGoonAI import DistributedGoonAI + +class DistributedGridGoonAI(DistributedGoonAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedGridGoonAI") + + def setPathPts(self, todo0, todo1, todo2, todo3, todo4, todo5): + pass diff --git a/toontown/suit/DistributedLawbotBoss.py b/toontown/suit/DistributedLawbotBoss.py new file mode 100755 index 00000000..279af16b --- /dev/null +++ b/toontown/suit/DistributedLawbotBoss.py @@ -0,0 +1,1824 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.directutil import Mopath +from direct.distributed.ClockDelta import * +from direct.fsm import FSM +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from direct.showbase.PythonUtil import Functor +from direct.showbase.PythonUtil import StackTrace +from direct.showbase.ShowBase import * +from direct.task import Task +import math +from panda3d.core import * +import random + +import DistributedBossCog +import SuitDNA +from toontown.battle import BattleBase +from toontown.battle import MovieToonVictory +from toontown.battle import RewardPanel +from toontown.battle import SuitBattleGlobals +from toontown.battle.BattleProps import * +from toontown.building import ElevatorConstants +from toontown.building import ElevatorUtils +from toontown.coghq import CogDisguiseGlobals +from toontown.distributed import DelayDelete +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals +from toontown.toon import NPCToons +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownTimer + + +OneBossCog = None + + +class DistributedLawbotBoss(DistributedBossCog.DistributedBossCog, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotBoss') + debugPositions = False + + def __init__(self, cr): + self.notify.debug('----- __init___') + DistributedBossCog.DistributedBossCog.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedLawbotBoss') + self.lawyers = [] + self.lawyerRequest = None + self.bossDamage = 0 + self.attackCode = None + self.attackAvId = 0 + self.recoverRate = 0 + self.recoverStartTime = 0 + self.bossDamageMovie = None + self.everThrownPie = 0 + self.battleThreeMusicTime = 0 + self.insidesANodePath = None + self.insidesBNodePath = None + self.strafeInterval = None + self.onscreenMessage = None + self.bossMaxDamage = ToontownGlobals.LawbotBossMaxDamage + self.elevatorType = ElevatorConstants.ELEVATOR_CJ + self.gavels = {} + self.chairs = {} + self.cannons = {} + self.useCannons = 1 + self.juryBoxIval = None + self.juryTimer = None + self.witnessToon = None + self.witnessToonOnstage = False + self.numToonJurorsSeated = 0 + self.mainDoor = None + self.reflectedMainDoor = None + self.panFlashInterval = None + self.panDamage = ToontownGlobals.LawbotBossDefensePanDamage + if base.config.GetBool('lawbot-boss-cheat', 0): + self.panDamage = 25 + self.evidenceHitSfx = None + self.toonUpSfx = None + self.bonusTimer = None + self.juryMovesSfx = None + self.baseColStashed = False + self.battleDifficulty = 0 + self.bonusWeight = 0 + self.numJurorsLocalToonSeated = 0 + self.cannonIndex = -1 + return + + def announceGenerate(self): + global OneBossCog + self.notify.debug('----- announceGenerate') + DistributedBossCog.DistributedBossCog.announceGenerate(self) + self.setName(TTLocalizer.LawbotBossName) + nameInfo = TTLocalizer.BossCogNameWithDept % {'name': self.name, + 'dept': SuitDNA.getDeptFullname(self.style.dept)} + self.setDisplayName(nameInfo) + self.piesRestockSfx = loader.loadSfx('phase_5/audio/sfx/LB_receive_evidence.ogg') + self.rampSlideSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_ramp_slide.ogg') + self.evidenceHitSfx = loader.loadSfx('phase_11/audio/sfx/LB_evidence_hit.ogg') + self.juryMovesSfx = loader.loadSfx('phase_11/audio/sfx/LB_jury_moves.ogg') + self.toonUpSfx = loader.loadSfx('phase_11/audio/sfx/LB_toonup.ogg') + self.strafeSfx = [] + for i in xrange(10): + self.strafeSfx.append(loader.loadSfx('phase_3.5/audio/sfx/SA_shred.ogg')) + + render.setTag('pieCode', str(ToontownGlobals.PieCodeNotBossCog)) + insidesA = CollisionPolygon(Point3(4.0, -2.0, 5.0), Point3(-4.0, -2.0, 5.0), Point3(-4.0, -2.0, 0.5), Point3(4.0, -2.0, 0.5)) + insidesANode = CollisionNode('BossZap') + insidesANode.addSolid(insidesA) + insidesANode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.WallBitmask) + self.insidesANodePath = self.axle.attachNewNode(insidesANode) + self.insidesANodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossInsides)) + self.insidesANodePath.stash() + insidesB = CollisionPolygon(Point3(-4.0, 2.0, 5.0), Point3(4.0, 2.0, 5.0), Point3(4.0, 2.0, 0.5), Point3(-4.0, 2.0, 0.5)) + insidesBNode = CollisionNode('BossZap') + insidesBNode.addSolid(insidesB) + insidesBNode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.WallBitmask) + self.insidesBNodePath = self.axle.attachNewNode(insidesBNode) + self.insidesBNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossInsides)) + self.insidesBNodePath.stash() + target = CollisionTube(0, -1, 4, 0, -1, 9, 3.5) + targetNode = CollisionNode('BossZap') + targetNode.addSolid(target) + targetNode.setCollideMask(ToontownGlobals.PieBitmask) + self.targetNodePath = self.pelvis.attachNewNode(targetNode) + self.targetNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossCog)) + shield = CollisionTube(0, 1, 4, 0, 1, 7, 3.5) + shieldNode = CollisionNode('BossZap') + shieldNode.addSolid(shield) + shieldNode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask) + shieldNodePath = self.pelvis.attachNewNode(shieldNode) + disk = loader.loadModel('phase_9/models/char/bossCog-gearCollide') + disk.find('**/+CollisionNode').setName('BossZap') + disk.reparentTo(self.pelvis) + disk.setZ(0.8) + self.loadEnvironment() + self.__makeWitnessToon() + self.__loadMopaths() + base.localAvatar.chatMgr.chatInputSpeedChat.addCJMenu() + if OneBossCog != None: + self.notify.warning('Multiple BossCogs visible.') + OneBossCog = self + return + + def disable(self): + global OneBossCog + self.notify.debug('----- disable') + DistributedBossCog.DistributedBossCog.disable(self) + self.request('Off') + self.unloadEnvironment() + self.__cleanupWitnessToon() + self.__unloadMopaths() + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + self.__cleanupStrafe() + self.__cleanupJuryBox() + render.clearTag('pieCode') + self.targetNodePath.detachNode() + self.cr.relatedObjectMgr.abortRequest(self.lawyerRequest) + self.lawyerRequest = None + self.betweenBattleMusic.stop() + self.promotionMusic.stop() + self.stingMusic.stop() + self.battleTwoMusic.stop() + self.battleThreeMusic.stop() + self.epilogueMusic.stop() + if self.juryTimer: + self.juryTimer.destroy() + del self.juryTimer + if self.bonusTimer: + self.bonusTimer.destroy() + del self.bonusTimer + base.localAvatar.chatMgr.chatInputSpeedChat.removeCJMenu() + if OneBossCog == self: + OneBossCog = None + return + + def delete(self): + self.notify.debug('----- delete') + DistributedBossCog.DistributedBossCog.delete(self) + + def d_hitBoss(self, bossDamage): + self.notify.debug('----- d_hitBoss') + self.sendUpdate('hitBoss', [bossDamage]) + + def d_healBoss(self, bossHeal): + self.notify.debug('----- d_bossHeal') + self.sendUpdate('healBoss', [bossHeal]) + + def d_hitBossInsides(self): + self.notify.debug('----- d_hitBossInsides') + self.sendUpdate('hitBossInsides', []) + + def d_hitDefensePan(self): + self.notify.debug('----- d_hitDefensePan') + self.sendUpdate('hitDefensePan', []) + + def d_hitProsecutionPan(self): + self.notify.debug('----- d_hitProsecutionPan') + self.sendUpdate('hitProsecutionPan', []) + + def d_hitToon(self, toonId): + self.notify.debug('----- d_hitToon') + self.sendUpdate('hitToon', [toonId]) + + def gotToon(self, toon): + stateName = self.state + if stateName == 'Elevator': + self.placeToonInElevator(toon) + + def setLawyerIds(self, lawyerIds): + self.lawyers = [] + self.cr.relatedObjectMgr.abortRequest(self.lawyerRequest) + self.lawyerRequest = self.cr.relatedObjectMgr.requestObjects(lawyerIds, allCallback=self.__gotLawyers) + + def __gotLawyers(self, lawyers): + self.lawyerRequest = None + self.lawyers = lawyers + for i in xrange(len(self.lawyers)): + suit = self.lawyers[i] + suit.fsm.request('neutral') + suit.loop('neutral') + suit.setBossCogId(self.doId) + + return + + def setBossDamage(self, bossDamage, recoverRate, timestamp): + recoverStartTime = globalClockDelta.networkToLocalTime(timestamp) + self.bossDamage = bossDamage + self.recoverRate = recoverRate + self.recoverStartTime = recoverStartTime + taskName = 'RecoverBossDamage' + taskMgr.remove(taskName) + if self.bossDamageMovie: + if self.bossDamage >= self.bossMaxDamage: + self.notify.debug('finish the movie then transition to NearVictory') + self.bossDamageMovie.resumeUntil(self.bossDamageMovie.getDuration()) + else: + self.bossDamageMovie.resumeUntil(self.bossDamage * self.bossDamageToMovie) + if self.recoverRate: + taskMgr.add(self.__recoverBossDamage, taskName) + self.makeScaleReflectDamage() + + def getBossDamage(self): + self.notify.debug('----- getBossDamage') + now = globalClock.getFrameTime() + elapsed = now - self.recoverStartTime + return max(self.bossDamage - self.recoverRate * elapsed / 60.0, 0) + + def __recoverBossDamage(self, task): + self.notify.debug('----- __recoverBossDamage') + if self.bossDamageMovie: + self.bossDamageMovie.setT(self.getBossDamage() * self.bossDamageToMovie) + return Task.cont + + def __walkToonToPromotion(self, toonId, delay, mopath, track, delayDeletes): + self.notify.debug('----- __walkToonToPromotion') + toon = base.cr.doId2do.get(toonId) + if toon: + destPos = toon.getPos() + self.placeToonInElevator(toon) + toon.wrtReparentTo(render) + ival = Sequence(Wait(delay), Func(toon.suit.setPlayRate, 1, 'walk'), Func(toon.suit.loop, 'walk'), toon.posInterval(1, Point3(0, 90, 20)), ParallelEndTogether(MopathInterval(mopath, toon), toon.posInterval(2, destPos, blendType='noBlend')), Func(toon.suit.loop, 'neutral')) + track.append(ival) + delayDeletes.append(DelayDelete.DelayDelete(toon, 'LawbotBoss.__walkToonToPromotion')) + + def __walkSuitToPoint(self, node, fromPos, toPos): + self.notify.debug('----- __walkSuitToPoint') + vector = Vec3(toPos - fromPos) + distance = vector.length() + time = distance / (ToontownGlobals.SuitWalkSpeed * 1.8) + return Sequence(Func(node.setPos, fromPos), Func(node.headsUp, toPos), node.posInterval(time, toPos)) + + def __makeRollToBattleTwoMovie(self): + startPos = Point3(ToontownGlobals.LawbotBossBattleOnePosHpr[0], ToontownGlobals.LawbotBossBattleOnePosHpr[1], ToontownGlobals.LawbotBossBattleOnePosHpr[2]) + battlePos = Point3(ToontownGlobals.LawbotBossBattleTwoPosHpr[0], ToontownGlobals.LawbotBossBattleTwoPosHpr[1], ToontownGlobals.LawbotBossBattleTwoPosHpr[2]) + battleHpr = VBase3(ToontownGlobals.LawbotBossBattleTwoPosHpr[3], ToontownGlobals.LawbotBossBattleTwoPosHpr[4], ToontownGlobals.LawbotBossBattleTwoPosHpr[5]) + bossTrack = Sequence() + self.notify.debug('calling setPosHpr') + myInterval = base.camera.posHprInterval(8, Point3(-22, -100, 35), Point3(-10, -13, 0), startPos=Point3(-22, -90, 35), startHpr=Point3(-10, -13, 0), blendType='easeInOut') + chatTrack = Sequence(Func(self.setChatAbsolute, TTLocalizer.LawbotBossTempJury1, CFSpeech), Func(base.camera.reparentTo, localAvatar), Func(base.camera.setPos, localAvatar.cameraPositions[0][0]), Func(base.camera.setHpr, 0, 0, 0), Func(self.releaseToons, 1)) + bossTrack.append(Func(self.getGeomNode().setH, 180)) + track, hpr = self.rollBossToPoint(startPos, None, battlePos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(battlePos, hpr, battlePos, battleHpr, 0) + self.makeToonsWait() + finalPodiumPos = Point3(self.podium.getX(), self.podium.getY(), self.podium.getZ() + ToontownGlobals.LawbotBossBattleTwoPosHpr[2]) + finalReflectedPodiumPos = Point3(self.reflectedPodium.getX(), self.reflectedPodium.getY(), self.reflectedPodium.getZ() + ToontownGlobals.LawbotBossBattleTwoPosHpr[2]) + return Sequence(chatTrack, bossTrack, Func(self.getGeomNode().setH, 0), Parallel(self.podium.posInterval(5.0, finalPodiumPos), self.reflectedPodium.posInterval(5.0, finalReflectedPodiumPos), Func(self.stashBoss), self.posInterval(5.0, battlePos), Func(taskMgr.doMethodLater, 0.01, self.unstashBoss, 'unstashBoss')), name=self.uniqueName('BattleTwoMovie')) + + def __makeRollToBattleThreeMovie(self): + startPos = Point3(ToontownGlobals.LawbotBossBattleTwoPosHpr[0], ToontownGlobals.LawbotBossBattleTwoPosHpr[1], ToontownGlobals.LawbotBossBattleTwoPosHpr[2]) + battlePos = Point3(ToontownGlobals.LawbotBossBattleThreePosHpr[0], ToontownGlobals.LawbotBossBattleThreePosHpr[1], ToontownGlobals.LawbotBossBattleThreePosHpr[2]) + battleHpr = VBase3(ToontownGlobals.LawbotBossBattleThreePosHpr[3], ToontownGlobals.LawbotBossBattleThreePosHpr[4], ToontownGlobals.LawbotBossBattleThreePosHpr[5]) + bossTrack = Sequence() + myInterval = base.camera.posHprInterval(8, Point3(-22, -100, 35), Point3(-10, -13, 0), startPos=Point3(-22, -90, 35), startHpr=Point3(-10, -13, 0), blendType='easeInOut') + chatTrack = Sequence(Func(self.setChatAbsolute, TTLocalizer.LawbotBossTrialChat1, CFSpeech), Func(base.camera.reparentTo, localAvatar), Func(base.camera.setPos, localAvatar.cameraPositions[0][0]), Func(base.camera.setHpr, 0, 0, 0), Func(self.releaseToons, 1)) + bossTrack.append(Func(self.getGeomNode().setH, 180)) + bossTrack.append(Func(self.loop, 'Ff_neutral')) + track, hpr = self.rollBossToPoint(startPos, None, battlePos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(battlePos, hpr, battlePos, battleHpr, 0) + self.makeToonsWait() + return Sequence(chatTrack, bossTrack, Func(self.getGeomNode().setH, 0), name=self.uniqueName('BattleTwoMovie')) + + def toNeutralMode(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.setState('waitForBattle') + + def makeToonsWait(self): + self.notify.debug('makeToonsWait') + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.stopLookAround() + toon.stopSmooth() + + if self.hasLocalToon(): + self.toMovieMode() + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.loop('neutral') + + def makeEndOfBattleMovie(self, hasLocalToon): + name = self.uniqueName('Drop') + seq = Sequence(name=name) + seq += [Wait(0.0)] + if hasLocalToon: + seq += [Func(self.show), + Func(base.camera.reparentTo, localAvatar), + Func(base.camera.setPos, localAvatar.cameraPositions[0][0]), + Func(base.camera.setHpr, 0, 0, 0)] + seq.append(Func(self.setChatAbsolute, TTLocalizer.LawbotBossPassExam, CFSpeech)) + seq.append(Wait(5.0)) + seq.append(Func(self.clearChat)) + return seq + + def __makeBossDamageMovie(self): + self.notify.debug('---- __makeBossDamageMovie') + startPos = Point3(ToontownGlobals.LawbotBossBattleThreePosHpr[0], ToontownGlobals.LawbotBossBattleThreePosHpr[1], ToontownGlobals.LawbotBossBattleThreePosHpr[2]) + startHpr = Point3(*ToontownGlobals.LawbotBossBattleThreeHpr) + bottomPos = Point3(*ToontownGlobals.LawbotBossBottomPos) + deathPos = Point3(*ToontownGlobals.LawbotBossDeathPos) + self.setPosHpr(startPos, startHpr) + bossTrack = Sequence() + bossTrack.append(Func(self.loop, 'Ff_neutral')) + track, hpr = self.rollBossToPoint(startPos, startHpr, bottomPos, None, 1) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(bottomPos, startHpr, deathPos, None, 1) + bossTrack.append(track) + duration = bossTrack.getDuration() + return bossTrack + + def __showOnscreenMessage(self, text): + self.notify.debug('----- __showOnscreenmessage') + if self.onscreenMessage: + self.onscreenMessage.destroy() + self.onscreenMessage = None + self.onscreenMessage = DirectLabel(text=text, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.35), scale=0.1) + return + + def __clearOnscreenMessage(self): + if self.onscreenMessage: + self.onscreenMessage.destroy() + self.onscreenMessage = None + return + + def __showWaitingMessage(self, task): + self.notify.debug('----- __showWaitingMessage') + self.__showOnscreenMessage(TTLocalizer.WaitingForOtherToons) + + def loadEnvironment(self): + self.notify.debug('----- loadEnvironment') + DistributedBossCog.DistributedBossCog.loadEnvironment(self) + self.geom = loader.loadModel('phase_11/models/lawbotHQ/LawbotCourtroom3') + self.geom.setPos(0, 0, -71.601) + self.geom.setScale(1) + self.elevatorEntrance = self.geom.find('**/elevator_origin') + self.elevatorEntrance.getChildren().detach() + self.elevatorEntrance.setScale(1) + elevatorModel = loader.loadModel('phase_11/models/lawbotHQ/LB_Elevator') + elevatorModel.reparentTo(self.elevatorEntrance) + self.setupElevator(elevatorModel) + self.promotionMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.ogg') + self.betweenBattleMusic = base.loadMusic('phase_9/audio/bgm/encntr_toon_winning.ogg') + self.battleTwoMusic = base.loadMusic('phase_11/audio/bgm/LB_juryBG.ogg') + floor = self.geom.find('**/MidVaultFloor1') + if floor.isEmpty(): + floor = self.geom.find('**/CR3_Floor') + self.evFloor = self.replaceCollisionPolysWithPlanes(floor) + self.evFloor.reparentTo(self.geom) + self.evFloor.setName('floor') + plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, -50))) + planeNode = CollisionNode('dropPlane') + planeNode.addSolid(plane) + planeNode.setCollideMask(ToontownGlobals.PieBitmask) + self.geom.attachNewNode(planeNode) + self.door3 = self.geom.find('**/SlidingDoor1/') + if self.door3.isEmpty(): + self.door3 = self.geom.find('**/interior/CR3_Door') + self.mainDoor = self.geom.find('**/Door_1') + if not self.mainDoor.isEmpty(): + itemsToHide = ['interior/Door_1'] + for str in itemsToHide: + stuffToHide = self.geom.find('**/%s' % str) + if not stuffToHide.isEmpty(): + self.notify.debug('found %s' % stuffToHide) + stuffToHide.wrtReparentTo(self.mainDoor) + else: + self.notify.debug('not found %s' % stuffToHide) + + self.reflectedMainDoor = self.geom.find('**/interiorrefl/CR3_Door') + if not self.reflectedMainDoor.isEmpty(): + itemsToHide = ['Reflections/Door_1'] + for str in itemsToHide: + stuffToHide = self.geom.find('**/%s' % str) + if not stuffToHide.isEmpty(): + self.notify.debug('found %s' % stuffToHide) + stuffToHide.wrtReparentTo(self.reflectedMainDoor) + else: + self.notify.debug('not found %s' % stuffToHide) + + self.geom.reparentTo(render) + self.loadWitnessStand() + self.loadScale() + self.scaleNodePath.stash() + self.loadJuryBox() + self.loadPodium() + ug = self.geom.find('**/Reflections') + ug.setBin('ground', -10) + + def loadJuryBox(self): + self.juryBox = self.geom.find('**/JuryBox') + juryBoxPos = self.juryBox.getPos() + newPos = juryBoxPos - Point3(*ToontownGlobals.LawbotBossJuryBoxRelativeEndPos) + if not self.debugPositions: + self.juryBox.setPos(newPos) + self.reflectedJuryBox = self.geom.find('**/JuryBox_Geo_Reflect') + reflectedJuryBoxPos = self.reflectedJuryBox.getPos() + newReflectedPos = reflectedJuryBoxPos - Point3(*ToontownGlobals.LawbotBossJuryBoxRelativeEndPos) + if not self.debugPositions: + self.reflectedJuryBox.setPos(newReflectedPos) + if not self.reflectedJuryBox.isEmpty(): + if self.debugPositions: + self.reflectedJuryBox.show() + self.reflectedJuryBox.setZ(self.reflectedJuryBox.getZ() + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[2]) + + def loadPodium(self): + self.podium = self.geom.find('**/Podium') + if not self.debugPositions: + self.podium.setZ(ToontownGlobals.LawbotBossBattleOnePosHpr[2]) + self.reflectedPodium = self.geom.find('**/Podium_Geo1_Refl') + if not self.debugPositions: + self.reflectedPodium.setZ(ToontownGlobals.LawbotBossBattleTwoPosHpr[2]) + if not self.reflectedPodium.isEmpty(): + if self.debugPositions: + self.reflectedPodium.show() + + def loadCannons(self): + pass + + def loadWitnessStand(self): + self.realWitnessStand = self.geom.find('**/WitnessStand') + if not self.realWitnessStand.isEmpty(): + pass + self.reflectedWitnessStand = self.geom.find('**/Witnessstand_Geo_Reflect') + if not self.reflectedWitnessStand.isEmpty(): + pass + colNode = self.realWitnessStand.find('**/witnessStandCollisions/Witnessstand_Collision') + colNode.setName('WitnessStand') + + def loadScale(self): + self.useProgrammerScale = base.config.GetBool('want-injustice-scale-debug', 0) + if self.useProgrammerScale: + self.loadScaleOld() + else: + self.loadScaleNew() + + def __debugScale(self): + prosecutionPanPos = self.prosecutionPanNodePath.getPos() + origin = Point3(0, 0, 0) + prosecutionPanRelPos = self.scaleNodePath.getRelativePoint(self.prosecutionPanNodePath, origin) + panRenderPos = render.getRelativePoint(self.prosecutionPanNodePath, origin) + self.notify.debug('prosecutionPanPos = %s' % prosecutionPanPos) + self.notify.debug('prosecutionPanRelPos = %s' % prosecutionPanRelPos) + self.notify.debug('panRenderPos = %s' % panRenderPos) + prosecutionLocatorPos = self.prosecutionLocator.getPos() + prosecutionLocatorRelPos = self.scaleNodePath.getRelativePoint(self.prosecutionLocator, origin) + locatorRenderPos = render.getRelativePoint(self.prosecutionLocator, origin) + self.notify.debug('prosecutionLocatorPos = %s ' % prosecutionLocatorPos) + self.notify.debug('prosecutionLocatorRelPos = %s ' % prosecutionLocatorRelPos) + self.notify.debug('locatorRenderPos = %s' % locatorRenderPos) + beamPos = self.beamNodePath.getPos() + beamRelPos = self.scaleNodePath.getRelativePoint(self.beamNodePath, origin) + beamRenderPos = render.getRelativePoint(self.beamNodePath, origin) + self.notify.debug('beamPos = %s' % beamPos) + self.notify.debug('beamRelPos = %s' % beamRelPos) + self.notify.debug('beamRenderPos = %s' % beamRenderPos) + beamBoundsCenter = self.beamNodePath.getBounds().getCenter() + self.notify.debug('beamBoundsCenter = %s' % beamBoundsCenter) + beamLocatorBounds = self.beamLocator.getBounds() + beamLocatorPos = beamLocatorBounds.getCenter() + self.notify.debug('beamLocatorPos = %s' % beamLocatorPos) + + def loadScaleNew(self): + self.scaleNodePath = loader.loadModel('phase_11/models/lawbotHQ/scale') + self.beamNodePath = self.scaleNodePath.find('**/scaleBeam') + self.defensePanNodePath = self.scaleNodePath.find('**/defensePan') + self.prosecutionPanNodePath = self.scaleNodePath.find('**/prosecutionPan') + self.defenseColNodePath = self.scaleNodePath.find('**/DefenseCol') + self.defenseColNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeDefensePan)) + self.prosecutionColNodePath = self.scaleNodePath.find('**/ProsecutionCol') + self.prosecutionColNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeProsecutionPan)) + self.standNodePath = self.scaleNodePath.find('**/scaleStand') + self.scaleNodePath.setPosHpr(*ToontownGlobals.LawbotBossInjusticePosHpr) + self.defenseLocator = self.scaleNodePath.find('**/DefenseLocator') + defenseLocBounds = self.defenseLocator.getBounds() + defenseLocPos = defenseLocBounds.getCenter() + self.notify.debug('defenseLocatorPos = %s' % defenseLocPos) + self.defensePanNodePath.setPos(defenseLocPos) + self.defensePanNodePath.reparentTo(self.beamNodePath) + self.notify.debug('defensePanNodePath.getPos()=%s' % self.defensePanNodePath.getPos()) + self.prosecutionLocator = self.scaleNodePath.find('**/ProsecutionLocator') + prosecutionLocBounds = self.prosecutionLocator.getBounds() + prosecutionLocPos = prosecutionLocBounds.getCenter() + self.notify.debug('prosecutionLocatorPos = %s' % prosecutionLocPos) + self.prosecutionPanNodePath.setPos(prosecutionLocPos) + self.prosecutionPanNodePath.reparentTo(self.beamNodePath) + self.beamLocator = self.scaleNodePath.find('**/StandLocator1') + beamLocatorBounds = self.beamLocator.getBounds() + beamLocatorPos = beamLocatorBounds.getCenter() + negBeamLocatorPos = -beamLocatorPos + self.notify.debug('beamLocatorPos = %s' % beamLocatorPos) + self.notify.debug('negBeamLocatorPos = %s' % negBeamLocatorPos) + self.beamNodePath.setPos(beamLocatorPos) + self.scaleNodePath.setScale(*ToontownGlobals.LawbotBossInjusticeScale) + self.scaleNodePath.wrtReparentTo(self.geom) + self.baseHighCol = self.scaleNodePath.find('**/BaseHighCol') + oldBitMask = self.baseHighCol.getCollideMask() + newBitMask = oldBitMask & ~ToontownGlobals.PieBitmask + newBitMask = newBitMask & ~ToontownGlobals.CameraBitmask + self.baseHighCol.setCollideMask(newBitMask) + self.defenseHighCol = self.scaleNodePath.find('**/DefenseHighCol') + self.defenseHighCol.stash() + self.defenseHighCol.setCollideMask(newBitMask) + self.baseTopCol = self.scaleNodePath.find('**/Scale_base_top_collision') + self.baseSideCol = self.scaleNodePath.find('**/Scale_base_side_col') + self.defenseLocator.hide() + self.prosecutionLocator.hide() + self.beamLocator.hide() + + def loadScaleOld(self): + startingTilt = 0 + self.scaleNodePath = NodePath('injusticeScale') + beamGeom = self.createBlock(0.25, 2, 0.125, -0.25, -2, -0.125, 0, 1.0, 0, 1.0) + self.beamNodePath = NodePath('scaleBeam') + self.beamNodePath.attachNewNode(beamGeom) + self.beamNodePath.setPos(0, 0, 3) + self.beamNodePath.reparentTo(self.scaleNodePath) + defensePanGeom = self.createBlock(0.5, 0.5, 0, -0.5, -0.5, -2, 0, 0, 1.0, 0.25) + self.defensePanNodePath = NodePath('defensePan') + self.defensePanNodePath.attachNewNode(defensePanGeom) + self.defensePanNodePath.setPos(0, -2, 0) + self.defensePanNodePath.reparentTo(self.beamNodePath) + defenseTube = CollisionTube(0, 0, -0.5, 0, 0, -1.5, 0.6) + defenseTube.setTangible(1) + defenseCollNode = CollisionNode('DefenseCol') + defenseCollNode.addSolid(defenseTube) + self.defenseColNodePath = self.defensePanNodePath.attachNewNode(defenseCollNode) + self.defenseColNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeDefensePan)) + prosecutionPanGeom = self.createBlock(0.5, 0.5, 0, -0.5, -0.5, -2, 1.0, 0, 0, 1.0) + self.prosecutionPanNodePath = NodePath('prosecutionPan') + self.prosecutionPanNodePath.attachNewNode(prosecutionPanGeom) + self.prosecutionPanNodePath.setPos(0, 2, 0) + self.prosecutionPanNodePath.reparentTo(self.beamNodePath) + prosecutionTube = CollisionTube(0, 0, -0.5, 0, 0, -1.5, 0.6) + prosecutionTube.setTangible(1) + prosecutionCollNode = CollisionNode(self.uniqueName('ProsecutionCol')) + prosecutionCollNode.addSolid(prosecutionTube) + self.prosecutionColNodePath = self.prosecutionPanNodePath.attachNewNode(prosecutionCollNode) + self.prosecutionColNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeProsecutionPan)) + standGeom = self.createBlock(0.25, 0.25, 0, -0.25, -0.25, 3) + self.standNodePath = NodePath('scaleStand') + self.standNodePath.attachNewNode(standGeom) + self.standNodePath.reparentTo(self.scaleNodePath) + self.scaleNodePath.setPosHpr(*ToontownGlobals.LawbotBossInjusticePosHpr) + self.scaleNodePath.setScale(5.0) + self.scaleNodePath.wrtReparentTo(self.geom) + self.setScaleTilt(startingTilt) + + def setScaleTilt(self, tilt): + self.beamNodePath.setP(tilt) + if self.useProgrammerScale: + self.defensePanNodePath.setP(-tilt) + self.prosecutionPanNodePath.setP(-tilt) + else: + self.defensePanNodePath.setP(-tilt) + self.prosecutionPanNodePath.setP(-tilt) + + def stashBaseCol(self): + if not self.baseColStashed: + self.notify.debug('stashBaseCol') + self.baseTopCol.stash() + self.baseSideCol.stash() + self.baseColStashed = True + + def unstashBaseCol(self): + if self.baseColStashed: + self.notify.debug('unstashBaseCol') + self.baseTopCol.unstash() + self.baseSideCol.unstash() + self.baseColStashed = False + + def makeScaleReflectDamage(self): + diffDamage = self.bossDamage - ToontownGlobals.LawbotBossInitialDamage + diffDamage *= 1.0 + if diffDamage >= 0: + percentDamaged = diffDamage / (ToontownGlobals.LawbotBossMaxDamage - ToontownGlobals.LawbotBossInitialDamage) + tilt = percentDamaged * ToontownGlobals.LawbotBossWinningTilt + else: + percentDamaged = diffDamage / (ToontownGlobals.LawbotBossInitialDamage - 0) + tilt = percentDamaged * ToontownGlobals.LawbotBossWinningTilt + self.setScaleTilt(tilt) + if self.bossDamage < ToontownGlobals.LawbotBossMaxDamage * 0.85: + self.unstashBaseCol() + else: + self.stashBaseCol() + + def unloadEnvironment(self): + self.notify.debug('----- unloadEnvironment') + DistributedBossCog.DistributedBossCog.unloadEnvironment(self) + self.geom.removeNode() + del self.geom + + def __loadMopaths(self): + self.notify.debug('----- __loadMopaths') + self.toonsEnterA = Mopath.Mopath() + self.toonsEnterA.loadFile('phase_9/paths/bossBattle-toonsEnterA') + self.toonsEnterA.fFaceForward = 1 + self.toonsEnterA.timeScale = 35 + self.toonsEnterB = Mopath.Mopath() + self.toonsEnterB.loadFile('phase_9/paths/bossBattle-toonsEnterB') + self.toonsEnterB.fFaceForward = 1 + self.toonsEnterB.timeScale = 35 + + def __unloadMopaths(self): + self.notify.debug('----- __unloadMopaths') + self.toonsEnterA.reset() + self.toonsEnterB.reset() + + def enterOff(self): + self.notify.debug('----- enterOff') + DistributedBossCog.DistributedBossCog.enterOff(self) + if self.witnessToon: + self.witnessToon.clearChat() + + def enterWaitForToons(self): + self.notify.debug('----- enterWaitForToons') + DistributedBossCog.DistributedBossCog.enterWaitForToons(self) + self.geom.hide() + self.witnessToon.removeActive() + + def exitWaitForToons(self): + self.notify.debug('----- exitWaitForToons') + DistributedBossCog.DistributedBossCog.exitWaitForToons(self) + self.geom.show() + self.witnessToon.addActive() + + def enterElevator(self): + self.notify.debug('----- enterElevator') + DistributedBossCog.DistributedBossCog.enterElevator(self) + self.witnessToon.removeActive() + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.LawbotBossBattleTwoPosHpr) + self.happy = 1 + self.raised = 1 + self.forward = 1 + self.doAnimate() + self.__showWitnessToon() + if not self.mainDoor.isEmpty(): + self.mainDoor.stash() + if not self.reflectedMainDoor.isEmpty(): + self.reflectedMainDoor.stash() + base.camera.reparentTo(self.elevatorModel) + base.camera.setPosHpr(0, 30, 8, 180, 0, 0) + base.camLens.setMinFov(ToontownGlobals.CJElevatorFov/(4./3.)) + + def exitElevator(self): + self.notify.debug('----- exitElevator') + DistributedBossCog.DistributedBossCog.exitElevator(self) + self.witnessToon.removeActive() + + def enterIntroduction(self): + self.notify.debug('----- enterIntroduction') + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.LawbotBossBattleTwoPosHpr) + self.stopAnimate() + DistributedBossCog.DistributedBossCog.enterIntroduction(self) + base.playMusic(self.promotionMusic, looping=1, volume=0.9) + if not self.mainDoor.isEmpty(): + self.mainDoor.stash() + if not self.reflectedMainDoor.isEmpty(): + self.reflectedMainDoor.stash() + + def exitIntroduction(self): + self.notify.debug('----- exitIntroduction') + DistributedBossCog.DistributedBossCog.exitIntroduction(self) + self.promotionMusic.stop() + if not self.mainDoor.isEmpty(): + pass + if not self.reflectedMainDoor.isEmpty(): + self.reflectedMainDoor.unstash() + if not self.elevatorEntrance.isEmpty(): + pass + + def enterBattleOne(self): + self.notify.debug('----- LawbotBoss.enterBattleOne ') + self.setPosHpr(*ToontownGlobals.LawbotBossBattleOnePosHpr) + DistributedBossCog.DistributedBossCog.enterBattleOne(self) + self.reparentTo(render) + self.clearChat() + self.loop('Ff_neutral') + self.notify.debug('self.battleANode = %s' % self.battleANode) + if self.battleA == None or self.battleB == None: + pass + return + + def exitBattleOne(self): + self.notify.debug('----- exitBattleOne') + DistributedBossCog.DistributedBossCog.exitBattleOne(self) + + def stashBoss(self): + self.stash() + + def unstashBoss(self, task): + self.unstash() + self.reparentTo(render) + + def enterRollToBattleTwo(self): + self.notify.debug('----- enterRollToBattleTwo') + self.releaseToons(finalBattle=1) + self.stashBoss() + self.toonsToBattlePosition(self.involvedToons, self.battleANode) + self.stickBossToFloor() + intervalName = 'RollToBattleTwo' + seq = Sequence(self.__makeRollToBattleTwoMovie(), Func(self.__onToPrepareBattleTwo), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.betweenBattleMusic, looping=1, volume=0.9) + taskMgr.doMethodLater(0.01, self.unstashBoss, 'unstashBoss') + + def __onToPrepareBattleTwo(self): + self.notify.debug('----- __onToPrepareBattleTwo') + self.unstickBoss() + self.setPosHpr(*ToontownGlobals.LawbotBossBattleTwoPosHpr) + self.doneBarrier('RollToBattleTwo') + + def exitRollToBattleTwo(self): + self.notify.debug('----- exitRollToBattleTwo') + self.unstickBoss() + intervalName = 'RollToBattleTwo' + self.clearInterval(intervalName) + self.betweenBattleMusic.stop() + + def enterPrepareBattleTwo(self): + self.notify.debug('----- enterPrepareBattleTwo') + self.cleanupIntervals() + self.controlToons() + self.setToonsToNeutral(self.involvedToons) + self.clearChat() + self.reparentTo(render) + prepareBattleTwoMovie = self.__makePrepareBattleTwoMovie() + intervalName = 'prepareBattleTwo' + seq = Sequence(prepareBattleTwoMovie, name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + self.acceptOnce('doneChatPage', self.__showCannonsAppearing) + base.playMusic(self.stingMusic, looping=0, volume=1.0) + + def __showCannonsAppearing(self, elapsedTime = 0): + allCannonsAppear = Sequence(Func(self.__positionToonsInFrontOfCannons), Func(base.camera.reparentTo, localAvatar), Func(base.camera.setPos, localAvatar.cameraPositions[2][0]), Func(base.camera.lookAt, localAvatar)) + multiCannons = Parallel() + index = 0 + self.involvedToons.sort() + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + if index in self.cannons: + cannon = self.cannons[index] + cannonSeq = cannon.generateCannonAppearTrack(toon) + multiCannons.append(cannonSeq) + index += 1 + else: + self.notify.warning('No cannon %d but we have a toon =%d' % (index, toonId)) + + allCannonsAppear.append(multiCannons) + intervalName = 'prepareBattleTwoCannonsAppear' + seq = Sequence(allCannonsAppear, Func(self.__onToBattleTwo), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + + def __onToBattleTwo(self, elapsedTime = 0): + self.notify.debug('----- __onToBattleTwo') + self.doneBarrier('PrepareBattleTwo') + taskMgr.doMethodLater(1, self.__showWaitingMessage, self.uniqueName('WaitingMessage')) + + def exitPrepareBattleTwo(self): + self.notify.debug('----- exitPrepareBattleTwo') + self.show() + taskMgr.remove(self.uniqueName('WaitingMessage')) + self.ignore('doneChatPage') + self.__clearOnscreenMessage() + self.stingMusic.stop() + + def enterBattleTwo(self): + self.notify.debug('----- enterBattleTwo') + self.cleanupIntervals() + mult = ToontownBattleGlobals.getBossBattleCreditMultiplier(2) + localAvatar.inventory.setBattleCreditMultiplier(mult) + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.LawbotBossBattleTwoPosHpr) + self.clearChat() + self.witnessToon.clearChat() + self.releaseToons(finalBattle=1) + if not self.useCannons: + self.toonsToBattlePosition(self.toonsA, self.battleANode) + self.toonsToBattlePosition(self.toonsB, self.battleBNode) + base.playMusic(self.battleTwoMusic, looping=1, volume=0.9) + self.startJuryBoxMoving() + for index in xrange(len(self.cannons)): + cannon = self.cannons[index] + cannon.cannon.show() + + def getChairParent(self): + return self.juryBox + + def startJuryBoxMoving(self): + curPos = self.juryBox.getPos() + endingAbsPos = Point3(curPos[0] + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[0], curPos[1] + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[1], curPos[2] + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[2]) + curReflectedPos = self.reflectedJuryBox.getPos() + reflectedEndingAbsPos = Point3(curReflectedPos[0] + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[0], curReflectedPos[1] + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[1], curReflectedPos[2] + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[2]) + self.juryBoxIval = Parallel(self.juryBox.posInterval(ToontownGlobals.LawbotBossJuryBoxMoveTime, endingAbsPos), self.reflectedJuryBox.posInterval(ToontownGlobals.LawbotBossJuryBoxMoveTime, reflectedEndingAbsPos), SoundInterval(self.juryMovesSfx, node=self.chairs[2].nodePath, duration=ToontownGlobals.LawbotBossJuryBoxMoveTime, loop=1, volume=1.0)) + self.juryBoxIval.start() + self.juryTimer = ToontownTimer.ToontownTimer() + self.juryTimer.posInTopRightCorner() + self.juryTimer.countdown(ToontownGlobals.LawbotBossJuryBoxMoveTime) + + def exitBattleTwo(self): + self.notify.debug('----- exitBattleTwo') + intervalName = self.uniqueName('Drop') + self.clearInterval(intervalName) + self.cleanupBattles() + self.battleTwoMusic.stop() + localAvatar.inventory.setBattleCreditMultiplier(1) + if self.juryTimer: + self.juryTimer.destroy() + del self.juryTimer + self.juryTimer = None + for chair in self.chairs.values(): + chair.stopCogsFlying() + + return + + def enterRollToBattleThree(self): + self.notify.debug('----- enterRollToBattleThree') + self.reparentTo(render) + self.stickBossToFloor() + intervalName = 'RollToBattleThree' + seq = Sequence(self.__makeRollToBattleThreeMovie(), Func(self.__onToPrepareBattleThree), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.betweenBattleMusic, looping=1, volume=0.9) + + def __onToPrepareBattleThree(self): + self.notify.debug('----- __onToPrepareBattleThree') + self.unstickBoss() + self.setPosHpr(*ToontownGlobals.LawbotBossBattleThreePosHpr) + self.doneBarrier('RollToBattleThree') + + def exitRollToBattleThree(self): + self.notify.debug('----- exitRollToBattleThree') + self.unstickBoss() + intervalName = 'RollToBattleThree' + self.clearInterval(intervalName) + self.betweenBattleMusic.stop() + + def enterPrepareBattleThree(self): + self.notify.debug('----- enterPrepareBattleThree') + self.cleanupIntervals() + self.controlToons() + self.setToonsToNeutral(self.involvedToons) + self.clearChat() + self.reparentTo(render) + base.playMusic(self.betweenBattleMusic, looping=1, volume=0.9) + prepareBattleThreeMovie = self.__makePrepareBattleThreeMovie() + self.acceptOnce('doneChatPage', self.__onToBattleThree) + intervalName = 'prepareBattleThree' + seq = Sequence(prepareBattleThreeMovie, name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + + def __onToBattleThree(self, elapsed): + self.notify.debug('----- __onToBattleThree') + self.doneBarrier('PrepareBattleThree') + taskMgr.doMethodLater(1, self.__showWaitingMessage, self.uniqueName('WaitingMessage')) + + def exitPrepareBattleThree(self): + self.notify.debug('----- exitPrepareBattleThree') + self.show() + taskMgr.remove(self.uniqueName('WaitingMessage')) + self.ignore('doneChatPage') + intervalName = 'PrepareBattleThree' + self.clearInterval(intervalName) + self.__clearOnscreenMessage() + self.betweenBattleMusic.stop() + + def enterBattleThree(self): + DistributedBossCog.DistributedBossCog.enterBattleThree(self) + self.scaleNodePath.unstash() + localAvatar.setPos(-3, 0, 0) + base.camera.reparentTo(localAvatar) + base.camera.setPos(localAvatar.cameraPositions[0][0]) + base.camera.setHpr(0, 0, 0) + self.clearChat() + self.witnessToon.clearChat() + self.reparentTo(render) + self.happy = 1 + self.raised = 1 + self.forward = 1 + self.doAnimate() + self.accept('enterWitnessStand', self.__touchedWitnessStand) + self.accept('pieSplat', self.__pieSplat) + self.accept('localPieSplat', self.__localPieSplat) + self.accept('outOfPies', self.__outOfPies) + self.accept('begin-pie', self.__foundPieButton) + self.accept('enterDefenseCol', self.__enterDefenseCol) + self.accept('enterProsecutionCol', self.__enterProsecutionCol) + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + taskMgr.doMethodLater(30, self.__howToGetPies, self.uniqueName('PieAdvice')) + self.stickBossToFloor() + self.setPosHpr(*ToontownGlobals.LawbotBossBattleThreePosHpr) + self.bossMaxDamage = ToontownGlobals.LawbotBossMaxDamage + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9) + + def __doneBattleThree(self): + self.notify.debug('----- __doneBattleThree') + self.setState('NearVictory') + self.unstickBoss() + + def exitBattleThree(self): + self.notify.debug('----- exitBattleThree') + DistributedBossCog.DistributedBossCog.exitBattleThree(self) + NametagGlobals.setMasterArrowsOn(1) + bossDoneEventName = self.uniqueName('DestroyedBoss') + self.ignore(bossDoneEventName) + taskMgr.remove(self.uniqueName('StandUp')) + self.ignore('enterWitnessStand') + self.ignore('pieSplat') + self.ignore('localPieSplat') + self.ignore('outOfPies') + self.ignore('begin-pie') + self.ignore('enterDefenseCol') + self.ignore('enterProsecutionCol') + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + if self.bossDamageMovie: + self.bossDamageMovie.finish() + self.bossDamageMovie = None + self.unstickBoss() + taskName = 'RecoverBossDamage' + taskMgr.remove(taskName) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + return + + def enterNearVictory(self): + self.cleanupIntervals() + self.reparentTo(render) + self.setPos(*ToontownGlobals.LawbotBossDeathPos) + self.setHpr(*ToontownGlobals.LawbotBossBattleThreeHpr) + self.clearChat() + self.releaseToons(finalBattle=1) + self.accept('pieSplat', self.__finalPieSplat) + self.accept('localPieSplat', self.__localPieSplat) + self.accept('outOfPies', self.__outOfPies) + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.happy = 0 + self.raised = 0 + self.forward = 1 + self.doAnimate() + self.setDizzy(1) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def exitNearVictory(self): + self.notify.debug('----- exitNearVictory') + self.ignore('pieSplat') + self.ignore('localPieSplat') + self.ignore('outOfPies') + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.setDizzy(0) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + + def enterVictory(self): + self.notify.debug('----- enterVictory') + self.cleanupIntervals() + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.LawbotBossBattleThreePosHpr) + self.loop('neutral') + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.clearChat() + self.witnessToon.clearChat() + self.controlToons() + self.setToonsToNeutral(self.involvedToons) + self.happy = 1 + self.raised = 1 + self.forward = 1 + intervalName = 'VictoryMovie' + seq = Sequence(self.makeVictoryMovie(), Func(self.__continueVictory), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def __continueVictory(self): + self.notify.debug('----- __continueVictory') + self.stopAnimate() + self.doneBarrier('Victory') + + def exitVictory(self): + self.notify.debug('----- exitVictory') + self.stopAnimate() + self.unstash() + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + + def enterDefeat(self): + self.notify.debug('----- enterDefeat') + self.cleanupIntervals() + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.reparentTo(render) + self.clearChat() + self.releaseToons(finalBattle=1) + self.happy = 0 + self.raised = 0 + self.forward = 1 + intervalName = 'DefeatMovie' + seq = Sequence(self.makeDefeatMovie(), Func(self.__continueDefeat), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def __continueDefeat(self): + self.notify.debug('----- __continueDefeat') + self.stopAnimate() + self.doneBarrier('Defeat') + + def exitDefeat(self): + self.notify.debug('----- exitDefeat') + self.stopAnimate() + self.unstash() + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + + def enterReward(self): + self.cleanupIntervals() + self.clearChat() + self.witnessToon.clearChat() + self.stash() + self.stopAnimate() + self.controlToons() + panelName = self.uniqueName('reward') + self.rewardPanel = RewardPanel.RewardPanel(panelName) + victory, camVictory, skipper = MovieToonVictory.doToonVictory(1, self.involvedToons, self.toonRewardIds, self.toonRewardDicts, self.deathList, self.rewardPanel, allowGroupShot=0, uberList=self.uberList, noSkip=True) + ival = Sequence(Parallel(victory, camVictory), Func(self.__doneReward)) + intervalName = 'RewardMovie' + delayDeletes = [] + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'LawbotBoss.enterReward')) + + ival.delayDeletes = delayDeletes + ival.start() + self.storeInterval(ival, intervalName) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def __doneReward(self): + self.notify.debug('----- __doneReward') + self.doneBarrier('Reward') + self.toWalkMode() + + def exitReward(self): + self.notify.debug('----- exitReward') + intervalName = 'RewardMovie' + self.clearInterval(intervalName) + self.unstash() + self.rewardPanel.destroy() + del self.rewardPanel + self.battleThreeMusicTime = 0 + self.battleThreeMusic.stop() + + def enterEpilogue(self): + self.cleanupIntervals() + self.clearChat() + self.witnessToon.clearChat() + self.stash() + self.stopAnimate() + self.controlToons() + self.witnessToon.reparentTo(render) + self.witnessToon.setPosHpr(*ToontownGlobals.LawbotBossWitnessEpiloguePosHpr) + self.witnessToon.loop('Sit') + self.__arrangeToonsAroundWitnessToon() + base.camera.reparentTo(render) + base.camera.setPos(self.witnessToon, -9, 12, 6) + base.camera.lookAt(self.witnessToon, 0, 0, 3) + intervalName = 'EpilogueMovie' + seq = Sequence(self.makeEpilogueMovie(), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + self.accept('doneChatPage', self.__doneEpilogue) + base.playMusic(self.epilogueMusic, looping=1, volume=0.9) + + def __doneEpilogue(self, elapsedTime = 0): + self.notify.debug('----- __doneEpilogue') + intervalName = 'EpilogueMovieToonAnim' + self.clearInterval(intervalName) + track = Parallel(Sequence(Wait(0.5), Func(self.localToonToSafeZone))) + self.storeInterval(track, intervalName) + track.start() + + def exitEpilogue(self): + self.notify.debug('----- exitEpilogue') + self.clearInterval('EpilogueMovieToonAnim') + self.unstash() + self.epilogueMusic.stop() + + def enterFrolic(self): + self.notify.debug('----- enterFrolic') + self.setPosHpr(*ToontownGlobals.LawbotBossBattleOnePosHpr) + DistributedBossCog.DistributedBossCog.enterFrolic(self) + self.show() + + def doorACallback(self, isOpen): + if self.insidesANodePath: + if isOpen: + self.insidesANodePath.unstash() + else: + self.insidesANodePath.stash() + + def doorBCallback(self, isOpen): + if self.insidesBNodePath: + if isOpen: + self.insidesBNodePath.unstash() + else: + self.insidesBNodePath.stash() + + def __toonsToPromotionPosition(self, toonIds, battleNode): + self.notify.debug('----- __toonsToPromotionPosition') + points = BattleBase.BattleBase.toonPoints[len(toonIds) - 1] + for i in xrange(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + toon.reparentTo(render) + pos, h = points[i] + toon.setPosHpr(battleNode, pos[0], pos[1] + 10, pos[2], h, 0, 0) + + def __outOfPies(self): + self.notify.debug('----- outOfPies') + self.__showOnscreenMessage(TTLocalizer.LawbotBossNeedMoreEvidence) + taskMgr.doMethodLater(20, self.__howToGetPies, self.uniqueName('PieAdvice')) + + def __howToGetPies(self, task): + self.notify.debug('----- __howToGetPies') + self.__showOnscreenMessage(TTLocalizer.LawbotBossHowToGetEvidence) + + def __howToThrowPies(self, task): + self.notify.debug('----- __howToThrowPies') + self.__showOnscreenMessage(TTLocalizer.LawbotBossHowToThrowPies) + + def __foundPieButton(self): + self.everThrownPie = 1 + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + + def __touchedWitnessStand(self, entry): + self.sendUpdate('touchWitnessStand', []) + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + base.playSfx(self.piesRestockSfx) + if not self.everThrownPie: + taskMgr.doMethodLater(30, self.__howToThrowPies, self.uniqueName('PieAdvice')) + + def __pieSplat(self, toon, pieCode): + if pieCode == ToontownGlobals.PieCodeBossInsides: + if toon == localAvatar: + self.d_hitBossInsides() + self.flashRed() + elif pieCode == ToontownGlobals.PieCodeBossCog: + if toon == localAvatar: + self.d_hitBoss(1) + if self.dizzy: + self.flashRed() + self.doAnimate('hit', now=1) + elif pieCode == ToontownGlobals.PieCodeDefensePan: + self.flashRed() + self.flashPanBlue() + base.playSfx(self.evidenceHitSfx, node=self.defensePanNodePath, volume=0.25) + if toon == localAvatar: + self.d_hitBoss(self.panDamage) + elif pieCode == ToontownGlobals.PieCodeProsecutionPan: + self.flashGreen() + if toon == localAvatar: + pass + elif pieCode == ToontownGlobals.PieCodeLawyer: + pass + + def __localPieSplat(self, pieCode, entry): + if pieCode == ToontownGlobals.PieCodeLawyer: + self.__lawyerGotHit(entry) + if pieCode != ToontownGlobals.PieCodeToon: + return + avatarDoId = entry.getIntoNodePath().getNetTag('avatarDoId') + if avatarDoId == '': + self.notify.warning('Toon %s has no avatarDoId tag.' % repr(entry.getIntoNodePath())) + return + doId = int(avatarDoId) + if doId != localAvatar.doId: + self.d_hitToon(doId) + + def __lawyerGotHit(self, entry): + lawyerCol = entry.getIntoNodePath() + names = lawyerCol.getName().split('-') + lawyerDoId = int(names[1]) + for lawyer in self.lawyers: + if lawyerDoId == lawyer.doId: + lawyer.sendUpdate('hitByToon', []) + + def __finalPieSplat(self, toon, pieCode): + if pieCode != ToontownGlobals.PieCodeDefensePan: + return + self.sendUpdate('finalPieSplat', []) + self.ignore('pieSplat') + + def cleanupAttacks(self): + self.notify.debug('----- cleanupAttacks') + self.__cleanupStrafe() + + def __cleanupStrafe(self): + self.notify.debug('----- __cleanupStrage') + if self.strafeInterval: + self.strafeInterval.finish() + self.strafeInterval = None + return + + def __cleanupJuryBox(self): + self.notify.debug('----- __cleanupJuryBox') + if self.juryBoxIval: + self.juryBoxIval.finish() + self.juryBoxIval = None + if self.juryBox: + self.juryBox.removeNode() + return + + def doStrafe(self, side, direction): + gearRoot = self.rotateNode.attachNewNode('gearRoot') + if side == 0: + gearRoot.setPos(0, -7, 3) + gearRoot.setHpr(180, 0, 0) + door = self.doorA + else: + gearRoot.setPos(0, 7, 3) + door = self.doorB + gearRoot.setTag('attackCode', str(ToontownGlobals.BossCogStrafeAttack)) + gearModel = self.getGearFrisbee() + gearModel.setScale(0.1) + t = self.getBossDamage() / 100.0 + gearTrack = Parallel() + numGears = int(4 + 6 * t + 0.5) + time = 5.0 - 4.0 * t + spread = 60 * math.pi / 180.0 + if direction == 1: + spread = -spread + dist = 50 + rate = time / numGears + for i in xrange(numGears): + node = gearRoot.attachNewNode(str(i)) + node.hide() + node.setPos(0, 0, 0) + gear = gearModel.instanceTo(node) + angle = (float(i) / (numGears - 1) - 0.5) * spread + x = dist * math.sin(angle) + y = dist * math.cos(angle) + h = random.uniform(-720, 720) + gearTrack.append(Sequence(Wait(i * rate), Func(node.show), Parallel(node.posInterval(1, Point3(x, y, 0), fluid=1), node.hprInterval(1, VBase3(h, 0, 0), fluid=1), Sequence(SoundInterval(self.strafeSfx[i], volume=0.2, node=self), duration=0)), Func(node.detachNode))) + + seq = Sequence(Func(door.request, 'open'), Wait(0.7), gearTrack, Func(door.request, 'close')) + self.__cleanupStrafe() + self.strafeInterval = seq + seq.start() + + def replaceCollisionPolysWithPlanes(self, model): + newCollisionNode = CollisionNode('collisions') + newCollideMask = BitMask32(0) + planes = [] + collList = model.findAllMatches('**/+CollisionNode') + if not collList: + collList = [model] + for cnp in collList: + cn = cnp.node() + if not isinstance(cn, CollisionNode): + self.notify.warning('Not a collision node: %s' % repr(cnp)) + break + newCollideMask = newCollideMask | cn.getIntoCollideMask() + for i in xrange(cn.getNumSolids()): + solid = cn.getSolid(i) + if isinstance(solid, CollisionPolygon): + plane = Plane(solid.getPlane()) + planes.append(plane) + else: + self.notify.warning('Unexpected collision solid: %s' % repr(solid)) + newCollisionNode.addSolid(plane) + + newCollisionNode.setIntoCollideMask(newCollideMask) + threshold = 0.1 + planes.sort(lambda p1, p2: p1.compareTo(p2, threshold)) + lastPlane = None + for plane in planes: + if lastPlane == None or plane.compareTo(lastPlane, threshold) != 0: + cp = CollisionPlane(plane) + newCollisionNode.addSolid(cp) + lastPlane = plane + + return NodePath(newCollisionNode) + + def makeIntroductionMovie(self, delayDeletes): + self.notify.debug('----- makeIntroductionMovie') + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'LawbotBoss.makeIntroductionMovie')) + + finalPodiumPos = Point3(self.podium.getX(), self.podium.getY(), -ToontownGlobals.LawbotBossBattleTwoPosHpr[2]) + finalReflectedPodiumPos = Point3(self.reflectedPodium.getX(), self.reflectedPodium.getY(), 0.0) + finalBossPos = Point3(self.getX(), self.getY(), ToontownGlobals.LawbotBossBattleOnePosHpr[2]) + + track = Track( + (0, Parallel(self.podium.posInterval(5, finalPodiumPos), self.reflectedPodium.posInterval(5, finalReflectedPodiumPos), self.posInterval(5, finalBossPos), Func(self.loop, 'Ff_speech'), Func(self.setChatAbsolute, TTLocalizer.LawbotBossTempIntro0, CFSpeech))), + (5, Parallel(base.camera.posInterval(6, (-2.8, 5.6, 19.3), blendType='easeInOut'), self.posInterval(16, ToontownGlobals.LawbotBossBattleOnePosHpr[:3]))), + (5.6, Func(self.setChatAbsolute, TTLocalizer.LawbotBossTempIntro1, CFSpeech)), + (11, Parallel(Func(self.play, 'Ff_lookRt'), base.camera.posInterval(3, (-2.8, -50.6, 19.3), blendType='easeInOut'))), + (11.5, Func(self.setChatAbsolute, TTLocalizer.LawbotBossTempIntro2, CFSpeech)), + (13, self.actorInterval('Ff_lookRt', playRate=-1)), + (15, Sequence(Func(base.camera.reparentTo, self.witnessToon), Func(base.camera.setPosHpr, 0, 8, 2, 180, 10, 0))), + (15.5, Sequence(Func(self.clearChat), Func(self.witnessToon.setChatAbsolute, TTLocalizer.LawbotBossTempIntro3, CFSpeech))), + (20, Sequence(Func(self.loop, 'Ff_speech'), Func(self.witnessToon.clearChat), Func(base.camera.reparentTo, render), Func(base.camera.setPosHpr, -2.4, -90.6, 19.5, 0, 0, 0))), + (21, Func(self.setChatAbsolute, TTLocalizer.LawbotBossTempIntro4, CFSpeech)), + (25, Func(self.setChatAbsolute, TTLocalizer.LawbotBossTempIntro5, CFSpeech)), + (29, Sequence(Func(self.loop, 'Ff_neutral'), Func(self.clearChat), self.loseCogSuits(self.toonsA + self.toonsB, render, (-2.798, -70, 10, 180, 0, 0)))), + (32, Sequence(self.toonNormalEyes(self.involvedToons), Func(self.loop, 'Ff_neutral'), Func(base.camera.reparentTo, render), Func(base.camera.setPosHpr, -2.4, -96.6, 4.5, 0, 20, 0))), + (33, Func(self.setChatAbsolute, TTLocalizer.LawbotBossTempIntro6, CFSpeech)), + (39, Sequence(Func(self.setChatAbsolute, TTLocalizer.BossCogAttackToons, CFSpeech), base.camera.posHprInterval(0.5, (-2.7, -90, 0), (0, 39.7, 8.3), blendType='easeInOut'))), + (42, Sequence())) + + return Sequence( + Func(self.stickToonsToFloor), + track, + Func(self.unstickToons), name=self.uniqueName('Introduction')) + + def walkToonsToBattlePosition(self, toonIds, battleNode): + self.notify.debug('walkToonsToBattlePosition-----------------------------------------------') + self.notify.debug('toonIds=%s battleNode=%s' % (toonIds, battleNode)) + ival = Parallel() + points = BattleBase.BattleBase.toonPoints[len(toonIds) - 1] + self.notify.debug('walkToonsToBattlePosition: points = %s' % points[0][0]) + for i in xrange(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + pos, h = points[i] + origPos = pos + self.notify.debug('origPos = %s' % origPos) + self.notify.debug('batlleNode.getTransform = %s render.getTransform=%s' % (battleNode.getTransform(), render.getTransform())) + self.notify.debug('render.getScale()=%s battleNode.getScale()=%s' % (render.getScale(), battleNode.getScale())) + myCurPos = self.getPos() + self.notify.debug('myCurPos = %s' % self.getPos()) + self.notify.debug('battleNode.parent() = %s' % battleNode.getParent()) + self.notify.debug('battleNode.parent().getPos() = %s' % battleNode.getParent().getPos()) + bnParent = battleNode.getParent() + battleNode.wrtReparentTo(render) + bnWorldPos = battleNode.getPos() + battleNode.wrtReparentTo(bnParent) + self.notify.debug('battle node world pos = %s' % bnWorldPos) + pos = render.getRelativePoint(battleNode, pos) + self.notify.debug('walktToonsToBattlePosition: render.getRelativePoint result = %s' % pos) + self.notify.debug('walkToonsToBattlePosition: final pos = %s' % pos) + ival.append(Sequence(Func(toon.setPlayRate, 0.8, 'walk'), Func(toon.loop, 'walk'), toon.posInterval(3, pos), Func(toon.setPlayRate, 1, 'walk'), Func(toon.loop, 'neutral'))) + + return ival + + def toonsToBattlePosition(self, toonIds, battleNode): + self.notify.debug('DistrutedLawbotBoss.toonsToBattlePosition----------------------------------------') + self.notify.debug('toonIds=%s battleNode=%s' % (toonIds, battleNode)) + if len(toonIds) < 5: + points = BattleBase.BattleBase.toonPoints[len(toonIds) - 1] + else: + points = list(BattleBase.BattleBase.toonPoints[3]) + points.extend(BattleBase.BattleBase.toonPoints[len(toonIds) - 5]) + self.notify.debug('toonsToBattlePosition: points = %s' % points[0][0]) + for i in xrange(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + toon.wrtReparentTo(render) + pos, h = points[i] + if i > 3: + pos.setY(pos.getY() + 2.0) + bnParent = battleNode.getParent() + battleNode.wrtReparentTo(render) + bnWorldPos = battleNode.getPos() + battleNode.wrtReparentTo(bnParent) + toon.setPosHpr(battleNode, pos[0], pos[1], pos[2], h, 0, 0) + self.notify.debug('new toon pos %s ' % toon.getPos()) + + def touchedGavel(self, gavel, entry): + self.notify.debug('touchedGavel') + attackCodeStr = entry.getIntoNodePath().getNetTag('attackCode') + if attackCodeStr == '': + self.notify.warning('Node %s has no attackCode tag.' % repr(entry.getIntoNodePath())) + return + attackCode = int(attackCodeStr) + into = entry.getIntoNodePath() + self.zapLocalToon(attackCode, into) + + def touchedGavelHandle(self, gavel, entry): + attackCodeStr = entry.getIntoNodePath().getNetTag('attackCode') + if attackCodeStr == '': + self.notify.warning('Node %s has no attackCode tag.' % repr(entry.getIntoNodePath())) + return + attackCode = int(attackCodeStr) + into = entry.getIntoNodePath() + self.zapLocalToon(attackCode, into) + + def createBlock(self, x1, y1, z1, x2, y2, z2, r = 1.0, g = 1.0, b = 1.0, a = 1.0): + gFormat = GeomVertexFormat.getV3n3cpt2() + myVertexData = GeomVertexData('holds my vertices', gFormat, Geom.UHDynamic) + vertexWriter = GeomVertexWriter(myVertexData, 'vertex') + normalWriter = GeomVertexWriter(myVertexData, 'normal') + colorWriter = GeomVertexWriter(myVertexData, 'color') + texWriter = GeomVertexWriter(myVertexData, 'texcoord') + vertexWriter.addData3f(x1, y1, z1) + vertexWriter.addData3f(x2, y1, z1) + vertexWriter.addData3f(x1, y2, z1) + vertexWriter.addData3f(x2, y2, z1) + vertexWriter.addData3f(x1, y1, z2) + vertexWriter.addData3f(x2, y1, z2) + vertexWriter.addData3f(x1, y2, z2) + vertexWriter.addData3f(x2, y2, z2) + for index in xrange(8): + normalWriter.addData3f(1.0, 1.0, 1.0) + colorWriter.addData4f(r, g, b, a) + texWriter.addData2f(1.0, 1.0) + + tris = GeomTriangles(Geom.UHDynamic) + tris.addVertex(0) + tris.addVertex(1) + tris.addVertex(2) + tris.closePrimitive() + tris.addVertex(1) + tris.addVertex(3) + tris.addVertex(2) + tris.closePrimitive() + tris.addVertex(2) + tris.addVertex(3) + tris.addVertex(6) + tris.closePrimitive() + tris.addVertex(3) + tris.addVertex(7) + tris.addVertex(6) + tris.closePrimitive() + tris.addVertex(0) + tris.addVertex(2) + tris.addVertex(4) + tris.closePrimitive() + tris.addVertex(2) + tris.addVertex(6) + tris.addVertex(4) + tris.closePrimitive() + tris.addVertex(1) + tris.addVertex(5) + tris.addVertex(3) + tris.closePrimitive() + tris.addVertex(3) + tris.addVertex(5) + tris.addVertex(7) + tris.closePrimitive() + tris.addVertex(0) + tris.addVertex(4) + tris.addVertex(5) + tris.closePrimitive() + tris.addVertex(1) + tris.addVertex(0) + tris.addVertex(5) + tris.closePrimitive() + tris.addVertex(4) + tris.addVertex(6) + tris.addVertex(7) + tris.closePrimitive() + tris.addVertex(7) + tris.addVertex(5) + tris.addVertex(4) + tris.closePrimitive() + cubeGeom = Geom(myVertexData) + cubeGeom.addPrimitive(tris) + cubeGN = GeomNode('cube') + cubeGN.addGeom(cubeGeom) + return cubeGN + + def __enterDefenseCol(self, entry): + self.notify.debug('__enterDefenseCol') + + def __enterProsecutionCol(self, entry): + self.notify.debug('__enterProsecutionCol') + + def makeVictoryMovie(self): + myFromPos = Point3(ToontownGlobals.LawbotBossBattleThreePosHpr[0], ToontownGlobals.LawbotBossBattleThreePosHpr[1], ToontownGlobals.LawbotBossBattleThreePosHpr[2]) + myToPos = Point3(myFromPos[0], myFromPos[1] + 30, myFromPos[2]) + rollThroughDoor = self.rollBossToPoint(fromPos=myFromPos, fromHpr=None, toPos=myToPos, toHpr=None, reverse=0) + rollTrack = Sequence( + Func(self.getGeomNode().setH, 180), + rollThroughDoor[0], + Func(self.getGeomNode().setH, 0)) + rollTrackDuration = rollTrack.getDuration() + self.notify.debug('rollTrackDuration = %f' % rollTrackDuration) + doorStartPos = self.door3.getPos() + doorEndPos = Point3(doorStartPos[0], doorStartPos[1], doorStartPos[2] + 25) + bossTrack = Track( + (0.5, Sequence( + Func(self.clearChat), + Func(base.camera.reparentTo, render), + Func(base.camera.setPos, -3, 45, 25), + Func(base.camera.setHpr, 0, 10, 0))), + (1.0, Func(self.setChatAbsolute, TTLocalizer.LawbotBossDefenseWins1, CFSpeech)), + (5.5, Func(self.setChatAbsolute, TTLocalizer.LawbotBossDefenseWins2, CFSpeech)), + (9.5, Sequence(Func(base.camera.wrtReparentTo, render))), + (9.6, Parallel( + rollTrack, + Func(self.setChatAbsolute, TTLocalizer.LawbotBossDefenseWins3, CFSpeech), + self.door3.posInterval(2, doorEndPos, startPos=doorStartPos))), + (13.1, Sequence(self.door3.posInterval(1, doorStartPos)))) + retTrack = Parallel(bossTrack, ActorInterval(self, 'Ff_speech', loop=1)) + return bossTrack + + def makeEpilogueMovie(self): + epSpeech = TTLocalizer.WitnessToonCongratulations + epSpeech = self.__talkAboutPromotion(epSpeech) + bossTrack = Sequence(Func(self.witnessToon.animFSM.request, 'neutral'), Func(self.witnessToon.setLocalPageChat, epSpeech, 0)) + return bossTrack + + def makeDefeatMovie(self): + bossTrack = Track((0.0, Sequence(Func(self.clearChat), Func(self.reverseHead), ActorInterval(self, 'Ff_speech'))), (1.0, Func(self.setChatAbsolute, TTLocalizer.LawbotBossProsecutionWins, CFSpeech))) + return bossTrack + + def __makeWitnessToon(self): + if self.witnessToon: + return + self.witnessToon = NPCToons.createLocalNPC(13002) + self.witnessToon.setPosHpr(*ToontownGlobals.LawbotBossWitnessStandPosHpr) + self.witnessToon.animFSM.request('Sit') + + def __cleanupWitnessToon(self): + self.__hideWitnessToon() + if self.witnessToon: + self.witnessToon.removeActive() + self.witnessToon.delete() + self.witnessToon = None + + def __showWitnessToon(self): + if not self.witnessToonOnstage: + self.witnessToon.addActive() + self.witnessToon.reparentTo(self.geom) + seatCenter = self.realWitnessStand.find('**/witnessStandSeatEdge') + center = seatCenter.getPos() + self.notify.debug('center = %s' % center) + self.witnessToon.setPos(center) + self.witnessToon.setH(180) + self.witnessToon.setZ(self.witnessToon.getZ() - 1.5) + self.witnessToon.setY(self.witnessToon.getY() - 1.15) + self.witnessToonOnstage = 1 + + def __hideWitnessToon(self): + if self.witnessToonOnstage: + self.witnessToon.removeActive() + self.witnessToon.detachNode() + self.witnessToonOnstage = 0 + + def __hideToons(self): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.hide() + + def __showToons(self): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.show() + + def __arrangeToonsAroundWitnessToon(self): + radius = 7 + numToons = len(self.involvedToons) + center = (numToons - 1) / 2.0 + for i in xrange(numToons): + toon = self.cr.doId2do.get(self.involvedToons[i]) + if toon: + angle = 90 - 15 * (i - center) + radians = angle * math.pi / 180.0 + x = math.cos(radians) * radius + y = math.sin(radians) * radius + toon.setPos(self.witnessToon, x, y, 0) + toon.headsUp(self.witnessToon) + toon.loop('neutral') + toon.show() + + def __talkAboutPromotion(self, speech): + if self.prevCogSuitLevel < ToontownGlobals.MaxCogSuitLevel: + newCogSuitLevel = localAvatar.getCogLevels()[CogDisguiseGlobals.dept2deptIndex(self.style.dept)] + if newCogSuitLevel == ToontownGlobals.MaxCogSuitLevel: + speech += TTLocalizer.WitnessToonLastPromotion % (ToontownGlobals.MaxCogSuitLevel + 1) + if newCogSuitLevel in ToontownGlobals.CogSuitHPLevels: + speech += TTLocalizer.WitnessToonHPBoost + else: + speech += TTLocalizer.WitnessToonMaxed % (ToontownGlobals.MaxCogSuitLevel + 1) + + if self.keyReward: + speech += TTLocalizer.BossRTKeyReward + + return speech + + def __positionToonsInFrontOfCannons(self): + self.notify.debug('__positionToonsInFrontOfCannons') + index = 0 + self.involvedToons.sort() + for toonId in self.involvedToons: + if index in self.cannons: + cannon = self.cannons[index] + toon = self.cr.doId2do.get(toonId) + self.notify.debug('cannonId = %d' % cannon.doId) + cannonPos = cannon.nodePath.getPos(render) + self.notify.debug('cannonPos = %s' % cannonPos) + if toon: + self.notify.debug('toon = %s' % toon.getName()) + toon.reparentTo(cannon.nodePath) + toon.setPos(0, 8, 0) + toon.setH(180) + renderPos = toon.getPos(render) + self.notify.debug('renderPos =%s' % renderPos) + index += 1 + + self.notify.debug('done with positionToons') + + def __makePrepareBattleTwoMovie(self): + chatString = TTLocalizer.WitnessToonPrepareBattleTwo % ToontownGlobals.LawbotBossJurorsForBalancedScale + movie = Sequence(Func(base.camera.reparentTo, self.witnessToon), Func(base.camera.setPos, 0, 8, 2), Func(base.camera.setHpr, 180, 10, 0), Func(self.witnessToon.setLocalPageChat, chatString, 0)) + return movie + + def __doWitnessPrepareBattleThreeChat(self): + self.notify.debug('__doWitnessPrepareBattleThreeChat: original self.numToonJurorsSeated = %d' % self.numToonJurorsSeated) + self.countToonJurors() + self.notify.debug('after calling self.countToonJurors, numToonJurorsSeated=%d' % self.numToonJurorsSeated) + if self.numToonJurorsSeated == 0: + juryResult = TTLocalizer.WitnessToonNoJuror + elif self.numToonJurorsSeated == 1: + juryResult = TTLocalizer.WitnessToonOneJuror + elif self.numToonJurorsSeated == 12: + juryResult = TTLocalizer.WitnessToonAllJurors + else: + juryResult = TTLocalizer.WitnessToonSomeJurors % self.numToonJurorsSeated + juryResult += '\x07' + trialSpeech = juryResult + trialSpeech += TTLocalizer.WitnessToonPrepareBattleThree + diffSettings = ToontownGlobals.LawbotBossDifficultySettings[self.battleDifficulty] + if diffSettings[4]: + newWeight, self.bonusWeight, self.numJurorsLocalToonSeated = self.calculateWeightOfToon(base.localAvatar.doId) + if self.bonusWeight > 0: + if self.bonusWeight == 1: + juryWeightBonus = TTLocalizer.WitnessToonJuryWeightBonusSingular.get(self.battleDifficulty) + else: + juryWeightBonus = TTLocalizer.WitnessToonJuryWeightBonusPlural.get(self.battleDifficulty) + if juryWeightBonus: + weightBonusText = juryWeightBonus % (self.numJurorsLocalToonSeated, self.bonusWeight) + trialSpeech += '\x07' + trialSpeech += weightBonusText + self.witnessToon.setLocalPageChat(trialSpeech, 0) + + def __makePrepareBattleThreeMovie(self): + movie = Sequence(Func(base.camera.reparentTo, render), Func(base.camera.setPos, -15, 15, 20), Func(base.camera.setHpr, -90, 0, 0), Wait(3), Func(base.camera.reparentTo, self.witnessToon), Func(base.camera.setPos, 0, 8, 2), Func(base.camera.setHpr, 180, 10, 0), Func(self.__doWitnessPrepareBattleThreeChat)) + return movie + + def countToonJurors(self): + self.numToonJurorsSeated = 0 + for key in self.chairs.keys(): + chair = self.chairs[key] + if chair.state == 'ToonJuror' or chair.state == None and chair.newState == 'ToonJuror': + self.numToonJurorsSeated += 1 + + self.notify.debug('self.numToonJurorsSeated = %d' % self.numToonJurorsSeated) + return + + def cleanupPanFlash(self): + if self.panFlashInterval: + self.panFlashInterval.finish() + self.panFlashInterval = None + return + + def flashPanBlue(self): + self.cleanupPanFlash() + intervalName = 'FlashPanBlue' + self.defensePanNodePath.setColorScale(1, 1, 1, 1) + seq = Sequence(self.defensePanNodePath.colorScaleInterval(0.1, colorScale=VBase4(0, 0, 1, 1)), self.defensePanNodePath.colorScaleInterval(0.3, colorScale=VBase4(1, 1, 1, 1)), name=intervalName) + self.panFlashInterval = seq + seq.start() + self.storeInterval(seq, intervalName) + + def saySomething(self, chatString): + intervalName = 'ChiefJusticeTaunt' + seq = Sequence(name=intervalName) + seq.append(Func(self.setChatAbsolute, chatString, CFSpeech)) + seq.append(Wait(4.0)) + seq.append(Func(self.clearChat)) + oldSeq = self.activeIntervals.get(intervalName) + if oldSeq: + oldSeq.finish() + seq.start() + self.storeInterval(seq, intervalName) + + def setTaunt(self, tauntIndex, extraInfo): + gotError = False + if not hasattr(self, 'state'): + self.notify.warning('returning from setTaunt, no attr state') + gotError = True + elif not self.state == 'BattleThree': + self.notify.warning('returning from setTaunt, not in battle three state, state=%s', self.state) + gotError = True + if not hasattr(self, 'nametag'): + self.notify.warning('returning from setTaunt, no attr nametag') + gotError = True + if gotError: + st = StackTrace() + print st + return + chatString = TTLocalizer.LawbotBossTaunts[1] + if tauntIndex == 0: + if extraInfo < len(self.involvedToons): + toonId = self.involvedToons[extraInfo] + toon = base.cr.doId2do.get(toonId) + if toon: + chatString = TTLocalizer.LawbotBossTaunts[tauntIndex] % toon.getName() + else: + chatString = TTLocalizer.LawbotBossTaunts[tauntIndex] + self.saySomething(chatString) + + def toonGotHealed(self, toonId): + toon = base.cr.doId2do.get(toonId) + if toon: + base.playSfx(self.toonUpSfx, node=toon) + + def hideBonusTimer(self): + if self.bonusTimer: + self.bonusTimer.hide() + + def enteredBonusState(self): + self.witnessToon.clearChat() + text = TTLocalizer.WitnessToonBonus % (ToontownGlobals.LawbotBossBonusWeightMultiplier, ToontownGlobals.LawbotBossBonusDuration) + self.witnessToon.setChatAbsolute(text, CFSpeech | CFTimeout) + base.playSfx(self.toonUpSfx) + if not self.bonusTimer: + self.bonusTimer = ToontownTimer.ToontownTimer() + self.bonusTimer.posInTopRightCorner() + self.bonusTimer.show() + self.bonusTimer.countdown(ToontownGlobals.LawbotBossBonusDuration, self.hideBonusTimer) + + def setAttackCode(self, attackCode, avId = 0): + DistributedBossCog.DistributedBossCog.setAttackCode(self, attackCode, avId) + if attackCode == ToontownGlobals.BossCogAreaAttack: + base.playSfx(self.warningSfx) + + def setBattleDifficulty(self, diff): + self.notify.debug('battleDifficulty = %d' % diff) + self.battleDifficulty = diff + + def toonEnteredCannon(self, toonId, cannonIndex): + if base.localAvatar.doId == toonId: + self.cannonIndex = cannonIndex + + def numJurorsSeatedByCannon(self, cannonIndex): + retVal = 0 + for chair in self.chairs.values(): + if chair.state == 'ToonJuror': + if chair.toonJurorIndex == cannonIndex: + retVal += 1 + + return retVal + + def calculateWeightOfToon(self, toonId): + defaultWeight = 1 + bonusWeight = 0 + newWeight = 1 + cannonIndex = self.cannonIndex + numJurors = 0 + if not cannonIndex == None and cannonIndex >= 0: + diffSettings = ToontownGlobals.LawbotBossDifficultySettings[self.battleDifficulty] + if diffSettings[4]: + numJurors = self.numJurorsSeatedByCannon(cannonIndex) + bonusWeight = numJurors - diffSettings[5] + if bonusWeight < 0: + bonusWeight = 0 + newWeight = defaultWeight + bonusWeight + self.notify.debug('toon %d has weight of %d' % (toonId, newWeight)) + return (newWeight, bonusWeight, numJurors) diff --git a/toontown/suit/DistributedLawbotBossAI.py b/toontown/suit/DistributedLawbotBossAI.py new file mode 100755 index 00000000..102ad9e4 --- /dev/null +++ b/toontown/suit/DistributedLawbotBossAI.py @@ -0,0 +1,943 @@ +from otp.ai.AIBaseGlobal import * +from direct.distributed.ClockDelta import * +from otp.ai.MagicWordGlobal import * +import DistributedBossCogAI +from direct.directnotify import DirectNotifyGlobal +from otp.avatar import DistributedAvatarAI +import DistributedSuitAI +from toontown.battle import BattleExperienceAI +from direct.fsm import FSM +from toontown.toonbase import ToontownGlobals +from toontown.toon import InventoryBase +from toontown.toonbase import TTLocalizer +from toontown.battle import BattleBase +from toontown.toon import NPCToons +from toontown.building import SuitBuildingGlobals +import SuitDNA +import random +from toontown.coghq import DistributedLawbotBossGavelAI +from toontown.suit import DistributedLawbotBossSuitAI +from toontown.coghq import DistributedLawbotCannonAI +from toontown.coghq import DistributedLawbotChairAI +from toontown.toonbase import ToontownBattleGlobals +import math + + +class DistributedLawbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotBossAI') + limitHitCount = 6 + hitCountDamage = 35 + numPies = 10 + maxToonLevels = 77 + BossName = "CJ" + + def __init__(self, air): + DistributedBossCogAI.DistributedBossCogAI.__init__(self, air, 'l') + FSM.FSM.__init__(self, 'DistributedLawbotBossAI') + self.lawyers = [] + self.cannons = None + self.chairs = None + self.gavels = None + self.cagedToonNpcId = random.choice(NPCToons.npcFriends.keys()) + self.bossMaxDamage = ToontownGlobals.LawbotBossMaxDamage + self.recoverRate = 0 + self.recoverStartTime = 0 + self.bossDamage = ToontownGlobals.LawbotBossInitialDamage + self.useCannons = 1 + self.numToonJurorsSeated = 0 + self.cannonBallsLeft = {} + self.toonLevels = 0 + if 'Defeat' not in self.keyStates: + self.keyStates.append('Defeat') + self.toonupValue = 1 + self.bonusState = False + self.bonusTimeStarted = 0 + self.numBonusStates = 0 + self.battleThreeTimeStarted = 0 + self.battleThreeTimeInMin = 0 + self.numAreaAttacks = 0 + self.lastAreaAttackTime = 0 + self.weightPerToon = {} + self.cannonIndexPerToon = {} + self.battleDifficulty = 0 + return + + def delete(self): + self.notify.debug('DistributedLawbotBossAI.delete') + self.__deleteBattleThreeObjects() + self.__deleteBattleTwoObjects() + taskName = self.uniqueName('clearBonus') + taskMgr.remove(taskName) + return DistributedBossCogAI.DistributedBossCogAI.delete(self) + + def getHoodId(self): + return ToontownGlobals.LawbotHQ + + def getCagedToonNpcId(self): + return self.cagedToonNpcId + + def magicWordHit(self, damage, avId): + if self.attackCode != ToontownGlobals.BossCogDizzyNow: + self.hitBossInsides() + self.hitBoss(damage) + + def hitBoss(self, bossDamage): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'hitBoss from unknown avatar'): + return + self.validate(avId, bossDamage == 1, 'invalid bossDamage %s' % bossDamage) + if bossDamage < 1: + return + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + if bossDamage <= 12: + newWeight = self.weightPerToon.get(avId) + if newWeight: + bossDamage = newWeight + if self.bonusState and bossDamage <= 12: + bossDamage *= ToontownGlobals.LawbotBossBonusWeightMultiplier + bossDamage = min(self.getBossDamage() + bossDamage, self.bossMaxDamage) + self.b_setBossDamage(bossDamage, 0, 0) + if self.bossDamage >= self.bossMaxDamage: + self.b_setState('Victory') + else: + self.__recordHit() + + def healBoss(self, bossHeal): + bossDamage = -bossHeal + avId = self.air.getAvatarIdFromSender() + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + bossDamage = min(self.getBossDamage() + bossDamage, self.bossMaxDamage) + bossDamage = max(bossDamage, 0) + self.b_setBossDamage(bossDamage, 0, 0) + if self.bossDamage == 0: + self.b_setState('Defeat') + else: + self.__recordHit() + + def hitBossInsides(self): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'hitBossInsides from unknown avatar'): + return + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + self.b_setAttackCode(ToontownGlobals.BossCogDizzyNow) + self.b_setBossDamage(self.getBossDamage(), 0, 0) + + def hitToon(self, toonId): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId != toonId, 'hitToon on self'): + return + if avId not in self.involvedToons or toonId not in self.involvedToons: + return + toon = self.air.doId2do.get(toonId) + if toon: + self.healToon(toon, self.toonupValue) + self.sendUpdate('toonGotHealed', [toonId]) + + def touchCage(self): + avId = self.air.getAvatarIdFromSender() + currState = self.getCurrentOrNextState() + if currState != 'BattleThree' and currState != 'NearVictory': + return + if not self.validate(avId, avId in self.involvedToons, 'touchCage from unknown avatar'): + return + toon = simbase.air.doId2do.get(avId) + if toon: + toon.b_setNumPies(self.numPies) + toon.__touchedCage = 1 + + def touchWitnessStand(self): + self.touchCage() + + def finalPieSplat(self): + self.notify.debug('finalPieSplat') + if self.state != 'NearVictory': + return + self.b_setState('Victory') + + def doTaunt(self): + if not self.state == 'BattleThree': + return + tauntIndex = random.randrange(len(TTLocalizer.LawbotBossTaunts)) + extraInfo = 0 + if tauntIndex == 0 and self.involvedToons: + extraInfo = random.randrange(len(self.involvedToons)) + self.sendUpdate('setTaunt', [tauntIndex, extraInfo]) + + def doNextAttack(self, task): + for lawyer in self.lawyers: + lawyer.doNextAttack(self) + + self.waitForNextAttack(ToontownGlobals.LawbotBossLawyerCycleTime) + timeSinceLastAttack = globalClock.getFrameTime() - self.lastAreaAttackTime + allowedByTime = 15 < timeSinceLastAttack or self.lastAreaAttackTime == 0 + doAttack = random.randrange(1,101) + self.notify.debug('allowedByTime=%d doAttack=%d' % (allowedByTime, doAttack)) + if doAttack <= ToontownGlobals.LawbotBossChanceToDoAreaAttack and allowedByTime: + self.__doAreaAttack() + self.numAreaAttacks += 1 + self.lastAreaAttackTime = globalClock.getFrameTime() + else: + chanceToDoTaunt = ToontownGlobals.LawbotBossChanceForTaunt + action = random.randrange(1,101) + if action <= chanceToDoTaunt: + self.doTaunt() + pass + return + if self.attackCode == ToontownGlobals.BossCogDizzyNow: + attackCode = ToontownGlobals.BossCogRecoverDizzyAttack + else: + attackCode = random.choice([ToontownGlobals.BossCogAreaAttack, + ToontownGlobals.BossCogFrontAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack]) + if attackCode == ToontownGlobals.BossCogAreaAttack: + self.__doAreaAttack() + elif attackCode == ToontownGlobals.BossCogDirectedAttack: + self.__doDirectedAttack() + else: + self.b_setAttackCode(attackCode) + def __doAreaAttack(self): + self.b_setAttackCode(ToontownGlobals.BossCogAreaAttack) + + def __doDirectedAttack(self): + if self.nearToons: + toonId = random.choice(self.nearToons) + self.b_setAttackCode(ToontownGlobals.BossCogDirectedAttack, toonId) + else: + self.__doAreaAttack() + + def b_setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + self.d_setBossDamage(bossDamage, recoverRate, recoverStartTime) + self.setBossDamage(bossDamage, recoverRate, recoverStartTime) + + def setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + self.bossDamage = bossDamage + self.recoverRate = recoverRate + self.recoverStartTime = recoverStartTime + + def getBossDamage(self): + now = globalClock.getFrameTime() + elapsed = now - self.recoverStartTime + return int(max(self.bossDamage - self.recoverRate * elapsed / 60.0, 0)) + + def d_setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + timestamp = globalClockDelta.localToNetworkTime(recoverStartTime) + self.sendUpdate('setBossDamage', [bossDamage, recoverRate, timestamp]) + + def waitForNextStrafe(self, delayTime): + currState = self.getCurrentOrNextState() + if currState == 'BattleThree': + taskName = self.uniqueName('NextStrafe') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.doNextStrafe, taskName) + + def stopStrafes(self): + taskName = self.uniqueName('NextStrafe') + taskMgr.remove(taskName) + + def doNextStrafe(self, task): + if self.attackCode != ToontownGlobals.BossCogDizzyNow: + side = random.choice([0, 1]) + direction = random.choice([0, 1]) + self.sendUpdate('doStrafe', [side, direction]) + delayTime = 9 + self.waitForNextStrafe(delayTime) + + def __sendLawyerIds(self): + lawyerIds = [] + for suit in self.lawyers: + lawyerIds.append(suit.doId) + + self.sendUpdate('setLawyerIds', [lawyerIds]) + + def d_cagedToonBattleThree(self, index, avId): + self.sendUpdate('cagedToonBattleThree', [index, avId]) + + def formatReward(self): + return str(self.cagedToonNpcId) + + def makeBattleOneBattles(self): + self.postBattleState = 'RollToBattleTwo' + self.initializeBattles(1, ToontownGlobals.LawbotBossBattleOnePosHpr) + + def generateSuits(self, battleNumber): + if battleNumber == 1: + weakenedValue = ((1, 1), + (2, 2), + (2, 2), + (1, 1), + (1, 1, 1, 1, 1)) + listVersion = list(SuitBuildingGlobals.SuitBuildingInfo) + if simbase.config.GetBool('lawbot-boss-cheat', 0): + listVersion[13] = weakenedValue + SuitBuildingGlobals.SuitBuildingInfo = tuple(listVersion) + return self.invokeSuitPlanner(13, 0) + else: + return self.invokeSuitPlanner(13, 1) + + def removeToon(self, avId): + toon = simbase.air.doId2do.get(avId) + if toon: + toon.b_setNumPies(0) + DistributedBossCogAI.DistributedBossCogAI.removeToon(self, avId) + + def enterOff(self): + self.notify.debug('enterOff') + DistributedBossCogAI.DistributedBossCogAI.enterOff(self) + self.__deleteBattleThreeObjects() + self.__resetLawyers() + + def enterElevator(self): + self.notify.debug('enterElevatro') + DistributedBossCogAI.DistributedBossCogAI.enterElevator(self) + self.b_setBossDamage(ToontownGlobals.LawbotBossInitialDamage, 0, 0) + + def enterIntroduction(self): + self.notify.debug('enterIntroduction') + DistributedBossCogAI.DistributedBossCogAI.enterIntroduction(self) + self.b_setBossDamage(ToontownGlobals.LawbotBossInitialDamage, 0, 0) + self.__makeChairs() + + def exitIntroduction(self): + self.notify.debug('exitIntroduction') + DistributedBossCogAI.DistributedBossCogAI.exitIntroduction(self) + + def enterRollToBattleTwo(self): + self.divideToons() + self.__makeCannons() + self.barrier = self.beginBarrier('RollToBattleTwo', self.involvedToons, 50, self.__doneRollToBattleTwo) + + def __doneRollToBattleTwo(self, avIds): + self.b_setState('PrepareBattleTwo') + + def exitRollToBattleTwo(self): + self.ignoreBarrier(self.barrier) + + def enterPrepareBattleTwo(self): + self.__makeCannons() + self.barrier = self.beginBarrier('PrepareBattleTwo', self.involvedToons, 45, self.__donePrepareBattleTwo) + self.makeBattleTwoBattles() + + def __donePrepareBattleTwo(self, avIds): + self.b_setState('BattleTwo') + + def exitPrepareBattleTwo(self): + self.ignoreBarrier(self.barrier) + + def __makeCannons(self): + if self.cannons == None: + self.cannons = [] + startPt = Point3(*ToontownGlobals.LawbotBossCannonPosA) + endPt = Point3(*ToontownGlobals.LawbotBossCannonPosB) + totalDisplacement = endPt - startPt + self.notify.debug('totalDisplacement=%s' % totalDisplacement) + numToons = len(self.involvedToons) + stepDisplacement = totalDisplacement / (numToons + 1) + for index in xrange(numToons): + newPos = stepDisplacement * (index + 1) + self.notify.debug('curDisplacement = %s' % newPos) + newPos += startPt + self.notify.debug('newPos = %s' % newPos) + cannon = DistributedLawbotCannonAI.DistributedLawbotCannonAI(self.air, self, index, newPos[0], newPos[1], newPos[2], -90, 0, 0) + cannon.generateWithRequired(self.zoneId) + self.cannons.append(cannon) + + return + + def __makeChairs(self): + if self.chairs == None: + self.chairs = [] + for index in xrange(12): + chair = DistributedLawbotChairAI.DistributedLawbotChairAI(self.air, self, index) + chair.generateWithRequired(self.zoneId) + self.chairs.append(chair) + + return + + def __makeBattleTwoObjects(self): + self.__makeCannons() + self.__makeChairs() + + def __deleteCannons(self): + if self.cannons != None: + for cannon in self.cannons: + cannon.requestDelete() + + self.cannons = None + return + + def __deleteChairs(self): + if self.chairs != None: + for chair in self.chairs: + chair.requestDelete() + + self.chairs = None + return + + def __stopChairs(self): + if self.chairs != None: + for chair in self.chairs: + chair.stopCogs() + + return + + def __deleteBattleTwoObjects(self): + self.__deleteCannons() + self.__deleteChairs() + + def getCannonBallsLeft(self, avId): + if avId in self.cannonBallsLeft: + return self.cannonBallsLeft[avId] + else: + self.notify.warning('getCannonBalsLeft invalid avId: %d' % avId) + return 0 + + def decrementCannonBallsLeft(self, avId): + if avId in self.cannonBallsLeft: + self.cannonBallsLeft[avId] -= 1 + if self.cannonBallsLeft[avId] < 0: + self.notify.warning('decrementCannonBallsLeft <0 cannonballs for %d' % avId) + self.cannonBallsLeft[avId] = 0 + else: + self.notify.warning('decrementCannonBallsLeft invalid avId: %d' % avId) + + def makeBattleTwoBattles(self): + self.postBattleState = 'RollToBattleThree' + if self.useCannons: + self.__makeBattleTwoObjects() + else: + self.initializeBattles(2, ToontownGlobals.LawbotBossBattleTwoPosHpr) + + def enterBattleTwo(self): + if self.useCannons: + self.cannonBallsLeft = {} + for toonId in self.involvedToons: + self.cannonBallsLeft[toonId] = ToontownGlobals.LawbotBossCannonBallMax + + for chair in self.chairs: + chair.requestEmptyJuror() + + self.barrier = self.beginBarrier('BattleTwo', self.involvedToons, ToontownGlobals.LawbotBossJuryBoxMoveTime + 1, self.__doneBattleTwo) + if not self.useCannons: + if self.battleA: + self.battleA.startBattle(self.toonsA, self.suitsA) + if self.battleB: + self.battleB.startBattle(self.toonsB, self.suitsB) + + def __doneBattleTwo(self, avIds): + if self.useCannons: + self.b_setState('PrepareBattleThree') + else: + self.b_setState('RollToBattleThree') + + def exitBattleTwo(self): + self.resetBattles() + self.numToonJurorsSeated = 0 + for chair in self.chairs: + self.notify.debug('chair.state==%s' % chair.state) + if chair.state == 'ToonJuror': + self.numToonJurorsSeated += 1 + + self.notify.debug('numToonJurorsSeated=%d' % self.numToonJurorsSeated) + self.air.writeServerEvent('jurorsSeated', self.doId, '%s|%s|%s' % (self.dept, self.involvedToons, self.numToonJurorsSeated)) + self.__deleteCannons() + self.__stopChairs() + + def enterRollToBattleThree(self): + self.divideToons() + self.barrier = self.beginBarrier('RollToBattleThree', self.involvedToons, 20, self.__doneRollToBattleThree) + + def __doneRollToBattleThree(self, avIds): + self.b_setState('PrepareBattleThree') + + def exitRollToBattleThree(self): + self.ignoreBarrier(self.barrier) + + def enterPrepareBattleThree(self): + self.calcAndSetBattleDifficulty() + self.barrier = self.beginBarrier('PrepareBattleThree', self.involvedToons, 45, self.__donePrepareBattleThree) + + def __donePrepareBattleThree(self, avIds): + self.b_setState('BattleThree') + + def exitPrepareBattleThree(self): + self.ignoreBarrier(self.barrier) + + def enterBattleThree(self): + self.battleThreeTimeStarted = globalClock.getFrameTime() + self.calcAndSetBattleDifficulty() + self.calculateWeightPerToon() + diffSettings = ToontownGlobals.LawbotBossDifficultySettings[self.battleDifficulty] + self.ammoCount = diffSettings[0] + self.numGavels = diffSettings[1] + if self.numGavels >= len(ToontownGlobals.LawbotBossGavelPosHprs): + self.numGavels = len(ToontownGlobals.LawbotBossGavelPosHprs) + self.numLawyers = diffSettings[2] + if self.numLawyers >= len(ToontownGlobals.LawbotBossLawyerPosHprs): + self.numLawyers = len(ToontownGlobals.LawbotBossLawyerPosHprs) + self.toonupValue = diffSettings[3] + self.notify.debug('diffLevel=%d ammoCount=%d gavels=%d lawyers = %d, toonup=%d' % (self.battleDifficulty, + self.ammoCount, + self.numGavels, + self.numLawyers, + self.toonupValue)) + self.air.writeServerEvent('lawbotBossSettings', self.doId, '%s|%s|%s|%s|%s|%s' % (self.dept, + self.battleDifficulty, + self.ammoCount, + self.numGavels, + self.numLawyers, + self.toonupValue)) + self.__makeBattleThreeObjects() + self.__makeLawyers() + self.numPies = self.ammoCount + self.resetBattles() + self.setPieType() + jurorsOver = self.numToonJurorsSeated - ToontownGlobals.LawbotBossJurorsForBalancedScale + dmgAdjust = jurorsOver * ToontownGlobals.LawbotBossDamagePerJuror + self.b_setBossDamage(ToontownGlobals.LawbotBossInitialDamage + dmgAdjust, 0, 0) + if simbase.config.GetBool('lawbot-boss-cheat', 0): + self.b_setBossDamage(ToontownGlobals.LawbotBossMaxDamage - 1, 0, 0) + self.battleThreeStart = globalClock.getFrameTime() + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.__touchedCage = 0 + + for aGavel in self.gavels: + aGavel.turnOn() + + self.waitForNextAttack(5) + self.notify.debug('battleDifficulty = %d' % self.battleDifficulty) + self.numToonsAtStart = len(self.involvedToons) + + def getToonDifficulty(self): + totalCogSuitTier = 0 + totalToons = 0 + + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + totalToons += 1 + totalCogSuitTier += toon.cogTypes[1] + + averageTier = math.floor(totalCogSuitTier / totalToons) + 1 + return int(averageTier) + + def __saySomething(self, task = None): + index = None + avId = 0 + if len(self.involvedToons) == 0: + return + avId = random.choice(self.involvedToons) + toon = simbase.air.doId2do.get(avId) + if toon.__touchedCage: + if self.cagedToonDialogIndex <= TTLocalizer.CagedToonBattleThreeMaxAdvice: + index = self.cagedToonDialogIndex + self.cagedToonDialogIndex += 1 + elif random.random() < 0.2: + index = random.randrange(100, TTLocalizer.CagedToonBattleThreeMaxAdvice + 1) + else: + index = random.randrange(20, TTLocalizer.CagedToonBattleThreeMaxTouchCage + 1) + if index: + self.d_cagedToonBattleThree(index, avId) + self.__saySomethingLater() + return + + def __saySomethingLater(self, delayTime = 15): + taskName = self.uniqueName('CagedToonSaySomething') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.__saySomething, taskName) + + def __goodJump(self, avId): + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + index = random.randrange(10, TTLocalizer.CagedToonBattleThreeMaxGivePies + 1) + self.d_cagedToonBattleThree(index, avId) + self.__saySomethingLater() + + def __makeBattleThreeObjects(self): + if self.gavels == None: + self.gavels = [] + for index in xrange(self.numGavels): + gavel = DistributedLawbotBossGavelAI.DistributedLawbotBossGavelAI(self.air, self, index) + gavel.generateWithRequired(self.zoneId) + self.gavels.append(gavel) + + return + + def __deleteBattleThreeObjects(self): + if self.gavels != None: + for gavel in self.gavels: + gavel.request('Off') + gavel.requestDelete() + + self.gavels = None + return + + def doBattleThreeInfo(self): + didTheyWin = 0 + if self.bossDamage == ToontownGlobals.LawbotBossMaxDamage: + didTheyWin = 1 + self.battleThreeTimeInMin = globalClock.getFrameTime() - self.battleThreeTimeStarted + self.battleThreeTimeInMin /= 60.0 + self.numToonsAtEnd = 0 + toonHps = [] + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + self.numToonsAtEnd += 1 + toonHps.append(toon.hp) + + self.air.writeServerEvent('b3Info', self.doId, '%d|%.2f|%d|%d|%d|%d|%d|%d|%d|%d|%d|%d|%s|%s' % (didTheyWin, + self.battleThreeTimeInMin, + self.numToonsAtStart, + self.numToonsAtEnd, + self.numToonJurorsSeated, + self.battleDifficulty, + self.ammoCount, + self.numGavels, + self.numLawyers, + self.toonupValue, + self.numBonusStates, + self.numAreaAttacks, + toonHps, + self.weightPerToon)) + + def exitBattleThree(self): + self.doBattleThreeInfo() + self.stopAttacks() + self.stopStrafes() + taskName = self.uniqueName('CagedToonSaySomething') + taskMgr.remove(taskName) + self.__resetLawyers() + self.__deleteBattleThreeObjects() + + def enterNearVictory(self): + self.resetBattles() + + def exitNearVictory(self): + pass + + def enterVictory(self): + self.resetBattles() + self.suitsKilled.append({'type': None, + 'level': None, + 'track': self.dna.dept, + 'isSkelecog': 0, + 'isForeman': 0, + 'isBoss': 1, + 'isSupervisor': 0, + 'isVirtual': 0, + 'activeToons': self.involvedToons[:]}) + self.addStats() + self.barrier = self.beginBarrier('Victory', self.involvedToons, 30, self.__doneVictory) + return + + def __doneVictory(self, avIds): + self.d_setBattleExperience() + self.b_setState('Reward') + BattleExperienceAI.assignRewards(self.involvedToons, self.toonSkillPtsGained, self.suitsKilled, ToontownGlobals.dept2cogHQ(self.dept), self.helpfulToons) + preferredDept = random.randrange(len(SuitDNA.suitDepts)) + #typeWeights = ['single'] * 70 + ['building'] * 27 + ['invasion'] * 3 + preferredSummonType = random.choice(['building', 'invasion', 'cogdo', 'skelinvasion', 'waiterinvasion', 'v2invasion']) + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + self.giveCogSummonReward(toon, preferredDept, preferredSummonType) + toon.b_promote(self.deptIndex) + + def giveCogSummonReward(self, toon, prefDeptIndex, prefSummonType): + cogLevel = self.toonLevels - 1 + deptIndex = prefDeptIndex + summonType = prefSummonType + hasSummon = toon.hasParticularCogSummons(prefDeptIndex, cogLevel, prefSummonType) + self.notify.debug('trying to find another reward') + if not toon.hasParticularCogSummons(prefDeptIndex, cogLevel, 'building'): + summonType = 'building' + elif not toon.hasParticularCogSummons(prefDeptIndex, cogLevel, 'invasion'): + summonType = 'invasion' + elif not toon.hasParticularCogSummons(prefDeptIndex, cogLevel, 'cogdo'): + summonType = 'cogdo' + elif not toon.hasParticularCogSummons(prefDeptIndex, cogLevel, 'skelinvasion'): + summonType = 'skelinvasion' + elif not toon.hasParticularCogSummons(prefDeptIndex, cogLevel, 'waiterinvasion'): + summonType = 'waiterinvasion' + elif not toon.hasParticularCogSummons(prefDeptIndex, cogLevel, 'v2invasion'): + summonType = 'v2invasion' + else: + foundOne = False + for curDeptIndex in xrange(len(SuitDNA.suitDepts)): + if not toon.hasParticularCogSummons(curDeptIndex, cogLevel, prefSummonType): + deptIndex = curDeptIndex + foundOne = True + break + elif not toon.hasParticularCogSummons(curDeptIndex, cogLevel, 'building'): + deptIndex = curDeptIndex + summonType = 'building' + foundOne = True + break + elif not toon.hasParticularCogSummons(curDeptIndex, cogLevel, 'invasion'): + summonType = 'invasion' + deptIndex = curDeptIndex + foundOne = True + break + elif not toon.hasParticularCogSummons(curDeptIndex, cogLevel, 'cogdo'): + summonType = 'cogdo' + deptIndex = curDeptIndex + foundOne = True + break + elif not toon.hasParticularCogSummons(curDeptIndex, cogLevel, 'skelinvasion'): + summonType = 'skelinvasion' + deptIndex = curDeptIndex + foundOne = True + break + elif not toon.hasParticularCogSummons(curDeptIndex, cogLevel, 'waiterinvasion'): + summonType = 'waiterinvasion' + deptIndex = curDeptIndex + foundOne = True + break + elif not toon.hasParticularCogSummons(curDeptIndex, cogLevel, 'v2invasion'): + summonType = 'v2invasion' + deptIndex = curDeptIndex + foundOne = True + break + + possibleCogLevel = range(SuitDNA.suitsPerDept) + possibleDeptIndex = range(len(SuitDNA.suitDepts)) + possibleSummonType = ['building', 'invasion', 'cogdo', 'skelinvasion', 'waiterinvasion', 'v2invasion'] + #typeWeights = ['single'] * 70 + ['building'] * 27 + ['invasion'] * 3 + if not foundOne: + for i in xrange(5): + randomCogLevel = random.choice(possibleCogLevel) + randomSummonType = random.choice(possibleSummonType) + randomDeptIndex = random.choice(possibleDeptIndex) + if not toon.hasParticularCogSummons(randomDeptIndex, randomCogLevel, randomSummonType): + foundOne = True + cogLevel = randomCogLevel + summonType = randomSummonType + deptIndex = randomDeptIndex + break + + for curType in possibleSummonType: + if foundOne: + break + for curCogLevel in possibleCogLevel: + if foundOne: + break + for curDeptIndex in possibleDeptIndex: + if foundOne: + break + if not toon.hasParticularCogSummons(curDeptIndex, curCogLevel, curType): + foundOne = True + cogLevel = curCogLevel + summonType = curType + deptIndex = curDeptIndex + + if not foundOne: + cogLevel = None + summonType = None + deptIndex = None + toon.assignNewCogSummons(cogLevel, summonType, deptIndex) + return + + def exitVictory(self): + self.takeAwayPies() + + def enterDefeat(self): + self.resetBattles() + self.barrier = self.beginBarrier('Defeat', self.involvedToons, 10, self.__doneDefeat) + + def __doneDefeat(self, avIds): + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + toon.b_setHp(0) + + def exitDefeat(self): + self.takeAwayPies() + + def enterFrolic(self): + DistributedBossCogAI.DistributedBossCogAI.enterFrolic(self) + self.b_setBossDamage(0, 0, 0) + + def setPieType(self): + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.d_setPieType(ToontownBattleGlobals.MAX_TRACK_INDEX + 1) + + def takeAwayPies(self): + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.b_setNumPies(0) + + def __recordHit(self): + now = globalClock.getFrameTime() + self.hitCount += 1 + if self.hitCount < self.limitHitCount or self.bossDamage < self.hitCountDamage: + return + + def __resetLawyers(self): + for suit in self.lawyers: + suit.requestDelete() + + self.lawyers = [] + + def __makeLawyers(self): + self.__resetLawyers() + lawCogChoices = ['b', + 'dt', + 'ac', + 'bs', + 'sd', + 'le', + 'bw'] + for i in xrange(self.numLawyers): + suit = DistributedLawbotBossSuitAI.DistributedLawbotBossSuitAI(self.air, None) + suit.dna = SuitDNA.SuitDNA() + lawCog = random.choice(lawCogChoices) + suit.dna.newSuit(lawCog) + suit.setPosHpr(*ToontownGlobals.LawbotBossLawyerPosHprs[i]) + suit.setBoss(self) + suit.generateWithRequired(self.zoneId) + self.lawyers.append(suit) + + self.__sendLawyerIds() + return + + def hitChair(self, chairIndex, npcToonIndex): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'hitChair from unknown avatar'): + return + if not self.chairs: + return + if chairIndex < 0 or chairIndex >= len(self.chairs): + self.notify.warning('invalid chairIndex = %d' % chairIndex) + return + if not self.state == 'BattleTwo': + return + self.chairs[chairIndex].b_setToonJurorIndex(npcToonIndex) + self.chairs[chairIndex].requestToonJuror() + + def clearBonus(self, taskName): + if self and hasattr(self, 'bonusState'): + self.bonusState = False + + def startBonusState(self): + self.notify.debug('startBonusState') + self.bonusTimeStarted = globalClock.getFrameTime() + self.bonusState = True + self.numBonusStates += 1 + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + self.healToon(toon, ToontownGlobals.LawbotBossBonusToonup) + + taskMgr.doMethodLater(ToontownGlobals.LawbotBossBonusDuration, self.clearBonus, self.uniqueName('clearBonus')) + self.sendUpdate('enteredBonusState', []) + + def areAllLawyersStunned(self): + for lawyer in self.lawyers: + if not lawyer.stunned: + return False + + return True + + def checkForBonusState(self): + if self.bonusState: + return + if not self.areAllLawyersStunned(): + return + curTime = globalClock.getFrameTime() + delta = curTime - self.bonusTimeStarted + if ToontownGlobals.LawbotBossBonusWaitTime < delta: + self.startBonusState() + + def toonEnteredCannon(self, toonId, cannonIndex): + self.cannonIndexPerToon[toonId] = cannonIndex + + def numJurorsSeatedByCannon(self, cannonIndex): + retVal = 0 + for chair in self.chairs: + if chair.state == 'ToonJuror': + if chair.toonJurorIndex == cannonIndex: + retVal += 1 + + return retVal + + def calculateWeightPerToon(self): + for toonId in self.involvedToons: + defaultWeight = 1 + bonusWeight = 0 + cannonIndex = self.cannonIndexPerToon.get(toonId) + if not cannonIndex == None: + diffSettings = ToontownGlobals.LawbotBossDifficultySettings[self.battleDifficulty] + if diffSettings[4]: + bonusWeight = self.numJurorsSeatedByCannon(cannonIndex) - diffSettings[5] + if bonusWeight < 0: + bonusWeight = 0 + newWeight = defaultWeight + bonusWeight + self.weightPerToon[toonId] = newWeight + self.notify.debug('toon %d has weight of %d' % (toonId, newWeight)) + + def b_setBattleDifficulty(self, batDiff): + self.setBattleDifficulty(batDiff) + self.d_setBattleDifficulty(batDiff) + + def setBattleDifficulty(self, batDiff): + self.battleDifficulty = batDiff + + def d_setBattleDifficulty(self, batDiff): + self.sendUpdate('setBattleDifficulty', [batDiff]) + + def calcAndSetBattleDifficulty(self): + self.toonLevels = self.getToonDifficulty() + self.b_setBattleDifficulty(self.toonLevels) + + +@magicWord(category=CATEGORY_ADMINISTRATOR) +def skipCJ(): + """ + Skips to the final round of the CJ. + """ + invoker = spellbook.getInvoker() + boss = None + for do in simbase.air.doId2do.values(): + if isinstance(do, DistributedLawbotBossAI): + if invoker.doId in do.involvedToons: + boss = do + break + if not boss: + return "You aren't in a CJ!" + if boss.state in ('PrepareBattleThree', 'BattleThree'): + return "You can't skip this round." + boss.exitIntroduction() + boss.b_setState('PrepareBattleThree') + + +@magicWord(category=CATEGORY_ADMINISTRATOR) +def killCJ(): + """ + Kills the CJ. + """ + invoker = spellbook.getInvoker() + boss = None + for do in simbase.air.doId2do.values(): + if isinstance(do, DistributedLawbotBossAI): + if invoker.doId in do.involvedToons: + boss = do + break + if not boss: + return "You aren't in a CJ" + boss.b_setState('Victory') + return 'Killed CJ.' diff --git a/toontown/suit/DistributedLawbotBossSuit.py b/toontown/suit/DistributedLawbotBossSuit.py new file mode 100755 index 00000000..74070ae9 --- /dev/null +++ b/toontown/suit/DistributedLawbotBossSuit.py @@ -0,0 +1,384 @@ +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +import DistributedSuitBase +from toontown.toonbase import ToontownGlobals +from toontown.battle import MovieUtil + +class DistributedLawbotBossSuit(DistributedSuitBase.DistributedSuitBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotBossSuit') + timeToShow = 1.0 + timeToRelease = 3.15 + throwPaperEndTime = 4.33 + + def __init__(self, cr): + self.flyingEvidenceTrack = None + try: + self.DistributedSuit_initialized + except: + self.DistributedSuit_initialized = 1 + DistributedSuitBase.DistributedSuitBase.__init__(self, cr) + self.activeIntervals = {} + self.boss = None + self.fsm = ClassicFSM.ClassicFSM('DistributedLawbotBossSuit', [ + State.State('Off', + self.enterOff, + self.exitOff, [ + 'Walk', + 'Battle', + 'neutral']), + State.State('Walk', + self.enterWalk, + self.exitWalk, [ + 'WaitForBattle', + 'Battle']), + State.State('Battle', + self.enterBattle, + self.exitBattle, []), + State.State('neutral', + self.enterNeutral, + self.exitNeutral, [ + 'PreThrowProsecute', + 'PreThrowAttack', + 'Stunned']), + State.State('PreThrowProsecute', + self.enterPreThrowProsecute, + self.exitPreThrowProsecute, + ['PostThrowProsecute', + 'neutral', + 'Stunned']), + State.State('PostThrowProsecute', + self.enterPostThrowProsecute, + self.exitPostThrowProsecute, [ + 'neutral', + 'Stunned']), + State.State('PreThrowAttack', + self.enterPreThrowAttack, + self.exitPreThrowAttack, [ + 'PostThrowAttack', + 'neutral', + 'Stunned']), + State.State('PostThrowAttack', + self.enterPostThrowAttack, + self.exitPostThrowAttack, [ + 'neutral', + 'Stunned']), + State.State('Stunned', + self.enterStunned, + self.exitStunned, [ + 'neutral']), + State.State('WaitForBattle', + self.enterWaitForBattle, + self.exitWaitForBattle, [ + 'Battle'])], + 'Off', 'Off') + self.fsm.enterInitialState() + + return + + def generate(self): + self.notify.debug('DLBS.generate:') + DistributedSuitBase.DistributedSuitBase.generate(self) + + def announceGenerate(self): + DistributedSuitBase.DistributedSuitBase.announceGenerate(self) + self.notify.debug('DLBS.announceGenerate') + colNode = self.find('**/distAvatarCollNode*') + colNode.setTag('pieCode', str(ToontownGlobals.PieCodeLawyer)) + self.attackEvidenceA = self.getEvidence(True) + self.attackEvidenceB = self.getEvidence(True) + self.attackEvidence = self.attackEvidenceA + self.prosecuteEvidence = self.getEvidence(False) + self.hideName() + self.setPickable(False) + + def disable(self): + self.notify.debug('DistributedSuit %d: disabling' % self.getDoId()) + self.setState('Off') + DistributedSuitBase.DistributedSuitBase.disable(self) + self.cleanupIntervals() + self.boss = None + return + + def delete(self): + try: + self.DistributedSuit_deleted + except: + self.DistributedSuit_deleted = 1 + self.notify.debug('DistributedSuit %d: deleting' % self.getDoId()) + del self.fsm + DistributedSuitBase.DistributedSuitBase.delete(self) + + def d_requestBattle(self, pos, hpr): + self.cr.playGame.getPlace().setState('WaitForBattle') + self.sendUpdate('requestBattle', [pos[0], + pos[1], + pos[2], + hpr[0], + hpr[1], + hpr[2]]) + return None + + def __handleToonCollision(self, collEntry): + toonId = base.localAvatar.getDoId() + self.notify.debug('Distributed suit: requesting a Battle with ' + 'toon: %d' % toonId) + self.d_requestBattle(self.getPos(), self.getHpr()) + self.setState('WaitForBattle') + return None + + def enterWalk(self): + self.notify.debug('enterWalk') + self.enableBattleDetect('walk', self.__handleToonCollision) + self.loop('walk', 0) + pathPoints = [Vec3(50, 15, 0), + Vec3(50, 25, 0), + Vec3(20, 25, 0), + Vec3(20, 15, 0), + Vec3(50, 15, 0)] + self.tutWalkTrack = self.makePathTrack(self, pathPoints, 4.5, 'tutFlunkyWalk') + self.tutWalkTrack.loop() + + def exitWalk(self): + self.notify.debug('exitWalk') + self.disableBattleDetect() + self.tutWalkTrack.pause() + self.tutWalkTrack = None + return + + def enterNeutral(self): + self.notify.debug('enterNeutral') + self.notify.debug('DistributedLawbotBossSuit: Neutral') + self.loop('neutral', 0) + + def exitNeutral(self): + self.notify.debug('exitNeutral') + + def doAttack(self, x1, y1, z1, x2, y2, z2): + self.notify.debug('x1=%.2f y1=%.2f z2=%.2f x2=%.2f y2=%.2f z2=%.2f' % (x1, + y1, + z1, + x2, + y2, + z2)) + self.curTargetPt = Point3(x2, y2, z2) + self.fsm.request('PreThrowAttack') + return + attackEvidence = self.getEvidence(True) + nodePath = render + node = nodePath.attachNewNode('attackEvidence-%s' % self.doId) + node.setPos(x1, y1, z1) + duration = 3.0 + throwName = self.uniqueName('lawyerAttack') + throwingSeq = self.makeAttackThrowingTrack(attackEvidence, duration, Point3(x2, y2, z2)) + fullSequence = Sequence(throwingSeq, name=throwName) + self.activeIntervals[throwName] = fullSequence + fullSequence.start() + + def doProsecute(self): + self.notify.debug('doProsecute') + bounds = self.boss.prosecutionColNodePath.getBounds() + panCenter = bounds.getCenter() + localPos = panCenter + prosecutionPanPos = render.getRelativePoint(self.boss.prosecutionColNodePath, localPos) + self.curTargetPt = prosecutionPanPos + self.fsm.request('PreThrowProsecute') + return + attackEvidence = self.getEvidence(False) + nodePath = render + node = nodePath.attachNewNode('prosecuteEvidence-%s' % self.doId) + node.setPos(self.getPos()) + duration = ToontownGlobals.LawbotBossLawyerToPanTime + throwName = self.uniqueName('lawyerProsecute') + throwingSeq = self.makeProsecuteThrowingTrack(attackEvidence, duration, prosecutionPanPos) + fullSequence = Sequence(throwingSeq, Func(self.boss.flashGreen), Func(self.clearInterval, throwName), name=throwName) + self.activeIntervals[throwName] = fullSequence + fullSequence.start() + + def makeDummySequence(self): + retval = Sequence(Wait(10)) + return retval + + def makeProsecuteThrowingTrack(self, evidence, inFlightDuration, hitPos): + suitTrack = Sequence() + suitTrack.append(ActorInterval(self, 'throw-paper')) + throwPaperDuration = suitTrack.getDuration() + inFlight = Parallel(evidence.posInterval(inFlightDuration, hitPos, fluid=1)) + origHpr = self.getHpr() + self.headsUp(hitPos) + newHpr = self.getHpr() + self.setHpr(origHpr) + rotateTrack = Sequence(self.hprInterval(self.timeToShow, newHpr, fluid=1)) + propTrack = Sequence(Func(evidence.hide), Func(evidence.setPos, 0, 0.5, -0.3), Func(evidence.reparentTo, self.getRightHand()), Wait(self.timeToShow), Func(evidence.show), Wait(self.timeToRelease - self.timeToShow), Func(evidence.wrtReparentTo, render), Func(self.makeDummySequence), inFlight, Func(evidence.detachNode)) + throwingTrack = Parallel(suitTrack, propTrack, rotateTrack) + return throwingTrack + + def makeAttackThrowingTrack(self, evidence, inFlightDuration, hitPos): + suitTrack = Sequence() + suitTrack.append(ActorInterval(self, 'throw-paper')) + throwPaperDuration = suitTrack.getDuration() + origHpr = self.getHpr() + self.headsUp(hitPos) + newHpr = self.getHpr() + self.setHpr(origHpr) + rotateTrack = Sequence(self.hprInterval(self.timeToShow, newHpr, fluid=1)) + propTrack = Sequence(Func(evidence.hide), Func(evidence.setPos, 0, 0.5, -0.3), Func(evidence.reparentTo, self.getRightHand()), Wait(self.timeToShow), Func(evidence.show), Wait(self.timeToRelease - self.timeToShow), Func(evidence.wrtReparentTo, render), Func(evidence.setZ, 1.3), evidence.posInterval(inFlightDuration, hitPos, fluid=1), Func(evidence.detachNode)) + throwingTrack = Parallel(suitTrack, propTrack, rotateTrack) + return throwingTrack + + def makePreThrowAttackTrack(self, evidence, inFlightDuration, hitPos): + suitTrack = Sequence() + suitTrack.append(ActorInterval(self, 'throw-paper', endTime=self.timeToRelease)) + throwPaperDuration = suitTrack.getDuration() + origHpr = self.getHpr() + self.headsUp(hitPos) + newHpr = self.getHpr() + self.setHpr(origHpr) + rotateTrack = Sequence(self.hprInterval(self.timeToShow, newHpr, fluid=1)) + propTrack = Sequence(Func(evidence.hide), Func(evidence.setPos, 0, 0.5, -0.3), Func(evidence.setScale, 1), Func(evidence.setHpr, 0, 0, 0), Func(evidence.reparentTo, self.getRightHand()), Wait(self.timeToShow), Func(evidence.show), Wait(self.timeToRelease - self.timeToShow)) + throwingTrack = Parallel(suitTrack, propTrack, rotateTrack) + return throwingTrack + + def makePostThrowAttackTrack(self, evidence, inFlightDuration, hitPos): + suitTrack = Sequence() + suitTrack.append(ActorInterval(self, 'throw-paper', startTime=self.timeToRelease)) + propTrack = Sequence(Func(evidence.wrtReparentTo, render), Func(evidence.setScale, 1), Func(evidence.show), Func(evidence.setZ, 1.3), evidence.posInterval(inFlightDuration, hitPos, fluid=1), Func(evidence.hide)) + return (suitTrack, propTrack) + + def makePreThrowProsecuteTrack(self, evidence, inFlightDuration, hitPos): + return self.makePreThrowAttackTrack(evidence, inFlightDuration, hitPos) + + def makePostThrowProsecuteTrack(self, evidence, inFlightDuration, hitPos): + suitTrack = Sequence() + suitTrack.append(ActorInterval(self, 'throw-paper', startTime=self.timeToRelease)) + propTrack = Sequence(Func(evidence.wrtReparentTo, render), Func(evidence.setScale, 1), Func(evidence.show), evidence.posInterval(inFlightDuration, hitPos, fluid=1), Func(evidence.hide)) + return (suitTrack, propTrack) + + def getEvidence(self, usedForAttack = False): + model = loader.loadModel('phase_5/models/props/lawbook') + if usedForAttack: + bounds = model.getBounds() + center = bounds.getCenter() + radius = bounds.getRadius() + sphere = CollisionSphere(center.getX(), center.getY(), center.getZ(), radius) + colNode = CollisionNode('BossZap') + colNode.setTag('attackCode', str(ToontownGlobals.BossCogLawyerAttack)) + colNode.addSolid(sphere) + model.attachNewNode(colNode) + model.setTransparency(1) + model.setAlphaScale(0.5) + return model + + def cleanupIntervals(self): + for interval in self.activeIntervals.values(): + interval.finish() + + self.activeIntervals = {} + + def clearInterval(self, name, finish = 1): + if name in self.activeIntervals: + ival = self.activeIntervals[name] + if finish: + ival.finish() + else: + ival.pause() + if name in self.activeIntervals: + del self.activeIntervals[name] + else: + self.notify.debug('interval: %s already cleared' % name) + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + + def doStun(self): + self.notify.debug('doStun') + self.fsm.request('Stunned') + + def enterPreThrowProsecute(self): + duration = ToontownGlobals.LawbotBossLawyerToPanTime + throwName = self.uniqueName('preThrowProsecute') + preThrowTrack = self.makePreThrowProsecuteTrack(self.prosecuteEvidence, duration, self.curTargetPt) + fullSequence = Sequence(preThrowTrack, Func(self.requestStateIfNotInFlux, 'PostThrowProsecute'), name=throwName) + self.activeIntervals[throwName] = fullSequence + fullSequence.start() + + def exitPreThrowProsecute(self): + throwName = self.uniqueName('preThrowProsecute') + if throwName in self.activeIntervals: + self.activeIntervals[throwName].pause() + del self.activeIntervals[throwName] + + def enterPostThrowProsecute(self): + duration = ToontownGlobals.LawbotBossLawyerToPanTime + throwName = self.uniqueName('postThrowProsecute') + postThrowTrack, self.flyingEvidenceTrack = self.makePostThrowProsecuteTrack(self.prosecuteEvidence, duration, self.curTargetPt) + fullSequence = Sequence(postThrowTrack, Func(self.requestStateIfNotInFlux, 'neutral'), name=throwName) + self.activeIntervals[throwName] = fullSequence + fullSequence.start() + flyName = self.uniqueName('flyingEvidence') + self.activeIntervals[flyName] = self.flyingEvidenceTrack + self.flyingEvidenceTrack.append(Func(self.finishedWithFlying, 'prosecute')) + self.flyingEvidenceTrack.start() + + def exitPostThrowProsecute(self): + throwName = self.uniqueName('postThrowProsecute') + if throwName in self.activeIntervals: + self.activeIntervals[throwName].finish() + del self.activeIntervals[throwName] + + def requestStateIfNotInFlux(self, state): + if not self.fsm._ClassicFSM__internalStateInFlux: + self.fsm.request(state) + + def enterPreThrowAttack(self): + if self.attackEvidence == self.attackEvidenceA: + self.attackEvidence = self.attackEvidenceB + else: + self.attackEvidence = self.attackEvidenceA + duration = 3.0 + throwName = self.uniqueName('preThrowAttack') + preThrowTrack = self.makePreThrowAttackTrack(self.attackEvidence, duration, self.curTargetPt) + fullSequence = Sequence(preThrowTrack, Func(self.requestStateIfNotInFlux, 'PostThrowAttack'), name=throwName) + self.activeIntervals[throwName] = fullSequence + fullSequence.start() + + def exitPreThrowAttack(self): + throwName = self.uniqueName('preThrowAttack') + if throwName in self.activeIntervals: + self.activeIntervals[throwName].pause() + del self.activeIntervals[throwName] + + def enterPostThrowAttack(self): + duration = 3.0 + throwName = self.uniqueName('postThrowAttack') + postThrowTrack, self.flyingEvidenceTrack = self.makePostThrowAttackTrack(self.attackEvidence, duration, self.curTargetPt) + fullSequence = Sequence(postThrowTrack, Func(self.requestStateIfNotInFlux, 'neutral'), name=throwName) + self.notify.debug('duration of postThrowAttack = %f' % fullSequence.getDuration()) + self.activeIntervals[throwName] = fullSequence + fullSequence.start() + flyName = self.uniqueName('flyingEvidence') + self.activeIntervals[flyName] = self.flyingEvidenceTrack + self.flyingEvidenceTrack.append(Func(self.finishedWithFlying, 'attack')) + self.flyingEvidenceTrack.start() + + def finishedWithFlying(self, str): + self.notify.debug('finished flyingEvidenceTrack %s' % str) + + def exitPostThrowAttack(self): + throwName = self.uniqueName('postThrowAttack') + if throwName in self.activeIntervals: + self.activeIntervals[throwName].finish() + del self.activeIntervals[throwName] + + def enterStunned(self): + stunSequence = MovieUtil.createSuitStunInterval(self, 0, ToontownGlobals.LawbotBossLawyerStunTime) + seqName = stunSequence.getName() + stunSequence.append(Func(self.fsm.request, 'neutral')) + self.activeIntervals[seqName] = stunSequence + stunSequence.start() + + def exitStunned(self): + self.prosecuteEvidence.hide() + self.attackEvidence.hide() diff --git a/toontown/suit/DistributedLawbotBossSuitAI.py b/toontown/suit/DistributedLawbotBossSuitAI.py new file mode 100755 index 00000000..017d441d --- /dev/null +++ b/toontown/suit/DistributedLawbotBossSuitAI.py @@ -0,0 +1,198 @@ +from otp.ai.AIBaseGlobal import * +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import SuitBattleGlobals +from toontown.toonbase import ToontownGlobals +import DistributedSuitBaseAI +import random +from direct.fsm import ClassicFSM, State +from direct.fsm import State + +class DistributedLawbotBossSuitAI(DistributedSuitBaseAI.DistributedSuitBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotBossSuitAI') + + def __init__(self, air, suitPlanner): + DistributedSuitBaseAI.DistributedSuitBaseAI.__init__(self, air, suitPlanner) + self.stunned = False + self.timeToRelease = 3.15 + self.timeProsecuteStarted = 0 + self.fsm = ClassicFSM.ClassicFSM('DistributedLawbotBossSuitAI', [State.State('Off', self.enterOff, self.exitOff, ['neutral']), + State.State('neutral', self.enterNeutral, self.exitNeutral, ['PreThrowProsecute', 'PreThrowAttack', 'Stunned']), + State.State('PreThrowProsecute', self.enterPreThrowProsecute, self.exitPreThrowProsecute, ['PostThrowProsecute', 'neutral', 'Stunned']), + State.State('PostThrowProsecute', self.enterPostThrowProsecute, self.exitPostThrowProsecute, ['neutral', 'Stunned']), + State.State('PreThrowAttack', self.enterPreThrowAttack, self.exitPreThrowAttack, ['PostThrowAttack', 'neutral', 'Stunned']), + State.State('PostThrowAttack', self.enterPostThrowAttack, self.exitPostThrowAttack, ['neutral', 'Stunned']), + State.State('Stunned', self.enterStunned, self.exitStunned, ['neutral'])], 'Off', 'Off') + self.fsm.enterInitialState() + + def delete(self): + self.notify.debug('delete %s' % self.doId) + self.ignoreAll() + DistributedSuitBaseAI.DistributedSuitBaseAI.delete(self) + self.notify.debug('setting self.boss to None') + self.boss = None + taskName = self.uniqueName('ProsecutionHealsBoss') + if taskMgr.hasTaskNamed(taskName): + self.notify.debug('still has task %s' % taskName) + taskMgr.remove(taskName) + taskName = self.uniqueName('unstun') + if taskMgr.hasTaskNamed(taskName): + self.notify.debug('still has task %s' % taskName) + taskMgr.remove(taskName) + self.fsm = None + return + + def requestBattle(self, x, y, z, h, p, r): + toonId = self.air.getAvatarIdFromSender() + if self.notify.getDebug(): + self.notify.debug(str(self.getDoId()) + str(self.zoneId) + ': request battle with toon: %d' % toonId) + self.confrontPos = Point3(x, y, z) + self.confrontHpr = Vec3(h, p, r) + if self.sp.requestBattle(self.zoneId, self, toonId): + self.acceptOnce(self.getDeathEvent(), self._logDeath, [toonId]) + if self.notify.getDebug(): + self.notify.debug('Suit %d requesting battle in zone %d' % (self.getDoId(), self.zoneId)) + else: + if self.notify.getDebug(): + self.notify.debug('requestBattle from suit %d - denied by battle manager' % self.getDoId()) + self.b_setBrushOff(SuitDialog.getBrushOffIndex(self.getStyleName())) + self.d_denyBattle(toonId) + + def getPosHpr(self): + return (self.getX(), + self.getY(), + self.getZ(), + self.getH(), + self.getP(), + self.getR()) + + def getConfrontPosHpr(self): + return (self.confrontPos, self.confrontHpr) + + def _logDeath(self, toonId): + pass + + def doNextAttack(self, lawbotBoss): + if self.stunned: + return + chanceToDoAttack = ToontownGlobals.LawbotBossLawyerChanceToAttack + action = random.randrange(1, 101) + if action > chanceToDoAttack: + self.doProsecute() + else: + if not lawbotBoss.involvedToons: + return + toonToAttackId = random.choice(lawbotBoss.involvedToons) + toon = self.air.doId2do.get(toonToAttackId) + if not toon: + self.doProsecute() + return + toonPos = toon.getPos() + z2 = toonPos[2] + 1.3 + toonPos = Point3(toonPos.getX(), toonPos.getY(), 0) + lawyerPos = self.getPos() + lawyerPos = Point3(self.getPos().getX(), self.getPos().getY(), 0) + dirVector = toonPos - lawyerPos + dirVector.normalize() + dirVector *= 200 + destPos = Point3(lawyerPos[0] + dirVector[0], lawyerPos[1] + dirVector[1], lawyerPos[2] + dirVector[2] + 1.3) + self.d_doAttack(lawyerPos[0], lawyerPos[1], lawyerPos[2], destPos[0], destPos[1], destPos[2]) + + def doProsecute(self): + self.notify.debug('doProsecute') + self.timeProsecuteStarted = globalClockDelta.getRealNetworkTime() + self.d_doProsecute() + taskName = self.uniqueName('ProsecutionHealsBoss') + duration = 5.65 + taskMgr.doMethodLater(duration, self.__prosecutionHeal, taskName) + + def __prosecutionHeal(self, extraArg): + self.notify.debug('__prosecutionHeal extraArg %s' % extraArg) + if self.boss: + self.boss.healBoss(ToontownGlobals.LawbotBossLawyerHeal) + + def d_doProsecute(self): + self.notify.debug('d_doProsecute') + self.sendUpdate('doProsecute', []) + + def d_doAttack(self, x1, y1, z1, x2, y2, z2): + self.notify.debug('doAttack: x1=%.2f y1=%.2f z2=%.2f x2=%.2f y2=%.2f z2=%.2f' % (x1, + y1, + z1, + x2, + y2, + z2)) + self.sendUpdate('doAttack', [x1, + y1, + z1, + x2, + y2, + z2]) + + def setBoss(self, lawbotBoss): + self.boss = lawbotBoss + + def hitByToon(self): + self.notify.debug('I got hit by a toon') + if not self.stunned: + curTime = globalClockDelta.getRealNetworkTime() + deltaTime = curTime - self.timeProsecuteStarted + deltaTime /= 100.0 + self.notify.debug('deltaTime = %f, curTime=%f, prosecuteStarted=%f' % (deltaTime, curTime, self.timeProsecuteStarted)) + if deltaTime < self.timeToRelease: + taskName = self.uniqueName('ProsecutionHealsBoss') + taskMgr.remove(taskName) + self.sendUpdate('doStun', []) + self.setStun(True) + taskName = self.uniqueName('unstun') + taskMgr.doMethodLater(ToontownGlobals.LawbotBossLawyerStunTime, self.unStun, taskName) + if self.boss: + self.boss.checkForBonusState() + + def setStun(self, val): + self.stunned = val + + def unStun(self, taskName): + self.setStun(False) + + def enterPreThrowProsecute(self): + pass + + def exitPreThrowProsecute(self): + pass + + def enterPostThrowProsecute(self): + pass + + def exitPostThrowProsecute(self): + pass + + def enterPreThrowAttack(self): + pass + + def exitPreThrowAttack(self): + pass + + def enterPostThrowAttack(self): + pass + + def exitPostThrowAttack(self): + pass + + def enterStunned(self): + pass + + def exitStunned(self): + pass + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterNeutral(self): + pass + + def exitNeutral(self): + pass diff --git a/toontown/suit/DistributedMintSuit.py b/toontown/suit/DistributedMintSuit.py new file mode 100755 index 00000000..6d736a0b --- /dev/null +++ b/toontown/suit/DistributedMintSuit.py @@ -0,0 +1,5 @@ +from toontown.suit import DistributedFactorySuit +from direct.directnotify import DirectNotifyGlobal + +class DistributedMintSuit(DistributedFactorySuit.DistributedFactorySuit): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintSuit') diff --git a/toontown/suit/DistributedMintSuitAI.py b/toontown/suit/DistributedMintSuitAI.py new file mode 100755 index 00000000..d5e4024e --- /dev/null +++ b/toontown/suit/DistributedMintSuitAI.py @@ -0,0 +1,14 @@ +from toontown.suit import DistributedFactorySuitAI +from direct.directnotify import DirectNotifyGlobal + +class DistributedMintSuitAI(DistributedFactorySuitAI.DistributedFactorySuitAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintSuitAI') + + def isForeman(self): + return 0 + + def isSupervisor(self): + return self.boss + + def isVirtual(self): + return 0 diff --git a/toontown/suit/DistributedSellbotBoss.py b/toontown/suit/DistributedSellbotBoss.py new file mode 100755 index 00000000..d0386886 --- /dev/null +++ b/toontown/suit/DistributedSellbotBoss.py @@ -0,0 +1,1288 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.directutil import Mopath +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import FSM +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from direct.showbase.PythonUtil import Functor +from direct.showutil import Rope +from direct.task import Task +import math +from panda3d.core import * +import random + +import DistributedBossCog +import SuitDNA +from toontown.battle import BattleBase +from toontown.battle import MovieToonVictory +from toontown.battle import RewardPanel +from toontown.battle import SuitBattleGlobals +from toontown.battle.BattleProps import * +from toontown.coghq import CogDisguiseGlobals +from toontown.distributed import DelayDelete +from toontown.suit import SellbotBossGlobals +from toontown.toon import NPCToons +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import ToontownGlobals +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals + + +OneBossCog = None + +class DistributedSellbotBoss(DistributedBossCog.DistributedBossCog, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSellbotBoss') + cageHeights = [100, + 81, + 63, + 44, + 25, + 18] + + def __init__(self, cr): + DistributedBossCog.DistributedBossCog.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedSellbotBoss') + self.cagedToonNpcId = None + self.doobers = [] + self.dooberRequest = None + self.bossDamage = 0 + self.attackCode = None + self.attackAvId = 0 + self.recoverRate = 0 + self.recoverStartTime = 0 + self.bossDamageMovie = None + self.cagedToon = None + self.cageShadow = None + self.cageIndex = 0 + self.everThrownPie = 0 + self.battleThreeMusicTime = 0 + self.insidesANodePath = None + self.insidesBNodePath = None + self.rampA = None + self.rampB = None + self.rampC = None + self.strafeInterval = None + self.onscreenMessage = None + self.toonMopathInterval = [] + self.nerfed = base.cr.newsManager.isHolidayRunning(ToontownGlobals.SELLBOT_NERF_HOLIDAY) + self.localToonPromoted = True + self.resetMaxDamage() + + def announceGenerate(self): + global OneBossCog + DistributedBossCog.DistributedBossCog.announceGenerate(self) + self.setName(TTLocalizer.SellbotBossName) + nameInfo = TTLocalizer.BossCogNameWithDept % {'name': self.name, + 'dept': SuitDNA.getDeptFullname(self.style.dept)} + self.setDisplayName(nameInfo) + self.cageDoorSfx = loader.loadSfx('phase_5/audio/sfx/CHQ_SOS_cage_door.ogg') + self.cageLandSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_SOS_cage_land.ogg') + self.cageLowerSfx = loader.loadSfx('phase_5/audio/sfx/CHQ_SOS_cage_lower.ogg') + self.piesRestockSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_SOS_pies_restock.ogg') + self.rampSlideSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_ramp_slide.ogg') + self.strafeSfx = [] + for i in xrange(10): + self.strafeSfx.append(loader.loadSfx('phase_3.5/audio/sfx/SA_shred.ogg')) + + render.setTag('pieCode', str(ToontownGlobals.PieCodeNotBossCog)) + insidesA = CollisionPolygon(Point3(4.0, -2.0, 5.0), Point3(-4.0, -2.0, 5.0), Point3(-4.0, -2.0, 0.5), Point3(4.0, -2.0, 0.5)) + insidesANode = CollisionNode('BossZap') + insidesANode.addSolid(insidesA) + insidesANode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.WallBitmask) + self.insidesANodePath = self.axle.attachNewNode(insidesANode) + self.insidesANodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossInsides)) + self.insidesANodePath.stash() + insidesB = CollisionPolygon(Point3(-4.0, 2.0, 5.0), Point3(4.0, 2.0, 5.0), Point3(4.0, 2.0, 0.5), Point3(-4.0, 2.0, 0.5)) + insidesBNode = CollisionNode('BossZap') + insidesBNode.addSolid(insidesB) + insidesBNode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.WallBitmask) + self.insidesBNodePath = self.axle.attachNewNode(insidesBNode) + self.insidesBNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossInsides)) + self.insidesBNodePath.stash() + target = CollisionTube(0, -1, 4, 0, -1, 9, 3.5) + targetNode = CollisionNode('BossZap') + targetNode.addSolid(target) + targetNode.setCollideMask(ToontownGlobals.PieBitmask) + self.targetNodePath = self.pelvis.attachNewNode(targetNode) + self.targetNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossCog)) + shield = CollisionTube(0, 1, 4, 0, 1, 7, 3.5) + shieldNode = CollisionNode('BossZap') + shieldNode.addSolid(shield) + shieldNode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask) + shieldNodePath = self.pelvis.attachNewNode(shieldNode) + disk = loader.loadModel('phase_9/models/char/bossCog-gearCollide') + disk.find('**/+CollisionNode').setName('BossZap') + disk.reparentTo(self.pelvis) + disk.setZ(0.8) + self.loadEnvironment() + self.__makeCagedToon() + self.__loadMopaths() + if OneBossCog is not None: + self.notify.warning('Multiple BossCogs visible.') + OneBossCog = self + + def disable(self): + global OneBossCog + DistributedBossCog.DistributedBossCog.disable(self) + self.request('Off') + self.unloadEnvironment() + self.__unloadMopaths() + self.__cleanupCagedToon() + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + self.__cleanupStrafe() + render.clearTag('pieCode') + self.targetNodePath.detachNode() + self.cr.relatedObjectMgr.abortRequest(self.dooberRequest) + self.dooberRequest = None + self.betweenBattleMusic.stop() + self.promotionMusic.stop() + self.stingMusic.stop() + self.battleTwoMusic.stop() + self.battleThreeMusic.stop() + self.epilogueMusic.stop() + while len(self.toonMopathInterval): + toonMopath = self.toonMopathInterval[0] + toonMopath.finish() + toonMopath.destroy() + self.toonMopathInterval.remove(toonMopath) + + if OneBossCog == self: + OneBossCog = None + + def resetMaxDamage(self): + if self.nerfed: + self.bossMaxDamage = ToontownGlobals.SellbotBossMaxDamageNerfed + else: + self.bossMaxDamage = ToontownGlobals.SellbotBossMaxDamage + + def d_hitBoss(self, bossDamage): + self.sendUpdate('hitBoss', [bossDamage]) + + def d_hitBossInsides(self): + self.sendUpdate('hitBossInsides', []) + + def d_hitToon(self, toonId): + self.sendUpdate('hitToon', [toonId]) + + def setCagedToonNpcId(self, npcId): + self.cagedToonNpcId = npcId + + def gotToon(self, toon): + stateName = self.state + if stateName == 'Elevator': + self.placeToonInElevator(toon) + + def setDooberIds(self, dooberIds): + self.doobers = [] + self.cr.relatedObjectMgr.abortRequest(self.dooberRequest) + self.dooberRequest = self.cr.relatedObjectMgr.requestObjects(dooberIds, allCallback=self.__gotDoobers) + + def __gotDoobers(self, doobers): + self.dooberRequest = None + self.doobers = doobers + + def setBossDamage(self, bossDamage, recoverRate, timestamp): + recoverStartTime = globalClockDelta.networkToLocalTime(timestamp) + self.bossDamage = bossDamage + self.recoverRate = recoverRate + self.recoverStartTime = recoverStartTime + taskName = 'RecoverBossDamage' + taskMgr.remove(taskName) + if self.bossDamageMovie: + if self.bossDamage >= self.bossMaxDamage: + self.bossDamageMovie.resumeUntil(self.bossDamageMovie.getDuration()) + else: + self.bossDamageMovie.resumeUntil(self.bossDamage * self.bossDamageToMovie) + if self.recoverRate: + taskMgr.add(self.__recoverBossDamage, taskName) + + def getBossDamage(self): + now = globalClock.getFrameTime() + elapsed = now - self.recoverStartTime + return max(self.bossDamage - self.recoverRate * elapsed / 60.0, 0) + + def __recoverBossDamage(self, task): + self.bossDamageMovie.setT(self.getBossDamage() * self.bossDamageToMovie) + return Task.cont + + def __makeCagedToon(self): + if self.cagedToon: + return + self.cagedToon = NPCToons.createLocalNPC(self.cagedToonNpcId) + self.cagedToon.addActive() + self.cagedToon.reparentTo(self.cage) + self.cagedToon.setPosHpr(0, -2, 0, 180, 0, 0) + self.cagedToon.loop('neutral') + self.cagedToon.setActiveShadow(0) + touch = CollisionPolygon( + Point3(-3.0382, 3.0382, -1), Point3(3.0382, 3.0382, -1), + Point3(3.0382, -3.0382, -1), Point3(-3.0382, -3.0382, -1)) + touch.setTangible(0) + touchNode = CollisionNode('Cage') + touchNode.setCollideMask(ToontownGlobals.WallBitmask) + touchNode.addSolid(touch) + self.cage.attachNewNode(touchNode) + + def __cleanupCagedToon(self): + if self.cagedToon: + self.cagedToon.removeActive() + self.cagedToon.delete() + self.cagedToon = None + + def __walkToonToPromotion(self, toonId, delay, mopath, track, delayDeletes): + toon = base.cr.doId2do.get(toonId) + if toon: + destPos = toon.getPos() + self.placeToonInElevator(toon) + toon.wrtReparentTo(render) + walkMopath = MopathInterval(mopath, toon) + ival = Sequence(Wait(delay), Func(toon.suit.setPlayRate, 1, 'walk'), Func(toon.suit.loop, 'walk'), toon.posInterval(1, Point3(0, 90, 20)), ParallelEndTogether(walkMopath, toon.posInterval(2, destPos, blendType='noBlend')), Func(toon.suit.loop, 'neutral')) + self.toonMopathInterval.append(walkMopath) + track.append(ival) + delayDeletes.append(DelayDelete.DelayDelete(toon, 'SellbotBoss.__walkToonToPromotion')) + + def __walkDoober(self, suit, delay, turnPos, track, delayDeletes): + turnPos = Point3(*turnPos) + turnPosDown = Point3(*ToontownGlobals.SellbotBossDooberTurnPosDown) + flyPos = Point3(*ToontownGlobals.SellbotBossDooberFlyPos) + seq = Sequence(Func(suit.headsUp, turnPos), Wait(delay), Func(suit.loop, 'walk', 0), self.__walkSuitToPoint(suit, suit.getPos(), turnPos), self.__walkSuitToPoint(suit, turnPos, turnPosDown), self.__walkSuitToPoint(suit, turnPosDown, flyPos), suit.beginSupaFlyMove(flyPos, 0, 'flyAway'), Func(suit.fsm.request, 'Off')) + track.append(seq) + delayDeletes.append(DelayDelete.DelayDelete(suit, 'SellbotBoss.__walkDoober')) + + def __walkSuitToPoint(self, node, fromPos, toPos): + vector = Vec3(toPos - fromPos) + distance = vector.length() + time = distance / (ToontownGlobals.SuitWalkSpeed * 1.8) + return Sequence(Func(node.setPos, fromPos), Func(node.headsUp, toPos), node.posInterval(time, toPos)) + + def makeIntroductionMovie(self, delayDeletes): + track = Parallel() + camera.reparentTo(render) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + dooberTrack = Parallel() + if self.doobers: + self.__doobersToPromotionPosition(self.doobers[:4], self.battleANode) + self.__doobersToPromotionPosition(self.doobers[4:], self.battleBNode) + turnPosA = ToontownGlobals.SellbotBossDooberTurnPosA + turnPosB = ToontownGlobals.SellbotBossDooberTurnPosB + self.__walkDoober(self.doobers[0], 0, turnPosA, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[1], 4, turnPosA, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[2], 8, turnPosA, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[3], 12, turnPosA, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[7], 2, turnPosB, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[6], 6, turnPosB, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[5], 10, turnPosB, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[4], 14, turnPosB, dooberTrack, delayDeletes) + toonTrack = Parallel() + self.__toonsToPromotionPosition(self.toonsA, self.battleANode) + self.__toonsToPromotionPosition(self.toonsB, self.battleBNode) + delay = 0 + for toonId in self.toonsA: + self.__walkToonToPromotion(toonId, delay, self.toonsEnterA, toonTrack, delayDeletes) + delay += 1 + + for toonId in self.toonsB: + self.__walkToonToPromotion(toonId, delay, self.toonsEnterB, toonTrack, delayDeletes) + delay += 1 + + toonTrack.append(Sequence(Wait(delay), self.closeDoors)) + self.rampA.request('extended') + self.rampB.request('extended') + self.rampC.request('retracted') + self.clearChat() + self.cagedToon.clearChat() + promoteDoobers = TTLocalizer.BossCogPromoteDoobers % SuitDNA.getDeptFullnameP(self.style.dept) + doobersAway = TTLocalizer.BossCogDoobersAway[self.style.dept] + welcomeToons = TTLocalizer.BossCogWelcomeToons + promoteToons = TTLocalizer.BossCogPromoteToons % SuitDNA.getDeptFullnameP(self.style.dept) + discoverToons = TTLocalizer.BossCogDiscoverToons + attackToons = TTLocalizer.BossCogAttackToons + interruptBoss = TTLocalizer.CagedToonInterruptBoss + rescueQuery = TTLocalizer.CagedToonRescueQuery + bossAnimTrack = Sequence( + ActorInterval(self, 'Ff_speech', startTime=2, duration=10, loop=1), + ActorInterval(self, 'ltTurn2Wave', duration=2), + ActorInterval(self, 'wave', duration=4, loop=1), + ActorInterval(self, 'ltTurn2Wave', startTime=2, endTime=0), + ActorInterval(self, 'Ff_speech', duration=7, loop=1)) + track.append(bossAnimTrack) + dialogTrack = Track( + (0, Parallel( + camera.posHprInterval(8, Point3(-22, -100, 35), Point3(-10, -13, 0), blendType='easeInOut'), + IndirectInterval(toonTrack, 0, 18))), + (5.6, Func(self.setChatAbsolute, promoteDoobers, CFSpeech)), + (9, IndirectInterval(dooberTrack, 0, 9)), + (10, Sequence( + Func(self.clearChat), + camera.posHprInterval(5, (-0.3, -105.1, 5.4), (0, 0, 0), blendType='easeInOut'))), + (12, Func(self.setChatAbsolute, doobersAway, CFSpeech)), + (16, Parallel( + Func(self.clearChat), + camera.posHprInterval(4, (-25, -99, 10), (-14, 10, 0), blendType='easeInOut'), + IndirectInterval(dooberTrack, 14), + IndirectInterval(toonTrack, 30))), + (18, Func(self.setChatAbsolute, welcomeToons, CFSpeech)), + (22, Func(self.setChatAbsolute, promoteToons, CFSpeech)), + (22.2, Sequence( + Func(self.cagedToon.nametag3d.setScale, 2), + Func(self.cagedToon.setChatAbsolute, interruptBoss, CFSpeech), + ActorInterval(self.cagedToon, 'wave'), + Func(self.cagedToon.loop, 'neutral'))), + (25, Sequence( + Func(self.clearChat), + Func(self.cagedToon.clearChat), + Func(camera.setPosHpr, -12, -15, 27, -151, -15, 0), + ActorInterval(self, 'Ff_lookRt'))), + (27, Sequence( + Func(self.cagedToon.setChatAbsolute, rescueQuery, CFSpeech), + Func(camera.setPosHpr, -12, 48, 94, -26, 20, 0), + ActorInterval(self.cagedToon, 'wave'), + Func(self.cagedToon.loop, 'neutral'))), + (31, Sequence( + camera.posHprInterval(1, (-20, -35, 10), (-88, 25, 0), blendType='easeOut'), + Func(self.setChatAbsolute, discoverToons, CFSpeech), + Func(self.cagedToon.nametag3d.setScale, 1), + Func(self.cagedToon.clearChat), + ActorInterval(self, 'turn2Fb'))), + (35, Sequence( + Func(self.clearChat), + self.loseCogSuits(self.toonsA, self.battleANode, (0, 18, 5, -180, 0, 0)), + self.loseCogSuits(self.toonsB, self.battleBNode, (0, 18, 5, -180, 0, 0)))), + (38, Sequence( + self.toonNormalEyes(self.involvedToons), + camera.posHprInterval(1, (-23.4, -145.6, 44.0), (-10.0, -12.5, 0), blendType='easeInOut'), + Func(self.loop, 'Fb_neutral'), + Func(self.rampA.request, 'retract'), + Func(self.rampB.request, 'retract'), + Parallel(self.backupToonsToBattlePosition(self.toonsA, self.battleANode), + self.backupToonsToBattlePosition(self.toonsB, self.battleBNode), + Sequence( + Wait(3), + camera.posHprInterval(1, (-25, -35, 20.5), (-90, 0, 0), blendType='easeOut'), + Func(self.setChatAbsolute, attackToons, CFSpeech), + Wait(3)))))) + track.append(dialogTrack) + return Sequence(Func(self.stickToonsToFloor), track, Func(self.unstickToons), name=self.uniqueName('Introduction')) + + def __makeRollToBattleTwoMovie(self): + startPos = Point3(ToontownGlobals.SellbotBossBattleOnePosHpr[0], ToontownGlobals.SellbotBossBattleOnePosHpr[1], ToontownGlobals.SellbotBossBattleOnePosHpr[2]) + if self.arenaSide: + topRampPos = Point3(*ToontownGlobals.SellbotBossTopRampPosB) + topRampTurnPos = Point3(*ToontownGlobals.SellbotBossTopRampTurnPosB) + p3Pos = Point3(*ToontownGlobals.SellbotBossP3PosB) + else: + topRampPos = Point3(*ToontownGlobals.SellbotBossTopRampPosA) + topRampTurnPos = Point3(*ToontownGlobals.SellbotBossTopRampTurnPosA) + p3Pos = Point3(*ToontownGlobals.SellbotBossP3PosA) + battlePos = Point3(ToontownGlobals.SellbotBossBattleTwoPosHpr[0], ToontownGlobals.SellbotBossBattleTwoPosHpr[1], ToontownGlobals.SellbotBossBattleTwoPosHpr[2]) + battleHpr = VBase3(ToontownGlobals.SellbotBossBattleTwoPosHpr[3], ToontownGlobals.SellbotBossBattleTwoPosHpr[4], ToontownGlobals.SellbotBossBattleTwoPosHpr[5]) + bossTrack = Sequence() + bossTrack.append(Func(self.setChatAbsolute, TTLocalizer.VPRampMessage, CFSpeech | CFTimeout)) + bossTrack.append(Func(self.getGeomNode().setH, 180)) + bossTrack.append(Func(self.loop, 'Fb_neutral')) + track, hpr = self.rollBossToPoint(startPos, None, topRampPos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(topRampPos, hpr, topRampTurnPos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(topRampTurnPos, hpr, p3Pos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(p3Pos, hpr, battlePos, None, 0) + bossTrack.append(track) + return Sequence(bossTrack, Func(self.getGeomNode().setH, 0), name=self.uniqueName('BattleTwo')) + + def cagedToonMovieFunction(self, instruct, cageIndex): + self.notify.debug('cagedToonMovieFunction()') + if not (hasattr(self, 'cagedToon') and hasattr(self.cagedToon, 'nametag') and hasattr(self.cagedToon, 'nametag3d')): + return + if instruct == 1: + self.cagedToon.nametag3d.setScale(2) + elif instruct == 2: + self.cagedToon.setChatAbsolute(TTLocalizer.CagedToonDrop[cageIndex], CFSpeech) + elif instruct == 3: + self.cagedToon.nametag3d.setScale(1) + elif instruct == 4: + self.cagedToon.clearChat() + + def makeEndOfBattleMovie(self, hasLocalToon): + name = self.uniqueName('CageDrop') + seq = Sequence(name=name) + seq.append(Func(self.cage.setPos, self.cagePos[self.cageIndex])) + if hasLocalToon: + seq += [Func(camera.reparentTo, render), + Func(camera.setPosHpr, self.cage, 0, -50, 0, 0, 0, 0), + Func(localAvatar.setCameraFov, ToontownGlobals.CogHQCameraFov), + Func(self.hide)] + seq += [Wait(0.5), + Parallel(self.cage.posInterval(1, self.cagePos[self.cageIndex + 1], blendType='easeInOut'), SoundInterval(self.cageLowerSfx, duration=1)), + Func(self.cagedToonMovieFunction, 1, self.cageIndex), + Func(self.cagedToonMovieFunction, 2, self.cageIndex), + Wait(3), + Func(self.cagedToonMovieFunction, 3, self.cageIndex), + Func(self.cagedToonMovieFunction, 4, self.cageIndex)] + if hasLocalToon: + seq += [Func(self.show), + Func(camera.reparentTo, localAvatar), + Func(camera.setPos, localAvatar.cameraPositions[0][0]), + Func(camera.setHpr, 0, 0, 0)] + self.cageIndex += 1 + return seq + + def __makeBossDamageMovie(self): + startPos = Point3(ToontownGlobals.SellbotBossBattleTwoPosHpr[0], ToontownGlobals.SellbotBossBattleTwoPosHpr[1], ToontownGlobals.SellbotBossBattleTwoPosHpr[2]) + startHpr = Point3(*ToontownGlobals.SellbotBossBattleThreeHpr) + bottomPos = Point3(*ToontownGlobals.SellbotBossBottomPos) + deathPos = Point3(*ToontownGlobals.SellbotBossDeathPos) + self.setPosHpr(startPos, startHpr) + bossTrack = Sequence() + bossTrack.append(Func(self.loop, 'Fb_neutral')) + track, hpr = self.rollBossToPoint(startPos, startHpr, bottomPos, None, 1) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(bottomPos, startHpr, deathPos, None, 1) + bossTrack.append(track) + duration = bossTrack.getDuration() + return bossTrack + + def __talkAboutPromotion(self, speech): + if not self.localToonPromoted: + pass + elif self.prevCogSuitLevel < ToontownGlobals.MaxCogSuitLevel: + speech += TTLocalizer.CagedToonPromotion + newCogSuitLevel = localAvatar.getCogLevels()[CogDisguiseGlobals.dept2deptIndex(self.style.dept)] + if newCogSuitLevel == ToontownGlobals.MaxCogSuitLevel: + speech += TTLocalizer.CagedToonLastPromotion % (ToontownGlobals.MaxCogSuitLevel + 1) + if newCogSuitLevel in ToontownGlobals.CogSuitHPLevels: + speech += TTLocalizer.CagedToonHPBoost + else: + speech += TTLocalizer.CagedToonMaxed % (ToontownGlobals.MaxCogSuitLevel + 1) + + if self.keyReward: + speech += TTLocalizer.BossRTKeyReward + + return speech + + return speech + + def __makeCageOpenMovie(self): + speech = TTLocalizer.CagedToonThankYou + speech = self.__talkAboutPromotion(speech) + name = self.uniqueName('CageOpen') + seq = Sequence( + Func(self.cage.setPos, self.cagePos[4]), + Func(self.cageDoor.setHpr, VBase3(0, 0, 0)), + Func(self.cagedToon.setPos, Point3(0, -2, 0)), + Parallel( + self.cage.posInterval(0.5, self.cagePos[5], blendType='easeOut'), + SoundInterval(self.cageLowerSfx, duration=0.5)), + Parallel( + self.cageDoor.hprInterval(0.5, VBase3(0, 90, 0), blendType='easeOut'), + Sequence(SoundInterval(self.cageDoorSfx), duration=0)), + Wait(0.2), + Func(self.cagedToon.loop, 'walk'), + self.cagedToon.posInterval(0.8, Point3(0, -6, 0)), + Func(self.cagedToon.setChatAbsolute, TTLocalizer.CagedToonYippee, CFSpeech), + ActorInterval(self.cagedToon, 'jump'), + Func(self.cagedToon.loop, 'neutral'), + Func(self.cagedToon.headsUp, localAvatar), + Func(self.cagedToon.setLocalPageChat, speech, 0), + Func(camera.reparentTo, localAvatar), + Func(camera.setPos, 0, -9, 9), + Func(camera.lookAt, self.cagedToon, Point3(0, 0, 2)), name=name) + return seq + + def __showOnscreenMessage(self, text): + if self.onscreenMessage: + self.onscreenMessage.destroy() + self.onscreenMessage = None + self.onscreenMessage = DirectLabel(text=text, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.35), scale=0.1) + + def __clearOnscreenMessage(self): + if self.onscreenMessage: + self.onscreenMessage.destroy() + self.onscreenMessage = None + + def __showWaitingMessage(self, task): + self.__showOnscreenMessage(TTLocalizer.WaitingForOtherToons) + + def __placeCageShadow(self): + if self.cageShadow == None: + self.cageShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.cageShadow.setPos(0, 77.9, 18) + self.cageShadow.setColorScale(1, 1, 1, 0.6) + self.cageShadow.reparentTo(render) + + def __removeCageShadow(self): + if self.cageShadow != None: + self.cageShadow.detachNode() + + def setCageIndex(self, cageIndex): + self.cageIndex = cageIndex + self.cage.setPos(self.cagePos[self.cageIndex]) + if self.cageIndex >= 4: + self.__placeCageShadow() + else: + self.__removeCageShadow() + + def loadEnvironment(self): + DistributedBossCog.DistributedBossCog.loadEnvironment(self) + self.geom = loader.loadModel('phase_9/models/cogHQ/BossRoomHQ') + self.rampA = self.__findRamp('rampA', '**/west_ramp2') + self.rampB = self.__findRamp('rampB', '**/west_ramp') + self.rampC = self.__findRamp('rampC', '**/west_ramp1') + self.cage = self.geom.find('**/cage') + elevatorEntrance = self.geom.find('**/elevatorEntrance') + elevatorEntrance.getChildren().detach() + elevatorEntrance.setScale(1) + elevatorModel = loader.loadModel('phase_9/models/cogHQ/cogHQ_elevator') + elevatorModel.reparentTo(elevatorEntrance) + self.setupElevator(elevatorModel) + pos = self.cage.getPos() + self.cagePos = [] + for height in self.cageHeights: + self.cagePos.append(Point3(pos[0], pos[1], height)) + + self.cageDoor = self.geom.find('**/cage_door') + self.cage.setScale(1) + self.rope = Rope.Rope(name='supportChain') + self.rope.reparentTo(self.cage) + self.rope.setup(2, ((self.cage, (0.15, 0.13, 16)), (self.geom, (0.23, 78, 120)))) + self.rope.ropeNode.setRenderMode(RopeNode.RMBillboard) + self.rope.ropeNode.setUvMode(RopeNode.UVDistance) + self.rope.ropeNode.setUvDirection(0) + self.rope.ropeNode.setUvScale(0.8) + self.rope.setTexture(self.cage.findTexture('hq_chain')) + self.rope.setTransparency(1) + self.promotionMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.ogg') + self.betweenBattleMusic = base.loadMusic('phase_9/audio/bgm/encntr_toon_winning.ogg') + self.battleTwoMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.ogg') + self.battleThreeMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.ogg') + self.geom.reparentTo(render) + + def unloadEnvironment(self): + DistributedBossCog.DistributedBossCog.unloadEnvironment(self) + self.geom.removeNode() + del self.geom + del self.cage + self.rampA.requestFinalState() + self.rampB.requestFinalState() + self.rampC.requestFinalState() + del self.rampA + del self.rampB + del self.rampC + + def __loadMopaths(self): + self.toonsEnterA = Mopath.Mopath() + self.toonsEnterA.loadFile('phase_9/paths/bossBattle-toonsEnterA') + self.toonsEnterA.fFaceForward = 1 + self.toonsEnterA.timeScale = 35 + self.toonsEnterB = Mopath.Mopath() + self.toonsEnterB.loadFile('phase_9/paths/bossBattle-toonsEnterB') + self.toonsEnterB.fFaceForward = 1 + self.toonsEnterB.timeScale = 35 + + def __unloadMopaths(self): + self.toonsEnterA.reset() + self.toonsEnterB.reset() + + def __findRamp(self, name, path): + ramp = self.geom.find(path) + children = ramp.getChildren() + animate = ramp.attachNewNode(name) + children.reparentTo(animate) + fsm = ClassicFSM.ClassicFSM(name, [ + State.State('extend', + Functor(self.enterRampExtend, animate), + Functor(self.exitRampExtend, animate), [ + 'extended', + 'retract', + 'retracted']), + State.State('extended', + Functor(self.enterRampExtended, animate), + Functor(self.exitRampExtended, animate), [ + 'retract', + 'retracted']), + State.State('retract', + Functor(self.enterRampRetract, animate), + Functor(self.exitRampRetract, animate), [ + 'extend', + 'extended', + 'retracted']), + State.State('retracted', + Functor(self.enterRampRetracted, animate), + Functor(self.exitRampRetracted, animate), [ + 'extend', + 'extended']), + State.State('off', + Functor(self.enterRampOff, animate), + Functor(self.exitRampOff, animate))], + 'off', 'off', onUndefTransition=ClassicFSM.ClassicFSM.DISALLOW) + fsm.enterInitialState() + return fsm + + def enterRampExtend(self, animate): + intervalName = self.uniqueName('extend-%s' % animate.getName()) + adjustTime = 2.0 * animate.getX() / 18.0 + ival = Parallel(SoundInterval(self.rampSlideSfx, node=animate), animate.posInterval(adjustTime, Point3(0, 0, 0), blendType='easeInOut', name=intervalName)) + ival.start() + self.storeInterval(ival, intervalName) + + def exitRampExtend(self, animate): + intervalName = self.uniqueName('extend-%s' % animate.getName()) + self.clearInterval(intervalName) + + def enterRampExtended(self, animate): + animate.setPos(0, 0, 0) + + def exitRampExtended(self, animate): + pass + + def enterRampRetract(self, animate): + intervalName = self.uniqueName('retract-%s' % animate.getName()) + adjustTime = 2.0 * (18 - animate.getX()) / 18.0 + ival = Parallel(SoundInterval(self.rampSlideSfx, node=animate), animate.posInterval(adjustTime, Point3(18, 0, 0), blendType='easeInOut', name=intervalName)) + ival.start() + self.storeInterval(ival, intervalName) + + def exitRampRetract(self, animate): + intervalName = self.uniqueName('retract-%s' % animate.getName()) + self.clearInterval(intervalName) + + def enterRampRetracted(self, animate): + animate.setPos(18, 0, 0) + + def exitRampRetracted(self, animate): + pass + + def enterRampOff(self, animate): + pass + + def exitRampOff(self, animate): + pass + + def enterOff(self): + DistributedBossCog.DistributedBossCog.enterOff(self) + if self.cagedToon: + self.cagedToon.clearChat() + if self.rampA: + self.rampA.request('off') + if self.rampB: + self.rampB.request('off') + if self.rampC: + self.rampC.request('off') + + def enterWaitForToons(self): + DistributedBossCog.DistributedBossCog.enterWaitForToons(self) + self.geom.hide() + self.cagedToon.removeActive() + + def exitWaitForToons(self): + DistributedBossCog.DistributedBossCog.exitWaitForToons(self) + self.geom.show() + self.cagedToon.addActive() + + def enterElevator(self): + DistributedBossCog.DistributedBossCog.enterElevator(self) + self.rampA.request('extended') + self.rampB.request('extended') + self.rampC.request('retracted') + self.setCageIndex(0) + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.SellbotBossBattleOnePosHpr) + self.happy = 1 + self.raised = 1 + self.forward = 1 + self.doAnimate() + self.cagedToon.removeActive() + base.camLens.setMinFov(ToontownGlobals.VPElevatorFov/(4./3.)) + + def exitElevator(self): + DistributedBossCog.DistributedBossCog.exitElevator(self) + self.cagedToon.addActive() + + def enterIntroduction(self): + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.SellbotBossBattleOnePosHpr) + self.stopAnimate() + DistributedBossCog.DistributedBossCog.enterIntroduction(self) + self.rampA.request('extended') + self.rampB.request('extended') + self.rampC.request('retracted') + self.setCageIndex(0) + base.playMusic(self.promotionMusic, looping=1, volume=0.9) + + def exitIntroduction(self): + DistributedBossCog.DistributedBossCog.exitIntroduction(self) + self.promotionMusic.stop() + + def enterBattleOne(self): + DistributedBossCog.DistributedBossCog.enterBattleOne(self) + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.SellbotBossBattleOnePosHpr) + self.clearChat() + self.cagedToon.clearChat() + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('retract') + if self.battleA == None or self.battleB == None: + cageIndex = 1 + else: + cageIndex = 0 + self.setCageIndex(cageIndex) + + def exitBattleOne(self): + DistributedBossCog.DistributedBossCog.exitBattleOne(self) + + def enterRollToBattleTwo(self): + self.disableToonCollision() + self.releaseToons() + if self.arenaSide: + self.rampA.request('retract') + self.rampB.request('extend') + else: + self.rampA.request('extend') + self.rampB.request('retract') + self.rampC.request('retract') + self.reparentTo(render) + self.setCageIndex(2) + self.stickBossToFloor() + intervalName = 'RollToBattleTwo' + seq = Sequence(self.__makeRollToBattleTwoMovie(), Func(self.__onToPrepareBattleTwo), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.betweenBattleMusic, looping=1, volume=0.9) + self.__showEasyBarrels() + taskMgr.doMethodLater(0.5, self.enableToonCollision, 'enableToonCollision') + + def __onToPrepareBattleTwo(self): + self.disableToonCollision() + self.unstickBoss() + self.setPosHpr(*ToontownGlobals.SellbotBossBattleTwoPosHpr) + self.doneBarrier('RollToBattleTwo') + + def exitRollToBattleTwo(self): + self.unstickBoss() + intervalName = 'RollToBattleTwo' + self.clearInterval(intervalName) + self.betweenBattleMusic.stop() + + def disableToonCollision(self): + base.localAvatar.collisionsOff() + + def enableToonCollision(self, task): + base.localAvatar.collisionsOn() + + def enterPrepareBattleTwo(self): + self.cleanupIntervals() + self.__hideEasyBarrels() + self.controlToons() + self.clearChat() + self.cagedToon.clearChat() + self.reparentTo(render) + if self.arenaSide: + self.rampA.request('retract') + self.rampB.request('extend') + else: + self.rampA.request('extend') + self.rampB.request('retract') + self.rampC.request('retract') + self.reparentTo(render) + self.setCageIndex(2) + camera.reparentTo(render) + camera.setPosHpr(self.cage, 0, -17, 3.3, 0, 0, 0) + (localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov),) + self.hide() + self.acceptOnce('doneChatPage', self.__onToBattleTwo) + self.cagedToon.setLocalPageChat(TTLocalizer.CagedToonPrepareBattleTwo, 1) + base.playMusic(self.stingMusic, looping=0, volume=1.0) + taskMgr.doMethodLater(0.5, self.enableToonCollision, 'enableToonCollision') + + def __onToBattleTwo(self, elapsed): + self.doneBarrier('PrepareBattleTwo') + taskMgr.doMethodLater(1, self.__showWaitingMessage, self.uniqueName('WaitingMessage')) + + def exitPrepareBattleTwo(self): + self.show() + taskMgr.remove(self.uniqueName('WaitingMessage')) + self.ignore('doneChatPage') + self.__clearOnscreenMessage() + self.stingMusic.stop() + + def enterBattleTwo(self): + self.cleanupIntervals() + mult = ToontownBattleGlobals.getBossBattleCreditMultiplier(2) + localAvatar.inventory.setBattleCreditMultiplier(mult) + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.SellbotBossBattleTwoPosHpr) + self.clearChat() + self.cagedToon.clearChat() + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('retract') + self.releaseToons() + self.toonsToBattlePosition(self.toonsA, self.battleANode) + self.toonsToBattlePosition(self.toonsB, self.battleBNode) + if self.battleA == None or self.battleB == None: + cageIndex = 3 + else: + cageIndex = 2 + self.setCageIndex(cageIndex) + base.playMusic(self.battleTwoMusic, looping=1, volume=0.9) + + def exitBattleTwo(self): + intervalName = self.uniqueName('cageDrop') + self.clearInterval(intervalName) + self.cleanupBattles() + self.battleTwoMusic.stop() + localAvatar.inventory.setBattleCreditMultiplier(1) + + def enterPrepareBattleThree(self): + self.cleanupIntervals() + self.controlToons() + self.clearChat() + self.cagedToon.clearChat() + self.reparentTo(render) + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('extend') + self.setCageIndex(4) + camera.reparentTo(render) + camera.setPosHpr(self.cage, 0, -17, 3.3, 0, 0, 0) + (localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov),) + self.hide() + self.acceptOnce('doneChatPage', self.__onToBattleThree) + self.cagedToon.setLocalPageChat(TTLocalizer.CagedToonPrepareBattleThree, 1) + base.playMusic(self.betweenBattleMusic, looping=1, volume=0.9) + + def __onToBattleThree(self, elapsed): + self.doneBarrier('PrepareBattleThree') + taskMgr.doMethodLater(1, self.__showWaitingMessage, self.uniqueName('WaitingMessage')) + + def exitPrepareBattleThree(self): + self.show() + taskMgr.remove(self.uniqueName('WaitingMessage')) + self.ignore('doneChatPage') + intervalName = 'PrepareBattleThree' + self.clearInterval(intervalName) + self.__clearOnscreenMessage() + self.betweenBattleMusic.stop() + + def enterBattleThree(self): + DistributedBossCog.DistributedBossCog.enterBattleThree(self) + self.clearChat() + self.cagedToon.clearChat() + self.reparentTo(render) + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('extend') + self.setCageIndex(4) + self.happy = 0 + self.raised = 1 + self.forward = 1 + self.doAnimate() + self.accept('enterCage', self.__touchedCage) + self.accept('pieSplat', self.__pieSplat) + self.accept('localPieSplat', self.__localPieSplat) + self.accept('outOfPies', self.__outOfPies) + self.accept('begin-pie', self.__foundPieButton) + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + taskMgr.doMethodLater(30, self.__howToGetPies, self.uniqueName('PieAdvice')) + self.stickBossToFloor() + self.bossDamageMovie = self.__makeBossDamageMovie() + bossDoneEventName = self.uniqueName('DestroyedBoss') + self.bossDamageMovie.setDoneEvent(bossDoneEventName) + self.acceptOnce(bossDoneEventName, self.__doneBattleThree) + self.resetMaxDamage() + self.bossDamageToMovie = self.bossDamageMovie.getDuration() / self.bossMaxDamage + self.bossDamageMovie.setT(self.bossDamage * self.bossDamageToMovie) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9) + + def __doneBattleThree(self): + self.setState('NearVictory') + self.unstickBoss() + + def exitBattleThree(self): + DistributedBossCog.DistributedBossCog.exitBattleThree(self) + bossDoneEventName = self.uniqueName('DestroyedBoss') + self.ignore(bossDoneEventName) + taskMgr.remove(self.uniqueName('StandUp')) + self.ignore('enterCage') + self.ignore('pieSplat') + self.ignore('localPieSplat') + self.ignore('outOfPies') + self.ignore('begin-pie') + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.__removeCageShadow() + self.bossDamageMovie.finish() + self.bossDamageMovie = None + self.unstickBoss() + taskName = 'RecoverBossDamage' + taskMgr.remove(taskName) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + + def enterNearVictory(self): + self.cleanupIntervals() + self.reparentTo(render) + self.setPos(*ToontownGlobals.SellbotBossDeathPos) + self.setHpr(*ToontownGlobals.SellbotBossBattleThreeHpr) + self.clearChat() + self.cagedToon.clearChat() + self.setCageIndex(4) + self.releaseToons(finalBattle=1) + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('extend') + self.accept('enterCage', self.__touchedCage) + self.accept('pieSplat', self.__finalPieSplat) + self.accept('localPieSplat', self.__localPieSplat) + self.accept('outOfPies', self.__outOfPies) + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.happy = 0 + self.raised = 0 + self.forward = 1 + self.doAnimate() + self.setDizzy(1) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def exitNearVictory(self): + self.ignore('enterCage') + self.ignore('pieSplat') + self.ignore('localPieSplat') + self.ignore('outOfPies') + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.__removeCageShadow() + self.setDizzy(0) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + + def enterVictory(self): + self.cleanupIntervals() + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.reparentTo(render) + self.setPos(*ToontownGlobals.SellbotBossDeathPos) + self.setHpr(*ToontownGlobals.SellbotBossBattleThreeHpr) + self.clearChat() + self.cagedToon.clearChat() + self.setCageIndex(4) + self.releaseToons(finalBattle=1) + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('extend') + self.happy = 0 + self.raised = 0 + self.forward = 1 + self.setChatAbsolute(TTLocalizer.VPDeathTaunt, CFSpeech | CFTimeout) + self.doAnimate('Fb_fall', now=1) + self.acceptOnce(self.animDoneEvent, self.__continueVictory) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def __continueVictory(self): + self.stopAnimate() + self.stash() + self.doneBarrier('Victory') + + def exitVictory(self): + self.stopAnimate() + self.unstash() + self.__removeCageShadow() + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + + def enterReward(self): + self.cleanupIntervals() + self.clearChat() + self.cagedToon.clearChat() + self.stash() + self.stopAnimate() + self.setCageIndex(4) + self.releaseToons(finalBattle=1) + self.toMovieMode() + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('extend') + panelName = self.uniqueName('reward') + self.rewardPanel = RewardPanel.RewardPanel(panelName) + victory, camVictory, skipper = MovieToonVictory.doToonVictory(1, self.involvedToons, self.toonRewardIds, self.toonRewardDicts, self.deathList, self.rewardPanel, allowGroupShot=0, uberList=self.uberList, noSkip=True) + ival = Sequence(Parallel(victory, camVictory), Func(self.__doneReward)) + intervalName = 'RewardMovie' + delayDeletes = [] + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'SellbotBoss.enterReward')) + + ival.delayDeletes = delayDeletes + ival.start() + self.storeInterval(ival, intervalName) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def __doneReward(self): + self.doneBarrier('Reward') + self.toWalkMode() + + def exitReward(self): + intervalName = 'RewardMovie' + self.clearInterval(intervalName) + self.unstash() + self.rewardPanel.destroy() + del self.rewardPanel + self.__removeCageShadow() + self.battleThreeMusicTime = 0 + self.battleThreeMusic.stop() + + def enterEpilogue(self): + self.cleanupIntervals() + self.clearChat() + self.cagedToon.clearChat() + self.stash() + self.stopAnimate() + self.setCageIndex(4) + self.controlToons() + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('extend') + self.__arrangeToonsAroundCage() + camera.reparentTo(render) + camera.setPosHpr(-24, 52, 27.5, -53, -13, 0) + intervalName = 'EpilogueMovie' + seq = Sequence(self.__makeCageOpenMovie(), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + self.accept('nextChatPage', self.__epilogueChatNext) + self.accept('doneChatPage', self.__epilogueChatDone) + base.playMusic(self.epilogueMusic, looping=1, volume=0.9) + + def __epilogueChatNext(self, pageNumber, elapsed): + if pageNumber == 2: + if self.cagedToon.style.torso[1] == 'd': + track = ActorInterval(self.cagedToon, 'curtsy') + else: + track = ActorInterval(self.cagedToon, 'bow') + track = Sequence(track, Func(self.cagedToon.loop, 'neutral')) + intervalName = 'EpilogueMovieToonAnim' + self.storeInterval(track, intervalName) + track.start() + + def __epilogueChatDone(self, elapsed): + self.cagedToon.setChatAbsolute(TTLocalizer.CagedToonGoodbye, CFSpeech) + self.ignore('nextChatPage') + self.ignore('doneChatPage') + intervalName = 'EpilogueMovieToonAnim' + self.clearInterval(intervalName) + track = Parallel(Sequence(ActorInterval(self.cagedToon, 'wave'), Func(self.cagedToon.loop, 'neutral')), Sequence(Wait(0.5), Func(self.localToonToSafeZone))) + self.storeInterval(track, intervalName) + track.start() + + def exitEpilogue(self): + self.clearInterval('EpilogueMovieToonAnim') + self.unstash() + self.__removeCageShadow() + self.epilogueMusic.stop() + + def __arrangeToonsAroundCage(self): + radius = 15 + numToons = len(self.involvedToons) + center = (numToons - 1) / 2.0 + for i in xrange(numToons): + toon = base.cr.doId2do.get(self.involvedToons[i]) + if toon: + angle = 270 - 15 * (i - center) + radians = angle * math.pi / 180.0 + x = math.cos(radians) * radius + y = math.sin(radians) * radius + toon.setPos(self.cage, x, y, 0) + toon.setZ(18.0) + toon.headsUp(self.cage) + + def enterFrolic(self): + DistributedBossCog.DistributedBossCog.enterFrolic(self) + self.setPosHpr(*ToontownGlobals.SellbotBossBattleOnePosHpr) + + def doorACallback(self, isOpen): + if self.insidesANodePath: + if isOpen: + self.insidesANodePath.unstash() + else: + self.insidesANodePath.stash() + + def doorBCallback(self, isOpen): + if self.insidesBNodePath: + if isOpen: + self.insidesBNodePath.unstash() + else: + self.insidesBNodePath.stash() + + def __toonsToPromotionPosition(self, toonIds, battleNode): + points = BattleBase.BattleBase.toonPoints[len(toonIds) - 1] + for i in xrange(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + toon.reparentTo(render) + pos, h = points[i] + toon.setPosHpr(battleNode, pos[0], pos[1] + 10, pos[2], h, 0, 0) + + def __doobersToPromotionPosition(self, doobers, battleNode): + points = BattleBase.BattleBase.toonPoints[len(doobers) - 1] + for i in xrange(len(doobers)): + suit = doobers[i] + suit.fsm.request('neutral') + suit.loop('neutral') + pos, h = points[i] + suit.setPosHpr(battleNode, pos[0], pos[1] + 10, pos[2], h, 0, 0) + + def __touchedCage(self, entry): + self.sendUpdate('touchCage', []) + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + base.playSfx(self.piesRestockSfx) + if not self.everThrownPie: + taskMgr.doMethodLater(30, self.__howToThrowPies, self.uniqueName('PieAdvice')) + + def __outOfPies(self): + self.__showOnscreenMessage(TTLocalizer.BossBattleNeedMorePies) + taskMgr.doMethodLater(20, self.__howToGetPies, self.uniqueName('PieAdvice')) + + def __howToGetPies(self, task): + self.__showOnscreenMessage(TTLocalizer.BossBattleHowToGetPies) + + def __howToThrowPies(self, task): + self.__showOnscreenMessage(TTLocalizer.BossBattleHowToThrowPies) + + def __foundPieButton(self): + self.everThrownPie = 1 + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + + def __pieSplat(self, toon, pieCode): + if base.config.GetBool('easy-vp', 0): + if not self.dizzy: + pieCode = ToontownGlobals.PieCodeBossInsides + if pieCode == ToontownGlobals.PieCodeBossInsides: + if toon == localAvatar: + self.d_hitBossInsides() + self.flashRed() + elif pieCode == ToontownGlobals.PieCodeBossCog: + if toon == localAvatar: + self.d_hitBoss(1) + if self.dizzy: + self.flashRed() + self.doAnimate('hit', now=1) + + def __localPieSplat(self, pieCode, entry): + if pieCode != ToontownGlobals.PieCodeToon: + return + avatarDoId = entry.getIntoNodePath().getNetTag('avatarDoId') + if avatarDoId == '': + self.notify.warning('Toon %s has no avatarDoId tag.' % repr(entry.getIntoNodePath())) + return + doId = int(avatarDoId) + if doId != localAvatar.doId: + self.d_hitToon(doId) + + def __finalPieSplat(self, toon, pieCode): + if pieCode != ToontownGlobals.PieCodeBossCog: + return + self.sendUpdate('finalPieSplat', []) + self.ignore('pieSplat') + + def cagedToonBattleThree(self, index, avId): + str = TTLocalizer.CagedToonBattleThree.get(index) + if str: + toonName = '' + if avId: + toon = self.cr.doId2do.get(avId) + if not toon: + self.cagedToon.clearChat() + return + toonName = toon.getName() + text = str % {'toon': toonName} + self.cagedToon.setChatAbsolute(text, CFSpeech | CFTimeout) + else: + self.cagedToon.clearChat() + + def cleanupAttacks(self): + self.__cleanupStrafe() + + def __cleanupStrafe(self): + if self.strafeInterval: + self.strafeInterval.finish() + self.strafeInterval = None + + def doStrafe(self, side, direction): + gearRoot = self.rotateNode.attachNewNode('gearRoot') + if side == 0: + gearRoot.setPos(0, -7, 3) + gearRoot.setHpr(180, 0, 0) + door = self.doorA + else: + gearRoot.setPos(0, 7, 3) + door = self.doorB + gearRoot.setTag('attackCode', str(ToontownGlobals.BossCogStrafeAttack)) + gearModel = self.getGearFrisbee() + gearModel.setScale(0.1) + t = self.getBossDamage() / 100.0 + gearTrack = Parallel() + numGears = int(4 + 6 * t + 0.5) + time = 5.0 - 4.0 * t + spread = 60 * math.pi / 180.0 + if direction == 1: + spread = -spread + dist = 50 + rate = time / numGears + for i in xrange(numGears): + node = gearRoot.attachNewNode(str(i)) + node.hide() + node.setPos(0, 0, 0) + gear = gearModel.instanceTo(node) + angle = (float(i) / (numGears - 1) - 0.5) * spread + x = dist * math.sin(angle) + y = dist * math.cos(angle) + h = random.uniform(-720, 720) + gearTrack.append(Sequence(Wait(i * rate), Func(node.show), Parallel(node.posInterval(1, Point3(x, y, 0), fluid=1), node.hprInterval(1, VBase3(h, 0, 0), fluid=1), Sequence(SoundInterval(self.strafeSfx[i], volume=0.2, node=self), duration=0)), Func(node.detachNode))) + + seq = Sequence(Func(door.request, 'open'), Wait(0.7), gearTrack, Func(door.request, 'close')) + self.__cleanupStrafe() + self.strafeInterval = seq + seq.start() + + def __showEasyBarrels(self): + barrelNodes = hidden.findAllMatches('**/Distributed*Barrel-*') + if not barrelNodes or barrelNodes.isEmpty(): + return + if render.find('barrelsRootNode'): + self.notify.warning('__showEasyBarrels(): barrelsRootNode already exists') + return + self.barrelsRootNode = render.attachNewNode('barrelsRootNode') + self.barrelsRootNode.setPos(*SellbotBossGlobals.BarrelsStartPos) + if self.arenaSide == 0: + self.barrelsRootNode.setHpr(180, 0, 0) + else: + self.barrelsRootNode.setHpr(0, 0, 0) + for i, barrelNode in enumerate(barrelNodes): + barrel = base.cr.doId2do.get(int(barrelNode.getNetTag('doId'))) + SellbotBossGlobals.setBarrelAttr(barrel, barrel.entId) + if hasattr(barrel, 'applyLabel'): + barrel.applyLabel() + barrel.setPosHpr(barrel.pos, barrel.hpr) + barrel.reparentTo(self.barrelsRootNode) + + intervalName = 'MakeBarrelsAppear' + seq = Sequence(LerpPosInterval(self.barrelsRootNode, 0.5, Vec3(*SellbotBossGlobals.BarrelsFinalPos), blendType='easeInOut'), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + + def __hideEasyBarrels(self): + if hasattr(self, 'barrelsRootNode'): + self.barrelsRootNode.removeNode() + intervalName = 'MakeBarrelsAppear' + self.clearInterval(intervalName) + + def toonPromoted(self, promoted): + self.localToonPromoted = promoted diff --git a/toontown/suit/DistributedSellbotBossAI.py b/toontown/suit/DistributedSellbotBossAI.py new file mode 100755 index 00000000..de4b6abf --- /dev/null +++ b/toontown/suit/DistributedSellbotBossAI.py @@ -0,0 +1,442 @@ +import random + +import DistributedBossCogAI +import DistributedSuitAI +import SuitDNA +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.fsm import FSM +from otp.ai.AIBaseGlobal import * +from toontown.battle import BattleExperienceAI +from toontown.toon import NPCToons +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.quest import Quests +from otp.ai.MagicWordGlobal import * + + +class DistributedSellbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSellbotBossAI') + limitHitCount = 6 + hitCountDamage = 35 + numPies = ToontownGlobals.FullPies + BossName = "VP" + + def __init__(self, air): + DistributedBossCogAI.DistributedBossCogAI.__init__(self, air, 's') + FSM.FSM.__init__(self, 'DistributedSellbotBossAI') + self.doobers = [] + self.cagedToonNpcId = random.choice(NPCToons.HQnpcFriends.keys()) + self.bossMaxDamage = ToontownGlobals.SellbotBossMaxDamage + self.recoverRate = 0 + self.recoverStartTime = 0 + + def delete(self): + return DistributedBossCogAI.DistributedBossCogAI.delete(self) + + def getHoodId(self): + return ToontownGlobals.SellbotHQ + + def getCagedToonNpcId(self): + return self.cagedToonNpcId + + def magicWordHit(self, damage, avId): + if self.attackCode != ToontownGlobals.BossCogDizzyNow: + self.hitBossInsides() + self.hitBoss(damage) + + def hitBoss(self, bossDamage): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'hitBoss from unknown avatar'): + return + self.validate(avId, bossDamage == 1, 'invalid bossDamage %s' % bossDamage) + if bossDamage < 1: + return + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + if self.attackCode != ToontownGlobals.BossCogDizzyNow: + return + bossDamage = min(self.getBossDamage() + bossDamage, self.bossMaxDamage) + self.b_setBossDamage(bossDamage, 0, 0) + if self.bossDamage >= self.bossMaxDamage: + self.setState('NearVictory') + else: + self.__recordHit() + + def hitBossInsides(self): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'hitBossInsides from unknown avatar'): + return + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + self.b_setAttackCode(ToontownGlobals.BossCogDizzyNow) + self.b_setBossDamage(self.getBossDamage(), 0, 0) + + def hitToon(self, toonId): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId != toonId, 'hitToon on self'): + return + if avId not in self.involvedToons or toonId not in self.involvedToons: + return + toon = self.air.doId2do.get(toonId) + if toon: + self.healToon(toon, 1) + + def touchCage(self): + avId = self.air.getAvatarIdFromSender() + currState = self.getCurrentOrNextState() + if currState != 'BattleThree' and currState != 'NearVictory': + return + if not self.validate(avId, avId in self.involvedToons, 'touchCage from unknown avatar'): + return + toon = simbase.air.doId2do.get(avId) + if toon: + toon.b_setNumPies(self.numPies) + toon.__touchedCage = 1 + self.__goodJump(avId) + + def finalPieSplat(self): + if self.state != 'NearVictory': + return + self.b_setState('Victory') + + def doNextAttack(self, task): + if self.attackCode == ToontownGlobals.BossCogDizzyNow: + attackCode = ToontownGlobals.BossCogRecoverDizzyAttack + else: + attackCode = random.choice([ToontownGlobals.BossCogAreaAttack, + ToontownGlobals.BossCogFrontAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack]) + if attackCode == ToontownGlobals.BossCogAreaAttack: + self.__doAreaAttack() + elif attackCode == ToontownGlobals.BossCogDirectedAttack: + self.__doDirectedAttack() + else: + self.b_setAttackCode(attackCode) + + def __doAreaAttack(self): + self.b_setAttackCode(ToontownGlobals.BossCogAreaAttack) + if self.recoverRate: + newRecoverRate = min(200, self.recoverRate * 1.2) + else: + newRecoverRate = 2 + now = globalClock.getFrameTime() + self.b_setBossDamage(self.getBossDamage(), newRecoverRate, now) + + def __doDirectedAttack(self): + if self.nearToons: + toonId = random.choice(self.nearToons) + self.b_setAttackCode(ToontownGlobals.BossCogDirectedAttack, toonId) + else: + self.__doAreaAttack() + + def b_setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + self.d_setBossDamage(bossDamage, recoverRate, recoverStartTime) + self.setBossDamage(bossDamage, recoverRate, recoverStartTime) + + def setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + self.bossDamage = bossDamage + self.recoverRate = recoverRate + self.recoverStartTime = recoverStartTime + + def getBossDamage(self): + now = globalClock.getFrameTime() + elapsed = now - self.recoverStartTime + return int(max(self.bossDamage - self.recoverRate * elapsed / 60.0, 0)) + + def d_setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + timestamp = globalClockDelta.localToNetworkTime(recoverStartTime) + self.sendUpdate('setBossDamage', [bossDamage, recoverRate, timestamp]) + + def waitForNextStrafe(self, delayTime): + currState = self.getCurrentOrNextState() + if currState == 'BattleThree': + taskName = self.uniqueName('NextStrafe') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.doNextStrafe, taskName) + + def stopStrafes(self): + taskName = self.uniqueName('NextStrafe') + taskMgr.remove(taskName) + + def doNextStrafe(self, task): + if self.attackCode != ToontownGlobals.BossCogDizzyNow: + side = random.choice([0, 1]) + direction = random.choice([0, 1]) + self.sendUpdate('doStrafe', [side, direction]) + delayTime = 9 + self.waitForNextStrafe(delayTime) + + def __sendDooberIds(self): + dooberIds = [] + for suit in self.doobers: + dooberIds.append(suit.doId) + + self.sendUpdate('setDooberIds', [dooberIds]) + + def d_cagedToonBattleThree(self, index, avId): + self.sendUpdate('cagedToonBattleThree', [index, avId]) + + def formatReward(self): + return str(self.cagedToonNpcId) + + def makeBattleOneBattles(self): + self.postBattleState = 'RollToBattleTwo' + self.initializeBattles(1, ToontownGlobals.SellbotBossBattleOnePosHpr) + + def generateSuits(self, battleNumber): + if battleNumber == 1: + return self.invokeSuitPlanner(9, 0) + else: + return self.invokeSuitPlanner(10, 1) + + def removeToon(self, avId): + toon = simbase.air.doId2do.get(avId) + if toon: + toon.b_setNumPies(0) + DistributedBossCogAI.DistributedBossCogAI.removeToon(self, avId) + + def enterOff(self): + DistributedBossCogAI.DistributedBossCogAI.enterOff(self) + self.__resetDoobers() + + def enterElevator(self): + DistributedBossCogAI.DistributedBossCogAI.enterElevator(self) + self.b_setBossDamage(0, 0, 0) + + def enterIntroduction(self): + DistributedBossCogAI.DistributedBossCogAI.enterIntroduction(self) + self.__makeDoobers() + self.b_setBossDamage(0, 0, 0) + + def exitIntroduction(self): + DistributedBossCogAI.DistributedBossCogAI.exitIntroduction(self) + self.__resetDoobers() + + def enterRollToBattleTwo(self): + self.divideToons() + self.barrier = self.beginBarrier('RollToBattleTwo', self.involvedToons, 45, self.__doneRollToBattleTwo) + + def __doneRollToBattleTwo(self, avIds): + self.b_setState('PrepareBattleTwo') + + def exitRollToBattleTwo(self): + self.ignoreBarrier(self.barrier) + + def enterPrepareBattleTwo(self): + self.barrier = self.beginBarrier('PrepareBattleTwo', self.involvedToons, 30, self.__donePrepareBattleTwo) + self.makeBattleTwoBattles() + + def __donePrepareBattleTwo(self, avIds): + self.b_setState('BattleTwo') + + def exitPrepareBattleTwo(self): + self.ignoreBarrier(self.barrier) + + def makeBattleTwoBattles(self): + self.postBattleState = 'PrepareBattleThree' + self.initializeBattles(2, ToontownGlobals.SellbotBossBattleTwoPosHpr) + + def enterBattleTwo(self): + if self.battleA: + self.battleA.startBattle(self.toonsA, self.suitsA) + if self.battleB: + self.battleB.startBattle(self.toonsB, self.suitsB) + + def exitBattleTwo(self): + self.resetBattles() + + def enterPrepareBattleThree(self): + self.barrier = self.beginBarrier('PrepareBattleThree', self.involvedToons, 30, self.__donePrepareBattleThree) + + def __donePrepareBattleThree(self, avIds): + self.b_setState('BattleThree') + + def exitPrepareBattleThree(self): + self.ignoreBarrier(self.barrier) + + def enterBattleThree(self): + self.resetBattles() + self.setPieType() + self.b_setBossDamage(0, 0, 0) + self.battleThreeStart = globalClock.getFrameTime() + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.__touchedCage = 0 + + self.waitForNextAttack(5) + self.waitForNextStrafe(9) + self.cagedToonDialogIndex = 100 + self.__saySomethingLater() + + def __saySomething(self, task = None): + index = None + avId = 0 + if len(self.involvedToons) == 0: + return + avId = random.choice(self.involvedToons) + toon = simbase.air.doId2do.get(avId) + if toon.__touchedCage: + if self.cagedToonDialogIndex <= TTLocalizer.CagedToonBattleThreeMaxAdvice: + index = self.cagedToonDialogIndex + self.cagedToonDialogIndex += 1 + elif random.random() < 0.2: + index = random.randrange(100, TTLocalizer.CagedToonBattleThreeMaxAdvice + 1) + else: + index = random.randrange(20, TTLocalizer.CagedToonBattleThreeMaxTouchCage + 1) + if index: + self.d_cagedToonBattleThree(index, avId) + self.__saySomethingLater() + return + + def __saySomethingLater(self, delayTime = 15): + taskName = self.uniqueName('CagedToonSaySomething') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.__saySomething, taskName) + + def __goodJump(self, avId): + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + index = random.randrange(10, TTLocalizer.CagedToonBattleThreeMaxGivePies + 1) + self.d_cagedToonBattleThree(index, avId) + self.__saySomethingLater() + + def exitBattleThree(self): + self.stopAttacks() + self.stopStrafes() + taskName = self.uniqueName('CagedToonSaySomething') + taskMgr.remove(taskName) + + def enterNearVictory(self): + self.resetBattles() + + def exitNearVictory(self): + pass + + def enterVictory(self): + self.resetBattles() + self.suitsKilled.append({'type': None, + 'level': None, + 'track': self.dna.dept, + 'isSkelecog': 0, + 'isForeman': 0, + 'isBoss': 1, + 'isSupervisor': 0, + 'isVirtual': 0, + 'activeToons': self.involvedToons[:]}) + self.addStats() + self.barrier = self.beginBarrier('Victory', self.involvedToons, 10, self.__doneVictory) + return + + def __doneVictory(self, avIds): + self.d_setBattleExperience() + self.b_setState('Reward') + BattleExperienceAI.assignRewards(self.involvedToons, self.toonSkillPtsGained, self.suitsKilled, ToontownGlobals.dept2cogHQ(self.dept), self.helpfulToons) + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + if not toon.attemptAddNPCFriend(self.cagedToonNpcId, Quests.InVP): + self.notify.info('%s.unable to add NPCFriend %s to %s.' % (self.doId, self.cagedToonNpcId, toonId)) + toon.b_promote(self.deptIndex) + + def exitVictory(self): + self.takeAwayPies() + + def enterFrolic(self): + DistributedBossCogAI.DistributedBossCogAI.enterFrolic(self) + self.b_setBossDamage(0, 0, 0) + + def __resetDoobers(self): + for suit in self.doobers: + suit.requestDelete() + + self.doobers = [] + + def __makeDoobers(self): + self.__resetDoobers() + for i in xrange(8): + suit = DistributedSuitAI.DistributedSuitAI(self.air, None) + level = random.randrange(len(SuitDNA.suitsPerLevel)) + suit.dna = SuitDNA.SuitDNA() + suit.dna.newSuitRandom(level=level, dept=self.dna.dept) + suit.setLevel(level) + suit.generateWithRequired(self.zoneId) + self.doobers.append(suit) + + self.__sendDooberIds() + return + + def setPieType(self): + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.d_setPieType(4) + + def takeAwayPies(self): + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.b_setNumPies(0) + + def __recordHit(self): + now = globalClock.getFrameTime() + self.hitCount += 1 + if self.hitCount < self.limitHitCount or self.bossDamage < self.hitCountDamage: + return + self.b_setAttackCode(ToontownGlobals.BossCogRecoverDizzyAttack) + + def enterReward(self): + DistributedBossCogAI.DistributedBossCogAI.enterReward(self) + +def getVP(invoker): + for do in simbase.air.doId2do.values(): + if isinstance(do, DistributedSellbotBossAI): + if invoker.doId in do.involvedToons: + return do + +@magicWord(category=CATEGORY_ADMINISTRATOR) +def secondVP(): + """ + Skips to the second round of the VP. + """ + invoker = spellbook.getInvoker() + boss = getVP(invoker) + if not boss: + return "You aren't in a VP!" + boss.exitIntroduction() + boss.b_setState('RollToBattleTwo') + return 'Skipping to the second round...' + +@magicWord(category=CATEGORY_ADMINISTRATOR) +def skipVP(): + """ + Skips to the final round of the VP. + """ + invoker = spellbook.getInvoker() + boss = getVP(invoker) + if not boss: + return "You aren't in a VP!" + if boss.state in ('PrepareBattleThree', 'BattleThree'): + return "You can't skip this round." + boss.exitIntroduction() + boss.b_setState('PrepareBattleThree') + return 'Skipping the first round...' + +@magicWord(category=CATEGORY_ADMINISTRATOR) +def killVP(): + """ + Kills the VP. + """ + invoker = spellbook.getInvoker() + boss = getVP(invoker) + if not boss: + return "You aren't in a VP!" + boss.b_setState('Victory') + return 'Killed VP.' diff --git a/toontown/suit/DistributedStageSuit.py b/toontown/suit/DistributedStageSuit.py new file mode 100755 index 00000000..33998136 --- /dev/null +++ b/toontown/suit/DistributedStageSuit.py @@ -0,0 +1,32 @@ +from toontown.suit import DistributedFactorySuit +from toontown.suit.Suit import * +from direct.directnotify import DirectNotifyGlobal +from direct.actor import Actor +from otp.avatar import Avatar +import SuitDNA +from toontown.toonbase import ToontownGlobals +from panda3d.core import * +from toontown.battle import SuitBattleGlobals +from direct.task import Task +from toontown.battle import BattleProps +from toontown.toonbase import TTLocalizer +import string + +class DistributedStageSuit(DistributedFactorySuit.DistributedFactorySuit): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageSuit') + + def setCogSpec(self, spec): + self.spec = spec + self.setPos(spec['pos']) + self.setH(spec['h']) + self.originalPos = spec['pos'] + self.escapePos = spec['pos'] + self.pathEntId = spec['path'] + self.behavior = spec['behavior'] + self.skeleton = spec['skeleton'] + self.boss = spec['boss'] + self.revives = spec.get('revives') + if self.reserve: + self.reparentTo(hidden) + else: + self.doReparent() diff --git a/toontown/suit/DistributedStageSuitAI.py b/toontown/suit/DistributedStageSuitAI.py new file mode 100755 index 00000000..2398461e --- /dev/null +++ b/toontown/suit/DistributedStageSuitAI.py @@ -0,0 +1,15 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.suit import DistributedFactorySuitAI + + +class DistributedStageSuitAI(DistributedFactorySuitAI.DistributedFactorySuitAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageSuitAI') + + def isForeman(self): + return 0 + + def isSupervisor(self): + return self.boss + + def isVirtual(self): + return self.virtual diff --git a/toontown/suit/DistributedSuit.py b/toontown/suit/DistributedSuit.py new file mode 100755 index 00000000..6f243ecd --- /dev/null +++ b/toontown/suit/DistributedSuit.py @@ -0,0 +1,699 @@ +import copy +from direct.directnotify import DirectNotifyGlobal +from direct.directtools.DirectGeometry import CLAMP +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.interval.IntervalGlobal import * +from direct.task import Task +import math +from panda3d.core import * +import random + +import DistributedSuitBase +import DistributedSuitPlanner +import Suit +import SuitBase +import SuitDialog +import SuitTimings +from otp.avatar import DistributedAvatar +from otp.otpbase import OTPLocalizer +from toontown.battle import BattleProps +from toontown.battle import DistributedBattle +from toontown.distributed.DelayDeletable import DelayDeletable +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals +from libpandadna import * +from toontown.toonbase import ToontownGlobals + + +STAND_OUTSIDE_DOOR = 2.5 +BATTLE_IGNORE_TIME = 6 +BATTLE_WAIT_TIME = 3 +CATCHUP_SPEED_MULTIPLIER = 3 +ALLOW_BATTLE_DETECT = 1 + +class DistributedSuit(DistributedSuitBase.DistributedSuitBase, DelayDeletable): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSuit') + ENABLE_EXPANDED_NAME = 0 + + def __init__(self, cr): + try: + self.DistributedSuit_initialized + return + except: + self.DistributedSuit_initialized = 1 + + DistributedSuitBase.DistributedSuitBase.__init__(self, cr) + self.spDoId = None + self.pathEndpointStart = 0 + self.pathEndpointEnd = 0 + self.minPathLen = 0 + self.maxPathLen = 0 + self.pathPositionIndex = 0 + self.pathPositionTimestamp = 0.0 + self.pathState = 0 + self.path = None + self.localPathState = 0 + self.currentLeg = -1 + self.pathStartTime = 0.0 + self.legList = None + self.initState = None + self.finalState = None + self.buildingSuit = 0 + self.fsm = ClassicFSM.ClassicFSM('DistributedSuit', [ + State.State('Off', + self.enterOff, + self.exitOff, [ + 'FromSky', + 'FromSuitBuilding', + 'Walk', + 'Battle', + 'neutral', + 'ToToonBuilding', + 'ToSuitBuilding', + 'ToCogHQ', + 'FromCogHQ', + 'ToSky', + 'FlyAway', + 'DanceThenFlyAway', + 'WalkToStreet', + 'WalkFromStreet']), + State.State('FromSky', + self.enterFromSky, + self.exitFromSky, [ + 'Walk', + 'Battle', + 'neutral', + 'ToSky', + 'WalkFromStreet']), + State.State('FromSuitBuilding', + self.enterFromSuitBuilding, + self.exitFromSuitBuilding, [ + 'WalkToStreet', + 'Walk', + 'Battle', + 'neutral', + 'ToSky']), + State.State('WalkToStreet', + self.enterWalkToStreet, + self.exitWalkToStreet, [ + 'Walk', + 'Battle', + 'neutral', + 'ToSky', + 'ToToonBuilding', + 'ToSuitBuilding', + 'ToCogHQ', + 'WalkFromStreet']), + State.State('WalkFromStreet', + self.enterWalkFromStreet, + self.exitWalkFromStreet, [ + 'ToToonBuilding', + 'ToSuitBuilding', + 'ToCogHQ', + 'Battle', + 'neutral', + 'ToSky']), + State.State('Walk', + self.enterWalk, + self.exitWalk, [ + 'WaitForBattle', + 'Battle', + 'neutral', + 'WalkFromStreet', + 'ToSky', + 'ToCogHQ', + 'Walk']), + State.State('Battle', + self.enterBattle, + self.exitBattle, [ + 'Walk', + 'ToToonBuilding', + 'ToCogHQ', + 'ToSuitBuilding', + 'ToSky']), + State.State('neutral', + self.enterNeutral, + self.exitNeutral, []), + State.State('WaitForBattle', + self.enterWaitForBattle, + self.exitWaitForBattle, [ + 'Battle', + 'neutral', + 'Walk', + 'WalkToStreet', + 'WalkFromStreet', + 'ToToonBuilding', + 'ToCogHQ', + 'ToSuitBuilding', + 'ToSky']), + State.State('ToToonBuilding', + self.enterToToonBuilding, + self.exitToToonBuilding, [ + 'neutral', + 'Battle']), + State.State('ToSuitBuilding', + self.enterToSuitBuilding, + self.exitToSuitBuilding, [ + 'neutral', + 'Battle']), + State.State('ToCogHQ', + self.enterToCogHQ, + self.exitToCogHQ, [ + 'neutral', + 'Battle']), + State.State('FromCogHQ', + self.enterFromCogHQ, + self.exitFromCogHQ, [ + 'neutral', + 'Battle', + 'Walk']), + State.State('ToSky', + self.enterToSky, + self.exitToSky, [ + 'Battle']), + State.State('FlyAway', + self.enterFlyAway, + self.exitFlyAway, + []), + State.State('DanceThenFlyAway', + self.enterDanceThenFlyAway, + self.exitDanceThenFlyAway, + [])], + 'Off', 'Off') + self.fsm.enterInitialState() + self.soundSequenceList = [] + self.__currentDialogue = None + return + + def generate(self): + DistributedSuitBase.DistributedSuitBase.generate(self) + + def disable(self): + for soundSequence in self.soundSequenceList: + soundSequence.finish() + + self.soundSequenceList = [] + self.notify.debug('DistributedSuit %d: disabling' % self.getDoId()) + self.resumePath(0) + self.stopPathNow() + self.setState('Off') + DistributedSuitBase.DistributedSuitBase.disable(self) + + def delete(self): + try: + self.DistributedSuit_deleted + except: + self.DistributedSuit_deleted = 1 + self.notify.debug('DistributedSuit %d: deleting' % self.getDoId()) + del self.fsm + DistributedSuitBase.DistributedSuitBase.delete(self) + + def setPathEndpoints(self, start, end, minPathLen, maxPathLen): + if self.pathEndpointStart == start and self.pathEndpointEnd == end and self.minPathLen == minPathLen and self.maxPathLen == maxPathLen and self.path != None: + return + self.pathEndpointStart = start + self.pathEndpointEnd = end + self.minPathLen = minPathLen + self.maxPathLen = maxPathLen + self.path = None + self.pathLength = 0 + self.currentLeg = -1 + self.legList = None + if self.maxPathLen == 0 or not self.verifySuitPlanner() or start not in self.sp.pointIndexes or end not in self.sp.pointIndexes: + return + self.startPoint = self.sp.pointIndexes[self.pathEndpointStart] + self.endPoint = self.sp.pointIndexes[self.pathEndpointEnd] + path = self.sp.genPath(self.startPoint, self.endPoint, self.minPathLen, self.maxPathLen) + self.setPath(path) + self.makeLegList() + return + + def verifySuitPlanner(self): + if self.sp == None and self.spDoId != 0: + self.notify.warning('Suit %d does not have a suit planner! Expected SP doId %s.' % (self.doId, self.spDoId)) + self.sp = self.cr.doId2do.get(self.spDoId, None) + if self.sp == None: + return 0 + return 1 + + def setPathPosition(self, index, timestamp): + if not self.verifySuitPlanner(): + return + if self.path == None: + self.setPathEndpoints(self.pathEndpointStart, self.pathEndpointEnd, self.minPathLen, self.maxPathLen) + self.pathPositionIndex = index + self.pathPositionTimestamp = globalClockDelta.networkToLocalTime(timestamp) + if self.legList != None: + self.pathStartTime = self.pathPositionTimestamp - self.legList.getStartTime(self.pathPositionIndex) + return + + def setPathState(self, state): + self.pathState = state + self.resumePath(state) + + def debugSuitPosition(self, elapsed, currentLeg, x, y, timestamp): + now = globalClock.getFrameTime() + chug = globalClock.getRealTime() - now + messageAge = now - globalClockDelta.networkToLocalTime(timestamp, now) + if messageAge < -(chug + 0.5) or messageAge > chug + 1.0: + print 'Apparently out of sync with AI by %0.2f seconds. Suggest resync!' % messageAge + return + localElapsed = now - self.pathStartTime + timeDiff = localElapsed - (elapsed + messageAge) + if abs(timeDiff) > 0.2: + print "%s (%d) appears to be %0.2f seconds out of sync along its path. Suggest '~cogs sync'." % (self.getName(), self.getDoId(), timeDiff) + return + if self.legList == None: + print "%s (%d) doesn't have a legList yet." % (self.getName(), self.getDoId()) + return + netPos = Point3(x, y, 0.0) + leg = self.legList.getLeg(currentLeg) + calcPos = leg.getPosAtTime(elapsed - leg.getStartTime()) + calcPos.setZ(0.0) + calcDelta = Vec3(netPos - calcPos) + diff = calcDelta.length() + if diff > 4.0: + print '%s (%d) is %0.2f feet from the AI computed path!' % (self.getName(), self.getDoId(), diff) + print 'Probably your DNA files are out of sync.' + return + localPos = Point3(self.getX(), self.getY(), 0.0) + localDelta = Vec3(netPos - localPos) + diff = localDelta.length() + if diff > 10.0: + print '%s (%d) in state %s is %0.2f feet from its correct position!' % (self.getName(), + self.getDoId(), + self.fsm.getCurrentState().getName(), + diff) + print 'Should be at (%0.2f, %0.2f), but is at (%0.2f, %0.2f).' % (x, + y, + localPos[0], + localPos[1]) + return + print '%s (%d) is in the correct position.' % (self.getName(), self.getDoId()) + return + + def denyBattle(self): + DistributedSuitBase.DistributedSuitBase.denyBattle(self) + self.disableBattleDetect() + + def resumePath(self, state): + if self.localPathState != state: + self.localPathState = state + if state == 0: + self.stopPathNow() + elif state == 1: + self.moveToNextLeg(None) + elif state == 2: + self.stopPathNow() + if self.sp != None: + self.setState('Off') + self.setState('FlyAway') + elif state == 3: + pass + elif state == 4: + self.stopPathNow() + if self.sp != None: + self.setState('Off') + self.setState('DanceThenFlyAway') + else: + self.notify.error('No such state as: ' + str(state)) + return + + def moveToNextLeg(self, task): + if self.legList == None: + self.notify.warning('Suit %d does not have a path!' % self.getDoId()) + return Task.done + now = globalClock.getFrameTime() + elapsed = now - self.pathStartTime + nextLeg = self.legList.getLegIndexAtTime(elapsed, 0) + numLegs = self.legList.getNumLegs() + if self.currentLeg != nextLeg: + self.currentLeg = nextLeg + self.doPathLeg(self.legList.getLeg(nextLeg), elapsed - self.legList.getStartTime(nextLeg)) + nextLeg += 1 + if nextLeg < numLegs: + nextTime = self.legList.getStartTime(nextLeg) + delay = nextTime - elapsed + name = self.taskName('move') + taskMgr.remove(name) + taskMgr.doMethodLater(delay, self.moveToNextLeg, name) + return Task.done + + def doPathLeg(self, leg, time): + self.fsm.request(SuitLeg.getTypeName(leg.getType()), [leg, time]) + return 0 + + def stopPathNow(self): + name = self.taskName('move') + taskMgr.remove(name) + self.currentLeg = -1 + + def calculateHeading(self, a, b): + xdelta = b[0] - a[0] + ydelta = b[1] - a[1] + if ydelta == 0: + if xdelta > 0: + return -90 + else: + return 90 + elif xdelta == 0: + if ydelta > 0: + return 0 + else: + return 180 + else: + angle = math.atan2(ydelta, xdelta) + return rad2Deg(angle) - 90 + + def beginBuildingMove(self, moveIn, doneEvent, suit = 0): + doorPt = Point3(0) + buildingPt = Point3(0) + streetPt = Point3(0) + if self.virtualPos: + doorPt.assign(self.virtualPos) + else: + doorPt.assign(self.getPos()) + if moveIn: + streetPt = self.prevPointPos() + else: + streetPt = self.currPointPos() + dx = doorPt[0] - streetPt[0] + dy = doorPt[1] - streetPt[1] + buildingPt = Point3(doorPt[0] + dx, doorPt[1] + dy, doorPt[2]) + if moveIn: + if suit: + moveTime = SuitTimings.toSuitBuilding + else: + moveTime = SuitTimings.toToonBuilding + return self.beginMove(doneEvent, buildingPt, time=moveTime) + else: + return self.beginMove(doneEvent, doorPt, buildingPt, time=SuitTimings.fromSuitBuilding) + return None + + def setSPDoId(self, doId): + self.spDoId = doId + self.sp = self.cr.doId2do.get(doId, None) + if self.sp == None and self.spDoId != 0: + self.notify.warning('Suit %s created before its suit planner, %d' % (self.doId, self.spDoId)) + return + + def d_requestBattle(self, pos, hpr): + self.cr.playGame.getPlace().setState('WaitForBattle') + self.sendUpdate('requestBattle', [pos[0], + pos[1], + pos[2], + hpr[0], + hpr[1], + hpr[2]]) + + def __handleToonCollision(self, collEntry): + if not base.localAvatar.wantBattles: + return + toonId = base.localAvatar.getDoId() + self.notify.debug('Distributed suit: requesting a Battle with ' + 'toon: %d' % toonId) + self.d_requestBattle(self.getPos(), self.getHpr()) + self.setState('WaitForBattle') + + def setAnimState(self, state): + self.setState(state) + + def enterFromSky(self, leg, time): + self.enableBattleDetect('fromSky', self.__handleToonCollision) + self.loop('neutral', 0) + if not self.verifySuitPlanner(): + return + a = leg.getPosA() + b = leg.getPosB() + h = self.calculateHeading(a, b) + self.setPosHprScale(a[0], a[1], a[2], h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = self.beginSupaFlyMove(a, 1, 'fromSky') + self.mtrack.start(time) + + def exitFromSky(self): + self.disableBattleDetect() + self.mtrack.finish() + del self.mtrack + self.detachPropeller() + + def enterWalkToStreet(self, leg, time): + self.enableBattleDetect('walkToStreet', self.__handleToonCollision) + self.loop('walk', 0) + a = leg.getPosA() + b = leg.getPosB() + delta = Vec3(b - a) + length = delta.length() + delta *= (length - STAND_OUTSIDE_DOOR) / length + a1 = Point3(b - delta) + self.enableRaycast(1) + h = self.calculateHeading(a, b) + self.setHprScale(h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = Sequence(LerpPosInterval(self, leg.getLegTime(), b, startPos=a1), name=self.taskName('walkToStreet')) + self.mtrack.start(time) + + def exitWalkToStreet(self): + self.disableBattleDetect() + self.enableRaycast(0) + self.mtrack.finish() + del self.mtrack + + def enterWalkFromStreet(self, leg, time): + self.enableBattleDetect('walkFromStreet', self.__handleToonCollision) + self.loop('walk', 0) + a = leg.getPosA() + b = leg.getPosB() + delta = Vec3(b - a) + length = delta.length() + delta *= (length - STAND_OUTSIDE_DOOR) / length + b1 = Point3(a + delta) + self.enableRaycast(1) + h = self.calculateHeading(a, b) + self.setHprScale(h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = Sequence(LerpPosInterval(self, leg.getLegTime(), b1, startPos=a), name=self.taskName('walkFromStreet')) + self.mtrack.start(time) + + def exitWalkFromStreet(self): + self.disableBattleDetect() + self.enableRaycast(0) + self.mtrack.finish() + del self.mtrack + + def enterWalk(self, leg, time): + self.enableBattleDetect('bellicose', self.__handleToonCollision) + self.loop('walk', 0) + a = leg.getPosA() + b = leg.getPosB() + h = self.calculateHeading(a, b) + pos = leg.getPosAtTime(time) + self.setPosHprScale(pos[0], pos[1], pos[2], h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = Sequence(LerpPosInterval(self, leg.getLegTime(), b, startPos=a), name=self.taskName('bellicose')) + self.mtrack.start(time) + + def exitWalk(self): + self.disableBattleDetect() + self.mtrack.pause() + del self.mtrack + + def enterToSky(self, leg, time): + self.enableBattleDetect('toSky', self.__handleToonCollision) + if not self.verifySuitPlanner(): + return + a = leg.getPosA() + b = leg.getPosB() + h = self.calculateHeading(a, b) + self.setPosHprScale(b[0], b[1], b[2], h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = self.beginSupaFlyMove(b, 0, 'toSky') + self.mtrack.start(time) + + def exitToSky(self): + self.disableBattleDetect() + self.mtrack.finish() + del self.mtrack + self.detachPropeller() + + def enterFromSuitBuilding(self, leg, time): + self.enableBattleDetect('fromSuitBuilding', self.__handleToonCollision) + self.loop('walk', 0) + if not self.verifySuitPlanner(): + return + a = leg.getPosA() + b = leg.getPosB() + delta = Vec3(b - a) + length = delta.length() + delta2 = delta * (self.sp.suitWalkSpeed * leg.getLegTime()) / length + delta *= (length - STAND_OUTSIDE_DOOR) / length + b1 = Point3(b - delta) + a1 = Point3(b1 - delta2) + self.enableRaycast(1) + h = self.calculateHeading(a, b) + self.setHprScale(h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = Sequence(LerpPosInterval(self, leg.getLegTime(), b1, startPos=a1), name=self.taskName('fromSuitBuilding')) + self.mtrack.start(time) + + def exitFromSuitBuilding(self): + self.disableBattleDetect() + self.mtrack.finish() + del self.mtrack + + def enterToToonBuilding(self, leg, time): + self.loop('neutral', 0) + + def exitToToonBuilding(self): + pass + + def enterToSuitBuilding(self, leg, time): + self.loop('walk', 0) + if not self.verifySuitPlanner(): + return + a = leg.getPosA() + b = leg.getPosB() + delta = Vec3(b - a) + length = delta.length() + delta2 = delta * (self.sp.suitWalkSpeed * leg.getLegTime()) / length + delta *= (length - STAND_OUTSIDE_DOOR) / length + a1 = Point3(a + delta) + b1 = Point3(a1 + delta2) + self.enableRaycast(1) + h = self.calculateHeading(a, b) + self.setHprScale(h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = Sequence(LerpPosInterval(self, leg.getLegTime(), b1, startPos=a1), name=self.taskName('toSuitBuilding')) + self.mtrack.start(time) + + def exitToSuitBuilding(self): + self.mtrack.finish() + del self.mtrack + + def enterToCogHQ(self, leg, time): + self.loop('neutral', 0) + + def exitToCogHQ(self): + pass + + def enterFromCogHQ(self, leg, time): + self.loop('neutral', 0) + self.detachNode() + + def exitFromCogHQ(self): + self.reparentTo(render) + + def enterBattle(self): + DistributedSuitBase.DistributedSuitBase.enterBattle(self) + self.resumePath(0) + + def enterNeutral(self): + self.notify.debug('DistributedSuit: Neutral (entering a Door)') + self.resumePath(0) + self.loop('neutral', 0) + + def exitNeutral(self): + pass + + def enterWaitForBattle(self): + DistributedSuitBase.DistributedSuitBase.enterWaitForBattle(self) + self.resumePath(0) + + def enterFlyAway(self): + self.enableBattleDetect('flyAway', self.__handleToonCollision) + if not self.verifySuitPlanner(): + return + b = Point3(self.getPos()) + self.mtrack = self.beginSupaFlyMove(b, 0, 'flyAway') + self.mtrack.start() + + def exitFlyAway(self): + self.disableBattleDetect() + self.mtrack.finish() + del self.mtrack + self.detachPropeller() + + def enterDanceThenFlyAway(self): + self.enableBattleDetect('danceThenFlyAway', self.__handleToonCollision) + if not self.verifySuitPlanner(): + return + danceTrack = self.actorInterval('victory') + b = Point3(self.getPos()) + flyMtrack = self.beginSupaFlyMove(b, 0, 'flyAway') + self.mtrack = Sequence(danceTrack, flyMtrack, name=self.taskName('danceThenFlyAway')) + self.mtrack.start() + + def exitDanceThenFlyAway(self): + self.disableBattleDetect() + self.mtrack.finish() + del self.mtrack + self.detachPropeller() + + def playCurrentDialogue(self, dialogue, chatFlags, interrupt = 1): + if interrupt and self.__currentDialogue is not None: + self.__currentDialogue.stop() + self.__currentDialogue = dialogue + if dialogue: + base.playSfx(dialogue, node=self) + elif chatFlags & CFSpeech != 0: + if self.nametag.getNumChatPages() > 0: + self.playDialogueForString(self.nametag.getChat()) + if self.soundChatBubble != None: + base.playSfx(self.soundChatBubble, node=self) + elif self.nametag.getChatStomp() > 0: + self.playDialogueForString(self.nametag.getStompText(), self.nametag.getStompDelay()) + + def playDialogueForString(self, chatString, delay = 0.0): + if len(chatString) == 0: + return + searchString = chatString.lower() + if searchString.find(OTPLocalizer.DialogSpecial) >= 0: + type = 'special' + elif searchString.find(OTPLocalizer.DialogExclamation) >= 0: + type = 'exclamation' + elif searchString.find(OTPLocalizer.DialogQuestion) >= 0: + type = 'question' + elif random.randint(0, 1): + type = 'statementA' + else: + type = 'statementB' + stringLength = len(chatString) + if stringLength <= OTPLocalizer.DialogLength1: + length = 1 + elif stringLength <= OTPLocalizer.DialogLength2: + length = 2 + elif stringLength <= OTPLocalizer.DialogLength3: + length = 3 + else: + length = 4 + self.playDialogue(type, length, delay) + + def playDialogue(self, type, length, delay = 0.0): + dialogueArray = self.getDialogueArray() + if dialogueArray == None: + return + sfxIndex = None + if type == 'statementA' or type == 'statementB': + if length == 1: + sfxIndex = 0 + elif length == 2: + sfxIndex = 1 + elif length >= 3: + sfxIndex = 2 + elif type == 'question': + sfxIndex = 3 + elif type == 'exclamation': + sfxIndex = 4 + elif type == 'special': + sfxIndex = 5 + else: + notify.error('unrecognized dialogue type: ', type) + if sfxIndex != None and sfxIndex < len(dialogueArray) and dialogueArray[sfxIndex] != None: + soundSequence = Sequence(Wait(delay), SoundInterval(dialogueArray[sfxIndex], node=None, listenerNode=base.localAvatar, loop=0, volume=1.0)) + self.soundSequenceList.append(soundSequence) + soundSequence.start() + self.cleanUpSoundList() + return + + def cleanUpSoundList(self): + removeList = [] + for soundSequence in self.soundSequenceList: + if soundSequence.isStopped(): + removeList.append(soundSequence) + + for soundSequence in removeList: + self.soundSequenceList.remove(soundSequence) diff --git a/toontown/suit/DistributedSuitAI.py b/toontown/suit/DistributedSuitAI.py new file mode 100755 index 00000000..5fb0e900 --- /dev/null +++ b/toontown/suit/DistributedSuitAI.py @@ -0,0 +1,370 @@ +from otp.ai.AIBaseGlobal import * +from panda3d.core import * +from direct.distributed.ClockDelta import * +from otp.avatar import DistributedAvatarAI +import SuitTimings +from direct.task import Task +import SuitPlannerBase +import SuitBase +import SuitDialog +import SuitDNA +from libpandadna import * +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import SuitBattleGlobals +from toontown.building import FADoorCodes +import DistributedSuitBaseAI +from toontown.hood import ZoneUtil +from toontown.toon import NPCToons +import random + +class DistributedSuitAI(DistributedSuitBaseAI.DistributedSuitBaseAI): + SUIT_BUILDINGS = simbase.config.GetBool('want-suit-buildings', 1) + DEBUG_SUIT_POSITIONS = simbase.config.GetBool('debug-suit-positions', 0) + UPDATE_TIMESTAMP_INTERVAL = 180.0 + myId = 0 + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSuitAI') + + def __init__(self, air, suitPlanner): + DistributedSuitBaseAI.DistributedSuitBaseAI.__init__(self, air, suitPlanner) + self.bldgTrack = None + self.branchId = None + if suitPlanner: + self.branchId = suitPlanner.zoneId + self.pathEndpointStart = 0 + self.pathEndpointEnd = 0 + self.minPathLen = 0 + self.maxPathLen = 0 + self.pathPositionIndex = 0 + self.pathPositionTimestamp = 0.0 + self.pathState = 0 + self.currentLeg = 0 + self.legType = SuitLeg.TOff + self.flyInSuit = 0 + self.buildingSuit = 0 + self.attemptingTakeover = 0 + self.buildingDestination = None + self.buildingDestinationIsCogdo = False + + def delete(self): + del self.bldgTrack + del self.branchId + del self.buildingDestination + del self.buildingDestinationIsCogdo + + DistributedSuitBaseAI.DistributedSuitBaseAI.delete(self) + + def stopTasks(self): + taskMgr.remove(self.taskName('flyAwayNow')) + taskMgr.remove(self.taskName('danceNowFlyAwayLater')) + taskMgr.remove(self.taskName('move')) + + def pointInMyPath(self, point, elapsedTime): + if self.pathState != 1: + return 0 + then = globalClock.getFrameTime() + elapsedTime + elapsed = then - self.pathStartTime + if not self.sp: + pass + return self.legList.isPointInRange(point, elapsed - self.sp.PATH_COLLISION_BUFFER, elapsed + self.sp.PATH_COLLISION_BUFFER) + + def requestBattle(self, x, y, z, h, p, r): + toonId = self.air.getAvatarIdFromSender() + if self.air.doId2do.get(toonId) == None: + return + if self.pathState == 3: + pass + elif self.pathState != 1: + if self.notify.getDebug(): + self.notify.debug('requestBattle() - suit %s not on path' % self.getDoId()) + if self.pathState == 2 or self.pathState == 4: + self.b_setBrushOff(SuitDialog.getBrushOffIndex(self.getStyleName())) + self.d_denyBattle(toonId) + return + elif self.legType != SuitLeg.TWalk: + if self.notify.getDebug(): + self.notify.debug('requestBattle() - suit %s not in Bellicose' % self.getDoId()) + self.b_setBrushOff(SuitDialog.getBrushOffIndex(self.getStyleName())) + self.d_denyBattle(toonId) + return + self.confrontPos = Point3(x, y, z) + self.confrontHpr = Vec3(h, p, r) + if self.sp.requestBattle(self.zoneId, self, toonId): + if self.notify.getDebug(): + self.notify.debug('Suit %s requesting battle in zone %s' % (self.getDoId(), self.zoneId)) + else: + if self.notify.getDebug(): + self.notify.debug('requestBattle from suit %s - denied by battle manager' % self.getDoId()) + self.b_setBrushOff(SuitDialog.getBrushOffIndex(self.getStyleName())) + self.d_denyBattle(toonId) + return + + def getConfrontPosHpr(self): + return (self.confrontPos, self.confrontHpr) + + def flyAwayNow(self): + self.b_setPathState(2) + self.stopPathNow() + name = self.taskName('flyAwayNow') + taskMgr.remove(name) + taskMgr.doMethodLater(SuitTimings.toSky, self.finishFlyAwayNow, name) + + def danceNowFlyAwayLater(self): + self.b_setPathState(4) + self.stopPathNow() + name = self.taskName('danceNowFlyAwayLater') + taskMgr.remove(name) + taskMgr.doMethodLater(SuitTimings.victoryDance + SuitTimings.toSky, self.finishFlyAwayNow, name) + + def finishFlyAwayNow(self, task): + self.notify.debug('Suit %s finishFlyAwayNow' % self.doId) + self.requestRemoval() + return Task.done + + def d_setSPDoId(self, doId): + self.sendUpdate('setSPDoId', [doId]) + + def getSPDoId(self): + if self.sp: + return self.sp.getDoId() + else: + return 0 + + def releaseControl(self): + self.b_setPathState(0) + + def b_setPathEndpoints(self, start, end, minPathLen, maxPathLen): + self.setPathEndpoints(start, end, minPathLen, maxPathLen) + self.d_setPathEndpoints(start, end, minPathLen, maxPathLen) + + def d_setPathEndpoints(self, start, end, minPathLen, maxPathLen): + self.sendUpdate('setPathEndpoints', [start, + end, + minPathLen, + maxPathLen]) + + def setPathEndpoints(self, start, end, minPathLen, maxPathLen): + self.pathEndpointStart = start + self.pathEndpointEnd = end + self.minPathLen = minPathLen + self.maxPathLen = maxPathLen + + def getPathEndpoints(self): + return (self.pathEndpointStart, + self.pathEndpointEnd, + self.minPathLen, + self.maxPathLen) + + def b_setPathPosition(self, index, timestamp): + self.setPathPosition(index, timestamp) + self.d_setPathPosition(index, timestamp) + + def d_setPathPosition(self, index, timestamp): + self.notify.debug('Suit %s reaches point %s at time %0.2f' % (self.getDoId(), index, timestamp)) + self.sendUpdate('setPathPosition', [index, globalClockDelta.localToNetworkTime(timestamp)]) + + def setPathPosition(self, index, timestamp): + self.pathPositionIndex = index + self.pathPositionTimestamp = timestamp + + def getPathPosition(self): + return (self.pathPositionIndex, globalClockDelta.localToNetworkTime(self.pathPositionTimestamp)) + + def b_setPathState(self, state): + self.setPathState(state) + self.d_setPathState(state) + + def d_setPathState(self, state): + self.sendUpdate('setPathState', [state]) + + def setPathState(self, state): + if self.pathState != state: + self.pathState = state + if state == 0: + self.stopPathNow() + elif state == 1: + self.moveToNextLeg(None) + elif state == 2: + self.stopPathNow() + elif state == 3: + pass + elif state == 4: + self.stopPathNow() + else: + self.notify.error('Invalid state: ' + str(state)) + return + + def getPathState(self): + return self.pathState + + def d_debugSuitPosition(self, elapsed, currentLeg, x, y, timestamp): + timestamp = globalClockDelta.localToNetworkTime(timestamp) + self.sendUpdate('debugSuitPosition', [elapsed, + currentLeg, + x, + y, + timestamp]) + + def initializePath(self): + self.makeLegList() + if self.notify.getDebug(): + self.notify.debug('Leg list:') + print self.legList + idx1 = self.startPoint.getIndex() + idx2 = self.endPoint.getIndex() + self.pathStartTime = globalClock.getFrameTime() + self.setPathEndpoints(idx1, idx2, self.minPathLen, self.maxPathLen) + self.setPathPosition(0, self.pathStartTime) + self.pathState = 1 + self.currentLeg = 0 + self.zoneId = self.legList.getZoneId(0) + self.legType = self.legList.getType(0) + if self.notify.getDebug(): + self.notify.debug('creating suit in zone %s' % self.zoneId) + + def resync(self): + self.b_setPathPosition(self.currentLeg, self.pathStartTime + self.legList.getStartTime(self.currentLeg)) + + def moveToNextLeg(self, task): + now = globalClock.getFrameTime() + elapsed = now - self.pathStartTime + nextLeg = self.legList.getLegIndexAtTime(elapsed, self.currentLeg) + numLegs = self.legList.getNumLegs() + if self.currentLeg != nextLeg: + if nextLeg >= numLegs: + self.flyAwayNow() + return Task.done + self.currentLeg = nextLeg + self.__beginLegType(self.legList.getType(nextLeg)) + zoneId = self.legList.getZoneId(nextLeg) + if zoneId: + self.__enterZone(zoneId) + self.notify.debug('Suit %s reached leg %s of %s in zone %s.' % (self.getDoId(), + nextLeg, + numLegs - 1, + self.zoneId)) + if self.DEBUG_SUIT_POSITIONS: + leg = self.legList.getLeg(nextLeg) + pos = leg.getPosAtTime(elapsed - leg.getStartTime()) + self.d_debugSuitPosition(elapsed, nextLeg, pos[0], pos[1], now) + if now - self.pathPositionTimestamp > self.UPDATE_TIMESTAMP_INTERVAL: + self.resync() + if self.pathState != 1: + return Task.done + nextLeg += 1 + while nextLeg + 1 < numLegs and self.legList.getZoneId(nextLeg) == ZoneUtil.getCanonicalZoneId(self.zoneId) and self.legList.getType(nextLeg) == self.legType: + nextLeg += 1 + + if nextLeg < numLegs: + nextTime = self.legList.getStartTime(nextLeg) + delay = nextTime - elapsed + taskMgr.remove(self.taskName('move')) + taskMgr.doMethodLater(delay, self.moveToNextLeg, self.taskName('move')) + else: + if simbase.config.GetBool('want-cogbuildings', True): + self.startTakeOver() + self.requestRemoval() + return Task.done + + def stopPathNow(self): + taskMgr.remove(self.taskName('move')) + + def __enterZone(self, zoneId): + if zoneId != self.zoneId: + self.sp.zoneChange(self, self.zoneId, zoneId) + # Originally, we would call self.air.sendSetZoneMsg(). I think the + # following is a worthy replacement, however: + self.b_setLocation(simbase.air.districtId, zoneId) + self.zoneId = zoneId + if self.pathState == 1: + self.sp.checkForBattle(zoneId, self) + + def __beginLegType(self, legType): + self.legType = legType + if legType == SuitLeg.TWalkFromStreet: + self.checkBuildingState() + elif legType == SuitLeg.TToToonBuilding: + self.openToonDoor() + elif legType == SuitLeg.TToSuitBuilding: + self.openSuitDoor() + elif legType == SuitLeg.TToCogHQ: + self.openCogHQDoor(1) + elif legType == SuitLeg.TFromCogHQ: + self.openCogHQDoor(0) + + def resume(self): + self.notify.debug('Suit %s resume' % self.doId) + if self.currHP <= 0: + self.notify.debug('Suit %s dead after resume' % self.doId) + self.requestRemoval() + else: + self.danceNowFlyAwayLater() + + def prepareToJoinBattle(self): + self.b_setPathState(0) + + def interruptMove(self): + SuitBase.SuitBase.interruptMove(self) + + def checkBuildingState(self): + blockNumber = self.buildingDestination + if blockNumber == None: + return + building = self.sp.buildingMgr.getBuilding(blockNumber) + if self.attemptingTakeover: + if not building.isToonBlock(): + self.flyAwayNow() + return + if not hasattr(building, 'door'): + self.flyAwayNow() + return + building.door.setDoorLock(FADoorCodes.SUIT_APPROACHING) + elif not building.isSuitBlock(): + self.flyAwayNow() + return + + def openToonDoor(self): + blockNumber = self.buildingDestination + building = self.sp.buildingMgr.getBuilding(blockNumber) + if not building.isToonBlock(): + self.flyAwayNow() + return + if not hasattr(building, 'door'): + self.flyAwayNow() + return + building.door.requestSuitEnter(self.getDoId()) + + def openSuitDoor(self): + blockNumber = self.buildingDestination + building = self.sp.buildingMgr.getBuilding(blockNumber) + if not building.isSuitBlock(): + self.flyAwayNow() + return + + def openCogHQDoor(self, enter): + blockNumber = self.legList.getBlockNumber(self.currentLeg) + try: + door = self.sp.cogHQDoors[blockNumber] + except: + self.notify.error('No CogHQ door %s in zone %s' % (blockNumber, self.sp.zoneId)) + return + + if enter: + door.requestSuitEnter(self.getDoId()) + else: + door.requestSuitExit(self.getDoId()) + + def startTakeOver(self): + if not self.SUIT_BUILDINGS: + return + blockNumber = self.buildingDestination + if self.sp.buildingMgr is None: + return + if not self.sp.buildingMgr.isSuitBlock(blockNumber): + self.notify.debug('Suit %s taking over building %s in %s' % (self.getDoId(), blockNumber, self.zoneId)) + difficulty = self.getActualLevel() - 1 + + dept = SuitDNA.getSuitDept(self.dna.name) + if self.buildingDestinationIsCogdo: + self.sp.cogdoTakeOver(blockNumber, difficulty, self.buildingHeight, dept) + else: + self.sp.suitTakeOver(blockNumber, dept, difficulty, self.buildingHeight) diff --git a/toontown/suit/DistributedSuitBase.py b/toontown/suit/DistributedSuitBase.py new file mode 100755 index 00000000..4006cfb4 --- /dev/null +++ b/toontown/suit/DistributedSuitBase.py @@ -0,0 +1,427 @@ +from panda3d.core import * +from direct.controls.ControlManager import CollisionHandlerRayStart +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.task import Task +from otp.avatar import DistributedAvatar +from otp.otpbase import OTPGlobals +from otp.nametag.NametagConstants import * +from otp.nametag import NametagGlobals +from toontown.battle import BattleProps +from toontown.toonbase import TTLocalizer, ToontownGlobals +import Suit, SuitBase, SuitDialog, SuitTimings +import random + +class DistributedSuitBase(DistributedAvatar.DistributedAvatar, Suit.Suit, SuitBase.SuitBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSuitBase') + + def __init__(self, cr): + try: + self.DistributedSuitBase_initialized + return + except: + self.DistributedSuitBase_initialized = 1 + + DistributedAvatar.DistributedAvatar.__init__(self, cr) + Suit.Suit.__init__(self) + SuitBase.SuitBase.__init__(self) + self.activeShadow = 0 + self.virtual = 0 + self.battleDetectName = None + self.cRay = None + self.cRayNode = None + self.cRayNodePath = None + self.cRayBitMask = None + self.lifter = None + self.cTrav = None + self.sp = None + self.fsm = None + self.prop = None + self.propInSound = None + self.propOutSound = None + self.reparentTo(hidden) + self.loop('neutral') + self.skeleRevives = 0 + self.maxSkeleRevives = 0 + self.sillySurgeText = False + self.interactivePropTrackBonus = -1 + + def setInteractivePropTrackBonus(self, trackBonus): + self.interactivePropTrackBonus = trackBonus + + def setVirtual(self, virtual): + pass + + def getVirtual(self): + return 0 + + def setSkeleRevives(self, num): + if num == None: + num = 0 + self.skeleRevives = num + if num > self.maxSkeleRevives: + self.maxSkeleRevives = num + if self.getSkeleRevives() > 0: + nameInfo = TTLocalizer.SuitBaseNameWithLevel % {'name': self.name, + 'dept': self.getStyleDept(), + 'level': '%s%s' % (self.getActualLevel(), TTLocalizer.SkeleRevivePostFix % (self.skeleRevives + 1))} + self.setDisplayName(nameInfo) + else: + nameInfo = TTLocalizer.SuitBaseNameWithLevel % {'name': self.name, + 'dept': self.getStyleDept(), + 'level': self.getActualLevel()} + self.setDisplayName(nameInfo) + + def getSkeleRevives(self): + return self.skeleRevives + + def getMaxSkeleRevives(self): + return self.maxSkeleRevives + + def generate(self): + DistributedAvatar.DistributedAvatar.generate(self) + + def disable(self): + self.notify.debug('DistributedSuit %d: disabling' % self.getDoId()) + self.ignoreAll() + self.__removeCollisionData() + self.cleanupLoseActor() + self.stop() + taskMgr.remove(self.uniqueName('blink-task')) + DistributedAvatar.DistributedAvatar.disable(self) + + def delete(self): + try: + self.DistributedSuitBase_deleted + except: + self.DistributedSuitBase_deleted = 1 + self.notify.debug('DistributedSuit %d: deleting' % self.getDoId()) + del self.dna + del self.sp + DistributedAvatar.DistributedAvatar.delete(self) + Suit.Suit.delete(self) + SuitBase.SuitBase.delete(self) + + def setDNAString(self, dnaString): + Suit.Suit.setDNAString(self, dnaString) + + def setDNA(self, dna): + Suit.Suit.setDNA(self, dna) + + def getHP(self): + return self.currHP + + def getMaxHP(self): + return self.maxHP + + def setHP(self, hp): + if hp > self.maxHP: + self.currHP = self.maxHP + else: + self.currHP = hp + return None + + def getDialogueArray(self, *args): + return Suit.Suit.getDialogueArray(self, *args) + + def __removeCollisionData(self): + self.enableRaycast(0) + self.cRay = None + self.cRayNode = None + self.cRayNodePath = None + self.lifter = None + self.cTrav = None + + def setHeight(self, height): + Suit.Suit.setHeight(self, height) + + def getRadius(self): + return Suit.Suit.getRadius(self) + + def setLevelDist(self, level): + if self.notify.getDebug(): + self.notify.debug('Got level %d from server for suit %d' % (level, self.getDoId())) + self.setLevel(level) + + def attachPropeller(self): + if self.prop == None: + self.prop = BattleProps.globalPropPool.getProp('propeller') + if self.propInSound == None: + self.propInSound = base.loadSfx('phase_5/audio/sfx/ENC_propeller_in.ogg') + if self.propOutSound == None: + self.propOutSound = base.loadSfx('phase_5/audio/sfx/ENC_propeller_out.ogg') + if base.config.GetBool('want-new-cogs', 0): + head = self.find('**/to_head') + if head.isEmpty(): + head = self.find('**/joint_head') + else: + head = self.find('**/joint_head') + self.prop.reparentTo(head) + + def detachPropeller(self): + if self.prop: + self.prop.cleanup() + self.prop.removeNode() + self.prop = None + if self.propInSound: + self.propInSound = None + if self.propOutSound: + self.propOutSound = None + + def beginSupaFlyMove(self, pos, moveIn, trackName, walkAfterLanding=True): + skyPos = Point3(pos) + if moveIn: + skyPos.setZ(pos.getZ() + SuitTimings.fromSky * ToontownGlobals.SuitWalkSpeed) + else: + skyPos.setZ(pos.getZ() + SuitTimings.toSky * ToontownGlobals.SuitWalkSpeed) + groundF = 28 + dur = self.getDuration('landing') + fr = self.getFrameRate('landing') + if fr: + animTimeInAir = groundF / fr + else: + animTimeInAir = groundF + impactLength = dur - animTimeInAir + timeTillLanding = SuitTimings.fromSky - impactLength + waitTime = timeTillLanding - animTimeInAir + if self.prop == None: + self.prop = BattleProps.globalPropPool.getProp('propeller') + propDur = self.prop.getDuration('propeller') + lastSpinFrame = 8 + fr = self.prop.getFrameRate('propeller') + spinTime = lastSpinFrame / fr + openTime = (lastSpinFrame + 1) / fr + if moveIn: + lerpPosTrack = Sequence(self.posInterval(timeTillLanding, pos, startPos=skyPos), Wait(impactLength)) + shadowScale = self.dropShadow.getScale() + shadowTrack = Sequence(Func(self.dropShadow.reparentTo, render), Func(self.dropShadow.setPos, pos), self.dropShadow.scaleInterval(timeTillLanding, self.scale, startScale=Vec3(0.01, 0.01, 1.0)), Func(self.dropShadow.reparentTo, self.getShadowJoint()), Func(self.dropShadow.setPos, 0, 0, 0), Func(self.dropShadow.setScale, shadowScale)) + fadeInTrack = Sequence(Func(self.setTransparency, 1), self.colorScaleInterval(1, colorScale=VBase4(1, 1, 1, 1), startColorScale=VBase4(1, 1, 1, 0)), Func(self.clearColorScale), Func(self.clearTransparency)) + animTrack = Sequence(Func(self.pose, 'landing', 0), Wait(waitTime), ActorInterval(self, 'landing', duration=dur)) + if walkAfterLanding: + animTrack.append(Func(self.loop, 'walk')) + self.attachPropeller() + propTrack = Parallel(SoundInterval(self.propInSound, duration=waitTime + dur, node=self), Sequence(ActorInterval(self.prop, 'propeller', constrainedLoop=1, duration=waitTime + spinTime, startTime=0.0, endTime=spinTime), ActorInterval(self.prop, 'propeller', duration=propDur - openTime, startTime=openTime), Func(self.detachPropeller))) + return Parallel(lerpPosTrack, shadowTrack, fadeInTrack, animTrack, propTrack, name=self.taskName('trackName')) + else: + lerpPosTrack = Sequence(Wait(impactLength), LerpPosInterval(self, timeTillLanding, skyPos, startPos=pos)) + shadowTrack = Sequence(Func(self.dropShadow.reparentTo, render), Func(self.dropShadow.setPos, pos), self.dropShadow.scaleInterval(timeTillLanding, Vec3(0.01, 0.01, 1.0), startScale=self.scale), Func(self.dropShadow.reparentTo, self.getShadowJoint()), Func(self.dropShadow.setPos, 0, 0, 0)) + fadeOutTrack = Sequence(Func(self.setTransparency, 1), self.colorScaleInterval(1, colorScale=VBase4(1, 1, 1, 0), startColorScale=VBase4(1, 1, 1, 1)), Func(self.clearColorScale), Func(self.clearTransparency), Func(self.reparentTo, hidden)) + actInt = ActorInterval(self, 'landing', loop=0, startTime=dur, endTime=0.0) + self.attachPropeller() + self.prop.hide() + propTrack = Parallel(SoundInterval(self.propOutSound, duration=waitTime + dur, node=self), Sequence(Func(self.prop.show), ActorInterval(self.prop, 'propeller', endTime=openTime, startTime=propDur), ActorInterval(self.prop, 'propeller', constrainedLoop=1, duration=propDur - openTime, startTime=spinTime, endTime=0.0), Func(self.detachPropeller))) + return Parallel(ParallelEndTogether(lerpPosTrack, shadowTrack, fadeOutTrack), actInt, propTrack, name=self.taskName('trackName')) + + def enableBattleDetect(self, name, handler): + if self.collTube: + self.battleDetectName = self.taskName(name) + self.collNode = CollisionNode(self.battleDetectName) + self.collNode.addSolid(self.collTube) + self.collNodePath = self.attachNewNode(self.collNode) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.accept('enter' + self.battleDetectName, handler) + return Task.done + + def disableBattleDetect(self): + if self.battleDetectName: + self.ignore('enter' + self.battleDetectName) + self.battleDetectName = None + if self.collNodePath: + self.collNodePath.removeNode() + self.collNodePath = None + + def enableRaycast(self, enable = 1): + if not self.cTrav or not hasattr(self, 'cRayNode') or not self.cRayNode: + return + self.cTrav.removeCollider(self.cRayNodePath) + if enable: + if self.notify.getDebug(): + self.notify.debug('enabling raycast') + self.cTrav.addCollider(self.cRayNodePath, self.lifter) + elif self.notify.getDebug(): + self.notify.debug('disabling raycast') + + def b_setBrushOff(self, index): + self.setBrushOff(index) + self.d_setBrushOff(index) + + def d_setBrushOff(self, index): + self.sendUpdate('setBrushOff', [index]) + + def setBrushOff(self, index): + self.setChatAbsolute(SuitDialog.getBrushOffText(self.getStyleName(), index), CFSpeech | CFTimeout) + + def initializeBodyCollisions(self, collIdStr): + DistributedAvatar.DistributedAvatar.initializeBodyCollisions(self, collIdStr) + if not self.ghostMode: + self.collNode.setCollideMask(self.collNode.getIntoCollideMask() | ToontownGlobals.PieBitmask) + self.cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0) + self.cRayNode = CollisionNode(self.taskName('cRay')) + self.cRayNode.addSolid(self.cRay) + self.cRayNodePath = self.attachNewNode(self.cRayNode) + self.cRayNodePath.hide() + self.cRayBitMask = ToontownGlobals.FloorBitmask + self.cRayNode.setFromCollideMask(self.cRayBitMask) + self.cRayNode.setIntoCollideMask(BitMask32.allOff()) + self.lifter = CollisionHandlerFloor() + self.lifter.setOffset(ToontownGlobals.FloorOffset) + self.lifter.setReach(6.0) + self.lifter.setMaxVelocity(8.0) + self.lifter.addCollider(self.cRayNodePath, self) + self.cTrav = base.cTrav + + def disableBodyCollisions(self): + self.disableBattleDetect() + self.enableRaycast(0) + if self.cRayNodePath: + self.cRayNodePath.removeNode() + del self.cRayNode + del self.cRay + del self.lifter + + def denyBattle(self): + self.notify.debug('denyBattle()') + place = self.cr.playGame.getPlace() + if place.fsm.getCurrentState().getName() == 'WaitForBattle': + place.setState('walk') + self.resumePath(self.pathState) + + def makePathTrack(self, nodePath, posPoints, velocity, name): + track = Sequence(name=name) + nodePath.setPos(posPoints[0]) + for pointIndex in xrange(len(posPoints) - 1): + startPoint = posPoints[pointIndex] + endPoint = posPoints[pointIndex + 1] + track.append(Func(nodePath.headsUp, endPoint[0], endPoint[1], endPoint[2])) + distance = Vec3(endPoint - startPoint).length() + duration = distance / velocity + track.append(LerpPosInterval(nodePath, duration=duration, pos=Point3(endPoint), startPos=Point3(startPoint))) + return track + + def setState(self, state): + if self.fsm == None: + return 0 + if self.fsm.getCurrentState().getName() == state: + return 0 + return self.fsm.request(state) + + def subclassManagesParent(self): + return 0 + + def enterOff(self, *args): + self.hideNametag3d() + self.hideNametag2d() + if not self.subclassManagesParent(): + self.setParent(ToontownGlobals.SPHidden) + + def exitOff(self): + if not self.subclassManagesParent(): + self.setParent(ToontownGlobals.SPRender) + self.showNametag3d() + self.showNametag2d() + self.loop('neutral', 0) + + def enterBattle(self): + self.loop('neutral', 0) + self.disableBattleDetect() + self.corpMedallion.hide() + self.healthBar.geom.show() + if self.currHP < self.maxHP: + self.updateHealthBar(0, 1) + + def exitBattle(self): + self.healthBar.geom.hide() + self.corpMedallion.show() + self.currHP = self.maxHP + self.interactivePropTrackBonus = -1 + + def enterWaitForBattle(self): + self.loop('neutral', 0) + + def exitWaitForBattle(self): + pass + + def setSkelecog(self, flag): + SuitBase.SuitBase.setSkelecog(self, flag) + if flag: + Suit.Suit.makeSkeleton(self) + + def setWaiter(self, flag): + SuitBase.SuitBase.setWaiter(self, flag) + if flag: + Suit.Suit.makeWaiter(self) + + def showHpText(self, number, bonus = 0, scale = 1, attackTrack = -1): + if self.HpTextEnabled and not self.ghostMode: + if number != 0: + if self.hpText: + self.hideHpText() + + self.HpTextGenerator.setFont(OTPGlobals.getSignFont()) + + if number < 0: + self.HpTextGenerator.setText(str(number)) + + if config.GetBool('silly-surge-text', True) and random.randrange(0, 100) < config.GetInt('silly-surge-chance', 10): + self.sillySurgeText = True + absNumber = int(abs(number) / 10) + + if len(TTLocalizer.SillySurgeTerms) > absNumber: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.SillySurgeTerms[absNumber]) + else: + self.HpTextGenerator.setText(str(number) + '\n' + random.choice(TTLocalizer.SillySurgeTerms)) + + if self.interactivePropTrackBonus > -1 and self.interactivePropTrackBonus == attackTrack: + self.sillySurgeText = True + + if attackTrack in TTLocalizer.InteractivePropTrackBonusTerms: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.InteractivePropTrackBonusTerms[attackTrack]) + else: + self.HpTextGenerator.setText('+' + str(number)) + + self.HpTextGenerator.clearShadow() + self.HpTextGenerator.setAlign(TextNode.ACenter) + + if bonus == 1: + color = [1, 1, 0, 1] + elif bonus == 2: + color = [1, 0.5, 0, 1] + elif number < 0: + color = [0.9, 0, 0, 1] + + if self.interactivePropTrackBonus > -1 and self.interactivePropTrackBonus == attackTrack: + color = [0, 0, 1, 1] + else: + color = [0, 0.9, 0, 1] + + self.HpTextGenerator.setTextColor(*color) + self.hpTextNode = self.HpTextGenerator.generate() + self.hpText = self.attachNewNode(self.hpTextNode) + self.hpText.setScale(scale) + self.hpText.setBillboardPointEye() + self.hpText.setBin('fixed', 100) + + if self.sillySurgeText: + self.nametag3d.setDepthTest(0) + self.nametag3d.setBin('fixed', 99) + + self.hpText.setPos(0, 0, self.height / 2) + color[3] = 0 + Sequence(self.hpText.posInterval(1.0, Point3(0, 0, self.height + 1.5), blendType='easeOut'), Wait(0.85), self.hpText.colorInterval(0.1, Vec4(*color), 0.1), Func(self.hideHpText)).start() + + def hideHpText(self): + DistributedAvatar.DistributedAvatar.hideHpText(self) + + if self.sillySurgeText: + self.nametag3d.clearDepthTest() + self.nametag3d.clearBin() + self.sillySurgeText = False + + def getAvIdName(self): + try: + level = self.getActualLevel() + except: + level = '???' + + return '%s\n%s\nLevel %s' % (self.getName(), self.doId, level) diff --git a/toontown/suit/DistributedSuitBaseAI.py b/toontown/suit/DistributedSuitBaseAI.py new file mode 100755 index 00000000..2ed9526e --- /dev/null +++ b/toontown/suit/DistributedSuitBaseAI.py @@ -0,0 +1,198 @@ +from otp.ai.AIBaseGlobal import * +from otp.avatar import DistributedAvatarAI +import SuitPlannerBase +import SuitBase +import SuitDNA +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import SuitBattleGlobals + +class DistributedSuitBaseAI(DistributedAvatarAI.DistributedAvatarAI, SuitBase.SuitBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSuitBaseAI') + + def __init__(self, air, suitPlanner): + DistributedAvatarAI.DistributedAvatarAI.__init__(self, air) + SuitBase.SuitBase.__init__(self) + self.sp = suitPlanner + self.maxHP = 10 + self.currHP = 10 + self.zoneId = 0 + self.dna = SuitDNA.SuitDNA() + self.virtual = 0 + self.waiter = 0 + self.skeleRevives = 0 + self.maxSkeleRevives = 0 + self.reviveFlag = 0 + self.buildingHeight = None + return + + def generate(self): + DistributedAvatarAI.DistributedAvatarAI.generate(self) + + def delete(self): + self.sp = None + del self.dna + + DistributedAvatarAI.DistributedAvatarAI.delete(self) + SuitBase.SuitBase.delete(self) + + def requestRemoval(self): + if self.sp != None: + self.sp.removeSuit(self) + else: + self.requestDelete() + return + + def setLevel(self, lvl = None): + attributes = SuitBattleGlobals.SuitAttributes[self.dna.name] + if lvl: + self.level = lvl - attributes['level'] - 1 + else: + self.level = SuitBattleGlobals.pickFromFreqList(attributes['freq']) + self.notify.debug('Assigning level ' + str(lvl)) + if hasattr(self, 'doId'): + self.d_setLevelDist(self.level) + hp = attributes['hp'][self.level] + self.maxHP = hp + self.currHP = hp + + def getLevelDist(self): + return self.getLevel() + + def d_setLevelDist(self, level): + self.sendUpdate('setLevelDist', [level]) + + def setupSuitDNA(self, level, type, track): + dna = SuitDNA.SuitDNA() + dna.newSuitRandom(type, track) + self.dna = dna + self.track = track + self.setLevel(level) + return None + + def getDNAString(self): + if self.dna: + return self.dna.makeNetString() + else: + self.notify.debug('No dna has been created for suit %d!' % self.getDoId()) + return '' + + def b_setBrushOff(self, index): + self.setBrushOff(index) + self.d_setBrushOff(index) + return None + + def d_setBrushOff(self, index): + self.sendUpdate('setBrushOff', [index]) + + def setBrushOff(self, index): + pass + + def d_denyBattle(self, toonId): + self.sendUpdateToAvatarId(toonId, 'denyBattle', []) + + def b_setSkeleRevives(self, num): + if num == None: + num = 0 + self.setSkeleRevives(num) + self.d_setSkeleRevives(self.getSkeleRevives()) + return + + def d_setSkeleRevives(self, num): + self.sendUpdate('setSkeleRevives', [num]) + + def getSkeleRevives(self): + return self.skeleRevives + + def setSkeleRevives(self, num): + if num == None: + num = 0 + self.skeleRevives = num + if num > self.maxSkeleRevives: + self.maxSkeleRevives = num + return + + def getMaxSkeleRevives(self): + return self.maxSkeleRevives + + def useSkeleRevive(self): + self.skeleRevives -= 1 + self.currHP = self.maxHP + self.reviveFlag = 1 + + def reviveCheckAndClear(self): + returnValue = 0 + if self.reviveFlag == 1: + returnValue = 1 + self.reviveFlag = 0 + return returnValue + + def getHP(self): + return self.currHP + + def setHP(self, hp): + if hp > self.maxHP: + self.currHP = self.maxHP + else: + self.currHP = hp + return None + + def b_setHP(self, hp): + self.setHP(hp) + self.d_setHP(hp) + + def d_setHP(self, hp): + self.sendUpdate('setHP', [hp]) + + def releaseControl(self): + return None + + def getDeathEvent(self): + return 'cogDead-%s' % self.doId + + def resume(self): + self.notify.debug('resume, hp=%s' % self.currHP) + if self.currHP <= 0: + messenger.send(self.getDeathEvent()) + self.requestRemoval() + return None + + def prepareToJoinBattle(self): + pass + + def b_setSkelecog(self, flag): + self.setSkelecog(flag) + self.d_setSkelecog(flag) + + def setSkelecog(self, flag): + SuitBase.SuitBase.setSkelecog(self, flag) + + def d_setSkelecog(self, flag): + self.sendUpdate('setSkelecog', [flag]) + + def isForeman(self): + return 0 + + def isSupervisor(self): + return 0 + + def setVirtual(self, virtual): + pass + + def getVirtual(self): + return 0 + + def isVirtual(self): + return self.getVirtual() + + def setWaiter(self, flag): + SuitBase.SuitBase.setWaiter(self, flag) + + def d_setWaiter(self, flag): + self.sendUpdate('setWaiter', [flag]) + + def b_setWaiter(self, flag): + self.setWaiter(flag) + self.d_setWaiter(flag) + + def getWaiter(self): + return self.waiter diff --git a/toontown/suit/DistributedSuitPlanner.py b/toontown/suit/DistributedSuitPlanner.py new file mode 100755 index 00000000..4d26de06 --- /dev/null +++ b/toontown/suit/DistributedSuitPlanner.py @@ -0,0 +1,187 @@ +import SuitPlannerBase +from direct.distributed import DistributedObject +from otp.ai.MagicWordGlobal import * +from panda3d.core import * +from toontown.dna.DNAParser import DNASuitPoint +from toontown.toonbase import ToontownGlobals + + +class DistributedSuitPlanner(DistributedObject.DistributedObject, SuitPlannerBase.SuitPlannerBase): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + SuitPlannerBase.SuitPlannerBase.__init__(self) + self.suitList = [] + self.buildingList = [0, 0, 0, 0] + self.pathViz = None + self.debugText = {} + + def generate(self): + self.notify.info('DistributedSuitPlanner %d: generating' % self.getDoId()) + DistributedObject.DistributedObject.generate(self) + base.cr.currSuitPlanner = self + + def disable(self): + self.notify.info('DistributedSuitPlanner %d: disabling' % self.getDoId()) + self.hidePaths() + DistributedObject.DistributedObject.disable(self) + base.cr.currSuitPlanner = None + + def d_suitListQuery(self): + self.sendUpdate('suitListQuery') + + def suitListResponse(self, suitList): + self.suitList = suitList + messenger.send('suitListResponse') + + def d_buildingListQuery(self): + self.sendUpdate('buildingListQuery') + + def buildingListResponse(self, buildingList): + self.buildingList = buildingList + messenger.send('buildingListResponse') + + def hidePaths(self): + if self.pathViz: + self.pathViz.detachNode() + self.pathViz = None + + def showPaths(self): + self.hidePaths() + vizNode = GeomNode(self.uniqueName('PathViz')) + lines = LineSegs() + self.pathViz = render.attachNewNode(vizNode) + points = self.frontdoorPointList + self.sidedoorPointList + self.cogHQDoorPointList + self.streetPointList + while len(points) > 0: + self.__doShowPoints(vizNode, lines, None, points) + cnode = CollisionNode('battleCells') + cnode.setCollideMask(BitMask32.allOff()) + for zoneId, cellPos in self.battlePosDict.items(): + cnode.addSolid(CollisionSphere(LPoint3f(cellPos), 9)) + text = '%s' % zoneId + self.__makePathVizText(text, cellPos[0], cellPos[1], cellPos[2] + 9, (1, 1, 1, 1)) + self.pathViz.attachNewNode(cnode).show() + + def __doShowPoints(self, vizNode, lines, p, points): + if p == None: + pi = len(points) - 1 + if pi < 0: + return + p = points[pi] + del points[pi] + else: + if p not in points: + return + pi = points.index(p) + del points[pi] + text = '%s' % p.getIndex() + pos = p.getPos() + if p.getPointType() == DNASuitPoint.FRONT_DOOR_POINT: + color = (1, 0, 0, 1) + elif p.getPointType() == DNASuitPoint.SIDE_DOOR_POINT: + color = (0, 0, 1, 1) + elif p.getPointType() == DNASuitPoint.COGHQ_IN_POINT: + color = (0, 0, 0, 1) + elif p.getPointType() == DNASuitPoint.COGHQ_OUT_POINT: + color = (0.5, 0.5, 0.5, 1) + else: + color = (0, 1, 0, 1) + self.__makePathVizText(text, pos[0], pos[1], pos[2], color, i=p.getIndex()) + cs = CollisionSphere(LPoint3f(pos), 3) + cs.setTangible(0) + triggerName = 'suitPoint-' + str(p.getIndex()) + cn = CollisionNode(triggerName) + cn.addSolid(cs) + cn.setIntoCollideMask(ToontownGlobals.WallBitmask) + base.accept('enter' + triggerName, self.__showEdges, [p.getIndex()]) + base.accept('exit' + triggerName, self.__hideEdges, [p.getIndex()]) + self.pathViz.attachNewNode(cn) + adjacent = self.dnaStore.getAdjacentPoints(p) + numPoints = adjacent.getNumPoints() + for i in xrange(numPoints): + qi = adjacent.getPointIndex(i) + q = self.dnaStore.getSuitPointWithIndex(qi) + pp = p.getPos() + qp = q.getPos() + v = Vec3(qp - pp) + v.normalize() + c = v.cross(Vec3.up()) + p1a = pp + v * 2 + c * 0.5 + p1b = pp + v * 3 + p1c = pp + v * 2 - c * 0.5 + lines.reset() + lines.moveTo(pp) + lines.drawTo(qp) + lines.moveTo(p1a) + lines.drawTo(p1b) + lines.drawTo(p1c) + lines.create(vizNode, 0) + self.__doShowPoints(vizNode, lines, q, points) + + def __makePathVizText(self, text, x, y, z, color, i=-1): + if not hasattr(self, 'debugTextNode'): + self.debugTextNode = TextNode('debugTextNode') + self.debugTextNode.setAlign(TextNode.ACenter) + self.debugTextNode.setFont(ToontownGlobals.getSignFont()) + self.debugTextNode.setTextColor(*color) + self.debugTextNode.setText(text) + np = self.pathViz.attachNewNode(self.debugTextNode.generate()) + np.setPos(x, y, z + 1) + np.setScale(1.0) + np.setBillboardPointEye(2) + np.node().setAttrib(TransparencyAttrib.make(TransparencyAttrib.MDual), 2) + if i >= 0: + self.debugText[i] = np + + def __showEdges(self, i, collisionEntry): + highlightedPoints = [i] + if i in self.dnaStore.suitEdges: + edges = self.dnaStore.suitEdges[i] + for edge in edges: + endPoint = edge.getEndPoint() + highlightedPoints.append(endPoint.getIndex()) + for i in highlightedPoints: + p = self.dnaStore.getSuitPointWithIndex(i) + pos = p.getPos() + self.debugText[i].removeNode() + self.__makePathVizText(str(p.getIndex()), pos[0], pos[1], pos[2], (0.95, 1, 0, 1), i=p.getIndex()) + + def __hideEdges(self, i, collisionEntry): + highlightedPoints = [i] + if i in self.dnaStore.suitEdges: + edges = self.dnaStore.suitEdges[i] + for edge in edges: + endPoint = edge.getEndPoint() + highlightedPoints.append(endPoint.getIndex()) + for i in highlightedPoints: + p = self.dnaStore.getSuitPointWithIndex(i) + pos = p.getPos() + self.debugText[i].removeNode() + if p.getPointType() == DNASuitPoint.FRONT_DOOR_POINT: + color = (1, 0, 0, 1) + elif p.getPointType() == DNASuitPoint.SIDE_DOOR_POINT: + color = (0, 0, 1, 1) + elif p.getPointType() == DNASuitPoint.COGHQ_IN_POINT: + color = (0, 0, 0, 1) + elif p.getPointType() == DNASuitPoint.COGHQ_OUT_POINT: + color = (0.5, 0.5, 0.5, 1) + else: + color = (0, 1, 0, 1) + self.__makePathVizText(str(p.getIndex()), pos[0], pos[1], pos[2], color, i=p.getIndex()) + + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def suitPaths(): + response = "Couldn't toggle suit path visualization." + for do in base.cr.doId2do.values(): + if not isinstance(do, DistributedSuitPlanner): + continue + if getattr(do, '_showPaths', False): + do.hidePaths() + do._showPaths = False + response = 'Suit paths are not being visualized.' + else: + do.showPaths() + do._showPaths = True + response = 'Suit paths are being visualized.' + return response diff --git a/toontown/suit/DistributedSuitPlannerAI.py b/toontown/suit/DistributedSuitPlannerAI.py new file mode 100755 index 00000000..5ae0d669 --- /dev/null +++ b/toontown/suit/DistributedSuitPlannerAI.py @@ -0,0 +1,970 @@ +from direct.directnotify.DirectNotifyGlobal import * +from direct.distributed import DistributedObjectAI +from direct.task import Task +import random + +import DistributedSuitAI +import SuitDNA +import SuitPlannerBase +import SuitTimings +from otp.ai.AIBaseGlobal import * +from toontown.battle import BattleManagerAI +from toontown.battle import SuitBattleGlobals +from toontown.building import HQBuildingAI +from toontown.building import SuitBuildingGlobals +from toontown.dna.DNAParser import DNASuitPoint +from toontown.hood import ZoneUtil +from toontown.suit.SuitInvasionGlobals import IFSkelecog, IFWaiter, IFV2 +from libpandadna import * +from toontown.toon import NPCToons +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import ToontownGlobals + + +ALLOWED_FO_TRACKS = 's' +if config.GetBool('want-lawbot-cogdo', True): + ALLOWED_FO_TRACKS += 'l' +if config.GetBool('want-cashbot-cogdo', False): + ALLOWED_FO_TRACKS += 'c' +if config.GetBool('want-bossbot-cogdo', False): + ALLOWED_FO_TRACKS += 'b' +if config.GetBool('want-omni-cogdo', False): + ALLOWED_FO_TRACKS += 'slcb' + +DEFAULT_COGDO_RATIO = .5 + +class DistributedSuitPlannerAI(DistributedObjectAI.DistributedObjectAI, SuitPlannerBase.SuitPlannerBase): + notify = directNotify.newCategory('DistributedSuitPlannerAI') + CogdoPopFactor = config.GetFloat('cogdo-pop-factor', 1.5) + CogdoRatio = min(1.0, max(0.0, config.GetFloat('cogdo-ratio', DEFAULT_COGDO_RATIO))) + MAX_SUIT_TYPES = 6 + POP_UPKEEP_DELAY = 10 + POP_ADJUST_DELAY = 300 + PATH_COLLISION_BUFFER = 5 + TOTAL_MAX_SUITS = 50 + MIN_PATH_LEN = 40 + MAX_PATH_LEN = 300 + MIN_TAKEOVER_PATH_LEN = 2 + SUITS_ENTER_BUILDINGS = 1 + SUIT_BUILDING_NUM_SUITS = 1.5 + SUIT_BUILDING_TIMEOUT = [ + None, None, None, None, None, None, + 72, 60, 48, 36, 24, 12, 6, 3, 1, 0.5 + ] + TOTAL_SUIT_BUILDING_PCT = 18 * CogdoPopFactor + BUILDING_HEIGHT_DISTRIBUTION = [14, 18, 25, 23, 20] + defaultSuitName = simbase.config.GetString('suit-type', 'random') + if defaultSuitName == 'random': + defaultSuitName = None + + def __init__(self, air, zoneId): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + SuitPlannerBase.SuitPlannerBase.__init__(self) + self.air = air + self.zoneId = zoneId + self.canonicalZoneId = ZoneUtil.getCanonicalZoneId(zoneId) + if simbase.air.wantCogdominiums: + if not hasattr(self.__class__, 'CogdoPopAdjusted'): + self.__class__.CogdoPopAdjusted = True + for index in xrange(len(self.SuitHoodInfo)): + SuitBuildingGlobals.buildingMinMax[self.zoneId][0] = int(0.5 + self.CogdoPopFactor * SuitBuildingGlobals.buildingMinMax[self.zoneId][0]) + SuitBuildingGlobals.buildingMinMax[self.zoneId][1] = int(0.5 + self.CogdoPopFactor * SuitBuildingGlobals.buildingMinMax[self.zoneId][1]) + self.hoodInfoIdx = -1 + for index in xrange(len(self.SuitHoodInfo)): + currHoodInfo = self.SuitHoodInfo[index] + if currHoodInfo[self.SUIT_HOOD_INFO_ZONE] == self.canonicalZoneId: + self.hoodInfoIdx = index + self.currDesired = None + self.baseNumSuits = ( + self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_MIN] + + self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_MAX]) / 2 + self.targetNumSuitBuildings = SuitBuildingGlobals.buildingMinMax[self.zoneId][0] + self.pendingBuildingTracks = [] + self.pendingBuildingHeights = [] + self.suitList = [] + self.numFlyInSuits = 0 + self.numBuildingSuits = 0 + self.numAttemptingTakeover = 0 + self.zoneInfo = {} + self.zoneIdToPointMap = None + self.cogHQDoors = [] + self.battleList = [] + self.battleMgr = BattleManagerAI.BattleManagerAI(self.air) + self.setupDNA() + if self.notify.getDebug(): + self.notify.debug('Creating a building manager AI in zone' + str(self.zoneId)) + self.buildingMgr = self.air.buildingManagers.get(self.zoneId) + if self.buildingMgr: + (blocks, hqBlocks, gagshopBlocks, petshopBlocks, kartshopBlocks) = self.buildingMgr.getDNABlockLists() + for currBlock in blocks: + bldg = self.buildingMgr.getBuilding(currBlock) + bldg.setSuitPlannerExt(self) + self.dnaStore.resetBlockNumbers() + self.initBuildingsAndPoints() + numSuits = simbase.config.GetInt('suit-count', -1) + if numSuits >= 0: + self.currDesired = numSuits + suitHood = simbase.config.GetInt('suits-only-in-hood', -1) + if suitHood >= 0: + if self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_ZONE] != suitHood: + self.currDesired = 0 + self.suitCountAdjust = 0 + + def cleanup(self): + taskMgr.remove(self.taskName('sptUpkeepPopulation')) + taskMgr.remove(self.taskName('sptAdjustPopulation')) + for suit in self.suitList: + suit.stopTasks() + if suit.isGenerated(): + self.zoneChange(suit, suit.zoneId) + suit.requestDelete() + self.suitList = [] + self.numFlyInSuits = 0 + self.numBuildingSuits = 0 + self.numAttemptingTakeover = 0 + + def delete(self): + self.cleanup() + DistributedObjectAI.DistributedObjectAI.delete(self) + SuitPlannerBase.SuitPlannerBase.delete(self) + + def initBuildingsAndPoints(self): + if not self.buildingMgr: + return + if self.notify.getDebug(): + self.notify.debug('Initializing building points') + self.buildingFrontDoors = {} + self.buildingSideDoors = {} + for p in self.frontdoorPointList: + blockNumber = p.getLandmarkBuildingIndex() + if blockNumber < 0: + self.notify.debug('No landmark building for (%s) in zone %s' % (str(p), self.zoneId)) + continue + if blockNumber in self.buildingFrontDoors: + self.notify.debug('Multiple front doors for building %s in zone %s' % (blockNumber, self.zoneId)) + continue + self.buildingFrontDoors[blockNumber] = p + for p in self.sidedoorPointList: + blockNumber = p.getLandmarkBuildingIndex() + if blockNumber < 0: + self.notify.debug('No landmark building for (%s) in zone %s' % (str(p), self.zoneId)) + continue + if blockNumber in self.buildingSideDoors: + self.buildingSideDoors[blockNumber].append(p) + continue + self.buildingSideDoors[blockNumber] = [p] + for bldg in self.buildingMgr.getBuildings(): + if isinstance(bldg, HQBuildingAI.HQBuildingAI): + continue + blockNumber = bldg.getBlock()[0] + if blockNumber not in self.buildingFrontDoors: + self.notify.debug('No front door for building %s in zone %s' % (blockNumber, self.zoneId)) + if blockNumber not in self.buildingSideDoors: + self.notify.debug('No side door for building %s in zone %s' % (blockNumber, self.zoneId)) + + def countNumSuitsPerTrack(self, count): + for suit in self.suitList: + if suit.track in count: + count[suit.track] += 1 + continue + count[suit.track] = 1 + + def countNumBuildingsPerTrack(self, count): + if not self.buildingMgr: + return + for building in self.buildingMgr.getBuildings(): + if building.isSuitBuilding(): + if building.track in count: + count[building.track] += 1 + else: + count[building.track] = 1 + + def countNumBuildingsPerHeight(self, count): + if not self.buildingMgr: + return + for building in self.buildingMgr.getBuildings(): + if building.isSuitBuilding(): + height = building.numFloors - 1 + if height in count: + count[height] += 1 + else: + count[height] = 1 + + def formatNumSuitsPerTrack(self, count): + result = ' ' + for (track, num) in count.items(): + result += ' %s:%s' % (track, num) + return result[2:] + + def calcDesiredNumFlyInSuits(self): + if self.currDesired is not None: + return 0 + return self.baseNumSuits + self.suitCountAdjust + + def calcDesiredNumBuildingSuits(self): + if self.currDesired is not None: + return self.currDesired + if not self.buildingMgr: + return 0 + suitBuildings = self.buildingMgr.getEstablishedSuitBlocks() + return int(len(suitBuildings) * self.SUIT_BUILDING_NUM_SUITS) + + def getZoneIdToPointMap(self): + if self.zoneIdToPointMap is not None: + return self.zoneIdToPointMap + self.zoneIdToPointMap = {} + for point in self.streetPointList: + points = self.dnaStore.getAdjacentPoints(point) + i = points.getNumPoints() - 1 + while i >= 0: + pi = points.getPointIndex(i) + p = self.pointIndexes[pi] + i -= 1 + zoneId = self.dnaStore.getSuitEdgeZone(point.getIndex(), p.getIndex()) + if zoneId in self.zoneIdToPointMap: + self.zoneIdToPointMap[zoneId].append(point) + continue + self.zoneIdToPointMap[zoneId] = [point] + return self.zoneIdToPointMap + + def getStreetPointsForBuilding(self, blockNumber): + pointList = [] + if blockNumber in self.buildingSideDoors: + for doorPoint in self.buildingSideDoors[blockNumber]: + points = self.dnaStore.getAdjacentPoints(doorPoint) + i = points.getNumPoints() - 1 + while i >= 0: + pi = points.getPointIndex(i) + point = self.pointIndexes[pi] + if point.getPointType() == DNASuitPoint.STREET_POINT: + pointList.append(point) + i -= 1 + if blockNumber in self.buildingFrontDoors: + doorPoint = self.buildingFrontDoors[blockNumber] + points = self.dnaStore.getAdjacentPoints(doorPoint) + i = points.getNumPoints() - 1 + while i >= 0: + pi = points.getPointIndex(i) + pointList.append(self.pointIndexes[pi]) + i -= 1 + return pointList + + def createNewSuit(self, blockNumbers, streetPoints, toonBlockTakeover=None, + cogdoTakeover=None, minPathLen=None, maxPathLen=None, + buildingHeight=None, suitLevel=None, suitType=None, suitTrack=None, + suitName=None, skelecog=None, revives=None, waiter=None): + startPoint = None + blockNumber = None + if self.notify.getDebug(): + self.notify.debug('Choosing origin from %d+%d possibles.' % (len(streetPoints), len(blockNumbers))) + while startPoint == None and len(blockNumbers) > 0: + bn = random.choice(blockNumbers) + blockNumbers.remove(bn) + if bn in self.buildingSideDoors: + for doorPoint in self.buildingSideDoors[bn]: + points = self.dnaStore.getAdjacentPoints(doorPoint) + i = points.getNumPoints() - 1 + while blockNumber == None and i >= 0: + pi = points.getPointIndex(i) + p = self.pointIndexes[pi] + i -= 1 + startTime = SuitTimings.fromSuitBuilding + startTime += self.dnaStore.getSuitEdgeTravelTime(doorPoint.getIndex(), pi, self.suitWalkSpeed) + if not self.pointCollision(p, doorPoint, startTime): + startTime = SuitTimings.fromSuitBuilding + startPoint = doorPoint + blockNumber = bn + while startPoint == None and len(streetPoints) > 0: + p = random.choice(streetPoints) + streetPoints.remove(p) + if not self.pointCollision(p, None, SuitTimings.fromSky): + startPoint = p + startTime = SuitTimings.fromSky + continue + if startPoint == None: + return None + newSuit = DistributedSuitAI.DistributedSuitAI(simbase.air, self) + newSuit.startPoint = startPoint + if blockNumber != None: + newSuit.buildingSuit = 1 + if suitTrack == None: + suitTrack = self.buildingMgr.getBuildingTrack(blockNumber) + else: + newSuit.flyInSuit = 1 + newSuit.attemptingTakeover = self.newSuitShouldAttemptTakeover() + if newSuit.attemptingTakeover: + if suitTrack == None and len(self.pendingBuildingTracks) > 0: + suitTrack = self.pendingBuildingTracks[0] + del self.pendingBuildingTracks[0] + self.pendingBuildingTracks.append(suitTrack) + + if buildingHeight == None and len(self.pendingBuildingHeights) > 0: + buildingHeight = self.pendingBuildingHeights[0] + del self.pendingBuildingHeights[0] + self.pendingBuildingHeights.append(buildingHeight) + if suitName is None: + suitDeptIndex, suitTypeIndex, flags = self.air.suitInvasionManager.getInvadingCog() + if flags & IFSkelecog: + skelecog = 1 + if flags & IFWaiter: + waiter = True + if flags & IFV2: + revives = 1 + if suitDeptIndex is not None: + suitTrack = SuitDNA.suitDepts[suitDeptIndex] + if suitTypeIndex is not None: + suitName = self.air.suitInvasionManager.getSuitName() + else: + suitName = self.defaultSuitName + if (suitType is None) and (suitName is not None): + suitType = SuitDNA.getSuitType(suitName) + suitTrack = SuitDNA.getSuitDept(suitName) + if (suitLevel is None) and (buildingHeight is not None): + suitLevel = self.chooseSuitLevel(self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_LVL], buildingHeight) + (suitLevel, suitType, suitTrack) = self.pickLevelTypeAndTrack(suitLevel, suitType, suitTrack) + newSuit.setupSuitDNA(suitLevel, suitType, suitTrack) + newSuit.buildingHeight = buildingHeight + gotDestination = self.chooseDestination(newSuit, startTime, toonBlockTakeover = toonBlockTakeover, cogdoTakeover = cogdoTakeover, minPathLen = minPathLen, maxPathLen = maxPathLen) + if not gotDestination: + self.notify.debug("Couldn't get a destination in %d!" % self.zoneId) + newSuit.doNotDeallocateChannel = None + newSuit.delete() + return None + newSuit.initializePath() + self.zoneChange(newSuit, None, newSuit.zoneId) + if skelecog: + newSuit.setSkelecog(skelecog) + newSuit.generateWithRequired(newSuit.zoneId) + if revives is not None: + newSuit.b_setSkeleRevives(revives) + if waiter: + newSuit.b_setWaiter(1) + newSuit.d_setSPDoId(self.doId) + newSuit.moveToNextLeg(None) + self.suitList.append(newSuit) + if newSuit.flyInSuit: + self.numFlyInSuits += 1 + if newSuit.buildingSuit: + self.numBuildingSuits += 1 + if newSuit.attemptingTakeover: + self.numAttemptingTakeover += 1 + return newSuit + + def countNumNeededBuildings(self): + if not self.buildingMgr: + return False + numSuitBuildings = len(self.buildingMgr.getSuitBlocks()) + if (random.random() * 100) < SuitBuildingGlobals.buildingChance[self.zoneId]: + return SuitBuildingGlobals.buildingMinMax[self.zoneId][1] - numSuitBuildings + else: + return self.targetNumSuitBuildings - numSuitBuildings + + def newSuitShouldAttemptTakeover(self): + if not self.SUITS_ENTER_BUILDINGS: + return False + numNeeded = self.countNumNeededBuildings() + if self.numAttemptingTakeover >= numNeeded: + self.pendingBuildingTracks = [] + return False + self.notify.debug('DSP %s is planning a takeover attempt in zone %s' % (self.getDoId(), self.zoneId)) + return True + + def chooseDestination(self, suit, startTime, toonBlockTakeover=None, + cogdoTakeover=None, minPathLen=None, maxPathLen=None): + possibles = [] + backup = [] + + if toonBlockTakeover is not None: + suit.attemptingTakeover = 1 + blockNumber = toonBlockTakeover + if blockNumber in self.buildingFrontDoors: + possibles.append((blockNumber, self.buildingFrontDoors[blockNumber])) + elif suit.attemptingTakeover: + for blockNumber in self.buildingMgr.getToonBlocks(): + building = self.buildingMgr.getBuilding(blockNumber) + (extZoneId, intZoneId) = building.getExteriorAndInteriorZoneId() + if not NPCToons.isZoneProtected(intZoneId): + if blockNumber in self.buildingFrontDoors: + possibles.append((blockNumber, self.buildingFrontDoors[blockNumber])) + if cogdoTakeover is None: + if suit.dna.dept in ALLOWED_FO_TRACKS: + cogdoTakeover = random.random() < self.CogdoRatio + elif self.buildingMgr: + for blockNumber in self.buildingMgr.getSuitBlocks(): + track = self.buildingMgr.getBuildingTrack(blockNumber) + if (track == suit.track) and (blockNumber in self.buildingSideDoors): + for doorPoint in self.buildingSideDoors[blockNumber]: + possibles.append((blockNumber, doorPoint)) + backup = [] + for p in self.streetPointList: + backup.append((None, p)) + if self.notify.getDebug(): + self.notify.debug('Choosing destination point from %s+%s possibles.' % (len(possibles), len(backup))) + if len(possibles) == 0: + possibles = backup + backup = [] + if minPathLen is None: + if suit.attemptingTakeover: + minPathLen = self.MIN_TAKEOVER_PATH_LEN + else: + minPathLen = self.MIN_PATH_LEN + if maxPathLen is None: + maxPathLen = self.MAX_PATH_LEN + retryCount = 0 + while (len(possibles) > 0) and (retryCount < 50): + p = random.choice(possibles) + possibles.remove(p) + if len(possibles) == 0: + possibles = backup + backup = [] + path = self.genPath(suit.startPoint, p[1], minPathLen, maxPathLen) + if path and (not self.pathCollision(path, startTime)): + suit.endPoint = p[1] + suit.minPathLen = minPathLen + suit.maxPathLen = maxPathLen + suit.buildingDestination = p[0] + suit.buildingDestinationIsCogdo = cogdoTakeover + suit.setPath(path) + return 1 + retryCount += 1 + return 0 + + def pathCollision(self, path, elapsedTime): + i = 0 + pi = path.getPointIndex(i) + point = self.pointIndexes[pi] + adjacentPoint = self.pointIndexes[path.getPointIndex(i + 1)] + while (point.getPointType() == DNASuitPoint.FRONT_DOOR_POINT) or ( + point.getPointType() == DNASuitPoint.SIDE_DOOR_POINT): + i += 1 + lastPi = pi + pi = path.getPointIndex(i) + adjacentPoint = point + point = self.pointIndexes[pi] + elapsedTime += self.dnaStore.getSuitEdgeTravelTime(lastPi, pi, self.suitWalkSpeed) + result = self.pointCollision(point, adjacentPoint, elapsedTime) + return result + + def pointCollision(self, point, adjacentPoint, elapsedTime): + for suit in self.suitList: + if suit.pointInMyPath(point, elapsedTime): + return 1 + if adjacentPoint is not None: + return self.battleCollision(point, adjacentPoint) + else: + points = self.dnaStore.getAdjacentPoints(point) + i = points.getNumPoints() - 1 + while i >= 0: + pi = points.getPointIndex(i) + p = self.pointIndexes[pi] + i -= 1 + if self.battleCollision(point, p): + return 1 + return 0 + + def battleCollision(self, point, adjacentPoint): + zoneId = self.dnaStore.getSuitEdgeZone(point.getIndex(), adjacentPoint.getIndex()) + return self.battleMgr.cellHasBattle(zoneId) + + def removeSuit(self, suit): + self.zoneChange(suit, suit.zoneId) + if self.suitList.count(suit) > 0: + self.suitList.remove(suit) + if suit.flyInSuit: + self.numFlyInSuits -= 1 + if suit.buildingSuit: + self.numBuildingSuits -= 1 + if suit.attemptingTakeover: + self.numAttemptingTakeover -= 1 + suit.requestDelete() + + def countTakeovers(self): + count = 0 + for suit in self.suitList: + if suit.attemptingTakeover: + count += 1 + return count + + def __waitForNextUpkeep(self): + t = random.random() * 2.0 + self.POP_UPKEEP_DELAY + taskMgr.doMethodLater(t, self.upkeepSuitPopulation, self.taskName('sptUpkeepPopulation')) + + def __waitForNextAdjust(self): + t = random.random() * 10.0 + self.POP_ADJUST_DELAY + taskMgr.doMethodLater(t, self.adjustSuitPopulation, self.taskName('sptAdjustPopulation')) + + def upkeepSuitPopulation(self, task): + targetFlyInNum = self.calcDesiredNumFlyInSuits() + targetFlyInNum = min(targetFlyInNum, self.TOTAL_MAX_SUITS - self.numBuildingSuits) + streetPoints = self.streetPointList[:] + flyInDeficit = ((targetFlyInNum - self.numFlyInSuits) + 3) / 4 + while flyInDeficit > 0: + if not self.createNewSuit([], streetPoints): + break + flyInDeficit -= 1 + if self.buildingMgr: + suitBuildings = self.buildingMgr.getEstablishedSuitBlocks() + else: + suitBuildings = [] + if self.currDesired != None: + targetBuildingNum = max(0, self.currDesired - self.numFlyInSuits) + else: + targetBuildingNum = int(len(suitBuildings) * self.SUIT_BUILDING_NUM_SUITS) + targetBuildingNum += flyInDeficit + targetBuildingNum = min(targetBuildingNum, self.TOTAL_MAX_SUITS - self.numFlyInSuits) + buildingDeficit = ((targetBuildingNum - self.numBuildingSuits) + 3) / 4 + while buildingDeficit > 0: + if not self.createNewSuit(suitBuildings, streetPoints): + break + buildingDeficit -= 1 + if self.notify.getDebug() and self.currDesired == None: + self.notify.debug('zone %d has %d of %d fly-in and %d of %d building suits.' % (self.zoneId, self.numFlyInSuits, targetFlyInNum, self.numBuildingSuits, targetBuildingNum)) + if buildingDeficit != 0: + self.notify.debug('remaining deficit is %d.' % buildingDeficit) + if self.buildingMgr: + suitBuildings = self.buildingMgr.getEstablishedSuitBlocks() + timeoutIndex = min(len(suitBuildings), len(self.SUIT_BUILDING_TIMEOUT) - 1) + timeout = self.SUIT_BUILDING_TIMEOUT[timeoutIndex] + if timeout != None: + timeout *= 3600.0 + oldest = None + oldestAge = 0 + now = time.time() + for b in suitBuildings: + building = self.buildingMgr.getBuilding(b) + if hasattr(building, 'elevator'): + if building.elevator.fsm.getCurrentState().getName() == 'waitEmpty': + age = now - building.becameSuitTime + if age > oldestAge: + oldest = building + oldestAge = age + if oldestAge > timeout: + self.notify.info('Street %d has %d buildings; reclaiming %0.2f-hour-old building.' % (self.zoneId, len(suitBuildings), oldestAge / 3600.0)) + oldest.b_setVictorList([0, 0, 0, 0]) + oldest.updateSavedBy(None) + oldest.toonTakeOver() + self.__waitForNextUpkeep() + return Task.done + + def adjustSuitPopulation(self, task): + hoodInfo = self.SuitHoodInfo[self.hoodInfoIdx] + if hoodInfo[self.SUIT_HOOD_INFO_MAX] == 0: + self.__waitForNextAdjust() + return Task.done + min = hoodInfo[self.SUIT_HOOD_INFO_MIN] + max = hoodInfo[self.SUIT_HOOD_INFO_MAX] + adjustment = random.choice((-2, -1, -1, 0, 0, 0, 1, 1, 2)) + self.suitCountAdjust += adjustment + desiredNum = self.calcDesiredNumFlyInSuits() + if desiredNum < min: + self.suitCountAdjust = min - self.baseNumSuits + elif desiredNum > max: + self.suitCountAdjust = max - self.baseNumSuits + self.__waitForNextAdjust() + return Task.done + + def suitTakeOver(self, blockNumber, suitTrack, difficulty, buildingHeight): + if self.pendingBuildingTracks.count(suitTrack) > 0: + self.pendingBuildingTracks.remove(suitTrack) + if self.pendingBuildingHeights.count(buildingHeight) > 0: + self.pendingBuildingHeights.remove(buildingHeight) + building = self.buildingMgr.getBuilding(blockNumber) + if building is None: + return + building.suitTakeOver(suitTrack, difficulty, buildingHeight) + + def cogdoTakeOver(self, blockNumber, difficulty, buildingHeight, dept): + if self.pendingBuildingHeights.count(buildingHeight) > 0: + self.pendingBuildingHeights.remove(buildingHeight) + building = self.buildingMgr.getBuilding(blockNumber) + building.cogdoTakeOver(difficulty, buildingHeight, dept) + + def recycleBuilding(self): + bmin = SuitBuildingGlobals.buildingMinMax[self.zoneId][0] + current = len(self.buildingMgr.getSuitBlocks()) + if (self.targetNumSuitBuildings > bmin) and (current <= self.targetNumSuitBuildings): + self.targetNumSuitBuildings -= 1 + self.assignSuitBuildings(1) + + def createInitialSuitBuildings(self): + if self.buildingMgr is None: + return + + # If we aren't at our minimum number of buildings, let's spawn some! + suitBlockCount = len(self.buildingMgr.getSuitBlocks()) + if suitBlockCount < self.targetNumSuitBuildings: + for _ in xrange(self.targetNumSuitBuildings - suitBlockCount): + blockNumber = random.choice(self.buildingMgr.getToonBlocks()) + building = self.buildingMgr.getBuilding(blockNumber) + if building is None: + continue + if NPCToons.isZoneProtected(building.getExteriorAndInteriorZoneId()[1]): + continue + suitName = self.air.suitInvasionManager.getInvadingCog()[0] + if suitName is None: + suitName = self.defaultSuitName + suitType = None + suitTrack = None + if suitName is not None: + suitType = SuitDNA.getSuitType(suitName) + suitTrack = SuitDNA.getSuitDept(suitName) + (suitLevel, suitType, suitTrack) = self.pickLevelTypeAndTrack(None, suitType, suitTrack) + building.suitTakeOver(suitTrack, suitLevel, None) + + # Save the building manager's state: + self.buildingMgr.save() + + def assignInitialSuitBuildings(self): + totalBuildings = 0 + targetSuitBuildings = 0 + actualSuitBuildings = 0 + for sp in self.air.suitPlanners.values(): + totalBuildings += len(sp.frontdoorPointList) + targetSuitBuildings += sp.targetNumSuitBuildings + if sp.buildingMgr: + actualSuitBuildings += len(sp.buildingMgr.getSuitBlocks()) + wantedSuitBuildings = int((totalBuildings*self.TOTAL_SUIT_BUILDING_PCT) / 100) + self.notify.debug('Want %s out of %s total suit buildings; we currently have %s assigned, %s actual.' % (wantedSuitBuildings, totalBuildings, targetSuitBuildings, actualSuitBuildings)) + if actualSuitBuildings > 0: + numReassigned = 0 + for sp in self.air.suitPlanners.values(): + if sp.buildingMgr: + numBuildings = len(sp.buildingMgr.getSuitBlocks()) + else: + numBuildings = 0 + if numBuildings > sp.targetNumSuitBuildings: + more = numBuildings - sp.targetNumSuitBuildings + sp.targetNumSuitBuildings += more + targetSuitBuildings += more + numReassigned += more + if numReassigned > 0: + self.notify.debug('Assigned %s buildings where suit buildings already existed.' % numReassigned) + if wantedSuitBuildings > targetSuitBuildings: + additionalBuildings = wantedSuitBuildings - targetSuitBuildings + self.assignSuitBuildings(additionalBuildings) + elif wantedSuitBuildings < targetSuitBuildings: + extraBuildings = targetSuitBuildings - wantedSuitBuildings + self.unassignSuitBuildings(extraBuildings) + + def assignSuitBuildings(self, numToAssign): + hoodInfo = self.SuitHoodInfo[:] + totalWeight = self.TOTAL_BWEIGHT + totalWeightPerTrack = self.TOTAL_BWEIGHT_PER_TRACK[:] + totalWeightPerHeight = self.TOTAL_BWEIGHT_PER_HEIGHT[:] + numPerTrack = { + 'c': 0, + 'l': 0, + 'm': 0, + 's': 0 + } + for sp in self.air.suitPlanners.values(): + sp.countNumBuildingsPerTrack(numPerTrack) + numPerTrack['c'] += sp.pendingBuildingTracks.count('c') + numPerTrack['l'] += sp.pendingBuildingTracks.count('l') + numPerTrack['m'] += sp.pendingBuildingTracks.count('m') + numPerTrack['s'] += sp.pendingBuildingTracks.count('s') + numPerHeight = { + 0: 0, + 1: 0, + 2: 0, + 3: 0, + 4: 0 + } + for sp in self.air.suitPlanners.values(): + sp.countNumBuildingsPerHeight(numPerHeight) + numPerHeight[0] += sp.pendingBuildingHeights.count(0) + numPerHeight[1] += sp.pendingBuildingHeights.count(1) + numPerHeight[2] += sp.pendingBuildingHeights.count(2) + numPerHeight[3] += sp.pendingBuildingHeights.count(3) + numPerHeight[4] += sp.pendingBuildingHeights.count(4) + while numToAssign > 0: + smallestCount = None + smallestTracks = [] + for trackIndex in xrange(4): + if totalWeightPerTrack[trackIndex]: + track = SuitDNA.suitDepts[trackIndex] + count = numPerTrack[track] + if (smallestCount is None) or (count < smallestCount): + smallestTracks = [track] + smallestCount = count + elif count == smallestCount: + smallestTracks.append(track) + if not smallestTracks: + self.notify.info('No more room for buildings, with %s still to assign.' % numToAssign) + return + buildingTrack = random.choice(smallestTracks) + buildingTrackIndex = SuitDNA.suitDepts.index(buildingTrack) + smallestCount = None + smallestHeights = [] + for height in xrange(5): + if totalWeightPerHeight[height]: + count = float(numPerHeight[height]) / float(self.BUILDING_HEIGHT_DISTRIBUTION[height]) + if (smallestCount is None) or (count < smallestCount): + smallestHeights = [height] + smallestCount = count + elif count == smallestCount: + smallestHeights.append(height) + if not smallestHeights: + self.notify.info('No more room for buildings, with %s still to assign.' % numToAssign) + return + buildingHeight = random.choice(smallestHeights) + self.notify.info('Existing buildings are (%s, %s), choosing from (%s, %s), chose %s, %s.' % (self.formatNumSuitsPerTrack(numPerTrack), self.formatNumSuitsPerTrack(numPerHeight), smallestTracks, smallestHeights, buildingTrack, buildingHeight)) + repeat = 1 + while repeat and (buildingTrack is not None) and (buildingHeight is not None): + if len(hoodInfo) == 0: + self.notify.warning('No more streets can have suit buildings, with %s buildings unassigned!' % numToAssign) + return + repeat = 0 + currHoodInfo = self.chooseStreetWithPreference(hoodInfo, buildingTrackIndex, buildingHeight) + zoneId = currHoodInfo[self.SUIT_HOOD_INFO_ZONE] + if zoneId in self.air.suitPlanners: + sp = self.air.suitPlanners[zoneId] + numTarget = sp.targetNumSuitBuildings + numTotalBuildings = len(sp.frontdoorPointList) + else: + numTarget = 0 + numTotalBuildings = 0 + if numTarget >= SuitBuildingGlobals.buildingMinMax[self.zoneId][1] or numTarget >= numTotalBuildings: + self.notify.info('Zone %s has enough buildings.' % zoneId) + hoodInfo.remove(currHoodInfo) + weight = currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT] + tracks = currHoodInfo[self.SUIT_HOOD_INFO_TRACK] + heights = currHoodInfo[self.SUIT_HOOD_INFO_HEIGHTS] + totalWeight -= weight + totalWeightPerTrack[0] -= weight * tracks[0] + totalWeightPerTrack[1] -= weight * tracks[1] + totalWeightPerTrack[2] -= weight * tracks[2] + totalWeightPerTrack[3] -= weight * tracks[3] + totalWeightPerHeight[0] -= weight * heights[0] + totalWeightPerHeight[1] -= weight * heights[1] + totalWeightPerHeight[2] -= weight * heights[2] + totalWeightPerHeight[3] -= weight * heights[3] + totalWeightPerHeight[4] -= weight * heights[4] + if totalWeightPerTrack[buildingTrackIndex] <= 0: + buildingTrack = None + if totalWeightPerHeight[buildingHeight] <= 0: + buildingHeight = None + repeat = 1 + if (buildingTrack is not None) and (buildingHeight is not None): + sp.targetNumSuitBuildings += 1 + sp.pendingBuildingTracks.append(buildingTrack) + sp.pendingBuildingHeights.append(buildingHeight) + self.notify.info('Assigning building to zone %s, pending tracks = %s, pending heights = %s' % (zoneId, sp.pendingBuildingTracks, sp.pendingBuildingHeights)) + numPerTrack[buildingTrack] += 1 + numPerHeight[buildingHeight] += 1 + numToAssign -= 1 + + def unassignSuitBuildings(self, numToAssign): + hoodInfo = self.SuitHoodInfo[:] + totalWeight = self.TOTAL_BWEIGHT + while numToAssign > 0: + repeat = 1 + while repeat: + if len(hoodInfo) == 0: + self.notify.warning('No more streets can remove suit buildings, with %s buildings too many!' % numToAssign) + return + repeat = 0 + currHoodInfo = self.chooseStreetNoPreference(hoodInfo, totalWeight) + zoneId = currHoodInfo[self.SUIT_HOOD_INFO_ZONE] + if zoneId in self.air.suitPlanners: + sp = self.air.suitPlanners[zoneId] + numTarget = sp.targetNumSuitBuildings + numTotalBuildings = len(sp.frontdoorPointList) + else: + numTarget = 0 + numTotalBuildings = 0 + if numTarget <= SuitBuildingGlobals.buildingMinMax[self.zoneId][0]: + self.notify.info("Zone %s can't remove any more buildings." % zoneId) + hoodInfo.remove(currHoodInfo) + totalWeight -= currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT] + repeat = 1 + self.notify.info('Unassigning building from zone %s.' % zoneId) + sp.targetNumSuitBuildings -= 1 + numToAssign -= 1 + + def chooseStreetNoPreference(self, hoodInfo, totalWeight): + c = random.random() * totalWeight + t = 0 + for currHoodInfo in hoodInfo: + weight = currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT] + t += weight + if c < t: + return currHoodInfo + self.notify.warning('Weighted random choice failed! Total is %s, chose %s' % (t, c)) + return random.choice(hoodInfo) + + def chooseStreetWithPreference(self, hoodInfo, buildingTrackIndex, buildingHeight): + dist = [] + for currHoodInfo in hoodInfo: + weight = currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT] + thisValue = weight * currHoodInfo[self.SUIT_HOOD_INFO_TRACK][buildingTrackIndex] * currHoodInfo[self.SUIT_HOOD_INFO_HEIGHTS][buildingHeight] + dist.append(thisValue) + totalWeight = sum(dist) + c = random.random() * totalWeight + t = 0 + for i in xrange(len(hoodInfo)): + t += dist[i] + if c < t: + return hoodInfo[i] + self.notify.warning('Weighted random choice failed! Total is %s, chose %s' % (t, c)) + return random.choice(hoodInfo) + + def chooseSuitLevel(self, possibleLevels, buildingHeight): + choices = [] + for level in possibleLevels: + (minFloors, maxFloors) = SuitBuildingGlobals.SuitBuildingInfo[level - 1][0] + if buildingHeight >= minFloors - 1 and buildingHeight <= maxFloors - 1: + choices.append(level) + return random.choice(choices) + + + def initTasks(self): + if self.air.wantCogbuildings: + self.createInitialSuitBuildings() + self.__waitForNextUpkeep() + self.__waitForNextAdjust() + + def resyncSuits(self): + for suit in self.suitList: + suit.resync() + + def flySuits(self): + for suit in self.suitList: + if suit.pathState == 1: + suit.flyAwayNow() + + def requestBattle(self, zoneId, suit, toonId): + self.notify.debug('requestBattle() - zone: %s suit: %s toon: %s' % (zoneId, suit.doId, toonId)) + canonicalZoneId = ZoneUtil.getCanonicalZoneId(zoneId) + if canonicalZoneId not in self.battlePosDict: + return 0 + toon = self.air.doId2do.get(toonId) + if toon.getBattleId() > 0: + self.notify.warning('We tried to request a battle when the toon was already in battle') + return 0 + if toon: + if hasattr(toon, 'doId'): + toon.b_setBattleId(toonId) + + pos = self.battlePosDict[canonicalZoneId] + + interactivePropTrackBonus = -1 + if config.GetBool('props-buff-battles', True) and canonicalZoneId in self.cellToGagBonusDict: + interactivePropTrackBonus = self.cellToGagBonusDict[canonicalZoneId] + + self.battleMgr.newBattle( + zoneId, zoneId, pos, suit, toonId, self.__battleFinished, + self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_SMAX], + interactivePropTrackBonus) + for currOther in self.zoneInfo[zoneId]: + self.notify.debug('Found suit %s in this new battle zone %s' % (currOther.getDoId(), zoneId)) + if currOther != suit: + if currOther.pathState == 1 and currOther.legType == SuitLeg.TWalk: + self.checkForBattle(zoneId, currOther) + return 1 + + def __battleFinished(self, zoneId): + self.notify.debug('DistSuitPlannerAI: battle in zone ' + str(zoneId) + ' finished') + currBattleIdx = 0 + while currBattleIdx < len(self.battleList): + currBattle = self.battleList[currBattleIdx] + if currBattle[0] == zoneId: + self.notify.debug('DistSuitPlannerAI: battle removed') + self.battleList.remove(currBattle) + currBattleIdx = currBattleIdx + 1 + + def __suitCanJoinBattle(self, zoneId): + battle = self.battleMgr.getBattle(zoneId) + if len(battle.suits) >= 4: + return 0 + if battle: + if simbase.config.GetBool('suits-always-join', 0): + return 1 + jChanceList = self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_JCHANCE] + ratioIdx = (len(battle.toons) - battle.numSuitsEver) + 2 + if ratioIdx >= 0: + if ratioIdx < len(jChanceList): + if random.randint(0, 99) < jChanceList[ratioIdx]: + return 1 + else: + self.notify.warning('__suitCanJoinBattle idx out of range!') + return 1 + return 0 + + + def checkForBattle(self, zoneId, suit): + if self.battleMgr.cellHasBattle(zoneId): + if self.__suitCanJoinBattle(zoneId) and self.battleMgr.requestBattleAddSuit(zoneId, suit): + return 1 + suit.flyAwayNow() + return 1 + else: + return 0 + + + def postBattleResumeCheck(self, suit): + self.notify.debug('DistSuitPlannerAI:postBattleResumeCheck: suit ' + str(suit.getDoId()) + ' is leaving battle') + battleIndex = 0 + for currBattle in self.battleList: + if suit.zoneId == currBattle[0]: + self.notify.debug(' battle found' + str(suit.zoneId)) + for currPath in currBattle[1]: + for currPathPtSuit in xrange(suit.currWpt, suit.myPath.getNumPoints()): + ptIdx = suit.myPath.getPointIndex(currPathPtSuit) + if self.notify.getDebug(): + self.notify.debug(' comparing' + str(ptIdx) + 'with' + str(currPath)) + if currPath == ptIdx: + if self.notify.getDebug(): + self.notify.debug(' match found, telling' + 'suit to fly') + return 0 + battleIndex += 1 + pointList = [] + for currPathPtSuit in xrange(suit.currWpt, suit.myPath.getNumPoints()): + ptIdx = suit.myPath.getPointIndex(currPathPtSuit) + if self.notify.getDebug(): + self.notify.debug(' appending point with index of' + str(ptIdx)) + pointList.append(ptIdx) + self.battleList.append([suit.zoneId, pointList]) + return 1 + + def zoneChange(self, suit, oldZone, newZone=None): + if (oldZone in self.zoneInfo) and (suit in self.zoneInfo[oldZone]): + self.zoneInfo[oldZone].remove(suit) + if newZone is not None: + if newZone not in self.zoneInfo: + self.zoneInfo[newZone] = [] + self.zoneInfo[newZone].append(suit) + + def d_setZoneId(self, zoneId): + self.sendUpdate('setZoneId', [self.getZoneId()]) + + def getZoneId(self): + return self.zoneId + + def suitListQuery(self): + suitIndexList = [] + for suit in self.suitList: + suitIndexList.append(SuitDNA.suitHeadTypes.index(suit.dna.name)) + self.sendUpdateToAvatarId(self.air.getAvatarIdFromSender(), 'suitListResponse', [suitIndexList]) + + def buildingListQuery(self): + buildingDict = {} + self.countNumBuildingsPerTrack(buildingDict) + buildingList = [0, 0, 0, 0] + for dept in SuitDNA.suitDepts: + if dept in buildingDict: + buildingList[SuitDNA.suitDepts.index(dept)] = buildingDict[dept] + self.sendUpdateToAvatarId(self.air.getAvatarIdFromSender(), 'buildingListResponse', [buildingList]) + + def pickLevelTypeAndTrack(self, level=None, type=None, track=None): + if level is None: + level = random.choice(self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_LVL]) + if type is None: + typeChoices = range(max(level - 4, 1), min(level, self.MAX_SUIT_TYPES) + 1) + type = random.choice(typeChoices) + else: + level = min(max(level, type), type + 4) + if track is None: + track = SuitDNA.suitDepts[SuitBattleGlobals.pickFromFreqList(self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_TRACK])] + self.notify.debug('pickLevelTypeAndTrack: %s %s %s' % (level, type, track)) + return (level, type, track) diff --git a/toontown/suit/DistributedTutorialSuit.py b/toontown/suit/DistributedTutorialSuit.py new file mode 100755 index 00000000..bb332659 --- /dev/null +++ b/toontown/suit/DistributedTutorialSuit.py @@ -0,0 +1,71 @@ +import DistributedSuitBase +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from panda3d.core import * +from toontown.distributed.DelayDeletable import DelayDeletable + + +class DistributedTutorialSuit(DistributedSuitBase.DistributedSuitBase, DelayDeletable): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTutorialSuit') + + def __init__(self, cr): + try: + self.DistributedSuit_initialized + except: + self.DistributedSuit_initialized = 1 + DistributedSuitBase.DistributedSuitBase.__init__(self, cr) + self.fsm = ClassicFSM.ClassicFSM('DistributedSuit', [State.State('Off', self.enterOff, self.exitOff, ['Walk', 'Battle']), + State.State('Walk', self.enterWalk, self.exitWalk, ['WaitForBattle', 'Battle']), + State.State('Battle', self.enterBattle, self.exitBattle, []), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['Battle'])], 'Off', 'Off') + self.fsm.enterInitialState() + + def generate(self): + DistributedSuitBase.DistributedSuitBase.generate(self) + + def announceGenerate(self): + DistributedSuitBase.DistributedSuitBase.announceGenerate(self) + self.setState('Walk') + + def disable(self): + self.notify.debug('DistributedSuit %d: disabling' % self.getDoId()) + self.setState('Off') + DistributedSuitBase.DistributedSuitBase.disable(self) + + def delete(self): + try: + self.DistributedSuit_deleted + except: + self.DistributedSuit_deleted = 1 + self.notify.debug('DistributedSuit %d: deleting' % self.getDoId()) + del self.fsm + DistributedSuitBase.DistributedSuitBase.delete(self) + + def d_requestBattle(self, pos, hpr): + self.cr.playGame.getPlace().setState('WaitForBattle') + self.sendUpdate('requestBattle', [pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2]]) + + def __handleToonCollision(self, collEntry): + toonId = base.localAvatar.getDoId() + self.notify.debug('Distributed suit: requesting a Battle with ' + 'toon: %d' % toonId) + self.d_requestBattle(self.getPos(), self.getHpr()) + self.setState('WaitForBattle') + + def enterWalk(self): + self.enableBattleDetect('walk', self.__handleToonCollision) + self.loop('walk', 0) + pathPoints = [ + Vec3(55, 25, -0.5), + Vec3(25, 25, -0.5), + Vec3(25, 15, -0.5), + Vec3(55, 15, -0.5), + Vec3(55, 25, -0.5) + ] + self.tutWalkTrack = self.makePathTrack(self, pathPoints, 4.5, 'tutFlunkyWalk') + self.tutWalkTrack.loop() + + def exitWalk(self): + self.disableBattleDetect() + self.tutWalkTrack.pause() + self.tutWalkTrack = None diff --git a/toontown/suit/DistributedTutorialSuitAI.py b/toontown/suit/DistributedTutorialSuitAI.py new file mode 100755 index 00000000..88f023f4 --- /dev/null +++ b/toontown/suit/DistributedTutorialSuitAI.py @@ -0,0 +1,57 @@ +from direct.directnotify.DirectNotifyGlobal import * +from panda3d.core import * + +from toontown.suit import SuitDNA +from toontown.suit import SuitDialog +from toontown.suit.DistributedSuitBaseAI import DistributedSuitBaseAI +from toontown.tutorial.DistributedBattleTutorialAI import DistributedBattleTutorialAI + + +class FakeBattleManager: + def __init__(self, avId): + self.avId = avId + + def destroy(self, battle): + if battle.suitsKilledThisBattle: + if self.avId in simbase.air.tutorialManager.avId2fsm: + simbase.air.tutorialManager.avId2fsm[self.avId].demand('HQ') + battle.requestDelete() + + +class DistributedTutorialSuitAI(DistributedSuitBaseAI): + notify = directNotify.newCategory('DistributedTutorialSuitAI') + + def __init__(self, air): + DistributedSuitBaseAI.__init__(self, air, None) + + suitDNA = SuitDNA.SuitDNA() + suitDNA.newSuit('f') + self.dna = suitDNA + self.setLevel(1) + + def destroy(self): + del self.dna + + def requestBattle(self, x, y, z, h, p, r): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av is None: + return + + self.confrontPos = Point3(x, y, z) + self.confrontHpr = Vec3(h, p, r) + + if av.getBattleId() > 0: + self.notify.warning('Avatar %d tried to request a battle, but is already in one.' % avId) + self.b_setBrushOff(SuitDialog.getBrushOffIndex(self.getStyleName())) + self.d_denyBattle(avId) + return + + battle = DistributedBattleTutorialAI( + self.air, FakeBattleManager(avId), Point3(35, 20, -0.5), self, + avId, 20001) + battle.generateWithRequired(self.zoneId) + battle.battleCellId = 0 + + def getConfrontPosHpr(self): + return (self.confrontPos, self.confrontHpr) diff --git a/toontown/suit/Goon.py b/toontown/suit/Goon.py new file mode 100755 index 00000000..364ccf26 --- /dev/null +++ b/toontown/suit/Goon.py @@ -0,0 +1,151 @@ +from panda3d.core import * +from direct.actor import Actor +from otp.avatar import Avatar +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import GoonGlobals +import SuitDNA +import math +AnimDict = {'pg': (('walk', '-walk'), ('collapse', '-collapse'), ('recovery', '-recovery')), + 'sg': (('walk', '-walk'), ('collapse', '-collapse'), ('recovery', '-recovery'))} +ModelDict = {'pg': 'phase_9/models/char/Cog_Goonie', + 'sg': 'phase_9/models/char/Cog_Goonie'} + +class Goon(Avatar.Avatar): + + def __init__(self, dnaName = None): + try: + self.Goon_initialized + except: + self.Goon_initialized = 1 + Avatar.Avatar.__init__(self) + self.ignore('nametagAmbientLightChanged') + self.hFov = 70 + self.attackRadius = 15 + self.strength = 15 + self.velocity = 4 + self.scale = 1.0 + if dnaName is not None: + self.initGoon(dnaName) + + return + + def initGoon(self, dnaName): + dna = SuitDNA.SuitDNA() + dna.newGoon(dnaName) + self.setDNA(dna) + self.type = dnaName + self.createHead() + self.find('**/actorGeom').setH(180) + self.nametag3d.hide() + + def initializeBodyCollisions(self, collIdStr): + Avatar.Avatar.initializeBodyCollisions(self, collIdStr) + if not self.ghostMode: + self.collNode.setCollideMask(self.collNode.getIntoCollideMask() | ToontownGlobals.PieBitmask) + + def delete(self): + try: + self.Goon_deleted + except: + self.Goon_deleted = 1 + filePrefix = ModelDict[self.style.name] + loader.unloadModel(filePrefix + '-zero') + animList = AnimDict[self.style.name] + for anim in animList: + loader.unloadModel(filePrefix + anim[1]) + + Avatar.Avatar.delete(self) + + return None + + def setDNAString(self, dnaString): + self.dna = SuitDNA.SuitDNA() + self.dna.makeFromNetString(dnaString) + self.setDNA(self.dna) + + def setDNA(self, dna): + if self.style: + pass + else: + self.style = dna + self.generateGoon() + self.initializeDropShadow() + self.initializeNametag3d() + + def generateGoon(self): + dna = self.style + filePrefix = ModelDict[dna.name] + self.loadModel(filePrefix + '-zero') + animDict = {} + animList = AnimDict[dna.name] + for anim in animList: + animDict[anim[0]] = filePrefix + anim[1] + + self.loadAnims(animDict) + + def getShadowJoint(self): + return self.getGeomNode() + + def getNametagJoints(self): + return [] + + def createHead(self): + self.headHeight = 3.0 + head = self.find('**/joint35') + if head.isEmpty(): + head = self.find('**/joint40') + self.hat = self.find('**/joint8') + parentNode = head.getParent() + self.head = parentNode.attachNewNode('headRotate') + head.reparentTo(self.head) + self.hat.reparentTo(self.head) + if self.type == 'pg': + self.hat.find('**/security_hat').hide() + elif self.type == 'sg': + self.hat.find('**/hard_hat').hide() + else: + self.hat.find('**/security_hat').hide() + self.hat.find('**/hard_hat').hide() + self.eye = self.find('**/eye') + self.eye.setColorScale(1, 1, 1, 1) + self.eye.setColor(1, 1, 0, 1) + self.radar = None + return + + def scaleRadar(self): + if self.radar: + self.radar.removeNode() + self.radar = self.eye.attachNewNode('radar') + model = loader.loadModel('phase_9/models/cogHQ/alphaCone2') + beam = self.radar.attachNewNode('beam') + transformNode = model.find('**/transform') + transformNode.getChildren().reparentTo(beam) + self.radar.setPos(0, -.5, 0.4) + self.radar.setTransparency(1) + self.radar.setDepthWrite(0) + self.halfFov = self.hFov / 2.0 + fovRad = self.halfFov * math.pi / 180.0 + self.cosHalfFov = math.cos(fovRad) + kw = math.tan(fovRad) * self.attackRadius / 10.5 + kl = math.sqrt(self.attackRadius * self.attackRadius + 9.0) / 25.0 + beam.setScale(kw / self.scale, kl / self.scale, kw / self.scale) + beam.setHpr(0, self.halfFov, 0) + p = self.radar.getRelativePoint(beam, Point3(0, -6, -1.8)) + self.radar.setSz(-3.5 / p[2]) + self.radar.flattenMedium() + self.radar.setColor(1, 1, 1, 0.2) + + def colorHat(self): + if self.type == 'pg': + colorList = GoonGlobals.PG_COLORS + elif self.type == 'sg': + colorList = GoonGlobals.SG_COLORS + else: + return + if self.strength >= 20: + self.hat.setColorScale(colorList[0]) + elif self.strength >= 15: + self.hat.setColorScale(colorList[1]) + else: + self.hat.clearColorScale() diff --git a/toontown/suit/GoonDeath.py b/toontown/suit/GoonDeath.py new file mode 100755 index 00000000..6bd3198c --- /dev/null +++ b/toontown/suit/GoonDeath.py @@ -0,0 +1,24 @@ +from direct.interval.IntervalGlobal import * +from panda3d.core import * +from direct.particles import ParticleEffect +from toontown.battle import BattleParticles +import random + +def createExplosionTrack(parent, deathNode, scale): + explosion = loader.loadModel('phase_3.5/models/props/explosion.bam') + explosion.getChild(0).setScale(scale) + explosion.reparentTo(deathNode) + explosion.setBillboardPointEye() + explosion.setPos(0, 0, 2) + return Sequence(Func(deathNode.reparentTo, parent), Wait(0.6), Func(deathNode.detachNode)) + + +def createGoonExplosion(parent, explosionPoint, scale): + BattleParticles.loadParticles() + deathNode = NodePath('goonDeath') + deathNode.setPos(explosionPoint) + explosion = createExplosionTrack(parent, deathNode, scale) + smallGearExplosion = BattleParticles.createParticleEffect('GearExplosion', numParticles=10) + bigGearExplosion = BattleParticles.createParticleEffect('WideGearExplosion', numParticles=30) + deathSound = base.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart_%s.ogg' % random.randint(1, 6)) + return Parallel(explosion, SoundInterval(deathSound), ParticleInterval(smallGearExplosion, deathNode, worldRelative=0, duration=4.3, cleanup=True), ParticleInterval(bigGearExplosion, deathNode, worldRelative=0, duration=1.0, cleanup=True), name='gears2MTrack') diff --git a/toontown/suit/GoonGlobals.py b/toontown/suit/GoonGlobals.py new file mode 100755 index 00000000..e633324b --- /dev/null +++ b/toontown/suit/GoonGlobals.py @@ -0,0 +1,14 @@ +from pandac.PandaModules import Vec4 +PG_COLORS = [Vec4(0.95, 0.0, 0.0, 1.0), Vec4(0.75, 0.35, 0.1, 1.0)] +SG_COLORS = [Vec4(0.0, 0.0, 0.95, 1.0), Vec4(0.35, 0.0, 0.75, 1.0)] +GOON_FORWARD = 1 +GOON_REVERSE = -1 +GOON_MOVIE_WALK = 0 +GOON_MOVIE_STUNNED = 1 +GOON_MOVIE_BATTLE = 2 +GOON_MOVIE_RECOVERY = 3 +GOON_MOVIE_SYNC = 4 +T_TURN = 3.0 +ANIM_WALK_RATE = 2.8 +ANIM_TURN_RATE = 20 +DEFAULT_WALK_RATE = 4 diff --git a/toontown/suit/GoonPathData.py b/toontown/suit/GoonPathData.py new file mode 100755 index 00000000..554c3567 --- /dev/null +++ b/toontown/suit/GoonPathData.py @@ -0,0 +1,216 @@ +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +taskZoneId2pathId = {ToontownGlobals.SellbotFactoryInt: 'sellbotFactory', + ToontownGlobals.SellbotMegaCorpInt: 'sellbotFactory', + ToontownGlobals.CashbotMintIntA: 'cashbotMint', + ToontownGlobals.CashbotMintIntB: 'cashbotMint', + ToontownGlobals.CashbotMintIntC: 'cashbotMint', + ToontownGlobals.LawbotOfficeInt: 'lawOfficeStage', + ToontownGlobals.LawbotStageIntA: 'lawOfficeStage', + ToontownGlobals.LawbotStageIntB: 'lawOfficeStage', + ToontownGlobals.LawbotStageIntC: 'lawOfficeStage', + ToontownGlobals.LawbotStageIntD: 'lawOfficeStage'} +Paths = {'sellbotFactory': {0: [Vec3(10.0, 0.0, 0.0), + Vec3(10.0, 10.0, 0.0), + Vec3(-10.0, 10.0, 0.0), + Vec3(-10.0, 0.0, 0.0)], + 1: [Vec3(10.0, 5.0, 0.0), Vec3(10.0, 0.0, 0.0), Vec3(-10.0, -5.0, 0.0)], + 2: [Vec3(-48.31, -0.001, 0), + Vec3(-48.0, -3.709, 0), + Vec3(35.041, -3.27, 0), + Vec3(34.751, -91.376, 0), + Vec3(39.869, -91.248, 0), + Vec3(39.93, -0.022, 0)], + 3: [Vec3(-47.9110107422, -6.86798095703, 0.0), + Vec3(27.691986084, -5.68200683594, 0.0), + Vec3(34.049987793, 3.55303955078, 0.0), + Vec3(-39.983001709, 3.68499755859, 0.0)], + 4: [Vec3(3.5649, 35.397, 0), + Vec3(-5.335, 36.067, 0), + Vec3(-4.605, -69.67, 0), + Vec3(17.815, -70.577, 0), + Vec3(17.4979, -50.997, 0), + Vec3(3.479, -46.775, 0)], + 5: [Vec3(-2.993, -21.085, 0), + Vec3(5.209, -20.966, 0), + Vec3(2.164, 74.742, 0), + Vec3(-50.439, 78.55, 0), + Vec3(-52.042, 58.831, 0), + Vec3(-3.549, 57.295, 0)], + 6: [Vec3(31.627, 2.093, 0.0), Vec3(4, 43, 0)], + 7: [Vec3(34.627, 2.093, 0.0), Vec3(32, 43, 0)], + 8: [Vec3(64.0, 43.0, 0.0), Vec3(59.5, 1.8, 0.0)], + 9: [Vec3(84.0, 43.0, 0.0), Vec3(93.0, 25.0, 0.0), Vec3(66.0, 2.0, 0.0)], + 10: [Vec3(85.0, 43.0, 0.0), + Vec3(66.0, 47.0, 0.0), + Vec3(53.0, 49.0, 0.0), + Vec3(71.0, 22.0, 0.0)], + 11: [Vec3(63.0, 43.0, 0.0), Vec3(33.0, 47.0, 0.0)], + 12: [Vec3(10.5139980316, 73.4393844604, 0.0), Vec3(-10.0053958893, 72.9239883423, 0.0), Vec3(5.55112314224, 90.2847213745, 0.0)], + 13: [Vec3(-2.62728261948, 50.5329399109, 0.0), Vec3(-2.21770620346, 3.07684659958, 0.0)], + 14: [Vec3(-15.9598636627, -15.0503959656, 0.0), + Vec3(8.35170555115, -17.5856513977, 0.0), + Vec3(8.34984397888, 3.9258685112, 0.0), + Vec3(-11.5888309479, 7.29379606247, 0.0)], + 15: [Vec3(-25.7975006104, 18.4752807617, 0.0), Vec3(12.6321563721, 19.1084594727, 0.0), Vec3(24.8442764282, -4.62582397461, 0.0)], + 16: [Vec3(6.70755004883, -4.16766357422, 0.0), + Vec3(-24.5565567017, -6.02560424805, 0.0), + Vec3(-24.4623031616, 15.6810913086, 0.0), + Vec3(3.93852233887, 17.1640014648, 0.0)], + 17: [Vec3(0.474000930786, -29.1558818817, 0.0), + Vec3(0.460096359253, -16.5713024139, 0.0), + Vec3(27.9419631958, -16.0025310516, 0.0), + Vec3(28.3607826233, -38.4133338928, 0.0), + Vec3(27.2721481323, 0.622072696686, 0.0), + Vec3(-29.8864364624, 2.13151788712, 0.0), + Vec3(-28.907831192, -38.875164032, 0.0), + Vec3(-26.5185241699, -16.1851940155, 0.0), + Vec3(0.0833129882813, -16.7483196259, 0.0)], + 18: [Vec3(-37.6936645508, 4.92616510391, 0.0), + Vec3(-6.09254932404, 3.79619073868, 0.0), + Vec3(-5.81788635254, 16.4960193634, 0.0), + Vec3(6.79872131348, 17.3943042755, 0.0), + Vec3(6.47452545166, 6.80963373184, 0.0), + Vec3(29.4301567078, 4.76448297501, 0.0)], + 19: [Vec3(-11.6953277588, -13.8257198334, -0.00400207517669), + Vec3(-26.5939941406, -38.6423110962, -0.00400207517669), + Vec3(-12.9963378906, -54.1221084595, -0.00400207517669), + Vec3(5.45291900635, -50.0818252563, -0.00400207517669), + Vec3(0.500541687012, -27.6731319427, -0.00400207517669), + Vec3(10.6271972656, -16.9095211029, -0.00400207517669), + Vec3(3.95051574707, -11.6894283295, -0.00400207517669)], + 20: [Vec3(1.44152832031, -18.6059322357, -0.00400207517669), + Vec3(-0.327140808105, -38.2261734009, -0.00400207517669), + Vec3(19.4757766724, -20.1440181732, -0.00400207517669), + Vec3(-13.617980957, -38.8843765259, -0.00400207517669), + Vec3(9.44762420654, -28.4113521576, -0.00400207517669)], + 21: [Vec3(26.8655929565, -13.403295517, -0.00400207517669), + Vec3(32.0896224976, -27.145483017, -0.00400207517669), + Vec3(23.4544830322, -40.3218765259, -0.00400207517669), + Vec3(14.4268798828, -42.4280166626, -0.00400207517669), + Vec3(6.38285827637, -40.3579483032, -0.00400207517669), + Vec3(-2.16972351074, -26.8708248138, -0.00400207517669), + Vec3(-3.16332244873, -17.7787837982, -0.00400207517669), + Vec3(-3.16332244873, -17.7787837982, -0.00400207517669), + Vec3(2.0150680542, -5.54251718521, -0.00400207517669), + Vec3(7.65352630615, -5.11417245865, -0.00400207517669)], + 22: [Vec3(-4.81932353973, 4.0960521698, 0.0), + Vec3(-37.2935218811, -35.963394165, 0.0), + Vec3(-32.3968849182, -49.8670196533, 0.0), + Vec3(-52.6336708069, -33.5889434814, 0.0), + Vec3(-53.0538520813, -21.5930957794, 0.0), + Vec3(-44.2506980896, -27.3775806427, 0.0), + Vec3(-1.58745121956, 3.91203117371, 0.0)], + 23: [Vec3(5.80584430695, 1.62095463276, 0.0), + Vec3(-10.3068342209, -10.9403858185, 0.0), + Vec3(-27.9555549622, 11.9806346893, 0.0), + Vec3(-2.36316990852, -15.246049881, 0.0), + Vec3(-12.8535642624, -3.13337898254, 0.0)], + 24: [Vec3(-12.7202939987, 15.884016037, 0.0), Vec3(16.7396354675, -14.0337085724, 0.0)], + 25: [Vec3(-17.7801113129, -8.74084472656, 0.0), Vec3(-13.1200618744, -9.26202392578, -0.0)], + 26: [Vec3(8.624584198, -10.9699707031, 0.0), Vec3(3.29245567322, -2.59405517578, 0.0)], + 27: [Vec3(12.3031358719, 7.98439788818, 0.0), Vec3(-13.3801307678, 7.98439788818, 0.0)], + 28: [Vec3(-32.1953697205, -85.9077148438, 0.0), + Vec3(-33.518032074, -60.6316223145, 0.0), + Vec3(-25.2015018463, -34.200378418, 0.0), + Vec3(-0.624415278435, -24.7206726074, 0.0)], + 29: [Vec3(2.76529407501, -19.7032775879, 0.0), + Vec3(12.5924119949, -5.15737915039, 0.0), + Vec3(1.32620728016, 16.3902587891, 0.0), + Vec3(-13.4380140305, 18.5373535156, 0.0), + Vec3(-9.65627574921, -6.18496704102, 0.0), + Vec3(-24.4610538483, -31.9492492676, 0.0), + Vec3(-14.1554517746, -43.6148376465, 0.0)], + 30: [Vec3(-0.175471186638, 24.0636901855, 0.0), + Vec3(-18.316400528, 61.1733856201, 0.0), + Vec3(-34.2972984314, 58.8515930176, 0.0), + Vec3(-18.7033634186, 27.5550231934, 0.0)], + 31: [Vec3(25.8017997742, 73.792678833, 0.0), Vec3(11.0946779251, 51.4213256836, 0.0), Vec3(31.6651115417, 43.7239074707, 0.0)], + 32: [Vec3(4.61576557159, 31.6040344238, 0.0), + Vec3(-10.1993589401, -7.03125, 0.0), + Vec3(-4.72947978973, -29.3165893555, 0.0), + Vec3(22.23179245, -28.7750854492, 0.0), + Vec3(11.0534486771, 0.391784667969, 0.0), + Vec3(18.2302970886, 28.4787597656, 0.0)], + 33: [Vec3(8.04011249542, -51.311706543, 0.0), + Vec3(25.7791862488, -46.4603881836, 0.0), + Vec3(42.3690605164, -57.4282226563, 0.0), + Vec3(34.1978569031, -77.1849365234, 0.0)], + 34: [Point3(2.75689697266, -21.3427124023, 0.0), Point3(2.78796386719, 37.0751342773, 0.000232696533203)], + 35: [Point3(7.67395019531, 40.565612793, -0.000492095947266), Point3(78.0889282227, 39.3659667969, -0.00810623168945)], + 36: [Point3(98.1296081543, 38.8174743652, -0.0102634429932), + Point3(104.098999023, 38.9267272949, -0.0109195709229), + Point3(105.774627686, 243.011169434, -0.0125999450684), + Point3(100.351745605, 242.852386475, -0.0125999450684)], + 37: [Point3(152.121673584, 222.440261841, -5.0125999450684), + Point3(249.456481934, 223.478790283, -5.01259994507), + Point3(247.701660156, 233.899627686, -5.01259994507), + Point3(247.01550293, 224.162445068, -5.01259994507)], + 38: [Point3(-1.65826416016, 25.0982055664, 0.025), + Point3(-1.44326782227, 9.02752685547, 0.025), + Point3(-8.76708984375, 11.2831420898, 0.025), + Point3(11.4426574707, 7.11584472656, 0.025), + Point3(2.77236938477, 7.02355957031, 0.025)], + 39: [Point3(-3.69488525391, -1.89245605469, 0.025), + Point3(-5.73229980469, -9.62658691406, 0.025), + Point3(4.14199829102, -10.7971191406, 0.025), + Point3(7.77349853516, 1.58245849609, 0.025)], + 40: [Point3(1.67153930664, 18.4436645508, 0.025), Point3(-7.59463500977, 18.5286254883, 0.025)], + 41: [Point3(0.726699829102, -48.1898803711, -4.99999809265), + Point3(0.787185668945, -74.4460754395, -4.99999809265), + Point3(39.1846923828, -76.5356445313, -4.99999809265), + Point3(0.787185668945, -74.4460754395, -4.99999809265)], + 42: [Vec3(6.0, -6.0, 0.0), + Vec3(6.0, 6.0, 0.0), + Vec3(-6.0, 6.0, 0.0), + Vec3(-6.0, -6.0, 0.0)]}, + 'cashbotMint': {28: [Vec3(-32.1953697205, -85.9077148438, 0.0), + Vec3(-33.518032074, -60.6316223145, 0.0), + Vec3(-25.2015018463, -34.200378418, 0.0), + Vec3(-0.624415278435, -24.7206726074, 0.0)], + 29: [Vec3(2.76529407501, -19.7032775879, 0.0), + Vec3(12.5924119949, -5.15737915039, 0.0), + Vec3(1.32620728016, 16.3902587891, 0.0), + Vec3(-13.4380140305, 18.5373535156, 0.0), + Vec3(-9.65627574921, -6.18496704102, 0.0), + Vec3(-24.4610538483, -31.9492492676, 0.0), + Vec3(-14.1554517746, -43.6148376465, 0.0)], + 30: [Vec3(-0.175471186638, 24.0636901855, 0.0), + Vec3(-18.316400528, 61.1733856201, 0.0), + Vec3(-34.2972984314, 58.8515930176, 0.0), + Vec3(-18.7033634186, 27.5550231934, 0.0)], + 0: [Vec3(-5, 0.0, 0.0), + Vec3(-5, 10.0, 0.0), + Vec3(5.0, 10.0, 0.0), + Vec3(5.0, 0.0, 0.0)], + 1: [Vec3(0.0, 0.0, 0.0), Vec3(-5.77, 10.0, 0.0), Vec3(5.77, 10.0, 0.0)], + 2: [Vec3(-5.77, 10.0, 0.0), + Vec3(5.77, 10.0, 0.0), + Vec3(-5.77, -10.0, 0.0), + Vec3(5.77, -10.0, 0.0)], + 3: [Vec3(-10, 0, 0), Vec3(10, 0, 0)]}, + 'lawOfficeStage': {28: [Vec3(-32.1953697205, -85.9077148438, 0.0), + Vec3(-33.518032074, -60.6316223145, 0.0), + Vec3(-25.2015018463, -34.200378418, 0.0), + Vec3(-0.624415278435, -24.7206726074, 0.0)], + 29: [Vec3(2.76529407501, -19.7032775879, 0.0), + Vec3(12.5924119949, -5.15737915039, 0.0), + Vec3(1.32620728016, 16.3902587891, 0.0), + Vec3(-13.4380140305, 18.5373535156, 0.0), + Vec3(-9.65627574921, -6.18496704102, 0.0), + Vec3(-24.4610538483, -31.9492492676, 0.0), + Vec3(-14.1554517746, -43.6148376465, 0.0)], + 30: [Vec3(-0.175471186638, 24.0636901855, 0.0), + Vec3(-18.316400528, 61.1733856201, 0.0), + Vec3(-34.2972984314, 58.8515930176, 0.0), + Vec3(-18.7033634186, 27.5550231934, 0.0)], + 0: [Vec3(-5, 0.0, 0.0), + Vec3(-5, 10.0, 0.0), + Vec3(5.0, 10.0, 0.0), + Vec3(5.0, 0.0, 0.0)], + 1: [Vec3(0.0, 0.0, 0.0), Vec3(-5.77, 10.0, 0.0), Vec3(5.77, 10.0, 0.0)], + 2: [Vec3(-5.77, 10.0, 0.0), + Vec3(5.77, 10.0, 0.0), + Vec3(-5.77, -10.0, 0.0), + Vec3(5.77, -10.0, 0.0)], + 3: [Vec3(-10, 0, 0), Vec3(10, 0, 0)]}} diff --git a/toontown/suit/RoguesGallery.py b/toontown/suit/RoguesGallery.py new file mode 100755 index 00000000..336c619f --- /dev/null +++ b/toontown/suit/RoguesGallery.py @@ -0,0 +1,183 @@ +from panda3d.core import * +from direct.fsm import StateData +import Suit +import SuitDNA +from toontown.toonbase import ToontownGlobals +import random + +class RoguesGallery(StateData.StateData): + + def __init__(self, rognamestr = None): + StateData.StateData.__init__(self, 'roguesDone') + self.rognamestr = rognamestr + self.left = -1.333 + self.right = 1.333 + self.bottom = -1.0 + self.top = 1.0 + self.sideMargins = 0.1 + self.topMargins = 0.1 + self.xSpaceBetweenDifferentSuits = 0.01 + self.xSpaceBetweenSameSuits = 0.0 + self.ySpaceBetweenSuits = 0.05 + self.labelScale = 1.0 + + def load(self): + if StateData.StateData.load(self): + self.width = self.right - self.left - self.sideMargins * 2.0 + self.height = self.top - self.bottom - self.topMargins * 2.0 + if self.rognamestr == None: + self.numSuitTypes = SuitDNA.suitsPerDept + self.numSuitDepts = len(SuitDNA.suitDepts) + else: + self.numSuitTypes = 1 + self.numSuitDepts = 1 + self.xSpaceBetweenDifferentSuits = 0.0 + self.xSpaceBetweenSameSuits = 0.0 + self.ySpaceBetweenSuits = 0.0 + self.ySuitInc = (self.height + self.ySpaceBetweenSuits) / self.numSuitDepts + self.ySuitMaxAllowed = self.ySuitInc - self.ySpaceBetweenSuits + self.xRowSpace = self.width - (self.numSuitTypes - 1) * self.xSpaceBetweenDifferentSuits - self.numSuitTypes * self.xSpaceBetweenSameSuits + self.__makeGallery() + return + + def unload(self): + if StateData.StateData.unload(self): + self.gallery.removeNode() + del self.suits + del self.actors + + def enter(self): + if StateData.StateData.enter(self): + render.hide() + aspect2d.hide() + self.gallery.reparentTo(render2d) + self.gallery.setMat(base.aspect2d.getMat()) + self.gallery.setPos(0.0, 10.0, 0.0) + base.setBackgroundColor(0.6, 0.6, 0.6) + + def exit(self): + if StateData.StateData.exit(self): + self.stop() + render.show() + aspect2d.show() + self.gallery.reparentTo(hidden) + self.gallery.clearMat() + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + self.ignoreAll() + + def animate(self): + self.load() + for suit in self.actors: + suit.pose('neutral', random.randint(0, suit.getNumFrames('neutral') - 1)) + suit.loop('neutral', 0) + + def stop(self): + self.load() + for suit in self.actors: + suit.pose('neutral', 30) + + def autoExit(self): + self.acceptOnce('mouse1', self.exit) + + def __makeGallery(self): + self.gallery = hidden.attachNewNode('gallery') + self.gallery.setDepthWrite(1) + self.gallery.setDepthTest(1) + self.suits = [] + self.actors = [] + self.text = TextNode('rogues') + self.text.setFont(ToontownGlobals.getInterfaceFont()) + self.text.setAlign(TextNode.ACenter) + self.text.setTextColor(0.0, 0.0, 0.0, 1.0) + self.rowHeight = 0.0 + self.minXScale = None + print "rognamestr='", self.rognamestr, "'\n" + if self.rognamestr == None or len(self.rognamestr) == 0: + for dept in SuitDNA.suitDepts: + self.__makeDept(dept) + + else: + self.suitRow = [] + self.rowWidth = 0.0 + self.__makeSuit(None, None, self.rognamestr) + self.minXScale = self.xRowSpace / self.rowWidth + self.suits.append((self.rowWidth, self.suitRow)) + del self.suitRow + self.__rescaleSuits() + return + + def __makeDept(self, dept): + self.suitRow = [] + self.rowWidth = 0.0 + for type in xrange(self.numSuitTypes): + self.__makeSuit(dept, type) + + xScale = self.xRowSpace / self.rowWidth + if self.minXScale == None or self.minXScale > xScale: + self.minXScale = xScale + self.suits.append((self.rowWidth, self.suitRow)) + del self.suitRow + return + + def __makeSuit(self, dept, type, name = None): + dna = SuitDNA.SuitDNA() + if name != None: + dna.newSuit(name) + else: + dna.newSuitRandom(type + 1, dept) + suit = Suit.Suit() + suit.setStyle(dna) + suit.generateSuit() + suit.pose('neutral', 30) + ll = Point3() + ur = Point3() + suit.update() + suit.calcTightBounds(ll, ur) + suitWidth = ur[0] - ll[0] + suitDepth = ur[1] - ll[1] + suitHeight = ur[2] - ll[2] + self.rowWidth += suitWidth + suitDepth + self.rowHeight = max(self.rowHeight, suitHeight) + suit.reparentTo(self.gallery) + suit.setHpr(180.0, 0.0, 0.0) + profile = Suit.Suit() + profile.setStyle(dna) + profile.generateSuit() + profile.pose('neutral', 30) + profile.reparentTo(self.gallery) + profile.setHpr(90.0, 0.0, 0.0) + self.suitRow.append((type, + suitWidth, + suit, + suitDepth, + profile)) + self.actors.append(suit) + self.actors.append(profile) + return + + def __rescaleSuits(self): + yScale = self.ySuitMaxAllowed / self.rowHeight + scale = min(self.minXScale, yScale) + y = self.top - self.topMargins + self.ySpaceBetweenSuits + for rowWidth, suitRow in self.suits: + rowWidth *= scale + extraSpace = self.xRowSpace - rowWidth + extraSpacePerSuit = extraSpace / (self.numSuitTypes * 2 - 1) + x = self.left + self.sideMargins + y -= self.ySuitInc + for type, width, suit, depth, profile in suitRow: + left = x + width *= scale + suit.setScale(scale) + suit.setPos(x + width / 2.0, 0.0, y) + x += width + self.xSpaceBetweenSameSuits + extraSpacePerSuit + depth *= scale + profile.setScale(scale) + profile.setPos(x + depth / 2.0, 0.0, y) + x += depth + right = x + x += self.xSpaceBetweenDifferentSuits + extraSpacePerSuit + self.text.setText(suit.getName()) + name = self.gallery.attachNewNode(self.text.generate()) + name.setPos((right + left) / 2.0, 0.0, y + (suit.height + self.labelScale * 0.5) * scale) + name.setScale(self.labelScale * scale) diff --git a/toontown/suit/SellbotBossGlobals.py b/toontown/suit/SellbotBossGlobals.py new file mode 100755 index 00000000..e141e874 --- /dev/null +++ b/toontown/suit/SellbotBossGlobals.py @@ -0,0 +1,56 @@ +from panda3d.core import * + +try: + from toontown.coghq.DistributedHealBarrelAI import DistributedHealBarrelAI + from toontown.coghq.DistributedGagBarrelAI import DistributedGagBarrelAI +except ImportError: + DistributedHealBarrelAI = None + DistributedGagBarrelAI = None + +PieToonup = 1 +PieToonupNerfed = 2 +PieDamageMult = 1.0 +PieDamageMultNerfed = 2.0 +AttackMult = 1.0 +AttackMultNerfed = 0.5 +HitCountDamage = 35 +HitCountDamageNerfed = 50 +BarrelDefs = { + 8000: {'type': DistributedHealBarrelAI, + 'pos': Point3(15, 23, 0), + 'hpr': Vec3(-45, 0, 0), + 'rewardPerGrab': 50, + 'rewardPerGrabMax': 0}, + 8001: {'type': DistributedGagBarrelAI, + 'pos': Point3(15, -23, 0), + 'hpr': Vec3(-135, 0, 0), + 'gagLevel': 3, + 'gagLevelMax': 0, + 'gagTrack': 3, + 'rewardPerGrab': 10, + 'rewardPerGrabMax': 0}, + 8002: {'type': DistributedGagBarrelAI, + 'pos': Point3(21, 20, 0), + 'hpr': Vec3(-45, 0, 0), + 'gagLevel': 3, + 'gagLevelMax': 0, + 'gagTrack': 4, + 'rewardPerGrab': 10, + 'rewardPerGrabMax': 0}, + 8003: {'type': DistributedGagBarrelAI, + 'pos': Point3(21, -20, 0), + 'hpr': Vec3(-135, 0, 0), + 'gagLevel': 3, + 'gagLevelMax': 0, + 'gagTrack': 5, + 'rewardPerGrab': 10, + 'rewardPerGrabMax': 0} +} + +def setBarrelAttr(barrel, entId): + for defAttr, defValue in BarrelDefs[entId].iteritems(): + setattr(barrel, defAttr, defValue) + + +BarrelsStartPos = (0, -36, -8) +BarrelsFinalPos = (0, -36, 0) diff --git a/toontown/suit/Suit.py b/toontown/suit/Suit.py new file mode 100755 index 00000000..ca0db861 --- /dev/null +++ b/toontown/suit/Suit.py @@ -0,0 +1,687 @@ +from panda3d.core import * +from direct.actor import Actor +from direct.task.Task import Task +from otp.avatar import Avatar +from toontown.battle import SuitBattleGlobals +from otp.nametag.NametagGroup import NametagGroup +from toontown.toonbase import TTLocalizer, ToontownGlobals +from toontown.suit import SuitGlobals +import SuitDNA, SuitHealthBar, string + +aSize = 6.06 +bSize = 5.29 +cSize = 4.14 +SuitDialogArray = [] +SkelSuitDialogArray = [] +AllSuits = (('walk', 'walk'), ('run', 'walk'), ('neutral', 'neutral')) +AllSuitsMinigame = (('victory', 'victory'), + ('flail', 'flailing'), + ('tug-o-war', 'tug-o-war'), + ('slip-backward', 'slip-backward'), + ('slip-forward', 'slip-forward')) +AllSuitsTutorialBattle = (('lose', 'lose'), ('pie-small-react', 'pie-small'), ('squirt-small-react', 'squirt-small')) +AllSuitsBattle = (('drop-react', 'anvil-drop'), + ('flatten', 'drop'), + ('sidestep-left', 'sidestep-left'), + ('sidestep-right', 'sidestep-right'), + ('squirt-large-react', 'squirt-large'), + ('landing', 'landing'), + ('reach', 'walknreach'), + ('rake-react', 'rake'), + ('hypnotized', 'hypnotize'), + ('soak', 'soak'), + ('lured', 'lured')) +SuitsCEOBattle = (('sit', 'sit'), + ('sit-eat-in', 'sit-eat-in'), + ('sit-eat-loop', 'sit-eat-loop'), + ('sit-eat-out', 'sit-eat-out'), + ('sit-angry', 'sit-angry'), + ('sit-hungry-left', 'leftsit-hungry'), + ('sit-hungry-right', 'rightsit-hungry'), + ('sit-lose', 'sit-lose'), + ('tray-walk', 'tray-walk'), + ('tray-neutral', 'tray-neutral'), + ('sit-lose', 'sit-lose')) +f = (('throw-paper', 'throw-paper', 3.5), ('phone', 'phone', 3.5), ('shredder', 'shredder', 3.5)) +p = (('pencil-sharpener', 'pencil-sharpener', 5), + ('pen-squirt', 'pen-squirt', 5), + ('hold-eraser', 'hold-eraser', 5), + ('finger-wag', 'finger-wag', 5), + ('hold-pencil', 'hold-pencil', 5)) +ym = (('throw-paper', 'throw-paper', 5), + ('golf-club-swing', 'golf-club-swing', 5), + ('magic3', 'magic3', 5), + ('rubber-stamp', 'rubber-stamp', 5), + ('smile', 'smile', 5)) +mm = (('speak', 'speak', 5), + ('effort', 'effort', 5), + ('magic1', 'magic1', 5), + ('pen-squirt', 'fountain-pen', 5), + ('finger-wag', 'finger-wag', 5)) +ds = (('magic1', 'magic1', 5), + ('magic2', 'magic2', 5), + ('throw-paper', 'throw-paper', 5), + ('magic3', 'magic3', 5)) +hh = (('pen-squirt', 'fountain-pen', 7), + ('glower', 'glower', 5), + ('throw-paper', 'throw-paper', 5), + ('magic1', 'magic1', 5), + ('magic3', 'magic3', 5), + ('roll-o-dex', 'roll-o-dex', 5)) +cr = (('pickpocket', 'pickpocket', 5), ('throw-paper', 'throw-paper', 3.5), ('glower', 'glower', 5)) +tbc = (('cigar-smoke', 'cigar-smoke', 8), + ('glower', 'glower', 5), + ('song-and-dance', 'song-and-dance', 8), + ('golf-club-swing', 'golf-club-swing', 5)) +cc = (('speak', 'speak', 5), + ('glower', 'glower', 5), + ('phone', 'phone', 3.5), + ('finger-wag', 'finger-wag', 5)) +tm = (('speak', 'speak', 5), + ('throw-paper', 'throw-paper', 5), + ('pickpocket', 'pickpocket', 5), + ('roll-o-dex', 'roll-o-dex', 5), + ('finger-wag', 'finger-wag', 5)) +nd = (('pickpocket', 'pickpocket', 5), + ('roll-o-dex', 'roll-o-dex', 5), + ('magic3', 'magic3', 5), + ('smile', 'smile', 5)) +gh = (('speak', 'speak', 5), ('pen-squirt', 'fountain-pen', 5), ('rubber-stamp', 'rubber-stamp', 5)) +ms = (('effort', 'effort', 5), + ('throw-paper', 'throw-paper', 5), + ('stomp', 'stomp', 5), + ('quick-jump', 'jump', 6)) +tf = (('phone', 'phone', 5), + ('smile', 'smile', 5), + ('throw-object', 'throw-object', 5), + ('magic3', 'magic3', 5), + ('glower', 'glower', 5)) +m = (('speak', 'speak', 5), + ('magic2', 'magic2', 5), + ('magic1', 'magic1', 5), + ('golf-club-swing', 'golf-club-swing', 5), + ('cigar-smoke', 'cigar-smoke', 8)) +mh = (('magic1', 'magic1', 5), + ('smile', 'smile', 5), + ('golf-club-swing', 'golf-club-swing', 5), + ('song-and-dance', 'song-and-dance', 5)) +sc = (('throw-paper', 'throw-paper', 3.5), ('watercooler', 'watercooler', 5), ('pickpocket', 'pickpocket', 5)) +pp = (('throw-paper', 'throw-paper', 5), ('glower', 'glower', 5), ('finger-wag', 'fingerwag', 5)) +tw = (('throw-paper', 'throw-paper', 3.5), + ('glower', 'glower', 5), + ('magic2', 'magic2', 5), + ('finger-wag', 'finger-wag', 5)) +bc = (('phone', 'phone', 5), ('hold-pencil', 'hold-pencil', 5)) +nc = (('phone', 'phone', 5), ('throw-object', 'throw-object', 5)) +mb = (('magic1', 'magic1', 5), ('throw-paper', 'throw-paper', 3.5)) +ls = (('throw-paper', 'throw-paper', 5), ('throw-object', 'throw-object', 5), ('hold-pencil', 'hold-pencil', 5)) +rb = (('glower', 'glower', 5), ('cigar-smoke', 'cigar-smoke', 8), ('magic1', 'magic1', 5), ('golf-club-swing', 'golf-club-swing', 5)) +bf = (('pickpocket', 'pickpocket', 5), + ('rubber-stamp', 'rubber-stamp', 5), + ('shredder', 'shredder', 3.5), + ('watercooler', 'watercooler', 5)) +b = (('effort', 'effort', 5), + ('throw-paper', 'throw-paper', 5), + ('throw-object', 'throw-object', 5), + ('magic1', 'magic1', 5)) +dt = (('rubber-stamp', 'rubber-stamp', 5), + ('throw-paper', 'throw-paper', 5), + ('speak', 'speak', 5), + ('finger-wag', 'fingerwag', 5), + ('throw-paper', 'throw-paper', 5)) +ac = (('throw-object', 'throw-object', 5), + ('roll-o-dex', 'roll-o-dex', 5), + ('stomp', 'stomp', 5), + ('phone', 'phone', 5), + ('throw-paper', 'throw-paper', 5)) +bs = (('magic1', 'magic1', 5), ('cigar-smoke', 'cigar-smoke', 8), ('throw-paper', 'throw-paper', 5), ('finger-wag', 'fingerwag', 5)) +sd = (('magic2', 'magic2', 5), + ('quick-jump', 'jump', 6), + ('stomp', 'stomp', 5), + ('magic3', 'magic3', 5), + ('hold-pencil', 'hold-pencil', 5), + ('throw-paper', 'throw-paper', 5)) +le = (('speak', 'speak', 5), + ('throw-object', 'throw-object', 5), + ('glower', 'glower', 5), + ('throw-paper', 'throw-paper', 5)) +bw = (('finger-wag', 'fingerwag', 5), + ('cigar-smoke', 'cigar-smoke', 8), + ('gavel', 'gavel', 8), + ('magic1', 'magic1', 5), + ('throw-object', 'throw-object', 5), + ('throw-paper', 'throw-paper', 5)) +if not base.config.GetBool('want-new-cogs', 0): + ModelDict = {'a': ('/models/char/suitA-', 4), + 'b': ('/models/char/suitB-', 4), + 'c': ('/models/char/suitC-', 3.5)} + TutorialModelDict = {'a': ('/models/char/suitA-', 4), + 'b': ('/models/char/suitB-', 4), + 'c': ('/models/char/suitC-', 3.5)} +else: + ModelDict = {'a': ('/models/char/tt_a_ene_cga_', 4), + 'b': ('/models/char/tt_a_ene_cgb_', 4), + 'c': ('/models/char/tt_a_ene_cgc_', 3.5)} + TutorialModelDict = {'a': ('/models/char/tt_a_ene_cga_', 4), + 'b': ('/models/char/tt_a_ene_cgb_', 4), + 'c': ('/models/char/tt_a_ene_cgc_', 3.5)} +HeadModelDict = {'a': ('/models/char/suitA-', 4), + 'b': ('/models/char/suitB-', 4), + 'c': ('/models/char/suitC-', 3.5)} + +SuitParts = ['phase_3.5/models/char/suitA-mod', + 'phase_3.5/models/char/suitB-mod', + 'phase_3.5/models/char/suitC-mod', + 'phase_4/models/char/suitA-heads', + 'phase_4/models/char/suitB-heads', + 'phase_3.5/models/char/suitC-heads'] + +Preloaded = {} + +def loadModels(): + global Preloaded + if not Preloaded: + print 'Preloading suits...' + for filepath in SuitParts: + Preloaded[filepath] = loader.loadModel(filepath) + Preloaded[filepath].flattenMedium() + +def loadTutorialSuit(): + loader.loadModel('phase_3.5/models/char/suitC-mod') + loadDialog(1) + +def loadSuits(level): + loadDialog(level) + +def unloadSuits(level): + #loadSuitModelsAndAnims(level, flag=0) + unloadDialog(level) + +def loadSuitModelsAndAnims(level, flag = 0): + for key in ModelDict.keys(): + model, phase = ModelDict[key] + if flag: + filepath = 'phase_3.5' + model + 'mod' + Preloaded[filepath] = loader.loadModel(filepath) + filepath = 'phase_' + str(phase) + model + 'heads' + Preloaded[filepath] = loader.loadModel(filepath) + +def loadSuitAnims(suit, flag = 1): + if suit in SuitDNA.suitHeadTypes: + try: + animList = eval(suit) + except NameError: + animList = () + + else: + print 'Invalid suit name: ', suit + return -1 + for anim in animList: + phase = 'phase_' + str(anim[2]) + filePrefix = ModelDict[bodyType][0] + animName = filePrefix + anim[1] + if flag: + loader.loadModel(animName) + else: + loader.unloadModel(animName) + + +def loadDialog(level): + global SuitDialogArray + if len(SuitDialogArray) > 0: + return + else: + loadPath = 'phase_3.5/audio/dial/' + SuitDialogFiles = ['COG_VO_grunt', + 'COG_VO_murmur', + 'COG_VO_statement', + 'COG_VO_question', + 'COG_VO_exclaim'] + for file in SuitDialogFiles: + SuitDialogArray.append(base.loadSfx(loadPath + file + '.ogg')) + + SuitDialogArray.append(SuitDialogArray[2]) + + +def loadSkelDialog(): + global SkelSuitDialogArray + if len(SkelSuitDialogArray) > 0: + return + else: + grunt = loader.loadSfx('phase_5/audio/sfx/Skel_COG_VO_grunt.ogg') + murmur = loader.loadSfx('phase_5/audio/sfx/Skel_COG_VO_murmur.ogg') + statement = loader.loadSfx('phase_5/audio/sfx/Skel_COG_VO_statement.ogg') + question = loader.loadSfx('phase_5/audio/sfx/Skel_COG_VO_question.ogg') + exclaim = loader.loadSfx('phase_5/audio/sfx/Skel_COG_VO_exclaim.ogg') + SkelSuitDialogArray = [grunt, + murmur, + statement, + question, + exclaim, + statement] + + +def unloadDialog(level): + global SuitDialogArray + SuitDialogArray = [] + + +def unloadSkelDialog(): + global SkelSuitDialogArray + SkelSuitDialogArray = [] + + +def attachSuitHead(node, suitName): + suitIndex = SuitDNA.suitHeadTypes.index(suitName) + suitDNA = SuitDNA.SuitDNA() + suitDNA.newSuit(suitName) + suit = Suit() + suit.setDNA(suitDNA) + headParts = suit.getHeadParts() + head = node.attachNewNode('head') + for part in headParts: + copyPart = part.copyTo(head) + copyPart.setDepthTest(1) + copyPart.setDepthWrite(1) + + suit.delete() + suit = None + p1 = Point3() + p2 = Point3() + head.calcTightBounds(p1, p2) + d = p2 - p1 + biggest = max(d[0], d[2]) + column = suitIndex % SuitDNA.suitsPerDept + s = (0.2 + column / 100.0) / biggest + pos = -0.14 + (SuitDNA.suitsPerDept - column - 1) / 135.0 + head.setPosHprScale(0, 0, pos, 180, 0, 0, s, s, s) + return head + + +class Suit(Avatar.Avatar): + __module__ = __name__ + medallionColors = {'c': Vec4(0.863, 0.776, 0.769, 1.0), + 's': Vec4(0.843, 0.745, 0.745, 1.0), + 'l': Vec4(0.749, 0.776, 0.824, 1.0), + 'm': Vec4(0.749, 0.769, 0.749, 1.0)} + + def __init__(self): + try: + self.Suit_initialized + return + except: + self.Suit_initialized = 1 + + Avatar.Avatar.__init__(self) + self.setFont(ToontownGlobals.getSuitFont()) + self.nametag.setSpeechFont(ToontownGlobals.getSuitFont()) + self.setPlayerType(NametagGroup.CCSuit) + self.setPickable(1) + self.leftHand = None + self.rightHand = None + self.shadowJoint = None + self.nametagJoint = None + self.headParts = [] + self.healthBar = SuitHealthBar.SuitHealthBar() + self.isDisguised = 0 + self.isWaiter = 0 + self.isRental = 0 + + def delete(self): + try: + self.Suit_deleted + except: + self.Suit_deleted = 1 + if self.leftHand: + self.leftHand.removeNode() + self.leftHand = None + if self.rightHand: + self.rightHand.removeNode() + self.rightHand = None + if self.shadowJoint: + self.shadowJoint.removeNode() + self.shadowJoint = None + if self.nametagJoint: + self.nametagJoint.removeNode() + self.nametagJoint = None + for part in self.headParts: + part.removeNode() + + self.headParts = [] + self.healthBar.delete() + Avatar.Avatar.delete(self) + + def setHeight(self, height): + Avatar.Avatar.setHeight(self, height) + self.nametag3d.setPos(0, 0, height + 1.0) + + def getRadius(self): + return 2 + + def setDNAString(self, dnaString): + self.dna = SuitDNA.SuitDNA() + self.dna.makeFromNetString(dnaString) + self.setDNA(self.dna) + + def setDNA(self, dna): + if self.style: + return + + self.style = dna + self.generateSuit() + self.initializeDropShadow() + self.initializeNametag3d() + + def generateSuit(self): + dna = self.style + self.headParts = [] + self.headColor = None + self.headTexture = None + self.loseActor = None + self.isSkeleton = 0 + + if dna.name in SuitGlobals.suitProperties: + properties = SuitGlobals.suitProperties[dna.name] + self.scale = properties[SuitGlobals.SCALE_INDEX] + self.handColor = properties[SuitGlobals.HAND_COLOR_INDEX] + + if dna.name == 'cc': + self.headColor = SuitGlobals.ColdCallerHead + + self.generateBody() + + if properties[SuitGlobals.HEAD_TEXTURE_INDEX]: + self.headTexture = properties[SuitGlobals.HEAD_TEXTURE_INDEX] + + for head in properties[SuitGlobals.HEADS_INDEX]: + self.generateHead(head) + + self.setHeight(properties[SuitGlobals.HEIGHT_INDEX]) + + self.setName(SuitBattleGlobals.SuitAttributes[dna.name]['name']) + self.getGeomNode().setScale(self.scale) + self.generateHealthBar() + self.generateCorporateMedallion() + + def generateBody(self): + global Preloaded + animDict = self.generateAnimDict() + filePrefix, bodyPhase = ModelDict[self.style.body] + filepath = 'phase_3.5' + filePrefix + 'mod' + self.loadModel(Preloaded[filepath], copy = True) + self.loadAnims(animDict) + self.setSuitClothes() + + def generateAnimDict(self): + animDict = {} + filePrefix, bodyPhase = ModelDict[self.style.body] + for anim in AllSuits: + animDict[anim[0]] = 'phase_' + str(bodyPhase) + filePrefix + anim[1] + + for anim in AllSuitsMinigame: + animDict[anim[0]] = 'phase_4' + filePrefix + anim[1] + + for anim in AllSuitsTutorialBattle: + filePrefix, bodyPhase = TutorialModelDict[self.style.body] + animDict[anim[0]] = 'phase_' + str(bodyPhase) + filePrefix + anim[1] + + for anim in AllSuitsBattle: + animDict[anim[0]] = 'phase_5' + filePrefix + anim[1] + + if self.style.body == 'a': + animDict['neutral'] = 'phase_4/models/char/suitA-neutral' + for anim in SuitsCEOBattle: + animDict[anim[0]] = 'phase_12/models/char/suitA-' + anim[1] + elif self.style.body == 'b': + animDict['neutral'] = 'phase_4/models/char/suitB-neutral' + for anim in SuitsCEOBattle: + animDict[anim[0]] = 'phase_12/models/char/suitB-' + anim[1] + elif self.style.body == 'c': + animDict['neutral'] = 'phase_3.5/models/char/suitC-neutral' + for anim in SuitsCEOBattle: + animDict[anim[0]] = 'phase_12/models/char/suitC-' + anim[1] + + try: + animList = eval(self.style.name) + except NameError: + animList = () + + for anim in animList: + phase = 'phase_' + str(anim[2]) + animDict[anim[0]] = phase + filePrefix + anim[1] + + return animDict + + def initializeBodyCollisions(self, collIdStr): + Avatar.Avatar.initializeBodyCollisions(self, collIdStr) + if not self.ghostMode: + self.collNode.setCollideMask(self.collNode.getIntoCollideMask() | ToontownGlobals.PieBitmask) + + def setSuitClothes(self, modelRoot = None): + if not modelRoot: + modelRoot = self + dept = self.style.dept + phase = 3.5 + + torsoTex = loader.loadTexture('phase_%s/maps/%s_blazer.jpg' % (phase, dept)) + torsoTex.setMinfilter(Texture.FTLinearMipmapLinear) + torsoTex.setMagfilter(Texture.FTLinear) + legTex = loader.loadTexture('phase_%s/maps/%s_leg.jpg' % (phase, dept)) + legTex.setMinfilter(Texture.FTLinearMipmapLinear) + legTex.setMagfilter(Texture.FTLinear) + armTex = loader.loadTexture('phase_%s/maps/%s_sleeve.jpg' % (phase, dept)) + armTex.setMinfilter(Texture.FTLinearMipmapLinear) + armTex.setMagfilter(Texture.FTLinear) + modelRoot.find('**/torso').setTexture(torsoTex, 1) + modelRoot.find('**/arms').setTexture(armTex, 1) + modelRoot.find('**/legs').setTexture(legTex, 1) + modelRoot.find('**/hands').setColorScale(self.handColor) + self.leftHand = self.find('**/joint_Lhold') + self.rightHand = self.find('**/joint_Rhold') + self.shadowJoint = self.find('**/joint_shadow') + self.nametagJoint = self.find('**/joint_nameTag') + + def makeWaiter(self, modelRoot = None): + if not modelRoot: + modelRoot = self + self.isWaiter = 1 + torsoTex = loader.loadTexture('phase_3.5/maps/waiter_m_blazer.jpg') + torsoTex.setMinfilter(Texture.FTLinearMipmapLinear) + torsoTex.setMagfilter(Texture.FTLinear) + legTex = loader.loadTexture('phase_3.5/maps/waiter_m_leg.jpg') + legTex.setMinfilter(Texture.FTLinearMipmapLinear) + legTex.setMagfilter(Texture.FTLinear) + armTex = loader.loadTexture('phase_3.5/maps/waiter_m_sleeve.jpg') + armTex.setMinfilter(Texture.FTLinearMipmapLinear) + armTex.setMagfilter(Texture.FTLinear) + modelRoot.find('**/torso').setTexture(torsoTex, 1) + modelRoot.find('**/arms').setTexture(armTex, 1) + modelRoot.find('**/legs').setTexture(legTex, 1) + + def makeRentalSuit(self, suitType, modelRoot = None): + if not modelRoot: + modelRoot = self.getGeomNode() + if suitType == 's': + torsoTex = loader.loadTexture('phase_3.5/maps/tt_t_ene_sellbotRental_blazer.jpg') + legTex = loader.loadTexture('phase_3.5/maps/tt_t_ene_sellbotRental_leg.jpg') + armTex = loader.loadTexture('phase_3.5/maps/tt_t_ene_sellbotRental_sleeve.jpg') + handTex = loader.loadTexture('phase_3.5/maps/tt_t_ene_sellbotRental_hand.jpg') + else: + self.notify.warning('No rental suit for cog type %s' % suitType) + return + self.isRental = 1 + modelRoot.find('**/torso').setTexture(torsoTex, 1) + modelRoot.find('**/arms').setTexture(armTex, 1) + modelRoot.find('**/legs').setTexture(legTex, 1) + modelRoot.find('**/hands').setTexture(handTex, 1) + + def generateHead(self, headType): + filePrefix, phase = ModelDict[self.style.body] + filepath = 'phase_' + str(phase) + filePrefix + 'heads' + headModel = NodePath('cog_head') + Preloaded[filepath].copyTo(headModel) + headReferences = headModel.findAllMatches('**/' + headType) + for i in xrange(0, headReferences.getNumPaths()): + headPart = self.instance(headReferences.getPath(i), 'modelRoot', 'joint_head') + if self.headTexture: + headTex = loader.loadTexture('phase_' + str(phase) + '/maps/' + self.headTexture) + headTex.setMinfilter(Texture.FTLinearMipmapLinear) + headTex.setMagfilter(Texture.FTLinear) + headPart.setTexture(headTex, 1) + if self.headColor: + headPart.setColor(self.headColor) + headPart.flattenStrong() + self.headParts.append(headPart) + headModel.removeNode() + + def generateCorporateTie(self, modelPath = None): + if not modelPath: + modelPath = self + dept = self.style.dept + tie = modelPath.find('**/tie') + if tie.isEmpty(): + self.notify.warning('skelecog has no tie model!!!') + return + if dept == 'c': + tieTex = loader.loadTexture('phase_5/maps/cog_robot_tie_boss.jpg') + elif dept == 's': + tieTex = loader.loadTexture('phase_5/maps/cog_robot_tie_sales.jpg') + elif dept == 'l': + tieTex = loader.loadTexture('phase_5/maps/cog_robot_tie_legal.jpg') + elif dept == 'm': + tieTex = loader.loadTexture('phase_5/maps/cog_robot_tie_money.jpg') + tieTex.setMinfilter(Texture.FTLinearMipmapLinear) + tieTex.setMagfilter(Texture.FTLinear) + tie.setTexture(tieTex, 1) + + def generateCorporateMedallion(self): + icons = loader.loadModel('phase_3/models/gui/cog_icons') + dept = self.style.dept + chestNull = self.find('**/joint_attachMeter') + if dept in SuitDNA.suitDeptModelPaths: + self.corpMedallion = icons.find(SuitDNA.suitDeptModelPaths[dept]).copyTo(chestNull) + self.corpMedallion.setPosHprScale(0.02, 0.05, 0.04, 180.0, 0.0, 0.0, 0.51, 0.51, 0.51) + self.corpMedallion.setColor(self.medallionColors[dept]) + icons.removeNode() + + def generateHealthBar(self): + self.healthBar.generate() + self.healthBar.geom.reparentTo(self.find('**/joint_attachMeter')) + self.healthBar.geom.setScale(3.0) + + def resetHealthBarForSkele(self): + self.healthBar.geom.setPos(0.0, 0.1, 0.0) + + def updateHealthBar(self, hp, forceUpdate = 0): + if hp > self.currHP: + hp = self.currHP + self.currHP -= hp + self.healthBar.update(float(self.currHP) / float(self.maxHP)) + + def getLoseActor(self): + if self.loseActor == None: + if not self.isSkeleton: + filePrefix, phase = TutorialModelDict[self.style.body] + loseModel = 'phase_' + str(phase) + filePrefix + 'lose-mod' + loseAnim = 'phase_' + str(phase) + filePrefix + 'lose' + self.loseActor = Actor.Actor(loseModel, {'lose': loseAnim}) + loseNeck = self.loseActor.find('**/joint_head') + for part in self.headParts: + part.instanceTo(loseNeck) + + if self.isWaiter: + self.makeWaiter(self.loseActor) + else: + self.setSuitClothes(self.loseActor) + else: + loseModel = 'phase_5/models/char/cog' + string.upper(self.style.body) + '_robot-lose-mod' + filePrefix, phase = TutorialModelDict[self.style.body] + loseAnim = 'phase_' + str(phase) + filePrefix + 'lose' + self.loseActor = Actor.Actor(loseModel, {'lose': loseAnim}) + self.generateCorporateTie(self.loseActor) + self.loseActor.setScale(self.scale) + self.loseActor.setPos(self.getPos()) + self.loseActor.setHpr(self.getHpr()) + shadowJoint = self.loseActor.find('**/joint_shadow') + dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') + dropShadow.setScale(0.45) + dropShadow.setColor(0.0, 0.0, 0.0, 0.5) + dropShadow.reparentTo(shadowJoint) + return self.loseActor + + def cleanupLoseActor(self): + self.notify.debug('cleanupLoseActor()') + if self.loseActor != None: + self.notify.debug('cleanupLoseActor() - got one') + self.loseActor.cleanup() + self.loseActor = None + return + + def makeSkeleton(self): + model = 'phase_5/models/char/cog' + string.upper(self.style.body) + '_robot-zero' + anims = self.generateAnimDict() + anim = self.getCurrentAnim() + dropShadow = self.dropShadow + if not dropShadow.isEmpty(): + dropShadow.reparentTo(hidden) + self.removePart('modelRoot') + self.loadModel(model) + self.loadAnims(anims) + self.getGeomNode().setScale(self.scale * 1.0173) + self.generateHealthBar() + self.generateCorporateMedallion() + self.generateCorporateTie() + self.setHeight(self.height) + parts = self.findAllMatches('**/pPlane*') + for partNum in xrange(0, parts.getNumPaths()): + bb = parts.getPath(partNum) + bb.setTwoSided(1) + + self.setName(TTLocalizer.Skeleton) + nameInfo = TTLocalizer.SuitBaseNameWithLevel % {'name': self.name, + 'dept': self.getStyleDept(), + 'level': self.getActualLevel()} + self.setDisplayName(nameInfo) + self.leftHand = self.find('**/joint_Lhold') + self.rightHand = self.find('**/joint_Rhold') + self.shadowJoint = self.find('**/joint_shadow') + self.nametagNull = self.find('**/joint_nameTag') + if not dropShadow.isEmpty(): + dropShadow.setScale(0.75) + if not self.shadowJoint.isEmpty(): + dropShadow.reparentTo(self.shadowJoint) + self.loop(anim) + self.isSkeleton = 1 + + def getHeadParts(self): + return self.headParts + + def getRightHand(self): + return self.rightHand + + def getLeftHand(self): + return self.leftHand + + def getShadowJoint(self): + return self.shadowJoint + + def getNametagJoints(self): + return [] + + def getDialogueArray(self): + if self.isSkeleton: + loadSkelDialog() + return SkelSuitDialogArray + else: + return SuitDialogArray + + def getTypeText(self): + if self.virtual: + return TTLocalizer.CogPanelVirtual + elif self.isWaiter: + return TTLocalizer.CogPanelWaiter + elif self.skeleRevives: + return TTLocalizer.CogPanelRevives % (self.skeleRevives + 1) + elif self.isSkelecog: + return TTLocalizer.CogPanelSkeleton + return '' \ No newline at end of file diff --git a/toontown/suit/SuitAvatarPanel.py b/toontown/suit/SuitAvatarPanel.py new file mode 100755 index 00000000..e3b7effc --- /dev/null +++ b/toontown/suit/SuitAvatarPanel.py @@ -0,0 +1,71 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from direct.showbase import DirectObject +from otp.avatar import Avatar +from direct.distributed import DistributedObject +import SuitDNA +from toontown.toonbase import TTLocalizer +from otp.avatar import AvatarPanel +from toontown.friends import FriendsListPanel + +class SuitAvatarPanel(AvatarPanel.AvatarPanel): + currentAvatarPanel = None + + def __init__(self, avatar): + AvatarPanel.AvatarPanel.__init__(self, avatar, FriendsListPanel=FriendsListPanel) + self.avName = avatar.getName() + gui = loader.loadModel('phase_3.5/models/gui/suit_detail_panel') + self.frame = DirectFrame(geom=gui.find('**/avatar_panel'), geom_scale=0.21, geom_pos=(0, 0, 0.02), relief=None, pos=(1.1, 100, 0.525)) + disabledImageColor = Vec4(1, 1, 1, 0.4) + text0Color = Vec4(1, 1, 1, 1) + text1Color = Vec4(0.5, 1, 0.5, 1) + text2Color = Vec4(1, 1, 0.5, 1) + text3Color = Vec4(1, 1, 1, 0.2) + self.head = self.frame.attachNewNode('head') + for part in avatar.headParts: + copyPart = part.copyTo(self.head) + copyPart.setDepthTest(1) + copyPart.setDepthWrite(1) + + p1 = Point3() + p2 = Point3() + self.head.calcTightBounds(p1, p2) + d = p2 - p1 + biggest = max(d[0], d[1], d[2]) + s = 0.3 / biggest + self.head.setPosHprScale(0, 0, 0, 180, 0, 0, s, s, s) + self.nameLabel = DirectLabel(parent=self.frame, pos=(0.0125, 0, 0.36), relief=None, text=self.avName, text_font=avatar.getFont(), text_fg=Vec4(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.047, text_wordwrap=7.5, text_shadow=(1, 1, 1, 1)) + level = avatar.getActualLevel() + dept = SuitDNA.getSuitDeptFullname(avatar.dna.name) + self.levelLabel = DirectLabel(parent=self.frame, pos=(0, 0, -0.1), relief=None, text=TTLocalizer.AvatarPanelCogLevel % level, text_font=avatar.getFont(), text_align=TextNode.ACenter, text_fg=Vec4(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.05, text_wordwrap=8.0) + corpIcon = avatar.corpMedallion.copyTo(hidden) + corpIcon.iPosHprScale() + self.corpIcon = DirectLabel(parent=self.frame, geom=corpIcon, geom_scale=0.13, pos=(0, 0, -0.175), relief=None) + corpIcon.removeNode() + self.deptLabel = DirectLabel(parent=self.frame, pos=(0, 0, -0.28), relief=None, text=dept, text_font=avatar.getFont(), text_align=TextNode.ACenter, text_fg=Vec4(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.05, text_wordwrap=8.0) + self.closeButton = DirectButton(parent=self.frame, relief=None, pos=(0.0, 0, -0.36), text=TTLocalizer.AvatarPanelCogDetailClose, text_font=avatar.getFont(), text0_fg=Vec4(0, 0, 0, 1), text1_fg=Vec4(0.5, 0, 0, 1), text2_fg=Vec4(1, 0, 0, 1), text_pos=(0, 0), text_scale=0.05, command=self.__handleClose) + gui.removeNode() + menuX = -0.05 + menuScale = 0.064 + base.localAvatar.obscureFriendsListButton(1) + self.frame.show() + messenger.send('avPanelDone') + self.frame.reparentTo(base.a2dTopRight) + self.frame.setPos(-0.25, 0, -0.5) + + def cleanup(self): + if self.frame == None: + return + self.frame.destroy() + del self.frame + self.frame = None + self.head.removeNode() + del self.head + base.localAvatar.obscureFriendsListButton(-1) + AvatarPanel.AvatarPanel.cleanup(self) + return + + def __handleClose(self): + self.cleanup() + AvatarPanel.currentAvatarPanel = None + return diff --git a/toontown/suit/SuitBase.py b/toontown/suit/SuitBase.py new file mode 100755 index 00000000..58ea1b2d --- /dev/null +++ b/toontown/suit/SuitBase.py @@ -0,0 +1,103 @@ +import SuitDNA +from libpandadna import * +import SuitTimings +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from panda3d.core import * +from pandac.PandaModules import Point3 +from toontown.battle import SuitBattleGlobals +from toontown.toonbase import TTLocalizer + + +TIME_BUFFER_PER_WPT = 0.25 +TIME_DIVISOR = 100 +DISTRIBUTE_TASK_CREATION = 0 + +class SuitBase: + notify = DirectNotifyGlobal.directNotify.newCategory('SuitBase') + + def __init__(self): + self.dna = None + self.level = 0 + self.maxHP = 10 + self.currHP = 10 + self.isSkelecog = 0 + self.isWaiter = 0 + return + + def delete(self): + if hasattr(self, 'legList'): + del self.legList + + def getCurrHp(self): + if hasattr(self, 'currHP') and self.currHP: + return self.currHP + else: + self.notify.error('currHP is None') + return 'unknown' + + def getMaxHp(self): + if hasattr(self, 'maxHP') and self.maxHP: + return self.maxHP + else: + self.notify.error('maxHP is None') + return 'unknown' + + def getStyleName(self): + if hasattr(self, 'dna') and self.dna: + return self.dna.name + else: + self.notify.error('called getStyleName() before dna was set!') + return 'unknown' + + def getStyleDept(self): + if hasattr(self, 'dna') and self.dna: + return SuitDNA.getDeptFullname(self.dna.dept) + else: + self.notify.error('called getStyleDept() before dna was set!') + return 'unknown' + + def getLevel(self): + return self.level + + def setLevel(self, level): + self.level = level + nameWLevel = TTLocalizer.SuitBaseNameWithLevel % {'name': self.name, + 'dept': self.getStyleDept(), + 'level': self.getActualLevel()} + self.setDisplayName(nameWLevel) + attributes = SuitBattleGlobals.SuitAttributes[self.dna.name] + self.maxHP = attributes['hp'][self.level] + self.currHP = self.maxHP + + def getSkelecog(self): + return self.isSkelecog + + def setSkelecog(self, flag): + self.isSkelecog = flag + + def setWaiter(self, flag): + self.isWaiter = flag + + def getActualLevel(self): + if hasattr(self, 'dna'): + return SuitBattleGlobals.getActualFromRelativeLevel(self.getStyleName(), self.level) + 1 + else: + self.notify.warning('called getActualLevel with no DNA, returning 1 for level') + return 1 + + def setPath(self, path): + self.path = path + self.pathLength = self.path.getNumPoints() + + def getPath(self): + return self.path + + def printPath(self): + print '%d points in path' % self.pathLength + for currPathPt in xrange(self.pathLength): + indexVal = self.path.getPointIndex(currPathPt) + print '\t', self.sp.dnaStore.getSuitPointWithIndex(indexVal) + + def makeLegList(self): + self.legList = SuitLegList(self.path, self.sp.dnaStore) diff --git a/toontown/suit/SuitDNA.py b/toontown/suit/SuitDNA.py new file mode 100755 index 00000000..62b039b1 --- /dev/null +++ b/toontown/suit/SuitDNA.py @@ -0,0 +1,293 @@ +import random +from panda3d.core import * +from direct.directnotify.DirectNotifyGlobal import * +from toontown.toonbase import TTLocalizer, ToontownGlobals +import random +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.PyDatagramIterator import PyDatagramIterator +notify = directNotify.newCategory('SuitDNA') +suitHeadTypes = ['f', + 'p', + 'ym', + 'mm', + 'ds', + 'hh', + 'cr', + 'tbc', + 'bf', + 'b', + 'dt', + 'ac', + 'bs', + 'sd', + 'le', + 'bw', + 'sc', + 'pp', + 'tw', + 'bc', + 'nc', + 'mb', + 'ls', + 'rb', + 'cc', + 'tm', + 'nd', + 'gh', + 'ms', + 'tf', + 'm', + 'mh'] +suitATypes = ['ym', + 'hh', + 'tbc', + 'dt', + 'bs', + 'le', + 'bw', + 'pp', + 'nc', + 'rb', + 'nd', + 'tf', + 'm', + 'mh'] +suitBTypes = ['p', + 'ds', + 'b', + 'ac', + 'sd', + 'bc', + 'ls', + 'tm', + 'ms'] +suitCTypes = ['f', + 'mm', + 'cr', + 'bf', + 'sc', + 'tw', + 'mb', + 'cc', + 'gh'] +suitDepts = ['c', + 'l', + 'm', + 's'] +suitDeptZones = [ToontownGlobals.BossbotHQ, + ToontownGlobals.LawbotHQ, + ToontownGlobals.CashbotHQ, + ToontownGlobals.SellbotHQ] +suitDeptFullnames = {'c': TTLocalizer.Bossbot, + 'l': TTLocalizer.Lawbot, + 'm': TTLocalizer.Cashbot, + 's': TTLocalizer.Sellbot} +suitDeptFullnamesP = {'c': TTLocalizer.BossbotP, + 'l': TTLocalizer.LawbotP, + 'm': TTLocalizer.CashbotP, + 's': TTLocalizer.SellbotP} +suitDeptFilenames = {'c': 'boss', + 'l': 'law', + 'm': 'cash', + 's': 'sell' +} +suitDeptModelPaths = {'c': '**/CorpIcon', + 0: '**/CorpIcon', + 'l': '**/LegalIcon', + 1: '**/LegalIcon', + 'm': '**/MoneyIcon', + 2: '**/MoneyIcon', + 's': '**/SalesIcon', + 3: '**/SalesIcon'} +corpPolyColor = VBase4(0.95, 0.75, 0.75, 1.0) +legalPolyColor = VBase4(0.75, 0.75, 0.95, 1.0) +moneyPolyColor = VBase4(0.65, 0.95, 0.85, 1.0) +salesPolyColor = VBase4(0.95, 0.75, 0.95, 1.0) +suitsPerLevel = [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1] +suitsPerDept = 8 +goonTypes = ['pg', 'sg'] + +def getSuitBodyType(name): + if name in suitATypes: + return 'a' + elif name in suitBTypes: + return 'b' + elif name in suitCTypes: + return 'c' + else: + print 'Unknown body type for suit name: ', name + + +def getSuitDept(name): + index = suitHeadTypes.index(name) + if index < suitsPerDept: + return suitDepts[0] + elif index < suitsPerDept * 2: + return suitDepts[1] + elif index < suitsPerDept * 3: + return suitDepts[2] + elif index < suitsPerDept * 4: + return suitDepts[3] + else: + print 'Unknown dept for suit name: ', name + return None + return None + + +def getDeptFullname(dept): + return suitDeptFullnames[dept] + + +def getDeptFullnameP(dept): + return suitDeptFullnamesP[dept] + + +def getSuitDeptFullname(name): + return suitDeptFullnames[getSuitDept(name)] + + +def getSuitType(name): + index = suitHeadTypes.index(name) + return index % suitsPerDept + 1 + + +def getSuitName(deptIndex, typeIndex): + return suitHeadTypes[(suitsPerDept*deptIndex) + typeIndex] + + +def getRandomSuitType(level, rng = random): + return random.randint(max(level - 4, 1), min(level, 8)) + + +def getRandomSuitByDept(dept): + deptNumber = suitDepts.index(dept) + return suitHeadTypes[suitsPerDept * deptNumber + random.randint(0, 7)] + +def getSuitsInDept(dept): + start = dept * suitsPerDept + end = start + suitsPerDept + return suitHeadTypes[start:end] + +class SuitDNA: + + def __init__(self, str = None, type = None, dna = None, r = None, b = None, g = None): + if str != None: + self.makeFromNetString(str) + elif type != None: + if type == 's': + self.newSuit() + else: + self.type = 'u' + return + + def __str__(self): + if self.type == 's': + return 'type = %s\nbody = %s, dept = %s, name = %s' % ('suit', + self.body, + self.dept, + self.name) + elif self.type == 'b': + return 'type = boss cog\ndept = %s' % self.dept + else: + return 'type undefined' + + def makeNetString(self): + dg = PyDatagram() + dg.addFixedString(self.type, 1) + if self.type == 's': + dg.addFixedString(self.name, 3) + dg.addFixedString(self.dept, 1) + elif self.type == 'b': + dg.addFixedString(self.dept, 1) + elif self.type == 'u': + notify.error('undefined avatar') + else: + notify.error('unknown avatar type: ', self.type) + return dg.getMessage() + + def makeFromNetString(self, string): + dg = PyDatagram(string) + dgi = PyDatagramIterator(dg) + self.type = dgi.getFixedString(1) + if self.type == 's': + self.name = dgi.getFixedString(3) + self.dept = dgi.getFixedString(1) + self.body = getSuitBodyType(self.name) + elif self.type == 'b': + self.dept = dgi.getFixedString(1) + else: + notify.error('unknown avatar type: ', self.type) + return None + + def __defaultGoon(self): + self.type = 'g' + self.name = goonTypes[0] + + def __defaultSuit(self): + self.type = 's' + self.name = 'ds' + self.dept = getSuitDept(self.name) + self.body = getSuitBodyType(self.name) + + def newSuit(self, name = None): + if name == None: + self.__defaultSuit() + else: + self.type = 's' + self.name = name + self.dept = getSuitDept(self.name) + self.body = getSuitBodyType(self.name) + return + + def newBossCog(self, dept): + self.type = 'b' + self.dept = dept + + def newSuitRandom(self, level = None, dept = None): + self.type = 's' + if level == None: + level = random.choice(range(1, len(suitsPerLevel))) + elif level < 0 or level > len(suitsPerLevel): + notify.error('Invalid suit level: %d' % level) + if dept == None: + dept = random.choice(suitDepts) + self.dept = dept + index = suitDepts.index(dept) + base = index * suitsPerDept + offset = 0 + if level > 1: + for i in xrange(1, level): + offset = offset + suitsPerLevel[i - 1] + + bottom = base + offset + top = bottom + suitsPerLevel[level - 1] + self.name = suitHeadTypes[random.choice(range(bottom, top))] + self.body = getSuitBodyType(self.name) + return + + def newGoon(self, name = None): + if type == None: + self.__defaultGoon() + else: + self.type = 'g' + if name in goonTypes: + self.name = name + else: + notify.error('unknown goon type: ', name) + return + + def getType(self): + if self.type == 's': + type = 'suit' + elif self.type == 'b': + type = 'boss' + else: + notify.error('Invalid DNA type: ', self.type) + return type diff --git a/toontown/suit/SuitDialog.py b/toontown/suit/SuitDialog.py new file mode 100755 index 00000000..fe082479 --- /dev/null +++ b/toontown/suit/SuitDialog.py @@ -0,0 +1,26 @@ +from direct.directnotify import DirectNotifyGlobal +import random + +from otp.otpbase import OTPLocalizer + + +notify = DirectNotifyGlobal.directNotify.newCategory('SuitDialog') + + +def getBrushOffIndex(suitName): + if suitName in SuitBrushOffs: + brushoffs = SuitBrushOffs[suitName] + else: + brushoffs = SuitBrushOffs[None] + return random.randrange(len(brushoffs)) + + +def getBrushOffText(suitName, index): + if suitName in SuitBrushOffs: + brushoffs = SuitBrushOffs[suitName] + else: + brushoffs = SuitBrushOffs[None] + return brushoffs[index] + + +SuitBrushOffs = OTPLocalizer.SuitBrushOffs diff --git a/toontown/suit/SuitGlobals.py b/toontown/suit/SuitGlobals.py new file mode 100755 index 00000000..0af7b59d --- /dev/null +++ b/toontown/suit/SuitGlobals.py @@ -0,0 +1,53 @@ +# SuitGlobals are used to set the appearance of Cogs. +from toontown.suit import SuitDNA +from pandac.PandaModules import VBase4 + +SCALE_INDEX = 0 # The scale of the cog +HAND_COLOR_INDEX = 1 # The hand color +HEADS_INDEX = 2 # A list of heads +HEAD_TEXTURE_INDEX = 3 # The texture to use for the head +HEIGHT_INDEX = 4 # The height of the cog + +aSize = 6.06 # Size of body type 'a' +bSize = 5.29 # Size of body type 'b' +cSize = 4.14 # Size of body type 'c' + +ColdCallerHead = VBase4(0.25, 0.35, 1.0, 1.0) # Head used by Cold Caller + + # Bossbots +suitProperties = {'f': (4.0 / cSize, SuitDNA.corpPolyColor, ['flunky', 'glasses'], '', 4.88), + 'p': (3.35 / bSize, SuitDNA.corpPolyColor, ['pencilpusher'], '', 5.0), + 'ym': (4.125 / aSize, SuitDNA.corpPolyColor, ['yesman'], '', 5.28), + 'mm': (2.5 / cSize, SuitDNA.corpPolyColor, ['micromanager'], '', 3.25), + 'ds': (4.5 / bSize, VBase4(0.8, 0.7, 0.7, 1.0), ['downsizer', 'hatjp187187'], '', 6.08), + 'hh': (6.5 / aSize, SuitDNA.corpPolyColor, ['headhunter'], '', 7.45), + 'cr': (6.75 / cSize, VBase4(0.85, 0.55, 0.55, 1.0), ['flunky'], 'corporate-raider.jpg', 8.23), + 'tbc': (7.0 / aSize, VBase4(0.75, 0.95, 0.75, 1.0), ['bigcheese'], '', 9.34), + # Lawbots + 'bf': (4.0 / cSize, SuitDNA.legalPolyColor, ['tightwad'], 'bottom-feeder.jpg', 4.81), + 'b': (4.375 / bSize, VBase4(0.95, 0.95, 1.0, 1.0), ['movershaker'], 'blood-sucker.jpg', 6.17), + 'dt': (4.25 / aSize, SuitDNA.legalPolyColor, ['twoface'], 'double-talker.jpg', 5.63), + 'ac': (4.35 / bSize, SuitDNA.legalPolyColor, ['ambulancechaser'], '', 6.39), + 'bs': (4.5 / aSize, SuitDNA.legalPolyColor, ['backstabber'], '', 6.71), + 'sd': (5.65 / bSize, VBase4(0.8, 0.9, 0.7, 1.0), ['spindoctor', 'scopejp187187', 'bandjp187187'], '', 7.9), + 'le': (7.125 / aSize, VBase4(0.25, 0.25, 0.5, 1.0), ['legaleagle'], '', 8.27), + 'bw': (7.0 / aSize, SuitDNA.legalPolyColor, ['bigwig'], '', 8.69), + # Cashbots + 'sc': (3.6 / cSize, SuitDNA.moneyPolyColor, ['coldcaller'], '', 4.77), + 'pp': (3.55 / aSize, VBase4(1.0, 0.5, 0.6, 1.0), ['pennypincher'], '', 5.26), + 'tw': (4.5 / cSize, SuitDNA.moneyPolyColor, ['tightwad'], '', 5.41), + 'bc': (4.4 / bSize, SuitDNA.moneyPolyColor, ['beancounter'], '', 5.95), + 'nc': (5.25 / aSize, SuitDNA.moneyPolyColor, ['numbercruncher'], '', 7.22), + 'mb': (5.3 / cSize, SuitDNA.moneyPolyColor, ['moneybags'], '', 6.97), + 'ls': (6.5 / bSize, VBase4(0.5, 0.85, 0.75, 1.0), ['loanshark'], '', 8.58), + 'rb': (7.0 / aSize, SuitDNA.moneyPolyColor, ['yesman'], 'robber-baron.jpg', 8.95), + # Sellbots + 'cc': (3.5 / cSize, VBase4(0.55, 0.65, 1.0, 1.0), ['coldcaller'], '', 4.63), + 'tm': (3.75 / bSize, SuitDNA.salesPolyColor, ['telemarketer'], '', 5.24), + 'nd': (4.35 / aSize, SuitDNA.salesPolyColor, ['numbercruncher'], 'name-dropper.jpg', 5.98), + 'gh': (4.75 / cSize, SuitDNA.salesPolyColor, ['gladhander'], '', 6.4), + 'ms': (4.75 / bSize, SuitDNA.salesPolyColor, ['movershaker'], '', 6.7), + 'tf': (5.25 / aSize, SuitDNA.salesPolyColor, ['twoface'], '', 6.95), + 'm': (5.75 / aSize, SuitDNA.salesPolyColor, ['twoface'], 'mingler.jpg', 7.61), + 'mh': (7.0 / aSize, SuitDNA.salesPolyColor, ['yesman'], '', 8.95), + } diff --git a/toontown/suit/SuitHealthBar.py b/toontown/suit/SuitHealthBar.py new file mode 100644 index 00000000..def25e2b --- /dev/null +++ b/toontown/suit/SuitHealthBar.py @@ -0,0 +1,115 @@ +from direct.task.Task import Task +from toontown.battle import BattleProps + +HEALTH_COLORS = ( + (0, 1, 0, 1), + (0.5, 1, 0, 1), + (0.75, 1, 0, 1), + (1, 1, 0, 1), + (1, 0.86, 0, 1), + (1, 0.6, 0, 1), + (1, 0.5, 0, 1), + (1, 0.25, 0, 1.0), + (1, 0, 0, 1), + (0.3, 0.3, 0.3, 1) +) +HEALTH_GLOW_COLORS = ( + (0.25, 1, 0.25, 0.5), + (0.5, 1, 0.25, .5), + (0.75, 1, 0.25, .5), + (1, 1, 0.25, 0.5), + (1, 0.866, 0.25, .5), + (1, 0.6, 0.25, .5), + (1, 0.5, 0.25, 0.5), + (1, 0.25, 0.25, 0.5), + (1, 0.25, 0.25, 0.5), + (0.3, 0.3, 0.3, 0)) + +class SuitHealthBar: + + def __init__(self): + self.geom = None + self.geomGlow = None + self.healthCondition = 0 + + def delete(self): + if self.geom: + self.geom.removeNode() + self.geom = None + self.geomGlow = None + taskMgr.remove('blink-task-%s' % id(self)) + self.healthCondition = 0 + + def generate(self): + self.delete() + model = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + button = model.find('**/minnieCircle') + model.removeNode() + + button.setH(180.0) + button.setColor(HEALTH_COLORS[0]) + self.geom = button + + glow = BattleProps.globalPropPool.getProp('glow') + glow.reparentTo(self.geom) + glow.setScale(0.28) + glow.setPos(-0.005, 0.01, 0.015) + glow.setColor(HEALTH_GLOW_COLORS[0]) + + button.flattenLight() + self.geomGlow = glow + self.geom.hide() + self.healthCondition = 0 + + def getHealthCondition(self, health): + if health > 0.95: + return 0 + elif health > 0.9: + return 1 + elif health > 0.8: + return 2 + elif health > 0.7: + return 3 + elif health > 0.6: + return 4 + elif health > 0.5: + return 5 + elif health > 0.3: + return 6 + elif health > 0.15: + return 7 + elif health > 0.05: + return 8 + elif health > 0.0: + return 9 + return 10 + + def update(self, hp, forceUpdate = 0): + if not self.geom: + return + condition = self.getHealthCondition(hp) + + if self.healthCondition != condition or forceUpdate: + taskMgr.remove('blink-task-%s' % id(self)) + + if condition in (9, 10): + blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.75 if condition == 9 else 0.25), Task(self.__blinkGray), Task.pause(0.1)) + taskMgr.add(blinkTask, 'blink-task-%s' % id(self)) + else: + self.geom.setColor(HEALTH_COLORS[condition], 1) + self.geomGlow.setColor(HEALTH_GLOW_COLORS[condition], 1) + + self.healthCondition = condition + + def __blink(self, color): + if not self.geom: + return + + self.geom.setColor(HEALTH_COLORS[color], 1) + self.geomGlow.setColor(HEALTH_GLOW_COLORS[color], 1) + + def __blinkRed(self, task): + self.__blink(8) + + def __blinkGray(self, task): + self.__blink(9) \ No newline at end of file diff --git a/toontown/suit/SuitInvasionGlobals.py b/toontown/suit/SuitInvasionGlobals.py new file mode 100755 index 00000000..fa2ae2ce --- /dev/null +++ b/toontown/suit/SuitInvasionGlobals.py @@ -0,0 +1,9 @@ +# Types: +INVASION_TYPE_NORMAL = 0 +INVASION_TYPE_MEGA = 1 +INVASION_TYPE_BRUTAL = 2 + +# Flags: +IFSkelecog = 1 << 0 +IFWaiter = 1 << 1 +IFV2 = 1 << 2 \ No newline at end of file diff --git a/toontown/suit/SuitInvasionManagerAI.py b/toontown/suit/SuitInvasionManagerAI.py new file mode 100755 index 00000000..055bf96e --- /dev/null +++ b/toontown/suit/SuitInvasionManagerAI.py @@ -0,0 +1,227 @@ +import time, random + +from toontown.battle import SuitBattleGlobals +from toontown.suit import SuitDNA +from toontown.suit.SuitInvasionGlobals import * +from toontown.toonbase import ToontownGlobals + + +class SuitInvasionManagerAI: + def __init__(self, air): + self.air = air + + self.invading = False + self.start = 0 + self.remaining = 0 + self.total = 0 + self.suitDeptIndex = None + self.suitTypeIndex = None + self.flags = 0 + + self.air.accept( + 'startInvasion', self.handleStartInvasion) + self.air.accept( + 'stopInvasion', self.handleStopInvasion) + + # We want to handle shard status queries so that a ShardStatusReceiver + # being created after we're created will know where we're at: + self.air.accept('queryShardStatus', self.sendInvasionStatus) + + self.sendInvasionStatus() + + def getInvading(self): + return self.invading + + def getInvadingCog(self): + return (self.suitDeptIndex, self.suitTypeIndex, self.flags) + + def startInvasion(self, suitDeptIndex=None, suitTypeIndex=None, flags=0, + type=INVASION_TYPE_NORMAL): + if self.invading: + # An invasion is currently in progress; ignore this request. + return False + + if (suitDeptIndex is None) and (suitTypeIndex is None) and (not flags): + # This invasion is no-op. + return False + + if (suitDeptIndex is None) and (suitTypeIndex is not None): + # It's impossible to determine the invading Cog. + return False + + if flags not in (0, IFV2, IFSkelecog, IFWaiter): + # The provided flag combination is not possible. + return False + + if (suitDeptIndex is not None) and (suitDeptIndex >= len(SuitDNA.suitDepts)): + # Invalid suit department. + return False + + if (suitTypeIndex is not None) and (suitTypeIndex >= SuitDNA.suitsPerDept): + # Invalid suit type. + return False + + if type not in (INVASION_TYPE_NORMAL, INVASION_TYPE_MEGA, INVASION_TYPE_BRUTAL): + # Invalid invasion type. + return False + + # Looks like we're all good. Begin the invasion: + self.invading = True + self.start = int(time.time()) + self.suitDeptIndex = suitDeptIndex + self.suitTypeIndex = suitTypeIndex + self.flags = flags + + # How many suits do we want? + if type == INVASION_TYPE_NORMAL: + self.total = random.randint(1000, 3000) + elif type == INVASION_TYPE_MEGA: + self.total = 5000 + elif type == INVASION_TYPE_BRUTAL: + self.total = 10000 + self.remaining = self.total + + self.flySuits() + self.notifyInvasionStarted() + + # Update the invasion tracker on the districts page in the Shticker Book: + if self.suitDeptIndex is not None: + self.air.districtStats.b_setInvasionStatus(self.suitDeptIndex + 1) + else: + self.air.districtStats.b_setInvasionStatus(5) + + # If this is a normal invasion, and the players take too long to defeat + # all of the Cogs, we'll want the invasion to timeout: + if type == INVASION_TYPE_NORMAL: + timeout = config.GetInt('invasion-timeout', 1800) + taskMgr.doMethodLater(timeout, self.stopInvasion, 'invasionTimeout') + + # If this is a mega invasion, and the players take to long to defeat + # all of the cogs, we want the invasion to take a bit longer to timeout: + if type == INVASION_TYPE_MEGA: + timeout = config.GetInt('invasion-timeout', 3200) + + # If this is a brutal invasion, the players will have a very long time to + # Defeat the cogs before the invasion times out: + if type == INVASION_TYPE_BRUTAL: + timeout = config.GetInt('invasion-timeout', 10000) + + self.sendInvasionStatus() + return True + + def stopInvasion(self, task=None): + if not self.invading: + # We are not currently invading. + return False + + # Stop the invasion timeout task: + taskMgr.remove('invasionTimeout') + + # Update the invasion tracker on the districts page in the Shticker Book: + self.air.districtStats.b_setInvasionStatus(0) + + # Revert what was done when the invasion started: + self.notifyInvasionEnded() + self.invading = False + self.start = 0 + self.suitDeptIndex = None + self.suitTypeIndex = None + self.flags = 0 + self.total = 0 + self.remaining = 0 + self.flySuits() + + self.sendInvasionStatus() + return True + + def getSuitName(self): + if self.suitDeptIndex is not None: + if self.suitTypeIndex is not None: + return SuitDNA.getSuitName(self.suitDeptIndex, self.suitTypeIndex) + else: + return SuitDNA.suitDepts[self.suitDeptIndex] + else: + return SuitDNA.suitHeadTypes[0] + + def notifyInvasionStarted(self): + msgType = ToontownGlobals.SuitInvasionBegin + if self.flags & IFSkelecog: + msgType = ToontownGlobals.SkelecogInvasionBegin + elif self.flags & IFWaiter: + msgType = ToontownGlobals.WaiterInvasionBegin + elif self.flags & IFV2: + msgType = ToontownGlobals.V2InvasionBegin + self.air.newsManager.sendUpdate( + 'setInvasionStatus', + [msgType, self.getSuitName(), self.total, self.flags]) + + def notifyInvasionEnded(self): + msgType = ToontownGlobals.SuitInvasionEnd + if self.flags & IFSkelecog: + msgType = ToontownGlobals.SkelecogInvasionEnd + elif self.flags & IFWaiter: + msgType = ToontownGlobals.WaiterInvasionEnd + elif self.flags & IFV2: + msgType = ToontownGlobals.V2InvasionEnd + self.air.newsManager.sendUpdate( + 'setInvasionStatus', [msgType, self.getSuitName(), 0, self.flags]) + + def notifyInvasionUpdate(self): + self.air.newsManager.sendUpdate( + 'setInvasionStatus', + [ToontownGlobals.SuitInvasionUpdate, self.getSuitName(), + self.remaining, self.flags]) + + def notifyInvasionBulletin(self, avId): + msgType = ToontownGlobals.SuitInvasionBulletin + if self.flags & IFSkelecog: + msgType = ToontownGlobals.SkelecogInvasionBulletin + elif self.flags & IFWaiter: + msgType = ToontownGlobals.WaiterInvasionBulletin + elif self.flags & IFV2: + msgType = ToontownGlobals.V2InvasionBulletin + self.air.newsManager.sendUpdateToAvatarId( + avId, 'setInvasionStatus', + [msgType, self.getSuitName(), self.remaining, self.flags]) + + def flySuits(self): + for suitPlanner in self.air.suitPlanners.values(): + suitPlanner.flySuits() + + def handleSuitDefeated(self): + self.remaining -= 1 + if self.remaining == 0: + self.stopInvasion() + elif self.remaining == (self.total/2): + self.notifyInvasionUpdate() + self.sendInvasionStatus() + + def handleStartInvasion(self, shardId, *args): + if shardId == self.air.ourChannel: + self.startInvasion(*args) + + def handleStopInvasion(self, shardId): + if shardId == self.air.ourChannel: + self.stopInvasion() + + def sendInvasionStatus(self): + if self.invading: + if self.suitDeptIndex is not None: + if self.suitTypeIndex is not None: + type = SuitBattleGlobals.SuitAttributes[self.getSuitName()]['name'] + else: + type = SuitDNA.getDeptFullname(self.getSuitName()) + else: + type = None + status = { + 'invasion': { + 'type': type, + 'flags': self.flags, + 'remaining': self.remaining, + 'total': self.total, + 'start': self.start + } + } + else: + status = {'invasion': None} + self.air.sendNetEvent('shardStatus', [self.air.ourChannel, status]) diff --git a/toontown/suit/SuitPlannerBase.py b/toontown/suit/SuitPlannerBase.py new file mode 100755 index 00000000..32386238 --- /dev/null +++ b/toontown/suit/SuitPlannerBase.py @@ -0,0 +1,614 @@ +from panda3d.core import * +from direct.directnotify.DirectNotifyGlobal import * +from toontown.hood import ZoneUtil, HoodUtil +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals +from toontown.building import SuitBuildingGlobals +from toontown.dna.DNAParser import * + +class SuitPlannerBase: + notify = directNotify.newCategory('SuitPlannerBase') + SuitHoodInfo = [[2100, + 5, + 15, + 0, + 5, + 20, + 3, + (1, + 5, + 10, + 40, + 60, + 80), + (25, + 25, + 25, + 25), + (1, 2, 3), + []], + [2200, + 3, + 10, + 0, + 5, + 15, + 3, + (1, + 5, + 10, + 40, + 60, + 80), + (10, + 70, + 10, + 10), + (1, 2, 3), + []], + [2300, + 3, + 10, + 0, + 5, + 15, + 3, + (1, + 5, + 10, + 40, + 60, + 80), + (10, + 10, + 40, + 40), + (1, 2, 3), + []], + [1100, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (90, + 10, + 0, + 0), + (2, 3, 4), + []], + [1200, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 0, + 90, + 10), + (3, + 4, + 5, + 6), + []], + [1300, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (40, + 40, + 10, + 10), + (3, + 4, + 5, + 6), + []], + [3100, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (90, + 10, + 0, + 0), + (5, 6, 7), + []], + [3200, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (10, + 20, + 30, + 40), + (5, 6, 7), + []], + [3300, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (5, + 85, + 5, + 5), + (7, 8, 9), + []], + [4100, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 0, + 50, + 50), + (2, 3, 4), + []], + [4200, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 0, + 90, + 10), + (3, + 4, + 5, + 6), + []], + [4300, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (50, + 50, + 0, + 0), + (3, + 4, + 5, + 6), + []], + [5100, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 20, + 10, + 70), + (2, 3, 4), + []], + [5200, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (10, + 70, + 0, + 20), + (3, + 4, + 5, + 6), + []], + [5300, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (5, + 5, + 5, + 85), + (3, + 4, + 5, + 6), + []], + [9100, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (25, + 25, + 25, + 25), + (6, + 7, + 8, + 9), + []], + [9200, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (5, + 5, + 85, + 5), + (6, + 7, + 8, + 9), + []], + [10000, + 3, + 15, + 0, + 5, + 15, + 3, + (1, + 5, + 10, + 40, + 60, + 80), + (100, + 0, + 0, + 0), + (7, 8, 9, 10), + []], + [11000, + 3, + 15, + 0, + 0, + 0, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 0, + 0, + 100), + (4, 5, 6), + []], + [11200, + 10, + 20, + 0, + 0, + 0, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 0, + 0, + 100), + (4, 5, 6), + []], + [12000, + 10, + 20, + 0, + 0, + 0, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 0, + 100, + 0), + (7, 8, 9), + []], + [13000, + 10, + 20, + 0, + 0, + 0, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 100, + 0, + 0), + (8, 9, 10), + []]] + SUIT_HOOD_INFO_ZONE = 0 + SUIT_HOOD_INFO_MIN = 1 + SUIT_HOOD_INFO_MAX = 2 + SUIT_HOOD_INFO_BMIN = 3 + SUIT_HOOD_INFO_BMAX = 4 + SUIT_HOOD_INFO_BWEIGHT = 5 + SUIT_HOOD_INFO_SMAX = 6 + SUIT_HOOD_INFO_JCHANCE = 7 + SUIT_HOOD_INFO_TRACK = 8 + SUIT_HOOD_INFO_LVL = 9 + SUIT_HOOD_INFO_HEIGHTS = 10 + TOTAL_BWEIGHT = 0 + TOTAL_BWEIGHT_PER_TRACK = [0, + 0, + 0, + 0] + TOTAL_BWEIGHT_PER_HEIGHT = [0, + 0, + 0, + 0, + 0] + for currHoodInfo in SuitHoodInfo: + weight = currHoodInfo[SUIT_HOOD_INFO_BWEIGHT] + tracks = currHoodInfo[SUIT_HOOD_INFO_TRACK] + levels = currHoodInfo[SUIT_HOOD_INFO_LVL] + heights = [0, + 0, + 0, + 0, + 0] + for level in levels: + minFloors, maxFloors = SuitBuildingGlobals.SuitBuildingInfo[level - 1][0] + for i in xrange(minFloors - 1, maxFloors): + heights[i] += 1 + + currHoodInfo[SUIT_HOOD_INFO_HEIGHTS] = heights + TOTAL_BWEIGHT += weight + TOTAL_BWEIGHT_PER_TRACK[0] += weight * tracks[0] + TOTAL_BWEIGHT_PER_TRACK[1] += weight * tracks[1] + TOTAL_BWEIGHT_PER_TRACK[2] += weight * tracks[2] + TOTAL_BWEIGHT_PER_TRACK[3] += weight * tracks[3] + TOTAL_BWEIGHT_PER_HEIGHT[0] += weight * heights[0] + TOTAL_BWEIGHT_PER_HEIGHT[1] += weight * heights[1] + TOTAL_BWEIGHT_PER_HEIGHT[2] += weight * heights[2] + TOTAL_BWEIGHT_PER_HEIGHT[3] += weight * heights[3] + TOTAL_BWEIGHT_PER_HEIGHT[4] += weight * heights[4] + + def __init__(self): + self.suitWalkSpeed = ToontownGlobals.SuitWalkSpeed + self.dnaStore = None + self.pointIndexes = {} + return + + def delete(self): + del self.dnaStore + + def setupDNA(self): + if self.dnaStore: + return None + self.dnaStore = DNAStorage() + dnaFileName = self.genDNAFileName() + loadDNAFileAI(self.dnaStore, dnaFileName) + self.initDNAInfo() + + def genDNAFileName(self): + zoneId = ZoneUtil.getCanonicalZoneId(self.getZoneId()) + hoodId = ZoneUtil.getCanonicalHoodId(zoneId) + hood = ToontownGlobals.dnaMap[hoodId] + phase = ToontownGlobals.streetPhaseMap[hoodId] + if hoodId == zoneId: + zoneId = 'sz' + return 'phase_%s/dna/%s_%s.pdna' % (phase, hood, zoneId) + + def getZoneId(self): + return self.zoneId + + def setZoneId(self, zoneId): + self.notify.debug('setting zone id for suit planner') + self.zoneId = zoneId + self.setupDNA() + + def extractGroupName(self, groupFullName): + return groupFullName.split(':', 1)[0] + + def initDNAInfo(self): + numGraphs = self.dnaStore.discoverContinuity() + if numGraphs != 1: + self.notify.info('zone %s has %s disconnected suit paths.' % (self.zoneId, numGraphs)) + self.battlePosDict = {} + self.cellToGagBonusDict = {} + + for i in xrange(self.dnaStore.getNumDNAVisGroupsAI()): + vg = self.dnaStore.getDNAVisGroupAI(i) + zoneId = int(self.extractGroupName(vg.getName())) + + if vg.getNumBattleCells() == 1: + battleCell = vg.getBattleCell(0) + self.battlePosDict[zoneId] = vg.getBattleCell(0).getPos() + elif vg.getNumBattleCells() > 1: + self.notify.warning('multiple battle cells for zone: %d' % zoneId) + self.battlePosDict[zoneId] = vg.getBattleCell(0).getPos() + + if True: + for i in xrange(vg.getNumChildren()): + childDnaGroup = vg.at(i) + + if isinstance(childDnaGroup, DNAInteractiveProp): + self.notify.debug('got interactive prop %s' % childDnaGroup) + battleCellId = childDnaGroup.getCellId() + + if battleCellId == -1: + self.notify.warning('interactive prop %s at %s not associated with a a battle' % (childDnaGroup, zoneId)) + + elif battleCellId == 0: + if zoneId in self.cellToGagBonusDict: + self.notify.error('FIXME battle cell at zone %s has two props %s %s linked to it' % (zoneId, self.cellToGagBonusDict[zoneId], childDnaGroup)) + else: + name = childDnaGroup.getName() + propType = HoodUtil.calcPropType(name) + if propType in ToontownBattleGlobals.PropTypeToTrackBonus: + trackBonus = ToontownBattleGlobals.PropTypeToTrackBonus[propType] + self.cellToGagBonusDict[zoneId] = trackBonus + + self.dnaStore.resetDNAGroups() + self.dnaStore.resetDNAVisGroups() + self.dnaStore.resetDNAVisGroupsAI() + self.streetPointList = [] + self.frontdoorPointList = [] + self.sidedoorPointList = [] + self.cogHQDoorPointList = [] + numPoints = self.dnaStore.getNumSuitPoints() + for i in xrange(numPoints): + point = self.dnaStore.getSuitPointAtIndex(i) + if point.getPointType() == DNASuitPoint.FRONT_DOOR_POINT: + self.frontdoorPointList.append(point) + elif point.getPointType() == DNASuitPoint.SIDE_DOOR_POINT: + self.sidedoorPointList.append(point) + elif (point.getPointType() == DNASuitPoint.COGHQ_IN_POINT) or (point.getPointType() == DNASuitPoint.COGHQ_OUT_POINT): + self.cogHQDoorPointList.append(point) + else: + self.streetPointList.append(point) + self.pointIndexes[point.getIndex()] = point + + def performPathTest(self): + if not self.notify.getDebug(): + return None + startAndEnd = self.pickPath() + if not startAndEnd: + return None + startPoint = startAndEnd[0] + endPoint = startAndEnd[1] + path = self.dnaStore.getSuitPath(startPoint, endPoint) + numPathPoints = path.getNumPoints() + for i in xrange(numPathPoints - 1): + zone = self.dnaStore.getSuitEdgeZone(path.getPointIndex(i), path.getPointIndex(i + 1)) + travelTime = self.dnaStore.getSuitEdgeTravelTime(path.getPointIndex(i), path.getPointIndex(i + 1), self.suitWalkSpeed) + self.notify.debug('edge from point ' + `i` + ' to point ' + `(i + 1)` + ' is in zone: ' + `zone` + ' and will take ' + `travelTime` + ' seconds to walk.') + + def genPath(self, startPoint, endPoint, minPathLen, maxPathLen): + return self.dnaStore.getSuitPath(startPoint, endPoint, minPathLen, maxPathLen) + + def getDnaStore(self): + return self.dnaStore diff --git a/toontown/suit/SuitTimings.py b/toontown/suit/SuitTimings.py new file mode 100755 index 00000000..fa016f95 --- /dev/null +++ b/toontown/suit/SuitTimings.py @@ -0,0 +1,8 @@ +dict = {} +fromSky = 6.5 +toSky = 6.5 +victoryDance = 9.08 +fromSuitBuilding = 2.0 +toSuitBuilding = 2.5 +toToonBuilding = 2.5 +suitDeath = 7.5 diff --git a/toontown/suit/__init__.py b/toontown/suit/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/toon/AccessoryGlobals.py b/toontown/toon/AccessoryGlobals.py new file mode 100755 index 00000000..e79f91af --- /dev/null +++ b/toontown/toon/AccessoryGlobals.py @@ -0,0 +1,1158 @@ +HatTransTable = {'hl': ((0.0, -0.32198, 0.406916), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.205193, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.424631)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.1, 0.685092), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, -0.048714, 0.46431), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 341.565063, 0.0), (0.334803, 0.38662, 0.408829)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.044478, 0.645978), (180.0, 352.405365, 0.0), (0.360603, 0.40683, 0.342697)), + 'cs': ((0.0, -0.14621, 0.382862), (180.0, 343.300751, 0.0), (0.350607, 0.403063, 0.307962)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.298001, 0.383177, 0.368997)), + 'ss': ((0.0, -0.138924, 0.126139), (180.0, 319.398712, 0.0), (0.357568, 0.387685, 0.34839)), + 'rl': ((0.0, -0.107251, 0.353435), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.597447, 0.45943, 0.581963)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.554896, 0.483187, 0.468427)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 341.565063, 0.0), (0.527025, 0.507327, 0.604765)), + 'fs': ((0.0, -0.188123, 0.623452), (180.0, 345.963745, 0.0), (0.527899, 0.51, 0.458304))} +GlassesTransTable = {'hl': ((0.0, 0.118239, 0.073909), (180.0, 338.962494, 0.0), (0.243308, 0.19733, 0.288041)), + 'hs': ((0.0, 0.218512, 0.047869), (180.0, 320.0, 0.0), (0.238619, 0.160504, 0.245208)), + 'pl': ((0.0, 0.181508, 0.06527), (180.0, 341.565063, 0.0), (0.357565, 0.306053, 0.375362)), + 'ps': ((0.0, 0.181299, 0.089964), (180.0, 350.537689, 0.0), (0.282347, 0.295057, 0.28182)), + 'dl': ((0.0, 0.204627, 0.277646), (180.0, 350.0, 0.0), (0.280495, 0.197035, 0.28)), + 'ds': ((0.0, 0.16996, 0.31), (180.0, 350.0, 0.0), (0.29, 0.220048, 0.277431)), + 'bl': ((0.0, 0.22, 0.108153), (180.0, 350.0, 0.0), (0.27447, 0.262137, 0.287293)), + 'bs': ((0.0, 0.200803, 0.097391), (180.0, 350.0, 0.0), (0.274179, 0.274179, 0.274179)), + 'cl': ((0.0, 0.148353, 0.06), (180.0, 350.0, 0.0), (0.35351, 0.293013, 0.332505)), + 'cs': ((0.0, 0.259957, 0.056374), (180.0, 350.0, 0.0), (0.342668, 0.214791, 0.311371)), + 'sl': ((0.0, 0.335741, -0.018402), (180.0, 350.0, 0.0), (0.32, 0.206504, 0.32)), + 'ss': ((0.0, 0.301501, -0.030958), (180.0, 350.0, 0.0), (0.28, 0.211589, 0.28)), + 'rl': ((0.0, 0.250739, -0.037004), (180.0, 350.537689, 0.0), (0.363858, 0.181402, 0.337987)), + 'rs': ((0.0, 0.276756, -0.031632), (180.0, 354.289398, 0.0), (0.306648, 0.142252, 0.308336)), + 'ml': ((0.0, 0.14204, 0.298711), (180.0, 350.0, 0.0), (0.44, 0.314073, 0.44)), + 'ms': ((0.0, 0.170296, 0.25674), (180.0, 350.0, 0.0), (0.441395, 0.278491, 0.398912)), + 'fl': ((0.0, 0.126437, 0.12), (180.0, 350.0, 0.0), (0.41, 0.320314, 0.41)), + 'fs': ((0.0, 0.129622, 0.13), (180.0, 350.0, 0.0), (0.42, 0.319754, 0.42))} +BackpackTransTable = {'s': ((0.184578, -1.585814, 0.134797), (180.0, 6.340192, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.227545, -1.05, 0.053624), (180.0, 6.0, 0.0), (0.319532, 0.319532, 0.319532)), + 'l': ((0.26576, -1.28023, 0.0), (180.0, 0.0, 0.0), (0.32, 0.32, 0.32))} +ExtendedHatTransTable = {1: {'hl': ((0.0, -0.295705, 0.406916), (180.0, 320.194427, 0.0), (0.254, 0.245594, 0.246926)), + 'hs': ((0.0, -0.195882, 0.244268), (180.0, 311.633545, 0.0), (0.263445, 0.243299, 0.243299)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.434684, 0.33591, 0.424631)), + 'ps': ((0.0, -0.063579, 0.353951), (180.0, 323.972626, 0.0), (0.365148, 0.3296, 0.348283)), + 'dl': ((0.0, -0.07662, 0.685092), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, -0.022553, 0.46431), (180.0, 323.130096, 0.0), (0.315467, 0.315467, 0.315467)), + 'bl': ((0.0, -0.071458, 0.580411), (180.0, 341.565063, 0.0), (0.332492, 0.346676, 0.424343)), + 'bs': ((0.0, -0.000624, 0.392553), (180.0, 338.198578, 0.0), (0.310198, 0.337212, 0.32)), + 'cl': ((0.0, -0.011011, 0.596829), (180.0, 352.405365, 0.0), (0.394101, 0.374709, 0.342697)), + 'cs': ((0.0, -0.087502, 0.347226), (180.0, 343.300751, 0.0), (0.400106, 0.382564, 0.307962)), + 'sl': ((0.0, -0.118797, 0.296321), (180.0, 336.801422, 0.0), (0.285034, 0.366504, 0.352941)), + 'ss': ((0.0, -0.138924, 0.126139), (180.0, 319.398712, 0.0), (0.338425, 0.370884, 0.34839)), + 'rl': ((0.0, -0.055533, 0.353435), (180.0, 340.346161, 0.0), (0.29569, 0.252995, 0.262981)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.536616, 0.45943, 0.581963)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.483682, 0.423291, 0.468427)), + 'fl': ((0.0, -0.144887, 0.701924), (180.0, 341.565063, 0.0), (0.527401, 0.487251, 0.604765)), + 'fs': ((0.0, -0.178833, 0.632517), (180.0, 345.963745, 0.0), (0.489695, 0.479546, 0.458304))}, + 2: {'dl': ((0.0, -0.1, 0.685092), (180.0, 319.398712, 0.0), (0.295951, 0.295951, 0.295951)), + 'cs': ((-0.026207, -0.14621, 0.382862), (180.0, 343.300751, 6.0), (0.350607, 0.403063, 0.307962)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.383177, 0.383177, 0.383177))}, + 3: {'cs': ((0.0, -0.14621, 0.382862), (180.0, 343.300751, 0.0), (0.350607, 0.403063, 0.307962))}, + 5: {'hs': ((0.0, -0.280617, 0.335528), (180.0, 315.0, 0.0), (0.315279, 0.277932, 0.309252)), + 'cs': ((0.0, -0.14621, 0.382862), (180.0, 343.300751, 8.530766), (0.419412, 0.417991, 0.319367))}, + 6: {'cs': ((0.0, -0.073163, 0.411979), (180.0, 343.300751, 0.0), (0.350607, 0.403063, 0.307962))}, + 10: {'pl': ((-0.099051, -0.175888, 0.592198), (180.0, 330.0, 25.201124), (0.492972, 0.418136, 0.424631)), + 'ps': ((-0.041475, -0.06182, 0.393373), (180.0, 323.972626, 9.462322), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.070785, 0.704706), (180.0, 319.398712, 8.130102), (0.304895, 0.297812, 0.375712)), + 'ds': ((-0.021678, -0.01167, 0.498881), (180.0, 333.434937, 9.462322), (0.318405, 0.318405, 0.318405)), + 'bl': ((-0.002771, -0.172801, 0.738516), (180.0, 329.743561, 9.462322), (0.317369, 0.317369, 0.317369)), + 'bs': ((-0.050198, 0.004136, 0.404443), (194.03624, 341.565063, 14.036243), (0.310959, 0.310959, 0.310959)), + 'cl': ((-0.004527, 0.016114, 0.686246), (180.0, 6.009006, 8.130102), (0.370254, 0.370254, 0.370254)), + 'cs': ((-0.030719, 0.000539, 0.361591), (180.0, 0.0, 5.440332), (0.40023, 0.40023, 0.40023)), + 'sl': ((0.0, -0.137519, 0.341029), (180.0, 336.801422, 5.710593), (0.327711, 0.375616, 0.327711)), + 'ss': ((-0.027001, -0.172305, 0.180213), (180.0, 326.309937, 11.309933), (0.372096, 0.372096, 0.372096)), + 'rl': ((-0.015145, -0.026606, 0.253854), (180.0, 340.346161, 9.462322), (0.380015, 0.348041, 0.380015)), + 'rs': ((-0.009053, -0.011238, 0.288839), (124.99202, 355.236359, 0.0), (0.352183, 0.352183, 0.352183)), + 'ml': ((-0.017855, -0.026353, 0.768487), (180.0, 350.537689, 8.746162), (0.651424, 0.533307, 0.581963)), + 'ms': ((-0.049162, -0.051857, 0.556686), (180.0, 344.0, 6.709837), (0.554896, 0.483187, 0.468427)), + 'fl': ((0.006534, -0.184093, 0.852904), (180.0, 0.0, 354.289398), (0.527988, 0.527988, 0.527988)), + 'fs': ((0.012731, -0.079046, 0.676545), (180.0, 6.340192, 354.289398), (0.527899, 0.527899, 0.527899))}, + 11: {'cs': ((-0.019078, -0.14621, 0.382862), (180.0, 343.300751, 6.0), (0.361787, 0.403063, 0.307962)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.30748, 0.383177, 0.368997))}, + 12: {'hl': ((0.0, -0.309314, 0.406916), (180.0, 320.194427, 0.0), (0.271819, 0.262823, 0.264249)), + 'pl': ((0.0, -0.181953, 0.57), (180.0, 330.0, 0.0), (0.45327, 0.377233, 0.424631)), + 'dl': ((0.0, -0.083903, 0.685092), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, -0.048714, 0.46431), (180.0, 323.130096, 0.0), (0.318405, 0.308617, 0.318405)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 341.565063, 0.0), (0.367935, 0.362207, 0.408829)), + 'bs': ((0.0, -0.021139, 0.392553), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.044478, 0.645978), (180.0, 352.405365, 0.0), (0.378772, 0.40683, 0.342697)), + 'cs': ((-0.010907, -0.107796, 0.382862), (180.0, 343.300751, 6.0), (0.406312, 0.377019, 0.307962)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.294358, 0.378979, 0.368997)), + 'ss': ((0.0, -0.138924, 0.126139), (180.0, 319.398712, 0.0), (0.356448, 0.387685, 0.34839)), + 'rl': ((0.0, -0.067593, 0.353435), (180.0, 340.346161, 0.0), (0.294372, 0.268597, 0.25764)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.37158, 0.322002, 0.352713)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.56417, 0.474334, 0.581963)), + 'ms': ((0.0, -0.088935, 0.588494), (180.0, 344.0, 0.0), (0.48211, 0.395267, 0.468427)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 341.565063, 0.0), (0.530228, 0.524303, 0.604765)), + 'fs': ((0.0, -0.144201, 0.623452), (180.0, 345.963745, 0.0), (0.527899, 0.51, 0.458304))}, + 13: {'hl': ((0.0, -0.412612, 0.487717), (180.0, 311.987213, 0.0), (0.16889, 0.16889, 0.16889)), + 'hs': ((0.0, -0.286132, 0.359341), (180.0, 311.633545, 0.0), (0.128881, 0.128881, 0.128881)), + 'pl': ((0.0, -0.268135, 0.72809), (180.0, 330.0, 0.0), (0.323023, 0.272871, 0.272871)), + 'ps': ((0.0, -0.094548, 0.408059), (180.0, 323.972626, 0.0), (0.320213, 0.296061, 0.296061)), + 'dl': ((0.0, -0.143021, 0.812855), (180.0, 333.434937, 0.0), (0.21997, 0.21997, 0.21997)), + 'ds': ((0.0, -0.060565, 0.575688), (180.0, 333.434937, 0.0), (0.248021, 0.248021, 0.248021)), + 'bl': ((0.0, -0.121603, 0.639803), (180.0, 341.565063, 0.0), (0.330776, 0.304925, 0.304925)), + 'bs': ((0.0, -0.102031, 0.466414), (180.0, 330.945404, 0.0), (0.2459, 0.2459, 0.2459)), + 'cl': ((0.0, -0.026431, 0.728948), (180.0, 352.405365, 0.0), (0.287266, 0.287266, 0.287266)), + 'cs': ((-0.050589, -0.081308, 0.469158), (180.0, 345.963745, 8.130102), (0.299346, 0.299346, 0.299346)), + 'sl': ((0.0, -0.169265, 0.446898), (180.0, 336.801422, 0.0), (0.240421, 0.256241, 0.256241)), + 'ss': ((0.0, -0.270782, 0.24262), (180.0, 319.398712, 0.0), (0.225974, 0.301156, 0.301156)), + 'rl': ((0.0, -0.049562, 0.413636), (180.0, 340.346161, 0.0), (0.279306, 0.207526, 0.207526)), + 'rs': ((-0.090265, -0.098991, 0.45038), (180.0, 336.801422, 23.962488), (0.218332, 0.218332, 0.218332)), + 'ml': ((0.0, -0.196901, 0.997155), (180.0, 333.434937, 0.0), (0.435704, 0.363724, 0.383599)), + 'ms': ((0.0, -0.149188, 0.735846), (180.0, 338.198578, 0.0), (0.385714, 0.313025, 0.313025)), + 'fl': ((-0.090384, -0.178217, 1.065685), (180.0, 0.0, 12.994617), (0.343651, 0.343651, 0.343651)), + 'fs': ((-0.101206, -0.188123, 0.832492), (180.0, 345.963745, 8.972627), (0.366177, 0.366177, 0.366177))}, + 14: {'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.509562, 0.377233, 0.424631)), + 'ps': ((0.0, -0.06878, 0.36349), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.023114, 0.591717), (180.0, 320.710602, 0.0), (0.408557, 0.3748, 0.3748)), + 'ds': ((0.0, -0.037187, 0.471363), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 341.565063, 0.0), (0.390313, 0.392326, 0.408829)), + 'cl': ((0.0, -0.020176, 0.645978), (180.0, 352.405365, 0.0), (0.360603, 0.40683, 0.342697)), + 'cs': ((0.0, -0.085443, 0.382862), (180.0, 343.300751, 0.0), (0.377142, 0.403063, 0.307962)), + 'ss': ((0.0, -0.138924, 0.126139), (180.0, 319.398712, 0.0), (0.385168, 0.41761, 0.375282)), + 'rl': ((0.0, -0.031954, 0.353435), (180.0, 340.346161, 0.0), (0.303994, 0.276488, 0.25764)), + 'rs': ((0.0, -0.020569, 0.257018), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ml': ((0.0, -0.06454, 0.768487), (180.0, 333.434937, 0.0), (0.597447, 0.505423, 0.581963)), + 'ms': ((0.0, -0.051857, 0.515682), (180.0, 344.0, 0.0), (0.631431, 0.483187, 0.468427)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 341.565063, 0.0), (0.630656, 0.560066, 0.604765)), + 'fs': ((0.0, -0.188123, 0.623452), (180.0, 345.963745, 0.0), (0.615579, 0.526355, 0.458304))}, + 15: {'hl': ((0.0, -0.32198, 0.406916), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.243293)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.361169)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'cl': ((0.0, -0.044478, 0.434606), (180.0, 352.405365, 0.0), (0.3873, 0.436949, 0.368068)), + 'cs': ((0.0, -0.09931, 0.352211), (180.0, 0.0, 4.763642), (0.406173, 0.406173, 0.406173)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.298001, 0.383177, 0.298392)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 347.471191, 0.0), (0.597447, 0.45943, 0.464198)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 354.805573, 0.0), (0.542386, 0.507327, 0.420425))}, + 16: {'hs': ((0.0, -0.158257, 0.27033), (180.0, 311.633545, 0.0), (0.301503, 0.301503, 0.301503)), + 'pl': ((0.0, -0.175888, 0.575867), (180.0, 330.0, 0.0), (0.496736, 0.496736, 0.496736)), + 'ps': ((0.0, -0.052435, 0.353951), (180.0, 323.972626, 0.0), (0.388558, 0.388558, 0.388558)), + 'dl': ((0.0, -0.078244, 0.685092), (180.0, 333.434937, 0.0), (0.345497, 0.349719, 0.391548)), + 'ds': ((0.0, 0.024539, 0.46431), (180.0, 323.130096, 0.0), (0.347816, 0.347816, 0.347816)), + 'bl': ((0.009605, -0.098169, 0.555411), (180.0, 341.565063, 0.0), (0.399212, 0.391299, 0.391299)), + 'bs': ((0.0, 0.004136, 0.399126), (180.0, 338.198578, 0.0), (0.361711, 0.361711, 0.361711)), + 'cl': ((0.0, -0.013398, 0.50033), (180.0, 352.405365, 0.0), (0.456027, 0.456027, 0.456027)), + 'cs': ((0.0, -0.09551, 0.349781), (180.0, 343.300751, 0.0), (0.452026, 0.452026, 0.452026)), + 'sl': ((0.0, -0.048961, 0.296321), (180.0, 336.801422, 0.0), (0.365214, 0.365214, 0.365214)), + 'rl': ((0.0, -0.107251, 0.353435), (180.0, 340.346161, 0.0), (0.314149, 0.314149, 0.314149)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.379328, 0.379328, 0.379328)), + 'ml': ((0.0, -0.043199, 0.768487), (180.0, 333.434937, 0.0), (0.559512, 0.516789, 0.558125)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.523591, 0.523591, 0.523591)), + 'fl': ((0.0, -0.13484, 0.701924), (180.0, 341.565063, 0.0), (0.581492, 0.581492, 0.581492)), + 'fs': ((0.0, -0.136718, 0.623452), (180.0, 345.963745, 0.0), (0.551042, 0.551042, 0.551042))}, + 17: {'hl': ((0.0, -0.288235, 0.406916), (180.0, 320.194427, 0.0), (0.241566, 0.233572, 0.234839)), + 'hs': ((0.0, -0.191402, 0.247444), (180.0, 311.633545, 0.0), (0.269808, 0.237829, 0.249303)), + 'pl': ((0.0, -0.142213, 0.57), (180.0, 330.0, 0.0), (0.432458, 0.345032, 0.354144)), + 'dl': ((0.0, -0.079517, 0.685092), (180.0, 333.434937, 0.0), (0.270819, 0.265906, 0.332068)), + 'ds': ((0.0, -0.025315, 0.46431), (180.0, 323.130096, 0.0), (0.296901, 0.280258, 0.28153)), + 'bl': ((0.0, -0.06624, 0.574968), (180.0, 341.565063, 0.0), (0.311075, 0.341038, 0.367032)), + 'cl': ((0.0, -0.011135, 0.645978), (180.0, 352.405365, 0.0), (0.35892, 0.357605, 0.342697)), + 'cs': ((0.0, -0.077403, 0.382862), (180.0, 343.300751, 0.0), (0.363435, 0.35624, 0.307962)), + 'sl': ((0.0, -0.107696, 0.296321), (180.0, 336.801422, 0.0), (0.287417, 0.344961, 0.281365)), + 'ss': ((0.0, -0.108221, 0.126139), (180.0, 319.398712, 0.0), (0.340291, 0.368281, 0.34839)), + 'rl': ((0.0, -0.051276, 0.353435), (180.0, 340.346161, 0.0), (0.283907, 0.248885, 0.229335)), + 'rs': ((0.0, -0.013377, 0.30232), (180.0, 351.869904, 0.0), (0.334657, 0.285461, 0.29066)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.488528, 0.427275, 0.444023)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.452082, 0.355888, 0.354063)), + 'fl': ((0.0, -0.13699, 0.701924), (180.0, 341.565063, 0.0), (0.48533, 0.471774, 0.562384)), + 'fs': ((0.0, -0.146903, 0.63012), (180.0, 345.963745, 0.0), (0.476258, 0.448793, 0.431941))}, + 18: {'hs': ((0.0, -0.205193, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'cl': ((0.0, -0.044478, 0.645978), (180.0, 352.405365, 0.0), (0.304127, 0.304127, 0.304127)), + 'rl': ((0.0, -0.054094, 0.353435), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.297602, 0.268747, 0.294379)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.454681, 0.45943, 0.581963)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.406678, 0.404195, 0.468427)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 341.565063, 0.0), (0.527025, 0.507327, 0.604765)), + 'fs': ((0.0, -0.120977, 0.623452), (180.0, 345.963745, 0.0), (0.481652, 0.465321, 0.418154))}, + 19: {'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.298001, 0.383177, 0.368997))}, + 20: {'hl': ((0.0, -0.275845, 0.406916), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.184604, 0.253956), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.146144, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.424631)), + 'ps': ((0.0, -0.066372, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.042963, 0.685092), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, -0.003173, 0.46431), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, -0.054528, 0.574968), (180.0, 341.565063, 0.0), (0.334803, 0.339222, 0.408829)), + 'cl': ((0.0, 0.013352, 0.538953), (180.0, 352.405365, 0.0), (0.345773, 0.362333, 0.342697)), + 'cs': ((0.0, -0.044327, 0.382862), (180.0, 343.300751, 0.0), (0.366347, 0.3658, 0.307962)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.480187, 0.418133, 0.40536)), + 'fs': ((0.0, -0.115848, 0.623452), (180.0, 345.963745, 0.0), (0.527899, 0.51, 0.458304))}, + 21: {'hl': ((0.0, -0.227, 0.279386), (180.0, 320.194427, 0.0), (0.276594, 0.281422, 0.318059)), + 'hs': ((0.0, -0.15377, 0.166578), (180.0, 311.633545, 0.0), (0.286051, 0.245637, 0.302753)), + 'pl': ((0.0, -0.175888, 0.46633), (180.0, 330.0, 0.0), (0.451908, 0.389167, 0.424631)), + 'ps': ((0.0, -0.046908, 0.240283), (180.0, 323.972626, 0.0), (0.365007, 0.365007, 0.365007)), + 'dl': ((0.0, -0.1, 0.685092), (180.0, 333.434937, 0.0), (0.25984, 0.243202, 0.322344)), + 'ds': ((0.0, -0.048714, 0.429002), (180.0, 323.130096, 0.0), (0.30725, 0.277881, 0.279921)), + 'sl': ((0.0, -0.137519, 0.182433), (180.0, 336.801422, 0.0), (0.298001, 0.383177, 0.368997)), + 'rl': ((0.0, -0.107251, 0.353435), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ms': ((0.0, -0.10249, 0.556469), (180.0, 344.0, 0.0), (0.466104, 0.354776, 0.380968)), + 'fl': ((0.0, -0.184093, 0.642801), (180.0, 341.565063, 0.0), (0.496574, 0.482481, 0.588552)), + 'fs': ((0.0, -0.188123, 0.528571), (180.0, 345.963745, 0.0), (0.498111, 0.492008, 0.432218))}, + 22: {'hl': ((0.0, -0.265135, 0.418068), (180.0, 349.69516, 0.0), (0.274705, 0.284073, 0.285614)), + 'hs': ((0.0, -0.116422, 0.29659), (180.0, 343.300751, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.424631)), + 'ps': ((0.0, -0.023007, 0.319507), (180.0, 337.380127, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.078245, 0.662967), (180.0, 17.10273, 0.0), (0.346512, 0.298172, 0.364691)), + 'ds': ((0.0, 0.064558, 0.46431), (180.0, 343.300751, 0.0), (0.352626, 0.352626, 0.352626)), + 'bl': ((0.0, -0.04906, 0.71477), (180.0, 347.905243, 0.0), (0.310724, 0.38662, 0.408829)), + 'bs': ((0.0, 0.054698, 0.374537), (180.0, 341.565063, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, 0.077073, 0.656977), (180.0, 0.0, 0.0), (0.338451, 0.40683, 0.309464)), + 'cs': ((0.0, 0.027906, 0.378118), (180.0, 5.710593, 0.0), (0.370551, 0.334462, 0.304923)), + 'sl': ((0.0, -0.022724, 0.342427), (180.0, 342.897278, 0.0), (0.319084, 0.383177, 0.368997)), + 'ss': ((0.0, 0.009212, 0.200081), (180.0, 351.869904, 0.0), (0.348083, 0.387685, 0.34839)), + 'rl': ((0.0, -0.031396, 0.374398), (180.0, 28.072487, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, -0.087833, 0.299892), (180.0, 32.471191, 0.0), (0.31491, 0.322002, 0.352713)), + 'ml': ((0.0, -0.030558, 0.768487), (180.0, 11.309933, 0.0), (0.564019, 0.45943, 0.581963)), + 'ms': ((0.0, -0.000704, 0.535926), (180.0, 10.619656, 0.0), (0.554896, 0.483187, 0.468427)), + 'fl': ((0.0, -0.264786, 0.741353), (180.0, 341.565063, 0.0), (0.527025, 0.507327, 0.604765)), + 'fs': ((0.0, -0.188123, 0.630058), (180.0, 345.963745, 0.0), (0.527899, 0.51, 0.458304))}, + 23: {'hl': ((0.0, -0.234499, 0.006963), (180.0, 320.194427, 0.0), (0.232292, 0.232292, 0.232292)), + 'hs': ((0.0, -0.153971, -0.084937), (180.0, 311.633545, 0.0), (0.235204, 0.213215, 0.222742)), + 'pl': ((0.0, -0.175888, 0.019199), (180.0, 330.0, 0.0), (0.388413, 0.305218, 0.330135)), + 'ps': ((0.0, -0.084697, -0.065677), (180.0, 323.972626, 0.0), (0.278152, 0.278152, 0.278152)), + 'dl': ((0.0, -0.136028, 0.167535), (180.0, 333.434937, 0.0), (0.262787, 0.275414, 0.275414)), + 'ds': ((0.0, -0.036535, 0.101389), (180.0, 323.130096, 0.0), (0.250177, 0.250177, 0.250177)), + 'bl': ((0.0, -0.217551, 0.145652), (180.0, 341.565063, 0.0), (0.293826, 0.293826, 0.293826)), + 'bs': ((0.0, -0.154371, -0.017713), (180.0, 338.198578, 0.0), (0.285894, 0.285894, 0.285894)), + 'cl': ((0.0, -0.262728, 0.16244), (180.0, 341.565063, 0.0), (0.319499, 0.319499, 0.319499)), + 'cs': ((0.0, -0.212642, -0.002031), (180.0, 339.44397, 0.0), (0.281438, 0.281438, 0.281438)), + 'sl': ((0.0, -0.205294, -0.330001), (180.0, 336.801422, 0.0), (0.284792, 0.336415, 0.368997)), + 'ss': ((0.0, -0.112658, -0.327791), (180.0, 319.398712, 0.0), (0.250103, 0.32364, 0.32364)), + 'rl': ((0.0, -0.150722, -0.181009), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.315328)), + 'rs': ((0.0, -0.178553, -0.229451), (180.0, 336.801422, 0.0), (0.245044, 0.278633, 0.333909)), + 'ml': ((0.0, -0.107093, 0.354219), (180.0, 320.194427, 0.0), (0.352015, 0.352015, 0.352015)), + 'ms': ((0.0, -0.205055, 0.073881), (180.0, 333.434937, 0.0), (0.361383, 0.334152, 0.334152)), + 'fl': ((0.0, -0.312542, 0.038707), (180.0, 341.565063, 0.0), (0.458827, 0.407775, 0.407775)), + 'fs': ((0.0, -0.463631, 0.088544), (180.0, 345.963745, 0.0), (0.401967, 0.401967, 0.401967))}, + 24: {'hl': ((-0.11, -0.184156, 0.592693), (144.460007, 349.700012, 23.200001), (0.151377, 0.11, 0.132524)), + 'hs': ((-0.108716, -0.050365, 0.439436), (197.354019, 8.746162, 23.198591), (0.143583, 0.101404, 0.135683)), + 'pl': ((-0.167152, -0.06624, 0.831271), (180.0, 15.255118, 16.38954), (0.18146, 0.157851, 0.20563)), + 'ps': ((-0.120682, 0.107162, 0.559596), (180.0, 3.814075, 18.434948), (0.173479, 0.173479, 0.173479)), + 'dl': ((-0.125428, 0.023363, 0.897022), (180.0, 20.0, 26.565052), (0.147892, 0.127208, 0.157488)), + 'ds': ((-0.090555, 0.08351, 0.687713), (180.0, 4.0, 15.0), (0.147183, 0.120944, 0.168701)), + 'bl': ((-0.061272, -0.038389, 0.91813), (180.0, 17.354025, 16.699244), (0.151674, 0.131145, 0.161627)), + 'bs': ((-0.10904, 0.08919, 0.587726), (180.0, 6.340192, 21.801409), (0.144145, 0.144145, 0.144145)), + 'cl': ((-0.196067, 0.131514, 0.803251), (197.525574, 26.565052, 23.198591), (0.186031, 0.15093, 0.175068)), + 'cs': ((-0.116544, 0.113971, 0.580452), (180.0, 8.0, 10.0), (0.151921, 0.13188, 0.163922)), + 'sl': ((-0.071043, 0.003687, 0.568108), (205.016891, 15.945395, 11.309933), (0.152124, 0.159079, 0.161514)), + 'ss': ((-0.102084, 0.029174, 0.439706), (203.629379, 5.0, 17.10273), (0.137263, 0.153714, 0.161588)), + 'rl': ((-0.10141, 0.080647, 0.488627), (200.556046, 30.465546, 15.524111), (0.142403, 0.132055, 0.141315)), + 'rs': ((-0.062125, 0.044286, 0.505601), (198.434952, 22.619865, 8.746162), (0.139132, 0.124646, 0.152884)), + 'ml': ((-0.174461, 0.038167, 1.181215), (198.434952, 18.434948, 11.309933), (0.20696, 0.185704, 0.183701)), + 'ms': ((-0.149403, 0.004724, 0.857602), (201.801407, 8.130102, 8.130102), (0.191181, 0.170632, 0.181855)), + 'fl': ((-0.208681, 0.112598, 1.082435), (208.300751, 36.027374, 8.130102), (0.213688, 0.16739, 0.17565)), + 'fs': ((-0.244239, 0.131354, 0.916029), (220.236359, 30.0, 7.125016), (0.21074, 0.159387, 0.18308))}, + 25: {'hl': ((0.0, -0.257746, 0.352813), (180.0, 320.194427, 0.0), (0.254668, 0.275077, 0.315898)), + 'hs': ((0.0, -0.205193, 0.236684), (180.0, 311.633545, 0.0), (0.261249, 0.268914, 0.335529)), + 'pl': ((0.0, -0.208555, 0.607816), (180.0, 323.130096, 0.0), (0.401451, 0.309874, 0.400292)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.1, 0.709002), (180.0, 333.434937, 0.0), (0.274969, 0.268582, 0.338836)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 341.565063, 0.0), (0.312558, 0.340434, 0.345114)), + 'bs': ((0.0, -0.044201, 0.392553), (180.0, 338.198578, 0.0), (0.29647, 0.32595, 0.338802)), + 'cl': ((0.0, -0.044478, 0.644346), (180.0, 352.405365, 0.0), (0.309155, 0.344185, 0.358538)), + 'cs': ((-0.048082, -0.11322, 0.439645), (180.0, 343.300751, 9.462322), (0.315072, 0.318579, 0.300552)), + 'sl': ((0.0, -0.260325, 0.448953), (180.0, 330.255127, 0.0), (0.195717, 0.251657, 0.303867)), + 'ss': ((0.0, -0.363379, 0.297801), (180.0, 315.0, 0.0), (0.155729, 0.168846, 0.264391)), + 'rl': ((0.0, -0.068453, 0.308529), (180.0, 340.346161, 0.0), (0.322224, 0.254291, 0.407353)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.359496, 0.288956, 0.401003)), + 'ml': ((0.0, -0.306013, 1.133884), (180.0, 333.434937, 0.0), (0.243077, 0.186923, 0.360584)), + 'ms': ((0.0, -0.094093, 0.870924), (180.0, 344.0, 0.0), (0.194086, 0.169004, 0.332554)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 341.565063, 0.0), (0.474508, 0.479617, 0.569219)), + 'fs': ((0.0, -0.188123, 0.623452), (180.0, 345.963745, 0.0), (0.448616, 0.464668, 0.480208))}, + 26: {'hl': ((0.0, -0.277809, 0.384787), (180.0, 310.601288, 0.0), (0.184928, 0.199671, 0.221602)), + 'hs': ((0.0, -0.157871, 0.252226), (180.0, 311.633545, 0.0), (0.206806, 0.190991, 0.215819)), + 'pl': ((0.0, 0.026523, 0.340678), (180.0, 330.0, 0.0), (0.377123, 0.292389, 0.381522)), + 'ps': ((0.0, -0.001829, 0.256953), (180.0, 323.972626, 0.0), (0.260122, 0.286854, 0.318477)), + 'dl': ((0.0, -0.051435, 0.581176), (180.0, 333.434937, 0.0), (0.232037, 0.226646, 0.285931)), + 'ds': ((0.0, -0.039145, 0.436639), (180.0, 323.130096, 0.0), (0.22056, 0.232648, 0.263107)), + 'bl': ((0.0, -0.05878, 0.529694), (180.0, 330.945404, 0.0), (0.24374, 0.287275, 0.303777)), + 'bs': ((0.0, 0.004136, 0.348848), (180.0, 338.198578, 0.0), (0.248931, 0.248931, 0.248931)), + 'cl': ((0.0, -0.020201, 0.462021), (180.0, 352.405365, 0.0), (0.29328, 0.330876, 0.318429)), + 'cs': ((0.0, -0.051409, 0.233579), (180.0, 343.300751, 0.0), (0.299086, 0.337743, 0.339323)), + 'sl': ((0.0, -0.111747, 0.296321), (180.0, 336.801422, 0.0), (0.199779, 0.25688, 0.247374)), + 'ss': ((0.0, -0.078654, 0.083501), (180.0, 319.398712, 0.0), (0.247413, 0.291703, 0.298101)), + 'rl': ((0.0, -0.066437, 0.315062), (180.0, 336.370636, 0.0), (0.190027, 0.223328, 0.238153)), + 'rs': ((0.0, -0.070996, 0.263283), (180.0, 330.945404, 0.0), (0.235096, 0.219512, 0.273396)), + 'ml': ((0.0, -0.006407, 0.517215), (180.0, 333.434937, 0.0), (0.416147, 0.38934, 0.475146)), + 'ms': ((0.0, 0.007342, 0.3426), (180.0, 325.007965, 0.0), (0.406536, 0.369898, 0.432582)), + 'fl': ((0.0, -0.089114, 0.528296), (180.0, 341.565063, 0.0), (0.380638, 0.441579, 0.526389)), + 'fs': ((0.0, -0.084017, 0.38766), (180.0, 326.309937, 0.0), (0.415967, 0.401863, 0.558658))}, + 27: {'hl': ((-0.524481, -0.206691, 0.50416), (265.236359, 318.366455, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((-0.543209, -0.294683, 0.298021), (281.309937, 326.309937, 334.798889), (0.291181, 0.268914, 0.268914)), + 'pl': ((-0.792834, -0.010562, 0.86648), (235.00798, 306.027374, 318.012787), (0.433654, 0.377233, 0.424631)), + 'ps': ((-0.652948, 0.063679, 0.399579), (261.869904, 337.380127, 330.945404), (0.348283, 0.348283, 0.348283)), + 'dl': ((-0.546014, 0.108562, 0.685092), (249.443954, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((-0.569904, 0.157461, 0.667186), (246.037506, 318.366455, 330.255127), (0.318405, 0.318405, 0.318405)), + 'bl': ((-0.567081, 0.388385, 0.528339), (230.906143, 333.434937, 15.945395), (0.334803, 0.38662, 0.408829)), + 'bs': ((-0.537026, 0.127819, 0.541201), (257.471191, 318.814087, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((-0.692766, 0.069207, 0.658108), (258.690063, 333.434937, 333.434937), (0.360603, 0.40683, 0.342697)), + 'cs': ((-0.636327, 0.216326, 0.572623), (245.556046, 319.763641, 345.068573), (0.350607, 0.403063, 0.307962)), + 'sl': ((-0.632226, 0.268632, 0.279659), (246.801407, 335.224854, 5.194429), (0.298001, 0.383177, 0.368997)), + 'ss': ((-0.61473, 0.19353, 0.375886), (246.801407, 319.398712, 0.0), (0.357568, 0.387685, 0.34839)), + 'rl': ((-0.515258, 0.181444, 0.355185), (233.130096, 333.434937, 333.434937), (0.289685, 0.276488, 0.25764)), + 'rs': ((-0.528824, 0.217179, 0.259516), (233.130096, 335.55603, 343.300751), (0.298414, 0.26948, 0.295182)), + 'ml': ((-0.916889, 0.32838, 0.892706), (228.366455, 323.130096, 335.224854), (0.597447, 0.45943, 0.581963)), + 'ms': ((-0.833346, 0.134992, 0.710331), (254.744888, 321.340179, 344.744873), (0.554896, 0.483187, 0.468427)), + 'fl': ((-0.84855, 0.495989, 0.585959), (225.0, 335.55603, 6.340192), (0.527025, 0.507327, 0.604765)), + 'fs': ((-0.80813, 0.176572, 0.642824), (250.463348, 338.198578, 345.963745), (0.527899, 0.51, 0.458304))}, + 28: {'hl': ((0.0, -0.261502, 0.37089), (180.0, 311.185913, 0.0), (0.21093, 0.229836, 0.205056)), + 'hs': ((0.0, -0.233683, 0.314243), (180.0, 319.398712, 0.0), (0.162104, 0.185872, 0.149708)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 320.194427, 0.0), (0.327264, 0.327264, 0.327264)), + 'ps': ((0.0, -0.052272, 0.386975), (180.0, 323.972626, 0.0), (0.264953, 0.264953, 0.264953)), + 'dl': ((0.0, -0.012768, 0.538493), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, 0.046735, 0.369845), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, 0.045479, 0.303616), (180.0, 329.743561, 0.0), (0.334803, 0.38662, 0.408829)), + 'bs': ((0.0, 0.024414, 0.232217), (180.0, 326.309937, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, 0.001005, 0.360967), (180.0, 352.405365, 0.0), (0.360603, 0.40683, 0.342697)), + 'cs': ((0.0, -0.021548, 0.247119), (180.0, 355.236359, 12.094757), (0.375144, 0.431271, 0.329515)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.230655, 0.294738, 0.294738)), + 'ss': ((0.0, -0.155476, 0.164886), (180.0, 319.398712, 0.0), (0.225954, 0.302162, 0.291002)), + 'rl': ((0.0, -0.039658, 0.3126), (180.0, 340.346161, 0.0), (0.231198, 0.231198, 0.231198)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.257363, 0.257363, 0.257363)), + 'ml': ((0.0, -0.068179, 0.768487), (180.0, 348.690063, 8.746162), (0.414675, 0.414675, 0.414675)), + 'ms': ((0.0, -0.000002, 0.541642), (180.0, 344.0, 4.398705), (0.389266, 0.389266, 0.389266)), + 'fl': ((0.058768, 0.008708, 0.382249), (180.0, 341.565063, 5.710593), (0.527025, 0.507327, 0.604765)), + 'fs': ((0.0, -0.061993, 0.504703), (180.0, 345.963745, 8.746162), (0.436285, 0.51, 0.458304))}, + 29: {'hl': ((0.0, -0.416379, 0.521259), (180.0, 320.194427, 0.0), (0.228402, 0.220843, 0.222041)), + 'hs': ((0.0, -0.255588, 0.433379), (180.0, 326.309937, 0.0), (0.187592, 0.173247, 0.173247)), + 'pl': ((0.0, -0.117104, 0.57), (180.0, 330.0, 0.0), (0.556625, 0.377233, 0.424631)), + 'ps': ((0.0, -0.072488, 0.435111), (180.0, 323.972626, 0.0), (0.425365, 0.348015, 0.348283)), + 'dl': ((0.0, -0.114301, 0.685092), (180.0, 333.434937, 0.0), (0.416012, 0.289076, 0.364691)), + 'ds': ((0.0, -0.104245, 0.567932), (180.0, 323.130096, 0.0), (0.33946, 0.318405, 0.318405)), + 'bl': ((0.0, -0.057791, 0.507975), (180.0, 336.037506, 0.0), (0.43777, 0.391952, 0.391952)), + 'bs': ((0.0, 0.014032, 0.392553), (180.0, 338.198578, 0.0), (0.446885, 0.370112, 0.38918)), + 'cl': ((0.0, -0.02548, 0.616522), (180.0, 352.405365, 0.0), (0.505136, 0.410945, 0.342697)), + 'cs': ((0.0, -0.074619, 0.363931), (180.0, 347.471191, 0.0), (0.55192, 0.428574, 0.43323)), + 'sl': ((0.0, -0.134215, 0.445321), (180.0, 336.801422, 0.0), (0.307777, 0.383177, 0.368997)), + 'ss': ((0.0, -0.251233, 0.287217), (180.0, 319.398712, 0.0), (0.307606, 0.387685, 0.34839)), + 'rl': ((0.0, -0.088597, 0.44314), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, -0.090461, 0.398918), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ml': ((0.0, -0.167995, 0.81218), (180.0, 333.434937, 0.0), (0.659638, 0.45943, 0.581963)), + 'ms': ((0.0, -0.121411, 0.641263), (180.0, 333.434937, 0.0), (0.620033, 0.483187, 0.468427)), + 'fl': ((0.0, -0.256771, 0.823526), (180.0, 348.690063, 0.0), (0.658947, 0.507327, 0.604765)), + 'fs': ((0.0, -0.171212, 0.623452), (180.0, 345.963745, 0.0), (0.717775, 0.572076, 0.485491))}, + 30: {'hl': ((0.0, -0.302346, 0.406916), (180.0, 320.194427, 0.0), (0.28056, 0.273055, 0.285614)), + 'hs': ((0.0, -0.179472, 0.236684), (180.0, 311.633545, 0.0), (0.284893, 0.261428, 0.268914)), + 'pl': ((0.0, -0.241035, 0.660515), (180.0, 330.0, 0.0), (0.411001, 0.351334, 0.477315)), + 'ps': ((0.0, -0.089396, 0.414964), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.093456, 0.725988), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, -0.048714, 0.511672), (180.0, 323.130096, 0.0), (0.309983, 0.309983, 0.309983)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 323.972626, 0.0), (0.371269, 0.325894, 0.344614)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.353902, 0.340747, 0.326553)), + 'cl': ((0.0, 0.007365, 0.645978), (180.0, 0.0, 0.0), (0.390058, 0.370094, 0.342697)), + 'cs': ((0.0, -0.072315, 0.474424), (173.659805, 347.471191, 0.0), (0.350231, 0.343982, 0.307962)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.318263, 0.40923, 0.394086)), + 'ss': ((0.0, -0.165839, 0.22666), (180.0, 325.007965, 0.0), (0.310093, 0.376156, 0.354515)), + 'rl': ((0.0, -0.052403, 0.396194), (180.0, 340.346161, 0.0), (0.287082, 0.257379, 0.25764)), + 'rs': ((0.0, -0.047608, 0.345909), (180.0, 336.801422, 0.0), (0.344324, 0.310939, 0.340595)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.530841, 0.445802, 0.581963)), + 'ms': ((0.0, -0.089467, 0.663395), (180.0, 344.0, 0.0), (0.466715, 0.405444, 0.43353)), + 'fl': ((0.0, -0.171277, 0.739647), (180.0, 341.565063, 0.0), (0.527025, 0.507327, 0.604765)), + 'fs': ((0.0, -0.164048, 0.583369), (180.0, 345.963745, 0.0), (0.514292, 0.493466, 0.458304))}, + 31: {'hl': ((0.0, -0.220449, 0.119023), (180.0, 345.963745, 0.0), (0.219105, 0.211854, 0.213004)), + 'hs': ((0.0, -0.113965, 0.072509), (180.0, 341.565063, 0.0), (0.20267, 0.187171, 0.187171)), + 'pl': ((0.0, 0.14036, -0.04792), (180.0, 338.962494, 0.0), (0.37692, 0.32788, 0.369077)), + 'ps': ((0.0, 0.089531, -0.099235), (180.0, 342.349884, 0.0), (0.293212, 0.291542, 0.29589)), + 'dl': ((0.0, 0.252744, 0.11708), (180.0, 333.434937, 0.0), (0.277106, 0.289076, 0.364691)), + 'ds': ((0.0, 0.230431, 0.076485), (180.0, 344.054596, 0.0), (0.255877, 0.319512, 0.319512)), + 'bl': ((0.0, 0.13071, 0.0), (180.0, 341.565063, 0.0), (0.236665, 0.288235, 0.304792)), + 'bs': ((0.0, 0.247398, -0.020869), (180.0, 338.198578, 0.0), (0.275836, 0.275836, 0.275836)), + 'cl': ((0.0, -0.000939, 0.088425), (180.0, 6.340192, 0.0), (0.294071, 0.283581, 0.342697)), + 'cs': ((0.0, 0.144906, -0.046214), (180.0, 355.914398, 0.0), (0.288037, 0.354037, 0.307962)), + 'sl': ((0.0, 0.23445, -0.275263), (180.0, 344.744873, 0.0), (0.28155, 0.362024, 0.348627)), + 'ss': ((0.0, 0.112075, -0.209017), (180.0, 339.44397, 0.0), (0.290682, 0.315166, 0.283221)), + 'rl': ((0.0, 0.026388, -0.139429), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, 0.149484, -0.250004), (180.0, 336.801422, 0.0), (0.310614, 0.280498, 0.30725)), + 'ml': ((0.0, 0.127118, 0.101103), (180.0, 354.289398, 0.0), (0.411167, 0.371526, 0.470614)), + 'ms': ((0.0, 0.165484, -0.053722), (180.0, 353.659821, 0.0), (0.417746, 0.409935, 0.397413)), + 'fl': ((0.0, 0.156775, 0.016309), (180.0, 341.565063, 0.0), (0.410564, 0.454725, 0.454725)), + 'fs': ((0.0, 0.267444, -0.051085), (180.0, 345.963745, 0.0), (0.400003, 0.400003, 0.400003))}, + 32: {'hl': ((0.0, -0.268822, 0.406916), (180.0, 320.194427, 0.0), (0.269517, 0.260597, 0.262011)), + 'hs': ((0.0, -0.166399, 0.251857), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.188816, 0.613454), (180.0, 330.0, 0.0), (0.419755, 0.345824, 0.389276)), + 'ps': ((0.0, -0.084697, 0.387917), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.085445, 0.685092), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, -0.033759, 0.46431), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 341.565063, 0.0), (0.333684, 0.385327, 0.407462)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.044478, 0.645978), (180.0, 352.405365, 0.0), (0.331049, 0.373487, 0.31461)), + 'ss': ((0.0, -0.128516, 0.126139), (180.0, 319.398712, 0.0), (0.337212, 0.387685, 0.34839)), + 'rl': ((0.0, -0.039212, 0.23774), (180.0, 340.346161, 0.0), (0.357267, 0.292876, 0.25764)), + 'rs': ((0.0, -0.083155, 0.32749), (180.0, 336.801422, 0.0), (0.344561, 0.290413, 0.352713)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.526441, 0.45943, 0.581963)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.46582, 0.405622, 0.393232)), + 'fl': ((0.0, -0.142518, 0.701924), (180.0, 341.565063, 0.0), (0.529662, 0.509865, 0.607791)), + 'fs': ((0.0, -0.117987, 0.623452), (180.0, 345.963745, 0.0), (0.496473, 0.51, 0.458304))}, + 33: {'hl': ((0.0, -0.276202, 0.449818), (180.0, 320.194427, 0.0), (0.160414, 0.164019, 0.164019)), + 'hs': ((0.0, -0.128921, 0.27467), (180.0, 323.130096, 0.0), (0.23257, 0.164909, 0.164909)), + 'pl': ((0.0, -0.123782, 0.57), (180.0, 333.434937, 0.0), (0.38581, 0.211429, 0.237994)), + 'ps': ((0.0, 0.044661, 0.244389), (180.0, 341.565063, 0.0), (0.325913, 0.240032, 0.240032)), + 'dl': ((0.0, 0.028148, 0.356259), (180.0, 0.0, 0.0), (0.337421, 0.243651, 0.307384)), + 'ds': ((0.0, 0.176612, 0.285511), (180.0, 351.253845, 0.0), (0.34149, 0.276383, 0.310987)), + 'bl': ((0.0, 0.280522, 0.28634), (180.0, 341.565063, 0.0), (0.355425, 0.355425, 0.355425)), + 'bs': ((0.0, 0.221584, 0.183878), (180.0, 338.198578, 0.0), (0.318295, 0.318295, 0.318295)), + 'cl': ((0.0, 0.119931, 0.181552), (180.0, 6.340192, 0.0), (0.382162, 0.346204, 0.346204)), + 'cs': ((0.0, 0.060867, 0.118824), (180.0, 356.185913, 0.0), (0.37923, 0.318294, 0.318294)), + 'sl': ((0.0, -0.006457, 0.119641), (180.0, 336.801422, 0.0), (0.308605, 0.267992, 0.267992)), + 'ss': ((0.0, -0.003716, 0.031253), (180.0, 330.255127, 0.0), (0.303881, 0.265755, 0.265755)), + 'rl': ((0.0, 0.130827, 0.192487), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, 0.026163, 0.170432), (180.0, 352.405365, 0.0), (0.33485, 0.248124, 0.254152)), + 'ml': ((0.0, 0.162528, 0.732716), (180.0, 355.914398, 0.0), (0.473264, 0.359797, 0.359797)), + 'ms': ((0.0, 0.030821, 0.470736), (180.0, 344.0, 0.0), (0.426564, 0.284277, 0.346083)), + 'fl': ((0.0, 0.002367, 0.411145), (180.0, 347.471191, 0.0), (0.498433, 0.397726, 0.457847)), + 'fs': ((0.0, -0.021222, 0.217551), (180.0, 345.963745, 0.0), (0.54123, 0.357232, 0.48969))}, + 34: {'hl': ((0.0, -0.241063, 0.36174), (180.0, 326.627808, 0.0), (0.35835, 0.348827, 0.359012)), + 'hs': ((0.0, -0.205193, 0.260101), (180.0, 323.130096, 0.0), (0.347553, 0.351846, 0.343119)), + 'pl': ((0.0, -0.015615, 0.335195), (180.0, 330.0, 0.0), (0.602317, 0.546368, 0.599335)), + 'ps': ((0.0, -0.014131, 0.262376), (180.0, 327.994629, 0.0), (0.47315, 0.477293, 0.452478)), + 'dl': ((0.0, 0.006244, 0.566997), (180.0, 345.963745, 0.0), (0.418932, 0.418932, 0.418932)), + 'ds': ((0.0, 0.051982, 0.475703), (180.0, 351.253845, 0.0), (0.421277, 0.421277, 0.421277)), + 'bl': ((0.0, -0.001356, 0.427197), (180.0, 341.565063, 0.0), (0.451408, 0.451408, 0.462089)), + 'bs': ((0.0, 0.088443, 0.187984), (180.0, 345.963745, 0.0), (0.469158, 0.443694, 0.443694)), + 'cl': ((0.0, -0.047778, 0.369149), (180.0, 8.0, 0.0), (0.548045, 0.517806, 0.488176)), + 'cs': ((0.0, -0.045206, 0.329218), (180.0, 8.130102, 0.0), (0.496898, 0.542449, 0.492921)), + 'sl': ((0.0, -0.085306, 0.108033), (180.0, 333.434937, 0.0), (0.542136, 0.523295, 0.49822)), + 'ss': ((0.0, -0.087918, 0.120867), (180.0, 333.434937, 0.0), (0.457195, 0.511239, 0.456877)), + 'rl': ((0.0, -0.060758, 0.120939), (180.0, 344.744873, 0.0), (0.53018, 0.434141, 0.415247)), + 'rs': ((0.0, -0.035385, 0.173212), (180.0, 355.914398, 0.0), (0.435072, 0.431223, 0.413015)), + 'ml': ((0.0, 0.026015, 0.465531), (180.0, 333.434937, 0.0), (0.754976, 0.676776, 0.676776)), + 'ms': ((0.0, 0.00079, 0.419305), (180.0, 344.0, 0.0), (0.715514, 0.571614, 0.59854)), + 'fl': ((0.0, -0.083368, 0.453166), (180.0, 341.565063, 0.0), (0.700743, 0.700743, 0.700743)), + 'fs': ((0.0, -0.0811, 0.457792), (180.0, 345.963745, 0.0), (0.703631, 0.6177, 0.6177))}, + 35: {'hl': ((0.0, -0.266643, 0.406916), (180.0, 348.690063, 0.0), (0.270584, 0.284073, 0.285614)), + 'hs': ((0.0, -0.1642, 0.253538), (180.0, 355.236359, 0.0), (0.304984, 0.268914, 0.268914)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.424631)), + 'ps': ((0.0, -0.06062, 0.329113), (180.0, 339.44397, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, 0.017045, 0.675274), (180.0, 354.289398, 0.0), (0.328408, 0.289076, 0.364691)), + 'ds': ((0.0, 0.033582, 0.46431), (180.0, 341.565063, 0.0), (0.348472, 0.318405, 0.318405)), + 'bl': ((0.0, -0.045561, 0.641867), (180.0, 345.963745, 0.0), (0.316367, 0.38662, 0.408829)), + 'bs': ((0.0, 0.015046, 0.392553), (180.0, 352.874969, 0.0), (0.323645, 0.326553, 0.326553)), + 'cl': ((0.0, 0.042263, 0.635366), (180.0, 0.0, 0.0), (0.35145, 0.40683, 0.342697)), + 'cs': ((0.0, 0.072944, 0.366422), (180.0, 0.0, 0.0), (0.370864, 0.403063, 0.307962)), + 'sl': ((0.0, -0.037346, 0.301475), (180.0, 350.537689, 0.0), (0.317633, 0.383177, 0.368997)), + 'ss': ((0.0, 0.030194, 0.191854), (180.0, 348.690063, 0.0), (0.348539, 0.387685, 0.34839)), + 'rl': ((0.0, 0.032847, 0.34277), (180.0, 12.994617, 0.0), (0.302167, 0.276488, 0.25764)), + 'rs': ((0.0, -0.041031, 0.257018), (180.0, 21.250505, 0.0), (0.317561, 0.322002, 0.352713)), + 'ml': ((0.0, -0.000721, 0.750719), (180.0, 3.576334, 0.0), (0.559837, 0.45943, 0.581963)), + 'ms': ((0.0, -0.007377, 0.531291), (180.0, 4.763642, 0.0), (0.544796, 0.483187, 0.468427)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 341.565063, 0.0), (0.527025, 0.507327, 0.604765))}, + 36: {'hl': ((0.0, -0.347161, 0.406916), (180.0, 320.194427, 0.0), (0.319728, 0.300149, 0.285614)), + 'hs': ((0.0, -0.183052, 0.263381), (180.0, 311.633545, 0.0), (0.363318, 0.268914, 0.268914)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.553432, 0.377233, 0.424631)), + 'ps': ((0.0, -0.044527, 0.355215), (180.0, 323.972626, 0.0), (0.431773, 0.36121, 0.388921)), + 'dl': ((0.0, -0.081082, 0.726366), (180.0, 333.434937, 0.0), (0.383692, 0.289076, 0.364691)), + 'ds': ((0.0, -0.035633, 0.46431), (180.0, 323.130096, 0.0), (0.432336, 0.318405, 0.318405)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 341.565063, 0.0), (0.43827, 0.38662, 0.408829)), + 'bs': ((0.0, 0.004136, 0.394945), (180.0, 338.198578, 0.0), (0.404135, 0.326553, 0.326553)), + 'cl': ((0.0, -0.060938, 0.645978), (180.0, 352.405365, 0.0), (0.459022, 0.40683, 0.342697)), + 'cs': ((0.0, -0.109454, 0.411967), (180.0, 343.300751, 0.0), (0.476119, 0.403063, 0.307962)), + 'sl': ((0.0, -0.099204, 0.296321), (180.0, 336.801422, 0.0), (0.390197, 0.383177, 0.368997)), + 'ss': ((0.0, -0.123037, 0.1702), (180.0, 319.398712, 0.0), (0.394785, 0.387685, 0.34839)), + 'rl': ((0.0, -0.075822, 0.353435), (180.0, 340.346161, 0.0), (0.374044, 0.276488, 0.25764)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ml': ((0.0, -0.055902, 0.768487), (180.0, 333.434937, 0.0), (0.702092, 0.45943, 0.581963)), + 'ms': ((0.0, -0.124136, 0.614941), (180.0, 344.0, 0.0), (0.578858, 0.483187, 0.468427)), + 'fl': ((0.0, -0.250494, 0.701924), (180.0, 341.565063, 0.0), (0.675813, 0.628315, 0.604765)), + 'fs': ((0.0, -0.153541, 0.623452), (180.0, 345.963745, 0.0), (0.628635, 0.51, 0.458304))}, + 37: {'hl': ((0.0, -0.109185, 0.182812), (180.0, 320.194427, 0.0), (0.412429, 0.412429, 0.412429)), + 'hs': ((0.0, -0.115121, 0.194043), (180.0, 311.633545, 0.0), (0.407209, 0.407209, 0.407209)), + 'pl': ((0.0, -0.024329, 0.288434), (180.0, 330.0, 0.0), (0.594207, 0.594207, 0.594207)), + 'ps': ((0.0, 0.079729, 0.157044), (180.0, 323.972626, 0.0), (0.496503, 0.496503, 0.496503)), + 'dl': ((0.0, -0.000942, 0.470992), (180.0, 333.434937, 0.0), (0.415705, 0.415705, 0.415705)), + 'ds': ((0.0, 0.037145, 0.335535), (180.0, 323.130096, 0.0), (0.422923, 0.422923, 0.422923)), + 'bl': ((0.0, 0.031956, 0.375886), (180.0, 341.565063, 0.0), (0.416934, 0.416934, 0.416934)), + 'bs': ((0.0, 0.073504, 0.186075), (180.0, 338.198578, 0.0), (0.415699, 0.415699, 0.415699)), + 'cl': ((0.0, 0.004228, 0.316654), (180.0, 352.405365, 0.0), (0.492512, 0.492512, 0.492512)), + 'cs': ((0.0, -0.053196, 0.245083), (180.0, 343.300751, 0.0), (0.504193, 0.504193, 0.504193)), + 'sl': ((0.0, 0.002899, 0.005551), (180.0, 336.801422, 0.0), (0.509032, 0.509032, 0.509032)), + 'ss': ((0.0, 0.08639, -0.02387), (180.0, 319.398712, 0.0), (0.508092, 0.508092, 0.508092)), + 'rl': ((0.0, -0.000893, 0.084083), (180.0, 340.346161, 0.0), (0.464507, 0.464507, 0.464507)), + 'rs': ((0.0, -0.000596, 0.048775), (180.0, 336.801422, 0.0), (0.486521, 0.486521, 0.486521)), + 'ml': ((0.0, 0.005332, 0.38043), (180.0, 347.471191, 0.0), (0.621133, 0.621133, 0.621133)), + 'ms': ((0.0, 0.002662, 0.434725), (180.0, 344.0, 0.0), (0.591085, 0.591085, 0.591085)), + 'fl': ((0.0, -0.046462, 0.421144), (180.0, 341.565063, 0.0), (0.606256, 0.606256, 0.606256)), + 'fs': ((0.0, -0.075515, 0.4517), (180.0, 345.963745, 0.0), (0.588141, 0.588141, 0.588141))}, + 38: {'hl': ((0.0, -0.189091, 0.312985), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.135863, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.025753, 0.421012), (180.0, 319.398712, 0.0), (0.463344, 0.374374, 0.427883)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, 0.021175, 0.49384), (180.0, 333.434937, 0.0), (0.328248, 0.328248, 0.328248)), + 'ds': ((0.0, 0.035984, 0.40283), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, 0.009891, 0.420859), (180.0, 330.255127, 0.0), (0.34536, 0.38662, 0.408829)), + 'bs': ((0.0, 0.004136, 0.303897), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.026523, 0.329198), (180.0, 352.405365, 0.0), (0.41184, 0.41184, 0.41184)), + 'cs': ((0.0, -0.093244, 0.256664), (180.0, 343.300751, 0.0), (0.384419, 0.384419, 0.384419)), + 'sl': ((0.0, -0.05817, 0.151832), (180.0, 336.801422, 0.0), (0.362878, 0.362878, 0.362878)), + 'ss': ((0.0, -0.089353, 0.067922), (180.0, 319.398712, 0.0), (0.359873, 0.359873, 0.359873)), + 'rl': ((0.0, -0.00253, 0.209439), (180.0, 340.346161, 0.0), (0.331792, 0.331792, 0.331792)), + 'rs': ((0.0, -0.01575, 0.152739), (180.0, 336.801422, 0.0), (0.356531, 0.356531, 0.356531)), + 'ml': ((0.0, -0.032083, 0.604757), (180.0, 333.434937, 0.0), (0.597447, 0.45943, 0.581963)), + 'ms': ((0.0, -0.009932, 0.492561), (180.0, 344.0, 0.0), (0.554896, 0.483187, 0.468427)), + 'fl': ((0.0, -0.061669, 0.439301), (180.0, 341.565063, 0.0), (0.548073, 0.548073, 0.548073)), + 'fs': ((0.0, -0.076523, 0.530715), (180.0, 345.963745, 0.0), (0.527899, 0.51, 0.458304))}, + 39: {'hl': ((0.0, -0.370962, 0.465244), (180.0, 315.0, 0.0), (0.194279, 0.194279, 0.194279)), + 'hs': ((0.0, -0.27024, 0.351372), (180.0, 311.633545, 0.0), (0.155642, 0.155642, 0.155642)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.424631)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, 0.003874, 0.52465), (180.0, 333.434937, 0.0), (0.355373, 0.355373, 0.355373)), + 'ds': ((0.0, 0.069869, 0.31272), (180.0, 323.130096, 0.0), (0.36301, 0.36301, 0.36301)), + 'bl': ((0.0, -0.003084, 0.459231), (180.0, 340.016907, 0.0), (0.342305, 0.342305, 0.342305)), + 'bs': ((0.0, 0.032354, 0.298681), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.007892, 0.498436), (180.0, 352.405365, 0.0), (0.37638, 0.37638, 0.37638)), + 'cs': ((0.0, -0.049355, 0.279454), (180.0, 343.300751, 0.0), (0.388102, 0.388102, 0.388102)), + 'sl': ((0.0, -0.033132, 0.137355), (180.0, 336.037506, 0.0), (0.359353, 0.359353, 0.359353)), + 'ss': ((0.0, -0.092922, 0.06292), (180.0, 319.398712, 0.0), (0.357568, 0.387685, 0.34839)), + 'rl': ((0.0, -0.016446, 0.309796), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, -0.019304, 0.2101), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ml': ((0.0, -0.040067, 0.564875), (180.0, 333.434937, 0.0), (0.597447, 0.45943, 0.581963)), + 'ms': ((0.0, -0.051857, 0.478629), (180.0, 344.0, 0.0), (0.554896, 0.483187, 0.468427)), + 'fl': ((0.0, -0.092176, 0.491097), (180.0, 341.565063, 0.0), (0.527025, 0.507327, 0.604765)), + 'fs': ((0.0, -0.102106, 0.406191), (180.0, 345.963745, 0.0), (0.523747, 0.523747, 0.523747))}, + 40: {'hl': ((0.0, -0.251709, 0.329275), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.205193, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, 0.011017, 0.32744), (180.0, 330.0, 0.0), (0.431377, 0.431377, 0.431377)), + 'ps': ((0.0, 0.073004, 0.277736), (180.0, 323.972626, 0.0), (0.362429, 0.362429, 0.362429)), + 'dl': ((0.0, -0.044813, 0.42961), (180.0, 333.434937, 0.0), (0.337081, 0.337081, 0.337081)), + 'ds': ((0.0, -0.048714, 0.350014), (180.0, 333.434937, 0.0), (0.340109, 0.340109, 0.340109)), + 'bl': ((0.0, 0.01989, 0.360009), (180.0, 336.037506, 0.0), (0.328004, 0.328004, 0.328004)), + 'bs': ((0.0, 0.000781, 0.296234), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.084146, 0.338479), (180.0, 340.016907, 0.0), (0.436503, 0.436503, 0.436503)), + 'cs': ((0.0, -0.084972, 0.275964), (180.0, 343.300751, 0.0), (0.440246, 0.440246, 0.440246)), + 'sl': ((0.0, -0.036041, 0.139261), (180.0, 336.801422, 0.0), (0.330952, 0.330952, 0.330952)), + 'ss': ((0.0, -0.03715, 0.039413), (180.0, 319.398712, 0.0), (0.342313, 0.342313, 0.342313)), + 'rl': ((0.0, -0.03045, 0.157932), (180.0, 340.346161, 0.0), (0.39319, 0.329908, 0.351655)), + 'rs': ((0.0, -0.062976, 0.121681), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ml': ((0.0, -0.041088, 0.530235), (180.0, 333.434937, 0.0), (0.49824, 0.437567, 0.437567)), + 'ms': ((0.0, -0.051857, 0.449145), (180.0, 344.0, 0.0), (0.474517, 0.443477, 0.443477)), + 'fl': ((0.0, -0.144287, 0.503187), (180.0, 344.054596, 0.0), (0.516411, 0.516411, 0.516411)), + 'fs': ((0.0, -0.142902, 0.428907), (180.0, 345.963745, 0.0), (0.520535, 0.520535, 0.520535))}, + 41: {'hl': ((0.0, -0.084578, 0.354849), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, 0.008233, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, 0.143572, 0.398106), (180.0, 330.0, 0.0), (0.374423, 0.377233, 0.424631)), + 'ps': ((0.0, 0.154568, 0.286657), (180.0, 323.972626, 0.0), (0.317053, 0.348283, 0.348283)), + 'dl': ((0.0, 0.120854, 0.517766), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, 0.138863, 0.422914), (180.0, 343.300751, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, 0.162537, 0.383821), (180.0, 332.447174, 0.0), (0.334803, 0.38662, 0.408829)), + 'bs': ((0.0, 0.147779, 0.392553), (180.0, 338.198578, 0.0), (0.33965, 0.33965, 0.33965)), + 'cl': ((0.0, 0.136744, 0.399326), (180.0, 352.405365, 0.0), (0.360603, 0.40683, 0.342697)), + 'cs': ((0.0, 0.094793, 0.26724), (180.0, 348.690063, 0.0), (0.350607, 0.403063, 0.307962)), + 'sl': ((0.0, 0.131652, 0.172827), (180.0, 336.801422, 0.0), (0.346467, 0.383177, 0.368997)), + 'ss': ((0.0, 0.113461, 0.126139), (180.0, 319.398712, 0.0), (0.357568, 0.387685, 0.34839)), + 'rl': ((0.0, 0.07514, 0.106074), (180.0, 340.346161, 0.0), (0.35494, 0.310819, 0.325636)), + 'rs': ((0.0, 0.110525, 0.112845), (180.0, 336.801422, 0.0), (0.313714, 0.322002, 0.352713)), + 'ml': ((0.0, 0.201553, 0.490531), (180.0, 333.434937, 0.0), (0.505534, 0.467415, 0.581963)), + 'ms': ((0.0, 0.14557, 0.549109), (180.0, 344.0, 0.0), (0.428046, 0.421804, 0.469385)), + 'fl': ((0.0, 0.086472, 0.552777), (180.0, 341.565063, 0.0), (0.463455, 0.463455, 0.463455)), + 'fs': ((0.0, 0.065404, 0.441737), (180.0, 345.963745, 0.0), (0.450962, 0.450962, 0.450962))}, + 42: {'hl': ((0.0, -0.286549, 0.386187), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.205193, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.017794, 0.340809), (180.0, 330.0, 0.0), (0.458436, 0.458436, 0.458436)), + 'ps': ((0.0, 0.097452, 0.239314), (180.0, 323.972626, 0.0), (0.389192, 0.389192, 0.389192)), + 'dl': ((0.0, 0.043303, 0.45846), (180.0, 352.405365, 0.0), (0.341148, 0.341148, 0.341148)), + 'ds': ((0.0, 0.03943, 0.391337), (180.0, 345.963745, 0.0), (0.324893, 0.324893, 0.324893)), + 'bl': ((0.0, 0.064688, 0.291713), (180.0, 341.565063, 0.0), (0.349791, 0.403928, 0.427131)), + 'bs': ((0.0, 0.061082, 0.270894), (180.0, 338.198578, 0.0), (0.367503, 0.367503, 0.367503)), + 'cl': ((0.0, 0.012301, 0.394174), (180.0, 5.194429, 0.0), (0.360603, 0.40683, 0.342697)), + 'cs': ((0.0, -0.027674, 0.270412), (180.0, 0.0, 0.0), (0.401389, 0.461443, 0.352567)), + 'sl': ((0.0, -0.049582, 0.109724), (180.0, 336.801422, 0.0), (0.377995, 0.437348, 0.32446)), + 'ss': ((0.0, -0.020503, 0.042076), (180.0, 319.398712, 0.0), (0.387488, 0.387488, 0.387488)), + 'rl': ((0.0, -0.05794, 0.19864), (180.0, 340.346161, 0.0), (0.318473, 0.318473, 0.318473)), + 'rs': ((0.0, -0.028572, 0.162014), (180.0, 336.801422, 0.0), (0.328632, 0.328632, 0.328632)), + 'ml': ((0.0, 0.06944, 0.497543), (180.0, 333.434937, 0.0), (0.525626, 0.525626, 0.525626)), + 'ms': ((0.0, -0.024846, 0.441586), (180.0, 344.0, 0.0), (0.456958, 0.456958, 0.456958)), + 'fl': ((0.0, 0.059544, 0.327538), (180.0, 341.565063, 0.0), (0.465083, 0.602695, 0.527746)), + 'fs': ((0.0, 0.008886, 0.310591), (180.0, 345.963745, 0.0), (0.493619, 0.518037, 0.518037))}, + 43: {'hl': ((0.0, -0.228686, 0.356353), (180.0, 320.194427, 0.0), (0.23157, 0.23157, 0.23157)), + 'hs': ((0.0, -0.182653, 0.236684), (180.0, 311.633545, 0.0), (0.21686, 0.21686, 0.21686)), + 'pl': ((0.0, -0.051214, 0.370904), (180.0, 330.0, 0.0), (0.368753, 0.354921, 0.368753)), + 'ps': ((0.0, 0.087279, 0.220612), (180.0, 338.198578, 0.0), (0.31121, 0.31121, 0.31121)), + 'dl': ((0.0, -0.030876, 0.485436), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, 0.032724, 0.333517), (180.0, 333.434937, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, -0.044879, 0.401084), (180.0, 337.380127, 0.0), (0.353446, 0.353446, 0.353446)), + 'bs': ((0.0, 0.010719, 0.297905), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, 0.007381, 0.422222), (180.0, 352.405365, 0.0), (0.324078, 0.324078, 0.324078)), + 'cs': ((0.0, -0.051448, 0.21626), (180.0, 343.300751, 0.0), (0.350607, 0.350607, 0.350607)), + 'sl': ((0.0, -0.049603, 0.161602), (180.0, 336.801422, 0.0), (0.300563, 0.300563, 0.300563)), + 'ss': ((0.0, -0.039971, 0.032108), (180.0, 319.398712, 0.0), (0.327619, 0.327619, 0.327619)), + 'rl': ((0.0, -0.024611, 0.188832), (180.0, 340.346161, 0.0), (0.276816, 0.276816, 0.276816)), + 'rs': ((0.0, -0.039301, 0.158296), (180.0, 336.801422, 0.0), (0.276859, 0.276859, 0.276859)), + 'ml': ((0.0, -0.063149, 0.621559), (180.0, 344.054596, 0.0), (0.414732, 0.414732, 0.414732)), + 'ms': ((0.0, -0.051857, 0.43725), (180.0, 344.0, 0.0), (0.385786, 0.385786, 0.385786)), + 'fl': ((0.0, -0.115042, 0.523577), (180.0, 341.565063, 0.0), (0.451344, 0.451344, 0.451344)), + 'fs': ((0.0, -0.095579, 0.345507), (180.0, 345.963745, 0.0), (0.453144, 0.453144, 0.453144))}, + 44: {'hl': ((0.0, -0.328898, 0.453809), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.205193, 0.268249), (180.0, 311.633545, 0.0), (0.302123, 0.302123, 0.302123)), + 'pl': ((0.0, -0.175888, 0.628293), (180.0, 330.0, 0.0), (0.451127, 0.451127, 0.451127)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.381628, 0.381628, 0.381628)), + 'dl': ((0.0, -0.135721, 0.753186), (180.0, 333.434937, 0.0), (0.342229, 0.342229, 0.435064)), + 'ds': ((0.0, -0.048714, 0.46431), (180.0, 323.130096, 0.0), (0.352882, 0.352882, 0.352882)), + 'bl': ((0.0, -0.098169, 0.717164), (180.0, 341.565063, 0.0), (0.409048, 0.409048, 0.409048)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.373375, 0.373375, 0.373375)), + 'cl': ((0.0, -0.044478, 0.64138), (180.0, 352.405365, 0.0), (0.414064, 0.414064, 0.414064)), + 'cs': ((0.0, -0.107646, 0.391816), (180.0, 343.300751, 0.0), (0.436207, 0.436207, 0.436207)), + 'sl': ((0.0, -0.175185, 0.265483), (180.0, 330.945404, 0.0), (0.437525, 0.437525, 0.462425)), + 'ss': ((0.0, -0.138924, 0.175421), (180.0, 319.398712, 0.0), (0.42384, 0.42384, 0.42384)), + 'rl': ((0.0, -0.077274, 0.353435), (180.0, 340.346161, 0.0), (0.311073, 0.311073, 0.311073)), + 'rs': ((0.0, -0.064793, 0.312286), (180.0, 336.801422, 0.0), (0.329469, 0.329469, 0.329469)), + 'ml': ((0.0, -0.081206, 0.886824), (180.0, 350.537689, 0.0), (0.489672, 0.489672, 0.510828)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.554896, 0.483187, 0.468427)), + 'fl': ((0.0, -0.184093, 0.780337), (180.0, 341.565063, 0.0), (0.524276, 0.581964, 0.672796)), + 'fs': ((0.0, -0.159124, 0.658902), (180.0, 345.963745, 0.0), (0.550204, 0.550204, 0.550204))}, + 45: {'hl': ((0.0, -0.241128, 0.417908), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.200778, 0.303221), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.047732, 0.552948), (180.0, 348.690063, 0.0), (0.492423, 0.403813, 0.424631)), + 'ps': ((0.0, 0.056255, 0.354062), (180.0, 338.962494, 0.0), (0.403027, 0.396591, 0.375032)), + 'dl': ((0.0, -0.000591, 0.732163), (180.0, 0.0, 0.0), (0.325423, 0.325423, 0.371332)), + 'ds': ((0.0, 0.042396, 0.453346), (180.0, 323.130096, 0.0), (0.350369, 0.341622, 0.341622)), + 'bl': ((0.0, -0.035221, 0.649262), (180.0, 5.710593, 0.0), (0.34146, 0.334907, 0.350949)), + 'bs': ((0.0, 0.072326, 0.446993), (180.0, 0.0, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, 0.034271, 0.544421), (180.0, 0.0, 0.0), (0.423135, 0.416994, 0.392359)), + 'cs': ((0.0, -0.026709, 0.30591), (180.0, 345.068573, 0.0), (0.485345, 0.429348, 0.364762)), + 'sl': ((0.0, -0.020918, 0.24747), (180.0, 345.068573, 0.0), (0.39036, 0.438042, 0.404339)), + 'ss': ((0.0, -0.101919, 0.14662), (180.0, 326.309937, 0.0), (0.366286, 0.421492, 0.364771)), + 'rl': ((0.0, -0.024001, 0.253725), (180.0, 348.690063, 0.0), (0.346643, 0.33651, 0.320607)), + 'rs': ((0.0, -0.012127, 0.304147), (180.0, 355.601288, 0.0), (0.342155, 0.318324, 0.316706)), + 'ml': ((0.0, -0.05639, 0.774362), (180.0, 353.659821, 0.0), (0.58308, 0.458441, 0.599689)), + 'ms': ((0.0, -0.051283, 0.614941), (180.0, 344.0, 0.0), (0.559086, 0.44277, 0.581816)), + 'fl': ((0.0, -0.098486, 0.693572), (180.0, 3.0, 0.0), (0.547692, 0.518618, 0.604765)), + 'fs': ((0.0, -0.1132, 0.623452), (180.0, 345.963745, 0.0), (0.555113, 0.51, 0.458304))}, + 46: {'hl': ((0.0, -0.2364, 0.406916), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.183944, 0.251387), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.096859, 0.57), (180.0, 330.0, 0.0), (0.494291, 0.377233, 0.424631)), + 'ps': ((0.0, -0.010074, 0.353951), (180.0, 323.972626, 0.0), (0.411516, 0.359163, 0.359163)), + 'dl': ((0.0, -0.021335, 0.685092), (180.0, 333.434937, 0.0), (0.317983, 0.317983, 0.384017)), + 'ds': ((0.0, 0.018677, 0.46431), (180.0, 323.130096, 0.0), (0.346891, 0.346891, 0.346891)), + 'bl': ((0.0, -0.050428, 0.574968), (180.0, 341.565063, 0.0), (0.350284, 0.369367, 0.395716)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, 0.028487, 0.645978), (180.0, 352.405365, 0.0), (0.411501, 0.376726, 0.342697)), + 'cs': ((0.0, -0.058881, 0.382862), (180.0, 343.300751, 0.0), (0.421838, 0.403063, 0.307962)), + 'sl': ((0.0, -0.085229, 0.296321), (180.0, 336.801422, 0.0), (0.332158, 0.382887, 0.382887)), + 'ss': ((0.0, -0.091855, 0.126139), (180.0, 319.398712, 0.0), (0.385279, 0.413317, 0.413317)), + 'rl': ((0.0, -0.025175, 0.353435), (180.0, 340.346161, 0.0), (0.325989, 0.281306, 0.251126)), + 'rs': ((0.0, -0.035506, 0.298774), (180.0, 336.801422, 0.0), (0.389809, 0.30633, 0.269237)), + 'ml': ((0.0, -0.078541, 0.768487), (180.0, 333.434937, 0.0), (0.561767, 0.470323, 0.422517)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.510816, 0.422641, 0.422641)), + 'fl': ((0.0, -0.115715, 0.701924), (180.0, 341.565063, 0.0), (0.56429, 0.507327, 0.515048)), + 'fs': ((0.0, -0.12798, 0.623452), (180.0, 345.963745, 0.0), (0.538414, 0.51, 0.458304))}, + 47: {'hl': ((0.0, -0.32198, 0.406916), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.205193, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.424631)), + 'dl': ((0.0, -0.1, 0.507172), (180.0, 333.434937, 0.0), (0.354193, 0.354193, 0.354193)), + 'ds': ((0.0, -0.048714, 0.46431), (180.0, 323.130096, 0.0), (0.327934, 0.327934, 0.327934)), + 'bl': ((0.0, -0.098169, 0.498031), (180.0, 341.565063, 0.0), (0.334803, 0.38662, 0.408829)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.255482, 0.322577), (180.0, 352.405365, 0.0), (0.436398, 0.436398, 0.436398)), + 'cs': ((0.0, -0.14621, 0.247035), (180.0, 343.300751, 0.0), (0.406043, 0.406043, 0.406043)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.298001, 0.383177, 0.368997)), + 'ss': ((0.0, -0.138924, 0.126139), (180.0, 319.398712, 0.0), (0.357568, 0.387685, 0.34839)), + 'rl': ((0.0, -0.107251, 0.353435), (180.0, 340.346161, 0.0), (0.294208, 0.294208, 0.294208)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.356575, 0.356575, 0.356575)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.560363, 0.560363, 0.560363)), + 'ms': ((0.0, -0.088633, 0.45922), (180.0, 344.0, 0.0), (0.527299, 0.527299, 0.527299)), + 'fl': ((0.0, -0.184093, 0.503724), (180.0, 333.434937, 0.0), (0.554647, 0.554647, 0.554647)), + 'fs': ((0.0, -0.21074, 0.516329), (180.0, 345.963745, 0.0), (0.551474, 0.551474, 0.551474))}, + 48: {'hl': ((0.0, -0.379865, 0.459952), (180.0, 311.185913, 0.0), (0.220475, 0.220475, 0.220475)), + 'hs': ((0.0, -0.283891, 0.379233), (180.0, 311.633545, 0.0), (0.175105, 0.175105, 0.175105)), + 'pl': ((0.0, -0.175888, 0.607607), (180.0, 330.0, 0.0), (0.415325, 0.350414, 0.350414)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.1, 0.803362), (180.0, 333.434937, 0.0), (0.249582, 0.249582, 0.249582)), + 'ds': ((0.0, -0.134309, 0.55885), (180.0, 323.130096, 0.0), (0.25313, 0.25313, 0.25313)), + 'bl': ((0.0, -0.081709, 0.574968), (180.0, 341.565063, 0.0), (0.350462, 0.355917, 0.408829)), + 'cl': ((0.0, -0.004879, 0.645978), (180.0, 352.405365, 0.0), (0.360603, 0.379238, 0.342697)), + 'cs': ((-0.052862, -0.045363, 0.425891), (180.0, 354.289398, 10.304847), (0.350607, 0.379473, 0.307962)), + 'sl': ((0.0, -0.171653, 0.530645), (180.0, 336.801422, 0.0), (0.196465, 0.209684, 0.209684)), + 'ss': ((0.0, -0.302327, 0.311569), (180.0, 319.398712, 0.0), (0.192818, 0.192818, 0.192818)), + 'rl': ((0.0, -0.061428, 0.368627), (180.0, 340.346161, 0.0), (0.283145, 0.283145, 0.283145)), + 'rs': ((0.0, -0.064793, 0.332653), (180.0, 336.801422, 0.0), (0.369749, 0.316126, 0.27979)), + 'ml': ((0.0, -0.107093, 0.841645), (180.0, 333.434937, 0.0), (0.531162, 0.427389, 0.427389)), + 'ms': ((0.0, -0.107107, 0.703806), (180.0, 344.0, 0.0), (0.446526, 0.360857, 0.446526)), + 'fl': ((0.132628, -0.076284, 1.146246), (180.0, 11.309933, 345.963745), (0.302753, 0.302753, 0.302753)), + 'fs': ((-0.096783, -0.068098, 0.950602), (180.0, 4.398705, 7.125016), (0.265791, 0.265791, 0.265791))}, + 49: {'hl': ((0.0, -0.18204, 0.334817), (180.0, 320.194427, 0.0), (0.316307, 0.316307, 0.316307)), + 'hs': ((0.0, -0.135678, 0.221156), (180.0, 311.633545, 0.0), (0.304362, 0.304362, 0.304362)), + 'pl': ((0.0, -0.110108, 0.572772), (180.0, 330.0, 0.0), (0.444693, 0.444457, 0.416579)), + 'ps': ((0.0, 0.055268, 0.322045), (180.0, 323.972626, 0.0), (0.372883, 0.372883, 0.372883)), + 'dl': ((0.0, 0.0344, 0.66062), (180.0, 350.0, 0.0), (0.344863, 0.324908, 0.376133)), + 'ds': ((0.0, 0.058199, 0.600436), (180.0, 355.0, 0.0), (0.314241, 0.311441, 0.331872)), + 'bl': ((0.0, 0.018331, 0.426509), (180.0, 335.55603, 0.0), (0.356082, 0.38662, 0.408829)), + 'bs': ((0.0, 0.07111, 0.303576), (180.0, 338.198578, 0.0), (0.350289, 0.390083, 0.367367)), + 'cl': ((0.0, -0.009119, 0.365049), (180.0, 348.690063, 0.0), (0.435275, 0.484142, 0.426705)), + 'cs': ((0.0, -0.08866, 0.311198), (180.0, 345.963745, 0.0), (0.446451, 0.509151, 0.438783)), + 'sl': ((0.0, -0.055102, 0.25231), (180.0, 340.0, 0.0), (0.365126, 0.419666, 0.367644)), + 'ss': ((0.0, -0.093716, 0.280104), (180.0, 340.0, 0.0), (0.308486, 0.360756, 0.308486)), + 'rl': ((0.0, -0.025355, 0.279759), (180.0, 350.0, 0.0), (0.330575, 0.30742, 0.351356)), + 'rs': ((0.0, -0.005626, 0.304083), (180.0, 350.0, 0.0), (0.330629, 0.321739, 0.334487)), + 'ml': ((0.0, 0.006205, 0.730745), (180.0, 350.0, 0.0), (0.541236, 0.495014, 0.505914)), + 'ms': ((0.0, -0.041817, 0.657982), (180.0, 350.0, 0.0), (0.528558, 0.428241, 0.440781)), + 'fl': ((0.0, -0.025665, 0.472024), (180.0, 341.565063, 0.0), (0.537187, 0.568602, 0.504242)), + 'fs': ((0.0, -0.053262, 0.541627), (180.0, 350.0, 0.0), (0.536297, 0.551713, 0.536297))}, + 50: {'hl': ((0.0, -0.161351, 0.238781), (180.0, 320.194427, 0.0), (0.22609, 0.22609, 0.253839)), + 'hs': ((0.0, -0.105822, 0.167984), (180.0, 311.633545, 0.0), (0.227463, 0.227463, 0.267459)), + 'pl': ((0.0, 0.044602, 0.222782), (180.0, 330.0, 0.0), (0.355647, 0.325393, 0.387358)), + 'ps': ((0.0, 0.074645, 0.161964), (180.0, 323.972626, 0.0), (0.290684, 0.279793, 0.345262)), + 'dl': ((0.0, 0.069948, 0.264177), (180.0, 333.434937, 0.0), (0.319059, 0.312181, 0.404947)), + 'ds': ((0.0, 0.079943, 0.178171), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.395141)), + 'bl': ((0.0, 0.086611, 0.238876), (180.0, 324.462311, 0.0), (0.311766, 0.311766, 0.347182)), + 'bs': ((0.0, 0.09289, 0.177123), (180.0, 333.434937, 0.0), (0.278967, 0.278967, 0.321948)), + 'cl': ((0.0, -0.011046, 0.01), (180.0, 341.565063, 0.0), (0.400879, 0.358704, 0.482993)), + 'cs': ((0.0, -0.017134, 0.007449), (180.0, 343.300751, 0.0), (0.403063, 0.351357, 0.482298)), + 'sl': ((0.0, -0.003789, 0.041869), (180.0, 336.801422, 0.0), (0.287709, 0.305749, 0.336502)), + 'ss': ((0.0, 0.069593, -0.009474), (180.0, 319.398712, 0.0), (0.294156, 0.294156, 0.343695)), + 'rl': ((0.0, -0.005708, 0.072391), (180.0, 340.346161, 0.0), (0.292084, 0.251606, 0.305578)), + 'rs': ((0.0, -0.013988, 0.104925), (180.0, 336.801422, 0.0), (0.266919, 0.2285, 0.303516)), + 'ml': ((0.0, 0.055975, 0.422612), (180.0, 333.434937, 0.0), (0.414861, 0.349499, 0.475353)), + 'ms': ((0.0, 0.009975, 0.385303), (180.0, 344.0, 0.0), (0.383794, 0.323108, 0.434328)), + 'fl': ((0.0, -0.02587, 0.28487), (180.0, 341.565063, 0.0), (0.439388, 0.44, 0.509668)), + 'fs': ((0.0, -0.031342, 0.241033), (180.0, 345.963745, 0.0), (0.416083, 0.416083, 0.505574))}, + 51: {'hl': ((0.0, -0.336955, 0.45264), (180.0, 320.194427, 0.0), (0.146723, 0.146723, 0.146723)), + 'hs': ((0.0, -0.292919, 0.358681), (180.0, 309.805573, 0.0), (0.10362, 0.10362, 0.10362)), + 'pl': ((0.0, -0.109834, 0.526816), (180.0, 330.0, 0.0), (0.275856, 0.239965, 0.270116)), + 'ps': ((0.0, -0.128212, 0.441408), (180.0, 323.972626, 0.0), (0.19634, 0.179978, 0.19634)), + 'dl': ((0.0, -0.073249, 0.721831), (180.0, 333.434937, 0.0), (0.179183, 0.179183, 0.179183)), + 'ds': ((0.003206, -0.027011, 0.539462), (180.0, 333.434937, 0.0), (0.187017, 0.163523, 0.163523)), + 'bl': ((0.0, -0.098169, 0.613644), (180.0, 341.565063, 0.0), (0.191202, 0.191202, 0.191202)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.200878, 0.200878, 0.200878)), + 'cl': ((-0.036369, -0.011516, 0.645978), (180.0, 352.405365, 6.340192), (0.225296, 0.217393, 0.212222)), + 'cs': ((0.0, -0.062589, 0.382862), (180.0, 343.300751, 4.398705), (0.247978, 0.247978, 0.247978)), + 'sl': ((0.0, -0.103722, 0.311299), (180.0, 336.801422, 0.0), (0.189026, 0.22511, 0.230503)), + 'ss': ((0.0, -0.197966, 0.209493), (180.0, 319.398712, 0.0), (0.176416, 0.211725, 0.190265)), + 'rl': ((0.0, -0.052372, 0.353435), (180.0, 340.346161, 0.0), (0.168127, 0.160467, 0.149528)), + 'rs': ((0.0, -0.067906, 0.257018), (180.0, 336.801422, 0.0), (0.196729, 0.177654, 0.194598)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.347874, 0.286073, 0.358574)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.286768, 0.249709, 0.242081)), + 'fl': ((0.022361, -0.185967, 0.682343), (198.434952, 341.565063, 0.0), (0.335775, 0.345189, 0.345189)), + 'fs': ((0.0, -0.150963, 0.623452), (180.0, 345.963745, 0.0), (0.311173, 0.322407, 0.311763))}, + 52: {'hl': ((0.0, -0.226334, 0.320665), (180.0, 320.194427, 0.0), (0.293796, 0.307458, 0.285614)), + 'hs': ((0.0, -0.177439, 0.236684), (180.0, 311.633545, 0.0), (0.295823, 0.290815, 0.268914)), + 'pl': ((0.0, -0.083734, 0.442113), (180.0, 330.0, 0.0), (0.456098, 0.426337, 0.424631)), + 'ps': ((0.0, 0.014579, 0.248759), (180.0, 333.434937, 0.0), (0.373453, 0.418225, 0.380561)), + 'dl': ((0.0, -0.0512, 0.611287), (180.0, 333.434937, 0.0), (0.321924, 0.332988, 0.332093)), + 'ds': ((0.0, -0.009323, 0.478144), (180.0, 333.434937, 0.0), (0.335801, 0.330875, 0.347303)), + 'bl': ((0.0, -0.039067, 0.459635), (180.0, 338.962494, 0.0), (0.339525, 0.395091, 0.384313)), + 'bs': ((0.0, 0.074196, 0.222437), (180.0, 320.710602, 0.0), (0.338939, 0.367465, 0.379679)), + 'cl': ((0.0, -0.027292, 0.402756), (180.0, 344.054596, 0.0), (0.3893, 0.442017, 0.371083)), + 'cs': ((0.0, -0.090977, 0.293858), (180.0, 343.300751, 0.0), (0.390856, 0.442619, 0.360844)), + 'sl': ((0.0, -0.077828, 0.180907), (180.0, 336.801422, 0.0), (0.338979, 0.448591, 0.326381)), + 'ss': ((0.0, -0.059941, 0.09653), (180.0, 323.130096, 0.0), (0.351759, 0.42779, 0.34839)), + 'rl': ((0.0, -0.032308, 0.217786), (180.0, 340.346161, 0.0), (0.351805, 0.335044, 0.277298)), + 'rs': ((0.0, -0.038272, 0.184249), (180.0, 336.801422, 0.0), (0.347485, 0.34102, 0.352713)), + 'ml': ((0.0, -0.085907, 0.611453), (180.0, 333.434937, 0.0), (0.558198, 0.492803, 0.571304)), + 'ms': ((0.0, -0.086949, 0.539928), (180.0, 330.945404, 0.0), (0.560604, 0.459901, 0.488545)), + 'fl': ((0.0, -0.126071, 0.564014), (180.0, 345.963745, 0.0), (0.525956, 0.534213, 0.488643)), + 'fs': ((0.0, -0.106, 0.440965), (180.0, 331.389526, 0.0), (0.533015, 0.52384, 0.458304))}, + 53: {'hl': ((0.020459, -0.210665, 0.345419), (180.0, 320.194427, 350.0), (0.266338, 0.284073, 0.308024)), + 'hs': ((0.02712, -0.18269, 0.236684), (180.0, 311.633545, 350.0), (0.267142, 0.258474, 0.268914)), + 'pl': ((0.014521, -0.093809, 0.505128), (180.0, 330.0, 350.0), (0.427842, 0.363547, 0.424631)), + 'ps': ((0.019338, -0.01701, 0.293182), (180.0, 323.972626, 350.0), (0.342165, 0.342165, 0.342165)), + 'dl': ((0.008258, -0.05772, 0.610897), (180.0, 333.434937, 350.0), (0.295951, 0.289076, 0.411137)), + 'ds': ((0.022613, 0.000299, 0.408023), (180.0, 323.130096, 350.0), (0.308948, 0.308948, 0.308948)), + 'bl': ((0.0, -0.070305, 0.536696), (180.0, 341.565063, 350.0), (0.334803, 0.353877, 0.407806)), + 'bs': ((0.019763, 0.007837, 0.370366), (180.0, 338.198578, 350.0), (0.31034, 0.31034, 0.31034)), + 'cl': ((0.016021, -0.025354, 0.499371), (180.0, 352.405365, 350.0), (0.360603, 0.360596, 0.376552)), + 'cs': ((0.024724, -0.091999, 0.360134), (180.0, 343.300751, 350.0), (0.350607, 0.382517, 0.307962)), + 'sl': ((0.022434, -0.104239, 0.212358), (180.0, 336.801422, 350.0), (0.30672, 0.383177, 0.369655)), + 'ss': ((0.042464, -0.107015, 0.123057), (180.0, 319.398712, 350.0), (0.310777, 0.379575, 0.316983)), + 'rl': ((0.030567, -0.035828, 0.27221), (180.0, 341.565063, 355.236359), (0.297828, 0.277349, 0.25764)), + 'rs': ((0.017497, -0.05696, 0.278086), (180.0, 350.0, 350.537689), (0.298738, 0.275566, 0.276556)), + 'ml': ((0.012491, -0.04269, 0.599857), (180.0, 333.434937, 350.0), (0.51396, 0.45943, 0.581963)), + 'ms': ((0.041512, -0.032625, 0.548914), (180.0, 344.0, 350.0), (0.499605, 0.416345, 0.468427)), + 'fl': ((0.0, -0.10554, 0.532121), (180.0, 341.565063, 350.0), (0.507808, 0.493757, 0.637366)), + 'fs': ((0.03488, -0.125225, 0.530575), (180.0, 345.963745, 350.0), (0.483888, 0.47173, 0.469081))}, + 54: {'hl': ((0.0, -0.172063, 0.39568), (180.0, 340.0, 0.0), (0.265415, 0.265415, 0.265415)), + 'hs': ((0.0, -0.100836, 0.354621), (180.0, 340.0, 0.0), (0.260796, 0.260796, 0.260796)), + 'pl': ((0.0, -0.105848, 0.504514), (180.0, 345.0, 0.0), (0.324843, 0.324843, 0.324843)), + 'ps': ((0.0, -0.05623, 0.375395), (180.0, 345.0, 0.0), (0.297445, 0.297445, 0.297445)), + 'dl': ((0.0, 0.056451, 0.571274), (180.0, 345.0, 0.0), (0.299061, 0.299061, 0.299061)), + 'ds': ((0.0, 0.096606, 0.556323), (180.0, 345.0, 0.0), (0.281711, 0.281711, 0.281711)), + 'bl': ((0.0, 0.064179, 0.411482), (180.0, 345.0, 0.0), (0.277218, 0.277218, 0.277218)), + 'bs': ((0.0, 0.082597, 0.366588), (180.0, 345.0, 0.0), (0.287716, 0.287716, 0.287716)), + 'cl': ((0.0, -0.209901, 0.348118), (180.0, 345.0, 0.0), (0.360488, 0.360488, 0.360488)), + 'cs': ((0.0, -0.292991, 0.321374), (180.0, 345.0, 0.0), (0.321124, 0.321124, 0.321124)), + 'sl': ((0.0, -0.07251, 0.192706), (180.0, 345.0, 0.0), (0.319115, 0.319115, 0.319115)), + 'ss': ((0.0, -0.016214, 0.186471), (180.0, 345.0, 0.0), (0.311696, 0.311696, 0.311696)), + 'rl': ((0.0, -0.042589, 0.219306), (180.0, 345.0, 0.0), (0.311668, 0.311668, 0.311668)), + 'rs': ((0.0, -0.067707, 0.218238), (180.0, 345.0, 0.0), (0.292378, 0.292378, 0.292378)), + 'ml': ((0.0, -0.047411, 0.786543), (180.0, 345.0, 0.0), (0.403984, 0.403984, 0.403984)), + 'ms': ((0.0, -0.012533, 0.652898), (180.0, 345.0, 0.0), (0.376858, 0.376858, 0.376858)), + 'fl': ((0.0, -0.148821, 0.654557), (180.0, 345.0, 0.0), (0.388997, 0.388997, 0.388997)), + 'fs': ((0.0, -0.200781, 0.623452), (180.0, 345.0, 0.0), (0.383601, 0.383601, 0.383601))}, + 55: {'hl': ((0.0, -0.228169, 0.399476), (180.0, 340.0, 0.0), (0.27971, 0.27971, 0.27971)), + 'hs': ((0.0, -0.122724, 0.311463), (180.0, 340.0, 0.0), (0.255711, 0.255711, 0.255711)), + 'pl': ((0.0, -0.08111, 0.509503), (180.0, 340.0, 0.0), (0.432522, 0.380665, 0.339927)), + 'ps': ((0.0, 0.021208, 0.266881), (180.0, 340.0, 0.0), (0.356491, 0.356491, 0.356491)), + 'dl': ((0.0, -0.001257, 0.50995), (180.0, 333.434937, 0.0), (0.328001, 0.328001, 0.328001)), + 'ds': ((0.0, 0.012151, 0.464755), (180.0, 339.44397, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, -0.032385, 0.428182), (180.0, 341.565063, 0.0), (0.334803, 0.334803, 0.334803)), + 'bs': ((0.0, -0.006513, 0.273053), (180.0, 338.198578, 0.0), (0.330546, 0.330546, 0.330546)), + 'cl': ((0.0, -0.032998, 0.508111), (180.0, 0.0, 0.0), (0.377949, 0.377949, 0.377949)), + 'cs': ((0.0, -0.115053, 0.368073), (180.0, 343.300751, 0.0), (0.358967, 0.328466, 0.328466)), + 'sl': ((0.0, -0.107067, 0.211263), (180.0, 336.801422, 0.0), (0.313981, 0.400916, 0.338408)), + 'ss': ((0.0, -0.128894, 0.14304), (180.0, 330.0, 0.0), (0.295818, 0.375986, 0.341114)), + 'rl': ((0.0, -0.040936, 0.19804), (180.0, 340.346161, 0.0), (0.333866, 0.312983, 0.329459)), + 'rs': ((-0.010545, -0.024856, 0.191691), (180.0, 340.0, 0.0), (0.345062, 0.328625, 0.328617)), + 'ml': ((0.0, -0.072264, 0.704137), (180.0, 350.0, 0.0), (0.495034, 0.470078, 0.444982)), + 'ms': ((0.0, -0.039924, 0.600244), (180.0, 350.0, 0.0), (0.458971, 0.411162, 0.410363)), + 'fl': ((0.0, -0.08348, 0.451257), (180.0, 341.565063, 0.0), (0.506291, 0.514455, 0.472984)), + 'fs': ((0.0, -0.123047, 0.514919), (180.0, 345.963745, 0.0), (0.497508, 0.476158, 0.438191))}, + 56: {'hl': ((0.0, -0.302389, 0.443009), (180.0, 320.194427, 0.0), (0.293796, 0.336659, 0.285614)), + 'hs': ((0.0, -0.235974, 0.280137), (180.0, 311.633545, 0.0), (0.352422, 0.32792, 0.268914)), + 'pl': ((0.0, -0.273686, 0.752608), (180.0, 330.0, 0.0), (0.377632, 0.377632, 0.377632)), + 'ps': ((0.0, -0.071794, 0.477607), (180.0, 335.0, 0.0), (0.36897, 0.36897, 0.36897)), + 'dl': ((0.0, -0.067366, 0.685092), (180.0, 333.434937, 0.0), (0.361692, 0.356022, 0.364691)), + 'ds': ((0.0, -0.073314, 0.516935), (180.0, 323.130096, 0.0), (0.364571, 0.364571, 0.364571)), + 'bl': ((0.0, -0.028989, 0.47762), (180.0, 331.389526, 0.0), (0.463479, 0.450893, 0.431349)), + 'bs': ((0.0, -0.015279, 0.389929), (180.0, 338.198578, 0.0), (0.419855, 0.419855, 0.419855)), + 'cl': ((0.0, -0.018993, 0.754007), (180.0, 352.405365, 0.0), (0.365337, 0.365337, 0.365337)), + 'cs': ((0.0, -0.079396, 0.533163), (180.0, 347.471191, 0.0), (0.359826, 0.359826, 0.359826)), + 'sl': ((0.0, -0.137519, 0.421834), (180.0, 336.801422, 0.0), (0.298001, 0.383177, 0.368997)), + 'ss': ((0.0, -0.272625, 0.275158), (180.0, 319.398712, 0.0), (0.352415, 0.352415, 0.352415)), + 'rl': ((0.0, -0.054797, 0.414761), (180.0, 340.346161, 0.0), (0.338241, 0.312043, 0.311832)), + 'rs': ((0.0, -0.099264, 0.463569), (180.0, 336.801422, 0.0), (0.305813, 0.305813, 0.305813)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.670874, 0.61412, 0.581963)), + 'ms': ((0.0, -0.051857, 0.656112), (180.0, 344.0, 0.0), (0.613962, 0.513484, 0.468427)), + 'fl': ((0.0, -0.149469, 0.711494), (180.0, 341.565063, 0.0), (0.633023, 0.668821, 0.589631)), + 'fs': ((0.0, -0.212405, 0.686062), (180.0, 335.224854, 0.0), (0.613244, 0.565097, 0.519809))}, + 57: {'hl': ((0.0, -0.302389, 0.443009), (180.0, 320.194427, 0.0), (0.293796, 0.336659, 0.285614)), + 'hs': ((0.0, -0.235974, 0.280137), (180.0, 311.633545, 0.0), (0.352422, 0.32792, 0.268914)), + 'pl': ((0.0, -0.273686, 0.752608), (180.0, 330.0, 0.0), (0.377632, 0.377632, 0.377632)), + 'ps': ((0.0, 0.18, 0.08), (180.0, -50.0, 0.0), (0.5, 0.45, 0.5)), + 'dl': ((0.0, -0.067366, 0.685092), (180.0, 333.434937, 0.0), (0.361692, 0.356022, 0.364691)), + 'ds': ((0.0, -0.073314, 0.516935), (180.0, 323.130096, 0.0), (0.364571, 0.364571, 0.364571)), + 'bl': ((0.0, -0.028989, 0.47762), (180.0, 331.389526, 0.0), (0.463479, 0.450893, 0.431349)), + 'bs': ((0.0, -0.015279, 0.389929), (180.0, 338.198578, 0.0), (0.419855, 0.419855, 0.419855)), + 'cl': ((0.0, -0.018993, 0.754007), (180.0, 352.405365, 0.0), (0.365337, 0.365337, 0.365337)), + 'cs': ((0.0, -0.079396, 0.533163), (180.0, 347.471191, 0.0), (0.359826, 0.359826, 0.359826)), + 'sl': ((0.0, -0.137519, 0.421834), (180.0, 336.801422, 0.0), (0.298001, 0.383177, 0.368997)), + 'ss': ((0.0, -0.272625, 0.275158), (180.0, 319.398712, 0.0), (0.352415, 0.352415, 0.352415)), + 'rl': ((0.0, -0.054797, 0.414761), (180.0, 340.346161, 0.0), (0.338241, 0.312043, 0.311832)), + 'rs': ((0.0, -0.099264, 0.463569), (180.0, 336.801422, 0.0), (0.305813, 0.305813, 0.305813)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.670874, 0.61412, 0.581963)), + 'ms': ((0.0, -0.051857, 0.656112), (180.0, 344.0, 0.0), (0.613962, 0.513484, 0.468427)), + 'fl': ((0.0, -0.149469, 0.711494), (180.0, 341.565063, 0.0), (0.633023, 0.668821, 0.589631)), + 'fs': ((0.0, -0.212405, 0.686062), (180.0, 335.224854, 0.0), (0.613244, 0.565097, 0.519809))}} +ExtendedGlassesTransTable = {1: {'fl': ((0.0, 0.143374, 0.12), (180.0, 350.0, 0.0), (0.41, 0.320314, 0.41))}, + 2: {'hl': ((0.0, 0.078454, 0.218205), (180.0, 351.869904, 0.0), (0.243308, 0.19733, 0.288041)), + 'hs': ((0.0, 0.218512, 0.047869), (180.0, 320.0, 0.0), (0.238619, 0.160504, 0.245208)), + 'pl': ((0.0, 0.142359, 0.203738), (180.0, 0.0, 0.0), (0.366415, 0.306053, 0.375362)), + 'ps': ((0.0, 0.155287, 0.151189), (180.0, 0.0, 0.0), (0.288139, 0.295057, 0.28182)), + 'dl': ((0.0, 0.204627, 0.277646), (180.0, 350.0, 0.0), (0.280495, 0.197035, 0.28)), + 'ds': ((0.0, 0.16996, 0.359171), (180.0, 8.130102, 0.0), (0.274636, 0.20839, 0.262733)), + 'cl': ((0.0, 0.148353, 0.121322), (180.0, 350.0, 0.0), (0.35351, 0.293013, 0.332505)), + 'cs': ((0.0, 0.223866, 0.168877), (180.0, 4.085617, 0.0), (0.342668, 0.214791, 0.311371)), + 'sl': ((0.0, 0.335741, 0.022323), (180.0, 350.0, 0.0), (0.297347, 0.191886, 0.297347)), + 'ss': ((0.0, 0.301501, 0.003744), (180.0, 350.0, 0.0), (0.28, 0.211589, 0.28)), + 'rl': ((0.0, 0.250739, -0.037004), (180.0, 350.537689, 0.0), (0.363858, 0.181402, 0.337987)), + 'rs': ((0.0, 0.276756, -0.031632), (180.0, 354.289398, 0.0), (0.306648, 0.142252, 0.308336)), + 'ml': ((0.0, 0.090395, 0.340701), (180.0, 0.0, 0.0), (0.447977, 0.317201, 0.444383)), + 'ms': ((0.0, 0.146782, 0.328656), (180.0, 0.0, 0.0), (0.434794, 0.278491, 0.398912)), + 'fl': ((0.0, 0.085281, 0.231974), (180.0, 9.462322, 0.0), (0.414286, 0.320314, 0.41)), + 'fs': ((0.0, 0.081915, 0.195784), (180.0, 4.085617, 0.0), (0.42992, 0.319754, 0.42))}, + 3: {'pl': ((0.0, 0.100011, 0.167805), (180.0, 0.0, 0.0), (0.351783, 0.306053, 0.41622)), + 'ps': ((0.0, 0.181299, 0.089964), (180.0, 350.537689, 0.0), (0.279175, 0.295057, 0.28182)), + 'ds': ((0.0, 0.16996, 0.309818), (180.0, 3.814075, 0.0), (0.265522, 0.220048, 0.277431)), + 'ml': ((0.0, 0.054899, 0.321116), (180.0, 5.710593, 0.0), (0.417545, 0.314073, 0.492024)), + 'ms': ((0.0, 0.121952, 0.285459), (180.0, 0.0, 0.0), (0.414148, 0.278491, 0.424701)), + 'fl': ((0.0, 0.074909, 0.145675), (180.0, 3.17983, 0.0), (0.380459, 0.320314, 0.41)), + 'fs': ((0.0, 0.070644, 0.189464), (180.0, 8.365886, 0.0), (0.407842, 0.319754, 0.42))}, + 4: {'hl': ((0.0, 0.118239, 0.109231), (180.0, 338.962494, 0.0), (0.243308, 0.19733, 0.288041)), + 'hs': ((0.0, 0.140803, 0.175499), (180.0, 349.69516, 0.0), (0.238619, 0.160504, 0.245208)), + 'pl': ((0.0, 0.131786, 0.154064), (180.0, 350.537689, 0.0), (0.357565, 0.306053, 0.375362)), + 'ds': ((0.0, 0.16996, 0.281233), (180.0, 350.0, 0.0), (0.281221, 0.220048, 0.277431)), + 'ml': ((0.0, 0.14204, 0.298711), (180.0, 350.0, 0.0), (0.44, 0.314073, 0.44)), + 'ms': ((0.0, 0.170296, 0.25674), (180.0, 350.0, 0.0), (0.437745, 0.278491, 0.398912)), + 'fl': ((0.0, 0.084205, 0.234174), (180.0, 4.085617, 0.0), (0.413278, 0.320314, 0.41)), + 'fs': ((0.0, 0.104292, 0.181239), (180.0, 0.0, 0.0), (0.423149, 0.319754, 0.42))}, + 5: {'hs': ((0.0, 0.172497, 0.160052), (180.0, 351.869904, 0.0), (0.244122, 0.164205, 0.250863)), + 'pl': ((0.0, 0.11696, 0.201066), (180.0, 0.0, 0.0), (0.357565, 0.306053, 0.375362)), + 'dl': ((0.0, 0.204627, 0.281054), (180.0, 350.0, 0.0), (0.265293, 0.186357, 0.264825)), + 'ds': ((0.0, 0.16996, 0.279106), (180.0, 350.0, 0.0), (0.29, 0.220048, 0.277431)), + 'bs': ((0.0, 0.200803, 0.097391), (180.0, 350.0, 0.0), (0.274179, 0.274179, 0.274179)), + 'cl': ((0.0, 0.154052, 0.01827), (180.0, 350.0, 0.0), (0.35351, 0.293013, 0.332505)), + 'sl': ((0.0, 0.335741, -0.018402), (180.0, 350.0, 0.0), (0.32, 0.206504, 0.32)), + 'ss': ((0.0, 0.308654, -0.030958), (180.0, 350.0, 0.0), (0.28, 0.211589, 0.28)), + 'rl': ((0.0, 0.250739, -0.037004), (180.0, 350.537689, 0.0), (0.363858, 0.181402, 0.337987)), + 'rs': ((0.0, 0.276756, -0.031632), (180.0, 354.289398, 0.0), (0.306648, 0.142252, 0.308336)), + 'ml': ((0.0, 0.14204, 0.298711), (180.0, 350.0, 0.0), (0.44, 0.314073, 0.44)), + 'ms': ((0.0, 0.170296, 0.25674), (180.0, 350.0, 0.0), (0.431919, 0.272512, 0.390348)), + 'fl': ((0.0, 0.098967, 0.216033), (180.0, 9.462322, 0.0), (0.400429, 0.320314, 0.41)), + 'fs': ((0.0, 0.087841, 0.182049), (180.0, 7.125016, 0.0), (0.415009, 0.319754, 0.42))}, + 6: {'hl': ((0.0, 0.118239, 0.073909), (180.0, 338.962494, 0.0), (0.243308, 0.19733, 0.288041)), + 'pl': ((0.0, 0.141548, 0.136942), (180.0, 351.869904, 0.0), (0.357565, 0.306053, 0.375362)), + 'ps': ((0.0, 0.141343, 0.182113), (180.0, 0.0, 0.0), (0.282347, 0.295057, 0.28182)), + 'dl': ((0.0, 0.204627, 0.257552), (180.0, 350.0, 0.0), (0.280495, 0.197035, 0.28)), + 'ds': ((0.0, 0.16996, 0.243795), (180.0, 350.0, 0.0), (0.29, 0.220048, 0.277431)), + 'cl': ((0.0, 0.148353, 0.043679), (180.0, 350.0, 0.0), (0.35351, 0.293013, 0.332505)), + 'cs': ((0.0, 0.259957, 0.067284), (180.0, 350.0, 0.0), (0.342668, 0.214791, 0.311371)), + 'sl': ((0.0, 0.335741, -0.018402), (180.0, 350.0, 0.0), (0.32, 0.206504, 0.32)), + 'ss': ((0.0, 0.301501, -0.030958), (180.0, 350.0, 0.0), (0.28, 0.211589, 0.28)), + 'fl': ((0.0, 0.136288, 0.109261), (180.0, 0.0, 0.0), (0.41, 0.320314, 0.41)), + 'fs': ((0.0, 0.08556, 0.133995), (180.0, 4.763642, 0.0), (0.42, 0.319754, 0.42))}, + 7: {'fs': ((0.0, 0.129622, 0.13), (180.0, 350.0, 0.0), (0.42, 0.319754, 0.42))}, + 8: {'hl': ((0.0, 0.118239, 0.102791), (180.0, 338.962494, 0.0), (0.243308, 0.19733, 0.288041)), + 'pl': ((0.0, 0.120805, 0.185922), (180.0, 0.0, 0.0), (0.357565, 0.306053, 0.375362)), + 'ps': ((0.0, 0.151001, 0.176525), (180.0, 8.130102, 0.0), (0.278085, 0.290603, 0.277566)), + 'ds': ((0.0, 0.16996, 0.266553), (180.0, 350.0, 0.0), (0.29, 0.220048, 0.277431)), + 'cl': ((0.0, 0.148353, 0.06), (180.0, 350.0, 0.0), (0.35351, 0.293013, 0.332505)), + 'cs': ((0.0, 0.259957, 0.084672), (180.0, 350.0, 0.0), (0.342668, 0.214791, 0.311371)), + 'ss': ((0.0, 0.301501, 0.00469), (180.0, 350.0, 0.0), (0.28, 0.211589, 0.28)), + 'fl': ((0.0, 0.126437, 0.137321), (180.0, 3.012788, 0.0), (0.41, 0.320314, 0.41)), + 'fs': ((0.0, 0.129622, 0.160967), (180.0, 5.710593, 0.0), (0.42, 0.319754, 0.42))}, + 9: {'hl': ((0.0, 0.108462, 0.17683), (180.0, 338.962494, 0.0), (0.223613, 0.181357, 0.264725)), + 'hs': ((0.0, 0.145966, 0.115009), (180.0, 326.309937, 0.0), (0.238619, 0.160504, 0.245208)), + 'pl': ((0.0, 0.177217, 0.190527), (180.0, 352.874969, 0.0), (0.340186, 0.282429, 0.346387)), + 'ps': ((0.0, 0.181299, 0.089964), (180.0, 350.537689, 0.0), (0.268044, 0.28011, 0.267543)), + 'dl': ((0.0, 0.204627, 0.277646), (180.0, 350.0, 0.0), (0.261641, 0.183791, 0.261179)), + 'ds': ((0.0, 0.16996, 0.31), (180.0, 350.0, 0.0), (0.268071, 0.220048, 0.277431)), + 'cl': ((0.0, 0.129969, 0.109178), (180.0, 356.423676, 0.0), (0.35351, 0.293013, 0.332505)), + 'cs': ((0.0, 0.259957, 0.0994), (180.0, 350.0, 0.0), (0.312134, 0.195652, 0.283626)), + 'sl': ((0.0, 0.365983, 0.06233), (180.0, 350.0, 0.0), (0.266053, 0.171691, 0.266053)), + 'ss': ((0.0, 0.35414, 0.076935), (180.0, 350.0, 0.0), (0.270549, 0.204447, 0.270549)), + 'ml': ((0.0, 0.14204, 0.343069), (180.0, 350.0, 0.0), (0.406747, 0.314073, 0.44)), + 'ms': ((0.0, 0.204449, 0.338906), (180.0, 350.0, 0.0), (0.398254, 0.251272, 0.359924))}, + 10: {'pl': ((0.0, 0.101137, 0.110166), (180.0, 354.289398, 0.0), (0.357565, 0.306053, 0.375362)), + 'ps': ((0.0, 0.0829, 0.089964), (180.0, 0.0, 0.0), (0.282347, 0.295057, 0.28182)), + 'dl': ((0.0, 0.204627, 0.228147), (180.0, 350.0, 0.0), (0.280495, 0.197035, 0.28)), + 'ds': ((0.0, 0.16996, 0.204106), (180.0, 350.0, 0.0), (0.29, 0.220048, 0.277431)), + 'sl': ((0.0, 0.335741, -0.036645), (180.0, 350.0, 0.0), (0.247571, 0.206504, 0.32)), + 'ss': ((0.0, 0.301501, -0.030958), (180.0, 350.0, 0.0), (0.250248, 0.211589, 0.28)), + 'rl': ((0.0, 0.250739, -0.091041), (180.0, 338.962494, 0.0), (0.266926, 0.180318, 0.337987)), + 'rs': ((0.0, 0.27828, -0.035432), (180.0, 341.565063, 0.0), (0.260345, 0.148854, 0.322646)), + 'ml': ((0.0, 0.14204, 0.298711), (180.0, 350.0, 0.0), (0.390165, 0.314073, 0.44)), + 'ms': ((0.0, 0.170296, 0.25674), (180.0, 350.0, 0.0), (0.403087, 0.278491, 0.398912)), + 'fl': ((0.0, 0.223794, 0.067759), (180.0, 0.0, 0.0), (0.380542, 0.2973, 0.380542)), + 'fs': ((0.0, 0.129622, 0.081605), (180.0, 0.0, 0.0), (0.385318, 0.29335, 0.385318))}, + 11: {'hl': ((0.0, 0.124293, 0.064234), (180.0, 333.434937, 0.0), (0.231442, 0.207132, 0.302348)), + 'hs': ((0.0, 0.170523, 0.084807), (180.0, 333.434937, 0.0), (0.227436, 0.168825, 0.25792)), + 'pl': ((0.0, 0.10421, 0.131327), (180.0, 0.0, 0.0), (0.374201, 0.334149, 0.409821)), + 'ps': ((0.0, 0.184491, 0.179369), (180.0, 12.528808, 0.0), (0.288791, 0.295057, 0.28182)), + 'dl': ((0.0, 0.146495, 0.313994), (180.0, 4.398705, 0.0), (0.272351, 0.219304, 0.311646)), + 'ds': ((0.0, 0.160652, 0.265002), (180.0, 4.969741, 0.0), (0.275094, 0.232508, 0.32776)), + 'bl': ((0.0, 0.093978, 0.152735), (180.0, 3.17983, 0.0), (0.287872, 0.343174, 0.343174)), + 'bs': ((0.0, 0.179059, 0.134669), (180.0, 6.340192, 0.0), (0.27849, 0.284113, 0.284113)), + 'cl': ((0.0, 0.143426, 0.094572), (180.0, 8.746162, 0.0), (0.325081, 0.293013, 0.332505)), + 'cs': ((0.0, 0.263087, 0.056374), (180.0, 3.814075, 0.0), (0.290627, 0.214791, 0.311371)), + 'sl': ((0.0, 0.335741, -0.06116), (180.0, 350.0, 0.0), (0.32, 0.206504, 0.32)), + 'ss': ((0.0, 0.292303, -0.018359), (180.0, 350.0, 0.0), (0.275273, 0.233029, 0.308371)), + 'rl': ((0.0, 0.238463, -0.060425), (180.0, 345.963745, 0.0), (0.363858, 0.181402, 0.337987)), + 'rs': ((0.0, 0.052822, -0.049536), (180.0, 354.289398, 0.0), (0.306648, 0.306648, 0.306648)), + 'ml': ((0.0, 0.097596, 0.338379), (180.0, 8.746162, 0.0), (0.436884, 0.314073, 0.44)), + 'ms': ((0.0, 0.18893, 0.244251), (180.0, 0.0, 0.0), (0.429678, 0.278491, 0.398912)), + 'fl': ((0.0, 0.092028, 0.236901), (180.0, 19.440035, 0.0), (0.394799, 0.320314, 0.41)), + 'fs': ((0.0, 0.080417, 0.206739), (180.0, 15.945395, 0.0), (0.408524, 0.319754, 0.42))}, + 12: {'hl': ((0.0, 0.19161, 0.045311), (180.0, 338.962494, 0.0), (0.224522, 0.182094, 0.265801)), + 'hs': ((0.0, 0.218512, 0.062592), (180.0, 341.565063, 0.0), (0.219717, 0.14779, 0.225784)), + 'pl': ((0.0, 0.188844, 0.073246), (180.0, 2.770216, 0.0), (0.341317, 0.292146, 0.358305)), + 'ps': ((0.0, 0.232416, 0.089964), (180.0, 12.528808, 0.0), (0.25825, 0.269876, 0.257768)), + 'dl': ((0.0, 0.276064, 0.186943), (180.0, 350.0, 0.0), (0.252447, 0.181582, 0.25804)), + 'ds': ((0.0, 0.245301, 0.173859), (180.0, 350.0, 0.0), (0.273943, 0.207864, 0.26207)), + 'bl': ((0.0, 0.245554, 0.075448), (180.0, 0.0, 0.0), (0.247436, 0.252864, 0.27713)), + 'bs': ((0.0, 0.284205, 0.064097), (180.0, 0.0, 0.0), (0.249024, 0.235859, 0.235859)), + 'cl': ((0.0, 0.189496, 0.017453), (180.0, 3.012788, 0.0), (0.344809, 0.293013, 0.332505)), + 'cs': ((0.0, 0.328401, -0.031304), (180.0, 350.0, 0.0), (0.316518, 0.214791, 0.311371)), + 'sl': ((0.0, 0.378279, -0.117174), (180.0, 350.0, 0.0), (0.283015, 0.198119, 0.307007)), + 'ss': ((0.0, 0.377926, -0.099811), (180.0, 344.744873, 0.0), (0.269911, 0.211589, 0.28)), + 'rl': ((0.0, 0.300494, -0.145078), (180.0, 348.690063, 0.0), (0.329358, 0.164568, 0.306623)), + 'rs': ((0.0, 0.328352, -0.10298), (180.0, 354.289398, 0.0), (0.268046, 0.124345, 0.269521)), + 'ml': ((0.0, 0.14204, 0.171892), (180.0, 7.125016, 0.0), (0.420106, 0.314073, 0.44)), + 'ms': ((0.0, 0.196812, 0.106915), (180.0, 0.0, 0.0), (0.409559, 0.278491, 0.398912)), + 'fl': ((0.0, 0.189265, 0.033205), (180.0, 6.340192, 0.0), (0.379707, 0.296647, 0.379707)), + 'fs': ((0.0, 0.122984, 0.070585), (180.0, 12.528808, 0.0), (0.40212, 0.307063, 0.40333))}, + 13: {'hl': ((0.0, 0.044781, 0.053253), (180.0, 354.289398, 0.0), (0.243308, 0.19733, 0.288041)), + 'hs': ((0.0, 0.165599, 0.06227), (180.0, 349.380341, 0.0), (0.231915, 0.155994, 0.238318)), + 'pl': ((0.0, 0.022, 0.105292), (180.0, 8.130102, 0.0), (0.335671, 0.323128, 0.396304)), + 'ps': ((0.0, 0.189743, 0.077616), (180.0, 13.24052, 0.0), (0.256183, 0.244263, 0.311845)), + 'dl': ((0.0, 0.141211, 0.26153), (180.0, 9.462322, 0.0), (0.255671, 0.197035, 0.28)), + 'ds': ((0.0, 0.128204, 0.215993), (180.0, 4.763642, 0.0), (0.255741, 0.220048, 0.277431)), + 'bl': ((0.0, 0.175024, 0.118577), (180.0, 3.366461, 0.0), (0.249215, 0.262137, 0.287293)), + 'bs': ((0.0, 0.161287, 0.059038), (180.0, 4.085617, 0.0), (0.274179, 0.274179, 0.274179)), + 'cl': ((0.0, 0.129041, -0.024628), (180.0, 0.0, 0.0), (0.341982, 0.293013, 0.332505)), + 'cs': ((0.0, 0.224526, -0.008677), (180.0, 3.012788, 0.0), (0.303967, 0.214791, 0.311371)), + 'sl': ((0.0, 0.330448, -0.150585), (180.0, 350.0, 0.0), (0.317897, 0.206504, 0.32)), + 'ss': ((0.0, 0.318292, -0.136152), (180.0, 350.0, 0.0), (0.282145, 0.211589, 0.310307)), + 'rl': ((0.0, 0.225347, -0.127599), (180.0, 0.0, 0.0), (0.322214, 0.181402, 0.337987)), + 'rs': ((0.0, 0.168132, -0.175565), (180.0, 345.068573, 0.0), (0.299433, 0.243571, 0.308336)), + 'ml': ((0.0, 0.058056, 0.183242), (180.0, 5.194429, 0.0), (0.42027, 0.314073, 0.44)), + 'ms': ((0.0, 0.133166, 0.153596), (180.0, 0.0, 0.0), (0.393897, 0.278491, 0.398912)), + 'fl': ((0.0, 0.066667, -0.02432), (180.0, 5.710593, 0.0), (0.41, 0.320314, 0.41)), + 'fs': ((0.0, 0.06194, 0.016133), (180.0, 8.746162, 0.0), (0.392879, 0.319754, 0.42))}, + 14: {'hl': ((0.0, 0.159438, 0.022837), (180.0, 338.962494, 0.0), (0.208469, 0.19733, 0.288041)), + 'hs': ((0.0, 0.205684, 0.053855), (180.0, 337.380127, 0.0), (0.204941, 0.160504, 0.245208)), + 'pl': ((0.0, 0.131875, 0.09965), (180.0, 11.309933, 0.0), (0.3288, 0.306053, 0.427301)), + 'ps': ((0.0, 0.183146, 0.12941), (180.0, 18.434948, 0.0), (0.246167, 0.295057, 0.28182)), + 'dl': ((0.0, 0.199277, 0.214196), (180.0, 0.0, 0.0), (0.246498, 0.195386, 0.301291)), + 'ds': ((0.0, 0.187358, 0.193808), (180.0, 0.0, 0.0), (0.233216, 0.231488, 0.31248)), + 'bl': ((0.0, 0.242963, 0.039121), (180.0, 351.869904, 0.0), (0.237675, 0.286305, 0.316986)), + 'bs': ((0.0, 0.261231, 0.079908), (180.0, 0.0, 0.0), (0.242969, 0.251667, 0.251667)), + 'cl': ((0.0, 0.177716, -0.027931), (180.0, 0.0, 0.0), (0.301445, 0.301725, 0.342391)), + 'cs': ((0.0, 0.300251, 0.008259), (180.0, 0.0, 0.0), (0.264824, 0.198711, 0.288061)), + 'sl': ((0.0, 0.386537, -0.106746), (180.0, 350.0, 0.0), (0.261013, 0.206504, 0.32)), + 'ss': ((0.0, 0.35882, -0.078951), (180.0, 350.0, 0.0), (0.231939, 0.22731, 0.316365)), + 'rl': ((0.0, 0.275572, -0.141484), (180.0, 355.236359, 0.0), (0.290351, 0.178071, 0.337987)), + 'rs': ((0.0, 0.224285, -0.129626), (180.0, 354.289398, 0.0), (0.24115, 0.218006, 0.28326)), + 'ml': ((0.0, 0.163282, 0.185676), (180.0, 0.0, 0.0), (0.408571, 0.314073, 0.44)), + 'ms': ((0.0, 0.191787, 0.171119), (180.0, 0.0, 0.0), (0.383077, 0.278491, 0.398912)), + 'fl': ((0.0, 0.105486, 0.070603), (180.0, 12.994617, 0.0), (0.346458, 0.320314, 0.421144)), + 'fs': ((0.0, 0.128433, -0.001473), (180.0, 5.194429, 0.0), (0.360925, 0.319754, 0.428685))}, + 15: {'hl': ((0.0, 0.301712, 0.016432), (180.0, 326.309937, 0.0), (0.173932, 0.388926, 0.210167)), + 'hs': ((0.0, 0.344876, 0.012905), (180.0, 329.036255, 0.0), (0.169557, 0.306392, 0.210683)), + 'pl': ((0.0, 0.484294, -0.125862), (180.0, 347.471191, 0.0), (0.272837, 0.367441, 0.375362)), + 'ps': ((0.0, 0.471856, -0.033389), (180.0, 350.537689, 0.0), (0.206302, 0.328445, 0.24029)), + 'dl': ((0.0, 0.3818, 0.027969), (180.0, 350.0, 0.0), (0.210956, 0.331618, 0.354747)), + 'ds': ((0.0, 0.411029, 0.020927), (180.0, 350.0, 0.0), (0.175509, 0.304495, 0.305118)), + 'bl': ((0.0, 0.473563, -0.105577), (180.0, 348.690063, 0.0), (0.202038, 0.390395, 0.324432)), + 'bs': ((0.0, 0.577318, -0.020901), (180.0, 343.300751, 0.0), (0.163677, 0.207711, 0.202636)), + 'cl': ((0.0, 0.391839, -0.113814), (180.0, 350.0, 0.0), (0.243017, 0.520533, 0.265248)), + 'cs': ((0.0, 0.440759, -0.093247), (180.0, 349.69516, 0.0), (0.222206, 0.3746, 0.241183)), + 'sl': ((0.0, 0.59427, -0.255686), (180.0, 335.55603, 0.0), (0.199497, 0.407114, 0.32)), + 'ss': ((0.0, 0.604184, -0.237158), (180.0, 335.559998, 0.0), (0.201407, 0.399207, 0.319597)), + 'rl': ((0.0, 0.420781, -0.212944), (180.0, 337.380127, 0.0), (0.219401, 0.366796, 0.244425)), + 'rs': ((0.0, 0.358988, -0.175504), (180.0, 345.963745, 0.0), (0.20978, 0.385531, 0.197511)), + 'ml': ((0.0, 0.455571, 0.108166), (180.0, 350.0, 0.0), (0.290113, 0.415556, 0.327887)), + 'ms': ((0.0, 0.409596, 0.131089), (180.0, 350.0, 0.0), (0.278673, 0.482069, 0.305658)), + 'fl': ((0.0, 0.32819, -0.099642), (180.0, 5.0, 0.0), (0.305757, 0.555172, 0.359937)), + 'fs': ((0.0, 0.315781, -0.091302), (180.0, 0.0, 0.0), (0.304348, 0.64574, 0.323712))}, + 16: {'hl': ((0.0, 0.182116, -0.026324), (180.0, 329.036255, 0.0), (0.183549, 0.30147, 0.271493)), + 'hs': ((0.0, 0.162093, -0.030989), (180.0, 324.462311, 0.0), (0.178275, 0.320864, 0.245208)), + 'pl': ((0.0, 0.246463, 0.012467), (180.0, 0.0, 0.0), (0.272063, 0.319204, 0.406015)), + 'ps': ((0.0, 0.224921, -0.049279), (180.0, 8.130102, 0.0), (0.230198, 0.332714, 0.415864)), + 'dl': ((0.0, 0.112679, 0.214552), (180.0, 5.0, 0.0), (0.219236, 0.343335, 0.303383)), + 'ds': ((0.0, 0.15343, 0.184704), (180.0, 3.576334, 0.0), (0.213594, 0.29264, 0.277431)), + 'bl': ((0.0, 0.227667, 0.028155), (180.0, 356.308624, 0.0), (0.216215, 0.365241, 0.287293)), + 'bs': ((0.0, 0.271482, 0.081997), (180.0, 6.340192, 0.0), (0.204564, 0.290903, 0.20646)), + 'cl': ((0.0, 0.108276, -0.046338), (180.0, 0.0, 0.0), (0.259248, 0.454764, 0.347817)), + 'cs': ((0.0, 0.135967, -0.067787), (180.0, 352.874969, 0.0), (0.231762, 0.453885, 0.311371)), + 'sl': ((0.0, 0.355049, -0.190943), (180.0, 345.963745, 0.0), (0.215919, 0.317866, 0.32)), + 'ss': ((0.0, 0.440798, -0.15735), (180.0, 345.068573, 0.0), (0.182208, 0.230814, 0.28)), + 'rl': ((0.0, 0.220375, -0.226519), (180.0, 344.054596, 0.0), (0.247484, 0.323244, 0.302106)), + 'rs': ((0.0, 0.201528, -0.241307), (180.0, 343.300751, 0.0), (0.211784, 0.344268, 0.308336)), + 'ml': ((0.0, 0.042295, 0.149711), (180.0, 5.0, 0.0), (0.355454, 0.48344, 0.44)), + 'ms': ((0.0, 0.09302, 0.152271), (180.0, 5.0, 0.0), (0.328114, 0.419293, 0.398912)), + 'fl': ((0.0, 0.086183, 0.092251), (180.0, 15.255118, 0.0), (0.303423, 0.450213, 0.41)), + 'fs': ((0.0, 0.075361, 0.026021), (180.0, 9.462322, 0.0), (0.302482, 0.476086, 0.42))}, + 17: {'hl': ((0.200149, 0.456515, -0.044682), (146.309937, 331.389526, 351.869904), (0.286858, 0.228466, 0.30339)), + 'hs': ((0.263342, 0.498851, 0.009944), (160.346176, 320.0, 0.0), (0.270183, 0.181735, 0.277643)), + 'pl': ((0.296252, 0.558951, -0.20513), (153.434952, 351.869904, 0.0), (0.423468, 0.362461, 0.435161)), + 'ps': ((0.222674, 0.669512, -0.157144), (172.874985, 347.005371, 0.0), (0.337275, 0.352457, 0.336645)), + 'dl': ((0.231222, 0.491262, 0.038021), (161.565048, 352.874969, 0.0), (0.322775, 0.226735, 0.322205)), + 'ds': ((0.195962, 0.478829, -0.019759), (164.744888, 355.601288, 0.0), (0.31404, 0.269666, 0.339989)), + 'bl': ((0.219856, 0.587778, -0.014678), (149.740005, 350.0, 0.0), (0.27447, 0.262137, 0.287293)), + 'bs': ((0.209768, 0.607517, -0.092937), (175.914383, 0.0, 0.0), (0.274179, 0.274179, 0.274179)), + 'cl': ((0.25794, 0.652492, -0.167668), (180.0, 350.0, 0.0), (0.323214, 0.298842, 0.324137)), + 'cs': ((0.203543, 0.599911, -0.166034), (163.610458, 350.0, 0.0), (0.32206, 0.32206, 0.32206)), + 'sl': ((0.197982, 0.725899, -0.237164), (164.054611, 339.44397, 0.0), (0.32, 0.206504, 0.310708)), + 'ss': ((0.228123, 0.721239, -0.246728), (153.434952, 343.300751, 0.0), (0.333197, 0.251789, 0.333197)), + 'rl': ((0.327262, 0.488187, -0.308543), (149.03624, 343.300751, 9.462322), (0.349929, 0.181402, 0.337987)), + 'rs': ((0.309609, 0.508521, -0.283489), (146.309937, 344.054596, 0.0), (0.317594, 0.14733, 0.308496)), + 'ml': ((0.334214, 0.53388, -0.105688), (165.96376, 0.0, 0.0), (0.507756, 0.362438, 0.507756)), + 'ms': ((0.281916, 0.526409, -0.050112), (164.054611, 0.0, 0.0), (0.434891, 0.300639, 0.430637)), + 'fl': ((0.26164, 0.494054, -0.239046), (159.443954, 7.125016, 0.0), (0.435889, 0.34054, 0.435889)), + 'fs': ((0.233777, 0.45318, -0.218664), (160.016891, 11.309933, 0.0), (0.442738, 0.324942, 0.426815))}, + 18: {'hl': ((0.0, 0.267218, 0.01861), (180.0, 321.340179, 0.0), (0.212691, 0.172499, 0.251795)), + 'hs': ((0.0, 0.323439, 0.027186), (180.0, 320.0, 0.0), (0.221186, 0.135888, 0.248775)), + 'pl': ((0.0, 0.223562, 0.028227), (180.0, 0.0, 0.0), (0.318784, 0.272859, 0.366049)), + 'ps': ((0.0, 0.247291, 0.005782), (180.0, 0.0, 0.0), (0.264524, 0.276432, 0.26403)), + 'dl': ((0.0, 0.230289, 0.199214), (180.0, 0.0, 0.0), (0.244199, 0.182002, 0.302211)), + 'ds': ((0.0, 0.284963, 0.218269), (180.0, 0.0, 0.0), (0.240139, 0.172094, 0.26219)), + 'bl': ((0.0, 0.361287, 0.04086), (180.0, 350.0, 0.0), (0.247754, 0.204835, 0.298714)), + 'bs': ((0.0, 0.338075, 0.03841), (180.0, 350.0, 0.0), (0.232495, 0.233087, 0.233087)), + 'cl': ((0.0, 0.263456, -0.006592), (180.0, 350.0, 0.0), (0.300945, 0.266277, 0.285502)), + 'cs': ((0.0, 0.326731, 0.001341), (180.0, 350.0, 0.0), (0.261293, 0.214791, 0.264974)), + 'sl': ((0.0, 0.42476, -0.087801), (180.0, 350.0, 0.0), (0.260745, 0.168266, 0.284507)), + 'ss': ((0.0, 0.518544, -0.030958), (180.0, 350.0, 0.0), (0.236973, 0.190286, 0.25181)), + 'rl': ((0.0, 0.3468, -0.124107), (180.0, 350.537689, 0.0), (0.300296, 0.149713, 0.278944)), + 'rs': ((0.0, 0.369128, -0.121969), (180.0, 347.471191, 0.0), (0.246406, 0.12082, 0.284085)), + 'ml': ((0.0, 0.204131, 0.151426), (180.0, 0.0, 0.0), (0.396343, 0.282911, 0.396343)), + 'ms': ((0.0, 0.222319, 0.183222), (180.0, 6.340192, 0.0), (0.379141, 0.239213, 0.378554)), + 'fl': ((0.0, 0.228013, 0.068334), (180.0, 3.814075, 0.0), (0.355548, 0.277773, 0.373469)), + 'fs': ((0.0, 0.243492, 0.010407), (180.0, 0.0, 0.0), (0.351609, 0.273235, 0.381459))}, + 19: {'ss': ((0.0, 0.334479, -0.006105), (180.0, 350.0, 0.0), (0.304684, 0.191148, 0.297905)), + 'rs': ((0.0, 0.251783, -0.031632), (180.0, 342.645966, 0.0), (0.306648, 0.164736, 0.308336)), + 'fl': ((0.0, 0.141625, 0.12), (180.0, 350.0, 0.0), (0.41, 0.320314, 0.41)), + 'fs': ((0.0, 0.176809, 0.13), (180.0, 350.0, 0.0), (0.42, 0.319754, 0.42))}, + 20: {'hl': ((0.034523, 0.096095, 0.087941), (180.0, 333.434937, 0.0), (0.243308, 0.19733, 0.333919)), + 'hs': ((0.041357, 0.042114, 0.009901), (180.0, 326.309937, 0.0), (0.270682, 0.270682, 0.270682)), + 'pl': ((0.048776, 0.198799, 0.08818), (179.0, 345.0, 0.0), (0.410697, 0.292758, 0.4919)), + 'ps': ((0.0, 0.250319, 0.114899), (180.0, 0.0, 0.0), (0.311948, 0.215569, 0.347937)), + 'dl': ((0.020275, 0.149782, 0.307257), (180.0, 0.0, 0.0), (0.285743, 0.197035, 0.353524)), + 'ds': ((0.0, 0.051992, 0.321583), (180.0, 0.0, 0.0), (0.273281, 0.281429, 0.300349)), + 'bl': ((0.030846, 0.301048, 0.142036), (180.0, 347.471191, 0.0), (0.285646, 0.198551, 0.365914)), + 'bs': ((0.002871, 0.237243, 0.162152), (180.0, 0.0, 0.0), (0.281518, 0.222397, 0.291614)), + 'cl': ((0.047769, 0.148353, 0.092036), (180.0, 0.0, 0.0), (0.35351, 0.293013, 0.332505)), + 'cs': ((0.030424, 0.248212, 0.056374), (180.0, 350.0, 0.0), (0.342668, 0.214791, 0.311371)), + 'sl': ((0.0, 0.109274, -0.061695), (180.0, 350.0, 0.0), (0.328929, 0.332035, 0.33273)), + 'ss': ((0.007205, 0.175002, -0.011751), (180.0, 350.0, 0.0), (0.287876, 0.283501, 0.357428)), + 'rl': ((0.0, 0.06814, -0.094117), (180.0, 343.300751, 0.0), (0.363858, 0.285186, 0.337987)), + 'rs': ((0.0, 0.099706, -0.070101), (180.0, 345.0, 0.0), (0.316131, 0.26144, 0.308336)), + 'ml': ((0.04816, 0.097082, 0.349655), (180.0, 3.0, 0.0), (0.456455, 0.297276, 0.569451)), + 'ms': ((0.034563, 0.17555, 0.251134), (180.0, 355.601288, 0.0), (0.443263, 0.258657, 0.472933)), + 'fl': ((0.050144, 0.001648, 0.245803), (180.0, 17.0, 0.0), (0.467929, 0.382283, 0.479589)), + 'fs': ((0.047045, 0.089182, 0.226824), (180.0, 12.0, 0.0), (0.433673, 0.319754, 0.42))}, + 21: {'fl': ((0.0, 0.126437, 0.063992), (180.0, 350.0, 0.0), (0.41, 0.320314, 0.41)), + 'fs': ((0.0, 0.129622, 0.056358), (180.0, 350.0, 0.0), (0.42, 0.319754, 0.42))}} +ExtendedBackpackTransTable = {1: {'m': ((0.227545, -1.05, 0.053624), (180.0, 6.0, 0.0), (0.319532, 0.319532, 0.319532)), + 'l': ((0.26576, -1.28023, 0.0), (180.0, 0.0, 0.0), (0.32, 0.32, 0.32))}, + 2: {'m': ((0.265322, -1.05, 0.101718), (180.0, 6.009006, 0.0), (0.246001, 0.319532, 0.319532)), + 'l': ((0.21263, -1.28023, 0.27888), (180.0, 0.0, 0.0), (0.209654, 0.32, 0.32))}, + 4: {'m': ((0.262368, -1.05, 0.195095), (180.0, 20.0, 0.0), (0.147268, 0.319532, 0.319532)), + 'l': ((0.23955, -1.241722, 0.347059), (180.0, 20.0, 0.0), (0.168072, 0.319779, 0.319779))}, + 7: {'s': ((0.290997, -1.625686, 0.134797), (180.0, 6.340192, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.273327, -1.170334, 0.137522), (180.0, 14.036243, 0.0), (0.14773, 0.290714, 0.297074)), + 'l': ((0.217695, -1.286976, 0.455049), (180.0, 0.0, 0.0), (0.176572, 0.32, 0.32))}, + 8: {'s': ((0.41387, -1.464616, 0.394859), (180.0, 16.38954, 0.0), (0.253971, 0.253971, 0.253971)), + 'm': ((0.290309, -0.969267, 0.434042), (180.0, 6.0, 0.0), (0.148556, 0.184951, 0.184951)), + 'l': ((0.251222, -1.10169, 0.597807), (180.0, 0.0, 0.0), (0.127983, 0.178798, 0.193945))}, + 9: {'s': ((0.303128, -1.50918, 0.401274), (180.0, 6.340192, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.295221, -1.172374, 0.307521), (180.0, 6.0, 0.0), (0.220651, 0.319532, 0.319532)), + 'l': ((0.21774, -1.183531, 0.606077), (180.0, 0.0, 0.0), (0.181037, 0.32, 0.32))}, + 10: {'s': ((0.31255, -1.437493, 0.578569), (180.0, 6.340192, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.26387, -1.099816, 0.584935), (180.0, 6.0, 0.0), (0.248266, 0.319532, 0.319532)), + 'l': ((0.223449, -1.200059, 0.775343), (180.0, 0.0, 0.0), (0.261044, 0.32, 0.32))}, + 11: {'s': ((0.28701, -1.299067, -0.136342), (180.0, 12.994617, 0.0), (0.389964, 0.353497, 0.409548)), + 'm': ((0.264689, -0.995974, -0.067336), (180.0, 12.994617, 358.0), (0.299865, 0.256064, 0.416953)), + 'l': ((0.232795, -1.043043, 0.025233), (180.0, 7.594644, 0.0), (0.363232, 0.439772, 0.4632))}, + 12: {'s': ((0.29374, -1.585814, 0.243433), (180.0, 6.340192, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.280119, -1.05, 0.236418), (180.0, 6.0, 0.0), (0.170751, 0.319532, 0.319532)), + 'l': ((0.210635, -1.253645, 0.469426), (180.0, 11.309933, 0.0), (0.187917, 0.32, 0.32))}, + 13: {'s': ((0.317368, -1.892817, -0.509906), (180.0, 10.304847, 0.0), (0.403833, 0.305326, 0.307871)), + 'm': ((0.289202, -1.371564, -0.423113), (180.0, 6.0, 0.0), (0.323075, 0.269037, 0.273195)), + 'l': ((0.242066, -1.380625, -0.422389), (180.0, 0.0, 0.0), (0.2944, 0.278142, 0.278142))}, + 14: {'m': ((0.274095, -1.05, 0.19327), (180.0, 6.0, 0.0), (0.177726, 0.32, 0.32)), + 'l': ((0.2291, -1.202605, 0.333037), (180.0, 0.0, 0.0), (0.20932, 0.32, 0.32))}, + 15: {'s': ((0.3496, -1.566231, 0.297443), (180.0, 11.309933, 0.0), (0.434117, 0.305907, 0.323214)), + 'm': ((0.281317, -1.154318, 0.273442), (180.0, 6.0, 0.0), (0.361197, 0.279386, 0.279386)), + 'l': ((0.216334, -1.245587, 0.280549), (180.0, 0.0, 0.0), (0.326107, 0.297644, 0.297644))}, + 16: {'s': ((0.291561, -1.583429, 0.015556), (180.0, 8.746162, 0.0), (0.447062, 0.32, 0.32)), + 'm': ((0.287122, -1.199939, 0.158015), (180.0, 6.0, 0.0), (0.357934, 0.286914, 0.303731)), + 'l': ((0.216544, -1.200739, 0.361558), (180.0, 0.0, 0.0), (0.353442, 0.271948, 0.31356))}, + 17: {'s': ((0.30377, -1.180818, 0.019505), (180.0, 0.0, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.241677, -0.768537, 0.123365), (180.0, 0.0, 0.0), (0.304765, 0.304765, 0.304765)), + 'l': ((0.224225, -0.769975, 0.238668), (180.0, 0.0, 0.0), (0.299818, 0.299818, 0.299818))}, + 18: {'s': ((0.000383, -1.563254, -0.217828), (180.0, 17.0, 325.0), (0.289457, 0.289457, 0.289457)), + 'm': ((0.01529, -1.153655, -0.253422), (180.0, 12.09, 337.0), (0.28419, 0.28419, 0.28419)), + 'l': ((0.128042, -1.227634, -0.296953), (180.0, 8.0, 350.0), (0.245614, 0.32, 0.305041))}, + 19: {'s': ((0.299164, -1.040617, 0.541799), (180.0, 0.0, 0.0), (0.33993, 0.367266, 0.321871)), + 'm': ((0.235848, -0.671119, 0.582503), (180.0, 0.0, 0.0), (0.285994, 0.305137, 0.322617)), + 'l': ((0.215214, -0.788198, 0.798792), (180.0, 0.0, 0.0), (0.284314, 0.27715, 0.438783))}, + 20: {'s': ((0.3, -1.04, 0.54), (180.0, 0.0, 0.0), (0.34, 0.32, 0.32)), + 'm': ((0.227545, -0.67, 0.58), (180.0, 0.0, 0.0), (0.32, 0.32, 0.32)), + 'l': ((0.22, -0.79, 0.8), (180.0, 0.0, 0.0), (0.28, 0.28, 0.44))}, + 21: {'s': ((0.314386, -1.166389, -0.053999), (180.0, 5.194429, 0.0), (0.274937, 0.274937, 0.274937)), + 'm': ((0.253176, -0.731249, 0.026498), (180.0, 5.0, 0.0), (0.274722, 0.274722, 0.274722)), + 'l': ((0.213058, -0.781995, 0.095885), (180.0, 3.0, 0.0), (0.246885, 0.298552, 0.363845))}, + 22: {'s': ((0.272049, -1.545308, -0.077449), (180.0, 0.0, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.227545, -1.186519, -0.22177), (180.0, 0.0, 0.0), (0.32, 0.319532, 0.319532)), + 'l': ((0.239467, -1.234463, 0.033669), (180.0, 0.0, 0.0), (0.32, 0.32, 0.32))}, + 24: {'s': ((0.294021, -1.620192, -0.216908), (180.0, 6.340192, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.227545, -1.164832, -0.106984), (180.0, 6.0, 0.0), (0.290735, 0.290735, 0.290735)), + 'l': ((0.208737, -1.215698, 0.012413), (180.0, 0.0, 0.0), (0.295111, 0.295111, 0.295111))}} diff --git a/toontown/toon/AvatarPanelBase.py b/toontown/toon/AvatarPanelBase.py new file mode 100755 index 00000000..712246bc --- /dev/null +++ b/toontown/toon/AvatarPanelBase.py @@ -0,0 +1,223 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from direct.showbase import DirectObject +from otp.avatar import AvatarPanel +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.ai import ReportGlobals +IGNORE_SCALE = 0.06 +STOP_IGNORE_SCALE = 0.04 + +class AvatarPanelBase(AvatarPanel.AvatarPanel): + + def __init__(self, avatar, FriendsListPanel = None): + self.dialog = None + self.category = None + AvatarPanel.AvatarPanel.__init__(self, avatar, FriendsListPanel) + return + + def getIgnoreButtonInfo(self): + if base.localAvatar.isIgnored(self.avId): + return (TTLocalizer.AvatarPanelStopIgnoring, self.handleStopIgnoring, STOP_IGNORE_SCALE) + else: + return (TTLocalizer.AvatarPanelIgnore, self.handleIgnore, IGNORE_SCALE) + + def handleIgnore(self): + if base.cr.isFriend(self.avatar.doId): + self.dialog = TTDialog.TTGlobalDialog( + style=TTDialog.CancelOnly, + text=TTLocalizer.IgnorePanelAddFriendAvatar % self.avName, + text_wordwrap=18.5, + text_scale=0.06, + cancelButtonText=TTLocalizer.lCancel, + doneEvent='IgnoreBlocked', + command=self.freeLocalAvatar) + else: + self.dialog = TTDialog.TTGlobalDialog( + style=TTDialog.TwoChoice, + text=TTLocalizer.IgnorePanelAddIgnore % self.avName, + text_wordwrap=18.5, + text_scale=TTLocalizer.APBdialog, + okButtonText=TTLocalizer.AvatarPanelIgnore, + cancelButtonText=TTLocalizer.lCancel, + doneEvent='IgnoreConfirm', + command=self.handleIgnoreConfirm) + DirectLabel( + parent=self.dialog, + relief=None, + pos=(0, TTLocalizer.APBdirectLabelPosY, 0.125), + text=TTLocalizer.IgnorePanelTitle, + textMayChange=0, + text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleStopIgnoring(self): + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.TwoChoice, text=TTLocalizer.IgnorePanelRemoveIgnore % self.avName, text_wordwrap=18.5, text_scale=0.06, okButtonText=TTLocalizer.AvatarPanelStopIgnoring, cancelButtonText=TTLocalizer.lCancel, buttonPadSF=4.0, doneEvent='StopIgnoringConfirm', command=self.handleStopIgnoringConfirm) + DirectLabel(parent=self.dialog, relief=None, pos=(0, TTLocalizer.APBdirectLabelPosY, 0.15), text=TTLocalizer.IgnorePanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleIgnoreConfirm(self, value): + if value == -1: + self.freeLocalAvatar() + return + base.localAvatar.addIgnore(self.avId) + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.Acknowledge, text=TTLocalizer.IgnorePanelIgnore % self.avName, text_wordwrap=18.5, text_scale=0.06, topPad=0.1, doneEvent='IgnoreComplete', command=self.handleDoneIgnoring) + DirectLabel(parent=self.dialog, relief=None, pos=(0, TTLocalizer.APBdirectLabelPosY, 0.15), text=TTLocalizer.IgnorePanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleStopIgnoringConfirm(self, value): + if value == -1: + self.freeLocalAvatar() + return + base.localAvatar.removeIgnore(self.avId) + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.Acknowledge, text=TTLocalizer.IgnorePanelEndIgnore % self.avName, text_wordwrap=18.5, text_scale=0.06, topPad=0.1, doneEvent='StopIgnoringComplete', command=self.handleDoneIgnoring) + DirectLabel(parent=self.dialog, relief=None, pos=(0, TTLocalizer.APBdirectLabelPosY, 0.15), text=TTLocalizer.IgnorePanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleDoneIgnoring(self, value): + self.freeLocalAvatar() + + def handleReport(self): + if base.localAvatar.isReported(self.avId): + self.alreadyReported() + else: + self.confirmReport() + + def confirmReport(self): + if base.cr.isFriend(self.avId): + string = TTLocalizer.ReportPanelBodyFriends + titlePos = 0.41 + else: + string = TTLocalizer.ReportPanelBody + titlePos = 0.35 + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.TwoChoice, text=string % self.avName, text_wordwrap=18.5, text_scale=0.06, okButtonText=TTLocalizer.AvatarPanelReport, cancelButtonText=TTLocalizer.lCancel, doneEvent='ReportConfirm', command=self.handleReportConfirm) + DirectLabel(parent=self.dialog, relief=None, pos=(0, 0, titlePos), text=TTLocalizer.ReportPanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleReportConfirm(self, value): + self.cleanupDialog() + if value == 1: + self.chooseReportCategory() + else: + self.requestWalk() + + def alreadyReported(self): + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.Acknowledge, text=TTLocalizer.ReportPanelAlreadyReported % self.avName, text_wordwrap=18.5, text_scale=0.06, topPad=0.1, doneEvent='AlreadyReported', command=self.handleAlreadyReported) + DirectLabel(parent=self.dialog, relief=None, pos=(0, 0, 0.2), text=TTLocalizer.ReportPanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleAlreadyReported(self, value): + self.freeLocalAvatar() + + def chooseReportCategory(self): + self.dialog = TTDialog.TTGlobalDialog(pos=(0, 0, 0.4), style=TTDialog.CancelOnly, text=TTLocalizer.ReportPanelCategoryBody % (self.avName, self.avName), text_wordwrap=18.5, text_scale=0.06, topPad=0.05, midPad=0.75, cancelButtonText=TTLocalizer.lCancel, doneEvent='ReportCategory', command=self.handleReportCategory) + DirectLabel(parent=self.dialog, relief=None, pos=(0, 0, 0.225), text=TTLocalizer.ReportPanelTitle, textMayChange=0, text_scale=0.08) + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + DirectButton(parent=self.dialog, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(2.125, 1.0, 1.0), text=TTLocalizer.ReportPanelCategoryLanguage, text_scale=0.06, text_pos=(0, -0.0124), pos=(0, 0, -0.3), command=self.handleReportCategory, extraArgs=[0]) + DirectButton(parent=self.dialog, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(2.15, 1.0, 1.0), text=TTLocalizer.ReportPanelCategoryGreening, text_scale=0.06, text_pos=(0, -0.0125), pos=(0, 0, -0.425), command=self.handleReportCategory, extraArgs=[1]) + DirectButton(parent=self.dialog, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(2.125, 1.0, 1.0), text=TTLocalizer.ReportPanelCategoryRude, text_scale=0.06, text_pos=(0, -0.0125), pos=(0, 0, -0.55), command=self.handleReportCategory, extraArgs=[2]) + DirectButton(parent=self.dialog, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(2.125, 1.0, 1.0), text=TTLocalizer.ReportPanelCategoryName, text_scale=0.06, text_pos=(0, -0.0125), pos=(0, 0, -0.675), command=self.handleReportCategory, extraArgs=[3]) + DirectButton(parent=self.dialog, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(2.125, 1.0, 1.0), text=TTLocalizer.ReportPanelCategoryHacking, text_scale=0.06, text_pos=(0, -0.0125), pos=(0, 0, -0.8), command=self.handleReportCategory, extraArgs=[4]) + guiButton.removeNode() + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleReportCategory(self, value): + self.cleanupDialog() + if value >= 0 and ReportGlobals.isValidCategory(value): + self.category = ReportGlobals.getCategory(value) + self.confirmReportCategory(value) + else: + self.requestWalk() + + def confirmReportCategory(self, category): + string = TTLocalizer.ReportPanelConfirmations[category] + string += '\n\n' + TTLocalizer.ReportPanelWarning + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.TwoChoice, text=string % self.avName, text_wordwrap=18.5, text_scale=0.06, topPad=0.1, okButtonText=TTLocalizer.AvatarPanelReport, cancelButtonText=TTLocalizer.lCancel, doneEvent='ReportConfirmCategory', command=self.handleReportCategoryConfirm) + DirectLabel(parent=self.dialog, relief=None, pos=(0, 0, 0.5), text=TTLocalizer.ReportPanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + return + + def handleReportCategoryConfirm(self, value): + self.cleanupDialog() + removed = 0 + + if value > 0: + if base.cr.isFriend(self.avId): + base.cr.removeFriend(self.avId) + removed = 1 + + base.cr.reportMgr.sendReport(self.avId, self.category) + self.reportComplete(removed) + else: + self.requestWalk() + + def reportComplete(self, removed): + string = TTLocalizer.ReportPanelThanks + titlePos = 0.25 + if removed: + string += ' ' + TTLocalizer.ReportPanelRemovedFriend % self.avName + titlePos = 0.3 + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.Acknowledge, text=string, text_wordwrap=18.5, text_scale=0.06, topPad=0.1, doneEvent='ReportComplete', command=self.handleReportComplete) + DirectLabel(parent=self.dialog, relief=None, pos=(0, 0, titlePos), text=TTLocalizer.ReportPanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + return + + def handleReportComplete(self, value): + self.freeLocalAvatar() + + def freeLocalAvatar(self, value = None): + self.cleanupDialog() + self.requestWalk() + + def cleanupDialog(self, state=None): + if self.dialog: + base.cr.openAvatarPanels.discard(self) + self.dialog.ignore('exitingStoppedState') + self.dialog.cleanup() + self.dialog = None + + def requestStopped(self): + base.cr.openAvatarPanels.add(self) + if not base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'stickerBook': + if base.cr.playGame.getPlace().fsm.hasStateNamed('stopped'): + base.cr.playGame.getPlace().fsm.request('stopped') + else: + self.notify.warning('skipping request to stopped in %s' % base.cr.playGame.getPlace()) + else: + self.cleanup() + + def requestWalk(self): + if base.cr.playGame.getPlace().fsm.hasStateNamed('finalBattle'): + base.cr.playGame.getPlace().fsm.request('finalBattle') + elif base.cr.playGame.getPlace().fsm.hasStateNamed('walk'): + if base.cr.playGame.getPlace().getState() == 'stopped': + base.cr.playGame.getPlace().fsm.request('walk') + else: + self.notify.warning('skipping request to walk in %s' % base.cr.playGame.getPlace()) + + def __acceptStoppedStateMsg(self): + self.dialog.ignore('exitingStoppedState') + self.dialog.accept('exitingStoppedState', self.cleanupDialog) diff --git a/toontown/toon/BoardingGroupInviterPanels.py b/toontown/toon/BoardingGroupInviterPanels.py new file mode 100755 index 00000000..23472275 --- /dev/null +++ b/toontown/toon/BoardingGroupInviterPanels.py @@ -0,0 +1,166 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toontowngui import TTDialog +from otp.otpbase import OTPLocalizer +from toontown.toontowngui import ToonHeadDialog +from direct.gui.DirectGui import DGG +from otp.otpbase import OTPGlobals +from toontown.toonbase import TTLocalizer + +class BoardingGroupInviterPanels: + notify = DirectNotifyGlobal.directNotify.newCategory('BoardingGroupInviterPanels') + + def __init__(self): + self.__invitingPanel = None + self.__invitationRejectedPanel = None + return + + def cleanup(self): + self.destroyInvitingPanel() + self.destroyInvitationRejectedPanel() + + def createInvitingPanel(self, boardingParty, inviteeId, **kw): + self.destroyInvitingPanel() + self.destroyInvitationRejectedPanel() + self.notify.debug('Creating Inviting Panel.') + self.__invitingPanel = BoardingGroupInvitingPanel(boardingParty, inviteeId, **kw) + + def createInvitationRejectedPanel(self, boardingParty, inviteeId, **kw): + self.destroyInvitingPanel() + self.destroyInvitationRejectedPanel() + self.notify.debug('Creating Invititation Rejected Panel.') + self.__invitationRejectedPanel = BoardingGroupInvitationRejectedPanel(boardingParty, inviteeId, **kw) + + def destroyInvitingPanel(self): + if self.isInvitingPanelUp(): + self.__invitingPanel.cleanup() + self.__invitingPanel = None + return + + def destroyInvitationRejectedPanel(self): + if self.isInvitationRejectedPanelUp(): + self.__invitationRejectedPanel.cleanup() + self.__invitationRejectedPanel = None + return + + def isInvitingPanelIdCorrect(self, inviteeId): + if self.isInvitingPanelUp(): + if inviteeId == self.__invitingPanel.avId: + return True + else: + self.notify.warning('Got a response back from an invitee, but a different invitee panel was open. Maybe lag?') + return False + + def isInvitingPanelUp(self): + if self.__invitingPanel: + if not self.__invitingPanel.isEmpty(): + return True + self.__invitingPanel = None + return False + + def isInvitationRejectedPanelUp(self): + if self.__invitationRejectedPanel: + if not self.__invitationRejectedPanel.isEmpty(): + return True + self.__invitationRejectedPanel = None + return False + + def forceCleanup(self): + if self.isInvitingPanelUp(): + self.__invitingPanel.forceCleanup() + self.__invitingPanel = None + if self.isInvitationRejectedPanelUp(): + self.__invitationRejectedPanel.forceCleanup() + self.__invitationRejectedPanel = None + return + + +class BoardingGroupInviterPanelBase(ToonHeadDialog.ToonHeadDialog): + notify = DirectNotifyGlobal.directNotify.newCategory('BoardingGroupInviterPanelBase') + + def __init__(self, boardingParty, inviteeId, **kw): + self.boardingParty = boardingParty + self.avId = inviteeId + avatar = base.cr.doId2do.get(self.avId) + self.avatarName = '' + if avatar: + self.avatar = avatar + self.avatarName = avatar.getName() + avatarDNA = avatar.getStyle() + self.defineParams() + command = self.handleButton + optiondefs = (('dialogName', self.dialogName, None), + ('text', self.inviterText, None), + ('style', self.panelStyle, None), + ('buttonTextList', self.buttonTextList, None), + ('command', command, None), + ('image_color', (1.0, 0.89, 0.77, 1.0), None), + ('geom_scale', 0.2, None), + ('geom_pos', (-0.1, 0, -0.025), None), + ('pad', (0.075, 0.075), None), + ('topPad', 0, None), + ('midPad', 0, None), + ('pos', (0.45, 0, 0.75), None), + ('scale', 0.75, None)) + self.defineoptions(kw, optiondefs) + ToonHeadDialog.ToonHeadDialog.__init__(self, avatarDNA) + self.show() + return + + def defineParams(self): + self.notify.error('setupParams: This method should not be called from the base class. Derived class should override this method') + + def cleanup(self): + self.notify.debug('Destroying Panel.') + ToonHeadDialog.ToonHeadDialog.cleanup(self) + + def forceCleanup(self): + self.handleButton(0) + + def handleButton(self, value): + self.cleanup() + + +class BoardingGroupInvitingPanel(BoardingGroupInviterPanelBase): + notify = DirectNotifyGlobal.directNotify.newCategory('BoardingGroupInvitingPanel') + + def __init__(self, boardingParty, inviteeId, **kw): + BoardingGroupInviterPanelBase.__init__(self, boardingParty, inviteeId, **kw) + self.initialiseoptions(BoardingGroupInvitingPanel) + self.setupUnexpectedExitHooks() + + def defineParams(self): + self.dialogName = 'BoardingGroupInvitingPanel' + self.inviterText = TTLocalizer.BoardingInvitingMessage % self.avatarName + self.panelStyle = TTDialog.CancelOnly + self.buttonTextList = [OTPLocalizer.lCancel] + + def handleButton(self, value): + self.boardingParty.requestCancelInvite(self.avId) + BoardingGroupInviterPanelBase.cleanup(self) + + def setupUnexpectedExitHooks(self): + if self.avId in base.cr.doId2do: + toon = base.cr.doId2do[self.avId] + self.unexpectedExitEventName = toon.uniqueName('disable') + self.accept(self.unexpectedExitEventName, self.forceCleanup) + + def forceCleanup(self): + self.ignore(self.unexpectedExitEventName) + BoardingGroupInviterPanelBase.forceCleanup(self) + + +class BoardingGroupInvitationRejectedPanel(BoardingGroupInviterPanelBase): + notify = DirectNotifyGlobal.directNotify.newCategory('BoardingGroupInvitationRejectedPanel') + + def __init__(self, boardingParty, inviteeId, **kw): + BoardingGroupInviterPanelBase.__init__(self, boardingParty, inviteeId, **kw) + self.initialiseoptions(BoardingGroupInvitationRejectedPanel) + + def defineParams(self): + self.dialogName = 'BoardingGroupInvitationRejectedPanel' + self.inviterText = TTLocalizer.BoardingInvitationRejected % self.avatarName + self.panelStyle = TTDialog.Acknowledge + self.buttonTextList = [OTPLocalizer.lOK] diff --git a/toontown/toon/DeathForceAcknowledge.py b/toontown/toon/DeathForceAcknowledge.py new file mode 100755 index 00000000..acda97f1 --- /dev/null +++ b/toontown/toon/DeathForceAcknowledge.py @@ -0,0 +1,56 @@ +from panda3d.core import * +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from direct.showbase import Transitions +from direct.gui.DirectGui import * +import LaffMeter + +class DeathForceAcknowledge: + + def __init__(self, doneEvent): + fadeModel = loader.loadModel('phase_3/models/misc/fade') + if fadeModel: + self.fade = DirectFrame( + parent=aspect2dp, + relief=None, + image=fadeModel, + image_color=(0, 0, 0, 0.4), + image_scale=4.0, + state=DGG.NORMAL) + self.fade.reparentTo(aspect2d, FADE_SORT_INDEX) + fadeModel.removeNode() + else: + print 'Problem loading fadeModel.' + self.fade = None + self.dialog = TTDialog.TTGlobalDialog( + message=TTLocalizer.PlaygroundDeathAckMessage, + doneEvent=doneEvent, + style=TTDialog.Acknowledge, + suppressKeys=True) + self.dialog['text_pos'] = (-.26, 0.1) + scale = self.dialog.component('image0').getScale() + scale.setX(scale[0] * 1.3) + self.dialog.component('image0').setScale(scale) + av = base.localAvatar + self.laffMeter = LaffMeter.LaffMeter(av.style, av.hp, av.maxHp) + self.laffMeter.reparentTo(self.dialog) + if av.style.getAnimal() == 'monkey': + self.laffMeter.setPos(-0.46, 0, -0.035) + self.laffMeter.setScale(0.085) + else: + self.laffMeter.setPos(-0.48, 0, -0.035) + self.laffMeter.setScale(0.1) + self.laffMeter.start() + self.dialog.show() + return + + def cleanup(self): + if self.fade: + self.fade.destroy() + if self.laffMeter: + self.laffMeter.destroy() + del self.laffMeter + if self.dialog: + self.dialog.cleanup() + self.dialog = None + return diff --git a/toontown/toon/DistributedNPCBlocker.py b/toontown/toon/DistributedNPCBlocker.py new file mode 100755 index 00000000..3b024a96 --- /dev/null +++ b/toontown/toon/DistributedNPCBlocker.py @@ -0,0 +1,68 @@ +from panda3d.core import * +from DistributedNPCToonBase import * +from direct.gui.DirectGui import * +import NPCToons +from toontown.toonbase import TTLocalizer +from direct.distributed import DistributedObject +from toontown.quest import QuestParser + + +class DistributedNPCBlocker(DistributedNPCToonBase): + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + + self.cSphereNodePath.setScale(4.5, 1.0, 6.0) + self.isLocalToon = False + self.movie = None + + def initToonState(self): + self.setAnimState('neutral', 0.9, None, None) + posh = NPCToons.BlockerPositions[self.name] + self.setPos(posh[0]) + self.setH(posh[1]) + + def disable(self): + if hasattr(self, 'movie') and self.movie: + self.movie.cleanup() + del self.movie + if self.isLocalToon: + base.localAvatar.posCamera(0, 0) + + DistributedNPCToonBase.disable(self) + + def handleCollisionSphereEnter(self, collEntry): + base.cr.playGame.getPlace().fsm.request('quest', [self]) + self.sendUpdate('avatarEnter', []) + + def __handleUnexpectedExit(self): + self.notify.warning('unexpected exit') + + def resetBlocker(self): + self.cSphereNode.setCollideMask(BitMask32()) + if hasattr(self, 'movie') and self.movie: + self.movie.cleanup() + self.movie = None + self.startLookAround() + self.clearMat() + if self.isLocalToon: + base.localAvatar.posCamera(0, 0) + self.freeAvatar() + self.isLocalToon = False + + def setMovie(self, mode, npcId, avId, timestamp): + self.npcId = npcId + self.isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.BLOCKER_MOVIE_CLEAR: + return + elif mode == NPCToons.BLOCKER_MOVIE_START: + if self.isLocalToon: + self.hideNametag2d() + self.movie = QuestParser.NPCMoviePlayer('tutorial_blocker', base.localAvatar, self) + self.movie.play() + elif mode == NPCToons.BLOCKER_MOVIE_TIMEOUT: + return + + def finishMovie(self, av, isLocalToon, elapsedTime): + self.resetBlocker() + if self.isLocalToon: + self.showNametag2d() diff --git a/toontown/toon/DistributedNPCBlockerAI.py b/toontown/toon/DistributedNPCBlockerAI.py new file mode 100755 index 00000000..256dea5d --- /dev/null +++ b/toontown/toon/DistributedNPCBlockerAI.py @@ -0,0 +1,66 @@ +from otp.ai.AIBaseGlobal import * +from panda3d.core import * +from DistributedNPCToonBaseAI import * +import NPCToons +from direct.task.Task import Task + +class DistributedNPCBlockerAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId): + DistributedNPCToonBaseAI.__init__(self, air, npcId) + self.tutorial = 0 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + self.ignoreAll() + DistributedNPCToonBaseAI.delete(self) + + def setTutorial(self, val): + self.tutorial = val + + def getTutorial(self): + return self.tutorial + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + DistributedNPCToonBaseAI.avatarEnter(self) + av = self.air.doId2do.get(avId) + if av is None: + self.notify.warning('toon isnt there! toon: %s' % avId) + return + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + self.sendStartMovie(avId) + return + + def sendStartMovie(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.BLOCKER_MOVIE_START, + self.npcId, + avId, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(NPCToons.CLERK_COUNTDOWN_TIME, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def sendTimeoutMovie(self, task): + self.timedOut = 1 + self.sendUpdate('setMovie', [NPCToons.BLOCKER_MOVIE_TIMEOUT, + self.npcId, + self.busy, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendClearMovie(None) + return Task.done + + def sendClearMovie(self, task): + self.busy = 0 + self.timedOut = 0 + self.sendUpdate('setMovie', [NPCToons.BLOCKER_MOVIE_CLEAR, + self.npcId, + 0, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + return Task.done + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + if not self.tutorial: + self.sendTimeoutMovie(None) + return diff --git a/toontown/toon/DistributedNPCClerk.py b/toontown/toon/DistributedNPCClerk.py new file mode 100755 index 00000000..74cc15f0 --- /dev/null +++ b/toontown/toon/DistributedNPCClerk.py @@ -0,0 +1,85 @@ +from otp.nametag.NametagConstants import CFSpeech, CFTimeout +from toontown.minigame import ClerkPurchase +from toontown.toonbase import TTLocalizer, ToontownGlobals +from toontown.toon import NPCToons +from DistributedNPCToonBase import DistributedNPCToonBase +import time + +class DistributedNPCClerk(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.lastCollision = 0 + self.purchaseGui = None + + def disable(self): + self.destroyDialog() + DistributedNPCToonBase.disable(self) + + def destroyDialog(self): + self.ignoreAll() + self.clearChat() + taskMgr.remove(self.uniqueName('popupPurchaseGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + + if self.purchaseGui: + self.purchaseGui.exit() + self.purchaseGui.unload() + self.purchaseGui = None + + def freeAvatar(self): + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().fsm.request('walk') + + def handleCollisionSphereEnter(self, collEntry): + if self.lastCollision > time.time(): + return + + self.lastCollision = time.time() + ToontownGlobals.NPCCollisionDelay + + if not base.localAvatar.getMoney(): + self.setChatAbsolute(TTLocalizer.STOREOWNER_NEEDJELLYBEANS, CFSpeech | CFTimeout) + return + + self.d_setState(ToontownGlobals.CLERK_GREETING) + base.cr.playGame.getPlace().fsm.request('purchase') + camera.wrtReparentTo(render) + camera.posQuatInterval(1, Vec3(-5, 9, self.getHeight() - 0.5), Vec3(-150, -2, 0), other=self, blendType='easeOut', name=self.uniqueName('lerpCamera')).start() + taskMgr.doMethodLater(1.0, self.popupPurchaseGUI, self.uniqueName('popupPurchaseGUI')) + + def d_setInventory(self, inventory, money): + self.sendUpdate('setInventory', [inventory, money]) + + def d_setState(self, state): + self.sendUpdate('setState', [0, state]) + + def setState(self, avId, state): + av = base.cr.doId2do.get(avId) + + if not av: + return + + if state == ToontownGlobals.CLERK_GOODBYE: + self.setChatAbsolute(TTLocalizer.STOREOWNER_GOODBYE, CFSpeech | CFTimeout) + elif state == ToontownGlobals.CLERK_GREETING: + self.lookAtAvatar(av) + self.setChatAbsolute(TTLocalizer.STOREOWNER_GREETING, CFSpeech | CFTimeout) + return + elif state == ToontownGlobals.CLERK_TOOKTOOLONG: + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech | CFTimeout) + + self.initToonState() + + def popupPurchaseGUI(self, task): + self.clearChat() + self.acceptOnce('purchaseClerkDone', self.__handlePurchaseDone) + self.purchaseGui = ClerkPurchase.ClerkPurchase(base.localAvatar, NPCToons.CLERK_COUNTDOWN_TIME, 'purchaseClerkDone') + self.purchaseGui.load() + self.purchaseGui.enter() + + def __handlePurchaseDone(self, state): + self.d_setInventory(base.localAvatar.inventory.makeNetString(), base.localAvatar.getMoney()) + self.destroyDialog() + self.freeAvatar() + self.detectAvatars() + self.d_setState(state) \ No newline at end of file diff --git a/toontown/toon/DistributedNPCClerkAI.py b/toontown/toon/DistributedNPCClerkAI.py new file mode 100755 index 00000000..6e24e6bc --- /dev/null +++ b/toontown/toon/DistributedNPCClerkAI.py @@ -0,0 +1,15 @@ +from DistributedNPCToonBaseAI import DistributedNPCToonBaseAI + +class DistributedNPCClerkAI(DistributedNPCToonBaseAI): + + def setInventory(self, inventory, money): + av = self.air.doId2do.get(self.air.getAvatarIdFromSender()) + + if not av: + return + + av.b_setMoney(money if av.inventory.validatePurchase(av.inventory.makeFromNetString(inventory), av.getMoney(), money) else av.getMoney()) + av.d_setInventory(av.inventory.makeNetString()) + + def setState(self, avId, state): + self.sendUpdate('setState', [self.air.getAvatarIdFromSender(), state]) \ No newline at end of file diff --git a/toontown/toon/DistributedNPCFisherman.py b/toontown/toon/DistributedNPCFisherman.py new file mode 100755 index 00000000..150726fc --- /dev/null +++ b/toontown/toon/DistributedNPCFisherman.py @@ -0,0 +1,163 @@ +from direct.gui.DirectGui import * +from direct.interval.LerpInterval import LerpPosHprInterval +from direct.task.Task import Task +from panda3d.core import * +import time + +from DistributedNPCToonBase import * +import NPCToons +from toontown.fishing import FishSellGUI +from toontown.toonbase import TTLocalizer +from otp.nametag.NametagConstants import * + + +class DistributedNPCFisherman(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.isLocalToon = 0 + self.av = None + self.button = None + self.popupInfo = None + self.fishGui = None + self.nextCollision = 0 + return + + def disable(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupFishGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + if self.fishGui: + self.fishGui.destroy() + self.fishGui = None + self.av = None + if self.isLocalToon: + base.localAvatar.posCamera(0, 0) + DistributedNPCToonBase.disable(self) + return + + def generate(self): + DistributedNPCToonBase.generate(self) + self.fishGuiDoneEvent = 'fishGuiDone' + + def announceGenerate(self): + DistributedNPCToonBase.announceGenerate(self) + + def initToonState(self): + self.setAnimState('neutral', 1.05, None, None) + npcOrigin = self.cr.playGame.hood.loader.geom.find('**/npc_fisherman_origin_%s;+s' % self.posIndex) + if not npcOrigin.isEmpty(): + self.reparentTo(npcOrigin) + self.clearMat() + else: + self.notify.warning('announceGenerate: Could not find npc_fisherman_origin_' + str(self.posIndex)) + return + + def getCollSphereRadius(self): + return 1.0 + + def handleCollisionSphereEnter(self, collEntry): + self.currentTime = time.time() + if self.nextCollision > self.currentTime: + self.nextCollision = self.currentTime + 2 + else: + base.cr.playGame.getPlace().fsm.request('purchase') + self.sendUpdate('avatarEnter', []) + self.nextCollision = self.currentTime + 2 + + def __handleUnexpectedExit(self): + self.notify.warning('unexpected exit') + self.av = None + return + + def setupAvatars(self, av): + self.ignoreAvatars() + av.stopLookAround() + av.lerpLookAt(Point3(-0.5, 4, 0), time=0.5) + self.stopLookAround() + self.lerpLookAt(Point3(av.getPos(self)), time=0.5) + + def resetFisherman(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupFishGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.fishGui: + self.fishGui.destroy() + self.fishGui = None + self.show() + self.startLookAround() + self.detectAvatars() + self.clearMat() + if self.isLocalToon: + self.freeAvatar() + return Task.done + + def setMovie(self, mode, npcId, avId, extraArgs, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.remain = NPCToons.CLERK_COUNTDOWN_TIME - timeStamp + self.npcId = npcId + self.isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.SELL_MOVIE_CLEAR: + return + if mode == NPCToons.SELL_MOVIE_TIMEOUT: + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.isLocalToon: + self.ignore(self.fishGuiDoneEvent) + if self.popupInfo: + self.popupInfo.reparentTo(hidden) + if self.fishGui: + self.fishGui.destroy() + self.fishGui = None + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech | CFTimeout) + self.resetFisherman() + elif mode == NPCToons.SELL_MOVIE_START: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + self.accept(self.av.uniqueName('disable'), self.__handleUnexpectedExit) + self.setupAvatars(self.av) + if self.isLocalToon: + camera.wrtReparentTo(render) + quat = Quat() + quat.setHpr((-150, -2, 0)) + camera.posQuatInterval(1, Point3(-5, 9, base.localAvatar.getHeight() - 0.5), quat, other=self, blendType='easeOut').start() + if self.isLocalToon: + taskMgr.doMethodLater(1.0, self.popupFishGUI, self.uniqueName('popupFishGUI')) + elif mode == NPCToons.SELL_MOVIE_COMPLETE: + chatStr = TTLocalizer.STOREOWNER_THANKSFISH + self.setChatAbsolute(chatStr, CFSpeech | CFTimeout) + self.resetFisherman() + elif mode == NPCToons.SELL_MOVIE_TROPHY: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + numFish, totalNumFish = extraArgs + self.setChatAbsolute(TTLocalizer.STOREOWNER_TROPHY % (numFish, totalNumFish), CFSpeech | CFTimeout) + self.resetFisherman() + elif mode == NPCToons.SELL_MOVIE_NOFISH: + chatStr = TTLocalizer.STOREOWNER_NOFISH + self.setChatAbsolute(chatStr, CFSpeech | CFTimeout) + self.resetFisherman() + elif mode == NPCToons.SELL_MOVIE_NO_MONEY: + self.notify.warning('SELL_MOVIE_NO_MONEY should not be called') + self.resetFisherman() + return + + def __handleSaleDone(self, sell): + self.ignore(self.fishGuiDoneEvent) + self.sendUpdate('completeSale', [sell]) + self.fishGui.destroy() + self.fishGui = None + return + + def popupFishGUI(self, task): + self.setChatAbsolute('', CFSpeech) + self.acceptOnce(self.fishGuiDoneEvent, self.__handleSaleDone) + self.fishGui = FishSellGUI.FishSellGUI(self.fishGuiDoneEvent) diff --git a/toontown/toon/DistributedNPCFishermanAI.py b/toontown/toon/DistributedNPCFishermanAI.py new file mode 100755 index 00000000..25d9b850 --- /dev/null +++ b/toontown/toon/DistributedNPCFishermanAI.py @@ -0,0 +1,95 @@ +from otp.ai.AIBaseGlobal import * +from panda3d.core import * +from DistributedNPCToonBaseAI import * +from toontown.fishing import FishGlobals +from toontown.toonbase import TTLocalizer +from toontown.fishing import FishGlobals +from direct.task import Task + +class DistributedNPCFishermanAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId): + DistributedNPCToonBaseAI.__init__(self, air, npcId) + self.givesQuests = 0 + self.busy = 0 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + self.ignoreAll() + DistributedNPCToonBaseAI.delete(self) + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.air.doId2do: + self.notify.warning('Avatar: %s not found' % avId) + return + if self.isBusy(): + self.freeAvatar(avId) + return + av = self.air.doId2do[avId] + self.busy = avId + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + value = av.fishTank.getTotalValue() + if value > 0: + flag = NPCToons.SELL_MOVIE_START + self.d_setMovie(avId, flag) + taskMgr.doMethodLater(30.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + else: + flag = NPCToons.SELL_MOVIE_NOFISH + self.d_setMovie(avId, flag) + self.sendClearMovie(None) + DistributedNPCToonBaseAI.avatarEnter(self) + return + + def rejectAvatar(self, avId): + self.notify.warning('rejectAvatar: should not be called by a fisherman!') + + def d_setMovie(self, avId, flag, extraArgs = []): + self.sendUpdate('setMovie', [flag, + self.npcId, + avId, + extraArgs, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + + def sendTimeoutMovie(self, task): + self.d_setMovie(self.busy, NPCToons.SELL_MOVIE_TIMEOUT) + self.sendClearMovie(None) + return Task.done + + def sendClearMovie(self, task): + self.ignore(self.air.getAvatarExitEvent(self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.busy = 0 + self.d_setMovie(0, NPCToons.SELL_MOVIE_CLEAR) + return Task.done + + def completeSale(self, sell): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCFishermanAI.completeSale busy with %s' % self.busy) + self.notify.warning('somebody called setMovieDone that I was not busy with! avId: %s' % avId) + return + if sell: + av = simbase.air.doId2do.get(avId) + if av: + trophyResult = self.air.fishManager.creditFishTank(av) + if trophyResult: + movieType = NPCToons.SELL_MOVIE_TROPHY + extraArgs = [len(av.fishCollection), FishGlobals.getTotalNumFish()] + else: + movieType = NPCToons.SELL_MOVIE_COMPLETE + extraArgs = [] + self.d_setMovie(avId, movieType, extraArgs) + else: + av = simbase.air.doId2do.get(avId) + if av: + self.d_setMovie(avId, NPCToons.SELL_MOVIE_NOFISH) + self.sendClearMovie(None) + return + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie(None) + return diff --git a/toontown/toon/DistributedNPCFlippyInToonHall.py b/toontown/toon/DistributedNPCFlippyInToonHall.py new file mode 100755 index 00000000..9900ea9b --- /dev/null +++ b/toontown/toon/DistributedNPCFlippyInToonHall.py @@ -0,0 +1,21 @@ +from panda3d.core import * +from DistributedNPCToon import * + +class DistributedNPCFlippyInToonHall(DistributedNPCToon): + + def __init__(self, cr): + DistributedNPCToon.__init__(self, cr) + + def getCollSphereRadius(self): + return 4 + + def initPos(self): + self.clearMat() + self.setScale(1.25) + + def handleCollisionSphereEnter(self, collEntry): + base.cr.playGame.getPlace().fsm.request('quest', [self]) + self.sendUpdate('avatarEnter', []) + self.nametag3d.setDepthTest(0) + self.nametag3d.setBin('fixed', 0) + self.lookAt(base.localAvatar) diff --git a/toontown/toon/DistributedNPCFlippyInToonHallAI.py b/toontown/toon/DistributedNPCFlippyInToonHallAI.py new file mode 100755 index 00000000..bf53074f --- /dev/null +++ b/toontown/toon/DistributedNPCFlippyInToonHallAI.py @@ -0,0 +1,6 @@ +from DistributedNPCToonAI import * + +class DistributedNPCFlippyInToonHallAI(DistributedNPCToonAI): + + def __init__(self, air, npcId, questCallback = None, hq = 0): + DistributedNPCToonAI.__init__(self, air, npcId, questCallback) diff --git a/toontown/toon/DistributedNPCGlove.py b/toontown/toon/DistributedNPCGlove.py new file mode 100755 index 00000000..573425b5 --- /dev/null +++ b/toontown/toon/DistributedNPCGlove.py @@ -0,0 +1,77 @@ +from otp.nametag.NametagConstants import CFSpeech, CFTimeout +from toontown.toonbase import TTLocalizer, ToontownGlobals +from toontown.toon import NPCToons +from DistributedNPCToonBase import DistributedNPCToonBase +import GloveNPCGlobals, GloveShopGui, time + +class DistributedNPCGlove(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.lastCollision = 0 + self.gloveDialog = None + + def disable(self): + self.ignoreAll() + self.destroyDialog() + DistributedNPCToonBase.disable(self) + + def destroyDialog(self): + self.clearChat() + + if self.gloveDialog: + self.gloveDialog.destroy() + self.gloveDialog = None + + def initToonState(self): + self.setAnimState('neutral', 0.9, None, None) + + if self.name in NPCToons.GlovePositions: + pos = NPCToons.GlovePositions[self.name] + self.setPos(*pos[0]) + self.setH(pos[1]) + + def getCollSphereRadius(self): + return 1.25 + + def handleCollisionSphereEnter(self, collEntry): + if self.lastCollision > time.time(): + return + + self.lastCollision = time.time() + ToontownGlobals.NPCCollisionDelay + + if base.localAvatar.getTotalMoney() < ToontownGlobals.GloveCost: + self.setChatAbsolute(TTLocalizer.GloveMoreMoneyMessage % ToontownGlobals.GloveCost, CFSpeech|CFTimeout) + return + + base.cr.playGame.getPlace().fsm.request('stopped') + base.setCellsAvailable(base.bottomCells, 0) + self.setChatAbsolute(TTLocalizer.GlovePickColorMessage, CFSpeech|CFTimeout) + self.acceptOnce('gloveShopDone', self.__gloveShopDone) + self.gloveDialog = GloveShopGui.GloveShopGui() + + def freeAvatar(self): + base.cr.playGame.getPlace().fsm.request('walk') + base.setCellsAvailable(base.bottomCells, 1) + + def __gloveShopDone(self, state, glove): + self.freeAvatar() + + if state == GloveNPCGlobals.TIMER_END: + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech|CFTimeout) + return + elif state == GloveNPCGlobals.USER_CANCEL: + self.setChatAbsolute(TTLocalizer.STOREOWNER_GOODBYE, CFSpeech|CFTimeout) + return + elif state == GloveNPCGlobals.CHANGE: + self.sendUpdate('changeGlove', [glove]) + + def changeGloveResult(self, avId, state): + if state in GloveNPCGlobals.ChangeMessages: + self.setChatAbsolute(GloveNPCGlobals.ChangeMessages[state], CFSpeech|CFTimeout) + + if state == GloveNPCGlobals.CHANGE_SUCCESSFUL: + av = self.cr.doId2do.get(avId) + + if av: + av.getDustCloud().start() diff --git a/toontown/toon/DistributedNPCGloveAI.py b/toontown/toon/DistributedNPCGloveAI.py new file mode 100755 index 00000000..b6c57cd6 --- /dev/null +++ b/toontown/toon/DistributedNPCGloveAI.py @@ -0,0 +1,27 @@ +from toontown.toonbase import ToontownGlobals +import DistributedNPCToonBaseAI, GloveNPCGlobals, ToonDNA + +class DistributedNPCGloveAI(DistributedNPCToonBaseAI.DistributedNPCToonBaseAI): + + def changeGlove(self, color): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + + if av is None or not hasattr(av, 'dna'): + return + elif len(ToonDNA.allColorsList) <= color: + self.sendUpdate('changeGloveResult', [avId, GloveNPCGlobals.INVALID_COLOR]) + return + elif av.dna.gloveColor == color: + self.sendUpdate('changeGloveResult', [avId, GloveNPCGlobals.SAME_COLOR]) + return + elif av.getTotalMoney() < ToontownGlobals.GloveCost: + self.sendUpdate('changeGloveResult', [avId, GloveNPCGlobals.NOT_ENOUGH_MONEY]) + return + + av.takeMoney(ToontownGlobals.GloveCost) + newDNA = ToonDNA.ToonDNA() + newDNA.makeFromNetString(av.getDNAString()) + newDNA.gloveColor = ToonDNA.allColorsList[color] + taskMgr.doMethodLater(1.0, lambda task: av.b_setDNAString(newDNA.makeNetString()), 'transform-%d' % avId) + self.sendUpdate('changeGloveResult', [avId, GloveNPCGlobals.CHANGE_SUCCESSFUL]) diff --git a/toontown/toon/DistributedNPCKartClerk.py b/toontown/toon/DistributedNPCKartClerk.py new file mode 100755 index 00000000..0e5f716c --- /dev/null +++ b/toontown/toon/DistributedNPCKartClerk.py @@ -0,0 +1,129 @@ +from direct.gui.DirectGui import * +from direct.task.Task import Task +from panda3d.core import * + +from DistributedNPCToonBase import * +import NPCToons +from toontown.racing.KartShopGlobals import * +from toontown.racing.KartShopGui import * +from toontown.toonbase import TTLocalizer +from otp.nametag.NametagConstants import * + + +class DistributedNPCKartClerk(DistributedNPCToonBase): + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + + self.isLocalToon = 0 + self.av = None + self.button = None + self.popupInfo = None + self.kartShopGui = None + + def disable(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupKartShopGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + if self.kartShopGui: + self.kartShopGui.destroy() + self.kartShopGui = None + self.av = None + if self.isLocalToon: + base.localAvatar.posCamera(0, 0) + + DistributedNPCToonBase.disable(self) + + def getCollSphereRadius(self): + return 2.25 + + def handleCollisionSphereEnter(self, collEntry): + base.cr.playGame.getPlace().fsm.request('purchase') + self.sendUpdate('avatarEnter', []) + + def __handleUnexpectedExit(self): + self.notify.warning('unexpected exit') + self.av = None + + def resetKartShopClerk(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupKartShopGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.kartShopGui: + self.kartShopGui.destroy() + self.kartShopGui = None + self.show() + self.startLookAround() + self.detectAvatars() + self.clearMat() + if self.isLocalToon: + self.showNametag2d() + self.freeAvatar() + return Task.done + + def ignoreEventDict(self): + for event in KartShopGlobals.EVENTDICT: + self.ignore(event) + + def setMovie(self, mode, npcId, avId, extraArgs, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.remain = NPCToons.CLERK_COUNTDOWN_TIME - timeStamp + self.npcId = npcId + self.isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.SELL_MOVIE_CLEAR: + return + if mode == NPCToons.SELL_MOVIE_TIMEOUT: + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.isLocalToon: + self.ignoreEventDict() + if self.popupInfo: + self.popupInfo.reparentTo(hidden) + if self.kartShopGui: + self.kartShopGui.destroy() + self.kartShopGui = None + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech | CFTimeout) + self.resetKartShopClerk() + elif mode == NPCToons.SELL_MOVIE_START: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + self.accept(self.av.uniqueName('disable'), self.__handleUnexpectedExit) + self.setupAvatars(self.av) + if self.isLocalToon: + self.hideNametag2d() + camera.wrtReparentTo(render) + quat = Quat() + quat.setHpr((-150, -2, 0)) + camera.posQuatInterval(1, Point3(-5, 9, base.localAvatar.getHeight() - 0.5), quat, other=self, blendType='easeOut').start() + taskMgr.doMethodLater(1.0, self.popupKartShopGUI, self.uniqueName('popupKartShopGUI')) + elif mode == NPCToons.SELL_MOVIE_COMPLETE: + self.setChatAbsolute(TTLocalizer.STOREOWNER_GOODBYE, CFSpeech | CFTimeout) + self.resetKartShopClerk() + elif mode == NPCToons.SELL_MOVIE_PETCANCELED: + self.setChatAbsolute(TTLocalizer.STOREOWNER_GOODBYE, CFSpeech | CFTimeout) + self.resetKartShopClerk() + + def __handleBuyKart(self, kartID): + self.sendUpdate('buyKart', [kartID]) + + def __handleBuyAccessory(self, accID): + self.sendUpdate('buyAccessory', [accID]) + + def __handleGuiDone(self, bTimedOut = False): + self.ignoreAll() + if hasattr(self, 'kartShopGui') and self.kartShopGui != None: + self.kartShopGui.destroy() + self.kartShopGui = None + if not bTimedOut: + self.sendUpdate('transactionDone') + + def popupKartShopGUI(self, task): + self.setChatAbsolute('', CFSpeech) + self.accept(KartShopGlobals.EVENTDICT['buyAccessory'], self.__handleBuyAccessory) + self.accept(KartShopGlobals.EVENTDICT['buyKart'], self.__handleBuyKart) + self.acceptOnce(KartShopGlobals.EVENTDICT['guiDone'], self.__handleGuiDone) + self.kartShopGui = KartShopGuiMgr(KartShopGlobals.EVENTDICT) diff --git a/toontown/toon/DistributedNPCKartClerkAI.py b/toontown/toon/DistributedNPCKartClerkAI.py new file mode 100755 index 00000000..2ee18b01 --- /dev/null +++ b/toontown/toon/DistributedNPCKartClerkAI.py @@ -0,0 +1,129 @@ +from otp.ai.AIBaseGlobal import * +from panda3d.core import * +from DistributedNPCToonBaseAI import * +from toontown.toonbase import TTLocalizer +from direct.task import Task +from toontown.racing.KartShopGlobals import * +from toontown.racing.KartDNA import * + +class DistributedNPCKartClerkAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId): + DistributedNPCToonBaseAI.__init__(self, air, npcId) + self.givesQuests = 0 + self.busy = 0 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + self.ignoreAll() + DistributedNPCToonBaseAI.delete(self) + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.air.doId2do: + self.notify.warning('Avatar: %s not found' % avId) + return + if self.isBusy(): + self.freeAvatar(avId) + return + self.transactionType = '' + av = self.air.doId2do[avId] + self.busy = avId + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + flag = NPCToons.SELL_MOVIE_START + self.d_setMovie(avId, flag) + taskMgr.doMethodLater(KartShopGlobals.KARTCLERK_TIMER, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + DistributedNPCToonBaseAI.avatarEnter(self) + + def rejectAvatar(self, avId): + self.notify.warning('rejectAvatar: should not be called by a kart clerk!') + + def d_setMovie(self, avId, flag, extraArgs = []): + self.sendUpdate('setMovie', [flag, + self.npcId, + avId, + extraArgs, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + + def sendTimeoutMovie(self, task): + self.d_setMovie(self.busy, NPCToons.SELL_MOVIE_TIMEOUT) + self.sendClearMovie(None) + return Task.done + + def sendClearMovie(self, task): + self.ignore(self.air.getAvatarExitEvent(self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.busy = 0 + self.d_setMovie(0, NPCToons.SELL_MOVIE_CLEAR) + return Task.done + + def buyKart(self, whichKart): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCKartClerkAI.buyKart busy with %s' % self.busy) + self.notify.warning('somebody called buyKart that I was not busy with! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + movieType = NPCToons.SELL_MOVIE_COMPLETE + extraArgs = [] + cost = getKartCost(whichKart) + if cost == 'key error': + self.air.writeServerEvent('suspicious', avId, 'Player trying to buy non-existent kart %s' % whichKart) + self.notify.warning('somebody is trying to buy non-existent kart%s! avId: %s' % (whichKart, avId)) + return + elif cost > av.getTickets(): + self.air.writeServerEvent('suspicious', avId, "DistributedNPCKartClerkAI.buyKart and toon doesn't have enough tickets!") + self.notify.warning("somebody called buyKart and didn't have enough tickets to purchase! avId: %s" % avId) + return + av.b_setTickets(av.getTickets() - cost) + self.air.writeServerEvent('kartingTicketsSpent', avId, '%s' % cost) + av.b_setKartBodyType(whichKart) + self.air.writeServerEvent('kartingKartPurchased', avId, '%s' % whichKart) + + def buyAccessory(self, whichAcc): + avId = self.air.getAvatarIdFromSender() + av = simbase.air.doId2do.get(avId) + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCKartClerkAI.buyAccessory busy with %s' % self.busy) + self.notify.warning('somebody called buyAccessory that I was not busy with! avId: %s' % avId) + return + if len(av.getKartAccessoriesOwned()) >= KartShopGlobals.MAX_KART_ACC: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCKartClerkAI.buyAcc and toon already has max number of accessories!') + self.notify.warning('somebody called buyAcc and already has maximum allowed accessories! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + movieType = NPCToons.SELL_MOVIE_COMPLETE + extraArgs = [] + cost = getAccCost(whichAcc) + if cost > av.getTickets(): + self.air.writeServerEvent('suspicious', avId, "DistributedNPCKartClerkAI.buyAcc and toon doesn't have enough tickets!") + self.notify.warning("somebody called buyAcc and didn't have enough tickets to purchase! avId: %s" % avId) + return + av.b_setTickets(av.getTickets() - cost) + self.air.writeServerEvent('kartingTicketsSpent', avId, '%s' % cost) + av.addOwnedAccessory(whichAcc) + self.air.writeServerEvent('kartingAccessoryPurchased', avId, '%s' % whichAcc) + av.updateKartDNAField(getAccessoryType(whichAcc), whichAcc) + + def transactionDone(self): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCKartClerkAI.transactionDone busy with %s' % self.busy) + self.notify.warning('somebody called transactionDone that I was not busy with! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + movieType = NPCToons.SELL_MOVIE_COMPLETE + extraArgs = [] + self.d_setMovie(avId, movieType, extraArgs) + self.sendClearMovie(None) + return + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie(None) + return diff --git a/toontown/toon/DistributedNPCLaffRestock.py b/toontown/toon/DistributedNPCLaffRestock.py new file mode 100755 index 00000000..733398fa --- /dev/null +++ b/toontown/toon/DistributedNPCLaffRestock.py @@ -0,0 +1,71 @@ +from otp.nametag.NametagConstants import CFSpeech, CFTimeout +from toontown.toonbase import TTLocalizer, ToontownGlobals +from toontown.toon import NPCToons +from DistributedNPCToonBase import DistributedNPCToonBase +import LaffRestockGlobals, LaffShopGui, time + +class DistributedNPCLaffRestock(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.lastCollision = 0 + self.laffDialog = None + + def disable(self): + self.ignoreAll() + self.destroyDialog() + DistributedNPCToonBase.disable(self) + + def destroyDialog(self): + self.clearChat() + + if self.laffDialog: + self.laffDialog.destroy() + self.laffDialog = None + + def initToonState(self): + self.setAnimState('neutral', 0.9, None, None) + self.putOnSuit(ToontownGlobals.cogHQZoneId2deptIndex(self.zoneId), rental=True) + + if self.name in NPCToons.LaffRestockPositions: + pos = NPCToons.LaffRestockPositions[self.name] + self.setPos(*pos[0]) + self.setH(pos[1]) + + def getCollSphereRadius(self): + return 1.25 + + def handleCollisionSphereEnter(self, collEntry): + if self.lastCollision > time.time(): + return + + self.lastCollision = time.time() + ToontownGlobals.NPCCollisionDelay + self.lookAt(base.localAvatar) + + if base.localAvatar.getHp() >= base.localAvatar.getMaxHp(): + self.setChatAbsolute(TTLocalizer.RestockFullLaffMessage, CFSpeech | CFTimeout) + return + + base.cr.playGame.getPlace().fsm.request('stopped') + base.setCellsAvailable(base.bottomCells, 0) + self.destroyDialog() + self.acceptOnce('laffShopDone', self.__laffShopDone) + self.laffDialog = LaffShopGui.LaffShopGui() + + def freeAvatar(self): + base.cr.playGame.getPlace().fsm.request('walk') + base.setCellsAvailable(base.bottomCells, 1) + + def __laffShopDone(self, state, laff): + self.freeAvatar() + + if state == LaffRestockGlobals.TIMER_END: + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech|CFTimeout) + elif state == LaffRestockGlobals.USER_CANCEL: + self.setChatAbsolute(TTLocalizer.STOREOWNER_GOODBYE, CFSpeech|CFTimeout) + elif state == LaffRestockGlobals.RESTOCK: + self.sendUpdate('restock', [laff]) + + def restockResult(self, state): + if state in LaffRestockGlobals.RestockMessages: + self.setChatAbsolute(LaffRestockGlobals.RestockMessages[state], CFSpeech | CFTimeout) \ No newline at end of file diff --git a/toontown/toon/DistributedNPCLaffRestockAI.py b/toontown/toon/DistributedNPCLaffRestockAI.py new file mode 100755 index 00000000..26aa9061 --- /dev/null +++ b/toontown/toon/DistributedNPCLaffRestockAI.py @@ -0,0 +1,29 @@ +from toontown.toonbase import ToontownGlobals +import LaffRestockGlobals, DistributedNPCToonBaseAI + +class DistributedNPCLaffRestockAI(DistributedNPCToonBaseAI.DistributedNPCToonBaseAI): + + def restock(self, laff): + av = simbase.air.doId2do.get(self.air.getAvatarIdFromSender()) + + if not av: + return + + newLaff = av.getHp() + laff + + if newLaff > av.getMaxHp(): + self.sendUpdate('restockResult', [LaffRestockGlobals.FULL_LAFF]) + return + elif laff <= 0 or newLaff <= av.getHp(): + self.sendUpdate('restockResult', [LaffRestockGlobals.LESS_LAFF]) + return + + cost = laff * ToontownGlobals.CostPerLaffRestock + + if cost > av.getTotalMoney(): + self.sendUpdate('restockResult', [LaffRestockGlobals.NOT_ENOUGH_MONEY]) + return + + av.takeMoney(cost) + av.toonUp(laff) + self.sendUpdate('restockResult', [LaffRestockGlobals.RESTOCK_SUCCESSFUL]) \ No newline at end of file diff --git a/toontown/toon/DistributedNPCPartyPerson.py b/toontown/toon/DistributedNPCPartyPerson.py new file mode 100755 index 00000000..4acda0b5 --- /dev/null +++ b/toontown/toon/DistributedNPCPartyPerson.py @@ -0,0 +1,178 @@ +from direct.distributed import ClockDelta +from direct.distributed.DistributedObject import DistributedObject +from direct.task.Task import Task +from panda3d.core import * + +from DistributedNPCToonBase import DistributedNPCToonBase +from otp.otpbase import OTPLocalizer +from otp.nametag.NametagConstants import * +from toontown.parties import PartyGlobals +from toontown.toon import NPCToons +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog + + +class DistributedNPCPartyPerson(DistributedNPCToonBase): + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.isInteractingWithLocalToon = 0 + self.av = None + self.button = None + self.askGui = None + return + + def disable(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupAskGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + self.av = None + if self.isInteractingWithLocalToon: + base.localAvatar.posCamera(0, 0) + DistributedNPCToonBase.disable(self) + return + + def delete(self): + if self.askGui: + self.ignore(self.planPartyQuestionGuiDoneEvent) + self.askGui.cleanup() + del self.askGui + DistributedNPCToonBase.delete(self) + + def generate(self): + DistributedNPCToonBase.generate(self) + + def announceGenerate(self): + DistributedNPCToonBase.announceGenerate(self) + self.planPartyQuestionGuiDoneEvent = 'planPartyQuestionDone' + self.askGui = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('askGui'), doneEvent=self.planPartyQuestionGuiDoneEvent, message=TTLocalizer.PartyDoYouWantToPlan, style=TTDialog.YesNo, okButtonText=OTPLocalizer.DialogYes, cancelButtonText=OTPLocalizer.DialogNo) + self.askGui.hide() + + def initToonState(self): + self.setAnimState('neutral', 1.05, None, None) + if self.posIndex % 2 == 0: + side = 'left' + else: + side = 'right' + npcOrigin = self.cr.playGame.hood.loader.geom.find('**/party_person_%s;+s' % side) + if not npcOrigin.isEmpty(): + self.reparentTo(npcOrigin) + self.clearMat() + else: + self.notify.warning('announceGenerate: Could not find party_person_%s' % side) + return + + def getCollSphereRadius(self): + return 1.0 + + def handleCollisionSphereEnter(self, collEntry): + base.cr.playGame.getPlace().fsm.request('purchase') + self.sendUpdate('avatarEnter', []) + + def __handleUnexpectedExit(self): + self.notify.warning('unexpected exit') + self.av = None + return + + def setupAvatars(self, av): + self.ignoreAvatars() + av.stopLookAround() + av.lerpLookAt(Point3(-0.5, 4, 0), time=0.5) + self.stopLookAround() + self.lerpLookAt(Point3(av.getPos(self)), time=0.5) + + def resetPartyPerson(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupAskGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.askGui: + self.askGui.hide() + self.show() + self.startLookAround() + self.detectAvatars() + self.clearMat() + if self.isInteractingWithLocalToon: + self.freeAvatar() + return Task.done + + def setMovie(self, mode, npcId, avId, extraArgs, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.remain = NPCToons.CLERK_COUNTDOWN_TIME - timeStamp + self.npcId = npcId + self.isInteractingWithLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.PARTY_MOVIE_CLEAR: + return + if mode == NPCToons.PARTY_MOVIE_TIMEOUT: + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.isInteractingWithLocalToon: + self.ignore(self.planPartyQuestionGuiDoneEvent) + if self.askGui: + self.askGui.hide() + self.ignore(self.planPartyQuestionGuiDoneEvent) + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech | CFTimeout) + self.resetPartyPerson() + elif mode == NPCToons.PARTY_MOVIE_START: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + self.accept(self.av.uniqueName('disable'), self.__handleUnexpectedExit) + self.setupAvatars(self.av) + if self.isInteractingWithLocalToon: + camera.wrtReparentTo(render) + quat = Quat() + quat.setHpr((-150, -2, 0)) + camera.posQuatInterval(1, Point3(-5, 9, base.localAvatar.getHeight() - 0.5), quat, other=self, blendType='easeOut').start() + taskMgr.doMethodLater(1.0, self.popupAskGUI, self.uniqueName('popupAskGUI')) + else: + self.setChatAbsolute(TTLocalizer.PartyDoYouWantToPlan, CFSpeech | CFTimeout) + elif mode == NPCToons.PARTY_MOVIE_COMPLETE: + chatStr = TTLocalizer.PartyPlannerOnYourWay + self.setChatAbsolute(chatStr, CFSpeech | CFTimeout) + self.resetPartyPerson() + if self.isInteractingWithLocalToon: + base.localAvatar.aboutToPlanParty = True + base.cr.partyManager.setPartyPlannerStyle(self.style) + base.cr.partyManager.setPartyPlannerName(self.name) + base.localAvatar.creatingNewPartyWithMagicWord = False + loaderId = 'safeZoneLoader' + whereId = 'party' + hoodId, zoneId = extraArgs + avId = -1 + place = base.cr.playGame.getPlace() + requestStatus = {'loader': loaderId, + 'where': whereId, + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': avId} + place.requestLeave(requestStatus) + elif mode == NPCToons.PARTY_MOVIE_MAYBENEXTTIME: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + self.setChatAbsolute(TTLocalizer.PartyPlannerMaybeNextTime, CFSpeech | CFTimeout) + self.resetPartyPerson() + elif mode == NPCToons.PARTY_MOVIE_ALREADYHOSTING: + chatStr = TTLocalizer.PartyPlannerHostingTooMany + self.setChatAbsolute(chatStr, CFSpeech | CFTimeout) + self.resetPartyPerson() + elif mode == NPCToons.PARTY_MOVIE_MINCOST: + chatStr = TTLocalizer.PartyPlannerNpcMinCost % PartyGlobals.MinimumPartyCost + self.setChatAbsolute(chatStr, CFSpeech | CFTimeout) + self.resetPartyPerson() + return + + def __handleAskDone(self): + self.ignore(self.planPartyQuestionGuiDoneEvent) + self.sendUpdate('answer', [self.askGui.doneStatus == 'ok']) + self.askGui.hide() + + def popupAskGUI(self, task): + self.setChatAbsolute('', CFSpeech) + self.acceptOnce(self.planPartyQuestionGuiDoneEvent, self.__handleAskDone) + self.askGui.show() diff --git a/toontown/toon/DistributedNPCPartyPersonAI.py b/toontown/toon/DistributedNPCPartyPersonAI.py new file mode 100755 index 00000000..9069fecf --- /dev/null +++ b/toontown/toon/DistributedNPCPartyPersonAI.py @@ -0,0 +1,94 @@ +from DistributedNPCToonBaseAI import DistributedNPCToonBaseAI +from toontown.toonbase import TTLocalizer +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from toontown.toon import NPCToons +from direct.distributed import ClockDelta +from toontown.parties import PartyGlobals + +class DistributedNPCPartyPersonAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId): + DistributedNPCToonBaseAI.__init__(self, air, npcId) + self.givesQuests = 0 + self.busy = 0 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + self.ignoreAll() + DistributedNPCToonBaseAI.delete(self) + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.air.doId2do: + self.notify.warning('Avatar: %s not found' % avId) + return + if self.isBusy(): + self.freeAvatar(avId) + return + av = self.air.doId2do[avId] + self.busy = avId + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + parties = av.hostedParties + if av.getTotalMoney() < PartyGlobals.MinimumPartyCost: + flag = NPCToons.PARTY_MOVIE_MINCOST + self.d_setMovie(avId, flag) + self.sendClearMovie(None) + elif av.canPlanParty(): + flag = NPCToons.PARTY_MOVIE_START + self.d_setMovie(avId, flag) + taskMgr.doMethodLater(30.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + else: + flag = NPCToons.PARTY_MOVIE_ALREADYHOSTING + self.d_setMovie(avId, flag) + self.sendClearMovie(None) + DistributedNPCToonBaseAI.avatarEnter(self) + return + + def rejectAvatar(self, avId): + self.notify.warning('rejectAvatar: should not be called by a party person!') + + def d_setMovie(self, avId, flag, extraArgs = []): + self.sendUpdate('setMovie', [flag, + self.npcId, + avId, + extraArgs, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + + def sendTimeoutMovie(self, task): + self.d_setMovie(self.busy, NPCToons.PARTY_MOVIE_TIMEOUT) + self.sendClearMovie(None) + return Task.done + + def sendClearMovie(self, task): + self.ignore(self.air.getAvatarExitEvent(self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.busy = 0 + self.d_setMovie(0, NPCToons.PARTY_MOVIE_CLEAR) + return Task.done + + def answer(self, wantsToPlan): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCPartyPersonAI.answer busy with %s' % self.busy) + self.notify.warning('somebody called setMovieDone that I was not busy with! avId: %s' % avId) + return + if wantsToPlan: + av = simbase.air.doId2do.get(avId) + if av: + zoneId = self.air.allocateZone() + hoodId = ToontownGlobals.PartyHood + self.d_setMovie(avId, NPCToons.PARTY_MOVIE_COMPLETE, [hoodId, zoneId]) + else: + av = simbase.air.doId2do.get(avId) + if av: + self.d_setMovie(avId, NPCToons.PARTY_MOVIE_MAYBENEXTTIME) + self.sendClearMovie(None) + return + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie(None) + return diff --git a/toontown/toon/DistributedNPCPetclerk.py b/toontown/toon/DistributedNPCPetclerk.py new file mode 100755 index 00000000..0a426c28 --- /dev/null +++ b/toontown/toon/DistributedNPCPetclerk.py @@ -0,0 +1,181 @@ +from direct.gui.DirectGui import * +from direct.task.Task import Task +from panda3d.core import * + +from DistributedNPCToonBase import * +import NPCToons +from toontown.hood import ZoneUtil +from toontown.pets import PetshopGUI +from toontown.toonbase import TTLocalizer +from otp.nametag.NametagConstants import * + + +class DistributedNPCPetclerk(DistributedNPCToonBase): + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.isLocalToon = 0 + self.av = None + self.button = None + self.popupInfo = None + self.petshopGui = None + self.petSeeds = None + self.waitingForPetSeeds = False + + def disable(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupPetshopGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + if self.petshopGui: + self.petshopGui.destroy() + self.petshopGui = None + self.av = None + if self.isLocalToon: + base.localAvatar.posCamera(0, 0) + + DistributedNPCToonBase.disable(self) + + def generate(self): + DistributedNPCToonBase.generate(self) + + self.eventDict = {} + self.eventDict['guiDone'] = 'guiDone' + self.eventDict['petAdopted'] = 'petAdopted' + self.eventDict['petReturned'] = 'petReturned' + self.eventDict['fishSold'] = 'fishSold' + + def getCollSphereRadius(self): + return 4.0 + + def handleCollisionSphereEnter(self, collEntry): + base.cr.playGame.getPlace().fsm.request('purchase') + self.sendUpdate('avatarEnter', []) + + def __handleUnexpectedExit(self): + self.notify.warning('unexpected exit') + self.av = None + + def resetPetshopClerk(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupPetshopGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.petshopGui: + self.petshopGui.destroy() + self.petshopGui = None + self.show() + self.startLookAround() + self.detectAvatars() + self.clearMat() + if self.isLocalToon: + self.showNametag2d() + self.freeAvatar() + self.petSeeds = None + self.waitingForPetSeeds = False + return Task.done + + def ignoreEventDict(self): + for event in self.eventDict.values(): + self.ignore(event) + + def setPetSeeds(self, petSeeds): + self.petSeeds = petSeeds + if self.waitingForPetSeeds: + self.waitingForPetSeeds = False + self.popupPetshopGUI(None) + + def setMovie(self, mode, npcId, avId, extraArgs, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.remain = NPCToons.CLERK_COUNTDOWN_TIME - timeStamp + self.npcId = npcId + self.isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.SELL_MOVIE_CLEAR: + return + if mode == NPCToons.SELL_MOVIE_TIMEOUT: + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.isLocalToon: + self.ignoreEventDict() + if self.popupInfo: + self.popupInfo.reparentTo(hidden) + if self.petshopGui: + self.petshopGui.destroy() + self.petshopGui = None + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_START: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + self.accept(self.av.uniqueName('disable'), self.__handleUnexpectedExit) + self.setupAvatars(self.av) + if self.isLocalToon: + self.hideNametag2d() + camera.wrtReparentTo(render) + seq = Sequence((camera.posQuatInterval(1, Vec3(-5, 9, self.getHeight() - 0.5), Vec3(-150, -2, 0), other=self, blendType='easeOut', name=self.uniqueName('lerpCamera')))) + seq.start() + taskMgr.doMethodLater(1.0, self.popupPetshopGUI, self.uniqueName('popupPetshopGUI')) + elif mode == NPCToons.SELL_MOVIE_COMPLETE: + self.setChatAbsolute(TTLocalizer.STOREOWNER_THANKSFISH_PETSHOP, CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_PETRETURNED: + self.setChatAbsolute(TTLocalizer.STOREOWNER_PETRETURNED, CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_PETADOPTED: + self.setChatAbsolute(TTLocalizer.STOREOWNER_PETADOPTED, CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_PETCANCELED: + self.setChatAbsolute(TTLocalizer.STOREOWNER_PETCANCELED, CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_TROPHY: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + numFish, totalNumFish = extraArgs + self.setChatAbsolute(TTLocalizer.STOREOWNER_TROPHY % (numFish, totalNumFish), CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_NOFISH: + self.setChatAbsolute(TTLocalizer.STOREOWNER_NOFISH, CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_NO_MONEY: + self.notify.warning('SELL_MOVIE_NO_MONEY should not be called') + self.resetPetshopClerk() + + def __handlePetAdopted(self, whichPet, nameIndex): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ADOPTADOOLE: Adopt a doodle.') + base.cr.removePetFromFriendsMap() + self.ignore(self.eventDict['petAdopted']) + self.sendUpdate('petAdopted', [whichPet, nameIndex]) + + def __handlePetReturned(self): + base.cr.removePetFromFriendsMap() + self.ignore(self.eventDict['petReturned']) + self.sendUpdate('petReturned') + + def __handleFishSold(self): + self.ignore(self.eventDict['fishSold']) + self.sendUpdate('fishSold') + + def __handleGUIDone(self, bTimedOut = False): + self.ignore(self.eventDict['guiDone']) + self.petshopGui.destroy() + self.petshopGui = None + if not bTimedOut: + self.sendUpdate('transactionDone') + return + + def popupPetshopGUI(self, task): + if not self.petSeeds: + self.waitingForPetSeeds = True + return + self.setChatAbsolute('', CFSpeech) + self.acceptOnce(self.eventDict['guiDone'], self.__handleGUIDone) + self.acceptOnce(self.eventDict['petAdopted'], self.__handlePetAdopted) + self.acceptOnce(self.eventDict['petReturned'], self.__handlePetReturned) + self.acceptOnce(self.eventDict['fishSold'], self.__handleFishSold) + self.petshopGui = PetshopGUI.PetshopGUI(self.eventDict, self.petSeeds) diff --git a/toontown/toon/DistributedNPCPetclerkAI.py b/toontown/toon/DistributedNPCPetclerkAI.py new file mode 100755 index 00000000..5eebcedf --- /dev/null +++ b/toontown/toon/DistributedNPCPetclerkAI.py @@ -0,0 +1,155 @@ +from otp.ai.AIBaseGlobal import * +from panda3d.core import * +from DistributedNPCToonBaseAI import * +from toontown.toonbase import TTLocalizer +from direct.task import Task +from toontown.fishing import FishGlobals +from toontown.pets import PetUtil, PetDNA, PetConstants +from toontown.hood import ZoneUtil + +class DistributedNPCPetclerkAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId): + DistributedNPCToonBaseAI.__init__(self, air, npcId) + self.givesQuests = 0 + self.busy = 0 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + self.ignoreAll() + DistributedNPCToonBaseAI.delete(self) + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if not avId in self.air.doId2do: + self.notify.warning('Avatar: %s not found' % avId) + return + if self.isBusy(): + self.freeAvatar(avId) + return + self.petSeeds = self.air.petMgr.getAvailablePets(ZoneUtil.getCanonicalHoodId(self.zoneId)) + numGenders = len(PetDNA.PetGenders) + self.petSeeds *= numGenders + self.petSeeds.sort() + self.sendUpdateToAvatarId(avId, 'setPetSeeds', [self.petSeeds]) + self.transactionType = '' + av = self.air.doId2do[avId] + self.busy = avId + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + flag = NPCToons.SELL_MOVIE_START + self.d_setMovie(avId, flag) + taskMgr.doMethodLater(PetConstants.PETCLERK_TIMER, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + DistributedNPCToonBaseAI.avatarEnter(self) + + def rejectAvatar(self, avId): + self.notify.warning('rejectAvatar: should not be called by a fisherman!') + + def d_setMovie(self, avId, flag, extraArgs = []): + self.sendUpdate('setMovie', [flag, + self.npcId, + avId, + extraArgs, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + + def sendTimeoutMovie(self, task): + self.d_setMovie(self.busy, NPCToons.SELL_MOVIE_TIMEOUT) + self.sendClearMovie(None) + return Task.done + + def sendClearMovie(self, task): + self.ignore(self.air.getAvatarExitEvent(self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.busy = 0 + self.d_setMovie(0, NPCToons.SELL_MOVIE_CLEAR) + return Task.done + + def fishSold(self): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCPetshopAI.fishSold busy with %s' % self.busy) + self.notify.warning('somebody called fishSold that I was not busy with! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + trophyResult = self.air.fishManager.creditFishTank(av) + if trophyResult: + movieType = NPCToons.SELL_MOVIE_TROPHY + extraArgs = [len(av.fishCollection), FishGlobals.getTotalNumFish()] + else: + movieType = NPCToons.SELL_MOVIE_COMPLETE + extraArgs = [] + self.d_setMovie(avId, movieType, extraArgs) + self.transactionType = 'fish' + self.sendClearMovie(None) + return + + def petAdopted(self, petNum, nameIndex): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCPetshopAI.petAdopted busy with %s' % self.busy) + self.notify.warning('somebody called petAdopted that I was not busy with! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + from toontown.hood import ZoneUtil + zoneId = ZoneUtil.getCanonicalSafeZoneId(self.zoneId) + if petNum not in xrange(0, len(self.petSeeds)): + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCPetshopAI.petAdopted and no such pet!') + self.notify.warning('somebody called petAdopted on a non-existent pet! avId: %s' % avId) + return + cost = PetUtil.getPetCostFromSeed(self.petSeeds[petNum], zoneId) + if cost > av.getTotalMoney(): + self.air.writeServerEvent('suspicious', avId, "DistributedNPCPetshopAI.petAdopted and toon doesn't have enough money!") + self.notify.warning("somebody called petAdopted and didn't have enough money to adopt! avId: %s" % avId) + return + if av.petId != 0: + simbase.air.petMgr.deleteToonsPet(avId) + gender = petNum % len(PetDNA.PetGenders) + if nameIndex not in xrange(0, len(TTLocalizer.PetNameDictionary) - 1): + self.air.writeServerEvent('avoid_crash', avId, "DistributedNPCPetclerkAI.petAdopted and didn't have valid nameIndex!") + self.notify.warning("somebody called petAdopted and didn't have valid nameIndex to adopt! avId: %s" % avId) + return + simbase.air.petMgr.createNewPetFromSeed(avId, self.petSeeds[petNum], nameIndex=nameIndex, gender=gender, safeZoneId=zoneId) + self.notify.warning("Created new pet from seed") + self.transactionType = 'adopt' + bankPrice = min(av.getBankMoney(), cost) + walletPrice = cost - bankPrice + av.b_setBankMoney(av.getBankMoney() - bankPrice) + av.b_setMoney(av.getMoney() - walletPrice) + + def petReturned(self): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCPetshopAI.petReturned busy with %s' % self.busy) + self.notify.warning('somebody called petReturned that I was not busy with! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + simbase.air.petMgr.deleteToonsPet(avId) + self.transactionType = 'return' + + self.transactionDone() + + def transactionDone(self): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCPetshopAI.transactionDone busy with %s' % self.busy) + self.notify.warning('somebody called transactionDone that I was not busy with! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + if self.transactionType == 'adopt': + self.d_setMovie(avId, NPCToons.SELL_MOVIE_PETADOPTED) + elif self.transactionType == 'return': + self.d_setMovie(avId, NPCToons.SELL_MOVIE_PETRETURNED) + elif self.transactionType == '': + self.d_setMovie(avId, NPCToons.SELL_MOVIE_PETCANCELED) + self.sendClearMovie(None) + return + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie(None) + return diff --git a/toontown/toon/DistributedNPCScientist.py b/toontown/toon/DistributedNPCScientist.py new file mode 100755 index 00000000..5be3e2d7 --- /dev/null +++ b/toontown/toon/DistributedNPCScientist.py @@ -0,0 +1,66 @@ +from toontown.toonbase import TTLocalizer +from DistributedNPCToonBase import DistributedNPCToonBase + +class DistributedNPCScientist(DistributedNPCToonBase): + + def getCollSphereRadius(self): + return 2.5 + + def initPos(self): + self.setHpr(180, 0, 0) + self.setScale(1.0) + + def handleCollisionSphereEnter(self, collEntry): + self.nametag3d.setDepthTest(0) + self.nametag3d.setBin('fixed', 0) + + def setModelHand(self, path): + model = loader.loadModel(path) + + for hand in self.getRightHands(): + placeholder = hand.attachNewNode('RightHandObj') + placeholder.setH(180) + placeholder.setScale(render, 1.0) + placeholder.setPos(0, 0, 0.1) + model.instanceTo(placeholder) + + def generateToon(self): + DistributedNPCToonBase.generateToon(self) + self.setupToonNodes() + self.setModelHand('phase_4/models/props/tt_m_prp_acs_%s' % ('sillyReader' if self.style.getAnimal() == 'duck' else 'clipboard')) + self.startSequence(config.GetInt('silly-meter-phase', 12)) + self.accept('SillyMeterPhase', self.startSequence) + + def startLookAround(self): + pass + + def startSequence(self, phase): + if not self.style.getAnimal() == 'horse': + return + + if phase < 4: + dialogue = TTLocalizer.ScientistPhase1Dialogue + elif phase < 8: + dialogue = TTLocalizer.ScientistPhase2Dialogue + elif phase < 12: + dialogue = TTLocalizer.ScientistPhase3Dialogue + elif phase == 12: + dialogue = TTLocalizer.ScientistPhase4Dialogue + elif phase == 13: + dialogue = TTLocalizer.ScientistPhase5Dialogue + else: + dialogue = TTLocalizer.ScientistPhase6Dialogue + + self.stopSequence() + self.sequence = self.createTalkSequence(dialogue, 1) + self.sequence.loop(0) + + def stopSequence(self): + if hasattr(self, 'sequence'): + self.sequence.pause() + del self.sequence + + def disable(self): + self.stopSequence() + self.ignore('SillyMeterPhase') + DistributedNPCToonBase.disable(self) diff --git a/toontown/toon/DistributedNPCScientistAI.py b/toontown/toon/DistributedNPCScientistAI.py new file mode 100755 index 00000000..e71f66c4 --- /dev/null +++ b/toontown/toon/DistributedNPCScientistAI.py @@ -0,0 +1,8 @@ +import DistributedNPCToonBaseAI, random + +Animation = random.choice(["ScientistPlay", "ScientistWork", "ScientistLessWork", "ScientistJealous"]) + +class DistributedNPCScientistAI(DistributedNPCToonBaseAI.DistributedNPCToonBaseAI): + + def getStartAnimState(self): + return 'ScientistEmcee' if self.npcId == 2020 else Animation diff --git a/toontown/toon/DistributedNPCSpecialQuestGiver.py b/toontown/toon/DistributedNPCSpecialQuestGiver.py new file mode 100755 index 00000000..f266a3d4 --- /dev/null +++ b/toontown/toon/DistributedNPCSpecialQuestGiver.py @@ -0,0 +1,229 @@ +from direct.interval.IntervalGlobal import * +from panda3d.core import * + +from DistributedNPCToonBase import * +from toontown.hood import ZoneUtil +from toontown.quest import QuestChoiceGui +from toontown.quest import QuestParser +from toontown.quest import TrackChoiceGui +from toontown.toonbase import TTLocalizer +from otp.nametag.NametagConstants import * + + +ChoiceTimeout = 20 + + +class DistributedNPCSpecialQuestGiver(DistributedNPCToonBase): + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + + self.curQuestMovie = None + self.questChoiceGui = None + self.trackChoiceGui = None + self.cr = cr + + def announceGenerate(self): + self.setAnimState('neutral', 0.9, None, None) + npcOrigin = self.cr.playGame.hood.loader.geom.find('**/npc_origin_' + `(self.posIndex)`) + if not npcOrigin.isEmpty(): + self.reparentTo(npcOrigin) + self.clearMat() + else: + self.notify.warning('announceGenerate: Could not find npc_origin_' + str(self.posIndex)) + + DistributedNPCToonBase.announceGenerate(self) + + messenger.send('doneTutorialSetup') + + def delayDelete(self): + DistributedNPCToonBase.delayDelete(self) + + if self.curQuestMovie: + curQuestMovie = self.curQuestMovie + self.curQuestMovie = None + curQuestMovie.timeout(fFinish=1) + curQuestMovie.cleanup() + + def disable(self): + self.cleanupMovie() + + DistributedNPCToonBase.disable(self) + + def cleanupMovie(self): + self.clearChat() + self.ignore('chooseQuest') + if self.questChoiceGui: + self.questChoiceGui.destroy() + self.questChoiceGui = None + self.ignore(self.uniqueName('doneChatPage')) + if self.curQuestMovie: + self.curQuestMovie.timeout(fFinish=1) + self.curQuestMovie.cleanup() + self.curQuestMovie = None + if self.trackChoiceGui: + self.trackChoiceGui.destroy() + self.trackChoiceGui = None + + def allowedToTalk(self): + return True + + def handleCollisionSphereEnter(self, collEntry): + base.cr.playGame.getPlace().fsm.request('quest', [self]) + self.sendUpdate('avatarEnter', []) + self.nametag3d.setDepthTest(0) + self.nametag3d.setBin('fixed', 0) + + def finishMovie(self, av, isLocalToon, elapsedTime): + self.cleanupMovie() + av.startLookAround() + self.startLookAround() + self.detectAvatars() + self.clearMat() + if isLocalToon: + self.showNametag2d() + taskMgr.remove(self.uniqueName('lerpCamera')) + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + self.sendUpdate('setMovieDone', []) + self.nametag3d.clearDepthTest() + self.nametag3d.clearBin() + + def setupCamera(self, mode): + camera.wrtReparentTo(render) + if mode == NPCToons.QUEST_MOVIE_QUEST_CHOICE or mode == NPCToons.QUEST_MOVIE_TRACK_CHOICE: + camera.posQuatInterval(1, (5, 9, self.getHeight() - 0.5), (155, -2, 0), other=self, blendType='easeOut').start() + else: + camera.posQuatInterval(1, (-5, 9, self.getHeight() - 0.5), (-150, -2, 0), other=self, blendType='easeOut').start() + + def setMovie(self, mode, npcId, avId, quests, timestamp): + isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.QUEST_MOVIE_CLEAR: + self.cleanupMovie() + return + if mode == NPCToons.QUEST_MOVIE_TIMEOUT: + self.cleanupMovie() + if isLocalToon: + self.freeAvatar() + self.setPageNumber(0, -1) + self.clearChat() + self.startLookAround() + self.detectAvatars() + return + av = base.cr.doId2do.get(avId) + if av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + if mode == NPCToons.QUEST_MOVIE_REJECT: + rejectString = Quests.chooseQuestDialogReject() + rejectString = Quests.fillInQuestNames(rejectString, avName=av.name) + self.setChatAbsolute(rejectString, CFSpeech | CFTimeout) + if isLocalToon: + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + return + if mode == NPCToons.QUEST_MOVIE_TIER_NOT_DONE: + rejectString = Quests.chooseQuestDialogTierNotDone() + rejectString = Quests.fillInQuestNames(rejectString, avName=av.name) + self.setChatAbsolute(rejectString, CFSpeech | CFTimeout) + if isLocalToon: + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + return + self.setupAvatars(av) + fullString = '' + toNpcId = None + if isLocalToon: + self.hideNametag2d() + if mode == NPCToons.QUEST_MOVIE_COMPLETE: + questId, rewardId, toNpcId = quests + scriptId = 'quest_complete_' + str(questId) + if QuestParser.questDefined(scriptId): + self.curQuestMovie = QuestParser.NPCMoviePlayer(scriptId, av, self) + self.curQuestMovie.play() + return + if isLocalToon: + self.setupCamera(mode) + greetingString = Quests.chooseQuestDialog(questId, Quests.GREETING) + if greetingString: + fullString += greetingString + '\x07' + fullString += Quests.chooseQuestDialog(questId, Quests.COMPLETE) + '\x07' + if rewardId: + fullString += Quests.getReward(rewardId).getString() + leavingString = Quests.chooseQuestDialog(questId, Quests.LEAVING) + if leavingString: + fullString += '\x07' + leavingString + elif mode == NPCToons.QUEST_MOVIE_QUEST_CHOICE_CANCEL: + fullString = TTLocalizer.QuestMovieQuestChoiceCancel + elif mode == NPCToons.QUEST_MOVIE_TRACK_CHOICE_CANCEL: + fullString = TTLocalizer.QuestMovieTrackChoiceCancel + elif mode == NPCToons.QUEST_MOVIE_INCOMPLETE: + questId, completeStatus, toNpcId = quests + scriptId = 'quest_incomplete_' + str(questId) + if QuestParser.questDefined(scriptId): + if self.curQuestMovie: + self.curQuestMovie.timeout() + self.curQuestMovie.cleanup() + self.curQuestMovie = None + self.curQuestMovie = QuestParser.NPCMoviePlayer(scriptId, av, self) + self.curQuestMovie.play() + return + if isLocalToon: + self.setupCamera(mode) + greetingString = Quests.chooseQuestDialog(questId, Quests.GREETING) + if greetingString: + fullString += greetingString + '\x07' + fullString += Quests.chooseQuestDialog(questId, completeStatus) + leavingString = Quests.chooseQuestDialog(questId, Quests.LEAVING) + if leavingString: + fullString += '\x07' + leavingString + elif mode == NPCToons.QUEST_MOVIE_ASSIGN: + questId, rewardId, toNpcId = quests + scriptId = 'quest_assign_' + str(questId) + if QuestParser.questDefined(scriptId): + if self.curQuestMovie: + self.curQuestMovie.timeout() + self.curQuestMovie.cleanup() + self.curQuestMovie = None + self.curQuestMovie = QuestParser.NPCMoviePlayer(scriptId, av, self) + self.curQuestMovie.play() + return + if isLocalToon: + self.setupCamera(mode) + fullString += Quests.chooseQuestDialog(questId, Quests.QUEST) + leavingString = Quests.chooseQuestDialog(questId, Quests.LEAVING) + if leavingString: + fullString += '\x07' + leavingString + elif mode == NPCToons.QUEST_MOVIE_QUEST_CHOICE: + if isLocalToon: + self.setupCamera(mode) + self.setChatAbsolute(TTLocalizer.QuestMovieQuestChoice, CFSpeech) + if isLocalToon: + self.acceptOnce('chooseQuest', self.sendChooseQuest) + self.questChoiceGui = QuestChoiceGui.QuestChoiceGui() + self.questChoiceGui.setQuests(quests, npcId, ChoiceTimeout) + return + elif mode == NPCToons.QUEST_MOVIE_TRACK_CHOICE: + if isLocalToon: + self.setupCamera(mode) + tracks = quests + self.setChatAbsolute(TTLocalizer.QuestMovieTrackChoice, CFSpeech) + if isLocalToon: + self.acceptOnce('chooseTrack', self.sendChooseTrack) + self.trackChoiceGui = TrackChoiceGui.TrackChoiceGui(tracks, ChoiceTimeout) + return + fullString = Quests.fillInQuestNames(fullString, avName=av.name, fromNpcId=npcId, toNpcId=toNpcId) + self.acceptOnce(self.uniqueName('doneChatPage'), self.finishMovie, extraArgs=[av, isLocalToon]) + self.clearChat() + self.setPageChat(avId, 0, fullString, 1) + + def sendChooseQuest(self, questId): + if self.questChoiceGui: + self.questChoiceGui.destroy() + self.questChoiceGui = None + self.sendUpdate('chooseQuest', [questId]) + + def sendChooseTrack(self, trackId): + if self.trackChoiceGui: + self.trackChoiceGui.destroy() + self.trackChoiceGui = None + self.sendUpdate('chooseTrack', [trackId]) diff --git a/toontown/toon/DistributedNPCSpecialQuestGiverAI.py b/toontown/toon/DistributedNPCSpecialQuestGiverAI.py new file mode 100755 index 00000000..58e2db3a --- /dev/null +++ b/toontown/toon/DistributedNPCSpecialQuestGiverAI.py @@ -0,0 +1,229 @@ +from otp.ai.AIBaseGlobal import * +from direct.task.Task import Task +from panda3d.core import * +from DistributedNPCToonBaseAI import * +from toontown.quest import Quests + +class DistributedNPCSpecialQuestGiverAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId, questCallback = None, hq = 0, tutorial = 0): + DistributedNPCToonBaseAI.__init__(self, air, npcId, questCallback) + self.hq = hq + self.tutorial = tutorial + self.pendingAvId = None + return + + def getTutorial(self): + return self.tutorial + + def setTutorial(self, val): + self.tutorial = val + + def setHq(self, hq): + self.hq = hq + + def getHq(self): + return self.hq + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('avatar enter ' + str(avId)) + self.air.questManager.requestInteract(avId, self) + DistributedNPCToonBaseAI.avatarEnter(self) + + def chooseQuest(self, questId, quest = None): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('chooseQuest: avatar %s choseQuest %s' % (avId, questId)) + if not self.pendingAvId: + self.notify.warning('chooseQuest: not expecting an answer from any avatar: %s' % avId) + return + if self.pendingAvId != avId: + self.notify.warning('chooseQuest: not expecting an answer from this avatar: %s' % avId) + return + if questId == 0: + self.pendingAvId = None + self.pendingQuests = None + self.air.questManager.avatarCancelled(avId) + self.cancelChoseQuest(avId) + return + for quest in self.pendingQuests: + if questId == quest[0]: + self.pendingAvId = None + self.pendingQuests = None + self.air.questManager.avatarChoseQuest(avId, self, *quest) + return + + self.air.questManager.avatarChoseQuest(avId, self, *quest) + self.notify.warning('chooseQuest: avatar: %s chose a quest not offered: %s' % (avId, questId)) + self.pendingAvId = None + self.pendingQuests = None + return + + def chooseTrack(self, trackId): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('chooseTrack: avatar %s choseTrack %s' % (avId, trackId)) + if not self.pendingAvId: + self.notify.warning('chooseTrack: not expecting an answer from any avatar: %s' % avId) + return + if self.pendingAvId != avId: + self.notify.warning('chooseTrack: not expecting an answer from this avatar: %s' % avId) + return + if trackId == -1: + self.pendingAvId = None + self.pendingTracks = None + self.pendingTrackQuest = None + self.air.questManager.avatarCancelled(avId) + self.cancelChoseTrack(avId) + return + for track in self.pendingTracks: + if trackId == track: + self.air.questManager.avatarChoseTrack(avId, self, self.pendingTrackQuest, trackId) + self.pendingAvId = None + self.pendingTracks = None + self.pendingTrackQuest = None + return + + self.notify.warning('chooseTrack: avatar: %s chose a track not offered: %s' % (avId, trackId)) + self.pendingAvId = None + self.pendingTracks = None + self.pendingTrackQuest = None + return + + def sendTimeoutMovie(self, task): + self.pendingAvId = None + self.pendingQuests = None + self.pendingTracks = None + self.pendingTrackQuest = None + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TIMEOUT, + self.npcId, + self.busy, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendClearMovie(None) + self.busy = 0 + return Task.done + + def sendClearMovie(self, task): + self.pendingAvId = None + self.pendingQuests = None + self.pendingTracks = None + self.pendingTrackQuest = None + self.busy = 0 + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_CLEAR, + self.npcId, + 0, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + return Task.done + + def rejectAvatar(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_REJECT, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(5.5, self.sendClearMovie, self.uniqueName('clearMovie')) + + def rejectAvatarTierNotDone(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TIER_NOT_DONE, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(5.5, self.sendClearMovie, self.uniqueName('clearMovie')) + + def completeQuest(self, avId, questId, rewardId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_COMPLETE, + self.npcId, + avId, + [questId, rewardId, 0], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(NPCToons.QUEST_COUNTDOWN_TIME, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def incompleteQuest(self, avId, questId, completeStatus, toNpcId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_INCOMPLETE, + self.npcId, + avId, + [questId, completeStatus, toNpcId], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(NPCToons.QUEST_COUNTDOWN_TIME, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def assignQuest(self, avId, questId, rewardId, toNpcId): + self.busy = avId + if self.questCallback: + self.questCallback() + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_ASSIGN, + self.npcId, + avId, + [questId, rewardId, toNpcId], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(NPCToons.QUEST_COUNTDOWN_TIME, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def presentQuestChoice(self, avId, quests): + self.busy = avId + self.pendingAvId = avId + self.pendingQuests = quests + flatQuests = [] + for quest in quests: + flatQuests.extend(quest) + + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_QUEST_CHOICE, + self.npcId, + avId, + flatQuests, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(NPCToons.QUEST_COUNTDOWN_TIME, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def presentTrackChoice(self, avId, questId, tracks): + self.busy = avId + self.pendingAvId = avId + self.pendingTracks = tracks + self.pendingTrackQuest = questId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TRACK_CHOICE, + self.npcId, + avId, + tracks, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(NPCToons.QUEST_COUNTDOWN_TIME, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def cancelChoseQuest(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_QUEST_CHOICE_CANCEL, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(NPCToons.QUEST_COUNTDOWN_TIME, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def cancelChoseTrack(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TRACK_CHOICE_CANCEL, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(NPCToons.QUEST_COUNTDOWN_TIME, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def setMovieDone(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setMovieDone busy: %s avId: %s' % (self.busy, avId)) + if self.busy == avId: + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie(None) + elif self.busy: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCToonAI.setMovieDone busy with %s' % self.busy) + self.notify.warning('somebody called setMovieDone that I was not busy with! avId: %s' % avId) + return diff --git a/toontown/toon/DistributedNPCTailor.py b/toontown/toon/DistributedNPCTailor.py new file mode 100755 index 00000000..2e24dde2 --- /dev/null +++ b/toontown/toon/DistributedNPCTailor.py @@ -0,0 +1,297 @@ +from direct.gui.DirectGui import * +from direct.task.Task import Task +from panda3d.core import * + +from DistributedNPCToonBase import * +import NPCToons +import TailorClothesGUI +import ToonDNA +from otp.nametag.NametagConstants import * +from toontown.estate import ClosetGlobals +from toontown.toonbase import TTLocalizer + + +class DistributedNPCTailor(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.isLocalToon = 0 + self.clothesGUI = None + self.av = None + self.oldStyle = None + self.browsing = 0 + self.roomAvailable = 0 + self.button = None + self.popupInfo = None + return + + def disable(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupPurchaseGUI')) + #taskMgr.remove(self.uniqueName('lerpCamera')) + if self.clothesGUI: + self.clothesGUI.exit() + self.clothesGUI.unload() + self.clothesGUI = None + if self.button != None: + self.button.destroy() + del self.button + self.cancelButton.destroy() + del self.cancelButton + del self.gui + self.counter.show() + del self.counter + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + self.av = None + self.oldStyle = None + base.localAvatar.posCamera(0, 0) + DistributedNPCToonBase.disable(self) + return + + def handleCollisionSphereEnter(self, collEntry): + base.cr.playGame.getPlace().fsm.request('purchase') + self.sendUpdate('avatarEnter', []) + + def __handleUnexpectedExit(self): + self.notify.warning('unexpected exit') + self.av = None + self.oldStyle = None + return + + def resetTailor(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupPurchaseGUI')) + #taskMgr.remove(self.uniqueName('lerpCamera')) + if self.clothesGUI: + self.clothesGUI.hideButtons() + self.clothesGUI.exit() + self.clothesGUI.unload() + self.clothesGUI = None + if self.button != None: + self.button.destroy() + del self.button + self.cancelButton.destroy() + del self.cancelButton + del self.gui + self.counter.show() + del self.counter + self.show() + self.startLookAround() + self.detectAvatars() + self.clearMat() + if self.isLocalToon: + self.freeAvatar() + return Task.done + + def setMovie(self, mode, npcId, avId, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.remain = NPCToons.CLERK_COUNTDOWN_TIME - timeStamp + self.npcId = npcId + self.isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.PURCHASE_MOVIE_CLEAR: + return + if mode == NPCToons.PURCHASE_MOVIE_TIMEOUT: + #taskMgr.remove(self.uniqueName('lerpCamera')) + if self.isLocalToon: + self.ignore(self.purchaseDoneEvent) + self.ignore(self.swapEvent) + if self.popupInfo: + self.popupInfo.reparentTo(hidden) + if self.clothesGUI: + self.clothesGUI.resetClothes(self.oldStyle) + self.__handlePurchaseDone(timeout=1) + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech | CFTimeout) + self.resetTailor() + elif mode == NPCToons.PURCHASE_MOVIE_START or mode == NPCToons.PURCHASE_MOVIE_START_BROWSE or mode == NPCToons.PURCHASE_MOVIE_START_NOROOM or mode == NPCToons.PURCHASE_MOVIE_START_BROWSE_JBS: + if mode == NPCToons.PURCHASE_MOVIE_START: + self.browsing = 0 + self.roomAvailable = 1 + elif mode == NPCToons.PURCHASE_MOVIE_START_BROWSE or mode == NPCToons.PURCHASE_MOVIE_START_BROWSE_JBS: + self.browsing = 1 + self.roomAvailable = 1 + elif mode == NPCToons.PURCHASE_MOVIE_START_NOROOM: + self.browsing = 0 + self.roomAvailable = 0 + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + self.accept(self.av.uniqueName('disable'), self.__handleUnexpectedExit) + style = self.av.getStyle() + self.oldStyle = ToonDNA.ToonDNA() + self.oldStyle.makeFromNetString(style.makeNetString()) + self.setupAvatars(self.av) + if self.isLocalToon: + #camera.wrtReparentTo(render) + #camera.lerpPosHpr(-5, 9, self.getHeight() - 0.5, -150, -2, 0, 1, other=self, blendType='easeOut', task=self.uniqueName('lerpCamera')) + self.cameraWork = camera.posHprInterval(2, Point3(-5, 9, self.getHeight() - 0.5), Point3(-150, -2, 0), blendType='easeOut') + self.cameraWork.start() + if self.browsing == 0: + if self.roomAvailable == 0: + self.setChatAbsolute(TTLocalizer.STOREOWNER_NOROOM, CFSpeech | CFTimeout) + else: + self.setChatAbsolute(TTLocalizer.STOREOWNER_GREETING, CFSpeech | CFTimeout) + elif mode == NPCToons.PURCHASE_MOVIE_START_BROWSE_JBS: + self.setChatAbsolute(TTLocalizer.STOREOWNER_BROWSING_JBS, CFSpeech | CFTimeout) + else: + self.setChatAbsolute(TTLocalizer.STOREOWNER_BROWSING, CFSpeech | CFTimeout) + if self.isLocalToon: + taskMgr.doMethodLater(3.0, self.popupPurchaseGUI, self.uniqueName('popupPurchaseGUI')) + print '-----------Starting tailor interaction-----------' + print 'avid: %s, gender: %s' % (self.av.doId, self.av.style.gender) + print 'current top = %s,%s,%s,%s and bot = %s,%s,' % (self.av.style.topTex, + self.av.style.topTexColor, + self.av.style.sleeveTex, + self.av.style.sleeveTexColor, + self.av.style.botTex, + self.av.style.botTexColor) + print 'topsList = %s' % self.av.getClothesTopsList() + print 'bottomsList = %s' % self.av.getClothesBottomsList() + print '-------------------------------------------------' + elif mode == NPCToons.PURCHASE_MOVIE_COMPLETE: + self.setChatAbsolute(TTLocalizer.STOREOWNER_GOODBYE, CFSpeech | CFTimeout) + if self.av and self.isLocalToon: + print '-----------ending tailor interaction-----------' + print 'avid: %s, gender: %s' % (self.av.doId, self.av.style.gender) + print 'current top = %s,%s,%s,%s and bot = %s,%s,' % (self.av.style.topTex, + self.av.style.topTexColor, + self.av.style.sleeveTex, + self.av.style.sleeveTexColor, + self.av.style.botTex, + self.av.style.botTexColor) + print 'topsList = %s' % self.av.getClothesTopsList() + print 'bottomsList = %s' % self.av.getClothesBottomsList() + print '-------------------------------------------------' + self.resetTailor() + elif mode == NPCToons.PURCHASE_MOVIE_NO_MONEY: + self.notify.warning('PURCHASE_MOVIE_NO_MONEY should not be called') + self.resetTailor() + return + + def popupPurchaseGUI(self, task): + self.setChatAbsolute('', CFSpeech) + self.purchaseDoneEvent = 'purchaseDone' + self.swapEvent = 'swap' + self.acceptOnce(self.purchaseDoneEvent, self.__handlePurchaseDone) + self.accept(self.swapEvent, self.__handleSwap) + self.clothesGUI = TailorClothesGUI.TailorClothesGUI(self.purchaseDoneEvent, self.swapEvent, self.npcId) + self.clothesGUI.load() + self.clothesGUI.enter(self.av) + self.clothesGUI.showButtons() + self.gui = loader.loadModel('phase_3/models/gui/create_a_toon_gui') + if self.browsing == 0: + self.button = DirectButton(relief=None, image=(self.gui.find('**/CrtAtoon_Btn1_UP'), self.gui.find('**/CrtAtoon_Btn1_DOWN'), self.gui.find('**/CrtAtoon_Btn1_RLLVR')), pos=(-0.15, 0, -0.85), command=self.__handleButton, text=('', TTLocalizer.MakeAToonDone, TTLocalizer.MakeAToonDone), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, -0.03), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + else: + self.button = None + self.cancelButton = DirectButton(relief=None, image=(self.gui.find('**/CrtAtoon_Btn2_UP'), self.gui.find('**/CrtAtoon_Btn2_DOWN'), self.gui.find('**/CrtAtoon_Btn2_RLLVR')), pos=(0.15, 0, -0.85), command=self.__handleCancel, text=('', TTLocalizer.MakeAToonCancel, TTLocalizer.MakeAToonCancel), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, -0.03), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + camera.setPosHpr(base.localAvatar, -4.16, 8.25, 2.47, -152.89, 0.0, 0.0) + self.counter = render.find('**/*mo1_TI_counter') + self.counter.hide() + self.hide() + return Task.done + + def __handleButton(self): + messenger.send('next') + + def __handleCancel(self): + self.clothesGUI.resetClothes(self.oldStyle) + messenger.send('last') + + def __handleSwap(self): + self.d_setDNA(self.av.getStyle().makeNetString(), 0) + + def __handlePurchaseDone(self, timeout = 0): + if self.clothesGUI.doneStatus == 'last' or timeout == 1: + self.d_setDNA(self.oldStyle.makeNetString(), 1) + else: + which = 0 + if self.clothesGUI.topChoice != -1: + which = which | ClosetGlobals.SHIRT + if self.clothesGUI.bottomChoice != -1: + which = which | ClosetGlobals.SHORTS + print 'setDNA: which = %d, top = %d, bot = %d' % (which, self.clothesGUI.topChoice, self.clothesGUI.bottomChoice) + if self.roomAvailable == 0: + if self.isLocalToon: + if self.av.isClosetFull(1) or which & ClosetGlobals.SHIRT and which & ClosetGlobals.SHORTS: + self.__enterConfirmLoss(2, which) + self.clothesGUI.hideButtons() + self.button.hide() + self.cancelButton.hide() + else: + self.d_setDNA(self.av.getStyle().makeNetString(), 2, which) + else: + self.d_setDNA(self.av.getStyle().makeNetString(), 2, which) + + def __enterConfirmLoss(self, finished, which): + if self.popupInfo == None: + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + cancelButtonImage = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.popupInfo = DirectFrame( + parent=hidden, + relief=None, + state='normal', + text=TTLocalizer.STOREOWNER_CONFIRM_LOSS, + text_wordwrap=10, + textMayChange=0, + frameSize=(-1, 1, -1, 1), + text_pos=(0, -0.05), + geom=DGG.getDefaultDialogGeom(), + geom_color=ToontownGlobals.GlobalDialogColor, + geom_scale=(0.88, 1, 0.55), + geom_pos=(0, 0, -.18), + text_scale=0.08) + + DirectButton( + self.popupInfo, + image=okButtonImage, + relief=None, + text=TTLocalizer.STOREOWNER_OK, + text_scale=0.05, + text_pos=(0.0, -0.1), + textMayChange=0, + pos=(-0.08, 0.0, -0.31), + command=self.__handleConfirmLossOK, + extraArgs=[finished, which]) + + DirectButton( + self.popupInfo, + image=cancelButtonImage, + relief=None, + text=TTLocalizer.STOREOWNER_CANCEL, + text_scale=0.05, + text_pos=(0.0, -0.1), + textMayChange=0, + pos=(0.08, 0.0, -0.31), + command=self.__handleConfirmLossCancel) + + buttons.removeNode() + + self.popupInfo.reparentTo(aspect2d) + + def __handleConfirmLossOK(self, finished, which): + self.d_setDNA(self.av.getStyle().makeNetString(), finished, which) + self.popupInfo.reparentTo(hidden) + + def __handleConfirmLossCancel(self): + self.d_setDNA(self.oldStyle.makeNetString(), 1) + self.popupInfo.reparentTo(hidden) + + def d_setDNA(self, dnaString, finished, whichItems = ClosetGlobals.SHIRT | ClosetGlobals.SHORTS): + self.sendUpdate('setDNA', [dnaString, finished, whichItems]) + + def setCustomerDNA(self, avId, dnaString): + if avId != base.localAvatar.doId: + av = base.cr.doId2do.get(avId, None) + if av: + if self.av == av: + oldTorso = self.av.style.torso + self.av.style.makeFromNetString(dnaString) + if len(oldTorso) == 2 and len(self.av.style.torso) == 2 and self.av.style.torso[1] != oldTorso[1]: + self.av.swapToonTorso(self.av.style.torso, genClothes=0) + self.av.loop('neutral', 0) + self.av.generateToonClothes() + return diff --git a/toontown/toon/DistributedNPCTailorAI.py b/toontown/toon/DistributedNPCTailorAI.py new file mode 100755 index 00000000..d4b73de9 --- /dev/null +++ b/toontown/toon/DistributedNPCTailorAI.py @@ -0,0 +1,191 @@ +from otp.ai.AIBaseGlobal import * +from panda3d.core import * +from DistributedNPCToonBaseAI import * +import ToonDNA +from direct.task.Task import Task +from toontown.estate import ClosetGlobals + +class DistributedNPCTailorAI(DistributedNPCToonBaseAI): + freeClothes = simbase.config.GetBool('free-clothes', 0) + housingEnabled = simbase.config.GetBool('want-housing', 1) + useJellybeans = simbase.config.GetBool('want-tailor-jellybeans', False) + + def __init__(self, air, npcId): + DistributedNPCToonBaseAI.__init__(self, air, npcId) + self.timedOut = 0 + self.givesQuests = 0 + self.customerDNA = None + self.customerId = None + self.jbCost = 150 + + if self.freeClothes: + self.useJellybeans = False + + def getTailor(self): + return 1 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + self.ignoreAll() + self.customerDNA = None + self.customerId = None + DistributedNPCToonBaseAI.delete(self) + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.air.doId2do: + self.notify.warning('Avatar: %s not found' % avId) + return + if self.isBusy(): + self.freeAvatar(avId) + return + av = self.air.doId2do[avId] + self.customerDNA = ToonDNA.ToonDNA() + self.customerDNA.makeFromNetString(av.getDNAString()) + self.customerId = avId + av.b_setDNAString(self.customerDNA.makeNetString()) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + + if self.useJellybeans: + flag = NPCToons.PURCHASE_MOVIE_START_BROWSE_JBS + else: + flag = NPCToons.PURCHASE_MOVIE_START_BROWSE + + if self.freeClothes: + flag = NPCToons.PURCHASE_MOVIE_START + elif self.useJellybeans and self.hasEnoughJbs(av): + flag = NPCToons.PURCHASE_MOVIE_START + elif self.air.questManager.hasTailorClothingTicket(av, self): + flag = NPCToons.PURCHASE_MOVIE_START + + if self.housingEnabled and self.isClosetAlmostFull(av): + flag = NPCToons.PURCHASE_MOVIE_START_NOROOM + + self.sendShoppingMovie(avId, flag) + DistributedNPCToonBaseAI.avatarEnter(self) + + def isClosetAlmostFull(self, av): + numClothes = len(av.clothesTopsList) / 4 + len(av.clothesBottomsList) / 2 + return numClothes >= av.maxClothes - 1 + + def hasEnoughJbs(self, av): + return av.getTotalMoney() >= self.jbCost + + def sendShoppingMovie(self, avId, flag): + self.busy = avId + self.sendUpdate('setMovie', [flag, + self.npcId, + avId, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + taskMgr.doMethodLater(NPCToons.TAILOR_COUNTDOWN_TIME, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def rejectAvatar(self, avId): + self.notify.warning('rejectAvatar: should not be called by a Tailor!') + + def sendTimeoutMovie(self, task): + toon = self.air.doId2do.get(self.customerId) + if toon != None and self.customerDNA: + toon.b_setDNAString(self.customerDNA.makeNetString()) + self.timedOut = 1 + self.sendUpdate('setMovie', [NPCToons.PURCHASE_MOVIE_TIMEOUT, + self.npcId, + self.busy, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendClearMovie(None) + return Task.done + + def sendClearMovie(self, task): + self.ignore(self.air.getAvatarExitEvent(self.busy)) + self.customerDNA = None + self.customerId = None + self.busy = 0 + self.timedOut = 0 + self.sendUpdate('setMovie', [NPCToons.PURCHASE_MOVIE_CLEAR, + self.npcId, + 0, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendUpdate('setCustomerDNA', [0, '']) + return Task.done + + def completePurchase(self, avId): + av = self.air.doId2do[avId] + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.PURCHASE_MOVIE_COMPLETE, + self.npcId, + avId, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendClearMovie(None) + if self.air.questManager.hasTailorClothingTicket(av, self): + self.air.questManager.removeClothingTicket(av, self) + + def setDNA(self, blob, finished, which): + avId = self.air.getAvatarIdFromSender() + if avId != self.customerId: + if self.customerId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCTailorAI.setDNA customer is %s' % self.customerId) + self.notify.warning('customerId: %s, but got setDNA for: %s' % (self.customerId, avId)) + return + testDNA = ToonDNA.ToonDNA() + if not testDNA.isValidNetString(blob): + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCTailorAI.setDNA: invalid dna: %s' % blob) + return + if avId in self.air.doId2do: + av = self.air.doId2do.get(avId) + if finished == 2 and which > 0: + if self.freeClothes or av.takeMoney(self.jbCost, bUseBank = True): + av.b_setDNAString(blob) + if which & ClosetGlobals.SHIRT: + if av.addToClothesTopsList(self.customerDNA.topTex, self.customerDNA.topTexColor, self.customerDNA.sleeveTex, self.customerDNA.sleeveTexColor) == 1: + av.b_setClothesTopsList(av.getClothesTopsList()) + else: + self.notify.warning('NPCTailor: setDNA() - unable to save old tops - we exceeded the tops list length') + if which & ClosetGlobals.SHORTS: + if av.addToClothesBottomsList(self.customerDNA.botTex, self.customerDNA.botTexColor) == 1: + av.b_setClothesBottomsList(av.getClothesBottomsList()) + else: + self.notify.warning('NPCTailor: setDNA() - unable to save old bottoms - we exceeded the bottoms list length') + self.air.writeServerEvent('boughtTailorClothes', avId, '%s|%s|%s' % (self.doId, which, self.customerDNA.asTuple())) + elif self.useJellybeans: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCTailorAI.setDNA tried to purchase with insufficient jellybeans') + self.notify.warning('NPCTailor: setDNA() - client tried to purchase with insufficient jellybeans!') + elif self.air.questManager.hasTailorClothingTicket(av, self): + self.air.questManager.removeClothingTicket(av, self) + av.b_setDNAString(blob) + if which & ClosetGlobals.SHIRT: + if av.addToClothesTopsList(self.customerDNA.topTex, self.customerDNA.topTexColor, self.customerDNA.sleeveTex, self.customerDNA.sleeveTexColor) == 1: + av.b_setClothesTopsList(av.getClothesTopsList()) + else: + self.notify.warning('NPCTailor: setDNA() - unable to save old tops - we exceeded the tops list length') + if which & ClosetGlobals.SHORTS: + if av.addToClothesBottomsList(self.customerDNA.botTex, self.customerDNA.botTexColor) == 1: + av.b_setClothesBottomsList(av.getClothesBottomsList()) + else: + self.notify.warning('NPCTailor: setDNA() - unable to save old bottoms - we exceeded the bottoms list length') + self.air.writeServerEvent('boughtTailorClothes', avId, '%s|%s|%s' % (self.doId, which, self.customerDNA.asTuple())) + elif finished == 1: + if self.customerDNA: + av.b_setDNAString(self.customerDNA.makeNetString()) + else: + self.sendUpdate('setCustomerDNA', [avId, blob]) + else: + self.notify.warning('no av for avId: %d' % avId) + if self.timedOut == 1 or finished == 0: + return + if self.busy == avId: + taskMgr.remove(self.uniqueName('clearMovie')) + self.completePurchase(avId) + elif self.busy: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCTailorAI.setDNA busy with %s' % self.busy) + self.notify.warning('setDNA from unknown avId: %s busy: %s' % (avId, self.busy)) + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + if self.customerId == avId: + dna = self.customerDNA.makeNetString() + self.air.dbInterface.updateObject(self.air.dbId, avId, self.air.dclassesByName['DistributedToonAI'], {'setDNAString': [dna]}) + else: + self.notify.warning('invalid customer avId: %s, customerId: %s ' % (avId, self.customerId)) + if self.busy == avId: + self.sendClearMovie(None) + else: + self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy)) diff --git a/toontown/toon/DistributedNPCToon.py b/toontown/toon/DistributedNPCToon.py new file mode 100755 index 00000000..e27fef44 --- /dev/null +++ b/toontown/toon/DistributedNPCToon.py @@ -0,0 +1,224 @@ +from direct.interval.IntervalGlobal import * +from panda3d.core import * +import time +from DistributedNPCToonBase import * +from toontown.hood import ZoneUtil +from otp.nametag.NametagConstants import * +from toontown.quest import QuestChoiceGui +from toontown.quest import QuestParser +from toontown.quest import TrackChoiceGui +from toontown.toonbase import TTLocalizer + + +ChoiceTimeout = 20 + + +class DistributedNPCToon(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + + self.curQuestMovie = None + self.questChoiceGui = None + self.trackChoiceGui = None + + def allowedToTalk(self): + return True + + def delayDelete(self): + DistributedNPCToonBase.delayDelete(self) + + if self.curQuestMovie: + curQuestMovie = self.curQuestMovie + self.curQuestMovie = None + curQuestMovie.timeout(fFinish=1) + curQuestMovie.cleanup() + + def disable(self): + self.cleanupMovie() + + DistributedNPCToonBase.disable(self) + + def cleanupMovie(self): + self.clearChat() + self.ignore('chooseQuest') + if self.questChoiceGui: + self.questChoiceGui.destroy() + self.questChoiceGui = None + self.ignore(self.uniqueName('doneChatPage')) + if self.curQuestMovie: + self.curQuestMovie.timeout(fFinish=1) + self.curQuestMovie.cleanup() + self.curQuestMovie = None + if self.trackChoiceGui: + self.trackChoiceGui.destroy() + self.trackChoiceGui = None + + def handleCollisionSphereEnter(self, collEntry): + base.cr.playGame.getPlace().fsm.request('quest', [self]) + self.sendUpdate('avatarEnter', []) + self.nametag3d.setDepthTest(0) + self.nametag3d.setBin('fixed', 0) + + def finishMovie(self, av, isLocalToon, elapsedTime): + self.cleanupMovie() + av.startLookAround() + self.startLookAround() + self.detectAvatars() + self.initPos() + if isLocalToon: + self.showNametag2d() + taskMgr.remove(self.uniqueName('lerpCamera')) + self.neutralizeCamera() + self.sendUpdate('setMovieDone', []) + self.nametag3d.clearDepthTest() + self.nametag3d.clearBin() + + def neutralizeCamera(self): + avHeight = max(base.localAvatar.getHeight(), 3.0) + scaleFactor = avHeight * 0.3333333333 + camera.wrtReparentTo(base.localAvatar) + camera.posQuatInterval(1, (0, -9 * scaleFactor, avHeight), (0, 0, 0), other=base.localAvatar, blendType='easeOut').start() + def walk(): + base.cr.playGame.getPlace().setState('walk') + Sequence(Wait(1), Func(walk)).start() + + def setupCamera(self, mode): + camera.wrtReparentTo(render) + if mode == NPCToons.QUEST_MOVIE_QUEST_CHOICE or mode == NPCToons.QUEST_MOVIE_TRACK_CHOICE: + camera.posQuatInterval(1, (5, 9, self.getHeight() - 0.5), (155, -2, 0), other=self, blendType='easeOut').start() + else: + camera.posQuatInterval(1, (-5, 9, self.getHeight() - 0.5), (-150, -2, 0), other=self, blendType='easeOut').start() + + def setMovie(self, mode, npcId, avId, quests, timestamp): + isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.QUEST_MOVIE_CLEAR: + self.cleanupMovie() + if isLocalToon: + self.neutralizeCamera() + return + if mode == NPCToons.QUEST_MOVIE_TIMEOUT: + self.cleanupMovie() + if isLocalToon: + self.neutralizeCamera() + self.setPageNumber(0, -1) + self.clearChat() + self.startLookAround() + self.detectAvatars() + return + av = base.cr.doId2do.get(avId) + if av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + if mode == NPCToons.QUEST_MOVIE_REJECT: + rejectString = Quests.chooseQuestDialogReject() + rejectString = Quests.fillInQuestNames(rejectString, avName=av.name) + self.setChatAbsolute(rejectString, CFSpeech | CFTimeout) + if isLocalToon: + base.cr.playGame.getPlace().setState('walk') + return + if mode == NPCToons.QUEST_MOVIE_TIER_NOT_DONE: + rejectString = Quests.chooseQuestDialogTierNotDone() + rejectString = Quests.fillInQuestNames(rejectString, avName=av.name) + self.setChatAbsolute(rejectString, CFSpeech | CFTimeout) + if isLocalToon: + self.neutralizeCamera() + return + self.setupAvatars(av) + fullString = '' + toNpcId = None + if isLocalToon: + self.hideNametag2d() + if mode == NPCToons.QUEST_MOVIE_COMPLETE: + questId, rewardId, toNpcId = quests + scriptId = 'quest_complete_' + str(questId) + if QuestParser.questDefined(scriptId): + self.curQuestMovie = QuestParser.NPCMoviePlayer(scriptId, av, self) + self.curQuestMovie.play() + return + if isLocalToon: + self.setupCamera(mode) + greetingString = Quests.chooseQuestDialog(questId, Quests.GREETING) + if greetingString: + fullString += greetingString + '\x07' + fullString += Quests.chooseQuestDialog(questId, Quests.COMPLETE) + '\x07' + if rewardId: + fullString += Quests.getReward(rewardId).getString() + leavingString = Quests.chooseQuestDialog(questId, Quests.LEAVING) + if leavingString: + fullString += '\x07' + leavingString + elif mode == NPCToons.QUEST_MOVIE_QUEST_CHOICE_CANCEL: + fullString = TTLocalizer.QuestMovieQuestChoiceCancel + elif mode == NPCToons.QUEST_MOVIE_TRACK_CHOICE_CANCEL: + fullString = TTLocalizer.QuestMovieTrackChoiceCancel + elif mode == NPCToons.QUEST_MOVIE_INCOMPLETE: + questId, completeStatus, toNpcId = quests + scriptId = 'quest_incomplete_' + str(questId) + if QuestParser.questDefined(scriptId): + if self.curQuestMovie: + self.curQuestMovie.timeout() + self.curQuestMovie.cleanup() + self.curQuestMovie = None + self.curQuestMovie = QuestParser.NPCMoviePlayer(scriptId, av, self) + self.curQuestMovie.play() + return + if isLocalToon: + self.setupCamera(mode) + greetingString = Quests.chooseQuestDialog(questId, Quests.GREETING) + if greetingString: + fullString += greetingString + '\x07' + fullString += Quests.chooseQuestDialog(questId, completeStatus) + leavingString = Quests.chooseQuestDialog(questId, Quests.LEAVING) + if leavingString: + fullString += '\x07' + leavingString + elif mode == NPCToons.QUEST_MOVIE_ASSIGN: + questId, rewardId, toNpcId = quests + scriptId = 'quest_assign_' + str(questId) + if QuestParser.questDefined(scriptId): + if self.curQuestMovie: + self.curQuestMovie.timeout() + self.curQuestMovie.cleanup() + self.curQuestMovie = None + self.curQuestMovie = QuestParser.NPCMoviePlayer(scriptId, av, self) + self.curQuestMovie.play() + return + if isLocalToon: + self.setupCamera(mode) + fullString += Quests.chooseQuestDialog(questId, Quests.QUEST) + leavingString = Quests.chooseQuestDialog(questId, Quests.LEAVING) + if leavingString: + fullString += '\x07' + leavingString + elif mode == NPCToons.QUEST_MOVIE_QUEST_CHOICE: + if isLocalToon: + self.setupCamera(mode) + self.setChatAbsolute(TTLocalizer.QuestMovieQuestChoice, CFSpeech) + if isLocalToon: + self.acceptOnce('chooseQuest', self.sendChooseQuest) + self.questChoiceGui = QuestChoiceGui.QuestChoiceGui() + self.questChoiceGui.setQuests(quests, npcId, ChoiceTimeout) + return + elif mode == NPCToons.QUEST_MOVIE_TRACK_CHOICE: + if isLocalToon: + self.setupCamera(mode) + tracks = quests + self.setChatAbsolute(TTLocalizer.QuestMovieTrackChoice, CFSpeech) + if isLocalToon: + self.acceptOnce('chooseTrack', self.sendChooseTrack) + self.trackChoiceGui = TrackChoiceGui.TrackChoiceGui(tracks, ChoiceTimeout) + return + fullString = Quests.fillInQuestNames(fullString, avName=av.name, fromNpcId=npcId, toNpcId=toNpcId) + self.acceptOnce(self.uniqueName('doneChatPage'), self.finishMovie, extraArgs=[av, isLocalToon]) + self.clearChat() + self.setPageChat(avId, 0, fullString, 1) + + def sendChooseQuest(self, questId): + if self.questChoiceGui: + self.questChoiceGui.destroy() + self.questChoiceGui = None + self.sendUpdate('chooseQuest', [questId]) + + def sendChooseTrack(self, trackId): + if self.trackChoiceGui: + self.trackChoiceGui.destroy() + self.trackChoiceGui = None + self.sendUpdate('chooseTrack', [trackId]) diff --git a/toontown/toon/DistributedNPCToonAI.py b/toontown/toon/DistributedNPCToonAI.py new file mode 100755 index 00000000..33319139 --- /dev/null +++ b/toontown/toon/DistributedNPCToonAI.py @@ -0,0 +1,276 @@ +from otp.ai.AIBaseGlobal import * +from direct.task.Task import Task +from panda3d.core import * +from DistributedNPCToonBaseAI import * +from toontown.quest import Quests + +class DistributedNPCToonAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId, questCallback = None, hq = 0): + DistributedNPCToonBaseAI.__init__(self, air, npcId, questCallback) + self.hq = hq + self.tutorial = 0 + self.pendingAvId = None + self.task = None + return + + def getTutorial(self): + return self.tutorial + + def setTutorial(self, val): + self.tutorial = val + + def getHq(self): + return self.hq + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if self.isBusy(): + self.freeAvatar(avId) + return + self.busy = avId + self.air.questManager.requestInteract(avId, self) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + + self.clearTasks() + self.task = self.uniqueName('clearMovie') + taskMgr.doMethodLater(20, self.sendTimeoutMovie, self.task) + DistributedNPCToonBaseAI.avatarEnter(self) + + def chooseQuest(self, questId): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('chooseQuest: avatar %s choseQuest %s' % (avId, questId)) + if not self.pendingAvId: + self.notify.warning('chooseQuest: not expecting an answer from any avatar: %s' % avId) + return + if self.pendingAvId != avId: + self.notify.warning('chooseQuest: not expecting an answer from this avatar: %s' % avId) + return + if self.pendingQuests is None: + self.notify.warning('chooseQuest: not expecting a quest choice from this avatar: %s' % avId) + self.air.writeServerEvent('suspicious', avId, 'unexpected chooseQuest') + return + if questId == 0: + self.pendingAvId = None + self.pendingQuests = None + self.air.questManager.avatarCancelled(self) + self.cancelChoseQuest(avId) + return + if questId == 401: + av = self.air.getDo(avId) + if not av: + self.notify.warning('chooseQuest: av not present: %s' % avId) + return + for quest in self.pendingQuests: + if questId == quest[0]: + self.pendingAvId = None + self.pendingQuests = None + self.air.questManager.avatarChoseQuest(avId, self, *quest) + return + self.notify.warning('chooseQuest: avatar: %s chose a quest not offered: %s' % (avId, questId)) + self.pendingAvId = None + self.pendingQuests = None + return + + def chooseTrack(self, trackId): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('chooseTrack: avatar %s choseTrack %s' % (avId, trackId)) + if not self.pendingAvId: + self.notify.warning('chooseTrack: not expecting an answer from any avatar: %s' % avId) + return + if self.pendingAvId != avId: + self.notify.warning('chooseTrack: not expecting an answer from this avatar: %s' % avId) + return + if self.pendingTracks is None: + self.notify.warning('chooseTrack: not expecting a track choice from this avatar: %s' % avId) + self.air.writeServerEvent('suspicious', avId, 'unexpected chooseTrack') + return + if trackId == -1: + self.pendingAvId = None + self.pendingTracks = None + self.pendingTrackQuest = None + self.air.questManager.avatarCancelled(avId) + self.cancelChoseTrack(avId) + return + for track in self.pendingTracks: + if trackId == track: + self.air.questManager.avatarChoseTrack(avId, self, self.pendingTrackQuest, trackId) + self.pendingAvId = None + self.pendingTracks = None + self.pendingTrackQuest = None + return + + self.notify.warning('chooseTrack: avatar: %s chose a track not offered: %s' % (avId, trackId)) + self.pendingAvId = None + self.pendingTracks = None + self.pendingTrackQuest = None + return + + def sendTimeoutMovie(self, task): + self.pendingAvId = None + self.pendingQuests = None + self.pendingTracks = None + self.pendingTrackQuest = None + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TIMEOUT, + self.npcId, + self.busy, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendClearMovie(None) + self.busy = 0 + return Task.done + + def sendClearMovie(self, task): + self.pendingAvId = None + self.pendingQuests = None + self.pendingTracks = None + self.pendingTrackQuest = None + self.busy = 0 + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_CLEAR, + self.npcId, + 0, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + return Task.done + + def rejectAvatar(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_REJECT, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(5.5, self.sendClearMovie, self.uniqueName('clearMovie')) + + def rejectAvatarTierNotDone(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TIER_NOT_DONE, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + self.clearTasks() + self.task = self.uniqueName('clearMovie') + taskMgr.doMethodLater(5.5, self.sendClearMovie, self.task) + + def completeQuest(self, avId, questId, rewardId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_COMPLETE, + self.npcId, + avId, + [questId, rewardId, 0], + ClockDelta.globalClockDelta.getRealNetworkTime(bits=16)]) + if not self.tutorial: + self.clearTasks() + self.task = self.uniqueName('clearMovie') + taskMgr.doMethodLater(540.0, self.sendTimeoutMovie, self.task) + + def incompleteQuest(self, avId, questId, completeStatus, toNpcId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_INCOMPLETE, + self.npcId, + avId, + [questId, completeStatus, toNpcId], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + self.clearTasks() + self.task = self.uniqueName('clearMovie') + taskMgr.doMethodLater(540.0, self.sendTimeoutMovie, self.task) + + def assignQuest(self, avId, questId, rewardId, toNpcId): + self.busy = avId + if self.questCallback: + self.questCallback() + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_ASSIGN, + self.npcId, + avId, + [questId, rewardId, toNpcId], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + self.clearTasks() + self.task = self.uniqueName('clearMovie') + taskMgr.doMethodLater(540.0, self.sendTimeoutMovie, self.task) + + def presentQuestChoice(self, avId, quests): + self.busy = avId + self.pendingAvId = avId + self.pendingQuests = quests + flatQuests = [] + for quest in quests: + flatQuests.extend(quest) + + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_QUEST_CHOICE, + self.npcId, + avId, + flatQuests, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + self.clearTasks() + self.task = self.uniqueName('clearMovie') + taskMgr.doMethodLater(20.0, self.sendTimeoutMovie, self.task) + + def presentTrackChoice(self, avId, questId, tracks): + self.busy = avId + self.pendingAvId = avId + self.pendingTracks = tracks + self.pendingTrackQuest = questId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TRACK_CHOICE, + self.npcId, + avId, + tracks, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + self.clearTasks() + self.task = self.uniqueName('clearMovie') + taskMgr.doMethodLater(20.0, self.sendTimeoutMovie, self.task) + + def cancelChoseQuest(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_QUEST_CHOICE_CANCEL, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + self.clearTasks() + self.task = self.uniqueName('clearMovie') + taskMgr.doMethodLater(NPCToons.QUEST_COUNTDOWN_TIME, self.sendTimeoutMovie, self.task) + + def cancelChoseTrack(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TRACK_CHOICE_CANCEL, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + self.clearTasks() + self.task = self.uniqueName('clearMovie') + taskMgr.doMethodLater(NPCToons.QUEST_COUNTDOWN_TIME, self.sendTimeoutMovie, self.task) + + def setMovieDone(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setMovieDone busy: %s avId: %s' % (self.busy, avId)) + if self.busy == avId: + self.clearTasks() + self.sendClearMovie(None) + elif self.busy: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCToonAI.setMovieDone busy with %s' % self.busy) + self.notify.warning('somebody called setMovieDone that I was not busy with! avId: %s' % avId) + return + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy)) + self.clearTasks() + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie(None) + return + + def clearTasks(self): + if self.task: + taskMgr.remove(self.task) + + self.task = None diff --git a/toontown/toon/DistributedNPCToonBase.py b/toontown/toon/DistributedNPCToonBase.py new file mode 100755 index 00000000..c04c9e50 --- /dev/null +++ b/toontown/toon/DistributedNPCToonBase.py @@ -0,0 +1,137 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from direct.distributed import DistributedObject +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.interval.IntervalGlobal import * +from panda3d.core import * +import random + +import DistributedToon +import NPCToons +from otp.nametag.NametagGroup import NametagGroup +from toontown.quest import QuestChoiceGui +from toontown.quest import QuestParser +from toontown.quest import Quests +from toontown.toonbase import ToontownGlobals + + +class DistributedNPCToonBase(DistributedToon.DistributedToon): + + def __init__(self, cr): + try: + self.DistributedNPCToon_initialized + except: + self.DistributedNPCToon_initialized = 1 + DistributedToon.DistributedToon.__init__(self, cr) + self.__initCollisions() + self.setPickable(0) + self.setPlayerType(NametagGroup.CCNonPlayer) + + def disable(self): + self.ignore('enter' + self.cSphereNode.getName()) + DistributedToon.DistributedToon.disable(self) + + def delete(self): + try: + self.DistributedNPCToon_deleted + except: + self.DistributedNPCToon_deleted = 1 + self.__deleteCollisions() + DistributedToon.DistributedToon.delete(self) + + def generate(self): + DistributedToon.DistributedToon.generate(self) + self.cSphereNode.setName(self.uniqueName('NPCToon')) + self.detectAvatars() + self.setParent(ToontownGlobals.SPRender) + self.startLookAround() + + def generateToon(self): + self.setLODs() + self.generateToonLegs() + self.generateToonHead() + self.generateToonTorso() + self.generateToonColor() + self.parentToonParts() + self.rescaleToon() + self.resetHeight() + self.rightHands = [] + self.leftHands = [] + self.headParts = [] + self.hipsParts = [] + self.torsoParts = [] + self.legsParts = [] + self.__bookActors = [] + self.__holeActors = [] + + def announceGenerate(self): + self.initToonState() + DistributedToon.DistributedToon.announceGenerate(self) + + def initToonState(self): + self.setAnimState('neutral', 0.9, None, None) + npcOrigin = render.find('**/npc_origin_' + str(self.posIndex)) + if not npcOrigin.isEmpty(): + self.reparentTo(npcOrigin) + self.initPos() + + def initPos(self): + self.clearMat() + + def wantsSmoothing(self): + return 0 + + def detectAvatars(self): + self.accept('enter' + self.cSphereNode.getName(), self.handleCollisionSphereEnter) + + def ignoreAvatars(self): + self.ignore('enter' + self.cSphereNode.getName()) + + def getCollSphereRadius(self): + return 3.25 + + def __initCollisions(self): + self.cSphere = CollisionTube(0.0, 1.0, 0.0, 0.0, 1.0, 5.0, self.getCollSphereRadius()) + self.cSphere.setTangible(0) + self.cSphereNode = CollisionNode('cSphereNode') + self.cSphereNode.addSolid(self.cSphere) + self.cSphereNodePath = self.attachNewNode(self.cSphereNode) + self.cSphereNodePath.hide() + self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + + def __deleteCollisions(self): + del self.cSphere + del self.cSphereNode + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + + def handleCollisionSphereEnter(self, collEntry): + pass + + def setupAvatars(self, av): + self.ignoreAvatars() + self.lookAtAvatar(av) + + def lookAtAvatar(self, av): + av.headsUp(self, 0, 0, 0) + self.headsUp(av, 0, 0, 0) + av.stopLookAround() + av.lerpLookAt(Point3(-0.5, 4, 0), time=0.5) + self.stopLookAround() + self.lerpLookAt(Point3(av.getPos(self)), time=0.5) + + def b_setPageNumber(self, paragraph, pageNumber): + self.setPageNumber(paragraph, pageNumber) + self.d_setPageNumber(paragraph, pageNumber) + + def d_setPageNumber(self, paragraph, pageNumber): + timestamp = ClockDelta.globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setPageNumber', [paragraph, pageNumber, timestamp]) + + def freeAvatar(self): + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + + def setPositionIndex(self, posIndex): + self.posIndex = posIndex diff --git a/toontown/toon/DistributedNPCToonBaseAI.py b/toontown/toon/DistributedNPCToonBaseAI.py new file mode 100755 index 00000000..14010eca --- /dev/null +++ b/toontown/toon/DistributedNPCToonBaseAI.py @@ -0,0 +1,66 @@ +from otp.ai.AIBaseGlobal import * +from panda3d.core import * +import DistributedToonAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.distributed import ClockDelta +from toontown.toonbase import ToontownGlobals +import NPCToons +from direct.task import Task +from toontown.quest import Quests + +class DistributedNPCToonBaseAI(DistributedToonAI.DistributedToonAI): + + def __init__(self, air, npcId, questCallback = None): + DistributedToonAI.DistributedToonAI.__init__(self, air) + self.air = air + self.npcId = npcId + self.busy = 0 + self.questCallback = questCallback + self.givesQuests = 1 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + DistributedToonAI.DistributedToonAI.delete(self) + + def _doPlayerEnter(self): + pass + + def _doPlayerExit(self): + pass + + def _announceArrival(self): + pass + + def isPlayerControlled(self): + return False + + def getHq(self): + return 0 + + def getTailor(self): + return 0 + + def getGivesQuests(self): + return self.givesQuests + + def avatarEnter(self): + pass + + def isBusy(self): + return self.busy > 0 + + def getNpcId(self): + return self.npcId + + def freeAvatar(self, avId): + self.sendUpdateToAvatarId(avId, 'freeAvatar', []) + + def setPositionIndex(self, posIndex): + self.posIndex = posIndex + + def getPositionIndex(self): + return self.posIndex + + def getStartAnimState(self): + return 'neutral' diff --git a/toontown/toon/DistributedToon.py b/toontown/toon/DistributedToon.py new file mode 100755 index 00000000..794a85fe --- /dev/null +++ b/toontown/toon/DistributedToon.py @@ -0,0 +1,2486 @@ +import copy +from direct.controls.GravityWalker import GravityWalker +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.distributed import DistributedSmoothNode +from direct.distributed.ClockDelta import * +from direct.distributed.MsgTypes import * +from direct.fsm import ClassicFSM +from direct.interval.IntervalGlobal import Sequence, Wait, Func, Parallel, SoundInterval +from direct.showbase import PythonUtil +from direct.task.Task import Task +import operator +from panda3d.core import * +import random +import time +import Experience +import InventoryNew +import TTEmote +import Toon +from otp.ai.MagicWordGlobal import * +from otp.avatar import Avatar, DistributedAvatar +from otp.avatar import DistributedPlayer +from otp.chat import TalkAssistant, ChatUtil +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPLocalizer +from otp.speedchat import SCDecoders +from toontown.catalog import CatalogItem +from toontown.catalog import CatalogItemList +from toontown.chat import ResistanceChat +from toontown.chat import ToonChatGarbler +from otp.nametag.NametagConstants import * +from otp.margins.WhisperPopup import * +from toontown.coghq import CogDisguiseGlobals +from toontown.distributed import DelayDelete +from toontown.distributed import DelayDelete +from toontown.distributed.DelayDeletable import DelayDeletable +from toontown.effects.ScavengerHuntEffects import * +from toontown.estate import DistributedGagTree +from toontown.estate import FlowerBasket +from toontown.estate import FlowerCollection +from toontown.estate import GardenDropGame +from toontown.estate import GardenGlobals +from toontown.fishing import FishCollection +from toontown.fishing import FishTank +from toontown.friends import FriendHandle +from toontown.golf import GolfGlobals +from toontown.hood import ZoneUtil +from otp.nametag import NametagGroup +from otp.nametag.NametagGroup import * +from toontown.parties import PartyGlobals +from toontown.parties.InviteInfo import InviteInfo +from toontown.parties.PartyGlobals import InviteStatus, PartyStatus +from toontown.parties.PartyInfo import PartyInfo +from toontown.parties.PartyReplyInfo import PartyReplyInfoBase +from toontown.parties.SimpleMailBase import SimpleMailBase +from toontown.shtiker.OptionsPage import speedChatStyles +from toontown.speedchat import TTSCDecoders +from toontown.suit import SuitDNA +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.battle import BattleParticles + +if base.wantKarts: + from toontown.racing.KartDNA import * + +class DistributedToon(DistributedPlayer.DistributedPlayer, Toon.Toon, DistributedSmoothNode.DistributedSmoothNode, DelayDeletable): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedToon') + partyNotify = DirectNotifyGlobal.directNotify.newCategory('DistributedToon_Party') + chatGarbler = ToonChatGarbler.ToonChatGarbler() + + def __init__(self, cr, bFake = False): + try: + self.DistributedToon_initialized + return + except: + self.DistributedToon_initialized = 1 + + DistributedPlayer.DistributedPlayer.__init__(self, cr) + Toon.Toon.__init__(self) + DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr) + self.bFake = bFake + self.kart = None + self.trophyScore = 0 + self.trophyStar = None + self.trophyStarSpeed = 0 + self.NPCFriendsDict = {} + self.earnedExperience = None + self.track = None + self.effect = None + self.maxCarry = 0 + self.disguisePageFlag = 0 + self.sosPageFlag = 0 + self.disguisePage = None + self.sosPage = None + self.gardenPage = None + self.emoteAccess = [0] * 27 + self.cogTypes = [0, 0, 0, 0] + self.cogLevels = [0, 0, 0, 0] + self.cogParts = [0, 0, 0, 0] + self.cogMerits = [0, 0, 0, 0] + self.savedCheesyEffect = ToontownGlobals.CENormal + self.savedCheesyHoodId = 0 + self.savedCheesyExpireTime = 0 + if hasattr(base, 'wantPets') and base.wantPets: + self.petTrickPhrases = [] + self.customMessages = [] + self.resistanceMessages = [] + self.cogSummonsEarned = [] + self.catalogNotify = ToontownGlobals.NoItems + self.mailboxNotify = ToontownGlobals.NoItems + self.simpleMailNotify = ToontownGlobals.NoItems + self.inviteMailNotify = ToontownGlobals.NoItems + self.catalogScheduleCurrentWeek = 0 + self.catalogScheduleNextTime = 0 + self.monthlyCatalog = CatalogItemList.CatalogItemList() + self.weeklyCatalog = CatalogItemList.CatalogItemList() + self.backCatalog = CatalogItemList.CatalogItemList() + self.onOrder = CatalogItemList.CatalogItemList(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + self.onGiftOrder = CatalogItemList.CatalogItemList(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + self.mailboxContents = CatalogItemList.CatalogItemList(store=CatalogItem.Customization) + self.awardMailboxContents = CatalogItemList.CatalogItemList(store=CatalogItem.Customization) + self.onAwardOrder = CatalogItemList.CatalogItemList(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + self.splash = None + self.tossTrack = None + self.pieTracks = {} + self.splatTracks = {} + self.lastTossedPie = 0 + self.clothesTopsList = [] + self.clothesBottomsList = [] + self.hatList = [] + self.glassesList = [] + self.backpackList = [] + self.shoesList = [] + self.tunnelTrack = None + self.tunnelPivotPos = [-14, -6, 0] + self.tunnelCenterOffset = 9.0 + self.tunnelCenterInfluence = 0.6 + self.pivotAngle = 90 + 45 + self.houseId = 0 + self.money = 0 + self.bankMoney = 0 + self.maxMoney = 0 + self.maxBankMoney = 0 + self.emblems = [0, 0] + self.petId = 0 + self.petTutorialDone = False + self.fishBingoTutorialDone = False + self.fishBingoMarkTutorialDone = False + self.accessories = [] + if base.wantKarts: + self.kartDNA = [-1] * getNumFields() + self.flowerCollection = None + self.shovel = 0 + self.shovelSkill = 0 + self.shovelModel = None + self.wateringCan = 0 + self.wateringCanSkill = 0 + self.wateringCanModel = None + self.gardenSpecials = [] + self.unlimitedSwing = 0 + self.soundSequenceList = [] + self.boardingParty = None + self.__currentDialogue = None + self.mail = None + self.invites = [] + self.hostedParties = [] + self.partiesInvitedTo = [] + self.partyReplyInfoBases = [] + self.buffs = [] + self.redeemedCodes = [] + self.ignored = [] + self.reported = [] + self.trueFriends = [] + self.specialInventory = [0, 0, 0, 0, 0] + + def disable(self): + for soundSequence in self.soundSequenceList: + soundSequence.finish() + + self.soundSequenceList = [] + if self.boardingParty: + self.boardingParty.demandDrop() + self.boardingParty = None + self.ignore('clientCleanup') + self.stopAnimations() + self.clearCheesyEffect() + self.stopBlink() + self.stopSmooth() + self.stopLookAroundNow() + self.setGhostMode(0) + if self.track != None: + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + if self.effect != None: + self.effect.destroy() + self.effect = None + if self.splash != None: + self.splash.destroy() + self.splash = None + if self.emote != None: + self.emote.finish() + self.emote = None + self.cleanupPies() + if self.isDisguised: + self.takeOffSuit() + if self.tunnelTrack: + self.tunnelTrack.finish() + self.tunnelTrack = None + self.setTrophyScore(0) + if self.doId in self.cr.toons: + del self.cr.toons[self.doId] + DistributedPlayer.DistributedPlayer.disable(self) + + def delete(self): + try: + self.DistributedToon_deleted + except: + self.DistributedToon_deleted = 1 + DistributedPlayer.DistributedPlayer.delete(self) + Toon.Toon.delete(self) + DistributedSmoothNode.DistributedSmoothNode.delete(self) + + def generate(self): + DistributedPlayer.DistributedPlayer.generate(self) + DistributedSmoothNode.DistributedSmoothNode.generate(self) + self.cr.toons[self.doId] = self + if base.cr.trophyManager != None: + base.cr.trophyManager.d_requestTrophyScore() + self.startBlink() + self.startSmooth() + self.accept('clientCleanup', self._handleClientCleanup) + + def announceGenerate(self): + DistributedPlayer.DistributedPlayer.announceGenerate(self) + if self.animFSM.getCurrentState().getName() == 'off': + self.setAnimState('neutral') + + def _handleClientCleanup(self): + if self.track != None: + DelayDelete.cleanupDelayDeletes(self.track) + + def setDNAString(self, dnaString): + Toon.Toon.setDNAString(self, dnaString) + + def setAdminAccess(self, access): + DistributedPlayer.DistributedPlayer.setAdminAccess(self, access) + self.removeGMIcon() + if self.isAdmin(): + self.setGMIcon(access) + + def setDNA(self, dna): + oldHat = self.getHat() + oldGlasses = self.getGlasses() + oldBackpack = self.getBackpack() + oldShoes = self.getShoes() + self.setHat(0, 0, 0) + self.setGlasses(0, 0, 0) + self.setBackpack(0, 0, 0) + self.setShoes(0, 0, 0) + Toon.Toon.setDNA(self, dna) + self.setHat(*oldHat) + self.setGlasses(*oldGlasses) + self.setBackpack(*oldBackpack) + self.setShoes(*oldShoes) + + def setHat(self, idx, textureIdx, colorIdx): + Toon.Toon.setHat(self, idx, textureIdx, colorIdx) + + def setGlasses(self, idx, textureIdx, colorIdx): + Toon.Toon.setGlasses(self, idx, textureIdx, colorIdx) + + def setBackpack(self, idx, textureIdx, colorIdx): + Toon.Toon.setBackpack(self, idx, textureIdx, colorIdx) + + def setShoes(self, idx, textureIdx, colorIdx): + Toon.Toon.setShoes(self, idx, textureIdx, colorIdx) + + def setExperience(self, experience): + self.experience = Experience.Experience(experience, self) + if self.inventory: + self.inventory.updateGUI() + + def setInventory(self, inventoryNetString): + if not self.inventory: + self.inventory = InventoryNew.InventoryNew(self, inventoryNetString) + self.inventory.updateInvString(inventoryNetString) + + def setLastHood(self, lastHood): + self.lastHood = lastHood + + def setBattleId(self, battleId): + self.battleId = battleId + messenger.send('ToonBattleIdUpdate', [self.doId]) + + def b_setSCToontask(self, taskId, toNpcId, toonProgress, msgIndex): + self.setSCToontask(taskId, toNpcId, toonProgress, msgIndex) + self.d_setSCToontask(taskId, toNpcId, toonProgress, msgIndex) + return None + + def d_setSCToontask(self, taskId, toNpcId, toonProgress, msgIndex): + messenger.send('wakeup') + self.sendUpdate('setSCToontask', [taskId, + toNpcId, + toonProgress, + msgIndex]) + + def setSCToontask(self, taskId, toNpcId, toonProgress, msgIndex): + if base.localAvatar.isIgnored(self.doId): + return + chatString = TTSCDecoders.decodeTTSCToontaskMsg(taskId, toNpcId, toonProgress, msgIndex) + if chatString: + self.setChatAbsolute(chatString, CFSpeech | CFQuicktalker | CFTimeout) + + def sendLogSuspiciousEvent(self, msg): + localAvatar.sendUpdate('logSuspiciousEvent', ['%s for %s' % (msg, self.doId)]) + + def d_reqSCResistance(self, msgIndex): + messenger.send('wakeup') + nearbyPlayers = self.getNearbyPlayers(ResistanceChat.EFFECT_RADIUS) + self.sendUpdate('reqSCResistance', [msgIndex, nearbyPlayers]) + + def getNearbyPlayers(self, radius, includeSelf = True): + nearbyToons = [] + toonIds = self.cr.getObjectsOfExactClass(DistributedToon) + for toonId, toon in toonIds.items(): + if toon is not self: + dist = toon.getDistance(self) + if dist < radius: + nearbyToons.append(toonId) + + if includeSelf: + nearbyToons.append(self.doId) + return nearbyToons + + def setSCResistance(self, msgIndex, nearbyToons = []): + chatString = TTSCDecoders.decodeTTSCResistanceMsg(msgIndex) + if chatString: + self.setChatAbsolute(chatString, CFSpeech | CFTimeout) + ResistanceChat.doEffect(msgIndex, self, nearbyToons) + + def d_battleSOS(self, sendToId): + self.cr.ttsFriendsManager.d_battleSOS(sendToId) + + def battleSOS(self, requesterId): + avatar = base.cr.identifyAvatar(requesterId) + + if isinstance(avatar, (DistributedToon, FriendHandle.FriendHandle)): + self.setSystemMessage(requesterId, + TTLocalizer.MovieSOSWhisperHelp % avatar.getName(), + whisperType=WTBattleSOS + ) + elif avatar: + self.notify.warning('got battleSOS from non-toon %s' % requesterId) + + def getDialogueArray(self, *args): + if hasattr(self, 'animalSound'): + dialogueArrays = [ + Toon.DogDialogueArray, + Toon.CatDialogueArray, + Toon.HorseDialogueArray, + Toon.MouseDialogueArray, + Toon.RabbitDialogueArray, + Toon.DuckDialogueArray, + Toon.MonkeyDialogueArray, + Toon.BearDialogueArray, + Toon.PigDialogueArray, + ] + return dialogueArrays[self.animalSound] + return Toon.Toon.getDialogueArray(self, *args) + + def setDefaultShard(self, shard): + self.defaultShard = shard + + def setDefaultZone(self, zoneId): + if not ZoneUtil.getCanonicalHoodId(zoneId) in ToontownGlobals.hoodNameMap: + self.defaultZone = ToontownGlobals.ToontownCentral + return + if ZoneUtil.getCanonicalHoodId(zoneId) == ToontownGlobals.FunnyFarm: + self.defaultZone = ToontownGlobals.ToontownCentral + return + if self.getHp() <= 0 and zoneId in ToontownGlobals.HQToSafezone: + self.defaultZone = ToontownGlobals.HQToSafezone[zoneId] + return + self.defaultZone = zoneId + + def __starSpin1(self, task): + now = globalClock.getFrameTime() + r = now * 90 % 360.0 + self.trophyStar1.setH(r) + return Task.cont + + def isAvFriend(self, avId): + return base.cr.isFriend(avId) + + def setTalkWhisper(self, avId, chat): + if not base.cr.chatAgent.verifyMessage(chat): + return + if not localAvatar.acceptingNonFriendWhispers: + if not self.isAvFriend(avId): + return + if base.localAvatar.isIgnored(avId): + return + if base.localAvatar.sleepFlag == 1: + if not base.cr.identifyAvatar(avId) == base.localAvatar: + base.cr.ttsFriendsManager.d_sleepAutoReply(avId) + if base.whiteList: + chat = base.whiteList.processThroughAll(chat, self.chatGarbler) + self.displayTalkWhisper(avId, chat) + + def setSleepAutoReply(self, fromId): + pass + + def _isValidWhisperSource(self, source): + return isinstance(source, (DistributedToon, FriendHandle.FriendHandle)) + + def setWhisperSCEmoteFrom(self, fromId, emoteId): + handle = base.cr.identifyAvatar(fromId) + if handle == None: + return + if not self._isValidWhisperSource(handle): + self.notify.warning('setWhisperSCEmoteFrom non-toon %s' % fromId) + return + if not localAvatar.acceptingNonFriendWhispers: + if not self.isAvFriend(fromId): + return + if base.localAvatar.isIgnored(fromId): + return + if base.localAvatar.sleepFlag == 1: + if not handle == base.localAvatar: + base.cr.ttsFriendsManager.d_sleepAutoReply(fromId) + chatString = SCDecoders.decodeSCEmoteWhisperMsg(emoteId, handle.getName()) + if chatString: + self.displayWhisper(fromId, chatString, WTEmote) + base.talkAssistant.receiveAvatarWhisperSpeedChat(TalkAssistant.SPEEDCHAT_EMOTE, emoteId, fromId) + return + + def setWhisperSCFrom(self, fromId, msgIndex): + handle = base.cr.identifyAvatar(fromId) + if handle == None: + return + if not self._isValidWhisperSource(handle): + self.notify.warning('setWhisperSCFrom non-toon %s' % fromId) + return + if not localAvatar.acceptingNonFriendWhispers: + if not self.isAvFriend(fromId): + return + if base.localAvatar.isIgnored(fromId): + return + if base.localAvatar.sleepFlag == 1: + if not handle == base.localAvatar: + base.cr.ttsFriendsManager.d_sleepAutoReply(fromId) + chatString = SCDecoders.decodeSCStaticTextMsg(msgIndex) + if chatString: + self.displayWhisper(fromId, chatString, WTNormal) + base.talkAssistant.receiveAvatarWhisperSpeedChat(TalkAssistant.SPEEDCHAT_NORMAL, msgIndex, fromId) + return + + def setWhisperSCCustomFrom(self, fromId, msgIndex): + handle = base.cr.identifyAvatar(fromId) + if handle == None: + return + if not localAvatar.acceptingNonFriendWhispers: + if not self.isAvFriend(fromId): + return + return DistributedPlayer.DistributedPlayer.setWhisperSCCustomFrom(self, fromId, msgIndex) + + def whisperSCToontaskTo(self, taskId, toNpcId, toonProgress, msgIndex, sendToId): + messenger.send('wakeup') + + base.cr.ttsFriendsManager.d_whisperSCToontaskTo(sendToId, taskId, + toNpcId, toonProgress, msgIndex + ) + + def setWhisperSCToontaskFrom(self, fromId, taskId, toNpcId, toonProgress, msgIndex): + sender = base.cr.identifyAvatar(fromId) + if sender is None: + return + + if not localAvatar.acceptingNonFriendWhispers: + if not self.isAvFriend(fromId): + return + + if base.localAvatar.isIgnored(fromId): + return + + chatString = TTSCDecoders.decodeTTSCToontaskMsg(taskId, toNpcId, toonProgress, msgIndex) + if chatString: + self.displayWhisper(fromId, chatString, WTNormal) + + def getNPCFriendsDict(self): + return self.NPCFriendsDict + + def getNPCFriendCount(self, npcId): + return self.NPCFriendsDict.get(npcId, 0) + + def setNPCFriendsDict(self, NPCFriendsList): + NPCFriendsDict = {} + for friendPair in NPCFriendsList: + NPCFriendsDict[friendPair[0]] = friendPair[1] + + self.NPCFriendsDict = NPCFriendsDict + + def setHatList(self, clothesList): + self.hatList = clothesList + + def getHatList(self): + return self.hatList + + def setGlassesList(self, clothesList): + self.glassesList = clothesList + + def getGlassesList(self): + return self.glassesList + + def setBackpackList(self, clothesList): + self.backpackList = clothesList + + def getBackpackList(self): + return self.backpackList + + def setShoesList(self, clothesList): + self.shoesList = clothesList + + def getShoesList(self): + return self.shoesList + + def isTrunkFull(self, extraAccessories = 0): + numAccessories = (len(self.hatList) + len(self.glassesList) + len(self.backpackList) + len(self.shoesList)) / 3 + return numAccessories + extraAccessories >= ToontownGlobals.MaxAccessories + + def setMaxClothes(self, max): + self.maxClothes = max + + def getMaxClothes(self): + return self.maxClothes + + def getClothesTopsList(self): + return self.clothesTopsList + + def setClothesTopsList(self, clothesList): + self.clothesTopsList = clothesList + + def getClothesBottomsList(self): + return self.clothesBottomsList + + def setClothesBottomsList(self, clothesList): + self.clothesBottomsList = clothesList + + def catalogGenClothes(self, avId): + if avId == self.doId: + self.generateToonClothes() + self.loop('neutral') + + def catalogGenAccessories(self, avId): + if avId == self.doId: + self.generateToonAccessories() + self.loop('neutral') + + def isClosetFull(self, extraClothes = 0): + numClothes = len(self.clothesTopsList) / 4 + len(self.clothesBottomsList) / 2 + return numClothes + extraClothes >= self.maxClothes + + def setMaxHp(self, hitPoints): + DistributedPlayer.DistributedPlayer.setMaxHp(self, hitPoints) + if self.inventory: + self.inventory.updateGUI() + + def died(self): + messenger.send(self.uniqueName('died')) + if self.isLocal(): + target_sz = ZoneUtil.getSafeZoneId(self.defaultZone) + place = self.cr.playGame.getPlace() + if place and place.fsm: + place.fsm.request('died', [{'loader': ZoneUtil.getLoaderName(target_sz), + 'where': ZoneUtil.getWhereName(target_sz, 1), + 'how': 'teleportIn', + 'hoodId': target_sz, + 'zoneId': target_sz, + 'shardId': None, + 'avId': -1, + 'battle': 1}]) + return + + def setHoodsVisited(self, hoods): + self.hoodsVisited = hoods + if ToontownGlobals.SellbotHQ in hoods or ToontownGlobals.CashbotHQ in hoods or ToontownGlobals.LawbotHQ in hoods or ToontownGlobals.BossbotHQ in hoods: + self.setDisguisePageFlag(1) + + def wrtReparentTo(self, parent): + DistributedSmoothNode.DistributedSmoothNode.wrtReparentTo(self, parent) + + def setTutorialAck(self, tutorialAck): + self.tutorialAck = tutorialAck + + def getTutorialAck(self): + return self.tutorialAck + + def setEarnedExperience(self, earnedExp): + self.earnedExperience = earnedExp + + def b_setTunnelIn(self, endX, tunnelOrigin): + timestamp = globalClockDelta.getFrameNetworkTime() + pos = tunnelOrigin.getPos(render) + h = tunnelOrigin.getH(render) + self.setTunnelIn(timestamp, endX, pos[0], pos[1], pos[2], h) + self.d_setTunnelIn(timestamp, endX, pos[0], pos[1], pos[2], h) + + def d_setTunnelIn(self, timestamp, endX, x, y, z, h): + self.sendUpdate('setTunnelIn', [timestamp, + endX, + x, + y, + z, + h]) + + def setTunnelIn(self, timestamp, endX, x, y, z, h): + t = globalClockDelta.networkToLocalTime(timestamp) + self.handleTunnelIn(t, endX, x, y, z, h) + + def getTunnelInToonTrack(self, endX, tunnelOrigin): + pivotNode = tunnelOrigin.attachNewNode(self.uniqueName('pivotNode')) + pivotNode.setPos(*self.tunnelPivotPos) + pivotNode.setHpr(0, 0, 0) + pivotY = pivotNode.getY(tunnelOrigin) + endY = 5.0 + straightLerpDur = abs(endY - pivotY) / ToontownGlobals.ToonForwardSpeed + pivotDur = 2.0 + pivotLerpDur = pivotDur * (90.0 / self.pivotAngle) + self.reparentTo(pivotNode) + self.setPos(0, 0, 0) + self.setX(tunnelOrigin, endX) + targetX = self.getX() + self.setX(self.tunnelCenterOffset + (targetX - self.tunnelCenterOffset) * (1.0 - self.tunnelCenterInfluence)) + self.setHpr(tunnelOrigin, 0, 0, 0) + pivotNode.setH(-self.pivotAngle) + return Sequence(Wait(0.8), Parallel(LerpHprInterval(pivotNode, pivotDur, hpr=Point3(0, 0, 0), name=self.uniqueName('tunnelInPivot')), Sequence(Wait(pivotDur - pivotLerpDur), LerpPosInterval(self, pivotLerpDur, pos=Point3(targetX, 0, 0), name=self.uniqueName('tunnelInPivotLerpPos')))), Func(self.wrtReparentTo, render), Func(pivotNode.removeNode), LerpPosInterval(self, straightLerpDur, pos=Point3(endX, endY, 0.1), other=tunnelOrigin, name=self.uniqueName('tunnelInStraightLerp'))) + + def handleTunnelIn(self, startTime, endX, x, y, z, h): + self.stopSmooth() + tunnelOrigin = render.attachNewNode('tunnelOrigin') + tunnelOrigin.setPosHpr(x, y, z, h, 0, 0) + if self.tunnelTrack: + self.tunnelTrack.finish() + self.tunnelTrack = Sequence(self.getTunnelInToonTrack(endX, tunnelOrigin), Func(tunnelOrigin.removeNode), Func(self.startSmooth)) + tOffset = globalClock.getFrameTime() - (startTime + self.smoother.getDelay()) + if tOffset < 0.0: + self.tunnelTrack = Sequence(Wait(-tOffset), self.tunnelTrack) + self.tunnelTrack.start() + else: + self.tunnelTrack.start(tOffset) + + def b_setTunnelOut(self, startX, startY, tunnelOrigin): + timestamp = globalClockDelta.getFrameNetworkTime() + pos = tunnelOrigin.getPos(render) + h = tunnelOrigin.getH(render) + self.setTunnelOut(timestamp, startX, startY, pos[0], pos[1], pos[2], h) + self.d_setTunnelOut(timestamp, startX, startY, pos[0], pos[1], pos[2], h) + + def d_setTunnelOut(self, timestamp, startX, startY, x, y, z, h): + self.sendUpdate('setTunnelOut', [timestamp, + startX, + startY, + x, + y, + z, + h]) + + def setTunnelOut(self, timestamp, startX, startY, x, y, z, h): + t = globalClockDelta.networkToLocalTime(timestamp) + self.handleTunnelOut(t, startX, startY, x, y, z, h) + + def getTunnelOutToonTrack(self, startX, startY, tunnelOrigin): + startPos = self.getPos(tunnelOrigin) + startHpr = self.getHpr(tunnelOrigin) + reducedAvH = PythonUtil.fitDestAngle2Src(startHpr[0], 180) + pivotNode = tunnelOrigin.attachNewNode(self.uniqueName('pivotNode')) + pivotNode.setPos(*self.tunnelPivotPos) + pivotNode.setHpr(0, 0, 0) + pivotY = pivotNode.getY(tunnelOrigin) + straightLerpDur = abs(startY - pivotY) / ToontownGlobals.ToonForwardSpeed + pivotDur = 2.0 + pivotLerpDur = pivotDur * (90.0 / self.pivotAngle) + + def getTargetPos(self = self): + pos = self.getPos() + return Point3(self.tunnelCenterOffset + (pos[0] - self.tunnelCenterOffset) * (1.0 - self.tunnelCenterInfluence), pos[1], pos[2]) + + return Sequence(Parallel(LerpPosInterval(self, straightLerpDur, pos=Point3(startX, pivotY, 0.1), startPos=startPos, other=tunnelOrigin, name=self.uniqueName('tunnelOutStraightLerp')), LerpHprInterval(self, straightLerpDur * 0.8, hpr=Point3(reducedAvH, 0, 0), startHpr=startHpr, other=tunnelOrigin, name=self.uniqueName('tunnelOutStraightLerpHpr'))), Func(self.wrtReparentTo, pivotNode), Parallel(LerpHprInterval(pivotNode, pivotDur, hpr=Point3(-self.pivotAngle, 0, 0), name=self.uniqueName('tunnelOutPivot')), LerpPosInterval(self, pivotLerpDur, pos=getTargetPos, name=self.uniqueName('tunnelOutPivotLerpPos'))), Func(self.wrtReparentTo, render), Func(pivotNode.removeNode)) + + def handleTunnelOut(self, startTime, startX, startY, x, y, z, h): + tunnelOrigin = render.attachNewNode('tunnelOrigin') + tunnelOrigin.setPosHpr(x, y, z, h, 0, 0) + if self.tunnelTrack: + self.tunnelTrack.finish() + self.tunnelTrack = Sequence(Func(self.stopSmooth), self.getTunnelOutToonTrack(startX, startY, tunnelOrigin), Func(self.detachNode), Func(tunnelOrigin.removeNode)) + tOffset = globalClock.getFrameTime() - (startTime + self.smoother.getDelay()) + if tOffset < 0.0: + self.tunnelTrack = Sequence(Wait(-tOffset), self.tunnelTrack) + self.tunnelTrack.start() + else: + self.tunnelTrack.start(tOffset) + + def enterTeleportOut(self, *args, **kw): + Toon.Toon.enterTeleportOut(self, *args, **kw) + if self.track: + self.track.delayDelete = DelayDelete.DelayDelete(self, 'enterTeleportOut') + + def exitTeleportOut(self): + if self.track != None: + DelayDelete.cleanupDelayDeletes(self.track) + Toon.Toon.exitTeleportOut(self) + return + + def b_setAnimState(self, animName, animMultiplier = 1.0, callback = None, extraArgs = []): + self.d_setAnimState(animName, animMultiplier, None, extraArgs) + self.setAnimState(animName, animMultiplier, None, None, callback, extraArgs) + return + + def d_setAnimState(self, animName, animMultiplier = 1.0, timestamp = None, extraArgs = []): + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setAnimState', [animName, animMultiplier, timestamp]) + + def setAnimState(self, animName, animMultiplier = 1.0, timestamp = None, animType = None, callback = None, extraArgs = []): + if not animName or animName == 'None': + return + if timestamp == None: + ts = 0.0 + else: + ts = globalClockDelta.localElapsedTime(timestamp) + if base.config.GetBool('check-invalid-anims', True): + if animMultiplier > 1.0 and animName in ['neutral']: + animMultiplier = 1.0 + if self.animFSM.getStateNamed(animName): + self.animFSM.request(animName, [animMultiplier, + ts, + callback, + extraArgs]) + self.cleanupPieInHand() + return + + def b_setEmoteState(self, animIndex, animMultiplier): + self.setEmoteState(animIndex, animMultiplier) + self.d_setEmoteState(animIndex, animMultiplier) + + def d_setEmoteState(self, animIndex, animMultiplier): + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setEmoteState', [animIndex, animMultiplier, timestamp]) + + def setEmoteState(self, animIndex, animMultiplier, timestamp = None): + if animIndex == TTEmote.EmoteClear: + return + if timestamp == None: + ts = 0.0 + else: + ts = globalClockDelta.localElapsedTime(timestamp) + callback = None + extraArgs = [] + extraArgs.insert(0, animIndex) + self.doEmote(animIndex, animMultiplier, ts, callback, extraArgs) + return + + def setCogStatus(self, cogStatusList): + self.cogs = cogStatusList + + def setCogCount(self, cogCountList): + self.cogCounts = cogCountList + if hasattr(self, 'suitPage'): + self.suitPage.updatePage() + + def setCogRadar(self, radar): + self.cogRadar = radar + if hasattr(self, 'suitPage'): + self.suitPage.updateCogRadarButtons(radar) + + def setBuildingRadar(self, radar): + self.buildingRadar = radar + if hasattr(self, 'suitPage'): + self.suitPage.updateBuildingRadarButtons(radar) + + def setCogTypes(self, types): + self.cogTypes = types + if self.disguisePage: + self.disguisePage.updatePage() + + def setCogLevels(self, levels): + self.cogLevels = levels + if self.disguisePage: + self.disguisePage.updatePage() + + def getCogLevels(self): + return self.cogLevels + + def setCogParts(self, parts): + self.cogParts = parts + messenger.send(self.uniqueName('cogMeritsChange')) + if self.disguisePage: + self.disguisePage.updatePage() + + def getCogParts(self): + return self.cogParts + + def setCogMerits(self, merits): + self.cogMerits = merits + messenger.send(self.uniqueName('cogMeritsChange')) + if self.disguisePage: + self.disguisePage.updatePage() + + def getCogMerits(self): + return self.cogMerits + + def readyForPromotion(self, dept): + merits = base.localAvatar.cogMerits[dept] + totalMerits = CogDisguiseGlobals.getTotalMerits(self, dept) + if merits >= totalMerits: + return 1 + else: + return 0 + + def setCogIndex(self, index): + self.cogIndex = index + if self.cogIndex == -1: + if self.isDisguised: + self.takeOffSuit() + else: + parts = self.getCogParts() + if CogDisguiseGlobals.isSuitComplete(parts, index): + cogIndex = self.cogTypes[index] + SuitDNA.suitsPerDept * index + cog = SuitDNA.suitHeadTypes[cogIndex] + self.putOnSuit(cog) + else: + self.putOnSuit(index, rental=True) + + def isCog(self): + if self.cogIndex == -1: + return 0 + else: + return 1 + + def setDisguisePageFlag(self, flag): + if flag and hasattr(self, 'book'): + self.loadDisguisePages() + self.disguisePageFlag = flag + + def setSosPageFlag(self, flag): + if flag and hasattr(self, 'book'): + self.loadSosPages() + self.sosPageFlag = flag + + def setFishCollection(self, genusList, speciesList, weightList): + self.fishCollection = FishCollection.FishCollection() + self.fishCollection.makeFromNetLists(genusList, speciesList, weightList) + + def getFishCollection(self): + return self.fishCollection + + def setMaxFishTank(self, maxTank): + self.maxFishTank = maxTank + + def getMaxFishTank(self): + return self.maxFishTank + + def setFishTank(self, genusList, speciesList, weightList): + self.fishTank = FishTank.FishTank() + self.fishTank.makeFromNetLists(genusList, speciesList, weightList) + messenger.send(self.uniqueName('fishTankChange')) + + def getFishTank(self): + return self.fishTank + + def isFishTankFull(self): + return len(self.fishTank) >= self.maxFishTank + + def setFishingRod(self, rodId): + self.fishingRod = rodId + if self == base.localAvatar: + messenger.send('refreshFishingRod') + + def getFishingRod(self): + return self.fishingRod + + def setMaxFishingRod(self, rodId): + self.maxFishingRod = rodId + if self == base.localAvatar: + messenger.send('refreshFishingRod') + + def getMaxFishingRod(self): + return self.maxFishingRod + + def requestFishingRod(self, rodId): + if not 0 <= rodId <= self.maxFishingRod: + return + + self.sendUpdate('requestFishingRod', [rodId]) + + def setFishingTrophies(self, trophyList): + self.fishingTrophies = trophyList + + def getFishingTrophies(self): + return self.fishingTrophies + + def setQuests(self, flattenedQuests): + questList = [] + questLen = 5 + for i in xrange(0, len(flattenedQuests), questLen): + questList.append(flattenedQuests[i:i + questLen]) + + self.quests = questList + if self == base.localAvatar: + messenger.send('questsChanged') + + def setQuestCarryLimit(self, limit): + self.questCarryLimit = limit + if self == base.localAvatar: + messenger.send('questsChanged') + + def getQuestCarryLimit(self): + return self.questCarryLimit + + def d_requestDeleteQuest(self, questDesc): + self.sendUpdate('requestDeleteQuest', [list(questDesc)]) + + def setMaxCarry(self, maxCarry): + self.maxCarry = maxCarry + if self.inventory: + self.inventory.updateGUI() + + def getMaxCarry(self): + return self.maxCarry + + def setCheesyEffect(self, effect, hoodId, expireTime): + self.savedCheesyEffect = effect + self.savedCheesyHoodId = hoodId + self.savedCheesyExpireTime = expireTime + if self == base.localAvatar: + self.notify.debug('setCheesyEffect(%s, %s, %s)' % (effect, hoodId, expireTime)) + if effect != ToontownGlobals.CENormal: + serverTime = time.time() + self.cr.getServerDelta() + duration = expireTime * 60 - serverTime + if duration < 0: + self.notify.debug('effect should have expired %s ago.' % PythonUtil.formatElapsedSeconds(-duration)) + else: + self.notify.debug('effect will expire in %s.' % PythonUtil.formatElapsedSeconds(duration)) + if self.activeState == DistributedObject.ESGenerated: + self.reconsiderCheesyEffect(lerpTime=0.5) + else: + self.reconsiderCheesyEffect() + + def reconsiderCheesyEffect(self, lerpTime = 0): + effect = self.savedCheesyEffect + hoodId = self.savedCheesyHoodId + if not self.cr.areCheesyEffectsAllowed(): + effect = ToontownGlobals.CENormal + if hoodId != 0: + try: + currentHoodId = base.cr.playGame.hood.id + except: + currentHoodId = None + + if hoodId == 1: + if currentHoodId == ToontownGlobals.ToontownCentral: + effect = ToontownGlobals.CENormal + elif currentHoodId != None and currentHoodId != hoodId: + effect = ToontownGlobals.CENormal + if self.ghostMode: + effect = ToontownGlobals.CEGhost + self.applyCheesyEffect(effect, lerpTime=lerpTime) + return + + def setGhostMode(self, flag): + if self.ghostMode != flag: + self.ghostMode = flag + if not hasattr(self, 'cr'): + return + if self.activeState <= DistributedObject.ESDisabled: + self.notify.debug('not applying cheesy effect to disabled Toon') + elif self.activeState == DistributedObject.ESGenerating: + self.reconsiderCheesyEffect() + elif self.activeState == DistributedObject.ESGenerated: + self.reconsiderCheesyEffect(lerpTime=0.5) + else: + self.notify.warning('unknown activeState: %s' % self.activeState) + self.showNametag2d() + self.showNametag3d() + if hasattr(self, 'collNode'): + if self.ghostMode: + self.collNode.setCollideMask(ToontownGlobals.GhostBitmask) + else: + self.collNode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.PieBitmask) + if self.isLocal(): + if self.ghostMode: + self.useGhostControls() + else: + self.useWalkControls() + + if hasattr(base, 'wantPets') and base.wantPets: + + def setPetTrickPhrases(self, petTricks): + self.petTrickPhrases = petTricks + if self.isLocal(): + messenger.send('petTrickPhrasesChanged') + + def setCustomMessages(self, customMessages): + self.customMessages = customMessages + if self.isLocal(): + messenger.send('customMessagesChanged') + + def setResistanceMessages(self, resistanceMessages): + self.resistanceMessages = resistanceMessages + if self.isLocal(): + messenger.send('resistanceMessagesChanged') + + def getResistanceMessageCharges(self, textId): + msgs = self.resistanceMessages + for i in xrange(len(msgs)): + if msgs[i][0] == textId: + return msgs[i][1] + + return 0 + + def setCatalogSchedule(self, currentWeek, nextTime): + self.catalogScheduleCurrentWeek = currentWeek + self.catalogScheduleNextTime = nextTime + if self.isLocal(): + self.notify.debug('setCatalogSchedule(%s, %s)' % (currentWeek, nextTime)) + if nextTime: + serverTime = time.time() + self.cr.getServerDelta() + duration = nextTime * 60 - serverTime + self.notify.debug('next catalog in %s.' % PythonUtil.formatElapsedSeconds(duration)) + + def setCatalog(self, monthlyCatalog, weeklyCatalog, backCatalog): + self.monthlyCatalog = CatalogItemList.CatalogItemList(monthlyCatalog) + self.weeklyCatalog = CatalogItemList.CatalogItemList(weeklyCatalog) + self.backCatalog = CatalogItemList.CatalogItemList(backCatalog) + if config.GetBool('want-house-types', False): + from toontown.catalog import CatalogHouseItem + self.backCatalog.extend(CatalogHouseItem.getAllHouses()) + if self.catalogNotify == ToontownGlobals.NewItems: + self.catalogNotify = ToontownGlobals.OldItems + + def setCatalogNotify(self, catalogNotify, mailboxNotify): + if len(self.weeklyCatalog) + len(self.monthlyCatalog) == 0: + catalogNotify = ToontownGlobals.NoItems + if len(self.mailboxContents) == 0: + mailboxNotify = ToontownGlobals.NoItems + self.catalogNotify = catalogNotify + self.mailboxNotify = mailboxNotify + if self.isLocal(): + self.gotCatalogNotify = 1 + self.refreshOnscreenButtons() + + def setDeliverySchedule(self, onOrder): + self.onOrder = CatalogItemList.CatalogItemList(onOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if self == base.localAvatar: + nextTime = self.onOrder.getNextDeliveryDate() + if nextTime != None: + serverTime = time.time() + self.cr.getServerDelta() + duration = nextTime * 60 - serverTime + self.notify.debug('next delivery in %s.' % PythonUtil.formatElapsedSeconds(duration)) + messenger.send('setDeliverySchedule-%s' % self.doId) + return + + def setMailboxContents(self, mailboxContents): + self.mailboxContents = CatalogItemList.CatalogItemList(mailboxContents, store=CatalogItem.Customization) + messenger.send('setMailboxContents-%s' % self.doId) + + def setAwardSchedule(self, onOrder): + self.onAwardOrder = CatalogItemList.CatalogItemList(onOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if self == base.localAvatar: + nextTime = self.onAwardOrder.getNextDeliveryDate() + if nextTime != None: + serverTime = time.time() + self.cr.getServerDelta() + duration = nextTime * 60 - serverTime + self.notify.debug('next delivery in %s.' % PythonUtil.formatElapsedSeconds(duration)) + messenger.send('setAwardSchedule-%s' % self.doId) + return + + def setAwardMailboxContents(self, awardMailboxContents): + self.notify.debug('Setting awardMailboxContents to %s.' % awardMailboxContents) + self.awardMailboxContents = CatalogItemList.CatalogItemList(awardMailboxContents, store=CatalogItem.Customization) + self.notify.debug('awardMailboxContents is %s.' % self.awardMailboxContents) + messenger.send('setAwardMailboxContents-%s' % self.doId) + + def setAwardNotify(self, awardNotify): + self.notify.debug('setAwardNotify( %s )' % awardNotify) + self.awardNotify = awardNotify + if self.isLocal(): + self.gotCatalogNotify = 1 + self.refreshOnscreenButtons() + + def setGiftSchedule(self, onGiftOrder): + self.onGiftOrder = CatalogItemList.CatalogItemList(onGiftOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if self == base.localAvatar: + nextTime = self.onGiftOrder.getNextDeliveryDate() + if nextTime != None: + serverTime = time.time() + self.cr.getServerDelta() + duration = nextTime * 60 - serverTime + self.notify.debug('next delivery in %s.' % PythonUtil.formatElapsedSeconds(duration)) + return + + def playSplashEffect(self, x, y, z): + if localAvatar.zoneId not in [ToontownGlobals.DonaldsDock, ToontownGlobals.OutdoorZone] and (not hasattr(localAvatar, 'inEstate') or localAvatar.inEstate != 1): + if random.random() < 0.1: + self.sendLogSuspiciousEvent('AvatarHackWarning! playing hacked splash effect') + return + from toontown.effects import Splash + if self.splash == None: + self.splash = Splash.Splash(render) + self.splash.setPos(x, y, z) + self.splash.setScale(2) + self.splash.play() + place = base.cr.playGame.getPlace() + if place: + if hasattr(place.loader, 'submergeSound'): + base.playSfx(place.loader.submergeSound, node=self) + return + + def d_playSplashEffect(self, x, y, z): + self.playSplashEffect(x, y, z) + self.sendUpdate('playSplashEffect', [x, y, z]) + + def setTrackAccess(self, trackArray): + self.trackArray = trackArray + if self.inventory: + self.inventory.updateGUI() + + def getTrackAccess(self): + return self.trackArray + + def hasTrackAccess(self, track): + return self.trackArray[track] + + def setTrackProgress(self, trackId, progress): + self.trackProgressId = trackId + self.trackProgress = progress + if hasattr(self, 'trackPage'): + self.trackPage.updatePage() + + def getTrackProgress(self): + return [self.trackProgressId, self.trackProgress] + + def getTrackProgressAsArray(self, maxLength = 15): + shifts = map(operator.rshift, maxLength * [self.trackProgress], range(maxLength - 1, -1, -1)) + digits = map(operator.mod, shifts, maxLength * [2]) + digits.reverse() + return digits + + def setTeleportAccess(self, teleportZoneArray): + self.teleportZoneArray = teleportZoneArray + + def getTeleportAccess(self): + return self.teleportZoneArray + + def hasTeleportAccess(self, zoneId): + return zoneId in self.teleportZoneArray + + def setScavengerHunt(self, scavengerHuntArray): + self.scavengerHuntArray = scavengerHuntArray + + def getScavengerHunt(self): + return self.scavengerHuntArray + + def setQuestHistory(self, questList): + self.questHistory = questList + + def getQuestHistory(self): + return self.questHistory + + def setRewardHistory(self, rewardTier, rewardList): + self.rewardTier = rewardTier + self.rewardHistory = rewardList + + def getRewardHistory(self): + return (self.rewardTier, self.rewardHistory) + + def doSmoothTask(self, task): + self.smoother.computeAndApplySmoothPosHpr(self, self) + self.setSpeed(self.smoother.getSmoothForwardVelocity(), self.smoother.getSmoothRotationalVelocity()) + return Task.cont + + def d_setParent(self, parentToken): + DistributedSmoothNode.DistributedSmoothNode.d_setParent(self, parentToken) + + def setEmoteAccess(self, bits): + if bits[26]: + bits.remove(bits[26]) + self.emoteAccess = bits + if self == base.localAvatar: + messenger.send('emotesChanged') + + def b_setHouseId(self, id): + self.setHouseId(id) + self.d_setHouseId(id) + + def d_setHouseId(self, id): + self.sendUpdate('setHouseId', [id]) + + def setHouseId(self, id): + self.houseId = id + + def getHouseId(self): + return self.houseId + + def b_setSpeedChatStyleIndex(self, index): + realIndexToSend = 0 + if type(index) == type(0) and 0 <= index and index < len(speedChatStyles): + realIndexToSend = index + self.setSpeedChatStyleIndex(realIndexToSend) + self.d_setSpeedChatStyleIndex(realIndexToSend) + + def d_setSpeedChatStyleIndex(self, index): + realIndexToSend = 0 + if type(index) == type(0) and 0 <= index and index < len(speedChatStyles): + realIndexToSend = index + self.sendUpdate('setSpeedChatStyleIndex', [realIndexToSend]) + + def setSpeedChatStyleIndex(self, index): + realIndexToUse = 0 + if type(index) == type(0) and 0 <= index and index < len(speedChatStyles): + realIndexToUse = index + else: + base.cr.centralLogger.writeClientEvent('Hacker victim setSpeedChatStyleIndex invalid attacking toon = %d' % self.doId) + self.speedChatStyleIndex = realIndexToUse + nameKey, arrowColor, rolloverColor, frameColor = speedChatStyles[realIndexToUse] + self.nametag.setQtColor(VBase4(frameColor[0], frameColor[1], frameColor[2], 1)) + if self.isLocal(): + messenger.send('SpeedChatStyleChange', []) + + def getSpeedChatStyleIndex(self): + return self.speedChatStyleIndex + + def setMaxMoney(self, maxMoney): + self.maxMoney = maxMoney + + def getMaxMoney(self): + return self.maxMoney + + def setMoney(self, money): + if money != self.money: + self.money = money + messenger.send(self.uniqueName('moneyChange'), [self.money]) + + def getMoney(self): + return self.money + + def setMaxBankMoney(self, maxMoney): + self.maxBankMoney = maxMoney + + def getMaxBankMoney(self): + return self.maxBankMoney + + def setBankMoney(self, money): + self.bankMoney = money + messenger.send(self.uniqueName('bankMoneyChange'), [self.bankMoney]) + + def getBankMoney(self): + return self.bankMoney + + def getTotalMoney(self): + return self.getBankMoney() + self.getMoney() + + def takeMoney(self, money): + self.sendUpdate('takeMoney', [money]) + + def setEmblems(self, emblems): + if self.emblems != emblems: + self.emblems = emblems + messenger.send(self.uniqueName('emblemsChange'), [self.emblems]) + + def getEmblems(self): + return self.emblems + + def isEnoughEmblemsToBuy(self, itemEmblemPrices): + for emblemIndex, emblemPrice in enumerate(itemEmblemPrices): + if emblemIndex >= len(self.emblems): + return False + if self.emblems[emblemIndex] < emblemPrice: + return False + + return True + + def isEnoughMoneyAndEmblemsToBuy(self, moneyPrice, itemEmblemPrices): + if self.getTotalMoney() < moneyPrice: + return False + for emblemIndex, emblemPrice in enumerate(itemEmblemPrices): + if emblemIndex >= len(self.emblems): + return False + if self.emblems[emblemIndex] < emblemPrice: + return False + + return True + + def presentPie(self, x, y, z, h, timestamp32): + if self.numPies <= 0: + return + lastTossTrack = Sequence() + if self.tossTrack: + lastTossTrack = self.tossTrack + tossTrack = None + ts = globalClockDelta.localElapsedTime(timestamp32, bits=32) + ts -= self.smoother.getDelay() + ival = self.getPresentPieInterval(x, y, z, h) + if ts > 0: + startTime = ts + lastTossTrack.finish() + else: + ival = Sequence(Wait(-ts), ival) + lastTossTrack.finish() + startTime = 0 + ival = Sequence(ival) + ival.start(startTime) + self.tossTrack = ival + return + + def tossPie(self, x, y, z, h, sequence, power, throwType, timestamp32): + if self.numPies <= 0: + return + if self.numPies != ToontownGlobals.FullPies: + self.setNumPies(self.numPies - 1) + self.lastTossedPie = globalClock.getFrameTime() + lastTossTrack = Sequence() + if self.tossTrack: + lastTossTrack = self.tossTrack + tossTrack = None + lastPieTrack = Sequence() + if sequence in self.pieTracks: + lastPieTrack = self.pieTracks[sequence] + del self.pieTracks[sequence] + ts = globalClockDelta.localElapsedTime(timestamp32, bits=32) + ts -= self.smoother.getDelay() + toss, pie, flyPie = self.getTossPieInterval(x, y, z, h, power, throwType) + if ts > 0: + startTime = ts + lastTossTrack.finish() + lastPieTrack.finish() + else: + toss = Sequence(Wait(-ts), toss) + pie = Sequence(Wait(-ts), pie) + lastTossTrack.finish() + lastPieTrack.finish() + startTime = 0 + self.tossTrack = toss + toss.start(startTime) + pie = Sequence(pie, Func(self.pieFinishedFlying, sequence)) + self.pieTracks[sequence] = pie + pie.start(startTime) + + def pieFinishedFlying(self, sequence): + if sequence in self.pieTracks: + del self.pieTracks[sequence] + + def pieFinishedSplatting(self, sequence): + if sequence in self.splatTracks: + del self.splatTracks[sequence] + + def pieSplat(self, x, y, z, sequence, pieCode, timestamp32): + if self.isLocal(): + return + elapsed = globalClock.getFrameTime() - self.lastTossedPie + if elapsed > 30: + return + lastPieTrack = Sequence() + if sequence in self.pieTracks: + lastPieTrack = self.pieTracks[sequence] + del self.pieTracks[sequence] + if sequence in self.splatTracks: + lastSplatTrack = self.splatTracks[sequence] + del self.splatTracks[sequence] + lastSplatTrack.finish() + ts = globalClockDelta.localElapsedTime(timestamp32, bits=32) + ts -= self.smoother.getDelay() + splat = self.getPieSplatInterval(x, y, z, pieCode) + splat = Sequence(Func(messenger.send, 'pieSplat', [self, pieCode]), splat) + if ts > 0: + startTime = ts + lastPieTrack.finish() + else: + splat = Sequence(Wait(-ts), splat) + startTime = 0 + splat = Sequence(splat, Func(self.pieFinishedSplatting, sequence)) + self.splatTracks[sequence] = splat + splat.start(startTime) + + def cleanupPies(self): + for track in self.pieTracks.values(): + track.finish() + + self.pieTracks = {} + for track in self.splatTracks.values(): + track.finish() + + self.splatTracks = {} + self.cleanupPieInHand() + + def cleanupPieInHand(self): + if self.tossTrack: + self.tossTrack.finish() + self.tossTrack = None + self.cleanupPieModel() + + def setNumPies(self, numPies): + self.numPies = numPies + if self.isLocal(): + self.updatePieButton() + if numPies == 0: + self.interruptPie() + + def setPieType(self, pieType): + self.pieType = pieType + if self.isLocal(): + self.updatePieButton() + + def setTrophyScore(self, score): + self.trophyScore = score + if self.trophyStar != None: + self.trophyStar.removeNode() + self.trophyStar = None + if self.trophyStarSpeed != 0: + taskMgr.remove(self.uniqueName('starSpin')) + self.trophyStarSpeed = 0 + if self.trophyScore >= ToontownGlobals.TrophyStarLevels[4]: + self.trophyStar = loader.loadModel('phase_3.5/models/gui/name_star') + np = NodePath(self.nametag.getNameIcon()) + self.trophyStar.reparentTo(np) + self.trophyStar.setScale(2) + self.trophyStar.setColor(ToontownGlobals.TrophyStarColors[4]) + self.trophyStarSpeed = 15 + if self.trophyScore >= ToontownGlobals.TrophyStarLevels[5]: + taskMgr.add(self.__starSpin, self.uniqueName('starSpin')) + elif self.trophyScore >= ToontownGlobals.TrophyStarLevels[2]: + self.trophyStar = loader.loadModel('phase_3.5/models/gui/name_star') + np = NodePath(self.nametag.getNameIcon()) + self.trophyStar.reparentTo(np) + self.trophyStar.setScale(1.5) + self.trophyStar.setColor(ToontownGlobals.TrophyStarColors[2]) + self.trophyStarSpeed = 10 + if self.trophyScore >= ToontownGlobals.TrophyStarLevels[3]: + taskMgr.add(self.__starSpin, self.uniqueName('starSpin')) + elif self.trophyScore >= ToontownGlobals.TrophyStarLevels[0]: + self.trophyStar = loader.loadModel('phase_3.5/models/gui/name_star') + np = NodePath(self.nametag.getNameIcon()) + self.trophyStar.reparentTo(np) + self.trophyStar.setScale(1.5) + self.trophyStar.setColor(ToontownGlobals.TrophyStarColors[0]) + self.trophyStarSpeed = 8 + if self.trophyScore >= ToontownGlobals.TrophyStarLevels[1]: + taskMgr.add(self.__starSpin, self.uniqueName('starSpin')) + self.setHeadPositions() + + def __starSpin(self, task): + now = globalClock.getFrameTime() + r = now * self.trophyStarSpeed % 360.0 + self.trophyStar.setR(r) + return Task.cont + + def getZoneId(self): + place = base.cr.playGame.getPlace() + if place: + return place.getZoneId() + else: + return None + return None + + def getRequestID(self): + return CLIENT_GET_AVATAR_DETAILS + + def announceBingo(self): + self.setChatAbsolute(TTLocalizer.FishBingoBingo, CFSpeech | CFTimeout) + + def squish(self, damage): + if self == base.localAvatar: + base.cr.playGame.getPlace().fsm.request('squished') + self.stunToon() + self.setZ(self.getZ(render) + 0.025) + + def d_squish(self, damage): + self.sendUpdate('squish', [damage]) + + def b_squish(self, damage): + if not self.isStunned: + self.squish(damage) + self.d_squish(damage) + self.playDialogueForString('!') + + def getShadowJoint(self): + return Toon.Toon.getShadowJoint(self) + + if base.wantKarts: + + def hasKart(self): + return self.kartDNA[KartDNA.bodyType] != -1 + + def getKartDNA(self): + return self.kartDNA + + def setTickets(self, numTickets): + self.tickets = numTickets + + def getTickets(self): + return self.tickets + + def getAccessoryByType(self, accType): + return self.kartDNA[accType] + + def setCurrentKart(self, avId): + self.kartId = avId + + def releaseKart(self): + self.kartId = None + return + + def setKartBodyType(self, bodyType): + self.kartDNA[KartDNA.bodyType] = bodyType + + def getKartBodyType(self): + return self.kartDNA[KartDNA.bodyType] + + def setKartBodyColor(self, bodyColor): + self.kartDNA[KartDNA.bodyColor] = bodyColor + + def getKartBodyColor(self): + return self.kartDNA[KartDNA.bodyColor] + + def setKartAccessoryColor(self, accColor): + self.kartDNA[KartDNA.accColor] = accColor + + def getKartAccessoryColor(self): + return self.kartDNA[KartDNA.accColor] + + def setKartEngineBlockType(self, ebType): + self.kartDNA[KartDNA.ebType] = ebType + + def getKartEngineBlockType(self): + return self.kartDNA[KartDNA.ebType] + + def setKartSpoilerType(self, spType): + self.kartDNA[KartDNA.spType] = spType + + def getKartSpoilerType(self): + return self.kartDNA[KartDNA.spType] + + def setKartFrontWheelWellType(self, fwwType): + self.kartDNA[KartDNA.fwwType] = fwwType + + def getKartFrontWheelWellType(self): + return self.kartDNA[KartDNA.fwwType] + + def setKartBackWheelWellType(self, bwwType): + self.kartDNA[KartDNA.bwwType] = bwwType + + def getKartBackWheelWellType(self): + return self.kartDNA[KartDNA.bwwType] + + def setKartRimType(self, rimsType): + self.kartDNA[KartDNA.rimsType] = rimsType + + def setKartDecalType(self, decalType): + self.kartDNA[KartDNA.decalType] = decalType + + def getKartDecalType(self): + return self.kartDNA[KartDNA.decalType] + + def getKartRimType(self): + return self.kartDNA[KartDNA.rimsType] + + def setKartAccessoriesOwned(self, accessories): + while len(accessories) < 16: + accessories.append(-1) + + self.accessories = accessories + + def getKartAccessoriesOwned(self): + owned = copy.deepcopy(self.accessories) + while InvalidEntry in owned: + owned.remove(InvalidEntry) + + return owned + + def requestKartDNAFieldUpdate(self, dnaField, fieldValue): + self.notify.debug('requestKartDNAFieldUpdate - dnaField %s, fieldValue %s' % (dnaField, fieldValue)) + self.sendUpdate('updateKartDNAField', [dnaField, fieldValue]) + + def requestAddOwnedAccessory(self, accessoryId): + self.notify.debug('requestAddOwnedAccessor - purchased accessory %s' % accessoryId) + self.sendUpdate('addOwnedAccessory', [accessoryId]) + + def requestRemoveOwnedAccessory(self, accessoryId): + self.notify.debug('requestRemoveOwnedAccessor - removed accessory %s' % accessoryId) + self.sendUpdate('removeOwnedAccessory', [accessoryId]) + + def setKartingTrophies(self, trophyList): + self.kartingTrophies = trophyList + + def getKartingTrophies(self): + return self.kartingTrophies + + def setKartingHistory(self, history): + self.kartingHistory = history + + def getKartingHistory(self): + return self.kartingHistory + + def setKartingPersonalBest(self, bestTimes): + self.kartingPersonalBest = bestTimes + + def getKartingPersonalBest(self): + return self.kartingPersonalBest + + def setKartingPersonalBest2(self, bestTimes2): + self.kartingPersonalBest2 = bestTimes2 + + def getKartingPersonalBest2(self): + return self.kartingPersonalBest2 + + def getKartingPersonalBestAll(self): + return self.kartingPersonalBest + self.kartingPersonalBest2 + + if hasattr(base, 'wantPets') and base.wantPets: + + def setPetId(self, petId): + self.petId = petId + if self.isLocal(): + base.cr.addPetToFriendsMap() + + def getPetId(self): + return self.petId + + def hasPet(self): + return self.petId != 0 + + def b_setPetTutorialDone(self, done): + self.d_setPetTutorialDone(done) + self.setPetTutorialDone(done) + + def d_setPetTutorialDone(self, done): + self.sendUpdate('setPetTutorialDone', [done]) + + def setPetTutorialDone(self, done): + self.petTutorialDone = done + + def b_setFishBingoTutorialDone(self, done): + self.d_setFishBingoTutorialDone(done) + self.setFishBingoTutorialDone(done) + + def d_setFishBingoTutorialDone(self, done): + self.sendUpdate('setFishBingoTutorialDone', [done]) + + def setFishBingoTutorialDone(self, done): + self.fishBingoTutorialDone = done + + def b_setFishBingoMarkTutorialDone(self, done): + self.d_setFishBingoMarkTutorialDone(done) + self.setFishBingoMarkTutorialDone(done) + + def d_setFishBingoMarkTutorialDone(self, done): + self.sendUpdate('setFishBingoMarkTutorialDone', [done]) + + def setFishBingoMarkTutorialDone(self, done): + self.fishBingoMarkTutorialDone = done + + def b_setPetMovie(self, petId, flag): + self.d_setPetMovie(petId, flag) + self.setPetMovie(petId, flag) + + def d_setPetMovie(self, petId, flag): + self.sendUpdate('setPetMovie', [petId, flag]) + + def setPetMovie(self, petId, flag): + pass + + def lookupPetDNA(self): + if self.petId and not self.petDNA: + from toontown.pets import PetDetail + PetDetail.PetDetail(self.petId, self.__petDetailsLoaded) + + def __petDetailsLoaded(self, pet): + self.petDNA = pet.style + + + def trickOrTreatTargetMet(self, beanAmount): + if self.effect: + self.effect.stop() + self.effect = TrickOrTreatTargetEffect(beanAmount) + self.effect.play() + + def winterCarolingTargetMet(self, beanAmount): + if self.effect: + self.effect.stop() + self.effect = WinterCarolingEffect(beanAmount) + self.effect.play() + + def d_reqCogSummons(self, type, suitIndex): + self.sendUpdate('reqCogSummons', [type, suitIndex]) + + def cogSummonsResponse(self, returnCode, suitIndex, doId): + messenger.send('cog-summons-response', [returnCode, suitIndex, doId]) + + def setCogSummonsEarned(self, cogSummonsEarned): + self.cogSummonsEarned = cogSummonsEarned + + def getCogSummonsEarned(self): + return self.cogSummonsEarned + + def hasCogSummons(self, suitIndex, type = None): + summons = self.getCogSummonsEarned() + curSetting = summons[suitIndex] + if type == 'building': + return curSetting & 1 + elif type == 'invasion': + return curSetting & 2 + elif type == 'cogdo': + return curSetting & 4 + elif type == 'skelinvasion': + return curSetting & 8 + elif type == 'waiterinvasion': + return curSetting & 16 + elif type == 'v2invasion': + return curSetting & 32 + return curSetting + + def setFlowerCollection(self, speciesList, varietyList): + self.flowerCollection = FlowerCollection.FlowerCollection() + self.flowerCollection.makeFromNetLists(speciesList, varietyList) + + def getFlowerCollection(self): + return self.flowerCollection + + def setMaxFlowerBasket(self, maxFlowerBasket): + self.maxFlowerBasket = maxFlowerBasket + + def getMaxFlowerBasket(self): + return self.maxFlowerBasket + + def isFlowerBasketFull(self): + return len(self.flowerBasket) >= self.maxFlowerBasket + + def setFlowerBasket(self, speciesList, varietyList): + self.flowerBasket = FlowerBasket.FlowerBasket() + self.flowerBasket.makeFromNetLists(speciesList, varietyList) + messenger.send('flowerBasketUpdated') + + def getFlowerBasket(self): + return self.flowerBasket + + def setShovel(self, shovelId): + self.shovel = shovelId + + def attachShovel(self): + self.shovelModel = self.getShovelModel() + self.shovelModel.reparentTo(self.rightHand) + return self.shovelModel + + def detachShovel(self): + if self.shovelModel: + self.shovelModel.removeNode() + + def getShovelModel(self): + shovels = loader.loadModel('phase_5.5/models/estate/shovels') + shovelId = ['A', + 'B', + 'C', + 'D'][self.shovel] + shovel = shovels.find('**/shovel' + shovelId) + shovel.setH(-90) + shovel.setP(216) + shovel.setX(0.2) + shovel.detachNode() + shovels.removeNode() + return shovel + + def setShovelSkill(self, skillLevel): + self.shovelSkill = skillLevel + + def getBoxCapability(self): + return GardenGlobals.getShovelPower(self.shovel, self.shovelSkill) + + def setWateringCan(self, wateringCanId): + self.wateringCan = wateringCanId + + def attachWateringCan(self): + self.wateringCanModel = self.getWateringCanModel() + self.wateringCanModel.reparentTo(self.rightHand) + return self.wateringCanModel + + def detachWateringCan(self): + if self.wateringCanModel: + self.wateringCanModel.removeNode() + + def getWateringCanModel(self): + scalePosHprsTable = ((0.25, 0.1, 0, 0.2, -90, -125, -45), + (0.2, 0.0, 0.25, 0.2, -90, -125, -45), + (0.2, 0.2, 0.1, 0.2, -90, -125, -45), + (0.2, 0.0, 0.25, 0.2, -90, -125, -45)) + cans = loader.loadModel('phase_5.5/models/estate/watering_cans') + canId = ['A', + 'B', + 'C', + 'D'][self.wateringCan] + can = cans.find('**/water_can' + canId) + can.setScale(scalePosHprsTable[self.wateringCan][0]) + can.setPos(scalePosHprsTable[self.wateringCan][1], scalePosHprsTable[self.wateringCan][2], scalePosHprsTable[self.wateringCan][3]) + can.setHpr(scalePosHprsTable[self.wateringCan][4], scalePosHprsTable[self.wateringCan][5], scalePosHprsTable[self.wateringCan][6]) + can.detachNode() + cans.removeNode() + if hasattr(base, 'rwc'): + if base.rwc: + if hasattr(self, 'wateringCan2'): + self.wateringCan2.removeNode() + self.wateringCan2 = can.copyTo(self.rightHand) + else: + self.wateringCan2.removeNode() + return can + + def setWateringCanSkill(self, skillLevel): + self.wateringCanSkill = skillLevel + + def setGardenSpecials(self, specials): + self.gardenSpecials = specials + if hasattr(self, 'gardenPage') and self.gardenPage: + self.gardenPage.updatePage() + + def getGardenSpecials(self): + return self.gardenSpecials + + def getMyTrees(self): + treeDict = self.cr.getObjectsOfClass(DistributedGagTree.DistributedGagTree) + trees = [] + for tree in treeDict.values(): + if tree.getOwnerId() == self.doId: + trees.append(tree) + + if not trees: + pass + return trees + + def isTreePlanted(self, track, level): + trees = self.getMyTrees() + for tree in trees: + if tree.gagTrack == track and tree.gagLevel == level: + return True + + return False + + def doIHaveRequiredTrees(self, track, level): + trees = self.getMyTrees() + trackAndLevelList = [] + for tree in trees: + trackAndLevelList.append((tree.gagTrack, tree.gagLevel)) + + haveRequired = True + for curLevel in xrange(level): + testTuple = (track, curLevel) + if testTuple not in trackAndLevelList: + haveRequired = False + break + + return haveRequired + + def setTrackBonusLevel(self, trackArray): + self.trackBonusLevel = trackArray + if self.inventory: + self.inventory.updateGUI() + + def getTrackBonusLevel(self, track = None): + if track == None: + return self.trackBonusLevel + else: + return self.trackBonusLevel[track] + return + + def checkGagBonus(self, track, level): + trackBonus = self.getTrackBonusLevel(track) + return trackBonus >= level + + def setGardenTrophies(self, trophyList): + self.gardenTrophies = trophyList + + def getGardenTrophies(self): + return self.gardenTrophies + + def useSpecialResponse(self, returnCode): + messenger.send('use-special-response', [returnCode]) + + def setGardenStarted(self, bStarted): + self.gardenStarted = bStarted + + def getGardenStarted(self): + return self.gardenStarted + + def sendToGolfCourse(self, zoneId): + hoodId = self.cr.playGame.hood.hoodId + golfRequest = {'loader': 'safeZoneLoader', + 'where': 'golfcourse', + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1} + base.cr.playGame.getPlace().requestLeave(golfRequest) + return + + def getGolfTrophies(self): + return self.golfTrophies + + def getGolfCups(self): + return self.golfCups + + def setGolfHistory(self, history): + self.golfHistory = history + self.golfTrophies = GolfGlobals.calcTrophyListFromHistory(self.golfHistory) + self.golfCups = GolfGlobals.calcCupListFromHistory(self.golfHistory) + if hasattr(self, 'book'): + self.addGolfPage() + + def getGolfHistory(self): + return self.golfHistory + + def hasPlayedGolf(self): + retval = False + for historyValue in self.golfHistory: + if historyValue: + retval = True + break + + return retval + + def setPackedGolfHoleBest(self, packedHoleBest): + unpacked = GolfGlobals.unpackGolfHoleBest(packedHoleBest) + self.setGolfHoleBest(unpacked) + + def setGolfHoleBest(self, holeBest): + self.golfHoleBest = holeBest + + def getGolfHoleBest(self): + return self.golfHoleBest + + def setGolfCourseBest(self, courseBest): + self.golfCourseBest = courseBest + + def getGolfCourseBest(self): + return self.golfCourseBest + + def setUnlimitedSwing(self, unlimitedSwing): + self.unlimitedSwing = unlimitedSwing + + def getUnlimitedSwing(self): + return self.unlimitedSwing + + def getPinkSlips(self): + return self.specialInventory[0] + + def getCrateKeys(self): + return self.specialInventory[1] + + def setSpecialInventory(self, specialInventory): + self.specialInventory = specialInventory + + def getSpecialInventory(self): + return self.specialInventory + + def setDisplayName(self, str): + if not self.isDisguised: + self.setFancyNametag(name=str) + else: + self.removeFancyNametag() + Avatar.Avatar.setDisplayName(self, str) + + def setFancyNametag(self, name=None): + if name is None: + name = self.getName() + if self.getNametagStyle() >= len(TTLocalizer.NametagFonts): + self.nametag.setFont(ToontownGlobals.getToonFont()) + else: + self.nametag.setFont(ToontownGlobals.getNametagFont(self.getNametagStyle())) + Avatar.Avatar.setDisplayName(self, name) + + def removeFancyNametag(self): + self.nametag.clearShadow() + + def getNametagStyle(self): + if hasattr(self, 'nametagStyle'): + return self.nametagStyle + else: + return 0 + + def setNametagStyle(self, nametagStyle): + if hasattr(self, 'gmToonLockStyle') and self.gmToonLockStyle: + return + if base.config.GetBool('want-nametag-avids', 0): + nametagStyle = 0 + self.nametagStyle = nametagStyle + self.setDisplayName(self.getName()) + + def getNametagStyles(self): + return self.nametagStyles + + def setNametagStyles(self, nametagStyles): + self.nametagStyles = nametagStyles + if self == base.localAvatar: + messenger.send('refreshNametagStyle') + + def requestNametagStyle(self, nametagStyle): + if nametagStyle not in self.nametagStyles: + return + + self.sendUpdate('requestNametagStyle', [nametagStyle]) + + def getAvIdName(self): + return '%s\n%s' % (self.getName(), self.doId) + + def playCurrentDialogue(self, dialogue, chatFlags, interrupt = 1): + if interrupt and self.__currentDialogue is not None: + self.__currentDialogue.stop() + self.__currentDialogue = dialogue + if dialogue: + base.playSfx(dialogue, node=self) + elif chatFlags & CFSpeech != 0: + if self.nametag.getNumChatPages() > 0: + self.playDialogueForString(self.nametag.getChat()) + if self.soundChatBubble != None: + base.playSfx(self.soundChatBubble, node=self) + elif self.nametag.getChatStomp(): + self.playDialogueForString(self.nametag.getStompText(), self.nametag.getStompDelay()) + + def playDialogueForString(self, chatString, delay = 0.0): + if len(chatString) == 0: + return + searchString = chatString.lower() + if searchString.find(OTPLocalizer.DialogSpecial) >= 0: + type = 'special' + elif searchString.find(OTPLocalizer.DialogExclamation) >= 0: + type = 'exclamation' + elif searchString.find(OTPLocalizer.DialogQuestion) >= 0: + type = 'question' + elif random.randint(0, 1): + type = 'statementA' + else: + type = 'statementB' + stringLength = len(chatString) + if stringLength <= OTPLocalizer.DialogLength1: + length = 1 + elif stringLength <= OTPLocalizer.DialogLength2: + length = 2 + elif stringLength <= OTPLocalizer.DialogLength3: + length = 3 + else: + length = 4 + self.playDialogue(type, length, delay) + + def playDialogue(self, type, length, delay = 0.0): + dialogueArray = self.getDialogueArray() + if dialogueArray == None: + return + sfxIndex = None + if type == 'statementA' or type == 'statementB': + if length == 1: + sfxIndex = 0 + elif length == 2: + sfxIndex = 1 + elif length >= 3: + sfxIndex = 2 + elif type == 'question': + sfxIndex = 3 + elif type == 'exclamation': + sfxIndex = 4 + elif type == 'special': + sfxIndex = 5 + else: + self.notify.error('unrecognized dialogue type: ', type) + if sfxIndex != None and sfxIndex < len(dialogueArray) and dialogueArray[sfxIndex] != None: + soundSequence = Sequence(Wait(delay), SoundInterval(dialogueArray[sfxIndex], node=None, listenerNode=base.localAvatar, loop=0, volume=1.0)) + self.soundSequenceList.append(soundSequence) + soundSequence.start() + self.cleanUpSoundList() + return + + def cleanUpSoundList(self): + removeList = [] + for soundSequence in self.soundSequenceList: + if soundSequence.isStopped(): + removeList.append(soundSequence) + + for soundSequence in removeList: + self.soundSequenceList.remove(soundSequence) + + def setChatAbsolute(self, chatString, chatFlags, dialogue = None, interrupt = 1, quiet = 0): + DistributedAvatar.DistributedAvatar.setChatAbsolute(self, chatString, chatFlags, dialogue, interrupt) + + def displayTalk(self, chatString): + flags = CFSpeech | CFTimeout + if ChatUtil.isThought(chatString): + flags = CFThought + chatString = ChatUtil.removeThoughtPrefix(chatString) + self.nametag.setChat(chatString, flags) + if base.toonChatSounds: + self.playCurrentDialogue(None, flags, interrupt=1) + + def setMail(self, mail): + DistributedToon.partyNotify.debug('setMail called with %d mail items' % len(mail)) + self.mail = [] + for i in xrange(len(mail)): + oneMailItem = mail[i] + newMail = SimpleMailBase(*oneMailItem) + self.mail.append(newMail) + + def setSimpleMailNotify(self, simpleMailNotify): + DistributedToon.partyNotify.debug('setSimpleMailNotify( %s )' % simpleMailNotify) + self.simpleMailNotify = simpleMailNotify + if self.isLocal(): + self.gotCatalogNotify = 1 + self.refreshOnscreenButtons() + + def setInviteMailNotify(self, inviteMailNotify): + DistributedToon.partyNotify.debug('setInviteMailNotify( %s )' % inviteMailNotify) + self.inviteMailNotify = inviteMailNotify + if self.isLocal(): + self.gotCatalogNotify = 1 + self.refreshOnscreenButtons() + + def setInvites(self, invites): + DistributedToon.partyNotify.debug('setInvites called passing in %d invites.' % len(invites)) + self.invites = [] + for i in xrange(len(invites)): + oneInvite = invites[i] + newInvite = InviteInfo(*oneInvite) + self.invites.append(newInvite) + + def updateInviteMailNotify(self): + invitesInMailbox = self.getInvitesToShowInMailbox() + newInvites = 0 + readButNotRepliedInvites = 0 + for invite in invitesInMailbox: + if invite.status == PartyGlobals.InviteStatus.NotRead: + newInvites += 1 + elif invite.status == PartyGlobals.InviteStatus.ReadButNotReplied: + readButNotRepliedInvites += 1 + + if newInvites: + self.setInviteMailNotify(ToontownGlobals.NewItems) + elif readButNotRepliedInvites: + self.setInviteMailNotify(ToontownGlobals.OldItems) + else: + self.setInviteMailNotify(ToontownGlobals.NoItems) + + def getInvitesToShowInMailbox(self): + result = [] + for invite in self.invites: + appendInvite = True + if invite.status == InviteStatus.Accepted or invite.status == InviteStatus.Rejected: + appendInvite = False + if appendInvite: + partyInfo = self.getOnePartyInvitedTo(invite.partyId) + if not partyInfo: + appendInvite = False + if appendInvite: + if partyInfo.status == PartyGlobals.PartyStatus.Cancelled: + appendInvite = False + if appendInvite: + endDate = partyInfo.endTime.date() + curDate = base.cr.toontownTimeManager.getCurServerDateTime().date() + if endDate < curDate: + appendInvite = False + if appendInvite: + result.append(invite) + + return result + + def getNumInvitesToShowInMailbox(self): + result = len(self.getInvitesToShowInMailbox()) + return result + + def setHostedParties(self, hostedParties): + DistributedToon.partyNotify.debug('setHostedParties called passing in %d parties.' % len(hostedParties)) + self.hostedParties = [] + for i in xrange(len(hostedParties)): + hostedInfo = hostedParties[i] + newParty = PartyInfo(*hostedInfo) + self.hostedParties.append(newParty) + + def setPartiesInvitedTo(self, partiesInvitedTo): + DistributedToon.partyNotify.debug('setPartiesInvitedTo called passing in %d parties.' % len(partiesInvitedTo)) + self.partiesInvitedTo = [] + for i in xrange(len(partiesInvitedTo)): + partyInfo = partiesInvitedTo[i] + newParty = PartyInfo(*partyInfo) + self.partiesInvitedTo.append(newParty) + + self.updateInviteMailNotify() + + def getOnePartyInvitedTo(self, partyId): + result = None + for i in xrange(len(self.partiesInvitedTo)): + partyInfo = self.partiesInvitedTo[i] + if partyInfo.partyId == partyId: + result = partyInfo + break + + return result + + def getInviteForPartyId(self, partyId): + result = None + for invite in self.invites: + if invite.partyId == partyId: + result = invite + break + + return result + + def setPartyReplies(self, replies): + DistributedToon.partyNotify.debug('setPartyReplies called passing in %d parties.' % len(replies)) + self.partyReplyInfoBases = [] + for i in xrange(len(replies)): + partyReply = replies[i] + repliesForOneParty = PartyReplyInfoBase(*partyReply) + self.partyReplyInfoBases.append(repliesForOneParty) + + def setPartyCanStart(self, partyId): + DistributedToon.partyNotify.debug('setPartyCanStart called passing in partyId=%s' % partyId) + for partyInfo in self.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.status = PartyGlobals.PartyStatus.CanStart + from toontown.shtiker import EventsPage + if hasattr(self, 'eventsPage') and base.localAvatar.book.entered and base.localAvatar.book.isOnPage(self.eventsPage) and self.eventsPage.getMode() == EventsPage.EventsPage_Host: + base.localAvatar.eventsPage.loadHostedPartyInfo() + self.setSystemMessage(0, TTLocalizer.PartyCanStart, WTSystem) + + def setPartyStatus(self, partyId, newStatus): + DistributedToon.partyNotify.debug('setPartyCanStatus called passing in partyId=%s status=%s' % (partyId, newStatus)) + found = False + for partyInfo in self.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.status = newStatus + found = True + break + + for partyInfo in self.partiesInvitedTo: + if partyInfo.partyId == partyId: + partyInfo.status = newStatus + found = True + from toontown.shtiker import EventsPage + if hasattr(self, 'eventsPage') and base.localAvatar.book.entered and base.localAvatar.book.isOnPage(self.eventsPage) and self.eventsPage.getMode() == EventsPage.EventsPage_Invited: + base.localAvatar.eventsPage.loadInvitations() + if newStatus == PartyStatus.Started and hasattr(self, 'setSystemMessage'): + invite = self.getInviteForPartyId(partyId) + if invite: + name = ' ' + host = base.cr.identifyAvatar(partyInfo.hostId) + if host: + name = host.getName() + if invite.status == InviteStatus.Accepted: + displayStr = TTLocalizer.PartyHasStartedAcceptedInvite % TTLocalizer.GetPossesive(name) + else: + displayStr = TTLocalizer.PartyHasStartedNotAcceptedInvite % TTLocalizer.GetPossesive(name) + self.setSystemMessage(partyInfo.hostId, displayStr, WTSystem) + break + + if not found: + self.notify.warning("setPartyCanStart can't find partyId=% status=%d" % (partyId, newStatus)) + + def announcePartyStarted(self, partyId): + DistributedToon.partyNotify.debug('announcePartyStarted') + return + for partyReplyInfo in self.partyReplyInfoBases: + if partyReplyInfo.partyId == partyId: + for singleReply in partyReplyInfo.replies: + toonId = singleReply.inviteeId + if base.cr.isFriend(toonId): + if base.cr.isFriendOnline(toonId): + if singleReply.status == InviteStatus.Accepted: + self.whisperSCTo(5302, toonId) + else: + self.whisperSCTo(5302, toonId) + + def updateInvite(self, inviteKey, newStatus): + DistributedToon.partyNotify.debug('updateInvite( inviteKey=%d, newStatus=%s )' % (inviteKey, InviteStatus.getString(newStatus))) + for invite in self.invites: + if invite.inviteKey == inviteKey: + invite.status = newStatus + self.updateInviteMailNotify() + break + + def updateReply(self, partyId, inviteeId, newStatus): + DistributedToon.partyNotify.debug('updateReply( partyId=%d, inviteeId=%d, newStatus=%s )' % (partyId, inviteeId, InviteStatus.getString(newStatus))) + for partyReplyInfoBase in self.partyReplyInfoBases: + if partyReplyInfoBase.partyId == partyId: + for reply in partyReplyInfoBase.replies: + if reply.inviteeId == inviteeId: + reply.status = newStatus + break + + def toonUp(self, hpGained, hasInteractivePropBonus = False): + if self.hp == None or hpGained < 0: + return + oldHp = self.hp + if self.hp + hpGained <= 0: + self.hp += hpGained + else: + self.hp = min(max(self.hp, 0) + hpGained, self.maxHp) + hpGained = self.hp - max(oldHp, 0) + if hpGained > 0: + self.showHpText(hpGained, hasInteractivePropBonus=hasInteractivePropBonus) + self.hpChange(quietly=0) + return + + def showHpText(self, number, bonus = 0, scale = 1, hasInteractivePropBonus = False): + if self.HpTextEnabled and not self.ghostMode: + if number != 0: + if self.hpText: + self.hideHpText() + self.HpTextGenerator.setFont(OTPGlobals.getSignFont()) + if number < 0: + self.HpTextGenerator.setText(str(number)) + else: + hpGainedStr = '+' + str(number) + if hasInteractivePropBonus: + hpGainedStr += '\n' + TTLocalizer.InteractivePropTrackBonusTerms[0] + self.HpTextGenerator.setText(hpGainedStr) + self.HpTextGenerator.clearShadow() + self.HpTextGenerator.setAlign(TextNode.ACenter) + if bonus == 1: + r = 1.0 + g = 1.0 + b = 0 + a = 1 + elif bonus == 2: + r = 1.0 + g = 0.5 + b = 0 + a = 1 + elif number < 0: + r = 0.9 + g = 0 + b = 0 + a = 1 + else: + r = 0 + g = 0.9 + b = 0 + a = 1 + self.HpTextGenerator.setTextColor(r, g, b, a) + self.hpTextNode = self.HpTextGenerator.generate() + self.hpText = self.attachNewNode(self.hpTextNode) + self.hpText.setScale(scale) + self.hpText.setBillboardPointEye() + self.hpText.setBin('fixed', 100) + self.hpText.setPos(0, 0, self.height / 2) + seq = Sequence(self.hpText.posInterval(1.0, Point3(0, 0, self.height + 1.5), blendType='easeOut'), Wait(0.85), self.hpText.colorInterval(0.1, Vec4(r, g, b, 0)), Func(self.hideHpText)) + seq.start() + + def setAnimalSound(self, index): + self.animalSound = index + + def setBuffs(self, buffs): + self.buffs = buffs + self.applyBuffs() + + def setRedeemedCodes(self, redeemedCodes): + self.redeemedCodes = redeemedCodes + + def b_setIgnored(self, ignored): + self.setIgnored(ignored) + self.d_setIgnored(ignored) + + def setIgnored(self, ignored): + self.ignored = ignored + + def d_setIgnored(self, ignored): + self.sendUpdate('setIgnored', [ignored]) + + def isIgnored(self, doId): + return doId in self.ignored + + def addIgnore(self, doId): + if not self.isIgnored(doId): + self.ignored.append(doId) + self.d_setIgnored(self.ignored) + + def removeIgnore(self, doId): + if self.isIgnored(doId): + self.ignored.remove(doId) + self.d_setIgnored(self.ignored) + + def setReported(self, reported): + self.reported = reported + + def isReported(self, doId): + return doId in self.reported + + def addReport(self, doId): + if not self.isReported(doId): + self.reported.append(doId) + + def setFriendsList(self, friendsList): + DistributedPlayer.DistributedPlayer.setFriendsList(self, friendsList) + messenger.send('friendsListChanged') + Toon.reconsiderAllToonsUnderstandable() + + def setTrueFriends(self, trueFriends): + self.trueFriends = trueFriends + Toon.reconsiderAllToonsUnderstandable() + messenger.send('friendsListChanged') + + def isTrueFriends(self, doId): + return base.cr.wantTrueFriends() and doId in self.trueFriends + + def applyBuffs(self): + for id, timestamp in enumerate(self.buffs): + if id == ToontownGlobals.BMovementSpeed: + if not timestamp: + return + if self.zoneId is None: + return + if ZoneUtil.isDynamicZone(self.zoneId): + return + if ZoneUtil.getWhereName(self.zoneId, True) not in ('playground', 'street', 'toonInterior', 'cogHQExterior', 'factoryExterior'): + return + self.controlManager.setSpeeds( + ToontownGlobals.ToonForwardSpeed * ToontownGlobals.BMovementSpeedMultiplier, + ToontownGlobals.ToonJumpForce, + ToontownGlobals.ToonReverseSpeed * ToontownGlobals.BMovementSpeedMultiplier, + ToontownGlobals.ToonRotateSpeed * ToontownGlobals.BMovementSpeedMultiplier) + + def setStats(self, stats): + self.stats = stats + if self == base.localAvatar: + messenger.send('refreshStats') + + def getStats(self): + return self.stats + + def getStat(self, index): + return self.stats[index] + + def wipeStats(self): + self.sendUpdate('wipeStats') + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def globalTeleport(): + """ + Activates the global teleport cheat. + """ + invoker = spellbook.getInvoker() + invoker.sendUpdate('setTeleportOverride', [1]) + invoker.setTeleportAccess(list(ToontownGlobals.HoodsForTeleportAll)) + return 'Global teleport has been activated.' + +@magicWord(category=CATEGORY_ADMINISTRATOR, types=[int]) +def zone(zoneId): + """ + Changes the invoker's zone ID. + """ + base.cr.sendSetZoneMsg(zoneId, [zoneId]) + return 'You have been moved to zone %d.' % zoneId + +@magicWord(category=CATEGORY_ADMINISTRATOR) +def blackCat(): + """ + Ask the black cat manager to turn you into a cat. + """ + base.cr.blackCatMgr.requestBlackCatTransformation() + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def toggleGM(): + invoker = spellbook.getInvoker() + if invoker.gmIcon: + invoker.setWantAdminTag(False) + invoker.removeGMIcon() + invoker.setNametagName()#setName(invoker.getName()) + else: + invoker.setWantAdminTag(True) + invoker.setGMIcon(invoker.getAdminAccess()) + invoker.setNametagName()#setName(invoker.getName()) + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[str]) +def showParticle(name): + """ + Shows a particle. + """ + + particle = BattleParticles.createParticleEffect(name) + + if particle: + particle.start(spellbook.getTarget()) + return 'Successfully started particle!' + + return 'Particle %s does not exist.' % name diff --git a/toontown/toon/DistributedToonAI.py b/toontown/toon/DistributedToonAI.py new file mode 100755 index 00000000..e460a683 --- /dev/null +++ b/toontown/toon/DistributedToonAI.py @@ -0,0 +1,5144 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedSmoothNodeAI +from direct.distributed.ClockDelta import * +from direct.distributed.MsgTypes import * +from direct.distributed.PyDatagram import PyDatagram +from direct.task import Task +from otp.ai.AIBaseGlobal import * +from otp.ai.MagicWordGlobal import * +from otp.avatar import DistributedAvatarAI, DistributedPlayerAI +from otp.otpbase import OTPGlobals, OTPLocalizer +from toontown.battle import SuitBattleGlobals +from toontown.catalog import CatalogAccessoryItem, CatalogItem, CatalogItemList +from toontown.cogdominium import CogdoUtil +from toontown.chat import ResistanceChat +from toontown.coghq import CogDisguiseGlobals +from toontown.estate import FlowerBasket, FlowerCollection, GardenGlobals +from toontown.fishing import FishCollection, FishTank, FishGlobals +from toontown.golf import GolfGlobals +from toontown.hood import ZoneUtil +from toontown.parties import PartyGlobals +from toontown.parties.InviteInfo import InviteInfoBase +from toontown.parties.PartyGlobals import InviteStatus +from toontown.parties.PartyInfo import PartyInfoAI +from toontown.parties.PartyReplyInfo import PartyReplyInfoBase +from toontown.quest import QuestRewardCounter, Quests +from toontown.racing import RaceGlobals +from toontown.shtiker import CogPageGlobals +from toontown.suit import SuitDNA, SuitInvasionGlobals +from toontown.toon import NPCToons +from toontown.toonbase import TTLocalizer, ToontownBattleGlobals, ToontownGlobals +from toontown.toonbase.ToontownGlobals import * +from NPCToons import npcFriends +import Experience, InventoryBase, ToonDNA, random, time +from toontown.uberdog import TopToonsGlobals + +if simbase.wantPets: + from toontown.pets import PetLookerAI, PetObserve +else: + class PetLookerAI: + class PetLookerAI: + pass + +if simbase.wantKarts: + from toontown.racing.KartDNA import * + +class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoothNodeAI.DistributedSmoothNodeAI, PetLookerAI.PetLookerAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedToonAI') + maxCallsPerNPC = 100 + partTypeIds = {ToontownGlobals.FT_FullSuit: (CogDisguiseGlobals.leftLegIndex, + CogDisguiseGlobals.rightLegIndex, + CogDisguiseGlobals.torsoIndex, + CogDisguiseGlobals.leftArmIndex, + CogDisguiseGlobals.rightArmIndex), + ToontownGlobals.FT_Leg: (CogDisguiseGlobals.leftLegIndex, CogDisguiseGlobals.rightLegIndex), + ToontownGlobals.FT_Arm: (CogDisguiseGlobals.leftArmIndex, CogDisguiseGlobals.rightArmIndex), + ToontownGlobals.FT_Torso: (CogDisguiseGlobals.torsoIndex,)} + petId = None + + def __init__(self, air): + DistributedPlayerAI.DistributedPlayerAI.__init__(self, air) + DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air) + + if simbase.wantPets: + PetLookerAI.PetLookerAI.__init__(self) + + self.air = air + self.dna = ToonDNA.ToonDNA() + self.inventory = None + self.fishCollection = None + self.fishTank = None + self.experience = None + self.petId = None + self.quests = [] + self.cogs = [] + self.cogCounts = [] + self.NPCFriendsDict = {} + self.clothesTopsList = [] + self.clothesBottomsList = [] + self.hatList = [] + self.glassesList = [] + self.backpackList = [] + self.shoesList = [] + self.hat = (0, 0, 0) + self.glasses = (0, 0, 0) + self.backpack = (0, 0, 0) + self.shoes = (0, 0, 0) + self.cogTypes = [0, 0, 0, 0] + self.cogLevel = [0, 0, 0, 0] + self.cogParts = [0, 0, 0, 0] + self.cogRadar = [0, 0, 0, 0] + self.cogIndex = -1 + self.disguisePageFlag = 0 + self.sosPageFlag = 0 + self.buildingRadar = [0, 0, 0, 0] + self.fishingRod = 0 + self.fishingTrophies = [] + self.trackArray = [] + self.emoteAccess = [0] * 27 + self.maxMoney = 0 + self.maxBankMoney = 0 + self.bankMoney = 0 + self.gardenSpecials = [] + self.houseId = 0 + self.savedCheesyEffect = ToontownGlobals.CENormal + self.savedCheesyHoodId = 0 + self.savedCheesyExpireTime = 0 + self.ghostMode = 0 + self.immortalMode = 0 + self.unlimitedGags = 0 + self.numPies = 0 + self.pieType = 0 + self.hpOwnedByBattle = 0 + if simbase.wantPets: + self.petTrickPhrases = [] + if simbase.wantBingo: + self.bingoCheat = False + self.customMessages = [] + self.catalogNotify = ToontownGlobals.NoItems + self.mailboxNotify = ToontownGlobals.NoItems + self.catalogScheduleCurrentWeek = 0 + self.catalogScheduleNextTime = 0 + self.monthlyCatalog = CatalogItemList.CatalogItemList() + self.weeklyCatalog = CatalogItemList.CatalogItemList() + self.backCatalog = CatalogItemList.CatalogItemList() + self.onOrder = CatalogItemList.CatalogItemList(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + self.onGiftOrder = CatalogItemList.CatalogItemList(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + self.mailboxContents = CatalogItemList.CatalogItemList(store=CatalogItem.Customization) + self.awardMailboxContents = CatalogItemList.CatalogItemList(store=CatalogItem.Customization) + self.onAwardOrder = CatalogItemList.CatalogItemList(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + self.kart = None + if simbase.wantKarts: + self.kartDNA = [-1] * getNumFields() + self.tickets = 200 + self.allowSoloRace = False + self.allowRaceTimeout = True + self.setBattleId(0) + self.gardenStarted = False + self.flowerCollection = None + self.shovel = 0 + self.shovelSkill = 0 + self.wateringCan = 0 + self.wateringCanSkill = 0 + self.hatePets = 1 + self.golfHistory = None + self.golfHoleBest = None + self.golfCourseBest = None + self.unlimitedSwing = False + self.numMailItems = 0 + self.simpleMailNotify = ToontownGlobals.NoItems + self.inviteMailNotify = ToontownGlobals.NoItems + self.invites = [] + self.hostedParties = [] + self.partiesInvitedTo = [] + self.partyReplyInfoBases = [] + self.teleportOverride = 0 + self.buffs = [] + self.redeemedCodes = [] + self.ignored = [] + self.reported = [] + self.trueFriends = [] + self.fishBingoTutorialDone = False + self.nextKnockHeal = 0 + self.tfRequest = (0, 0) + self.epp = [] + + def generate(self): + DistributedPlayerAI.DistributedPlayerAI.generate(self) + DistributedSmoothNodeAI.DistributedSmoothNodeAI.generate(self) + + def announceGenerate(self): + DistributedPlayerAI.DistributedPlayerAI.announceGenerate(self) + DistributedSmoothNodeAI.DistributedSmoothNodeAI.announceGenerate(self) + + if self.isPlayerControlled(): + messenger.send('avatarEntered', [self]) + + from toontown.toon.DistributedNPCToonBaseAI import DistributedNPCToonBaseAI + if not isinstance(self, DistributedNPCToonBaseAI): + self.sendUpdate('setDefaultShard', [self.air.districtId]) + + def clearChat(self): + self.sendUpdate('setTalk', ['.']) + + def setLocation(self, parentId, zoneId): + DistributedPlayerAI.DistributedPlayerAI.setLocation(self, parentId, zoneId) + + from toontown.toon.DistributedNPCToonBaseAI import DistributedNPCToonBaseAI + if not isinstance(self, DistributedNPCToonBaseAI): + self.clearChat() + + if 100 <= zoneId < ToontownGlobals.DynamicZonesBegin: + hood = ZoneUtil.getHoodId(zoneId) + self.b_setLastHood(hood) + self.b_setDefaultZone(hood) + + hoodsVisited = list(self.getHoodsVisited()) + if hood not in hoodsVisited: + hoodsVisited.append(hood) + self.b_setHoodsVisited(hoodsVisited) + + if zoneId == ToontownGlobals.GoofySpeedway or zoneId == ToontownGlobals.OutdoorZone: + tpAccess = self.getTeleportAccess() + if zoneId not in tpAccess: + tpAccess.append(zoneId) + self.b_setTeleportAccess(tpAccess) + + def sendDeleteEvent(self): + if simbase.wantPets: + isInEstate = self.isInEstate() + wasInEstate = self.wasInEstate() + if isInEstate or wasInEstate: + PetObserve.send(self.estateZones, PetObserve.PetActionObserve(PetObserve.Actions.LOGOUT, self.doId)) + if wasInEstate: + self.cleanupEstateData() + + DistributedAvatarAI.DistributedAvatarAI.sendDeleteEvent(self) + + def delete(self): + if self.isPlayerControlled(): + messenger.send('avatarExited', [self]) + if simbase.wantPets: + if self.isInEstate(): + self.exitEstate() + if self.zoneId != ToontownGlobals.QuietZone: + self.announceZoneChange(ToontownGlobals.QuietZone, self.zoneId) + taskName = self.uniqueName('cheesy-expires') + taskMgr.remove(taskName) + taskName = self.uniqueName('next-catalog') + taskMgr.remove(taskName) + taskName = self.uniqueName('next-delivery') + taskMgr.remove(taskName) + taskName = self.uniqueName('next-award-delivery') + taskMgr.remove(taskName) + taskName = 'next-bothDelivery-%s' % self.doId + taskMgr.remove(taskName) + self.stopToonUp() + del self.dna + if self.inventory: + self.inventory.unload() + del self.inventory + del self.experience + if simbase.wantPets: + PetLookerAI.PetLookerAI.destroy(self) + del self.kart + self._sendExitServerEvent() + + DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self) + DistributedPlayerAI.DistributedPlayerAI.delete(self) + + def deleteDummy(self): + if self.inventory: + self.inventory.unload() + del self.inventory + self.experience = None + taskName = self.uniqueName('next-catalog') + taskMgr.remove(taskName) + + def ban(self, comment): + pass + + def disconnect(self): + self.requestDelete() + + def patchDelete(self): + del self.dna + if self.inventory: + self.inventory.unload() + del self.inventory + del self.experience + if simbase.wantPets: + PetLookerAI.PetLookerAI.destroy(self) + self.doNotDeallocateChannel = True + self.zoneId = None + + DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self) + DistributedPlayerAI.DistributedPlayerAI.delete(self) + + def handleLogicalZoneChange(self, newZoneId, oldZoneId): + DistributedAvatarAI.DistributedAvatarAI.handleLogicalZoneChange(self, newZoneId, oldZoneId) + + if self.isPlayerControlled(): + messenger.send(self.staticGetLogicalZoneChangeAllEvent(), [newZoneId, oldZoneId, self]) + + def announceZoneChange(self, newZoneId, oldZoneId): + if simbase.wantPets: + broadcastZones = [oldZoneId, newZoneId] + if self.isInEstate() or self.wasInEstate(): + broadcastZones = union(broadcastZones, self.estateZones) + PetObserve.send(broadcastZones, PetObserve.PetActionObserve(PetObserve.Actions.CHANGE_ZONE, self.doId, (oldZoneId, newZoneId))) + + def checkAccessorySanity(self, accessoryType, idx, textureIdx, colorIdx): + if idx == 0 and textureIdx == 0 and colorIdx == 0: + return 1 + if accessoryType == ToonDNA.HAT: + stylesDict = ToonDNA.HatStyles + accessoryTypeStr = 'Hat' + elif accessoryType == ToonDNA.GLASSES: + stylesDict = ToonDNA.GlassesStyles + accessoryTypeStr = 'Glasses' + elif accessoryType == ToonDNA.BACKPACK: + stylesDict = ToonDNA.BackpackStyles + accessoryTypeStr = 'Backpack' + elif accessoryType == ToonDNA.SHOES: + stylesDict = ToonDNA.ShoesStyles + accessoryTypeStr = 'Shoes' + else: + return 0 + try: + styleStr = stylesDict.keys()[stylesDict.values().index([idx, textureIdx, colorIdx])] + accessoryItemId = 0 + for itemId in CatalogAccessoryItem.AccessoryTypes.keys(): + if styleStr == CatalogAccessoryItem.AccessoryTypes[itemId][CatalogAccessoryItem.ATString]: + accessoryItemId = itemId + break + if accessoryItemId == 0: + self.air.writeServerEvent('suspicious', self.doId, 'Toon tried to wear invalid %s %d %d %d' % (accessoryTypeStr, idx, textureIdx, colorIdx)) + return 0 + return 1 + except: + self.air.writeServerEvent('suspicious', self.doId, 'Toon tried to wear invalid %s %d %d %d' % (accessoryTypeStr, idx, textureIdx, colorIdx)) + return 0 + + def b_setHat(self, idx, textureIdx, colorIdx): + self.d_setHat(idx, textureIdx, colorIdx) + self.setHat(idx, textureIdx, colorIdx) + + def d_setHat(self, idx, textureIdx, colorIdx): + if self.checkAccessorySanity(ToonDNA.HAT, idx, textureIdx, colorIdx): + self.sendUpdate('setHat', [idx, textureIdx, colorIdx]) + + def setHat(self, idx, textureIdx, colorIdx): + if self.checkAccessorySanity(ToonDNA.HAT, idx, textureIdx, colorIdx): + self.hat = (idx, textureIdx, colorIdx) + + def getHat(self): + return self.hat + + def b_setGlasses(self, idx, textureIdx, colorIdx): + self.d_setGlasses(idx, textureIdx, colorIdx) + self.setGlasses(idx, textureIdx, colorIdx) + + def d_setGlasses(self, idx, textureIdx, colorIdx): + if self.checkAccessorySanity(ToonDNA.GLASSES, idx, textureIdx, colorIdx): + self.sendUpdate('setGlasses', [idx, textureIdx, colorIdx]) + + def setGlasses(self, idx, textureIdx, colorIdx): + if self.checkAccessorySanity(ToonDNA.GLASSES, idx, textureIdx, colorIdx): + self.glasses = (idx, textureIdx, colorIdx) + + def getGlasses(self): + return self.glasses + + def b_setBackpack(self, idx, textureIdx, colorIdx): + self.d_setBackpack(idx, textureIdx, colorIdx) + self.setBackpack(idx, textureIdx, colorIdx) + + def d_setBackpack(self, idx, textureIdx, colorIdx): + if self.checkAccessorySanity(ToonDNA.BACKPACK, idx, textureIdx, colorIdx): + self.sendUpdate('setBackpack', [idx, textureIdx, colorIdx]) + + def setBackpack(self, idx, textureIdx, colorIdx): + if self.checkAccessorySanity(ToonDNA.BACKPACK, idx, textureIdx, colorIdx): + self.backpack = (idx, textureIdx, colorIdx) + + def getBackpack(self): + return self.backpack + + def b_setShoes(self, idx, textureIdx, colorIdx): + self.d_setShoes(idx, textureIdx, colorIdx) + self.setShoes(idx, textureIdx, colorIdx) + + def d_setShoes(self, idx, textureIdx, colorIdx): + if self.checkAccessorySanity(ToonDNA.SHOES, idx, textureIdx, colorIdx): + self.sendUpdate('setShoes', [idx, textureIdx, colorIdx]) + + def setShoes(self, idx, textureIdx, colorIdx): + if self.checkAccessorySanity(ToonDNA.SHOES, idx, textureIdx, colorIdx): + self.shoes = (idx, textureIdx, colorIdx) + + def getShoes(self): + return self.shoes + + def b_setDNAString(self, string): + self.d_setDNAString(string) + self.setDNAString(string) + + def d_setDNAString(self, string): + self.sendUpdate('setDNAString', [string]) + + def setDNAString(self, string): + self.dna.makeFromNetString(string) + if not self.verifyDNA(): + self.notify.warning('Avatar %d has an invalid DNA string.' % self.doId) + self.air.writeServerEvent( + 'suspicious', self.doId, 'Invalid DNA string.') + + def verifyDNA(self): + return True + + def getDNAString(self): + return self.dna.makeNetString() + + def getStyle(self): + return self.dna + + def b_setExperience(self, experience): + self.d_setExperience(experience) + self.setExperience(experience) + + def d_setExperience(self, experience): + self.sendUpdate('setExperience', [experience]) + + def setExperience(self, experience): + self.experience = Experience.Experience(experience, self) + + def getExperience(self): + return self.experience.makeNetString() + + def b_setIgnored(self, ignored): + self.d_setIgnored(ignored) + self.setIgnored(ignored) + + def d_setIgnored(self, ignored): + self.sendUpdate('setIgnored', [ignored]) + + def setIgnored(self, ignored): + self.ignored = ignored + + def getIgnored(self): + return self.ignored + + def setReported(self, reported): + self.reported = reported + + def getReported(self): + return self.reported + + def isReported(self, doId): + return doId in self.reported + + def b_setInventory(self, inventory): + self.setInventory(inventory) + self.d_setInventory(self.getInventory()) + + def d_setInventory(self, inventory): + self.sendUpdate('setInventory', [inventory]) + + def setInventory(self, inventoryNetString): + if self.inventory: + self.inventory.updateInvString(inventoryNetString) + else: + self.inventory = InventoryBase.InventoryBase(self, inventoryNetString) + emptyInv = InventoryBase.InventoryBase(self) + emptyString = emptyInv.makeNetString() + lengthMatch = len(inventoryNetString) - len(emptyString) + if lengthMatch != 0: + if len(inventoryNetString) == 42: + oldTracks = 7 + oldLevels = 6 + elif len(inventoryNetString) == 49: + oldTracks = 7 + oldLevels = 7 + else: + oldTracks = 0 + oldLevels = 0 + if oldTracks == 0 and oldLevels == 0: + self.notify.warning('reseting invalid inventory to MAX on toon: %s' % self.doId) + self.inventory.zeroInv() + self.inventory.maxOutInv(1) + else: + newInventory = InventoryBase.InventoryBase(self) + oldList = emptyInv.makeFromNetStringForceSize(inventoryNetString, oldTracks, oldLevels) + for indexTrack in xrange(0, oldTracks): + for indexGag in xrange(0, oldLevels): + newInventory.addItems(indexTrack, indexGag, oldList[indexTrack][indexGag]) + self.inventory.unload() + self.inventory = newInventory + self.d_setInventory(self.getInventory()) + + def getInventory(self): + return self.inventory.makeNetString() + + def doRestock(self, noUber = 1): + self.inventory.zeroInv() + self.inventory.maxOutInv(noUber) + self.d_setInventory(self.inventory.makeNetString()) + + def setDefaultShard(self, shard): + self.defaultShard = shard + + def getDefaultShard(self): + return self.defaultShard + + def setDefaultZone(self, zone): + self.defaultZone = zone + + def d_setDefaultZone(self, zone): + self.sendUpdate('setDefaultZone', [zone]) + + def b_setDefaultZone(self, zone): + if zone != self.defaultZone: + self.setDefaultZone(zone) + self.d_setDefaultZone(zone) + + def getDefaultZone(self): + return self.defaultZone + + def d_setFriendsList(self, friendsList): + self.sendUpdate('setFriendsList', [friendsList]) + + def setFriendsList(self, friendsList): + self.friendsList = friendsList + + def getFriendsList(self): + return self.friendsList + + def extendFriendsList(self, friendId): + if friendId in self.friendsList: + return + + self.friendsList.append(friendId) + self.air.questManager.toonMadeFriend(self) + + def getBattleId(self): + if self.battleId >= 0: + return self.battleId + else: + return 0 + + def b_setBattleId(self, battleId): + self.setBattleId(battleId) + self.d_setBattleId(battleId) + + def d_setBattleId(self, battleId): + if self.battleId >= 0: + self.sendUpdate('setBattleId', [battleId]) + else: + self.sendUpdate('setBattleId', [0]) + + def setBattleId(self, battleId): + self.battleId = battleId + + def d_setNPCFriendsDict(self, NPCFriendsDict): + NPCFriendsList = [] + for friend in NPCFriendsDict.keys(): + NPCFriendsList.append((friend, NPCFriendsDict[friend])) + + self.sendUpdate('setNPCFriendsDict', [NPCFriendsList]) + + def setNPCFriendsDict(self, NPCFriendsList): + self.NPCFriendsDict = {} + for friendPair in NPCFriendsList: + self.NPCFriendsDict[friendPair[0]] = friendPair[1] + + def getNPCFriendsDict(self): + return self.NPCFriendsDict + + def b_setNPCFriendsDict(self, NPCFriendsList): + self.setNPCFriendsDict(NPCFriendsList) + self.d_setNPCFriendsDict(self.NPCFriendsDict) + + def resetNPCFriendsDict(self): + self.b_setNPCFriendsDict([]) + + def attemptAddNPCFriend(self, npcFriend, method=Quests.WithCheat): + numCalls = simbase.air.config.GetInt('sos-card-reward', 1) + + if numCalls <= 0: + self.notify.warning('invalid numCalls: %d' % numCalls) + return 0 + if npcFriend in self.NPCFriendsDict: + self.NPCFriendsDict[npcFriend] += numCalls + elif npcFriend in npcFriends: + self.NPCFriendsDict[npcFriend] = numCalls + else: + self.notify.warning('invalid NPC: %d' % npcFriend) + return 0 + if self.NPCFriendsDict[npcFriend] > self.maxCallsPerNPC: + self.NPCFriendsDict[npcFriend] = self.maxCallsPerNPC + self.d_setNPCFriendsDict(self.NPCFriendsDict) + self.air.questManager.toonMadeNPCFriend(self, numCalls, method) + self.addStat(ToontownGlobals.STAT_SOS, numCalls) + return 1 + + def attemptSubtractNPCFriend(self, npcFriend): + if npcFriend not in self.NPCFriendsDict: + self.notify.warning('attemptSubtractNPCFriend: invalid NPC %s' % npcFriend) + return 0 + if hasattr(self, 'autoRestockSOS') and self.autoRestockSOS: + cost = 0 + else: + cost = 1 + self.NPCFriendsDict[npcFriend] -= cost + if self.NPCFriendsDict[npcFriend] <= 0: + del self.NPCFriendsDict[npcFriend] + self.d_setNPCFriendsDict(self.NPCFriendsDict) + return 1 + + def restockAllNPCFriends(self): + desiredNpcFriends = [2001, 2011, 3112, 4119, 1116, 3137, 3135] + self.resetNPCFriendsDict() + for npcId in desiredNpcFriends: + self.attemptAddNPCFriend(npcId) + + def isTrunkFull(self, extraAccessories = 0): + numAccessories = (len(self.hatList) + len(self.glassesList) + len(self.backpackList) + len(self.shoesList)) / 3 + return numAccessories + extraAccessories >= ToontownGlobals.MaxAccessories + + def d_setHatList(self, clothesList): + self.sendUpdate('setHatList', [clothesList]) + + def setHatList(self, clothesList): + self.hatList = clothesList + + def b_setHatList(self, clothesList): + self.setHatList(clothesList) + self.d_setHatList(clothesList) + + def getHatList(self): + return self.hatList + + def d_setGlassesList(self, clothesList): + self.sendUpdate('setGlassesList', [clothesList]) + + def setGlassesList(self, clothesList): + self.glassesList = clothesList + + def b_setGlassesList(self, clothesList): + self.setGlassesList(clothesList) + self.d_setGlassesList(clothesList) + + def getGlassesList(self): + return self.glassesList + + def d_setBackpackList(self, clothesList): + self.sendUpdate('setBackpackList', [clothesList]) + + def setBackpackList(self, clothesList): + self.backpackList = clothesList + + def b_setBackpackList(self, clothesList): + self.setBackpackList(clothesList) + self.d_setBackpackList(clothesList) + + def getBackpackList(self): + return self.backpackList + + def d_setShoesList(self, clothesList): + self.sendUpdate('setShoesList', [clothesList]) + return None + + def setShoesList(self, clothesList): + self.shoesList = clothesList + + def b_setShoesList(self, clothesList): + self.setShoesList(clothesList) + self.d_setShoesList(clothesList) + + def getShoesList(self): + return self.shoesList + + def addToAccessoriesList(self, accessoryType, geomIdx, texIdx, colorIdx): + if self.isTrunkFull(): + return 0 + if accessoryType == ToonDNA.HAT: + itemList = self.hatList + elif accessoryType == ToonDNA.GLASSES: + itemList = self.glassesList + elif accessoryType == ToonDNA.BACKPACK: + itemList = self.backpackList + elif accessoryType == ToonDNA.SHOES: + itemList = self.shoesList + else: + return 0 + index = 0 + for i in xrange(0, len(itemList), 3): + if itemList[i] == geomIdx and itemList[i + 1] == texIdx and itemList[i + 2] == colorIdx: + return 0 + + if accessoryType == ToonDNA.HAT: + self.hatList.append(geomIdx) + self.hatList.append(texIdx) + self.hatList.append(colorIdx) + elif accessoryType == ToonDNA.GLASSES: + self.glassesList.append(geomIdx) + self.glassesList.append(texIdx) + self.glassesList.append(colorIdx) + elif accessoryType == ToonDNA.BACKPACK: + self.backpackList.append(geomIdx) + self.backpackList.append(texIdx) + self.backpackList.append(colorIdx) + elif accessoryType == ToonDNA.SHOES: + self.shoesList.append(geomIdx) + self.shoesList.append(texIdx) + self.shoesList.append(colorIdx) + return 1 + + def replaceItemInAccessoriesList(self, accessoryType, geomIdxA, texIdxA, colorIdxA, geomIdxB, texIdxB, colorIdxB): + if accessoryType == ToonDNA.HAT: + itemList = self.hatList + elif accessoryType == ToonDNA.GLASSES: + itemList = self.glassesList + elif accessoryType == ToonDNA.BACKPACK: + itemList = self.backpackList + elif accessoryType == ToonDNA.SHOES: + itemList = self.shoesList + else: + return 0 + index = 0 + for i in xrange(0, len(itemList), 3): + if itemList[i] == geomIdxA and itemList[i + 1] == texIdxA and itemList[i + 2] == colorIdxA: + if accessoryType == ToonDNA.HAT: + self.hatList[i] = geomIdxB + self.hatList[i + 1] = texIdxB + self.hatList[i + 2] = colorIdxB + elif accessoryType == ToonDNA.GLASSES: + self.glassesList[i] = geomIdxB + self.glassesList[i + 1] = texIdxB + self.glassesList[i + 2] = colorIdxB + elif accessoryType == ToonDNA.BACKPACK: + self.backpackList[i] = geomIdxB + self.backpackList[i + 1] = texIdxB + self.backpackList[i + 2] = colorIdxB + else: + self.shoesList[i] = geomIdxB + self.shoesList[i + 1] = texIdxB + self.shoesList[i + 2] = colorIdxB + return 1 + + return 0 + + def hasAccessory(self, accessoryType, geomIdx, texIdx, colorIdx): + if accessoryType == ToonDNA.HAT: + itemList = self.hatList + cur = self.hat + elif accessoryType == ToonDNA.GLASSES: + itemList = self.glassesList + cur = self.glasses + elif accessoryType == ToonDNA.BACKPACK: + itemList = self.backpackList + cur = self.backpack + elif accessoryType == ToonDNA.SHOES: + itemList = self.shoesList + cur = self.shoes + else: + raise 'invalid accessory type %s' % accessoryType + if cur == (geomIdx, texIdx, colorIdx): + return True + for i in xrange(0, len(itemList), 3): + if itemList[i] == geomIdx and itemList[i + 1] == texIdx and itemList[i + 2] == colorIdx: + return True + + return False + + def isValidAccessorySetting(self, accessoryType, geomIdx, texIdx, colorIdx): + if not geomIdx and not texIdx and not colorIdx: + return True + return self.hasAccessory(accessoryType, geomIdx, texIdx, colorIdx) + + def removeItemInAccessoriesList(self, accessoryType, geomIdx, texIdx, colorIdx): + if accessoryType == ToonDNA.HAT: + itemList = self.hatList + elif accessoryType == ToonDNA.GLASSES: + itemList = self.glassesList + elif accessoryType == ToonDNA.BACKPACK: + itemList = self.backpackList + elif accessoryType == ToonDNA.SHOES: + itemList = self.shoesList + else: + return 0 + listLen = len(itemList) + if listLen < 3: + self.notify.warning('Accessory list is not long enough to delete anything') + return 0 + index = 0 + for i in xrange(0, len(itemList), 3): + if itemList[i] == geomIdx and itemList[i + 1] == texIdx and itemList[i + 2] == colorIdx: + itemList = itemList[0:i] + itemList[i + 3:listLen] + if accessoryType == ToonDNA.HAT: + self.hatList = itemList[:] + styles = ToonDNA.HatStyles + descDict = TTLocalizer.HatStylesDescriptions + elif accessoryType == ToonDNA.GLASSES: + self.glassesList = itemList[:] + styles = ToonDNA.GlassesStyles + descDict = TTLocalizer.GlassesStylesDescriptions + elif accessoryType == ToonDNA.BACKPACK: + self.backpackList = itemList[:] + styles = ToonDNA.BackpackStyles + descDict = TTLocalizer.BackpackStylesDescriptions + elif accessoryType == ToonDNA.SHOES: + self.shoesList = itemList[:] + styles = ToonDNA.ShoesStyles + descDict = TTLocalizer.ShoesStylesDescriptions + styleName = 'none' + for style in styles.items(): + if style[1] == [geomIdx, texIdx, colorIdx]: + styleName = style[0] + break + + if styleName == 'none' or styleName not in descDict: + self.air.writeServerEvent('suspicious', self.doId, ' tried to remove wrong accessory code %d %d %d' % (geomIdx, texIdx, colorIdx)) + else: + self.air.writeServerEvent('accessory', self.doId, ' removed accessory %s' % descDict[styleName]) + return 1 + + return 0 + + def d_setMaxClothes(self, max): + self.sendUpdate('setMaxClothes', [self.maxClothes]) + + def setMaxClothes(self, max): + self.maxClothes = max + + def b_setMaxClothes(self, max): + self.setMaxClothes(max) + self.d_setMaxClothes(max) + + def getMaxClothes(self): + return self.maxClothes + + def isClosetFull(self, extraClothes = 0): + numClothes = len(self.clothesTopsList) / 4 + len(self.clothesBottomsList) / 2 + return numClothes + extraClothes >= self.maxClothes + + def d_setClothesTopsList(self, clothesList): + self.sendUpdate('setClothesTopsList', [clothesList]) + + def setClothesTopsList(self, clothesList): + self.clothesTopsList = clothesList + + def b_setClothesTopsList(self, clothesList): + self.setClothesTopsList(clothesList) + self.d_setClothesTopsList(clothesList) + + def getClothesTopsList(self): + return self.clothesTopsList + + def addToClothesTopsList(self, topTex, topTexColor, sleeveTex, sleeveTexColor): + if self.isClosetFull(1): + return 0 + index = 0 + for i in xrange(0, len(self.clothesTopsList), 4): + if self.clothesTopsList[i] == topTex and self.clothesTopsList[i + 1] == topTexColor and self.clothesTopsList[i + 2] == sleeveTex and self.clothesTopsList[i + 3] == sleeveTexColor: + return 0 + + self.clothesTopsList.append(topTex) + self.clothesTopsList.append(topTexColor) + self.clothesTopsList.append(sleeveTex) + self.clothesTopsList.append(sleeveTexColor) + return 1 + + def replaceItemInClothesTopsList(self, topTexA, topTexColorA, sleeveTexA, sleeveTexColorA, topTexB, topTexColorB, sleeveTexB, sleeveTexColorB): + index = 0 + for i in xrange(0, len(self.clothesTopsList), 4): + if self.clothesTopsList[i] == topTexA and self.clothesTopsList[i + 1] == topTexColorA and self.clothesTopsList[i + 2] == sleeveTexA and self.clothesTopsList[i + 3] == sleeveTexColorA: + self.clothesTopsList[i] = topTexB + self.clothesTopsList[i + 1] = topTexColorB + self.clothesTopsList[i + 2] = sleeveTexB + self.clothesTopsList[i + 3] = sleeveTexColorB + return 1 + + return 0 + + def removeItemInClothesTopsList(self, topTex, topTexColor, sleeveTex, sleeveTexColor): + listLen = len(self.clothesTopsList) + if listLen < 4: + self.notify.warning('Clothes top list is not long enough to delete anything') + return 0 + index = 0 + for i in xrange(0, listLen, 4): + if self.clothesTopsList[i] == topTex and self.clothesTopsList[i + 1] == topTexColor and self.clothesTopsList[i + 2] == sleeveTex and self.clothesTopsList[i + 3] == sleeveTexColor: + self.clothesTopsList = self.clothesTopsList[0:i] + self.clothesTopsList[i + 4:listLen] + return 1 + + return 0 + + def d_setClothesBottomsList(self, clothesList): + self.sendUpdate('setClothesBottomsList', [clothesList]) + + def setClothesBottomsList(self, clothesList): + self.clothesBottomsList = clothesList + + def b_setClothesBottomsList(self, clothesList): + self.setClothesBottomsList(clothesList) + self.d_setClothesBottomsList(clothesList) + + def getClothesBottomsList(self): + return self.clothesBottomsList + + def addToClothesBottomsList(self, botTex, botTexColor): + if self.isClosetFull(1): + self.notify.warning('clothes bottoms list is full') + return 0 + index = 0 + for i in xrange(0, len(self.clothesBottomsList), 2): + if self.clothesBottomsList[i] == botTex and self.clothesBottomsList[i + 1] == botTexColor: + return 0 + + self.clothesBottomsList.append(botTex) + self.clothesBottomsList.append(botTexColor) + return 1 + + def replaceItemInClothesBottomsList(self, botTexA, botTexColorA, botTexB, botTexColorB): + index = 0 + for i in xrange(0, len(self.clothesBottomsList), 2): + if self.clothesBottomsList[i] == botTexA and self.clothesBottomsList[i + 1] == botTexColorA: + self.clothesBottomsList[i] = botTexB + self.clothesBottomsList[i + 1] = botTexColorB + return 1 + + return 0 + + def removeItemInClothesBottomsList(self, botTex, botTexColor): + listLen = len(self.clothesBottomsList) + if listLen < 2: + self.notify.warning('Clothes bottoms list is not long enough to delete anything') + return 0 + index = 0 + for i in xrange(0, len(self.clothesBottomsList), 2): + if self.clothesBottomsList[i] == botTex and self.clothesBottomsList[i + 1] == botTexColor: + self.clothesBottomsList = self.clothesBottomsList[0:i] + self.clothesBottomsList[i + 2:listLen] + return 1 + + return 0 + + def d_catalogGenClothes(self): + self.sendUpdate('catalogGenClothes', [self.doId]) + + def d_catalogGenAccessories(self): + self.sendUpdate('catalogGenAccessories', [self.doId]) + + def takeDamage(self, hpLost, quietly = 0, sendTotal = 1): + if not self.immortalMode: + if not quietly: + self.sendUpdate('takeDamage', [hpLost]) + if hpLost > 0 and self.hp > 0: + self.hp -= hpLost + if self.hp <= 0: + self.hp = -1 + if not self.hpOwnedByBattle: + self.hp = min(self.hp, self.maxHp) + if sendTotal: + self.d_setHp(self.hp) + + if self.hp <= 0: + self.addStat(ToontownGlobals.STAT_SAD) + + def b_setMaxHp(self, maxHp): + if self.maxHp == maxHp: + return + + if (maxHp > ToontownGlobals.MaxHpLimit): + self.air.writeServerEvent('suspicious', self.doId, 'Toon tried to go over the HP limit.') + self.d_setMaxHp(ToontownGlobals.MaxHpLimit) + self.setMaxHp(ToontownGlobals.MaxHpLimit) + else: + self.d_setMaxHp(maxHp) + self.setMaxHp(maxHp) + + def d_setMaxHp(self, maxHp): + self.sendUpdate('setMaxHp', [maxHp]) + + def b_setTutorialAck(self, tutorialAck): + self.d_setTutorialAck(tutorialAck) + self.setTutorialAck(tutorialAck) + + def d_setTutorialAck(self, tutorialAck): + self.sendUpdate('setTutorialAck', [tutorialAck]) + + def setTutorialAck(self, tutorialAck): + self.tutorialAck = tutorialAck + + def getTutorialAck(self): + return self.tutorialAck + + def d_setEarnedExperience(self, earnedExp): + self.sendUpdate('setEarnedExperience', [earnedExp]) + + def setHoodsVisited(self, hoods): + self.hoodsVisited = hoods + self.notify.debug('setting hood zone list to %s' % self.hoodsVisited) + + def getHoodsVisited(self): + return self.hoodsVisited + + def setLastHood(self, hood): + self.lastHood = hood + + def d_setLastHood(self, hood): + self.sendUpdate('setLastHood', [hood]) + + def b_setLastHood(self, hood): + if hood != self.lastHood: + self.setLastHood(hood) + self.d_setLastHood(hood) + + def getLastHood(self): + return self.lastHood + + def b_setAnimState(self, animName, animMultiplier): + self.setAnimState(animName, animMultiplier) + self.d_setAnimState(animName, animMultiplier) + + def d_setAnimState(self, animName, animMultiplier): + timestamp = globalClockDelta.getRealNetworkTime() + self.sendUpdate('setAnimState', [animName, animMultiplier, timestamp]) + return None + + def setAnimState(self, animName, animMultiplier, timestamp = 0): + if animName not in ToontownGlobals.ToonAnimStates: + desc = 'tried to set invalid animState: %s' % (animName,) + if config.GetBool('want-ban-animstate', 1): + #simbase.air.banManager.ban(self.doId, self.DISLid, desc) + pass + else: + self.air.writeServerEvent('suspicious', self.doId, desc) + return + self.animName = animName + self.animMultiplier = animMultiplier + + def b_setCogStatus(self, cogStatusList): + self.setCogStatus(cogStatusList) + self.d_setCogStatus(cogStatusList) + + def setCogStatus(self, cogStatusList): + self.notify.debug('setting cogs to %s' % cogStatusList) + self.cogs = cogStatusList + + def d_setCogStatus(self, cogStatusList): + self.sendUpdate('setCogStatus', [cogStatusList]) + + def getCogStatus(self): + return self.cogs + + def b_setCogCount(self, cogCountList): + self.setCogCount(cogCountList) + self.d_setCogCount(cogCountList) + + def setCogCount(self, cogCountList): + self.notify.debug('setting cogCounts to %s' % cogCountList) + self.cogCounts = cogCountList + + def d_setCogCount(self, cogCountList): + self.sendUpdate('setCogCount', [cogCountList]) + + def getCogCount(self): + return self.cogCounts + + def b_setCogRadar(self, radar): + self.setCogRadar(radar) + self.d_setCogRadar(radar) + + def setCogRadar(self, radar): + if not radar: + self.notify.warning('cogRadar set to bad value: %s. Resetting to [0,0,0,0]' % radar) + self.cogRadar = [0, + 0, + 0, + 0] + else: + self.cogRadar = radar + + def d_setCogRadar(self, radar): + self.sendUpdate('setCogRadar', [radar]) + + def getCogRadar(self): + return self.cogRadar + + def b_setBuildingRadar(self, radar): + self.setBuildingRadar(radar) + self.d_setBuildingRadar(radar) + + def setBuildingRadar(self, radar): + if not radar: + self.notify.warning('buildingRadar set to bad value: %s. Resetting to [0,0,0,0]' % radar) + self.buildingRadar = [0, + 0, + 0, + 0] + else: + self.buildingRadar = radar + + def d_setBuildingRadar(self, radar): + self.sendUpdate('setBuildingRadar', [radar]) + + def getBuildingRadar(self): + return self.buildingRadar + + def b_setCogTypes(self, types): + self.setCogTypes(types) + self.d_setCogTypes(types) + + def setCogTypes(self, types): + if not types: + self.notify.warning('cogTypes set to bad value: %s. Resetting to [0,0,0,0]' % types) + self.cogTypes = [0, + 0, + 0, + 0] + else: + for i in xrange(len(types)): + if types[i] == SuitDNA.suitsPerDept - 1: + zoneId = SuitDNA.suitDeptZones[i] + tpAccess = self.getTeleportAccess() + + if zoneId not in tpAccess: + tpAccess.append(zoneId) + self.b_setTeleportAccess(tpAccess) + self.cogTypes = types + + def d_setCogTypes(self, types): + self.sendUpdate('setCogTypes', [types]) + + def getCogTypes(self): + return self.cogTypes + + def b_setCogLevels(self, levels): + self.setCogLevels(levels) + self.d_setCogLevels(levels) + + def setCogLevels(self, levels): + if not levels: + self.notify.warning('cogLevels set to bad value: %s. Resetting to [0,0,0,0]' % levels) + self.cogLevels = [0, + 0, + 0, + 0] + else: + self.cogLevels = levels + + def d_setCogLevels(self, levels): + self.sendUpdate('setCogLevels', [levels]) + + def getCogLevels(self): + return self.cogLevels + + def incCogLevel(self, dept): + newLevel = self.cogLevels[dept] + 1 + cogTypeStr = SuitDNA.suitHeadTypes[self.cogTypes[dept]] + lastCog = self.cogTypes[dept] >= SuitDNA.suitsPerDept - 1 + if not lastCog: + maxLevel = SuitBattleGlobals.SuitAttributes[cogTypeStr]['level'] + 4 + else: + maxLevel = ToontownGlobals.MaxCogSuitLevel + if newLevel > maxLevel: + if not lastCog: + self.cogTypes[dept] += 1 + self.d_setCogTypes(self.cogTypes) + cogTypeStr = SuitDNA.suitHeadTypes[self.cogTypes[dept]] + self.cogLevels[dept] = SuitBattleGlobals.SuitAttributes[cogTypeStr]['level'] + self.d_setCogLevels(self.cogLevels) + else: + self.cogLevels[dept] += 1 + self.d_setCogLevels(self.cogLevels) + if lastCog: + if self.cogLevels[dept] in ToontownGlobals.CogSuitHPLevels: + maxHp = self.getMaxHp() + maxHp = min(ToontownGlobals.MaxHpLimit, maxHp + 1) + self.b_setMaxHp(maxHp) + self.toonUp(maxHp) + self.air.writeServerEvent('cogSuit', self.doId, '%s|%s|%s' % (dept, self.cogTypes[dept], self.cogLevels[dept])) + + def getNumPromotions(self, dept): + if dept not in SuitDNA.suitDepts: + self.notify.warning('getNumPromotions: Invalid parameter dept=%s' % dept) + return 0 + deptIndex = SuitDNA.suitDepts.index(dept) + cogType = self.cogTypes[deptIndex] + cogTypeStr = SuitDNA.suitHeadTypes[cogType] + lowestCogLevel = SuitBattleGlobals.SuitAttributes[cogTypeStr]['level'] + multiple = 5 * cogType + additional = self.cogLevels[deptIndex] - lowestCogLevel + numPromotions = multiple + additional + return numPromotions + + def b_setCogParts(self, parts): + self.setCogParts(parts) + self.d_setCogParts(parts) + + def setCogParts(self, parts): + if not parts: + self.notify.warning('cogParts set to bad value: %s. Resetting to [0,0,0,0]' % parts) + self.cogParts = [0, + 0, + 0, + 0] + else: + self.cogParts = parts + + def d_setCogParts(self, parts): + self.sendUpdate('setCogParts', [parts]) + + def getCogParts(self): + return self.cogParts + + def giveCogPart(self, part, dept): + dept = CogDisguiseGlobals.dept2deptIndex(dept) + parts = self.getCogParts() + parts[dept] = parts[dept] | part + self.b_setCogParts(parts) + + def hasCogPart(self, part, dept): + dept = CogDisguiseGlobals.dept2deptIndex(dept) + if self.cogParts[dept] & part: + return 1 + else: + return 0 + + def giveGenericCogPart(self, factoryType, dept): + for partTypeId in self.partTypeIds[factoryType]: + nextPart = CogDisguiseGlobals.getNextPart(self.getCogParts(), partTypeId, dept) + if nextPart: + break + if nextPart: + self.giveCogPart(nextPart, dept) + return nextPart + + def takeCogPart(self, part, dept): + dept = CogDisguiseGlobals.dept2deptIndex(dept) + parts = self.getCogParts() + if parts[dept] & part: + parts[dept] = parts[dept] ^ part + self.b_setCogParts(parts) + + def loseCogParts(self, dept): + loseCount = random.randrange(CogDisguiseGlobals.MinPartLoss, CogDisguiseGlobals.MaxPartLoss + 1) + parts = self.getCogParts() + partBitmask = parts[dept] + partList = range(17) + while loseCount > 0 and partList: + losePart = random.choice(partList) + partList.remove(losePart) + losePartBit = 1 << losePart + if partBitmask & losePartBit: + partBitmask &= ~losePartBit + loseCount -= 1 + + parts[dept] = partBitmask + self.b_setCogParts(parts) + + def b_setCogMerits(self, merits): + self.setCogMerits(merits) + self.d_setCogMerits(merits) + + def setCogMerits(self, merits): + if not merits: + self.notify.warning('cogMerits set to bad value: %s. Resetting to [0,0,0,0]' % merits) + self.cogMerits = [0, + 0, + 0, + 0] + else: + self.cogMerits = merits + + def d_setCogMerits(self, merits): + self.sendUpdate('setCogMerits', [merits]) + + def getCogMerits(self): + return self.cogMerits + + def b_promote(self, dept): + oldMerits = CogDisguiseGlobals.getTotalMerits(self, dept) + self.incCogLevel(dept) + + if self.cogLevels[dept] < ToontownGlobals.MaxCogSuitLevel: + merits = self.getCogMerits() + + if not self.hasEPP(dept): + merits[dept] = 0 + + else: + # If we have EPP, check if the merit count is too much (i.e. enough to promote again) + if oldMerits >= CogDisguiseGlobals.getTotalMerits(self, dept): + # We have more merits than needed (i.e. promoting to another cog or earning laff) + # Therefore: + merits[dept] = 0 + + else: + merits[dept] = oldMerits + + self.d_setCogMerits(merits) + + def readyForPromotion(self, dept): + merits = self.cogMerits[dept] + totalMerits = CogDisguiseGlobals.getTotalMerits(self, dept) + if merits >= totalMerits: + return 1 + else: + return 0 + + def b_setCogIndex(self, index): + self.setCogIndex(index) + if simbase.config.GetBool('cogsuit-hack-prevent', False): + self.d_setCogIndex(self.cogIndex) + else: + self.d_setCogIndex(index) + + def setCogIndex(self, index): + if index != -1 and not ZoneUtil.canWearSuit(self.zoneId): + if not simbase.air.cogSuitMessageSent: + self.notify.warning('%s setCogIndex invalid: %s' % (self.doId, index)) + if simbase.config.GetBool('want-ban-wrong-suit-place', False): + commentStr = 'Toon %s trying to set cog index to %s in Zone: %s' % (self.doId, index, self.zoneId) + #simbase.air.banManager.ban(self.doId, self.DISLid, commentStr) + else: + self.cogIndex = index + + def d_setCogIndex(self, index): + self.sendUpdate('setCogIndex', [index]) + + def getCogIndex(self): + return self.cogIndex + + def b_setDisguisePageFlag(self, flag): + self.setDisguisePageFlag(flag) + self.d_setDisguisePageFlag(flag) + + def setDisguisePageFlag(self, flag): + self.disguisePageFlag = flag + + def d_setDisguisePageFlag(self, flag): + self.sendUpdate('setDisguisePageFlag', [flag]) + + def getDisguisePageFlag(self): + return self.disguisePageFlag + + def b_setSosPageFlag(self, flag): + self.setSosPageFlag(flag) + self.d_setSosPageFlag(flag) + + def setSosPageFlag(self, flag): + self.sosPageFlag = flag + + def d_setSosPageFlag(self, flag): + self.sendUpdate('setSosPageFlag', [flag]) + + def getSosPageFlag(self): + return self.sosPageFlag + + def b_setFishCollection(self, genusList, speciesList, weightList): + self.setFishCollection(genusList, speciesList, weightList) + self.d_setFishCollection(genusList, speciesList, weightList) + + def d_setFishCollection(self, genusList, speciesList, weightList): + self.sendUpdate('setFishCollection', [genusList, speciesList, weightList]) + + def setFishCollection(self, genusList, speciesList, weightList): + self.fishCollection = FishCollection.FishCollection() + self.fishCollection.makeFromNetLists(genusList, speciesList, weightList) + + def getFishCollection(self): + return self.fishCollection.getNetLists() + + def b_setMaxFishTank(self, maxTank): + self.d_setMaxFishTank(maxTank) + self.setMaxFishTank(maxTank) + + def d_setMaxFishTank(self, maxTank): + self.sendUpdate('setMaxFishTank', [maxTank]) + + def setMaxFishTank(self, maxTank): + self.maxFishTank = maxTank + + def getMaxFishTank(self): + return self.maxFishTank + + def b_setFishTank(self, genusList, speciesList, weightList): + self.setFishTank(genusList, speciesList, weightList) + self.d_setFishTank(genusList, speciesList, weightList) + + def d_setFishTank(self, genusList, speciesList, weightList): + self.sendUpdate('setFishTank', [genusList, speciesList, weightList]) + + def setFishTank(self, genusList, speciesList, weightList): + self.fishTank = FishTank.FishTank() + self.fishTank.makeFromNetLists(genusList, speciesList, weightList) + + def getFishTank(self): + return self.fishTank.getNetLists() + + def makeRandomFishTank(self): + self.fishTank.generateRandomTank() + self.d_setFishTank(*self.fishTank.getNetLists()) + + def addFishToTank(self, fish): + numFish = len(self.fishTank) + if numFish >= self.maxFishTank: + self.notify.warning('addFishToTank: cannot add fish, tank is full') + return 0 + elif self.fishTank.addFish(fish): + self.d_setFishTank(*self.fishTank.getNetLists()) + return 1 + else: + self.notify.warning('addFishToTank: addFish failed') + return 0 + + def removeFishFromTankAtIndex(self, index): + if self.fishTank.removeFishAtIndex(index): + self.d_setFishTank(*self.fishTank.getNetLists()) + return 1 + else: + self.notify.warning('removeFishFromTank: cannot find fish') + return 0 + + def b_setFishingRod(self, rodId): + self.d_setFishingRod(rodId) + self.setFishingRod(rodId) + + def d_setFishingRod(self, rodId): + self.sendUpdate('setFishingRod', [rodId]) + + def setFishingRod(self, rodId): + self.fishingRod = rodId + + def getFishingRod(self): + return self.fishingRod + + def b_setMaxFishingRod(self, rodId): + if (not 0 <= rodId <= 4) or rodId <= self.maxFishingRod: + return + + self.d_setMaxFishingRod(rodId) + self.setMaxFishingRod(rodId) + + def d_setMaxFishingRod(self, rodId): + self.sendUpdate('setMaxFishingRod', [rodId]) + + def setMaxFishingRod(self, rodId): + self.maxFishingRod = rodId + + def getMaxFishingRod(self): + return self.maxFishingRod + + def requestFishingRod(self, rodId): + if not 0 <= rodId <= self.maxFishingRod: + return + + self.b_setFishingRod(rodId) + + def b_setFishingTrophies(self, trophyList): + self.setFishingTrophies(trophyList) + self.d_setFishingTrophies(trophyList) + + def setFishingTrophies(self, trophyList): + self.notify.debug('setting fishingTrophies to %s' % trophyList) + self.fishingTrophies = trophyList + + def d_setFishingTrophies(self, trophyList): + self.sendUpdate('setFishingTrophies', [trophyList]) + + def getFishingTrophies(self): + return self.fishingTrophies + + def b_setQuests(self, questList): + flattenedQuests = [] + for quest in questList: + flattenedQuests.extend(quest) + + self.setQuests(flattenedQuests) + self.d_setQuests(flattenedQuests) + + def d_setQuests(self, flattenedQuests): + self.sendUpdate('setQuests', [flattenedQuests]) + + def setQuests(self, flattenedQuests): + self.notify.debug('setting quests to %s' % flattenedQuests) + questList = [] + questLen = 5 + for i in xrange(0, len(flattenedQuests), questLen): + questList.append(flattenedQuests[i:i + questLen]) + + self.quests = questList + + def getQuests(self): + flattenedQuests = [] + for quest in self.quests: + flattenedQuests.extend(quest) + + return flattenedQuests + + def getQuest(self, questId, visitNpcId = None, rewardId = None): + for quest in self.quests: + if quest[0] != questId: + continue + if visitNpcId != None: + if visitNpcId != quest[1] and visitNpcId != quest[2]: + continue + if rewardId != None: + if rewardId != quest[3]: + continue + return quest + + return + + def hasQuest(self, questId, visitNpcId = None, rewardId = None): + if self.getQuest(questId, visitNpcId=visitNpcId, rewardId=rewardId) == None: + return False + else: + return True + return + + def removeQuest(self, id, visitNpcId = None): + index = -1 + for i in xrange(len(self.quests)): + if self.quests[i][0] == id: + if visitNpcId: + otherId = self.quests[i][2] + if visitNpcId == otherId: + index = i + break + else: + index = i + break + + if index >= 0: + del self.quests[i] + self.b_setQuests(self.quests) + return 1 + else: + return 0 + + def addQuest(self, quest, finalReward, recordHistory = 1): + self.quests.append(quest) + self.b_setQuests(self.quests) + if recordHistory: + if quest[0] != Quests.VISIT_QUEST_ID: + newQuestHistory = self.questHistory + [quest[0]] + while newQuestHistory.count(Quests.VISIT_QUEST_ID) != 0: + newQuestHistory.remove(Quests.VISIT_QUEST_ID) + + self.b_setQuestHistory(newQuestHistory) + if finalReward: + newRewardHistory = self.rewardHistory + [finalReward] + self.b_setRewardHistory(self.rewardTier, newRewardHistory) + + def removeAllTracesOfQuest(self, questId, rewardId): + self.notify.debug('removeAllTracesOfQuest: questId: %s rewardId: %s' % (questId, rewardId)) + self.notify.debug('removeAllTracesOfQuest: quests before: %s' % self.quests) + removedQuest = self.removeQuest(questId) + self.notify.debug('removeAllTracesOfQuest: quests after: %s' % self.quests) + self.notify.debug('removeAllTracesOfQuest: questHistory before: %s' % self.questHistory) + removedQuestHistory = self.removeQuestFromHistory(questId) + self.notify.debug('removeAllTracesOfQuest: questHistory after: %s' % self.questHistory) + self.notify.debug('removeAllTracesOfQuest: reward history before: %s' % self.rewardHistory) + removedRewardHistory = self.removeRewardFromHistory(rewardId) + self.notify.debug('removeAllTracesOfQuest: reward history after: %s' % self.rewardHistory) + return (removedQuest, removedQuestHistory, removedRewardHistory) + + def requestDeleteQuest(self, questDesc): + if len(questDesc) != 5: + self.air.writeServerEvent('suspicious', self.doId, 'Toon tried to delete invalid questDesc %s' % str(questDesc)) + self.notify.warning('%s.requestDeleteQuest(%s) -- questDesc has incorrect params' % (self, str(questDesc))) + return + questId = questDesc[0] + rewardId = questDesc[3] + if not self.hasQuest(questId, rewardId=rewardId): + self.air.writeServerEvent('suspicious', self.doId, "Toon tried to delete quest they don't have %s" % str(questDesc)) + self.notify.warning("%s.requestDeleteQuest(%s) -- Toon doesn't have that quest" % (self, str(questDesc))) + return + if not Quests.isQuestJustForFun(questId, rewardId): + self.air.writeServerEvent('suspicious', self.doId, 'Toon tried to delete non-Just For Fun quest %s' % str(questDesc)) + self.notify.warning('%s.requestDeleteQuest(%s) -- Tried to cancel non-Just For Fun quest' % (self, str(questDesc))) + return + removedStatus = self.removeAllTracesOfQuest(questId, rewardId) + if 0 in removedStatus: + self.notify.warning('%s.requestDeleteQuest(%s) -- Failed to remove quest, status=%s' % (self, str(questDesc), removedStatus)) + + def b_setQuestCarryLimit(self, limit): + self.setQuestCarryLimit(limit) + self.d_setQuestCarryLimit(limit) + + def d_setQuestCarryLimit(self, limit): + self.sendUpdate('setQuestCarryLimit', [limit]) + + def setQuestCarryLimit(self, limit): + self.notify.debug('setting questCarryLimit to %s' % limit) + self.questCarryLimit = limit + + def getQuestCarryLimit(self): + return self.questCarryLimit + + def b_setMaxCarry(self, maxCarry): + self.setMaxCarry(maxCarry) + self.d_setMaxCarry(maxCarry) + + def d_setMaxCarry(self, maxCarry): + self.sendUpdate('setMaxCarry', [maxCarry]) + + def setMaxCarry(self, maxCarry): + self.maxCarry = maxCarry + + def getMaxCarry(self): + return self.maxCarry + + def b_setCheesyEffect(self, effect, hoodId, expireTime): + self.setCheesyEffect(effect, hoodId, expireTime) + self.d_setCheesyEffect(effect, hoodId, expireTime) + + def d_setCheesyEffect(self, effect, hoodId, expireTime): + self.sendUpdate('setCheesyEffect', [effect, hoodId, expireTime]) + + def setCheesyEffect(self, effect, hoodId, expireTime): + if (not simbase.air.newsManager.isHolidayRunning(ToontownGlobals.CHRISTMAS)) and effect == ToontownGlobals.CESnowMan: + self.b_setCheesyEffect(ToontownGlobals.CENormal, hoodId, expireTime) + self.b_setScavengerHunt([]) + return + elif (not simbase.air.newsManager.isHolidayRunning(ToontownGlobals.HALLOWEEN)) and effect == ToontownGlobals.CEPumpkin: + self.b_setCheesyEffect(ToontownGlobals.CENormal, hoodId, expireTime) + self.b_setScavengerHunt([]) + return + + self.savedCheesyEffect = effect + self.savedCheesyHoodId = hoodId + self.savedCheesyExpireTime = expireTime + taskName = self.uniqueName('cheesy-expires') + taskMgr.remove(taskName) + if expireTime and (effect != ToontownGlobals.CENormal): + duration = expireTime * 60 - time.time() + if duration > 0: + taskMgr.doMethodLater(duration, self.__undoCheesyEffect, taskName) + else: + self.__undoCheesyEffect(None) + + def getCheesyEffect(self): + return (self.savedCheesyEffect, self.savedCheesyHoodId, self.savedCheesyExpireTime) + + def __undoCheesyEffect(self, task): + self.b_setCheesyEffect(ToontownGlobals.CENormal, 0, 0) + return Task.cont + + def b_setTrackAccess(self, trackArray): + self.setTrackAccess(trackArray) + self.d_setTrackAccess(trackArray) + + def d_setTrackAccess(self, trackArray): + self.sendUpdate('setTrackAccess', [trackArray]) + + def setTrackAccess(self, trackArray): + self.trackArray = trackArray + + def getTrackAccess(self): + return self.trackArray + + def addTrackAccess(self, track): + self.trackArray[track] = 1 + self.b_setTrackAccess(self.trackArray) + + def removeTrackAccess(self, track): + self.trackArray[track] = 0 + self.b_setTrackAccess(self.trackArray) + + def hasTrackAccess(self, track): + if self.trackArray and track < len(self.trackArray): + return self.trackArray[track] + else: + return 0 + + def fixTrackAccess(self): + fixed = 0 + healExp, trapExp, lureExp, soundExp, throwExp, squirtExp, dropExp = self.experience.experience + numTracks = reduce(lambda a, b: a + b, self.trackArray) + if self.rewardTier in [0, + 1, + 2, + 3]: + if numTracks != 2: + self.notify.warning('bad num tracks in tier: %s, %s' % (self.rewardTier, self.trackArray)) + self.b_setTrackAccess([0, 0, 0, 0, 1, 1, 0]) + fixed = 1 + elif self.rewardTier in [4, 5, 6]: + if numTracks != 3: + self.notify.warning('bad num tracks in tier: %s, %s' % (self.rewardTier, self.trackArray)) + if self.trackArray[ToontownBattleGlobals.SOUND_TRACK] and not self.trackArray[ToontownBattleGlobals.HEAL_TRACK]: + self.b_setTrackAccess([0, 0, 0, 1, 1, 1, 0]) + elif self.trackArray[ToontownBattleGlobals.HEAL_TRACK] and not self.trackArray[ToontownBattleGlobals.SOUND_TRACK]: + self.b_setTrackAccess([1, 0, 0, 0, 1, 1, 0]) + elif soundExp >= healExp: + self.b_setTrackAccess([0, 0, 0, 1, 1, 1, 0]) + else: + self.b_setTrackAccess([1, 0, 0, 0, 1, 1, 0]) + fixed = 1 + elif self.rewardTier in [7, 8, 9, 10]: + if numTracks != 4: + self.notify.warning('bad num tracks in tier: %s, %s' % (self.rewardTier, self.trackArray)) + if self.trackArray[ToontownBattleGlobals.SOUND_TRACK] and not self.trackArray[ToontownBattleGlobals.HEAL_TRACK]: + if dropExp >= lureExp: + self.b_setTrackAccess([0, 0, 0, 1, 1, 1, 1]) + else: + self.b_setTrackAccess([0, 0, 1, 1, 1, 1, 0]) + elif self.trackArray[ToontownBattleGlobals.HEAL_TRACK] and not self.trackArray[ToontownBattleGlobals.SOUND_TRACK]: + if dropExp >= lureExp: + self.b_setTrackAccess([1, 0, 0, 0, 1, 1, 1]) + else: + self.b_setTrackAccess([1, 0, 1, 0, 1, 1, 0]) + elif soundExp >= healExp: + if dropExp >= lureExp: + self.b_setTrackAccess([0, 0, 0, 1, 1, 1, 1]) + else: + self.b_setTrackAccess([0, 0, 1, 1, 1, 1, 0]) + elif dropExp >= lureExp: + self.b_setTrackAccess([1, 0, 0, 0, 1, 1, 1]) + else: + self.b_setTrackAccess([1, 0, 1, 0, 1, 1, 0]) + fixed = 1 + elif self.rewardTier in [11, 12, 13]: + if numTracks != 5: + self.notify.warning('bad num tracks in tier: %s, %s' % (self.rewardTier, self.trackArray)) + if self.trackArray[ToontownBattleGlobals.SOUND_TRACK] and not self.trackArray[ToontownBattleGlobals.HEAL_TRACK]: + if self.trackArray[ToontownBattleGlobals.DROP_TRACK] and not self.trackArray[ToontownBattleGlobals.LURE_TRACK]: + if healExp >= trapExp: + self.b_setTrackAccess([1, 0, 0, 1, 1, 1, 1]) + else: + self.b_setTrackAccess([0, 1, 0, 1, 1, 1, 1]) + elif healExp >= trapExp: + self.b_setTrackAccess([1, 0, 1, 1, 1, 1, 0]) + else: + self.b_setTrackAccess([0, 1, 1, 1, 1, 1, 0]) + elif self.trackArray[ToontownBattleGlobals.HEAL_TRACK] and not self.trackArray[ToontownBattleGlobals.SOUND_TRACK]: + if self.trackArray[ToontownBattleGlobals.DROP_TRACK] and not self.trackArray[ToontownBattleGlobals.LURE_TRACK]: + if soundExp >= trapExp: + self.b_setTrackAccess([1, 0, 0, 1, 1, 1, 1]) + else: + self.b_setTrackAccess([1, 1, 0, 0, 1, 1, 1]) + elif soundExp >= trapExp: + self.b_setTrackAccess([1, 0, 1, 1, 1, 1, 0]) + else: + self.b_setTrackAccess([1, 1, 1, 0, 1, 1, 0]) + fixed = 1 + elif numTracks != 6: + self.notify.warning('bad num tracks in tier: %s, %s' % (self.rewardTier, self.trackArray)) + sortedExp = [healExp, + trapExp, + lureExp, + soundExp, + dropExp] + sortedExp.sort() + if trapExp == sortedExp[0]: + self.b_setTrackAccess([1, 0, 1, 1, 1, 1, 1]) + elif lureExp == sortedExp[0]: + self.b_setTrackAccess([1, 1, 0, 1, 1, 1, 1]) + elif dropExp == sortedExp[0]: + self.b_setTrackAccess([1, 1, 1, 1, 1, 1, 0]) + elif soundExp == sortedExp[0]: + self.b_setTrackAccess([1, 1, 1, 0, 1, 1, 1]) + elif healExp == sortedExp[0]: + self.b_setTrackAccess([0, 1, 1, 1, 1, 1, 1]) + else: + self.notify.warning('invalid exp?!: %s, %s' % (sortedExp, self.trackArray)) + self.b_setTrackAccess([1, 0, 1, 1, 1, 1, 1]) + fixed = 1 + if fixed: + self.inventory.zeroInv() + self.inventory.maxOutInv() + self.d_setInventory(self.inventory.makeNetString()) + self.notify.info('fixed tracks: %s' % self.trackArray) + return fixed + + def b_setTrackProgress(self, trackId, progress): + self.setTrackProgress(trackId, progress) + self.d_setTrackProgress(trackId, progress) + + def d_setTrackProgress(self, trackId, progress): + self.sendUpdate('setTrackProgress', [trackId, progress]) + + def setTrackProgress(self, trackId, progress): + self.trackProgressId = trackId + self.trackProgress = progress + + def addTrackProgress(self, trackId, progressIndex): + if self.trackProgressId != trackId: + self.notify.warning('tried to update progress on a track toon is not training') + newProgress = self.trackProgress | 1 << progressIndex - 1 + self.b_setTrackProgress(self.trackProgressId, newProgress) + + def clearTrackProgress(self): + self.b_setTrackProgress(-1, 0) + + def getTrackProgress(self): + return [self.trackProgressId, self.trackProgress] + + def b_setHoodsVisited(self, hoodsVisitedArray): + self.hoodsVisited = hoodsVisitedArray + self.d_setHoodsVisited(hoodsVisitedArray) + + def d_setHoodsVisited(self, hoodsVisitedArray): + self.sendUpdate('setHoodsVisited', [hoodsVisitedArray]) + + def b_setTeleportAccess(self, teleportZoneArray): + self.setTeleportAccess(teleportZoneArray) + self.d_setTeleportAccess(teleportZoneArray) + + def d_setTeleportAccess(self, teleportZoneArray): + self.sendUpdate('setTeleportAccess', [teleportZoneArray]) + + def setTeleportAccess(self, teleportZoneArray): + self.teleportZoneArray = teleportZoneArray + + def getTeleportAccess(self): + return self.teleportZoneArray + + def hasTeleportAccess(self, zoneId): + return zoneId in self.teleportZoneArray + + def addTeleportAccess(self, zoneId): + if zoneId not in self.teleportZoneArray: + self.teleportZoneArray.append(zoneId) + self.b_setTeleportAccess(self.teleportZoneArray) + + def removeTeleportAccess(self, zoneId): + if zoneId in self.teleportZoneArray: + self.teleportZoneArray.remove(zoneId) + self.b_setTeleportAccess(self.teleportZoneArray) + + def checkTeleportAccess(self, zoneId): + if zoneId not in self.getTeleportAccess() and self.teleportOverride != 1: + simbase.air.writeServerEvent('suspicious', self.doId, 'Toon teleporting to zone %s they do not have access to.' % zoneId) + if simbase.config.GetBool('want-ban-teleport', False): + commentStr = 'Toon %s teleporting to a zone %s they do not have access to' % (self.doId, zoneId) + simbase.air.banManager.ban(self.doId, self.DISLid, commentStr) + + def setTeleportOverride(self, flag): + self.teleportOverride = flag + self.b_setHoodsVisited([1000,2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000,13000]) + + def b_setScavengerHunt(self, scavengerHuntArray): + self.setScavengerHunt(scavengerHuntArray) + self.d_setScavengerHunt(scavengerHuntArray) + + def d_setScavengerHunt(self, scavengerHuntArray): + self.sendUpdate('setScavengerHunt', [scavengerHuntArray]) + + def setScavengerHunt(self, scavengerHuntArray): + self.scavengerHuntArray = scavengerHuntArray + + def getScavengerHunt(self): + return self.scavengerHuntArray + + def b_setQuestHistory(self, questList): + self.setQuestHistory(questList) + self.d_setQuestHistory(questList) + + def d_setQuestHistory(self, questList): + self.sendUpdate('setQuestHistory', [questList]) + + def setQuestHistory(self, questList): + self.notify.debug('setting quest history to %s' % questList) + self.questHistory = questList + + def getQuestHistory(self): + return self.questHistory + + def removeQuestFromHistory(self, questId): + if questId in self.questHistory: + self.questHistory.remove(questId) + self.d_setQuestHistory(self.questHistory) + return 1 + else: + return 0 + + def removeRewardFromHistory(self, rewardId): + rewardTier, rewardHistory = self.getRewardHistory() + if rewardId in rewardHistory: + rewardHistory.remove(rewardId) + self.b_setRewardHistory(rewardTier, rewardHistory) + return 1 + else: + return 0 + + def b_setRewardHistory(self, tier, rewardList): + self.setRewardHistory(tier, rewardList) + self.d_setRewardHistory(tier, rewardList) + + def d_setRewardHistory(self, tier, rewardList): + self.sendUpdate('setRewardHistory', [tier, rewardList]) + + def setRewardHistory(self, tier, rewardList): + self.air.writeServerEvent('questTier', self.getDoId(), str(tier)) + self.notify.debug('setting reward history to tier %s, %s' % (tier, rewardList)) + self.rewardTier = tier + self.rewardHistory = rewardList + + def getRewardHistory(self): + return (self.rewardTier, self.rewardHistory) + + def getRewardTier(self): + return self.rewardTier + + def fixAvatar(self): + anyChanged = 0 + qrc = QuestRewardCounter.QuestRewardCounter() + if qrc.fixAvatar(self): + self.notify.info("Fixed avatar %d's quest rewards." % self.doId) + anyChanged = 1 + if self.hp > self.maxHp: + self.notify.info('Changed avatar %d to have hp %d instead of %d, to fit with maxHp' % (self.doId, self.maxHp, self.hp)) + self.b_setHp(self.maxHp) + anyChanged = 1 + inventoryChanged = 0 + carry = self.maxCarry + for track in xrange(len(ToontownBattleGlobals.Tracks)): + if not self.hasTrackAccess(track): + for level in xrange(len(ToontownBattleGlobals.Levels[track])): + count = self.inventory.inventory[track][level] + if count != 0: + self.notify.info('Changed avatar %d to throw away %d items in track %d level %d; no access to track.' % (self.doId, + count, + track, + level)) + self.inventory.inventory[track][level] = 0 + inventoryChanged = 1 + + else: + curSkill = self.experience.getExp(track) + for level in xrange(len(ToontownBattleGlobals.Levels[track])): + count = self.inventory.inventory[track][level] + if curSkill < ToontownBattleGlobals.Levels[track][level]: + if count != 0: + self.notify.info('Changed avatar %d to throw away %d items in track %d level %d; no access to level.' % (self.doId, + count, + track, + level)) + self.inventory.inventory[track][level] = 0 + inventoryChanged = 1 + else: + newCount = min(count, carry) + newCount = min(count, self.inventory.getMax(track, level)) + if count != newCount: + self.notify.info('Changed avatar %d to throw away %d items in track %d level %d; too many gags.' % (self.doId, + count - newCount, + track, + level)) + self.inventory.inventory[track][level] = newCount + inventoryChanged = 1 + carry -= newCount + + self.inventory.calcTotalProps() + if inventoryChanged: + self.d_setInventory(self.inventory.makeNetString()) + anyChanged = 1 + if len(self.quests) > self.questCarryLimit: + self.notify.info('Changed avatar %d to throw out %d quests; too many quests.' % (self.doId, len(self.quests) - self.questCarryLimit)) + self.b_setQuests(self.quests[:self.questCarryLimit]) + self.fixAvatar() + anyChanged = 1 + if not (self.emoteAccess[0] and self.emoteAccess[1] and self.emoteAccess[2] and self.emoteAccess[3] and self.emoteAccess[4]): + self.emoteAccess[0] = 1 + self.emoteAccess[1] = 1 + self.emoteAccess[2] = 1 + self.emoteAccess[3] = 1 + self.emoteAccess[4] = 1 + self.b_setEmoteAccess(self.emoteAccess) + self.notify.info('Changed avatar %d to have emoteAccess: %s' % (self.doId, self.emoteAccess)) + anyChanged = 1 + return anyChanged + + def b_setEmoteAccess(self, bits): + if bits[26]: + bits.remove(bits[26]) + if self.emoteAccess[26]: + self.emoteAccess.remove(self.emoteAccess[26]) + self.setEmoteAccess(bits) + self.d_setEmoteAccess(bits) + + def d_setEmoteAccess(self, bits): + self.sendUpdate('setEmoteAccess', [bits]) + + def setEmoteAccess(self, bits): + if bits[26]: + bits.remove(bits[26]) + if self.emoteAccess[26]: + self.emoteAccess.remove(self.emoteAccess[26]) + maxBitCount = len(self.emoteAccess) + bits = bits[:maxBitCount] + bitCount = len(bits) + if bitCount < maxBitCount: + bits.extend([0] * (maxBitCount-bitCount)) + self.b_setEmoteAccess(bits) + else: + self.emoteAccess = bits + + def getEmoteAccess(self): + return self.emoteAccess + + def setEmoteAccessId(self, id, bit): + self.emoteAccess[id] = bit + self.d_setEmoteAccess(self.emoteAccess) + + def b_setHouseId(self, id): + self.setHouseId(id) + self.d_setHouseId(id) + + def d_setHouseId(self, id): + self.sendUpdate('setHouseId', [id]) + + def setHouseId(self, id): + self.houseId = id + + def getHouseId(self): + return self.houseId + + def b_setCustomMessages(self, customMessages): + self.d_setCustomMessages(customMessages) + self.setCustomMessages(customMessages) + + def d_setCustomMessages(self, customMessages): + self.sendUpdate('setCustomMessages', [customMessages]) + + def setCustomMessages(self, customMessages): + self.customMessages = customMessages + + def getCustomMessages(self): + return self.customMessages + + def b_setResistanceMessages(self, resistanceMessages): + self.d_setResistanceMessages(resistanceMessages) + self.setResistanceMessages(resistanceMessages) + + def d_setResistanceMessages(self, resistanceMessages): + self.sendUpdate('setResistanceMessages', [resistanceMessages]) + + def setResistanceMessages(self, resistanceMessages): + self.resistanceMessages = resistanceMessages + + def getResistanceMessages(self): + return self.resistanceMessages + + def addResistanceMessage(self, textId): + msgs = self.getResistanceMessages() + for i in xrange(len(msgs)): + if msgs[i][0] == textId: + msgs[i][1] += 1 + if msgs[i][1] > 32767: + msgs[i][1] = 32767 + self.b_setResistanceMessages(msgs) + return + + msgs.append([textId, 1]) + self.b_setResistanceMessages(msgs) + self.addStat(ToontownGlobals.STAT_UNITES) + + def removeResistanceMessage(self, textId): + msgs = self.getResistanceMessages() + for i in xrange(len(msgs)): + if msgs[i][0] == textId: + msgs[i][1] -= 1 + if msgs[i][1] <= 0: + del msgs[i] + self.b_setResistanceMessages(msgs) + return 1 + + self.notify.warning("Toon %s doesn't have resistance message %s" % (self.doId, textId)) + return 0 + + def restockAllResistanceMessages(self, charges = 1): + msgs = [] + for menuIndex in ResistanceChat.resistanceMenu: + for itemIndex in ResistanceChat.getItems(menuIndex): + textId = ResistanceChat.encodeId(menuIndex, itemIndex) + msgs.append([textId, charges]) + + self.b_setResistanceMessages(msgs) + + def b_setCatalogSchedule(self, currentWeek, nextTime): + self.setCatalogSchedule(currentWeek, nextTime) + self.d_setCatalogSchedule(currentWeek, nextTime) + + def d_setCatalogSchedule(self, currentWeek, nextTime): + self.sendUpdate('setCatalogSchedule', [currentWeek, nextTime]) + + def setCatalogSchedule(self, currentWeek, nextTime): + self.catalogScheduleCurrentWeek = currentWeek + self.catalogScheduleNextTime = nextTime + taskName = self.uniqueName('next-catalog') + taskMgr.remove(taskName) + duration = max(10.0, nextTime * 60 - time.time()) + taskMgr.doMethodLater(duration, self.__deliverCatalog, taskName) + + def getCatalogSchedule(self): + return (self.catalogScheduleCurrentWeek, self.catalogScheduleNextTime) + + def __deliverCatalog(self, task): + self.air.catalogManager.deliverCatalogFor(self) + return Task.done + + def b_setCatalog(self, monthlyCatalog, weeklyCatalog, backCatalog): + self.setCatalog(monthlyCatalog, weeklyCatalog, backCatalog) + self.d_setCatalog(monthlyCatalog, weeklyCatalog, backCatalog) + + def d_setCatalog(self, monthlyCatalog, weeklyCatalog, backCatalog): + self.sendUpdate('setCatalog', [monthlyCatalog.getBlob(), weeklyCatalog.getBlob(), backCatalog.getBlob()]) + + def setCatalog(self, monthlyCatalog, weeklyCatalog, backCatalog): + self.monthlyCatalog = CatalogItemList.CatalogItemList(monthlyCatalog) + self.weeklyCatalog = CatalogItemList.CatalogItemList(weeklyCatalog) + self.backCatalog = CatalogItemList.CatalogItemList(backCatalog) + + def getCatalog(self): + return (self.monthlyCatalog.getBlob(), self.weeklyCatalog.getBlob(), self.backCatalog.getBlob()) + + def b_setCatalogNotify(self, catalogNotify, mailboxNotify): + self.setCatalogNotify(catalogNotify, mailboxNotify) + self.d_setCatalogNotify(catalogNotify, mailboxNotify) + + def d_setCatalogNotify(self, catalogNotify, mailboxNotify): + self.sendUpdate('setCatalogNotify', [catalogNotify, mailboxNotify]) + + def setCatalogNotify(self, catalogNotify, mailboxNotify): + self.catalogNotify = catalogNotify + self.mailboxNotify = mailboxNotify + + def getCatalogNotify(self): + return (self.catalogNotify, self.mailboxNotify) + + def addToDeliverySchedule(self, item, minutes=0): + if config.GetBool('want-instant-delivery', False): + minutes = 0 + + item.deliveryDate = int(time.time() / 60. + minutes + .5) + self.onOrder.append(item) + self.b_setDeliverySchedule(self.onOrder) + + def b_setDeliverySchedule(self, onOrder, doUpdateLater = True): + self.setDeliverySchedule(onOrder, doUpdateLater) + self.d_setDeliverySchedule(onOrder) + + def d_setDeliverySchedule(self, onOrder): + self.sendUpdate('setDeliverySchedule', [onOrder.getBlob(store=CatalogItem.Customization | CatalogItem.DeliveryDate)]) + + def d_setGiftSchedule(self, onGiftOrder): + self.sendUpdate('setGiftSchedule', [self.onGiftOrder.getBlob(store=CatalogItem.Customization | CatalogItem.DeliveryDate)]) + + def setDeliverySchedule(self, onOrder, doUpdateLater = True): + self.setBothSchedules(onOrder, None) + + def getDeliverySchedule(self): + return self.onOrder.getBlob(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + + def b_setBothSchedules(self, onOrder, onGiftOrder, doUpdateLater = True): + self.setBothSchedules(onOrder, onGiftOrder, doUpdateLater) + self.d_setDeliverySchedule(onOrder) + self.d_setGiftSchedule(onGiftOrder) + + def setBothSchedules(self, onOrder, onGiftOrder, doUpdateLater = True): + if onOrder != None: + self.onOrder = CatalogItemList.CatalogItemList(onOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if onGiftOrder != None: + self.onGiftOrder = CatalogItemList.CatalogItemList(onGiftOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if not hasattr(self, 'air') or self.air == None: + return + if doUpdateLater and hasattr(self, 'name'): + taskName = 'next-bothDelivery-%s' % self.doId + now = int(time.time() / 60 + 0.5) + nextItem = None + nextGiftItem = None + nextTime = None + nextGiftTime = None + if self.onOrder: + nextTime = self.onOrder.getNextDeliveryDate() + nextItem = self.onOrder.getNextDeliveryItem() + if self.onGiftOrder: + nextGiftTime = self.onGiftOrder.getNextDeliveryDate() + nextGiftItem = self.onGiftOrder.getNextDeliveryItem() + if nextItem: + pass + if nextGiftItem: + pass + if nextTime == None: + nextTime = nextGiftTime + if nextGiftTime == None: + nextGiftTime = nextTime + if nextGiftTime < nextTime: + nextTime = nextGiftTime + existingDuration = None + checkTaskList = taskMgr.getTasksNamed(taskName) + if checkTaskList: + currentTime = globalClock.getFrameTime() + checkTask = checkTaskList[0] + existingDuration = checkTask.wakeTime - currentTime + if nextTime: + newDuration = max(10.0, nextTime * 60 - time.time()) + if existingDuration and existingDuration >= newDuration: + taskMgr.remove(taskName) + taskMgr.doMethodLater(newDuration, self.__deliverBothPurchases, taskName) + elif existingDuration and existingDuration < newDuration: + pass + else: + taskMgr.doMethodLater(newDuration, self.__deliverBothPurchases, taskName) + return + + def __deliverBothPurchases(self, task): + now = int(time.time() / 60 + 0.5) + delivered, remaining = self.onOrder.extractDeliveryItems(now) + deliveredGifts, remainingGifts = self.onGiftOrder.extractDeliveryItems(now) + giftItem = CatalogItemList.CatalogItemList(deliveredGifts, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if len(giftItem) > 0: + self.air.writeServerEvent('Getting Gift', self.doId, 'sender %s receiver %s gift %s' % (giftItem[0].giftTag, self.doId, giftItem[0].getName())) + self.b_setMailboxContents(self.mailboxContents + delivered + deliveredGifts) + self.b_setCatalogNotify(self.catalogNotify, ToontownGlobals.NewItems) + self.b_setBothSchedules(remaining, remainingGifts) + return Task.done + + def setGiftSchedule(self, onGiftOrder, doUpdateLater = True): + self.setBothSchedules(None, onGiftOrder) + + def getGiftSchedule(self): + return self.onGiftOrder.getBlob(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + + def __deliverGiftPurchase(self, task): + now = int(time.time() / 60 + 0.5) + delivered, remaining = self.onGiftOrder.extractDeliveryItems(now) + self.notify.info('Gift Delivery for %s: %s.' % (self.doId, delivered)) + self.b_setMailboxContents(self.mailboxContents + delivered) + self.b_setCatalogNotify(self.catalogNotify, ToontownGlobals.NewItems) + return Task.done + + def __deliverPurchase(self, task): + now = int(time.time() / 60 + 0.5) + delivered, remaining = self.onOrder.extractDeliveryItems(now) + self.notify.info('Delivery for %s: %s.' % (self.doId, delivered)) + self.b_setMailboxContents(self.mailboxContents + delivered) + self.b_setDeliverySchedule(remaining) + self.b_setCatalogNotify(self.catalogNotify, ToontownGlobals.NewItems) + return Task.done + + def b_setMailboxContents(self, mailboxContents): + self.setMailboxContents(mailboxContents) + self.d_setMailboxContents(mailboxContents) + + def d_setMailboxContents(self, mailboxContents): + self.sendUpdate('setMailboxContents', [mailboxContents.getBlob(store=CatalogItem.Customization)]) + if len(mailboxContents) == 0: + self.b_setCatalogNotify(self.catalogNotify, ToontownGlobals.NoItems) + self.checkMailboxFullIndicator() + + def checkMailboxFullIndicator(self): + pass + + def setMailboxContents(self, mailboxContents): + self.notify.debug('Setting mailboxContents to %s.' % mailboxContents) + self.mailboxContents = CatalogItemList.CatalogItemList(mailboxContents, store=CatalogItem.Customization) + self.notify.debug('mailboxContents is %s.' % self.mailboxContents) + + def getMailboxContents(self): + return self.mailboxContents.getBlob(store=CatalogItem.Customization) + + def b_setGhostMode(self, flag): + self.setGhostMode(flag) + self.d_setGhostMode(flag) + + def d_setGhostMode(self, flag): + self.sendUpdate('setGhostMode', [flag]) + + def setGhostMode(self, flag): + self.ghostMode = flag + + def setImmortalMode(self, flag): + self.immortalMode = flag + + def setUnlimitedGags(self, flag): + self.unlimitedGags = flag + + def b_setSpeedChatStyleIndex(self, index): + self.setSpeedChatStyleIndex(index) + self.d_setSpeedChatStyleIndex(index) + + def d_setSpeedChatStyleIndex(self, index): + self.sendUpdate('setSpeedChatStyleIndex', [index]) + + def setSpeedChatStyleIndex(self, index): + self.speedChatStyleIndex = index + + def getSpeedChatStyleIndex(self): + return self.speedChatStyleIndex + + def b_setMaxMoney(self, maxMoney): + self.d_setMaxMoney(maxMoney) + self.setMaxMoney(maxMoney) + + if self.getMoney() > maxMoney: + self.b_setBankMoney(self.bankMoney + (self.getMoney() - maxMoney)) + self.b_setMoney(maxMoney) + + def d_setMaxMoney(self, maxMoney): + self.sendUpdate('setMaxMoney', [maxMoney]) + + def setMaxMoney(self, maxMoney): + self.maxMoney = maxMoney + + def getMaxMoney(self): + return self.maxMoney + + def b_setMaxBankMoney(self, maxBankMoney): + self.d_setMaxBankMoney(maxBankMoney) + self.setMaxBankMoney(maxBankMoney) + + if self.getBankMoney() > maxBankMoney: + self.b_setBankMoney(maxBankMoney) + + def d_setMaxBankMoney(self, maxBankMoney): + self.sendUpdate('setMaxBankMoney', [maxBankMoney]) + + def setMaxBankMoney(self, maxBankMoney): + self.maxBankMoney = maxBankMoney + + def getMaxBankMoney(self): + return self.maxBankMoney + + def addMoney(self, deltaMoney): + if deltaMoney > 0: + messenger.send('topToonsManager-event', [self.doId, TopToonsGlobals.CAT_JELLYBEAN, deltaMoney]) + money = deltaMoney + self.money + pocketMoney = min(money, self.maxMoney) + self.b_setMoney(pocketMoney) + overflowMoney = money - self.maxMoney + if overflowMoney > 0: + bankMoney = self.bankMoney + overflowMoney + self.b_setBankMoney(bankMoney) + self.addStat(ToontownGlobals.STAT_BEANS_EARNT, deltaMoney) + + def takeMoney(self, deltaMoney, bUseBank = True): + totalMoney = self.money + if bUseBank: + totalMoney += self.bankMoney + if deltaMoney > totalMoney: + self.notify.warning('Not enough money! AvId: %s Has:%s Charged:%s' % (self.doId, totalMoney, deltaMoney)) + return False + if bUseBank and deltaMoney > self.money: + self.b_setBankMoney(self.bankMoney - (deltaMoney - self.money)) + self.b_setMoney(0) + else: + self.b_setMoney(self.money - deltaMoney) + self.addStat(ToontownGlobals.STAT_BEANS_SPENT, deltaMoney) + return True + + def b_setMoney(self, money): + if bboard.get('autoRich-%s' % self.doId, False): + money = self.getMaxMoney() + self.setMoney(money) + self.d_setMoney(money) + + def d_setMoney(self, money): + self.sendUpdate('setMoney', [money]) + + def setMoney(self, money): + if money < 0: + simbase.air.writeServerEvent('suspicious', self.doId, 'toon has invalid money %s, forcing to zero' % money) + money = 0 + commentStr = 'User %s has negative money %s' % (self.doId, money) + dislId = self.DISLid + if simbase.config.GetBool('want-ban-negative-money', False): + simbase.air.banManager.ban(self.doId, dislId, commentStr) + pass + self.money = money + + def getMoney(self): + return self.money + + def getTotalMoney(self): + return self.money + self.bankMoney + + def b_setBankMoney(self, money): + bankMoney = min(money, ToontownGlobals.MaxBankMoney) + self.setBankMoney(bankMoney) + self.d_setBankMoney(bankMoney) + + def d_setBankMoney(self, money): + self.sendUpdate('setBankMoney', [money]) + + def setBankMoney(self, money): + self.bankMoney = money + + def getBankMoney(self): + return self.bankMoney + + def b_setEmblems(self, emblems): + self.setEmblems(emblems) + self.d_setEmblems(emblems) + + def setEmblems(self, emblems): + self.emblems = emblems + + def d_setEmblems(self, emblems): + if simbase.air.wantEmblems: + self.sendUpdate('setEmblems', [emblems]) + + def getEmblems(self): + return self.emblems + + def addEmblems(self, emblemsToAdd): + newEmblems = self.emblems[:] + for i in xrange(ToontownGlobals.NumEmblemTypes): + newEmblems[i] += emblemsToAdd[i] + + self.b_setEmblems(newEmblems) + + def subtractEmblems(self, emblemsToSubtract): + if len(emblemsToSubtract) < ToontownGlobals.NumEmblemTypes: + return True + + newEmblems = self.emblems[:] + for i in xrange(ToontownGlobals.NumEmblemTypes): + if newEmblems[i] < emblemsToSubtract[i]: + return False + + newEmblems[i] -= emblemsToSubtract[i] + + self.b_setEmblems(newEmblems) + return True + + def isEnoughEmblemsToBuy(self, itemEmblemPrices): + for emblemIndex, emblemPrice in enumerate(itemEmblemPrices): + if emblemIndex >= len(self.emblems): + return False + if self.emblems[emblemIndex] < emblemPrice: + return False + + return True + + + + def tossPie(self, x, y, z, h, p, r, sequence, power, timestamp32): + if not self.validate(self.doId, self.numPies > 0, 'tossPie with no pies available'): + return + if self.numPies != ToontownGlobals.FullPies: + self.b_setNumPies(self.numPies - 1) + + def b_setNumPies(self, numPies): + self.setNumPies(numPies) + self.d_setNumPies(numPies) + + def d_setNumPies(self, numPies): + self.sendUpdate('setNumPies', [numPies]) + + def setNumPies(self, numPies): + self.numPies = numPies + + def b_setPieType(self, pieType): + self.setPieType(pieType) + self.d_setPieType(pieType) + + def d_setPieType(self, pieType): + self.sendUpdate('setPieType', [pieType]) + + def setPieType(self, pieType): + self.pieType = pieType + + def d_setTrophyScore(self, score): + self.sendUpdate('setTrophyScore', [score]) + + def stopToonUp(self): + taskMgr.remove(self.uniqueName('safeZoneToonUp')) + self.ignore(self.air.getAvatarExitEvent(self.getDoId())) + + def startToonUp(self, healFrequency): + self.stopToonUp() + self.nextToonup = (healFrequency, self.indexOf(ToontownGlobals.TOONUP_PULSE_ZONES, ZoneUtil.getCanonicalHoodId(self.zoneId), 0) + 1) + self.__waitForNextToonUp() + + def indexOf(self, list, element, default): + try: + return list.index(element) + except: + return default + + def __waitForNextToonUp(self): + taskMgr.doMethodLater(self.nextToonup[0], self.toonUpTask, self.uniqueName('safeZoneToonUp')) + + def toonUpTask(self, task): + self.toonUp(self.nextToonup[1]) + self.__waitForNextToonUp() + return Task.done + + def toonUp(self, hpGained, quietly = 0, sendTotal = 1): + if hpGained > self.maxHp: + hpGained = self.maxHp + if not quietly: + self.sendUpdate('toonUp', [hpGained]) + if self.hp + hpGained <= 0: + self.hp += hpGained + else: + self.hp = max(self.hp, 0) + hpGained + clampedHp = min(self.hp, self.maxHp) + if not self.hpOwnedByBattle: + self.hp = clampedHp + if sendTotal and not self.hpOwnedByBattle: + self.d_setHp(clampedHp) + + def isToonedUp(self): + return self.hp >= self.maxHp + + def b_announceBingo(self): + self.d_announceBingo() + self.announceBingo + + def d_announceBingo(self): + self.sendUpdate('announceBingo', []) + + def announceBingo(self): + pass + + def incrementPopulation(self): + if self.isPlayerControlled(): + DistributedPlayerAI.DistributedPlayerAI.incrementPopulation(self) + + def decrementPopulation(self): + if self.isPlayerControlled(): + DistributedPlayerAI.DistributedPlayerAI.decrementPopulation(self) + + def reqSCResistance(self, msgIndex, nearbyPlayers): + self.d_setSCResistance(msgIndex, nearbyPlayers) + + def d_setSCResistance(self, msgIndex, nearbyPlayers): + if not ResistanceChat.validateId(msgIndex): + self.air.writeServerEvent('suspicious', self.doId, 'said resistance %s, which is invalid.' % msgIndex) + return + if not self.removeResistanceMessage(msgIndex): + self.air.writeServerEvent('suspicious', self.doId, 'said resistance %s, but does not have it.' % msgIndex) + return + if hasattr(self, 'autoResistanceRestock') and self.autoResistanceRestock: + self.restockAllResistanceMessages(1) + affectedPlayers = [] + for toonId in nearbyPlayers: + toon = self.air.doId2do.get(toonId) + if not toon: + self.notify.warning('%s said resistance %s for %s; not on server' % (self.doId, msgIndex, toonId)) + elif toon.__class__ != DistributedToonAI: + self.air.writeServerEvent('suspicious', self.doId, 'said resistance %s for %s; object of type %s' % (msgIndex, toonId, toon.__class__.__name__)) + elif toonId in affectedPlayers: + self.air.writeServerEvent('suspicious', self.doId, 'said resistance %s for %s twice in same message.' % (msgIndex, toonId)) + else: + toon.doResistanceEffect(msgIndex) + affectedPlayers.append(toonId) + + if len(affectedPlayers) > 50: + self.air.writeServerEvent('suspicious', self.doId, 'said resistance %s for %s toons.' % (msgIndex, len(affectedPlayers))) + self.notify.warning('%s said resistance %s for %s toons: %s' % (self.doId, + msgIndex, + len(affectedPlayers), + affectedPlayers)) + self.sendUpdate('setSCResistance', [msgIndex, affectedPlayers]) + type = ResistanceChat.getMenuName(msgIndex) + value = ResistanceChat.getItemValue(msgIndex) + self.air.writeServerEvent('resistanceChat', self.zoneId, '%s|%s|%s|%s' % (self.doId, + type, + value, + affectedPlayers)) + + def doResistanceEffect(self, msgIndex): + msgType, itemIndex = ResistanceChat.decodeId(msgIndex) + msgValue = ResistanceChat.getItemValue(msgIndex) + if msgType == ResistanceChat.RESISTANCE_TOONUP: + if msgValue == -1: + self.toonUp(self.maxHp) + else: + self.toonUp(msgValue) + elif msgType == ResistanceChat.RESISTANCE_RESTOCK: + self.inventory.NPCMaxOutInv(msgValue) + self.d_setInventory(self.inventory.makeNetString()) + elif msgType == ResistanceChat.RESISTANCE_MONEY: + self.addMoney(msgValue) + elif msgType == ResistanceChat.RESISTANCE_MERITS: + if msgValue == -1: + for i in xrange(len(SuitDNA.suitDepts)): + self.doResistanceMerits(i) + else: + self.doResistanceMerits(msgValue) + elif msgType == ResistanceChat.RESISTANCE_TICKETS: + self.b_setTickets(self.getTickets() + msgValue) + + def doResistanceMerits(self, dept): + if not CogDisguiseGlobals.isSuitComplete(self.cogParts, dept): + return + + totalMerits = CogDisguiseGlobals.getTotalMerits(self, dept) + merits = self.cogMerits[dept] + + if totalMerits == 0 or merits >= totalMerits: + return + + self.cogMerits[dept] = min(totalMerits, merits + (totalMerits / 3)) + self.b_setCogMerits(self.cogMerits) + + def squish(self, damage): + self.takeDamage(damage) + + if simbase.wantKarts: + + def hasKart(self): + return self.kartDNA[KartDNA.bodyType] != -1 + + def b_setTickets(self, numTickets): + if numTickets > RaceGlobals.MaxTickets: + numTickets = RaceGlobals.MaxTickets + self.d_setTickets(numTickets) + self.setTickets(numTickets) + + def d_setTickets(self, numTickets): + if numTickets > RaceGlobals.MaxTickets: + numTickets = RaceGlobals.MaxTickets + self.sendUpdate('setTickets', [numTickets]) + + def setTickets(self, numTickets): + if numTickets > RaceGlobals.MaxTickets: + numTickets = RaceGlobals.MaxTickets + self.tickets = numTickets + + def getTickets(self): + return self.tickets + + def b_setKartingTrophies(self, trophyList): + self.setKartingTrophies(trophyList) + self.d_setKartingTrophies(trophyList) + + def setKartingTrophies(self, trophyList): + self.notify.debug('setting kartingTrophies to %s' % trophyList) + self.kartingTrophies = trophyList + + def d_setKartingTrophies(self, trophyList): + self.sendUpdate('setKartingTrophies', [trophyList]) + + def getKartingTrophies(self): + return self.kartingTrophies + + def b_setKartingHistory(self, history): + self.setKartingHistory(history) + self.d_setKartingHistory(history) + + def setKartingHistory(self, history): + self.notify.debug('setting kartingHistory to %s' % history) + self.kartingHistory = history + + def d_setKartingHistory(self, history): + self.sendUpdate('setKartingHistory', [history]) + + def getKartingHistory(self): + return self.kartingHistory + + def b_setKartingPersonalBest(self, bestTimes): + best1 = bestTimes[0:6] + best2 = bestTimes[6:] + self.setKartingPersonalBest(best1) + self.setKartingPersonalBest2(best2) + self.d_setKartingPersonalBest(bestTimes) + + def d_setKartingPersonalBest(self, bestTimes): + best1 = bestTimes[0:6] + best2 = bestTimes[6:] + self.sendUpdate('setKartingPersonalBest', [best1]) + self.sendUpdate('setKartingPersonalBest2', [best2]) + + def setKartingPersonalBest(self, bestTimes): + self.notify.debug('setting karting to %s' % bestTimes) + self.kartingPersonalBest = bestTimes + + def setKartingPersonalBest2(self, bestTimes2): + self.notify.debug('setting karting2 to %s' % bestTimes2) + self.kartingPersonalBest2 = bestTimes2 + + def getKartingPersonalBest(self): + return self.kartingPersonalBest + + def getKartingPersonalBest2(self): + return self.kartingPersonalBest2 + + def getKartingPersonalBestAll(self): + return self.kartingPersonalBest + self.kartingPersonalBest2 + + def setKartDNA(self, kartDNA): + self.b_setKartBodyType(kartDNA[KartDNA.bodyType]) + self.b_setKartBodyColor(kartDNA[KartDNA.bodyColor]) + self.b_setKartAccColor(kartDNA[KartDNA.accColor]) + self.b_setKartEngineBlockType(kartDNA[KartDNA.ebType]) + self.b_setKartSpoilerType(kartDNA[KartDNA.spType]) + self.b_setKartFrontWheelWellType(kartDNA[KartDNA.fwwType]) + self.b_setKartBackWheelWellType(kartDNA[KartDNA.bwwType]) + self.b_setKartRimType(kartDNA[KartDNA.rimsType]) + self.b_setKartDecalType(kartDNA[KartDNA.decalType]) + + def b_setKartBodyType(self, bodyType): + self.d_setKartBodyType(bodyType) + self.setKartBodyType(bodyType) + + def d_setKartBodyType(self, bodyType): + self.sendUpdate('setKartBodyType', [bodyType]) + + def setKartBodyType(self, bodyType): + self.kartDNA[KartDNA.bodyType] = bodyType + + def getKartBodyType(self): + return self.kartDNA[KartDNA.bodyType] + + def b_setKartBodyColor(self, bodyColor): + self.d_setKartBodyColor(bodyColor) + self.setKartBodyColor(bodyColor) + + def d_setKartBodyColor(self, bodyColor): + self.sendUpdate('setKartBodyColor', [bodyColor]) + + def setKartBodyColor(self, bodyColor): + self.kartDNA[KartDNA.bodyColor] = bodyColor + + def getKartBodyColor(self): + return self.kartDNA[KartDNA.bodyColor] + + def b_setKartAccessoryColor(self, accColor): + self.d_setKartAccessoryColor(accColor) + self.setKartAccessoryColor(accColor) + + def d_setKartAccessoryColor(self, accColor): + self.sendUpdate('setKartAccessoryColor', [accColor]) + + def setKartAccessoryColor(self, accColor): + self.kartDNA[KartDNA.accColor] = accColor + + def getKartAccessoryColor(self): + return self.kartDNA[KartDNA.accColor] + + def b_setKartEngineBlockType(self, ebType): + self.d_setKartEngineBlockType(ebType) + self.setKartEngineBlockType(ebType) + + def d_setKartEngineBlockType(self, ebType): + self.sendUpdate('setKartEngineBlockType', [ebType]) + + def setKartEngineBlockType(self, ebType): + self.kartDNA[KartDNA.ebType] = ebType + + def getKartEngineBlockType(self): + return self.kartDNA[KartDNA.ebType] + + def b_setKartSpoilerType(self, spType): + self.d_setKartSpoilerType(spType) + self.setKartSpoilerType(spType) + + def d_setKartSpoilerType(self, spType): + self.sendUpdate('setKartSpoilerType', [spType]) + + def setKartSpoilerType(self, spType): + self.kartDNA[KartDNA.spType] = spType + + def getKartSpoilerType(self): + return self.kartDNA[KartDNA.spType] + + def b_setKartFrontWheelWellType(self, fwwType): + self.d_setKartFrontWheelWellType(fwwType) + self.setKartFrontWheelWellType(fwwType) + + def d_setKartFrontWheelWellType(self, fwwType): + self.sendUpdate('setKartFrontWheelWellType', [fwwType]) + + def setKartFrontWheelWellType(self, fwwType): + self.kartDNA[KartDNA.fwwType] = fwwType + + def getKartFrontWheelWellType(self): + return self.kartDNA[KartDNA.fwwType] + + def b_setKartBackWheelWellType(self, bwwType): + self.d_setKartBackWheelWellType(bwwType) + self.setKartBackWheelWellType(bwwType) + + def d_setKartBackWheelWellType(self, bwwType): + self.sendUpdate('setKartBackWheelWellType', [bwwType]) + + def setKartBackWheelWellType(self, bwwType): + self.kartDNA[KartDNA.bwwType] = bwwType + + def getKartBackWheelWellType(self): + return self.kartDNA[KartDNA.bwwType] + + def b_setKartRimType(self, rimsType): + self.d_setKartRimType(rimsType) + self.setKartRimType(rimsType) + + def d_setKartRimType(self, rimsType): + self.sendUpdate('setKartRimType', [rimsType]) + + def setKartRimType(self, rimsType): + self.kartDNA[KartDNA.rimsType] = rimsType + + def getKartRimType(self): + return self.kartDNA[KartDNA.rimsType] + + def b_setKartDecalType(self, decalType): + self.d_setKartDecalType(decalType) + self.setKartDecalType(decalType) + + def d_setKartDecalType(self, decalType): + self.sendUpdate('setKartDecalType', [decalType]) + + def setKartDecalType(self, decalType): + self.kartDNA[KartDNA.decalType] = decalType + + def getKartDecalType(self): + return self.kartDNA[KartDNA.decalType] + + def b_setKartAccessoriesOwned(self, accessories): + self.d_setKartAccessoriesOwned(accessories) + self.setKartAccessoriesOwned(accessories) + + def d_setKartAccessoriesOwned(self, accessories): + self.sendUpdate('setKartAccessoriesOwned', [accessories]) + + def setKartAccessoriesOwned(self, accessories): + self.accessories = accessories + + def getKartAccessoriesOwned(self): + owned = copy.deepcopy(self.accessories) + while InvalidEntry in owned: + owned.remove(InvalidEntry) + + return owned + + def addOwnedAccessory(self, accessoryId): + print 'in add owned accessory' + if accessoryId in AccessoryDict: + if self.accessories.count(accessoryId) > 0: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to add accessory %s which is already owned!' % accessoryId) + return + if self.accessories.count(InvalidEntry) > 0: + accList = list(self.accessories) + index = self.accessories.index(InvalidEntry) + accList[index] = accessoryId + self.b_setKartAccessoriesOwned(accList) + else: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to add accessory %s when accessory inventory is full!' % accessoryId) + return + else: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to add accessory %s which is not a valid accessory.' % accessoryId) + return + + def removeOwnedAccessory(self, accessoryId): + if accessoryId in AccessoryDict: + if self.accessories.count(accessoryId) == 0: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to remove accessory %s which is not currently owned!' % accessoryId) + return + else: + accList = list(self.accessories) + index = self.accessories.index(accessoryId) + accList[index] = InvalidEntry + self.air.writeServerEvent('deletedKartingAccessory', self.doId, '%s' % accessoryId) + self.b_setKartAccessoriesOwned(accList) + else: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to remove accessory %s which is not a valid accessory.' % accessoryId) + return + + def updateKartDNAField(self, dnaField, fieldValue): + if not checkKartFieldValidity(dnaField): + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update to dna value %s in the invalid field %s' % (fieldValue, dnaField)) + return + if dnaField == KartDNA.bodyType: + if fieldValue not in KartDict.keys() and fieldValue != InvalidEntry: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update kart body to invalid body %s.' % fieldValue) + return + self.b_setKartBodyType(fieldValue) + else: + accFields = [KartDNA.ebType, + KartDNA.spType, + KartDNA.fwwType, + KartDNA.bwwType, + KartDNA.rimsType, + KartDNA.decalType] + colorFields = [KartDNA.bodyColor, KartDNA.accColor] + if dnaField in accFields: + if fieldValue == InvalidEntry: + self.__updateKartDNAField(dnaField, fieldValue) + else: + if fieldValue not in self.accessories: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update to accessory %s which is not currently owned.' % fieldValue) + return + field = getAccessoryType(fieldValue) + if field == InvalidEntry: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update accessory %s in an illegal field %s' % (fieldValue, field)) + return + elif field != dnaField: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update accessory %s in a field %s that does not match client specified field %s' % (fieldValue, field, dnaField)) + return + self.__updateKartDNAField(dnaField, fieldValue) + elif dnaField in colorFields: + if fieldValue == InvalidEntry: + self.__updateKartDNAField(dnaField, fieldValue) + else: + if fieldValue not in self.accessories: + if fieldValue != getDefaultColor(): + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update to color %s which is not owned!' % fieldValue) + return + elif fieldValue == getDefaultColor() and self.kartDNA[dnaField] != InvalidEntry: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update to default color %s which is not owned!' % fieldValue) + return + if getAccessoryType(fieldValue) != KartDNA.bodyColor: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update invalid color %s for dna field %s' % (fieldValue, dnaField)) + return + self.__updateKartDNAField(dnaField, fieldValue) + else: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to udpate accessory %s in the invalid field %s' % (fieldValue, dnaField)) + return + + def __updateKartDNAField(self, dnaField, fieldValue): + if dnaField == KartDNA.bodyColor: + self.b_setKartBodyColor(fieldValue) + elif dnaField == KartDNA.accColor: + self.b_setKartAccessoryColor(fieldValue) + elif dnaField == KartDNA.ebType: + self.b_setKartEngineBlockType(fieldValue) + elif dnaField == KartDNA.spType: + self.b_setKartSpoilerType(fieldValue) + elif dnaField == KartDNA.fwwType: + self.b_setKartFrontWheelWellType(fieldValue) + elif dnaField == KartDNA.bwwType: + self.b_setKartBackWheelWellType(fieldValue) + elif dnaField == KartDNA.rimsType: + self.b_setKartRimType(fieldValue) + elif dnaField == KartDNA.decalType: + self.b_setKartDecalType(fieldValue) + + def setAllowSoloRace(self, allowSoloRace): + self.allowSoloRace = allowSoloRace + + def setAllowRaceTimeout(self, allowRaceTimeout): + self.allowRaceTimeout = allowRaceTimeout + + if simbase.wantPets: + + def getPetId(self): + return self.petId + + def b_setPetId(self, petId): + self.d_setPetId(petId) + self.setPetId(petId) + + def d_setPetId(self, petId): + self.sendUpdate('setPetId', [petId]) + + def setPetId(self, petId): + self.petId = petId + + def getPetTrickPhrases(self): + return self.petTrickPhrases + + def b_setPetTrickPhrases(self, tricks): + self.setPetTrickPhrases(tricks) + self.d_setPetTrickPhrases(tricks) + + def d_setPetTrickPhrases(self, tricks): + self.sendUpdate('setPetTrickPhrases', [tricks]) + + def setPetTrickPhrases(self, tricks): + self.petTrickPhrases = tricks + + def deletePet(self): + if self.petId == 0: + self.notify.warning("this toon doesn't have a pet to delete!") + return + simbase.air.petMgr.deleteToonsPet(self.doId) + + def setPetMovie(self, petId, flag): + self.notify.debug('setPetMovie: petId: %s, flag: %s' % (petId, flag)) + pet = simbase.air.doId2do.get(petId) + if pet is not None: + if pet.__class__.__name__ == 'DistributedPetAI': + pet.handleAvPetInteraction(flag, self.getDoId()) + else: + self.air.writeServerEvent('suspicious', self.doId, 'setPetMovie: playing pet movie %s on non-pet object %s' % (flag, petId)) + return + + def setPetTutorialDone(self, done): + self.petTutorialDone = True + + def setFishBingoTutorialDone(self, done): + self.fishBingoTutorialDone = True + + def setFishBingoMarkTutorialDone(self, done): + self.fishBingoMarkTutorialDone = True + + def enterEstate(self, ownerId, zoneId): + DistributedToonAI.notify.debug('enterEstate: %s %s %s' % (self.doId, ownerId, zoneId)) + if self.wasInEstate(): + self.cleanupEstateData() + collSphere = CollisionSphere(0, 0, 0, self.getRadius()) + collNode = CollisionNode('toonColl-%s' % self.doId) + collNode.addSolid(collSphere) + collNode.setFromCollideMask(BitMask32.allOff()) + collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.collNodePath = self.attachNewNode(collNode) + taskMgr.add(self._moveSphere, self._getMoveSphereTaskName(), priority=OTPGlobals.AICollMovePriority) + self.inEstate = 1 + self.estateOwnerId = ownerId + self.estateZones = [zoneId] + self.enterPetLook() + + def _getPetLookerBodyNode(self): + return self.collNodePath + + def _getMoveSphereTaskName(self): + return 'moveSphere-%s' % self.doId + + def _moveSphere(self, task): + self.collNodePath.setZ(self.getRender(), 0) + return Task.cont + + def isInEstate(self): + return hasattr(self, 'inEstate') and self.inEstate + + def exitEstate(self, ownerId = None, zoneId = None): + DistributedToonAI.notify.debug('exitEstate: %s %s %s' % (self.doId, ownerId, zoneId)) + DistributedToonAI.notify.debug('current zone: %s' % self.zoneId) + self.exitPetLook() + taskMgr.remove(self._getMoveSphereTaskName()) + self.collNodePath.removeNode() + del self.collNodePath + del self.estateOwnerId + del self.inEstate + self._wasInEstate = 1 + + def wasInEstate(self): + return hasattr(self, '_wasInEstate') and self._wasInEstate + + def cleanupEstateData(self): + del self.estateZones + del self._wasInEstate + + def setSC(self, msgId): + DistributedToonAI.notify.debug('setSC: %s' % msgId) + from toontown.pets import PetObserve + PetObserve.send(self.zoneId, PetObserve.getSCObserve(msgId, self.doId)) + if msgId in [21006]: + self.setHatePets(1) + elif msgId in [21000, + 21001, + 21003, + 21004, + 21200, + 21201, + 21202, + 21203, + 21204, + 21205, + 21206]: + self.setHatePets(0) + + def setSCCustom(self, msgId): + DistributedToonAI.notify.debug('setSCCustom: %s' % msgId) + from toontown.pets import PetObserve + PetObserve.send(self.zoneId, PetObserve.getSCObserve(msgId, self.doId)) + + def setHatePets(self, hate): + self.hatePets = hate + + def takeOutKart(self, zoneId = None): + if not self.kart: + from toontown.racing import DistributedVehicleAI + self.kart = DistributedVehicleAI.DistributedVehicleAI(self.air, self.doId) + if zoneId: + self.kart.generateWithRequired(zoneId) + else: + self.kart.generateWithRequired(self.zoneId) + self.kart.start() + + def reqCogSummons(self, type, suitIndex): + if type not in ('building', 'invasion', 'cogdo', 'skelinvasion', 'waiterinvasion', 'v2invasion'): + self.air.writeServerEvent('suspicious', self.doId, 'invalid cog summons type: %s' % type) + self.sendUpdate('cogSummonsResponse', ['fail', suitIndex, 0]) + return + if suitIndex >= len(SuitDNA.suitHeadTypes): + self.air.writeServerEvent('suspicious', self.doId, 'invalid suitIndex: %s' % suitIndex) + self.sendUpdate('cogSummonsResponse', ['fail', suitIndex, 0]) + return + if not self.hasCogSummons(suitIndex, type): + self.air.writeServerEvent('suspicious', self.doId, 'bogus cog summons') + self.sendUpdate('cogSummonsResponse', ['fail', suitIndex, 0]) + return + returnCode = None + if type == 'building': + returnCode = self.doBuildingTakeover(suitIndex) + elif type == 'cogdo': + returnCode = self.doCogdoTakeOver(suitIndex) + elif type.endswith('invasion'): + suitDeptIndex = suitIndex / SuitDNA.suitsPerDept + suitTypeIndex = suitIndex % SuitDNA.suitsPerDept + if type.startswith('v2'): + flags = SuitInvasionGlobals.IFV2 + elif type.startswith('skel'): + flags = SuitInvasionGlobals.IFSkelecog + elif type.startswith('waiter'): + flags = SuitInvasionGlobals.IFWaiter + else: + flags = 0 + returnCode = self.doCogInvasion(suitDeptIndex, suitTypeIndex, flags) + if returnCode: + if returnCode[0] == 'success': + self.air.writeServerEvent('cogSummoned', self.doId, '%s|%s|%s' % (type, suitIndex, self.zoneId)) + self.removeCogSummonsEarned(suitIndex, type) + self.sendUpdate('cogSummonsResponse', returnCode) + return + + def doSummonSingleCog(self, suitIndex): + if suitIndex >= len(SuitDNA.suitHeadTypes): + self.notify.warning('Bad suit index: %s' % suitIndex) + return ['badIndex', suitIndex, 0] + suitName = SuitDNA.suitHeadTypes[suitIndex] + streetId = ZoneUtil.getBranchZone(self.zoneId) + if streetId not in self.air.suitPlanners: + return ['badlocation', suitIndex, 0] + sp = self.air.suitPlanners[streetId] + map = sp.getZoneIdToPointMap() + zones = [self.zoneId, self.zoneId - 1, self.zoneId + 1] + for zoneId in zones: + if zoneId in map: + points = map[zoneId][:] + suit = sp.createNewSuit([], points, suitName=suitName) + if suit: + return ['success', suitIndex, 0] + + return ['badlocation', suitIndex, 0] + + def doBuildingTakeover(self, suitIndex): + streetId = ZoneUtil.getBranchZone(self.zoneId) + if streetId not in self.air.suitPlanners: + self.notify.warning('Street %d is not known.' % streetId) + return ['badlocation', suitIndex, 0] + sp = self.air.suitPlanners[streetId] + bm = sp.buildingMgr + building = self.findClosestDoor() + if building == None: + return ['badlocation', suitIndex, 0] + level = None + if suitIndex >= len(SuitDNA.suitHeadTypes): + self.notify.warning('Bad suit index: %s' % suitIndex) + return ['badIndex', suitIndex, 0] + suitName = SuitDNA.suitHeadTypes[suitIndex] + track = SuitDNA.getSuitDept(suitName) + type = SuitDNA.getSuitType(suitName) + level, type, track = sp.pickLevelTypeAndTrack(None, type, track) + building.suitTakeOver(track, level, None) + self.notify.warning('cogTakeOver %s %s %d %d' % (track, + level, + building.block, + self.zoneId)) + return ['success', suitIndex, building.doId] + + def doToonBuildingTakeover(self): + streetId = ZoneUtil.getBranchZone(self.zoneId) + if streetId not in self.air.suitPlanners: + self.notify.warning('Street %d is not known.' % streetId) + return ['badlocation', 'notknownstreet', 0] + sp = self.air.suitPlanners[streetId] + bm = sp.buildingMgr + building = self.findClosestSuitDoor() + if building == None: + return ['badlocation', 'nobuilding', 0] + if building.isToonBlock(): + return ['badlocation', 'toonblock', 0] + building.toonTakeOver() + self.notify.warning('toonTakeOverFromStreet %s %d' % (building.block, + self.zoneId)) + + return ['success', building.doId] + + def doCogdoTakeOver(self, suitIndex): + if suitIndex >= len(SuitDNA.suitHeadTypes): + self.notify.warning('Bad suit index: %s' % suitIndex) + return ['badIndex', suitIndex, 0] + suitName = SuitDNA.suitHeadTypes[suitIndex] + track = CogdoUtil.getCogdoTrack(suitName) + if not track: + return ['disabled', 0, 0] + streetId = ZoneUtil.getBranchZone(self.zoneId) + if streetId not in self.air.suitPlanners: + self.notify.warning('Street %d is not known.' % streetId) + return ['badlocation', 0, 0] + building = self.findClosestDoor() + if building == None: + return ['badlocation', 0, 0] + sp = self.air.suitPlanners[streetId] + level, type, track = sp.pickLevelTypeAndTrack(None, SuitDNA.getSuitType(suitName), track) + building.cogdoTakeOver(level, 2, track) + return ['success', level, building.doId] + + def doCogInvasion(self, suitDeptIndex, suitTypeIndex, flags=0): + if self.air.suitInvasionManager.getInvading(): + return ['busy', 0, 0] + + suitName = SuitDNA.getSuitName(suitDeptIndex, suitTypeIndex) + suitIndex = SuitDNA.suitHeadTypes.index(suitName) + + if self.air.suitInvasionManager.startInvasion( + suitDeptIndex=suitDeptIndex, suitTypeIndex=suitTypeIndex, flags=flags): + return ['success', suitIndex, 0] + + return ['fail', suitIndex, 0] + + def b_setCogSummonsEarned(self, cogSummonsEarned): + self.d_setCogSummonsEarned(cogSummonsEarned) + self.setCogSummonsEarned(cogSummonsEarned) + + def d_setCogSummonsEarned(self, cogSummonsEarned): + self.sendUpdate('setCogSummonsEarned', [cogSummonsEarned]) + + def setCogSummonsEarned(self, cogSummonsEarned): + self.cogSummonsEarned = cogSummonsEarned + + def getCogSummonsEarned(self): + return self.cogSummonsEarned + + def restockAllCogSummons(self): + numSuits = len(SuitDNA.suitHeadTypes) + fullSetForSuit = 1 | 2 | 4 | 8 + allSummons = numSuits * [fullSetForSuit] + self.b_setCogSummonsEarned(allSummons) + + def addCogSummonsEarned(self, suitIndex, type): + summons = self.getCogSummonsEarned() + curSetting = summons[suitIndex] + if type == 'building': + curSetting |= 1 + elif type == 'invasion': + curSetting |= 2 + elif type == 'cogdo': + curSetting |= 4 + elif type == 'skelinvasion': + curSetting |= 8 + elif type == 'waiterinvasion': + curSetting |= 16 + elif type == 'v2invasion': + curSetting |= 32 + summons[suitIndex] = curSetting + self.b_setCogSummonsEarned(summons) + + def removeCogSummonsEarned(self, suitIndex, type): + summons = self.getCogSummonsEarned() + curSetting = summons[suitIndex] + if self.hasCogSummons(suitIndex, type): + if type == 'building': + curSetting &= -2 + elif type == 'invasion': + curSetting &= -3 + elif type == 'cogdo': + curSetting &= -5 + elif type == 'skelinvasion': + curSetting &= -9 + elif type == 'waiterinvasion': + curSetting &= -17 + elif type == 'v2invasion': + curSetting &= -33 + summons[suitIndex] = curSetting + self.b_setCogSummonsEarned(summons) + if hasattr(self, 'autoRestockSummons') and self.autoRestockSummons: + self.restockAllCogSummons() + return True + self.notify.warning("Toon %s doesn't have a %s summons for %s" % (self.doId, type, suitIndex)) + return False + + def hasCogSummons(self, suitIndex, type = None): + summons = self.getCogSummonsEarned() + curSetting = summons[suitIndex] + if type == 'building': + return curSetting & 1 + elif type == 'invasion': + return curSetting & 2 + elif type == 'cogdo': + return curSetting & 4 + elif type == 'skelinvasion': + return curSetting & 8 + elif type == 'waiterinvasion': + return curSetting & 16 + elif type == 'v2invasion': + return curSetting & 32 + return curSetting + + def hasParticularCogSummons(self, deptIndex, level, type): + if deptIndex not in xrange(len(SuitDNA.suitDepts)): + self.notify.warning('invalid parameter deptIndex %s' % deptIndex) + return False + if level not in xrange(SuitDNA.suitsPerDept): + self.notify.warning('invalid parameter level %s' % level) + return False + suitIndex = deptIndex * SuitDNA.suitsPerDept + level + retval = self.hasCogSummons(suitIndex, type) + return retval + + def assignNewCogSummons(self, level = None, summonType = None, deptIndex = None): + if level != None: + if deptIndex in xrange(len(SuitDNA.suitDepts)): + dept = deptIndex + else: + numDepts = len(SuitDNA.suitDepts) + dept = random.randrange(0, numDepts) + suitIndex = dept * SuitDNA.suitsPerDept + level + elif deptIndex in xrange(len(SuitDNA.suitDepts)): + randomLevel = random.randrange(0, SuitDNA.suitsPerDept) + suitIndex = deptIndex * SuitDNA.suitsPerLevel + randomLevel + else: + numSuits = len(SuitDNA.suitHeadTypes) + suitIndex = random.randrange(0, numSuits) + summonTypes = ['building', 'invasion', 'cogdo', 'skelinvasion', 'waiterinvasion', 'v2invasion'] + if summonType in summonTypes: + type = summonType + else: + #typeWeights = ['single'] * 70 + ['building'] * 25 + ['invasion'] * 5 + type = random.choice(summonTypes) + if suitIndex >= len(SuitDNA.suitHeadTypes): + self.notify.warning('Bad suit index: %s' % suitIndex) + self.addCogSummonsEarned(suitIndex, type) + return (suitIndex, type) + + def findClosestDoor(self): + zoneId = self.zoneId + streetId = ZoneUtil.getBranchZone(zoneId) + sp = self.air.suitPlanners[streetId] + if not sp: + return None + bm = sp.buildingMgr + if not bm: + return None + zones = [zoneId, + zoneId - 1, + zoneId + 1, + zoneId - 2, + zoneId + 2] + for zone in zones: + for i in bm.getToonBlocks(): + building = bm.getBuilding(i) + extZoneId, intZoneId = building.getExteriorAndInteriorZoneId() + if not NPCToons.isZoneProtected(intZoneId): + if hasattr(building, 'door'): + if building.door.zoneId == zone: + return building + + return None + + def findClosestSuitDoor(self): + zoneId = self.zoneId + streetId = ZoneUtil.getBranchZone(zoneId) + sp = self.air.suitPlanners[streetId] + if not sp: + return None + bm = sp.buildingMgr + if not bm: + return None + zones = [zoneId, + zoneId - 1, + zoneId + 1, + zoneId - 2, + zoneId + 2] + for zone in zones: + for i in bm.getSuitBlocks(): + building = bm.getBuilding(i) + extZoneId, intZoneId = building.getExteriorAndInteriorZoneId() + if not NPCToons.isZoneProtected(intZoneId): + if hasattr(building, 'elevator'): + if building.elevator.zoneId == zone: + return building + + return None + + def b_setGardenTrophies(self, trophyList): + self.setGardenTrophies(trophyList) + self.d_setGardenTrophies(trophyList) + + def setGardenTrophies(self, trophyList): + self.notify.debug('setting gardenTrophies to %s' % trophyList) + self.gardenTrophies = trophyList + + def d_setGardenTrophies(self, trophyList): + self.sendUpdate('setGardenTrophies', [trophyList]) + + def getGardenTrophies(self): + return self.gardenTrophies + + def setGardenSpecials(self, specials): + for special in specials: + if special[1] > 255: + special[1] = 255 + + self.gardenSpecials = specials + + def getGardenSpecials(self): + return self.gardenSpecials + + def d_setGardenSpecials(self, specials): + self.sendUpdate('setGardenSpecials', [specials]) + + def b_setGardenSpecials(self, specials): + for special in specials: + if special[1] > 255: + newCount = 255 + index = special[0] + self.gardenSpecials.remove(special) + self.gardenSpecials.append((index, newCount)) + self.gardenSpecials.sort() + + self.setGardenSpecials(specials) + self.d_setGardenSpecials(specials) + + def addGardenItem(self, index, count): + for item in self.gardenSpecials: + if item[0] == index: + newCount = item[1] + count + self.gardenSpecials.remove(item) + self.gardenSpecials.append((index, newCount)) + self.gardenSpecials.sort() + self.b_setGardenSpecials(self.gardenSpecials) + return + + self.gardenSpecials.append((index, count)) + self.gardenSpecials.sort() + self.b_setGardenSpecials(self.gardenSpecials) + + def removeGardenItem(self, index, count): + for item in self.gardenSpecials: + if item[0] == index: + newCount = item[1] - count + self.gardenSpecials.remove(item) + if newCount > 0: + self.gardenSpecials.append((index, newCount)) + self.gardenSpecials.sort() + self.b_setGardenSpecials(self.gardenSpecials) + return 1 + + self.notify.warning("removing garden item %d that toon doesn't have" % index) + return 0 + + def b_setFlowerCollection(self, speciesList, varietyList): + self.setFlowerCollection(speciesList, varietyList) + self.d_setFlowerCollection(speciesList, varietyList) + + def d_setFlowerCollection(self, speciesList, varietyList): + self.sendUpdate('setFlowerCollection', [speciesList, varietyList]) + + def setFlowerCollection(self, speciesList, varietyList): + self.flowerCollection = FlowerCollection.FlowerCollection() + self.flowerCollection.makeFromNetLists(speciesList, varietyList) + + def getFlowerCollection(self): + return self.flowerCollection.getNetLists() + + def b_setMaxFlowerBasket(self, maxFlowerBasket): + self.d_setMaxFlowerBasket(maxFlowerBasket) + self.setMaxFlowerBasket(maxFlowerBasket) + + def d_setMaxFlowerBasket(self, maxFlowerBasket): + self.sendUpdate('setMaxFlowerBasket', [maxFlowerBasket]) + + def setMaxFlowerBasket(self, maxFlowerBasket): + self.maxFlowerBasket = maxFlowerBasket + + def getMaxFlowerBasket(self): + return self.maxFlowerBasket + + def b_setFlowerBasket(self, speciesList, varietyList): + self.setFlowerBasket(speciesList, varietyList) + self.d_setFlowerBasket(speciesList, varietyList) + + def d_setFlowerBasket(self, speciesList, varietyList): + self.sendUpdate('setFlowerBasket', [speciesList, varietyList]) + + def setFlowerBasket(self, speciesList, varietyList): + self.flowerBasket = FlowerBasket.FlowerBasket() + self.flowerBasket.makeFromNetLists(speciesList, varietyList) + + def getFlowerBasket(self): + return self.flowerBasket.getNetLists() + + def makeRandomFlowerBasket(self): + self.flowerBasket.generateRandomBasket() + self.d_setFlowerBasket(*self.flowerBasket.getNetLists()) + + def addFlowerToBasket(self, species, variety): + numFlower = len(self.flowerBasket) + if numFlower >= self.maxFlowerBasket: + self.notify.warning('addFlowerToBasket: cannot add flower, basket is full') + return 0 + elif self.flowerBasket.addFlower(species, variety): + self.d_setFlowerBasket(*self.flowerBasket.getNetLists()) + self.addStat(ToontownGlobals.STAT_FLOWERS) + return 1 + else: + self.notify.warning('addFlowerToBasket: addFlower failed') + return 0 + + def removeFlowerFromBasketAtIndex(self, index): + if self.flowerBasket.removeFlowerAtIndex(index): + self.d_setFlowerBasket(*self.flowerBasket.getNetLists()) + return 1 + else: + self.notify.warning('removeFishFromTank: cannot find fish') + return 0 + + def b_setShovel(self, shovelId): + self.d_setShovel(shovelId) + self.setShovel(shovelId) + + def d_setShovel(self, shovelId): + self.sendUpdate('setShovel', [shovelId]) + + def setShovel(self, shovelId): + self.shovel = shovelId + + def getShovel(self): + return self.shovel + + def b_setShovelSkill(self, skillLevel): + self.sendGardenEvent() + if skillLevel >= GardenGlobals.ShovelAttributes[self.shovel]['skillPts']: + if self.shovel < GardenGlobals.MAX_SHOVELS - 1: + self.b_setShovel(self.shovel + 1) + self.setShovelSkill(0) + self.d_setShovelSkill(0) + self.sendUpdate('promoteShovel', [self.shovel]) + self.air.writeServerEvent('garden_new_shovel', self.doId, '%d' % self.shovel) + else: + self.setShovelSkill(skillLevel) + self.d_setShovelSkill(skillLevel) + + def d_setShovelSkill(self, skillLevel): + self.sendUpdate('setShovelSkill', [skillLevel]) + + def setShovelSkill(self, skillLevel): + self.shovelSkill = skillLevel + + def getShovelSkill(self): + return self.shovelSkill + + def b_setWateringCan(self, wateringCanId): + self.d_setWateringCan(wateringCanId) + self.setWateringCan(wateringCanId) + + def d_setWateringCan(self, wateringCanId): + self.sendUpdate('setWateringCan', [wateringCanId]) + + def setWateringCan(self, wateringCanId): + self.wateringCan = wateringCanId + + def getWateringCan(self): + return self.wateringCan + + def b_setWateringCanSkill(self, skillLevel): + self.sendGardenEvent() + if skillLevel >= GardenGlobals.WateringCanAttributes[self.wateringCan]['skillPts']: + if self.wateringCan < GardenGlobals.MAX_WATERING_CANS - 1: + self.b_setWateringCan(self.wateringCan + 1) + self.setWateringCanSkill(0) + self.d_setWateringCanSkill(0) + self.sendUpdate('promoteWateringCan', [self.wateringCan]) + self.air.writeServerEvent('garden_new_wateringCan', self.doId, '%d' % self.wateringCan) + else: + skillLevel = GardenGlobals.WateringCanAttributes[self.wateringCan]['skillPts'] - 1 + self.setWateringCanSkill(skillLevel) + self.d_setWateringCanSkill(skillLevel) + else: + self.setWateringCanSkill(skillLevel) + self.d_setWateringCanSkill(skillLevel) + + def d_setWateringCanSkill(self, skillLevel): + self.sendUpdate('setWateringCanSkill', [skillLevel]) + + def setWateringCanSkill(self, skillLevel): + self.wateringCanSkill = skillLevel + + def getWateringCanSkill(self): + return self.wateringCanSkill + + def b_setTrackBonusLevel(self, trackBonusLevelArray): + self.setTrackBonusLevel(trackBonusLevelArray) + self.d_setTrackBonusLevel(trackBonusLevelArray) + + def d_setTrackBonusLevel(self, trackBonusLevelArray): + self.sendUpdate('setTrackBonusLevel', [trackBonusLevelArray]) + + def setTrackBonusLevel(self, trackBonusLevelArray): + self.trackBonusLevel = trackBonusLevelArray + + def getTrackBonusLevel(self, track = None): + if track == None: + return self.trackBonusLevel + else: + return self.trackBonusLevel[track] + return + + def checkGagBonus(self, track, level): + trackBonus = self.getTrackBonusLevel(track) + return trackBonus >= level + + def giveMeSpecials(self, id = None): + print 'Specials Go!!' + self.b_setGardenSpecials([(0, 3), + (1, 2), + (2, 3), + (3, 2), + (4, 3), + (5, 2), + (6, 3), + (7, 2), + (100, 1), + (101, 3), + (102, 1)]) + + def reqUseSpecial(self, special): + return # TODO/gardening + response = self.tryToUseSpecial(special) + self.sendUpdate('useSpecialResponse', [response]) + + def tryToUseSpecial(self, special): + estateOwnerDoId = simbase.air.estateMgr.zone2owner.get(self.zoneId) + response = 'badlocation' + doIHaveThisSpecial = False + for curSpecial in self.gardenSpecials: + if curSpecial[0] == special and curSpecial[1] > 0: + doIHaveThisSpecial = True + break + + if not doIHaveThisSpecial: + return response + if not self.doId == estateOwnerDoId: + self.notify.warning("how did this happen, planting an item you don't own") + return response + if estateOwnerDoId: + estate = simbase.air.estateMgr.estate.get(estateOwnerDoId) + if estate and hasattr(estate, 'avIdList'): + ownerIndex = estate.avIdList.index(estateOwnerDoId) + if ownerIndex >= 0: + estate.doEpochNow(onlyForThisToonIndex=ownerIndex) + self.removeGardenItem(special, 1) + response = 'success' + self.air.writeServerEvent('garden_fertilizer', self.doId, '') + return response + + def sendGardenEvent(self): + if hasattr(self, 'estateZones') and hasattr(self, 'doId'): + if simbase.wantPets and self.hatePets: + PetObserve.send(self.estateZones, PetObserve.PetActionObserve(PetObserve.Actions.GARDEN, self.doId)) + + def setGardenStarted(self, bStarted): + self.gardenStarted = bStarted + + def d_setGardenStarted(self, bStarted): + self.sendUpdate('setGardenStarted', [bStarted]) + + def b_setGardenStarted(self, bStarted): + self.setGardenStarted(bStarted) + self.d_setGardenStarted(bStarted) + + def getGardenStarted(self): + return self.gardenStarted + + def logSuspiciousEvent(self, eventName): + senderId = self.air.getAvatarIdFromSender() + eventStr = 'senderId=%s ' % senderId + eventStr += eventName + self.air.writeServerEvent('suspicious', self.doId, eventStr) + if simbase.config.GetBool('want-ban-setAnimState', True): + if eventName.startswith('setAnimState: '): + if senderId == self.doId: + commentStr = 'Toon %s trying to call setAnimState' % self.doId + simbase.air.banManager.ban(self.doId, self.DISLid, commentStr) + else: + self.notify.warning('logSuspiciousEvent event=%s senderId=%s != self.doId=%s' % (eventName, senderId, self.doId)) + + def getGolfTrophies(self): + return self.golfTrophies + + def getGolfCups(self): + return self.golfCups + + def b_setGolfHistory(self, history): + self.setGolfHistory(history) + self.d_setGolfHistory(history) + + def d_setGolfHistory(self, history): + self.sendUpdate('setGolfHistory', [history]) + + def setGolfHistory(self, history): + self.notify.debug('setting golfHistory to %s' % history) + self.golfHistory = history + self.golfTrophies = GolfGlobals.calcTrophyListFromHistory(self.golfHistory) + self.golfCups = GolfGlobals.calcCupListFromHistory(self.golfHistory) + + def getGolfHistory(self): + return self.golfHistory + + def b_setGolfHoleBest(self, holeBest): + self.setGolfHoleBest(holeBest) + self.d_setGolfHoleBest(holeBest) + + def d_setGolfHoleBest(self, holeBest): + packed = GolfGlobals.packGolfHoleBest(holeBest) + self.sendUpdate('setPackedGolfHoleBest', [packed]) + + def setGolfHoleBest(self, holeBest): + self.golfHoleBest = holeBest + + def getGolfHoleBest(self): + return self.golfHoleBest + + def getPackedGolfHoleBest(self): + packed = GolfGlobals.packGolfHoleBest(self.golfHoleBest) + return packed + + def setPackedGolfHoleBest(self, packedHoleBest): + unpacked = GolfGlobals.unpackGolfHoleBest(packedHoleBest) + self.setGolfHoleBest(unpacked) + + def b_setGolfCourseBest(self, courseBest): + self.setGolfCourseBest(courseBest) + self.d_setGolfCourseBest(courseBest) + + def d_setGolfCourseBest(self, courseBest): + self.sendUpdate('setGolfCourseBest', [courseBest]) + + def setGolfCourseBest(self, courseBest): + self.golfCourseBest = courseBest + + def getGolfCourseBest(self): + return self.golfCourseBest + + def setUnlimitedSwing(self, unlimitedSwing): + self.unlimitedSwing = unlimitedSwing + + def getUnlimitedSwing(self): + return self.unlimitedSwing + + def b_setUnlimitedSwing(self, unlimitedSwing): + self.setUnlimitedSwing(unlimitedSwing) + self.d_setUnlimitedSwing(unlimitedSwing) + + def d_setUnlimitedSwing(self, unlimitedSwing): + self.sendUpdate('setUnlimitedSwing', [unlimitedSwing]) + + def b_setPinkSlips(self, pinkSlips): + self.specialInventory[0] = pinkSlips + self.b_setSpecialInventory(self.specialInventory) + + def b_setCrateKeys(self, crateKeys): + self.specialInventory[1] = crateKeys + self.b_setSpecialInventory(self.specialInventory) + + def b_setSpecialInventory(self, specialInventory): + self.d_setSpecialInventory(specialInventory) + self.setSpecialInventory(specialInventory) + + def d_setSpecialInventory(self, specialInventory): + self.sendUpdate('setSpecialInventory', [specialInventory]) + + def setSpecialInventory(self, specialInventory): + self.specialInventory = specialInventory + + def getPinkSlips(self): + return self.specialInventory[0] + + def getCrateKeys(self): + return self.specialInventory[1] + + def addPinkSlips(self, amountToAdd): + pinkSlips = min(self.getPinkSlips() + amountToAdd, 255) + self.b_setPinkSlips(pinkSlips) + self.addStat(ToontownGlobals.STAT_SLIPS, amountToAdd) + + def removePinkSlips(self, amount): + if hasattr(self, 'autoRestockPinkSlips') and self.autoRestockPinkSlips: + amount = 0 + pinkSlips = max(self.getPinkSlips() - amount, 0) + self.b_setPinkSlips(pinkSlips) + + def addCrateKeys(self, amountToAdd): + self.b_setCrateKeys(min(self.getCrateKeys() + amountToAdd, 255)) + + def removeCrateKeys(self, amount): + self.b_setCrateKeys(max(self.getCrateKeys() - amount, 0)) + + def b_setNametagStyle(self, nametagStyle): + self.d_setNametagStyle(nametagStyle) + self.setNametagStyle(nametagStyle) + + def d_setNametagStyle(self, nametagStyle): + self.sendUpdate('setNametagStyle', [nametagStyle]) + + def setNametagStyle(self, nametagStyle): + self.nametagStyle = nametagStyle + + def getNametagStyle(self): + return self.nametagStyle + + def b_setNametagStyles(self, nametagStyles): + self.d_setNametagStyles(nametagStyles) + self.setNametagStyles(nametagStyles) + + def d_setNametagStyles(self, nametagStyles): + self.sendUpdate('setNametagStyles', [nametagStyles]) + + def setNametagStyles(self, nametagStyles): + self.nametagStyles = nametagStyles + + def addNametagStyle(self, nametagStyle): + if nametagStyle in self.nametagStyles: + return + + self.nametagStyles.append(nametagStyle) + self.b_setNametagStyles(self.nametagStyles) + + def getNametagStyles(self): + return self.nametagStyles + + def requestNametagStyle(self, nametagStyle): + if nametagStyle not in self.nametagStyles: + return + + self.b_setNametagStyle(nametagStyle) + + def b_setMail(self, mail): + self.d_setMail(mail) + self.setMail(mail) + + def d_setMail(self, mail): + self.sendUpdate('setMail', [mail]) + + def setMail(self, mail): + self.mail = mail + + def setNumMailItems(self, numMailItems): + self.numMailItems = numMailItems + + def setSimpleMailNotify(self, simpleMailNotify): + self.simpleMailNotify = simpleMailNotify + + def setInviteMailNotify(self, inviteMailNotify): + self.inviteMailNotify = inviteMailNotify + + def setInvites(self, invites): + self.invites = [] + for i in xrange(len(invites)): + oneInvite = invites[i] + newInvite = InviteInfoBase(*oneInvite) + self.invites.append(newInvite) + + def updateInviteMailNotify(self): + invitesInMailbox = self.getInvitesToShowInMailbox() + newInvites = 0 + readButNotRepliedInvites = 0 + for invite in invitesInMailbox: + if invite.status == PartyGlobals.InviteStatus.NotRead: + newInvites += 1 + elif invite.status == PartyGlobals.InviteStatus.ReadButNotReplied: + readButNotRepliedInvites += 1 + + if newInvites: + self.setInviteMailNotify(ToontownGlobals.NewItems) + elif readButNotRepliedInvites: + self.setInviteMailNotify(ToontownGlobals.OldItems) + else: + self.setInviteMailNotify(ToontownGlobals.NoItems) + + def getNumNonResponseInvites(self): + count = 0 + for i in xrange(len(self.invites)): + if self.invites[i].status == InviteStatus.NotRead or self.invites[i].status == InviteStatus.ReadButNotReplied: + count += 1 + + return count + + def getInvitesToShowInMailbox(self): + result = [] + for invite in self.invites: + appendInvite = True + if invite.status == InviteStatus.Accepted or invite.status == InviteStatus.Rejected: + appendInvite = False + if appendInvite: + partyInfo = self.getOnePartyInvitedTo(invite.partyId) + if not partyInfo: + appendInvite = False + if appendInvite: + if partyInfo.status == PartyGlobals.PartyStatus.Cancelled: + appendInvite = False + if appendInvite: + endDate = partyInfo.endTime.date() + curDate = simbase.air.toontownTimeManager.getCurServerDateTime().date() + if endDate < curDate: + appendInvite = False + if appendInvite: + result.append(invite) + + return result + + def getNumInvitesToShowInMailbox(self): + result = len(self.getInvitesToShowInMailbox()) + return result + + def setHostedParties(self, hostedParties): + self.hostedParties = [] + for i in xrange(len(hostedParties)): + hostedInfo = hostedParties[i] + newParty = PartyInfoAI(*hostedInfo) + self.hostedParties.append(newParty) + + def setPartiesInvitedTo(self, partiesInvitedTo): + self.partiesInvitedTo = [] + for i in xrange(len(partiesInvitedTo)): + partyInfo = partiesInvitedTo[i] + newParty = PartyInfoAI(*partyInfo) + self.partiesInvitedTo.append(newParty) + + self.updateInviteMailNotify() + self.checkMailboxFullIndicator() + + def getOnePartyInvitedTo(self, partyId): + result = None + for i in xrange(len(self.partiesInvitedTo)): + partyInfo = self.partiesInvitedTo[i] + if partyInfo.partyId == partyId: + result = partyInfo + break + + return result + + def setPartyReplyInfoBases(self, replies): + self.partyReplyInfoBases = [] + for i in xrange(len(replies)): + partyReply = replies[i] + repliesForOneParty = PartyReplyInfoBase(*partyReply) + self.partyReplyInfoBases.append(repliesForOneParty) + + def updateInvite(self, inviteKey, newStatus): + for invite in self.invites: + if invite.inviteKey == inviteKey: + invite.status = newStatus + self.updateInviteMailNotify() + self.checkMailboxFullIndicator() + break + + def updateReply(self, partyId, inviteeId, newStatus): + for partyReply in self.partyReplyInfoBases: + if partyReply.partyId == partyId: + for reply in partyReply.replies: + if reply.inviteeId == inviteeId: + reply.inviteeId = newStatus + break + + def canPlanParty(self): + nonCancelledPartiesInTheFuture = 0 + for partyInfo in self.hostedParties: + if partyInfo.status not in (PartyGlobals.PartyStatus.Cancelled, PartyGlobals.PartyStatus.Finished, PartyGlobals.PartyStatus.NeverStarted): + nonCancelledPartiesInTheFuture += 1 + if nonCancelledPartiesInTheFuture >= PartyGlobals.MaxHostedPartiesPerToon: + break + + result = nonCancelledPartiesInTheFuture < PartyGlobals.MaxHostedPartiesPerToon + return result + + def setPartyCanStart(self, partyId): + self.notify.debug('setPartyCanStart called passing in partyId=%s' % partyId) + found = False + for partyInfo in self.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.status = PartyGlobals.PartyStatus.CanStart + found = True + break + + if not found: + self.notify.warning("setPartyCanStart can't find partyId %s" % partyId) + + def setPartyStatus(self, partyId, newStatus): + self.notify.debug('setPartyStatus called passing in partyId=%s newStauts=%d' % (partyId, newStatus)) + found = False + for partyInfo in self.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.status = newStatus + found = True + break + + info = self.getOnePartyInvitedTo(partyId) + if info: + found = True + info.status = newStatus + if not found: + self.notify.warning("setPartyCanStart can't find hosted or invitedTO partyId %s" % partyId) + + def b_setAwardMailboxContents(self, awardMailboxContents): + self.setAwardMailboxContents(awardMailboxContents) + self.d_setAwardMailboxContents(awardMailboxContents) + + def d_setAwardMailboxContents(self, awardMailboxContents): + self.sendUpdate('setAwardMailboxContents', [awardMailboxContents.getBlob(store=CatalogItem.Customization)]) + + def setAwardMailboxContents(self, awardMailboxContents): + self.notify.debug('Setting awardMailboxContents to %s.' % awardMailboxContents) + self.awardMailboxContents = CatalogItemList.CatalogItemList(awardMailboxContents, store=CatalogItem.Customization) + self.notify.debug('awardMailboxContents is %s.' % self.awardMailboxContents) + if len(awardMailboxContents) == 0: + self.b_setAwardNotify(ToontownGlobals.NoItems) + self.checkMailboxFullIndicator() + + def getAwardMailboxContents(self): + return self.awardMailboxContents.getBlob(store=CatalogItem.Customization) + + def b_setAwardSchedule(self, onOrder, doUpdateLater = True): + self.setAwardSchedule(onOrder, doUpdateLater) + self.d_setAwardSchedule(onOrder) + + def d_setAwardSchedule(self, onOrder): + self.sendUpdate('setAwardSchedule', [onOrder.getBlob(store=CatalogItem.Customization | CatalogItem.DeliveryDate)]) + + def setAwardSchedule(self, onAwardOrder, doUpdateLater = True): + self.onAwardOrder = CatalogItemList.CatalogItemList(onAwardOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if hasattr(self, 'name'): + if doUpdateLater and hasattr(self, 'air'): + taskName = self.uniqueName('next-award-delivery') + taskMgr.remove(taskName) + now = int(time.time() / 60 + 0.5) + nextItem = None + nextTime = self.onAwardOrder.getNextDeliveryDate() + nextItem = self.onAwardOrder.getNextDeliveryItem() + if nextItem != None: + pass + if nextTime != None: + duration = max(10.0, nextTime * 60 - time.time()) + taskMgr.doMethodLater(duration, self.__deliverAwardPurchase, taskName) + return + + def __deliverAwardPurchase(self, task): + now = int(time.time() / 60 + 0.5) + delivered, remaining = self.onAwardOrder.extractDeliveryItems(now) + self.notify.info('Award Delivery for %s: %s.' % (self.doId, delivered)) + self.b_setAwardMailboxContents(self.awardMailboxContents + delivered) + self.b_setAwardSchedule(remaining) + if delivered: + self.b_setAwardNotify(ToontownGlobals.NewItems) + return Task.done + + def b_setAwardNotify(self, awardMailboxNotify): + self.setAwardNotify(awardMailboxNotify) + self.d_setAwardNotify(awardMailboxNotify) + + def d_setAwardNotify(self, awardMailboxNotify): + self.sendUpdate('setAwardNotify', [awardMailboxNotify]) + + def setAwardNotify(self, awardNotify): + self.awardNotify = awardNotify + + def teleportResponseToAI(self, toAvId, available, shardId, hoodId, zoneId, fromAvId): + senderId = self.air.getAvatarIdFromSender() + if toAvId != self.doId: + self.air.writeServerEvent('suspicious', self.doId, 'toAvId=%d is not equal to self.doId' % toAvId) + return + if available != 1: + self.air.writeServerEvent('suspicious', self.doId, 'invalid availableValue=%d' % available) + return + if fromAvId == 0: + return + self.air.teleportRegistrar.registerValidTeleport(toAvId, available, shardId, hoodId, zoneId, fromAvId) + dg = self.dclass.aiFormatUpdate('teleportResponse', fromAvId, fromAvId, self.doId, [toAvId, + available, + shardId, + hoodId, + zoneId]) + self.air.send(dg) + + @staticmethod + def staticGetLogicalZoneChangeAllEvent(): + return 'DOLogicalChangeZone-all' + + def handleHacking(self, response, comment, coconspirators = []): + if response == 'quietzone': + self.b_setLocation(self.parentId, ToontownGlobals.QuietZone) + elif response == 'disconnect': + self.disconnect() + elif response == 'disconnectall': + self.disconnect() + for coconspirator in coconspirators: + coconspirator.disconnect() + + elif response == 'ban': + self.ban('collision and position hacking') + self.disconnect() + elif response == 'banall': + self.ban('collision and position hacking') + self.disconnect() + for coconspirator in coconspirators: + coconspirator.ban('collision and position hacking') + coconspirator.disconnect() + + def setAnimalSound(self, index): + self.animalSound = index + + def d_setAnimalSound(self, index): + self.sendUpdate('setAnimalSound', [index]) + + def b_setAnimalSound(self, index): + self.setAnimalSound(index) + self.d_setAnimalSound(index) + + def getAnimalSound(self): + return self.animalSound + + def addBuff(self, id, duration): + buffCount = len(self.buffs) + if buffCount <= id: + self.buffs.extend([0] * ((id+1) - buffCount)) + timestamp = int(time.time()) + (duration*60) + self.buffs[id] = timestamp + self.b_setBuffs(self.buffs) + + def removeBuff(self, id): + if len(self.buffs) <= id: + self.notify.warning('tried to remove non-existent buff %d on avatar %d.' % (id, self.doId)) + return + self.buffs[id] = 0 + self.d_setBuffs(self.buffs) + + def hasBuff(self, id): + if len(self.buffs) <= id: + return False + return self.buffs[id] != 0 + + def setBuffs(self, buffs): + self.buffs = buffs + for id, timestamp in enumerate(self.buffs): + if timestamp: + taskName = self.uniqueName('removeBuff-%s' % id) + taskMgr.remove(taskName) + delayTime = max(timestamp - int(time.time()), 0) + taskMgr.doMethodLater(delayTime, self.removeBuff, taskName, extraArgs=[id]) + + def d_setBuffs(self, buffs): + self.sendUpdate('setBuffs', [buffs]) + + def b_setBuffs(self, buffs): + self.setBuffs(buffs) + self.d_setBuffs(buffs) + + def setRedeemedCodes(self, redeemedCodes): + self.redeemedCodes = redeemedCodes + + def d_setRedeemedCodes(self, redeemedCodes): + self.sendUpdate('setRedeemedCodes', [redeemedCodes]) + + def b_setRedeemedCodes(self, redeemedCodes): + self.setRedeemedCodes(redeemedCodes) + self.d_setRedeemedCodes(redeemedCodes) + + def getRedeemedCodes(self, redeemedCodes): + return self.redeemedCodes + + def isCodeRedeemed(self, code): + return code in self.redeemedCodes + + def redeemCode(self, code): + if not self.isCodeRedeemed(code): + self.redeemedCodes.append(code) + self.b_setRedeemedCodes(self.redeemedCodes) + + def removeCode(self, code): + if self.isCodeRedeemed(code): + self.redeemedCodes.remove(code) + self.b_setRedeemedCodes(self.redeemedCodes) + + def setTrueFriends(self, trueFriends): + self.trueFriends = trueFriends + + def d_setTrueFriends(self, trueFriends): + self.sendUpdate('setTrueFriends', [trueFriends]) + + def b_setTrueFriends(self, trueFriends): + self.setTrueFriends(trueFriends) + self.d_setTrueFriends(trueFriends) + + def isTrueFriends(self, avId): + return avId in self.trueFriends + + def addTrueFriend(self, avId): + if avId in self.trueFriends: + return + + self.trueFriends.append(avId) + self.b_setTrueFriends(self.trueFriends) + + def getTrueFriends(self, trueFriends): + return self.trueFriends + + def setNextKnockHeal(self, nextKnockHeal): + self.nextKnockHeal = nextKnockHeal + + def d_setNextKnockHeal(self, nextKnockHeal): + self.sendUpdate('setNextKnockHeal', [nextKnockHeal]) + + def b_setNextKnockHeal(self, nextKnockHeal): + self.setNextKnockHeal(nextKnockHeal) + self.d_setNextKnockHeal(nextKnockHeal) + + def getNextKnockHeal(self): + return self.nextKnockHeal + + def setTFRequest(self, tfRequest): + self.tfRequest = tfRequest + + def d_setTFRequest(self, tfRequest): + self.sendUpdate('setTFRequest', [tfRequest]) + + def b_setTFRequest(self, tfRequest): + self.setTFRequest(tfRequest) + self.d_setTFRequest(tfRequest) + + def getTFRequest(self): + return self.tfRequest + + def setEPP(self, epp): + self.epp = epp + + def d_setEPP(self, epp): + self.sendUpdate("setEPP", [epp]) + + def b_setEPP(self, epp): + self.setEPP(epp) + self.d_setEPP(epp) + + def addEPP(self, dept): + self.epp.append(dept) + self.d_setEPP(self.epp) + + def removeEPP(self, dept): + if dept in self.epp: + self.epp.remove(dept) + + self.d_setEPP(self.epp) + + def hasEPP(self, dept): + return dept in self.epp + + def b_setStats(self, stats): + self.d_setStats(stats) + self.setStats(stats) + + def d_setStats(self, stats): + self.sendUpdate('setStats', [stats]) + + def setStats(self, stats): + self.stats = stats + + def getStats(self): + return self.stats + + def getStat(self, index): + return self.stats[index] + + def addStat(self, index, amount=1): + if amount <= 0: + return + + self.stats[index] += amount + self.d_setStats(self.stats) + + def wipeStats(self): + self.stats = [0] * 22 + self.d_setStats(self.stats) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[str, int, int]) +def cheesyEffect(value, hood=0, expire=0): + """ + Modify the target's cheesy effect. + """ + try: + value = int(value) + except: + value = value.lower() + if isinstance(value, str): + if value not in OTPGlobals.CEName2Id: + return 'Invalid cheesy effect value: %s' % value + value = OTPGlobals.CEName2Id[value] + elif not 0 <= value <= 15: + return 'Invalid cheesy effect value: %d' % value + if (hood != 0) and (not 1000 <= hood < ToontownGlobals.DynamicZonesBegin): + return 'Invalid hood ID: %d' % hood + target = spellbook.getTarget() + target.b_setCheesyEffect(value, hood, expire) + return 'Set %s\'s cheesy effect to: %d' % (target.getName(), value) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def hp(hp): + """ + Modify the target's current HP. + """ + target = spellbook.getTarget() + maxHp = target.getMaxHp() + if not -1 <= hp <= maxHp: + return 'HP must be in range (-1-%d).' % maxHp + target.b_setHp(hp) + return 'Set your HP to: %d' % hp + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def maxHp(maxHp): + """ + Modify the invoker's max HP. + """ + if not 15 <= maxHp <= ToontownGlobals.MaxHpLimit: + return 'HP must be in range (15-%d).' % ToontownGlobals.MaxHpLimit + invoker = spellbook.getTarget() + invoker.b_setHp(maxHp) + invoker.b_setMaxHp(maxHp) + invoker.toonUp(maxHp - invoker.getHp()) + return 'Set your max HP to: %d' % maxHp + +@magicWord(category=CATEGORY_PROGRAMMER, types=[str]) +def textColor(color): + """ + Modify the target's text color. + """ + spellbook.getTarget().b_setTextColor(color) + return 'Set your color to: %s' % color + +@magicWord(category=CATEGORY_MODERATOR, types=[str]) +def allSummons(): + """ + Max the target's summons + """ + target = spellbook.getTarget() + + numSuits = len(SuitDNA.suitHeadTypes) + fullSetForSuit = 1 | 2 | 4 | 8 | 16 | 32 + allSummons = numSuits * [fullSetForSuit] + target.b_setCogSummonsEarned(allSummons) + return 'Lots of summons!' + +@magicWord(category=CATEGORY_PROGRAMMER, types=[str]) +def maxToon(missingTrack=None): + """ + Max the target's stats for end-level gameplay. + """ + target = spellbook.getTarget() + + # First, unlock the target's Gag tracks: + gagTracks = [1, 1, 1, 1, 1, 1, 1] + if missingTrack is not None: + try: + index = ('toonup', 'trap', 'lure', 'sound', 'throw', + 'squirt', 'drop').index(missingTrack) + except: + return 'Missing Gag track is invalid!' + if index in (4, 5): + return 'You are required to have Throw and Squirt.' + gagTracks[index] = 0 + target.b_setTrackAccess(gagTracks) + target.b_setMaxCarry(80) + + # Next, max out their experience for the tracks they have: + experience = Experience.Experience(target.getExperience(), target) + for i, track in enumerate(target.getTrackAccess()): + if track: + experience.experience[i] = ( + Experience.MaxSkill - Experience.UberSkill) + target.b_setExperience(experience.makeNetString()) + + # Max out their Laff: + target.b_setMaxHp(ToontownGlobals.MaxHpLimit) + target.toonUp(target.getMaxHp() - target.hp) + + # Unlock all of the emotes: + emotes = list(target.getEmoteAccess()) + for emoteId in OTPLocalizer.EmoteFuncDict.values(): + if emoteId >= len(emotes): + continue + # The following emotions are ignored because they are unable to be + # obtained: + if emoteId in (17, 18, 19): + continue + emotes[emoteId] = 1 + target.b_setEmoteAccess(emotes) + + # Max out their Cog suits: + target.b_setCogParts( + [ + CogDisguiseGlobals.PartsPerSuitBitmasks[0], # Bossbot + CogDisguiseGlobals.PartsPerSuitBitmasks[1], # Lawbot + CogDisguiseGlobals.PartsPerSuitBitmasks[2], # Cashbot + CogDisguiseGlobals.PartsPerSuitBitmasks[3] # Sellbot + ] + ) + target.b_setCogLevels([49] * 4) + target.b_setCogTypes([7, 7, 7, 7]) + + # Max their Cog gallery: + deptCount = len(SuitDNA.suitDepts) + target.b_setCogCount(list(CogPageGlobals.COG_QUOTAS[1]) * deptCount) + cogStatus = [CogPageGlobals.COG_COMPLETE2] * SuitDNA.suitsPerDept + target.b_setCogStatus(cogStatus * deptCount) + target.b_setCogRadar([1, 1, 1, 1]) + target.b_setBuildingRadar([1, 1, 1, 1]) + + # Max out their racing tickets: + target.b_setTickets(99999) + + # Give them teleport access everywhere (including Cog HQs): + hoods = list(ToontownGlobals.HoodsForTeleportAll) + target.b_setHoodsVisited(hoods) + target.b_setTeleportAccess(hoods) + + # Max their quest carry limit: + target.b_setQuestCarryLimit(4) + + # Complete their quests: + target.b_setQuests([]) + target.b_setRewardHistory(Quests.ELDER_TIER, []) + + # Max their money: + target.b_setMaxMoney(250) + target.b_setMaxBankMoney(30000) + target.b_setMoney(target.getMaxMoney()) + target.b_setBankMoney(target.getMaxBankMoney()) + + # Finally, unlock all of their pet phrases: + if simbase.wantPets: + target.b_setPetTrickPhrases(range(7)) + + return 'Maxed your Toon!' + +@magicWord(category=CATEGORY_PROGRAMMER) +def unlocks(): + """ + Unlocks the target's teleport access, emotions, and pet trick phrases. + """ + target = spellbook.getTarget() + + # First, unlock their teleport access: + hoods = list(ToontownGlobals.HoodsForTeleportAll) + target.b_setHoodsVisited(hoods) + target.b_setTeleportAccess(hoods) + + # Next, unlock all of their emotions: + emotes = list(target.getEmoteAccess()) + for emoteId in OTPLocalizer.EmoteFuncDict.values(): + if emoteId >= len(emotes): + continue + # The following emotions are ignored because they are unable to be + # obtained: + if emoteId in (17, 18, 19): + continue + emotes[emoteId] = 1 + target.b_setEmoteAccess(emotes) + + # Finally, unlock all of their pet phrases: + if simbase.wantPets: + target.b_setPetTrickPhrases(range(7)) + + return 'Unlocked teleport access, emotions, and pet trick phrases!' + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int, str]) +def sos(count, name): + """ + Modifies the target's specified SOS card count. + """ + target = spellbook.getTarget() + if not 0 <= count <= 100: + return 'Your SOS count must be in range (0-100).' + for npcId, npcName in TTLocalizer.NPCToonNames.items(): + if name.lower() == npcName.lower(): + if npcId not in NPCToons.npcFriends: + continue + break + else: + return 'SOS card %s was not found!' % name + if (count == 0) and (npcId in target.NPCFriendsDict): + del target.NPCFriendsDict[npcId] + else: + target.NPCFriendsDict[npcId] = count + target.d_setNPCFriendsDict(target.NPCFriendsDict) + return "%s was given %d %s SOS cards." % (target.getName(), count, name) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def unites(value=32767): + """ + Restock all resistance messages. + """ + target = spellbook.getTarget() + value = min(value, 32767) + target.restockAllResistanceMessages(value) + return 'Restocked %d unites for %s!' % (value, target.getName()) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def fires(count): + """ + Modifies the target's pink slip count. + """ + target = spellbook.getTarget() + if not 0 <= count <= 255: + return 'Your fire count must be in range (0-255).' + target.b_setPinkSlips(count) + return '%s was given %d fires.' % (target.getName(), count) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def crateKeys(count): + """ + Modifies the invoker's crate key count. + """ + target = spellbook.getTarget() + if not 0 <= count <= 255: + return 'Your crate key must be in range (0-255).' + target.b_setCrateKeys(count) + return 'You were given %d crate keys.' % count + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def maxMoney(maxMoney): + """ + Modifies the target's max money value. + """ + if not 40 <= maxMoney <= 250: + return 'Max money value must be in xrange (40-250).' + target = spellbook.getTarget() + spellbook.getTarget().b_setMaxMoney(maxMoney) + return "Set {0}'s max money value to {1}!".format(target.getName(), maxMoney) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def maxBankMoney(maxBankMoney): + """ + Modifies the target's max bank money value. + """ + + if not 10000 <= maxBankMoney <= 30000: + return 'Max bank money value must be in xrange (10000-30000).' + target = spellbook.getTarget() + spellbook.getTarget().b_setMaxBankMoney(maxBankMoney) + return "Set {0}'s max bank money value to {1}!".format(target.getName(), maxBankMoney) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def money(money): + """ + Modifies the target's current money value. + """ + target = spellbook.getTarget() + maxMoney = target.getMaxMoney() + if not 0 <= money <= maxMoney: + return 'Money value must be in xrange (0-%d).' % maxMoney + target.b_setMoney(money) + return "Set %s's money value to %d!" % (target.getName(), money) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def bank(money): + """ + Modifies the target's current bank money value. + """ + target = spellbook.getTarget() + maxMoney = target.getMaxBankMoney() + + if not 0 <= money <= maxMoney: + return 'Bank money must be in xrange (0-%d.)' % maxMoney + target.b_setBankMoney(money) + return "Set %s's bank money value to %d!" % (target.getName(), money) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def fishingRod(rod): + """ + Modify the target's fishing rod value. + """ + if not 0 <= rod <= 4: + return 'Rod value must be in xrange (0-4).' + target = spellbook.getTarget() + target.b_setFishingRod(rod) + target.b_setMaxFishingRod(rod) + return "Set %s's fishing rod to %d!" % (target.getName(), rod) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def maxFishTank(maxFishTank): + """ + Modify the target's max fish tank value. + """ + if not 20 <= maxFishTank <= FishGlobals.MaxTank: + return 'Max fish tank value must be in xrange (20-%s).' % FishGlobals.MaxTank + target = spellbook.getTarget() + target.b_setMaxFishTank(maxFishTank) + return "Set %s's max fish tank value to %d!" % (target.getName(), maxFishTank) + +@magicWord(category=CATEGORY_ADMINISTRATOR, types=[str]) +def name(name=''): + """ + Modify the target's name. + """ + target = spellbook.getTarget() + _name = target.getName() + target.b_setName(name) + if name: + return "Set %s's name to %s!" % (_name, name) + else: + return "%s's name is now empty!" % _name + +@magicWord(category=CATEGORY_SYSTEM_ADMINISTRATOR, types=[int]) +def squish(laff): + """ + Squish a toon. + """ + target = spellbook.getTarget() + target.squish(laff) + +@magicWord(category=CATEGORY_CREATIVE, types=[int, int]) +def hat(hatIndex, hatTex=0): + """ + Modify the target's hat. + """ + if not 0 <= hatIndex < len(ToonDNA.HatModels): + return 'Invalid hat index.' + if not 0 <= hatTex < len(ToonDNA.HatTextures): + return 'Invalid hat texture.' + target = spellbook.getTarget() + target.b_setHat(hatIndex, hatTex, 0) + return "Set %s's hat to %d, %d!" % (target.getName(), hatIndex, hatTex) + +@magicWord(category=CATEGORY_CREATIVE, types=[int, int]) +def glasses(glassesIndex, glassesTex=0): + """ + Modify the target's glasses. + """ + if not 0 <= glassesIndex < len(ToonDNA.GlassesModels): + return 'Invalid glasses index.' + if not 0 <= glassesTex < len(ToonDNA.GlassesTextures): + return 'Invalid glasses texture.' + target = spellbook.getTarget() + target.b_setGlasses(glassesIndex, glassesTex, 0) + return "Set %s's glasses to %d, %d!" % (target.getName(), glassesIndex, glassesTex) + +@magicWord(category=CATEGORY_CREATIVE, types=[int, int]) +def backpack(backpackIndex, backpackTex=0): + """ + Modify the target's backpack. + """ + if not 0 <= backpackIndex < len(ToonDNA.BackpackModels): + return 'Invalid backpack index.' + if not 0 <= backpackTex < len(ToonDNA.BackpackTextures): + return 'Invalid backpack texture.' + target = spellbook.getTarget() + target.b_setBackpack(backpackIndex, backpackTex, 0) + return "Set %s's backpack to %d, %d!" % (target.getName(), backpackIndex, backpackTex) + +@magicWord(category=CATEGORY_CREATIVE, types=[int, int]) +def shoes(shoesIndex, shoesTex=0): + """ + Modify the target's shoes. + """ + if not 0 <= shoesIndex < len(ToonDNA.ShoesModels): + return 'Invalid shoes index.' + if not 0 <= shoesTex < len(ToonDNA.ShoesTextures): + return 'Invalid shoes texture.' + target = spellbook.getTarget() + target.b_setShoes(shoesIndex, shoesTex, 0) + return "Set %s's shoes to %d, %d!" % (target.getName(), shoesIndex, shoesTex) + +@magicWord(category=CATEGORY_COMMUNITY_MANAGER) +def ghost(): + """ + Toggles invisibility on the invoker. Anyone with an access level below the + invoker will not be able to see him or her. + """ + invoker = spellbook.getInvoker() + if invoker.ghostMode == 0: + invoker.b_setGhostMode(2) + return 'Ghost mode is enabled.' + else: + invoker.b_setGhostMode(0) + return 'Ghost mode is disabled.' + +@magicWord(category=CATEGORY_MODERATOR) +def badName(): + """ + Revoke the target's name. + """ + target = spellbook.getTarget() + _name = target.getName() + colorString = TTLocalizer.ColorfulToon + animalType = TTLocalizer.AnimalToSpecies[target.dna.getAnimal()] + target.b_setName(colorString + ' ' + animalType) + target.sendUpdate('setWishNameState', ['REJECTED']) + return "Revoked %s's name!" % _name + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def tickets(tickets): + """ + Set the invoker's racing tickets value. + """ + if not 0 <= tickets <= 99999: + return 'Racing tickets value must be in range (0-99999).' + invoker = spellbook.getInvoker() + invoker.b_setTickets(tickets) + return 'Set your tickets to: %d' % tickets + +@magicWord(category=CATEGORY_ADMINISTRATOR, types=[int]) +def cogIndex(index): + """ + Modifies the invoker's Cog index. + """ + if not -1 <= index <= 3: + return 'Invalid Cog index.' + invoker = spellbook.getInvoker() + invoker.b_setCogIndex(index) + return 'Set your Cog index to %d!' % index + +@magicWord(category=CATEGORY_PROGRAMMER, types=[str, int, int]) +def inventory(a, b=None, c=None): + target = spellbook.getTarget() + inventory = target.inventory + if a == 'reset': + maxLevelIndex = b or 5 + if not 0 <= maxLevelIndex < len(ToontownBattleGlobals.Levels[0]): + return 'Invalid max level index: ' + str(maxLevelIndex) + targetTrack = -1 or c + if not -1 <= targetTrack < len(ToontownBattleGlobals.Tracks): + return 'Invalid target track index: ' + str(targetTrack) + for track in xrange(0, len(ToontownBattleGlobals.Tracks)): + if (targetTrack == -1) or (track == targetTrack): + inventory.inventory[track][:maxLevelIndex + 1] = [0] * (maxLevelIndex+1) + target.b_setInventory(inventory.makeNetString()) + if targetTrack == -1: + return 'Inventory reset.' + else: + return 'Inventory reset for target track index: ' + str(targetTrack) + elif a == 'restock': + maxLevelIndex = b or 5 + if not 0 <= maxLevelIndex < len(ToontownBattleGlobals.Levels[0]): + return 'Invalid max level index: ' + str(maxLevelIndex) + targetTrack = -1 or c + if not -1 <= targetTrack < len(ToontownBattleGlobals.Tracks): + return 'Invalid target track index: ' + str(targetTrack) + if (targetTrack != -1) and (not target.hasTrackAccess(targetTrack)): + return "You don't have target track index: " + str(targetTrack) + inventory.NPCMaxOutInv(targetTrack=targetTrack, maxLevelIndex=maxLevelIndex) + target.b_setInventory(inventory.makeNetString()) + if targetTrack == -1: + return 'Inventory restocked.' + else: + return 'Inventory restocked for target track index: ' + str(targetTrack) + else: + try: + targetTrack = int(a) + except: + return 'Invalid first argument.' + if not target.hasTrackAccess(targetTrack): + return "You don't have target track index: " + str(targetTrack) + maxLevelIndex = b or 6 + if not 0 <= maxLevelIndex < len(ToontownBattleGlobals.Levels[0]): + return 'Invalid max level index: ' + str(maxLevelIndex) + for _ in xrange(c): + inventory.addItem(targetTrack, maxLevelIndex) + target.b_setInventory(inventory.makeNetString()) + return 'Restored %d Gags to: %d, %d' % (c, targetTrack, maxLevelIndex) + +@magicWord(category=CATEGORY_CREATIVE, types=[str, str]) +def dna(part, value): + """Modify a DNA part on the target.""" + target = spellbook.getTarget() + + dna = ToonDNA.ToonDNA() + dna.makeFromNetString(target.getDNAString()) + + part = part.lower() + if part.endswith('color') or part.endswith('tex') or part.endswith('size'): + value = int(value) + + if part == 'gender': + if value not in ('m', 'f', 'male', 'female'): + return 'Invalid gender: ' + value + dna.gender = value[0] + target.b_setDNAString(dna.makeNetString()) + return 'Gender set to: ' + dna.gender + + if part in ('head', 'species'): + speciesNames = ( + 'dog', 'cat', 'horse', 'mouse', 'rabbit', 'duck', 'monkey', 'bear', + 'pig' + ) + if value in speciesNames: + speciesIndex = speciesNames.index(value) + value = ToonDNA.toonSpeciesTypes[speciesIndex] + if value not in ToonDNA.toonSpeciesTypes: + return 'Invalid species: ' + value + dna.head = value + dna.head[1:3] + target.b_setDNAString(dna.makeNetString()) + return 'Species set to: ' + dna.head[0] + + if part == 'headsize': + sizes = ('ls', 'ss', 'sl', 'll') + if not 0 <= value <= len(sizes): + return 'Invalid head size index: ' + str(value) + dna.head = dna.head[0] + sizes[value] + target.b_setDNAString(dna.makeNetString()) + return 'Head size index set to: ' + dna.head[1:] + + if part == 'torso': + if dna.gender not in ('m', 'f'): + return 'Unknown gender.' + value = int(value) + if (dna.gender == 'm') and (not 0 <= value <= 2): + return 'Male torso index out of range (0-2).' + if (dna.gender == 'f') and (not 3 <= value <= 8): + return 'Female torso index out of range (3-8).' + dna.torso = ToonDNA.toonTorsoTypes[value] + target.b_setDNAString(dna.makeNetString()) + return 'Torso set to: ' + dna.torso + + if part == 'legs': + value = int(value) + if not 0 <= value <= len(ToonDNA.toonLegTypes): + return 'Legs index out of range (0-%d).' % len(ToonDNA.toonLegTypes) + dna.legs = ToonDNA.toonLegTypes[value] + target.b_setDNAString(dna.makeNetString()) + return 'Legs set to: ' + dna.legs + + if part == 'headcolor': + if value not in ToonDNA.defaultColorList: + return 'Invalid head color index: ' + str(value) + dna.headColor = value + target.b_setDNAString(dna.makeNetString()) + return 'Head color index set to: ' + str(dna.headColor) + + if part == 'armcolor': + if value not in ToonDNA.defaultColorList: + return 'Invalid arm color index: ' + str(value) + dna.armColor = value + target.b_setDNAString(dna.makeNetString()) + return 'Arm color index set to: ' + str(dna.armColor) + + if part == 'legcolor': + if value not in ToonDNA.defaultColorList: + return 'Invalid leg color index: ' + str(value) + dna.legColor = value + target.b_setDNAString(dna.makeNetString()) + return 'Leg color index set to: ' + str(dna.legColor) + + if part == 'color': + if (value not in ToonDNA.defaultColorList) and (value != 0x1a) and (value != 0x00): + return 'Invalid color index: ' + str(value) + if (value == 0x1a) and (dna.getAnimal() != 'cat'): + return 'Invalid color index for species: ' + dna.getAnimal() + if (value == 0x00) and (dna.getAnimal() != 'bear'): + return 'Invalid color index for species: ' + dna.getAnimal() + dna.headColor = value + dna.armColor = value + dna.legColor = value + target.b_setDNAString(dna.makeNetString()) + return 'Color index set to: ' + str(dna.headColor) + + if part == 'gloves': + value = int(value) + dna.gloveColor = value + target.b_setDNAString(dna.makeNetString()) + return 'Glove color set to: ' + str(dna.gloveColor) + + if part == 'toptex': + if not 0 <= value <= len(ToonDNA.Shirts): + return 'Top texture index out of range (0-%d).' % len(ToonDNA.Shirts) + dna.topTex = value + target.b_setDNAString(dna.makeNetString()) + return 'Top texture index set to: ' + str(dna.topTex) + + if part == 'toptexcolor': + if not 0 <= value <= len(ToonDNA.ClothesColors): + return 'Top texture color index out of range(0-%d).' % len(ToonDNA.ClothesColors) + dna.topTexColor = value + target.b_setDNAString(dna.makeNetString()) + return 'Top texture color index set to: ' + str(dna.topTexColor) + + if part == 'sleevetex': + if not 0 <= value <= len(ToonDNA.Sleeves): + return 'Sleeve texture index out of range(0-%d).' % len(ToonDNA.Sleeves) + dna.sleeveTex = value + target.b_setDNAString(dna.makeNetString()) + return 'Sleeve texture index set to: ' + str(dna.sleeveTex) + + if part == 'sleevetexcolor': + if not 0 <= value <= len(ToonDNA.ClothesColors): + return 'Sleeve texture color index out of range(0-%d).' % len(ToonDNA.ClothesColors) + dna.sleeveTexColor = value + target.b_setDNAString(dna.makeNetString()) + return 'Sleeve texture color index set to: ' + str(dna.sleeveTexColor) + + if part == 'bottex': + if dna.gender not in ('m', 'f'): + return 'Unknown gender.' + if dna.gender == 'm': + bottoms = ToonDNA.BoyShorts + else: + bottoms = ToonDNA.GirlBottoms + if not 0 <= value <= len(bottoms): + return 'Bottom texture index out of range (0-%d).' % len(bottoms) + dna.botTex = value + target.b_setDNAString(dna.makeNetString()) + return 'Bottom texture index set to: ' + str(dna.botTex) + + if part == 'bottexcolor': + if not 0 <= value <= len(ToonDNA.ClothesColors): + return 'Bottom texture color index out of range(0-%d).' % len(ToonDNA.ClothesColors) + dna.botTexColor = value + target.b_setDNAString(dna.makeNetString()) + return 'Bottom texture color index set to: ' + str(dna.botTexColor) + + if part == 'show': + return dna.asNpcTuple() if value else dna.asTuple() + if part == 'showrandom': + return NPCToons.getRandomDNA(time.time(), value) + return 'Invalid part: ' + part + +@magicWord(category=CATEGORY_ADMINISTRATOR, types=[int]) +def trophyScore(value): + """ + Modifies the target's trophy score. + """ + if value < 0: + return 'Invalid trophy score: ' + str(value) + target = spellbook.getTarget() + simbase.air.trophyMgr.updateTrophyScore(target.doId, value) + return "%s's trophy score has been set to: %d" % (target.getName(), value) + +@magicWord(category=CATEGORY_ADMINISTRATOR, types=[int, int]) +def givePies(pieType, numPies=0): + """ + Give the target (numPies) of (pieType) pies. + """ + target = spellbook.getTarget() + if pieType == -1: + target.b_setNumPies(0) + return "Removed %s's pies." % target.getName() + if not 0 <= pieType <= 7: + return 'Pie type must be in range (0-7).' + if not -1 <= numPies <= 99: + return 'Pie count out of range (-1-99).' + target.b_setPieType(pieType) + if numPies >= 0: + target.b_setNumPies(numPies) + else: + target.b_setNumPies(ToontownGlobals.FullPies) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int, int]) +def trackBonus(trackIndex, level): + """ + Modify the invoker's track bonus level. + """ + invoker = spellbook.getInvoker() + if not 0 <= trackIndex < 7: + return 'Invalid track index!' + if not -1 <= level <= 6: + return 'Invalid level!' + trackBonusLevel = [0] * 7 + trackBonusLevel[trackIndex] = level + invoker.b_setTrackBonusLevel(trackBonusLevel) + return 'Your track bonus level has been set to %s!' % level + +@magicWord(category=CATEGORY_PROGRAMMER, types=[str, str, int]) +def track(command, track, value=None): + try: + index = ('toonup', 'trap', 'lure', 'sound', 'throw', + 'squirt', 'drop').index(track.lower()) + except: + return 'Invalid Gag track!' + invoker = spellbook.getInvoker() + trackAccess = invoker.getTrackAccess() + if (command.lower() not in ('add',)) and (not trackAccess[index]): + return "You don't have that track!" + if command.lower() == 'remove': + if index in (4, 5): + return "You can't remove throw and squirt!" + trackAccess[index] = 0 + invoker.b_setTrackAccess(trackAccess) + return 'Removed the %s track!' % track + if command.lower() == 'add': + trackAccess[index] = 1 + invoker.b_setTrackAccess(trackAccess) + return 'Added the %s track!' % track + if command.lower() == 'experience': + if value is None: + return 'You must provide an experience value.' + if not 0 <= value <= Experience.MaxSkill: + return 'Experience value not in xrange (0-%d).' % Experience.MaxSkill + experience = Experience.Experience(invoker.getExperience(), invoker) + experience.experience[index] = value + invoker.b_setExperience(experience.makeNetString()) + return 'Set the experience of the %s track to: %d!' % (track, value) + return 'Invalid command.' + +@magicWord(category=CATEGORY_ADMINISTRATOR, types=[str, str]) +def suit(command, suitName = 'f'): + invoker = spellbook.getInvoker() + command = command.lower() + if suitName not in SuitDNA.suitHeadTypes: + return 'Invalid suit name: ' + suitName + suitFullName = SuitBattleGlobals.SuitAttributes[suitName]['name'] + if command == 'spawn': + returnCode = invoker.doSummonSingleCog(SuitDNA.suitHeadTypes.index(suitName)) + if returnCode[0] == 'success': + return 'Successfully spawned: ' + suitFullName + return "Couldn't spawn: " + suitFullName + elif command == 'building': + returnCode = invoker.doBuildingTakeover(SuitDNA.suitHeadTypes.index(suitName)) + if returnCode[0] == 'success': + return 'Successfully spawned a Cog building with: ' + suitFullName + return "Couldn't spawn a Cog building with: " + suitFullName + elif command == 'nobuilding': + returnCode = invoker.doToonBuildingTakeover() + if returnCode[0] == 'success': + return 'Toons took over the cog building!' + return "Couldn't allow toons to take over cog building because " + returnCode[1] + else: + return 'Invalid command.' + +@magicWord(category=CATEGORY_PROGRAMMER) +def getZone(): + invoker = spellbook.getInvoker() + zone = invoker.zoneId + return 'ZoneID: %s' % (zone) + +@magicWord(category=CATEGORY_MODERATOR, types=[int]) +def nametagStyle(nametagStyle): + if nametagStyle >= len(TTLocalizer.NametagFontNames): + return 'Invalid nametag style.' + target = spellbook.getTarget() + target.b_setNametagStyle(nametagStyle) + target.addNametagStyle(nametagStyle) + return 'Nametag style set to: %s.' % TTLocalizer.NametagFontNames[nametagStyle] + +@magicWord(category=CATEGORY_PROGRAMMER, types=[str, int, int]) +def disguise(command, suitIndex, value): + invoker = spellbook.getInvoker() + + if suitIndex > 3: + return 'Invalid suit index: %s' % suitIndex + if value < 0: + return 'Invalid value: %s' % value + + if command == 'parts': + invoker.cogParts[suitIndex] = 0 + for _ in xrange(value): + invoker.giveGenericCogPart('fullSuit', suitIndex) + return 'Parts set.' + elif command == 'tier': + invoker.cogTypes[suitIndex] = value + invoker.d_setCogTypes(invoker.cogTypes) + return 'Tier set.' + elif command == 'level': + invoker.cogLevels[suitIndex] = value + invoker.d_setCogLevels(invoker.cogLevels) + return 'Level set.' + elif command == 'merits': + invoker.cogMerits[suitIndex] = value + invoker.d_setCogMerits(invoker.cogMerits) + return 'Merits set.' + else: + return 'Unknow command: %s' % command + +@magicWord(category=CATEGORY_PROGRAMMER) +def unlimitedGags(): + """ Restock avatar's gags at the start of each round. """ + av = spellbook.getTarget() if spellbook.getInvokerAccess() >= 500 else spellbook.getInvoker() + av.setUnlimitedGags(not av.unlimitedGags) + return 'Toggled unlimited gags %s for %s' % ('ON' if av.unlimitedGags else 'OFF', av.getName()) + +@magicWord(category=CATEGORY_PROGRAMMER) +def maxCogPage(): + """ Max the target's discovered cogs. """ + target = spellbook.getTarget() + deptCount = len(SuitDNA.suitDepts) + target.b_setCogCount(list(CogPageGlobals.COG_QUOTAS[1]) * deptCount) + cogStatus = [CogPageGlobals.COG_COMPLETE2] * SuitDNA.suitsPerDept + target.b_setCogStatus(cogStatus * deptCount) + target.b_setCogRadar([1, 1, 1, 1]) + target.b_setBuildingRadar([1, 1, 1, 1]) + return 'Maxed %s\'s discovered cogs!' % (target.getName()) + +@magicWord(category=CATEGORY_PROGRAMMER) +def immortal(): + """ Make target (if 500+) or self (if 499-) immortal. """ + av = spellbook.getTarget() if spellbook.getInvokerAccess() >= 500 else spellbook.getInvoker() + av.setImmortalMode(not av.immortalMode) + return 'Toggled immortal mode %s for %s' % ('ON' if av.immortalMode else 'OFF', av.getName()) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[str, int]) +def summoncogdo(track="s", difficulty=5): + tracks = CogdoUtil.getAllowedTracks() + + if track not in tracks: + return "Invalid track!" + + av = spellbook.getInvoker() + building = av.findClosestDoor() + if building == None: + return "No Toon building found!" + + building.cogdoTakeOver(difficulty, 2, track) + return 'Successfully spawned cogdo with track %s and difficulty %d' % (track, difficulty) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int, int]) +def emblems(silver=10, gold=10): + spellbook.getTarget().addEmblems((gold, silver)) + return 'Restocked with Gold: %s Silver: %d' % (gold, silver) + +@magicWord(category=CATEGORY_PROGRAMMER) +def catalog(): + simbase.air.catalogManager.deliverCatalogFor(spellbook.getTarget()) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[str]) +def remCode(code): + av = spellbook.getTarget() + if av.isCodeRedeemed(code): + av.removeCode(code) + return 'Player can now reuse the code %s' % code + else: + return "Player hasn't redeemed this code!" + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def shovelSkill(skill): + """ + Update shovel skill. + """ + av = spellbook.getTarget() + av.b_setShovelSkill(skill) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def canSkill(skill): + """ + Update watering can skill. + """ + av = spellbook.getTarget() + av.b_setWateringCanSkill(skill) + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int, str]) +def epp(dept, command="add"): + av = spellbook.getTarget() + if command == "add": + av.addEPP(dept) + + elif command == "remove": + av.removeEPP(dept) + + elif command == "get": + if dept == -1: + return av.epp + + return av.hasEPP(dept) + + else: + return "Unknown command!" + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def promote(dept): + spellbook.getTarget().b_promote(dept) + +@magicWord(category=CATEGORY_PROGRAMMER) +def maxGarden(): + av = spellbook.getInvoker() + av.b_setShovel(3) + av.b_setWateringCan(3) + av.b_setShovelSkill(639) + av.b_setWateringCanSkill(999) diff --git a/toontown/toon/DistributedToonUD.py b/toontown/toon/DistributedToonUD.py new file mode 100755 index 00000000..01b0d550 --- /dev/null +++ b/toontown/toon/DistributedToonUD.py @@ -0,0 +1,527 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectUD import DistributedObjectUD + +class DistributedToonUD(DistributedObjectUD): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedToonUD") + + def setAdminAccess(self, todo0): + pass + + def setDNAString(self, todo0): + pass + + def setMaxBankMoney(self, todo0): + pass + + def setBankMoney(self, todo0): + pass + + def setMaxMoney(self, todo0): + pass + + def setMoney(self, todo0): + pass + + def setMaxHp(self, todo0): + pass + + def setHp(self, todo0): + pass + + def toonUp(self, todo0): + pass + + def takeDamage(self, todo0): + pass + + def setBattleId(self, todo0): + pass + + def setExperience(self, todo0): + pass + + def setMaxCarry(self, todo0): + pass + + def setTrackAccess(self, todo0): + pass + + def setTrackProgress(self, todo0, todo1): + pass + + def setTrackBonusLevel(self, todo0): + pass + + def setInventory(self, todo0): + pass + + def setNPCFriendsDict(self, todo0): + pass + + def setDefaultShard(self, todo0): + pass + + def setDefaultZone(self, todo0): + pass + + def setHoodsVisited(self, todo0): + pass + + def setLastHood(self, todo0): + pass + + def setTutorialAck(self, todo0): + pass + + def setMaxClothes(self, todo0): + pass + + def setClothesTopsList(self, todo0): + pass + + def setClothesBottomsList(self, todo0): + pass + + def setHatList(self, todo0): + pass + + def setGlassesList(self, todo0): + pass + + def setBackpackList(self, todo0): + pass + + def setShoesList(self, todo0): + pass + + def setHat(self, todo0, todo1, todo2): + pass + + def setGlasses(self, todo0, todo1, todo2): + pass + + def setBackpack(self, todo0, todo1, todo2): + pass + + def setShoes(self, todo0, todo1, todo2): + pass + + def setGardenSpecials(self, todo0): + pass + + def setEarnedExperience(self, todo0): + pass + + def setTunnelIn(self, todo0, todo1, todo2, todo3, todo4, todo5): + pass + + def setTunnelOut(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6): + pass + + def setAnimState(self, todo0, todo1, todo2): + pass + + def setEmoteState(self, todo0, todo1, todo2): + pass + + def setEmoteAccess(self, todo0): + pass + + def setCustomMessages(self, todo0): + pass + + def setSleepAutoReply(self, todo0): + pass + + def setResistanceMessages(self, todo0): + pass + + def setPetTrickPhrases(self, todo0): + pass + + def setCatalogSchedule(self, todo0, todo1): + pass + + def setCatalog(self, todo0, todo1, todo2): + pass + + def setMailboxContents(self, todo0): + pass + + def setDeliverySchedule(self, todo0): + pass + + def setGiftSchedule(self, todo0): + pass + + def setAwardMailboxContents(self, todo0): + pass + + def setAwardSchedule(self, todo0): + pass + + def setAwardNotify(self, todo0): + pass + + def setCatalogNotify(self, todo0, todo1): + pass + + def playSplashEffect(self, todo0, todo1, todo2): + pass + + def setWhisperSCToontaskFrom(self, todo0, todo1, todo2, todo3, todo4): + pass + + def setSCToontask(self, todo0, todo1, todo2, todo3): + pass + + def reqSCResistance(self, todo0, todo1): + pass + + def setSCResistance(self, todo0, todo1): + pass + + def setSpeedChatStyleIndex(self, todo0): + pass + + def setTrophyScore(self, todo0): + pass + + def setTeleportAccess(self, todo0): + pass + + def checkTeleportAccess(self, todo0): + pass + + def setScavengerHunt(self, todo0): + pass + + def battleSOS(self, todo0): + pass + + def teleportQuery(self, todo0): + pass + + def teleportResponse(self, todo0, todo1, todo2, todo3, todo4): + pass + + def teleportResponseToAI(self, todo0, todo1, todo2, todo3, todo4, todo5): + pass + + def teleportGiveup(self, todo0): + pass + + def teleportGreeting(self, todo0): + pass + + def setCogStatus(self, todo0): + pass + + def setCogCount(self, todo0): + pass + + def setCogRadar(self, todo0): + pass + + def setBuildingRadar(self, todo0): + pass + + def setCogLevels(self, todo0): + pass + + def setCogTypes(self, todo0): + pass + + def setCogParts(self, todo0): + pass + + def setCogMerits(self, todo0): + pass + + def setCogIndex(self, todo0): + pass + + def setDisguisePageFlag(self, todo0): + pass + + def setSosPageFlag(self, todo0): + pass + + def setHouseId(self, todo0): + pass + + def setQuests(self, todo0): + pass + + def setQuestHistory(self, todo0): + pass + + def setRewardHistory(self, todo0, todo1): + pass + + def setQuestCarryLimit(self, todo0): + pass + + def requestDeleteQuest(self, todo0): + pass + + def setCheesyEffect(self, todo0, todo1, todo2): + pass + + def setGhostMode(self, todo0): + pass + + def setFishCollection(self, todo0, todo1, todo2): + pass + + def setMaxFishTank(self, todo0): + pass + + def setFishTank(self, todo0, todo1, todo2): + pass + + def setFishingRod(self, todo0): + pass + + def setMaxFishingRod(self, todo0): + pass + + def setFishingTrophies(self, todo0): + pass + + def setFlowerCollection(self, todo0, todo1): + pass + + def setFlowerBasket(self, todo0, todo1): + pass + + def setMaxFlowerBasket(self, todo0): + pass + + def setGardenTrophies(self, todo0): + pass + + def setShovel(self, todo0): + pass + + def setShovelSkill(self, todo0): + pass + + def setWateringCan(self, todo0): + pass + + def setWateringCanSkill(self, todo0): + pass + + def promoteShovel(self, todo0): + pass + + def promoteWateringCan(self, todo0): + pass + + def reactivateWater(self): + pass + + def presentPie(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6): + pass + + def tossPie(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6, todo7, todo8): + pass + + def pieSplat(self, todo0, todo1, todo2, todo3, todo4, todo5): + pass + + def setPieType(self, todo0): + pass + + def setNumPies(self, todo0): + pass + + def catalogGenClothes(self, todo0): + pass + + def catalogGenAccessories(self, todo0): + pass + + def setPetId(self, todo0): + pass + + def setPetMovie(self, todo0, todo1): + pass + + def setPetTutorialDone(self, todo0): + pass + + def setFishBingoTutorialDone(self, todo0): + pass + + def setFishBingoMarkTutorialDone(self, todo0): + pass + + def setKartBodyType(self, todo0): + pass + + def setKartBodyColor(self, todo0): + pass + + def setKartAccessoryColor(self, todo0): + pass + + def setKartEngineBlockType(self, todo0): + pass + + def setKartSpoilerType(self, todo0): + pass + + def setKartFrontWheelWellType(self, todo0): + pass + + def setKartBackWheelWellType(self, todo0): + pass + + def setKartRimType(self, todo0): + pass + + def setKartDecalType(self, todo0): + pass + + def updateKartDNAField(self, todo0, todo1): + pass + + def addOwnedAccessory(self, todo0): + pass + + def removeOwnedAccessory(self, todo0): + pass + + def setTickets(self, todo0): + pass + + def setKartingHistory(self, todo0): + pass + + def setKartingTrophies(self, todo0): + pass + + def setKartingPersonalBest(self, todo0): + pass + + def setKartingPersonalBest2(self, todo0): + pass + + def setKartAccessoriesOwned(self, todo0): + pass + + def setCurrentKart(self, todo0): + pass + + def squish(self, todo0): + pass + + def announceBingo(self): + pass + + def setCogSummonsEarned(self, todo0): + pass + + def reqCogSummons(self, todo0, todo1): + pass + + def cogSummonsResponse(self, todo0, todo1, todo2): + pass + + def reqUseSpecial(self, todo0): + pass + + def useSpecialResponse(self, todo0): + pass + + def setGardenStarted(self, todo0): + pass + + def sendToGolfCourse(self, todo0): + pass + + def setGolfHistory(self, todo0): + pass + + def setPackedGolfHoleBest(self, todo0): + pass + + def setGolfCourseBest(self, todo0): + pass + + def setUnlimitedSwing(self, todo0): + pass + + def logSuspiciousEvent(self, todo0): + pass + + def forceLogoutWithNotify(self): + pass + + def setSpecialInventory(self, todo0): + pass + + def setNametagStyle(self, todo0): + pass + + def setNametagStyles(self, todo0): + return + + def setMail(self, todo0): + pass + + def setNumMailItems(self, todo0): + pass + + def setSimpleMailNotify(self, todo0): + pass + + def setInvites(self, todo0): + pass + + def setPartiesInvitedTo(self, todo0): + pass + + def setHostedParties(self, todo0): + pass + + def setPartyReplies(self, todo0): + pass + + def updateInvite(self, todo0, todo1): + pass + + def updateReply(self, todo0, todo1, todo2): + pass + + def setPartyCanStart(self, todo0): + pass + + def setPartyStatus(self, todo0, todo1): + pass + + def announcePartyStarted(self, todo0): + pass + + def setNeverStartedPartyRefunded(self, todo0, todo1, todo2): + pass + + def setDISLid(self, todo0): + pass + + def setRedeemedCodes(self, redeemedCodes): + pass + + def setTrueFriends(self, trueFriends): + pass + + def setWishName(self, todo0): + pass + + def setWishNameState(self, todo0): + pass + + def setStats(self, todo0): + pass diff --git a/toontown/toon/ElevatorNotifier.py b/toontown/toon/ElevatorNotifier.py new file mode 100755 index 00000000..dcc2739a --- /dev/null +++ b/toontown/toon/ElevatorNotifier.py @@ -0,0 +1,77 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toontowngui import TTDialog + +class ElevatorNotifier: + notify = DirectNotifyGlobal.directNotify.newCategory('CatalogNotifyDialog') + + def __init__(self): + self.frame = None + return + + def handleButton(self): + self.__handleButton(1) + + def createFrame(self, message, framePos = None, withStopping = True, ttDialog = False): + if not framePos: + framePos = (0.0, 0, 0.78) + if not ttDialog: + self.frame = DirectFrame(relief=None, image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.4), text=message, text_wordwrap=16, text_scale=0.06, text_pos=(-0.0, 0.1), pos=framePos) + else: + self.frame = TTDialog.TTDialog(relief=None, image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.4), text=message, text_wordwrap=16, text_scale=0.06, text_pos=(-0.0, 0.1), pos=framePos) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.doneButton = DirectButton(parent=self.frame, relief=None, image=self.cancelImageList, command=self.handleButton, pos=(0, 0, -0.14)) + if not withStopping: + self.doneButton['command'] = self.__handleButtonWithoutStopping + self.doneButton.show() + self.frame.show() + return + + def cleanup(self): + if self.frame: + self.frame.destroy() + self.frame = None + self.nextButton = None + self.doneButton = None + self.okImageList = None + self.cancelImageList = None + return + + def setOkButton(self): + self.doneButton['image'] = self.okImageList + + def setCancelButton(self): + self.doneButton['image'] = self.cancelImageList + + def __handleButton(self, value): + self.cleanup() + place = base.cr.playGame.getPlace() + if place: + place.setState('walk') + + def showMe(self, message, pos = None, ttDialog = False): + if self.frame == None: + place = base.cr.playGame.getPlace() + if place: + self.createFrame(message, pos, True, ttDialog) + place.setState('stopped') + return + + def showMeWithoutStopping(self, message, pos = None, ttDialog = False): + if self.frame == None: + self.createFrame(message, pos, False, ttDialog) + return + + def __handleButtonWithoutStopping(self): + self.cleanup() + + def isNotifierOpen(self): + if self.frame: + return True + else: + return False diff --git a/toontown/toon/Experience.py b/toontown/toon/Experience.py new file mode 100755 index 00000000..c0e60f92 --- /dev/null +++ b/toontown/toon/Experience.py @@ -0,0 +1,129 @@ +from panda3d.core import * +from toontown.toonbase.ToontownBattleGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.PyDatagramIterator import PyDatagramIterator +from otp.otpbase import OTPGlobals + +class Experience: + notify = DirectNotifyGlobal.directNotify.newCategory('Experience') + + def __init__(self, expStr = None, owner = None): + self.owner = owner + if expStr == None: + self.experience = [] + for track in xrange(0, len(Tracks)): + self.experience.append(StartingLevel) + + else: + self.experience = self.makeFromNetString(expStr) + return + + def __str__(self): + return str(self.experience) + + def makeNetString(self): + dataList = self.experience + datagram = PyDatagram() + for track in xrange(0, len(Tracks)): + datagram.addUint16(dataList[track]) + + dgi = PyDatagramIterator(datagram) + return dgi.getRemainingBytes() + + def makeFromNetString(self, netString): + dataList = [] + dg = PyDatagram(netString) + dgi = PyDatagramIterator(dg) + for track in xrange(0, len(Tracks)): + dataList.append(dgi.getUint16()) + + return dataList + + def addExp(self, track, amount = 1): + if type(track) == type(''): + track = Tracks.index(track) + self.notify.debug('adding %d exp to track %d' % (amount, track)) + if self.experience[track] + amount <= MaxSkill: + self.experience[track] += amount + else: + self.experience[track] = MaxSkill + + def maxOutExp(self): + for track in xrange(0, len(Tracks)): + self.experience[track] = MaxSkill - UberSkill + + def maxOutExpMinusOne(self): + for track in xrange(0, len(Tracks)): + self.experience[track] = MaxSkill - 1 + + def makeExpHigh(self): + for track in xrange(0, len(Tracks)): + self.experience[track] = Levels[track][len(Levels[track]) - 1] - 1 + + def makeExpRegular(self): + import random + for track in xrange(0, len(Tracks)): + rank = random.choice((0, int(random.random() * 1500.0), int(random.random() * 2000.0))) + self.experience[track] = Levels[track][len(Levels[track]) - 1] - rank + + def zeroOutExp(self): + for track in xrange(0, len(Tracks)): + self.experience[track] = StartingLevel + + def setAllExp(self, num): + for track in xrange(0, len(Tracks)): + self.experience[track] = num + + def getExp(self, track): + if type(track) == type(''): + track = Tracks.index(track) + return self.experience[track] + + def setExp(self, track, exp): + if type(track) == type(''): + track = Tracks.index(track) + self.experience[track] = exp + + def getExpLevel(self, track): + if type(track) == type(''): + track = Tracks.index(track) + level = 0 + for amount in Levels[track]: + if self.experience[track] >= amount: + level = Levels[track].index(amount) + + return level + + def getTotalExp(self): + total = 0 + for level in self.experience: + total += level + + return total + + def getNextExpValue(self, track, curSkill = None): + if curSkill == None: + curSkill = self.experience[track] + retVal = Levels[track][len(Levels[track]) - 1] + for amount in Levels[track]: + if curSkill < amount: + retVal = amount + return retVal + + return retVal + + def getNewGagIndexList(self, track, extraSkill): + retList = [] + curSkill = self.experience[track] + nextExpValue = self.getNextExpValue(track, curSkill) + finalGagFlag = 0 + while curSkill + extraSkill >= nextExpValue and curSkill < nextExpValue and not finalGagFlag: + retList.append(Levels[track].index(nextExpValue)) + newNextExpValue = self.getNextExpValue(track, nextExpValue) + if newNextExpValue == nextExpValue: + finalGagFlag = 1 + else: + nextExpValue = newNextExpValue + + return retList diff --git a/toontown/toon/GloveNPCGlobals.py b/toontown/toon/GloveNPCGlobals.py new file mode 100644 index 00000000..19672e98 --- /dev/null +++ b/toontown/toon/GloveNPCGlobals.py @@ -0,0 +1,21 @@ +from toontown.toonbase import TTLocalizer + +TIMER_SECONDS = 60 + +# Glove Shop GUI +TIMER_END = 0 +USER_CANCEL = 1 +CHANGE = 2 + +# Glove Results +INVALID_COLOR = 0 +SAME_COLOR = 1 +NOT_ENOUGH_MONEY = 2 +CHANGE_SUCCESSFUL = 3 + +ChangeMessages = { + INVALID_COLOR: TTLocalizer.GloveInvalidColorMessage, + SAME_COLOR: TTLocalizer.GloveSameColorMessage, + NOT_ENOUGH_MONEY: TTLocalizer.GloveNoMoneyMessage, + CHANGE_SUCCESSFUL: TTLocalizer.GloveSuccessMessage +} diff --git a/toontown/toon/GloveShopGui.py b/toontown/toon/GloveShopGui.py new file mode 100644 index 00000000..b786f73f --- /dev/null +++ b/toontown/toon/GloveShopGui.py @@ -0,0 +1,144 @@ +from direct.gui.DirectGui import DirectButton, DirectLabel, DGG +from direct.task.Task import Task +from toontown.toon import ToonDNA +from toontown.toonbase import ToontownGlobals, TTLocalizer, ToontownTimer +import GloveNPCGlobals, time + +class GloveShopGui: + + def __init__(self): + self.index = 0 + self.id = time.time() + self.lastGlove = base.localAvatar.style.gloveColor + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(aspect2d) + self.timer.posInTopRightCorner() + self.timer.countdown(GloveNPCGlobals.TIMER_SECONDS, self.__exit, [GloveNPCGlobals.TIMER_END]) + self.setupButtons() + self.bindButtons() + self.__updateIndex(0) + + def setupButtons(self): + gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + arrowImage = (gui.find('**/tt_t_gui_mat_shuffleArrowUp'), gui.find('**/tt_t_gui_mat_shuffleArrowDown')) + buttonImage = (gui.find('**/tt_t_gui_mat_shuffleUp'), gui.find('**/tt_t_gui_mat_shuffleDown')) + + self.title = DirectLabel(aspect2d, relief=None, text=TTLocalizer.GloveGuiTitle, + text_fg=(0, 1, 0, 1), text_scale=0.15, text_font=ToontownGlobals.getSignFont(), + pos=(0, 0, -0.30), text_shadow=(1, 1, 1, 1)) + + self.notice = DirectLabel(aspect2d, relief=None, text='', text_fg=(1, 0, 0, 1), text_scale=0.11, + text_font=ToontownGlobals.getSignFont(), pos=(0, 0, -0.45), text_shadow=(1, 1, 1, 1)) + + self.color = DirectLabel(aspect2d, relief=None, text='', text_scale=0.11, text_font=ToontownGlobals.getSignFont(), + pos=(0, 0, -0.70), text_shadow=(1, 1, 1, 1)) + + self.buyButton = DirectButton(aspect2d, relief=None, image=buttonImage, text=TTLocalizer.GloveGuiBuy, + text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.11, text_pos=(0, -0.02), + pos=(-0.60, 0, -0.90), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), command=self.__exit, extraArgs=[GloveNPCGlobals.CHANGE]) + + self.cancelButton = DirectButton(aspect2d, relief=None, image=buttonImage, text=TTLocalizer.lCancel, + text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.11, text_pos=(0, -0.02), + pos=(0.60, 0, -0.90), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), command=self.__exit, extraArgs=[GloveNPCGlobals.USER_CANCEL]) + + self.downArrow = DirectButton(aspect2d, relief=None, image=arrowImage, pos=(-0.60, 0, -0.66)) + self.upArrow = DirectButton(aspect2d, relief=None, image=arrowImage, pos=(0.60, 0, -0.66), scale=-1) + + gui.removeNode() + + def bindButtons(self): + self.downArrow.bind(DGG.B1PRESS, self.__taskUpdate, extraArgs=[-1]) + self.downArrow.bind(DGG.B1RELEASE, self.__taskDone) + self.upArrow.bind(DGG.B1PRESS, self.__taskUpdate, extraArgs=[1]) + self.upArrow.bind(DGG.B1RELEASE, self.__taskDone) + + def destroy(self): + if self.timer: + self.timer.destroy() + + if not hasattr(self, 'title'): + return + + # TODO: DirectDialog-ify + self.title.destroy() + self.notice.destroy() + self.color.destroy() + self.buyButton.destroy() + self.cancelButton.destroy() + self.downArrow.destroy() + self.upArrow.destroy() + del self.title + del self.notice + del self.color + del self.buyButton + del self.cancelButton + del self.downArrow + del self.upArrow + + taskMgr.remove('runGloveCounter-%s' % self.id) + + def setClientGlove(self, color): + dna = base.localAvatar.style + + dna.gloveColor = color + base.localAvatar.setDNA(dna) + + def __exit(self, state): + self.destroy() + self.setClientGlove(self.lastGlove) + messenger.send('gloveShopDone', [state, self.index if state == GloveNPCGlobals.CHANGE else 0]) + + def __updateIndex(self, offset): + self.index += offset + hitLimit = 0 + color = ToonDNA.allColorsList[self.index] + + if self.index <= 0: + self.downArrow['state'] = DGG.DISABLED + hitLimit = 1 + else: + self.downArrow['state'] = DGG.NORMAL + + if (self.index + 1) >= len(TTLocalizer.NumToColor): + self.upArrow['state'] = DGG.DISABLED + hitLimit = 1 + else: + self.upArrow['state'] = DGG.NORMAL + + if self.lastGlove == color: + self.buyButton['state'] = DGG.DISABLED + self.notice['text'] = TTLocalizer.GloveGuiSameColor + else: + self.buyButton['state'] = DGG.NORMAL + self.notice['text'] = TTLocalizer.GloveGuiNotice % ToontownGlobals.GloveCost + + self.color['text'] = TTLocalizer.NumToColor[self.index] + self.color['text_fg'] = color + self.setClientGlove(color) + return hitLimit + + def __runTask(self, task): + if task.time - task.prevTime < task.delayTime: + return Task.cont + else: + task.delayTime = max(0.05, task.delayTime * 0.75) + task.prevTime = task.time + hitLimit = self.__updateIndex(task.delta) + + return Task.done if hitLimit else Task.cont + + def __taskDone(self, event): + messenger.send('wakeup') + taskMgr.remove('runGloveCounter-%s' % self.id) + + def __taskUpdate(self, delta, event): + messenger.send('wakeup') + + task = Task(self.__runTask) + task.delayTime = 0.4 + task.prevTime = 0.0 + task.delta = delta + hitLimit = self.__updateIndex(delta) + + if not hitLimit: + taskMgr.add(task, 'runGloveCounter-%s' % self.id) diff --git a/toontown/toon/GroupInvitee.py b/toontown/toon/GroupInvitee.py new file mode 100755 index 00000000..68eb64fb --- /dev/null +++ b/toontown/toon/GroupInvitee.py @@ -0,0 +1,63 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toontowngui import TTDialog +from otp.otpbase import OTPLocalizer +from toontown.toontowngui import ToonHeadDialog +from direct.gui.DirectGui import DGG +from otp.otpbase import OTPGlobals +from toontown.toonbase import TTLocalizer + +class GroupInvitee(ToonHeadDialog.ToonHeadDialog): + notify = DirectNotifyGlobal.directNotify.newCategory('GroupInvitee') + + def __init__(self): + pass + + def make(self, party, toon, leaderId, merger, **kw): + self.leaderId = leaderId + self.avName = toon.getName() + self.av = toon + self.avId = toon.doId + self.avDNA = toon.getStyle() + self.party = party + if merger: + text = TTLocalizer.BoardingInviteeMergeMessage % self.avName + else: + text = TTLocalizer.BoardingInviteeMessage % self.avName + style = TTDialog.TwoChoice + buttonTextList = [OTPLocalizer.FriendInviteeOK, OTPLocalizer.FriendInviteeNo] + command = self.__handleButton + optiondefs = (('dialogName', 'GroupInvitee', None), + ('text', text, None), + ('style', style, None), + ('buttonTextList', buttonTextList, None), + ('command', command, None), + ('image_color', (1.0, 0.89, 0.77, 1.0), None), + ('geom_scale', 0.2, None), + ('geom_pos', (-0.1, 0, -0.025), None), + ('pad', (0.075, 0.075), None), + ('topPad', 0, None), + ('midPad', 0, None), + ('scale', 0.75, None)) + self.defineoptions(kw, optiondefs) + ToonHeadDialog.ToonHeadDialog.__init__(self, self.avDNA) + self.initialiseoptions(GroupInvitee) + self.show() + return + + def cleanup(self): + ToonHeadDialog.ToonHeadDialog.cleanup(self) + + def forceCleanup(self): + self.party.requestRejectInvite(self.leaderId, self.avId) + self.cleanup() + + def __handleButton(self, value): + place = base.cr.playGame.getPlace() + if value == DGG.DIALOG_OK and place and not place.getState() == 'elevator': + self.party.requestAcceptInvite(self.leaderId, self.avId) + else: + self.party.requestRejectInvite(self.leaderId, self.avId) + self.cleanup() diff --git a/toontown/toon/GroupPanel.py b/toontown/toon/GroupPanel.py new file mode 100755 index 00000000..d8636f55 --- /dev/null +++ b/toontown/toon/GroupPanel.py @@ -0,0 +1,393 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.gui.DirectGui import * +from panda3d.core import * +from direct.showbase import DirectObject +from toontown.toon import ToonAvatarPanel +from toontown.toontowngui import TTDialog + +class GroupPanel(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('GroupPanel') + + def __init__(self, boardingParty): + self.boardingParty = boardingParty + self.leaderId = self.boardingParty.getGroupLeader(localAvatar.doId) + self.elevatorIdList = self.boardingParty.getElevatorIdList() + self.frame = None + self.confirmQuitDialog = None + self.goButton = None + self.destScrollList = None + self.destFrame = None + self.goingToLabel = None + self.destIndexSelected = 0 + self.__load() + self.ignore('stickerBookEntered') + self.accept('stickerBookEntered', self.__forceHide) + self.ignore('stickerBookExited') + self.accept('stickerBookExited', self.__forceShow) + return + + def cleanup(self): + base.setCellsAvailable(base.leftCells, 1) + self.quitButton.destroy() + self.hideButton.destroy() + self.showButton.destroy() + self.scrollList.destroy() + if self.goButton: + self.goButton.destroy() + self.goButton = None + if self.destScrollList: + self.destScrollList.destroy() + self.destScrollList = None + if self.destFrame: + self.destFrame.destroy() + self.destFrame = None + if self.goingToLabel: + self.goingToLabel.destroy() + self.goingToLabel = None + if self.frame: + self.frame.destroy() + self.frame = None + self.leaveButton = None + self.boardingParty = None + self.ignoreAll() + return + + def __load(self): + self.guiBg = loader.loadModel('phase_9/models/gui/tt_m_gui_brd_groupListBg') + self.__defineConstants() + if self.boardingParty.maxSize == 4: + bgImage = self.guiBg.find('**/tt_t_gui_brd_memberListTop_half') + bgImageZPos = 0.14 + frameZPos = -0.121442 + quitButtonZPos = -0.019958 + else: + bgImage = self.guiBg.find('**/tt_t_gui_brd_memberListTop') + bgImageZPos = 0 + frameZPos = 0.0278943 + quitButtonZPos = -0.30366 + guiButtons = loader.loadModel('phase_9/models/gui/tt_m_gui_brd_status') + self.frame = DirectFrame(parent=base.a2dLeftCenter, relief=None, image=bgImage, image_scale=(0.5, 1, 0.5), image_pos=(0, 0, bgImageZPos), textMayChange=1, pos=(0.32, 0, 0)) + self.frameBounds = self.frame.getBounds() + leaveButtonGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_brd_leaveBtn') + leaveImageList = (leaveButtonGui.find('**/tt_t_gui_brd_leaveUp'), + leaveButtonGui.find('**/tt_t_gui_brd_leaveDown'), + leaveButtonGui.find('**/tt_t_gui_brd_leaveHover'), + leaveButtonGui.find('**/tt_t_gui_brd_leaveUp')) + self.leaderButtonImage = guiButtons.find('**/tt_t_gui_brd_statusLeader') + self.availableButtonImage = guiButtons.find('**/tt_t_gui_brd_statusOn') + self.battleButtonImage = guiButtons.find('**/tt_t_gui_brd_statusBattle') + if localAvatar.doId == self.leaderId: + quitText = TTLocalizer.QuitBoardingPartyLeader + else: + quitText = TTLocalizer.QuitBoardingPartyNonLeader + self.disabledOrangeColor = Vec4(1, 0.5, 0.25, 0.9) + self.quitButton = DirectButton(parent=self.frame, relief=None, image=leaveImageList, image_scale=0.065, command=self.__handleLeaveButton, text=('', + quitText, + quitText, + ''), text_scale=0.06, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0.045, 0.0), text_align=TextNode.ALeft, pos=(0.223, 0, quitButtonZPos), image3_color=self.disabledOrangeColor) + arrowGui = loader.loadModel('phase_9/models/gui/tt_m_gui_brd_arrow') + hideImageList = (arrowGui.find('**/tt_t_gui_brd_arrow_up'), arrowGui.find('**/tt_t_gui_brd_arrow_down'), arrowGui.find('**/tt_t_gui_brd_arrow_hover')) + showImageList = (arrowGui.find('**/tt_t_gui_brd_arrow_up'), arrowGui.find('**/tt_t_gui_brd_arrow_down'), arrowGui.find('**/tt_t_gui_brd_arrow_hover')) + self.hideButton = DirectButton(parent=base.a2dLeftCenter, relief=None, text_pos=(0, 0.15), text_scale=0.06, text_align=TextNode.ALeft, text_fg=Vec4(0, 0, 0, 1), text_shadow=Vec4(1, 1, 1, 1), image=hideImageList, image_scale=(-0.35, 1, 0.5), pos=(0.04, 0, 0.03), scale=1.05, command=self.hide) + self.showButton = DirectButton(parent=base.a2dLeftCenter, relief=None, text=('', TTLocalizer.BoardingGroupShow, TTLocalizer.BoardingGroupShow), text_pos=(0.03, 0), text_scale=0.06, text_align=TextNode.ALeft, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), image=showImageList, image_scale=(0.35, 1, 0.5), pos=(0.04, 0, 0.03), scale=1.05, command=self.show) + self.showButton.hide() + self.frame.show() + self.__makeAvatarNameScrolledList() + if localAvatar.doId == self.leaderId: + self.__makeDestinationScrolledList() + else: + self.__makeDestinationFrame() + self.__makeGoingToLabel() + self.accept('updateGroupStatus', self.__checkGroupStatus) + self.accept('ToonBattleIdUpdate', self.__possibleGroupUpdate) + base.setCellsAvailable([base.leftCells[1], base.leftCells[2]], 0) + if self.boardingParty.isGroupLeader(localAvatar.doId): + base.setCellsAvailable([base.leftCells[0]], 0) + self.__addTestNames(self.boardingParty.maxSize) + self.guiBg.removeNode() + guiButtons.removeNode() + leaveButtonGui.removeNode() + arrowGui.removeNode() + return + + def __defineConstants(self): + self.forcedHidden = False + self.textFgcolor = Vec4(0.0, 0.6, 0.2, 1.0) + self.textBgRolloverColor = Vec4(1, 1, 0, 1) + self.textBgDownColor = Vec4(0.5, 0.9, 1, 1) + self.textBgDisabledColor = Vec4(0.4, 0.8, 0.4, 1) + + def __handleLeaveButton(self): + messenger.send('wakeup') + if not base.cr.playGame.getPlace().getState() == 'elevator': + self.confirmQuitDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.QuitBoardingPartyConfirm, command=self.__confirmQuitCallback) + self.confirmQuitDialog.show() + + def __confirmQuitCallback(self, value): + if self.confirmQuitDialog: + self.confirmQuitDialog.destroy() + self.confirmQuitDialog = None + if value > 0: + if self.boardingParty: + self.boardingParty.requestLeave() + return + + def __handleGoButton(self): + offset = self.destScrollList.getSelectedIndex() + elevatorId = self.elevatorIdList[offset] + self.boardingParty.requestGoToFirstTime(elevatorId) + + def __handleCancelGoButton(self): + self.boardingParty.cancelGoToElvatorDest() + + def __checkGroupStatus(self): + if not self.boardingParty: + return + self.notify.debug('__checkGroupStatus %s' % self.boardingParty.getGroupMemberList(localAvatar.doId)) + myMemberList = self.boardingParty.getGroupMemberList(localAvatar.doId) + self.scrollList.removeAndDestroyAllItems(refresh=0) + if myMemberList: + for avId in myMemberList: + avatarButton = self.__getAvatarButton(avId) + if avatarButton: + self.scrollList.addItem(avatarButton, refresh=0) + + self.scrollList.refresh() + + def __possibleGroupUpdate(self, avId): + self.notify.debug('GroupPanel __possibleGroupUpdate') + if not self.boardingParty: + return + myMemberList = self.boardingParty.getGroupMemberList(localAvatar.doId) + if avId in myMemberList: + self.__checkGroupStatus() + + def __makeAvatarNameScrolledList(self): + friendsListGui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.scrollList = DirectScrolledList(parent=self.frame, relief=None, incButton_image=(friendsListGui.find('**/FndsLst_ScrollUp'), + friendsListGui.find('**/FndsLst_ScrollDN'), + friendsListGui.find('**/FndsLst_ScrollUp_Rllvr'), + friendsListGui.find('**/FndsLst_ScrollUp')), incButton_pos=(0.0, 0.0, -0.35), incButton_image1_color=Vec4(1.0, 0.9, 0.4, 0), incButton_image3_color=Vec4(1.0, 1.0, 0.6, 0), incButton_scale=(1.0, 1.0, -1.0), incButton_relief=None, decButton_image=(friendsListGui.find('**/FndsLst_ScrollUp'), + friendsListGui.find('**/FndsLst_ScrollDN'), + friendsListGui.find('**/FndsLst_ScrollUp_Rllvr'), + friendsListGui.find('**/FndsLst_ScrollUp')), decButton_pos=(0.0, 0.0, 0.1), decButton_image1_color=Vec4(1.0, 1.0, 0.6, 0), decButton_image3_color=Vec4(1.0, 1.0, 0.6, 0), decButton_relief=None, itemFrame_pos=(-0.195, 0.0, 0.185), itemFrame_borderWidth=(0.1, 0.1), numItemsVisible=8, itemFrame_scale=1.0, forceHeight=0.07, items=[], pos=(0, 0, 0.075)) + clipper = PlaneNode('clipper') + clipper.setPlane(Plane(Vec3(-1, 0, 0), Point3(0.235, 0, 0))) + clipNP = self.scrollList.attachNewNode(clipper) + self.scrollList.setClipPlane(clipNP) + friendsListGui.removeNode() + return + + def __makeDestinationScrolledList(self): + arrowGui = loader.loadModel('phase_9/models/gui/tt_m_gui_brd_gotoArrow') + incrementImageList = (arrowGui.find('**/tt_t_gui_brd_arrowL_gotoUp'), + arrowGui.find('**/tt_t_gui_brd_arrowL_gotoDown'), + arrowGui.find('**/tt_t_gui_brd_arrowL_gotoHover'), + arrowGui.find('**/tt_t_gui_brd_arrowL_gotoUp')) + if self.boardingParty.maxSize == 4: + zPos = -0.177083 + else: + zPos = -0.463843 + bottomImage = self.guiBg.find('**/tt_t_gui_brd_memberListBtm_leader') + self.destScrollList = DirectScrolledList( + parent=self.frame, + relief=None, + image=bottomImage, + image_scale=(0.5, 1, 0.5), + incButton_image=incrementImageList, + incButton_pos=(0.217302, 0, 0.07), + incButton_image3_color=Vec4(1.0, 1.0, 0.6, 0.5), + incButton_scale=(-0.5, 1, 0.5), + incButton_relief=None, + incButtonCallback=self.__informDestChange, + decButton_image=incrementImageList, + decButton_pos=(-0.217302, 0, 0.07), + decButton_scale=(0.5, 1, 0.5), + decButton_image3_color=Vec4(1.0, 1.0, 0.6, 0.5), + decButton_relief=None, + decButtonCallback=self.__informDestChange, + itemFrame_pos=(0, 0, 0.06), + itemFrame_borderWidth=(0.1, 0.1), + numItemsVisible=1, + itemFrame_scale=TTLocalizer.GPdestScrollList, + forceHeight=0.07, + items=[], + pos=(0, 0, zPos), + scrollSpeed=0.1) + arrowGui.removeNode() + self.__addDestNames() + self.__makeGoButton() + return + + def __addDestNames(self): + for i in xrange(len(self.elevatorIdList)): + destName = self.__getDestName(i) + self.destScrollList.addItem(destName, refresh=0) + + self.destScrollList.refresh() + + def __getDestName(self, offset): + elevatorId = self.elevatorIdList[offset] + elevator = base.cr.doId2do.get(elevatorId) + if elevator: + destName = elevator.getDestName() + return destName + + def __makeDestinationFrame(self): + destName = self.__getDestName(self.destIndexSelected) + if self.boardingParty.maxSize == 4: + zPos = -0.12 + else: + zPos = -0.404267 + bottomImage = self.guiBg.find('**/tt_t_gui_brd_memberListBtm_nonLeader') + self.destFrame = DirectFrame(parent=self.frame, relief=None, image=bottomImage, image_scale=(0.5, 1, 0.5), text=destName, text_align=TextNode.ACenter, text_scale=TTLocalizer.GPdestFrame, pos=(0, 0, zPos)) + return + + def __makeGoButton(self): + goGui = loader.loadModel('phase_9/models/gui/tt_m_gui_brd_gotoBtn') + self.goImageList = (goGui.find('**/tt_t_gui_brd_gotoUp'), + goGui.find('**/tt_t_gui_brd_gotoDown'), + goGui.find('**/tt_t_gui_brd_gotoHover'), + goGui.find('**/tt_t_gui_brd_gotoUp')) + self.cancelGoImageList = (goGui.find('**/tt_t_gui_brd_cancelGotoUp'), + goGui.find('**/tt_t_gui_brd_cancelGotoDown'), + goGui.find('**/tt_t_gui_brd_cancelGotoHover'), + goGui.find('**/tt_t_gui_brd_cancelGotoUp')) + if self.boardingParty.maxSize == 4: + zPos = -0.028 + zPos = -0.0360483 + else: + zPos = -0.0353787 + self.goButton = DirectButton(parent=self.destScrollList, relief=None, image=self.goImageList, image_scale=(0.48, 1, 0.48), command=self.__handleGoButton, text=('', + TTLocalizer.BoardingGo, + TTLocalizer.BoardingGo, + ''), text_scale=TTLocalizer.GPgoButton, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -0.12), pos=(-0.003, 0, zPos)) + goGui.removeNode() + return + + def __getAvatarButton(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return None + toonName = toon.getName() + inBattle = 0 + buttonImage = self.availableButtonImage + if toon.battleId: + inBattle = 1 + buttonImage = self.battleButtonImage + if avId == localAvatar.doId: + self.__forceHide() + else: + if avId == self.leaderId: + buttonImage = self.leaderButtonImage + if avId == localAvatar.doId: + self.__forceShow() + return DirectButton(parent=self.frame, relief=None, image=buttonImage, image_scale=(0.06, 1.0, 0.06), text=toonName, text_align=TextNode.ALeft, text_wordwrap=16, text_scale=0.04, text_pos=(0.05, -0.015), text_fg=self.textFgcolor, text1_bg=self.textBgDownColor, text2_bg=self.textBgRolloverColor, text3_fg=self.textBgDisabledColor, pos=(0, 0, 0.2), command=self.__openToonAvatarPanel, extraArgs=[toon, avId]) + + def __openToonAvatarPanel(self, avatar, avId): + if avId != localAvatar.doId and avatar: + messenger.send('clickedNametag', [avatar]) + + def __addTestNames(self, num): + for i in xrange(num): + avatarButton = self.__getAvatarButton(localAvatar.doId) + self.scrollList.addItem(avatarButton, refresh=0) + + self.scrollList.refresh() + + def __isForcedHidden(self): + if self.forcedHidden and self.frame.isHidden(): + return True + else: + return False + + def hide(self): + self.frame.hide() + self.hideButton.hide() + self.showButton.show() + + def show(self): + self.frame.show() + self.forcedHidden = False + self.showButton.hide() + self.hideButton.show() + + def __forceHide(self): + if not self.frame.isHidden(): + self.forcedHidden = True + self.hide() + + def __forceShow(self): + if self.__isForcedHidden(): + self.show() + + def __informDestChange(self): + self.boardingParty.informDestChange(self.destScrollList.getSelectedIndex()) + + def changeDestination(self, offset): + if localAvatar.doId != self.leaderId: + self.destIndexSelected = offset + if self.destFrame: + self.destFrame['text'] = self.__getDestName(self.destIndexSelected) + + def scrollToDestination(self, offset): + if localAvatar.doId == self.leaderId: + if self.destScrollList: + self.destIndexSelected = offset + self.destScrollList.scrollTo(offset) + + def __makeGoingToLabel(self): + if self.boardingParty.maxSize == 4: + zPos = -0.0466546 + else: + zPos = -0.331731 + self.goingToLabel = DirectLabel(parent=self.frame, relief=None, text=TTLocalizer.BoardingGoingTo, text_scale=0.045, text_align=TextNode.ALeft, text_fg=Vec4(0, 0, 0, 1), pos=(-0.1966, 0, zPos)) + return + + def disableQuitButton(self): + if self.quitButton and not self.quitButton.isEmpty(): + self.quitButton['state'] = DGG.DISABLED + + def enableQuitButton(self): + if self.quitButton and not self.quitButton.isEmpty(): + self.quitButton['state'] = DGG.NORMAL + + def disableGoButton(self): + if self.goButton and not self.goButton.isEmpty(): + self.goButton['state'] = DGG.DISABLED + self.goButton['image_color'] = Vec4(1, 1, 1, 0.4) + + def enableGoButton(self): + if self.goButton and not self.goButton.isEmpty(): + self.goButton['state'] = DGG.NORMAL + self.goButton['image_color'] = Vec4(1, 1, 1, 1) + + def disableDestinationScrolledList(self): + if self.destScrollList and not self.destScrollList.isEmpty(): + self.destScrollList.incButton['state'] = DGG.DISABLED + self.destScrollList.decButton['state'] = DGG.DISABLED + + def enableDestinationScrolledList(self): + if self.destScrollList and not self.destScrollList.isEmpty(): + self.destScrollList.incButton['state'] = DGG.NORMAL + self.destScrollList.decButton['state'] = DGG.NORMAL + + def changeGoToCancel(self): + if self.goButton and not self.goButton.isEmpty(): + self.goButton['image'] = self.cancelGoImageList + self.goButton['text'] = (TTLocalizer.BoardingCancelGo, + TTLocalizer.BoardingCancelGo, + TTLocalizer.BoardingCancelGo, + '') + self.goButton['command'] = self.__handleCancelGoButton + + def changeCancelToGo(self): + if self.goButton and not self.goButton.isEmpty(): + self.goButton['image'] = self.goImageList + self.goButton['text'] = ('', + TTLocalizer.BoardingGo, + TTLocalizer.BoardingGo, + '') + self.goButton['command'] = self.__handleGoButton diff --git a/toontown/toon/HealthForceAcknowledge.py b/toontown/toon/HealthForceAcknowledge.py new file mode 100755 index 00000000..dd9c8dc5 --- /dev/null +++ b/toontown/toon/HealthForceAcknowledge.py @@ -0,0 +1,32 @@ +from panda3d.core import * +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer + +class HealthForceAcknowledge: + + def __init__(self, doneEvent): + self.doneEvent = doneEvent + self.dialog = None + return + + def enter(self, hpLevel): + doneStatus = {} + toonHp = base.localAvatar.getHp() + if toonHp >= hpLevel: + doneStatus['mode'] = 'complete' + messenger.send(self.doneEvent, [doneStatus]) + else: + base.localAvatar.b_setAnimState('neutral', 1) + doneStatus['mode'] = 'incomplete' + self.doneStatus = doneStatus + msg = TTLocalizer.HealthForceAcknowledgeMessage + self.dialog = TTDialog.TTDialog(text=msg, command=self.handleOk, style=TTDialog.Acknowledge) + + def exit(self): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + return + + def handleOk(self, value): + messenger.send(self.doneEvent, [self.doneStatus]) diff --git a/toontown/toon/InventoryBase.py b/toontown/toon/InventoryBase.py new file mode 100755 index 00000000..e225ca4e --- /dev/null +++ b/toontown/toon/InventoryBase.py @@ -0,0 +1,300 @@ +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase.ToontownBattleGlobals import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.PyDatagramIterator import PyDatagramIterator + +class InventoryBase(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('InventoryBase') + + def __init__(self, toon, invStr = None): + self.toon = toon + if invStr == None: + self.inventory = [] + for track in xrange(0, len(Tracks)): + level = [] + for thisLevel in xrange(0, len(Levels[track])): + level.append(0) + + self.inventory.append(level) + + else: + self.inventory = self.makeFromNetString(invStr) + self.calcTotalProps() + return + + def unload(self): + del self.toon + + def __str__(self): + retStr = 'totalProps: %d\n' % self.totalProps + for track in xrange(0, len(Tracks)): + retStr += Tracks[track] + ' = ' + str(self.inventory[track]) + '\n' + + return retStr + + def updateInvString(self, invString): + inventory = self.makeFromNetString(invString) + self.updateInventory(inventory) + return None + + def updateInventory(self, inv): + self.inventory = inv + self.calcTotalProps() + + def makeNetString(self): + dataList = self.inventory + datagram = PyDatagram() + for track in xrange(0, len(Tracks)): + for level in xrange(0, len(Levels[track])): + datagram.addUint8(dataList[track][level]) + + dgi = PyDatagramIterator(datagram) + return dgi.getRemainingBytes() + + def makeFromNetString(self, netString): + dataList = [] + dg = PyDatagram(netString) + dgi = PyDatagramIterator(dg) + for track in xrange(0, len(Tracks)): + subList = [] + for level in xrange(0, len(Levels[track])): + if dgi.getRemainingSize() > 0: + value = dgi.getUint8() + else: + value = 0 + subList.append(value) + + dataList.append(subList) + + return dataList + + def makeFromNetStringForceSize(self, netString, numTracks, numLevels): + dataList = [] + dg = PyDatagram(netString) + dgi = PyDatagramIterator(dg) + for track in xrange(0, numTracks): + subList = [] + for level in xrange(0, numLevels): + if dgi.getRemainingSize() > 0: + value = dgi.getUint8() + else: + value = 0 + subList.append(value) + + dataList.append(subList) + + return dataList + + def addItem(self, track, level): + return self.addItems(track, level, 1) + + def addItems(self, track, level, amount): + if isinstance(track, str): + track = Tracks.index(track) + max = self.getMax(track, level) + if (not hasattr(self.toon, 'experience')) or (not hasattr(self.toon.experience, 'getExpLevel')): + return 0 + if not (self.toon.experience.getExpLevel(track) >= level and self.toon.hasTrackAccess(track)): + return 0 + if self.numItem(track, level) > max - amount: + return -1 + if not (self.totalProps + amount <= self.toon.getMaxCarry() or level > LAST_REGULAR_GAG_LEVEL): + return -2 + self.inventory[track][level] += amount + self.totalProps += amount + return self.inventory[track][level] + + def addItemWithList(self, track, levelList): + for level in levelList: + self.addItem(track, level) + + def numItem(self, track, level): + if isinstance(track, str): + track = Tracks.index(track) + if track > len(Tracks) - 1 or level > len(Levels) - 1: + self.notify.warning("%s is using a gag that doesn't exist %s %s!" % (self.toon.doId, track, level)) + return -1 + return self.inventory[track][level] + + def useItem(self, track, level): + if type(track) == type(''): + track = Tracks.index(track) + if self.numItem(track, level) > 0: + self.inventory[track][level] -= 1 + self.calcTotalProps() + return 1 + elif self.numItem(track, level) == -1: + return -1 + + def setItem(self, track, level, amount): + if type(track) == type(''): + track = Tracks.index(track) + max = self.getMax(track, level) + curAmount = self.numItem(track, level) + if self.toon.experience.getExpLevel(track) >= level: + if amount <= max: + if self.totalProps - curAmount + amount <= self.toon.getMaxCarry(): + self.inventory[track][level] = amount + self.totalProps = self.totalProps - curAmount + amount + return self.inventory[track][level] + else: + return -2 + else: + return -1 + else: + return 0 + + def getMax(self, track, level): + if type(track) == type(''): + track = Tracks.index(track) + if self.toon.experience: + return CarryLimits[self.toon.experience.getExpLevel(track)][level] + else: + return 0 + + def getTrackAndLevel(self, propName): + for track in xrange(0, len(Tracks)): + if AvProps[track].count(propName): + return (tracks, AvProps[track].index(propName)) + + return (-1, -1) + + def calcTotalProps(self): + self.totalProps = 0 + for track in xrange(0, len(Tracks)): + for level in xrange(0, len(Levels[track])): + if level <= LAST_REGULAR_GAG_LEVEL: + self.totalProps += self.numItem(track, level) + + return None + + def countPropsInList(self, invList): + totalProps = 0 + for track in xrange(len(Tracks)): + for level in xrange(len(Levels[track])): + if level <= LAST_REGULAR_GAG_LEVEL: + totalProps += invList[track][level] + + return totalProps + + def setToMin(self, newInventory): + for track in xrange(len(Tracks)): + for level in xrange(len(Levels[track])): + self.inventory[track][level] = min(self.inventory[track][level], newInventory[track][level]) + + self.calcTotalProps() + return None + + def validateItemsBasedOnExp(self, newInventory, allowUber = 0): + if type(newInventory) == type('String'): + tempInv = self.makeFromNetString(newInventory) + else: + tempInv = newInventory + for track in xrange(len(Tracks)): + for level in xrange(len(Levels[track])): + if tempInv[track][level] > self.getMax(track, level): + return 0 + if tempInv[track][level] > 0 and not self.toon.hasTrackAccess(track): + commentStr = "Player %s trying to purchase gag they don't have track access to. track: %s level: %s" % (self.toon.doId, track, level) + dislId = self.toon.DISLid + if simbase.config.GetBool('want-ban-gagtrack', False): + #simbase.air.banManager.ban(self.toon.doId, dislId, commentStr) + pass + return 0 + if level > LAST_REGULAR_GAG_LEVEL and tempInv[track][level] > self.inventory[track][level] or allowUber: + return 0 + + return 1 + + def getMinCostOfPurchase(self, newInventory): + return self.countPropsInList(newInventory) - self.totalProps + + def validatePurchase(self, newInventory, currentMoney, newMoney): + if newMoney > currentMoney or newMoney < 0: + self.notify.warning('Somebody lied about their money! Rejecting purchase.') + return 0 + newItemTotal = self.countPropsInList(newInventory) + oldItemTotal = self.totalProps + if newItemTotal > oldItemTotal + currentMoney: + self.notify.warning('Somebody overspent! Rejecting purchase.') + return 0 + if newItemTotal - oldItemTotal > currentMoney - newMoney: + self.notify.warning('Too many items based on money spent! Rejecting purchase.') + return 0 + if newItemTotal > self.toon.getMaxCarry(): + self.notify.warning('Cannot carry %s items! Rejecting purchase.' % newItemTotal) + return 0 + if not self.validateItemsBasedOnExp(newInventory): + self.notify.warning('Somebody is trying to buy forbidden items! ' + 'Rejecting purchase.') + return 0 + self.updateInventory(newInventory) + return 1 + + def maxOutInv(self, filterUberGags = 0): + for track in xrange(len(Tracks)): + if self.toon.hasTrackAccess(track): + for level in xrange(len(Levels[track])): + if level <= LAST_REGULAR_GAG_LEVEL or not filterUberGags: + self.addItem(track, level) + + addedAnything = 1 + while addedAnything: + addedAnything = 0 + result = 0 + for track in xrange(len(Tracks)): + if self.toon.hasTrackAccess(track): + level = len(Levels[track]) - 1 + if level > LAST_REGULAR_GAG_LEVEL and filterUberGags: + level = LAST_REGULAR_GAG_LEVEL + result = self.addItem(track, level) + level -= 1 + while result <= 0 and level >= 0: + result = self.addItem(track, level) + level -= 1 + + if result > 0: + addedAnything = 1 + + self.calcTotalProps() + return None + + def NPCMaxOutInv(self, targetTrack=-1, maxLevelIndex=5): + result = 0 + for level in xrange(maxLevelIndex, -1, -1): + anySpotsAvailable = 1 + while anySpotsAvailable == 1: + anySpotsAvailable = 0 + trackResults = [] + for track in xrange(len(Tracks)): + if targetTrack != -1 and targetTrack != track: + continue + result = self.addItem(track, level) + trackResults.append(result) + if result == -2: + break + + for res in trackResults: + if res > 0: + anySpotsAvailable = 1 + + if result == -2: + break + + self.calcTotalProps() + return None + + def zeroInv(self, killUber = 0): + for track in xrange(len(Tracks)): + for level in xrange(UBER_GAG_LEVEL_INDEX): + self.inventory[track][level] = 0 + + if killUber: + self.inventory[track][UBER_GAG_LEVEL_INDEX] = 0 + if self.inventory[track][UBER_GAG_LEVEL_INDEX] > 1: + self.inventory[track][UBER_GAG_LEVEL_INDEX] = 1 + + self.calcTotalProps() + return None diff --git a/toontown/toon/InventoryNew.py b/toontown/toon/InventoryNew.py new file mode 100755 index 00000000..2cdf5d26 --- /dev/null +++ b/toontown/toon/InventoryNew.py @@ -0,0 +1,1126 @@ +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase.ToontownBattleGlobals import * +import InventoryBase +from toontown.toonbase import TTLocalizer +from toontown.quest import BlinkingArrows +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog +from otp.otpbase import OTPGlobals + +class InventoryNew(InventoryBase.InventoryBase, DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('InventoryNew') + PressableTextColor = Vec4(1, 1, 1, 1) + PressableGeomColor = Vec4(1, 1, 1, 1) + PressableImageColor = Vec4(0, 0.6, 1, 1) + PropBonusPressableImageColor = Vec4(1.0, 0.6, 0.0, 1) + NoncreditPressableImageColor = Vec4(0.3, 0.6, 0.6, 1) + PropBonusNoncreditPressableImageColor = Vec4(0.6, 0.6, 0.3, 1) + DeletePressableImageColor = Vec4(0.7, 0.1, 0.1, 1) + UnpressableTextColor = Vec4(1, 1, 1, 0.3) + UnpressableGeomColor = Vec4(1, 1, 1, 0.3) + UnpressableImageColor = Vec4(0.3, 0.3, 0.3, 0.8) + BookUnpressableTextColor = Vec4(1, 1, 1, 1) + BookUnpressableGeomColor = Vec4(1, 1, 1, 1) + BookUnpressableImage0Color = Vec4(0, 0.6, 1, 1) + BookUnpressableImage2Color = Vec4(0.1, 0.7, 1, 1) + ShadowColor = Vec4(0, 0, 0, 0) + ShadowBuffedColor = Vec4(1, 1, 1, 1) + UnpressableShadowBuffedColor = Vec4(1, 1, 1, 0.3) + TrackYOffset = 0.0 + TrackYSpacing = -0.12 + ButtonXOffset = -0.31 + ButtonXSpacing = 0.18 + + def __init__(self, toon, invStr = None, ShowSuperGags = 1): + InventoryBase.InventoryBase.__init__(self, toon, invStr) + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(InventoryNew) + self.battleCreditLevel = None + self.detailCredit = None + self.__battleCreditMultiplier = base.baseXpMultiplier + self.__invasionCreditMultiplier = base.baseXpMultiplier + self.__respectInvasions = 1 + self.interactivePropTrackBonus = -1 + self.tutorialFlag = 0 + self.gagTutMode = 0 + self.showSuperGags = ShowSuperGags + self.clickSuperGags = 1 + self.propAndOrganicBonusStack = base.config.GetBool('prop-and-organic-bonus-stack', 0) + self.propBonusIval = Parallel() + self.activateMode = 'book' + self.load() + self.hide() + return + + def setBattleCreditMultiplier(self, mult): + self.__battleCreditMultiplier = mult + + def getBattleCreditMultiplier(self): + return self.__battleCreditMultiplier + + def setInteractivePropTrackBonus(self, trackBonus): + self.interactivePropTrackBonus = trackBonus + + def getInteractivePropTrackBonus(self): + return self.interactivePropTrackBonus + + def setInvasionCreditMultiplier(self, mult): + self.__invasionCreditMultiplier = mult + + def getInvasionCreditMultiplier(self): + return self.__invasionCreditMultiplier + + def setRespectInvasions(self, flag): + self.__respectInvasions = flag + + def getRespectInvasions(self): + return self.__respectInvasions + + def show(self): + if self.tutorialFlag: + self.tutText.show() + self.tutText.reparentTo(self.battleFrame, 1) + DirectFrame.show(self) + + def uberGagToggle(self, showSuperGags = 1): + self.showSuperGags = showSuperGags + for itemList in self.invModels: + for itemIndex in xrange(MAX_LEVEL_INDEX + 1): + if itemIndex <= LAST_REGULAR_GAG_LEVEL + 1 or self.showSuperGags: + itemList[itemIndex].show() + else: + itemList[itemIndex].hide() + + for buttonList in self.buttons: + for buttonIndex in xrange(MAX_LEVEL_INDEX + 1): + if buttonIndex <= LAST_REGULAR_GAG_LEVEL or self.showSuperGags: + buttonList[buttonIndex].show() + else: + buttonList[buttonIndex].hide() + + def enableUberGags(self, enableSG = -1): + if enableSG != -1: + self.clickSuperGags = enableSG + for buttonList in self.buttons: + for buttonIndex in xrange(LAST_REGULAR_GAG_LEVEL + 1, MAX_LEVEL_INDEX + 1): + if self.clickSuperGags: + pass + else: + self.makeUnpressable(buttonList[buttonIndex], self.buttons.index(buttonList), buttonIndex) + + def hide(self): + if self.tutorialFlag: + self.tutText.hide() + DirectFrame.hide(self) + + def updateTotalPropsText(self): + textTotal = '%s\n\n' % (TTLocalizer.InventoryTotalGags % (self.totalProps, self.toon.getMaxCarry())) + + if localAvatar.getPinkSlips() > 1: + textTotal += TTLocalizer.InventoryPinkSlips % localAvatar.getPinkSlips() + textTotal += '\n' + elif localAvatar.getPinkSlips() == 1: + textTotal += TTLocalizer.InventoryPinkSlip + textTotal += '\n' + + if localAvatar.getCrateKeys() > 1: + textTotal += TTLocalizer.InventoryCrateKeys % localAvatar.getCrateKeys() + elif localAvatar.getCrateKeys() == 1: + textTotal += TTLocalizer.InventoryCrateKey + + self.totalLabel['text'] = textTotal + + def unload(self): + self.notify.debug('Unloading Inventory for %d' % self.toon.doId) + self.stopAndClearPropBonusIval() + self.propBonusIval.finish() + self.propBonusIval = None + del self.invModels + self.buttonModels.removeNode() + del self.buttonModels + del self.upButton + del self.downButton + del self.rolloverButton + del self.flatButton + del self.invFrame + del self.battleFrame + del self.purchaseFrame + del self.storePurchaseFrame + self.deleteAllButton.destroy() + del self.deleteAllButton + self.deleteEnterButton.destroy() + del self.deleteEnterButton + self.deleteExitButton.destroy() + del self.deleteExitButton + del self.detailFrame + del self.detailNameLabel + del self.detailAmountLabel + del self.detailDataLabel + del self.totalLabel + self.cleanupDialog() + + for row in self.trackRows: + row.destroy() + + del self.trackRows + del self.trackNameLabels + del self.trackBars + for buttonList in self.buttons: + for buttonIndex in xrange(MAX_LEVEL_INDEX + 1): + buttonList[buttonIndex].destroy() + + del self.buttons + InventoryBase.InventoryBase.unload(self) + DirectFrame.destroy(self) + + def cleanupDialog(self): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + + def load(self): + self.notify.debug('Loading Inventory for %d' % self.toon.doId) + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + self.invModels = [] + for track in xrange(len(AvPropsNew)): + itemList = [] + for item in xrange(len(AvPropsNew[track])): + itemList.append(invModel.find('**/' + AvPropsNew[track][item])) + + self.invModels.append(itemList) + + invModel.removeNode() + del invModel + self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + self.rowModel = self.buttonModels.find('**/InventoryRow') + self.upButton = self.buttonModels.find('**/InventoryButtonUp') + self.downButton = self.buttonModels.find('**/InventoryButtonDown') + self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover') + self.flatButton = self.buttonModels.find('**/InventoryButtonFlat') + self.invFrame = DirectFrame(relief=None, parent=self) + self.battleFrame = None + self.purchaseFrame = None + self.storePurchaseFrame = None + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') + trashcanImage = (trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_RLVR')) + self.deleteEnterButton = DirectButton(parent=self.invFrame, image=trashcanImage, text=('', TTLocalizer.InventoryDelete, TTLocalizer.InventoryDelete), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_pos=(0, -0.1), text_font=getInterfaceFont(), textMayChange=0, relief=None, pos=(-1, 0, -0.35), scale=1.0) + self.deleteAllButton = DirectButton(parent=self.invFrame, image=trashcanImage, text=('', TTLocalizer.InventoryDeleteAll, TTLocalizer.InventoryDeleteAll), text_fg=(1, 0, 0, 1), text_shadow=(1, 1, 1, 1), text_scale=0.1, text_pos=(0, -0.1), text_font=getInterfaceFont(), textMayChange=0, relief=None, pos=(-0.3, 0, -0.91), scale=0.75, command=self.__zeroInvConfirm) + self.deleteExitButton = DirectButton(parent=self.invFrame, image=(trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_RLVR')), text=('', TTLocalizer.InventoryDone, TTLocalizer.InventoryDone), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_pos=(0, -0.1), text_font=getInterfaceFont(), textMayChange=0, relief=None, pos=(-1, 0, -0.35), scale=1.0) + trashcanGui.removeNode() + self.deleteHelpText = DirectLabel(parent=self.invFrame, relief=None, pos=(0.272, 0.3, -0.907), text=TTLocalizer.InventoryDeleteHelp, text_fg=(0, 0, 0, 1), text_scale=0.08, textMayChange=0) + self.deleteHelpText.hide() + self.detailFrame = DirectFrame(parent=self.invFrame, relief=None, pos=(1.05, 0, -0.08)) + self.detailNameLabel = DirectLabel(parent=self.detailFrame, text='', text_scale=TTLocalizer.INdetailNameLabel, text_fg=(0.05, 0.14, 0.4, 1), scale=0.045, pos=(0, 0, 0), text_font=getInterfaceFont(), relief=None, image=self.invModels[0][0]) + self.detailAmountLabel = DirectLabel(parent=self.detailFrame, text='', text_fg=(0.05, 0.14, 0.4, 1), scale=0.04, pos=(0.16, 0, -0.175), text_font=getInterfaceFont(), text_align=TextNode.ARight, relief=None) + self.detailDataLabel = DirectLabel(parent=self.detailFrame, text='', text_fg=(0.05, 0.14, 0.4, 1), scale=0.04, pos=(-0.22, 0, -0.24), text_font=getInterfaceFont(), text_align=TextNode.ALeft, relief=None) + self.detailCreditLabel = DirectLabel(parent=self.detailFrame, text=TTLocalizer.InventorySkillCreditNone, text_fg=(0.05, 0.14, 0.4, 1), scale=0.04, pos=(-0.22, 0, -0.365), text_font=getInterfaceFont(), text_align=TextNode.ALeft, relief=None) + self.detailCreditLabel.hide() + self.totalLabel = DirectLabel(text='', parent=self.detailFrame, pos=(0, 0, -0.095), scale=0.05, text_fg=(0.05, 0.14, 0.4, 1), text_font=getInterfaceFont(), relief=None) + self.dialog = None + self.updateTotalPropsText() + self.trackRows = [] + self.trackNameLabels = [] + self.trackBars = [] + self.buttons = [] + for track in xrange(0, len(Tracks)): + trackFrame = DirectFrame(parent=self.invFrame, image=self.rowModel, scale=(1.0, 1.0, 1.1), pos=(0, 0.3, self.TrackYOffset + track * self.TrackYSpacing), image_color=(TrackColors[track][0], + TrackColors[track][1], + TrackColors[track][2], + 1), state=DGG.NORMAL, relief=None) + trackFrame.bind(DGG.WITHIN, self.enterTrackFrame, extraArgs=[track]) + trackFrame.bind(DGG.WITHOUT, self.exitTrackFrame, extraArgs=[track]) + self.trackRows.append(trackFrame) + adjustLeft = -0.065 + self.trackNameLabels.append(DirectLabel(text=TextEncoder.upper(Tracks[track]), parent=self.trackRows[track], pos=(-0.72 + adjustLeft, -0.1, 0.01), scale=TTLocalizer.INtrackNameLabels, relief=None, text_fg=(0.2, 0.2, 0.2, 1), text_font=getInterfaceFont(), text_align=TextNode.ALeft, textMayChange=0)) + self.trackBars.append(DirectWaitBar(parent=self.trackRows[track], pos=(-0.58 + adjustLeft, -0.1, -0.025), relief=DGG.SUNKEN, frameSize=(-0.6, + 0.6, + -0.1, + 0.1), borderWidth=(0.02, 0.02), scale=0.25, frameColor=(TrackColors[track][0] * 0.6, + TrackColors[track][1] * 0.6, + TrackColors[track][2] * 0.6, + 1), barColor=(TrackColors[track][0] * 0.9, + TrackColors[track][1] * 0.9, + TrackColors[track][2] * 0.9, + 1), text='0 / 0', text_scale=0.16, text_fg=(0, 0, 0, 0.8), text_align=TextNode.ACenter, text_pos=(0, -0.05))) + self.buttons.append([]) + for item in xrange(0, len(Levels[track])): + button = DirectButton(parent=self.trackRows[track], image=(self.upButton, + self.downButton, + self.rolloverButton, + self.flatButton), geom=self.invModels[track][item], text='50', text_scale=0.04, text_align=TextNode.ARight, geom_scale=0.7, geom_pos=(-0.01, -0.1, 0), text_fg=Vec4(1, 1, 1, 1), text_pos=(0.07, -0.04), textMayChange=1, relief=None, image_color=(0, 0.6, 1, 1), pos=(self.ButtonXOffset + item * self.ButtonXSpacing + adjustLeft, -0.1, 0), command=self.__handleSelection, extraArgs=[track, item]) + button.bind(DGG.ENTER, self.showDetail, extraArgs=[track, item]) + button.bind(DGG.EXIT, self.hideDetail) + self.buttons[track].append(button) + + def __handleSelection(self, track, level): + if self.activateMode == 'purchaseDelete' or self.activateMode == 'storePurchaseDelete': + if self.numItem(track, level): + self.useItem(track, level) + self.updateGUI(track, level) + messenger.send('inventory-deletion', [track, level]) + self.showDetail(track, level) + elif self.activateMode == 'purchase' or self.activateMode == 'storePurchase': + messenger.send('inventory-selection', [track, level]) + self.showDetail(track, level) + elif self.gagTutMode: + pass + else: + messenger.send('inventory-selection', [track, level]) + + def __handleRun(self): + messenger.send('inventory-run') + + def __handleFire(self): + messenger.send('inventory-fire') + + def __handleSOS(self): + messenger.send('inventory-sos') + + def __handlePass(self): + messenger.send('inventory-pass') + + def __handleBackToPlayground(self): + messenger.send('inventory-back-to-playground') + + def __zeroInvConfirm(self): + self.cleanupDialog() + self.dialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.InventoryDeleteConfirm, command=self.__zeroInvAndUpdate) + self.dialog.show() + + def __zeroInvAndUpdate(self, value): + self.cleanupDialog() + + if value > 0: + self.zeroInv() + self.updateGUI() + + def showDetail(self, track, level, event = None): + self.totalLabel.hide() + self.detailNameLabel.show() + self.detailNameLabel.configure(text=AvPropStrings[track][level], image_image=self.invModels[track][level]) + self.detailNameLabel.configure(image_scale=20, image_pos=(-0.2, 0, -2.2)) + self.detailAmountLabel.show() + self.detailAmountLabel.configure(text=TTLocalizer.InventoryDetailAmount % {'numItems': self.numItem(track, level), + 'maxItems': self.getMax(track, level)}) + self.detailDataLabel.show() + damage = getAvPropDamage(track, level, self.toon.experience.getExp(track)) + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + damageBonusStr = '' + damageBonus = 0 + if self.propAndOrganicBonusStack: + if propBonus: + damageBonus += getDamageBonus(damage) + if organicBonus: + damageBonus += getDamageBonus(damage) + if damageBonus: + damageBonusStr = TTLocalizer.InventoryDamageBonus % damageBonus + else: + if propBonus or organicBonus: + damageBonus += getDamageBonus(damage) + if damageBonus: + damageBonusStr = TTLocalizer.InventoryDamageBonus % damageBonus + accString = AvTrackAccStrings[track] + if (organicBonus or propBonus) and track == LURE_TRACK: + accString = TTLocalizer.BattleGlobalLureAccMedium + self.detailDataLabel.configure(text=TTLocalizer.InventoryDetailData % {'accuracy': accString, + 'damageString': self.getToonupDmgStr(track, level), + 'damage': damage, + 'bonus': damageBonusStr, + 'singleOrGroup': self.getSingleGroupStr(track, level)}) + if self.itemIsCredit(track, level): + mult = self.__battleCreditMultiplier + if self.__respectInvasions: + mult *= self.__invasionCreditMultiplier + self.setDetailCredit(track, (level + 1) * mult) + else: + self.setDetailCredit(track, None) + self.detailCreditLabel.show() + return + + def setDetailCredit(self, track, credit): + if credit != None: + if self.toon.earnedExperience: + maxCredit = ExperienceCap - self.toon.earnedExperience[track] + credit = min(credit, maxCredit) + credit = int(credit * 10 + 0.5) + if credit % 10 == 0: + credit /= 10 + else: + credit /= 10.0 + if self.detailCredit == credit: + return + if credit != None: + self.detailCreditLabel['text'] = TTLocalizer.InventorySkillCredit % credit + if self.detailCredit == None: + self.detailCreditLabel['text_fg'] = (0.05, 0.14, 0.4, 1) + else: + self.detailCreditLabel['text'] = TTLocalizer.InventorySkillCreditNone + self.detailCreditLabel['text_fg'] = (0.5, 0.0, 0.0, 1.0) + self.detailCredit = credit + return + + def hideDetail(self, event = None): + self.totalLabel.show() + self.detailNameLabel.hide() + self.detailAmountLabel.hide() + self.detailDataLabel.hide() + self.detailCreditLabel.hide() + + def noDetail(self): + self.totalLabel.hide() + self.detailNameLabel.hide() + self.detailAmountLabel.hide() + self.detailDataLabel.hide() + self.detailCreditLabel.hide() + + def setActivateMode(self, mode, heal = 1, trap = 1, lure = 1, bldg = 0, creditLevel = None, tutorialFlag = 0, gagTutMode = 0): + self.notify.debug('setActivateMode() mode:%s heal:%s trap:%s lure:%s bldg:%s' % (mode, + heal, + trap, + lure, + bldg)) + self.previousActivateMode = self.activateMode + self.activateMode = mode + self.deactivateButtons() + self.heal = heal + self.trap = trap + self.lure = lure + self.bldg = bldg + self.battleCreditLevel = creditLevel + self.tutorialFlag = tutorialFlag + self.gagTutMode = gagTutMode + self.__activateButtons() + self.enableUberGags() + return None + + def setActivateModeBroke(self): + if self.activateMode == 'storePurchase': + self.setActivateMode('storePurchaseBroke') + elif self.activateMode == 'purchase': + self.setActivateMode('purchaseBroke', gagTutMode=self.gagTutMode) + else: + self.notify.error('Unexpected mode in setActivateModeBroke(): %s' % self.activateMode) + self.enableUberGags() + + def deactivateButtons(self): + self.cleanupDialog() + if self.previousActivateMode == 'purchaseDelete': + self.purchaseDeleteDeactivateButtons() + elif self.previousActivateMode == 'purchase': + self.purchaseDeactivateButtons() + elif self.previousActivateMode == 'purchaseBroke': + self.purchaseBrokeDeactivateButtons() + elif self.previousActivateMode == 'gagTutDisabled': + self.gagTutDisabledDeactivateButtons() + elif self.previousActivateMode == 'battle': + self.battleDeactivateButtons() + elif self.previousActivateMode == 'storePurchaseDelete': + self.storePurchaseDeleteDeactivateButtons() + elif self.previousActivateMode == 'storePurchase': + self.storePurchaseDeactivateButtons() + elif self.previousActivateMode == 'storePurchaseBroke': + self.storePurchaseBrokeDeactivateButtons() + elif self.previousActivateMode == 'plantTree': + self.plantTreeDeactivateButtons() + + def __activateButtons(self): + self.cleanupDialog() + if hasattr(self, 'activateMode'): + if self.activateMode == 'book': + self.bookActivateButtons() + elif self.activateMode == 'purchaseDelete': + self.purchaseDeleteActivateButtons() + elif self.activateMode == 'purchase': + self.purchaseActivateButtons() + elif self.activateMode == 'purchaseBroke': + self.purchaseBrokeActivateButtons() + elif self.activateMode == 'gagTutDisabled': + self.gagTutDisabledActivateButtons() + elif self.activateMode == 'battle': + self.battleActivateButtons() + elif self.activateMode == 'storePurchaseDelete': + self.storePurchaseDeleteActivateButtons() + elif self.activateMode == 'storePurchase': + self.storePurchaseActivateButtons() + elif self.activateMode == 'storePurchaseBroke': + self.storePurchaseBrokeActivateButtons() + elif self.activateMode == 'plantTree': + self.plantTreeActivateButtons() + else: + self.notify.error('No such mode as %s' % self.activateMode) + return None + + def bookActivateButtons(self): + self.setPos(0, 0, 0.52) + self.setScale(1.0) + self.detailFrame.setPos(-0.05, 0, -0.855) + self.detailFrame.setScale(0.75) + self.deleteEnterButton.hide() + self.deleteAllButton.hide() + self.deleteExitButton.hide() + self.invFrame.reparentTo(self) + self.invFrame.setPos(0, 0, 0) + self.invFrame.setScale(1) + + for track in xrange(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in xrange(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + self.makeBookUnpressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + def updateDeleteButtons(self): + for track in xrange(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in xrange(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + if self.numItem(track, level) <= 0 or level >= UBER_GAG_LEVEL_INDEX: + self.makeUnpressable(button, track, level) + else: + self.makeDeletePressable(button, track, level) + else: + button.hide() + else: + self.hideTrack(track) + + def updateUseableButtons(self): + for track in xrange(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in xrange(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + self.makeUnpressable(button, track, level) + else: + button.hide() + else: + self.hideTrack(track) + + def updatePurchaseButtons(self): + for track in xrange(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in xrange(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + if self.numItem(track, level) >= self.getMax(track, level) or self.totalProps == self.toon.getMaxCarry() or level > LAST_REGULAR_GAG_LEVEL: + self.makeUnpressable(button, track, level) + else: + self.makePressable(button, track, level) + else: + button.hide() + else: + self.hideTrack(track) + + def purchaseDeleteActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.purchaseFrame == None: + self.loadPurchaseFrame() + self.purchaseFrame.show() + self.invFrame.reparentTo(self.purchaseFrame) + self.invFrame.setPos(-0.235, 0, 0.52) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.17, 0, -0.02) + self.detailFrame.setScale(1.25) + self.deleteEnterButton.hide() + self.deleteEnterButton.setPos(-0.8, 0, -0.917) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.show() + self.deleteExitButton.setPos(-0.8, 0, -0.917) + self.deleteExitButton.setScale(0.75) + self.deleteExitButton['command'] = self.setActivateMode + self.deleteExitButton['extraArgs'] = [self.previousActivateMode] + self.updateDeleteButtons() + + def purchaseDeleteDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.purchaseFrame.hide() + self.deleteDeactivateButtons() + self.updateDeleteButtons() + + def storePurchaseDeleteActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.storePurchaseFrame == None: + self.loadStorePurchaseFrame() + self.storePurchaseFrame.show() + self.invFrame.reparentTo(self.storePurchaseFrame) + self.invFrame.setPos(-0.23, 0, 0.505) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.175, 0, 0) + self.detailFrame.setScale(1.25) + self.deleteEnterButton.hide() + self.deleteEnterButton.setPos(-0.8, 0, -0.91) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.show() + self.deleteExitButton.setPos(-0.8, 0, -0.91) + self.deleteExitButton.setScale(0.75) + self.deleteExitButton['command'] = self.setActivateMode + self.deleteExitButton['extraArgs'] = [self.previousActivateMode] + self.updateDeleteButtons() + + def storePurchaseDeleteDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.storePurchaseFrame.hide() + self.deleteDeactivateButtons() + + def storePurchaseBrokeActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.storePurchaseFrame == None: + self.loadStorePurchaseFrame() + self.storePurchaseFrame.show() + self.invFrame.reparentTo(self.storePurchaseFrame) + self.invFrame.setPos(-0.23, 0, 0.505) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.175, 0, 0) + self.detailFrame.setScale(1.25) + self.deleteAllButton.show() + self.deleteEnterButton.show() + self.deleteEnterButton.setPos(-0.8, 0, -0.91) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.hide() + self.deleteExitButton.setPos(-0.8, 0, -0.91) + self.deleteExitButton.setScale(0.75) + self.updateUseableButtons() + + def storePurchaseBrokeDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.storePurchaseFrame.hide() + + def deleteActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0, 0, 0) + self.setScale(1) + self.deleteEnterButton.hide() + self.deleteExitButton.show() + self.deleteExitButton['command'] = self.setActivateMode + self.deleteExitButton['extraArgs'] = [self.previousActivateMode] + for track in xrange(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in xrange(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + if self.numItem(track, level) <= 0: + self.makeUnpressable(button, track, level) + else: + self.makePressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + return None + + def deleteDeactivateButtons(self): + self.deleteExitButton['command'] = None + return + + def purchaseActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.purchaseFrame == None: + self.loadPurchaseFrame() + self.purchaseFrame.show() + self.invFrame.reparentTo(self.purchaseFrame) + self.invFrame.setPos(-0.235, 0, 0.52) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.17, 0, -0.02) + self.detailFrame.setScale(1.25) + self.deleteAllButton.show() + self.deleteEnterButton.show() + self.deleteEnterButton.setPos(-0.8, 0, -0.917) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.hide() + self.deleteExitButton.setPos(-0.8, 0, -0.917) + self.deleteExitButton.setScale(0.75) + if self.gagTutMode: + self.deleteAllButton.hide() + self.deleteEnterButton.hide() + self.deleteEnterButton['command'] = self.setActivateMode + self.deleteEnterButton['extraArgs'] = ['purchaseDelete'] + self.updatePurchaseButtons() + + def purchaseDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.purchaseFrame.hide() + + def storePurchaseActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.storePurchaseFrame == None: + self.loadStorePurchaseFrame() + self.storePurchaseFrame.show() + self.invFrame.reparentTo(self.storePurchaseFrame) + self.invFrame.setPos(-0.23, 0, 0.505) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.175, 0, 0) + self.detailFrame.setScale(1.25) + self.deleteAllButton.show() + self.deleteEnterButton.show() + self.deleteEnterButton.setPos(-0.8, 0, -0.91) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.hide() + self.deleteExitButton.setPos(-0.8, 0, -0.91) + self.deleteExitButton.setScale(0.75) + self.deleteEnterButton['command'] = self.setActivateMode + self.deleteEnterButton['extraArgs'] = ['storePurchaseDelete'] + self.updatePurchaseButtons() + + def storePurchaseDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.storePurchaseFrame.hide() + + def purchaseBrokeActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.purchaseFrame == None: + self.loadPurchaseFrame() + self.purchaseFrame.show() + self.invFrame.reparentTo(self.purchaseFrame) + self.invFrame.setPos(-0.235, 0, 0.52) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.17, 0, -0.02) + self.detailFrame.setScale(1.25) + self.deleteAllButton.show() + self.deleteEnterButton.show() + self.deleteEnterButton.setPos(-0.8, 0, -0.917) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.hide() + self.deleteExitButton.setPos(-0.8, 0, -0.917) + self.deleteExitButton.setScale(0.75) + if self.gagTutMode: + self.deleteAllButton.hide() + self.deleteEnterButton.hide() + for track in xrange(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in xrange(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + if not self.gagTutMode: + self.makeUnpressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + return + + def purchaseBrokeDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.purchaseFrame.hide() + + def gagTutDisabledActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.purchaseFrame == None: + self.loadPurchaseFrame() + self.purchaseFrame.show() + self.invFrame.reparentTo(self.purchaseFrame) + self.invFrame.setPos(-0.235, 0, 0.52) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.17, 0, -0.02) + self.detailFrame.setScale(1.25) + self.deleteExitButton.hide() + self.deleteEnterButton.hide() + self.deleteAllButton.hide() + self.updateUseableButtons() + + def gagTutDisabledDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.purchaseFrame.hide() + + def battleActivateButtons(self): + self.stopAndClearPropBonusIval() + self.reparentTo(aspect2d) + self.setPos(0, 0, 0.1) + self.setScale(1) + if self.battleFrame == None: + self.loadBattleFrame() + self.battleFrame.show() + self.battleFrame.setScale(0.9) + self.invFrame.reparentTo(self.battleFrame) + self.invFrame.setPos(-0.26, 0, 0.35) + self.invFrame.setScale(1) + self.detailFrame.setPos(1.125, 0, -0.08) + self.detailFrame.setScale(1) + self.deleteAllButton.hide() + self.deleteEnterButton.hide() + self.deleteExitButton.hide() + if self.bldg == 1: + self.runButton.hide() + self.sosButton.show() + self.passButton.show() + elif self.tutorialFlag == 1: + self.runButton.hide() + self.sosButton.hide() + self.passButton.hide() + self.fireButton.hide() + else: + self.runButton.show() + self.sosButton.show() + self.passButton.show() + self.fireButton.show() + if localAvatar.getPinkSlips(): + self.fireButton['state'] = DGG.NORMAL + self.fireButton['image_color'] = Vec4(0, 0.6, 1, 1) + else: + self.fireButton['state'] = DGG.DISABLED + self.fireButton['image_color'] = Vec4(0.4, 0.4, 0.4, 1) + for track in xrange(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in xrange(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + if self.numItem(track, level) <= 0 or track == HEAL_TRACK and not self.heal or track == TRAP_TRACK and not self.trap or track == LURE_TRACK and not self.lure: + self.makeUnpressable(button, track, level) + elif self.itemIsCredit(track, level): + self.makePressable(button, track, level) + else: + self.makeNoncreditPressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + self.propBonusIval.loop() + return + + def battleDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.battleFrame.hide() + self.stopAndClearPropBonusIval() + + def plantTreeActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0, 0, 0.1) + self.setScale(1) + if self.battleFrame == None: + self.loadBattleFrame() + self.battleFrame.show() + self.battleFrame.setScale(0.9) + self.invFrame.reparentTo(self.battleFrame) + self.invFrame.setPos(-0.25, 0, 0.35) + self.invFrame.setScale(1) + self.detailFrame.setPos(1.125, 0, -0.08) + self.detailFrame.setScale(1) + self.deleteAllButton.hide() + self.deleteEnterButton.hide() + self.deleteExitButton.hide() + self.runButton.hide() + self.sosButton.hide() + self.fireButton.hide() + self.passButton['text'] = TTLocalizer.lCancel + self.passButton.show() + for track in xrange(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in xrange(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level) and (level == 0 or self.toon.doIHaveRequiredTrees(track, level)): + button.show() + self.makeUnpressable(button, track, level) + if self.numItem(track, level) > 0: + if not self.toon.isTreePlanted(track, level): + self.makePressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + return + + def plantTreeDeactivateButtons(self): + self.passButton['text'] = TTLocalizer.InventoryPass + self.invFrame.reparentTo(self) + self.battleFrame.hide() + + def itemIsUsable(self, track, level): + if self.gagTutMode: + trackAccess = self.toon.getTrackAccess() + return trackAccess[track] >= level + 1 + curSkill = self.toon.experience.getExp(track) + if curSkill < Levels[track][level]: + return 0 + else: + return 1 + + def itemIsCredit(self, track, level): + if self.toon.earnedExperience: + if self.toon.earnedExperience[track] >= ExperienceCap: + return 0 + if self.battleCreditLevel == None: + return 1 + else: + return level < self.battleCreditLevel + return + + def getMax(self, track, level): + if self.gagTutMode and (track not in (4, 5) or level > 0): + return 1 + return InventoryBase.InventoryBase.getMax(self, track, level) + + def getCurAndNextExpValues(self, track): + curSkill = self.toon.experience.getExp(track) + retVal = MaxSkill + for amount in Levels[track]: + if curSkill < amount: + retVal = amount + return (curSkill, retVal) + + return (curSkill, retVal) + + def makePressable(self, button, track, level): + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + shadowColor = self.ShadowBuffedColor + else: + shadowColor = self.ShadowColor + button.configure(image0_image=self.upButton, image2_image=self.rolloverButton, text_shadow=shadowColor, geom_color=self.PressableGeomColor, commandButtons=(DGG.LMB,)) + if self.interactivePropTrackBonus == track: + button.configure(image_color=self.PropBonusPressableImageColor) + self.addToPropBonusIval(button) + else: + button.configure(image_color=self.PressableImageColor) + + def makeDisabledPressable(self, button, track, level): + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + shadowColor = self.UnpressableShadowBuffedColor + else: + shadowColor = self.ShadowColor + button.configure(text_shadow=shadowColor, geom_color=self.UnpressableGeomColor, image_image=self.flatButton, commandButtons=(DGG.LMB,)) + button.configure(image_color=self.UnpressableImageColor) + + def makeNoncreditPressable(self, button, track, level): + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + shadowColor = self.ShadowBuffedColor + else: + shadowColor = self.ShadowColor + button.configure(image0_image=self.upButton, image2_image=self.rolloverButton, text_shadow=shadowColor, geom_color=self.PressableGeomColor, commandButtons=(DGG.LMB,)) + if self.interactivePropTrackBonus == track: + button.configure(image_color=self.PropBonusNoncreditPressableImageColor) + self.addToPropBonusIval(button) + else: + button.configure(image_color=self.NoncreditPressableImageColor) + + def makeDeletePressable(self, button, track, level): + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + shadowColor = self.ShadowBuffedColor + else: + shadowColor = self.ShadowColor + button.configure(image0_image=self.upButton, image2_image=self.rolloverButton, text_shadow=shadowColor, geom_color=self.PressableGeomColor, commandButtons=(DGG.LMB,)) + button.configure(image_color=self.DeletePressableImageColor) + + def makeUnpressable(self, button, track, level): + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + shadowColor = self.UnpressableShadowBuffedColor + else: + shadowColor = self.ShadowColor + button.configure(text_shadow=shadowColor, geom_color=self.UnpressableGeomColor, image_image=self.flatButton, commandButtons=()) + button.configure(image_color=self.UnpressableImageColor) + + def makeBookUnpressable(self, button, track, level): + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + shadowColor = self.ShadowBuffedColor + else: + shadowColor = self.ShadowColor + button.configure(text_shadow=shadowColor, geom_color=self.BookUnpressableGeomColor, image_image=self.flatButton, commandButtons=()) + button.configure(image0_color=self.BookUnpressableImage0Color, image2_color=self.BookUnpressableImage2Color) + + def hideTrack(self, trackIndex): + self.trackNameLabels[trackIndex].show() + self.trackBars[trackIndex].hide() + for levelIndex in xrange(0, len(Levels[trackIndex])): + self.buttons[trackIndex][levelIndex].hide() + + def showTrack(self, trackIndex): + self.trackNameLabels[trackIndex].show() + self.trackBars[trackIndex].show() + for levelIndex in xrange(0, len(Levels[trackIndex])): + self.buttons[trackIndex][levelIndex].show() + + curExp, nextExp = self.getCurAndNextExpValues(trackIndex) + if curExp >= regMaxSkill: + self.trackBars[trackIndex]['range'] = UberSkill + self.trackBars[trackIndex]['text'] = TTLocalizer.InventoryUberTrackExp % {'nextExp': MaxSkill - curExp} + else: + self.trackBars[trackIndex]['range'] = nextExp + self.trackBars[trackIndex]['text'] = TTLocalizer.InventoryTrackExp % {'curExp': curExp, + 'nextExp': nextExp} + + def updateInvString(self, invString): + InventoryBase.InventoryBase.updateInvString(self, invString) + self.updateGUI() + return None + + def updateButton(self, track, level): + button = self.buttons[track][level] + button['text'] = str(self.numItem(track, level)) + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + textScale = 0.05 + else: + textScale = 0.04 + button.configure(text_scale=textScale) + + def buttonBoing(self, track, level): + button = self.buttons[track][level] + oldScale = button.getScale() + s = Sequence(button.scaleInterval(0.1, oldScale * 1.333, blendType='easeOut'), button.scaleInterval(0.1, oldScale, blendType='easeIn'), name='inventoryButtonBoing-' + str(self.this)) + s.start() + + def updateGUI(self, track = None, level = None): + self.updateTotalPropsText() + if track == None and level == None: + for track in xrange(len(Tracks)): + curExp, nextExp = self.getCurAndNextExpValues(track) + if curExp >= regMaxSkill: + self.trackBars[track]['text'] = TTLocalizer.InventoryUberTrackExp % {'nextExp': MaxSkill - curExp} + self.trackBars[track]['value'] = curExp - regMaxSkill + else: + self.trackBars[track]['text'] = TTLocalizer.InventoryTrackExp % {'curExp': curExp, + 'nextExp': nextExp} + self.trackBars[track]['value'] = curExp + for level in xrange(0, len(Levels[track])): + self.updateButton(track, level) + + elif track != None and level != None: + self.updateButton(track, level) + else: + self.notify.error('Invalid use of updateGUI') + self.__activateButtons() + return + + def getSingleGroupStr(self, track, level): + if track == HEAL_TRACK: + if isGroup(track, level): + return TTLocalizer.InventoryAffectsAllToons + else: + return TTLocalizer.InventoryAffectsOneToon + elif isGroup(track, level): + return TTLocalizer.InventoryAffectsAllCogs + else: + return TTLocalizer.InventoryAffectsOneCog + + def getToonupDmgStr(self, track, level): + if track == HEAL_TRACK: + return TTLocalizer.InventoryHealString + elif track == LURE_TRACK: + return TTLocalizer.InventoryLureString + else: + return TTLocalizer.InventoryDamageString + + def deleteItem(self, track, level): + if self.numItem(track, level) > 0: + self.useItem(track, level) + self.updateGUI(track, level) + + def loadBattleFrame(self): + battleModels = loader.loadModel('phase_3.5/models/gui/battle_gui') + self.battleFrame = DirectFrame(relief=None, image=battleModels.find('**/BATTLE_Menu'), image_scale=0.8, parent=self) + self.runButton = DirectButton(parent=self.battleFrame, relief=None, pos=(0.73, 0, -0.398), text=TTLocalizer.InventoryRun, text_scale=TTLocalizer.INrunButton, text_pos=(0, -0.02), text_fg=Vec4(1, 1, 1, 1), textMayChange=0, image=(self.upButton, self.downButton, self.rolloverButton), image_scale=1.05, image_color=(0, 0.6, 1, 1), command=self.__handleRun) + self.sosButton = DirectButton(parent=self.battleFrame, relief=None, pos=(0.96, 0, -0.398), text=TTLocalizer.InventorySOS, text_scale=0.05, text_pos=(0, -0.02), text_fg=Vec4(1, 1, 1, 1), textMayChange=0, image=(self.upButton, self.downButton, self.rolloverButton), image_scale=1.05, image_color=(0, 0.6, 1, 1), command=self.__handleSOS) + self.passButton = DirectButton(parent=self.battleFrame, relief=None, pos=(0.96, 0, -0.242), text=TTLocalizer.InventoryPass, text_scale=TTLocalizer.INpassButton, text_pos=(0, -0.02), text_fg=Vec4(1, 1, 1, 1), textMayChange=1, image=(self.upButton, self.downButton, self.rolloverButton), image_scale=1.05, image_color=(0, 0.6, 1, 1), command=self.__handlePass) + self.fireButton = DirectButton(parent=self.battleFrame, relief=None, pos=(0.73, 0, -0.242), text=TTLocalizer.InventoryFire, text_scale=TTLocalizer.INfireButton, text_pos=(0, -0.02), text_fg=Vec4(1, 1, 1, 1), textMayChange=0, image=(self.upButton, self.downButton, self.rolloverButton), image_scale=1.05, image_color=(0, 0.6, 1, 1), command=self.__handleFire) + self.tutText = DirectFrame(parent=self.battleFrame, relief=None, pos=(0.05, 0, -0.1133), scale=0.143, image=DGG.getDefaultDialogGeom(), image_scale=5.125, image_pos=(0, 0, -0.65), image_color=ToontownGlobals.GlobalDialogColor, text_scale=TTLocalizer.INclickToAttack, text=TTLocalizer.InventoryClickToAttack, textMayChange=0) + self.tutText.hide() + battleModels.removeNode() + self.battleFrame.hide() + return + + def loadPurchaseFrame(self): + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + self.purchaseFrame = DirectFrame(relief=None, image=purchaseModels.find('**/PurchasePanel'), image_pos=(-0.21, 0, 0.08), parent=self) + self.purchaseFrame.setX(-.06) + self.purchaseFrame.hide() + purchaseModels.removeNode() + return + + def loadStorePurchaseFrame(self): + storePurchaseModels = loader.loadModel('phase_4/models/gui/gag_shop_purchase_gui') + self.storePurchaseFrame = DirectFrame(relief=None, image=storePurchaseModels.find('**/gagShopPanel'), image_pos=(-0.21, 0, 0.18), parent=self) + self.storePurchaseFrame.hide() + storePurchaseModels.removeNode() + return + + def buttonLookup(self, track, level): + return self.invModels[track][level] + + def enterTrackFrame(self, track, guiItem): + messenger.send('enterTrackFrame', [track]) + + def exitTrackFrame(self, track, guiItem): + messenger.send('exitTrackFrame', [track]) + + def checkPropBonus(self, track): + return track == self.interactivePropTrackBonus + + def stopAndClearPropBonusIval(self): + if self.propBonusIval and self.propBonusIval.isPlaying(): + self.propBonusIval.finish() + self.propBonusIval = Parallel(name='dummyPropBonusIval') + + def addToPropBonusIval(self, button): + flashObject = button + try: + flashObject = button.component('image0') + except: + pass + + goDark = LerpColorScaleInterval(flashObject, 0.5, Point4(0.1, 0.1, 0.1, 1.0), Point4(1, 1, 1, 1), blendType='easeIn') + goBright = LerpColorScaleInterval(flashObject, 0.5, Point4(1, 1, 1, 1), Point4(0.1, 0.1, 0.1, 1.0), blendType='easeOut') + newSeq = Sequence(goDark, goBright, Wait(0.2)) + self.propBonusIval.append(newSeq) diff --git a/toontown/toon/LaffMeter.py b/toontown/toon/LaffMeter.py new file mode 100755 index 00000000..2f21876f --- /dev/null +++ b/toontown/toon/LaffMeter.py @@ -0,0 +1,189 @@ +from pandac.PandaModules import Vec4 +from direct.gui.DirectGui import DirectFrame, DirectLabel +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownIntervals + +class LaffMeter(DirectFrame): + deathColor = Vec4(0.58039216, 0.80392157, 0.34117647, 1.0) + + def __init__(self, avdna, hp, maxHp): + DirectFrame.__init__(self, relief=None, sortOrder=50) + self.initialiseoptions(LaffMeter) + self.container = DirectFrame(parent=self, relief=None) + self.style = avdna + self.av = None + self.hp = hp + self.maxHp = maxHp + self.__obscured = 0 + if self.style.type == 't': + self.isToon = 1 + else: + self.isToon = 0 + self.load() + return + + def obscure(self, obscured): + self.__obscured = obscured + if self.__obscured: + self.hide() + + def isObscured(self): + return self.__obscured + + def load(self): + gui = loader.loadModel('phase_3/models/gui/laff_o_meter') + if self.isToon: + hType = self.style.getType() + if hType == 'dog': + headModel = gui.find('**/doghead') + elif hType == 'cat': + headModel = gui.find('**/cathead') + elif hType == 'mouse': + headModel = gui.find('**/mousehead') + elif hType == 'horse': + headModel = gui.find('**/horsehead') + elif hType == 'rabbit': + headModel = gui.find('**/bunnyhead') + elif hType == 'duck': + headModel = gui.find('**/duckhead') + elif hType == 'monkey': + headModel = gui.find('**/monkeyhead') + elif hType == 'bear': + headModel = gui.find('**/bearhead') + elif hType == 'pig': + headModel = gui.find('**/pighead') + else: + raise StandardError('unknown toon species: ', hType) + self.color = self.style.getHeadColor() + self.container['image'] = headModel + self.container['image_color'] = self.color + self.resetFrameSize() + self.setScale(0.1) + self.frown = DirectFrame(parent=self.container, relief=None, image=gui.find('**/frown')) + self.smile = DirectFrame(parent=self.container, relief=None, image=gui.find('**/smile')) + self.eyes = DirectFrame(parent=self.container, relief=None, image=gui.find('**/eyes')) + self.openSmile = DirectFrame(parent=self.container, relief=None, image=gui.find('**/open_smile')) + self.tooth1 = DirectFrame(parent=self.openSmile, relief=None, image=gui.find('**/tooth_1')) + self.tooth2 = DirectFrame(parent=self.openSmile, relief=None, image=gui.find('**/tooth_2')) + self.tooth3 = DirectFrame(parent=self.openSmile, relief=None, image=gui.find('**/tooth_3')) + self.tooth4 = DirectFrame(parent=self.openSmile, relief=None, image=gui.find('**/tooth_4')) + self.tooth5 = DirectFrame(parent=self.openSmile, relief=None, image=gui.find('**/tooth_5')) + self.tooth6 = DirectFrame(parent=self.openSmile, relief=None, image=gui.find('**/tooth_6')) + self.maxLabel = DirectLabel(parent=self.eyes, relief=None, pos=(0.442, 0, 0.051), text='120', text_scale=0.4, text_font=ToontownGlobals.getInterfaceFont()) + self.hpLabel = DirectLabel(parent=self.eyes, relief=None, pos=(-0.398, 0, 0.051), text='120', text_scale=0.4, text_font=ToontownGlobals.getInterfaceFont()) + self.teeth = [self.tooth6, + self.tooth5, + self.tooth4, + self.tooth3, + self.tooth2, + self.tooth1] + self.fractions = [0.0, + 0.166666, + 0.333333, + 0.5, + 0.666666, + 0.833333] + gui.removeNode() + return + + def destroy(self): + if self.av: + ToontownIntervals.cleanup(self.av.uniqueName('laffMeterBoing') + '-' + str(self.this)) + ToontownIntervals.cleanup(self.av.uniqueName('laffMeterBoing') + '-' + str(self.this) + '-play') + self.ignore(self.av.uniqueName('hpChange')) + del self.style + del self.av + del self.hp + del self.maxHp + if self.isToon: + del self.frown + del self.smile + del self.openSmile + del self.tooth1 + del self.tooth2 + del self.tooth3 + del self.tooth4 + del self.tooth5 + del self.tooth6 + del self.teeth + del self.fractions + del self.maxLabel + del self.hpLabel + DirectFrame.destroy(self) + + def adjustTeeth(self): + if self.isToon: + for i in xrange(len(self.teeth)): + if self.hp > self.maxHp * self.fractions[i]: + self.teeth[i].show() + else: + self.teeth[i].hide() + + def adjustText(self): + if self.isToon: + if self.maxLabel['text'] != str(self.maxHp) or self.hpLabel['text'] != str(self.hp): + self.maxLabel['text'] = str(self.maxHp) + self.hpLabel['text'] = str(self.hp) + + def animatedEffect(self, delta): + if delta == 0 or self.av == None: + return + name = self.av.uniqueName('laffMeterBoing') + '-' + str(self.this) + ToontownIntervals.cleanup(name) + if delta > 0: + ToontownIntervals.start(ToontownIntervals.getPulseLargerIval(self.container, name)) + else: + ToontownIntervals.start(ToontownIntervals.getPulseSmallerIval(self.container, name)) + return + + def adjustFace(self, hp, maxHp, quietly = 0): + if self.isToon and self.hp != None: + self.frown.hide() + self.smile.hide() + self.openSmile.hide() + self.eyes.hide() + for tooth in self.teeth: + tooth.hide() + + delta = hp - self.hp + self.hp = hp + self.maxHp = maxHp + if self.hp < 1: + self.frown.show() + self.container['image_color'] = self.deathColor + elif self.hp >= self.maxHp: + self.smile.show() + self.eyes.show() + self.container['image_color'] = self.color + else: + self.openSmile.show() + self.eyes.show() + self.maxLabel.show() + self.hpLabel.show() + self.container['image_color'] = self.color + self.adjustTeeth() + self.adjustText() + if not quietly: + self.animatedEffect(delta) + + def start(self): + if self.av: + self.hp = self.av.hp + self.maxHp = self.av.maxHp + if self.isToon: + if not self.__obscured: + self.show() + self.adjustFace(self.hp, self.maxHp, 1) + if self.av: + self.accept(self.av.uniqueName('hpChange'), self.adjustFace) + + def stop(self): + if self.isToon: + self.hide() + if self.av: + self.ignore(self.av.uniqueName('hpChange')) + + def setAvatar(self, av): + if self.av: + self.ignore(self.av.uniqueName('hpChange')) + self.av = av diff --git a/toontown/toon/LaffRestockGlobals.py b/toontown/toon/LaffRestockGlobals.py new file mode 100755 index 00000000..5bef9aab --- /dev/null +++ b/toontown/toon/LaffRestockGlobals.py @@ -0,0 +1,21 @@ +from toontown.toonbase import TTLocalizer + +TIMER_SECONDS = 30 + +# Laff Shop GUI +TIMER_END = 0 +USER_CANCEL = 1 +RESTOCK = 2 + +# Restock Results +FULL_LAFF = 0 +LESS_LAFF = 1 +NOT_ENOUGH_MONEY = 2 +RESTOCK_SUCCESSFUL = 3 + +RestockMessages = { + FULL_LAFF: TTLocalizer.RestockFullLaffMessage, + LESS_LAFF: TTLocalizer.RestockLessLaffMessage, + NOT_ENOUGH_MONEY: TTLocalizer.RestockNoMoneyMessage, + RESTOCK_SUCCESSFUL: TTLocalizer.RestockSuccessfulMessage +} \ No newline at end of file diff --git a/toontown/toon/LaffShopGui.py b/toontown/toon/LaffShopGui.py new file mode 100755 index 00000000..f061598a --- /dev/null +++ b/toontown/toon/LaffShopGui.py @@ -0,0 +1,117 @@ +from direct.gui.DirectGui import DirectButton, DirectFrame, DGG +from direct.task.Task import Task +from otp.otpbase import OTPLocalizer +from toontown.toonbase import ToontownGlobals, TTLocalizer, ToontownTimer +import LaffMeter, LaffRestockGlobals + +class LaffShopGui(DirectFrame): + + def __init__(self): + DirectFrame.__init__(self, parent=aspect2d, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.33, 1, 1.3), pos=(0, 0, 0), text='', text_scale=0.07, text_pos=(0, 0.475)) + self.initialiseoptions(LaffShopGui) + + self.additionalLaff = 0 + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(aspect2d) + self.timer.posInTopRightCorner() + self.timer.countdown(LaffRestockGlobals.TIMER_SECONDS, self.__cancel, [LaffRestockGlobals.TIMER_END]) + self.setupButtons() + self.bindButtons() + self.laffMeter = LaffMeter.LaffMeter(base.localAvatar.style, base.localAvatar.getHp(), base.localAvatar.getMaxHp()) + self.laffMeter.reparentTo(self) + self.laffMeter.setPos(0, 0, 0.065) + self.laffMeter.setScale(0.13) + self.__updateLaffMeter(1) + + def setupButtons(self): + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + arrowGui = loader.loadModel('phase_3/models/gui/create_a_toon_gui') + arrowImageList = (arrowGui.find('**/CrtATn_R_Arrow_UP'), arrowGui.find('**/CrtATn_R_Arrow_DN'), arrowGui.find('**/CrtATn_R_Arrow_RLVR'), arrowGui.find('**/CrtATn_R_Arrow_UP')) + + self.cancelButton = DirectButton(parent=self, relief=None, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), pos=(-0.2, 0, -0.5), text=OTPLocalizer.lCancel, text_scale=0.06, text_pos=(0, -0.1), command=self.__cancel, extraArgs=[LaffRestockGlobals.USER_CANCEL]) + self.okButton = DirectButton(parent=self, relief=None, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), pos=(0.2, 0, -0.5), text=OTPLocalizer.lOK, text_scale=0.06, text_pos=(0, -0.1), command=self.__restock) + self.upArrow = DirectButton(parent=self, relief=None, image=arrowImageList, image_scale=(1, 1, 1), image3_color=Vec4(0.6, 0.6, 0.6, 0.25), pos=(0.2, 0, -0.265)) + self.downArrow = DirectButton(parent=self, relief=None, image=arrowImageList, image_scale=(-1, 1, 1), image3_color=Vec4(0.6, 0.6, 0.6, 0.25), pos=(-0.2, 0, -0.265)) + + buttons.removeNode() + arrowGui.removeNode() + + def bindButtons(self): + self.downArrow.bind(DGG.B1PRESS, self.__taskUpdate, extraArgs=[-1]) + self.downArrow.bind(DGG.B1RELEASE, self.__taskDone) + self.upArrow.bind(DGG.B1PRESS, self.__taskUpdate, extraArgs=[1]) + self.upArrow.bind(DGG.B1RELEASE, self.__taskDone) + + def destroy(self): + self.ignoreAll() + + if self.timer: + self.timer.destroy() + + taskMgr.remove(self.taskName('runLaffCounter')) + DirectFrame.destroy(self) + + def __cancel(self, state): + self.destroy() + messenger.send('laffShopDone', [state, 0]) + + def __restock(self): + self.destroy() + messenger.send('laffShopDone', [LaffRestockGlobals.RESTOCK, self.additionalLaff]) + + def __updateLaffMeter(self, amount): + self.additionalLaff += amount + hitLimit = 0 + newLaff = base.localAvatar.getHp() + self.additionalLaff + + if (newLaff - 1) <= base.localAvatar.getHp(): + self.downArrow['state'] = DGG.DISABLED + hitLimit = 1 + else: + self.downArrow['state'] = DGG.NORMAL + + if newLaff >= base.localAvatar.getMaxHp(): + self.upArrow['state'] = DGG.DISABLED + hitLimit = 1 + else: + self.upArrow['state'] = DGG.NORMAL + + cost = self.additionalLaff * ToontownGlobals.CostPerLaffRestock + self['text'] = TTLocalizer.RestockAskMessage % (self.additionalLaff, cost) + + if cost > base.localAvatar.getTotalMoney(): + self.okButton['state'] = DGG.DISABLED + self['text'] += TTLocalizer.RestockNoMoneyGuiMessage + else: + self.okButton['state'] = DGG.NORMAL + + self.laffMeter.hp = newLaff + self.laffMeter.start() + + return hitLimit + + def __runTask(self, task): + if task.time - task.prevTime < task.delayTime: + return Task.cont + else: + task.delayTime = max(0.05, task.delayTime * 0.75) + task.prevTime = task.time + hitLimit = self.__updateLaffMeter(task.delta) + + return Task.done if hitLimit else Task.cont + + def __taskDone(self, event): + messenger.send('wakeup') + taskMgr.remove(self.taskName('runLaffCounter')) + + def __taskUpdate(self, delta, event): + messenger.send('wakeup') + + task = Task(self.__runTask) + task.delayTime = 0.4 + task.prevTime = 0.0 + task.delta = delta + hitLimit = self.__updateLaffMeter(delta) + + if not hitLimit: + taskMgr.add(task, self.taskName('runLaffCounter')) \ No newline at end of file diff --git a/toontown/toon/LocalToon.py b/toontown/toon/LocalToon.py new file mode 100755 index 00000000..ddf604c9 --- /dev/null +++ b/toontown/toon/LocalToon.py @@ -0,0 +1,1677 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.gui import DirectGuiGlobals +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from direct.showbase import PythonUtil +from direct.showbase.PythonUtil import * +from direct.task import Task +import math +from panda3d.core import * +import random +import re +import time +import DistributedToon +import LaffMeter +import Toon +from otp.avatar import DistributedPlayer +from otp.avatar import LocalAvatar +from otp.avatar import PositionExaminer +from otp.otpbase import OTPGlobals +from toontown.battle import Fanfare +from toontown.battle.BattleSounds import * +from toontown.catalog import CatalogNotifyDialog +from toontown.chat import TTTalkAssistant +from toontown.chat import ToontownChatManager +from otp.nametag.NametagConstants import * +from otp.margins.WhisperPopup import * +from toontown.estate import GardenGlobals +from toontown.parties import PartyGlobals +from toontown.quest import QuestMap +from toontown.quest import QuestParser +from toontown.quest import Quests +from toontown.shtiker import DisguisePage +from toontown.shtiker import PhotoAlbumPage +from toontown.shtiker import EventsPage +from toontown.shtiker import FishPage +from toontown.shtiker import GardenPage +from toontown.shtiker import GolfPage +from toontown.shtiker import InventoryPage +from toontown.shtiker import KartPage +from toontown.shtiker import MapPage +from toontown.shtiker import NPCFriendPage +from toontown.shtiker import OptionsPage +from toontown.shtiker import QuestPage +from toontown.shtiker import ShardPage +from toontown.shtiker import ShtikerBook +from toontown.shtiker import SuitPage +from toontown.shtiker import StatPage +from toontown.shtiker import TrackPage +from toontown.toon import ElevatorNotifier +from toontown.toon import ToonDNA +from toontown.toon.DistributedNPCToonBase import DistributedNPCToonBase +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase.ToontownGlobals import * +from toontown.friends.FriendHandle import FriendHandle + +ClaraBaseXPos = 0.12 + +class LocalToon(DistributedToon.DistributedToon, LocalAvatar.LocalAvatar): + neverDisable = 1 + piePowerSpeed = base.config.GetDouble('pie-power-speed', 0.2) + piePowerExponent = base.config.GetDouble('pie-power-exponent', 0.75) + + def __init__(self, cr): + try: + self.LocalToon_initialized + except: + self.LocalToon_initialized = 1 + self.numFlowers = 0 + self.maxFlowerBasket = 0 + DistributedToon.DistributedToon.__init__(self, cr) + chatMgr = ToontownChatManager.ToontownChatManager(cr, self) + talkAssistant = TTTalkAssistant.TTTalkAssistant() + LocalAvatar.LocalAvatar.__init__(self, cr, chatMgr, talkAssistant, passMessagesThrough=True) + self.soundRun = base.loadSfx('phase_3.5/audio/sfx/AV_footstep_runloop.ogg') + self.soundWalk = base.loadSfx('phase_3.5/audio/sfx/AV_footstep_walkloop.ogg') + self.soundWhisper = base.loadSfx('phase_3.5/audio/sfx/GUI_whisper_3.ogg') + self.soundPhoneRing = base.loadSfx('phase_3.5/audio/sfx/telephone_ring.ogg') + self.soundSystemMessage = base.loadSfx('phase_3/audio/sfx/clock03.ogg') + self.positionExaminer = PositionExaminer.PositionExaminer() + friendsGui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + friendsButtonNormal = friendsGui.find('**/FriendsBox_Closed') + friendsButtonPressed = friendsGui.find('**/FriendsBox_Rollover') + friendsButtonRollover = friendsGui.find('**/FriendsBox_Rollover') + self.bFriendsList = DirectButton(image=(friendsButtonNormal, friendsButtonPressed, friendsButtonRollover), relief=None, pos=(-0.141, 0, -0.125), parent=base.a2dTopRight, scale=0.8, text=('', TTLocalizer.FriendsListLabel, TTLocalizer.FriendsListLabel), text_scale=0.09, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -0.18), text_font=ToontownGlobals.getInterfaceFont(), command=self.sendFriendsListEvent) + self.bFriendsList.hide() + self.friendsListButtonActive = 0 + self.friendsListButtonObscured = 0 + self.moveFurnitureButtonObscured = 0 + self.clarabelleButtonObscured = 0 + friendsGui.removeNode() + self.__furnitureGui = None + self.__lerpFurnitureButton = None + self.__clarabelleButton = None + self.__clarabelleFlash = None + self.furnitureManager = None + self.furnitureDirector = None + self.gotCatalogNotify = 0 + self.__catalogNotifyDialog = None + Toon.loadDialog() + self.isIt = 0 + self.cantLeaveGame = 0 + self.tunnelX = 0.0 + self.estate = None + self.__pieBubble = None + self.allowPies = 0 + self.__pieButton = None + self.__piePowerMeter = None + self.__piePowerMeterSequence = None + self.__pieButtonType = None + self.__pieButtonCount = None + self.tossPieStart = None + self.__presentingPie = 0 + self.__pieSequence = 0 + self.wantBattles = base.config.GetBool('want-battles', 1) + wantNameTagAvIds = base.config.GetBool('want-nametag-avids', 0) + if wantNameTagAvIds: + messenger.send('nameTagShowAvId', []) + base.idTags = 1 + self.glitchX = 0 + self.glitchY = 0 + self.glitchZ = 0 + self.glitchCount = 0 + self.ticker = 0 + self.glitchOkay = 1 + self.tempGreySpacing = 0 + self.wantStatePrint = base.config.GetBool('want-statePrint', 0) + self.__gardeningGui = None + self.__gardeningGuiFake = None + self.__shovelButton = None + self.shovelRelatedDoId = 0 + self.shovelAbility = '' + self.plantToWater = 0 + self.petId = 0 + self.shovelButtonActiveCount = 0 + self.wateringCanButtonActiveCount = 0 + self.showingWateringCan = 0 + self.showingShovel = 0 + self.touchingPlantList = [] + self.inGardenAction = None + self.guiConflict = 0 + self.elevatorNotifier = ElevatorNotifier.ElevatorNotifier() + self._zoneId = None + self.accept('system message aknowledge', self.systemWarning) + self.systemMsgAckGuiDoneEvent = 'systemMsgAckGuiDoneEvent' + self.accept(self.systemMsgAckGuiDoneEvent, self.hideSystemMsgAckGui) + self.systemMsgAckGui = None + self.createSystemMsgAckGui() + self.acceptingNewFriends = True + self.acceptingNonFriendWhispers = True + self.acceptingTeleport = True + self.physControls.event.addAgainPattern('again%in') + self.oldPos = None + self.questMap = None + self.prevToonIdx = 0 + self.houseType = 0 + + def setDNA(self, dna): + base.localAvatarStyle = dna + DistributedToon.DistributedToon.setDNA(self, dna) + + def setName(self, name): + base.localAvatarName = name + DistributedToon.DistributedToon.setName(self, name) + messenger.send('refreshNametagStyle') + + def wantLegacyLifter(self): + return True + + def startGlitchKiller(self): + if localAvatar.getZoneId() not in GlitchKillerZones: + return + taskMgr.remove(self.uniqueName('glitchKiller')) + taskMgr.add(self.glitchKiller, self.uniqueName('glitchKiller')) + self.glitchOkay = 1 + + def pauseGlitchKiller(self): + self.tempGreySpacing = 1 + + def unpauseGlitchKiller(self): + self.tempGreySpacing = 0 + + def stopGlitchKiller(self): + taskMgr.remove(self.uniqueName('glitchKiller')) + self.glitchOkay = 1 + + def glitchKiller(self, taskFooler = 0): + if base.greySpacing or self.tempGreySpacing: + return Task.cont + self.ticker += 1 + if not self.physControls.lifter.hasContact() and not self.glitchOkay: + self.glitchCount += 1 + else: + self.glitchX = self.getX() + self.glitchY = self.getY() + self.glitchZ = self.getZ() + self.glitchCount = 0 + if self.physControls.lifter.hasContact(): + self.glitchOkay = 0 + if hasattr(self, 'physControls'): + if self.ticker >= 10: + self.ticker = 0 + if self.glitchCount >= 7: + print 'GLITCH MAXED!!! resetting pos' + self.setX(self.glitchX - 1 * (self.getX() - self.glitchX)) + self.setY(self.glitchY - 1 * (self.getY() - self.glitchY)) + self.glitchCount = 0 + return Task.cont + + def announceGenerate(self): + self.startLookAround() + DistributedToon.DistributedToon.announceGenerate(self) + + acceptingNewFriends = settings.get('acceptingNewFriends', {}) + acceptingNonFriendWhispers = settings.get('acceptingNonFriendWhispers', {}) + acceptingTeleport = settings.get('acceptingTeleport', {}) + if str(self.doId) not in acceptingNewFriends: + acceptingNewFriends[str(self.doId)] = True + settings['acceptingNewFriends'] = acceptingNewFriends + if str(self.doId) not in acceptingNonFriendWhispers: + acceptingNonFriendWhispers[str(self.doId)] = True + settings['acceptingNonFriendWhispers'] = acceptingNonFriendWhispers + if str(self.doId) not in acceptingTeleport: + acceptingTeleport[str(self.doId)] = True + settings['acceptingTeleport'] = acceptingTeleport + self.acceptingNewFriends = acceptingNewFriends[str(self.doId)] + self.acceptingNonFriendWhispers = acceptingNonFriendWhispers[str(self.doId)] + self.acceptingTeleport = acceptingTeleport[str(self.doId)] + + def disable(self): + self.laffMeter.destroy() + del self.laffMeter + self.questMap.destroy() + self.questMap = None + self.book.unload() + del self.optionsPage + del self.shardPage + del self.mapPage + del self.invPage + del self.questPage + del self.suitPage + del self.sosPage + del self.disguisePage + del self.fishPage + del self.gardenPage + del self.trackPage + del self.book + if base.wantKarts: + if hasattr(self, 'kartPage'): + del self.kartPage + self.ignoreAll() + DistributedToon.DistributedToon.disable(self) + return + + def disableBodyCollisions(self): + pass + + def delete(self): + try: + self.LocalToon_deleted + except: + self.LocalToon_deleted = 1 + Toon.unloadDialog() + QuestParser.clear() + DistributedToon.DistributedToon.delete(self) + LocalAvatar.LocalAvatar.delete(self) + self.bFriendsList.destroy() + del self.bFriendsList + if self.__pieButton: + self.__pieButton.destroy() + self.__pieButton = None + if self.__piePowerMeter: + self.__piePowerMeter.destroy() + self.__piePowerMeter = None + taskMgr.remove('unlockGardenButtons') + if self.__lerpFurnitureButton: + self.__lerpFurnitureButton.finish() + if self.__furnitureGui: + self.__furnitureGui.destroy() + del self.__furnitureGui + if self.__gardeningGui: + self.__gardeningGui.destroy() + del self.__gardeningGui + if self.__gardeningGuiFake: + self.__gardeningGuiFake.destroy() + del self.__gardeningGuiFake + if self.__clarabelleButton: + self.__clarabelleButton.destroy() + del self.__clarabelleButton + if self.__clarabelleFlash: + self.__clarabelleFlash.finish() + del self.__clarabelleFlash + if self.__catalogNotifyDialog: + self.__catalogNotifyDialog.cleanup() + del self.__catalogNotifyDialog + + return + + def initInterface(self): + self.book = ShtikerBook.ShtikerBook('bookDone') + self.book.load() + self.book.hideButton() + self.optionsPage = OptionsPage.OptionsPage() + self.optionsPage.load() + self.book.addPage(self.optionsPage, pageName=TTLocalizer.OptionsPageTitle) + self.shardPage = ShardPage.ShardPage() + self.shardPage.load() + self.book.addPage(self.shardPage, pageName=TTLocalizer.ShardPageTitle) + self.mapPage = MapPage.MapPage() + self.mapPage.load() + self.book.addPage(self.mapPage, pageName=TTLocalizer.MapPageTitle) + self.invPage = InventoryPage.InventoryPage() + self.invPage.load() + self.book.addPage(self.invPage, pageName=TTLocalizer.InventoryPageTitle) + self.questPage = QuestPage.QuestPage() + self.questPage.load() + self.book.addPage(self.questPage, pageName=TTLocalizer.QuestPageToonTasks) + self.trackPage = TrackPage.TrackPage() + self.trackPage.load() + self.book.addPage(self.trackPage, pageName=TTLocalizer.TrackPageShortTitle) + self.suitPage = SuitPage.SuitPage() + self.suitPage.load() + self.book.addPage(self.suitPage, pageName=TTLocalizer.SuitPageTitle) + self.fishPage = FishPage.FishPage() + self.fishPage.setAvatar(self) + self.fishPage.load() + self.book.addPage(self.fishPage, pageName=TTLocalizer.FishPageTitle) + if base.wantKarts: + self.addKartPage() + if self.disguisePageFlag: + self.loadDisguisePages() + if self.sosPageFlag: + self.loadSosPages() + if self.gardenStarted: + self.loadGardenPages() + self.addGolfPage() + self.photoPage = PhotoAlbumPage.PhotoAlbumPage() + self.photoPage.load() + self.book.addPage(self.photoPage, pageName=TTLocalizer.PhotoPageTitle) + self.addEventsPage() + self.statPage = StatPage.StatPage() + self.statPage.load() + self.book.addPage(self.statPage, pageName=TTLocalizer.StatPageTitle) + self.book.setPage(self.mapPage, enterPage=False) + self.laffMeter = LaffMeter.LaffMeter(self.style, self.hp, self.maxHp) + self.laffMeter.setAvatar(self) + self.laffMeter.setScale(0.075) + self.laffMeter.reparentTo(base.a2dBottomLeft) + if self.style.getAnimal() == 'monkey': + self.laffMeter.setPos(0.153, 0.0, 0.13) + else: + self.laffMeter.setPos(0.133, 0.0, 0.13) + self.laffMeter.stop() + self.questMap = QuestMap.QuestMap(self) + self.questMap.stop() + self.accept('time-insert', self.__beginTossPie) + self.accept('time-insert-up', self.__endTossPie) + self.accept('time-delete', self.__beginTossPie) + self.accept('time-delete-up', self.__endTossPie) + self.accept('pieHit', self.__pieHit) + self.accept('interrupt-pie', self.interruptPie) + self.accept('InputState-jump', self.__toonMoved) + self.accept('InputState-forward', self.__toonMoved) + self.accept('InputState-reverse', self.__toonMoved) + self.accept('InputState-turnLeft', self.__toonMoved) + self.accept('InputState-turnRight', self.__toonMoved) + self.accept('InputState-slide', self.__toonMoved) + + QuestParser.init() + return + + if base.wantKarts: + + def addKartPage(self): + if self.hasKart(): + if hasattr(self, 'kartPage') and self.kartPage != None: + return + self.kartPage = KartPage.KartPage() + self.kartPage.setAvatar(self) + self.kartPage.load() + self.book.addPage(self.kartPage, pageName=TTLocalizer.KartPageTitle) + return + + def setWantBattles(self, wantBattles): + self.wantBattles = wantBattles + + def loadDisguisePages(self): + if self.disguisePage != None: + return + self.disguisePage = DisguisePage.DisguisePage() + self.disguisePage.load() + self.book.addPage(self.disguisePage, pageName=TTLocalizer.DisguisePageTitle) + self.loadSosPages() + return + + def loadSosPages(self): + if self.sosPage != None: + return + self.sosPage = NPCFriendPage.NPCFriendPage() + self.sosPage.load() + self.book.addPage(self.sosPage, pageName=TTLocalizer.NPCFriendPageTitle) + return + + def loadGardenPages(self): + if self.gardenPage != None: + return + self.gardenPage = GardenPage.GardenPage() + self.gardenPage.load() + self.book.addPage(self.gardenPage, pageName=TTLocalizer.GardenPageTitle) + + def displayTalkWhisper(self, avId, chat): + sender = base.cr.identifyAvatar(avId) + + if not sender: + return + + if base.whiteList: + chat = base.whiteList.processThroughAll(chat, sender, sender.chatGarbler) + + chatString = '%s: %s' % (sender.getName(), chat) + whisper = WhisperPopup(chatString, OTPGlobals.getInterfaceFont(), WTNormal) + whisper.setClickable(avId) + whisper.manage(base.marginManager) + base.playSfx(self.soundWhisper) + + def isLocal(self): + return 1 + + def startChat(self): + if self.tutorialAck: + self.notify.info('calling LocalAvatar.startchat') + LocalAvatar.LocalAvatar.startChat(self) + self.accept('chatUpdateSCToontask', self.b_setSCToontask) + self.accept('chatUpdateSCResistance', self.d_reqSCResistance) + self.accept('whisperUpdateSCToontask', self.whisperSCToontaskTo) + else: + self.notify.info('NOT calling LocalAvatar.startchat, in tutorial') + + def stopChat(self): + LocalAvatar.LocalAvatar.stopChat(self) + self.ignore('chatUpdateSCToontask') + self.ignore('chatUpdateSCResistance') + self.ignore('whisperUpdateSCToontask') + + def tunnelIn(self, tunnelOrigin): + self.b_setTunnelIn(self.tunnelX * 0.8, tunnelOrigin) + + def tunnelOut(self, tunnelOrigin): + self.tunnelX = self.getX(tunnelOrigin) + tunnelY = self.getY(tunnelOrigin) + self.b_setTunnelOut(self.tunnelX * 0.95, tunnelY, tunnelOrigin) + + def handleTunnelIn(self, startTime, endX, x, y, z, h): + self.notify.debug('LocalToon.handleTunnelIn') + tunnelOrigin = render.attachNewNode('tunnelOrigin') + tunnelOrigin.setPosHpr(x, y, z, h, 0, 0) + self.b_setAnimState('run', self.animMultiplier) + self.stopLookAround() + self.reparentTo(render) + self.runSound() + camera.reparentTo(render) + camera.setPosHpr(tunnelOrigin, 0, 20, 12, 180, -20, 0) + base.transitions.irisIn(0.4) + toonTrack = self.getTunnelInToonTrack(endX, tunnelOrigin) + + def cleanup(self = self, tunnelOrigin = tunnelOrigin): + self.stopSound() + tunnelOrigin.removeNode() + messenger.send('tunnelInMovieDone') + + self.tunnelTrack = Sequence(toonTrack, Func(cleanup)) + self.tunnelTrack.start(globalClock.getFrameTime() - startTime) + + def handleTunnelOut(self, startTime, startX, startY, x, y, z, h): + self.notify.debug('LocalToon.handleTunnelOut') + tunnelOrigin = render.attachNewNode('tunnelOrigin') + tunnelOrigin.setPosHpr(x, y, z, h, 0, 0) + self.b_setAnimState('run', self.animMultiplier) + self.runSound() + self.stopLookAround() + tracks = Parallel() + camera.wrtReparentTo(render) + startPos = camera.getPos(tunnelOrigin) + startHpr = camera.getHpr(tunnelOrigin) + camLerpDur = 1.0 + reducedCamH = fitDestAngle2Src(startHpr[0], 180) + tracks.append(LerpPosHprInterval(camera, camLerpDur, pos=Point3(0, 20, 12), hpr=Point3(reducedCamH, -20, 0), startPos=startPos, startHpr=startHpr, other=tunnelOrigin, blendType='easeInOut', name='tunnelOutLerpCamPos')) + toonTrack = self.getTunnelOutToonTrack(startX, startY, tunnelOrigin) + tracks.append(toonTrack) + irisDur = 0.4 + tracks.append(Sequence(Wait(toonTrack.getDuration() - (irisDur + 0.1)), Func(base.transitions.irisOut, irisDur))) + + def cleanup(self = self, tunnelOrigin = tunnelOrigin): + self.stopSound() + self.detachNode() + tunnelOrigin.removeNode() + messenger.send('tunnelOutMovieDone') + + self.tunnelTrack = Sequence(tracks, Func(cleanup)) + self.tunnelTrack.start(globalClock.getFrameTime() - startTime) + + def getPieBubble(self): + if self.__pieBubble == None: + bubble = CollisionSphere(0, 0, 0, 1) + node = CollisionNode('pieBubble') + node.addSolid(bubble) + node.setFromCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask | ToontownGlobals.FloorBitmask) + node.setIntoCollideMask(BitMask32.allOff()) + self.__pieBubble = NodePath(node) + self.pieHandler = CollisionHandlerEvent() + self.pieHandler.addInPattern('pieHit') + self.pieHandler.addInPattern('pieHit-%in') + return self.__pieBubble + + def __beginTossPieMouse(self, mouseParam): + self.__beginTossPie(globalClock.getFrameTime()) + + def __endTossPieMouse(self, mouseParam): + self.__endTossPie(globalClock.getFrameTime()) + + def __beginTossPie(self, time): + if self.tossPieStart != None: + return + if not self.allowPies: + return + if self.numPies == 0: + messenger.send('outOfPies') + return + if self.__pieInHand(): + return + if getattr(self.controlManager.currentControls, 'isAirborne', 0): + return + messenger.send('wakeup') + self.localPresentPie(time) + taskName = self.uniqueName('updatePiePower') + taskMgr.add(self.__updatePiePower, taskName) + return + + def __endTossPie(self, time): + if self.tossPieStart == None: + return + taskName = self.uniqueName('updatePiePower') + taskMgr.remove(taskName) + messenger.send('wakeup') + power = self.__getPiePower(time) + self.tossPieStart = None + self.localTossPie(power) + return + + def localPresentPie(self, time): + import TTEmote + from otp.avatar import Emote + self.__stopPresentPie() + if self.tossTrack: + tossTrack = self.tossTrack + self.tossTrack = None + tossTrack.finish() + self.interruptPie() + self.tossPieStart = time + self.__pieSequence = self.__pieSequence + 1 & 255 + sequence = self.__pieSequence + self.__presentingPie = 1 + pos = self.getPos() + hpr = self.getHpr() + timestamp32 = globalClockDelta.getFrameNetworkTime(bits=32) + self.sendUpdate('presentPie', [pos[0], + pos[1], + pos[2], + hpr[0] % 360.0, + timestamp32]) + Emote.globalEmote.disableBody(self) + messenger.send('begin-pie') + ival = self.getPresentPieInterval(pos[0], pos[1], pos[2], hpr[0]) + ival = Sequence(ival, name=self.uniqueName('localPresentPie')) + self.tossTrack = ival + ival.start() + self.makePiePowerMeter() + self.__piePowerMeter.show() + self.__piePowerMeterSequence = sequence + self.__piePowerMeter['value'] = 0 + return + + def __stopPresentPie(self): + if self.__presentingPie: + import TTEmote + from otp.avatar import Emote + Emote.globalEmote.releaseBody(self) + messenger.send('end-pie') + self.__presentingPie = 0 + taskName = self.uniqueName('updatePiePower') + taskMgr.remove(taskName) + + def __getPiePower(self, time): + elapsed = max(time - self.tossPieStart, 0.0) + t = elapsed / self.piePowerSpeed + t = math.pow(t, self.piePowerExponent) + power = int(t * 100) % 200 + if power > 100: + power = 200 - power + return power + + def __updatePiePower(self, task): + if not self.__piePowerMeter: + return Task.done + self.__piePowerMeter['value'] = self.__getPiePower(globalClock.getFrameTime()) + return Task.cont + + def interruptPie(self): + self.cleanupPieInHand() + self.__stopPresentPie() + if self.__piePowerMeter: + self.__piePowerMeter.hide() + pie = self.pieTracks.get(self.__pieSequence) + if pie and pie.getT() < 14.0 / 24.0: + del self.pieTracks[self.__pieSequence] + pie.pause() + + def __pieInHand(self): + pie = self.pieTracks.get(self.__pieSequence) + return pie and pie.getT() < 15.0 / 24.0 + + def __toonMoved(self, isSet): + if isSet: + self.interruptPie() + + def localTossPie(self, power): + if not self.__presentingPie: + return + pos = self.getPos() + hpr = self.getHpr() + timestamp32 = globalClockDelta.getFrameNetworkTime(bits=32) + sequence = self.__pieSequence + if self.tossTrack: + tossTrack = self.tossTrack + self.tossTrack = None + tossTrack.finish() + if sequence in self.pieTracks: + pieTrack = self.pieTracks[sequence] + del self.pieTracks[sequence] + pieTrack.finish() + if sequence in self.splatTracks: + splatTrack = self.splatTracks[sequence] + del self.splatTracks[sequence] + splatTrack.finish() + self.makePiePowerMeter() + self.__piePowerMeter['value'] = power + self.__piePowerMeter.show() + self.__piePowerMeterSequence = sequence + pieBubble = self.getPieBubble().instanceTo(NodePath()) + + def pieFlies(self = self, pos = pos, hpr = hpr, sequence = sequence, power = power, timestamp32 = timestamp32, pieBubble = pieBubble): + self.sendUpdate('tossPie', [pos[0], + pos[1], + pos[2], + hpr[0] % 360.0, + sequence, + power, + self.pieThrowType, + timestamp32]) + if self.numPies != ToontownGlobals.FullPies: + self.setNumPies(self.numPies - 1) + base.cTrav.addCollider(pieBubble, self.pieHandler) + + toss, pie, flyPie = self.getTossPieInterval(pos[0], pos[1], pos[2], hpr[0], power, self.pieThrowType, beginFlyIval=Func(pieFlies)) + pieBubble.reparentTo(flyPie) + flyPie.setTag('pieSequence', str(sequence)) + toss = Sequence(toss) + self.tossTrack = toss + toss.start() + pie = Sequence(pie, Func(base.cTrav.removeCollider, pieBubble), Func(self.pieFinishedFlying, sequence)) + self.pieTracks[sequence] = pie + pie.start() + return + + def pieFinishedFlying(self, sequence): + DistributedToon.DistributedToon.pieFinishedFlying(self, sequence) + if self.__piePowerMeterSequence == sequence: + self.__piePowerMeter.hide() + + def __finishPieTrack(self, sequence): + if sequence in self.pieTracks: + pieTrack = self.pieTracks[sequence] + del self.pieTracks[sequence] + pieTrack.finish() + + def __pieHit(self, entry): + if not entry.hasSurfacePoint() or not entry.hasInto(): + return + if not entry.getInto().isTangible(): + return + sequence = int(entry.getFromNodePath().getNetTag('pieSequence')) + self.__finishPieTrack(sequence) + if sequence in self.splatTracks: + splatTrack = self.splatTracks[sequence] + del self.splatTracks[sequence] + splatTrack.finish() + pieCode = 0 + pieCodeStr = entry.getIntoNodePath().getNetTag('pieCode') + if pieCodeStr: + pieCode = int(pieCodeStr) + pos = entry.getSurfacePoint(render) + timestamp32 = globalClockDelta.getFrameNetworkTime(bits=32) + self.sendUpdate('pieSplat', [pos[0], + pos[1], + pos[2], + sequence, + pieCode, + timestamp32]) + splat = self.getPieSplatInterval(pos[0], pos[1], pos[2], pieCode) + splat = Sequence(splat, Func(self.pieFinishedSplatting, sequence)) + self.splatTracks[sequence] = splat + splat.start() + messenger.send('pieSplat', [self, pieCode]) + messenger.send('localPieSplat', [pieCode, entry]) + + def beginAllowPies(self): + self.allowPies = 1 + self.updatePieButton() + + def endAllowPies(self): + self.allowPies = 0 + self.updatePieButton() + + def makePiePowerMeter(self): + from direct.gui.DirectGui import DirectWaitBar, DGG + if self.__piePowerMeter == None: + self.__piePowerMeter = DirectWaitBar(frameSize=(-0.2, + 0.2, + -0.03, + 0.03), relief=DGG.SUNKEN, borderWidth=(0.005, 0.005), barColor=(0.4, 0.6, 1.0, 1), pos=(0, 0.1, 0.8)) + self.__piePowerMeter.hide() + return + + def updatePieButton(self): + from toontown.toonbase import ToontownBattleGlobals + from direct.gui.DirectGui import DirectButton, DGG + wantButton = 0 + if self.allowPies and self.numPies > 0: + wantButton = 1 + haveButton = self.__pieButton != None + if not haveButton and not wantButton: + return + if haveButton and not wantButton: + self.__pieButton.destroy() + self.__pieButton = None + self.__pieButtonType = None + self.__pieButtonCount = None + return + if self.__pieButtonType != self.pieType: + if self.__pieButton: + self.__pieButton.destroy() + self.__pieButton = None + if self.__pieButton == None: + inv = self.inventory + if self.pieType >= len(inv.invModels[ToontownBattleGlobals.THROW_TRACK]): + gui = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + pieGui = gui.find('**/summons') + pieScale = 0.1 + else: + gui = None + pieGui = (inv.invModels[ToontownBattleGlobals.THROW_TRACK][self.pieType],) + pieScale = 0.85 + self.__pieButton = DirectButton(image=(inv.upButton, inv.downButton, inv.rolloverButton), geom=pieGui, text='50', text_scale=0.04, text_align=TextNode.ARight, geom_scale=pieScale, geom_pos=(-0.01, 0, 0), text_fg=Vec4(1, 1, 1, 1), text_pos=(0.07, -0.04), relief=None, image_color=(0, 0.6, 1, 1), pos=(0, 0.1, 0.9)) + self.__pieButton.bind(DGG.B1PRESS, self.__beginTossPieMouse) + self.__pieButton.bind(DGG.B1RELEASE, self.__endTossPieMouse) + self.__pieButtonType = self.pieType + self.__pieButtonCount = None + if gui: + del gui + if self.__pieButtonCount != self.numPies: + if self.numPies == ToontownGlobals.FullPies: + self.__pieButton['text'] = '' + else: + self.__pieButton['text'] = str(self.numPies) + self.__pieButtonCount = self.numPies + + def displayWhisper(self, fromId, chatString, whisperType): + LocalAvatar.LocalAvatar.displayWhisper(self, fromId, chatString, whisperType) + + def loadFurnitureGui(self): + if self.__furnitureGui: + return + guiModels = loader.loadModel('phase_5.5/models/gui/house_design_gui') + self.__furnitureGui = DirectFrame(relief=None, parent=base.a2dTopLeft, pos=(0.115, 0.0, -0.66), scale=0.04, image=guiModels.find('**/attic')) + DirectLabel(parent=self.__furnitureGui, relief=None, image=guiModels.find('**/rooftile')) + bMoveStartUp = guiModels.find('**/bu_attic/bu_attic_up') + bMoveStartDown = guiModels.find('**/bu_attic/bu_attic_down') + bMoveStartRollover = guiModels.find('**/bu_attic/bu_attic_rollover') + DirectButton(parent=self.__furnitureGui, relief=None, image=[bMoveStartUp, + bMoveStartDown, + bMoveStartRollover, + bMoveStartUp], text=['', TTLocalizer.HDMoveFurnitureButton, TTLocalizer.HDMoveFurnitureButton], text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), pos=(-0.3, 0, 9.4), command=self.__startMoveFurniture) + self.__furnitureGui.hide() + guiModels.removeNode() + + def showFurnitureGui(self): + self.loadFurnitureGui() + self.__furnitureGui.show() + + def hideFurnitureGui(self): + if self.__furnitureGui: + self.__furnitureGui.hide() + + def loadClarabelleGui(self): + if self.__clarabelleButton: + return + guiItems = loader.loadModel('phase_5.5/models/gui/catalog_gui') + circle = guiItems.find('**/cover/blue_circle') + icon = guiItems.find('**/cover/clarabelle') + icon.reparentTo(circle) + rgba = VBase4(0.71589, 0.784547, 0.974, 1.0) + white = VBase4(1.0, 1.0, 1.0, 1.0) + icon.setColor(white) + claraXPos = ClaraBaseXPos + self.__clarabelleButton = DirectButton(relief=None, image=circle, text='', text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_pos=(-1.06, 1.06), text_font=ToontownGlobals.getInterfaceFont(), pos=(claraXPos, 1.0, -0.63), scale=0.5, command=self.__handleClarabelleButton) + self.__clarabelleButton.reparentTo(base.a2dTopRight, DGG.BACKGROUND_SORT_INDEX - 1) + button = self.__clarabelleButton.stateNodePath[0] + self.__clarabelleFlash = Sequence(LerpColorInterval(button, 2, white, blendType='easeInOut'), LerpColorInterval(button, 2, rgba, blendType='easeInOut')) + self.__clarabelleFlash.loop() + self.__clarabelleFlash.pause() + return + + def showClarabelleGui(self, mailboxItems): + self.loadClarabelleGui() + if mailboxItems: + self.__clarabelleButton['text'] = ['', TTLocalizer.CatalogNewDeliveryButton, TTLocalizer.CatalogNewDeliveryButton] + else: + self.__clarabelleButton['text'] = ['', TTLocalizer.CatalogNewCatalogButton, TTLocalizer.CatalogNewCatalogButton] + if not self.mailboxNotify and not self.awardNotify and self.catalogNotify == ToontownGlobals.OldItems and (self.simpleMailNotify != ToontownGlobals.NoItems or self.inviteMailNotify != ToontownGlobals.NoItems): + self.__clarabelleButton['text'] = ['', TTLocalizer.MailNewMailButton, TTLocalizer.MailNewMailButton] + self.__clarabelleButton.show() + self.__clarabelleFlash.resume() + + def hideClarabelleGui(self): + if self.__clarabelleButton: + self.__clarabelleButton.hide() + self.__clarabelleFlash.pause() + + def __handleClarabelleButton(self): + self.stopMoveFurniture() + place = base.cr.playGame.getPlace() + if place == None: + self.notify.warning('Tried to go home, but place is None.') + return + if self.__catalogNotifyDialog: + self.__catalogNotifyDialog.cleanup() + self.__catalogNotifyDialog = None + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: VISITESTATE: Visit estate') + place.goHomeNow(self.lastHood) + return + + def __startMoveFurniture(self): + self.oldPos = self.getPos() + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ESTATE: Furniture Placement') + if self.cr.furnitureManager != None: + self.cr.furnitureManager.d_suggestDirector(self.doId) + elif self.furnitureManager != None: + self.furnitureManager.d_suggestDirector(self.doId) + return + + def stopMoveFurniture(self): + if self.oldPos: + self.setPos(self.oldPos) + if self.furnitureManager != None: + self.furnitureManager.d_suggestDirector(0) + return + + def setFurnitureDirector(self, avId, furnitureManager): + if avId == 0: + if self.furnitureManager == furnitureManager: + messenger.send('exitFurnitureMode', [furnitureManager]) + self.furnitureManager = None + self.furnitureDirector = None + elif avId != self.doId: + if self.furnitureManager == None or self.furnitureDirector != avId: + self.furnitureManager = furnitureManager + self.furnitureDirector = avId + messenger.send('enterFurnitureMode', [furnitureManager, 0]) + else: + if self.furnitureManager != None: + messenger.send('exitFurnitureMode', [self.furnitureManager]) + self.furnitureManager = None + self.furnitureManager = furnitureManager + self.furnitureDirector = avId + messenger.send('enterFurnitureMode', [furnitureManager, 1]) + self.refreshOnscreenButtons() + return + + def getAvPosStr(self): + pos = self.getPos() + hpr = self.getHpr() + serverVersion = base.cr.getServerVersion() + districtName = base.cr.getShardName(base.localAvatar.defaultShard) + if hasattr(base.cr.playGame.hood, 'loader') and hasattr(base.cr.playGame.hood.loader, 'place') and base.cr.playGame.getPlace() != None: + zoneId = base.cr.playGame.getPlace().getZoneId() + else: + zoneId = '?' + strPosCoordText = 'X: %.3f' % pos[0] + ', Y: %.3f' % pos[1] + '\nZ: %.3f' % pos[2] + ', H: %.3f' % hpr[0] + '\nZone: %s' % str(zoneId) + ', Ver: %s, ' % serverVersion + 'District: %s' % districtName + return strPosCoordText + self.refreshOnscreenButtons() + return + + def thinkPos(self): + pos = self.getPos() + hpr = self.getHpr() + serverVersion = base.cr.getServerVersion() + districtName = base.cr.getShardName(base.localAvatar.defaultShard) + if hasattr(base.cr.playGame.hood, 'loader') and hasattr(base.cr.playGame.hood.loader, 'place') and base.cr.playGame.getPlace() != None: + zoneId = base.cr.playGame.getPlace().getZoneId() + else: + zoneId = '?' + strPos = '(%.3f' % pos[0] + '\n %.3f' % pos[1] + '\n %.3f)' % pos[2] + '\nH: %.3f' % hpr[0] + '\nZone: %s' % str(zoneId) + ',\nVer: %s, ' % serverVersion + '\nDistrict: %s' % districtName + print 'Current position=', strPos.replace('\n', ', ') + self.setChatAbsolute(strPos, CFThought | CFTimeout) + return + + def __placeMarker(self): + pos = self.getPos() + hpr = self.getHpr() + chest = loader.loadModel('phase_4/models/props/coffin') + chest.reparentTo(render) + chest.setColor(1, 0, 0, 1) + chest.setPosHpr(pos, hpr) + chest.setScale(0.5) + + def setFriendsListButtonActive(self, active): + self.friendsListButtonActive = active + self.refreshOnscreenButtons() + + def obscureFriendsListButton(self, increment): + self.friendsListButtonObscured += increment + self.refreshOnscreenButtons() + + def obscureMoveFurnitureButton(self, obscured): + self.moveFurnitureButtonObscured = obscured + self.refreshOnscreenButtons() + + def obscureClarabelleButton(self, increment): + self.clarabelleButtonObscured += increment + self.refreshOnscreenButtons() + + def refreshOnscreenButtons(self): + self.bFriendsList.hide() + self.hideFurnitureGui() + self.hideClarabelleGui() + clarabelleHidden = 1 + self.ignore(ToontownGlobals.FriendsListHotkey) + if self.friendsListButtonActive and self.friendsListButtonObscured <= 0: + self.bFriendsList.show() + self.accept(ToontownGlobals.FriendsListHotkey, self.sendFriendsListEvent) + if self.clarabelleButtonObscured <= 0 and self.isTeleportAllowed(): + if self.catalogNotify == ToontownGlobals.NewItems or self.mailboxNotify == ToontownGlobals.NewItems or self.simpleMailNotify == ToontownGlobals.NewItems or self.inviteMailNotify == ToontownGlobals.NewItems or self.awardNotify == ToontownGlobals.NewItems: + showClarabelle = 1 + for quest in self.quests: + if quest[0] in Quests.PreClarabelleQuestIds and self.mailboxNotify != ToontownGlobals.NewItems and self.awardNotify != ToontownGlobals.NewItems: + showClarabelle = 0 + + if base.cr.playGame.getPlace().getState() == 'stickerBook': + showClarabelle = 0 + if showClarabelle: + newItemsInMailbox = self.mailboxNotify == ToontownGlobals.NewItems or self.awardNotify == ToontownGlobals.NewItems + self.showClarabelleGui(newItemsInMailbox) + clarabelleHidden = 0 + if clarabelleHidden: + if self.__catalogNotifyDialog: + self.__catalogNotifyDialog.cleanup() + self.__catalogNotifyDialog = None + else: + self.newCatalogNotify() + if self.moveFurnitureButtonObscured: + if self.__furnitureGui: + self.__furnitureGui.hide() + else: + if self.furnitureManager != None and self.furnitureDirector == self.doId: + self.loadFurnitureGui() + self.__furnitureGui.setPos(0.155, -0.6, -1.045) + self.__furnitureGui.setScale(0.06) + elif self.cr.furnitureManager != None: + self.showFurnitureGui() + if self.__lerpFurnitureButton: + self.__lerpFurnitureButton.finish() + self.__lerpFurnitureButton = self.__furnitureGui.posHprScaleInterval(1.0, pos=Point3(0.115, 0.0, -0.66), hpr=Vec3(0.0, 0.0, 0.0), scale=Vec3(0.04, 0.04, 0.04), blendType='easeInOut', name='lerpFurnitureButton') + self.__lerpFurnitureButton.start() + if hasattr(self, 'inEstate') and self.inEstate: + self.loadGardeningGui() + self.hideGardeningGui() + else: + self.hideGardeningGui() + return + + def setGhostMode(self, flag): + if flag == 2: + self.seeGhosts = 1 + DistributedToon.DistributedToon.setGhostMode(self, flag) + + def newCatalogNotify(self): + if not self.gotCatalogNotify: + return + if not self.friendsListButtonActive or self.friendsListButtonObscured > 0: + return + self.gotCatalogNotify = 0 + currentWeek = self.catalogScheduleCurrentWeek - 1 + if currentWeek < 57: + seriesNumber = currentWeek / ToontownGlobals.CatalogNumWeeksPerSeries + 1 + weekNumber = currentWeek % ToontownGlobals.CatalogNumWeeksPerSeries + 1 + elif currentWeek < 65: + seriesNumber = 6 + weekNumber = currentWeek - 56 + else: + seriesNumber = currentWeek / ToontownGlobals.CatalogNumWeeksPerSeries + 2 + weekNumber = currentWeek % ToontownGlobals.CatalogNumWeeksPerSeries + 1 + message = None + if self.mailboxNotify == ToontownGlobals.NoItems: + if self.catalogNotify == ToontownGlobals.NewItems: + if self.catalogScheduleCurrentWeek == 1: + message = (TTLocalizer.CatalogNotifyFirstCatalog, TTLocalizer.CatalogNotifyInstructions) + else: + message = (TTLocalizer.CatalogNotifyNewCatalog % weekNumber,) + elif self.mailboxNotify == ToontownGlobals.NewItems: + if self.catalogNotify == ToontownGlobals.NewItems: + message = (TTLocalizer.CatalogNotifyNewCatalogNewDelivery % weekNumber,) + else: + message = (TTLocalizer.CatalogNotifyNewDelivery,) + elif self.mailboxNotify == ToontownGlobals.OldItems: + if self.catalogNotify == ToontownGlobals.NewItems: + message = (TTLocalizer.CatalogNotifyNewCatalogOldDelivery % weekNumber,) + else: + message = (TTLocalizer.CatalogNotifyOldDelivery,) + if self.awardNotify == ToontownGlobals.NoItems: + pass + elif self.awardNotify == ToontownGlobals.NewItems: + oldStr = '' + if message: + oldStr = message[0] + ' ' + oldStr += TTLocalizer.AwardNotifyNewItems + message = (oldStr,) + elif self.awardNotify == ToontownGlobals.OldItems: + oldStr = '' + if message: + oldStr = message[0] + ' ' + oldStr += TTLocalizer.AwardNotifyOldItems + message = (oldStr,) + if self.simpleMailNotify == ToontownGlobals.NewItems or self.inviteMailNotify == ToontownGlobals.NewItems: + oldStr = '' + if message: + oldStr = message[0] + ' ' + oldStr += TTLocalizer.MailNotifyNewItems + message = (oldStr,) + if message == None: + return + if self.__catalogNotifyDialog: + self.__catalogNotifyDialog.cleanup() + self.__catalogNotifyDialog = CatalogNotifyDialog.CatalogNotifyDialog(message) + base.playSfx(self.soundPhoneRing) + return + + def allowHardLand(self): + retval = LocalAvatar.LocalAvatar.allowHardLand(self) + return retval and not self.isDisguised + + def setShovelGuiLevel(self, level = 0): + pass + + def setWateringCanGuiLevel(self, level = 0): + pass + + def loadGardeningGui(self): + if self.__gardeningGui: + return + gardenGuiCard = loader.loadModel('phase_5.5/models/gui/planting_gui') + self.__gardeningGui = DirectFrame(relief=None, parent=base.a2dTopLeft, geom=gardenGuiCard, geom_color=GlobalDialogColor, geom_scale=(0.17, 1.0, 0.3), pos=(0.1335, 0.0, -0.50), scale=1.0) + self.__gardeningGui.setName('gardeningFrame') + self.__gardeningGuiFake = DirectFrame(relief=None, parent=base.a2dTopLeft, geom=None, geom_color=GlobalDialogColor, geom_scale=(0.17, 1.0, 0.3), pos=(0.1335, 0.0, -0.50), scale=1.0) + self.__gardeningGuiFake.setName('gardeningFrameFake') + iconScale = 1 + iconColorWhite = Vec4(1.0, 1.0, 1.0, 1.0) + iconColorGrey = Vec4(0.7, 0.7, 0.7, 1.0) + iconColorBrown = Vec4(0.7, 0.4, 0.3, 1.0) + iconColorBlue = Vec4(0.2, 0.3, 1.0, 1.0) + shovelCardP = loader.loadModel('phase_5.5/models/gui/planting_but_shovel_P') + shovelCardY = loader.loadModel('phase_5.5/models/gui/planting_but_shovel_Y') + wateringCanCardP = loader.loadModel('phase_5.5/models/gui/planting_but_can_P') + wateringCanCardY = loader.loadModel('phase_5.5/models/gui/planting_but_can_Y') + backCard = loader.loadModel('phase_5.5/models/gui/planting_gui') + iconImage = None + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/fish') + buttonText = TTLocalizer.GardeningPlant + self.shovelText = ('', + '', + buttonText, + '') + self.__shovelButtonFake = DirectLabel(parent=self.__gardeningGuiFake, relief=None, text=self.shovelText, text_align=TextNode.ALeft, text_pos=(0.0, -0.0), text_scale=0.07, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image_scale=(0.18, 1.0, 0.36), geom=None, geom_scale=iconScale, geom_color=iconColorWhite, pos=(0.15, 0, 0.2), scale=0.775) + self.shovelButtonFake = self.__shovelButtonFake + self.shovelText = ('', + '', + buttonText, + '') + self.__shovelButton = DirectButton(parent=self.__gardeningGui, relief=None, text=self.shovelText, text_align=TextNode.ACenter, text_pos=(0.0, -0.0), text_scale=0.1, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image=(shovelCardP, + shovelCardY, + shovelCardY, + shovelCardY), image_scale=(0.18, 1.0, 0.36), geom=None, geom_scale=iconScale, geom_color=iconColorWhite, pos=(0, 0, 0.2), scale=0.775, command=self.__shovelButtonClicked) + self.shovelButton = self.__shovelButton + iconGeom = iconModels.find('**/teleportIcon') + buttonText = TTLocalizer.GardeningWater + self.waterText = (buttonText, + buttonText, + buttonText, + '') + self.__wateringCanButtonFake = DirectLabel(parent=self.__gardeningGuiFake, relief=None, text=self.waterText, text_align=TextNode.ALeft, text_pos=(0.0, -0.0), text_scale=0.07, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image_scale=(0.18, 1.0, 0.36), geom=None, geom_scale=iconScale, geom_color=iconColorWhite, pos=(0.15, 0, 0.01), scale=0.775) + self.wateringCanButtonFake = self.__wateringCanButtonFake + self.__wateringCanButton = DirectButton(parent=self.__gardeningGui, relief=None, text=self.waterText, text_align=TextNode.ACenter, text_pos=(0.0, -0.0), text_scale=0.1, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image=(wateringCanCardP, + wateringCanCardY, + wateringCanCardY, + wateringCanCardY), image_scale=(0.18, 1.0, 0.36), geom=None, geom_scale=iconScale, geom_color=iconColorWhite, pos=(0, 0, 0.01), scale=0.775, command=self.__wateringCanButtonClicked) + self.wateringCanButton = self.__wateringCanButton + self.basketText = '%s / %s' % (self.numFlowers, self.maxFlowerBasket) + self.basketButton = DirectLabel(parent=self.__gardeningGui, relief=None, text=self.basketText, text_align=TextNode.ALeft, text_pos=(0.82, -1.4), text_scale=0.2, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image=None, image_scale=iconScale, geom=None, geom_scale=iconScale, geom_color=iconColorWhite, pos=(-0.34, 0, 0.16), scale=0.3, textMayChange=1) + if hasattr(self, 'shovel'): + self.setShovelGuiLevel(self.shovel) + if hasattr(self, 'wateringCan'): + self.setWateringCanGuiLevel(self.wateringCan) + self.__shovelButton.hide() + self.__wateringCanButton.hide() + self.__shovelButtonFake.hide() + self.__wateringCanButtonFake.hide() + return + + def changeButtonText(self, button, text): + button['text'] = text + + def resetWaterText(self): + self.wateringCanButton['text'] = self.waterText + + def resetShovelText(self): + self.shovelButton['text'] = self.holdShovelText + + def showGardeningGui(self): + self.loadGardeningGui() + self.__gardeningGui.show() + base.setCellsAvailable([base.leftCells[2]], 0) + + def hideGardeningGui(self): + if self.__gardeningGui: + self.__gardeningGui.hide() + base.setCellsAvailable([base.leftCells[2]], 1) + + def showShovelButton(self, add = 0): + if add: + self.shovelButtonActiveCount += add + else: + self.showingShovel = 1 + self.notify.debug('showing shovel %s' % self.shovelButtonActiveCount) + self.__gardeningGui.show() + self.__shovelButton.show() + + def hideShovelButton(self, deduct = 0): + self.shovelButtonActiveCount -= deduct + if deduct == 0: + self.showingShovel = 0 + if self.shovelButtonActiveCount < 1: + self.shovelButtonActiveCount = 0 + if self.showingShovel == 0: + self.__shovelButton.hide() + self.handleAllGardeningButtonsHidden() + self.notify.debug('hiding shovel %s' % self.shovelButtonActiveCount) + + def showWateringCanButton(self, add = 0): + if add: + self.wateringCanButtonActiveCount += add + else: + self.showingWateringCan = 1 + self.__gardeningGui.show() + self.__wateringCanButton.show() + self.basketButton.show() + + def hideWateringCanButton(self, deduct = 0): + self.wateringCanButtonActiveCount -= deduct + if deduct == 0: + self.showingWateringCan = 0 + if self.wateringCanButtonActiveCount < 1: + wateringCanButtonActiveCount = 0 + if self.showingWateringCan == 0: + self.__wateringCanButton.hide() + self.handleAllGardeningButtonsHidden() + + def showWateringCanButtonFake(self, add = 0): + self.__wateringCanButtonFake.show() + + def hideWateringCanButtonFake(self, deduct = 0): + self.__wateringCanButtonFake.hide() + + def showShovelButtonFake(self, add = 0): + self.__shovelButtonFake.show() + + def hideShovelButtonFake(self, deduct = 0): + self.__shovelButtonFake.hide() + + def levelWater(self, change = 1): + if change < 0: + return + self.showWateringCanButtonFake(1) + if change < 1: + changeString = TTLocalizer.GardeningNoSkill + else: + changeString = '+%s %s' % (change, TTLocalizer.GardeningWaterSkill) + self.waterTrack = Sequence(Wait(0.0), Func(self.changeButtonText, self.wateringCanButtonFake, changeString), SoundInterval(globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'), node=self), Wait(1.0), Func(self.hideWateringCanButtonFake, 1)) + self.waterTrack.start() + + def levelShovel(self, change = 1): + if change < 1: + return + self.showShovelButtonFake(1) + if change < 1: + changeString = TTLocalizer.GardeningNoSkill + else: + changeString = '+%s %s' % (change, TTLocalizer.GardeningShovelSkill) + plant = base.cr.doId2do.get(self.shovelRelatedDoId) + if plant: + self.holdShovelText = plant.getShovelAction() + self.shovelTrack = Sequence(Wait(0.0), Func(self.changeButtonText, self.shovelButtonFake, changeString), SoundInterval(globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'), node=self), Wait(1.0), Func(self.hideShovelButtonFake, 1)) + self.shovelTrack.start() + + def setGuiConflict(self, con): + self.guiConflict = con + + def getGuiConflict(self, con): + return self.guiConflict + + def verboseState(self): + self.lastPlaceState = 'None' + taskMgr.add(self.__expressState, 'expressState', extraArgs=[]) + + def __expressState(self, task = None): + place = base.cr.playGame.getPlace() + if place: + state = place.fsm.getCurrentState() + if state.getName() != self.lastPlaceState: + print 'Place State Change From %s to %s' % (self.lastPlaceState, state.getName()) + self.lastPlaceState = state.getName() + return Task.cont + + def addShovelRelatedDoId(self, doId): + if hasattr(base.cr.playGame.getPlace(), 'detectedGardenPlotDone'): + place = base.cr.playGame.getPlace() + state = place.fsm.getCurrentState() + if state.getName() == 'stopped': + return + self.touchingPlantList.append(doId) + self.autoSetActivePlot() + + def removeShovelRelatedDoId(self, doId): + if doId in self.touchingPlantList: + self.touchingPlantList.remove(doId) + self.autoSetActivePlot() + + def autoSetActivePlot(self): + if self.guiConflict: + return + if len(self.touchingPlantList) > 0: + minDist = 10000 + minDistPlot = 0 + for plot in self.touchingPlantList: + plant = base.cr.doId2do.get(plot) + if plant: + if self.getDistance(plant) < minDist: + minDist = self.getDistance(plant) + minDistPlot = plot + else: + self.touchingPlantList.remove(plot) + + if len(self.touchingPlantList) == 0: + self.setActivePlot(None) + else: + self.setActivePlot(minDistPlot) + else: + self.setActivePlot(None) + return + + def setActivePlot(self, doId): + if not self.gardenStarted: + return + self.shovelRelatedDoId = doId + plant = base.cr.doId2do.get(doId) + if plant: + self.startStareAt(plant, Point3(0, 0, 1)) + self.__shovelButton['state'] = DGG.NORMAL + if not plant.canBePicked(): + self.hideShovelButton() + else: + self.showShovelButton() + self.setShovelAbility(TTLocalizer.GardeningPlant) + if plant.getShovelAction(): + self.setShovelAbility(plant.getShovelAction()) + if plant.getShovelAction() == TTLocalizer.GardeningPick: + if not plant.unlockPick(): + self.__shovelButton['state'] = DGG.DISABLED + self.setShovelAbility(TTLocalizer.GardeningFull) + self.notify.debug('self.shovelRelatedDoId = %d' % self.shovelRelatedDoId) + if plant.getShovelCommand(): + self.extraShovelCommand = plant.getShovelCommand() + self.__shovelButton['command'] = self.__shovelButtonClicked + if plant.canBeWatered(): + self.showWateringCanButton() + else: + self.hideWateringCanButton() + else: + self.stopStareAt() + self.shovelRelatedDoId = 0 + if self.__shovelButton: + self.__shovelButton['command'] = None + self.hideShovelButton() + self.hideWateringCanButton() + self.handleAllGardeningButtonsHidden() + if not self.inGardenAction: + if hasattr(base.cr.playGame.getPlace(), 'detectedGardenPlotDone'): + place = base.cr.playGame.getPlace() + if place: + place.detectedGardenPlotDone() + return + + def setPlantToWater(self, plantId): + if self.plantToWater == None: + self.plantToWater = plantId + self.notify.debug('setting plant to water %s' % plantId) + return + + def clearPlantToWater(self, plantId): + if not hasattr(self, 'secondaryPlant'): + self.secondaryWaterPlant = None + if self.plantToWater == plantId: + self.plantToWater = None + self.hideWateringCanButton() + return + + def hasPlant(self): + if self.plantToWater != None: + return 1 + else: + return 0 + return + + def handleAllGardeningButtonsHidden(self): + somethingVisible = False + if not self.__shovelButton.isHidden(): + somethingVisible = True + if not self.__wateringCanButton.isHidden(): + somethingVisible = True + if not somethingVisible: + self.hideGardeningGui() + + def setShovelAbility(self, ability): + self.shovelAbility = ability + if self.__shovelButton: + self.__shovelButton['text'] = ability + + def setFlowerBasket(self, speciesList, varietyList): + DistributedToon.DistributedToon.setFlowerBasket(self, speciesList, varietyList) + self.numFlowers = len(self.flowerBasket.flowerList) + self.maxFlowerBasket + if hasattr(self, 'basketButton'): + self.basketText = '%s / %s' % (self.numFlowers, self.maxFlowerBasket) + self.basketButton['text'] = self.basketText + + def setShovelSkill(self, skillLevel): + if hasattr(self, 'shovelSkill') and hasattr(self, 'shovelButton'): + if self.shovelSkill != None: + self.levelShovel(skillLevel - self.shovelSkill) + oldShovelSkill = self.shovelSkill + DistributedToon.DistributedToon.setShovelSkill(self, skillLevel) + if hasattr(self, 'shovel'): + oldShovelPower = GardenGlobals.getShovelPower(self.shovel, oldShovelSkill) + newShovelPower = GardenGlobals.getShovelPower(self.shovel, self.shovelSkill) + almostMaxedSkill = GardenGlobals.ShovelAttributes[GardenGlobals.MAX_SHOVELS - 1]['skillPts'] - 2 + if skillLevel >= GardenGlobals.ShovelAttributes[self.shovel]['skillPts']: + self.promoteShovel() + elif oldShovelSkill and oldShovelPower < newShovelPower: + self.promoteShovelSkill(self.shovel, self.shovelSkill) + elif oldShovelSkill == almostMaxedSkill and newShovelPower == GardenGlobals.getNumberOfShovelBoxes(): + self.promoteShovelSkill(self.shovel, self.shovelSkill) + return + + def setWateringCanSkill(self, skillLevel): + skillDelta = skillLevel - self.wateringCanSkill + if skillDelta or 1: + if hasattr(self, 'wateringCanSkill') and hasattr(self, 'wateringCanButton'): + if self.wateringCanSkill != None: + self.levelWater(skillDelta) + DistributedToon.DistributedToon.setWateringCanSkill(self, skillLevel) + if hasattr(self, 'wateringCan'): + if skillLevel >= GardenGlobals.WateringCanAttributes[self.wateringCan]['skillPts']: + self.promoteWateringCan() + return + + def unlockGardeningButtons(self, task = None): + if hasattr(self, '_LocalToon__shovelButton'): + try: + self.__shovelButton['state'] = DGG.NORMAL + except TypeError: + self.notify.warning('Could not unlock the shovel button- Type Error') + + if hasattr(self, '_LocalToon__wateringCanButton'): + try: + self.__wateringCanButton['state'] = DGG.NORMAL + except TypeError: + self.notify.warning('Could not unlock the watering can button - Type Error') + + taskMgr.remove('unlockGardenButtons') + return None + + def lockGardeningButtons(self, task = None): + if hasattr(self, '_LocalToon__shovelButton'): + try: + self.__shovelButton['state'] = DGG.DISABLED + except TypeError: + self.notify.warning('Could not lock the shovel button- Type Error') + + if hasattr(self, '_LocalToon__wateringCanButton'): + try: + self.__wateringCanButton['state'] = DGG.DISABLED + except TypeError: + self.notify.warning('Could not lock the watering can button - Type Error') + + self.accept('endPlantInteraction', self.__handleEndPlantInteraction) + return None + + def reactivateShovel(self, task = None): + if hasattr(self, '_LocalToon__shovelButton'): + self.__shovelButton['state'] = DGG.NORMAL + taskMgr.remove('reactShovel') + return None + + def reactivateWater(self, task = None): + if hasattr(self, '_LocalToon__wateringCanButton'): + self.__wateringCanButton['state'] = DGG.NORMAL + taskMgr.remove('reactWater') + return None + + def handleEndPlantInteraction(self, object = None, replacement = 0): + if not replacement: + self.setInGardenAction(None, object) + self.autoSetActivePlot() + return + + def __handleEndPlantInteraction(self, task = None): + self.setInGardenAction(None) + self.autoSetActivePlot() + return + + def promoteShovelSkill(self, shovelLevel, shovelSkill): + shovelName = GardenGlobals.ShovelAttributes[shovelLevel]['name'] + shovelBeans = GardenGlobals.getShovelPower(shovelLevel, shovelSkill) + oldShovelBeans = GardenGlobals.getShovelPower(shovelLevel, shovelSkill - 1) + doPartyBall = False + message = TTLocalizer.GardenShovelSkillLevelUp % {'shovel': shovelName, + 'oldbeans': oldShovelBeans, + 'newbeans': shovelBeans} + if shovelBeans == GardenGlobals.getNumberOfShovelBoxes(): + if shovelSkill == GardenGlobals.ShovelAttributes[shovelLevel]['skillPts'] - 1: + doPartyBall = True + message = TTLocalizer.GardenShovelSkillMaxed % {'shovel': shovelName, + 'oldbeans': oldShovelBeans, + 'newbeans': shovelBeans} + messagePos = Vec2(0, 0.2) + messageScale = 0.07 + image = loader.loadModel('phase_5.5/models/gui/planting_but_shovel_P') + imagePos = Vec3(0, 0, -0.13) + imageScale = Vec3(0.28, 0, 0.56) + if doPartyBall: + go = Fanfare.makeFanfareWithMessageImage(0, base.localAvatar, 1, message, Vec2(0, 0.2), 0.08, image, Vec3(0, 0, -0.1), Vec3(0.35, 0, 0.7), wordwrap=23) + Sequence(go[0], Func(go[1].show), LerpColorScaleInterval(go[1], duration=0.5, startColorScale=Vec4(1, 1, 1, 0), colorScale=Vec4(1, 1, 1, 1)), Wait(10), LerpColorScaleInterval(go[1], duration=0.5, startColorScale=Vec4(1, 1, 1, 1), colorScale=Vec4(1, 1, 1, 0)), Func(go[1].remove)).start() + else: + go = Fanfare.makePanel(base.localAvatar, 1) + Fanfare.makeMessageBox(go, message, messagePos, messageScale, wordwrap=24) + Fanfare.makeImageBox(go.itemFrame, image, imagePos, imageScale) + Sequence(Func(go.show), LerpColorScaleInterval(go, duration=0.5, startColorScale=Vec4(1, 1, 1, 0), colorScale=Vec4(1, 1, 1, 1)), Wait(10), LerpColorScaleInterval(go, duration=0.5, startColorScale=Vec4(1, 1, 1, 1), colorScale=Vec4(1, 1, 1, 0)), Func(go.remove)).start() + + def promoteShovel(self, shovelLevel = 0): + shovelName = GardenGlobals.ShovelAttributes[shovelLevel]['name'] + shovelBeans = GardenGlobals.getShovelPower(shovelLevel, 0) + message = TTLocalizer.GardenShovelLevelUp % {'shovel': shovelName, + 'oldbeans': shovelBeans - 1, + 'newbeans': shovelBeans} + messagePos = Vec2(0, 0.2) + messageScale = 0.07 + image = loader.loadModel('phase_5.5/models/gui/planting_but_shovel_P') + imagePos = Vec3(0, 0, -0.13) + imageScale = Vec3(0.28, 0, 0.56) + go = Fanfare.makePanel(base.localAvatar, 1) + Fanfare.makeMessageBox(go, message, messagePos, messageScale, wordwrap=24) + Fanfare.makeImageBox(go.itemFrame, image, imagePos, imageScale) + Sequence(Func(go.show), LerpColorScaleInterval(go, duration=0.5, startColorScale=Vec4(1, 1, 1, 0), colorScale=Vec4(1, 1, 1, 1)), Wait(10), LerpColorScaleInterval(go, duration=0.5, startColorScale=Vec4(1, 1, 1, 1), colorScale=Vec4(1, 1, 1, 0)), Func(go.remove)).start() + + def promoteWateringCan(self, wateringCanlevel = 0): + message = TTLocalizer.GardenWateringCanLevelUp + ' \n' + GardenGlobals.WateringCanAttributes[wateringCanlevel]['name'] + messagePos = Vec2(0, 0.2) + messageScale = 0.08 + image = loader.loadModel('phase_5.5/models/gui/planting_but_can_P') + imagePos = Vec3(0, 0, -0.1) + imageScale = Vec3(0.35, 0, 0.7) + if wateringCanlevel >= GardenGlobals.MAX_WATERING_CANS - 1: + go = Fanfare.makeFanfareWithMessageImage(0, base.localAvatar, 1, message, Vec2(0, 0.2), 0.08, image, Vec3(0, 0, -0.1), Vec3(0.35, 0, 0.7)) + Sequence(go[0], Func(go[1].show), LerpColorScaleInterval(go[1], duration=0.5, startColorScale=Vec4(1, 1, 1, 0), colorScale=Vec4(1, 1, 1, 1)), Wait(5), LerpColorScaleInterval(go[1], duration=0.5, startColorScale=Vec4(1, 1, 1, 1), colorScale=Vec4(1, 1, 1, 0)), Func(go[1].remove)).start() + else: + go = Fanfare.makePanel(base.localAvatar, 1) + Fanfare.makeMessageBox(go, message, messagePos, messageScale) + Fanfare.makeImageBox(go.itemFrame, image, imagePos, imageScale) + Sequence(Func(go.show), LerpColorScaleInterval(go, duration=0.5, startColorScale=Vec4(1, 1, 1, 0), colorScale=Vec4(1, 1, 1, 1)), Wait(5), LerpColorScaleInterval(go, duration=0.5, startColorScale=Vec4(1, 1, 1, 1), colorScale=Vec4(1, 1, 1, 0)), Func(go.remove)).start() + + def setInGardenAction(self, actionObject, fromObject = None): + if actionObject: + self.lockGardeningButtons() + elif fromObject: + self.unlockGardeningButtons() + else: + self.unlockGardeningButtons() + self.inGardenAction = actionObject + + def __wateringCanButtonClicked(self): + self.notify.debug('wateringCanButtonClicked') + if self.inGardenAction: + return + plant = base.cr.doId2do.get(self.shovelRelatedDoId) + if plant: + if hasattr(plant, 'handleWatering'): + plant.handleWatering() + messenger.send('wakeup') + + def __shovelButtonClicked(self): + if self.inGardenAction: + return + self.notify.debug('shovelButtonClicked') + messenger.send('wakeup') + thingId = self.shovelRelatedDoId + thing = base.cr.doId2do.get(thingId) + if hasattr(self, 'extraShovelCommand'): + self.extraShovelCommand() + self.setActivePlot(thingId) + + def setShovel(self, shovelId): + DistributedToon.DistributedToon.setShovel(self, shovelId) + if self.__gardeningGui: + self.setShovelGuiLevel(shovelId) + + def setWateringCan(self, wateringCanId): + DistributedToon.DistributedToon.setWateringCan(self, wateringCanId) + if self.__gardeningGui: + self.setWateringCanGuiLevel(wateringCanId) + + def setGardenStarted(self, bStarted): + self.gardenStarted = bStarted + if self.gardenStarted and not self.gardenPage and hasattr(self, 'book'): + self.loadGardenPages() + + def b_setAnimState(self, animName, animMultiplier = 1.0, callback = None, extraArgs = []): + if self.wantStatePrint: + print 'Local Toon Anim State %s' % animName + DistributedToon.DistributedToon.b_setAnimState(self, animName, animMultiplier, callback, extraArgs) + + def __handleSwimExitTeleport(self, requestStatus): + self.notify.info('closing shard...') + base.cr.gameFSM.request('closeShard', ['afkTimeout']) + + def addGolfPage(self): + if self.hasPlayedGolf(): + if hasattr(self, 'golfPage') and self.golfPage != None: + return + self.golfPage = GolfPage.GolfPage() + self.golfPage.setAvatar(self) + self.golfPage.load() + self.book.addPage(self.golfPage, pageName=TTLocalizer.GolfPageTitle) + return + + def addEventsPage(self): + if hasattr(self, 'eventsPage') and self.eventsPage != None: + return + self.eventsPage = EventsPage.EventsPage() + self.eventsPage.load() + self.book.addPage(self.eventsPage, pageName=TTLocalizer.EventsPageName) + return + + def setSpecialInventory(self, specialInventory): + DistributedToon.DistributedToon.setSpecialInventory(self, specialInventory) + self.inventory.updateTotalPropsText() + + def hasActiveBoardingGroup(self): + if hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty: + return localAvatar.boardingParty.hasActiveGroup(localAvatar.doId) + else: + return False + + def getZoneId(self): + return self._zoneId + + def setZoneId(self, value): + if value == -1: + self.notify.error('zoneId should not be set to -1, tell Redmond') + self._zoneId = value + + zoneId = property(getZoneId, setZoneId) + + def systemWarning(self, warningText = 'Acknowledge this system message.'): + self.createSystemMsgAckGui() + self.systemMsgAckGui['text'] = warningText + self.systemMsgAckGui.show() + + def createSystemMsgAckGui(self): + if self.systemMsgAckGui == None or self.systemMsgAckGui.isEmpty(): + message = 'o' * 100 + self.systemMsgAckGui = TTDialog.TTGlobalDialog(doneEvent=self.systemMsgAckGuiDoneEvent, message=message, style=TTDialog.Acknowledge) + self.systemMsgAckGui.hide() + return + + def hideSystemMsgAckGui(self): + if self.systemMsgAckGui != None and not self.systemMsgAckGui.isEmpty(): + self.systemMsgAckGui.hide() + return + + def setSleepAutoReply(self, fromId): + av = base.cr.identifyAvatar(fromId) + if isinstance(av, (DistributedToon.DistributedToon, FriendHandle)): + base.localAvatar.setSystemMessage(0, TTLocalizer.SleepAutoReply % av.getName(), WTToontownBoardingGroup) + elif av: + self.notify.warning('setSleepAutoReply from non-toon %s' % fromId) + + def cheatCogdoMazeGame(self, kindOfCheat = 0): + if base.config.GetBool('allow-cogdo-maze-suit-hit-cheat'): + maze = base.cr.doFind('DistCogdoMazeGame') + if maze: + if kindOfCheat == 0: + for suitNum in maze.game.suitsById.keys(): + suit = maze.game.suitsById[suitNum] + maze.sendUpdate('requestSuitHitByGag', [suit.type, suitNum]) + + elif kindOfCheat == 1: + for joke in maze.game.pickups: + maze.sendUpdate('requestPickUp', [joke.serialNum]) + + else: + self.sendUpdate('logSuspiciousEvent', ['cheatCogdoMazeGame']) + + def doTeleportResponse(self, fromAvatar, toAvatar, avId, available, shardId, hoodId, zoneId, sendToId): + self.d_teleportResponse(avId, available, shardId, hoodId, zoneId, sendToId) + + def startQuestMap(self): + if self.questMap: + self.questMap.start() + + def stopQuestMap(self): + if self.questMap: + self.questMap.stop() + + def getPetId(self): + return self.petId + + def hasPet(self): + return self.petId != 0 + + def getPetDNA(self): + if self.hasPet(): + pet = base.cr.identifyFriend(self.petId) + return pet.style if pet else None + return None + + def setPetId(self, petId): + self.petId = petId + if self.isLocal(): + base.cr.addPetToFriendsMap() + + def startAprilToonsControls(self): + self.controlManager.currentControls.setGravity(ToontownGlobals.GravityValue * 0.75) + + def stopAprilToonsControls(self): + self.controlManager.currentControls.setGravity(ToontownGlobals.GravityValue * 2.0) diff --git a/toontown/toon/Motion.py b/toontown/toon/Motion.py new file mode 100755 index 00000000..32f2bfe9 --- /dev/null +++ b/toontown/toon/Motion.py @@ -0,0 +1,110 @@ +from direct.fsm import StateData +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import TTEmote +from otp.avatar import Emote + +class Motion(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('Motion') + + def __init__(self, toon): + self.lt = toon + self.doneEvent = 'motionDone' + StateData.StateData.__init__(self, self.doneEvent) + self.fsm = ClassicFSM.ClassicFSM('Motion', [State.State('off', self.enterOff, self.exitOff), + State.State('neutral', self.enterNeutral, self.exitNeutral), + State.State('walk', self.enterWalk, self.exitWalk), + State.State('run', self.enterRun, self.exitRun), + State.State('sad-neutral', self.enterSadNeutral, self.exitSadNeutral), + State.State('sad-walk', self.enterSadWalk, self.exitSadWalk), + State.State('catch-neutral', self.enterCatchNeutral, self.exitCatchNeutral), + State.State('catch-run', self.enterCatchRun, self.exitCatchRun), + State.State('catch-eatneutral', self.enterCatchEatNeutral, self.exitCatchEatNeutral), + State.State('catch-eatnrun', self.enterCatchEatNRun, self.exitCatchEatNRun)], 'off', 'off') + self.fsm.enterInitialState() + + def delete(self): + del self.fsm + + def load(self): + pass + + def unload(self): + pass + + def enter(self): + self.notify.debug('enter') + + def exit(self): + self.fsm.requestFinalState() + + def enterOff(self, rate = 0): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterNeutral(self, rate = 0): + self.notify.debug('enterNeutral') + + def exitNeutral(self): + self.notify.debug('exitNeutral') + + def enterWalk(self, rate = 0): + self.notify.debug('enterWalk') + Emote.globalEmote.disableBody(self.lt, 'enterWalk') + + def exitWalk(self): + self.notify.debug('exitWalk') + Emote.globalEmote.releaseBody(self.lt, 'exitWalk') + + def enterRun(self, rate = 0): + self.notify.debug('enterRun') + Emote.globalEmote.disableBody(self.lt, 'enterRun') + + def exitRun(self): + self.notify.debug('exitRun') + Emote.globalEmote.releaseBody(self.lt, 'exitRun') + + def enterSadNeutral(self, rate = 0): + self.notify.debug('enterSadNeutral') + + def exitSadNeutral(self): + self.notify.debug('exitSadNeutral') + + def enterSadWalk(self, rate = 0): + self.notify.debug('enterSadWalk') + + def exitSadWalk(self): + pass + + def enterCatchNeutral(self, rate = 0): + self.notify.debug('enterCatchNeutral') + + def exitCatchNeutral(self): + self.notify.debug('exitCatchNeutral') + + def enterCatchRun(self, rate = 0): + self.notify.debug('enterCatchRun') + + def exitCatchRun(self): + self.notify.debug('exitCatchRun') + + def enterCatchEatNeutral(self, rate = 0): + self.notify.debug('enterCatchEatNeutral') + + def exitCatchEatNeutral(self): + self.notify.debug('exitCatchEatNeutral') + + def enterCatchEatNRun(self, rate = 0): + self.notify.debug('enterCatchEatNRun') + + def exitCatchEatNRun(self): + self.notify.debug('exitCatchEatNRun') + + def setState(self, anim, rate): + toon = self.lt + if toon.playingAnim != anim: + self.fsm.request(anim, [rate]) diff --git a/toontown/toon/NPCForceAcknowledge.py b/toontown/toon/NPCForceAcknowledge.py new file mode 100755 index 00000000..3d5e8f81 --- /dev/null +++ b/toontown/toon/NPCForceAcknowledge.py @@ -0,0 +1,52 @@ +from panda3d.core import * +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from direct.gui import DirectLabel +from toontown.quest import Quests + +class NPCForceAcknowledge: + + def __init__(self, doneEvent): + self.doneEvent = doneEvent + self.dialog = None + return + + def enter(self): + doneStatus = {} + questHistory = base.localAvatar.getQuestHistory() + imgScale = 0.5 + if questHistory != [] and questHistory != [1000] and questHistory != [101, 110]: + doneStatus['mode'] = 'complete' + messenger.send(self.doneEvent, [doneStatus]) + elif len(base.localAvatar.quests) > 1 or len(base.localAvatar.quests) == 0: + doneStatus['mode'] = 'complete' + messenger.send(self.doneEvent, [doneStatus]) + elif base.localAvatar.quests[0][0] != Quests.TROLLEY_QUEST_ID: + doneStatus['mode'] = 'complete' + messenger.send(self.doneEvent, [doneStatus]) + else: + base.localAvatar.b_setAnimState('neutral', 1) + doneStatus['mode'] = 'incomplete' + self.doneStatus = doneStatus + imageModel = loader.loadModel('phase_4/models/gui/tfa_images') + if Quests.avatarHasTrolleyQuest(base.localAvatar): + if base.localAvatar.quests[0][4] != 0: + imgNodePath = imageModel.find('**/hq-dialog-image') + imgPos = (0, 0, -0.02) + msg = TTLocalizer.NPCForceAcknowledgeMessage2 + else: + imgNodePath = imageModel.find('**/trolley-dialog-image') + imgPos = (0, 0, 0.04) + msg = TTLocalizer.NPCForceAcknowledgeMessage + self.dialog = TTDialog.TTDialog(text=msg, command=self.handleOk, style=TTDialog.Acknowledge) + imgLabel = DirectLabel.DirectLabel(parent=self.dialog, relief=None, pos=imgPos, scale=TTLocalizer.NPCFimgLabel, image=imgNodePath, image_scale=imgScale) + return + + def exit(self): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + return + + def handleOk(self, value): + messenger.send(self.doneEvent, [self.doneStatus]) diff --git a/toontown/toon/NPCFriendPanel.py b/toontown/toon/NPCFriendPanel.py new file mode 100755 index 00000000..a98ca2eb --- /dev/null +++ b/toontown/toon/NPCFriendPanel.py @@ -0,0 +1,218 @@ +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer, ToontownGlobals, ToontownBattleGlobals +import NPCToons, ToonDNA, ToonHead + +def createNPCToonHead(NPCID, dimension = 0.5): + NPCInfo = NPCToons.NPCToonDict[NPCID] + dnaList = NPCInfo[2] + gender = NPCInfo[3] + if dnaList == 'r': + dnaList = NPCToons.getRandomDNA(NPCID, gender) + dna = ToonDNA.ToonDNA() + dna.newToonFromProperties(*dnaList) + head = ToonHead.ToonHead() + head.setupHead(dna, forGui=1) + fitGeometry(head, fFlip=1, dimension=dimension) + return head + +def fitGeometry(geom, fFlip = 0, dimension = 0.5): + p1 = Point3() + p2 = Point3() + geom.calcTightBounds(p1, p2) + if fFlip: + t = p1[0] + p1.setX(-p2[0]) + p2.setX(-t) + d = p2 - p1 + biggest = max(d[0], d[2]) + s = dimension / biggest + mid = (p1 + d / 2.0) * s + geomXform = hidden.attachNewNode('geomXform') + for child in geom.getChildren(): + child.reparentTo(geomXform) + + geomXform.setPosHprScale(-mid[0], -mid[1] + 1, -mid[2], 180, 0, 0, s, s, s) + geomXform.reparentTo(geom) + +class NPCFriendPanel(DirectFrame): + + def __init__(self, parent = aspect2d, callable = False, **kw): + optiondefs = (('relief', None, None), ('doneEvent', None, None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent=parent) + self.callable = callable + self.cardList = [] + self.friendDict = {} + self.pos = 0 + self.updateLayout() + self.initialiseoptions(NPCFriendPanel) + + gui = loader.loadModel('phase_3.5/models/gui/battle_gui') + buttonImage = (gui.find('**/PckMn_BackBtn'), gui.find('**/PckMn_BackBtn_Dn'), gui.find('**/PckMn_BackBtn_Rlvr')) + self.leftArrow = DirectButton(parent=self, relief=None, image=buttonImage, pos=(-6.8, 0, 0), scale=3.8, command=self.addPageIndex, extraArgs=[-1]) + self.rightArrow = DirectButton(parent=self, relief=None, image=buttonImage, pos=(6.8, 0, 0), scale=-3.8, command=self.addPageIndex, extraArgs=[1]) + gui.removeNode() + + self.leftArrow.hide() + + def addPageIndex(self, index): + self.pos += (16 * index) + + if self.pos > 0: + self.leftArrow.show() + else: + self.leftArrow.hide() + + self.update() + + def update(self): + friendList = sorted(self.friendDict.keys(), reverse=True, key=lambda id: NPCToons.getNPCTrackLevelHpRarity(id)[3]) + cardNum = 0 + + for i in xrange(self.pos, self.pos + 16): + card = self.cardList[cardNum] + + if len(friendList) > i: + npcId = friendList[i] + card.update(npcId, self.friendDict[npcId], self.callable) + self.rightArrow.show() + else: + card.update(None, 0, self.callable) + self.rightArrow.hide() + + cardNum += 1 + + def updateLayout(self): + for card in self.cardList: + card.destroy() + + self.cardList = [] + xOffset = -5.2 + yOffset = 3.5 + count = 0 + + for i in xrange(16): + card = NPCFriendCard(parent=self, doneEvent=self['doneEvent']) + self.cardList.append(card) + card.setPos(xOffset, 1, yOffset) + card.setScale(0.75) + xOffset += 3.5 + count += 1 + + if count % 4 == 0: + xOffset = -5.25 + yOffset += -2.45 + + def setFriends(self, friends): + self.friendDict = friends + +class NPCFriendCard(DirectFrame): + normalTextColor = (0.3, 0.25, 0.2, 1) + maxRarity = 5 + sosTracks = ToontownBattleGlobals.Tracks + ToontownBattleGlobals.NPCTracks + + def __init__(self, parent = aspect2dp, **kw): + optiondefs = (('NPCID', 'Uninitialized', None), ('relief', None, None), ('doneEvent', None, None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent=parent) + self.initialiseoptions(NPCFriendCard) + cardModel = loader.loadModel('phase_3.5/models/gui/playingCard') + self.front = DirectFrame(parent=self, relief=None, image=cardModel.find('**/card_front')) + self.front.hide() + self.back = DirectFrame(parent=self, relief=None, image=cardModel.find('**/card_back')) + self.sosTypeInfo = DirectLabel(parent=self.front, relief=None, text='', text_font=ToontownGlobals.getMinnieFont(), text_fg=self.normalTextColor, text_scale=0.35, text_align=TextNode.ACenter, text_wordwrap=16.0, pos=(0, 0, 1.15)) + self.NPCHead = None + self.NPCName = DirectLabel(parent=self.front, relief=None, text='', text_fg=self.normalTextColor, text_scale=0.4, text_align=TextNode.ACenter, text_wordwrap=8.0, pos=(0, 0, -0.45)) + buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + upButton = buttonModels.find('**/InventoryButtonUp') + downButton = buttonModels.find('**/InventoryButtonDown') + rolloverButton = buttonModels.find('**/InventoryButtonRollover') + self.sosCallButton = DirectButton(parent=self.front, relief=None, text=TTLocalizer.NPCCallButtonLabel, text_fg=self.normalTextColor, text_scale=0.28, text_align=TextNode.ACenter, image=(upButton, + downButton, + rolloverButton, + upButton), image_color=(1.0, 0.2, 0.2, 1), image0_color=Vec4(1.0, 0.4, 0.4, 1), image3_color=Vec4(1.0, 0.4, 0.4, 0.4), image_scale=(4.4, 1, 3.6), image_pos=Vec3(0, 0, 0.08), pos=(-1.15, 0, -0.9), scale=1.25, command=self.__chooseNPCFriend) + self.sosCallButton.hide() + self.sosCountInfo = DirectLabel(parent=self.front, relief=None, text='', text_fg=self.normalTextColor, text_scale=0.75, text_align=TextNode.ALeft, textMayChange=1, pos=(0.0, 0, -1.0)) + star = loader.loadModel('phase_3.5/models/gui/name_star') + self.rarityStars = [] + + for i in xrange(self.maxRarity): + label = DirectLabel(parent=self.front, relief=None, image=star, image_scale=0.2, image_color=Vec4(0.502, 0.251, 0.251, 1.0), pos=(1.1 - i * 0.24, 0, -1.2)) + label.hide() + self.rarityStars.append(label) + + def __chooseNPCFriend(self): + if self['NPCID'] and self['doneEvent']: + doneStatus = {} + doneStatus['mode'] = 'NPCFriend' + doneStatus['friend'] = self['NPCID'] + messenger.send(self['doneEvent'], [doneStatus]) + + def destroy(self): + if self.NPCHead: + self.NPCHead.detachNode() + self.NPCHead.delete() + + DirectFrame.destroy(self) + + def update(self, NPCID, count = 0, fCallable = 0): + oldNPCID = self['NPCID'] + self['NPCID'] = NPCID + + if NPCID != oldNPCID: + if self.NPCHead: + self.NPCHead.detachNode() + self.NPCHead.delete() + if NPCID is None: + self.showBack() + return + + self.front.show() + self.back.hide() + self.NPCName['text'] = TTLocalizer.NPCToonNames[NPCID] + self.NPCHead = createNPCToonHead(NPCID, dimension=1.2) + self.NPCHead.reparentTo(self.front) + self.NPCHead.setZ(0.45) + track, level, hp, rarity = NPCToons.getNPCTrackLevelHpRarity(NPCID) + sosText = self.sosTracks[track] + + if track == ToontownBattleGlobals.NPC_RESTOCK_GAGS: + if level == -1: + sosText += ' All' + else: + sosText += ' ' + self.sosTracks[level] + sosText = TextEncoder.upper(sosText) + self.sosTypeInfo['text'] = sosText + + for i in xrange(self.maxRarity): + if i < rarity: + self.rarityStars[i].show() + else: + self.rarityStars[i].hide() + + if fCallable: + self.sosCallButton.show() + self.sosCountInfo.setPos(-0.4, 0, -0.9) + self.sosCountInfo['text_scale'] = 0.4 + self.sosCountInfo['text_align'] = TextNode.ALeft + else: + self.sosCallButton.hide() + self.sosCountInfo.setPos(0, 0, -0.9) + self.sosCountInfo['text_scale'] = 0.5 + self.sosCountInfo['text_align'] = TextNode.ACenter + if count > 0: + countText = TTLocalizer.NPCFriendPanelRemaining % count + self.sosCallButton['state'] = DGG.NORMAL + else: + countText = TTLocalizer.NPCFriendUnavailable + self.sosCallButton['state'] = DGG.DISABLED + self.sosCountInfo['text'] = countText + return + + def showFront(self): + self.front.show() + self.back.hide() + + def showBack(self): + self.front.hide() + self.back.show() diff --git a/toontown/toon/NPCToons.py b/toontown/toon/NPCToons.py new file mode 100755 index 00000000..8caffa5d --- /dev/null +++ b/toontown/toon/NPCToons.py @@ -0,0 +1,1002 @@ +from panda3d.core import * +from otp.nametag.NametagGroup import * +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer, ToontownBattleGlobals, ToontownGlobals +import ToonDNA + +QUEST_MOVIE_CLEAR = 0 +QUEST_MOVIE_REJECT = 1 +QUEST_MOVIE_COMPLETE = 2 +QUEST_MOVIE_INCOMPLETE = 3 +QUEST_MOVIE_ASSIGN = 4 +QUEST_MOVIE_BUSY = 5 +QUEST_MOVIE_QUEST_CHOICE = 6 +QUEST_MOVIE_QUEST_CHOICE_CANCEL = 7 +QUEST_MOVIE_TRACK_CHOICE = 8 +QUEST_MOVIE_TRACK_CHOICE_CANCEL = 9 +QUEST_MOVIE_TIMEOUT = 10 +QUEST_MOVIE_TIER_NOT_DONE = 11 +PURCHASE_MOVIE_CLEAR = 0 +PURCHASE_MOVIE_START = 1 +PURCHASE_MOVIE_START_BROWSE = 9 +PURCHASE_MOVIE_START_BROWSE_JBS = 11 +PURCHASE_MOVIE_COMPLETE = 2 +PURCHASE_MOVIE_NO_MONEY = 3 +PURCHASE_MOVIE_TIMEOUT = 8 +PURCHASE_MOVIE_START_NOROOM = 10 +SELL_MOVIE_CLEAR = 0 +SELL_MOVIE_START = 1 +SELL_MOVIE_COMPLETE = 2 +SELL_MOVIE_NOFISH = 3 +SELL_MOVIE_TROPHY = 4 +SELL_MOVIE_TIMEOUT = 8 +SELL_MOVIE_PETRETURNED = 9 +SELL_MOVIE_PETADOPTED = 10 +SELL_MOVIE_PETCANCELED = 11 +SELL_MOVIE_CHEATER = 15 +PARTY_MOVIE_CLEAR = 0 +PARTY_MOVIE_START = 1 +PARTY_MOVIE_COMPLETE = 2 +PARTY_MOVIE_ALREADYHOSTING = 3 +PARTY_MOVIE_MAYBENEXTTIME = 4 +PARTY_MOVIE_MINCOST = 6 +PARTY_MOVIE_TIMEOUT = 7 +BLOCKER_MOVIE_CLEAR = 0 +BLOCKER_MOVIE_START = 1 +BLOCKER_MOVIE_TIMEOUT = 8 +NPC_REGULAR = 0 +NPC_CLERK = 1 +NPC_TAILOR = 2 +NPC_HQ = 3 +NPC_BLOCKER = 4 +NPC_FISHERMAN = 5 +NPC_PETCLERK = 6 +NPC_KARTCLERK = 7 +NPC_PARTYPERSON = 8 +NPC_SPECIALQUESTGIVER = 9 +NPC_FLIPPYTOONHALL = 10 +NPC_SCIENTIST = 11 +NPC_GLOVE = 12 +NPC_LAFF_RESTOCK = 13 +QUEST_COUNTDOWN_TIME = 120 +CLERK_COUNTDOWN_TIME = 120 +TAILOR_COUNTDOWN_TIME = 300 + +def getRandomDNA(seed, gender): + randomDNA = ToonDNA.ToonDNA() + randomDNA.newToonRandom(seed, gender, 1) + return randomDNA.asTuple() + +def createNPC(air, npcId, desc, zoneId, posIndex = 0, questCallback = None): + import DistributedNPCToonAI + import DistributedNPCClerkAI + import DistributedNPCTailorAI + import DistributedNPCBlockerAI + import DistributedNPCFishermanAI + import DistributedNPCPetclerkAI + import DistributedNPCKartClerkAI + import DistributedNPCPartyPersonAI + import DistributedNPCSpecialQuestGiverAI + import DistributedNPCFlippyInToonHallAI + import DistributedNPCScientistAI + import DistributedNPCGloveAI + import DistributedNPCLaffRestockAI + canonicalZoneId, name, dnaType, gender, protected, type = desc + if type == NPC_REGULAR: + npc = DistributedNPCToonAI.DistributedNPCToonAI(air, npcId, questCallback=questCallback) + elif type == NPC_HQ: + npc = DistributedNPCToonAI.DistributedNPCToonAI(air, npcId, questCallback=questCallback, hq=1) + elif type == NPC_CLERK: + npc = DistributedNPCClerkAI.DistributedNPCClerkAI(air, npcId) + elif type == NPC_TAILOR: + npc = DistributedNPCTailorAI.DistributedNPCTailorAI(air, npcId) + elif type == NPC_BLOCKER: + npc = DistributedNPCBlockerAI.DistributedNPCBlockerAI(air, npcId) + elif type == NPC_FISHERMAN: + npc = DistributedNPCFishermanAI.DistributedNPCFishermanAI(air, npcId) + elif type == NPC_PETCLERK: + npc = DistributedNPCPetclerkAI.DistributedNPCPetclerkAI(air, npcId) + elif type == NPC_KARTCLERK: + npc = DistributedNPCKartClerkAI.DistributedNPCKartClerkAI(air, npcId) + elif type == NPC_PARTYPERSON: + npc = DistributedNPCPartyPersonAI.DistributedNPCPartyPersonAI(air, npcId) + elif type == NPC_SPECIALQUESTGIVER: + npc = DistributedNPCSpecialQuestGiverAI.DistributedNPCSpecialQuestGiverAI(air, npcId) + elif type == NPC_FLIPPYTOONHALL: + npc = DistributedNPCFlippyInToonHallAI.DistributedNPCFlippyInToonHallAI(air, npcId) + elif type == NPC_SCIENTIST: + npc = DistributedNPCScientistAI.DistributedNPCScientistAI(air, npcId) + elif type == NPC_GLOVE: + npc = DistributedNPCGloveAI.DistributedNPCGloveAI(air, npcId) + elif type == NPC_LAFF_RESTOCK: + npc = DistributedNPCLaffRestockAI.DistributedNPCLaffRestockAI(air, npcId) + else: + print 'Invalid NPC type: %s' % type + npc.setName(name) + dna = ToonDNA.ToonDNA() + if dnaType == 'r': + dnaList = getRandomDNA(npcId, gender) + else: + dnaList = dnaType + dna.newToonFromProperties(*dnaList) + npc.setDNAString(dna.makeNetString()) + npc.setHp(15) + npc.setMaxHp(15) + npc.setPositionIndex(posIndex) + npc.generateWithRequired(zoneId) + npc.d_setAnimState(npc.getStartAnimState(), 1.0) + return npc + +def createNpcsInZone(air, zoneId): + npcs = [] + npcIdList = sorted(zone2NpcDict.get(ZoneUtil.getCanonicalZoneId(zoneId), [])) + + for i, npcId in enumerate(npcIdList): + npcDesc = NPCToonDict.get(npcId) + + if npcDesc[5] == NPC_FISHERMAN and not air.wantFishing: + continue + elif npcDesc[5] == NPC_PARTYPERSON and not air.wantParties: + continue + + npcs.append(createNPC(air, npcId, npcDesc, zoneId, posIndex=i)) + + return npcs + +def createLocalNPC(npcId): + if npcId not in NPCToonDict: + return + import Toon + desc = NPCToonDict[npcId] + canonicalZoneId, name, dnaType, gender, protected, type = desc + npc = Toon.Toon() + npc.setName(name) + npc.setPickable(0) + npc.setPlayerType(NametagGroup.CCNonPlayer) + dna = ToonDNA.ToonDNA() + if dnaType == 'r': + dnaList = getRandomDNA(npcId, gender) + else: + dnaList = dnaType + dna.newToonFromProperties(*dnaList) + npc.setDNAString(dna.makeNetString()) + npc.animFSM.request('neutral') + return npc + +# Some buildings don't have NPCs, so we need to store their zone IDs here: +badBlocks = [ + 2606, 2602, 2708, 2705, 2704, 2701, 2803, 2804, 2809, 2805, 5607, 1707, + 5609, 3605, 3703 +] + +def isZoneProtected(zoneId): + if zoneId in badBlocks: + return 1 + + for npcId in zone2NpcDict.get(zoneId, []): + if NPCToonDict.get(npcId)[4]: + return 1 + + return 0 + +lnames = TTLocalizer.NPCToonNames +NPCToonDict = { + 20000: (-1, lnames[20000], ('dls', 'ms', 'm', 'm', 7, 0, 7, 7, 2, 6, 2, 6, 2, 16), 'm', 1, NPC_SPECIALQUESTGIVER), + 999: (-1, lnames[999], 'r', 'm', 1, NPC_TAILOR), + 1000: (-1, lnames[1000], 'r', 'm', 1, NPC_HQ), + 20001: (-1, lnames[20001], ('dss', 'ms', 'm', 'm', 17, 0, 17, 17, 3, 3, 3, 3, 7, 2), 'm', 1, NPC_BLOCKER), + 20002: (-1, TTLocalizer.TutorialHQOfficerName, ('dls', 'ms', 'm', 'm', 6, 0, 6, 6, 0, 10, 0, 10, 2, 9), 'm', 1, NPC_SPECIALQUESTGIVER), + 2002: (2514, lnames[2002], ('hss', 'ls', 'l', 'm', 4, 0, 4, 4, 0, 3, 0, 3, 1, 18), 'm', 1, NPC_REGULAR), + 2003: (2516, lnames[2003], ('cll', 'ms', 'l', 'm', 18, 0, 18, 18, 0, 4, 0, 4, 1, 15), 'm', 1, NPC_REGULAR), + 2004: (2521, lnames[2004], ('rll', 'md', 'm', 'f', 15, 0, 5, 7, 3, 5, 3, 5, 0, 3), 'f', 1, NPC_TAILOR), + 2005: (2518, lnames[2005], ('cls', 'ls', 'l', 'm', 4, 0, 4, 4, 0, 4, 0, 4, 1, 9), 'm', 1, NPC_REGULAR), + 2006: (2519, lnames[2006], ('dsl', 'ls', 'l', 'm', 18, 0, 18, 18, 1, 4, 1, 4, 1, 2), 'm', 1, NPC_CLERK), + 2011: (2519, lnames[2011], ('rll', 'ms', 'l', 'f', 2, 0, 2, 2, 1, 9, 1, 9, 23, 27), 'f', 1, NPC_CLERK), + 2007: (2520, lnames[2007], ('dss', 'ms', 'l', 'm', 10, 0, 10, 10, 1, 5, 1, 5, 1, 20), 'm', 1, NPC_HQ), + 2008: (2520, lnames[2008], ('fll', 'ss', 'l', 'm', 3, 0, 3, 3, 1, 5, 1, 5, 1, 17), 'm', 1, NPC_HQ), + 2009: (2520, lnames[2009], ('fsl', 'md', 'l', 'f', 18, 0, 18, 18, 1, 8, 1, 8, 11, 27), 'f', 1, NPC_HQ), + 2010: (2520, lnames[2010], ('fls', 'ls', 'l', 'f', 11, 0, 11, 11, 1, 8, 1, 8, 8, 4), 'f', 1, NPC_HQ), + 2012: (2000, lnames[2012], ('rss', 'ls', 'l', 'm', 17, 0, 17, 17, 1, 6, 1, 6, 1, 1), 'm', 1, NPC_FISHERMAN), + 2013: (2522, lnames[2013], ('rls', 'ms', 'l', 'm', 9, 0, 9, 9, 0, 7, 0, 7, 1, 19), 'm', 1, NPC_PETCLERK), + 2014: (2522, lnames[2014], ('mls', 'ms', 'm', 'f', 2, 0, 2, 2, 0, 12, 0, 12, 1, 0), 'f', 1, NPC_PETCLERK), + 2015: (2522, lnames[2015], ('hsl', 'ls', 'm', 'm', 17, 0, 17, 17, 0, 8, 0, 8, 1, 13), 'm', 1, NPC_PETCLERK), + 2016: (2000, lnames[2016], ('sls', 'ls', 'm', 'm', 10, 0, 9, 9, 0, 3, 0, 3, 0, 18), 'm', 1, NPC_PARTYPERSON), + 2017: (2000, lnames[2017], ('sss', 'ld', 'm', 'f', 10, 0, 9, 9, 0, 23, 0, 23, 0, 5), 'f', 1, NPC_PARTYPERSON), + 2018: (2513, lnames[2019], ('fll', 'ss', 's', 'm', 15, 0, 15, 15, 99, 27, 86, 27, 39, 27), 'm', 1, NPC_SCIENTIST), + 2019: (2513, lnames[2018], ('pls', 'ls', 'l', 'm', 9, 0, 9, 9, 98, 27, 86, 27, 38, 27), 'm', 1, NPC_SCIENTIST), + 2020: (2513, lnames[2020], ('hss', 'ms', 'm', 'm', 20, 0, 20, 20, 97, 27, 86, 27, 37, 27), 'm', 1, NPC_SCIENTIST), + 2021: (2000, lnames[2021], ('dss', 'ls', 's', 'm', 13, 0, 13, 13, 1, 6, 1, 6, 0, 18), 'm', 1, NPC_GLOVE), + 2101: (2601, lnames[2101], ('rll', 'ms', 'l', 'm', 15, 0, 15, 15, 0, 9, 0, 9, 0, 6), 'm', 1, NPC_REGULAR), + 2102: (2619, lnames[2102], 'r', 'f', 0, NPC_REGULAR), + 2103: (2616, lnames[2103], ('csl', 'ss', 's', 'm', 9, 0, 8, 5, 0, 11, 0, 11, 2, 10), 'm', 0, NPC_REGULAR), + 2104: (2671, lnames[2104], ('mls', 'ms', 'm', 'm', 15, 0, 15, 15, 1, 10, 1, 10, 0, 16), 'm', 1, NPC_HQ), + 2105: (2671, lnames[2105], ('hsl', 'ss', 'm', 'm', 7, 0, 7, 7, 1, 10, 1, 10, 0, 13), 'm', 1, NPC_HQ), + 2106: (2671, lnames[2106], ('hss', 'ld', 'm', 'f', 23, 0, 23, 23, 1, 23, 1, 23, 24, 27), 'f', 1, NPC_HQ), + 2107: (2671, lnames[2107], ('cll', 'sd', 'm', 'f', 14, 0, 14, 14, 1, 24, 1, 24, 7, 4), 'f', 1, NPC_HQ), + 2108: (2603, lnames[2108], ('csl', 'ms', 'm', 'f', 7, 0, 7, 7, 1, 24, 1, 24, 3, 2), 'f', 1, NPC_REGULAR), + 2109: (2604, lnames[2109], 'r', 'm', 1, NPC_REGULAR), + 2110: (2605, lnames[2110], ('dll', 'ls', 'm', 'm', 14, 0, 14, 14, 0, 27, 0, 27, 0, 15), 'm', 0, NPC_REGULAR), + 2111: (2607, lnames[2111], 'r', 'm', 1, NPC_REGULAR), + 2112: (2610, lnames[2112], ('fll', 'ss', 'm', 'm', 20, 0, 20, 20, 0, 27, 0, 27, 0, 9), 'm', 1, NPC_REGULAR), + 2113: (2617, lnames[2113], ('fsl', 'ls', 'm', 'm', 14, 0, 14, 14, 0, 0, 0, 0, 0, 2), 'm', 0, NPC_REGULAR), + 2114: (2618, lnames[2114], ('fls', 'sd', 'm', 'f', 6, 0, 6, 6, 0, 0, 0, 0, 23, 27), 'f', 0, NPC_REGULAR), + 2115: (2621, lnames[2115], ('rll', 'ms', 'l', 'f', 22, 0, 22, 22, 1, 1, 1, 1, 26, 27), 'f', 0, NPC_REGULAR), + 2116: (2624, lnames[2116], ('rss', 'ls', 'l', 'm', 13, 0, 13, 13, 1, 1, 1, 1, 1, 14), 'm', 0, NPC_REGULAR), + 2117: (2625, lnames[2117], ('rls', 'sd', 'l', 'f', 6, 0, 6, 6, 1, 2, 1, 2, 25, 27), 'f', 0, NPC_REGULAR), + 2118: (2626, lnames[2118], ('mls', 'ss', 'l', 'm', 20, 0, 20, 20, 1, 1, 1, 1, 1, 6), 'm', 0, NPC_REGULAR), + 2119: (2629, lnames[2119], ('hll', 'ss', 'l', 'f', 13, 0, 13, 13, 1, 3, 1, 3, 19, 27), 'f', 1, NPC_REGULAR), + 2120: (2632, lnames[2120], ('hss', 'ls', 'l', 'm', 5, 0, 5, 5, 1, 2, 1, 2, 1, 19), 'm', 0, NPC_REGULAR), + 2121: (2633, lnames[2121], ('cll', 'ls', 'l', 'f', 21, 0, 21, 21, 0, 4, 0, 4, 4, 4), 'f', 0, NPC_REGULAR), + 2122: (2639, lnames[2122], ('csl', 'ss', 'l', 'm', 13, 0, 13, 13, 0, 3, 0, 3, 1, 13), 'm', 0, NPC_REGULAR), + 2123: (2643, lnames[2123], ('cls', 'md', 'l', 'f', 4, 0, 4, 4, 0, 5, 0, 5, 14, 27), 'f', 0, NPC_REGULAR), + 2124: (2644, lnames[2124], ('dll', 'sd', 'l', 'f', 21, 0, 21, 21, 0, 5, 0, 5, 8, 21), 'f', 0, NPC_REGULAR), + 2125: (2649, lnames[2125], ('dss', 'ss', 'l', 'm', 12, 0, 12, 12, 0, 4, 0, 4, 1, 0), 'm', 0, NPC_REGULAR), + 2126: (2654, lnames[2126], ('dls', 'ld', 'l', 'f', 4, 0, 4, 4, 0, 6, 0, 6, 3, 7), 'f', 1, NPC_REGULAR), + 2127: (2655, lnames[2127], ('fsl', 'ms', 'l', 'm', 19, 0, 19, 19, 0, 5, 0, 5, 1, 15), 'm', 1, NPC_REGULAR), + 2128: (2656, lnames[2128], ('fss', 'ss', 'l', 'm', 12, 0, 12, 12, 1, 5, 1, 5, 1, 12), 'm', 1, NPC_REGULAR), + 2129: (2657, lnames[2129], ('rll', 'ss', 'l', 'm', 4, 0, 4, 4, 1, 5, 1, 5, 1, 9), 'm', 0, NPC_REGULAR), + 2130: (2659, lnames[2130], ('rss', 'md', 'l', 'f', 19, 0, 19, 19, 1, 8, 1, 8, 7, 7), 'f', 0, NPC_REGULAR), + 2131: (2660, lnames[2131], ('rls', 'ls', 'l', 'f', 12, 0, 12, 12, 1, 8, 1, 8, 1, 26), 'f', 1, NPC_REGULAR), + 2132: (2661, lnames[2132], ('mls', 'ss', 'l', 'm', 4, 0, 4, 4, 1, 6, 1, 6, 1, 17), 'm', 0, NPC_REGULAR), + 2133: (2662, lnames[2133], ('hll', 'ls', 'l', 'm', 18, 0, 18, 18, 1, 6, 1, 6, 1, 14), 'm', 0, NPC_REGULAR), + 2134: (2664, lnames[2134], 'r', 'f', 0, NPC_REGULAR), + 2135: (2665, lnames[2135], ('hls', 'ms', 'l', 'f', 3, 0, 3, 3, 0, 12, 0, 12, 2, 26), 'f', 1, NPC_REGULAR), + 2136: (2666, lnames[2136], ('csl', 'ls', 'l', 'm', 18, 0, 18, 18, 0, 8, 0, 8, 1, 1), 'm', 0, NPC_REGULAR), + 2137: (2667, lnames[2137], ('css', 'sd', 'l', 'f', 11, 0, 11, 11, 0, 21, 0, 21, 24, 27), 'f', 0, NPC_REGULAR), + 2138: (2669, lnames[2138], ('dll', 'ss', 'l', 'm', 3, 0, 3, 3, 0, 9, 0, 9, 1, 16), 'm', 0, NPC_REGULAR), + 2139: (2670, lnames[2139], 'r', 'm', 0, NPC_REGULAR), + 2140: (2156, lnames[2140], ('dls', 'ls', 'l', 'm', 10, 0, 10, 10, 1, 9, 1, 9, 1, 10), 'm', 0, NPC_FISHERMAN), + 2201: (2711, lnames[2201], ('dss', 'ss', 'l', 'm', 13, 0, 13, 13, 1, 6, 1, 6, 0, 17), 'm', 1, NPC_REGULAR), + 2202: (2718, lnames[2202], 'r', 'f', 1, NPC_REGULAR), + 2203: (2742, lnames[2203], ('fss', 'ms', 's', 'm', 19, 0, 19, 19, 0, 7, 0, 7, 0, 11), 'm', 1, NPC_HQ), + 2204: (2742, lnames[2204], ('fls', 'ss', 's', 'm', 13, 0, 13, 13, 0, 7, 0, 7, 0, 6), 'm', 1, NPC_HQ), + 2205: (2742, lnames[2205], ('rsl', 'md', 's', 'f', 4, 0, 4, 4, 0, 11, 0, 11, 16, 27), 'f', 1, NPC_HQ), + 2206: (2742, lnames[2206], ('rss', 'sd', 's', 'f', 21, 0, 21, 21, 0, 12, 0, 12, 0, 8), 'f', 1, NPC_HQ), + 2207: (2705, lnames[2207], ('mss', 'ss', 's', 'm', 12, 0, 12, 12, 0, 8, 0, 8, 0, 16), 'm', 1, NPC_REGULAR), + 2208: (2708, lnames[2208], ('mls', 'ls', 's', 'm', 4, 0, 4, 4, 1, 8, 1, 8, 0, 13), 'm', 1, NPC_REGULAR), + 2209: (2712, lnames[2209], ('hsl', 'ms', 's', 'm', 19, 0, 19, 19, 1, 8, 1, 8, 0, 10), 'm', 1, NPC_REGULAR), + 2210: (2713, lnames[2210], ('hss', 'ms', 's', 'f', 12, 0, 12, 12, 1, 21, 1, 21, 1, 24), 'f', 1, NPC_REGULAR), + 2211: (2716, lnames[2211], ('cll', 'ss', 's', 'f', 3, 0, 3, 3, 1, 22, 1, 22, 25, 27), 'f', 1, NPC_REGULAR), + 2212: (2717, lnames[2212], ('css', 'ls', 's', 'm', 18, 0, 18, 18, 1, 9, 1, 9, 0, 18), 'm', 0, NPC_REGULAR), + 2213: (2720, lnames[2213], ('cls', 'ls', 's', 'f', 12, 0, 12, 12, 1, 23, 1, 23, 11, 27), 'f', 1, NPC_REGULAR), + 2214: (2723, lnames[2214], 'r', 'm', 0, NPC_REGULAR), + 2215: (2727, lnames[2215], ('dss', 'ls', 'm', 'm', 18, 0, 18, 18, 0, 11, 0, 11, 0, 9), 'm', 0, NPC_REGULAR), + 2216: (2728, lnames[2216], ('fll', 'sd', 'm', 'f', 11, 0, 11, 11, 0, 25, 0, 25, 12, 27), 'f', 0, NPC_REGULAR), + 2217: (2729, lnames[2217], ('fsl', 'ss', 'm', 'm', 4, 0, 4, 4, 0, 12, 0, 12, 0, 20), 'm', 1, NPC_REGULAR), + 2218: (2730, lnames[2218], 'r', 'f', 0, NPC_REGULAR), + 2219: (2732, lnames[2219], ('rll', 'ms', 'm', 'm', 10, 0, 10, 10, 0, 27, 0, 27, 0, 14), 'm', 0, NPC_REGULAR), + 2220: (2733, lnames[2220], ('rss', 'ss', 'm', 'm', 3, 0, 3, 3, 1, 12, 1, 12, 0, 11), 'm', 0, NPC_REGULAR), + 2221: (2734, lnames[2221], 'r', 'f', 0, NPC_REGULAR), + 2222: (2735, lnames[2222], ('mls', 'ls', 'm', 'm', 10, 0, 10, 10, 1, 0, 1, 0, 0, 1), 'm', 0, NPC_REGULAR), + 2223: (2739, lnames[2223], 'r', 'f', 0, NPC_REGULAR), + 2224: (2740, lnames[2224], ('hss', 'ss', 'm', 'm', 17, 0, 17, 17, 1, 1, 1, 1, 0, 16), 'm', 0, NPC_REGULAR), + 2225: (2236, lnames[2225], ('cll', 'ls', 'm', 'm', 9, 0, 9, 9, 1, 1, 1, 1, 0, 13), 'm', 0, NPC_FISHERMAN), + 2301: (2804, lnames[2301], ('cll', 'ms', 'm', 'm', 10, 0, 10, 10, 1, 3, 1, 3, 0, 6), 'm', 1, NPC_REGULAR), + 2302: (2831, lnames[2302], ('css', 'ms', 'm', 'm', 3, 0, 3, 3, 1, 3, 1, 3, 0, 1), 'm', 1, NPC_REGULAR), + 2303: (2834, lnames[2303], 'r', 'f', 0, NPC_REGULAR), + 2304: (2832, lnames[2304], ('dss', 'ss', 'm', 'm', 9, 0, 9, 9, 0, 10, 0, 10, 1, 12), 'm', 1, NPC_HQ), + 2305: (2832, lnames[2305], ('dss', 'ss', 'm', 'm', 8, 0, 8, 8, 1, 0, 1, 0, 1, 9), 'm', 1, NPC_HQ), + 2306: (2832, lnames[2306], ('fll', 'md', 'm', 'f', 24, 0, 24, 24, 1, 0, 1, 0, 16, 27), 'f', 1, NPC_HQ), + 2307: (2832, lnames[2307], ('fsl', 'ls', 'm', 'f', 16, 0, 16, 16, 1, 1, 1, 1, 3, 1), 'f', 1, NPC_HQ), + 2308: (2801, lnames[2308], ('fls', 'ss', 'm', 'f', 8, 0, 8, 8, 1, 1, 1, 1, 14, 27), 'f', 1, NPC_REGULAR), + 2309: (2802, lnames[2309], ('rsl', 'ls', 'm', 'm', 22, 0, 22, 22, 1, 1, 1, 1, 1, 14), 'm', 1, NPC_REGULAR), + 2311: (2809, lnames[2311], ('mss', 'ss', 'm', 'm', 7, 0, 7, 7, 0, 2, 0, 2, 1, 6), 'm', 1, NPC_REGULAR), + 2312: (2837, lnames[2312], ('mls', 'ld', 'm', 'f', 24, 0, 24, 24, 0, 3, 0, 3, 4, 6), 'f', 0, NPC_REGULAR), + 2313: (2817, lnames[2313], 'r', 'f', 0, NPC_REGULAR), + 2314: (2818, lnames[2314], ('hss', 'ms', 'm', 'm', 7, 0, 7, 7, 0, 3, 0, 3, 1, 16), 'm', 0, NPC_REGULAR), + 2315: (2822, lnames[2315], ('cll', 'ss', 'm', 'm', 21, 0, 21, 21, 0, 3, 0, 3, 1, 13), 'm', 0, NPC_REGULAR), + 2316: (2823, lnames[2316], ('csl', 'md', 'l', 'f', 15, 0, 15, 15, 0, 5, 0, 5, 0, 23), 'f', 0, NPC_REGULAR), + 2318: (2829, lnames[2318], ('dsl', 'ss', 'l', 'm', 21, 0, 21, 21, 1, 4, 1, 4, 1, 0), 'm', 0, NPC_REGULAR), + 2319: (2830, lnames[2319], ('dss', 'ls', 'l', 'm', 14, 0, 14, 14, 1, 5, 1, 5, 1, 18), 'm', 0, NPC_REGULAR), + 2320: (2839, lnames[2320], 'r', 'm', 0, NPC_REGULAR), + 2321: (2341, lnames[2321], ('fsl', 'ss', 'l', 'm', 21, 0, 21, 21, 1, 5, 1, 5, 0, 12), 'm', 0, NPC_FISHERMAN), + 1001: (1506, lnames[1001], ('rss', 'ms', 'l', 'm', 10, 0, 10, 10, 0, 11, 0, 11, 0, 0), 'm', 0, NPC_CLERK), + 1002: (1506, lnames[1002], ('mss', 'ss', 'l', 'm', 3, 0, 3, 3, 1, 10, 1, 10, 0, 18), 'm', 0, NPC_CLERK), + 1003: (1507, lnames[1003], ('mls', 'ss', 'l', 'm', 17, 0, 17, 17, 1, 11, 1, 11, 0, 15), 'm', 0, NPC_HQ), + 1004: (1507, lnames[1004], ('hsl', 'md', 'l', 'f', 10, 0, 10, 10, 1, 24, 1, 24, 24, 27), 'f', 0, NPC_HQ), + 1005: (1507, lnames[1005], ('hss', 'ms', 'l', 'm', 3, 0, 3, 3, 1, 11, 1, 11, 0, 9), 'm', 0, NPC_HQ), + 1006: (1507, lnames[1006], ('cll', 'ss', 'l', 'f', 18, 0, 18, 18, 1, 25, 1, 25, 19, 27), 'f', 0, NPC_HQ), + 1007: (1508, lnames[1007], ('csl', 'ls', 'm', 'm', 9, 0, 9, 9, 1, 12, 1, 12, 0, 20), 'm', 0, NPC_TAILOR), + 1008: (1000, lnames[1008], ('cls', 'ms', 'm', 'm', 3, 0, 3, 3, 0, 27, 0, 27, 0, 17), 'm', 0, NPC_FISHERMAN), + 1009: (1510, lnames[1009], ('dsl', 'ss', 'm', 'm', 17, 0, 17, 17, 0, 0, 0, 0, 0, 14), 'm', 0, NPC_PETCLERK), + 1010: (1510, lnames[1010], ('dss', 'ld', 'm', 'f', 10, 0, 10, 10, 0, 0, 0, 0, 26, 27), 'f', 0, NPC_PETCLERK), + 1011: (1510, lnames[1011], ('fll', 'sd', 'm', 'f', 1, 0, 1, 1, 0, 1, 0, 1, 4, 25), 'f', 0, NPC_PETCLERK), + 1012: (1000, lnames[1012], ('fls', 'ms', 'l', 'm', 14, 0, 3, 3, 0, 1, 0, 1, 0, 13), 'm', 1, NPC_PARTYPERSON), + 1013: (1000, lnames[1013], ('fss', 'ms', 'm', 'f', 2, 0, 3, 3, 1, 6, 1, 6, 5, 6), 'f', 1, NPC_PARTYPERSON), + 1101: (1627, lnames[1101], ('fll', 'ls', 'm', 'm', 14, 0, 14, 14, 1, 3, 1, 3, 1, 9), 'm', 0, NPC_REGULAR), + 1102: (1612, lnames[1102], ('fsl', 'ms', 'm', 'm', 7, 0, 7, 7, 1, 3, 1, 3, 1, 2), 'm', 0, NPC_REGULAR), + 1103: (1626, lnames[1103], 'r', 'm', 0, NPC_REGULAR), + 1104: (1617, lnames[1104], 'r', 'm', 0, NPC_REGULAR), + 1105: (1606, lnames[1105], ('rss', 'ms', 'm', 'm', 6, 0, 6, 6, 0, 4, 0, 4, 1, 14), 'm', 1, NPC_REGULAR), + 1106: (1604, lnames[1106], 'r', 'f', 1, NPC_REGULAR), + 1107: (1621, lnames[1107], 'r', 'm', 0, NPC_REGULAR), + 1108: (1629, lnames[1108], ('hsl', 'ls', 'l', 'm', 6, 0, 6, 6, 0, 6, 0, 6, 1, 1), 'm', 0, NPC_HQ), + 1109: (1629, lnames[1109], ('hss', 'ls', 'l', 'f', 22, 0, 22, 22, 0, 8, 0, 8, 14, 27), 'f', 0, NPC_HQ), + 1110: (1629, lnames[1110], ('cll', 'ss', 'l', 'm', 13, 0, 13, 13, 1, 6, 1, 6, 1, 16), 'm', 0, NPC_HQ), + 1111: (1629, lnames[1111], ('csl', 'ld', 'l', 'f', 6, 0, 6, 6, 1, 9, 1, 9, 2, 2), 'f', 0, NPC_HQ), + 1112: (1602, lnames[1112], ('cls', 'ms', 'l', 'm', 20, 0, 20, 20, 1, 7, 1, 7, 1, 10), 'm', 1, NPC_REGULAR), + 1113: (1608, lnames[1113], ('dll', 'ms', 'l', 'f', 13, 0, 13, 13, 1, 11, 1, 11, 0, 27), 'f', 1, NPC_REGULAR), + 1114: (1609, lnames[1114], ('dss', 'ls', 'l', 'm', 5, 0, 5, 5, 1, 7, 1, 7, 1, 0), 'm', 1, NPC_REGULAR), + 1115: (1613, lnames[1115], ('fll', 'sd', 'l', 'f', 21, 0, 21, 21, 1, 12, 1, 12, 25, 27), 'f', 0, NPC_REGULAR), + 1116: (1614, lnames[1116], ('fsl', 'ls', 'l', 'f', 13, 0, 13, 13, 1, 12, 1, 12, 1, 25), 'f', 0, NPC_REGULAR), + 1117: (1615, lnames[1117], ('fls', 'ss', 'l', 'm', 5, 0, 5, 5, 0, 9, 0, 9, 1, 12), 'm', 0, NPC_REGULAR), + 1118: (1616, lnames[1118], ('rll', 'ls', 'l', 'm', 19, 0, 19, 19, 0, 9, 0, 9, 1, 9), 'm', 0, NPC_REGULAR), + 1121: (1619, lnames[1121], 'r', 'f', 0, NPC_REGULAR), + 1122: (1620, lnames[1122], ('hll', 'ms', 'l', 'm', 12, 0, 12, 12, 0, 11, 0, 11, 0, 14), 'm', 0, NPC_REGULAR), + 1123: (1622, lnames[1123], ('hss', 'ms', 'l', 'f', 4, 0, 4, 4, 1, 23, 1, 23, 23, 27), 'f', 0, NPC_REGULAR), + 1124: (1624, lnames[1124], ('cll', 'ls', 'l', 'm', 19, 0, 19, 19, 1, 11, 1, 11, 0, 6), 'm', 0, NPC_REGULAR), + 1125: (1628, lnames[1125], ('csl', 'sd', 'l', 'f', 12, 0, 12, 12, 1, 24, 1, 24, 25, 27), 'f', 0, NPC_REGULAR), + 1126: (1129, lnames[1126], ('cls', 'ms', 'l', 'm', 4, 0, 4, 4, 1, 11, 1, 11, 0, 19), 'm', 0, NPC_FISHERMAN), + 1201: (1710, lnames[1201], ('css', 'ls', 's', 'f', 12, 0, 12, 12, 0, 0, 0, 0, 1, 24), 'f', 0, NPC_REGULAR), + 1202: (1713, lnames[1202], ('cls', 'ss', 's', 'm', 4, 0, 4, 4, 0, 0, 0, 0, 1, 14), 'm', 0, NPC_REGULAR), + 1203: (1725, lnames[1203], 'r', 'm', 0, NPC_REGULAR), + 1204: (1712, lnames[1204], ('dss', 'ms', 's', 'm', 12, 0, 12, 12, 1, 1, 1, 1, 1, 6), 'm', 0, NPC_REGULAR), + 1205: (1729, lnames[1205], ('fll', 'ss', 's', 'm', 4, 0, 4, 4, 1, 1, 1, 1, 1, 1), 'm', 0, NPC_HQ), + 1206: (1729, lnames[1206], ('fss', 'ld', 's', 'f', 19, 0, 19, 19, 1, 2, 1, 2, 7, 11), 'f', 0, NPC_HQ), + 1207: (1729, lnames[1207], ('fls', 'ms', 's', 'm', 12, 0, 12, 12, 1, 2, 1, 2, 1, 16), 'm', 0, NPC_HQ), + 1208: (1729, lnames[1208], ('rsl', 'ls', 'm', 'f', 3, 0, 3, 3, 1, 3, 1, 3, 23, 27), 'f', 0, NPC_HQ), + 1209: (1701, lnames[1209], ('rss', 'ss', 'm', 'f', 19, 0, 19, 19, 0, 4, 0, 4, 17, 27), 'f', 1, NPC_REGULAR), + 1210: (1703, lnames[1210], 'r', 'm', 1, NPC_REGULAR), + 1211: (1705, lnames[1211], ('mls', 'ms', 'm', 'm', 4, 0, 4, 4, 0, 4, 0, 4, 1, 0), 'm', 1, NPC_REGULAR), + 1212: (1706, lnames[1212], ('hsl', 'ss', 'm', 'm', 18, 0, 18, 18, 0, 4, 0, 4, 1, 18), 'm', 1, NPC_REGULAR), + 1213: (1707, lnames[1213], ('hss', 'ls', 'm', 'm', 10, 0, 10, 10, 0, 4, 0, 4, 1, 15), 'm', 1, NPC_REGULAR), + 1214: (1709, lnames[1214], ('cll', 'sd', 'm', 'f', 2, 0, 2, 2, 0, 7, 0, 7, 1, 12), 'f', 1, NPC_REGULAR), + 1215: (1711, lnames[1215], ('css', 'ms', 'm', 'f', 18, 0, 18, 18, 0, 7, 0, 7, 25, 27), 'f', 0, NPC_REGULAR), + 1216: (1714, lnames[1216], ('cls', 'ls', 'm', 'm', 10, 0, 10, 10, 1, 5, 1, 5, 1, 2), 'm', 0, NPC_REGULAR), + 1217: (1716, lnames[1217], 'r', 'f', 0, NPC_REGULAR), + 1218: (1717, lnames[1218], ('dss', 'ms', 'm', 'm', 17, 0, 17, 17, 1, 6, 1, 6, 1, 17), 'm', 0, NPC_REGULAR), + 1219: (1718, lnames[1219], ('fll', 'ss', 'm', 'm', 9, 0, 9, 9, 1, 6, 1, 6, 1, 14), 'm', 0, NPC_REGULAR), + 1220: (1719, lnames[1220], ('fsl', 'md', 'm', 'f', 2, 0, 2, 2, 1, 9, 1, 9, 7, 23), 'f', 0, NPC_REGULAR), + 1221: (1720, lnames[1221], 'r', 'm', 0, NPC_REGULAR), + 1222: (1721, lnames[1222], 'r', 'm', 0, NPC_REGULAR), + 1223: (1723, lnames[1223], ('rss', 'ls', 'l', 'm', 2, 0, 2, 2, 0, 8, 0, 8, 1, 19), 'm', 0, NPC_REGULAR), + 1224: (1724, lnames[1224], ('mss', 'sd', 'l', 'f', 17, 0, 17, 17, 0, 21, 0, 21, 7, 9), 'f', 0, NPC_REGULAR), + 1225: (1726, lnames[1225], ('mls', 'ss', 'l', 'm', 9, 0, 9, 9, 0, 9, 0, 9, 1, 13), 'm', 0, NPC_REGULAR), + 1226: (1727, lnames[1226], ('hsl', 'ls', 'l', 'm', 2, 0, 2, 2, 0, 9, 0, 9, 1, 10), 'm', 0, NPC_REGULAR), + 1227: (1728, lnames[1227], ('hss', 'sd', 'l', 'f', 17, 0, 17, 17, 0, 22, 0, 22, 3, 7), 'f', 0, NPC_REGULAR), + 1228: (1236, lnames[1228], ('cll', 'ms', 'l', 'm', 8, 0, 8, 8, 1, 9, 1, 9, 1, 0), 'm', 0, NPC_FISHERMAN), + 1301: (1828, lnames[1301], ('mls', 'md', 'm', 'f', 16, 0, 16, 16, 1, 8, 1, 8, 14, 27), 'f', 0, NPC_REGULAR), + 1302: (1832, lnames[1302], ('hsl', 'ms', 'm', 'm', 8, 0, 8, 8, 1, 6, 1, 6, 0, 18), 'm', 0, NPC_REGULAR), + 1303: (1826, lnames[1303], ('hls', 'ss', 'm', 'm', 22, 0, 22, 22, 1, 6, 1, 6, 0, 15), 'm', 0, NPC_REGULAR), + 1304: (1804, lnames[1304], ('cll', 'md', 'm', 'f', 15, 0, 15, 15, 1, 9, 1, 9, 23, 27), 'f', 1, NPC_REGULAR), + 1305: (1835, lnames[1305], ('css', 'ms', 'm', 'm', 7, 0, 7, 7, 1, 7, 1, 7, 0, 9), 'm', 0, NPC_HQ), + 1306: (1835, lnames[1306], ('cls', 'ms', 'm', 'f', 24, 0, 24, 24, 0, 12, 0, 12, 0, 7), 'f', 0, NPC_HQ), + 1307: (1835, lnames[1307], ('dsl', 'ls', 'm', 'm', 15, 0, 15, 15, 0, 8, 0, 8, 0, 20), 'm', 0, NPC_HQ), + 1308: (1835, lnames[1308], ('dss', 'sd', 'm', 'f', 8, 0, 8, 8, 0, 21, 0, 21, 4, 5), 'f', 0, NPC_HQ), + 1309: (1802, lnames[1309], ('fll', 'ms', 'l', 'f', 23, 0, 23, 23, 0, 21, 0, 21, 1, 12), 'f', 1, NPC_REGULAR), + 1310: (1805, lnames[1310], ('fsl', 'ss', 'l', 'm', 15, 0, 15, 15, 0, 9, 0, 9, 0, 11), 'm', 1, NPC_REGULAR), + 1311: (1806, lnames[1311], 'r', 'f', 1, NPC_REGULAR), + 1312: (1807, lnames[1312], ('rsl', 'ms', 'l', 'm', 21, 0, 21, 21, 1, 9, 1, 9, 0, 1), 'm', 1, NPC_REGULAR), + 1313: (1808, lnames[1313], ('rss', 'ss', 'l', 'm', 14, 0, 14, 14, 1, 10, 1, 10, 0, 19), 'm', 1, NPC_REGULAR), + 1314: (1809, lnames[1314], ('mss', 'ls', 'l', 'm', 6, 0, 6, 6, 1, 10, 1, 10, 0, 16), 'm', 1, NPC_REGULAR), + 1315: (1810, lnames[1315], 'r', 'f', 0, NPC_REGULAR), + 1316: (1811, lnames[1316], ('hsl', 'ms', 'l', 'f', 14, 0, 14, 14, 1, 24, 1, 24, 7, 7), 'f', 0, NPC_REGULAR), + 1317: (1813, lnames[1317], ('hss', 'ld', 'l', 'f', 7, 0, 7, 7, 1, 25, 1, 25, 26, 27), 'f', 0, NPC_REGULAR), + 1318: (1814, lnames[1318], ('cll', 'ms', 'l', 'm', 20, 0, 20, 20, 0, 12, 0, 12, 0, 0), 'm', 0, NPC_REGULAR), + 1319: (1815, lnames[1319], ('csl', 'ss', 'l', 'm', 14, 0, 14, 14, 0, 27, 0, 27, 0, 18), 'm', 0, NPC_REGULAR), + 1320: (1818, lnames[1320], 'r', 'm', 0, NPC_REGULAR), + 1321: (1819, lnames[1321], ('dsl', 'md', 'l', 'f', 22, 0, 22, 22, 0, 27, 0, 27, 7, 5), 'f', 0, NPC_REGULAR), + 1322: (1820, lnames[1322], ('dss', 'ls', 'm', 'f', 13, 0, 13, 13, 0, 0, 0, 0, 26, 27), 'f', 0, NPC_REGULAR), + 1323: (1821, lnames[1323], ('fll', 'ss', 'm', 'm', 6, 0, 6, 6, 0, 0, 0, 0, 1, 2), 'm', 0, NPC_REGULAR), + 1324: (1823, lnames[1324], ('fsl', 'md', 'm', 'f', 22, 0, 22, 22, 0, 1, 0, 1, 10, 27), 'f', 0, NPC_REGULAR), + 1325: (1824, lnames[1325], 'r', 'm', 0, NPC_REGULAR), + 1326: (1825, lnames[1326], ('rll', 'ms', 'm', 'f', 6, 0, 6, 6, 1, 2, 1, 2, 10, 27), 'f', 0, NPC_REGULAR), + 1327: (1829, lnames[1327], 'r', 'f', 0, NPC_REGULAR), + 1328: (1830, lnames[1328], ('rls', 'ms', 'm', 'm', 13, 0, 13, 13, 1, 2, 1, 2, 1, 6), 'm', 0, NPC_REGULAR), + 1329: (1831, lnames[1329], ('mls', 'ms', 'm', 'f', 4, 0, 4, 4, 1, 3, 1, 3, 25, 27), 'f', 0, NPC_REGULAR), + 1330: (1833, lnames[1330], ('hsl', 'ls', 'm', 'm', 19, 0, 19, 19, 1, 3, 1, 3, 1, 19), 'm', 0, NPC_REGULAR), + 1331: (1834, lnames[1331], ('hss', 'ls', 'm', 'm', 12, 0, 12, 12, 0, 3, 0, 3, 1, 16), 'm', 0, NPC_REGULAR), + 1332: (1330, lnames[1332], ('cll', 'ms', 'm', 'm', 5, 0, 5, 5, 0, 4, 0, 4, 1, 13), 'm', 0, NPC_FISHERMAN), + 3001: (3506, lnames[3001], 'r', 'f', 0, NPC_REGULAR), + 3002: (3508, lnames[3002], ('cls', 'ms', 'm', 'm', 4, 0, 4, 4, 1, 9, 1, 9, 0, 18), 'm', 0, NPC_HQ), + 3003: (3508, lnames[3003], ('dsl', 'ss', 'm', 'f', 19, 0, 19, 19, 1, 22, 1, 22, 25, 27), 'f', 0, NPC_HQ), + 3004: (3508, lnames[3004], ('dss', 'ls', 'm', 'm', 12, 0, 12, 12, 1, 10, 1, 10, 0, 12), 'm', 0, NPC_HQ), + 3005: (3508, lnames[3005], ('fll', 'ms', 'm', 'm', 4, 0, 4, 4, 0, 11, 0, 11, 0, 9), 'm', 0, NPC_HQ), + 3006: (3507, lnames[3006], ('fsl', 'ss', 'm', 'm', 18, 0, 18, 18, 0, 11, 0, 11, 0, 2), 'm', 0, NPC_CLERK), + 3007: (3507, lnames[3007], ('fls', 'ld', 'm', 'f', 12, 0, 12, 12, 0, 25, 0, 25, 8, 5), 'f', 0, NPC_CLERK), + 3008: (3509, lnames[3008], ('rll', 'ms', 'l', 'm', 4, 0, 4, 4, 0, 12, 0, 12, 0, 17), 'm', 0, NPC_TAILOR), + 3009: (3000, lnames[3009], ('rss', 'ls', 'l', 'f', 19, 0, 19, 19, 0, 26, 0, 26, 4, 23), 'f', 0, NPC_FISHERMAN), + 3010: (3511, lnames[3010], ('rls', 'ss', 'l', 'm', 10, 0, 10, 10, 0, 12, 0, 12, 0, 11), 'm', 0, NPC_PETCLERK), + 3011: (3511, lnames[3011], ('mls', 'md', 'l', 'f', 3, 0, 3, 3, 1, 26, 1, 26, 26, 27), 'f', 0, NPC_PETCLERK), + 3012: (3511, lnames[3012], ('hsl', 'ms', 'l', 'm', 18, 0, 18, 18, 1, 12, 1, 12, 0, 1), 'm', 0, NPC_PETCLERK), + 3013: (3000, lnames[3013], ('cls', 'ss', 'm', 'm', 18, 0, 17, 17, 1, 7, 1, 7, 1, 9), 'm', 1, NPC_PARTYPERSON), + 3014: (3000, lnames[3014], ('css', 'sd', 'm', 'f', 17, 0, 16, 16, 0, 24, 0, 24, 0, 9), 'f', 1, NPC_PARTYPERSON), + 3101: (3611, lnames[3101], ('mls', 'ls', 'l', 'm', 16, 0, 16, 16, 1, 1, 1, 1, 1, 6), 'm', 0, NPC_REGULAR), + 3102: (3625, lnames[3102], 'r', 'f', 0, NPC_REGULAR), + 3103: (3641, lnames[3103], 'r', 'm', 0, NPC_REGULAR), + 3104: (3602, lnames[3104], ('cll', 'ss', 'l', 'f', 16, 0, 16, 16, 0, 4, 0, 4, 3, 2), 'f', 1, NPC_REGULAR), + 3105: (3651, lnames[3105], 'r', 'm', 0, NPC_REGULAR), + 3106: (3636, lnames[3106], ('fll', 'ls', 'l', 'm', 8, 2, 8, 8, 10, 27, 0, 27, 7, 11), 'm', 0, NPC_REGULAR), + 3107: (3630, lnames[3107], ('dll', 'ms', 'l', 'f', 15, 0, 15, 15, 0, 5, 0, 5, 4, 4), 'f', 0, NPC_REGULAR), + 3108: (3638, lnames[3108], 'r', 'm', 0, NPC_REGULAR), + 3109: (3637, lnames[3109], ('fll', 'sd', 'm', 'f', 23, 0, 23, 23, 1, 6, 1, 6, 12, 27), 'f', 0, NPC_REGULAR), + 3110: (3629, lnames[3110], ('fss', 'ms', 'l', 'm', 10, 10, 10, 10, 16, 4, 0, 4, 5, 4), 'm', 0, NPC_REGULAR), + 3111: (3627, lnames[3111], ('dsl', 'ls', 's', 'm', 6, 0, 6, 6, 14, 27, 10, 27, 1, 14), 'm', 1, NPC_REGULAR), + 3112: (3607, lnames[3112], ('rll', 'ls', 'm', 'm', 21, 0, 21, 21, 1, 5, 1, 5, 1, 9), 'm', 1, NPC_REGULAR), + 3113: (3618, lnames[3113], ('rss', 'ms', 'm', 'm', 14, 0, 14, 14, 1, 5, 1, 5, 0, 2), 'm', 0, NPC_REGULAR), + 3114: (3620, lnames[3114], ('rls', 'ss', 'm', 'm', 7, 0, 7, 7, 0, 6, 0, 6, 0, 20), 'm', 0, NPC_REGULAR), + 3115: (3654, lnames[3115], ('mls', 'ls', 'm', 'm', 21, 0, 21, 21, 0, 7, 0, 7, 0, 17), 'm', 0, NPC_HQ), + 3116: (3654, lnames[3116], ('hll', 'ls', 'm', 'f', 14, 0, 14, 14, 0, 11, 0, 11, 0, 12), 'f', 0, NPC_HQ), + 3117: (3654, lnames[3117], ('hss', 'ss', 'm', 'm', 6, 0, 6, 6, 0, 7, 0, 7, 0, 11), 'm', 0, NPC_HQ), + 3118: (3654, lnames[3118], ('cll', 'ls', 'm', 'm', 20, 0, 20, 20, 0, 8, 0, 8, 0, 6), 'm', 0, NPC_HQ), + 3119: (3653, lnames[3119], ('csl', 'ms', 'm', 'm', 14, 0, 14, 14, 0, 8, 0, 8, 0, 1), 'm', 0, NPC_REGULAR), + 3120: (3610, lnames[3120], ('cls', 'ss', 'm', 'm', 6, 0, 6, 6, 1, 8, 1, 8, 0, 19), 'm', 0, NPC_REGULAR), + 3121: (3601, lnames[3121], ('dll', 'ls', 'm', 'm', 20, 0, 20, 20, 1, 8, 1, 8, 0, 16), 'm', 1, NPC_REGULAR), + 3122: (3608, lnames[3122], ('dss', 'md', 'l', 'f', 13, 0, 13, 13, 1, 21, 1, 21, 10, 27), 'f', 1, NPC_REGULAR), + 3123: (3612, lnames[3123], ('dls', 'ms', 'l', 'm', 6, 0, 6, 6, 1, 9, 1, 9, 0, 10), 'm', 0, NPC_REGULAR), + 3124: (3613, lnames[3124], ('fsl', 'ss', 'l', 'm', 20, 0, 20, 20, 1, 9, 1, 9, 0, 4), 'm', 0, NPC_REGULAR), + 3125: (3614, lnames[3125], ('fls', 'ls', 'l', 'm', 13, 0, 13, 13, 1, 9, 1, 9, 0, 0), 'm', 0, NPC_REGULAR), + 3126: (3615, lnames[3126], ('rll', 'ls', 'l', 'f', 6, 0, 6, 6, 0, 24, 0, 24, 17, 27), 'f', 0, NPC_REGULAR), + 3127: (3617, lnames[3127], ('rss', 'ms', 'l', 'f', 21, 0, 21, 21, 0, 24, 0, 24, 19, 27), 'f', 0, NPC_REGULAR), + 3128: (3621, lnames[3128], ('rls', 'ls', 'l', 'm', 13, 0, 13, 13, 0, 11, 0, 11, 0, 12), 'm', 0, NPC_REGULAR), + 3129: (3623, lnames[3129], ('mls', 'sd', 'l', 'f', 4, 0, 4, 4, 0, 25, 0, 25, 23, 27), 'f', 0, NPC_REGULAR), + 3130: (3624, lnames[3130], ('hll', 'ms', 'l', 'f', 21, 0, 21, 21, 0, 26, 0, 26, 25, 27), 'f', 0, NPC_REGULAR), + 3131: (3634, lnames[3131], ('hss', 'ls', 'l', 'm', 12, 0, 12, 12, 0, 12, 0, 12, 0, 20), 'm', 0, NPC_REGULAR), + 3132: (3635, lnames[3132], 'r', 'f', 0, NPC_REGULAR), + 3133: (3642, lnames[3133], ('csl', 'ms', 'l', 'm', 19, 0, 19, 19, 1, 12, 1, 12, 0, 14), 'm', 0, NPC_REGULAR), + 3134: (3643, lnames[3134], ('cls', 'ss', 'l', 'm', 12, 0, 12, 12, 1, 0, 1, 0, 0, 11), 'm', 0, NPC_REGULAR), + 3135: (3644, lnames[3135], ('dll', 'md', 'l', 'f', 4, 0, 4, 4, 1, 0, 1, 0, 4, 12), 'f', 0, NPC_REGULAR), + 3136: (3647, lnames[3136], ('dss', 'ls', 'l', 'f', 19, 0, 19, 19, 1, 1, 1, 1, 25, 27), 'f', 0, NPC_REGULAR), + 3137: (3648, lnames[3137], ('dls', 'ss', 'l', 'm', 12, 0, 12, 12, 1, 1, 1, 1, 0, 19), 'm', 0, NPC_REGULAR), + 3138: (3649, lnames[3138], ('fsl', 'ld', 'l', 'f', 3, 0, 3, 3, 1, 2, 1, 2, 26, 27), 'f', 0, NPC_REGULAR), + 3139: (3650, lnames[3139], ('fss', 'sd', 'l', 'f', 19, 0, 19, 19, 0, 2, 0, 2, 16, 27), 'f', 0, NPC_REGULAR), + 3140: (3136, lnames[3140], ('rll', 'ms', 'l', 'f', 11, 0, 11, 11, 0, 3, 0, 3, 12, 27), 'f', 0, NPC_FISHERMAN), + 3201: (3715, lnames[3201], 'r', 'f', 0, NPC_REGULAR), + 3202: (3723, lnames[3202], ('rsl', 'ss', 'l', 'm', 6, 0, 6, 6, 1, 12, 1, 12, 1, 13), 'm', 0, NPC_REGULAR), + 3203: (3712, lnames[3203], ('rss', 'ls', 'l', 'm', 20, 0, 20, 20, 1, 12, 1, 12, 1, 10), 'm', 0, NPC_REGULAR), + 3204: (3734, lnames[3204], ('mss', 'md', 'l', 'f', 13, 0, 13, 13, 1, 26, 1, 26, 4, 5), 'f', 0, NPC_REGULAR), + 3205: (3721, lnames[3205], 'r', 'm', 0, NPC_REGULAR), + 3206: (3722, lnames[3206], ('hsl', 'ss', 'l', 'f', 21, 0, 21, 21, 1, 0, 1, 0, 11, 27), 'f', 0, NPC_REGULAR), + 3207: (3713, lnames[3207], ('hss', 'ls', 'l', 'm', 13, 0, 13, 13, 0, 0, 0, 0, 1, 15), 'm', 0, NPC_REGULAR), + 3208: (3732, lnames[3208], ('cll', 'ms', 'l', 'm', 5, 0, 5, 5, 0, 1, 0, 1, 1, 12), 'm', 0, NPC_REGULAR), + 3209: (3737, lnames[3209], ('css', 'ss', 'l', 'm', 19, 0, 19, 19, 0, 1, 0, 1, 1, 9), 'm', 0, NPC_REGULAR), + 3210: (3728, lnames[3210], ('pls', 'ls', 's', 'm', 13, 0, 13, 13, 2, 1, 2, 1, 5, 2), 'm', 0, NPC_REGULAR), + 3211: (3710, lnames[3211], 'r', 'f', 0, NPC_REGULAR), + 3212: (3707, lnames[3212], ('dss', 'ss', 's', 'm', 19, 0, 19, 19, 0, 2, 0, 2, 1, 17), 'm', 1, NPC_REGULAR), + 3213: (3739, lnames[3213], ('fll', 'ls', 's', 'm', 12, 0, 12, 12, 1, 2, 1, 2, 1, 14), 'm', 0, NPC_HQ), + 3214: (3739, lnames[3214], ('fsl', 'md', 's', 'f', 4, 0, 4, 4, 1, 4, 1, 4, 3, 1), 'f', 0, NPC_HQ), + 3215: (3739, lnames[3215], ('fls', 'ms', 's', 'm', 19, 0, 19, 19, 1, 3, 1, 3, 1, 6), 'm', 0, NPC_HQ), + 3216: (3739, lnames[3216], ('rll', 'ss', 's', 'm', 12, 0, 12, 12, 1, 4, 1, 4, 1, 1), 'm', 0, NPC_HQ), + 3217: (3738, lnames[3217], ('rss', 'ls', 's', 'm', 4, 0, 4, 4, 1, 4, 1, 4, 1, 19), 'm', 0, NPC_REGULAR), + 3218: (3702, lnames[3218], ('mss', 'ms', 's', 'm', 18, 0, 18, 18, 1, 4, 1, 4, 1, 16), 'm', 1, NPC_REGULAR), + 3219: (3705, lnames[3219], ('mls', 'ss', 's', 'm', 12, 0, 12, 12, 0, 5, 0, 5, 1, 13), 'm', 1, NPC_REGULAR), + 3220: (3706, lnames[3220], ('hsl', 'ls', 's', 'm', 4, 0, 4, 4, 0, 5, 0, 5, 1, 10), 'm', 1, NPC_REGULAR), + 3221: (3708, lnames[3221], ('hss', 'sd', 's', 'f', 19, 0, 19, 19, 0, 8, 0, 8, 7, 12), 'f', 1, NPC_REGULAR), + 3222: (3716, lnames[3222], 'r', 'f', 0, NPC_REGULAR), + 3223: (3718, lnames[3223], ('csl', 'ls', 'm', 'm', 4, 0, 4, 4, 0, 6, 0, 6, 1, 18), 'm', 0, NPC_REGULAR), + 3224: (3719, lnames[3224], ('cls', 'md', 'm', 'f', 18, 0, 18, 18, 0, 9, 0, 9, 17, 27), 'f', 0, NPC_REGULAR), + 3225: (3724, lnames[3225], 'r', 'm', 0, NPC_REGULAR), + 3226: (3725, lnames[3226], ('dss', 'ss', 'm', 'm', 3, 0, 3, 3, 1, 7, 1, 7, 1, 9), 'm', 0, NPC_REGULAR), + 3227: (3726, lnames[3227], ('fll', 'ls', 'm', 'm', 17, 0, 17, 17, 1, 7, 1, 7, 1, 2), 'm', 0, NPC_REGULAR), + 3228: (3730, lnames[3228], ('fsl', 'ls', 'm', 'f', 11, 0, 11, 11, 1, 12, 1, 12, 25, 27), 'f', 0, NPC_REGULAR), + 3229: (3731, lnames[3229], ('fls', 'ms', 'm', 'f', 2, 0, 2, 2, 1, 12, 1, 12, 0, 7), 'f', 0, NPC_REGULAR), + 3230: (3735, lnames[3230], ('rll', 'ls', 'm', 'm', 17, 0, 17, 17, 1, 8, 1, 8, 1, 14), 'm', 0, NPC_REGULAR), + 3231: (3736, lnames[3231], ('rss', 'ms', 'm', 'm', 9, 0, 9, 9, 0, 9, 0, 9, 1, 12), 'm', 0, NPC_REGULAR), + 3232: (3236, lnames[3232], ('rls', 'ss', 'm', 'm', 3, 0, 3, 3, 0, 10, 0, 10, 1, 9), 'm', 0, NPC_FISHERMAN), + 3301: (3810, lnames[3301], ('dsl', 'ms', 'm', 'f', 11, 0, 11, 11, 0, 22, 0, 22, 2, 11), 'f', 0, NPC_REGULAR), + 3302: (3806, lnames[3302], ('dls', 'ls', 'm', 'm', 4, 0, 4, 4, 0, 10, 0, 10, 1, 1), 'm', 1, NPC_REGULAR), + 3303: (3830, lnames[3303], ('fll', 'ms', 'm', 'm', 18, 0, 18, 18, 0, 10, 0, 10, 1, 19), 'm', 0, NPC_REGULAR), + 3304: (3828, lnames[3304], ('pll', 'ls', 'l', 'm', 0, 0, 0, 0, 1, 5, 1, 5, 1, 6), 'f', 0, NPC_REGULAR), + 3305: (3812, lnames[3305], ('fls', 'ls', 'm', 'm', 3, 0, 3, 3, 0, 11, 0, 11, 1, 13), 'm', 0, NPC_REGULAR), + 3306: (3821, lnames[3306], ('bss', 'sd', 'm', 'f', 0, 0, 0, 0, 31, 27, 22, 27, 8, 11), 'f', 0, NPC_REGULAR), + 3307: (3329, lnames[3307], ('rss', 'ls', 'm', 'f', 11, 0, 11, 11, 1, 24, 1, 24, 1, 9), 'f', 0, NPC_FISHERMAN), + 3308: (3815, lnames[3308], ('mss', 'ss', 'm', 'm', 3, 0, 3, 3, 1, 11, 1, 11, 1, 0), 'm', 0, NPC_REGULAR), + 3309: (3826, lnames[3309], ('hll', 'ls', 'm', 'm', 17, 0, 17, 17, 1, 11, 1, 11, 1, 18), 'm', 0, NPC_REGULAR), + 3310: (3823, lnames[3310], ('pll', 'ms', 'm', 'm', 10, 0, 10, 10, 60, 27, 49, 27, 0, 13), 'm', 0, NPC_REGULAR), + 3311: (3829, lnames[3311], 'r', 'f', 0, NPC_REGULAR), + 3312: (3813, lnames[3312], ('rss', 'ms', 'l', 'm', 4, 0, 4, 4, 5, 2, 5, 2, 1, 10), 'm', 0, NPC_REGULAR), + 3313: (3801, lnames[3313], ('css', 'ms', 'l', 'm', 9, 0, 9, 9, 0, 0, 0, 0, 1, 2), 'm', 0, NPC_HQ), + 3314: (3801, lnames[3314], ('cls', 'ms', 'l', 'f', 1, 0, 1, 1, 0, 0, 0, 0, 3, 25), 'f', 0, NPC_HQ), + 3315: (3801, lnames[3315], ('dsl', 'ls', 'l', 'm', 17, 0, 17, 17, 0, 0, 0, 0, 1, 17), 'm', 0, NPC_HQ), + 3316: (3801, lnames[3316], ('dss', 'md', 'l', 'f', 10, 0, 10, 10, 0, 1, 0, 1, 10, 27), 'f', 0, NPC_HQ), + 3317: (3816, lnames[3317], ('fll', 'ls', 'l', 'f', 1, 0, 1, 1, 0, 2, 0, 2, 3, 24), 'f', 0, NPC_REGULAR), + 3318: (3808, lnames[3318], ('dss', 'ms', 'm', 'm', 18, 0, 18, 18, 57, 1, 46, 1, 12, 1), 'm', 1, NPC_REGULAR), + 3319: (3825, lnames[3319], ('fls', 'ls', 'l', 'm', 9, 0, 9, 9, 1, 2, 1, 2, 1, 1), 'm', 0, NPC_REGULAR), + 3320: (3814, lnames[3320], ('rsl', 'ls', 'l', 'f', 1, 0, 1, 1, 1, 3, 1, 3, 12, 27), 'f', 0, NPC_REGULAR), + 3321: (3818, lnames[3321], ('rss', 'ss', 'l', 'm', 16, 0, 16, 16, 1, 2, 1, 2, 1, 16), 'm', 0, NPC_REGULAR), + 3322: (3819, lnames[3322], 'r', 'm', 0, NPC_REGULAR), + 3323: (3811, lnames[3323], ('mls', 'ms', 'l', 'm', 22, 0, 22, 22, 1, 3, 1, 3, 1, 10), 'm', 0, NPC_REGULAR), + 3324: (3809, lnames[3324], 'r', 'm', 1, NPC_REGULAR), + 3325: (3827, lnames[3325], ('hss', 'ls', 'l', 'm', 8, 0, 8, 8, 0, 4, 0, 4, 1, 0), 'm', 0, NPC_REGULAR), + 3326: (3820, lnames[3326], ('cll', 'md', 'l', 'f', 24, 0, 24, 24, 0, 6, 0, 6, 12, 27), 'f', 0, NPC_REGULAR), + 3327: (3824, lnames[3327], ('css', 'ms', 'l', 'm', 15, 0, 15, 15, 0, 5, 0, 5, 1, 15), 'm', 0, NPC_REGULAR), + 3328: (3807, lnames[3328], ('dll', 'sd', 'l', 'f', 8, 0, 8, 8, 0, 25, 0, 25, 14, 27), 'f', 1, NPC_REGULAR), + 3329: (3817, lnames[3329], ('dll', 'ms', 'l', 'm', 6, 0, 6, 6, 0, 1, 0, 1, 1, 1), 'm', 0, NPC_REGULAR), + 4001: (4502, lnames[4001], 'r', 'f', 0, NPC_REGULAR), + 4002: (4504, lnames[4002], ('fll', 'ss', 'm', 'm', 5, 0, 5, 5, 0, 2, 0, 2, 1, 17), 'm', 0, NPC_HQ), + 4003: (4504, lnames[4003], ('fsl', 'md', 'm', 'f', 21, 0, 21, 21, 0, 3, 0, 3, 10, 27), 'f', 0, NPC_HQ), + 4004: (4504, lnames[4004], ('fls', 'ls', 'm', 'f', 13, 0, 13, 13, 1, 3, 1, 3, 2, 11), 'f', 0, NPC_HQ), + 4005: (4504, lnames[4005], ('rll', 'ss', 'm', 'f', 4, 0, 4, 4, 1, 4, 1, 4, 24, 27), 'f', 0, NPC_HQ), + 4006: (4503, lnames[4006], ('rss', 'md', 'm', 'f', 21, 0, 21, 21, 1, 4, 1, 4, 8, 8), 'f', 0, NPC_CLERK), + 4007: (4503, lnames[4007], ('rls', 'ms', 'm', 'm', 12, 0, 12, 12, 1, 3, 1, 3, 1, 19), 'm', 0, NPC_CLERK), + 4008: (4506, lnames[4008], ('mls', 'ms', 'm', 'f', 4, 0, 4, 4, 1, 5, 1, 5, 7, 9), 'f', 0, NPC_TAILOR), + 4009: (4000, lnames[4009], ('hsl', 'ld', 'm', 'f', 19, 0, 19, 19, 1, 6, 1, 6, 12, 27), 'f', 0, NPC_FISHERMAN), + 4010: (4508, lnames[4010], ('hss', 'ms', 'm', 'm', 12, 0, 12, 12, 0, 5, 0, 5, 1, 10), 'm', 0, NPC_PETCLERK), + 4011: (4508, lnames[4011], ('cll', 'ss', 'm', 'm', 4, 0, 4, 4, 0, 5, 0, 5, 1, 4), 'm', 0, NPC_PETCLERK), + 4012: (4508, lnames[4012], ('csl', 'ss', 'm', 'f', 19, 0, 19, 19, 0, 8, 0, 8, 10, 27), 'f', 0, NPC_PETCLERK), + 4013: (4000, lnames[4013], ('bll', 'ls', 's', 'm', 3, 0, 19, 19, 0, 8, 0, 8, 1, 12), 'm', 1, NPC_PARTYPERSON), + 4014: (4000, lnames[4014], ('bss', 'md', 'm', 'f', 24, 0, 19, 19, 0, 24, 0, 24, 0, 12), 'f', 1, NPC_PARTYPERSON), + 4101: (4603, lnames[4101], ('cll', 'ms', 'm', 'm', 16, 0, 16, 16, 1, 7, 1, 7, 0, 6), 'm', 1, NPC_REGULAR), + 4102: (4605, lnames[4102], ('csl', 'ms', 'm', 'f', 9, 0, 9, 9, 1, 11, 1, 11, 10, 27), 'f', 1, NPC_REGULAR), + 4103: (4612, lnames[4103], ('cls', 'ls', 'l', 'm', 2, 0, 2, 2, 1, 8, 1, 8, 0, 19), 'm', 0, NPC_REGULAR), + 4104: (4659, lnames[4104], ('dll', 'ms', 'l', 'm', 16, 0, 16, 16, 1, 8, 1, 8, 0, 16), 'm', 0, NPC_HQ), + 4105: (4659, lnames[4105], ('dss', 'ls', 'l', 'f', 9, 0, 9, 9, 1, 21, 1, 21, 11, 27), 'f', 0, NPC_HQ), + 4106: (4659, lnames[4106], ('fll', 'ss', 'l', 'f', 24, 0, 24, 24, 0, 22, 0, 22, 19, 27), 'f', 0, NPC_HQ), + 4107: (4659, lnames[4107], ('fsl', 'md', 'l', 'f', 16, 0, 16, 16, 0, 22, 0, 22, 17, 27), 'f', 0, NPC_HQ), + 4108: (4626, lnames[4108], ('fls', 'ms', 'l', 'm', 8, 0, 8, 8, 0, 10, 0, 10, 0, 0), 'm', 0, NPC_REGULAR), + 4109: (4606, lnames[4109], ('rll', 'ss', 'l', 'm', 22, 0, 22, 22, 0, 11, 0, 11, 0, 18), 'm', 1, NPC_REGULAR), + 4110: (4604, lnames[4110], ('rss', 'ld', 'l', 'f', 16, 0, 16, 16, 0, 24, 0, 24, 3, 2), 'f', 1, NPC_REGULAR), + 4111: (4607, lnames[4111], 'r', 'm', 1, NPC_REGULAR), + 4112: (4609, lnames[4112], ('mls', 'ms', 'l', 'f', 24, 0, 24, 24, 0, 25, 0, 25, 11, 27), 'f', 1, NPC_REGULAR), + 4113: (4610, lnames[4113], ('hsl', 'ld', 'l', 'f', 15, 0, 15, 15, 1, 25, 1, 25, 14, 27), 'f', 0, NPC_REGULAR), + 4114: (4611, lnames[4114], ('hss', 'ms', 'l', 'm', 7, 0, 7, 7, 1, 11, 1, 11, 1, 20), 'm', 0, NPC_REGULAR), + 4115: (4614, lnames[4115], ('cll', 'ls', 'l', 'f', 23, 0, 23, 23, 1, 26, 1, 26, 10, 27), 'f', 0, NPC_REGULAR), + 4116: (4615, lnames[4116], ('csl', 'ss', 'm', 'm', 15, 0, 15, 15, 1, 12, 1, 12, 1, 14), 'm', 0, NPC_REGULAR), + 4117: (4617, lnames[4117], ('cls', 'md', 'm', 'f', 7, 0, 7, 7, 1, 0, 1, 0, 1, 25), 'f', 0, NPC_REGULAR), + 4118: (4618, lnames[4118], 'r', 'm', 0, NPC_REGULAR), + 4119: (4619, lnames[4119], ('dss', 'ss', 'm', 'm', 14, 0, 14, 14, 0, 0, 0, 0, 1, 1), 'm', 0, NPC_REGULAR), + 4120: (4622, lnames[4120], ('dls', 'ld', 'm', 'f', 7, 0, 7, 7, 0, 1, 0, 1, 26, 27), 'f', 0, NPC_REGULAR), + 4121: (4623, lnames[4121], ('fsl', 'ms', 'm', 'm', 21, 0, 21, 21, 0, 1, 0, 1, 1, 16), 'm', 0, NPC_REGULAR), + 4122: (4625, lnames[4122], ('fls', 'ms', 'm', 'f', 14, 0, 14, 14, 0, 2, 0, 2, 17, 27), 'f', 0, NPC_REGULAR), + 4123: (4628, lnames[4123], ('rll', 'ls', 'm', 'm', 6, 0, 6, 6, 0, 2, 0, 2, 1, 10), 'm', 0, NPC_REGULAR), + 4124: (4629, lnames[4124], ('rss', 'ms', 'm', 'm', 20, 0, 20, 20, 0, 2, 0, 2, 1, 4), 'm', 0, NPC_REGULAR), + 4125: (4630, lnames[4125], ('rls', 'ls', 'm', 'f', 14, 0, 14, 14, 1, 3, 1, 3, 8, 6), 'f', 0, NPC_REGULAR), + 4126: (4631, lnames[4126], ('mls', 'ss', 'm', 'm', 6, 0, 6, 6, 1, 3, 1, 3, 1, 18), 'm', 0, NPC_REGULAR), + 4127: (4632, lnames[4127], ('hll', 'md', 'm', 'f', 22, 0, 22, 22, 1, 4, 1, 4, 23, 27), 'f', 0, NPC_REGULAR), + 4128: (4635, lnames[4128], ('hss', 'ms', 'm', 'm', 13, 0, 13, 13, 1, 3, 1, 3, 1, 12), 'm', 0, NPC_REGULAR), + 4129: (4637, lnames[4129], ('hls', 'ss', 'l', 'f', 6, 0, 6, 6, 1, 5, 1, 5, 26, 27), 'f', 0, NPC_REGULAR), + 4130: (4638, lnames[4130], 'r', 'm', 0, NPC_REGULAR), + 4131: (4639, lnames[4131], 'r', 'm', 0, NPC_REGULAR), + 4132: (4641, lnames[4132], ('dll', 'ms', 'l', 'f', 6, 0, 6, 6, 0, 7, 0, 7, 17, 27), 'f', 0, NPC_REGULAR), + 4133: (4642, lnames[4133], ('dss', 'ls', 'l', 'm', 20, 0, 20, 20, 0, 5, 0, 5, 1, 14), 'm', 0, NPC_REGULAR), + 4134: (4645, lnames[4134], 'r', 'm', 0, NPC_REGULAR), + 4135: (4648, lnames[4135], ('fsl', 'ms', 'l', 'm', 5, 0, 5, 5, 0, 6, 0, 6, 1, 6), 'm', 0, NPC_REGULAR), + 4136: (4652, lnames[4136], ('fss', 'ss', 'l', 'f', 21, 0, 21, 21, 0, 9, 0, 9, 7, 4), 'f', 0, NPC_REGULAR), + 4137: (4654, lnames[4137], ('rll', 'ls', 'l', 'm', 13, 0, 13, 13, 1, 6, 1, 6, 1, 19), 'm', 0, NPC_REGULAR), + 4138: (4655, lnames[4138], ('rsl', 'ms', 'l', 'm', 5, 0, 5, 5, 1, 7, 1, 7, 1, 16), 'm', 0, NPC_REGULAR), + 4139: (4657, lnames[4139], ('rls', 'ss', 'l', 'f', 21, 0, 21, 21, 1, 11, 1, 11, 14, 27), 'f', 0, NPC_REGULAR), + 4140: (4658, lnames[4140], ('mls', 'ls', 'l', 'm', 12, 0, 12, 12, 1, 7, 1, 7, 1, 10), 'm', 0, NPC_REGULAR), + 4141: (4148, lnames[4141], ('hll', 'ms', 'l', 'm', 4, 0, 4, 4, 1, 8, 1, 8, 1, 4), 'm', 0, NPC_FISHERMAN), + 4201: (4704, lnames[4201], ('mss', 'ss', 'l', 'f', 14, 0, 14, 14, 0, 6, 0, 6, 11, 27), 'f', 1, NPC_REGULAR), + 4202: (4725, lnames[4202], ('mls', 'ls', 'l', 'm', 6, 0, 6, 6, 0, 5, 0, 5, 0, 13), 'm', 0, NPC_REGULAR), + 4203: (4702, lnames[4203], ('hsl', 'ms', 'l', 'm', 21, 0, 21, 21, 0, 5, 0, 5, 0, 10), 'm', 1, NPC_REGULAR), + 4204: (4739, lnames[4204], ('hss', 'ss', 'l', 'm', 14, 0, 14, 14, 0, 6, 0, 6, 0, 4), 'm', 0, NPC_HQ), + 4205: (4739, lnames[4205], ('cll', 'ld', 'l', 'f', 6, 0, 6, 6, 1, 8, 1, 8, 10, 27), 'f', 0, NPC_HQ), + 4206: (4739, lnames[4206], ('css', 'sd', 'l', 'f', 22, 0, 22, 22, 1, 8, 1, 8, 25, 27), 'f', 0, NPC_HQ), + 4207: (4739, lnames[4207], ('cls', 'ls', 'l', 'f', 14, 0, 14, 14, 1, 9, 1, 9, 17, 27), 'f', 0, NPC_HQ), + 4208: (4730, lnames[4208], ('dsl', 'ss', 'l', 'f', 6, 0, 6, 6, 1, 9, 1, 9, 10, 27), 'f', 0, NPC_REGULAR), + 4209: (4701, lnames[4209], ('dss', 'md', 'l', 'f', 22, 0, 22, 22, 1, 11, 1, 11, 1, 9), 'f', 1, NPC_REGULAR), + 4211: (4703, lnames[4211], ('fsl', 'ss', 'l', 'm', 5, 0, 5, 5, 1, 8, 1, 8, 0, 20), 'm', 1, NPC_REGULAR), + 4212: (4705, lnames[4212], ('fls', 'ls', 'l', 'm', 20, 0, 20, 20, 0, 9, 0, 9, 0, 17), 'm', 1, NPC_REGULAR), + 4213: (4707, lnames[4213], ('rll', 'sd', 'l', 'f', 13, 0, 13, 13, 0, 21, 0, 21, 24, 27), 'f', 1, NPC_REGULAR), + 4214: (4709, lnames[4214], 'r', 'f', 1, NPC_REGULAR), + 4215: (4710, lnames[4215], ('mss', 'ls', 'l', 'm', 19, 0, 19, 19, 0, 10, 0, 10, 0, 6), 'm', 0, NPC_REGULAR), + 4216: (4712, lnames[4216], ('mls', 'ms', 's', 'm', 13, 0, 13, 13, 0, 10, 0, 10, 0, 1), 'm', 0, NPC_REGULAR), + 4217: (4713, lnames[4217], ('hsl', 'ms', 's', 'm', 5, 0, 5, 5, 0, 10, 0, 10, 0, 19), 'm', 0, NPC_REGULAR), + 4218: (4716, lnames[4218], ('hss', 'ss', 's', 'f', 21, 0, 21, 21, 1, 23, 1, 23, 26, 27), 'f', 0, NPC_REGULAR), + 4219: (4717, lnames[4219], 'r', 'm', 0, NPC_REGULAR), + 4220: (4718, lnames[4220], 'r', 'm', 0, NPC_REGULAR), + 4221: (4719, lnames[4221], ('cls', 'ss', 's', 'm', 19, 0, 19, 19, 1, 11, 1, 11, 0, 4), 'm', 0, NPC_REGULAR), + 4222: (4720, lnames[4222], ('dsl', 'ls', 's', 'm', 12, 0, 12, 12, 1, 11, 1, 11, 0, 0), 'm', 0, NPC_REGULAR), + 4223: (4722, lnames[4223], ('dss', 'sd', 's', 'f', 3, 0, 3, 3, 1, 25, 1, 25, 24, 27), 'f', 0, NPC_REGULAR), + 4224: (4723, lnames[4224], 'r', 'm', 0, NPC_REGULAR), + 4225: (4724, lnames[4225], ('fsl', 'ld', 's', 'f', 12, 0, 12, 12, 0, 27, 0, 27, 11, 27), 'f', 0, NPC_REGULAR), + 4226: (4727, lnames[4226], ('fls', 'sd', 's', 'f', 3, 0, 3, 3, 0, 0, 0, 0, 11, 27), 'f', 0, NPC_REGULAR), + 4227: (4728, lnames[4227], ('rll', 'ls', 's', 'f', 19, 0, 19, 19, 0, 0, 0, 0, 23, 27), 'f', 0, NPC_REGULAR), + 4228: (4729, lnames[4228], ('rss', 'ss', 's', 'f', 11, 0, 11, 11, 0, 1, 0, 1, 0, 1), 'f', 0, NPC_REGULAR), + 4229: (4731, lnames[4229], ('rls', 'md', 'm', 'f', 3, 0, 3, 3, 0, 1, 0, 1, 26, 27), 'f', 0, NPC_REGULAR), + 4230: (4732, lnames[4230], ('mls', 'ms', 'm', 'm', 18, 0, 18, 18, 1, 1, 1, 1, 0, 14), 'm', 0, NPC_REGULAR), + 4231: (4735, lnames[4231], ('hsl', 'ss', 'm', 'f', 11, 0, 11, 11, 1, 2, 1, 2, 8, 0), 'f', 0, NPC_REGULAR), + 4232: (4736, lnames[4232], ('hss', 'ls', 'm', 'm', 3, 0, 3, 3, 1, 2, 1, 2, 1, 6), 'm', 0, NPC_REGULAR), + 4233: (4737, lnames[4233], ('cll', 'ms', 'm', 'm', 17, 0, 17, 17, 1, 2, 1, 2, 1, 1), 'm', 0, NPC_REGULAR), + 4234: (4738, lnames[4234], 'r', 'm', 0, NPC_REGULAR), + 4235: (4240, lnames[4235], ('cls', 'ls', 'm', 'm', 3, 0, 3, 3, 1, 3, 1, 3, 1, 16), 'm', 0, NPC_FISHERMAN), + 4301: (4819, lnames[4301], ('fss', 'md', 'l', 'f', 12, 0, 12, 12, 1, 2, 1, 2, 17, 27), 'f', 0, NPC_REGULAR), + 4302: (4821, lnames[4302], ('fls', 'ls', 'l', 'f', 3, 0, 3, 3, 1, 2, 1, 2, 2, 3), 'f', 0, NPC_REGULAR), + 4303: (4853, lnames[4303], ('rsl', 'ss', 'l', 'm', 18, 0, 18, 18, 1, 2, 1, 2, 0, 18), 'm', 0, NPC_REGULAR), + 4304: (4873, lnames[4304], ('rss', 'ls', 'm', 'm', 12, 0, 12, 12, 0, 2, 0, 2, 0, 15), 'm', 0, NPC_HQ), + 4305: (4873, lnames[4305], ('mss', 'sd', 'm', 'f', 3, 0, 3, 3, 0, 4, 0, 4, 26, 27), 'f', 0, NPC_HQ), + 4306: (4873, lnames[4306], ('hll', 'ms', 'm', 'f', 19, 0, 19, 19, 0, 5, 0, 5, 4, 25), 'f', 0, NPC_HQ), + 4307: (4873, lnames[4307], ('hsl', 'ld', 'm', 'f', 11, 0, 11, 11, 0, 5, 0, 5, 17, 27), 'f', 0, NPC_HQ), + 4308: (4835, lnames[4308], ('css', 'md', 'm', 'f', 6, 0, 6, 6, 3, 5, 3, 5, 0, 14), 'f', 0, NPC_REGULAR), + 4309: (4801, lnames[4309], ('cll', 'ms', 'm', 'm', 18, 0, 18, 18, 0, 4, 0, 4, 0, 17), 'm', 1, NPC_REGULAR), + 4310: (4803, lnames[4310], 'r', 'f', 1, NPC_REGULAR), + 4311: (4804, lnames[4311], 'r', 'f', 1, NPC_REGULAR), + 4312: (4807, lnames[4312], ('dsl', 'ms', 'm', 'm', 18, 0, 18, 18, 1, 5, 1, 5, 0, 9), 'm', 1, NPC_REGULAR), + 4313: (4809, lnames[4313], ('dss', 'ss', 'm', 'm', 10, 0, 10, 10, 1, 5, 1, 5, 0, 2), 'm', 1, NPC_REGULAR), + 4314: (4817, lnames[4314], ('fll', 'ld', 'm', 'f', 2, 0, 2, 2, 1, 8, 1, 8, 12, 27), 'f', 0, NPC_REGULAR), + 4315: (4827, lnames[4315], ('fss', 'sd', 'm', 'f', 18, 0, 18, 18, 1, 9, 1, 9, 26, 27), 'f', 0, NPC_REGULAR), + 4316: (4828, lnames[4316], ('fls', 'ss', 'm', 'm', 9, 0, 9, 9, 1, 6, 1, 6, 0, 14), 'm', 0, NPC_REGULAR), + 4317: (4829, lnames[4317], ('rsl', 'ls', 'l', 'm', 3, 0, 3, 3, 0, 7, 0, 7, 0, 11), 'm', 0, NPC_REGULAR), + 4318: (4836, lnames[4318], ('rss', 'ms', 'l', 'm', 17, 0, 17, 17, 0, 8, 0, 8, 0, 6), 'm', 0, NPC_REGULAR), + 4319: (4838, lnames[4319], ('mss', 'ls', 'l', 'f', 10, 0, 10, 10, 0, 12, 0, 12, 1, 23), 'f', 0, NPC_REGULAR), + 4320: (4840, lnames[4320], ('mls', 'ss', 'l', 'f', 1, 0, 1, 1, 0, 21, 0, 21, 11, 27), 'f', 0, NPC_REGULAR), + 4321: (4841, lnames[4321], ('hsl', 'ls', 'l', 'm', 17, 0, 17, 17, 0, 9, 0, 9, 0, 16), 'm', 0, NPC_REGULAR), + 4322: (4842, lnames[4322], ('hls', 'ms', 'l', 'm', 9, 0, 9, 9, 0, 9, 0, 9, 0, 13), 'm', 0, NPC_REGULAR), + 4323: (4844, lnames[4323], ('cll', 'ss', 'l', 'f', 1, 0, 1, 1, 1, 21, 1, 21, 24, 27), 'f', 0, NPC_REGULAR), + 4324: (4845, lnames[4324], ('css', 'ld', 'l', 'f', 17, 0, 17, 17, 1, 22, 1, 22, 10, 27), 'f', 0, NPC_REGULAR), + 4325: (4848, lnames[4325], ('cls', 'ms', 'l', 'm', 9, 0, 9, 9, 1, 9, 1, 9, 0, 0), 'm', 0, NPC_REGULAR), + 4326: (4850, lnames[4326], ('dsl', 'ms', 'l', 'f', 1, 0, 1, 1, 1, 23, 1, 23, 14, 27), 'f', 0, NPC_REGULAR), + 4327: (4852, lnames[4327], ('dss', 'ld', 'l', 'f', 16, 0, 16, 16, 1, 23, 1, 23, 7, 1), 'f', 0, NPC_REGULAR), + 4328: (4854, lnames[4328], ('fll', 'ms', 'l', 'm', 8, 0, 8, 8, 1, 11, 1, 11, 0, 12), 'm', 0, NPC_REGULAR), + 4329: (4855, lnames[4329], ('fsl', 'ls', 'l', 'f', 24, 0, 24, 24, 0, 25, 0, 25, 26, 27), 'f', 0, NPC_REGULAR), + 4330: (4862, lnames[4330], 'r', 'm', 0, NPC_REGULAR), + 4331: (4867, lnames[4331], 'r', 'm', 0, NPC_REGULAR), + 4332: (4870, lnames[4332], ('rss', 'ms', 'l', 'm', 22, 0, 22, 22, 0, 27, 0, 27, 0, 17), 'm', 0, NPC_REGULAR), + 4333: (4871, lnames[4333], ('mss', 'ss', 'l', 'm', 15, 0, 15, 15, 0, 27, 0, 27, 0, 14), 'm', 0, NPC_REGULAR), + 4334: (4872, lnames[4334], ('mls', 'ls', 'l', 'm', 8, 0, 8, 8, 0, 0, 0, 0, 0, 11), 'm', 0, NPC_REGULAR), + 4335: (4345, lnames[4335], ('hsl', 'ms', 'l', 'm', 22, 0, 22, 22, 1, 0, 1, 0, 0, 6), 'm', 0, NPC_FISHERMAN), + 5001: (5502, lnames[5001], ('fls', 'ls', 's', 'm', 14, 0, 14, 14, 1, 7, 1, 7, 0, 20), 'm', 0, NPC_HQ), + 5002: (5502, lnames[5002], ('rll', 'ms', 's', 'm', 6, 0, 6, 6, 0, 8, 0, 8, 0, 17), 'm', 0, NPC_HQ), + 5003: (5502, lnames[5003], ('rss', 'ms', 's', 'f', 22, 0, 22, 22, 0, 12, 0, 12, 26, 27), 'f', 0, NPC_HQ), + 5004: (5502, lnames[5004], ('rls', 'ld', 's', 'f', 13, 0, 13, 13, 0, 21, 0, 21, 4, 11), 'f', 0, NPC_HQ), + 5005: (5501, lnames[5005], ('mls', 'md', 's', 'f', 6, 0, 6, 6, 0, 21, 0, 21, 2, 3), 'f', 0, NPC_CLERK), + 5006: (5501, lnames[5006], ('hsl', 'ms', 's', 'm', 20, 0, 20, 20, 0, 9, 0, 9, 0, 1), 'm', 0, NPC_CLERK), + 5007: (5503, lnames[5007], ('hss', 'ss', 's', 'f', 13, 0, 13, 13, 0, 22, 0, 22, 3, 2), 'f', 0, NPC_TAILOR), + 5008: (5000, lnames[5008], ('cll', 'md', 's', 'f', 4, 0, 4, 4, 1, 22, 1, 22, 19, 27), 'f', 0, NPC_FISHERMAN), + 5009: (5505, lnames[5009], ('csl', 'ls', 'm', 'f', 21, 0, 21, 21, 1, 23, 1, 23, 8, 23), 'f', 0, NPC_PETCLERK), + 5010: (5505, lnames[5010], ('cls', 'ss', 'm', 'm', 13, 0, 13, 13, 1, 10, 1, 10, 0, 10), 'm', 0, NPC_PETCLERK), + 5011: (5505, lnames[5011], ('dll', 'ls', 'm', 'm', 5, 0, 5, 5, 1, 10, 1, 10, 0, 4), 'm', 0, NPC_PETCLERK), + 5012: (5000, lnames[5012], ('dls', 'ms', 'm', 'm', 13, 0, 12, 12, 0, 1, 0, 1, 0, 6), 'm', 1, NPC_PARTYPERSON), + 5013: (5000, lnames[5013], ('dss', 'md', 'm', 'f', 1, 0, 3, 3, 1, 5, 1, 5, 0, 5), 'f', 1, NPC_PARTYPERSON), + 5101: (5602, lnames[5101], ('dsl', 'ms', 'l', 'm', 10, 0, 10, 10, 1, 4, 1, 4, 0, 11), 'm', 1, NPC_REGULAR), + 5102: (5610, lnames[5102], 'r', 'f', 0, NPC_REGULAR), + 5103: (5615, lnames[5103], ('fll', 'ls', 'l', 'm', 18, 0, 18, 18, 1, 5, 1, 5, 0, 1), 'm', 0, NPC_REGULAR), + 5104: (5617, lnames[5104], ('fsl', 'ms', 'l', 'm', 10, 0, 10, 10, 1, 5, 1, 5, 0, 19), 'm', 0, NPC_REGULAR), + 5105: (5619, lnames[5105], 'r', 'm', 0, NPC_REGULAR), + 5106: (5613, lnames[5106], ('rsl', 'ls', 'l', 'm', 18, 0, 18, 18, 1, 6, 1, 6, 0, 13), 'm', 0, NPC_REGULAR), + 5107: (5607, lnames[5107], 'r', 'm', 1, NPC_REGULAR), + 5108: (5616, lnames[5108], ('mss', 'ls', 'l', 'f', 2, 0, 2, 2, 0, 11, 0, 11, 24, 27), 'f', 0, NPC_REGULAR), + 5109: (5627, lnames[5109], ('mls', 'ss', 'l', 'm', 17, 0, 17, 17, 0, 7, 0, 7, 0, 0), 'm', 1, NPC_HQ), + 5110: (5627, lnames[5110], ('hsl', 'ls', 'l', 'm', 10, 0, 10, 10, 0, 8, 0, 8, 0, 18), 'm', 1, NPC_HQ), + 5111: (5627, lnames[5111], ('hss', 'ls', 'l', 'f', 2, 0, 2, 2, 0, 12, 0, 12, 7, 4), 'f', 1, NPC_HQ), + 5112: (5627, lnames[5112], ('cll', 'ms', 'l', 'f', 17, 0, 17, 17, 0, 21, 0, 21, 14, 27), 'f', 1, NPC_HQ), + 5113: (5601, lnames[5113], ('css', 'ld', 'l', 'f', 10, 0, 10, 10, 0, 21, 0, 21, 3, 2), 'f', 1, NPC_REGULAR), + 5114: (5603, lnames[5114], ('cls', 'ms', 'l', 'm', 2, 0, 2, 2, 1, 9, 1, 9, 0, 2), 'm', 1, NPC_REGULAR), + 5115: (5604, lnames[5115], ('dsl', 'ms', 'l', 'f', 17, 0, 17, 17, 1, 22, 1, 22, 10, 27), 'f', 1, NPC_REGULAR), + 5116: (5605, lnames[5116], ('dss', 'ls', 'l', 'm', 9, 0, 9, 9, 1, 9, 1, 9, 0, 17), 'm', 1, NPC_REGULAR), + 5117: (5606, lnames[5117], ('fll', 'md', 'l', 'f', 1, 0, 1, 1, 1, 23, 1, 23, 17, 27), 'f', 1, NPC_REGULAR), + 5118: (5608, lnames[5118], ('fsl', 'ms', 'l', 'm', 16, 0, 16, 16, 1, 10, 1, 10, 0, 11), 'm', 1, NPC_REGULAR), + 5119: (5609, lnames[5119], ('fls', 'ss', 'l', 'm', 9, 0, 9, 9, 1, 10, 1, 10, 0, 6), 'm', 1, NPC_REGULAR), + 5120: (5611, lnames[5120], ('rsl', 'ss', 'l', 'm', 22, 0, 22, 22, 1, 3, 1, 3, 1, 19), 'm', 0, NPC_REGULAR), + 5121: (5618, lnames[5121], ('rss', 'ss', 'l', 'f', 23, 0, 23, 23, 1, 9, 1, 9, 0, 25), 'f', 0, NPC_REGULAR), + 5122: (5620, lnames[5122], 'r', 'm', 0, NPC_REGULAR), + 5123: (5621, lnames[5123], ('mls', 'sd', 'm', 'f', 7, 0, 7, 7, 1, 11, 1, 11, 25, 27), 'f', 0, NPC_REGULAR), + 5124: (5622, lnames[5124], ('hll', 'ss', 'm', 'm', 21, 0, 21, 21, 0, 8, 0, 8, 0, 4), 'm', 0, NPC_REGULAR), + 5125: (5623, lnames[5125], ('hss', 'ls', 'm', 'm', 14, 0, 14, 14, 0, 9, 0, 9, 0, 0), 'm', 0, NPC_REGULAR), + 5126: (5624, lnames[5126], ('hls', 'sd', 'm', 'f', 7, 0, 7, 7, 0, 21, 0, 21, 14, 27), 'f', 0, NPC_REGULAR), + 5127: (5625, lnames[5127], ('csl', 'ms', 'm', 'f', 23, 0, 23, 23, 0, 22, 0, 22, 2, 2), 'f', 0, NPC_REGULAR), + 5128: (5626, lnames[5128], 'r', 'f', 0, NPC_REGULAR), + 5129: (5139, lnames[5129], ('dll', 'md', 'm', 'f', 7, 0, 7, 7, 0, 23, 0, 23, 17, 27), 'f', 0, NPC_FISHERMAN), + 5201: (5702, lnames[5201], ('hls', 'ls', 'l', 'm', 15, 0, 15, 15, 1, 10, 1, 10, 1, 16), 'm', 1, NPC_REGULAR), + 5202: (5703, lnames[5202], ('cll', 'ls', 'l', 'f', 7, 0, 7, 7, 1, 23, 1, 23, 11, 27), 'f', 1, NPC_REGULAR), + 5203: (5704, lnames[5203], ('css', 'ss', 'l', 'f', 23, 0, 23, 23, 1, 24, 1, 24, 19, 27), 'f', 1, NPC_REGULAR), + 5204: (5726, lnames[5204], ('cls', 'ls', 'l', 'm', 14, 0, 14, 14, 0, 12, 0, 12, 1, 4), 'm', 0, NPC_REGULAR), + 5205: (5718, lnames[5205], 'r', 'm', 0, NPC_REGULAR), + 5206: (5720, lnames[5206], ('dss', 'ss', 'l', 'm', 21, 0, 21, 21, 0, 27, 0, 27, 1, 18), 'm', 0, NPC_REGULAR), + 5207: (5717, lnames[5207], ('fll', 'ld', 'l', 'f', 14, 0, 14, 14, 0, 27, 0, 27, 26, 27), 'f', 0, NPC_REGULAR), + 5208: (5719, lnames[5208], ('fsl', 'sd', 'l', 'f', 7, 0, 7, 7, 0, 27, 0, 27, 1, 12), 'f', 0, NPC_REGULAR), + 5209: (5728, lnames[5209], ('fls', 'ss', 'l', 'm', 21, 0, 21, 21, 0, 0, 0, 0, 1, 9), 'm', 1, NPC_HQ), + 5210: (5728, lnames[5210], ('rsl', 'ss', 'l', 'm', 14, 0, 14, 14, 1, 0, 1, 0, 1, 2), 'm', 1, NPC_HQ), + 5211: (5728, lnames[5211], ('rss', 'md', 'l', 'f', 6, 0, 6, 6, 1, 1, 1, 1, 23, 27), 'f', 1, NPC_HQ), + 5212: (5728, lnames[5212], ('mss', 'ls', 'l', 'f', 22, 0, 22, 22, 1, 1, 1, 1, 10, 27), 'f', 1, NPC_HQ), + 5213: (5701, lnames[5213], ('mls', 'ss', 'l', 'm', 13, 0, 13, 13, 1, 1, 1, 1, 1, 14), 'm', 1, NPC_REGULAR), + 5214: (5705, lnames[5214], ('hsl', 'md', 'l', 'f', 6, 0, 6, 6, 1, 2, 1, 2, 17, 27), 'f', 1, NPC_REGULAR), + 5215: (5706, lnames[5215], 'r', 'f', 1, NPC_REGULAR), + 5216: (5707, lnames[5216], ('cll', 'ss', 'l', 'm', 13, 0, 13, 13, 0, 2, 0, 2, 1, 1), 'm', 1, NPC_REGULAR), + 5217: (5708, lnames[5217], ('csl', 'ls', 'l', 'm', 5, 0, 5, 5, 0, 3, 0, 3, 1, 19), 'm', 1, NPC_REGULAR), + 5218: (5709, lnames[5218], 'r', 'm', 1, NPC_REGULAR), + 5219: (5710, lnames[5219], ('dsl', 'ss', 'l', 'm', 13, 0, 13, 13, 0, 3, 0, 3, 1, 13), 'm', 0, NPC_REGULAR), + 5220: (5711, lnames[5220], 'r', 'f', 0, NPC_REGULAR), + 5221: (5712, lnames[5221], ('fll', 'md', 'l', 'f', 21, 0, 21, 21, 0, 6, 0, 6, 25, 27), 'f', 0, NPC_REGULAR), + 5222: (5713, lnames[5222], 'r', 'f', 0, NPC_REGULAR), + 5223: (5714, lnames[5223], ('fls', 'ss', 's', 'm', 5, 0, 5, 5, 1, 4, 1, 4, 1, 18), 'm', 0, NPC_REGULAR), + 5224: (5715, lnames[5224], ('rll', 'ls', 's', 'm', 19, 0, 19, 19, 1, 5, 1, 5, 1, 15), 'm', 0, NPC_REGULAR), + 5225: (5716, lnames[5225], ('rss', 'sd', 's', 'f', 12, 0, 12, 12, 1, 7, 1, 7, 10, 27), 'f', 0, NPC_REGULAR), + 5226: (5721, lnames[5226], ('rls', 'ss', 's', 'm', 4, 0, 4, 4, 1, 5, 1, 5, 1, 9), 'm', 0, NPC_REGULAR), + 5227: (5725, lnames[5227], ('mls', 'ld', 's', 'f', 19, 0, 19, 19, 1, 8, 1, 8, 23, 27), 'f', 0, NPC_REGULAR), + 5228: (5727, lnames[5228], ('hsl', 'ms', 's', 'm', 12, 0, 12, 12, 1, 6, 1, 6, 1, 20), 'm', 0, NPC_REGULAR), + 5229: (5245, lnames[5229], ('hss', 'ms', 's', 'f', 3, 0, 3, 3, 0, 11, 0, 11, 16, 27), 'f', 0, NPC_FISHERMAN), + 5301: (5802, lnames[5301], ('rss', 'ms', 'l', 'f', 13, 0, 13, 13, 0, 11, 0, 11, 1, 12), 'f', 1, NPC_HQ), + 5302: (5802, lnames[5302], ('mss', 'ss', 'l', 'f', 4, 0, 4, 4, 0, 12, 0, 12, 17, 27), 'f', 1, NPC_HQ), + 5303: (5802, lnames[5303], ('hll', 'ls', 'l', 'm', 19, 0, 19, 19, 1, 8, 1, 8, 1, 18), 'm', 1, NPC_HQ), + 5304: (5802, lnames[5304], ('hsl', 'ls', 'l', 'f', 12, 0, 12, 12, 1, 12, 1, 12, 19, 27), 'f', 1, NPC_HQ), + 5305: (5804, lnames[5305], ('hls', 'ss', 'l', 'f', 4, 0, 4, 4, 1, 21, 1, 21, 16, 27), 'f', 1, NPC_REGULAR), + 5306: (5805, lnames[5306], 'r', 'm', 1, NPC_REGULAR), + 5307: (5809, lnames[5307], ('css', 'ms', 'l', 'm', 12, 0, 12, 12, 1, 9, 1, 9, 1, 2), 'm', 1, NPC_REGULAR), + 5308: (5810, lnames[5308], ('cls', 'ms', 'l', 'f', 4, 0, 4, 4, 1, 22, 1, 22, 10, 27), 'f', 0, NPC_REGULAR), + 5309: (5811, lnames[5309], 'r', 'f', 0, NPC_REGULAR), + 5310: (5815, lnames[5310], ('dls', 'ms', 'l', 'm', 12, 0, 12, 12, 0, 11, 0, 11, 1, 14), 'm', 0, NPC_REGULAR), + 5311: (5817, lnames[5311], ('fll', 'ms', 'm', 'f', 3, 0, 3, 3, 0, 24, 0, 24, 12, 27), 'f', 0, NPC_REGULAR), + 5312: (5819, lnames[5312], ('fss', 'ss', 'm', 'm', 18, 0, 18, 18, 0, 12, 0, 12, 1, 6), 'm', 0, NPC_REGULAR), + 5313: (5821, lnames[5313], ('fls', 'ls', 'm', 'm', 10, 0, 10, 10, 0, 12, 0, 12, 1, 1), 'm', 0, NPC_REGULAR), + 5314: (5826, lnames[5314], 'r', 'f', 0, NPC_REGULAR), + 5315: (5827, lnames[5315], ('rss', 'ss', 'm', 'm', 18, 0, 18, 18, 1, 12, 1, 12, 1, 16), 'm', 0, NPC_REGULAR), + 5316: (5828, lnames[5316], ('mss', 'ls', 'm', 'm', 10, 0, 10, 10, 1, 12, 1, 12, 1, 13), 'm', 0, NPC_REGULAR), + 5317: (5830, lnames[5317], 'r', 'm', 0, NPC_REGULAR), + 5318: (5833, lnames[5318], ('hsl', 'ss', 'm', 'm', 18, 0, 18, 18, 1, 0, 1, 0, 1, 4), 'm', 0, NPC_REGULAR), + 5319: (5835, lnames[5319], 'r', 'f', 0, NPC_REGULAR), + 5320: (5836, lnames[5320], ('cll', 'sd', 'm', 'f', 2, 0, 2, 2, 1, 1, 1, 1, 17, 27), 'f', 0, NPC_REGULAR), + 5321: (5837, lnames[5321], ('css', 'ms', 'm', 'f', 18, 0, 18, 18, 1, 1, 1, 1, 17, 27), 'f', 0, NPC_REGULAR), + 5322: (5318, lnames[5322], ('cls', 'ss', 'm', 'f', 10, 0, 10, 10, 0, 2, 0, 2, 11, 27), 'f', 0, NPC_FISHERMAN), + 6000: (6000, lnames[6000], ('hsl', 'ms', 'm', 'm', 8, 0, 8, 8, 1, 6, 1, 6, 0, 18), 'm', 0, NPC_FISHERMAN), + 8001: (8501, lnames[8001], ('psl', 'ms', 'm', 'm', 13, 0, 13, 13, 0, 11, 0, 11, 2, 10), 'm', 0, NPC_KARTCLERK), + 8002: (8501, lnames[8002], ('psl', 'ld', 's', 'f', 23, 0, 23, 23, 0, 11, 0, 11, 2, 10), 'f', 0, NPC_KARTCLERK), + 8003: (8501, lnames[8003], ('pll', 'ss', 'l', 'f', 1, 0, 1, 1, 0, 11, 0, 11, 2, 10), 'f', 0, NPC_KARTCLERK), + 8004: (8501, lnames[8004], ('pls', 'ms', 'l', 'm', 16, 0, 16, 16, 0, 11, 0, 11, 2, 10), 'm', 0, NPC_KARTCLERK), + 9001: (9503, lnames[9001], ('fll', 'ss', 'l', 'f', 16, 0, 16, 16, 0, 6, 0, 6, 26, 27), 'f', 0, NPC_REGULAR), + 9002: (9502, lnames[9002], 'r', 'm', 0, NPC_REGULAR), + 9003: (9501, lnames[9003], ('fls', 'ms', 'l', 'm', 22, 0, 22, 22, 1, 5, 1, 5, 0, 14), 'm', 0, NPC_REGULAR), + 9004: (9505, lnames[9004], ('rll', 'ms', 'l', 'f', 16, 0, 16, 16, 1, 7, 1, 7, 3, 8), 'f', 1, NPC_HQ), + 9005: (9505, lnames[9005], ('rss', 'ld', 'l', 'f', 9, 0, 9, 9, 1, 8, 1, 8, 19, 27), 'f', 1, NPC_HQ), + 9006: (9505, lnames[9006], ('rls', 'ms', 'l', 'm', 22, 0, 22, 22, 1, 6, 1, 6, 0, 1), 'm', 1, NPC_HQ), + 9007: (9505, lnames[9007], ('mls', 'ms', 'l', 'm', 15, 0, 15, 15, 1, 6, 1, 6, 0, 19), 'm', 1, NPC_HQ), + 9008: (9504, lnames[9008], ('hll', 'ss', 'l', 'f', 8, 0, 8, 8, 1, 9, 1, 9, 12, 27), 'f', 0, NPC_CLERK), + 9009: (9504, lnames[9009], ('hss', 'ls', 'l', 'm', 22, 0, 22, 22, 0, 7, 0, 7, 0, 13), 'm', 0, NPC_CLERK), + 9010: (9506, lnames[9010], ('cll', 'ms', 'l', 'm', 15, 0, 15, 15, 0, 8, 0, 8, 0, 10), 'm', 0, NPC_TAILOR), + 9011: (9000, lnames[9011], ('csl', 'ss', 'l', 'm', 7, 0, 7, 7, 0, 8, 0, 8, 0, 4), 'm', 0, NPC_FISHERMAN), + 9012: (9508, lnames[9012], ('cls', 'ld', 'l', 'f', 23, 0, 23, 23, 0, 21, 0, 21, 10, 27), 'f', 0, NPC_PETCLERK), + 9013: (9508, lnames[9013], ('dll', 'sd', 'l', 'f', 15, 0, 15, 15, 0, 21, 0, 21, 10, 27), 'f', 0, NPC_PETCLERK), + 9014: (9508, lnames[9014], ('dss', 'ss', 'l', 'm', 7, 0, 7, 7, 0, 9, 0, 9, 1, 15), 'm', 0, NPC_PETCLERK), + 9015: (9000, lnames[9015], ('rss', 'ls', 'l', 'm', 21, 0, 20, 20, 0, 12, 0, 12, 0, 11), 'm', 1, NPC_PARTYPERSON), + 9016: (9000, lnames[9016], ('rls', 'md', 'l', 'f', 6, 0, 21, 21, 1, 11, 1, 11, 0, 11), 'f', 1, NPC_PARTYPERSON), + 9101: (9604, lnames[9101], ('css', 'ls', 'l', 'm', 14, 0, 14, 14, 1, 1, 1, 1, 0, 11), 'm', 1, NPC_REGULAR), + 9102: (9607, lnames[9102], 'r', 'f', 1, NPC_REGULAR), + 9103: (9620, lnames[9103], ('dsl', 'ss', 'l', 'm', 20, 0, 20, 20, 0, 2, 0, 2, 0, 1), 'm', 0, NPC_REGULAR), + 9104: (9642, lnames[9104], ('dss', 'ld', 'l', 'f', 14, 0, 14, 14, 0, 3, 0, 3, 0, 23), 'f', 0, NPC_REGULAR), + 9105: (9609, lnames[9105], 'r', 'm', 1, NPC_REGULAR), + 9106: (9619, lnames[9106], ('fsl', 'ss', 'l', 'm', 20, 0, 20, 20, 0, 3, 0, 3, 0, 13), 'm', 0, NPC_REGULAR), + 9107: (9601, lnames[9107], ('fls', 'ld', 'l', 'f', 13, 0, 13, 13, 0, 5, 0, 5, 3, 2), 'f', 1, NPC_REGULAR), + 9108: (9602, lnames[9108], ('rll', 'ms', 'l', 'm', 6, 0, 6, 6, 1, 4, 1, 4, 0, 4), 'm', 1, NPC_REGULAR), + 9109: (9605, lnames[9109], ('rss', 'ls', 'l', 'f', 22, 0, 22, 22, 1, 6, 1, 6, 10, 27), 'f', 1, NPC_REGULAR), + 9110: (9608, lnames[9110], ('mss', 'ss', 'l', 'f', 13, 0, 13, 13, 1, 6, 1, 6, 25, 27), 'f', 1, NPC_REGULAR), + 9111: (9616, lnames[9111], 'r', 'f', 0, NPC_REGULAR), + 9112: (9617, lnames[9112], ('hsl', 'ms', 'm', 'm', 19, 0, 19, 19, 1, 5, 1, 5, 0, 12), 'm', 0, NPC_REGULAR), + 9113: (9622, lnames[9113], ('hss', 'ss', 'm', 'm', 13, 0, 13, 13, 1, 5, 1, 5, 0, 9), 'm', 0, NPC_REGULAR), + 9114: (9625, lnames[9114], ('cll', 'ld', 'm', 'f', 4, 0, 4, 4, 0, 8, 0, 8, 10, 27), 'f', 0, NPC_REGULAR), + 9115: (9626, lnames[9115], 'r', 'm', 0, NPC_REGULAR), + 9116: (9627, lnames[9116], ('cls', 'ss', 'm', 'm', 12, 0, 12, 12, 0, 7, 0, 7, 0, 17), 'm', 0, NPC_REGULAR), + 9117: (9628, lnames[9117], ('dsl', 'ld', 'm', 'f', 4, 0, 4, 4, 0, 11, 0, 11, 2, 9), 'f', 0, NPC_REGULAR), + 9118: (9629, lnames[9118], 'r', 'f', 0, NPC_REGULAR), + 9119: (9630, lnames[9119], ('fll', 'ms', 'm', 'm', 12, 0, 12, 12, 0, 8, 0, 8, 0, 6), 'm', 0, NPC_REGULAR), + 9120: (9631, lnames[9120], 'r', 'f', 0, NPC_REGULAR), + 9121: (9634, lnames[9121], ('fls', 'md', 'm', 'f', 19, 0, 19, 19, 1, 12, 1, 12, 16, 27), 'f', 0, NPC_REGULAR), + 9122: (9636, lnames[9122], ('rll', 'ms', 'm', 'm', 12, 0, 12, 12, 1, 8, 1, 8, 0, 16), 'm', 0, NPC_REGULAR), + 9123: (9639, lnames[9123], ('rss', 'ss', 'm', 'm', 4, 0, 4, 4, 1, 9, 1, 9, 0, 13), 'm', 0, NPC_REGULAR), + 9124: (9640, lnames[9124], ('rls', 'md', 'm', 'f', 19, 0, 19, 19, 1, 22, 1, 22, 8, 9), 'f', 0, NPC_REGULAR), + 9125: (9643, lnames[9125], ('mls', 'ms', 'l', 'm', 10, 0, 10, 10, 1, 9, 1, 9, 0, 4), 'm', 0, NPC_REGULAR), + 9126: (9644, lnames[9126], ('hsl', 'ms', 'l', 'f', 3, 0, 3, 3, 1, 23, 1, 23, 23, 27), 'f', 0, NPC_REGULAR), + 9127: (9645, lnames[9127], ('hss', 'ld', 'l', 'f', 19, 0, 19, 19, 0, 24, 0, 24, 10, 27), 'f', 0, NPC_REGULAR), + 9128: (9647, lnames[9128], ('cll', 'ms', 'l', 'm', 10, 0, 10, 10, 0, 11, 0, 11, 0, 15), 'm', 0, NPC_REGULAR), + 9129: (9649, lnames[9129], ('csl', 'ms', 'l', 'f', 3, 0, 3, 3, 0, 25, 0, 25, 25, 27), 'f', 0, NPC_REGULAR), + 9130: (9650, lnames[9130], ('cls', 'ss', 'l', 'm', 18, 0, 18, 18, 0, 12, 0, 12, 0, 9), 'm', 0, NPC_REGULAR), + 9131: (9651, lnames[9131], 'r', 'f', 0, NPC_REGULAR), + 9132: (9652, lnames[9132], ('dss', 'ls', 'l', 'f', 2, 0, 2, 2, 0, 27, 0, 27, 0, 0), 'f', 0, NPC_HQ), + 9133: (9652, lnames[9133], ('dls', 'ss', 'l', 'm', 17, 0, 17, 17, 1, 12, 1, 12, 0, 17), 'm', 0, NPC_HQ), + 9134: (9652, lnames[9134], ('fsl', 'ls', 'l', 'm', 10, 0, 10, 10, 1, 0, 1, 0, 0, 14), 'm', 0, NPC_HQ), + 9135: (9652, lnames[9135], ('fls', 'ms', 'l', 'm', 3, 0, 3, 3, 1, 0, 1, 0, 0, 11), 'm', 0, NPC_HQ), + 9136: (9153, lnames[9136], ('rll', 'ss', 'l', 'm', 17, 0, 17, 17, 1, 0, 1, 0, 1, 6), 'm', 0, NPC_FISHERMAN), + 9201: (9752, lnames[9201], ('psl', 'ss', 'm', 'm', 9, 0, 9, 9, 17, 11, 0, 11, 7, 20), 'm', 0, NPC_REGULAR), + 9202: (9703, lnames[9202], ('dss', 'ss', 's', 'm', 21, 0, 21, 21, 8, 3, 8, 3, 1, 17), 'm', 1, NPC_REGULAR), + 9203: (9741, lnames[9203], ('pls', 'ls', 's', 'm', 5, 0, 5, 5, 37, 27, 26, 27, 7, 4), 'm', 0, NPC_REGULAR), + 9204: (9704, lnames[9204], ('fsl', 'sd', 's', 'f', 19, 0, 19, 19, 21, 10, 0, 10, 8, 23), 'f', 1, NPC_REGULAR), + 9205: (9736, lnames[9205], ('dsl', 'ms', 'm', 'm', 15, 0, 15, 15, 45, 27, 34, 27, 2, 17), 'm', 0, NPC_REGULAR), + 9206: (9727, lnames[9206], ('rls', 'ld', 'l', 'f', 8, 0, 8, 8, 25, 27, 16, 27, 10, 27), 'f', 0, NPC_REGULAR), + 9207: (9709, lnames[9207], ('hss', 'ss', 's', 'f', 24, 0, 24, 24, 36, 27, 25, 27, 9, 27), 'f', 1, NPC_REGULAR), + 9208: (9705, lnames[9208], ('dsl', 'ms', 's', 'm', 20, 0, 20, 20, 46, 27, 35, 27, 6, 27), 'm', 1, NPC_REGULAR), + 9209: (9706, lnames[9209], ('pll', 'ss', 'm', 'm', 13, 0, 13, 13, 8, 12, 8, 12, 1, 12), 'm', 1, NPC_REGULAR), + 9210: (9740, lnames[9210], ('hsl', 'ls', 'l', 'm', 6, 0, 6, 6, 1, 0, 1, 0, 0, 0), 'm', 0, NPC_REGULAR), + 9211: (9707, lnames[9211], ('rll', 'ss', 's', 'f', 3, 0, 3, 3, 22, 22, 0, 22, 6, 22), 'f', 1, NPC_REGULAR), + 9212: (9753, lnames[9212], ('pss', 'md', 'm', 'f', 16, 0, 16, 16, 45, 27, 34, 27, 0, 3), 'f', 0, NPC_REGULAR), + 9213: (9711, lnames[9213], ('fsl', 'ss', 'm', 'm', 2, 0, 2, 2, 37, 27, 26, 27, 7, 18), 'm', 0, NPC_REGULAR), + 9214: (9710, lnames[9214], ('rll', 'ls', 'l', 'm', 18, 0, 18, 18, 10, 27, 0, 27, 0, 13), 'm', 0, NPC_REGULAR), + 9215: (9744, lnames[9215], ('csl', 'ls', 'l', 'm', 18, 0, 18, 18, 11, 4, 0, 4, 0, 4), 'm', 0, NPC_REGULAR), + 9216: (9725, lnames[9216], ('csl', 'sd', 'm', 'f', 14, 0, 14, 14, 1, 7, 1, 7, 3, 7), 'f', 0, NPC_REGULAR), + 9217: (9713, lnames[9217], ('mss', 'ms', 'm', 'f', 17, 0, 17, 17, 20, 26, 0, 26, 5, 12), 'f', 0, NPC_REGULAR), + 9218: (9737, lnames[9218], ('dss', 'md', 'l', 'f', 23, 0, 23, 23, 24, 27, 15, 27, 11, 27), 'f', 0, NPC_REGULAR), + 9219: (9712, lnames[9219], ('hll', 'sd', 'l', 'f', 10, 0, 10, 10, 9, 22, 9, 22, 12, 27), 'f', 0, NPC_REGULAR), + 9220: (9716, lnames[9220], ('mls', 'ms', 'l', 'm', 7, 0, 7, 7, 0, 27, 0, 27, 1, 10), 'm', 0, NPC_REGULAR), + 9221: (9738, lnames[9221], ('fss', 'md', 'l', 'f', 22, 0, 22, 22, 45, 27, 34, 27, 0, 6), 'f', 0, NPC_REGULAR), + 9222: (9754, lnames[9222], ('hsl', 'ls', 'l', 'm', 10, 0, 10, 10, 52, 27, 41, 27, 12, 27), 'm', 0, NPC_REGULAR), + 9223: (9714, lnames[9223], ('fsl', 'ms', 'm', 'm', 20, 0, 20, 20, 43, 27, 32, 27, 0, 0), 'm', 0, NPC_REGULAR), + 9224: (9718, lnames[9224], ('css', 'ms', 'm', 'f', 1, 0, 1, 1, 6, 8, 6, 8, 6, 8), 'f', 0, NPC_REGULAR), + 9225: (9717, lnames[9225], ('rss', 'md', 'm', 'f', 11, 0, 11, 11, 40, 27, 29, 27, 0, 27), 'f', 0, NPC_REGULAR), + 9226: (9715, lnames[9226], ('mls', 'ms', 's', 'm', 12, 0, 12, 12, 3, 10, 3, 10, 6, 10), 'm', 0, NPC_REGULAR), + 9227: (9721, lnames[9227], ('cls', 'ss', 's', 'm', 13, 0, 13, 13, 8, 5, 8, 5, 3, 18), 'm', 0, NPC_REGULAR), + 9228: (9720, lnames[9228], ('fss', 'sd', 's', 'f', 4, 0, 4, 4, 15, 5, 11, 5, 8, 5), 'f', 0, NPC_REGULAR), + 9229: (9708, lnames[9229], ('css', 'ld', 'm', 'f', 4, 0, 4, 4, 22, 21, 0, 21, 4, 21), 'f', 1, NPC_REGULAR), + 9230: (9719, lnames[9230], ('mss', 'ss', 's', 'm', 8, 0, 8, 8, 53, 27, 42, 27, 13, 27), 'm', 0, NPC_REGULAR), + 9231: (9722, lnames[9231], ('dll', 'ss', 's', 'm', 6, 0, 6, 6, 27, 27, 18, 27, 3, 8), 'm', 0, NPC_REGULAR), + 9232: (9759, lnames[9232], ('pss', 'ld', 'm', 'f', 21, 0, 21, 21, 0, 27, 0, 27, 13, 27), 'f', 0, NPC_REGULAR), + 9233: (9756, lnames[9233], ('csl', 'ls', 'l', 'f', 22, 0, 22, 22, 1, 7, 1, 7, 12, 27), 'f', 0, NPC_HQ), + 9234: (9756, lnames[9234], ('cls', 'ss', 'l', 'm', 14, 0, 14, 14, 1, 5, 1, 5, 0, 19), 'm', 0, NPC_HQ), + 9235: (9756, lnames[9235], ('dll', 'ls', 'l', 'm', 6, 0, 6, 6, 1, 6, 1, 6, 0, 16), 'm', 0, NPC_HQ), + 9236: (9756, lnames[9236], ('dss', 'ms', 'l', 'm', 20, 0, 20, 20, 0, 6, 0, 6, 0, 13), 'm', 0, NPC_HQ), + 9237: (9255, lnames[9237], ('dls', 'ss', 'l', 'm', 14, 0, 14, 14, 0, 7, 0, 7, 0, 10), 'm', 0, NPC_FISHERMAN), + 9301: (9329, lnames[9301], 'r', 'm', 0, NPC_FISHERMAN), + 9302: (9802, lnames[9302], ('dss', 'ld', 'l', 'f', 17, 0, 17, 17, 5, 21, 5, 21, 8, 26), 'f', 0, NPC_REGULAR), + 9303: (9826, lnames[9303], 'r', 'm', 0, NPC_REGULAR), + 9304: (9804, lnames[9304], ('dss', 'ls', 's', 'm', 16, 0, 16, 16, 14, 10, 10, 10, 3, 19), 'm', 0, NPC_REGULAR), + 9305: (9829, lnames[9305], 'r', 'm', 0, NPC_HQ), + 9306: (9829, lnames[9306], 'r', 'm', 0, NPC_HQ), + 9307: (9829, lnames[9307], 'r', 'f', 0, NPC_HQ), + 9308: (9829, lnames[9308], 'r', 'f', 0, NPC_HQ), + 9309: (9808, lnames[9309], ('css', 'ms', 'm', 'm', 26, 0, 26, 26, 8, 4, 8, 4, 7, 4), 'm', 0, NPC_REGULAR), + 9310: (9820, lnames[9310], 'r', 'm', 0, NPC_REGULAR), + 9311: (9809, lnames[9311], ('dls', 'ls', 'm', 'm', 24, 0, 18, 18, 11, 7, 0, 7, 1, 11), 'm', 0, NPC_REGULAR), + 9312: (9828, lnames[9312], 'r', 'f', 0, NPC_REGULAR), + 9313: (9827, lnames[9313], 'r', 'm', 0, NPC_REGULAR), + 9314: (9812, lnames[9314], 'r', 'm', 0, NPC_REGULAR), + 9315: (9813, lnames[9315], 'r', 'f', 0, NPC_REGULAR), + 9316: (9814, lnames[9316], 'r', 'f', 0, NPC_REGULAR), + 9317: (9815, lnames[9317], ('css', 'md', 's', 'f', 22, 0, 22, 22, 7, 4, 7, 4, 8, 11), 'f', 0, NPC_REGULAR), + 9318: (9816, lnames[9318], 'r', 'm', 0, NPC_REGULAR), + 9319: (9817, lnames[9319], ('css', 'ls', 'm', 'm', 26, 0, 26, 26, 5, 11, 5, 11, 5, 11), 'm', 0, NPC_REGULAR), + 9320: (9819, lnames[9320], 'r', 'm', 0, NPC_REGULAR), + 9321: (9824, lnames[9321], ('dss', 'ms', 'm', 'm', 2, 0, 2, 2, 4, 1, 4, 1, 2, 16), 'm', 0, NPC_REGULAR), + 9322: (9821, lnames[9322], ('dss', 'ms', 'm', 'm', 15, 0, 15, 15, 5, 6, 5, 6, 7, 9), 'f', 0, NPC_REGULAR), + 9323: (9822, lnames[9323], ('css', 'ms', 's', 'm', 31, 0, 31, 31, 8, 2, 8, 2, 5, 11), 'm', 0, NPC_REGULAR), + 9324: (9806, lnames[9324], ('fss', 'ls', 'l', 'm', 16, 0, 16, 16, 2, 9, 2, 9, 7, 20), 'm', 0, NPC_REGULAR), + 7001: (-1, lnames[7001], ('bss', 'md', 'm', 'f', 25, 0, 25, 25, 6, 12, 0, 0, 0, 2), 'f', 0, NPC_REGULAR), + 7002: (-1, lnames[7002], ('sss', 'ms', 'l', 'm', 7, 0, 7, 7, 18, 11, 0, 0, 4, 3), 'm', 0, NPC_REGULAR), + 7003: (-1, lnames[7003], ('sss', 'md', 'm', 'f', 21, 0, 21, 21, 45, 0, 0, 0, 7, 6), 'f', 0, NPC_REGULAR), + 7004: (-1, lnames[7004], ('pss', 'ls', 'l', 'm', 16, 0, 16, 16, 27, 0, 0, 0, 7, 16), 'm', 0, NPC_REGULAR), + 7005: (-1, lnames[7005], ('pls', 'ld', 's', 'f', 5, 0, 5, 5, 25, 0, 0, 0, 10, 0), 'f', 0, NPC_REGULAR), + 7006: (-1, lnames[7006], ('bll', 'ms', 's', 'm', 18, 0, 18, 18, 15, 4, 0, 0, 9, 3), 'm', 0, NPC_REGULAR), + 7007: (-1, lnames[7007], ('pls', 'ls', 's', 'm', 11, 0, 11, 11, 46, 0, 0, 0, 5, 16), 'm', 0, NPC_REGULAR), + 7008: (-1, lnames[7008], ('bls', 'ld', 's', 'f', 23, 0, 23, 23, 15, 6, 0, 0, 0, 18), 'f', 0, NPC_REGULAR), + 7009: (-1, lnames[7009], ('sll', 'ss', 's', 'm', 1, 0, 1, 1, 1, 6, 0, 0, 0, 6), 'm', 0, NPC_REGULAR), + 7010: (-1, lnames[7010], ('rll', 'ms', 'm', 'm', 2, 0, 2, 2, 19, 10, 13, 10, 7, 14, 0), 'm', 0, NPC_REGULAR), + 7011: (-1, lnames[7011], ('fll', 'ls', 'm', 'm', 0, 0, 9, 0, 10, 10, 0, 10, 5, 27), 'm', 0, NPC_REGULAR), + 7012: (-1, lnames[7012], ('pss', 'ms', 'l', 'm', 20, 0, 20, 20, 26, 0, 0, 0, 15), 'm', 0, NPC_REGULAR), + 7013: (-1, lnames[7013], ('bsl', 'ms', 'm', 'f', 20, 0, 20, 20, 3, 4, 0, 0, 5, 18), 'f', 0, NPC_REGULAR), + 7014: (-1, lnames[7014], ('bll', 'ss', 's', 'm', 11, 0, 11, 11, 3, 6, 0, 0, 1, 2), 'm', 0, NPC_REGULAR), + 7015: (-1, lnames[7015], ('ssl', 'sd', 'l', 'f', 13, 0, 13, 13, 1, 2, 0, 0, 0, 10), 'f', 0, NPC_REGULAR), + 7016: (-1, lnames[7016], ('hll', 'ls', 'l', 'm', 8, 0, 8, 8, 1, 3, 0, 0, 1, 16), 'm', 0, NPC_REGULAR), + 7017: (-1, lnames[7017], ('dsl', 'ms', 's', 'm', 5, 0, 5, 5, 1, 0, 0, 0, 0, 4), 'm', 0, NPC_REGULAR), + 7018: (-1, lnames[7018], ('pls', 'ls', 's', 'f', 14, 0, 14, 14, 0, 11, 0, 0, 5, 9), 'f', 0, NPC_REGULAR), + 7019: (-1, lnames[7019], ('bsl', 'ls', 'l', 'm', 12, 0, 12, 12, 1, 10, 0, 0, 1, 13), 'm', 0, NPC_REGULAR), + 7020: (-1, lnames[7020], ('sss', 'ms', 'l', 'm', 2, 0, 2, 2, 0, 4, 0, 0, 0, 6), 'm', 0, NPC_REGULAR), + 7021: (-1, lnames[7021], ('fsl', 'ls', 'm', 'm', 17, 0, 17, 17, 4, 4, 0, 0, 0, 10), 'm', 0, NPC_REGULAR), + 7022: (-1, lnames[7022], ('mss', 'sd', 's', 'f', 24, 0, 24, 24, 3, 1, 0, 0, 0, 13), 'f', 0, NPC_REGULAR), + 7023: (-1, lnames[7023], ('pss', 'sd', 'l', 'f', 9, 0, 9, 9, 0, 8, 0, 0, 11, 0), 'f', 0, NPC_REGULAR), + 10001: (10000, lnames[10001], 'r', 'f', 0, NPC_LAFF_RESTOCK), + 10002: (-1, lnames[10002], ('sls', 'ss', 'm', 'm', 15, 0, 15, 15, 111, 27, 97, 27, 41, 27), 'm', 0, NPC_REGULAR), + 11001: (11000, lnames[11001], 'r', 'm', 0, NPC_LAFF_RESTOCK), + 12001: (12000, lnames[12001], 'r', 'm', 0, NPC_LAFF_RESTOCK), + 12002: (-1, lnames[12002], ('pls', 'ls', 'l', 'f', 3, 0, 3, 3, 111, 27, 97, 27, 45, 27), 'f', 0, NPC_REGULAR), + 13001: (13000, lnames[13001], 'r', 'f', 0, NPC_LAFF_RESTOCK), + 13002: (-1, lnames[13002], ('bss', 'ss', 'm', 'm', 19, 0, 19, 19, 0, 3, 0, 3, 1, 16), 'm', 0, NPC_REGULAR) +} + +if config.GetBool('want-new-toonhall', 1): + NPCToonDict[2001] = (2513, lnames[2001], ('dss', 'ms', 'm', 'm', 17, 0, 17, 17, 3, 3, 3, 3, 7, 2), 'm', 1, NPC_FLIPPYTOONHALL) +else: + NPCToonDict[2001] = (2513, lnames[2001], ('dss', 'ms', 'm', 'm', 17, 0, 17, 17, 3, 3, 3, 3, 7, 2), 'm', 1, NPC_REGULAR) + +BlockerPositions = {TTLocalizer.Flippy: (Point3(207.4, 18.81, -0.475), 90.0)} +LaffRestockPositions = {lnames[11001]: ((-27.0, -170.0, -19.6), 215.0), + lnames[12001]: ((361.9, -394.4, -23.5), 120.0), + lnames[13001]: ((143.7, -381.4, -68.4), 0.0), + lnames[10001]: ((135.0, 128.8, 0.025), -212.8)} +GlovePositions = {lnames[2021]: ((101, -14, 4), -305)} +del lnames +zone2NpcDict = {} + +def generateZone2NpcDict(): + if zone2NpcDict: + return + + for id, npcDesc in NPCToonDict.items(): + zoneId = npcDesc[0] + if zoneId in zone2NpcDict: + zone2NpcDict[zoneId].append(id) + else: + zone2NpcDict[zoneId] = [id] + + +def getNPCName(npcId): + npc = NPCToonDict.get(npcId) + return npc[1] if npc else None + + +def getNPCZone(npcId): + npc = NPCToonDict.get(npcId) + return npc[0] if npc else None + + +def getBuildingArticle(zoneId): + return TTLocalizer.zone2TitleDict.get(zoneId, 'Toon Building')[1] + + +def getBuildingTitle(zoneId): + return TTLocalizer.zone2TitleDict.get(zoneId, 'Toon Building')[0] + + + +HQnpcFriends = { + 2001: (ToontownBattleGlobals.HEAL_TRACK, 5, ToontownGlobals.MaxHpLimit, 5), + 2132: (ToontownBattleGlobals.HEAL_TRACK, 5, 70, 4), + 2121: (ToontownBattleGlobals.HEAL_TRACK, 5, 45, 3), + 2011: (ToontownBattleGlobals.TRAP_TRACK, 4, 180, 5), + 3007: (ToontownBattleGlobals.TRAP_TRACK, 4, 70, 4), + 1001: (ToontownBattleGlobals.TRAP_TRACK, 4, 50, 3), + 3112: (ToontownBattleGlobals.LURE_TRACK, 5, 0, 5), + 1323: (ToontownBattleGlobals.LURE_TRACK, 5, 0, 3), + 2308: (ToontownBattleGlobals.LURE_TRACK, 5, 0, 3), + 4119: (ToontownBattleGlobals.SOUND_TRACK, 5, 80, 5), + 4219: (ToontownBattleGlobals.SOUND_TRACK, 5, 50, 4), + 4115: (ToontownBattleGlobals.SOUND_TRACK, 5, 40, 3), + 1116: (ToontownBattleGlobals.DROP_TRACK, 5, 170, 5), + 2311: (ToontownBattleGlobals.DROP_TRACK, 5, 100, 4), + 4140: (ToontownBattleGlobals.DROP_TRACK, 5, 60, 3), + 3137: (ToontownBattleGlobals.NPC_COGS_MISS, 0, 0, 4), + 4327: (ToontownBattleGlobals.NPC_COGS_MISS, 0, 0, 4), + 4230: (ToontownBattleGlobals.NPC_COGS_MISS, 0, 0, 4), + 3135: (ToontownBattleGlobals.NPC_TOONS_HIT, 0, 0, 4), + 2208: (ToontownBattleGlobals.NPC_TOONS_HIT, 0, 0, 4), + 5124: (ToontownBattleGlobals.NPC_TOONS_HIT, 0, 0, 4), + 2003: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, -1, 0, 5), + 2126: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.HEAL_TRACK, 0, 3), + 4007: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.TRAP_TRACK, 0, 3), + 1315: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.LURE_TRACK, 0, 3), + 5207: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.SQUIRT_TRACK, 0, 3), + 3129: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.THROW_TRACK, 0, 3), + 4125: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.SOUND_TRACK, 0, 3), + 1329: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.DROP_TRACK, 0, 3) +} + +FOnpcFriends = { + 7012: (ToontownBattleGlobals.HEAL_TRACK, 3, 10, 0), + 7013: (ToontownBattleGlobals.HEAL_TRACK, 3, 20, 1), + 7014: (ToontownBattleGlobals.HEAL_TRACK, 3, 30, 2), + 7015: (ToontownBattleGlobals.DROP_TRACK, 1, 20, 0), + 7016: (ToontownBattleGlobals.DROP_TRACK, 2, 35, 1), + 7017: (ToontownBattleGlobals.DROP_TRACK, 3, 50, 2), + 7018: (ToontownBattleGlobals.SOUND_TRACK, 1, 10, 0), + 7019: (ToontownBattleGlobals.SOUND_TRACK, 3, 20, 1), + 7020: (ToontownBattleGlobals.SOUND_TRACK, 4, 30, 2), + 7021: (ToontownBattleGlobals.LURE_TRACK, 1, 0, 0), + 7022: (ToontownBattleGlobals.LURE_TRACK, 1, 0, 1), + 7023: (ToontownBattleGlobals.LURE_TRACK, 3, 0, 2) +} + +disabledSosCards = ConfigVariableList('disable-sos-card') + +for npcId in disabledSosCards: + npcId = int(npcId) + if npcId in HQnpcFriends: + del HQnpcFriends[npcId] + if npcId in FOnpcFriends: + del FOnpcFriends[npcId] + +npcFriends = dict(HQnpcFriends) +npcFriends.update(FOnpcFriends) + +def getNPCName(npcId): + if npcId in NPCToonDict: + return NPCToonDict[npcId][1] + +def npcFriendsMinMaxStars(minStars, maxStars): + return [id for id in npcFriends.keys() if getNPCTrackLevelHpRarity(id)[3] >= minStars and getNPCTrackLevelHpRarity(id)[3] <= maxStars] + +def getNPCTrack(npcId): + if npcId in npcFriends: + return npcFriends[npcId][0] + +def getNPCTrackHp(npcId): + if npcId in npcFriends: + track, level, hp, rarity = npcFriends[npcId] + return (track, hp) + return (None, None) + +def getNPCTrackLevelHp(npcId): + if npcId in npcFriends: + track, level, hp, rarity = npcFriends[npcId] + return (track, level, hp) + return (None, None, None) + +def getNPCTrackLevelHpRarity(npcId): + if npcId in npcFriends: + return npcFriends[npcId] + return (None, None, None, None) diff --git a/toontown/toon/TTEmote.py b/toontown/toon/TTEmote.py new file mode 100755 index 00000000..c0770046 --- /dev/null +++ b/toontown/toon/TTEmote.py @@ -0,0 +1,524 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.showbase import PythonUtil +from panda3d.core import * +import random +import types + +import Toon, ToonDNA +from otp.avatar import Emote +from otp.otpbase import OTPLocalizer +from otp.nametag.NametagConstants import * +from otp.nametag.NametagGroup import * +from toontown.toonbase import TTLocalizer + + +EmoteSleepIndex = 4 +EmoteClear = -1 + +def doVictory(toon, volume = 1): + duration = toon.getDuration('victory', 'legs') + sfx = base.loadSfx('phase_3.5/audio/sfx/ENC_Win.ogg') + sfxDuration = duration - 1.0 + sfxTrack = SoundInterval(sfx, loop=1, duration=sfxDuration, node=toon, volume=volume) + track = Sequence(Func(toon.play, 'victory'), sfxTrack, duration=0) + return (track, duration, None) + + +def doJump(toon, volume = 1): + track = Sequence(Func(toon.play, 'jump')) + return (track, 0, None) + + +def doDead(toon, volume = 1): + toon.animFSM.request('Sad') + return (None, 0, None) + + +def doAnnoyed(toon, volume = 1): + duration = toon.getDuration('angry', 'torso') + sfx = None + if toon.style.getAnimal() == 'bear': + sfx = base.loadSfx('phase_3.5/audio/dial/AV_bear_exclaim.ogg') + else: + sfx = base.loadSfx('phase_3.5/audio/sfx/avatar_emotion_angry.ogg') + + def playSfx(): + base.playSfx(sfx, volume=volume, node=toon) + + track = Sequence(Func(toon.angryEyes), Func(toon.blinkEyes), Func(toon.play, 'angry'), Func(playSfx)) + exitTrack = Sequence(Func(toon.normalEyes), Func(toon.blinkEyes)) + return (track, duration, exitTrack) + + +def doAngryEyes(toon, volume = 1): + track = Sequence(Func(toon.angryEyes), Func(toon.blinkEyes), Wait(10.0), Func(toon.normalEyes)) + return (track, 0.1, None) + + +def doHappy(toon, volume = 1): + track = Sequence(Func(toon.play, 'jump'), Func(toon.normalEyes), Func(toon.blinkEyes)) + duration = toon.getDuration('jump', 'legs') + return (track, duration, None) + + +def doSad(toon, volume = 1): + track = Sequence(Func(toon.sadEyes), Func(toon.blinkEyes)) + exitTrack = Sequence(Func(toon.normalEyes), Func(toon.blinkEyes)) + return (track, 3, exitTrack) + + +def doSleep(toon, volume = 1): + duration = 4 + track = Sequence(Func(toon.stopLookAround), Func(toon.stopBlink), Func(toon.closeEyes), Func(toon.lerpLookAt, Point3(0, 1, -4)), Func(toon.loop, 'neutral'), Func(toon.setPlayRate, 0.4, 'neutral'), Func(toon.setChatAbsolute, TTLocalizer.ToonSleepString, CFThought)) + + def wakeUpFromSleepEmote(): + toon.startLookAround() + toon.openEyes() + toon.startBlink() + toon.setPlayRate(1, 'neutral') + if toon.nametag.getChat() == TTLocalizer.ToonSleepString: + toon.clearChat() + toon.lerpLookAt(Point3(0, 1, 0), time=0.25) + + exitTrack = Sequence(Func(wakeUpFromSleepEmote)) + return (track, duration, exitTrack) + + +def doYes(toon, volume = 1): + tracks = Parallel(autoFinish=1) + for lod in toon.getLODNames(): + h = toon.getPart('head', lod) + tracks.append(Sequence(LerpHprInterval(h, 0.1, Vec3(0, -30, 0)), LerpHprInterval(h, 0.15, Vec3(0, 20, 0)), LerpHprInterval(h, 0.15, Vec3(0, -20, 0)), LerpHprInterval(h, 0.15, Vec3(0, 20, 0)), LerpHprInterval(h, 0.15, Vec3(0, -20, 0)), LerpHprInterval(h, 0.15, Vec3(0, 20, 0)), LerpHprInterval(h, 0.1, Vec3(0, 0, 0)))) + + tracks.start() + return (None, 0, None) + + +def doNo(toon, volume = 1): + tracks = Parallel(autoFinish=1) + for lod in toon.getLODNames(): + h = toon.getPart('head', lod) + tracks.append(Sequence(LerpHprInterval(h, 0.1, Vec3(40, 0, 0)), LerpHprInterval(h, 0.15, Vec3(-40, 0, 0)), LerpHprInterval(h, 0.15, Vec3(40, 0, 0)), LerpHprInterval(h, 0.15, Vec3(-40, 0, 0)), LerpHprInterval(h, 0.15, Vec3(20, 0, 0)), LerpHprInterval(h, 0.15, Vec3(-20, 0, 0)), LerpHprInterval(h, 0.1, Vec3(0, 0, 0)))) + + tracks.start() + return (None, 0, None) + + +def doOk(toon, volume = 1): + return (None, 0, None) + + +def doShrug(toon, volume = 1): + sfx = base.loadSfx('phase_3.5/audio/sfx/avatar_emotion_shrug.ogg') + + def playSfx(): + base.playSfx(sfx, volume=volume, node=toon) + + track = Sequence(Func(toon.play, 'shrug'), Func(playSfx)) + duration = toon.getDuration('shrug', 'torso') + return (track, duration, None) + + +def doWave(toon, volume = 1): + track = Sequence(Func(toon.play, 'wave')) + duration = toon.getDuration('wave', 'torso') + return (track, duration, None) + + +def doApplause(toon, volume = 1): + sfx = base.loadSfx('phase_4/audio/sfx/avatar_emotion_applause.ogg') + + def playSfx(): + base.playSfx(sfx, volume=1, node=toon) + + track = Sequence(Func(toon.play, 'applause'), Func(playSfx)) + duration = toon.getDuration('applause', 'torso') + return (track, duration, None) + + +def doConfused(toon, volume = 1): + sfx = base.loadSfx('phase_4/audio/sfx/avatar_emotion_confused.ogg') + + def playSfx(): + base.playSfx(sfx, node=toon, volume=volume) + + track = Sequence(Func(toon.play, 'confused'), Func(playSfx)) + duration = toon.getDuration('confused', 'torso') + return (track, duration, None) + + +def doSlipForward(toon, volume = 1): + sfx = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg') + + def playSfx(): + base.playSfx(sfx, volume=volume, node=toon) + + sfxDelay = 0.7 + track = Sequence(Func(toon.play, 'slip-forward'), Wait(sfxDelay), Func(playSfx)) + duration = toon.getDuration('slip-forward', 'torso') - sfxDelay + return (track, duration, None) + + +def doBored(toon, volume = 1): + sfx = base.loadSfx('phase_4/audio/sfx/avatar_emotion_bored.ogg') + + def playSfx(): + base.playSfx(sfx, volume=volume, node=toon) + + sfxDelay = 2.2 + track = Sequence(Func(toon.play, 'bored'), Wait(sfxDelay), Func(playSfx)) + duration = toon.getDuration('bored', 'torso') - sfxDelay + return (track, duration, None) + + +def doBow(toon, volume = 1): + if toon.style.torso[1] == 'd': + track = Sequence(Func(toon.play, 'curtsy')) + duration = toon.getDuration('curtsy', 'torso') + else: + track = Sequence(Func(toon.play, 'bow')) + duration = toon.getDuration('bow', 'torso') + return (track, duration, None) + + +def doSlipBackward(toon, volume = 1): + sfx = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg') + + def playSfx(): + base.playSfx(sfx, volume=volume, node=toon) + + sfxDelay = 0.7 + track = Sequence(Func(toon.play, 'slip-backward'), Wait(sfxDelay), Func(playSfx)) + duration = toon.getDuration('slip-backward', 'torso') - sfxDelay + return (track, duration, None) + + +def doThink(toon, volume = 1): + duration = 47.0 / 24.0 * 2 + animTrack = Sequence(ActorInterval(toon, 'think', startFrame=0, endFrame=46), ActorInterval(toon, 'think', startFrame=46, endFrame=0)) + track = Sequence(animTrack, duration=0) + return (track, duration, None) + + +def doCringe(toon, volume = 1): + track = Sequence(Func(toon.play, 'cringe')) + duration = toon.getDuration('cringe', 'torso') + return (track, duration, None) + + +def doResistanceSalute(toon, volume=1): + track = Sequence( + Func(toon.setChatAbsolute, OTPLocalizer.CustomSCStrings[4020], CFSpeech|CFTimeout), + Func(toon.setPlayRate, 0.75, 'victory'), + Func(toon.pingpong, 'victory', fromFrame=0, toFrame=9), + Func(toon.setPlayRate, 1, 'victory') + ) + duration = 20 / toon.getFrameRate('victory') + return (track, duration, None) + + +def doNothing(toon, volume = 1): + return (None, 0, None) + + +def doSurprise(toon, volume = 1): + sfx = None + sfx = base.loadSfx('phase_4/audio/sfx/avatar_emotion_surprise.ogg') + + def playSfx(volume = 1): + base.playSfx(sfx, volume=volume, node=toon) + + def playAnim(anim): + anim.start() + + def stopAnim(anim): + anim.finish() + toon.stop() + sfx.stop() + + anim = Sequence(ActorInterval(toon, 'conked', startFrame=9, endFrame=50), ActorInterval(toon, 'conked', startFrame=70, endFrame=101)) + track = Sequence(Func(toon.stopBlink), Func(toon.surpriseEyes), Func(toon.showSurpriseMuzzle), Parallel(Func(playAnim, anim), Func(playSfx, volume))) + exitTrack = Sequence(Func(toon.hideSurpriseMuzzle), Func(toon.openEyes), Func(toon.startBlink), Func(stopAnim, anim)) + return (track, 3.0, exitTrack) + + +def doUpset(toon, volume = 1): + sfxList = ('phase_4/audio/sfx/avatar_emotion_very_sad_1.ogg', 'phase_4/audio/sfx/avatar_emotion_very_sad.ogg') + sfx = base.loadSfx(random.choice(sfxList)) + + def playSfx(volume = 1): + base.playSfx(sfx, volume=volume, node=toon) + + def playAnim(anim): + anim.start() + + def stopAnim(anim): + anim.finish() + toon.stop() + sfx.stop() + + anim = Sequence(ActorInterval(toon, 'bad-putt', startFrame=29, endFrame=59, playRate=-0.75), ActorInterval(toon, 'bad-putt', startFrame=29, endFrame=59, playRate=0.75)) + track = Sequence(Func(toon.sadEyes), Func(toon.blinkEyes), Func(toon.showSadMuzzle), Parallel(Func(playAnim, anim), Func(playSfx, volume))) + exitTrack = Sequence(Func(toon.hideSadMuzzle), Func(toon.normalEyes), Func(stopAnim, anim)) + return (track, 4.0, exitTrack) + + +def doDelighted(toon, volume = 1): + sfx = None + sfx = base.loadSfx('phase_4/audio/sfx/delighted_06.ogg') + + def playSfx(volume = 1): + base.playSfx(sfx, volume=volume, node=toon) + + def playAnim(anim): + anim.start() + + def stopAnim(anim): + anim.finish() + toon.stop() + sfx.stop() + + anim = Sequence(ActorInterval(toon, 'left'), Wait(1), ActorInterval(toon, 'left', playRate=-1)) + track = Sequence(Func(toon.blinkEyes), Func(toon.showSmileMuzzle), Parallel(Func(playAnim, anim), Func(playSfx, volume))) + exitTrack = Sequence(Func(toon.hideSmileMuzzle), Func(toon.blinkEyes), Func(stopAnim, anim)) + return (track, 2.5, exitTrack) + + +def doFurious(toon, volume = 1): + duration = toon.getDuration('angry', 'torso') + sfx = None + sfx = base.loadSfx('phase_4/audio/sfx/furious_03.ogg') + + def playSfx(volume = 1): + base.playSfx(sfx, volume=volume, node=toon) + + track = Sequence(Func(toon.angryEyes), Func(toon.blinkEyes), Func(toon.showAngryMuzzle), Func(toon.play, 'angry'), Func(playSfx, volume)) + exitTrack = Sequence(Func(toon.normalEyes), Func(toon.blinkEyes), Func(toon.hideAngryMuzzle)) + return (track, duration, exitTrack) + + +def doLaugh(toon, volume = 1): + sfx = None + sfx = base.loadSfx('phase_4/audio/sfx/avatar_emotion_laugh.ogg') + + def playSfx(volume = 1): + base.playSfx(sfx, volume=volume, node=toon) + + def playAnim(): + toon.setPlayRate(10, 'neutral') + toon.loop('neutral') + + def stopAnim(): + toon.setPlayRate(1, 'neutral') + + track = Sequence(Func(toon.blinkEyes), Func(toon.showLaughMuzzle), Func(playAnim), Func(playSfx, volume)) + exitTrack = Sequence(Func(toon.hideLaughMuzzle), Func(toon.blinkEyes), Func(stopAnim)) + return (track, 2, exitTrack) + +def doRage(toon, volume=1): + sfx = base.loadSfx('phase_4/audio/sfx/furious_03.ogg') + track = Sequence( + Func(toon.blinkEyes), + Func(toon.play, 'good-putt', fromFrame=12), + Func(base.playSfx, sfx, volume=volume, node=toon) + ) + duration = toon.getDuration('rage') + return (track, duration, None) + +def returnToLastAnim(toon): + if hasattr(toon, 'playingAnim') and toon.playingAnim: + toon.loop(toon.playingAnim) + elif not hasattr(toon, 'hp') or toon.hp > 0: + toon.loop('neutral') + else: + toon.loop('sad-neutral') + + +EmoteFunc = [[doWave, 0], + [doHappy, 0], + [doSad, 0], + [doAnnoyed, 0], + [doSleep, 0], + [doShrug, 0], + [doVictory, 0], + [doThink, 0], + [doBored, 0], + [doApplause, 0], + [doCringe, 0], + [doConfused, 0], + [doSlipForward, 0], + [doBow, 0], + [doSlipBackward, 0], + [doResistanceSalute, 0], + [doNothing, 0], + [doYes, 0], + [doNo, 0], + [doOk, 0], + [doSurprise, 0], + [doUpset, 0], + [doDelighted, 0], + [doFurious, 0], + [doLaugh, 0], + [doRage, 0]] + +class TTEmote(Emote.Emote): + notify = DirectNotifyGlobal.directNotify.newCategory('TTEmote') + SLEEP_INDEX = 4 + + def __init__(self): + self.emoteFunc = EmoteFunc + self.bodyEmotes = [0, + 1, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 20, + 21, + 22, + 23, + 24, + 25] + self.headEmotes = [2, + 17, + 18, + 19] + if len(self.emoteFunc) != len(OTPLocalizer.EmoteList): + self.notify.error('Emote.EmoteFunc and OTPLocalizer.EmoteList are different lengths.') + self.track = None + self.stateChangeMsgLocks = 0 + self.stateHasChanged = 0 + return + + def lockStateChangeMsg(self): + self.stateChangeMsgLocks += 1 + + def unlockStateChangeMsg(self): + if self.stateChangeMsgLocks <= 0: + print PythonUtil.lineTag() + ': someone unlocked too many times' + return + self.stateChangeMsgLocks -= 1 + if self.stateChangeMsgLocks == 0 and self.stateHasChanged: + messenger.send(self.EmoteEnableStateChanged) + self.stateHasChanged = 0 + + def emoteEnableStateChanged(self): + if self.stateChangeMsgLocks > 0: + self.stateHasChanged = 1 + else: + messenger.send(self.EmoteEnableStateChanged) + + def disableAll(self, toon, msg = None): + if toon != base.localAvatar: + return + self.disableGroup(range(len(self.emoteFunc)), toon) + + def releaseAll(self, toon, msg = None): + if toon != base.localAvatar: + return + self.enableGroup(range(len(self.emoteFunc)), toon) + + def disableBody(self, toon, msg = None): + if toon != base.localAvatar: + return + self.disableGroup(self.bodyEmotes, toon) + + def releaseBody(self, toon, msg = None): + if toon != base.localAvatar: + return + self.enableGroup(self.bodyEmotes, toon) + + def disableHead(self, toon, msg = None): + if toon != base.localAvatar: + return + self.disableGroup(self.headEmotes, toon) + + def releaseHead(self, toon, msg = None): + if toon != base.localAvatar: + return + self.enableGroup(self.headEmotes, toon) + + def getHeadEmotes(self): + return self.headEmotes + + def disableGroup(self, indices, toon): + self.lockStateChangeMsg() + for i in indices: + self.disable(i, toon) + + self.unlockStateChangeMsg() + + def enableGroup(self, indices, toon): + self.lockStateChangeMsg() + for i in indices: + self.enable(i, toon) + + self.unlockStateChangeMsg() + + def disable(self, index, toon): + if isinstance(index, types.StringType): + index = OTPLocalizer.EmoteFuncDict[index] + self.emoteFunc[index][1] = self.emoteFunc[index][1] + 1 + if toon is base.localAvatar: + if self.emoteFunc[index][1] == 1: + self.emoteEnableStateChanged() + + def enable(self, index, toon): + if isinstance(index, types.StringType): + index = OTPLocalizer.EmoteFuncDict[index] + self.emoteFunc[index][1] = self.emoteFunc[index][1] - 1 + if toon is base.localAvatar: + if self.emoteFunc[index][1] == 0: + self.emoteEnableStateChanged() + + def doEmote(self, toon, emoteIndex, ts = 0, volume = 1): + try: + func = self.emoteFunc[emoteIndex][0] + except: + print 'Error in finding emote func %s' % emoteIndex + return (None, None) + + def clearEmoteTrack(): + base.localAvatar.emoteTrack = None + base.localAvatar.d_setEmoteState(self.EmoteClear, 1.0) + return + + if volume == 1: + track, duration, exitTrack = func(toon) + else: + track, duration, exitTrack = func(toon, volume) + if track != None: + track = Sequence(Func(self.disableAll, toon, 'doEmote'), track) + if duration > 0: + track = Sequence(track, Wait(duration)) + if exitTrack != None: + track = Sequence(track, exitTrack) + if duration > 0: + track = Sequence(track, Func(returnToLastAnim, toon)) + track = Sequence(track, Func(self.releaseAll, toon, 'doEmote'), autoFinish=1) + if toon.isLocal(): + track = Sequence(track, Func(clearEmoteTrack)) + if track != None: + if toon.emote != None: + toon.emote.finish() + toon.emote = None + toon.emote = track + track.start(ts) + del clearEmoteTrack + return (track, duration) + + def printEmoteState(self, action, msg): + pass + + +Emote.globalEmote = TTEmote() diff --git a/toontown/toon/TailorClothesGUI.py b/toontown/toon/TailorClothesGUI.py new file mode 100755 index 00000000..ebf6f5b2 --- /dev/null +++ b/toontown/toon/TailorClothesGUI.py @@ -0,0 +1,21 @@ +from toontown.makeatoon import ClothesGUI +import ToonDNA + +class TailorClothesGUI(ClothesGUI.ClothesGUI): + notify = directNotify.newCategory('MakeClothesGUI') + + def __init__(self, doneEvent, swapEvent, tailorId): + ClothesGUI.ClothesGUI.__init__(self, ClothesGUI.CLOTHES_TAILOR, doneEvent, swapEvent) + self.tailorId = tailorId + + def setupScrollInterface(self): + self.dna = self.toon.getStyle() + gender = self.dna.getGender() + if self.swapEvent != None: + self.tops = ToonDNA.getTops(gender, tailorId=self.tailorId) + self.bottoms = ToonDNA.getBottoms(gender, tailorId=self.tailorId) + self.gender = gender + self.topChoice = -1 + self.bottomChoice = -1 + self.setupButtons() + return diff --git a/toontown/toon/Toon.py b/toontown/toon/Toon.py new file mode 100755 index 00000000..dd547c97 --- /dev/null +++ b/toontown/toon/Toon.py @@ -0,0 +1,3146 @@ +from direct.actor import Actor +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.task.Task import Task +from panda3d.core import * +import random +import types +import math +import AccessoryGlobals +import Motion +import TTEmote +import ToonDNA +import LaffMeter +from ToonHead import * +from otp.ai.MagicWordGlobal import * +from otp.avatar import Avatar +from otp.avatar import Emote +from otp.avatar.Avatar import teleportNotify +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPLocalizer +from toontown.battle import SuitBattleGlobals +from otp.nametag.NametagConstants import * +from toontown.distributed import DelayDelete +from toontown.effects import DustCloud +from toontown.effects import Wake +from toontown.hood import ZoneUtil +from otp.nametag.NametagGroup import * +from toontown.suit import SuitDNA +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals + +def teleportDebug(requestStatus, msg, onlyIfToAv = True): + if teleportNotify.getDebug(): + teleport = 'teleport' + if 'how' in requestStatus and requestStatus['how'][:len(teleport)] == teleport: + if not onlyIfToAv or 'avId' in requestStatus and requestStatus['avId'] > 0: + teleportNotify.debug(msg) + + +SLEEP_STRING = TTLocalizer.ToonSleepString +DogDialogueArray = [] +CatDialogueArray = [] +HorseDialogueArray = [] +RabbitDialogueArray = [] +MouseDialogueArray = [] +DuckDialogueArray = [] +MonkeyDialogueArray = [] +BearDialogueArray = [] +PigDialogueArray = [] +LegsAnimDict = {} +TorsoAnimDict = {} +HeadAnimDict = {} +Preloaded = {} +Phase3AnimList = (('neutral', 'neutral'), ('run', 'run')) +Phase3_5AnimList = (('walk', 'walk'), + ('teleport', 'teleport'), + ('book', 'book'), + ('jump', 'jump'), + ('running-jump', 'running-jump'), + ('jump-squat', 'jump-zstart'), + ('jump-idle', 'jump-zhang'), + ('jump-land', 'jump-zend'), + ('running-jump-squat', 'leap_zstart'), + ('running-jump-idle', 'leap_zhang'), + ('running-jump-land', 'leap_zend'), + ('pushbutton', 'press-button'), + ('throw', 'pie-throw'), + ('victory', 'victory-dance'), + ('sidestep-left', 'sidestep-left'), + ('conked', 'conked'), + ('cringe', 'cringe'), + ('wave', 'wave'), + ('shrug', 'shrug'), + ('angry', 'angry'), + ('tutorial-neutral', 'tutorial-neutral'), + ('left-point', 'left-point'), + ('right-point', 'right-point'), + ('right-point-start', 'right-point-start'), + ('give-props', 'give-props'), + ('give-props-start', 'give-props-start'), + ('right-hand', 'right-hand'), + ('right-hand-start', 'right-hand-start'), + ('duck', 'duck'), + ('sidestep-right', 'jump-back-right'), + ('periscope', 'periscope')) +Phase4AnimList = (('sit', 'sit'), + ('sit-start', 'intoSit'), + ('swim', 'swim'), + ('tug-o-war', 'tug-o-war'), + ('sad-walk', 'losewalk'), + ('sad-neutral', 'sad-neutral'), + ('up', 'up'), + ('down', 'down'), + ('left', 'left'), + ('right', 'right'), + ('applause', 'applause'), + ('confused', 'confused'), + ('bow', 'bow'), + ('curtsy', 'curtsy'), + ('bored', 'bored'), + ('think', 'think'), + ('battlecast', 'fish'), + ('cast', 'cast'), + ('castlong', 'castlong'), + ('fish-end', 'fishEND'), + ('fish-neutral', 'fishneutral'), + ('fish-again', 'fishAGAIN'), + ('reel', 'reel'), + ('reel-H', 'reelH'), + ('reel-neutral', 'reelneutral'), + ('pole', 'pole'), + ('pole-neutral', 'poleneutral'), + ('slip-forward', 'slip-forward'), + ('slip-backward', 'slip-backward'), + ('catch-neutral', 'gameneutral'), + ('catch-run', 'gamerun'), + ('catch-eatneutral', 'eat_neutral'), + ('catch-eatnrun', 'eatnrun'), + ('catch-intro-throw', 'gameThrow'), + ('swing', 'swing'), + ('pet-start', 'petin'), + ('pet-loop', 'petloop'), + ('pet-end', 'petend'), + ('scientistJealous', 'scientistJealous'), + ('scientistEmcee', 'scientistEmcee'), + ('scientistWork', 'scientistWork'), + ('scientistGame', 'scientistGame')) +Phase5AnimList = (('water-gun', 'water-gun'), + ('hold-bottle', 'hold-bottle'), + ('firehose', 'firehose'), + ('spit', 'spit'), + ('tickle', 'tickle'), + ('smooch', 'smooch'), + ('happy-dance', 'happy-dance'), + ('sprinkle-dust', 'sprinkle-dust'), + ('juggle', 'juggle'), + ('climb', 'climb'), + ('sound', 'shout'), + ('toss', 'toss'), + ('hold-magnet', 'hold-magnet'), + ('hypnotize', 'hypnotize'), + ('struggle', 'struggle'), + ('lose', 'lose'), + ('melt', 'melt')) +Phase5_5AnimList = (('takePhone', 'takePhone'), + ('phoneNeutral', 'phoneNeutral'), + ('phoneBack', 'phoneBack'), + ('bank', 'jellybeanJar'), + ('callPet', 'callPet'), + ('feedPet', 'feedPet'), + ('start-dig', 'into_dig'), + ('loop-dig', 'loop_dig'), + ('water', 'water')) +Phase6AnimList = (('headdown-putt', 'headdown-putt'), + ('into-putt', 'into-putt'), + ('loop-putt', 'loop-putt'), + ('rotateL-putt', 'rotateL-putt'), + ('rotateR-putt', 'rotateR-putt'), + ('swing-putt', 'swing-putt'), + ('look-putt', 'look-putt'), + ('lookloop-putt', 'lookloop-putt'), + ('bad-putt', 'bad-putt'), + ('badloop-putt', 'badloop-putt'), + ('good-putt', 'good-putt')) +Phase9AnimList = (('push', 'push'),) +Phase10AnimList = (('leverReach', 'leverReach'), ('leverPull', 'leverPull'), ('leverNeutral', 'leverNeutral')) +Phase12AnimList = () +LegDict = {'s': '/models/char/tt_a_chr_dgs_shorts_legs_', + 'm': '/models/char/tt_a_chr_dgm_shorts_legs_', + 'l': '/models/char/tt_a_chr_dgl_shorts_legs_'} +TorsoDict = { + 'ss': '/models/char/tt_a_chr_dgs_shorts_torso_', + 'ms': '/models/char/tt_a_chr_dgm_shorts_torso_', + 'ls': '/models/char/tt_a_chr_dgl_shorts_torso_', + 'sd': '/models/char/tt_a_chr_dgs_skirt_torso_', + 'md': '/models/char/tt_a_chr_dgm_skirt_torso_', + 'ld': '/models/char/tt_a_chr_dgl_skirt_torso_'} + +def loadModels(): + global Preloaded + if not Preloaded: + print 'Preloading avatars...' + + for key in LegDict.keys(): + fileRoot = LegDict[key] + + Preloaded[fileRoot+'-1000'] = loader.loadModel('phase_3' + fileRoot + '1000') + Preloaded[fileRoot+'-500'] = loader.loadModel('phase_3' + fileRoot + '500') + Preloaded[fileRoot+'-250'] = loader.loadModel('phase_3' + fileRoot + '250') + + for key in TorsoDict.keys(): + fileRoot = TorsoDict[key] + + Preloaded[fileRoot+'-1000'] = loader.loadModel('phase_3' + fileRoot + '1000') + + if len(key) > 1: + Preloaded[fileRoot+'-500'] = loader.loadModel('phase_3' + fileRoot + '500') + Preloaded[fileRoot+'-250'] = loader.loadModel('phase_3' + fileRoot + '250') + +def loadBasicAnims(): + loadPhaseAnims() + +def unloadBasicAnims(): + loadPhaseAnims(0) + +def loadTutorialBattleAnims(): + loadPhaseAnims('phase_3.5') + +def unloadTutorialBattleAnims(): + loadPhaseAnims('phase_3.5', 0) + +def loadMinigameAnims(): + loadPhaseAnims('phase_4') + +def unloadMinigameAnims(): + loadPhaseAnims('phase_4', 0) + +def loadBattleAnims(): + loadPhaseAnims('phase_5') + +def unloadBattleAnims(): + loadPhaseAnims('phase_5', 0) + +def loadSellbotHQAnims(): + loadPhaseAnims('phase_9') + +def unloadSellbotHQAnims(): + loadPhaseAnims('phase_9', 0) + +def loadCashbotHQAnims(): + loadPhaseAnims('phase_10') + +def unloadCashbotHQAnims(): + loadPhaseAnims('phase_10', 0) + +def loadBossbotHQAnims(): + loadPhaseAnims('phase_12') + +def unloadBossbotHQAnims(): + loadPhaseAnims('phase_12', 0) + +def loadPhaseAnims(phaseStr = 'phase_3', loadFlag = 1): + if phaseStr == 'phase_3': + animList = Phase3AnimList + elif phaseStr == 'phase_3.5': + animList = Phase3_5AnimList + elif phaseStr == 'phase_4': + animList = Phase4AnimList + elif phaseStr == 'phase_5': + animList = Phase5AnimList + elif phaseStr == 'phase_5.5': + animList = Phase5_5AnimList + elif phaseStr == 'phase_6': + animList = Phase6AnimList + elif phaseStr == 'phase_9': + animList = Phase9AnimList + elif phaseStr == 'phase_10': + animList = Phase10AnimList + elif phaseStr == 'phase_12': + animList = Phase12AnimList + else: + self.notify.error('Unknown phase string %s' % phaseStr) + for key in LegDict.keys(): + for anim in animList: + if loadFlag: + pass + elif anim[0] in LegsAnimDict[key]: + if base.localAvatar.style.legs == key: + base.localAvatar.unloadAnims([anim[0]], 'legs', None) + + for key in TorsoDict.keys(): + for anim in animList: + if loadFlag: + pass + elif anim[0] in TorsoAnimDict[key]: + if base.localAvatar.style.torso == key: + base.localAvatar.unloadAnims([anim[0]], 'torso', None) + + for key in HeadDict.keys(): + if key.find('d') >= 0: + for anim in animList: + if loadFlag: + pass + elif anim[0] in HeadAnimDict[key]: + if base.localAvatar.style.head == key: + base.localAvatar.unloadAnims([anim[0]], 'head', None) + +def compileGlobalAnimList(): + phaseList = [Phase3AnimList, + Phase3_5AnimList, + Phase4AnimList, + Phase5AnimList, + Phase5_5AnimList, + Phase6AnimList, + Phase9AnimList, + Phase10AnimList, + Phase12AnimList] + phaseStrList = ['phase_3', + 'phase_3.5', + 'phase_4', + 'phase_5', + 'phase_5.5', + 'phase_6', + 'phase_9', + 'phase_10', + 'phase_12'] + for animList in phaseList: + phaseStr = phaseStrList[phaseList.index(animList)] + for key in LegDict.keys(): + LegsAnimDict.setdefault(key, {}) + for anim in animList: + file = phaseStr + LegDict[key] + anim[1] + LegsAnimDict[key][anim[0]] = file + + for key in TorsoDict.keys(): + TorsoAnimDict.setdefault(key, {}) + for anim in animList: + file = phaseStr + TorsoDict[key] + anim[1] + TorsoAnimDict[key][anim[0]] = file + + for key in HeadDict.keys(): + if key.find('d') >= 0: + HeadAnimDict.setdefault(key, {}) + for anim in animList: + file = phaseStr + HeadDict[key] + anim[1] + HeadAnimDict[key][anim[0]] = file + +def loadDialog(): + loadPath = 'phase_3.5/audio/dial/' + + DogDialogueFiles = ('AV_dog_short', 'AV_dog_med', 'AV_dog_long', 'AV_dog_question', 'AV_dog_exclaim', 'AV_dog_howl') + global DogDialogueArray + for file in DogDialogueFiles: + DogDialogueArray.append(base.loadSfx(loadPath + file + '.ogg')) + + catDialogueFiles = ('AV_cat_short', 'AV_cat_med', 'AV_cat_long', 'AV_cat_question', 'AV_cat_exclaim', 'AV_cat_howl') + global CatDialogueArray + for file in catDialogueFiles: + CatDialogueArray.append(base.loadSfx(loadPath + file + '.ogg')) + + horseDialogueFiles = ('AV_horse_short', 'AV_horse_med', 'AV_horse_long', 'AV_horse_question', 'AV_horse_exclaim', 'AV_horse_howl') + global HorseDialogueArray + for file in horseDialogueFiles: + HorseDialogueArray.append(base.loadSfx(loadPath + file + '.ogg')) + + rabbitDialogueFiles = ('AV_rabbit_short', 'AV_rabbit_med', 'AV_rabbit_long', 'AV_rabbit_question', 'AV_rabbit_exclaim', 'AV_rabbit_howl') + global RabbitDialogueArray + for file in rabbitDialogueFiles: + RabbitDialogueArray.append(base.loadSfx(loadPath + file + '.ogg')) + + mouseDialogueFiles = ('AV_mouse_short', 'AV_mouse_med', 'AV_mouse_long', 'AV_mouse_question', 'AV_mouse_exclaim', 'AV_mouse_howl') + global MouseDialogueArray + for file in mouseDialogueFiles: + MouseDialogueArray.append(base.loadSfx(loadPath + file + '.ogg')) + + duckDialogueFiles = ('AV_duck_short', 'AV_duck_med', 'AV_duck_long', 'AV_duck_question', 'AV_duck_exclaim', 'AV_duck_howl') + global DuckDialogueArray + for file in duckDialogueFiles: + DuckDialogueArray.append(base.loadSfx(loadPath + file + '.ogg')) + + monkeyDialogueFiles = ('AV_monkey_short', 'AV_monkey_med', 'AV_monkey_long', 'AV_monkey_question', 'AV_monkey_exclaim', 'AV_monkey_howl') + global MonkeyDialogueArray + for file in monkeyDialogueFiles: + MonkeyDialogueArray.append(base.loadSfx(loadPath + file + '.ogg')) + + bearDialogueFiles = ('AV_bear_short', 'AV_bear_med', 'AV_bear_long', 'AV_bear_question', 'AV_bear_exclaim', 'AV_bear_howl') + global BearDialogueArray + for file in bearDialogueFiles: + BearDialogueArray.append(base.loadSfx(loadPath + file + '.ogg')) + + pigDialogueFiles = ('AV_pig_short', 'AV_pig_med', 'AV_pig_long', 'AV_pig_question', 'AV_pig_exclaim', 'AV_pig_howl') + global PigDialogueArray + for file in pigDialogueFiles: + PigDialogueArray.append(base.loadSfx(loadPath + file + '.ogg')) + +def unloadDialog(): + global CatDialogueArray + global PigDialogueArray + global BearDialogueArray + global DuckDialogueArray + global RabbitDialogueArray + global MouseDialogueArray + global DogDialogueArray + global HorseDialogueArray + global MonkeyDialogueArray + DogDialogueArray = [] + CatDialogueArray = [] + HorseDialogueArray = [] + RabbitDialogueArray = [] + MouseDialogueArray = [] + DuckDialogueArray = [] + MonkeyDialogueArray = [] + BearDialogueArray = [] + PigDialogueArray = [] + +def reconsiderAllToonsUnderstandable(): + for av in Avatar.Avatar.ActiveAvatars: + if isinstance(av, Toon): + av.considerUnderstandable() + +class Toon(Avatar.Avatar, ToonHead): + notify = DirectNotifyGlobal.directNotify.newCategory('Toon') + afkTimeout = base.config.GetInt('afk-timeout', 600) + + def __init__(self): + try: + self.Toon_initialized + return + except: + self.Toon_initialized = 1 + + Avatar.Avatar.__init__(self) + ToonHead.__init__(self) + self.forwardSpeed = 0.0 + self.rotateSpeed = 0.0 + self.avatarType = 'toon' + self.motion = Motion.Motion(self) + self.standWalkRunReverse = None + self.playingAnim = None + self.soundTeleport = None + self.cheesyEffect = ToontownGlobals.CENormal + self.effectTrack = None + self.emoteTrack = None + self.emote = None + self.stunTrack = None + self.__bookActors = [] + self.__holeActors = [] + self.holeClipPath = None + self.wake = None + self.lastWakeTime = 0 + self.forceJumpIdle = False + self.numPies = 0 + self.pieType = 0 + self.pieThrowType = ToontownGlobals.PieThrowArc + self.pieModel = None + self.__pieModelType = None + self.pieScale = 1.0 + self.hatNodes = [] + self.glassesNodes = [] + self.backpackNodes = [] + self.hat = (0, 0, 0) + self.glasses = (0, 0, 0) + self.backpack = (0, 0, 0) + self.shoes = (0, 0, 0) + self.isStunned = 0 + self.isDisguised = 0 + self.defaultColorScale = None + self.jar = None + self.headMeter = None + self.gmIcon = None + self.partyHat = None + self.setTag('pieCode', str(ToontownGlobals.PieCodeToon)) + self.setFont(ToontownGlobals.getToonFont()) + self.nametag.setSpeechFont(ToontownGlobals.getToonFont()) + self.soundChatBubble = base.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.ogg') + self.swimRunSfx = base.loadSfx('phase_4/audio/sfx/AV_footstep_runloop_water.ogg') + self.swimRunLooping = False + self.animFSM = ClassicFSM('Toon', [State('off', self.enterOff, self.exitOff), + State('neutral', self.enterNeutral, self.exitNeutral), + State('victory', self.enterVictory, self.exitVictory), + State('Happy', self.enterHappy, self.exitHappy), + State('Sad', self.enterSad, self.exitSad), + State('Catching', self.enterCatching, self.exitCatching), + State('CatchEating', self.enterCatchEating, self.exitCatchEating), + State('Sleep', self.enterSleep, self.exitSleep), + State('walk', self.enterWalk, self.exitWalk), + State('jumpSquat', self.enterJumpSquat, self.exitJumpSquat), + State('jump', self.enterJump, self.exitJump), + State('jumpAirborne', self.enterJumpAirborne, self.exitJumpAirborne), + State('jumpLand', self.enterJumpLand, self.exitJumpLand), + State('run', self.enterRun, self.exitRun), + State('swim', self.enterSwim, self.exitSwim), + State('swimhold', self.enterSwimHold, self.exitSwimHold), + State('dive', self.enterDive, self.exitDive), + State('cringe', self.enterCringe, self.exitCringe), + State('OpenBook', self.enterOpenBook, self.exitOpenBook, ['ReadBook', 'CloseBook']), + State('ReadBook', self.enterReadBook, self.exitReadBook), + State('CloseBook', self.enterCloseBook, self.exitCloseBook), + State('TeleportOut', self.enterTeleportOut, self.exitTeleportOut), + State('Died', self.enterDied, self.exitDied), + State('TeleportedOut', self.enterTeleportedOut, self.exitTeleportedOut), + State('TeleportIn', self.enterTeleportIn, self.exitTeleportIn), + State('Emote', self.enterEmote, self.exitEmote), + State('SitStart', self.enterSitStart, self.exitSitStart), + State('Sit', self.enterSit, self.exitSit), + State('Push', self.enterPush, self.exitPush), + State('Squish', self.enterSquish, self.exitSquish), + State('FallDown', self.enterFallDown, self.exitFallDown), + State('GolfPuttLoop', self.enterGolfPuttLoop, self.exitGolfPuttLoop), + State('GolfRotateLeft', self.enterGolfRotateLeft, self.exitGolfRotateLeft), + State('GolfRotateRight', self.enterGolfRotateRight, self.exitGolfRotateRight), + State('GolfPuttSwing', self.enterGolfPuttSwing, self.exitGolfPuttSwing), + State('GolfGoodPutt', self.enterGolfGoodPutt, self.exitGolfGoodPutt), + State('GolfBadPutt', self.enterGolfBadPutt, self.exitGolfBadPutt), + State('Flattened', self.enterFlattened, self.exitFlattened), + State('CogThiefRunning', self.enterCogThiefRunning, self.exitCogThiefRunning), + State('ScientistJealous', self.enterScientistJealous, self.exitScientistJealous), + State('ScientistEmcee', self.enterScientistEmcee, self.exitScientistEmcee), + State('ScientistWork', self.enterScientistWork, self.exitScientistWork), + State('ScientistLessWork', self.enterScientistLessWork, self.exitScientistLessWork), + State('ScientistPlay', self.enterScientistPlay, self.enterScientistPlay)], 'off', 'off') + animStateList = self.animFSM.getStates() + self.animFSM.enterInitialState() + + def stopAnimations(self): + if hasattr(self, 'animFSM'): + if not self.animFSM.isInternalStateInFlux(): + self.animFSM.request('off') + else: + self.notify.warning('animFSM in flux, state=%s, not requesting off' % self.animFSM.getCurrentState().getName()) + else: + self.notify.warning('animFSM has been deleted') + if self.effectTrack != None: + self.effectTrack.finish() + self.effectTrack = None + if self.emoteTrack != None: + self.emoteTrack.finish() + self.emoteTrack = None + if self.stunTrack != None: + self.stunTrack.finish() + self.stunTrack = None + if self.wake: + self.wake.stop() + self.wake.destroy() + self.wake = None + self.cleanupPieModel() + return + + def delete(self): + try: + self.Toon_deleted + except: + self.Toon_deleted = 1 + self.stopAnimations() + self.rightHands = None + self.rightHand = None + self.leftHands = None + self.leftHand = None + self.headParts = None + self.torsoParts = None + self.hipsParts = None + self.legsParts = None + del self.animFSM + for bookActor in self.__bookActors: + bookActor.cleanup() + + del self.__bookActors + for holeActor in self.__holeActors: + holeActor.cleanup() + + del self.__holeActors + self.soundTeleport = None + self.motion.delete() + self.motion = None + + self.removeHeadMeter() + self.removeGMIcon() + self.removePartyHat() + Avatar.Avatar.delete(self) + ToonHead.delete(self) + + def updateToonDNA(self, newDNA, fForce = 0): + self.style.gender = newDNA.getGender() + oldDNA = self.style + if fForce or newDNA.head != oldDNA.head: + self.swapToonHead(newDNA.head) + if fForce or newDNA.torso != oldDNA.torso: + self.swapToonTorso(newDNA.torso, genClothes=0) + self.loop('neutral') + if fForce or newDNA.legs != oldDNA.legs: + self.swapToonLegs(newDNA.legs) + self.swapToonColor(newDNA) + self.__swapToonClothes(newDNA) + + def setDNAString(self, dnaString): + newDNA = ToonDNA.ToonDNA() + newDNA.makeFromNetString(dnaString) + if len(newDNA.torso) < 2: + self.sendLogSuspiciousEvent('nakedToonDNA %s was requested' % newDNA.torso) + newDNA.torso = newDNA.torso + 's' + self.setDNA(newDNA) + + def setDNA(self, dna): + if hasattr(self, 'isDisguised'): + if self.isDisguised: + return + if self.style: + self.updateToonDNA(dna) + else: + self.style = dna + self.generateToon() + self.initializeDropShadow() + self.initializeNametag3d() + + def parentToonParts(self): + if self.hasLOD(): + for lodName in self.getLODNames(): + if base.config.GetBool('want-new-anims', 1): + if not self.getPart('torso', lodName).find('**/def_head').isEmpty(): + self.attach('head', 'torso', 'def_head', lodName) + else: + self.attach('head', 'torso', 'joint_head', lodName) + else: + self.attach('head', 'torso', 'joint_head', lodName) + self.attach('torso', 'legs', 'joint_hips', lodName) + + else: + self.attach('head', 'torso', 'joint_head') + self.attach('torso', 'legs', 'joint_hips') + + def unparentToonParts(self): + if self.hasLOD(): + for lodName in self.getLODNames(): + self.getPart('head', lodName).reparentTo(self.getLOD(lodName)) + self.getPart('torso', lodName).reparentTo(self.getLOD(lodName)) + self.getPart('legs', lodName).reparentTo(self.getLOD(lodName)) + + else: + self.getPart('head').reparentTo(self.getGeomNode()) + self.getPart('torso').reparentTo(self.getGeomNode()) + self.getPart('legs').reparentTo(self.getGeomNode()) + + def setLODs(self): + self.setLODNode() + levelOneIn = base.config.GetInt('lod1-in', 20) + levelOneOut = base.config.GetInt('lod1-out', 0) + levelTwoIn = base.config.GetInt('lod2-in', 80) + levelTwoOut = base.config.GetInt('lod2-out', 20) + levelThreeIn = base.config.GetInt('lod3-in', 280) + levelThreeOut = base.config.GetInt('lod3-out', 80) + self.addLOD(1000, levelOneIn, levelOneOut) + self.addLOD(500, levelTwoIn, levelTwoOut) + self.addLOD(250, levelThreeIn, levelThreeOut) + + def generateToon(self): + self.setLODs() + self.generateToonLegs() + self.generateToonHead() + self.generateToonTorso() + self.generateToonColor() + self.parentToonParts() + self.rescaleToon() + self.resetHeight() + self.setupToonNodes() + + def setupToonNodes(self): + rightHand = NodePath('rightHand') + self.rightHand = None + self.rightHands = [] + leftHand = NodePath('leftHand') + self.leftHands = [] + self.leftHand = None + for lodName in self.getLODNames(): + hand = self.getPart('torso', lodName).find('**/joint_Rhold') + if base.config.GetBool('want-new-anims', 1): + if not self.getPart('torso', lodName).find('**/def_joint_right_hold').isEmpty(): + hand = self.getPart('torso', lodName).find('**/def_joint_right_hold') + else: + hand = self.getPart('torso', lodName).find('**/joint_Rhold') + self.rightHands.append(hand) + rightHand = rightHand.instanceTo(hand) + if base.config.GetBool('want-new-anims', 1): + if not self.getPart('torso', lodName).find('**/def_joint_left_hold').isEmpty(): + hand = self.getPart('torso', lodName).find('**/def_joint_left_hold') + else: + hand = self.getPart('torso', lodName).find('**/joint_Lhold') + self.leftHands.append(hand) + leftHand = leftHand.instanceTo(hand) + if self.rightHand == None: + self.rightHand = rightHand + if self.leftHand == None: + self.leftHand = leftHand + + self.headParts = self.findAllMatches('**/__Actor_head') + self.legsParts = self.findAllMatches('**/__Actor_legs') + self.hipsParts = self.legsParts.findAllMatches('**/joint_hips') + self.torsoParts = self.hipsParts.findAllMatches('**/__Actor_torso') + return + + def initializeBodyCollisions(self, collIdStr): + Avatar.Avatar.initializeBodyCollisions(self, collIdStr) + if not self.ghostMode: + self.collNode.setCollideMask(self.collNode.getIntoCollideMask() | ToontownGlobals.PieBitmask) + + def getBookActors(self): + if self.__bookActors: + return self.__bookActors + bookActor = Actor.Actor('phase_3.5/models/props/book-mod', {'book': 'phase_3.5/models/props/book-chan'}) + bookActor2 = Actor.Actor(other=bookActor) + bookActor3 = Actor.Actor(other=bookActor) + self.__bookActors = [bookActor, bookActor2, bookActor3] + hands = self.getRightHands() + for bookActor, hand in zip(self.__bookActors, hands): + bookActor.reparentTo(hand) + bookActor.hide() + + return self.__bookActors + + def getHoleActors(self): + if self.__holeActors: + return self.__holeActors + holeActor = Actor.Actor('phase_3.5/models/props/portal-mod', {'hole': 'phase_3.5/models/props/portal-chan'}) + holeActor2 = Actor.Actor(other=holeActor) + holeActor3 = Actor.Actor(other=holeActor) + self.__holeActors = [holeActor, holeActor2, holeActor3] + for ha in self.__holeActors: + if hasattr(self, 'uniqueName'): + holeName = self.uniqueName('toon-portal') + else: + holeName = 'toon-portal' + ha.setName(holeName) + + return self.__holeActors + + def rescaleToon(self): + animalStyle = self.style.getAnimal() + bodyScale = ToontownGlobals.toonBodyScales[animalStyle] + headScale = ToontownGlobals.toonHeadScales[animalStyle] + self.setAvatarScale(bodyScale) + for lod in self.getLODNames(): + self.getPart('head', lod).setScale(headScale) + + def getBodyScale(self): + animalStyle = self.style.getAnimal() + bodyScale = ToontownGlobals.toonBodyScales[animalStyle] + return bodyScale + + def resetHeight(self): + if hasattr(self, 'style') and self.style: + animal = self.style.getAnimal() + bodyScale = ToontownGlobals.toonBodyScales[animal] + headScale = ToontownGlobals.toonHeadScales[animal][2] + shoulderHeight = ToontownGlobals.legHeightDict[self.style.legs] * bodyScale + ToontownGlobals.torsoHeightDict[self.style.torso] * bodyScale + height = shoulderHeight + ToontownGlobals.headHeightDict[self.style.head] * headScale + self.shoulderHeight = shoulderHeight + if self.cheesyEffect == ToontownGlobals.CEBigToon or self.cheesyEffect == ToontownGlobals.CEBigWhite: + height *= ToontownGlobals.BigToonScale + elif self.cheesyEffect == ToontownGlobals.CESmallToon: + height *= ToontownGlobals.SmallToonScale + self.setHeight(height) + + def generateToonLegs(self, copy = 1): + global Preloaded + legStyle = self.style.legs + filePrefix = LegDict.get(legStyle) + if filePrefix is None: + self.notify.error('unknown leg style: %s' % legStyle) + self.loadModel(Preloaded[filePrefix+'-1000'], 'legs', '1000', True) + self.loadModel(Preloaded[filePrefix+'-500'], 'legs', '500', True) + self.loadModel(Preloaded[filePrefix+'-250'], 'legs', '250', True) + if not copy: + self.showPart('legs', '1000') + self.showPart('legs', '500') + self.showPart('legs', '250') + self.loadAnims(LegsAnimDict[legStyle], 'legs', '1000') + self.loadAnims(LegsAnimDict[legStyle], 'legs', '500') + self.loadAnims(LegsAnimDict[legStyle], 'legs', '250') + self.findAllMatches('**/boots_short').stash() + self.findAllMatches('**/boots_long').stash() + self.findAllMatches('**/shoes').stash() + return + + def swapToonLegs(self, legStyle, copy = 1): + self.unparentToonParts() + self.removePart('legs', '1000') + self.removePart('legs', '500') + self.removePart('legs', '250') + # Bugfix: Until upstream Panda3D includes this, we have to do it here. + if 'legs' in self._Actor__commonBundleHandles: + del self._Actor__commonBundleHandles['legs'] + self.style.legs = legStyle + self.generateToonLegs(copy) + self.generateToonColor() + self.parentToonParts() + self.rescaleToon() + self.resetHeight() + del self.shadowJoint + self.initializeDropShadow() + self.initializeNametag3d() + + def generateToonTorso(self, copy = 1, genClothes = 1): + global Preloaded + torsoStyle = self.style.torso + filePrefix = TorsoDict.get(torsoStyle) + if filePrefix is None: + self.notify.error('unknown torso style: %s' % torsoStyle) + self.loadModel(Preloaded[filePrefix+'-1000'], 'torso', '1000', True) + if len(torsoStyle) == 1: + self.loadModel(Preloaded[filePrefix+'-1000'], 'torso', '500', True) + self.loadModel(Preloaded[filePrefix+'-1000'], 'torso', '250', True) + else: + self.loadModel(Preloaded[filePrefix+'-500'], 'torso', '500', True) + self.loadModel(Preloaded[filePrefix+'-250'], 'torso', '250', True) + if not copy: + self.showPart('torso', '1000') + self.showPart('torso', '500') + self.showPart('torso', '250') + self.loadAnims(TorsoAnimDict[torsoStyle], 'torso', '1000') + self.loadAnims(TorsoAnimDict[torsoStyle], 'torso', '500') + self.loadAnims(TorsoAnimDict[torsoStyle], 'torso', '250') + if genClothes == 1 and not len(torsoStyle) == 1: + self.generateToonClothes() + return + + def swapToonTorso(self, torsoStyle, copy = 1, genClothes = 1): + self.unparentToonParts() + self.removePart('torso', '1000') + self.removePart('torso', '500') + self.removePart('torso', '250') + # Bugfix: Until upstream Panda3D includes this, we have to do it here. + if 'torso' in self._Actor__commonBundleHandles: + del self._Actor__commonBundleHandles['torso'] + self.style.torso = torsoStyle + self.generateToonTorso(copy, genClothes) + self.generateToonColor() + self.parentToonParts() + self.rescaleToon() + self.resetHeight() + self.setupToonNodes() + self.generateBackpack() + + def generateToonHead(self, copy = 1): + headHeight = ToonHead.generateToonHead(self, copy, self.style, ('1000', '500', '250')) + if self.style.getAnimal() == 'dog': + self.loadAnims(HeadAnimDict[self.style.head], 'head', '1000') + self.loadAnims(HeadAnimDict[self.style.head], 'head', '500') + self.loadAnims(HeadAnimDict[self.style.head], 'head', '250') + + def swapToonHead(self, headStyle=-1, copy = 1): + self.stopLookAroundNow() + self.eyelids.request('open') + self.unparentToonParts() + self.removePart('head', '1000') + self.removePart('head', '500') + self.removePart('head', '250') + # Bugfix: Until upstream Panda3D includes this, we have to do it here. + if 'head' in self._Actor__commonBundleHandles: + del self._Actor__commonBundleHandles['head'] + if headStyle > -1: + self.style.head = headStyle + self.generateToonHead(copy) + self.generateToonColor() + self.parentToonParts() + self.rescaleToon() + self.resetHeight() + self.eyelids.request('open') + self.startLookAround() + + def generateToonColor(self): + ToonHead.generateToonColor(self, self.style) + armColor = self.style.getArmColor() + gloveColor = self.style.getGloveColor() + legColor = self.style.getLegColor() + for lodName in self.getLODNames(): + torso = self.getPart('torso', lodName) + if len(self.style.torso) == 1: + parts = torso.findAllMatches('**/torso*') + parts.setColor(*armColor) + for pieceName in ('arms', 'neck'): + piece = torso.find('**/' + pieceName) + piece.setColor(*armColor) + + hands = torso.find('**/hands') + hands.setColor(*gloveColor) + legs = self.getPart('legs', lodName) + for pieceName in ('legs', 'feet'): + piece = legs.find('**/%s;+s' % pieceName) + piece.setColor(*legColor) + + if self.cheesyEffect == ToontownGlobals.CEGreenToon: + self.reapplyCheesyEffect() + + def swapToonColor(self, dna): + self.setStyle(dna) + self.generateToonColor() + + def __swapToonClothes(self, dna): + self.setStyle(dna) + self.generateToonClothes(fromNet=1) + + def sendLogSuspiciousEvent(self, msg): + pass + + def generateToonClothes(self, fromNet = 0): + swappedTorso = 0 + if self.hasLOD(): + if self.style.getGender() == 'f' and fromNet == 0: + try: + bottomPair = ToonDNA.GirlBottoms[self.style.botTex] + except: + bottomPair = ToonDNA.GirlBottoms[0] + + if len(self.style.torso) < 2: + self.sendLogSuspiciousEvent('nakedToonDNA %s was requested' % self.style.torso) + return 0 + elif self.style.torso[1] == 's' and bottomPair[1] == ToonDNA.SKIRT: + self.swapToonTorso(self.style.torso[0] + 'd', genClothes=0) + swappedTorso = 1 + elif self.style.torso[1] == 'd' and bottomPair[1] == ToonDNA.SHORTS: + self.swapToonTorso(self.style.torso[0] + 's', genClothes=0) + swappedTorso = 1 + try: + texName = ToonDNA.Shirts[self.style.topTex] + except: + texName = ToonDNA.Shirts[0] + + shirtTex = loader.loadTexture(texName, okMissing=True) + if shirtTex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + shirtTex = loader.loadTexture(ToonDNA.Shirts[0]) + shirtTex.setMinfilter(Texture.FTLinearMipmapLinear) + shirtTex.setMagfilter(Texture.FTLinear) + try: + shirtColor = ToonDNA.ClothesColors[self.style.topTexColor] + except: + shirtColor = ToonDNA.ClothesColors[0] + + try: + texName = ToonDNA.Sleeves[self.style.sleeveTex] + except: + texName = ToonDNA.Sleeves[0] + + sleeveTex = loader.loadTexture(texName, okMissing=True) + if sleeveTex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + sleeveTex = loader.loadTexture(ToonDNA.Sleeves[0]) + sleeveTex.setMinfilter(Texture.FTLinearMipmapLinear) + sleeveTex.setMagfilter(Texture.FTLinear) + try: + sleeveColor = ToonDNA.ClothesColors[self.style.sleeveTexColor] + except: + sleeveColor = ToonDNA.ClothesColors[0] + + if self.style.getGender() == 'm': + try: + texName = ToonDNA.BoyShorts[self.style.botTex] + except: + texName = ToonDNA.BoyShorts[0] + + else: + try: + texName = ToonDNA.GirlBottoms[self.style.botTex][0] + except: + texName = ToonDNA.GirlBottoms[0][0] + + bottomTex = loader.loadTexture(texName, okMissing=True) + if bottomTex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + if self.style.getGender() == 'm': + bottomTex = loader.loadTexture(ToonDNA.BoyShorts[0]) + else: + bottomTex = loader.loadTexture(ToonDNA.GirlBottoms[0][0]) + bottomTex.setMinfilter(Texture.FTLinearMipmapLinear) + bottomTex.setMagfilter(Texture.FTLinear) + try: + bottomColor = ToonDNA.ClothesColors[self.style.botTexColor] + except: + bottomColor = ToonDNA.ClothesColors[0] + + darkBottomColor = bottomColor * 0.5 + darkBottomColor.setW(1.0) + for lodName in self.getLODNames(): + thisPart = self.getPart('torso', lodName) + top = thisPart.find('**/torso-top') + top.setTexture(shirtTex, 1) + top.setColor(shirtColor) + sleeves = thisPart.find('**/sleeves') + sleeves.setTexture(sleeveTex, 1) + sleeves.setColor(sleeveColor) + bottoms = thisPart.findAllMatches('**/torso-bot') + for bottomNum in xrange(0, bottoms.getNumPaths()): + bottom = bottoms.getPath(bottomNum) + bottom.setTexture(bottomTex, 1) + bottom.setColor(bottomColor) + + caps = thisPart.findAllMatches('**/torso-bot-cap') + caps.setColor(darkBottomColor) + + return swappedTorso + + def generateHat(self, fromRTM = False): + hat = self.getHat() + if hat[0] >= len(ToonDNA.HatModels): + self.sendLogSuspiciousEvent('tried to put a wrong hat idx %d' % hat[0]) + return + if len(self.hatNodes) > 0: + for hatNode in self.hatNodes: + hatNode.removeNode() + + self.hatNodes = [] + self.showEars() + if hat[0] != 0: + hatGeom = loader.loadModel(ToonDNA.HatModels[hat[0]], okMissing=True) + if hatGeom: + if hat[0] == 54: + self.hideEars() + if hat[1] != 0: + texName = ToonDNA.HatTextures[hat[1]] + tex = loader.loadTexture(texName, okMissing=True) + if tex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + else: + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + hatGeom.setTexture(tex, 1) + if fromRTM: + reload(AccessoryGlobals) + transOffset = None + if AccessoryGlobals.ExtendedHatTransTable.get(hat[0]): + transOffset = AccessoryGlobals.ExtendedHatTransTable[hat[0]].get(self.style.head[:2]) + if transOffset is None: + transOffset = AccessoryGlobals.HatTransTable.get(self.style.head[:2]) + if transOffset is None: + return + hatGeom.setPos(transOffset[0][0], transOffset[0][1], transOffset[0][2]) + hatGeom.setHpr(transOffset[1][0], transOffset[1][1], transOffset[1][2]) + hatGeom.setScale(transOffset[2][0], transOffset[2][1], transOffset[2][2]) + headNodes = self.findAllMatches('**/__Actor_head') + for headNode in headNodes: + hatNode = headNode.attachNewNode('hatNode') + self.hatNodes.append(hatNode) + hatGeom.instanceTo(hatNode) + + return + + def generateGlasses(self, fromRTM = False): + glasses = self.getGlasses() + if glasses[0] >= len(ToonDNA.GlassesModels): + self.sendLogSuspiciousEvent('tried to put a wrong glasses idx %d' % glasses[0]) + return + if len(self.glassesNodes) > 0: + for glassesNode in self.glassesNodes: + glassesNode.removeNode() + + self.glassesNodes = [] + self.showEyelashes() + if glasses[0] != 0: + glassesGeom = loader.loadModel(ToonDNA.GlassesModels[glasses[0]], okMissing=True) + if glassesGeom: + if glasses[0] in [15, 16]: + self.hideEyelashes() + if glasses[1] != 0: + texName = ToonDNA.GlassesTextures[glasses[1]] + tex = loader.loadTexture(texName, okMissing=True) + if tex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + else: + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + glassesGeom.setTexture(tex, 1) + if fromRTM: + reload(AccessoryGlobals) + transOffset = None + if AccessoryGlobals.ExtendedGlassesTransTable.get(glasses[0]): + transOffset = AccessoryGlobals.ExtendedGlassesTransTable[glasses[0]].get(self.style.head[:2]) + if transOffset is None: + transOffset = AccessoryGlobals.GlassesTransTable.get(self.style.head[:2]) + if transOffset is None: + return + glassesGeom.setPos(transOffset[0][0], transOffset[0][1], transOffset[0][2]) + glassesGeom.setHpr(transOffset[1][0], transOffset[1][1], transOffset[1][2]) + glassesGeom.setScale(transOffset[2][0], transOffset[2][1], transOffset[2][2]) + headNodes = self.findAllMatches('**/__Actor_head') + for headNode in headNodes: + glassesNode = headNode.attachNewNode('glassesNode') + self.glassesNodes.append(glassesNode) + glassesGeom.instanceTo(glassesNode) + + return + + def generateBackpack(self, fromRTM = False): + backpack = self.getBackpack() + if backpack[0] >= len(ToonDNA.BackpackModels): + self.sendLogSuspiciousEvent('tried to put a wrong backpack idx %d' % backpack[0]) + return + if len(self.backpackNodes) > 0: + for backpackNode in self.backpackNodes: + backpackNode.removeNode() + + self.backpackNodes = [] + if backpack[0] != 0: + geom = loader.loadModel(ToonDNA.BackpackModels[backpack[0]], okMissing=True) + if geom: + if backpack[1] != 0: + texName = ToonDNA.BackpackTextures[backpack[1]] + tex = loader.loadTexture(texName, okMissing=True) + if tex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + else: + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + geom.setTexture(tex, 1) + if fromRTM: + reload(AccessoryGlobals) + transOffset = None + if AccessoryGlobals.ExtendedBackpackTransTable.get(backpack[0]): + transOffset = AccessoryGlobals.ExtendedBackpackTransTable[backpack[0]].get(self.style.torso[:1]) + if transOffset is None: + transOffset = AccessoryGlobals.BackpackTransTable.get(self.style.torso[:1]) + if transOffset is None: + return + geom.setPos(transOffset[0][0], transOffset[0][1], transOffset[0][2]) + geom.setHpr(transOffset[1][0], transOffset[1][1], transOffset[1][2]) + geom.setScale(transOffset[2][0], transOffset[2][1], transOffset[2][2]) + nodes = self.findAllMatches('**/def_joint_attachFlower') + for node in nodes: + theNode = node.attachNewNode('backpackNode') + self.backpackNodes.append(theNode) + geom.instanceTo(theNode) + + return + + def generateShoes(self): + shoes = self.getShoes() + if shoes[0] >= len(ToonDNA.ShoesModels): + self.sendLogSuspiciousEvent('tried to put a wrong shoes idx %d' % shoes[0]) + return + self.findAllMatches('**/feet;+s').stash() + self.findAllMatches('**/boots_short;+s').stash() + self.findAllMatches('**/boots_long;+s').stash() + self.findAllMatches('**/shoes;+s').stash() + geoms = self.findAllMatches('**/%s;+s' % ToonDNA.ShoesModels[shoes[0]]) + for geom in geoms: + geom.unstash() + + if shoes[0] != 0: + for geom in geoms: + texName = ToonDNA.ShoesTextures[shoes[1]] + if self.style.legs == 'l' and shoes[0] == 3: + texName = texName[:-4] + 'LL.jpg' + tex = loader.loadTexture(texName, okMissing=True) + if tex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + else: + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + geom.setTexture(tex, 1) + + return + + def generateToonAccessories(self): + self.generateHat() + self.generateGlasses() + self.generateBackpack() + self.generateShoes() + + def setHat(self, hatIdx, textureIdx, colorIdx, fromRTM = False): + self.hat = (hatIdx, textureIdx, colorIdx) + self.generateHat(fromRTM=fromRTM) + + def getHat(self): + return self.hat + + def setGlasses(self, glassesIdx, textureIdx, colorIdx, fromRTM = False): + self.glasses = (glassesIdx, textureIdx, colorIdx) + self.generateGlasses(fromRTM=fromRTM) + + def getGlasses(self): + return self.glasses + + def setBackpack(self, backpackIdx, textureIdx, colorIdx, fromRTM = False): + self.backpack = (backpackIdx, textureIdx, colorIdx) + self.generateBackpack(fromRTM=fromRTM) + + def getBackpack(self): + return self.backpack + + def setShoes(self, shoesIdx, textureIdx, colorIdx): + self.shoes = (shoesIdx, textureIdx, colorIdx) + self.generateShoes() + + def getShoes(self): + return self.shoes + + def getDialogueArray(self): + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.APRIL_TOONS_WEEK): + animalType = random.choice(TTLocalizer.AnimalToSpecies.keys()) + else: + animalType = self.style.getType() + if animalType == 'dog': + dialogueArray = DogDialogueArray + elif animalType == 'cat': + dialogueArray = CatDialogueArray + elif animalType == 'horse': + dialogueArray = HorseDialogueArray + elif animalType == 'mouse': + dialogueArray = MouseDialogueArray + elif animalType == 'rabbit': + dialogueArray = RabbitDialogueArray + elif animalType == 'duck': + dialogueArray = DuckDialogueArray + elif animalType == 'monkey': + dialogueArray = MonkeyDialogueArray + elif animalType == 'bear': + dialogueArray = BearDialogueArray + elif animalType == 'pig': + dialogueArray = PigDialogueArray + else: + dialogueArray = None + return dialogueArray + + def getShadowJoint(self): + if hasattr(self, 'shadowJoint'): + return self.shadowJoint + shadowJoint = NodePath('shadowJoint') + for lodName in self.getLODNames(): + joint = self.getPart('legs', lodName).find('**/joint_shadow') + shadowJoint = shadowJoint.instanceTo(joint) + + self.shadowJoint = shadowJoint + return shadowJoint + + def getNametagJoints(self): + joints = [] + for lodName in self.getLODNames(): + bundle = self.getPartBundle('legs', lodName) + joint = bundle.findChild('joint_nameTag') + if joint: + joints.append(joint) + + return joints + + def getRightHands(self): + return self.rightHands + + def getLeftHands(self): + return self.leftHands + + def getHeadParts(self): + return self.headParts + + def getHipsParts(self): + return self.hipsParts + + def getTorsoParts(self): + return self.torsoParts + + def getLegsParts(self): + return self.legsParts + + def findSomethingToLookAt(self): + if self.randGen.random() < 0.1 or not hasattr(self, 'cr'): + x = self.randGen.choice((-0.8, + -0.5, + 0, + 0.5, + 0.8)) + y = self.randGen.choice((-0.5, + 0, + 0.5, + 0.8)) + self.lerpLookAt(Point3(x, 1.5, y), blink=1) + return + nodePathList = [] + for id, obj in self.cr.doId2do.items(): + if hasattr(obj, 'getStareAtNodeAndOffset') and obj != self: + node, offset = obj.getStareAtNodeAndOffset() + if node.getY(self) > 0.0: + nodePathList.append((node, offset)) + + if nodePathList: + nodePathList.sort(lambda x, y: cmp(x[0].getDistance(self), y[0].getDistance(self))) + if len(nodePathList) >= 2: + if self.randGen.random() < 0.9: + chosenNodePath = nodePathList[0] + else: + chosenNodePath = nodePathList[1] + else: + chosenNodePath = nodePathList[0] + self.lerpLookAt(chosenNodePath[0].getPos(self), blink=1) + else: + ToonHead.findSomethingToLookAt(self) + + def setForceJumpIdle(self, value): + self.forceJumpIdle = value + + def setupPickTrigger(self): + Avatar.Avatar.setupPickTrigger(self) + torso = self.getPart('torso', '1000') + if torso == None: + return 0 + self.pickTriggerNp.reparentTo(torso) + size = self.style.getTorsoSize() + if size == 'short': + self.pickTriggerNp.setPosHprScale(0, 0, 0.5, 0, 0, 0, 1.5, 1.5, 2) + elif size == 'medium': + self.pickTriggerNp.setPosHprScale(0, 0, 0.5, 0, 0, 0, 1, 1, 2) + else: + self.pickTriggerNp.setPosHprScale(0, 0, 1, 0, 0, 0, 1, 1, 2) + return 1 + + def showBooks(self): + for bookActor in self.getBookActors(): + bookActor.show() + + def hideBooks(self): + for bookActor in self.getBookActors(): + bookActor.hide() + + def getWake(self): + if not self.wake: + self.wake = Wake.Wake(render, self) + return self.wake + + def getJar(self): + if not self.jar: + self.jar = loader.loadModel('phase_5.5/models/estate/jellybeanJar') + self.jar.setP(290.0) + self.jar.setY(0.5) + self.jar.setZ(0.5) + self.jar.setScale(0.0) + return self.jar + + def removeJar(self): + if self.jar: + self.jar.removeNode() + self.jar = None + return + + def setSpeed(self, forwardSpeed, rotateSpeed): + self.forwardSpeed = forwardSpeed + self.rotateSpeed = rotateSpeed + action = None + if self.standWalkRunReverse != None: + if forwardSpeed >= ToontownGlobals.RunCutOff: + action = OTPGlobals.RUN_INDEX + elif forwardSpeed > ToontownGlobals.WalkCutOff: + action = OTPGlobals.WALK_INDEX + elif forwardSpeed < -ToontownGlobals.WalkCutOff: + action = OTPGlobals.REVERSE_INDEX + elif rotateSpeed != 0.0: + action = OTPGlobals.WALK_INDEX + else: + action = OTPGlobals.STAND_INDEX + anim, rate = self.standWalkRunReverse[action] + self.motion.enter() + self.motion.setState(anim, rate) + if anim != self.playingAnim: + self.playingAnim = anim + self.playingRate = rate + self.stop() + self.loop(anim) + self.setPlayRate(rate, anim) + if self.isDisguised: + rightHand = self.suit.rightHand + numChildren = rightHand.getNumChildren() + if numChildren > 0: + anim = 'tray-' + anim + if anim == 'tray-run': + anim = 'tray-walk' + self.suit.stop() + self.suit.loop(anim) + self.suit.setPlayRate(rate, anim) + elif rate != self.playingRate: + self.playingRate = rate + if not self.isDisguised: + self.setPlayRate(rate, anim) + else: + self.suit.setPlayRate(rate, anim) + showWake, wakeWaterHeight = ZoneUtil.getWakeInfo() + if showWake and self.getZ(render) < wakeWaterHeight and abs(forwardSpeed) > ToontownGlobals.WalkCutOff: + currT = globalClock.getFrameTime() + deltaT = currT - self.lastWakeTime + if action == OTPGlobals.RUN_INDEX and deltaT > ToontownGlobals.WakeRunDelta or deltaT > ToontownGlobals.WakeWalkDelta: + self.getWake().createRipple(wakeWaterHeight, rate=1, startFrame=4) + if not self.swimRunLooping: + base.playSfx(self.swimRunSfx, node=self, looping=1) + self.lastWakeTime = currT + self.swimRunLooping = True + else: + self.stopSwimRunSfx() + return action + + def stopSwimRunSfx(self): + if self.swimRunLooping: + self.swimRunSfx.stop() + self.swimRunLooping = False + + def enterOff(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.setActiveShadow(0) + self.playingAnim = None + return + + def exitOff(self): + pass + + def enterNeutral(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + anim = 'neutral' + self.pose(anim, int(self.getNumFrames(anim) * self.randGen.random())) + self.loop(anim, restart=0) + self.setPlayRate(animMultiplier, anim) + self.playingAnim = anim + self.setActiveShadow(1) + + def exitNeutral(self): + self.stop() + + def enterVictory(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + anim = 'victory' + frame = int(ts * self.getFrameRate(anim) * animMultiplier) + self.pose(anim, frame) + self.loop('victory', restart=0) + self.setPlayRate(animMultiplier, 'victory') + self.playingAnim = anim + self.setActiveShadow(0) + + def exitVictory(self): + self.stop() + + def enterHappy(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.playingAnim = None + self.playingRate = None + self.standWalkRunReverse = (('neutral', 1.0), + ('walk', 1.0), + ('run', 1.0), + ('walk', -1.0)) + self.setSpeed(self.forwardSpeed, self.rotateSpeed) + self.setActiveShadow(1) + return + + def exitHappy(self): + self.standWalkRunReverse = None + self.stop() + self.motion.exit() + return + + def enterSad(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.playingAnim = 'sad' + self.playingRate = None + self.standWalkRunReverse = (('sad-neutral', 1.0), + ('sad-walk', 1.2), + ('sad-walk', 1.2), + ('sad-walk', -1.0)) + self.setSpeed(0, 0) + Emote.globalEmote.disableBody(self, 'toon, enterSad') + self.setActiveShadow(1) + if self.isLocal(): + self.controlManager.disableAvatarJump() + return + + def exitSad(self): + self.standWalkRunReverse = None + self.stop() + self.motion.exit() + Emote.globalEmote.releaseBody(self, 'toon, exitSad') + if self.isLocal(): + self.controlManager.enableAvatarJump() + return + + def enterCatching(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.playingAnim = None + self.playingRate = None + self.standWalkRunReverse = (('catch-neutral', 1.0), + ('catch-run', 1.0), + ('catch-run', 1.0), + ('catch-run', -1.0)) + self.setSpeed(self.forwardSpeed, self.rotateSpeed) + self.setActiveShadow(1) + return + + def exitCatching(self): + self.standWalkRunReverse = None + self.stop() + self.motion.exit() + return + + def enterCatchEating(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.playingAnim = None + self.playingRate = None + self.standWalkRunReverse = (('catch-eatneutral', 1.0), + ('catch-eatnrun', 1.0), + ('catch-eatnrun', 1.0), + ('catch-eatnrun', -1.0)) + self.setSpeed(self.forwardSpeed, self.rotateSpeed) + self.setActiveShadow(0) + return + + def exitCatchEating(self): + self.standWalkRunReverse = None + self.stop() + self.motion.exit() + return + + def enterWalk(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('walk') + self.setPlayRate(animMultiplier, 'walk') + self.setActiveShadow(1) + + def exitWalk(self): + self.stop() + + def getJumpDuration(self): + if self.playingAnim == 'neutral': + return self.getDuration('jump', 'legs') + else: + return self.getDuration('running-jump', 'legs') + + def enterJump(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if not self.isDisguised: + if self.playingAnim == 'neutral': + anim = 'jump' + else: + anim = 'running-jump' + self.playingAnim = anim + self.setPlayRate(animMultiplier, anim) + self.play(anim) + self.setActiveShadow(1) + + def exitJump(self): + self.stop() + self.playingAnim = 'neutral' + + def enterJumpSquat(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if not self.isDisguised: + if self.playingAnim == 'neutral': + anim = 'jump-squat' + else: + anim = 'running-jump-squat' + self.playingAnim = anim + self.setPlayRate(animMultiplier, anim) + self.play(anim) + self.setActiveShadow(1) + + def exitJumpSquat(self): + self.stop() + self.playingAnim = 'neutral' + + def enterJumpAirborne(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if not self.isDisguised: + if self.playingAnim == 'neutral' or self.forceJumpIdle: + anim = 'jump-idle' + else: + anim = 'running-jump-idle' + self.playingAnim = anim + self.setPlayRate(animMultiplier, anim) + self.loop(anim) + self.setActiveShadow(1) + + def exitJumpAirborne(self): + self.stop() + self.playingAnim = 'neutral' + + def enterJumpLand(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if not self.isDisguised: + if self.playingAnim == 'running-jump-idle': + anim = 'running-jump-land' + skipStart = 0.2 + else: + anim = 'jump-land' + skipStart = 0.0 + self.playingAnim = anim + self.setPlayRate(animMultiplier, anim) + self.play(anim) + self.setActiveShadow(1) + + def exitJumpLand(self): + self.stop() + self.playingAnim = 'neutral' + + def enterRun(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('run') + self.setPlayRate(animMultiplier, 'run') + Emote.globalEmote.disableBody(self, 'toon, enterRun') + self.setActiveShadow(1) + + def exitRun(self): + self.stop() + Emote.globalEmote.releaseBody(self, 'toon, exitRun') + + def enterSwim(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.stopSwimRunSfx() + Emote.globalEmote.disableAll(self, 'enterSwim') + self.playingAnim = 'swim' + self.loop('swim') + self.setPlayRate(animMultiplier, 'swim') + self.getGeomNode().setP(-89.0) + self.dropShadow.hide() + if self.isLocal(): + self.book.obscureButton(1) + self.useSwimControls() + self.nametag3d.setPos(0, -2, 1) + self.startBobSwimTask() + self.setActiveShadow(0) + + def enterCringe(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('cringe') + self.getGeomNode().setPos(0, 0, -2) + self.setPlayRate(animMultiplier, 'swim') + + def exitCringe(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.stop() + self.getGeomNode().setPos(0, 0, 0) + self.playingAnim = 'neutral' + self.setPlayRate(animMultiplier, 'swim') + + def enterDive(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('swim') + if hasattr(self.getGeomNode(), 'setPos'): + self.getGeomNode().setPos(0, 0, -2) + self.setPlayRate(animMultiplier, 'swim') + self.setActiveShadow(0) + self.dropShadow.hide() + self.nametag3d.setPos(0, -2, 1) + + def exitDive(self): + self.stop() + self.getGeomNode().setPos(0, 0, 0) + self.playingAnim = 'neutral' + self.dropShadow.show() + self.nametag3d.setPos(0, 0, self.height + 0.5) + + def enterSwimHold(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.getGeomNode().setPos(0, 0, -2) + self.nametag3d.setPos(0, -2, 1) + self.pose('swim', 55) + + def exitSwimHold(self): + self.stop() + self.getGeomNode().setPos(0, 0, 0) + self.playingAnim = 'neutral' + self.dropShadow.show() + self.nametag3d.setPos(0, 0, self.height + 0.5) + + def exitSwim(self): + self.stop() + self.playingAnim = 'neutral' + self.stopBobSwimTask() + self.getGeomNode().setPosHpr(0, 0, 0, 0, 0, 0) + self.dropShadow.show() + if self.isLocal(): + self.useWalkControls() + self.book.obscureButton(False) + self.nametag3d.setPos(0, 0, self.height + 0.5) + Emote.globalEmote.releaseAll(self, 'exitSwim') + + def startBobSwimTask(self): + if getattr(self, 'swimBob', None): + self.swimBob.finish() + self.swimBob = None + self.nametag3d.setZ(5.0) + geomNode = self.getGeomNode() + geomNode.setZ(4.0) + self.swimBob = Sequence( + geomNode.posInterval(1, Point3(0, -3, 3), startPos=Point3(0, -3, 4), blendType='easeInOut'), + geomNode.posInterval(1, Point3(0, -3, 4), startPos=Point3(0, -3, 3), blendType='easeInOut')) + self.swimBob.loop() + + def stopBobSwimTask(self): + swimBob = getattr(self, 'swimBob', None) + if swimBob: + swimBob.finish() + self.getGeomNode().setPos(0, 0, 0) + self.nametag3d.setZ(1.0) + + def enterOpenBook(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableAll(self, 'enterOpenBook') + self.playingAnim = 'openBook' + self.stopLookAround() + self.lerpLookAt(Point3(0, 1, -2)) + bookTracks = Parallel() + for bookActor in self.getBookActors(): + bookTracks.append(ActorInterval(bookActor, 'book', startTime=1.2, endTime=1.5)) + + bookTracks.append(ActorInterval(self, 'book', startTime=1.2, endTime=1.5)) + if hasattr(self, 'uniqueName'): + trackName = self.uniqueName('openBook') + else: + trackName = 'openBook' + self.track = Sequence(Func(self.showBooks), bookTracks, Wait(0.1), name=trackName) + if callback: + self.track.setDoneEvent(self.track.getName()) + self.acceptOnce(self.track.getName(), callback, extraArgs) + self.track.start(ts) + self.setActiveShadow(0) + + def exitOpenBook(self): + self.playingAnim = 'neutralob' + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + self.hideBooks() + self.startLookAround() + Emote.globalEmote.releaseAll(self, 'exitOpenBook') + return + + def enterReadBook(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableBody(self, 'enterReadBook') + self.playingAnim = 'readBook' + self.stopLookAround() + self.lerpLookAt(Point3(0, 1, -2)) + self.showBooks() + for bookActor in self.getBookActors(): + bookActor.pingpong('book', fromFrame=38, toFrame=118) + + self.pingpong('book', fromFrame=38, toFrame=118) + self.setActiveShadow(0) + + def exitReadBook(self): + self.playingAnim = 'neutralrb' + self.hideBooks() + for bookActor in self.getBookActors(): + bookActor.stop() + + self.startLookAround() + Emote.globalEmote.releaseBody(self, 'exitReadBook') + + def enterCloseBook(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableAll(self, 'enterCloseBook') + self.playingAnim = 'closeBook' + bookTracks = Parallel() + for bookActor in self.getBookActors(): + bookTracks.append(ActorInterval(bookActor, 'book', startTime=4.96, endTime=6.5)) + + bookTracks.append(ActorInterval(self, 'book', startTime=4.96, endTime=6.5)) + if hasattr(self, 'uniqueName'): + trackName = self.uniqueName('closeBook') + else: + trackName = 'closeBook' + self.track = Sequence(Func(self.showBooks), bookTracks, Func(self.hideBooks), name=trackName) + if callback: + self.track.setDoneEvent(self.track.getName()) + self.acceptOnce(self.track.getName(), callback, extraArgs) + self.track.start(ts) + self.setActiveShadow(0) + + def exitCloseBook(self): + self.playingAnim = 'neutralcb' + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + Emote.globalEmote.releaseAll(self, 'exitCloseBook') + return + + def getSoundTeleport(self): + if not self.soundTeleport: + self.soundTeleport = base.loadSfx('phase_3.5/audio/sfx/AV_teleport.ogg') + return self.soundTeleport + + def getTeleportOutTrack(self, autoFinishTrack = 1): + + def showHoles(holes, hands): + for hole, hand in zip(holes, hands): + hole.reparentTo(hand) + + def reparentHoles(holes, toon): + holes[0].reparentTo(toon) + holes[1].detachNode() + holes[2].detachNode() + holes[0].setBin('shadow', 0) + holes[0].setDepthTest(0) + holes[0].setDepthWrite(0) + + def cleanupHoles(holes): + holes[0].detachNode() + holes[0].clearBin() + holes[0].clearDepthTest() + holes[0].clearDepthWrite() + + holes = self.getHoleActors() + hands = self.getRightHands() + holeTrack = Track((0.0, Func(showHoles, holes, hands)), (0.5, SoundInterval(self.getSoundTeleport(), node=self)), (1.708, Func(reparentHoles, holes, self)), (3.4, Func(cleanupHoles, holes))) + if hasattr(self, 'uniqueName'): + trackName = self.uniqueName('teleportOut') + else: + trackName = 'teleportOut' + track = Parallel(holeTrack, name=trackName, autoFinish=autoFinishTrack) + for hole in holes: + track.append(ActorInterval(hole, 'hole', duration=3.4)) + + track.append(ActorInterval(self, 'teleport', duration=3.4)) + return track + + def startQuestMap(self): + pass + + def stopQuestMap(self): + pass + + def enterTeleportOut(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + name = self.name + if hasattr(self, 'doId'): + name += '-' + str(self.doId) + self.notify.debug('enterTeleportOut %s' % name) + if self.ghostMode or self.isDisguised: + if callback: + callback(*extraArgs) + return + self.playingAnim = 'teleport' + Emote.globalEmote.disableAll(self, 'enterTeleportOut') + if self.isLocal(): + autoFinishTrack = 0 + else: + autoFinishTrack = 1 + self.track = self.getTeleportOutTrack(autoFinishTrack) + self.track.setDoneEvent(self.track.getName()) + self.acceptOnce(self.track.getName(), self.finishTeleportOut, [callback, extraArgs]) + holeClip = PlaneNode('holeClip') + self.holeClipPath = self.attachNewNode(holeClip) + self.getGeomNode().setClipPlane(self.holeClipPath) + self.nametag3d.setClipPlane(self.holeClipPath) + avHeight = max(self.getHeight(), 3) + + if self == base.localAvatar and settings['tpTransition'] and not ZoneUtil.isDynamicZone(self.zoneId): + def lerpCam(task): + degrees = task.time * 52.941 + radians = degrees * (math.pi / 180.0) + x = -12 * math.sin(radians) + y = -12 * math.cos(radians) + z = base.localAvatar.getHeight() + camera.setPos(x, y, z) + camera.setH(-degrees) + return task.done if task.time > 3.4 else task.cont + + taskMgr.add(lerpCam, 'lerpCam') + + self.track.start(ts) + self.setActiveShadow(0) + + def finishTeleportOut(self, callback = None, extraArgs = []): + name = self.name + if hasattr(self, 'doId'): + name += '-' + str(self.doId) + self.notify.debug('finishTeleportOut %s' % name) + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + if hasattr(self, 'animFSM'): + self.animFSM.request('TeleportedOut') + if callback: + callback(*extraArgs) + return + + def exitTeleportOut(self): + name = self.name + if hasattr(self, 'doId'): + name += '-' + str(self.doId) + self.notify.debug('exitTeleportOut %s' % name) + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + self.track = None + geomNode = self.getGeomNode() + if geomNode and not geomNode.isEmpty(): + self.getGeomNode().clearClipPlane() + if self.nametag3d and not self.nametag3d.isEmpty(): + self.nametag3d.clearClipPlane() + if self.holeClipPath: + self.holeClipPath.removeNode() + self.holeClipPath = None + Emote.globalEmote.releaseAll(self, 'exitTeleportOut') + if self and not self.isEmpty(): + self.show() + return + + def enterTeleportedOut(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.setActiveShadow(0) + + def exitTeleportedOut(self): + pass + + def getDiedInterval(self, autoFinishTrack = 1): + sound = loader.loadSfx('phase_5/audio/sfx/ENC_Lose.ogg') + if hasattr(self, 'uniqueName'): + trackName = self.uniqueName('died') + else: + trackName = 'died' + ival = Sequence(Func(Emote.globalEmote.disableBody, self), Func(self.sadEyes), Func(self.blinkEyes), Track((0, ActorInterval(self, 'lose')), (2, SoundInterval(sound, node=self)), (5.333, self.scaleInterval(1.5, VBase3(0.01, 0.01, 0.01), blendType='easeInOut'))), Func(self.detachNode), Func(self.setScale, 1, 1, 1), Func(self.normalEyes), Func(self.blinkEyes), Func(Emote.globalEmote.releaseBody, self), name=trackName, autoFinish=autoFinishTrack) + return ival + + def enterDied(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if self.ghostMode: + if callback: + callback(*extraArgs) + return + if self.isDisguised: + self.takeOffSuit() + self.playingAnim = 'lose' + Emote.globalEmote.disableAll(self, 'enterDied') + if self.isLocal(): + autoFinishTrack = 0 + else: + autoFinishTrack = 1 + if hasattr(self, 'jumpLandAnimFixTask') and self.jumpLandAnimFixTask: + self.jumpLandAnimFixTask.remove() + self.jumpLandAnimFixTask = None + self.track = self.getDiedInterval(autoFinishTrack) + if callback: + self.track = Sequence(self.track, Func(callback, *extraArgs), autoFinish=autoFinishTrack) + self.track.start(ts) + self.setActiveShadow(0) + return + + def finishDied(self, callback = None, extraArgs = []): + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + if hasattr(self, 'animFSM'): + self.animFSM.request('TeleportedOut') + if callback: + callback(*extraArgs) + return + + def exitDied(self): + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + Emote.globalEmote.releaseAll(self, 'exitDied') + self.show() + return + + def getTeleportInTrack(self): + hole = self.getHoleActors()[0] + hole.setBin('shadow', 0) + hole.setDepthTest(0) + hole.setDepthWrite(0) + holeTrack = Sequence() + holeTrack.append(Func(hole.reparentTo, self)) + pos = Point3(0, -2.4, 0) + holeTrack.append(Func(hole.setPos, self, pos)) + holeTrack.append(ActorInterval(hole, 'hole', startTime=3.4, endTime=3.1)) + holeTrack.append(Wait(0.6)) + holeTrack.append(ActorInterval(hole, 'hole', startTime=3.1, endTime=3.4)) + + def restoreHole(hole): + hole.setPos(0, 0, 0) + hole.detachNode() + hole.clearBin() + hole.clearDepthTest() + hole.clearDepthWrite() + + holeTrack.append(Func(restoreHole, hole)) + toonTrack = Sequence(Wait(0.3), Func(self.getGeomNode().show), Func(self.nametag3d.show), ActorInterval(self, 'jump', startTime=0.45)) + if hasattr(self, 'uniqueName'): + trackName = self.uniqueName('teleportIn') + else: + trackName = 'teleportIn' + return Parallel(holeTrack, toonTrack, name=trackName) + + def enterTeleportIn(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if self.ghostMode or self.isDisguised: + if callback: + callback(*extraArgs) + return + self.show() + self.playingAnim = 'teleport' + Emote.globalEmote.disableAll(self, 'enterTeleportIn') + self.pose('teleport', self.getNumFrames('teleport') - 1) + self.getGeomNode().hide() + self.nametag3d.hide() + self.track = self.getTeleportInTrack() + if callback: + self.track.setDoneEvent(self.track.getName()) + self.acceptOnce(self.track.getName(), callback, extraArgs) + self.track.start(ts) + self.setActiveShadow(0) + + def exitTeleportIn(self): + self.playingAnim = None + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + if not self.ghostMode and not self.isDisguised: + self.getGeomNode().show() + self.nametag3d.show() + Emote.globalEmote.releaseAll(self, 'exitTeleportIn') + return + + def enterSitStart(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableBody(self) + self.playingAnim = 'sit-start' + if self.isLocal(): + self.track = Sequence(ActorInterval(self, 'sit-start'), Func(self.b_setAnimState, 'Sit', animMultiplier)) + else: + self.track = Sequence(ActorInterval(self, 'sit-start')) + self.track.start(ts) + self.setActiveShadow(0) + + def exitSitStart(self): + self.playingAnim = 'neutral' + if self.track != None: + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + Emote.globalEmote.releaseBody(self) + return + + def enterSit(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableBody(self) + self.playingAnim = 'sit' + self.loop('sit') + self.setActiveShadow(0) + + def exitSit(self): + self.playingAnim = 'neutral' + Emote.globalEmote.releaseBody(self) + + def enterSleep(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.stopLookAround() + self.stopBlink() + self.closeEyes() + self.lerpLookAt(Point3(0, 1, -4)) + self.loop('neutral') + self.setPlayRate(animMultiplier * 0.4, 'neutral') + self.setChatAbsolute(SLEEP_STRING, CFThought) + if self == base.localAvatar: + self.notify.debug('Adding timeout task to Toon.') + taskMgr.doMethodLater(self.afkTimeout, self.__handleAfkTimeout, self.uniqueName('afkTimeout')) + self.setActiveShadow(0) + + def __handleAfkTimeout(self, task): + self.notify.debug('Handling timeout task on Toon.') + self.ignore('wakeup') + self.takeOffSuit() + base.cr.playGame.getPlace().fsm.request('final') + self.b_setAnimState('TeleportOut', 1, self.__handleAfkExitTeleport, [0]) + return Task.done + + def __handleAfkExitTeleport(self, requestStatus): + self.notify.info('closing shard...') + base.cr.gameFSM.request('closeShard', ['afkTimeout']) + + def exitSleep(self): + taskMgr.remove(self.uniqueName('afkTimeout')) + self.startLookAround() + self.openEyes() + self.startBlink() + if config.GetBool('stuck-sleep-fix', 1): + doClear = SLEEP_STRING in (self.nametag.getChat(), self.nametag.getStompText()) + else: + doClear = self.nametag.getChat() == SLEEP_STRING + if doClear: + self.clearChat() + self.lerpLookAt(Point3(0, 1, 0), time=0.25) + self.stop() + + def enterPush(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableBody(self) + self.playingAnim = 'push' + self.track = Sequence(ActorInterval(self, 'push')) + self.track.loop() + self.setActiveShadow(1) + + def exitPush(self): + self.playingAnim = 'neutral' + if self.track != None: + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + Emote.globalEmote.releaseBody(self) + return + + def enterEmote(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if len(extraArgs) > 0: + emoteIndex = extraArgs[0] + else: + return + self.playingAnim = None + self.playingRate = None + self.standWalkRunReverse = (('neutral', 1.0), + ('walk', 1.0), + ('run', 1.0), + ('walk', -1.0)) + self.setSpeed(self.forwardSpeed, self.rotateSpeed) + if self.isLocal() and emoteIndex != Emote.globalEmote.EmoteSleepIndex: + if self.sleepFlag: + self.b_setAnimState('Happy', self.animMultiplier) + self.wakeUp() + duration = 0 + self.emoteTrack, duration = Emote.globalEmote.doEmote(self, emoteIndex, ts) + self.setActiveShadow(1) + return + + def doEmote(self, emoteIndex, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if not self.isLocal(): + if base.localAvatar.isIgnored(self.doId): + return + duration = 0 + if self.isLocal(): + self.wakeUp() + if self.hasTrackAnimToSpeed(): + self.trackAnimToSpeed(None) + self.emoteTrack, duration = Emote.globalEmote.doEmote(self, emoteIndex, ts) + return + + def __returnToLastAnim(self, task): + if self.playingAnim: + self.loop(self.playingAnim) + elif self.hp > 0: + self.loop('neutral') + else: + self.loop('sad-neutral') + return Task.done + + def __finishEmote(self, task): + if self.isLocal(): + if self.hp > 0: + self.b_setAnimState('Happy') + else: + self.b_setAnimState('Sad') + return Task.done + + def exitEmote(self): + self.stop() + if self.emoteTrack != None: + self.emoteTrack.finish() + self.emoteTrack = None + taskMgr.remove(self.taskName('finishEmote')) + return + + def enterSquish(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableAll(self) + sound = loader.loadSfx('phase_9/audio/sfx/toon_decompress.ogg') + lerpTime = 0.1 + node = self.getGeomNode().getChild(0) + origScale = node.getScale() + self.track = Sequence(LerpScaleInterval(node, lerpTime, VBase3(2, 2, 0.025), blendType='easeInOut'), Wait(1.0), Parallel(Sequence(Wait(0.4), LerpScaleInterval(node, lerpTime, VBase3(1.4, 1.4, 1.4), blendType='easeInOut'), LerpScaleInterval(node, lerpTime / 2.0, VBase3(0.8, 0.8, 0.8), blendType='easeInOut'), LerpScaleInterval(node, lerpTime / 3.0, origScale, blendType='easeInOut')), ActorInterval(self, 'jump', startTime=0.2), SoundInterval(sound))) + self.track.start(ts) + self.setActiveShadow(1) + + def exitSquish(self): + self.playingAnim = 'neutral' + if self.track != None: + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + Emote.globalEmote.releaseAll(self) + return + + def enterFallDown(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.playingAnim = 'fallDown' + Emote.globalEmote.disableAll(self) + self.track = Sequence(ActorInterval(self, 'slip-backward'), name='fallTrack') + if callback: + self.track.setDoneEvent(self.track.getName()) + self.acceptOnce(self.track.getName(), callback, extraArgs) + self.track.start(ts) + + def exitFallDown(self): + self.playingAnim = 'neutral' + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + Emote.globalEmote.releaseAll(self) + return + + def stunToon(self, ts = 0, callback = None, knockdown = 0): + if not self.isStunned: + if self.stunTrack: + self.stunTrack.finish() + self.stunTrack = None + + def setStunned(stunned): + self.isStunned = stunned + if self == base.localAvatar: + messenger.send('toonStunned-' + str(self.doId), [self.isStunned]) + + node = self.getGeomNode() + lerpTime = 0.5 + down = self.doToonColorScale(VBase4(1, 1, 1, 0.6), lerpTime) + up = self.doToonColorScale(VBase4(1, 1, 1, 0.9), lerpTime) + clear = self.doToonColorScale(self.defaultColorScale, lerpTime) + track = Sequence(Func(setStunned, 1), down, up, down, up, down, up, down, clear, Func(self.restoreDefaultColorScale), Func(setStunned, 0)) + if knockdown: + self.stunTrack = Parallel(ActorInterval(self, animName='slip-backward'), track) + else: + self.stunTrack = track + self.stunTrack.start() + return + + def getPieces(self, *pieces): + results = [] + for lodName in self.getLODNames(): + for partName, pieceNames in pieces: + part = self.getPart(partName, lodName) + if part: + if type(pieceNames) == types.StringType: + pieceNames = (pieceNames,) + for pieceName in pieceNames: + npc = part.findAllMatches('**/%s;+s' % pieceName) + for i in xrange(npc.getNumPaths()): + results.append(npc[i]) + + return results + + def applyCheesyEffect(self, effect, lerpTime = 0): + if self.effectTrack != None: + self.effectTrack.finish() + self.effectTrack = None + if self.cheesyEffect != effect: + oldEffect = self.cheesyEffect + self.cheesyEffect = effect + if oldEffect == ToontownGlobals.CENormal: + self.effectTrack = self.__doCheesyEffect(effect, lerpTime) + elif effect == ToontownGlobals.CENormal: + self.effectTrack = self.__undoCheesyEffect(oldEffect, lerpTime) + else: + self.effectTrack = Sequence(self.__undoCheesyEffect(oldEffect, lerpTime / 2.0), self.__doCheesyEffect(effect, lerpTime / 2.0)) + self.effectTrack.start() + return + + def reapplyCheesyEffect(self, lerpTime = 0): + if self.effectTrack != None: + self.effectTrack.finish() + self.effectTrack = None + effect = self.cheesyEffect + self.effectTrack = Sequence(self.__undoCheesyEffect(effect, 0), self.__doCheesyEffect(effect, lerpTime)) + self.effectTrack.start() + return + + def clearCheesyEffect(self, lerpTime = 0): + self.applyCheesyEffect(ToontownGlobals.CENormal, lerpTime=lerpTime) + if self.effectTrack != None: + self.effectTrack.finish() + self.effectTrack = None + return + + def __doHeadScale(self, scale, lerpTime): + if scale == None: + scale = ToontownGlobals.toonHeadScales[self.style.getAnimal()] + track = Parallel() + for hi in xrange(self.headParts.getNumPaths()): + head = self.headParts[hi] + track.append(LerpScaleInterval(head, lerpTime, scale, blendType='easeInOut')) + + return track + + def __doLegsScale(self, scale, lerpTime): + if scale == None: + scale = 1 + invScale = 1 + else: + invScale = 1.0 / scale + track = Parallel() + for li in xrange(self.legsParts.getNumPaths()): + legs = self.legsParts[li] + torso = self.torsoParts[li] + track.append(LerpScaleInterval(legs, lerpTime, scale, blendType='easeInOut')) + track.append(LerpScaleInterval(torso, lerpTime, invScale, blendType='easeInOut')) + + return track + + def __doToonScale(self, scale, lerpTime): + if scale == None: + scale = 1 + node = self.getGeomNode().getChild(0) + track = Sequence(Parallel(LerpHprInterval(node, lerpTime, Vec3(0.0, 0.0, 0.0), blendType='easeInOut'), LerpScaleInterval(node, lerpTime, scale, blendType='easeInOut')), Func(self.resetHeight)) + return track + + def doToonColorScale(self, scale, lerpTime, keepDefault = 0): + if keepDefault: + self.defaultColorScale = scale + if scale == None: + scale = VBase4(1, 1, 1, 1) + node = self.getGeomNode() + caps = self.getPieces(('torso', 'torso-bot-cap')) + track = Sequence() + track.append(Func(node.setTransparency, 1)) + if scale[3] != 1: + for cap in caps: + track.append(HideInterval(cap)) + + track.append(LerpColorScaleInterval(node, lerpTime, scale, blendType='easeInOut')) + if scale[3] == 1: + track.append(Func(node.clearTransparency)) + for cap in caps: + track.append(ShowInterval(cap)) + + elif scale[3] == 0: + track.append(Func(node.clearTransparency)) + return track + + def __doPumpkinHeadSwitch(self, lerpTime, toPumpkin): + node = self.getGeomNode() + dust = self.getDustCloud(0.0) + track = Sequence() + + if toPumpkin: + track.append(Func(self.stopBlink)) + track.append(Func(self.closeEyes)) + if lerpTime > 0.0: + track.append(Func(dust.start)) + track.append(Wait(0.5)) + else: + dust.finish() + + def hideParts(): + self.notify.debug('hideParts') + for head in self.headParts: + for p in head.getChildren(): + if hasattr(self, 'pumpkins') and not self.pumpkins.hasPath(p): + p.hide() + p.setTag('pumpkin', 'enabled') + + track.append(Func(hideParts)) + track.append(Func(self.enablePumpkins, True)) + else: + if lerpTime > 0.0: + track.append(Func(dust.start)) + track.append(Wait(0.5)) + else: + dust.finish() + + def showHiddenParts(): + self.notify.debug('showHiddenParts') + for head in self.headParts: + for p in head.getChildren(): + if not self.pumpkins.hasPath(p) and p.getTag('pumpkin') == 'enabled': + p.show() + p.setTag('pumpkin', 'disabled') + + track.append(Func(showHiddenParts)) + track.append(Func(self.enablePumpkins, False)) + track.append(Func(self.startBlink)) + return track + + def __doSnowManHeadSwitch(self, lerpTime, toSnowMan): + node = self.getGeomNode() + dust = self.getDustCloud(0.0) + track = Sequence() + + if toSnowMan: + track.append(Func(self.stopBlink)) + track.append(Func(self.closeEyes)) + if lerpTime > 0.0: + track.append(Func(dust.start)) + track.append(Wait(0.5)) + else: + dust.finish() + + def hideParts(): + self.notify.debug('HidePaths') + for hi in xrange(self.headParts.getNumPaths()): + head = self.headParts[hi] + parts = head.getChildren() + for pi in xrange(parts.getNumPaths()): + p = parts[pi] + if not p.isHidden(): + p.hide() + p.setTag('snowman', 'enabled') + + track.append(Func(hideParts)) + track.append(Func(self.enableSnowMen, True)) + else: + if lerpTime > 0.0: + track.append(Func(dust.start)) + track.append(Wait(0.5)) + else: + dust.finish() + + def showHiddenParts(): + self.notify.debug('ShowHiddenPaths') + for hi in xrange(self.headParts.getNumPaths()): + head = self.headParts[hi] + parts = head.getChildren() + for pi in xrange(parts.getNumPaths()): + p = parts[pi] + if not self.snowMen.hasPath(p) and p.getTag('snowman') == 'enabled': + p.show() + p.setTag('snowman', 'disabled') + + track.append(Func(showHiddenParts)) + track.append(Func(self.enableSnowMen, False)) + track.append(Func(self.startBlink)) + return track + + def __doGreenToon(self, lerpTime, toGreen): + track = Sequence() + greenTrack = Parallel() + + if lerpTime > 0.0: + dust = self.getDustCloud(0.0) + track.append(Func(dust.start)) + track.append(Wait(0.5)) + if toGreen: + skinGreen = VBase4(76 / 255.0, 240 / 255.0, 84 / 255.0, 1) + muzzleGreen = VBase4(4 / 255.0, 205 / 255.0, 90 / 255.0, 1) + gloveGreen = VBase4(14 / 255.0, 173 / 255.0, 40 / 255.0, 1) + greenTrack.append(self.__colorToonSkin(skinGreen, lerpTime)) + greenTrack.append(self.__colorToonEars(skinGreen, muzzleGreen, lerpTime)) + greenTrack.append(self.__colorScaleToonMuzzle(muzzleGreen, lerpTime)) + greenTrack.append(self.__colorToonGloves(gloveGreen, lerpTime)) + else: + greenTrack.append(self.__colorToonSkin(None, lerpTime)) + greenTrack.append(self.__colorToonEars(None, None, lerpTime)) + greenTrack.append(self.__colorScaleToonMuzzle(None, lerpTime)) + greenTrack.append(self.__colorToonGloves(None, lerpTime)) + track.append(greenTrack) + return track + + def __colorToonSkin(self, color, lerpTime): + track = Sequence() + colorTrack = Parallel() + torsoPieces = self.getPieces(('torso', ('arms', 'neck'))) + legPieces = self.getPieces(('legs', ('legs', 'feet'))) + headPieces = self.getPieces(('head', '*head*')) + if color == None: + armColor = self.style.getArmColor() + legColor = self.style.getLegColor() + headColor = self.style.getHeadColor() + else: + armColor = color + legColor = color + headColor = color + for piece in torsoPieces: + colorTrack.append(Func(piece.setColor, *armColor)) + + for piece in legPieces: + colorTrack.append(Func(piece.setColor, *legColor)) + + for piece in headPieces: + if 'hatNode' not in str(piece) and 'glassesNode' not in str(piece): + colorTrack.append(Func(piece.setColor, *headColor)) + + track.append(colorTrack) + return track + + def __colorToonEars(self, color, colorScale, lerpTime): + track = Sequence() + earPieces = self.getPieces(('head', '*ear*')) + if len(earPieces) == 0: + return track + colorTrack = Parallel() + if earPieces[0].hasColor(): + if color == None: + headColor = self.style.getHeadColor() + else: + headColor = color + for piece in earPieces: + colorTrack.append(Func(piece.setColor, *headColor)) + + else: + if colorScale == None: + colorScale = VBase4(1, 1, 1, 1) + for piece in earPieces: + colorTrack.append(Func(piece.setColorScale, *colorScale)) + + track.append(colorTrack) + return track + + def __colorScaleToonMuzzle(self, scale, lerpTime): + track = Sequence() + colorTrack = Parallel() + muzzlePieces = self.getPieces(('head', '*muzzle*')) + if scale == None: + scale = VBase4(1, 1, 1, 1) + for piece in muzzlePieces: + colorTrack.append(Func(piece.setColorScale, scale)) + + track.append(colorTrack) + return track + + def __colorToonGloves(self, color, lerpTime): + track = Sequence() + colorTrack = Parallel() + glovePieces = self.getPieces(('torso', '*hands*')) + if color == None: + for piece in glovePieces: + colorTrack.append(Func(piece.clearColor)) + + else: + for piece in glovePieces: + colorTrack.append(Func(piece.setColor, color)) + + track.append(colorTrack) + return track + + def __doBigAndWhite(self, color, scale, lerpTime): + track = Parallel() + track.append(self.__doToonColor(color, lerpTime)) + track.append(self.__doToonScale(scale, lerpTime)) + return track + + def __doVirtual(self): + track = Parallel() + track.append(self.__doToonColor(VBase4(0.25, 0.25, 1.0, 1), 0.0)) + self.setPartsAdd(self.getHeadParts()) + self.setPartsAdd(self.getTorsoParts()) + self.setPartsAdd(self.getHipsParts()) + self.setPartsAdd(self.getLegsParts()) + return track + + def __doUnVirtual(self): + track = Parallel() + track.append(self.__doToonColor(None, 0.0)) + self.setPartsNormal(self.getHeadParts(), 1) + self.setPartsNormal(self.getTorsoParts(), 1) + self.setPartsNormal(self.getHipsParts(), 1) + self.setPartsNormal(self.getLegsParts(), 1) + return track + + def setPartsAdd(self, parts): + actorCollection = parts + for thingIndex in xrange(0, actorCollection.getNumPaths()): + thing = actorCollection[thingIndex] + if thing.getName() not in ('joint_attachMeter', 'joint_nameTag'): + thing.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + thing.setDepthWrite(False) + self.setBin('fixed', 1) + + def setPartsNormal(self, parts, alpha = 0): + actorCollection = parts + for thingIndex in xrange(0, actorCollection.getNumPaths()): + thing = actorCollection[thingIndex] + if thing.getName() not in ('joint_attachMeter', 'joint_nameTag'): + thing.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MNone)) + thing.setDepthWrite(True) + self.setBin('default', 0) + if alpha: + thing.setTransparency(1) + thing.setBin('transparent', 0) + + def __doToonGhostColorScale(self, scale, lerpTime, keepDefault = 0): + if keepDefault: + self.defaultColorScale = scale + if scale == None: + scale = VBase4(1, 1, 1, 1) + node = self.getGeomNode() + caps = self.getPieces(('torso', 'torso-bot-cap')) + track = Sequence() + track.append(Func(node.setTransparency, 1)) + track.append(ShowInterval(node)) + if scale[3] != 1: + for cap in caps: + track.append(HideInterval(cap)) + + track.append(LerpColorScaleInterval(node, lerpTime, scale, blendType='easeInOut')) + if scale[3] == 1: + track.append(Func(node.clearTransparency)) + for cap in caps: + track.append(ShowInterval(cap)) + + elif scale[3] == 0: + track.append(Func(node.clearTransparency)) + track.append(HideInterval(node)) + return track + + def restoreDefaultColorScale(self): + node = self.getGeomNode() + if node: + if self.defaultColorScale: + node.setColorScale(self.defaultColorScale) + if self.defaultColorScale[3] != 1: + node.setTransparency(1) + else: + node.clearTransparency() + else: + node.clearColorScale() + node.clearTransparency() + + def __doToonColor(self, color, lerpTime): + node = self.getGeomNode() + if color == None: + return Func(node.clearColor) + else: + return Func(node.setColor, color, 1) + return + + def __doPartsColorScale(self, scale, lerpTime): + if scale == None: + scale = VBase4(1, 1, 1, 1) + node = self.getGeomNode() + pieces = self.getPieces(('torso', ('arms', 'neck')), ('legs', ('legs', 'feet')), ('head', '+GeomNode')) + track = Sequence() + track.append(Func(node.setTransparency, 1)) + for piece in pieces: + if piece.getName()[:7] == 'muzzle-' and piece.getName()[-8:] != '-neutral': + continue + track.append(ShowInterval(piece)) + + p1 = Parallel() + for piece in pieces: + if piece.getName()[:7] == 'muzzle-' and piece.getName()[-8:] != '-neutral': + continue + p1.append(LerpColorScaleInterval(piece, lerpTime, scale, blendType='easeInOut')) + + track.append(p1) + if scale[3] == 1: + track.append(Func(node.clearTransparency)) + elif scale[3] == 0: + track.append(Func(node.clearTransparency)) + for piece in pieces: + if piece.getName()[:7] == 'muzzle-' and piece.getName()[-8:] != '-neutral': + continue + track.append(HideInterval(piece)) + + self.generateHat() + self.generateGlasses() + return track + + def __doCheesyEffect(self, effect, lerpTime): + if effect == ToontownGlobals.CEBigHead: + return self.__doHeadScale(2.5, lerpTime) + elif effect == ToontownGlobals.CESmallHead: + return self.__doHeadScale(0.5, lerpTime) + elif effect == ToontownGlobals.CEBigLegs: + return self.__doLegsScale(1.4, lerpTime) + elif effect == ToontownGlobals.CESmallLegs: + return self.__doLegsScale(0.6, lerpTime) + elif effect == ToontownGlobals.CEBigToon: + return self.__doToonScale(ToontownGlobals.BigToonScale, lerpTime) + elif effect == ToontownGlobals.CESmallToon: + return self.__doToonScale(ToontownGlobals.SmallToonScale, lerpTime) + elif effect == ToontownGlobals.CEFlatPortrait: + return self.__doToonScale(VBase3(1, 0.05, 1), lerpTime) + elif effect == ToontownGlobals.CEFlatProfile: + return self.__doToonScale(VBase3(0.05, 1, 1), lerpTime) + elif effect == ToontownGlobals.CETransparent: + return self.doToonColorScale(VBase4(1, 1, 1, 0.6), lerpTime, keepDefault=1) + elif effect == ToontownGlobals.CENoColor: + return self.__doToonColor(VBase4(1, 1, 1, 1), lerpTime) + elif effect == ToontownGlobals.CEInvisible: + return self.__doPartsColorScale(VBase4(1, 1, 1, 0), lerpTime) + elif effect == ToontownGlobals.CEPumpkin: + return self.__doPumpkinHeadSwitch(lerpTime, toPumpkin=True) + elif effect == ToontownGlobals.CEBigWhite: + return self.__doBigAndWhite(VBase4(1, 1, 1, 1), ToontownGlobals.BigToonScale, lerpTime) + elif effect == ToontownGlobals.CESnowMan: + return self.__doSnowManHeadSwitch(lerpTime, toSnowMan=True) + elif effect == ToontownGlobals.CEGreenToon: + return self.__doGreenToon(lerpTime, toGreen=True) + elif effect == ToontownGlobals.CEVirtual: + return self.__doVirtual() + elif effect == ToontownGlobals.CEGhost: + alpha = 0.25 + if base.localAvatar.getAdminAccess() < self.adminAccess: + alpha = 0 + return Sequence(self.__doToonGhostColorScale(VBase4(1, 1, 1, alpha), lerpTime, keepDefault=1), Func(self.nametag3d.hide)) + return Sequence() + + def __undoCheesyEffect(self, effect, lerpTime): + if effect == ToontownGlobals.CEBigHead: + return self.__doHeadScale(None, lerpTime) + elif effect == ToontownGlobals.CESmallHead: + return self.__doHeadScale(None, lerpTime) + if effect == ToontownGlobals.CEBigLegs: + return self.__doLegsScale(None, lerpTime) + elif effect == ToontownGlobals.CESmallLegs: + return self.__doLegsScale(None, lerpTime) + elif effect == ToontownGlobals.CEBigToon: + return self.__doToonScale(None, lerpTime) + elif effect == ToontownGlobals.CESmallToon: + return self.__doToonScale(None, lerpTime) + elif effect == ToontownGlobals.CEFlatPortrait: + return self.__doToonScale(None, lerpTime) + elif effect == ToontownGlobals.CEFlatProfile: + return self.__doToonScale(None, lerpTime) + elif effect == ToontownGlobals.CETransparent: + return self.doToonColorScale(None, lerpTime, keepDefault=1) + elif effect == ToontownGlobals.CENoColor: + return self.__doToonColor(None, lerpTime) + elif effect == ToontownGlobals.CEInvisible: + return self.__doPartsColorScale(None, lerpTime) + elif effect == ToontownGlobals.CEPumpkin: + return self.__doPumpkinHeadSwitch(lerpTime, toPumpkin=False) + elif effect == ToontownGlobals.CEBigWhite: + return self.__doBigAndWhite(None, None, lerpTime) + elif effect == ToontownGlobals.CESnowMan: + return self.__doSnowManHeadSwitch(lerpTime, toSnowMan=False) + elif effect == ToontownGlobals.CEGreenToon: + return self.__doGreenToon(lerpTime, toGreen=False) + elif effect == ToontownGlobals.CEVirtual: + return self.__doUnVirtual() + elif effect == ToontownGlobals.CEGhost: + return Sequence(Func(self.nametag3d.show), self.__doToonGhostColorScale(None, lerpTime, keepDefault=1)) + return Sequence() + + def putOnSuit(self, suitType, setDisplayName = True, rental = False): + if self.isDisguised: + self.takeOffSuit() + from toontown.suit import Suit + deptIndex = suitType + suit = Suit.Suit() + dna = SuitDNA.SuitDNA() + if rental == True: + if SuitDNA.suitDepts[deptIndex] == 's': + suitType = 'cc' + elif SuitDNA.suitDepts[deptIndex] == 'm': + suitType = 'sc' + elif SuitDNA.suitDepts[deptIndex] == 'l': + suitType = 'bf' + elif SuitDNA.suitDepts[deptIndex] == 'c': + suitType = 'f' + else: + self.notify.warning('Suspicious: Incorrect rental suit department requested') + suitType = 'cc' + dna.newSuit(suitType) + suit.setStyle(dna) + suit.isDisguised = 1 + suit.generateSuit() + suit.initializeDropShadow() + suit.setPos(self.getPos()) + suit.setHpr(self.getHpr()) + for part in suit.getHeadParts(): + part.hide() + + suitHeadNull = suit.find('**/joint_head') + toonHead = self.getPart('head', '1000') + Emote.globalEmote.disableAll(self) + toonGeom = self.getGeomNode() + toonGeom.hide() + worldScale = toonHead.getScale(render) + self.headOrigScale = toonHead.getScale() + headPosNode = hidden.attachNewNode('headPos') + toonHead.reparentTo(headPosNode) + toonHead.setPos(0, 0, 0.2) + headPosNode.reparentTo(suitHeadNull) + headPosNode.setScale(render, worldScale) + suitGeom = suit.getGeomNode() + suitGeom.reparentTo(self) + if rental == True: + suit.makeRentalSuit(SuitDNA.suitDepts[deptIndex]) + self.suit = suit + self.suitGeom = suitGeom + self.setHeight(suit.getHeight()) + self.nametag3d.setPos(0, 0, self.height + 1.3) + if self.isLocal(): + if hasattr(self, 'book'): + self.book.obscureButton(1) + self.oldForward = ToontownGlobals.ToonForwardSpeed + self.oldReverse = ToontownGlobals.ToonReverseSpeed + self.oldRotate = ToontownGlobals.ToonRotateSpeed + ToontownGlobals.ToonForwardSpeed = ToontownGlobals.SuitWalkSpeed + ToontownGlobals.ToonReverseSpeed = ToontownGlobals.SuitWalkSpeed + ToontownGlobals.ToonRotateSpeed = ToontownGlobals.ToonRotateSlowSpeed + if self.hasTrackAnimToSpeed(): + self.stopTrackAnimToSpeed() + self.startTrackAnimToSpeed() + self.controlManager.disableAvatarJump() + indices = range(OTPLocalizer.SCMenuCommonCogIndices[0], OTPLocalizer.SCMenuCommonCogIndices[1] + 1) + customIndices = OTPLocalizer.SCMenuCustomCogIndices[suitType] + indices += range(customIndices[0], customIndices[1] + 1) + self.chatMgr.chatInputSpeedChat.addCogMenu(indices) + self.suit.loop('neutral') + self.isDisguised = 1 + self.setFont(ToontownGlobals.getSuitFont()) + self.nametag.setSpeechFont(ToontownGlobals.getSuitFont()) + if setDisplayName: + if hasattr(base, 'idTags') and base.idTags: + name = self.getAvIdName() + else: + name = self.getName() + suitDept = SuitDNA.suitDepts.index(SuitDNA.getSuitDept(suitType)) + suitName = SuitBattleGlobals.SuitAttributes[suitType]['name'] + self.nametag.setDisplayName(TTLocalizer.SuitBaseNameWithLevel % {'name': name, + 'dept': suitName, + 'level': self.cogLevels[suitDept] + 1}) + self.nametag.setWordwrap(9.0) + + def takeOffSuit(self): + if not self.isDisguised: + return + suitType = self.suit.style.name + toonHeadNull = self.find('**/1000/**/def_head') + if not toonHeadNull: + toonHeadNull = self.find('**/1000/**/joint_head') + toonHead = self.getPart('head', '1000') + toonHead.reparentTo(toonHeadNull) + toonHead.setScale(self.headOrigScale) + toonHead.setPos(0, 0, 0) + headPosNode = self.suitGeom.find('**/headPos') + headPosNode.removeNode() + self.suitGeom.reparentTo(self.suit) + self.resetHeight() + self.nametag3d.setPos(0, 0, self.height + 0.5) + toonGeom = self.getGeomNode() + toonGeom.show() + Emote.globalEmote.releaseAll(self) + self.isDisguised = 0 + self.setFont(ToontownGlobals.getToonFont()) + self.nametag.setSpeechFont(ToontownGlobals.getToonFont()) + self.nametag.setWordwrap(None) + if hasattr(base, 'idTags') and base.idTags: + name = self.getAvIdName() + else: + name = self.getName() + self.setDisplayName(name) + if self.isLocal(): + if hasattr(self, 'book'): + self.book.obscureButton(0) + ToontownGlobals.ToonForwardSpeed = self.oldForward + ToontownGlobals.ToonReverseSpeed = self.oldReverse + ToontownGlobals.ToonRotateSpeed = self.oldRotate + if self.hasTrackAnimToSpeed(): + self.stopTrackAnimToSpeed() + self.startTrackAnimToSpeed() + del self.oldForward + del self.oldReverse + del self.oldRotate + self.controlManager.enableAvatarJump() + self.chatMgr.chatInputSpeedChat.removeCogMenu() + self.suit.delete() + del self.suit + del self.suitGeom + + def makeWaiter(self): + if not self.isDisguised: + return + self.suit.makeWaiter(self.suitGeom) + + def getPieModel(self): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + if self.pieModel != None and self.__pieModelType != self.pieType: + self.pieModel.detachNode() + self.pieModel = None + if self.pieModel == None: + self.__pieModelType = self.pieType + pieName = ToontownBattleGlobals.pieNames[self.pieType] + self.pieModel = BattleProps.globalPropPool.getProp(pieName) + self.pieScale = self.pieModel.getScale() + return self.pieModel + + def getPresentPieInterval(self, x, y, z, h): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + from toontown.battle import MovieUtil + pie = self.getPieModel() + pieName = ToontownBattleGlobals.pieNames[self.pieType] + pieType = BattleProps.globalPropPool.getPropType(pieName) + animPie = Sequence() + pingpongPie = Sequence() + if pieType == 'actor': + animPie = ActorInterval(pie, pieName, startFrame=0, endFrame=31) + pingpongPie = Func(pie.pingpong, pieName, fromFrame=32, toFrame=47) + track = Sequence(Func(self.setPosHpr, x, y, z, h, 0, 0), Func(pie.reparentTo, self.rightHand), Func(pie.setPosHpr, 0, 0, 0, 0, 0, 0), Parallel(pie.scaleInterval(1, self.pieScale, startScale=MovieUtil.PNT3_NEARZERO), ActorInterval(self, 'throw', startFrame=0, endFrame=31), animPie), Func(self.pingpong, 'throw', fromFrame=32, toFrame=47), pingpongPie) + return track + + def getTossPieInterval(self, x, y, z, h, power, throwType, beginFlyIval = Sequence()): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + pie = self.getPieModel() + flyPie = pie.copyTo(NodePath('a')) + pieName = ToontownBattleGlobals.pieNames[self.pieType] + pieType = BattleProps.globalPropPool.getPropType(pieName) + animPie = Sequence() + if pieType == 'actor': + animPie = ActorInterval(pie, pieName, startFrame=48) + sound = loader.loadSfx('phase_3.5/audio/sfx/AA_pie_throw_only.ogg') + if throwType == ToontownGlobals.PieThrowArc: + t = power / 100.0 + dist = 100 - 70 * t + time = 1 + 0.5 * t + proj = ProjectileInterval(None, startPos=Point3(0, 0, 0), + endPos=Point3(0, dist, 0), duration=time) + relVel = proj.startVel + elif throwType == ToontownGlobals.PieThrowLinear: + magnitude = power / 2. + 25 + + relVel = Vec3(0, 1, 0.25) + relVel.normalize() + relVel *= magnitude + + def getVelocity(toon = self, relVel = relVel): + return render.getRelativeVector(toon, relVel) + + toss = Track((0, Sequence(Func(self.setPosHpr, x, y, z, h, 0, 0), Func(pie.reparentTo, self.rightHand), Func(pie.setPosHpr, 0, 0, 0, 0, 0, 0), Parallel(ActorInterval(self, 'throw', startFrame=48), animPie), Func(self.loop, 'neutral'))), (16.0 / 24.0, Func(pie.detachNode))) + fly = Track((14.0 / 24.0, SoundInterval(sound, node=self)), (16.0 / 24.0, Sequence(Func(flyPie.reparentTo, render), Func(flyPie.setScale, self.pieScale), Func(flyPie.setPosHpr, self, 0.52, 0.97, 2.24, 89.42, -10.56, 87.94), beginFlyIval, ProjectileInterval(flyPie, startVel=getVelocity, duration=3), Func(flyPie.detachNode)))) + return (toss, fly, flyPie) + + def getPieSplatInterval(self, x, y, z, pieCode): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + pieName = ToontownBattleGlobals.pieNames[self.pieType] + splatName = 'splat-%s' % pieName + if pieName == 'lawbook': + splatName = 'dust' + splat = BattleProps.globalPropPool.getProp(splatName) + splat.setBillboardPointWorld(2) + color = ToontownGlobals.PieCodeColors.get(pieCode) + if color: + splat.setColor(*color) + vol = 1.0 + if pieName == 'lawbook': + sound = loader.loadSfx('phase_11/audio/sfx/LB_evidence_miss.ogg') + vol = 0.25 + else: + sound = loader.loadSfx('phase_4/audio/sfx/AA_wholepie_only.ogg') + ival = Parallel(Func(splat.reparentTo, render), Func(splat.setPos, x, y, z), SoundInterval(sound, node=splat, volume=vol), Sequence(ActorInterval(splat, splatName), Func(splat.detachNode))) + return ival + + def cleanupPieModel(self): + if self.pieModel != None: + self.pieModel.detachNode() + self.pieModel = None + return + + def getFeedPetIval(self): + return Sequence(ActorInterval(self, 'feedPet'), Func(self.animFSM.request, 'neutral')) + + def getScratchPetIval(self): + return Sequence(ActorInterval(self, 'pet-start'), ActorInterval(self, 'pet-loop'), ActorInterval(self, 'pet-end')) + + def getCallPetIval(self): + return ActorInterval(self, 'callPet') + + def enterGolfPuttLoop(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('loop-putt') + + def exitGolfPuttLoop(self): + self.stop() + + def enterGolfRotateLeft(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('rotateL-putt') + + def exitGolfRotateLeft(self): + self.stop() + + def enterGolfRotateRight(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('rotateR-putt') + + def exitGolfRotateRight(self): + self.stop() + + def enterGolfPuttSwing(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('swing-putt') + + def exitGolfPuttSwing(self): + self.stop() + + def enterGolfGoodPutt(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('good-putt', restart=0) + + def exitGolfGoodPutt(self): + self.stop() + + def enterGolfBadPutt(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('badloop-putt', restart=0) + + def exitGolfBadPutt(self): + self.stop() + + def enterFlattened(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableAll(self) + sound = loader.loadSfx('phase_9/audio/sfx/toon_decompress.ogg') + lerpTime = 0.1 + node = self.getGeomNode().getChild(0) + self.origScale = node.getScale() + self.track = Sequence(LerpScaleInterval(node, lerpTime, VBase3(2, 2, 0.025), blendType='easeInOut')) + self.track.start(ts) + self.setActiveShadow(1) + + def exitFlattened(self): + self.playingAnim = 'neutral' + if self.track != None: + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + node = self.getGeomNode().getChild(0) + node.setScale(self.origScale) + Emote.globalEmote.releaseAll(self) + return + + def enterCogThiefRunning(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.playingAnim = None + self.playingRate = None + self.standWalkRunReverse = (('neutral', 1.0), + ('run', 1.0), + ('run', 1.0), + ('run', -1.0)) + self.setSpeed(self.forwardSpeed, self.rotateSpeed) + self.setActiveShadow(1) + return + + def exitCogThiefRunning(self): + self.standWalkRunReverse = None + self.stop() + self.motion.exit() + return + + def enterScientistJealous(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('scientistJealous') + + def exitScientistJealous(self): + self.stop() + + def enterScientistEmcee(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('scientistEmcee') + + def exitScientistEmcee(self): + self.stop() + + def enterScientistWork(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('scientistWork') + + def exitScientistWork(self): + self.stop() + + def enterScientistLessWork(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('scientistWork', fromFrame=319, toFrame=619) + + def exitScientistLessWork(self): + self.stop() + + def enterScientistPlay(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('scientistGame') + + def exitScientistPlay(self): + self.stop() + + def getDustCloud(self, delay=0.5, color=None): + dustCloud = DustCloud.DustCloud(fBillboard=0, wantSound=1) + + dustCloud.setBillboardAxis(2.0) + dustCloud.setZ(3) + dustCloud.setScale(0.4) + dustCloud.createTrack() + sequence = Sequence(Wait(delay), Func(dustCloud.reparentTo, self), dustCloud.track, Func(dustCloud.destroy), name='dustCloudIval') + + if color is not None and hasattr(self, 'laffMeter'): + self.laffMeter.color = color + sequence.append(Func(self.laffMeter.adjustFace, self.hp, self.maxHp)) + + return sequence + + def createHeadMeter(self): + if self.headMeter: + return + + nodePath = NodePath(self.nametag.getNameIcon()) + + if nodePath.isEmpty(): + return + + self.headMeter = LaffMeter.LaffMeter(self.style, self.getHp(), self.getMaxHp()) + self.headMeter.av = self + self.headMeter.reparentTo(nodePath) + self.headMeter.setScale(1) + self.headMeter.setBin("fixed", 40) + self.headMeter.setDepthWrite(False) + self.headMeter.start() + self.setHeadPositions() + + def removeHeadMeter(self): + if not self.headMeter: + return + + self.headMeter.destroy() + self.headMeter = None + self.setHeadPositions() + + def setGMIcon(self, access): + if self.gmIcon: + return + + icons = loader.loadModel('phase_3/models/props/gm_icons') + self.gmIcon = icons.find('**/access_level_%s' % access) + np = NodePath(self.nametag.getNameIcon()) + + if np.isEmpty() or not self.gmIcon: + return + + self.gmIcon.flattenStrong() + self.gmIcon.reparentTo(np) + self.gmIcon.setScale(1.6) + self.gmIconInterval = LerpHprInterval(self.gmIcon, 3.0, Point3(0, 0, 0), Point3(-360, 0, 0)) + self.gmIconInterval.loop() + self.setHeadPositions() + + def removeGMIcon(self): + if not self.gmIcon: + return + + self.gmIconInterval.finish() + self.gmIcon.detachNode() + del self.gmIconInterval + self.gmIcon = None + self.setHeadPositions() + + def setPartyHat(self): + if self.partyHat: + return + + nodePath = NodePath(self.nametag.getNameIcon()) + + if nodePath.isEmpty(): + return + + model = loader.loadModel('phase_4/models/parties/partyStickerbook') + self.partyHat = model.find('**/Stickerbook_PartyIcon') + self.partyHat.setHpr(0.0, 0.0, -50.0) + self.partyHat.setScale(4) + self.partyHat.setBillboardAxis() + self.partyHat.reparentTo(nodePath) + model.removeNode() + self.setHeadPositions() + + def removePartyHat(self): + if not self.partyHat: + return + + self.partyHat.detachNode() + self.partyHat = None + self.setHeadPositions() + + def setHeadPositions(self): + position = 2.5 + + if self.gmIcon: + self.gmIcon.setZ(position) + position += (2.5 if self.trophyStar else 2.7) + + if self.trophyStar: + self.trophyStar.setZ(position) + position += 2.7 + + if self.headMeter: + self.headMeter.setZ(position) + position += 3.3 + + if self.partyHat: + self.partyHat.setZ(position) + +loadModels() +compileGlobalAnimList() + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def headMeter(create=True): + """ + Create or remove the head meter. + """ + for av in base.cr.doId2do.values(): + if isinstance(av, Toon): + av.createHeadMeter() if create else av.removeHeadMeter() + +@magicWord(category=CATEGORY_PROGRAMMER, types=[int]) +def partyHat(create=True): + """ + Create or remove the party hat. + """ + for av in base.cr.doId2do.values(): + if isinstance(av, Toon): + av.setPartyHat() if create else av.removePartyHat() diff --git a/toontown/toon/ToonAvatarDetailPanel.py b/toontown/toon/ToonAvatarDetailPanel.py new file mode 100755 index 00000000..e60517d1 --- /dev/null +++ b/toontown/toon/ToonAvatarDetailPanel.py @@ -0,0 +1,293 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +import DistributedToon +from toontown.friends import FriendInviter +import ToonTeleportPanel +from toontown.toonbase import TTLocalizer, ToontownGlobals +from toontown.hood import ZoneUtil +from toontown.toonbase.ToontownBattleGlobals import Tracks, Levels, getAvPropDamage +import Toon, NPCFriendPanel +globalAvatarDetail = None + +def showAvatarDetail(avId, avName): + global globalAvatarDetail + if globalAvatarDetail != None: + globalAvatarDetail.cleanup() + globalAvatarDetail = None + globalAvatarDetail = ToonAvatarDetailPanel(avId, avName) + + +def hideAvatarDetail(): + global globalAvatarDetail + if globalAvatarDetail != None: + globalAvatarDetail.cleanup() + globalAvatarDetail = None + return + + +def unloadAvatarDetail(): + global globalAvatarDetail + if globalAvatarDetail != None: + globalAvatarDetail.cleanup() + globalAvatarDetail = None + return + + +class ToonAvatarDetailPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonAvatarDetailPanel') + + def __init__(self, avId, avName, parent = base.a2dTopRight, **kw): + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + sosGui = loader.loadModel('phase_3.5/models/gui/playingCard') + detailPanel = gui.find('**/avatarInfoPanel') + textScale = 0.095 + textWrap = 16.4 + optiondefs = (('pos', (-0.79, 0.0, -0.47), None), + ('scale', 0.5, None), + ('relief', None, None), + ('image', detailPanel, None), + ('image_color', GlobalDialogColor, None), + ('text', '', None), + ('text_wordwrap', textWrap, None), + ('text_scale', textScale, None), + ('text_pos', (-0.125, 0.775), None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.dataText = DirectLabel(self, text='', text_scale=0.09, text_align=TextNode.ALeft, text_wordwrap=15, relief=None, pos=(-0.85, 0.0, 0.645)) + self.avId = avId + self.avName = avName + self.avatar = None + self.createdAvatar = None + self.fsm = ClassicFSM.ClassicFSM('ToonAvatarDetailPanel', [State.State('off', self.enterOff, self.exitOff, ['begin']), + State.State('begin', self.enterBegin, self.exitBegin, ['query', 'data', 'off']), + State.State('query', self.enterQuery, self.exitQuery, ['data', 'invalid', 'off']), + State.State('data', self.enterData, self.exitData, ['off']), + State.State('invalid', self.enterInvalid, self.exitInvalid, ['off'])], 'off', 'off') + ToonTeleportPanel.hideTeleportPanel() + FriendInviter.hideFriendInviter() + self.bCancel = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=1.1, relief=None, text=TTLocalizer.AvatarDetailPanelCancel, text_scale=TTLocalizer.TADPbCancel, text_pos=(0.12, -0.01), pos=TTLocalizer.TADPbCancelPos, scale=2.0, command=self.__handleCancel) + self.bCancel.hide() + self.sosButton = DirectButton(self, relief=None, image=sosGui.find('**/card_back'), scale=0.05, pos=(0.3, 0, -0.76), text=('', TTLocalizer.DetailPanelSOS, TTLocalizer.DetailPanelSOS, ''), text_fg=(1, 1, 0.5, 1), text_shadow=(0, 0, 0, 1), text_scale=2, text_pos=(0, -3.4), text_align=TextNode.ACenter, state=DGG.NORMAL, command=self.__toggleSOSGui) + self.sosButton.hide() + self.sosFrame = DirectFrame(self, relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(2.6, 1, 1.87), image_color=ToontownGlobals.GlobalDialogColor, pos=(0.2, 0, 0.8)) + self.sosFrame.setBin('background', 10) + self.sosFrame.setScale(0.5) + self.sosPage = NPCFriendPanel.NPCFriendPanel(parent=self.sosFrame, callable=False) + self.sosPage.setScale(0.18) + self.sosPage.setPos(0, 0, 0.05) + self.initialiseoptions(ToonAvatarDetailPanel) + self.fsm.enterInitialState() + self.fsm.request('begin') + buttons.removeNode() + gui.removeNode() + sosGui.removeNode() + + def cleanup(self): + if self.fsm: + self.fsm.request('off') + self.fsm = None + base.cr.cancelAvatarDetailsRequest(self.avatar) + if self.createdAvatar: + self.avatar.delete() + self.createdAvatar = None + self.destroy() + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterBegin(self): + myId = base.localAvatar.doId + self['text'] = self.avName + if self.avId == myId: + self.avatar = base.localAvatar + self.createdAvatar = 0 + self.fsm.request('data') + else: + self.fsm.request('query') + + def exitBegin(self): + pass + + def enterQuery(self): + self.dataText['text'] = TTLocalizer.AvatarDetailPanelLookup % self.avName + self.sosButton.hide() + self.bCancel.show() + self.avatar = base.cr.doId2do.get(self.avId) + if self.avatar != None and not self.avatar.ghostMode: + self.createdAvatar = 0 + else: + self.avatar = DistributedToon.DistributedToon(base.cr) + self.createdAvatar = 1 + self.avatar.doId = self.avId + self.avatar.forceAllowDelayDelete() + base.cr.getAvatarDetails(self.avatar, self.__handleAvatarDetails, 'DistributedToon') + return + + def exitQuery(self): + self.bCancel.hide() + + def enterData(self): + self.bCancel['text'] = TTLocalizer.AvatarDetailPanelClose + self.bCancel.show() + self.__showData() + + def exitData(self): + self.bCancel.hide() + + def enterInvalid(self): + self.dataText['text'] = TTLocalizer.AvatarDetailPanelFailedLookup % self.avName + + def exitInvalid(self): + self.bCancel.hide() + + def __handleCancel(self): + unloadAvatarDetail() + + def __handleAvatarDetails(self, gotData, avatar, dclass): + if not self.fsm or avatar != self.avatar: + self.notify.warning('Ignoring unexpected request for avatar %s' % avatar.doId) + return + if gotData: + self.fsm.request('data') + else: + self.fsm.request('invalid') + + def __showData(self): + av = self.avatar + online = 1 + if base.cr.isFriend(self.avId): + online = base.cr.isFriendOnline(self.avId) + identifier = int(str(self.avId)[1:]) + + if online: + shardName = base.cr.getShardName(av.defaultShard) + hoodName = base.cr.hoodMgr.getFullnameFromId(av.lastHood) + text = TTLocalizer.AvatarDetailPanelOnline % {'district': shardName, 'location': hoodName, 'identifier': identifier} + else: + text = TTLocalizer.AvatarDetailPanelOffline % {'identifier': identifier} + self.dataText['text'] = text + self.sosButton.show() + self.__addToonModel() + self.__updateTrackInfo() + self.__updateTrophyInfo() + self.__updateLaffInfo() + self.__updateSOSPage() + + def __addToonModel(self): + toon = Toon.Toon() + toon.setDNA(self.avatar.style) + toon.reparentTo(self) + toon.setPos(0.45, 0, 0.3) + toon.setH(180) + toon.setScale(0.11) + toon.loop('neutral') + toon.setDepthWrite(True) + toon.setDepthTest(True) + + def __updateLaffInfo(self): + avatar = self.avatar + messenger.send('updateLaffMeter', [avatar, avatar.hp, avatar.maxHp]) + + def __updateTrackInfo(self): + xOffset = -0.501814 + xSpacing = 0.1835 + yOffset = 0.1 + ySpacing = -0.115 + inventory = self.avatar.inventory + self.inventoryFrame = DirectFrame(parent=self, relief=None) + inventoryModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + rolloverFrame = DirectFrame(parent=self.inventoryFrame, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=(0, 0.5, 1, 1), geom_scale=(0.5, 0.3, 0.2), text_scale=0.05, text_pos=(0, 0.0125), text='', text_fg=(1, 1, 1, 1)) + rolloverFrame.setBin('gui-popup', 0) + rolloverFrame.hide() + buttonModel = inventoryModels.find('**/InventoryButtonUp') + for track in xrange(0, len(Tracks)): + DirectLabel(parent=self.inventoryFrame, relief=None, text=TextEncoder.upper(TTLocalizer.BattleGlobalTracks[track]), text_scale=TTLocalizer.TADPtrackLabel, text_align=TextNode.ALeft, pos=(-0.9, 0, TTLocalizer.TADtrackLabelPosZ + track * ySpacing)) + if self.avatar.hasTrackAccess(track): + curExp, nextExp = inventory.getCurAndNextExpValues(track) + for item in xrange(0, len(Levels[track])): + level = Levels[track][item] + if curExp >= level: + numItems = inventory.numItem(track, item) + organic = self.avatar.checkGagBonus(track, item) + if numItems == 0: + image_color = Vec4(0.5, 0.5, 0.5, 1) + geom_color = Vec4(0.2, 0.2, 0.2, 0.5) + elif organic: + image_color = Vec4(0, 0.8, 0.4, 1) + geom_color = None + else: + image_color = Vec4(0, 0.6, 1, 1) + geom_color = None + pos = (xOffset + item * xSpacing, 0, yOffset + track * ySpacing) + label = DirectLabel(parent=self.inventoryFrame, image=buttonModel, image_scale=(0.92, 1, 1), image_color=image_color, geom=inventory.invModels[track][item], geom_color=geom_color, geom_scale=0.6, relief=None, pos=pos, state=DGG.NORMAL) + label.bind(DGG.ENTER, self.showInfo, extraArgs=[rolloverFrame, track, int(getAvPropDamage(track, item, curExp, organic)), numItems, (pos[0] + 0.37, pos[1], pos[2])]) + label.bind(DGG.EXIT, self.hideInfo, extraArgs=[rolloverFrame]) + else: + break + + def showInfo(self, frame, track, damage, numItems, pos, extra): + frame.setPos(*pos) + frame.show() + frame['text'] = TTLocalizer.GagPopup % (self.avatar.inventory.getToonupDmgStr(track, 0), damage, numItems) + + def hideInfo(self, frame, extra): + frame.hide() + + def __updateTrophyInfo(self): + if self.createdAvatar: + return + if self.avatar.trophyScore >= TrophyStarLevels[2]: + color = TrophyStarColors[2] + elif self.avatar.trophyScore >= TrophyStarLevels[1]: + color = TrophyStarColors[1] + elif self.avatar.trophyScore >= TrophyStarLevels[0]: + color = TrophyStarColors[0] + else: + color = None + if color: + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + star = gui.find('**/avatarStar') + self.star = DirectLabel(parent=self, image=star, image_color=color, pos=(0.610165, 0, -0.760678), scale=0.9, relief=None) + gui.removeNode() + + def __updateSOSPage(self): + self.sosPage.setFriends(self.avatar.NPCFriendsDict) + self.sosPage.update() + + def __toggleSOSGui(self): + self.sosButton['state'] = DGG.DISABLED + + if self.sosFrame.getScale() == 0.5: + pos = (-0.4, 0, -1.88) + scale = 1.0 + else: + pos = (0, 0, 0.05) + scale = 0.5 + self.sosFrame.setBin('background', 10) + + Sequence( + Parallel( + self.sosFrame.posInterval(1.0, pos, blendType='easeOut'), + self.sosFrame.scaleInterval(1.0, scale, blendType='easeOut') + ), + Func(self.__enableSOSButton) + ).start() + + def __enableSOSButton(self): + try: + self.sosButton['state'] = DGG.NORMAL + + if self.sosFrame.getScale() == 1.0: + self.sosFrame.clearBin() + except: + pass \ No newline at end of file diff --git a/toontown/toon/ToonAvatarPanel.py b/toontown/toon/ToonAvatarPanel.py new file mode 100755 index 00000000..19416132 --- /dev/null +++ b/toontown/toon/ToonAvatarPanel.py @@ -0,0 +1,563 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from direct.showbase import DirectObject +import ToonHead +import LaffMeter +from otp.avatar import Avatar +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.friends import ToontownFriendSecret +import ToonAvatarDetailPanel +import AvatarPanelBase +from toontown.toontowngui import TTDialog +from otp.otpbase import OTPGlobals + +class ToonAvatarPanel(AvatarPanelBase.AvatarPanelBase): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonAvatarPanel') + + def __init__(self, avatar): + from toontown.friends import FriendsListPanel + + if base.cr.doId2do.get(avatar.getDoId()): + avatar = base.cr.doId2do.get(avatar.getDoId()) + AvatarPanelBase.AvatarPanelBase.__init__(self, avatar, FriendsListPanel=FriendsListPanel) + + self.notify.debug('Opening toon panel, avId=%d' % self.avId) + + self.laffMeter = None + wantsLaffMeter = hasattr(avatar, 'hp') + + if not hasattr(avatar, 'style'): + self.notify.warning("Avatar has no 'style'. Abort initialization.") + AvatarPanelBase.AvatarPanelBase.cleanup(self) + return + + base.localAvatar.obscureFriendsListButton(1) + + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.frame = DirectFrame( + image=gui.find('**/avatar_panel'), + relief=None, + pos=(-0.22, 0, -0.47), + parent=base.a2dTopRight) + + self.disabledImageColor = Vec4(1, 1, 1, 0.4) + self.text0Color = Vec4(1, 1, 1, 1) + self.text1Color = Vec4(0.5, 1, 0.5, 1) + self.text2Color = Vec4(1, 1, 0.5, 1) + self.text3Color = Vec4(0.6, 0.6, 0.6, 1) + + self.head = self.frame.attachNewNode('head') + self.head.setPos(0.02, 0, 0.31) + self.headModel = ToonHead.ToonHead() + self.headModel.setupHead(avatar.style, forGui=1) + self.headModel.fitAndCenterHead(0.175, forGui=1) + self.headModel.reparentTo(self.head) + + self.headModel.startBlink() + self.headModel.startLookAround() + + self.healthText = DirectLabel( + parent=self.frame, + text='', + pos=(0.06, 0, 0.2), + text_pos=(0, 0), + text_scale=0.05) + + self.healthText.hide() + + self.nameLabel = DirectLabel( + parent=self.frame, + pos=(0.0125, 0, 0.4), + relief=None, + text=self.avName, + text_font=avatar.getFont(), + text_fg=Vec4(0, 0, 0, 1), + text_pos=(0, 0), + text_scale=0.042, + text_wordwrap=7.5, + text_shadow=(1, 1, 1, 1)) + + self.closeButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/CloseBtn_UP'), + gui.find('**/CloseBtn_DN'), + gui.find('**/CloseBtn_Rllvr'), + gui.find('**/CloseBtn_UP')), + relief=None, + pos=(0.157644, 0, -0.379167), + command=self.__handleClose) + + self.friendButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/Frnds_Btn_UP'), + gui.find('**/Frnds_Btn_DN'), + gui.find('**/Frnds_Btn_RLVR'), + gui.find('**/Frnds_Btn_UP')), + image3_color=self.disabledImageColor, + image_scale=0.9, + relief=None, + text=TTLocalizer.AvatarPanelFriends, + text_scale=0.06, + pos=(-0.103, 0, 0.133), + text0_fg=self.text0Color, + text1_fg=self.text1Color, + text2_fg=self.text2Color, + text3_fg=self.text3Color, + text_pos=(0.06, -0.02), + text_align=TextNode.ALeft, + command=self.__handleFriend) + + self.goToButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/Go2_Btn_UP'), + gui.find('**/Go2_Btn_DN'), + gui.find('**/Go2_Btn_RLVR'), + gui.find('**/Go2_Btn_UP')), + image3_color=self.disabledImageColor, + image_scale=0.9, + relief=None, + pos=(-0.103, 0, 0.045), + text=TTLocalizer.AvatarPanelGoTo, + text0_fg=self.text0Color, + text1_fg=self.text1Color, + text2_fg=self.text2Color, + text3_fg=self.text3Color, + text_scale=0.06, + text_pos=(0.06, -0.015), + text_align=TextNode.ALeft, + command=self.__handleGoto) + + self.whisperButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/ChtBx_ChtBtn_UP'), + gui.find('**/ChtBx_ChtBtn_DN'), + gui.find('**/ChtBx_ChtBtn_RLVR'), + gui.find('**/ChtBx_ChtBtn_UP')), + image3_color=self.disabledImageColor, + image_scale=0.9, + relief=None, + pos=(-0.103, 0, -0.0375), + text=TTLocalizer.AvatarPanelWhisper, + text0_fg=self.text0Color, + text1_fg=self.text1Color, + text2_fg=self.text2Color, + text3_fg=self.text3Color, + text_scale=TTLocalizer.TAPwhisperButton, + text_pos=(0.06, -0.0125), + text_align=TextNode.ALeft, + command=self.__handleWhisper) + + self.trueFriendsButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/Amuse_Btn_UP'), + gui.find('**/Amuse_Btn_DN'), + gui.find('**/Amuse_Btn_RLVR'), + gui.find('**/Amuse_Btn_UP')), + image3_color=self.disabledImageColor, + image_scale=0.9, + relief=None, + pos=(-0.103, 0, -0.13), + text=TTLocalizer.AvatarPanelTrueFriends, + text0_fg=self.text0Color, + text1_fg=self.text1Color, + text2_fg=self.text2Color, + text3_fg=self.text3Color, + text_scale=TTLocalizer.TAPtruefriendsButton, + text_pos=(0.055, -0.01), + text_align=TextNode.ALeft, + command=self.__handleTrueFriends) + + ignoreStr, ignoreCmd, ignoreScale = self.getIgnoreButtonInfo() + + self.ignoreButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/Ignore_Btn_UP'), + gui.find('**/Ignore_Btn_DN'), + gui.find('**/Ignore_Btn_RLVR'), + gui.find('**/Ignore_Btn_UP')), + image3_color=self.disabledImageColor, + image_scale=0.9, + relief=None, + pos=(-0.103697, 0, -0.21), + text=ignoreStr, + text0_fg=self.text0Color, + text1_fg=self.text1Color, + text2_fg=self.text2Color, + text3_fg=self.text3Color, + text_scale=ignoreScale, + text_pos=(0.06, -0.015), + text_align=TextNode.ALeft, + command=ignoreCmd) + self.reportButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/report_BtnUP'), + gui.find('**/report_BtnDN'), + gui.find('**/report_BtnRLVR'), + gui.find('**/report_BtnUP')), + image3_color=self.disabledImageColor, + image_scale=0.65, + relief=None, + pos=(-0.103, 0, -0.29738), + text=TTLocalizer.AvatarPanelReport, + text0_fg=self.text0Color, + text1_fg=self.text1Color, + text2_fg=self.text2Color, + text3_fg=self.text3Color, + text_scale=0.06, + text_pos=(0.06, -0.015), + text_align=TextNode.ALeft, + command=self.handleReport) + + if avatar.isAdmin(): + self.ignoreButton['state'] = DGG.DISABLED + self.reportButton['state'] = DGG.DISABLED + elif base.localAvatar.isIgnored(self.avId): + self.friendButton['state'] = DGG.DISABLED + self.goToButton['state'] = DGG.DISABLED + self.whisperButton['state'] = DGG.DISABLED + self.trueFriendsButton['state'] = DGG.DISABLED + + if not base.localAvatar.isTeleportAllowed(): + self.goToButton['state'] = DGG.DISABLED + + self.detailButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/ChtBx_BackBtn_UP'), + gui.find('**/ChtBx_BackBtn_DN'), + gui.find('**/ChtBx_BackBtn_Rllvr'), + gui.find('**/ChtBx_BackBtn_UP')), + relief=None, + text=('', TTLocalizer.AvatarPanelDetail, + TTLocalizer.AvatarPanelDetail, ''), + text_fg=self.text2Color, + text_shadow=(0, 0, 0, 1), + text_scale=0.055, + text_pos=(-0.075, -0.01), + text_align=TextNode.ARight, + pos=(-0.133773, 0, -0.395), + command=self.__handleDetails) + + + self.__makeBoardingGui() + self.__makePetGui(avatar) + + self.__checkGroupStatus() + + gui.removeNode() + + + if wantsLaffMeter: + self.__makeLaffMeter(avatar) + self.__updateHp(avatar.hp, avatar.maxHp) + self.healthText.show() + self.laffMeter.show() + + menuX = -0.05 + menuScale = 0.064 + + if self.avGenerateName: + self.accept(self.avGenerateName, self.__handleGenerateAvatar) + if self.avHpChangeName: + self.accept(self.avHpChangeName, self.__updateHp) + self.accept('updateLaffMeter', self.__updateLaffMeter) + + self.accept('updateGroupStatus', self.__checkGroupStatus) + + self.frame.show() + messenger.send('avPanelDone') + + def disableAll(self): + self.detailButton['state'] = DGG.DISABLED + self.reportButton['state'] = DGG.DISABLED + self.ignoreButton['state'] = DGG.DISABLED + self.goToButton['state'] = DGG.DISABLED + self.trueFriendsButton['state'] = DGG.DISABLED + self.whisperButton['state'] = DGG.DISABLED + self.petButton['state'] = DGG.DISABLED + self.friendButton['state'] = DGG.DISABLED + self.closeButton['state'] = DGG.DISABLED + self.groupButton['state'] = DGG.DISABLED + self.boardingInfoButton['state'] = DGG.DISABLED + + def cleanup(self): + if not hasattr(self, 'frame') or self.frame == None: + return + self.notify.debug('Clean up toon panel, avId=%d' % self.avId) + if self.frame: + self.frame.destroy() + del self.frame + self.frame = None + ToonAvatarDetailPanel.unloadAvatarDetail() + if self.groupButton: + self.groupButton.destroy() + del self.groupButton + self.groupButton = None + if self.boardingInfoButton: + self.boardingInfoButton.destroy() + del self.boardingInfoButton + self.boardingInfoButton = None + if self.boardingInfoText: + self.boardingInfoText.destroy() + del self.boardingInfoText + self.boardingInfoText = None + if self.groupFrame: + self.groupFrame.destroy() + del self.groupFrame + self.groupFrame = None + self.head.removeNode() + del self.head + self.headModel.stopBlink() + self.headModel.stopLookAroundNow() + self.headModel.delete() + del self.headModel + base.localAvatar.obscureFriendsListButton(-1) + self.laffMeter = None + self.ignore('updateLaffMeter') + self.ignoreAll() + if hasattr(self.avatar, 'bFake') and self.avatar.bFake: + self.avatar.delete() + base.setCellsAvailable([base.rightCells[0]], 1) + AvatarPanelBase.AvatarPanelBase.cleanup(self) + return + + def __handleGoto(self): + if base.localAvatar.isTeleportAllowed(): + base.localAvatar.chatMgr.noWhisper() + messenger.send('gotoAvatar', [self.avId, self.avName, self.avDisableName]) + + def __handleToPet(self): + toonAvatar = self.avatar + if base.cr.doId2do.get(toonAvatar.getDoId()): + toonAvatar = base.cr.doId2do.get(toonAvatar.getDoId()) + petAvatar = base.cr.doId2do.get(toonAvatar.getPetId()) + self.disableAll() + from toontown.pets import PetDetail + PetDetail.PetDetail(toonAvatar.getPetId(), self.__petDetailsLoaded) + + def __petDetailsLoaded(self, avatar): + self.cleanup() + if avatar: + self.notify.debug("Looking at someone's pet. pet doId = %s" % avatar.doId) + messenger.send('clickedNametag', [avatar]) + + def __handleWhisper(self): + base.localAvatar.chatMgr.whisperTo(self.avName, self.avId) + + def __handleTrueFriends(self): + base.localAvatar.chatMgr.noWhisper() + ToontownFriendSecret.showFriendSecret() + + def __handleFriend(self): + base.localAvatar.chatMgr.noWhisper() + messenger.send('friendAvatar', [self.avId, self.avName, self.avDisableName]) + + def __handleDetails(self): + base.localAvatar.chatMgr.noWhisper() + messenger.send('avatarDetails', [self.avId, self.avName]) + + def __handleDisableAvatar(self): + if not base.cr.isFriend(self.avId): + self.cleanup() + AvatarPanelBase.currentAvatarPanel = None + else: + self.healthText.hide() + if self.laffMeter != None: + self.laffMeter.stop() + self.laffMeter.destroy() + self.laffMeter = None + return + + def __handleGenerateAvatar(self, avatar): + newAvatar = base.cr.doId2do.get(self.avatar.doId) + if newAvatar: + self.avatar = newAvatar + self.__updateLaffMeter(avatar, avatar.hp, avatar.maxHp) + self.__checkGroupStatus() + + def __updateLaffMeter(self, avatar, hp, maxHp): + if self.laffMeter == None: + self.__makeLaffMeter(avatar) + self.__updateHp(avatar.hp, avatar.maxHp) + self.laffMeter.show() + self.healthText.show() + return + + def __makeLaffMeter(self, avatar): + self.laffMeter = LaffMeter.LaffMeter(avatar.style, avatar.hp, avatar.maxHp) + self.laffMeter.reparentTo(self.frame) + self.laffMeter.setPos(-0.1, 0, 0.24) + self.laffMeter.setScale(0.03) + + def __updateHp(self, hp, maxHp, quietly = 0): + if self.laffMeter != None and hp != None and maxHp != None: + self.laffMeter.adjustFace(hp, maxHp) + self.healthText['text'] = '%d / %d' % (hp, maxHp) + return + + def __handleClose(self): + self.cleanup() + AvatarPanelBase.currentAvatarPanel = None + if self.friendsListShown: + self.FriendsListPanel.showFriendsList() + return + + def getAvId(self): + if hasattr(self, 'avatar'): + if self.avatar: + return self.avatar.doId + return None + + def isHidden(self): + if not hasattr(self, 'frame') or not self.frame: + return 1 + return self.frame.isHidden() + + def getType(self): + return 'toon' + + def handleInvite(self): + if localAvatar.boardingParty.isInviteePanelUp(): + localAvatar.boardingParty.showMe(TTLocalizer.BoardingPendingInvite, pos=(0, 0, 0)) + else: + self.groupButton['state'] = DGG.DISABLED + localAvatar.boardingParty.requestInvite(self.avId) + + def handleKick(self): + if not base.cr.playGame.getPlace().getState() == 'elevator': + self.confirmKickOutDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.BoardingKickOutConfirm % self.avName, command=self.__confirmKickOutCallback) + self.confirmKickOutDialog.show() + + def __confirmKickOutCallback(self, value): + if self.confirmKickOutDialog: + self.confirmKickOutDialog.destroy() + self.confirmKickOutDialog = None + if value > 0: + if self.groupButton: + self.groupButton['state'] = DGG.DISABLED + localAvatar.boardingParty.requestKick(self.avId) + return + + def __checkGroupStatus(self): + self.groupFrame.hide() + if hasattr(self, 'avatar'): + if self.avatar and hasattr(self.avatar, 'getZoneId') and localAvatar.getZoneId() == self.avatar.getZoneId(): + if localAvatar.boardingParty: + if self.avId in localAvatar.boardingParty.getGroupMemberList(localAvatar.doId): + if localAvatar.boardingParty.getGroupLeader(localAvatar.doId) == localAvatar.doId: + self.groupButton['text'] = ('', TTLocalizer.AvatarPanelGroupMemberKick, TTLocalizer.AvatarPanelGroupMemberKick) + self.groupButton['image'] = self.kickOutImageList + self.groupButton['command'] = self.handleKick + self.groupButton['state'] = DGG.NORMAL + else: + self.groupButton['text'] = ('', TTLocalizer.AvatarPanelGroupMember, TTLocalizer.AvatarPanelGroupMember) + self.groupButton['command'] = None + self.groupButton['image'] = self.inviteImageDisabled + self.groupButton['image_color'] = Vec4(1, 1, 1, 0.4) + self.groupButton['state'] = DGG.NORMAL + else: + g1 = localAvatar.boardingParty.countInGroup(self.avId) + g2 = localAvatar.boardingParty.countInGroup(localAvatar.doId) + if (g1 + g2) > localAvatar.boardingParty.maxSize: + self.groupButton['text'] = ('', TTLocalizer.AvatarPanelGroupMember, TTLocalizer.AvatarPanelGroupMember) + self.groupButton['command'] = None + self.groupButton['image'] = self.inviteImageDisabled + self.groupButton['image_color'] = Vec4(1, 1, 1, 0.4) + else: + if g1 > 0 and g2 > 0: + self.groupButton['text'] = ('', TTLocalizer.AvatarPanelGroupInvite, "%s %d"%(TTLocalizer.AvatarPanelGroupMerge, (g1+g2))) + self.groupFrame['text']=TTLocalizer.BoardingPartyTitleMerge; + else: + self.groupButton['text'] = ('', TTLocalizer.AvatarPanelGroupInvite, TTLocalizer.AvatarPanelGroupInvite) + self.groupFrame['text']=TTLocalizer.BoardingPartyTitle; + self.groupButton['command'] = self.handleInvite + self.groupButton['image'] = self.inviteImageList + self.groupButton['state'] = DGG.NORMAL + if base.config.GetBool('want-boarding-groups', 1): + base.setCellsAvailable([base.rightCells[0]], 0) + self.groupFrame.show() + return + + def handleReadInfo(self, task = None): + self.boardingInfoButton['state'] = DGG.DISABLED + if self.boardingInfoText: + self.boardingInfoText.destroy() + self.boardingInfoText = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.BoardingPartyInform % localAvatar.boardingParty.maxSize, command=self.handleCloseInfo) + + def handleCloseInfo(self, *extraArgs): + self.boardingInfoButton['state'] = DGG.NORMAL + if self.boardingInfoText: + self.boardingInfoText.destroy() + del self.boardingInfoText + self.boardingInfoText = None + return + + def __makePetGui(self, avatar): + if self.avatar.isAdmin(): + helpGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_brd_help') + image = (helpGui.find('**/tt_t_gui_brd_helpUp'), helpGui.find('**/tt_t_gui_brd_helpDown'), helpGui.find('**/tt_t_gui_brd_helpHover'), helpGui.find('**/tt_t_gui_brd_helpDown')) + text = ('', TTLocalizer.AvatarPanelCast, TTLocalizer.AvatarPanelCast, '') + self.petButton = DirectButton(parent=self.frame, image=image, relief=None, pos=(0.02, -0.2, -0.385), text=text, text_fg=self.text2Color, scale=0.8, + text_shadow=(0, 0, 0, 1), text_scale=0.07, text_pos=(0, -0.125), text_align=TextNode.ACenter, command=self.__handleCastDialog) + helpGui.removeNode() + return + + petGui = loader.loadModel('phase_3.5/models/gui/PetControlPannel') + self.petButton = DirectButton(parent=self.frame, image=(petGui.find('**/PetControlToonButtonUp1'), petGui.find('**/PetControlToonButtonDown1'), petGui.find('**/PetControlToonButtonRollover1')), geom=petGui.find('**/PetBattleIcon'), geom3_color=self.disabledImageColor, relief=None, pos=(0.22, -0.2, -0.475), text=('', + TTLocalizer.AvatarPanelPet, + TTLocalizer.AvatarPanelPet, + ''), text_fg=self.text2Color, text_shadow=(0, 0, 0, 1), text_scale=0.325, text_pos=(-1.3, 0.05), text_align=TextNode.ACenter, command=self.__handleToPet) + self.petButton.setScale(0.15) + if not (base.wantPets and avatar.hasPet()): + self.petButton['state'] = DGG.DISABLED + self.petButton.hide() + petGui.removeNode() + + def __handleCastDialog(self): + self.cleanupDialog() + base.cr.playGame.getPlace().setState('stopped') + self.dialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.AvatarPanelCastInfo % self.avatar.getName(), text_wordwrap=20, command=self.__cleanupDialogAndWalk) + self.dialog.show() + + def __cleanupDialogAndWalk(self, extra=None): + if self.dialog: + self.dialog.destroy() + self.dialog = None + base.cr.playGame.getPlace().fsm.request('walk') + + def __makeBoardingGui(self): + self.confirmKickOutDialog = None + groupAvatarBgGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_brd_avatarPanelBg') + boardingGroupBGImage = groupAvatarBgGui.find('**/tt_t_gui_brd_avatar_panel_party') + self.groupFrame = DirectFrame(parent=self.frame, relief=None, image=boardingGroupBGImage, image_scale=(0.5, 1, 0.5), textMayChange=1, text=TTLocalizer.BoardingPartyTitle, text_wordwrap=16, text_scale=TTLocalizer.TAPgroupFrame, text_pos=(0.01, 0.08), pos=(0, 0, -0.61)) + groupInviteGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_brd_inviteButton') + self.inviteImageList = (groupInviteGui.find('**/tt_t_gui_brd_inviteUp'), + groupInviteGui.find('**/tt_t_gui_brd_inviteDown'), + groupInviteGui.find('**/tt_t_gui_brd_inviteHover'), + groupInviteGui.find('**/tt_t_gui_brd_inviteUp')) + self.kickOutImageList = (groupInviteGui.find('**/tt_t_gui_brd_kickoutUp'), + groupInviteGui.find('**/tt_t_gui_brd_kickoutDown'), + groupInviteGui.find('**/tt_t_gui_brd_kickoutHover'), + groupInviteGui.find('**/tt_t_gui_brd_kickoutUp')) + self.inviteImageDisabled = groupInviteGui.find('**/tt_t_gui_brd_inviteDisabled') + self.groupButton = DirectButton(parent=self.groupFrame, image=self.inviteImageList, image3_color=self.disabledImageColor, image_scale=0.85, relief=None, text=('', TTLocalizer.AvatarPanelGroupInvite, TTLocalizer.AvatarPanelGroupInvite), text0_fg=self.text0Color, text1_fg=self.text1Color, text2_fg=self.text2Color, text3_fg=self.text3Color, text_scale=TTLocalizer.TAPgroupButton, text_pos=(-0.0, -0.1), text_align=TextNode.ACenter, command=self.handleInvite, pos=(0.01013, 0, -0.05464)) + helpGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_brd_help') + helpImageList = (helpGui.find('**/tt_t_gui_brd_helpUp'), + helpGui.find('**/tt_t_gui_brd_helpDown'), + helpGui.find('**/tt_t_gui_brd_helpHover'), + helpGui.find('**/tt_t_gui_brd_helpDown')) + self.boardingInfoButton = DirectButton(parent=self.groupFrame, relief=None, text_pos=(-0.05, 0.05), text_scale=0.06, text_align=TextNode.ALeft, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), image=helpImageList, image_scale=(0.5, 1, 0.5), image3_color=self.disabledImageColor, scale=1.05, command=self.handleReadInfo, pos=(0.1829, 0, 0.02405)) + self.boardingInfoText = None + groupInviteGui.removeNode() + groupAvatarBgGui.removeNode() + helpGui.removeNode() \ No newline at end of file diff --git a/toontown/toon/ToonDNA.py b/toontown/toon/ToonDNA.py new file mode 100755 index 00000000..38798263 --- /dev/null +++ b/toontown/toon/ToonDNA.py @@ -0,0 +1,2826 @@ +import random, colorsys +from panda3d.core import * +from direct.directnotify.DirectNotifyGlobal import * +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.PyDatagramIterator import PyDatagramIterator +from toontown.toonbase import ToontownGlobals +notify = directNotify.newCategory('ToonDNA') +mergeMATTailor = config.GetBool('want-mat-all-tailors', 0) +toonSpeciesTypes = ['d', + 'c', + 'h', + 'm', + 'r', + 'f', + 'p', + 'b', + 's'] +toonHeadTypes = ['dls', + 'dss', + 'dsl', + 'dll', + 'cls', + 'css', + 'csl', + 'cll', + 'hls', + 'hss', + 'hsl', + 'hll', + 'mls', + 'mss', + 'rls', + 'rss', + 'rsl', + 'rll', + 'fls', + 'fss', + 'fsl', + 'fll', + 'pls', + 'pss', + 'psl', + 'pll', + 'bls', + 'bss', + 'bsl', + 'bll', + 'sls', + 'sss', + 'ssl', + 'sll'] + +def getHeadList(species): + headList = [] + for head in toonHeadTypes: + if head[0] == species: + headList.append(head) + + return headList + + +def getHeadStartIndex(species): + for head in toonHeadTypes: + if head[0] == species: + return toonHeadTypes.index(head) + + +def getSpecies(head): + for species in toonSpeciesTypes: + if species == head[0]: + return species + + +def getSpeciesName(head): + species = getSpecies(head) + if species == 'd': + speciesName = 'dog' + elif species == 'c': + speciesName = 'cat' + elif species == 'h': + speciesName = 'horse' + elif species == 'm': + speciesName = 'mouse' + elif species == 'r': + speciesName = 'rabbit' + elif species == 'f': + speciesName = 'duck' + elif species == 'p': + speciesName = 'monkey' + elif species == 'b': + speciesName = 'bear' + elif species == 's': + speciesName = 'pig' + return speciesName + + +toonHeadAnimalIndices = [0, + 4, + 8, + 12, + 14, + 18, + 22, + 26, + 30] +allToonHeadAnimalIndices = [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33] +toonTorsoTypes = ['ss', + 'ms', + 'ls', + 'sd', + 'md', + 'ld', + 's', + 'm', + 'l'] +toonLegTypes = ['s', 'm', 'l'] +Shirts = ['phase_3/maps/desat_shirt_1.jpg', + 'phase_3/maps/desat_shirt_2.jpg', + 'phase_3/maps/desat_shirt_3.jpg', + 'phase_3/maps/desat_shirt_4.jpg', + 'phase_3/maps/desat_shirt_5.jpg', + 'phase_3/maps/desat_shirt_6.jpg', + 'phase_3/maps/desat_shirt_7.jpg', + 'phase_3/maps/desat_shirt_8.jpg', + 'phase_3/maps/desat_shirt_9.jpg', + 'phase_3/maps/desat_shirt_10.jpg', + 'phase_3/maps/desat_shirt_11.jpg', + 'phase_3/maps/desat_shirt_12.jpg', + 'phase_3/maps/desat_shirt_13.jpg', + 'phase_3/maps/desat_shirt_14.jpg', + 'phase_3/maps/desat_shirt_15.jpg', + 'phase_3/maps/desat_shirt_16.jpg', + 'phase_3/maps/desat_shirt_17.jpg', + 'phase_3/maps/desat_shirt_18.jpg', + 'phase_3/maps/desat_shirt_19.jpg', + 'phase_3/maps/desat_shirt_20.jpg', + 'phase_3/maps/desat_shirt_21.jpg', + 'phase_3/maps/desat_shirt_22.jpg', + 'phase_3/maps/desat_shirt_23.jpg', + 'phase_4/maps/female_shirt1b.jpg', + 'phase_4/maps/female_shirt2.jpg', + 'phase_4/maps/female_shirt3.jpg', + 'phase_4/maps/male_shirt1.jpg', + 'phase_4/maps/male_shirt2_palm.jpg', + 'phase_4/maps/male_shirt3c.jpg', + 'phase_4/maps/shirt_ghost.jpg', + 'phase_4/maps/shirt_pumkin.jpg', + 'phase_4/maps/holiday_shirt1.jpg', + 'phase_4/maps/holiday_shirt2b.jpg', + 'phase_4/maps/holidayShirt3b.jpg', + 'phase_4/maps/holidayShirt4.jpg', + 'phase_4/maps/female_shirt1b.jpg', + 'phase_4/maps/female_shirt5New.jpg', + 'phase_4/maps/shirtMale4B.jpg', + 'phase_4/maps/shirt6New.jpg', + 'phase_4/maps/shirtMaleNew7.jpg', + 'phase_4/maps/femaleShirtNew6.jpg', + 'phase_4/maps/Vday1Shirt5.jpg', + 'phase_4/maps/Vday1Shirt6SHD.jpg', + 'phase_4/maps/Vday1Shirt4.jpg', + 'phase_4/maps/Vday_shirt2c.jpg', + 'phase_4/maps/shirtTieDyeNew.jpg', + 'phase_4/maps/male_shirt1.jpg', + 'phase_4/maps/StPats_shirt1.jpg', + 'phase_4/maps/StPats_shirt2.jpg', + 'phase_4/maps/ContestfishingVestShirt2.jpg', + 'phase_4/maps/ContestFishtankShirt1.jpg', + 'phase_4/maps/ContestPawShirt1.jpg', + 'phase_4/maps/CowboyShirt1.jpg', + 'phase_4/maps/CowboyShirt2.jpg', + 'phase_4/maps/CowboyShirt3.jpg', + 'phase_4/maps/CowboyShirt4.jpg', + 'phase_4/maps/CowboyShirt5.jpg', + 'phase_4/maps/CowboyShirt6.jpg', + 'phase_4/maps/4thJulyShirt1.jpg', + 'phase_4/maps/4thJulyShirt2.jpg', + 'phase_4/maps/shirt_Cat7_01.jpg', + 'phase_4/maps/shirt_Cat7_02.jpg', + 'phase_4/maps/contest_backpack3.jpg', + 'phase_4/maps/contest_leder.jpg', + 'phase_4/maps/contest_mellon2.jpg', + 'phase_4/maps/contest_race2.jpg', + 'phase_4/maps/PJBlueBanana2.jpg', + 'phase_4/maps/PJRedHorn2.jpg', + 'phase_4/maps/PJGlasses2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_valentine1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_valentine2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_desat4.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_fishing1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_fishing2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_gardening1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_gardening2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_party1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_party2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_racing1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_racing2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_summer1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_summer2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_golf1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_golf2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_marathon1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_saveBuilding1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_saveBuilding2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_toonTask1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_toonTask2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_trolley1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_trolley2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_winter1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween3.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween4.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_valentine3.jpg', + 'phase_4/maps/tt_t_chr_shirt_scientistC.jpg', + 'phase_4/maps/tt_t_chr_shirt_scientistA.jpg', + 'phase_4/maps/tt_t_chr_shirt_scientistB.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_mailbox.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_trashcan.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_loonyLabs.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_hydrant.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_whistle.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_cogbuster.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_mostCogsDefeated01.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_victoryParty01.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_victoryParty02.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_sellbotIcon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_sellbotVPIcon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_sellbotCrusher.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_jellyBeans.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_doodle.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween5.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloweenTurtle.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_greentoon1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_getConnectedMoverShaker.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_racingGrandPrix.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_bee.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_pirate.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_supertoon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_vampire.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_dinosaur.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_fishing04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_golf03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_mostCogsDefeated02.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_racing03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_saveBuilding3.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_trolley03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_fishing05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_golf04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween06.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_winter03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween07.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_winter02.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_fishing06.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_fishing07.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_golf05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_racing04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_racing05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_mostCogsDefeated03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_mostCogsDefeated04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_trolley04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_trolley05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_saveBuilding4.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_saveBuilding05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_anniversary.jpg', + 'phase_4/maps/flannelshirt_red.jpg'] +BoyShirts = [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (8, 8), + (9, 9), + (10, 0), + (11, 0), + (14, 10), + (16, 0), + (17, 0), + (18, 12), + (19, 13)] +GirlShirts = [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (5, 5), + (6, 6), + (7, 7), + (9, 9), + (12, 0), + (13, 11), + (15, 11), + (16, 0), + (20, 0), + (21, 0), + (22, 0)] + +def isValidBoyShirt(index): + for pair in BoyShirts: + if index == pair[0]: + return 1 + + return 0 + + +def isValidGirlShirt(index): + for pair in GirlShirts: + if index == pair[0]: + return 1 + + return 0 + + +Sleeves = ['phase_3/maps/desat_sleeve_1.jpg', + 'phase_3/maps/desat_sleeve_2.jpg', + 'phase_3/maps/desat_sleeve_3.jpg', + 'phase_3/maps/desat_sleeve_4.jpg', + 'phase_3/maps/desat_sleeve_5.jpg', + 'phase_3/maps/desat_sleeve_6.jpg', + 'phase_3/maps/desat_sleeve_7.jpg', + 'phase_3/maps/desat_sleeve_8.jpg', + 'phase_3/maps/desat_sleeve_9.jpg', + 'phase_3/maps/desat_sleeve_10.jpg', + 'phase_3/maps/desat_sleeve_15.jpg', + 'phase_3/maps/desat_sleeve_16.jpg', + 'phase_3/maps/desat_sleeve_19.jpg', + 'phase_3/maps/desat_sleeve_20.jpg', + 'phase_4/maps/female_sleeve1b.jpg', + 'phase_4/maps/female_sleeve2.jpg', + 'phase_4/maps/female_sleeve3.jpg', + 'phase_4/maps/male_sleeve1.jpg', + 'phase_4/maps/male_sleeve2_palm.jpg', + 'phase_4/maps/male_sleeve3c.jpg', + 'phase_4/maps/shirt_Sleeve_ghost.jpg', + 'phase_4/maps/shirt_Sleeve_pumkin.jpg', + 'phase_4/maps/holidaySleeve1.jpg', + 'phase_4/maps/holidaySleeve3.jpg', + 'phase_4/maps/female_sleeve1b.jpg', + 'phase_4/maps/female_sleeve5New.jpg', + 'phase_4/maps/male_sleeve4New.jpg', + 'phase_4/maps/sleeve6New.jpg', + 'phase_4/maps/SleeveMaleNew7.jpg', + 'phase_4/maps/female_sleeveNew6.jpg', + 'phase_4/maps/Vday5Sleeve.jpg', + 'phase_4/maps/Vda6Sleeve.jpg', + 'phase_4/maps/Vday_shirt4sleeve.jpg', + 'phase_4/maps/Vday2cSleeve.jpg', + 'phase_4/maps/sleeveTieDye.jpg', + 'phase_4/maps/male_sleeve1.jpg', + 'phase_4/maps/StPats_sleeve.jpg', + 'phase_4/maps/StPats_sleeve2.jpg', + 'phase_4/maps/ContestfishingVestSleeve1.jpg', + 'phase_4/maps/ContestFishtankSleeve1.jpg', + 'phase_4/maps/ContestPawSleeve1.jpg', + 'phase_4/maps/CowboySleeve1.jpg', + 'phase_4/maps/CowboySleeve2.jpg', + 'phase_4/maps/CowboySleeve3.jpg', + 'phase_4/maps/CowboySleeve4.jpg', + 'phase_4/maps/CowboySleeve5.jpg', + 'phase_4/maps/CowboySleeve6.jpg', + 'phase_4/maps/4thJulySleeve1.jpg', + 'phase_4/maps/4thJulySleeve2.jpg', + 'phase_4/maps/shirt_sleeveCat7_01.jpg', + 'phase_4/maps/shirt_sleeveCat7_02.jpg', + 'phase_4/maps/contest_backpack_sleeve.jpg', + 'phase_4/maps/Contest_leder_sleeve.jpg', + 'phase_4/maps/contest_mellon_sleeve2.jpg', + 'phase_4/maps/contest_race_sleeve.jpg', + 'phase_4/maps/PJSleeveBlue.jpg', + 'phase_4/maps/PJSleeveRed.jpg', + 'phase_4/maps/PJSleevePurple.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_valentine1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_valentine2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_desat4.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_fishing1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_fishing2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_gardening1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_gardening2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_party1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_party2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_racing1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_racing2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_summer1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_summer2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_golf1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_golf2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_marathon1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_saveBuilding1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_saveBuilding2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_toonTask1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_toonTask2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_trolley1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_trolley2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_winter1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween3.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween4.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_valentine3.jpg', + 'phase_4/maps/tt_t_chr_shirtSleeve_scientist.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_mailbox.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_trashcan.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_loonyLabs.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_hydrant.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_whistle.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_cogbuster.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_mostCogsDefeated01.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_victoryParty01.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_victoryParty02.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_sellbotIcon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_sellbotVPIcon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_sellbotCrusher.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_jellyBeans.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_doodle.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween5.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloweenTurtle.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_greentoon1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_getConnectedMoverShaker.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_racingGrandPrix.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_bee.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_pirate.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_supertoon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_vampire.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_dinosaur.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_fishing04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_golf03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_mostCogsDefeated02.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_racing03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_saveBuilding3.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_trolley03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_fishing05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_golf04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween06.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_winter03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween07.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_winter02.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_fishing06.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_fishing07.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_golf05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_racing04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_racing05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_mostCogsDefeated03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_mostCogsDefeated04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_trolley04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_trolley05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_saveBuilding4.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_saveBuilding05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_anniversary.jpg', + 'phase_4/maps/flannelsleeve_red.jpg'] +BoyShorts = ['phase_3/maps/desat_shorts_1.jpg', + 'phase_3/maps/desat_shorts_2.jpg', + 'phase_3/maps/desat_shorts_4.jpg', + 'phase_3/maps/desat_shorts_6.jpg', + 'phase_3/maps/desat_shorts_7.jpg', + 'phase_3/maps/desat_shorts_8.jpg', + 'phase_3/maps/desat_shorts_9.jpg', + 'phase_3/maps/desat_shorts_10.jpg', + 'phase_4/maps/VdayShorts2.jpg', + 'phase_4/maps/shorts4.jpg', + 'phase_4/maps/shorts1.jpg', + 'phase_4/maps/shorts5.jpg', + 'phase_4/maps/CowboyShorts1.jpg', + 'phase_4/maps/CowboyShorts2.jpg', + 'phase_4/maps/4thJulyShorts1.jpg', + 'phase_4/maps/shortsCat7_01.jpg', + 'phase_4/maps/Blue_shorts_1.jpg', + 'phase_4/maps/Red_shorts_1.jpg', + 'phase_4/maps/Purple_shorts_1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_winter1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_winter2.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_winter3.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_winter4.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_valentine1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_valentine2.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_fishing1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_gardening1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_party1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_racing1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_summer1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_golf1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_halloween1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_halloween2.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_saveBuilding1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_trolley1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_halloween4.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_halloween3.jpg', + 'phase_4/maps/tt_t_chr_shorts_scientistA.jpg', + 'phase_4/maps/tt_t_chr_shorts_scientistB.jpg', + 'phase_4/maps/tt_t_chr_shorts_scientistC.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_cogbuster.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_sellbotCrusher.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_halloween5.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_halloweenTurtle.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_greentoon1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_racingGrandPrix.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_bee.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_pirate.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_supertoon.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_vampire.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_dinosaur.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_golf03.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_racing03.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_golf04.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_golf05.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_racing04.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_racing05.jpg'] +SHORTS = 0 +SKIRT = 1 +GirlBottoms = [('phase_3/maps/desat_skirt_1.jpg', SKIRT), + ('phase_3/maps/desat_skirt_2.jpg', SKIRT), + ('phase_3/maps/desat_skirt_3.jpg', SKIRT), + ('phase_3/maps/desat_skirt_4.jpg', SKIRT), + ('phase_3/maps/desat_skirt_5.jpg', SKIRT), + ('phase_3/maps/desat_shorts_1.jpg', SHORTS), + ('phase_3/maps/desat_shorts_5.jpg', SHORTS), + ('phase_3/maps/desat_skirt_6.jpg', SKIRT), + ('phase_3/maps/desat_skirt_7.jpg', SKIRT), + ('phase_3/maps/desat_shorts_10.jpg', SHORTS), + ('phase_4/maps/female_skirt1.jpg', SKIRT), + ('phase_4/maps/female_skirt2.jpg', SKIRT), + ('phase_4/maps/female_skirt3.jpg', SKIRT), + ('phase_4/maps/VdaySkirt1.jpg', SKIRT), + ('phase_4/maps/skirtNew5.jpg', SKIRT), + ('phase_4/maps/shorts5.jpg', SHORTS), + ('phase_4/maps/CowboySkirt1.jpg', SKIRT), + ('phase_4/maps/CowboySkirt2.jpg', SKIRT), + ('phase_4/maps/4thJulySkirt1.jpg', SKIRT), + ('phase_4/maps/skirtCat7_01.jpg', SKIRT), + ('phase_4/maps/Blue_shorts_1.jpg', SHORTS), + ('phase_4/maps/Red_shorts_1.jpg', SHORTS), + ('phase_4/maps/Purple_shorts_1.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_skirt_winter1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_winter2.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_winter3.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_winter4.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_valentine1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_valentine2.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_fishing1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_gardening1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_party1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_racing1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_summer1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_golf1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_halloween1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_halloween2.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_saveBuilding1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_trolley1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_halloween3.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_halloween4.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_shorts_scientistA.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_shorts_scientistB.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_shorts_scientistC.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_cogbuster.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_sellbotCrusher.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_halloween5.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_halloweenTurtle.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_skirt_greentoon1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_racingGrandPrix.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_shorts_bee.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_pirate.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_skirt_pirate.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_shorts_supertoon.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_vampire.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_dinosaur.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_skirt_golf02.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_racing03.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_golf03.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_golf04.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_racing04.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_racing05.jpg', SKIRT)] +ClothesColors = [VBase4(0.933594, 0.265625, 0.28125, 1.0), + VBase4(0.863281, 0.40625, 0.417969, 1.0), + VBase4(0.710938, 0.234375, 0.4375, 1.0), + VBase4(0.992188, 0.480469, 0.167969, 1.0), + VBase4(0.996094, 0.898438, 0.320312, 1.0), + VBase4(0.550781, 0.824219, 0.324219, 1.0), + VBase4(0.242188, 0.742188, 0.515625, 1.0), + VBase4(0.433594, 0.90625, 0.835938, 1.0), + VBase4(0.347656, 0.820312, 0.953125, 1.0), + VBase4(0.191406, 0.5625, 0.773438, 1.0), + VBase4(0.285156, 0.328125, 0.726562, 1.0), + VBase4(0.460938, 0.378906, 0.824219, 1.0), + VBase4(0.546875, 0.28125, 0.75, 1.0), + VBase4(0.570312, 0.449219, 0.164062, 1.0), + VBase4(0.640625, 0.355469, 0.269531, 1.0), + VBase4(0.996094, 0.695312, 0.511719, 1.0), + VBase4(0.832031, 0.5, 0.296875, 1.0), + VBase4(0.992188, 0.480469, 0.167969, 1.0), + VBase4(0.550781, 0.824219, 0.324219, 1.0), + VBase4(0.433594, 0.90625, 0.835938, 1.0), + VBase4(0.347656, 0.820312, 0.953125, 1.0), + VBase4(0.96875, 0.691406, 0.699219, 1.0), + VBase4(0.996094, 0.957031, 0.597656, 1.0), + VBase4(0.855469, 0.933594, 0.492188, 1.0), + VBase4(0.558594, 0.589844, 0.875, 1.0), + VBase4(0.726562, 0.472656, 0.859375, 1.0), + VBase4(0.898438, 0.617188, 0.90625, 1.0), + VBase4(1.0, 1.0, 1.0, 1.0), + VBase4(0.0, 0.2, 0.956862, 1.0), + VBase4(0.972549, 0.094117, 0.094117, 1.0), + VBase4(0.447058, 0.0, 0.90196, 1.0), + VBase4(0.3, 0.3, 0.35, 1.0), + VBase4(0.196078, 0.803921, 0.196078, 1.0), + VBase4(0.462745098039216, 0.0901960784313725, 0.0901960784313725, 1.0)] + +ShirtStyles = {'bss1': [0, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (27, 27)]], + 'bss2': [1, 1, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss3': [2, 2, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss4': [3, 3, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss5': [4, 4, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss6': [5, 5, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss7': [8, 8, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (27, 27)]], + 'bss8': [9, 9, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss9': [10, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (27, 27)]], + 'bss10': [11, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (27, 27)]], + 'bss11': [14, 10, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss12': [16, 0, [(27, 27), + (27, 4), + (27, 5), + (27, 6), + (27, 7), + (27, 8), + (27, 9)]], + 'bss13': [17, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss14': [18, 12, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (27, 27)]], + 'bss15': [19, 13, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (27, 27)]], + 'gss1': [0, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26), + (27, 27)]], + 'gss2': [1, 1, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss3': [2, 2, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss4': [3, 3, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss5': [5, 5, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss6': [6, 6, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss7': [7, 7, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss8': [9, 9, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss9': [12, 0, [(27, 27)]], + 'gss10': [13, 11, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss11': [15, 11, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss12': [16, 0, [(27, 27), + (27, 4), + (27, 5), + (27, 6), + (27, 7), + (27, 8), + (27, 9)]], + 'gss13': [20, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss14': [21, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss15': [22, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'c_ss1': [25, 16, [(27, 27)]], + 'c_ss2': [27, 18, [(27, 27)]], + 'c_ss3': [38, 27, [(27, 27)]], + 'c_bss1': [26, 17, [(27, 27)]], + 'c_bss2': [28, 19, [(27, 27)]], + 'c_bss3': [37, 26, [(27, 27)]], + 'c_bss4': [39, 28, [(27, 27)]], + 'c_gss1': [23, 14, [(27, 27)]], + 'c_gss2': [24, 15, [(27, 27)]], + 'c_gss3': [35, 24, [(27, 27)]], + 'c_gss4': [36, 25, [(27, 27)]], + 'c_gss5': [40, 29, [(27, 27)]], + 'c_ss4': [45, 34, [(27, 27)]], + 'c_ss5': [46, 35, [(27, 27)]], + 'c_ss6': [52, 41, [(27, 27)]], + 'c_ss7': [53, 42, [(27, 27)]], + 'c_ss8': [54, 43, [(27, 27)]], + 'c_ss9': [55, 44, [(27, 27)]], + 'c_ss10': [56, 45, [(27, 27)]], + 'c_ss11': [57, 46, [(27, 27)]], + 'hw_ss1': [29, 20, [(27, 27)]], + 'hw_ss2': [30, 21, [(27, 27)]], + 'hw_ss3': [114, 101, [(27, 27)]], + 'hw_ss4': [115, 102, [(27, 27)]], + 'hw_ss5': [122, 109, [(27, 27)]], + 'hw_ss6': [123, 110, [(27, 27)]], + 'hw_ss7': [124, 111, [(27, 27)]], + 'hw_ss8': [125, 112, [(27, 27)]], + 'hw_ss9': [126, 113, [(27, 27)]], + 'wh_ss1': [31, 22, [(27, 27)]], + 'wh_ss2': [32, 22, [(27, 27)]], + 'wh_ss3': [33, 23, [(27, 27)]], + 'wh_ss4': [34, 23, [(27, 27)]], + 'vd_ss1': [41, 30, [(27, 27)]], + 'vd_ss2': [42, 31, [(27, 27)]], + 'vd_ss3': [43, 32, [(27, 27)]], + 'vd_ss4': [44, 33, [(27, 27)]], + 'vd_ss5': [69, 58, [(27, 27)]], + 'vd_ss6': [70, 59, [(27, 27)]], + 'vd_ss7': [96, 85, [(27, 27)]], + 'sd_ss1': [47, 36, [(27, 27)]], + 'sd_ss2': [48, 37, [(27, 27)]], + 'sd_ss3': [116, 103, [(27, 27)]], + 'tc_ss1': [49, 38, [(27, 27)]], + 'tc_ss2': [50, 39, [(27, 27)]], + 'tc_ss3': [51, 40, [(27, 27)]], + 'tc_ss4': [62, 51, [(27, 27)]], + 'tc_ss5': [63, 52, [(27, 27)]], + 'tc_ss6': [64, 53, [(27, 27)]], + 'tc_ss7': [65, 54, [(27, 27)]], + 'j4_ss1': [58, 47, [(27, 27)]], + 'j4_ss2': [59, 48, [(27, 27)]], + 'c_ss12': [60, 49, [(27, 27)]], + 'c_ss13': [61, 50, [(27, 27)]], + 'pj_ss1': [66, 55, [(27, 27)]], + 'pj_ss2': [67, 56, [(27, 27)]], + 'pj_ss3': [68, 57, [(27, 27)]], + 'sa_ss1': [71, 60, [(27, 27)]], + 'sa_ss2': [72, 61, [(27, 27)]], + 'sa_ss3': [73, 62, [(27, 27)]], + 'sa_ss4': [74, 63, [(27, 27)]], + 'sa_ss5': [75, 64, [(27, 27)]], + 'sa_ss6': [76, 65, [(27, 27)]], + 'sa_ss7': [77, 66, [(27, 27)]], + 'sa_ss8': [78, 67, [(27, 27)]], + 'sa_ss9': [79, 68, [(27, 27)]], + 'sa_ss10': [80, 69, [(27, 27)]], + 'sa_ss11': [81, 70, [(27, 27)]], + 'sa_ss12': [82, 71, [(27, 27)]], + 'sa_ss13': [83, 72, [(27, 27)]], + 'sa_ss14': [84, 73, [(27, 27)]], + 'sa_ss15': [85, 74, [(27, 27)]], + 'sa_ss16': [86, 75, [(27, 27)]], + 'sa_ss17': [87, 76, [(27, 27)]], + 'sa_ss18': [88, 77, [(27, 27)]], + 'sa_ss19': [89, 78, [(27, 27)]], + 'sa_ss20': [90, 79, [(27, 27)]], + 'sa_ss21': [91, 80, [(27, 27)]], + 'sa_ss22': [92, 81, [(27, 27)]], + 'sa_ss23': [93, 82, [(27, 27)]], + 'sa_ss24': [94, 83, [(27, 27)]], + 'sa_ss25': [95, 84, [(27, 27)]], + 'sa_ss26': [106, 93, [(27, 27)]], + 'sa_ss27': [110, 97, [(27, 27)]], + 'sa_ss28': [111, 98, [(27, 27)]], + 'sa_ss29': [120, 107, [(27, 27)]], + 'sa_ss30': [121, 108, [(27, 27)]], + 'sa_ss31': [118, 105, [(27, 27)]], + 'sa_ss32': [127, 114, [(27, 27)]], + 'sa_ss33': [128, 115, [(27, 27)]], + 'sa_ss34': [129, 116, [(27, 27)]], + 'sa_ss35': [130, 117, [(27, 27)]], + 'sa_ss36': [131, 118, [(27, 27)]], + 'sa_ss37': [132, 119, [(27, 27)]], + 'sa_ss38': [133, 120, [(27, 27)]], + 'sa_ss39': [134, 121, [(27, 27)]], + 'sa_ss40': [135, 122, [(27, 27)]], + 'sa_ss41': [136, 123, [(27, 27)]], + 'sa_ss42': [137, 124, [(27, 27)]], + 'sa_ss43': [138, 125, [(27, 27)]], + 'sa_ss44': [139, 126, [(27, 27)]], + 'sa_ss45': [140, 127, [(27, 27)]], + 'sa_ss46': [141, 128, [(27, 27)]], + 'sa_ss47': [142, 129, [(27, 27)]], + 'sa_ss48': [143, 130, [(27, 27)]], + 'sa_ss49': [144, 116, [(27, 27)]], + 'sa_ss50': [145, 131, [(27, 27)]], + 'sa_ss51': [146, 133, [(27, 27)]], + 'sa_ss52': [147, 134, [(27, 27)]], + 'sa_ss53': [148, 135, [(27, 27)]], + 'sa_ss54': [149, 136, [(27, 27)]], + 'sa_ss55': [150, 137, [(27, 27)]], + 'sc_1': [97, 86, [(27, 27)]], + 'sc_2': [98, 86, [(27, 27)]], + 'sc_3': [99, 86, [(27, 27)]], + 'sil_1': [100, 87, [(27, 27)]], + 'sil_2': [101, 88, [(27, 27)]], + 'sil_3': [102, 89, [(27, 27)]], + 'sil_4': [103, 90, [(27, 27)]], + 'sil_5': [104, 91, [(27, 27)]], + 'sil_6': [105, 92, [(27, 27)]], + 'sil_7': [107, 94, [(27, 27)]], + 'sil_8': [108, 95, [(27, 27)]], + 'emb_us1': [103, 90, [(27, 27)]], + 'emb_us2': [100, 87, [(27, 27)]], + 'emb_us3': [101, 88, [(27, 27)]], + 'sb_1': [109, 96, [(27, 27)]], + 'jb_1': [112, 99, [(27, 27)]], + 'jb_2': [113, 100, [(27, 27)]], + 'ugcms': [117, 104, [(27, 27)]], + 'lb_1': [119, 106, [(27, 27)]], + 'flannel': [148, 135, [(27, 27)]]} +BottomStyles = {'bbs1': [0, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20]], + 'bbs2': [1, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20]], + 'bbs3': [2, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20]], + 'bbs4': [3, [0, + 1, + 2, + 4, + 6, + 8, + 9, + 11, + 12, + 13, + 15, + 16, + 17, + 18, + 19, + 20, + 27]], + 'bbs5': [4, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20]], + 'bbs6': [5, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 27]], + 'bbs7': [6, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 20, + 27]], + 'bbs8': [7, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 27]], + 'vd_bs1': [8, [27]], + 'vd_bs2': [23, [27]], + 'vd_bs3': [24, [27]], + 'c_bs1': [9, [27]], + 'c_bs2': [10, [27]], + 'c_bs5': [15, [27]], + 'sd_bs1': [11, [27]], + 'sd_bs2': [44, [27]], + 'pj_bs1': [16, [27]], + 'pj_bs2': [17, [27]], + 'pj_bs3': [18, [27]], + 'wh_bs1': [19, [27]], + 'wh_bs2': [20, [27]], + 'wh_bs3': [21, [27]], + 'wh_bs4': [22, [27]], + 'hw_bs1': [47, [27]], + 'hw_bs2': [48, [27]], + 'hw_bs5': [49, [27]], + 'hw_bs6': [50, [27]], + 'hw_bs7': [51, [27]], + 'gsk1': [0, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26, + 27]], + 'gsk2': [1, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26]], + 'gsk3': [2, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26]], + 'gsk4': [3, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26]], + 'gsk5': [4, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26]], + 'gsk6': [7, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26, + 27]], + 'gsk7': [8, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26, + 27]], + 'gsh1': [5, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26, + 27]], + 'gsh2': [6, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26, + 27]], + 'gsh3': [9, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26, + 27]], + 'c_gsk1': [10, [27]], + 'c_gsk2': [11, [27]], + 'c_gsk3': [12, [27]], + 'vd_gs1': [13, [27]], + 'vd_gs2': [27, [27]], + 'vd_gs3': [28, [27]], + 'c_gsk4': [14, [27]], + 'sd_gs1': [15, [27]], + 'sd_gs2': [48, [27]], + 'c_gsk5': [16, [27]], + 'c_gsk6': [17, [27]], + 'c_bs3': [12, [27]], + 'c_bs4': [13, [27]], + 'j4_bs1': [14, [27]], + 'j4_gs1': [18, [27]], + 'c_gsk7': [19, [27]], + 'pj_gs1': [20, [27]], + 'pj_gs2': [21, [27]], + 'pj_gs3': [22, [27]], + 'wh_gsk1': [23, [27]], + 'wh_gsk2': [24, [27]], + 'wh_gsk3': [25, [27]], + 'wh_gsk4': [26, [27]], + 'sa_bs1': [25, [27]], + 'sa_bs2': [26, [27]], + 'sa_bs3': [27, [27]], + 'sa_bs4': [28, [27]], + 'sa_bs5': [29, [27]], + 'sa_bs6': [30, [27]], + 'sa_bs7': [31, [27]], + 'sa_bs8': [32, [27]], + 'sa_bs9': [33, [27]], + 'sa_bs10': [34, [27]], + 'sa_bs11': [35, [27]], + 'sa_bs12': [36, [27]], + 'sa_bs13': [41, [27]], + 'sa_bs14': [46, [27]], + 'sa_bs15': [45, [27]], + 'sa_bs16': [52, [27]], + 'sa_bs17': [53, [27]], + 'sa_bs18': [54, [27]], + 'sa_bs19': [55, [27]], + 'sa_bs20': [56, [27]], + 'sa_bs21': [57, [27]], + 'sa_gs1': [29, [27]], + 'sa_gs2': [30, [27]], + 'sa_gs3': [31, [27]], + 'sa_gs4': [32, [27]], + 'sa_gs5': [33, [27]], + 'sa_gs6': [34, [27]], + 'sa_gs7': [35, [27]], + 'sa_gs8': [36, [27]], + 'sa_gs9': [37, [27]], + 'sa_gs10': [38, [27]], + 'sa_gs11': [39, [27]], + 'sa_gs12': [40, [27]], + 'sa_gs13': [45, [27]], + 'sa_gs14': [50, [27]], + 'sa_gs15': [49, [27]], + 'sa_gs16': [57, [27]], + 'sa_gs17': [58, [27]], + 'sa_gs18': [59, [27]], + 'sa_gs19': [60, [27]], + 'sa_gs20': [61, [27]], + 'sa_gs21': [62, [27]], + 'sc_bs1': [37, [27]], + 'sc_bs2': [38, [27]], + 'sc_bs3': [39, [27]], + 'sc_gs1': [41, [27]], + 'sc_gs2': [42, [27]], + 'sc_gs3': [43, [27]], + 'sil_bs1': [40, [27]], + 'sil_gs1': [44, [27]], + 'hw_bs3': [42, [27]], + 'hw_gs3': [46, [27]], + 'hw_bs4': [43, [27]], + 'hw_gs4': [47, [27]], + 'hw_gs1': [51, [27]], + 'hw_gs2': [52, [27]], + 'hw_gs5': [54, [27]], + 'hw_gs6': [55, [27]], + 'hw_gs7': [56, [27]], + 'hw_gsk1': [53, [27]], + 'nig2': [57, [27]]} +MAKE_A_TOON = 1 +TAMMY_TAILOR = 2004 +LONGJOHN_LEROY = 1007 +TAILOR_HARMONY = 4008 +BONNIE_BLOSSOM = 5007 +WARREN_BUNDLES = 3008 +WORNOUT_WAYLON = 9010 +TailorCollections = {MAKE_A_TOON: [['bss1', 'bss2'], + ['gss1', 'gss2'], + ['bbs1', 'bbs2'], + ['gsk1', 'gsh1']], + TAMMY_TAILOR: [['bss1', 'bss2'], + ['gss1', 'gss2'], + ['bbs1', 'bbs2'], + ['gsk1', 'gsh1']], + LONGJOHN_LEROY: [['bss3', 'bss14'], + ['gss3', 'gss14'], + ['bbs3', 'bbs4'], + ['gsk2', 'gsh2']], + TAILOR_HARMONY: [['bss5', 'bss6', 'bss10'], + ['gss5', 'gss6', 'gss9'], + ['bbs5'], + ['gsk3', 'gsh3']], + BONNIE_BLOSSOM: [['bss7', 'bss8', 'bss12'], + ['gss8', 'gss10', 'gss12'], + ['bbs6'], + ['gsk4', 'gsk5']], + WARREN_BUNDLES: [['bss9', 'bss13'], + ['gss7', 'gss11'], + ['bbs7'], + ['gsk6']], + WORNOUT_WAYLON: [['bss11', 'bss15'], + ['gss13', 'gss15'], + ['bbs8'], + ['gsk7']]} + +BOY_SHIRTS = 0 +GIRL_SHIRTS = 1 +BOY_SHORTS = 2 +GIRL_BOTTOMS = 3 +HAT = 1 +GLASSES = 2 +BACKPACK = 4 +SHOES = 8 +MakeAToonBoyBottoms = [] +MakeAToonBoyShirts = [] +MakeAToonGirlBottoms = [] +MakeAToonGirlShirts = [] +MakeAToonGirlSkirts = [] +MakeAToonGirlShorts = [] + +#Combine all tailors into MAKE_A_TOON tailor. +if mergeMATTailor: + for tailors in TailorCollections: + for girlBottoms in TailorCollections[tailors][GIRL_BOTTOMS]: + if girlBottoms not in TailorCollections[MAKE_A_TOON][GIRL_BOTTOMS]: + TailorCollections[MAKE_A_TOON][GIRL_BOTTOMS].append(girlBottoms) + for boyShorts in TailorCollections[tailors][BOY_SHORTS]: + if boyShorts not in TailorCollections[MAKE_A_TOON][BOY_SHORTS]: + TailorCollections[MAKE_A_TOON][BOY_SHORTS].append(boyShorts) + for girlShirts in TailorCollections[tailors][GIRL_SHIRTS]: + if girlShirts not in TailorCollections[MAKE_A_TOON][GIRL_SHIRTS]: + TailorCollections[MAKE_A_TOON][GIRL_SHIRTS].append(girlShirts) + for boyShirts in TailorCollections[tailors][BOY_SHIRTS]: + if boyShirts not in TailorCollections[MAKE_A_TOON][BOY_SHIRTS]: + TailorCollections[MAKE_A_TOON][BOY_SHIRTS].append(boyShirts) + +for style in TailorCollections[MAKE_A_TOON][BOY_SHORTS]: + index = BottomStyles[style][0] + MakeAToonBoyBottoms.append(index) + +for style in TailorCollections[MAKE_A_TOON][BOY_SHIRTS]: + index = ShirtStyles[style][0] + MakeAToonBoyShirts.append(index) + +for style in TailorCollections[MAKE_A_TOON][GIRL_BOTTOMS]: + index = BottomStyles[style][0] + MakeAToonGirlBottoms.append(index) + +for style in TailorCollections[MAKE_A_TOON][GIRL_SHIRTS]: + index = ShirtStyles[style][0] + MakeAToonGirlShirts.append(index) + +for index in MakeAToonGirlBottoms: + flag = GirlBottoms[index][1] + if flag == SKIRT: + MakeAToonGirlSkirts.append(index) + elif flag == SHORTS: + MakeAToonGirlShorts.append(index) + else: + notify.error('Invalid flag') + +def getRandomTop(gender, tailorId = MAKE_A_TOON, generator = None): + if generator == None: + generator = random + collection = TailorCollections[tailorId] + if gender == 'm': + style = generator.choice(collection[BOY_SHIRTS]) + else: + style = generator.choice(collection[GIRL_SHIRTS]) + styleList = ShirtStyles[style] + colors = generator.choice(styleList[2]) + return (styleList[0], + colors[0], + styleList[1], + colors[1]) + + +def getRandomBottom(gender, tailorId = MAKE_A_TOON, generator = None, girlBottomType = None): + if generator == None: + generator = random + collection = TailorCollections[tailorId] + if gender == 'm': + style = generator.choice(collection[BOY_SHORTS]) + elif girlBottomType is None: + style = generator.choice(collection[GIRL_BOTTOMS]) + elif girlBottomType == SKIRT: + skirtCollection = filter(lambda style: GirlBottoms[BottomStyles[style][0]][1] == SKIRT, collection[GIRL_BOTTOMS]) + style = generator.choice(skirtCollection) + elif girlBottomType == SHORTS: + shortsCollection = filter(lambda style: GirlBottoms[BottomStyles[style][0]][1] == SHORTS, collection[GIRL_BOTTOMS]) + style = generator.choice(shortsCollection) + else: + notify.error('Bad girlBottomType: %s' % girlBottomType) + styleList = BottomStyles[style] + color = generator.choice(styleList[1]) + return (styleList[0], color) + + +def getRandomGirlBottom(type): + bottoms = [] + index = 0 + for bottom in GirlBottoms: + if bottom[1] == type: + bottoms.append(index) + index += 1 + + return random.choice(bottoms) + + +def getRandomGirlBottomAndColor(type): + bottoms = [] + if type == SHORTS: + typeStr = 'gsh' + else: + typeStr = 'gsk' + for bottom in BottomStyles.keys(): + if bottom.find(typeStr) >= 0: + bottoms.append(bottom) + + style = BottomStyles[random.choice(bottoms)] + return (style[0], random.choice(style[1])) + + +def getRandomizedTops(gender, tailorId = MAKE_A_TOON, generator = None): + if generator == None: + generator = random + collection = TailorCollections[tailorId] + if gender == 'm': + collection = collection[BOY_SHIRTS][:] + else: + collection = collection[GIRL_SHIRTS][:] + tops = [] + random.shuffle(collection) + for style in collection: + colors = ShirtStyles[style][2][:] + random.shuffle(colors) + for color in colors: + tops.append((ShirtStyles[style][0], + color[0], + ShirtStyles[style][1], + color[1])) + + return tops + + +def getRandomizedBottoms(gender, tailorId = MAKE_A_TOON, generator = None): + if generator == None: + generator = random + collection = TailorCollections[tailorId] + if gender == 'm': + collection = collection[BOY_SHORTS][:] + else: + collection = collection[GIRL_BOTTOMS][:] + bottoms = [] + random.shuffle(collection) + for style in collection: + colors = BottomStyles[style][1][:] + random.shuffle(colors) + for color in colors: + bottoms.append((BottomStyles[style][0], color)) + + return bottoms + + +def getTops(gender, tailorId = MAKE_A_TOON): + if gender == 'm': + collection = TailorCollections[tailorId][BOY_SHIRTS] + else: + collection = TailorCollections[tailorId][GIRL_SHIRTS] + tops = [] + for style in collection: + for color in ShirtStyles[style][2]: + tops.append((ShirtStyles[style][0], + color[0], + ShirtStyles[style][1], + color[1])) + + return tops + + +def getAllTops(gender): + tops = [] + for style in ShirtStyles.keys(): + if gender == 'm': + if style[0] == 'g' or style[:3] == 'c_g': + continue + elif style[0] == 'b' or style[:3] == 'c_b': + continue + for color in ShirtStyles[style][2]: + tops.append((ShirtStyles[style][0], + color[0], + ShirtStyles[style][1], + color[1])) + + return tops + + +def getBottoms(gender, tailorId = MAKE_A_TOON): + if gender == 'm': + collection = TailorCollections[tailorId][BOY_SHORTS] + else: + collection = TailorCollections[tailorId][GIRL_BOTTOMS] + bottoms = [] + for style in collection: + for color in BottomStyles[style][1]: + bottoms.append((BottomStyles[style][0], color)) + + return bottoms + + +def getAllBottoms(gender, output = 'both'): + bottoms = [] + for style in BottomStyles.keys(): + if gender == 'm': + if style[0] == 'g' or style[:3] == 'c_g' or style[:4] == 'vd_g' or style[:4] == 'sd_g' or style[:4] == 'j4_g' or style[:4] == 'pj_g' or style[:4] == 'wh_g' or style[:4] == 'sa_g' or style[:4] == 'sc_g' or style[:5] == 'sil_g' or style[:4] == 'hw_g': + continue + elif style[0] == 'b' or style[:3] == 'c_b' or style[:4] == 'vd_b' or style[:4] == 'sd_b' or style[:4] == 'j4_b' or style[:4] == 'pj_b' or style[:4] == 'wh_b' or style[:4] == 'sa_b' or style[:4] == 'sc_b' or style[:5] == 'sil_b' or style[:4] == 'hw_b': + continue + bottomIdx = BottomStyles[style][0] + if gender == 'f': + textureType = GirlBottoms[bottomIdx][1] + else: + textureType = SHORTS + if output == 'both' or output == 'skirts' and textureType == SKIRT or output == 'shorts' and textureType == SHORTS: + for color in BottomStyles[style][1]: + bottoms.append((bottomIdx, color)) + + return bottoms + + +allColorsList = [(1.0, 1.0, 1.0, 1.0), + (0.96, 0.69, 0.69, 1.0), + (0.93, 0.26, 0.28, 1.0), + (0.86, 0.40, 0.41, 1.0), + (0.71, 0.23, 0.43, 1.0), + (0.57, 0.44, 0.16, 1.0), + (0.64, 0.35, 0.26, 1.0), + (0.99, 0.69, 0.51, 1.0), + (0.83, 0.5, 0.29, 1.0), + (0.99, 0.48, 0.16, 1.0), + (0.99, 0.89, 0.32, 1.0), + (0.99, 0.95, 0.59, 1.0), + (0.85, 0.93, 0.49, 1.0), + (0.55, 0.82, 0.32, 1.0), + (0.24, 0.74, 0.51, 1.0), + (0.30, 0.96, 0.4, 1.0), + (0.43, 0.9, 0.83, 1.0), + (0.34, 0.82, 0.95, 1.0), + (0.19, 0.56, 0.77, 1.0), + (0.55, 0.58, 0.87, 1.0), + (0.28, 0.32, 0.72, 1.0), + (0.46, 0.37, 0.82, 1.0), + (0.54, 0.28, 0.75, 1.0), + (0.72, 0.47, 0.85, 1.0), + (0.89, 0.61, 0.9, 1.0), + (0.7, 0.7, 0.8, 1.0), + (0.3, 0.3, 0.35, 1.0), + (0.0, 0.63, 0.25, 1.0), + (0.67, 0.92, 1.0, 1.0), + (0.98, 0.89, 0.74, 1.0), + (0.74, 1.0, 0.84, 1.0), + (0.47, 0.44, 0.44, 1.0), + (0.99, 0.25, 0.39, 1.0), + (0.81, 0.7, 0.23, 1.0), + (0.74, 0.75, 0.76, 1.0), + (1.0, 0.63, 0.26, 1.0), + (0.0, 0.4, 0.64, 1.0), + (0.86, 0.07, 0.23, 1.0), + (0.0, 0.63, 0.51, 1.0), + (0.8, 0.49, 0.19, 1.0)] +disallowedColorsList = [(1.0, 1.0, 1.0, 1.0), + (0.7, 0.7, 0.8, 1.0), + (0.3, 0.3, 0.35, 1.0), + (0.47, 0.44, 0.44, 1.0), + (0.74, 0.75, 0.76, 1.0)] +matColorsList = [x for x in allColorsList if x not in disallowedColorsList] +defaultColorList = [0, + 1, + 32, + 2, + 3, + 4, + 5, + 6, + 7, + 29, + 8, + 37, + 35, + 9, + 10, + 33, + 11, + 12, + 30, + 13, + 14, + 15, + 39, + 27, + 28, + 16, + 17, + 18, + 19, + 20, + 21, + 38, + 36, + 22, + 23, + 24, + 25, + 34, + 31, + 26] +HatModels = [None, + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_baseball', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_safari', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_ribbon', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_heart', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_topHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_anvil', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_flowerPot', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_sandbag', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_weight', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_fez', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_golfHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_partyHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_pillBox', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_crown', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_cowboyHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_pirateHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_propellerHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_fishingHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_sombreroHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_strawHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_sunHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_antenna', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_beeHiveHairdo', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_bowler', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_chefsHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_detective', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_feathers', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_fedora', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_mickeysBandConductorHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_nativeAmericanFeather', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_pompadorHairdo', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_princess', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_robinHoodHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_romanHelmet', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_spiderAntennaThingy', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_tiara', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_vikingHelmet', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_witch', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_wizard', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_conquistadorHelmet', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_firefighterHelmet', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_foilPyramid', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_minersHardhatWithLight', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_napoleonHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_pilotsCap', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_policeHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_rainbowAfroWig', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_sailorHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_carmenMirandaFruitHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_bobbyHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_jugheadHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_winter', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_bandana', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_dinosaur', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_band', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_birdNest', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_mousekateer'] +HatTextures = [None, + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonRed.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonPurple.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_heartYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_topHatBlue.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_safariBrown.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_safariGreen.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_baseballBlue.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_baseballOrange.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonChecker.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonLtRed.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonRainbow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_baseballYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_baseballRed.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_baseballTeal.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonPinkDots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_baseballPurple.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonCheckerGreen.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_partyToon.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_bowlerRoger.jpg', + 'phase_4/maps/tt_m_chr_avt_acc_hat_mousekateer.jpg'] +GlassesModels = [None, + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_roundGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_miniblinds', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_narrowGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_starGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_3dGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_aviator', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_catEyeGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_dorkGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_jackieOShades', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_scubaMask', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_goggles', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_grouchoMarxEyebrow', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_heartGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_insectEyeGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_masqueradeTypeMask', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_masqueradeTypeMask3', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_monocle', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_mouthGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_squareRims', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_eyepatch', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_alienGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_hypno_goggles17'] +GlassesTextures = [None, + 'phase_4/maps/tt_t_chr_avt_acc_msk_masqueradeTypeMask2.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_msk_masqueradeTypeMask4.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_msk_masqueradeTypeMask5.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_msk_eyepatchGems.jpg'] +BackpackModels = [None, + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_backpack', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_batWings', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_beeWings', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_dragonFlyWings', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_scubaTank', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_sharkFin', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_angelWings', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_backpackWithToys', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_butterflyWings', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_dragonWing', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_jetPack', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_spiderLegs', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_stuffedAnimalBackpackA', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_birdWings', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_stuffedAnimalBackpackCat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_stuffedAnimalBackpackDog', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_airplane', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_woodenSword', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_supertoonCape', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_vampireCape', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_dinosaurTail', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_band', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_gags', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_flunky'] +BackpackTextures = [None, + 'phase_4/maps/tt_t_chr_avt_acc_pac_backpackOrange.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_pac_backpackPurple.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_pac_backpackPolkaDotRed.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_pac_backpackPolkaDotYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_pac_angelWingsMultiColor.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_pac_butterflyWingsStyle2.jpg'] +ShoesModels = ['feet', + 'shoes', + 'boots_short', + 'boots_long'] +ShoesTextures = ['phase_3/maps/tt_t_chr_avt_acc_sho_athleticGreen.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_athleticRed.jpg', + 'phase_3/maps/tt_t_chr_avt_acc_sho_docMartinBootsGreen.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_converseStyleGreen.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_wingtips.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_maryJaneShoes.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_deckShoes.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_athleticYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_converseStyleBlack.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_converseStyleWhite.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_converseStylePink.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_cowboyBoots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_fashionBootsPurple.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_hiTopSneakers.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_maryJaneShoesBrown.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_maryJaneShoesRed.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_superToonRedBoots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_tennisShoesGreen.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_tennisShoesPink.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_converseStyleRed.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_docMartinBootsAqua.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_docMartinBootsBrown.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_docMartinBootsYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_fashionBootsBlueSquares.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_fashionBootsGreenHearts.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_fashionBootsGreyDots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_fashionBootsOrangeStars.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_fashionBootsPinkStars.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_loafers.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_maryJaneShoesPurple.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_motorcycleBoots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_oxfords.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_rainBootsPink.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_santaBoots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_winterBootsBeige.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_winterBootsPink.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_workBoots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_converseStyleYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_docMartinBootsPink.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_hiTopSneakersPink.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_rainBootsRedDots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_tennisShoesPurple.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_tennisShoesViolet.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_tennisShoesYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_rainBootsBlue.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_rainBootsYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_athleticBlack.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_pirate.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_dinosaur.jpg'] +HatStyles = {'none': [0, 0, 0], + 'hbb1': [1, 0, 0], + 'hsf1': [2, 0, 0], + 'hsf2': [2, 5, 0], + 'hsf3': [2, 6, 0], + 'hht1': [4, 0, 0], + 'hht2': [4, 3, 0], + 'htp1': [5, 0, 0], + 'htp2': [5, 4, 0], + 'hav1': [6, 0, 0], + 'hfp1': [7, 0, 0], + 'hsg1': [8, 0, 0], + 'hwt1': [9, 0, 0], + 'hfz1': [10, 0, 0], + 'hgf1': [11, 0, 0], + 'hpt1': [12, 0, 0], + 'hpt2': [12, 19, 0], + 'hpb1': [13, 0, 0], + 'hcr1': [14, 0, 0], + 'hbb2': [1, 7, 0], + 'hbb3': [1, 8, 0], + 'hcw1': [15, 0, 0], + 'hpr1': [16, 0, 0], + 'hpp1': [17, 0, 0], + 'hfs1': [18, 0, 0], + 'hsb1': [19, 0, 0], + 'hst1': [20, 0, 0], + 'hat1': [22, 0, 0], + 'hhd1': [23, 0, 0], + 'hbw1': [24, 0, 0], + 'hch1': [25, 0, 0], + 'hdt1': [26, 0, 0], + 'hft1': [27, 0, 0], + 'hfd1': [28, 0, 0], + 'hmk1': [29, 0, 0], + 'hft2': [30, 0, 0], + 'hhd2': [31, 0, 0], + 'hrh1': [33, 0, 0], + 'hhm1': [34, 0, 0], + 'hat2': [35, 0, 0], + 'htr1': [36, 0, 0], + 'hhm2': [37, 0, 0], + 'hwz1': [38, 0, 0], + 'hwz2': [39, 0, 0], + 'hhm3': [40, 0, 0], + 'hhm4': [41, 0, 0], + 'hfp2': [42, 0, 0], + 'hhm5': [43, 0, 0], + 'hnp1': [44, 0, 0], + 'hpc2': [45, 0, 0], + 'hph1': [46, 0, 0], + 'hwg1': [47, 0, 0], + 'hbb4': [1, 13, 0], + 'hbb5': [1, 14, 0], + 'hbb6': [1, 15, 0], + 'hsl1': [48, 0, 0], + 'hfr1': [49, 0, 0], + 'hby1': [50, 0, 0], + 'hjh1': [51, 0, 0], + 'hbb7': [1, 17, 0], + 'hwt2': [52, 0, 0], + 'hhw2': [54, 0, 0], + 'hob1': [55, 0, 0], + 'hbn1': [56, 0, 0], + 'hrb1': [3, 0, 0], + 'hrb2': [3, 1, 0], + 'hrb3': [3, 2, 0], + 'hsu1': [21, 0, 0], + 'hrb4': [3, 9, 0], + 'hrb5': [3, 10, 0], + 'hrb6': [3, 11, 0], + 'hrb7': [3, 12, 0], + 'hpc1': [32, 0, 0], + 'hrb8': [3, 16, 0], + 'hrb9': [3, 18, 0], + 'hhw1': [53, 0, 0], + 'kmh1': [57, 0, 0]} +GlassesStyles = {'none': [0, 0, 0], + 'grd1': [1, 0, 0], + 'gmb1': [2, 0, 0], + 'gnr1': [3, 0, 0], + 'gst1': [4, 0, 0], + 'g3d1': [5, 0, 0], + 'gav1': [6, 0, 0], + 'gjo1': [9, 0, 0], + 'gsb1': [10, 0, 0], + 'ggl1': [11, 0, 0], + 'ggm1': [12, 0, 0], + 'ghg1': [13, 0, 0], + 'gie1': [14, 0, 0], + 'gmt1': [15, 0, 0], + 'gmt2': [15, 1, 0], + 'gmt3': [16, 0, 0], + 'gmt4': [16, 2, 0], + 'gmt5': [16, 3, 0], + 'gmn1': [17, 0, 0], + 'gmo1': [18, 0, 0], + 'gsr1': [19, 0, 0], + 'gce1': [7, 0, 0], + 'gdk1': [8, 0, 0], + 'gag1': [21, 0, 0], + 'ghy1': [22, 0, 0], + 'ghw1': [20, 0, 0], + 'ghw2': [20, 4, 0]} +BackpackStyles = {'none': [0, 0, 0], + 'bpb1': [1, 0, 0], + 'bpb2': [1, 1, 0], + 'bpb3': [1, 2, 0], + 'bpd1': [1, 3, 0], + 'bpd2': [1, 4, 0], + 'bwg1': [2, 0, 0], + 'bwg2': [3, 0, 0], + 'bwg3': [4, 0, 0], + 'bst1': [5, 0, 0], + 'bfn1': [6, 0, 0], + 'baw1': [7, 0, 0], + 'baw2': [7, 5, 0], + 'bwt1': [8, 0, 0], + 'bwg4': [9, 0, 0], + 'bwg5': [9, 6, 0], + 'bwg6': [10, 0, 0], + 'bjp1': [11, 0, 0], + 'blg1': [12, 0, 0], + 'bsa1': [13, 0, 0], + 'bwg7': [14, 0, 0], + 'bsa2': [15, 0, 0], + 'bsa3': [16, 0, 0], + 'bap1': [17, 0, 0], + 'bhw1': [18, 0, 0], + 'bhw2': [19, 0, 0], + 'bhw3': [20, 0, 0], + 'bhw4': [21, 0, 0], + 'bob1': [22, 0, 0], + 'bfg1': [23, 0, 0], + 'bfl1': [24, 0, 0]} +ShoesStyles = {'none': [0, 0, 0], + 'sat1': [1, 0, 0], + 'sat2': [1, 1, 0], + 'smb1': [3, 2, 0], + 'scs1': [2, 3, 0], + 'sdk1': [1, 6, 0], + 'sat3': [1, 7, 0], + 'scs2': [2, 8, 0], + 'scs3': [2, 9, 0], + 'scs4': [2, 10, 0], + 'scb1': [3, 11, 0], + 'sht1': [2, 13, 0], + 'ssb1': [3, 16, 0], + 'sts1': [1, 17, 0], + 'sts2': [1, 18, 0], + 'scs5': [2, 19, 0], + 'smb2': [3, 20, 0], + 'smb3': [3, 21, 0], + 'smb4': [3, 22, 0], + 'slf1': [1, 28, 0], + 'smt1': [3, 30, 0], + 'sox1': [1, 31, 0], + 'srb1': [3, 32, 0], + 'sst1': [3, 33, 0], + 'swb1': [3, 34, 0], + 'swb2': [3, 35, 0], + 'swk1': [2, 36, 0], + 'scs6': [2, 37, 0], + 'smb5': [3, 38, 0], + 'sht2': [2, 39, 0], + 'srb2': [3, 40, 0], + 'sts3': [1, 41, 0], + 'sts4': [1, 42, 0], + 'sts5': [1, 43, 0], + 'srb3': [3, 44, 0], + 'srb4': [3, 45, 0], + 'sat4': [1, 46, 0], + 'shw1': [3, 47, 0], + 'shw2': [3, 48, 0], + 'swt1': [1, 4, 0], + 'smj1': [2, 5, 0], + 'sfb1': [3, 12, 0], + 'smj2': [2, 14, 0], + 'smj3': [2, 15, 0], + 'sfb2': [3, 23, 0], + 'sfb3': [3, 24, 0], + 'sfb4': [3, 25, 0], + 'sfb5': [3, 26, 0], + 'sfb6': [3, 27, 0], + 'smj4': [2, 29, 0]} + +def isValidHat(itemIdx, textureIdx, colorIdx): + for style in HatStyles.values(): + if itemIdx == style[0] and textureIdx == style[1] and colorIdx == style[2]: + return True + + return False + + +def isValidGlasses(itemIdx, textureIdx, colorIdx): + for style in GlassesStyles.values(): + if itemIdx == style[0] and textureIdx == style[1] and colorIdx == style[2]: + return True + + return False + + +def isValidBackpack(itemIdx, textureIdx, colorIdx): + for style in BackpackStyles.values(): + if itemIdx == style[0] and textureIdx == style[1] and colorIdx == style[2]: + return True + + return False + + +def isValidShoes(itemIdx, textureIdx, colorIdx): + for style in ShoesStyles.values(): + if itemIdx == style[0] and textureIdx == style[1] and colorIdx == style[2]: + return True + + return False + + +def isValidAccessory(itemIdx, textureIdx, colorIdx, which): + if which == HAT: + return isValidHat(itemIdx, textureIdx, colorIdx) + elif which == GLASSES: + return isValidGlasses(itemIdx, textureIdx, colorIdx) + elif which == BACKPACK: + return isValidBackpack(itemIdx, textureIdx, colorIdx) + elif which == SHOES: + return isValidShoes(itemIdx, textureIdx, colorIdx) + else: + return False + + +class ToonDNA: + + def __init__(self, str = None, type = None, dna = None, r = None, b = None, g = None): + if str != None: + self.makeFromNetString(str) + elif type != None: + if type == 't': + if dna == None: + self.newToonRandom(r, g, b) + else: + self.newToonFromProperties(*dna.asTuple()) + else: + self.type = 'u' + + def __str__(self): + string = 'type = toon\n' + string = string + 'gender = %s\n' % self.gender + string = string + 'head = %s, torso = %s, legs = %s\n' % (self.head, self.torso, self.legs) + string = string + 'arm color = %s\n' % (self.armColor,) + string = string + 'glove color = %s\n' % (self.gloveColor,) + string = string + 'leg color = %s\n' % (self.legColor,) + string = string + 'head color = %s\n' % (self.headColor,) + string = string + 'top texture = %d\n' % self.topTex + string = string + 'top texture color = %d\n' % self.topTexColor + string = string + 'sleeve texture = %d\n' % self.sleeveTex + string = string + 'sleeve texture color = %d\n' % self.sleeveTexColor + string = string + 'bottom texture = %d\n' % self.botTex + string = string + 'bottom texture color = %d\n' % self.botTexColor + return string + + def clone(self): + d = ToonDNA() + d.makeFromNetString(self.makeNetString()) + return d + + def makeNetString(self): + dg = PyDatagram() + dg.addFixedString(self.type, 1) + if self.type == 't': + headIndex = toonHeadTypes.index(self.head) + torsoIndex = toonTorsoTypes.index(self.torso) + legsIndex = toonLegTypes.index(self.legs) + dg.addUint8(headIndex) + dg.addUint8(torsoIndex) + dg.addUint8(legsIndex) + if self.gender == 'm': + dg.addUint8(1) + else: + dg.addUint8(0) + dg.addUint8(self.topTex) + dg.addUint8(self.topTexColor) + dg.addUint8(self.sleeveTex) + dg.addUint8(self.sleeveTexColor) + dg.addUint8(self.botTex) + dg.addUint8(self.botTexColor) + self.armColor = self.migrateColor(self.armColor) + self.gloveColor = self.migrateColor(self.gloveColor) + self.legColor = self.migrateColor(self.legColor) + self.headColor = self.migrateColor(self.headColor) + for colors in (self.armColor, self.gloveColor, self.legColor, self.headColor): + for color in colors[:-1]: + dg.addFloat64(color) + elif self.type == 'u': + notify.error('undefined avatar') + else: + notify.error('unknown avatar type: ', self.type) + return dg.getMessage() + + def isValidNetString(self, string): + dg = PyDatagram(string) + dgi = PyDatagramIterator(dg) + type = dgi.getFixedString(1) + if type not in ('t',): + return False + headIndex = dgi.getUint8() + torsoIndex = dgi.getUint8() + legsIndex = dgi.getUint8() + if headIndex >= len(toonHeadTypes): + return False + if torsoIndex >= len(toonTorsoTypes): + return False + if legsIndex >= len(toonLegTypes): + return False + gender = dgi.getUint8() + if gender == 1: + gender = 'm' + else: + gender = 'f' + topTex = dgi.getUint8() + topTexColor = dgi.getUint8() + sleeveTex = dgi.getUint8() + sleeveTexColor = dgi.getUint8() + botTex = dgi.getUint8() + botTexColor = dgi.getUint8() + armColor = (dgi.getFloat64(), dgi.getFloat64(), dgi.getFloat64(), 1.0) + gloveColor = (dgi.getFloat64(), dgi.getFloat64(), dgi.getFloat64(), 1.0) + legColor = (dgi.getFloat64(), dgi.getFloat64(), dgi.getFloat64(), 1.0) + headColor = (dgi.getFloat64(), dgi.getFloat64(), dgi.getFloat64(), 1.0) + if topTex >= len(Shirts): + return False + if topTexColor >= len(ClothesColors): + return False + if sleeveTex >= len(Sleeves): + return False + if sleeveTexColor >= len(ClothesColors): + return False + if botTex >= choice(gender == 'm', len(BoyShorts), len(GirlBottoms)): + return False + if botTexColor >= len(ClothesColors): + return False + if not self.isValid(armColor): + return False + if not self.isValid(gloveColor): + return False + if not self.isValid(legColor): + return False + if not self.isValid(headColor): + return False + return True + + def isValid(self, color): + if color in allColorsList: + return True + + hsv = colorsys.rgb_to_hsv(*color[:-1]) + return ToontownGlobals.COLOR_SATURATION_MIN <= hsv[1] <= ToontownGlobals.COLOR_SATURATION_MAX and ToontownGlobals.COLOR_VALUE_MIN <= hsv[2] <= ToontownGlobals.COLOR_VALUE_MAX + + def makeFromNetString(self, string): + dg = PyDatagram(string) + dgi = PyDatagramIterator(dg) + self.type = dgi.getFixedString(1) + if self.type == 't': + headIndex = dgi.getUint8() + torsoIndex = dgi.getUint8() + legsIndex = dgi.getUint8() + self.head = toonHeadTypes[headIndex] + self.torso = toonTorsoTypes[torsoIndex] + self.legs = toonLegTypes[legsIndex] + gender = dgi.getUint8() + if gender == 1: + self.gender = 'm' + else: + self.gender = 'f' + self.topTex = dgi.getUint8() + self.topTexColor = dgi.getUint8() + self.sleeveTex = dgi.getUint8() + self.sleeveTexColor = dgi.getUint8() + self.botTex = dgi.getUint8() + self.botTexColor = dgi.getUint8() + self.armColor = (dgi.getFloat64(), dgi.getFloat64(), dgi.getFloat64(), 1.0) + self.gloveColor = (dgi.getFloat64(), dgi.getFloat64(), dgi.getFloat64(), 1.0) + self.legColor = (dgi.getFloat64(), dgi.getFloat64(), dgi.getFloat64(), 1.0) + self.headColor = (dgi.getFloat64(), dgi.getFloat64(), dgi.getFloat64(), 1.0) + else: + notify.error('unknown avatar type: ', self.type) + + def defaultColor(self): + return 25 + + def newToon(self, dna, color = None): + if len(dna) == 4: + self.type = 't' + self.head = dna[0] + self.torso = dna[1] + self.legs = dna[2] + self.gender = dna[3] + self.topTex = 0 + self.topTexColor = 0 + self.sleeveTex = 0 + self.sleeveTexColor = 0 + self.botTex = 0 + self.botTexColor = 0 + if color == None: + color = 25 + color = self.migrateColor(color) + self.armColor = color + self.legColor = color + self.headColor = color + self.gloveColor = 0 + else: + notify.error("tuple must be in format ('%s', '%s', '%s', '%s')") + + def migrateColor(self, color): + return allColorsList[color] if isinstance(color, int) else color + + def newToonFromProperties(self, head, torso, legs, gender, armColor, gloveColor, legColor, headColor, topTexture, topTextureColor, sleeveTexture, sleeveTextureColor, bottomTexture, bottomTextureColor): + self.type = 't' + self.head = head + self.torso = torso + self.legs = legs + self.gender = gender + self.armColor = self.migrateColor(armColor) + self.gloveColor = self.migrateColor(gloveColor) + self.legColor = self.migrateColor(legColor) + self.headColor = self.migrateColor(headColor) + self.topTex = topTexture + self.topTexColor = topTextureColor + self.sleeveTex = sleeveTexture + self.sleeveTexColor = sleeveTextureColor + self.botTex = bottomTexture + self.botTexColor = bottomTextureColor + + def updateToonProperties(self, head = None, torso = None, legs = None, gender = None, armColor = None, gloveColor = None, legColor = None, headColor = None, topTexture = None, topTextureColor = None, sleeveTexture = None, sleeveTextureColor = None, bottomTexture = None, bottomTextureColor = None, shirt = None, bottom = None): + if head: + self.head = head + if torso: + self.torso = torso + if legs: + self.legs = legs + if gender: + self.gender = gender + if armColor: + self.armColor = self.migrateColor(armColor) + if gloveColor: + self.gloveColor = self.migrateColor(gloveColor) + if legColor: + self.legColor = self.migrateColor(legColor) + if headColor: + self.headColor = self.migrateColor(headColor) + if topTexture: + self.topTex = topTexture + if topTextureColor: + self.topTexColor = topTextureColor + if sleeveTexture: + self.sleeveTex = sleeveTexture + if sleeveTextureColor: + self.sleeveTexColor = sleeveTextureColor + if bottomTexture: + self.botTex = bottomTexture + if bottomTextureColor: + self.botTexColor = bottomTextureColor + if shirt: + str, colorIndex = shirt + defn = ShirtStyles[str] + self.topTex = defn[0] + self.topTexColor = defn[2][colorIndex][0] + self.sleeveTex = defn[1] + self.sleeveTexColor = defn[2][colorIndex][1] + if bottom: + str, colorIndex = bottom + defn = BottomStyles[str] + self.botTex = defn[0] + self.botTexColor = defn[1][colorIndex] + + def newToonRandom(self, seed = None, gender = 'm', npc = 0, stage = None): + if seed: + generator = random.Random() + generator.seed(seed) + else: + generator = random + self.type = 't' + self.legs = generator.choice(toonLegTypes + ['m', + 'l', + 'l', + 'l']) + self.gender = gender + if not npc: + if stage == MAKE_A_TOON: + animal = generator.choice(allToonHeadAnimalIndices) + self.head = toonHeadTypes[animal] + else: + self.head = generator.choice(toonHeadTypes) + else: + self.head = generator.choice(toonHeadTypes[:22]) + top, topColor, sleeve, sleeveColor = getRandomTop(gender, generator=generator) + bottom, bottomColor = getRandomBottom(gender, generator=generator) + color = generator.choice(matColorsList) + if gender == 'm': + self.torso = generator.choice(toonTorsoTypes[:3]) + self.topTex = top + self.topTexColor = topColor + self.sleeveTex = sleeve + self.sleeveTexColor = sleeveColor + self.botTex = bottom + self.botTexColor = bottomColor + else: + self.torso = generator.choice(toonTorsoTypes[:6]) + self.topTex = top + self.topTexColor = topColor + self.sleeveTex = sleeve + self.sleeveTexColor = sleeveColor + if self.torso[1] == 'd': + bottom, bottomColor = getRandomBottom(gender, generator=generator, girlBottomType=SKIRT) + else: + bottom, bottomColor = getRandomBottom(gender, generator=generator, girlBottomType=SHORTS) + self.botTex = bottom + self.botTexColor = bottomColor + self.armColor = color + self.legColor = color + self.headColor = color + self.gloveColor = self.migrateColor(0) + + def asTuple(self): + return (self.head, + self.torso, + self.legs, + self.gender, + self.armColor, + self.gloveColor, + self.legColor, + self.headColor, + self.topTex, + self.topTexColor, + self.sleeveTex, + self.sleeveTexColor, + self.botTex, + self.botTexColor) + + def asNpcTuple(self): + return (self.head, + self.torso, + self.legs, + self.gender, + allColorsList.index(self.armColor), + allColorsList.index(self.gloveColor), + allColorsList.index(self.legColor), + allColorsList.index(self.headColor), + self.topTex, + self.topTexColor, + self.sleeveTex, + self.sleeveTexColor, + self.botTex, + self.botTexColor) + + def getType(self): + if self.type == 't': + type = self.getAnimal() + else: + notify.error('Invalid DNA type: ', self.type) + return type + + def getAnimal(self): + if self.head[0] == 'd': + return 'dog' + elif self.head[0] == 'c': + return 'cat' + elif self.head[0] == 'm': + return 'mouse' + elif self.head[0] == 'h': + return 'horse' + elif self.head[0] == 'r': + return 'rabbit' + elif self.head[0] == 'f': + return 'duck' + elif self.head[0] == 'p': + return 'monkey' + elif self.head[0] == 'b': + return 'bear' + elif self.head[0] == 's': + return 'pig' + else: + notify.error('unknown headStyle: ', self.head[0]) + + def getHeadSize(self): + if self.head[1] == 'l': + return 'long' + elif self.head[1] == 's': + return 'short' + else: + notify.error('unknown head size: ', self.head[1]) + + def getMuzzleSize(self): + if self.head[2] == 'l': + return 'long' + elif self.head[2] == 's': + return 'short' + else: + notify.error('unknown muzzle size: ', self.head[2]) + + def getTorsoSize(self): + if self.torso[0] == 'l': + return 'long' + elif self.torso[0] == 'm': + return 'medium' + elif self.torso[0] == 's': + return 'short' + else: + notify.error('unknown torso size: ', self.torso[0]) + + def getLegSize(self): + if self.legs == 'l': + return 'long' + elif self.legs == 'm': + return 'medium' + elif self.legs == 's': + return 'short' + else: + notify.error('unknown leg size: ', self.legs) + + def getGender(self): + return self.gender + + def getClothes(self): + if len(self.torso) == 1: + return 'naked' + elif self.torso[1] == 's': + return 'shorts' + elif self.torso[1] == 'd': + return 'dress' + else: + notify.error('unknown clothing type: ', self.torso[1]) + + def getArmColor(self): + return self.armColor + + def getLegColor(self): + return self.legColor + + def getHeadColor(self): + return self.headColor + + def getGloveColor(self): + return self.gloveColor + + def getBlackColor(self): + try: + return allColorsList[26] + except: + return allColorsList[0] + + def getWhiteColor(self): + return allColorsList[0] \ No newline at end of file diff --git a/toontown/toon/ToonDetail.py b/toontown/toon/ToonDetail.py new file mode 100755 index 00000000..b845bc33 --- /dev/null +++ b/toontown/toon/ToonDetail.py @@ -0,0 +1,14 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from otp.avatar import AvatarDetail +from toontown.toon import DistributedToon + +class ToonDetail(AvatarDetail.AvatarDetail): + notify = directNotify.newCategory('ToonDetail') + + def getDClass(self): + return 'DistributedToon' + + def createHolder(self): + toon = DistributedToon.DistributedToon(base.cr, bFake=True) + toon.forceAllowDelayDelete() + return toon diff --git a/toontown/toon/ToonHead.py b/toontown/toon/ToonHead.py new file mode 100755 index 00000000..5893bad3 --- /dev/null +++ b/toontown/toon/ToonHead.py @@ -0,0 +1,1340 @@ +from direct.actor import Actor +from direct.task import Task +from toontown.toonbase import ToontownGlobals +import string +import random +from panda3d.core import * +from direct.interval.IntervalGlobal import * +from direct.fsm.ClassicFSM import ClassicFSM +from direct.fsm.State import State +from direct.directnotify import DirectNotifyGlobal + +if not base.config.GetBool('want-new-anims', 1): + HeadDict = {'dls': '/models/char/dogMM_Shorts-head-', + 'dss': '/models/char/dogMM_Skirt-head-', + 'dsl': '/models/char/dogSS_Shorts-head-', + 'dll': '/models/char/dogLL_Shorts-head-', + 'c': '/models/char/cat-heads-', + 'h': '/models/char/horse-heads-', + 'm': '/models/char/mouse-heads-', + 'r': '/models/char/rabbit-heads-', + 'f': '/models/char/duck-heads-', + 'p': '/models/char/monkey-heads-', + 'b': '/models/char/bear-heads-', + 's': '/models/char/pig-heads-'} +else: + HeadDict = {'dls': '/models/char/tt_a_chr_dgm_shorts_head_', + 'dss': '/models/char/tt_a_chr_dgm_skirt_head_', + 'dsl': '/models/char/tt_a_chr_dgs_shorts_head_', + 'dll': '/models/char/tt_a_chr_dgl_shorts_head_', + 'c': '/models/char/cat-heads-', + 'h': '/models/char/horse-heads-', + 'm': '/models/char/mouse-heads-', + 'r': '/models/char/rabbit-heads-', + 'f': '/models/char/duck-heads-', + 'p': '/models/char/monkey-heads-', + 'b': '/models/char/bear-heads-', + 's': '/models/char/pig-heads-'} +EyelashDict = {'d': '/models/char/dog-lashes', + 'c': '/models/char/cat-lashes', + 'h': '/models/char/horse-lashes', + 'm': '/models/char/mouse-lashes', + 'r': '/models/char/rabbit-lashes', + 'f': '/models/char/duck-lashes', + 'p': '/models/char/monkey-lashes', + 'b': '/models/char/bear-lashes', + 's': '/models/char/pig-lashes'} +DogMuzzleDict = {'dls': '/models/char/dogMM_Shorts-headMuzzles-', + 'dss': '/models/char/dogMM_Skirt-headMuzzles-', + 'dsl': '/models/char/dogSS_Shorts-headMuzzles-', + 'dll': '/models/char/dogLL_Shorts-headMuzzles-'} + +PreloadHeads = {} + +def preloadToonHeads(): + global PreloadHeads + if not PreloadHeads: + print 'Preloading Toon heads...' + for key in HeadDict.keys(): + fileRoot = HeadDict[key] + + PreloadHeads['phase_3' + fileRoot + '1000'] = loader.loadModel('phase_3' + fileRoot + '1000') + PreloadHeads['phase_3' + fileRoot + '1000'].flattenMedium() + + PreloadHeads['phase_3' + fileRoot + '500'] = loader.loadModel('phase_3' + fileRoot + '500') + PreloadHeads['phase_3' + fileRoot + '500'].flattenMedium() + + PreloadHeads['phase_3' + fileRoot + '250'] = loader.loadModel('phase_3' + fileRoot + '250') + PreloadHeads['phase_3' + fileRoot + '250'].flattenMedium() + +preloadToonHeads() + +class ToonHead(Actor.Actor): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonHead') + EyesOpen = loader.loadTexture('phase_3/maps/eyes.jpg', 'phase_3/maps/eyes_a.rgb') + EyesOpen.setMinfilter(Texture.FTLinear) + EyesOpen.setMagfilter(Texture.FTLinear) + EyesClosed = loader.loadTexture('phase_3/maps/eyesClosed.jpg', 'phase_3/maps/eyesClosed_a.rgb') + EyesClosed.setMinfilter(Texture.FTLinear) + EyesClosed.setMagfilter(Texture.FTLinear) + EyesSadOpen = loader.loadTexture('phase_3/maps/eyesSad.jpg', 'phase_3/maps/eyesSad_a.rgb') + EyesSadOpen.setMinfilter(Texture.FTLinear) + EyesSadOpen.setMagfilter(Texture.FTLinear) + EyesSadClosed = loader.loadTexture('phase_3/maps/eyesSadClosed.jpg', 'phase_3/maps/eyesSadClosed_a.rgb') + EyesSadClosed.setMinfilter(Texture.FTLinear) + EyesSadClosed.setMagfilter(Texture.FTLinear) + EyesAngryOpen = loader.loadTexture('phase_3/maps/eyesAngry.jpg', 'phase_3/maps/eyesAngry_a.rgb') + EyesAngryOpen.setMinfilter(Texture.FTLinear) + EyesAngryOpen.setMagfilter(Texture.FTLinear) + EyesAngryClosed = loader.loadTexture('phase_3/maps/eyesAngryClosed.jpg', 'phase_3/maps/eyesAngryClosed_a.rgb') + EyesAngryClosed.setMinfilter(Texture.FTLinear) + EyesAngryClosed.setMagfilter(Texture.FTLinear) + EyesSurprised = loader.loadTexture('phase_3/maps/eyesSurprised.jpg', 'phase_3/maps/eyesSurprised_a.rgb') + EyesSurprised.setMinfilter(Texture.FTLinear) + EyesSurprised.setMagfilter(Texture.FTLinear) + Muzzle = loader.loadTexture('phase_3/maps/muzzleShrtGeneric.jpg') + Muzzle.setMinfilter(Texture.FTLinear) + Muzzle.setMagfilter(Texture.FTLinear) + MuzzleSurprised = loader.loadTexture('phase_3/maps/muzzleShortSurprised.jpg') + MuzzleSurprised.setMinfilter(Texture.FTLinear) + MuzzleSurprised.setMagfilter(Texture.FTLinear) + LeftA = Point3(0.06, 0.0, 0.14) + LeftB = Point3(-0.13, 0.0, 0.1) + LeftC = Point3(-0.05, 0.0, 0.0) + LeftD = Point3(0.06, 0.0, 0.0) + RightA = Point3(0.13, 0.0, 0.1) + RightB = Point3(-0.06, 0.0, 0.14) + RightC = Point3(-0.06, 0.0, 0.0) + RightD = Point3(0.05, 0.0, 0.0) + LeftAD = Point3(LeftA[0] - LeftA[2] * (LeftD[0] - LeftA[0]) / (LeftD[2] - LeftA[2]), 0.0, 0.0) + LeftBC = Point3(LeftB[0] - LeftB[2] * (LeftC[0] - LeftB[0]) / (LeftC[2] - LeftB[2]), 0.0, 0.0) + RightAD = Point3(RightA[0] - RightA[2] * (RightD[0] - RightA[0]) / (RightD[2] - RightA[2]), 0.0, 0.0) + RightBC = Point3(RightB[0] - RightB[2] * (RightC[0] - RightB[0]) / (RightC[2] - RightB[2]), 0.0, 0.0) + + def __init__(self): + try: + self.ToonHead_initialized + except: + self.ToonHead_initialized = 1 + Actor.Actor.__init__(self) + self.toonName = 'ToonHead-' + str(self.this) + self.__blinkName = 'blink-' + self.toonName + self.__stareAtName = 'stareAt-' + self.toonName + self.__lookName = 'look-' + self.toonName + self.lookAtTrack = None + self.__eyes = None + self.__eyelashOpen = None + self.__eyelashClosed = None + self.__lod500Eyes = None + self.__lod250Eyes = None + self.__lpupil = None + self.__lod500lPupil = None + self.__lod250lPupil = None + self.__rpupil = None + self.__lod500rPupil = None + self.__lod250rPupil = None + self.__muzzle = None + self.__eyesOpen = ToonHead.EyesOpen + self.__eyesClosed = ToonHead.EyesClosed + self.__height = 0.0 + self.__eyelashesHiddenByGlasses = False + self.randGen = random.Random() + self.randGen.seed(random.random()) + self.eyelids = ClassicFSM('eyelids', [State('off', self.enterEyelidsOff, self.exitEyelidsOff, ['open', 'closed', 'surprised']), + State('open', self.enterEyelidsOpen, self.exitEyelidsOpen, ['closed', 'surprised', 'off']), + State('surprised', self.enterEyelidsSurprised, self.exitEyelidsSurprised, ['open', 'closed', 'off']), + State('closed', self.enterEyelidsClosed, self.exitEyelidsClosed, ['open', 'surprised', 'off'])], 'off', 'off') + self.eyelids.enterInitialState() + self.emote = None + self.__stareAtNode = NodePath() + self.__defaultStarePoint = Point3(0, 0, 0) + self.__stareAtPoint = self.__defaultStarePoint + self.__stareAtTime = 0 + self.lookAtPositionCallbackArgs = None + + return + + def delete(self): + try: + self.ToonHead_deleted + except: + self.ToonHead_deleted = 1 + taskMgr.remove(self.__blinkName) + taskMgr.remove(self.__lookName) + taskMgr.remove(self.__stareAtName) + if self.lookAtTrack: + self.lookAtTrack.finish() + self.lookAtTrack = None + del self.eyelids + del self.__stareAtNode + del self.__stareAtPoint + if self.__eyes: + del self.__eyes + if self.__lpupil: + del self.__lpupil + if self.__rpupil: + del self.__rpupil + if self.__eyelashOpen: + del self.__eyelashOpen + if self.__eyelashClosed: + del self.__eyelashClosed + self.lookAtPositionCallbackArgs = None + Actor.Actor.delete(self) + + return + + def setupHead(self, dna, forGui = 0): + self.__height = self.generateToonHead(1, dna, ('1000',), forGui) + self.generateToonColor(dna) + animalStyle = dna.getAnimal() + bodyScale = ToontownGlobals.toonBodyScales[animalStyle] + headScale = ToontownGlobals.toonHeadScales[animalStyle] + self.getGeomNode().setScale(headScale[0] * bodyScale * 1.3, headScale[1] * bodyScale * 1.3, headScale[2] * bodyScale * 1.3) + if forGui: + self.getGeomNode().setDepthWrite(1) + self.getGeomNode().setDepthTest(1) + if dna.getAnimal() == 'dog': + self.loop('neutral') + + def fitAndCenterHead(self, maxDim, forGui = 0): + p1 = Point3() + p2 = Point3() + self.calcTightBounds(p1, p2) + if forGui: + h = 180 + t = p1[0] + p1.setX(-p2[0]) + p2.setX(-t) + else: + h = 0 + d = p2 - p1 + biggest = max(d[0], d[2]) + s = maxDim / biggest + mid = (p1 + d / 2.0) * s + self.setPosHprScale(-mid[0], -mid[1] + 1, -mid[2], h, 0, 0, s, s, s) + + def setLookAtPositionCallbackArgs(self, argTuple): + self.lookAtPositionCallbackArgs = argTuple + + def getHeight(self): + return self.__height + + def getRandomForwardLookAtPoint(self): + x = self.randGen.choice((-0.8, + -0.5, + 0, + 0.5, + 0.8)) + z = self.randGen.choice((-0.5, + 0, + 0.5, + 0.8)) + return Point3(x, 1.5, z) + + def findSomethingToLookAt(self): + if self.lookAtPositionCallbackArgs != None: + pnt = self.lookAtPositionCallbackArgs[0].getLookAtPosition(self.lookAtPositionCallbackArgs[1], self.lookAtPositionCallbackArgs[2]) + self.startStareAt(self, pnt) + return + if self.randGen.random() < 0.33: + lookAtPnt = self.getRandomForwardLookAtPoint() + else: + lookAtPnt = self.__defaultStarePoint + self.lerpLookAt(lookAtPnt, blink=1) + return + + def generateToonHead(self, copy, style, lods, forGui = 0): + global PreloadHeads + headStyle = style.head + fix = None + if headStyle == 'dls': + filePrefix = HeadDict['dls'] + headHeight = 0.75 + elif headStyle == 'dss': + filePrefix = HeadDict['dss'] + headHeight = 0.5 + elif headStyle == 'dsl': + filePrefix = HeadDict['dsl'] + headHeight = 0.5 + elif headStyle == 'dll': + filePrefix = HeadDict['dll'] + headHeight = 0.75 + elif headStyle == 'cls': + filePrefix = HeadDict['c'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'css': + filePrefix = HeadDict['c'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'csl': + filePrefix = HeadDict['c'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'cll': + filePrefix = HeadDict['c'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + elif headStyle == 'hls': + filePrefix = HeadDict['h'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'hss': + filePrefix = HeadDict['h'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'hsl': + filePrefix = HeadDict['h'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'hll': + filePrefix = HeadDict['h'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + elif headStyle == 'mls': + filePrefix = HeadDict['m'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'mss': + filePrefix = HeadDict['m'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'rls': + filePrefix = HeadDict['r'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'rss': + filePrefix = HeadDict['r'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'rsl': + filePrefix = HeadDict['r'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'rll': + filePrefix = HeadDict['r'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + elif headStyle == 'fls': + filePrefix = HeadDict['f'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'fss': + filePrefix = HeadDict['f'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'fsl': + filePrefix = HeadDict['f'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'fll': + filePrefix = HeadDict['f'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + elif headStyle == 'pls': + filePrefix = HeadDict['p'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'pss': + filePrefix = HeadDict['p'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'psl': + filePrefix = HeadDict['p'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'pll': + filePrefix = HeadDict['p'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + elif headStyle == 'bls': + filePrefix = HeadDict['b'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'bss': + filePrefix = HeadDict['b'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'bsl': + filePrefix = HeadDict['b'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'bll': + filePrefix = HeadDict['b'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + elif headStyle == 'sls': + filePrefix = HeadDict['s'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'sss': + filePrefix = HeadDict['s'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'ssl': + filePrefix = HeadDict['s'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'sll': + filePrefix = HeadDict['s'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + else: + ToonHead.notify.error('unknown head style: %s' % headStyle) + if len(lods) == 1: + filepath = 'phase_3' + filePrefix + lods[0] + self.loadModel(PreloadHeads[filepath], 'head', 'lodRoot', copy = True) + if not forGui: + pLoaded = self.loadPumpkin(headStyle[1], None, copy) + self.loadSnowMan(headStyle[1], None, copy) + if not copy: + self.showAllParts('head') + if fix != None: + fix(style, None, copy) + if not forGui: + if pLoaded: + self.__fixPumpkin(style, None, copy) + else: + self.__lods = lods + self.__style = style + self.__headStyle = headStyle + self.__copy = copy + else: + for lod in lods: + filepath = 'phase_3' + filePrefix + lod + self.loadModel(PreloadHeads[filepath], 'head', lod, True) + if not forGui: + pLoaded = self.loadPumpkin(headStyle[1], lod, copy) + self.loadSnowMan(headStyle[1], lod, copy) + if not copy: + self.showAllParts('head', lod) + if fix != None: + fix(style, lod, copy) + if not forGui: + if pLoaded: + self.__fixPumpkin(style, lod, copy) + else: + self.__lods = lods + self.__style = style + self.__headStyle = headStyle + self.__copy = copy + + self.__fixEyes(style, forGui) + self.setupEyelashes(style) + self.eyelids.request('closed') + self.eyelids.request('open') + self.setupMuzzles(style) + return headHeight + + def loadPumpkin(self, headStyle, lod, copy): + if not hasattr(self, 'pumpkins'): + self.pumpkins = NodePathCollection() + ppath = 'phase_4/models/estate/pumpkin_' + if headStyle is 'l': + if copy: + pmodel = loader.loadModel(ppath + 'tall') + else: + pmodel = loader.loadModel(ppath + 'tall') + ptype = 'tall' + else: + if copy: + pmodel = loader.loadModel(ppath + 'short') + else: + pmodel = loader.loadModel(ppath + 'short') + ptype = 'short' + if pmodel: + p = pmodel.find('**/pumpkin_' + ptype + '*') + p.setScale(0.5) + p.setZ(-0.5) + p.setH(180) + if lod: + p.reparentTo(self.find('**/' + lod + '/**/__Actor_head')) + else: + p.reparentTo(self.find('**/__Actor_head')) + self.pumpkins.addPath(p) + pmodel.removeNode() + return True + else: + del self.pumpkins + return False + + def loadSnowMan(self, headStyle, lod, copy): + if not hasattr(self, 'snowMen'): + self.snowMen = NodePathCollection() + snowManPath = 'phase_4/models/props/tt_m_efx_snowmanHead_' + if headStyle is 'l': + snowManPath = snowManPath + 'tall' + else: + snowManPath = snowManPath + 'short' + model = loader.loadModel(snowManPath) + if model: + model.setZ(-0.5) + model.setH(180) + if lod: + model.reparentTo(self.getPart('head', lod)) + else: + model.reparentTo(self.find('**/__Actor_head')) + self.snowMen.addPath(model) + model.stash() + return True + else: + del self.snowMen + return False + + def __fixPumpkin(self, style, lodName = None, copy = 1): + if lodName == None: + searchRoot = self + else: + searchRoot = self.find('**/' + str(lodName)) + pumpkin = searchRoot.find('**/__Actor_head/pumpkin*') + pumpkin.stash() + return + + def enablePumpkins(self, enable): + if not hasattr(self, 'pumpkins'): + if len(self.__lods) == 1: + pLoaded = self.loadPumpkin(self.__headStyle[1], None, self.__copy) + if pLoaded: + self.__fixPumpkin(self.__style, None, self.__copy) + else: + for lod in self.__lods: + pLoaded = self.loadPumpkin(self.__headStyle[1], lod, self.__copy) + if pLoaded: + self.__fixPumpkin(self.__style, lod, self.__copy) + + if hasattr(self, 'pumpkins'): + for x in ['__lods', + '__style', + '__headStyle', + '__copy']: + if hasattr(self, '_ToonHead' + x): + delattr(self, '_ToonHead' + x) + + if hasattr(self, 'pumpkins'): + if enable: + if self.__eyelashOpen: + self.__eyelashOpen.stash() + if self.__eyelashClosed: + self.__eyelashClosed.stash() + self.pumpkins.unstash() + else: + if not self.__eyelashesHiddenByGlasses: + if self.__eyelashOpen: + self.__eyelashOpen.unstash() + if self.__eyelashClosed: + self.__eyelashClosed.unstash() + self.pumpkins.stash() + return + + def enableSnowMen(self, enable): + if not hasattr(self, 'snowMen'): + if len(self.__lods) == 1: + self.loadSnowMan(self.__headStyle[1], None, self.__copy) + else: + for lod in self.__lds: + self.loadSnowMan(self.__headStyle[1], lod, self.__copy) + + if hasattr(self, 'snowMen'): + if enable: + if self.__eyelashOpen: + self.__eyelashOpen.stash() + if self.__eyelashClosed: + self.__eyelashClosed.stash() + self.snowMen.unstash() + else: + if not self.__eyelashesHiddenByGlasses: + if self.__eyelashOpen: + self.__eyelashOpen.unstash() + if self.__eyelashClosed: + self.__eyelashClosed.unstash() + self.snowMen.stash() + return + + def hideEars(self): + self.findAllMatches('**/ears*;+s').stash() + + def showEars(self): + self.findAllMatches('**/ears*;+s').unstash() + + def hideEyelashes(self): + if self.__eyelashOpen: + self.__eyelashOpen.stash() + if self.__eyelashClosed: + self.__eyelashClosed.stash() + self.__eyelashesHiddenByGlasses = True + + def showEyelashes(self): + if self.__eyelashOpen: + self.__eyelashOpen.unstash() + if self.__eyelashClosed: + self.__eyelashClosed.unstash() + self.__eyelashesHiddenByGlasses = False + + def generateToonColor(self, style): + parts = self.findAllMatches('**/head*') + parts.setColor(style.getHeadColor()) + animalType = style.getAnimal() + if animalType == 'cat' or animalType == 'rabbit' or animalType == 'bear' or animalType == 'mouse' or animalType == 'pig': + parts = self.findAllMatches('**/ear?-*') + parts.setColor(style.getHeadColor()) + + def __fixEyes(self, style, forGui = 0): + mode = -3 + if forGui: + mode = -2 + if self.hasLOD(): + for lodName in self.getLODNames(): + self.drawInFront('eyes*', 'head-front*', mode, lodName=lodName) + if base.config.GetBool('want-new-anims', 1): + if not self.find('**/joint_pupil*').isEmpty(): + self.drawInFront('joint_pupil*', 'eyes*', -1, lodName=lodName) + else: + self.drawInFront('def_*_pupil', 'eyes*', -1, lodName=lodName) + else: + self.drawInFront('joint_pupil*', 'eyes*', -1, lodName=lodName) + + self.__eyes = self.getLOD(1000).find('**/eyes*') + self.__lod500Eyes = self.getLOD(500).find('**/eyes*') + self.__lod250Eyes = self.getLOD(250).find('**/eyes*') + if self.__lod500Eyes.isEmpty(): + self.__lod500Eyes = None + else: + self.__lod500Eyes.setColorOff() + if base.config.GetBool('want-new-anims', 1): + if not self.find('**/joint_pupilL*').isEmpty(): + self.__lod500lPupil = self.__lod500Eyes.find('**/joint_pupilL*') + self.__lod500rPupil = self.__lod500Eyes.find('**/joint_pupilR*') + else: + self.__lod500lPupil = self.__lod500Eyes.find('**/def_left_pupil*') + self.__lod500rPupil = self.__lod500Eyes.find('**/def_right_pupil*') + else: + self.__lod500lPupil = self.__lod500Eyes.find('**/joint_pupilL*') + self.__lod500rPupil = self.__lod500Eyes.find('**/joint_pupilR*') + if self.__lod250Eyes.isEmpty(): + self.__lod250Eyes = None + else: + self.__lod250Eyes.setColorOff() + if base.config.GetBool('want-new-anims', 1): + if not self.find('**/joint_pupilL*').isEmpty(): + self.__lod250lPupil = self.__lod250Eyes.find('**/joint_pupilL*') + self.__lod250rPupil = self.__lod250Eyes.find('**/joint_pupilR*') + else: + self.__lod250lPupil = self.__lod250Eyes.find('**/def_left_pupil*') + self.__lod250rPupil = self.__lod250Eyes.find('**/def_right_pupil*') + else: + self.__lod250lPupil = self.__lod250Eyes.find('**/joint_pupilL*') + self.__lod250rPupil = self.__lod250Eyes.find('**/joint_pupilR*') + else: + self.drawInFront('eyes*', 'head-front*', mode) + if base.config.GetBool('want-new-anims', 1): + if not self.find('joint_pupil*').isEmpty(): + self.drawInFront('joint_pupil*', 'eyes*', -1) + else: + self.drawInFront('def_*_pupil', 'eyes*', -1) + else: + self.drawInFront('joint_pupil*', 'eyes*', -1) + self.__eyes = self.find('**/eyes*') + if not self.__eyes.isEmpty(): + self.__eyes.setColorOff() + self.__lpupil = None + self.__rpupil = None + if base.config.GetBool('want-new-anims', 1): + if not self.find('**/joint_pupilL*').isEmpty(): + if self.getLOD(1000): + lp = self.getLOD(1000).find('**/joint_pupilL*') + rp = self.getLOD(1000).find('**/joint_pupilR*') + else: + lp = self.find('**/joint_pupilL*') + rp = self.find('**/joint_pupilR*') + elif not self.getLOD(1000): + lp = self.find('**/def_left_pupil*') + rp = self.find('**/def_right_pupil*') + else: + lp = self.getLOD(1000).find('**/def_left_pupil*') + rp = self.getLOD(1000).find('**/def_right_pupil*') + else: + lp = self.__eyes.find('**/joint_pupilL*') + rp = self.__eyes.find('**/joint_pupilR*') + if lp.isEmpty() or rp.isEmpty(): + print 'Unable to locate pupils.' + else: + leye = self.__eyes.attachNewNode('leye') + reye = self.__eyes.attachNewNode('reye') + lmat = Mat4(0.802174, 0.59709, 0, 0, -0.586191, 0.787531, 0.190197, 0, 0.113565, -0.152571, 0.981746, 0, -0.233634, 0.418062, 0.0196875, 1) + leye.setMat(lmat) + rmat = Mat4(0.786788, -0.617224, 0, 0, 0.602836, 0.768447, 0.214658, 0, -0.132492, -0.16889, 0.976689, 0, 0.233634, 0.418062, 0.0196875, 1) + reye.setMat(rmat) + self.__lpupil = leye.attachNewNode('lpupil') + self.__rpupil = reye.attachNewNode('rpupil') + lpt = self.__eyes.attachNewNode('') + rpt = self.__eyes.attachNewNode('') + lpt.wrtReparentTo(self.__lpupil) + rpt.wrtReparentTo(self.__rpupil) + lp.reparentTo(lpt) + rp.reparentTo(rpt) + self.__lpupil.adjustAllPriorities(1) + self.__rpupil.adjustAllPriorities(1) + if self.__lod500Eyes: + self.__lod500lPupil.adjustAllPriorities(1) + self.__lod500rPupil.adjustAllPriorities(1) + if self.__lod250Eyes: + self.__lod250lPupil.adjustAllPriorities(1) + self.__lod250rPupil.adjustAllPriorities(1) + animalType = style.getAnimal() + if animalType != 'dog': + self.__lpupil.flattenStrong() + self.__rpupil.flattenStrong() + return + + def __setPupilDirection(self, x, y): + if y < 0.0: + y2 = -y + left1 = self.LeftAD + (self.LeftD - self.LeftAD) * y2 + left2 = self.LeftBC + (self.LeftC - self.LeftBC) * y2 + right1 = self.RightAD + (self.RightD - self.RightAD) * y2 + right2 = self.RightBC + (self.RightC - self.RightBC) * y2 + else: + y2 = y + left1 = self.LeftAD + (self.LeftA - self.LeftAD) * y2 + left2 = self.LeftBC + (self.LeftB - self.LeftBC) * y2 + right1 = self.RightAD + (self.RightA - self.RightAD) * y2 + right2 = self.RightBC + (self.RightB - self.RightBC) * y2 + left0 = Point3(0.0, 0.0, left1[2] - left1[0] * (left2[2] - left1[2]) / (left2[0] - left1[0])) + right0 = Point3(0.0, 0.0, right1[2] - right1[0] * (right2[2] - right1[2]) / (right2[0] - right1[0])) + if x < 0.0: + x2 = -x + left = left0 + (left2 - left0) * x2 + right = right0 + (right2 - right0) * x2 + else: + x2 = x + left = left0 + (left1 - left0) * x2 + right = right0 + (right1 - right0) * x2 + self.__lpupil.setPos(left) + self.__rpupil.setPos(right) + + def __lookPupilsAt(self, node, point): + if node != None: + mat = node.getMat(self.__eyes) + point = mat.xformPoint(point) + distance = 1.0 + recip_z = 1.0 / max(0.1, point[1]) + x = distance * point[0] * recip_z + y = distance * point[2] * recip_z + x = min(max(x, -1), 1) + y = min(max(y, -1), 1) + self.__setPupilDirection(x, y) + return + + def __lookHeadAt(self, node, point, frac = 1.0, lod = None): + reachedTarget = 1 + if lod == None: + head = self.getPart('head', self.getLODNames()[0]) + else: + head = self.getPart('head', lod) + if node != None: + headParent = head.getParent() + mat = node.getMat(headParent) + point = mat.xformPoint(point) + rot = Mat3(0, 0, 0, 0, 0, 0, 0, 0, 0) + lookAt(rot, Vec3(point), Vec3(0, 0, 1), CSDefault) + scale = VBase3(0, 0, 0) + hpr = VBase3(0, 0, 0) + if decomposeMatrix(rot, scale, hpr, CSDefault): + hpr = VBase3(min(max(hpr[0], -60), 60), min(max(hpr[1], -20), 30), 0) + if frac != 1: + currentHpr = head.getHpr() + reachedTarget = abs(hpr[0] - currentHpr[0]) < 1.0 and abs(hpr[1] - currentHpr[1]) < 1.0 + hpr = currentHpr + (hpr - currentHpr) * frac + if lod == None: + for lodName in self.getLODNames(): + head = self.getPart('head', lodName) + head.setHpr(hpr) + + else: + head.setHpr(hpr) + return reachedTarget + + def setupEyelashes(self, style): + if style.getGender() == 'm': + if self.__eyelashOpen: + self.__eyelashOpen.removeNode() + self.__eyelashOpen = None + if self.__eyelashClosed: + self.__eyelashClosed.removeNode() + self.__eyelashClosed = None + else: + if self.__eyelashOpen: + self.__eyelashOpen.removeNode() + if self.__eyelashClosed: + self.__eyelashClosed.removeNode() + animal = style.head[0] + model = loader.loadModel('phase_3' + EyelashDict[animal]) + if self.hasLOD(): + head = self.getPart('head', '1000') + else: + head = self.getPart('head', 'lodRoot') + length = style.head[1] + if length == 'l': + openString = 'open-long' + closedString = 'closed-long' + else: + openString = 'open-short' + closedString = 'closed-short' + self.__eyelashOpen = model.find('**/' + openString).copyTo(head) + self.__eyelashClosed = model.find('**/' + closedString).copyTo(head) + model.removeNode() + return + + def __fixHeadLongLong(self, style, lodName = None, copy = 1): + if lodName == None: + searchRoot = self + else: + searchRoot = self.find('**/' + str(lodName)) + otherParts = searchRoot.findAllMatches('**/*short*') + for partNum in xrange(0, otherParts.getNumPaths()): + if copy: + otherParts.getPath(partNum).removeNode() + else: + otherParts.getPath(partNum).stash() + + return + + def __fixHeadLongShort(self, style, lodName = None, copy = 1): + animalType = style.getAnimal() + headStyle = style.head + if lodName == None: + searchRoot = self + else: + searchRoot = self.find('**/' + str(lodName)) + if animalType != 'duck' and animalType != 'horse': + if animalType == 'rabbit': + if copy: + searchRoot.find('**/ears-long').removeNode() + else: + searchRoot.find('**/ears-long').hide() + elif copy: + searchRoot.find('**/ears-short').removeNode() + else: + searchRoot.find('**/ears-short').hide() + if animalType != 'rabbit': + if copy: + searchRoot.find('**/eyes-short').removeNode() + else: + searchRoot.find('**/eyes-short').hide() + if animalType != 'dog': + if copy: + searchRoot.find('**/joint_pupilL_short').removeNode() + searchRoot.find('**/joint_pupilR_short').removeNode() + else: + searchRoot.find('**/joint_pupilL_short').stash() + searchRoot.find('**/joint_pupilR_short').stash() + if copy: + self.find('**/head-short').removeNode() + self.find('**/head-front-short').removeNode() + else: + self.find('**/head-short').hide() + self.find('**/head-front-short').hide() + if animalType != 'rabbit': + muzzleParts = searchRoot.findAllMatches('**/muzzle-long*') + for partNum in xrange(0, muzzleParts.getNumPaths()): + if copy: + muzzleParts.getPath(partNum).removeNode() + else: + muzzleParts.getPath(partNum).hide() + + else: + muzzleParts = searchRoot.findAllMatches('**/muzzle-short*') + for partNum in xrange(0, muzzleParts.getNumPaths()): + if copy: + muzzleParts.getPath(partNum).removeNode() + else: + muzzleParts.getPath(partNum).hide() + + return + + def __fixHeadShortLong(self, style, lodName = None, copy = 1): + animalType = style.getAnimal() + headStyle = style.head + if lodName == None: + searchRoot = self + else: + searchRoot = self.find('**/' + str(lodName)) + if animalType != 'duck' and animalType != 'horse': + if animalType == 'rabbit': + if copy: + searchRoot.find('**/ears-short').removeNode() + else: + searchRoot.find('**/ears-short').hide() + elif copy: + searchRoot.find('**/ears-long').removeNode() + else: + searchRoot.find('**/ears-long').hide() + if animalType != 'rabbit': + if copy: + searchRoot.find('**/eyes-long').removeNode() + else: + searchRoot.find('**/eyes-long').hide() + if animalType != 'dog': + if copy: + searchRoot.find('**/joint_pupilL_long').removeNode() + searchRoot.find('**/joint_pupilR_long').removeNode() + else: + searchRoot.find('**/joint_pupilL_long').stash() + searchRoot.find('**/joint_pupilR_long').stash() + if copy: + searchRoot.find('**/head-long').removeNode() + searchRoot.find('**/head-front-long').removeNode() + else: + searchRoot.find('**/head-long').hide() + searchRoot.find('**/head-front-long').hide() + if animalType != 'rabbit': + muzzleParts = searchRoot.findAllMatches('**/muzzle-short*') + for partNum in xrange(0, muzzleParts.getNumPaths()): + if copy: + muzzleParts.getPath(partNum).removeNode() + else: + muzzleParts.getPath(partNum).hide() + + else: + muzzleParts = searchRoot.findAllMatches('**/muzzle-long*') + for partNum in xrange(0, muzzleParts.getNumPaths()): + if copy: + muzzleParts.getPath(partNum).removeNode() + else: + muzzleParts.getPath(partNum).hide() + + return + + def __fixHeadShortShort(self, style, lodName = None, copy = 1): + if lodName == None: + searchRoot = self + else: + searchRoot = self.find('**/' + str(lodName)) + otherParts = searchRoot.findAllMatches('**/*long*') + for partNum in xrange(0, otherParts.getNumPaths()): + if copy: + otherParts.getPath(partNum).removeNode() + else: + otherParts.getPath(partNum).stash() + + return + + def __blinkOpenEyes(self, task): + if self.eyelids.getCurrentState().getName() == 'closed': + self.eyelids.request('open') + r = self.randGen.random() + if r < 0.1: + t = 0.2 + else: + t = r * 4.0 + 1.0 + taskMgr.doMethodLater(t, self.__blinkCloseEyes, self.__blinkName) + return Task.done + + def __blinkCloseEyes(self, task): + if self.eyelids.getCurrentState().getName() != 'open': + taskMgr.doMethodLater(4.0, self.__blinkCloseEyes, self.__blinkName) + else: + self.eyelids.request('closed') + taskMgr.doMethodLater(0.125, self.__blinkOpenEyes, self.__blinkName) + return Task.done + + def startBlink(self): + taskMgr.remove(self.__blinkName) + if self.__eyes: + self.openEyes() + taskMgr.doMethodLater(self.randGen.random() * 4.0 + 1, self.__blinkCloseEyes, self.__blinkName) + + def stopBlink(self): + taskMgr.remove(self.__blinkName) + if self.__eyes: + self.eyelids.request('open') + + def closeEyes(self): + self.eyelids.request('closed') + + def openEyes(self): + self.eyelids.request('open') + + def surpriseEyes(self): + self.eyelids.request('surprised') + + def sadEyes(self): + self.__eyesOpen = ToonHead.EyesSadOpen + self.__eyesClosed = ToonHead.EyesSadClosed + + def angryEyes(self): + self.__eyesOpen = ToonHead.EyesAngryOpen + self.__eyesClosed = ToonHead.EyesAngryClosed + + def normalEyes(self): + self.__eyesOpen = ToonHead.EyesOpen + self.__eyesClosed = ToonHead.EyesClosed + + def blinkEyes(self): + taskMgr.remove(self.__blinkName) + self.eyelids.request('closed') + taskMgr.doMethodLater(0.1, self.__blinkOpenEyes, self.__blinkName) + + def __stareAt(self, task): + frac = 2 * globalClock.getDt() + reachedTarget = self.__lookHeadAt(self.__stareAtNode, self.__stareAtPoint, frac) + self.__lookPupilsAt(self.__stareAtNode, self.__stareAtPoint) + if reachedTarget and self.__stareAtNode == None: + return Task.done + else: + return Task.cont + return + + def doLookAroundToStareAt(self, node, point): + self.startStareAt(node, point) + self.startLookAround() + + def startStareAtHeadPoint(self, point): + self.startStareAt(self, point) + + def startStareAt(self, node, point): + taskMgr.remove(self.__stareAtName) + if self.lookAtTrack: + self.lookAtTrack.finish() + self.lookAtTrack = None + self.__stareAtNode = node + if point != None: + self.__stareAtPoint = point + else: + self.__stareAtPoint = self.__defaultStarePoint + self.__stareAtTime = globalClock.getFrameTime() + taskMgr.add(self.__stareAt, self.__stareAtName) + return + + def lerpLookAt(self, point, time = 1.0, blink = 0): + taskMgr.remove(self.__stareAtName) + if self.lookAtTrack: + self.lookAtTrack.finish() + self.lookAtTrack = None + lodNames = self.getLODNames() + if lodNames: + lodName = lodNames[0] + else: + return 0 + head = self.getPart('head', lodName) + startHpr = head.getHpr() + startLpupil = self.__lpupil.getPos() + startRpupil = self.__rpupil.getPos() + self.__lookHeadAt(None, point, lod=lodName) + self.__lookPupilsAt(None, point) + endHpr = head.getHpr() + endLpupil = self.__lpupil.getPos() * 0.5 + endRpupil = self.__rpupil.getPos() * 0.5 + head.setHpr(startHpr) + self.__lpupil.setPos(startLpupil) + self.__rpupil.setPos(startRpupil) + if startHpr.almostEqual(endHpr, 10): + return 0 + if blink: + self.blinkEyes() + lookToTgt_TimeFraction = 0.2 + lookToTgtTime = time * lookToTgt_TimeFraction + returnToEyeCenterTime = time - lookToTgtTime - 0.5 + origin = Point3(0, 0, 0) + blendType = 'easeOut' + self.lookAtTrack = Parallel(Sequence(LerpPosInterval(self.__lpupil, lookToTgtTime, endLpupil, blendType=blendType), Wait(0.5), LerpPosInterval(self.__lpupil, returnToEyeCenterTime, origin, blendType=blendType)), Sequence(LerpPosInterval(self.__rpupil, lookToTgtTime, endRpupil, blendType=blendType), Wait(0.5), LerpPosInterval(self.__rpupil, returnToEyeCenterTime, origin, blendType=blendType)), name=self.__stareAtName) + for lodName in self.getLODNames(): + head = self.getPart('head', lodName) + self.lookAtTrack.append(LerpHprInterval(head, time, endHpr, blendType='easeInOut')) + + self.lookAtTrack.start() + return 1 + + def stopStareAt(self): + self.lerpLookAt(Vec3.forward()) + + def stopStareAtNow(self): + taskMgr.remove(self.__stareAtName) + if self.lookAtTrack: + self.lookAtTrack.finish() + self.lookAtTrack = None + if self.__lpupil and self.__rpupil: + self.__setPupilDirection(0, 0) + for lodName in self.getLODNames(): + head = self.getPart('head', lodName) + head.setHpr(0, 0, 0) + + return + + def __lookAround(self, task): + self.findSomethingToLookAt() + t = self.randGen.random() * 4.0 + 3.0 + taskMgr.doMethodLater(t, self.__lookAround, self.__lookName) + return Task.done + + def startLookAround(self): + taskMgr.remove(self.__lookName) + t = self.randGen.random() * 5.0 + 2.0 + taskMgr.doMethodLater(t, self.__lookAround, self.__lookName) + + def stopLookAround(self): + taskMgr.remove(self.__lookName) + self.stopStareAt() + + def stopLookAroundNow(self): + taskMgr.remove(self.__lookName) + self.stopStareAtNow() + + def enterEyelidsOff(self): + pass + + def exitEyelidsOff(self): + pass + + def enterEyelidsOpen(self): + if not self.__eyes.isEmpty(): + self.__eyes.setTexture(self.__eyesOpen, 1) + if self.__eyelashOpen: + self.__eyelashOpen.show() + if self.__eyelashClosed: + self.__eyelashClosed.hide() + if self.__lod500Eyes: + self.__lod500Eyes.setTexture(self.__eyesOpen, 1) + if self.__lod250Eyes: + self.__lod250Eyes.setTexture(self.__eyesOpen, 1) + if self.__lpupil: + self.__lpupil.show() + self.__rpupil.show() + if self.__lod500lPupil: + self.__lod500lPupil.show() + self.__lod500rPupil.show() + if self.__lod250lPupil: + self.__lod250lPupil.show() + self.__lod250rPupil.show() + + def exitEyelidsOpen(self): + pass + + def enterEyelidsClosed(self): + if not self.__eyes.isEmpty() and self.__eyesClosed: + self.__eyes.setTexture(self.__eyesClosed, 1) + if self.__eyelashOpen: + self.__eyelashOpen.hide() + if self.__eyelashClosed: + self.__eyelashClosed.show() + if self.__lod500Eyes: + self.__lod500Eyes.setTexture(self.__eyesClosed, 1) + if self.__lod250Eyes: + self.__lod250Eyes.setTexture(self.__eyesClosed, 1) + if self.__lpupil: + self.__lpupil.hide() + self.__rpupil.hide() + if self.__lod500lPupil: + self.__lod500lPupil.hide() + self.__lod500rPupil.hide() + if self.__lod250lPupil: + self.__lod250lPupil.hide() + self.__lod250rPupil.hide() + + def exitEyelidsClosed(self): + pass + + def enterEyelidsSurprised(self): + if not self.__eyes.isEmpty() and ToonHead.EyesSurprised: + self.__eyes.setTexture(ToonHead.EyesSurprised, 1) + if self.__eyelashOpen: + self.__eyelashOpen.hide() + if self.__eyelashClosed: + self.__eyelashClosed.hide() + if self.__lod500Eyes: + self.__lod500Eyes.setTexture(ToonHead.EyesSurprised, 1) + if self.__lod250Eyes: + self.__lod250Eyes.setTexture(ToonHead.EyesSurprised, 1) + if self.__muzzle: + self.__muzzle.setTexture(ToonHead.MuzzleSurprised, 1) + if self.__lpupil: + self.__lpupil.show() + self.__rpupil.show() + if self.__lod500lPupil: + self.__lod500lPupil.show() + self.__lod500rPupil.show() + if self.__lod250lPupil: + self.__lod250lPupil.show() + self.__lod250rPupil.show() + + def exitEyelidsSurprised(self): + if self.__muzzle: + self.__muzzle.setTexture(ToonHead.Muzzle, 1) + + def setupMuzzles(self, style): + self.__muzzles = [] + self.__surpriseMuzzles = [] + self.__angryMuzzles = [] + self.__sadMuzzles = [] + self.__smileMuzzles = [] + self.__laughMuzzles = [] + + def hideAddNonEmptyItemToList(item, list): + if not item.isEmpty(): + item.hide() + list.append(item) + + def hideNonEmptyItem(item): + if not item.isEmpty(): + item.hide() + + if self.hasLOD(): + for lodName in self.getLODNames(): + animal = style.getAnimal() + if animal != 'dog': + muzzle = self.find('**/' + lodName + '/**/muzzle*neutral') + else: + muzzle = self.find('**/' + lodName + '/**/muzzle*') + if lodName == '1000' or lodName == '500': + filePrefix = DogMuzzleDict[style.head] + muzzles = loader.loadModel('phase_3' + filePrefix + lodName) + if base.config.GetBool('want-new-anims', 1): + if not self.find('**/' + lodName + '/**/__Actor_head/def_head').isEmpty(): + muzzles.reparentTo(self.find('**/' + lodName + '/**/__Actor_head/def_head')) + else: + muzzles.reparentTo(self.find('**/' + lodName + '/**/joint_toHead')) + elif self.find('**/' + lodName + '/**/joint_toHead'): + muzzles.reparentTo(self.find('**/' + lodName + '/**/joint_toHead')) + surpriseMuzzle = self.find('**/' + lodName + '/**/muzzle*surprise') + angryMuzzle = self.find('**/' + lodName + '/**/muzzle*angry') + sadMuzzle = self.find('**/' + lodName + '/**/muzzle*sad') + smileMuzzle = self.find('**/' + lodName + '/**/muzzle*smile') + laughMuzzle = self.find('**/' + lodName + '/**/muzzle*laugh') + self.__muzzles.append(muzzle) + hideAddNonEmptyItemToList(surpriseMuzzle, self.__surpriseMuzzles) + hideAddNonEmptyItemToList(angryMuzzle, self.__angryMuzzles) + hideAddNonEmptyItemToList(sadMuzzle, self.__sadMuzzles) + hideAddNonEmptyItemToList(smileMuzzle, self.__smileMuzzles) + hideAddNonEmptyItemToList(laughMuzzle, self.__laughMuzzles) + + else: + if style.getAnimal() != 'dog': + muzzle = self.find('**/muzzle*neutral') + else: + muzzle = self.find('**/muzzle*') + filePrefix = DogMuzzleDict[style.head] + muzzles = loader.loadModel('phase_3' + filePrefix + '1000') + if base.config.GetBool('want-new-anims', 1): + if not self.find('**/def_head').isEmpty(): + muzzles.reparentTo(self.find('**/def_head')) + else: + muzzles.reparentTo(self.find('**/joint_toHead')) + else: + muzzles.reparentTo(self.find('**/joint_toHead')) + surpriseMuzzle = self.find('**/muzzle*surprise') + angryMuzzle = self.find('**/muzzle*angry') + sadMuzzle = self.find('**/muzzle*sad') + smileMuzzle = self.find('**/muzzle*smile') + laughMuzzle = self.find('**/muzzle*laugh') + self.__muzzles.append(muzzle) + hideAddNonEmptyItemToList(surpriseMuzzle, self.__surpriseMuzzles) + hideAddNonEmptyItemToList(angryMuzzle, self.__angryMuzzles) + hideAddNonEmptyItemToList(sadMuzzle, self.__sadMuzzles) + hideAddNonEmptyItemToList(smileMuzzle, self.__smileMuzzles) + hideAddNonEmptyItemToList(laughMuzzle, self.__laughMuzzles) + + def getMuzzles(self): + return self.__muzzles + + def getSurpriseMuzzles(self): + return self.__surpriseMuzzles + + def getAngryMuzzles(self): + return self.__angryMuzzles + + def getSadMuzzles(self): + return self.__sadMuzzles + + def getSmileMuzzles(self): + return self.__smileMuzzles + + def getLaughMuzzles(self): + return self.__laughMuzzles + + def showNormalMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in xrange(len(self.__muzzles)): + self.__muzzles[muzzleNum].show() + + def hideNormalMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in xrange(len(self.__muzzles)): + self.__muzzles[muzzleNum].hide() + + def showAngryMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in xrange(len(self.__angryMuzzles)): + self.__angryMuzzles[muzzleNum].show() + self.__muzzles[muzzleNum].hide() + + def hideAngryMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in xrange(len(self.__angryMuzzles)): + self.__angryMuzzles[muzzleNum].hide() + self.__muzzles[muzzleNum].show() + + def showSadMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in xrange(len(self.__sadMuzzles)): + self.__sadMuzzles[muzzleNum].show() + self.__muzzles[muzzleNum].hide() + + def hideSadMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in xrange(len(self.__sadMuzzles)): + self.__sadMuzzles[muzzleNum].hide() + self.__muzzles[muzzleNum].show() + + def showSmileMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in xrange(len(self.__smileMuzzles)): + self.__smileMuzzles[muzzleNum].show() + self.__muzzles[muzzleNum].hide() + + def hideSmileMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in xrange(len(self.__smileMuzzles)): + self.__smileMuzzles[muzzleNum].hide() + self.__muzzles[muzzleNum].show() + + def showLaughMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in xrange(len(self.__laughMuzzles)): + self.__laughMuzzles[muzzleNum].show() + self.__muzzles[muzzleNum].hide() + + def hideLaughMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in xrange(len(self.__laughMuzzles)): + self.__laughMuzzles[muzzleNum].hide() + self.__muzzles[muzzleNum].show() + + def showSurpriseMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in xrange(len(self.__surpriseMuzzles)): + self.__surpriseMuzzles[muzzleNum].show() + self.__muzzles[muzzleNum].hide() + + def hideSurpriseMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in xrange(len(self.__surpriseMuzzles)): + self.__surpriseMuzzles[muzzleNum].hide() + self.__muzzles[muzzleNum].show() + + def isIgnoreCheesyEffect(self): + if hasattr(self, 'savedCheesyEffect'): + if self.savedCheesyEffect == 10 or self.savedCheesyEffect == 11 or self.savedCheesyEffect == 12 or self.savedCheesyEffect == 13 or self.savedCheesyEffect == 14: + return True + return False diff --git a/toontown/toon/ToonHeadFrame.py b/toontown/toon/ToonHeadFrame.py new file mode 100755 index 00000000..e384e97e --- /dev/null +++ b/toontown/toon/ToonHeadFrame.py @@ -0,0 +1,63 @@ +from direct.gui.DirectGui import * +from panda3d.core import * + +import ToonHead +from toontown.distributed import DelayDelete +from otp.nametag.Nametag import Nametag +from otp.nametag.NametagFloat2d import NametagFloat2d +from toontown.toonbase import ToontownGlobals + + +class ToonHeadFrame(DirectFrame): + + def __init__(self, av, color = ToontownGlobals.GlobalDialogColor, g = DGG.getDefaultDialogGeom()): + DirectFrame.__init__(self, relief=None, geom=g, geom_color=color, geom_scale=(1, 1, 0.5), pos=(0, 0, 0)) + self.initialiseoptions(ToonHeadFrame) + self.av = av + self.avKeep = DelayDelete.DelayDelete(av, 'ToonHeadFrame.avKeep') + self.head = self.stateNodePath[0].attachNewNode('head', 20) + self.head.setPosHprScale(-0.27, 10.0, -0.09, 180.0, 0.0, 0.0, 0.2, 0.2, 0.2) + self.headModel = ToonHead.ToonHead() + self.headModel.startBlink() + self.headModel.setupHead(self.av.style, forGui=1) + self.headModel.reparentTo(self.head) + self.tag1Node = NametagFloat2d() + self.tag1Node.setContents(Nametag.CSpeech | Nametag.CThought) + self.av.nametag.addNametag(self.tag1Node) + self.tag1 = self.attachNewNode(self.tag1Node) + self.tag1.setPosHprScale(-0.16, 0, -0.09, 0, 0, 0, 0.055, 0.055, 0.055) + self.tag2Node = NametagFloat2d() + self.tag2Node.setContents(Nametag.CName) + self.av.nametag.addNametag(self.tag2Node) + self.tag2 = self.attachNewNode(self.tag2Node) + self.tag2.setPosHprScale(-0.27, 10.0, 0.16, 0, 0, 0, 0.05, 0.05, 0.05) + self.extraData = DirectLabel(parent=self, relief=None, pos=(0.0, 0.0, 0.06), scale=1.0, text='', text0_fg=(0.3, 0.2, 1, 1), text_scale=(0.14, 0.06), text_pos=(0, -0.01)) + self.extraData.hide() + + def destroy(self): + DirectFrame.destroy(self) + self.headModel.delete() + del self.headModel + self.head.removeNode() + del self.head + if not self.av.isEmpty(): + self.av.nametag.removeNametag(self.tag1Node) + self.av.nametag.removeNametag(self.tag2Node) + self.tag1.removeNode() + self.tag2.removeNode() + del self.tag1 + del self.tag2 + del self.tag1Node + del self.tag2Node + del self.av + if self.avKeep: + self.avKeep.destroy() + del self.avKeep + self.extraData.removeNode() + del self.extraData + + def removeAvKeep(self): + if hasattr(self, 'avKeep') and self.avKeep: + self.avKeep.destroy() + self.avKeep = None + return diff --git a/toontown/toon/ToonTeleportPanel.py b/toontown/toon/ToonTeleportPanel.py new file mode 100755 index 00000000..43005fc4 --- /dev/null +++ b/toontown/toon/ToonTeleportPanel.py @@ -0,0 +1,293 @@ +from panda3d.core import * +from direct.gui.DirectGui import * +from toontown.toonbase import ToontownGlobals +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +from otp.avatar.Avatar import teleportNotify +import ToonAvatarDetailPanel +from toontown.toonbase import TTLocalizer +from toontown.hood import ZoneUtil +globalTeleport = None + +def showTeleportPanel(avId, avName, avDisableName): + global globalTeleport + if globalTeleport != None: + globalTeleport.cleanup() + globalTeleport = None + globalTeleport = ToonTeleportPanel(avId, avName, avDisableName) + return + + +def hideTeleportPanel(): + global globalTeleport + if globalTeleport != None: + globalTeleport.cleanup() + globalTeleport = None + return + + +def unloadTeleportPanel(): + global globalTeleport + if globalTeleport != None: + globalTeleport.cleanup() + globalTeleport = None + return + + +class ToonTeleportPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonTeleportPanel') + + def __init__(self, avId, avName, avDisableName): + DirectFrame.__init__(self, pos=(-1.01, 0.1, -0.35), parent=base.a2dTopRight, image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.6), text='', text_wordwrap=13.5, text_scale=0.06, text_pos=(0.0, 0.18)) + messenger.send('releaseDirector') + self['image'] = DGG.getDefaultDialogGeom() + self.avId = avId + self.avName = avName + self.avDisableName = avDisableName + self.fsm = ClassicFSM.ClassicFSM('ToonTeleportPanel', [ + State.State('off', + self.enterOff, + self.exitOff), + State.State('begin', + self.enterBegin, + self.exitBegin), + State.State('checkAvailability', + self.enterCheckAvailability, + self.exitCheckAvailability), + State.State('notAvailable', + self.enterNotAvailable, + self.exitNotAvailable), + State.State('ignored', + self.enterIgnored, + self.exitIgnored), + State.State('noTeleport', + self.enterNoTeleport, + self.exitNoTeleport), + State.State('notOnline', + self.enterNotOnline, + self.exitNotOnline), + State.State('wentAway', + self.enterWentAway, + self.exitWentAway), + State.State('self', + self.enterSelf, + self.exitSelf), + State.State('unknownHood', + self.enterUnknownHood, + self.exitUnknownHood), + State.State('otherShard', + self.enterOtherShard, + self.exitOtherShard), + State.State('teleport', + self.enterTeleport, + self.exitTeleport)], + 'off', 'off') + from toontown.friends import FriendInviter + FriendInviter.hideFriendInviter() + ToonAvatarDetailPanel.hideAvatarDetail() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.bOk = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.TeleportPanelOK, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, 0.0, -0.1), command=self.__handleOk) + self.bOk.hide() + self.bCancel = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=TTLocalizer.TeleportPanelCancel, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, 0.0, -0.1), command=self.__handleCancel) + self.bCancel.hide() + self.bYes = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.TeleportPanelYes, text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.15, 0.0, -0.15), command=self.__handleYes) + self.bYes.hide() + self.bNo = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=TTLocalizer.TeleportPanelNo, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.15, 0.0, -0.15), command=self.__handleNo) + self.bNo.hide() + buttons.removeNode() + self.accept(self.avDisableName, self.__handleDisableAvatar) + self.show() + self.fsm.enterInitialState() + self.fsm.request('begin') + return + + def cleanup(self): + self.fsm.request('off') + del self.fsm + self.ignore(self.avDisableName) + self.destroy() + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterBegin(self): + myId = base.localAvatar.doId + if self.avId == myId: + self.fsm.request('self') + elif self.avId in base.cr.doId2do: + self.fsm.request('checkAvailability') + elif base.cr.isFriend(self.avId): + if base.cr.isFriendOnline(self.avId): + self.fsm.request('checkAvailability') + else: + self.fsm.request('notOnline') + else: + self.fsm.request('wentAway') + + def exitBegin(self): + pass + + def enterCheckAvailability(self): + myId = base.localAvatar.getDoId() + base.cr.ttsFriendsManager.d_teleportQuery(self.avId) + self['text'] = TTLocalizer.TeleportPanelCheckAvailability % self.avName + self.accept('teleportResponse', self.__teleportResponse) + self.bCancel.show() + + def exitCheckAvailability(self): + self.ignore('teleportResponse') + self.bCancel.hide() + + def enterNotAvailable(self): + self['text'] = TTLocalizer.TeleportPanelNotAvailable % self.avName + self.bOk.show() + + def exitNotAvailable(self): + self.bOk.hide() + + def enterIgnored(self): + self['text'] = TTLocalizer.TeleportPanelIgnored % self.avName + self.bOk.show() + + def exitIgnored(self): + self.bOk.hide() + + def enterNoTeleport(self): + self['text'] = TTLocalizer.TeleportPanelNoTeleport % self.avName + self.bOk.show() + + def exitNoTeleport(self): + self.bOk.hide() + + def enterNotOnline(self): + self['text'] = TTLocalizer.TeleportPanelNotOnline % self.avName + self.bOk.show() + + def exitNotOnline(self): + self.bOk.hide() + + def enterWentAway(self): + self['text'] = TTLocalizer.TeleportPanelWentAway % self.avName + self.bOk.show() + + def exitWentAway(self): + self.bOk.hide() + + def enterUnknownHood(self, hoodId): + self['text'] = TTLocalizer.TeleportPanelUnknownHood % base.cr.hoodMgr.getFullnameFromId(hoodId) + self.bOk.show() + + def exitUnknownHood(self): + self.bOk.hide() + + def enterSelf(self): + self['text'] = TTLocalizer.TeleportPanelDenySelf + self.bOk.show() + + def exitSelf(self): + self.bOk.hide() + + def enterOtherShard(self, shardId, hoodId, zoneId): + shardName = base.cr.getShardName(shardId) + if shardName is None: + self.fsm.request('notAvailable') + return + myShardName = base.cr.getShardName(base.localAvatar.defaultShard) + pop = None + for shard in base.cr.listActiveShards(): + if shard[0] == shardId: + pop = shard[2] + + self.bYes.show() + self.bNo.show() + + if pop and pop > localAvatar.shardPage.midPop: + self.notify.warning('Entering full shard: issuing performance warning') + self['text'] = TTLocalizer.TeleportPanelBusyShard % {'avName': self.avName} + self.bYes.hide() + self.bNo.hide() + + self.bOk.show() + else: + self['text'] = TTLocalizer.TeleportPanelOtherShard % {'avName': self.avName, + 'shardName': shardName, + 'myShardName': myShardName} + + self.shardId = shardId + self.hoodId = hoodId + self.zoneId = zoneId + + def exitOtherShard(self): + self.bYes.hide() + self.bNo.hide() + + def enterTeleport(self, shardId, hoodId, zoneId): + shardName = base.cr.getShardName(shardId) + if shardName is None: + shardName = 'unknown' + hoodsVisited = base.localAvatar.hoodsVisited + canonicalHoodId = ZoneUtil.getCanonicalZoneId(hoodId) + if hoodId == ToontownGlobals.MyEstate: + teleportNotify.debug('enterTeleport: estate') + if shardId == base.localAvatar.defaultShard: + shardId = None + place = base.cr.playGame.getPlace() + place.requestTeleport(hoodId, zoneId, shardId, self.avId) + unloadTeleportPanel() + elif canonicalHoodId not in hoodsVisited + ToontownGlobals.HoodsAlwaysVisited: + teleportNotify.debug('enterTeleport: unknownHood') + self.fsm.request('unknownHood', [hoodId]) + else: + if shardId == base.localAvatar.defaultShard: + shardId = None + teleportNotify.debug('enterTeleport: requesting teleport') + place = base.cr.playGame.getPlace() + place.requestTeleport(hoodId, zoneId, shardId, self.avId) + unloadTeleportPanel() + return + + def exitTeleport(self): + pass + + def __handleOk(self): + unloadTeleportPanel() + + def __handleCancel(self): + unloadTeleportPanel() + + def __handleYes(self): + self.fsm.request('teleport', [self.shardId, self.hoodId, self.zoneId]) + + def __handleNo(self): + unloadTeleportPanel() + + def __teleportResponse(self, avId, available, shardId, hoodId, zoneId): + teleportNotify.debug('__teleportResponse%s' % ((avId, + available, + shardId, + hoodId, + zoneId),)) + if avId != self.avId: + return + if available == 0: + teleportNotify.debug('__teleportResponse: not available') + self.fsm.request('notAvailable') + elif available == 2: + teleportNotify.debug('__teleportResponse: ignored') + self.fsm.request('ignored') + elif available == 3: + self.fsm.request('noTeleport') + elif shardId != base.localAvatar.defaultShard: + teleportNotify.debug('__teleportResponse: otherShard') + self.fsm.request('otherShard', [shardId, hoodId, zoneId]) + else: + teleportNotify.debug('__teleportResponse: teleport') + self.fsm.request('teleport', [shardId, hoodId, zoneId]) + + def __handleDisableAvatar(self): + self.fsm.request('wentAway') diff --git a/toontown/toon/__init__.py b/toontown/toon/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/toonbase/BitmaskGlobals.py b/toontown/toonbase/BitmaskGlobals.py new file mode 100755 index 00000000..3aaf0385 --- /dev/null +++ b/toontown/toonbase/BitmaskGlobals.py @@ -0,0 +1,23 @@ +from pandac.PandaModules import BitMask32 + +WallBitmask = BitMask32(1) +FloorBitmask = BitMask32(2) +CameraBitmask = BitMask32(4) +CameraTransparentBitmask = BitMask32(8) +SafetyNetBitmask = BitMask32(512) +SafetyGateBitmask = BitMask32(1024) +GhostBitmask = BitMask32(2048) +PathFindingBitmask = BitMask32.bit(29) +PickerBitmask = BitMask32(4096) +CeilingBitmask = BitMask32(256) +FloorEventBitmask = BitMask32(16) +PieBitmask = BitMask32(256) +PetBitmask = BitMask32(8) +CatchGameBitmask = BitMask32(16) +CashbotBossObjectBitmask = BitMask32(16) +FurnitureSideBitmask = BitMask32(32) +FurnitureTopBitmask = BitMask32(64) +FurnitureDragBitmask = BitMask32(128) +PetLookatPetBitmask = BitMask32(256) +PetLookatNonPetBitmask = BitMask32(512) +BanquetTableBitmask = BitMask32(1024) diff --git a/toontown/toonbase/PythonUtil.py b/toontown/toonbase/PythonUtil.py new file mode 100644 index 00000000..a6f1f079 --- /dev/null +++ b/toontown/toonbase/PythonUtil.py @@ -0,0 +1,101 @@ +import bisect, sys + +""" +This file contains functions that were originally included in +Panda3D but were removed due to being obsolete. These are required +by Toontown, and probably cannot be replaced by anything else that +is still in Panda3D. +""" + +class PriorityCallbacks: + """ manage a set of prioritized callbacks, and allow them to be invoked in order of priority """ + def __init__(self): + self._callbacks = [] + + def clear(self): + while self._callbacks: + self._callbacks.pop() + + def add(self, callback, priority=None): + if priority is None: + priority = 0 + item = (priority, callback) + bisect.insort(self._callbacks, item) + return item + + def remove(self, item): + self._callbacks.pop(bisect.bisect_left(self._callbacks, item)) + + def __call__(self): + for priority, callback in self._callbacks: + callback() + +def clampScalar(value, a, b): + # calling this ought to be faster than calling both min and max + if a < b: + if value < a: + return a + elif value > b: + return b + else: + return value + else: + if value < b: + return b + elif value > a: + return a + else: + return value + +def describeException(backTrace = 4): + # When called in an exception handler, returns a string describing + # the current exception. + + def byteOffsetToLineno(code, byte): + # Returns the source line number corresponding to the given byte + # offset into the indicated Python code module. + + import array + lnotab = array.array('B', code.co_lnotab) + + line = code.co_firstlineno + for i in xrange(0, len(lnotab), 2): + byte -= lnotab[i] + if byte <= 0: + return line + line += lnotab[i+1] + + return line + + infoArr = sys.exc_info() + exception = infoArr[0] + exceptionName = getattr(exception, '__name__', None) + extraInfo = infoArr[1] + trace = infoArr[2] + + stack = [] + while trace.tb_next: + # We need to call byteOffsetToLineno to determine the true + # line number at which the exception occurred, even though we + # have both trace.tb_lineno and frame.f_lineno, which return + # the correct line number only in non-optimized mode. + frame = trace.tb_frame + module = frame.f_globals.get('__name__', None) + lineno = byteOffsetToLineno(frame.f_code, frame.f_lasti) + stack.append("%s:%s, " % (module, lineno)) + trace = trace.tb_next + + frame = trace.tb_frame + module = frame.f_globals.get('__name__', None) + lineno = byteOffsetToLineno(frame.f_code, frame.f_lasti) + stack.append("%s:%s, " % (module, lineno)) + + description = "" + for i in xrange(len(stack) - 1, max(len(stack) - backTrace, 0) - 1, -1): + description += stack[i] + + description += "%s: %s" % (exceptionName, extraInfo) + return description + +import __builtin__ +__builtin__.describeException = describeException diff --git a/toontown/toonbase/TTLocalizer.py b/toontown/toonbase/TTLocalizer.py new file mode 100755 index 00000000..1798f6e2 --- /dev/null +++ b/toontown/toonbase/TTLocalizer.py @@ -0,0 +1,38 @@ +import string +import types + +try: + language = settings['language'] +except: + language = 'English' + +def getLanguage(): + return language + +print 'TTLocalizer: Running in language: %s' % language +from toontown.toonbase.TTLocalizerEnglish import * + +if language != 'English': + l = {} + g = {} + module = 'toontown.toonbase.TTLocalizer' + language + englishModule = __import__('toontown.toonbase.TTLocalizerEnglish', g, l) + foreignModule = __import__(module, g, l) + for key, val in englishModule.__dict__.items(): + if key not in foreignModule.__dict__: + print 'WARNING: Foreign module: %s missing key: %s' % (module, key) + locals()[key] = val + elif isinstance(val, types.DictType): + fval = foreignModule.__dict__.get(key) + for dkey, dval in val.items(): + if dkey not in fval: + print 'WARNING: Foreign module: %s missing key: %s.%s' % (module, key, dkey) + fval[dkey] = dval + + for dkey in fval.keys(): + if dkey not in val: + print 'WARNING: Foreign module: %s extra key: %s.%s' % (module, key, dkey) + + for key in foreignModule.__dict__.keys(): + if key not in englishModule.__dict__: + print 'WARNING: Foreign module: %s extra key: %s' % (module, key) diff --git a/toontown/toonbase/TTLocalizerEnglish.py b/toontown/toonbase/TTLocalizerEnglish.py new file mode 100755 index 00000000..dba7eefa --- /dev/null +++ b/toontown/toonbase/TTLocalizerEnglish.py @@ -0,0 +1,10029 @@ +from toontown.toonbase.TTLocalizerEnglishProperty import * +from toontown.catalog import CatalogAccessoryItemGlobals +from otp.otpbase import OTPLocalizer as OL +import random +OL.SpeedChatStaticText = OL.SpeedChatStaticTextToontown.copy() +for key in OL.SpeedChatStaticTextCommon.iterkeys(): + OL.SpeedChatStaticText[key] = OL.SpeedChatStaticTextCommon[key] + +commitmantst = 'kptmptest - removable' +InterfaceFont = 'phase_3/models/fonts/ImpressBT.ttf' +ToonFont = 'phase_3/models/fonts/ImpressBT.ttf' +SuitFont = 'phase_3/models/fonts/vtRemingtonPortable.ttf' +SignFont = 'phase_3/models/fonts/MickeyFont' +MinnieFont = 'phase_3/models/fonts/MinnieFont' +ChalkFont = 'phase_3/models/fonts/Chawp.ttf' +BuildingNametagFont = 'phase_3/models/fonts/MickeyFont' +BuildingNametagShadow = None +NametagFonts = ( + 'phase_3/models/fonts/ImpressBT.ttf', + 'phase_3/models/fonts/AnimGothic.bam', + 'phase_3/models/fonts/Aftershock.bam', + 'phase_3/models/fonts/JiggeryPokery.bam', + 'phase_3/models/fonts/Ironwork.bam', + 'phase_3/models/fonts/HastyPudding.bam', + 'phase_3/models/fonts/Comedy.bam', + 'phase_3/models/fonts/Humanist.bam', + 'phase_3/models/fonts/Portago.bam', + 'phase_3/models/fonts/Musicals.bam', + 'phase_3/models/fonts/Scurlock.bam', + 'phase_3/models/fonts/Danger.bam', + 'phase_3/models/fonts/Alie.bam', + 'phase_3/models/fonts/OysterBar.bam', + 'phase_3/models/fonts/RedDogSaloon.bam', + 'phase_3/models/fonts/PBN.ttf', + 'phase_3/models/fonts/SomethingStrange.ttf', + 'phase_3/models/fonts/DinosaursAreAlive.ttf' +) +NametagFontNames = ( + 'Basic', + 'Plain', + 'Shivering', + 'Wonky', + 'Fancy', + 'Silly', + 'Zany', + 'Practical', + 'Nautical', + 'Whimsical', + 'Spooky', + 'Action', + 'Poetic', + 'Boardwalk', + 'Western', + 'Pixelated', + 'Metal', + 'Dinosaurs' +) +NametagLabel = ' Nametag' +ScreenshotPath = 'user/screenshots/' +Flippy = 'Flippy' +lTheBrrrgh = 'The Brrrgh' +lDaisyGardens = 'Daisy Gardens' +lDonaldsDock = "Donald's Dock" +lDonaldsDreamland = "Donald's Dreamland" +lMinniesMelodyland = "Minnie's Melodyland" +lToontownCentral = 'Toontown Central' +lToonHQ = 'Toon HQ' +lSellbotHQ = 'Sellbot HQ' +lGoofySpeedway = 'Goofy Speedway' +lOutdoorZone = "Chip 'n Dale's Acorn Acres" +lGolfZone = "Chip 'n Dale's MiniGolf" +lPartyHood = 'Party Grounds' +GlobalStreetNames = {20000: ('to', 'on', 'Tutorial Terrace'), + 1000: ('to the', 'in the', 'Playground'), + 1100: ('to', 'on', 'Barnacle Boulevard'), + 1200: ('to', 'on', 'Seaweed Street'), + 1300: ('to', 'on', 'Lighthouse Lane'), + 2000: ('to the', 'in the', 'Playground'), + 2100: ('to', 'on', 'Silly Street'), + 2200: ('to', 'on', 'Loopy Lane'), + 2300: ('to', 'on', 'Punchline Place'), + 3000: ('to the', 'in the', 'Playground'), + 3100: ('to', 'on', 'Walrus Way'), + 3200: ('to', 'on', 'Sleet Street'), + 3300: ('to', 'on', 'Polar Place'), + 4000: ('to the', 'in the', 'Playground'), + 4100: ('to', 'on', 'Alto Avenue'), + 4200: ('to', 'on', 'Baritone Boulevard'), + 4300: ('to', 'on', 'Tenor Terrace'), + 5000: ('to the', 'in the', 'Playground'), + 5100: ('to', 'on', 'Elm Street'), + 5200: ('to', 'on', 'Maple Street'), + 5300: ('to', 'on', 'Oak Street'), + 9000: ('to the', 'in the', 'Playground'), + 9100: ('to', 'on', 'Lullaby Lane'), + 9200: ('to', 'on', 'Pajama Place'), + 9300: ('to', 'on', 'Bedtime Boulevard'), + 10000: ('to the', 'in the', 'Bossbot HQ Country Club'), + 10100: ('to the', 'in the', 'Bossbot HQ Lobby'), + 10200: ('to', 'in', 'The Clubhouse'), + 10500: ('to', 'in', 'The Front Three'), + 10600: ('to', 'in', 'The Middle Six'), + 10700: ('to', 'in', 'The Back Nine'), + 11000: ('to the', 'in the', 'Sellbot HQ Courtyard'), + 11100: ('to the', 'in the', 'Sellbot HQ Lobby'), + 11200: ('to the', 'in the', 'Sellbot Factory'), + 11500: ('to the', 'in the', 'Sellbot Factory'), + 11600: ('to the', 'in the', 'Sellbot Megacorp'), + 12000: ('to the', 'in the', 'Cashbot Train Yard'), + 12100: ('to the', 'in the', 'Cashbot HQ Lobby'), + 12500: ('to the', 'in the', 'Cashbot Coin Mint'), + 12600: ('to the', 'in the', 'Cashbot Dollar Mint'), + 12700: ('to the', 'in the', 'Cashbot Bullion Mint'), + 13000: ('to the', 'in the', 'Lawbot HQ Courtyard'), + 13100: ('to the', 'in the', 'Courthouse Lobby'), + 13200: ('to the', 'in the', "DA's Office Lobby"), + 13300: ('to the', 'in the', 'Lawbot A Office'), + 13400: ('to the', 'in the', 'Lawbot B Office'), + 13500: ('to the', 'in the', 'Lawbot C Office'), + 13600: ('to the', 'in the', 'Lawbot D Office')} +DonaldsDock = ('to', 'in', lDonaldsDock) +ToontownCentral = ('to', 'in', lToontownCentral) +TheBrrrgh = ('to', 'in', lTheBrrrgh) +MinniesMelodyland = ('to', 'in', lMinniesMelodyland) +DaisyGardens = ('to', 'in', lDaisyGardens) +OutdoorZone = ('to', 'in', lOutdoorZone) +FunnyFarm = ('to the', 'in the', 'Funny Farm') +GoofySpeedway = ('to', 'in', lGoofySpeedway) +DonaldsDreamland = ('to', 'in', lDonaldsDreamland) +BossbotHQ = ('to', 'in', 'Bossbot HQ') +SellbotHQ = ('to', 'in', 'Sellbot HQ') +CashbotHQ = ('to', 'in', 'Cashbot HQ') +LawbotHQ = ('to', 'in', 'Lawbot HQ') +Tutorial = ('to the', 'in the', 'Toon-torial') +MyEstate = ('to', 'in', 'your house') +GolfZone = ('to', 'in', lGolfZone) +PartyHood = ('to the', 'in the', lPartyHood) +Factory = 'Factory' +Headquarters = 'Headquarters' +SellbotFrontEntrance = 'Front Entrance' +SellbotSideEntrance = 'Side Entrance' +Office = 'Office' +FactoryNames = {11500: 'Sellbot Cog Factory', + 11600: 'Sellbot Cog Megacorp', + 13300: 'Lawbot Cog Office'} +FactoryTypeLeg = 'Leg' +FactoryTypeArm = 'Arm' +FactoryTypeTorso = 'Torso' +MintFloorTitle = 'Floor %s' +lCancel = 'Cancel' +lClose = 'Close' +lOK = 'OK' +lNext = 'Next' +lQuit = 'Quit' +lYes = 'Yes' +lNo = 'No' +SleepAutoReply = '%s is sleeping right now.' +lHQOfficerF = 'HQ Officer' +lHQOfficerM = 'HQ Officer' +Cog = 'Cog' +Cogs = 'Cogs' +ACog = 'a Cog' +TheCogs = 'The Cogs' +ASkeleton = 'a Skelecog' +Skeleton = 'Skelecog' +SkeletonP = 'Skelecogs' +Av2Cog = 'a Version 2.0 Cog' +v2Cog = 'Version 2.0 Cog' +v2CogP = 'Version 2.0 Cogs' +ASkeleton = 'a Skelecog' +Foreman = 'Factory Foreman' +ForemanP = 'Factory Foremen' +AForeman = 'a Factory Foreman' +CogVP = Cog + ' V.P.' +CogVPs = "Cog V.P.'s" +ACogVP = ACog + ' V.P.' +Supervisor = 'Mint Supervisor' +SupervisorP = 'Mint Supervisors' +ASupervisor = 'a Mint Supervisor' +CogCFO = Cog + ' C.F.O.' +CogCFOs = "Cog C.F.O.'s" +ACogCFO = ACog + ' C.F.O.' +CogCJs = "Cog C.J.'s" +ACogCJ = ACog + ' C.J.' +CogCEOs = "Cog C.E.O.'s" +ACogCEO = ACog + ' C.E.O.' +CogBosses = 'Cog Bosses' +ACogBoss = ACog + ' Boss' +TheFish = 'the Fish' +AFish = 'a fish' +Level = 'Level' +QuestsCompleteString = 'Complete' +QuestsNotChosenString = 'Not chosen' +Period = '.' +Laff = 'Laff' +QuestInLocationString = ' %(inPhrase)s %(location)s' +QuestsDefaultGreeting = ('Hello, _avName_!', + 'Hi, _avName_!', + 'Hey there, _avName_!', + 'Say there, _avName_!', + 'Welcome, _avName_!', + 'Howdy, _avName_!', + 'How are you, _avName_?', + 'Greetings _avName_!') +QuestsDefaultIncomplete = ("How's that task coming, _avName_?", + 'Looks like you still have more work to do on that task!', + 'Keep up the good work, _avName_!', + 'Keep trying to finish that task. I know you can do it!', + 'Keep trying to complete that task, we are counting on you!', + 'Keep working on that ToonTask!') +QuestsDefaultIncompleteProgress = ('You came to the right place, but you need to finish your ToonTask first.', 'When you are finished with that ToonTask, come back here.', 'Come back when you are finished with your ToonTask.') +QuestsDefaultIncompleteWrongNPC = ('Nice work on that ToonTask. You should go visit _toNpcName_._where_', 'Looks like you are ready to finish your ToonTask. Go see _toNpcName_._where_.', 'Go see _toNpcName_ to finish your ToonTask._where_') +QuestsDefaultComplete = ('Nice work! Here is your reward...', 'Great job, _avName_! Take this reward...', 'Wonderful job, _avName_! Here is your reward...') +QuestsDefaultLeaving = ('Bye!', + 'Goodbye!', + 'So long, _avName_.', + 'See ya, _avName_!', + 'Good luck!', + 'Have fun in Toontown!', + 'See you later!') +QuestsDefaultReject = ('Heya, _avName_!', + 'Whatcha need?', + 'Hello! How are you doing?', + 'Hi there.', + "Sorry _avName_, I'm a bit busy right now.", + 'Yes?', + 'Howdy, _avName_!', + 'Welcome, _avName_!', + "Hey, _avName_! How's it hanging?", + "Need any help?", + "Hi _avName_, what brings you here?") +QuestsDefaultTierNotDone = ('Hello, _avName_! You must finish your current ToonTasks before getting a new one.', 'Hi there! You need to finish the ToonTasks you are working on in order to get a new one.', 'Hi, _avName_! Before I can give you a new ToonTask, you need to finish the ones you have.') +QuestsDefaultQuest = None +QuestsDefaultVisitQuestDialog = ('I heard _toNpcName_ is looking for you._where_', + 'Stop by and see _toNpcName_ when you get a chance._where_', + 'Pay a visit to _toNpcName_ next time you are over that way._where_', + 'If you get a chance, stop in and say hi to _toNpcName_._where_', + '_toNpcName_ will give you your next ToonTask._where_') +QuestsLocationArticle = '' + +def getLocalNum(num): + return str(num) + + +QuestsItemNameAndNum = '%(num)s %(name)s' +QuestsCogQuestProgress = '%(progress)s of %(numCogs)s defeated' +QuestsCogQuestHeadline = 'WANTED' +QuestsCogQuestSCStringS = 'I need to defeat %(cogName)s%(cogLoc)s.' +QuestsCogQuestSCStringP = 'I need to defeat some %(cogName)s%(cogLoc)s.' +QuestsCogQuestDefeat = 'Defeat %s' +QuestsCogQuestDefeatDesc = '%(numCogs)s %(cogName)s' +QuestsCogTrackQuestProgress = '%(progress)s of %(numCogs)s defeated' +QuestsCogTrackQuestHeadline = 'WANTED' +QuestsCogTrackQuestSCStringS = 'I need to defeat %(cogText)s%(cogLoc)s.' +QuestsCogTrackQuestSCStringP = 'I need to defeat some %(cogText)s%(cogLoc)s.' +QuestsCogTrackQuestDefeat = 'Defeat %s' +QuestsCogTrackDefeatDesc = '%(numCogs)s %(trackName)s' +QuestsCogLevelQuestProgress = '%(progress)s of %(numCogs)s defeated' +QuestsCogLevelQuestHeadline = 'WANTED' +QuestsCogLevelQuestDefeat = 'Defeat %s' +QuestsCogLevelQuestDesc = 'a Level %(level)s+ %(name)s' +QuestsCogLevelQuestDescC = '%(count)s Level %(level)s+ %(name)s' +QuestsCogLevelQuestDescI = 'some Level %(level)s+ %(name)s' +QuestsCogLevelQuestSCString = 'I need to defeat %(objective)s%(location)s.' +QuestsBuildingQuestFloorNumbers = ('', + 'two+', + 'three+', + 'four+', + 'five+') +QuestsBuildingQuestHeadline = 'DEFEAT' +QuestsBuildingQuestProgressString = '%(progress)s of %(num)s defeated' +QuestsBuildingQuestString = 'Defeat %s' +QuestsBuildingQuestSCString = 'I need to defeat %(objective)s%(location)s.' +QuestsBuildingQuestDesc = 'a %(type)s Building' +QuestsBuildingQuestDescF = 'a %(floors)s story %(type)s Building' +QuestsBuildingQuestDescC = '%(count)s %(type)s Buildings' +QuestsBuildingQuestDescCF = '%(count)s %(floors)s story %(type)s Buildings' +QuestsBuildingQuestDescI = 'some %(type)s Buildings' +QuestsBuildingQuestDescIF = 'some %(floors)s story %(type)s Buildings' +QuestsCogdoQuestDesc = 'a %(type)s Field Office' +QuestsCogdoQuestDescM = '%(count)s %(type)s Field Offices' +QuestsCogdoQuestDescMUI = 'some %(type)s Field Offices' +QuestsCogdoQuestDescU = 'a Field Office' +QuestsCogdoQuestDescUM = '%(count)s Field Offices' +QuestsCogdoQuestDescMI = 'some Field Offices' +QuestFactoryQuestFactory = 'Factory' +QuestsFactoryQuestFactories = 'Factories' +QuestsFactoryQuestHeadline = 'DEFEAT' +QuestsFactoryQuestProgressString = '%(progress)s of %(num)s defeated' +QuestsFactoryQuestString = 'Defeat %s' +QuestsFactoryQuestSCString = 'I need to defeat %(objective)s%(location)s.' +QuestsFactoryQuestDesc = 'a %(type)s Factory' +QuestsFactoryQuestDescC = '%(count)s %(type)s Factories' +QuestsFactoryQuestDescI = 'some %(type)s Factories' +QuestMintQuestMint = 'Mint' +QuestsMintQuestMints = 'Mints' +QuestsMintQuestHeadline = 'DEFEAT' +QuestsMintQuestProgressString = '%(progress)s of %(num)s defeated' +QuestsMintQuestString = 'Defeat %s' +QuestsMintQuestSCString = 'I need to defeat %(objective)s%(location)s.' +QuestsMintQuestDesc = 'a Cog Mint' +QuestsMintQuestDescC = '%(count)s Cog Mints' +QuestsMintQuestDescI = 'some Cog Mints' +QuestsRescueQuestProgress = '%(progress)s of %(numToons)s rescued' +QuestsRescueQuestHeadline = 'RESCUE' +QuestsRescueQuestSCStringS = 'I need to rescue a Toon %(toonLoc)s.' +QuestsRescueQuestSCStringP = 'I need to rescue some Toons %(toonLoc)s.' +QuestsRescueQuestRescue = 'Rescue %s' +QuestsRescueQuestRescueDesc = '%(numToons)s Toons' +QuestsRescueQuestToonS = 'a Toon' +QuestsRescueQuestToonP = 'Toons' +QuestsRescueQuestAux = 'Rescue:' +QuestCogPartQuestCogPart = 'Cog Suit Part' +QuestsCogPartQuestFactories = 'Factories' +QuestsCogPartQuestHeadline = 'RETRIEVE' +QuestsCogPartQuestProgressString = '%(progress)s of %(num)s retrieved' +QuestsCogPartQuestString = 'Retrieve %s' +QuestsCogPartQuestSCString = 'I need to retrieve %(objective)s%(location)s.' +QuestsCogPartQuestAux = 'Retrieve:' +QuestsCogPartQuestDesc = 'a Cog Suit Part' +QuestsCogPartQuestDescC = '%(count)s Cog Suit Parts' +QuestsCogPartQuestDescI = 'some Cog Suit Parts' +QuestsDeliverGagQuestProgress = '%(progress)s of %(numGags)s delivered' +QuestsDeliverGagQuestHeadline = 'DELIVER' +QuestsDeliverGagQuestToSCStringS = 'I need to deliver %(gagName)s.' +QuestsDeliverGagQuestToSCStringP = 'I need to deliver some %(gagName)s.' +QuestsDeliverGagQuestSCString = 'I need to make a delivery.' +QuestsDeliverGagQuestString = 'Deliver %s' +QuestsDeliverGagQuestStringLong = 'Deliver %s to _toNpcName_.' +QuestsDeliverGagQuestInstructions = 'You can buy this gag in the Gag Shop once you earn access to it.' +QuestsDeliverItemQuestProgress = '' +QuestsDeliverItemQuestHeadline = 'DELIVER' +QuestsDeliverItemQuestSCString = 'I need to deliver %(article)s%(itemName)s.' +QuestsDeliverItemQuestString = 'Deliver %s' +QuestsDeliverItemQuestStringLong = 'Deliver %s to _toNpcName_.' +QuestsVisitQuestProgress = '' +QuestsVisitQuestHeadline = 'VISIT' +QuestsVisitQuestStringShort = 'Visit' +QuestsVisitQuestStringLong = 'Visit _toNpcName_' +QuestsVisitQuestSeeSCString = 'I need to see %s.' +QuestsRecoverItemQuestProgress = '%(progress)s of %(numItems)s recovered' +QuestsRecoverItemQuestHeadline = 'RECOVER' +QuestsRecoverItemQuestSeeHQSCString = 'I need to see an ' + lHQOfficerM + '.' +QuestsRecoverItemQuestReturnToHQSCString = 'I need to return %s to an ' + lHQOfficerM + '.' +QuestsRecoverItemQuestReturnToSCString = 'I need to return %(item)s to %(npcName)s.' +QuestsRecoverItemQuestGoToHQSCString = 'I need to go to a Toon HQ.' +QuestsRecoverItemQuestGoToPlaygroundSCString = 'I need to go to %s Playground.' +QuestsRecoverItemQuestGoToStreetSCString = 'I need to go %(to)s %(street)s in %(hood)s.' +QuestsRecoverItemQuestVisitBuildingSCString = 'I need to visit %s%s.' +QuestsRecoverItemQuestWhereIsBuildingSCString = 'Where is %s%s?' +QuestsRecoverItemQuestRecoverFromSCString = 'I need to recover %(item)s from %(holder)s%(loc)s.' +QuestsRecoverItemQuestString = 'Recover %(item)s from %(holder)s' +QuestsRecoverItemQuestHolderString = '%(level)s %(holder)d+ %(cogs)s' +QuestsTrackChoiceQuestHeadline = 'CHOOSE' +QuestsTrackChoiceQuestSCString = 'I need to choose between %(trackA)s and %(trackB)s.' +QuestsTrackChoiceQuestMaybeSCString = 'Maybe I should choose %s.' +QuestsTrackChoiceQuestString = 'Choose between %(trackA)s and %(trackB)s' +QuestsFriendQuestHeadline = 'FRIEND' +QuestsFriendQuestSCString = 'I need to make a friend.' +QuestsFriendQuestString = 'Make a friend' +QuestsMailboxQuestHeadline = 'MAIL' +QuestsMailboxQuestSCString = 'I need to check my mail.' +QuestsMailboxQuestString = 'Check your mail' +QuestsPhoneQuestHeadline = 'CLARABELLE' +QuestsPhoneQuestSCString = 'I need to call Clarabelle.' +QuestsPhoneQuestString = 'Call Clarabelle' +QuestsTrolleyQuestHeadline = 'TROLLEY' +QuestsTrolleyQuestSCString = 'I need to ride the trolley.' +QuestsTrolleyQuestString = 'Ride on the trolley' +QuestsTrolleyQuestStringShort = 'Ride the trolley' +QuestsMaxHpReward = 'Your Laff limit has been increased by %s.' +QuestsMaxHpRewardPoster = 'Reward: %s point Laff boost' +QuestsMoneyRewardSingular = 'You get 1 Jellybean.' +QuestsMoneyRewardPlural = 'You get %s Jellybeans.' +QuestsMoneyRewardPosterSingular = 'Reward: 1 Jellybean' +QuestsMoneyRewardPosterPlural = 'Reward: %s Jellybeans' +QuestsMaxMoneyRewardSingular = 'You can now carry 1 Jellybean.' +QuestsMaxMoneyRewardPlural = 'You can now carry %s Jellybeans.' +QuestsMaxMoneyRewardPosterSingular = 'Reward: Carry 1 Jellybean' +QuestsMaxMoneyRewardPosterPlural = 'Reward: Carry %s Jellybeans' +QuestsMaxGagCarryReward = 'You get a %(name)s. You can now carry %(num)s gags.' +QuestsMaxGagCarryRewardPoster = 'Reward: %(name)s (%(num)s)' +QuestsMaxQuestCarryReward = 'You can now have %s ToonTasks.' +QuestsMaxQuestCarryRewardPoster = 'Reward: Carry %s ToonTasks' +QuestsTeleportReward = 'You now have teleport access to %s.' +QuestsTeleportRewardPoster = 'Reward: Teleport access to %s' +QuestsTrackTrainingReward = 'You can now train for "%s" gags.' +QuestsTrackTrainingRewardPoster = 'Reward: Gag training' +QuestsTrackProgressReward = 'You now have frame %(frameNum)s of the %(trackName)s track animation.' +QuestsTrackProgressRewardPoster = 'Reward: "%(trackName)s" track animation frame %(frameNum)s' +QuestsTrackCompleteReward = 'You may now carry and use "%s" gags.' +QuestsTrackCompleteRewardPoster = 'Reward: Final %s track training' +QuestsClothingTicketReward = 'You can change your clothes' +QuestsClothingTicketRewardPoster = 'Reward: Clothing Ticket' +TIPQuestsClothingTicketReward = 'You can change your shirt for a TIP shirt' +TIPQuestsClothingTicketRewardPoster = 'Reward: TIP Clothing Ticket' +QuestsCheesyEffectRewardPoster = 'Reward: %s' +QuestsCogSuitPartReward = 'You now have a %(cogTrack)s %(part)s Cog Suit Part.' +QuestsCogSuitPartRewardPoster = 'Reward: %(cogTrack)s %(part)s Part' +QuestsEPPReward = 'You now have %s Easy Promotion Papers.' +QuestsEPPRewardPoster = 'Reward: %s Easy Promotion Papers' +QuestsStreetLocationThisPlayground = 'in this playground' +QuestsStreetLocationThisStreet = 'on this street' +QuestsStreetLocationNamedPlayground = 'in the %s playground' +QuestsStreetLocationNamedStreet = 'on %(toStreetName)s in %(toHoodName)s' +QuestsLocationString = '%(string)s%(location)s' +QuestsLocationBuilding = "%s's building is called" +QuestsLocationBuildingVerb = 'which is' +QuestsLocationParagraph = '\x07%(building)s "%(buildingName)s"...\x07...%(buildingVerb)s %(street)s.' +QuestsGenericFinishSCString = 'I need to finish a ToonTask.' +QuestsMediumPouch = 'Medium Pouch' +QuestsLargePouch = 'Large Pouch' +QuestsSmallBag = 'Small Bag' +QuestsMediumBag = 'Medium Bag' +QuestsLargeBag = 'Large Bag' +QuestsSmallBackpack = 'Small Backpack' +QuestsMediumBackpack = 'Medium Backpack' +QuestsLargeBackpack = 'Large Backpack' +QuestsItemDict = {1: ['Pair of Glasses', 'Pairs of Glasses', 'a '], + 2: ['Key', 'Keys', 'a '], + 3: ['Blackboard', 'Blackboards', 'a '], + 4: ['Book', 'Books', 'a '], + 5: ['Candy Bar', 'Candy Bars', 'a '], + 6: ['Piece of Chalk', 'Pieces of Chalk', 'a '], + 7: ['Recipe', 'Recipes', 'a '], + 8: ['Note', 'Notes', 'a '], + 9: ['Adding machine', 'Adding machines', 'an '], + 10: ['Clown car tire', 'Clown car tires', 'a '], + 11: ['Air pump', 'Air pumps', 'an '], + 12: ['Octopus ink', 'Octopus inks', 'some '], + 13: ['Package', 'Package', 'a '], + 14: ['Goldfish receipt', 'Goldfish receipts', 'a '], + 15: ['Goldfish', 'Goldfish', 'a '], + 16: ['Oil', 'Oils', 'some '], + 17: ['Grease', 'Greases', 'some '], + 18: ['Water', 'Waters', 'some '], + 19: ['Gear report', 'Gear reports', 'a '], + 20: ['Blackboard Eraser', 'Blackboard Erasers', 'a '], + 110: ['TIP Clothing Ticket', 'Clothing Tickets', 'a '], + 1000: ['Clothing Ticket', 'Clothing Tickets', 'a '], + 2001: ['Inner Tube', 'Inner Tubes', 'an '], + 2002: ['Monocle Prescription', 'Monocle Prescriptions', 'a '], + 2003: ['Eyeglass Frames', 'Eyeglass Frames', 'some '], + 2004: ['Monocle', 'Monocles', 'a '], + 2005: ['Big White Wig', 'Big White Wigs', 'a '], + 2006: ['Bushel of Ballast', 'Bushels of Ballast', 'a '], + 2007: ['Cog Gear', 'Cog Gears', 'a '], + 2008: ['Sea Chart', 'Sea Charts', 'a '], + 2009: ['Cruddy Clovis', 'Cruddy Clovi', 'a '], + 2010: ['Clean Clovis', 'Clean Clovi', 'a '], + 2011: ['Clock Spring', 'Clock Springs', 'a '], + 2012: ['Counter Weight', 'Counter Weights', 'a '], + 4001: ["Tina's Inventory", "Tina's Inventories", ''], + 4002: ["Yuki's Inventory", "Yuki's Inventories", ''], + 4003: ['Inventory Form', 'Inventory Forms', 'an '], + 4004: ["Fifi's Inventory", "Fifi's Inventories", ''], + 4005: ["Lumber Jack's Ticket", "Lumber Jack's Tickets", ''], + 4006: ["Tabitha's Ticket", "Tabitha's Tickets", ''], + 4007: ["Barry's Ticket", "Barry's Tickets", ''], + 4008: ['Cloudy Castanet', 'Cloudy Castanets', ''], + 4009: ['Blue Squid Ink', 'Blue Squid Ink', 'some '], + 4010: ['Clear Castanet', 'Clear Castanets', 'a '], + 4011: ["Leo's Lyrics", "Leo's Lyrics", ''], + 5001: ['Silk necktie', 'Silk neckties', 'a '], + 5002: ['Pinstripe Suit', 'Pinstripe Suits', 'a '], + 5003: ['Pair of Scissors', 'Pairs of Scissors', 'a '], + 5004: ['Postcard', 'Postcards', 'a '], + 5005: ['Pen', 'Pens', 'a '], + 5006: ['Inkwell', 'Inkwells', 'an '], + 5007: ['Notepad', 'Notepads', 'a '], + 5008: ['Office Lockbox', 'Office Lockboxes', 'an '], + 5009: ['Bag of Bird Seed', 'Bags of Bird Seed', 'a '], + 5010: ['Sprocket', 'Sprockets', 'a '], + 5011: ['Salad', 'Salads', 'a '], + 5012: ['Key to ' + lDaisyGardens, 'Keys to ' + lDaisyGardens, 'a '], + 5013: [lSellbotHQ + ' Blueprints', lSellbotHQ + ' HQ Blueprints', 'some '], + 5014: [lSellbotHQ + ' Memo', lSellbotHQ + ' Memos', 'a '], + 5015: [lSellbotHQ + ' Memo', lSellbotHQ + ' Memos', 'a '], + 5016: [lSellbotHQ + ' Memo', lSellbotHQ + ' Memos', 'a '], + 5017: [lSellbotHQ + ' Memo', lSellbotHQ + ' Memos', 'a '], + 3001: ['Soccer ball', 'Soccer balls', 'a '], + 3002: ['Toboggan', 'Toboggans', 'a '], + 3003: ['Ice cube', 'Ice cubes', 'an '], + 3004: ['Love letter', 'Love letters', 'a '], + 3005: ['Wiener dog', 'Wiener dogs', 'a '], + 3006: ['Engagement ring', 'Engagement rings', 'an '], + 3007: ['Sardine whiskers', 'Sardine whiskers', 'some '], + 3008: ['Calming potion', 'Calming potion', 'a '], + 3009: ['Broken tooth', 'Broken teeth', 'a '], + 3010: ['Gold tooth', 'Gold teeth', 'a '], + 3011: ['Pine cone bread', 'Pine cone breads', 'a '], + 3012: ['Lumpy cheese', 'Lumpy cheeses', 'some '], + 3013: ['Simple spoon', 'Simple spoons', 'a '], + 3014: ['Talking toad', 'Talking toad', 'a '], + 3015: ['Ice cream cone', 'Ice cream cones', 'an '], + 3016: ['Wig powder', 'Wig powders', 'some '], + 3017: ['Rubber ducky', 'Rubber duckies', 'a '], + 3018: ['Fuzzy dice', 'Fuzzy dice', 'some '], + 3019: ['Microphone', 'Microphones', 'a '], + 3020: ['Electric keyboard', 'Electric keyboards', 'an '], + 3021: ['Platform shoes', 'Platform shoes', 'some '], + 3022: ['Caviar', 'Caviar', 'some '], + 3023: ['Make-up powder', 'Make-up powders', 'some '], + 3024: ['Yarn', 'Yarn', 'some '], + 3025: ['Knitting Needle', 'Knitting Needles', 'a '], + 3026: ['Alibi', 'Alibis', 'an '], + 3027: ['External Temperature Sensor', 'External Temperature Sensors', 'an '], + 6001: ['Cashbot HQ Plans', 'Cashbot HQ Plans', 'some '], + 6002: ['Rod', 'Rods', 'a '], + 6003: ['Drive Belt', 'Drive Belts', 'a '], + 6004: ['Pair of Pincers', 'Pairs of Pincers', 'a '], + 6005: ['Reading Lamp', 'Reading Lamps', 'a '], + 6006: ['Zither', 'Zithers', 'a '], + 6007: ['Zamboni', 'Zambonis', 'a '], + 6008: ['Zebra Zabuton', 'Zebra Zabutons', 'a '], + 6009: ['Zinnias', 'Zinnias', 'some '], + 6010: ['Zydeco Records', 'Zydeco Records', 'some '], + 6011: ['Zucchini', 'Zucchinis', 'a '], + 6012: ['Zoot Suit', 'Zoot Suits', 'a '], + 7001: ['Plain Bed', 'Plain Beds', 'a '], + 7002: ['Fancy Bed', 'Fancy Beds', 'a '], + 7003: ['Blue Bedspread', 'Blue Bedspreads', 'a '], + 7004: ['Paisley Bedspread', 'Paisley Bedspreads', 'a '], + 7005: ['Pillows', 'Pillows', 'some '], + 7006: ['Hard Pillows', 'Hard Pillows', 'some '], + 7007: ['Pajamas', 'Pajamas', 'a pair of '], + 7008: ['Footie Pajamas', 'Footie Pajamas', 'a pair of '], + 7009: ['Puce Footie Pajamas', 'Puce Footie Pajamas', 'a pair of '], + 7010: ['Fuchsia Footie Pajamas', 'Fuchsia Footie Pajamas', 'a pair of '], + 7011: ['Cauliflower Coral', 'Cauliflower Coral', 'some '], + 7012: ['Slimy Kelp', 'Slimy Kelp', 'some '], + 7013: ['Pestle', 'Pestles', 'a '], + 7014: ['Jar of Wrinkle Cream', 'Jars of Wrinkle Cream', 'a ']} +QuestsHQOfficerFillin = lHQOfficerM +QuestsHQWhereFillin = '' +QuestsHQBuildingNameFillin = lToonHQ +QuestsHQLocationNameFillin = 'in any neighborhood' +QuestsTailorFillin = 'Tailor' +QuestsTailorWhereFillin = '' +QuestsTailorBuildingNameFillin = 'Clothing Store' +QuestsTailorLocationNameFillin = 'in any neighborhood' +QuestsTailorQuestSCString = 'I need to see a Tailor.' +QuestMovieQuestChoiceCancel = 'Come back later if you need a ToonTask! Bye!' +QuestMovieTrackChoiceCancel = 'Come back when you are ready to decide! Bye!' +QuestMovieQuestChoice = 'Choose a ToonTask.' +QuestMovieTrackChoice = 'Ready to decide? Choose a track, or come back later.' +GREETING = 0 +QUEST = 1 +INCOMPLETE = 2 +INCOMPLETE_PROGRESS = 3 +INCOMPLETE_WRONG_NPC = 4 +COMPLETE = 5 +LEAVING = 6 +TheBrrrghTrackQuestDict = {GREETING: '', + QUEST: 'Now you are ready.\x07Go out and walk the earth until you know which track you would like to choose.\x07Choose wisely, because this is your final track.\x07When you are certain, return to me.', + INCOMPLETE_PROGRESS: 'Choose wisely.', + INCOMPLETE_WRONG_NPC: 'Choose wisely.', + COMPLETE: 'Very wise choice!', + LEAVING: 'Good luck. Return to me when you have mastered your new skill.'} +QuestDialog_3225 = {QUEST: "Oh, thanks for coming, _avName_!\x07The Cogs in the neighborhood frightened away my delivery person.\x07I don't have anyone to deliver this salad to _toNpcName_!\x07Can you do it for me? Thanks so much!_where_"} +QuestDialog_2910 = {QUEST: 'Back so soon?\x07Great job on the spring.\x07The final item is a counter weight.\x07Stop by and see _toNpcName_ and bring back whatever you can get._where_'} +QuestDialogDict = { + 120: {QUEST: "Good job finding the trolley!\x07By the way, have you met Banker Bob?\x07He has quite a sweet tooth.\x07Why don't you introduce yourself by taking him this candy bar as a gift._where_"}, + 121: {QUEST: "Yum, thank you for the Candy Bar.\x07Say, if you can help me, I'll give you a reward.\x07Those Cogs stole the keys to my safe. Defeat Cogs to find a stolen key.\x07When you find a key, bring it back to me."}, + 130: {QUEST: 'Good job finding the trolley!\x07By the way, I received a package for Professor Pete today.\x07It must be his new chalk he ordered.\x07Can you please take it to him?_where_'}, + 131: {QUEST: 'Oh, thanks for the chalk.\x07What?!?\x07Those Cogs stole my blackboard. Defeat Cogs to find my stolen blackboard.\x07When you find it, bring it back to me.'}, + 140: {QUEST: 'Good job finding the trolley!\x07By the way, I have this friend, Librarian Larry, who is quite a book worm.\x07I picked this book up for him last time I was over in ' + lDonaldsDock + '.\x07Could you take it over to him?_where_'}, + 141: {QUEST: 'Oh, yes, this book almost completes my collection.\x07Let me see...\x07Uh oh...\x07Now where did I put my glasses?\x07I had them just before those Cogs took over my building.\x07Defeat Cogs to find my stolen glasses.'}, + 142: {QUEST: "Thank you! I have a feeling you will be a great help to everyone around here.\x07By the way, I just received a message from Toon HQ.\x07They forgot to mention a few important concepts. You're needed there!"}, + 160: {GREETING: '', + QUEST: "Ok, now I think you are ready for something more rewarding.\x07If you can defeat 3 Bossbots I'll give you a little bonus.", + INCOMPLETE_PROGRESS: TheCogs + ' are out in the streets, through the tunnels.', + INCOMPLETE_WRONG_NPC: 'Good job defeating those Cogs. Now go to the Toon Headquarters for your next step!', + COMPLETE: QuestsDefaultComplete, + LEAVING: QuestsDefaultLeaving}, + 161: {GREETING: '', + QUEST: "Ok, now I think you are ready for something more rewarding.\x07Come back after you defeat 3 Lawbots and I'll have a little something for you.", + INCOMPLETE_PROGRESS: TheCogs + ' are out in the streets, through the tunnels.', + INCOMPLETE_WRONG_NPC: 'Good job defeating those Cogs. Now go to the Toon Headquarters for your next step!', + COMPLETE: QuestsDefaultComplete, + LEAVING: QuestsDefaultLeaving}, + 162: {GREETING: '', + QUEST: 'Ok, now I think you are ready for something more rewarding.\x07Defeat 3 Cashbots and come back here to claim the bounty.', + INCOMPLETE_PROGRESS: TheCogs + ' are out in the streets, through the tunnels.', + INCOMPLETE_WRONG_NPC: 'Good job defeating those Cogs. Now go to the Toon Headquarters for your next step!', + COMPLETE: QuestsDefaultComplete, + LEAVING: QuestsDefaultLeaving}, + 163: {GREETING: '', + QUEST: "Ok, now I think you are ready for something more rewarding.\x07Come see us after you defeat 3 Sellbots and we'll hook you up.", + INCOMPLETE_PROGRESS: TheCogs + ' are out in the streets, through the tunnels.', + INCOMPLETE_WRONG_NPC: 'Good job defeating those Cogs. Now go to the Toon Headquarters for your next step!', + COMPLETE: QuestsDefaultComplete, + LEAVING: QuestsDefaultLeaving}, + 164: {QUEST: 'You look like you could use some new gags.\x07Go see %s, maybe he can help you out._where_' % Flippy}, + 165: {QUEST: 'Hi there.\x07Looks like you need to practice training your gags.\x07Every time you hit a Cog with one of your gags, your experience increases.\x07When you get enough experience, you will be able to use an even better gag.\x07Go practice your gags by defeating 4 Cogs.'}, + 166: {QUEST: 'Nice work defeating those Cogs.\x07You know, the Cogs come in four different types.\x07They are Lawbots, Cashbots, Sellbots, and Bossbots.\x07You can tell them apart by their coloring and their name labels.\x07For practice go defeat 4 Bossbots.'}, + 167: {QUEST: 'Nice work defeating those Cogs.\x07You know, the Cogs come in four different types.\x07They are Lawbots, Cashbots, Sellbots, and Bossbots.\x07You can tell them apart by their coloring and their name labels.\x07For practice go defeat 4 Lawbots.'}, + 168: {QUEST: 'Nice work defeating those Cogs.\x07You know, the Cogs come in four different types.\x07They are Lawbots, Cashbots, Sellbots, and Bossbots.\x07You can tell them apart by their coloring and their name labels.\x07For practice go defeat 4 Sellbots.'}, + 169: {QUEST: 'Nice work defeating those Cogs.\x07You know, the Cogs come in four different types.\x07They are Lawbots, Cashbots, Sellbots, and Bossbots.\x07You can tell them apart by their coloring and their name labels.\x07For practice go defeat 4 Cashbots.'}, + 170: {QUEST: 'Nice work, now you know the difference between the 4 types of Cogs.\x07I think you are ready to start training for your third gag track.\x07Go talk to _toNpcName_ to choose your next gag track - he can give you some expert advice._where_'}, + 171: {QUEST: 'Nice work, now you know the difference between the 4 types of Cogs.\x07I think you are ready to start training for your third gag track.\x07Go talk to _toNpcName_ to choose your next gag track - he can give you some expert advice._where_'}, + 172: {QUEST: 'Nice work, now you know the difference between the 4 types of Cogs.\x07I think you are ready to start training for your third gag track.\x07Go talk to _toNpcName_ to choose your next gag track - she can give you some expert advice._where_'}, + 175: {GREETING: '', + QUEST: "Did you know you have your very own Toon house?\x07Clarabelle Cow runs a phone catalog where you can order furniture to decorate your house.\x07You can also buy SpeedChat phrases, clothing, and other fun things!\x07I'll tell Clarabelle to send you your first catalog now.\x07You get a catalog with new items every week!\x07Go to your home and use your phone to call Clarabelle.", + INCOMPLETE_PROGRESS: 'Go home and use your phone to call Clarabelle.', + COMPLETE: 'Hope you have fun ordering things from Clarabelle!\x07I just finished redecorating my house. It looks Toontastic!\x07Keep doing ToonTasks to get more rewards!', + LEAVING: QuestsDefaultLeaving}, + 400: {GREETING: '', + QUEST: 'Throw and Squirt are great, but you will need more gags to fight higher level Cogs.\x07When you team up with other Toons against the Cogs, you can combine attacks for even more damage.\x07Try different combinations of gags to see what works best.\x07For your next track, choose between Sound and Toonup.\x07Sound is special because when it hits, it damages all Cogs.\x07Toonup lets you heal other Toons in battle.\x07When you are ready to decide, come back here and choose.', + INCOMPLETE_PROGRESS: 'Back so soon? Okay, are you ready to choose?', + INCOMPLETE_WRONG_NPC: 'Think about your decision before choosing.', + COMPLETE: 'Good decision. Now before you can use those gags, you must train for them.\x07You must complete a series of ToonTasks for training.\x07Each task will give you a single frame of your gag attack animation.\x07When you collect all 15, you can get the Final Gag Training task that will allow you to use your new gags.\x07You can check your progress in the Shticker Book.', + LEAVING: QuestsDefaultLeaving}, + 1039: {QUEST: 'Visit _toNpcName_ if you want to get around town more easily._where_'}, + 1040: {QUEST: 'Visit _toNpcName_ if you want to get around town more easily._where_'}, + 1041: {QUEST: 'Hi! What brings you here?\x07Everybody uses their portable hole to travel around Toontown.\x07Why, you can teleport to your friends using the Friends List, or to any neighborhood using the map in the Shticker Book.\x07Of course, you have to earn that!\x07Say, I can turn on your teleport access to ' + lToontownCentral + ' if you help out a friend of mine.\x07Seems the Cogs are causing trouble over on Loopy Lane. Go visit _toNpcName_._where_'}, + 1042: {QUEST: 'Hi! What brings you here?\x07Everybody uses their portable hole to travel around Toontown.\x07Why, you can teleport to your friends using the Friends List, or to any neighborhood using the map in the Shticker Book.\x07Of course, you have to earn that!\x07Say, I can turn on your teleport access to ' + lToontownCentral + ' if you help out a friend of mine.\x07Seems the Cogs are causing trouble over on Loopy Lane. Go visit _toNpcName_._where_'}, + 1043: {QUEST: 'Hi! What brings you here?\x07Everybody uses their portable hole to travel around Toontown.\x07Why, you can teleport to your friends using the Friends List, or to any neighborhood using the map in the Shticker Book.\x07Of course, you have to earn that!\x07Say, I can turn on your teleport access to ' + lToontownCentral + ' if you help out a friend of mine.\x07Seems the Cogs are causing trouble over on Loopy Lane. Go visit _toNpcName_._where_'}, + 1044: {QUEST: 'Oh, thanks for stopping by. I really need some help.\x07As you can see, I have no customers.\x07My secret recipe book is lost and nobody comes to my restaurant anymore.\x07I last saw it just before those Cogs took over my building.\x07Can you help me by recovering four of my famous recipes?', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck finding my recipes?'}, + 1045: {QUEST: 'Thank you so much!\x07Before long I will have the entire collection and can reopen my restaurant.\x07Oh, I have a note here for you - something about teleport access?\x07It says thanks for helping my friend and to deliver this to Toon Headquarters.\x07Well, thanks indeed - bye!', + LEAVING: '', + COMPLETE: 'Ah, yes, says here you have been a great help to some of the fine folks out on Loopy Lane.\x07Says you need teleport access to ' + lToontownCentral + '.\x07Well, consider it done.\x07Now you can teleport back to the playground from almost anywhere in Toontown.\x07Just open your map and click on ' + lToontownCentral + '.'}, + 1046: {QUEST: 'The Cashbots have really been bothering the Funny Money Savings and Loan.\x07Stop by there and see if there is anything you can do._where_'}, + 1047: {QUEST: 'Cashbots have been sneaking into the bank and stealing our machines.\x07Please recover 5 adding machines from Cashbots.\x07To save you from running back and forth, just bring them all back at once.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Still looking for adding machines?'}, + 1048: {QUEST: 'Wow! Thanks for finding our adding machines.\x07Hm... They look a little damaged.\x07Say, could you take them over to _toNpcName_ over at her shop, "Tickle Machines" on this street?\x07See if she can fix them.', + LEAVING: ''}, + 1049: {QUEST: "What's that? Broken adding machines?\x07Cashbots you say?\x07Well, let's have a look see...\x07Yep, gears are stripped, but I'm out of that part...\x07You know what might work - some Cog gears, large ones, from larger Cogs...\x07Level 3 Cog gears should do the trick. I'll need 2 for each machine, so 10 total.\x07Bring them back all at once and I'll fix em up!", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Remember, I need 10 gears to fix the machines.'}, + 1053: {QUEST: "Ah yes, that should do the trick indeedy.\x07All fixed now, free of charge.\x07Take these back to Funny Money, and tell 'im I said howdy.", + LEAVING: '', + COMPLETE: "Adding machines all fixed up?\x07Nice work. I'm sure I've got something around here to reward you with..."}, + 1054: {QUEST: '_toNpcName_ needs some help with his clown cars._where_'}, + 1055: {QUEST: "Yowza! I can't find the tires to this here clown car anywhere!\x07Do ya think you could help me out?\x07I think Loopy Bob may have tossed them in the pond in the " + lToontownCentral + ' playground.\x07If you stand on one of the docks there you can try and fish out the tires for me.', + GREETING: 'Woohoo!', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Are you having trouble fishing out all 4 tires?'}, + 1056: {QUEST: 'Fan-flying-tastic! Now I can get this old clown car on the road again!\x07Hey, I thought I had an air pump around here to inflate these tires...\x07Maybe _toNpcName_ borrowed it?\x07Could you go ask for it back for me?_where_', + LEAVING: ''}, + 1057: {QUEST: "Hi there.\x07A tire pump you say?\x07I'll tell you what - you help clean up the streets of some of those high level Cogs for me...\x07And I'll let you have the tire pump.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Is that the best you can do?'}, + 1058: {QUEST: "Good job - I knew you could do it.\x07Here's the pump. I'm sure _toNpcName_ will be glad to get it back.", + LEAVING: '', + GREETING: '', + COMPLETE: "Yeehaw! Now I'm good to go!\x07By the way, thanks for helping me out.\x07Here, take this."}, + 1059: {QUEST: '_toNpcName_ is running low on supplies. Maybe you can give him a hand?_where_'}, + 1060: {QUEST: "Thanks for stopping by!\x07Those Cogs have been stealing my ink, so I'm running very low.\x07Could you fish some octopus ink out of the pond for me?\x07Just stand on a dock near the pond to fish.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Are you having trouble fishing?'}, + 1061: {QUEST: "Great - thanks for the ink!\x07You know what, maybe if you cleared away some of those Pencil Pushers...\x07I wouldn't run out of ink again so quickly.\x07Defeat 6 Pencil Pushers in " + lToontownCentral + ' for your reward.', + LEAVING: '', + COMPLETE: 'Thanks! Let me reward you for your help.', + INCOMPLETE_PROGRESS: 'I just saw some more Pencil Pushers.'}, + 1062: {QUEST: "Great - thanks for the ink!\x07You know what, maybe if you cleared away some of those Bloodsuckers...\x07I wouldn't run out of ink again so quickly.\x07Defeat 6 Bloodsuckers in " + lToontownCentral + ' for your reward.', + LEAVING: '', + COMPLETE: 'Thanks! Let me reward you for your help.', + INCOMPLETE_PROGRESS: 'I just saw some more Bloodsuckers.'}, + 900: {QUEST: 'I hear _toNpcName_ needs help with a package._where_'}, + 1063: {QUEST: 'Hi - thanks for coming in.\x07A Cog stole a very important package from right under my nose.\x07Please see if you can get it back. I think he was a level 3...\x07So, defeat level 3 Cogs until you find my package.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding the package, huh?'}, + 1067: {QUEST: "That's it, all right!\x07Hey, the address is smudged...\x07All I can read is that it's for a Dr. - the rest is all blurry.\x07Maybe it's for _toNpcName_? Could you take it to him?_where_", + LEAVING: ''}, + 1068: {QUEST: "I wasn't expecting a package. Maybe it's for Dr. I.M. Euphoric?\x07My assistant was going over there today anyway, so I'll have him check for you.\x07In the meantime, would you mind getting rid of some of the Cogs on my street?\x07Defeat 10 Cogs in " + lToontownCentral + '.', + LEAVING: '', + INCOMPLETE_PROGRESS: "My assistant isn't back yet."}, + 1069: {QUEST: "Dr. Euphoric says he wasn't expecting a package either.\x07Unfortunately, a Cashbot stole it from my assistant on the way back.\x07Could you try and get it back?", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding the package, huh?'}, + 1070: {QUEST: "Dr. Euphoric says he wasn't expecting a package either.\x07Unfortunately, a Sellbot stole the package from my assistant on the way back.\x07I'm sorry, but you'll have to find that Sellbot and get it back.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding the package, huh?'}, + 1071: {QUEST: "Dr. Euphoric says he wasn't expecting a package either.\x07Unfortunately, a Bossbot stole it from my assistant on the way back.\x07Could you try and get it back?", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding the package, huh?'}, + 1072: {QUEST: 'Great - you got it back!\x07Maybe you should try _toNpcName_, it could be for him._where_', + LEAVING: ''}, + 1073: {QUEST: 'Oh, thanks for bringing me my packages.\x07Wait a second, I was expecting two. Could you check with _toNpcName_ and see if he has the other one?', + INCOMPLETE: 'Were you able to find my other package?', + LEAVING: ''}, + 1074: {QUEST: 'He said there was another package? Maybe the Cogs stole it too.\x07Defeat Cogs until you find the second package.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding the other package, huh?'}, + 1075: {QUEST: 'I guess there was a second package after all!\x07Hurry and take it over to _toNpcName_ with my apologies.', + COMPLETE: 'Hey, my package is here!\x07Since you seem to be such a helpful Toon, this should come in handy.', + LEAVING: ''}, + 1076: {QUEST: "There's been some trouble over at 14 Karat Goldfish.\x07_toNpcName_ could probably use a hand._where_"}, + 1077: {QUEST: "Thanks for coming - the Cogs stole all my goldfish.\x07I think the Cogs want to sell them to make a quick buck.\x07Those 5 fish have been my only companions in this tiny store for so many years...\x07If you could get them back for me I'd really appreciate it.\x07I'm sure one of the Cogs has my fish.\x07Defeat Cogs until you find my goldfish.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Please return my goldfish to me.'}, + 1078: {QUEST: "Oh, you have my fish!\x07Huh? What's this - a receipt?\x07Sigh, I guess they are Cogs, after all.\x07I can't make heads or tails out of this receipt. Could you take it to _toNpcName_ and see if he can read it?_where_", + INCOMPLETE: 'What did _toNpcName_ have to say about the receipt?', + LEAVING: ''}, + 1079: {QUEST: "Mmm, let me see that receipt.\x07...Ah Yes, it says that 1 goldfish was sold to a Flunky.\x07It doesn't seem to mention what happened to the other 4 fish.\x07Maybe you should try and find that Flunky.", + LEAVING: '', + INCOMPLETE_PROGRESS: "I don't think there's anything else I can help you with.\x07Why don't you try and find that goldfish?"}, + 1092: {QUEST: "Mmm, let me see that receipt.\x07...Ah Yes, it says that 1 goldfish was sold to a Short Change.\x07It doesn't seem to mention what happened to the other 4 fish.\x07Maybe you should try and find that Short Change.", + LEAVING: '', + INCOMPLETE_PROGRESS: "I don't think there's anything else I can help you with.\x07Why don't you try and find that goldfish?"}, + 1080: {QUEST: "Oh thank heavens! You found Oscar - he's my favorite.\x07What's that, Oscar? Uh huh... they did? ... they are?\x07Oscar says the other 4 escaped into the pond in the playground.\x07Could you go round them up for me?\x07Just fish them out of the pond.", + LEAVING: '', + COMPLETE: 'Ahh, I am sooo happy! To be reunited with my little buddies!\x07You deserve a handsome reward for this!', + INCOMPLETE_PROGRESS: 'Are you having trouble finding those fish?'}, + 1081: {QUEST: '_toNpcName_ appears to be in a sticky situation. He sure could use a hand._where_'}, + 1082: {QUEST: "I spilled quick dry glue and I'm stuck - stuck cold!\x07If there were a way out, I sure would be sold.\x07That gives me an idea, if you are feeling loyal.\x07Defeat some Sellbots and bring back some oil.", + LEAVING: '', + GREETING: '', + INCOMPLETE_PROGRESS: 'Can you help me get un-stuck?'}, + 1083: {QUEST: "Well, oil helped a little, but I still cannot budge,\x07What else would help? It's hard to judge.\x07That gives me an idea; it's worth a try at least.\x07Defeat some Lawbots and bring back some grease.", + LEAVING: '', + GREETING: '', + INCOMPLETE_PROGRESS: 'Can you help me get un-stuck?'}, + 1084: {QUEST: "Nope, that didn't help. This is really not funny.\x07I put the grease right there on the money,\x07That gives me an idea, before I forget it.\x07Defeat some Cashbots; bring back water to wet it.", + LEAVING: '', + GREETING: '', + COMPLETE: "Hooray, I'm free of this quick drying glue,\x07As a reward I give this gift to you,\x07You can laugh a little longer while battling and then...\x07Oh, no! I'm already stuck here again!", + INCOMPLETE_PROGRESS: 'Can you help me get un-stuck?'}, + 1085: {QUEST: '_toNpcName_ is conducting some research on the Cogs.\x07Go talk to him if you want to help out._where_'}, + 1086: {QUEST: "That's right, I'm conducting a study of the Cogs.\x07I want to know what makes them tick.\x07It sure would help me if you could gather some gears from Cogs.\x07Make sure they're from at least level 2 Cogs so they're big enough to examine.", + LEAVING: '', + INCOMPLETE_PROGRESS: "Can't find enough gears?"}, + 1089: {QUEST: "Okay, let's take a look. These are excellent specimens!\x07Mmmm...\x07Okay, here's my report. Take this back to Toon Headquarters right away.", + INCOMPLETE: 'Have you delivered my report to Headquarters?', + COMPLETE: "Good work _avName_, we'll take this one from here.", + LEAVING: ''}, + 1090: {QUEST: '_toNpcName_ has some useful information for you._where_'}, + 1091: {QUEST: 'I hear that Toon Headquarters is working on a sort of Cog Radar.\x07It will let you see where the Cogs are so that it will be easier to find them.\x07That Cog Page in your Shticker Book is the key.\x07By defeating enough Cogs, you can tune in to their signals and actually track where they are.\x07Keep defeating Cogs, so you will be ready.', + COMPLETE: 'Good work! You could probably use this...', + LEAVING: ''}, + 401: {GREETING: '', + QUEST: 'Now you get to choose the next gag track you want to learn.\x07Take your time deciding, and come back here when you are ready to choose.', + INCOMPLETE_PROGRESS: 'Think about your decision before choosing.', + INCOMPLETE_WRONG_NPC: 'Think about your decision before choosing.', + COMPLETE: 'A wise decision...', + LEAVING: QuestsDefaultLeaving}, + 2201: {QUEST: 'Those sneaky Cogs are at it again.\x07_toNpcName_ has reported another missing item. Stop by and see if you can straighten it out._where_'}, + 2202: {QUEST: "Hi, _avName_. Thank goodness you're here. A mean looking Penny Pincher was just in here and he made off with an inner tube.\x07I fear they may use it for their vile purposes.\x07Please see if you can find him and bring it back.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck finding my inner tube?', + COMPLETE: 'You found my inner tube! You ARE good. Here, take your reward...'}, + 2203: {QUEST: TheCogs + ' are wreaking havoc over at the bank.\x07Go see Captain Carl and see what you can do._where_'}, + 2204: {QUEST: "Welcome aboard, matey.\x07Argh! Those rapscallion Cogs smashed my monocle and I can't sort me change without it.\x07Be a good landlubber and take this prescription to _toNpcName_ and fetch me a new one._where_", + GREETING: '', + LEAVING: ''}, + 2205: {QUEST: "What's this?\x07Oh, I'd love to fill this prescription but the Cogs have been pilfering my supplies.\x07If you can get me the eyeglass frames off a flunky I can probably help you out.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Sorry. No flunky frames, no monocle.'}, + 2206: {QUEST: 'Excellent!\x07Just a second...\x07Your prescription is filled. Please take this monocle straight to Captain Carl._where_', + GREETING: '', + LEAVING: '', + COMPLETE: "Avast Ye!\x07You're gonna earn your sea legs after all.\x07Here ye be."}, + 2207: {QUEST: "Barnacle Barbara has a Cog in her shop!\x07You'd better get over there pronto._where_"}, + 2208: {QUEST: "Gosh! You just missed him, sweetie.\x07There was a Back Stabber in here. He took my big white wig.\x07He said it was for his boss and something about 'legal precedent.'\x07If you can get it back I'd be forever grateful.", + LEAVING: '', + GREETING: '', + INCOMPLETE_PROGRESS: "Still haven't found him?\x07He's tall and has a pointy head", + COMPLETE: "You found it!?!?\x07Aren't you a darling!\x07You've more than earned this..."}, + 2209: {QUEST: 'Melville is preparing for an important voyage.\x07Pop in and see what you can do to help sort him out._where_'}, + 2210: {QUEST: "I can use your help.\x07I've been asked by Toon HQ to take a voyage and see if I can find where the Cogs are coming from.\x07I'll need a few things for my ship but I don't have many Jellybeans.\x07Stop by and pick up some ballast from Alice. You'll have to do a favor for her to get it._where_", + GREETING: 'Howdy, _avName_!', + LEAVING: ''}, + 2211: {QUEST: "So Melville wants ballast, does he?\x07He still owes me for the last bushel.\x07I'll give it to you if you can clear five Micromanagers off my street.", + INCOMPLETE_PROGRESS: 'No, silly! I said FIVE micromanagers...', + GREETING: 'What can I do for you?', + LEAVING: ''}, + 2212: {QUEST: "A deal's a deal.\x07Here's your ballast for that cheapskate Melville._where_", + GREETING: 'Well, look what the cat dragged in...', + LEAVING: ''}, + 2213: {QUEST: "Excellent work. I knew she'd be reasonable.\x07Next I'll need a sailing chart from Art.\x07I don't think my credit is good there either so you'll have to work something out with him._where_", + GREETING: '', + LEAVING: ''}, + 2214: {QUEST: "Yes, I have the sea chart Melville wants.\x07And if you're willing to work for it I'll let you have it.\x07I'm trying to build an astrolabe to navigate by the stars.\x07I could use three Cog gears to build it.\x07Come back when you've found them.", + INCOMPLETE_PROGRESS: "How's it coming with those Cog gears?", + GREETING: 'Welcome!', + LEAVING: 'Good luck!'}, + 2215: {QUEST: "Ooh! These gears will do rather nicely.\x07Here's the chart. Give it to Melville with my compliments._where_", + GREETING: '', + LEAVING: '', + COMPLETE: "Well, that just about does it. I'm ready to sail!\x07I'd take you with me if you weren't so green. Take this instead."}, + 901: {QUEST: "If you're up for it Ahab could use some assistance over at his place..._where_"}, + 2902: {QUEST: "Are you the new recruit?\x07Good, good. Maybe you can help me.\x07I'm building a giant prefab crab to confuse the Cogs.\x07I could use a clovis though. Go see Claggart and bring one back, please._where_"}, + 2903: {QUEST: "Hi there!\x07Yes, I heard about the giant crab Ahab's working on.\x07The best clovis I have is a little on the dirty side though.\x07Be a sport and run it by the cleaners for me before you drop it off._where_", + LEAVING: 'Thanks!'}, + 2904: {QUEST: 'You must be the one that Claggart sent over.\x07I think I can clean that up in short order.\x07Just a minute...\x07There you are. Good as new!\x07Tell Ahab I said hello._where_'}, + 2905: {QUEST: "Ah, now this is exactly what I was looking for.\x07While you're here, I'm also going to need a very large clock spring.\x07Take a walk over to Hook's place and see if he has one._where_"}, + 2906: {QUEST: "A large spring, eh?\x07I'm sorry but the largest spring I have is still quite small.\x07Perhaps I could assemble one out of squirt gun trigger springs.\x07Bring me three of these gags and I'll see what I can do."}, + 2907: {QUEST: "Let's have a look then...\x07Smashing. Simply Smashing.\x07Sometimes I even surprise myself.\x07Here you go: one large spring for Ahab!_where_", + LEAVING: 'Bon Voyage!'}, + 2911: {QUEST: "I'd be happy to help the cause, _avName_.\x07But I'm afraid the streets are no longer safe.\x07Why don't you go take out some Cashbot Cogs and we'll talk.", + INCOMPLETE_PROGRESS: 'I still think you need to make the streets safer.'}, + 2916: {QUEST: 'Yes, I have a weight that Ahab can have.\x07I think it would be safer if you defeated a couple sellbots first though.', + INCOMPLETE_PROGRESS: 'Not yet. Defeat some more sellbots.'}, + 2921: {QUEST: "Hmmm, I suppose I could give up a weight.\x07I'd feel a lot better about it if there weren't so many Bossbot Cogs creeping around.\x07Defeat six and then come see me.", + INCOMPLETE_PROGRESS: "I don't think its safe yet..."}, + 2925: {QUEST: "All done?\x07Well, I guess it's safe enough now.\x07Here's the counter weight for Ahab._where_"}, + 2926: {QUEST: "Well, that's everything.\x07Let's see if it works.\x07Hmmm, one small problem.\x07I'm not getting any power because that Cog building is blocking my solar panel.\x07Could you retake it for me?", + INCOMPLETE_PROGRESS: 'Still no power. How about that building?', + COMPLETE: 'Super! You are one heck of a Cog crusher! Here, take this as your reward...'}, + 3200: {QUEST: "I just got a call in from _toNpcName_.\x07He's having a hard day. Maybe you can help him out!\x07Drop by and see what he needs._where_"}, + 3201: {QUEST: 'Oh, thanks for coming!\x07I need someone to take this new silk tie to _toNpcName_.\x07Would you be able to do that for me?_where_'}, + 3203: {QUEST: 'Oh, this must be the tie I ordered! Thanks!\x07It matches a pinstripe suit I just finished, right over here.\x07Hey, what happened to that suit?\x07Oh no! The Cogs must have stolen my new suit!\x07Defeat Cogs until you find my suit, and bring it back to me.', + LEAVING: '', + INCOMPLETE_PROGRESS: "Have you found my suit yet? I'm sure the Cogs took it!", + COMPLETE: 'Hooray! You found my new suit!\x07See, I told you the Cogs had it! Here is your reward...'}, + 3204: {QUEST: "_toNpcName_ just called to report a theft.\x07Why don't you stop by and see if you can sort things out?_where_"}, + 3205: {QUEST: "Hello, _avName_! Have you come to help me?\x07I just chased a Bloodsucker out of my shop. Whew! That was scary.\x07But now I can't find my scissors anywhere! I'm sure that Bloodsucker took them.\x07Find that Bloodsucker, and recover my scissors for me.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Are you still looking for my scissors?', + COMPLETE: 'My scissors! Thank you so much! Here is your reward...'}, + 3206: {QUEST: 'It sounds like _toNpcName_ is having problems with some Cogs.\x07Go see if you can help him out._where_'}, + 3207: {QUEST: 'Hi, _avName_! Thanks for coming by!\x07A bunch of Double Talkers just broke in and stole a stack of postcards from my counter.\x07Please go out and defeat all those Double Talkers to get my postcards back!', + INCOMPLETE_PROGRESS: "That's not enough postcards! Keep looking!", + COMPLETE: 'Oh, thank you! Now I can deliver the mail on time! Here is your reward...'}, + 3208: {QUEST: "We've been getting complaints from the residents lately about all of the Cold Callers.\x07See if you can defeat 10 Cold Callers to help out your fellow Toons in " + lDaisyGardens + '.'}, + 3209: {QUEST: 'Thanks for taking care of those Cold Callers!\x07But now the Telemarketers have gotten out of hand.\x07Defeat 10 Telemarketers in ' + lDaisyGardens + ' and come back here for your reward.'}, + 3247: {QUEST: "We've been getting complaints from the residents lately about all of the Bloodsuckers.\x07See if you can defeat 20 Bloodsuckers to help out your fellow Toons in " + lDaisyGardens + '.'}, + 3210: {QUEST: 'Oh no, The Squirting Flower on Maple Street just ran out of flowers!\x07Take them ten of your own squirting flowers to help out.\x07Make sure you have 10 squirting flowers in your inventory first.', + LEAVING: '', + INCOMPLETE_PROGRESS: "I need to have 10 squirting flowers. You don't have enough!"}, + 3211: {QUEST: "Oh, thank you so much! Those squirting flowers will save the day.\x07But I'm scared of the Cogs outside.\x07Can you help me out and defeat some of those Cogs?\x07Come back to me after you have defeated 20 Cogs on this street.", + INCOMPLETE_PROGRESS: 'There are still Cogs out there to defeat! Keep it up!', + COMPLETE: 'Oh, thank you! That helps a lot. Your reward is...'}, + 3212: {QUEST: '_toNpcName_ needs some help looking for something she lost.\x07Go visit her and see what you can do._where_'}, + 3213: {QUEST: 'Hi, _avName_. Can you help me?\x07I seem to have misplaced my pen. I think maybe some Cogs took it.\x07Defeat Cogs to find my stolen pen.', + INCOMPLETE_PROGRESS: 'Have you found my pen yet?'}, + 3214: {QUEST: "Yes, that's my pen! Thanks so much!\x07But while you were gone I realized my inkwell was missing too.\x07Defeat Cogs to find my inkwell.", + INCOMPLETE_PROGRESS: "I'm still looking for my inkwell!"}, + 3215: {QUEST: "Great! Now I have my pen and my inkwell back!\x07But wouldn't you know it?\x07My notepad is gone! They must have stolen it too!\x07Defeat Cogs to find my stolen notepad, and then bring it back for your reward.", + INCOMPLETE_PROGRESS: 'Any word on that notepad yet?'}, + 3216: {QUEST: "That's my notepad! Hooray! Your reward is...\x07Hey! Where did it go?\x07I had your reward right here in my office lockbox. But the whole lockbox is gone!\x07Can you believe it? Those Cogs stole your reward!\x07Defeat Cogs to recover my lockbox.\x07When you bring it back to me I'll give you your reward.", + INCOMPLETE_PROGRESS: 'Keep looking for that lockbox! It has your reward inside it!', + COMPLETE: 'Finally! I had your new gag bag in that lockbox. Here it is...'}, + 3217: {QUEST: "We've been performing some studies on Sellbot mechanics.\x07We still need to study some pieces more closely.\x07Bring us a sprocket from a Name Dropper.\x07You can catch one when the Cog is exploding."}, + 3218: {QUEST: 'Good job! Now we need a sprocket from a Glad Hander for comparison.\x07These sprockets are harder to catch, so keep trying.'}, + 3219: {QUEST: 'Great! Now we need just one more sprocket.\x07This time, we need a sprocket from a Mover & Shaker.\x07You might need to look inside some Sellbot buildings to find these Cogs.\x07When you catch one, bring it back for your reward.'}, + 3244: {QUEST: "We've been performing some studies on Lawbot mechanics.\x07We still need to study some pieces more closely.\x07Bring us a sprocket from an Ambulance Chaser.\x07You can catch one when the Cog is exploding."}, + 3245: {QUEST: 'Good job! Now we need a sprocket from a Back Stabber for comparison.\x07These sprockets are harder to catch, so keep trying.'}, + 3246: {QUEST: 'Great! Now we need just one more sprocket.\x07This time, we need a sprocket from a Spin Doctor.\x07When you catch one, bring it back for your reward.'}, + 3220: {QUEST: "I just heard that _toNpcName_ was asking around for you.\x07Why don't you drop by and see what she wants?_where_"}, + 3221: {QUEST: 'Hi, _avName_! There you are!\x07I heard you were quite an expert in squirt attacks.\x07I need someone to set a good example for all the Toons in ' + lDaisyGardens + '.\x07Use your squirt attacks to defeat a bunch of Cogs.\x07Encourage your friends to use squirt too.\x07When you have defeated 20 Cogs, come back here for a reward!'}, + 3222: {QUEST: "It's time to demonstrate your Toonmanship.\x07If you successfully reclaim a number of Cog buildings, you'll earn the right to carry three quests.\x07First, defeat any two Cog buildings.\x07Feel free to call on your friends to help you out."}, + 3223: {QUEST: 'Great job on those buildings!\x07Now, defeat two more buildings.\x07These buildings must be at least two stories high, or higher.'}, + 3224: {QUEST: 'Fantastic!\x07Now just defeat two more buildings.\x07These buildings must be at least three stories high.\x07When you finish, come back for your reward!', + COMPLETE: 'You did it, _avName_!\x07You demonstrated your superior Toonmanship.', + GREETING: ''}, + 3225: {QUEST: "_toNpcName_ says she needs some help.\x07Why don't you go see what you can do to help out?_where_"}, + 3235: {QUEST: "Oh, this is the salad I ordered!\x07Thank you for bringing it to me.\x07All those Cogs must have frightened away _toNpcName_'s regular delivery person again.\x07Why don't you do us a favor and defeat some of the Cogs out there?\x07Defeat 10 Cogs in " + lDaisyGardens + ' and then report back to _toNpcName_.', + INCOMPLETE_PROGRESS: "You're working on defeating Cogs for me?\x07That's wonderful! Keep up the good work!", + COMPLETE: 'Oh, thank you so much for defeating those Cogs!\x07Now maybe I can keep my regular delivery schedule.\x07Your reward is...', + INCOMPLETE_WRONG_NPC: "Go tell _toNpcName_ about the Cogs you've defeated._where_"}, + 3236: {QUEST: 'There are far too many Lawbots out there.\x07You can do your part to help!\x07Defeat 3 Lawbot buildings.'}, + 3237: {QUEST: 'Great job on those Lawbot buildings!\x07But now there are too many Sellbots!\x07Defeat 3 Sellbot buildings, then come back for your reward.'}, + 3238: {QUEST: 'Oh no! A "Mingler" Cog has stolen the Key to ' + lDaisyGardens + '!\x07See if you can recover it.\x07Remember, The Mingler can be found only inside Sellbot buildings.'}, + 3239: {QUEST: "You found a key all right, but it isn't the right one!\x07We need the Key to " + lDaisyGardens + '.\x07Keep looking! A "Mingler" Cog still has it!'}, + 3242: {QUEST: 'Oh no! A Legal Eagle Cog has stolen the Key to ' + lDaisyGardens + '!\x07See if you can recover it.\x07Remember, Legal Eagles can be found only inside Lawbot buildings.'}, + 3243: {QUEST: "You found a key all right, but it isn't the right one!\x07We need the Key to " + lDaisyGardens + '.\x07Keep looking! A Legal Eagle Cog still has it!'}, + 3240: {QUEST: "I've just heard from _toNpcName_ that a Legal Eagle stole a bag of his bird seed.\x07Defeat Legal Eagles until you recover Bud's bird seed, and take it to him.\x07Legal Eagles are only found inside Lawbot buildings._where_", + COMPLETE: 'Oh, thank you so much for finding my bird seed!\x07Your reward is...', + INCOMPLETE_WRONG_NPC: 'Good job getting that bird seed back!\x07Now take it to _toNpcName_._where_'}, + 3241: {QUEST: 'Some of the Cog buildings out there are getting too tall for our comfort.\x07See if you can bring down some of the tallest buildings.\x07Rescue 5 3-story buildings or taller and come back for your reward.'}, + 3250: {QUEST: 'Detective Lima over on Oak Street has heard some reports of a Sellbot Headquarters.\x07Head over there and help her investigate.'}, + 3251: {QUEST: "There is something strange going on around here.\x07There are so many Sellbots!\x07I've heard they have organized their own headquarters at the end of this street.\x07Head down the street and see if you can get to the bottom of this.\x07Find Sellbot Cogs in their headquarters, defeat 5 of them, and report back."}, + 3252: {QUEST: "Ok, spill the beans.\x07What's that you say?\x07Sellbot Headquarters?? Oh no!!! Something must be done.\x07We must notify Judge McIntosh - she'll know what to do.\x07Go at once and tell her what you have found out. She's just down the street."}, + 3253: {QUEST: "Yes, can I help you? I'm very busy.\x07Eh? Cog Headquarters?\x07Eh? Nonsense. That could never happen.\x07You must be mistaken. Preposterous.\x07Eh? Don't argue with me.\x07Ok then, bring back some proof.\x07If Sellbots really are building this Cog HQ, any Cog there will be carrying blueprints.\x07Cogs love paperwork, you know?\x07Defeat Sellbots in there until you find blueprints.\x07Bring them back here and maybe I'll believe you."}, + 3254: {QUEST: "You again, eh? Blueprints? You have them?\x07Let me see those! Hmmm... A factory?\x07That must be where they are building the Sellbots... And what's this?\x07Yes, just what I suspected. I knew it all along.\x07They are building a Sellbot Cog Headquarters.\x07This is not good. Must make some phone calls. Very busy. Goodbye!\x07Eh? Oh yes, take these blueprints back to Detective Lima.\x07She can make more sense of them.", + COMPLETE: "What did Judge McIntosh say?\x07We were right? Oh no. Let's see those blueprints.\x07Hmmm... Looks like Sellbots constructed a factory with machinery for building Cogs.\x07Sounds very dangerous. Stay out until you have more Laff points.\x07When you have more Laff points, we have much more to learn about Sellbot HQ.\x07For now, nice work, here is your reward."}, + 3255: {QUEST: '_toNpcName_ is investigating Sellbot Headquarters.\x07Go see if you can help._where_'}, + 3256: {QUEST: '_toNpcName_ is investigating Sellbot Headquarters.\x07Go see if you can help._where_'}, + 3257: {QUEST: '_toNpcName_ is investigating Sellbot Headquarters.\x07Go see if you can help._where_'}, + 3258: {QUEST: 'There is much confusion about what the Cogs are up to in their new headquarters.\x07I need you to bring back some information directly from them.\x07If we can get four internal memos from Sellbots inside their HQ, that will clear things up.\x07Bring back your first memo to me so we can learn more.'}, + 3259: {QUEST: 'Great! This let\'s see what the memo says....\x07"Attn Sellbots:"\x07"I\'ll be in my office at the top of Sellbot Towers promoting Cogs to higher levels."\x07"When you earn enough merits enter the elevator in the lobby to see me."\x07"Break time\'s over - back to work!"\x07"Signed, Sellbot V.P."\x07Aha.... Flippy will want to see this. I\'ll send it to him right now.\x07Please go get your second memo and bring it back.'}, + 3260: {QUEST: 'Oh good, you\'re back. Let\'s see what you found....\x07"Attn Sellbots:"\x07"Sellbot Towers has installed a new security system to keep all Toons out."\x07"Toons caught in Sellbot Towers will be detained for questioning."\x07"Please meet in the lobby for appetizers to discuss."\x07"Signed, Mingler"\x07Very interesting... I\'ll pass on this information immediately.\x07Please bring a third memo back.'}, + 3261: {QUEST: 'Excellent job _avName_! What does the memo say?\x07"Attn Sellbots:"\x07"Toons have somehow found a way to infiltrate Sellbot Towers."\x07"I\'ll call you tonight during dinner to give you the details."\x07"Signed, Telemarketer"\x07Hmmm... I wonder how Toons are breaking in....\x07Please bring back one more memo and I think we\'ll have enough info for now.', + COMPLETE: 'I knew you could do it! Ok, the memo says....\x07"Attn Sellbots:"\x07"I was having lunch with Mr. Hollywood yesterday."\x07"He reports that the V.P. is very busy these days."\x07"He will only be taking appointments from Cogs that deserve a promotion."\x07"Forgot to mention, Gladhander is golfing with me on Sunday."\x07"Signed, Name Dropper"\x07Well... _avName_, this has been very helpful.\x07Here is your reward.'}, + 3262: {QUEST: "_toNpcName_ has some new information about the Sellbot HQ Factory.\x07Go see what he's got._where_"}, + 3263: {GREETING: 'Hi buddy!', + QUEST: 'I\'m Coach Zucchini, but you can just call me Coach Z.\x07I put the "squash" in squash and stretch, if you know what I mean.\x07Listen, Sellbots have finished an enormous factory to pump out Sellbots 24 hours a day.\x07Get a group of Toon buddies together and squash the factory!\x07Inside Sellbot HQ, look for the tunnel to the Factory then board the Factory elevator.\x07Make sure you have full gags, full Laff points, and some strong Toons as guides.\x07Defeat the Foreman inside the factory to slow the Sellbot progress.\x07Sounds like a real workout, if you know what I mean.', + LEAVING: 'See ya buddy!', + COMPLETE: 'Hey buddy, nice work on that Factory!\x07Looks like you found part of a Cog suit.\x07It must be left over from their Cog manufacturing process.\x07That may come in handy. Keep collecting these when you have spare time.\x07Maybe when you collect an entire Cog suit it could be useful for something....'}, + 4001: {GREETING: '', + QUEST: 'Now you get to choose the next gag track you want to learn.\x07Take your time deciding, and come back here when you are ready to choose.', + INCOMPLETE_PROGRESS: 'Think about your decision before choosing.', + INCOMPLETE_WRONG_NPC: 'Think about your decision before choosing.', + COMPLETE: 'A wise decision...', + LEAVING: QuestsDefaultLeaving}, + 4002: {GREETING: '', + QUEST: 'Now you get to choose the next gag track you want to learn.\x07Take your time deciding, and come back here when you are ready to choose.', + INCOMPLETE_PROGRESS: 'Think about your decision before choosing.', + INCOMPLETE_WRONG_NPC: 'Think about your decision before choosing.', + COMPLETE: 'A wise decision...', + LEAVING: QuestsDefaultLeaving}, + 4200: {QUEST: "I bet Tom could use some help with some research he's doing._where_"}, + 4201: {GREETING: 'Howdy!', + QUEST: "I'm very concerned about a rash of musical instrument theft.\x07I'm conducting a survey among my fellow merchants.\x07Perhaps I can find a pattern to help me crack this case.\x07Stop by and ask Tina for a concertina inventory._where_"}, + 4202: {QUEST: 'Yes, I talked to Tom this morning.\x07I have the inventory right here.\x07Bring it right back to him, ok?_where_'}, + 4203: {QUEST: "Great! One down...\x07Now swing by and get Yuki's._where_"}, + 4204: {QUEST: 'Oh! The inventory!\x07I forgot all about it.\x07I bet I can have it done by the time you defeat 10 Cogs.\x07Stop in after that and I promise it will be ready.', + INCOMPLETE_PROGRESS: '31, 32... DOH!\x07You made me lose count!', + GREETING: ''}, + 4205: {QUEST: 'Ah, there you are.\x07Thanks for giving me some time.\x07Take this to Tom and tell him I said Hello._where_'}, + 4206: {QUEST: "Hmmm, very interesting.\x07Now we are getting somewhere.\x07Ok, the last inventory is Fifi's._where_"}, + 4207: {QUEST: "Inventory?\x07How can I do an inventory if I don't have the form?\x07Go see Cleff and see if he has one for me._where_", + INCOMPLETE_PROGRESS: 'Any sign of that form yet?'}, + 4208: {QUEST: "Sure I got an inventory form, mon!\x07But dey ain't free, you know.\x07I'll tell you woht. I trade you for a whole cream pie.", + GREETING: 'Hey, mon!', + LEAVING: 'Cool runnings...', + INCOMPLETE_PROGRESS: "A slice won't do.\x07I be hungry, mon. I need de WHOLE pie."}, + 4209: {GREETING: '', + QUEST: 'Mmmm...\x07Dem mighty nice!\x07Here be your form for Fifi._where_'}, + 4210: {GREETING: '', + QUEST: "Thank you. That's a big help.\x07Let's see...Fiddles: 2\x07All done! Off you go!_where_", + COMPLETE: "Great work, _avName_.\x07I'm sure I'll get to the bottom of these thefts now.\x07Why don't you get to the bottom of this!"}, + 4211: {QUEST: 'Say, Dr. Fret keeps calling every five minutes. Can you go see what his problem is?_where_'}, + 4212: {QUEST: "Whew! I'm glad Toon HQ finally sent somebody.\x07I haven't had a customer in days.\x07It's these darned Number Crunchers every where.\x07I think they are teaching our residents bad oral hygiene.\x07Defeat ten of them and let's see if business picks up.", + INCOMPLETE_PROGRESS: 'Still no customers. But keep it up!'}, + 4213: {QUEST: "You know maybe it wasn't the Number Crunchers after all.\x07Maybe it's just the Cashbots in general.\x07Take out twenty of them and hopefully someone will come in for at least a checkup.", + INCOMPLETE_PROGRESS: "I know twenty is a lot. But I'm sure it's going to pay off in spades."}, + 4214: {GREETING: '', + LEAVING: '', + QUEST: "I just don't understand it!\x07Still not a SINGLE customer.\x07Maybe we need to go to the source.\x07Try reclaiming a Cashbot Cog building.\x07That Should do the trick...", + INCOMPLETE_PROGRESS: 'Oh, please! Just one little building...', + COMPLETE: "Still not a soul in here.\x07But you know, come to think of it.\x07I didn't have any customers before the Cogs invaded either!\x07I really appreciate all your help though.\x07This should help you get around."}, + 4215: {QUEST: "Anna desperately needs someone to help her.\x07Why don't you drop in and see what you can do._where_"}, + 4216: {QUEST: "Thanks for coming so quickly!\x07Seems like the Cogs have made off with several of my customers' cruise tickets.\x07Yuki said she saw a Glad Hander leaving here with his glad hands full of them.\x07See if you can get Lumber Jack's ticket to Alaska back.", + INCOMPLETE_PROGRESS: 'Those Glad Handers could be anywhere now...'}, + 4217: {QUEST: "Oh, great. You found it!\x07Now be a trooper and run in by Jack's for me, would you?_where_"}, + 4218: {QUEST: "Great Googely Moogely!\x07Alaska here I come!\x07I can't take these infernal Cogs anymore.\x07Say, I think Anna needs you again._where_"}, + 4219: {QUEST: "Yup, you guessed it.\x07I need you to shake down those pesky Glad Handers for Tabitha's ticket to Jazzfest.\x07You know the procedure...", + INCOMPLETE_PROGRESS: "There's more out there somewhere..."}, + 4220: {QUEST: 'Sweet!\x07Could you swing this one by his place for me too?_where_'}, + 4221: {GREETING: '', + LEAVING: 'Be cool...', + QUEST: "Cool, daddio!\x07Now I'm in fat city, _avName_.\x07Before you split, you better go check out Anna Banana again..._where_"}, + 4222: {QUEST: "This is the last one, I promise!\x07Now you are looking for Barry's ticket to the big singing contest.", + INCOMPLETE_PROGRESS: "C'mon, _avName_.\x07Barry is counting on you."}, + 4223: {QUEST: "This should put a smile on Barry's face._where_"}, + 4224: {GREETING: '', + LEAVING: '', + QUEST: 'Hello, Hello, HELLO!\x07Terrific!\x07I just know me and the boys are going to clean up this year.\x07Anna says to swing back by and get your reward._where_\x07Goodbye, Goodbye, GOODBYE!', + COMPLETE: 'Thanks for all your help, _avName_.\x07You really are an asset here in Toontown.\x07Speaking of assets...'}, + 902: {QUEST: 'Go see Leo.\x07He needs someone to deliver a message for him._where_'}, + 4903: {QUEST: 'Dude!\x07My castanets are all cloudy and I have a big show tonight.\x07Take them to Carlos and see if he can polish them up._where_'}, + 4904: {QUEST: 'Jes, I tink I can polish dees.\x07But I need soma de blue ink from de squid.', + GREETING: 'Hola!', + LEAVING: 'Adios!', + INCOMPLETE_PROGRESS: "Juo can find de squid wherever dere's a fishing pier"}, + 4905: {QUEST: "Jes! Dat's it!\x07Now I need a leetle time to polish dees.\x07Why don juo go takeover a one story beelding while I work?", + GREETING: 'Hola!', + LEAVING: 'Adios!', + INCOMPLETE_PROGRESS: 'Jest anodder minute...'}, + 4906: {QUEST: 'Bery good!\x07Here are de castanets for Leo._where_'}, + 4907: {GREETING: '', + QUEST: "Cool, dude!\x07They look awesome!\x07Now I need you to get a copy of the lyrics to 'A Beat Christmas' from Hedy._where_"}, + 4908: {QUEST: "Hello there!\x07Hmmm, I don't have a copy of that song handy.\x07If you give me a little while I could transcribe it from memory.\x07Why don't you run along and reclaim a two story building while I write?"}, + 4909: {QUEST: "I'm sorry.\x07My memory is getting a little fuzzy.\x07If you go reclaim a three story building I'm sure I'll be done when you get back..."}, + 4910: {QUEST: 'All done!\x07Sorry it took so long.\x07Take this back to Leo._where_', + GREETING: '', + COMPLETE: 'Awesome, dude!\x07My concert is gonna rock!\x07Speaking of rock, you can rock some Cogs with this...'}, + 5247: {QUEST: 'This neighborhood is pretty tough...\x07You might want to learn some new tricks.\x07_toNpcName_ taught me everything I know, so maybe he can help you too._where_'}, + 5248: {GREETING: 'Ahh, yes.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'You appear to be struggling with my assignment.', + QUEST: 'Ahh, so welcome, new apprentice.\x07I know all there is to know about the pie game.\x07But before we can begin your training, a small demonstration is necessary.\x07Go out and defeat ten of the largest Cogs.'}, + 5249: {GREETING: 'Mmmmm.', + QUEST: 'Excellent!\x07Now demonstrate your skill as a fisherman.\x07I dropped three fuzzy dice in the pond yesterday.\x07Fish them out and bring them to me.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'It seems you may not be so clever with the rod and reel.'}, + 5250: {GREETING: '', + LEAVING: '', + QUEST: 'Aha! These dice will look great hanging from the rearview mirror of my ox cart!\x07Now, show me that you can tell your enemies from one another.\x07Return when you have restored two of the tallest Lawbot buildings.', + INCOMPLETE_PROGRESS: 'Do the buildings give you trouble?'}, + 5258: {GREETING: '', + LEAVING: '', + QUEST: 'Aha! These dice will look great hanging from the rearview mirror of my ox cart!\x07Now, show me that you can tell your enemies from one another.\x07Return when you have restored two of the tallest Bossbot buildings.', + INCOMPLETE_PROGRESS: 'Do the buildings give you trouble?'}, + 5259: {GREETING: '', + LEAVING: '', + QUEST: 'Aha! These dice will look great hanging from the rearview mirror of my ox cart!\x07Now, show me that you can tell your enemies from one another.\x07Return when you have restored two of the tallest Cashbot buildings.', + INCOMPLETE_PROGRESS: 'Do the buildings give you trouble?'}, + 5260: {GREETING: '', + LEAVING: '', + QUEST: 'Aha! These dice will look great hanging from the rearview mirror of my ox cart!\x07Now, show me that you can tell your enemies from one another.\x07Return when you have restored two of the tallest Sellbot buildings.', + INCOMPLETE_PROGRESS: 'Do the buildings give you trouble?'}, + 5200: {QUEST: 'Those sneaky Cogs are at it again.\x07_toNpcName_ has reported another missing item. Stop by and see if you can straighten it out._where_'}, + 5201: {GREETING: '', + QUEST: 'Hi, _avName_. I reckon I should thank you for coming.\x07A group of those Head Hunters came in and stole my soccer ball.\x07The leader told me that I had to make some cutbacks and just grabbed it away from me!\x07Can you get my ball back?', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck finding my soccer ball?', + COMPLETE: 'Yeehaw! You found it!\x07 Here, take your reward...'}, + 5261: {GREETING: '', + QUEST: 'Hi, _avName_. I reckon I should thank you for coming.\x07A group of those Two-Faces came in and stole my soccer ball.\x07The leader told me that I had to make some cutbacks and just grabbed it away from me!\x07Can you get my ball back?', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck finding my soccer ball?', + COMPLETE: 'Yeehaw! You found it!\x07 Here, take your reward...'}, + 5262: {GREETING: '', + QUEST: 'Hi, _avName_. I reckon I should thank you for coming.\x07A group of those Money Bags came in and stole my soccer ball.\x07The leader told me that I had to make some cutbacks and just grabbed it away from me!\x07Can you get my ball back?', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck finding my soccer ball?', + COMPLETE: 'Yeehaw! You found it!\x07 Here, take your reward...'}, + 5263: {GREETING: '', + QUEST: 'Hi, _avName_. I reckon I should thank you for coming.\x07A group of those Spin Doctors came in and stole my soccer ball.\x07The leader told me that I had to make some cutbacks and just grabbed it away from me!\x07Can you get my ball back?', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck finding my soccer ball?', + COMPLETE: 'Yeehaw! You found it!\x07 Here, take your reward...'}, + 5202: {QUEST: lTheBrrrgh + " has been overrun with some of the toughest Cogs we've seen yet.\x07You will probably want to carry more gags around here.\x07I hear _toNpcName_ may have a large bag you can use to carry more gags._where_"}, + 5203: {GREETING: 'Huh? Are you on my sledding team?', + QUEST: "What's that? You want a bag?\x07I had one somewhere around here... maybe it's in my toboggan?\x07Only... I haven't seen my toboggan since the big race!\x07Maybe one of those Cogs took it?", + LEAVING: 'Have you seen my toboggan?', + INCOMPLETE_PROGRESS: "Who are you again? Sorry, I'm a little woozy from the crash."}, + 5204: {GREETING: '', + LEAVING: '', + QUEST: "Is that my toboggan? I don't see any bag here.\x07I think Bumpy Noggin was on the team... maybe he has it?_where_"}, + 5205: {GREETING: 'Ohhh, my head!', + LEAVING: '', + QUEST: "Huh? Ted who? A bag?\x07Oh, maybe he was on our toboggan team?\x07My head hurts so much I can't think straight.\x07Could you fish me out some ice cubes from the frozen pond for my head?", + INCOMPLETE_PROGRESS: "Oww, my head's killing me! Got any ice?"}, + 5206: {GREETING: '', + LEAVING: '', + QUEST: "Ahhh, that feels much better!\x07So you're looking for Ted's bag, huh?\x07I think it ended up on Sam Simian's head after the crash._where_"}, + 5207: {GREETING: 'Eeeep!', + LEAVING: '', + QUEST: 'What is bag? Who is Bompy?\x07Me scared of buildings! You beat buildings, I give you bag!', + INCOMPLETE_PROGRESS: 'More buildings! Me still scared!', + COMPLETE: 'Ooooh! Me like you!'}, + 5208: {GREETING: '', + LEAVING: 'Eeeek!', + QUEST: 'Ooooh! Me like you!\x07Go to Ski Clinic. Bag there.'}, + 5209: {GREETING: 'Dude!', + LEAVING: 'Later!', + QUEST: "Man, that Simian Sam is crazy!\x07If you're wild like Sam, I'll give you your bag, man.\x07Go bag some Cogs for your bag, man! Hey now!", + INCOMPLETE_PROGRESS: "Are you sure you're extreme enough? Go bag some more Cogs.", + COMPLETE: "Hey, you are pretty wild! That was a heap of Cogs you bagged!\x07Here's your bag!"}, + 5210: {QUEST: '_toNpcName_ is secretly in love with someone in the neighborhood.\x07If you help her, she may reward you handsomely._where_'}, + 5211: {GREETING: 'Boo hoo.', + QUEST: 'I spent all last night writing a letter to the dog I love.\x07But before I could deliver it, one of those nasty Cogs with a beak came in and took it.\x07Can you get it back for me?', + LEAVING: 'Boo hoo.', + INCOMPLETE_PROGRESS: 'Please find my letter.'}, + 5264: {GREETING: 'Boo hoo.', + QUEST: 'I spent all last night writing a letter to the dog I love.\x07But before I could deliver it, one of those nasty Cogs with a fin came in and took it.\x07Can you get it back for me?', + LEAVING: 'Boo hoo.', + INCOMPLETE_PROGRESS: 'Please find my letter.'}, + 5265: {GREETING: 'Boo hoo.', + QUEST: 'I spent all last night writing a letter to the dog I love.\x07But before I could deliver it, one of those nasty Mingler Cogs came in and took it.\x07Can you get it back for me?', + LEAVING: 'Boo hoo.', + INCOMPLETE_PROGRESS: 'Please find my letter.'}, + 5266: {GREETING: 'Boo hoo.', + QUEST: 'I spent all last night writing a letter to the dog I love.\x07But before I could deliver it, one of those nasty Corporate Raiders came in and took it.\x07Can you get it back for me?', + LEAVING: 'Boo hoo.', + INCOMPLETE_PROGRESS: 'Please find my letter.'}, + 5212: {QUEST: 'Oh, thank you for finding my letter!\x07Please, please, please could you deliver it to the most handsome dog in the neighborhood?', + GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "You didn't deliver my letter, did you?"}, + 5213: {GREETING: "Charmed, I'm sure.", + QUEST: "I can't be bothered with your letter, you see.\x07All my doggies have been taken from me!\x07If you bring them back, maybe we can talk then.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'My poor little doggies!'}, + 5214: {GREETING: '', + LEAVING: 'Toodleloo!', + QUEST: "Thank you for bringing back my little beauties.\x07Let's take a look at your letter now...\nMmmm, it seems I have yet another secret admirer.\x07This calls for a trip to my dear friend Carl.\x07I'm sure you'll like him immensely._where_"}, + 5215: {GREETING: 'Heh, heh...', + LEAVING: 'Come back, yes, yes.', + INCOMPLETE_PROGRESS: "There are still some big ones around. Comes back to us when they're gone.", + QUEST: "Who sent you to us? We don't like Snootsies much, we don't...\x07But we likes Cogs even less...\x07Run the big ones off and we'll helps you we will."}, + 5216: {QUEST: 'We told you we would helps you.\x07So take this ring to the girl.', + GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'You still haves the ring???', + COMPLETE: 'Oh darrrling!!! Thank you!!!\x07Oh, and I have something special for you as well.'}, + 5217: {QUEST: 'It sounds like _toNpcName_ could use some help._where_'}, + 5218: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "I'm sure there are more Minglers around somewhere.", + QUEST: "Help!!! Help!!! I can't take it anymore!\x07Those Minglers are driving me batty!!!"}, + 5219: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "That can't be all of them. I just saw one!!!", + QUEST: "Oh, thanks, but now it's the Corporate Raiders!!!\x07You've got to help me!!!"}, + 5220: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'No, no, no there was one just here!', + QUEST: "I realize now that it's those Loan Sharks!!!\x07I thought you were going to save me!!!"}, + 5221: {GREETING: '', + LEAVING: '', + QUEST: "You know what, maybe it isn't the Cogs at all!\x07Could you ask Fanny to make me a soothing potion? Maybe that would help...._where_"}, + 5222: {LEAVING: '', + QUEST: "Oh, that Harry, he sure is a card!\x07I'll whip up something that will fix him right up!\x07Oh, I appear to be out of sardine whiskers...\x07Be a dear and run down to the pond and catch some for me.", + INCOMPLETE_PROGRESS: 'Got those whiskers for me yet?'}, + 5223: {QUEST: 'Okay. Thanks, hon.\x07Here, now take this to Harry. It should calm him right down.', + GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Go on now, take the potion to Harry.'}, + 5224: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Go get those Legal Eagles for me, will you?', + QUEST: "Oh thank goodness you're back!\x07Give me the potion, quick!!!\x07Glug, glug, glug...\x07That tasted awful!\x07You know, what, though? I feel much calmer. Now that I can think clearly, I realize that...\x07It was the Legal Eagles that were driving me crazy all this time!!!", + COMPLETE: "Oh boy! Now I can relax!\x07I'm sure there's something here I can give you. Oh, take this!"}, + 5225: {QUEST: 'Ever since the incident with the turnip bread, Grumpy Phil has been mad at _toNpcName_.\x07Maybe you could help Gus fix things between them?_where_'}, + 5226: {QUEST: 'Yeah, you probably heard Grumpy Phil is mad at me...\x07I was just trying to be nice with that turnip bread.\x07Maybe you can help cheer him up.\x07Phil really hates those Cashbot Cogs, especially their buildings.\x07If you reclaim some Cashbot buildings, it might help.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Maybe a few more buildings?'}, + 5227: {QUEST: "That's terrific! Go tell Phil what you've done._where_"}, + 5228: {QUEST: 'Oh he did, did he?\x07That Gus thinks he can get off so easy, does he?\x07Only broke my tooth, he did, with that turnip bread of his!\x07Maybe if you took my tooth to Dr. Mumbleface for me he could fix it.', + GREETING: 'Mmmmrrphh.', + LEAVING: 'Grumble, grumble.', + INCOMPLETE_PROGRESS: 'You again? I thought you were going to get my tooth fixed for me.'}, + 5229: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "I'm still working on the tooth. It will be a bit longer.", + QUEST: "Yes, that tooth looks pretty bad, alrighty.\x07Maybe I can do something, but it will be a little while.\x07Maybe you could clear some of those Cashbot Cogs off the streets while you're waiting?\x07They're scaring off my customers."}, + 5267: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "I'm still working on the tooth. It will be a bit longer.", + QUEST: "Yes, that tooth looks pretty bad, alrighty.\x07Maybe I can do something, but it will be a little while.\x07Maybe you could clear some of those Sellbot Cogs off the streets while you're waiting?\x07They're scaring off my customers."}, + 5268: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "I'm still working on the tooth. It will be a bit longer.", + QUEST: "Yes, that tooth looks pretty bad, alrighty.\x07Maybe I can do something, but it will be a little while.\x07Maybe you could clear some of those Lawbot Cogs off the streets while you're waiting?\x07They're scaring off my customers."}, + 5269: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "I'm still working on the tooth. It will be a bit longer.", + QUEST: "Yes, that tooth looks pretty bad, alrighty.\x07Maybe I can do something, but it will be a little while.\x07Maybe you could clear some of those Bossbot Cogs off the streets while you're waiting?\x07They're scaring off my customers."}, + 5230: {GREETING: '', + QUEST: "I'm glad you're back!\x07I gave up trying to fix that old tooth, and made a new gold tooth for Phil instead.\x07Unfortunately a Robber Baron came in and took it from me.\x07Maybe you can catch him if you hurry.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Did you find that tooth yet?'}, + 5270: {GREETING: '', + QUEST: "I'm glad you're back!\x07I gave up trying to fix that old tooth, and made a new gold tooth for Phil instead.\x07Unfortunately a Big Cheese came in and took it from me.\x07Maybe you can catch him if you hurry.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Did you find that tooth yet?'}, + 5271: {GREETING: '', + QUEST: "I'm glad you're back!\x07I gave up trying to fix that old tooth, and made a new gold tooth for Phil instead.\x07Unfortunately Mr. Hollywood came in and took it from me.\x07Maybe you can catch him if you hurry.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Did you find that tooth yet?'}, + 5272: {GREETING: '', + QUEST: "I'm glad you're back!\x07I gave up trying to fix that old tooth, and made a new gold tooth for Phil instead.\x07Unfortunately a Big Wig came in and took it from me.\x07Maybe you can catch him if you hurry.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Did you find that tooth yet?'}, + 5231: {QUEST: "Great, that's the tooth alrighty!\x07Why don't you just run it over to Phil for me?", + GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'I bet Phil would like to see his new tooth.'}, + 5232: {QUEST: "Oh, thanks.\x07Mmmrrrphhhh\x07How's that look, huh?\x07Okay, you can tell Gus that I forgive him.", + LEAVING: '', + GREETING: ''}, + 5233: {QUEST: "Oh, that's great to hear.\x07I figured old Phil couldn't stay mad at me.\x07As a gesture of goodwill, I baked him this Pine cone bread.\x07Could you run it over to him for me?", + GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "Better hurry. Pine cone bread is better when it's hot.", + COMPLETE: "Oh, what's this? For me?\x07Munch, munch...\x07Owwww! My tooth! That Gus Gooseburger!\x07Oh well, it wasn't your fault. Here, you can have this for your trouble."}, + 903: {QUEST: 'You may be ready to see _toNpcName_ the Blizzard Wizard for your final test._where_'}, + 5234: {GREETING: '', + QUEST: 'Aha, you are back.\x07Before we begin, we must eat.\x07Bring us some lumpy cheese for our broth.\x07Lumpy cheese can only be gathered from Big Cheese Cogs.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'We still need lumpy cheese'}, + 5278: {GREETING: '', + QUEST: 'Aha, you are back.\x07Before we begin, we must eat.\x07Bring us some caviar for our broth.\x07Caviar can only be gathered from Mr. Hollywood Cogs.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'We still need caviar'}, + 5235: {GREETING: '', + QUEST: 'A simple man eats with a simple spoon.\x07A Cog took my simple spoon, so I simply can not eat.\x07Return my spoon to me. I think a Robber Baron took it.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'I simply must have my spoon.'}, + 5279: {GREETING: '', + QUEST: 'A simple man eats with a simple spoon.\x07A Cog took my simple spoon, so I can not eat.\x07Return my spoon to me. I think a Big Wig took it.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'I simply must have my spoon.'}, + 5236: {GREETING: '', + QUEST: 'Many thanks.\x07Slurp, slurp...\x07Ahhh, now you must catch a talking toad. Try fishing in the pond.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Where is that talking toad?'}, + 5237: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'You have not yet obtained dessert.', + QUEST: "Oh, that is certainly a talking toad. Give him to me.\x07What's that you say, toad?\x07Uh huh.\x07Uh huh...\x07The toad has spoken. We need dessert.\x07Bring us some ice cream cones from _toNpcName_.\x07The toad likes red bean flavored ice cream for some reason._where_"}, + 5238: {GREETING: '', + QUEST: "So the wizard sent you. I'm sad to say we're fresh out of red bean ice cream cones.\x07You see, a bunch of Cogs came in and just took them.\x07They said they were for Mr. Hollywood, or some such nonsense.\x07I'd sure appreciate if you could round them back up for me.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Have you found all my ice cream cones yet?'}, + 5280: {GREETING: '', + QUEST: "So the wizard sent you. I'm sad to say we're fresh out of red bean ice cream cones.\x07You see, a bunch of Cogs came in and just took them.\x07They said they were for The Big Cheese, or some such nonsense.\x07I'd sure appreciate if you could round them back up for me.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Have you found all my ice cream cones yet?'}, + 5239: {QUEST: "Thanks for bringing back my ice cream cones!\x07Here's one for Lil Oldman.", + GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'You better bring that ice cream to Lil Oldman before it melts.'}, + 5240: {GREETING: '', + QUEST: 'Very good. Here you go toad...\x07Slurp, slurp...\x07Okay, now we are almost ready.\x07If you can just bring me some powder to dry my hands.\x07I think those Big Wig Cogs sometimes have powder from their wigs.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Did you find any powder?'}, + 5281: {GREETING: '', + QUEST: 'Very good. Here you go toad...\x07Slurp, slurp...\x07Okay, now we are almost ready.\x07If you can just bring me some powder to dry my hands.\x07I think those Mr. Hollywood Cogs sometimes keep powder for their noses.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Did you find any powder?'}, + 5241: {QUEST: 'Okay.\x07As I once said, to truly throw a pie, you must throw not with the hand...\x07...but with the soul.\x07I know not what that means, so I will sit and contemplate while you restore buildings.\x07Return when you have completed your task.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Your task is not yet complete.'}, + 5242: {GREETING: '', + QUEST: 'Although I still know not what I am talking about, you truly are worthy.\x07I give you a final task...\x07The talking toad would like a girlfriend.\x07Find another talking toad. The toad has spoken.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Where is that other talking toad?', + COMPLETE: 'Whew! I am tired from all this effort. I must rest now.\x07Here, take your reward and be off.'}, + 5243: {QUEST: 'Sweaty Pete is starting to stink up the street.\x07Can you talk him into taking a shower or something?_where_'}, + 5244: {GREETING: '', + QUEST: "Yeah, I guess I do work up quite a sweat in here.\x07Mmmm, maybe if I could fix that leaky pipe in my shower...\x07I figure a gear from one of those tiny Cogs would do the trick.\x07Go find a gear from a Micromanager and we'll try it.", + LEAVING: '', + INCOMPLETE_PROGRESS: "Where's that gear you were going to get?"}, + 5245: {GREETING: '', + QUEST: 'Yup, that seemed to do the trick.\x07But I get lonely when I shower...\x07Could you go fish me up a rubber ducky to keep me company?', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck with that duck?'}, + 5246: {QUEST: "The ducky's great, but...\x07All those buildings around here make me nervous.\x07I'd feel a lot more relaxed if there were fewer buildings around.", + LEAVING: '', + COMPLETE: "Okay, I'll shower up now. And here's something for you too.", + INCOMPLETE_PROGRESS: "I'm still worried about buildings."}, + 5251: {QUEST: 'Lounge Lassard is supposed to be playing a gig tonight.\x07I hear he might be having some trouble with his equipment._where_'}, + 5252: {GREETING: '', + QUEST: 'Oh yeah! I could sure use some help.\x07Those Cogs came in and swiped all my gear while I was unloading the van.\x07Can you give me a hand and get back my microphone?', + LEAVING: '', + INCOMPLETE_PROGRESS: "Hey man, I can't sing without my microphone."}, + 5253: {GREETING: '', + QUEST: "Yeah, that's my microphone all right.\x07Thanks for getting it for me, but...\x07I really need my keyboard so I can tickle the ivories.\x07I think one of those Corporate Raiders got my keyboard.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding my keboard?'}, + 5273: {GREETING: '', + QUEST: "Yeah, that's my microphone all right.\x07Thanks for getting it for me, but...\x07I really need my keyboard so I can tickle the ivories.\x07I think one of those Minglers got my keyboard.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding my keboard?'}, + 5274: {GREETING: '', + QUEST: "Yeah, that's my microphone all right.\x07Thanks for getting it for me, but...\x07I really need my keyboard so I can tickle the ivories.\x07I think one of those Loan Sharks got my keyboard.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding my keboard?'}, + 5275: {GREETING: '', + QUEST: "Yeah, that's my microphone all right.\x07Thanks for getting it for me, but...\x07I really need my keyboard so I can tickle the ivories.\x07I think one of those Legal Eagles got my keyboard.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding my keboard?'}, + 5254: {GREETING: '', + QUEST: "All right! Now I'm in business.\x07If only they hadn't taken my platform shoes...\x07Those shoes probably ended up with a Mr. Hollywood, if I had to guess.", + LEAVING: '', + COMPLETE: "Allright!! I'm ready now.\x07Hello Brrrgh!!!\x07Huh? Where is everyone?\x07Okay, take this and round me up some fans, huh?", + INCOMPLETE_PROGRESS: "I can't perform barefoot, can I?"}, + 5282: {GREETING: '', + QUEST: "All right! Now I'm in business.\x07If only they hadn't taken my platform shoes...\x07Those shoes probably ended up with a Big Cheese, if I had to guess.", + LEAVING: '', + COMPLETE: "Allright!! I'm ready now.\x07Hello Brrrgh!!!\x07Huh? Where is everyone?\x07Okay, take this and round me up some fans, huh?", + INCOMPLETE_PROGRESS: "I can't perform barefoot, can I?"}, + 5283: {GREETING: '', + QUEST: "All right! Now I'm in business.\x07If only they hadn't taken my platform shoes...\x07Those shoes probably ended up with a Robber Baron, if I had to guess.", + LEAVING: '', + COMPLETE: "Allright!! I'm ready now.\x07Hello Brrrgh!!!\x07Huh? Where is everyone?\x07Okay, take this and round me up some fans, huh?", + INCOMPLETE_PROGRESS: "I can't perform barefoot, can I?"}, + 5284: {GREETING: '', + QUEST: "All right! Now I'm in business.\x07If only they hadn't taken my platform shoes...\x07Those shoes probably ended up with a Big Wig, if I had to guess.", + LEAVING: '', + COMPLETE: "Allright!! I'm ready now.\x07Hello Brrrgh!!!\x07Huh? Where is everyone?\x07Okay, take this and round me up some fans, huh?", + INCOMPLETE_PROGRESS: "I can't perform barefoot, can I?"}, + 5255: {QUEST: 'You look like you could use more Laff points.\x07Maybe _toNpcName_ could sort you out._where_'}, + 5256: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "A deal's a deal.", + QUEST: "So you're looking for Laff points, huh?\x07Have I got a deal for you!\x07Simply take care of a few Bossbot Cogs for me...\x07And I'll make it worth your while."}, + 5276: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "A deal's a deal.", + QUEST: "So you're looking for Laff points, huh?\x07Have I got a deal for you!\x07Simply take care of a few Lawbot Cogs for me...\x07And I'll make it worth your while."}, + 5257: {GREETING: '', + LEAVING: '', + COMPLETE: "Okay, but I'm certain I told you to round up some Lawbot Cogs.\x07Well, if you say so, but you owe me one.", + INCOMPLETE_PROGRESS: "I don't think you're done yet.", + QUEST: "You say you're done? Defeated all the Cogs?\x07You must have misunderstood, our deal was for Sellbot Cogs.\x07I'm sure I told you to defeat some Sellbot Cogs for me."}, + 5277: {GREETING: '', + LEAVING: '', + COMPLETE: "Okay, but I'm certain I told you to round up some Lawbot Cogs.\x07Well, if you say so, but you owe me one.", + INCOMPLETE_PROGRESS: "I don't think you're done yet.", + QUEST: "You say you're done? Defeated all the Cogs?\x07You must have misunderstood, our deal was for Cashbot Cogs.\x07I'm sure I told you to defeat some Cashbot Cogs for me."}, + 5301: {QUEST: "I can't help you with Laff points, but maybe _toNpcName_ will cut you a deal.\x07He's a little on tempermental side though..._where_"}, + 5302: {GREETING: '', + LEAVING: '', + COMPLETE: "I told you what?!?!\x07Thanks a bunch! Here's your Laff point!", + INCOMPLETE_PROGRESS: 'Hi!\x07What are you doing in here again!', + QUEST: 'A Laff point? I dont think so!\x07Sure, but only if you clear out some of these pesky Lawbots first.'}, + 5303: {QUEST: lTheBrrrgh + " is teeming with very dangerous Cogs.\x07If I were you, I'd carry more gags around here.\x07I hear _toNpcName_ can make you a large bag if you are willing to do the legwork._where_"}, + 5304: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'There should be plenty of Lawbots out there.\x07So get to it!', + QUEST: "A bigger bag?\x07I could probably whip one up for ya.\x07I'll need some yarn though.\x07Some Lawbots made off with mine yesterday morning."}, + 5305: {GREETING: 'Howdy!', + LEAVING: '', + INCOMPLETE_PROGRESS: "Go get some more cogs.\x07This color hasn't taken yet.", + QUEST: "That there's some fine yarn!\x07Not my first choice of color though.\x07Tell you what...\x07You go out there and beat up some of the tougher cogs...\x07And I'll get to work dyeing this yarn."}, + 5306: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'They gotta be down there somewhere...', + QUEST: "Well, the yarn is all dyed. But we've got a small problem.\x07I can't find my knitting needles anywhere.\x07Last place I saw them was down at the pond."}, + 5307: {GREETING: '', + LEAVING: 'Much obliged!', + INCOMPLETE_PROGRESS: "Rome wasn't knit in a day!", + QUEST: "Those are my needles alright.\x07While I'm knitting, why don't you go clear some of them big buildings?", + COMPLETE: "Great work!\x07And speaking of great work...\x07Here's your new bag!"}, + 5308: {GREETING: '', + LEAVING: '', + QUEST: 'I hear _toNpcName_ is having some legal troubles.\x07Can you stop by and check it out?_where_'}, + 5309: {GREETING: "I'm glad you're here...", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Please hurry! The street is crawling with them!', + QUEST: "The Lawbots have really taken over out there.\x07I'm afraid they are going to take me to court.\x07Do you think you could help get them off of this street?"}, + 5310: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'I think I hear them coming for me...', + QUEST: "Thanks. I feel a little better now.\x07 But there is one more thing...\x07Could you drop by _toNpcName_'s and get me an alibi?_where_"}, + 5311: {GREETING: 'WHAAAA!!!!', + LEAVING: '', + INCOMPLETE_PROGRESS: "I can't help him if you can't find it!", + QUEST: "Alibi?! Why that's a great idea!\x07You'd better make it two!\x07I bet a Legal Eagle would have some..."}, + 5312: {GREETING: 'Finally!', + LEAVING: '', + INCOMPLETE_PROGRESS: '', + COMPLETE: "Whew! Am I ever relieved to have this.\x07Here's your reward...", + QUEST: "Super! You'd better run these back to _toNpcName_!"}, + 6201: {QUEST: 'Powers Erge needs some help. Could you drop by and lend her a hand?_where_'}, + 6202: {GREETING: '', + LEAVING: '', + QUEST: "Oh, a customer! Great! What can I do for you?\x07What do you mean, what can you do for me? OH! You're not a customer.\x07I remember now. You're here to help with those dreadful Cogs.\x07Well I could certainly use the help even if you're not a customer.\x07If you clean up the streets a bit, I'll have a little something for you.", + INCOMPLETE_PROGRESS: "If you don't want electricity, I can't help you until you defeat those Cogs.", + COMPLETE: "Good job on those Cogs, _avName_.\x07Now, are you sure I can't interest you in some electricity? Might come in handy....\x07No? OK, suit yourself.\x07Hunh? Oh yeah, I remember. Here ya go. This is sure to help with those nasty Cogs.\x07Keep up the good work!"}, + 6206: {QUEST: "Well, _avName_, I don't have anything for you right now.\x07Wait! I think Susan Siesta was looking for help. Why don't you go see her?_where_"}, + 6207: {GREETING: '', + LEAVING: '', + QUEST: "I'll never get rich with those darn Cogs driving away all my business!\x07You've got to help me, _avName_.\x07Clear out a few Cog buildings for the sake of the neighborhood and I'll add to your riches.", + INCOMPLETE_PROGRESS: "Poor me! Can't you get rid of those buildings?", + COMPLETE: "Now I'll be in the money! I can see it now!\x07I'll spend all my time fishing. Now, let me enrich your life a little.\x07There you go!"}, + 6211: {QUEST: 'Hey _avName_! I heard Lawful Linda was looking for you.\x07You should stop by and pay her a visit._where_'}, + 6212: {GREETING: '', + LEAVING: '', + QUEST: "Hi there! Wow, am I glad to see you!\x07I've been working on this answering machine in my spare time but I'm short a couple of parts.\x07I need three more rods and the ones from Bean Counters seem to work pretty well.\x07Could you see if you could find some rods for me?", + INCOMPLETE_PROGRESS: 'Still trying to find those rods?'}, + 6213: {GREETING: '', + LEAVING: '', + QUEST: "Oh, those will do nicely.\x07That's funny. I was sure I had a spare drive belt around here but I can't find it.\x07Could you please get one from a Money Bags for me? Thanks!", + INCOMPLETE: "Well, I can't help you until I get that drive belt."}, + 6214: {GREETING: '', + LEAVING: '', + QUEST: "Ah, that's it. Now it should run like a charm.\x07Where'd my pliers go? I can't tighten this up without the pliers.\x07Maybe pincers from a Penny Pincher would do the job?\x07If you'd go find one, I could give you a little something to help you with those Cogs.", + INCOMPLETE_PROGRESS: 'No pincers yet, hunh? Keep looking.', + COMPLETE: "Great! Now I'll just tighten this up.\x07It seems to be working now. Back in business!\x07Well, except that we don't have a phone. But I'm glad for the help, anyway.\x07I think this'll help you out with those Cogs. Good luck!"}, + 6221: {QUEST: 'I heard Rocco was looking for help. See what you can do for him._where_'}, + 6222: {GREETING: '', + LEAVING: '', + QUEST: "Yo! Youse came to da right place. I ain't too happy.\x07Yeah, I was lookin for some help wid dose Cogs. Dey always come and boss me around.\x07If you can retire some of dem Bossbots, I'll make it worth your while.", + INCOMPLETE_PROGRESS: "Hey, _avName_, what's up wid youse?\x07You gotta keep after dem Bossbots. We got a deal, remember?\x07Rocco always keeps his word.", + COMPLETE: "Yo, _avName_! Youse ok in my book.\x07Dem Bossbots ain't so bossy now, is they?\x07Here ya go! A nice big boost. Now, you stay outta trouble, ya hear!"}, + 6231: {QUEST: 'Nat over on Pajama Place heard rumors about a Cashbot Headquarters.\x07Head over there and see if you can help him out._where_'}, + 6232: {GREETING: '', + LEAVING: '', + QUEST: "I got a nibble about some strange goings on.\x07Well, maybe it's the fleas but something is going on anyway.\x07All these Cashbots!\x07I think they've opened another headquarters right off Pajama Place.\x07P.J. knows his way around.\x07Go see _toNpcName_ _where_ Ask him if he's heard anything.", + INCOMPLETE_PROGRESS: "You haven't seen P.J. yet? What's keeping you?\x07Oh, these darn fleas!"}, + 6233: {GREETING: '', + LEAVING: '', + QUEST: "Hey there _avName_, where are you headed?\x07Cashbot Headquarters?? I haven't seen anything.\x07Could you go to the end of Pajama Place and see if it's true?\x07Find some Cashbot Cogs in their headquarters, defeat a few of them, and come tell me about it.", + INCOMPLETE_PROGRESS: "Found the HQ yet? You'll need to defeat some Cashbots there to scope it out."}, + 6234: {GREETING: '', + LEAVING: '', + QUEST: "What?! There really IS a Cashbot HQ?\x07You better go tell Nat right away!\x07Who would have guessed there'd be a Cog HQ right down the street from him?", + INCOMPLETE_PROGRESS: "What did Nat have to say? You haven't seen him yet?"}, + 6235: {GREETING: '', + LEAVING: '', + QUEST: "So, I'm itching to hear what P.J. had to say.\x07Hmm...we need more information about this Cog business but I've got to get rid of these fleas!\x07I know! YOU can go find out more!\x07Go defeat Cashbots at the HQ until you find some plans then come right back!", + INCOMPLETE_PROGRESS: "No plans yet? Keep searching those Cogs!\x07They're bound to have some plans!", + COMPLETE: "You got the plans?\x07Great! Let's see what they say.\x07I see... the Cashbots built a Mint to make Cogbucks.\x07It must be FULL of Cashbots. We should find out more about this.\x07Maybe if you had a disguise. Hmmm...wait! I think I've got part of a Cog suit here somewhere....\x07Here it is! Why don't you take this for your trouble? Thanks again for your help!"}, + 6241: {QUEST: "The Countess has been looking everywhere for you! Please pay her a visit so she'll stop calling._where_"}, + 6242: {GREETING: '', + LEAVING: '', + QUEST: "_avName_, I'm counting on you to help me!\x07You see, these Cogs are making so much noise that I simply can't concentrate.\x07I keep losing count of my sheep!\x07If you'll cut down on the noise, I'll help you out too! You can count on it!\x07Now, where was I? Right, one hundred thirty-six, one hundred thirty-seven....", + INCOMPLETE_PROGRESS: "Four hundred forty-two...four hundred forty-three...\x07What? You're back already? But it's still so noisy!\x07Oh no, I've lost count again.\x07 One...two...three....", + COMPLETE: "Five hundred ninety-three...five hundred ninety-four...\x07Hello? Oh, I knew I could count on you! It's much quieter now.\x07Here you go, for all those Number Crunchers.\x07Number? Now I need to start counting all over again! One...two...."}, + 6251: {QUEST: "Poor Zari broke her zipper and now she can't make deliveries to her customers. She could sure use your help._where_"}, + 6252: {GREETING: '', + LEAVING: '', + QUEST: "Oh, hi _avName_. You're here to help with my deliveries?\x07That's terrific! This broken zipper makes it tough to zip around.\x07Let me see...ok, this should be easy. Cowboy George ordered a zither last week.\x07Could you please bring it over to him? _where_", + INCOMPLETE_PROGRESS: 'Oh, hi! Did you forget something? Cowboy George is waiting for that zither.'}, + 6253: {GREETING: '', + LEAVING: '', + QUEST: "My zither! At last! Gosh, I can't wait to play it.\x07Go tell Zari that I said thanks, would you?", + INCOMPLETE_PROGRESS: "Thanks again for the zither. Doesn't Zari have more deliveries for you to do?"}, + 6254: {GREETING: '', + LEAVING: '', + QUEST: "That was fast. What's next on my list?\x07Right. Master Mike ordered a Zamboni. That zany guy.\x07Could you bring this to him, please?_where_", + INCOMPLETE_PROGRESS: 'That Zamboni needs to go to Master Mike._where_'}, + 6255: {GREETING: '', + LEAVING: '', + QUEST: "All-right! The Zamboni I ordered!\x07Now, if only there weren't so many Cogs around, I might have some time to use it.\x07Be a sport and take care of a few of those Cashbots for me, would you?", + INCOMPLETE_PROGRESS: 'Those Cashbots are tough, hunh? They make it hard to test my Zamboni.'}, + 6256: {GREETING: '', + LEAVING: '', + QUEST: "Excellent! Now I can go try out my Zamboni.\x07Tell Zari that I'll be in next week to place my next order, please.", + INCOMPLETE_PROGRESS: "That's all I need for now. Isn't Zari waiting for you?"}, + 6257: {GREETING: '', + LEAVING: '', + QUEST: "So, Master Mike was happy with his Zamboni? Great.\x07Who's next? Oh, Zen Glen ordered a zebra-striped zabuton.\x07Here it is! Could you zoom over to his place, please?_where_", + INCOMPLETE_PROGRESS: 'I think Zen Glen needs that zabuton to meditate.'}, + 6258: {GREETING: '', + LEAVING: '', + QUEST: "Ah, my zabuton at last. Now I can meditate.\x07Who could focus with that racket going on? All those Cogs!\x07Since you're already here, maybe you could take care of some of these Cogs?\x07Then I could use my zabuton in peace.", + INCOMPLETE_PROGRESS: 'Still so noisy with those Cogs! Who could focus?'}, + 6259: {GREETING: '', + LEAVING: '', + QUEST: 'Peace and quiet at last. Thanks, _avName_.\x07Please tell Zari how happy I am. OM....', + INCOMPLETE_PROGRESS: 'Zari called looking for you. You should go see what she needs.'}, + 6260: {GREETING: '', + LEAVING: '', + QUEST: "I'm glad to hear that Zen Glen is happy with his zebra zabuton.\x07Oh, these zinnias just came in for Rose Petals.\x07Since you seem to have some zeal for deliveries, perhaps you could take them over to her?_where_", + INCOMPLETE_PROGRESS: "Those zinnias will wilt if you don't deliver them soon."}, + 6261: {GREETING: '', + LEAVING: '', + QUEST: 'What lovely zinnias! Zari sure does deliver.\x07Oh, well, I guess YOU deliver, _avName_. Please thank Zari for me!', + INCOMPLETE_PROGRESS: "Don't forget to thank Zari for the zinnias!"}, + 6262: {GREETING: '', + LEAVING: '', + QUEST: "Welcome back, _avName_. You're pretty zippy.\x07Let's see...what's next on my list to deliver? Zydeco records for Wyda Wake._where_", + INCOMPLETE_PROGRESS: "I'm sure Wyda Wake is waiting for those Zydeco records."}, + 6263: {GREETING: '', + LEAVING: '', + QUEST: "Zydeco records? I don't remember asking for Zydeco records.\x07Oh, I bet Lullaby Lou ordered them._where_", + INCOMPLETE_PROGRESS: 'No, those Zydeco records are for Lullaby Lou._where_'}, + 6264: {GREETING: '', + LEAVING: '', + QUEST: "At last, my Zydeco records! I thought Zari had forgotten.\x07Could you please bring this zucchini to her? She'll find someone who wants one. Thanks!", + INCOMPLETE_PROGRESS: "Oh, I've got plenty of zucchini already. Take that one to Zari."}, + 6265: {GREETING: '', + LEAVING: '', + QUEST: "Zucchini? Hmm. Well, someone will want it, I'm sure.\x07Ok, we're nearly done with my list. One more delivery to make.\x07Babyface MacDougal ordered a zoot suit._where_", + INCOMPLETE_PROGRESS: "If you don't deliver that zoot suit to Babyface MacDougal,\x07 it'll get all wrinkled."}, + 6266: {GREETING: '', + LEAVING: '', + QUEST: "Once upon a time...oh! You're not here for a story, are you?\x07You're delivering my zoot suit? Great! Wow, that's something.\x07Hey, could you give Zari a message for me? I'll be needing zircon cufflinks to go with the suit. Thanks!", + INCOMPLETE_PROGRESS: 'Did you give Zari my message?', + COMPLETE: "Zircon cufflinks, hunh? Well, I'll see what I can do for him.\x07Anyway, you've been the very zenith of helpfulness and I can't let you leave with zilch.\x07Here's a BIG boost to help you zap those Cogs!"}, + 6271: {QUEST: "Drowsy Dave is having some trouble that you might be able to help with. Why don't you stop by his shop?_where_"}, + 6272: {GREETING: '', + LEAVING: '', + QUEST: "What? Huh? Oh, I must've fallen asleep.\x07You know, those Cogs buildings are full of machinery that makes me really sleepy.\x07I listen to them humming all day and...\x07Huh? Oh, yeah, right. If you could get rid of some of those Cog buildings, I could stay awake.", + INCOMPLETE_PROGRESS: "Zzzzz...huh? Oh, it's you, _avName_.\x07Back already? I was just taking a little nap.\x07Come back when you're done with those buildings.", + COMPLETE: "What? I dropped off to sleep for a minute there.\x07Now that those Cog buildings are gone I can finally relax.\x07Thanks for your help, _avName_.\x07See you later! I think maybe I'll take a little nap."}, + 6281: {QUEST: "Head over and call on Teddy Blair. He's got a job for you._where_"}, + 6282: {GREETING: '', + LEAVING: '', + QUEST: "What did you say? No, I don't have a fob for you.\x07Oh, a job! Why didn't you say so? You'll need to speak up.\x07Those Cogs make it hard to hibernate. If you'll help make Dreamland quieter,\x07I'll give you a little something.", + INCOMPLETE_PROGRESS: "You beat the bogs? What bogs?\x07Oh, the Cogs! Why didn't you say so?\x07Hmm, it's still pretty loud. How 'bout you defeat a few more?", + COMPLETE: "You had fun? Huh? Oh!\x07You're done! Great. Really nice of you to help out this way.\x07I found this in the back room but I don't have any use for it.\x07Maybe you'll find something to do with it. So long, _avName_!"}, + 6291: {QUEST: 'Cogs broke into the First Security Blanket Bank! Go see William Teller and see if you can help.'}, + 6292: {QUEST: 'Oh those darn Cashbot Cogs! They stole my reading lamps!\x07I need them back right away. Can you go look for them?\x07If you can get my reading lamps, I might be able to help you get into see the C.F.O.\x07Hurry!', + INCOMPLETE_PROGRESS: 'I need those lamps back. Keep looking for them!', + COMPLETE: "You're back! And you got my lamps!\x07I can't thank you enough but I can give you this."}, + 7201: {QUEST: 'Nina Nightlight was looking for you, _avName_. She needs some help._where_'}, + 7202: {GREETING: '', + LEAVING: '', + QUEST: "Oh! I'm so glad to see you, _avName_. I could use some help!\x07Those darn Cogs have kept the delivery folks away and I have no beds in stock.\x07Could you go see Hardy O'Toole and bring me back a bed?_where_ ", + INCOMPLETE_PROGRESS: "Did Hardy have any beds? I was sure he'd have one.", + COMPLETE: ''}, + 7203: {GREETING: '', + LEAVING: '', + QUEST: 'A bed? Sure, here\'s one all ready to go.\x07Just bring it over to her for me, would you? Get it?\x07"WOOD" you? Hee-hee!\x07Pretty funny. No? Well, take it over there anyway, please?', + INCOMPLETE_PROGRESS: 'Did Nina like the bed?', + COMPLETE: ''}, + 7204: {GREETING: '', + LEAVING: '', + QUEST: "This bed isn't right. It's much too plain.\x07Go see if he has anything fancier, would you?\x07I'm sure it won't take but a minute.", + INCOMPLETE_PROGRESS: "I'm certain that Hardy has a fancier bed.", + COMPLETE: ''}, + 7205: {GREETING: '', + LEAVING: '', + QUEST: "Didn't hit the nail on the head with that bed, huh? I've got one here that will do the job.\x07One small problem though - it needs to be assembled first.\x07While I hammer out this problem, could you get rid of some of those Cogs that are outside?\x07Those awful Cogs throw a wrench in the works.\x07Come back when you're done and the bed will be ready.", + INCOMPLETE_PROGRESS: "Not quite done with assembling the bed.\x07When you're done with the Cogs, it'll be ready.", + COMPLETE: ''}, + 7206: {GREETING: '', + LEAVING: '', + QUEST: 'Hey there _avName_!\x07You did a bang-up job on those Cogs.\x07The bed is all ready. Could you please deliver it for me?\x07Now that those Cogs are gone, business will be brisk!', + INCOMPLETE_PROGRESS: "I think Nina's waiting for that bed delivery.", + COMPLETE: 'What a lovely bed!\x07Now my customers will be happy. Thanks, _avName_.\x07Say, you might be able to use this. Someone left it here.'}, + 7209: {QUEST: 'Go see Honey Moon. She needs some help._where_'}, + 7210: {GREETING: '', + LEAVING: '', + QUEST: "Oh! I'm so glad to see you, _avName_. I really need some help!\x07I haven't been able to get my beauty sleep for ages. You see, those Cogs stole my bedspread.\x07Say, could you please run over and see if Ed's got anything in blue?_where_", + INCOMPLETE_PROGRESS: 'What did Ed have to say about a blue bedspread?', + COMPLETE: ''}, + 7211: {GREETING: '', + LEAVING: '', + QUEST: "So, Honey wants a bedspread, huh?\x07What color? BLUE?!\x07Well, I'd have to make that for her special. Everything I've got is red.\x07Tell ya what...if you'll go deal with some of those Cogs out there, I'll make a special blue bedspread just for her.\x07Blue bedspreads...what'll it be next?", + INCOMPLETE_PROGRESS: 'Still working on this blue bedspread, _avName_. Keep at those Cogs!', + COMPLETE: ''}, + 7212: {GREETING: '', + LEAVING: '', + QUEST: "Nice to see you again. I've got something for you!\x07Here's the bedspread and it's blue. She'll love it.", + INCOMPLETE_PROGRESS: 'Did Honey like the bedspread?', + COMPLETE: ''}, + 7213: {GREETING: '', + LEAVING: '', + QUEST: "My bedspread? No, that's not right.\x07It's PLAID! How can anyone sleep with such a LOUD pattern?\x07You'll just have to take it back and get a different one.\x07I'm sure he'll have others.", + INCOMPLETE_PROGRESS: 'I simply will not accept a plaid bedspread. See what Ed can do about it.', + COMPLETE: ''}, + 7214: {GREETING: '', + LEAVING: '', + QUEST: "What? She doesn't like PLAID?\x07Hmm...let me see what we've got here.\x07This will take a while. Why don't you go take care of a few Cogs while I try to find something else?\x07I'll have something by the time you get back here.", + INCOMPLETE_PROGRESS: "I'm still looking for another bedspread. How's it going with the Cogs?", + COMPLETE: ''}, + 7215: {GREETING: '', + LEAVING: '', + QUEST: "Hey, good job on those Cogs!\x07Here you go, it's blue and it's not plaid.\x07Sure hope she likes paisley.\x07Bring the bedspread back to Honey.", + INCOMPLETE_PROGRESS: "That's all I've got for you right now.\x07Please bring that bedspread to Honey.", + COMPLETE: "Oh! That's lovely! Paisley suits me quite well.\x07Time for my beauty sleep, then! So long, _avName_.\x07What? You're still here? Can't you see I'm trying to sleep?\x07Here, take this and let me rest. I must look a fright!"}, + 7218: {QUEST: 'Dreamy Daphne could use a hand._where_'}, + 7219: {GREETING: '', + LEAVING: '', + QUEST: "Oh, _avName_, I'm glad to see you! Those Cogs took my pillows.\x07Could you go see if Tex has some pillows?_where_\x07I'm sure he can help.", + INCOMPLETE_PROGRESS: 'Does Tex have any pillows for me? ', + COMPLETE: ''}, + 7220: {GREETING: '', + LEAVING: '', + QUEST: "Howdy! Daphne needs some pillows, huh? Well, you came to the right place, pardner!\x07More pillows in here than there's spines on a cactus.\x07Here you go, _avName_. Take these back over to Daphne with my compliments.\x07Always glad to help a gal out.", + INCOMPLETE_PROGRESS: 'Were those pillows soft enough for the little lady?', + COMPLETE: ''}, + 7221: {GREETING: '', + LEAVING: '', + QUEST: "You got the pillows! Great!\x07Hey, wait a second! These pillows are awfully soft.\x07Much too soft for me. I need harder pillows.\x07Take these back to Tex and see what else he's got. Thanks.", + INCOMPLETE_PROGRESS: 'Nope! Too soft. Ask Tex for different pillows.', + COMPLETE: ''}, + 7222: {GREETING: '', + LEAVING: '', + QUEST: "Too soft, huh? Well, let me see what I've got....\x07Hmm...seems I had me a whole passel of hard pillows. Where'd they get to?\x07Oh! I remember. I was fixing to send them back so they're in storage.\x07How 'bout you clean up some of those Cog buildings out there while I get 'em out of storage, pardner?", + INCOMPLETE_PROGRESS: "Cog buildings are hard. But these pillows aren't.\x07I'll keep looking.", + COMPLETE: ''}, + 7223: {GREETING: '', + LEAVING: '', + QUEST: "Back already? Well, that's jess fine. See, I found those pillows Daphne wanted.\x07Now, you jess take these over to her. They're hard enough to break a tooth on!", + INCOMPLETE_PROGRESS: "Yeah, those pillows are mighty hard. I hope Daphne fancies 'em.", + COMPLETE: 'I knew Tex would have some harder pillows.\x07Oh yes, those are perfect. Nice and hard.\x07Would you have a use for this piece of a Cog suit? Might as well take it with you.'}, + 7226: {QUEST: "Drop by to see Sandy Sandman. She's lost her pajamas._where_"}, + 7227: {GREETING: '', + LEAVING: '', + QUEST: "I have no pajamas! They're missing!\x07What will I do? Oh! I know!\x07Go see Big Mama. She'll have pajamas for me._where_", + INCOMPLETE_PROGRESS: 'Does Big Mama have pajamas for me?', + COMPLETE: ''}, + 7228: {GREETING: '', + LEAVING: '', + QUEST: "Hey there, little toon! Big Mama's got the best pajamas from the Bahamas.\x07Oh, something for Sandy Sandman, huh? Well, let me see what I've got.\x07Here's a little something. Now she can sleep in style!\x07Would you run these back over to her for me? I can't leave the shop just now.\x07Thanks, _avName_. See you around!", + INCOMPLETE_PROGRESS: 'You need to take those pajamas to Sandy._where_', + COMPLETE: ''}, + 7229: {GREETING: '', + LEAVING: '', + QUEST: "Big Mama sent these for me? Oh...\x07Doesn't she have any pajamas with feet on them?\x07I always wear pajamas with feet. Doesn't everybody?\x07Take these back and ask her to find some with feet.", + INCOMPLETE_PROGRESS: 'My pajamas must have feet. See what Big Mama can do.', + COMPLETE: ''}, + 7230: {GREETING: '', + LEAVING: '', + QUEST: "Feet? Let me think....\x07Wait! I've got just the thing!\x07Ta-dah! Pajamas with feet. Nice blue pajamas with feet. Best ones on any island.\x07Please take them back to her, would you? Thanks!", + INCOMPLETE_PROGRESS: 'Did Sandy like the blue footie pajamas?', + COMPLETE: ''}, + 7231: {GREETING: '', + LEAVING: '', + QUEST: "Well, these DO have feet, but I can't wear blue pajamas!\x07Ask Big Mama if she has a different color.", + INCOMPLETE_PROGRESS: "I'm sure Big Mama has footie pajamas in a different color.", + COMPLETE: ''}, + 7232: {GREETING: '', + LEAVING: '', + QUEST: "That's too bad. These are the only pajamas with feet I have.\x07Oh, I've got an idea. Go ask Cat. She may have some pajamas with feet._where_", + INCOMPLETE_PROGRESS: "Nope, those are all the pajamas I've got. Go see what Cat has._where_", + COMPLETE: ''}, + 7233: {GREETING: '', + LEAVING: '', + QUEST: "Pajamas with feet? Sure thing.\x07What do you mean, these are blue? She doesn't want blue?\x07Oh, that's a little trickier. Here, try these.\x07They're not blue and they DO have feet.", + INCOMPLETE_PROGRESS: "I just love puce, don't you?\x07I hope Sandy likes them....", + COMPLETE: ''}, + 7234: {GREETING: '', + LEAVING: '', + QUEST: "No, these aren't blue but no one with my complexion could possibly wear puce.\x07Absolutely not. Back they go and you with them! See what else Cat has.", + INCOMPLETE_PROGRESS: 'Cat must have more pajamas. No puce for me!', + COMPLETE: ''}, + 7235: {GREETING: '', + LEAVING: '', + QUEST: "Not puce either. Hmm....\x07By my whiskers, I know I have some other ones.\x07They'll take a little while to find. Let's make a deal.\x07I'll find the other pajamas if you'll get rid of some of these Cog buildings. They're very unsettling.\x07I'll have the pajamas ready when you get back, _avName_.", + INCOMPLETE_PROGRESS: 'You need to clear out a few more Cog buildings while I look for other pajamas.', + COMPLETE: ''}, + 7236: {GREETING: '', + LEAVING: '', + QUEST: 'You did a great job on those Cogs! Thanks!\x07I found those pajamas for Sandy; hope she likes them.\x07Bring them over to her. Thank you.', + INCOMPLETE_PROGRESS: "Sandy's waiting for those pajamas, _avName_.", + COMPLETE: "Fuchsia pajamas with feet! Purr-fect!\x07Ah, now I'm all set. Let's see....\x07Oh, I suppose I should give you something for helping me out.\x07Maybe you can use this. Someone left it here."}, + 7239: {QUEST: "Go see Smudgy Mascara. She's been looking for some help._where_"}, + 7240: {GREETING: '', + LEAVING: '', + QUEST: 'Those darn Cogs took my wrinkle cream!\x07My customers simply MUST have wrinkle cream while I work on them.\x07Go see Rip and see if he has my special formula in stock._where_', + INCOMPLETE_PROGRESS: 'I refuse to work on anyone without wrinkle cream.\x07See what Rip has for me.'}, + 7241: {GREETING: '', + LEAVING: '', + QUEST: "Oh, that Smudgy's a picky character. She won't settle for my usual formula.\x07That means I'll need some cauliflower coral, my super-secret special ingredient. But I haven't any in stock.\x07Could you go fish some out of the pond for me? As soon as you get the coral, I'll whip up a batch for Smudgy.", + INCOMPLETE_PROGRESS: "I'll need that cauliflower coral to make a batch of wrinkle cream."}, + 7242: {GREETING: '', + LEAVING: '', + QUEST: "Wow, that's a nice cauliflower coral!\x07Ok, let's see...a little of this and a splash of that...now, just a dollop of kelp.\x07Huh, where's the kelp? Looks like I'm out of kelp, too.\x07Could you pop down to the pond and fish me out some nice, slimy kelp?", + INCOMPLETE_PROGRESS: "Not a strip of slimy kelp in the shop.\x07Can't make the cream without it."}, + 7243: {GREETING: '', + LEAVING: '', + QUEST: "Oooh! Very slimy kelp you've got there, _avName_.\x07Now, I just crush some pearls with the mortar and pestle.\x07Um, where's my pestle? What good is a mortar without a pestle?\x07I bet that darn Loan Shark took it when he came in here!\x07You need to help me find it! He was headed for Cashbot HQ!", + INCOMPLETE_PROGRESS: 'I simply cannot crush the pearls without a pestle.\x07Darn those Loan Sharks!'}, + 7244: {GREETING: '', + LEAVING: '', + QUEST: "Alright! You got my pestle!\x07Now we're in business. Crush that...stir this up and...\x07There ya go! Tell Smudgy's it's good and fresh.", + INCOMPLETE_PROGRESS: "You should bring this over to Smudgy while it's fresh.\x07She's very picky.", + COMPLETE: "Didn't Rip have a bigger jar of wrinkle cream than this? No?\x07Well, I guess I'll just order more when I run out.\x07So long, _avName_.\x07What? You're still here? Can't you see I'm trying to work?\x07Here, take this."}, + 11000: {GREETING: '', + LEAVING: '', + QUEST: 'If you are interested in Lawbot disguise parts you should visit _toNpcName_.\x07I hear he could use some help with his weather research._where_'}, + 11001: {GREETING: '', + LEAVING: '', + QUEST: 'Yes, yes. I have Lawbot disguise parts.\x07But they are of no interest to me.\x07The focus of my research is fluctuations in the ambient temperature of Toontown.\x07I will gladly trade you disguise parts for cog temperature sensors.\x07You can start on %s.' % GlobalStreetNames[2100][-1], + INCOMPLETE_PROGRESS: 'Have you tried looking on %s?' % GlobalStreetNames[2100][-1], + COMPLETE: 'Ah, excellent!\x07Just as I feared...\x07Oh, yes! Here is your disguise part.'}, + 11002: {GREETING: '', + LEAVING: '', + QUEST: 'For more Lawbot disguise parts you should visit _toNpcName_ again.\x07I hear he needs more research assistants._where_'}, + 11003: {GREETING: '', + LEAVING: '', + QUEST: 'More Lawbot disguise parts?\x07Well, if you insist...\x07but I will require another cog temperature sensor.\x07This time look on %s.' % GlobalStreetNames[2200][-1], + INCOMPLETE_PROGRESS: 'You are looking on %s, right?' % GlobalStreetNames[2200][-1], + COMPLETE: 'Thank you!\x07And here is your disguise part.'}, + 11004: {GREETING: '', + LEAVING: '', + QUEST: 'If you need more Lawbot disguise parts you should return to _toNpcName_.\x07I hear he still needs help with his weather reasearch._where_'}, + 11005: {GREETING: '', + LEAVING: '', + QUEST: "You're proving yourself quite useful!\x07Can you take a look on %s?" % GlobalStreetNames[2300][-1], + INCOMPLETE_PROGRESS: "Are you sure you're looking on %s?" % GlobalStreetNames[2300][-1], + COMPLETE: "Hmmm, I don't like the looks of this...\x07but here is your disguise part..."}, + 11006: {GREETING: '', + LEAVING: '', + QUEST: 'You-know-who needs more temperature readings.\x07Stop by if you would like another disguise part._where_'}, + 11007: {GREETING: '', + LEAVING: '', + QUEST: 'Back again?\x07You are very dedicated...\x07The next stop is %s.' % GlobalStreetNames[1100][-1], + INCOMPLETE_PROGRESS: 'Have you tried looking on %s?' % GlobalStreetNames[1100][-1], + COMPLETE: 'Good! You seem to be getting the hang of this!\x07Your disguise part...'}, + 11008: {GREETING: '', + LEAVING: '', + QUEST: "If you're up for another Lawbot disguise part..._where_"}, + 11009: {GREETING: '', + LEAVING: '', + QUEST: 'Fancy seeing you here!\x07Now I need readings on %s.' % GlobalStreetNames[1200][-1], + INCOMPLETE_PROGRESS: 'You are looking on %s, right?' % GlobalStreetNames[1200][-1], + COMPLETE: 'Thank you very much.\x07Your disguise must be getting close...'}, + 11010: {GREETING: '', + LEAVING: '', + QUEST: 'I believe _toNpcName_ has more work for you._where_'}, + 11011: {GREETING: '', + LEAVING: '', + QUEST: 'Good to see you again, _avName_!\x07Can you get a reading on %s, please?' % GlobalStreetNames[1300][-1], + INCOMPLETE_PROGRESS: 'Have you tried looking on %s?' % GlobalStreetNames[1300][-1], + COMPLETE: "Great work!\x07Here's your well earned reward!"}, + 11012: {GREETING: '', + LEAVING: '', + QUEST: 'You know the drill._where_'}, + 11013: {GREETING: '', + LEAVING: '', + QUEST: '_avName_, my dear friend!\x07Can you go to %s and find another temperature sensor?' % GlobalStreetNames[5100][-1], + INCOMPLETE_PROGRESS: "Are you sure you're looking on %s?" % GlobalStreetNames[5100][-1], + COMPLETE: "Excellent!\x07With your help my research is coming quickly!\x07Here's your reward."}, + 11014: {GREETING: '', + LEAVING: '', + QUEST: "_toNpcName_ was asking for you by name.\x07It appears you've made quite an impression!_where_"}, + 11015: {GREETING: '', + LEAVING: '', + QUEST: "Welcome back!\x07I've been waiting for you.\x07The next reading I need is on %s." % GlobalStreetNames[5200][-1], + INCOMPLETE_PROGRESS: 'You are looking on %s, right?' % GlobalStreetNames[5200][-1], + COMPLETE: "Thanks!\x07Here's your reward."}, + 11016: {GREETING: '', + LEAVING: '', + QUEST: 'If you need to finish your Lawbot disguise...\x07_toNpcName_ can help you out._where_'}, + 11017: {GREETING: '', + LEAVING: '', + QUEST: 'Hello, Junior Research Scientist!\x07We still need readings from %s.' % GlobalStreetNames[5300][-1], + INCOMPLETE_PROGRESS: 'Have you tried looking on %s?' % GlobalStreetNames[5300][-1], + COMPLETE: 'Excellent job!\x07Here is your Lawbot thingy...'}, + 11018: {GREETING: '', + LEAVING: '', + QUEST: "_toNpcName_ has another job for you.\x07If you're not sick of him yet..._where_"}, + 11019: {GREETING: '', + LEAVING: '', + QUEST: 'Well, then.\x07Are you ready for another recovery?\x07This time try %s.' % GlobalStreetNames[4100][-1], + INCOMPLETE_PROGRESS: "Are you sure you're looking on %s?" % GlobalStreetNames[4100][-1], + COMPLETE: 'Another one!\x07My you are the picture of efficiency!'}, + 11020: {GREETING: '', + LEAVING: '', + QUEST: 'Are you still after Lawbot disguise parts?_where_'}, + 11021: {GREETING: '', + LEAVING: '', + QUEST: 'You could probably guess by now...\x07but I need readings from %s.' % GlobalStreetNames[4200][-1], + INCOMPLETE_PROGRESS: 'You are looking on %s, right?' % GlobalStreetNames[4200][-1], + COMPLETE: 'Almost there!\x07Here you go...'}, + 11022: {GREETING: '', + LEAVING: '', + QUEST: 'I hate to say it, but..._where_'}, + 11023: {GREETING: '', + LEAVING: '', + QUEST: 'What do you think about %s? Could you get a sensor from there too?' % GlobalStreetNames[4300][-1], + INCOMPLETE_PROGRESS: 'Have you tried looking on %s?' % GlobalStreetNames[4300][-1], + COMPLETE: 'Another excellent job, _avName_'}, + 11024: {GREETING: '', + LEAVING: '', + QUEST: 'Go visit the Professor if you still need disguise parts._where_'}, + 11025: {GREETING: '', + LEAVING: '', + QUEST: 'I believe we still need a reading from %s.' % GlobalStreetNames[9100][-1], + INCOMPLETE_PROGRESS: "Are you sure you're looking on %s?" % GlobalStreetNames[9100][-1], + COMPLETE: 'Good work!\x07I think we are getting very close...'}, + 11026: {GREETING: '', + LEAVING: '', + QUEST: '_toNpcName_ has one final mission for you._where_'}, + 11027: {GREETING: '', + LEAVING: '', + QUEST: 'Back so soon?\x07The final reading is on %s.' % GlobalStreetNames[9200][-1], + INCOMPLETE_PROGRESS: 'You are looking on %s, right?' % GlobalStreetNames[9200][-1], + COMPLETE: "You're all done!\x07Now you are ready to infiltrate the District Attorney's Office and collect Jury Notices.\x07Good luck and thanks for all your help!"}, + 12000: {GREETING: '', + LEAVING: '', + QUEST: 'If you are interested in Bossbot disguise parts you should visit _toNpcName_._where_'}, + 12001: {GREETING: '', + LEAVING: '', + QUEST: "Yes, I can get you Bossbot parts.\x07But I'll need you to help me complete my Bossbot collection.\x07Go out there and defeat a Flunky.", + INCOMPLETE_PROGRESS: "You can't find a Flunky? For shame...", + COMPLETE: "You didn't flunk that, now did you?\x07Here's your first disguise part."}, + 12002: {GREETING: '', + LEAVING: '', + QUEST: "_toNpcName_ needs more help, if you're up for it._where_"}, + 12003: {GREETING: '', + LEAVING: '', + QUEST: 'Another disguise part?\x07Certainly...\x07but only if you defeat a Pencil Pusher.', + INCOMPLETE_PROGRESS: 'Pencil Pushers can be found in the streets.', + COMPLETE: "He was a real pushover!\x07Here's your second disguise part."}, + 12004: {GREETING: '', + LEAVING: '', + QUEST: "There's really only one place to go for Bossbot parts._where_"}, + 12005: {GREETING: '', + LEAVING: '', + QUEST: 'Now I need a Yesman...', + INCOMPLETE_PROGRESS: 'Yesmen can be found in the streets.', + COMPLETE: "Yes! Man, you are good.\x07Here's your third disguise part."}, + 12006: {GREETING: '', + LEAVING: '', + QUEST: '_toNpcName_ has more parts for you...'}, + 12007: {GREETING: '', + LEAVING: '', + QUEST: "If you defeat a Micromanager I'll give you another part.", + INCOMPLETE_PROGRESS: 'Try looking on %s' % GlobalStreetNames[1100][-1], + COMPLETE: "You managed that quite well!\x07Here's your fourth disguise part."}, + 12008: {GREETING: '', + LEAVING: '', + QUEST: 'Head on over to..._where_'}, + 12009: {GREETING: '', + LEAVING: '', + QUEST: "I'm after a Downsizer now...", + INCOMPLETE_PROGRESS: 'Having trouble? Try looking on %s' % GlobalStreetNames[3100][-1], + COMPLETE: "He went down hard!\x07Here's your fifth disguise part."}, + 12010: {GREETING: '', + LEAVING: '', + QUEST: 'I think you know where to go by now..._where_'}, + 12011: {GREETING: '', + LEAVING: '', + QUEST: 'A Head Hunter is next on my list.', + INCOMPLETE_PROGRESS: 'You might have better luck looking buildings.', + COMPLETE: "I see you had no problem hunting one down.\x07Here's your sixth disguise part."}, + 12012: {GREETING: '', + LEAVING: '', + QUEST: '_toNpcName_ needs more Bossbots.'}, + 12013: {GREETING: '', + LEAVING: '', + QUEST: "Next I'll need you to track down a Corporate Raider.", + INCOMPLETE_PROGRESS: 'You might have better luck looking buildings.', + COMPLETE: "You're quite the little raider yourself!\x07Here's your seventh disguise part."}, + 12014: {GREETING: '', + LEAVING: '', + QUEST: 'If you want more disguise parts, go to..._where_'}, + 12015: {GREETING: '', + LEAVING: '', + QUEST: 'Now the coup de grace: The Big Cheese!', + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: "I knew I could count on you to cut...\x07Ah, never mind.\x07Here's your next disguise part."}, + 12016: {GREETING: '', + LEAVING: '', + QUEST: '_toNpcName_ was looking for you...'}, + 12017: {GREETING: '', + LEAVING: '', + QUEST: 'Now I need you to defeat one of the new, more treacherous Bossbot Cogs.', + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: 'They are tougher than they look, huh?\x07I guess I owe you a disguise part.'}, + 12018: {GREETING: '', + LEAVING: '', + QUEST: 'Could you swing by..._where_'}, + 12019: {GREETING: '', + LEAVING: '', + QUEST: 'These Version 2.0 Cogs are very interesting.\x07Please go defeat another one.', + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: 'Thanks!\x07Another disguise part coming right up.'}, + 12020: {GREETING: '', + LEAVING: '', + QUEST: 'If you get a chance, stop by and see _toNpcName_.'}, + 12021: {GREETING: '', + LEAVING: '', + QUEST: 'I wonder if they can keep regenerating...', + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: "I guess not.\x07Here's your part..."}, + 12022: {GREETING: '', + LEAVING: '', + QUEST: 'You know..._where_'}, + 12023: {GREETING: '', + LEAVING: '', + QUEST: "Maybe they aren't Bossbots at all...", + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: 'Hmmm, I guess they are Bossbots after all.\x07Help yourself to another part.'}, + 12024: {GREETING: '', + LEAVING: '', + QUEST: "You probably know what I'm going to say already..."}, + 12025: {GREETING: '', + LEAVING: '', + QUEST: 'Perhaps they are related to the Skelecogs somehow...', + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: "That was inconclusive...\x07Here's your disguise part."}, + 12026: {GREETING: '', + LEAVING: '', + QUEST: 'Please go see _toNpcName_ again.'}, + 12027: {GREETING: '', + LEAVING: '', + QUEST: "I'm still not convinced they aren't some type of Skelecog...", + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: "Well, maybe not.\x07Here's your next part."}, + 12028: {GREETING: '', + LEAVING: '', + QUEST: "It's probably the last place you want to go. but..."}, + 12029: {GREETING: '', + LEAVING: '', + QUEST: 'I am still quite baffled by these new cogs.\x07Could you go defeat another, please?', + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: 'Fascinating. Simply fascinating.\x07A disguise part for your troubles.'}, + 12030: {GREETING: '', + LEAVING: '', + QUEST: '_toNpcName_ is starting to sound like a broken record...'}, + 12031: {GREETING: '', + LEAVING: '', + QUEST: "I've almost determined what these new Cogs are.\x07Just one more...", + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: "Yes, I think I'm onto something.\x07Oh, yes.\x07This is for you..."}, + 12032: {GREETING: '', + LEAVING: '', + QUEST: 'You need to go tell Flippy about this...', + INCOMPLETE_PROGRESS: 'Flippy can be found in Toon Hall', + COMPLETE: 'A new type of Cog!\x07Good work!\x07Here is your final disguise part.'}} +ChatGarblerDog = ['woof', 'arf', 'rruff'] +ChatGarblerCat = ['meow', 'mew'] +ChatGarblerMouse = ['squeak', 'squeaky', 'squeakity'] +ChatGarblerHorse = ['neigh', 'brrr'] +ChatGarblerRabbit = ['eek', 'eepr', 'eepy', 'eeky'] +ChatGarblerDuck = ['quack', 'quackity', 'quacky'] +ChatGarblerMonkey = ['ooh', 'ooo', 'ahh'] +ChatGarblerBear = ['growl', 'grrr'] +ChatGarblerPig = ['oink', 'oik', 'snort'] +ChatGarblerDefault = ['blah'] +Bossbot = 'Bossbot' +Lawbot = 'Lawbot' +Cashbot = 'Cashbot' +Sellbot = 'Sellbot' +BossbotS = 'a Bossbot' +LawbotS = 'a Lawbot' +CashbotS = 'a Cashbot' +SellbotS = 'a Sellbot' +BossbotP = 'Bossbots' +LawbotP = 'Lawbots' +CashbotP = 'Cashbots' +SellbotP = 'Sellbots' +BossbotSkelS = 'a Bossbot Skelecog' +LawbotSkelS = 'a Lawbot Skelecog' +CashbotSkelS = 'a Cashbot Skelecog' +SellbotSkelS = 'a Sellbot Skelecog' +BossbotSkelP = 'Bossbot Skelecogs' +LawbotSkelP = 'Lawbot Skelecogs' +CashbotSkelP = 'Cashbot Skelecogs' +SellbotSkelP = 'Sellbot Skelecogs' +SkeleRevivePostFix = ' v%s.0' +AvatarDetailPanelOK = lOK +AvatarDetailPanelCancel = lCancel +AvatarDetailPanelClose = lClose +AvatarDetailPanelLookup = 'Looking up details for %s.' +AvatarDetailPanelFailedLookup = 'Unable to get details for %s.' +AvatarDetailPanelOnline = 'District: %(district)s\nLocation: %(location)s\nIdentifier: %(identifier)s' +AvatarDetailPanelOffline = 'District: offline\nLocation: offline\nIdentifier: %(identifier)s' +OfflineLocation = 'Offline' +AvatarPanelFriends = 'Friends' +AvatarPanelWhisper = 'Whisper' +AvatarPanelTrueFriends = 'True Friends' +AvatarPanelGoTo = 'Go To' +AvatarPanelPet = 'Show Doodle' +AvatarPanelIgnore = 'Ignore' +AvatarPanelIgnoreCant = 'Okay' +AvatarPanelStopIgnoring = 'Stop Ignoring' +AvatarPanelReport = 'Report' +AvatarPanelCogLevel = 'Level: %s' +AvatarPanelCogDetailClose = lClose +AvatarPanelDetail = 'Toon Details' +AvatarPanelGroupInvite = 'Invite' +AvatarPanelGroupMerge = 'Resulting in' +AvatarPanelGroupRetract = 'Retract Invitation' +AvatarPanelGroupMember = 'Already In Group' +AvatarPanelGroupMemberKick = 'Remove' +AvatarPanelCast = 'Cast Member' +AvatarPanelCastInfo = '%s is a Toontown Stride Cast Member.' +ReportPanelTitle = 'Report A Toon' +ReportPanelBody = 'This feature will send a complete report to a Moderator. Instead of sending a report, you might choose to do one of the following:\n\n - Teleport to another district\n - Use "Ignore" on the toon\'s panel\n\nDo you really want to report %s to a Moderator?' +ReportPanelBodyFriends = 'This feature will send a complete report to a Moderator. Instead of sending a report, you might choose to do one of the following:\n\n - Teleport to another district\n - Break your friendship\n\nDo you really want to report %s to a Moderator?\n\n(This will also break your friendship)' +ReportPanelCategoryBody = 'You are about to report %s. A Moderator will be alerted to your complaint and will take appropriate action for anyone breaking our rules. Please choose the reason you are reporting %s:' +ReportPanelCategoryLanguage = 'Inappropriate Language' +ReportPanelCategoryRude = 'Rude or Mean Behavior' +ReportPanelCategoryName = 'Bad Name' +ReportPanelCategoryHacking = 'Hacking' +ReportPanelCategoryGreening = 'Greening' +ReportPanelConfirmations = ('You are about to report that %s has used obscene, bigoted or sexually explicit language.', + 'You are about to report that %s has disrupted your experience by intentionally causing your toon to go sad.', + 'You are about to report that %s is bullying, harassing, or using extreme behavior to disrupt the game.', + "You are about to report that %s has created a name that does not follow the rules.", + 'You are about to report that %s has hacked/tampered with the game or used third party software.') +ReportPanelWarning = "We take reporting very seriously. Your report will be viewed by a Moderator who will take appropriate action for anyone breaking our rules. If your account is found to have participated in breaking the rules, or if you make false reports or abuse the 'Report a Toon' system, a Moderator may take action against your account. Are you absolutely sure you want to report this toon?" +ReportPanelThanks = 'Thank you! Your report has been sent to a Moderator for review. There is no need to contact us again about the issue. The moderation team will take appropriate action for a toon found breaking our rules.' +ReportPanelRemovedFriend = 'We have automatically removed %s from your Toon Friends List.' +ReportPanelAlreadyReported = 'You have already reported %s during this session. A Moderator will review your previous report.' +IgnorePanelTitle = 'Ignore A Toon' +IgnorePanelAddIgnore = 'Would you like to ignore %s?' +IgnorePanelIgnore = 'You are now ignoring %s.' +IgnorePanelRemoveIgnore = 'Would you like to stop ignoring %s?' +IgnorePanelEndIgnore = 'You are no longer ignoring %s.' +IgnorePanelAddFriendAvatar = '%s is your friend, you cannot ignore them while you are friends.' +PetPanelFeed = 'Feed' +PetPanelCall = 'Call' +PetPanelGoTo = 'Go To' +PetPanelOwner = 'Show Owner' +PetPanelDetail = 'Pet Details' +PetPanelScratch = 'Scratch' +PetDetailPanelTitle = 'Trick Training' +PetTrickStrings = {0: 'Jump', + 1: 'Beg', + 2: 'Play dead', + 3: 'Rollover', + 4: 'Backflip', + 5: 'Dance', + 6: 'Speak'} +PetMoodAdjectives = {'neutral': 'neutral', + 'hunger': 'hungry', + 'boredom': 'bored', + 'excitement': 'excited', + 'sadness': 'sad', + 'restlessness': 'restless', + 'playfulness': 'playful', + 'loneliness': 'lonely', + 'fatigue': 'tired', + 'confusion': 'confused', + 'anger': 'angry', + 'surprise': 'surprised', + 'affection': 'affectionate'} +SpokenMoods = {'neutral': 'neutral', + 'hunger': ["I'm tired of Jellybeans! How'bout giving me a slice of pie?", "How'bout a Red Jellybean? I'm tired of the Green ones!", "Oh, those Jellybeans were for planting?!! But I'm hungry!"], + 'boredom': ["I'm dying of boredom over here!", "You didn't think I understood you, huh?", 'Could we, like, DO something already?'], + 'excitement': ["Wow, it's you, it's you, it's you!", + 'mmm, Jellybeans, mmm!', + 'Does it GET any better than this?', + "Happy April Toons' Week!"], + 'sadness': ["Don't go, Don't go, Don't go, Don't go, Don't go, Don't go, Don't go, Don't go, Don't go, Don't go, Don't go...", "I'll be good, I promise!", "I don't know WHY I'm sad, I just am!!!"], + 'restlessness': ["I'm sooo restless!!!"], + 'playfulness': ["Let's play, Let's play, Let's play, Let's play, Let's play, Let's play, Let's play, Let's play, Let's play...", 'Play with me or I dig up some flowers!', 'Lets run around and around and around and around and around and around...'], + 'loneliness': ['Where have you been?', 'Wanna cuddle?', 'I want to go with you when you fight Cogs!'], + 'fatigue': ['That swim in the pond really tired me out!', 'Being a Doodle is exhausting!', 'I gotta get to Dreamland!'], + 'confusion': ['Where am I? Who are you again?', "What's a Toon-up again?", "Whoa, I'm standing between you and the Cogs! Run away!"], + 'anger': ['... and you wonder why I never give you a Toon-up?!!!', 'You always leave me behind!', 'You love your gags more than you love me!'], + 'surprise': ['Of course Doodles can talk!', 'Toons can talk?!!', 'Whoa, where did you come from?'], + 'affection': ["You're the best Toon EVER!!!!!!!!!!", 'Do you even KNOW how great you are?!?', 'I am SO lucky to be with you!!!']} +DialogQuestion = '?' +FriendsListLabel = 'Friends' +TeleportPanelOK = lOK +TeleportPanelCancel = lCancel +TeleportPanelYes = lYes +TeleportPanelNo = lNo +TeleportPanelCheckAvailability = 'Trying to go to %s.' +TeleportPanelNotAvailable = '%s is busy right now; try again later.' +TeleportPanelIgnored = '%s is ignoring you.' +TeleportPanelNotOnline = "%s isn't online right now." +TeleportPanelWentAway = '%s went away.' +TeleportPanelUnknownHood = "You don't know how to get to %s!" +TeleportPanelDenySelf = "You can't go to yourself!" +TeleportPanelOtherShard = "%(avName)s is in district %(shardName)s, and you're in district %(myShardName)s. Do you want to switch to %(shardName)s?" +TeleportPanelBusyShard = '%(avName)s is in a full District.' +BattleBldgBossTaunt = "I'm the boss." +CogdoBattleBldgBossTaunt = "I don't take meetings with Toons." +FactoryBossTaunt = "I'm the Foreman." +FactoryBossBattleTaunt = 'Let me introduce you to the Foreman.' +MintBossTaunt = "I'm the Supervisor." +MintBossBattleTaunt = 'You need to talk to the Supervisor.' +StageBossTaunt = "My Justice isn't Blind." +StageBossBattleTaunt = 'I am above the Law.' +CountryClubBossTaunt = "I'm the Club President." +CountryClubBossBattleTaunt = 'You need to talk to the Club President.' +ForcedLeaveCountryClubAckMsg = 'The Club President was defeated before you could reach him. You did not recover any Stock Options.' +ToonHealJokes = [['What goes TICK-TICK-TICK-WOOF?', 'A watchdog! '], + ['Why do male deer need braces?', "Because they have 'buck teeth'!"], + ['Why is it hard for a ghost to tell a lie?', 'Because you can see right through him.'], + ['What did the ballerina do when she hurt her foot?', 'She called the toe truck!'], + ['What has one horn and gives milk?', 'A milk truck!'], + ["Why don't witches ride their brooms when they're angry?", "They don't want to fly off the handle!"], + ['Why did the dolphin cross the ocean?', 'To get to the other tide.'], + ['What kind of mistakes do spooks make?', 'Boo boos.'], + ['Why did the chicken cross the playground?', 'To get to the other slide!'], + ['Where does a peacock go when he loses his tail?', 'A retail store.'], + ["Why didn't the skeleton cross the road?", "He didn't have the guts."], + ["Why wouldn't they let the butterfly into the dance?", 'Because it was a moth ball.'], + ["What's gray and squirts jam at you?", 'A mouse eating a doughnut.'], + ['What happened when 500 hares got loose on the main street?', 'The police had to comb the area.'], + ["What's the difference between a fish and a piano?", "You can tune a piano, but you can't tuna fish!"], + ['What do people do in clock factories?', 'They make faces all day.'], + ['What do you call a blind dinosaur?', "An I-don't-think-he-saurus."], + ['If you drop a white hat into the Red Sea, what does it become?', 'Wet.'], + ["What two things can't you have for breakfast?", 'Lunch and dinner.'], + ['What do you give an elephant with big feet?', 'Big shoes.'], + ['Where do baby ghosts go during the day?', 'Day-scare centers.'], + ['What did Snow White say to the photographer?', 'Some day my prints will come.'], + ["What's Tarzan's favorite song?", 'Jungle bells.'], + ["What's green and loud?", 'A froghorn.'], + ["What's worse than raining cats and dogs?", 'Hailing taxis.'], + ['When is the vet busiest?', "When it's raining cats and dogs."], + ['What do you call a gorilla wearing ear-muffs?', "Anything you want, he can't hear you."], + ['Where would you weigh a whale?', 'At a whale-weigh station.'], + ['What travels around the world but stays in the corner?', 'A stamp.'], + ['What do you give a pig with a sore throat?', 'Oinkment.'], + ['What did the hat say to the scarf?', 'You hang around while I go on a head.'], + ["What's the best parting gift?", 'A comb.'], + ['What kind of cats like to go bowling?', 'Alley cats.'], + ['What did one eye say to the other?', 'Between you and me, something smells.'], + ["What's round, white and giggles?", 'A tickled onion.'], + ['What do you get when you cross Bambi with a ghost?', 'Bamboo.'], + ['Why do golfers take an extra pair of socks?', 'In case they get a hole in one.'], + ['What do you call a fly with no wings?', 'A walk.'], + ['Who did Frankenstein take to the prom?', 'His ghoul friend.'], + ['What lies on its back, one hundred feet in the air?', 'A sleeping centipede.'], + ['How do you keep a bull from charging?', 'Take away his credit card.'], + ['What do you call a chicken at the North Pole?', 'Lost.'], + ['What do you get if you cross a cat with a dog?', 'An animal that chases itself.'], + ['What did the digital watch say to the grandfather clock?', 'Look dad, no hands.'], + ['Where does Ariel the mermaid go to see movies?', 'The dive-in.'], + ['What do you call a mosquito with a tin suit?', 'A bite in shining armor.'], + ['What do giraffes have that no other animal has?', 'Baby giraffes.'], + ['Why did the man hit the clock?', 'Because the clock struck first.'], + ['Why did the apple go out with a fig?', "Because it couldn't find a date."], + ['What do you get when you cross a parrot with a monster?', 'A creature that gets a cracker whenever it asks for one.'], + ["Why didn't the monster make the football team?", 'Because he threw like a ghoul!'], + ['What do you get if you cross a Cocker Spaniel with a Poodle and a rooster?', 'A cockapoodledoo!'], + ['What goes dot-dot-dash-dash-squeak?', 'Mouse code.'], + ["Why aren't elephants allowed on beaches?", "They can't keep their trunks up."], + ['What is at the end of everything?', 'The letter G.'], + ['How do trains hear?', 'Through the engineers.'], + ['What does the winner of a marathon lose?', 'His breath.'], + ['Why did the pelican refuse to pay for his meal?', 'His bill was too big.'], + ['What has six eyes but cannot see?', 'Three blind mice.'], + ["What works only when it's fired?", 'A rocket.'], + ["Why wasn't there any food left after the monster party?", 'Because everyone was a goblin!'], + ['What bird can be heard at mealtimes?', 'A swallow.'], + ['What goes Oh, Oh, Oh?', 'Santa walking backwards.'], + ['What has green hair and runs through the forest?', 'Moldy locks.'], + ['Where do ghosts pick up their mail?', 'At the ghost office.'], + ['Why do dinosaurs have long necks?', 'Because their feet smell.'], + ['What do mermaids have on toast?', 'Mermarlade.'], + ['Why do elephants never forget?', 'Because nobody ever tells them anything.'], + ["What's in the middle of a jellyfish?", 'A jellybutton.'], + ['What do you call a very popular perfume?', 'A best-smeller.'], + ["Why can't you play jokes on snakes?", 'Because you can never pull their legs.'], + ['Why did the baker stop making donuts?', 'He got sick of the hole business.'], + ['Why do mummies make excellent spies?', "They're good at keeping things under wraps."], + ['How do you stop an elephant from going through the eye of a needle?', 'Tie a knot in its tail.'], + ["What goes 'Ha Ha Ha Thud'?", 'Someone laughing his head off.'], + ["My friend thinks he's a rubber band.", 'I told him to snap out of it.'], + ["My sister thinks she's a pair of curtains.", 'I told her to pull herself together!'], + ['Did you hear about the dentist that married the manicurist?', 'Within a month they were fighting tooth and nail.'], + ['Why do hummingbirds hum?', "Because they don't know the words."], + ['Why did the baby turkey bolt down his food?', 'Because he was a little gobbler.'], + ['Where did the whale go when it was bankrupt?', 'To the loan shark.'], + ['How does a sick sheep feel?', 'Baah-aahd.'], + ["What's gray, weighs 10 pounds and squeaks?", 'A mouse that needs to go on a diet.'], + ['Why did the dog chase his tail?', 'To make ends meet.'], + ['Why do elephants wear running shoes?', 'For jogging of course.'], + ['Why are elephants big and gray?', "Because if they were small and yellow they'd be canaries."], + ['If athletes get tennis elbow what do astronauts get?', 'Missile toe.'], + ['Did you hear about the man who hated Santa?', 'He suffered from Claustrophobia.'], + ['Why did Joey sprinkle sugar on his pillow?', 'Because he wanted to have sweet dreams.'], + ['Why did Goshi take his comb to the dentist?', 'Because it had lost all its teeth.'], + ['Why did Denial wear his shirt in the bath?', 'Because the label said wash and wear.'], + ['Why did the dirty chicken cross the road?', 'For some fowl purpose.'], + ["Why didn't the skeleton go to the party?", 'He had no body to go with.'], + ['Why did the burglar take a shower?', 'To make a clean getaway.'], + ['Why does a sheep have a woolly coat?', "Because he'd look silly in a plastic one."], + ['Why do potatoes argue all the time?', "They can't see eye to eye."], + ['Why did Quackity sleep with a banana peel?', 'So he could slip out of bed in the morning.'], + ['Why did the mouse wear brown sneakers?', 'His white ones were in the wash.'], + ['Why are false teeth like stars?', 'They come out at night.'], + ['Why are Saturday and Sunday so strong?', 'Because the others are weekdays.'], + ['Why did the archaeologist go bankrupt?', 'Because his career was in ruins.'], + ['What do you get if you cross the Atlantic on the Titanic?', 'Very wet.'], + ['What do you get if you cross a chicken with cement?', 'A brick-layer.'], + ['What do you get if you cross a dog with a phone?', 'A golden receiver.'], + ['What do you get if you cross an elephant with a shark?', 'Swimming trunks with sharp teeth.'], + ['What did the tablecloth say to the table?', "Don't move, I've got you covered."], + ['Did you hear about the time Nick ate a candle?', 'He wanted a light snack.'], + ['What did the balloon say to the pin?', 'Hi Buster.'], + ['What did the big chimney say to the little chimney?', "You're too young to smoke."], + ['What did the carpet say to the floor?', 'I got you covered.'], + ['What did the necklace say to the hat?', "You go ahead, I'll hang around."], + ['What goes zzub-zzub?', 'A bee flying backwards.'], + ['How do you communicate with a fish?', 'Drop him a line.'], + ["What do you call a dinosaur that's never late?", 'A prontosaurus.'], + ['What do you get if you cross a bear and a skunk?', 'Winnie-the-phew.'], + ['How do you clean a tuba?', 'With a tuba toothpaste.'], + ['What do frogs like to sit on?', 'Toadstools.'], + ['Why was the math book unhappy?', 'It had too many problems.'], + ['Why was the school clock punished?', 'It tocked too much.'], + ["What's a polygon?", 'A dead parrot.'], + ['What needs a bath and keeps crossing the street?', 'A dirty double crosser.'], + ['What do you get if you cross a camera with a crocodile?', 'A snap shot.'], + ['What do you get if you cross an elephant with a canary?', 'A very messy cage.'], + ['What do you get if you cross a jeweler with a plumber?', 'A ring around the bathtub.'], + ['What do you get if you cross an elephant with a crow?', 'Lots of broken telephone poles.'], + ['What do you get if you cross a plum with a tiger?', 'A purple people eater.'], + ["What's the best way to save water?", 'Dilute it.'], + ["What's a lazy shoe called?", 'A loafer.'], + ["What's green, noisy and dangerous?", 'A thundering herd of cucumbers.'], + ['What color is a shout?', 'Yellow!'], + ['What do you call a sick duck?', 'A mallardy.'], + ["What's worse then a giraffe with a sore throat?", "A centipede with athlete's foot."], + ['What goes ABC...slurp...DEF...slurp?', 'Someone eating alphabet soup.'], + ["What's green and jumps up and down?", 'Lettuce at a dance.'], + ["What's a cow after she gives birth?", 'De-calf-inated.'], + ['What do you get if you cross a cow and a camel?', 'Lumpy milk shakes.'], + ["What's white with black and red spots?", 'A Dalmatian with measles.'], + ["What's brown has four legs and a trunk?", 'A mouse coming back from vacation.'], + ["What does a skunk do when it's angry?", 'It raises a stink.'], + ["What's gray, weighs 200 pounds and says, Here Kitty, kitty?", 'A 200 pound mouse.'], + ["What's the best way to catch a squirrel?", 'Climb a tree and act like a nut.'], + ["What's the best way to catch a rabbit?", 'Hide in a bush and make a noise like lettuce.'], + ['What do you call a spider that just got married?', 'A newly web.'], + ['What do you call a duck that robs banks?', 'A safe quacker.'], + ["What's furry, meows and chases mice underwater?", 'A catfish.'], + ["What's a funny egg called?", 'A practical yolker.'], + ["What's green on the outside and yellow inside?", 'A banana disguised as a cucumber.'], + ['What did the elephant say to the lemon?', "Let's play squash."], + ['What weighs 4 tons, has a trunk and is bright red?', 'An embarrassed elephant.'], + ["What's gray, weighs 4 tons, and wears glass slippers?", 'Cinderelephant.'], + ["What's an elephant in a fridge called?", 'A very tight squeeze.'], + ['What did the elephant say to her naughty child?', 'Tusk! Tusk!'], + ['What did the peanut say to the elephant?', "Nothing -- Peanuts can't talk."], + ['What do elephants say when they bump into each other?', "Small world, isn't it?"], + ['What did the cashier say to the register?', "I'm counting on you."], + ['What did the flea say to the other flea?', 'Shall we walk or take the cat?'], + ['What did the big hand say to the little hand?', 'Got a minute.'], + ['What does the sea say to the sand?', 'Not much. It usually waves.'], + ['What did the stocking say to the shoe?', 'See you later, I gotta run.'], + ['What did one tonsil say to the other tonsil?', 'It must be spring, here comes a swallow.'], + ['What did the soil say to the rain?', 'Stop, or my name is mud.'], + ['What did the puddle say to the rain?', 'Drop in sometime.'], + ['What did the bee say to the rose?', 'Hi, bud.'], + ['What did the appendix say to the kidney?', "The doctor's taking me out tonight."], + ['What did the window say to the venetian blinds?', "If it wasn't for you it'd be curtains for me."], + ['What did the doctor say to the sick orange?', 'Are you peeling well?'], + ['What do you get if you cross a chicken with a banjo?', 'A self-plucking chicken.'], + ['What do you get if you cross a hyena with a bouillon cube?', 'An animal that makes a laughing stock of itself.'], + ['What do you get if you cross a rabbit with a spider?', 'A hare net.'], + ['What do you get if you cross a germ with a comedian?', 'Sick jokes.'], + ['What do you get if you cross a hyena with a mynah bird?', 'An animal that laughs at its own jokes.'], + ['What do you get if you cross a railway engine with a stick of gum?', 'A chew-chew train.'], + ['What would you get if you crossed an elephant with a computer?', 'A big know-it-all.'], + ['What would you get if you crossed an elephant with a skunk?', 'A big stinker.']] +MovieHealLaughterMisses = ('hmm', + 'heh', + 'ha', + 'harr harr') +MovieHealLaughterHits1 = ('Ha Ha Ha', + 'Hee Hee', + 'Tee Hee', + 'Ha Ha') +MovieHealLaughterHits2 = ('BWAH HAH HAH!', 'HO HO HO!', 'HA HA HA!') +MovieSOSCallHelp = '%s HELP!' +MovieSOSWhisperHelp = '%s needs help in battle!' +MovieSOSObserverHelp = 'HELP!' +MovieNPCSOSGreeting = 'Hi %s! Glad to help!' +MovieNPCSOSGoodbye = 'See you later!' +MovieNPCSOSToonsHit = 'Toons Always Hit!' +MovieNPCSOSCogsMiss = 'Cogs Always Miss!' +MovieNPCSOSRestockGags = 'Restocking %s gags!' +MovieNPCSOSHeal = 'Heal' +MovieNPCSOSTrap = 'Trap' +MovieNPCSOSLure = 'Lure' +MovieNPCSOSSound = 'Sound' +MovieNPCSOSThrow = 'Throw' +MovieNPCSOSSquirt = 'Squirt' +MovieNPCSOSDrop = 'Drop' +MovieNPCSOSAll = 'All' +MoviePetSOSTrickFail = 'Sigh...' +MoviePetSOSTrickSucceedBoy = 'Good boy!' +MoviePetSOSTrickSucceedGirl = 'Good girl!' +MovieSuitCancelled = 'CANCELLED\nCANCELLED\nCANCELLED' +RewardPanelToonTasks = 'ToonTasks' +RewardPanelItems = 'Items Recovered' +RewardPanelMissedItems = 'Items Not Recovered' +RewardPanelQuestLabel = 'Quest %s' +RewardPanelCongratsStrings = ['Yeah!', + 'Congratulations!', + 'Wow!', + 'Cool!', + 'Awesome!', + 'Toon-tastic!'] +RewardPanelNewGag = 'New %(gagName)s gag for %(avName)s!' +RewardPanelUberGag = '%(avName)s earned the %(gagName)s gag with %(exp)s experience points!' +RewardPanelEndTrack = 'Yay! %(avName)s has reached the end of the %(gagName)s Gag Track!' +RewardPanelMeritsMaxed = 'Maxed' +RewardPanelMeritBarLabels = ['Stock Options', + 'Jury Notices', + 'Cogbucks', + 'Merits'] +RewardPanelMeritAlert = 'Ready for promotion!' +RewardPanelCogPart = 'You gained a Cog disguise part!' +RewardPanelPromotion = 'Ready for promotion in %s track!' +RewardPanelSkip = 'Skip' +CheesyEffectDescriptions = [('Normal Toon', 'you will be normal'), + ('Big head', 'you will have a big head'), + ('Small head', 'you will have a small head'), + ('Big legs', 'you will have big legs'), + ('Small legs', 'you will have small legs'), + ('Big toon', 'you will be a little bigger'), + ('Small toon', 'you will be a little smaller'), + ('Flat portrait', 'you will be two-dimensional'), + ('Flat profile', 'you will be two-dimensional'), + ('Transparent', 'you will be transparent'), + ('No color', 'you will be colorless'), + ('Invisible toon', 'you will be invisible')] +CheesyEffectIndefinite = 'Until you choose another effect, %(effectName)s%(whileIn)s.' +CheesyEffectMinutes = 'For the next %(time)s minutes, %(effectName)s%(whileIn)s.' +CheesyEffectHours = 'For the next %(time)s hours, %(effectName)s%(whileIn)s.' +CheesyEffectDays = 'For the next %(time)s days, %(effectName)s%(whileIn)s.' +CheesyEffectWhileYouAreIn = ' while you are in %s' +CheesyEffectExceptIn = ', except in %s' +SuitFlunky = 'Flunky' +SuitPencilPusher = 'Pencil Pusher' +SuitYesman = 'Yesman' +SuitMicromanager = 'Micro\x03manager' +SuitDownsizer = 'Downsizer' +SuitHeadHunter = 'Head Hunter' +SuitCorporateRaider = 'Corporate Raider' +SuitTheBigCheese = 'The Big Cheese' +SuitColdCaller = 'Cold Caller' +SuitTelemarketer = 'Tele\x03marketer' +SuitNameDropper = 'Name Dropper' +SuitGladHander = 'Glad Hander' +SuitMoverShaker = 'Mover & Shaker' +SuitTwoFace = 'Two-Face' +SuitTheMingler = 'The Mingler' +SuitMrHollywood = 'Mr. Hollywood' +SuitShortChange = 'Short Change' +SuitPennyPincher = 'Penny Pincher' +SuitTightwad = 'Tightwad' +SuitBeanCounter = 'Bean Counter' +SuitNumberCruncher = 'Number Cruncher' +SuitMoneyBags = 'Money Bags' +SuitLoanShark = 'Loan Shark' +SuitRobberBaron = 'Robber Baron' +SuitBottomFeeder = 'Bottom Feeder' +SuitBloodsucker = 'Blood\x03sucker' +SuitDoubleTalker = 'Double Talker' +SuitAmbulanceChaser = 'Ambulance Chaser' +SuitBackStabber = 'Back Stabber' +SuitSpinDoctor = 'Spin Doctor' +SuitLegalEagle = 'Legal Eagle' +SuitBigWig = 'Big Wig' +SuitFlunkyS = 'a Flunky' +SuitPencilPusherS = 'a Pencil Pusher' +SuitYesmanS = 'a Yesman' +SuitMicromanagerS = 'a Micromanager' +SuitDownsizerS = 'a Downsizer' +SuitHeadHunterS = 'a Head Hunter' +SuitCorporateRaiderS = 'a Corporate Raider' +SuitTheBigCheeseS = 'a The Big Cheese' +SuitColdCallerS = 'a Cold Caller' +SuitTelemarketerS = 'a Telemarketer' +SuitNameDropperS = 'a Name Dropper' +SuitGladHanderS = 'a Glad Hander' +SuitMoverShakerS = 'a Mover & Shaker' +SuitTwoFaceS = 'a Two-Face' +SuitTheMinglerS = 'a The Mingler' +SuitMrHollywoodS = 'a Mr. Hollywood' +SuitShortChangeS = 'a Short Change' +SuitPennyPincherS = 'a Penny Pincher' +SuitTightwadS = 'a Tightwad' +SuitBeanCounterS = 'a Bean Counter' +SuitNumberCruncherS = 'a Number Cruncher' +SuitMoneyBagsS = 'a Money Bags' +SuitLoanSharkS = 'a Loan Shark' +SuitRobberBaronS = 'a Robber Baron' +SuitBottomFeederS = 'a Bottom Feeder' +SuitBloodsuckerS = 'a Bloodsucker' +SuitDoubleTalkerS = 'a Double Talker' +SuitAmbulanceChaserS = 'an Ambulance Chaser' +SuitBackStabberS = 'a Back Stabber' +SuitSpinDoctorS = 'a Spin Doctor' +SuitLegalEagleS = 'a Legal Eagle' +SuitBigWigS = 'a Big Wig' +SuitFlunkyP = 'Flunkies' +SuitPencilPusherP = 'Pencil Pushers' +SuitYesmanP = 'Yesmen' +SuitMicromanagerP = 'Micromanagers' +SuitDownsizerP = 'Downsizers' +SuitHeadHunterP = 'Head Hunters' +SuitCorporateRaiderP = 'Corporate Raiders' +SuitTheBigCheeseP = 'The Big Cheeses' +SuitColdCallerP = 'Cold Callers' +SuitTelemarketerP = 'Telemarketers' +SuitNameDropperP = 'Name Droppers' +SuitGladHanderP = 'Glad Handers' +SuitMoverShakerP = 'Movers & Shakers' +SuitTwoFaceP = 'Two-Faces' +SuitTheMinglerP = 'The Minglers' +SuitMrHollywoodP = 'Mr. Hollywoods' +SuitShortChangeP = 'Short Changes' +SuitPennyPincherP = 'Penny Pinchers' +SuitTightwadP = 'Tightwads' +SuitBeanCounterP = 'Bean Counters' +SuitNumberCruncherP = 'Number Crunchers' +SuitMoneyBagsP = 'Money Bags' +SuitLoanSharkP = 'Loan Sharks' +SuitRobberBaronP = 'Robber Barons' +SuitBottomFeederP = 'Bottom Feeders' +SuitBloodsuckerP = 'Bloodsuckers' +SuitDoubleTalkerP = 'Double Talkers' +SuitAmbulanceChaserP = 'Ambulance Chasers' +SuitBackStabberP = 'Back Stabbers' +SuitSpinDoctorP = 'Spin Doctors' +SuitLegalEagleP = 'Legal Eagles' +SuitBigWigP = 'Big Wigs' +SuitFaceoffDefaultTaunts = ['Boo!'] +SuitAttackDefaultTaunts = ['Take that!', 'Take a memo on this!'] +SuitAttackNames = {'Audit': 'Audit!', + 'Bite': 'Bite!', + 'BounceCheck': 'Bounce Check!', + 'BrainStorm': 'Brain Storm!', + 'BuzzWord': 'Buzz Word!', + 'Calculate': 'Calculate!', + 'Canned': 'Canned!', + 'Chomp': 'Chomp!', + 'CigarSmoke': 'Cigar Smoke!', + 'ClipOnTie': 'Clip On Tie!', + 'Crunch': 'Crunch!', + 'Demotion': 'Demotion!', + 'Downsize': 'Downsize!', + 'DoubleTalk': 'Double Talk!', + 'EvictionNotice': 'Eviction Notice!', + 'EvilEye': 'Evil Eye!', + 'Filibuster': 'Filibuster!', + 'FillWithLead': 'Fill With Lead!', + 'FiveOClockShadow': "Five O'Clock Shadow!", + 'FingerWag': 'Finger Wag!', + 'Fired': 'Fired!', + 'FloodTheMarket': 'Flood The Market!', + 'FountainPen': 'Fountain Pen!', + 'FreezeAssets': 'Freeze Assets!', + 'Gavel': 'Gavel!', + 'GlowerPower': 'Glower Power!', + 'GuiltTrip': 'Guilt Trip!', + 'HalfWindsor': 'Half Windsor!', + 'HangUp': 'Hang Up!', + 'HeadShrink': 'Head Shrink!', + 'HotAir': 'Hot Air!', + 'Jargon': 'Jargon!', + 'Legalese': 'Legalese!', + 'Liquidate': 'Liquidate!', + 'MarketCrash': 'Market Crash!', + 'MumboJumbo': 'Mumbo Jumbo!', + 'ParadigmShift': 'Paradigm Shift!', + 'PeckingOrder': 'Pecking Order!', + 'PickPocket': 'Pick Pocket!', + 'PinkSlip': 'Pink Slip!', + 'PlayHardball': 'Play Hardball!', + 'PoundKey': 'Pound Key!', + 'PowerTie': 'Power Tie!', + 'PowerTrip': 'Power Trip!', + 'Quake': 'Quake!', + 'RazzleDazzle': 'Razzle Dazzle!', + 'RedTape': 'Red Tape!', + 'ReOrg': 'Re-Org!', + 'RestrainingOrder': 'Restraining Order!', + 'Rolodex': 'Rolodex!', + 'RubberStamp': 'Rubber Stamp!', + 'RubOut': 'Rub Out!', + 'Sacked': 'Sacked!', + 'SandTrap': 'Sand Trap!', + 'Schmooze': 'Schmooze!', + 'Shake': 'Shake!', + 'Shred': 'Shred!', + 'SongAndDance': 'Song And Dance!', + 'Spin': 'Spin!', + 'Synergy': 'Synergy!', + 'Tabulate': 'Tabulate!', + 'TeeOff': 'Tee Off!', + 'ThrowBook': 'Throw Book!', + 'Tremor': 'Tremor!', + 'Watercooler': 'Watercooler!', + 'Withdrawal': 'Withdrawal!', + 'WriteOff': 'Write Off!'} +SuitAttackTaunts = {'Audit': ["I believe your books don't balance.", + "Looks like you're in the red.", + 'Let me help you with your books.', + 'Your debit column is much too high.', + "Let's check your assets.", + 'This will put you in debt.', + "Let's take a close look at what you owe.", + 'This should drain your account.', + 'Time for you to account for your expenses.', + "I've found an error in your books."], + 'Bite': ['Would you like a bite?', + 'Try a bite of this!', + "You're biting off more than you can chew.", + 'My bite is bigger than my bark.', + 'Bite down on this!', + 'Watch out, I may bite.', + "I don't just bite when I'm cornered.", + "I'm just gonna grab a quick bite.", + "I haven't had a bite all day.", + 'I just want a bite. Is that too much to ask?'], + 'BounceCheck': ["Ah, too bad, you're funless.", + 'You have a payment due.', + 'I believe this check is yours.', + 'You owed me for this.', + "I'm collecting on this debt.", + "This check isn't going to be tender.", + "You're going to be charged for this.", + 'Check this out.', + 'This is going to cost you.', + "I'd like to cash this in.", + "I'm just going to kick this back to you.", + 'This is one sour note.', + "I'm deducting a service charge."], + 'BrainStorm': ['I forecast rain.', + 'Hope you packed your umbrella.', + 'I want to enlighten you.', + 'How about a few rain DROPS?', + 'Not so sunny now, are you Toon?', + 'Ready for a down pour?', + "I'm going to take you by storm.", + 'I call this a lightning attack.', + 'I love to be a wet blanket.'], + 'BuzzWord': ['Pardon me if I drone on.', + 'Have you heard the latest?', + 'Can you catch on to this?', + 'See if you can hum this Toon.', + 'Let me put in a good word for you.', + 'I\'ll "B" perfectly clear.', + 'You should "B" more careful.', + 'See if you can dodge this swarm.', + "Careful, you're about to get stung.", + 'Looks like you have a bad case of hives.'], + 'Calculate': ['These numbers do add up!', + 'Did you count on this?', + "Add it up, you're going down.", + 'Let me help you add this up.', + 'Did you register all your expenses?', + "According to my calculations, you won't be around much longer.", + "Here's the grand total.", + 'Wow, your bill is adding up.', + 'Try fiddling with these numbers!', + Cogs + ': 1 Toons: 0'], + 'Canned': ['Do you like it out of the can?', + '"Can" you handle this?', + "This one's fresh out of the can!", + 'Ever been attacked by canned goods before?', + "I'd like to donate this canned good to you!", + 'Get ready to "Kick the can"!', + 'You think you "can", you think you "can".', + "I'll throw you in the can!", + "I'm making me a can o' toon-a!", + "You don't taste so good out of the can."], + 'Chomp': ['Take a look at these chompers!', + 'Chomp, chomp, chomp!', + "Here's something to chomp on.", + 'Looking for something to chomp on?', + "Why don't you chomp on this?", + "I'm going to have you for dinner.", + 'I love to feed on Toons!'], + 'CigarSmoke': ['Gentlemen.', + "It's a good day to have a smoke.", + 'Take a breath of this.', + "It's tradition you know.", + 'Another day another dollar.', + 'I always have the occasional cigar.', + 'I need a good smoke.', + 'Smoking is a dirty habit.'], + 'ClipOnTie': ['Better dress for our meeting.', + "You can't go OUT without your tie.", + 'The best dressed ' + Cogs + ' wear them.', + 'Try this on for size.', + 'You should dress for success.', + 'No tie, no service.', + 'Do you need help putting this on?', + 'Nothing says powerful like a good tie.', + "Let's see if this fits.", + 'This is going to choke you up.', + "You'll want to dress up before you go OUT.", + "I think I'll tie you up."], + 'Crunch': ["Looks like you're in a crunch.", + "It's crunch time!", + "I'll give you something to crunch on!", + 'Crunch on this!', + 'I pack quite a crunch.', + 'Which do you prefer, smooth or crunchy?', + "I hope you're ready for crunch time.", + "It sounds like you're getting crunched!", + "I'll crunch you like a can."], + 'Demotion': ["You're moving down the corporate ladder.", + "I'm sending you back to the Mail Room.", + 'Time to turn in your nameplate.', + "You're going down, clown.", + "Looks like you're stuck.", + "You're going nowhere fast.", + "You're in a dead end position.", + "You won't be moving anytime soon.", + "You're not going anywhere.", + 'This will go on your permanent record.'], + 'Downsize': ['Come on down!', + 'Do you know how to get down?', + "Let's get down to business.", + "What's wrong? You look down.", + 'Going down?', + "What's goin' down? You!", + 'Why pick on people my own size?', + "Why don't I size you up, or should I say, down?", + 'Would you like a smaller size for just a quarter more?', + 'Try this on for size!', + 'You can get this in a smaller size.', + 'This attack is one size fits all!'], + 'EvictionNotice': ["It's moving time.", + 'Pack your bags, Toon.', + 'Time to make some new living arrangements.', + 'Consider yourself served.', + "You're behind on your lease.", + 'This will be extremely unsettling.', + "You're about to be uprooted.", + "I'm going to send you packing.", + "You're out of place.", + 'Prepare to be relocated.', + "You're in a hostel position."], + 'EvilEye': ["I'm giving you the evil eye.", + 'Could you eye-ball this for me?', + "Wait. I've got something in my eye.", + "I've got my eye on you!", + 'Could you keep an eye on this for me?', + "I've got a real eye for evil.", + "I'll poke you in the eye!", + '"Eye" am as evil as they come!', + "I'll put you in the eye of the storm!", + "I'm rolling my eye at you."], + 'Filibuster': ["Shall I fill 'er up?", + 'This is going to take awhile.', + 'I could do this all day.', + "I don't even need to take a breath.", + 'I keep going and going and going.', + 'I never get tired of this one.', + 'I can talk a blue streak.', + 'Mind if I bend your ear?', + "I think I'll shoot the breeze.", + 'I can always get a word in edgewise.'], + 'FingerWag': ['I have told you a thousand times.', + 'Now see here Toon.', + "Don't make me laugh.", + "Don't make me come over there.", + "I'm tired of repeating myself.", + "I believe we've been over this.", + 'You have no respect for us ' + Cogs + '.', + "I think it's time you pay attention.", + 'Blah, Blah, Blah, Blah, Blah.', + "Don't make me stop this meeting.", + 'Am I going to have to separate you?', + "We've been through this before."], + 'Fired': ['I hope you brought some marshmallows.', + "It's going to get rather warm around here.", + 'This should take the chill out of the air.', + "I hope you're cold blooded.", + 'Hot, hot and hotter.', + 'You better stop, drop, and roll!', + "You're outta here.", + 'How does "well-done" sound?', + 'Can you say ouch?', + 'Hope you wore sunscreen.', + 'Do you feel a little toasty?', + "You're going down in flames.", + "You'll go out in a blaze.", + "You're a flash in the pan.", + 'I think I have a bit of a flare about me.', + "I just sparkle, don't I?", + 'Oh look, a crispy critter.', + "You shouldn't run around half baked."], + 'FountainPen': ['This is going to leave a stain.', + "Let's ink this deal.", + 'Be prepared for some permanent damage.', + "You're going to need a good dry cleaner.", + 'You should change.', + 'This fountain pen has such a nice font.', + "Here, I'll use my pen.", + 'Can you read my writing?', + 'I call this the plume of doom.', + "There's a blot on your performance.", + "Don't you hate when this happens?"], + 'FreezeAssets': ['Your assets are mine.', + 'Do you feel a draft?', + "Hope you don't have plans.", + 'This should keep you on ice.', + "There's a chill in the air.", + 'Winter is coming early this year.', + 'Are you feeling a little blue?', + 'Let me crystallize my plan.', + "You're going to take this hard.", + 'This should cause freezer burn.', + 'I hope you like cold cuts.', + "I'm very cold blooded."], + 'GlowerPower': ['You looking at me?', + "I'm told I have very piercing eyes.", + 'I like to stay on the cutting edge.', + "Jeepers, Creepers, don't you love my peepers?", + "Here's looking at you kid.", + "How's this for expressive eyes?", + 'My eyes are my strongest feature.', + 'The eyes have it.', + 'Peeka-boo, I see you.', + 'Look into my eyes...', + 'Shall we take a peek at your future?'], + 'GuiltTrip': ["I'll lay a real guilt trip on you!", + 'Feeling guilty?', + "It's all your fault!", + 'I always blame everything on you.', + 'Wallow in your own guilt!', + 'Did you have a nice trip?', + "You had better say you're sorry.", + "I wouldn't forgive you in a million years!", + 'See you next fall.', + 'Call me when you get back from your trip.', + 'When do you get back from your trip?'], + 'HalfWindsor': ["This is the fanciest tie you'll ever see!", + 'Try not to get too winded.', + "This isn't even half the trouble you're in.", + "You're lucky I don't have a whole windsor.", + "You can't afford this tie.", + "I bet you've never even SEEN a half windsor!", + 'This tie is out of your league.', + "I shouldn't even waste this tie on you.", + "You're not even worth half of this tie!"], + 'HangUp': ["You've been disconnected.", + 'Good bye!', + "It's time I end our connection.", + "...and don't call back!", + 'Click!', + 'This conversation is over.', + "I'm severing this link.", + 'I think you have a few hang ups.', + "It appears you've got a weak link.", + 'Your time is up.', + 'I hope you receive this loud and clear.', + 'You got the wrong number.'], + 'HeadShrink': ["Looks like you're seeing a shrink.", + 'Honey, I shrunk the toon.', + "Hope this doesn't shrink your pride.", + 'Do you shrink in the wash?', + 'I shrink therefore I am.', + "It's nothing to lose your head over.", + 'Are you going out of your head?', + 'Heads up! Or should I say, down.', + 'Objects may be larger than they appear.', + 'Good Toons come in small packages.'], + 'HotAir': ["We're having a heated discussion.", + "You're experiencing a heat wave.", + "I've reached my boiling point.", + 'This should cause some wind burn.', + 'I hate to grill you, but...', + "Always remember, where there's smoke, there's fire.", + "You're looking a little burned out.", + 'Another meeting up in smoke.', + "Guess it's time to add fuel to the fire.", + 'Let me kindle a working relationship.', + 'I have some glowing remarks for you.', + 'Air Raid!!!'], + 'Jargon': ['What nonsense.', + 'See if you can make sense of this.', + 'I hope you get this loud and clear.', + "Looks like I'm going to have to raise my voice.", + 'I insist on having my say.', + "I'm very outspoken.", + 'I must pontificate on this subject.', + 'See, words can hurt you.', + 'Did you catch my meaning?', + 'Words, words, words, words, words.'], + 'Legalese': ['You must cease and desist.', + 'You will be defeated, legally speaking.', + 'Are you aware of the legal ramifications?', + "You aren't above the law!", + 'There should be a law against you.', + "There's no ex post facto with me!", + "The opinions expressed in this attack are not those of Toontown Stride.", + 'We cannot be held responsible for damages suffered in this attack.', + 'Your results for this attack may vary.', + 'This attack is void where prohibited.', + "You don't fit into my legal system!", + "You can't handle the legal matters."], + 'Liquidate': ['I like to keep things fluid.', + 'Are you having some cash flow problems?', + "I'll have to purge your assets.", + 'Time for you to go with the flow.', + "Remember it's slippery when wet.", + 'Your numbers are running.', + 'You seem to be slipping.', + "It's all crashing down on you.", + "I think you're diluted.", + "You're all washed up."], + 'MarketCrash': ["I'm going to crash your party.", + "You won't survive the crash.", + "I'm more than the market can bear.", + "I've got a real crash course for you!", + "Now I'll come crashing down.", + "I'm a real bull in the market.", + 'Looks like the market is going down.', + 'You had better get out quick!', + 'Sell! Sell! Sell!', + 'Shall I lead the recession?', + "Everybody's getting out, shouldn't you?"], + 'MumboJumbo': ['Let me make this perfectly clear.', + "It's as simple as this.", + "This is how we're going to do this.", + 'Let me supersize this for you.', + 'You might call this technobabble.', + 'Here are my five-dollar words.', + 'Boy, this is a mouth full.', + 'Some call me bombastic.', + 'Let me just interject this.', + 'I believe these are the right words.'], + 'ParadigmShift': ["Watch out! I'm rather shifty.", + 'Prepare to have your paradigm shifted!', + "Isn't this an interesting paradigm.", + "You'll get shifted out of place.", + "I guess it's your shift now.", + 'Your shift is up!', + "You've never shifted this much in your life.", + "I'm giving you the bad shift!", + 'Look into my shifty eyes!'], + 'PeckingOrder': ["This one's for the birds.", + 'Get ready for a bird bath.', + "Looks like you're going to hit a birdie.", + 'Some think this attack is fowl.', + "You're on the bottom of the pecking order.", + 'A bird in my hand is worth ten on your head!', + 'Your order is up; the pecking order!', + "Why don't I peck on someone my own size? Nah.", + 'Birds of a feather strike together.'], + 'PickPocket': ['Let me check your valuables.', + "Hey, what's that over there?", + 'Like taking candy from a baby.', + 'What a steal.', + "I'll hold this for you.", + 'Watch my hands at all times.', + 'The hand is quicker than the eye.', + "There's nothing up my sleeve.", + 'The management is not responsible for lost items.', + "Finder's keepers.", + "You'll never see it coming.", + 'One for me, none for you.', + "Don't mind if I do.", + "You won't be needing this..."], + 'PinkSlip': ['Try not to slip up.', + "Are you frightened? You've turned pink!", + 'This one will surely slip you up.', + 'Oops, I guess you slipped there, huh?', + "Watch yourself, wouldn't want to slip!", + "This one's slippery when wet.", + "I'll just slip this one in.", + "Don't mind if you slip by, do you?", + "Pink isn't really your color.", + "Here's your pink slip, you're outta here!"], + 'PlayHardball': ['So you wanna play hardball?', + "You don't wanna play hardball with me.", + 'Batter up!', + 'Hey batter, batter!', + "And here's the pitch...", + "You're going to need a relief pitcher.", + "I'm going to knock you out of the park.", + "Once you get hit, you'll run home.", + 'This is your final inning!', + "You can't play with me!", + "I'll strike you out.", + "I'm throwing you a real curve ball!"], + 'PoundKey': ['Time to return some calls.', + "I'd like to make a collect call.", + "Ring-a-ling - it's for you!", + "I've been wanting to drop a pound or two.", + 'I have a lot of clout.', + 'This may cause a slight pounding sensation.', + "I'll just punch in this number.", + 'Let me call up a little surprise.', + "I'll ring you up.", + "O.K. Toon, it's the pound for you."], + 'PowerTie': ["I'll call later, you looked tied up.", + 'Are you ready to tie die?', + "Ladies and gentlemen, it's a tie!", + 'You had better learn how to tie.', + "I'll have you tongue-tied!", + "This is the worst tie you'll ever get!", + 'Can you feel the power?', + 'My powers are far too great for you!', + "I've got the power!", + "By the powers vested in me, I'll tie you up."], + 'PowerTrip': ["Pack your bags, we're taking a little trip.", + 'Did you have a nice trip?', + "Nice trip, I guess I'll see you next fall.", + 'How was your trip?', + 'Sorry to trip you up there!', + 'You look a little tripped up.', + "Now you see who's in power!", + 'I am much more powerful than you.', + "Who's got the power now?", + "You can't fight the power.", + 'Power corrupts, especially in my hands!'], + 'Quake': ["Let's quake, rattle, and roll.", + "I've got a whole lot of quakin' goin' on!", + "I see you quakin' in your shoes.", + "Here it comes, it's the big one!", + "This one's off the Richter scale.", + 'Now the earth will quake!', + "Hey, what's shakin'? You!", + 'Ever been in an earthquake?', + "You're on shaky ground now!"], + 'RazzleDazzle': ['Read my lips.', + 'How about these choppers?', + "Aren't I charming?", + "I'm going to wow you.", + 'My dentist does excellent work.', + "Blinding aren't they?", + "Hard to believe these aren't real.", + "Shocking, aren't they?", + "I'm going to cap this off.", + 'I floss after every meal.', + 'Say Cheese!'], + 'RedTape': ['This should wrap things up.', + "I'm going to tie you up for awhile.", + "You're on a roll.", + 'See if you can cut through this.', + 'This will get sticky.', + "Hope you're claustrophobic.", + "I'll make sure you stick around.", + 'Let me keep you busy.', + 'Just try to unravel this.', + 'I want this meeting to stick with you.'], + 'ReOrg': ["You don't like the way I reorganized things!", + 'Perhaps a little reorganization is in order.', + "You're not that bad, you just need to be reorganized.", + 'Do you like my organizational skills.', + "I just thought I'd give things a new look.", + 'You need to get organized!', + "You're looking a little disorganized.", + 'Hold on while I reorganize your thoughts.', + "I'll just wait for you to get a little organized.", + "You don't mind if I just reorganize a bit?"], + 'RestrainingOrder': ['You should show a little restraint.', + "I'm slapping you with a restraining order!", + "You can't come within five feet of me.", + 'Perhaps you better keep your distance.', + 'You should be restrained.', + Cogs + '! Restrain that Toon!', + 'Try and restrain yourself.', + "I hope I'm being too much of a restraint on you.", + 'See if you can lift these restraints!', + "I'm ordering you to restrain!", + "Why don't we start with basic restraining?"], + 'Rolodex': ["Your card's in here somewhere.", + "Here's the number for a pest exterminator.", + 'I want to give you my card.', + "I've got your number right here.", + "I've got you covered from a-z.", + "You'll flip over this.", + 'Take this for a spin.', + 'Watch out for paper cuts.', + "I'll let my fingers do the knocking.", + 'Is this how I can contact you?', + 'I want to make sure we stay in touch.'], + 'RubberStamp': ['I always make a good impression.', + "It's important to apply firm and even pressure.", + 'A perfect imprint every time.', + 'I want to stamp you out.', + 'You must be RETURNED TO SENDER.', + "You've been CANCELLED.", + 'You have a PRIORITY delivery.', + "I'll make sure you RECEIVED my message.", + "You're not going anywhere - you have POSTAGE DUE.", + "I'll need a response ASAP."], + 'RubOut': ['And now for my disappearing act.', + "I sense I've lost you somewhere.", + 'I decided to leave you out.', + 'I always rub out all obstacles.', + "I'll just erase this error.", + 'I can make any nuisance disappear.', + 'I like things neat and tidy.', + 'Please try and stay animated.', + "Now I see you... now I don't.", + 'This will cause some fading.', + "I'm going to eliminate the problem.", + 'Let me take care of your problem areas.'], + 'Sacked': ["Looks like you're getting sacked.", + "This one's in the bag.", + "You've been bagged.", + 'Paper or plastic?', + 'My enemies shall be sacked!', + 'I hold the Toontown record in sacks per game.', + "You're no longer wanted around here.", + "Your time is up around here, you're being sacked!", + 'Let me bag that for you.', + 'No defense can match my sack attack!'], + 'Schmooze': ["You'll never see this coming.", + 'This will look good on you.', + "You've earned this.", + "I don't mean to gush.", + 'Flattery will get me everywhere.', + "I'm going to pile it on now.", + 'Time to lay it on thick.', + "I'm going to get on your good side.", + 'That deserves a good slap on the back.', + "I'm going to ring your praises.", + 'I hate to knock you off your pedestal, but...'], + 'Shake': ["You're right on the epicenter.", + "You're standing on a fault line.", + "It's going to be a bumpy ride.", + 'I think of this as a natural disaster.', + "It's a disaster of seismic proportions.", + "This one's off the Richter scale.", + 'Time to duck and cover.', + 'You seem disturbed.', + 'Ready for a jolt?', + "I'll have you shaken, not stirred.", + 'This will shake you up.', + 'I suggest a good escape plan.'], + 'Shred': ['I need to get rid of some hazardous waste.', + "I'm increasing my throughput.", + "I think I'll dispose of you right now.", + 'This will get rid of the evidence.', + "There's no way to prove it now.", + 'See if you can put this back together.', + 'This should cut you down to size.', + "I'm going to rip that idea to shreds.", + "We don't want this to fall into the wrong hands.", + 'Easy come, easy go.', + "Isn't this your last shred of hope?"], + 'Spin': ['What do you say we go for a little spin?', + 'Do you use the spin cycle?', + "This'll really make your head spin!", + "Here's my spin on things.", + "I'll take you for a spin.", + 'How do you like to "spin" your time?', + "Watch it. Wouldn't want to spin out of control!", + "Oh what a spin you're in!", + 'My attacks will make your head spin!'], + 'Synergy': ["I'm taking this to committee.", + "Your project's been cancelled.", + "Your budget's been cut.", + "We're restructuring your division.", + 'I put it to a vote, and you lose.', + 'I just received the final approval.', + 'A good team can get rid of any problem.', + "I'll get back to you on this.", + "Let's get right to business.", + 'Consider this a Synergy crisis.'], + 'Tabulate': ["This doesn't add up.", + 'By my count, you lose.', + "You're racking up quite a tab.", + "I'll have you totaled in a moment.", + 'Are you ready for these numbers?', + 'Your bill is now due and payable.', + 'Time for the reckoning.', + 'I like to put things in order.', + 'And the tally is...', + 'These numbers should prove to be quite powerful.'], + 'TeeOff': ["You're not up to par.", + 'Fore!', + "I'm getting teed off.", + "Caddie, I'll need my driver!", + 'Just try and avoid this hazard.', + 'Swing!', + 'This is a sure hole in one.', + "You're in my fairway.", + 'Notice my grip.', + 'Watch the birdie!', + 'Keep your eye on the ball!', + 'Mind if I play through?'], + 'ThrowBook': ['My book from Law School should help.', + 'You better have a good lawyer.', + "I'll have to take legal action.", + 'Legal Eagle will be pleased to see this.', + 'Objection!', + 'Under article 14 subsection C...', + 'I see you have broken the law!', + "It seems you don't understand the authority of law.", + "I'll see you in court, Toon."], + 'Tremor': ['Did you feel that?', + 'Not afraid of a little tremor are you?', + 'A tremor is only the beginning.', + 'You look jittery.', + "I'll shake things up a bit!", + 'Are you ready to rumble?', + "What's wrong? You look shaken.", + 'Tremor with fear!', + 'Why are you tremoring with fear?'], + 'Watercooler': ['This ought to cool you off.', + "Isn't this refreshing?", + 'I deliver.', + 'Straight from the tap - into your lap.', + "What's the matter, it's just spring water.", + "Don't worry, it's purified.", + 'Ah, another satisfied customer.', + "It's time for your daily delivery.", + "Hope your colors don't run.", + 'Care for a drink?', + 'It all comes out in the wash.', + "The drink's on you."], + 'Withdrawal': ["I believe you're overdrawn.", + 'I hope your balance is high enough for this.', + 'Take that, with interest.', + 'Your balance is dropping.', + "You're going to need to make a deposit soon.", + "You've suffered an economic collapse.", + "I think you're in a slump.", + 'Your finances have taken a decline.', + 'I foresee a definite downturn.', + "It's a reversal of fortune."], + 'WriteOff': ['Let me increase your losses.', + "Let's make the best of a bad deal.", + 'Time to balance the books.', + "This won't look good on your books.", + "I'm looking for some dividends.", + 'You must account for your losses.', + 'You can forget about a bonus.', + "I'll shuffle your accounts around.", + "You're about to suffer some losses.", + 'This is going to hurt your bottom line.']} +WaitingForOtherToons = 'Waiting for other toons...' +WaitingForOtherToonsDots = 'Waiting for other toons%s' +ElevatorHopOff = 'Hop off' +ElevatorLeaderOff = 'Only your leader can decide when to hop off.' +ElevatorHopOK = 'Okay' +ElevatorGroupMember = 'Only your group leader can\n decide when to board.' +CogsIncExt = ', Inc.' +CogsIncModifier = '%s' + CogsIncExt +CogsInc = Cogs.upper() + CogsIncExt +CogdominiumsExt = ' Field Office' +Cogdominiums = Cog.upper() + CogdominiumsExt +DoorKnockKnock = 'Knock, knock.' +DoorWhosThere = "Who's there?" +DoorWhoAppendix = ' who?' +DoorNametag = 'Door' +FADoorCodes_UNLOCKED = None +FADoorCodes_TALK_TO_TOM = 'You need gags! Go talk to Tutorial Tom!' +FADoorCodes_DEFEAT_FLUNKY_HQ = 'Come back here when you have defeated the Flunky!' +FADoorCodes_TALK_TO_HQ = 'Go get your reward from HQ Harry!' +FADoorCodes_WRONG_DOOR_HQ = 'Wrong door! Take the other door to the playground!' +FADoorCodes_GO_TO_PLAYGROUND = 'Wrong way! You need to go to the playground!' +FADoorCodes_DEFEAT_FLUNKY_TOM = 'Walk up to that Flunky to battle him!' +FADoorCodes_TALK_TO_HQ_TOM = 'Go get your reward from Toon Headquarters!' +FADoorCodes_SUIT_APPROACHING = None +FADoorCodes_BUILDING_TAKEOVER = "Watch out! There's a Cog in there!" +FADoorCodes_SB_DISGUISE_INCOMPLETE = "You'll get caught going in there as a Toon! You need to complete your Sellbot Disguise first!\n\nBuild your Sellbot Disguise out of parts from the Factory." +FADoorCodes_CB_DISGUISE_INCOMPLETE = "You'll get caught going in there as a Toon! You need to complete your Cashbot Disguise first!\n\nBuild your Cashbot Disguise by doing ToonTasks in Donald's Dreamland." +FADoorCodes_LB_DISGUISE_INCOMPLETE = "You'll get caught going in there as a Toon! You need to complete your Lawbot Disguise first!\n\nBuild your Lawbot Disguise by doing the ToonTasks after Donald's Dreamland." +FADoorCodes_BB_DISGUISE_INCOMPLETE = "You'll get caught going in there as a Toon! You need to complete your Bossbot Disguise first!\n\nBuild your Bossbot Disguise by doing the ToonTasks after Donald's Dreamland." +KnockKnockJokes = [['Who', "Bad echo in here, isn't there?"], + ['Dozen', 'Dozen anybody want to let me in?'], + ['Freddie', 'Freddie or not, here I come.'], + ['Dishes', 'Dishes your friend, let me in.'], + ['Wooden shoe', 'Wooden shoe like to know.'], + ['Betty', "Betty doesn't know who I am."], + ['Kent', 'Kent you tell?'], + ['Noah', "Noah don't know who either."], + ["I don't know", 'Neither do I, I keep telling you that.'], + ['Howard', 'Howard I know?'], + ['Emma', 'Emma so glad you asked me that.'], + ['Auto', "Auto know, but I've forgotten."], + ['Jess', 'Jess me and my shadow.'], + ['One', 'One-der why you keep asking that?'], + ['Alma', 'Alma not going to tell you!'], + ['Zoom', 'Zoom do you expect?'], + ['Amy', "Amy fraid I've forgotten."], + ['Arfur', 'Arfur got.'], + ['Ewan', 'No, just me'], + ['Cozy', "Cozy who's knocking will you?"], + ['Sam', 'Sam person who knocked on the door last time.'], + ['Fozzie', 'Fozzie hundredth time, my name is ' + Flippy + '.'], + ['Deduct', 'Donald Deduct.'], + ['Max', 'Max no difference, just open the door.'], + ['N.E.', 'N.E. body you like, let me in.'], + ['Amos', 'Amos-quito bit me.'], + ['Alma', "Alma candy's gone."], + ['Bruce', "I Bruce very easily, don't hit me."], + ['Colleen', "Colleen up your room, it's filthy."], + ['Elsie', 'Elsie you later.'], + ['Hugh', 'Hugh is going to let me in?'], + ['Hugo', "Hugo first - I'm scared."], + ['Ida', 'Ida know. Sorry!'], + ['Isabel', 'Isabel on a bike really necessary?'], + ['Joan', "Joan call us, we'll call you."], + ['Kay', 'Kay, L, M, N, O, P.'], + ['Justin', 'Justin time for dinner.'], + ['Liza', 'Liza wrong to tell.'], + ['Luke', 'Luke and see who it is.'], + ['Mandy', "Mandy the lifeboats, we're sinking."], + ['Max', 'Max no difference - just open the door!'], + ['Nettie', 'Nettie as a fruitcake.'], + ['Olivia', 'Olivia me alone!'], + ['Oscar', 'Oscar stupid question, you get a stupid answer.'], + ['Patsy', 'Patsy dog on the head, he likes it.'], + ['Paul', "Paul hard, the door's stuck again."], + ['Thea', 'Thea later, alligator.'], + ['Tyrone', "Tyrone shoelaces, you're old enough."], + ['Stella', 'Stella no answer at the door.'], + ['Uriah', 'Keep Uriah on the ball.'], + ['Dwayne', "Dwayne the bathtub. I'm drowning."], + ['Dismay', "Dismay be a joke, but it didn't make me laugh."], + ['Ocelot', "Ocelot of questions, don't you?"], + ['Thermos', 'Thermos be a better knock knock joke than this.'], + ['Sultan', 'Sultan Pepper.'], + ['Vaughan', 'Vaughan day my prince will come.'], + ['Donald', 'Donald come baby, cradle and all.'], + ['Lettuce', "Lettuce in, won't you?"], + ['Ivor', 'Ivor sore hand from knocking on your door!'], + ['Isabel', 'Isabel broken, because I had to knock.'], + ['Heywood, Hugh, Harry', 'Heywood Hugh Harry up and open this door.'], + ['Juan', "Juan of this days you'll find out."], + ['Earl', 'Earl be glad to tell you if you open this door.'], + ['Abbot', 'Abbot time you opened this door!'], + ['Ferdie', 'Ferdie last time, open the door!'], + ['Don', 'Don mess around, just open the door.'], + ['Sis', 'Sis any way to treat a friend?'], + ['Isadore', 'Isadore open or locked?'], + ['Harry', 'Harry up and let me in!'], + ['Theodore', "Theodore wasn't open so I knocked-knocked."], + ['Ken', 'Ken I come in?'], + ['Boo', "There's no need to cry about it."], + ['You', 'You who! Is there anybody there?'], + ['Ice cream', "Ice cream if you don't let me in."], + ['Sarah', "Sarah 'nother way into this building?"], + ['Mikey', 'Mikey dropped down the drain.'], + ['Doris', 'Doris jammed again.'], + ['Yelp', 'Yelp me, the door is stuck.'], + ['Scold', 'Scold outside.'], + ['Diana', 'Diana third, can I have a drink please?'], + ['Doris', 'Doris slammed on my finger, open it quick!'], + ['Lettuce', 'Lettuce tell you some knock knock jokes.'], + ['Izzy', 'Izzy come, izzy go.'], + ['Omar', 'Omar goodness gracious - wrong door!'], + ['Says', "Says me, that's who!"], + ['Duck', "Just duck, they're throwing things at us."], + ['Tank', "You're welcome."], + ['Eyes', 'Eyes got loads more knock knock jokes for you.'], + ['Pizza', 'Pizza cake would be great right now.'], + ['Closure', 'Closure mouth when you eat.'], + ['Harriet', "Harriet all my lunch, I'm starving."], + ['Wooden', 'Wooden you like to know?'], + ['Punch', 'Not me, please.'], + ['Gorilla', 'Gorilla me a hamburger.'], + ['Jupiter', "Jupiter hurry, or you'll miss the trolley."], + ['Bertha', 'Happy Bertha to you!'], + ['Cows', 'Cows go "moo" not "who."'], + ['Tuna fish', "You can tune a piano, but you can't tuna fish."], + ['Consumption', 'Consumption be done about all these knock knock jokes?'], + ['Banana', 'Banana spilt so ice creamed.'], + ['X', 'X-tremely pleased to meet you.'], + ['Haydn', 'Haydn seek is fun to play.'], + ['Rhoda', 'Rhoda boat as fast as you can.'], + ['Quacker', "Quacker 'nother bad joke and I'm off!"], + ['Nana', 'Nana your business.'], + ['Ether', 'Ether bunny.'], + ['Little old lady', "My, you're good at yodelling!"], + ['Beets', 'Beets me, I forgot the joke.'], + ['Hal', 'Halloo to you too!'], + ['Sarah', 'Sarah doctor in the house?'], + ['Aileen', 'Aileen Dover and fell down.'], + ['Atomic', 'Atomic ache'], + ['Agatha', 'Agatha headache. Got an aspirin?'], + ['Stan', "Stan back, I'm going to sneeze."], + ['Hatch', 'Bless you.'], + ['Ida', "It's not Ida who, it's Idaho."], + ['Zippy', 'Mrs. Zippy.'], + ['Yukon', 'Yukon go away and come back another time.']] +ScientistPhase1Dialogue = ['Fellow Toons, this is the Silly Meter!', + "It is tracking Toontown's rising silly levels...", + 'Which are causing objects on the street to animate!', + 'And YOU can help push these levels higher!', + 'Battle Cogs to cause Silly Surges...', + 'Make Toontown sillier than ever...', + "And let's watch the world come alive!", + "Now I'll repeat what I said, but only once more."] +ScientistPhase2Dialogue = ['Good Gag work, Toons!', + "You're keeping those silly levels rising...", + 'And Toontown is getting sillier every day!', + 'Fire hydrants, trash cans, and mailboxes are springing to life...', + 'Making the world more animated than ever!', + "You know the Cogs aren't happy about this...", + 'But Toons sure are!'] +ScientistPhase3Dialogue = ['Gadzooks! The Silly Meter is even crazier than expected!', + 'Your Silly Surges are working wonders...', + 'And Toontown is getting more animated every day!', + 'Keep up the good Gag work...', + 'And lets see how silly we can make Toontown!', + "You know the Cogs aren't happy about what's going on...", + 'But Toons sure are!'] +ScientistPhase4Dialogue = ['YOU DID IT TOONS!', + 'You brought the streets of Toontown to life!', + 'You deserve a reward!', + 'Enter the code SILLYMETER in your Shticker Book...', + '...to get a Silly Meter T-Shirt!'] +ScientistPhase5Dialogue = ['Attention all Toons!', + 'The Cog invasions have been an unhappy event.', + 'As a result, silly levels have rapidly fallen...', + 'And no new objects are coming to life.', + 'But those that have are very thankful...', + 'And are showing their appreciation by helping in battle!', + 'We may hold off the Cogs yet, so keep up the fight!'] +ScientistPhase6Dialogue = ['Congratulations Toons!', + 'You all succesfully held off the Cog Invasions...', + 'With a little help from our newly animated friends...', + 'And brought Toontown back to its usual silly self!', + 'We hope to get the Silly Meter rising again soon...', + 'So in the meantime, keep up the Cog fight...', + 'And enjoy the silliest place ever, Toontown!'] +FriendsListPanelNewFriend = 'New Friend' +FriendsListPanelOnlineFriends = 'ONLINE TOON\nFRIENDS' +FriendsListPanelAllFriends = 'ALL TOON\nFRIENDS' +FriendsListPanelPets = 'NEARBY\nPETS' +FriendsListPanelTrueFriends = 'True Friends' +FriendInviterClickToon = 'Click on the toon you would like to make friends with.\n\n(You have %s friends)' +FriendInviterThatToon = 'That toon' +FriendInviterToonTooMany = 'You have too many toon friends to add another one now. You will have to remove some toon friends if you want to make friends with %s.' +FriendInviterToonAlready = '%s is already your toon friend.' +FriendInviterStopBeingToonFriends = 'Stop being toon friends' +FriendInviterEndFriendshipToon = 'Are you sure you want to stop being toon friends with %s?' +FriendInviterRemainToon = '\n(You will still be toon friends with %s)' +AvatarChoiceMakeAToon = 'Make A\nToon' +AvatarChoicePlayThisToon = 'Play\nThis Toon' +AvatarChoiceDelete = 'Delete' +AvatarChoiceDeleteConfirm = 'This will delete %s forever.' +AvatarChoiceNameRejected = 'Name\nRejected' +AvatarChoiceNameApproved = 'Name\nApproved!' +AvatarChoiceNameReview = 'Under\nReview' +AvatarChoiceNameYourToon = 'Name\nYour Toon!' +AvatarChoiceDeleteConfirmText = "Careful! This will delete %(name)s forever. If you are sure you want to do this, type your toon's name and click OK." +AvatarChoiceDeletePasswordTitle = 'Delete Toon?' +AvatarChoiceDeletePasswordOK = lOK +AvatarChoiceDeletePasswordCancel = lCancel +AvatarChoiceDeleteWrongConfirm = "You didn\'t type the right thing. To delete %(name)s, type your toon's name and click OK. Do not type the quotation marks. Click Cancel if you have changed your mind." +AvatarChooserPickAToon = 'Pick A Toon To Play' +AvatarChooserQuit = lQuit +DateOfBirthEntryMonths = ['Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec'] +PhotoPageTitle = 'Snapshots' +PhotoPageNoName = 'Unnamed' +PhotoPageUnknownName = 'Unknown' +PhotoPageAddName = 'Add Caption' +PhotoPageAddNamePanel = 'Add a Caption to this Snapshot:' +PhotoPageDelete = 'Are you sure you want to delete' +PhotoPageConfirm = 'Yep!' +PhotoPageCancel = lCancel +PhotoPageClose = lClose +PhotoPageDirectory = 'Open Folder' +PhotoPageTutorial = 'You haven\'t taken any snapshots yet! Press TAB to change your camera angle, and press F9 to take a snapshot.\n\n Once you\'ve made a snapshot, come here to manage and name them.' +BuildingPageTitle = 'Buildings\n(Coming Soon)' +InventoryPageTitle = 'Gags' +InventoryPageTrackFull = 'You have all the gags in the %s track.' +InventoryPagePluralPoints = 'You will get a new\n%(trackName)s gag when you\nget %(numPoints)s more %(trackName)s points.' +InventoryPageSinglePoint = 'You will get a new\n%(trackName)s gag when you\nget %(numPoints)s more %(trackName)s point.' +InventoryPageNoAccess = 'You do not have access to the %s track yet.' +NPCFriendPageTitle = 'SOS Toons' +PartyDateFormat = '%(mm)s %(dd)d, %(yyyy).4d' +PartyTimeFormat = '%d:%.2d %s' +PartyTimeFormatMeridiemAM = 'am' +PartyTimeFormatMeridiemPM = 'pm' +PartyCanStart = "It's Party Time, click Start Party in your Shticker Book Hosting page!" +PartyHasStartedAcceptedInvite = '%s party has started! Teleport to the host!' +PartyHasStartedNotAcceptedInvite = '%s party has started! You can still go to it by teleporting to the host.' +EventsPageName = 'Events' +EventsPageCalendarTabName = 'Calendar' +EventsPageCalendarTabParty = 'Party' +EventsPageToontownTimeIs = 'TOONTOWN TIME IS' +EventsPageConfirmCancel = 'If you cancel, you will get a %d%% refund. Are you sure you want to cancel your party?' +EventsPageCancelPartyResultOk = 'Your party was cancelled and you got %d Jellybeans back!' +EventsPageCancelPartyResultError = 'Sorry, your party was not cancelled.' +EventsPageCancelPartyAlreadyRefunded = 'Your party was never started. Check your mailbox for your refund!' +EventsPageTooLateToStart = 'Sorry, it is too late to start your party. You can cancel it and plan another one.' +EventsPagePublicPrivateChange = "Changing your party's privacy setting..." +EventsPagePublicPrivateNoGo = "Sorry, you can't change your party's privacy setting right now." +EventsPagePublicPrivateAlreadyStarted = "Sorry, your party has already started, so you can't change your party's privacy setting." +EventsPageHostTabName = 'Hosting' +EventsPageHostTabTitle = 'My Next Party' +EventsPageHostTabTitleNoParties = 'No Parties' +EventsPageHostTabDateTimeLabel = 'You are having a party on %s at %s Toontown Time.' +EventsPageHostingTabNoParty = 'Go to a playground\nParty Gate to plan\nyour own party!' +EventsPageHostTabPublicPrivateLabel = 'This party is:' +EventsPageHostTabToggleToPrivate = 'Private' +EventsPageHostTabToggleToPublic = 'Public' +EventsPageHostingTabGuestListTitle = 'Guests' +EventsPageHostingTabActivityListTitle = 'Activities' +EventsPageHostingTabDecorationsListTitle = 'Decorations' +EventsPageHostingTabPartiesListTitle = 'Hosts' +EventsPageHostTabCancelButton = 'Cancel Party' +EventsPageGoButton = 'Start\nParty!' +EventsPageGoBackButton = 'Party\nNow!' +EventsPageInviteGoButton = 'Go to\nParty!' +EventsPageUnknownToon = 'Unknown Toon' +EventsPageInvitedTabName = 'Invitations' +EventsPageInvitedTabTitle = 'Party Invitations' +EventsPageInvitedTabInvitationListTitle = 'Invitations' +EventsPageInvitedTabActivityListTitle = 'Activities' +EventsPageInvitedTabTime = '%s %s Toontown Time' +SelectedInvitationInformation = '%s is having a party on %s at %s Toontown Time.' +PartyPlannerNextButton = 'Continue' +PartyPlannerPreviousButton = 'Back' +PartyPlannerWelcomeTitle = 'Toontown Party Planner' +PartyPlannerInstructions = 'Hosting your own party is a lot of fun!\nStart planning with the arrows at the bottom!' +PartyPlannerDateTitle = 'Pick A Day For Your Party' +PartyPlannerTimeTitle = 'Pick A Time For Your Party' +PartyPlannerGuestTitle = 'Choose Your Guests' +PartyPlannerEditorTitle = 'Design Your Party\nPlace Activities and Decorations' +PartyPlannerConfirmTitle = 'Choose Invitations To Send' +PartyPlannerConfirmTitleNoFriends = 'Double Check Your Party' +PartyPlannerTimeToontown = 'Toontown' +PartyPlannerTimeTime = 'Time' +PartyPlannerTimeRecap = 'Party Date and Time' +PartyPlannerPartyNow = 'As Soon As Possible' +PartyPlannerTimeToontownTime = 'Toontown Time:' +PartyPlannerTimeLocalTime = 'Party Local Time : ' +PartyPlannerPublicPrivateLabel = 'This party will be:' +PartyPlannerPublicDescription = 'Any Toon\ncan come!' +PartyPlannerPrivateDescription = 'Only\nInvited Toons\ncan come!' +PartyPlannerPublic = 'Public' +PartyPlannerPrivate = 'Private' +PartyPlannerCheckAll = 'Check\nAll' +PartyPlannerUncheckAll = 'Uncheck\nAll' +PartyPlannerDateText = 'Date' +PartyPlannerTimeText = 'Time' +PartyPlannerTTTimeText = 'Toontown Time' +PartyPlannerEditorInstructionsIdle = 'Click on the Party Activity or Decoration you would like to purchase.' +PartyPlannerEditorInstructionsClickedElementActivity = 'Click Buy or Drag the Activity Icon onto the Party Grounds Map' +PartyPlannerEditorInstructionsClickedElementDecoration = 'Click Buy or Drag the Decoration onto the Party Grounds Map' +PartyPlannerEditorInstructionsDraggingActivity = 'Drag the Activity onto the Party Grounds Map.' +PartyPlannerEditorInstructionsDraggingDecoration = 'Drag the Activity onto the Party Grounds Map.' +PartyPlannerEditorInstructionsPartyGrounds = 'Click and Drag items to move them around the Party Grounds Map' +PartyPlannerEditorInstructionsTrash = 'Drag an Activity or Decoration here to remove it.' +PartyPlannerEditorInstructionsNoRoom = 'There is no room to place that activity.' +PartyPlannerEditorInstructionsRemoved = '%(removed)s removed since %(added)s was added.' +PartyPlannerBeans = 'beans' +PartyPlannerTotalCost = 'Total Cost:\n%d beans' +PartyPlannerSoldOut = 'SOLD OUT' +PartyPlannerBuy = 'BUY' +PartyPlannerPartyGrounds = 'PARTY GROUNDS MAP' +PartyPlannerOkWithGroundsLayout = 'Are you done moving your Party Activities and Decorations around the Party Grounds Map?' +PartyPlannerChooseFutureTime = 'Please choose a time in the future.' +PartyPlannerInviteButton = 'Send Invites' +PartyPlannerInviteButtonNoFriends = 'Plan Party' +PartyPlannerBirthdayTheme = 'Birthday' +PartyPlannerGenericMaleTheme = 'Star' +PartyPlannerGenericFemaleTheme = 'Flower' +PartyPlannerRacingTheme = 'Racing' +PartyPlannerValentoonsTheme = 'ValenToons' +PartyPlannerVictoryPartyTheme = 'Victory' +PartyPlannerWinterPartyTheme = 'Winter' +PartyPlannerGuestName = 'Guest Name' +PartyPlannerClosePlanner = 'Close Planner' +PartyPlannerConfirmationAllOkTitle = 'Congratulations!' +PartyPlannerConfirmationAllOkText = 'Your party has been created and your invitations sent out.\nThanks!' +PartyPlannerConfirmationAllOkTextNoFriends = 'Your party has been created!\nThanks!' +PartyPlannerConfirmationErrorTitle = 'Oops.' +PartyPlannerConfirmationValidationErrorText = 'Sorry, there seems to be a problem\nwith that party.\nPlease go back and try again.' +PartyPlannerConfirmationDatabaseErrorText = "Sorry, I couldn't record all your information.\nPlease try again later.\nDon't worry, no beans were lost." +PartyPlannerConfirmationTooManyText = 'Sorry, you are already hosting a party.\nIf you want to plan another party, please\ncancel your current party.' +PartyPlannerInvitationThemeWhatSentence = 'You are invited to my %s party! %s!' +PartyPlannerInvitationThemeWhatSentenceNoFriends = 'I am hosting a %s party! %s!' +PartyPlannerInvitationThemeWhatActivitiesBeginning = 'It will have ' +PartyPlannerInvitationWhoseSentence = '%s Party' +PartyPlannerInvitationTheme = 'Theme' +PartyPlannerInvitationWhenSentence = 'It will be on %s,\nat %s Toontown Time.\nHope you can make it!' +PartyPlannerInvitationWhenSentenceNoFriends = 'It will be on %s,\nat %s Toontown Time.\nToontastic!' +PartyPlannerGenericName = 'Party Planner' +PartyJukeboxOccupied = 'Someone else is using the jukebox. Try again later.' +PartyJukeboxNowPlaying = 'The song you chose is now playing on the jukebox!' +MusicEncntrGeneralBg = 'Encounter With Cogs' +MusicTcSzActivity = 'Toontorial Medley' +MusicTcSz = 'Strolling Along' +MusicCreateAToon = 'The New Toon in Town' +MusicTtsTheme = 'The Toontown Stride Theme' +MusicTtTheme = 'The Toontown Theme' +MusicMinigameRace = 'Slow and Steady' +MusicTcNbrhood = 'Toontown Central' +MusicMgDiving = 'Treasure Lullaby' +MusicMgCannonGame = 'Fire the Cannons!' +MusicMgTwodgame = 'Running Toon' +MusicMgCogthief = 'Catch That Cog!' +MusicMgTugOWar = 'Tug-of-War' +MusicMgVine = 'The Jungle Swing' +MusicMgIcegame = 'Slippery Situation' +MusicMgToontag = 'Minigame Medley' +MusicMMatchBg2 = 'Jazzy Minnie' +MusicMgTarget = "Soarin' Over Toontown" +MusicFfSafezone = 'The Funny Farm' +MusicDdSz = 'Waddling Way' +MusicMmNbrhood = "Minnie's Melodyland" +MusicGzPlaygolf = "Let's Play Golf!" +MusicGsSz = 'Goofy Speedway' +MusicOzSz = "Chip n' Dale's Acres" +MusicGsRaceCc = 'Downtown Driving' +MusicGsRaceSs = 'Ready, Set, Go!' +MusicGsRaceRr = 'Route 66' +MusicGzSz = 'The Putt-Putt Polka' +MusicMmSz = 'Dancing in the Streets' +MusicMmSzActivity = 'Here Comes Treble' +MusicDdNbrhood = "Donald's Dock" +MusicGsKartshop = 'Mr. Goofywrench' +MusicDdSzActivity = 'Sea Shanty' +MusicEncntrGeneralBgIndoor = 'Building Excitement' +MusicTtElevator = 'Going Up?' +MusicEncntrToonWinningIndoor = 'Toons Unite!' +MusicEncntrGeneralSuitWinningIndoor = 'Cog-tastrophe!' +MusicTbNbrhood = 'The Brrrgh' +MusicDlNbrhood = "Donald's Dreamland" +MusicDlSzActivity = 'Counting Sheep' +MusicDgSz = 'Waltz of the Flowers' +MusicDlSz = 'Sleepwalking' +MusicTbSzActivity = 'Snow Problem' +MusicTbSz = 'Shiver and Shimmy' +MusicDgNbrhood = "Daisy's Garden" +MusicEncntrHallOfFame = 'The Hall of Fame' +MusicEncntrSuitHqNbrhood = 'Dollars and Cents' +MusicChqFactBg = 'Cog Factory' +MusicCoghqFinale = 'Triumph of the Toons' +MusicEncntrToonWinning = 'Cashing In!' +MusicEncntrSuitWinning = 'Selling You Short' +MusicEncntrHeadSuitTheme = 'The Big Boss' +MusicLbJurybg = 'Court is in Session' +MusicLbCourtyard = 'Balancing Act' +MusicBossbotCeoV2 = 'Head Honcho' +MusicBossbotFactoryV1 = 'Cog Waltz' +MusicBossbotCeoV1 = 'Bossing You Around' +MusicPartyOriginalTheme = 'Party Time' +MusicPartyPolkaDance = 'Party Polka' +MusicPartySwingDance = 'Party Swing' +MusicPartyWaltzDance = 'Party Waltz' +MusicPartyGenericThemeJazzy = 'Party Jazz' +MusicPartyGenericTheme = 'Party Jingle' +JukeboxAddSong = 'Add\nSong' +JukeboxReplaceSong = 'Replace\nSong' +JukeboxQueueLabel = 'Playing Next:' +JukeboxSongsLabel = 'Pick a Song:' +JukeboxClose = 'Done' +JukeboxCurrentlyPlaying = 'Currently Playing' +JukeboxCurrentlyPlayingNothing = 'Jukebox is paused.' +JukeboxCurrentSongNothing = 'Add a song to the playlist!' +PartyOverWarningNoName = 'The party has ended! Thanks for coming!' +PartyOverWarningWithName = '%s party has ended! Thanks for coming!' +PartyCountdownClockText = 'Time\n\nLeft' +PartyTitleText = '%s Party' +PartyActivityConjunction = ', and' +PartyActivityNameDict = {0: {'generic': 'Jukebox', + 'invite': 'a Jukebox', + 'editor': 'Jukebox', + 'description': 'Listen to music with your own jukebox!'}, + 1: {'generic': 'Party Cannons', + 'invite': 'Party Cannons', + 'editor': 'Cannons', + 'description': 'Fire yourself out of the cannons and into fun!'}, + 2: {'generic': 'Trampoline', + 'invite': 'Trampoline', + 'editor': 'Trampoline', + 'description': 'Collect Jellybeans and bounce the highest!'}, + 3: {'generic': 'Party Catch', + 'invite': 'Party Catch', + 'editor': 'Party Catch', + 'description': 'Catch fruit to win beans! Dodge those anvils!'}, + 4: {'generic': 'Dance Floor\n10 moves', + 'invite': 'a 10 move Dance Floor', + 'editor': 'Dance Floor - 10', + 'description': 'Show off all 10 of your moves, toon style!'}, + 5: {'generic': 'Party Tug-of-War', + 'invite': 'Party Tug-of-War', + 'editor': 'Tug-of-War', + 'description': 'Up to 4 on 4 toon tugging craziness!'}, + 6: {'generic': 'Party Fireworks', + 'invite': 'Party Fireworks', + 'editor': 'Fireworks', + 'description': 'Launch your very own fireworks show!'}, + 7: {'generic': 'Party Clock', + 'invite': 'a Party Clock', + 'editor': 'Party Clock', + 'description': 'Counts down the time left in your party.'}, + 8: {'generic': 'Deluxe Jukebox', + 'invite': 'a deluxe jukebox', + 'editor': 'Deluxe Jukebox', + 'description': 'Your own deluxe jukebox with double the tunes for double the deal!'}, + 9: {'generic': 'Dance Floor\n20 moves', + 'invite': 'a 20 move Dance Floor', + 'editor': 'Dance Floor - 20', + 'description': 'Show off all 20 of your moves, toon style!'}, + 10: {'generic': 'Cog-O-War', + 'invite': 'Cog-O-War', + 'editor': 'Cog-O-War', + 'description': 'The team vs. team game of Cog splatting!'}, + 11: {'generic': 'Cog Trampoline', + 'invite': 'Cog Trampoline', + 'editor': 'Cog Trampoline', + 'description': "Jump on a Cog's face!"}, + 12: {'generic': 'Present Catch', + 'invite': 'Present Catch', + 'editor': 'Present Catch', + 'description': 'Catch presents to win beans! Dodge those anvils!'}, + 13: {'generic': 'Holiday Trampoline', + 'invite': 'Holiday Trampoline', + 'editor': 'Holiday Trampoline', + 'description': 'Jump if you love Winter Holidays!'}, + 14: {'generic': 'Holiday Cog-O-War', + 'invite': 'Holiday Cog-O-War', + 'editor': 'Holiday Cog-O-War', + 'description': 'The team vs. team game of Cog splattering!'}, + 15: {'generic': 'Dance Floor\n10 moves', + 'invite': 'a 10 move ValenToons Dance Floor', + 'editor': 'Dance Floor - 10', + 'description': 'Get your ValenToon Groove On!'}, + 16: {'generic': 'Dance Floor\n20 moves', + 'invite': 'a 20 move ValenToons Dance Floor', + 'editor': 'Dance Floor - 20', + 'description': 'Get your ValenToon Groove On!'}, + 17: {'generic': 'Jukebox\n20 songs', + 'invite': 'a 20 song Valentoons Jukebox', + 'editor': 'Jukebox - 20', + 'description': 'Nothing sets the mood like music!'}, + 18: {'generic': 'Jukebox\n40 songs', + 'invite': 'a 40 song Valentoons jukebox', + 'editor': 'Jukebox - 40', + 'description': 'Nothing sets the mood like music!'}, + 19: {'generic': 'Trampoline', + 'invite': 'ValenToons Trampoline', + 'editor': 'Trampoline', + 'description': "Jump to your heart's content!"}} +PartyDecorationNameDict = {0: {'editor': 'Balloon Anvil', + 'description': 'Try to keep the fun from floating away!'}, + 1: {'editor': 'Party Stage', + 'description': 'Balloons, stars, what else could you want?'}, + 2: {'editor': 'Party Bow', + 'description': 'Wrap up the fun!'}, + 3: {'editor': 'Cake', + 'description': 'Delicious.'}, + 4: {'editor': 'Party Castle', + 'description': "A Toon's home is his castle."}, + 5: {'editor': 'Gift Pile', + 'description': 'Gifts for every Toon!'}, + 6: {'editor': 'Streamer Horn', + 'description': 'This horn is a hoot! Streamers!'}, + 7: {'editor': 'Party Gate', + 'description': 'Multi-colored and crazy!'}, + 8: {'editor': 'Noise Makers', + 'description': 'Tweeeeet!'}, + 9: {'editor': 'Pinwheel', + 'description': 'Colorful twirling for everyone!'}, + 10: {'editor': 'Gag Globe', + 'description': 'Gag and star globe designed by Olivea'}, + 11: {'editor': 'Bean Banner', + 'description': 'A Jellybean banner designed by Cassidy'}, + 12: {'editor': 'Gag Cake', + 'description': 'A Topsy Turvy gag cake designed by Felicia'}, + 13: {'editor': "Cupid's Heart", + 'description': 'Ready...Aim...\nValenToons!'}, + 14: {'editor': 'Candy Hearts\n Banner', + 'description': "Who doesn't love candy hearts?"}, + 15: {'editor': 'Flying Heart', + 'description': 'This heart is getting carried away!'}, + 16: {'editor': 'Victory Bandstand', + 'description': 'All our new friends are ready to dance!'}, + 17: {'editor': 'Victory Banner', + 'description': 'Not just a normal banner!'}, + 18: {'editor': 'Confetti Cannons', + 'description': 'BOOM! Confetti! Fun!'}, + 19: {'editor': 'Cog & Doodle', + 'description': "Ouch! That's gotta hurt."}, + 20: {'editor': 'Cog Flappy Man', + 'description': 'A Cog full of hot air, what a shock!'}, + 21: {'editor': 'Cog Ice Cream', + 'description': 'A Cog looking his best.'}, + 22: {'editor': 'CogCicle', + 'description': 'A Cog looking his holiday best.'}, + 23: {'editor': 'Holiday Bandstand', + 'description': 'Everyone loves a Holiday Party!'}, + 24: {'editor': 'Chilly Cog', + 'description': "Ouch! That's gotta hurt."}, + 25: {'editor': 'Snowman', + 'description': "So cool, he's hot!"}, + 26: {'editor': 'SnowDoodle', + 'description': 'His only trick is being cold!'}, + 27: {'editor': 'ValenToons Anvil', + 'description': "We've got your heart on a string!"}} +ActivityLabel = 'Cost - Activity Name' +PartyDoYouWantToPlan = 'Would you like to plan a new party right now?' +PartyPlannerOnYourWay = 'Have fun planning your party!' +PartyPlannerMaybeNextTime = 'Maybe next time. Have a good day!' +PartyPlannerHostingTooMany = 'You can only host one party at a time, sorry.' +PartyPlannerNpcMinCost = 'It costs a minimum of %d Jellybeans to plan a party.' +PartyHatPublicPartyChoose = 'Do you want to go to the 1st available public party?' +PartyGateTitle = 'Public Parties' +PartyGateGoToParty = 'Go to\nParty!' +PartyGatePartiesListTitle = 'Hosts' +PartyGatesPartiesListToons = 'Toons' +PartyGatesPartiesListActivities = 'Activities' +PartyGatesPartiesListMinLeft = 'Minutes Left' +PartyGateLeftSign = 'Come On In!' +PartyGateRightSign = 'Public Parties Here!' +PartyGatePartyUnavailable = 'Sorry. That party is no longer available.' +PartyGatePartyFull = 'Sorry. That party is full.' +PartyGateInstructions = 'Click on a host, then click on "Go to Party"' +PartyActivityWaitingForOtherPlayers = 'Waiting for other toons to join the party game...' +PartyActivityPleaseWait = 'Please wait...' +DefaultPartyActivityTitle = 'Party Game Title' +DefaultPartyActivityInstructions = 'PartyGame Instructions' +PartyOnlyHostLeverPull = 'Only the host can start this activity. Sorry.' +PartyActivityDefaultJoinDeny = 'You cannot join this activity right now. Sorry.' +PartyActivityDefaultExitDeny = 'You cannot leave this activity right now. Sorry.' +PartyJellybeanRewardOK = 'OK' +PartyCatchActivityTitle = 'Party Catch Activity' +PartyCatchActivityInstructions = "Catch as many pieces of fruit as you can. Try not to 'catch' any %(badThing)s!" +PartyCatchActivityFinishPerfect = 'PERFECT GAME!' +PartyCatchActivityFinish = 'Good Game!' +PartyCatchActivityExit = 'EXIT' +PartyCatchActivityApples = 'apples' +PartyCatchActivityOranges = 'oranges' +PartyCatchActivityPears = 'pears' +PartyCatchActivityCoconuts = 'coconuts' +PartyCatchActivityWatermelons = 'watermelons' +PartyCatchActivityPineapples = 'pineapples' +PartyCatchActivityAnvils = 'anvils' +PartyCatchStarted = 'The game has started. Go play it.' +PartyCatchCannotStart = 'The game could not start right now.' +PartyCatchRewardMessage = 'Pieces of fruit caught: %s\n\nJellybeans earned: %d' +WinterPartyCatchActivityInstructions = "Catch as many presents as you can. Try not to 'catch' any %(badThing)s!" +WinterPartyCatchRewardMessage = 'Presents caught: %s\n\nJellybeans earned: %s' +PartyDanceActivityTitle = 'Party Dance Floor' +PartyDanceActivityInstructions = 'Combine 3 or more ARROW KEY patterns to do dance moves! There are 10 dance moves available. Can you find them all?' +PartyDanceActivity20Title = 'Party Dance Floor' +PartyDanceActivity20Instructions = 'Combine 3 or more ARROW KEY patterns to do dance moves! There are 20 dance moves available. Can you find them all?' +DanceAnimRight = 'Right' +DanceAnimReelNeutral = 'The Fishertoon' +DanceAnimConked = 'The Headbob' +DanceAnimHappyDance = 'The Happy Dance' +DanceAnimConfused = 'Very Dizzy' +DanceAnimWalk = 'Walking on the Moon' +DanceAnimJump = 'The Jump!' +DanceAnimFirehose = 'The Firetoon' +DanceAnimShrug = 'Who Knows?' +DanceAnimSlipForward = 'The Fall' +DanceAnimSadWalk = 'Tired' +DanceAnimWave = 'Hello Goodbye' +DanceAnimStruggle = 'The Shuffle Hop' +DanceAnimRunningJump = 'The Running Toon' +DanceAnimSlipBackward = 'The Backfall' +DanceAnimDown = 'Down' +DanceAnimUp = 'Up' +DanceAnimGoodPutt = 'The Putt' +DanceAnimVictory = 'The Victory Dance' +DanceAnimPush = 'The Mimetoon' +DanceAnimAngry = "Rock n' Roll" +DanceAnimLeft = 'Left' +PartyCannonActivityTitle = 'Party Cannons' +PartyCannonActivityInstructions = 'Hit the clouds to change their color and bounce in the air! While IN THE AIR, you can USE THE ARROW KEYS to GLIDE.' +PartyCannonResults = 'You collected %d jelly beans!\n\nNumber of Clouds Hit: %d' +FireworksActivityInstructions = 'Look up using the "Page Up" key to see better.' +FireworksActivityBeginning = 'Party fireworks are about to start! Enjoy the show!' +FireworksActivityEnding = 'Hope you enjoyed the show!' +PartyFireworksAlreadyActive = 'The fireworks show has already started.' +PartyFireworksAlreadyDone = 'The fireworks show is over.' +PartyTrampolineJellyBeanTitle = 'Jelly Beans Trampoline' +PartyTrampolineTricksTitle = 'Tricks Trampoline' +PartyTrampolineActivityInstructions = 'Use the Control key to jump.\n\nJump when your Toon is at its lowest point on the trampoline to jump higher.' +PartyTrampolineActivityOccupied = 'Trampoline in use.' +PartyTrampolineQuitEarlyButton = 'Hop Off' +PartyTrampolineBeanResults = 'You collected %d jelly beans.' +PartyTrampolineBonusBeanResults = 'You collected %d jelly beans, plus %d more for getting the Big Bean. ' +PartyTrampolineTopHeightResults = 'Your top height was %d ft.' +PartyTrampolineTimesUp = "Time's Up" +PartyTrampolineReady = 'Ready...' +PartyTrampolineGo = 'Go!' +PartyTrampolineBestHeight = 'Best Height So Far:\n%s\n%d ft' +PartyTrampolineNoHeightYet = 'How high\ncan you jump?' +PartyTrampolineGetHeight = '%d ft' +PartyTeamActivityForMorePlural = 's' +PartyTeamActivityForMore = 'Waiting for %d toon%s\non each side...' +PartyTeamActivityForMoreWithBalance = 'Waiting for %d more toon%s...' +PartyTeamActivityWaitingForOtherPlayers = 'Waiting for other toons...' +PartyTeamActivityWaitingToStart = 'Starting in...' +PartyTeamActivityExitButton = 'Hop Off' +PartyTeamActivitySwitchTeamsButton = 'Switch\nTeams' +PartyTeamActivityWins = '%s team wins!' +PartyTeamActivityLocalAvatarTeamWins = 'Your team won!' +PartyTeamActivityGameTie = "It's a tie!" +PartyTeamActivityJoinDenied = "Sorry, you can't join %s at this time." +PartyTeamActivityExitDenied = 'Sorry, you are unable to leave %s at this time.' +PartyTeamActivitySwitchDenied = "Sorry, you cant's switch teams at this time." +PartyTeamActivityTeamFull = 'Sorry, that team is already full!' +PartyTeamActivityRewardMessage = 'You got %d Jellybeans. Good job!' +PartyCogTeams = ('Blue', 'Orange') +PartyCogRewardMessage = 'Your Score: %d\n' +PartyCogRewardBonus = '\nYou got %d additional Jellybean%s because your team won!' +PartyCogJellybeanPlural = 's' +PartyCogSignNote = 'HI-SCORE\n%s\n%d' +PartyCogTitle = 'Cog-O-War' +PartyCogInstructions = 'Throw pies at cogs to push them away from your team. ' + "When time's up, the team with most cogs on the other side wins!" + '\n\nThrow with the CONTROL KEY. Move with the ARROW KEYS.' +PartyCogDistance = '%d ft' +PartyCogTimeUp = "Time's up!" +PartyCogGuiScoreLabel = 'SCORE' +PartyCogGuiPowerLabel = 'POWER' +PartyCogGuiSpamWarning = 'Hold CONTROL for more power!' +PartyCogBalanceBar = 'BALANCE' +PartyTugOfWarReady = 'Ready...' +PartyTugOfWarGo = 'GO!' +PartyTugOfWarGameEnd = 'Good game!' +PartyTugOfWarTitle = 'Party Tug-of-War' +CalendarShowAll = 'Show All' +CalendarShowOnlyHolidays = 'Show Only Holidays' +CalendarShowOnlyParties = 'Show Only Parties' +CalendarEndsAt = 'Ends on' +CalendarStartedOn = 'Started on' +CalendarEndOf = 'End of' +CalendarPartyGetReady = 'Get ready!' +CalendarPartyGo = 'Go party!' +CalendarPartyFinished = "It's over..." +CalendarPartyCancelled = 'Cancelled.' +CalendarPartyNeverStarted = 'Never Started.' +NPCFriendPanelRemaining = '%d Remaining' +MapPageTitle = 'Map' +MapPageBackToPlayground = 'Back to Playground' +MapPageBackToCogHQ = 'Back to Cog Headquarters' +MapPageGoHome = 'Go Home' +MapPageYouAreHere = 'You are in: %s\n%s' +MapPageYouAreAtHome = 'You are at\nyour estate' +MapPageYouAreAtSomeonesHome = 'You are at %s estate' +MapPageGoTo = 'Go To\n%s' +OptionsPageTitle = 'Options' +ExtraOptionsPageTitle = 'Extra' +OptionsTabTitle = 'Options\n& Codes' +OptionsPageExitToontown = 'Exit Toontown' +OptionsPageMusic = 'Music Volume:' +OptionsPageSFX = 'SFX Volume:' +OptionsPageSFXOnLabel = 'Sound Effects are on.' +OptionsPageSFXOffLabel = 'Sound Effects are off.' +OptionsPageToonChatSoundsOnLabel = ' Type Chat Sounds are on.' +OptionsPageToonChatSoundsOffLabel = ' Type Chat Sounds are off.' +OptionsPageFriendsEnabledLabel = 'Accepting new friend requests.' +OptionsPageFriendsDisabledLabel = 'Not accepting friend requests.' +OptionsPageWhisperEnabledLabel = 'Allowing whispers from anyone.' +OptionsPageWhisperDisabledLabel = 'Allowing whispers from friends only.' +OptionsPageSpeedChatStyleLabel = 'SpeedChat Color' +OptionsPageDisplayWindowed = 'windowed' +OptionsPageSelect = 'Select' +OptionsPageToggleOn = 'Turn On' +OptionsPageToggleOff = 'Turn Off' +OptionsPageChange = 'Change' +OptionsPageDisplaySettings = 'Display: %(screensize)s, %(api)s' +OptionsPageDisplaySettingsNoApi = 'Display: %(screensize)s' +OptionsPageExitConfirm = 'Exit Toontown?' +DisplaySettingsTitle = 'Display Settings' +DisplaySettingsIntro = 'The following settings are used to configure the way Toontown is displayed on your computer. It is usually unnecessary to adjust these unless you are experiencing a problem.' +DisplaySettingsIntroSimple = 'You may adjust the screen resolution to a higher value to improve the clarity of text and graphics in Toontown, but depending on your graphics card, some higher values may make the game run less smoothly or may not work at all.' +DisplaySettingsApi = 'Graphics API:' +DisplaySettingsResolution = 'Resolution:' +DisplaySettingsWindowed = 'In a window' +DisplaySettingsFullscreen = 'Full screen' +DisplaySettingsApply = 'Apply' +DisplaySettingsCancel = lCancel +DisplaySettingsApplyWarning = 'When you press OK, the display settings will change. If the new configuration does not display properly on your computer, the display will automatically return to its original configuration after %s seconds.' +DisplaySettingsAccept = 'Press OK to keep the new settings, or Cancel to revert. If you do not press anything, the settings will automatically revert back in %s seconds.' +DisplaySettingsRevertUser = 'Your previous display settings have been restored.' +DisplaySettingsRevertFailed = 'The selected display settings do not work on your computer. Your previous display settings have been restored.' +OptionsPageCodesTab = 'Enter Code' +CdrPageTitle = 'Enter a Code' +CdrInstructions = 'Enter your code to receive a special item in your mailbox.' +CdrResultSuccess = 'Congratulations! Check your mailbox to claim your item!' +CdrResultInvalidCode = "You've entered an invalid code. Please check the code and try again." +CdrResultExpiredCode = "We're sorry. This code has expired." +CdrResultMailboxFull = 'Your mailbox is full. Please remove an item, then enter your code again.' +CdrResultAlreadyRedeemed = "You've already redeemed this item!" +CdrResultNotReady = "This code can't be used yet!" +CdrResultNotEligible = 'You are not eligible to use this code!' +TrackPageTitle = 'Gag Track Training' +TrackPageShortTitle = 'Gag Training' +TrackPageSubtitle = 'Complete ToonTasks to learn how to use new gags!' +TrackPageTraining = 'You are training to use %s gags.\nWhen you complete all 16 tasks you\nwill be able to use %s gags in battle.' +TrackPageClear = 'You are not training any Gag Tracks now.' +TrackPageFilmTitle = '%s\nTraining\nFilm' +TrackPageDone = 'FIN' +QuestPageToonTasks = 'ToonTasks' +QuestPageChoose = 'Choose' +QuestPageLocked = 'Locked' +QuestPageDestination = '%s\n%s\n%s' +QuestPageNameAndDestination = '%s\n%s\n%s\n%s' +QuestPosterHQOfficer = lHQOfficerM +QuestPosterHQBuildingName = lToonHQ +QuestPosterHQStreetName = 'Any Street' +QuestPosterHQLocationName = 'Any Neighborhood' +QuestPosterTailor = 'Tailor' +QuestPosterTailorBuildingName = 'Clothing Store' +QuestPosterTailorStreetName = 'Any Playground' +QuestPosterTailorLocationName = 'Any Neighborhood' +QuestPosterPlayground = 'In the playground' +QuestPosterAtHome = 'At your home' +QuestPosterInHome = 'In your home' +QuestPosterOnPhone = 'On your phone' +QuestPosterEstate = 'At your estate' +QuestPosterAnywhere = 'Anywhere' +QuestPosterAuxTo = 'to:' +QuestPosterAuxFrom = 'from:' +QuestPosterAuxFor = 'for:' +QuestPosterAuxOr = 'or:' +QuestPosterAuxReturnTo = 'Return to:' +QuestPosterLocationIn = ' in ' +QuestPosterLocationOn = ' in ' +QuestPosterFun = 'Just for fun!' +QuestPosterFishing = 'GO FISHING' +QuestPosterComplete = 'COMPLETE' +QuestPosterConfirmDelete = 'Are you sure you want to delete this ToonTask?' +QuestPosterDeleteBtn = 'Delete' +QuestPosterDialogYes = 'Delete' +QuestPosterDialogNo = 'Cancel' +ShardPageTitle = 'Districts' +ShardPageHelpIntro = 'Each District is a copy of the Toontown world.' +ShardPageHelpWhere = ' You are currently in the "%s" District.' +ShardPageHelpMove = ' To move to a new District, click on its name.' +ShardPagePopulationTotal = 'Total Toontown Population:\n%d' +ShardPageScrollTitle = 'Name Population' +ShardPageLow = 'Quiet' +ShardPageMed = 'Ideal' +ShardPageHigh = 'Full' +ShardPageChoiceReject = 'Sorry, that district is full. Please try another one.' +SuitPageTitle = 'Cog Gallery' +SuitPageMystery = DialogQuestion + DialogQuestion + DialogQuestion +SuitPageQuota = '%s of %s' +SuitPageCogRadar = '%s present' +SuitPageBuildingRadarS = '%s building' +SuitPageBuildingRadarP = '%s buildings' +DisguisePageTitle = Cog + ' Disguise' +DisguisePageMeritAlert = 'Ready for\npromotion!' +DisguisePageCogLevel = 'Level %s' +DisguisePageMeritFull = 'Full' +FishPageTitle = 'Fishing' +FishPageTitleTank = 'Fish Bucket' +FishPageTitleCollection = 'Fish Album' +FishPageTitleTrophy = 'Fishing Trophies' +FishPageWeightStr = 'Weight: ' +FishPageWeightLargeS = '%d lb. ' +FishPageWeightLargeP = '%d lbs. ' +FishPageWeightSmallS = '%d oz.' +FishPageWeightSmallP = '%d oz.' +FishPageWeightConversion = 16 +FishPageValueS = 'Value: %d Jellybean' +FishPageValueP = 'Value: %d Jellybeans' +FishPageCollectedTotal = 'Fish Species Collected: %d of %d' +FishPageRodInfo = '%s Rod\n%d - %d Pounds' +FishPageTankTab = 'Bucket' +FishPageCollectionTab = 'Album' +FishPageTrophyTab = 'Trophies' +FishPickerTotalValue = 'Bucket: %s / %s\nValue: %d Jellybeans' +UnknownFish = '???' +FishingRod = '%s Rod' +FishTank = '%s Fishing Bucket' +FishingRodNameDict = {0: 'Twig', + 1: 'Bamboo', + 2: 'Hardwood', + 3: 'Steel', + 4: 'Gold'} +FishTankNameDict = {40: 'Medium', + 60: 'Big', + 80: 'Large', + 100: 'Ultra'} +FishTrophyNameDict = {0: 'Guppy', + 1: 'Minnow', + 2: 'Fish', + 3: 'Flying Fish', + 4: 'Shark', + 5: 'Swordfish', + 6: 'Killer Whale'} +GardenPageTitle = 'Gardening' +GardenPageTitleBasket = 'Flower Basket' +GardenPageTitleCollection = 'Flower Album' +GardenPageTitleTrophy = 'Gardening Trophies' +GardenPageTitleSpecials = 'Gardening Specials' +GardenPageBasketTab = 'Basket' +GardenPageCollectionTab = 'Album' +GardenPageTrophyTab = 'Trophies' +GardenPageSpecialsTab = 'Specials' +GardenPageCollectedTotal = 'Flower Varieties Collected: %d of %d' +GardenPageValueS = 'Value: %d Jellybean' +GardenPageValueP = 'Value: %d Jellybeans' +FlowerPickerTotalValue = 'Basket: %s / %s\nValue: %d Jellybeans' +GardenPageShovelInfo = '%s Shovel: %d / %d\n' +GardenPageWateringCanInfo = '%s Watering Can: %d / %d' +FlowerPageWeightConversion = 1 +FlowerPageWeightLargeP = 'Large P' +FlowerPageWeightLargeS = 'LargeS ' +FlowerPageWeightSmallP = 'SmallP ' +FlowerPageWeightSmallS = 'SmallS ' +FlowerPageWeightStr = 'Weight: %s' +KartPageTitle = 'Karts' +KartPageTitleCustomize = 'Kart Customizer' +KartPageTitleRecords = 'Personal Best Records' +KartPageTitleTrophy = 'Racing Trophies' +KartPageCustomizeTab = 'Customize' +KartPageRecordsTab = 'Records' +KartPageTrophyTab = 'Trophy' +KartPageTrophyDetail = 'Trophy %s : %s' +KartPageTickets = 'Tickets : ' +KartPageConfirmDelete = 'Delete Accessory?' +KartShtikerDelete = 'Delete' +KartShtikerSelect = 'Select a Category' +KartShtikerNoAccessories = 'No Accessories Owned' +KartShtikerBodyColors = 'Kart Colors' +KartShtikerAccColors = 'Accessory Colors' +KartShtikerEngineBlocks = 'Hood Accessories' +KartShtikerSpoilers = 'Trunk Accessories' +KartShtikerFrontWheelWells = 'Front Wheel Accessories' +KartShtikerBackWheelWells = 'Back Wheel Accessories' +KartShtikerRims = 'Rim Accessories' +KartShtikerDecals = 'Decal Accessories' +KartShtikerBodyColor = 'Kart Color' +KartShtikerAccColor = 'Accessory Color' +KartShtikerEngineBlock = 'Hood' +KartShtikerSpoiler = 'Trunk' +KartShtikerFrontWheelWell = 'Front Wheel' +KartShtikerBackWheelWell = 'Back Wheel' +KartShtikerRim = 'Rim' +KartShtikerDecal = 'Decal' +KartShtikerDefault = 'Default %s' +KartShtikerNo = 'No %s Accessory' +QuestChoiceGuiCancel = lCancel +TrackChoiceGuiChoose = 'Choose' +TrackChoiceGuiCancel = lCancel +TrackChoiceGuiHEAL = 'Toonup lets you heal other Toons in battle.' +TrackChoiceGuiTRAP = 'Traps are powerful gags that must be used with Lure.' +TrackChoiceGuiLURE = 'Use Lure to stun Cogs or draw them into traps.' +TrackChoiceGuiSOUND = 'Sound gags affect all Cogs, but are not very powerful.' +TrackChoiceGuiDROP = 'Drop gags do lots of damage, but are not very accurate.' +EmotePageTitle = 'Expressions / Emotions' +EmotePageDance = 'You have built the following dance sequence:' +EmoteJump = 'Jump' +EmoteDance = 'Dance' +EmoteHappy = 'Happy' +EmoteSad = 'Sad' +EmoteAnnoyed = 'Annoyed' +EmoteSleep = 'Sleepy' +SuitBaseNameWithLevel = '%(name)s\n%(dept)s\nLevel %(level)s' +HealthForceAcknowledgeMessage = 'You cannot leave the playground until your Laff meter is smiling!' +InventoryTotalGags = 'Total gags\n%d / %d' +InventoryPinkSlips = '%s Pink Slips' +InventoryPinkSlip = '1 Pink Slip' +InventoryCrateKeys = '%s Crate Keys' +InventoryCrateKey = '1 Crate Key' +InventoryDelete = 'DELETE' +InventoryDone = 'DONE' +InventoryDeleteHelp = 'Click on a gag to DELETE it.' +InventorySkillCredit = 'Skill credit: %s' +InventorySkillCreditNone = 'Skill credit: None' +InventoryDetailAmount = '%(numItems)s / %(maxItems)s' +InventoryDetailData = 'Accuracy: %(accuracy)s\n%(damageString)s: %(damage)d%(bonus)s\n%(singleOrGroup)s' +InventoryTrackExp = '%(curExp)s / %(nextExp)s' +InventoryUberTrackExp = '%(nextExp)s to Go!' +InventoryAffectsOneCog = 'Affects: One ' + Cog +InventoryAffectsOneToon = 'Affects: One Toon' +InventoryAffectsAllToons = 'Affects: All Toons' +InventoryAffectsAllCogs = 'Affects: All ' + Cogs +InventoryHealString = 'Toon-up' +InventoryDamageString = 'Damage' +InventoryLureString = 'Rounds effective' +InventoryBattleMenu = 'BATTLE MENU' +InventoryRun = 'RUN' +InventorySOS = 'SOS' +InventoryPass = 'PASS' +InventoryFire = 'FIRE' +InventoryClickToAttack = 'Click a\ngag to\nattack' +InventoryDamageBonus = '(+%d)' +NPCForceAcknowledgeMessage = "You must ride the trolley before leaving.\n\n\n\n\n\n\n\n\nYou can find the trolley next to Goofy's Gag Shop." +NPCForceAcknowledgeMessage2 = 'You must return to Toon Headquarters before leaving.\n\n\n\n\n\n\n\n\n\nToon Headquarters is located near the center of the playground.' +NPCForceAcknowledgeMessage3 = "Remember to ride the trolley.\n\n\n\n\n\n\n\nYou can find the trolley next to Goofy's Gag Shop." +NPCForceAcknowledgeMessage4 = 'Congratulations! You found and rode the trolley!\n\n\n\n\n\n\n\n\n\nNow report back to Toon Headquarters.' +NPCForceAcknowledgeMessage5 = "Don't forget your ToonTask!\n\n\n\n\n\n\n\n\n\n\nYou can find Cogs to defeat on the other side of tunnels like this." +NPCForceAcknowledgeMessage6 = 'Great job defeating those Cogs!\n\n\n\n\n\n\n\n\nHead back to Toon Headquarters as soon as possible.' +NPCForceAcknowledgeMessage7 = "Don't forget to make a friend!\n\n\n\n\n\n\nClick on another toon and use the New Friend button." +NPCForceAcknowledgeMessage8 = 'Great! You made a new friend!\n\n\n\n\n\n\n\n\nYou should go back at Toon Headquarters now.' +NPCForceAcknowledgeMessage9 = 'Good job using the phone!\n\n\n\n\n\n\n\n\nReturn to Toon Headquarters to claim your reward.' +ToonSleepString = '. . . ZZZ . . .' +MovieTutorialReward1 = 'You received %s %s point! When you get 10, you will get a new gag!' +MovieTutorialReward3 = 'Good job! You completed your first ToonTask!' +MovieTutorialReward4 = 'Go to Toon Headquarters for your reward!' +MovieTutorialReward5 = 'Have fun!' +BattleGlobalTracks = ['toon-up', + 'trap', + 'lure', + 'sound', + 'throw', + 'squirt', + 'drop'] +BattleGlobalNPCTracks = ['restock', 'toons hit', 'cogs miss'] +BattleGlobalAvPropStrings = (('Feather', + 'Megaphone', + 'Lipstick', + 'Bamboo Cane', + 'Pixie Dust', + 'Juggling Balls', + 'High Dive'), + ('Banana Peel', + 'Rake', + 'Marbles', + 'Quicksand', + 'Trapdoor', + 'TNT', + 'Railroad'), + ('$1 bill', + 'Small Magnet', + '$5 bill', + 'Big Magnet', + '$10 bill', + 'Hypno-goggles', + 'Presentation'), + ('Bike Horn', + 'Whistle', + 'Bugle', + 'Aoogah', + 'Elephant Trunk', + 'Foghorn', + 'Opera Singer'), + ('Cupcake', + 'Fruit Pie Slice', + 'Cream Pie Slice', + 'Whole Fruit Pie', + 'Whole Cream Pie', + 'Birthday Cake', + 'Wedding Cake'), + ('Squirting Flower', + 'Glass of Water', + 'Squirt Gun', + 'Seltzer Bottle', + 'Fire Hose', + 'Storm Cloud', + 'Geyser'), + ('Flower Pot', + 'Sandbag', + 'Anvil', + 'Big Weight', + 'Safe', + 'Grand Piano', + 'Toontanic')) +BattleGlobalAvPropStringsSingular = (('a Feather', + 'a Megaphone', + 'a Lipstick', + 'a Bamboo Cane', + 'a Pixie Dust', + 'a set of Juggling Balls', + 'a High Dive'), + ('a Banana Peel', + 'a Rake', + 'a set of Marbles', + 'a patch of Quicksand', + 'a Trapdoor', + 'a TNT', + 'a Railroad'), + ('a $1 bill', + 'a Small Magnet', + 'a $5 bill', + 'a Big Magnet', + 'a $10 bill', + 'a pair of Hypno-goggles', + 'a Presentation'), + ('a Bike Horn', + 'a Whistle', + 'a Bugle', + 'an Aoogah', + 'an Elephant Trunk', + 'a Foghorn', + 'an Opera Singer'), + ('a Cupcake', + 'a Fruit Pie Slice', + 'a Cream Pie Slice', + 'a Whole Fruit Pie', + 'a Whole Cream Pie', + 'a Birthday Cake', + 'a Wedding Cake'), + ('a Squirting Flower', + 'a Glass of Water', + 'a Squirt Gun', + 'a Seltzer Bottle', + 'a Fire Hose', + 'a Storm Cloud', + 'a Geyser'), + ('a Flower Pot', + 'a Sandbag', + 'an Anvil', + 'a Big Weight', + 'a Safe', + 'a Grand Piano', + 'the Toontanic')) +BattleGlobalAvPropStringsPlural = (('Feathers', + 'Megaphones', + 'Lipsticks', + 'Bamboo Canes', + 'Pixie Dusts', + 'sets of Juggling Balls', + 'High Dives'), + ('Banana Peels', + 'Rakes', + 'sets of Marbles', + 'patches of Quicksand', + 'Trapdoors', + 'TNTs', + 'Railroads'), + ('$1 bills', + 'Small Magnets', + '$5 bills', + 'Big Magnets', + '$10 bills', + 'pairs of Hypno-goggles', + 'Presentations'), + ('Bike Horns', + 'Whistles', + 'Bugles', + 'Aoogahs', + 'Elephant Trunks', + 'Foghorns', + 'Opera Singers'), + ('Cupcakes', + 'Fruit Pie Slices', + 'Cream Pie Slices', + 'Whole Fruit Pies', + 'Whole Cream Pies', + 'Birthday Cakes', + 'Wedding cakes'), + ('Squirting Flowers', + 'Glasses of Water', + 'Squirt Guns', + 'Seltzer Bottles', + 'Fire Hoses', + 'Storm Clouds', + 'Geysers'), + ('Flower Pots', + 'Sandbags', + 'Anvils', + 'Big Weights', + 'Safes', + 'Grand Pianos', + 'Oceanliners')) +BattleGlobalAvTrackAccStrings = ('Medium', + 'Perfect', + 'Low', + 'High', + 'Medium', + 'High', + 'Low') +BattleGlobalLureAccLow = 'Low' +BattleGlobalLureAccMedium = 'Medium' +AttackMissed = 'MISSED' +NPCCallButtonLabel = 'CALL' +LoaderLabel = 'Loading...' +StarringIn = 'Starring In...' +HeadingToHood = 'Heading %(to)s %(hood)s...' +HeadingToYourEstate = 'Heading to your estate...' +HeadingToEstate = "Heading to %s's estate..." +HeadingToFriend = "Heading to %s's friend's estate..." +HeadingToPlayground = 'Heading to the Playground...' +HeadingToStreet = 'Heading %(to)s %(street)s...' +TownBattleRun = 'Run all the way back to the playground?' +TownBattleChooseAvatarToonTitle = 'WHICH TOON?' +TownBattleChooseAvatarCogTitle = 'WHICH ' + Cog.upper() + '?' +TownBattleChooseAvatarBack = 'BACK' +FireCogTitle = 'PINK SLIPS LEFT:%s\nFIRE WHICH COG?' +FireCogLowTitle = 'PINK SLIPS LEFT:%s\nNOT ENOUGH SLIPS!' +TownBattleSOSNoFriends = 'No friends to call!' +TownBattleSOSWhichFriend = 'Call which friend?' +TownBattleSOSNPCFriends = 'Rescued Toons' +TownBattleSOSBack = 'BACK' +TownBattleToonSOS = 'SOS' +TownBattleToonFire = 'Fire' +TownBattleUndecided = '?' +TownBattleHealthText = '%(hitPoints)s/%(maxHit)s' +TownBattleWaitTitle = 'Waiting for\nother toons...' +TownSoloBattleWaitTitle = 'Please wait...' +TownBattleWaitBack = 'BACK' +TownBattleSOSPetSearchTitle = 'Searching for doodle\n%s...' +TownBattleSOSPetInfoTitle = '%s is %s' +TownBattleSOSPetInfoOK = lOK +TrolleyHFAMessage = 'You may not board the trolley until your Laff meter is smiling.' +TrolleyTFAMessage = 'You may not board the trolley until Joey says so.' +TrolleyHopOff = 'Hop off' +FishingExit = 'Exit' +FishingCast = 'Cast' +FishingAutoReel = 'Auto Reel' +FishingItemFound = 'You caught:' +FishingCrankTooSlow = 'Too\nslow' +FishingCrankTooFast = 'Too\nfast' +FishingFailure = "You didn't catch anything!" +FishingFailureTooSoon = "Don't start to reel in the line until you see a nibble. Wait for your float to bob up and down rapidly!" +FishingFailureTooLate = 'Be sure to reel in the line while the fish is still nibbling!' +FishingFailureAutoReel = "The auto-reel didn't work this time. Turn the crank by hand, at just the right speed, for your best chance to catch something!" +FishingFailureTooSlow = 'You turned the crank too slowly. Some fish are faster than others. Try to keep the speed bar centered!' +FishingFailureTooFast = 'You turned the crank too quickly. Some fish are slower than others. Try to keep the speed bar centered!' +FishingOverTankLimit = 'Your fish bucket is full. Go sell your fish to the Pet Shop Clerk and come back.' +FishingBroke = 'You do not have any more Jellybeans for bait! Ride the trolley or sell fish to the Pet Shop Clerks to earn more Jellybeans.' +FishingHowToFirstTime = 'Click and drag down from the Cast button. The farther down you drag, the stronger your cast will be. Adjust your angle to hit the fish targets.\n\nTry it now!' +FishingHowToFailed = 'Click and drag down from the Cast button. The farther down you drag, the stronger your cast will be. Adjust your angle to hit the fish targets.\n\nTry it again now!' +FishingBootItem = 'An old boot' +FishingJellybeanItem = '%s Jellybeans' +FishingNewEntry = 'New Species!' +FishingNewRecord = 'New Record!' +FishPokerCashIn = 'Cash In\n%s\n%s' +FishPokerLock = 'Lock' +FishPokerUnlock = 'Unlock' +FishPoker5OfKind = '5 of a Kind' +FishPoker4OfKind = '4 of a Kind' +FishPokerFullHouse = 'Full House' +FishPoker3OfKind = '3 of a Kind' +FishPoker2Pair = '2 Pair' +FishPokerPair = 'Pair' +TutorialGreeting1 = 'Hi %s!' +TutorialGreeting2 = 'Hi %s!\nCome over here!' +TutorialGreeting3 = 'Hi %s!\nCome over here!\nUse the arrow keys!' +PetTutorialTitle1 = 'The Doodle Panel' +PetTutorialTitle2 = 'Doodle SpeedChat' +PetTutorialTitle3 = 'Doodle Cattlelog' +PetTutorialNext = 'Next Page' +PetTutorialPrev = 'Previous Page' +PetTutorialDone = 'Done' +PetTutorialPage1 = 'Click on a Doodle to display the Doodle panel. From here you can feed, scratch, and call the Doodle.' +PetTutorialPage2 = "Use the new 'Pets' area in the SpeedChat menu to get a Doodle to do a trick. If he does it, reward him and he'll get better!" +PetTutorialPage3 = "Purchase new Doodle tricks from Clarabelle's Cattlelog. Better tricks give better Toon-Ups!" + +def getPetGuiAlign(): + from pandac.PandaModules import TextNode + return TextNode.ACenter + + +GardenTutorialTitle1 = 'Gardening' +GardenTutorialTitle2 = 'Flowers' +GardenTutorialTitle3 = 'Trees' +GardenTutorialTitle4 = 'How-to' +GardenTutorialTitle5 = 'Statues' +GardenTutorialNext = 'Next Page' +GardenTutorialPrev = 'Previous Page' +GardenTutorialDone = 'Done' +GardenTutorialPage1 = 'Toon up your Estate with a garden! You can plant flowers, grow trees, harvest super-powerful gags, and decorate with statues!' +GardenTutorialPage2 = 'Flowers are finicky and require unique Jellybean recipes. Once grown, put them in the wheelbarrow to sell them and work toward Laff boosts!' +GardenTutorialPage3 = 'Use a gag from your inventory to plant a tree. After a few days, that gag will do more damage! Remember to keep it healthy or the damage boost will go away.' +GardenTutorialPage4 = 'Walk up to these spots to plant, water, dig up or harvest your garden.' +GardenTutorialPage5 = "Statues can be purchased in Clarabelle's Cattlelog. Increase your skill to unlock the more extravagant statues!" +EstatePlaneReturn = "Cog invasion!!!" +EstatePlaneHoliday = "Happy halloween!!!" +PlaygroundDeathAckMessage = TheCogs + ' took all your gags!\n\nYou are sad. You may not leave the playground until you are happy.' +ForcedLeaveFactoryAckMsg = 'The ' + Foreman + ' was defeated before you could reach him. You did not recover any Cog parts.' +ForcedLeaveMintAckMsg = 'The Mint Floor Supervisor was defeated before you could reach him. You did not recover any Cogbucks.' +HeadingToFactoryTitle = '%s' +ForemanConfrontedMsg = '%s is battling the ' + Foreman + '!' +MintBossConfrontedMsg = '%s is battling the Supervisor!' +StageBossConfrontedMsg = '%s is battling the District Attorney!' +StageToonEnterElevator = '%s\nhas entered the elevator!' +ForcedLeaveStageAckMsg = 'The Law Clerk was defeated before you could reach him. You did not recover any Jury Notices.' +MinigameWaitingForOtherToons = 'Waiting for other toons to join...' +MinigamePleaseWait = 'Please wait...' +DefaultMinigameTitle = 'Minigame Title' +DefaultMinigameInstructions = 'Minigame Instructions' +HeadingToMinigameTitle = '%s' +MinigamePowerMeterLabel = 'Power Meter' +MinigamePowerMeterTooSlow = 'Too\nslow' +MinigamePowerMeterTooFast = 'Too\nfast' +MinigameTemplateTitle = 'Minigame Template' +MinigameTemplateInstructions = 'This is a template minigame. Use it to create new minigames.' +CannonGameTitle = 'Cannon Game' +CannonGameInstructions = 'Shoot your toon into the water tower as quickly as you can. Use the mouse or the arrow keys to aim the cannon. Be quick and win a big reward for everyone!' +CannonGameReward = 'REWARD' +TwoDGameTitle = 'Toon Escape' +TwoDGameInstructions = 'Escape from the ' + Cog + ' den as soon as you can. Use arrow keys to run/jump and Ctrl to squirt a ' + Cog + '. Collect ' + Cog + ' treasures to gain even more points.' +TwoDGameElevatorExit = 'EXIT' +TugOfWarGameTitle = 'Tug-of-War' +TugOfWarInstructions = "Alternately tap the left and right arrow keys just fast enough to line up the green bar with the red line. Don't tap them too slow or too fast, or you'll end up in the water!" +TugOfWarGameGo = 'GO!' +TugOfWarGameReady = 'Ready...' +TugOfWarGameEnd = 'Good game!' +TugOfWarGameTie = 'You tied!' +TugOfWarPowerMeter = 'Power meter' +PatternGameTitle = 'Match Jaymo' +PatternGameInstructions = 'Jaymo will show you a dance sequence. ' + "Try to repeat Jaymo's dance just the way you see it using the arrow keys!" +PatternGameWatch = 'Watch these dance steps...' +PatternGameGo = 'GO!' +PatternGameRight = 'Good, %s!' +PatternGameWrong = 'Oops!' +PatternGamePerfect = 'That was perfect, %s!' +PatternGameBye = 'Thanks for playing!' +PatternGamePleaseWait = 'Please wait...' +PatternGameFaster = 'You were\nfaster!' +PatternGameFastest = 'You were\nthe fastest!' +PatternGameYouCanDoIt = 'Come on!\nYou can do it!' +PatternGameOtherFaster = '\nwas faster!' +PatternGameOtherFastest = '\nwas the fastest!' +PatternGameGreatJob = 'Great Job!' +PatternGameRound = 'Round %s!' +PatternGameImprov = 'You did great! Now Improv!' +RaceGameTitle = 'Race Game' +RaceGameInstructions = 'Click a number. Choose wisely! You only advance if no one else picked the same number.' +RaceGameWaitingChoices = 'Waiting for other toons to choose...' +RaceGameCardText = '%(name)s draws: %(reward)s' +RaceGameCardTextBeans = '%(name)s receives: %(reward)s' +RaceGameCardTextHi1 = '%(name)s is one Fabulous Toon!' +RaceGameForwardOneSpace = ' forward 1 space' +RaceGameForwardTwoSpaces = ' forward 2 spaces' +RaceGameForwardThreeSpaces = ' forward 3 spaces' +RaceGameBackOneSpace = ' back 1 space' +RaceGameBackTwoSpaces = ' back 2 spaces' +RaceGameBackThreeSpaces = ' back 3 spaces' +RaceGameOthersForwardThree = ' all others forward \n3 spaces' +RaceGameOthersBackThree = 'all others back \n3 spaces' +RaceGameInstantWinner = 'Instant Winner!' +RaceGameJellybeans2 = '2 Jellybeans' +RaceGameJellybeans4 = '4 Jellybeans' +RaceGameJellybeans10 = '10 Jellybeans!' +RingGameTitle = 'Ring Game' +RingGameInstructionsSinglePlayer = 'Try to swim through as many of the %s rings as you can. Use the arrow keys to swim.' +RingGameInstructionsMultiPlayer = 'Try to swim through the %s rings. Other toons will try for the other colored rings. Use the arrow keys to swim.' +RingGameMissed = 'MISSED' +RingGameGroupPerfect = 'GROUP\nPERFECT!!' +RingGamePerfect = 'PERFECT!' +RingGameGroupBonus = 'GROUP BONUS' +ColorRed = 'red' +ColorGreen = 'green' +ColorOrange = 'orange' +ColorPurple = 'purple' +ColorWhite = 'white' +ColorBlack = 'black' +ColorYellow = 'yellow' +DivingGameTitle = 'Treasure Dive' +DivingInstructionsSinglePlayer = 'Treasures will appear at the bottom of the lake. Use the arrow keys to swim. Avoid the fish and get the treasures up to the boat!' +DivingInstructionsMultiPlayer = 'Treasures will appear at the bottom of the lake. Use the arrow keys to swim. Work together to get the treasures up to the boat!' +DivingGameTreasuresRetrieved = 'Treasures Retrieved' +TargetGameTitle = 'Toon Slingshot' +TargetGameInstructionsSinglePlayer = 'Use your umbrella to land on the targets. The smaller the target, the more Jellybeans you get!' +TargetGameInstructionsMultiPlayer = 'Use your umbrella to land on the targets. The smaller the target, the more Jellybeans you get!' +TargetGameBoard = 'Round %s - Keeping Best Score' +TargetGameCountdown = 'Forced launch in %s seconds' +TargetGameCountHelp = 'Pound left and right arrows for power, stop to launch' +TargetGameFlyHelp = 'Press down to open umbrella' +TargetGameFallHelp = 'Use the arrow keys to land on target' +TargetGameBounceHelp = ' Bouncing can knock you off target' +TagGameTitle = 'Tag Game' +TagGameInstructions = 'Collect the treasures. You cannot collect treasure when you are IT!' +TagGameYouAreIt = 'You Are IT!' +TagGameSomeoneElseIsIt = '%s is IT!' +MazeGameTitle = 'Maze Game' +MazeGameInstructions = 'Collect the treasures. Try to get them all, but look out for the ' + Cogs + '!' +CatchGameTitle = 'Catching Game' +CatchGameInstructions = 'Catch as many %(fruit)s as you can. Watch out for the ' + Cogs + ", and try not to 'catch' any %(badThing)s!" +CatchGamePerfect = 'PERFECT!' +CatchGameApples = 'apples' +CatchGameOranges = 'oranges' +CatchGamePears = 'pears' +CatchGameCoconuts = 'coconuts' +CatchGameWatermelons = 'watermelons' +CatchGamePineapples = 'pineapples' +CatchGameAnvils = 'anvils' +PieTossGameTitle = 'Pie Toss Game' +PieTossGameInstructions = 'Toss pies at the targets.' +CogThiefGameTitle = 'Cog Thief' +CogThiefGameInstructions = 'Stop these Cogs from stealing our Gags! Press the Control key to throw pies. But be careful... they have a tendancy to explode!' +CogThiefBarrelsSaved = '%(num)d Barrels\nSaved!' +CogThiefBarrelSaved = '%(num)d Barrel\nSaved!' +CogThiefNoBarrelsSaved = 'No Barrels\nSaved' +CogThiefPerfect = 'PERFECT!' +MinigameRulesPanelPlay = 'PLAY' +MinigameRulesPanelSkip = 'SKIP\n%s/%s' +GagShopName = "Goofy's Gag Shop" +GagShopPlayAgain = 'PLAY\nAGAIN' +GagShopBackToPlayground = 'EXIT BACK TO\nPLAYGROUND' +GagShopYouHave = 'You have %s Jellybeans to spend' +GagShopYouHaveOne = 'You have 1 Jellybean to spend' +GagShopTooManyProps = 'Sorry, you have too many props' +GagShopDoneShopping = 'DONE\nSHOPPING' +GagShopTooManyOfThatGag = 'Sorry, you have enough %s already' +GagShopInsufficientSkill = 'You do not have enough skill for that yet' +GagShopNotEnoughJellybeans = 'You do not have enough Jellybeans for that gag' +GagShopYouPurchased = 'You purchased %s' +GagShopOutOfJellybeans = 'Sorry, you are all out of Jellybeans!' +GagShopPlayerDisconnected = '%s has disconnected' +GagShopPlayerExited = '%s has exited' +GagShopPlayerPlayAgain = 'Play Again' +GagShopPlayerBuying = 'Buying' +GenderShopFollow = 'Follow me!' +GenderShopSeeYou = 'See you later!' +GenderShopBoyButtonText = 'Boy' +GenderShopGirlButtonText = 'Girl' +BodyShopHead = 'Head' +BodyShopBody = 'Body' +BodyShopLegs = 'Legs' +ColorShopToon = 'Toon Color' +ColorShopHead = 'Head' +ColorShopBody = 'Body' +ColorShopLegs = 'Legs' +ColorShopParts = 'Multi Color' +ColorShopAll = 'Single Color' +ClothesShopShorts = 'Shorts' +ClothesShopShirt = 'Shirts' +ClothesShopBottoms = 'Bottoms' +PromptTutorial = "Congratulations!!\nYou are Toontown's newest citizen!\n\nWould you like to continue to the Toontorial or teleport directly to Toontown Central?" +MakeAToonSkipTutorial = 'Skip Toontorial' +MakeAToonEnterTutorial = 'Enter Toontorial' +MakeAToonDone = 'Done' +MakeAToonCancel = lCancel +MakeAToonNext = lNext +MakeAToonLast = 'Back' +CreateYourToon = 'Click the arrows to create your toon.' +CreateYourToonTitle = 'Choose Boy or Girl' +ShapeYourToonTitle = 'Choose Your Type' +PaintYourToonTitle = 'Choose Your Color' +PickClothesTitle = 'Choose Your Clothes' +NameToonTitle = 'Choose Your Name' +CreateYourToonHead = "Click the 'head' arrows to pick different animals." +MakeAToonClickForNextScreen = 'Click the arrow below to go to the next screen.' +PickClothes = 'Click the arrows to pick clothes!' +PaintYourToon = 'Click the arrows to paint your toon!' +MakeAToonYouCanGoBack = 'You can go back to change your body too!' +MakeAFunnyName = 'Choose a funny name for your toon with my Pick-A-Name game!' +MustHaveAFirstOrLast1 = "Your toon should have a first or last name, don't you think?" +MustHaveAFirstOrLast2 = "Don't you want your toon to have a first or last name?" +ApprovalForName1 = "That's it, your toon deserves a great name!" +ApprovalForName2 = 'Toon names are the best kind of names!' +MakeAToonLastStep = 'Last step before going to Toontown!' +PickANameYouLike = 'Pick a name you like!' +TitleCheckBox = 'Title' +FirstCheckBox = 'First' +LastCheckBox = 'Last' +RandomButton = 'Random' +ShuffleButton = 'Shuffle' +NameShopSubmitButton = 'Submit' +TypeANameButton = 'Type-A-Name' +TypeAName = "Don't like these names?\nClick here -->" +PickAName = 'Try the PickAName game!\nClick here -->' +PickANameButton = 'Pick-A-Name' +RejectNameText = 'That name is not allowed. Please try again.' +WaitingForNameSubmission = 'Submitting your name...' +PetshopUnknownName = 'Name: ???' +PetshopDescGender = 'Gender:\t%s' +PetshopDescCost = 'Cost:\t%s Jellybeans' +PetshopDescTrait = 'Traits:\t%s' +PetshopDescStandard = 'Standard' +PetshopCancel = lCancel +PetshopSell = 'Sell Fish' +PetshopAdoptAPet = 'Adopt a Doodle' +PetshopReturnPet = 'Return your Doodle' +PetshopAdoptConfirm = 'Adopt %s for %d Jellybeans?' +PetshopGoBack = 'Go Back' +PetshopAdopt = 'Adopt' +PetshopReturnConfirm = 'Return %s?' +PetshopReturn = 'Return' +PetshopChooserTitle = "TODAY'S DOODLES" +PetshopGoHomeText = 'Would you like to go to your estate to play with your new Doodle?' +NameShopNameMaster = 'NameMasterEnglish.txt' +NameShopContinueSubmission = 'Continue Submission' +NameShopChooseAnother = 'Choose Another Name' +NameShopToonCouncil = 'The Toon Council\nwill review your\nname. ' + 'Review may\ntake a few days.\nWhile you wait\nyour name will be\n ' +PleaseTypeName = 'Please type your name:' +AllNewNames = 'All new names must be\napproved by the Toon Council.' +NameMessages = 'Be creative, and remember:\nno NPC names, please.' +NameShopNameRejected = 'The name you\nsubmitted has\nbeen rejected.' +NoPunctuation = "You can't use punctuation marks in your name!" +PeriodOnlyAfterLetter = 'You can use a period in your name, but only after a letter.' +ApostropheOnlyAfterLetter = 'You can use an apostrophe in your name, but only after a letter.' +NoNumbersInTheMiddle = 'Numeric digits may not appear in the middle of a word.' +ThreeWordsOrLess = 'Your name must be three words or fewer.' +NumToColor = ['White', + 'Peach', + 'Bright Red', + 'Red', + 'Maroon', + 'Sienna', + 'Brown', + 'Tan', + 'Coral', + 'Orange', + 'Yellow', + 'Cream', + 'Citrine', + 'Lime', + 'Sea Green', + 'Green', + 'Light Blue', + 'Aqua', + 'Blue', + 'Periwinkle', + 'Royal Blue', + 'Slate Blue', + 'Purple', + 'Lavender', + 'Pink', + 'Plum', + 'Black', + 'Mountain Green', + 'Icy Blue', + 'Desert Sand', + 'Mint', + 'Charcoal', + 'Hot Pink', + 'Honey Mustard', + 'Gray', + 'Neon Orange', + 'Sapphire', + 'Crimson', + 'Emerald', + 'Bronze'] +AnimalToSpecies = {'dog': 'Dog', + 'cat': 'Cat', + 'mouse': 'Mouse', + 'horse': 'Horse', + 'rabbit': 'Rabbit', + 'duck': 'Duck', + 'monkey': 'Monkey', + 'bear': 'Bear', + 'pig': 'Pig'} +NameTooLong = 'That name is too long. Please try again.' +ToonAlreadyExists = 'You already have a toon named %s!' +NameAlreadyInUse = 'That name is already used!' +EmptyNameError = 'You must enter a name first.' +NameError = 'Sorry. That name will not work.' +NCTooShort = 'That name is too short.' +NCNoDigits = 'Your name cannot contain numbers.' +NCNeedLetters = 'Each word in your name must contain some letters.' +NCNeedVowels = 'Each word in your name must contain some vowels.' +NCAllCaps = 'Your name cannot be all capital letters.' +NCMixedCase = 'That name has too many capital letters.' +NCBadCharacter = "Your name cannot contain the character '%s'" +NCGeneric = 'Sorry, that name will not work.' +NCTooManyWords = 'Your name cannot be more than four words long.' +NCDashUsage = "Dashes may only be used to connect two words together (like in 'Boo-Boo')." +NCCommaEdge = 'Your name may not begin or end with a comma.' +NCCommaAfterWord = 'You may not begin a word with a comma.' +NCCommaUsage = 'That name does not use commas properly. Commas must join two words together, like in the name "Dr. Quack, MD". Commas must also be followed by a space.' +NCPeriodUsage = 'That name does not use periods properly. Periods are only allowed in words like "Mr.", "Mrs.", "J.T.", etc.' +NCApostrophes = 'That name has too many apostrophes.' +RemoveTrophy = lToonHQ + ': ' + TheCogs + ' took over one of the buildings you rescued!' +STOREOWNER_TOOKTOOLONG = 'Need more time to think?' +STOREOWNER_GOODBYE = 'See you later!' +STOREOWNER_NEEDJELLYBEANS = 'You need to ride the Trolley to get some Jellybeans.' +STOREOWNER_GREETING = 'Choose what you want to buy.' +STOREOWNER_BROWSING = 'You can browse, but you need a clothing ticket to buy.' +STOREOWNER_BROWSING_JBS = 'You can browse, but you need at least 200 Jellybeans to buy.' +STOREOWNER_NOCLOTHINGTICKET = 'You need a clothing ticket to shop for clothes.' +STOREOWNER_NOFISH = 'Come back here to sell fish to the Pet Shop for Jellybeans.' +STOREOWNER_THANKSFISH = 'Thanks! The Pet Shop will love these. Bye!' +STOREOWNER_THANKSFISH_PETSHOP = 'These are some fine specimens! Thanks.' +STOREOWNER_PETRETURNED = "Don't worry. We'll find a good home for your Doodle." +STOREOWNER_PETADOPTED = 'Congratulations on purchasing a Doodle! You can play with your new friend at your estate.' +STOREOWNER_PETCANCELED = 'Remember, if you see a Doodle you like, make sure to adopt him before someone else does!' +STOREOWNER_NOROOM = 'Hmm...you might want to make room in your closet before you buy new clothes.\n' +STOREOWNER_CONFIRM_LOSS = 'Your closet is full. You will lose the clothes you were wearing.' +STOREOWNER_OK = lOK +STOREOWNER_CANCEL = lCancel +STOREOWNER_TROPHY = 'Wow! You collected %s of %s fish. That deserves a trophy and a Laff boost!' +SuitInvasionPrefix = '%s: ' % lToonHQ +SuitInvasionBegin = [ + 'A Cog invasion has begun!!!', + '%(plural)s have taken over Toontown!!!' +] +SuitInvasionEnd = [ + 'The %(singular)s invasion has ended!!!', + 'The Toons have saved the day once again!!!' +] +SuitInvasionUpdate = [ + 'Keep it up, Toons!!!', + "The Cogs are beginning to explode in laughter!!!" +] +SuitInvasionBulletin = [ + 'There is a Cog invasion in progress!!!', + '%(plural)s have taken over Toontown!!!' +] +SkelecogInvasionBegin = [ + "This just in, Sellbot HQ's factory is going bonkers!", + "Sellbot HQ's factory is pushing out incomplete cogs!", + '%(singular)s Skelecogs have taken over Toontown!!!' +] +SkelecogInvasionEnd = [ + 'The %(singular)s Skelecog invasion has ended!!!', + 'The Toons have saved the day once again!!!' +] +SkelecogInvasionBulletin = [ + 'There is a Cog invasion in progress!!!', + "Sellbot HQ's factory has pushed out incomplete Cogs!", + '%(singular)s Skelecogs have taken over Toontown!!!' +] +WaiterInvasionBegin = [ + 'Uh oh, it appears that the Cogs banquet has been rescheduled!', + 'The Waiter %(singular)s are invading Toontown!!!' +] +WaiterInvasionEnd = [ + 'The Waiter %(singular)s waiters have been defeated!!!', + 'The Toons have saved the day once again!!!' +] +WaiterInvasionBulletin = [ + 'There is a Cog invasion in progress!!!', + 'The Cogs banquet has been rescheduled!!!', + 'The Waiter %(singular)s are invading Toontown!!!' +] +V2InvasionBegin = [ + "Oh my, stock up on gags toons!", + 'The Toon HQ has spotted a Version 2.0 Cog Invasion!', + 'The V2 %(plural)s have taken over Toontown!!!' +] +V2InvasionEnd = [ + 'The V2 %(singular)s invasion has ended!!!', + 'The Toons have saved the day once again!!!' +] +V2InvasionBulletin = [ + "There is a Cog invasion in progress!!!", + 'The Toon HQ has spotted a storm of Version 2.0 Cogs!', + 'The V2 %(plural)s have taken over Toontown!!!' +] +LeaderboardTitle = 'Toon Platoon' +QuestScript101_0 = 'Come here! Use the arrow keys to move.' +QuestScript101_1 = 'These are Cogs. They are robots that are trying to take over Toontown.' +QuestScript101_2 = 'There are many different kinds of Cogs and...' +QuestScript101_3 = '...they turn happy Toon buildings...' +QuestScript101_4 = '...into ugly Cog buildings!' +QuestScript101_5 = "But Cogs can't take a joke!" +QuestScript101_6 = 'A good gag will stop them.' +QuestScript101_8 = 'You also need a Laff meter.' +QuestScript101_9 = "If your Laff meter gets too low, you'll be sad!" +QuestScript101_10 = 'A happy Toon is a healthy Toon!' +QuestScript101_11 = "OH NO! There's a Cog outside my shop!" +QuestScript101_12 = 'HELP ME, PLEASE! Defeat that Cog!' +QuestScript101_13 = 'Here is your first ToonTask!' +QuestScript101_14 = 'Hurry up! Go defeat that Flunky!' +QuestScript110_1 = 'Good work defeating that Flunky. Let me give you a Shticker Book...' +QuestScript110_2 = 'The book is full of good stuff.' +QuestScript110_3 = "Open it, and I'll show you." +QuestScript110_4 = "The map shows where you've been." +QuestScript110_5 = 'Turn the page to see your gags...' +QuestScript110_6 = 'You can see your gags here.' +QuestScript110_7 = 'Turn the page to see your tasks.' +QuestScript110_8 = 'Take a ride on the trolley, and earn jelly beans to buy gags!' +QuestScript110_9 = 'To get to the trolley, go out the door behind me and head for the playground.' +QuestScript110_10 = 'Now, close the book and find the trolley!' +QuestScript110_11 = 'Return to Toon HQ when you are done. Bye!' +QuestScriptTutorialBlocker_1 = 'Why, hello there!' +QuestScriptTutorialBlocker_2 = 'Hello?' +QuestScriptTutorialBlocker_3 = "Oh! You don't know how to use SpeedChat!" +QuestScriptTutorialBlocker_4 = 'Click on the button to say something.' +QuestScriptTutorialBlocker_5 = 'Very good!\x07Where you are going there are many Toons to talk to.' +QuestScriptTutorialBlocker_6 = "If you want to chat with other Toons using the keyboard, there's another button you can use." +QuestScriptTutorialBlocker_7 = "It's called the SpeedChat Plus button." +QuestScriptTutorialBlocker_8 = 'Good luck! See you later!' +QuestScriptGagShop_1 = 'Welcome to the Gag Shop!' +QuestScriptGagShop_1a = 'This is where Toons come to buy gags to use against the Cogs.' +QuestScriptGagShop_3 = 'To buy gags, click on the gag buttons. Try getting some now!' +QuestScriptGagShop_4 = 'Good! You can use these gags in battle against the Cogs.' +QuestScriptGagShop_5 = "Here's a peek at the advanced gags..." +QuestScriptGagShop_6 = "When you're done buying gags, click this button to return to the Playground." +QuestScriptGagShop_7 = 'Normally you can use this button to play another Trolley Game...' +QuestScriptGagShop_8 = "...but there's no time for another game right now. You're needed in Toon HQ!" +QuestScript145_1 = 'I see you had no problem with the trolley!\x07Listen, the Cogs have stolen our blackboard eraser.\x07Go into the streets and fight Cogs until you recover the eraser.\x07To reach the streets go through one of the tunnels like this:' +QuestScript145_2 = "When you find our eraser, bring it back here.\x07Don't forget, if you need gags, ride the trolley.\x07Also, if you need to recover Laff points, collect ice cream cones in the Playground." +QuestScript150_1 = 'Great work!\x07Toontown is more fun when you have friends!' +QuestScript150_2 = 'To make friends, find another toon, and use the New Friend button.' +QuestScript150_3 = 'Once you have made a friend, come back here.' +QuestScript150_4 = 'Some tasks are too difficult to do alone!' +MissingKeySanityCheck = 'Ignore me' +SellbotBossName = 'Senior V. P.' +CashbotBossName = 'C. F. O.' +LawbotBossName = 'Chief Justice' +BossbotBossName = 'C. E. O.' +BossCogNameWithDept = '%(name)s\n%(dept)s' +BossCogPromoteDoobers = 'You are hereby promoted to full-fledged %s. Congratulations!' +BossCogDoobersAway = {'s': 'Go! And make that sale!'} +BossCogWelcomeToons = 'Welcome, new Cogs!' +BossCogPromoteToons = 'You are hereby promoted to full-fledged %s. Congratu--' +CagedToonInterruptBoss = 'Hey! Hiya! Hey over there!' +CagedToonRescueQuery = 'So, did you Toons come to rescue me?' +BossCogDiscoverToons = 'Huh? Toons! In disguise!' +BossCogAttackToons = 'Attack!!' +CagedToonDrop = ["Great job! You're wearing him down!", + "Keep after him! He's on the run!", + 'You guys are doing great!', + "Fantastic! You've almost got him now!"] +CagedToonPrepareBattleTwo = "Look out, he's trying to get away!\x07Help me, everyone--get up here and stop him!" +CagedToonPrepareBattleThree = "Hooray, I'm almost free!\x07Now you need to attack the V.P. Cog directly.\x07I've got a whole bunch of pies you can use!\x07Jump up and touch the bottom of my cage and I'll give you some pies.\x07Press the Delete key to throw pies once you've got them!" +BossBattleNeedMorePies = 'You need to get more pies!' +BossBattleHowToGetPies = 'Jump up to touch the cage to get pies.' +BossBattleHowToThrowPies = 'Press the Delete key to throw pies!' +CagedToonYippee = 'Yippee!' +CagedToonThankYou = "It's great to be free!\x07Thanks for all your help!\x07I am in your debt.\x07Here's my card. If you ever need a hand in battle, give a shout!\x07Just click on your SOS button." +CagedToonPromotion = "\x07Say--that V.P. Cog left behind your promotion papers.\x07I'll file them for you on the way out, so you'll get your promotion!" +CagedToonLastPromotion = "\x07Wow, you've reached level %s on your Cog suit!\x07Cogs don't get promoted higher than that.\x07You can't upgrade your Cog suit anymore, but you can certainly keep rescuing Toons!" +CagedToonHPBoost = "\x07You've rescued a lot of Toons from this HQ.\x07The Toon Council has decided to give you another Laff point. Congratulations!" +CagedToonMaxed = '\x07I see that you have a level %s Cog suit. Very impressive!\x07On behalf of the Toon Council, thank you for coming back to rescue more Toons!' +CagedToonGoodbye = 'See ya!' +CagedToonBattleThree = {10: 'Nice jump, %(toon)s. Here are some pies!', + 11: 'Hi, %(toon)s! Have some pies!', + 12: "Hey there, %(toon)s! You've got some pies now!", + 20: 'Hey, %(toon)s! Jump up to my cage and get some pies to throw!', + 21: 'Hi, %(toon)s! Use the Ctrl key to jump up and touch my cage!', + 100: 'Press the Delete key to throw a pie.', + 101: 'The blue power meter shows how high your pie will go.', + 102: 'First try to lob a pie inside his undercarriage to gum up his works.', + 103: 'Wait for the door to open, and throw a pie straight inside.', + 104: "When he's dizzy, hit him in the face or chest to knock him back!", + 105: "You'll know you've got a good hit when you see the splat in color.", + 106: 'If you hit a Toon with a pie, it gives that Toon a Laff point!'} +CagedToonBattleThreeMaxGivePies = 12 +CagedToonBattleThreeMaxTouchCage = 21 +CagedToonBattleThreeMaxAdvice = 106 +CashbotBossHadEnough = "That's it. I've had enough of these pesky Toons!" +CashbotBossOuttaHere = "I've got a train to catch!" +ResistanceToonCongratulations = "You did it! Congratulations!\x07You're an asset to the Resistance!\x07Here's a special phrase you can use in a tight spot:\x07%s\x07When you say it, %s.\x07But you can only use it once, so choose that time well!" +ResistanceToonToonupInstructions = 'all the Toons near you will gain %s Laff points' +ResistanceToonToonupAllInstructions = 'all the Toons near you will gain full Laff points' +ResistanceToonMoneyInstructions = 'all the Toons near you will gain %s jellybeans' +ResistanceToonMoneyAllInstructions = 'all the Toons near you will fill their jellybean jars' +ResistanceToonTicketsInstructions = 'all the Toons near you will gain %s tickets' +ResistanceToonRestockInstructions = 'all the Toons near you will restock their "%s" gags' +ResistanceToonRestockAllInstructions = 'all the Toons near you will restock all their gags' +ResistanceToonMeritsInstructions = 'all the Toons near you will fill part of their %s' +ResistanceToonMeritsAllInstructions = 'all the Toons near you will fill part of all their promotion papers' +ResistanceToonLastPromotion = "\x07Wow, you've reached level %s on your Cog suit!\x07Cogs don't get promoted higher than that.\x07You can't upgrade your Cog suit anymore, but you can certainly keep working for the Resistance!" +ResistanceToonHPBoost = "\x07You've done a lot of work for the Resistance.\x07The Toon Council has decided to give you another Laff point. Congratulations!" +ResistanceToonMaxed = '\x07I see that you have a level %s Cog suit. Very impressive!\x07On behalf of the Toon Council, thank you for coming back to rescue more Toons!' +CashbotBossCogAttack = 'Get them!!!' +ResistanceToonWelcome = 'Hey, you made it! Follow me to the main vault before the C.F.O. finds us!' +ResistanceToonTooLate = "Blast it! We're too late!" +CashbotBossDiscoverToons1 = 'Ah-HAH!' +CashbotBossDiscoverToons2 = 'I thought I smelled something a little toony in here! Imposters!' +ResistanceToonKeepHimBusy = "Keep him busy! I'm going to set a trap!" +ResistanceToonWatchThis = 'Watch this!' +CashbotBossGetAwayFromThat = 'Hey! Get away from that!' +ResistanceToonCraneInstructions1 = 'Control a magnet by stepping up to a podium.' +ResistanceToonCraneInstructions2 = 'Use the arrow keys to move the crane, and press the Ctrl key to grab an object.' +ResistanceToonCraneInstructions3 = "Grab a safe with a magnet and knock the C.F.O.'s safe-ty helmet off." +ResistanceToonCraneInstructions4 = 'Once his helmet is gone, grab a disabled goon and hit him in the head!' +ResistanceToonGetaway = 'Eek! Gotta run!' +CashbotCraneLeave = 'Leave Crane' +CashbotCraneAdvice = 'Use the arrow keys to move the overhead crane.' +CashbotMagnetAdvice = 'Hold down the control key to pick things up.' +CashbotCraneLeaving = 'Leaving crane' +MintElevatorRejectMessage = 'You cannot enter the Mints until you have completed your %s Cog Suit.' +BossElevatorRejectMessage = 'You cannot board this elevator until you have earned a promotion.' +SellbotRentalSuitMessage = "Wear this Rental Suit so you can get close enough to the VP to attack.\n\nYou won't earn Merits or promotions, but you can rescue a Toon for an SOS reward!" +SellbotCogSuitNoMeritsMessage = "Your Sellbot Disguise will get you in, but since you don't have enough Merits, you won't earn a promotion.\n\nIf you rescue the trapped Toon, you will earn an SOS Toon reward!" +SellbotCogSuitHasMeritsMessage = "It's Operation: Storm Sellbot!\n\nBring 5 or more Rental Suit Toons with you to defeat the VP and earn credit towards a reward!" +LawbotRentalSuitMessage = "Wear this Rental Suit so you can get close enough to the CJ to attack.\n\nYou won't earn Jury Notices or promotions, but you can pass the bar exam for a Cog Summon!" +LawbotCogSuitNoNoticesMessage = "Your Lawbot Disguise will get you in, but since you don't have enough Jury Notices, you won't earn a promotion.\n\nIf you pass the bar exam, you will earn a Cog Summon!" +LawbotCogSuitHasNoticesMessage = "It's Operation: Lawbots Lose!\n\nBring 7 or more Rental Suit Toons with you to defeat the CJ and earn credit towards a summon!" +CashbotRentalSuitMessage = "Wear this Rental Suit so you can get close enough to the CFO to attack.\n\nYou wont earn Cogbucks or promotions, but you can delay the CFO for a Toon Unite!" +CashbotCogSuitNoCogbucksMessage = "Your Cashbot Disguise will get you in, but since you don't have enough Cogbucks, you won't earn a promotion.\n\n If you delay the CFO, you will earn a Toon Unite!" +CashbotCogSuitHasCogbucksMessage = "It's Operation: Cashbot Chaos!\n\n Bring 6 or more Rental Suit Toons with you to defeat the CFO and earn credit towards a unite!" +BossbotRentalSuitMessage = "Wear this Rental Suit so you can get close enough to the CEO to attack.\n\bYou wont earn Stock Options or promotions, but you can soak the CEO for a Fire!" +BossbotCogSuitNoOptionsMessage = "Your Bossbot Disguise will get you in, but since you don't have enough Stock Options you won't warn a promotion.\n\n If you soak the CEO, you will earn a Fire!" +BossbotCogSuitHasOptionsMessage = "It's Operation: Besiege Bossbot!\n\n Bring in 8 Rental Suit Toons with you to defeat the CEO and earn credit towards a Fire!" +FurnitureTypeName = 'Furniture' +PaintingTypeName = 'Painting' +ClothingTypeName = 'Clothing' +ChatTypeName = 'SpeedChat Phrase' +EmoteTypeName = 'Acting Lessons' +BeanTypeName = 'Jellybeans' +PoleTypeName = 'Fishing Pole' +TankTypeName = 'Fishing Bucket' +WindowViewTypeName = 'Window View' +PetTrickTypeName = 'Doodle Training' +GardenTypeName = 'Garden Supplies' +RentalTypeName = 'Rental Item' +GardenStarterTypeName = 'Gardening Kit' +NametagTypeName = 'Name tag' +AccessoryTypeName = 'Accessory' +HatStylesDescriptions = {'hbb1': 'Green Baseball Cap', + 'kmh1': 'Mouskateer', + 'hbb2': 'Blue Baseball Cap', + 'hbb3': 'Orange Baseball Cap', + 'hsf1': 'Beige Safari Hat', + 'hsf2': 'Brown Safari Hat', + 'hsf3': 'Green Safari Hat', + 'hrb1': 'Pink Bow', + 'hrb2': 'Red Bow', + 'hrb3': 'Purple Bow', + 'hht1': 'Pink Heart', + 'hht2': 'Yellow Heart', + 'htp1': 'Black Top Hat', + 'htp2': 'Blue Top Hat', + 'hav1': 'Anvil Hat', + 'hfp1': 'Flower Hat', + 'hsg1': 'Sandbag Hat', + 'hwt1': 'Weight Hat', + 'hfz1': 'Fez Hat', + 'hgf1': 'Golf Hat', + 'hpt1': 'Party Hat', + 'hpt2': 'Toon Party Hat', + 'hpb1': 'Fancy Hat', + 'hcr1': 'Crown', + 'hcw1': 'Cowboy Hat', + 'hpr1': 'Pirate Hat', + 'hpp1': 'Propeller Hat', + 'hfs1': 'Fishing Hat', + 'hsb1': 'Sombrero Hat', + 'hst1': 'Straw Hat', + 'hsu1': 'Sun Hat', + 'hrb4': 'Yellow Bow', + 'hrb5': 'Checker Bow', + 'hrb6': 'Light Red Bow', + 'hrb7': 'Rainbow Bow', + 'hat1': 'Antenna Thingy', + 'hhd1': 'Beehive Hairdo', + 'hbw1': 'Bowler Hat', + 'hch1': 'Chef Hat', + 'hdt1': 'Detective Hat', + 'hft1': 'Fancy Feathers Hat', + 'hfd1': 'Fedora', + 'hmk1': "Mickey's Band Hat", + 'hft2': 'Feather Headband', + 'hhd2': 'Pompadour Hairdo', + 'hpc1': 'Princess Hat', + 'hrh1': 'Archer Hat', + 'hhm1': 'Roman Helmet', + 'hat2': 'Spider Antenna Thingy', + 'htr1': 'Tiara', + 'hhm2': 'Viking Helmet', + 'hwz1': 'Witch Hat', + 'hwz2': 'Wizard Hat', + 'hhm3': 'Conquistador Helmet', + 'hhm4': 'Firefighter Helmet', + 'hfp2': 'Anti-Cog Control Hat', + 'hhm5': 'Miner Hat', + 'hnp1': 'Napoleon Hat', + 'hpc2': 'Pilot Cap', + 'hph1': 'Cop Hat', + 'hwg1': 'Rainbow Wacky Wig', + 'hbb4': 'Yellow Baseball Cap', + 'hbb5': 'Red Baseball Cap', + 'hbb6': 'Aqua Baseball Cap', + 'hsl1': 'Sailor Hat', + 'hfr1': 'Samba Hat', + 'hby1': 'Bobby Hat', + 'hrb8': 'Pink Dots Bow', + 'hjh1': 'Jester Hat', + 'hbb7': 'Purple Baseball Cap', + 'hrb9': 'Green Checker Bow', + 'hwt2': 'Winter Hat', + 'hhw1': 'Bandana', + 'hhw2': 'Toonosaur Hat', + 'hob1': 'Jamboree Hat', + 'hbn1': 'Bird Hat'} +GlassesStylesDescriptions = {'grd1': 'Round Glasses', + 'gmb1': 'White Mini Blinds', + 'gnr1': 'Purple Narrow Glasses', + 'gst1': 'Yellow Star Glasses', + 'g3d1': 'Movie Glasses', + 'gav1': 'Aviator', + 'gce1': 'Cateye Glasses', + 'gdk1': 'Nerd Glasses', + 'gjo1': 'Celebrity Shades', + 'gsb1': 'Scuba Mask', + 'ggl1': 'Goggles', + 'ggm1': 'Groucho Glasses', + 'ghg1': 'Heart Glasses', + 'gie1': 'Bug Eye Glasses', + 'gmt1': 'Black Secret ID Mask', + 'gmt2': 'Blue Secret ID Mask', + 'gmt3': 'Blue Carnivale Mask', + 'gmt4': 'Purple Carnivale Mask', + 'gmt5': 'Aqua Carnivale Mask', + 'gmn1': 'Monocle', + 'gmo1': 'Smooch Glasses', + 'gsr1': 'Square Frame Glasses', + 'ghw1': 'Skull Eyepatch', + 'ghw2': 'Gem Eyepatch', + 'gag1': 'Alien Eyes', + 'ghy1': 'Hypno Lure Look'} +BackpackStylesDescriptions = {'bpb1': 'Blue Backpack', + 'bpb2': 'Orange Backpack', + 'bpb3': 'Purple BackPack', + 'bpd1': 'Red Dot Backpack', + 'bpd2': 'Yellow Dot Backpack', + 'bwg1': 'Bat Wings', + 'bwg2': 'Bee Wings', + 'bwg3': 'DragonFly Wings', + 'bst1': 'Scuba Tank', + 'bfn1': 'Shark Fin', + 'baw1': 'White Angel Wings', + 'baw2': 'Rainbow Angel Wings', + 'bwt1': 'Toys Backpack', + 'bwg4': 'Butterfly Wings', + 'bwg5': 'Pixie Wings', + 'bwg6': 'Dragon Wings', + 'bjp1': 'Jet Pack', + 'blg1': 'Bug Backpack', + 'bsa1': 'Plush Bear Pack', + 'bwg7': 'Bird wings', + 'bsa2': 'Plush Cat Pack', + 'bsa3': 'Plush Dog Pack', + 'bap1': 'Airplane Wings', + 'bhw1': 'Pirate Sword', + 'bhw2': 'Super Toon Cape', + 'bhw3': 'Vampire Cape', + 'bhw4': 'Toonosaur Backpack', + 'bob1': 'Jamboree Pack', + 'bfg1': 'Gag Attack Pack', + 'bfl1': 'Cog Pack'} +ShoesStylesDescriptions = {'sat1': 'Green Athletic Shoes', + 'sat2': 'Red Athletic Shoes', + 'smb1': 'Green Toon Boots', + 'scs1': 'Green Sneakers', + 'swt1': 'Wingtips', + 'smj1': 'Black Fancy Shoes', + 'sdk1': 'Boat Shoes', + 'sat3': 'Yellow Athletic Shoes', + 'scs2': 'Black Sneakers', + 'scs3': 'White Sneakers', + 'scs4': 'Pink Sneakers', + 'scb1': 'Cowboy Boots', + 'sfb1': 'Purple Boots', + 'sht1': 'Green Hi Top Sneakers', + 'smj2': 'Brown Fancy Shoes', + 'smj3': 'Red Fancy Shoes', + 'ssb1': 'Red Super Toon Boots', + 'sts1': 'Green Tennis Shoes', + 'sts2': 'Pink Tennis Shoes', + 'scs5': 'Red Sneakers', + 'smb2': 'Aqua Toon Boots', + 'smb3': 'Brown Toon Boots', + 'smb4': 'Yellow Toon Boots', + 'sfb2': 'Blue Square Boots', + 'sfb3': 'Green Hearts Boots', + 'sfb4': 'Grey Dots Boots', + 'sfb5': 'Orange Stars Boots', + 'sfb6': 'Pink Stars Boots', + 'slf1': 'Loafers', + 'smj4': 'Purple Fancy Shoes', + 'smt1': 'Motorcycle Boots', + 'sox1': 'Oxfords', + 'srb1': 'Pink Rain Boots', + 'sst1': 'Jolly Boots', + 'swb1': 'Beige Winter Boots', + 'swb2': 'Pink Winter Boots', + 'swk1': 'Work Boots', + 'scs6': 'Yellow Sneakers', + 'smb5': 'Pink Toon Boots', + 'sht2': 'Pink Hi Top Sneakers', + 'srb2': 'Red Dots Rain Boots', + 'sts3': 'Purple Tennis Shoes', + 'sts4': 'Violet Tennis Shoes', + 'sts5': 'Yellow Tennis Shoes', + 'srb3': 'Blue Rain Boots', + 'srb4': 'Yellow Rain Boots', + 'sat4': 'Black Athletic Shoes', + 'shw1': 'Pirate Shoes', + 'shw2': 'Toonosaur Feet'} +AccessoryNamePrefix = {0: 'hat unisex ', + 1: 'glasses unisex ', + 2: 'backpack unisex ', + 3: 'shoes unisex ', + 4: 'hat boy ', + 5: 'glasses boy ', + 6: 'backpack boy ', + 7: 'shoes boy ', + 8: 'hat girl ', + 9: 'glasses girl ', + 10: 'backpack girl ', + 11: 'shoes girl '} +AccessoryTypeNames = {} +for accessoryId in CatalogAccessoryItemGlobals.AccessoryTypes.keys(): + accessoryInfo = CatalogAccessoryItemGlobals.AccessoryTypes[accessoryId] + if accessoryInfo[0] % 4 == 0: + accessoryStyleDescription = HatStylesDescriptions + elif accessoryInfo[0] % 4 == 1: + accessoryStyleDescription = GlassesStylesDescriptions + elif accessoryInfo[0] % 4 == 2: + accessoryStyleDescription = BackpackStylesDescriptions + else: + accessoryStyleDescription = ShoesStylesDescriptions + AccessoryTypeNames[accessoryId] = accessoryStyleDescription[accessoryInfo[1]] + +SpecialEventMailboxStrings = {1: 'A special item from the Toon Council just for you!', + 2: "Here is your Melville's Fishing Tournament prize! Congratulations!", + 3: "Here is your Billy Budd's Fishing Tournament prize! Congratulations!", + 4: 'Here is your Acorn Acres April Invitational prize! Congratulations!', + 5: 'Here is your Acorn Acres C.U.P. Championship prize! Congratulations!', + 6: 'Here is your Gift-Giving Extravaganza prize! Congratulations!', + 7: "Here is your Top Toons New Year's Day Marathon prize! Congratulations!", + 8: 'Here is your Perfect Trolley Games Weekend prize! Congratulations!', + 9: 'Here is your Trolley Games Madness prize! Congratulations!', + 10: 'Here is your Grand Prix Weekend prize! Congratulations!', + 11: 'Here is your ToonTask Derby prize! Congratulations!', + 12: 'Here is your Save a Building Marathon prize! Congratulations!', + 13: 'Here is your Most Cogs Defeated Tournament prize! Congratulations!', + 14: 'Here is your Most V.P.s Defeated Tournament prize! Congratulations!', + 15: 'Here is your Operation: Storm Sellbot prize! Congratulations!', + 16: 'Here is your Most C.J.s Defeated Tournament prize! Congratulations!', + 17: 'Here is your Operation: Lawbots Lose prize! Congratulations!', + 18: 'Here is your Most C.F.O.s Defeated Tournament prize! Congratulations!', + 19: 'Here is your Operation: Cashbot Chaos prize! Congratulations!', + 20: 'Here is your Most C.E.O.s Defeated Tournament prize! Congratulations!', + 21: 'Here is your Operation: Besiege Bossbot prize! Congratulations!'} +RentalHours = 'Hours' +RentalOf = 'Of' +RentalCannon = 'Cannons!' +RentalGameTable = 'Game Table!' +EstateCannonGameEnd = 'The Cannon Game rental is over.' +GameTableRentalEnd = 'The Game Table rental is over.' +MessageConfirmRent = 'Begin rental? Cancel to save the rental for later' +MessageConfirmGarden = 'Are you sure you want to start a garden?' +FurnitureYourOldCloset = 'your old wardrobe' +FurnitureYourOldBank = 'your old bank' +TrunkHatGUI = 'Hats' +TrunkGlassesGUI = 'Glasses' +TrunkBackpackGUI = 'Backpacks' +TrunkShoesGUI = 'Shoes' +ChatItemQuotes = '"%s"' +FurnitureNames = {100: 'Armchair', + 105: 'Armchair', + 110: 'Chair', + 120: 'Desk Chair', + 130: 'Log Chair', + 140: 'Lobster Chair', + 145: 'Lifejacket Chair', + 150: 'Saddle Stool', + 160: 'Native Chair', + 170: 'Cupcake Chair', + 200: 'Bed', + 205: 'Bed', + 210: 'Bed', + 220: 'Bathtub Bed', + 230: 'Leaf Bed', + 240: 'Boat Bed', + 250: 'Cactus Hammock', + 260: 'Ice Cream Bed', + 270: "Olivia Erin & Cat's Bed", + 300: 'Player Piano', + 310: 'Pipe Organ', + 400: 'Fireplace', + 410: 'Fireplace', + 420: 'Round Fireplace', + 430: 'Fireplace', + 440: 'Apple Fireplace', + 450: "Erin's Fireplace", + 460: "Erin's Lit Fireplace", + 470: 'Lit Fireplace', + 480: 'Round Lit Fireplace', + 490: 'Lit Fireplace', + 491: 'Lit Fireplace', + 492: 'Apple Lit Fireplace', + 500: 'Wardrobe', + 502: '15 item Wardrobe', + 504: '20 item Wardrobe', + 506: '25 item Wardrobe', + 508: '50 item Wardrobe', + 510: 'Wardrobe', + 512: '15 item Wardrobe', + 514: '20 item Wardrobe', + 516: '25 item Wardrobe', + 518: '50 item Wardrobe', + 600: 'Short Lamp', + 610: 'Tall Lamp', + 620: 'Table Lamp', + 625: 'Table Lamp', + 630: 'Daisy Lamp', + 640: 'Daisy Lamp', + 650: 'Jellyfish Lamp', + 660: 'Jellyfish Lamp', + 670: 'Cowboy Lamp', + 680: 'Candle', + 681: 'Lit Candle', + 700: 'Cushioned Chair', + 705: 'Cushioned Chair', + 710: 'Couch', + 715: 'Couch', + 720: 'Hay Couch', + 730: 'Shortcake Couch', + 800: 'Desk', + 810: 'Log Desk', + 900: 'Umbrella Stand', + 910: 'Coat Rack', + 920: 'Trash Can', + 930: 'Red Mushroom', + 940: 'Yellow Mushroom', + 950: 'Coat Rack', + 960: 'Barrel Stand', + 970: 'Cactus Plant', + 980: 'Teepee', + 990: "Juliette's Fan", + 1000: 'Large Rug', + 1010: 'Round Rug', + 1015: 'Round Rug', + 1020: 'Small Rug', + 1030: 'Leaf Mat', + 1040: 'Presents', + 1050: 'Sled', + 1100: 'Display Cabinet', + 1110: 'Display Cabinet', + 1120: 'Tall Bookcase', + 1130: 'Low Bookcase', + 1140: 'Sundae Chest', + 1200: 'End Table', + 1210: 'Small Table', + 1215: 'Small Table', + 1220: 'Coffee Table', + 1230: 'Coffee Table', + 1240: "Snorkeler's Table", + 1250: 'Cookie Table', + 1260: 'Bedroom Table', + 1300: 'Bean Bank', + 1310: '15000 Bean Bank', + 1320: '20000 Bean Bank', + 1330: '25000 Bean Bank', + 1340: '30000 Bean Bank', + 1399: 'Telephone', + 1400: 'Cezanne Toon', + 1410: 'Flowers', + 1420: 'Modern Mickey', + 1430: 'Rembrandt Toon', + 1440: 'Toonscape', + 1441: "Whistler's Horse", + 1442: 'Toon Star', + 1443: 'Not a Pie', + 1450: 'Mickey and Minnie', + 1500: 'Radio', + 1510: 'Radio', + 1520: 'Radio', + 1530: 'Television', + 1531: '50" HD Television', + 1532: '100" HD Television', + 1600: 'Short Vase', + 1610: 'Tall Vase', + 1620: 'Short Vase', + 1630: 'Tall Vase', + 1640: 'Short Vase', + 1650: 'Short Vase', + 1660: 'Coral Vase', + 1661: 'Shell Vase', + 1670: 'Rose Vase', + 1680: 'Rose Watercan', + 1700: 'Popcorn Cart', + 1710: 'Ladybug', + 1720: 'Fountain', + 1725: 'Washing Machine', + 1800: 'Fish Bowl', + 1810: 'Fish Bowl', + 1900: 'Swordfish', + 1910: 'Hammerhead', + 1920: 'Hanging Horns', + 1930: 'Simple Sombrero', + 1940: 'Fancy Sombrero', + 1950: 'Dream Catcher', + 1960: 'Horseshoe', + 1970: 'Bison Portrait', + 2000: 'Candy Swing Set', + 2010: 'Cake Slide', + 3000: 'Banana Split Tub', + 4000: 'Boy Trunk', + 4010: 'Girl Trunk', + 10000: 'Short Pumpkin', + 10010: 'Tall Pumpkin', + 10020: 'Winter Tree', + 10030: 'Winter Wreath', + 10040: 'Cog Nation Crate'} +ClothingArticleNames = ('Shirt', + 'Shirt', + 'Shirt', + 'Shorts', + 'Shorts', + 'Skirt', + 'Shorts') +ClothingTypeNames = {1001: 'Ghost Shirt', + 1002: 'Pumpkin Shirt', + 1112: 'Bee Shirt', + 1113: 'Pirate Shirt', + 1114: 'Super Toon Shirt', + 1115: 'Vampire Shirt', + 1116: 'Toonosaur Shirt', + 1117: 'Bee Shorts', + 1118: 'Pirate Shorts', + 1119: 'Super Toon Shorts', + 1120: 'Vampire Shorts', + 1121: 'Toonosaur Shorts', + 1122: 'Bee Shorts', + 1123: 'Pirate Shorts', + 1124: 'Super Toon Shorts', + 1125: 'Vampire Shorts', + 1126: 'Toonosaur Shorts', + 1127: 'Pirate Skirt', + 1304: "O'Shirt", + 1305: "O'Shorts", + 1306: "O'Skirt", + 1400: "Matthew's Shirt", + 1401: "Jessica's Shirt", + 1402: "Marissa's Shirt", + 1600: 'Trap Outfit', + 1601: 'Sound Outfit', + 1602: 'Lure Outfit', + 1603: 'Trap Outfit', + 1604: 'Sound Outfit', + 1605: 'Lure Outfit', + 1606: 'Trap Outfit', + 1607: 'Sound Outfit', + 1608: 'Lure Outfit', + 1723: 'Bee Shirt', + 1724: 'SuperToon Shirt', + 1734: 'Bee Shorts', + 1735: 'SuperToon Shorts', + 1739: 'Bee Skirt', + 1740: 'SuperToon Skirt', + 1743: 'Skeleton Shirt', + 1744: 'Spider Shirt', + 1745: 'Spider Shorts', + 1746: 'Skeleton Shorts', + 1747: 'Skeleton Skirt', + 1748: 'Spider Skirt', + 1749: 'Silly Mailbox Shirt', + 1750: 'Silly Trash Can Shirt', + 1751: 'Loony Labs Shirt', + 1752: 'Silly Hydrant Shirt', + 1753: 'Silly Meter Shirt', + 1754: 'Cog-Crusher Shirt', + 1755: 'Cog-Crusher Shorts', + 1756: 'Cog-Crusher Shorts', + 1757: 'Victory Party Shirt', + 1758: 'Relaxed Victory Shirt', + 1763: 'Smashed Sellbot Shirt', + 1764: 'Most V.P.s Defeated Shirt', + 1765: 'Sellbot Smasher Shirt', + 1766: 'Sellbot Smasher Shorts', + 1767: 'Sellbot Smasher Shorts', + 1768: 'Jellybean Bank Shirt', + 1769: 'Doodle Shirt', + 1770: 'Vampire Shirt', + 1771: 'Turtle Shirt', + 1772: 'Vampire Shorts', + 1773: 'Vampire Shorts', + 1774: 'Turtle Shorts', + 1775: 'Turtle Shorts', + 1776: 'Get Connected Mover & Shaker Shirt', + 1777: 'Smashed Lawbot Shirt', + 1778: 'Most C.J.s Defeated Shirt', + 1779: 'Lawbot Smasher Shirt', + 1780: 'Lawbot Smasher Shorts', + 1781: 'Lawbot Smasher Shorts', + 1782: 'Racing Shirt 3', + 1783: 'Racing Shorts 1', + 1784: 'Racing Skirt 1', + 1801: 'Batty Moon Shirt', + 1802: 'Mittens Shirt', + 1821: 'Plaid Punk Shirt'} +AccessoryArticleNames = ('Hat', + 'Glasses', + 'Backpack', + 'Shoes', + 'Hat', + 'Glasses', + 'Backpack', + 'Shoes', + 'Hat', + 'Glasses', + 'Backpack', + 'Shoes') +SurfaceNames = ('Wallpaper', + 'Moulding', + 'Flooring', + 'Wainscoting', + 'Border') +WallpaperNames = {1000: 'Parchment', + 1100: 'Milan', + 1200: 'Dover', + 1300: 'Victoria', + 1400: 'Newport', + 1500: 'Pastoral', + 1600: 'Harlequin', + 1700: 'Moon', + 1800: 'Stars', + 1900: 'Flowers', + 2000: 'Spring Garden', + 2100: 'Formal Garden', + 2200: 'Race Day', + 2300: 'Touchdown!', + 2400: 'Cloud 9', + 2500: 'Climbing Vine', + 2600: 'Springtime', + 2700: 'Kokeshi', + 2800: 'Posies', + 2900: 'Angel Fish', + 3000: 'Bubbles', + 3100: 'Bubbles', + 3200: 'Go Fish', + 3300: 'Stop Fish', + 3400: 'Sea Horse', + 3500: 'Sea Shells', + 3600: 'Underwater', + 3700: 'Boots', + 3800: 'Cactus', + 3900: 'Cowboy Hat', + 10100: 'Cats', + 10200: 'Bats', + 11000: 'Snowflakes', + 11100: 'Hollyleaf', + 11200: 'Snowman', + 12000: 'ValenToons', + 12100: 'ValenToons', + 12200: 'ValenToons', + 12300: 'ValenToons', + 13000: 'Shamrock', + 13100: 'Shamrock', + 13200: 'Rainbow', + 13300: 'Shamrock'} +FlooringNames = {1000: 'Hardwood Floor', + 1010: 'Carpet', + 1020: 'Diamond Tile', + 1030: 'Diamond Tile', + 1040: 'Grass', + 1050: 'Beige Bricks', + 1060: 'Red Bricks', + 1070: 'Square Tile', + 1080: 'Stone', + 1090: 'Boardwalk', + 1100: 'Dirt', + 1110: 'Wood Tile', + 1120: 'Tile', + 1130: 'Honeycomb', + 1140: 'Water', + 1150: 'Beach Tile', + 1160: 'Beach Tile', + 1170: 'Beach Tile', + 1180: 'Beach Tile', + 1190: 'Sand', + 10000: 'Ice Cube', + 10010: 'Igloo', + 11000: 'Shamrock', + 11010: 'Shamrock'} +MouldingNames = {1000: 'Knotty', + 1010: 'Painted', + 1020: 'Dental', + 1030: 'Flowers', + 1040: 'Flowers', + 1050: 'Ladybug', + 1060: 'ValenToons', + 1070: 'Beach', + 1080: 'Winter Lights 1', + 1085: 'Winter Lights 2', + 1090: 'Winter Lights 3', + 1100: "ValenToon's Cupid", + 1110: "ValenToon's Heart 1", + 1120: "ValenToon's Heart 2"} +WainscotingNames = {1000: 'Painted', + 1010: 'Wood Panel', + 1020: 'Wood', + 1030: 'ValenToons', + 1040: 'Underwater'} +WindowViewNames = {10: 'Large Garden', + 20: 'Wild Garden', + 30: 'Greek Garden', + 40: 'Cityscape', + 50: 'Wild West', + 60: 'Under the Sea', + 70: 'Tropical Island', + 80: 'Starry Night', + 90: 'Tiki Pool', + 100: 'Frozen Frontier', + 110: 'Farm Country', + 120: 'Native Camp', + 130: 'Main Street'} +SpecialEventNames = {1: 'Generic Award', + 2: "Melville's Fishing Tournament", + 3: "Billy Budd's Fishing Tournament", + 4: 'Acorn Acres April Invitational', + 5: 'Acorn Acres C.U.P. Championship', + 6: 'Gift-Giving Extravaganza', + 7: "Top Toons New Year's Day Marathon", + 8: 'Perfect Trolley Games Weekend', + 9: 'Trolley Games Madness', + 10: 'Grand Prix Weekend', + 11: 'ToonTask Derby', + 12: 'Save a Building Marathon', + 13: 'Most Cogs Defeated', + 14: 'Most V.P.s Defeated', + 15: 'Operation Storm Sellbot Event', + 16: 'Most C.J.s Defeated', + 17: 'Operation Lawbots Lose Event', + 18: 'Most C.F.O.s Defeated', + 19: 'Operation Cashbot Chaos Event', + 20: 'Most C.E.O.s Defeated', + 21: 'Operation Besiege Bossbot Event'} +NewCatalogNotify = 'There are new items available to order at your phone!' +NewDeliveryNotify = 'A new delivery has just arrived at your mailbox!' +CatalogNotifyFirstCatalog = 'Your first cattlelog has arrived! You may use this to order new items for yourself or for your house.' +CatalogNotifyNewCatalog = 'Your cattlelog #%s has arrived! You can go to your phone to order items from this cattlelog.' +CatalogNotifyNewCatalogNewDelivery = 'A new delivery has arrived at your mailbox! Also, your cattlelog #%s has arrived!' +CatalogNotifyNewDelivery = 'A new delivery has arrived at your mailbox!' +CatalogNotifyNewCatalogOldDelivery = 'Your cattlelog #%s has arrived, and there are still items waiting in your mailbox!' +CatalogNotifyOldDelivery = 'There are still items waiting in your mailbox for you to pick up!' +CatalogNotifyInstructions = 'Click the "Go home" button on the map page in your Shticker Book, then walk up to the phone inside your house.' +CatalogNewDeliveryButton = 'New\nDelivery!' +CatalogNewCatalogButton = 'New\nCattlelog' +CatalogSaleItem = 'Sale! ' +DistributedMailboxEmpty = 'Your mailbox is empty right now. Come back here to look for deliveries after you place an order from your phone!' +DistributedMailboxWaiting = 'Your mailbox is empty right now, but the package you ordered is on its way. Check back later!' +DistributedMailboxReady = 'Your order has arrived!' +DistributedMailboxNotOwner = 'Sorry, this is not your mailbox.' +Clarabelle = 'Clarabelle' +MailboxExitButton = 'Close Mailbox' +MailboxAcceptButton = 'Take this item' +MailBoxDiscard = 'Discard this item' +MailboxAcceptInvite = 'Accept this invite' +MailBoxRejectInvite = 'Reject this invite' +MailBoxDiscardVerify = 'Are you sure you want to Discard %s?' +MailBoxRejectVerify = 'Are you sure you want to Reject %s?' +MailboxOneItem = 'Your mailbox contains 1 item.' +MailboxNumberOfItems = 'Your mailbox contains %s items.' +MailboxGettingItem = 'Taking %s from mailbox.' +MailboxGiftTag = 'Gift From: %s' +MailboxGiftTagAnonymous = 'Anonymous' +MailboxItemNext = 'Next\nItem' +MailboxItemPrev = 'Previous\nItem' +MailboxDiscard = 'Discard' +MailboxReject = 'Reject' +MailboxLeave = 'Keep' +CatalogCurrency = 'beans' +CatalogHangUp = 'Hang Up' +CatalogNew = 'NEW' +CatalogBackorder = 'BACKORDER' +CatalogSpecial = 'SPECIAL' +CatalogEmblem = 'EMBLEM' +CatalogPagePrefix = 'Page' +CatalogGreeting = "Hello! Thanks for calling Clarabelle's Cattlelog. Can I help you?" +CatalogGoodbyeList = ['Bye now!', + 'Call back soon!', + 'Thanks for calling!', + 'Ok, bye now!', + 'Bye!'] +CatalogHelpText1 = 'Turn the page to see items for sale.' +CatalogSeriesLabel = 'Series %s' +CatalogGiftError = 'Error' +CatalogGiftFor = 'Buy Gift for:' +CatalogGiftTo = 'To: %s' +CatalogGiftToggleOn = 'Stop Gifting' +CatalogGiftToggleOff = 'Buy Gifts' +CatalogGiftUpdating = 'Updating...' +CatalogGiftChoose = 'Choose a friend!' +CatalogPurchaseItemAvailable = 'Congratulations on your new purchase! You can start using it right away.' +CatalogPurchaseGiftItemAvailable = 'Excellent! %s can start using your gift right away.' +CatalogPurchaseItemOnOrder = 'Congratulations! Your purchase will be delivered to your mailbox soon.' +CatalogPurchaseGiftItemOnOrder = 'Excellent! Your gift to %s will be delivered to their mailbox.' +CatalogAnythingElse = 'Anything else I can get you today?' +CatalogPurchaseClosetFull = 'Your closet is full. You may purchase this item anyway, but if you do you will need to delete something from your closet to make room for it when it arrives.\n\nDo you still want to purchase this item?' +CatalogPurchaseTrunkFull = 'Your trunk is full. If you purchase this item, you\xe2\x80\x99ll need to delete another item from your trunk to make more room.\n\nDo you still want to purchase this item?' +CatalogAcceptClosetFull = 'Your closet is full. You must go inside and delete something from your closet to make room for this item before you can take it out of your mailbox.' +CatalogAcceptTrunkFull = 'Your trunk is full. You must delete something from your trunk before you can take this item out of your mailbox.' +CatalogAcceptShirt = 'You are now wearing your new hat. The hat you were wearing before has been moved to your trunk.' +CatalogAcceptShorts = 'You are now wearing your new shorts. What you were wearing before has been moved to your closet.' +CatalogAcceptSkirt = 'You are now wearing your new skirt. What you were wearing before has been moved to your closet.' +CatalogAcceptHat = 'You are now wearing your new hat. The hat you were wearing before has been moved to your trunk.' +CatalogAcceptGlasses = 'You are now wearing your new glasses. The glasses you were wearing before have been moved to your trunk.' +CatalogAcceptBackpack = 'You are now wearing your new backpack. The backpack you were wearing before has been moved to your trunk.' +CatalogAcceptShoes = 'You are now wearing your new shoes. The shoes you were wearing before have been moved to your trunk.' +CatalogAcceptPole = "You're now ready to go catch some bigger fish with your new pole!" +CatalogAcceptPoleUnneeded = 'You already have a better pole than this one!' +CatalogAcceptTank = "You're now ready to catch more fish!" +CatalogAcceptTankUnneeded = 'You already have a bigger tank than this one!' +CatalogAcceptChat = 'You now have a new SpeedChat!' +CatalogAcceptEmote = 'You now have a new Emotion!' +CatalogAcceptBeans = 'You received some jelly beans!' +CatalogAcceptRATBeans = 'Your Toon recruit reward has arrived!' +CatalogAcceptPartyRefund = "Your party was never started. Here's your refund!" +CatalogAcceptNametag = 'Your new name tag has arrived!' +CatalogAcceptGarden = 'Your garden supplies have arrived!' +CatalogAcceptPet = 'You now have a new Pet Trick!' +CatalogPurchaseHouseFull = 'Your house is full. You may purchase this item anyway, but if you do you will need to delete something from your house to make room for it when it arrives.\n\nDo you still want to purchase this item?' +CatalogAcceptHouseFull = 'Your house is full. You can not accept this item until you free up some room. Would you like to discard this item now?' +CatalogAcceptInAttic = 'Your new item is now in your attic. You can put it in your house by going inside and clicking on the "Move Furniture" button.' +CatalogAcceptInAtticP = 'Your new items are now in your attic. You can put them in your house by going inside and clicking on the "Move Furniture" button.' +CatalogPurchaseMailboxFull = "Your mailbox is full! You can't purchase this item until you take some items out of your mailbox to make room." +CatalogPurchaseGiftMailboxFull = "%s's mailbox is full! You can't purchase this item." +CatalogPurchaseOnOrderListFull = "You have too many items currently on order. You can't order any more items until some of the ones you have already ordered arrive." +CatalogPurchaseGiftOnOrderListFull = '%s has too many items currently on order.' +CatalogPurchaseGeneralError = 'The item could not be purchased because of some internal game error: error code %s.' +CatalogPurchaseGiftGeneralError = 'The item could not be gifted to %(friend)s because of some internal game error: error code %(error)s.' +CatalogPurchaseGiftNotAGift = 'This item could not be sent to %s because it would be an unfair advantage.' +CatalogPurchaseGiftWillNotFit = "This item could not be sent to %s because it doesn't fit them." +CatalogPurchaseGiftLimitReached = "This item could not be sent to %s because they already have it." +CatalogPurchaseGiftNotEnoughMoney = "This item could not be sent to %s because you can't afford it." +CatalogPurchaseGiftTooFast = "This item could not be sent to %s because you are sending gifts too fast." +CatalogAcceptGeneralError = 'The item could not be removed from your mailbox because of some internal game error: error code %s.' +CatalogAcceptRoomError = "You don't have any place to put this. You'll have to get rid of something." +CatalogAcceptLimitError = "You already have as many of these as you can handle. You'll have to get rid of something." +CatalogAcceptFitError = "This won't fit you!" +CatalogAcceptInvalidError = 'This item has gone out of style!' +CatalogAcceptClosetError = 'You already have a bigger closet!' +MailboxOverflowButtonDicard = 'Discard' +MailboxOverflowButtonLeave = 'Leave' +HDMoveFurnitureButton = 'Move\nFurniture' +HDStopMoveFurnitureButton = 'Done\nMoving' +HDAtticPickerLabel = 'In the attic' +HDInRoomPickerLabel = 'In the room' +HDInTrashPickerLabel = 'In the trash' +HDDeletePickerLabel = 'Delete?' +HDInAtticLabel = 'Attic' +HDInRoomLabel = 'Room' +HDInTrashLabel = 'Trash' +HDToAtticLabel = 'Send\nto attic' +HDMoveLabel = 'Move' +HDRotateCWLabel = 'Rotate Right' +HDRotateCCWLabel = 'Rotate Left' +HDReturnVerify = 'Return this item to the attic?' +HDReturnFromTrashVerify = 'Return this item to the attic from the trash?' +HDDeleteItem = 'Click OK to send this item to the trash, or Cancel to keep it.' +HDNonDeletableItem = "You can't delete items of this type!" +HDNonDeletableBank = "You can't delete your bank!" +HDNonDeletableCloset = "You can't delete your wardrobe!" +HDNonDeletablePhone = "You can't delete your phone!" +HDNonDeletableTrunk = "You can't delete your trunk!" +HDNonDeletableNotOwner = "You can't delete %s's things!" +HDHouseFull = 'Your house is full. You have to delete something else from your house or attic before you can return this item from the trash.' +HDHelpDict = {'DoneMoving': 'Finish room decorating.', + 'Attic': 'Show list of items in attic. The attic stores items that are not in your room.', + 'Room': 'Show list of items in room. Useful for finding lost items.', + 'Trash': 'Show items in trash. Oldest items are deleted after a while or when trash overflows.', + 'ZoomIn': 'Get a closer view of room.', + 'ZoomOut': 'Get a farther view of room.', + 'SendToAttic': 'Send the current furniture item to attic for storage.', + 'RotateLeft': 'Turn left.', + 'RotateRight': 'Turn right.', + 'DeleteEnter': 'Change to delete mode.', + 'DeleteExit': 'Exit delete mode.', + 'FurnitureItemPanelDelete': 'Send %s to trash.', + 'FurnitureItemPanelAttic': 'Place %s in room.', + 'FurnitureItemPanelRoom': 'Return %s to attic.', + 'FurnitureItemPanelTrash': 'Return %s to attic.'} +MessagePickerTitle = 'You have too many phrases. In order to purchase\n"%s"\n you must choose one to remove:' +MessagePickerCancel = lCancel +MessageConfirmDelete = 'Are you sure you want to remove "%s" from your SpeedChat menu?' +CatalogBuyText = 'Buy' +CatalogRentText = 'Rent' +CatalogGiftText = 'Gift' +CatalogOnOrderText = 'On Order' +CatalogPurchasedText = 'Already\nPurchased' +CatalogGiftedText = 'Gifted\nTo You' +CatalogPurchasedGiftText = 'Already\nOwned' +CatalogMailboxFull = 'No Room' +CatalogNotAGift = 'Not a Gift' +CatalogNoFit = "Doesn't\nFit" +CatalogSndOnText = 'Snd On' +CatalogSndOffText = 'Snd Off' +CatalogPurchasedMaxText = 'Already\nPurchased Max' +CatalogVerifyPurchase = 'Purchase %(item)s for %(price)s Jellybeans?' +CatalogVerifyPurchaseBeanSilverGold = 'Purchase %(item)s for %(price)s Jellybeans, %(silver)s silver emblems and %(gold)s gold emblems?' +CatalogVerifyPurchaseBeanGold = 'Purchase %(item)s for %(price)s Jellybeans and %(gold)s gold emblems?' +CatalogVerifyPurchaseBeanSilver = 'Purchase %(item)s for %(price)s Jellybeans and %(silver)s silver emblems?' +CatalogVerifyPurchaseSilverGold = 'Purchase %(item)s for %(silver)s silver emblems and %(gold)s gold emblems?' +CatalogVerifyPurchaseSilver = 'Purchase %(item)s for %(silver)s silver emblems?' +CatalogVerifyPurchaseGold = 'Purchase %(item)s for %(gold)s gold emblems?' +CatalogVerifyRent = 'Rent %(item)s for %(price)s Jellybeans?' +CatalogVerifyGift = 'Purchase %(item)s for %(price)s Jellybeans as a gift for %(friend)s?' +CatalogOnlyOnePurchase = 'You may only have one of these items at a time. If you purchase this one, it will replace %(old)s.\n\nAre you sure you want to purchase %(item)s for %(price)s Jellybeans?' +CatalogExitButtonText = 'Hang Up' +CatalogPastButtonText = 'To Past Items' +TutorialHQOfficerName = 'HQ Harry' +NPCToonNames = {20000: 'Tutorial Tom', + 999: 'Toon Tailor', + 1000: lToonHQ, + 20001: Flippy, + 2001: Flippy, + 2002: 'Banker Bob', + 2003: 'Professor Pete', + 2004: 'Tammy the Tailor', + 2005: 'Librarian Larry', + 2006: 'Clerk Clark', + 2011: 'Clerk Clara', + 2007: lHQOfficerM, + 2008: lHQOfficerM, + 2009: lHQOfficerF, + 2010: lHQOfficerF, + 2012: 'Fisherman Freddy', + 2013: 'Clerk Poppy', + 2014: 'Clerk Peppy', + 2015: 'Clerk Pappy', + 2016: 'Party Planner Pumpkin', + 2017: 'Party Planner Polly', + 2018: 'Doctor Surlee', + 2019: 'Doctor Dimm', + 2020: 'Professor Prepostera', + 2021: 'Painter Eddy', + 2101: 'Dentist Daniel', + 2102: 'Sheriff Sherry', + 2103: 'Sneezy Kitty', + 2104: lHQOfficerM, + 2105: lHQOfficerM, + 2106: lHQOfficerF, + 2107: lHQOfficerF, + 2108: 'Canary Coalmine', + 2109: 'Sir Babbles A Lot', + 2110: 'Bill Board', + 2111: 'Dancing Diego', + 2112: 'Dr. Tom', + 2113: 'Rollo The Amazing', + 2114: 'Roz Berry', + 2115: 'Patty Papercut', + 2116: 'Bruiser McDougal', + 2117: 'Ma Putrid', + 2118: 'Jesse Jester', + 2119: 'Honey Haha', + 2120: 'Professor Binky', + 2121: 'Madam Chuckle', + 2122: 'Harry Ape', + 2123: 'Spamonia Biggles', + 2124: 'T.P. Rolle', + 2125: 'Lazy Hal', + 2126: 'Professor Guffaw', + 2127: 'Woody Nickel', + 2128: 'Loony Louis', + 2129: 'Frank Furter', + 2130: 'Joy Buzzer', + 2131: 'Feather Duster', + 2132: 'Daffy Don', + 2133: 'Dr. Euphoric', + 2134: 'Silent Simone', + 2135: 'Mary', + 2136: 'Sal Snicker', + 2137: 'Happy Heikyung', + 2138: 'Muldoon', + 2139: 'Dan Dribbles', + 2140: 'Fisherman Billy', + 2201: 'Postmaster Pete', + 2202: 'Shirley U. Jest', + 2203: lHQOfficerM, + 2204: lHQOfficerM, + 2205: lHQOfficerF, + 2206: lHQOfficerF, + 2207: 'Will Wiseacre', + 2208: 'Sticky Lou', + 2209: 'Charlie Chortle', + 2210: 'Tee Hee', + 2211: 'Sally Spittake', + 2212: 'Weird Warren', + 2213: 'Lucy Tires', + 2214: 'Sam Stain', + 2215: 'Sid Seltzer', + 2216: 'Nona Seeya', + 2217: 'Sharky Jones', + 2218: 'Fanny Pages', + 2219: 'Chef Knucklehead', + 2220: 'Rick Rockhead', + 2221: 'Clovinia Cling', + 2222: 'Shorty Fuse', + 2223: 'Sasha Sidesplitter', + 2224: 'Smokey Joe', + 2225: 'Fisherman Droopy', + 2301: 'Dr. Pulyurleg', + 2302: 'Professor Wiggle', + 2303: 'Nurse Nancy', + 2304: lHQOfficerM, + 2305: lHQOfficerM, + 2306: lHQOfficerF, + 2307: lHQOfficerF, + 2308: 'Nancy Gas', + 2309: 'Big Bruce', + 2311: 'Franz Neckvein', + 2312: 'Dr. Sensitive', + 2313: 'Lucy Shirtspot', + 2314: 'Ned Slinger', + 2315: 'Chewy Morsel', + 2316: 'Cindy Sprinkles', + 2318: 'Tony Maroni', + 2319: 'Zippy', + 2320: 'Crunchy Alfredo', + 2321: 'Fisherman Punchy', + 1001: 'Clerk Will', + 1002: 'Clerk Bill', + 1003: lHQOfficerM, + 1004: lHQOfficerF, + 1005: lHQOfficerM, + 1006: lHQOfficerF, + 1007: 'Longjohn Leroy', + 1008: 'Fisherman Furball', + 1009: 'Clerk Barky', + 1010: 'Clerk Purr', + 1011: 'Clerk Bloop', + 1012: 'Party Planner Pickles', + 1013: 'Party Planner Patty', + 1101: 'Billy Budd', + 1102: 'Captain Carl', + 1103: 'Fishy Frank', + 1104: 'Doctor Squall', + 1105: 'Admiral Hook', + 1106: 'Mrs. Starch', + 1107: 'Cal Estenicks', + 1108: lHQOfficerM, + 1109: lHQOfficerF, + 1110: lHQOfficerM, + 1111: lHQOfficerF, + 1112: 'Gary Glubglub', + 1113: 'Lisa Luff', + 1114: 'Charlie Chum', + 1115: 'Sheila Squid, Atty', + 1116: 'Barnacle Bessie', + 1117: 'Captain Yucks', + 1118: 'Choppy McDougal', + 1121: 'Linda Landlubber', + 1122: 'Salty Stan', + 1123: 'Electra Eel', + 1124: 'Flappy Docksplinter', + 1125: 'Eileen Overboard', + 1126: 'Fisherman Barney', + 1201: 'Barnacle Barbara', + 1202: 'Art', + 1203: 'Ahab', + 1204: 'Rocky Shores', + 1205: lHQOfficerM, + 1206: lHQOfficerF, + 1207: lHQOfficerM, + 1208: lHQOfficerF, + 1209: 'Professor Plank', + 1210: 'Gang Wei', + 1211: 'Wynn Bag', + 1212: 'Toby Tonguestinger', + 1213: 'Dante Dolphin', + 1214: 'Gusty Kate', + 1215: 'Dinah Down', + 1216: 'Rod Reel', + 1217: 'CC Weed', + 1218: 'Pacific Tim', + 1219: 'Brian Beachead', + 1220: 'Carla Canal', + 1221: 'Blisters McKee', + 1222: 'Shep Ahoy', + 1223: 'Sid Squid', + 1224: 'Emily Eel', + 1225: 'Bonzo Bilgepump', + 1226: 'Heave Ho', + 1227: 'Coral Reef', + 1228: 'Fisherman Reed', + 1301: 'Alice', + 1302: 'Melville', + 1303: 'Claggart', + 1304: 'Svetlana', + 1305: lHQOfficerM, + 1306: lHQOfficerF, + 1307: lHQOfficerM, + 1308: lHQOfficerF, + 1309: 'Seafoam', + 1310: 'Ted Tackle', + 1311: 'Topsy Turvey', + 1312: 'Ethan Keel', + 1313: 'William Wake', + 1314: 'Rusty Ralph', + 1315: 'Doctor Drift', + 1316: 'Wilma Wobble', + 1317: 'Paula Pylon', + 1318: 'Dinghy Dan', + 1319: 'Davey Drydock', + 1320: 'Ted Calm', + 1321: 'Dinah Docker', + 1322: 'Whoopie Cushion', + 1323: 'Stinky Ned', + 1324: 'Pearl Diver', + 1325: 'Ned Setter', + 1326: 'Felicia Chips', + 1327: 'Cindy Splat', + 1328: 'Fred Flounder', + 1329: 'Shelly Seaweed', + 1330: 'Porter Hole', + 1331: 'Rudy Rudder', + 1332: 'Fisherman Shane', + 3001: 'Betty Freezes', + 3002: lHQOfficerM, + 3003: lHQOfficerF, + 3004: lHQOfficerM, + 3005: lHQOfficerM, + 3006: 'Clerk Lenny', + 3007: 'Clerk Penny', + 3008: 'Warren Bundles', + 3009: 'Fisherman Frizzy', + 3010: 'Clerk Skip', + 3011: 'Clerk Dip', + 3012: 'Clerk Kipp', + 3013: 'Party Planner Pete', + 3014: 'Party Planner Penny', + 3101: 'Mr. Cow', + 3102: 'Auntie Freeze', + 3103: 'Fred', + 3104: 'Bonnie', + 3105: 'Frosty Freddy', + 3106: 'Gus Gooseburger', + 3107: 'Patty Passport', + 3108: 'Toboggan Ted', + 3109: 'Kate', + 3110: 'Chicken Boy', + 3111: 'Snooty Sinjin', + 3112: 'Lil Oldman', + 3113: 'Hysterical Harry', + 3114: 'Henry the Hazard', + 3115: lHQOfficerM, + 3116: lHQOfficerF, + 3117: lHQOfficerM, + 3118: lHQOfficerM, + 3119: 'Creepy Carl', + 3120: 'Mike Mittens', + 3121: 'Joe Shockit', + 3122: 'Lucy Luge', + 3123: 'Frank Lloyd Ice', + 3124: 'Lance Iceberg', + 3125: 'Colonel Crunchmouth', + 3126: 'Colestra Awl', + 3127: 'Ifalla Yufalla', + 3128: 'Sticky George', + 3129: 'Baker Bridget', + 3130: 'Sandy', + 3131: 'Lazy Lorenzo', + 3132: 'Ashy', + 3133: 'Dr. Friezeframe', + 3134: 'Lounge Lassard', + 3135: 'Soggy Nell', + 3136: 'Happy Sue', + 3137: 'Mr. Freeze', + 3138: 'Chef Bumblesoup', + 3139: 'Granny Icestockings', + 3140: 'Fisherman Lucille', + 3201: 'Aunt Arctic', + 3202: 'Shakey', + 3203: 'Walt', + 3204: 'Dr. Ivanna Cee', + 3205: 'Bumpy Noggin', + 3206: 'Vidalia VaVoom', + 3207: 'Dr. Mumbleface', + 3208: 'Grumpy Phil', + 3209: 'Giggles McGhee', + 3210: 'Simian Sam', + 3211: 'Fanny Freezes', + 3212: 'Frosty Fred', + 3213: lHQOfficerM, + 3214: lHQOfficerF, + 3215: lHQOfficerM, + 3216: lHQOfficerM, + 3217: 'Sweaty Pete', + 3218: 'Blue Lou', + 3219: 'Tom Tandemfrost', + 3220: 'Mr. Sneeze', + 3221: 'Nelly Snow', + 3222: 'Mindy Windburn', + 3223: 'Chappy', + 3224: 'Freida Frostbite', + 3225: 'Blake Ice', + 3226: 'Santa Paws', + 3227: 'Solar Ray', + 3228: 'Wynne Chill', + 3229: 'Hernia Belt', + 3230: 'Balding Benjy', + 3231: 'Choppy', + 3232: 'Fisherman Albert', + 3301: 'Paisley Patches', + 3302: 'Bjorn Bord', + 3303: 'Dr. Peepers', + 3304: 'Eddie the Yeti', + 3305: 'Mack Ramay', + 3306: 'Paula Behr', + 3307: 'Fisherman Fredrica', + 3308: 'Donald Frump', + 3309: 'Bootsy', + 3310: 'Professor Flake', + 3311: 'Connie Ferris', + 3312: 'March Harry', + 3313: lHQOfficerM, + 3314: lHQOfficerF, + 3315: lHQOfficerM, + 3316: lHQOfficerF, + 3317: 'Kissy Krissy', + 3318: 'Johnny Cashmere', + 3319: 'Sam Stetson', + 3320: 'Fizzy Lizzy', + 3321: 'Pickaxe Paul', + 3322: 'Flue Lou', + 3323: 'Dallas Borealis', + 3324: 'Snaggletooth Stu', + 3325: 'Groovy Garland', + 3326: 'Blanche', + 3327: 'Chuck Roast', + 3328: 'Shady Sadie', + 3329: 'Treading Ed', + 4001: 'Molly Molloy', + 4002: lHQOfficerM, + 4003: lHQOfficerF, + 4004: lHQOfficerF, + 4005: lHQOfficerF, + 4006: 'Clerk Doe', + 4007: 'Clerk Ray', + 4008: 'Tailor Harmony', + 4009: 'Fisherman Fanny', + 4010: 'Clerk Chris', + 4011: 'Clerk Neil', + 4012: 'Clerk Westin Girl', + 4013: 'Party Planner Preston', + 4014: 'Party Planner Penelope', + 4101: 'Tom', + 4102: 'Fifi', + 4103: 'Dr. Fret', + 4104: lHQOfficerM, + 4105: lHQOfficerF, + 4106: lHQOfficerF, + 4107: lHQOfficerF, + 4108: 'Cleff', + 4109: 'Carlos', + 4110: 'Metra Gnome', + 4111: 'Tom Hum', + 4112: 'Fa', + 4113: 'Madam Manners', + 4114: 'Offkey Eric', + 4115: 'Barbara Seville', + 4116: 'Piccolo', + 4117: 'Mandy Lynn', + 4118: 'Attendant Abe', + 4119: 'Moe Zart', + 4120: 'Viola Padding', + 4121: 'Gee Minor', + 4122: 'Minty Bass', + 4123: 'Lightning Ted', + 4124: 'Riff Raff', + 4125: 'Melody Wavers', + 4126: 'Mel Canto', + 4127: 'Happy Feet', + 4128: 'Luciano Scoop', + 4129: 'Tootie Twostep', + 4130: 'Metal Mike', + 4131: 'Abraham Armoire', + 4132: 'Lowdown Sally', + 4133: 'Scott Poplin', + 4134: 'Disco Dave', + 4135: 'Sluggo Songbird', + 4136: 'Patty Pause', + 4137: 'Tony Deff', + 4138: 'Cliff Cleff', + 4139: 'Harmony Swell', + 4140: 'Clumsy Ned', + 4141: 'Fisherman Jed', + 4201: 'Tina', + 4202: 'Barry', + 4203: 'Lumber Jack', + 4204: lHQOfficerM, + 4205: lHQOfficerF, + 4206: lHQOfficerF, + 4207: lHQOfficerF, + 4208: 'Hedy', + 4209: 'Corny Canter', + 4211: 'Carl Concerto', + 4212: 'Detective Dirge', + 4213: 'Fran Foley', + 4214: 'Tina Toehooks', + 4215: 'Tim Tailgater', + 4216: 'Gummy Whistle', + 4217: 'Handsome Anton', + 4218: 'Wilma Wind', + 4219: 'Sid Sonata', + 4220: 'Curtis Finger', + 4221: 'Moe Madrigal', + 4222: 'John Doe', + 4223: 'Penny Prompter', + 4224: 'Jungle Jim', + 4225: 'Holly Hiss', + 4226: 'Thelma Throatreacher', + 4227: 'Quiet Francesca', + 4228: 'August Winds', + 4229: 'June Loon', + 4230: 'Julius Wheezer', + 4231: 'Steffi Squeezebox', + 4232: 'Hedly Hymn', + 4233: 'Charlie Carp', + 4234: 'Leed Guitar', + 4235: 'Fisherman Larry', + 4301: 'Yuki', + 4302: 'Anna', + 4303: 'Leo', + 4304: lHQOfficerM, + 4305: lHQOfficerF, + 4306: lHQOfficerF, + 4307: lHQOfficerF, + 4308: 'Tabitha', + 4309: 'Marshall', + 4310: 'Martha Mopp', + 4311: 'Sea Shanty', + 4312: 'Moe Saj', + 4313: 'Dumb Dolph', + 4314: 'Dana Dander', + 4315: 'Karen Clockwork', + 4316: 'Tim Tango', + 4317: 'Stubby Toe', + 4318: 'Bob Marlin', + 4319: 'Rinky Dink', + 4320: 'Cammy Coda', + 4321: 'Luke Lute', + 4322: 'Randy Rythm', + 4323: 'Hanna Hogg', + 4324: 'Ellie', + 4325: 'Banker Bran', + 4326: 'Fran Fret', + 4327: 'Flim Flam', + 4328: 'Wagner', + 4329: 'Telly Prompter', + 4330: 'Quentin', + 4331: 'Mellow Costello', + 4332: 'Ziggy', + 4333: 'Harry', + 4334: 'Fast Freddie', + 4335: 'Fisherman Walden', + 5001: lHQOfficerM, + 5002: lHQOfficerM, + 5003: lHQOfficerF, + 5004: lHQOfficerF, + 5005: 'Clerk Peaches', + 5006: 'Clerk Herb', + 5007: 'Bonnie Blossom', + 5008: 'Fisherman Flora', + 5009: 'Clerk Bo Tanny', + 5010: 'Clerk Tom A. Dough', + 5011: 'Clerk Doug Wood', + 5012: 'Party Planner Pierce', + 5013: 'Party Planner Peggy', + 5101: 'Artie', + 5102: 'Susan', + 5103: 'Bud', + 5104: 'Flutterby', + 5105: 'Jack', + 5106: 'Barber Bjorn', + 5107: 'Postman Felipe', + 5108: 'Innkeeper Janet', + 5109: lHQOfficerM, + 5110: lHQOfficerM, + 5111: lHQOfficerF, + 5112: lHQOfficerF, + 5113: 'Dr. Spud', + 5114: 'Wilt', + 5115: 'Honey Dew', + 5116: 'Vegetable Vern', + 5117: 'Petal', + 5118: 'Pop Corn', + 5119: 'Barry Medly', + 5120: 'Gopher', + 5121: 'Paula Peapod', + 5122: 'Leif Pyle', + 5123: 'Diane Vine', + 5124: 'Soggy Bottom', + 5125: 'Sanjay Splash', + 5126: 'Madam Mum', + 5127: 'Polly Pollen', + 5128: 'Shoshanna Sap', + 5129: 'Fisherman Sally', + 5201: 'Jake', + 5202: 'Cynthia', + 5203: 'Lisa', + 5204: 'Bert', + 5205: 'Dan D. Lion', + 5206: 'Vine Green', + 5207: 'Sofie Squirt', + 5208: 'Samantha Spade', + 5209: lHQOfficerM, + 5210: lHQOfficerM, + 5211: lHQOfficerF, + 5212: lHQOfficerF, + 5213: 'Big Galoot', + 5214: 'Itchie Bumps', + 5215: 'Tammy Tuber', + 5216: 'Stinky Jim', + 5217: 'Greg Greenethumb', + 5218: 'Rocky Raspberry', + 5219: 'Lars Bicep', + 5220: 'Lacy Underalls', + 5221: 'Pink Flamingo', + 5222: 'Whiny Wilma', + 5223: 'Wet Will', + 5224: 'Uncle Bumpkin', + 5225: 'Pamela Puddle', + 5226: 'Pete Moss', + 5227: 'Begonia Biddlesmore', + 5228: 'Digger Mudhands', + 5229: 'Fisherman Lily', + 5301: lHQOfficerM, + 5302: lHQOfficerM, + 5303: lHQOfficerM, + 5304: lHQOfficerM, + 5305: 'Crystal', + 5306: 'S. Cargo', + 5307: 'Fun Gus', + 5308: 'Naggy Nell', + 5309: 'Ro Maine', + 5310: 'Timothy', + 5311: 'Judge McIntosh', + 5312: 'Eugene', + 5313: 'Coach Zucchini', + 5314: 'Aunt Hill', + 5315: 'Uncle Mud', + 5316: 'Uncle Spud', + 5317: 'Detective Lima', + 5318: 'Caesar', + 5319: 'Rose', + 5320: 'April', + 5321: 'Professor Ivy', + 5322: 'Fisherman Rose', + 6000: 'Fisherman Melville', + 8001: 'Graham Pree', + 8002: 'Ivona Race', + 8003: 'Anita Winn', + 8004: 'Phil Errup', + 9001: "Snoozin' Susan", + 9002: 'Sleeping Tom', + 9003: 'Drowsy Dennis', + 9004: lHQOfficerF, + 9005: lHQOfficerF, + 9006: lHQOfficerM, + 9007: lHQOfficerM, + 9008: 'Clerk Jill', + 9009: 'Clerk Phil', + 9010: 'Worn Out Waylon', + 9011: 'Fisherman Freud', + 9012: 'Clerk Sarah Snuze', + 9013: 'Clerk Kat Knap', + 9014: 'Clerk R. V. Winkle', + 9015: 'Party Planner Pebbles', + 9016: 'Party Planner Pearl', + 9101: 'Ed', + 9102: 'Big Mama', + 9103: 'P.J.', + 9104: 'Sweet Slumber', + 9105: 'Professor Yawn', + 9106: 'Max', + 9107: 'Snuggles', + 9108: 'Winky Wilbur', + 9109: 'Dreamy Daphne', + 9110: 'Kathy Nip', + 9111: 'Powers Erge', + 9112: 'Lullaby Lou', + 9113: 'Jacques Clock', + 9114: 'Smudgy Mascara', + 9115: 'Babyface MacDougal', + 9116: 'Dances with Sheep', + 9117: 'Afta Hours', + 9118: 'Starry Knight', + 9119: 'Rocco', + 9120: 'Sarah Slumber', + 9121: 'Serena Shortsheeter', + 9122: 'Puffy Ayes', + 9123: 'Teddy Blair', + 9124: 'Nina Nitelight', + 9125: 'Dr. Bleary', + 9126: 'Wyda Wake', + 9127: 'Tabby Tucker', + 9128: "Hardy O'Toole", + 9129: 'Bertha Bedhog', + 9130: 'Charlie Chamberpot', + 9131: 'Susan Siesta', + 9132: lHQOfficerF, + 9133: lHQOfficerF, + 9134: lHQOfficerF, + 9135: lHQOfficerF, + 9136: 'Fisherman Taylor', + 9201: 'Bernie', + 9202: 'Orville', + 9203: 'Nat', + 9204: 'Claire de Loon', + 9205: 'Zen Glen', + 9206: 'Skinny Ginny', + 9207: 'Jane Drain', + 9208: 'Drowsy Dave', + 9209: 'Dr. Floss', + 9210: 'Master Mike', + 9211: 'Dawn', + 9212: 'Moonbeam', + 9213: 'Rooster Rick', + 9214: 'Dr. Blinky', + 9215: 'Rip', + 9216: 'Cat', + 9217: 'Lawful Linda', + 9218: 'Waltzing Matilda', + 9219: 'The Countess', + 9220: 'Grumpy Gordon', + 9221: 'Zari', + 9222: 'Cowboy George', + 9223: 'Mark the Lark', + 9224: 'Sandy Sandman', + 9225: 'Fidgety Bridget', + 9226: 'William Teller', + 9227: 'Bed Head Ted', + 9228: 'Whispering Willow', + 9229: 'Rose Petals', + 9230: 'Tex', + 9231: 'Harry Hammock', + 9232: 'Honey Moon', + 9233: lHQOfficerM, + 9234: lHQOfficerM, + 9235: lHQOfficerM, + 9236: lHQOfficerM, + 9237: 'Fisherman Jung', + 9301: 'Fisherman John', + 9302: 'Blithesome Barbra', + 9303: 'Hasty John', + 9304: 'Angry Dan', + 9305: lHQOfficerM, + 9306: lHQOfficerM, + 9307: lHQOfficerF, + 9308: lHQOfficerF, + 9309: 'Ori-O', + 9310: 'Mr. Batty', + 9311: 'Doctor Flippenbrains', + 9312: 'Barden Betty', + 9313: 'Frekly Fred', + 9314: 'Robby', + 9315: 'Baker Penelope', + 9316: 'Angie Lerr', + 9317: 'Nocturnal Nattie', + 9318: 'Los Carlos', + 9319: 'Psyche', + 9320: 'Toony Bob', + 9321: 'Sir Biscuit', + 9322: 'Glower', + 9323: 'Zedd', + 9324: 'Nacib', + 7001: 'N. Prisoned', + 7002: 'R.E. Leaseme', + 7003: 'Lemmy Owte', + 7004: 'T. Rapped', + 7005: 'Little Helphere', + 7006: 'Gimmy Ahand', + 7007: 'Dewin Tymme', + 7008: 'Ima Cagedtoon', + 7009: 'Jimmy Thelock', + 7010: 'Jaymo', + 7011: 'Donald', + 7012: 'Phil Bettur', + 7013: 'Emma Phatic', + 7014: 'GiggleMesh', + 7015: 'Anne Ville', + 7016: 'Bud Erfingerz', + 7017: 'J.S. Bark', + 7018: 'Bea Sharpe', + 7019: 'Otto Toon', + 7020: 'Al Capella', + 7021: 'Des Traction', + 7022: 'Dee Version', + 7023: 'Bo Nanapeel', + 10001: 'Healer Sara', + 10002: "Good ol' Gil Giggles", + 11001: 'Healer Gabriel', + 12001: 'Healer Bill', + 12002: 'Mata Hairy', + 13001: 'Healer Clover', + 13002: 'Bumpy Bumblebehr'} +zone2TitleDict = {2513: ('Toon Hall', ''), + 2514: ('Toontown Bank', ''), + 2516: ('Toontown School House', ''), + 2518: ('Toontown Library', ''), + 2519: ('Gag Shop', ''), + 2520: (lToonHQ, ''), + 2521: ('Clothing Shop', ''), + 2522: ('Pet Shop', ''), + 2601: ('All Smiles Tooth Repair', ''), + 2602: ('', ''), + 2603: ('One-Liner Miners', ''), + 2604: ('Hogwash & Dry', ''), + 2605: ('Toontown Sign Factory', ''), + 2606: ('', ''), + 2607: ('Jumping Beans', ''), + 2610: ('Dr. Tom Foolery', ''), + 2611: ('', ''), + 2616: ("Weird Beard's Disguise Shop", ''), + 2617: ('Silly Stunts', ''), + 2618: ('All That Razz', ''), + 2621: ('Paper Airplanes', ''), + 2624: ('Happy Hooligans', ''), + 2625: ('House of Bad Pies', ''), + 2626: ("Jesse's Joke Repair", ''), + 2629: ("The Laughin' Place", ''), + 2632: ('Clown Class', ''), + 2633: ('Tee-Hee Tea Shop', ''), + 2638: ('Toontown Playhouse', ''), + 2639: ('Monkey Tricks', ''), + 2643: ('Canned Bottles', ''), + 2644: ('Impractical Jokes', ''), + 2649: ('All Fun and Games Shop', ''), + 2652: ('', ''), + 2653: ('', ''), + 2654: ('Laughing Lessons', ''), + 2655: ('Funny Money Savings & Loan', ''), + 2656: ('Used Clown Cars', ''), + 2657: ("Frank's Pranks", ''), + 2659: ('Joy Buzzers to the World', ''), + 2660: ('Tickle Machines', ''), + 2661: ('Daffy Taffy', ''), + 2662: ('Dr. I.M. Euphoric', ''), + 2663: ('Toontown Cinerama', ''), + 2664: ('The Merry Mimes', ''), + 2665: ("Mary's Go Around Travel Company", ''), + 2666: ('Laughing Gas Station', ''), + 2667: ('Happy Times', ''), + 2669: ("Muldoon's Maroon Balloons", ''), + 2670: ('Soup Forks', ''), + 2671: ('', ''), + 2701: ('', ''), + 2704: ('Movie Multiplex', ''), + 2705: ("Wiseacre's Noisemakers", ''), + 2708: ('Blue Glue', ''), + 2711: ('Toontown Post Office', ''), + 2712: ('Chortle Cafe', ''), + 2713: ('Laughter Hours Cafe', ''), + 2714: ('Kooky CinePlex', ''), + 2716: ('Soup and Crack Ups', ''), + 2717: ('Bottled Cans', ''), + 2720: ('Crack Up Auto Repair', ''), + 2725: ('', ''), + 2727: ('Seltzer Bottles and Cans', ''), + 2728: ('Vanishing Cream', ''), + 2729: ('14 Karat Goldfish', ''), + 2730: ('News for the Amused', ''), + 2731: ('', ''), + 2732: ('Spaghetti and Goofballs', ''), + 2733: ('Cast Iron Kites', ''), + 2734: ('Suction Cups and Saucers', ''), + 2735: ('The Kaboomery', ''), + 2739: ("Sidesplitter's Mending", ''), + 2740: ('Used Firecrackers', ''), + 2741: ('', ''), + 2742: ('', ''), + 2743: ('Ragtime Dry Cleaners', ''), + 2744: ('', ''), + 2747: ('Visible Ink', ''), + 2748: ('Jest for Laughs', ''), + 2801: ('Sofa Whoopee Cushions', ''), + 2802: ('Inflatable Wrecking Balls', ''), + 2803: ('The Karnival Kid', ''), + 2804: ('Dr. Pulyurleg, Chiropractor', ''), + 2805: ('', ''), + 2809: ('The Punch Line Gym', ''), + 2814: ('Toontown Theatre', ''), + 2818: ('The Flying Pie', ''), + 2821: ('', ''), + 2822: ('Rubber Chicken Sandwiches', ''), + 2823: ('Sundae Funnies Ice Cream', ''), + 2824: ('Punchline Movie Palace', ''), + 2829: ('Phony Baloney', ''), + 2830: ("Zippy's Zingers", ''), + 2831: ("Professor Wiggle's House of Giggles", ''), + 2832: ('', ''), + 2833: ('', ''), + 2834: ('Funny Bone Emergency Room', ''), + 2836: ('', ''), + 2837: ('Hardy Harr Seminars', ''), + 2839: ('Barely Palatable Pasta', ''), + 2841: ('', ''), + 1506: ('Gag Shop', ''), + 1507: ('Toon Headquarters', ''), + 1508: ('Clothing Shop', ''), + 1510: ('', ''), + 1602: ('Used Life Preservers', ''), + 1604: ('Wet Suit Dry Cleaners', ''), + 1606: ("Hook's Clock Repair", ''), + 1608: ("Luff 'N Stuff", ''), + 1609: ('Every Little Bait', ''), + 1612: ('Dime & Quarterdeck Bank', ''), + 1613: ('Squid Pro Quo, Attorneys at Law', ''), + 1614: ('Trim the Nail Boutique', ''), + 1615: ("Yacht's All, Folks!", ''), + 1616: ("Blackbeard's Beauty Parlor", ''), + 1617: ('Out to See Optics', ''), + 1619: ('Disembark! Tree Surgeons', ''), + 1620: ('From Fore to Aft', ''), + 1621: ('Poop Deck Gym', ''), + 1622: ('Bait and Switches Electrical Shop', ''), + 1624: ('Soles Repaired While U Wait', ''), + 1626: ('Salmon Chanted Evening Formal Wear', ''), + 1627: ("Billy Budd's Big Bargain Binnacle Barn", ''), + 1628: ('Piano Tuna', ''), + 1629: ('', ''), + 1701: ('Buoys and Gulls Nursery School', ''), + 1703: ('Wok the Plank Chinese Food', ''), + 1705: ('Sails for Sale', ''), + 1706: ('Peanut Butter and Jellyfish', ''), + 1707: ('Gifts With a Porpoise', ''), + 1709: ('Windjammers and Jellies', ''), + 1710: ('Barnacle Bargains', ''), + 1711: ('Deep Sea Diner', ''), + 1712: ('Able-Bodied Gym', ''), + 1713: ("Art's Smart Chart Mart", ''), + 1714: ("Reel 'Em Inn", ''), + 1716: ('Mermaid Swimwear', ''), + 1717: ('Be More Pacific Ocean Notions', ''), + 1718: ('Run Aground Taxi Service', ''), + 1719: ("Duck's Back Water Company", ''), + 1720: ('The Reel Deal', ''), + 1721: ('All For Nautical', ''), + 1723: ("Squid's Seaweed", ''), + 1724: ("That's a Moray!", ''), + 1725: ("Ahab's Prefab Sea Crab Center", ''), + 1726: ('Root Beer Afloats', ''), + 1727: ('This Oar That', ''), + 1728: ('Good Luck Horseshoe Crabs', ''), + 1729: ('', ''), + 1802: ('Nautical But Nice', ''), + 1804: ('Mussel Beach Gymnasium', ''), + 1805: ('Tackle Box Lunches', ''), + 1806: ('Cap Size Hat Store', ''), + 1807: ('Keel Deals', ''), + 1808: ('Knots So Fast', ''), + 1809: ('Rusty Buckets', ''), + 1810: ('Anchor Management', ''), + 1811: ("What's Canoe With You?", ''), + 1813: ('Pier Pressure Plumbing', ''), + 1814: ('The Yo Ho Stop and Go', ''), + 1815: ("What's Up, Dock?", ''), + 1818: ('Seven Seas Cafe', ''), + 1819: ("Docker's Diner", ''), + 1820: ('Hook, Line, and Sinker Prank Shop', ''), + 1821: ("King Neptoon's Cannery", ''), + 1823: ('The Clam Bake Diner', ''), + 1824: ('Dog Paddles', ''), + 1825: ('Wholly Mackerel! Fish Market', ''), + 1826: ("Claggart's Clever Clovis Closet", ''), + 1828: ("Alice's Ballast Palace", ''), + 1829: ('Seagull Statue Store', ''), + 1830: ('Lost and Flounder', ''), + 1831: ('Kelp Around the House', ''), + 1832: ("Melville's Massive Mizzenmast Mart", ''), + 1833: ('This Transom Man Custom Tailored Suits', ''), + 1834: ('Rudderly Ridiculous!', ''), + 1835: ('', ''), + 4503: ('Gag Shop', ''), + 4504: ('Toon Headquarters', ''), + 4506: ('Clothing Shop', ''), + 4508: ('', ''), + 4603: ("Tom-Tom's Drums", ''), + 4604: ('In Four-Four Time', ''), + 4605: ("Fifi's Fiddles", ''), + 4606: ('Casa De Castanets', ''), + 4607: ('Catchy Toon Apparel', ''), + 4609: ('Do, Rae, Me Piano Keys', ''), + 4610: ('Please Refrain', ''), + 4611: ('Tuning Forks and Spoons', ''), + 4612: ("Dr. Fret's Dentistry", ''), + 4614: ('Shave and a Haircut for a Song', ''), + 4615: ("Piccolo's Pizza", ''), + 4617: ('Happy Mandolins', ''), + 4618: ('Rests Rooms', ''), + 4619: ('More Scores', ''), + 4622: ('Chin Rest Pillows', ''), + 4623: ('Flats Sharpened', ''), + 4625: ('Tuba Toothpaste', ''), + 4626: ('Notations', ''), + 4628: ('Accidental Insurance', ''), + 4629: ("Riff's Paper Plates", ''), + 4630: ('Music Is Our Forte', ''), + 4631: ('Canto Help You', ''), + 4632: ('Dance Around the Clock Shop', ''), + 4635: ('Tenor Times', ''), + 4637: ('For Good Measure', ''), + 4638: ('Hard Rock Shop', ''), + 4639: ('Four Score Antiques', ''), + 4641: ('Blues News', ''), + 4642: ('Ragtime Dry Cleaners', ''), + 4645: ('Club 88', ''), + 4646: ('', ''), + 4648: ('Carry a Toon Movers', ''), + 4649: ('', ''), + 4652: ('Full Stop Shop', ''), + 4653: ('', ''), + 4654: ('Pitch Perfect Roofing', ''), + 4655: ("The Treble Chef's Cooking School", ''), + 4656: ('', ''), + 4657: ('Barbershop Quartet', ''), + 4658: ('Plummeting Pianos', ''), + 4659: ('', ''), + 4701: ('The Schmaltzy Waltz School of Dance', ''), + 4702: ('Timbre! Equipment for the Singing Lumberjack', ''), + 4703: ('I Can Handel It!', ''), + 4704: ("Tina's Concertina Concerts", ''), + 4705: ('Zither Here Nor There', ''), + 4707: ("Doppler's Sound Effects Studio", ''), + 4709: ('On Ballet! Climbing Supplies', ''), + 4710: ('Hurry Up, Slow Polka! School of Driving', ''), + 4712: ('C-Flat Tire Repair', ''), + 4713: ('B-Sharp Fine Menswear', ''), + 4716: ('Four-Part Harmonicas', ''), + 4717: ('Sonata Your Fault! Discount Auto Insurance', ''), + 4718: ('Chopin Blocks and Other Kitchen Supplies', ''), + 4719: ('Madrigal Motor Homes', ''), + 4720: ('Name That Toon', ''), + 4722: ('Overture Understudies', ''), + 4723: ('Haydn Go Seek Playground Supplies', ''), + 4724: ('White Noise for Girls and Boys', ''), + 4725: ('The Baritone Barber', ''), + 4727: ('Vocal Chords Braided', ''), + 4728: ("Sing Solo We Can't Hear You", ''), + 4729: ('Double Reed Bookstore', ''), + 4730: ('Lousy Lyrics', ''), + 4731: ('Toon Tunes', ''), + 4732: ('Etude Brute? Theatre Company', ''), + 4733: ('', ''), + 4734: ('', ''), + 4735: ('Accordions, If You Want In, Just Bellow!', ''), + 4736: ('Her and Hymn Wedding Planners', ''), + 4737: ('Harp Tarps', ''), + 4738: ('Canticle Your Fancy Gift Shop', ''), + 4739: ('', ''), + 4801: ("Marshall's Stacks", ''), + 4803: ('What a Mezzo! Maid Service', ''), + 4804: ('Mixolydian Scales', ''), + 4807: ('Relax the Bach', ''), + 4809: ("I Can't Understanza!", ''), + 4812: ('', ''), + 4817: ('The Ternary Pet Shop', ''), + 4819: ("Yuki's Ukeleles", ''), + 4820: ('', ''), + 4821: ("Anna's Cruises", ''), + 4827: ('Common Time Watches', ''), + 4828: ("Schumann's Shoes for Men", ''), + 4829: ("Pachelbel's Canonballs", ''), + 4835: ('Ursatz for Kool Katz', ''), + 4836: ('Reggae Regalia', ''), + 4838: ('Kazoology School of Music', ''), + 4840: ('Coda Pop Musical Beverages', ''), + 4841: ('Lyre, Lyre, Pants on Fire!', ''), + 4842: ('The Syncopation Corporation', ''), + 4843: ('', ''), + 4844: ('Con Moto Cycles', ''), + 4845: ("Ellie's Elegant Elegies", ''), + 4848: ('Lotsa Lute Savings & Loan', ''), + 4849: ('', ''), + 4850: ('The Borrowed Chord Pawn Shop', ''), + 4852: ('Flowery Flute Fleeces', ''), + 4853: ("Leo's Fenders", ''), + 4854: ("Wagner's Vocational Violin Videos", ''), + 4855: ('The Teli-Caster Network', ''), + 4856: ('', ''), + 4862: ("Quentin's Quintessen\x03tial Quadrilles", ''), + 4867: ("Mr. Costello's Yellow Cellos", ''), + 4868: ('', ''), + 4870: ("Ziggy's Zoo of Zigeuner\x03musik", ''), + 4871: ("Harry's House of Harmonious Humbuckers", ''), + 4872: ("Fast Freddie's Fretless Fingerboards", ''), + 4873: ('', ''), + 5501: ('Gag Shop', ''), + 5502: (lToonHQ, ''), + 5503: ('Clothing Shop', ''), + 5505: ('', ''), + 5601: ('Eye of the Potato Optometry', ''), + 5602: ("Artie Choke's Neckties", ''), + 5603: ('Lettuce Alone', ''), + 5604: ('Cantaloupe Bridal Shop', ''), + 5605: ('Vege-tables and Chairs', ''), + 5606: ('Petals', ''), + 5607: ('Compost Office', ''), + 5608: ('Mom and Pop Corn', ''), + 5609: ('Berried Treasure', ''), + 5610: ("Black-eyed Susan's Boxing Lessons", ''), + 5611: ("Gopher's Gags", ''), + 5613: ('Crop Top Barbers', ''), + 5615: ("Bud's Bird Seed", ''), + 5616: ('Dew Drop Inn', ''), + 5617: ("Flutterby's Butterflies", ''), + 5618: ("Peas and Q's", ''), + 5619: ("Jack's Beanstalks", ''), + 5620: ('Rake It Inn', ''), + 5621: ('Grape Expectations', ''), + 5622: ('Petal Pusher Bicycles', ''), + 5623: ('Bubble Bird Baths', ''), + 5624: ("Mum's the Word", ''), + 5625: ('Leaf It Bees', ''), + 5626: ('Pine Needle Crafts', ''), + 5627: ('', ''), + 5701: ('From Start to Spinach', ''), + 5702: ("Jake's Rakes", ''), + 5703: ("Photo Cynthia's Camera Shop", ''), + 5704: ('Lisa Lemon Used Cars', ''), + 5705: ('Poison Oak Furniture', ''), + 5706: ('14 Carrot Jewelers', ''), + 5707: ('Musical Fruit', ''), + 5708: ("We'd Be Gone Travel Agency", ''), + 5709: ('Astroturf Mowers', ''), + 5710: ('Tuft Guy Gym', ''), + 5711: ('Garden Hosiery', ''), + 5712: ('Silly Statues', ''), + 5713: ('Trowels and Tribulations', ''), + 5714: ('Spring Rain Seltzer Bottles', ''), + 5715: ('Hayseed News', ''), + 5716: ('Take It or Leaf It Pawn Shop', ''), + 5717: ('The Squirting Flower', ''), + 5718: ('The Dandy Lion Exotic Pets', ''), + 5719: ('Trellis the Truth! Private Investi\x03gators', ''), + 5720: ('Vine and Dandy Menswear', ''), + 5721: ('Root 66 Diner', ''), + 5725: ('Barley, Hops, and Malt Shop', ''), + 5726: ("Bert's Dirt", ''), + 5727: ('Gopher Broke Savings & Loan', ''), + 5728: ('', ''), + 5802: (lToonHQ, ''), + 5804: ('Just Vase It', ''), + 5805: ('Snail Mail', ''), + 5809: ('Fungi Clown School', ''), + 5810: ('Honeydew This', ''), + 5811: ('Lettuce Inn', ''), + 5815: ('Grass Roots', ''), + 5817: ('Apples and Oranges', ''), + 5819: ('Green Bean Jeans', ''), + 5821: ('Squash and Stretch Gym', ''), + 5826: ('Ant Farming Supplies', ''), + 5827: ('Dirt. Cheap.', ''), + 5828: ('Couch Potato Furniture', ''), + 5830: ('Spill the Beans', ''), + 5833: ('The Salad Bar', ''), + 5835: ('Flower Bed and Breakfast', ''), + 5836: ("April's Showers and Tubs", ''), + 5837: ('School of Vine Arts', ''), + 9501: ('Lullaby Library', ''), + 9503: ('The Snooze Bar', ''), + 9504: ('Gag Shop', ''), + 9505: (lToonHQ, ''), + 9506: ('Clothing Shop', ''), + 9508: ('', ''), + 9601: ('Snuggle Inn', ''), + 9602: ('Forty Winks for the Price of Twenty', ''), + 9604: ("Ed's Red Bed Spreads", ''), + 9605: ('Cloud Nine Design', ''), + 9607: ("Big Mama's Bahama Pajamas", ''), + 9608: ('Cat Nip for Cat Naps', ''), + 9609: ('Deep Sleep for Cheap', ''), + 9613: ('Clock Cleaners', ''), + 9616: ('Lights Out Electric Co.', ''), + 9617: ('Crib Notes - Music to Sleep By', ''), + 9619: ('Relax to the Max', ''), + 9620: ("PJ's Taxi Service", ''), + 9622: ('Sleepy Time Pieces', ''), + 9625: ('Curl Up Beauty Parlor', ''), + 9626: ('Bed Time Stories', ''), + 9627: ('The Sleepy Teepee', ''), + 9628: ('Call It a Day Calendars', ''), + 9629: ('Silver Lining Jewelers', ''), + 9630: ('Rock to Sleep Quarry', ''), + 9631: ('Down Time Watch Repair', ''), + 9633: ('The Dreamland Screening Room', ''), + 9634: ('Mind Over Mattress', ''), + 9636: ('Insomniac Insurance', ''), + 9639: ('House of Hibernation', ''), + 9640: ('Nightstand Furniture Company', ''), + 9642: ('Sawing Wood Slumber Lumber', ''), + 9643: ('Shut-Eye Optometry', ''), + 9644: ('Pillow Fights Nightly', ''), + 9645: ('The All Tucked Inn', ''), + 9647: ('Make Your Bed! Hardware Store', ''), + 9649: ('Snore or Less', ''), + 9650: ('Crack of Dawn Repairs', ''), + 9651: ('For Richer or Snorer', ''), + 9652: ('', ''), + 9703: ('Fly By Night Travel Agency', ''), + 9704: ('Night Owl Pet Shop', ''), + 9705: ('Asleep At The Wheel Car Repair', ''), + 9706: ('Tooth Fairy Dentistry', ''), + 9707: ("Dawn's Yawn & Garden Center", ''), + 9708: ('Bed Of Roses Florist', ''), + 9709: ('Pipe Dream Plumbers', ''), + 9710: ('REM Optometry', ''), + 9711: ('Wake-Up Call Phone Company', ''), + 9712: ("Counting Sheep - So You Don't Have To!", ''), + 9713: ('Wynken, Blynken & Nod, Attorneys at Law', ''), + 9714: ('Dreamboat Marine Supply', ''), + 9715: ('First Security Blanket Bank', ''), + 9716: ('Wet Blanket Party Planners', ''), + 9717: ("Baker's Dozin' Doughnuts", ''), + 9718: ("Sandman's Sandwiches", ''), + 9719: ('Armadillo Pillow Company', ''), + 9720: ('Talking In Your Sleep Voice Training', ''), + 9721: ('Snug As A Bug Rug Dealer', ''), + 9722: ('Dream On Talent Agency', ''), + 9725: ("Cat's Pajamas", ''), + 9727: ('You Snooze, You Lose', ''), + 9736: ('Dream Jobs Employment Agency', ''), + 9737: ("Waltzing Matilda's Dance School", ''), + 9738: ('House of Zzzzzs', ''), + 9740: ('Hit The Sack Fencing School', ''), + 9741: ("Don't Let The Bed Bugs Bite Exterminators", ''), + 9744: ("Rip Van Winkle's Wrinkle Cream", ''), + 9752: ('Midnight Oil & Gas Company', ''), + 9753: ("Moonbeam's Ice Creams", ''), + 9754: ('Sleepless in the Saddle All Night Pony Rides', ''), + 9755: ('Bedknobs & Broomsticks Movie House', ''), + 9756: ('', ''), + 9759: ('Sleeping Beauty Parlor', ''), + 3507: ('Gag Shop', ''), + 3508: (lToonHQ, ''), + 3509: ('Clothing Shop', ''), + 3511: ('', ''), + 3601: ('Northern Lights Electric Company', ''), + 3602: ("Nor'easter Bonnets", ''), + 3605: ('', ''), + 3607: ('The Blizzard Wizard', ''), + 3608: ('Nothing to Luge', ''), + 3610: ("Mike's Massive Mukluk Mart", ''), + 3611: ("Mr. Cow's Snow Plows", ''), + 3612: ('Igloo Design', ''), + 3613: ('Ice Cycle Bikes', ''), + 3614: ('Snowflakes Cereal Company', ''), + 3615: ('Fried Baked Alaskas', ''), + 3617: ('Cold Air Balloon Rides', ''), + 3618: ('Snow Big Deal! Crisis Management', ''), + 3620: ('Skiing Clinic', ''), + 3621: ('The Melting Ice Cream Bar', ''), + 3622: ('', ''), + 3623: ('The Mostly Toasty Bread Company', ''), + 3624: ('Subzero Sandwich Shop', ''), + 3625: ("Auntie Freeze's Radiator Supply", ''), + 3627: ('St. Bernard Kennel Club', ''), + 3629: ('Pea Soup Cafe', ''), + 3630: ('Icy London, Icy France Travel Agency', ''), + 3634: ('Easy Chair Lifts', ''), + 3635: ('Used Firewood', ''), + 3636: ('Affordable Goosebumps', ''), + 3637: ("Kate's Skates", ''), + 3638: ('Toboggan or Not Toboggan', ''), + 3641: ("Fred's Red Sled Beds", ''), + 3642: ('Eye of the Storm Optics', ''), + 3643: ('Snowball Hall', ''), + 3644: ('Melted Ice Cubes', ''), + 3647: ('The Sanguine Penguin Tuxedo Shop', ''), + 3648: ('Instant Ice', ''), + 3649: ('Hambrrrgers', ''), + 3650: ('Antarctic Antiques', ''), + 3651: ("Frosty Freddy's Frozen Frankfurters", ''), + 3653: ('Ice House Jewelry', ''), + 3654: ('', ''), + 3702: ('Winter Storage', ''), + 3703: ('', ''), + 3705: ('Icicles Built for Two', ''), + 3706: ("Shiverin' Shakes Malt Shop", ''), + 3707: ('Snowplace Like Home', ''), + 3708: ("Pluto's Place", ''), + 3710: ('Dropping Degrees Diner', ''), + 3711: ('', ''), + 3712: ('Go With the Floe', ''), + 3713: ('Chattering Teeth, Subzero Dentist', ''), + 3715: ("Aunt Arctic's Soup Shop", ''), + 3716: ('Road Salt and Pepper', ''), + 3717: ('Juneau What I Mean?', ''), + 3718: ('Designer Inner Tubes', ''), + 3719: ('Ice Cube on a Stick', ''), + 3721: ("Noggin's Toboggan Bargains", ''), + 3722: ('Snow Bunny Ski Shop', ''), + 3723: ("Shakey's Snow Globes", ''), + 3724: ('The Chattering Chronicle', ''), + 3725: ('You Sleigh Me', ''), + 3726: ('Solar Powered Blankets', ''), + 3728: ('Lowbrow Snowplows', ''), + 3729: ('', ''), + 3730: ('Snowmen Bought & Sold', ''), + 3731: ('Portable Fireplaces', ''), + 3732: ('The Frozen Nose', ''), + 3734: ('Icy Fine, Do You? Optometry', ''), + 3735: ('Polar Ice Caps', ''), + 3736: ('Diced Ice at a Nice Price', ''), + 3737: ('Downhill Diner', ''), + 3738: ("Heat-Get It While It's Hot", ''), + 3739: ('', ''), + 3801: ('Toon HQ', ''), + 3806: ('Alpine Chow Line', ''), + 3807: ('Used Groundhog Shadows', ''), + 3808: ('The Sweater Lodge', ''), + 3809: ('Ice Saw It Too', ''), + 3810: ('A Better Built Quilt', ''), + 3811: ('Your Snow Angel', ''), + 3812: ('Mittens for Kittens', ''), + 3813: ("Snowshoes You Can't Refuse", ''), + 3814: ('Malt in Your Mouth Soda Fountain', ''), + 3815: ('The Toupee Chalet', ''), + 3816: ('Just So Mistletoe', ''), + 3817: ('Winter Wonderland Walking Club', ''), + 3818: ('The Shovel Hovel', ''), + 3819: ('Clean Sweep Chimney Service', ''), + 3820: ('Snow Whitening', ''), + 3821: ('Hibernation Vacations', ''), + 3823: ('Precipitation Foundation', ''), + 3824: ('Open Fire Chestnut Roasting', ''), + 3825: ('Cool Cat Hats', ''), + 3826: ('Oh My Galoshes!', ''), + 3827: ('Choral Wreaths', ''), + 3828: ("Snowman's Land", ''), + 3829: ('Pinecone Zone', ''), + 3830: ('Wait and See Goggle Defogging', ''), + 9802: ("Barbra's Bohemian Art Supplies", ''), + 9804: ("Angry Dan's Garden Sprinklers", ''), + 9806: ("Nacib Niri", ''), + 9808: ("Ori-O's Creamery and Dairy", ''), + 9809: ('Rave of the Forks', ''), + 9812: ('Louds Silent-Shop', ''), + 9813: ('Bedpost Bakery', ''), + 9814: ("Amazing Angler's Silly Shop", ''), + 9815: ("Nattie's Catties", ''), + 9816: ('Carlos Carpentry BunkBeds Inc.', ''), + 9817: ('The Psyche-Ologist', ''), + 9819: ("Toony Bob's Fluffy Pillows", ''), + 9820: ("Batty's Bat Supply", ''), + 9821: ("Milk or Sugar? Tea Shop", ''), + 9822: ("Zedd's Pasta", ''), + 9824: ("Joe's Burnt Biscuits", ''), + 9826: ("Juste-A-Cote Taxi Service", ''), + 9827: ("Frekly Fred's Storage Shack", ''), + 9828: ("Barden Betty's Clingy Clothing", ''), + 9829: (lToonHQ, ''), +} +ClosetTimeoutMessage = 'Sorry, you ran out\n of time.' +ClosetNotOwnerMessage = "This isn't your closet, but you may try on the clothes." +ClosetPopupOK = lOK +ClosetPopupCancel = lCancel +ClosetDiscardButton = 'Remove' +ClosetAreYouSureMessage = 'You have deleted some clothes. Do you really want to delete them?' +ClosetYes = lYes +ClosetNo = lNo +ClosetVerifyDelete = 'Really delete %s?' +ClosetShirt = 'this shirt' +ClosetShorts = 'these shorts' +ClosetSkirt = 'this skirt' +ClosetDeleteShirt = 'Delete\nshirt' +ClosetDeleteShorts = 'Delete\nshorts' +ClosetDeleteSkirt = 'Delete\nskirt' +TrunkNotOwnerMessage = "This isn't your trunk, but you may try on the accessories." +TrunkAreYouSureMessage = 'You have deleted some accessories. Do you really want to delete them?' +TrunkHat = 'this hat' +TrunkGlasses = 'these glasses' +TrunkBackpack = 'this backpack' +TrunkShoes = 'these shoes' +TrunkDeleteHat = 'Delete\nhat' +TrunkDeleteGlasses = 'Delete\nglasses' +TrunkDeleteBackpack = 'Delete\nbackpack' +TrunkDeleteShoes = 'Delete\nshoes' +EstateOwnerLeftMessage = "Sorry, the owner of this estate left. You'll be sent to the playground in %s seconds" +EstatePopupOK = lOK +EstateTeleportFailed = "Couldn't go home. Try again!" +EstateTeleportFailedNotFriends = "Sorry, %s is in a toon's estate that you are not friends with." +EstateTargetGameStart = 'The Toon-up Target game has started!' +EstateTargetGameInst = "The more you hit the red target, the more you'll get Tooned up." +EstateTargetGameEnd = 'The Toon-up Target game is now over...' +AvatarsHouse = '%s\n House' +BankGuiCancel = lCancel +BankGuiOk = lOK +DistributedBankNoOwner = 'Sorry, this is not your bank.' +DistributedBankNotOwner = 'Sorry, this is not your bank.' +FishGuiCancel = lCancel +FishGuiOk = 'Sell All' +FishTankValue = 'Hi, %(name)s! You have %(num)s fish in your bucket worth a total of %(value)s Jellybeans. Do you want to sell them all?' +FlowerGuiCancel = lCancel +FlowerGuiOk = 'Sell All' +FlowerBasketValue = '%(name)s, you have %(num)s flowers in your basket worth a total of %(value)s Jellybeans. Do you want to sell them all?' + +def GetPossesive(name): + if name[-1:] == 's': + possesive = name + "'" + else: + possesive = name + "'s" + return possesive + + +PetTrait2descriptions = {'hungerThreshold': ('Always Hungry', + 'Often Hungry', + 'Sometimes Hungry', + 'Rarely Hungry'), + 'boredomThreshold': ('Always Bored', + 'Often Bored', + 'Sometimes Bored', + 'Rarely Bored'), + 'angerThreshold': ('Always Grumpy', + 'Often Grumpy', + 'Sometimes Grumpy', + 'Rarely Grumpy'), + 'forgetfulness': ('Always Forgets', + 'Often Forgets', + 'Sometimes Forgets', + 'Rarely Forgets'), + 'excitementThreshold': ('Very Calm', + 'Pretty Calm', + 'Pretty Excitable', + 'Very Excitable'), + 'sadnessThreshold': ('Always Sad', + 'Often Sad', + 'Sometimes Sad', + 'Rarely Sad'), + 'restlessnessThreshold': ('Always Restless', + 'Often Restless', + 'Sometimes Restless', + 'Rarely Restless'), + 'playfulnessThreshold': ('Rarely Playful', + 'Sometimes Playful', + 'Often Playful', + 'Always Playful'), + 'lonelinessThreshold': ('Always Lonely', + 'Often Lonely', + 'Sometimes Lonely', + 'Rarely Lonely'), + 'fatigueThreshold': ('Always Tired', + 'Often Tired', + 'Sometimes Tired', + 'Rarely Tired'), + 'confusionThreshold': ('Always Confused', + 'Often Confused', + 'Sometimes Confused', + 'Rarely Confused'), + 'surpriseThreshold': ('Always Surprised', + 'Often Surprised', + 'Sometimes Surprised', + 'Rarely Surprised'), + 'affectionThreshold': ('Rarely Affectionate', + 'Sometimes Affectionate', + 'Often Affectionate', + 'Always Affectionate')} +FireworksInstructions = lToonHQ + ': Hit the "Page Up" key to see the show!' +FireworksJuly4Beginning = lToonHQ + ': Welcome to summer fireworks! Enjoy the show!' +FireworksJuly4Ending = lToonHQ + ': Hope you enjoyed the show! Have a great summer!' +FireworksNewYearsEveBeginning = lToonHQ + ': Happy New Year! Enjoy the fireworks show, sponsored by Flippy!' +FireworksNewYearsEveEnding = lToonHQ + ': Hope you enjoyed the show! Have a Toontastic New Year!' +FireworksComboBeginning = lToonHQ + ': Enjoy lots of Laffs with Toon fireworks!' +FireworksComboEnding = lToonHQ + ': Thank you, Toons! Hope you enjoyed the show!' +TIP_NONE = 0 +TIP_GENERAL = 1 +TIP_STREET = 2 +TIP_MINIGAME = 3 +TIP_COGHQ = 4 +TIP_ESTATE = 5 +TIP_KARTING = 6 +TIP_GOLF = 7 +TipTitle = 'TOON TIP:' +TipDict = {TIP_NONE: ('',), + TIP_GENERAL: ('Quickly check your ToonTask progress by holding down the "End" key.', + 'Quickly check your Gag page by holding down the "Home" key.', + 'Open your Friends List by pressing the "F7" key.', + 'Open or close your Shticker Book by pressing the "F8" key.', + 'You can look up by pressing the "Page Up" key and look down by pressing the "Page Down" key.', + 'Press the "Control" key to jump.', + 'Press the "F9" key to take a screenshot, which will be saved in your Toontown Stride folder on your computer.', + 'You can change your screen resolution, adjust audio, and control other options on the Options Page in the Shticker Book.', + "Try on your friend's clothing at the closet in their house.", + 'You can go to your house using the "Go Home" button on your map.', + 'Every time you turn in a completed ToonTask your Laff points are automatically refilled.', + 'You can browse the selection at Clothing Stores even without a clothing ticket.', + 'Rewards for some ToonTasks allow you to carry more gags and Jellybeans.', + 'You can have up to 50 friends on your Friends List.', + 'Some ToonTask rewards let you teleport to playgrounds in Toontown by using the Map Page in the Shticker Book.', + 'Increase your Laff points in the Playgrounds by collecting treasures like stars and ice cream cones.', + 'To heal quickly after a battle, go to your estate and play with your Doodle.', + 'Change to different views of your Toon by pressing the Tab Key.', + 'Sometimes you can find several different ToonTasks offered for the same reward. Shop around!', + 'Finding friends with similar ToonTasks is a fun way to progress through the game.', + 'You never need to save your Toontown progress. The Toontown Stride servers continually save all the necessary information.', + 'You can whisper to other Toons either by clicking on them or by selecting them from your Friends List.', + 'Some SpeedChat phrases play emotion animations on your Toon.', + 'If the area you are in is crowded, try changing Districts. Go to the District Page in the Shticker Book and select a different one.', + 'If you actively rescue buildings you will get a bronze, silver, or gold star above your Toon.', + 'If you rescue enough buildings to get a star above your head you may find your name on the blackboard in a Toon HQ.', + 'Rescued buildings are sometimes recaptured by the Cogs. The only way to keep your star is to go out and rescue more buildings!', + 'The names of your True Friends will appear in Blue.', + 'See if you can collect all the fish in Toontown!', + 'Different ponds hold different fish. Try them all!', + 'When your fishing bucket is full sell your fish to the Fishermen in the Playgrounds.', + 'You can sell your fish to the Fishermen or inside Pet Shops.', + 'Stronger fishing rods catch heavier fish but cost more Jellybeans to use.', + 'You can purchase stronger fishing rods in the Cattlelog.', + 'Heavier fish are worth more Jellybeans to the Pet Shop.', + 'Rare fish are worth more Jellybeans to the Pet Shop.', + 'You can sometimes find bags of Jellybeans while fishing.', + 'Some ToonTasks require fishing items out of the ponds.', + 'Fishing ponds in the Playgrounds have different fish than ponds on the streets.', + 'Some fish are really rare. Keep fishing until you collect them all!', + 'The pond at your estate has fish that can only be found there.', + 'For every 10 species you catch, you will get a fishing trophy!', + 'You can see what fish you have collected in your Shticker Book.', + 'Some fishing trophies reward you with a Laff boost.', + 'Fishing is a good way to earn more Jellybeans.', + 'Adopt a Doodle at the Pet Shop!', + 'Pet Shops get new Doodles to sell every day.', + 'Visit the Pet Shops every day to see what new Doodles they have.', + 'Different neighborhoods have different Doodles offered for adoption.', + "Show off your stylin' ride and turbo-boost your Laff limit at Goofy Speedway.", + 'Enter Goofy Speedway through the tire-shaped tunnel in Toontown Central Playground.', + 'Earn Laff points at Goofy Speedway.', + 'Goofy Speedway has six different race tracks. '), + TIP_STREET: ('There are four types of Cogs: Lawbots, Cashbots, Sellbots, and Bossbots.', + 'Each Gag Track has different amounts of accuracy and damage.', + 'Sound gags will affect all Cogs but will wake up any lured Cogs.', + 'Defeating Cogs in strategic order can greatly increase your chances of winning battles.', + 'The Toon-Up Gag Track lets you heal other Toons in battle.', + 'Gag experience points are doubled during a Cog Invasion!', + 'Multiple Toons can team up and use the same Gag Track in battle to get bonus Cog damage.', + 'In battle, gags are used in order from top to bottom as displayed on the Gag Menu.', + 'The row of circular lights over Cog Building elevators show how many floors will be inside.', + 'Click on a Cog to see more details.', + 'Using high level gags against low level Cogs will not earn any experience points.', + 'A gag that will earn experience has a blue background on the Gag Menu in battle.', + 'Gag experience is multiplied when used inside Cog Buildings. Higher floors have higher multipliers.', + 'When a Cog is defeated, each Toon in that round will get credit for the Cog when the battle is over.', + 'Each street in Toontown has different Cog levels and types.', + 'Sidewalks are safe from Cogs.', + 'On the streets, side doors tell knock-knock jokes when approached.', + 'Some ToonTasks train you for new Gag Tracks.', + 'Traps are only useful if you or your friends coordinate using Lure in battle.', + 'Higher level Lures are less likely to miss.', + 'Lower level gags have a lower accuracy against high level Cogs.', + 'Cogs cannot attack once they have been lured in battle.', + 'When you and your friends defeat a Cog building you are rewarded with portraits inside the rescued Toon Building.', + 'Using a Toon-Up gag on a Toon with a full Laff meter will not earn Toon-Up experience.', + 'Cogs will be briefly stunned when hit by any gag. This increases the chance that other gags in the same round will hit.', + 'Drop gags have low chance of hitting, but accuracy is increased when Cogs are first hit by another gag in the same round.', + 'When you\'ve defeated enough Cogs, use the "Cog Radar" by clicking the Cog icons on the Cog Gallery page in your Shticker Book.', + 'During a battle, you can tell which Cog your teammates are attacking by looking at the dashes (-) and Xs.', + 'During a battle, Cogs have a light on them that displays their health; green is healthy, red is nearly destroyed.', + 'A maximum of four Toons can battle at once.', + 'On the street, Cogs are more likely to join a fight against multiple Toons than just one Toon.', + 'The two most difficult Cogs of each type are only found in buildings.', + 'Drop gags never work against lured Cogs.', + 'Cogs tend to attack the Toon that has done them the most damage.', + 'Sound gags do not get bonus damage against lured Cogs.', + 'If you wait too long to attack a lured Cog, it will wake up. Higher level lures last longer.', + 'There are fishing ponds on every street in Toontown. Some streets have unique fish.'), + TIP_MINIGAME: ('After you fill up your Jellybean jar, any Jellybeans you get from Trolley Games automatically spill over into your bank.', + 'You can use the arrow keys instead of the mouse in the "Match Jaymo" Trolley Game.', + 'In the Cannon Game you can use the arrow keys to move your cannon and press the "Control" key to fire.', + 'In the Ring Game, bonus points are awarded when the entire group successfully swims through its rings.', + 'A perfect game of Match Jaymo will double your points.', + 'In the Tug-of-War you are awarded more Jellybeans if you play against a tougher Cog.', + 'Trolley Game difficulty varies by neighborhood; ' + lToontownCentral + ' has the easiest and ' + lDonaldsDreamland + ' has the hardest.', + 'Certain Trolley Games can only be played in a group.'), + TIP_COGHQ: ('You must complete your Sellbot Disguise before visiting the V.P.', + 'You must complete your Cashbot Disguise before visiting the C.F.O.', + 'You must complete your Lawbot Disguise before visiting the Chief Justice.', + 'You can jump on Cog Goons to temporarily disable them.', + 'Collect Cog Merits by defeating Sellbot Cogs in battle.', + 'Collect Cogbucks by defeating Cashbot Cogs in battle.', + 'Collect Jury Notices by defeating Lawbot Cogs in battle.', + 'Collect Stock Options by defeating Bossbot Cogs in battle.', + 'You get more Merits, Cogbucks, Jury Notices, or Stock Options from higher level Cogs.', + 'When you collect enough Cog Merits to earn a promotion, go see the Sellbot V.P.!', + 'When you collect enough Cogbucks to earn a promotion, go see the Cashbot C.F.O.!', + 'When you collect enough Jury Notices to earn a promotion, go see the Lawbot Chief Justice!', + 'When you collect enough Stock Options to earn a promotion, go see the Bossbot C.E.O.!', + 'You can talk like a Cog when you are wearing your Cog Disguise.', + 'Up to eight Toons can join together to fight the Sellbot V.P.', + 'Up to eight Toons can join together to fight the Cashbot C.F.O.', + 'Up to eight Toons can join together to fight the Lawbot Chief Justice.', + 'Up to eight Toons can join together to fight the Bossbot C.E.O.', + 'Inside Cog Headquarters follow stairs leading up to find your way.', + 'Each time you battle through a Sellbot HQ factory, you will gain one part of your Sellbot Cog Disguise.', + 'You can check the progress of your Cog Disguise in your Shticker Book.', + 'You can check your promotion progress on your Disguise Page in your Shticker Book.', + 'Make sure you have full gags and a full Laff Meter before going to Cog Headquarters.', + 'As you get promoted, your Cog disguise updates.', + 'You must defeat the ' + Foreman + ' to recover a Sellbot Cog Disguise part.', + "Earn Cashbot disguise suit parts as rewards for completing ToonTasks in Donald's Dreamland.", + 'Cashbots manufacture and distribute their currency, Cogbucks, in three Mints - Coin, Dollar and Bullion.', + 'Wait until the C.F.O. is dizzy to throw a safe, or he will use it as a helmet! Hit the helmet with another safe to knock it off.', + 'Earn Lawbot disguise suit parts as rewards for completing ToonTasks for Professor Flake.', + "It pays to be puzzled: the virtual Cogs in Lawbot HQ won't reward you with Jury Notices."), + TIP_ESTATE: ('Doodles can understand some SpeedChat phrases. Try them!', + 'Use the "Pet" SpeedChat menu to ask your Doodle to do tricks.', + "You can teach Doodles tricks with training lessons from Clarabelle's Cattlelog.", + 'Reward your Doodle for doing tricks.', + "If you visit a friend's estate, your Doodle will come too.", + 'Feed your Doodle a Jellybean when it is hungry.', + 'Click on a Doodle to get a menu where you can Feed, Scratch, and Call him.', + 'Doodles love company. Invite your friends over to play!', + 'All Doodles have unique personalities.', + 'You can return your Doodle and adopt a new one at the Pet Shops.', + 'When a Doodle performs a trick, the Toons around it heal.', + 'Doodles become better at tricks with practice. Keep at it!', + 'More advanced Doodle tricks heal Toons faster.', + 'Experienced Doodles can perform more tricks before getting tired.', + 'You can see a list of nearby Doodles in your Friends List.', + "Purchase furniture from Clarabelle's Cattlelog to decorate your house.", + 'The bank inside your house holds extra Jellybeans.', + 'The closet inside your house holds extra clothes.', + "Go to your friend's house and try on his clothes.", + "Purchase better fishing rods from Clarabelle's Cattlelog.", + 'Call Clarabelle using the phone inside your house.', + 'Clarabelle sells a larger closet that holds more clothing.', + 'Make room in your closet before using a Clothing Ticket.', + 'Clarabelle sells everything you need to decorate your house.', + 'Check your mailbox for deliveries after ordering from Clarabelle.', + "Clothing from Clarabelle's Cattlelog takes one hour to be delivered.", + "Wallpaper and flooring from Clarabelle's Cattlelog take one hour to be delivered.", + "Furniture from Clarabelle's Cattlelog takes a full day to be delivered.", + 'Store extra furniture in your attic.', + 'You will get a notice from Clarabelle when a new Cattlelog is ready.', + 'You will get a notice from Clarabelle when a Cattlelog delivery arrives.', + 'New Cattlelogs are delivered each week.', + 'Look for limited-edition holiday items in the Cattlelog.', + 'Move unwanted furniture to the trash can.', + 'Some fish, like the Holey Mackerel, are more commonly found in Toon Estates.', + 'You can invite your friends to your Estate using SpeedChat.', + 'Did you know the color of your house matches the color of your Pick-A-Toon panel?'), + TIP_KARTING: ("Buy a Roadster, TUV, or Cruiser kart in Goofy's Auto Shop.", + "Customize your kart with decals, rims and more in Goofy's Auto Shop.", + 'Earn tickets by kart racing at Goofy Speedway.', + "Tickets are the only currency accepted at Goofy's Auto Shop.", + 'Tickets are required as deposits to race.', + 'A special page in the Shticker Book allows you to customize your kart.', + 'A special page in the Shticker Book allows you to view records on each track.', + 'A special page in the Shticker Book allows you to display trophies.', + 'Screwball Stadium is the easiest track at Goofy Speedway.', + 'Airborne Acres has the most hills and jumps of any track at Goofy Speedway.', + 'Blizzard Boulevard is the most challenging track at Goofy Speedway.'), + TIP_GOLF: ('Press the Tab key to see a top view of the golf course.', 'Press the Up Arrow key to point yourself towards the golf hole.', 'Swinging the club is just like throwing a pie.')} +FishGenusNames = {0: 'Balloon Fish', + 2: 'Cat Fish', + 4: 'Clown Fish', + 6: 'Frozen Fish', + 8: 'Star Fish', + 10: 'Holey Mackerel', + 12: 'Dog Fish', + 14: 'Amore Eel', + 16: 'Nurse Shark', + 18: 'King Crab', + 20: 'Moon Fish', + 22: 'Sea Horse', + 24: 'Pool Shark', + 26: 'Bear Acuda', + 28: 'Cutthroat Trout', + 30: 'Piano Tuna', + 32: 'Peanut Butter & Jellyfish', + 34: 'Devil Ray'} +FishSpeciesNames = {0: ('Balloon Fish', + 'Hot Air Balloon Fish', + 'Weather Balloon Fish', + 'Water Balloon Fish', + 'Red Balloon Fish'), + 2: ('Cat Fish', + 'Siamese Cat Fish', + 'Alley Cat Fish', + 'Tabby Cat Fish', + 'Tom Cat Fish'), + 4: ('Clown Fish', + 'Sad Clown Fish', + 'Party Clown Fish', + 'Circus Clown Fish'), + 6: ('Frozen Fish',), + 8: ('Star Fish', + 'Five Star Fish', + 'Rock Star Fish', + 'Shining Star Fish', + 'All Star Fish'), + 10: ('Holey Mackerel',), + 12: ('Dog Fish', + 'Bull Dog Fish', + 'Hot Dog Fish', + 'Dalmatian Dog Fish', + 'Puppy Dog Fish'), + 14: ('Amore Eel', 'Electric Amore Eel'), + 16: ('Nurse Shark', 'Clara Nurse Shark', 'Florence Nurse Shark'), + 18: ('King Crab', 'Alaskan King Crab', 'Old King Crab'), + 20: ('Moon Fish', + 'Full Moon Fish', + 'Half Moon Fish', + 'New Moon Fish', + 'Crescent Moon Fish', + 'Harvest Moon Fish'), + 22: ('Sea Horse', + 'Rocking Sea Horse', + 'Clydesdale Sea Horse', + 'Arabian Sea Horse'), + 24: ('Pool Shark', + 'Kiddie Pool Shark', + 'Swimming Pool Shark', + 'Olympic Pool Shark'), + 26: ('Brown Bear Acuda', + 'Black Bear Acuda', + 'Koala Bear Acuda', + 'Honey Bear Acuda', + 'Polar Bear Acuda', + 'Panda Bear Acuda', + 'Kodiac Bear Acuda', + 'Grizzly Bear Acuda'), + 28: ('Cutthroat Trout', 'Captain Cutthroat Trout', 'Scurvy Cutthroat Trout'), + 30: ('Piano Tuna', + 'Grand Piano Tuna', + 'Baby Grand Piano Tuna', + 'Upright Piano Tuna', + 'Player Piano Tuna'), + 32: ('Peanut Butter & Jellyfish', + 'Grape PB&J Fish', + 'Crunchy PB&J Fish', + 'Strawberry PB&J Fish', + 'Concord Grape PB&J Fish'), + 34: ('Devil Ray',)} +CogPartNames = ('Upper Left Leg', + 'Lower Left Leg', + 'Left Foot', + 'Upper Right Leg', + 'Lower Right Leg', + 'Right Foot', + 'Left Shoulder', + 'Right Shoulder', + 'Chest', + 'Health Meter', + 'Pelvis', + 'Upper Left Arm', + 'Lower Left Arm', + 'Left Hand', + 'Upper Right Arm', + 'Lower Right Arm', + 'Right Hand') +CogPartNamesSimple = ('Upper Torso',) +SellbotLegFactorySpecMainEntrance = 'Front Entrance' +SellbotLegFactorySpecLobby = 'Lobby' +SellbotLegFactorySpecLobbyHallway = 'Lobby Hallway' +SellbotLegFactorySpecGearRoom = 'Gear Room' +SellbotLegFactorySpecBoilerRoom = 'Boiler Room' +SellbotLegFactorySpecEastCatwalk = 'East Catwalk' +SellbotLegFactorySpecPaintMixer = 'Paint Mixer' +SellbotLegFactorySpecPaintMixerStorageRoom = 'Paint Mixer Storage Room' +SellbotLegFactorySpecWestSiloCatwalk = 'West Silo Catwalk' +SellbotLegFactorySpecPipeRoom = 'Pipe Room' +SellbotLegFactorySpecDuctRoom = 'Duct Room' +SellbotLegFactorySpecSideEntrance = 'Side Entrance' +SellbotLegFactorySpecStomperAlley = 'Stomper Alley' +SellbotLegFactorySpecLavaRoomFoyer = 'Lava Room Foyer' +SellbotLegFactorySpecLavaRoom = 'Lava Room' +SellbotLegFactorySpecLavaStorageRoom = 'Lava Storage Room' +SellbotLegFactorySpecWestCatwalk = 'West Catwalk' +SellbotLegFactorySpecOilRoom = 'Oil Room' +SellbotLegFactorySpecLookout = 'Lookout' +SellbotLegFactorySpecWarehouse = 'Warehouse' +SellbotLegFactorySpecOilRoomHallway = 'Oil Room Hallway' +SellbotLegFactorySpecEastSiloControlRoom = 'East Silo Control Room' +SellbotLegFactorySpecWestSiloControlRoom = 'West Silo Control Room' +SellbotLegFactorySpecCenterSiloControlRoom = 'Center Silo Control Room' +SellbotLegFactorySpecEastSilo = 'East Silo' +SellbotLegFactorySpecWestSilo = 'West Silo' +SellbotLegFactorySpecCenterSilo = 'Center Silo' +SellbotLegFactorySpecEastSiloCatwalk = 'East Silo Catwalk' +SellbotLegFactorySpecWestElevatorShaft = 'West Elevator Shaft' +SellbotLegFactorySpecEastElevatorShaft = 'East Elevator Shaft' +FishBingoBingo = 'BINGO!' +FishBingoVictory = 'VICTORY!!' +FishBingoJackpot = 'JACKPOT!' +FishBingoGameOver = 'GAME OVER' +FishBingoIntermission = 'Intermission\nEnds In:' +FishBingoNextGame = 'Next Game\nStarts In:' +FishBingoTypeNormal = 'Classic' +FishBingoTypeCorners = 'Four Corners' +FishBingoTypeDiagonal = 'Diagonals' +FishBingoTypeThreeway = 'Three Way' +FishBingoTypeBlockout = 'BLOCKOUT!' +SillySaturdayStart = "It's time for Silly Saturday! Saturdays are silly with Fish Bingo and Grand Prix throughout the day!" +SillySaturdayOngoing = 'Welcome! Silly Saturday is currently in progress.' +SillySaturdayEnd = 'Silly Saturday is over. Hope you had fun. See you next week!' +AprilToonsWeekStart = "It's April Toons Week!" +AprilToonsWeekEnd = 'April Toons Week is over. See you next year!' +FishBingoStart = "It's time for Fish Bingo! Go to any available pier to play!" +FishBingoOngoing = 'Welcome! Fish Bingo is currently in progress.' +FishBingoEnd = 'Hope you had fun playing Fish Bingo.' +FishBingoHelpMain = 'Welcome to Toontown Fish Bingo! Everyone at the pond works together to fill the card before time runs out.' +FishBingoHelpFlash = 'When you catch a fish, click on one of the flashing squares to mark the card.' +FishBingoHelpNormal = 'This is a Classic Bingo card. Mark any row down, across or diagonally to win.' +FishBingoHelpDiagonals = 'Mark both of the diagonals to win.' +FishBingoHelpCorners = 'An easy Corners card. Mark all four corners to win.' +FishBingoHelpThreeway = "Three-way. Mark both diagonals and the middle row to win. This one isn't easy!" +FishBingoHelpBingo = 'Bingo!' +FishBingoHelpBlockout = 'Blockout!. Mark the entire card to win. You are competing against all the other ponds for a huge jackpot!' +FishBingoOfferToSellFish = 'Your fish bucket is full. Would you like to sell your fish?' +FishBingoJackpotWin = 'Win %s Jellybeans!' +ResistanceToonupMenu = 'Toon-up' +ResistanceToonupItem = '%s Toon-up' +ResistanceToonupItemMax = 'Max' +ResistanceToonupChat = 'Toons of the World, Toon-up!' +ResistanceRestockMenu = 'Gag-up' +ResistanceRestockItem = 'Gag-up %s' +ResistanceRestockItemAll = 'All' +ResistanceRestockChat = 'Toons of the World, Gag-up!' +ResistanceMeritsMenu = 'Merits' +ResistanceMeritsItem = 'Merit-up %s' +ResistanceMeritsChat = 'Toons of the World, Merit-up!' +ResistanceMoneyMenu = 'Jellybeans' +ResistanceMoneyItem = '%s Jellybeans' +ResistanceMoneyChat = 'Toons of the World, Spend Wisely!' +ResistanceTicketsMenu = 'Tickets' +ResistanceTicketsItem = '%s Tickets' +ResistanceTicketsChat = 'Toons of the World, Go Race!' +ResistanceEmote1 = NPCToonNames[9228] + ': Welcome to the Resistance!' +ResistanceEmote2 = NPCToonNames[9228] + ': Use your new emote to identify yourself to other members.' +ResistanceEmote3 = NPCToonNames[9228] + ': Good luck!' +KartUIExit = 'Leave Kart' +KartShop_Cancel = lCancel +KartShop_BuyKart = 'Buy Kart' +KartShop_BuyAccessories = 'Buy Accessories' +KartShop_BuyAccessory = 'Buy Accessory' +KartShop_Cost = 'Cost: %d Tickets' +KartShop_ConfirmBuy = 'Buy the %s for %d Tickets?' +KartShop_NoAvailableAcc = 'No available accessories of this type' +KartShop_FullTrunk = 'Your trunk is full.' +KartShop_ConfirmReturnKart = 'Are you sure you want to return your current Kart?' +KartShop_ConfirmBoughtTitle = 'Congratulations!' +KartShop_NotEnoughTickets = 'Not Enough Tickets!' +KartView_Rotate = 'Rotate' +KartView_Right = 'Right' +KartView_Left = 'Left' +StartingBlock_NotEnoughTickets = "You don't have enough tickets! Try a practice race instead." +StartingBlock_NoBoard = 'Boarding has ended for this race. Please wait for the next race to begin.' +StartingBlock_NoKart = 'You need a kart first! Try asking one of the clerks in the Kart Shop.' +StartingBlock_Occupied = 'This block is currently occupied! Please try another spot.' +StartingBlock_TrackClosed = 'Sorry, this track is closed for remodeling.' +StartingBlock_EnterPractice = 'Would you like to enter a practice race?' +StartingBlock_EnterNonPractice = 'Would you like to enter a %s race for %s tickets?' +StartingBlock_EnterShowPad = 'Would you like to park your car here?' +StartingBlock_KickSoloRacer = 'Toon Battle and Grand Prix races require two or more racers.' +StartingBlock_Loading = 'Goofy Speedway' +LeaderBoard_Time = 'Time' +LeaderBoard_Name = 'Racer Name' +LeaderBoard_Daily = 'Daily Scores' +LeaderBoard_Weekly = 'Weekly Scores' +LeaderBoard_AllTime = 'All Time Best Scores' +RecordPeriodStrings = [LeaderBoard_Daily, LeaderBoard_Weekly, LeaderBoard_AllTime] +KartRace_RaceNames = ['Practice', 'Toon Battle', 'Grand Prix'] +from toontown.racing import RaceGlobals +KartRace_Go = 'Go!' +KartRace_Reverse = ' Rev' +KartRace_TrackNames = {RaceGlobals.RT_Speedway_1: 'Screwball Stadium', + RaceGlobals.RT_Speedway_1_rev: 'Screwball Stadium' + KartRace_Reverse, + RaceGlobals.RT_Rural_1: 'Rustic Raceway', + RaceGlobals.RT_Rural_1_rev: 'Rustic Raceway' + KartRace_Reverse, + RaceGlobals.RT_Urban_1: 'City Circuit', + RaceGlobals.RT_Urban_1_rev: 'City Circuit' + KartRace_Reverse, + RaceGlobals.RT_Speedway_2: 'Corkscrew Coliseum', + RaceGlobals.RT_Speedway_2_rev: 'Corkscrew Coliseum' + KartRace_Reverse, + RaceGlobals.RT_Rural_2: 'Airborne Acres', + RaceGlobals.RT_Rural_2_rev: 'Airborne Acres' + KartRace_Reverse, + RaceGlobals.RT_Urban_2: 'Blizzard Boulevard', + RaceGlobals.RT_Urban_2_rev: 'Blizzard Boulevard' + KartRace_Reverse} +KartRace_Unraced = 'N/A' +KartDNA_KartNames = {0: 'Cruiser', + 1: 'Roadster', + 2: 'Toon Utility Vehicle'} +KartDNA_AccNames = {1000: 'Air Cleaner', + 1001: 'Four Barrel', + 1002: 'Flying Eagle', + 1003: 'Steer Horns', + 1004: 'Straight Six', + 1005: 'Small Scoop', + 1006: 'Single Overhead', + 1007: 'Medium Scoop', + 1008: 'Single Barrel', + 1009: 'Flugle Horn', + 1010: 'Striped Scoop', + 2000: 'Space Wing', + 2001: 'Patched Spare', + 2002: 'Roll Cage', + 2003: 'Single Fin', + 2004: 'Double-decker Wing', + 2005: 'Single Wing', + 2006: 'Standard Spare', + 2007: 'Single Fin', + 2008: 'sp9', + 2009: 'sp10', + 3000: 'Dueling Horns', + 3001: "Freddie's Fenders", + 3002: 'Cobalt Running Boards', + 3003: 'Cobra Sidepipes', + 3004: 'Straight Sidepipes', + 3005: 'Scalloped Fenders', + 3006: 'Carbon Running Boards', + 3007: 'Wood Running Boards', + 3008: 'fw9', + 3009: 'fw10', + 4000: 'Curly Tailpipes', + 4001: 'Splash Fenders', + 4002: 'Dual Exhaust', + 4003: 'Plain Dual Fins', + 4004: 'Plain Mudflaps', + 4005: 'Quad Exhaust', + 4006: 'Dual Flares', + 4007: 'Mega Exhaust', + 4008: 'Striped Dual Fins', + 4009: 'Bubble Duals Fins', + 4010: 'Striped Mudflaps', + 4011: 'Mickey Mudflaps', + 4012: 'Scalloped Mudflaps', + 5000: 'Turbo', + 5001: 'Moon', + 5002: 'Patched', + 5003: 'Three Spoke', + 5004: 'Paint Lid', + 5005: 'Heart', + 5006: 'Mickey', + 5007: 'Five Bolt', + 5008: 'Daisy', + 5009: 'Basketball', + 5010: 'Hypno', + 5011: 'Tribal', + 5012: 'Gemstone', + 5013: 'Five Spoke', + 5014: 'Knockoff', + 6000: 'Number Five', + 6001: 'Splatter', + 6002: 'Checkerboard', + 6003: 'Flames', + 6004: 'Hearts', + 6005: 'Bubbles', + 6006: 'Tiger', + 6007: 'Flowers', + 6008: 'Lightning', + 6009: 'Angel', + 7000: 'Chartreuse', + 7001: 'Peach', + 7002: 'Bright Red', + 7003: 'Red', + 7004: 'Maroon', + 7005: 'Sienna', + 7006: 'Brown', + 7007: 'Tan', + 7008: 'Coral', + 7009: 'Orange', + 7010: 'Yellow', + 7011: 'Cream', + 7012: 'Citrine', + 7013: 'Lime', + 7014: 'Sea Green', + 7015: 'Green', + 7016: 'Light Blue', + 7017: 'Aqua', + 7018: 'Blue', + 7019: 'Periwinkle', + 7020: 'Royal Blue', + 7021: 'Slate Blue', + 7022: 'Purple', + 7023: 'Lavender', + 7024: 'Pink', + 7025: 'Plum', + 7026: 'Black'} +RaceHoodSpeedway = 'Speedway' +RaceHoodRural = 'Rural' +RaceHoodUrban = 'Urban' +RaceTypeCircuit = 'Tournament' +RaceQualified = 'qualified' +RaceSwept = 'swept' +RaceWon = 'won' +Race = 'race' +Races = 'races' +Total = 'total' +GrandTouring = 'Grand Touring' + +def getTrackGenreString(genreId): + genreStrings = ['Speedway', 'Country', 'City'] + return genreStrings[genreId].lower() + + +def getTunnelSignName(trackId, padId): + if trackId == 2 and padId == 0: + return 'tunne1l_citysign' + elif trackId == 1 and padId == 0: + return 'tunnel_countrysign1' + else: + genreId = RaceGlobals.getTrackGenre(trackId) + return 'tunnel%s_%ssign' % (padId + 1, RaceGlobals.getTrackGenreString(genreId)) + + +KartTrophyDescriptions = [str(RaceGlobals.QualifiedRaces[0]) + ' ' + RaceHoodSpeedway + ' ' + Race + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[1]) + ' ' + RaceHoodSpeedway + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[2]) + ' ' + RaceHoodSpeedway + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[0]) + ' ' + RaceHoodRural + ' ' + Race + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[1]) + ' ' + RaceHoodRural + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[2]) + ' ' + RaceHoodRural + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[0]) + ' ' + RaceHoodUrban + ' ' + Race + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[1]) + ' ' + RaceHoodUrban + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[2]) + ' ' + RaceHoodUrban + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.TotalQualifiedRaces) + ' ' + Total + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.WonRaces[0]) + ' ' + RaceHoodSpeedway + ' ' + Race + ' ' + RaceWon, + str(RaceGlobals.WonRaces[1]) + ' ' + RaceHoodSpeedway + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonRaces[2]) + ' ' + RaceHoodSpeedway + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonRaces[0]) + ' ' + RaceHoodRural + ' ' + Race + ' ' + RaceWon, + str(RaceGlobals.WonRaces[1]) + ' ' + RaceHoodRural + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonRaces[2]) + ' ' + RaceHoodRural + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonRaces[0]) + ' ' + RaceHoodUrban + ' ' + Race + ' ' + RaceWon, + str(RaceGlobals.WonRaces[1]) + ' ' + RaceHoodUrban + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonRaces[2]) + ' ' + RaceHoodUrban + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.TotalWonRaces) + ' ' + Total + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonCircuitRaces[0]) + ' ' + RaceTypeCircuit + ' ' + Race + ' ' + RaceQualified, + str(RaceGlobals.WonCircuitRaces[1]) + ' ' + RaceTypeCircuit + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.WonCircuitRaces[2]) + ' ' + RaceTypeCircuit + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.WonCircuitRaces[0]) + ' ' + RaceTypeCircuit + ' ' + Race + ' ' + RaceWon, + str(RaceGlobals.WonCircuitRaces[1]) + ' ' + RaceTypeCircuit + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonCircuitRaces[2]) + ' ' + RaceTypeCircuit + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.SweptCircuitRaces[0]) + ' ' + RaceTypeCircuit + ' ' + Race + ' ' + RaceSwept, + str(RaceGlobals.SweptCircuitRaces[1]) + ' ' + RaceTypeCircuit + ' ' + Races + ' ' + RaceSwept, + str(RaceGlobals.SweptCircuitRaces[2]) + ' ' + RaceTypeCircuit + ' ' + Races + ' ' + RaceSwept, + GrandTouring, + str(RaceGlobals.TrophiesPerCup) + ' Kart Racing trophies won! Laff point boost!', + str(RaceGlobals.TrophiesPerCup * 2) + ' Kart Racing trophies won! Laff point boost!', + str(RaceGlobals.TrophiesPerCup * 3) + ' Kart Racing trophies won! Laff point boost!'] +KartRace_TitleInfo = 'Get Ready to Race' +KartRace_SSInfo = 'Welcome to Screwball Stadium!\nPut the pedal to the metal and hang on tight!\n' +KartRace_CoCoInfo = 'Welcome to Corkscrew Coliseum!\nUse the banked turns to keep your speed up!\n' +KartRace_RRInfo = 'Welcome to Rustic Raceway!\nPlease be kind to the fauna and stay on the track!\n' +KartRace_AAInfo = 'Welcome to Airborne Acres!\nHold onto your hats! It looks bumpy up ahead...\n' +KartRace_CCInfo = 'Welcome to City Circuit!\nWatch out for pedestrians as you speed through downtown!\n' +KartRace_BBInfo = 'Welcome to Blizzard Boulevard!\nWatch your speed. There might be ice out there.\n' +KartRace_GeneralInfo = 'Use Control to throw gags you pick up on the track, and the arrow keys to control your kart.' +KartRace_TrackInfo = {RaceGlobals.RT_Speedway_1: KartRace_SSInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Speedway_1_rev: KartRace_SSInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Speedway_2: KartRace_CoCoInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Speedway_2_rev: KartRace_CoCoInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Rural_1: KartRace_RRInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Rural_1_rev: KartRace_RRInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Rural_2: KartRace_AAInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Rural_2_rev: KartRace_AAInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Urban_1: KartRace_CCInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Urban_1_rev: KartRace_CCInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Urban_2: KartRace_BBInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Urban_2_rev: KartRace_BBInfo + KartRace_GeneralInfo} +KartRecordStrings = {RaceGlobals.Daily: 'daily', + RaceGlobals.Weekly: 'weekly', + RaceGlobals.AllTime: 'all time'} +KartRace_FirstSuffix = 'st' +KartRace_SecondSuffix = ' nd' +KartRace_ThirdSuffix = ' rd' +KartRace_FourthSuffix = ' th' +KartRace_WrongWay = 'Wrong\nWay!' +KartRace_LapText = 'Lap %s' +KartRace_FinalLapText = 'Final Lap!' +KartRace_Exit = 'Exit Race' +KartRace_NextRace = 'Next Race' +KartRace_Leave = 'Leave Race' +KartRace_Qualified = 'Qualified!' +KartRace_Record = 'Record!' +KartRace_RecordString = 'You have set a new %s record for %s! Your bonus is %s tickets.' +KartRace_Tickets = 'Tickets' +KartRace_Exclamations = '!' +KartRace_Deposit = 'Deposit' +KartRace_Winnings = 'Winnings' +KartRace_Bonus = 'Bonus' +KartRace_RaceTotal = 'Race Total' +KartRace_CircuitTotal = 'Circuit Total' +KartRace_Trophies = 'Trophies' +KartRace_Zero = '0' +KartRace_Colon = ':' +KartRace_TicketPhrase = '%s ' + KartRace_Tickets +KartRace_DepositPhrase = KartRace_Deposit + KartRace_Colon + '\n' +KartRace_QualifyPhrase = 'Qualify:\n' +KartRace_RaceTimeout = 'You timed out of that race. Your tickets have been refunded. Keep trying!' +KartRace_RaceTimeoutNoRefund = 'You timed out of that race. Your tickets have not been refunded because the Grand Prix had already started. Keep trying!' +KartRace_RacerTooSlow = 'You took too long to finish the race. Your tickets have not been refunded. Keep trying!' +KartRace_PhotoFinish = 'Photo Finish!' +KartRace_CircuitPoints = 'Circuit Points' +CircuitRaceStart = 'The Toontown Grand Prix at Goofy Speedway is about to begin! To win, collect the most points in three consecutive races!' +CircuitRaceOngoing = 'Welcome! The Toontown Grand Prix is currently in progress.' +CircuitRaceEnd = "That's all for today's Toontown Grand Prix at Goofy Speedway. See you next week!" +TrickOrTreatMsg = 'You have already\nfound this treat!' +WinterCarolingMsg = 'You have already been caroling here!' +LawbotBossTempIntro0 = "Hrmmm, what's on the docket for today?" +LawbotBossTempIntro1 = 'Aha, we have a Toon on trial!' +LawbotBossTempIntro2 = "The prosecution's case is strong..." +LawbotBossTempIntro3 = "Hey, your honorable blindness, you're looking the wrong way!" +LawbotBossTempIntro4 = 'I may be blind...' +LawbotBossTempIntro5 = 'But Justice is NOT!' +LawbotBossTempIntro6 = 'I should have known you Toons would try to upset this trial.' +LawbotBossTempJury1 = 'Jury selection will now commence.' +LawbotBossHowToGetEvidence = 'Touch the witness stand to get evidence.' +LawbotBossTrialChat1 = 'Court is now in session' +LawbotBossHowToThrowPies = 'Press the Delete key to throw the evidence\n at the lawyers or into the scale!' +LawbotBossNeedMoreEvidence = 'You need to get more evidence!' +LawbotBossDefenseWins1 = 'Impossible! The defense won?' +LawbotBossDefenseWins2 = 'No. I declare a mistrial! A new one will be scheduled.' +LawbotBossDefenseWins3 = "Hrrmpphh. I'll be in my chambers." +LawbotBossProsecutionWins = 'I find in favor of the plaintiff!' +LawbotBossReward = 'I award a promotion and the ability to summon Cogs' +LawbotBossLeaveCannon = 'Leave cannon' +LawbotBossPassExam = 'Bah, so you passed the bar exam.' +LawbotBossTaunts = ['%s, I find you in contempt of court!', + 'Objection sustained!', + 'Strike that from the record.', + 'Your appeal has been rejected. I sentence you to sadness!', + 'Order in the court!'] +WitnessToonPrepareBattleTwo = "Oh no! They're putting only Cogs on the jury!\x07Quick, use the cannons and shoot some Toon jurors into the jury chairs.\x07We need %d to get a balanced scale." +WitnessToonNoJuror = 'Oh oh, no Toon jurors. This will be a tough trial.' +WitnessToonOneJuror = 'Cool! There is 1 Toon in the jury!' +WitnessToonSomeJurors = 'Cool! There are %d Toons in the jury!' +WitnessToonAllJurors = 'Awesome! All the jurors are Toons!' +WitnessToonPrepareBattleThree = 'Hurry, touch the witness stand to get evidence.\x07Press the Delete key to throw the evidence at the lawyers, or at the defense pan.' +WitnessToonCongratulations = "You did it! Thank you for a spectacular defense!\x07Here, take these papers the Chief Justice left behind.\x07With it you'll be able to summon Cogs from your Cog Gallery page." +WitnessToonLastPromotion = "\x07Wow, you've reached level %s on your Cog Suit!\x07Cogs don't get promoted higher than that.\x07You can't upgrade your Cog Suit anymore, but you can certainly keep working for the Resistance!" +WitnessToonHPBoost = "\x07You've done a lot of work for the Resistance.\x07The Toon Council has decided to give you another Laff point. Congratulations!" +WitnessToonMaxed = '\x07I see that you have a level %s Cog Suit. Very impressive!\x07On behalf of the Toon Council, thank you for coming back to defend more Toons!' +WitnessToonBonus = 'Wonderful! All the lawyers are stunned. Your evidence weight is %s times heavier for %s seconds' +WitnessToonJuryWeightBonusSingular = {6: 'This is a tough case. You seated %d Toon juror, so your evidence has a bonus weight of %d.', + 7: 'This is a very tough case. You seated %d Toon juror, so your evidence has a bonus weight of %d.', + 8: 'This is the toughest case. You seated %d Toon juror, so your evidence has a bonus weight of %d.'} +WitnessToonJuryWeightBonusPlural = {6: 'This is a tough case. You seated %d Toon jurors, so your evidence has a bonus weight of %d.', + 7: 'This is a very tough case. You seated %d Toon jurors, so your evidence has a bonus weight of %d.', + 8: 'This is the toughest case. You seated %d Toon jurors, so your evidence has a bonus weight of %d.'} +IssueSummons = 'Summon' +SummonDlgTitle = 'Issue a Cog Summon' +SummonDlgButton1 = 'Summon a Cog Building' +SummonDlgButton2 = 'Summon a Cog Invasion' +SummonDlgButton3 = 'Summon a Field Office' +SummonDlgButton4 = 'Summon a Skelecog Invasion' +SummonDlgButton5 = 'Summon a Waiter Invasion' +SummonDlgButton6 = 'Summon a Version 2.0 Invasion' +SummonDlgBuildingConf = 'Would you like to summon a %s to a nearby Toon building?' +SummonDlgInvasionConf = 'Would you like to summon a %s invasion?' +SummonDlgSkelInvasionConf = 'Would you like to summon a %s (Skelecog) invasion?' +SummonDlgWaiterInvasionConf = 'Would you like to summon a %s (Waiter) invasion?' +SummonDlgV2InvasionConf = 'Would you like to summon a Version 2.0 %s invasion?' +SummonDlgNumLeft = 'You have %s left.' +SummonDlgDelivering = 'Delivering Summons...' +SummonDlgBldgSuccess = 'You have successfully summoned the Cogs. %s has agreed to let them temporarily take over %s!' +SummonDlgBldgSuccess2 = 'You have successfully summoned the Cogs. A Shopkeeper has agreed to let them temporarily take over their building!' +SummonDlgBldgBadLoc = 'Sorry, there are no Toon buildings nearby for the Cogs to take over.' +SummonDlgBldgNoCogdos = "Sorry, but you can't summon a Field Office right now. Please try again later." +SummonDlgInvasionSuccess = "You have successfully summoned the Cogs. It's an invasion!" +SummonDlgInvasionBusy = 'A %s cannot be found now. Try again when the Cog invasion is over.' +SummonDlgInvasionFail = 'Sorry, the Cog invasion has failed.' +SummonDlgShopkeeper = 'The Shopkeeper ' +PolarPlaceEffect1 = NPCToonNames[3306] + ': Welcome to Polar Place!' +PolarPlaceEffect2 = NPCToonNames[3306] + ': Try this on for size.' +PolarPlaceEffect3 = NPCToonNames[3306] + ': Your new look will only work in ' + lTheBrrrgh + '.' +GreenToonEffectMsg = NPCToonNames[5312] + ': You look Toontastic in green!' +LaserGameMine = 'Skull Finder!' +LaserGameRoll = 'Matching' +LaserGameAvoid = 'Avoid the Skulls' +LaserGameDrag = 'Drag three of a color in a row' +LaserGameDefault = 'Unknown Game' +PinballHiScore = 'High Score: %s\n' +PinballHiScoreAbbrev = '...' +PinballYourBestScore = 'Your Best Score:\n' +PinballScore = 'Score: %d x %d = ' +PinballScoreHolder = '%s\n' +GagTreeFeather = 'Feather Gag Tree' +GagTreeJugglingBalls = 'Juggling Balls Gag Tree' +StatuaryFountain = 'Fountain' +StatuaryDonald = 'Donald Statue' +StatuaryMinnie = 'Minnie Statue' +StatuaryMickey1 = 'Mickey Statue' +StatuaryMickey2 = 'Mickey Fountain' +StatuaryToon = 'Toon Statue' +StatuaryToonWave = 'Toon Wave Statue' +StatuaryToonVictory = 'Toon Victory Statue' +StatuaryToonCrossedArms = 'Toon Authority Statue' +StatuaryToonThinking = 'Toon Embrace Statue' +StatuaryMeltingSnowman = 'Melting Snowman' +StatuaryMeltingSnowDoodle = 'Melting SnowDoodle' +StatuaryGardenAccelerator = 'Insta-Grow Fertilizer' +AnimatedStatuaryFlappyCog = 'Flappy Cog' +FlowerColorStrings = ['Red', + 'Orange', + 'Violet', + 'Blue', + 'Pink', + 'Yellow', + 'White', + 'Green'] +FlowerSpeciesNames = {49: 'Daisy', + 50: 'Tulip', + 51: 'Carnation', + 52: 'Lily', + 53: 'Daffodil', + 54: 'Pansy', + 55: 'Petunia', + 56: 'Rose'} +FlowerFunnyNames = {49: ('School Daisy', + 'Lazy Daisy', + 'Midsummer Daisy', + 'Freshasa Daisy', + 'Whoopsie Daisy', + 'Upsy Daisy', + 'Crazy Daisy', + 'Hazy Dazy'), + 50: ('Onelip', 'Twolip', 'Threelip'), + 51: ('What-in Carnation', + 'Instant Carnation', + 'Hybrid Carnation', + 'Side Carnation', + 'Model Carnation'), + 52: ('Lily-of-the-Alley', + 'Lily Pad', + 'Tiger Lily', + 'Livered Lily', + 'Chili Lily', + 'Silly Lily', + 'Indubitab Lily', + 'Dilly Lilly'), + 53: ('Laff-o-dil', + 'Daffy Dill', + 'Giraff-o-dil', + 'Time and a half-o-dil'), + 54: ('Dandy Pansy', + 'Chim Pansy', + 'Potsen Pansy', + 'Marzi Pansy', + 'Smarty Pansy'), + 55: ('Car Petunia', 'Platoonia'), + 56: ("Summer's Last Rose", + 'Corn Rose', + 'Tinted Rose', + 'Stinking Rose', + 'Istilla Rose')} +FlowerVarietyNameFormat = '%s %s' +FlowerUnknown = '????' +FloweringNewEntry = 'New Entry' +ShovelNameDict = {0: 'Tin', + 1: 'Bronze', + 2: 'Silver', + 3: 'Gold'} +WateringCanNameDict = {0: 'Small', + 1: 'Medium', + 2: 'Large', + 3: 'Huge'} +GardeningPlant = 'Plant' +GardeningWater = 'Water' +GardeningRemove = 'Remove' +GardeningPick = 'Pick' +GardeningFull = 'Full' +GardeningSkill = 'Skill' +GardeningWaterSkill = 'Water Skill' +GardeningShovelSkill = 'Shovel Skill' +GardeningNoSkill = 'No Skill Up' +GardeningPlantFlower = 'Plant\nFlower' +GardeningPlantTree = 'Plant\nTree' +GardeningPlantItem = 'Plant\nItem' +PlantingGuiOk = 'Plant' +PlantingGuiCancel = 'Cancel' +PlantingGuiReset = 'Reset' +GardeningChooseBeans = 'Choose the Jellybeans you want to plant.' +GardeningChooseBeansItem = 'Choose the Jellybeans / item you want to plant.' +GardeningChooseToonStatue = 'Choose the toon you want to create a statue of.' +GardenShovelLevelUp = "Congratulations you've earned a %(shovel)s! You've mastered the %(oldbeans)d bean flower! To progress you should pick %(newbeans)d bean flowers." +GardenShovelSkillLevelUp = "Congratulations! You've mastered the %(oldbeans)d bean flower! To progress you should pick %(newbeans)d bean flowers." +GardenShovelSkillMaxed = "Amazing! You've maxed out your shovel skill!" +GardenWateringCanLevelUp = "Congratulations you've earned a new watering can!" +GardenMiniGameWon = "Congratulations you've watered the plant!" +ShovelTin = 'Tin Shovel' +ShovelSteel = 'Bronze Shovel' +ShovelSilver = 'Silver Shovel' +ShovelGold = 'Gold Shovel' +WateringCanSmall = 'Small Watering Can' +WateringCanMedium = 'Medium Watering Can' +WateringCanLarge = 'Large Watering Can' +WateringCanHuge = 'Huge Watering Can' +BeanColorWords = ('red', + 'green', + 'orange', + 'violet', + 'blue', + 'pink', + 'yellow', + 'cyan', + 'silver') +PlantItWith = ' Plant with %s.' +MakeSureWatered = ' Make sure all your plants are watered first.' +UseFromSpecialsTab = ' Use from the specials tab of the garden page.' +UseSpecial = 'Use Special' +UseSpecialBadLocation = 'You can only use that in your garden.' +UseSpecialSuccess = 'Success! Your watered plants just grew.' +ConfirmWiltedFlower = '%(plant)s is wilted. Are you sure you want to remove it? It will not go into your flower basket, nor will you get an increase in skill.' +ConfirmUnbloomingFlower = '%(plant)s is not blooming. Are you sure you want to remove it? It will not go into your flower basket, nor will you get an increase in skill.' +ConfirmNoSkillupFlower = 'Are you sure you want to pick the %(plant)s? It will go into your flower basket, but you will NOT get an increase in skill.' +ConfirmSkillupFlower = 'Are you sure you want to pick the %(plant)s? It will go into your flower basket. You will also get an increase in skill.' +ConfirmMaxedSkillFlower = "Are you sure you want to pick the %(plant)s? It will go into your flower basket. You will NOT get an increase in skill since you've maximized it already." +ConfirmBasketFull = 'Your flower basket is full. Sell some flowers first.' +ConfirmRemoveTree = 'Are you sure you want to remove the %(tree)s?' +ConfirmWontBeAbleToHarvest = " If you remove this tree, you won't be able to harvest gags from the higher level trees." +ConfirmRemoveStatuary = 'Are you sure you want to permanently delete the %(item)s?' +ResultPlantedSomething = 'Congratulations! You just planted a %s.' +ResultPlantedSomethingAn = 'Congratulations! You just planted an %s.' +ResultPlantedNothing = "That didn't work. Please try a different combination of Jellybeans." +GardenGagTree = ' Gag Tree' +GardenUberGag = 'Uber Gag' + +def getRecipeBeanText(beanTuple): + retval = '' + if not beanTuple: + return retval + allTheSame = True + for index in xrange(len(beanTuple)): + if index + 1 < len(beanTuple): + if not beanTuple[index] == beanTuple[index + 1]: + allTheSame = False + break + + if allTheSame: + if len(beanTuple) > 1: + retval = '%d %s Jellybeans' % (len(beanTuple), BeanColorWords[beanTuple[0]]) + else: + retval = 'a %s Jellybean' % BeanColorWords[beanTuple[0]] + else: + retval += 'a' + maxBeans = len(beanTuple) + for index in xrange(maxBeans): + if index == maxBeans - 1: + retval += ' and %s Jellybean' % BeanColorWords[beanTuple[index]] + elif index == 0: + retval += ' %s' % BeanColorWords[beanTuple[index]] + else: + retval += ', %s' % BeanColorWords[beanTuple[index]] + + return retval + + +GardenTextMagicBeans = 'Magic Beans' +GardenTextMagicBeansB = 'Some Other Beans' +GardenSpecialDiscription = 'This text should explain how to use a certain garden special' +GardenSpecialDiscriptionB = 'This text should explain how to use a certain garden special, in yo face foo!' +GardenTrophyAwarded = 'Wow! You collected %s of %s flowers. That deserves a trophy and a Laff boost!' +GardenTrophyNameDict = {0: 'Wheelbarrow', + 1: 'Shovels', + 2: 'Flower', + 3: 'Watering Can', + 4: 'Shark', + 5: 'Swordfish', + 6: 'Killer Whale'} +SkillTooLow = 'Skill\nToo Low' +NoGarden = 'No\nGarden' + +def isVowelStart(str): + retval = False + if str and len(str) > 0: + vowels = ['A', + 'E', + 'I', + 'O', + 'U'] + firstLetter = str.upper()[0:1] + if firstLetter in vowels: + retval = True + return retval + + +def getResultPlantedSomethingSentence(flowerName): + if isVowelStart(flowerName): + retval = ResultPlantedSomethingAn % flowerName + else: + retval = ResultPlantedSomething % flowerName + return retval + +VineGameTitle = 'Jungle Vines' +VineGameInstructions = 'Get to the rightmost vine in time. Press Up or Down to climb the vine. Press Left or Right to change facing and jump. The lower you are on the vine, the faster you jump off. Collect the bananas if you can, but avoid the bats and spiders.' +ValentinesDayStart = "Happy ValenToon's Day!" +ValentinesDayEnd = "That's all for ValenToon's Day!" +GolfCourseNames = {0: 'Walk In The Par', + 1: 'Hole Some Fun', + 2: 'The Hole Kit And Caboodle'} +GolfHoleNames = {0: 'Whole In Won', + 1: 'No Putts About It', + 2: 'Down The Hatch', + 3: 'Seeing Green', + 4: 'Hot Links', + 5: 'Peanut Putter', + 6: 'Swing-A-Long', + 7: 'Afternoon Tee', + 8: 'Hole In Fun', + 9: 'Rock And Roll In', + 10: 'Bogey Nights', + 11: 'Tea Off Time', + 12: 'Holey Mackerel!', + 13: 'One Little Birdie', + 14: 'At The Drive In', + 15: 'Swing Time', + 16: 'Hole On The Range', + 17: 'Second Wind', + 18: 'Whole In Won-2', + 19: 'No Putts About It-2', + 20: 'Down The Hatch-2', + 21: 'Seeing Green-2', + 22: 'Hot Links-2', + 23: 'Peanut Putter-2', + 24: 'Swing-A-Long-2', + 25: 'Afternoon Tee-2', + 26: 'Hole In Fun-2', + 27: 'Rock And Roll In-2', + 28: 'Bogey Nights-2', + 29: 'Tea Off Time-2', + 30: 'Holey Mackerel!-2', + 31: 'One Little Birdie-2', + 32: 'At The Drive In-2', + 33: 'Swing Time-2', + 34: 'Hole On The Range-2', + 35: 'Second Wind-2'} +GolfHoleInOne = 'Hole In One' +GolfCondor = 'Condor' +GolfAlbatross = 'Albatross' +GolfEagle = 'Eagle' +GolfBirdie = 'Birdie' +GolfPar = 'Par' +GolfBogey = 'Bogey' +GolfDoubleBogey = 'Double Bogey' +GolfTripleBogey = 'Triple Bogey' +GolfShotDesc = {-4: GolfCondor, + -3: GolfAlbatross, + -2: GolfEagle, + -1: GolfBirdie, + 0: GolfPar, + 1: GolfBogey, + 2: GolfDoubleBogey, + 3: GolfTripleBogey} +from toontown.golf import GolfGlobals +CoursesCompleted = 'Courses Completed' +CoursesUnderPar = 'Courses Under Par' +HoleInOneShots = 'Hole In One Shots' +EagleOrBetterShots = 'Eagle Or Better Shots' +BirdieOrBetterShots = 'Birdie Or Better Shots' +ParOrBetterShots = 'Par Or Better Shots' +MultiPlayerCoursesCompleted = 'Multiplayer Courses Completed' +TwoPlayerWins = 'Two Player Wins' +ThreePlayerWins = 'Three Player Wins' +FourPlayerWins = 'Four Player Wins' +CourseZeroWins = GolfCourseNames[0] + ' Wins' +CourseOneWins = GolfCourseNames[1] + ' Wins' +CourseTwoWins = GolfCourseNames[2] + ' Wins' +GolfHistoryDescriptions = [CoursesCompleted, + CoursesUnderPar, + HoleInOneShots, + EagleOrBetterShots, + BirdieOrBetterShots, + ParOrBetterShots, + MultiPlayerCoursesCompleted, + CourseZeroWins, + CourseOneWins, + CourseTwoWins] +GolfTrophyDescriptions = [str(GolfGlobals.TrophyRequirements[GolfGlobals.CoursesCompleted][0]) + ' ' + CoursesCompleted, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CoursesCompleted][1]) + ' ' + CoursesCompleted, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CoursesCompleted][2]) + ' ' + CoursesCompleted, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CoursesUnderPar][0]) + ' ' + CoursesUnderPar, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CoursesUnderPar][1]) + ' ' + CoursesUnderPar, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CoursesUnderPar][2]) + ' ' + CoursesUnderPar, + str(GolfGlobals.TrophyRequirements[GolfGlobals.HoleInOneShots][0]) + ' ' + HoleInOneShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.HoleInOneShots][1]) + ' ' + HoleInOneShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.HoleInOneShots][2]) + ' ' + HoleInOneShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.EagleOrBetterShots][0]) + ' ' + EagleOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.EagleOrBetterShots][1]) + ' ' + EagleOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.EagleOrBetterShots][2]) + ' ' + EagleOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.BirdieOrBetterShots][0]) + ' ' + BirdieOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.BirdieOrBetterShots][1]) + ' ' + BirdieOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.BirdieOrBetterShots][2]) + ' ' + BirdieOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.ParOrBetterShots][0]) + ' ' + ParOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.ParOrBetterShots][1]) + ' ' + ParOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.ParOrBetterShots][2]) + ' ' + ParOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.MultiPlayerCoursesCompleted][0]) + ' ' + MultiPlayerCoursesCompleted, + str(GolfGlobals.TrophyRequirements[GolfGlobals.MultiPlayerCoursesCompleted][1]) + ' ' + MultiPlayerCoursesCompleted, + str(GolfGlobals.TrophyRequirements[GolfGlobals.MultiPlayerCoursesCompleted][2]) + ' ' + MultiPlayerCoursesCompleted, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseZeroWins][0]) + ' ' + CourseZeroWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseZeroWins][1]) + ' ' + CourseZeroWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseZeroWins][2]) + ' ' + CourseZeroWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseOneWins][0]) + ' ' + CourseOneWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseOneWins][1]) + ' ' + CourseOneWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseOneWins][2]) + ' ' + CourseOneWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseTwoWins][0]) + ' ' + CourseTwoWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseTwoWins][1]) + ' ' + CourseTwoWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseTwoWins][2]) + ' ' + CourseTwoWins] +GolfCupDescriptions = [str(GolfGlobals.TrophiesPerCup) + ' Trophies won', str(GolfGlobals.TrophiesPerCup * 2) + ' Trophies won', str(GolfGlobals.TrophiesPerCup * 3) + ' Trophies won'] +GolfAvReceivesHoleBest = '%(name)s scored a new hole best at %(hole)s!' +GolfAvReceivesCourseBest = '%(name)s scored a new course best at %(course)s!' +GolfAvReceivesCup = '%(name)s receives the %(cup)s cup!! Laff point boost!' +GolfAvReceivesTrophy = '%(name)s receives the %(award)s trophy!!' +GolfRanking = 'Ranking: \n' +GolfPowerBarText = '%(power)s%%' +GolfChooseTeeInstructions = 'Press Left or Right to change tee spot.\nPress Control to select.' +GolfWarningMustSwing = 'Warning: You must press Control on your next swing.' +GolfAimInstructions = 'Press Left or Right to aim.\nPress and hold Control to swing.' +GolferExited = '%s has left the golf course.' +GolfPowerReminder = 'Hold Down Control Longer to\nHit the Ball Further' +GolfPar = 'Par' +GolfHole = 'Hole' +GolfTotal = 'Total' +GolfExitCourse = 'Exit Course' +GolfUnknownPlayer = '???' +GolfPageTitle = 'Golf' +GolfPageTitleCustomize = 'Golf Customizer' +GolfPageTitleRecords = 'Personal Best Records' +GolfPageTitleTrophy = 'Golfing Trophies' +GolfPageCustomizeTab = 'Customize' +GolfPageRecordsTab = 'Records' +GolfPageTrophyTab = 'Trophy' +GolfPageTickets = 'Tickets : ' +GolfPageConfirmDelete = 'Delete Accessory?' +GolfTrophyTextDisplay = 'Trophy %(number)s : %(desc)s' +GolfCupTextDisplay = 'Cup %(number)s : %(desc)s' +GolfCurrentHistory = 'Current %(historyDesc)s : %(num)s' +GolfTieBreakWinner = '%(name)s wins the random tie breaker!' +GolfSeconds = ' - %(time).2f seconds' +GolfTimeTieBreakWinner = '%(name)s wins the total aiming time tie breaker!!!' +MoreXpHolidayStart = 'Good news! Exclusive Test Toon double gag experience time has started.' +MoreXpHolidayOngoing = 'Welcome! Exclusive Test Toon double gag experience time is currently ongoing.' +MoreXpHolidayEnd = 'Exclusive Test Toon double gag experience time has ended. Thanks for helping us Test things!' +JellybeanDayHolidayStart = "It's Jellybean Day! Get Double Jellybean rewards at Parties!" +JellybeanDayHolidayEnd = "That's all for Jellybean Day. See you next year." +PartyRewardDoubledJellybean = 'Double Jellybeans!' +GrandPrixWeekendHolidayStart = "It's Grand Prix Weekend at Goofy Speedway! Toons collect the most points in three consecutive races." +GrandPrixWeekendHolidayEnd = "That's all for Grand Prix Weekend. See you next year." +KartRace_DoubleTickets = 'Double Tickets' +SellbotNerfHolidayStart = 'Operation: Storm Sellbot is happening now! Battle the VP today!' +SellbotNerfHolidayEnd = 'Operation: Storm Sellbot has ended. Great work, Toons!' +LawbotNerfHolidayStart = 'Operation: Lawbots Lose is happening now! Battle the CJ today!' +LawbotNerfHolidayEnd = 'Operation: Lawbots Lose has ended. Great work, Toons!' +CashbotNerfHolidayStart = 'Operation: Cashbot Chaos is happening now! Battle the CFO today!' +CashbotNerfHolidayEnd = 'Opeation: Cashbot Chaos has ended. Great work, Toons!' +BossbotNerfHolidayStart = 'Operation: Besiege Bossbot is happening now! Battle the CEO today!' +BossbotNerfHolidayEnd = 'Operation: Besiege Bossbot has ended. Great work, Toons!' +JellybeanTrolleyHolidayStart = 'Double Bean Days for Trolley Games have begun!' +JellybeanTrolleyHolidayEnd = 'Double Bean Days for Trolley Games have ended!' +JellybeanFishingHolidayStart = 'Double Bean Days for Fishing have begun!' +JellybeanFishingHolidayEnd = 'Double Bean Days for Fishing have ended!' +JellybeanPartiesHolidayStart = "It's Jellybean Week! Get Double Jellybean rewards!" +JellybeanPartiesHolidayEnd = "That's all for Jellybean Week. See you next year." +JellybeanMonthHolidayStart = 'Celebrate Toontown with double beans, Cattlelog items and silly surprises!' +BankUpgradeHolidayStart = 'Something Toontastic happened to your Jellybean Bank!' +HalloweenPropsHolidayStart = "It's Halloween in Toontown!" +HalloweenPropsHolidayEnd = 'Halloween has ended. Boo!' +SpookyPropsHolidayStart = 'Silly Meter spins Toontown into spooky mode!' +BlackCatHolidayStart = 'Create a Black Cat - today only!' +BlackCatHolidayEnd = 'Black Cat day has ended!' +SpookyBlackCatHolidayStart = 'Friday 13th means a Black Cat blast!' +TopToonsMarathonStart = "The Top Toons New Year's Day Marathon has begun!" +TopToonsMarathonEnd = "The Top Toons New Year's Day Marathon has ended." +WinterDecorationsStart = "It's Winter Holiday time in Toontown!" +WinterDecorationsEnd = 'Winter Holiday is over - Happy New Year!' +WackyWinterDecorationsStart = 'Brrr! Silly Meter goes from silly to chilly!' +WinterCarolingStart = 'Caroling has come to Toontown. Sing for your Snowman Head - see the Blog for details!' +WinterCarolingEnd = 'Caroling is over. See you next year.' +TrickOrTreatStart = 'Happy Halloween! Trick or treat throughout Toontown to get a nifty Halloween pumpkin head reward!' +TrickOrTreatEnd = 'Halloween is over. See you next year.' +ExpandedClosetsStart = 'Attention Toons: For a limited time, Members can purchase the new 50 item Closet from the Cattlelog for the low price of 50 Jellybeans!' +KartingTicketsHolidayStart = 'Get double tickets from Practice races at Goofy Speedway today!' +IdesOfMarchStart = 'Toons go GREEN!' +IdesOfMarchEnd = 'Hope you had fun being green.' +LogoutForced = 'You have done something wrong\n and are being logged out automatically,\n additionally your account may be frozen.\n Try going on a walk outside, it is fun.' +CountryClubToonEnterElevator = '%s \nhas jumped in the golf kart.' +CountryClubBossConfrontedMsg = '%s is battling the Club President!' +ElevatorBlockedRoom = 'All challenges must be defeated first.' +MolesLeft = 'Moles Left: %d' +MolesInstruction = 'Mole Stomp!\nJump on the red moles!' +MolesFinished = 'Mole Stomp successful!' +MolesPityWin = 'Stomp Failed! But the moles left.' +MolesRestarted = 'Stomp Failed! Restarting...' +BustACogInstruction = 'Remove the cog ball!' +BustACogExit = 'Exit for Now' +BustACogHowto = 'How to Play' +BustACogFailure = 'Out of Time!' +BustACogSuccess = 'Success!' +GolfGreenGameScoreString = 'Puzzles Left: %s' +GolfGreenGamePlayerScore = 'Solved %s' +GolfGreenGameBonusGag = 'You won %s!' +GolfGreenGameGotHelp = '%s solved a Puzzle!' +GolfGreenGameDirections = 'Shoot balls using the the mouse\n\n\nMatching three of a color causes the balls to fall\n\n\nRemove all Cog balls from the board' +enterHedgeMaze = 'Race through the Hedge Maze\n for a laff bonus!' +toonFinishedHedgeMaze = '%s \n finished in %s place!' +hedgeMazePlaces = ['first', + 'second', + 'third', + 'Fourth'] +mazeLabel = 'Maze Race!' +BoardingPartyReadme = 'Boarding Group?' +BoardingGroupHide = 'Hide' +BoardingGroupShow = 'Show Boarding Group' +BoardingPartyInform = 'Create an elevator Boarding Group by clicking on another Toon and Inviting them.\nIn this area Boarding Groups cannot have more than %s Toons.' +BoardingPartyTitle = 'Boarding Group' +BoardingPartyTitleMerge = 'Merge Group' +QuitBoardingPartyLeader = 'Disband' +QuitBoardingPartyNonLeader = 'Leave' +QuitBoardingPartyConfirm = 'Are you sure you want to quit this Boarding Group?' +BoardcodeMissing = 'Something went wrong; try again later.' +BoardcodePromotionLeader = 'Your group cannot board because you do not have enough promotion merits.' +BoardcodePromotionNonLeaderSingular = 'Your group cannot board because %s does not have enough promotion merits.' +BoardcodePromotionNonLeaderPlural = 'Your group cannot board because %s do not have enough promotion merits.' +BoardcodeSpace = 'Your group cannot board because there is not enough space.' +BoardcodeBattleLeader = 'Your group cannot board because you are in battle.' +BoardcodeBattleNonLeaderSingular = 'Your group cannot board because %s is in battle.' +BoardcodeBattleNonLeaderPlural = 'Your group cannot board because %s are in battle.' +BoardingInvitePromotionInviter = 'You need to earn a promotion before being a member of this Boarding Group.' +BoardingInvitePromotionInvitee = '%s needs to earn a promotion before being a member of this Boarding Group.' +BoardingInviteeInDiffGroup = '%s is already in a different Boarding Group.' +BoardingInviteeInKickOutList = '%s had been removed by your leader. Only the leader can re-invite removed members.' +BoardingInviteePendingIvite = '%s has a pending invite; try again later.' +BoardingInviteeInElevator = '%s is currently busy; try again later.' +BoardingInviteGroupFull = 'Your Boarding Group is already full.' +BoardingGroupsTooLarge = '%s is already in a different Boarding Group that is too large to merge.' +BoardingAlreadyInGroup = 'You cannot accept this invitation because you are part of another Boarding Group.' +BoardingGroupAlreadyFull = 'You cannot accept this invitation because the group is already full.' +BoardingKickOutConfirm = 'Are you sure you want to remove %s?' +BoardingPendingInvite = 'You need to deal with the\n pending invitation first.' +BoardingCannotLeaveZone = 'You cannot leave this area because you are part of a Boarding Group.' +BoardingInviteeMessage = '%s would like you to join their Boarding Group.' +BoardingInviteeMergeMessage = '%s would like you merge with their Boarding Group.' +BoardingInvitingMessage = 'Inviting %s to your Boarding Group.' +BoardingInvitationRejected = '%s has rejected to join your Boarding Group.' +BoardingMessageKickedOut = 'You have been removed from the Boarding Group.' +BoardingMessageInvited = '%s has invited %s to the Boarding Group.' +BoardingMessageLeftGroup = '%s has left the Boarding Group.' +BoardingMessageGroupDissolved = 'Your Boarding Group was disbanded by the group leader.' +BoardingMessageGroupDisbandedGeneric = 'Your Boarding Group was disbanded.' +BoardingMessageInvitationFailed = '%s tried to invite you to their Boarding Group.' +BoardingMessageGroupFull = '%s tried to accept your invitation but your group was full.' +BoardingGo = 'GO' +BoardingCancelGo = 'Click Again to\nCancel Go' +And = 'and' +BoardingGoingTo = 'Going To:' +BoardingTimeWarning = 'Boarding the elevator in ' +BoardingMore = 'more' +BoardingGoShow = 'Going to\n%s in ' +BoardingGoPreShow = 'Confirming...' +BossbotBossName = 'C.E.O.' +BossbotRTWelcome = 'You toons will need different disguises.' +BossbotRTRemoveSuit = 'First take off your cog suits...' +BossbotRTFightWaiter = 'and then fight these waiters.' +BossbotRTWearWaiter = "Good job! Now put on the waiters' clothes." +BossbotBossPreTwo1 = "What's taking so long?" +BossbotBossPreTwo2 = 'Get cracking and serve my banquet!' +BossbotRTServeFood1 = 'Hehe, serve the food I place on these conveyor belts.' +BossbotRTServeFood2 = 'If you serve a cog three times in a row it will explode.' +BossbotPhase3Speech1 = "What's happening here?!" +BossbotPhase3Speech2 = 'These waiters are toons!' +BossbotPhase3Speech3 = 'Get them!!!' +BossbotPhase4Speech1 = 'Hrrmmpph. When I need a job done right...' +BossbotPhase4Speech2 = "I'll do it myself." +BossbotRTPhase4Speech1 = 'Good job! Now squirt the C.E.O. with the water on the tables...' +BossbotRTPhase4Speech2 = 'or use golf balls to slow him down.' +BossbotPitcherLeave = 'Leave Bottle' +BossbotPitcherLeaving = 'Leaving Bottle' +BossbotPitcherAdvice = 'Use the left and right keys to rotate.\nHold down Ctrl increase power.\nRelease Ctrl to fire.' +BossbotGolfSpotLeave = 'Leave Golf Ball' +BossbotGolfSpotLeaving = 'Leaving Golf Ball' +BossbotGolfSpotAdvice = 'Use the left and right keys to rotate.\nCtrl to fire.' +BossbotRewardSpeech1 = "No! The Chairman won't like this." +BossbotRewardSpeech2 = 'Arrrggghhh!!!!' +BossbotRTCongratulations = "You did it! You've demoted the C.E.O.!\x07Here, take these pink slips the C.E.O. left behind.\x07With it you'll be able to fire Cogs in a battle." +BossbotRTHPBoost = "\x07You've done a lot of work for the Resistance.\x07The Toon Council has decided to give you another Laff point. Congratulations!" +BossbotRTMaxed = '\x07I see that you have a level %s Cog Suit. Very impressive!\x07On behalf of the Toon Council, thank you for coming back to defend more Toons!' +BossbotRTLastPromotion = "\x07Wow, you've reached level %s on your Cog Suit!\x07Cogs don't get promoted higher than that.\x07You can't upgrade your Cog Suit anymore, but you can certainly keep working for the Resistance!" +BossRTKeyReward = '\x07By the way, thanks to your exquisite performance, the Toon Council has decided to offer to you a Cog Nation Crate key!\x07Using this, you will be able to open the crates you have earned in the cog factories.' +GolfAreaAttackTaunt = 'Fore!' +OvertimeAttackTaunts = ["It's time to reorganize.", "Now let's downsize."] +ElevatorBossBotBoss = 'Bossbot Clubhouse' +ElevatorBossBotCourse0 = 'The Front Three' +ElevatorBossBotCourse1 = 'The Middle Six' +ElevatorBossBotCourse2 = 'The Back Nine' +ElevatorCashBotBoss = 'Cashbot Vault' +ElevatorCashBotMint0 = 'Coin Mint' +ElevatorCashBotMint1 = 'Dollar Mint' +ElevatorCashBotMint2 = 'Bullion Mint' +ElevatorSellBotBoss = 'Sellbot Towers' +ElevatorSellBotFactory0 = 'Front Entrance' +ElevatorSellBotFactory1 = 'Side Entrance' +ElevatorSellBotFactory2 = 'Megacorp Entrance' +ElevatorLawBotBoss = 'Lawbot Courthouse' +ElevatorLawBotCourse0 = 'Office A' +ElevatorLawBotCourse1 = 'Office B' +ElevatorLawBotCourse2 = 'Office C' +ElevatorLawBotCourse3 = 'Office D' +IceGameTitle = 'Ice Slide' +IceGameInstructions = 'Get as close to the center by the end of the second round. Use arrow keys to change direction and force. Press Ctrl to launch your toon. Hit barrels for extra points and avoid the TNT!' +IceGameInstructionsNoTnt = 'Get as close to the center by the end of the second round. Use arrow keys to change direction and force. Press Ctrl to launch your toon. Hit barrels for extra points.' +IceGameInfo = 'Match %(curMatch)d/%(numMatch)d, Round %(curRound)d/%(numRound)d' +IceGameControlKeyWarning = 'Remember to press the Ctrl key!' +PicnicTableJoinButton = 'Join' +PicnicTableObserveButton = 'Observe' +PicnicTableCancelButton = 'Cancel' +PicnicTableTutorial = 'How To Play' +PicnicTableMenuTutorial = 'What game do you want to learn?' +PicnicTableMenuSelect = 'What game do you want to play?' +ChineseCheckersGetUpButton = 'Get Up' +ChineseCheckersStartButton = 'Start Game' +ChineseCheckersQuitButton = 'Quit Game' +ChineseCheckersIts = "It's " +ChineseCheckersYourTurn = 'Your Turn' +ChineseCheckersGreenTurn = "Green's Turn" +ChineseCheckersYellowTurn = "Yellow's Turn" +ChineseCheckersPurpleTurn = "Purple's Turn" +ChineseCheckersBlueTurn = "Blue's Turn" +ChineseCheckersPinkTurn = "Pink's Turn" +ChineseCheckersRedTurn = "Red's Turn" +ChineseCheckersColorG = 'You are Green' +ChineseCheckersColorY = 'You are Yellow' +ChineseCheckersColorP = 'You are Purple' +ChineseCheckersColorB = 'You are Blue' +ChineseCheckersColorPink = 'You are Pink' +ChineseCheckersColorR = 'You are Red' +ChineseCheckersColorO = 'You are Observing' +ChineseCheckersYouWon = 'You just won a game of Chinese Checkers!' +ChineseCheckers = 'Chinese Checkers.' +ChineseCheckersGameOf = ' has just won a game of ' +ChineseTutorialTitle1 = 'Objective' +ChineseTutorialTitle2 = 'How to Play' +ChineseTutorialPrev = 'Previous Page' +ChineseTutorialNext = 'Next Page' +ChineseTutorialDone = 'Done' +ChinesePage1 = 'The goal of Chinese Checkers is to be the first toon to move all of your marbles from the bottom triangle across the board and into the triangle at the top. The first toon to do so wins!' +ChinesePage2 = 'Toons take turns moving any marble of their own color. A marble can move into an adjacent hole or it can hop over other marbles. Hops must go over a marble and end in an empty hole. It is possible to chain hops together for longer moves!' +CheckersPage1 = 'The goal of Checkers is to leave the opponent without any possible moves. To do this you can either capture all of his peices or block them in such that he has no available moves.' +CheckersPage2 = 'Toons take turns moving any peice of their own color. A peice can move one square diagonal and forward. A peice can only move into a square that is not occupied by another peice. Kings follow the same rules but are allowed to move backwards.' +CheckersPage3 = 'To capture an opponents peice your peice must jump over it diagonally into the vacant square beyond it. If you have any jump moves during a turn, you must do one of them. You can chain jump moves together as long as it is with the same peice.' +CheckersPage4 = 'A peice becomes a king when it reaches the last row on the board. A peice that has just become a king cannot continue jumping until the next turn. Additionally, kings are allowed to move all directions and are allowed to change directions while jumping.' +CheckersGetUpButton = 'Get Up' +CheckersStartButton = 'Start Game' +CheckersQuitButton = 'Quit Game' +CheckersIts = "It's " +CheckersYourTurn = 'Your Turn' +CheckersWhiteTurn = "White's Turn" +CheckersBlackTurn = "Black's Turn" +CheckersColorWhite = 'You are White' +CheckersColorBlack = 'You are Black' +CheckersObserver = 'You are Observing' +RegularCheckers = 'Checkers.' +RegularCheckersGameOf = ' has just won a game of ' +RegularCheckersYouWon = 'You just won a game of Checkers!' +GardenDropTitle = 'Garden Drop' +GardenDropExitGame = 'Exit Mini Game' +GardenDropHelpTitle = 'Instructions:' +GardenDropInstructions = "Match the ghost balls with the normal balls! But beware of the cog ball, it will try to block you off!" +GardenDropBackToGame = "Back to Game" +GardenDropButtonTitle = 'Garden\nDrop' +GardenDropCongradulations = 'Super Congratulations!!' +GardenDropProgressLevels = "Click 'Next' to go to the next level!" +GardenDropWinGame = 'You have won the Garden Drop Game!' +GardenDropExit = 'Exit' +MailNotifyNewItems = "You've got mail!" +MailNewMailButton = 'Mail' +MailSimpleMail = 'Note' +MailFromTag = 'Note From: %s' +AwardNotifyNewItems = 'You have a new award in your mailbox!' +AwardNotifyOldItems = 'There are still awards waiting in your mailbox for you to pick up!' +InviteInvitation = 'the invitation' +InviteAcceptInvalidError = 'The invitation is no longer valid.' +InviteAcceptPartyInvalid = 'That party has been cancelled.' +InviteAcceptAllOk = 'The host has been informed of your reply.' +InviteRejectAllOk = 'The host has been informed that you declined the invitation.' +Months = {1: 'JANUARY', + 2: 'FEBRUARY', + 3: 'MARCH', + 4: 'APRIL', + 5: 'MAY', + 6: 'JUNE', + 7: 'JULY', + 8: 'AUGUST', + 9: 'SEPTEMBER', + 10: 'OCTOBER', + 11: 'NOVEMBER', + 12: 'DECEMBER'} +DayNames = ('Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday') +DayNamesAbbrev = ('MON', + 'TUE', + 'WED', + 'THU', + 'FRI', + 'SAT', + 'SUN') +SummerFireworksStart = 'Celebrate Summer with a fireworks show every hour in each playground!' +SummerFireworksEnd = 'Summer Fireworks are over. Hope you had fun.' +NewYearFireworksStart = 'Happy New Year! Enjoy a fireworks show every hour in each playground!' +NewYearFireworksEnd = 'New Year Fireworks are over. See you next year!' +HolidayNamesInCalendar = {1: ('Summer Fireworks', 'Celebrate Summer with a fireworks show every hour in each playground!'), + 2: ('New Year Fireworks', 'Happy New Year! Enjoy a fireworks show every hour in each playground!'), + 3: ('Halloween', 'Happy Halloween! Trick or treat throughout Toontown to get a nifty Halloween pumpkin head reward!'), + 4: ('Winter Holiday', 'Celebrate the Winter Holiday with Toontastic decorations, party and Cattlelog items, and more!'), + 5: ('Skelecog Invasion', 'Stop the Skelecogs from invading Toontown!'), + 6: ('Mr. Hollywood Invasion', 'Stop the Mr. Hollywood Cogs from invading Toontown!'), + 8: ('Toon Species Election', 'Vote on the new Toon species! Will it be Goat? Will it be Pig?'), + 9: ('Black Cat Day', 'Create a Toontastic Black Cat Toon - today only!'), + 16: ('Grand Prix', 'Grand Prix Monday at Goofy Speedway! To win, collect the most points in three consecutive races!'), + 17: ('Fish Bingo', 'Fish Bingo Wednesday! Everyone at the pond works together to complete the card before time runs out.'), + 18: ('Silly Saturdays', 'Saturdays are silly with Fish Bingo and Grand Prix throughout the day!'), + 24: ('Ides of March', 'Beware the Ides of March! Stop the Backstabber Cogs from invading Toontown!'), + 26: ('Halloween Decor', 'Celebrate Halloween as spooky trees and streetlights transform Toontown!'), + 28: ('Winter Invasion', 'The sellbots are on the loose spreading their cold sales tactics!'), + 29: ("April Toons' Week", "Celebrate April Toons' Week - a holiday built by Toons for Toons!"), + 33: ('Sellbot Surprise 1', 'Sellbot Surprise! Stop the Cold Caller Cogs from invading Toontown!'), + 34: ('Sellbot Surprise 2', 'Sellbot Surprise! Stop the Name Dropper Cogs from invading Toontown!'), + 35: ('Sellbot Surprise 3', 'Sellbot Surprise! Stop the Gladhander Cogs from invading Toontown!'), + 36: ('Sellbot Surprise 4', 'Sellbot Surprise! Stop the Mover & Shaker Cogs from invading Toontown!'), + 37: ('A Cashbot Conundrum 1', 'A Cashbot Conundrum. Stop the Short Change Cogs from invading Toontown!'), + 38: ('A Cashbot Conundrum 2', 'A Cashbot Conundrum. Stop the Penny Pincher Cogs from invading Toontown!'), + 39: ('A Cashbot Conundrum 3', 'A Cashbot Conundrum. Stop the Bean Counter Cogs from invading Toontown!'), + 40: ('A Cashbot Conundrum 4', 'A Cashbot Conundrum. Stop the Number Cruncher Cogs from invading Toontown!'), + 41: ('The Lawbot Gambit 1', 'The Lawbot Gambit. Stop the Bottomfeeder Cogs from invading Toontown!'), + 42: ('The Lawbot Gambit 2', 'The Lawbot Gambit. Stop the Double Talker Cogs from invading Toontown!'), + 43: ('The Lawbot Gambit 3', 'The Lawbot Gambit. Stop the Ambulance Chaser Cogs from invading Toontown!'), + 44: ('The Lawbot Gambit 4', 'The Lawbot Gambit. Stop the Backstabber Cogs from invading Toontown!'), + 45: ('The Trouble With Bossbots 1', 'The Trouble with Bossbots. Stop the Flunky Cogs from invading Toontown!'), + 46: ('The Trouble With Bossbots 2', 'The Trouble with Bossbots. Stop the Pencil Pusher Cogs from invading Toontown!'), + 47: ('The Trouble With Bossbots 3', 'The Trouble with Bossbots. Stop the Micromanager Cogs from invading Toontown!'), + 48: ('The Trouble With Bossbots 4', 'The Trouble with Bossbots. Stop the Downsizer Cogs from invading Toontown!'), + 49: ('Jellybean Day', 'Celebrate Jellybean Day with double Jellybean rewards at parties!'), + 53: ('Cold Caller Invasion', 'Stop the Cold Caller Cogs from invading Toontown!'), + 54: ('Bean Counter Invasion', 'Stop the Bean Counter Cogs from invading Toontown!'), + 55: ('Double Talker Invasion', 'Stop the Double Talker Cogs from invading Toontown!'), + 56: ('Downsizer Invasion', 'Stop the Downsizer Cogs from invading Toontown!'), + 59: ("ValenToon's Day", "Celebrate ValenToon's Day from Feb 09 to Feb 16!"), + 72: ('Yes Men Invasion', 'Stop the Yes Men Cogs from invading Toontown!'), + 73: ('Tightwad Invasion', 'Stop the Tightwad Cogs from invading Toontown!'), + 74: ('Telemarketers Invasion', 'Stop the Telemarketer Cogs from invading Toontown!'), + 75: ('Head Hunter Invasion', 'Stop the Head Hunter Cogs from invading Toontown!'), + 76: ('Spin Doctor Invasion', 'Stop the Spin Doctor Cogs from invading Toontown!'), + 77: ('Moneybags Invasion', 'Stop the Moneybags from invading Toontown!'), + 78: ('Two-faces Invasion', 'Stop the Two-faces from invading Toontown!'), + 79: ('Mingler Invasion', 'Stop the Mingler Cogs from invading Toontown!'), + 80: ('Loan Shark Invasion', 'Stop the Loanshark Cogs from invading Toontown!'), + 81: ('Corporate Raider Invasion', 'Stop the Corporate Raider Cogs from invading Toontown!'), + 82: ('Robber Baron Invasion', 'Stop the Robber Baron Cogs from invading Toontown!'), + 83: ('Legal Eagle Invasion', 'Stop the Legal Eagle Cogs from invading Toontown!'), + 84: ('Big Wig Invasion', 'Stop the Big Wig Cogs from invading Toontown!'), + 85: ('Big Cheese Invasion', 'Stop the Big Cheese from invading Toontown!'), + 86: ('Down Sizer Invasion', 'Stop the Down Sizer Cogs from invading Toontown!'), + 87: ('Mover And Shaker Invasion', 'Stop the Mover and Shaker Cogs from invading Toontown!'), + 88: ('Double Talker Invasion', 'Stop the Double Talkers Cogs from invading Toontown!'), + 89: ('Penny Pincher Invasion', 'Stop the Penny Pinchers Cogs from invading Toontown!'), + 90: ('Name Dropper Invasion', 'Stop the Name Dropper Cogs from invading Toontown!'), + 91: ('Ambulance Chaser Invasion', 'Stop the Ambulance Chaser Cogs from invading Toontown!'), + 92: ('Micro Manager Invasion', 'Stop the Micro Manager Cogs from invading Toontown!'), + 93: ('Number Cruncher Invasion', 'Stop the Number Cruncher Cogs from invading Toontown!'), + 95: ('Victory Parties', 'Celebrate our historic triumph against the Cogs!'), + 96: ('Operation: Storm Sellbot', "Sellbot HQ is open to everyone. Let's go fight the VP!"), + 97: ('Operation: Lawbots Lose', "Lawbot HQ is open to everyone. Let's go fight the CJ!"), + 98: ('Operation: Cashbot Chaos', "Cashbot HQ is open to everyone. Let's go fight the CFO!"), + 99: ('Operation: Besiege Bossbot', "Bossbot HQ is open to everyone. Let's go fight the CEO!"), + 100: ('Double Bean Days - Trolley Games', ''), + 101: ('Double Bean Days - Fishing', ''), + 102: ('Jellybean Week', 'Celebrate Jellybean Week with double Jellybean rewards!'), + 103: ("Top Toons New Year's Day Marathon", "Chances to win every hour! See the What's New Blog for details!"), + 105: ('Toons go GREEN!', 'Toons make a green scene at Green Bean Jeans on Oak Street in Daisy Gardens!'), + 123: ('Day of the Magic Cat', 'Toons gather around to honor the Magic Cat, the creator of the universe!')} +UnknownHoliday = 'Unknown Holiday %d' +HolidayFormat = '%b %d ' +HourFormat = '12' +CogdoMemoGuiTitle = 'Memos:' +CogdoMemoNames = 'Barrel-Destruction Memos' +CogdoStomperName = 'Stomp-O-Matic' +BoardroomGameTitle = 'Boardroom Hijinks' +BoardroomGameInstructions = 'The Bossbots are having a meeting to decide what to do with stolen gags. Slide on through and grab as many gag-destruction memos as you can!' +CogdoCraneGameTitle = 'Vend-A-Stomper' +CogdoCraneGameInstructions = 'The Cashbots are using a coin-operated machine to destroy laff barrels. Use the cranes to pick up and throw money bags, in order to prevent barrel destruction!' +CogdoMazeGameTitle = 'Mover & Shaker\nField Office' +CogdoMazeGameInstructions = 'The big Mover & Shaker Cogs have the code to open the door. Defeat them with your water balloons in order to get it!' +CogdoMazeIntroMovieDialogue = (("This is the Toon Resistance! The Movers & Shakers\nhave our Jokes, and they've locked the exit!",), ('Grab water balloons at coolers, and throw them at Cogs!\nSmall Cogs drop Jokes, BIG COGS open the exit.',), ('The more Jokes you rescue, the bigger your Toon-Up\nat the end. Good luck!',)) +CogdoMazeGameDoorOpens = 'THE EXIT IS OPEN FOR 60 SECONDS!\nGET THERE FAST FOR A BIGGER TOON-UP' +CogdoMazeGameLocalToonFoundExit = "The exit will open when\nyou've busted all four BIG COGS!" +CogdoMazeGameTimeOut = 'Oh no, time ran out! You lost your jokes.' +CogdoMazeGameTimeAlert = 'Hurry up! 60 seconds to go!' +CogdoMazeGameBossGuiTitle = 'BIG COGS:' +CogdoMazeFindHint = 'Find a Water Cooler' +CogdoMazeThrowHint = "Press 'Ctrl' to throw your water balloon" +CogdoMazeSquashHint = 'Falling objects pop your balloon' +CogdoMazeBossHint = 'Big Cogs take TWO hits to defeat' +CogdoMazeMinionHint = 'Smaller Cogs drop jokes' +CogdoMazeGameElevatorRewardLaff = 'Great job, Toons!\nYou get a Toon-Up from the jokes you saved!' +CogdoFlyingGameTitle = 'Legal Eagle\nField Office' +CogdoFlyingGameInstructions = "Fly through the Legal Eagles' lair. Watch out for obstacles and Cogs along the way, and don't forget to refuel your helicopter!" +CogdoFlyingIntroMovieDialogue = (("You won't ruffle our feathers, Toons! We're destroying barrels of your Laff, and you cannot stop us!", "A flock of Toons! We're crushing barrels of your Laff in our %s, and there's nothing you can do about it!" % CogdoStomperName, "You can't egg us on, Toons! We're powering our offices with your Laff, and you're powerless to stop us!"), ('This is the Toon Resistance! A little bird told me you can use propellers to fly around, grab Barrel Destruction Memos, and keep Laff from being destroyed! Good luck, Toons!', 'Attention Toons! Wing it with a propeller and collect Barrel Destruction Memos to keep our Laff from being stomped! Toon Resistance out!', 'Toon Resistance here! Cause a flap by finding propellers, flying to the Barrel Destruction Memos, and keeping our Laff from being smashed! Have fun!'), ("Squawk! I'm a Silver Sprocket Award winner, I don't need this!", 'Do your best, Toons! You will find us to be quite talon-ted!', "We'll teach you to obey the pecking order, Toons!")) +CogdoFlyingGameFuelLabel = 'Fuel' +CogdoFlyingGameLegalEagleTargeting = 'A Legal Eagle has noticed you!' +CogdoFlyingGameLegalEagleAttacking = 'Incoming Eagle!' +CogdoFlyingGamePickUpAPropeller = 'You need a propeller to fly!' +CogdoFlyingGamePressCtrlToFly = "Press 'Ctrl' to fly up!" +CogdoFlyingGameYouAreInvincible = 'Red Tape protects you!' +CogdoFlyingGameTimeIsRunningOut = 'Time is running out!' +CogdoFlyingGameMinimapIntro = 'This meter shows your progress!\nX marks the finish line.' +CogdoFlyingGameMemoIntro = 'Memos prevent Laff Barrels in\nthe Stomper Room from being destroyed!' +CogdoFlyingGameOutOfTime = 'Oh No! You ran out of time!' +CogdoFlyingGameYouMadeIt = 'Good work, you made it on time!' +CogdoFlyingGameTakingMemos = 'The Legal Eagles took all your memos!' +CogdoBarrelRoomTitle = 'Stomper Room' +CogdoBarrelRoomIntroDialog = 'Good work, Toons! You have haulted the Stomp-O-Matic and are now able to collect some of the stolen Laff barrels, but make sure to hurry before the Cogs come!' +CogdoExecutiveSuiteTitle = 'Executive Suite' +CogdoExecutiveSuiteIntroMessage = "Oh no, they've got the shop keeper!\nDefeat the Cogs and free the captive." +CogdoExecutiveSuiteToonThankYou = 'Thanks for the rescue!\nIf you need help in a fight, use this SOS card to call my friend %s.' +CogdoExecutiveSuiteToonThankYouLawbot = 'Thanks for the rescue!\nThe Lawbots have left behind some sprocket awards that you can use to buy new things in your cattlelog!' +CogdoExecutiveSuiteToonBye = 'Bye!' +SillySurgeTerms = ['Amusing Ascent!', + 'Silly Surge!', + 'Ridiculous Rise!', + 'Giggle Growth!', + 'Funny Fueling!', + 'Batty Boost!', + 'Crazy Climb!', + 'Jolly Jump!', + 'Loony Lift!', + 'Hilarity Hike!', + 'Insanity Increase!', + 'Cracked-Uptick!'] +InteractivePropTrackBonusTerms = {0: 'Super Toon-Up!', + 1: '', + 2: '', + 3: '', + 4: 'Super Throw!', + 5: 'Super Squirt!', + 6: ''} +GloveInvalidColorMessage = "That's an unpainted color." +GloveSameColorMessage = "You're already wearing those gloves!" +GloveNoMoneyMessage = "You don't have enough jellybeans!" +GloveMoreMoneyMessage = 'You need %s jellybeans to buy new gloves!' +GloveSuccessMessage = 'Have fun with your new gloves!' +GlovePickColorMessage = 'Feel free to choose!' +GloveGuiTitle = 'Choose a glove color!' +GloveGuiNotice = 'Costs %s jellybeans.' +GloveGuiBuy = 'Buy' +GloveGuiSameColor = 'You already have these!' +LanguageButtonText = 'Language' +LanguageSelectorTitle = 'Choose your language!' +LanguageSelectorCurrent = 'Current language: %s' +LanguageSelectorAvailable = 'Available languages:' +LanguageSelectorBack = 'Back' +LanguageSelectorConfirm = 'Are you sure you want to change your language to %s? This will close your game.' +LanguageSelectorSameLanguage = "You're already using that language!" +CogLevelLabelOn = 'The cog level GUI is on.' +CogLevelLabelOff = 'The cog level GUI is off.' + +HouseNames = ("Bungalow", "Tiki", "Teepee", "Castle", "Cupcake", "Cabin") +CatalogPurchaseHouseType = "When you buy a new house type, the current one is replaced by it. To recover the old type, you'll need to buy it back. Continue?" +BugReportButton = 'Report a Bug' +BugReportNotice = 'Attention!\n\nThis button will open a browser which will send you to a third party bug tracker website. This site requires an Ubuntu One account to login. It may ask you to create an account.\n\nAre you sure you want to continue?' +# Buffs + +buffIdStrings = { + 0: ('Your movement speed will be slightly increased for the next %d %s.', + 'Reward: Increased movement speed') +} + +def getBuffString(buffId, buffTime): + if buffTime < 60: + return buffIdStrings[buffId][0] % (buffTime, 'minutes') + else: + return buffIdStrings[buffId][0] % (buffTime / 60, 'hours') + + +def getBuffPosterString(buffId): + return buffIdStrings[buffId][1] + +BoyPetNames = ['Achilles', 'Adolfo', 'Adonis', 'Aesop', 'Agamemnon', 'Ahab', 'Ahmed', 'Ajax', 'Alastair', 'Alberto', 'Alexander', 'Alfonso', 'Alonzo', 'Amadeus', 'Ambrose', 'Andre', 'Angelo', 'Angus', 'Antoine', 'Anton', 'Antonio', 'Archibald', 'Archimedes', 'Arnold', 'Atilla', 'Attaboy', 'Avi', 'Barney', 'Bart', 'Bartleby', 'Basil', 'Baxter', 'Beardsley', 'Beauchamp', 'Beauregard', 'Ben', 'Benny', 'Bernie', 'Billygoat', 'Bluebeard', 'Bo', 'Bogart', 'Bogie', 'Bowser', 'Brando', 'Bruce', 'Bruno', 'Brutus', 'Bubba', 'Buck', 'Buster', 'Butch', 'Cagney', 'Carlton', 'Casanova', 'Casper', 'Cecil', 'Cedric', 'Charlie', 'Chekhov', 'Chico', 'Chief', 'Chip', 'Chuck', 'Chuckie', 'Chucko', 'Clancy', 'Clark', 'Claude', 'Clayton', 'Cliff', 'Clive', 'Clyde', 'Cody', 'Colonel', 'Confucius', 'Conrad', 'Cooper', 'Cowboy', 'Cyril', 'Dagwood', 'Dante', 'Deano', 'Donahue', 'Donatello', 'Drysdale', 'Duke', 'Dweezil', 'Eddie', 'Edgar', 'Edison', 'Eggman', 'Einstein', 'Elmer', 'Elwood', 'Emerson', 'Emmet', 'Errol', 'Ethan', 'Fafnir', 'Farnsworth', 'Felix', 'Fenimore', 'Fenwick', 'Ferdinand', 'Fergus', 'Fido', 'Figaro', 'Filbert', 'Fitzgerald', 'Floyd', 'Frampton', 'Fred', 'Gabriel', 'Garfunkel', 'General', 'Genghis', 'Gershwin', 'Gladiator', 'Gladstone', 'Gorilla', 'Grampa', 'Gramps', 'Groucho', 'Gulliver', 'Gunther', 'Gus', 'Hamlet', 'Harold', 'Harpo', 'Harvey', 'Haseltine', 'Heathcliff', 'Hector', 'Hemingway', 'Hendrix', 'Herbert', 'Hercules', 'Herman', 'Hermes', 'Higgins', 'Hobart', 'Homestar', 'Horace', 'Horatio', 'Horton', 'Houdini', 'Humphrey', 'Ichabod', 'Iggy', 'Ignatius', 'Ignatz', 'Igor', 'Ike', 'Iron Ike', 'Ironside', 'Irving', 'Irwin', 'Isaac', 'Ishmael', 'Ivan', 'Ivanhoe', 'Jack', 'Jake', 'Jasper', 'Jaymo', 'Jerry', 'Jethro', 'Jimmy', 'Jonah', 'Karloff', 'Kasper', 'Keanu', 'Kilroy', 'King', 'Kingfish', 'Kirby', 'Kissinger', 'Lambert', 'Lancelot', 'Lazarus', 'Leonardo', 'Lindbergh', 'Linus', 'Lionel', 'Luigi', 'Macarthur', 'Macbeth', 'Mack', 'Malcolm', 'Mandrake', 'Marcel', 'Marco', 'Mario', 'Marmalade', 'Matador', 'Maurice', 'Maximilian', 'Maxwell', 'Maynard', 'Melville', 'Merlin', 'Michelangelo', 'Milo', 'Moe', 'Morissey', 'Mortimer', 'Mozart', 'Mugsy', 'Muldoon', 'Nabokov', 'Napoleon', 'Narcissus', 'Nelson', 'Nero', 'Nimoy', 'Nimrod', 'Norbert', 'Norman', 'Norris', 'Obadiah', 'Odysseus', 'Ogden', 'Olaf', 'Omar', 'Opie', 'Opus', 'Oswald', 'Othello', 'Otto', 'Pablo', 'Paco', 'Paddington', 'Pancho', 'Paolo', 'Pappy', 'Pavarotti', 'Pavlov', 'Peabody', 'Petey', 'Picasso', 'Pierre', 'Pinkerton', 'Pippin', 'Poindexter', 'Pop', 'Popperton', 'Poseidon', 'Prince', 'Puccini', 'Puck', 'Pugsly', 'Pushkin', 'Quigley', 'Quimby', 'Quincy', 'Reggie', 'Regis', 'Reinhold', 'Rembrandt', 'Renoir', 'Reuben', 'Rex', 'Rhett', 'Ricardo', 'Richter', 'Rocky', 'Roderick', 'Romeo', 'Romulus', 'Roosevelt', 'Rover', 'Rudolph', 'Rudy', 'Rufus', 'Rupert', 'Salvador', 'Sandeep', 'Schubert', 'Scooby', 'Scotty', 'Scruffy', 'Sebastian', 'Seymour', 'Shakespeare', 'Shatner', 'Siegfried', 'Sigmund', 'Simon', 'Sinbad', 'Skyler', 'Snowman', 'Snyder', 'Socrates', 'Sorcerer', 'Spalding', 'Spanky', 'Spencer', 'Spinoza', 'Spock', 'Stallion', 'Steinbeck', 'Stradivarius', 'T Bone', 'Tchaikovsky', 'Terminator', 'Tesla', 'Thaxter', 'Theo', 'Theodore', 'Thessalus', 'Throckmorton', 'Tiberius', 'Tiger', 'Tobias', 'Tolkein', 'Toreador', 'Travis', 'Travolta', 'Triton', 'Ulysses', 'Umberto', 'Victor', 'Vincent', 'Vinnie', 'Virgil', 'Vlad', 'Vladimir', 'Voltaire', 'Vonnegut', 'Wainwright', 'Waldo', 'Waldorf', 'Walter', 'Warhol', 'Watson', 'Wayne', 'Wilbur', 'Wilfred', 'Wilhelm', 'Winston', 'Wiseguy', 'Wolfgang', 'Woodbury', 'Woodrow', 'Wyatt', 'Xanthus', 'Xavier', 'Zeus', 'Zorro'] +GirlPetNames = ['Abby', 'Adorabelle', 'Aggie', 'Akiko', 'Allessandria', 'Amaryllis', 'Ambrosia', 'Andrea', 'Anita', 'Annette', 'Annie', 'Antionette', 'April', 'Arabella', 'Ariel', 'Ashley', 'Athena', 'Babette', 'Babs', 'Babydoll', 'Ballerina', 'Beatrice', 'Bertie', 'Beulah', 'Beverly', 'Bianca', 'Blossom', 'Bozette', 'Brittany', 'Buttercup', 'Callalilly', 'Candy', 'Carmen', 'Carnation', 'Cassandra', 'Cece', 'Celeste', 'Chanel', 'Chantilly', 'Charlotte', 'Chelsea', 'Cher', 'Cherry', 'Chickie', 'Chloe', 'Cindy', 'Cinnamon', 'Cissy', 'Claire', 'Clementine', 'Cleopatra', 'Coco', 'Cowgirl', 'Daffodil', 'Dagmar', 'Daisy', 'Daphne', 'Dee Dee', 'Delilah', 'Dixie', 'Dolly', 'Dot', 'Dottie', 'Duchess', 'Edith', 'Electra', 'Elsie', 'Elvira', 'Emerald', 'Europa', 'Fannie', 'Fatima', 'Felicity', 'Fifi', 'Flopsy', 'Flower', 'Funnygirl', 'Gabby', 'Garbo', 'Geranium', 'Gidget', 'Gigi', 'Ginger', 'Goldie', 'Granny', 'Guinevere', 'Gwen', 'Hannah', 'Heidi', 'Henrietta', 'Hera', 'Hermione', 'Hildegard', 'Honey', 'Honeysuckle', 'Hortense', 'Iris', 'Ivy', 'Jade', 'Jaquelin', 'Jasmine', 'Jewel', 'Jo', 'Josephine', 'Joy', 'Juliet', 'June', 'Juniper', 'Kandy', 'Kate', 'Katrina', 'Koko', 'Kornelia', 'Krystal', 'Lacey', 'Lady', 'Lambchop', 'Lapis Lazuli', 'Lassie', 'Laverne', 'Leonora', 'Libby', 'Libra', 'Lilac', 'Lily', 'Lipstick', 'Lisa', 'Lottie', 'Lovebird', 'Lovesong', 'Lucretia', 'Lucy', 'Lulu', 'Luna', 'MK', 'Mabel', 'Magenta', 'Maggie', 'Mamacita', 'Mantissa', 'Maxine', 'Mayflower', 'Medusa', 'Melody', 'Mercedes', 'Meriwether', 'Mermaid', 'Mildred', 'Minerva', 'Missy', 'Mitzi', 'Mocha', 'Molly', 'Mona Lisa', 'Moneypenny', 'Monique', 'Mopsy', 'Nadia', 'Nancy', 'Nannygoat', 'Naomi', 'Nellybelle', 'Nightingale', 'Nikita', 'Odelia', 'Olga', 'Olive', 'Olympia', 'Oona', 'Opal', 'Ophelia', 'Organdy', 'Pandora', 'Patrikia', 'Penelope', 'Penny', 'Petinka', 'Petunia', 'Phoebe', 'Phoenicia', 'Pixie', 'Poinsettia', 'Pookie', 'Precious', 'Prettipaws', 'Primrose', 'Princess', 'Pudgette', 'Queenie', 'Raphael', 'Rapunzel', 'Raquel', 'Rosabelle', 'Rosebud', 'Roxanne', 'Roxy', 'Ruby', 'Sable', 'Sabrina', 'Sadie', 'Saffron', 'Sage', 'Sapphire', 'Sassafras', 'Savannah', 'Scarlett', 'Scheherazade', 'Serenade', 'Sienna', 'Silky', 'Sissy', 'Snoogie', 'Snookie', 'Sonja', 'Sophia', 'Sophie', 'Sorceress', 'Summer', 'Sunbonnet', 'Sunny', 'Sunshine', 'Tabitha', 'Tasha', 'Titania', 'Tootsie', 'Topaz', 'Trixie', 'Truffles', 'Tulip', 'Twiggy', 'Velma', 'Venice', 'Venus', 'Veronica', 'Vicki', 'Victoria', 'Viola', 'Violet', 'Wallflower', 'Winnie', 'Winona', 'Yum Yum', 'Zelda', 'Zsa Zsa'] +NeutralPetNames = ['Abner', 'Abracadabra', 'Acacia', 'Ace', 'Admiral', 'Aglet', 'Akimbo', 'Alabaster', 'Alcatraz', 'Alchemy', 'Alfalfa', 'Alien', 'Allegro', 'Alto', 'Amazon', 'Ambassador', 'Amethyst', 'Amnesia', 'Ampersand', 'Anaconda', 'Anchovy', 'Andretti', 'Andromeda', 'Angstrom', 'Animal', 'Antergy', 'Anubus', 'Apogee', 'Apollo', 'Applesauce', 'Apprentice', 'Apricot', 'Aquarius', 'Aramis', 'Archer', 'Arf', 'Argus', 'Argyle', 'Aristotle', 'Arizona', 'Arrow', 'Arsenio', 'Asimov', 'Asparagus', 'Asphalt', 'Astro', 'Atom', 'Augie', 'August', 'Avalanche', 'Avalon', 'Avenger', 'Avis', 'Axel', 'Axle', 'Azalea', 'Aztec', 'Azure', 'Babbit', 'Babble', 'Babbler', 'Baby', 'Babykins', 'Baccarat', 'Backgammon', 'Badger', 'Baggy', 'Bailey', 'Baja', 'Balboa', 'Baldo', 'Baldric', 'Baldy', 'Ballyhoo', 'Bambino', 'Bamboo', 'Bamboozle', 'Bamboozler', 'Banana', 'Bandit', 'Bandito', 'Bangle', 'Banjo', 'Banshee', 'Banzai', 'Barbarian', 'Bargraph', 'Barky', 'Barnacle', 'Barracuda', 'Bashful', 'Bazooka', 'Beaker', 'Bean', 'Beaner', 'Beanstalk', 'Beany', 'Bear', 'Beauty', 'Beaver', 'Beep', 'Beeper', 'Beepy', 'Beethoven', 'Beezneez', 'Begonia', 'Bellyflop', 'Benson', 'Bentley', 'Beppo', 'Bermuda', 'Betatest', 'Bewitched', 'Big Red', 'Big Shot', 'Bigbelly', 'Bigfoot', 'Bijou', 'Bindle', 'Bing', 'Bingo', 'Binky', 'Biscuit', 'Bitsy', 'Bizzy', 'Blackberry', 'Blackbird', 'Blackfoot', 'Blackie', 'Blackjack', 'Blacktop', 'Blanket', 'Blaze', 'Bleary', 'Blimp', 'Blimpo', 'Blimpy', 'Blip Blop', 'Blizzard', 'Blockhead', 'Blondie', 'Blooper', 'Blorange', 'Blot', 'Blotto', 'Bluebell', 'Blueberry', 'Bluegrass', 'Bluenose', 'Blueprint', 'Blunder', 'Blurp', 'Boa', 'Bobbin', 'Bobo', 'Bobtail', 'Bodyguard', 'Boggs', 'Bojangles', 'Bolt', 'Bombshell', 'Bonbon', 'Bones', 'Bongo', 'Bonkers', 'Bono', 'Bonsai', 'Boo Boo', 'Boober', 'Boogeyman', 'Boom Boom', 'Boomer', 'Boots', 'Borax', 'Borg', 'Bosco', 'Bottlecap', 'Bottlenose', 'Boulder', 'Bounce Bounce', 'Bouncer', 'Bouncy', 'Bowtie', 'Bowzer', 'Boyd', 'Bozo', 'Brahms', 'Brainchild', 'Bratwurst', 'Bravo', 'Briar', 'Brie', 'Broadway', 'Broccoli', 'Bronco', 'Brooklyn', 'Brouhaha', 'Brownie', 'Bruiser', 'Bubblegum', 'Bubbles', 'Bubo', 'Buccaneer', 'Buckaroo', 'Buckeye', 'Buckingham', 'Buckle', 'Buckshot', 'Buckskin', 'Buckwheat', 'Budapest', 'Buddy', 'Buffalo', 'Buffoon', 'Bug', 'Bugaboo', 'Bugeye', 'Bugsy', 'Bullet', 'Bullwinkle', 'Bumblebee', 'Bumbler', 'Bumper', 'Bumpkin', 'Bumpus', 'Bumpy', 'Bungee', 'Bunko', 'Bunyan', 'Burbank', 'Butterball', 'Butterscotch', 'Buzz', 'Buzzard', 'Buzzy', 'Byte', 'Caboodle', 'Caboose', 'Cadbury', 'Cadet', 'Caesar', 'Calculus', 'Calico', 'Callisto', 'Calypso', 'Calzone', 'Cambridge', 'Camelot', 'Camembert', 'Cameo', 'Campbell', 'Canoe', 'Cap', 'Cappy', 'Capricorn', 'Captain', 'Caramba', 'Caramel', 'Cargo', 'Carlos', 'Carpool', 'Caruso', 'Casablanca', 'Casbah', 'Casey', 'Cashmere', 'Cassidy', 'Castaway', 'Catamaran', 'Caviar', 'Cayenne', 'Cerebro', 'Cha Cha', 'Challenger', 'Chamberlain', 'Chamomile', 'Champ', 'Chaos', 'Charade', 'Charcoal', 'Charger', 'Charmed', 'Chaser', 'Chasm', 'Checkers', 'Cheesecake', 'Cheesy', 'Cheetah', 'Chester', 'Chewie', 'Chicory', 'Chiffon', 'Chigger', 'Chili', 'Chili Bean', 'Chipmunk', 'Chipper', 'Chips', 'Chocolate', 'Choo Choo', 'Chopin', 'Chopper', 'Chops', 'Chopsticks', 'Chowder', 'Chowderhead', 'Chromium', 'Chubby', 'Chuckles', 'Chutzpah', 'Cinder', 'Citrus', 'Clipper', 'Cloud', 'Cloudy', 'Clover', 'Clue', 'Clueless', 'Clunky', 'Coach', 'Cobol', 'Cobra', 'Cocoa', 'Coconut', 'Cola', 'Colorado', 'Comet', 'Comma', 'Confetti', 'Connecticut', 'Cookie', 'Cooter', 'Copper', 'Coral', 'Corduroy', 'Corky', 'Cornball', 'Corncob', 'Cornelia', 'Cornmo', 'Corny', 'Corona', 'Coronet', 'Cosmo', 'Cottontail', 'Couscous', 'Coyote', 'Crackers', 'Cranberry', 'Crash', 'Crawdad', 'Creamy', 'Creeper', 'Creepy', 'Crescent', 'Cricket', 'Critter', 'Croissant', 'Crouton', 'Crufty', 'Cruiser', 'Crumb', 'Crumbly', 'Cruncher', 'Crunchy', 'Cucamonga', 'Cucaracha', 'Cuckoo', 'Cucumber', 'Cuddles', 'Cupcake', 'Cupid', 'Curmudgeon', 'Curry', 'Cutie', 'Cutie Pie', 'Cypress', 'DJ', 'Dakota', 'Dali', 'Damascus', 'Dancer', 'Dandelion', 'Danger', 'Database', 'Dazzle', 'Dazzler', 'Dazzy', 'December', 'Delaware', 'Delphi', 'Detonator', 'Detour', 'Detroit', 'Deuce', 'Dewfall', 'Diamond', 'Dice', 'Diego', 'Digby', 'Digger', 'Digit', 'Dimples', 'Dingo', 'Dingus', 'Dinky', 'Divot', 'Dizzy', 'Doc', 'Dodger', 'Dodo', 'Dolby', 'Donut', 'Doodad', 'Doodah', 'Doodle', 'Draco', 'Dracula', 'Drawers', 'Dreadnought', 'Dream', 'Dreamer', 'Dreamweaver', 'Dreamy', 'Drippy', 'Droopy', 'Drummer', 'Dumpling', 'Durango', 'Dustmop', 'Dynamite', 'Dynamo', 'Eagle', 'Ebony', 'Eccentric', 'Echo', 'Eclipse', 'Ecstatic', 'Edge', 'Egad', 'Egghead', 'Eggnog', 'Eggtoss', 'Egypt', 'Elderberry', 'Electron', 'Elf', 'Elijah', 'Elvis', 'Email', 'Ember', 'Encore', 'Enoch', 'Epcot', 'Epic', 'Epilog', 'Equinox', 'Ergo', 'Escapade', 'Eskimo', 'Espresso', 'Euclid', 'Euphoria', 'Euphrates', 'Excalibur', 'Exeter', 'Eyespy', 'Faberge', 'Fable', 'Facepuff', 'Fahrenheit', 'Fairbanks', 'Fairfax', 'Faith', 'Faithful', 'Fajita', 'Falafel', 'Falcon', 'Fancy', 'Fandango', 'Fang', 'Fangtastic', 'Fantasia', 'Fats', 'Fatso', 'Fatty', 'Faust', 'Fax', 'Fearless', 'Feather', 'Feedback', 'Fellini', 'Ferrari', 'Fester', 'Fetch', 'Fiddler', 'Fiddlesticks', 'Fiesta', 'Filibuster', 'Fingerprint', 'Fingers', 'Fink', 'Fishbone', 'Fishface', 'Fishhook', 'Fizz', 'Fizzle', 'Fizzy', 'Flabby', 'Flagpole', 'Flame', 'Flannel', 'Flapjack', 'Flash', 'Flatfoot', 'Flicker', 'Fling', 'Flip', 'Flipper', 'Flop', 'Flopper', 'Floppy', 'Florida', 'Fluffster', 'Fluffy', 'Flurry', 'Flute', 'Fog', 'Fogarty', 'Fondue', 'Fortran', 'Fox', 'Foxy', 'Frankfurter', 'Freckles', 'Freebie', 'Freezerburn', 'French Toast', 'Friday', 'Frogface', 'Frogmar', 'Frost', 'Frosty', 'Fruitcake', 'Frump', 'Frumpson', 'Frumpy', 'Fudd', 'Fuddles', 'Fudge', 'Fugitive', 'Fuji', 'Fungus', 'Funky', 'Furball', 'Furface', 'Fusebox', 'Fuzzball', 'Fuzzy', 'Fuzzyface', 'Fuzzykins', 'Gabardine', 'Galaxy', 'Gallop', 'Gambit', 'Gambler', 'Gangway', 'Garlic', 'Garlicbreath', 'Garnet', 'Garth', 'Gavotte', 'Gecko', 'Geewhiz', 'Gem', 'Gemini', 'Gerbil', 'Gewgaw', 'Ghost', 'Giddyup', 'Giggles', 'Gingerbread', 'Gingersnap', 'Glick', 'Gnash', 'Gnasher', 'Gnocchi', 'Gnome', 'Gobbledegook', 'Gobbler', 'Goblet', 'Goblin', 'Gobo', 'Gogo', 'Goiter', 'Goliath', 'Gomer', 'Goober', 'Gooch', 'Gooey', 'Googol', 'Goose', 'Gooseberry', 'Goosebump', 'Gopher', 'Gouda', 'Governor', 'Gracie', 'Graffiti', 'Graham', 'Granite', 'Granola', 'Gravlax', 'Gremlin', 'Grep', 'Grok', 'Grue', 'Grumpus', 'Grumpy', 'Grungy', 'Guffaw', 'Gumbo', 'Gumdrop', 'Gump', 'Gumpus', 'Gumshoe', 'Gypsy', 'Gyro', 'Haggis', 'Haha', 'Hairball', 'Half Pint', 'Halibut', 'Halifax', 'Halloweenie', 'Halo', 'Halogen', 'Hambone', 'Hamburger', 'Hammer', 'Hammerhead', 'Hammerstein', 'Hammy', 'Hamster', 'Happy', 'Harlequin', 'Harley', 'Harmonica', 'Harmony', 'Harvard', 'Havoc', 'Hawk', 'Hawkeye', 'Hayseed', 'Haywire', 'Hazel', 'Heartbreaker', 'Heathrow', 'Heckler', 'Hedgehog', 'Heehee', 'Hemlock', 'Herringbone', 'Hiccup', 'Hifi', 'Hip Hip', 'Hippo', 'Hippodrome', 'Hoagie', 'Hobo', 'Hoho', 'Holmes', 'Honeybee', 'Hooligan', 'Hoops', 'Hoosier', 'Horoscope', 'Hot Dog', 'Hotfoot', 'Hotshot', 'Houston', 'Howard', 'Huckleberry', 'Huffy', 'Huggy', 'Hugo', 'Humdinger', 'Humdrum', 'Hurricane', 'Hydraulic', 'Hypnos', 'IOU', 'Iapyx', 'Ibex', 'Icarus', 'Icky', 'Icon', 'Icy', 'Idiom', 'Idlewild', 'Igloo', 'Iguana', 'Illogical', 'Illusion', 'Imagine', 'Imp', 'Impy', 'Imus', 'Indigo', 'Indy', 'Inferno', 'Infinity', 'Infrared', 'Ingot', 'Inkblot', 'Inkwell', 'Innie', 'Input', 'Insomnia', 'Intro', 'Iodine', 'Iota', 'Itchy', 'Ivory', 'Izzat', 'Izzy', 'Jabber', 'Jabberwock', 'Jackaroo', 'Jackhammer', 'Jackpot', 'Jackrabbit', 'Jacuzzi', 'Jag', 'Jaguar', 'Jalapeno', 'Jambalaya', 'Jamboree', 'January', 'Jargon', 'Java', 'Jaws', 'Jaybird', 'Jester', 'Jet', 'Jicko', 'Jiffy', 'Jigsaw', 'Jimjam', 'Jingle', 'Jinx', 'Jitterbug', 'Jocko', 'Jojo', 'Joker', 'Jokester', 'Joliet', 'Joplin', 'Jordie', 'Jove', 'Joyride', 'Jubilee', 'Jughead', 'Jujitsu', 'Jukebox', 'July', 'Jumbo', 'Jumpy', 'Junior', 'Juno', 'Kabob', 'Kabuki', 'Kafka', 'Kahuna', 'Kalamazoo', 'Kaleidoscope', 'Kalmuk', 'Kansas', 'Kappa', 'Karamazov', 'Karate', 'Karma', 'Katmandu', 'Katsumi', 'Kayak', 'Keepsake', 'Kellogg', 'Kelvin', 'Ketchup', 'Kewpie', 'Keyboard', 'Keyring', 'Khaki', 'Kibbles', 'Kiddo', 'Kielbasa', 'Kilimanjaro', 'Kilowatt', 'Kimono', 'Kinetic', 'Kipling', 'Kismet', 'Kissyface', 'Kitten', 'Klinger', 'Klondike', 'Kludge', 'Klute', 'Klutz', 'Klutzy', 'Knickerbocker', 'Knievel', 'Knish', 'Knock Knock', 'Knockwurst', 'Knoop', 'Knucklebone', 'Knuckles', 'Koala', 'Kodiak', 'Kong', 'Kookaburra', 'Kooky', 'Kool Beanz', 'Kornball', 'Kosmix', 'Krakatoa', 'Kramer', 'Krispy', 'Krooner', 'Lab Rat', 'Labyrinth', 'Lacquer', 'Laddie', 'Ladybug', 'Lambada', 'Lamborghini', 'Lampoon', 'Lamster', 'Landmark', 'Landshark', 'Lanky', 'Lapper', 'Laptop', 'Lasagna', 'Laser', 'Latex', 'Lava', 'Lava Lamp', 'Lavender', 'Layaway', 'Leafy', 'Leaky', 'Lefty', 'Legend', 'Lemming', 'Lemonade', 'Lentil', 'Lettuce', 'Lexy', 'Lickety Split', 'Lickums', 'Licky', 'Licorice', 'Lightning', 'Lima', 'Limbo', 'Limey', 'Linguini', 'Link', 'Lintball', 'Lionheart', 'Lithgow', 'Litmus', 'Littlefoot', 'Liverpool', 'Liverwurst', 'Lizard', 'Lobo', 'Lofty', 'Logan', 'Logical', 'Lollipop', 'London', 'Longfellow', 'Longshot', 'Loofah', 'Looney', 'Loopy', 'Lotus', 'Luau', 'Lucky', 'Ludwig', 'Lullaby', 'Lumpy', 'Lunatic', 'Lynx', 'Lyrical', 'Macaroni', 'Macaroon', 'Macho', 'Macintosh', 'Mackerel', 'Mad Max', 'Madison', 'Maestro', 'Mage', 'Magic', 'Magma', 'Magnet', 'Magnolia', 'Magnus', 'Magoo', 'Magpie', 'Mahogany', 'Majestic', 'Malibu', 'Mambo', 'Mango', 'Manhattan', 'Manitoba', 'Mantra', 'Maple Syrup', 'Maraschino', 'Marathon', 'Mariachi', 'Marquee', 'Marshmallow', 'Martian', 'Marzipan', 'Mascot', 'Matchmaker', 'Matzoh', 'Maverick', 'Max', 'Maybe', 'Mayhem', 'Mazy', 'Meanie', 'Meatball', 'Meatloaf', 'Melange', 'Melbourne', 'Mellifluent', 'Melrose', 'Memo', 'Memphis', 'Menthol', 'Meow Meow', 'Meringue', 'Mesopotamia', 'Mesquite', 'Meta', 'Metric', 'Metro', 'Mezzo', 'Miami', 'Microfilm', 'Microwave', 'Midas', 'Midget', 'Midnight', 'Mikado', 'Milestone', 'Milkshake', 'Minty', 'Minuet', 'Minus', 'Mischief', 'Misery', 'Mist', 'Misty', 'Mittens', 'Mo', 'Mobius', 'Modesta', 'Mohair', 'Mohawk', 'Mojo', 'Molasses', 'Mole', 'Molecule', 'Monday', 'Mongoose', 'Monkey', 'Monogram', 'Montgomery', 'Monty', 'Moocher', 'Moochie', 'Moonbeam', 'Moondancer', 'Moondoggie', 'Moonmist', 'Moose', 'Mooshoo', 'Moptop', 'Mork', 'Morocco', 'Mosaic', 'Moscow', 'Motley', 'Moustache', 'Moxie', 'Mudpie', 'Muffin', 'Mulberry', 'Mumbles', 'Mumford', 'Mumpy', 'Munchkin', 'Murphy', 'Mushmouth', 'Mushroom', 'Mustang', 'Mustard', 'Mutt', 'Muttzie', 'Mylar', 'Nacho', 'Nameless', 'Nardek', 'Nashville', 'Naugahyde', 'Navel', 'Naxos', 'Nectarine', 'Ned', 'Needle', 'Needler', 'Needles', 'Nehru', 'Neon', 'Neptune', 'Network', 'Neuron', 'Neutron', 'Nevada', 'Newt', 'Newton', 'Niagara', 'Nibbler', 'Nibbles', 'Nibbly', 'Niccolo', 'Nickel', 'Nifty', 'Niftykins', 'Nightmare', 'Nim', 'Nimbus', 'Nitro', 'Nix', 'Nixy', 'Noisemaker', 'Nomad', 'Noname', 'Noodles', 'Nooly', 'Norbie', 'Nostrildamus', 'Nosy', 'November', 'Nugget', 'Numbers', 'Nutmeg', 'Oasis', 'Oatcake', 'Oatmeal', 'Oberon', 'Oblong', 'Oboe', 'Obsession', 'Octagon', 'October', 'Oddjob', 'Odzanends', 'Offbeat', 'Ogee', 'Ohio', 'Oink Oink', 'Oinker', 'Okeedoke', 'Okra', 'Oleander', 'Omega', 'Omelet', 'Onion', 'Onionbreath', 'Onionhead', 'Ono', 'Onomatopoeia', 'Onyx', 'Oozy', 'Opaque', 'Opossum', 'Orangeade', 'Orbit', 'Orchid', 'Oregano', 'Original', 'Oriole', 'Orlando', 'Oroonoko', 'Orpheus', 'Orville', 'Oscar', 'Osmosis', 'Ostrich', 'Outie', 'Outlaw', 'Outlet', 'Outrageous', 'Owl', 'Ox', 'Oyster', 'Ozzie', 'PJ', 'Pacemaker', 'Pachyderm', 'Padlock', 'Pagoda', 'Paisley', 'Paladin', 'Palomino', 'Panache', 'Panda', 'Pandemonium', 'Panfried', 'Pantaloon', 'Panther', 'Paprika', 'Papyrus', 'Parachute', 'Paradise', 'Parakeet', 'Parallax', 'Paris', 'Parmesan', 'Parsley', 'Parsnip', 'Pascal', 'Pasternak', 'Patches', 'Patchouli', 'Patchwork', 'Patience', 'Pauper', 'Paws', 'Peachy', 'Peanut', 'Pearl', 'Pebbles', 'Pecan', 'Peck Peck', 'Peepers', 'Peewee', 'Pegasus', 'Pele', 'Pendragon', 'Penguin', 'Peoria', 'Pepper', 'Peppermint', 'Pepperoni', 'Peppy', 'Percival', 'Periwinkle', 'Peroxide', 'Persephone', 'Perseus', 'Persnickety', 'Pesto', 'Pesty', 'Petra', 'Petros', 'Pettibones', 'Phantom', 'Philadelphia', 'Phoenix', 'Phynotaprox', 'Piano', 'Piccolo', 'Pickles', 'Pickwick', 'Pico', 'Piecewise', 'Pigpen', 'Pilaf', 'Pimento', 'Ping', 'Ping Pong', 'Pip', 'Pipsqueak', 'Pistachio', 'Piston', 'Pitabread', 'Pixel', 'Pizza', 'Pizzazz', 'Plato', 'Plumb Bob', 'Plumpy', 'Plunko', 'Pluto', 'Pocket', 'Poco', 'Poe', 'Pointer', 'Pointy', 'Pokey', 'Polaris', 'Polifax', 'Pollywog', 'Poltergeist', 'Pom Pom', 'Poofball', 'Poofy', 'Popcorn', 'Poppy', 'Porcupine', 'Porkchop', 'Portobello', 'Postcard', 'Potbelly', 'Potpie', 'Potzy', 'Pouncer', 'Powder', 'Prancer', 'Preston', 'Pretzel', 'Priscilla', 'Prissy', 'Procyon', 'Prodigy', 'Proton', 'Providence', 'Prowler', 'Proxy', 'Pudding', 'Puddles', 'Pudgy', 'Puff', 'Puffball', 'Puffin', 'Puffy', 'Pugnose', 'Pumpernickel', 'Pumpkin', 'Punch', 'Punky', 'Puree', 'Purrfect', 'Pygmy', 'Pyrex', 'Python', 'Quack Quack', 'Quagmire', 'Quartz', 'Quasar', 'Quasi', 'Queasy', 'Quenby', 'Quesadilla', 'Quester', 'Quetzal', 'Quibbler', 'Quicksand', 'Quicksilver', 'Quinn', 'Quippy', 'Quiqui', 'Quirky', 'Quixote', 'Quizzical', 'Quizzix', 'Rabbit', 'Raccoon', 'Racecar', 'Rachel', 'Radar', 'Radcliffe', 'Radish', 'Radium', 'Radix', 'Radman', 'Raffle', 'Ragamuffin', 'Ragdoll', 'Ragmop', 'Rags', 'Ragtime', 'Ragweed', 'Rainbow', 'Rainstorm', 'Rainwater', 'Raisin', 'Rambler', 'Ramrod', 'Ranger', 'Rascal', 'Raspberry', 'Rathbone', 'Rattler', 'Raven', 'Ravioli', 'Rawhide', 'Raymond', 'Razzmatazz', 'Reactor', 'Recall', 'Recycler', 'Redwood', 'Relay', 'Relic', 'Relish', 'Renegade', 'Repeat', 'Rescuer', 'Retread', 'Rewind', 'Rhapsody', 'Rhinestone', 'Rhino', 'Rhymer', 'Rhythm', 'Ribbons', 'Rickrack', 'Ricochet', 'Riddler', 'Riddles', 'Riffraff', 'Rigatoni', 'Righty', 'Ringleader', 'Ringo', 'Riot', 'Ripley', 'Ripple', 'Ripples', 'Risky', 'Ritz', 'Ritzy', 'Roamer', 'Robin', 'Rocco', 'Rocket', 'Rockhead', 'Rockwell', 'Rococo', 'Rogue', 'Rolex', 'Rollo', 'Roly Poly', 'Ronno', 'Rookie', 'Rooster', 'Roscoe', 'Rosebug', 'Rothchild', 'Rowf', 'Rowser', 'Ruffles', 'Ruggelah', 'Ruggles', 'Rumba', 'Runaway', 'Runt', 'Rushmore', 'Rusty', 'Sabotage', 'Safari', 'Saga', 'Saggy', 'Sagittarius', 'Sahara', 'Salamander', 'Salazar', 'Salinger', 'Salisbury', 'Salsa', 'Salty', 'Sam', 'Samba', 'Sammy', 'Samson', 'Samurai', 'Sandman', 'Sandy', 'Santana', 'Santiago', 'Sasha', 'Sashimi', 'Sasquatch', 'Sassy', 'Satchel', 'Satire', 'Saturn', 'Saucer', 'Saucy', 'Sausage', 'Savage', 'Saxophone', 'Scallop', 'Scamp', 'Scamper', 'Scandal', 'Scarecrow', 'Scary', 'Schlemmer', 'Schmooze', 'Schnook', 'Schnookie', 'Scone', 'Scoops', 'Scoot', 'Scooter', 'Scorpio', 'Scorpion', 'Scoundrel', 'Scout', 'Scrabble', 'Scrambler', 'Scrappy', 'Scrooge', 'Scrumptious', 'Scupper', 'Seagull', 'Seismic', 'Seltzer', 'Seneca', 'September', 'Sepulveda', 'Serengeti', 'Shackles', 'Shamrock', 'Shangri La', 'Sharky', 'Shazam', 'Sheba', 'Shelby', 'Sheldrake', 'Shelley', 'Shelton', 'Shenanigan', 'Shep', 'Sherbet', 'Sherlock', 'Sherwood', 'Shiny', 'Shmunday', 'Shoeless', 'Shogun', 'Shortcake', 'Shortstack', 'Shortstop', 'Shorty', 'Showboat', 'Showoff', 'Shredder', 'Shrimpy', 'Shylock', 'Sideshow', 'Sideways', 'Sidney', 'Silhouette', 'Silverspoon', 'Siren', 'Skeet', 'Skeeter', 'Skelton', 'Skidder', 'Skidoo', 'Skidsy', 'Skinky', 'Skipper', 'Skippy', 'Skunk', 'Skunkmuffin', 'Skunky', 'Sky', 'Skyrocket', 'Slappy', 'Slate', 'Slick', 'Slicker', 'Slippers', 'Sloth', 'Slothful', 'Slugger', 'Sly', 'Smarty', 'Smartypants', 'Smash', 'Smasher', 'Smilestone', 'Smocks', 'Smoke', 'Smoky', 'Smoocher', 'Smoothie', 'Smores', 'Smug', 'Snaggletooth', 'Snapdragon', 'Snappy', 'Snaps', 'Snarly', 'Sneaker', 'Sneakers', 'Sneezer', 'Sneezy', 'Snickers', 'Sniffer', 'Sniffler', 'Sniffles', 'Sniffy', 'Snooker', 'Snookums', 'Snooper', 'Snoots', 'Snooty', 'Snowball', 'Snowberry', 'Snowbunny', 'Snowcap', 'Snowflake', 'Snowpea', 'Snowshoe', 'Snowy', 'Snuffles', 'Snuffy', 'Snugglepot', 'Snuggles', 'Socks', 'Sodapop', 'Soho', 'Sojourner', 'Solo', 'Sonar', 'Sorbet', 'Souffle', 'Soupbowl', 'Soupy', 'Sourball', 'Sourdough', 'Southpaw', 'Spacey', 'Spades', 'Spaghetti', 'Spam', 'Sparkler', 'Sparkplug', 'Sparky', 'Sparrow', 'Sparx', 'Speck', 'Speckles', 'Spectro', 'Spectrum', 'Speedy', 'Spex', 'Sphinx', 'Spicy', 'Spider', 'Spiffy', 'Spike', 'Spiky', 'Spinach', 'Spinner', 'Spiral', 'Spirit', 'Spiro', 'Spitfire', 'Splash', 'Splashy', 'Spoiler', 'Spoof', 'Spooky', 'Sport', 'Sporty', 'Spot', 'Spots', 'Spotty', 'Spring', 'Springbok', 'Springy', 'Sprinkle', 'Sprinkles', 'Spud', 'Spunky', 'Sputnik', 'Spy', 'Squash', 'Squeak', 'Squeaky', 'Squid', 'Squiddly', 'Squidface', 'Squiggle', 'Squiggly', 'Squishy', 'Stalactite', 'Stalagmite', 'Starbuck', 'Stardust', 'Starfire', 'Stargazer', 'Starlight', 'Steamy', 'Steely', 'Stereopsis', 'Sterling', 'Stethoscope', 'Stetson', 'Stewart', 'Sticky', 'Stinger', 'Stingy', 'Stinkwell', 'Stinky', 'Stony', 'Storm', 'Stormy', 'Stowaway', 'Strange', 'Strawberry', 'Strep', 'Stretch', 'Stretchy', 'Stripe', 'Striper', 'Stripes', 'Strudel', 'Stubby', 'Stumpy', 'Sudsy', 'Suede', 'Sugar', 'Sugarbaby', 'Sulfur', 'Sultan', 'Sumo', 'Sundance', 'Sundown', 'Sunflower', 'Sunset', 'Superdoodle', 'Surprise', 'Sushi', 'Swabbie', 'Swampy', 'Sweathog', 'Sweetheart', 'Sweetie Pie', 'Sweetness', 'Sweets', 'Swift', 'Swifty', 'Swizzle', 'Sylvester', 'Synergy', 'Syrup', 'TNT', 'Tablespoon', 'Tabloid', 'Taboo', 'Tacky', 'Taco', 'Tacos', 'Tadpole', 'Taffeta', 'Taffy', 'Tagalong', 'Tags', 'Tahiti', 'Taj', 'Takeout', 'Talisman', 'Tallahassee', 'Tallulah', 'Talon', 'Tamale', 'Tambourine', 'Tandem', 'Tanglewood', 'Tango', 'Tank', 'Tanker', 'Tanner', 'Tantrum', 'Tapestry', 'Tapioca', 'Tapper', 'Tarantula', 'Tardy', 'Target', 'Tarkington', 'Tartan', 'Tasty', 'Tatsu', 'Tatters', 'Tattletale', 'Tattoo', 'Taurus', 'Tawny', 'Taxbreak', 'Taxcut', 'Taxi', 'Taxicab', 'Teacup', 'Teaky', 'Teapot', 'Teaspoon', 'Technicolor', 'Teddy', 'Teehee', 'Teevee', 'Telegram', 'Telepath', 'Telescope', 'Telex', 'Tempest', 'Templeton', 'Tempo', 'Tenderfoot', 'Tennessee', 'Tennisball', 'Tennyson', 'Terence', 'Teriyaki', 'Terror', 'Texas', 'Textbook', 'Thackeray', 'Thaddeus', 'Tharp', 'Tharpo', 'Theoretical', 'Theory', 'Thermos', 'Thickie', 'Thimble', 'Thistle', 'Thorny', 'Thriller', 'Thud', 'Thumbtack', 'Thunder', 'Thunderbird', 'Thurgood', 'Thursday', 'Thyme', 'Tickets', 'Tidbit', 'Tilly', 'Tilted', 'Timbuktu', 'Tinkles', 'Tinsel', 'Tintin', 'Tiny', 'Tipperary', 'Tipsy', 'Titanium', 'Tizzy', 'Toad', 'Toadstool', 'Toady', 'Toaster', 'Toby', 'Toco', 'Toffee', 'Tofu', 'Tokyo', 'Toledo', 'Tomato', 'Tomorrow', 'Tonic', 'Toodleoo', 'Toodles', 'Toot', 'Tooter', 'Toothsome', 'Toothy', 'Tootles', 'Toots', 'Topeka', 'Topper', 'Topsy', 'Tornado', 'Torpedo', 'Tortellini', 'Tortoni', 'Totem', 'Toto', 'Totsy', 'Toucan', 'Toupee', 'Toy', 'Toybox', 'Tracer', 'Trailblazer', 'Tramp', 'Trampoline', 'Trancer', 'Trapper', 'Traveller', 'Treasure', 'Treetop', 'Trekker', 'Trickster', 'Trickstick', 'Tricky', 'Trident', 'Trilogy', 'Trinidad', 'Trinket', 'Trio', 'Tripper', 'Troll', 'Tropix', 'Trouble', 'Trout', 'Trumpet', 'Trusty', 'Tsunami', 'Tuba', 'Tubbo', 'Tubby', 'Tuesday', 'Tuffy', 'Tugboat', 'Tumblebumble', 'Tumbler', 'Tumbleweed', 'Tunafish', 'Tundra', 'Turbo', 'Turkey', 'Turnip', 'Turtleneck', 'Tutu', 'Twaddler', 'Twain', 'Tweezer', 'Twerp', 'Twiggs', 'Twilight', 'Twinkle', 'Twirler', 'Twister', 'Twittery', 'Tycoon', 'Typhoon', 'UFO', 'Ubu', 'Ugly', 'Ukelele', 'Ultimate', 'Ultra', 'Ultrasonic', 'Ultra', 'Umber', 'Umbrella', 'Umpire', 'Underfoot', 'Underwood', 'Unicorn', 'Unique', 'Upbeat', 'Upshot', 'Upside', 'Upstart', 'Uptight', 'Urchin', 'Ursula', 'Utoo', 'Utopia', 'Vacuum', 'Vagabond', 'Valentine', 'Valerie', 'Valiant', 'Vamp', 'Vanderbilt', 'Vanilla', 'Vanity', 'Vaudeville', 'Vegas', 'Velvet', 'Venezuela', 'Vermicelli', 'Vermont', 'Vern', 'Vernon', 'Vertigo', 'Vexy', 'Vibes', 'Victrola', 'Video', 'Viking', 'Vinaigrette', 'Vintage', 'Viper', 'Virtuosity', 'Vivian', 'Voodoo', 'Vulcan', 'Vulture', 'Wabble', 'Wabbler', 'Wacky', 'Waddle', 'Waddler', 'Waddles', 'Wafer', 'Waffle', 'Waffler', 'Waffles', 'Wag', 'Waggles', 'Wags', 'Wagtail', 'Wahoo', 'Waikiki', 'Wallaby', 'Wallaroo', 'Walnut', 'Walnuts', 'Walrus', 'Waltzy', 'Wanderer', 'Warthog', 'Warty', 'Wasco', 'Waterberry', 'Watergate', 'Wavy', 'Waxy', 'Weasel', 'Weaver', 'Webster', 'Wedgewood', 'Wedgie', 'Wednesday', 'Weekend', 'Weepy', 'Weezer', 'Weezie', 'Welcome', 'Wellington', 'Wembly', 'Wendy', 'Wesley', 'Whatchamacallit', 'Whatever', 'Wheatcake', 'Wheedler', 'Whim', 'Whimsy', 'Whipple', 'Whirlwind', 'Whisker', 'Whiskers', 'Whisper', 'Whistler', 'Whistlestop', 'Whittaker', 'Whiz', 'Whizzer', 'Wholesale', 'Whoopdedoo', 'Whoopie', 'Wiggle', 'Wiggler', 'Wiggles', 'Wiggly', 'Wildberry', 'Wildcat', 'Wildflower', 'Wildwood', 'Willy', 'Wily', 'Wimbledon', 'Wimpster', 'Wimpy', 'Windjammer', 'Wing', 'Wink', 'Winker', 'Winkle', 'Winkles', 'Winky', 'Winx', 'Wiretap', 'Wisecrack', 'Wispy', 'Wisteria', 'Wizard', 'Wizkid', 'Wobble', 'Wobbler', 'Wobbles', 'Wobbly', 'Wolverine', 'Wonder', 'Wonderbunny', 'Wonton', 'Wooble', 'Woobles', 'Woobly', 'Woodchuck', 'Woodstock', 'Woodwind', 'Woof Woof', 'Woofer', 'Woolly', 'Woolworth', 'Wordsworth', 'Worm', 'Wormy', 'Wrex', 'Wriggler', 'Wriggles', 'Wriggly', 'Wrinkle', 'Wrinkler', 'Wrinkles', 'Wuggums', 'Wumpus', 'Wuzzie', 'Wyoming', 'Xanadu', 'Xenobia', 'Xifto', 'Xinx', 'Xinxu', 'Xippy', 'Xowie', 'Xoxo', 'Xoxxy', 'Xpresso', 'Yahoo', 'Yammie', 'Yancy', 'Yappy', 'Yardstick', 'Yasu', 'Yeasty', 'Yellowstone', 'Yelper', 'Yertle', 'Yesterday', 'Yeti', 'Yippie', 'Yodeler', 'Yoga', 'Yoko', 'Yonder', 'Yonkers', 'Yorty', 'Yosemite', 'Yukon', 'Yummy', 'Yutu', 'Yvonne', 'Zack', 'Zadok', 'Zaftig', 'Zaire', 'Zapata', 'Zappa', 'Zazen', 'Zebu', 'Zebulon', 'Zen', 'Zenith', 'Zenzen', 'Zepellin', 'Zephyr', 'Zeppo', 'Zero', 'Zesty', 'Zigzag', 'Zilch', 'Zillion', 'Zing', 'Zinger', 'Zingy', 'Zipcode', 'Zircon', 'Zodiac', 'Zoltan', 'Zonk', 'Zoo', 'Zooble', 'Zoom', 'Zoomer', 'Zoomy', 'Zowie', 'Zucchini', 'Zylon'] +PetNameDictionary = {} +id = 0 + +for dictionary in [BoyPetNames, GirlPetNames, NeutralPetNames]: + for name in dictionary: + PetNameDictionary[id] = name + id += 1 + +def getPetName(uniqueID): + try: + return PetNameDictionary[uniqueID] + except: + return PetNameDictionary[0] + +def getRandomPetName(gender = None, seed = None): + if seed is not None: + random.seed(seed) + + nameList = list(NeutralPetNames) + + if gender == 0: + nameList += BoyPetNames + else: + nameList += GirlPetNames + + return random.choice(nameList) + +def getPetNameId(name): + for key, value in PetNameDictionary.items(): + if name == value: + return key + + return 0 + +DonaldChatter = ["I'm glad you're here today!", + "You look like you're having fun.", + "Oh boy, I'm having a good day.", + 'I hope you are enjoying your ride!', + 'I like meeting new people.', + 'Have fun in my neighborhood.', + 'I like to make people laugh.', + 'Watch out for the Cogs!', + 'Come aboard!', + "I heard it's snowing at the Brrrgh.", + 'Looks like the trolley is coming!', + 'Sometimes I play trolley games just to eat the fruit pie!', + 'I hope you are enjoying your stay in Toontown!', + 'I like what you are wearing.', + 'I love to play tag. Do you?'] +NPCFriendUnavailable = 'Unavailable' +FireTalkMessage = "You're fired!" + +RestockAskMessage = "Would you like to\nrestock %s laff for %s jellybeans?" +RestockNoMoneyGuiMessage = "\n\x01WLRed\x01Not enough jellybeans\x02" +RestockFullLaffMessage = "You're already happy!" +RestockLessLaffMessage = "Why would you want to be less happy than you are right now?" +RestockNoMoneyMessage = "You don't have enough jellybeans for that!" +RestockSuccessfulMessage = "You're welcome! Have fun!" +InVP = ' in a V.P. Battle' +InFieldOffice = ' in a Sellbot Field Office' +CogPanelLevel = 'Level %s' +CogPanelSkeleton = 'Skeleton' +CogPanelVirtual = 'Virtual' +CogPanelRevives = 'v%s.0' +CogPanelWaiter = 'Waiter' + +def convertSecondsToDate(seconds): + m, s = divmod(seconds, 60) + h, m = divmod(m, 60) + + return '%d:%02d:%02d' % (h, m, s) + +ToonDefeatedMessage = '%s was defeated!' + +BugReportButton = 'Report a Bug' +BugReportNotice = 'Attention!\n\nThis button will open a browser which will send you to a third party bug tracker website. This site requires an Ubuntu One account to login. It may ask you to create an account.\n\nAre you sure you want to continue?' + +CodeRedemptionWarning = 'NOTICE: All codes can only be entered once!' + +CogInterfaceLabelOn = 'The cog battle interface is on.' +CogInterfaceLabelOff = 'The cog battle interface is off.' +TpTransitionLabelOn = 'The teleport transition is on.' +TpTransitionLabelOff = 'The teleport transition is off.' +FieldOfViewLabel = 'Field of View:' +NametagStyleLabel = 'Nametag Style:' +FishingPoleLabel = 'Fishing Rod:' + +BossLocations = { + 'c': 'Bossbot Clubhouse\nBanquet', + 'l': "Lawbot Courthouse\nBumpy Bumblebehr's Trial", + 'm': 'Cashbot Treasury Vault', + 's': 'Sellbot Towers\nRooftop' +} +SendCogBossTaunts = { + 's': [ + "We're going door to door to give you an dedious offer.", + "It's time for your estimated cost.", + 'You toons never learn when to snatch up a great deal.', + "Call now and we'll reduce your laff, absolutely free.", + 'Sell, sell, sell!', + 'Let us Sellbots give you a helping hand.', + 'The deal has only just begun.', + 'Going sad already? How about we settle a retirement plan.', + 'Get them! Get them all!' + ], + 'm': [ + "Let's show you how money talks.", + "We'll bring you into bankruptcy.", + 'The Jellybean is only worth a tenth of the cogbuck!', + 'Hey! Those cogs cost me money!', + "I'll put you on the next train back to the playground!", + 'You toons are wasting so much of your currency fighting us.', + 'It seems Toontown has high amounts of debt to the Cogs.', + "Invest in Cogbucks, it'll fix Toontown right up!", + 'Do toons understand the value of money?' + ], + 'l': [ + 'Order in the court!', + 'The defendant is appearing to be proven guilty.', + 'Justice is important to keep a stable society.', + 'Toontown will be full of corruption and greed.', + 'Toons do not understand the importance of the Cog order.', + 'The state of your actions will prove you toons guilty.', + "Bah! Don't you tell me how to run a trial!", + 'My decision will state what to legally do to you Toons.', + 'This blindfold protects me from the horrible corruption!' + ] +} +VPRampMessage = "I don't have time for this, I'm late for my meeting!" +VPDeathTaunt = "WAAAH! YOU HAVEN'T SEEN THE LAST OF ME!" +DirectedAttackBossTaunts = { + 's': [ + 'Have a free sample!', + 'I see you over there!', + 'My products beat out all of Toontown!', + "Don't think I've skipped you, %(toon)s!" + ], + 'm': [ + "Here's money well spent.", + 'Hey! Get away from that crane!', + 'Stop!', + '%(toon)s, get over here!' + ], + 'c': [ + 'These are brand new tables!', + "A little water isn't going to affect me!", + 'Bug off! This is my clubhouse!', + "You've ruined my banquet! Now you'll pay!" + ] +} +VPSpinMessages = [ + "Let's get these ideas going!", + "I wouldn't get too close. My patents protect these gears.", + 'Why worry about problems when you can shake them off?', + "I may be old, but I'm still reliable." +] +JumpBossTaunts = { + 's': [ + 'STOP!', + 'Pay attention to my pitch!', + 'This is a waste of my time!', + 'Time to send you back to the playground!' + ], + 'l': [ + "You're all in contempt of court!" + ], + 'm': [ + "These Goons weren't free you know!", + 'HEY, THOSE ARE VINTAGE SAFES!', + 'This will cost you a lot more than candy!', + "I'll buy out Toontown if it means getting rid of you Toons!" + ] +} +CEOSpeech = [ + "Good evening Cogs, it's an honor having you here tonight in the Bossbot Clubhouse.", + 'I apologize for sending invitations on such short notice, but your presence is urgent.', + 'Lately, it appears the Toons have created strategies to fight against us.', + "This is a sign that each day us Cogs are getting weaker, and they're getting stronger.", + 'I have gathered you all here today not only to state this problem, but to propose an idea.', + 'This idea is the same one that was mentioned in the meeting 3 weeks ago, as to hire a new department of employees.', + 'If we were to do such however, they would be more or less assisting over attacking.', + 'We also can consider the odds that a department would have to be treated special.', + 'On another note, business operations are higher than ever.', + 'Because of this, improvement for HQs will be funded.', + 'And thus we, the Cog Nation, are striving.', + 'I am very delighted by this news, I just wanted to thank you all for working so hard for this news.', + "Wait, what's going on? I can't see but I hear explosions." +] # Len of words + 10 + +CrateRewardMessage1 = 'Nice! You have earned a crate! It will arrive in your mailbox shortly.' +CrateRewardMessage2 = 'You can open it with keys from boss battles.' +CrateRewardMessages = [CrateRewardMessage1, CrateRewardMessage2] +CrateNotOwner = 'Sorry, this is not your crate.' +CrateNoKeys = 'Sorry, but you have no keys. You can find some in the cog facilities.' +CrateAskToUse = 'Would you like to use a key to open this crate?' +CrateBeanPrize = "Congratulations! You found %s jellybeans. They've been automatically added to your jellybean bank!" +CrateBuffPrize = 'Congratulations! %s' +CrateNametagPrize = "Congratulations! You've received a nametag. Check your mailbox to find out which one!" +CrateEmotePrize = "Congratulations! You've unlocked a new emote. Check your mailbox to pick it up!" +CrateClothingPrize = "Congratulations! You've received a new clothing item. Check your mailbox to check it out!" +CrateAccessoryPrize = 'Congratulations! You found a new accessory for your Toon. Check your mailbox and rock it!' + +Stats = [ + 'Cogs defeated: %s', + 'V2.0 cogs defeated: %s', + 'Skelecogs defeated: %s', + 'Jellybeans spent: %s', + 'Jellybeans earnt: %s', + 'Tasks completed: %s', + 'Total VP defeats: %s', + 'Total CFO defeats: %s', + 'Total CJ defeats: %s', + 'Total CEO defeats: %s', + 'Gone sad: %s times', + 'Buildings liberated: %s', + 'Offices defeated: %s', + 'Items ordered: %s', + 'Fish caught: %s', + 'Flowers picked: %s', + 'Races completed: %s', + 'Golf holes played: %s', + 'Total SOS cards: %s', + 'Total unites: %s', + 'Total pink slips: %s', + 'Total gags used: %s' +] +StatPageTitle = 'Statistics' +StatPageClear = 'Clear' +StatPageClearAsk = 'Are you sure you want to clear your stats? This is an irreversible action!' +StatPageClearDone = 'Your stats have been cleared.' + +ChairAskToUse = 'Would you like to sit on this chair?' + +FriendSecretIntro = "If you are playing Toontown Stride with someone you know in the real world, you can become True Friends. You can chat using the keyboard with your True Friends. Other Toons won't understand what you're saying.\n\nYou do this by getting a True Friend Code. Tell the True Friend Code to your friend, but not to anyone else. When your friend types in your True Friend Code on his or her screen, you'll be True Friends in Toontown!" +FriendSecretGetSecret = 'Get a True Friend Code' +FriendSecretEnterSecret = 'If you have a True Friend Code from someone you know, type it here.' +FriendSecretOK = lOK +FriendSecretEnter = 'Enter True Friend Code' +FriendSecretCancel = lCancel +FriendSecretGettingSecret = 'Getting True Friend Code. . .' +FriendSecretGotSecret = "Here is your new True Friend Code. Be sure to write it down!\n\nYou may give this True Friend Code to one person only. Once someone types in your True Friend Code, it will not work for anyone else. If you want to give a True Friend Code to more than one person, get another True Friend Code.\n\nThe True Friend Code will only work for the next three days. Your friend will have to type it in before it goes away, or it won't work.\n\nYour True Friend Code is:" +FriendSecretTooMany = "Sorry, you can't have any more True Friend Codes today. You've already had more than your fair share!\n\nTry again tomorrow." +FriendSecretTryingSecret = 'Trying True Friend Code. . .' +FriendSecretEnteredSecretUnknown = "That's not anyone's True Friend Code. Are you sure you spelled it correctly?\n\nIf you did type it correctly, it may have expired. Ask your friend to get a new True Friend Code for you (or get a new one yourself and give it to your friend)." +FriendSecretEnteredSecretFullYou = "You can't be True Friends because you have too many friends on your friends list." +FriendSecretEnteredSecretFullHim = "You can't be True Friends because %s has too many friends on his friends list." +FriendSecretAlreadyFriends = 'You are already True Friends!' +FriendSecretAlreadyFriendsName = 'You are already True Friends with %s!' +FriendSecretEnteredSecretSelf = 'You just typed in your own True Friend Code!' +FriendSecretTooFast = 'You are redeeming codes too fast! Please wait a few seconds.' +FriendSecretNowFriends = 'You are now True Friends with %s!' + +GroupAskNoAccess = 'Sorry, but you have no teleport access %s %s.\n\nWould you still like to teleport to %s?' +GroupAskNoAccessSame = 'Sorry, but you have no teleport access %s %s.' +GroupAskAccess = 'Would you like to teleport %s %s in %s?' + +TVNotOwner = 'Sorry, but this is not your TV.' +TVInvalidVideo = "Sorry, but we can't play that video. Make sure it is a MP4 video." +TVUnknownVideo = "Oops! Looks like the owner has picked a video to play which isn't currently on your computer!" +TVUnknownVideoPack = 'Oops! Looks like the owner has picked a video to play, but you need to download the %s TV Pack in the launcher.' +TVChooseVideo = 'Choose a video to play!' +TVOK = 'The video you selected is now playing!' +TVPacks = ['QuackityHQ'] + +GagPopup = '%s: %s\nGags: %s' + +ColorfulToon = 'Vibrant' +ColorAdvanced = 'Advanced' +ColorBasic = 'Basic' +ColorAll = 'All' + +ShardPagePreferred = 'Preferred' +ShardPageShardTitle = '%s Population: %s' +ShardPageTeleport = 'Teleport to\n%s' + +TeleportButton = 'Teleport' +TeleportButtonNoMoney = 'Sorry, but you need %s jellybeans to teleport!' +TeleportButtonConfirm = 'Would you like to spend %s jellybeans to teleport?' +TeleportButtonTakenOver = 'Sorry, but this shop has been taken over by the Cogs!' + +BattleCogPopup = '\x01androidGreen\x01Group attacks:\x02\n%s\n\n\x01androidGreen\x01Regular attacks:\x02\n%s' +BattleCogPopupAttack = '%s %s HP' +BattleCogPopupAttackDanger = '\x01red\x01' + BattleCogPopupAttack + '\x02' +BattleCogPopupDanger = '\x01red\x01Dangerous!\x02\n\n' +BattleCogPopupDangerColor = '\x01red' + +SuitPageAttackFormat = 'Levels: %s-%s\n\n' + BattleCogPopup +SuitPageNoAttacks = 'None' + +BattleGagPopup = '%s: %s\nGags left: %s' +BattleSOSPopup = '\x01azure\x01%s\x02\n%s\n%s%s stars\nSOS left: %s' +BattleSOSPopupHeal = 'Heals' +BattleSOSPopupHarm = 'Deals' +BattleSOSPopupHP = '%s %s HP\n' + +DetailPanelSOS = 'SOS Cards' + +TeleportLabelOn = 'Accepting teleports.' +TeleportLabelOff = 'Not accepting teleports.' +TeleportPanelNoTeleport = '%s needs some time alone right now.' + +InventoryDeleteAll = 'DELETE ALL' +InventoryDeleteConfirm = "Are you sure you want to delete all your gags? Don't worry, your level 7 gags are safe!" + +ClothesGUICount = '%s/%s' + +FpsMeterLabelOn = 'The frame rate meter is on.' +FpsMeterLabelOff = 'The frame rate meter is off.' + +DefaultDoodleName = 'Smiley' + +Blacklist = [ + "$1ut", + "$h1t", + "$hit", + "$lut", + "'ho", + "'hobag", + "a$$", + "anal", + "anus", + "ass", + "assmunch", + "b1tch", + "ballsack", + "bastard", + "beaner", + "beastiality", + "biatch", + "beeyotch", + "bitch", + "bitchy", + "blow job", + "blow me", + "blowjob", + "bollock", + "bollocks", + "bollok", + "boner", + "boob", + "bugger", + "buttplug", + "c-0-c-k", + "c-o-c-k", + "c-u-n-t", + "c.0.c.k", + "c.o.c.k.", + "c.u.n.t", + "jerk", + "jackoff", + "jackhole", + "j3rk0ff", + "homo", + "hom0", + "hobag", + "hell", + "h0mo", + "h0m0", + "goddamn", + "goddammit", + "godamnit", + "god damn", + "ghey", + "ghay", + "gfy", + "gay", + "fudgepacker", + "fudge packer", + "fuckwad", + "fucktard", + "fuckoff", + "fucker", + "fuck-tard", + "fuck off", + "fuck", + "fellatio", + "fellate", + "felching", + "felcher", + "felch", + "fartknocker", + "fart", + "fannybandit", + "fanny bandit", + "faggot", + "fagg", + "fag", + "f.u.c.k", + "f-u-c-k", + "f u c k", + "dyke", + "douchebag", + "douche", + "douch3", + "doosh", + "dildo", + "dike", + "dick", + "damnit", + "damn", + "dammit", + "d1ldo", + "d1ld0", + "d1ck", + "d0uche", + "d0uch3", + "cunt", + "cumstain", + "cum", + "crap", + "coon", + "cock", + "clitoris", + "clit", + "cl1t", + "cawk", + "c0ck", + "jerk0ff", + "jerkoff", + "jizz", + "knob end", + "knobend", + "labia", + "lmfao", + "lul", + "moolie", + "muff", + "nigga", + "nigger", + "p.u.s.s.y.", + "penis", + "piss", + "piss-off", + "pissoff", + "prick", + "pube", + "pussy", + "queer", + "retard", + "retarded", + "s hit", + "s-h-1-t", + "s-h-i-t", + "s.h.i.t.", + "scrotum", + "sex", + "sh1t", + "shit", + "slut", + "smegma", + "t1t", + "tard", + "terd", + "tit", + "tits", + "titties", + "turd", + "twat", + "vag", + "vagina", + "wank", + "wetback", + "whore", + "whoreface", + "F*ck", + "sh*t", + "pu$$y", + "p*ssy", + "diligaf", + "wtf", + "stfu", + "fu*ck", + "fack", + "shite", + "fxck", + "sh!t", + "@sshole", + "assh0le", + "assho!e", + "a$$hole", + "a$$h0le", + "a$$h0!e", + "a$$h01e", + "assho1e", + "wh0re", + "f@g", + "f@gg0t", + "f@ggot", + "motherf*cker", + "mofo", + "cuntlicker", + "cuntface", + "dickbag", + "douche waffle", + "jizz bag", + "cockknocker", + "beatch", + "fucknut", + "nucking futs", + "mams", + "carpet muncher", + "ass munch", + "ass hat", + "cunny", + "quim", + "clitty", + "fuck wad", + "kike", + "spic", + "wop", + "chink", + "wet back", + "mother humper", + "feltch", + "feltcher", + "FvCk", + "ahole", + "nads", + "spick", + "douchey", + "Bullturds", + "gonads", + "bitch", + "butt", + "fellatio", + "lmao", + "s-o-b", + "spunk", + "he11", + "jizm", + "jism", + "bukkake", + "shiz", + "wigger", + "gook", + "ritard", + "reetard", + "masterbate", + "masturbate", + "goatse", + "masterbating", + "masturbating", + "hitler", + "nazi", + "tubgirl", + "GTFO", + "FOAD", + "r-tard", + "rtard", + "hoor", + "g-spot", + "gspot", + "vulva", + "assmaster", + "viagra", + "Phuck", + "frack", + "fuckwit", + "assbang", + "assbanged", + "assbangs", + "asshole", + "assholes", + "asswipe", + "asswipes", + "b1tch", + "bastards", + "bitched", + "bitches", + "blow jobs", + "boners", + "bullshit", + "bullshits", + "bullshitted", + "cameltoe", + "camel toe", + "camel toes", + "chinc", + "chincs", + "chink", + "chode", + "chodes", + "clit", + "clits", + "cocks", + "coons", + "cumming", + "cunts", + "d1ck", + "dickhead", + "dickheads", + "doggie-style", + "dildos", + "douchebags", + "dumass", + "dumb ass", + "dumbasses", + "dykes", + "f-u-c-k", + "faggit", + "fags", + "fucked", + "fucker", + "fuckface", + "fucks", + "godamnit", + "gooks", + "humped", + "humping", + "jackass", + "jap", + "japs", + "jerk off", + "jizzed", + "kikes", + "knobend", + "kooch", + "kooches", + "kootch", + "mother fucker", + "mother fuckers", + "motherfucking", + "niggah", + "niggas", + "niggers", + "p.u.s.s.y.", + "porch monkey", + "porch monkeys", + "pussies", + "queers", + "rim job", + "rim jobs", + "sand nigger", + "sand niggers", + "s0b", + "shitface", + "shithead", + "shits", + "shitted", + "s.o.b.", + "spik", + "spiks", + "twats", + "whack off", + "whores", + "zoophile", + "m-fucking", + "mthrfucking", + "muthrfucking", + "mutherfucking", + "mutherfucker", + "mtherfucker", + "mthrfucker", + "mthrf*cker", + "whorehopper", + "maternal copulator", + "(!)", + "whoralicious", + "whorealicious", + "( Y )", + "(@ Y @)", + "(. Y .)", + "aeolus", + "Analprobe", + "Areola", + "areole", + "aryan", + "arian", + "asses", + "assfuck", + "azazel", + "baal", + "Babes", + "bang", + "banger", + "Barf", + "bawdy", + "Beardedclam", + "beater", + "Beaver", + "beer", + "bigtits", + "bimbo", + "Blew", + "blow", + "blowjobs", + "blowup", + "bod", + "bodily", + "boink", + "Bone", + "boned", + "bong", + "Boobies", + "Boobs", + "booby", + "booger", + "Bookie", + "Booky", + "bootee", + "bootie", + "Booty", + "Booze", + "boozer", + "boozy", + "bosom", + "bosomy", + "bowel", + "bowels", + "bra", + "Brassiere", + "breast", + "breasts", + "bung", + "babe", + "bush", + "buttfuck", + "cocaine", + "kinky", + "klan", + "panties", + "pedophile", + "pedophilia", + "pedophiliac", + "punkass", + "queaf", + "rape", + "scantily", + "essohbee", + "shithouse", + "smut", + "snatch", + "toots", + "doggie style", + "anorexia", + "bulimia", + "bulimiic", + "burp", + "busty", + "Buttfucker", + "caca", + "cahone", + "Carnal", + "Carpetmuncher", + "cervix", + "climax", + "Cocain", + "Cocksucker", + "Coital", + "coke", + "commie", + "condom", + "corpse", + "Coven", + "Crabs", + "crack", + "Crackwhore", + "crappy", + "cuervo", + "Cummin", + "Cumshot", + "cumshots", + "Cunnilingus", + "dago", + "dagos", + "damned", + "dick-ish", + "dickish", + "Dickweed", + "anorexic", + "prostitute", + "marijuana", + "LSD", + "PCP", + "diddle", + "dawgie-style", + "dimwit", + "dingle", + "doofus", + "dopey", + "douche", + "Drunk", + "Dummy", + "Ejaculate", + "enlargement", + "erect", + "erotic", + "exotic", + "extacy", + "Extasy", + "faerie", + "faery", + "fagged", + "fagot", + "Fairy", + "fisted", + "fisting", + "Fisty", + "floozy", + "fondle", + "foobar", + "foreskin", + "frigg", + "frigga", + "fubar", + "Fucking", + "fuckup", + "ganja", + "gays", + "glans", + "godamn", + "goddam", + "Goldenshower", + "gonad", + "gonads", + "Handjob", + "hebe", + "hemp", + "heroin", + "herpes", + "hijack", + "Hiv", + "Homey", + "Honky", + "hooch", + "hookah", + "Hooker", + "Hootch", + "hooter", + "hooters", + "hump", + "hussy", + "hymen", + "inbred", + "incest", + "injun", + "jerked", + "Jiz", + "Jizm", + "horny", + "junkie", + "junky", + "kill", + "kkk", + "kraut", + "kyke", + "lech", + "leper", + "lesbians", + "lesbos", + "Lez", + "Lezbian", + "lezbians", + "Lezbo", + "Lezbos", + "Lezzie", + "Lezzies", + "Lezzy", + "loin", + "loins", + "lube", + "Lust", + "lusty", + "Massa", + "Masterbation", + "Masturbation", + "maxi", + "Menses", + "Menstruate", + "Menstruation", + "meth", + "molest", + "moron", + "Motherfucka", + "Motherfucker", + "murder", + "Muthafucker", + "nad", + "naked", + "napalm", + "Nappy", + "nazism", + "negro", + "niggle", + "nimrod", + "ninny", + "Nipple", + "nooky", + "Nympho", + "Opiate", + "opium", + "oral", + "orally", + "organ", + "orgasm", + "orgies", + "orgy", + "ovary", + "ovum", + "ovums", + "Paddy", + "pantie", + "panty", + "Pastie", + "pasty", + "Pecker", + "pedo", + "pee", + "Peepee", + "Penetrate", + "Penetration", + "penial", + "penile", + "perversion", + "peyote", + "phalli", + "Phallic", + "Pillowbiter", + "pimp", + "pinko", + "pissed", + "pms", + "polack", + "porn", + "porno", + "pornography", + "pot", + "potty", + "prig", + "prude", + "pubic", + "pubis", + "punky", + "puss", + "Queef", + "quicky", + "Racist", + "racy", + "raped", + "Raper", + "rapist", + "raunch", + "rectal", + "rectum", + "rectus", + "reefer", + "reich", + "revue", + "risque", + "rum", + "rump", + "sadism", + "sadist", + "satan", + "scag", + "schizo", + "screw", + "Screwed", + "scrog", + "Scrot", + "Scrote", + "scrud", + "scum", + "seaman", + "seamen", + "seduce", + "semen", + "sex_story", + "sexual", + "Shithole", + "Shitter", + "shitty", + "s*o*b", + "sissy", + "skag", + "slave", + "sleaze", + "sleazy", + "sluts", + "smutty", + "sniper", + "snuff", + "sodom", + "souse", + "soused", + "sperm", + "spooge", + "Stab", + "steamy", + "Stiffy", + "stoned", + "strip", + "Stroke", + "whacking off", + "suck", + "sucked", + "sucking", + "tampon", + "tawdry", + "teat", + "teste", + "testee", + "testes", + "Testis", + "thrust", + "thug", + "tinkle", + "Titfuck", + "titi", + "titty", + "whacked off", + "toke", + "tramp", + "trashy", + "tush", + "undies", + "unwed", + "urinal", + "urine", + "uterus", + "uzi", + "valium", + "virgin", + "vixen", + "vodka", + "vomit", + "voyeur", + "vulgar", + "wad", + "wazoo", + "wedgie", + "weed", + "weenie", + "weewee", + "weiner", + "weirdo", + "wench", + "whitey", + "whiz", + "Whored", + "Whorehouse", + "Whoring", + "womb", + "woody", + "x-rated", + "xxx", + "B@lls", + "yeasty", + "yobbo", + "sumofabiatch", + "doggy-style", + "doggy style", + "wang", + "dong", + "d0ng", + "w@ng", + "wh0reface", + "wh0ref@ce", + "wh0r3f@ce", + "tittyfuck", + "tittyfucker", + "tittiefucker", + "cockholster", + "cockblock", + "gai", + "gey", + "faig", + "faigt", + "a55", + "a55hole", + "gae", + "corksucker", + "rumprammer", + "slutdumper", + "niggaz", + "muthafuckaz", + "gigolo", + "pussypounder", + "herp", + "herpy", + "transsexual", + "gender dysphoria", + "orgasmic", + "cunilingus", + "anilingus", + "dickdipper", + "dickwhipper", + "dicksipper", + "dickripper", + "dickflipper", + "dickzipper", + "homoey", + "queero", + "freex", + "cunthunter", + "shamedame", + "slutkiss", + "shiteater", + "slut devil", + "fuckass", + "fucka$$", + "clitorus", + "assfucker", + "dillweed", + "cracker", + "teabagging", + "shitt", + "azz", + "fuk", + "fucknugget", + "cuntlick", + "g@y", + "@ss", + "beotch", + "a55", + "anal", + "anus", + "ar5e", + "arrse", + "arse", + "ass", + "ass-fucker", + "asses", + "assfucker", + "assfukka", + "asshole", + "assholes", + "asswhole", + "a_s_s", + "b!tch", + "b00bs", + "b17ch", + "b1tch", + "ballbag", + "balls", + "ballsack", + "bastard", + "batard", + "beastial", + "beastiality", + "bellend", + "bestial", + "bestiality", + "bi\+ch", + "biatch", + "bitch", + "bitcher", + "bitchers", + "bitches", + "bitchin", + "bitching", + "bloody", + "blow job", + "blowjob", + "blowjobs", + "boiolas", + "bollock", + "bollok", + "boner", + "boob", + "boobs", + "booobs", + "boooobs", + "booooobs", + "booooooobs", + "breasts", + "buceta", + "bugger", + "bum", + "bunny fucker", + "butt", + "butthole", + "buttmuch", + "buttplug", + "c0ck", + "c0cksucker", + "carpet muncher", + "cawk", + "chink", + "chieuse", + "chieur", + "cipa", + "cl1t", + "clit", + "clitoris", + "clits", + "cnut", + "cock", + "cock-sucker", + "cock sucker", + "cockface", + "cockhead", + "cockmunch", + "cockmuncher", + "cocks", + "cocksuck", + "cocksucked", + "cocksucker", + "cocksucking", + "cocksucks", + "cocksuka", + "cocksukka", + "cok", + "cokmuncher", + "coksucka", + "coon", + "connard", + "connasse", + "conne", + "cox", + "crap", + "cum", + "cummer", + "cumming", + "cums", + "cumshot", + "cunilingus", + "cunillingus", + "cunnilingus", + "cunt", + "cuntlick", + "cuntlicker", + "cuntlicking", + "cunts", + "cyalis", + "cyberfuc", + "cyberfuck", + "cyberfucked", + "cyberfucker", + "cyberfuckers", + "cyberfucking", + "d1ck", + "damn", + "dick", + "dickhead", + "dildo", + "dildos", + "dink", + "dinks", + "dirsa", + "dlck", + "dog-fucker", + "doggin", + "dogging", + "donkeyribber", + "doosh", + "duche", + "dyke", + "ejaculate", + "ejaculated", + "ejaculates", + "ejaculating", + "ejaculatings", + "ejaculation", + "ejakulate", + "enculer", + "f u c k", + "f u c k e r", + "f4nny", + "fag", + "fagging", + "faggitt", + "faggot", + "faggs", + "fagot", + "fagots", + "fags", + "fanny", + "fannyflaps", + "fannyfucker", + "fanyy", + "fatass", + "fcuk", + "fcuker", + "fcuking", + "feck", + "fecker", + "felching", + "fellate", + "fellatio", + "fingerfuck", + "fingerfucked", + "fingerfucker", + "fingerfuckers", + "fingerfucking", + "fingerfucks", + "fistfuck", + "fistfucked", + "fistfucker", + "fistfuckers", + "fistfucking", + "fistfuckings", + "fistfucks", + "flange", + "fook", + "fooker", + "fuck", + "fucka", + "fucked", + "fucker", + "fuckers", + "fuckhead", + "fuckheads", + "fuckin", + "fucking", + "fuckings", + "fuckingshitmotherfucker", + "fuckme", + "fucks", + "fuckwhit", + "fuckwit", + "fudge packer", + "fudgepacker", + "fuk", + "fuker", + "fukker", + "fukkin", + "fuks", + "fukwhit", + "fukwit", + "fux", + "fux0r", + "f_u_c_k", + "gangbang", + "gangbanged", + "gangbangs", + "gaylord", + "gaysex", + "goatse", + "God", + "god-dam", + "god-damned", + "goddamn", + "goddamned", + "hardcoresex", + "hell", + "heshe", + "hoar", + "hoare", + "hoer", + "homo", + "hore", + "horniest", + "horny", + "hotsex", + "jack-off", + "jackoff", + "jap", + "jerk-off", + "jism", + "jiz", + "jizm", + "jizz", + "kawk", + "knob", + "knobead", + "knobed", + "knobend", + "knobhead", + "knobjocky", + "knobjokey", + "kock", + "kondum", + "kondums", + "kum", + "kummer", + "kumming", + "kums", + "kunilingus", + "l3i\+ch", + "l3itch", + "labia", + "lmfao", + "lust", + "lusting", + "m0f0", + "m0fo", + "m45terbate", + "ma5terb8", + "ma5terbate", + "masochist", + "master-bate", + "masterb8", + "masterbat", + "masterbat3", + "masterbate", + "masterbation", + "masterbations", + "masturbate", + "merde", + "mo-fo", + "mof0", + "mofo", + "mothafuck", + "mothafucka", + "mothafuckas", + "mothafuckaz", + "mothafucked", + "mothafucker", + "mothafuckers", + "mothafuckin", + "mothafucking", + "mothafuckings", + "mothafucks", + "motherfuck", + "motherfucked", + "motherfucker", + "motherfuckers", + "motherfuckin", + "motherfucking", + "motherfuckings", + "motherfuckka", + "motherfucks", + "muff", + "mutha", + "muthafecker", + "muthafuckker", + "muther", + "mutherfucker", + "n1gga", + "n1gger", + "nazi", + "nigg3r", + "nigg4h", + "nigga", + "niggah", + "niggas", + "niggaz", + "nigger", + "niggers", + "nique", + "niquer", + "nob", + "nobhead", + "nobjocky", + "nobjokey", + "numbnuts", + "nutsack", + "orgasim", + "orgasims", + "orgasm", + "orgasms", + "p0rn", + "pauvre con", + "pawn", + "pecker", + "penis", + "penisfucker", + "phonesex", + "phuck", + "phuk", + "phuked", + "phuking", + "phukked", + "phukking", + "phuks", + "phuq", + "pigfucker", + "pimpis", + "piss", + "pissed", + "pisser", + "pissers", + "pisses", + "pissflaps", + "pissin", + "pissing", + "pissoff", + "poop", + "porn", + "porno", + "pornography", + "pornos", + "prick", + "pron", + "pube", + "pusse", + "pussi", + "pussies", + "pussy", + "pute", + "putain", + "rectum", + "retard", + "rimjaw", + "rimming", + "s.o.b.", + "sadist", + "salaud", + "salop", + "salope", + "saloperie", + "schlong", + "screwing", + "scroat", + "scrote", + "scrotum", + "semen", + "sex", + "sh!\+", + "sh!t", + "sh1t", + "shag", + "shagger", + "shaggin", + "shagging", + "shemale", + "shi\+", + "shit", + "shitdick", + "shite", + "shited", + "shitey", + "shitfuck", + "shitfull", + "shithead", + "shiting", + "shits", + "shitted", + "shitter", + "shitting", + "shitty", + "skank", + "slut", + "sluts", + "smegma", + "smut", + "snatch", + "son-of-a-bitch", + "spac", + "spunk", + "s_h_i_t", + "t1tt1e5", + "t1tties", + "teets", + "teez", + "testical", + "testicle", + "tit", + "titfuck", + "tits", + "titt", + "tittie5", + "tittiefucker", + "titties", + "tittyfuck", + "tittywank", + "titwank", + "tosser", + "trou du cul", + "turd", + "tw4t", + "twat", + "twathead", + "twatty", + "twunt", + "twunter", + "v14gra", + "v1gra", + "vagina", + "viagra", + "vulva", + "w00se", + "wang", + "wank", + "wanker", + "wanky", + "whoar", + "whore", + "willies", + "willy", + "xrated", + "xxx" +] diff --git a/toontown/toonbase/TTLocalizerEnglishProperty.py b/toontown/toonbase/TTLocalizerEnglishProperty.py new file mode 100755 index 00000000..c41398de --- /dev/null +++ b/toontown/toonbase/TTLocalizerEnglishProperty.py @@ -0,0 +1,330 @@ +PBPTonscreenText = 0.2 +RPdirectFrame = (1.75, 1, 0.75) +RPtrackLabels = 0.05 +RPmeritBarLabels = 0.165 +RPskipScale = 0.2 +RPskipPos = (0, -.28) +RPmeritLabelPosX = 0.55 +RPmeritBarsPosX = 0.825 +BBbattleInputTimeout = 20.0 +FCPtextFrame = 0.08 +DHQInamePath = 0.9 +DHQInamePathPos = (-4, 0, 0) +DHQIscorePathPos = (-4.6, 0, 0) +DHQItrophyStarPos = (-6.6, 0, 0.3) +EexitButton = 0.8 +CCIPexitButton = 0.06 +CIPnameLabel = 1 +CIPwordwrapOffset = 0 +CIPtypeLabel = 0.075 +CIPbuyButton = (0.06, 0.05) +CSgiftTogglePos = (0.855, -0.13) +CSgiftToggle = 0.08 +CSbackCatalogButton = 0.065 +NametagReverse = False +TTCISCspeedChat = 0.055 +TTCISCtopLevelOverlap = 0.0 +TCMnormalButton = 0.06 +TCMscButtonPos = (0.204, 0, -0.072) +TCMscButton = 0.06 +TCMwhisperFrame = 0.06 +TCMwhisperButton = 0.05 +TCMwhisperScButton = 0.05 +TCMdirectButtonTextPos = (0.0, 0.0, -0.28) +LCHQLfdText = 0.1 +SCHQLfdTypeText = 0.075 +SCHQLdgText = 0.1 +DMEEsignText = 2 +BCHQLsignText = 1.12 +DGGGquitButton = 0.045 +DGGGhowToButton = 0.045 +DGGGscoreLabel = 0.075 +FPvaluePos = (0, 0, -0.35) +FPvalue = 0.05 +FPinfo = 0.055 +FSGUIdirectFrame = 0.06 +FSGUIcancelButton = 0.06 +FSGUIokButton = 0.06 +GTenterPage2Wordwrap = 13.5 +GTenterPage4Wordwrap = 16.5 +BCGjpText = 0.04 +BCGjpTextWordwrap = 10.5 +BCGnextGame = 1.71 +FSGUIokButton = 0.06 +FSGUIcancelButton = 0.06 +FPnewEntry = 0.08 +FPnewRecord = 0.08 +GPgenus = 0.045 +FLPnewFriend = 0.045 +FLPtruefriends = 0.045 +FLPtruefriendsPos = (0.152, 0.0, 0.14) +FLPtitle = 0.04 +FIbStop = 0.05 +FIdirectFrame = 0.06 +FIbCancelPos = (0.0, 0.0, -0.1) +FIbStopTextPos = (0.075, -0.015) +FIbStopPos = (-0.2, 0.0, 0.05) +FIbYesPos = (-0.15, 0.0, -0.1) +FIdirectFrameWordwrap = 14 +FIdirectFramePos = (0, 0.2) +DGHaimInstructions = 0.1 +DGHteeInstructions = 0.1 +GSBexitCourseBPos = (0.15, -.01) +GSBtitleLabel = 0.07 +EHpopupInfo = 0.08 +HtitleText = 0.16 +ACplayThisToon = 0.12 +ACmakeAToon = 0.12 +ACdeleteWithPasswordFrame = 0.06 +ACstatusText = 1.0 +ACtitle = 0.15 +ACquitButton = 0.1 +AClanguageButton = 0.075 +ACquitButtonPos = (0, -0.035) +MASPscoreText = 0.1 +MASPnameText = 0.055 +MRPgameTitleText = 0.11 +MRgameTitleTextPos = (-0.046, 0.2, 0.092) +MRPplayButton = 0.055 +MRPinstructionsText = 0.07 +MRPinstructionsTextWordwrap = 26.5 +MRPinstructionsTextPos = (-0.115, 0.05, 0) +CRPgameTitleText = 0.088 +CRPgameTitleTextPos = (-0.046, 0.2, 0.13) +MPMpowerText = 0.07 +MPMtooSlow = 0.07 +MPMtooFast = 0.07 +MPMgaugeA = 0.35 +MPMgaugeTargetTop = 0.35 +MPMgaugeTargetBot = 0.35 +PstatusLabel = 0.08 +PBstatusLabel = 0.08 +CStoonFrame = 0.0575 +NSmaxNameWidth = 8.0 +NSdirectScrolleList = 0.1 +NSmakeLabel = 0.1 +NSmakeCheckBox = 0.8 +NSnameEntry = 0.08 +NStypeANameButton = 0.06 +NStypeANameButtonPos = (0, -0.02) +NSnameResult = (0.09, 0.084, 0.084) +NSnameLabel = 0.1 +NStypeNotification = 0.08 +NScolorPrecede = True +MATenterGenderShop = 0.18 +MATenterBodyShop = 0.18 +MATenterColorShop = 0.18 +MATenterClothesShop = 0.16 +MATenterNameShop = 0.15 +MATclothesGUIshirt_scale = 0.06 +MATclothesGUIshirt_posL = 0.01 +MATclothesGUIshirt_posR = -0.014 +MATguiCancelButton = 0.08 +MATguiNextButton = 0.08 +SBshuffleBtn = 0.08 +IVwhenTextLabel = 0.06 +IVactivityTextLabel = 0.06 +PPelementDescription = 0.06 +PPelementTitleLabel = 0.07 +PPelementBuyButton = 0.055 +PPtitleScale = 0.1 +PPpbulicDescriptionLabel = 0.065 +PPprivateDescriptionLabel = 0.065 +PPpublicButton = 0.05 +PPprivateButton = 0.05 +PPcostLabel = 0.065 +PPpartyGroundsLabel = 1.0 +PPinstructionLabel = 0.07 +PPelementPriceNode = 0.065 +DPtimerTextLabel = 1.1 +DPtimerMinute = 1.1 +DPtimerColon = 1.1 +DPtimerSecond = 1.1 +DPtimerMinutePos = (-1.2, 0.0, 0.0) +DPtimerColonPos = (0.0, 0.0, 0.0) +DPtimerSecondPos = (1.2, 0.0, 0.0) +PPGpartyStartButton = 0.065 +PPGinstructionsLabel = 0.065 +PPGtoonsLabel = 0.06 +PPGactivitiesLabel = 0.06 +PPGminLeftLabel = 0.06 +JGcurrentlyPlayingLabel = 0.07 +JGsongNameLabel = 0.13 +JGaddSongButton = 0.1 +JGnumItemsVisible = 9 +JGlistItem = 1.0 +PAPfeedButton = 0.5 +PAPcallButton = 0.5 +PAPownerButton = 0.35 +PAPscratchButton = 0.5 +PAPstateLabel = 0.4 +PAPstateLabelPos = (0.7, 0, 3.5) +PAPstateLabelWordwrap = 7.5 +PDPtrickText = 0.17 +PDPlaff = 0.17 +PDPlaffPos = (0.0, -0.05) +PGUItextScale = 1 +PGUIchooserTitle = 0.1 +PGUIwordwrap = 14 +PGUIdescLabel = 0.9 +PGUIreturnConfirm = 0.07 +PGUIokButton = 0.6 +PGUIsubmitButton = 0.8 +PGUIokButtonPos = (-0.21, 1.05) +PGUIcancelButtonPos = (-3.3, 2.95) +PGUIcharLength = 1 +PTtitle = 0.13 +PTenterPage1Pos = (0.15, 0.13) +PTenterPage2Pos = (-0.27, 0.16) +PTenterPage3Pos = (0.15, 0.13) +QPauxText = 0.04 +QPtextScale = 0.045 +QPtextWordwrap = 15.6 +QPinfoZ = -0.0625 +DLBbuildTitleRow = 0.4 +DRenterWaiting = 0.2 +DRrollScale = 0.5 +KSGtextSizeBig = 0.088 +KSGtextSizeSmall = 0.055 +KSGaccDescriptionWordwrap = 11 +REPlargeLabel = 0.08 +REPsmallLabel = 0.04 +REPtextPosX = -0.6 +RGUIphotoFinish = 0.25 +RGUIplaceLabelNumPos = (0.15, 0, 0.05) +RGUIplaceLabelStrPos = (0.31, 0.0, 0.22) +DRAIwaitingForJoin = 90 +PimgLabel = 1.0 +GZSZLsignText = 1.5 +EPtitleLabel = 0.12 +EPhostTab = 0.07 +EPinvitedTab = 0.07 +EPcalendarTab = 0.07 +EPhostingCancelButton = 0.04 +EPhostingDateLabel = 0.05 +EPpartyGoButton = 0.045 +EPpublicPrivateLabel = 0.05 +EPpublicButton = 0.5 +EPprivateButton = 0.5 +EPinvitePartyGoButton = 0.045 +EPdecorationItemLabel = 0.055 +EPactivityItemLabel = 0.055 +EPcreateListAndLabel = 0.055 +FPtankTab = 0.07 +FPcollectionTab = 0.07 +FPtrophyTab = 0.07 +DSDintroText = 0.06 +DSDintroTextWordwrap = 25 +DSDwindowedButtonPos = (0.0961, 0, -0.221) +DSDfullscreenButtonPos = (0.097, 0, -0.311) +DSDcancel = 0.06 +DSDcancelPos = (0, -0.02) +DPtab = 0.1 +DPdeptLabel = 0.17 +DPcogName = 0.093 +TPstartFrame = 0.12 +TPendFrame = 0.12 +SBpageTab = 0.75 +OPoptionsTab = 0.07 +OPextraOptionsTab = 0.075 +OPCodesInstructionPanelTextPos = (0, -0.01) +OPCodesInstructionPanelTextWordWrap = 6 +OPCodesResultPanelTextPos = (0, 0.35) +OPCodesResultPanelTextScale = 0.06 +OPCodesResultPanelTextWordWrap = 9 +OPCodesInputTextScale = 0.8 +OPCodesSubmitTextScale = 0.07 +OPCodesSubmitTextPos = (0, -0.02) +MPsafeZoneButton = 0.055 +MPgoHomeButton = 0.055 +MPhoodLabel = 0.06 +MPhoodLabelWordwrap = 14 +KPkartTab = 0.07 +KPdeleteButton = 0.06 +KProtateButton = 0.035 +GPbasketTab = 0.07 +GPcollectionTab = 0.07 +GPtrophyTab = 0.07 +GPspecialsTab = 0.07 +GPrecordsTab = 0.07 +GPrecordsTabPos = (0.92, 0, 0.1) +GPtrophyTab = 0.07 +GPtrophyTabTextPos = (0.03, 0.0, 0.0) +GPtrophyTabPos = (0.92, 0, -0.3) +APBdialog = 0.06 +APBdirectLabelPosY = 0 +TAPwhisperButton = 0.06 +TAPtruefriendsButton = 0.045 +TAPgroupFrame = 0.05 +TAPgroupButton = 0.055 +TADPbCancel = 0.05 +TADPbCancelPos = (-0.865, 0.0, -0.78) +TADPtrackLabel = 0.066 +TADtrackLabelPosZ = 0.08 +GPdestFrame = 0.05 +GPdestScrollList = 0.05 +GPgoButton = 0.06 +INtrackNameLabels = 0.05 +INclickToAttack = 1.0 +INpassButton = 0.05 +INrunButton = 0.05 +INdetailNameLabel = 1.0 +INfireButton = 0.05 +NPCFimgLabel = 1.0 +PIPtrueFriendsButton = 0.045 +PIPwisperButton = 0.06 +PIPdetailButton = 0.05 +TLStip = 0.18 +TPdialogWordwrap = 22 +TPdialog = 0.05 +TPpanel = 0.08 +TPpanelPos = (0.0, -0.7) +TPbrowserPosZ = -0.45 +TPbuttonTextList = 0.05 +TPhaveFun = 0.1 +TPjoinUs = 0.1 +TBSOSPSPenter = 0.1 +TexitButton = 0.8 +FSenterSecretTextPos = (0, 0, -0.25) +FSgotSecretPos = (0, 0, 0.47) +FSgetSecretButton = 0.06 +FSnextText = 1.0 +FSgetSecret = (1.55, 1, 1) +FSok1 = (1.55, 1, 1) +FSok2 = (0.6, 1, 1) +FScancel = (0.6, 1, 1) +#Some languages need to change the word order +SellbotFactoryPosPart1 = (0, -0.25) +SellbotFactoryScalePart1 = 0.075 +SellbotFactoryPosPart2 = (0, -0.34) +SellbotFactoryScalePart2 = 0.12 +BattleHoverCog = 0 +BattleHoverGag = 1 +BattleHoverSos = 2 + +BattleHoverAttributes = { + BattleHoverCog: { + 'geom_scale': (0.4, 0, 0.18), + 'text_pos': (0, 0.29), + 'geom_color': (0.5, 0.5, 0.5, 1), + 'pos': (0.6, 0, 0.05), + 'text_fg': (1, 1, 1, 1), + 'suit': True + }, + BattleHoverGag: { + 'geom_scale': (0.5, 0, 0.2), + 'text_pos': (0, 0.0125), + 'geom_color': (0.6, 1.0, 0.4, 1), + 'pos': (0.4, 0, 0), + 'text_fg': (0, 0, 0, 1), + 'suit': False + }, + BattleHoverSos: { + 'geom_scale': (0.5, 0, 0.3), + 'text_pos': (0, 0.08), + 'geom_color': (0.6, 1.0, 0.4, 1), + 'pos': (0.4, 0, 0.1), + 'text_fg': (0, 0, 0, 1), + 'suit': False + } +} \ No newline at end of file diff --git a/toontown/toonbase/ToonBase.py b/toontown/toonbase/ToonBase.py new file mode 100755 index 00000000..52f7a1bb --- /dev/null +++ b/toontown/toonbase/ToonBase.py @@ -0,0 +1,446 @@ +import atexit +from direct.directnotify import DirectNotifyGlobal +from direct.filter.CommonFilters import CommonFilters +from direct.gui import DirectGuiGlobals +from direct.gui.DirectGui import * +from direct.showbase.PythonUtil import * +from direct.showbase.Transitions import Transitions +from direct.task import * +import math +import os +from panda3d.core import * +import random +import shutil +from sys import platform +import sys +import tempfile +import time + +import ToontownGlobals +import ToontownLoader +from otp.otpbase import OTPBase +from otp.otpbase import OTPGlobals +from otp.nametag.ChatBalloon import ChatBalloon +from otp.nametag import NametagGlobals +from otp.margins.MarginManager import MarginManager +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownBattleGlobals +from toontown.toontowngui import TTDialog + +tempdir = tempfile.mkdtemp() +vfs = VirtualFileSystem.getGlobalPtr() +searchPath = DSearchPath() +if __debug__: + searchPath.appendDirectory(Filename('resources/phase_3/etc')) +searchPath.appendDirectory(Filename('/phase_3/etc')) + +for filename in ['toonmono.cur', 'icon.ico']: + p3filename = Filename(filename) + found = vfs.resolveFilename(p3filename, searchPath) + if not found: + continue + with open(os.path.join(tempdir, filename), 'wb') as f: + f.write(vfs.readFile(p3filename, False)) +loadPrcFileData('Window: icon', 'icon-filename %s' % Filename.fromOsSpecific(os.path.join(tempdir, 'icon.ico'))) + +class ToonBase(OTPBase.OTPBase): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonBase') + + def __init__(self): + OTPBase.OTPBase.__init__(self) + self.disableShowbaseMouse() + self.addCullBins() + self.debugRunningMultiplier /= OTPGlobals.ToonSpeedFactor + self.baseXpMultiplier = self.config.GetFloat('base-xp-multiplier', 1.0) + self.toonChatSounds = self.config.GetBool('toon-chat-sounds', 1) + self.placeBeforeObjects = self.config.GetBool('place-before-objects', 1) + self.endlessQuietZone = False + self.wantDynamicShadows = 0 + self.exitErrorCode = 0 + camera.setPosHpr(0, 0, 0, 0, 0, 0) + self.camLens.setMinFov(settings['fov']/(4./3.)) + self.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar) + self.musicManager.setVolume(0.65) + self.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + tpm = TextPropertiesManager.getGlobalPtr() + candidateActive = TextProperties() + candidateActive.setTextColor(0, 0, 1, 1) + tpm.setProperties('candidate_active', candidateActive) + candidateInactive = TextProperties() + candidateInactive.setTextColor(0.3, 0.3, 0.7, 1) + tpm.setProperties('candidate_inactive', candidateInactive) + self.transitions.IrisModelName = 'phase_3/models/misc/iris' + self.transitions.FadeModelName = 'phase_3/models/misc/fade' + self.exitFunc = self.userExit + globalClock.setMaxDt(0.2) + if self.config.GetBool('want-particles', 1) == 1: + self.notify.debug('Enabling particles') + self.enableParticles() + + self.accept(ToontownGlobals.ScreenshotHotkey, self.takeScreenShot) + + # OS X Specific Actions + if platform == "darwin": + self.acceptOnce(ToontownGlobals.QuitGameHotKeyOSX, self.exitOSX) + self.accept(ToontownGlobals.QuitGameHotKeyRepeatOSX, self.exitOSX) + self.acceptOnce(ToontownGlobals.HideGameHotKeyOSX, self.hideGame) + self.accept(ToontownGlobals.HideGameHotKeyRepeatOSX, self.hideGame) + self.acceptOnce(ToontownGlobals.MinimizeGameHotKeyOSX, self.minimizeGame) + self.accept(ToontownGlobals.MinimizeGameHotKeyRepeatOSX, self.minimizeGame) + + self.accept('f3', self.toggleGui) + self.accept('f4', self.oobe) + self.accept('panda3d-render-error', self.panda3dRenderError) + oldLoader = self.loader + self.loader = ToontownLoader.ToontownLoader(self) + __builtins__['loader'] = self.loader + oldLoader.destroy() + self.accept('PandaPaused', self.disableAllAudio) + self.accept('PandaRestarted', self.enableAllAudio) + self.wantPets = self.config.GetBool('want-pets', 1) + self.wantBingo = self.config.GetBool('want-fish-bingo', 1) + self.wantKarts = self.config.GetBool('want-karts', 1) + self.inactivityTimeout = self.config.GetFloat('inactivity-timeout', ToontownGlobals.KeyboardTimeout) + if self.inactivityTimeout: + self.notify.debug('Enabling Panda timeout: %s' % self.inactivityTimeout) + self.mouseWatcherNode.setInactivityTimeout(self.inactivityTimeout) + self.mouseWatcherNode.setEnterPattern('mouse-enter-%r') + self.mouseWatcherNode.setLeavePattern('mouse-leave-%r') + self.mouseWatcherNode.setButtonDownPattern('button-down-%r') + self.mouseWatcherNode.setButtonUpPattern('button-up-%r') + self.randomMinigameAbort = self.config.GetBool('random-minigame-abort', 0) + self.randomMinigameDisconnect = self.config.GetBool('random-minigame-disconnect', 0) + self.randomMinigameNetworkPlugPull = self.config.GetBool('random-minigame-netplugpull', 0) + self.autoPlayAgain = self.config.GetBool('auto-play-again', 0) + self.skipMinigameReward = self.config.GetBool('skip-minigame-reward', 0) + self.wantMinigameDifficulty = self.config.GetBool('want-minigame-difficulty', 0) + self.minigameDifficulty = self.config.GetFloat('minigame-difficulty', -1.0) + if self.minigameDifficulty == -1.0: + del self.minigameDifficulty + self.minigameSafezoneId = self.config.GetInt('minigame-safezone-id', -1) + if self.minigameSafezoneId == -1: + del self.minigameSafezoneId + cogdoGameSafezoneId = self.config.GetInt('cogdo-game-safezone-id', -1) + cogdoGameDifficulty = self.config.GetFloat('cogdo-game-difficulty', -1) + if cogdoGameDifficulty != -1: + self.cogdoGameDifficulty = cogdoGameDifficulty + if cogdoGameSafezoneId != -1: + self.cogdoGameSafezoneId = cogdoGameSafezoneId + ToontownBattleGlobals.SkipMovie = self.config.GetBool('skip-battle-movies', 0) + self.housingEnabled = self.config.GetBool('want-housing', 1) + self.cannonsEnabled = self.config.GetBool('estate-cannons', 0) + self.fireworksEnabled = self.config.GetBool('estate-fireworks', 0) + self.dayNightEnabled = self.config.GetBool('estate-day-night', 0) + self.cloudPlatformsEnabled = self.config.GetBool('estate-clouds', 0) + self.greySpacing = self.config.GetBool('allow-greyspacing', 0) + self.slowQuietZone = self.config.GetBool('slow-quiet-zone', 0) + self.slowQuietZoneDelay = self.config.GetFloat('slow-quiet-zone-delay', 5) + self.killInterestResponse = self.config.GetBool('kill-interest-response', 0) + tpMgr = TextPropertiesManager.getGlobalPtr() + WLDisplay = TextProperties() + WLDisplay.setSlant(0.3) + tpMgr.setProperties('WLDisplay', WLDisplay) + WLRed = tpMgr.getProperties('red') + WLRed.setTextColor(1.0, 0.0, 0.0, 1) + tpMgr.setProperties('WLRed', WLRed) + del tpMgr + self.lastScreenShotTime = globalClock.getRealTime() + self.accept('InputState-forward', self.__walking) + self.canScreenShot = 1 + self.glitchCount = 0 + self.walking = 0 + self.oldX = max(1, base.win.getXSize()) + self.oldY = max(1, base.win.getYSize()) + self.aspectRatio = float(self.oldX) / self.oldY + self.localAvatarStyle = None + + self.filters = CommonFilters(self.win, self.cam) + self.wantCogInterface = settings.get('cogInterface', True) + + def openMainWindow(self, *args, **kw): + result = OTPBase.OTPBase.openMainWindow(self, *args, **kw) + self.setCursorAndIcon() + return result + + def setCursorAndIcon(self): + atexit.register(shutil.rmtree, tempdir) + + wp = WindowProperties() + wp.setCursorFilename(Filename.fromOsSpecific(os.path.join(tempdir, 'toonmono.cur'))) + wp.setIconFilename(Filename.fromOsSpecific(os.path.join(tempdir, 'icon.ico'))) + self.win.requestProperties(wp) + + def addCullBins(self): + cbm = CullBinManager.getGlobalPtr() + cbm.addBin('ground', CullBinManager.BTUnsorted, 18) + cbm.addBin('shadow', CullBinManager.BTBackToFront, 19) + cbm.addBin('gui-popup', CullBinManager.BTUnsorted, 60) + + def disableShowbaseMouse(self): + self.useDrive() + self.disableMouse() + if self.mouseInterface: self.mouseInterface.detachNode() + if self.mouse2cam: self.mouse2cam.detachNode() + + def __walking(self, pressed): + self.walking = pressed + + def toggleGui(self): + if aspect2d.isHidden(): + aspect2d.show() + else: + aspect2d.hide() + + def takeScreenShot(self): + if not os.path.exists(TTLocalizer.ScreenshotPath): + os.mkdir(TTLocalizer.ScreenshotPath) + self.notify.info('Made new directory to save screenshots.') + + namePrefix = TTLocalizer.ScreenshotPath + launcher.logPrefix + 'screenshot' + timedif = globalClock.getRealTime() - self.lastScreenShotTime + if self.glitchCount > 10 and self.walking: + return + if timedif < 1.0 and self.walking: + self.glitchCount += 1 + return + if not hasattr(self, 'localAvatar'): + self.screenshot(namePrefix=namePrefix) + self.lastScreenShotTime = globalClock.getRealTime() + return + coordOnScreen = self.config.GetBool('screenshot-coords', 0) + self.localAvatar.stopThisFrame = 1 + ctext = self.localAvatar.getAvPosStr() + self.screenshotStr = '' + messenger.send('takingScreenshot') + if coordOnScreen: + coordTextLabel = DirectLabel(pos=(-0.81, 0.001, -0.87), text=ctext, text_scale=0.05, text_fg=VBase4(1.0, 1.0, 1.0, 1.0), text_bg=(0, 0, 0, 0), text_shadow=(0, 0, 0, 1), relief=None) + coordTextLabel.setBin('gui-popup', 0) + strTextLabel = None + if len(self.screenshotStr): + strTextLabel = DirectLabel(pos=(0.0, 0.001, 0.9), text=self.screenshotStr, text_scale=0.05, text_fg=VBase4(1.0, 1.0, 1.0, 1.0), text_bg=(0, 0, 0, 0), text_shadow=(0, 0, 0, 1), relief=None) + strTextLabel.setBin('gui-popup', 0) + self.graphicsEngine.renderFrame() + self.screenshot(namePrefix=namePrefix, imageComment=ctext + ' ' + self.screenshotStr) + self.lastScreenShotTime = globalClock.getRealTime() + self.snapshotSfx = base.loadSfx('phase_4/audio/sfx/Photo_shutter.ogg') + base.playSfx(self.snapshotSfx) + if coordOnScreen: + if strTextLabel is not None: + strTextLabel.destroy() + coordTextLabel.destroy() + return + + def addScreenshotString(self, str): + if len(self.screenshotStr): + self.screenshotStr += '\n' + self.screenshotStr += str + + def initNametagGlobals(self): + arrow = loader.loadModel('phase_3/models/props/arrow') + card = loader.loadModel('phase_3/models/props/panel') + speech3d = ChatBalloon(loader.loadModel('phase_3/models/props/chatbox')) + thought3d = ChatBalloon(loader.loadModel('phase_3/models/props/chatbox_thought_cutout')) + speech2d = ChatBalloon(loader.loadModel('phase_3/models/props/chatbox_noarrow')) + chatButtonGui = loader.loadModel('phase_3/models/gui/chat_button_gui') + NametagGlobals.setCamera(self.cam) + NametagGlobals.setArrowModel(arrow) + NametagGlobals.setNametagCard(card, VBase4(-0.5, 0.5, -0.5, 0.5)) + if self.mouseWatcherNode: + NametagGlobals.setMouseWatcher(self.mouseWatcherNode) + NametagGlobals.setSpeechBalloon3d(speech3d) + NametagGlobals.setThoughtBalloon3d(thought3d) + NametagGlobals.setSpeechBalloon2d(speech2d) + NametagGlobals.setThoughtBalloon2d(thought3d) + NametagGlobals.setPageButton(PGButton.SReady, chatButtonGui.find('**/Horiz_Arrow_UP')) + NametagGlobals.setPageButton(PGButton.SDepressed, chatButtonGui.find('**/Horiz_Arrow_DN')) + NametagGlobals.setPageButton(PGButton.SRollover, chatButtonGui.find('**/Horiz_Arrow_Rllvr')) + NametagGlobals.setQuitButton(PGButton.SReady, chatButtonGui.find('**/CloseBtn_UP')) + NametagGlobals.setQuitButton(PGButton.SDepressed, chatButtonGui.find('**/CloseBtn_DN')) + NametagGlobals.setQuitButton(PGButton.SRollover, chatButtonGui.find('**/CloseBtn_Rllvr')) + rolloverSound = DirectGuiGlobals.getDefaultRolloverSound() + if rolloverSound: + NametagGlobals.setRolloverSound(rolloverSound) + clickSound = DirectGuiGlobals.getDefaultClickSound() + if clickSound: + NametagGlobals.setClickSound(clickSound) + NametagGlobals.setToon(self.cam) + + self.marginManager = MarginManager() + self.margins = self.aspect2d.attachNewNode(self.marginManager, DirectGuiGlobals.MIDGROUND_SORT_INDEX + 1) + mm = self.marginManager + + # TODO: Dynamicaly add more and reposition cells + padding = 0.0225 + + # Order: Top to bottom + self.leftCells = [ + mm.addGridCell(0.2 + padding, -0.45, base.a2dTopLeft), # Above boarding groups + mm.addGridCell(0.2 + padding, -0.9, base.a2dTopLeft), # 1 + mm.addGridCell(0.2 + padding, -1.35, base.a2dTopLeft) # Below Boarding Groups + ] + + # Order: Left to right + self.bottomCells = [ + mm.addGridCell(-0.87, 0.2 + padding, base.a2dBottomCenter), # To the right of the laff meter + mm.addGridCell(-0.43, 0.2 + padding, base.a2dBottomCenter), # 1 + mm.addGridCell(0.01, 0.2 + padding, base.a2dBottomCenter), # 2 + mm.addGridCell(0.45, 0.2 + padding, base.a2dBottomCenter), # 3 + mm.addGridCell(0.89, 0.2 + padding, base.a2dBottomCenter) # To the left of the shtiker book + ] + + # Order: Bottom to top + self.rightCells = [ + mm.addGridCell(-0.2 - padding, -1.35, base.a2dTopRight), # Above the street map + mm.addGridCell(-0.2 - padding, -0.9, base.a2dTopRight), # Below the friends list + mm.addGridCell(-0.2 - padding, -0.45, base.a2dTopRight) # Behind the friends list + ] + + def hideFriendMargins(self): + middleCell = self.rightCells[1] + topCell = self.rightCells[2] + + self.setCellsAvailable([middleCell, topCell], False) + + def showFriendMargins(self): + middleCell = self.rightCells[1] + topCell = self.rightCells[2] + + self.setCellsAvailable([middleCell, topCell], True) + + def setCellsAvailable(self, cell_list, available): + for cell in cell_list: + self.marginManager.setCellAvailable(cell, available) + + def startShow(self, cr): + self.cr = cr + base.graphicsEngine.renderFrame() + # Get the base port. + serverPort = base.config.GetInt('server-port', 7199) + + # Get the number of client-agents. + clientagents = base.config.GetInt('client-agents', 1) - 1 + + # Get a new port. + serverPort += (random.randint(0, clientagents) * 100) + + serverList = [] + for name in launcher.getGameServer().split(';'): + url = URLSpec(name, 1) + if base.config.GetBool('server-force-ssl', False): + url.setScheme('s') + if not url.hasPort(): + url.setPort(serverPort) + serverList.append(url) + + if len(serverList) == 1: + failover = base.config.GetString('server-failover', '') + serverURL = serverList[0] + for arg in failover.split(): + try: + port = int(arg) + url = URLSpec(serverURL) + url.setPort(port) + except: + url = URLSpec(arg, 1) + + if url != serverURL: + serverList.append(url) + + cr.loginFSM.request('connect', [serverList]) + + # Start detecting speed hacks: + self.lastSpeedHackCheck = time.time() + self.lastTrueClockTime = TrueClock.getGlobalPtr().getLongTime() + taskMgr.add(self.__speedHackCheckTick, 'speedHackCheck-tick') + + def __speedHackCheckTick(self, task): + elapsed = time.time() - self.lastSpeedHackCheck + tcElapsed = TrueClock.getGlobalPtr().getLongTime() - self.lastTrueClockTime + + if tcElapsed > (elapsed + 0.05): + # The TrueClock is running faster than it should. This means the + # player may have sped up the process. Disconnect them: + self.cr.stopReaderPollTask() + self.cr.lostConnection() + return task.done + + self.lastSpeedHackCheck = time.time() + self.lastTrueClockTime = TrueClock.getGlobalPtr().getLongTime() + + return task.cont + + def removeGlitchMessage(self): + self.ignore('InputState-forward') + + def exitShow(self, errorCode = None): + self.notify.info('Exiting Toontown: errorCode = %s' % errorCode) + sys.exit() + + def setExitErrorCode(self, code): + self.exitErrorCode = code + + def getExitErrorCode(self): + return self.exitErrorCode + + def userExit(self): + try: + self.localAvatar.d_setAnimState('TeleportOut', 1) + except: + pass + + if self.cr.timeManager: + self.cr.timeManager.setDisconnectReason(ToontownGlobals.DisconnectCloseWindow) + base.cr._userLoggingOut = False + try: + localAvatar + except: + pass + else: + messenger.send('clientLogout') + self.cr.dumpAllSubShardObjects() + + self.cr.loginFSM.request('shutdown') + self.notify.warning('Could not request shutdown; exiting anyway.') + self.ignore(ToontownGlobals.QuitGameHotKeyOSX) + self.ignore(ToontownGlobals.QuitGameHotKeyRepeatOSX) + self.ignore(ToontownGlobals.HideGameHotKeyOSX) + self.ignore(ToontownGlobals.HideGameHotKeyRepeatOSX) + self.ignore(ToontownGlobals.MinimizeGameHotKeyOSX) + self.ignore(ToontownGlobals.MinimizeGameHotKeyRepeatOSX) + self.exitShow() + + def panda3dRenderError(self): + if self.cr.timeManager: + self.cr.timeManager.setDisconnectReason(ToontownGlobals.DisconnectGraphicsError) + self.cr.sendDisconnect() + sys.exit() + + def playMusic(self, music, looping = 0, interrupt = 1, volume = None, time = 0.0): + OTPBase.OTPBase.playMusic(self, music, looping, interrupt, volume, time) + + # OS X Specific Actions + def exitOSX(self): + self.confirm = TTDialog.TTGlobalDialog(doneEvent='confirmDone', message=TTLocalizer.OptionsPageExitConfirm, style=TTDialog.TwoChoice) + self.confirm.show() + self.accept('confirmDone', self.handleConfirm) + + def handleConfirm(self): + status = self.confirm.doneStatus + self.ignore('confirmDone') + self.confirm.cleanup() + del self.confirm + if status == 'ok': + self.userExit() + + def hideGame(self): + # Hacky, I know, but it works + hideCommand = """osascript -e 'tell application "System Events" + set frontProcess to first process whose frontmost is true + set visible of frontProcess to false + end tell'""" + os.system(hideCommand) + + def minimizeGame(self): + wp = WindowProperties() + wp.setMinimized(True) + base.win.requestProperties(wp) diff --git a/toontown/toonbase/ToonBaseGlobal.py b/toontown/toonbase/ToonBaseGlobal.py new file mode 100755 index 00000000..f593a0c3 --- /dev/null +++ b/toontown/toonbase/ToonBaseGlobal.py @@ -0,0 +1 @@ +from ToonBase import * diff --git a/toontown/toonbase/ToontownBattleGlobals.py b/toontown/toonbase/ToontownBattleGlobals.py new file mode 100755 index 00000000..1ae91a7d --- /dev/null +++ b/toontown/toonbase/ToontownBattleGlobals.py @@ -0,0 +1,312 @@ +from ToontownGlobals import * +import math +import TTLocalizer + +BattleCamFaceOffFov = 30.0 +BattleCamFaceOffPos = Point3(0, -10, 4) +BattleCamDefaultPos = Point3(0, -8.6, 16.5) +BattleCamDefaultHpr = Vec3(0, -61, 0) +BattleCamDefaultFov = 80.0 +BattleCamMenuFov = 65.0 +BattleCamJoinPos = Point3(0, -12, 13) +BattleCamJoinHpr = Vec3(0, -45, 0) +SkipMovie = 0 +BaseHp = 15 +Tracks = TTLocalizer.BattleGlobalTracks +NPCTracks = TTLocalizer.BattleGlobalNPCTracks +TrackColors = ( + (211 / 255.0, 148 / 255.0, 255 / 255.0), + (249 / 255.0, 255 / 255.0, 93 / 255.0), + (79 / 255.0, 190 / 255.0, 76 / 255.0), + (93 / 255.0, 108 / 255.0, 239 / 255.0), + (255 / 255.0, 145 / 255.0, 66 / 255.0), + (255 / 255.0, 65 / 255.0, 199 / 255.0), + (67 / 255.0, 243 / 255.0, 255 / 255.0) +) +HEAL_TRACK = 0 +TRAP_TRACK = 1 +LURE_TRACK = 2 +SOUND_TRACK = 3 +THROW_TRACK = 4 +SQUIRT_TRACK = 5 +DROP_TRACK = 6 +NPC_RESTOCK_GAGS = 7 +NPC_TOONS_HIT = 8 +NPC_COGS_MISS = 9 +MIN_TRACK_INDEX = 0 +MAX_TRACK_INDEX = 6 +MIN_LEVEL_INDEX = 0 +MAX_LEVEL_INDEX = 6 +LAST_REGULAR_GAG_LEVEL = 5 +UBER_GAG_LEVEL_INDEX = 6 +NUM_GAG_TRACKS = 7 +PropTypeToTrackBonus = {AnimPropTypes.Hydrant: SQUIRT_TRACK, + AnimPropTypes.Mailbox: THROW_TRACK, + AnimPropTypes.Trashcan: HEAL_TRACK} +Levels = [ + [0, 20, 200, 800, 2000, 6000, 10000], + [0, 20, 100, 800, 2000, 6000, 10000], + [0, 20, 100, 800, 2000, 6000, 10000], + [0, 40, 200, 1000, 2500, 7500, 10000], + [0, 10, 50, 400, 2000, 6000, 10000], + [0, 10, 50, 400, 2000, 6000, 10000], + [0, 20, 100, 500, 2000, 6000, 10000] +] +regMaxSkill = 10000 +UberSkill = 500 +MaxSkill = UberSkill + regMaxSkill +ExperienceCap = 300 + + +MaxToonAcc = 95 +StartingLevel = 0 +CarryLimits = ( + (10, 0, 0, 0, 0, 0, 0), + (10, 5, 0, 0, 0, 0, 0), + (15, 10, 5, 0, 0, 0, 0), + (20, 15, 10, 5, 0, 0, 0), + (25, 20, 15, 10, 3, 0, 0), + (30, 25, 20, 15, 7, 3, 0), + (30, 25, 20, 15, 7, 3, 1) +) +MaxProps = ((15, 40), (30, 60), (75, 80)) +DLF_SKELECOG = 1 +DLF_FOREMAN = 2 +DLF_BOSS = 4 +DLF_SUPERVISOR = 8 +DLF_VIRTUAL = 16 +DLF_REVIVES = 32 +pieNames = ['tart', 'fruitpie-slice', 'creampie-slice', 'fruitpie', 'creampie', 'birthday-cake', 'wedding-cake', 'lawbook'] +AvProps = ( + ('feather', 'bullhorn', 'lipstick', 'bamboocane', 'pixiedust', 'baton', 'baton'), + ('banana', 'rake', 'marbles', 'quicksand', 'trapdoor', 'tnt', 'traintrack'), + ('1dollar', 'smmagnet', '5dollar', 'bigmagnet', '10dollar', 'hypnogogs', 'hypnogogs'), + ('bikehorn', 'whistle', 'bugle', 'aoogah', 'elephant', 'foghorn', 'singing'), + ('cupcake', 'fruitpieslice', 'creampieslice', 'fruitpie', 'creampie', 'cake', 'cake'), + ('flower', 'waterglass', 'waterballoon', 'bottle', 'firehose', 'stormcloud', 'stormcloud'), + ('flowerpot', 'sandbag', 'anvil', 'weight', 'safe', 'piano', 'piano') +) +AvPropsNew = ( + ('inventory_feather', 'inventory_megaphone', 'inventory_lipstick', 'inventory_bamboo_cane', 'inventory_pixiedust', 'inventory_juggling_cubes', 'inventory_ladder'), + ('inventory_bannana_peel', 'inventory_rake', 'inventory_marbles', 'inventory_quicksand_icon', 'inventory_trapdoor', 'inventory_tnt', 'inventory_traintracks'), + ('inventory_1dollarbill', 'inventory_small_magnet', 'inventory_5dollarbill', 'inventory_big_magnet', 'inventory_10dollarbill', 'inventory_hypno_goggles', 'inventory_screen'), + ('inventory_bikehorn', 'inventory_whistle', 'inventory_bugle', 'inventory_aoogah', 'inventory_elephant', 'inventory_fog_horn', 'inventory_opera_singer'), + ('inventory_tart', 'inventory_fruit_pie_slice', 'inventory_cream_pie_slice', 'inventory_fruitpie', 'inventory_creampie', 'inventory_cake', 'inventory_wedding'), + ('inventory_squirt_flower', 'inventory_glass_of_water', 'inventory_water_gun', 'inventory_seltzer_bottle', 'inventory_firehose', 'inventory_storm_cloud', 'inventory_geyser'), + ('inventory_flower_pot', 'inventory_sandbag', 'inventory_anvil', 'inventory_weight', 'inventory_safe_box', 'inventory_piano', 'inventory_ship') +) +AvPropStrings = TTLocalizer.BattleGlobalAvPropStrings +AvPropStringsSingular = TTLocalizer.BattleGlobalAvPropStringsSingular +AvPropStringsPlural = TTLocalizer.BattleGlobalAvPropStringsPlural +AvPropAccuracy = ( + (70, 70, 70, 70, 70, 70, 100), + (0, 0, 0, 0, 0, 0, 0), + (50, 50, 60, 60, 70, 70, 90), + (95, 95, 95, 95, 95, 95, 95), + (75, 75, 75, 75, 75, 75, 75), + (95, 95, 95, 95, 95, 95, 95), + (70, 70, 70, 70, 70, 70, 70) +) +AvLureBonusAccuracy = (60, 60, 70, 70, 80, 80, 100) +AvTrackAccStrings = TTLocalizer.BattleGlobalAvTrackAccStrings +AvPropDamage = ( + ( # Toon-up + ((8, 10), (Levels[0][0], Levels[0][1])), + ((15, 18), (Levels[0][1], Levels[0][2])), + ((25, 30), (Levels[0][2], Levels[0][3])), + ((40, 45), (Levels[0][3], Levels[0][4])), + ((60, 70), (Levels[0][4], Levels[0][5])), + ((90, 120), (Levels[0][5], Levels[0][6])), + ((210, 210), (Levels[0][6], MaxSkill)) + ), + ( # Trap + ((10, 12), (Levels[1][0], Levels[1][1])), + ((18, 20), (Levels[1][1], Levels[1][2])), + ((30, 35), (Levels[1][2], Levels[1][3])), + ((45, 50), (Levels[1][3], Levels[1][4])), + ((60, 70), (Levels[1][4], Levels[1][5])), + ((90, 180), (Levels[1][5], Levels[1][6])), + ((195, 195), (Levels[1][6], MaxSkill)) + ), + ( # Lure + ((0, 0), (0, 0)), + ((0, 0), (0, 0)), + ((0, 0), (0, 0)), + ((0, 0), (0, 0)), + ((0, 0), (0, 0)), + ((0, 0), (0, 0)), + ((0, 0), (0, 0)) + ), + ( # Sound + ((3, 4), (Levels[3][0], Levels[3][1])), + ((5, 7), (Levels[3][1], Levels[3][2])), + ((9, 11), (Levels[3][2], Levels[3][3])), + ((14, 16), (Levels[3][3], Levels[3][4])), + ((19, 21), (Levels[3][4], Levels[3][5])), + ((25, 50), (Levels[3][5], Levels[3][6])), + ((90, 90), (Levels[3][6], MaxSkill)) + ), + ( # Throw + ((4, 6), (Levels[4][0], Levels[4][1])), + ((8, 10), (Levels[4][1], Levels[4][2])), + ((14, 17), (Levels[4][2], Levels[4][3])), + ((24, 27), (Levels[4][3], Levels[4][4])), + ((36, 40), (Levels[4][4], Levels[4][5])), + ((48, 100), (Levels[4][5], Levels[4][6])), + ((120, 120), (Levels[4][6], MaxSkill)) + ), + ( # Squirt + ((3, 4), (Levels[5][0], Levels[5][1])), + ((6, 8), (Levels[5][1], Levels[5][2])), + ((10, 12), (Levels[5][2], Levels[5][3])), + ((18, 21), (Levels[5][3], Levels[5][4])), + ((27, 30), (Levels[5][4], Levels[5][5])), + ((36, 80), (Levels[5][5], Levels[5][6])), + ((105, 105), (Levels[5][6], MaxSkill)) + ), + ( # Drop + ((10, 10), (Levels[6][0], Levels[6][1])), + ((18, 18), (Levels[6][1], Levels[6][2])), + ((30, 30), (Levels[6][2], Levels[6][3])), + ((45, 45), (Levels[6][3], Levels[6][4])), + ((60, 60), (Levels[6][4], Levels[6][5])), + ((80, 170), (Levels[6][5], Levels[6][6])), + ((195, 195), (Levels[6][6], MaxSkill)) + ) +) +ATK_SINGLE_TARGET = 0 +ATK_GROUP_TARGET = 1 +AvPropTargetCat = ( + (ATK_SINGLE_TARGET, ATK_GROUP_TARGET, ATK_SINGLE_TARGET, ATK_GROUP_TARGET, ATK_SINGLE_TARGET, ATK_GROUP_TARGET, ATK_GROUP_TARGET), + (ATK_SINGLE_TARGET, ATK_SINGLE_TARGET, ATK_SINGLE_TARGET, ATK_SINGLE_TARGET, ATK_SINGLE_TARGET, ATK_SINGLE_TARGET, ATK_SINGLE_TARGET), + (ATK_GROUP_TARGET, ATK_GROUP_TARGET, ATK_GROUP_TARGET, ATK_GROUP_TARGET, ATK_GROUP_TARGET, ATK_GROUP_TARGET, ATK_GROUP_TARGET), + (ATK_SINGLE_TARGET, ATK_SINGLE_TARGET, ATK_SINGLE_TARGET, ATK_SINGLE_TARGET, ATK_SINGLE_TARGET, ATK_SINGLE_TARGET, ATK_GROUP_TARGET) +) +AvPropTarget = (0, 3, 0, 2, 3, 3, 3) +NumRoundsLured = [2, 2, 3, 3, 4, 4, 15] + +def getAvPropDamage(attackTrack, attackLevel, exp, organicBonus = False, propBonus = False, propAndOrganicBonusStack = False): + if attackTrack == LURE_TRACK: + return NumRoundsLured[attackLevel] + minD = AvPropDamage[attackTrack][attackLevel][0][0] + maxD = AvPropDamage[attackTrack][attackLevel][0][1] + minE = AvPropDamage[attackTrack][attackLevel][1][0] + maxE = AvPropDamage[attackTrack][attackLevel][1][1] + expVal = min(exp, maxE) + expPerHp = float(maxE - minE + 1) / float(maxD - minD + 1) + damage = math.floor((expVal - minE) / expPerHp) + minD + if damage <= 0: + damage = minD + if propAndOrganicBonusStack: + originalDamage = damage + if organicBonus: + damage += getDamageBonus(originalDamage) + if propBonus: + damage += getDamageBonus(originalDamage) + elif organicBonus or propBonus: + damage += getDamageBonus(damage) + return damage + + +def getDamageBonus(normal): + bonus = int(normal * 0.1) + if bonus < 1 and normal > 0: + bonus = 1 + return bonus + + +def isGroup(track, level): + return AvPropTargetCat[AvPropTarget[track]][level] + + +def getCreditMultiplier(floorIndex): + return 1 + floorIndex * 0.5 + + +def getFactoryCreditMultiplier(factoryId): + if factoryId == SellbotMegaCorpInt: + return 6.0 + return 2.0 + + +def getFactoryMeritMultiplier(factoryId): + return 4.0 + + +def getMintCreditMultiplier(mintId): + return {CashbotMintIntA: 2.0, + CashbotMintIntB: 2.5, + CashbotMintIntC: 3.0}.get(mintId, 1.0) + + +def getStageCreditMultiplier(floor): + return getCreditMultiplier(floor) + + +def getCountryClubCreditMultiplier(countryClubId): + return {BossbotCountryClubIntA: 2.0, + BossbotCountryClubIntB: 2.5, + BossbotCountryClubIntC: 3.0}.get(countryClubId, 1.0) + + +def getBossBattleCreditMultiplier(battleNumber): + return 1 + battleNumber + + +def getInvasionMultiplier(): + return 2.0 + + +def getMoreXpHolidayMultiplier(): + return 2.0 + + +def encodeUber(trackList): + bitField = 0 + for trackIndex in xrange(len(trackList)): + if trackList[trackIndex] > 0: + bitField += pow(2, trackIndex) + + return bitField + + +def decodeUber(flagMask): + if flagMask == 0: + return [] + maxPower = 16 + workNumber = flagMask + workPower = maxPower + trackList = [] + while workPower >= 0: + if workNumber >= pow(2, workPower): + workNumber -= pow(2, workPower) + trackList.insert(0, 1) + else: + trackList.insert(0, 0) + workPower -= 1 + + endList = len(trackList) + foundOne = 0 + while not foundOne: + if trackList[endList - 1] == 0: + trackList.pop(endList - 1) + endList -= 1 + else: + foundOne = 1 + + return trackList + + +def getUberFlag(flagMask, index): + decode = decodeUber(flagMask) + if index >= len(decode): + return 0 + else: + return decode[index] + + +def getUberFlagSafe(flagMask, index): + if flagMask == 'unknown' or flagMask < 0: + return -1 + else: + return getUberFlag(flagMask, index) diff --git a/toontown/toonbase/ToontownGlobals.py b/toontown/toonbase/ToontownGlobals.py new file mode 100755 index 00000000..baa76345 --- /dev/null +++ b/toontown/toonbase/ToontownGlobals.py @@ -0,0 +1,1704 @@ +import TTLocalizer +from otp.otpbase.OTPGlobals import * +from direct.showbase.PythonUtil import Enum, invertDict +from pandac.PandaModules import BitMask32, Vec4 +MapHotkey = 'alt' +CogHQCameraFov = 60.0 +BossBattleCameraFov = 72.0 +MakeAToonCameraFov = 48.0 +CogdoFov = 56.9 +VPElevatorFov = 53.0 +CFOElevatorFov = 43.0 +CJElevatorFov = 59.0 +CEOElevatorFov = 59.0 +CBElevatorFov = 42.0 +CeilingBitmask = BitMask32(256) +FloorEventBitmask = BitMask32(16) +PieBitmask = BitMask32(256) +PetBitmask = BitMask32(8) +CatchGameBitmask = BitMask32(16) +CashbotBossObjectBitmask = BitMask32(16) +FurnitureSideBitmask = BitMask32(32) +FurnitureTopBitmask = BitMask32(64) +FurnitureDragBitmask = BitMask32(128) +PetLookatPetBitmask = BitMask32(256) +PetLookatNonPetBitmask = BitMask32(512) +BanquetTableBitmask = BitMask32(1024) +FullPies = 65535 +CogHQCameraFar = 900.0 +CogHQCameraNear = 1.0 +CashbotHQCameraFar = 2000.0 +CashbotHQCameraNear = 1.0 +LawbotHQCameraFar = 3000.0 +LawbotHQCameraNear = 1.0 +BossbotHQCameraFar = 3000.0 +BossbotHQCameraNear = 1.0 +SpeedwayCameraFar = 8000.0 +SpeedwayCameraNear = 1.0 +MaxMailboxContents = 60 +MaxHouseItems = 250 +MaxAccessories = 100 +ExtraDeletedItems = 5 +DeletedItemLifetime = 7 * 24 * 60 +CatalogNumWeeksPerSeries = 13 +CatalogNumWeeks = 78 +PetFloorCollPriority = 5 +PetPanelProximityPriority = 6 +P_TooFast = -28 +P_AlreadyOwnBiggerCloset = -27 +P_ItemAlreadyRented = -26 +P_OnAwardOrderListFull = -25 +P_AwardMailboxFull = -24 +P_ItemInPetTricks = -23 +P_ItemInMyPhrases = -22 +P_ItemOnAwardOrder = -21 +P_ItemInAwardMailbox = -20 +P_ItemAlreadyWorn = -19 +P_ItemInCloset = -18 +P_ItemOnGiftOrder = -17 +P_ItemOnOrder = -16 +P_ItemInMailbox = -15 +P_PartyNotFound = 14 +P_WillNotFit = -13 +P_NotAGift = -12 +P_OnOrderListFull = -11 +P_MailboxFull = -10 +P_NoPurchaseMethod = -9 +P_ReachedPurchaseLimit = -8 +P_NoRoomForItem = -7 +P_NotShopping = -6 +P_NotAtMailbox = -5 +P_NotInCatalog = -4 +P_NotEnoughMoney = -3 +P_InvalidIndex = -2 +P_UserCancelled = -1 +P_ItemAvailable = 1 +P_ItemOnOrder = 2 +P_ItemUnneeded = 3 +GIFT_user = 0 +GIFT_admin = 1 +GIFT_RAT = 2 +GIFT_mobile = 3 +GIFT_cogs = 4 +GIFT_partyrefund = 5 +FM_InvalidItem = -7 +FM_NondeletableItem = -6 +FM_InvalidIndex = -5 +FM_NotOwner = -4 +FM_NotDirector = -3 +FM_RoomFull = -2 +FM_HouseFull = -1 +FM_MovedItem = 1 +FM_SwappedItem = 2 +FM_DeletedItem = 3 +FM_RecoveredItem = 4 +SPDonaldsBoat = 3 +SPMinniesPiano = 4 +CEVirtual = 14 +MaxHpLimit = 137 +MaxCarryLimit = 80 +MaxQuestCarryLimit = 4 +GravityValue = 32.174 +MaxCogSuitLevel = 50 - 1 +CogSuitHPLevels = (15 - 1, + 20 - 1, + 30 - 1, + 40 - 1, + 50 - 1) +setInterfaceFont(TTLocalizer.InterfaceFont) +setSignFont(TTLocalizer.SignFont) +from toontown.toontowngui import TTDialog +setDialogClasses(TTDialog.TTDialog, TTDialog.TTGlobalDialog) +ToonFont = None +BuildingNametagFont = None +MinnieFont = None +SuitFont = None + +def getToonFont(): + global ToonFont + if ToonFont == None: + ToonFont = loader.loadFont(TTLocalizer.ToonFont, lineHeight=1.0) + return ToonFont + + +def getBuildingNametagFont(): + global BuildingNametagFont + if BuildingNametagFont == None: + BuildingNametagFont = loader.loadFont(TTLocalizer.BuildingNametagFont) + return BuildingNametagFont + + +def getMinnieFont(): + global MinnieFont + if MinnieFont == None: + MinnieFont = loader.loadFont(TTLocalizer.MinnieFont) + return MinnieFont + + +def getSuitFont(): + global SuitFont + if SuitFont == None: + SuitFont = loader.loadFont(TTLocalizer.SuitFont, pixelsPerUnit=40, spaceAdvance=0.25, lineHeight=1.0) + return SuitFont + + +DonaldsDock = 1000 +ToontownCentral = 2000 +TheBrrrgh = 3000 +MinniesMelodyland = 4000 +DaisyGardens = 5000 +OutdoorZone = 6000 +FunnyFarm = 7000 +GoofySpeedway = 8000 +DonaldsDreamland = 9000 +BarnacleBoulevard = 1100 +SeaweedStreet = 1200 +LighthouseLane = 1300 +SillyStreet = 2100 +LoopyLane = 2200 +PunchlinePlace = 2300 +WalrusWay = 3100 +SleetStreet = 3200 +PolarPlace = 3300 +AltoAvenue = 4100 +BaritoneBoulevard = 4200 +TenorTerrace = 4300 +ElmStreet = 5100 +MapleStreet = 5200 +OakStreet = 5300 +LullabyLane = 9100 +PajamaPlace = 9200 +BedtimeBoulevard = 9300 +ToonHall = 2513 +HoodHierarchy = {ToontownCentral: (SillyStreet, LoopyLane, PunchlinePlace), + DonaldsDock: (BarnacleBoulevard, SeaweedStreet, LighthouseLane), + TheBrrrgh: (WalrusWay, SleetStreet, PolarPlace), + MinniesMelodyland: (AltoAvenue, BaritoneBoulevard, TenorTerrace), + DaisyGardens: (ElmStreet, MapleStreet, OakStreet), + DonaldsDreamland: (LullabyLane, PajamaPlace, BedtimeBoulevard), + GoofySpeedway: ()} +BossbotHQ = 10000 +BossbotLobby = 10100 +BossbotCountryClubIntA = 10500 +BossbotCountryClubIntB = 10600 +BossbotCountryClubIntC = 10700 +SellbotHQ = 11000 +SellbotLobby = 11100 +SellbotFactoryExt = 11200 +SellbotFactoryInt = 11500 +SellbotMegaCorpInt = 11600 +CashbotHQ = 12000 +CashbotLobby = 12100 +CashbotMintIntA = 12500 +CashbotMintIntB = 12600 +CashbotMintIntC = 12700 +LawbotHQ = 13000 +LawbotLobby = 13100 +LawbotOfficeExt = 13200 +LawbotOfficeInt = 13300 +LawbotStageIntA = 13300 +LawbotStageIntB = 13400 +LawbotStageIntC = 13500 +LawbotStageIntD = 13600 +Tutorial = 15000 +MyEstate = 16000 +GolfZone = 17000 +PartyHood = 18000 +HoodsAlwaysVisited = [17000, 18000] +DynamicZonesBegin = 22000 +DynamicZonesEnd = 1 << 20 +cogDept2index = {'c': 0, + 'l': 1, + 'm': 2, + 's': 3} +cogIndex2dept = invertDict(cogDept2index) +HQToSafezone = {SellbotHQ: DaisyGardens, + CashbotHQ: DonaldsDreamland, + LawbotHQ: TheBrrrgh, + BossbotHQ: DonaldsDock} +CogDeptNames = [TTLocalizer.Bossbot, + TTLocalizer.Lawbot, + TTLocalizer.Cashbot, + TTLocalizer.Sellbot] + +def cogHQZoneId2deptIndex(zone): + if zone >= 13000 and zone <= 13999: + return 1 + elif zone >= 12000: + return 2 + elif zone >= 11000: + return 3 + else: + return 0 + + +def cogHQZoneId2dept(zone): + return cogIndex2dept[cogHQZoneId2deptIndex(zone)] + + +def dept2cogHQ(dept): + dept2hq = {'c': BossbotHQ, + 'l': LawbotHQ, + 'm': CashbotHQ, + 's': SellbotHQ} + return dept2hq[dept] + +MintNumFloors = {CashbotMintIntA: 20, + CashbotMintIntB: 20, + CashbotMintIntC: 20} +CashbotMintCogLevel = 10 +CashbotMintSkelecogLevel = 11 +CashbotMintBossLevel = 12 +MintNumBattles = {CashbotMintIntA: 4, + CashbotMintIntB: 6, + CashbotMintIntC: 8} +MintCogBuckRewards = {CashbotMintIntA: 8, + CashbotMintIntB: 14, + CashbotMintIntC: 20} +MintNumRooms = {CashbotMintIntA: 2 * (6,) + 5 * (7,) + 5 * (8,) + 5 * (9,) + 3 * (10,), + CashbotMintIntB: 3 * (8,) + 6 * (9,) + 6 * (10,) + 5 * (11,), + CashbotMintIntC: 4 * (10,) + 10 * (11,) + 6 * (12,)} +BossbotCountryClubCogLevel = 11 +BossbotCountryClubSkelecogLevel = 12 +BossbotCountryClubBossLevel = 12 +CountryClubNumRooms = {BossbotCountryClubIntA: (4,), + BossbotCountryClubIntB: 3 * (8,) + 6 * (9,) + 6 * (10,) + 5 * (11,), + BossbotCountryClubIntC: 4 * (10,) + 10 * (11,) + 6 * (12,)} +CountryClubNumBattles = {BossbotCountryClubIntA: 3, + BossbotCountryClubIntB: 2, + BossbotCountryClubIntC: 3} +CountryClubCogBuckRewards = {BossbotCountryClubIntA: 8, + BossbotCountryClubIntB: 14, + BossbotCountryClubIntC: 20} +LawbotStageCogLevel = 10 +LawbotStageSkelecogLevel = 11 +LawbotStageBossLevel = 12 +StageNumBattles = {LawbotStageIntA: 0, + LawbotStageIntB: 0, + LawbotStageIntC: 0, + LawbotStageIntD: 0} +StageNoticeRewards = {LawbotStageIntA: 75, + LawbotStageIntB: 150, + LawbotStageIntC: 225, + LawbotStageIntD: 300} +StageNumRooms = {LawbotStageIntA: 2 * (6,) + 5 * (7,) + 5 * (8,) + 5 * (9,) + 3 * (10,), + LawbotStageIntB: 3 * (8,) + 6 * (9,) + 6 * (10,) + 5 * (11,), + LawbotStageIntC: 4 * (10,) + 10 * (11,) + 6 * (12,), + LawbotStageIntD: 4 * (10,) + 10 * (11,) + 6 * (12,)} +FT_FullSuit = 'fullSuit' +FT_Leg = 'leg' +FT_Arm = 'arm' +FT_Torso = 'torso' +factoryId2factoryType = {SellbotFactoryInt: FT_FullSuit, + SellbotMegaCorpInt: FT_FullSuit, + LawbotOfficeInt: FT_FullSuit} +StreetNames = TTLocalizer.GlobalStreetNames +StreetBranchZones = StreetNames.keys() +Hoods = (DonaldsDock, + ToontownCentral, + TheBrrrgh, + MinniesMelodyland, + DaisyGardens, + OutdoorZone, + FunnyFarm, + GoofySpeedway, + DonaldsDreamland, + BossbotHQ, + SellbotHQ, + CashbotHQ, + LawbotHQ, + GolfZone) +HoodsForTeleportAll = (DonaldsDock, + ToontownCentral, + TheBrrrgh, + MinniesMelodyland, + DaisyGardens, + OutdoorZone, + GoofySpeedway, + DonaldsDreamland, + BossbotHQ, + SellbotHQ, + CashbotHQ, + LawbotHQ, + GolfZone) +BingoCardNames = {'normal': 0, +'corners': 1, +'diagonal': 2, +'threeway': 3, +'blockout': 4} +NoPreviousGameId = 0 +RaceGameId = 1 +CannonGameId = 2 +TagGameId = 3 +PatternGameId = 4 +RingGameId = 5 +MazeGameId = 6 +TugOfWarGameId = 7 +CatchGameId = 8 +DivingGameId = 9 +TargetGameId = 10 +VineGameId = 11 +IceGameId = 12 +CogThiefGameId = 13 +TwoDGameId = 14 +MinigameNames = {'race': RaceGameId, + 'cannon': CannonGameId, + 'tag': TagGameId, + 'pattern': PatternGameId, + 'jaymo': PatternGameId, + 'match': PatternGameId, + 'matching': PatternGameId, + 'ring': RingGameId, + 'maze': MazeGameId, + 'tug': TugOfWarGameId, + 'catch': CatchGameId, + 'diving': DivingGameId, + 'target': TargetGameId, + 'vine': VineGameId, + 'ice': IceGameId, + 'thief': CogThiefGameId, + '2d': TwoDGameId} +MinigameTemplateId = -1 +MinigameIDs = (RaceGameId, + CannonGameId, + TagGameId, + PatternGameId, + RingGameId, + MazeGameId, + TugOfWarGameId, + CatchGameId, + DivingGameId, + TargetGameId, + VineGameId, + IceGameId, + CogThiefGameId, + TwoDGameId) +MinigamePlayerMatrix = { + 1: (CannonGameId, MazeGameId, TugOfWarGameId, RingGameId, VineGameId, CogThiefGameId, TwoDGameId, DivingGameId, CatchGameId, TargetGameId), + 2: (CannonGameId, MazeGameId, TugOfWarGameId, PatternGameId, TagGameId, RingGameId, VineGameId, IceGameId, CogThiefGameId, TwoDGameId, DivingGameId, CatchGameId, TargetGameId), + 3: (CannonGameId, MazeGameId, TugOfWarGameId, PatternGameId, RaceGameId, TagGameId, VineGameId, RingGameId, IceGameId, CogThiefGameId, TwoDGameId, DivingGameId, CatchGameId, TargetGameId), + 4: (CannonGameId, MazeGameId, TugOfWarGameId, PatternGameId, RaceGameId, TagGameId, VineGameId, RingGameId, IceGameId, CogThiefGameId, TwoDGameId, DivingGameId, CatchGameId, TargetGameId), +} +KeyboardTimeout = 300 +phaseMap = {Tutorial: 4, + ToontownCentral: 4, + MyEstate: 5.5, + DonaldsDock: 6, + MinniesMelodyland: 6, + GoofySpeedway: 6, + TheBrrrgh: 8, + DaisyGardens: 8, + FunnyFarm: 8, + DonaldsDreamland: 8, + OutdoorZone: 6, + BossbotHQ: 12, + SellbotHQ: 9, + CashbotHQ: 10, + LawbotHQ: 11, + GolfZone: 6, + PartyHood: 13} +streetPhaseMap = {ToontownCentral: 5, + DonaldsDock: 6, + MinniesMelodyland: 6, + GoofySpeedway: 6, + TheBrrrgh: 8, + DaisyGardens: 8, + FunnyFarm: 8, + DonaldsDreamland: 8, + OutdoorZone: 8, + BossbotHQ: 12, + SellbotHQ: 9, + CashbotHQ: 10, + LawbotHQ: 11, + PartyHood: 13} +dnaMap = {Tutorial: 'toontown_central', + ToontownCentral: 'toontown_central', + DonaldsDock: 'donalds_dock', + MinniesMelodyland: 'minnies_melody_land', + GoofySpeedway: 'goofy_speedway', + TheBrrrgh: 'the_burrrgh', + DaisyGardens: 'daisys_garden', + FunnyFarm: 'not done yet', + DonaldsDreamland: 'donalds_dreamland', + OutdoorZone: 'outdoor_zone', + BossbotHQ: 'cog_hq_bossbot', + SellbotHQ: 'cog_hq_sellbot', + CashbotHQ: 'cog_hq_cashbot', + LawbotHQ: 'cog_hq_lawbot', + GolfZone: 'golf_zone'} +hoodNameMap = {DonaldsDock: TTLocalizer.DonaldsDock, + ToontownCentral: TTLocalizer.ToontownCentral, + TheBrrrgh: TTLocalizer.TheBrrrgh, + MinniesMelodyland: TTLocalizer.MinniesMelodyland, + DaisyGardens: TTLocalizer.DaisyGardens, + OutdoorZone: TTLocalizer.OutdoorZone, + FunnyFarm: TTLocalizer.FunnyFarm, + GoofySpeedway: TTLocalizer.GoofySpeedway, + DonaldsDreamland: TTLocalizer.DonaldsDreamland, + BossbotHQ: TTLocalizer.BossbotHQ, + SellbotHQ: TTLocalizer.SellbotHQ, + CashbotHQ: TTLocalizer.CashbotHQ, + LawbotHQ: TTLocalizer.LawbotHQ, + Tutorial: TTLocalizer.Tutorial, + MyEstate: TTLocalizer.MyEstate, + GolfZone: TTLocalizer.GolfZone, + PartyHood: TTLocalizer.PartyHood} +safeZoneCountMap = {MyEstate: 8, + Tutorial: 6, + ToontownCentral: 6, + DonaldsDock: 10, + MinniesMelodyland: 5, + GoofySpeedway: 500, + TheBrrrgh: 8, + DaisyGardens: 9, + FunnyFarm: 500, + DonaldsDreamland: 5, + OutdoorZone: 500, + GolfZone: 500, + PartyHood: 500} +townCountMap = {MyEstate: 8, + Tutorial: 40, + ToontownCentral: 37, + DonaldsDock: 40, + MinniesMelodyland: 40, + GoofySpeedway: 40, + TheBrrrgh: 40, + DaisyGardens: 40, + FunnyFarm: 40, + DonaldsDreamland: 40, + OutdoorZone: 40, + PartyHood: 20} +hoodCountMap = {MyEstate: 2, + Tutorial: 2, + ToontownCentral: 2, + DonaldsDock: 2, + MinniesMelodyland: 2, + GoofySpeedway: 2, + TheBrrrgh: 2, + DaisyGardens: 2, + FunnyFarm: 2, + DonaldsDreamland: 2, + OutdoorZone: 2, + BossbotHQ: 2, + SellbotHQ: 43, + CashbotHQ: 2, + LawbotHQ: 2, + GolfZone: 2, + PartyHood: 2} +TrophyStarLevels = (10, + 20, + 30, + 50, + 75, + 100) +TrophyStarColors = (Vec4(0.9, 0.6, 0.2, 1), + Vec4(0.9, 0.6, 0.2, 1), + Vec4(0.8, 0.8, 0.8, 1), + Vec4(0.8, 0.8, 0.8, 1), + Vec4(1, 1, 0, 1), + Vec4(1, 1, 0, 1)) +SuitWalkSpeed = 4.8 +PieThrowArc = 0 +PieThrowLinear = 1 +PieCodeBossCog = 1 +PieCodeNotBossCog = 2 +PieCodeToon = 3 +PieCodeBossInsides = 4 +PieCodeDefensePan = 5 +PieCodeProsecutionPan = 6 +PieCodeLawyer = 7 +PieCodeInvasionSuit = 8 +PieCodeColors = {PieCodeBossCog: None, + PieCodeNotBossCog: (0.8, + 0.8, + 0.8, + 1), + PieCodeToon: None} +suitIndex = { +'f' : 0, +'p' : 1, +'ym' : 2, +'mm' : 3, +'ds' : 4, +'hh' : 5, +'cr' : 6, +'tbc' : 7, +'bf' : 8, +'b' : 9, +'dt' : 10, +'ac' : 11, +'bs' : 12, +'sd' : 13, +'le' : 14, +'bw' : 15, +'sc' : 16, +'pp' : 17, +'tw' : 18, +'bc' : 19, +'nc' : 20, +'mb' : 21, +'ls' : 22, +'rb' : 23, +'cc' : 24, +'tm' : 25, +'nd' : 26, +'gh' : 27, +'ms' : 28, +'tf' : 29, +'m' : 30, +'mh' : 31 +} +BossCogRollSpeed = 7.5 +BossCogTurnSpeed = 20 +BossCogTreadSpeed = 3.5 +BossCogDizzy = 0 +BossCogElectricFence = 1 +BossCogSwatLeft = 2 +BossCogSwatRight = 3 +BossCogAreaAttack = 4 +BossCogFrontAttack = 5 +BossCogRecoverDizzyAttack = 6 +BossCogDirectedAttack = 7 +BossCogStrafeAttack = 8 +BossCogNoAttack = 9 +BossCogGoonZap = 10 +BossCogSlowDirectedAttack = 11 +BossCogDizzyNow = 12 +BossCogGavelStomp = 13 +BossCogGavelHandle = 14 +BossCogLawyerAttack = 15 +BossCogMoveAttack = 16 +BossCogGolfAttack = 17 +BossCogGolfAreaAttack = 18 +BossCogGearDirectedAttack = 19 +BossCogOvertimeAttack = 20 +BossCogAttackTimes = {BossCogElectricFence: 0, + BossCogSwatLeft: 5.5, + BossCogSwatRight: 5.5, + BossCogAreaAttack: 4.5, + BossCogFrontAttack: 2.65, + BossCogRecoverDizzyAttack: 5.1, + BossCogDirectedAttack: 4.84, + BossCogNoAttack: 6, + BossCogSlowDirectedAttack: 7.84, + BossCogMoveAttack: 3, + BossCogGolfAttack: 6, + BossCogGolfAreaAttack: 7, + BossCogGearDirectedAttack: 4.84, + BossCogOvertimeAttack: 5} +BossCogDamageLevels = {BossCogElectricFence: 1, + BossCogSwatLeft: 5, + BossCogSwatRight: 5, + BossCogAreaAttack: 10, + BossCogFrontAttack: 3, + BossCogRecoverDizzyAttack: 3, + BossCogDirectedAttack: 3, + BossCogStrafeAttack: 2, + BossCogGoonZap: 5, + BossCogSlowDirectedAttack: 10, + BossCogGavelStomp: 20, + BossCogGavelHandle: 2, + BossCogLawyerAttack: 5, + BossCogMoveAttack: 20, + BossCogGolfAttack: 15, + BossCogGolfAreaAttack: 15, + BossCogGearDirectedAttack: 15, + BossCogOvertimeAttack: 10} +BossCogBattleAPosHpr = (0, + -25, + 0, + 0, + 0, + 0) +BossCogBattleBPosHpr = (0, + 25, + 0, + 180, + 0, + 0) +SellbotBossMaxDamage = 100 +SellbotBossMaxDamageNerfed = 100 +SellbotBossBattleOnePosHpr = (0, + -35, + 0, + -90, + 0, + 0) +SellbotBossBattleTwoPosHpr = (0, + 60, + 18, + -90, + 0, + 0) +SellbotBossBattleThreeHpr = (180, 0, 0) +SellbotBossBottomPos = (0, -110, -6.5) +SellbotBossDeathPos = (0, -175, -6.5) +SellbotBossDooberTurnPosA = (-20, -50, 0) +SellbotBossDooberTurnPosB = (20, -50, 0) +SellbotBossDooberTurnPosDown = (0, -50, 0) +SellbotBossDooberFlyPos = (0, -135, -6.5) +SellbotBossTopRampPosA = (-80, -35, 18) +SellbotBossTopRampTurnPosA = (-80, 10, 18) +SellbotBossP3PosA = (-50, 40, 18) +SellbotBossTopRampPosB = (80, -35, 18) +SellbotBossTopRampTurnPosB = (80, 10, 18) +SellbotBossP3PosB = (50, 60, 18) +CashbotBossMaxDamage = 500 +CashbotBossOffstagePosHpr = (120, + -195, + 0, + 0, + 0, + 0) +CashbotBossBattleOnePosHpr = (120, + -230, + 0, + 90, + 0, + 0) +CashbotRTBattleOneStartPosHpr = (94, + -220, + 0, + 110, + 0, + 0) +CashbotBossBattleThreePosHpr = (120, + -315, + 0, + 180, + 0, + 0) +CashbotToonsBattleThreeStartPosHpr = [(105, + -285, + 0, + 208, + 0, + 0), + (136, + -342, + 0, + 398, + 0, + 0), + (105, + -342, + 0, + 333, + 0, + 0), + (135, + -292, + 0, + 146, + 0, + 0), + (93, + -303, + 0, + 242, + 0, + 0), + (144, + -327, + 0, + 64, + 0, + 0), + (145, + -302, + 0, + 117, + 0, + 0), + (93, + -327, + 0, + -65, + 0, + 0)] +CashbotBossSafePosHprs = [(120, + -315, + 30, + 0, + 0, + 0), + (77.2, + -329.3, + 0, + -90, + 0, + 0), + (77.1, + -302.7, + 0, + -90, + 0, + 0), + (165.7, + -326.4, + 0, + 90, + 0, + 0), + (165.5, + -302.4, + 0, + 90, + 0, + 0), + (107.8, + -359.1, + 0, + 0, + 0, + 0), + (133.9, + -359.1, + 0, + 0, + 0, + 0), + (107.0, + -274.7, + 0, + 180, + 0, + 0), + (134.2, + -274.7, + 0, + 180, + 0, + 0)] +CashbotBossCranePosHprs = [(97.4, + -337.6, + 0, + -45, + 0, + 0), + (97.4, + -292.4, + 0, + -135, + 0, + 0), + (142.6, + -292.4, + 0, + 135, + 0, + 0), + (142.6, + -337.6, + 0, + 45, + 0, + 0)] +CashbotBossToMagnetTime = 0.2 +CashbotBossFromMagnetTime = 1 +CashbotBossSafeKnockImpact = 0.5 +CashbotBossSafeNewImpact = 0.0 +CashbotBossGoonImpact = 0.1 +CashbotBossKnockoutDamage = 15 +TTWakeWaterHeight = -4.79 +DDWakeWaterHeight = 1.669 +EstateWakeWaterHeight = -.3 +OZWakeWaterHeight = -0.5 +WakeRunDelta = 0.1 +WakeWalkDelta = 0.2 +NoItems = 0 +NewItems = 1 +OldItems = 2 +SuitInvasionBegin = 0 +SuitInvasionEnd = 1 +SuitInvasionUpdate = 2 +SuitInvasionBulletin = 3 +SkelecogInvasionBegin = 4 +SkelecogInvasionEnd = 5 +SkelecogInvasionBulletin = 6 +WaiterInvasionBegin = 7 +WaiterInvasionEnd = 8 +WaiterInvasionBulletin = 9 +V2InvasionBegin = 10 +V2InvasionEnd = 11 +V2InvasionBulletin = 12 +EndingInvasions = [SuitInvasionEnd, SkelecogInvasionEnd, WaiterInvasionEnd, V2InvasionEnd] +SuitInvasions = { + SuitInvasionBegin: TTLocalizer.SuitInvasionBegin, + SuitInvasionEnd: TTLocalizer.SuitInvasionEnd, + SuitInvasionUpdate: TTLocalizer.SuitInvasionUpdate, + SuitInvasionBulletin: TTLocalizer.SuitInvasionBulletin, + SkelecogInvasionBegin: TTLocalizer.SkelecogInvasionBegin, + SkelecogInvasionEnd: TTLocalizer.SkelecogInvasionEnd, + SkelecogInvasionBulletin: TTLocalizer.SkelecogInvasionBulletin, + WaiterInvasionBegin: TTLocalizer.WaiterInvasionBegin, + WaiterInvasionEnd: TTLocalizer.WaiterInvasionEnd, + WaiterInvasionBulletin: TTLocalizer.WaiterInvasionBulletin, + V2InvasionBegin: TTLocalizer.V2InvasionBegin, + V2InvasionEnd: TTLocalizer.V2InvasionEnd, + V2InvasionBulletin: TTLocalizer.V2InvasionBulletin +} +SUMMER_FIREWORKS = 1 +NEW_YEAR_FIREWORKS = 2 +HALLOWEEN = 3 +CHRISTMAS = 4 +SKELECOG_INVASION = 5 +MR_HOLLYWOOD_INVASION = 6 +BLACK_CAT_DAY = 9 +RESISTANCE_EVENT = 10 +KART_RECORD_DAILY_RESET = 11 +KART_RECORD_WEEKLY_RESET = 12 +CIRCUIT_RACING = 14 +POLAR_PLACE_EVENT = 15 +GRAND_PRIX = 16 +FISH_BINGO = 17 +SILLY_SATURDAY = 18 +BOSSCOG_INVASION = 23 +MARCH_INVASION = 24 +MORE_XP_HOLIDAY = 25 +DECEMBER_INVASION = 28 +APRIL_TOONS_WEEK = 29 +OCTOBER31_FIREWORKS = 31 +NOVEMBER19_FIREWORKS = 32 +SELLBOT_SURPRISE_1 = 33 +SELLBOT_SURPRISE_2 = 34 +SELLBOT_SURPRISE_3 = 35 +SELLBOT_SURPRISE_4 = 36 +CASHBOT_CONUNDRUM_1 = 37 +CASHBOT_CONUNDRUM_2 = 38 +CASHBOT_CONUNDRUM_3 = 39 +CASHBOT_CONUNDRUM_4 = 40 +LAWBOT_GAMBIT_1 = 41 +LAWBOT_GAMBIT_2 = 42 +LAWBOT_GAMBIT_3 = 43 +LAWBOT_GAMBIT_4 = 44 +TROUBLE_BOSSBOTS_1 = 45 +TROUBLE_BOSSBOTS_2 = 46 +TROUBLE_BOSSBOTS_3 = 47 +TROUBLE_BOSSBOTS_4 = 48 +JELLYBEAN_DAY = 49 +FEBRUARY14_FIREWORKS = 51 +JULY14_FIREWORKS = 52 +JUNE22_FIREWORKS = 53 +BIGWIG_INVASION = 54 +COLD_CALLER_INVASION = 53 +BEAN_COUNTER_INVASION = 54 +DOUBLE_TALKER_INVASION = 55 +DOWNSIZER_INVASION = 56 +HYDRANT_ZERO_HOLIDAY = 58 +VALENTOONS_DAY = 59 +SILLYMETER_HOLIDAY = 60 +MAILBOX_ZERO_HOLIDAY = 61 +TRASHCAN_ZERO_HOLIDAY = 62 +SILLY_SURGE_HOLIDAY = 63 +SILLY_CHATTER_ONE = 67 +SILLY_CHATTER_TWO = 68 +SILLY_CHATTER_THREE = 69 +SILLY_CHATTER_FOUR = 70 +SILLY_TEST = 71 +YES_MAN_INVASION = 72 +TIGHTWAD_INVASION = 73 +TELEMARKETER_INVASION = 74 +HEADHUNTER_INVASION = 75 +SPINDOCTOR_INVASION = 76 +MONEYBAGS_INVASION = 77 +TWOFACES_INVASION = 78 +MINGLER_INVASION = 79 +LOANSHARK_INVASION = 80 +CORPORATE_RAIDER_INVASION = 81 +ROBBER_BARON_INVASION = 82 +LEGAL_EAGLE_INVASION = 83 +BIG_WIG_INVASION = 84 +BIG_CHEESE_INVASION = 85 +DOWN_SIZER_INVASION = 86 +MOVER_AND_SHAKER_INVASION = 87 +DOUBLETALKER_INVASION = 88 +PENNY_PINCHER_INVASION = 89 +NAME_DROPPER_INVASION = 90 +AMBULANCE_CHASER_INVASION = 91 +MICROMANAGER_INVASION = 92 +NUMBER_CRUNCHER_INVASION = 93 +SILLY_CHATTER_FIVE = 94 +VICTORY_PARTY_HOLIDAY = 95 +SELLBOT_NERF_HOLIDAY = 96 +JELLYBEAN_TROLLEY_HOLIDAY = 97 +JELLYBEAN_FISHING_HOLIDAY = 98 +JELLYBEAN_PARTIES_HOLIDAY = 99 +TOP_TOONS_MARATHON = 101 +SELLBOT_INVASION = 102 +SELLBOT_FIELD_OFFICE = 103 +SELLBOT_INVASION_MOVER_AND_SHAKER = 104 +IDES_OF_MARCH = 105 +EXPANDED_CLOSETS = 106 +TAX_DAY_INVASION = 107 +KARTING_TICKETS_HOLIDAY = 109 +PRE_JULY_4_DOWNSIZER_INVASION = 110 +PRE_JULY_4_BIGWIG_INVASION = 111 +COMBO_FIREWORKS = 112 +JELLYBEAN_TROLLEY_HOLIDAY_MONTH = 113 +JELLYBEAN_FISHING_HOLIDAY_MONTH = 114 +JELLYBEAN_PARTIES_HOLIDAY_MONTH = 115 +SILLYMETER_EXT_HOLIDAY = 116 +TOT_REWARD_JELLYBEAN_AMOUNT = 100 +TOT_REWARD_END_OFFSET_AMOUNT = 0 +LawbotBossMaxDamage = 2700 +LawbotBossWinningTilt = 40 +LawbotBossInitialDamage = 1350 +LawbotBossBattleOnePosHpr = (-2.798, + -60, + 0, + 0, + 0, + 0) +LawbotBossBattleTwoPosHpr = (-2.798, + 89, + 19.145, + 0, + 0, + 0) +LawbotBossBattleThreePosHpr = LawbotBossBattleTwoPosHpr +LawbotBossBottomPos = (50, 39, 0) +LawbotBossDeathPos = (50, 40, 0) +LawbotBossGavelPosHprs = [(35, + 78.328, + 0, + -135, + 0, + 0), + (68.5, + 78.328, + 0, + 135, + 0, + 0), + (47, + -33, + 0, + 45, + 0, + 0), + (-50, + -39, + 0, + -45, + 0, + 0), + (-9, + -37, + 0, + 0, + 0, + 0), + (-9, + 49, + 0, + -180, + 0, + 0), + (32, + 0, + 0, + 45, + 0, + 0), + (33, + 56, + 0, + 135, + 0, + 0)] +LawbotBossGavelTimes = [(0.2, 0.9, 0.6), + (0.25, 1, 0.5), + (1.0, 6, 0.5), + (0.3, 3, 1), + (0.26, 0.9, 0.45), + (0.24, 1.1, 0.65), + (0.27, 1.2, 0.45), + (0.25, 0.95, 0.5)] +LawbotBossGavelHeadings = [(0, + -15, + 4, + -70 - 45, + 5, + 45), + (0, + -45, + -4, + -35, + -45, + -16, + 32), + (0, + -8, + 19, + -7, + 5, + 23), + (0, + -4, + 8, + -16, + 32, + -45, + 7, + 7, + -30, + 19, + -13, + 25), + (0, + -45, + -90, + 45, + 90), + (0, + -45, + -90, + 45, + 90), + (0, -45, 45), + (0, -45, 45)] +LawbotBossCogRelBattleAPosHpr = (-25, + -10, + 0, + 0, + 0, + 0) +LawbotBossCogRelBattleBPosHpr = (-25, + 10, + 0, + 0, + 0, + 0) +LawbotBossCogAbsBattleAPosHpr = (-5, + -2, + 0, + 0, + 0, + 0) +LawbotBossCogAbsBattleBPosHpr = (-5, + 0, + 0, + 0, + 0, + 0) +LawbotBossWitnessStandPosHpr = (54, + 100, + 0, + -90, + 0, + 0) +LawbotBossInjusticePosHpr = (-3, + 12, + 0, + 90, + 0, + 0) +LawbotBossInjusticeScale = (1.75, 1.75, 1.5) +LawbotBossDefensePanDamage = 1 +LawbotBossLawyerPosHprs = [(-57, + -24, + 0, + -90, + 0, + 0), + (-57, + -12, + 0, + -90, + 0, + 0), + (-57, + 0, + 0, + -90, + 0, + 0), + (-57, + 12, + 0, + -90, + 0, + 0), + (-57, + 24, + 0, + -90, + 0, + 0), + (-57, + 36, + 0, + -90, + 0, + 0), + (-57, + 48, + 0, + -90, + 0, + 0), + (-57, + 60, + 0, + -90, + 0, + 0), + (-3, + -37.3, + 0, + 0, + 0, + 0), + (-3, + 53, + 0, + -180, + 0, + 0)] +LawbotBossLawyerCycleTime = 6 +LawbotBossLawyerToPanTime = 2.5 +LawbotBossLawyerChanceToAttack = 50 +LawbotBossLawyerHeal = 2 +LawbotBossLawyerStunTime = 5 +LawbotBossDifficultySettings = [(38, + 4, + 8, + 1, + 0, + 0), + (36, + 5, + 8, + 1, + 0, + 0), + (34, + 5, + 8, + 1, + 0, + 0), + (32, + 6, + 8, + 2, + 0, + 0), + (30, + 6, + 8, + 2, + 0, + 0), + (28, + 7, + 8, + 3, + 0, + 0), + (26, + 7, + 9, + 3, + 1, + 1), + (24, + 8, + 9, + 4, + 1, + 1), + (22, + 8, + 10, + 4, + 1, + 0)] +LawbotBossCannonPosHprs = [(-40, + -12, + 0, + -90, + 0, + 0), + (-40, + 0, + 0, + -90, + 0, + 0), + (-40, + 12, + 0, + -90, + 0, + 0), + (-40, + 24, + 0, + -90, + 0, + 0), + (-40, + 36, + 0, + -90, + 0, + 0), + (-40, + 48, + 0, + -90, + 0, + 0), + (-40, + 60, + 0, + -90, + 0, + 0), + (-40, + 72, + 0, + -90, + 0, + 0)] +LawbotBossCannonPosA = (-80, -51.48, 0) +LawbotBossCannonPosB = (-80, 70.73, 0) +LawbotBossChairPosHprs = [(60, + 72, + 0, + -90, + 0, + 0), + (60, + 62, + 0, + -90, + 0, + 0), + (60, + 52, + 0, + -90, + 0, + 0), + (60, + 42, + 0, + -90, + 0, + 0), + (60, + 32, + 0, + -90, + 0, + 0), + (60, + 22, + 0, + -90, + 0, + 0), + (70, + 72, + 5, + -90, + 0, + 0), + (70, + 62, + 5, + -90, + 0, + 0), + (70, + 52, + 5, + -90, + 0, + 0), + (70, + 42, + 5, + -90, + 0, + 0), + (70, + 32, + 5, + -90, + 0, + 0), + (70, + 22, + 5, + -90, + 0, + 0)] +LawbotBossChairRow1PosB = (59.3, 48, 14.05) +LawbotBossChairRow1PosA = (59.3, -18.2, 14.05) +LawbotBossChairRow2PosB = (75.1, 48, 28.2) +LawbotBossChairRow2PosA = (75.1, -18.2, 28.2) +LawbotBossCannonBallMax = 12 +LawbotBossJuryBoxStartPos = (94, -8, 5) +LawbotBossJuryBoxRelativeEndPos = (30, 0, 12.645) +LawbotBossJuryBoxMoveTime = 70 +LawbotBossJurorsForBalancedScale = 8 +LawbotBossDamagePerJuror = 68 +LawbotBossCogJurorFlightTime = 10 +LawbotBossCogJurorDistance = 75 +LawbotBossBaseJurorNpcId = 2001 +LawbotBossWitnessEpiloguePosHpr = (-3, + 0, + 0, + 180, + 0, + 0) +LawbotBossChanceForTaunt = 25 +LawbotBossBonusWaitTime = 60 +LawbotBossBonusDuration = 20 +LawbotBossBonusToonup = 10 +LawbotBossBonusWeightMultiplier = 2 +LawbotBossChanceToDoAreaAttack = 11 +LOW_POP_JP = 0 +MID_POP_JP = 100 +HIGH_POP_JP = 200 +LOW_POP_INTL = 399 +MID_POP_INTL = 499 +HIGH_POP_INTL = -1 +LOW_POP = 100 +MID_POP = 200 +HIGH_POP = -1 +PinballCannonBumper = 0 +PinballCloudBumperLow = 1 +PinballCloudBumperMed = 2 +PinballCloudBumperHigh = 3 +PinballTarget = 4 +PinballRoof = 5 +PinballHouse = 6 +PinballFence = 7 +PinballBridge = 8 +PinballStatuary = 9 +PinballScoring = [(100, 1), + (150, 1), + (200, 1), + (250, 1), + (350, 1), + (100, 1), + (50, 1), + (25, 1), + (100, 1), + (10, 1)] +PinballCannonBumperInitialPos = (0, -20, 40) +RentalCop = 0 +RentalCannon = 1 +RentalGameTable = 2 +GlitchKillerZones = [13300, + 13400, + 13500, + 13600] +ColorPlayer = (0.3, + 0.7, + 0.3, + 1) +ColorAvatar = (0.3, + 0.3, + 0.7, + 1) +ColorPet = (0.6, + 0.4, + 0.2, + 1) +ColorFreeChat = (0.3, + 0.3, + 0.8, + 1) +ColorSpeedChat = (0.2, + 0.6, + 0.4, + 1) +ColorNoChat = (0.8, + 0.5, + 0.1, + 1) +PICNIC_COUNTDOWN_TIME = 60 +BossbotRTIntroStartPosHpr = (0, + -64, + 0, + 180, + 0, + 0) +BossbotRTPreTwoPosHpr = (0, + -20, + 0, + 180, + 0, + 0) +BossbotRTEpiloguePosHpr = (0, + 90, + 0, + 180, + 0, + 0) +BossbotBossBattleOnePosHpr = (0, + 355, + 0, + 0, + 0, + 0) +BossbotBossPreTwoPosHpr = (0, + 20, + 0, + 0, + 0, + 0) +BossbotElevCamPosHpr = (0, + -100.544, + 7.18258, + 0, + 0, + 0) +BossbotFoodModelScale = 0.75 +BossbotNumFoodToExplode = 3 +BossbotBossServingDuration = 300 +BossbotPrepareBattleThreeDuration = 20 +WaiterBattleAPosHpr = (20, + -400, + 0, + 0, + 0, + 0) +WaiterBattleBPosHpr = (-20, + -400, + 0, + 0, + 0, + 0) +BossbotBossBattleThreePosHpr = (0, + 355, + 0, + 0, + 0, + 0) +DinerBattleAPosHpr = (20, + -240, + 0, + 0, + 0, + 0) +DinerBattleBPosHpr = (-20, + -240, + 0, + 0, + 0, + 0) +BossbotBossMaxDamage = 500 +BossbotMaxSpeedDamage = 90 +BossbotSpeedRecoverRate = 20 +BossbotBossDifficultySettings = [(8, + 4, + 11, + 3, + 30, + 25), + (9, + 5, + 12, + 6, + 28, + 26), + (10, + 6, + 11, + 7, + 26, + 27), + (8, + 8, + 12, + 8, + 24, + 28), + (13, + 5, + 12, + 9, + 22, + 29)] +BossbotRollSpeedMax = 22 +BossbotRollSpeedMin = 7.5 +BossbotTurnSpeedMax = 60 +BossbotTurnSpeedMin = 20 +BossbotTreadSpeedMax = 10.5 +BossbotTreadSpeedMin = 3.5 +CalendarFilterShowAll = 0 +CalendarFilterShowOnlyHolidays = 1 +CalendarFilterShowOnlyParties = 2 +TTC = 1 +DD = 2 +MM = 3 +GS = 4 +DG = 5 +BR = 6 +OZ = 7 +DL = 8 +AnimPropTypes = Enum(('Unknown', + 'Hydrant', + 'Mailbox', + 'Trashcan'), start=-1) +EmblemTypes = Enum(('Silver', 'Gold')) +NumEmblemTypes = 2 +MaxBankMoney = 30000 +DefaultBankItemId = 1300 +ToonAnimStates = set(['off', + 'neutral', + 'victory', + 'Happy', + 'Sad', + 'Catching', + 'CatchEating', + 'Sleep', + 'walk', + 'jumpSquat', + 'jump', + 'jumpAirborne', + 'jumpLand', + 'run', + 'swim', + 'swimhold', + 'dive', + 'cringe', + 'OpenBook', + 'ReadBook', + 'CloseBook', + 'TeleportOut', + 'Died', + 'TeleportedOut', + 'TeleportIn', + 'Emote', + 'SitStart', + 'Sit', + 'Push', + 'Squish', + 'FallDown', + 'GolfPuttLoop', + 'GolfRotateLeft', + 'GolfRotateRight', + 'GolfPuttSwing', + 'GolfGoodPutt', + 'GolfBadPutt', + 'Flattened', + 'CogThiefRunning', + 'ScientistJealous', + 'ScientistEmcee', + 'ScientistWork', + 'ScientistLessWork', + 'ScientistPlay']) +AV_FLAG_REASON_TOUCH = 1 +AV_FLAG_HISTORY_LEN = 500 +AV_TOUCH_CHECK_DELAY_AI = 3.0 +AV_TOUCH_CHECK_DELAY_CL = 1.0 +AV_TOUCH_CHECK_DIST = 2.0 +AV_TOUCH_CHECK_DIST_Z = 5.0 +AV_TOUCH_CHECK_TIMELIMIT_CL = 0.002 +AV_TOUCH_COUNT_LIMIT = 5 +AV_TOUCH_COUNT_TIME = 300 +GloveCost = 2000 + +BMovementSpeed = 0 +BMovementSpeedMultiplier = 1.3 + +BugReportSite = 'https://bugs.launchpad.net/toontown-united/+filebug' +NPCCollisionDelay = 2.5 + +CostPerLaffRestock = 3 + +FISHSALE_COMPLETE = 0 +FISHSALE_TROPHY = 1 + +CLERK_GOODBYE = 0 +CLERK_GREETING = 1 +CLERK_TOOKTOOLONG = 2 + +KnockKnockHeal = 12 +KnockKnockCooldown = 600 + +CRATE_NOT_OWNER = 0 +CRATE_NO_KEYS = 1 +CRATE_BEANS = 2 +CRATE_BUFFS = 3 +CRATE_NAMETAGS = 4 +CRATE_EMOTES = 5 +CRATE_CLOTHING = 6 +CRATE_ACCESSORIES = 7 + +STAT_COGS = 0 +STAT_V2 = 1 +STAT_SKELE = 2 +STAT_BEANS_SPENT = 3 +STAT_BEANS_EARNT = 4 +STAT_TASKS = 5 +STAT_VP = 6 +STAT_CFO = 7 +STAT_CJ = 8 +STAT_CEO = 9 +STAT_SAD = 10 +STAT_BLDG = 11 +STAT_COGDO = 12 +STAT_ITEMS = 13 +STAT_FISH = 14 +STAT_FLOWERS = 15 +STAT_RACING = 16 +STAT_GOLF = 17 +STAT_SOS = 18 +STAT_UNITES = 19 +STAT_SLIPS = 20 +STAT_GAGS = 21 + +CHAIR_START = 0 +CHAIR_STOP = 1 +CHAIR_NONE = 0 +CHAIR_EXIT = 1 +CHAIR_UNEXPECTED_EXIT = 2 + +MAX_TF_TRIES = 5 +TF_COOLDOWN_SECS = 60 * 60 * 24 +TF_EXPIRE_SECS = 3 * 60 * 60 * 24 +TF_COOLDOWN = 0 +TF_UNKNOWN_SECRET = 1 +TF_SELF_SECRET = 2 +TF_TOO_FAST = 3 +TF_FRIENDS_LIST_FULL_YOU = 4 +TF_FRIENDS_LIST_FULL_HIM = 5 +TF_ALREADY_FRIENDS = 6 +TF_ALREADY_FRIENDS_NAME = 7 +TF_SUCCESS = 8 + +GROUP_ZONES = [11000, 11100, 11200, 12000, 12100, 13000, 13100, 13200, 10000, 10100] + +TOONUP_PULSE_ZONES = [ToontownCentral, DonaldsDock, DaisyGardens, MinniesMelodyland, TheBrrrgh, DonaldsDreamland] +TOONUP_FREQUENCY = 30 + +TV_NOT_OWNER = 0 +TV_INVALID_VIDEO = 1 +TV_OK = 2 + +COLOR_SATURATION_MIN = 0.5 +COLOR_SATURATION_MAX = 0.8 +COLOR_VALUE_MIN = 0.5 +COLOR_VALUE_MAX = 0.8 + +TELEPORT_BUTTON_DEFAULT_COST = 50 +TELEPORT_BUTTON_COSTS = { + ToontownCentral: 5, + DonaldsDock: 15, + DaisyGardens: 30, + MinniesMelodyland: 45, + TheBrrrgh: 60, + DonaldsDreamland: 75 +} + +def getTeleportButtonCost(hoodId): + return TELEPORT_BUTTON_COSTS.get(hoodId, TELEPORT_BUTTON_DEFAULT_COST) \ No newline at end of file diff --git a/toontown/toonbase/ToontownIntervals.py b/toontown/toonbase/ToontownIntervals.py new file mode 100755 index 00000000..a9032097 --- /dev/null +++ b/toontown/toonbase/ToontownIntervals.py @@ -0,0 +1,37 @@ +from direct.interval.MetaInterval import Sequence +from direct.interval.FunctionInterval import Wait, Func +PULSE_GUI_DURATION = 0.2 +PULSE_GUI_CHANGE = 0.333 + +def cleanup(name): + taskMgr.remove(name) + + +def start(ival): + cleanup(ival.getName()) + ival.start() + return ival + + +def loop(ival): + cleanup(ival.getName()) + ival.loop() + return ival + + +def getPulseLargerIval(np, name, duration = PULSE_GUI_DURATION, scale = 1): + return getPulseIval(np, name, 1 + PULSE_GUI_CHANGE, duration=duration, scale=scale) + + +def getPulseSmallerIval(np, name, duration = PULSE_GUI_DURATION, scale = 1): + return getPulseIval(np, name, 1 - PULSE_GUI_CHANGE, duration=duration, scale=scale) + + +def getPulseIval(np, name, change, duration = PULSE_GUI_CHANGE, scale = 1): + return Sequence(np.scaleInterval(duration, scale * change, blendType='easeOut'), np.scaleInterval(duration, scale, blendType='easeIn'), name=name, autoFinish=1) + + +def getPresentGuiIval(np, name, waitDuration = 0.5, moveDuration = 1.0, parent = aspect2d, startPos = (0, 0, 0)): + endPos = np.getPos() + np.setPos(parent, startPos[0], startPos[1], startPos[2]) + return Sequence(Func(np.show), getPulseLargerIval(np, '', scale=np.getScale()), Wait(waitDuration), np.posInterval(moveDuration, endPos, blendType='easeInOut'), name=name, autoFinish=1) diff --git a/toontown/toonbase/ToontownLoader.py b/toontown/toonbase/ToontownLoader.py new file mode 100755 index 00000000..a71a2a8a --- /dev/null +++ b/toontown/toonbase/ToontownLoader.py @@ -0,0 +1,114 @@ +from panda3d.core import * +from direct.directnotify.DirectNotifyGlobal import * +from direct.showbase import Loader +from toontown.toontowngui import ToontownLoadingScreen +from toontown.dna.DNAParser import * + +class ToontownLoader(Loader.Loader): + TickPeriod = 0.2 + + def __init__(self, base): + Loader.Loader.__init__(self, base) + self.inBulkBlock = None + self.blockName = None + self.loadingScreen = ToontownLoadingScreen.ToontownLoadingScreen() + return + + def destroy(self): + self.loadingScreen.destroy() + del self.loadingScreen + Loader.Loader.destroy(self) + + def loadDNAFile(self, dnastore, filename): + return loadDNAFile(dnastore, filename) + + def beginBulkLoad(self, name, label, range, gui, tipCategory, zoneId): + self._loadStartT = globalClock.getRealTime() + Loader.Loader.notify.info("starting bulk load of block '%s'" % name) + if self.inBulkBlock: + Loader.Loader.notify.warning("Tried to start a block ('%s'), but am already in a block ('%s')" % (name, self.blockName)) + return None + self.inBulkBlock = 1 + self._lastTickT = globalClock.getRealTime() + self.blockName = name + self.loadingScreen.begin(range, label, gui, tipCategory, zoneId) + return None + + def endBulkLoad(self, name): + if not self.inBulkBlock: + Loader.Loader.notify.warning("Tried to end a block ('%s'), but not in one" % name) + return None + if name != self.blockName: + Loader.Loader.notify.warning("Tried to end a block ('%s'), other then the current one ('%s')" % (name, self.blockName)) + return None + self.inBulkBlock = None + expectedCount, loadedCount = self.loadingScreen.end() + now = globalClock.getRealTime() + Loader.Loader.notify.info("At end of block '%s', expected %s, loaded %s, duration=%s" % (self.blockName, + expectedCount, + loadedCount, + now - self._loadStartT)) + return + + def abortBulkLoad(self): + if self.inBulkBlock: + Loader.Loader.notify.info("Aborting block ('%s')" % self.blockName) + self.inBulkBlock = None + self.loadingScreen.abort() + return + + def tick(self): + if self.inBulkBlock: + now = globalClock.getRealTime() + if now - self._lastTickT > self.TickPeriod: + self._lastTickT += self.TickPeriod + self.loadingScreen.tick() + try: + base.cr.considerHeartbeat() + except: + pass + + def loadModel(self, *args, **kw): + ret = Loader.Loader.loadModel(self, *args, **kw) + if ret: + gsg = base.win.getGsg() + if gsg: + ret.prepareScene(gsg) + self.tick() + return ret + + def loadFont(self, *args, **kw): + ret = Loader.Loader.loadFont(self, *args, **kw) + self.tick() + return ret + + def loadTexture(self, texturePath, alphaPath = None, okMissing = False): + ret = Loader.Loader.loadTexture(self, texturePath, alphaPath, okMissing=okMissing) + self.tick() + if alphaPath: + self.tick() + return ret + + def pdnaModel(self, *args, **kw): + ret = Loader.Loader.loadModel(self, *args, **kw) + if ret: + gsg = base.win.getGsg() + if gsg: + ret.prepareScene(gsg) + return ret + + def pdnaFont(self, *args, **kw): + return Loader.Loader.loadFont(self, *args, **kw) + + def pdnaTexture(self, texturePath, alphaPath = None, okMissing = False): + return Loader.Loader.loadTexture(self, texturePath, alphaPath, okMissing=okMissing) + + def loadSfx(self, soundPath): + ret = Loader.Loader.loadSfx(self, soundPath) + self.tick() + return ret + + def loadMusic(self, soundPath): + ret = Loader.Loader.loadMusic(self, soundPath) + self.tick() + return ret diff --git a/toontown/toonbase/ToontownStart.py b/toontown/toonbase/ToontownStart.py new file mode 100644 index 00000000..d620890f --- /dev/null +++ b/toontown/toonbase/ToontownStart.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python2 +import gc + +gc.disable() + +import __builtin__ + +__builtin__.process = 'client' + +import sys, os +sys.path.append( + os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../../dependencies" + ) + ) +) + +# Temporary hack patch: +__builtin__.__dict__.update(__import__('pandac.PandaModules', fromlist=['*']).__dict__) + + +from panda3d.core import loadPrcFile + +if not os.path.exists('user/'): + os.mkdir('user/') + + +if __debug__: + try: + import wx + except: + import wxversion + wxversion.select('3.0') + import wx + import sys + from direct.stdpy import threading + + loadPrcFile('dependencies/config/general.prc') + loadPrcFile('dependencies/config/release/dev.prc') + + if os.path.isfile('dependencies/config/local.prc'): + loadPrcFile('dependencies/config/local.prc') + + defaultText = "" + + def __inject_wx(_): + code = textbox.GetValue() + exec(code, globals()) + + def openInjector_wx(): + app = wx.App(redirect=False) + frame = wx.Frame(None, title="Injector", size=(640, 400), style=wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX) + panel = wx.Panel(frame) + button = wx.Button(parent=panel, id=-1, label="Inject", size=(50, 20), pos=(295, 0)) + global textbox + textbox = wx.TextCtrl(parent=panel, id=-1, pos=(20, 22), size=(600, 340), style=wx.TE_MULTILINE) + frame.Bind(wx.EVT_BUTTON, __inject_wx, button) + frame.Show() + app.SetTopWindow(frame) + textbox.AppendText(defaultText) + threading.Thread(target=app.MainLoop).start() + + openInjector_wx() + +from direct.directnotify.DirectNotifyGlobal import directNotify + +notify = directNotify.newCategory('ToontownStart') +notify.setInfo(True) + +if __debug__: + # The VirtualFileSystem, which has already initialized, doesn't see the mount + # directives in the config(s) yet. We have to force it to load those manually: + from panda3d.core import VirtualFileSystem, ConfigVariableList, Filename + vfs = VirtualFileSystem.getGlobalPtr() + mounts = ConfigVariableList('vfs-mount') + for mount in mounts: + mountfile, mountpoint = (mount.split(' ', 2) + [None, None, None])[:2] + vfs.mount(Filename(mountfile), Filename(mountpoint), 0) + +from otp.settings.Settings import Settings +from otp.otpbase import OTPGlobals + +preferencesFilename = ConfigVariableString( + 'preferences-filename', + 'user/preferences.json' +).getValue() + +notify.info('Reading %s...' % preferencesFilename) + +__builtin__.settings = Settings(preferencesFilename) +if 'res' not in settings: + settings['res'] = (1280, 720) +if 'fullscreen' not in settings: + settings['fullscreen'] = False +if 'musicVol' not in settings: + settings['musicVol'] = 1.0 +if 'sfxVol' not in settings: + settings['sfxVol'] = 1.0 +if 'loadDisplay' not in settings: + settings['loadDisplay'] = 'pandagl' +if 'toonChatSounds' not in settings: + settings['toonChatSounds'] = True +if 'language' not in settings: + settings['language'] = 'English' +if 'cogInterface' not in settings: + settings['cogInterface'] = True +if 'speedchatPlus' not in settings: + settings['speedchatPlus'] = True +if 'trueFriends' not in settings: + settings['trueFriends'] = True +if 'tpTransition' not in settings: + settings['tpTransition'] = True +if 'fov' not in settings: + settings['fov'] = OTPGlobals.DefaultCameraFov +if 'talk2speech' not in settings: + settings['talk2speech'] = False +if 'fpsMeter' not in settings: + settings['fpsMeter'] = False + +loadPrcFileData('Settings: res', 'win-size %d %d' % tuple(settings['res'])) +loadPrcFileData('Settings: fullscreen', 'fullscreen %s' % settings['fullscreen']) +loadPrcFileData('Settings: musicVol', 'audio-master-music-volume %s' % settings['musicVol']) +loadPrcFileData('Settings: sfxVol', 'audio-master-sfx-volume %s' % settings['sfxVol']) +loadPrcFileData('Settings: loadDisplay', 'load-display %s' % settings['loadDisplay']) + +import time +import sys +import random +import __builtin__ +from toontown.launcher.TTSLauncher import TTSLauncher + +__builtin__.launcher = TTSLauncher() + +notify.info('Starting the game...') +tempLoader = Loader() +backgroundNode = tempLoader.loadSync(Filename('phase_3/models/gui/loading-background')) +from direct.gui import DirectGuiGlobals +from direct.gui.DirectGui import * +notify.info('Setting the default font...') +import ToontownGlobals +DirectGuiGlobals.setDefaultFontFunc(ToontownGlobals.getInterfaceFont) +import ToonBase +ToonBase.ToonBase() +from panda3d.core import * +if base.win is None: + notify.error('Unable to open window; aborting.') +ConfigVariableDouble('decompressor-step-time').setValue(0.01) +ConfigVariableDouble('extractor-step-time').setValue(0.01) +backgroundNodePath = aspect2d.attachNewNode(backgroundNode, 0) +backgroundNodePath.setPos(0.0, 0.0, 0.0) +backgroundNodePath.setScale(render2d, VBase3(1)) +backgroundNodePath.find('**/fg').hide() +logo = OnscreenImage( + image='phase_3/maps/toontown-logo.png', + scale=(1 / (4.0/3.0), 1, 1 / (4.0/3.0)), + pos=backgroundNodePath.find('**/fg').getPos()) +logo.setTransparency(TransparencyAttrib.MAlpha) +logo.setBin('fixed', 20) +logo.reparentTo(backgroundNodePath) +backgroundNodePath.find('**/bg').setBin('fixed', 10) +base.graphicsEngine.renderFrame() +DirectGuiGlobals.setDefaultRolloverSound(base.loadSfx('phase_3/audio/sfx/GUI_rollover.ogg')) +DirectGuiGlobals.setDefaultClickSound(base.loadSfx('phase_3/audio/sfx/GUI_create_toon_fwd.ogg')) +DirectGuiGlobals.setDefaultDialogGeom(loader.loadModel('phase_3/models/gui/dialog_box_gui')) +import TTLocalizer +if base.musicManagerIsValid: + music = base.loadMusic('phase_3/audio/bgm/tt_theme.ogg') + if music: + music.setLoop(1) + music.play() + notify.info('Loading the default GUI sounds...') + DirectGuiGlobals.setDefaultRolloverSound(base.loadSfx('phase_3/audio/sfx/GUI_rollover.ogg')) + DirectGuiGlobals.setDefaultClickSound(base.loadSfx('phase_3/audio/sfx/GUI_create_toon_fwd.ogg')) +else: + music = None +import ToontownLoader +from direct.gui.DirectGui import * +serverVersion = base.config.GetString('server-version', 'no_version_set') +version = OnscreenText(serverVersion, pos=(-1.3, -0.975), scale=0.06, fg=Vec4(0, 0, 1, 0.6), align=TextNode.ALeft) +version.setPos(0.03,0.03) +version.reparentTo(base.a2dBottomLeft) +from toontown.suit import Suit +Suit.loadModels() +loader.beginBulkLoad('init', TTLocalizer.LoaderLabel, 138, 0, TTLocalizer.TIP_NONE, 0) +from ToonBaseGlobal import * +from direct.showbase.MessengerGlobal import * +from toontown.distributed import ToontownClientRepository +cr = ToontownClientRepository.ToontownClientRepository(serverVersion) +cr.music = music +del music +base.initNametagGlobals() +base.setFrameRateMeter(settings['fpsMeter']) +base.cr = cr +loader.endBulkLoad('init') +from otp.friends import FriendManager +from otp.distributed.OtpDoGlobals import * +cr.generateGlobalObject(OTP_DO_ID_FRIEND_MANAGER, 'FriendManager') +base.startShow(cr) +backgroundNodePath.reparentTo(hidden) +backgroundNodePath.removeNode() +del backgroundNodePath +del backgroundNode +del tempLoader +version.cleanup() +del version +base.loader = base.loader +__builtin__.loader = base.loader +autoRun = ConfigVariableBool('toontown-auto-run', 1) + +gc.enable() +gc.collect() + +if autoRun: + try: + base.run() + except SystemExit: + pass + except: + import traceback + traceback.print_exc() \ No newline at end of file diff --git a/toontown/toonbase/ToontownStartRemoteDB.py b/toontown/toonbase/ToontownStartRemoteDB.py new file mode 100644 index 00000000..976b6353 --- /dev/null +++ b/toontown/toonbase/ToontownStartRemoteDB.py @@ -0,0 +1,39 @@ +import json, os, sys +import urllib, urllib2, cookielib, socket +from panda3d.core import * + + +req_version = (2,7,9) +cur_version = sys.version_info +if cur_version < req_version: + print 'Your version of python is too old. Please upgrade to 2.7.9.' + sys.exit() + +username = os.environ['ttsUsername'] +password = os.environ['ttsPassword'] +distribution = 'qa' + +accountServerEndpoint = 'https://toontownstride.com/api/' + +data = urllib.urlencode({'username': username, 'password': password, 'distribution': distribution, 'version': 'dev'}) +cookie_jar = cookielib.LWPCookieJar() +cookie = urllib2.HTTPCookieProcessor(cookie_jar) +opener = urllib2.build_opener(cookie) +req = urllib2.Request(accountServerEndpoint + 'login', data, + headers={"Content-Type" : "application/x-www-form-urlencoded"}) +req.get_method = lambda: "POST" +_response = opener.open(req).read() + +try: + response = json.loads(_response) +except ValueError: + print "Couldn't verify account credentials." +else: + if response['status'] != 7: + print response['message'] + else: + os.environ['TTS_PLAYCOOKIE'] = response['token'] + os.environ['TTS_GAMESERVER'] = response['gameserver'] + + # Start the game: + import toontown.toonbase.ToontownStart \ No newline at end of file diff --git a/toontown/toonbase/ToontownTimer.py b/toontown/toonbase/ToontownTimer.py new file mode 100755 index 00000000..07a32587 --- /dev/null +++ b/toontown/toonbase/ToontownTimer.py @@ -0,0 +1,117 @@ +from pandac.PandaModules import Vec4 +from direct.gui.DirectGui import DirectFrame, DGG +from direct.task import Task +from direct.showbase.PythonUtil import bound +from otp.otpbase import OTPGlobals + +class ToontownTimer(DirectFrame): + ClockImage = None + TimerId = 0 + + def __init__(self, useImage = True, highlightNearEnd = True): + if useImage: + image = self.getImage() + else: + image = None + DirectFrame.__init__(self, state=DGG.DISABLED, relief=None, scale=0.45, image=image, image_pos=(0, 0, 0), text='0', text_fg=(0, 0, 0, 1), text_font=OTPGlobals.getInterfaceFont(), text_pos=(-0.01, -0.15), text_scale=0.35) + self.initialiseoptions(ToontownTimer) + self.timerId = ToontownTimer.TimerId + ToontownTimer.TimerId += 1 + self.highlightNearEnd = highlightNearEnd + self.countdownTask = None + self.currentTime = 0 + self.taskTime = 0.0 + self.setFontColor(Vec4(0, 0, 0, 1)) + return + + def setFontColor(self, vColor): + self.vFontColor = vColor + + def getImage(self): + if ToontownTimer.ClockImage == None: + model = loader.loadModel('phase_3.5/models/gui/clock_gui') + ToontownTimer.ClockImage = model.find('**/alarm_clock') + model.removeNode() + return ToontownTimer.ClockImage + + def posInTopRightCorner(self): + self.reparentTo(base.a2dTopRight) + self.setPos(-0.173, 0, -0.17) + + def posBelowTopRightCorner(self): + self.reparentTo(base.a2dTopRight) + self.setPos(-0.173, 0, -0.42) + + def posAboveShtikerBook(self): + self.reparentTo(base.a2dBottomRight) + self.setPos(-0.173, 0, 0.37) + + def setTime(self, time): + time = bound(time, 0, 999) + if time == self.currentTime: + return + self.currentTime = time + timeStr = str(time) + timeStrLen = len(timeStr) + if timeStrLen == 1: + if time <= 5 and self.highlightNearEnd: + self.setTimeStr(timeStr, 0.34, (-0.025, -0.125), Vec4(1, 0, 0, 1)) + else: + self.setTimeStr(timeStr, 0.34, (-0.025, -0.125)) + elif timeStrLen == 2: + self.setTimeStr(timeStr, 0.27, (-0.025, -0.1)) + elif timeStrLen == 3: + self.setTimeStr(timeStr, 0.2, (-0.01, -0.08)) + + def setTimeStr(self, timeStr, scale = 0.2, pos = (-0.01, -0.08), fg = None): + self['text'] = '' + self['text_fg'] = fg or self.vFontColor + self['text_scale'] = scale + self['text_pos'] = pos + self['text'] = timeStr + + def getElapsedTime(self): + return self.taskTime + + def _timerTask(self, task): + countdownTime = int(task.duration - task.time) + self.setTime(countdownTime) + self.taskTime = task.time + if task.time >= task.duration: + self.timerExpired() + if task.callback: + task.callback(*task.extraArgs) + return Task.done + else: + return Task.cont + + def countdown(self, duration, callback = None, extraArgs = []): + self.countdownTask = Task.Task(self._timerTask) + self.countdownTask.duration = duration + self.countdownTask.callback = callback + self.countdownTask.extraArgs = extraArgs + taskMgr.remove('timerTask%s' % self.timerId) + return taskMgr.add(self.countdownTask, 'timerTask%s' % self.timerId) + + def timerExpired(self): + pass + + def stop(self): + if self.countdownTask: + taskMgr.remove(self.countdownTask) + + def reset(self): + self.stop() + self.setTime(0) + taskMgr.remove('timerTask%s' % self.timerId) + self.taskTime = 0.0 + + def destroy(self): + self.reset() + self.countdownTask = None + DirectFrame.destroy(self) + return + + def cleanup(self): + self.destroy() + self.notify.warning('Call destroy, not cleanup') diff --git a/toontown/toonbase/__init__.py b/toontown/toonbase/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/toontowngui/TTDialog.py b/toontown/toontowngui/TTDialog.py new file mode 100755 index 00000000..b11af4cb --- /dev/null +++ b/toontown/toontowngui/TTDialog.py @@ -0,0 +1,16 @@ +from otp.otpgui.OTPDialog import * + +class TTDialog(OTPDialog): + + def __init__(self, parent = None, style = NoButtons, **kw): + self.path = 'phase_3/models/gui/dialog_box_buttons_gui' + OTPDialog.__init__(self, parent, style, **kw) + self.initialiseoptions(TTDialog) + + +class TTGlobalDialog(GlobalDialog): + + def __init__(self, message = '', doneEvent = None, style = NoButtons, okButtonText = OTPLocalizer.DialogOK, cancelButtonText = OTPLocalizer.DialogCancel, **kw): + self.path = 'phase_3/models/gui/dialog_box_buttons_gui' + GlobalDialog.__init__(self, message, doneEvent, style, okButtonText, cancelButtonText, **kw) + self.initialiseoptions(TTGlobalDialog) diff --git a/toontown/toontowngui/ToonHeadDialog.py b/toontown/toontowngui/ToonHeadDialog.py new file mode 100755 index 00000000..8493e57f --- /dev/null +++ b/toontown/toontowngui/ToonHeadDialog.py @@ -0,0 +1,44 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +import TTDialog +from toontown.toon import ToonHead + +class ToonHeadDialog(TTDialog.TTDialog): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonHeadDialog') + + def __init__(self, dna, **kw): + self.dna = dna + head = hidden.attachNewNode('head', 20) + self.headModel = ToonHead.ToonHead() + self.headModel.setupHead(self.dna, forGui=1) + self.headModel.fitAndCenterHead(1.0, forGui=1) + self.headModel.reparentTo(head) + self.headModel.setName('headModel') + self.headModel.startBlink() + optiondefs = (('dialogName', 'ToonHeadDialog', None), + ('style', TTDialog.NoButtons, None), + ('geom', head, None), + ('geom_scale', 0.35, None), + ('geom_pos', (-0.25, 0, 0), None), + ('text_wordwrap', 9, None), + ('fadeScreen', 0, None)) + self.defineoptions(kw, optiondefs) + TTDialog.TTDialog.__init__(self, style=self['style']) + self.initialiseoptions(ToonHeadDialog) + self.postInitialiseFuncList.append(self.replaceHead) + self.reparentTo(base.a2dTopRight) + self.setPos(-0.85, 0, -0.25) + return + + def replaceHead(self): + head = self.stateNodePath[0].find('**/head') + headModelCopy = self.stateNodePath[0].find('**/headModel') + headModelCopy.removeNode() + self.headModel.reparentTo(head) + + def cleanup(self): + TTDialog.TTDialog.cleanup(self) + self.headModel.stopBlink() + self.headModel.stopLookAroundNow() + self.headModel.delete() diff --git a/toontown/toontowngui/ToontownLoadingScreen.py b/toontown/toontowngui/ToontownLoadingScreen.py new file mode 100755 index 00000000..00671845 --- /dev/null +++ b/toontown/toontowngui/ToontownLoadingScreen.py @@ -0,0 +1,79 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.hood import ZoneUtil +import random + +LOADING_SCREEN_SORT_INDEX = 4000 + +class ToontownLoadingScreen: + + def __init__(self): + self.__expectedCount = 0 + self.__count = 0 + self.textures = [(loader.loadTexture('phase_3.5/maps/loading/toon.jpg'), ToontownGlobals.getInterfaceFont(), (0, 0, 0.5, 1)), + (loader.loadTexture('phase_3.5/maps/loading/cog.jpg'), ToontownGlobals.getSuitFont(), (1.0, 1.0, 1.0, 1)), + (loader.loadTexture('phase_3.5/maps/loading/default.jpg'), ToontownGlobals.getInterfaceFont(), (0, 0, 0.5, 1)) + ] + self.gui = loader.loadModel('phase_3/models/gui/progress-background.bam') + self.title = DirectLabel(guiId='ToontownLoadingScreenTitle', parent=self.gui, relief=None, pos=(base.a2dRight/5, 0, 0.235), text='', textMayChange=1, text_scale=0.08, text_fg=(0, 0, 0.5, 1), text_align=TextNode.ALeft, text_font=ToontownGlobals.getInterfaceFont()) + self.tip = DirectLabel(guiId='ToontownLoadingScreenTip', parent=self.gui, relief=None, pos=(0, 0, 0.045), text='', textMayChange=1, text_scale=0.05, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_align=TextNode.ACenter) + self.waitBar = DirectWaitBar(guiId='ToontownLoadingScreenWaitBar', parent=self.gui, frameSize=(base.a2dLeft+(base.a2dRight/4.95), base.a2dRight-(base.a2dRight/4.95), -0.03, 0.03), pos=(0, 0, 0.15), text='') + logoScale = 0.5625 # Scale for our locked aspect ratio (2:1). + self.logo = OnscreenImage( + image='phase_3/maps/toontown-logo.png', + scale=(logoScale * 2.0, 1, logoScale)) + self.logo.reparentTo(hidden) + self.logo.setTransparency(TransparencyAttrib.MAlpha) + scale = self.logo.getScale() + self.logo.setPos(0, 0, -scale[2]) + + def destroy(self): + self.tip.destroy() + self.title.destroy() + self.gui.removeNode() + self.logo.removeNode() + + def getTip(self, tipCategory): + return TTLocalizer.TipTitle + ' ' + random.choice(TTLocalizer.TipDict.get(tipCategory)) + + def begin(self, range, label, gui, tipCategory, zoneId): + info = self.textures[ZoneUtil.isCogHQZone(zoneId) if zoneId else 2] + self.waitBar['range'] = range + self.title['text'] = label + self.__count = 0 + self.__expectedCount = range + if gui: + self.waitBar['frameSize'] = (base.a2dLeft+(base.a2dRight/4.95), base.a2dRight-(base.a2dRight/4.95), -0.03, 0.03) + self.title['text_font'] = info[1] + self.title['text_fg'] = info[2] + self.title.reparentTo(base.a2dpBottomLeft, LOADING_SCREEN_SORT_INDEX) + self.title.setPos(base.a2dRight/5, 0, 0.235) + self.tip['text'] = self.getTip(tipCategory) + self.gui.setPos(0, -0.1, 0) + self.gui.reparentTo(aspect2d, LOADING_SCREEN_SORT_INDEX) + self.gui.setTexture(info[0], 1) + self.logo.reparentTo(base.a2dpTopCenter, LOADING_SCREEN_SORT_INDEX) + else: + self.title.reparentTo(base.a2dpBottomLeft, LOADING_SCREEN_SORT_INDEX) + self.gui.reparentTo(hidden) + self.logo.reparentTo(hidden) + self.tip.reparentTo(base.a2dpBottomCenter, LOADING_SCREEN_SORT_INDEX) + self.waitBar.reparentTo(base.a2dpBottomCenter, LOADING_SCREEN_SORT_INDEX) + self.waitBar.update(self.__count) + + def end(self): + self.waitBar.finish() + self.waitBar.reparentTo(self.gui) + self.title.reparentTo(self.gui) + self.tip.reparentTo(self.gui) + self.gui.reparentTo(hidden) + self.logo.reparentTo(hidden) + return (self.__expectedCount, self.__count) + + def abort(self): + self.gui.reparentTo(hidden) + + def tick(self): + self.__count = self.__count + 1 + self.waitBar.update(self.__count) \ No newline at end of file diff --git a/toontown/toontowngui/__init__.py b/toontown/toontowngui/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/town/BRStreet.py b/toontown/town/BRStreet.py new file mode 100755 index 00000000..283112eb --- /dev/null +++ b/toontown/town/BRStreet.py @@ -0,0 +1,20 @@ +from direct.task.Task import Task +import random + +from toontown.town import Street + + +class BRStreet(Street.Street): + def enter(self, requestStatus): + Street.Street.enter(self, requestStatus) + taskMgr.doMethodLater(1, self.__windTask, 'BR-wind') + + def exit(self): + Street.Street.exit(self) + taskMgr.remove('BR-wind') + + def __windTask(self, task): + base.playSfx(random.choice(self.loader.windSound)) + time = random.random() * 8.0 + 1 + taskMgr.doMethodLater(time, self.__windTask, 'BR-wind') + return Task.done diff --git a/toontown/town/BRTownLoader.py b/toontown/town/BRTownLoader.py new file mode 100755 index 00000000..919be568 --- /dev/null +++ b/toontown/town/BRTownLoader.py @@ -0,0 +1,43 @@ +from toontown.battle import BattleParticles +from toontown.suit import Suit +from toontown.town import BRStreet +from toontown.town import TownLoader + + +class BRTownLoader(TownLoader.TownLoader): + def __init__(self, hood, parentFSM, doneEvent): + TownLoader.TownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = BRStreet.BRStreet + self.musicFile = 'phase_8/audio/bgm/TB_SZ.ogg' + self.activityMusicFile = 'phase_8/audio/bgm/TB_SZ_activity.ogg' + self.townStorageDNAFile = 'phase_8/dna/storage_BR_town.pdna' + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadSuits(3) + dnaFile = 'phase_8/dna/the_burrrgh_' + str(self.canonicalBranchZone) + '.pdna' + self.createHood(dnaFile) + self.windSound = map(base.loadSfx, ['phase_8/audio/sfx/SZ_TB_wind_1.ogg', + 'phase_8/audio/sfx/SZ_TB_wind_2.ogg', + 'phase_8/audio/sfx/SZ_TB_wind_3.ogg']) + self.snow = BattleParticles.loadParticleFile('snowdisk.ptf') + self.snow.setPos(0, 0, 5) + self.snowRender = self.geom.attachNewNode('snowRender') + self.snowRender.setDepthWrite(0) + self.snowRender.setBin('fixed', 1) + + def unload(self): + TownLoader.TownLoader.unload(self) + Suit.unloadSuits(3) + del self.windSound + del self.snow + del self.snowRender + + def enter(self, requestStatus): + TownLoader.TownLoader.enter(self, requestStatus) + self.snow.start(camera, self.snowRender) + + def exit(self): + TownLoader.TownLoader.exit(self) + self.snow.cleanup() + self.snowRender.removeNode() diff --git a/toontown/town/DDStreet.py b/toontown/town/DDStreet.py new file mode 100755 index 00000000..8edf7c37 --- /dev/null +++ b/toontown/town/DDStreet.py @@ -0,0 +1,11 @@ +from toontown.town import Street + + +class DDStreet(Street.Street): + def enter(self, requestStatus): + Street.Street.enter(self, requestStatus) + self.loader.hood.setWhiteFog() + + def exit(self): + Street.Street.exit(self) + self.loader.hood.setNoFog() diff --git a/toontown/town/DDTownLoader.py b/toontown/town/DDTownLoader.py new file mode 100755 index 00000000..d9fce400 --- /dev/null +++ b/toontown/town/DDTownLoader.py @@ -0,0 +1,22 @@ +from toontown.suit import Suit +from toontown.town import DDStreet +from toontown.town import TownLoader + + +class DDTownLoader(TownLoader.TownLoader): + def __init__(self, hood, parentFSM, doneEvent): + TownLoader.TownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = DDStreet.DDStreet + self.musicFile = 'phase_6/audio/bgm/DD_SZ.ogg' + self.activityMusicFile = 'phase_6/audio/bgm/DD_SZ_activity.ogg' + self.townStorageDNAFile = 'phase_6/dna/storage_DD_town.pdna' + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadSuits(2) + dnaFile = 'phase_6/dna/donalds_dock_' + str(self.canonicalBranchZone) + '.pdna' + self.createHood(dnaFile) + + def unload(self): + TownLoader.TownLoader.unload(self) + Suit.unloadSuits(2) diff --git a/toontown/town/DGStreet.py b/toontown/town/DGStreet.py new file mode 100755 index 00000000..fdf42f1e --- /dev/null +++ b/toontown/town/DGStreet.py @@ -0,0 +1,5 @@ +from toontown.town import Street + + +class DGStreet(Street.Street): + pass diff --git a/toontown/town/DGTownLoader.py b/toontown/town/DGTownLoader.py new file mode 100755 index 00000000..89ddca97 --- /dev/null +++ b/toontown/town/DGTownLoader.py @@ -0,0 +1,22 @@ +from toontown.suit import Suit +from toontown.town import DGStreet +from toontown.town import TownLoader + + +class DGTownLoader(TownLoader.TownLoader): + def __init__(self, hood, parentFSM, doneEvent): + TownLoader.TownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = DGStreet.DGStreet + self.musicFile = 'phase_8/audio/bgm/DG_SZ.ogg' + self.activityMusicFile = 'phase_8/audio/bgm/DG_SZ.ogg' + self.townStorageDNAFile = 'phase_8/dna/storage_DG_town.pdna' + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadSuits(3) + dnaFile = 'phase_8/dna/daisys_garden_' + str(self.canonicalBranchZone) + '.pdna' + self.createHood(dnaFile) + + def unload(self): + TownLoader.TownLoader.unload(self) + Suit.unloadSuits(3) diff --git a/toontown/town/DLStreet.py b/toontown/town/DLStreet.py new file mode 100755 index 00000000..55d7645f --- /dev/null +++ b/toontown/town/DLStreet.py @@ -0,0 +1,5 @@ +from toontown.town import Street + + +class DLStreet(Street.Street): + pass diff --git a/toontown/town/DLTownLoader.py b/toontown/town/DLTownLoader.py new file mode 100755 index 00000000..8bccd1b7 --- /dev/null +++ b/toontown/town/DLTownLoader.py @@ -0,0 +1,22 @@ +from toontown.suit import Suit +from toontown.town import DLStreet +from toontown.town import TownLoader + + +class DLTownLoader(TownLoader.TownLoader): + def __init__(self, hood, parentFSM, doneEvent): + TownLoader.TownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = DLStreet.DLStreet + self.musicFile = 'phase_8/audio/bgm/DL_SZ.ogg' + self.activityMusicFile = 'phase_8/audio/bgm/DL_SZ_activity.ogg' + self.townStorageDNAFile = 'phase_8/dna/storage_DL_town.pdna' + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadSuits(3) + dnaFile = 'phase_8/dna/donalds_dreamland_' + str(self.canonicalBranchZone) + '.pdna' + self.createHood(dnaFile) + + def unload(self): + TownLoader.TownLoader.unload(self) + Suit.unloadSuits(3) diff --git a/toontown/town/MMStreet.py b/toontown/town/MMStreet.py new file mode 100755 index 00000000..69edcbc8 --- /dev/null +++ b/toontown/town/MMStreet.py @@ -0,0 +1,5 @@ +from toontown.town import Street + + +class MMStreet(Street.Street): + pass diff --git a/toontown/town/MMTownLoader.py b/toontown/town/MMTownLoader.py new file mode 100755 index 00000000..66b68a44 --- /dev/null +++ b/toontown/town/MMTownLoader.py @@ -0,0 +1,22 @@ +from toontown.suit import Suit +from toontown.town import MMStreet +from toontown.town import TownLoader + + +class MMTownLoader(TownLoader.TownLoader): + def __init__(self, hood, parentFSM, doneEvent): + TownLoader.TownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = MMStreet.MMStreet + self.musicFile = 'phase_6/audio/bgm/MM_SZ.ogg' + self.activityMusicFile = 'phase_6/audio/bgm/MM_SZ_activity.ogg' + self.townStorageDNAFile = 'phase_6/dna/storage_MM_town.pdna' + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadSuits(2) + dnaFile = 'phase_6/dna/minnies_melody_land_' + str(self.canonicalBranchZone) + '.pdna' + self.createHood(dnaFile) + + def unload(self): + TownLoader.TownLoader.unload(self) + Suit.unloadSuits(2) diff --git a/toontown/town/Street.py b/toontown/town/Street.py new file mode 100755 index 00000000..8544a5b0 --- /dev/null +++ b/toontown/town/Street.py @@ -0,0 +1,361 @@ +from panda3d.core import * +from toontown.battle.BattleProps import * +from toontown.battle.BattleSounds import * +from toontown.distributed.ToontownMsgTypes import * +from direct.gui.DirectGui import cleanupDialog +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import Place +from toontown.battle import BattlePlace +from direct.showbase import DirectObject +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.task import Task +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.battle import BattleParticles +from toontown.building import Elevator +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.toon.Toon import teleportDebug +from toontown.estate import HouseGlobals +from toontown.toonbase import TTLocalizer +from direct.interval.IntervalGlobal import * +from otp.nametag import NametagGlobals + +visualizeZones = base.config.GetBool('visualize-zones', 0) + +class Street(BattlePlace.BattlePlace): + + notify = DirectNotifyGlobal.directNotify.newCategory('Street') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.fsm = ClassicFSM.ClassicFSM('Street', [State.State('start', self.enterStart, self.exitStart, ['walk', + 'tunnelIn', + 'doorIn', + 'teleportIn', + 'elevatorIn']), + State.State('walk', self.enterWalk, self.exitWalk, ['push', + 'sit', + 'stickerBook', + 'WaitForBattle', + 'battle', + 'doorOut', + 'elevator', + 'tunnelIn', + 'tunnelOut', + 'teleportOut', + 'quest', + 'stopped', + 'fishing', + 'purchase', + 'died']), + State.State('sit', self.enterSit, self.exitSit, ['walk']), + State.State('push', self.enterPush, self.exitPush, ['walk']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'push', + 'sit', + 'battle', + 'doorOut', + 'elevator', + 'tunnelIn', + 'tunnelOut', + 'WaitForBattle', + 'teleportOut', + 'quest', + 'stopped', + 'fishing', + 'purchase']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', 'walk']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk', 'stopped']), + State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk', 'stopped']), + State.State('elevatorIn', self.enterElevatorIn, self.exitElevatorIn, ['walk']), + State.State('elevator', self.enterElevator, self.exitElevator, ['walk']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', + 'teleportOut', + 'quietZone', + 'WaitForBattle', + 'battle']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', 'quietZone', 'WaitForBattle']), + State.State('died', self.enterDied, self.exitDied, ['quietZone']), + State.State('tunnelIn', self.enterTunnelIn, self.exitTunnelIn, ['walk']), + State.State('tunnelOut', self.enterTunnelOut, self.exitTunnelOut, ['final']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['teleportIn']), + State.State('quest', self.enterQuest, self.exitQuest, ['walk', 'stopped']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk']), + State.State('fishing', self.enterFishing, self.exitFishing, ['walk']), + State.State('purchase', self.enterPurchase, self.exitPurchase, ['walk']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.parentFSM = parentFSM + self.tunnelOriginList = [] + self.elevatorDoneEvent = 'elevatorDone' + self.halloweenLights = [] + self.zone = 0 + + def enter(self, requestStatus, visibilityFlag = 1, arrowsOn = 1): + teleportDebug(requestStatus, 'Street.enter(%s)' % (requestStatus,)) + self._ttfToken = None + self.fsm.enterInitialState() + base.playMusic(self.loader.music, looping=1, volume=0.8) + self.loader.geom.reparentTo(render) + if visibilityFlag: + self.visibilityOn() + base.localAvatar.setGeom(self.loader.geom) + base.localAvatar.setOnLevelGround(1) + self._telemLimiter = TLGatherAllAvs('Street', RotationLimitToH) + NametagGlobals.setMasterArrowsOn(arrowsOn) + self.zone = ZoneUtil.getBranchZone(requestStatus['zoneId']) + + def __lightDecorationOn__(): + geom = base.cr.playGame.getPlace().loader.geom + self.halloweenLights = geom.findAllMatches('**/*light*') + self.halloweenLights += geom.findAllMatches('**/*lamp*') + self.halloweenLights += geom.findAllMatches('**/prop_snow_tree*') + for light in self.halloweenLights: + light.setColorScaleOff(1) + + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.HALLOWEEN) and self.loader.hood.spookySkyFile: + lightsOff = Sequence(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(0.55, 0.55, 0.65, 1)), Func(self.loader.hood.startSpookySky)) + lightsOff.start() + else: + self.loader.hood.startSky() + lightsOn = LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(1, 1, 1, 1)) + lightsOn.start() + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + self.enterZone(requestStatus['zoneId']) + self.tunnelOriginList = base.cr.hoodMgr.addLinkTunnelHooks(self, self.loader.nodeList) + self.fsm.request(requestStatus['how'], [requestStatus]) + + def exit(self, visibilityFlag = 1): + if visibilityFlag: + self.visibilityOff() + self.loader.geom.reparentTo(hidden) + self._telemLimiter.destroy() + del self._telemLimiter + + def __lightDecorationOff__(): + for light in self.halloweenLights: + light.reparentTo(hidden) + + NametagGlobals.setMasterArrowsOn(0) + self.loader.hood.stopSky() + self.loader.music.stop() + base.localAvatar.setGeom(render) + base.localAvatar.setOnLevelGround(0) + + def load(self): + BattlePlace.BattlePlace.load(self) + self.parentFSM.getStateNamed('street').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('street').removeChild(self.fsm) + del self.parentFSM + del self.fsm + self.enterZone(None) + cleanupDialog('globalDialog') + self.ignoreAll() + BattlePlace.BattlePlace.unload(self) + return + + def enterElevatorIn(self, requestStatus): + self._eiwbTask = taskMgr.add(Functor(self._elevInWaitBldgTask, requestStatus['bldgDoId']), uniqueName('elevInWaitBldg')) + + def _elevInWaitBldgTask(self, bldgDoId, task): + bldg = base.cr.doId2do.get(bldgDoId) + if bldg: + if bldg.elevatorNodePath is not None: + if self._enterElevatorGotElevator(): + return Task.done + return Task.cont + + def _enterElevatorGotElevator(self): + if not messenger.whoAccepts('insideVictorElevator'): + return False + messenger.send('insideVictorElevator') + return True + + def exitElevatorIn(self): + taskMgr.remove(self._eiwbTask) + + def enterElevator(self, distElevator): + base.localAvatar.cantLeaveGame = 1 + self.accept(self.elevatorDoneEvent, self.handleElevatorDone) + self.elevator = Elevator.Elevator(self.fsm.getStateNamed('elevator'), self.elevatorDoneEvent, distElevator) + self.elevator.load() + self.elevator.enter() + + def exitElevator(self): + base.localAvatar.cantLeaveGame = 0 + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + del self.elevator + + def detectedElevatorCollision(self, distElevator): + self.fsm.request('elevator', [distElevator]) + return None + + def handleElevatorDone(self, doneStatus): + self.notify.debug('handling elevator done event') + where = doneStatus['where'] + if where == 'reject': + if hasattr(base.localAvatar, 'elevatorNotifier') and base.localAvatar.elevatorNotifier.isNotifierOpen(): + pass + else: + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where in ('suitInterior', 'cogdoInterior'): + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') + + def enterTunnelIn(self, requestStatus): + self.enterZone(requestStatus['zoneId']) + BattlePlace.BattlePlace.enterTunnelIn(self, requestStatus) + + def enterTeleportIn(self, requestStatus): + teleportDebug(requestStatus, 'Street.enterTeleportIn(%s)' % (requestStatus,)) + zoneId = requestStatus['zoneId'] + self._ttfToken = self.addSetZoneCompleteCallback(Functor(self._teleportToFriend, requestStatus)) + self.enterZone(zoneId) + BattlePlace.BattlePlace.enterTeleportIn(self, requestStatus) + + def _teleportToFriend(self, requestStatus): + avId = requestStatus['avId'] + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + if avId != -1: + if avId not in base.cr.doId2do: + friend = base.cr.identifyAvatar(avId) + if friend == None: + teleportDebug(requestStatus, "couldn't find friend %s" % avId) + handle = base.cr.identifyFriend(avId) + requestStatus = {'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': hoodId, + 'shardId': None, + 'loader': 'safeZoneLoader', + 'where': 'playground', + 'avId': avId} + self.fsm.request('final') + self.__teleportOutDone(requestStatus) + return + + def exitTeleportIn(self): + self.removeSetZoneCompleteCallback(self._ttfToken) + self._ttfToken = None + BattlePlace.BattlePlace.exitTeleportIn(self) + return + + def enterTeleportOut(self, requestStatus): + if 'battle' in requestStatus: + self.__teleportOutDone(requestStatus) + else: + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + shardId = requestStatus['shardId'] + if hoodId == self.loader.hood.id and shardId == None: + if zoneId == self.zoneId: + self.fsm.request('teleportIn', [requestStatus]) + elif requestStatus['where'] == 'street' and ZoneUtil.getBranchZone(zoneId) == self.loader.branchZone: + self.fsm.request('quietZone', [requestStatus]) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + elif hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + def exitTeleportOut(self): + BattlePlace.BattlePlace.exitTeleportOut(self) + + def goHomeFailed(self, task): + self.notifyUserGoHomeFailed() + self.ignore('setLocalEstateZone') + self.doneStatus['avId'] = -1 + self.doneStatus['zoneId'] = self.getZoneId() + self.fsm.request('teleportIn', [self.doneStatus]) + return Task.done + + def renameFloorPolys(self, nodeList): + for i in nodeList: + collNodePaths = i.findAllMatches('**/+CollisionNode') + numCollNodePaths = collNodePaths.getNumPaths() + visGroupName = i.node().getName() + for j in xrange(numCollNodePaths): + collNodePath = collNodePaths.getPath(j) + bitMask = collNodePath.node().getIntoCollideMask() + if bitMask.getBit(1): + collNodePath.node().setName(visGroupName) + + def hideAllVisibles(self): + for i in self.loader.nodeList: + i.stash() + + def showAllVisibles(self): + for i in self.loader.nodeList: + i.unstash() + + def visibilityOn(self): + self.hideAllVisibles() + self.accept('on-floor', self.enterZone) + + def visibilityOff(self): + self.ignore('on-floor') + self.showAllVisibles() + + def doEnterZone(self, newZoneId): + if self.zoneId != None: + for i in self.loader.nodeDict[self.zoneId]: + if newZoneId: + if i not in self.loader.nodeDict[newZoneId]: + self.loader.fadeOutDict[i].start() + self.loader.exitAnimatedProps(i) + else: + i.stash() + self.loader.exitAnimatedProps(i) + + if newZoneId != None: + for i in self.loader.nodeDict[newZoneId]: + if self.zoneId: + if i not in self.loader.nodeDict[self.zoneId]: + self.loader.fadeInDict[i].start() + self.loader.enterAnimatedProps(i) + else: + if self.loader.fadeOutDict[i].isPlaying(): + self.loader.fadeOutDict[i].finish() + if self.loader.fadeInDict[i].isPlaying(): + self.loader.fadeInDict[i].finish() + self.loader.enterAnimatedProps(i) + i.unstash() + + if newZoneId != self.zoneId: + if visualizeZones: + if self.zoneId != None: + self.loader.zoneDict[self.zoneId].clearColor() + if newZoneId != None: + self.loader.zoneDict[newZoneId].setColor(0, 0, 1, 1, 100) + if newZoneId is not None: + loader = base.cr.playGame.getPlace().loader + if newZoneId in loader.zoneVisDict: + base.cr.sendSetZoneMsg(newZoneId, loader.zoneVisDict[newZoneId]) + else: + visList = [newZoneId] + loader.zoneVisDict.values()[0] + base.cr.sendSetZoneMsg(newZoneId, visList) + self.zoneId = newZoneId + geom = base.cr.playGame.getPlace().loader.geom + self.halloweenLights = geom.findAllMatches('**/*light*') + self.halloweenLights += geom.findAllMatches('**/*lamp*') + self.halloweenLights += geom.findAllMatches('**/prop_snow_tree*') + for light in self.halloweenLights: + light.setColorScaleOff(1) diff --git a/toontown/town/TTStreet.py b/toontown/town/TTStreet.py new file mode 100755 index 00000000..ed57a07a --- /dev/null +++ b/toontown/town/TTStreet.py @@ -0,0 +1,5 @@ +from toontown.town import Street + + +class TTStreet(Street.Street): + pass diff --git a/toontown/town/TTTownLoader.py b/toontown/town/TTTownLoader.py new file mode 100755 index 00000000..109c817e --- /dev/null +++ b/toontown/town/TTTownLoader.py @@ -0,0 +1,22 @@ +from toontown.suit import Suit +from toontown.town import TTStreet +from toontown.town import TownLoader + + +class TTTownLoader(TownLoader.TownLoader): + def __init__(self, hood, parentFSM, doneEvent): + TownLoader.TownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = TTStreet.TTStreet + self.musicFile = 'phase_3.5/audio/bgm/TC_SZ.ogg' + self.activityMusicFile = 'phase_3.5/audio/bgm/TC_SZ_activity.ogg' + self.townStorageDNAFile = 'phase_5/dna/storage_TT_town.pdna' + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadSuits(1) + dnaFile = 'phase_5/dna/toontown_central_' + str(self.canonicalBranchZone) + '.pdna' + self.createHood(dnaFile) + + def unload(self): + TownLoader.TownLoader.unload(self) + Suit.unloadSuits(1) diff --git a/toontown/town/TownBattle.py b/toontown/town/TownBattle.py new file mode 100755 index 00000000..cba7b4c0 --- /dev/null +++ b/toontown/town/TownBattle.py @@ -0,0 +1,683 @@ +from toontown.toonbase.ToontownBattleGlobals import * +import types +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import TownBattleAttackPanel +import TownBattleWaitPanel +import TownBattleChooseAvatarPanel +import TownBattleSOSPanel +import TownBattleSOSPetSearchPanel +import TownBattleSOSPetInfoPanel +import TownBattleToonPanel +import TownBattleCogPanel +from toontown.toontowngui import TTDialog +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattleBase +from direct.showbase import PythonUtil +from toontown.toonbase import TTLocalizer, ToontownGlobals, ToontownTimer +from toontown.pets import PetConstants +from direct.gui.DirectGui import * +from toontown.battle import FireCogPanel + +class TownBattle(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('TownBattle') + evenPos = (0.75, + 0.25, + -0.25, + -0.75) + oddPos = (0.5, 0, -0.5) + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.numCogs = 1 + self.creditLevel = None + self.luredIndices = [] + self.trappedIndices = [] + self.numToons = 1 + self.toons = [] + self.localNum = 0 + self.time = 0 + self.bldg = 0 + self.track = -1 + self.level = -1 + self.target = 0 + self.toonAttacks = [(-1, 0, 0)] * 4 + self.fsm = ClassicFSM.ClassicFSM('TownBattle', [ + State.State('Off', + self.enterOff, + self.exitOff, + ['Attack']), + State.State('Attack', + self.enterAttack, + self.exitAttack, + ['ChooseCog', + 'ChooseToon', + 'AttackWait', + 'Run', + 'Fire', + 'SOS']), + State.State('ChooseCog', + self.enterChooseCog, + self.exitChooseCog, + ['AttackWait', + 'Attack']), + State.State('AttackWait', + self.enterAttackWait, + self.exitAttackWait, + ['ChooseCog', + 'ChooseToon', + 'Attack']), + State.State('ChooseToon', + self.enterChooseToon, + self.exitChooseToon, + ['AttackWait', + 'Attack']), + State.State('Run', + self.enterRun, + self.exitRun, + ['Attack']), + State.State('SOS', + self.enterSOS, + self.exitSOS, + ['Attack', + 'AttackWait', + 'SOSPetSearch', + 'SOSPetInfo']), + State.State('SOSPetSearch', + self.enterSOSPetSearch, + self.exitSOSPetSearch, + ['SOS', + 'SOSPetInfo']), + State.State('SOSPetInfo', + self.enterSOSPetInfo, + self.exitSOSPetInfo, + ['SOS', + 'AttackWait']), + State.State('Fire', + self.enterFire, + self.exitFire, + ['Attack', + 'AttackWait'])], + 'Off', 'Off') + self.runPanel = TTDialog.TTDialog(dialogName='TownBattleRunPanel', text=TTLocalizer.TownBattleRun, style=TTDialog.TwoChoice, command=self.__handleRunPanelDone) + self.runPanel.hide() + self.attackPanelDoneEvent = 'attack-panel-done' + self.attackPanel = TownBattleAttackPanel.TownBattleAttackPanel(self.attackPanelDoneEvent) + self.waitPanelDoneEvent = 'wait-panel-done' + self.waitPanel = TownBattleWaitPanel.TownBattleWaitPanel(self.waitPanelDoneEvent) + self.chooseCogPanelDoneEvent = 'choose-cog-panel-done' + self.chooseCogPanel = TownBattleChooseAvatarPanel.TownBattleChooseAvatarPanel(self.chooseCogPanelDoneEvent, 0) + self.chooseToonPanelDoneEvent = 'choose-toon-panel-done' + self.chooseToonPanel = TownBattleChooseAvatarPanel.TownBattleChooseAvatarPanel(self.chooseToonPanelDoneEvent, 1) + self.SOSPanelDoneEvent = 'SOS-panel-done' + self.SOSPanel = TownBattleSOSPanel.TownBattleSOSPanel(self.SOSPanelDoneEvent) + self.SOSPetSearchPanelDoneEvent = 'SOSPetSearch-panel-done' + self.SOSPetSearchPanel = TownBattleSOSPetSearchPanel.TownBattleSOSPetSearchPanel(self.SOSPetSearchPanelDoneEvent) + self.SOSPetInfoPanelDoneEvent = 'SOSPetInfo-panel-done' + self.SOSPetInfoPanel = TownBattleSOSPetInfoPanel.TownBattleSOSPetInfoPanel(self.SOSPetInfoPanelDoneEvent) + self.fireCogPanelDoneEvent = 'fire-cog-panel-done' + self.FireCogPanel = FireCogPanel.FireCogPanel(self.fireCogPanelDoneEvent) + self.rolloverFrame = DirectFrame(aspect2d, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=(0.6, 1.0, 0.4, 1), geom_scale=(0.5, 0.3, 0.2), text_scale=0.05, text_pos=(0, 0.0125), text='', text_fg=(0, 0, 0, 1), pos=(0.4, 0, 0)) + self.rolloverFrame.setBin('gui-popup', 0) + self.rolloverFrame.hide() + self.suitGui = loader.loadModel('phase_3.5/models/gui/suit_detail_panel') + self.suitGui.find('**/avatar_panel/shadow').setColor(1, 1, 1, 0.5) + self.toonPanels = [TownBattleToonPanel.TownBattleToonPanel(self) for i in xrange(4)] + self.cogPanels = [TownBattleCogPanel.TownBattleCogPanel(self) for i in xrange(4)] + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setScale(0.4) + self.timer.hide() + + def cleanup(self): + self.ignore(self.attackPanelDoneEvent) + self.unload() + del self.fsm + self.runPanel.cleanup() + del self.runPanel + del self.attackPanel + del self.waitPanel + del self.chooseCogPanel + del self.chooseToonPanel + del self.SOSPanel + del self.FireCogPanel + del self.SOSPetSearchPanel + del self.SOSPetInfoPanel + del self.rolloverFrame + + for panel in self.toonPanels + self.cogPanels: + panel.cleanup() + + del self.toonPanels + del self.cogPanels + self.timer.destroy() + self.suitGui.removeNode() + del self.suitGui + del self.timer + del self.toons + + def enter(self, event, parentFSMState, bldg = 0, creditMultiplier = 1, tutorialFlag = 0): + self.parentFSMState = parentFSMState + self.parentFSMState.addChild(self.fsm) + if not self.isLoaded: + self.load() + self.battleEvent = event + self.fsm.enterInitialState() + base.localAvatar.laffMeter.start() + self.numToons = 1 + self.numCogs = 1 + self.toons = [base.localAvatar.doId] + self.toonPanels[0].setLaffMeter(base.localAvatar) + self.bldg = bldg + self.creditLevel = None + self.creditMultiplier = creditMultiplier + self.tutorialFlag = tutorialFlag + base.localAvatar.inventory.setBattleCreditMultiplier(self.creditMultiplier) + base.localAvatar.inventory.setActivateMode('battle', heal=0, bldg=bldg, tutorialFlag=tutorialFlag) + self.SOSPanel.bldg = bldg + + def exit(self): + base.localAvatar.laffMeter.stop() + self.parentFSMState.removeChild(self.fsm) + del self.parentFSMState + base.localAvatar.inventory.setBattleCreditMultiplier(1) + + def load(self): + if self.isLoaded: + return + self.attackPanel.load() + self.waitPanel.load() + self.chooseCogPanel.load() + self.chooseToonPanel.load() + self.SOSPanel.load() + if hasattr(base, 'wantPets') and base.wantPets: + self.SOSPetSearchPanel.load() + self.SOSPetInfoPanel.load() + self.isLoaded = 1 + + def unload(self): + if not self.isLoaded: + return + self.attackPanel.unload() + self.waitPanel.unload() + self.chooseCogPanel.unload() + self.chooseToonPanel.unload() + self.FireCogPanel.unload() + self.SOSPanel.unload() + if hasattr(base, 'wantPets') and base.wantPets: + self.SOSPetSearchPanel.unload() + self.SOSPetInfoPanel.unload() + self.isLoaded = 0 + + def setState(self, state): + if hasattr(self, 'fsm'): + self.fsm.request(state) + + def updateTimer(self, time): + self.time = time + self.timer.setTime(time) + return None + + def showRolloverFrame(self, parent, type, text, extra=None): + dict = TTLocalizer.BattleHoverAttributes[type] + + for key, value in dict.iteritems(): + if key == 'pos': + self.rolloverFrame.setPos(value) + elif key == 'suit': + if value: + self.rolloverFrame['text_font'] = ToontownGlobals.getSuitFont() + self.rolloverFrame['geom'] = self.suitGui.find('**/avatar_panel') + else: + self.rolloverFrame['text_font'] = ToontownGlobals.getInterfaceFont() + self.rolloverFrame['geom'] = DGG.getDefaultDialogGeom() + else: + self.rolloverFrame[key] = value + + self.rolloverFrame.reparentTo(parent) + self.rolloverFrame.show() + self.rolloverFrame['text'] = text + + def hideRolloverFrame(self, extra=None): + self.rolloverFrame.hide() + + def isAttackDangerous(self, hp): + for panel in self.toonPanels: + if panel.hasAvatar() and panel.avatar.getHp() <= hp: + return True + + def __enterPanels(self, num, localNum): + self.notify.debug('enterPanels() num: %d localNum: %d' % (num, localNum)) + for toonPanel in self.toonPanels: + toonPanel.hide() + toonPanel.setPos(0, 0, -0.9) + + self.positionPanels(num, self.toonPanels) + + def __enterCogPanels(self, num): + for cogPanel in self.cogPanels: + cogPanel.hide() + cogPanel.updateHealthBar() + cogPanel.updateRolloverBind() + cogPanel.setPos(0, 0, 0.62) + + self.positionPanels(num, self.cogPanels) + + def positionPanels(self, num, panels): + pos = self.evenPos if num % 2 == 0 else self.oddPos + + for i, panel in enumerate(panels): + if num > i: + panel.setX(pos[i if num >= 3 else i + 1]) + panel.show() + + def updateChosenAttacks(self, battleIndices, tracks, levels, targets): + self.notify.debug('updateChosenAttacks bi=%s tracks=%s levels=%s targets=%s' % (battleIndices, + tracks, + levels, + targets)) + for i in xrange(4): + if battleIndices[i] == -1: + pass + else: + if tracks[i] == BattleBase.NO_ATTACK: + numTargets = 0 + target = -2 + elif tracks[i] == BattleBase.PASS_ATTACK: + numTargets = 0 + target = -2 + elif tracks[i] == BattleBase.NPCSOS: + numTargets = 0 + target = targets[i] + elif tracks[i] == BattleBase.SOS or tracks[i] == BattleBase.PETSOS: + numTargets = 0 + target = -2 + elif tracks[i] == HEAL_TRACK: + numTargets = self.numToons + if self.__isGroupHeal(levels[i]): + target = -2 + else: + target = targets[i] + else: + numTargets = self.numCogs + if self.__isGroupAttack(tracks[i], levels[i]): + target = -1 + else: + target = targets[i] + if target == -1: + numTargets = None + self.toonPanels[battleIndices[i]].setValues(battleIndices[i], tracks[i], levels[i], numTargets, target, self.localNum) + + def chooseDefaultTarget(self): + if self.track > -1: + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + response['target'] = self.target + messenger.send(self.battleEvent, [response]) + return 1 + return 0 + + def updateLaffMeter(self, toonNum, hp): + self.toonPanels[toonNum].updateLaffMeter(hp) + + def enterOff(self): + if self.isLoaded: + for panel in self.toonPanels + self.cogPanels: + panel.hide() + + self.toonAttacks = [(-1, 0, 0)] * 4 + self.target = 0 + + if hasattr(self, 'timer'): + self.timer.hide() + + def exitOff(self): + if self.isLoaded: + self.__enterPanels(self.numToons, self.localNum) + if settings['cogInterface']: + self.__enterCogPanels(self.numCogs) + self.timer.show() + self.track = -1 + self.level = -1 + self.target = 0 + + def enterAttack(self): + self.attackPanel.enter() + self.accept(self.attackPanelDoneEvent, self.__handleAttackPanelDone) + + def exitAttack(self): + self.ignore(self.attackPanelDoneEvent) + self.attackPanel.exit() + + def __handleAttackPanelDone(self, doneStatus): + self.notify.debug('doneStatus: %s' % doneStatus) + mode = doneStatus['mode'] + if mode == 'Inventory': + self.track = doneStatus['track'] + self.level = doneStatus['level'] + self.toonPanels[self.localNum].setValues(self.localNum, self.track, self.level) + if self.track == HEAL_TRACK: + if self.__isGroupHeal(self.level): + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + response['target'] = self.target + messenger.send(self.battleEvent, [response]) + self.fsm.request('AttackWait') + elif self.numToons == 3 or self.numToons == 4: + self.fsm.request('ChooseToon') + elif self.numToons == 2: + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + if self.localNum == 0: + response['target'] = 1 + elif self.localNum == 1: + response['target'] = 0 + else: + self.notify.error('Bad localNum value: %s' % self.localNum) + messenger.send(self.battleEvent, [response]) + self.fsm.request('AttackWait') + else: + self.notify.error('Heal was chosen when number of toons is %s' % self.numToons) + elif self.__isCogChoiceNecessary(): + self.notify.debug('choice needed') + self.fsm.request('ChooseCog') + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + response['target'] = -1 + messenger.send(self.battleEvent, [response]) + else: + self.notify.debug('no choice needed') + self.fsm.request('AttackWait') + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + response['target'] = 0 + messenger.send(self.battleEvent, [response]) + elif mode == 'Run': + self.fsm.request('Run') + elif mode == 'SOS': + self.fsm.request('SOS') + elif mode == 'Fire': + self.fsm.request('Fire') + elif mode == 'Pass': + response = {} + response['mode'] = 'Pass' + response['id'] = -1 + messenger.send(self.battleEvent, [response]) + self.fsm.request('AttackWait') + else: + self.notify.warning('unknown mode: %s' % mode) + + def checkHealTrapLure(self): + self.notify.debug('numToons: %s, numCogs: %s, lured: %s, trapped: %s' % (self.numToons, + self.numCogs, + self.luredIndices, + self.trappedIndices)) + if len(PythonUtil.union(self.trappedIndices, self.luredIndices)) == self.numCogs: + canTrap = 0 + else: + canTrap = 1 + if len(self.luredIndices) == self.numCogs: + canLure = 0 + canTrap = 0 + else: + canLure = 1 + if self.numToons == 1: + canHeal = 0 + else: + canHeal = 1 + return (canHeal, canTrap, canLure) + + def adjustCogsAndToons(self, cogs, luredIndices, trappedIndices, toons): + numCogs = len(cogs) + self.notify.debug('adjustCogsAndToons() numCogs: %s self.numCogs: %s' % (numCogs, self.numCogs)) + self.notify.debug('adjustCogsAndToons() luredIndices: %s self.luredIndices: %s' % (luredIndices, self.luredIndices)) + self.notify.debug('adjustCogsAndToons() trappedIndices: %s self.trappedIndices: %s' % (trappedIndices, self.trappedIndices)) + toonIds = [toon.doId for toon in toons] + self.notify.debug('adjustCogsAndToons() toonIds: %s self.toons: %s' % (toonIds, self.toons)) + maxSuitLevel = 0 + for cog in cogs: + maxSuitLevel = max(maxSuitLevel, cog.getActualLevel()) + + creditLevel = maxSuitLevel + resetActivateMode = numCogs != self.numCogs or creditLevel != self.creditLevel or luredIndices != self.luredIndices or trappedIndices != self.trappedIndices or toonIds != self.toons + self.notify.debug('adjustCogsAndToons() resetActivateMode: %s' % resetActivateMode) + self.numCogs = numCogs + self.creditLevel = creditLevel + self.luredIndices = luredIndices + self.trappedIndices = trappedIndices + self.toons = toonIds + self.numToons = len(toons) + self.localNum = toons.index(base.localAvatar) + currStateName = self.fsm.getCurrentState().getName() + + if settings['cogInterface']: + self.__enterCogPanels(self.numCogs) + + for i in xrange(len(cogs)): + self.cogPanels[i].setSuit(cogs[i]) + + if resetActivateMode: + self.__enterPanels(self.numToons, self.localNum) + for i in xrange(len(toons)): + self.toonPanels[i].setLaffMeter(toons[i]) + + if currStateName == 'ChooseCog': + self.chooseCogPanel.adjustCogs(self.numCogs, self.luredIndices, self.trappedIndices, self.track) + elif currStateName == 'ChooseToon': + self.chooseToonPanel.adjustToons(self.numToons, self.localNum) + canHeal, canTrap, canLure = self.checkHealTrapLure() + base.localAvatar.inventory.setBattleCreditMultiplier(self.creditMultiplier) + base.localAvatar.inventory.setActivateMode('battle', heal=canHeal, trap=canTrap, lure=canLure, bldg=self.bldg, creditLevel=self.creditLevel, tutorialFlag=self.tutorialFlag) + + def enterChooseCog(self): + self.cog = 0 + self.chooseCogPanel.enter(self.numCogs, luredIndices=self.luredIndices, trappedIndices=self.trappedIndices, track=self.track) + self.accept(self.chooseCogPanelDoneEvent, self.__handleChooseCogPanelDone) + + def exitChooseCog(self): + self.ignore(self.chooseCogPanelDoneEvent) + self.chooseCogPanel.exit() + + def __handleChooseCogPanelDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'Back': + self.fsm.request('Attack') + elif mode == 'Avatar': + self.cog = doneStatus['avatar'] + self.target = self.cog + self.fsm.request('AttackWait') + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + response['target'] = self.cog + messenger.send(self.battleEvent, [response]) + else: + self.notify.warning('unknown mode: %s' % mode) + + def enterAttackWait(self, chosenToon = -1): + self.accept(self.waitPanelDoneEvent, self.__handleAttackWaitBack) + self.waitPanel.enter(self.numToons) + + def exitAttackWait(self): + self.waitPanel.exit() + self.ignore(self.waitPanelDoneEvent) + + def __handleAttackWaitBack(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'Back': + if self.track == HEAL_TRACK: + self.fsm.request('Attack') + elif self.track == BattleBase.NO_ATTACK: + self.fsm.request('Attack') + elif self.__isCogChoiceNecessary(): + self.fsm.request('ChooseCog') + else: + self.fsm.request('Attack') + response = {} + response['mode'] = 'UnAttack' + messenger.send(self.battleEvent, [response]) + else: + self.notify.error('unknown mode: %s' % mode) + + def enterChooseToon(self): + self.toon = 0 + self.chooseToonPanel.enter(self.numToons, localNum=self.localNum) + self.accept(self.chooseToonPanelDoneEvent, self.__handleChooseToonPanelDone) + + def exitChooseToon(self): + self.ignore(self.chooseToonPanelDoneEvent) + self.chooseToonPanel.exit() + + def __handleChooseToonPanelDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'Back': + self.fsm.request('Attack') + elif mode == 'Avatar': + self.toon = doneStatus['avatar'] + self.target = self.toon + self.fsm.request('AttackWait', [self.toon]) + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + response['target'] = self.toon + messenger.send(self.battleEvent, [response]) + else: + self.notify.warning('unknown mode: %s' % mode) + + def enterRun(self): + self.runPanel.show() + + def exitRun(self): + self.runPanel.hide() + + def __handleRunPanelDone(self, doneStatus): + if doneStatus == DGG.DIALOG_OK: + response = {} + response['mode'] = 'Run' + messenger.send(self.battleEvent, [response]) + else: + self.fsm.request('Attack') + + def enterFire(self): + canHeal, canTrap, canLure = self.checkHealTrapLure() + self.FireCogPanel.enter(self.numCogs, luredIndices=self.luredIndices, trappedIndices=self.trappedIndices, track=self.track) + self.accept(self.fireCogPanelDoneEvent, self.__handleCogFireDone) + + def exitFire(self): + self.ignore(self.fireCogPanelDoneEvent) + self.FireCogPanel.exit() + + def __handleCogFireDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'Back': + self.fsm.request('Attack') + elif mode == 'Avatar': + self.cog = doneStatus['avatar'] + self.target = self.cog + self.fsm.request('AttackWait') + response = {} + response['mode'] = 'Fire' + response['target'] = self.cog + messenger.send(self.battleEvent, [response]) + else: + self.notify.warning('unknown mode: %s' % mode) + + def enterSOS(self): + canHeal, canTrap, canLure = self.checkHealTrapLure() + self.SOSPanel.enter(canLure, canTrap) + self.accept(self.SOSPanelDoneEvent, self.__handleSOSPanelDone) + + def exitSOS(self): + self.ignore(self.SOSPanelDoneEvent) + self.SOSPanel.exit() + + def __handleSOSPanelDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'Friend': + doId = doneStatus['friend'] + response = {} + response['mode'] = 'SOS' + response['id'] = doId + messenger.send(self.battleEvent, [response]) + self.fsm.request('AttackWait') + elif mode == 'Pet': + self.petId = doneStatus['petId'] + self.petName = doneStatus['petName'] + self.fsm.request('SOSPetSearch') + elif mode == 'NPCFriend': + doId = doneStatus['friend'] + response = {} + response['mode'] = 'NPCSOS' + response['id'] = doId + messenger.send(self.battleEvent, [response]) + self.fsm.request('AttackWait') + elif mode == 'Back': + self.fsm.request('Attack') + + def enterSOSPetSearch(self): + response = {} + response['mode'] = 'PETSOSINFO' + response['id'] = self.petId + self.SOSPetSearchPanel.enter(self.petId, self.petName) + self.proxyGenerateMessage = 'petProxy-%d-generated' % self.petId + self.accept(self.proxyGenerateMessage, self.__handleProxyGenerated) + self.accept(self.SOSPetSearchPanelDoneEvent, self.__handleSOSPetSearchPanelDone) + messenger.send(self.battleEvent, [response]) + + def exitSOSPetSearch(self): + self.ignore(self.proxyGenerateMessage) + self.ignore(self.SOSPetSearchPanelDoneEvent) + self.SOSPetSearchPanel.exit() + + def __handleSOSPetSearchPanelDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'Back': + self.fsm.request('SOS') + else: + self.notify.error('invalid mode in handleSOSPetSearchPanelDone') + + def __handleProxyGenerated(self): + self.fsm.request('SOSPetInfo') + + def enterSOSPetInfo(self): + self.SOSPetInfoPanel.enter(self.petId) + self.accept(self.SOSPetInfoPanelDoneEvent, self.__handleSOSPetInfoPanelDone) + + def exitSOSPetInfo(self): + self.ignore(self.SOSPetInfoPanelDoneEvent) + self.SOSPetInfoPanel.exit() + + def __handleSOSPetInfoPanelDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'OK': + response = {} + response['mode'] = 'PETSOS' + response['id'] = self.petId + response['trickId'] = doneStatus['trickId'] + messenger.send(self.battleEvent, [response]) + self.fsm.request('AttackWait') + bboard.post(PetConstants.OurPetsMoodChangedKey, True) + elif mode == 'Back': + self.fsm.request('SOS') + + def __isCogChoiceNecessary(self): + return self.numCogs > 1 and not self.__isGroupAttack(self.track, self.level) + + def __isGroupAttack(self, trackNum, levelNum): + return BattleBase.attackAffectsGroup(trackNum, levelNum) + + def __isGroupHeal(self, levelNum): + return self.__isGroupAttack(HEAL_TRACK, levelNum) diff --git a/toontown/town/TownBattleAttackPanel.py b/toontown/town/TownBattleAttackPanel.py new file mode 100755 index 00000000..50deae10 --- /dev/null +++ b/toontown/town/TownBattleAttackPanel.py @@ -0,0 +1,77 @@ +from panda3d.core import * +from direct.directnotify import DirectNotifyGlobal +import string +from direct.fsm import StateData +AttackPanelHidden = 0 + +def hideAttackPanel(flag): + global AttackPanelHidden + AttackPanelHidden = flag + messenger.send('hide-attack-panel') + + +class TownBattleAttackPanel(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('TownBattleAttackPanel') + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + + def load(self): + StateData.StateData.load(self) + + def unload(self): + StateData.StateData.unload(self) + + def enter(self): + StateData.StateData.enter(self) + if not AttackPanelHidden: + base.localAvatar.inventory.show() + self.accept('inventory-selection', self.__handleInventory) + self.accept('inventory-run', self.__handleRun) + self.accept('inventory-sos', self.__handleSOS) + self.accept('inventory-pass', self.__handlePass) + self.accept('inventory-fire', self.__handleFire) + self.accept('hide-attack-panel', self.__handleHide) + return + + def exit(self): + StateData.StateData.exit(self) + self.ignore('inventory-selection') + self.ignore('inventory-run') + self.ignore('inventory-sos') + self.ignore('inventory-pass') + self.ignore('inventory-fire') + self.ignore('hide-attack-panel') + base.localAvatar.inventory.hide() + + def __handleRun(self): + doneStatus = {'mode': 'Run'} + messenger.send(self.doneEvent, [doneStatus]) + + def __handleSOS(self): + doneStatus = {'mode': 'SOS'} + messenger.send(self.doneEvent, [doneStatus]) + + def __handlePass(self): + doneStatus = {'mode': 'Pass'} + messenger.send(self.doneEvent, [doneStatus]) + + def __handleFire(self): + doneStatus = {'mode': 'Fire'} + messenger.send(self.doneEvent, [doneStatus]) + + def __handleInventory(self, track, level): + if base.localAvatar.inventory.numItem(track, level) > 0: + doneStatus = {} + doneStatus['mode'] = 'Inventory' + doneStatus['track'] = track + doneStatus['level'] = level + messenger.send(self.doneEvent, [doneStatus]) + else: + self.notify.error("An item we don't have: track %s level %s was selected." % [track, level]) + + def __handleHide(self): + if AttackPanelHidden: + base.localAvatar.inventory.hide() + else: + base.localAvatar.inventory.show() diff --git a/toontown/town/TownBattleChooseAvatarPanel.py b/toontown/town/TownBattleChooseAvatarPanel.py new file mode 100755 index 00000000..fd75bf1d --- /dev/null +++ b/toontown/town/TownBattleChooseAvatarPanel.py @@ -0,0 +1,114 @@ +from toontown.toonbase.ToontownBattleGlobals import * +from toontown.toonbase import ToontownGlobals +from direct.fsm import StateData +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattleBase +from direct.gui.DirectGui import * +from panda3d.core import * +from toontown.toonbase import TTLocalizer + +class TownBattleChooseAvatarPanel(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('ChooseAvatarPanel') + + def __init__(self, doneEvent, toon): + self.notify.debug('Init choose panel...') + StateData.StateData.__init__(self, doneEvent) + self.numAvatars = 0 + self.chosenAvatar = 0 + self.toon = toon + + def load(self): + gui = loader.loadModel('phase_3.5/models/gui/battle_gui') + self.frame = DirectFrame(relief=None, image=gui.find('**/BtlPick_TAB'), image_color=Vec4(1, 0.2, 0.2, 1)) + self.frame.hide() + self.statusFrame = DirectFrame(parent=self.frame, relief=None, image=gui.find('**/ToonBtl_Status_BG'), image_color=Vec4(0.5, 0.9, 0.5, 1), pos=(0.611, 0, 0)) + self.textFrame = DirectFrame(parent=self.frame, relief=None, image=gui.find('**/PckMn_Select_Tab'), image_color=Vec4(1, 1, 0, 1), text='', text_fg=Vec4(0, 0, 0, 1), text_pos=(0, -0.025, 0), text_scale=0.08, pos=(-0.013, 0, 0.013)) + if self.toon: + self.textFrame['text'] = TTLocalizer.TownBattleChooseAvatarToonTitle + else: + self.textFrame['text'] = TTLocalizer.TownBattleChooseAvatarCogTitle + self.avatarButtons = [] + for i in xrange(4): + button = DirectButton(parent=self.frame, relief=None, image=(gui.find('**/PckMn_Arrow_Up'), gui.find('**/PckMn_Arrow_Dn'), gui.find('**/PckMn_Arrow_Rlvr')), command=self.__handleAvatar, extraArgs=[i]) + if self.toon: + button.setScale(1, 1, -1) + button.setPos(0, 0, -0.2) + else: + button.setScale(1, 1, 1) + button.setPos(0, 0, 0.2) + self.avatarButtons.append(button) + + self.backButton = DirectButton(parent=self.frame, relief=None, image=(gui.find('**/PckMn_BackBtn'), gui.find('**/PckMn_BackBtn_Dn'), gui.find('**/PckMn_BackBtn_Rlvr')), pos=(-0.647, 0, 0.006), scale=1.05, text=TTLocalizer.TownBattleChooseAvatarBack, text_scale=0.05, text_pos=(0.01, -0.012), text_fg=Vec4(0, 0, 0.8, 1), command=self.__handleBack) + gui.removeNode() + return + + def unload(self): + self.frame.destroy() + del self.frame + del self.statusFrame + del self.textFrame + del self.avatarButtons + del self.backButton + + def enter(self, numAvatars, localNum = None, luredIndices = None, trappedIndices = None, track = None): + self.frame.show() + invalidTargets = [] + if not self.toon: + if len(luredIndices) > 0: + if track == BattleBase.TRAP or track == BattleBase.LURE: + invalidTargets += luredIndices + if len(trappedIndices) > 0: + if track == BattleBase.TRAP: + invalidTargets += trappedIndices + self.__placeButtons(numAvatars, invalidTargets, localNum) + + def exit(self): + self.frame.hide() + + def __handleBack(self): + doneStatus = {'mode': 'Back'} + messenger.send(self.doneEvent, [doneStatus]) + + def __handleAvatar(self, avatar): + doneStatus = {'mode': 'Avatar', + 'avatar': avatar} + messenger.send(self.doneEvent, [doneStatus]) + + def adjustCogs(self, numAvatars, luredIndices, trappedIndices, track): + invalidTargets = [] + if len(luredIndices) > 0: + if track == BattleBase.TRAP or track == BattleBase.LURE: + invalidTargets += luredIndices + if len(trappedIndices) > 0: + if track == BattleBase.TRAP: + invalidTargets += trappedIndices + self.__placeButtons(numAvatars, invalidTargets, None) + return + + def adjustToons(self, numToons, localNum): + self.__placeButtons(numToons, [], localNum) + + def __placeButtons(self, numAvatars, invalidTargets, localNum): + for i in xrange(4): + if numAvatars > i and i not in invalidTargets and i != localNum: + self.avatarButtons[i].show() + else: + self.avatarButtons[i].hide() + + if numAvatars == 1: + self.avatarButtons[0].setX(0) + elif numAvatars == 2: + self.avatarButtons[0].setX(0.2) + self.avatarButtons[1].setX(-0.2) + elif numAvatars == 3: + self.avatarButtons[0].setX(0.4) + self.avatarButtons[1].setX(0.0) + self.avatarButtons[2].setX(-0.4) + elif numAvatars == 4: + self.avatarButtons[0].setX(0.6) + self.avatarButtons[1].setX(0.2) + self.avatarButtons[2].setX(-0.2) + self.avatarButtons[3].setX(-0.6) + else: + self.notify.error('Invalid number of avatars: %s' % numAvatars) + return None diff --git a/toontown/town/TownBattleCogPanel.py b/toontown/town/TownBattleCogPanel.py new file mode 100755 index 00000000..8335ac2b --- /dev/null +++ b/toontown/town/TownBattleCogPanel.py @@ -0,0 +1,94 @@ +from direct.gui.DirectGui import * +from toontown.battle import SuitBattleGlobals +from toontown.suit import Suit, SuitHealthBar +from toontown.toonbase import TTLocalizer + +class TownBattleCogPanel(DirectFrame): + + def __init__(self, battle): + gui = loader.loadModel('phase_3.5/models/gui/battle_gui') + DirectFrame.__init__(self, relief=None, image=gui.find('**/ToonBtl_Status_BG'), image_color=(0.86, 0.86, 0.86, 0.7), scale=0.8) + self.initialiseoptions(TownBattleCogPanel) + self.battle = battle + self.levelText = DirectLabel(parent=self, text='', pos=(-0.06, 0, -0.075), text_scale=0.055) + self.typeText = DirectLabel(parent=self, text='', pos=(0.12, 0, -0.075), text_scale=0.045) + self.healthBar = SuitHealthBar.SuitHealthBar() + self.generateHealthBar() + self.hoverButton = DirectButton(parent=self, relief=None, image_scale=(0.07, 0, 0.06), pos=(0.105, 0, 0.05), image='phase_3/maps/invisible.png', pressEffect=0) + self.hoverButton.setTransparency(True) + self.hoverButton.bind(DGG.EXIT, self.battle.hideRolloverFrame) + self.suit = None + self.suitHead = None + self.hide() + gui.removeNode() + + def cleanup(self): + self.cleanupHead() + self.levelText.removeNode() + self.typeText.removeNode() + self.healthBar.delete() + self.hoverButton.removeNode() + del self.levelText + del self.typeText + del self.healthBar + del self.hoverButton + DirectFrame.destroy(self) + + def cleanupHead(self): + if self.suitHead: + self.suitHead.removeNode() + del self.suitHead + + def setSuit(self, suit): + if self.suit == suit: + return + + self.cleanupHead() + self.suit = suit + self.generateSuitHead(suit.getStyleName()) + self.updateHealthBar() + self.levelText['text'] = TTLocalizer.CogPanelLevel % suit.getActualLevel() + self.typeText['text'] = suit.getTypeText() + self.updateRolloverBind() + + def updateRolloverBind(self): + if not self.suit: + return + + attributes = SuitBattleGlobals.SuitAttributes[self.suit.getStyleName()] + groupAttacks, singleAttacks = SuitBattleGlobals.getAttacksByType(attributes) + level = self.suit.getLevel() + info = TTLocalizer.BattleCogPopup % (self.getAttackStrings(groupAttacks, level), self.getAttackStrings(singleAttacks, level)) + + if TTLocalizer.BattleCogPopupDangerColor in info: + info = TTLocalizer.BattleCogPopupDanger + info + + self.hoverButton.bind(DGG.ENTER, self.battle.showRolloverFrame, extraArgs=[self, TTLocalizer.BattleHoverCog, info]) + + def getAttackStrings(self, attacks, level): + attackStrings = [] + + for attack in attacks: + hp = attack[1][level] + attackString = TTLocalizer.BattleCogPopupAttackDanger if self.battle.isAttackDangerous(hp) else TTLocalizer.BattleCogPopupAttack + attackStrings.append(attackString % (TTLocalizer.SuitAttackNames[attack[0]], hp)) + + return '\n'.join(attackStrings) if attackStrings else TTLocalizer.SuitPageNoAttacks + + def generateSuitHead(self, name): + self.suitHead = Suit.attachSuitHead(self, name) + self.suitHead.setScale(0.05) + self.suitHead.setPos(0.1, 0, 0.01) + + def generateHealthBar(self): + self.healthBar.generate() + self.healthBar.geom.reparentTo(self) + self.healthBar.geom.setScale(0.5) + self.healthBar.geom.setPos(-0.065, 0, 0.05) + self.healthBar.geom.show() + + def updateHealthBar(self): + if not self.suit: + return + + self.healthBar.update(float(self.suit.getHP()) / float(self.suit.getMaxHP())) \ No newline at end of file diff --git a/toontown/town/TownBattleSOSPanel.py b/toontown/town/TownBattleSOSPanel.py new file mode 100755 index 00000000..e6063f3a --- /dev/null +++ b/toontown/town/TownBattleSOSPanel.py @@ -0,0 +1,217 @@ +from panda3d.core import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import types +from toontown.toon import NPCToons +from toontown.toon import NPCFriendPanel +from toontown.toonbase import ToontownBattleGlobals + +class TownBattleSOSPanel(DirectFrame, StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('TownBattleSOSPanel') + + def __init__(self, doneEvent): + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(TownBattleSOSPanel) + StateData.StateData.__init__(self, doneEvent) + self.friends = {} + self.NPCFriends = {} + self.textRolloverColor = Vec4(1, 1, 0, 1) + self.textDownColor = Vec4(0.5, 0.9, 1, 1) + self.textDisabledColor = Vec4(0.4, 0.8, 0.4, 1) + self.bldg = 0 + self.chosenNPCToons = [] + return + + def load(self): + if self.isLoaded == 1: + return None + self.isLoaded = 1 + bgd = loader.loadModel('phase_3.5/models/gui/frame') + gui = loader.loadModel('phase_3.5/models/gui/frame4names') + scrollGui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + backGui = loader.loadModel('phase_3.5/models/gui/battle_gui') + self['image'] = bgd + self['image_pos'] = (0.0, 0.1, -0.08) + self.setScale(0.3) + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.TownBattleSOSNoFriends, text_scale=0.4, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), pos=(0.0, 0.0, 1.5)) + self.NPCFriendPanel = NPCFriendPanel.NPCFriendPanel(parent=self, callable=True, doneEvent=self.doneEvent) + self.NPCFriendPanel.setPos(-0.75, 0, -0.15) + self.NPCFriendPanel.setScale(0.325) + self.NPCFriendsLabel = DirectLabel(parent=self, relief=None, text=TTLocalizer.TownBattleSOSNPCFriends, text_scale=0.3, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), pos=(-0.75, 0.0, -2.0)) + self.scrollList = DirectScrolledList(parent=self, relief=None, image=gui.find('**/frame4names'), image_scale=(0.11, 1, 0.1), text=TTLocalizer.FriendsListPanelOnlineFriends, text_scale=0.04, text_pos=(-0.02, 0.275), text_fg=(0, 0, 0, 1), incButton_image=(scrollGui.find('**/FndsLst_ScrollUp'), + scrollGui.find('**/FndsLst_ScrollDN'), + scrollGui.find('**/FndsLst_ScrollUp_Rllvr'), + scrollGui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_pos=(0.0, 0.0, -0.3), incButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), incButton_scale=(1.0, 1.0, -1.0), decButton_image=(scrollGui.find('**/FndsLst_ScrollUp'), + scrollGui.find('**/FndsLst_ScrollDN'), + scrollGui.find('**/FndsLst_ScrollUp_Rllvr'), + scrollGui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_pos=(0.0, 0.0, 0.175), decButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), itemFrame_pos=(-0.17, 0.0, 0.11), itemFrame_relief=None, numItemsVisible=9, items=[], pos=(2.4, 0.0, 0.025), scale=3.5) + clipper = PlaneNode('clipper') + clipper.setPlane(Plane(Vec3(-1, 0, 0), Point3(0.32, 0, 0))) + clipNP = self.scrollList.component('itemFrame').attachNewNode(clipper) + self.scrollList.component('itemFrame').setClipPlane(clipNP) + self.close = DirectButton(parent=self, relief=None, image=(backGui.find('**/PckMn_BackBtn'), backGui.find('**/PckMn_BackBtn_Dn'), backGui.find('**/PckMn_BackBtn_Rlvr')), pos=(2.3, 0.0, -1.65), scale=3, text=TTLocalizer.TownBattleSOSBack, text_scale=0.05, text_pos=(0.01, -0.012), text_fg=Vec4(0, 0, 0.8, 1), command=self.__close) + gui.removeNode() + scrollGui.removeNode() + backGui.removeNode() + bgd.removeNode() + self.hide() + return + + def unload(self): + if self.isLoaded == 0: + return None + self.isLoaded = 0 + self.exit() + del self.title + del self.scrollList + del self.close + del self.friends + del self.NPCFriends + DirectFrame.destroy(self) + return None + + def makeFriendButton(self, friendId): + handle = base.cr.identifyFriend(friendId) + + if not handle: + base.cr.fillUpFriendsMap() + return + + friendName = handle.getName() + + if handle.isPet(): + com = self.__chosePet + else: + com = self.__choseFriend + + return DirectButton(relief=None, text=friendName, text_scale=0.04, text_align=TextNode.ALeft, text_fg=(0.0, 0.0, 0.0, 1.0), text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, text3_fg=self.textDisabledColor, command=com, extraArgs=[friendId, friendName]) + + def makeNPCFriendButton(self, NPCFriendId, numCalls): + if NPCFriendId not in TTLocalizer.NPCToonNames: + return None + friendName = TTLocalizer.NPCToonNames[NPCFriendId] + friendName += ' %d' % numCalls + fg = Vec4(0.0, 0.0, 0.0, 1.0) + return DirectButton(relief=None, text=friendName, text_scale=0.04, text_align=TextNode.ALeft, text_fg=fg, text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, text3_fg=self.textDisabledColor, command=self.__choseNPCFriend, extraArgs=[NPCFriendId]) + + def enter(self, canLure = 1, canTrap = 1): + if self.isEntered == 1: + return None + self.isEntered = 1 + if self.isLoaded == 0: + self.load() + self.canLure = canLure + self.canTrap = canTrap + self.factoryToonIdList = None + messenger.send('SOSPanelEnter', [self]) + self.__updateScrollList() + self.__updateNPCFriendsPanel() + self.__updateTitleText() + self.show() + self.accept('friendOnline', self.__friendOnline) + self.accept('friendOffline', self.__friendOffline) + self.accept('friendsListChanged', self.__friendsListChanged) + self.accept('friendsMapComplete', self.__friendsListChanged) + return + + def exit(self): + if self.isEntered == 0: + return None + self.isEntered = 0 + self.hide() + self.ignore('friendOnline') + self.ignore('friendOffline') + self.ignore('friendsListChanged') + self.ignore('friendsMapComplete') + messenger.send(self.doneEvent) + return None + + def __close(self): + doneStatus = {} + doneStatus['mode'] = 'Back' + messenger.send(self.doneEvent, [doneStatus]) + + def __choseFriend(self, friendId, friendName): + doneStatus = {} + doneStatus['mode'] = 'Friend' + doneStatus['friend'] = friendId + messenger.send(self.doneEvent, [doneStatus]) + + def __chosePet(self, petId, petName): + doneStatus = {} + doneStatus['mode'] = 'Pet' + doneStatus['petId'] = petId + doneStatus['petName'] = petName + messenger.send(self.doneEvent, [doneStatus]) + + def __choseNPCFriend(self, friendId): + doneStatus = {} + doneStatus['mode'] = 'NPCFriend' + doneStatus['friend'] = friendId + self.chosenNPCToons.append(friendId) + messenger.send(self.doneEvent, [doneStatus]) + + def setFactoryToonIdList(self, toonIdList): + self.factoryToonIdList = toonIdList[:] + + def __updateScrollList(self): + friends = [] + + if base.wantPets and config.GetBool('want-pets-in-battle', 1) and base.localAvatar.hasPet(): + friends.append(base.localAvatar.getPetId()) + if not self.bldg or self.factoryToonIdList is not None: + for friendId in base.localAvatar.friendsList: + if base.cr.isFriendOnline(friendId): + if self.factoryToonIdList is None or friendId in self.factoryToonIdList: + friends.append(friendId) + + for friendId in self.friends.keys(): + if friendId not in friends: + friendButton = self.friends[friendId] + self.scrollList.removeItem(friendButton) + if not friendButton.isEmpty(): + friendButton.destroy() + del self.friends[friendId] + + for friendId in friends: + if friendId not in self.friends: + friendButton = self.makeFriendButton(friendId) + if friendButton: + self.scrollList.addItem(friendButton) + self.friends[friendId] = friendButton + + def __updateNPCFriendsPanel(self): + self.NPCFriends = {} + for friend, count in base.localAvatar.NPCFriendsDict.items(): + track = NPCToons.getNPCTrack(friend) + if track == ToontownBattleGlobals.LURE_TRACK and self.canLure == 0 or track == ToontownBattleGlobals.TRAP_TRACK and self.canTrap == 0: + self.NPCFriends[friend] = 0 + else: + self.NPCFriends[friend] = count + + self.NPCFriendPanel.setFriends(self.NPCFriends) + self.NPCFriendPanel.update() + + def __updateTitleText(self): + isEmpty = (len(self.friends) == 0 and len(self.NPCFriends) == 0) + if isEmpty: + self.title['text'] = TTLocalizer.TownBattleSOSNoFriends + else: + self.title['text'] = TTLocalizer.TownBattleSOSWhichFriend + + def __friendOnline(self, doId): + self.__updateScrollList() + self.__updateTitleText() + + def __friendOffline(self, doId): + self.__updateScrollList() + self.__updateTitleText() + + def __friendsListChanged(self): + self.__updateScrollList() + self.__updateTitleText() diff --git a/toontown/town/TownBattleSOSPetInfoPanel.py b/toontown/town/TownBattleSOSPetInfoPanel.py new file mode 100755 index 00000000..53898140 --- /dev/null +++ b/toontown/town/TownBattleSOSPetInfoPanel.py @@ -0,0 +1,160 @@ +from panda3d.core import * +from direct.fsm import StateData +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from toontown.pets import Pet, PetTricks, PetDetailPanel +from toontown.speedchat import TTSCPetTrickMenu +from otp.speedchat import SpeedChatGlobals, SCSettings +from otp.otpbase import OTPLocalizer + +class TownBattleSOSPetInfoPanel(StateData.StateData): + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + + def load(self): + gui = loader.loadModel('phase_3.5/models/gui/PetControlPannel') + guiScale = 0.116 + guiPos = (0, 0, 0) + self.frame = DirectFrame(image=gui, scale=guiScale, pos=guiPos, relief=None) + self.frame.hide() + disabledImageColor = Vec4(0.6, 0.6, 0.6, 1) + text0Color = Vec4(1, 1, 1, 1) + text1Color = Vec4(0.5, 1, 0.5, 1) + text2Color = Vec4(1, 1, 0.5, 1) + text3Color = Vec4(0.6, 0.6, 0.6, 1) + self.closeButton = DirectButton(parent=self.frame, image=(gui.find('**/CancelButtonUp'), gui.find('**/CancelButtonDown'), gui.find('**/CancelButtonRollover')), relief=None, command=self.__handleClose) + self.feedButton = DirectButton(parent=self.frame, image=(gui.find('**/ButtonFeedUp'), + gui.find('**/ButtonFeedDown'), + gui.find('**/ButtonFeedRollover'), + gui.find('**/ButtonFeedUp')), geom=gui.find('**/PetControlFeedIcon'), image3_color=disabledImageColor, relief=None, text=TTLocalizer.PetPanelFeed, text_scale=0.5, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, text_pos=(-0.5, 2.8), text_align=TextNode.ALeft) + self.feedButton['state'] = DGG.DISABLED + self.callButton = DirectButton(parent=self.frame, image=(gui.find('**/ButtonGoToUp'), + gui.find('**/ButtonGoToDown'), + gui.find('**/ButtonGoToRollover'), + gui.find('**/ButtonGoToUp')), geom=gui.find('**/PetControlGoToIcon'), image3_color=disabledImageColor, relief=None, text=TTLocalizer.PetPanelCall, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, text_scale=0.5, text_pos=(-0.5, 1.3), text_align=TextNode.ALeft) + self.callButton['state'] = DGG.DISABLED + self.scratchButton = DirectButton(parent=self.frame, image=(gui.find('**/ButtonScratchUp'), + gui.find('**/ButtonScratchDown'), + gui.find('**/ButtonScratchRollover'), + gui.find('**/ButtonScratchUp')), geom=gui.find('**/PetControlScratchIcon'), image3_color=disabledImageColor, relief=None, text=TTLocalizer.PetPanelScratch, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, text_scale=0.5, text_pos=(-0.5, 2.05), text_align=TextNode.ALeft) + self.scratchButton['state'] = DGG.DISABLED + self.callOwnerButton = DirectButton(parent=self.frame, image=(gui.find('**/PetControlToonButtonUp'), gui.find('**/PetControlToonButtonDown'), gui.find('**/PetControlToonButtonRollover')), geom=gui.find('**/PetControlToonIcon'), geom3_color=disabledImageColor, relief=None, image3_color=disabledImageColor, text=('', + TTLocalizer.PetPanelOwner, + TTLocalizer.PetPanelOwner, + ''), text_fg=text2Color, text_shadow=(0, 0, 0, 1), text_scale=0.35, text_pos=(0.3, 1.1), text_align=TextNode.ACenter, command=self.__handleDetail) + self.callOwnerButton['state'] = DGG.DISABLED + self.detailButton = DirectButton(parent=self.frame, image=(gui.find('**/PetControlToonButtonUp1'), gui.find('**/PetControlToonButtonDown1'), gui.find('**/PetControlToonButtonRollover1')), geom=gui.find('**/PetBattleIcon'), geom3_color=disabledImageColor, relief=None, pos=(0, 0, 0), image3_color=disabledImageColor, text=('', + TTLocalizer.PetPanelDetail, + TTLocalizer.PetPanelDetail, + ''), text_fg=text2Color, text_shadow=(0, 0, 0, 1), text_scale=0.35, text_pos=(0.3, 1.1), text_align=TextNode.ACenter, command=self.__handleDetail) + self.detailButton['state'] = DGG.NORMAL + gui.removeNode() + self.nameLabel = None + self.trickMenu = TTSCPetTrickMenu.TTSCPetTrickMenu() + self.settings = SCSettings.SCSettings(eventPrefix='') + self.trickMenu.privSetSettingsRef(self.settings) + self.trickMenuEventName = self.trickMenu.getEventName(SpeedChatGlobals.SCStaticTextMsgEvent) + self.trickMenu.setScale(0.055) + self.trickMenu.setBin('gui-popup', 0) + self.trickMenu.finalizeAll() + localAvatar.chatMgr.chatInputSpeedChat.whisperAvatarId = None + self.petDetailPanel = None + return + + def unload(self): + self.frame.destroy() + del self.frame + self.frame = None + if hasattr(self, 'petView'): + self.petView.removeNode() + del self.petView + if hasattr(self, 'petModel'): + self.petModel.delete() + del self.petModel + del self.closeButton + del self.feedButton + del self.callButton + del self.scratchButton + del self.callOwnerButton + del self.detailButton + self.trickMenu.destroy() + del self.trickMenu + del self.petDetailPanel + return + + def enter(self, petProxyId): + self.petProxyId = petProxyId + if petProxyId not in base.cr.doId2do: + self.notify.warning('petProxyId %s not in doId2do!' % petProxyId) + return + self.petProxy = base.cr.doId2do[petProxyId] + self.__fillPetInfo(self.petProxy) + self.frame.show() + self.accept(self.trickMenuEventName, self.__handleTrickMenuEvent) + self.trickMenu.reparentTo(aspect2dp, DGG.FOREGROUND_SORT_INDEX) + localAvatar.chatMgr.chatInputSpeedChat.whisperAvatarId = None + self.detailButton['state'] = DGG.NORMAL + return + + def exit(self): + self.ignore(self.trickMenuEventName) + self.trickMenu.reparentTo(hidden) + self.petProxy = None + if self.petDetailPanel != None: + self.petDetailPanel.cleanup() + self.petDetailPanel = None + self.frame.hide() + return + + def __handleTrickMenuEvent(self, textId): + if textId in PetTricks.ScId2trickId: + trickId = PetTricks.ScId2trickId[textId] + doneStatus = {'mode': 'OK', + 'trickId': trickId} + messenger.send(self.doneEvent, [doneStatus]) + self.detailButton['state'] = DGG.NORMAL + + def __handleClose(self): + doneStatus = {'mode': 'Back'} + messenger.send(self.doneEvent, [doneStatus]) + + def __handleCall(self): + doneStatus = {'mode': 'OK', + 'trickId': 0} + messenger.send(self.doneEvent, [doneStatus]) + + def __handleDetailDone(self): + if self.petDetailPanel != None: + self.petDetailPanel.cleanup() + self.petDetailPanel = None + self.detailButton['state'] = DGG.NORMAL + return + + def __handleDetail(self): + self.petDetailPanel = PetDetailPanel.PetDetailPanel(pet=self.petProxy, closeCallback=self.__handleDetailDone, parent=self.frame) + self.detailButton['state'] = DGG.DISABLED + + def __fillPetInfo(self, avatar): + self.notify.debug('__fillPetInfo(): doId=%s' % avatar.doId) + if self.nameLabel == None: + self.petView = self.frame.attachNewNode('petView') + self.petView.setPos(0, 0, 5.4) + self.petModel = Pet.Pet(forGui=1) + self.petModel.setDNA(avatar.getDNA()) + self.petModel.fitAndCenterHead(3.575, forGui=1) + self.petModel.reparentTo(self.petView) + self.petModel.enterNeutralHappy() + self.petModel.startBlink() + self.petModel.setScale(0.75) + self.nameLabel = DirectLabel(parent=self.frame, pos=(0, 0, 5.2), relief=None, text=avatar.getName(), text_font=avatar.getFont(), text_fg=Vec4(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.4, text_wordwrap=7.5, text_shadow=(1, 1, 1, 1)) + self.stateLabel = DirectLabel(parent=self.frame, pos=(0.7, 0, 3.5), relief=None, text='', text_font=avatar.getFont(), text_fg=Vec4(0, 0, 0, 1), text_scale=0.4, text_wordwrap=7.5, text_shadow=(1, 1, 1, 1)) + self.__refreshPetInfo(avatar) + return + + def __refreshPetInfo(self, avatar): + self.notify.debug('__refreshPetInfo(): doId=%s' % avatar.doId) + avatar.updateOfflineMood() + mood = avatar.getDominantMood() + self.stateLabel['text'] = TTLocalizer.PetMoodAdjectives[mood] + self.nameLabel['text'] = avatar.getName() diff --git a/toontown/town/TownBattleSOSPetSearchPanel.py b/toontown/town/TownBattleSOSPetSearchPanel.py new file mode 100755 index 00000000..7e6f2f15 --- /dev/null +++ b/toontown/town/TownBattleSOSPetSearchPanel.py @@ -0,0 +1,37 @@ +from panda3d.core import * +from direct.fsm import StateData +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer + +class TownBattleSOSPetSearchPanel(StateData.StateData): + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + + def load(self): + gui = loader.loadModel('phase_3.5/models/gui/battle_gui') + self.frame = DirectFrame(relief=None, image=gui.find('**/Waiting4Others'), text_align=TextNode.ALeft, pos=(0, 0, 0), scale=0.65) + self.frame.hide() + self.backButton = DirectButton(parent=self.frame, relief=None, image=(gui.find('**/PckMn_BackBtn'), gui.find('**/PckMn_BackBtn_Dn'), gui.find('**/PckMn_BackBtn_Rlvr')), pos=(-0.647, 0, -0.011), scale=1.05, text=TTLocalizer.TownBattleWaitBack, text_scale=0.05, text_pos=(0.01, -0.012), text_fg=Vec4(0, 0, 0.8, 1), command=self.__handleBack) + gui.removeNode() + return + + def unload(self): + self.frame.destroy() + del self.frame + del self.backButton + + def enter(self, petId, petName): + self.petId = petId + self.petName = petName + self.frame['text'] = TTLocalizer.TownBattleSOSPetSearchTitle % petName + self.frame['text_pos'] = (0, 0.01, 0) + self.frame['text_scale'] = TTLocalizer.TBSOSPSPenter + self.frame.show() + + def exit(self): + self.frame.hide() + + def __handleBack(self): + doneStatus = {'mode': 'Back'} + messenger.send(self.doneEvent, [doneStatus]) diff --git a/toontown/town/TownBattleToonPanel.py b/toontown/town/TownBattleToonPanel.py new file mode 100755 index 00000000..03c7ba6b --- /dev/null +++ b/toontown/town/TownBattleToonPanel.py @@ -0,0 +1,213 @@ +from panda3d.core import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase.ToontownBattleGlobals import * +from direct.directnotify import DirectNotifyGlobal +import string +from toontown.toon import LaffMeter, NPCToons +from toontown.battle import BattleBase +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from toontown.toon.NPCFriendPanel import createNPCToonHead + +class TownBattleToonPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('TownBattleToonPanel') + sosTracks = Tracks + NPCTracks + + def __init__(self, battle): + gui = loader.loadModel('phase_3.5/models/gui/battle_gui') + DirectFrame.__init__(self, relief=None, image=gui.find('**/ToonBtl_Status_BG'), image_color=Vec4(0.5, 0.9, 0.5, 0.7)) + self.setScale(0.8) + self.initialiseoptions(TownBattleToonPanel) + self.battle = battle + self.avatar = None + self.sosText = DirectLabel(parent=self, relief=None, pos=(0.1, 0, 0.015), text=TTLocalizer.TownBattleToonSOS, text_scale=0.06) + self.sosText.hide() + self.fireText = DirectLabel(parent=self, relief=None, pos=(0.1, 0, 0.015), text=TTLocalizer.TownBattleToonFire, text_scale=0.06) + self.fireText.hide() + self.sosHead = None + self.undecidedText = DirectLabel(parent=self, relief=None, pos=(0.1, 0, 0.015), text=TTLocalizer.TownBattleUndecided, text_scale=0.1) + self.healthText = DirectLabel(parent=self, text='', pos=(-0.06, 0, -0.075), text_scale=0.055) + self.hpChangeEvent = None + self.gagNode = self.attachNewNode('gag') + self.gagNode.setPos(0.1, 0, 0.03) + self.hasGag = 0 + passGui = gui.find('**/tt_t_gui_bat_pass') + passGui.detachNode() + self.passNode = self.attachNewNode('pass') + self.passNode.setPos(0.1, 0, 0.05) + passGui.setScale(0.2) + passGui.reparentTo(self.passNode) + self.passNode.hide() + self.laffMeter = None + self.whichText = DirectLabel(parent=self, relief=None, text='', pos=(0.1, 0, -0.08), text_scale=0.05) + self.hoverButton = DirectButton(parent=self, relief=None, image_scale=(0.07, 0, 0.06), pos=(0.105, 0, 0.05), image='phase_3/maps/invisible.png', pressEffect=0) + self.hoverButton.setTransparency(True) + self.hoverButton.bind(DGG.EXIT, self.battle.hideRolloverFrame) + self.hide() + gui.removeNode() + + def hasAvatar(self): + return self.avatar is not None + + def setLaffMeter(self, avatar): + self.notify.debug('setLaffMeter: new avatar %s' % avatar.doId) + + if self.avatar == avatar: + messenger.send(self.avatar.uniqueName('hpChange'), [avatar.hp, avatar.maxHp, 1]) + return + + if self.avatar or self.laffMeter: + self.cleanupLaffMeter() + self.cleanupSosHead() + + self.avatar = avatar + self.laffMeter = LaffMeter.LaffMeter(avatar.style, avatar.hp, avatar.maxHp) + self.laffMeter.setAvatar(self.avatar) + self.laffMeter.reparentTo(self) + self.laffMeter.setPos(-0.06, 0, 0.05) + self.laffMeter.setScale(0.045) + self.laffMeter.start() + self.setHealthText(avatar.hp, avatar.maxHp) + self.hpChangeEvent = self.avatar.uniqueName('hpChange') + self.accept(self.hpChangeEvent, self.setHealthText) + + def setHealthText(self, hp, maxHp, quietly = 0): + self.healthText['text'] = TTLocalizer.TownBattleHealthText % {'hitPoints': hp, + 'maxHit': maxHp} + + def show(self): + DirectFrame.show(self) + if self.laffMeter: + self.laffMeter.start() + + def hide(self): + DirectFrame.hide(self) + if self.laffMeter: + self.laffMeter.stop() + + def updateLaffMeter(self, hp): + if self.laffMeter: + self.laffMeter.adjustFace(hp, self.avatar.maxHp) + self.setHealthText(hp, maxHp) + + def setValues(self, index, track, level = None, numTargets = None, targetIndex = None, localNum = None): + self.notify.debug('Toon Panel setValues: index=%s track=%s level=%s numTargets=%s targetIndex=%s localNum=%s' % (index, + track, + level, + numTargets, + targetIndex, + localNum)) + self.undecidedText.hide() + self.sosText.hide() + self.fireText.hide() + self.gagNode.hide() + self.whichText.hide() + self.passNode.hide() + self.cleanupSosHead() + self.hoverButton.unbind(DGG.ENTER) + if self.hasGag: + self.gag.removeNode() + self.hasGag = 0 + if track == BattleBase.NO_ATTACK or track == BattleBase.UN_ATTACK: + self.undecidedText.show() + elif track == BattleBase.PASS_ATTACK: + self.passNode.show() + elif track == BattleBase.FIRE: + self.fireText.show() + self.whichText.show() + self.whichText['text'] = self.determineWhichText(numTargets, targetIndex, localNum, index) + elif track == BattleBase.NPCSOS: + self.sosHead = createNPCToonHead(targetIndex) + self.sosHead.reparentTo(self) + self.sosHead.setPos(0.1, 0, 0.045) + self.sosHead.setScale(0.24) + track, level, hp, rarity = NPCToons.getNPCTrackLevelHpRarity(targetIndex) + sosType = self.sosTracks[track] + + if track == NPC_RESTOCK_GAGS: + if level == -1: + sosType += ' All' + else: + sosType += ' ' + self.sosTracks[level] + + if hp: + hpString = TTLocalizer.BattleSOSPopupHP % (TTLocalizer.BattleSOSPopupHeal if track == HEAL_TRACK else TTLocalizer.BattleSOSPopupHarm, hp) + + sosType = TextEncoder.upper(sosType) + count = max(0, self.avatar.getNPCFriendCount(targetIndex) - 1) + info = TTLocalizer.BattleSOSPopup % (sosType, NPCToons.getNPCName(targetIndex), hpString if hp else '', rarity, count) + self.hoverButton.bind(DGG.ENTER, self.battle.showRolloverFrame, extraArgs=[self, TTLocalizer.BattleHoverSos, info]) + elif track == BattleBase.SOS or track == BattleBase.PETSOS: + self.sosText.show() + elif track >= MIN_TRACK_INDEX and track <= MAX_TRACK_INDEX: + self.undecidedText.hide() + self.passNode.hide() + self.gagNode.show() + invButton = base.localAvatar.inventory.buttonLookup(track, level) + self.gag = invButton.instanceUnderNode(self.gagNode, 'gag') + self.gag.setScale(0.8) + self.gag.setPos(0, 0, 0.02) + self.hasGag = 1 + if self.avatar: + curExp, nextExp = self.avatar.inventory.getCurAndNextExpValues(track) + organic = self.avatar.checkGagBonus(track, level) + damage = int(getAvPropDamage(track, level, curExp, organic)) + numItems = max(0, self.avatar.inventory.numItem(track, level) - 1) + info = TTLocalizer.BattleGagPopup % (self.avatar.inventory.getToonupDmgStr(track, 0), damage, numItems) + self.hoverButton.bind(DGG.ENTER, self.battle.showRolloverFrame, extraArgs=[self, TTLocalizer.BattleHoverGag, info]) + + if self.avatar.checkGagBonus(track, level): + self.gag.setColor((1, 0, 0, 1) if track == 1 and level == 5 else (0, 1, 0, 1)) + if numTargets is not None and targetIndex is not None and localNum is not None: + self.whichText.show() + self.whichText['text'] = self.determineWhichText(numTargets, targetIndex, localNum, index) + else: + self.notify.error('Bad track value: %s' % track) + + def determineWhichText(self, numTargets, targetIndex, localNum, index): + returnStr = '' + targetList = range(numTargets) + targetList.reverse() + + for i in targetList: + if targetIndex == -1: + returnStr += 'X' + elif targetIndex == -2: + if i == index: + returnStr += '-' + else: + returnStr += 'X' + elif targetIndex >= 0 and targetIndex <= 3: + if i == targetIndex: + returnStr += 'X' + else: + returnStr += '-' + else: + self.notify.error('Bad target index: %s' % targetIndex) + + return returnStr + + def cleanup(self): + self.ignoreAll() + self.cleanupLaffMeter() + if self.hasGag: + self.gag.removeNode() + del self.gag + self.gagNode.removeNode() + del self.gagNode + self.hoverButton.removeNode() + del self.hoverButton + self.cleanupSosHead() + DirectFrame.destroy(self) + + def cleanupSosHead(self): + if self.sosHead: + self.sosHead.removeNode() + self.sosHead = None + + def cleanupLaffMeter(self): + self.ignore(self.hpChangeEvent) + + if self.laffMeter: + self.laffMeter.destroy() + self.laffMeter = None diff --git a/toontown/town/TownBattleWaitPanel.py b/toontown/town/TownBattleWaitPanel.py new file mode 100755 index 00000000..793b4e51 --- /dev/null +++ b/toontown/town/TownBattleWaitPanel.py @@ -0,0 +1,40 @@ +from panda3d.core import * +from direct.fsm import StateData +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer + +class TownBattleWaitPanel(StateData.StateData): + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + + def load(self): + gui = loader.loadModel('phase_3.5/models/gui/battle_gui') + self.frame = DirectFrame(relief=None, image=gui.find('**/Waiting4Others'), text_align=TextNode.ALeft, pos=(0, 0, 0), scale=0.65) + self.frame.hide() + self.backButton = DirectButton(parent=self.frame, relief=None, image=(gui.find('**/PckMn_BackBtn'), gui.find('**/PckMn_BackBtn_Dn'), gui.find('**/PckMn_BackBtn_Rlvr')), pos=(-0.647, 0, -0.011), scale=1.05, text=TTLocalizer.TownBattleWaitBack, text_scale=0.05, text_pos=(0.01, -0.012), text_fg=Vec4(0, 0, 0.8, 1), command=self.__handleBack) + gui.removeNode() + return + + def unload(self): + self.frame.destroy() + del self.frame + del self.backButton + + def enter(self, numParticipants): + if numParticipants > 1: + self.frame['text'] = TTLocalizer.TownBattleWaitTitle + self.frame['text_pos'] = (0, 0.01, 0) + self.frame['text_scale'] = 0.1 + else: + self.frame['text'] = TTLocalizer.TownSoloBattleWaitTitle + self.frame['text_pos'] = (0, -0.05, 0) + self.frame['text_scale'] = 0.13 + self.frame.show() + + def exit(self): + self.frame.hide() + + def __handleBack(self): + doneStatus = {'mode': 'Back'} + messenger.send(self.doneEvent, [doneStatus]) diff --git a/toontown/town/TownLoader.py b/toontown/town/TownLoader.py new file mode 100755 index 00000000..264b791a --- /dev/null +++ b/toontown/town/TownLoader.py @@ -0,0 +1,347 @@ +from panda3d.core import * +from toontown.battle.BattleProps import * +from toontown.battle.BattleSounds import * +from toontown.distributed.ToontownMsgTypes import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import cleanupDialog +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import Place +from direct.showbase import DirectObject +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +import TownBattle +from toontown.toon import Toon +from toontown.toon.Toon import teleportDebug +from toontown.battle import BattleParticles +from direct.fsm import StateData +from toontown.building import ToonInterior +from toontown.hood import QuietZoneState, ZoneUtil, HydrantInteractiveProp, MailboxInteractiveProp, TrashcanInteractiveProp +from direct.interval.IntervalGlobal import * +from toontown.dna.DNAParser import DNABulkLoader + +class TownLoader(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('TownLoader') + + def __init__(self, hood, parentFSMState, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.hood = hood + self.parentFSMState = parentFSMState + self.fsm = ClassicFSM.ClassicFSM('TownLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'street', 'toonInterior']), + State.State('street', self.enterStreet, self.exitStreet, ['quietZone']), + State.State('toonInterior', self.enterToonInterior, self.exitToonInterior, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['street', 'toonInterior']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.branchZone = None + self.canonicalBranchZone = None + self.placeDoneEvent = 'placeDone' + self.townBattleDoneEvent = 'town-battle-done' + return + + def loadBattleAnims(self): + Toon.loadBattleAnims() + + def unloadBattleAnims(self): + Toon.unloadBattleAnims() + + def load(self, zoneId): + self.zoneId = zoneId + self.parentFSMState.addChild(self.fsm) + self.loadBattleAnims() + self.branchZone = ZoneUtil.getBranchZone(zoneId) + self.canonicalBranchZone = ZoneUtil.getCanonicalBranchZone(zoneId) + self.music = base.loadMusic(self.musicFile) + self.activityMusic = base.loadMusic(self.activityMusicFile) + self.battleMusic = base.loadMusic('phase_3.5/audio/bgm/encntr_general_bg.ogg') + self.townBattle = TownBattle.TownBattle(self.townBattleDoneEvent) + self.townBattle.load() + + def unload(self): + self.unloadBattleAnims() + globalPropPool.unloadProps() + globalBattleSoundCache.clear() + BattleParticles.unloadParticles() + self.parentFSMState.removeChild(self.fsm) + del self.parentFSMState + del self.fsm + del self.streetClass + self.landmarkBlocks.removeNode() + del self.landmarkBlocks + self.hood.dnaStore.resetSuitPoints() + self.hood.dnaStore.resetBattleCells() + del self.hood + del self.nodeDict + del self.zoneDict + del self.fadeInDict + del self.fadeOutDict + del self.nodeList + self.geom.removeNode() + del self.geom + self.townBattle.unload() + self.townBattle.cleanup() + del self.townBattle + del self.battleMusic + del self.music + del self.activityMusic + del self.holidayPropTransforms + self.deleteAnimatedProps() + cleanupDialog('globalDialog') + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def enter(self, requestStatus): + teleportDebug(requestStatus, 'TownLoader.enter(%s)' % requestStatus) + self.fsm.enterInitialState() + teleportDebug(requestStatus, 'setting state: %s' % requestStatus['where']) + self.setState(requestStatus['where'], requestStatus) + + def exit(self): + self.ignoreAll() + + def setState(self, stateName, requestStatus): + self.fsm.request(stateName, [requestStatus]) + + def enterStart(self): + pass + + def exitStart(self): + pass + + def enterStreet(self, requestStatus): + teleportDebug(requestStatus, 'enterStreet(%s)' % requestStatus) + self.acceptOnce(self.placeDoneEvent, self.streetDone) + self.place = self.streetClass(self, self.fsm, self.placeDoneEvent) + self.place.load() + base.cr.playGame.setPlace(self.place) + self.place.enter(requestStatus) + + def exitStreet(self): + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def streetDone(self): + self.requestStatus = self.place.doneStatus + status = self.place.doneStatus + if status['loader'] == 'townLoader' and ZoneUtil.getBranchZone(status['zoneId']) == self.branchZone and status['shardId'] == None: + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + messenger.send(self.doneEvent) + return + + def enterToonInterior(self, requestStatus): + self.acceptOnce(self.placeDoneEvent, self.handleToonInteriorDone) + self.place = ToonInterior.ToonInterior(self, self.fsm.getStateNamed('toonInterior'), self.placeDoneEvent) + base.cr.playGame.setPlace(self.place) + self.place.load() + self.place.enter(requestStatus) + + def exitToonInterior(self): + self.ignore(self.placeDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def handleToonInteriorDone(self): + status = self.place.doneStatus + if ZoneUtil.getBranchZone(status['zoneId']) == self.branchZone and status['shardId'] == None: + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + messenger.send(self.doneEvent) + return + + def enterQuietZone(self, requestStatus): + self.quietZoneDoneEvent = uniqueName('quietZoneDone') + self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone) + self.quietZoneStateData = QuietZoneState.QuietZoneState(self.quietZoneDoneEvent) + self.quietZoneStateData.load() + self.quietZoneStateData.enter(requestStatus) + + def exitQuietZone(self): + self.ignore(self.quietZoneDoneEvent) + del self.quietZoneDoneEvent + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + return + + def handleQuietZoneDone(self): + status = self.quietZoneStateData.getRequestStatus() + self.fsm.request(status['where'], [status]) + + def enterFinal(self): + pass + + def exitFinal(self): + pass + + def createHood(self, dnaFile, loadStorage = 1): + if loadStorage: + files = ('phase_5/dna/storage_town.pdna', self.townStorageDNAFile) + dnaBulk = DNABulkLoader(self.hood.dnaStore, files) + dnaBulk.loadDNAFiles() + node = loader.loadDNAFile(self.hood.dnaStore, dnaFile) + self.notify.debug('done loading %s' % dnaFile) + if node.getNumParents() == 1: + self.geom = NodePath(node.getParent(0)) + self.geom.reparentTo(hidden) + else: + self.geom = hidden.attachNewNode(node) + self.makeDictionaries(self.hood.dnaStore) + self.reparentLandmarkBlockNodes() + self.renameFloorPolys(self.nodeList) + self.createAnimatedProps(self.nodeList) + self.holidayPropTransforms = {} + npl = self.geom.findAllMatches('**/=DNARoot=holiday_prop') + for i in xrange(npl.getNumPaths()): + np = npl.getPath(i) + np.setTag('transformIndex', `i`) + self.holidayPropTransforms[i] = np.getNetTransform() + gsg = base.win.getGsg() + if gsg: + self.geom.prepareScene(gsg) + self.geom.flattenLight() + self.geom.setName('town_top_level') + + def reparentLandmarkBlockNodes(self): + bucket = self.landmarkBlocks = hidden.attachNewNode('landmarkBlocks') + npc = self.geom.findAllMatches('**/sb*:*_landmark_*_DNARoot') + for i in xrange(npc.getNumPaths()): + nodePath = npc.getPath(i) + nodePath.wrtReparentTo(bucket) + + def makeDictionaries(self, dnaStore): + self.nodeDict = {} + self.zoneDict = {} + self.zoneVisDict = {} + self.nodeList = [] + self.fadeInDict = {} + self.fadeOutDict = {} + a1 = Vec4(1, 1, 1, 1) + a0 = Vec4(1, 1, 1, 0) + numVisGroups = dnaStore.getNumDNAVisGroupsAI() + for i in xrange(numVisGroups): + groupFullName = dnaStore.getDNAVisGroupName(i) + visGroup = dnaStore.getDNAVisGroupAI(i) + groupName = base.cr.hoodMgr.extractGroupName(groupFullName) + zoneId = int(groupName) + groupNode = self.geom.find('**/' + groupFullName) + if groupNode.isEmpty(): + self.notify.error('Could not find visgroup') + else: + if ':' in groupName: + groupName = '%s%s' % (zoneId, groupName[groupName.index(':'):]) + else: + groupName = '%s' % zoneId + groupNode.setName(groupName) + groupNode.flattenMedium() + self.nodeDict[zoneId] = [] + self.nodeList.append(groupNode) + self.zoneDict[zoneId] = groupNode + visibles = [] + for i in xrange(visGroup.getNumVisibles()): + visibles.append(int(visGroup.getVisible(i))) + visibles.append(ZoneUtil.getBranchZone(zoneId)) + self.zoneVisDict[zoneId] = visibles + fadeDuration = 0.5 + self.fadeOutDict[groupNode] = Sequence(Func(groupNode.setTransparency, 1), LerpColorScaleInterval(groupNode, fadeDuration, a0, startColorScale=a1), Func(groupNode.clearColorScale), Func(groupNode.clearTransparency), Func(groupNode.stash), name='fadeZone-' + str(zoneId), autoPause=1) + self.fadeInDict[groupNode] = Sequence(Func(groupNode.unstash), Func(groupNode.setTransparency, 1), LerpColorScaleInterval(groupNode, fadeDuration, a1, startColorScale=a0), Func(groupNode.clearColorScale), Func(groupNode.clearTransparency), name='fadeZone-' + str(zoneId), autoPause=1) + + for i in xrange(numVisGroups): + groupFullName = dnaStore.getDNAVisGroupName(i) + zoneId = int(base.cr.hoodMgr.extractGroupName(groupFullName)) + for j in xrange(dnaStore.getNumVisiblesInDNAVisGroup(i)): + visName = dnaStore.getVisibleName(i, j) + groupName = base.cr.hoodMgr.extractGroupName(visName) + nextZoneId = int(groupName) + visNode = self.zoneDict[nextZoneId] + self.nodeDict[zoneId].append(visNode) + + self.hood.dnaStore.resetPlaceNodes() + self.hood.dnaStore.resetDNAGroups() + self.hood.dnaStore.resetDNAVisGroups() + self.hood.dnaStore.resetDNAVisGroupsAI() + + def renameFloorPolys(self, nodeList): + for i in nodeList: + collNodePaths = i.findAllMatches('**/+CollisionNode') + numCollNodePaths = collNodePaths.getNumPaths() + visGroupName = i.node().getName() + for j in xrange(numCollNodePaths): + collNodePath = collNodePaths.getPath(j) + bitMask = collNodePath.node().getIntoCollideMask() + if bitMask.getBit(1): + collNodePath.node().setName(visGroupName) + + def createAnimatedProps(self, nodeList): + self.animPropDict = {} + self.zoneIdToInteractivePropDict = {} + + for i in nodeList: + animPropNodes = i.findAllMatches('**/animated_prop_*') + numAnimPropNodes = animPropNodes.getNumPaths() + for j in xrange(numAnimPropNodes): + animPropNode = animPropNodes.getPath(j) + if animPropNode.getName().startswith('animated_prop_generic'): + className = 'GenericAnimatedProp' + elif animPropNode.getName().startswith('animated_prop_'): + name = animPropNode.getName()[len('animated_prop_'):] + splits = name.split('_') + className = splits[0] + else: + className = animPropNode.getName()[14:-8] + symbols = {} + base.cr.importModule(symbols, 'toontown.hood', [className]) + classObj = getattr(symbols[className], className) + animPropObj = classObj(animPropNode) + animPropList = self.animPropDict.setdefault(i, []) + animPropList.append(animPropObj) + + interactivePropNodes = i.findAllMatches('**/interactive_prop_*') + + for j in xrange(interactivePropNodes.getNumPaths()): + propNode = interactivePropNodes.getPath(j) + propName = propNode.getName() + + if 'hydrant' in propName: + prop = HydrantInteractiveProp.HydrantInteractiveProp(propNode) + elif 'trashcan' in propName: + prop = TrashcanInteractiveProp.TrashcanInteractiveProp(propNode) + elif 'mailbox' in propName: + prop = MailboxInteractiveProp.MailboxInteractiveProp(propNode) + else: + continue + + if i in self.animPropDict: + self.animPropDict[i].append(prop) + else: + self.animPropDict[i] = [prop] + + self.zoneIdToInteractivePropDict[int(i.getName())] = prop + + def deleteAnimatedProps(self): + for zoneNode, animPropList in self.animPropDict.items(): + for animProp in animPropList: + animProp.delete() + + del self.animPropDict + + def enterAnimatedProps(self, zoneNode): + for animProp in self.animPropDict.get(zoneNode, ()): + animProp.enter() + + def exitAnimatedProps(self, zoneNode): + for animProp in self.animPropDict.get(zoneNode, ()): + animProp.exit() + + def getInteractiveProp(self, zoneId): + if zoneId in self.zoneIdToInteractivePropDict: + return self.zoneIdToInteractivePropDict[zoneId] + return None diff --git a/toontown/town/TutorialStreet.py b/toontown/town/TutorialStreet.py new file mode 100755 index 00000000..8d8af0e3 --- /dev/null +++ b/toontown/town/TutorialStreet.py @@ -0,0 +1,22 @@ +import TTStreet + +class TutorialStreet(TTStreet.TTStreet): + + def enter(self, requestStatus): + TTStreet.TTStreet.enter(self, requestStatus, visibilityFlag=0, arrowsOn=0) + + def exit(self): + TTStreet.TTStreet.exit(self, visibilityFlag=0) + + def enterTeleportIn(self, requestStatus): + TTStreet.TTStreet.enterTeleportIn(self, requestStatus) + + def enterTownBattle(self, event): + self.loader.townBattle.enter(event, self.fsm.getStateNamed('battle'), tutorialFlag=1) + + def handleEnterTunnel(self, requestStatus, collEntry): + messenger.send('stopTutorial') + TTStreet.TTStreet.handleEnterTunnel(self, requestStatus, collEntry) + + def exitDoorIn(self): + base.localAvatar.obscureMoveFurnitureButton(0) diff --git a/toontown/town/TutorialTownLoader.py b/toontown/town/TutorialTownLoader.py new file mode 100755 index 00000000..b3e3625b --- /dev/null +++ b/toontown/town/TutorialTownLoader.py @@ -0,0 +1,30 @@ +import TownLoader +import TTTownLoader +import TutorialStreet +from toontown.suit import Suit +from toontown.toon import Toon +from toontown.hood import ZoneUtil + +class TutorialTownLoader(TTTownLoader.TTTownLoader): + + def __init__(self, hood, parentFSM, doneEvent): + TTTownLoader.TTTownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = TutorialStreet.TutorialStreet + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadTutorialSuit() + dnaFile = 'phase_3.5/dna/tutorial_street.pdna' + self.createHood(dnaFile, loadStorage=0) + self.alterDictionaries() + + def loadBattleAnims(self): + Toon.loadTutorialBattleAnims() + + def unloadBattleAnims(self): + Toon.unloadTutorialBattleAnims() + + def alterDictionaries(self): + zoneId = ZoneUtil.tutorialDict['exteriors'][0] + self.nodeDict[zoneId] = self.nodeDict[20001] + del self.nodeDict[20001] diff --git a/toontown/town/__init__.py b/toontown/town/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/trolley/Trolley.py b/toontown/trolley/Trolley.py new file mode 100755 index 00000000..bd6fc1ae --- /dev/null +++ b/toontown/trolley/Trolley.py @@ -0,0 +1,213 @@ +from panda3d.core import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal + +class Trolley(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('Trolley') + + def __init__(self, safeZone, parentFSM, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.fsm = ClassicFSM.ClassicFSM('Trolley', [ + State.State('start', + self.enterStart, + self.exitStart, + ['requestBoard', + 'trolleyHFA', + 'trolleyTFA']), + State.State('trolleyHFA', + self.enterTrolleyHFA, + self.exitTrolleyHFA, + ['final']), + State.State('trolleyTFA', + self.enterTrolleyTFA, + self.exitTrolleyTFA, + ['final']), + State.State('requestBoard', + self.enterRequestBoard, + self.exitRequestBoard, + ['boarding']), + State.State('boarding', + self.enterBoarding, + self.exitBoarding, + ['boarded']), + State.State('boarded', + self.enterBoarded, + self.exitBoarded, + ['requestExit', + 'trolleyLeaving', + 'final']), + State.State('requestExit', + self.enterRequestExit, + self.exitRequestExit, + ['exiting', + 'trolleyLeaving']), + State.State('trolleyLeaving', + self.enterTrolleyLeaving, + self.exitTrolleyLeaving, + ['final']), + State.State('exiting', + self.enterExiting, + self.exitExiting, + ['final']), + State.State('final', + self.enterFinal, + self.exitFinal, + ['start'])], + 'start', 'final') + self.parentFSM = parentFSM + return None + + def load(self): + self.parentFSM.getStateNamed('trolley').addChild(self.fsm) + self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + self.upButton = self.buttonModels.find('**//InventoryButtonUp') + self.downButton = self.buttonModels.find('**/InventoryButtonDown') + self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover') + + def unload(self): + self.parentFSM.getStateNamed('trolley').removeChild(self.fsm) + del self.fsm + del self.parentFSM + self.buttonModels.removeNode() + del self.buttonModels + del self.upButton + del self.downButton + del self.rolloverButton + + def enter(self): + self.fsm.enterInitialState() + if base.localAvatar.hp > 0: + messenger.send('enterTrolleyOK') + self.fsm.request('requestBoard') + else: + self.fsm.request('trolleyHFA') + return None + + def exit(self): + self.ignoreAll() + return None + + def enterStart(self): + return None + + def exitStart(self): + return None + + def enterTrolleyHFA(self): + self.noTrolleyBox = TTDialog.TTGlobalDialog(message=TTLocalizer.TrolleyHFAMessage, doneEvent='noTrolleyAck', style=TTDialog.Acknowledge) + self.noTrolleyBox.show() + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('noTrolleyAck', self.__handleNoTrolleyAck) + + def exitTrolleyHFA(self): + self.ignore('noTrolleyAck') + self.noTrolleyBox.cleanup() + del self.noTrolleyBox + + def enterTrolleyTFA(self): + self.noTrolleyBox = TTDialog.TTGlobalDialog(message=TTLocalizer.TrolleyTFAMessage, doneEvent='noTrolleyAck', style=TTDialog.Acknowledge) + self.noTrolleyBox.show() + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('noTrolleyAck', self.__handleNoTrolleyAck) + + def exitTrolleyTFA(self): + self.ignore('noTrolleyAck') + self.noTrolleyBox.cleanup() + del self.noTrolleyBox + + def __handleNoTrolleyAck(self): + ntdoneStatus = self.noTrolleyBox.doneStatus + if ntdoneStatus == 'ok': + doneStatus = {} + doneStatus['mode'] = 'reject' + messenger.send(self.doneEvent, [doneStatus]) + else: + self.notify.error('Unrecognized doneStatus: ' + str(ntdoneStatus)) + + def enterRequestBoard(self): + return None + + def handleRejectBoard(self): + doneStatus = {} + doneStatus['mode'] = 'reject' + messenger.send(self.doneEvent, [doneStatus]) + + def exitRequestBoard(self): + return None + + def enterBoarding(self, nodePath): + camera.wrtReparentTo(nodePath) + self.cameraBoardTrack = LerpPosHprInterval(camera, 1.5, Point3(-35, 0, 8), Point3(-90, 0, 0)) + self.cameraBoardTrack.start() + return None + + def exitBoarding(self): + self.ignore('boardedTrolley') + return None + + def enterBoarded(self): + self.enableExitButton() + return None + + def exitBoarded(self): + self.cameraBoardTrack.finish() + self.disableExitButton() + return None + + def enableExitButton(self): + self.exitButton = DirectButton(relief=None, text=TTLocalizer.TrolleyHopOff, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=TTLocalizer.TexitButton, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0, 0, 0.8), scale=0.15, command=lambda self = self: self.fsm.request('requestExit')) + return + + def disableExitButton(self): + self.exitButton.destroy() + + def enterRequestExit(self): + messenger.send('trolleyExitButton') + return None + + def exitRequestExit(self): + return None + + def enterTrolleyLeaving(self): + camera.posHprInterval(3, (0, 18.55, 3.75), (-180, 0, 0), blendType='easeInOut').start() + self.acceptOnce('playMinigame', self.handlePlayMinigame) + return None + + def handlePlayMinigame(self, zoneId, minigameId): + base.localAvatar.b_setParent(ToontownGlobals.SPHidden) + doneStatus = {} + doneStatus['mode'] = 'minigame' + doneStatus['zoneId'] = zoneId + doneStatus['minigameId'] = minigameId + messenger.send(self.doneEvent, [doneStatus]) + + def exitTrolleyLeaving(self): + self.ignore('playMinigame') + taskMgr.remove('leavingCamera') + return None + + def enterExiting(self): + return None + + def handleOffTrolley(self): + doneStatus = {} + doneStatus['mode'] = 'exit' + messenger.send(self.doneEvent, [doneStatus]) + return None + + def exitExiting(self): + return None + + def enterFinal(self): + return None + + def exitFinal(self): + return None diff --git a/toontown/trolley/__init__.py b/toontown/trolley/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/tutorial/DistributedBattleTutorial.py b/toontown/tutorial/DistributedBattleTutorial.py new file mode 100755 index 00000000..34a8c557 --- /dev/null +++ b/toontown/tutorial/DistributedBattleTutorial.py @@ -0,0 +1,11 @@ +from toontown.battle import DistributedBattle +from direct.directnotify import DirectNotifyGlobal + +class DistributedBattleTutorial(DistributedBattle.DistributedBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleTutorial') + + def startTimer(self, ts = 0): + self.townBattle.timer.hide() + + def playReward(self, ts): + self.movie.playTutorialReward(ts, self.uniqueName('reward'), self.handleRewardDone) diff --git a/toontown/tutorial/DistributedBattleTutorialAI.py b/toontown/tutorial/DistributedBattleTutorialAI.py new file mode 100755 index 00000000..e3143f4e --- /dev/null +++ b/toontown/tutorial/DistributedBattleTutorialAI.py @@ -0,0 +1,16 @@ +from direct.directnotify.DirectNotifyGlobal import * +from toontown.battle.DistributedBattleAI import DistributedBattleAI + + +class DistributedBattleTutorialAI(DistributedBattleAI): + notify = directNotify.newCategory('DistributedBattleTutorialAI') + + def __init__(self, air, battleMgr, pos, suit, toonId, zoneId, + finishCallback=None, maxSuits=4, tutorialFlag=1, levelFlag=0, + interactivePropTrackBonus=-1): + DistributedBattleAI.__init__(self, air, battleMgr, pos, suit, toonId, + zoneId, finishCallback=finishCallback, maxSuits=1, + tutorialFlag=1, levelFlag=0, interactivePropTrackBonus=-1) + + def startRewardTimer(self): + pass # We don't want a reward timer in the tutorial. diff --git a/toontown/tutorial/TutorialManager.py b/toontown/tutorial/TutorialManager.py new file mode 100755 index 00000000..d0df2cd2 --- /dev/null +++ b/toontown/tutorial/TutorialManager.py @@ -0,0 +1,54 @@ +from panda3d.core import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import ZoneUtil + +class TutorialManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TutorialManager') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + messenger.send('tmGenerate') + self.cr.tutorialManager = self + self.accept('requestTutorial', self.d_requestTutorial) + self.accept('requestSkipTutorial', self.d_requestSkipTutorial) + self.accept('rejectTutorial', self.d_rejectTutorial) + + def disable(self): + self.ignoreAll() + ZoneUtil.overrideOff() + DistributedObject.DistributedObject.disable(self) + + def d_requestTutorial(self): + self.sendUpdate('requestTutorial', []) + + def d_rejectTutorial(self): + self.sendUpdate('rejectTutorial', []) + + def d_requestSkipTutorial(self): + self.sendUpdate('requestSkipTutorial', []) + + def skipTutorialResponse(self, allOk): + messenger.send('skipTutorialAnswered', [allOk]) + + def enterTutorial(self, branchZone, streetZone, shopZone, hqZone): + base.localAvatar.cantLeaveGame = 1 + ZoneUtil.overrideOn(branch=branchZone, exteriorList=[streetZone], interiorList=[shopZone, hqZone]) + messenger.send('startTutorial', [shopZone]) + self.acceptOnce('stopTutorial', self.__handleStopTutorial) + self.acceptOnce('toonArrivedTutorial', self.d_toonArrived) + + def __handleStopTutorial(self): + base.localAvatar.cantLeaveGame = 0 + self.d_allDone() + ZoneUtil.overrideOff() + + def d_allDone(self): + self.sendUpdate('allDone', []) + + def d_toonArrived(self): + self.sendUpdate('toonArrived', []) diff --git a/toontown/tutorial/TutorialManagerAI.py b/toontown/tutorial/TutorialManagerAI.py new file mode 100755 index 00000000..1303980d --- /dev/null +++ b/toontown/tutorial/TutorialManagerAI.py @@ -0,0 +1,175 @@ +from direct.directnotify.DirectNotifyGlobal import * +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.fsm.FSM import FSM +from toontown.building import FADoorCodes +from toontown.building.HQBuildingAI import HQBuildingAI +from toontown.building.TutorialBuildingAI import TutorialBuildingAI +from toontown.quest import Quests +from toontown.suit.DistributedTutorialSuitAI import DistributedTutorialSuitAI +from toontown.toon import NPCToons +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import ToontownGlobals + + +class TutorialFSM(FSM): + def __init__(self, air, zones, avId): + FSM.__init__(self, 'TutorialFSM') + + self.air = air + self.zones = zones + self.avId = avId + + npcDesc = NPCToons.NPCToonDict.get(20000) + self.tutorialTom = NPCToons.createNPC(self.air, 20000, npcDesc, self.zones['building']) + self.tutorialTom.setTutorial(1) + + npcDesc = NPCToons.NPCToonDict.get(20002) + self.hqHarry = NPCToons.createNPC(self.air, 20002, npcDesc, self.zones['hq']) + self.hqHarry.setTutorial(1) + self.hqHarry.setHq(1) + + self.building = TutorialBuildingAI( + self.air, self.zones['street'], self.zones['building'], 2, self.tutorialTom.getDoId()) + self.hq = HQBuildingAI(self.air, self.zones['street'], self.zones['hq'], 1) + + self.forceTransition('Introduction') + + def enterIntroduction(self): + self.building.insideDoor.setDoorLock(FADoorCodes.TALK_TO_TOM) + + def exitIntroduction(self): + self.building.insideDoor.setDoorLock(FADoorCodes.UNLOCKED) + + def enterBattle(self): + self.suit = DistributedTutorialSuitAI(self.air) + self.suit.generateWithRequired(self.zones['street']) + + self.building.door.setDoorLock(FADoorCodes.DEFEAT_FLUNKY_TOM) + self.hq.door0.setDoorLock(FADoorCodes.DEFEAT_FLUNKY_HQ) + self.hq.door1.setDoorLock(FADoorCodes.DEFEAT_FLUNKY_HQ) + + def exitBattle(self): + if self.suit: + self.suit.requestDelete() + + def enterHQ(self): + self.building.door.setDoorLock(FADoorCodes.TALK_TO_HQ) + self.hq.door0.setDoorLock(FADoorCodes.UNLOCKED) + self.hq.door1.setDoorLock(FADoorCodes.UNLOCKED) + self.hq.insideDoor0.setDoorLock(FADoorCodes.TALK_TO_HQ) + self.hq.insideDoor1.setDoorLock(FADoorCodes.TALK_TO_HQ) + + def enterTunnel(self): + npcDesc = NPCToons.NPCToonDict.get(20001) + self.flippy = NPCToons.createNPC(self.air, 20001, npcDesc, self.zones['street'], 0) + self.hq.insideDoor0.setDoorLock(FADoorCodes.WRONG_DOOR_HQ) + self.hq.insideDoor1.setDoorLock(FADoorCodes.UNLOCKED) + self.hq.door0.setDoorLock(FADoorCodes.GO_TO_PLAYGROUND) + self.hq.door1.setDoorLock(FADoorCodes.GO_TO_PLAYGROUND) + self.building.door.setDoorLock(FADoorCodes.GO_TO_PLAYGROUND) + + def exitTunnel(self): + self.flippy.requestDelete() + + def enterCleanup(self): + self.building.cleanup() + self.hq.cleanup() + self.tutorialTom.requestDelete() + self.hqHarry.requestDelete() + + self.air.deallocateZone(self.zones['street']) + self.air.deallocateZone(self.zones['building']) + self.air.deallocateZone(self.zones['hq']) + + del self.air.tutorialManager.avId2fsm[self.avId] + + +class TutorialManagerAI(DistributedObjectAI): + notify = directNotify.newCategory('TutorialManagerAI') + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + + self.avId2fsm = {} + + def requestTutorial(self): + avId = self.air.getAvatarIdFromSender() + + zones = {} + zones['street'] = self.air.allocateZone() + zones['building'] = self.air.allocateZone() + zones['hq'] = self.air.allocateZone() + + self.avId2fsm[avId] = TutorialFSM(self.air, zones, avId) + + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + self.d_enterTutorial(avId, ToontownGlobals.Tutorial, zones['street'], zones['building'], zones['hq']) + + def rejectTutorial(self): + pass + + def requestSkipTutorial(self): + avId = self.air.getAvatarIdFromSender() + self.d_skipTutorialResponse(avId, 1) + + + def handleTutorialSkipped(av): + av.b_setTutorialAck(1) + av.b_setQuests([[110, 1, 1000, 100, 1]]) + av.b_setQuestHistory([101]) + av.b_setRewardHistory(1, []) + + + # We must wait for the avatar to be generated: + self.acceptOnce('generate-%d' % avId, handleTutorialSkipped) + + def d_skipTutorialResponse(self, avId, allOk): + self.sendUpdateToAvatarId(avId, 'skipTutorialResponse', [allOk]) + + def d_enterTutorial(self, avId, branchZone, streetZone, shopZone, hqZone): + self.sendUpdateToAvatarId(avId, 'enterTutorial', [branchZone, streetZone, shopZone, hqZone]) + + def allDone(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av is not None: + av.b_setTutorialAck(1) + self.ignore(self.air.getAvatarExitEvent(avId)) + fsm = self.avId2fsm.get(avId) + if fsm is not None: + fsm.demand('Cleanup') + else: + self.air.writeServerEvent('suspicious', avId, issue='Attempted to exit a non-existent tutorial.') + + def toonArrived(self): + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av is None: + return + + if av.getTutorialAck(): + self.avId2fsm[avId].demand('Cleanup') + self.air.writeServerEvent('suspicious', avId, issue='Attempted to enter a tutorial when it should be impossible.') + return + + # Prepare the player for the tutorial: + av.b_setQuests([]) + av.b_setQuestHistory([]) + av.b_setRewardHistory(0, []) + av.b_setHp(15) + av.b_setMaxHp(15) + av.inventory.zeroInv(killUber=True) + access = av.getTrackAccess() + + for i in xrange(len(access)): + if access[i] == 1: + av.inventory.addItem(i, 0) + + av.d_setInventory(av.inventory.makeNetString()) + av.experience.zeroOutExp() + av.d_setExperience(av.experience.makeNetString()) + + def __handleUnexpectedExit(self, avId): + fsm = self.avId2fsm.get(avId) + if fsm is not None: + fsm.demand('Cleanup') diff --git a/toontown/tutorial/__init__.py b/toontown/tutorial/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/toontown/uberdog/ARGManager.py b/toontown/uberdog/ARGManager.py new file mode 100755 index 00000000..994a1183 --- /dev/null +++ b/toontown/uberdog/ARGManager.py @@ -0,0 +1,51 @@ +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal +from otp.speedchat import SpeedChatGlobals +from direct.directnotify.DirectNotifyGlobal import directNotify + +# Portable Hole settings +Hood2Details = { + # hood : [speedchatIndex, destination] + 2514: [109, 2514] # TTC, Hello? +} +Interior2Messages = { + 2514: ["Banker Bob: I have a very important message for you. Do not forget it.", "Banker Bob: Li0uLiAuLSAuLS0gLS4uLiAtLS0gLSAuLi4gLyAuLS4uIC0tLSAuLi4gLg=="] +} + +class ARGManager(DistributedObjectGlobal): + """ + This is a client-view of the manager that handles everything to do + with the portable hole ARG event. + """ + notify = directNotify.newCategory('ARGManager') + + def __init__(self, cr): + DistributedObjectGlobal.__init__(self, cr) + self.setupPortableHoleEvent() + + def disable(self): + self.cleanupPortableHoleEvent() + DistributedObjectGlobal.disable(self) + + def delete(self): + self.cleanupPortableHoleEvent() + DistributedObjectGlobal.delete(self) + + def setupPortableHoleEvent(self): + def phraseSaid(phraseId): + if not hasattr(base.cr.playGame, 'place') or not base.cr.playGame.getPlace(): + return + speedchatIndex, destination = Hood2Details.get(base.cr.playGame.getPlace().getZoneId(), [None, None]) + if not speedchatIndex or not destination: + return + if speedchatIndex != phraseId: + return + msgBefore, msgAfter = Interior2Messages.get(destination, [None, None]) + if not msgBefore: + self.notify.warning("Interior %d has no message definitions!" % destination) + return + taskMgr.doMethodLater(2, base.localAvatar.setSystemMessage, self.uniqueName("arg-before-msg"), extraArgs=[0, msgBefore]) + taskMgr.doMethodLater(7, base.localAvatar.setSystemMessage, self.uniqueName("arg-after-msg"), extraArgs=[0, msgAfter]) + self.accept(SpeedChatGlobals.SCStaticTextMsgEvent, phraseSaid) + + def cleanupPortableHoleEvent(self): + self.ignore(SpeedChatGlobals.SCStaticTextMsgEvent) diff --git a/toontown/uberdog/ClientServicesManager.py b/toontown/uberdog/ClientServicesManager.py new file mode 100755 index 00000000..2e74d11e --- /dev/null +++ b/toontown/uberdog/ClientServicesManager.py @@ -0,0 +1,99 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal +import hmac +from panda3d.core import * + +from otp.distributed.PotentialAvatar import PotentialAvatar +from otp.otpbase import OTPGlobals +from otp.nametag.NametagConstants import WTSystem +from otp.margins.WhisperPopup import WhisperPopup + + +class ClientServicesManager(DistributedObjectGlobal): + notify = directNotify.newCategory('ClientServicesManager') + + # --- LOGIN LOGIC --- + def performLogin(self, doneEvent): + self.doneEvent = doneEvent + + self.systemMessageSfx = None + + token = self.cr.playToken or 'dev' + + key = 'c603c5833021ce79f734943f6e662250fd4ecf7432bf85905f71707dc4a9370c6ae15a8716302ead43810e5fba3cf0876bbbfce658e2767b88d916f5d89fd31' + digest_maker = hmac.new(key) + digest_maker.update(token) + clientKey = digest_maker.hexdigest() + + self.sendUpdate('login', [token, clientKey]) + + def acceptLogin(self, timestamp): + messenger.send(self.doneEvent, [{'mode': 'success', 'timestamp': timestamp}]) + + + # --- AVATARS LIST --- + def requestAvatars(self): + self.sendUpdate('requestAvatars') + + def setAvatars(self, chatSettings, avatars): + avList = [] + for avNum, avName, avDNA, avPosition, nameState in avatars: + nameOpen = int(nameState == 1) + names = [avName, '', '', ''] + if nameState == 2: # PENDING + names[1] = avName + elif nameState == 3: # APPROVED + names[2] = avName + elif nameState == 4: # REJECTED + names[3] = avName + avList.append(PotentialAvatar(avNum, names, avDNA, avPosition, nameOpen)) + + self.cr.handleChatSettings(chatSettings) + self.cr.handleAvatarsList(avList) + + # --- AVATAR CREATION/DELETION --- + def sendCreateAvatar(self, avDNA, _, index): + self.sendUpdate('createAvatar', [avDNA.makeNetString(), index]) + + def createAvatarResp(self, avId): + messenger.send('nameShopCreateAvatarDone', [avId]) + + def sendDeleteAvatar(self, avId): + self.sendUpdate('deleteAvatar', [avId]) + + # No deleteAvatarResp; it just sends a setAvatars when the deed is done. + + # --- AVATAR NAMING --- + def sendSetNameTyped(self, avId, name, callback): + self._callback = callback + self.sendUpdate('setNameTyped', [avId, name]) + + def setNameTypedResp(self, avId, status): + self._callback(avId, status) + + def sendSetNamePattern(self, avId, p1, f1, p2, f2, p3, f3, p4, f4, callback): + self._callback = callback + self.sendUpdate('setNamePattern', [avId, p1, f1, p2, f2, p3, f3, p4, f4]) + + def setNamePatternResp(self, avId, status): + self._callback(avId, status) + + def sendAcknowledgeAvatarName(self, avId, callback): + self._callback = callback + self.sendUpdate('acknowledgeAvatarName', [avId]) + + def acknowledgeAvatarNameResp(self): + self._callback() + + # --- AVATAR CHOICE --- + def sendChooseAvatar(self, avId): + self.sendUpdate('chooseAvatar', [avId]) + + def systemMessage(self, message): + whisper = WhisperPopup(message, OTPGlobals.getInterfaceFont(), WTSystem) + whisper.manage(base.marginManager) + + if self.systemMessageSfx is None: + self.systemMessageSfx = base.loadSfx('phase_3/audio/sfx/clock03.ogg') + + base.playSfx(self.systemMessageSfx) diff --git a/toontown/uberdog/ClientServicesManagerUD.py b/toontown/uberdog/ClientServicesManagerUD.py new file mode 100755 index 00000000..2bd953e3 --- /dev/null +++ b/toontown/uberdog/ClientServicesManagerUD.py @@ -0,0 +1,1202 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.distributed.DistributedObjectGlobalUD import DistributedObjectGlobalUD +from direct.distributed.PyDatagram import * +from direct.fsm.FSM import FSM + +from otp.ai.MagicWordGlobal import * +from otp.distributed import OtpDoGlobals + +from toontown.makeatoon.NameGenerator import NameGenerator +from toontown.toon.ToonDNA import ToonDNA +from toontown.toonbase import TTLocalizer +from toontown.uberdog import NameJudgeBlacklist + +from panda3d.core import * + +import hashlib, hmac, json +import anydbm, math, os +import urllib2, time, urllib +import cookielib, socket + +def rejectConfig(issue, securityIssue=True, retarded=True): + print + print + print 'Lemme get this straight....' + print 'You are trying to use remote account database type...' + print 'However,', issue + '!!!!' + if securityIssue: + print 'Do you want this server to get hacked?' + if retarded: + print '"Either down\'s or autism"\n - JohnnyDaPirate, 2015' + print 'Go fix that!' + exit() + +def entropy(string): + prob = [float(string.count(c)) / len(string) for c in dict.fromkeys(list(string))] + entropy = -sum([p * math.log(p) / math.log(2.0) for p in prob]) + return entropy + +def entropyIdeal(length): + prob = 1.0 / length + return -length * prob * math.log(prob) / math.log(2.0) + +accountDBType = config.GetString('accountdb-type', 'developer') +accountServerSecret = config.GetString('account-server-secret', 'dev') +accountServerHashAlgo = config.GetString('account-server-hash-algo', 'sha512') +apiSecret = accountServerSecret = config.GetString('api-key', 'dev') +if accountDBType == 'remote': + if accountServerSecret == 'dev': + rejectConfig('you have not changed the secret in config/local.prc') + + if apiSecret == 'dev': + rejectConfig('you have not changed the api key in config/local.prc') + + if len(accountServerSecret) < 16: + rejectConfig('the secret is too small! Make it 16+ bytes', retarded=False) + + secretLength = len(accountServerSecret) + ideal = entropyIdeal(secretLength) / 2 + entropy = entropy(accountServerSecret) + if entropy < ideal: + rejectConfig('the secret entropy is too low! For %d bytes,' + ' it should be %d. Currently it is %d' % (secretLength, ideal, entropy), + retarded=False) + + hashAlgo = getattr(hashlib, accountServerHashAlgo, None) + if not hashAlgo: + rejectConfig('%s is not a valid hash algo' % accountServerHashAlgo, securityIssue=False) + + hashSize = len(hashAlgo('').digest()) + +minAccessLevel = config.GetInt('min-access-level', 100) + +def executeHttpRequest(url, **extras): + # TO DO: THIS IS QUITE DISGUSTING + # MOVE THIS TO ToontownInternalRepository (this might be interesting for AI) + ##### USE PYTHON 2.7.9 ON PROD WITH SSL AND CLOUDFLARE ##### + _data = {} + if len(extras.items()) != 0: + for k, v in extras.items(): + _data[k] = v + signature = hashlib.sha512(json.dumps(_data) + apiSecret).hexdigest() + data = urllib.urlencode({'data': json.dumps(_data), 'hmac': signature}) + cookie_jar = cookielib.LWPCookieJar() + cookie = urllib2.HTTPCookieProcessor(cookie_jar) + opener = urllib2.build_opener(cookie) + req = urllib2.Request('http://192.168.1.212/api/' + url, data, + headers={"Content-Type" : "application/x-www-form-urlencoded"}) + req.get_method = lambda: "POST" + try: + return opener.open(req).read() + except: + return None + +notify = directNotify.newCategory('ClientServicesManagerUD') + +def executeHttpRequestAndLog(url, **extras): + # SEE ABOVE + response = executeHttpRequest(url, extras) + + if response is None: + notify.error('A request to ' + url + ' went wrong.') + return None + + try: + data = json.loads(response) + except: + notify.error('Malformed response from ' + url + '.') + return None + + if 'error' in data: + notify.warning('Error from ' + url + ':' + data['error']) + + return data + +#blacklist = executeHttpRequest('names/blacklist.json') # todo; create a better system for this +blacklist = json.dumps(["none"]) +if blacklist: + blacklist = json.loads(blacklist) + +def judgeName(name): + if not name: + return False + if blacklist: + for namePart in name.split(' '): + namePart = namePart.lower() + if len(namePart) < 1: + return False + for banned in blacklist: + if banned in namePart: + return False + return True + +# --- ACCOUNT DATABASES --- +# These classes make up the available account databases for Toontown Stride. +# DeveloperAccountDB is a special database that accepts a username, and assigns +# each user with 700 access automatically upon login. + +class AccountDB: + notify = directNotify.newCategory('AccountDB') + + def __init__(self, csm): + self.csm = csm + + filename = config.GetString('account-bridge-filename', 'account-bridge.db') + filename = os.path.join('dependencies', filename) + + self.dbm = anydbm.open(filename, 'c') + + def addNameRequest(self, avId, name, accountID = None): + return True + + def getNameStatus(self, accountId, callback = None): + return 'APPROVED' + + def removeNameRequest(self, avId): + pass + + def lookup(self, data, callback): + userId = data['userId'] + + data['success'] = True + data['accessLevel'] = max(data['accessLevel'], minAccessLevel) + + if str(userId) not in self.dbm: + data['accountId'] = 0 + + else: + data['accountId'] = int(self.dbm[str(userId)]) + + callback(data) + return data + + def storeAccountID(self, userId, accountId, callback): + self.dbm[str(userId)] = str(accountId) # anydbm only allows strings. + if getattr(self.dbm, 'sync', None): + self.dbm.sync() + callback(True) + else: + self.notify.warning('Unable to associate user %s with account %d!' % (userId, accountId)) + callback(False) + +class DeveloperAccountDB(AccountDB): + notify = directNotify.newCategory('DeveloperAccountDB') + + def lookup(self, userId, callback): + return AccountDB.lookup(self, {'userId': userId, + 'accessLevel': 700, + 'notAfter': 0}, + callback) + +class RemoteAccountDB: + # TO DO FOR NAMES: + # CURRENTLY IT MAKES n REQUESTS FOR EACH AVATAR + # IN THE FUTURE, MAKE ONLY 1 REQUEST + # WHICH RETURNS ALL PENDING AVS + # ^ done, check before removing todo note + notify = directNotify.newCategory('RemoteAccountDB') + + def __init__(self, csm): + self.csm = csm + + def addNameRequest(self, avId, name, accountID = None): + username = avId + if accountID is not None: + username = accountID + + res = executeHttpRequest('names', action='set', username=str(username), + avId=str(avId), wantedName=name) + if res is not None: + return True + return False + + def getNameStatus(self, accountId, callback = None): + r = executeHttpRequest('names', action='get', username=str(accountId)) + try: + r = json.loads(r) + if callback is not None: + callback(r) + return True + except: + return False + + def removeNameRequest(self, avId): + r = executeHttpRequest('names', action='del', avId=str(avId)) + if r: + return 'SUCCESS' + return 'FAILURE' + + def lookup(self, token, callback): + ''' + Token format: + The token is obfuscated a bit, but nothing too hard to read. + Most of the security is based on the hash. + + I. Data contained in a token: + A json-encoded dict, which contains timestamp, userid and extra info + + II. Token format + X = BASE64(ROT13(DATA)[::-1]) + H = HASH(X)[::-1] + Token = BASE64(H + X) + ''' + + cookie_check = executeHttpRequest('cookie', cookie=token) + + try: + check = json.loads(cookie_check) + if check['success'] is not True: + raise ValueError(check['error']) + token = token.decode('base64') + hash, token = token[:hashSize], token[hashSize:] + correctHash = hashAlgo(token + accountServerSecret).digest() + if len(hash) != len(correctHash): + raise ValueError('Invalid hash.') + + value = 0 + for x, y in zip(hash[::-1], correctHash): + value |= ord(x) ^ ord(y) + + if value: + raise ValueError('Invalid hash.') + + token = json.loads(token.decode('base64')[::-1].decode('rot13')) + + if token['notAfter'] < int(time.time()): + raise ValueError('Expired token.') + except: + resp = {'success': False} + callback(resp) + return resp + + return self.account_lookup(token, callback) + + def account_lookup(self, data, callback): + data['success'] = True + data['accessLevel'] = max(data['accessLevel'], minAccessLevel) + data['accountId'] = int(data['accountId']) + + callback(data) + return data + + def storeAccountID(self, userId, accountId, callback): + r = executeHttpRequest('associateuser', username=str(userId), accountId=str(accountId)) + try: + r = json.loads(r) + if r['success']: + callback(True) + else: + self.notify.warning('Unable to associate user %s with account %d, got the return message of %s!' % (userId, accountId, r['error'])) + callback(False) + except: + self.notify.warning('Unable to associate user %s with account %d!' % (userId, accountId)) + callback(False) + + +# --- FSMs --- +class OperationFSM(FSM): + TARGET_CONNECTION = False + + def __init__(self, csm, target): + self.csm = csm + self.target = target + + FSM.__init__(self, self.__class__.__name__) + + def enterKill(self, reason=''): + if self.TARGET_CONNECTION: + self.csm.killConnection(self.target, reason) + else: + self.csm.killAccount(self.target, reason) + self.demand('Off') + + def enterOff(self): + if self.TARGET_CONNECTION: + del self.csm.connection2fsm[self.target] + else: + del self.csm.account2fsm[self.target] + +class LoginAccountFSM(OperationFSM): + notify = directNotify.newCategory('LoginAccountFSM') + TARGET_CONNECTION = True + + def enterStart(self, token): + self.token = token + self.demand('QueryAccountDB') + + def enterQueryAccountDB(self): + self.csm.accountDB.lookup(self.token, self.__handleLookup) + + def __handleLookup(self, result): + if not result.get('success'): + self.csm.air.writeServerEvent('tokenRejected', self.target, self.token) + self.demand('Kill', result.get('reason', 'The account server rejected your token.')) + return + + self.userId = result.get('userId', 0) + self.accountId = result.get('accountId', 0) + self.accessLevel = result.get('accessLevel', 0) + self.notAfter = result.get('notAfter', 0) + if self.accountId: + self.demand('RetrieveAccount') + else: + self.demand('CreateAccount') + + def enterRetrieveAccount(self): + self.csm.air.dbInterface.queryObject( + self.csm.air.dbId, self.accountId, self.__handleRetrieve) + + def __handleRetrieve(self, dclass, fields): + if dclass != self.csm.air.dclassesByName['AccountUD']: + self.demand('Kill', 'Your account object was not found in the database!') + return + + self.account = fields + + if self.notAfter: + if self.account.get('LAST_LOGIN_TS', 0) > self.notAfter: + self.notify.debug('Rejecting old token: %d, notAfter=%d' % (self.account.get('LAST_LOGIN_TS', 0), self.notAfter)) + return self.__handleLookup({'success': False}) + + self.demand('SetAccount') + + def enterCreateAccount(self): + self.account = { + 'ACCOUNT_AV_SET': [0] * 6, + 'ESTATE_ID': 0, + 'ACCOUNT_AV_SET_DEL': [], + 'CREATED': time.ctime(), + 'LAST_LOGIN': time.ctime(), + 'LAST_LOGIN_TS': time.time(), + 'ACCOUNT_ID': str(self.userId), + 'ACCESS_LEVEL': self.accessLevel, + 'CHAT_SETTINGS': [1, 1] + } + self.csm.air.dbInterface.createObject( + self.csm.air.dbId, + self.csm.air.dclassesByName['AccountUD'], + self.account, + self.__handleCreate) + + def __handleCreate(self, accountId): + if self.state != 'CreateAccount': + self.notify.warning('Received a create account response outside of the CreateAccount state.') + return + + if not accountId: + self.notify.warning('Database failed to construct an account object!') + self.demand('Kill', 'Your account object could not be created in the game database.') + return + + self.accountId = accountId + self.csm.air.writeServerEvent('accountCreated', accountId) + self.demand('StoreAccountID') + + def enterStoreAccountID(self): + self.csm.accountDB.storeAccountID( + self.userId, + self.accountId, + self.__handleStored) + + def __handleStored(self, success=True): + if not success: + self.demand('Kill', 'The account server could not save your user ID!') + return + + self.demand('SetAccount') + + def enterSetAccount(self): + # If necessary, update their account information: + if self.accessLevel: + self.csm.air.dbInterface.updateObject( + self.csm.air.dbId, + self.accountId, + self.csm.air.dclassesByName['AccountUD'], + {'ACCESS_LEVEL': self.accessLevel}) + + # If there's anybody on the account, kill them for redundant login: + datagram = PyDatagram() + datagram.addServerHeader( + self.csm.GetAccountConnectionChannel(self.accountId), + self.csm.air.ourChannel, + CLIENTAGENT_EJECT) + datagram.addUint16(100) + datagram.addString('This account has been logged in from elsewhere.') + self.csm.air.send(datagram) + + # Next, add this connection to the account channel. + datagram = PyDatagram() + datagram.addServerHeader( + self.target, + self.csm.air.ourChannel, + CLIENTAGENT_OPEN_CHANNEL) + datagram.addChannel(self.csm.GetAccountConnectionChannel(self.accountId)) + self.csm.air.send(datagram) + + # Subscribe to any "staff" channels that the account has access to. + access = self.account.get('ADMIN_ACCESS', 0) + if access >= 200: + # Subscribe to the moderator channel. + dg = PyDatagram() + dg.addServerHeader(self.target, self.csm.air.ourChannel, CLIENTAGENT_OPEN_CHANNEL) + dg.addChannel(OtpDoGlobals.OTP_MOD_CHANNEL) + self.csm.air.send(dg) + if access >= 400: + # Subscribe to the administrator channel. + dg = PyDatagram() + dg.addServerHeader(self.target, self.csm.air.ourChannel, CLIENTAGENT_OPEN_CHANNEL) + dg.addChannel(OtpDoGlobals.OTP_ADMIN_CHANNEL) + self.csm.air.send(dg) + if access >= 500: + # Subscribe to the system administrator channel. + dg = PyDatagram() + dg.addServerHeader(self.target, self.csm.air.ourChannel, CLIENTAGENT_OPEN_CHANNEL) + dg.addChannel(OtpDoGlobals.OTP_SYSADMIN_CHANNEL) + self.csm.air.send(dg) + + # Now set their sender channel to represent their account affiliation: + datagram = PyDatagram() + datagram.addServerHeader( + self.target, + self.csm.air.ourChannel, + CLIENTAGENT_SET_CLIENT_ID) + # Account ID in high 32 bits, 0 in low (no avatar): + datagram.addChannel(self.accountId << 32) + self.csm.air.send(datagram) + + # Un-sandbox them! + datagram = PyDatagram() + datagram.addServerHeader( + self.target, + self.csm.air.ourChannel, + CLIENTAGENT_SET_STATE) + datagram.addUint16(2) # ESTABLISHED + self.csm.air.send(datagram) + + # Update the last login timestamp: + self.csm.air.dbInterface.updateObject( + self.csm.air.dbId, + self.accountId, + self.csm.air.dclassesByName['AccountUD'], + {'LAST_LOGIN': time.ctime(), + 'LAST_LOGIN_TS': time.time(), + 'ACCOUNT_ID': str(self.userId)}) + + # We're done. + self.csm.air.writeServerEvent('accountLogin', self.target, self.accountId, self.userId) + self.csm.sendUpdateToChannel(self.target, 'acceptLogin', [int(time.time())]) + self.demand('Off') + +class CreateAvatarFSM(OperationFSM): + notify = directNotify.newCategory('CreateAvatarFSM') + + def enterStart(self, dna, index): + # Basic sanity-checking: + if index >= 6: + self.demand('Kill', 'Invalid index specified!') + return + + if not ToonDNA().isValidNetString(dna): + self.demand('Kill', 'Invalid DNA specified!') + return + + self.index = index + self.dna = dna + + # Okay, we're good to go, let's query their account. + self.demand('RetrieveAccount') + + def enterRetrieveAccount(self): + self.csm.air.dbInterface.queryObject( + self.csm.air.dbId, self.target, self.__handleRetrieve) + + def __handleRetrieve(self, dclass, fields): + if dclass != self.csm.air.dclassesByName['AccountUD']: + self.demand('Kill', 'Your account object was not found in the database!') + return + + self.account = fields + + # For use in calling name requests: + self.accountID = self.account['ACCOUNT_ID'] + + self.avList = self.account['ACCOUNT_AV_SET'] + # Sanitize: + self.avList = self.avList[:6] + self.avList += [0] * (6-len(self.avList)) + + # Make sure the index is open: + if self.avList[self.index]: + self.demand('Kill', 'This avatar slot is already taken by another avatar!') + return + + # Okay, there's space. Let's create the avatar! + self.demand('CreateAvatar') + + def enterCreateAvatar(self): + dna = ToonDNA() + dna.makeFromNetString(self.dna) + colorString = TTLocalizer.ColorfulToon + animalType = TTLocalizer.AnimalToSpecies[dna.getAnimal()] + name = ' '.join((colorString, animalType)) + toonFields = { + 'setName': (name,), + 'setWishNameState': ('OPEN',), + 'setWishName': ('',), + 'setDNAString': (self.dna,), + 'setDISLid': (self.target,), + } + self.csm.air.dbInterface.createObject( + self.csm.air.dbId, + self.csm.air.dclassesByName['DistributedToonUD'], + toonFields, + self.__handleCreate) + + def __handleCreate(self, avId): + if not avId: + self.demand('Kill', 'Database failed to create the new avatar object!') + return + + self.avId = avId + self.demand('StoreAvatar') + + def enterStoreAvatar(self): + # Associate the avatar with the account... + self.avList[self.index] = self.avId + self.csm.air.dbInterface.updateObject( + self.csm.air.dbId, + self.target, + self.csm.air.dclassesByName['AccountUD'], + {'ACCOUNT_AV_SET': self.avList}, + {'ACCOUNT_AV_SET': self.account['ACCOUNT_AV_SET']}, + self.__handleStoreAvatar) + + self.accountID = self.account['ACCOUNT_ID'] + + def __handleStoreAvatar(self, fields): + if fields: + self.demand('Kill', 'Database failed to associate the new avatar to your account!') + return + + # Otherwise, we're done! + self.csm.air.writeServerEvent('avatarCreated', self.avId, self.target, self.dna.encode('hex'), self.index) + self.csm.sendUpdateToAccountId(self.target, 'createAvatarResp', [self.avId]) + self.demand('Off') + +class AvatarOperationFSM(OperationFSM): + POST_ACCOUNT_STATE = 'Off' # This needs to be overridden. + + def enterRetrieveAccount(self): + # Query the account: + self.csm.air.dbInterface.queryObject( + self.csm.air.dbId, self.target, self.__handleRetrieve) + + def __handleRetrieve(self, dclass, fields): + if dclass != self.csm.air.dclassesByName['AccountUD']: + self.demand('Kill', 'Your account object was not found in the database!') + return + + self.account = fields + + # For use in calling name requests: + self.accountID = self.account['ACCOUNT_ID'] + + self.avList = self.account['ACCOUNT_AV_SET'] + # Sanitize: + self.avList = self.avList[:6] + self.avList += [0] * (6-len(self.avList)) + + self.demand(self.POST_ACCOUNT_STATE) + +class GetAvatarsFSM(AvatarOperationFSM): + notify = directNotify.newCategory('GetAvatarsFSM') + POST_ACCOUNT_STATE = 'QueryAvatars' + + def enterStart(self): + self.demand('RetrieveAccount') + self.nameStateData = None + + def enterQueryAvatars(self): + self.pendingAvatars = set() + self.avatarFields = {} + for avId in self.avList: + if avId: + self.pendingAvatars.add(avId) + + def response(dclass, fields, avId=avId): + if self.state != 'QueryAvatars': + return + if dclass != self.csm.air.dclassesByName['DistributedToonUD']: + self.demand('Kill', "One of the account's avatars is invalid!") + return + self.avatarFields[avId] = fields + self.pendingAvatars.remove(avId) + if not self.pendingAvatars: + self.demand('SendAvatars') + + self.csm.air.dbInterface.queryObject( + self.csm.air.dbId, + avId, + response) + + if not self.pendingAvatars: + self.demand('SendAvatars') + + def enterSendAvatars(self): + potentialAvs = [] + + for avId, fields in self.avatarFields.items(): + index = self.avList.index(avId) + wishNameState = fields.get('setWishNameState', [''])[0] + name = fields['setName'][0] + nameState = 0 + + if wishNameState == 'OPEN': + nameState = 1 + elif wishNameState == 'PENDING': + if accountDBType == 'remote': + if self.nameStateData is None: + self.demand('QueryNameState') + return + actualNameState = self.nameStateData[str(avId)] + else: + actualNameState = self.csm.accountDB.getNameStatus(self.account['ACCOUNT_ID']) + self.csm.air.dbInterface.updateObject( + self.csm.air.dbId, + avId, + self.csm.air.dclassesByName['DistributedToonUD'], + {'setWishNameState': [actualNameState]} + ) + if actualNameState == 'PENDING': + nameState = 2 + if actualNameState == 'APPROVED': + nameState = 3 + name = fields['setWishName'][0] + elif actualNameState == 'REJECTED': + nameState = 4 + elif wishNameState == 'APPROVED': + nameState = 3 + elif wishNameState == 'REJECTED': + nameState = 4 + + potentialAvs.append([avId, name, fields['setDNAString'][0], + index, nameState]) + + self.csm.sendUpdateToAccountId(self.target, 'setAvatars', [self.account['CHAT_SETTINGS'], potentialAvs]) + self.demand('Off') + + def enterQueryNameState(self): + def gotStates(data): + self.nameStateData = data + taskMgr.doMethodLater(0, GetAvatarsFSM.demand, 'demand-QueryAvatars', + extraArgs=[self, 'QueryAvatars']) + + self.csm.accountDB.getNameStatus(self.account['ACCOUNT_ID'], gotStates) + # We should've called the taskMgr action by now. + + +# This inherits from GetAvatarsFSM, because the delete operation ends in a +# setAvatars message being sent to the client. +class DeleteAvatarFSM(GetAvatarsFSM): + notify = directNotify.newCategory('DeleteAvatarFSM') + POST_ACCOUNT_STATE = 'ProcessDelete' + + def enterStart(self, avId): + self.avId = avId + GetAvatarsFSM.enterStart(self) + + def enterProcessDelete(self): + if self.avId not in self.avList: + self.demand('Kill', 'Tried to delete an avatar not in the account!') + return + + index = self.avList.index(self.avId) + self.avList[index] = 0 + + avsDeleted = list(self.account.get('ACCOUNT_AV_SET_DEL', [])) + avsDeleted.append([self.avId, int(time.time())]) + + estateId = self.account.get('ESTATE_ID', 0) + + if estateId != 0: + # This assumes that the house already exists, but it shouldn't + # be a problem if it doesn't. + self.csm.air.dbInterface.updateObject( + self.csm.air.dbId, + estateId, + self.csm.air.dclassesByName['DistributedEstateAI'], + {'setSlot%dToonId' % index: [0], + 'setSlot%dGarden' % index: [[]]} + ) + + self.csm.air.dbInterface.updateObject( + self.csm.air.dbId, + self.target, + self.csm.air.dclassesByName['AccountUD'], + {'ACCOUNT_AV_SET': self.avList, + 'ACCOUNT_AV_SET_DEL': avsDeleted}, + {'ACCOUNT_AV_SET': self.account['ACCOUNT_AV_SET'], + 'ACCOUNT_AV_SET_DEL': self.account['ACCOUNT_AV_SET_DEL']}, + self.__handleDelete) + self.csm.accountDB.removeNameRequest(self.avId) + + def __handleDelete(self, fields): + if fields: + self.demand('Kill', 'Database failed to mark the avatar as deleted!') + return + + self.csm.air.friendsManager.clearList(self.avId) + self.csm.air.writeServerEvent('avatarDeleted', self.avId, self.target) + self.demand('QueryAvatars') + +class SetNameTypedFSM(AvatarOperationFSM): + notify = directNotify.newCategory('SetNameTypedFSM') + POST_ACCOUNT_STATE = 'RetrieveAvatar' + + def enterStart(self, avId, name): + self.avId = avId + self.name = name + self.set_account_id = None + + if self.avId: + self.demand('RetrieveAccount') + return + + # Hmm, self.avId was 0. Okay, let's just cut to the judging: + self.demand('JudgeName') + + def enterRetrieveAvatar(self): + if self.accountID: + self.set_account_id = self.accountID + if self.avId and self.avId not in self.avList: + self.demand('Kill', 'Tried to name an avatar not in the account!') + return + + self.csm.air.dbInterface.queryObject(self.csm.air.dbId, self.avId, + self.__handleAvatar) + + def __handleAvatar(self, dclass, fields): + if dclass != self.csm.air.dclassesByName['DistributedToonUD']: + self.demand('Kill', "One of the account's avatars is invalid!") + return + + if fields['setWishNameState'][0] != 'OPEN': + self.demand('Kill', 'Avatar is not in a namable state!') + return + + self.demand('JudgeName') + + def enterJudgeName(self): + # Let's see if the name is valid: + status = judgeName(self.name) + + if self.avId and status: + if self.csm.accountDB.addNameRequest(self.avId, self.name, accountID=self.set_account_id): + self.csm.air.dbInterface.updateObject( + self.csm.air.dbId, + self.avId, + self.csm.air.dclassesByName['DistributedToonUD'], + {'setWishNameState': ('PENDING',), + 'setWishName': (self.name,)}) + else: + status = False + + if self.avId: + self.csm.air.writeServerEvent('avatarWishname', self.avId, self.name) + + self.csm.sendUpdateToAccountId(self.target, 'setNameTypedResp', [self.avId, status]) + self.demand('Off') + +class SetNamePatternFSM(AvatarOperationFSM): + notify = directNotify.newCategory('SetNamePatternFSM') + POST_ACCOUNT_STATE = 'RetrieveAvatar' + + def enterStart(self, avId, pattern): + self.avId = avId + self.pattern = pattern + + self.demand('RetrieveAccount') + + def enterRetrieveAvatar(self): + if self.avId and self.avId not in self.avList: + self.demand('Kill', 'Tried to name an avatar not in the account!') + return + + self.csm.air.dbInterface.queryObject(self.csm.air.dbId, self.avId, + self.__handleAvatar) + + def __handleAvatar(self, dclass, fields): + if dclass != self.csm.air.dclassesByName['DistributedToonUD']: + self.demand('Kill', "One of the account's avatars is invalid!") + return + + if fields['setWishNameState'][0] != 'OPEN': + self.demand('Kill', 'Avatar is not in a namable state!') + return + + self.demand('SetName') + + def enterSetName(self): + # Render the pattern into a string: + parts = [] + for p, f in self.pattern: + part = self.csm.nameGenerator.nameDictionary.get(p, ('', ''))[1] + if f: + part = part[:1].upper() + part[1:] + else: + part = part.lower() + parts.append(part) + + parts[2] += parts.pop(3) # Merge 2&3 (the last name) as there should be no space. + while '' in parts: + parts.remove('') + name = ' '.join(parts) + + self.csm.air.dbInterface.updateObject( + self.csm.air.dbId, + self.avId, + self.csm.air.dclassesByName['DistributedToonUD'], + {'setWishNameState': ('',), + 'setWishName': ('',), + 'setName': (name,)}) + + self.csm.air.writeServerEvent('avatarNamed', self.avId, name) + self.csm.sendUpdateToAccountId(self.target, 'setNamePatternResp', [self.avId, 1]) + self.demand('Off') + +class AcknowledgeNameFSM(AvatarOperationFSM): + notify = directNotify.newCategory('AcknowledgeNameFSM') + POST_ACCOUNT_STATE = 'GetTargetAvatar' + + def enterStart(self, avId): + self.avId = avId + self.demand('RetrieveAccount') + + def enterGetTargetAvatar(self): + # Make sure the target avatar is part of the account: + if self.avId not in self.avList: + self.demand('Kill', 'Tried to acknowledge name on an avatar not in the account!') + return + + self.csm.air.dbInterface.queryObject(self.csm.air.dbId, self.avId, + self.__handleAvatar) + + def __handleAvatar(self, dclass, fields): + if dclass != self.csm.air.dclassesByName['DistributedToonUD']: + self.demand('Kill', "One of the account's avatars is invalid!") + return + + # Process the WishNameState change. + wishNameState = fields['setWishNameState'][0] + wishName = fields['setWishName'][0] + name = fields['setName'][0] + + if wishNameState == 'APPROVED': + wishNameState = '' + name = wishName + wishName = '' + self.csm.accountDB.removeNameRequest(self.avId) + elif wishNameState == 'REJECTED': + wishNameState = 'OPEN' + wishName = '' + self.csm.accountDB.removeNameRequest(self.avId) + else: + self.demand('Kill', "Tried to acknowledge name on an avatar in %s state!" % wishNameState) + return + + # Push the change back through: + self.csm.air.dbInterface.updateObject( + self.csm.air.dbId, + self.avId, + self.csm.air.dclassesByName['DistributedToonUD'], + {'setWishNameState': (wishNameState,), + 'setWishName': (wishName,), + 'setName': (name,)}, + {'setWishNameState': fields['setWishNameState'], + 'setWishName': fields['setWishName'], + 'setName': fields['setName']}) + + self.csm.sendUpdateToAccountId(self.target, 'acknowledgeAvatarNameResp', []) + self.demand('Off') + +class LoadAvatarFSM(AvatarOperationFSM): + notify = directNotify.newCategory('LoadAvatarFSM') + POST_ACCOUNT_STATE = 'GetTargetAvatar' + + def enterStart(self, avId): + self.avId = avId + self.demand('RetrieveAccount') + + def enterGetTargetAvatar(self): + # Make sure the target avatar is part of the account: + if self.avId not in self.avList: + self.demand('Kill', 'Tried to play an avatar not in the account!') + return + + self.csm.air.dbInterface.queryObject(self.csm.air.dbId, self.avId, + self.__handleAvatar) + + def __handleAvatar(self, dclass, fields): + if dclass != self.csm.air.dclassesByName['DistributedToonUD']: + self.demand('Kill', "One of the account's avatars is invalid!") + return + + self.avatar = fields + self.demand('SetAvatar') + + def enterSetAvatarTask(self, channel, task): + # Finally, grant ownership and shut down. + datagram = PyDatagram() + datagram.addServerHeader( + self.avId, + self.csm.air.ourChannel, + STATESERVER_OBJECT_SET_OWNER) + datagram.addChannel(self.target<<32 | self.avId) + self.csm.air.send(datagram) + + # Tell the GlobalPartyManager as well: + self.csm.air.globalPartyMgr.avatarJoined(self.avId) + + fields = self.avatar + fields.update({'setAdminAccess': [self.account.get('ACCESS_LEVEL', 100)]}) + self.csm.air.friendsManager.addToonData(self.avId, fields) + + self.csm.air.writeServerEvent('avatarChosen', self.avId, self.target) + self.demand('Off') + return task.done + + def enterSetAvatar(self): + channel = self.csm.GetAccountConnectionChannel(self.target) + + # First, give them a POSTREMOVE to unload the avatar, just in case they + # disconnect while we're working. + datagramCleanup = PyDatagram() + datagramCleanup.addServerHeader( + self.avId, + channel, + STATESERVER_OBJECT_DELETE_RAM) + datagramCleanup.addUint32(self.avId) + datagram = PyDatagram() + datagram.addServerHeader( + channel, + self.csm.air.ourChannel, + CLIENTAGENT_ADD_POST_REMOVE) + datagram.addString(datagramCleanup.getMessage()) + self.csm.air.send(datagram) + + # Activate the avatar on the DBSS: + self.csm.air.sendActivate( + self.avId, 0, 0, self.csm.air.dclassesByName['DistributedToonUD'], + {'setAdminAccess': [self.account.get('ACCESS_LEVEL', 100)]}) + + # Next, add them to the avatar channel: + datagram = PyDatagram() + datagram.addServerHeader( + channel, + self.csm.air.ourChannel, + CLIENTAGENT_OPEN_CHANNEL) + datagram.addChannel(self.csm.GetPuppetConnectionChannel(self.avId)) + self.csm.air.send(datagram) + + # Now set their sender channel to represent their account affiliation: + datagram = PyDatagram() + datagram.addServerHeader( + channel, + self.csm.air.ourChannel, + CLIENTAGENT_SET_CLIENT_ID) + datagram.addChannel(self.target<<32 | self.avId) + self.csm.air.send(datagram) + + # Eliminate race conditions. + taskMgr.doMethodLater(0.2, self.enterSetAvatarTask, + 'avatarTask-%s' % self.avId, extraArgs=[channel], + appendTask=True) + +class UnloadAvatarFSM(OperationFSM): + notify = directNotify.newCategory('UnloadAvatarFSM') + + def enterStart(self, avId): + self.avId = avId + + # We don't even need to query the account, we know the avatar is being played! + self.demand('UnloadAvatar') + + def enterUnloadAvatar(self): + channel = self.csm.GetAccountConnectionChannel(self.target) + + # Tell TTSFriendsManager somebody is logging off: + self.csm.air.friendsManager.toonOffline(self.avId) + + # Clear off POSTREMOVE: + datagram = PyDatagram() + datagram.addServerHeader( + channel, + self.csm.air.ourChannel, + CLIENTAGENT_CLEAR_POST_REMOVES) + self.csm.air.send(datagram) + + # Remove avatar channel: + datagram = PyDatagram() + datagram.addServerHeader( + channel, + self.csm.air.ourChannel, + CLIENTAGENT_CLOSE_CHANNEL) + datagram.addChannel(self.csm.GetPuppetConnectionChannel(self.avId)) + self.csm.air.send(datagram) + + # Reset sender channel: + datagram = PyDatagram() + datagram.addServerHeader( + channel, + self.csm.air.ourChannel, + CLIENTAGENT_SET_CLIENT_ID) + datagram.addChannel(self.target<<32) + self.csm.air.send(datagram) + + # Unload avatar object: + datagram = PyDatagram() + datagram.addServerHeader( + self.avId, + channel, + STATESERVER_OBJECT_DELETE_RAM) + datagram.addUint32(self.avId) + self.csm.air.send(datagram) + + # Done! + self.csm.air.writeServerEvent('avatarUnload', self.avId) + self.demand('Off') + +# --- CLIENT SERVICES MANAGER UBERDOG --- +class ClientServicesManagerUD(DistributedObjectGlobalUD): + notify = directNotify.newCategory('ClientServicesManagerUD') + + def announceGenerate(self): + DistributedObjectGlobalUD.announceGenerate(self) + + # These keep track of the connection/account IDs currently undergoing an + # operation on the CSM. This is to prevent (hacked) clients from firing up more + # than one operation at a time, which could potentially lead to exploitation + # of race conditions. + self.connection2fsm = {} + self.account2fsm = {} + + # For processing name patterns. + self.nameGenerator = NameGenerator() + + # Temporary HMAC key: + self.key = 'c603c5833021ce79f734943f6e662250fd4ecf7432bf85905f71707dc4a9370c6ae15a8716302ead43810e5fba3cf0876bbbfce658e2767b88d916f5d89fd31' + + # Instantiate our account DB interface: + if accountDBType == 'developer': + self.accountDB = DeveloperAccountDB(self) + elif accountDBType == 'remote': + self.accountDB = RemoteAccountDB(self) + else: + self.notify.error('Invalid accountdb-type: ' + accountDBType) + + def killConnection(self, connId, reason): + datagram = PyDatagram() + datagram.addServerHeader( + connId, + self.air.ourChannel, + CLIENTAGENT_EJECT) + datagram.addUint16(101) + datagram.addString(reason) + self.air.send(datagram) + + def killConnectionFSM(self, connId): + fsm = self.connection2fsm.get(connId) + + if not fsm: + self.notify.warning('Tried to kill connection %d for duplicate FSM, but none exists!' % connId) + return + + self.killConnection(connId, 'An operation is already underway: ' + fsm.name) + + def killAccount(self, accountId, reason): + self.killConnection(self.GetAccountConnectionChannel(accountId), reason) + + def killAccountFSM(self, accountId): + fsm = self.account2fsm.get(accountId) + if not fsm: + + self.notify.warning('Tried to kill account %d for duplicate FSM, but none exists!' % accountId) + return + + self.killAccount(accountId, 'An operation is already underway: ' + fsm.name) + + def runAccountFSM(self, fsmtype, *args): + sender = self.air.getAccountIdFromSender() + + if not sender: + self.killAccount(sender, 'Client is not logged in.') + + if sender in self.account2fsm: + self.killAccountFSM(sender) + return + + self.account2fsm[sender] = fsmtype(self, sender) + self.account2fsm[sender].request('Start', *args) + + def login(self, cookie, authKey): + self.notify.debug('Received login cookie %r from %d' % (cookie, self.air.getMsgSender())) + + sender = self.air.getMsgSender() + + # Time to check this login to see if its authentic + digest_maker = hmac.new(self.key) + digest_maker.update(cookie) + serverKey = digest_maker.hexdigest() + if serverKey == authKey: + # This login is authentic! + pass + else: + # This login is not authentic. + self.killConnection(sender, ' ') + + if sender >> 32: + self.killConnection(sender, 'Client is already logged in.') + return + + if sender in self.connection2fsm: + self.killConnectionFSM(sender) + return + + self.connection2fsm[sender] = LoginAccountFSM(self, sender) + self.connection2fsm[sender].request('Start', cookie) + + def requestAvatars(self): + self.notify.debug('Received avatar list request from %d' % (self.air.getMsgSender())) + self.runAccountFSM(GetAvatarsFSM) + + def createAvatar(self, dna, index): + self.runAccountFSM(CreateAvatarFSM, dna, index) + + def deleteAvatar(self, avId): + self.runAccountFSM(DeleteAvatarFSM, avId) + + def setNameTyped(self, avId, name): + self.runAccountFSM(SetNameTypedFSM, avId, name) + + def setNamePattern(self, avId, p1, f1, p2, f2, p3, f3, p4, f4): + self.runAccountFSM(SetNamePatternFSM, avId, [(p1, f1), (p2, f2), + (p3, f3), (p4, f4)]) + + def acknowledgeAvatarName(self, avId): + self.runAccountFSM(AcknowledgeNameFSM, avId) + + def chooseAvatar(self, avId): + currentAvId = self.air.getAvatarIdFromSender() + accountId = self.air.getAccountIdFromSender() + if currentAvId and avId: + self.killAccount(accountId, 'A Toon is already chosen!') + return + elif not currentAvId and not avId: + # This isn't really an error, the client is probably just making sure + # none of its Toons are active. + return + + if avId: + self.runAccountFSM(LoadAvatarFSM, avId) + else: + self.runAccountFSM(UnloadAvatarFSM, currentAvId) diff --git a/toontown/uberdog/DistributedLobbyManager.py b/toontown/uberdog/DistributedLobbyManager.py new file mode 100644 index 00000000..244a8102 --- /dev/null +++ b/toontown/uberdog/DistributedLobbyManager.py @@ -0,0 +1,69 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObject import DistributedObject + +class DistributedLobbyManager(DistributedObject): + neverDisable = 1 + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLobbyManager') + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + base.cr.lobbyManager = self + self.allowUnreleased = False + + def delete(self): + DistributedObject.delete(self) + self.cr.lobbyManager = None + + def disable(self): + self.ignore('deallocateZoneIdFromPlannedLobby') + self.ignoreAll() + DistributedObject.disable(self) + + def generate(self): + DistributedObject.generate(self) + self.accept('deallocateZoneIdFromPlannedLobby', self.deallocateZoneIdFromPlannedLobby) + + def deallocateZoneIdFromPlannedLobby(self, zoneId): + self.sendUpdate('freeZoneIdFromPlannedLobby', [base.localAvatar.doId, zoneId]) + + def allowUnreleasedClient(self): + return self.allowUnreleased + + def setAllowUnreleaseClient(self, newValue): + self.allowUnreleased = newValue + + def toggleAllowUnreleasedClient(self): + self.allowUnreleased = not self.allowUnreleased + return self.allowUnreleased + + def sendAddLobby(self, hostId): + self.sendUpdate('addPartyRequest', [hostId]) + + def requestLobbyZone(self, avId, zoneId, callback): + if zoneId < 0: + zoneId = 0 + self.acceptOnce('requestLobbyZoneComplete', callback) + if hasattr(base.localAvatar, 'aboutToCreateLobby'): + if base.localAvatar.aboutToCreateLobby: + self.sendUpdate('getLobbyZone', [avId, zoneId, True]) + self.sendUpdate('getLobbyZone', [avId, zoneId, False]) + + def receiveLobbyZone(self, hostId, lobbyId, zoneId): + if lobbyId != 0 and zoneId != 0: + if base.localAvatar.doId == hostId: + lobbyInfo = base.localAvatar.hostedLobby + if lobbyInfo.lobbyId == lobbyId: + lobbyInfo.status == LobbyGlobals.LobbyStatus.Open + messenger.send('requestLobbyZoneComplete', [hostId, lobbyId, zoneId]) + + def leaveLobby(self): + if self.isDisabled(): + return + self.sendUpdate('exitLobby', [localAvatar.zoneId]) + + def sendAvatarToLobby(self, hostId): + self.sendUpdate('requestShardIdZoneIdForHostId', [hostId]) + + def sendShardIdZoneIdToAvatar(self, shardId, zoneId): + # Avatar goes through door. + pass diff --git a/toontown/uberdog/DistributedLobbyManagerAI.py b/toontown/uberdog/DistributedLobbyManagerAI.py new file mode 100644 index 00000000..50c4cc2b --- /dev/null +++ b/toontown/uberdog/DistributedLobbyManagerAI.py @@ -0,0 +1,121 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI + +class DistributedLobbyManagerAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedLobbyManagerAI") + + def announceGenerate(self): + DistributedObjectAI.announceGenerate(self) + self.lobbyId2Zone = {} + self.lobbyId2PlanningZone = {} + self.lobbyId2Host = {} + self.host2LobbyId = {} + self.avId2LobbyId = {} + self.id2Lobby = {} + self.pubLobbyInfo = {} + self.idPool = range(self.air.ourChannel, self.air.ourChannel + 100000) + + def receiveId(self, ids): + self.idPool += ids + + def _makeLobbyDict(self, struct): + lobby = {} + lobby['lobbyId'] = struct[0] + lobby['hostId'] = struct[1] + return lobby + + def lobbyManagerUdStartingUp(self): + self.notify.info("LobbyManager UD is starting") + + def lobbyManagerUdLost(self): + self.notify.warning("LobbyManager UD is lost") + + def addLobbyRequest(self, hostId): + simbase.air.globalLobbyMgr.sendAddLobby(hostId, self.host2LobbyId[hostId]) + + def addLobbyResponseUdToAi(self, lobbyId, errorCode, lobbyStruct): + avId = lobbyStruct[1] + self.sendUpdateToAvatarId(avId, 'addLobbyResponse', [avId, errorCode]) + self.air.doId2do[avId].sendUpdate('setHostedLobby', [[lobbyStruct]]) + pass + + def getLobbyZone(self, hostId, zoneId, isAvAboutToCreateLobby): + avId = self.air.getAvatarIdFromSender() + if isAvAboutToCreateLobby: + lobbyId = self.idPool.pop() + self.lobbyId2Host[lobbyId] = hostId + self.lobbyId2PlanningZone[lobbyId] = zoneId + self.host2LobbyId[hostId] = lobbyId + else: + if hostId not in self.host2LobbyId: + self.air.globalLobbyMgr.queryLobbyForHost(hostId) + return + lobbyId = self.host2LobbyId[hostId] + if lobbyId in self.lobbyId2Zone: + zoneId = self.lobbyId2Zone[lobbyId] + self.sendUpdateToAvatarId(avId, 'receiveLobbyZone', [hostId, lobbyId, zoneId]) + + def lobbyInfoOfHostResponseUdToAi(self, lobbyStruct): + lobby = self._makeLobbyDict(lobbyStruct) + av = self.air.doId2do.get(lobby['hostId'], None) + if not av: + return + lobbyId = lobby['lobbyId'] + zoneId = self.air.allocateZone() + self.lobbyId2Zone[lobbyId] = zoneId + self.host2LobbyId[lobby['hostId']] = lobbyId + + lobbyAI = DistributedLobbyAI(self.air, lobby['hostId'], zoneId, lobby) + lobbyAI.generateWithRequiredAndId(self.air.allocateChannel(), self.air.districtId, zoneId) + self.id2Lobby[lobbyId] = lobbyAI + + self.air.globalLobbyMgr.d_lobbyStarted(lobbyId, self.air.ourChannel, zoneId, av.getName()) + self.sendUpdateToAvatarId(lobby['hostId'], 'receiveLobbyZone', [lobby['hostId'], lobbyId, zoneId]) + + def closeLobby(self, lobbyId): + lobbyAI = self.id2Lobby[lobbyId] + self.air.globalLobbyMgr.d_lobbyDone(lobbyId) + for av in lobbyAI.avIdsInLobby: + self.sendUpdateToAvatarId(av, 'sendAvToPlayground', [av, 0]) + lobbyAI.b_setLobbyState(LobbyStatus.Finished) + taskMgr.doMethodLater(10, self.__deleteLobby, 'closeLobby%d' % lobbyId, extraArgs=[lobbyId]) + + def __deleteLobby(self, lobbyId): + lobbyAI = self.id2Lobby[lobbyId] + for av in lobbyAI.avIdsAtLobby: + self.sendUpdateToAvatarId(av, 'sendAvToPlayground', [av, 1]) + lobbyAI.requestDelete() + zoneId = self.lobbyId2Zone[lobbyId] + del self.lobbyId2Zone[lobbyId] + del self.id2Lobby[lobbyId] + del self.pubLobbyInfo[lobbyId] + self.air.deallocateZone(zoneId) + + def freeZoneIdFromLobby(self, hostId, zoneId): + sender = self.air.getAvatarIdFromSender() + lobbyId = self.host2LobbyId[hostId] + if lobbyId in self.lobbyId2PlanningZone: + self.air.deallocateZone(self.lobbyId2PlanningZone[lobbyId]) + del self.lobbyId2PlanningZone[lobbyId] + del self.host2LobbyId[hostId] + del self.lobbyId2Host[lobbyId] + + def exitLobby(self, lobbyZone): + avId = simbase.air.getAvatarIdFromSender() + for lobbyInfo in self.pubLobbyInfo.values(): + if lobbyInfo['zoneId'] == lobbyZone: + lobby = self.id2Lobby.get(lobbyInfo['lobbyId']) + if lobby: + lobby._removeAvatar(avId) + + def getPublicLobbies(self): + p = [] + for lobbyId in self.pubLobbyInfo: + lobby = self.pubLobbyInfo[lobbyId] + toons = lobby.get('numToons', 0) + if toons > 8: + toons = 8 + elif toons < 0: + toons = 0 + p.append([lobby['shardId'], lobby['zoneId'], toons, lobby.get('hostName', '')]) + return p diff --git a/toontown/uberdog/DistributedLobbyManagerUD.py b/toontown/uberdog/DistributedLobbyManagerUD.py new file mode 100644 index 00000000..33e4bbd0 --- /dev/null +++ b/toontown/uberdog/DistributedLobbyManagerUD.py @@ -0,0 +1,60 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectUD import DistributedObjectUD + +class DistributedLobbyManagerUD(DistributedObjectUD): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedLobbyManagerUD") + + def announceGenerate(self): + DistributedObjectUD.announceGenerate(self) + self.sendUpdate('lobbyManagerUdStartingUp') + + def addLobby(self, todo0, todo1, todo2, todo3): + pass + + def addLobbyRequest(self, hostId): + pass + + def addLobbyResponse(self, hostId, errorCode): + pass + + def getLobbyZone(self, avId, zoneId, isAvAboutToCreateLobby): + pass + + def receiveLobbyZone(self, todo0, todo1, todo2): + pass + + def freeZoneIdFromCreatedLobby(self, avId, zoneId): + pass + + def sendAvToPlayground(self, todo0, todo1): + pass + + def exitParty(self, zoneIdOfAv): + pass + + def lobbyManagerAIStartingUp(self, todo0, todo1): + pass + + def lobbyManagerAIGoingDown(self, todo0, todo1): + pass + + def lobbyHasStartedAiToUd(self, todo0, todo1, todo2, todo3, todo4): + pass + + def requestShardIdZoneIdForHostId(self, hostId): + pass + + def sendShardIdZoneIdToAvatar(self, shardId, zoneId): + pass + + def toonHasEnteredPartyAiToUd(self, todo0): + pass + + def toonHasExitedPartyAiToUd(self, todo0): + pass + + def lobbyHasFinishedUdToAllAi(self, todo0): + pass + + def lobbyManagerUdStartingUp(self): + pass diff --git a/toontown/uberdog/DistributedPartyManager.py b/toontown/uberdog/DistributedPartyManager.py new file mode 100755 index 00000000..82607988 --- /dev/null +++ b/toontown/uberdog/DistributedPartyManager.py @@ -0,0 +1,204 @@ +from direct.distributed.DistributedObject import DistributedObject +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal + +from otp.nametag.NametagConstants import * +from toontown.parties import PartyGlobals +from toontown.toon import ToonDNA +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals + + +class DistributedPartyManager(DistributedObject): + neverDisable = 1 + notify = directNotify.newCategory('DistributedPartyManager') + PartyStatusChangedEvent = 'changePartyStatusResponseReceived' + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + base.cr.partyManager = self + self.allowUnreleased = False + self.partyPlannerStyle = None + self.partyPlannerName = None + self.showDoid = False + return + + def delete(self): + DistributedObject.delete(self) + self.cr.partyManager = None + return + + def disable(self): + self.notify.debug("i'm disabling DistributedPartyManager rightnow.") + self.ignore('deallocateZoneIdFromPlannedParty') + self.ignoreAll() + DistributedObject.disable(self) + + def generate(self): + self.notify.debug('BASE: generate') + DistributedObject.generate(self) + self.accept('deallocateZoneIdFromPlannedParty', self.deallocateZoneIdFromPlannedParty) + self.announceGenerateName = self.uniqueName('generate') + + def deallocateZoneIdFromPlannedParty(self, zoneId): + self.sendUpdate('freeZoneIdFromPlannedParty', [base.localAvatar.doId, zoneId]) + + def allowUnreleasedClient(self): + return self.allowUnreleased + + def setAllowUnreleaseClient(self, newValue): + self.allowUnreleased = newValue + + def toggleAllowUnreleasedClient(self): + self.allowUnreleased = not self.allowUnreleased + return self.allowUnreleased + + def sendAddParty(self, hostId, startTime, endTime, isPrivate, inviteTheme, activities, decorations, inviteeIds): + self.sendUpdate('addPartyRequest', [hostId, + startTime, + endTime, + isPrivate, + inviteTheme, + activities, + decorations, + inviteeIds]) + + def addPartyResponse(self, hostId, errorCode): + messenger.send('addPartyResponseReceived', [hostId, errorCode]) + if hasattr(base.localAvatar, 'creatingNewPartyWithMagicWord'): + if base.localAvatar.creatingNewPartyWithMagicWord: + base.localAvatar.creatingNewPartyWithMagicWord = False + if errorCode == PartyGlobals.AddPartyErrorCode.AllOk: + base.localAvatar.setChatAbsolute('New party entered into database successfully.', CFSpeech | CFTimeout) + else: + base.localAvatar.setChatAbsolute('New party creation failed : %s' % PartyGlobals.AddPartyErrorCode.getString(errorCode), CFSpeech | CFTimeout) + + def requestPartyZone(self, avId, zoneId, callback): + if zoneId < 0: + zoneId = 0 + self.acceptOnce('requestPartyZoneComplete', callback) + if hasattr(base.localAvatar, 'aboutToPlanParty'): + if base.localAvatar.aboutToPlanParty: + self.sendUpdate('getPartyZone', [avId, zoneId, True]) + return + self.sendUpdate('getPartyZone', [avId, zoneId, False]) + + def receivePartyZone(self, hostId, partyId, zoneId): + if partyId != 0 and zoneId != 0: + if base.localAvatar.doId == hostId: + for partyInfo in base.localAvatar.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.status == PartyGlobals.PartyStatus.Started + + messenger.send('requestPartyZoneComplete', [hostId, partyId, zoneId]) + + def sendChangePrivateRequest(self, partyId, newPrivateStatus): + self.sendUpdate('changePrivateRequest', [partyId, newPrivateStatus]) + + def changePrivateResponse(self, partyId, newPrivateStatus, errorCode): + if errorCode == PartyGlobals.ChangePartyFieldErrorCode.AllOk: + self.notify.info('succesfully changed private field for the party') + for partyInfo in localAvatar.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.isPrivate = newPrivateStatus + + messenger.send('changePartyPrivateResponseReceived', [partyId, newPrivateStatus, errorCode]) + else: + messenger.send('changePartyPrivateResponseReceived', [partyId, newPrivateStatus, errorCode]) + self.notify.info('FAILED changing private field for the party') + + def sendChangePartyStatusRequest(self, partyId, newPartyStatus): + self.sendUpdate('changePartyStatusRequest', [partyId, newPartyStatus]) + + def changePartyStatusResponse(self, partyId, newPartyStatus, errorCode, beansRefunded): + self.notify.debug('changePartyStatusResponse : partyId=%s newPartyStatus=%s errorCode=%s' % (partyId, newPartyStatus, errorCode)) + for partyInfo in localAvatar.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.status = newPartyStatus + + messenger.send(self.PartyStatusChangedEvent, [partyId, + newPartyStatus, + errorCode, + beansRefunded]) + + def setNeverStartedPartyRefunded(self, partyId, newStatus, refund): + partyInfo = None + for pInfo in localAvatar.hostedParties: + if pInfo.partyId == partyId: + partyInfo = pInfo + break + + if partyInfo: + partyInfo.status = newStatus + messenger.send(self.PartyStatusChangedEvent, [partyId, + newStatus, + 0, + refund]) + return + + def sendAvToPlayground(self, avId, retCode): + messenger.send(PartyGlobals.KICK_TO_PLAYGROUND_EVENT, [retCode]) + self.notify.debug('sendAvToPlayground: %d' % avId) + + def leaveParty(self): + if self.isDisabled(): + self.notify.warning('DistributedPartyManager disabled; unable to leave party.') + return + self.sendUpdate('exitParty', [localAvatar.zoneId]) + + def removeGuest(self, ownerId, avId): + self.notify.debug('removeGuest ownerId = %s, avId = %s' % (ownerId, avId)) + self.sendUpdate('removeGuest', [ownerId, avId]) + + def isToonAllowedAtParty(self, avId, partyId): + return PartyGlobals.GoToPartyStatus.AllowedToGo + + def getGoToPartyFailedMessage(self, reason): + return '' + + def sendAvatarToParty(self, hostId): + DistributedPartyManager.notify.debug('sendAvatarToParty hostId = %s' % hostId) + self.sendUpdate('requestShardIdZoneIdForHostId', [hostId]) + + def sendShardIdZoneIdToAvatar(self, shardId, zoneId): + DistributedPartyManager.notify.debug('sendShardIdZoneIdToAvatar shardId = %s zoneId = %s' % (shardId, zoneId)) + if shardId == 0 or zoneId == 0: + base.cr.playGame.getPlace().handleBookClose() + return + hoodId = ToontownGlobals.PartyHood + if shardId == base.localAvatar.defaultShard: + shardId = None + base.cr.playGame.getPlace().requestLeave({'loader': 'safeZoneLoader', + 'where': 'party', + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': shardId, + 'avId': -1}) + return + + def setPartyPlannerStyle(self, dna): + self.partyPlannerStyle = dna + + def getPartyPlannerStyle(self): + if self.partyPlannerStyle: + return self.partyPlannerStyle + else: + dna = ToonDNA.ToonDNA() + dna.newToonRandom() + return dna + + def setPartyPlannerName(self, name): + self.partyPlannerName = name + + def getPartyPlannerName(self): + if self.partyPlannerName: + return self.partyPlannerName + else: + return TTLocalizer.PartyPlannerGenericName + + def toggleShowDoid(self): + self.showDoid = not self.showDoid + return self.showDoid + + def getShowDoid(self): + return self.showDoid diff --git a/toontown/uberdog/DistributedPartyManagerAI.py b/toontown/uberdog/DistributedPartyManagerAI.py new file mode 100755 index 00000000..6b4e84d2 --- /dev/null +++ b/toontown/uberdog/DistributedPartyManagerAI.py @@ -0,0 +1,288 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.task import Task +from otp.distributed.OtpDoGlobals import * +from panda3d.core import * +from toontown.parties.DistributedPartyAI import DistributedPartyAI +from datetime import datetime +from toontown.parties.PartyGlobals import * +from otp.ai.MagicWordGlobal import * + +class DistributedPartyManagerAI(DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyManagerAI") + + def announceGenerate(self): + DistributedObjectAI.announceGenerate(self) + self.partyId2Zone = {} + self.partyId2PlanningZone = {} + self.partyId2Host = {} + self.host2PartyId = {} + self.avId2PartyId = {} + self.id2Party = {} + self.pubPartyInfo = {} + self.idPool = range(self.air.ourChannel, self.air.ourChannel + 100000) + # get 100 ids at the start and top up + #taskMgr.doMethodLater(0, self.__getIds, 'DistributedPartyManagerAI___getIds') + + def receiveId(self, ids): + self.idPool += ids + +# def __getIds(self, task): +# if len(self.idPool) < 50: +# self.air.globalPartyMgr.allocIds(100 - len(self.idPool)) +# taskMgr.doMethodLater(180, self.__getIds, 'DistributedPartyManagerAI___getIds') + + def _makePartyDict(self, struct): + PARTY_TIME_FORMAT = '%Y-%m-%d %H:%M:%S' + party = {} + party['partyId'] = struct[0] + party['hostId'] = struct[1] + start = '%s-%s-%s %s:%s:00' % (struct[2], struct[3], struct[4], struct[5], struct[6]) + party['start'] = datetime.strptime(start, PARTY_TIME_FORMAT) + end = '%s-%s-%s %s:%s:00' % (struct[7], struct[8], struct[9], struct[10], struct[11]) + party['end'] = datetime.strptime(end, PARTY_TIME_FORMAT) + party['isPrivate'] = struct[12] + party['inviteTheme'] = struct[13] + party['activities'] = struct[14] + party['decorations'] = struct[15] + # struct[16] = partystatus + return party + + # Management stuff + def partyManagerUdStartingUp(self): + # This is sent in reply to the GPMAI's hello + self.notify.info("uberdog has said hello") + + def partyManagerUdLost(self): + # well fuck. ud died. + self.notify.warning("uberdog lost!") + + def canBuyParties(self): + return True + + + def addPartyRequest(self, hostId, startTime, endTime, isPrivate, inviteTheme, activities, decorations, inviteeIds): + if hostId != simbase.air.getAvatarIdFromSender(): + self.air.writeServerEvent('suspicious',simbase.air.getAvatarIdFromSender(),'Toon tried to create a party as someone else!') + return + print 'party requested: host %s, start %s, end %s, private %s, invitetheme %s, activities omitted, decor omitted, invitees %s' % (hostId, startTime, endTime, isPrivate, inviteTheme, inviteeIds) + simbase.air.globalPartyMgr.sendAddParty(hostId, self.host2PartyId[hostId], startTime, endTime, isPrivate, inviteTheme, activities, decorations, inviteeIds) + + def addPartyResponseUdToAi(self, partyId, errorCode, partyStruct): + avId = partyStruct[1] + print 'responding to client now that ud got back to us' + self.sendUpdateToAvatarId(avId, 'addPartyResponse', [avId, errorCode]) + # We also need to remember to update the field on the DToon indicating parties he's hosting + self.air.doId2do[avId].sendUpdate('setHostedParties', [[partyStruct]]) + pass + + def markInviteAsReadButNotReplied(self, todo0, todo1): + pass + + def respondToInvite(self, todo0, todo1, todo2, todo3, todo4): + pass + + def respondToInviteResponse(self, todo0, todo1, todo2, todo3, todo4): + pass + + def changePrivateRequest(self, todo0, todo1): + pass + + def changePrivateRequestAiToUd(self, todo0, todo1, todo2): + pass + + def changePrivateResponseUdToAi(self, todo0, todo1, todo2, todo3): + pass + + def changePrivateResponse(self, todo0, todo1, todo2): + pass + + def changePartyStatusRequest(self, partyId, newPartyStatus): + pass + + def changePartyStatusRequestAiToUd(self, todo0, todo1, todo2): + pass + + def changePartyStatusResponseUdToAi(self, todo0, todo1, todo2, todo3): + pass + + def changePartyStatusResponse(self, todo0, todo1, todo2, todo3): + pass + + def partyInfoOfHostFailedResponseUdToAi(self, todo0): + pass + + def givePartyRefundResponse(self, todo0, todo1, todo2, todo3, todo4): + pass + + def getPartyZone(self, hostId, zoneId, isAvAboutToPlanParty): + self.notify.debug('getPartyZone(hostId = %s, zoneId = %s, isAboutToPlan = %s' % (hostId, zoneId, isAvAboutToPlanParty)) + avId = self.air.getAvatarIdFromSender() + if isAvAboutToPlanParty: + partyId = self.idPool.pop() + print 'pid %s' % partyId + self.partyId2Host[partyId] = hostId + self.partyId2PlanningZone[partyId] = zoneId + self.host2PartyId[hostId] = partyId + print 'Responding to a get party zone when planning, av,party,zone: %s %s %s' % (avId, partyId, zoneId) + else: + if hostId not in self.host2PartyId: + # Uhh, we don't know if the host even has a party. Better ask the ud + self.air.globalPartyMgr.queryPartyForHost(hostId) + print 'querying for details against hostId %s ' % hostId + return + partyId = self.host2PartyId[hostId] + # Is the party already running? + if partyId in self.partyId2Zone: + # Yep! + zoneId = self.partyId2Zone[partyId] + else: + self.notify.warning("getPartyZone did not match a case!") + + self.sendUpdateToAvatarId(avId, 'receivePartyZone', [hostId, partyId, zoneId]) + + def partyInfoOfHostResponseUdToAi(self, partyStruct, inviteeIds): + party = self._makePartyDict(partyStruct) + av = self.air.doId2do.get(party['hostId'], None) + if not av: + return # The host isn't on the district... wat do + party['inviteeIds'] = inviteeIds + partyId = party['partyId'] + # This is issued in response to a request for the party to start, essentially. So let's alloc a zone + zoneId = self.air.allocateZone() + self.partyId2Zone[partyId] = zoneId + self.host2PartyId[party['hostId']] = partyId + + # We need to setup the party itself on our end, so make an ai party + partyAI = DistributedPartyAI(self.air, party['hostId'], zoneId, party) + partyAI.generateWithRequiredAndId(self.air.allocateChannel(), self.air.districtId, zoneId) + self.id2Party[partyId] = partyAI + + # Alert the UD + self.air.globalPartyMgr.d_partyStarted(partyId, self.air.ourChannel, zoneId, av.getName()) + + # Don't forget this was initially started by a getPartyZone, so we better tell the host the partyzone + self.sendUpdateToAvatarId(party['hostId'], 'receivePartyZone', [party['hostId'], partyId, zoneId]) + + # And last, set up our cleanup stuff + taskMgr.doMethodLater(PARTY_DURATION, self.closeParty, 'DistributedPartyManagerAI_cleanup%s' % partyId, [partyId]) + + def closeParty(self, partyId): + partyAI = self.id2Party[partyId] + self.air.globalPartyMgr.d_partyDone(partyId) + for av in partyAI.avIdsAtParty: + self.sendUpdateToAvatarId(av, 'sendAvToPlayground', [av, 0]) + partyAI.b_setPartyState(PartyStatus.Finished) + taskMgr.doMethodLater(10, self.__deleteParty, 'closeParty%d' % partyId, extraArgs=[partyId]) + + def __deleteParty(self, partyId): + partyAI = self.id2Party[partyId] + for av in partyAI.avIdsAtParty: + self.sendUpdateToAvatarId(av, 'sendAvToPlayground', [av, 1]) + partyAI.requestDelete() + zoneId = self.partyId2Zone[partyId] + del self.partyId2Zone[partyId] + del self.id2Party[partyId] + del self.pubPartyInfo[partyId] + self.air.deallocateZone(zoneId) + + + def freeZoneIdFromPlannedParty(self, hostId, zoneId): + sender = self.air.getAvatarIdFromSender() + # Only the host of a party can free its zone + if sender != hostId: + self.air.writeServerEvent('suspicious',sender,'Toon tried to free zone for someone else\'s party!') + return + partyId = self.host2PartyId[hostId] + if partyId in self.partyId2PlanningZone: + self.air.deallocateZone(self.partyId2PlanningZone[partyId]) + del self.partyId2PlanningZone[partyId] + del self.host2PartyId[hostId] + del self.partyId2Host[partyId] + return + + def sendAvToPlayground(self, todo0, todo1): + pass + + def exitParty(self, partyZone): + avId = simbase.air.getAvatarIdFromSender() + for partyInfo in self.pubPartyInfo.values(): + if partyInfo['zoneId'] == partyZone: + party = self.id2Party.get(partyInfo['partyId']) + if party: + party._removeAvatar(avId) + + + + def removeGuest(self, ownerId, avId): + pass + + def partyManagerAIStartingUp(self, todo0, todo1): + pass + + def partyManagerAIGoingDown(self, todo0, todo1): + pass + + def toonHasEnteredPartyAiToUd(self, todo0): + pass + + def toonHasExitedPartyAiToUd(self, todo0): + pass + + def partyHasFinishedUdToAllAi(self, partyId): + # FIXME I bet i have to do some cleanup + del self.pubPartyInfo[partyId] + + def updateToPublicPartyInfoUdToAllAi(self, shardId, zoneId, partyId, hostId, numGuests, maxGuests, hostName, activities, minLeft): + # The uberdog is informing us of a public party. + # Note that we never update the publicPartyInfo of our own parties without going through the UD. It's just good practice :) + started = None + self.pubPartyInfo[partyId] = { + 'shardId': shardId, + 'zoneId': zoneId, + 'partyId': partyId, + 'hostId': hostId, + 'numGuests': numGuests, + 'maxGuests': maxGuests, + 'hostName': hostName, + 'minLeft': minLeft, + 'started': datetime.now(), + 'activities': activities } + + def updateToPublicPartyCountUdToAllAi(self, partyCount, partyId): + # Update the number of guests at a party + if partyId in self.pubPartyInfo.keys(): + self.pubPartyInfo[partyId]['numGuests'] = partyCount + + def getPublicParties(self): + p = [] + for partyId in self.pubPartyInfo: + party = self.pubPartyInfo[partyId] + # calculate time left + minLeft = party['minLeft'] - int((datetime.now() - party['started']).seconds / 60) + #less band-aidy bandaid + guests = party.get('numGuests', 0) + if guests > 255: + guests = 255 + elif guests < 0: + guests = 0 + p.append([party['shardId'], party['zoneId'], guests, party.get('hostName', ''), party.get('activities', []), minLeft]) + return p + + def requestShardIdZoneIdForHostId(self, todo0): + pass + + def sendShardIdZoneIdToAvatar(self, todo0, todo1): + pass + + def updateAllPartyInfoToUd(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6, todo7, todo8): + pass + + def forceCheckStart(self): + pass + + def requestMw(self, todo0, todo1, todo2, todo3): + pass + + def mwResponseUdToAllAi(self, todo0, todo1, todo2, todo3): + pass diff --git a/toontown/uberdog/DistributedPartyManagerUD.py b/toontown/uberdog/DistributedPartyManagerUD.py new file mode 100755 index 00000000..c93addd2 --- /dev/null +++ b/toontown/uberdog/DistributedPartyManagerUD.py @@ -0,0 +1,130 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObjectUD import DistributedObjectUD + +class DistributedPartyManagerUD(DistributedObjectUD): + notify = DirectNotifyGlobal.directNotify.newCategory("DistributedPartyManagerUD") + + def announceGenerate(self): + DistributedObjectUD.announceGenerate(self) + + self.sendUpdate('partyManagerUdStartingUp') # Shouldn't have to send to anyone special, as the field is airecv + + def addParty(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6, todo7, todo8, todo9): + pass + + def addPartyRequest(self, hostId, startTime, endTime, isPrivate, inviteTheme, activities, decorations, inviteeIds): + pass + + def addPartyResponse(self, hostId, errorCode): + pass + + def addPartyResponseUdToAi(self, todo0, todo1, todo2): + pass + + def markInviteAsReadButNotReplied(self, todo0, todo1): + pass + + def respondToInvite(self, todo0, todo1, todo2, todo3, todo4): + pass + + def respondToInviteResponse(self, todo0, todo1, todo2, todo3, todo4): + pass + + def changePrivateRequest(self, todo0, todo1): + pass + + def changePrivateRequestAiToUd(self, todo0, todo1, todo2): + pass + + def changePrivateResponseUdToAi(self, todo0, todo1, todo2, todo3): + pass + + def changePrivateResponse(self, todo0, todo1, todo2): + pass + + def changePartyStatusRequest(self, partyId, newPartyStatus): + pass + + def changePartyStatusRequestAiToUd(self, todo0, todo1, todo2): + pass + + def changePartyStatusResponseUdToAi(self, todo0, todo1, todo2, todo3): + pass + + def changePartyStatusResponse(self, todo0, todo1, todo2, todo3): + pass + + def partyInfoOfHostRequestAiToUd(self, todo0, todo1): + pass + + def partyInfoOfHostFailedResponseUdToAi(self, todo0): + pass + + def partyInfoOfHostResponseUdToAi(self, todo0, todo1): + pass + + def givePartyRefundResponse(self, todo0, todo1, todo2, todo3, todo4): + pass + + def getPartyZone(self, avId, zoneId, isAvAboutToPlanParty): + pass + + def receivePartyZone(self, todo0, todo1, todo2): + pass + + def freeZoneIdFromPlannedParty(self, avId, zoneId): + pass + + def sendAvToPlayground(self, todo0, todo1): + pass + + def exitParty(self, zoneIdOfAv): + pass + + def removeGuest(self, ownerId, avId): + pass + + def partyManagerAIStartingUp(self, todo0, todo1): + pass + + def partyManagerAIGoingDown(self, todo0, todo1): + pass + + def partyHasStartedAiToUd(self, todo0, todo1, todo2, todo3, todo4): + pass + + def toonHasEnteredPartyAiToUd(self, todo0): + pass + + def toonHasExitedPartyAiToUd(self, todo0): + pass + + def partyHasFinishedUdToAllAi(self, todo0): + pass + + def updateToPublicPartyInfoUdToAllAi(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6, todo7, todo8): + pass + + def updateToPublicPartyCountUdToAllAi(self, todo0, todo1): + pass + + def requestShardIdZoneIdForHostId(self, hostId): + pass + + def sendShardIdZoneIdToAvatar(self, shardId, zoneId): + pass + + def partyManagerUdStartingUp(self): + pass + + def updateAllPartyInfoToUd(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6, todo7, todo8): + pass + + def forceCheckStart(self): + pass + + def requestMw(self, todo0, todo1, todo2, todo3): + pass + + def mwResponseUdToAllAi(self, todo0, todo1, todo2, todo3): + pass diff --git a/toontown/uberdog/GlobalLobbyManager.py b/toontown/uberdog/GlobalLobbyManager.py new file mode 100644 index 00000000..98b047cd --- /dev/null +++ b/toontown/uberdog/GlobalLobbyManager.py @@ -0,0 +1,6 @@ +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal +from direct.distributed.PyDatagram import * +from direct.directnotify.DirectNotifyGlobal import directNotify + +class GlobalLobbyManager(DistributedObjectGlobal): + notify = directNotify.newCategory('GlobalLobbyManager') diff --git a/toontown/uberdog/GlobalLobbyManagerAI.py b/toontown/uberdog/GlobalLobbyManagerAI.py new file mode 100644 index 00000000..f6a98a11 --- /dev/null +++ b/toontown/uberdog/GlobalLobbyManagerAI.py @@ -0,0 +1,52 @@ +from direct.distributed.DistributedObjectGlobalAI import DistributedObjectGlobalAI +from direct.distributed.PyDatagram import * +from direct.directnotify.DirectNotifyGlobal import directNotify + +class GlobalLobbyManagerAI(DistributedObjectGlobalAI): + notify = directNotify.newCategory('GlobalLobbyManagerAI') + + def announceGenerate(self): + DistributedObjectGlobalAI.announceGenerate(self) + self.sendUpdate('lobbyManagerAIHello', [simbase.air.lobbyManager.doId]) + + def sendAddLobby(self, avId, lobbyId): + self.sendUpdate('addLobby', [avId, lobbyId]) + + def queryLobbyForHost(self, hostId): + self.sendUpdate('queryLobby', [hostId]) + + def d_lobbyStarted(self, lobbyId, shardId, zoneId, hostName): + self.sendUpdate('lobbyHasStarted', [lobbyId, shardId, zoneId, hostName]) + + def lobbyStarted(self, lobbyId, shardId, zoneId, hostName): + pass + + def d_lobbyDone(self, lobbyId): + self.sendUpdate('lobbyDone', [lobbyId]) + + def lobbyDone(self, lobbyId): + pass + + def d_toonJoinedLobby(self, lobbyId, avId): + self.sendUpdate('toonJoinedLobby', [lobbyId, avId]) + + def toonJoinedLobby(self, lobbyId, avId): + pass + + def d_toonLeftLobby(self, lobbyId, avId): + self.sendUpdate('toonLeftLobby', [lobbyId, avId]) + + def toonLeftLobby(self, lobbyId, avId): + pass + + def d_requestLobbySlot(self, lobbyId, avId): + self.sendUpdate('requestLobbySlot', [lobbyId, avId]) + + def requestLobbySlot(self, lobbyId, avId): + pass + + def d_allocIds(self, numIds): + self.sendUpdate('allocIds', [numIds]) + + def allocIds(self, numIds): + pass diff --git a/toontown/uberdog/GlobalLobbyManagerUD.py b/toontown/uberdog/GlobalLobbyManagerUD.py new file mode 100644 index 00000000..0c87ba0e --- /dev/null +++ b/toontown/uberdog/GlobalLobbyManagerUD.py @@ -0,0 +1,124 @@ +from direct.distributed.DistributedObjectGlobalUD import DistributedObjectGlobalUD +from direct.distributed.PyDatagram import * +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.task import Task +from LobbyGlobals import * +from datetime import datetime, timedelta +from panda3d.core import * + +class GlobalLobbyManagerUD(DistributedObjectGlobalUD): + notify = directNotify.newCategory('GlobalLobbyManagerUD') + + def announceGenerate(self): + DistributedObjectGlobalUD.announceGenerate(self) + self.notify.debug("GLMUD generated") + self.senders2Mgrs = {} + self.host2LobbyId = {} + self.id2Lobby = {} + self.lobby2PubInfo = {} + self.tempSlots = {} + self.lobbyAllocator = UniqueIdAllocator(0, 100000000) + + def _makeAIMsg(self, field, values, recipient): + return self.air.dclassesByName['DistributedLobbyManagerUD'].getFieldByName(field).aiFormatUpdate(recipient, recipient, simbase.air.ourChannel, values) + + def sendToAI(self, field, values, sender=None): + if not sender: + sender = self.air.getAvatarIdFromSender() + dg = self._makeAIMsg(field, values, self.senders2Mgrs.get(sender, sender + 8)) + self.air.send(dg) + + def _makeAvMsg(self, field, values, recipient): + return self.air.dclassesByName['DistributedToonUD'].getFieldByName(field).aiFormatUpdate(recipient, recipient, simbase.air.ourChannel, values) + + def sendToAv(self, avId, field, values): + dg = self._makeAvMsg(field, values, avId) + self.air.send(dg) + + def _formatLobby(self, lobbyDict): + return [lobbyDict['lobbyId'], lobbyDict['hostId']] + + def avatarJoined(self, avId): + lobbyId = self.host2LobbyId.get(avId, None) + if lobbyId: + lobby = self.id2Lobby.get(lobbyId, None) + if not lobby: + return + self.sendToAv(avId, 'setHostedLobby', [[self._formatLobby(lobby)]]) + + def __updateLobbyCount(self, lobbyId): + for sender in self.senders2Mgrs.keys(): + self.sendToAI('updateToPublicLobbyCountUdToAllAi', [self.lobby2PubInfo[lobbyId]['numGuests'], lobbyId], sender=sender) + + def lobbyDone(self, lobbyId): + del self.lobby2PubInfo[lobbyId] + self.id2Lobby[lobbyId]['status'] = LobbyStatus.Finished + lobby = self.id2Lobby.get(lobbyId, None) + self.sendToAv(lobby['hostId'], 'setHostedLobby', [[self._formatLobby(lobby)]]) + del self.id2Lobby[lobbyId] + self.air.writeServerEvent('lobby-done', '%s') + + def toonJoinedLobby(self, lobbyId, avId): + if avId in self.tempSlots: + del self.tempSlots[avId] + return + self.lobby2PubInfo.get(lobbyId, {'numGuests': 0})['numGuests'] += 1 + self.__updateLobbyCount(lobbyId) + + def toonLeftLobby(self, lobbyId, avId): + self.lobby2PubInfo.get(lobbyId, {'numGuests': 0})['numGuests'] -= 1 + self.__updateLobbyCount(lobbyId) + + def lobbyManagerAIHello(self, channel): + print 'AI with base channel %s, will send replies to DPM %s' % (simbase.air.getAvatarIdFromSender(), channel) + self.senders2Mgrs[simbase.air.getAvatarIdFromSender()] = channel + self.sendToAI('lobbyManagerUdStartingUp', []) + self.air.addPostRemove(self._makeAIMsg('lobbyManagerUdLost', [], channel)) + + def addLobby(self, avId, lobbyId): + if avId in self.host2LobbyId: + self.sendToAI('addLobbyResponseUdToAi', [lobbyId, AddLobbyErrorCode.TooManyHostedLobbies, self._formatLobby(self.id2Lobby[lobbyId])]) + self.id2Lobby[lobbyId] = {'lobbyId': lobbyId, 'hostId': avId} + self.host2LobbyId[avId] = lobbyId + self.sendToAI('addLobbyResponseUdToAi', [lobbyId, AddLobbyErrorCode.AllOk, self._formatLobby(self.id2Lobby[lobbyId])]) + + def queryLobby(self, hostId): + if hostId in self.host2LobbyId: + lobby = self.id2Lobby[self.host2LobbyId[hostId]] + self.sendToAI('lobbyInfoOfHostResponseUdToAi', [self._formatLobby(lobby)]) + return + print 'query failed, av %s isnt hosting anything' % hostId + + def requestLobbySlot(self, lobbyId, avId): + lobby = self.lobby2PubInfo[lobbyId] + if lobby['numGuests'] >= lobby['maxGuests']: + recipient = self.GetPuppetConnectionChannel(avId) + sender = simbase.air.getAvatarIdFromSender() + #dg = self.air.dclassesByName['DistributedPartyGateAI'].getFieldByName('partyRequestDenied').aiFormatUpdate(gateId, recipient, sender, [PartyGateDenialReasons.Full]) + #self.air.send(dg) + return + lobby['numGuests'] += 1 + self.__updateLobbyCount(lobbyId) + self.tempSlots[avId] = lobbyId + + taskMgr.doMethodLater(60, self._removeTempSlot, 'lobbyManagerTempSlot%d' % avId, extraArgs=[avId]) + + info = [lobby['shardId'], lobby['zoneId'], lobby['numGuests'], lobby['hostName']] + hostId = self.id2Lobby[lobby['lobbyId']]['hostId'] + recipient = self.GetPuppetConnectionChannel(avId) + sender = simbase.air.getAvatarIdFromSender() + #dg = self.air.dclassesByName['DistributedPartyGateAI'].getFieldByName('setParty').aiFormatUpdate(gateId, recipient, sender, [info, hostId]) + #self.air.send(dg) + + def _removeTempSlot(self, avId): + lobbyId = self.tempSlots.get(avId) + if lobbyId: + del self.tempSlots[avId] + self.lobby2PubInfo.get(lobbyId, {'numGuests': 0})['numGuests'] -= 1 + self.__updateLobbyCount(lobbyId) + + def allocIds(self, numIds): + ids = [] + while len(ids) < numIds: + ids.append(self.lobbyAllocator.allocate()) + self.sendToAI('receiveId', ids) diff --git a/toontown/uberdog/LobbyGlobals.py b/toontown/uberdog/LobbyGlobals.py new file mode 100644 index 00000000..d82b3884 --- /dev/null +++ b/toontown/uberdog/LobbyGlobals.py @@ -0,0 +1,24 @@ +from pandac.PandaModules import BitMask32 +from pandac.PandaModules import Point3, VBase4 +from direct.showbase import PythonUtil +from toontown.toonbase import TTLocalizer +KICK_TO_PLAYGROUND_EVENT = 'lobbies_kickToPlayground' +UberdogCheckLobbyStartFrequency = 5.0 +UberdogPurgeLobbyPeriod = 24.0 +UberdogLobbiesSanityCheckFrequency = 60 +MaxToonsAtALobby = 8 +ActivityRequestStatus = PythonUtil.Enum(('Joining', 'Exiting')) +InviteStatus = PythonUtil.Enum(('NotRead', + 'ReadButNotReplied', + 'Accepted', + 'Rejected')) +PartyStatus = PythonUtil.Enum(('Pending', + 'Cancelled', + 'Finished', + 'CanStart', + 'Started', + 'NeverStarted')) +AddPartyErrorCode = PythonUtil.Enum(('AllOk', + 'ValidationError', + 'DatabaseError', + 'TooManyHostedParties')) diff --git a/toontown/uberdog/NameJudgeBlacklist.py b/toontown/uberdog/NameJudgeBlacklist.py new file mode 100755 index 00000000..c2897e22 --- /dev/null +++ b/toontown/uberdog/NameJudgeBlacklist.py @@ -0,0 +1 @@ +blacklist = ['phallic', 'phallus', 'anal', 'anus', 'ass', 'assmunch', 'ballsack', 'bastard', 'beaner', 'beastiality', 'biatch', 'beeyotch', 'bitch', 'bitchy', 'blowjob', 'bollock', 'bollocks', 'bollok', 'boner', 'boob', 'bugger', 'buttplug', 'jerk', 'jackoff', 'jackhole', 'homo', 'hobag', 'hell', 'goddamn', 'goddammit', 'godamnit', 'ghey', 'ghay', 'gfy', 'gay', 'fudgepacker', 'fuckwad', 'fucktard', 'fuckoff', 'fucker', 'fuck', 'fellatio', 'fellate', 'felching', 'felcher', 'felch', 'fartknocker', 'fart', 'fannybandit', 'faggot', 'fagg', 'fag', 'dyke', 'douchebag', 'douche', 'doosh', 'dildo', 'dike', 'dick', 'damnit', 'damn', 'dammit', 'cunt', 'cumstain', 'cum', 'crap', 'coon', 'cock', 'clitoris', 'clit', 'cawk', 'jerkoff', 'jizz', 'knobend', 'labia', 'lmfao', 'lul', 'moolie', 'muff', 'nigga', 'nigger', 'penis', 'piss', 'pissoff', 'prick', 'pube', 'pussy', 'queer', 'retard', 'retarded', 'scrotum', 'sex', 'shit', 'slut', 'smegma', 'tard', 'terd', 'tit', 'tits', 'titties', 'turd', 'twat', 'vag', 'vagina', 'wank', 'wetback', 'whore', 'whoreface', 'diligaf', 'wtf', 'stfu', 'fack', 'shite', 'fxck', 'mofo', 'cuntlicker', 'cuntface', 'dickbag', 'cockknocker', 'beatch', 'fucknut', 'mams', 'cunny', 'quim', 'clitty', 'kike', 'spic', 'wop', 'chink', 'feltch', 'feltcher', 'FvCk', 'ahole', 'nads', 'spick', 'douchey', 'Bullturds', 'gonads', 'bitch', 'butt', 'fellatio', 'lmao', 'spunk', 'jizm', 'jism', 'bukkake', 'shiz', 'wigger', 'gook', 'ritard', 'reetard', 'masterbate', 'masturbate', 'goatse', 'masterbating', 'masturbating', 'hitler', 'nazi', 'tubgirl', 'GTFO', 'FOAD', 'rtard', 'hoor', 'gspot', 'vulva', 'assmaster', 'viagra', 'Phuck', 'frack', 'fuckwit', 'assbang', 'assbanged', 'assbangs', 'asshole', 'assholes', 'asswipe', 'asswipes', 'bastards', 'bitched', 'bitches', 'boners', 'bullshit', 'bullshits', 'bullshitted', 'cameltoe', 'chinc', 'chincs', 'chink', 'chode', 'chodes', 'clit', 'clits', 'cocks', 'coons', 'cumming', 'cunts', 'dickhead', 'dickheads', 'dildos', 'douchebags', 'dumass', 'dumbasses', 'dykes', 'faggit', 'fags', 'fucked', 'fucker', 'fuckface', 'fucks', 'godamnit', 'gooks', 'humped', 'humping', 'jackass', 'jap', 'japs', 'jizzed', 'kikes', 'knobend', 'kooch', 'kooches', 'kootch', 'motherfucking', 'niggah', 'niggas', 'niggers', 'pussies', 'queers', 'shitface', 'shithead', 'shits', 'shitted', 'spik', 'spiks', 'twats', 'whores', 'zoophile', 'mthrfucking', 'muthrfucking', 'mutherfucking', 'mutherfucker', 'mtherfucker', 'mthrfucker', 'whorehopper', 'whoralicious', 'whorealicious', 'aeolus', 'Analprobe', 'Areola', 'areole', 'aryan', 'arian', 'asses', 'assfuck', 'azazel', 'baal', 'Babes', 'bang', 'banger', 'Barf', 'bawdy', 'Beardedclam', 'beater', 'Beaver', 'beer', 'bigtits', 'bimbo', 'Blew', 'blow', 'blowjobs', 'blowup', 'bod', 'bodily', 'boink', 'Bone', 'boned', 'bong', 'Boobies', 'Boobs', 'booby', 'booger', 'Bookie', 'Booky', 'bootee', 'bootie', 'Booty', 'Booze', 'boozer', 'boozy', 'bosom', 'bosomy', 'bowel', 'bowels', 'bra', 'Brassiere', 'breast', 'breasts', 'bung', 'babe', 'bush', 'buttfuck', 'cocaine', 'kinky', 'klan', 'panties', 'pedophile', 'pedophilia', 'pedophiliac', 'punkass', 'queaf', 'rape', 'scantily', 'essohbee', 'shithouse', 'smut', 'snatch', 'toots', 'anorexia', 'bulimia', 'bulimiic', 'burp', 'busty', 'Buttfucker', 'caca', 'cahone', 'Carnal', 'Carpetmuncher', 'cervix', 'climax', 'Cocain', 'Cocksucker', 'Coital', 'coke', 'commie', 'condom', 'corpse', 'Coven', 'Crabs', 'crack', 'Crackwhore', 'crappy', 'cuervo', 'Cummin', 'Cumshot', 'cumshots', 'Cunnilingus', 'dago', 'dagos', 'damned', 'dickish', 'Dickweed', 'anorexic', 'prostitute', 'marijuana', 'LSD', 'PCP', 'diddle', 'dimwit', 'dingle', 'doofus', 'dopey', 'douche', 'Drunk', 'Dummy', 'Ejaculate', 'enlargement', 'erect', 'erotic', 'exotic', 'extacy', 'Extasy', 'faerie', 'faery', 'fagged', 'fagot', 'Fairy', 'fisted', 'fisting', 'Fisty', 'floozy', 'fondle', 'foobar', 'foreskin', 'frigg', 'frigga', 'fubar', 'Fucking', 'fuckup', 'ganja', 'gays', 'glans', 'godamn', 'goddam', 'Goldenshower', 'gonad', 'gonads', 'Handjob', 'hebe', 'hemp', 'heroin', 'herpes', 'hijack', 'Hiv', 'Homey', 'Honky', 'hooch', 'hookah', 'Hooker', 'Hootch', 'hooter', 'hooters', 'hump', 'hussy', 'hymen', 'inbred', 'incest', 'injun', 'jerked', 'Jiz', 'Jizm', 'horny', 'junkie', 'junky', 'kill', 'kkk', 'kraut', 'kyke', 'lech', 'leper', 'lesbians', 'lesbos', 'Lez', 'Lezbian', 'lezbians', 'Lezbo', 'Lezbos', 'Lezzie', 'Lezzies', 'Lezzy', 'loin', 'loins', 'lube', 'Lust', 'lusty', 'Massa', 'Masterbation', 'Masturbation', 'maxi', 'Menses', 'Menstruate', 'Menstruation', 'meth', 'molest', 'moron', 'Motherfucka', 'Motherfucker', 'murder', 'Muthafucker', 'nad', 'naked', 'napalm', 'Nappy', 'nazism', 'negro', 'niggle', 'nimrod', 'ninny', 'Nipple', 'nooky', 'Nympho', 'Opiate', 'opium', 'oral', 'orally', 'organ', 'orgasm', 'orgies', 'orgy', 'ovary', 'ovum', 'ovums', 'Paddy', 'pantie', 'panty', 'Pastie', 'pasty', 'Pecker', 'pedo', 'pee', 'Peepee', 'Penetrate', 'Penetration', 'penial', 'penile', 'perversion', 'peyote', 'phalli', 'Phallic', 'Pillowbiter', 'pimp', 'pinko', 'pissed', 'pms', 'polack', 'porn', 'porno', 'pornography', 'pot', 'potty', 'prig', 'prude', 'pubic', 'pubis', 'punky', 'puss', 'Queef', 'quicky', 'Racist', 'racy', 'raped', 'Raper', 'rapist', 'raunch', 'rectal', 'rectum', 'rectus', 'reefer', 'reich', 'revue', 'risque', 'rum', 'rump', 'sadism', 'sadist', 'satan', 'scag', 'schizo', 'screw', 'Screwed', 'scrog', 'Scrot', 'Scrote', 'scrud', 'scum', 'seaman', 'seamen', 'seduce', 'semen', 'sexual', 'Shithole', 'Shitter', 'shitty', 'sissy', 'skag', 'slave', 'sleaze', 'sleazy', 'sluts', 'smutty', 'sniper', 'snuff', 'sodom', 'souse', 'soused', 'sperm', 'spooge', 'Stab', 'steamy', 'Stiffy', 'stoned', 'strip', 'Stroke', 'suck', 'sucked', 'sucking', 'tampon', 'tawdry', 'teat', 'teste', 'testee', 'testes', 'Testis', 'thrust', 'thug', 'tinkle', 'Titfuck', 'titi', 'titty', 'toke', 'tramp', 'trashy', 'tush', 'undies', 'unwed', 'urinal', 'urine', 'uterus', 'uzi', 'valium', 'virgin', 'vixen', 'vodka', 'vomit', 'voyeur', 'vulgar', 'wad', 'wazoo', 'wedgie', 'weed', 'weenie', 'weewee', 'weiner', 'weirdo', 'wench', 'whitey', 'whiz', 'Whored', 'Whorehouse', 'Whoring', 'womb', 'woody', 'xxx', 'yeasty', 'yobbo', 'sumofabiatch', 'wang', 'dong', 'tittyfuck', 'tittyfucker', 'tittiefucker', 'cockholster', 'cockblock', 'gai', 'gey', 'faig', 'faigt', 'gae', 'corksucker', 'rumprammer', 'slutdumper', 'niggaz', 'muthafuckaz', 'gigolo', 'pussypounder', 'herp', 'herpy', 'transsexual', 'orgasmic', 'cunilingus', 'anilingus', 'dickdipper', 'dickwhipper', 'dicksipper', 'dickripper', 'dickflipper', 'dickzipper', 'homoey', 'queero', 'freex', 'cunthunter', 'shamedame', 'slutkiss', 'shiteater', 'fuckass', 'clitorus', 'assfucker', 'dillweed', 'cracker', 'teabagging', 'shitt', 'azz', 'fuk', 'fucknugget', 'cuntlick', 'beotch', 'anal', 'anus', 'arrse', 'arse', 'ass', 'asses', 'assfucker', 'assfukka', 'asshole', 'assholes', 'asswhole', 'ballbag', 'balls', 'ballsack', 'bastard', 'batard', 'beastial', 'beastiality', 'bellend', 'bestial', 'bestiality', 'biatch', 'bitch', 'bitcher', 'bitchers', 'bitches', 'bitchin', 'bitching', 'bloody', 'blowjob', 'blowjobs', 'boiolas', 'bollock', 'bollok', 'boner', 'boob', 'boobs', 'booobs', 'boooobs', 'booooobs', 'booooooobs', 'breasts', 'buceta', 'bugger', 'bum', 'butt', 'butthole', 'buttmuch', 'buttplug', 'cawk', 'chink', 'chieuse', 'chieur', 'cipa', 'clit', 'clitoris', 'clits', 'cnut', 'cock', 'cockface', 'cockhead', 'cockmunch', 'cockmuncher', 'cocks', 'cocksuck', 'cocksucked', 'cocksucker', 'cocksucking', 'cocksucks', 'cocksuka', 'cocksukka', 'cok', 'cokmuncher', 'coksucka', 'coon', 'connard', 'connasse', 'conne', 'cox', 'crap', 'cum', 'cummer', 'cumming', 'cums', 'cumshot', 'cunilingus', 'cunillingus', 'cunnilingus', 'cunt', 'cuntlick', 'cuntlicker', 'cuntlicking', 'cunts', 'cyalis', 'cyberfuc', 'cyberfuck', 'cyberfucked', 'cyberfucker', 'cyberfuckers', 'cyberfucking', 'damn', 'dick', 'dickhead', 'dildo', 'dildos', 'dink', 'dinks', 'dirsa', 'dlck', 'doggin', 'dogging', 'donkeyribber', 'doosh', 'duche', 'dyke', 'ejaculate', 'ejaculated', 'ejaculates', 'ejaculating', 'ejaculatings', 'ejaculation', 'ejakulate', 'enculer', 'fag', 'fagging', 'faggitt', 'faggot', 'faggs', 'fagot', 'fagots', 'fags', 'fanny', 'fannyflaps', 'fannyfucker', 'fanyy', 'fatass', 'fcuk', 'fcuker', 'fcuking', 'feck', 'fecker', 'felching', 'fellate', 'fellatio', 'fingerfuck', 'fingerfucked', 'fingerfucker', 'fingerfuckers', 'fingerfucking', 'fingerfucks', 'fistfuck', 'fistfucked', 'fistfucker', 'fistfuckers', 'fistfucking', 'fistfuckings', 'fistfucks', 'flange', 'fook', 'fooker', 'fuck', 'fucka', 'fucked', 'fucker', 'fuckers', 'fuckhead', 'fuckheads', 'fuckin', 'fucking', 'fuckings', 'fuckingshitmotherfucker', 'fuckme', 'fucks', 'fuckwhit', 'fuckwit', 'fudgepacker', 'fuk', 'fuker', 'fukker', 'fukkin', 'fuks', 'fukwhit', 'fukwit', 'fux', 'gangbang', 'gangbanged', 'gangbangs', 'gaylord', 'gaysex', 'goatse', 'God', 'goddamn', 'goddamned', 'hardcoresex', 'hell', 'heshe', 'hoar', 'hoare', 'hoer', 'homo', 'hore', 'horniest', 'horny', 'hotsex', 'jackoff', 'jap', 'jism', 'jiz', 'jizm', 'jizz', 'kawk', 'knob', 'knobead', 'knobed', 'knobend', 'knobhead', 'knobjocky', 'knobjokey', 'kock', 'kondum', 'kondums', 'kum', 'kummer', 'kumming', 'kums', 'kunilingus', 'labia', 'lmfao', 'lust', 'lusting', 'masochist', 'masterbat', 'masterbate', 'masterbation', 'masterbations', 'masturbate', 'merde', 'mofo', 'mothafuck', 'mothafucka', 'mothafuckas', 'mothafuckaz', 'mothafucked', 'mothafucker', 'mothafuckers', 'mothafuckin', 'mothafucking', 'mothafuckings', 'mothafucks', 'motherfuck', 'motherfucked', 'motherfucker', 'motherfuckers', 'motherfuckin', 'motherfucking', 'motherfuckings', 'motherfuckka', 'motherfucks', 'muff', 'mutha', 'muthafecker', 'muthafuckker', 'muther', 'mutherfucker', 'nazi', 'nigga', 'niggah', 'niggas', 'niggaz', 'nigger', 'niggers', 'nique', 'niquer', 'nob', 'nobhead', 'nobjocky', 'nobjokey', 'numbnuts', 'nutsack', 'orgasim', 'orgasims', 'orgasm', 'orgasms', 'pawn', 'pecker', 'penis', 'penisfucker', 'phonesex', 'phuck', 'phuk', 'phuked', 'phuking', 'phukked', 'phukking', 'phuks', 'phuq', 'pigfucker', 'pimpis', 'piss', 'pissed', 'pisser', 'pissers', 'pisses', 'pissflaps', 'pissin', 'pissing', 'pissoff', 'poop', 'porn', 'porno', 'pornography', 'pornos', 'prick', 'pron', 'pube', 'pusse', 'pussi', 'pussies', 'pussy', 'pute', 'putain', 'rectum', 'retard', 'rimjaw', 'rimming', 'sadist', 'salaud', 'salop', 'salope', 'saloperie', 'schlong', 'screwing', 'scroat', 'scrote', 'scrotum', 'semen', 'sex', 'shag', 'shagger', 'shaggin', 'shagging', 'shemale', 'shit', 'shitdick', 'shite', 'shited', 'shitey', 'shitfuck', 'shitfull', 'shithead', 'shiting', 'shits', 'shitted', 'shitter', 'shitting', 'shitty', 'skank', 'slut', 'sluts', 'smegma', 'smut', 'snatch', 'spac', 'spunk', 'teets', 'teez', 'testical', 'testicle', 'tit', 'titfuck', 'tits', 'titt', 'tittiefucker', 'titties', 'tittyfuck', 'tittywank', 'titwank', 'tosser', 'turd', 'twat', 'twathead', 'twatty', 'twunt', 'twunter', 'vagina', 'viagra', 'vulva', 'wang', 'wank', 'wanker', 'wanky', 'whoar', 'whore', 'willies', 'willy', 'xrated', 'xxx'] diff --git a/toontown/uberdog/ServiceStart.py b/toontown/uberdog/ServiceStart.py new file mode 100755 index 00000000..0bc6bdb7 --- /dev/null +++ b/toontown/uberdog/ServiceStart.py @@ -0,0 +1,70 @@ +import __builtin__ + + +__builtin__.process = 'uberdog' + +# Temporary hack patch: +__builtin__.__dict__.update(__import__('pandac.PandaModules', fromlist=['*']).__dict__) +from direct.extensions_native import HTTPChannel_extensions + +import sys, os +sys.path.append( + os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../../dependencies" + ) + ) +) + +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument('--base-channel', help='The base channel that the server may use.') +parser.add_argument('--max-channels', help='The number of channels the server may use.') +parser.add_argument('--stateserver', help="The control channel of this UD's designated State Server.") +parser.add_argument('--astron-ip', help="The IP address of the Astron Message Director to connect to.") +parser.add_argument('--eventlogger-ip', help="The IP address of the Astron Event Logger to log to.") +parser.add_argument('config', nargs='*', default=['dependencies/config/general.prc', 'dependencies/config/release/dev.prc'], help="PRC file(s) to load.") +args = parser.parse_args() + +for prc in args.config: + loadPrcFile(prc) + +if os.path.isfile('dependencies/config/local.prc'): + loadPrcFile('dependencies/config/local.prc') + +localconfig = '' +if args.base_channel: + localconfig += 'air-base-channel %s\n' % args.base_channel +if args.max_channels: + localconfig += 'air-channel-allocation %s\n' % args.max_channels +if args.stateserver: + localconfig += 'air-stateserver %s\n' % args.stateserver +if args.astron_ip: + localconfig += 'air-connect %s\n' % args.astron_ip +if args.eventlogger_ip: + localconfig += 'eventlog-host %s\n' % args.eventlogger_ip +loadPrcFileData('Command-line', localconfig) + + +from otp.ai.AIBaseGlobal import * + +from toontown.uberdog.ToontownUberRepository import ToontownUberRepository +simbase.air = ToontownUberRepository(config.GetInt('air-base-channel', 400000000), + config.GetInt('air-stateserver', 4002)) +host = config.GetString('air-connect', '127.0.0.1') +port = 7100 +if ':' in host: + host, port = host.split(':', 1) + port = int(port) +simbase.air.connect(host, port) + +try: + run() +except SystemExit: + raise +except Exception: + info = describeException() + simbase.air.writeServerEvent('uberdog-exception', simbase.air.getAvatarIdFromSender(), simbase.air.getAccountIdFromSender(), info) + raise diff --git a/toontown/uberdog/ToontownUberRepository.py b/toontown/uberdog/ToontownUberRepository.py new file mode 100755 index 00000000..52278f95 --- /dev/null +++ b/toontown/uberdog/ToontownUberRepository.py @@ -0,0 +1,44 @@ +from direct.distributed.PyDatagram import * +import urlparse +from otp.distributed.OtpDoGlobals import * +from otp.distributed.DistributedDirectoryAI import DistributedDirectoryAI +from toontown.distributed.ToontownInternalRepository import ToontownInternalRepository +import toontown.minigame.MinigameCreatorAI +from toontown.uberdog.TopToonsManagerUD import TopToonsManagerUD + +if config.GetBool('want-rpc-server', False): + from toontown.rpc.ToontownRPCServer import ToontownRPCServer + from toontown.rpc.ToontownRPCHandler import ToontownRPCHandler + +class ToontownUberRepository(ToontownInternalRepository): + def __init__(self, baseChannel, serverId): + ToontownInternalRepository.__init__(self, baseChannel, serverId, dcSuffix='UD') + + self.notify.setInfo(True) + self.wantTopToons = self.config.GetBool('want-top-toons', True) + + def handleConnected(self): + ToontownInternalRepository.handleConnected(self) + rootObj = DistributedDirectoryAI(self) + rootObj.generateWithRequiredAndId(self.getGameDoId(), 0, 0) + + if config.GetBool('want-rpc-server', False): + endpoint = config.GetString('rpc-server-endpoint', 'http://localhost:8080/') + self.rpcServer = ToontownRPCServer(endpoint, ToontownRPCHandler(self)) + self.rpcServer.start(useTaskChain=True) + + self.createGlobals() + self.notify.info('Done.') + + def createGlobals(self): + """ + Create "global" objects. + """ + + self.csm = simbase.air.generateGlobalObject(OTP_DO_ID_CLIENT_SERVICES_MANAGER, 'ClientServicesManager') + self.chatAgent = simbase.air.generateGlobalObject(OTP_DO_ID_CHAT_MANAGER, 'ChatAgent') + self.friendsManager = simbase.air.generateGlobalObject(OTP_DO_ID_TTS_FRIENDS_MANAGER, 'TTSFriendsManager') + self.globalPartyMgr = simbase.air.generateGlobalObject(OTP_DO_ID_GLOBAL_PARTY_MANAGER, 'GlobalPartyManager') + if self.wantTopToons: + self.topToonsMgr = TopToonsManagerUD(self) + diff --git a/toontown/uberdog/TopToonsGlobals.py b/toontown/uberdog/TopToonsGlobals.py new file mode 100644 index 00000000..7786d828 --- /dev/null +++ b/toontown/uberdog/TopToonsGlobals.py @@ -0,0 +1,19 @@ +CAT_COGS = 1 +CAT_BLDG = 2 +CAT_CATALOG = 4 +CAT_GIFTS = 8 +CAT_TASKS = 16 +CAT_TROLLEY = 32 +CAT_RACE_WON = 64 +CAT_FISH = 128 +CAT_JELLYBEAN = 256 +CAT_HOLE_IN_ONE = 512 +CAT_COURSE_UNDER_PAR = 1024 +CAT_VP = 2048 +CAT_CFO = 4096 +CAT_CJ = 8192 +CAT_CEO = 16384 + +_CAT_BEGIN = CAT_COGS +_CAT_END = CAT_CEO +_CAT_ALL = (_CAT_END << 1) - 1 \ No newline at end of file diff --git a/toontown/uberdog/TopToonsManagerAI.py b/toontown/uberdog/TopToonsManagerAI.py new file mode 100644 index 00000000..5b41ac26 --- /dev/null +++ b/toontown/uberdog/TopToonsManagerAI.py @@ -0,0 +1,31 @@ +from direct.showbase.DirectObject import * +import TopToonsGlobals + +class TopToonsManagerAI(DirectObject): + def __init__(self, air): + self.air = air + + self.accept('topToonsManager-event', self.__handleEvent) + + def toonKilledBoss(self, av, boss): + cat = {'VP': TopToonsGlobals.CAT_VP, + 'CFO': TopToonsGlobals.CAT_CFO, + 'CJ': TopToonsGlobals.CAT_CJ, + 'CEO': TopToonsGlobals.CAT_CEO}.get(boss, 0) + self.__handleEvent(av.doId, cat, 1) + + def __handleEvent(self, *args): # avId, categories, score + self.air.sendNetEvent('topToonsManager-AI-score-site', list(args)) + +from otp.ai.MagicWordGlobal import * +@magicWord(types=[int, int]) +def topToon(score, cat=TopToonsGlobals._CAT_ALL): + av = spellbook.getTarget() + mgr = av.air.topToonsMgr + if not mgr: + return 'No manager!' + + if cat > TopToonsGlobals._CAT_ALL: + return 'Max value: %d' % TopToonsGlobals._CAT_ALL + + messenger.send('topToonsManager-event', [av.doId, cat, score]) \ No newline at end of file diff --git a/toontown/uberdog/TopToonsManagerUD.py b/toontown/uberdog/TopToonsManagerUD.py new file mode 100644 index 00000000..6bb752cb --- /dev/null +++ b/toontown/uberdog/TopToonsManagerUD.py @@ -0,0 +1,246 @@ +# CLEANUP IMPORTS + +from direct.directnotify import DirectNotifyGlobal +from direct.fsm.FSM import FSM +from direct.showbase.DirectObject import * +from toontown.toon.ToonDNA import ToonDNA, getSpeciesName +import TopToonsGlobals +import time, random +import datetime, json +import urllib +import urllib2 +import hashlib + +def getCurrentMonth(): + dt = datetime.date.today() + month = dt.month + year = dt.year + return year * 100 + month + +def getPrevMonth(): + current = getCurrentMonth() + year, month = divmod(current, 100) + month -= 1 + if not month: + month = 12 + year -= 1 + + return year * 100 + month + +def getNextMonth(): + current = getCurrentMonth() + year, month = divmod(current, 100) + month += 1 + if month > 12: + month = 1 + year += 1 + + return year * 100 + month + +def timeToNextMonth(): + now = datetime.datetime.now() + year, month = divmod(getNextMonth(), 100) + return (datetime.datetime(year, month, 1) - now).total_seconds() + +def getEmptySiteToonsColl(month): + coll = {} + + start = TopToonsGlobals._CAT_BEGIN + end = TopToonsGlobals._CAT_END + while start <= end: + coll[str(start)] = {} + start *= 2 + + coll['month'] = month + return coll + +class SiteUploadFSM(FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('SiteUploadFSM') + URL = config.GetString('toptoons-api-endpoint', 'http://toontownstride.com/toptoons/post/') # Let's hope jumbleweed hasn't changed this + + def __init__(self, mgr, data): + FSM.__init__(self, 'SiteUploadFSM') + + self.mgr = mgr + self.data = {} + self.month = data.pop('month') + for category, avs in data.items(): + self.data[int(category)] = sorted(avs.items(), key=lambda x: -x[1]) + + self.__cat = TopToonsGlobals._CAT_BEGIN + self.__responses = {} + self.__cache = {} + self.__waiting = {} + self.__dataToSend = {} + self.__failures = -1 + + self.demand('QueryAvatars') + + def enterQueryAvatars(self): + avs = self.data[self.__cat] + cutoff = self.__failures + if cutoff == -1: + cutoff = 5 + selected, remaining = avs[:cutoff], avs[cutoff:] + self.data[self.__cat] = remaining + + self.__waiting = {int(x[0]): x[1] for x in selected} + avIds = self.__waiting.keys() + for avId in avIds: + if avId in self.__cache: + self.__responses[avId] = (self.__cache[avId][0], self.__waiting.pop(avId)) + + self.__failures = 0 + for avId in self.__waiting: + def response(x, y, avId=avId): + self.__handleToon(avId, x, y) + + self.mgr.air.dbInterface.queryObject(self.mgr.air.dbId, avId, response) + + if not self.__waiting: + self.demand('SortResults') + + def __handleToon(self, avId, dclass, fields): + if avId not in self.__waiting: + return + + if dclass != self.mgr.air.dclassesByName['DistributedToonUD']: + self.__failures += 1 + self.notify.warning('%d query failed!' % avId) + del self.__waiting[avId] + if not self.__waiting: + self.demand('QueryAvatars') + return + + name = fields['setName'][0] + hp = fields['setMaxHp'][0] + + dna = ToonDNA(fields['setDNAString'][0]) + species = getSpeciesName(dna.head) + color = dna.headColor + + if species == 'pig': + dna = 'pig' + + else: + if species == 'cat' and color == 26: + dna = 'blackcat' + + else: + if color > 23: + color = 0 + + dna = '%s_%s_%d' % (species, dna.head[1:], color) + + self.__responses[avId] = ((name, dna, hp), self.__waiting.pop(avId)) + + if not self.__waiting: + self.demand('QueryAvatars') + + def enterSortResults(self): + responses = sorted(self.__responses.values(), key=lambda x: -x[-1]) + self.__dataToSend[self.__cat] = responses + self.__cache.update(self.__responses) + self.__failures = -1 + self.__responses = {} + self.__cat *= 2 + if self.__cat * 2 == TopToonsGlobals._CAT_END: + self.demand('Upload') + return + + self.demand('QueryAvatars') + + def enterUpload(self): + self.__dataToSend['month'] = self.month + + (success, error), res = self.post(self.URL, self.__dataToSend) + print (success, error), res + + def post(self, url, data): + headers = {'User-Agent' : 'TTUberAgent'} + + innerData = json.dumps(data) + hmac = hashlib.sha512(innerData + self.mgr.air.getApiKey()).hexdigest() # XXX PROVIDE THE KEY HERE + + data = 'data=%s' % urllib.quote(innerData) + data += '&hmac=%s' % urllib.quote(hmac) + + success = True + error = None + res = {} + + try: + req = urllib2.Request(url, data, headers) + res = json.loads(urllib2.urlopen(req).read()) + success = res['success'] + error = res.get('error') + + except Exception as e: + if hasattr(e, 'read'): + with open('../e.html', 'wb') as f: + f.write(e.read()) + + success = False + error = str(e) + + return (success, error), res + +class TopToonsManagerUD(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TopToonsManagerUD') + + def __init__(self, air): + self.air = air + + self.__curMonth = getCurrentMonth() + coll = None + if self.air.dbConn: + coll = self.air.dbGlobalCursor.strideToons.find_one({'month': self.__curMonth}) + if not coll: + lastMonthColl = self.air.dbGlobalCursor.strideToons.find_one({'month': getPrevMonth()}) + if lastMonthColl: + self.__uploadLastMonth(lastMonthColl) + + if not coll: + coll = getEmptySiteToonsColl(self.__curMonth) + + self.__topToonsData = coll + self.__topToonsData.pop('_id', None) + + self.accept('topToonsManager-AI-score-site', self.__topToonsScore) + self.waitForNextMonth() + + def __uploadLastMonth(self, data): + self.notify.info('Sending last month result to site...') + SiteUploadFSM(self, data) + + def waitForNextMonth(self): + def _nm(task): + self.__uploadLastMonth(self.__topToonsData) + + self.__curMonth = getCurrentMonth() + self.__topToonsData = getEmptySiteToonsColl(self.__curMonth) + + self.waitForNextMonth() + + return task.done + + taskMgr.doMethodLater(timeToNextMonth() + 1, _nm, 'TopToonsManagerUD-nextMonth') + + def saveSite(self): + if self.air.dbConn: + self.air.dbGlobalCursor.strideToons.update({'month': self.__curMonth}, {'$set': self.__topToonsData}, upsert=True) + + def __topToonsScore(self, avId, categories, score): + def _add(cat): + cd = self.__topToonsData[str(cat)] + cd[str(avId)] = cd.get(str(avId), 0) + score + + start = TopToonsGlobals._CAT_BEGIN + end = TopToonsGlobals._CAT_END + while start <= end: + if categories & start: + _add(start) + + start *= 2 + + self.saveSite() \ No newline at end of file diff --git a/toontown/uberdog/__init__.py b/toontown/uberdog/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/user/.gitignore b/user/.gitignore new file mode 100644 index 00000000..b64f54bd --- /dev/null +++ b/user/.gitignore @@ -0,0 +1 @@ +preferences.json diff --git a/user/logs/.gitignore b/user/logs/.gitignore new file mode 100644 index 00000000..397b4a76 --- /dev/null +++ b/user/logs/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/user/screenshots/.gitignore b/user/screenshots/.gitignore new file mode 100644 index 00000000..005717ea --- /dev/null +++ b/user/screenshots/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/user/videos/.gitignore b/user/videos/.gitignore new file mode 100644 index 00000000..c96a04f0 --- /dev/null +++ b/user/videos/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file